[asterisk-addons-commits] mnicholson: branch mnicholson/chan-mobile-refactor r762 - /team/mnicholson/ch...
SVN commits to the Asterisk addons project
asterisk-addons-commits at lists.digium.com
Wed Jan 28 16:04:49 CST 2009
Author: mnicholson
Date: Wed Jan 28 16:04:48 2009
New Revision: 762
URL: http://svn.digium.com/svn-view/asterisk-addons?view=rev&rev=762
Log:
Added a bunch of handsfree profile (HFP) helper code to chan_mobile to make
dealing with HFP devices more robust and more structured. This new code has
been partial integrated into the do_monitor_phone() function. All of the new
code is documented with in code comments.
* channels/chan_mobile.c (struct mbl_pvt): modified to contain a struct hfp_pvt member
* channels/chan_mobile.c (start_monitor): modified to set up the
hfp_pvt->rsock member
* channels/chan_mobile.c (do_monitor_phone): updated to use new CIEV handling
code and new HFP handling code. Also tweaked some error and debug messages.
* channels/chan_mobile.c (mbl_load_config): updated to initialize an hfp_pvt
structure for phones
* channels/chan_mobile.c (unload_module): updated to clean up the hfp_pvt
structure when devices are destroyed
Modified:
team/mnicholson/chan-mobile-refactor/channels/chan_mobile.c
Modified: team/mnicholson/chan-mobile-refactor/channels/chan_mobile.c
URL: http://svn.digium.com/svn-view/asterisk-addons/team/mnicholson/chan-mobile-refactor/channels/chan_mobile.c?view=diff&rev=762&r1=761&r2=762
==============================================================================
--- team/mnicholson/chan-mobile-refactor/channels/chan_mobile.c (original)
+++ team/mnicholson/chan-mobile-refactor/channels/chan_mobile.c Wed Jan 28 16:04:48 2009
@@ -130,6 +130,7 @@
static AST_RWLIST_HEAD_STATIC(adapters, adapter_pvt);
+struct hfp_pvt;
struct mbl_pvt {
struct ast_channel *owner; /* Channel we belong to, possibly NULL */
struct ast_frame fr; /* "null" frame */
@@ -140,6 +141,7 @@
struct adapter_pvt *adapter; /* the adapter we use */
char context[AST_MAX_CONTEXT]; /* the context for incoming calls */
char connected; /* is it connected? */
+ struct hfp_pvt *hfp; /*!< hfp pvt */
int rfcomm_port; /* rfcomm port number */
int rfcomm_socket; /* rfcomm socket descriptor */
char rfcomm_buf[256];
@@ -234,6 +236,165 @@
static void *do_sco_listen(void *data);
static int sdp_search(char *addr, int profile);
+/*
+ * bluetooth handsfree profile helpers
+ */
+
+#define HFP_HF_ECNR (1 << 0)
+#define HFP_HF_CW (1 << 1)
+#define HFP_HF_CID (1 << 2)
+#define HFP_HF_VOICE (1 << 3)
+#define HFP_HF_VOLUME (1 << 4)
+#define HFP_HF_STATUS (1 << 5)
+#define HFP_HF_CONTROL (1 << 6)
+
+#define HFP_AG_CW (1 << 0)
+#define HFP_AG_ECNR (1 << 1)
+#define HFP_AG_VOICE (1 << 2)
+#define HFP_AG_RING (1 << 3)
+#define HFP_AG_TAG (1 << 4)
+#define HFP_AG_REJECT (1 << 5)
+#define HFP_AG_STATUS (1 << 6)
+#define HFP_AG_CONTROL (1 << 7)
+#define HFP_AG_ERRORS (1 << 8)
+
+#define HFP_CIND_NONE 0
+#define HFP_CIND_SERVICE 1
+#define HFP_CIND_CALL 2
+#define HFP_CIND_CALLSETUP 3
+#define HFP_CIND_CALLHELD 4
+#define HFP_CIND_SIGNAL 5
+#define HFP_CIND_ROAM 6
+#define HFP_CIND_BATTCHG 7
+
+/* call indicator values */
+#define HFP_CIND_CALL_NONE 0
+#define HFP_CIND_CALL_ACTIVE 1
+
+/* callsetup indicator values */
+#define HFP_CIND_CALLSETUP_NONE 0
+#define HFP_CIND_CALLSETUP_INCOMING 1
+#define HFP_CIND_CALLSETUP_OUTGOING 2
+#define HFP_CIND_CALLSETUP_ALERTING 3
+
+typedef enum {
+ HFP_DISCONNECT = -2,
+ HFP_TIMEOUT = -1,
+ HFP_UNKNOWN = 0,
+ HFP_OK,
+ HFP_ERROR,
+ HFP_RING,
+ HFP_BRSF,
+ HFP_CIND,
+ HFP_CIEV,
+ HFP_CLIP,
+} hfp_message_t;
+
+/*
+ * \brief This struct holds HFP features that we support.
+ */
+struct hfp_hf {
+ int ecnr:1; /*!< echo-cancel/noise reduction */
+ int cw:1; /*!< call waiting and three way calling */
+ int cid:1; /*!< cli presentation (callier id) */
+ int voice:1; /*!< voice recognition activation */
+ int volume:1; /*!< remote volume control */
+ int status:1; /*!< enhanced call status */
+ int control:1; /*!< enhanced call control*/
+};
+
+/*
+ * \brief This struct holds HFP features the AG supports.
+ */
+struct hfp_ag {
+ int cw:1; /*!< three way calling */
+ int ecnr:1; /*!< echo-cancel/noise reduction */
+ int voice:1; /*!< voice recognition */
+ int ring:1; /*!< in band ring tone capability */
+ int tag:1; /*!< attach a number to a voice tag */
+ int reject:1; /*!< ability to reject a call */
+ int status:1; /*!< enhanced call status */
+ int control:1; /*!< enhanced call control*/
+ int errors:1; /*!< extended error result codes*/
+};
+
+/*
+ * \brief This struct holds mappings for indications.
+ */
+struct hfp_cind {
+ int service; /*!< whether we have service or not */
+ int call; /*!< call state */
+ int callsetup; /*!< bluetooth call setup indications */
+ int callheld; /*!< bluetooth call hold indications */
+ int signal; /*!< signal strength */
+ int roam; /*!< roaming indicator */
+ int battchg; /*!< battery charge indicator */
+};
+
+
+/*
+ * \brief This struct holds state information about the current hfp connection.
+ */
+struct hfp_pvt {
+ struct mbl_pvt *owner; /*!< the mbl_pvt struct that owns this struct */
+ int connected:1; /*!< whether a service level connection exists or not */
+ int blackberry:1; /*!< blackberry mode (send CMER before CIND=?) */
+ int nocallsetup:1; /*!< whether we detected a callsetup indicator */
+ int have_sms:1; /*!< whether we detected sms support or not */
+ struct hfp_ag brsf; /*!< the supported feature set of the AG */
+ int cind_index[16]; /*!< the cind/ciev index to name mapping for this AG */
+ int cind_state[16]; /*!< the cind/ciev state for this AG */
+ struct hfp_cind cind_map; /*!< the cind name to index mapping for this AG */
+ int rsock; /*!< our rfcomm socket */
+ int rport; /*!< our rfcomm port */
+};
+
+
+/* Our supported features.
+ * we only support caller id
+ */
+static struct hfp_hf hfp_our_brsf = {
+ .ecnr = 0,
+ .cw = 0,
+ .cid = 1,
+ .voice = 0,
+ .volume = 0,
+ .status = 0,
+ .control = 0,
+};
+
+
+static int hfp_init(struct hfp_pvt *pvt);
+static int hfp_init_sms(struct hfp_pvt *pvt);
+static int hfp_wait(struct hfp_pvt *pvt);
+static int hfp_parse_ciev(struct hfp_pvt *pvt, char *buf, int *value);
+
+static int hfp_brsf2int(struct hfp_hf *hf);
+static struct hfp_ag *hfp_int2brsf(int brsf, struct hfp_ag *ag);
+
+static int hfp_send_brsf(struct hfp_pvt *pvt, struct hfp_hf *brsf);
+static int hfp_send_cind(struct hfp_pvt *pvt);
+static int hfp_send_cind_test(struct hfp_pvt *pvt);
+static int hfp_send_cmer(struct hfp_pvt *pvt, int status);
+static int hfp_send_clip(struct hfp_pvt *pvt, int status);
+static int hfp_send_vgs(struct hfp_pvt *pvt, int value);
+#if 0
+static int hfp_send_vgm(struct hfp_pvt *pvt, int value);
+#endif
+static int hfp_send_dtmf(struct hfp_pvt *pvt, char digit);
+static int hfp_send_cmgf(struct hfp_pvt *pvt, int mode);
+static int hfp_send_cnmi(struct hfp_pvt *pvt);
+
+static hfp_message_t hfp_read_full(struct hfp_pvt *pvt, char *buf, size_t count);
+static hfp_message_t hfp_read(struct hfp_pvt *pvt);
+static int hfp_read_brsf(struct hfp_pvt *pvt);
+static int hfp_read_cind(struct hfp_pvt *pvt);
+static int hfp_read_cind_test(struct hfp_pvt *pvt);
+
+/*
+ * channel stuff
+ */
+
static const struct ast_channel_tech mbl_tech = {
.type = "Mobile",
.description = "Bluetooth Mobile Device Channel Driver",
@@ -739,40 +900,17 @@
static int mbl_digit_end(struct ast_channel *ast, char digit, unsigned int duration)
{
-
- struct mbl_pvt *pvt;
- char buf[11];
-
- pvt = ast->tech_pvt;
+ struct mbl_pvt *pvt = ast->tech_pvt;
if (pvt->type == MBL_TYPE_HEADSET)
return 0;
-
+
ast_debug(1, "Dialed %c\n", digit);
-
- switch(digit) {
- case '0':
- case '1':
- case '2':
- case '3':
- case '4':
- case '5':
- case '6':
- case '7':
- case '8':
- case '9':
- case '*':
- case '#':
- snprintf(buf, sizeof(buf), "AT+VTS=%c\r", digit);
- rfcomm_write(pvt->rfcomm_socket, buf);
- break;
- default:
- ast_log(LOG_WARNING, "Unknown digit '%c'\n", digit);
+ if (hfp_send_dtmf(pvt->hfp, digit) || !hfp_wait(pvt->hfp) || hfp_read(pvt->hfp) != HFP_OK) {
return -1;
}
return 0;
-
}
static struct ast_frame *mbl_read(struct ast_channel *ast)
@@ -1245,6 +1383,589 @@
}
/*
+ * bluetooth handsfree profile helpers
+ */
+
+/*
+ * \brief Initilize an HFP service level connection.
+ * \param pvt an hfp_pvt struct
+ *
+ * This function brings up an HFP service level connection.
+ *
+ * \note This function expects a connected rfcomm socket and it expects the pvt
+ * structure to be initilized with zeroes.
+ */
+static int hfp_init(struct hfp_pvt *pvt)
+{
+ /* send and receive BRSF data */
+ if (hfp_send_brsf(pvt, &hfp_our_brsf) || !hfp_wait(pvt) || hfp_read_brsf(pvt)) {
+ ast_debug(1, "[%s] error sending/receiving BRSF\n", pvt->owner->id);
+ return -1;
+ }
+
+ /* if this is a blackberry, do CMER now */
+ if (pvt->blackberry) {
+ if (hfp_send_cmer(pvt, 1) || !hfp_wait(pvt) || hfp_read(pvt) != HFP_OK) {
+ ast_debug(1, "[%s] error sending CMER, try setting 'blackberry=no'\n", pvt->owner->id);
+ return -1;
+ }
+ }
+
+ /* send CIND test */
+ if (hfp_send_cind_test(pvt) || !hfp_wait(pvt) || hfp_read_cind_test(pvt)) {
+ ast_debug(1, "[%s] error performing CIND test, try setting 'blackberry=yes'\n", pvt->owner->id);
+ return -1;
+ }
+
+ /* read current CIND state */
+ if (hfp_send_cind(pvt) || !hfp_wait(pvt) || hfp_read_cind(pvt)) {
+ ast_debug(1, "[%s] error getting CIND state\n", pvt->owner->id);
+ return -1;
+ }
+
+ /* check if a call is active */
+ if (pvt->cind_state[pvt->cind_map.call]) {
+ ast_verb(3, "Bluetooth Device %s has a call in progress - delaying connection.\n", pvt->owner->id);
+ return -1;
+ }
+
+ /* if this is not a blackberry send CMER now */
+ if (!pvt->blackberry) {
+ if (hfp_send_cmer(pvt, 1) || !hfp_wait(pvt) || hfp_read(pvt) != HFP_OK) {
+ ast_debug(1, "[%s] error sending CMER\n", pvt->owner->id);
+ return -1;
+ }
+ }
+
+ /* enalbe calling line identification notification */
+ if (hfp_send_clip(pvt, 1) || !hfp_wait(pvt) || hfp_read(pvt) != HFP_OK) {
+ ast_debug(1, "[%s] error enabling calling line notification\n", pvt->owner->id);
+ return -1;
+ }
+
+ /* send current gain levels */
+ if (hfp_send_vgs(pvt, 15) || !hfp_wait(pvt) || hfp_read(pvt) != HFP_OK) {
+ ast_debug(1, "[%s] error synchronizing gain settings\n", pvt->owner->id);
+ return -1;
+ }
+
+ /* we now have a service level connection */
+ pvt->connected = 1;
+
+ if (hfp_init_sms(pvt)) {
+ ast_debug(1, "[%s] no SMS support\n", pvt->owner->id);
+ } else {
+ pvt->have_sms = 1;
+ }
+
+ return 0;
+}
+
+static int hfp_init_sms(struct hfp_pvt *pvt)
+{
+ /* set the SMS operating mode to text mode */
+ if (hfp_send_cmgf(pvt, 1) || !hfp_wait(pvt) || hfp_read(pvt) != HFP_OK) {
+ ast_debug(1, "[%s] error setting CMGF\n", pvt->owner->id);
+ return -1;
+ }
+
+ /* turn on SMS new message indication */
+ if (hfp_send_cnmi(pvt) || !hfp_wait(pvt) || hfp_read(pvt) != HFP_OK) {
+ ast_debug(1, "[%s] error setting CNMI\n", pvt->owner->id);
+ return -1;
+ }
+
+ return 0;
+}
+
+
+/*
+ * \brief Wait a default timeout.
+ * \return zero on timeout and non zero on data
+ */
+static int hfp_wait(struct hfp_pvt *pvt)
+{
+ int ms = 1000;
+ return rfcomm_wait(pvt->rsock, &ms);
+}
+
+/*
+ * \brief Parse a CIEV event.
+ * \param pvt an hfp_pvt struct
+ * \param buf the buffer to parse (null terminated)
+ * \param value a pointer to an int to store the event value in (can be NULL)
+ * \return 0 on error (parse error, or unknown event) or a HFP_CIND_* value on
+ * success
+ */
+static int hfp_parse_ciev(struct hfp_pvt *pvt, char *buf, int *value)
+{
+ int i, v;
+ if (!value)
+ value = &v;
+
+ if (!sscanf(buf, "+CIEV: %i,%i", &i, value)) {
+ ast_debug(2, "[%s] error parsing CIEV event '%s'\n", pvt->owner->id, buf);
+ return HFP_CIND_NONE;
+ }
+
+ if (i >= sizeof(pvt->cind_state)) {
+ ast_debug(2, "[%s] CIEV event index too high (%s)\n", pvt->owner->id, buf);
+ return HFP_CIND_NONE;
+ }
+
+ pvt->cind_state[i] = *value;
+ return pvt->cind_index[i];
+}
+
+/*
+ * \brief Convert a hfp_hf struct to a BRSF int.
+ */
+static int hfp_brsf2int(struct hfp_hf *hf)
+{
+ int brsf = 0;
+
+ brsf |= hf->ecnr ? HFP_HF_ECNR : 0;
+ brsf |= hf->cw ? HFP_HF_CW : 0;
+ brsf |= hf->cid ? HFP_HF_CID : 0;
+ brsf |= hf->voice ? HFP_HF_VOICE : 0;
+ brsf |= hf->volume ? HFP_HF_VOLUME : 0;
+ brsf |= hf->status ? HFP_HF_STATUS : 0;
+ brsf |= hf->control ? HFP_HF_CONTROL : 0;
+
+ return brsf;
+}
+
+/*
+ * \brief Convert a BRSF int to an hfp_ag struct.
+ */
+static struct hfp_ag *hfp_int2brsf(int brsf, struct hfp_ag *ag)
+{
+ ag->cw = brsf & HFP_AG_CW ? 1 : 0;
+ ag->ecnr = brsf & HFP_AG_ECNR ? 1 : 0;
+ ag->voice = brsf & HFP_AG_VOICE ? 1 : 0;
+ ag->ring = brsf & HFP_AG_RING ? 1 : 0;
+ ag->tag = brsf & HFP_AG_TAG ? 1 : 0;
+ ag->reject = brsf & HFP_AG_REJECT ? 1 : 0;
+ ag->status = brsf & HFP_AG_STATUS ? 1 : 0;
+ ag->control = brsf & HFP_AG_CONTROL ? 1 : 0;
+ ag->errors = brsf & HFP_AG_ERRORS ? 1 : 0;
+
+ return ag;
+}
+
+/*
+ * \brief Match the given buffer with the given prefix.
+ * \param buf the buffer to match
+ * \param prefix the prefix to match
+ */
+static int hfp_match_prefix(char *buf, char *prefix)
+{
+ return !strncmp(buf, prefix, strlen(prefix));
+}
+
+/*
+ * \brief Read an HFP message and clasify it.
+ * \param pvt an hfp_pvt struct
+ * \param buf the buffer to store the result in
+ * \param count the size of the buffer or the maximum number of characters to read
+ * \return the type of message received, in addition buf will contain the
+ * message received and will be null terminated
+ * \see hfp_read()
+ */
+static hfp_message_t hfp_read_full(struct hfp_pvt *pvt, char *buf, size_t count)
+{
+ ssize_t s;
+ if ((s = rfcomm_read(pvt->rsock, buf, count - 1)) < 1)
+ return HFP_DISCONNECT;
+ buf[s] = '\0';
+
+ if (!strcmp("OK", buf)) {
+ return HFP_OK;
+ } else if (!strcmp("ERROR", buf)) {
+ return HFP_ERROR;
+ } else if (!strcmp("RING", buf)) {
+ return HFP_RING;
+ } else if (hfp_match_prefix(buf, "+CIEV:")) {
+ return HFP_CIEV;
+ } else if (hfp_match_prefix(buf, "+BRSF:")) {
+ return HFP_BRSF;
+ } else if (hfp_match_prefix(buf, "+CIND:")) {
+ return HFP_CIND;
+ } else if (hfp_match_prefix(buf, "+CLIP:")) {
+ return HFP_CLIP;
+ } else {
+ return HFP_UNKNOWN;
+ }
+}
+
+/*
+ * \brief Read an HFP message and clasify it.
+ * \param pvt an hfp_pvt struct
+ * \return the type of message received
+ * \see hfp_read_full()
+ */
+static hfp_message_t hfp_read(struct hfp_pvt *pvt)
+{
+ char buf[16];
+ return hfp_read_full(pvt, buf, sizeof(buf));
+}
+
+
+/*
+ * \brief Send a BRSF request.
+ * \param pvt an hfp_pvt struct
+ * \param brsf an hfp_hf brsf struct
+ *
+ * \retval 0 on success
+ * \retval -1 on error
+ */
+static int hfp_send_brsf(struct hfp_pvt *pvt, struct hfp_hf *brsf)
+{
+ char cmd[32];
+ snprintf(cmd, sizeof(cmd), "AT+BRSF=%d\r", hfp_brsf2int(brsf));
+ return rfcomm_write(pvt->rsock, cmd);
+}
+
+/*
+ * \brief Send the CIND read command.
+ * \param pvt an hfp_pvt struct
+ */
+static int hfp_send_cind(struct hfp_pvt *pvt)
+{
+ return rfcomm_write(pvt->rsock, "AT+CIND?\r");
+}
+
+/*
+ * \brief Send the CIND test command.
+ * \param pvt an hfp_pvt struct
+ */
+static int hfp_send_cind_test(struct hfp_pvt *pvt)
+{
+ return rfcomm_write(pvt->rsock, "AT+CIND=?\r");
+}
+
+/*
+ * \brief Enable or disable indicator events reporting.
+ * \param pvt an hfp_pvt struct
+ * \param status enable or disable events reporting (should be 1 or 0)
+ */
+static int hfp_send_cmer(struct hfp_pvt *pvt, int status)
+{
+ char cmd[32];
+ snprintf(cmd, sizeof(cmd), "AT+CMER=3,0,0,%i\r", status ? 1 : 0);
+ return rfcomm_write(pvt->rsock, cmd);
+}
+
+/*
+ * \brief Send the current speaker gain level.
+ * \param pvt an hfp_pvt struct
+ * \param value the value to send (must be between 0 and 15)
+ */
+static int hfp_send_vgs(struct hfp_pvt *pvt, int value)
+{
+ char cmd[32];
+ snprintf(cmd, sizeof(cmd), "AT+VGS=%i\r", value);
+ return rfcomm_write(pvt->rsock, cmd);
+}
+
+#if 0
+/*
+ * \brief Send the current microphone gain level.
+ * \param pvt an hfp_pvt struct
+ * \param value the value to send (must be between 0 and 15)
+ */
+static int hfp_send_vgm(struct hfp_pvt *pvt, int value)
+{
+ char cmd[32];
+ snprintf(cmd, sizeof(cmd), "AT+VGM=%i\r", value);
+ return rfcomm_write(pvt->rsock, cmd);
+}
+#endif
+
+/*
+ * \brief Enable or disable calling line identification.
+ * \param pvt an hfp_pvt struct
+ * \param status enable or disable calling line identification (should be 1 or
+ * 0)
+ */
+static int hfp_send_clip(struct hfp_pvt *pvt, int status)
+{
+ char cmd[32];
+ snprintf(cmd, sizeof(cmd), "AT+CLIP=%i\r", status ? 1 : 0);
+ return rfcomm_write(pvt->rsock, cmd);
+}
+
+/*
+ * \brief Send a DTMF command.
+ * \param pvt an hfp_pvt struct
+ * \param digit the dtmf digit to send
+ * \return the result of rfcomm_write() or -1 on an invalid digit being sent
+ */
+static int hfp_send_dtmf(struct hfp_pvt *pvt, char digit)
+{
+ char cmd[10];
+
+ switch(digit) {
+ case '0':
+ case '1':
+ case '2':
+ case '3':
+ case '4':
+ case '5':
+ case '6':
+ case '7':
+ case '8':
+ case '9':
+ case '*':
+ case '#':
+ snprintf(cmd, sizeof(cmd), "AT+VTS=%c\r", digit);
+ return rfcomm_write(pvt->rsock, cmd);
+ default:
+ return -1;
+ }
+}
+
+/*
+ * \brief Set the SMS mode.
+ * \param pvt an hfp_pvt struct
+ * \param mode the sms mode (0 = PDU, 1 = Text)
+ */
+static int hfp_send_cmgf(struct hfp_pvt *pvt, int mode)
+{
+ char cmd[32];
+ snprintf(cmd, sizeof(cmd), "AT+CMGF=%i\r", mode);
+ return rfcomm_write(pvt->rsock, cmd);
+}
+
+/*
+ * \brief Setup SMS new message indication.
+ * \param pvt an hfp_pvt struct
+ */
+static int hfp_send_cnmi(struct hfp_pvt *pvt)
+{
+ return rfcomm_write(pvt->rsock, "AT+CNMI=2,1,0,1,0\r");
+}
+
+/*
+ * \brief Read BRSF data.
+ * \param pvt an hfp_pvt struct
+ */
+static int hfp_read_brsf(struct hfp_pvt *pvt)
+{
+ int brsf;
+ char buf[128];
+
+ /* read the BRSF data */
+ if (hfp_read_full(pvt, buf, sizeof(buf)) != HFP_BRSF)
+ return -1;
+
+ if (!sscanf(buf, "+BRSF:%i", &brsf))
+ return -1;
+
+ hfp_int2brsf(brsf, &pvt->brsf);
+
+ /* read the OK message */
+ if (!hfp_wait(pvt) || hfp_read(pvt) != HFP_OK)
+ return -1;
+
+ return 0;
+}
+
+/*
+ * \brief Parse and store the given indicator.
+ * \param pvt an hfp_pvt struct
+ * \param group the indicator group
+ * \param indicator the indicator to parse
+ */
+static int hfp_parse_cind_indicator(struct hfp_pvt *pvt, int group, char *indicator)
+{
+ int value;
+
+ /* store the current indicator */
+ if (group >= sizeof(pvt->cind_state)) {
+ ast_debug(1, "ignoring CIND state '%s' for group %i, we only support up to %i indicators\n", indicator, group, (int) sizeof(pvt->cind_state));
+ return -1;
+ }
+
+ if (!sscanf(indicator, "%i", &value)) {
+ ast_debug(1, "error parsing CIND state '%s' for group %i\n", indicator, group);
+ return -1;
+ }
+
+ pvt->cind_state[group] = value;
+ return 0;
+}
+
+/*
+ * \brief Read the result of the AT+CIND? command.
+ * \param pvt an hfp_pvt struct
+ * \note hfp_send_cind_test() and hfp_read_cind_test() should be called at
+ * least once before this function is called.
+ */
+static int hfp_read_cind(struct hfp_pvt *pvt)
+{
+ int i, state, group;
+ size_t s;
+ char buf[256];
+ char *indicator;
+
+ if (hfp_read_full(pvt, buf, sizeof(buf)) != HFP_CIND)
+ return -1;
+
+ /* parse current state of all of our indicators. The list is in the
+ * following format:
+ * +CIND: 1,0,2,0,0,0,0
+ */
+ group = 0;
+ state = 0;
+ s = strlen(buf);
+ for (i = 0; i < s; i++) {
+ switch (state) {
+ case 0: /* search for start of the status indicators (a space) */
+ if (buf[i] == ' ') {
+ group++;
+ state++;
+ }
+ break;
+ case 1: /* mark this indicator */
+ indicator = &buf[i];
+ state++;
+ break;
+ case 2: /* search for the start of the next indicator (a comma) */
+ if (buf[i] == ',') {
+ buf[i] = '\0';
+
+ hfp_parse_cind_indicator(pvt, group, indicator);
+
+ group++;
+ state = 1;
+ }
+ break;
+ }
+ }
+
+ /* store the last indicator */
+ if (state == 2)
+ hfp_parse_cind_indicator(pvt, group, indicator);
+
+ /* read the OK message */
+ if (!hfp_wait(pvt) || hfp_read(pvt) != HFP_OK)
+ return -1;
+
+ return 0;
+}
+
+/*
+ * \brief Read the result of the AT+CIND=? command.
+ * \param pvt an hfp_pvt struct
+ */
+static int hfp_read_cind_test(struct hfp_pvt *pvt)
+{
+ int i, state, group;
+ size_t s;
+ char buf[512];
+ char *indicator, *values;
+
+ if (hfp_read_full(pvt, buf, sizeof(buf)) != HFP_CIND)
+ return -1;
+
+ pvt->nocallsetup = 1;
+
+ /* parse the indications list. It is in the follwing format:
+ * +CIND: ("ind1",(0-1)),("ind2",(0-5))
+ */
+ group = 0;
+ state = 0;
+ s = strlen(buf);
+ for (i = 0; i < s; i++) {
+ switch (state) {
+ case 0: /* search for start of indicator block */
+ if (buf[i] == '(') {
+ group++;
+ state++;
+ }
+ break;
+ case 1: /* search for '"' in indicator block */
+ if (buf[i] == '"') {
+ state++;
+ }
+ break;
+ case 2: /* mark the start of the indicator name */
+ indicator = &buf[i];
+ state++;
+ break;
+ case 3: /* look for the end of the indicator name */
+ if (buf[i] == '"') {
+ buf[i] = '\0';
+ state++;
+ }
+ break;
+ case 4: /* find the start of the value range */
+ if (buf[i] == '(') {
+ state++;
+ }
+ break;
+ case 5: /* mark the start of the value range */
+ values = &buf[i];
+ state++;
+ break;
+ case 6: /* find the end of the value range */
+ if (buf[i] == ')') {
+ buf[i] = '\0';
+ state++;
+ }
+ break;
+ case 7: /* process the values we found */
+ if (group < sizeof(pvt->cind_index)) {
+ if (!strcmp(indicator, "service")) {
+ pvt->cind_map.service = group;
+ pvt->cind_index[group] = HFP_CIND_SERVICE;
+ } else if (!strcmp(indicator, "call")) {
+ pvt->cind_map.call = group;
+ pvt->cind_index[group] = HFP_CIND_CALL;
+ } else if (!strcmp(indicator, "callsetup")) {
+ pvt->nocallsetup = 0;
+ pvt->cind_map.callsetup = group;
+ pvt->cind_index[group] = HFP_CIND_CALLSETUP;
+ } else if (!strcmp(indicator, "call_setup")) { /* non standard call setup identifier */
+ pvt->nocallsetup = 0;
+ pvt->cind_map.callsetup = group;
+ pvt->cind_index[group] = HFP_CIND_CALLSETUP;
+ } else if (!strcmp(indicator, "callheld")) {
+ pvt->cind_map.callheld = group;
+ pvt->cind_index[group] = HFP_CIND_CALLHELD;
+ } else if (!strcmp(indicator, "signal")) {
+ pvt->cind_map.signal = group;
+ pvt->cind_index[group] = HFP_CIND_SIGNAL;
+ } else if (!strcmp(indicator, "roam")) {
+ pvt->cind_map.roam = group;
+ pvt->cind_index[group] = HFP_CIND_ROAM;
+ } else if (!strcmp(indicator, "battchg")) {
+ pvt->cind_map.battchg = group;
+ pvt->cind_index[group] = HFP_CIND_BATTCHG;
+ } else {
+ ast_debug(2, "ignoring unknown CIND indicator '%s'\n", indicator);
+ }
+ } else {
+ ast_debug(1, "can't store indicator %d (%s), we only support up to %i indicators", group, indicator, (int) sizeof(pvt->cind_index));
+ }
+
+ state = 0;
+ break;
+ }
+ }
+
+ pvt->owner->no_callsetup = pvt->nocallsetup;
+
+ /* read the OK message */
+ if (!hfp_wait(pvt) || hfp_read(pvt) != HFP_OK)
+ return -1;
+
+ return 0;
+}
+
+
+/*
sdp helpers
@@ -1370,22 +2091,27 @@
{
struct mbl_pvt *pvt = (struct mbl_pvt *)data;
+ struct hfp_pvt *hfp = pvt->hfp;
struct ast_channel *chn;
char monitor = 1;
char buf[256];
char cid_num[AST_MAX_EXTENSION], *pcids, *pcide;
int t, i, smsi, waitfd;
- ssize_t s;
int group, group2;
int callp = 0, callsetupp;
char brsf, nsmode, *p, *p1;
char sms_src[13];
char sms_txt[160];
+ hfp_message_t hfp_msg;
brsf = nsmode = 0;
- if (rfcomm_write(pvt->rfcomm_socket, "AT+BRSF=4\r"))
+ if (hfp_init(hfp)) {
+ ast_verb(3, "Error initializing Bluetooth device %s.\n", pvt->id);
monitor = 0;
+ } else {
+ pvt->state = MBL_STATE_PREIDLE;
+ }
while (monitor) {
@@ -1400,14 +2126,17 @@
else
t = 1000;
- if (waitfd = rfcomm_wait(pvt->rfcomm_socket, &t)) {
- if ((s = rfcomm_read(pvt->rfcomm_socket, buf, sizeof(buf) - 1)) < 1) {
- ast_verb(3, "Bluetooth Device %s has disconnected, reason (%d).\n", pvt->id, errno);
+ if ((waitfd = rfcomm_wait(pvt->rfcomm_socket, &t))) {
+ if ((hfp_msg = hfp_read_full(hfp, 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);
+
monitor = 0;
continue;
}
- buf[s] = '\0';
- ast_debug(1, "rfcomm_read() (%s) [%s]\n", pvt->id, buf);
+ ast_debug(1, "[%s] %s\n", pvt->id, buf);
switch (pvt->state) {
case MBL_STATE_INIT:
@@ -1521,13 +2250,18 @@
case MBL_STATE_PREIDLE: /* Nothing handled here, wait for timeout, then off we go... */
break;
case MBL_STATE_IDLE:
- ast_debug(1, "Device %s [%s]\n", pvt->id, buf);
- if (strstr(buf, "RING")) {
+ if (hfp_msg == HFP_RING) {
pvt->state = MBL_STATE_RING;
- } else if (strstr(buf, "+CIEV:")) {
- if (strstr(buf, pvt->ciev_callsetup_3)) { /* User has dialed out on handset */
- monitor = 0; /* We disconnect now, as he is */
- } /* probably leaving BT range... */
+ } else if (hfp_msg == HFP_CIEV
+ && hfp_parse_ciev(hfp, buf, &i) == HFP_CIND_CALLSETUP
+ && i == HFP_CIND_CALLSETUP_ALERTING) {
+
+ /* the user has dialed out on the
+ * handset we disconnect now, as he is
+ * probably leaving BT range...
+ */
+ monitor = 0;
+ ast_verb(3, "Disconnecting bluetooth device %s, user has initated a call from the handset.\n", pvt->id);
}
break;
case MBL_STATE_DIAL: /* Nothing handled here, we need to wait for a timeout */
@@ -1543,14 +2277,23 @@
}
break;
case MBL_STATE_OUTGOING:
- if (strstr(buf, "+CIEV")) {
- if (strstr(buf, pvt->ciev_call_0)) { /* call was hung up */
- pvt->do_hangup = 0;
- ast_queue_control(pvt->owner, AST_CONTROL_HANGUP);
- } else if (strstr(buf, pvt->ciev_callsetup_3)) { /* b-party ringing */
- ast_queue_control(pvt->owner, AST_CONTROL_RINGING);
- } else if (strstr(buf, pvt->ciev_call_1) && !pvt->no_callsetup) { /* b-party answer */
- ast_queue_control(pvt->owner, AST_CONTROL_ANSWER);
+ if (hfp_msg == HFP_CIEV) {
+ switch (hfp_parse_ciev(hfp, buf, &i)) {
+ case HFP_CIND_CALL:
+ if (i == HFP_CIND_CALL_NONE) {
+ pvt->do_hangup = 0;
+ ast_queue_control(pvt->owner, AST_CONTROL_HANGUP);
+ } else if (i == HFP_CIND_CALL_ACTIVE && !pvt->no_callsetup) {
+ ast_queue_control(pvt->owner, AST_CONTROL_ANSWER);
+ }
+ break;
+ case HFP_CIND_CALLSETUP:
+ if (i == HFP_CIND_CALLSETUP_ALERTING) {
+ ast_queue_control(pvt->owner, AST_CONTROL_RINGING);
+ }
+ break;
+ default: /* do nothing */
+ break;
}
}
break;
@@ -1592,32 +2335,45 @@
}
break;
case MBL_STATE_RING3:
- if (strstr(buf, "+CIEV")) {
- if (strstr(buf, pvt->ciev_call_1)) {
- if (pvt->sent_answer) { /* We answered */
- pvt->state = MBL_STATE_INCOMING;
- } else { /* User answered on handset!, disconnect */
- pvt->state = MBL_STATE_IDLE;
- if (pvt->sco_socket > -1)
- close(pvt->sco_socket);
+ if (hfp_msg == HFP_CIEV) {
+ switch (hfp_parse_ciev(hfp, buf, &i)) {
+ case HFP_CIND_CALL:
+ if (i) { /* call active */
+ if (pvt->sent_answer) { /* We answered */
+ pvt->state = MBL_STATE_INCOMING;
+ } else { /* User answered on handset!, disconnect */
+ pvt->state = MBL_STATE_IDLE;
+ if (pvt->sco_socket > -1)
+ close(pvt->sco_socket);
+ ast_queue_control(pvt->owner, AST_CONTROL_HANGUP);
+ }
+ } else { /* no call */
ast_queue_control(pvt->owner, AST_CONTROL_HANGUP);
}
- }
- if ((strstr(buf, pvt->ciev_callsetup_0) || strstr(buf, pvt->ciev_call_0))) { /* Caller disconnected */
- ast_queue_control(pvt->owner, AST_CONTROL_HANGUP);
+ break;
+ case HFP_CIND_CALLSETUP:
+ if (i == HFP_CIND_CALLSETUP_NONE) {
+ ast_queue_control(pvt->owner, AST_CONTROL_HANGUP);
+ }
+ break;
+ default: /* do nothing */
+ break;
}
}
break;
case MBL_STATE_INCOMING:
- if (strstr(buf, "+CIEV")) {
- if (strstr(buf, pvt->ciev_call_0)) {
- pvt->do_hangup = 0;
- ast_queue_control(pvt->owner, AST_CONTROL_HANGUP);
- }
+ if (hfp_msg == HFP_CIEV
+ && hfp_parse_ciev(hfp, buf, &i) == HFP_CIND_CALL
+ && i == HFP_CIND_CALL_NONE) {
+
+ pvt->do_hangup = 0;
+ ast_queue_control(pvt->owner, AST_CONTROL_HANGUP);
}
break;
case MBL_STATE_HANGUP:
- if (strstr(buf, "OK") || strstr(buf, pvt->ciev_call_0)) {
+ if (hfp_msg == HFP_OK
+ || (hfp_parse_ciev(hfp, buf, &i) == HFP_CIND_CALL
+ && i == HFP_CIND_CALL_NONE)) {
close(pvt->sco_socket);
pvt->sco_socket = -1;
pvt->state = MBL_STATE_IDLE;
@@ -1743,6 +2499,7 @@
close(pvt->adapter->sco_socket);
+ ast_verb(3, "Bluetooth Device %s has disconnected.\n", pvt->id);
manager_event(EVENT_FLAG_SYSTEM, "MobileStatus", "Status: Disconnect\r\nDevice: %s\r\n", pvt->id);
pvt->adapter->inuse = 0;
@@ -1841,6 +2598,8 @@
{
if (pvt->type == MBL_TYPE_PHONE) {
+ pvt->hfp->rsock = pvt->rfcomm_socket;
+
if (ast_pthread_create_background(&pvt->monitor_thread, NULL, do_monitor_phone, pvt) < 0) {
pvt->monitor_thread = AST_PTHREADT_NULL;
return 0;
@@ -1880,7 +2639,7 @@
pvt->connected = 1;
adapter->inuse = 1;
manager_event(EVENT_FLAG_SYSTEM, "MobileStatus", "Status: Connect\r\nDevice: %s\r\n", pvt->id);
- ast_verb(3, "Bluetooth Device %s has connected.\n", pvt->id);
+ ast_verb(3, "Bluetooth Device %s has connected, initilizing...\n", pvt->id);
}
}
}
@@ -2149,6 +2908,20 @@
}
}
+ if (pvt->type == MBL_TYPE_PHONE) {
+ if (!(pvt->hfp = ast_calloc(1, sizeof(*pvt->hfp)))) {
+ ast_log(LOG_ERROR, "Skipping device %s. Error allocating memory.\n", pvt->id);
+ ast_dsp_free(pvt->dsp);
+ ast_free(pvt);
+ continue;
+ }
+
+ pvt->hfp->owner = pvt;
+ pvt->hfp->rport = pvt->rfcomm_port;
+ pvt->hfp->blackberry = pvt->blackberry;
+ pvt->hfp->nocallsetup = pvt->no_callsetup;
+ }
+
AST_RWLIST_WRLOCK(&devices);
AST_RWLIST_INSERT_HEAD(&devices, pvt, entry);
AST_RWLIST_UNLOCK(&devices);
@@ -2228,6 +3001,10 @@
if (pvt->rfcomm_socket > -1) {
close(pvt->rfcomm_socket);
}
+ if (pvt->hfp) {
+ ast_free(pvt->hfp);
+ }
+
ast_dsp_free(pvt->dsp);
ast_free(pvt);
}
More information about the asterisk-addons-commits
mailing list