- corrected an wrong condition in bridging code (should be a very rare case)

QSIG:
 - enabled automatic call transfer and path replacement on bridge attempt
 - nicer output of debugging informations
 - removed wrong data from SendingComplete CAPI struct (usage of sendf)
This commit is contained in:
MelwareDE 2007-06-04 18:37:12 +00:00
parent 63c3da3a81
commit cae2b60cd4
6 changed files with 180 additions and 63 deletions

View File

@ -35,7 +35,9 @@ To use Q.SIG with asterisk, you'll need a card like Eicon DIVA Server
The QSIG support includes:
==========================
- Name presentation on Call SETUP incoming like outgoing
- ISDN LEG INFO2 field - a message which delivers informations about call diversions on incoming call to asterisk
Data is stored in Asterisk variables:
QSIG_LI2_DIVREASON Reason of divertion: 0 - unknown, 1 - unconditional, 2 - user busy, 3 - user no reply
@ -47,17 +49,20 @@ The QSIG support includes:
QSIG_LI2_ODIVNAME original diverting name
at the moment only incoming handling is supported
- Possibility to inform QSIG switch about call from public network
If you set variable QSIG_SETUP=X then the QSIG switch on the other side will know,
this call source is public network - you will get different ring tone, etc.
In dialplan use: Set(__QSIG_SETUP=X) command.
The leading "__" tells asterisk, to export this variable to the outgoing channel and
its subchannels
- Simple Call Transfer
With capicommand(qsig_ct|src-id|dst-id) you can transfer an inbound call back to the qsig switch.
The B-Channel of this call will be relased, so that the line is free for a next call.
Unfortunately the call will be completely released by the switch, if the target is busy.
If you want need to know, if your target is busy, you can use the call transfer feature below.
- Call Transfer (outgoing)
You can do an outbound call transfer.
First you need the PLCI (logical channel id) of your first channel. You'll get it with capicommand(getplci). This
@ -68,16 +73,24 @@ The QSIG support includes:
If the external switch offers an path replacement propose, it will be taken automatically in account.
The B-Channels will be cleared by the switch after call connection. Your channels stay free.
- Automatic Call Transfer and Path Replacement (if allowed/possible) on bridge/line interconnect
If an line interconnect is set up from asterisk, chan_capi sends an Call Transfer facility out and waits for an
Path Replacement Propose message - if no Path Replacement is received, the line interconnect will proceed.
The Call Transfer allows your connected extensions in every case (if the switch supports the Call Transfer feature)
to see the name and number of his connected peer.
This should be configurable in the next release.
Future Targets:
===============
- check code for buffer overflows
- Call Transfer inbound
- Path Replacement inbound
- Allow/Disallow Path Replacement within capi.conf
- Call Rerouting feature [ECMA-174]
- CCBS
- AOC
- Sent out LEG_INFO2 - maybe this would allow an sendtext (display instructions on the connected set)
- ...
How to use:

View File

