Finalized CCBS implementation.
parent
ecf0c248af
commit
117ae4eb13
1
CHANGES
1
CHANGES
|
@ -6,6 +6,7 @@ HEAD
|
|||
- possibly fixed ECT channel hang
|
||||
- added 'x' option to capicommand(ect) to have real 'explicit call transfer'
|
||||
(needed by some ISDN lines)
|
||||
- support CCBS (call completion on busy subscriber)
|
||||
|
||||
|
||||
chan_capi-1.0.1
|
||||
|
|
4
README
4
README
|
@ -68,6 +68,7 @@ This chan_capi version includes:
|
|||
- CLI command "capi show channels" shows details on channel status.
|
||||
- Asterisk 1.4 jitterbuffer configuration.
|
||||
- some QSIG extensions (see README.qsig)
|
||||
- CCBS (call completion on busy subscriber)
|
||||
|
||||
|
||||
The Dial string
|
||||
|
@ -246,6 +247,9 @@ Call completion on subscriber busy (CCBS):
|
|||
If the remote party becomes 'non-busy', the network initiates the callback which will be
|
||||
sent to the provided context/exten/priority. Of course, this only happens if your local
|
||||
phone is set to 'free' with capicommand(ccpartybusy), which is the default.
|
||||
In this context/exten/priority you should just setup a callfile to initiate an outgoing
|
||||
call from your extension to
|
||||
exten => s,1,Dial(CAPI/ccbs/${CCLINKAGEID}/)
|
||||
|
||||
Deactivate CCBS:
|
||||
To deactivate a previously activated CCBS, use following command:
|
||||
|
|
139
chan_capi.c
139
chan_capi.c
|
@ -814,6 +814,7 @@ static void interface_cleanup(struct capi_pvt *i)
|
|||
i->NCCI = 0;
|
||||
i->onholdPLCI = 0;
|
||||
i->doEC = i->doEC_global;
|
||||
i->ccbsnrhandle = 0;
|
||||
|
||||
memset(i->cid, 0, sizeof(i->cid));
|
||||
memset(i->dnid, 0, sizeof(i->dnid));
|
||||
|
@ -1143,6 +1144,48 @@ static int pbx_capi_call(struct ast_channel *c, char *idest, int timeout)
|
|||
return -1;
|
||||
}
|
||||
|
||||
i->peer = cc_get_peer_link_id(pbx_builtin_getvar_helper(c, "CAPIPEERLINKID"));
|
||||
i->outgoing = 1;
|
||||
i->isdnstate |= CAPI_ISDN_STATE_PBX;
|
||||
i->state = CAPI_STATE_CONNECTPENDING;
|
||||
ast_setstate(c, AST_STATE_DIALING);
|
||||
i->MessageNumber = get_capi_MessageNumber();
|
||||
|
||||
/* if this is a CCBS/CCNR callback call */
|
||||
if (i->ccbsnrhandle) {
|
||||
_cword cip = (_cword)tcap2cip(c->transfercapability);
|
||||
_cword rbref;
|
||||
|
||||
i->doOverlap = 0;
|
||||
rbref = capi_ccbsnr_take_ref(i->ccbsnrhandle);
|
||||
|
||||
if ((rbref == 0xdead) ||
|
||||
((capi_sendf(NULL, 0, CAPI_FACILITY_REQ, i->controller, i->MessageNumber,
|
||||
"w(w(www(wwwsss())()()()()))",
|
||||
FACILITYSELECTOR_SUPPLEMENTARY,
|
||||
0x0012, /* CCBS call */
|
||||
rbref, /* reference */
|
||||
cip, /* CIP */
|
||||
0, /* reserved */
|
||||
/* B protocol */
|
||||
b_protocol_table[i->bproto].b1protocol,
|
||||
b_protocol_table[i->bproto].b2protocol,
|
||||
b_protocol_table[i->bproto].b3protocol,
|
||||
b_protocol_table[i->bproto].b1configuration,
|
||||
b_protocol_table[i->bproto].b2configuration,
|
||||
b_protocol_table[i->bproto].b3configuration
|
||||
/* */ /* BC */
|
||||
/* */ /* LLC */
|
||||
/* */ /* HLC */
|
||||
/* */ /* Additional Info */
|
||||
)))) {
|
||||
i->state = CAPI_STATE_DISCONNECTED;
|
||||
ast_setstate(c, AST_STATE_RESERVED);
|
||||
return 1;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
CLIR = c->cid.cid_pres;
|
||||
callernplan = c->cid.cid_ton & 0x7f;
|
||||
|
||||
|
@ -1166,9 +1209,6 @@ static int pbx_capi_call(struct ast_channel *c, char *idest, int timeout)
|
|||
dsa = calledsubaddress;
|
||||
}
|
||||
|
||||
i->peer = cc_get_peer_link_id(pbx_builtin_getvar_helper(c, "CAPIPEERLINKID"));
|
||||
|
||||
i->MessageNumber = get_capi_MessageNumber();
|
||||
CONNECT_REQ_HEADER(&CMSG, capi_ApplID, i->MessageNumber, i->controller);
|
||||
CONNECT_REQ_CONTROLLER(&CMSG) = i->controller;
|
||||
CONNECT_REQ_CIPVALUE(&CMSG) = tcap2cip(c->transfercapability);
|
||||
|
@ -1228,20 +1268,11 @@ static int pbx_capi_call(struct ast_channel *c, char *idest, int timeout)
|
|||
CONNECT_REQ_FACILITYDATAARRAY(&CMSG) = facilityarray;
|
||||
}
|
||||
|
||||
cc_mutex_lock(&i->lock);
|
||||
|
||||
i->outgoing = 1;
|
||||
i->isdnstate |= CAPI_ISDN_STATE_PBX;
|
||||
i->state = CAPI_STATE_CONNECTPENDING;
|
||||
ast_setstate(c, AST_STATE_DIALING);
|
||||
|
||||
if ((error = _capi_put_cmsg(&CMSG))) {
|
||||
i->state = CAPI_STATE_DISCONNECTED;
|
||||
ast_setstate(c, AST_STATE_RESERVED);
|
||||
cc_mutex_unlock(&i->lock);
|
||||
return error;
|
||||
}
|
||||
cc_mutex_unlock(&i->lock);
|
||||
|
||||
/* now we shall return .... the rest has to be done by handle_msg */
|
||||
return 0;
|
||||
|
@ -1923,6 +1954,7 @@ pbx_capi_request(const char *type, int format, void *data, int *cause)
|
|||
char buffer[CAPI_MAX_STRING];
|
||||
ast_group_t capigroup = 0;
|
||||
unsigned int controller = 0;
|
||||
unsigned int ccbsnrhandle = 0;
|
||||
|
||||
cc_verbose(1, 1, VERBOSE_PREFIX_4 "data = %s format=%d\n", (char *)data, format);
|
||||
|
||||
|
@ -1943,6 +1975,16 @@ pbx_capi_request(const char *type, int format, void *data, int *cause)
|
|||
controller = atoi(interface + 5);
|
||||
cc_verbose(1, 1, VERBOSE_PREFIX_4 "capi request controller = %d\n",
|
||||
controller);
|
||||
} else if (!strncmp(interface, "ccbs", 4)) {
|
||||
ccbsnrhandle = (unsigned int)strtoul(dest, NULL, 0);
|
||||
cc_verbose(1, 1, VERBOSE_PREFIX_4 "capi request ccbs handle = %u\n",
|
||||
ccbsnrhandle);
|
||||
if ((controller = capi_get_ccbsnrcontroller(ccbsnrhandle)) == 0) {
|
||||
cc_verbose(2, 0, VERBOSE_PREFIX_3 "didn't find CCBS handle %u\n",
|
||||
ccbsnrhandle);
|
||||
*cause = AST_CAUSE_REQUESTED_CHAN_UNAVAIL;
|
||||
return NULL;
|
||||
}
|
||||
} else {
|
||||
cc_verbose(1, 1, VERBOSE_PREFIX_4 "capi request for interface '%s'\n",
|
||||
interface);
|
||||
|
@ -1983,6 +2025,7 @@ pbx_capi_request(const char *type, int format, void *data, int *cause)
|
|||
}
|
||||
i->PLCI = 0;
|
||||
i->outgoing = 1; /* this is an outgoing line */
|
||||
i->ccbsnrhandle = ccbsnrhandle;
|
||||
cc_mutex_unlock(&iflock);
|
||||
return tmp;
|
||||
}
|
||||
|
@ -3583,7 +3626,7 @@ static void capidev_handle_connect_indication(_cmsg *CMSG, unsigned int PLCI, un
|
|||
/*
|
||||
* CAPI FACILITY_CONF
|
||||
*/
|
||||
static void capidev_handle_facility_confirmation(_cmsg *CMSG, unsigned int PLCI, unsigned int NCCI, struct capi_pvt *i)
|
||||
static void capidev_handle_facility_confirmation(_cmsg *CMSG, unsigned int PLCI, unsigned int NCCI, struct capi_pvt **i)
|
||||
{
|
||||
int selector;
|
||||
|
||||
|
@ -3594,26 +3637,26 @@ static void capidev_handle_facility_confirmation(_cmsg *CMSG, unsigned int PLCI,
|
|||
return;
|
||||
}
|
||||
|
||||
if (i == NULL)
|
||||
if (*i == NULL)
|
||||
return;
|
||||
|
||||
if (selector == FACILITYSELECTOR_DTMF) {
|
||||
cc_verbose(2, 1, VERBOSE_PREFIX_4 "%s: DTMF conf(PLCI=%#x)\n",
|
||||
i->vname, PLCI);
|
||||
(*i)->vname, PLCI);
|
||||
return;
|
||||
}
|
||||
if (selector == i->ecSelector) {
|
||||
if (selector == (*i)->ecSelector) {
|
||||
if (FACILITY_CONF_INFO(CMSG)) {
|
||||
cc_verbose(2, 0, VERBOSE_PREFIX_3 "%s: Error setting up echo canceller (PLCI=%#x)\n",
|
||||
i->vname, PLCI);
|
||||
(*i)->vname, PLCI);
|
||||
return;
|
||||
}
|
||||
if (FACILITY_CONF_FACILITYCONFIRMATIONPARAMETER(CMSG)[1] == EC_FUNCTION_DISABLE) {
|
||||
cc_verbose(3, 0, VERBOSE_PREFIX_3 "%s: Echo canceller successfully disabled (PLCI=%#x)\n",
|
||||
i->vname, PLCI);
|
||||
(*i)->vname, PLCI);
|
||||
} else {
|
||||
cc_verbose(3, 0, VERBOSE_PREFIX_3 "%s: Echo canceller successfully set up (PLCI=%#x)\n",
|
||||
i->vname, PLCI);
|
||||
(*i)->vname, PLCI);
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
@ -3622,18 +3665,18 @@ static void capidev_handle_facility_confirmation(_cmsg *CMSG, unsigned int PLCI,
|
|||
(FACILITY_CONF_FACILITYCONFIRMATIONPARAMETER(CMSG)[2] == 0x0)) {
|
||||
/* enable */
|
||||
if (FACILITY_CONF_FACILITYCONFIRMATIONPARAMETER(CMSG)[0] > 12) {
|
||||
show_capi_info(i, read_capi_word(&FACILITY_CONF_FACILITYCONFIRMATIONPARAMETER(CMSG)[12]));
|
||||
show_capi_info(*i, read_capi_word(&FACILITY_CONF_FACILITYCONFIRMATIONPARAMETER(CMSG)[12]));
|
||||
}
|
||||
} else {
|
||||
/* disable */
|
||||
if (FACILITY_CONF_FACILITYCONFIRMATIONPARAMETER(CMSG)[0] > 12) {
|
||||
show_capi_info(i, read_capi_word(&FACILITY_CONF_FACILITYCONFIRMATIONPARAMETER(CMSG)[12]));
|
||||
show_capi_info(*i, read_capi_word(&FACILITY_CONF_FACILITYCONFIRMATIONPARAMETER(CMSG)[12]));
|
||||
}
|
||||
}
|
||||
return;
|
||||
}
|
||||
cc_log(LOG_ERROR, "%s: unhandled FACILITY_CONF 0x%x\n",
|
||||
i->vname, FACILITY_CONF_FACILITYSELECTOR(CMSG));
|
||||
(*i)->vname, FACILITY_CONF_FACILITYSELECTOR(CMSG));
|
||||
}
|
||||
|
||||
/*
|
||||
|
@ -3707,6 +3750,37 @@ static void capidev_post_handling(struct capi_pvt *i, _cmsg *CMSG)
|
|||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* handle CONNECT_CONF or FACILITY_CONF(CCBS call)
|
||||
*/
|
||||
void capidev_handle_connection_conf(struct capi_pvt **i, unsigned int PLCI,
|
||||
unsigned short wInfo, unsigned short wMsgNum)
|
||||
{
|
||||
struct capi_pvt *ii;
|
||||
struct ast_frame fr = { AST_FRAME_CONTROL, AST_CONTROL_BUSY, };
|
||||
|
||||
if (*i) {
|
||||
cc_log(LOG_ERROR, "CAPI: CONNECT_CONF for already "
|
||||
"defined interface received\n");
|
||||
return;
|
||||
}
|
||||
*i = find_interface_by_msgnum(wMsgNum);
|
||||
ii = *i;
|
||||
if ((ii == NULL) || (!ii->owner)) {
|
||||
return;
|
||||
}
|
||||
cc_verbose(1, 1, VERBOSE_PREFIX_3 "%s: received CONNECT_CONF PLCI = %#x\n",
|
||||
ii->vname, PLCI);
|
||||
cc_mutex_lock(&ii->lock);
|
||||
if (wInfo == 0) {
|
||||
ii->PLCI = PLCI;
|
||||
} else {
|
||||
/* error in connect, so set correct state and signal busy */
|
||||
ii->state = CAPI_STATE_DISCONNECTED;
|
||||
local_queue_frame(ii, &fr);
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* handle CAPI msg
|
||||
*/
|
||||
|
@ -3773,28 +3847,11 @@ static void capidev_handle_msg(_cmsg *CMSG)
|
|||
|
||||
case CAPI_P_CONF(FACILITY):
|
||||
wInfo = FACILITY_CONF_INFO(CMSG);
|
||||
capidev_handle_facility_confirmation(CMSG, PLCI, NCCI, i);
|
||||
capidev_handle_facility_confirmation(CMSG, PLCI, NCCI, &i);
|
||||
break;
|
||||
case CAPI_P_CONF(CONNECT):
|
||||
wInfo = CONNECT_CONF_INFO(CMSG);
|
||||
if (i) {
|
||||
cc_log(LOG_ERROR, "CAPI: CONNECT_CONF for already "
|
||||
"defined interface received\n");
|
||||
break;
|
||||
}
|
||||
i = find_interface_by_msgnum(wMsgNum);
|
||||
if ((i == NULL) || (!i->owner))
|
||||
break;
|
||||
cc_verbose(1, 1, VERBOSE_PREFIX_3 "%s: received CONNECT_CONF PLCI = %#x\n",
|
||||
i->vname, PLCI);
|
||||
if (wInfo == 0) {
|
||||
i->PLCI = PLCI;
|
||||
} else {
|
||||
/* error in connect, so set correct state and signal busy */
|
||||
i->state = CAPI_STATE_DISCONNECTED;
|
||||
struct ast_frame fr = { AST_FRAME_CONTROL, AST_CONTROL_BUSY, };
|
||||
local_queue_frame(i, &fr);
|
||||
}
|
||||
capidev_handle_connection_conf(&i, PLCI, wInfo, wMsgNum);
|
||||
break;
|
||||
case CAPI_P_CONF(CONNECT_B3):
|
||||
wInfo = CONNECT_B3_CONF_INFO(CMSG);
|
||||
|
|
|
@ -380,6 +380,9 @@ struct capi_pvt {
|
|||
/* Fax status */
|
||||
unsigned int FaxState;
|
||||
|
||||
/* handle for CCBS/CCNR callback */
|
||||
unsigned int ccbsnrhandle;
|
||||
|
||||
/* not all codecs supply frames in nice 160 byte chunks */
|
||||
struct ast_smoother *smoother;
|
||||
|
||||
|
@ -563,5 +566,7 @@ extern unsigned capi_ApplID;
|
|||
extern struct capi_pvt *iflist;
|
||||
extern void cc_start_b3(struct capi_pvt *i);
|
||||
extern void queue_cause_control(struct capi_pvt *i, int control);
|
||||
extern void capidev_handle_connection_conf(struct capi_pvt **i, unsigned int PLCI,
|
||||
unsigned short wInfo, unsigned short wMsgNum);
|
||||
|
||||
#endif
|
||||
|
|
|
@ -68,6 +68,28 @@ void cleanup_ccbsnr(void)
|
|||
cc_mutex_unlock(&ccbsnr_lock);
|
||||
}
|
||||
|
||||
/*
|
||||
* return the controller of ccbsnr handle
|
||||
*/
|
||||
unsigned int capi_get_ccbsnrcontroller(unsigned int handle)
|
||||
{
|
||||
unsigned int contr = 0;
|
||||
struct ccbsnr_s *ccbsnr;
|
||||
|
||||
cc_mutex_lock(&ccbsnr_lock);
|
||||
ccbsnr = ccbsnr_list;
|
||||
while (ccbsnr) {
|
||||
if (ccbsnr->handle == handle) {
|
||||
contr = (ccbsnr->plci & 0xff);
|
||||
break;
|
||||
}
|
||||
ccbsnr = ccbsnr->next;
|
||||
}
|
||||
cc_mutex_unlock(&ccbsnr_lock);
|
||||
|
||||
return contr;
|
||||
}
|
||||
|
||||
/*
|
||||
* a new CCBS/CCNR id was received
|
||||
*/
|
||||
|
@ -224,6 +246,34 @@ static void del_ccbsnr_ref(unsigned int plci, _cword ref)
|
|||
cc_mutex_unlock(&ccbsnr_lock);
|
||||
}
|
||||
|
||||
/*
|
||||
* return rbref of CCBS/CCNR and delete entry
|
||||
*/
|
||||
_cword capi_ccbsnr_take_ref(unsigned int handle)
|
||||
{
|
||||
unsigned int plci = 0;
|
||||
_cword rbref = 0xdead;
|
||||
struct ccbsnr_s *ccbsnr;
|
||||
|
||||
cc_mutex_lock(&ccbsnr_lock);
|
||||
ccbsnr = ccbsnr_list;
|
||||
while (ccbsnr) {
|
||||
if (ccbsnr->handle == handle) {
|
||||
plci = ccbsnr->plci;
|
||||
rbref = ccbsnr->rbref;
|
||||
break;
|
||||
}
|
||||
ccbsnr = ccbsnr->next;
|
||||
}
|
||||
cc_mutex_unlock(&ccbsnr_lock);
|
||||
|
||||
if (rbref != 0xdead) {
|
||||
del_ccbsnr_ref(plci, rbref);
|
||||
}
|
||||
|
||||
return rbref;
|
||||
}
|
||||
|
||||
/*
|
||||
* a CCBS/CCNR id was removed
|
||||
*/
|
||||
|
@ -567,14 +617,14 @@ int handle_facility_indication_supplementary(
|
|||
* CAPI FACILITY_CONF supplementary
|
||||
*/
|
||||
void handle_facility_confirmation_supplementary(
|
||||
_cmsg *CMSG, unsigned int PLCI, unsigned int NCCI, struct capi_pvt *i)
|
||||
_cmsg *CMSG, unsigned int PLCI, unsigned int NCCI, struct capi_pvt **i)
|
||||
{
|
||||
_cword function;
|
||||
_cword serviceinfo;
|
||||
char name[64];
|
||||
|
||||
if (i) {
|
||||
strncpy(name, i->vname, sizeof(name) - 1);
|
||||
if (*i) {
|
||||
strncpy(name, (*i)->vname, sizeof(name) - 1);
|
||||
} else {
|
||||
snprintf(name, sizeof(name) - 1, "contr%d", PLCI & 0xff);
|
||||
}
|
||||
|
@ -593,6 +643,11 @@ void handle_facility_confirmation_supplementary(
|
|||
cc_verbose(2, 1, VERBOSE_PREFIX_3 "%s: CCBS request confirmation (0x%04x) (PLCI=%#x)\n",
|
||||
name, serviceinfo, PLCI);
|
||||
break;
|
||||
case 0x0012: /* CCBS call */
|
||||
cc_verbose(2, 1, VERBOSE_PREFIX_3 "%s: CCBS call confirmation (0x%04x) (PLCI=%#x)\n",
|
||||
name, serviceinfo, PLCI);
|
||||
capidev_handle_connection_conf(i, PLCI, FACILITY_CONF_INFO(CMSG), HEADER_MSGNUM(CMSG));
|
||||
break;
|
||||
default:
|
||||
cc_verbose(3, 1, VERBOSE_PREFIX_3 "%s: unhandled FACILITY_CONF supplementary function %04x\n",
|
||||
name, function);
|
||||
|
|
|
@ -44,10 +44,12 @@ extern void ListenOnSupplementary(unsigned controller);
|
|||
extern int handle_facility_indication_supplementary(
|
||||
_cmsg *CMSG, unsigned int PLCI, unsigned int NCCI, struct capi_pvt *i);
|
||||
extern void handle_facility_confirmation_supplementary(
|
||||
_cmsg *CMSG, unsigned int PLCI, unsigned int NCCI, struct capi_pvt *i);
|
||||
_cmsg *CMSG, unsigned int PLCI, unsigned int NCCI, struct capi_pvt **i);
|
||||
extern int pbx_capi_ccbs(struct ast_channel *c, char *data);
|
||||
extern int pbx_capi_ccbsstop(struct ast_channel *c, char *data);
|
||||
extern int pbx_capi_ccpartybusy(struct ast_channel *c, char *data);
|
||||
extern void cleanup_ccbsnr(void);
|
||||
extern unsigned int capi_get_ccbsnrcontroller(unsigned int handle);
|
||||
extern _cword capi_ccbsnr_take_ref(unsigned int handle);
|
||||
|
||||
#endif
|
||||
|
|
|
@ -323,8 +323,12 @@ MESSAGE_EXCHANGE_ERROR capi_sendf(
|
|||
break;
|
||||
case 's': /* struct, length is the first byte */
|
||||
string = va_arg(ap, unsigned char *);
|
||||
for (j = 0; j <= string[0]; j++)
|
||||
*(p++) = string[j];
|
||||
if (string == NULL) {
|
||||
*(p++) = 0;
|
||||
} else {
|
||||
for (j = 0; j <= string[0]; j++)
|
||||
*(p++) = string[j];
|
||||
}
|
||||
break;
|
||||
case 'a': /* ascii string, NULL terminated string */
|
||||
string = va_arg(ap, unsigned char *);
|
||||
|
@ -894,10 +898,9 @@ struct ast_channel *cc_get_peer_link_id(const char *p)
|
|||
if ((id >= 0) && (id < CAPI_MAX_PEERLINKCHANNELS)) {
|
||||
chan = peerlinkchannel[id].channel;
|
||||
peerlinkchannel[id].channel = NULL;
|
||||
} else {
|
||||
cc_verbose(3, 1, VERBOSE_PREFIX_4 "capi: peerlink %d allocated, peer is %s\n",
|
||||
id, (chan)?chan->name:"unlinked");
|
||||
}
|
||||
cc_verbose(3, 1, VERBOSE_PREFIX_4 "capi: peerlink %d allocated, peer is %s\n",
|
||||
id, (chan)?chan->name:"unlinked");
|
||||
cc_mutex_unlock(&peerlink_lock);
|
||||
return chan;
|
||||
}
|
||||
|
|
Loading…
Reference in New Issue