From 117ae4eb134bfbfd203776991cb90b03d90bf93f Mon Sep 17 00:00:00 2001 From: MelwareDE Date: Tue, 24 Apr 2007 20:21:04 +0000 Subject: [PATCH] Finalized CCBS implementation. --- CHANGES | 1 + README | 4 ++ chan_capi.c | 139 +++++++++++++++++++++++++++----------- chan_capi.h | 5 ++ chan_capi_supplementary.c | 61 ++++++++++++++++- chan_capi_supplementary.h | 4 +- chan_capi_utils.c | 13 ++-- 7 files changed, 177 insertions(+), 50 deletions(-) diff --git a/CHANGES b/CHANGES index 65c23ba..45bdcb5 100644 --- a/CHANGES +++ b/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 diff --git a/README b/README index eeac3ce..27beaf9 100644 --- a/README +++ b/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: diff --git a/chan_capi.c b/chan_capi.c index 1642c50..ecc8dc5 100644 --- a/chan_capi.c +++ b/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); diff --git a/chan_capi.h b/chan_capi.h index 772ed93..81492af 100644 --- a/chan_capi.h +++ b/chan_capi.h @@ -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 diff --git a/chan_capi_supplementary.c b/chan_capi_supplementary.c index ffc7634..242cb31 100644 --- a/chan_capi_supplementary.c +++ b/chan_capi_supplementary.c @@ -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); diff --git a/chan_capi_supplementary.h b/chan_capi_supplementary.h index 1f83b42..7d3ea9b 100644 --- a/chan_capi_supplementary.h +++ b/chan_capi_supplementary.h @@ -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 diff --git a/chan_capi_utils.c b/chan_capi_utils.c index 148cb0b..2d1f2ae 100644 --- a/chan_capi_utils.c +++ b/chan_capi_utils.c @@ -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; }