[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