Index: channels/sig_pri.h =================================================================== --- channels/sig_pri.h (.../trunk) (revision 295977) +++ channels/sig_pri.h (.../team/rmudgett/align_analog) (revision 295995) @@ -90,10 +90,10 @@ void (* const unlock_private)(void *pvt); /* Lock the private in the signalling private structure. ... */ void (* const lock_private)(void *pvt); - /* Function which is called back to handle any other DTMF up events that are received. Called by analog_handle_event. Why is this + /* Function which is called back to handle any other DTMF events that are received. Called by analog_handle_event. Why is this * important to use, instead of just directly using events received before they are passed into the library? Because sometimes, * (CWCID) the library absorbs DTMF events received. */ - //void (* const handle_dtmfup)(void *pvt, struct ast_channel *ast, enum analog_sub analog_index, struct ast_frame **dest); + //void (* const handle_dtmf)(void *pvt, struct ast_channel *ast, enum analog_sub analog_index, struct ast_frame **dest); //int (* const dial_digits)(void *pvt, enum analog_sub sub, struct analog_dialoperation *dop); int (* const play_tone)(void *pvt, enum sig_pri_tone tone); Index: channels/chan_dahdi.c =================================================================== --- channels/chan_dahdi.c (.../trunk) (revision 295977) +++ channels/chan_dahdi.c (.../team/rmudgett/align_analog) (revision 295995) @@ -492,11 +492,12 @@ #define MASK_AVAIL (1 << 0) /*!< Channel available for PRI use */ #define MASK_INUSE (1 << 1) /*!< Channel currently in use */ -#define CALLWAITING_SILENT_SAMPLES ( (300 * 8) / READ_SIZE) /*!< 300 ms */ -#define CALLWAITING_REPEAT_SAMPLES ( (10000 * 8) / READ_SIZE) /*!< 10,000 ms */ -#define CIDCW_EXPIRE_SAMPLES ( (500 * 8) / READ_SIZE) /*!< 500 ms */ -#define MIN_MS_SINCE_FLASH ( (2000) ) /*!< 2000 ms */ -#define DEFAULT_RINGT ( (8000 * 8) / READ_SIZE) /*!< 8,000 ms */ +#define CALLWAITING_SILENT_SAMPLES ((300 * 8) / READ_SIZE) /*!< 300 ms */ +#define CALLWAITING_REPEAT_SAMPLES ((10000 * 8) / READ_SIZE) /*!< 10,000 ms */ +#define CALLWAITING_SUPPRESS_SAMPLES ((100 * 8) / READ_SIZE) /*!< 100 ms */ +#define CIDCW_EXPIRE_SAMPLES ((500 * 8) / READ_SIZE) /*!< 500 ms */ +#define MIN_MS_SINCE_FLASH ((2000) ) /*!< 2000 ms */ +#define DEFAULT_RINGT ((8000 * 8) / READ_SIZE) /*!< 8,000 ms */ struct dahdi_pvt; @@ -1053,7 +1054,8 @@ struct timeval dtmfcid_delay; /*!< Time value used for allow line to settle */ int callingpres; /*!< The value of calling 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 */ + int cidcwexpire; /*!< When to stop waiting for CID/CW CAS response (In samples) */ + int cid_suppress_expire; /*!< How many samples to suppress after a CID spill. */ /*! \brief Analog caller ID waveform sample buffer */ unsigned char *cidspill; /*! \brief Position in the cidspill buffer to send out next. */ @@ -1074,7 +1076,12 @@ * characters are processed. */ int stripmsd; - /*! \brief BOOLEAN. XXX Meaning what?? */ + /*! + * \brief TRUE if Call Waiting (CW) CPE Alert Signal (CAS) is being sent. + * \note + * After CAS is sent, the call waiting caller id will be sent if the phone + * gives a positive reply. + */ int callwaitcas; /*! \brief Number of call waiting rings. */ int callwaitrings; @@ -1839,18 +1846,19 @@ return 0; } -static int send_callerid(struct dahdi_pvt *p); - static int my_stop_callwait(void *pvt) { struct dahdi_pvt *p = pvt; p->callwaitingrepeat = 0; p->cidcwexpire = 0; + p->cid_suppress_expire = 0; return 0; } +static int send_callerid(struct dahdi_pvt *p); static int save_conference(struct dahdi_pvt *p); +static int restore_conference(struct dahdi_pvt *p); static int my_callwait(void *pvt) { @@ -1860,6 +1868,11 @@ ast_log(LOG_WARNING, "Spill already exists?!?\n"); ast_free(p->cidspill); } + + /* + * SAS: Subscriber Alert Signal, 440Hz for 300ms + * CAS: CPE Alert Signal, 2130Hz * 2750Hz sine waves + */ if (!(p->cidspill = ast_malloc(2400 /* SAS */ + 680 /* CAS */ + READ_SIZE * 4))) return -1; save_conference(p); @@ -1898,6 +1911,8 @@ caller->id.number.str, AST_LAW(p)); } else { + ast_verb(3, "CPE supports Call Waiting Caller*ID. Sending '%s/%s'\n", + caller->id.name.str, caller->id.number.str); p->callwaitcas = 0; p->cidcwexpire = 0; p->cidlen = ast_callerid_callwaiting_generate(p->cidspill, @@ -1907,6 +1922,7 @@ p->cidlen += READ_SIZE * 4; } p->cidpos = 0; + p->cid_suppress_expire = 0; send_callerid(p); } return 0; @@ -1978,68 +1994,72 @@ static inline int dahdi_confmute(struct dahdi_pvt *p, int muted); -static void my_handle_dtmfup(void *pvt, struct ast_channel *ast, enum analog_sub analog_index, struct ast_frame **dest) +static void my_handle_dtmf(void *pvt, struct ast_channel *ast, enum analog_sub analog_index, struct ast_frame **dest) { struct ast_frame *f = *dest; struct dahdi_pvt *p = pvt; int idx = analogsub_to_dahdisub(analog_index); - ast_debug(1, "DTMF digit: %c on %s\n", f->subclass.integer, ast->name); + ast_debug(1, "%s DTMF digit: 0x%02X '%c' on %s\n", + f->frametype == AST_FRAME_DTMF_BEGIN ? "Begin" : "End", + f->subclass.integer, f->subclass.integer, ast->name); if (f->subclass.integer == 'f') { - /* Fax tone -- Handle and return NULL */ - if ((p->callprogress & CALLPROGRESS_FAX) && !p->faxhandled) { - /* If faxbuffers are configured, use them for the fax transmission */ - if (p->usefaxbuffers && !p->bufferoverrideinuse) { - struct dahdi_bufferinfo bi = { - .txbufpolicy = p->faxbuf_policy, - .bufsize = p->bufsize, - .numbufs = p->faxbuf_no - }; - int res; + if (f->frametype == AST_FRAME_DTMF_END) { + /* Fax tone -- Handle and return NULL */ + if ((p->callprogress & CALLPROGRESS_FAX) && !p->faxhandled) { + /* If faxbuffers are configured, use them for the fax transmission */ + if (p->usefaxbuffers && !p->bufferoverrideinuse) { + struct dahdi_bufferinfo bi = { + .txbufpolicy = p->faxbuf_policy, + .bufsize = p->bufsize, + .numbufs = p->faxbuf_no + }; + int res; - if ((res = ioctl(p->subs[idx].dfd, DAHDI_SET_BUFINFO, &bi)) < 0) { - ast_log(LOG_WARNING, "Channel '%s' unable to set buffer policy, reason: %s\n", ast->name, strerror(errno)); - } else { - p->bufferoverrideinuse = 1; + if ((res = ioctl(p->subs[idx].dfd, DAHDI_SET_BUFINFO, &bi)) < 0) { + ast_log(LOG_WARNING, "Channel '%s' unable to set buffer policy, reason: %s\n", ast->name, strerror(errno)); + } else { + p->bufferoverrideinuse = 1; + } } - } - p->faxhandled = 1; - if (p->dsp) { - p->dsp_features &= ~DSP_FEATURE_FAX_DETECT; - ast_dsp_set_features(p->dsp, p->dsp_features); - ast_debug(1, "Disabling FAX tone detection on %s after tone received\n", ast->name); - } - if (strcmp(ast->exten, "fax")) { - const char *target_context = S_OR(ast->macrocontext, ast->context); + p->faxhandled = 1; + if (p->dsp) { + p->dsp_features &= ~DSP_FEATURE_FAX_DETECT; + ast_dsp_set_features(p->dsp, p->dsp_features); + ast_debug(1, "Disabling FAX tone detection on %s after tone received\n", ast->name); + } + if (strcmp(ast->exten, "fax")) { + const char *target_context = S_OR(ast->macrocontext, ast->context); - /* We need to unlock 'ast' here because ast_exists_extension has the - * potential to start autoservice on the channel. Such action is prone - * to deadlock. - */ - ast_mutex_unlock(&p->lock); - ast_channel_unlock(ast); - if (ast_exists_extension(ast, target_context, "fax", 1, - S_COR(ast->caller.id.number.valid, ast->caller.id.number.str, NULL))) { - ast_channel_lock(ast); - ast_mutex_lock(&p->lock); - ast_verb(3, "Redirecting %s to fax extension\n", ast->name); - /* Save the DID/DNIS when we transfer the fax call to a "fax" extension */ - pbx_builtin_setvar_helper(ast, "FAXEXTEN", ast->exten); - if (ast_async_goto(ast, target_context, "fax", 1)) - ast_log(LOG_WARNING, "Failed to async goto '%s' into fax of '%s'\n", ast->name, target_context); + /* We need to unlock 'ast' here because ast_exists_extension has the + * potential to start autoservice on the channel. Such action is prone + * to deadlock. + */ + ast_mutex_unlock(&p->lock); + ast_channel_unlock(ast); + if (ast_exists_extension(ast, target_context, "fax", 1, + S_COR(ast->caller.id.number.valid, ast->caller.id.number.str, NULL))) { + ast_channel_lock(ast); + ast_mutex_lock(&p->lock); + ast_verb(3, "Redirecting %s to fax extension\n", ast->name); + /* Save the DID/DNIS when we transfer the fax call to a "fax" extension */ + pbx_builtin_setvar_helper(ast, "FAXEXTEN", ast->exten); + if (ast_async_goto(ast, target_context, "fax", 1)) + ast_log(LOG_WARNING, "Failed to async goto '%s' into fax of '%s'\n", ast->name, target_context); + } else { + ast_channel_lock(ast); + ast_mutex_lock(&p->lock); + ast_log(LOG_NOTICE, "Fax detected, but no fax extension\n"); + } } else { - ast_channel_lock(ast); - ast_mutex_lock(&p->lock); - ast_log(LOG_NOTICE, "Fax detected, but no fax extension\n"); + ast_debug(1, "Already in a fax extension, not redirecting\n"); } } else { - ast_debug(1, "Already in a fax extension, not redirecting\n"); + ast_debug(1, "Fax already handled\n"); } - } else { - ast_debug(1, "Fax already handled\n"); + dahdi_confmute(p, 0); } - dahdi_confmute(p, 0); p->subs[idx].f.frametype = AST_FRAME_NULL; p->subs[idx].f.subclass.integer = 0; *dest = &p->subs[idx].f; @@ -2225,13 +2245,20 @@ return 0; } +static void my_set_callwaiting(void *pvt, int callwaiting_enable) +{ + struct dahdi_pvt *p = pvt; + + p->callwaiting = callwaiting_enable; +} + static void my_cancel_cidspill(void *pvt) { struct dahdi_pvt *p = pvt; - if (p->cidspill) { - ast_free(p->cidspill); - p->cidspill = NULL; - } + + ast_free(p->cidspill); + p->cidspill = NULL; + restore_conference(p); } static int my_confmute(void *pvt, int mute) @@ -3504,7 +3531,7 @@ .lock_private = my_lock_private, .unlock_private = my_unlock_private, .deadlock_avoidance_private = my_deadlock_avoidance_private, - .handle_dtmfup = my_handle_dtmfup, + .handle_dtmf = my_handle_dtmf, .wink = my_wink, .new_ast_channel = my_new_analog_ast_channel, .dsp_set_digitmode = my_dsp_set_digitmode, @@ -3532,6 +3559,7 @@ .check_waitingfordt = my_check_waitingfordt, .set_confirmanswer = my_set_confirmanswer, .check_confirmanswer = my_check_confirmanswer, + .set_callwaiting = my_set_callwaiting, .cancel_cidspill = my_cancel_cidspill, .confmute = my_confmute, .set_pulsedial = my_set_pulsedial, @@ -5049,17 +5077,16 @@ ast_log(LOG_WARNING, "Unable to restore conference info: %s\n", strerror(errno)); return -1; } + ast_debug(1, "Restored conferencing\n"); } - ast_debug(1, "Restored conferencing\n"); return 0; } -static int send_callerid(struct dahdi_pvt *p); - static int send_cwcidspill(struct dahdi_pvt *p) { p->callwaitcas = 0; p->cidcwexpire = 0; + p->cid_suppress_expire = 0; if (!(p->cidspill = ast_malloc(MAX_CALLERID_SIZE))) return -1; p->cidlen = ast_callerid_callwaiting_generate(p->cidspill, p->callwait_name, p->callwait_num, AST_LAW(p)); @@ -5122,11 +5149,13 @@ return 0; p->cidpos += res; } + p->cid_suppress_expire = CALLWAITING_SUPPRESS_SAMPLES; ast_free(p->cidspill); p->cidspill = NULL; if (p->callwaitcas) { /* Wait for CID/CW to expire */ p->cidcwexpire = CIDCW_EXPIRE_SAMPLES; + p->cid_suppress_expire = p->cidcwexpire; } else restore_conference(p); return 0; @@ -5140,6 +5169,11 @@ ast_log(LOG_WARNING, "Spill already exists?!?\n"); ast_free(p->cidspill); } + + /* + * SAS: Subscriber Alert Signal, 440Hz for 300ms + * CAS: CPE Alert Signal, 2130Hz * 2750Hz sine waves + */ if (!(p->cidspill = ast_malloc(2400 /* SAS */ + 680 /* CAS */ + READ_SIZE * 4))) return -1; save_conference(p); @@ -5615,9 +5649,7 @@ break; } } - if (p->cidspill) { - ast_free(p->cidspill); - } + ast_free(p->cidspill); if (p->use_smdi) ast_smdi_interface_unref(p->smdi_iface); if (p->mwi_event_sub) @@ -6339,11 +6371,11 @@ p->callwaitingrepeat = 0; p->cidcwexpire = 0; + p->cid_suppress_expire = 0; p->oprmode = 0; ast->tech_pvt = NULL; hangup_out: - if (p->cidspill) - ast_free(p->cidspill); + ast_free(p->cidspill); p->cidspill = NULL; ast_mutex_unlock(&p->lock); @@ -7037,6 +7069,24 @@ return AST_BRIDGE_RETRY; } + if ((p0->callwaiting && p0->callwaitingcallerid) + || (p1->callwaiting && p1->callwaitingcallerid)) { + /* + * Call Waiting Caller ID requires DTMF detection to know if it + * can send the CID spill. + * + * For now, don't attempt to native bridge if either channel + * needs DTMF detection. There is code below to handle it + * properly until DTMF is actually seen, but due to currently + * unresolved issues it's ignored... + */ + ast_mutex_unlock(&p0->lock); + ast_mutex_unlock(&p1->lock); + ast_channel_unlock(c0); + ast_channel_unlock(c1); + return AST_BRIDGE_FAILED_NOWARN; + } + #if defined(HAVE_PRI) if ((dahdi_sig_pri_lib_handles(p0->sig) && ((struct sig_pri_chan *) p0->sig_pvt)->no_b_channel) @@ -7474,87 +7524,98 @@ return DAHDI_ALARM_NONE; } -static void dahdi_handle_dtmfup(struct ast_channel *ast, int idx, struct ast_frame **dest) +static void dahdi_handle_dtmf(struct ast_channel *ast, int idx, struct ast_frame **dest) { struct dahdi_pvt *p = ast->tech_pvt; struct ast_frame *f = *dest; - ast_debug(1, "DTMF digit: %c on %s\n", (int) f->subclass.integer, ast->name); + ast_debug(1, "%s DTMF digit: 0x%02X '%c' on %s\n", + f->frametype == AST_FRAME_DTMF_BEGIN ? "Begin" : "End", + f->subclass.integer, f->subclass.integer, ast->name); if (p->confirmanswer) { - ast_debug(1, "Confirm answer on %s!\n", ast->name); - /* Upon receiving a DTMF digit, consider this an answer confirmation instead - of a DTMF digit */ - p->subs[idx].f.frametype = AST_FRAME_CONTROL; - p->subs[idx].f.subclass.integer = AST_CONTROL_ANSWER; + if (f->frametype == AST_FRAME_DTMF_END) { + ast_debug(1, "Confirm answer on %s!\n", ast->name); + /* Upon receiving a DTMF digit, consider this an answer confirmation instead + of a DTMF digit */ + p->subs[idx].f.frametype = AST_FRAME_CONTROL; + p->subs[idx].f.subclass.integer = AST_CONTROL_ANSWER; + /* Reset confirmanswer so DTMF's will behave properly for the duration of the call */ + p->confirmanswer = 0; + } else { + p->subs[idx].f.frametype = AST_FRAME_NULL; + p->subs[idx].f.subclass.integer = 0; + } *dest = &p->subs[idx].f; - /* Reset confirmanswer so DTMF's will behave properly for the duration of the call */ - p->confirmanswer = 0; } else if (p->callwaitcas) { - if ((f->subclass.integer == 'A') || (f->subclass.integer == 'D')) { - ast_debug(1, "Got some DTMF, but it's for the CAS\n"); - if (p->cidspill) + if (f->frametype == AST_FRAME_DTMF_END) { + if ((f->subclass.integer == 'A') || (f->subclass.integer == 'D')) { + ast_debug(1, "Got some DTMF, but it's for the CAS\n"); ast_free(p->cidspill); - send_cwcidspill(p); + p->cidspill = NULL; + send_cwcidspill(p); + } + p->callwaitcas = 0; } - p->callwaitcas = 0; p->subs[idx].f.frametype = AST_FRAME_NULL; p->subs[idx].f.subclass.integer = 0; *dest = &p->subs[idx].f; } else if (f->subclass.integer == 'f') { - /* Fax tone -- Handle and return NULL */ - if ((p->callprogress & CALLPROGRESS_FAX) && !p->faxhandled) { - /* If faxbuffers are configured, use them for the fax transmission */ - if (p->usefaxbuffers && !p->bufferoverrideinuse) { - struct dahdi_bufferinfo bi = { - .txbufpolicy = p->faxbuf_policy, - .bufsize = p->bufsize, - .numbufs = p->faxbuf_no - }; - int res; + if (f->frametype == AST_FRAME_DTMF_END) { + /* Fax tone -- Handle and return NULL */ + if ((p->callprogress & CALLPROGRESS_FAX) && !p->faxhandled) { + /* If faxbuffers are configured, use them for the fax transmission */ + if (p->usefaxbuffers && !p->bufferoverrideinuse) { + struct dahdi_bufferinfo bi = { + .txbufpolicy = p->faxbuf_policy, + .bufsize = p->bufsize, + .numbufs = p->faxbuf_no + }; + int res; - if ((res = ioctl(p->subs[idx].dfd, DAHDI_SET_BUFINFO, &bi)) < 0) { - ast_log(LOG_WARNING, "Channel '%s' unable to set buffer policy, reason: %s\n", ast->name, strerror(errno)); - } else { - p->bufferoverrideinuse = 1; + if ((res = ioctl(p->subs[idx].dfd, DAHDI_SET_BUFINFO, &bi)) < 0) { + ast_log(LOG_WARNING, "Channel '%s' unable to set buffer policy, reason: %s\n", ast->name, strerror(errno)); + } else { + p->bufferoverrideinuse = 1; + } } - } - p->faxhandled = 1; - if (p->dsp) { - p->dsp_features &= ~DSP_FEATURE_FAX_DETECT; - ast_dsp_set_features(p->dsp, p->dsp_features); - ast_debug(1, "Disabling FAX tone detection on %s after tone received\n", ast->name); - } - if (strcmp(ast->exten, "fax")) { - const char *target_context = S_OR(ast->macrocontext, ast->context); + p->faxhandled = 1; + if (p->dsp) { + p->dsp_features &= ~DSP_FEATURE_FAX_DETECT; + ast_dsp_set_features(p->dsp, p->dsp_features); + ast_debug(1, "Disabling FAX tone detection on %s after tone received\n", ast->name); + } + if (strcmp(ast->exten, "fax")) { + const char *target_context = S_OR(ast->macrocontext, ast->context); - /* We need to unlock 'ast' here because ast_exists_extension has the - * potential to start autoservice on the channel. Such action is prone - * to deadlock. - */ - ast_mutex_unlock(&p->lock); - ast_channel_unlock(ast); - if (ast_exists_extension(ast, target_context, "fax", 1, - S_COR(ast->caller.id.number.valid, ast->caller.id.number.str, NULL))) { - ast_channel_lock(ast); - ast_mutex_lock(&p->lock); - ast_verb(3, "Redirecting %s to fax extension\n", ast->name); - /* Save the DID/DNIS when we transfer the fax call to a "fax" extension */ - pbx_builtin_setvar_helper(ast, "FAXEXTEN", ast->exten); - if (ast_async_goto(ast, target_context, "fax", 1)) - ast_log(LOG_WARNING, "Failed to async goto '%s' into fax of '%s'\n", ast->name, target_context); + /* We need to unlock 'ast' here because ast_exists_extension has the + * potential to start autoservice on the channel. Such action is prone + * to deadlock. + */ + ast_mutex_unlock(&p->lock); + ast_channel_unlock(ast); + if (ast_exists_extension(ast, target_context, "fax", 1, + S_COR(ast->caller.id.number.valid, ast->caller.id.number.str, NULL))) { + ast_channel_lock(ast); + ast_mutex_lock(&p->lock); + ast_verb(3, "Redirecting %s to fax extension\n", ast->name); + /* Save the DID/DNIS when we transfer the fax call to a "fax" extension */ + pbx_builtin_setvar_helper(ast, "FAXEXTEN", ast->exten); + if (ast_async_goto(ast, target_context, "fax", 1)) + ast_log(LOG_WARNING, "Failed to async goto '%s' into fax of '%s'\n", ast->name, target_context); + } else { + ast_channel_lock(ast); + ast_mutex_lock(&p->lock); + ast_log(LOG_NOTICE, "Fax detected, but no fax extension\n"); + } } else { - ast_channel_lock(ast); - ast_mutex_lock(&p->lock); - ast_log(LOG_NOTICE, "Fax detected, but no fax extension\n"); + ast_debug(1, "Already in a fax extension, not redirecting\n"); } } else { - ast_debug(1, "Already in a fax extension, not redirecting\n"); + ast_debug(1, "Fax already handled\n"); } - } else { - ast_debug(1, "Fax already handled\n"); + dahdi_confmute(p, 0); } - dahdi_confmute(p, 0); p->subs[idx].f.frametype = AST_FRAME_NULL; p->subs[idx].f.subclass.integer = 0; *dest = &p->subs[idx].f; @@ -7632,7 +7693,7 @@ p->subs[idx].f.frametype = AST_FRAME_DTMF_END; p->subs[idx].f.subclass.integer = res & 0xff; } - dahdi_handle_dtmfup(ast, idx, &f); + dahdi_handle_dtmf(ast, idx, &f); return f; } @@ -7642,6 +7703,7 @@ dahdi_confmute(p, 1); p->subs[idx].f.frametype = AST_FRAME_DTMF_BEGIN; p->subs[idx].f.subclass.integer = res & 0xff; + dahdi_handle_dtmf(ast, idx, &f); return &p->subs[idx].f; } @@ -7813,6 +7875,7 @@ #endif p->callwaitingrepeat = 0; p->cidcwexpire = 0; + p->cid_suppress_expire = 0; p->owner = NULL; /* Don't start streaming audio yet if the incoming call isn't up yet */ if (p->subs[SUB_REAL].owner->_state != AST_STATE_UP) @@ -7959,11 +8022,12 @@ p->subs[SUB_REAL].needringing = 0; dahdi_set_hook(p->subs[idx].dfd, DAHDI_OFFHOOK); ast_debug(1, "channel %d answered\n", p->channel); - if (p->cidspill) { - /* Cancel any running CallerID spill */ - ast_free(p->cidspill); - p->cidspill = NULL; - } + + /* Cancel any running CallerID spill */ + ast_free(p->cidspill); + p->cidspill = NULL; + restore_conference(p); + p->dialing = 0; p->callwaitcas = 0; if (p->confirmanswer) { @@ -8128,6 +8192,11 @@ case SIG_FXOKS: ast_debug(1, "Winkflash, index: %d, normal: %d, callwait: %d, thirdcall: %d\n", idx, p->subs[SUB_REAL].dfd, p->subs[SUB_CALLWAIT].dfd, p->subs[SUB_THREEWAY].dfd); + + /* Cancel any running CallerID spill */ + ast_free(p->cidspill); + p->cidspill = NULL; + restore_conference(p); p->callwaitcas = 0; if (idx != SUB_REAL) { @@ -8147,6 +8216,7 @@ } p->callwaitingrepeat = 0; p->cidcwexpire = 0; + p->cid_suppress_expire = 0; /* Start music on hold if appropriate */ if (!p->subs[SUB_CALLWAIT].inthreeway && ast_bridged_channel(p->subs[SUB_CALLWAIT].owner)) { ast_queue_control_data(p->subs[SUB_CALLWAIT].owner, AST_CONTROL_HOLD, @@ -8479,6 +8549,7 @@ dahdi_ring_phone(p); p->callwaitingrepeat = 0; p->cidcwexpire = 0; + p->cid_suppress_expire = 0; } else ast_log(LOG_WARNING, "Absorbed on hook, but nobody is left!?!?\n"); update_conf(p); @@ -8510,6 +8581,7 @@ } p->callwaitingrepeat = 0; p->cidcwexpire = 0; + p->cid_suppress_expire = 0; if (ast_bridged_channel(p->owner)) ast_queue_control(p->owner, AST_CONTROL_UNHOLD); p->subs[SUB_REAL].needunhold = 1; @@ -8781,22 +8853,26 @@ return &p->subs[idx].f; } } - /* Ensure the CW timer decrements only on a single subchannel */ - if (p->callwaitingrepeat && dahdi_get_index(ast, p, 1) == SUB_REAL) { - p->callwaitingrepeat--; + if (idx == SUB_REAL) { + /* Ensure the CW timers decrement only on a single subchannel */ + if (p->cidcwexpire) { + if (!--p->cidcwexpire) { + /* Expired CID/CW */ + ast_verb(3, "CPE does not support Call Waiting Caller*ID.\n"); + restore_conference(p); + } + } + if (p->cid_suppress_expire) { + --p->cid_suppress_expire; + } + if (p->callwaitingrepeat) { + if (!--p->callwaitingrepeat) { + /* Expired, Repeat callwaiting tone */ + ++p->callwaitrings; + dahdi_callwait(ast); + } + } } - if (p->cidcwexpire) - p->cidcwexpire--; - /* Repeat callwaiting */ - if (p->callwaitingrepeat == 1) { - p->callwaitrings++; - dahdi_callwait(ast); - } - /* Expire CID/CW */ - if (p->cidcwexpire == 1) { - ast_verb(3, "CPE does not support Call Waiting Caller*ID.\n"); - restore_conference(p); - } if (p->subs[idx].linear) { p->subs[idx].f.datalen = READ_SIZE * 2; } else @@ -8899,11 +8975,31 @@ } else f = &p->subs[idx].f; - if (f && (f->frametype == AST_FRAME_DTMF)) { - if (analog_lib_handles(p->sig, p->radio, p->oprmode)) { - analog_handle_dtmfup(p->sig_pvt, ast, idx, &f); - } else - dahdi_handle_dtmfup(ast, idx, &f); + if (f) { + switch (f->frametype) { + case AST_FRAME_DTMF_BEGIN: + case AST_FRAME_DTMF_END: + if (analog_lib_handles(p->sig, p->radio, p->oprmode)) { + analog_handle_dtmf(p->sig_pvt, ast, idx, &f); + } else { + dahdi_handle_dtmf(ast, idx, &f); + } + break; + case AST_FRAME_VOICE: + if (p->cidspill || p->cid_suppress_expire) { + /* We are/were sending a caller id spill. Suppress any echo. */ + p->subs[idx].f.frametype = AST_FRAME_NULL; + p->subs[idx].f.subclass.integer = 0; + p->subs[idx].f.samples = 0; + p->subs[idx].f.mallocd = 0; + p->subs[idx].f.offset = 0; + p->subs[idx].f.data.ptr = NULL; + p->subs[idx].f.datalen= 0; + } + break; + default: + break; + } } /* If we have a fake_event, trigger exception to handle it */ @@ -8968,7 +9064,8 @@ return 0; } if (p->cidspill) { - ast_debug(1, "Dropping frame since I've still got a callerid spill\n"); + ast_debug(1, "Dropping frame since I've still got a callerid spill on %s...\n", + ast->name); return 0; } /* Return if it's not valid data */ @@ -10880,11 +10977,9 @@ handled = 1; if (dahdi_set_hook(pvt->subs[SUB_REAL].dfd, DAHDI_RINGOFF) ) { - ast_log(LOG_WARNING, "Unable to finsh RP-AS: %s mwi send aborted\n", strerror(errno)); - if(pvt->cidspill) { - ast_free(pvt->cidspill); - pvt->cidspill = NULL; - } + ast_log(LOG_WARNING, "Unable to finish RP-AS: %s mwi send aborted\n", strerror(errno)); + ast_free(pvt->cidspill); + pvt->cidspill = NULL; pvt->mwisend_data.mwisend_current = MWI_SEND_DONE; pvt->mwisendactive = 0; } else { @@ -10961,11 +11056,12 @@ res = dahdi_set_hook(i->subs[SUB_REAL].dfd, DAHDI_OFFHOOK); if (res && (errno == EBUSY)) break; - if (i->cidspill) { - /* Cancel VMWI spill */ - ast_free(i->cidspill); - i->cidspill = NULL; - } + + /* Cancel VMWI spill */ + ast_free(i->cidspill); + i->cidspill = NULL; + restore_conference(i); + if (i->immediate) { dahdi_enable_ec(i); /* The channel is immediately up. Start right away */ Index: channels/sig_analog.c =================================================================== --- channels/sig_analog.c (.../trunk) (revision 295977) +++ channels/sig_analog.c (.../team/rmudgett/align_analog) (revision 295995) @@ -624,10 +624,10 @@ return -1; } -static void analog_cb_handle_dtmfup(struct analog_pvt *p, struct ast_channel *ast, enum analog_sub analog_index, struct ast_frame **dest) +static void analog_cb_handle_dtmf(struct analog_pvt *p, struct ast_channel *ast, enum analog_sub analog_index, struct ast_frame **dest) { - if (p->calls->handle_dtmfup) { - p->calls->handle_dtmfup(p->chan_pvt, ast, analog_index, dest); + if (p->calls->handle_dtmf) { + p->calls->handle_dtmf(p->chan_pvt, ast, analog_index, dest); } } @@ -859,10 +859,7 @@ static int analog_stop_callwait(struct analog_pvt *p) { - if (p->callwaitingcallerid) { - p->callwaitcas = 0; - } - + p->callwaitcas = 0; if (p->calls->stop_callwait) { return p->calls->stop_callwait(p->chan_pvt); } @@ -871,15 +868,21 @@ static int analog_callwait(struct analog_pvt *p) { - if (p->callwaitingcallerid) { - p->callwaitcas = 1; - } + p->callwaitcas = p->callwaitingcallerid; if (p->calls->callwait) { return p->calls->callwait(p->chan_pvt); } return 0; } +static void analog_set_callwaiting(struct analog_pvt *p, int callwaiting_enable) +{ + p->callwaiting = callwaiting_enable; + if (p->calls->set_callwaiting) { + p->calls->set_callwaiting(p->chan_pvt, callwaiting_enable); + } +} + static void analog_set_cadence(struct analog_pvt *p, struct ast_channel *chan) { if (p->calls->set_cadence) { @@ -1456,7 +1459,7 @@ ast_channel_setoption(ast,AST_OPTION_TONE_VERIFY,&x,sizeof(char),0); ast_channel_setoption(ast,AST_OPTION_TDD,&x,sizeof(char),0); p->callwaitcas = 0; - p->callwaiting = p->permcallwaiting; + analog_set_callwaiting(p, p->permcallwaiting); p->hidecallerid = p->permhidecallerid; analog_set_dialing(p, 0); analog_update_conf(p); @@ -1574,35 +1577,45 @@ } } -void analog_handle_dtmfup(struct analog_pvt *p, struct ast_channel *ast, enum analog_sub idx, struct ast_frame **dest) +void analog_handle_dtmf(struct analog_pvt *p, struct ast_channel *ast, enum analog_sub idx, struct ast_frame **dest) { struct ast_frame *f = *dest; - ast_debug(1, "DTMF digit: %c on %s\n", f->subclass.integer, ast->name); + ast_debug(1, "%s DTMF digit: 0x%02X '%c' on %s\n", + f->frametype == AST_FRAME_DTMF_BEGIN ? "Begin" : "End", + f->subclass.integer, f->subclass.integer, ast->name); if (analog_check_confirmanswer(p)) { - ast_debug(1, "Confirm answer on %s!\n", ast->name); - /* Upon receiving a DTMF digit, consider this an answer confirmation instead - of a DTMF digit */ - p->subs[idx].f.frametype = AST_FRAME_CONTROL; - p->subs[idx].f.subclass.integer = AST_CONTROL_ANSWER; + if (f->frametype == AST_FRAME_DTMF_END) { + ast_debug(1, "Confirm answer on %s!\n", ast->name); + /* Upon receiving a DTMF digit, consider this an answer confirmation instead + of a DTMF digit */ + p->subs[idx].f.frametype = AST_FRAME_CONTROL; + p->subs[idx].f.subclass.integer = AST_CONTROL_ANSWER; + /* Reset confirmanswer so DTMF's will behave properly for the duration of the call */ + analog_set_confirmanswer(p, 0); + } else { + p->subs[idx].f.frametype = AST_FRAME_NULL; + p->subs[idx].f.subclass.integer = 0; + } *dest = &p->subs[idx].f; - /* Reset confirmanswer so DTMF's will behave properly for the duration of the call */ - analog_set_confirmanswer(p, 0); } else if (p->callwaitcas) { - if ((f->subclass.integer == 'A') || (f->subclass.integer == 'D')) { - ast_debug(1, "Got some DTMF, but it's for the CAS\n"); - p->caller.id.name.str = p->callwait_name; - p->caller.id.number.str = p->callwait_num; - analog_send_callerid(p, 1, &p->caller); + if (f->frametype == AST_FRAME_DTMF_END) { + if ((f->subclass.integer == 'A') || (f->subclass.integer == 'D')) { + ast_debug(1, "Got some DTMF, but it's for the CAS\n"); + p->caller.id.name.str = p->callwait_name; + p->caller.id.number.str = p->callwait_num; + analog_send_callerid(p, 1, &p->caller); + } + if (analog_handles_digit(f)) { + p->callwaitcas = 0; + } } - if (analog_handles_digit(f)) - p->callwaitcas = 0; p->subs[idx].f.frametype = AST_FRAME_NULL; p->subs[idx].f.subclass.integer = 0; *dest = &p->subs[idx].f; } else { - analog_cb_handle_dtmfup(p, ast, idx, dest); + analog_cb_handle_dtmf(p, ast, idx, dest); } } @@ -2122,7 +2135,7 @@ } else if (p->callwaiting && !strcmp(exten, "*70")) { ast_verb(3, "Disabling call waiting on %s\n", chan->name); /* Disable call waiting if enabled */ - p->callwaiting = 0; + analog_set_callwaiting(p, 0); res = analog_play_tone(p, idx, ANALOG_TONE_DIALRECALL); if (res) { ast_log(LOG_WARNING, "Unable to do dial recall on channel %s: %s\n", @@ -2631,7 +2644,7 @@ analog_confmute(p, 0); p->subs[idx].f.frametype = AST_FRAME_DTMF_END; p->subs[idx].f.subclass.integer = res & 0xff; - analog_handle_dtmfup(p, ast, idx, &f); + analog_handle_dtmf(p, ast, idx, &f); return f; } @@ -2641,6 +2654,7 @@ analog_confmute(p, 1); p->subs[idx].f.frametype = AST_FRAME_DTMF_BEGIN; p->subs[idx].f.subclass.integer = res & 0xff; + analog_handle_dtmf(p, ast, idx, &f); return f; } @@ -2890,7 +2904,10 @@ analog_set_needringing(p, 0); analog_off_hook(p); ast_debug(1, "channel %d answered\n", p->channel); + + /* Cancel any running CallerID spill */ analog_cancel_cidspill(p); + analog_set_dialing(p, 0); p->callwaitcas = 0; if (analog_check_confirmanswer(p)) { @@ -3050,6 +3067,8 @@ ast_debug(1, "Winkflash, index: %d, normal: %d, callwait: %d, thirdcall: %d\n", idx, analog_get_sub_fd(p, ANALOG_SUB_REAL), analog_get_sub_fd(p, ANALOG_SUB_CALLWAIT), analog_get_sub_fd(p, ANALOG_SUB_THREEWAY)); + /* Cancel any running CallerID spill */ + analog_cancel_cidspill(p); p->callwaitcas = 0; if (idx != ANALOG_SUB_REAL) { @@ -3568,7 +3587,10 @@ if (res && (errno == EBUSY)) { break; } + + /* Cancel VMWI spill */ analog_cancel_cidspill(i); + if (i->immediate) { analog_set_echocanceller(i, 1); /* The channel is immediately up. Start right away */ @@ -3826,7 +3848,7 @@ p->permcallwaiting = 0; } - p->callwaiting = p->permcallwaiting; + analog_set_callwaiting(p, p->permcallwaiting); return 0; } Index: channels/sig_analog.h =================================================================== --- channels/sig_analog.h (.../trunk) (revision 295977) +++ channels/sig_analog.h (.../team/rmudgett/align_analog) (revision 295995) @@ -134,10 +134,10 @@ /* Do deadlock avoidance for the private signaling structure lock. */ void (* const deadlock_avoidance_private)(void *pvt); - /* Function which is called back to handle any other DTMF up events that are received. Called by analog_handle_event. Why is this + /* Function which is called back to handle any other DTMF events that are received. Called by analog_handle_event. Why is this * important to use, instead of just directly using events received before they are passed into the library? Because sometimes, * (CWCID) the library absorbs DTMF events received. */ - void (* const handle_dtmfup)(void *pvt, struct ast_channel *ast, enum analog_sub analog_index, struct ast_frame **dest); + void (* const handle_dtmf)(void *pvt, struct ast_channel *ast, enum analog_sub analog_index, struct ast_frame **dest); int (* const get_event)(void *pvt); int (* const wait_event)(void *pvt); @@ -228,6 +228,7 @@ int (* const check_waitingfordt)(void *pvt); void (* const set_confirmanswer)(void *pvt, int flag); int (* const check_confirmanswer)(void *pvt); + void (* const set_callwaiting)(void *pvt, int callwaiting_enable); void (* const cancel_cidspill)(void *pvt); int (* const confmute)(void *pvt, int mute); void (* const set_pulsedial)(void *pvt, int flag); @@ -271,14 +272,14 @@ unsigned int dahditrcallerid:1; /*!< should we use the callerid from incoming call on dahdi transfer or not */ unsigned int hanguponpolarityswitch:1; unsigned int immediate:1; - unsigned int permcallwaiting:1; + unsigned int permcallwaiting:1; /*!< TRUE if call waiting is enabled. (Configured option) */ unsigned int permhidecallerid:1; /*!< Whether to hide our outgoing caller ID or not */ unsigned int pulse:1; unsigned int threewaycalling:1; unsigned int transfer:1; unsigned int transfertobusy:1; /*!< allow flash-transfers to busy channels */ unsigned int use_callerid:1; /*!< Whether or not to use caller id on this channel */ - unsigned int callwaitingcallerid:1; + unsigned int callwaitingcallerid:1; /*!< TRUE if send caller ID for Call Waiting */ /*! * \brief TRUE if SMDI (Simplified Message Desk Interface) is enabled */ @@ -289,6 +290,7 @@ /* Not used for anything but log messages. Could be just the TCID */ int channel; /*!< Channel Number */ + enum analog_sigtype outsigmod; int echotraining; int cid_signalling; /*!< Asterisk callerid type we're using */ @@ -301,13 +303,21 @@ /* XXX: All variables after this are internal */ - unsigned int callwaiting:1; + unsigned int callwaiting:1; /*!< TRUE if call waiting is enabled. (Active option) */ unsigned int dialednone:1; unsigned int dialing:1; /*!< TRUE if in the process of dialing digits or sending something */ unsigned int dnd:1; /*!< TRUE if Do-Not-Disturb is enabled. */ unsigned int echobreak:1; unsigned int hidecallerid:1; unsigned int outgoing:1; + unsigned int inalarm:1; + /*! + * \brief TRUE if Call Waiting (CW) CPE Alert Signal (CAS) is being sent. + * \note + * After CAS is sent, the call waiting caller id will be sent if the phone + * gives a positive reply. + */ + unsigned int callwaitcas:1; char callwait_num[AST_MAX_EXTENSION]; char callwait_name[AST_MAX_EXTENSION]; @@ -331,10 +341,6 @@ struct ast_channel *ss_astchan; /* All variables after this are definitely going to be audited */ - unsigned int inalarm:1; - - int callwaitcas; - int ringt; int ringt_base; }; @@ -360,7 +366,7 @@ int analog_config_complete(struct analog_pvt *p); -void analog_handle_dtmfup(struct analog_pvt *p, struct ast_channel *ast, enum analog_sub index, struct ast_frame **dest); +void analog_handle_dtmf(struct analog_pvt *p, struct ast_channel *ast, enum analog_sub index, struct ast_frame **dest); enum analog_cid_start analog_str_to_cidstart(const char *value);