]> git.ipfire.org Git - thirdparty/asterisk.git/commitdiff
Merged revisions 206489 via svnmerge from
authorRichard Mudgett <rmudgett@digium.com>
Tue, 14 Jul 2009 18:32:20 +0000 (18:32 +0000)
committerRichard Mudgett <rmudgett@digium.com>
Tue, 14 Jul 2009 18:32:20 +0000 (18:32 +0000)
https://origsvn.digium.com/svn/asterisk/trunk

................
  r206489 | rmudgett | 2009-07-14 12:01:48 -0500 (Tue, 14 Jul 2009) | 35 lines

  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/branches/1.6.1@206558 65c4cc65-6c06-0410-ace0-fbb531ad65f3

channels/chan_misdn.c
channels/misdn/isdn_lib.c
channels/misdn/isdn_lib.h

index 9e95a2c83332a23f7353a8d0929bcbeba17273ae..4c87a2f93ec50d820f18df7459eb13ab9117046b 100644 (file)
@@ -126,30 +126,32 @@ enum misdn_chan_state {
        MISDN_ALERTING, /*!<  when Alerting */
        MISDN_BUSY, /*!<  when BUSY */
        MISDN_CONNECTED, /*!<  when connected */
-       MISDN_PRECONNECTED, /*!<  when connected */
        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, /*!< if this chan is holded */
-       MISDN_HOLD_DISCONNECT, /*!< if this chan is holded */
-  
 };
 
 #define ORG_AST 1
 #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 
-        * because the B channel is no longer associated. 
+        * \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;
@@ -313,13 +315,13 @@ struct chan_list {
        struct misdn_bchannel *bc;
 
        /*!
-        * \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. 
+       /*!
+        * \brief From associated B channel: Layer 3 process ID
+        * \note Used to find the HELD channel call record when retrieving a call.
         */
        unsigned int l3id;
 
@@ -482,7 +484,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, 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
@@ -528,12 +529,13 @@ 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);
 
 static int misdn_check_l2l1(struct ast_channel *chan, void *data);
 static int misdn_set_opt_exec(struct ast_channel *chan, void *data);
@@ -1294,15 +1296,8 @@ static 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 */
 };
 
 static const char *misdn_get_ch_state(struct chan_list *p) 
@@ -1461,8 +1456,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"
                                                " --> dad:%s oad:%s\n"
                                                " --> hold_port: %d\n"
@@ -1470,8 +1465,8 @@ static char *handle_cli_misdn_show_channels(struct ast_cli_entry *e, int cmd, st
                                                help->l3id,
                                                ast->exten,
                                                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);
@@ -2631,13 +2626,19 @@ static int misdn_indication(struct ast_channel *ast, int cond, const void *data,
                return -1;
        }
        
-       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->bc) {
+               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:
@@ -2738,6 +2739,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 ? p->bc->pid : -1);
+               return -1;
        }
   
        return 0;
@@ -2746,173 +2748,168 @@ 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);
-
-       if (!ast || ! (p=MISDN_ASTERISK_TECH_PVT(ast) ) ) return -1;
+       struct misdn_bchannel *bc;
+       const char *var;
 
-       if (!p) {
-               chan_misdn_log(3, 0, "misdn_hangup called, without chan_list obj.\n");
-               return 0 ;
+       if (!ast || !(p = MISDN_ASTERISK_TECH_PVT(ast))) {
+               return -1;
        }
+       MISDN_ASTERISK_TECH_PVT(ast) = NULL;
 
-       bc = p->bc;
+       ast_debug(1, "misdn_hangup(%s)\n", ast->name);
 
-       if (bc) {
-               const char *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;
                }
        }
 
-       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);
-               
-               if (bc)
+               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;
-               
-       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;
        }
-    
-       chan_misdn_log(1, bc->port, "* IND : HANGUP\tpid:%d ctx:%s dad:%s oad:%s State:%s\n", p->bc ? p->bc->pid : -1, ast->context, ast->exten, ast->cid.cid_num, misdn_get_ch_state(p));
+
+       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);
+       }
+
+       chan_misdn_log(1, bc->port,
+               "* IND : HANGUP\tpid:%d ctx:%s dad:%s oad:%s State:%s\n",
+               bc->pid,
+               ast->context,
+               ast->exten,
+               ast->cid.cid_num,
+               misdn_get_ch_state(p));
        chan_misdn_log(3, bc->port, " --> l3id:%x\n", p->l3id);
        chan_misdn_log(3, bc->port, " --> cause:%d\n", bc->cause);
        chan_misdn_log(2, bc->port, " --> out_cause:%d\n", bc->out_cause);
