Index: channels/chan_zap.c =================================================================== --- channels/chan_zap.c (.../trunk/channels/chan_zap.c) (révision 38) +++ channels/chan_zap.c (.../branches/newmwi/channels/chan_zap.c) (révision 40) @@ -224,6 +224,16 @@ #define DCHAN_AVAILABLE (DCHAN_PROVISIONED | DCHAN_NOTINALARM | DCHAN_UP) +/* +* Message Waiting Indication related defines +* +* What to do when mailbox state changed for a FXS channel? +*/ +/*! Send mwi cidspill while on-hook (default mode) */ +#define OMC_MWI 0 +/*! Jump to "mboxchanged" extension from channel's context using a local channel */ +#define OMC_JUMP 1 + /* Overlap dialing option types */ #define ZAP_OVERLAPDIAL_NONE 0 #define ZAP_OVERLAPDIAL_OUTGOING 1 @@ -557,6 +567,7 @@ unsigned int use_callerid:1; /*!< Whether or not to use caller id on this channel */ unsigned int use_callingpres:1; /*!< Whether to use the callingpres the calling switch sends */ unsigned int usedistinctiveringdetection:1; + unsigned int callerid_is_mwi:1; /*!< Whether or not to use mwi instead of caller id on this channel */ unsigned int zaptrcallerid:1; /*!< should we use the callerid from incoming call on zap transfer or not */ unsigned int transfertobusy:1; /*!< allow flash-transfers to busy channels */ /* Channel state or unavilability flags */ @@ -611,6 +622,7 @@ time_t guardtime; /*!< Must wait this much time before using for new call */ int cid_signalling; /*!< CID signalling type bell202 or v23 */ int cid_start; /*!< CID start indicator, polarity or ring */ + int on_mbox_change; /*!< What to do when mailbox state changed */ int callingpres; /*!< The value of callling presentation that we're going to use when placing a PRI call */ int callwaitingrepeat; /*!< How many samples to wait before repeating call waiting */ int cidcwexpire; /*!< When to expire our muting for CID/CW */ @@ -758,8 +770,12 @@ .accountcode = "", .mailbox = "", + + /* what will we do when mailbox state changed for an FXS channel? + * by default send MWI cidspill while on-hook + */ + .on_mbox_change = OMC_MWI, - .polarityonanswerdelay = 600, .sendcalleridafter = DEFAULT_CIDRINGS @@ -1966,6 +1982,7 @@ struct zt_pvt *p = ast->tech_pvt; int x, res, index,mysig; char *c, *n, *l; + const char* has_vm; #ifdef HAVE_PRI char *s = NULL; #endif @@ -2020,7 +2037,20 @@ } p->callwaitcas = 0; if ((p->cidspill = ast_malloc(MAX_CALLERID_SIZE))) { - p->cidlen = ast_callerid_generate(p->cidspill, ast->cid.cid_name, ast->cid.cid_num, AST_LAW(p)); + /* + * Is this channel configured to send callerid info + * or message waiting indication ? + */ + if(p->callerid_is_mwi) { + has_vm = pbx_builtin_getvar_helper(ast, "HASVOICEMAIL"); + /* for now mwi is sent using Full MDMF format */ + p->cidlen = ast_vmwi_generate(p->cidspill, + (ast_true(has_vm)? 1 : 0), CID_MWI_TYPE_MDMF_FULL, + ast->cid.cid_name, ast->cid.cid_num, AST_LAW(p)); + } else { + p->cidlen = ast_callerid_generate(p->cidspill, ast->cid.cid_name, + ast->cid.cid_num, AST_LAW(p)); + } p->cidpos = 0; send_callerid(p); } @@ -3014,7 +3044,7 @@ #endif restart_monitor(); } - + p->callerid_is_mwi = 0; p->callwaitingrepeat = 0; p->cidcwexpire = 0; p->oprmode = 0; @@ -4171,7 +4201,14 @@ case SIG_FXOGS: case SIG_FXOKS: p->onhooktime = time(NULL); - p->msgstate = -1; + + /* only reset message state if we are in 'send mwi' mode + * because mbox state didn't change (we are only going on-hook) + */ + if(p->on_mbox_change == OMC_MWI) { + p->msgstate = -1; + } + /* Check for some special conditions regarding call waiting */ if (index == SUB_REAL) { /* The normal line was hung up */ @@ -4466,6 +4503,13 @@ p->subs[index].f.subclass = AST_CONTROL_RINGING; break; case ZT_EVENT_RINGERON: + /* + * For MWI indication we should stop the ringer after cidspill is sent + * because rings are not allowed after mwi cidspill + */ + if((ast->rings == p->cidrings) && p->callerid_is_mwi) { + zt_set_hook(p->subs[index].zfd, ZT_RINGOFF); + } break; case ZT_EVENT_NOALARM: p->inalarm = 0; @@ -6893,6 +6937,22 @@ if (smdi_msg) ASTOBJ_UNREF(smdi_msg, ast_smdi_md_message_destroy); + if (callerid_is_mwi(cs) && (callerid_is_mwi_on(cs) != -1)) { + /* callerid contains Message Waiting Indication */ + pbx_builtin_setvar_helper(chan, "HASVOICEMAIL", + (callerid_is_mwi_on(cs)? "1":"0")); + + /* does mwi extension exists ? */ + if (ast_exists_extension(chan, chan->context, "mwi", 1, chan->cid.cid_num)) { + ast_verb(3, "Starting %s from mwi extension\n", chan->name); + /* starting extension for channel will be "mwi" */ + strcpy(chan->exten, "mwi"); + } else { + ast_log(LOG_NOTICE, "Message Waiting Indication detected, but no 'mwi'" + " extension for %s\n", chan->name); + } + } + if (cs) callerid_free(cs); @@ -7139,6 +7199,133 @@ return 0; } +/*! \internal +* \brief What to do when we detected that mailbox state changed for this channel? +* \param p the channel which mailbox state changed +* \param newstate new state for the mailbox +* \param cur_time the time from which we should make checks (almost current time) +* \return 1 if the change is acknowledged and 0 otherwise +* \note This routine has side-effects on 'p' fields, one of these is that +* it may update the msgstate to the newstate. +* \note It is assumed that p is an FXS channel and that it is free +* \note It is assumed that we hold iflock +* \warning the iflock maybe released during a while during execution +*/ +static inline int handle_mbox_change(struct zt_pvt *p, int newstate, time_t cur_time) +{ + int x, res; + char localtype[AST_MAX_CONTEXT+15]; + int state; + struct ast_channel *chan; + + /* the check below is theorically only needed for + * on-hook transfer mode because it checks that any previous on-hook transfer is + * finished on the channel. + * + * Anyway we check it even for other modes (OMC_JUMP...) because it may avoid races. + * One of these is due to zaptel implementation of KewlStart which is somewhat + * buggy (it does kewlstart even when other party is onhook). + * + * At the time you read this it may be already fixed in zaptel but take in account + * that getting mwi when you've been on-hook for 4 seconds at least is not + * a bad user experience at all. + */ + if (cur_time - p->onhooktime < 4) { + return 0; + } + + ast_debug(1, "Message status for %s changed from %d to %d on %d\n", + p->mailbox, p->msgstate, newstate, p->channel); + +#ifdef ZT_VMWI + /* FIXME: add this as an option because only a few + * hardware support ZT_VMWI. + */ + res = ioctl(p->subs[SUB_REAL].zfd, ZT_VMWI, newstate); + if (res) + ast_log(LOG_DEBUG, "Unable to control message waiting led on channel %d\n", + p->channel); +#endif + + if (p->on_mbox_change == OMC_JUMP) { + /* acknowledge this try, won't try again even if failed */ + p->msgstate = newstate; + /* + * workarround so as to let the 'p' channel free: + * we start the pbx on a local channel that way p is not owned and + * can be dialed from pbx extension + * note: mboxchanged must exist in p's context + * warning: take care of not anwering any call made from this channel in + * the dialplan as it will loop on mboxchanged extension + */ + snprintf(localtype, sizeof(localtype), "mboxchanged@%s/n", p->context); + /* + * prefer to unlock the list as we may + * do some huge work now + */ + ast_mutex_unlock(&iflock); + + chan = ast_request("Local", AST_FORMAT_ALAW | AST_FORMAT_ULAW, localtype, + &state); + + if(chan) { + /* That variable should be inherited at least once */ + pbx_builtin_setvar_helper(chan, "_HASVOICEMAIL", (newstate? "1":"0")); + /* + * choose mboxchanged as starting point + * since local channel would point at 's' by default + */ + strcpy(chan->exten, "mboxchanged"); + + if(ast_pbx_start(chan)) { + ast_log(LOG_WARNING, "Couldn't start PBX on %s\n", chan->name); + ast_hangup(chan); + } + } else { + /* + * We failed to request that channel, maybe because + * mboxchanged extension doest not exist + */ + ast_log(LOG_WARNING, "MailBox changed it's state but couldn't get a channel," + "does 'mboxchanged' extension exists for Zap/%d?\n", p->channel); + } + ast_mutex_lock(&iflock); + /* + * we intentionnaly let channel to be processed as normal + * so as to get events in case of going off hook... + * => mboxchanged extension should correctly handle busy states + */ + return 1; + } else { + /* Default Mode (OMC_MWI): Send mwi cidspill */ + /* + * Check that we don't already own a cidspill + * note: we've already checked that any previous on-hook transfer is finished + */ + if (!p->cidspill) { + x = ZT_FLUSH_BOTH; + res = ioctl(p->subs[SUB_REAL].zfd, ZT_FLUSH, &x); + if (res) { + ast_log(LOG_WARNING, "Unable to flush input on channel %d\n", + p->channel); + } + if ((p->cidspill = ast_calloc(1, MAX_CALLERID_SIZE))) { + /* Turn on on hook transfer for 4 seconds */ + x = 4000; + ioctl(p->subs[SUB_REAL].zfd, ZT_ONHOOKTRANSFER, &x); + p->cidlen = ast_vmwi_generate(p->cidspill, newstate, + CID_MWI_TYPE_MDMF, NULL, NULL, AST_LAW(p)); + p->cidpos = 0; + p->msgstate = res; + p->onhooktime = cur_time; + } + return 1; + } + } + return 0; +} + + static void *do_monitor(void *data) { int count, res, res2, spoint, pollres=0; @@ -7220,31 +7407,12 @@ if (!found && ((i == last) || ((i == iflist) && !last))) { last = i; if (last) { - if (!last->cidspill && !last->owner && !ast_strlen_zero(last->mailbox) && (thispass - last->onhooktime > 3) && + if (!last->owner && !ast_strlen_zero(last->mailbox) && (last->sig & __ZT_SIG_FXO)) { res = has_voicemail(last); if (last->msgstate != res) { - int x; - ast_debug(1, "Message status for %s changed from %d to %d on %d\n", last->mailbox, last->msgstate, res, last->channel); -#ifdef ZT_VMWI - res2 = ioctl(last->subs[SUB_REAL].zfd, ZT_VMWI, res); - if (res2) - ast_log(LOG_DEBUG, "Unable to control message waiting led on channel %d\n", last->channel); -#endif - x = ZT_FLUSH_BOTH; - res2 = ioctl(last->subs[SUB_REAL].zfd, ZT_FLUSH, &x); - if (res2) - ast_log(LOG_WARNING, "Unable to flush input on channel %d\n", last->channel); - if ((last->cidspill = ast_calloc(1, MAX_CALLERID_SIZE))) { - /* Turn on on hook transfer for 4 seconds */ - x = 4000; - ioctl(last->subs[SUB_REAL].zfd, ZT_ONHOOKTRANSFER, &x); - last->cidlen = vmwi_generate(last->cidspill, res, 1, AST_LAW(last)); - last->cidpos = 0; - last->msgstate = res; - last->onhooktime = thispass; - } - found ++; + if (handle_mbox_change(last, res, thispass)) + found++; } } last = last->next; @@ -7864,6 +8032,8 @@ tmp->use_callerid = conf.chan.use_callerid; tmp->cid_signalling = conf.chan.cid_signalling; tmp->cid_start = conf.chan.cid_start; + tmp->callerid_is_mwi = 0; + tmp->on_mbox_change = conf.chan.on_mbox_change; tmp->zaptrcallerid = conf.chan.zaptrcallerid; tmp->restrictcid = conf.chan.restrictcid; tmp->use_callingpres = conf.chan.use_callingpres; @@ -8367,6 +8537,18 @@ if (opt == 'c') { /* Confirm answer */ p->confirmanswer = 1; + } else if (opt == 'm') { + /* + * Instead of sending callerid only + * we will be sending message waiting indicator also + */ + p->callerid_is_mwi = 1; + + /* we can use distinctivering with mwi */ + if(res >= 3) { + p->distinctivering = y; + } + } else if (opt == 'r') { /* Distinctive ring */ if (res < 3) @@ -12215,6 +12397,16 @@ confp->chan.cid_start = CID_START_POLARITY; else if (ast_true(v->value)) confp->chan.cid_start = CID_START_RING; + } else if(!strcasecmp(v->name, "onmboxchange")) { + if(!strcasecmp(v->value, "jumptomboxchanged")) { + confp->chan.on_mbox_change = OMC_JUMP; + } else if(!strcasecmp(v->value, "sendmwi")){ + confp->chan.on_mbox_change = OMC_MWI; + } else { + ast_log(LOG_WARNING, "Unknown onmboxchange value '%s'," + "assuming 'sendmwi'\n", v->value); + confp->chan.on_mbox_change = OMC_MWI; + } } else if (!strcasecmp(v->name, "threewaycalling")) { confp->chan.threewaycalling = ast_true(v->value); } else if (!strcasecmp(v->name, "cancallforward")) {