#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;
int cid_start; /*!< CID start indicator, polarity or ring */
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 */
+ 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. */
* 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;
ast_log(LOG_WARNING, "Unable to restore conference info: %s\n", strerror(errno));
return -1;
}
+ if (option_debug)
+ ast_log(LOG_DEBUG, "Restored conferencing\n");
}
- if (option_debug)
- ast_log(LOG_DEBUG, "Restored conferencing\n");
return 0;
}
{
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));
return 0;
p->cidpos += res;
}
+ p->cid_suppress_expire = CALLWAITING_SUPPRESS_SAMPLES;
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;
ast_log(LOG_WARNING, "Spill already exists?!?\n");
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);
p->prev->next = p->next;
if (p->next)
p->next->prev = p->prev;
+
+ free(p->cidspill);
if (p->use_smdi)
ast_smdi_interface_unref(p->smdi_iface);
ast_mutex_destroy(&p->lock);
/* Destroy all the interfaces and free their memory */
p = iflist;
while (p) {
- /* Free any callerid */
- if (p->cidspill)
- ast_free(p->cidspill);
pl = p;
p = p->next;
x = pl->channel;
/* Free associated memory */
- if (pl)
- destroy_dahdi_pvt(&pl);
+ destroy_dahdi_pvt(&pl);
if (option_verbose > 2)
ast_verbose(VERBOSE_PREFIX_2 "Unregistered channel %d\n", x);
}
default:
tone_zone_play_tone(p->subs[SUB_REAL].dfd, -1);
}
- if (p->cidspill)
- free(p->cidspill);
+ free(p->cidspill);
+ p->cidspill = NULL;
if (p->sig)
dahdi_disable_ec(p);
x = 0;
ast_channel_setoption(ast,AST_OPTION_TONE_VERIFY,&x,sizeof(char),0);
ast_channel_setoption(ast,AST_OPTION_TDD,&x,sizeof(char),0);
p->didtdd = 0;
- p->cidspill = NULL;
p->callwaitcas = 0;
p->callwaiting = p->permcallwaiting;
p->hidecallerid = p->permhidecallerid;
p->callwaitingrepeat = 0;
p->cidcwexpire = 0;
+ p->cid_suppress_expire = 0;
p->oprmode = 0;
ast->tech_pvt = NULL;
ast_mutex_unlock(&p->lock);
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_mutex_unlock(&c0->lock);
+ ast_mutex_unlock(&c1->lock);
+ return AST_BRIDGE_FAILED_NOWARN;
+ }
+
if ((oi0 == SUB_REAL) && (oi1 == SUB_REAL)) {
if (p0->owner && p1->owner) {
/* If we don't have a call-wait in a 3-way, and we aren't in a 3-way, we can be master */
return DAHDI_ALARM_NONE;
}
-static void dahdi_handle_dtmfup(struct ast_channel *ast, int index, struct ast_frame **dest)
+static void dahdi_handle_dtmf(struct ast_channel *ast, int index, struct ast_frame **dest)
{
struct dahdi_pvt *p = ast->tech_pvt;
struct ast_frame *f = *dest;
if (option_debug)
- ast_log(LOG_DEBUG, "DTMF digit: %c on %s\n", f->subclass, ast->name);
+ ast_log(LOG_DEBUG, "%s DTMF digit: 0x%02X '%c' on %s\n",
+ f->frametype == AST_FRAME_DTMF_BEGIN ? "Begin" : "End",
+ f->subclass, f->subclass, ast->name);
if (p->confirmanswer) {
- if (option_debug)
- ast_log(LOG_DEBUG, "Confirm answer on %s!\n", ast->name);
- /* Upon receiving a DTMF digit, consider this an answer confirmation instead
- of a DTMF digit */
- p->subs[index].f.frametype = AST_FRAME_CONTROL;
- p->subs[index].f.subclass = AST_CONTROL_ANSWER;
+ if (f->frametype == AST_FRAME_DTMF_END) {
+ if (option_debug)
+ ast_log(LOG_DEBUG, "Confirm answer on %s!\n", ast->name);
+ /* Upon receiving a DTMF digit, consider this an answer confirmation instead
+ of a DTMF digit */
+ p->subs[index].f.frametype = AST_FRAME_CONTROL;
+ p->subs[index].f.subclass = AST_CONTROL_ANSWER;
+ /* Reset confirmanswer so DTMF's will behave properly for the duration of the call */
+ p->confirmanswer = 0;
+ } else {
+ p->subs[index].f.frametype = AST_FRAME_NULL;
+ p->subs[index].f.subclass = 0;
+ }
*dest = &p->subs[index].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 == 'A') || (f->subclass == 'D')) {
- if (option_debug)
- ast_log(LOG_DEBUG, "Got some DTMF, but it's for the CAS\n");
- if (p->cidspill)
+ if (f->frametype == AST_FRAME_DTMF_END) {
+ if ((f->subclass == 'A') || (f->subclass == 'D')) {
+ if (option_debug)
+ ast_log(LOG_DEBUG, "Got some DTMF, but it's for the CAS\n");
free(p->cidspill);
- send_cwcidspill(p);
+ p->cidspill = NULL;
+ send_cwcidspill(p);
+ }
+ if ((f->subclass != 'm') && (f->subclass != 'u'))
+ p->callwaitcas = 0;
}
- if ((f->subclass != 'm') && (f->subclass != 'u'))
- p->callwaitcas = 0;
p->subs[index].f.frametype = AST_FRAME_NULL;
p->subs[index].f.subclass = 0;
*dest = &p->subs[index].f;
} else if (f->subclass == 'f') {
- /* Fax tone -- Handle and return NULL */
- if ((p->callprogress & 0x6) && !p->faxhandled) {
- p->faxhandled = 1;
- 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, ast->cid.cid_num)) {
- ast_channel_lock(ast);
- ast_mutex_lock(&p->lock);
- if (option_verbose > 2)
- ast_verbose(VERBOSE_PREFIX_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");
- }
+ if (f->frametype == AST_FRAME_DTMF_END) {
+ /* Fax tone -- Handle and return NULL */
+ if ((p->callprogress & 0x6) && !p->faxhandled) {
+ p->faxhandled = 1;
+ 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, ast->cid.cid_num)) {
+ ast_channel_lock(ast);
+ ast_mutex_lock(&p->lock);
+ if (option_verbose > 2)
+ ast_verbose(VERBOSE_PREFIX_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 if (option_debug)
+ ast_log(LOG_DEBUG, "Already in a fax extension, not redirecting\n");
} else if (option_debug)
- ast_log(LOG_DEBUG, "Already in a fax extension, not redirecting\n");
- } else if (option_debug)
ast_log(LOG_DEBUG, "Fax already handled\n");
- dahdi_confmute(p, 0);
+ dahdi_confmute(p, 0);
+ }
p->subs[index].f.frametype = AST_FRAME_NULL;
p->subs[index].f.subclass = 0;
*dest = &p->subs[index].f;
} else if (f->subclass == 'm') {
- /* Confmute request */
- dahdi_confmute(p, 1);
+ if (f->frametype == AST_FRAME_DTMF_END) {
+ /* Confmute request */
+ dahdi_confmute(p, 1);
+ }
p->subs[index].f.frametype = AST_FRAME_NULL;
p->subs[index].f.subclass = 0;
*dest = &p->subs[index].f;
} else if (f->subclass == 'u') {
- /* Unmute */
- dahdi_confmute(p, 0);
+ if (f->frametype == AST_FRAME_DTMF_END) {
+ /* Unmute */
+ dahdi_confmute(p, 0);
+ }
p->subs[index].f.frametype = AST_FRAME_NULL;
p->subs[index].f.subclass = 0;
*dest = &p->subs[index].f;
- } else
- dahdi_confmute(p, 0);
+ } else {
+ if (f->frametype == AST_FRAME_DTMF_END) {
+ dahdi_confmute(p, 0);
+ }
+ }
}
static void handle_alarms(struct dahdi_pvt *p, int alarms)
#ifdef HAVE_PRI
if (!p->proceeding && p->sig == SIG_PRI && p->pri && (p->pri->overlapdial & DAHDI_OVERLAPDIAL_INCOMING)) {
/* absorb event */
- } else {
+ } else
#endif
+ {
p->subs[index].f.frametype = AST_FRAME_DTMF_END;
p->subs[index].f.subclass = res & 0xff;
-#ifdef HAVE_PRI
+ dahdi_handle_dtmf(ast, index, &f);
}
-#endif
- dahdi_handle_dtmfup(ast, index, &f);
return f;
}
if (res & DAHDI_EVENT_DTMFDOWN) {
if (option_debug)
ast_log(LOG_DEBUG, "DTMF Down '%c'\n", res & 0xff);
- /* Mute conference */
- dahdi_confmute(p, 1);
- p->subs[index].f.frametype = AST_FRAME_DTMF_BEGIN;
- p->subs[index].f.subclass = res & 0xff;
+#ifdef HAVE_PRI
+ if (!p->proceeding && p->sig == SIG_PRI && p->pri && (p->pri->overlapdial & DAHDI_OVERLAPDIAL_INCOMING)) {
+ /* absorb event */
+ } else
+#endif
+ {
+ /* Mute conference */
+ dahdi_confmute(p, 1);
+ p->subs[index].f.frametype = AST_FRAME_DTMF_BEGIN;
+ p->subs[index].f.subclass = res & 0xff;
+ dahdi_handle_dtmf(ast, index, &f);
+ }
return &p->subs[index].f;
}
#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)
dahdi_set_hook(p->subs[index].dfd, DAHDI_OFFHOOK);
p->subs[SUB_REAL].needringing = 0;
ast_log(LOG_DEBUG, "channel %d answered\n", p->channel);
- if (p->cidspill) {
- /* Cancel any running CallerID spill */
- free(p->cidspill);
- p->cidspill = NULL;
- }
+
+ /* Cancel any running CallerID spill */
+ free(p->cidspill);
+ p->cidspill = NULL;
+ restore_conference(p);
+
p->dialing = 0;
p->callwaitcas = 0;
if (p->confirmanswer) {
case SIG_FXOKS:
ast_log(LOG_DEBUG, "Winkflash, index: %d, normal: %d, callwait: %d, thirdcall: %d\n",
index, p->subs[SUB_REAL].dfd, p->subs[SUB_CALLWAIT].dfd, p->subs[SUB_THREEWAY].dfd);
+
+ /* Cancel any running CallerID spill */
+ free(p->cidspill);
+ p->cidspill = NULL;
+ restore_conference(p);
p->callwaitcas = 0;
if (index != SUB_REAL) {
}
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)) {
dahdi_ring_phone(p);
p->callwaitingrepeat = 0;
p->cidcwexpire = 0;
+ p->cid_suppress_expire = 0;
} else {
ast_log(LOG_WARNING, "Absorbed %s, but nobody is left!?!?\n",
event2str(res));
}
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;
return &p->subs[index].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 (p->cidcwexpire)
- p->cidcwexpire--;
- /* Repeat callwaiting */
- if (p->callwaitingrepeat == 1) {
- p->callwaitrings++;
- dahdi_callwait(ast);
- }
- /* Expire CID/CW */
- if (p->cidcwexpire == 1) {
- if (option_verbose > 2)
- ast_verbose(VERBOSE_PREFIX_3 "CPE does not support Call Waiting Caller*ID.\n");
- restore_conference(p);
+ if (index == SUB_REAL) {
+ /* Ensure the CW timers decrement only on a single subchannel */
+ if (p->cidcwexpire) {
+ if (!--p->cidcwexpire) {
+ /* Expired CID/CW */
+ if (option_verbose > 2)
+ ast_verbose(VERBOSE_PREFIX_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->subs[index].linear) {
p->subs[index].f.datalen = READ_SIZE * 2;
} else
f = &p->subs[index].f;
- if (f && (f->frametype == AST_FRAME_DTMF))
- dahdi_handle_dtmfup(ast, index, &f);
+ if (f) {
+ switch (f->frametype) {
+ case AST_FRAME_DTMF_BEGIN:
+ case AST_FRAME_DTMF_END:
+ dahdi_handle_dtmf(ast, index, &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[index].f.frametype = AST_FRAME_NULL;
+ p->subs[index].f.subclass = 0;
+ p->subs[index].f.samples = 0;
+ p->subs[index].f.mallocd = 0;
+ p->subs[index].f.offset = 0;
+ p->subs[index].f.data = NULL;
+ p->subs[index].f.datalen= 0;
+ }
+ break;
+ default:
+ break;
+ }
+ }
/* If we have a fake_event, trigger exception to handle it */
if (p->fake_event)
return 0;
}
if (p->cidspill) {
- if (option_debug)
- ast_log(LOG_DEBUG, "Dropping frame since I've still got a callerid spill\n");
+ if (option_debug) {
+ ast_log(LOG_DEBUG,
+ "Dropping frame since I've still got a callerid spill on %s...\n",
+ ast->name);
+ }
return 0;
}
/* Return if it's not valid data */
res = dahdi_set_hook(i->subs[SUB_REAL].dfd, DAHDI_OFFHOOK);
if (res && (errno == EBUSY))
break;
- if (i->cidspill) {
- /* Cancel VMWI spill */
- free(i->cidspill);
- i->cidspill = NULL;
- }
+
+ /* Cancel VMWI spill */
+ free(i->cidspill);
+ i->cidspill = NULL;
+ restore_conference(i);
+
if (i->immediate) {
dahdi_enable_ec(i);
/* The channel is immediately up. Start right away */
i->cidpos += res2;
if (i->cidpos >= i->cidlen) {
free(i->cidspill);
- i->cidspill = 0;
+ i->cidspill = NULL;
i->cidpos = 0;
i->cidlen = 0;
}