-       chan_misdn_log(2, bc->port, " --> state:%s\n", misdn_get_ch_state(p));
 
        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);
-               misdn_lib_send_event( bc, EVENT_RELEASE_COMPLETE);
-               break;
-       case MISDN_HOLDED:
+               release_chan(p, bc);
+               misdn_lib_send_event(bc, EVENT_RELEASE_COMPLETE);
+               return 0;
        case MISDN_DIALING:
-               start_bc_tones(p);
-               hanguptone_indicate(p);
-               
-               p->state=MISDN_CLEANING;
-               if (bc->need_disconnect)
-                       misdn_lib_send_event( bc, EVENT_DISCONNECT);
+               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 (bc->need_disconnect)
-                       misdn_lib_send_event( bc, EVENT_DISCONNECT);
+               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);
+               }
                break;
-      
+
        case MISDN_CALLING:
        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);
+               }
+
+               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 = 8;
+                       bc->progress_indicator = INFO_PI_INBAND_AVAILABLE;
+               }
+               if (bc->need_disconnect) {
+                       misdn_lib_send_event(bc, EVENT_DISCONNECT);
                }
-               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; */
+               if (bc->need_release) {
+                       misdn_lib_send_event(bc, EVENT_RELEASE);
+               }
                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)
+                       if (bc->need_release) {
                                misdn_lib_send_event(bc, EVENT_RELEASE);
-                       p->state = MISDN_CLEANING; 
+                       }
                } else {
-                       if (bc->need_disconnect)
+                       if (bc->need_disconnect) {
                                misdn_lib_send_event(bc, EVENT_DISCONNECT);
+                       }
                }
+               break;
        }
 
        p->state = MISDN_CLEANING;
-    
-       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;
 }
@@ -3004,7 +3001,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;
        }
@@ -3088,8 +3085,8 @@ static int misdn_write(struct ast_channel *ast, struct ast_frame *frame)
        
        if (!ast || ! (ch = MISDN_ASTERISK_TECH_PVT(ast)) ) 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;
        }
        
@@ -3320,10 +3317,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)
@@ -3742,39 +3738,72 @@ 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;
 
        if (bc->pri) return NULL;
 
-       chan_misdn_log(6, bc->port, "$$$ find_holded: channel:%d oad:%s dad:%s\n", bc->channel, bc->oad, bc->dad);
+       chan_misdn_log(6, bc->port, "$$$ find_hold_call: channel:%d oad:%s dad:%s\n", bc->channel, bc->oad, bc->dad);
        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_chan: No channel found for oad:%s dad:%s\n", bc->oad, bc->dad);
+       chan_misdn_log(6, bc->port, "$$$ find_hold_call: No channel found for oad:%s dad:%s\n", bc->oad, bc->dad);
 
        return NULL;
 }
 
 
-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;
        }
 
        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);
@@ -3838,23 +3867,26 @@ 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 = ch ? (ch->bc ? ch->bc->port : 0) : 0;
+       int port;
+
        if (!ch) {
                cb_log(1, 0, "Cannot hangup chan, no ch\n");
                return;
        }
 
+       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)
+               if (ch->ast) {
+                       send_cause2ast(ch->ast, bc, ch);
                        ast_hangup(ch->ast);
+               }
                return;
        }
 
