Introduce new Dial option 's' for stay-online mode to postpone the

DISCONNECT_REQ.
capicommand(hangup) or a timeout will then really disconnect the line.
This is needed for CCBS activation.
This commit is contained in:
MelwareDE 2007-04-23 18:47:49 +00:00
parent 767dd90bed
commit e5b84d2052
4 changed files with 158 additions and 49 deletions

10
README
View File

@ -93,6 +93,10 @@ The Dial string
(Useful if additional digits shall be send afterwards or together
with 'b' to get dialtone and then send the number, e.g. if otherwise
no progress tones are available)
's' : activate 'stay-online': don't disconnect CAPI connection on Hangup.
This is needed to give additional commands like CCBS after Hangup.
To really hangup the CAPI connection, use either capicommand(hangup)
or wait for chan-capi/network timeout (about 20 seconds).
If the <interface-name> is used in dialstring, be sure the name (specified
in capi.conf) does not start with 'contr' or 'g'.
@ -212,6 +216,12 @@ Peer link creation:
Example:
exten => s,1,capicommand(peerlink)
Hangup in mode 'stay-online':
After hangup in 'stay-online' mode, the line isn't really disconnected
until timeout or command:
exten => s,1,capicommand(hangup)
This works after capicommand(peerlink) only.
Set local party to 'busy' or 'free':
Set the local phone to status to 'busy' or 'free' when
awaiting a callback for CCBS/CCNR. If the network wants to

View File

