From 757441b693fc940a75d7fdcfa0daa356e2577e23 Mon Sep 17 00:00:00 2001 From: Adam Borowski <adam.borowski@sigma.se> Date: Fri, 14 May 2021 13:49:58 +0200 Subject: [PATCH] Feature 4599 - Integration of call clearing tones into iopsys voice Play howler tone in case user forgot to put handset on hook as a reminder to end the call properly. --- channels/chan_brcm.c | 132 +++++++++++++++++++++++++++++++++++-------- channels/chan_brcm.h | 8 ++- 2 files changed, 115 insertions(+), 25 deletions(-) diff --git a/channels/chan_brcm.c b/channels/chan_brcm.c index e541526738..1ac3fba3d1 100644 --- a/channels/chan_brcm.c +++ b/channels/chan_brcm.c @@ -105,6 +105,7 @@ static void *pe_base_run(void *unused); static int brcm_create_connection(struct brcm_subchannel *p); static int brcm_signal_congestion(struct brcm_pvt *p); static int brcm_stop_dialtone(struct brcm_pvt *p); +static void brcm_signal_howler(struct brcm_pvt *p); static int brcm_signal_ringing(struct brcm_pvt *p); static int brcm_stop_ringing(struct brcm_pvt *p); static int brcm_signal_ringing_callerid_pending(struct brcm_pvt *p); @@ -122,6 +123,7 @@ static int brcm_should_relay_dtmf(const struct brcm_subchannel *sub); static struct ast_channel *brcm_new(struct brcm_subchannel *subchan, int state, const char *ext, const char *context, const struct ast_assigned_ids *assignedids, const struct ast_channel *requestor, struct ast_format_cap *format); +static int handle_dialtone_timeout(const void *data); /* Global brcm channel parameters */ static const char tdesc[] = "Broadcom SLIC Driver"; @@ -832,7 +834,9 @@ static int brcm_hangup(struct ast_channel *ast) p = sub->parent; sub_peer = brcm_subchannel_get_peer(sub); - ast_debug(1, "brcm_hangup(%s) line_id=%d connection_id=%d\n", ast_channel_name(ast), p->line_id, sub->connection_id); + ast_debug(1, "brcm_hangup(%s) line_id=%d connection_id=%d dialtone=%s channel_state=%s\n", + ast_channel_name(ast), p->line_id, sub->connection_id, dialtone_map[p->dialtone].str, + state2str(sub->channel_state)); if (sub->channel_state == CALLWAITING) { brcm_stop_callwaiting(p); @@ -877,7 +881,18 @@ static int brcm_hangup(struct ast_channel *ast) p->lastformat = NULL; p->lastinput = NULL; p->hf_detected = 0; - sub->channel_state = CALLENDED; + + /** + * Howler tone is played using a media file on an active channel + * Treated like a normal call but we want to preserve the channel state + * Additional check for ONHOOK channel state so that handle_dialtone_timeout() + * isn't called again when going on-hook + */ + if(p->dialtone != DIALTONE_HOWLER && sub->channel_state != ONHOOK) { + sub->channel_state = CALLENDED; + ast_debug(6, "Setting channel state to %s\n", state2str(sub->channel_state)); + } + if (terminate_conference && sub->conference_initiator && brcm_in_conference(p)) { /* Switch still active call leg out of conference mode */ brcm_stop_conference(sub); @@ -895,6 +910,12 @@ static int brcm_hangup(struct ast_channel *ast) ast_channel_tech_pvt_set(ast, NULL); brcm_close_connection(sub); + if(sub->channel_state == CALLENDED) { + /* If we hangup but not playing howler, start playing timeout tones */ + p->dialtone = DIALTONE_ON; + handle_dialtone_timeout(p); + } + pvt_unlock(p); return 0; } @@ -927,7 +948,15 @@ static int brcm_answer(struct ast_channel *ast) ast_debug(2, "brcm_answer(%s) set state to up\n", ast_channel_name(ast)); } ast_channel_rings_set(ast, 0); - sub->channel_state = INCALL; + + /** + * Howler tone is played using a media file on an active channel + * Treated like a normal call but we want to preserve the channel_state + */ + if(pvt->dialtone != DIALTONE_HOWLER) { + sub->channel_state = INCALL; + } + endpt_signal(pvt->line_id, "ringback", "off", NULL); pvt_unlock(pvt); @@ -1130,11 +1159,13 @@ struct brcm_subchannel *brcm_subchannel_get_peer(const struct brcm_subchannel *s /* Tell endpoint to play country specific dialtone. */ static int brcm_signal_dialtone(struct brcm_pvt *p) { - + + ast_verbose("Setting dialtone to %s\n", dialtone_map[p->dialtone].str); switch (p->dialtone) { case DIALTONE_OFF: endpt_signal(p->line_id, "dial", "off", NULL); endpt_signal(p->line_id, "stutter", "off", NULL); + endpt_signal(p->line_id, "unobtainable", "off", NULL); break; case DIALTONE_ON: endpt_signal(p->line_id, "dial", "on", NULL); @@ -1145,6 +1176,13 @@ static int brcm_signal_dialtone(struct brcm_pvt *p) { case DIALTONE_SPECIAL_CONDITION: endpt_signal(p->line_id, "stutter", "on", NULL); break; + case DIALTONE_UNOBTAINABLE: + endpt_signal(p->line_id, "unobtainable", "on", NULL); + break; + case DIALTONE_HOWLER: + ast_debug(9, "Trigger howler tone from Asterisk\n"); + brcm_signal_howler(p); + break; default: ast_log(LOG_ERROR, "Requested to signal unknown dialtone\n"); return -1; @@ -1152,10 +1190,29 @@ static int brcm_signal_dialtone(struct brcm_pvt *p) { return 0; } +static void brcm_signal_howler(struct brcm_pvt *p) { + struct brcm_subchannel *sub = brcm_get_active_subchannel(p); + + if(!sub) { + ast_debug(9, "Unable to find active subchannel\n"); + return; + } + + /* Start the pbx */ + if (!sub->connection_init) { + sub->connection_id = ast_atomic_fetchadd_int((int *)¤t_connection_id, +1); + brcm_create_connection(sub); + } + + /* Create a new channel to context 'howler' handled by extensions.conf */ + brcm_new(sub, AST_STATE_RING, "0", "howler", NULL, NULL, NULL); +} + int brcm_stop_dialtone(struct brcm_pvt *p) { endpt_signal(p->line_id, "dial", "off", NULL); endpt_signal(p->line_id, "stutter", "off", NULL); - + p->dialtone = DIALTONE_OFF; + brcm_signal_dialtone(p); return 0; } @@ -1537,10 +1594,29 @@ static int handle_dialtone_timeout(const void *data) p->dialtone_timeout_timer_id = -1; struct brcm_subchannel *sub = brcm_get_active_subchannel(p); - if (sub && sub->channel_state == OFFHOOK) { - /* Enter state where nothing else than ONHOOK is accepted and play congestion tone */ + if (sub && (sub->channel_state == OFFHOOK || sub->channel_state == AWAITONHOOK || sub->channel_state == CALLENDED)) { + /* Enter state where nothing else other than ONHOOK is accepted and play series of tones */ sub->channel_state = AWAITONHOOK; - brcm_signal_congestion(p); + line_settings *s = &line_config[p->line_id]; + + switch (p->dialtone) { + case DIALTONE_ON: + p->dialtone = DIALTONE_UNOBTAINABLE; + p->dialtone_timeout_timer_id = ast_sched_add(sched, s->offhook_nu_timeoutmsec, handle_dialtone_timeout, p); + break; + case DIALTONE_UNOBTAINABLE: + p->dialtone = DIALTONE_OFF; + p->dialtone_timeout_timer_id = ast_sched_add(sched, s->offhook_silence_timeoutmsec, handle_dialtone_timeout, p); + break; + case DIALTONE_OFF: + p->dialtone = DIALTONE_HOWLER; + break; + default: + p->dialtone = DIALTONE_OFF; + ast_log(LOG_ERROR, "Invalid dialtone timeout state\n"); + break; + } + brcm_signal_dialtone(p); } //ast_mutex_unlock(&p->lock); @@ -1638,6 +1714,7 @@ static void handle_hookflash(struct brcm_subchannel *sub, struct brcm_subchannel } /* Provide new line */ + p->dialtone = DIALTONE_ON; brcm_signal_dialtone(p); sub_peer->channel_state = OFFHOOK; @@ -2140,7 +2217,7 @@ static void *brcm_process_event(struct endpt_event *ev) { struct brcm_pvt *p = NULL; struct brcm_subchannel *sub = NULL; - ast_debug(9, "Event %s detected\n", ev->name); + ast_debug(9, "Event %s detected from line %d\n", ev->name, ev->line); p = brcm_get_pvt_from_lineid(iflist, ev->line); if (!p) { ast_debug(3, "No pvt with the correct line_id %d found!\n", ev->line); @@ -2234,21 +2311,21 @@ static void *brcm_process_event(struct endpt_event *ev) { } else if (sub->channel_state == OFFHOOK) { /* EVENT_OFFHOOK changed endpoint state to OFFHOOK, apply dialtone */ - ast_debug(9, "Resetting dial tones.\n"); - if ( p->context[0] != '\0' && is_sip_account_registered(p->context)) { - p->dialtone = DIALTONE_ON; - brcm_signal_dialtone(p); - } - - line_settings *s = &line_config[p->line_id]; - - if (strlen(s->autodial_ext)) { - /* Schedule autodial timeout if autodial extension is set */ - p->autodial_timer_id = ast_sched_add(sched, s->autodial_timeoutmsec, handle_autodial_timeout, p); - } - else { - /* No autodial, schedule dialtone timeout */ - p->dialtone_timeout_timer_id = ast_sched_add(sched, s->dialtone_timeoutmsec, handle_dialtone_timeout, p); + if ( p->context[0] != '\0' && is_sip_account_registered(p->context)) { + ast_debug(9, "Resetting dial tones.\n"); + p->dialtone = DIALTONE_ON; + brcm_signal_dialtone(p); + line_settings *s = &line_config[p->line_id]; + if (strlen(s->autodial_ext)) { + /* Schedule autodial timeout if autodial extension is set */ + p->autodial_timer_id = ast_sched_add(sched, s->autodial_timeoutmsec, handle_autodial_timeout, p); + } else { + /* No autodial, schedule dialtone timeout */ + ast_verbose("Scheduling dialtone timeout in %dms\n", s->dialtone_timeoutmsec); + p->dialtone_timeout_timer_id = ast_sched_add(sched, s->dialtone_timeoutmsec, handle_dialtone_timeout, p); + } + } else { + ast_debug(9, "OFFHOOK but SIP account not registered\n"); } } break; @@ -2678,6 +2755,7 @@ static void brcm_show_pvts(struct ast_cli_args *a) dialtone++; } ast_cli(a->fd, "%s\n", dialtone->str); + ast_cli(a->fd, "Dialtone Timer id : %d\n", p->dialtone_timeout_timer_id); /* Print status for subchannels */ brcm_show_subchannels(a, p); @@ -3014,6 +3092,8 @@ static line_settings line_settings_create(void) .period = 20, .hangup_xfer = 0, .dialtone_timeoutmsec = 20000, + .offhook_nu_timeoutmsec = 60000, + .offhook_silence_timeoutmsec = 180000, .callwaiting = 1, .do_not_disturb = 0, .clir = 0, @@ -3052,6 +3132,10 @@ static void line_settings_load(line_settings *line_config, struct ast_variable * line_config->autodial_timeoutmsec = atoi(v->value); } else if (!strcasecmp(v->name, "dialtone_timeoutmsec")) { line_config->dialtone_timeoutmsec = atoi(v->value); + } else if (!strcasecmp(v->name, "offhook_nu_timeoutmsec")) { + line_config->offhook_nu_timeoutmsec = atoi(v->value); + } else if (!strcasecmp(v->name, "offhook_silence_timeoutmsec")) { + line_config->offhook_silence_timeoutmsec = atoi(v->value); } else if (!strcasecmp(v->name, "hangup_xfer")) { line_config->hangup_xfer = ast_true(v->value)?1:0; diff --git a/channels/chan_brcm.h b/channels/chan_brcm.h index 695cd4c7bd..aa5ff7c06b 100644 --- a/channels/chan_brcm.h +++ b/channels/chan_brcm.h @@ -86,6 +86,8 @@ typedef enum dialtone_state { DIALTONE_ON, DIALTONE_CONGESTION, DIALTONE_SPECIAL_CONDITION, + DIALTONE_UNOBTAINABLE, + DIALTONE_HOWLER, DIALTONE_UNKNOWN, DIALTONE_LAST, } dialtone_state; @@ -175,7 +177,7 @@ typedef struct DTMF_CHARNAME_MAP typedef struct DIALTONE_MAP { dialtone_state state; - char str[11]; + char str[24]; } DIALTONE_MAP; static const DIALTONE_MAP dialtone_map[] = @@ -184,6 +186,8 @@ static const DIALTONE_MAP dialtone_map[] = {DIALTONE_ON, "on"}, {DIALTONE_CONGESTION, "congestion"}, {DIALTONE_SPECIAL_CONDITION, "special"}, + {DIALTONE_UNOBTAINABLE, "number unobtainable"}, + {DIALTONE_HOWLER, "howler"}, {DIALTONE_UNKNOWN, "unknown"}, {DIALTONE_LAST, "-"}, }; @@ -205,6 +209,8 @@ typedef struct { int period; int hangup_xfer; int dialtone_timeoutmsec; + int offhook_nu_timeoutmsec; + int offhook_silence_timeoutmsec; int callwaiting; int do_not_disturb; int clir; -- GitLab