@@ -3864,97 +3896,185 @@ 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) {
-       struct ast_channel *ast=NULL;
+/*!
+ * \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 ast_channel *ast;
+
+       ch->state = MISDN_CLEANING;
 
        ast_mutex_lock(&release_lock);
-       {
-               struct chan_list *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 (ch->ast) {
-                       ast = ch->ast;
-               } 
+       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 ) {
-                       misdn_jb_destroy(ch->jb);
-                       ch->jb = NULL;
-               } else {
-                       if (!bc->nojitter)
-                               chan_misdn_log(5, bc->port, "Jitterbuffer already destroyed.\n");
+       /* releasing jitterbuffer */
+       if (ch->jb) {
+               misdn_jb_destroy(ch->jb);
+               ch->jb = NULL;
+       } else {
+               if (!bc->nojitter) {
+                       chan_misdn_log(5, bc->port, "Jitterbuffer already destroyed.\n");
                }
+       }
 
-               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->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->originator == ORG_AST) {
-                       misdn_out_calls[bc->port]--;
-               } else {
-                       misdn_in_calls[bc->port]--;
+       if (ch->originator == ORG_AST) {
+               --misdn_out_calls[bc->port];
+       } else {
+               --misdn_in_calls[bc->port];
+       }
+
+       close(ch->pipe[0]);
+       close(ch->pipe[1]);
+
+       ast = ch->ast;
+       if (ast) {
+               MISDN_ASTERISK_TECH_PVT(ast) = NULL;
+               chan_misdn_log(1, bc->port,
+                       "* RELEASING CHANNEL pid:%d ctx:%s dad:%s oad:%s\n",
+                       bc->pid,
+                       ast->context,
+                       ast->exten,
+                       ast->cid.cid_num);
+
+               if (ast->_state != AST_STATE_RESERVED) {
+                       chan_misdn_log(3, bc->port, " --> Setting AST State to down\n");
+                       ast_setstate(ast, AST_STATE_DOWN);
                }
+       }
 
-               if (ch) {
-                       close(ch->pipe[0]);
-                       close(ch->pipe[1]);
+       ast_free(ch);
 
-                       if (ast && MISDN_ASTERISK_TECH_PVT(ast)) {
-                               chan_misdn_log(1, bc->port, "* RELEASING CHANNEL pid:%d ctx:%s dad:%s oad:%s state: %s\n", bc ? bc->pid : -1, ast->context, ast->exten, 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_mutex_unlock(&release_lock);
+}
 
-                               if (ast->_state != AST_STATE_RESERVED) {
-                                       chan_misdn_log(3, bc->port, " --> Setting AST State to down\n");
-                                       ast_setstate(ast, AST_STATE_DOWN);
-                               }
-                       }
+/*!
+ * \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;
-                       cl_dequeue_chan(&cl_te, ch);
+       ch->state = MISDN_CLEANING;
 
-                       ast_free(ch);
+       ast_mutex_lock(&release_lock);
+
+       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 {
-                       /* chan is already cleaned, so exiting  */
+                       --misdn_in_calls[ch->hold.port];
                }
+       }
+
+       close(ch->pipe[0]);
+       close(ch->pipe[1]);
 
-               ast_mutex_unlock(&release_lock);
+       ast = ch->ast;
+       if (ast) {
+               MISDN_ASTERISK_TECH_PVT(ast) = NULL;
+               if (ast->_state != AST_STATE_RESERVED) {
+                       ast_setstate(ast, AST_STATE_DOWN);
+               }
        }
-/*** release end **/
+
+       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;
+
+       switch (active_ch->state) {
+       case MISDN_PROCEEDING:
+       case MISDN_PROGRESS:
+       case MISDN_ALERTING:
+       case MISDN_CONNECTED:
+               break;
+       default:
+               return -1;
+       }
 
-       tmp_ch->state = MISDN_HOLD_DISCONNECT;
+       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;
 
-       ast_moh_stop(ast_bridged_channel(holded_chan->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;
+       }
 
-       holded_chan->state=MISDN_CONNECTED;
-       /* misdn_lib_transfer(holded_chan->bc); */
-       ast_channel_masquerade(holded_chan->ast, ast_bridged_channel(tmp_ch->ast));
+       return retval;
 }
 
 
