diff --git a/CHANGES b/CHANGES index 88de8ba58e38caf799bbd34b67f70fd9856b0545..2600c05eba95d55594663863a6a3f0a8aef9b5cb 100644 --- a/CHANGES +++ b/CHANGES @@ -11,6 +11,7 @@ --- Functionality changes from Asterisk 11 to Asterisk 12 -------------------- ------------------------------------------------------------------------------ + AMI (Asterisk Manager Interface) ------------------ * The SIPshowpeer action will now include a 'SubscribeContext' field for a peer @@ -31,6 +32,16 @@ AMI (Asterisk Manager Interface) 'Manager Show Command' now displays the privileges needed for using a given manager command instead. +Channel Drivers +------------------ + +chan_mobile +------------------ + * Added general support for busy detection. + + * Added ECAM command support for Sony Ericsson phones. + + Features ------------------- * The BRIDGE_FEATURES channel variable would previously only set features for diff --git a/addons/chan_mobile.c b/addons/chan_mobile.c index 95c7f27d9406f5ef46e11e6dfa10e5f9df9bd9da..96c525856971a0a7f6d7e40fbbe3bd830268d1bf 100644 --- a/addons/chan_mobile.c +++ b/addons/chan_mobile.c @@ -147,6 +147,7 @@ struct mbl_pvt { int ring_sched_id; struct ast_dsp *dsp; struct ast_sched_context *sched; + int hangupcause; /* flags */ unsigned int outgoing:1; /*!< outgoing call */ @@ -172,6 +173,9 @@ static int handle_response_ring(struct mbl_pvt *pvt, char *buf); static int handle_response_cmti(struct mbl_pvt *pvt, char *buf); static int handle_response_cmgr(struct mbl_pvt *pvt, char *buf); static int handle_response_cusd(struct mbl_pvt *pvt, char *buf); +static int handle_response_busy(struct mbl_pvt *pvt); +static int handle_response_no_dialtone(struct mbl_pvt *pvt, char *buf); +static int handle_response_no_carrier(struct mbl_pvt *pvt, char *buf); static int handle_sms_prompt(struct mbl_pvt *pvt, char *buf); /* CLI stuff */ @@ -341,6 +345,7 @@ struct hfp_pvt { struct hfp_cind cind_map; /*!< the cind name to index mapping for this AG */ int rsock; /*!< our rfcomm socket */ int rport; /*!< our rfcomm port */ + int sent_alerting; /*!< have we sent alerting? */ }; @@ -435,6 +440,10 @@ typedef enum { AT_CMER, AT_CIND_TEST, AT_CUSD, + AT_BUSY, + AT_NO_DIALTONE, + AT_NO_CARRIER, + AT_ECAM, } at_message_t; static int at_match_prefix(char *buf, char *prefix); @@ -981,6 +990,7 @@ static int mbl_call(struct ast_channel *ast, const char *dest, int timeout) ast_log(LOG_ERROR, "error sending ATD command on %s\n", pvt->id); return -1; } + pvt->hangupcause = 0; pvt->needchup = 1; msg_queue_push(pvt, AT_OK, AT_D); } else { @@ -1314,6 +1324,9 @@ static int mbl_queue_hangup(struct mbl_pvt *pvt) if (ast_channel_trylock(pvt->owner)) { DEADLOCK_AVOIDANCE(&pvt->lock); } else { + if (pvt->hangupcause != 0) { + ast_channel_hangupcause_set(pvt->owner, pvt->hangupcause); + } ast_queue_hangup(pvt->owner); ast_channel_unlock(pvt->owner); break; @@ -2030,6 +2043,14 @@ static at_message_t at_read_full(int rsock, char *buf, size_t count) return AT_VGS; } else if (at_match_prefix(buf, "+CUSD:")) { return AT_CUSD; + } else if (at_match_prefix(buf, "BUSY")) { + return AT_BUSY; + } else if (at_match_prefix(buf, "NO DIALTONE")) { + return AT_NO_DIALTONE; + } else if (at_match_prefix(buf, "NO CARRIER")) { + return AT_NO_CARRIER; + } else if (at_match_prefix(buf, "*ECAV:")) { + return AT_ECAM; } else { return AT_UNKNOWN; } @@ -2074,6 +2095,12 @@ static inline const char *at_msg2str(at_message_t msg) return "SMS PROMPT"; case AT_CMS_ERROR: return "+CMS ERROR"; + case AT_BUSY: + return "BUSY"; + case AT_NO_DIALTONE: + return "NO DIALTONE"; + case AT_NO_CARRIER: + return "NO CARRIER"; /* at commands */ case AT_A: return "ATA"; @@ -2101,6 +2128,8 @@ static inline const char *at_msg2str(at_message_t msg) return "AT+CIND=?"; case AT_CUSD: return "AT+CUSD"; + case AT_ECAM: + return "AT*ECAM"; } } @@ -2109,6 +2138,40 @@ static inline const char *at_msg2str(at_message_t msg) * bluetooth handsfree profile helpers */ + /*! + * \brief Parse a ECAV event. + * \param hfp an hfp_pvt struct + * \param buf the buffer to parse (null terminated) + * \return -1 on error (parse error) or a ECAM value on success + * + * Example string: *ECAV: <ccid>,<ccstatus>,<calltype>[,<processid>] + * [,exitcause][,<number>,<type>] + * + * Example indicating busy: *ECAV: 1,7,1 + */ +static int hfp_parse_ecav(struct hfp_pvt *hfp, char *buf) +{ + int ccid = 0; + int ccstatus = 0; + int calltype = 0; + + if (!sscanf(buf, "*ECAV: %2d,%2d,%2d", &ccid, &ccstatus, &calltype)) { + ast_debug(1, "[%s] error parsing ECAV event '%s'\n", hfp->owner->id, buf); + return -1; + } + + return ccstatus; +} + +/*! + * \brief Enable Sony Erricson extensions / indications. + * \param hfp an hfp_pvt struct + */ +static int hfp_send_ecam(struct hfp_pvt *hfp) +{ + return rfcomm_write(hfp->rsock, "AT*ECAM=1\r"); +} + /*! * \brief Parse a CIEV event. * \param hfp an hfp_pvt struct @@ -3214,6 +3277,14 @@ static int handle_response_ok(struct mbl_pvt *pvt, char *buf) break; case AT_CLIP: ast_debug(1, "[%s] caling line indication enabled\n", pvt->id); + if (hfp_send_ecam(pvt->hfp) || msg_queue_push(pvt, AT_OK, AT_ECAM)) { + ast_debug(1, "[%s] error enabling Sony Ericsson call monitoring extensions\n", pvt->id); + goto e_return; + } + + break; + case AT_ECAM: + ast_debug(1, "[%s] Sony Ericsson call monitoring is active on device\n", pvt->id); if (hfp_send_vgs(pvt->hfp, 15) || msg_queue_push(pvt, AT_OK, AT_VGS)) { ast_debug(1, "[%s] error synchronizing gain settings\n", pvt->id); goto e_return; @@ -3344,6 +3415,21 @@ static int handle_response_error(struct mbl_pvt *pvt, char *buf) pvt->has_sms = 0; ast_debug(1, "[%s] error setting CNMI\n", pvt->id); ast_debug(1, "[%s] no SMS support\n", pvt->id); + break; + case AT_ECAM: + ast_debug(1, "[%s] Mobile does not support Sony Ericsson extensions\n", pvt->id); + + /* this is not a fatal error, let's continue with the initialization */ + + if (hfp_send_vgs(pvt->hfp, 15) || msg_queue_push(pvt, AT_OK, AT_VGS)) { + ast_debug(1, "[%s] error synchronizing gain settings\n", pvt->id); + goto e_return; + } + + pvt->timeout = -1; + pvt->hfp->initialized = 1; + ast_verb(3, "Bluetooth Device %s initialized and ready.\n", pvt->id); + break; /* end initialization stuff */ @@ -3440,6 +3526,9 @@ static int handle_response_ciev(struct mbl_pvt *pvt, char *buf) case HFP_CIND_CALLSETUP_NONE: if (pvt->hfp->cind_state[pvt->hfp->cind_map.call] != HFP_CIND_CALL_ACTIVE) { if (pvt->owner) { + if (pvt->hfp->sent_alerting == 1) { + handle_response_busy(pvt); + } if (mbl_queue_hangup(pvt)) { ast_log(LOG_ERROR, "[%s] error queueing hangup, disconnectiong...\n", pvt->id); return -1; @@ -3458,6 +3547,7 @@ static int handle_response_ciev(struct mbl_pvt *pvt, char *buf) break; case HFP_CIND_CALLSETUP_OUTGOING: if (pvt->outgoing) { + pvt->hfp->sent_alerting = 0; ast_debug(1, "[%s] outgoing call\n", pvt->id); } else { ast_verb(3, "[%s] user dialed from handset, disconnecting\n", pvt->id); @@ -3468,6 +3558,7 @@ static int handle_response_ciev(struct mbl_pvt *pvt, char *buf) if (pvt->outgoing) { ast_debug(1, "[%s] remote alerting\n", pvt->id); mbl_queue_control(pvt, AST_CONTROL_RINGING); + pvt->hfp->sent_alerting = 1; } break; } @@ -3662,6 +3753,50 @@ static int handle_response_cusd(struct mbl_pvt *pvt, char *buf) return 0; } +/*! + * \brief Handle BUSY messages. + * \param pvt a mbl_pvt structure + * \retval 0 success + * \retval -1 error + */ +static int handle_response_busy(struct mbl_pvt *pvt) +{ + pvt->hangupcause = AST_CAUSE_USER_BUSY; + pvt->needchup = 1; + mbl_queue_control(pvt, AST_CONTROL_BUSY); + return 0; +} + +/*! + * \brief Handle NO DIALTONE messages. + * \param pvt a mbl_pvt structure + * \param buf a null terminated buffer containing an AT message + * \retval 0 success + * \retval -1 error + */ +static int handle_response_no_dialtone(struct mbl_pvt *pvt, char *buf) +{ + ast_verb(1, "[%s] mobile reports NO DIALTONE\n", pvt->id); + pvt->needchup = 1; + mbl_queue_control(pvt, AST_CONTROL_CONGESTION); + return 0; +} + +/*! + * \brief Handle NO CARRIER messages. + * \param pvt a mbl_pvt structure + * \param buf a null terminated buffer containing an AT message + * \retval 0 success + * \retval -1 error + */ +static int handle_response_no_carrier(struct mbl_pvt *pvt, char *buf) +{ + ast_verb(1, "[%s] mobile reports NO CARRIER\n", pvt->id); + pvt->needchup = 1; + mbl_queue_control(pvt, AST_CONTROL_CONGESTION); + return 0; +} + static void *do_monitor_phone(void *data) { @@ -3820,6 +3955,40 @@ static void *do_monitor_phone(void *data) } ast_mutex_unlock(&pvt->lock); break; + case AT_BUSY: + ast_mutex_lock(&pvt->lock); + if (handle_response_busy(pvt)) { + ast_mutex_unlock(&pvt->lock); + goto e_cleanup; + } + ast_mutex_unlock(&pvt->lock); + break; + case AT_NO_DIALTONE: + ast_mutex_lock(&pvt->lock); + if (handle_response_no_dialtone(pvt, buf)) { + ast_mutex_unlock(&pvt->lock); + goto e_cleanup; + } + ast_mutex_unlock(&pvt->lock); + break; + case AT_NO_CARRIER: + ast_mutex_lock(&pvt->lock); + if (handle_response_no_carrier(pvt, buf)) { + ast_mutex_unlock(&pvt->lock); + goto e_cleanup; + } + ast_mutex_unlock(&pvt->lock); + break; + case AT_ECAM: + ast_mutex_lock(&pvt->lock); + if (hfp_parse_ecav(hfp, buf) == 7) { + if (handle_response_busy(pvt)) { + ast_mutex_unlock(&pvt->lock); + goto e_cleanup; + } + } + ast_mutex_unlock(&pvt->lock); + break; case AT_UNKNOWN: ast_debug(1, "[%s] ignoring unknown message: %s\n", pvt->id, buf); break;