chan_vpb: Fix a gcc 7 out-of-bounds complaint
[asterisk/asterisk.git] / addons / chan_mobile.c
index c9a9c84..8f04af7 100644 (file)
  * \ingroup channel_drivers
  */
 
+/*! \li \ref chan_mobile.c uses the configuration file \ref chan_mobile.conf
+ * \addtogroup configuration_file Configuration Files
+ */
+
+/*!
+ * \page chan_mobile.conf chan_mobile.conf
+ * \verbinclude chan_mobile.conf.sample
+ */
+
 /*** MODULEINFO
        <depend>bluetooth</depend>
        <defaultenabled>no</defaultenabled>
@@ -33,8 +42,6 @@
 
 #include "asterisk.h"
 
-ASTERISK_FILE_VERSION(__FILE__, "$Revision$")
-
 #include <pthread.h>
 #include <signal.h>
 
@@ -64,16 +71,16 @@ ASTERISK_FILE_VERSION(__FILE__, "$Revision$")
 #include "asterisk/app.h"
 #include "asterisk/manager.h"
 #include "asterisk/io.h"
+#include "asterisk/smoother.h"
+#include "asterisk/format_cache.h"
 
 #define MBL_CONFIG "chan_mobile.conf"
 #define MBL_CONFIG_OLD "mobile.conf"
 
 #define DEVICE_FRAME_SIZE 48
-#define DEVICE_FRAME_FORMAT AST_FORMAT_SLINEAR
+#define DEVICE_FRAME_FORMAT ast_format_slin
 #define CHANNEL_FRAME_SIZE 320
 
-static struct ast_format prefformat;
-
 static int discovery_interval = 60;                    /* The device discovery interval, default 60 seconds. */
 static pthread_t discovery_thread = AST_PTHREADT_NULL; /* The discovery thread */
 static sdp_session_t *sdp_session;
@@ -138,6 +145,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 */
@@ -163,6 +171,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 */
@@ -196,9 +207,9 @@ static char *mblsendsms_desc =
 "  Message - text of the message\n";
 
 static struct ast_channel *mbl_new(int state, struct mbl_pvt *pvt, char *cid_num,
-               const struct ast_channel *requestor);
+               const struct ast_assigned_ids *assignedids, const struct ast_channel *requestor);
 static struct ast_channel *mbl_request(const char *type, struct ast_format_cap *cap,
-               const struct ast_channel *requestor, const char *data, int *cause);
+               const struct ast_assigned_ids *assignedids, const struct ast_channel *requestor, const char *data, int *cause);
 static int mbl_call(struct ast_channel *ast, const char *dest, int timeout);
 static int mbl_hangup(struct ast_channel *ast);
 static int mbl_answer(struct ast_channel *ast);
@@ -332,6 +343,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? */
 };
 
 