@@ -3990,7 +4110,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->oad) || pbx_start_chan(ch) < 0) {
                ast = NULL;
                bc->out_cause = AST_CAUSE_UNALLOCATED;
-               hangup_chan(ch);
+               hangup_chan(ch, bc);
                hanguptone_indicate(ch);
 
                if (bc->nt)
@@ -4165,7 +4285,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);
@@ -4189,6 +4309,7 @@ static void wait_for_digits(struct chan_list *ch, struct misdn_bchannel *bc, str
 static enum event_response_e
 cb_events(enum event_e event, struct misdn_bchannel *bc, void *user_data)
 {
+       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) { /*  Debug Only Non-Bchan */
@@ -4207,14 +4328,13 @@ 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:
                        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:
@@ -4272,7 +4392,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) {
@@ -4330,12 +4450,12 @@ 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 {
                                        struct ast_channel *chan = ch->ast;
                                        ch->state = MISDN_CALLING_ACKNOWLEDGE;
                                        ast_setstate(chan, AST_STATE_DOWN);
-                                       hangup_chan(ch);
+                                       hangup_chan(ch, bc);
                                        ch->ast = NULL;
                                        break;
                                }
@@ -4586,11 +4706,11 @@ 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;
                                ast_setstate(chan, AST_STATE_DOWN);
-                               hangup_chan(ch);
+                               hangup_chan(ch, bc);
                                ch->ast = NULL;
                                break;
                        }
@@ -4838,8 +4958,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);
-
                        chan_misdn_log(3, bc->port, " --> org:%d nt:%d, inbandavail:%d state:%d\n", ch->originator, bc->nt, misdn_inband_avail(bc), ch->state);
                        if (ch->originator == ORG_AST && !bc->nt && misdn_inband_avail(bc) && ch->state != MISDN_CONNECTED) {
                                /* If there's inband information available (e.g. a
@@ -4861,23 +4979,34 @@ 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 && 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)
@@ -4885,29 +5014,41 @@ cb_events(enum event_e event, struct misdn_bchannel *bc, void *user_data)
                break;
        
        case EVENT_RELEASE:
-               {
-                       bc->need_disconnect = 0;
-                       bc->need_release = 0;
-
-                       hangup_chan(ch);
-                       release_chan(bc);
+               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;
+                       }
                }
+
+               bc->need_disconnect = 0;
+               bc->need_release = 0;
+
+               hangup_chan(ch, bc);
+               release_chan(ch, bc);
                break;
        case EVENT_RELEASE_COMPLETE:
-       {
+               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));
+                               break;
+                       }
+               }
+
                bc->need_disconnect = 0;
                bc->need_release = 0;
                bc->need_release_complete = 0;
 
                stop_bc_tones(ch);
-               hangup_chan(ch);
-
-               if (ch)
-                       ch->state = MISDN_CLEANING;
-
-               release_chan(bc);
-       }
-       break;
+               hangup_chan(ch, bc);
+               release_chan(ch, bc);
+               break;
        case EVENT_BCHAN_ERROR:
        case EVENT_CLEANUP:
        {
@@ -4921,8 +5062,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;
 
@@ -5008,8 +5149,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");
@@ -5069,70 +5210,57 @@ 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;
+               ch->hold.state = MISDN_HOLD_IDLE;
+               ch->hold.port = 0;
+               ch->hold.channel = 0;
 
-               hold_ast = ast_bridged_channel(ch->ast);
-
-               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;
+               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);
-
-                       /* 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);
 
-                       /*forget the channel now*/
+                       /* forget the channel now */
                        ch->bc = NULL;
-                       ch->hold_info.port = bc->port;
-                       ch->hold_info.channel = bc->channel;
+                       ch->hold.state = MISDN_HOLD_ACTIVE;
+                       ch->hold.port = bc->port;
+                       ch->hold.channel = bc->channel;
 
+                       ast_queue_control(ch->ast, AST_CONTROL_HOLD);
+                       
+                       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");
@@ -5198,7 +5326,7 @@ cb_events(enum event_e event, struct misdn_bchannel *bc, void *user_data)
 
                if (!bc->dummy) {
                        stop_bc_tones(ch);
-                       release_chan(bc);
+                       release_chan(ch, bc);
                }
                break;
 
index de3923bb6aeac9f7828a7e79f2ad612103a3776b..9a89cc923b0f9280a26433c735c098fb091a0b9d 100644 (file)
@@ -222,8 +222,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);
@@ -3106,13 +3104,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;
@@ -3331,6 +3322,7 @@ int misdn_lib_send_event(struct misdn_bchannel *bc, enum event_e event )
        msg_t *msg; 
        int retval=0;
        struct misdn_stack *stack;
+       struct misdn_bchannel *held_bc;
   
        if (!bc) RETURN(-1,OUT_POST_UNLOCK);
        
@@ -3417,21 +3409,22 @@ int misdn_lib_send_event(struct misdn_bchannel *bc, enum event_e event )
                break;
 
        case EVENT_HOLD_ACKNOWLEDGE:
-       {
-               struct misdn_bchannel *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) {
                        int channel;
                        if (bc->bc_state == BCHAN_BRIDGED) {
@@ -3456,9 +3449,7 @@ int misdn_lib_send_event(struct misdn_bchannel *bc, enum event_e event )
 
                        bc->in_use=0;   
                }
-               
-       }
-       break;
+               break;
 
        /* finishing the channel eh ? */
        case EVENT_DISCONNECT:
@@ -4382,28 +4373,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;
@@ -4425,7 +4394,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) 
 {
index 3ea5385d0bdf8e8fcd43b59d93e68c1a10d1a0f7..24c1b442a9adbceddfb59224f54d50f89e6d3699 100644 (file)
@@ -405,8 +405,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);
 
 void manager_bchannel_activate(struct misdn_bchannel *bc);
@@ -442,6 +440,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);