@ -1268,7 +1268,7 @@ static int pbx_capi_call(struct ast_channel *c, char *idest, int timeout)
}
error = capi_sendf(NULL, 0, CAPI_CONNECT_REQ, i->controller, i->MessageNumber,
"wssss(wwwsss())()()()((w)()()s)",
"wssss(wwwsss())()()()((w)()()s())",
tcap2cip(i->transfercapability), /* CIP value */
called, /* called party number */
calling, /* calling party number */
@ -1584,13 +1584,28 @@ static CC_BRIDGE_RETURN pbx_capi_bridge(struct ast_channel *c0,
}
if ((i0->isdnstate & CAPI_ISDN_STATE_ECT) ||
(i0->isdnstate & CAPI_ISDN_STATE_ECT)) {
(i1->isdnstate & CAPI_ISDN_STATE_ECT)) {
return AST_BRIDGE_FAILED;
}
if ((i0->qsigfeat) && (i1->qsigfeat)) {
int br_status = pbx_capi_qsig_bridge(i0, i1);
switch (br_status) {
case 1: /* successfull established Call Transfer with PR */
return ret;
case 2: /* don't do bridge - call transfer is active */
cc_verbose(3, 1, VERBOSE_PREFIX_2 "%s:%s cancelled bridge (path replacement was sent) for %s and %s\n",
i0->vname, i1->vname, c0->name, c1->name);
return AST_BRIDGE_FAILED_NOWARN;
default: /* let's do line interconnect */
break;
}
}
capi_wait_for_b3_up(i0);
capi_wait_for_b3_up(i1);
if (!(flags & AST_BRIDGE_DTMF_CHANNEL_0))
capi_detect_dtmf(i0, 0);
@ -5718,6 +5733,10 @@ int unload_module(void)
cc_log(LOG_WARNING, "On unload, interface still has owner or is used.\n");
if (i->smoother)
ast_smoother_free(i->smoother);
if (i->qsigfeat)
pbx_capi_qsig_unload_module(i);
cc_mutex_destroy(&i->lock);
ast_cond_destroy(&i->event_trigger);
itmp = i;

View File

@ -266,12 +266,16 @@ struct cc_qsig_data {
/* Path Replacement */
int pr_propose_sendback; /* send back an prior received PR PROPOSE on Connect */
int pr_propose_sentback; /* set to 1 after sending an PR PROPOSE */
int pr_propose_active;
char *pr_propose_cid; /* Call identity */
char *pr_propose_pn; /* Party Number */
/* Partner Channel - needed for many features */
struct capi_pvt *partner_ch;
unsigned int partner_plci;
ast_cond_t event_trigger;
unsigned int waitevent;
};
/* ! Private data for a capi device */

View File

@ -20,6 +20,9 @@
#define QSIG_TYPE_ALCATEL_ECMA 0x01 /* use additional Alcatel ECMA */
#define QSIG_TYPE_HICOM_ECMAV2 0x02 /* use additional Hicom ECMA V2 */
#define CAPI_QSIG_WAITEVENT_PRPROPOSE 0x01000000
#define Q932_PROTOCOL_ROSE 0x11 /* X.219 & X.229 */
#define Q932_PROTOCOL_CMIP 0x12 /* Q.941 */
#define Q932_PROTOCOL_ACSE 0x13 /* X.217 & X.227 */
@ -94,6 +97,9 @@
#define CCQSIG__ECMA__PRPROPOSE 1004 /* Path Replacement Propose */
#define CCQSIG__ECMA__LEGINFO2 1021 /* LEG INFORMATION2 */
#define CCQSIG_TIMER_WAIT_PRPROPOSE 1 /* Wait x seconds */
/*
* INVOKE Data struct, contains data for further operations
*/
@ -167,9 +173,12 @@ extern int pbx_capi_qsig_getplci(struct ast_channel *c, char *param);
extern int pbx_capi_qsig_ssct(struct ast_channel *c, char *param);
extern int pbx_capi_qsig_ct(struct ast_channel *c, char *param);
extern int pbx_capi_qsig_callmark(struct ast_channel *c, char *param);
extern int pbx_capi_qsig_bridge(struct capi_pvt *i0, struct capi_pvt *i1);
extern void cc_qsig_interface_init(struct cc_capi_conf *conf, struct capi_pvt *tmp);
extern void interface_cleanup_qsig(struct capi_pvt *i);
extern void pbx_capi_qsig_unload_module(struct capi_pvt *i);
extern void pbx_capi_qsig_handle_info_indication(_cmsg *CMSG, unsigned int PLCI, unsigned int NCCI, struct capi_pvt *i);
/*

View File

@ -754,6 +754,8 @@ unsigned int cc_qsig_add_call_setup_data(unsigned char *data, struct capi_pvt *i
char *pp = NULL;
int add_externalinfo = 0;
data[0] = 0; /* Initialize array length */
if ((p = (char *)pbx_builtin_getvar_helper(c, "QSIG_SETUP"))) {
/* some special dial parameters */
/* parse the parameters */
@ -833,10 +835,12 @@ unsigned int cc_qsig_add_call_setup_data(unsigned char *data, struct capi_pvt *i
if (add_externalinfo) {
/* add PROGRESS INDICATOR for external calls*/
memcpy(&data[dataidx], xprogress, sizeof(xprogress));
data[0] += data[0] + sizeof(xprogress);
int progress_size = sizeof(xprogress);
memcpy(&data[dataidx], xprogress, progress_size);
data[0] += progress_size;
dataidx += progress_size;
}
/* }*/
return 0;
}
@ -866,7 +870,7 @@ unsigned int cc_qsig_do_facility(unsigned char *fac, struct ast_channel *c, cha
break;
}
cc_qsig_build_facility_struct(fac, &facidx, protocolvar, APDUINTERPRETATION_REJECT, &nfe);
cc_qsig_build_facility_struct(fac, &facidx, protocolvar, APDUINTERPRETATION_IGNORE, &nfe);
switch (factype) {
case 4: /* ECMA-xxx pathReplacementPropose */
cc_qsig_encode_ecma_prpropose(fac, &facidx, &invoke, i, param);
@ -992,6 +996,30 @@ int pbx_capi_qsig_callmark(struct ast_channel *c, char *param)
return 0;
}
static void send_feature_calltransfer(struct capi_pvt *i)
{
unsigned char *fac = alloca(CAPI_MAX_FACILITYDATAARRAY_SIZE);
struct capi_pvt *ii = capi_find_interface_by_plci(i->qsig_data.partner_plci);
/* needed for Path Replacement */
ii->qsig_data.partner_plci = i->PLCI;
if (ii) {
cc_qsig_do_facility(fac, ii->owner, NULL, 12, 0);
capi_sendf(NULL, 0, CAPI_INFO_REQ, ii->PLCI, get_capi_MessageNumber(), "()(()()()s())", fac);
cc_qsig_do_facility(fac, i->owner, NULL, 12, 1);
capi_sendf(NULL, 0, CAPI_INFO_REQ, i->PLCI, get_capi_MessageNumber(), "()(()()()s())", fac);
} else {
cc_log(LOG_WARNING, "Call Transfer failed - second channel not found (PLCI %#x)!\n", i->qsig_data.partner_plci);
}
}
/*
* init QSIG data on new channel - will be called by mkif
*/
@ -1004,13 +1032,18 @@ void cc_qsig_interface_init(struct cc_capi_conf *conf, struct capi_pvt *tmp)
tmp->qsig_data.dnameid = NULL;
/* Path Replacement */
tmp->qsig_data.pr_propose_active = 0;
tmp->qsig_data.pr_propose_sendback = 0; /* send back an prior received PR PROPOSE on Connect */
tmp->qsig_data.pr_propose_sentback = 0;
tmp->qsig_data.pr_propose_cid = NULL; /* Call identity */
tmp->qsig_data.pr_propose_pn = NULL; /* Party Number */
/* Partner Channel - needed for many features */
tmp->qsig_data.partner_ch = NULL;
tmp->qsig_data.partner_plci = 0;
tmp->qsig_data.waitevent = 0;
ast_cond_init(&tmp->qsig_data.event_trigger, NULL);
}
/*
@ -1022,6 +1055,8 @@ static void qsig_cleanup_channel(struct capi_pvt *i)
i->qsig_data.partner_ch = NULL;
i->qsig_data.calltransfer_active = 0;
i->qsig_data.calltransfer_onring = 0;
i->qsig_data.pr_propose_active = 0;
i->qsig_data.pr_propose_sentback = 0;
if (i->qsig_data.pr_propose_cid) {
free(i->qsig_data.pr_propose_cid);
i->qsig_data.pr_propose_cid = NULL;
@ -1034,6 +1069,7 @@ static void qsig_cleanup_channel(struct capi_pvt *i)
free(i->qsig_data.dnameid);
i->qsig_data.dnameid = NULL;
}
}
/*
@ -1042,10 +1078,85 @@ static void qsig_cleanup_channel(struct capi_pvt *i)
void interface_cleanup_qsig(struct capi_pvt *i)
{
if (i->qsigfeat) {
cc_verbose(1, 1, VERBOSE_PREFIX_4 "QSIG: cleanup channel\n");
qsig_cleanup_channel(i);
}
}
/*
* cleanup QSIG stuff on module unload
*/
void pbx_capi_qsig_unload_module(struct capi_pvt *i)
{
ast_cond_destroy(&i->qsig_data.event_trigger);
}
/*
* wait for B3 up
*/
int pbx_capi_qsig_wait_for_prpropose(struct capi_pvt *i)
{
struct timespec abstime;
int ret = 1;
cc_mutex_lock(&i->lock);
if (!(i->qsig_data.pr_propose_sentback)) {
i->qsig_data.waitevent = CAPI_QSIG_WAITEVENT_PRPROPOSE;
abstime.tv_sec = time(NULL) + CCQSIG_TIMER_WAIT_PRPROPOSE; /* PR PROPOSE TIMER */
abstime.tv_nsec = 0;
cc_verbose(4, 1, "%s: wait for PATH REPLACEMENT.\n",
i->vname);
if (ast_cond_timedwait(&i->qsig_data.event_trigger, &i->lock, &abstime) != 0) {
cc_log(LOG_WARNING, "%s: timed out waiting for PATH REPLACEMENT.\n",
i->vname);
ret = 0;
} else {
cc_verbose(4, 1, "%s: cond signal received for PATH REPLACEMENT.\n",
i->vname);
}
}
cc_mutex_unlock(&i->lock);
return ret;
}
/*
* check special conditions, wake waiting threads and send outstanding commands
* for the given interface
*/
static void pbx_capi_qsig_post_handling(struct capi_pvt *i)
{
if ((i->qsig_data.waitevent == CAPI_QSIG_WAITEVENT_PRPROPOSE) &&
(i->qsig_data.pr_propose_sentback == 1)) {
i->qsig_data.waitevent = 0;
ast_cond_signal(&i->qsig_data.event_trigger);
cc_verbose(4, 1, "%s: found and signal for PATH REPLACEMENT state.\n",
i->vname);
return;
}
}
/*
* handle a bridge attempt - maybe we're allowed to make a path replacement
*/
int pbx_capi_qsig_bridge(struct capi_pvt *i0,struct capi_pvt *i1)
{
if (i1->qsig_data.pr_propose_sentback) {
return 2; /* Path Replacement already sent out - call will be cleared in short */
}
i1->qsig_data.partner_plci = i0->PLCI;
send_feature_calltransfer(i1);
if (pbx_capi_qsig_wait_for_prpropose(i1))
return 1; /* Path Replacement successful */
/* No Path Replacement - allow line interconnect */
return 0;
}
/*
* CAPI INFO_IND (QSIG part)
*/
@ -1080,10 +1191,13 @@ void pbx_capi_qsig_handle_info_indication(_cmsg *CMSG, unsigned int PLCI, unsign
"()(()()()s)",
fac
);
i->qsig_data.pr_propose_active = 1;
ii->qsig_data.pr_propose_sentback = 1;
} else { /* Path Replacement has to be sent back after Connect on second line */
ii->qsig_data.pr_propose_sendback = 1;
ii->qsig_data.pr_propose_cid = strdup(i->qsig_data.pr_propose_cid);
ii->qsig_data.pr_propose_pn = strdup(i->qsig_data.pr_propose_pn);
ii->qsig_data.pr_propose_active = 1;
}
} else
cc_verbose(1, 1, VERBOSE_PREFIX_4 " * QSIG_PATHREPLACEMENT_PROPOSE: no partner channel found (%#x)\n", i->qsig_data.partner_plci);
@ -1122,31 +1236,8 @@ void pbx_capi_qsig_handle_info_indication(_cmsg *CMSG, unsigned int PLCI, unsign
case 0x8001: /* ALERTING */
/* TODO: some checks, if there's any work here */
if (i->qsig_data.calltransfer_onring) {
unsigned char fac[CAPI_MAX_FACILITYDATAARRAY_SIZE];
struct capi_pvt *ii = capi_find_interface_by_plci(i->qsig_data.partner_plci);
/* needed for Path Replacement */
ii->qsig_data.partner_plci = i->PLCI;
i->qsig_data.calltransfer_onring = 0;
if (ii) {
cc_qsig_do_facility(fac, ii->owner, NULL, 12, 0);
capi_sendf(NULL, 0, CAPI_INFO_REQ, ii->PLCI, get_capi_MessageNumber(),
"()(()()()s)",
fac
);
cc_qsig_do_facility(fac, i->owner, NULL, 12, 1);
capi_sendf(NULL, 0, CAPI_INFO_REQ, i->PLCI, get_capi_MessageNumber(),
"()(()()()s)",
fac
);
} else {
cc_log(LOG_WARNING, "Call Transfer failed - second channel not found (PLCI %#x)!\n", i->qsig_data.partner_plci);
}
send_feature_calltransfer(i);
}
break;
case 0x8002: /* CALL PROCEEDING */
@ -1157,31 +1248,8 @@ void pbx_capi_qsig_handle_info_indication(_cmsg *CMSG, unsigned int PLCI, unsign
break;
case 0x8007: /* CONNECT */
if (i->qsig_data.calltransfer) {
unsigned char fac[CAPI_MAX_FACILITYDATAARRAY_SIZE];
struct capi_pvt *ii = capi_find_interface_by_plci(i->qsig_data.partner_plci);
/* needed for Path Replacement */
ii->qsig_data.partner_plci = i->PLCI;
i->qsig_data.calltransfer = 0;
if (ii) {
cc_qsig_do_facility(fac, ii->owner, NULL, 12, 0);
capi_sendf(NULL, 0, CAPI_INFO_REQ, ii->PLCI, get_capi_MessageNumber(),
"()(()()()s)",
fac
);
cc_qsig_do_facility(fac, i->owner, NULL, 12, 1);
capi_sendf(NULL, 0, CAPI_INFO_REQ, i->PLCI, get_capi_MessageNumber(),
"()(()()()s)",
fac
);
} else {
cc_log(LOG_WARNING, "Call Transfer failed - second channel not found (PLCI %#x)!\n", i->qsig_data.partner_plci);
}
send_feature_calltransfer(i);
}
{
/* handle prior received Path Replacement */
@ -1200,6 +1268,8 @@ void pbx_capi_qsig_handle_info_indication(_cmsg *CMSG, unsigned int PLCI, unsign
i->qsig_data.pr_propose_cid = NULL;
free(i->qsig_data.pr_propose_pn);
i->qsig_data.pr_propose_pn = NULL;
i->qsig_data.pr_propose_sentback = 1;
}
}
@ -1233,6 +1303,7 @@ void pbx_capi_qsig_handle_info_indication(_cmsg *CMSG, unsigned int PLCI, unsign
default:
break;
}
pbx_capi_qsig_post_handling(i);
return;
}

View File

@ -94,7 +94,7 @@ void cc_qsig_op_ecma_isdn_namepres(struct cc_qsig_invokedata *invoke, struct cap
break;
}
cc_verbose(1, 1, VERBOSE_PREFIX_4 " * %s: (%i byte(s)): \"%s\"\n", nametype, namelength, callername);
cc_verbose(1, 1, VERBOSE_PREFIX_4 " * Got %s: \"%s\" (%i byte(s))\n", nametype, callername, namelength);
/* if there was an sequence tag, we have more informations here, but we will ignore it at the moment */
@ -161,7 +161,8 @@ int cc_qsig_encode_ecma_name_invoke(unsigned char * buf, unsigned int *idx, stru
invoke->datalen = dataidx;
memcpy(invoke->data, data, dataidx);
cc_verbose(1, 1, VERBOSE_PREFIX_4 " * Sending \"%s\": (%i byte(s))\n", namebuf, namelen);
return 0;
}
@ -277,7 +278,7 @@ void cc_qsig_op_ecma_isdn_leginfo2(struct cc_qsig_invokedata *invoke, struct cap
pbx_builtin_setvar_helper(i->owner, "_QSIG_LI2_DIVNAME", divertName);
pbx_builtin_setvar_helper(i->owner, "_QSIG_LI2_ODIVNAME", origCalledName);
cc_verbose(1, 1, VERBOSE_PREFIX_4 " * QSIG_LEG_INFO2: %i(%i), %ix %s->%s, %s->%s\n", divReason, orgDivReason, divCount, origCalledNum, divertNum, origCalledName, divertName);
cc_verbose(1, 1, VERBOSE_PREFIX_4 " * Got QSIG_LEG_INFO2: %i(%i), %ix %s->%s, %s->%s\n", divReason, orgDivReason, divCount, origCalledNum, divertNum, origCalledName, divertName);
return;
@ -408,7 +409,7 @@ void cc_qsig_encode_ecma_calltransfer(unsigned char * buf, unsigned int *idx, st
invoke->datalen = ix;
memcpy(invoke->data, c, ix);
cc_verbose(1, 1, VERBOSE_PREFIX_4 " * QSIG_CT: %i->%s\n", info, cid);
cc_verbose(1, 1, VERBOSE_PREFIX_4 " * Sending QSIG_CT: %i->%s\n", info, cid);
if (cid)
free(cid);
@ -484,7 +485,7 @@ void cc_qsig_encode_ecma_sscalltransfer(unsigned char * buf, unsigned int *idx,
invoke->datalen = ix;
memcpy(invoke->data, c, ix);
cc_verbose(1, 1, VERBOSE_PREFIX_4 " * QSIG_SSCT: %s->%s\n", cidsrc, ciddst);
cc_verbose(1, 1, VERBOSE_PREFIX_4 " * Sending QSIG_SSCT: %s->%s\n", cidsrc, ciddst);
}
@ -554,7 +555,7 @@ void cc_qsig_op_ecma_isdn_prpropose(struct cc_qsig_invokedata *invoke, struct ca
i->qsig_data.pr_propose_cid = strdup(callid);
i->qsig_data.pr_propose_pn = strdup(reroutingnr);
cc_verbose(1, 1, VERBOSE_PREFIX_4 " * QSIG_PATHREPLACEMENT_PROPOSE Call identity: %s, Party number: %s (%i)\n", callid, reroutingnr, temp);
cc_verbose(1, 1, VERBOSE_PREFIX_4 " * Got QSIG_PATHREPLACEMENT_PROPOSE Call identity: %s, Party number: %s (%i)\n", callid, reroutingnr, temp);
return;
}
@ -622,7 +623,7 @@ void cc_qsig_encode_ecma_prpropose(unsigned char * buf, unsigned int *idx, struc
invoke->datalen = ix;
memcpy(invoke->data, c, ix);
cc_verbose(1, 1, VERBOSE_PREFIX_4 " * QSIG_PATHREPLACEMENT_PROPOSE: %s,%s\n", callid, reroutingnr);
cc_verbose(1, 1, VERBOSE_PREFIX_4 " * Sending QSIG_PATHREPLACEMENT_PROPOSE: Call identity: %s, Party number: %s\n", callid, reroutingnr);
return;