Pass the hangup cause all the way to the calling app/channel.
authorMichiel van Baak <michiel@vanbaak.info>
Thu, 24 Apr 2008 22:16:48 +0000 (22:16 +0000)
committerMichiel van Baak <michiel@vanbaak.info>
Thu, 24 Apr 2008 22:16:48 +0000 (22:16 +0000)
(closes issue #11328)
Reported by: rain
Patches:
      20071207__pass_cause_in_hangup_control_frame.diff.txt uploaded by Corydon76 (license 14)
brought up-to-date to trunk by me

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

21 files changed:
apps/app_alarmreceiver.c
apps/app_dial.c
apps/app_disa.c
apps/app_externalivr.c
apps/app_followme.c
apps/app_queue.c
channels/chan_alsa.c
channels/chan_gtalk.c
channels/chan_h323.c
channels/chan_iax2.c
channels/chan_jingle.c
channels/chan_local.c
channels/chan_mgcp.c
channels/chan_misdn.c
channels/chan_oss.c
channels/chan_sip.c
channels/chan_skinny.c
channels/chan_unistim.c
channels/chan_zap.c
include/asterisk/channel.h
main/channel.c

index 45b11ac..5230681 100644 (file)
@@ -252,6 +252,9 @@ static int receive_dtmf_digits(struct ast_channel *chan, char *digit_string, int
 
                /* If they hung up, leave */
                if ((f->frametype == AST_FRAME_CONTROL) && (f->subclass == AST_CONTROL_HANGUP)) {
+                       if (f->seqno) {
+                               chan->hangupcause = f->seqno;
+                       }
                        ast_frfree(f);
                        res = -1;
                        break;
index 6d0c6a6..e7a8163 100644 (file)
@@ -788,8 +788,11 @@ static struct ast_channel *wait_for_answer(struct ast_channel *in,
                                *to = -1;
                                strcpy(pa->status, "CANCEL");
                                ast_cdr_noanswer(in->cdr);
-                               if (f)
+                               if (f) {
+                                       if (f->seqno)
+                                               in->hangupcause = f->seqno;
                                        ast_frfree(f);
+                               }
                                return NULL;
                        }
 
index 654cb6c..16c2675 100644 (file)
@@ -195,6 +195,8 @@ static int disa_exec(struct ast_channel *chan, void *data)
                }
 
                if ((f->frametype == AST_FRAME_CONTROL) && (f->subclass == AST_CONTROL_HANGUP)) {
+                       if (f->seqno)
+                               chan->hangupcause = f->seqno;
                        ast_frfree(f);
                        ast_clear_flag(chan, AST_FLAG_END_DTMF_ONLY);
                        return -1;
index 1012376..6d3e868 100644 (file)
@@ -560,6 +560,9 @@ static int eivr_comm(struct ast_channel *chan, struct ivr_localuser *u,
                        } else if ((f->frametype == AST_FRAME_CONTROL) && (f->subclass == AST_CONTROL_HANGUP)) {
                                ast_chan_log(LOG_NOTICE, chan, "Got AST_CONTROL_HANGUP\n");
                                send_eivr_event(eivr_events, 'H', NULL, chan);
+                               if (f->seqno) {
+                                       chan->hangupcause = f->seqno;
+                               }
                                ast_frfree(f);
                                res = -1;
                                break;
index eeb9f03..0dc9bc0 100644 (file)
@@ -607,6 +607,9 @@ static struct ast_channel *wait_for_winner(struct findme_user_listptr *findme_us
                                        switch(f->subclass) {
                                        case AST_CONTROL_HANGUP:
                                                ast_verb(3, "%s received a hangup frame.\n", winner->name);
+                                               if (f->seqno) {
+                                                       winner->hangupcause = f->seqno;
+                                               }
                                                if (dg == 0) {
                                                        ast_verb(3, "The calling channel hungup. Need to drop everyone else.\n");
                                                        clear_calling_tree(findme_user_list);
index c955e8f..06a5c0b 100644 (file)
@@ -2644,6 +2644,8 @@ static struct callattempt *wait_for_answer(struct queue_ent *qe, struct callatte
                                /* Got hung up */
                                *to = -1;
                                if (f) {
+                                       if (f->seqno)
+                                               in->hangupcause = f->seqno;
                                        ast_frfree(f);
                                }
                                return NULL;
index 3e9199f..1d26ef2 100644 (file)
@@ -766,7 +766,7 @@ static char *console_hangup(struct ast_cli_entry *e, int cmd, struct ast_cli_arg
                hookstate = 0;
                grab_owner();
                if (alsa.owner) {
-                       ast_queue_hangup(alsa.owner);
+                       ast_queue_hangup(alsa.owner, AST_CAUSE_NORMAL_CLEARING);
                        ast_channel_unlock(alsa.owner);
                }
        }
index 04ea608..ad70346 100644 (file)
@@ -620,7 +620,7 @@ static int gtalk_is_answered(struct gtalk *client, ikspak *pak)
                        ast_getformatname_multiple(s2, BUFSIZ, tmp->peercapability),
                        ast_getformatname_multiple(s3, BUFSIZ, tmp->jointcapability));
                /* close session if capabilities don't match */
-               ast_queue_hangup(tmp->owner);
+               ast_queue_hangup(tmp->owner, -1);
 
                return -1;
 
@@ -749,7 +749,7 @@ static int gtalk_hangup_farend(struct gtalk *client, ikspak *pak)
        if (tmp) {
                tmp->alreadygone = 1;
                if (tmp->owner)
-                       ast_queue_hangup(tmp->owner);
+                       ast_queue_hangup(tmp->owner, -1);
        } else
                ast_log(LOG_NOTICE, "Whoa, didn't find call!\n");
        gtalk_response(client, from, pak, NULL, NULL);
index a3f6ee6..dec9aa0 100644 (file)
@@ -342,7 +342,7 @@ static void __oh323_update_info(struct ast_channel *c, struct oh323_pvt *pvt)
                        ast_debug(1, "Process pending hangup for %s\n", c->name);
                c->_softhangup |= AST_SOFTHANGUP_DEV;
                c->hangupcause = pvt->hangupcause;
-               ast_queue_hangup(c);
+               ast_queue_hangup(c, pvt->hangupcause);
                pvt->needhangup = 0;
                pvt->newstate = pvt->newcontrol = pvt->newdigit = pvt->DTMFsched = -1;
        }
@@ -2379,7 +2379,7 @@ static void cleanup_connection(unsigned call_reference, const char *call_token)
        /* Send hangup */
        if (pvt->owner) {
                pvt->owner->_softhangup |= AST_SOFTHANGUP_DEV;
-               ast_queue_hangup(pvt->owner);
+               ast_queue_hangup(pvt->owner, -1);
                ast_channel_unlock(pvt->owner);
        }
        ast_mutex_unlock(&pvt->lock);
@@ -2404,7 +2404,7 @@ static void hangup_connection(unsigned int call_reference, const char *token, in
        if (pvt->owner && !ast_channel_trylock(pvt->owner)) {
                pvt->owner->_softhangup |= AST_SOFTHANGUP_DEV;
                pvt->owner->hangupcause = pvt->hangupcause = cause;
-               ast_queue_hangup(pvt->owner);
+               ast_queue_hangup(pvt->owner, cause);
                ast_channel_unlock(pvt->owner);
        }
        else {
index ab13d26..2d53205 100644 (file)
@@ -1662,7 +1662,7 @@ static int iax2_queue_hangup(int callno)
                                usleep(1);
                                ast_mutex_lock(&iaxsl[callno]);
                        } else {
-                               ast_queue_hangup(iaxs[callno]->owner);
+                               ast_queue_hangup(iaxs[callno]->owner, -1);
                                ast_channel_unlock(iaxs[callno]->owner);
                                break;
                        }
@@ -2146,7 +2146,7 @@ retry:
                        /* If there's an owner, prod it to give up */
                        /* It is ok to use ast_queue_hangup() here instead of iax2_queue_hangup()
                         * because we already hold the owner channel lock. */
-                       ast_queue_hangup(owner);
+                       ast_queue_hangup(owner, -1);
                }
 
                AST_LIST_LOCK(&frame_queue);
@@ -2220,10 +2220,8 @@ static void __attempt_transmit(const void *data)
                                                        ast_log(LOG_WARNING, "Max retries exceeded to host %s on %s (type = %d, subclass = %d, ts=%d, seqno=%d)\n", ast_inet_ntoa(iaxs[f->callno]->addr.sin_addr),iaxs[f->callno]->owner->name , f->af.frametype, f->af.subclass, f->ts, f->oseqno);
                                                iaxs[callno]->error = ETIMEDOUT;
                                                if (iaxs[callno]->owner) {
-                                                       struct ast_frame fr = { 0, };
+                                                       struct ast_frame fr = { AST_FRAME_CONTROL, AST_CONTROL_HANGUP, .seqno = AST_CAUSE_DESTINATION_OUT_OF_ORDER };
                                                        /* Hangup the fd */
-                                                       fr.frametype = AST_FRAME_CONTROL;
-                                                       fr.subclass = AST_CONTROL_HANGUP;
                                                        iax2_queue_frame(callno, &fr); /* XXX */
                                                        /* Remember, owner could disappear */
                                                        if (iaxs[callno] && iaxs[callno]->owner)
index 7285549..39aa46c 100644 (file)
@@ -573,7 +573,7 @@ static int jingle_hangup_farend(struct jingle *client, ikspak *pak)
        if (tmp) {
                tmp->alreadygone = 1;
                if (tmp->owner)
-                       ast_queue_hangup(tmp->owner);
+                       ast_queue_hangup(tmp->owner, -1);
        } else
                ast_log(LOG_NOTICE, "Whoa, didn't find call!\n");
        jingle_response(client, pak, NULL, NULL);
index 636dad7..176bc73 100644 (file)
@@ -534,7 +534,7 @@ static int local_hangup(struct ast_channel *ast)
 {
        struct local_pvt *p = ast->tech_pvt;
        int isoutbound;
-       struct ast_frame f = { AST_FRAME_CONTROL, AST_CONTROL_HANGUP };
+       struct ast_frame f = { AST_FRAME_CONTROL, AST_CONTROL_HANGUP, .seqno = ast->hangupcause };
        struct ast_channel *ochan = NULL;
        int glaredetect = 0, res = 0;
 
index 021999a..592301e 100644 (file)
@@ -610,7 +610,7 @@ static void mgcp_queue_hangup(struct mgcp_subchannel *sub)
        for(;;) {
                if (sub->owner) {
                        if (!ast_channel_trylock(sub->owner)) {
-                               ast_queue_hangup(sub->owner);
+                               ast_queue_hangup(sub->owner, -1);
                                ast_channel_unlock(sub->owner);
                                break;
                        } else {
index ea005d5..cdbe3b4 100644 (file)
@@ -2320,13 +2320,13 @@ static int misdn_answer(struct ast_channel *ast)
        
        if (!p) {
                ast_log(LOG_WARNING, " --> Channel not connected ??\n");
-               ast_queue_hangup(ast);
+               ast_queue_hangup(ast, AST_CAUSE_NETWORK_OUT_OF_ORDER);
        }
 
        if (!p->bc) {
                chan_misdn_log(1, 0, " --> Got Answer, but theres no bc obj ??\n");
 
-               ast_queue_hangup(ast);
+               ast_queue_hangup(ast, AST_CAUSE_PROTOCOL_ERROR);
        }
 
        tmp = pbx_builtin_getvar_helper(p->ast, "CRYPT_KEY");
@@ -3665,7 +3665,7 @@ static void hangup_chan(struct chan_list *ch)
                send_cause2ast(ch->ast, ch->bc, ch);
 
                if (ch->ast)
-                       ast_queue_hangup(ch->ast);
+                       ast_queue_hangup(ch->ast, ch->bc->cause);
                cb_log(2, port, " --> queue_hangup\n");
        } else {
                cb_log(1, port, "Cannot hangup chan, no ast\n");
index 9d1c456..6edbbb8 100644 (file)
@@ -1040,7 +1040,7 @@ static char *console_hangup(struct ast_cli_entry *e, int cmd, struct ast_cli_arg
        }
        o->hookstate = 0;
        if (o->owner)
-               ast_queue_hangup(o->owner);
+               ast_queue_hangup(o->owner, AST_CAUSE_NORMAL_CLEARING);
        setformat(o, O_CLOSE);
        return CLI_SUCCESS;
 }
index 02d2e96..7a27168 100644 (file)
@@ -2970,7 +2970,7 @@ static int retrans_pkt(const void *data)
                if (pkt->owner->owner) {
                        sip_alreadygone(pkt->owner);
                        ast_log(LOG_WARNING, "Hanging up call %s - no reply to our critical packet.\n", pkt->owner->callid);
-                       ast_queue_hangup(pkt->owner->owner);
+                       ast_queue_hangup(pkt->owner->owner, AST_CAUSE_PROTOCOL_ERROR);
                        ast_channel_unlock(pkt->owner->owner);
                } else {
                        /* If no channel owner, destroy now */
@@ -3112,7 +3112,7 @@ static int __sip_autodestruct(const void *data)
 
        if (p->owner) {
                ast_log(LOG_WARNING, "Autodestruct on dialog '%s' with owner in place (Method: %s)\n", p->callid, sip_methods[p->method].text);
-               ast_queue_hangup(p->owner);
+               ast_queue_hangup(p->owner, AST_CAUSE_PROTOCOL_ERROR);
        } else if (p->refer) {
                ast_debug(3, "Finally hanging up channel after transfer: %s\n", p->callid);
                transmit_request_with_auth(p, SIP_BYE, 0, XMIT_RELIABLE, 1);
@@ -15695,7 +15695,7 @@ static void handle_response_invite(struct sip_pvt *p, int resp, char *rest, stru
                */
                xmitres = transmit_request(p, SIP_ACK, seqno, XMIT_UNRELIABLE, FALSE);
                if (p->owner && !req->ignore) {
-                       ast_queue_hangup(p->owner);
+                       ast_queue_hangup(p->owner, AST_CAUSE_NORMAL_CLEARING);
                        append_history(p, "Hangup", "Got 487 on CANCEL request from us. Queued AST hangup request");
                } else if (!req->ignore) {
                        update_call_counter(p, DEC_CALL_LIMIT);
@@ -15776,7 +15776,7 @@ static void handle_response_notify(struct sip_pvt *p, int resp, char *rest, stru
                if (p->owner) {
                        if (!p->refer) {
                                ast_log(LOG_WARNING, "Notify answer on an owned channel? - %s\n", p->owner->name);
-                               ast_queue_hangup(p->owner);
+                               ast_queue_hangup(p->owner, AST_CAUSE_NORMAL_UNSPECIFIED);
                        } else {
                                ast_debug(4, "Got OK on REFER Notify message\n");
                        }
@@ -16338,7 +16338,7 @@ static void handle_response(struct sip_pvt *p, int resp, char *rest, struct sip_
                                default:
                                        /* Send hangup */       
                                        if (owner && sipmethod != SIP_MESSAGE && sipmethod != SIP_INFO && sipmethod != SIP_BYE)
-                                               ast_queue_hangup(p->owner);
+                                               ast_queue_hangup(p->owner, AST_CAUSE_PROTOCOL_ERROR);
                                        break;
                                }
                                /* ACK on invite */
@@ -16392,7 +16392,7 @@ static void handle_response(struct sip_pvt *p, int resp, char *rest, struct sip_
                                                ast_debug(1, "Got 200 OK on NOTIFY for transfer\n");
                                        } else
                                                ast_log(LOG_WARNING, "Notify answer on an owned channel?\n");
-                                       /* ast_queue_hangup(p->owner); Disabled */
+                                       /* ast_queue_hangup(p->owner, -1); Disabled */
                                } else {
                                        if (!p->subscribed && !p->refer)
                                                p->needdestroy = 1;
@@ -18242,7 +18242,7 @@ static int handle_request_cancel(struct sip_pvt *p, struct sip_request *req)
 
        stop_media_flows(p); /* Immediately stop RTP, VRTP and UDPTL as applicable */
        if (p->owner)
-               ast_queue_hangup(p->owner);
+               ast_queue_hangup(p->owner, -1);
        else
                sip_scheddestroy(p, DEFAULT_TRANS_TIMEOUT);
        if (p->initreq.len > 0) {
@@ -18401,15 +18401,15 @@ static int handle_request_bye(struct sip_pvt *p, struct sip_request *req)
                                        ast_queue_control(c, AST_CONTROL_UNHOLD);
                                        ast_async_goto(bridged_to, p->context, p->refer->refer_to, 1);
                                } else
-                                       ast_queue_hangup(p->owner);
+                                       ast_queue_hangup(p->owner, -1);
                        }
                } else {
                        ast_log(LOG_WARNING, "Invalid transfer information from '%s'\n", ast_inet_ntoa(p->recv.sin_addr));
                        if (p->owner)
-                               ast_queue_hangup(p->owner);
+                               ast_queue_hangup(p->owner, AST_CAUSE_PROTOCOL_ERROR);
                }
        } else if (p->owner) {
-               ast_queue_hangup(p->owner);
+               ast_queue_hangup(p->owner, -1);
                ast_debug(3, "Received bye, issuing owner hangup\n");
        } else {
                sip_scheddestroy(p, DEFAULT_TRANS_TIMEOUT);
index c4458ba..b609527 100644 (file)
@@ -4513,7 +4513,7 @@ static int handle_onhook_message(struct skinny_req *req, struct skinnysession *s
                if ((res = attempt_transfer(p)) < 0) {
                        if (sub->next && sub->next->owner) {
                                sub->next->alreadygone = 1;
-                               ast_queue_hangup(sub->next->owner,1);
+                               ast_queue_hangup(sub->next->owner, -1);
                        }
                } else if (res) {
                        ast_log(LOG_WARNING, "Transfer attempt failed\n");
@@ -4525,7 +4525,7 @@ static int handle_onhook_message(struct skinny_req *req, struct skinnysession *s
                /* If there is another active call, skinny_hangup will ring the phone with the other call */
                if (sub->owner) {
                        sub->alreadygone = 1;
-                       ast_queue_hangup(sub->owner);
+                       ast_queue_hangup(sub->owner, -1);
                } else {
                        ast_log(LOG_WARNING, "Skinny(%s@%s-%d) channel already destroyed\n",
                                l->name, d->name, sub->callid);
@@ -5222,7 +5222,7 @@ static int handle_soft_key_event_message(struct skinny_req *req, struct skinnyse
                                if ((res = attempt_transfer(p)) < 0) {
                                        if (sub->next && sub->next->owner) {
                                                sub->next->alreadygone = 1;
-                                               ast_queue_hangup(sub->next->owner, 1);
+                                               ast_queue_hangup(sub->next->owner, -1);
                                        }
                                } else if (res) {
                                        ast_log(LOG_WARNING, "Transfer attempt failed\n");
@@ -5234,7 +5234,7 @@ static int handle_soft_key_event_message(struct skinny_req *req, struct skinnyse
                                /* If there is another active call, skinny_hangup will ring the phone with the other call */
                                if (sub->owner) {
                                        sub->alreadygone = 1;
-                                       ast_queue_hangup(sub->owner);
+                                       ast_queue_hangup(sub->owner, -1);
                                } else {
                                        ast_log(LOG_WARNING, "Skinny(%s@%s-%d) channel already destroyed\n",
                                                l->name, d->name, sub->callid);
index 7318e73..dca01ad 100644 (file)
@@ -1125,7 +1125,7 @@ static void close_client(struct unistimsession *s)
                                if (sub->owner) {       /* Call in progress ? */
                                        if (unistimdebug)
                                                ast_verb(0, "Aborting call\n");
-                                       ast_queue_hangup(sub->owner);
+                                       ast_queue_hangup(sub->owner, AST_CAUSE_NETWORK_OUT_OF_ORDER);
                                }
                        } else
                                ast_log(LOG_WARNING, "Freeing a client with no subchannel !\n");
@@ -1974,11 +1974,11 @@ static void close_call(struct unistimsession *pte)
                        if (attempt_transfer(sub, l->subs[SUB_THREEWAY]) < 0)
                                ast_verb(0, "attempt_transfer failed.\n");
                } else
-                       ast_queue_hangup(sub->owner);
+                       ast_queue_hangup(sub->owner, -1);
        } else {
                if (l->subs[SUB_THREEWAY]) {
                        if (l->subs[SUB_THREEWAY]->owner)
-                               ast_queue_hangup(l->subs[SUB_THREEWAY]->owner);
+                               ast_queue_hangup(l->subs[SUB_THREEWAY]->owner, AST_CAUSE_NORMAL_CLEARING);
                        else
                                ast_log(LOG_WARNING, "threeway sub without owner\n");
                } else
@@ -2312,7 +2312,7 @@ static void TransferCallStep1(struct unistimsession *pte)
                if (unistimdebug)
                        ast_verb(0, "Transfer canceled, hangup our threeway channel\n");
                if (p->subs[SUB_THREEWAY]->owner)
-                       ast_queue_hangup(p->subs[SUB_THREEWAY]->owner);
+                       ast_queue_hangup(p->subs[SUB_THREEWAY]->owner, AST_CAUSE_NORMAL_CLEARING);
                else
                        ast_log(LOG_WARNING, "Canceling a threeway channel without owner\n");
                return;
@@ -2368,7 +2368,7 @@ static void HandleCallOutgoing(struct unistimsession *s)
                        /* start switch */
                        if (ast_pthread_create(&t, NULL, unistim_ss, c)) {
                                display_last_error("Unable to create switch thread");
-                               ast_queue_hangup(c);
+                               ast_queue_hangup(c, AST_CAUSE_SWITCH_CONGESTION);
                        }
                } else
                        ast_log(LOG_WARNING, "Unable to create channel for %s@%s\n",
index 927275d..9063bb8 100644 (file)
@@ -4522,7 +4522,7 @@ static struct ast_frame *zt_handle_event(struct ast_channel *ast)
                                                        /* It hasn't been long enough since the last flashook.  This is probably a bounce on 
                                                           hanging up.  Hangup both channels now */
                                                        if (p->subs[SUB_THREEWAY].owner)
-                                                               ast_queue_hangup(p->subs[SUB_THREEWAY].owner);
+                                                               ast_queue_hangup(p->subs[SUB_THREEWAY].owner, AST_CAUSE_NO_ANSWER);
                                                        p->subs[SUB_THREEWAY].owner->_softhangup |= AST_SOFTHANGUP_DEV;
                                                        ast_debug(1, "Looks like a bounced flash, hanging up both calls on %d\n", p->channel);
                                                        ast_channel_unlock(p->subs[SUB_THREEWAY].owner);
@@ -10096,7 +10096,7 @@ static int pri_hangup_all(struct zt_pvt *p, struct zt_pri *pri)
                                ast_mutex_lock(&p->lock);
                        }
                        if (p->subs[x].owner) {
-                               ast_queue_hangup(p->subs[x].owner);
+                               ast_queue_hangup(p->subs[x].owner, AST_CAUSE_PRE_EMPTED);
                                ast_channel_unlock(p->subs[x].owner);
                        }
                }
index e0b1f79..fcd1aa8 100644 (file)
@@ -731,7 +731,7 @@ int ast_queue_frame(struct ast_channel *chan, struct ast_frame *f);
  *
  * \note The channel does not need to be locked before calling this function.
  */
-int ast_queue_hangup(struct ast_channel *chan);
+int ast_queue_hangup(struct ast_channel *chan, int cause);
 
 /*!
  * \brief Queue a control frame with payload
index fbb14d8..351d641 100644 (file)
@@ -1000,12 +1000,19 @@ int ast_queue_frame(struct ast_channel *chan, struct ast_frame *fin)
 }
 
 /*! \brief Queue a hangup frame for channel */
-int ast_queue_hangup(struct ast_channel *chan)
+int ast_queue_hangup(struct ast_channel *chan, int cause)
 {
        struct ast_frame f = { AST_FRAME_CONTROL, AST_CONTROL_HANGUP };
+
+       if (cause >= 0)
+               f.seqno = cause;
+
        /* Yeah, let's not change a lock-critical value without locking */
        if (!ast_channel_trylock(chan)) {
                chan->_softhangup |= AST_SOFTHANGUP_DEV;
+               if (cause < 0)
+                       f.seqno = chan->hangupcause;
+
                ast_channel_unlock(chan);
        }
        return ast_queue_frame(chan, &f);
@@ -2307,7 +2314,7 @@ static struct ast_frame *__ast_read(struct ast_channel *chan, int dropaudio)
        struct ast_frame *f = NULL;     /* the return value */
        int blah;
        int prestate;
-       int count = 0;
+       int count = 0, cause = 0;
 
        /* this function is very long so make sure there is only one return
         * point at the end (there are only two exceptions to this).
@@ -2418,6 +2425,7 @@ static struct ast_frame *__ast_read(struct ast_channel *chan, int dropaudio)
                /* Interpret hangup and return NULL */
                /* XXX why not the same for frames from the channel ? */
                if (f->frametype == AST_FRAME_CONTROL && f->subclass == AST_CONTROL_HANGUP) {
+                       cause = f->seqno;
                        ast_frfree(f);
                        f = NULL;
                }
@@ -2687,6 +2695,8 @@ static struct ast_frame *__ast_read(struct ast_channel *chan, int dropaudio)
        } else {
                /* Make sure we always return NULL in the future */
                chan->_softhangup |= AST_SOFTHANGUP_DEV;
+               if (cause)
+                       chan->hangupcause = cause;
                if (chan->generator)
                        ast_deactivate_generator(chan);
                /* End the CDR if appropriate */