@@ -426,6 +438,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);
@@ -557,7 +573,7 @@ static char *handle_cli_mobile_search(struct ast_cli_entry *e, int cmd, struct a
        max_rsp = 255;
        flags = IREQ_CACHE_FLUSH;
 
-       ii = alloca(max_rsp * sizeof(inquiry_info));
+       ii = ast_alloca(max_rsp * sizeof(inquiry_info));
        num_rsp = hci_inquiry(adapter->dev_id, len, max_rsp, NULL, &ii, flags);
        if (num_rsp > 0) {
                ast_cli(a->fd, FORMAT1, "Address", "Name", "Usable", "Type", "Port");
@@ -820,9 +836,8 @@ e_return:
 */
 
 static struct ast_channel *mbl_new(int state, struct mbl_pvt *pvt, char *cid_num,
-               const struct ast_channel *requestor)
+               const struct ast_assigned_ids *assignedids, const struct ast_channel *requestor)
 {
-
        struct ast_channel *chn;
 
        pvt->answered = 0;
@@ -837,18 +852,18 @@ static struct ast_channel *mbl_new(int state, struct mbl_pvt *pvt, char *cid_num
        ast_dsp_digitreset(pvt->dsp);
 
        chn = ast_channel_alloc(1, state, cid_num, pvt->id, 0, 0, pvt->context,
-                       requestor ? ast_channel_linkedid(requestor) : "", 0,
+                       assignedids, requestor, 0,
                        "Mobile/%s-%04lx", pvt->id, ast_random() & 0xffff);
        if (!chn) {
                goto e_return;
        }
 
        ast_channel_tech_set(chn, &mbl_tech);
-       ast_format_cap_add(ast_channel_nativeformats(chn), &prefformat);
-       ast_format_copy(ast_channel_rawreadformat(chn), &prefformat);
-       ast_format_copy(ast_channel_rawwriteformat(chn), &prefformat);
-       ast_format_copy(ast_channel_writeformat(chn), &prefformat);
-       ast_format_copy(ast_channel_readformat(chn), &prefformat);
+       ast_channel_nativeformats_set(chn, mbl_tech.capabilities);
+       ast_channel_set_rawreadformat(chn, DEVICE_FRAME_FORMAT);
+       ast_channel_set_rawwriteformat(chn, DEVICE_FRAME_FORMAT);
+       ast_channel_set_writeformat(chn, DEVICE_FRAME_FORMAT);
+       ast_channel_set_readformat(chn, DEVICE_FRAME_FORMAT);
        ast_channel_tech_pvt_set(chn, pvt);
 
        if (state == AST_STATE_RING)
@@ -860,6 +875,7 @@ static struct ast_channel *mbl_new(int state, struct mbl_pvt *pvt, char *cid_num
        if (pvt->sco_socket != -1) {
                ast_channel_set_fd(chn, 0, pvt->sco_socket);
        }
+       ast_channel_unlock(chn);
 
        return chn;
 
@@ -868,7 +884,7 @@ e_return:
 }
 
 static struct ast_channel *mbl_request(const char *type, struct ast_format_cap *cap,
-               const struct ast_channel *requestor, const char *data, int *cause)
+               const struct ast_assigned_ids *assignedids, const struct ast_channel *requestor, const char *data, int *cause)
 {
 
        struct ast_channel *chn = NULL;
@@ -883,9 +899,9 @@ static struct ast_channel *mbl_request(const char *type, struct ast_format_cap *
                return NULL;
        }
 
-       if (!(ast_format_cap_iscompatible(cap, &prefformat))) {
-               char tmp[256];
-               ast_log(LOG_WARNING, "Asked to get a channel of unsupported format '%s'\n", ast_getformatname_multiple(tmp, sizeof(tmp), cap));
+       if (ast_format_cap_iscompatible_format(cap, DEVICE_FRAME_FORMAT) == AST_FORMAT_CMP_NOT_EQUAL) {
+               struct ast_str *codec_buf = ast_str_alloca(AST_FORMAT_CAP_NAMES_LEN);
+               ast_log(LOG_WARNING, "Asked to get a channel of unsupported format '%s'\n", ast_format_cap_get_names(cap, &codec_buf));
                *cause = AST_CAUSE_FACILITY_NOT_IMPLEMENTED;
                return NULL;
        }
@@ -927,7 +943,7 @@ static struct ast_channel *mbl_request(const char *type, struct ast_format_cap *
        }
 
        ast_mutex_lock(&pvt->lock);
-       chn = mbl_new(AST_STATE_DOWN, pvt, NULL, requestor);
+       chn = mbl_new(AST_STATE_DOWN, pvt, NULL, assignedids, requestor);
        ast_mutex_unlock(&pvt->lock);
        if (!chn) {
                ast_log(LOG_WARNING, "Unable to allocate channel structure.\n");
@@ -972,6 +988,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 {
@@ -1096,7 +1113,7 @@ static struct ast_frame *mbl_read(struct ast_channel *ast)
 
        memset(&pvt->fr, 0x00, sizeof(struct ast_frame));
        pvt->fr.frametype = AST_FRAME_VOICE;
-       ast_format_set(&pvt->fr.subclass.format, DEVICE_FRAME_FORMAT, 0);
+       pvt->fr.subclass.format = DEVICE_FRAME_FORMAT;
        pvt->fr.src = "Mobile";
        pvt->fr.offset = AST_FRIENDLY_OFFSET;
        pvt->fr.mallocd = 0;
@@ -1305,6 +1322,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;
@@ -1317,9 +1337,7 @@ static int mbl_queue_hangup(struct mbl_pvt *pvt)
 
 static int mbl_ast_hangup(struct mbl_pvt *pvt)
 {
-       if (pvt->owner) {
-               ast_hangup(pvt->owner);
-       }
+       ast_hangup(pvt->owner);
        return 0;
 }
 
@@ -1367,7 +1385,7 @@ static int rfcomm_connect(bdaddr_t src, bdaddr_t dst, int remote_channel)
        memset(&addr, 0, sizeof(addr));
        addr.rc_family = AF_BLUETOOTH;
        bacpy(&addr.rc_bdaddr, &src);
-       addr.rc_channel = (uint8_t) 1;
+       addr.rc_channel = (uint8_t) 0;
        if (bind(s, (struct sockaddr *)&addr, sizeof(addr)) < 0) {
                ast_debug(1, "bind() failed (%d).\n", errno);
                close(s);
@@ -1524,8 +1542,8 @@ static int rfcomm_read_and_append_char(int rsock, char **buf, size_t count, size
 }
 
 /*!
- * \brief Read until '\r\n'.
- * This function consumes the '\r\n' but does not add it to buf.
+ * \brief Read until \verbatim '\r\n'. \endverbatim
+ * This function consumes the \verbatim'\r\n'\endverbatim but does not add it to buf.
  */
 static int rfcomm_read_until_crlf(int rsock, char **buf, size_t count, size_t *in_count)
 {
@@ -1552,7 +1570,7 @@ static int rfcomm_read_until_crlf(int rsock, char **buf, size_t count, size_t *i
 
 /*!
  * \brief Read the remainder of an AT SMS prompt.
- * \note the entire parsed string is '\r\n> '
+ * \note the entire parsed string is \verbatim '\r\n> ' \endverbatim
  *
  * By the time this function is executed, only a ' ' is left to read.
  */
@@ -1570,7 +1588,7 @@ e_return:
 }
 
 /*!
- * \brief Read until a \r\nOK\r\n message.
+ * \brief Read until a \verbatim \r\nOK\r\n \endverbatim message.
  */
 static int rfcomm_read_until_ok(int rsock, char **buf, size_t count, size_t *in_count)
 {
@@ -1667,7 +1685,7 @@ static int rfcomm_read_until_ok(int rsock, char **buf, size_t count, size_t *in_
 
 /*!
  * \brief Read the remainder of a +CMGR message.
- * \note the entire parsed string is '+CMGR: ...\r\n...\r\n...\r\n...\r\nOK\r\n'
+ * \note the entire parsed string is \verbatim '+CMGR: ...\r\n...\r\n...\r\n...\r\nOK\r\n' \endverbatim
  */
 static int rfcomm_read_cmgr(int rsock, char **buf, size_t count, size_t *in_count)
 {
@@ -1686,7 +1704,7 @@ static int rfcomm_read_cmgr(int rsock, char **buf, size_t count, size_t *in_coun
 
 /*!
  * \brief Read and AT result code.
- * \note the entire parsed string is '\r\n<result code>\r\n'
+ * \note the entire parsed string is \verbatim '\r\n<result code>\r\n' \endverbatim
  */
 static int rfcomm_read_result(int rsock, char **buf, size_t count, size_t *in_count)
 {
@@ -1724,7 +1742,7 @@ e_return:
 
 /*!
  * \brief Read the remainder of an AT command.
- * \note the entire parsed string is '<at command>\r'
+ * \note the entire parsed string is \verbatim '<at command>\r' \endverbatim
  */
 static int rfcomm_read_command(int rsock, char **buf, size_t count, size_t *in_count)
 {
@@ -1758,8 +1776,8 @@ static int rfcomm_read_command(int rsock, char **buf, size_t count, size_t *in_c
  * \endverbatim
  *
  * These formats correspond to AT result codes, AT commands, and the AT SMS
- * prompt respectively.  When messages are read the leading and trailing '\r'
- * and '\n' characters are discarded.  If the given buffer is not large enough
+ * prompt respectively.  When messages are read the leading and trailing \verbatim '\r' \endverbatim
+ * and \verbatim '\n' \endverbatim characters are discarded.  If the given buffer is not large enough
  * to hold the response, what does not fit in the buffer will be dropped.
  *
  * \note The rfcomm connection to the device is asynchronous, so there is no
@@ -2021,6 +2039,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;
        }
@@ -2065,6 +2091,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";
@@ -2092,6 +2124,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";
        }
 }
 
@@ -2100,6 +2134,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
@@ -2119,7 +2187,7 @@ static int hfp_parse_ciev(struct hfp_pvt *hfp, char *buf, int *value)
                return HFP_CIND_NONE;
        }
 
-       if (i >= sizeof(hfp->cind_state)) {
+       if (i >= ARRAY_LEN(hfp->cind_state)) {
                ast_debug(2, "[%s] CIEV event index too high (%s)\n", hfp->owner->id, buf);
                return HFP_CIND_NONE;
        }
@@ -2132,7 +2200,7 @@ static int hfp_parse_ciev(struct hfp_pvt *hfp, char *buf, int *value)
  * \brief Parse a CLIP event.
  * \param hfp an hfp_pvt struct
  * \param buf the buffer to parse (null terminated)
- * @note buf will be modified when the CID string is parsed
+ * \note buf will be modified when the CID string is parsed
  * \return NULL on error (parse error) or a pointer to the caller id
  * information in buf
  */
@@ -2178,7 +2246,7 @@ static char *hfp_parse_clip(struct hfp_pvt *hfp, char *buf)
  * \brief Parse a CMTI notification.
  * \param hfp an hfp_pvt struct
  * \param buf the buffer to parse (null terminated)
- * @note buf will be modified when the CMTI message is parsed
+ * \note buf will be modified when the CMTI message is parsed
  * \return -1 on error (parse error) or the index of the new sms message
  */
 static int hfp_parse_cmti(struct hfp_pvt *hfp, char *buf)
@@ -2203,7 +2271,7 @@ static int hfp_parse_cmti(struct hfp_pvt *hfp, char *buf)
  * \param from_number a pointer to a char pointer which will store the from
  * number
  * \param text a pointer to a char pointer which will store the message text
- * @note buf will be modified when the CMGR message is parsed
+ * \note buf will be modified when the CMGR message is parsed
  * \retval -1 parse error
  * \retval 0 success
  */
@@ -2229,6 +2297,7 @@ static int hfp_parse_cmgr(struct hfp_pvt *hfp, char *buf, char **from_number, ch
                        if (buf[i] == '"') {
                                state++;
                        }
+                       break;
                case 2: /* mark the start of the number */
                        if (from_number) {
                                *from_number = &buf[i];
@@ -2266,7 +2335,7 @@ static int hfp_parse_cmgr(struct hfp_pvt *hfp, char *buf, char **from_number, ch
  * \brief Parse a CUSD answer.
  * \param hfp an hfp_pvt struct
  * \param buf the buffer to parse (null terminated)
- * @note buf will be modified when the CUSD string is parsed
+ * \note buf will be modified when the CUSD string is parsed
  * \return NULL on error (parse error) or a pointer to the cusd message
  * information in buf
  */
@@ -2601,7 +2670,7 @@ static int hfp_parse_cind_indicator(struct hfp_pvt *hfp, int group, char *indica
        int value;
 
        /* store the current indicator */
-       if (group >= sizeof(hfp->cind_state)) {
+       if (group >= ARRAY_LEN(hfp->cind_state)) {
                ast_debug(1, "ignoring CIND state '%s' for group %d, we only support up to %d indicators\n", indicator, group, (int) sizeof(hfp->cind_state));
                return -1;
        }
@@ -3205,6 +3274,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;
@@ -3336,6 +3413,21 @@ static int handle_response_error(struct mbl_pvt *pvt, char *buf)
                        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 */
 
                case AT_A:
@@ -3431,6 +3523,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;
@@ -3449,6 +3544,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);
@@ -3459,6 +3555,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;
                }
@@ -3491,7 +3588,7 @@ static int handle_response_clip(struct mbl_pvt *pvt, char *buf)
                        ast_debug(1, "[%s] error parsing CLIP: %s\n", pvt->id, buf);
                }
 
-               if (!(chan = mbl_new(AST_STATE_RING, pvt, clip, NULL))) {
+               if (!(chan = mbl_new(AST_STATE_RING, pvt, clip, NULL, NULL))) {
                        ast_log(LOG_ERROR, "[%s] unable to allocate channel for incoming call\n", pvt->id);
                        hfp_send_chup(pvt->hfp);
                        msg_queue_push(pvt, AT_OK, AT_CHUP);
@@ -3581,7 +3678,7 @@ static int handle_response_cmgr(struct mbl_pvt *pvt, char *buf)
                pvt->incoming_sms = 0;
 
                /* XXX this channel probably does not need to be associated with this pvt */
-               if (!(chan = mbl_new(AST_STATE_DOWN, pvt, NULL, NULL))) {
+               if (!(chan = mbl_new(AST_STATE_DOWN, pvt, NULL, NULL, NULL))) {
                        ast_debug(1, "[%s] error creating sms message channel, disconnecting\n", pvt->id);
                        return -1;
                }
@@ -3653,12 +3750,56 @@ 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)
 {
        struct mbl_pvt *pvt = (struct mbl_pvt *)data;
        struct hfp_pvt *hfp = pvt->hfp;
-       char buf[256];
+       char buf[350];
        int t;
        at_message_t at_msg;
        struct msg_queue_entry *entry;
@@ -3712,10 +3853,7 @@ static void *do_monitor_phone(void *data)
                }
 
                if ((at_msg = at_read_full(hfp->rsock, buf, sizeof(buf))) < 0) {
-                       /* XXX gnu specific strerror_r is assummed here, this
-                        * is not really safe.  See the strerror(3) man page
-                        * for more info. */
-                       ast_debug(1, "[%s] error reading from device: %s (%d)\n", pvt->id, strerror_r(errno, buf, sizeof(buf)), errno);
+                       ast_debug(1, "[%s] error reading from device: %s (%d)\n", pvt->id, strerror(errno), errno);
                        break;
                }
 
@@ -3811,6 +3949,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;
@@ -3818,7 +3990,7 @@ static void *do_monitor_phone(void *data)
                        ast_debug(1, "[%s] error parsing message\n", pvt->id);
                        goto e_cleanup;
                case AT_READ_ERROR:
-                       ast_debug(1, "[%s] error reading from device: %s (%d)\n", pvt->id, strerror_r(errno, buf, sizeof(buf)), errno);
+                       ast_debug(1, "[%s] error reading from device: %s (%d)\n", pvt->id, strerror(errno), errno);
                        goto e_cleanup;
                default:
                        break;
@@ -3896,11 +4068,7 @@ static void *do_monitor_headset(void *data)
                        continue;
 
                if ((at_msg = at_read_full(pvt->rfcomm_socket, buf, sizeof(buf))) < 0) {
-                       if (strerror_r(errno, buf, sizeof(buf)))
-                               ast_debug(1, "[%s] error reading from device\n", pvt->id);
-                       else
-                               ast_debug(1, "[%s] error reading from device: %s (%d)\n", pvt->id, buf, errno);
-
+                       ast_debug(1, "[%s] error reading from device: %s (%d)\n", pvt->id, strerror(errno), errno);
                        goto e_cleanup;
                }
                ast_debug(1, "[%s] %s\n", pvt->id, buf);
@@ -3962,7 +4130,7 @@ static void *do_monitor_headset(void *data)
 
                                pvt->incoming = 1;
 
-                               if (!(chan = mbl_new(AST_STATE_UP, pvt, NULL, NULL))) {
+                               if (!(chan = mbl_new(AST_STATE_UP, pvt, NULL, NULL, NULL))) {
                                        ast_log(LOG_ERROR, "[%s] unable to allocate channel for incoming call\n", pvt->id);
                                        ast_mutex_unlock(&pvt->lock);
                                        goto e_cleanup;
@@ -4519,7 +4687,8 @@ static int unload_module(void)
        if (sdp_session)
                sdp_close(sdp_session);
 
-       mbl_tech.capabilities = ast_format_cap_destroy(mbl_tech.capabilities);
+       ao2_ref(mbl_tech.capabilities, -1);
+       mbl_tech.capabilities = NULL;
        return 0;
 }
 
@@ -4528,16 +4697,20 @@ static int load_module(void)
 
        int dev_id, s;
 
-       if (!(mbl_tech.capabilities = ast_format_cap_alloc())) {
+       if (!(mbl_tech.capabilities = ast_format_cap_alloc(AST_FORMAT_CAP_FLAG_DEFAULT))) {
                return AST_MODULE_LOAD_DECLINE;
        }
-       ast_format_set(&prefformat, DEVICE_FRAME_FORMAT, 0);
-       ast_format_cap_add(mbl_tech.capabilities, &prefformat);
+
+       ast_format_cap_append(mbl_tech.capabilities, DEVICE_FRAME_FORMAT, 0);
        /* Check if we have Bluetooth, no point loading otherwise... */
        dev_id = hci_get_route(NULL);
+
        s = hci_open_dev(dev_id);
        if (dev_id < 0 || s < 0) {
                ast_log(LOG_ERROR, "No Bluetooth devices found. Not loading module.\n");
+               ao2_ref(mbl_tech.capabilities, -1);
+               mbl_tech.capabilities = NULL;
+               hci_close_dev(s);
                return AST_MODULE_LOAD_DECLINE;
        }
 
@@ -4545,6 +4718,8 @@ static int load_module(void)
 
        if (mbl_load_config()) {
                ast_log(LOG_ERROR, "Errors reading config file %s. Not loading module.\n", MBL_CONFIG);
+               ao2_ref(mbl_tech.capabilities, -1);
+               mbl_tech.capabilities = NULL;
                return AST_MODULE_LOAD_DECLINE;
        }
 
@@ -4569,14 +4744,14 @@ static int load_module(void)
        return AST_MODULE_LOAD_SUCCESS;
 
 e_cleanup:
-       if (sdp_session)
-               sdp_close(sdp_session);
+       unload_module();
 
-       return AST_MODULE_LOAD_FAILURE;
+       return AST_MODULE_LOAD_DECLINE;
 }
 
 AST_MODULE_INFO(ASTERISK_GPL_KEY, AST_MODFLAG_LOAD_ORDER, "Bluetooth Mobile Device Channel Driver",
-               .load = load_module,
-               .unload = unload_module,
-               .load_pri = AST_MODPRI_CHANNEL_DRIVER,
+       .support_level = AST_MODULE_SUPPORT_EXTENDED,
+       .load = load_module,
+       .unload = unload_module,
+       .load_pri = AST_MODPRI_CHANNEL_DRIVER,
 );