Add setsubstate_onhook.
authorDamien Wedhorn <voip@facts.com.au>
Thu, 12 May 2011 07:25:52 +0000 (07:25 +0000)
committerDamien Wedhorn <voip@facts.com.au>
Thu, 12 May 2011 07:25:52 +0000 (07:25 +0000)
Add the setsubstate_onhook to complete the initial substate handling
procedures. Added dumpsub(sub, forcehangup) which is the common way of
calling setsubstate_onhook. Dumpsub attempts to activate another sub
after setting the current one onhook.

git-svn-id: https://origsvn.digium.com/svn/asterisk/trunk@318600 65c4cc65-6c06-0410-ace0-fbb531ad65f3

channels/chan_skinny.c

index 96e21a1..77d5099 100644 (file)
@@ -1211,7 +1211,6 @@ struct skinny_subchannel {
        int cxmode;
        int nat;
        int calldirection;
-       int alreadygone;
        int blindxfer;
        int xferor;
        int substate;
@@ -1417,6 +1416,7 @@ static void setsubstate_busy(struct skinny_subchannel *sub);
 static void setsubstate_congestion(struct skinny_subchannel *sub);
 static void setsubstate_progress(struct skinny_subchannel *sub);
 static void setsubstate_hold(struct skinny_subchannel *sub);
+static void dumpsub(struct skinny_subchannel *sub, int forcehangup);
 
 static struct ast_channel_tech skinny_tech = {
        .type = "Skinny",
@@ -4084,88 +4084,20 @@ static int skinny_call(struct ast_channel *ast, char *dest, int timeout)
 static int skinny_hangup(struct ast_channel *ast)
 {
        struct skinny_subchannel *sub = ast->tech_pvt;
-       struct skinny_line *l;
-       struct skinny_device *d;
 
        if (!sub) {
                ast_debug(1, "Asked to hangup channel not connected\n");
                return 0;
        }
-
-       l = sub->line;
-       d = l->device;
+       
+       dumpsub(sub, 1);
 
        if (skinnydebug)
-               ast_verb(3,"Hanging up %s/%d\n",d->name,sub->callid);
-
-       AST_LIST_REMOVE(&l->sub, sub, list);
-
-       if (d->registered) {
-               /* Ignoring l->type, doesn't seem relevant and previous code 
-                  assigned rather than tested, ie always true */
-               if (!AST_LIST_EMPTY(&l->sub)) {
-                       if (sub->related) {
-                               sub->related->related = NULL;
+               ast_verb(3,"Destroying subchannel %d\n", sub->callid);
 
-                       }
-                       if (sub == l->activesub) {      /* we are killing the active sub, but there are other subs on the line*/
-                               ast_verb(4,"Killing active sub %d\n", sub->callid);
-                               if (sub->related) {
-                                       l->activesub = sub->related;
-                               } else {
-                                       if (AST_LIST_NEXT(sub, list)) {
-                                               l->activesub = AST_LIST_NEXT(sub, list);
-                                       } else {
-                                               l->activesub = AST_LIST_FIRST(&l->sub);
-                                       }
-                               }
-                               if (l->activesub) {
-                                       transmit_selectsoftkeys(d, 0, 0, KEYDEF_ONHOLD);
-                               }
-                               transmit_closereceivechannel(d, sub);
-                               transmit_stopmediatransmission(d, sub);
-                               transmit_lamp_indication(d, STIMULUS_LINE, l->instance, SKINNY_LAMP_BLINK);
-                               transmit_stop_tone(d, l->instance, sub->callid);
-                               transmit_callstate(d, l->instance, sub->callid, SKINNY_ONHOOK);
-                               transmit_activatecallplane(d, l);
-                       } else {    /* we are killing a background sub on the line with other subs*/
-                               ast_verb(4,"Killing inactive sub %d\n", sub->callid);
-                               if (AST_LIST_NEXT(sub, list)) {
-                                       transmit_lamp_indication(d, STIMULUS_LINE, l->instance, SKINNY_LAMP_BLINK);
-                               } else {
-                                       transmit_lamp_indication(d, STIMULUS_LINE, l->instance, SKINNY_LAMP_ON);
-                               }
-                               transmit_callstate(d, l->instance, sub->callid, SKINNY_ONHOOK);
-                               transmit_activatecallplane(d, l);
-                       }
-               } else {                                                /* no more subs on line so make idle */
-                       ast_verb(4,"Killing only sub %d\n", sub->callid);
-                       d->hookstate = SKINNY_ONHOOK;
-                       transmit_closereceivechannel(d, sub);
-                       transmit_stopmediatransmission(d, sub);
-                       transmit_speaker_mode(d, SKINNY_SPEAKEROFF);
-                       transmit_clearpromptmessage(d, l->instance, sub->callid);
-                       transmit_callstate(d, l->instance, sub->callid, SKINNY_ONHOOK);
-                       transmit_selectsoftkeys(d, 0, 0, KEYDEF_ONHOOK);
-                       transmit_activatecallplane(d, l);
-                       l->activesub = NULL;
-                       transmit_lamp_indication(d, STIMULUS_LINE, l->instance, SKINNY_LAMP_OFF);
-                       if (sub->line == d->activeline) {
-                               transmit_activatecallplane(d, l);
-                               transmit_closereceivechannel(d, sub);
-                               transmit_stopmediatransmission(d, sub);
-                               transmit_speaker_mode(d, SKINNY_SPEAKEROFF);
-                               transmit_ringer_mode(d, SKINNY_RING_OFF);
-                               transmit_clear_display_message(d, l->instance, sub->callid);
-                               transmit_stop_tone(d, l->instance, sub->callid);
-                               /* we should check to see if we can start the ringer if another line is ringing */
-                       }
-               }
-       }
        ast_mutex_lock(&sub->lock);
        sub->owner = NULL;
        ast->tech_pvt = NULL;
-       sub->alreadygone = 0;
        if (sub->rtp) {
                ast_rtp_instance_destroy(sub->rtp);
                sub->rtp = NULL;
@@ -4720,6 +4652,45 @@ static void setsubstate_offhook(struct skinny_subchannel *sub)
        }
 }
 
+static void setsubstate_onhook(struct skinny_subchannel *sub)
+{
+       struct skinny_line *l = sub->line;
+       struct skinny_device *d = l->device;
+
+       AST_LIST_REMOVE(&l->sub, sub, list);
+       if (sub->related) {
+               sub->related->related = NULL;
+       }
+       
+       if (sub == l->activesub) {
+               l->activesub = NULL;
+               transmit_closereceivechannel(d, sub);
+               transmit_stopmediatransmission(d, sub);
+               transmit_stop_tone(d, l->instance, sub->callid);
+               transmit_callstate(d, l->instance, sub->callid, SKINNY_ONHOOK);
+               transmit_clearpromptmessage(d, l->instance, sub->callid);
+               transmit_ringer_mode(d, SKINNY_RING_OFF);
+               d->hookstate = SKINNY_ONHOOK;
+               transmit_speaker_mode(d, SKINNY_SPEAKEROFF); 
+               transmit_definetimedate(d); 
+               transmit_lamp_indication(d, STIMULUS_LINE, l->instance, SKINNY_LAMP_OFF);
+       } else {
+               transmit_stop_tone(d, l->instance, sub->callid);
+               transmit_callstate(d, l->instance, sub->callid, SKINNY_ONHOOK);
+               transmit_clearpromptmessage(d, l->instance, sub->callid);
+       }
+
+       sub->cxmode = SKINNY_CX_RECVONLY;       
+       sub->substate = SUBSTATE_ONHOOK;
+       if (sub->rtp) {
+               ast_rtp_instance_destroy(sub->rtp);
+               sub->rtp = NULL;
+       }
+       if (sub->owner) {
+               ast_queue_hangup(sub->owner);
+       }
+}
+
 static void setsubstate_dialing(struct skinny_subchannel *sub, char exten[AST_MAX_EXTENSION])
 {
        struct skinny_line *l = sub->line;
@@ -4914,11 +4885,16 @@ static void setsubstate_hold(struct skinny_subchannel *sub)
        struct skinny_line *l = sub->line;
        struct skinny_device *d = l->device;
 
-       if (sub->substate != SUBSTATE_CONNECTED) {
+       if (!(sub->substate == SUBSTATE_CONNECTED || sub->substate == SUBSTATE_HOLD)) {
                ast_log(LOG_WARNING, "Cannot set substate to SUBSTATE_HOLD from %s (on call-%d)\n", substate2str(sub->substate), sub->callid);
                return;
        }
        
+       if (sub->substate == SUBSTATE_HOLD) {
+               transmit_callstate(d, l->instance, sub->callid, SKINNY_HOLD);
+               return;
+       }
+
        ast_queue_control_data(sub->owner, AST_CONTROL_HOLD,
                S_OR(l->mohsuggest, NULL),
                !ast_strlen_zero(l->mohsuggest) ? strlen(l->mohsuggest) + 1 : 0);
@@ -4933,6 +4909,57 @@ static void setsubstate_hold(struct skinny_subchannel *sub)
        sub->substate = SUBSTATE_HOLD;
 }
 
+static void dumpsub(struct skinny_subchannel *sub, int forcehangup)
+{
+       struct skinny_line *l = sub->line;
+       struct skinny_subchannel *activatesub = NULL;
+       struct skinny_subchannel *tsub;
+
+       if (skinnydebug) {
+               ast_verb(3, "Dumping sub %d - state = %s\n", sub->callid, substate2str(sub->substate));
+       }
+       
+       if (!forcehangup && sub->substate == SUBSTATE_HOLD) {
+               l->activesub = NULL;
+               return;
+       }
+       
+       if (sub == l->activesub) {
+               if (sub->related) {
+                       activatesub = sub->related;
+                       setsubstate_onhook(sub);
+                       l->activesub = activatesub;
+                       if (l->activesub->substate != SUBSTATE_HOLD) {
+                               ast_log(LOG_WARNING, "Sub-%d was related but not at SUBSTATE_HOLD\n", sub->callid);
+                               return;
+                       }
+                       setsubstate_hold(l->activesub);
+               } else {
+                       setsubstate_onhook(sub);
+                       AST_LIST_TRAVERSE(&l->sub, tsub, list) {
+                               if (tsub->substate == SUBSTATE_CALLWAIT) {
+                                       activatesub = tsub;
+                               }
+                       }
+                       if (activatesub) {
+                               setsubstate_ringin(activatesub);
+                               return;
+                       }
+                       AST_LIST_TRAVERSE(&l->sub, tsub, list) {
+                               if (tsub->substate == SUBSTATE_HOLD) {
+                                       activatesub = tsub;
+                               }
+                       }
+                       if (activatesub) {
+                               setsubstate_hold(activatesub);
+                               return;
+                       }
+               }
+       } else {
+               setsubstate_onhook(sub);
+       }
+}
+
 static int handle_hold_button(struct skinny_subchannel *sub)
 {
        if (!sub)
@@ -5453,7 +5480,6 @@ static int handle_onhook_message(struct skinny_req *req, struct skinnysession *s
        struct skinny_subchannel *sub;
        int instance;
        int reference;
-       int onlysub = 0;
 
        instance = letohl(req->data.onhook.instance);
        reference = letohl(req->data.onhook.reference);
@@ -5479,62 +5505,22 @@ static int handle_onhook_message(struct skinny_req *req, struct skinnysession *s
                return 0;
        }
 
-       ast_devstate_changed(AST_DEVICE_NOT_INUSE, "Skinny/%s@%s", l->name, d->name);
-
-       if (sub->substate == SUBSTATE_HOLD) {
-               return 0;
-       }
-
-       if (!AST_LIST_NEXT(sub, list)) {
-               onlysub = 1;
-       } else {
-               AST_LIST_REMOVE(&l->sub, sub, list);
-       }
-
-       sub->cxmode = SKINNY_CX_RECVONLY;
-       if (onlysub || sub->xferor){  /* is this the only call to this device? */
-               d->hookstate = SKINNY_ONHOOK;
-               if (skinnydebug)
-                       ast_debug(1, "Skinny %s@%s-%d went on hook\n", l->name, d->name, reference);
-       }
-
-       if (d->hookstate == SKINNY_ONHOOK) {
-               transmit_closereceivechannel(d, sub);
-               transmit_stopmediatransmission(d, sub);
-               transmit_speaker_mode(d, SKINNY_SPEAKEROFF);
-               transmit_clearpromptmessage(d, instance, sub->callid);
-               transmit_callstate(d, l->instance, sub->callid, SKINNY_ONHOOK);
-               transmit_selectsoftkeys(d, 0, 0, KEYDEF_ONHOOK);
-               transmit_activatecallplane(d, l);
-       } else if (d->hookstate == SKINNY_OFFHOOK) {
-               transmit_callstate(d, l->instance, sub->callid, SKINNY_OFFHOOK);
-               transmit_activatecallplane(d, l);
-       } else {
-               transmit_callstate(d, l->instance, sub->callid, d->hookstate); /* wedhorn: this seems wrong!!! */
-       }
-
        if (l->transfer && sub->xferor && sub->owner->_state >= AST_STATE_RING) {
                /* We're allowed to transfer, we have two active calls and
                   we made at least one of the calls.  Let's try and transfer */
                handle_transfer_button(sub);
-       } else {
-               /* Hangup the current call */
-               /* If there is another active call, skinny_hangup will ring the phone with the other call */
-               if (sub->xferor && sub->related){
-                       sub->related->related = NULL;
-                       sub->related->blindxfer = 0;
-               }
-
-               if (sub->owner) {
-                       sub->alreadygone = 1;
-                       ast_queue_hangup(sub->owner);
-               } else {
-                       ast_log(LOG_WARNING, "Skinny(%s@%s-%d) channel already destroyed\n",
-                               l->name, d->name, sub->callid);
-               }
-               /* Not ideal, but let's send updated time at onhook and offhook, as it clears the display */
-               transmit_definetimedate(d);
+               return 0;
        }
+       
+       ast_devstate_changed(AST_DEVICE_NOT_INUSE, "Skinny/%s@%s", l->name, d->name);
+       
+       dumpsub(sub, 0);
+
+       d->hookstate = SKINNY_ONHOOK;
+       
+       /* Not ideal, but let's send updated time at onhook and offhook, as it clears the display */
+       transmit_definetimedate(d);
+
        return 1;
 }
 
@@ -5998,66 +5984,27 @@ static int handle_soft_key_event_message(struct skinny_req *req, struct skinnyse
 
                if (d->hookstate == SKINNY_ONHOOK) {
                        /* Something else already put us back on hook */
-                       break;
+                       /* Not ideal, but let's send updated time anyway, as it clears the display */
+                       transmit_definetimedate(d);
+                       return 0;
                }
-               if (sub) {
-                       int onlysub = 0;
 
-                       if (!AST_LIST_NEXT(sub, list)) {
-                               onlysub = 1;
-                       } else {
-                               AST_LIST_REMOVE(&l->sub, sub, list);
-                       }
-
-                       sub->cxmode = SKINNY_CX_RECVONLY;
-                       if (onlysub || sub->xferor){    /*Are there other calls to this device */
-                               d->hookstate = SKINNY_ONHOOK;
-                               if (skinnydebug)
-                                       ast_debug(1, "Skinny %s@%s-%d went on hook\n", l->name, d->name, callreference);
-                       }
-
-                       if (d->hookstate == SKINNY_ONHOOK) {
-                               transmit_closereceivechannel(d, sub);
-                               transmit_stopmediatransmission(d, sub);
-                               transmit_speaker_mode(d, SKINNY_SPEAKEROFF);
-                               transmit_clearpromptmessage(d, instance, sub->callid);
-                               transmit_callstate(d, l->instance, sub->callid, SKINNY_ONHOOK);
-                               transmit_selectsoftkeys(d, 0, 0, KEYDEF_ONHOOK);
-                               transmit_activatecallplane(d, l);
-                       } else if (d->hookstate == SKINNY_OFFHOOK) {
-                               transmit_callstate(d, l->instance, sub->callid, SKINNY_OFFHOOK);
-                               transmit_activatecallplane(d, l);
-                       } else {
-                               transmit_callstate(d, l->instance, sub->callid, d->hookstate); /* wedhorn - this seems wrong */
-                       }
+               if (l->transfer && sub->xferor && sub->owner->_state >= AST_STATE_RING) {
+                       /* We're allowed to transfer, we have two active calls and
+                           we made at least one of the calls.  Let's try and transfer */
+                       handle_transfer_button(sub);
+                       return 0;
+               }
+       
+               ast_devstate_changed(AST_DEVICE_NOT_INUSE, "Skinny/%s@%s", l->name, d->name);
+       
+               dumpsub(sub, 1);
 
-                       ast_devstate_changed(AST_DEVICE_NOT_INUSE, "Skinny/%s@%s", l->name, d->name);
-                       if (skinnydebug)
-                               ast_verb(1, "Skinny %s@%s went on hook\n", l->name, d->name);
-                       if (l->transfer && sub->xferor && sub->owner->_state >= AST_STATE_RING) {
-                               /* We're allowed to transfer, we have two active calls and
-                                  we made at least one of the calls.  Let's try and transfer */
-                               handle_transfer_button(sub);
-                       } else {
-                               /* Hangup the current call */
-                               /* If there is another active call, skinny_hangup will ring the phone with the other call */
-                               if (sub->xferor && sub->related){
-                                       sub->related->related = NULL;
-                                       sub->related->blindxfer = 0;
-                               }
+               d->hookstate = SKINNY_ONHOOK;
+       
+               /* Not ideal, but let's send updated time at onhook and offhook, as it clears the display */
+               transmit_definetimedate(d);
 
-                               if (sub->owner) {
-                                       sub->alreadygone = 1;
-                                       ast_queue_hangup(sub->owner);
-                               } else {
-                                       ast_log(LOG_WARNING, "Skinny(%s@%s-%d) channel already destroyed\n",
-                                               l->name, d->name, sub->callid);
-                               }
-                       }
-                       if ((d->hookstate == SKINNY_ONHOOK) && (AST_LIST_NEXT(sub, list) && !AST_LIST_NEXT(sub, list)->rtp)) {
-                               ast_devstate_changed(AST_DEVICE_NOT_INUSE, "Skinny/%s@%s", l->name, d->name);
-                       }
-               }
                break;
        case SOFTKEY_RESUME:
                if (skinnydebug)
@@ -7531,7 +7478,6 @@ static int unload_module(void)
                        AST_LIST_TRAVERSE(&l->sub, sub, list) {
                                ast_mutex_lock(&sub->lock);
                                if (sub->owner) {
-                                       sub->alreadygone = 1;
                                        ast_softhangup(sub->owner, AST_SOFTHANGUP_APPUNLOAD);
                                }
                                ast_mutex_unlock(&sub->lock);