From 58b440bc29b4731862ae92a16db83b3ee24734b6 Mon Sep 17 00:00:00 2001 From: Richard Mudgett Date: Tue, 14 Jul 2009 17:01:48 +0000 Subject: Merged revisions 206487 via svnmerge from https://origsvn.digium.com/svn/asterisk/branches/1.4 ........ r206487 | rmudgett | 2009-07-14 11:44:47 -0500 (Tue, 14 Jul 2009) | 28 lines Fixes several call transfer issues with chan_misdn. * issue #14355 - Crash if attempt to transfer a call to an application. Masquerade the other pair of the four asterisk channels involved in the two calls. The held call already must be a bridged call (not an applicaton) or it would have been rejected. * issue #14692 - Held calls are not automatically cleared after transfer. Allow the core to initate disconnect of held calls to the ISDN port. This also fixes a similar case where the party on hold hangs up before being transferred or taken off hold. * JIRA ABE-1903 - Orphaned held calls left in music-on-hold. Do not simply block passing the hangup event on held calls to asterisk core. * Fixed to allow held calls to be transferred to ringing calls. Previously, held calls could only be transferred to connected calls. * Eliminated unused call states to simplify hangup code. * Eliminated most uses of "holded" because it is not a word. (closes issue #14355) (closes issue #14692) Reported by: sodom Patches: misdn_xfer_v14_r205839.patch uploaded by rmudgett (license 664) Tested by: rmudgett ........ git-svn-id: https://origsvn.digium.com/svn/asterisk/trunk@206489 65c4cc65-6c06-0410-ace0-fbb531ad65f3 --- channels/chan_misdn.c | 597 +++++++++++++++++++++++++++------------------- channels/misdn/isdn_lib.c | 77 +++--- channels/misdn/isdn_lib.h | 3 +- 3 files changed, 394 insertions(+), 283 deletions(-) diff --git a/channels/chan_misdn.c b/channels/chan_misdn.c index 5be536abf..3a5a5b4f1 100644 --- a/channels/chan_misdn.c +++ b/channels/chan_misdn.c @@ -305,15 +305,8 @@ enum misdn_chan_state { MISDN_ALERTING, /*!< when Alerting */ MISDN_BUSY, /*!< when BUSY */ MISDN_CONNECTED, /*!< when connected */ - MISDN_PRECONNECTED, /*!< when connected (Noone sets this state) */ MISDN_DISCONNECTED, /*!< when connected */ - MISDN_RELEASED, /*!< when connected */ - MISDN_BRIDGED, /*!< when bridged */ MISDN_CLEANING, /*!< when hangup from * but we were connected before */ - MISDN_HUNGUP_FROM_MISDN, /*!< when DISCONNECT/RELEASE/REL_COMP came from misdn */ - MISDN_HUNGUP_FROM_AST, /*!< when DISCONNECT/RELEASE/REL_COMP came out of misdn_hangup */ - MISDN_HOLDED, /*!< when on hold */ - MISDN_HOLD_DISCONNECT, /*!< when on hold */ }; /*! Asterisk created the channel (outgoing call) */ @@ -321,15 +314,25 @@ enum misdn_chan_state { /*! mISDN created the channel (incoming call) */ #define ORG_MISDN 2 +enum misdn_hold_state { + MISDN_HOLD_IDLE, /*!< HOLD not active */ + MISDN_HOLD_ACTIVE, /*!< Call is held */ + MISDN_HOLD_TRANSFER, /*!< Held call is being transferred */ + MISDN_HOLD_DISCONNECT, /*!< Held call is being disconnected */ +}; struct hold_info { /*! - * \brief Logical port the channel call record is HOLDED on + * \brief Call HOLD state. + */ + enum misdn_hold_state state; + /*! + * \brief Logical port the channel call record is HELD on * because the B channel is no longer associated. */ int port; /*! - * \brief Original B channel number the HOLDED call was using. + * \brief Original B channel number the HELD call was using. * \note Used only for debug display messages. */ int channel; @@ -501,13 +504,13 @@ struct chan_list { #endif /* defined(AST_MISDN_ENHANCEMENTS) */ /*! - * \brief HOLDED channel information + * \brief HELD channel call information */ - struct hold_info hold_info; + struct hold_info hold; /*! * \brief From associated B channel: Layer 3 process ID - * \note Used to find the HOLDED channel call record when retrieving a call. + * \note Used to find the HELD channel call record when retrieving a call. */ unsigned int l3id; @@ -662,7 +665,6 @@ static void chan_misdn_log(int level, int port, char *tmpl, ...) static struct ast_channel *misdn_new(struct chan_list *cl, int state, char *exten, char *callerid, int format, const char *linkedid, int port, int c); static void send_digit_to_chan(struct chan_list *cl, char digit); -static void hangup_chan(struct chan_list *ch); static int pbx_start_chan(struct chan_list *ch); #define MISDN_ASTERISK_TECH_PVT(ast) ast->tech_pvt @@ -702,12 +704,13 @@ static struct chan_list *find_chan_by_bc(struct chan_list *list, struct misdn_bc static struct chan_list *find_chan_by_pid(struct chan_list *list, int pid); static int dialtone_indicate(struct chan_list *cl); -static int hanguptone_indicate(struct chan_list *cl); +static void hanguptone_indicate(struct chan_list *cl); static int stop_indicate(struct chan_list *cl); static int start_bc_tones(struct chan_list *cl); static int stop_bc_tones(struct chan_list *cl); -static void release_chan(struct misdn_bchannel *bc); +static void release_chan_early(struct chan_list *ch); +static void release_chan(struct chan_list *ch, struct misdn_bchannel *bc); #if defined(AST_MISDN_ENHANCEMENTS) static const char misdn_command_name[] = "misdn_command"; @@ -4039,15 +4042,8 @@ static const struct state_struct state_array[] = { { MISDN_ALERTING, "ALERTING" }, /* when Alerting */ { MISDN_BUSY, "BUSY" }, /* when BUSY */ { MISDN_CONNECTED, "CONNECTED" }, /* when connected */ - { MISDN_PRECONNECTED, "PRECONNECTED" }, /* when connected */ { MISDN_DISCONNECTED, "DISCONNECTED" }, /* when connected */ - { MISDN_RELEASED, "RELEASED" }, /* when connected */ - { MISDN_BRIDGED, "BRIDGED" }, /* when bridged */ { MISDN_CLEANING, "CLEANING" }, /* when hangup from * but we were connected before */ - { MISDN_HUNGUP_FROM_MISDN, "HUNGUP_FROM_MISDN" }, /* when DISCONNECT/RELEASE/REL_COMP came from misdn */ - { MISDN_HOLDED, "HOLDED" }, /* when DISCONNECT/RELEASE/REL_COMP came from misdn */ - { MISDN_HOLD_DISCONNECT, "HOLD_DISCONNECT" }, /* when DISCONNECT/RELEASE/REL_COMP came from misdn */ - { MISDN_HUNGUP_FROM_AST, "HUNGUP_FROM_AST" }, /* when DISCONNECT/RELEASE/REL_COMP came out of misdn_hangup */ /* *INDENT-ON* */ }; @@ -4218,8 +4214,8 @@ static char *handle_cli_misdn_show_channels(struct ast_cli_entry *e, int cmd, st if (bc) { print_bc_info(a->fd, help, bc); } else { - if (help->state == MISDN_HOLDED) { - ast_cli(a->fd, "ITS A HOLDED BC:\n"); + if (help->hold.state != MISDN_HOLD_IDLE) { + ast_cli(a->fd, "ITS A HELD CALL BC:\n"); ast_cli(a->fd, " --> l3_id: %x\n" " --> dialed:%s\n" " --> caller:\"%s\" <%s>\n" @@ -4229,8 +4225,8 @@ static char *handle_cli_misdn_show_channels(struct ast_cli_entry *e, int cmd, st ast->exten, ast->cid.cid_name ? ast->cid.cid_name : "", ast->cid.cid_num ? ast->cid.cid_num : "", - help->hold_info.port, - help->hold_info.channel + help->hold.port, + help->hold.channel ); } else { ast_cli(a->fd, "* Channel in unknown STATE !!! Exten:%s, Callerid:%s\n", ast->exten, ast->cid.cid_num); @@ -6662,12 +6658,18 @@ static int misdn_indication(struct ast_channel *ast, int cond, const void *data, } if (!p->bc) { - chan_misdn_log(1, 0, "* IND : Indication from %s\n", ast->exten); - ast_log(LOG_WARNING, "Private Pointer but no bc ?\n"); + if (p->hold.state == MISDN_HOLD_IDLE) { + chan_misdn_log(1, 0, "* IND : Indication [%d] ignored on %s\n", cond, + ast->name); + ast_log(LOG_WARNING, "Private Pointer but no bc ?\n"); + } else { + chan_misdn_log(1, 0, "* IND : Indication [%d] ignored on hold %s\n", + cond, ast->name); + } return -1; } - chan_misdn_log(5, p->bc->port, "* IND : Indication [%d] from %s\n", cond, ast->exten); + chan_misdn_log(5, p->bc->port, "* IND : Indication [%d] on %s\n\n", cond, ast->name); switch (cond) { case AST_CONTROL_BUSY: @@ -6778,7 +6780,7 @@ static int misdn_indication(struct ast_channel *ast, int cond, const void *data, break; default: chan_misdn_log(1, p->bc->port, " --> * Unknown Indication:%d pid:%d\n", cond, p->bc->pid); - break; + return -1; } return 0; @@ -6787,91 +6789,79 @@ static int misdn_indication(struct ast_channel *ast, int cond, const void *data, static int misdn_hangup(struct ast_channel *ast) { struct chan_list *p; - struct misdn_bchannel *bc = NULL; - const char *varcause = NULL; - - ast_debug(1, "misdn_hangup(%s)\n", ast->name); + struct misdn_bchannel *bc; + const char *var; if (!ast || !(p = MISDN_ASTERISK_TECH_PVT(ast))) { return -1; } + MISDN_ASTERISK_TECH_PVT(ast) = NULL; - if (!p) { - chan_misdn_log(3, 0, "misdn_hangup called, without chan_list obj.\n"); - return 0 ; - } - - bc = p->bc; - - if (bc) { - const char *tmp; + ast_debug(1, "misdn_hangup(%s)\n", ast->name); - ast_channel_lock(ast); - tmp = pbx_builtin_getvar_helper(ast, "MISDN_USERUSER"); - if (tmp) { - ast_log(LOG_NOTICE, "MISDN_USERUSER: %s\n", tmp); - strcpy(bc->uu, tmp); - bc->uulen = strlen(bc->uu); + if (p->hold.state == MISDN_HOLD_IDLE) { + bc = p->bc; + } else { + p->hold.state = MISDN_HOLD_DISCONNECT; + bc = misdn_lib_find_held_bc(p->hold.port, p->l3id); + if (!bc) { + chan_misdn_log(4, p->hold.port, + "misdn_hangup: Could not find held bc for (%s)\n", ast->name); + release_chan_early(p); + return 0; } - ast_channel_unlock(ast); } - MISDN_ASTERISK_TECH_PVT(ast) = NULL; - p->ast = NULL; - - if (ast->_state == AST_STATE_RESERVED || - p->state == MISDN_NOTHING || - p->state == MISDN_HOLDED || - p->state == MISDN_HOLD_DISCONNECT) { - -CLEAN_CH: + if (ast->_state == AST_STATE_RESERVED || p->state == MISDN_NOTHING) { /* between request and call */ ast_debug(1, "State Reserved (or nothing) => chanIsAvail\n"); - MISDN_ASTERISK_TECH_PVT(ast) = NULL; - - ast_mutex_lock(&release_lock); - cl_dequeue_chan(&cl_te, p); - close(p->pipe[0]); - close(p->pipe[1]); - ast_free(p); - ast_mutex_unlock(&release_lock); - + release_chan_early(p); if (bc) { misdn_lib_release(bc); } - return 0; } - if (!bc) { - ast_log(LOG_WARNING, "Hangup with private but no bc ? state:%s l3id:%x\n", misdn_get_ch_state(p), p->l3id); - goto CLEAN_CH; + ast_log(LOG_WARNING, "Hangup with private but no bc ? state:%s l3id:%x\n", + misdn_get_ch_state(p), p->l3id); + release_chan_early(p); + return 0; } - + p->ast = NULL; p->need_hangup = 0; p->need_queue_hangup = 0; p->need_busy = 0; - - if (!p->bc->nt) { + if (!bc->nt) { stop_bc_tones(p); } bc->out_cause = ast->hangupcause ? ast->hangupcause : AST_CAUSE_NORMAL_CLEARING; ast_channel_lock(ast); - if ((varcause = pbx_builtin_getvar_helper(ast, "HANGUPCAUSE")) || - (varcause = pbx_builtin_getvar_helper(ast, "PRI_CAUSE"))) { - int tmpcause = atoi(varcause); + var = pbx_builtin_getvar_helper(ast, "HANGUPCAUSE"); + if (!var) { + var = pbx_builtin_getvar_helper(ast, "PRI_CAUSE"); + } + if (var) { + int tmpcause; + tmpcause = atoi(var); bc->out_cause = tmpcause ? tmpcause : AST_CAUSE_NORMAL_CLEARING; } + + var = pbx_builtin_getvar_helper(ast, "MISDN_USERUSER"); + if (var) { + ast_log(LOG_NOTICE, "MISDN_USERUSER: %s\n", var); + ast_copy_string(bc->uu, var, sizeof(bc->uu)); + bc->uulen = strlen(bc->uu); + } ast_channel_unlock(ast); chan_misdn_log(1, bc->port, "* IND : HANGUP\tpid:%d context:%s dialed:%s caller:\"%s\" <%s> State:%s\n", - p->bc ? p->bc->pid : -1, + bc->pid, ast->context, ast->exten, ast->cid.cid_name ? ast->cid.cid_name : "", @@ -6883,27 +6873,29 @@ CLEAN_CH: switch (p->state) { case MISDN_INCOMING_SETUP: - p->state = MISDN_CLEANING; - /* This is the only place in misdn_hangup, where we - * can call release_chan, else it might create lot's of trouble - * */ + /* + * This is the only place in misdn_hangup, where we + * can call release_chan, else it might create a lot of trouble. + */ ast_log(LOG_NOTICE, "release channel, in INCOMING_SETUP state.. no other events happened\n"); - release_chan(bc); + release_chan(p, bc); misdn_lib_send_event(bc, EVENT_RELEASE_COMPLETE); - break; - case MISDN_HOLDED: + return 0; case MISDN_DIALING: - start_bc_tones(p); - hanguptone_indicate(p); + if (p->hold.state == MISDN_HOLD_IDLE) { + start_bc_tones(p); + hanguptone_indicate(p); + } - p->state = MISDN_CLEANING; if (bc->need_disconnect) { misdn_lib_send_event(bc, EVENT_DISCONNECT); } break; case MISDN_CALLING_ACKNOWLEDGE: - start_bc_tones(p); - hanguptone_indicate(p); + if (p->hold.state == MISDN_HOLD_IDLE) { + start_bc_tones(p); + hanguptone_indicate(p); + } if (bc->need_disconnect) { misdn_lib_send_event(bc, EVENT_DISCONNECT); @@ -6914,79 +6906,53 @@ CLEAN_CH: case MISDN_ALERTING: case MISDN_PROGRESS: case MISDN_PROCEEDING: - if (p->originator != ORG_AST) { + if (p->originator != ORG_AST && p->hold.state == MISDN_HOLD_IDLE) { hanguptone_indicate(p); } - /*p->state=MISDN_CLEANING;*/ if (bc->need_disconnect) { misdn_lib_send_event(bc, EVENT_DISCONNECT); } break; case MISDN_CONNECTED: - case MISDN_PRECONNECTED: /* Alerting or Disconnect */ - if (p->bc->nt) { + if (bc->nt && p->hold.state == MISDN_HOLD_IDLE) { start_bc_tones(p); hanguptone_indicate(p); - p->bc->progress_indicator = INFO_PI_INBAND_AVAILABLE; + bc->progress_indicator = INFO_PI_INBAND_AVAILABLE; } if (bc->need_disconnect) { misdn_lib_send_event(bc, EVENT_DISCONNECT); } - - /*p->state=MISDN_CLEANING;*/ break; case MISDN_DISCONNECTED: if (bc->need_release) { misdn_lib_send_event(bc, EVENT_RELEASE); } - p->state = MISDN_CLEANING; /* MISDN_HUNGUP_FROM_AST; */ break; - case MISDN_RELEASED: case MISDN_CLEANING: - p->state = MISDN_CLEANING; - break; + return 0; case MISDN_BUSY: break; - - case MISDN_HOLD_DISCONNECT: - /* need to send release here */ - chan_misdn_log(1, bc->port, " --> cause %d\n", bc->cause); - chan_misdn_log(1, bc->port, " --> out_cause %d\n", bc->out_cause); - - bc->out_cause = -1; - if (bc->need_release) { - misdn_lib_send_event(bc, EVENT_RELEASE); - } - p->state = MISDN_CLEANING; - break; default: if (bc->nt) { bc->out_cause = -1; if (bc->need_release) { misdn_lib_send_event(bc, EVENT_RELEASE); } - p->state = MISDN_CLEANING; } else { if (bc->need_disconnect) { misdn_lib_send_event(bc, EVENT_DISCONNECT); } } + break; } p->state = MISDN_CLEANING; - -#if defined(AST_MISDN_ENHANCEMENTS) - if (p->peer) { - ao2_ref(p->peer, -1); - p->peer = NULL; - } -#endif /* AST_MISDN_ENHANCEMENTS */ - - chan_misdn_log(3, bc->port, " --> Channel: %s hanguped new state:%s\n", ast->name, misdn_get_ch_state(p)); + chan_misdn_log(3, bc->port, " --> Channel: %s hungup new state:%s\n", ast->name, + misdn_get_ch_state(p)); return 0; } @@ -7082,7 +7048,7 @@ static struct ast_frame *misdn_read(struct ast_channel *ast) return NULL; } - if (!tmp->bc && !(tmp->state == MISDN_HOLDED)) { + if (!tmp->bc && tmp->hold.state == MISDN_HOLD_IDLE) { chan_misdn_log(1, 0, "misdn_read called without bc\n"); return NULL; } @@ -7164,8 +7130,8 @@ static int misdn_write(struct ast_channel *ast, struct ast_frame *frame) return -1; } - if (ch->state == MISDN_HOLDED) { - chan_misdn_log(7, 0, "misdn_write: Returning because holded\n"); + if (ch->hold.state != MISDN_HOLD_IDLE) { + chan_misdn_log(7, 0, "misdn_write: Returning because hold active\n"); return 0; } @@ -7398,10 +7364,9 @@ static int dialtone_indicate(struct chan_list *cl) return 0; } -static int hanguptone_indicate(struct chan_list *cl) +static void hanguptone_indicate(struct chan_list *cl) { misdn_lib_send_tone(cl->bc, TONE_HANGUP); - return 0; } static int stop_indicate(struct chan_list *cl) @@ -7903,7 +7868,7 @@ static struct chan_list *find_chan_by_pid(struct chan_list *list, int pid) return NULL; } -static struct chan_list *find_holded(struct chan_list *list, struct misdn_bchannel *bc) +static struct chan_list *find_hold_call(struct chan_list *list, struct misdn_bchannel *bc) { struct chan_list *help = list; @@ -7911,20 +7876,19 @@ static struct chan_list *find_holded(struct chan_list *list, struct misdn_bchann return NULL; } - chan_misdn_log(6, bc->port, "$$$ find_holded: channel:%d dialed:%s caller:\"%s\" <%s>\n", + chan_misdn_log(6, bc->port, "$$$ find_hold_call: channel:%d dialed:%s caller:\"%s\" <%s>\n", bc->channel, bc->dialed.number, bc->caller.name, bc->caller.number); for (; help; help = help->next) { - chan_misdn_log(4, bc->port, "$$$ find_holded: --> holded:%d channel:%d\n", help->state == MISDN_HOLDED, help->hold_info.channel); - if ((help->state == MISDN_HOLDED) && - (help->hold_info.port == bc->port)) { + chan_misdn_log(4, bc->port, "$$$ find_hold_call: --> hold:%d channel:%d\n", help->hold.state, help->hold.channel); + if (help->hold.state == MISDN_HOLD_ACTIVE && help->hold.port == bc->port) { return help; } } chan_misdn_log(6, bc->port, - "$$$ find_holded: No channel found for dialed:%s caller:\"%s\" <%s>\n", + "$$$ find_hold_call: No channel found for dialed:%s caller:\"%s\" <%s>\n", bc->dialed.number, bc->caller.name, bc->caller.number); @@ -7933,13 +7897,12 @@ static struct chan_list *find_holded(struct chan_list *list, struct misdn_bchann } -static struct chan_list *find_holded_l3(struct chan_list *list, unsigned long l3_id, int w) +static struct chan_list *find_hold_call_l3(struct chan_list *list, unsigned long l3_id) { struct chan_list *help = list; for (; help; help = help->next) { - if ((help->state == MISDN_HOLDED) && - (help->l3id == l3_id)) { + if (help->hold.state != MISDN_HOLD_IDLE && help->l3id == l3_id) { return help; } } @@ -7947,6 +7910,42 @@ static struct chan_list *find_holded_l3(struct chan_list *list, unsigned long l3 return NULL; } +#define TRANSFER_ON_HELD_CALL_HANGUP 1 +#if defined(TRANSFER_ON_HELD_CALL_HANGUP) +/*! + * \internal + * \brief Find a suitable active call to go with a held call so we could try a transfer. + * + * \param list Channel list. + * \param bc B channel record. + * + * \return Found call record or NULL. + * + * \note There could be a possibility where we find the wrong active call to transfer. + * This concern is mitigated by the fact that there could be at most one other call + * on a PTMP BRI link to another device. Maybe the l3_id could help in locating an + * active call on the same TEI? + */ +static struct chan_list *find_hold_active_call(struct chan_list *list, struct misdn_bchannel *bc) +{ + for (; list; list = list->next) { + if (list->hold.state == MISDN_HOLD_IDLE && list->bc && list->bc->port == bc->port + && list->ast) { + switch (list->state) { + case MISDN_PROCEEDING: + case MISDN_PROGRESS: + case MISDN_ALERTING: + case MISDN_CONNECTED: + return list; + default: + break; + } + } + } + return NULL; +} +#endif /* defined(TRANSFER_ON_HELD_CALL_HANGUP) */ + static void cl_queue_chan(struct chan_list **list, struct chan_list *chan) { chan_misdn_log(4, chan->bc ? chan->bc->port : 0, "* Queuing chan %p\n", chan); @@ -8009,7 +8008,7 @@ static int pbx_start_chan(struct chan_list *ch) return ret; } -static void hangup_chan(struct chan_list *ch) +static void hangup_chan(struct chan_list *ch, struct misdn_bchannel *bc) { int port; @@ -8018,15 +8017,15 @@ static void hangup_chan(struct chan_list *ch) return; } - port = ch->bc ? ch->bc->port : 0; + port = bc->port; cb_log(5, port, "hangup_chan called\n"); if (ch->need_hangup) { cb_log(2, port, " --> hangup\n"); - send_cause2ast(ch->ast, ch->bc, ch); ch->need_hangup = 0; ch->need_queue_hangup = 0; if (ch->ast) { + send_cause2ast(ch->ast, bc, ch); ast_hangup(ch->ast); } return; @@ -8038,33 +8037,43 @@ static void hangup_chan(struct chan_list *ch) ch->need_queue_hangup = 0; if (ch->ast) { - send_cause2ast(ch->ast, ch->bc, ch); - - if (ch->ast) { - ast_queue_hangup_with_cause(ch->ast, ch->bc->cause); - } + send_cause2ast(ch->ast, bc, ch); + ast_queue_hangup_with_cause(ch->ast, bc->cause); cb_log(2, port, " --> queue_hangup\n"); } else { cb_log(1, port, "Cannot hangup chan, no ast\n"); } } -/** Isdn asks us to release channel, pendant to misdn_hangup **/ -static void release_chan(struct misdn_bchannel *bc) +/*! + * \internal + * \brief ISDN asked us to release channel, pendant to misdn_hangup. + * + * \param ch Call channel record to release. + * \param bc Current B channel record associated with ch. + * + * \return Nothing + * + * \note ch must not be referenced after calling. + */ +static void release_chan(struct chan_list *ch, struct misdn_bchannel *bc) { - struct chan_list *ch; struct ast_channel *ast; + ch->state = MISDN_CLEANING; + ast_mutex_lock(&release_lock); - ch = find_chan_by_bc(cl_te, bc); - if (!ch) { - chan_misdn_log(1, bc->port, "release_chan: Ch not found!\n"); - ast_mutex_unlock(&release_lock); - return; +#if defined(AST_MISDN_ENHANCEMENTS) + if (ch->peer) { + ao2_ref(ch->peer, -1); + ch->peer = NULL; } +#endif /* AST_MISDN_ENHANCEMENTS */ + + cl_dequeue_chan(&cl_te, ch); - chan_misdn_log(5, bc->port, "release_chan: bc with l3id: %x\n", bc->l3_id); + chan_misdn_log(5, bc->port, "release_chan: bc with pid:%d l3id: %x\n", bc->pid, bc->l3_id); /* releasing jitterbuffer */ if (ch->jb) { @@ -8094,17 +8103,15 @@ static void release_chan(struct misdn_bchannel *bc) close(ch->pipe[1]); ast = ch->ast; - if (ast && MISDN_ASTERISK_TECH_PVT(ast)) { + if (ast) { + MISDN_ASTERISK_TECH_PVT(ast) = NULL; chan_misdn_log(1, bc->port, - "* RELEASING CHANNEL pid:%d context:%s dialed:%s caller:\"%s\" <%s> state: %s\n", + "* RELEASING CHANNEL pid:%d context:%s dialed:%s caller:\"%s\" <%s>\n", bc->pid, ast->context, ast->exten, ast->cid.cid_name ? ast->cid.cid_name : "", - ast->cid.cid_num ? ast->cid.cid_num : "", - misdn_get_ch_state(ch)); - chan_misdn_log(3, bc->port, " --> * State Down\n"); - MISDN_ASTERISK_TECH_PVT(ast) = NULL; + ast->cid.cid_num ? ast->cid.cid_num : ""); if (ast->_state != AST_STATE_RESERVED) { chan_misdn_log(3, bc->port, " --> Setting AST State to down\n"); @@ -8112,25 +8119,118 @@ static void release_chan(struct misdn_bchannel *bc) } } + ast_free(ch); + + ast_mutex_unlock(&release_lock); +} + +/*! + * \internal + * \brief Do everything in release_chan() that makes sense without a bc. + * + * \param ch Call channel record to release. + * + * \return Nothing + * + * \note ch must not be referenced after calling. + */ +static void release_chan_early(struct chan_list *ch) +{ + struct ast_channel *ast; + ch->state = MISDN_CLEANING; + + ast_mutex_lock(&release_lock); + +#if defined(AST_MISDN_ENHANCEMENTS) + if (ch->peer) { + ao2_ref(ch->peer, -1); + ch->peer = NULL; + } +#endif /* AST_MISDN_ENHANCEMENTS */ + cl_dequeue_chan(&cl_te, ch); + /* releasing jitterbuffer */ + if (ch->jb) { + misdn_jb_destroy(ch->jb); + ch->jb = NULL; + } + + if (ch->overlap_dial) { + if (ch->overlap_dial_task != -1) { + misdn_tasks_remove(ch->overlap_dial_task); + ch->overlap_dial_task = -1; + } + ast_mutex_destroy(&ch->overlap_tv_lock); + } + + if (ch->hold.state != MISDN_HOLD_IDLE) { + if (ch->originator == ORG_AST) { + --misdn_out_calls[ch->hold.port]; + } else { + --misdn_in_calls[ch->hold.port]; + } + } + + close(ch->pipe[0]); + close(ch->pipe[1]); + + ast = ch->ast; + if (ast) { + MISDN_ASTERISK_TECH_PVT(ast) = NULL; + if (ast->_state != AST_STATE_RESERVED) { + ast_setstate(ast, AST_STATE_DOWN); + } + } + ast_free(ch); ast_mutex_unlock(&release_lock); } -static void misdn_transfer_bc(struct chan_list *tmp_ch, struct chan_list *holded_chan) +/*! + * \internal + * \brief Attempt to transfer the active channel party to the held channel party. + * + * \param active_ch Channel currently connected. + * \param held_ch Channel currently on hold. + * + * \retval 0 on success. + * \retval -1 on error. + */ +static int misdn_attempt_transfer(struct chan_list *active_ch, struct chan_list *held_ch) { - chan_misdn_log(4, 0, "TRANSFERRING %s to %s\n", holded_chan->ast->name, tmp_ch->ast->name); + int retval; + struct ast_channel *bridged; - tmp_ch->state = MISDN_HOLD_DISCONNECT; + switch (active_ch->state) { + case MISDN_PROCEEDING: + case MISDN_PROGRESS: + case MISDN_ALERTING: + case MISDN_CONNECTED: + break; + default: + return -1; + } - ast_moh_stop(ast_bridged_channel(holded_chan->ast)); + bridged = ast_bridged_channel(held_ch->ast); + if (bridged) { + ast_queue_control(held_ch->ast, AST_CONTROL_UNHOLD); + held_ch->hold.state = MISDN_HOLD_TRANSFER; - holded_chan->state = MISDN_CONNECTED; - /* misdn_lib_transfer(holded_chan->bc); */ - ast_channel_masquerade(holded_chan->ast, ast_bridged_channel(tmp_ch->ast)); + chan_misdn_log(1, held_ch->hold.port, "TRANSFERRING %s to %s\n", + held_ch->ast->name, active_ch->ast->name); + retval = ast_channel_masquerade(active_ch->ast, bridged); + } else { + /* + * Could not transfer. Held channel is not bridged anymore. + * Held party probably got tired of waiting and hung up. + */ + retval = -1; + } + + return retval; } @@ -8169,7 +8269,7 @@ static void do_immediate_setup(struct misdn_bchannel *bc, struct chan_list *ch, if (!ast_canmatch_extension(ast, ast->context, ast->exten, 1, bc->caller.number) || pbx_start_chan(ch) < 0) { ast = NULL; bc->out_cause = AST_CAUSE_UNALLOCATED; - hangup_chan(ch); + hangup_chan(ch, bc); hanguptone_indicate(ch); misdn_lib_send_event(bc, bc->nt ? EVENT_RELEASE_COMPLETE : EVENT_DISCONNECT); @@ -8351,7 +8451,7 @@ int add_out_calls(int port) static void start_pbx(struct chan_list *ch, struct misdn_bchannel *bc, struct ast_channel *chan) { if (pbx_start_chan(ch) < 0) { - hangup_chan(ch); + hangup_chan(ch, bc); chan_misdn_log(-1, bc->port, "ast_pbx_start returned <0 in SETUP\n"); if (bc->nt) { hanguptone_indicate(ch); @@ -9299,6 +9399,7 @@ cb_events(enum event_e event, struct misdn_bchannel *bc, void *user_data) #if defined(AST_MISDN_ENHANCEMENTS) struct misdn_cc_record *cc_record; #endif /* defined(AST_MISDN_ENHANCEMENTS) */ + struct chan_list *held_ch; struct chan_list *ch = find_chan_by_bc(cl_te, bc); if (event != EVENT_BCHAN_DATA && event != EVENT_TONE_GENERATE) { @@ -9327,15 +9428,14 @@ cb_events(enum event_e event, struct misdn_bchannel *bc, void *user_data) switch(event) { case EVENT_SETUP: case EVENT_DISCONNECT: + case EVENT_RELEASE: + case EVENT_RELEASE_COMPLETE: case EVENT_PORT_ALARM: case EVENT_RETRIEVE: case EVENT_NEW_BC: case EVENT_FACILITY: case EVENT_REGISTER: break; - case EVENT_RELEASE_COMPLETE: - chan_misdn_log(1, bc->port, " --> no Ch, so we've already released.\n"); - break; case EVENT_CLEANUP: case EVENT_TONE_GENERATE: case EVENT_BCHAN_DATA: @@ -9396,7 +9496,7 @@ cb_events(enum event_e event, struct misdn_bchannel *bc, void *user_data) case EVENT_NEW_BC: if (!ch) { - ch = find_holded(cl_te,bc); + ch = find_hold_call(cl_te,bc); } if (!ch) { @@ -9458,10 +9558,10 @@ cb_events(enum event_e event, struct misdn_bchannel *bc, void *user_data) /* Check for Pickup Request first */ if (!strcmp(ch->ast->exten, ast_pickup_ext())) { if (ast_pickup_call(ch->ast)) { - hangup_chan(ch); + hangup_chan(ch, bc); } else { ch->state = MISDN_CALLING_ACKNOWLEDGE; - hangup_chan(ch); + hangup_chan(ch, bc); ch->ast = NULL; break; } @@ -9681,10 +9781,10 @@ cb_events(enum event_e event, struct misdn_bchannel *bc, void *user_data) ch->state = MISDN_INCOMING_SETUP; } if (ast_pickup_call(chan)) { - hangup_chan(ch); + hangup_chan(ch, bc); } else { ch->state = MISDN_CALLING_ACKNOWLEDGE; - hangup_chan(ch); + hangup_chan(ch, bc); ch->ast = NULL; break; } @@ -9988,8 +10088,6 @@ cb_events(enum event_e event, struct misdn_bchannel *bc, void *user_data) case EVENT_DISCONNECT: /* we might not have an ch->ast ptr here anymore */ if (ch) { - struct chan_list *holded_ch = find_holded(cl_te, bc); - if (bc->fac_in.Function != Fac_None) { misdn_facility_ie_handler(event, bc, ch); } @@ -10016,23 +10114,40 @@ cb_events(enum event_e event, struct misdn_bchannel *bc, void *user_data) break; } - /* Check for holded channel, to implement transfer */ - if (holded_ch && holded_ch != ch && ch->ast && ch->state == MISDN_CONNECTED) { - cb_log(1, bc->port, " --> found holded ch\n"); - misdn_transfer_bc(ch, holded_ch) ; - } - bc->need_disconnect = 0; - stop_bc_tones(ch); - hangup_chan(ch); -#if 0 + + /* Check for held channel, to implement transfer */ + held_ch = find_hold_call(cl_te, bc); + if (!held_ch || !ch->ast || misdn_attempt_transfer(ch, held_ch)) { + hangup_chan(ch, bc); + } } else { - ch = find_holded_l3(cl_te, bc->l3_id,1); - if (ch) { - hangup_chan(ch); + held_ch = find_hold_call_l3(cl_te, bc->l3_id); + if (held_ch) { + if (bc->fac_in.Function != Fac_None) { + misdn_facility_ie_handler(event, bc, held_ch); + } + + if (held_ch->hold.state == MISDN_HOLD_ACTIVE) { + bc->need_disconnect = 0; + +#if defined(TRANSFER_ON_HELD_CALL_HANGUP) + /* + * Some phones disconnect the held call and the active call at the + * same time to do the transfer. Unfortunately, either call could + * be disconnected first. + */ + ch = find_hold_active_call(cl_te, bc); + if (!ch || misdn_attempt_transfer(ch, held_ch)) { + held_ch->hold.state = MISDN_HOLD_DISCONNECT; + hangup_chan(held_ch, bc); + } +#else + hangup_chan(held_ch, bc); +#endif /* defined(TRANSFER_ON_HELD_CALL_HANGUP) */ + } } -#endif } bc->out_cause = -1; if (bc->need_release) { @@ -10040,6 +10155,15 @@ cb_events(enum event_e event, struct misdn_bchannel *bc, void *user_data) } break; case EVENT_RELEASE: + if (!ch) { + ch = find_hold_call_l3(cl_te, bc->l3_id); + if (!ch) { + chan_misdn_log(1, bc->port, + " --> no Ch, so we've already released. (%s)\n", + manager_isdn_get_info(event)); + return -1; + } + } if (bc->fac_in.Function != Fac_None) { misdn_facility_ie_handler(event, bc, ch); } @@ -10047,12 +10171,12 @@ cb_events(enum event_e event, struct misdn_bchannel *bc, void *user_data) bc->need_disconnect = 0; bc->need_release = 0; - hangup_chan(ch); - release_chan(bc); + hangup_chan(ch, bc); + release_chan(ch, bc); break; case EVENT_RELEASE_COMPLETE: - if (bc->fac_in.Function != Fac_None) { - misdn_facility_ie_handler(event, bc, ch); + if (!ch) { + ch = find_hold_call_l3(cl_te, bc->l3_id); } bc->need_disconnect = 0; @@ -10060,11 +10184,15 @@ cb_events(enum event_e event, struct misdn_bchannel *bc, void *user_data) bc->need_release_complete = 0; if (ch) { + if (bc->fac_in.Function != Fac_None) { + misdn_facility_ie_handler(event, bc, ch); + } + stop_bc_tones(ch); - hangup_chan(ch); - ch->state = MISDN_CLEANING; -#if defined(AST_MISDN_ENHANCEMENTS) + hangup_chan(ch, bc); + release_chan(ch, bc); } else { +#if defined(AST_MISDN_ENHANCEMENTS) /* * A call-completion signaling link established with * REGISTER does not have a struct chan_list record @@ -10078,9 +10206,11 @@ cb_events(enum event_e event, struct misdn_bchannel *bc, void *user_data) } AST_LIST_UNLOCK(&misdn_cc_records_db); #endif /* defined(AST_MISDN_ENHANCEMENTS) */ - } - release_chan(bc); + chan_misdn_log(1, bc->port, + " --> no Ch, so we've already released. (%s)\n", + manager_isdn_get_info(event)); + } break; case EVENT_BCHAN_ERROR: case EVENT_CLEANUP: @@ -10094,8 +10224,8 @@ cb_events(enum event_e event, struct misdn_bchannel *bc, void *user_data) break; } - hangup_chan(ch); - release_chan(bc); + hangup_chan(ch, bc); + release_chan(ch, bc); break; case EVENT_TONE_GENERATE: { @@ -10181,8 +10311,8 @@ cb_events(enum event_e event, struct misdn_bchannel *bc, void *user_data) chan_misdn_log(0, bc->port, "Write returned <=0 (err=%s) --> hanging up channel\n", strerror(errno)); stop_bc_tones(ch); - hangup_chan(ch); - release_chan(bc); + hangup_chan(ch, bc); + release_chan(ch, bc); } } else { chan_misdn_log(1, bc->port, "Write Pipe full!\n"); @@ -10237,67 +10367,56 @@ cb_events(enum event_e event, struct misdn_bchannel *bc, void *user_data) /** Supplementary Services **/ /****************************/ case EVENT_RETRIEVE: - { - struct ast_channel *hold_ast; - - if (!ch) { - chan_misdn_log(4, bc->port, " --> no CH, searching in holded\n"); - ch = find_holded_l3(cl_te, bc->l3_id, 1); - } - if (!ch) { - ast_log(LOG_WARNING, "Found no Holded channel, cannot Retrieve\n"); - misdn_lib_send_event(bc, EVENT_RETRIEVE_REJECT); - break; + chan_misdn_log(4, bc->port, " --> no CH, searching for held call\n"); + ch = find_hold_call_l3(cl_te, bc->l3_id); + if (!ch || ch->hold.state != MISDN_HOLD_ACTIVE) { + ast_log(LOG_WARNING, "No held call found, cannot Retrieve\n"); + misdn_lib_send_event(bc, EVENT_RETRIEVE_REJECT); + break; + } } - /*remember the channel again*/ + /* remember the channel again */ ch->bc = bc; - ch->state = MISDN_CONNECTED; - - ch->hold_info.port = 0; - ch->hold_info.channel = 0; - hold_ast = ast_bridged_channel(ch->ast); + ch->hold.state = MISDN_HOLD_IDLE; + ch->hold.port = 0; + ch->hold.channel = 0; - if (hold_ast) { - ast_moh_stop(hold_ast); - } + ast_queue_control(ch->ast, AST_CONTROL_UNHOLD); if (misdn_lib_send_event(bc, EVENT_RETRIEVE_ACKNOWLEDGE) < 0) { chan_misdn_log(4, bc->port, " --> RETRIEVE_ACK failed\n"); misdn_lib_send_event(bc, EVENT_RETRIEVE_REJECT); } break; - } case EVENT_HOLD: { int hold_allowed; - struct ast_channel *bridged = ast_bridged_channel(ch->ast); + struct ast_channel *bridged; misdn_cfg_get(bc->port, MISDN_CFG_HOLD_ALLOWED, &hold_allowed, sizeof(hold_allowed)); - if (!hold_allowed) { chan_misdn_log(-1, bc->port, "Hold not allowed this port.\n"); misdn_lib_send_event(bc, EVENT_HOLD_REJECT); break; } + bridged = ast_bridged_channel(ch->ast); if (bridged) { chan_misdn_log(2, bc->port, "Bridge Partner is of type: %s\n", bridged->tech->type); - ch->state = MISDN_HOLDED; ch->l3id = bc->l3_id; - misdn_lib_send_event(bc, EVENT_HOLD_ACKNOWLEDGE); + /* forget the channel now */ + ch->bc = NULL; + ch->hold.state = MISDN_HOLD_ACTIVE; + ch->hold.port = bc->port; + ch->hold.channel = bc->channel; - /* XXX This should queue an AST_CONTROL_HOLD frame on this channel - * instead of starting moh on the bridged channel directly */ - ast_moh_start(bridged, NULL, NULL); + ast_queue_control(ch->ast, AST_CONTROL_HOLD); - /*forget the channel now*/ - ch->bc = NULL; - ch->hold_info.port = bc->port; - ch->hold_info.channel = bc->channel; + misdn_lib_send_event(bc, EVENT_HOLD_ACKNOWLEDGE); } else { misdn_lib_send_event(bc, EVENT_HOLD_REJECT); chan_misdn_log(0, bc->port, "We aren't bridged to anybody\n"); @@ -10379,7 +10498,7 @@ cb_events(enum event_e event, struct misdn_bchannel *bc, void *user_data) case EVENT_RESTART: if (!bc->dummy) { stop_bc_tones(ch); - release_chan(bc); + release_chan(ch, bc); } break; default: diff --git a/channels/misdn/isdn_lib.c b/channels/misdn/isdn_lib.c index 10590d64f..8cd2fd10f 100644 --- a/channels/misdn/isdn_lib.c +++ b/channels/misdn/isdn_lib.c @@ -240,8 +240,6 @@ struct misdn_bchannel *find_bc_by_l3id(struct misdn_stack *stack, unsigned long struct misdn_bchannel *find_bc_by_confid(unsigned long confid); -struct misdn_bchannel *stack_holder_find_bychan(struct misdn_stack *stack, int chan); - int setup_bc(struct misdn_bchannel *bc); int manager_isdn_handler(iframe_t *frm ,msg_t *msg); @@ -3208,13 +3206,6 @@ void te_lib_destroy(int midev) cb_log(4, 0, "midev closed\n"); } - - -void misdn_lib_transfer(struct misdn_bchannel* holded_bc) -{ - holded_bc->holded=0; -} - struct misdn_bchannel *manager_find_bc_by_pid(int pid) { struct misdn_stack *stack; @@ -3594,7 +3585,7 @@ int misdn_lib_send_event(struct misdn_bchannel *bc, enum event_e event ) { msg_t *msg; struct misdn_bchannel *bc2; - struct misdn_bchannel *holded_bc; + struct misdn_bchannel *held_bc; struct misdn_stack *stack; int retval = 0; int channel; @@ -3705,20 +3696,22 @@ int misdn_lib_send_event(struct misdn_bchannel *bc, enum event_e event ) break; case EVENT_HOLD_ACKNOWLEDGE: - holded_bc = malloc(sizeof(struct misdn_bchannel)); - if (!holded_bc) { - cb_log(0,bc->port, "Could not allocate holded_bc!!!\n"); + held_bc = malloc(sizeof(struct misdn_bchannel)); + if (!held_bc) { + cb_log(0, bc->port, "Could not allocate held_bc!!!\n"); RETURN(-1,OUT); } - /*backup the bc*/ - memcpy(holded_bc,bc,sizeof(struct misdn_bchannel)); - holded_bc->holded=1; - bc_state_change(holded_bc,BCHAN_CLEANED); - - stack_holder_add(stack,holded_bc); + /* backup the bc and put it in storage */ + *held_bc = *bc; + held_bc->holded = 1; + held_bc->channel = 0;/* A held call does not have a channel anymore. */ + held_bc->channel_preselected = 0; + held_bc->channel_found = 0; + bc_state_change(held_bc, BCHAN_CLEANED); + stack_holder_add(stack, held_bc); - /*kill the bridge and clean the bchannel*/ + /* kill the bridge and clean the real b-channel record */ if (stack->nt) { if (bc->bc_state == BCHAN_BRIDGED) { misdn_split_conf(bc,bc->conf_id); @@ -4681,28 +4674,6 @@ void stack_holder_remove(struct misdn_stack *stack, struct misdn_bchannel *holde } } -struct misdn_bchannel *stack_holder_find_bychan(struct misdn_stack *stack, int chan) -{ - struct misdn_bchannel *help; - - cb_log(4,stack?stack->port:0, "*HOLDER: find_bychan %c\n", chan); - - if (!stack) return NULL; - - for (help=stack->holding; - help; - help=help->next) { - if (help->channel == chan) { - cb_log(4,stack->port, "*HOLDER: found_bychan bc\n"); - return help; - } - } - - cb_log(4,stack->port, "*HOLDER: find_bychan nothing\n"); - return NULL; - -} - struct misdn_bchannel *stack_holder_find(struct misdn_stack *stack, unsigned long l3id) { struct misdn_bchannel *help; @@ -4724,7 +4695,29 @@ struct misdn_bchannel *stack_holder_find(struct misdn_stack *stack, unsigned lon return NULL; } +/*! + * \brief Find a held call's B channel record. + * + * \param port Port the call is on. + * \param l3_id mISDN Layer 3 ID of held call. + * + * \return Found bc-record or NULL. + */ +struct misdn_bchannel *misdn_lib_find_held_bc(int port, int l3_id) +{ + struct misdn_bchannel *bc; + struct misdn_stack *stack; + + bc = NULL; + for (stack = get_misdn_stack(); stack; stack = stack->next) { + if (stack->port == port) { + bc = stack_holder_find(stack, l3_id); + break; + } + } + return bc; +} void misdn_lib_send_tone(struct misdn_bchannel *bc, enum tone_e tone) { diff --git a/channels/misdn/isdn_lib.h b/channels/misdn/isdn_lib.h index 934be9830..89565327e 100644 --- a/channels/misdn/isdn_lib.h +++ b/channels/misdn/isdn_lib.h @@ -753,8 +753,6 @@ void misdn_lib_log_ies(struct misdn_bchannel *bc); char *manager_isdn_get_info(enum event_e event); -void misdn_lib_transfer(struct misdn_bchannel* holded_bc); - struct misdn_bchannel* misdn_lib_get_free_bc(int port, int channel, int inout, int dec); #if defined(AST_MISDN_ENHANCEMENTS) struct misdn_bchannel *misdn_lib_get_register_bc(int port); @@ -793,6 +791,7 @@ int misdn_lib_get_port_up (int port) ; int misdn_lib_maxports_get(void) ; +struct misdn_bchannel *misdn_lib_find_held_bc(int port, int l3_id); void misdn_lib_release(struct misdn_bchannel *bc); int misdn_cap_is_speech(int cap); -- cgit v1.2.3