@ -804,6 +804,8 @@ static void interface_cleanup(struct capi_pvt *i)
i->isdnstate = 0;
i->cause = 0;
i->whentohangup = 0;
i->FaxState &= ~CAPI_FAX_STATE_MASK;
i->PLCI = 0;
@ -826,6 +828,7 @@ static void interface_cleanup(struct capi_pvt *i)
i->peer = NULL;
i->owner = NULL;
i->used = NULL;
return;
}
@ -920,6 +923,23 @@ static void send_progress(struct capi_pvt *i)
return;
}
/*
* send disconnect_req
*/
static void capi_send_disconnect(unsigned int PLCI, struct capi_pvt *i)
{
_cmsg CMSG;
DISCONNECT_REQ_HEADER(&CMSG, capi_ApplID, get_capi_MessageNumber(), 0);
DISCONNECT_REQ_PLCI(&CMSG) = PLCI;
if (i) {
_capi_put_cmsg_wait_conf(i, &CMSG);
} else {
_capi_put_cmsg(&CMSG);
}
}
/*
* hangup a line (CAPI messages)
* (this must be called with i->lock held)
@ -952,6 +972,15 @@ static void capi_activehangup(struct ast_channel *c, int state)
_capi_put_cmsg(&CMSG);
return;
}
if ((i->isdnstate & CAPI_ISDN_STATE_STAYONLINE)) {
/* user has requested to leave channel online for further actions
like CCBS */
cc_verbose(2, 1, VERBOSE_PREFIX_4 "%s: disconnect deferred, stay-online mode PLCI=%#x\n",
i->vname, i->PLCI);
i->whentohangup = time(NULL) + 18; /* timeout 18 seconds */
return;
}
/* active disconnect */
if ((i->isdnstate & CAPI_ISDN_STATE_B3_UP)) {
@ -965,9 +994,7 @@ static void capi_activehangup(struct ast_channel *c, int state)
/* CONNECT_CONF not received yet? */
capi_wait_conf(i, CAPI_CONNECT_CONF);
}
DISCONNECT_REQ_HEADER(&CMSG, capi_ApplID, get_capi_MessageNumber(), 0);
DISCONNECT_REQ_PLCI(&CMSG) = i->PLCI;
_capi_put_cmsg_wait_conf(i, &CMSG);
capi_send_disconnect(i->PLCI, i);
}
return;
}
@ -1018,9 +1045,12 @@ static int pbx_capi_hangup(struct ast_channel *c)
/* not disconnected yet, we must actively do it */
capi_activehangup(c, state);
}
i->owner = NULL;
CC_CHANNEL_PVT(c) = NULL;
cc_mutex_unlock(&i->lock);
CC_CHANNEL_PVT(c) = NULL;
ast_setstate(c, AST_STATE_DOWN);
#ifdef CC_AST_HAS_VERSION_1_4
@ -1091,6 +1121,11 @@ static int pbx_capi_call(struct ast_channel *c, char *idest, int timeout)
cc_log(LOG_WARNING, "Default CID already set in '%s'\n", idest);
use_defaultcid = 1;
break;
case 's': /* stay online */
if ((i->isdnstate & CAPI_ISDN_STATE_STAYONLINE))
cc_log(LOG_WARNING, "'stay-online' already set in '%s'\n", idest);
i->isdnstate |= CAPI_ISDN_STATE_STAYONLINE;
break;
case 'q': /* disable QSIG */
cc_verbose(4, 0, VERBOSE_PREFIX_4 "%s: QSIG extensions for this call disabled\n",
i->vname);
@ -1856,6 +1891,7 @@ static struct ast_channel *capi_new(struct capi_pvt *i, int state)
#endif
i->owner = tmp;
i->used = tmp;
#ifdef CC_AST_HAS_VERSION_1_4
ast_atomic_fetchadd_int(&usecnt, 1);
@ -1886,7 +1922,6 @@ 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;
int notfound = 1;
cc_verbose(1, 1, VERBOSE_PREFIX_4 "data = %s format=%d\n", (char *)data, format);
@ -1914,8 +1949,8 @@ pbx_capi_request(const char *type, int format, void *data, int *cause)
cc_mutex_lock(&iflock);
for (i = iflist; (i && notfound); i = i->next) {
if ((i->owner) || (i->channeltype != CAPI_CHANNELTYPE_B)) {
for (i = iflist; i; i = i->next) {
if ((i->used) || (i->channeltype != CAPI_CHANNELTYPE_B)) {
/* if already in use or no real channel */
continue;
}
@ -2362,11 +2397,11 @@ static void start_pbx_on_match(struct capi_pvt *i, unsigned int PLCI, _cword Mes
return;
}
switch(search_did(i->owner)) {
switch(search_did(c)) {
case 0: /* match */
i->isdnstate |= CAPI_ISDN_STATE_PBX;
ast_setstate(c, AST_STATE_RING);
if (ast_pbx_start(i->owner)) {
if (ast_pbx_start(c)) {
cc_log(LOG_ERROR, "%s: Unable to start pbx on channel!\n",
i->vname);
capi_channel_task(c, CAPI_CHANNEL_TASK_HANGUP);
@ -2468,8 +2503,6 @@ static void queue_cause_control(struct capi_pvt *i, int control)
*/
static void capidev_handle_info_disconnect(_cmsg *CMSG, unsigned int PLCI, unsigned int NCCI, struct capi_pvt *i)
{
_cmsg CMSG2;
i->isdnstate |= CAPI_ISDN_STATE_DISCONNECT;
if ((PLCI == i->onholdPLCI) || (i->isdnstate & CAPI_ISDN_STATE_ECT)) {
@ -2477,9 +2510,7 @@ static void capidev_handle_info_disconnect(_cmsg *CMSG, unsigned int PLCI, unsig
i->vname);
/* the caller onhold hung up (or ECTed away) */
/* send a disconnect_req , we cannot hangup the channel here!!! */
DISCONNECT_REQ_HEADER(&CMSG2, capi_ApplID, get_capi_MessageNumber(), 0);
DISCONNECT_REQ_PLCI(&CMSG2) = PLCI;
_capi_put_cmsg(&CMSG2);
capi_send_disconnect(PLCI, NULL);
return;
}
@ -2513,9 +2544,7 @@ static void capidev_handle_info_disconnect(_cmsg *CMSG, unsigned int PLCI, unsig
i->vname);
if (i->FaxState & CAPI_FAX_STATE_ACTIVE) {
/* in fax mode, we just hangup */
DISCONNECT_REQ_HEADER(&CMSG2, capi_ApplID, get_capi_MessageNumber(), 0);
DISCONNECT_REQ_PLCI(&CMSG2) = i->PLCI;
_capi_put_cmsg(&CMSG2);
capi_send_disconnect(i->PLCI, NULL);
return;
}
queue_cause_control(i, 0);
@ -3262,13 +3291,14 @@ static void capidev_handle_disconnect_b3_indication(_cmsg *CMSG, unsigned int PL
}
}
if ((i->state == CAPI_STATE_DISCONNECTING) ||
((!(i->isdnstate & CAPI_ISDN_STATE_B3_SELECT)) &&
(i->FaxState & CAPI_FAX_STATE_SENDMODE))) {
/* active disconnect */
DISCONNECT_REQ_HEADER(&CMSG2, capi_ApplID, get_capi_MessageNumber(), 0);
DISCONNECT_REQ_PLCI(&CMSG2) = PLCI;
_capi_put_cmsg(&CMSG2);
if ((i->state == CAPI_STATE_DISCONNECTING)) {
if (!(i->isdnstate & CAPI_ISDN_STATE_STAYONLINE)) {
/* active disconnect */
capi_send_disconnect(PLCI, NULL);
}
} else if ((!(i->isdnstate & CAPI_ISDN_STATE_B3_SELECT)) &&
(i->FaxState & CAPI_FAX_STATE_SENDMODE)) {
capi_send_disconnect(PLCI, NULL);
}
capi_controllers[i->controller]->nfreebchannels++;
@ -3421,8 +3451,8 @@ static void capidev_handle_connect_indication(_cmsg *CMSG, unsigned int PLCI, un
/* well...somebody is calling us. let's set up a channel */
cc_mutex_lock(&iflock);
for (i = iflist; i; i = i->next) {
if (i->owner) {
/* has already owner */
if (i->used) {
/* is already used */
continue;
}
if (i->controller != controller) {
@ -4258,6 +4288,46 @@ static int pbx_capi_holdtype(struct ast_channel *c, char *param)
return 0;
}
/*
* send the disconnect commands to capi
*/
static void capi_disconnect(struct capi_pvt *i)
{
cc_mutex_lock(&i->lock);
i->isdnstate &= ~CAPI_ISDN_STATE_STAYONLINE;
if ((i->isdnstate & CAPI_ISDN_STATE_B3_UP)) {
cc_disconnect_b3(i, 0);
} else {
capi_send_disconnect(i->PLCI, NULL);
}
cc_mutex_unlock(&i->lock);
}
/*
* really hangup a channel if the stay-online mode was activated
*/
static int pbx_capi_realhangup(struct ast_channel *c, char *param)
{
struct capi_pvt *i;
cc_mutex_lock(&iflock);
for (i = iflist; i; i = i->next) {
if (i->peer == c)
break;
}
cc_mutex_unlock(&iflock);
if ((i) && (i->state == CAPI_STATE_DISCONNECTING)) {
cc_verbose(3, 1, VERBOSE_PREFIX_2 "%s: capi command hangup PLCI=0x%#x.\n",
i->vname, i->PLCI);
capi_disconnect(i);
}
return 0;
}
/*
* set early-B3 (progress) for incoming connections
* (only for NT mode)
@ -4392,6 +4462,7 @@ static struct capicommands_s {
{ "ccbs", pbx_capi_ccbs, 0 },
{ "ccbsstop", pbx_capi_ccbsstop, 0 },
{ "ccpartybusy", pbx_capi_ccpartybusy, 0 },
{ "hangup", pbx_capi_realhangup, 0 },
{ "qsig_ssct", pbx_capi_qsig_ssct, 1 },
{ "qsig_ct", pbx_capi_qsig_ct, 1 },
{ "qsig_callmark",pbx_capi_qsig_callmark, 1 },
@ -4641,6 +4712,26 @@ static void capi_do_channel_task(void)
channel_task = CAPI_CHANNEL_TASK_NONE;
}
/*
* check for tasks every second
*/
static void capidev_run_secondly(time_t now)
{
struct capi_pvt *i;
/* check for channels to hangup (timeout) */
cc_mutex_lock(&iflock);
for (i = iflist; i; i = i->next) {
if ((i->used) && (i->whentohangup) && (i->whentohangup < now)) {
cc_verbose(3, 1, VERBOSE_PREFIX_2 "%s: stay-online timeout, hanging up.\n",
i->vname);
i->whentohangup = 0;
capi_disconnect(i);
}
}
cc_mutex_unlock(&iflock);
}
/*
* module stuff, monitor...
*/
@ -4648,6 +4739,8 @@ static void *capidev_loop(void *data)
{
unsigned int Info;
_cmsg monCMSG;
time_t lastcall = 0;
time_t newtime;
for (/* for ever */;;) {
switch(Info = capidev_check_wait_get_cmsg(&monCMSG)) {
@ -4669,6 +4762,11 @@ static void *capidev_loop(void *data)
/* something is wrong! */
break;
} /* switch */
newtime = time(NULL);
if (lastcall != newtime) {
lastcall = newtime;
capidev_run_secondly(newtime);
}
} /* for */
/* never reached */
@ -5679,8 +5777,8 @@ int unload_module(void)
i = iflist;
while (i) {
if (i->owner)
cc_log(LOG_WARNING, "On unload, interface still has owner.\n");
if ((i->owner) || (i->used))
cc_log(LOG_WARNING, "On unload, interface still has owner or is used.\n");
if (i->smoother)
ast_smoother_free(i->smoother);
cc_mutex_destroy(&i->lock);

View File

@ -243,7 +243,8 @@ struct cc_capi_gains {
#define CAPI_ISDN_STATE_EC 0x00002000
#define CAPI_ISDN_STATE_DTMF 0x00004000
#define CAPI_ISDN_STATE_B3_SELECT 0x00008000
#define CAPI_ISDN_STATE_3PTY 0x10000000
#define CAPI_ISDN_STATE_STAYONLINE 0x00010000
#define CAPI_ISDN_STATE_3PTY 0x10000000
#define CAPI_ISDN_STATE_PBX_DONT 0x40000000
#define CAPI_ISDN_STATE_PBX 0x80000000
@ -288,9 +289,10 @@ struct capi_pvt {
char vname[CAPI_MAX_STRING];
unsigned char tmpbuf[CAPI_MAX_STRING];
/*! Channel who used us, possibly NULL */
struct ast_channel *used;
/*! Channel we belong to, possibly NULL */
struct ast_channel *owner;
/*! Channel who called us, possibly NULL */
struct ast_channel *peer;
@ -413,6 +415,8 @@ struct capi_pvt {
unsigned int reason;
unsigned int reasonb3;
time_t whentohangup;
/* RTP */
struct ast_rtp *rtp;
int capability;

View File

@ -223,30 +223,27 @@ MESSAGE_EXCHANGE_ERROR capidev_check_wait_get_cmsg(_cmsg *CMSG)
MESSAGE_EXCHANGE_ERROR Info;
struct timeval tv;
repeat:
Info = capi_get_cmsg(CMSG, capi_ApplID);
tv.tv_sec = 0;
tv.tv_usec = 500000;
#if (CAPI_OS_HINT == 1) || (CAPI_OS_HINT == 2)
/*
* For BSD allow controller 0:
*/
if ((HEADER_CID(CMSG) & 0xFF) == 0) {
HEADER_CID(CMSG) += capi_num_controllers;
}
#endif
Info = capi20_waitformessage(capi_ApplID, &tv);
/* if queue is empty */
if (Info == 0x1104) {
/* try waiting a maximum of 0.100 seconds for a message */
tv.tv_sec = 0;
tv.tv_usec = 10000;
if (Info == 0x0000) {
Info = capi20_waitformessage(capi_ApplID, &tv);
Info = capi_get_cmsg(CMSG, capi_ApplID);
if (Info == 0x0000)
goto repeat;
if (Info == 0x0000) {
#if (CAPI_OS_HINT == 1) || (CAPI_OS_HINT == 2)
/*
* For BSD allow controller 0:
*/
if ((HEADER_CID(CMSG) & 0xFF) == 0) {
HEADER_CID(CMSG) += capi_num_controllers;
}
#endif
}
}
if ((Info != 0x0000) && (Info != 0x1104)) {
if (capidebug) {
cc_log(LOG_DEBUG, "Error waiting for cmsg... INFO = %#x\n", Info);