[svn-commits] mnicholson: branch mnicholson/chan-mobile-refactor r802 - /team/mnicholson/ch...

SVN commits to the Digium repositories svn-commits at lists.digium.com
Fri Mar 13 11:23:57 CDT 2009


Author: mnicholson
Date: Fri Mar 13 11:23:52 2009
New Revision: 802

URL: http://svn.digium.com/svn-view/asterisk-addons?view=rev&rev=802
Log:
Cleaned up the handsfree profile code extensively.

Changes:
 * Rewrote do_monitor_phone() function to be more clear and more robust.
 * Added locking throughout the code.
 * Added debugging messages throughout the code.
 * Tweaked hangup, answer, and dial handling.
 * Hangup the chan_mobile channel when the bluetooth device disconnects.
 * Cleaned up the mbl_pvt and the hfp_pvt structure.
 * Tweaked output of mobile show devices cli command.

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=802&r1=801&r2=802
==============================================================================
--- team/mnicholson/chan-mobile-refactor/channels/chan_mobile.c (original)
+++ team/mnicholson/chan-mobile-refactor/channels/chan_mobile.c Fri Mar 13 11:23:52 2009
@@ -95,27 +95,11 @@
 
 enum mbl_state {
 	MBL_STATE_INIT = 0,
-	MBL_STATE_INIT1,
-	MBL_STATE_INIT2,
-	MBL_STATE_INIT3,
-	MBL_STATE_INIT4,
-	MBL_STATE_INIT5,
-	MBL_STATE_INIT6,
-	MBL_STATE_INIT7,
 	MBL_STATE_PREIDLE,
 	MBL_STATE_IDLE,
-	MBL_STATE_DIAL,
-	MBL_STATE_DIAL1,
-	MBL_STATE_OUTGOING,
 	MBL_STATE_RING,
 	MBL_STATE_RING2,
-	MBL_STATE_RING3,
 	MBL_STATE_INCOMING,
-	MBL_STATE_HANGUP,
-	MBL_STATE_INSMS,
-	MBL_STATE_OUTSMS,
-	MBL_STATE_OUTSMS1,
-	MBL_STATE_OUTSMS2
 };
 
 struct adapter_pvt {
@@ -135,18 +119,20 @@
 
 static AST_RWLIST_HEAD_STATIC(adapters, adapter_pvt);
 
+struct msg_queue_entry;
 struct hfp_pvt;
 struct mbl_pvt {
 	struct ast_channel *owner;			/* Channel we belong to, possibly NULL */
 	struct ast_frame fr;				/* "null" frame */
 	ast_mutex_t lock;				/*!< pvt lock */
+	/*! queue for messages we are expecting */
+	AST_LIST_HEAD_NOLOCK(msg_queue, msg_queue_entry) msg_queue;
 	enum mbl_type type;				/* Phone or Headset */
 	char id[31];					/* The id from mobile.conf */
 	int group;					/* group number for group dialling */
 	bdaddr_t addr;					/* address of device */
 	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 */
@@ -157,24 +143,39 @@
 	int sco_socket;					/* sco socket descriptor */
 	enum mbl_state state;				/* monitor thread current state */
 	pthread_t monitor_thread;			/* monitor thread handle */
-	char dial_number[AST_MAX_EXTENSION];		/* number for the monitor thread to dial */
-	int dial_timeout;
+	int timeout;					/*!< used to set the timeout for rfcomm data (may be used in the future) */
 	unsigned int no_callsetup:1;
 	unsigned int has_sms:1;
-	unsigned int sent_answer:1;
 	unsigned int do_alignment_detection:1;
 	unsigned int alignment_detection_triggered:1;
-	unsigned int do_hangup:1;
 	unsigned int blackberry:1;
 	short alignment_samples[4];
 	int alignment_count;
-	char sms_txt[160];
 	struct ast_dsp *dsp;
-	char hangup_count;
+
+	/* flags */
+	unsigned int outgoing:1;	/*!< outgoing call */
+	unsigned int incoming:1;	/*!< incoming call */
+	unsigned int outgoing_sms:1;	/*!< outgoing sms */
+	unsigned int incoming_sms:1;	/*!< outgoing sms */
+	unsigned int needcallerid:1;	/*!< we need callerid */
+	unsigned int needchup:1;	/*!< we need to send a chup */
+	unsigned int sent_answer:1;	/*!< we sent an answer */
+	unsigned int connected:1;	/*!< do we have an rfcomm connection to a device */
+
 	AST_LIST_ENTRY(mbl_pvt) entry;
 };
 
 static AST_RWLIST_HEAD_STATIC(devices, mbl_pvt);
+
+static int handle_response_ok(struct mbl_pvt *pvt, char *buf);
+static int handle_response_error(struct mbl_pvt *pvt, char *buf);
+static int handle_response_ciev(struct mbl_pvt *pvt, char *buf);
+static int handle_response_clip(struct mbl_pvt *pvt, char *buf);
+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_sms_prompt(struct mbl_pvt *pvt, char *buf);
 
 /* CLI stuff */
 static char *handle_cli_mobile_show_devices(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a);
@@ -323,7 +324,6 @@
 	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 */
@@ -395,9 +395,11 @@
  * Hayes AT command helpers
  */
 typedef enum {
+	/* errors */
 	AT_PARSE_ERROR = -2,
 	AT_READ_ERROR = -1,
 	AT_UNKNOWN = 0,
+	/* at responses */
 	AT_OK,
 	AT_ERROR,
 	AT_RING,
@@ -407,16 +409,37 @@
 	AT_CLIP,
 	AT_CMTI,
 	AT_CMGR,
+	AT_SMS_PROMPT,
+	/* at commands */
+	AT_A,
+	AT_D,
+	AT_CHUP,
 	AT_CKPD,
+	AT_CMGS,
 	AT_VGM,
 	AT_VGS,
-	AT_SMS_PROMPT,
+	AT_VTS,
 } at_message_t;
 
 static int at_match_prefix(char *buf, char *prefix);
 static at_message_t at_read_full(int rsock, char *buf, size_t count);
 static at_message_t at_read(int rsock);
-
+static inline const char *at_msg2str(at_message_t msg);
+
+struct msg_queue_entry {
+	at_message_t expected;
+	at_message_t response_to;
+	void *data;
+
+	AST_LIST_ENTRY(msg_queue_entry) entry;
+};
+
+static int msg_queue_push(struct mbl_pvt *pvt, at_message_t expect, at_message_t response_to);
+static int msg_queue_push_data(struct mbl_pvt *pvt, at_message_t expect, at_message_t response_to, void *data);
+static struct msg_queue_entry *msg_queue_pop(struct mbl_pvt *pvt);
+static void msg_queue_free_and_pop(struct mbl_pvt *pvt);
+static void msg_queue_flush(struct mbl_pvt *pvt);
+static struct msg_queue_entry *msg_queue_head(struct mbl_pvt *pvt);
 
 /*
  * channel stuff
@@ -464,11 +487,19 @@
 	ast_cli(a->fd, FORMAT1, "ID", "Address", "Group", "Adapter", "Connected", "State", "SMS");
 	AST_RWLIST_RDLOCK(&devices);
 	AST_RWLIST_TRAVERSE(&devices, pvt, entry) {
+		ast_mutex_lock(&pvt->lock);
 		ba2str(&pvt->addr, bdaddr);
-		snprintf(group, 5, "%d", pvt->group);
-		ast_cli(a->fd, FORMAT1, pvt->id, bdaddr, group, pvt->adapter->id, pvt->connected ? "Yes" : "No",
-			(pvt->state == MBL_STATE_IDLE) ? "Free" : (pvt->state < MBL_STATE_IDLE) ? "Init" : "Busy",
-			(pvt->has_sms) ? "Yes" : "No");
+		snprintf(group, sizeof(group), "%d", pvt->group);
+		ast_cli(a->fd, FORMAT1,
+				pvt->id,
+				bdaddr,
+				group,
+				pvt->adapter->id,
+				pvt->connected ? "Yes" : "No",
+				(!pvt->connected) ? "None" : (pvt->owner) ? "Busy" : (pvt->outgoing_sms || pvt->incoming_sms) ? "SMS" : "Free",
+				(pvt->has_sms) ? "Yes" : "No"
+		       );
+		ast_mutex_unlock(&pvt->lock);
 	}
 	AST_RWLIST_UNLOCK(&devices);
 
@@ -574,14 +605,24 @@
 	}
 	AST_RWLIST_UNLOCK(&devices);
 
-	if (!pvt || !pvt->connected) {
+	if (!pvt) {
 		ast_cli(a->fd, "Device %s not found.\n", a->argv[2]);
-		return CLI_SUCCESS;
+		goto e_return;
+	}
+
+	ast_mutex_lock(&pvt->lock);
+	if (!pvt->connected) {
+		ast_cli(a->fd, "Device %s not connected.\n", a->argv[2]);
+		goto e_unlock_pvt;
 	}
 
 	snprintf(buf, sizeof(buf), "%s\r", a->argv[3]);
 	rfcomm_write(pvt->rfcomm_socket, buf);
-
+	msg_queue_push(pvt, AT_OK, AT_UNKNOWN);
+
+e_unlock_pvt:
+	ast_mutex_unlock(&pvt->lock);
+e_return:
 	return CLI_SUCCESS;
 }
 
@@ -624,10 +665,12 @@
 	AST_RWLIST_UNLOCK(&devices);
 
 	if (pvt) {
+		ast_mutex_lock(&pvt->lock);
 		if (pvt->connected)
 			stat = 2;
 		if (pvt->owner)
 			stat = 3;
+		ast_mutex_unlock(&pvt->lock);
 	}
 
 	snprintf(status, sizeof(status), "%d", stat);
@@ -641,7 +684,7 @@
 {
 
 	struct mbl_pvt *pvt;
-	char *parse;
+	char *parse, *message;
 
 	AST_DECLARE_APP_ARGS(args,
 		AST_APP_ARG(device);
@@ -680,30 +723,39 @@
 
 	if (!pvt) {
 		ast_log(LOG_ERROR,"Bluetooth device %s wasn't found in the list -- SMS will not be sent.\n", args.device);
-		return -1;
-	}
-
+		goto e_return;
+	}
+
+	ast_mutex_lock(&pvt->lock);
 	if (!pvt->connected) {
 		ast_log(LOG_ERROR,"Bluetooth device %s wasn't connected -- SMS will not be sent.\n", args.device);
-		return -1;
+		goto e_unlock_pvt;
 	}
 
 	if (!pvt->has_sms) {
 		ast_log(LOG_ERROR,"Bluetooth device %s doesn't handle SMS -- SMS will not be sent.\n", args.device);
-		return -1;
-	}
-
-	if (pvt->state != MBL_STATE_IDLE) {
-		ast_log(LOG_ERROR,"Bluetooth device %s isn't IDLE -- SMS will not be sent.\n", args.device);
-		return -1;
-	}
-
-	ast_copy_string(pvt->dial_number, args.dest, sizeof(pvt->dial_number));
-	ast_copy_string(pvt->sms_txt, args.message, sizeof(pvt->sms_txt));
-	pvt->state = MBL_STATE_OUTSMS;
+		goto e_unlock_pvt;
+	}
+
+	message = ast_strdup(args.message);
+
+	if (hfp_send_cmgs(pvt->hfp, args.dest)
+		|| msg_queue_push_data(pvt, AT_SMS_PROMPT, AT_CMGS, message)) {
+
+		ast_log(LOG_ERROR, "[%s] problem sending SMS message\n", pvt->id);
+		goto e_free_message;
+	}
+
+	ast_mutex_unlock(&pvt->lock);
 
 	return 0;
 
+e_free_message:
+	ast_free(message);
+e_unlock_pvt:
+	ast_mutex_unlock(&pvt->lock);
+e_return:
+	return -1;
 }
 
 /*
@@ -718,13 +770,10 @@
 	struct ast_channel *chn;
 
 	if (pipe(pvt->io_pipe) == -1) {
-		ast_log(LOG_ERROR, "Failed to create io_pipe.\n");
-		return NULL;
-	}
-
-	if (pvt->sco_socket != -1)
-		close(pvt->sco_socket);
-	pvt->sco_socket = -1;
+		ast_log(LOG_ERROR, "[%s] failed to create io_pipe for audio data\n", pvt->id);
+		goto e_return;
+	}
+
 	pvt->sent_answer = 0;
 	pvt->alignment_count = 0;
 	pvt->alignment_detection_triggered = 0;
@@ -732,30 +781,37 @@
 		pvt->do_alignment_detection = 1;
 	else
 		pvt->do_alignment_detection = 0;
-	pvt->do_hangup = 1;
 
 	ast_smoother_reset(pvt->smoother, DEVICE_FRAME_SIZE);
 	ast_dsp_digitreset(pvt->dsp);
 
 	chn = ast_channel_alloc(1, state, cid_num, pvt->id, 0, 0, pvt->context, 0, "Mobile/%s-%04lx", pvt->id, ast_random() & 0xffff);
-	if (chn) {
-		chn->tech = &mbl_tech;
-		chn->nativeformats = prefformat;
-		chn->rawreadformat = prefformat;
-		chn->rawwriteformat = prefformat;
-		chn->writeformat = prefformat;
-		chn->readformat = prefformat;
-		chn->tech_pvt = pvt;
-		ast_channel_set_fd(chn, 0, pvt->io_pipe[0]);
-		if (state == AST_STATE_RING)
-			chn->rings = 1;
-		ast_string_field_set(chn, language, "en");
-		pvt->owner = chn;
-		return chn;
-	}
-
+	if (!chn) {
+		goto e_close_pipe;
+	}
+
+	chn->tech = &mbl_tech;
+	chn->nativeformats = prefformat;
+	chn->rawreadformat = prefformat;
+	chn->rawwriteformat = prefformat;
+	chn->writeformat = prefformat;
+	chn->readformat = prefformat;
+	chn->tech_pvt = pvt;
+	ast_channel_set_fd(chn, 0, pvt->io_pipe[0]);
+
+	if (state == AST_STATE_RING)
+		chn->rings = 1;
+
+	ast_string_field_set(chn, language, "en");
+	pvt->owner = chn;
+
+	return chn;
+
+e_close_pipe:
+	close(pvt->io_pipe[0]);
+	close(pvt->io_pipe[1]);
+e_return:
 	return NULL;
-
 }
 
 static struct ast_channel *mbl_request(const char *type, int format, void *data, int *cause)
@@ -813,7 +869,9 @@
 		return NULL;
 	}
 
+	ast_mutex_lock(&pvt->lock);
 	chn = mbl_new(AST_STATE_DOWN, pvt, NULL);
+	ast_mutex_unlock(&pvt->lock);
 	if (!chn) {
 		ast_log(LOG_WARNING, "Unable to allocate channel structure.\n");
 		*cause = AST_CAUSE_REQUESTED_CHAN_UNAVAIL;
@@ -852,9 +910,14 @@
 	ast_debug(1, "Calling %s on %s\n", dest, ast->name);
 
 	if (pvt->type == MBL_TYPE_PHONE) {
-		ast_copy_string(pvt->dial_number, dest_num, sizeof(pvt->dial_number));
-		pvt->state = MBL_STATE_DIAL;
-		pvt->dial_timeout = (timeout == 0) ? 30 : timeout;
+		ast_mutex_lock(&pvt->lock);
+		if (hfp_send_atd(pvt->hfp, dest_num)) {
+			ast_mutex_unlock(&pvt->lock);
+			ast_log(LOG_ERROR, "error sending ATD command on %s\n", pvt->id);
+			return -1;
+		}
+		msg_queue_push(pvt, AT_OK, AT_D);
+		ast_mutex_unlock(&pvt->lock);
 	} else {
 		pvt->state = MBL_STATE_RING;
 	}
@@ -874,25 +937,28 @@
 	}
 	pvt = ast->tech_pvt;
 
-	ast_debug(1, "Hanging up device %s.\n", pvt->id);
+	ast_debug(1, "[%s] hanging up device\n", pvt->id);
 
 	ast_channel_set_fd(ast, 0, -1);
+
+	ast_mutex_lock(&pvt->lock);
 	close(pvt->io_pipe[0]);
 	close(pvt->io_pipe[1]);
 
 	close(pvt->sco_socket);
-
-	if ((pvt->state == MBL_STATE_INCOMING || pvt->state == MBL_STATE_OUTGOING || pvt->state == MBL_STATE_DIAL1 || pvt->state == MBL_STATE_RING3) && pvt->type == MBL_TYPE_PHONE) {
-		if (pvt->do_hangup) {
-			hfp_send_chup(pvt->hfp);
-		}
-		pvt->state = MBL_STATE_HANGUP;
-		pvt->hangup_count = 0;
-	} else
-		pvt->state = MBL_STATE_IDLE;
+	pvt->sco_socket = -1;
+
+	if (pvt->needchup) {
+		hfp_send_chup(pvt->hfp);
+		msg_queue_push(pvt, AT_OK, AT_CHUP);
+		pvt->needchup = 0;
+	}
 
 	pvt->owner = NULL;
 	ast->tech_pvt = NULL;
+
+	ast_mutex_unlock(&pvt->lock);
+
 	ast_setstate(ast, AST_STATE_DOWN);
 
 	return 0;
@@ -906,11 +972,13 @@
 
 	pvt = ast->tech_pvt;
 
-	hfp_send_ata(pvt->hfp);
-
-	ast_setstate(ast, AST_STATE_UP);
-
-	pvt->sent_answer = 1;
+	ast_mutex_lock(&pvt->lock);
+	if (pvt->incoming) {
+		hfp_send_ata(pvt->hfp);
+		msg_queue_push(pvt, AT_OK, AT_A);
+		pvt->sent_answer = 1;
+	}
+	ast_mutex_unlock(&pvt->lock);
 
 	return 0;
 
@@ -922,11 +990,17 @@
 
 	if (pvt->type == MBL_TYPE_HEADSET)
 		return 0;
-	
-	ast_debug(1, "Dialed %c\n", digit);
+
+	ast_mutex_lock(&pvt->lock);
 	if (hfp_send_dtmf(pvt->hfp, digit)) {
-		return -1;
-	}
+		ast_mutex_unlock(&pvt->lock);
+		ast_debug(1, "[%s] error sending digit %c\n", pvt->id, digit);
+		return -1;
+	}
+	msg_queue_push(pvt, AT_OK, AT_VTS);
+	ast_mutex_unlock(&pvt->lock);
+
+	ast_debug(1, "[%s] dialed %c\n", pvt->id, digit);
 
 	return 0;
 }
@@ -996,8 +1070,15 @@
 
 	struct mbl_pvt *pvt = oldchan->tech_pvt;
 
-	if (pvt && pvt->owner == oldchan)
+	if (!pvt) {
+		ast_debug(1, "fixup failed, no pvt on oldchan\n");
+		return -1;
+	}
+
+	ast_mutex_lock(&pvt->lock);
+	if (pvt->owner == oldchan)
 		pvt->owner = newchan;
+	ast_mutex_unlock(&pvt->lock);
 
 	return 0;
 
@@ -1021,14 +1102,17 @@
 	}
 	AST_RWLIST_UNLOCK(&devices);
 
-	if (pvt) {
-		if (pvt->connected) {
-			if (pvt->owner)
-				res = AST_DEVICE_INUSE;
-			else
-				res = AST_DEVICE_NOT_INUSE;
-		}
-	}
+	if (!pvt)
+		return res;
+
+	ast_mutex_lock(&pvt->lock);
+	if (pvt->connected) {
+		if (pvt->owner)
+			res = AST_DEVICE_INUSE;
+		else
+			res = AST_DEVICE_NOT_INUSE;
+	}
+	ast_mutex_unlock(&pvt->lock);
 
 	return res;
 
@@ -1514,8 +1598,11 @@
 	if (pvt->do_alignment_detection)
 		do_alignment_detection(pvt, buf, res);
 
-	if (pvt->owner && pvt->owner->_state == AST_STATE_UP)
-		write(pvt->io_pipe[1], buf, res);
+	if (pvt->owner && pvt->owner->_state == AST_STATE_UP) {
+		if (write(pvt->io_pipe[1], buf, res) == -1) {
+			ast_debug(1, "[%s] failed to queue sco data: %s", pvt->id, strerror_r(errno, buf, sizeof(buf)));
+		}
+	}
 
 	return 1;
 }
@@ -1696,6 +1783,63 @@
 	return at_read_full(rsock, buf, sizeof(buf));
 }
 
+/*!
+ * \brief Get the string representation of the given AT message.
+ * \param msg the message to process
+ * \return a string describing the given message
+ */
+static inline const char *at_msg2str(at_message_t msg)
+{
+	switch (msg) {
+	/* errors */
+	case AT_PARSE_ERROR:
+		return "PARSE ERROR";
+	case AT_READ_ERROR:
+		return "READ ERROR";
+	default:
+	case AT_UNKNOWN:
+		return "UNKNOWN";
+	/* at responses */
+	case AT_OK:
+		return "OK";
+	case AT_ERROR:
+		return "ERROR";
+	case AT_RING:
+		return "RING";
+	case AT_BRSF:
+		return "AT+BRSF";
+	case AT_CIND:
+		return "AT+CIND";
+	case AT_CIEV:
+		return "AT+CIEV";
+	case AT_CLIP:
+		return "AT+CLIP";
+	case AT_CMTI:
+		return "AT+CMTI";
+	case AT_CMGR:
+		return "AT+CMGR";
+	case AT_CMGS:
+		return "AT+CMGS";
+	case AT_SMS_PROMPT:
+		return "SMS PROMPT";
+	/* at commands */
+	case AT_A:
+		return "ATA";
+	case AT_D:
+		return "ATD";
+	case AT_CHUP:
+		return "AT+CHUP";
+	case AT_CKPD:
+		return "AT+CKPD";
+	case AT_VGM:
+		return "AT+VGM";
+	case AT_VGS:
+		return "AT+VGS";
+	case AT_VTS:
+		return "AT+VTS";
+	}
+}
+
 
 /*
  * bluetooth handsfree profile helpers
@@ -1773,7 +1917,6 @@
 	if (hfp_init_sms(pvt)) {
 		ast_debug(1, "[%s] no SMS support\n", pvt->owner->id);
 	} else {
-		pvt->have_sms = 1;
 		pvt->owner->has_sms = 1;
 	}
 
@@ -2482,6 +2625,100 @@
 	return rfcomm_write(rsock, "\r\nRING\r\n");
 }
 
+/*
+ * message queue functions
+ */
+
+/*!
+ * \brief Add an item to the back of the queue.
+ * \param pvt a mbl_pvt structure
+ * \param expect the msg we expect to recieve
+ * \param response_to the message that was sent to generate the expected
+ * response
+ */
+static int msg_queue_push(struct mbl_pvt *pvt, at_message_t expect, at_message_t response_to)
+{
+	struct msg_queue_entry *msg;
+	if (!(msg = ast_malloc(sizeof(*msg)))) {
+		return -1;
+	}
+	msg->expected = expect;
+	msg->response_to = response_to;
+	msg->data = NULL;
+
+	AST_LIST_INSERT_TAIL(&pvt->msg_queue, msg, entry);
+	return 0;
+}
+
+/*!
+ * \brief Add an item to the back of the queue with data.
+ * \param pvt a mbl_pvt structure
+ * \param expect the msg we expect to recieve
+ * \param response_to the message that was sent to generate the expected
+ * response
+ * \param data data associated with this message, it will be freed when the
+ * message is freed
+ */
+static int msg_queue_push_data(struct mbl_pvt *pvt, at_message_t expect, at_message_t response_to, void *data)
+{
+	struct msg_queue_entry *msg;
+	if (!(msg = ast_malloc(sizeof(*msg)))) {
+		return -1;
+	}
+	msg->expected = expect;
+	msg->response_to = response_to;
+	msg->data = data;
+
+	AST_LIST_INSERT_TAIL(&pvt->msg_queue, msg, entry);
+	return 0;
+}
+
+/*!
+ * \brief Remove an item from the front of the queue.
+ * \param pvt a mbl_pvt structure
+ * \return a pointer to the removed item
+ */
+static struct msg_queue_entry *msg_queue_pop(struct mbl_pvt *pvt)
+{
+	return AST_LIST_REMOVE_HEAD(&pvt->msg_queue, entry);
+}
+
+/*!
+ * \brief Remove an item from the front of the queue, and free it.
+ * \param pvt a mbl_pvt structure
+ */
+static void msg_queue_free_and_pop(struct mbl_pvt *pvt)
+{
+	struct msg_queue_entry *msg;
+	if ((msg = msg_queue_pop(pvt))) {
+		if (msg->data)
+			ast_free(msg->data);
+		ast_free(msg);
+	}
+}
+
+/*!
+ * \brief Remove all itmes from the queue and free them.
+ * \param pvt a mbl_pvt structure
+ */
+static void msg_queue_flush(struct mbl_pvt *pvt)
+{
+	struct msg_queue_entry *msg;
+	while ((msg = msg_queue_head(pvt)))
+		msg_queue_free_and_pop(pvt);
+}
+
+/*!
+ * \brief Get the head of a queue.
+ * \param pvt a mbl_pvt structure
+ * \return a pointer to the head of the given msg queue
+ */
+static struct msg_queue_entry *msg_queue_head(struct mbl_pvt *pvt)
+{
+	return pvt->msg_queue.first;
+}
+
+
 
 /*
 
@@ -2605,282 +2842,478 @@
 
 */
 
+/*!
+ * \brief Handle OK AT 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_ok(struct mbl_pvt *pvt, char *buf)
+{
+	struct msg_queue_entry *entry;
+	if ((entry = msg_queue_head(pvt)) && entry->expected == AT_OK) {
+		switch (entry->response_to) {
+		case AT_A:
+			ast_debug(1, "[%s] answer sent successfully\n", pvt->id);
+			pvt->needchup = 1;
+			break;
+		case AT_D:
+			ast_debug(1, "[%s] dial sent successfully\n", pvt->id);
+			pvt->needchup = 1;
+			pvt->outgoing = 1;
+			break;
+		case AT_CHUP:
+			ast_debug(1, "[%s] successful hangup\n", pvt->id);
+			break;
+		case AT_CMGR:
+			ast_debug(1, "[%s] successfully read sms message\n", pvt->id);
+			pvt->incoming_sms = 0;
+			break;
+		case AT_CMGS:
+			ast_debug(1, "[%s] successfully sent sms message\n", pvt->id);
+			pvt->outgoing_sms = 0;
+			break;
+		case AT_VTS:
+			ast_debug(1, "[%s] digit sent successfully\n", pvt->id);
+			break;
+		case AT_UNKNOWN:
+		default:
+			ast_debug(1, "[%s] recieved OK for unhandled request: %s\n", pvt->id, at_msg2str(entry->response_to));
+			break;
+		}
+		msg_queue_free_and_pop(pvt);
+	} else if (entry) {
+		ast_debug(1, "[%s] recieved AT message 'OK' when expecting %s, ignoring\n", pvt->id, at_msg2str(entry->expected));
+	} else {
+		ast_debug(1, "[%s] recieved unexpected AT message 'OK'\n", pvt->id);
+	}
+	return 0;
+}
+
+/*!
+ * \brief Handle ERROR AT 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_error(struct mbl_pvt *pvt, char *buf)
+{
+	int res = 0;
+	struct msg_queue_entry *entry;
+	if ((entry = msg_queue_head(pvt))
+			&& (entry->expected == AT_OK
+			|| entry->expected == AT_ERROR
+			|| entry->expected == AT_SMS_PROMPT)) {
+		switch (entry->response_to) {
+		case AT_A:
+			ast_debug(1, "[%s] answer failed\n", pvt->id);
+			ast_queue_hangup(pvt->owner);
+			break;
+		case AT_D:
+			ast_debug(1, "[%s] dial failed\n", pvt->id);
+			ast_queue_control(pvt->owner, AST_CONTROL_CONGESTION);
+			break;
+		case AT_CHUP:
+			ast_debug(1, "[%s] error sending hangup, disconnecting\n", pvt->id);
+			res = -1;
+			break;
+		case AT_CMGR:
+			ast_debug(1, "[%s] error reading sms message\n", pvt->id);
+			pvt->incoming_sms = 0;
+			break;
+		case AT_CMGS:
+			ast_debug(1, "[%s] error sending sms message\n", pvt->id);
+			pvt->outgoing_sms = 0;
+			break;
+		case AT_VTS:
+			ast_debug(1, "[%s] error sending digit\n", pvt->id);
+			break;
+		case AT_UNKNOWN:
+		default:
+			ast_debug(1, "[%s] recieved ERROR for unhandled request: %s\n", pvt->id, at_msg2str(entry->response_to));
+			break;
+		}
+		msg_queue_free_and_pop(pvt);
+	} else if (entry) {
+		ast_debug(1, "[%s] recieved AT message 'ERROR' when expecting %s, ignoring\n", pvt->id, at_msg2str(entry->expected));
+	} else {
+		ast_debug(1, "[%s] recieved unexpected AT message 'ERROR'\n", pvt->id);
+	}
+
+	return res;
+}
+
+/*!
+ * \brief Handle AT+CIEV 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_ciev(struct mbl_pvt *pvt, char *buf)
+{
+	int i;
+	switch (hfp_parse_ciev(pvt->hfp, buf, &i)) {
+	case HFP_CIND_CALL:
+		switch (i) {
+		case HFP_CIND_CALL_NONE:
+			if (pvt->owner) {
+				if (ast_queue_hangup(pvt->owner)) {
+					ast_log(LOG_ERROR, "[%s] error queueing hangup, disconnectiong...\n", pvt->id);
+					return -1;
+				}
+			}
+			pvt->needchup = 0;
+			pvt->needcallerid = 0;
+			pvt->incoming = 0;
+			pvt->outgoing = 0;
+			break;
+		case HFP_CIND_CALL_ACTIVE:
+			if (pvt->outgoing) {
+				ast_debug(1, "[%s] remote end answered\n", pvt->id);
+				ast_queue_control(pvt->owner, AST_CONTROL_ANSWER);
+			} else if (pvt->incoming && pvt->sent_answer) {
+				ast_setstate(pvt->owner, AST_STATE_UP);
+			} else if (pvt->incoming) {
+				/* user answered from handset, disconnecting */
+				ast_verb(3, "[%s] user answered bluetooth device from handset, disconnecting\n", pvt->id);
+				ast_queue_hangup(pvt->owner);
+				return -1;
+			}
+			break;
+		}
+		break;
+
+	case HFP_CIND_CALLSETUP:
+		switch (i) {
+		case HFP_CIND_CALLSETUP_NONE:
+			if (pvt->hfp->cind_state[pvt->hfp->cind_map.call] != HFP_CIND_CALL_ACTIVE) {
+				if (pvt->owner) {
+					if (ast_queue_hangup(pvt->owner)) {
+						ast_log(LOG_ERROR, "[%s] error queueing hangup, disconnectiong...\n", pvt->id);
+						return -1;
+					}
+				}
+				pvt->needchup = 0;
+				pvt->needcallerid = 0;
+				pvt->incoming = 0;
+				pvt->outgoing = 0;
+			}
+			break;
+		case HFP_CIND_CALLSETUP_INCOMING:
+			ast_debug(1, "[%s] incoming call, waiting for caller id\n", pvt->id);
+			pvt->needcallerid = 1;
+			pvt->incoming = 1;
+			break;
+		case HFP_CIND_CALLSETUP_OUTGOING:
+			if (pvt->outgoing) {
+				ast_debug(1, "[%s] outgoing call\n", pvt->id);
+			} else {
+				ast_verb(3, "[%s] user dialed from handset, disconnecting\n", pvt->id);
+				return -1;
+			}
+			break;
+		case HFP_CIND_CALLSETUP_ALERTING:
+			if (pvt->outgoing) {
+				ast_debug(1, "[%s] remote alerting\n", pvt->id);
+				ast_queue_control(pvt->owner, AST_CONTROL_RINGING);
+			}
+			break;
+		}
+		break;
+	case HFP_CIND_NONE:
+		ast_debug(1, "[%s] error parsing CIND: %s\n", pvt->id, buf);
+		break;
+	}
+	return 0;
+}
+
+/*!
+ * \brief Handle AT+CLIP 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_clip(struct mbl_pvt *pvt, char *buf)
+{
+	char *clip;
+	struct msg_queue_entry *msg;
+	struct ast_channel *chan;
+
+	if ((msg = msg_queue_head(pvt)) && msg->expected == AT_CLIP) {
+		pvt->needcallerid = 0;
+		if (!(clip = hfp_parse_clip(pvt->hfp, buf))) {
+			ast_debug(1, "[%s] error parsing CLIP: %s\n", pvt->id, buf);
+		}
+
+		if (!(chan = mbl_new(AST_STATE_RING, pvt, clip))) {
+			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);
+			return -1;
+		}
+
+		if (ast_pbx_start(chan)) {
+			ast_log(LOG_ERROR, "[%s] unable to start pbx on incoming call\n", pvt->id);
+			ast_hangup(chan);
+			return -1;
+		}
+
+		msg_queue_free_and_pop(pvt);
+	}
+
+	return 0;
+}
+
+/*!
+ * \brief Handle RING 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_ring(struct mbl_pvt *pvt, char *buf)
+{
+	if (pvt->needcallerid) {
+		ast_debug(1, "[%s] got ring while waiting for caller id\n", pvt->id);
+		return msg_queue_push(pvt, AT_CLIP, AT_UNKNOWN);
+	} else {
+		return 0;
+	}
+}
+
+/*!
+ * \brief Handle AT+CMTI 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_cmti(struct mbl_pvt *pvt, char *buf)
+{
+	int index = hfp_parse_cmti(pvt->hfp, buf);
+	if (index > 0) {
+		ast_debug(1, "[%s] incoming sms message\n", pvt->id);
+		hfp_send_cmgr(pvt->hfp, index);
+		pvt->incoming_sms = 1;
+		return 0;
+	} else {
+		ast_debug(1, "[%s] error parsing incoming sms message alert, disconnecting\n", pvt->id);
+		return -1;
+	}
+}
+
+/*!
+ * \brief Handle AT+CMGR 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_cmgr(struct mbl_pvt *pvt, char *buf)
+{
+	char *from_number, *text;
+	struct ast_channel *chan;
+
+	if (hfp_parse_cmgr(pvt->hfp, buf, &from_number, &text)
+		|| msg_queue_push(pvt, AT_OK, AT_CMGR)) {
+
+		ast_debug(1, "[%s] error parsing sms message, disconnecting\n", pvt->id);
+		return -1;
+	}
+
+	/* XXX this channel probably does not need to be associated with this pvt */
+	if (!(chan = mbl_new(AST_STATE_DOWN, pvt, NULL))) {
+		ast_debug(1, "[%s] error creating sms message channel, disconnecting\n", pvt->id);
+		return 0;
+	}
+
+	strcpy(chan->exten, "sms");
+	pbx_builtin_setvar_helper(chan, "SMSSRC", from_number);
+	pbx_builtin_setvar_helper(chan, "SMSTXT", text);
+
+	if (ast_pbx_start(chan)) {
+		ast_log(LOG_ERROR, "[%s] unable to start pbx on incoming sms\n", pvt->id);
+		ast_hangup(chan);
+	}
+
+	return 0;
+}
+
+/*!
+ * \brief Send an SMS message from the queue.
+ * \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_sms_prompt(struct mbl_pvt *pvt, char *buf)
+{
+	struct msg_queue_entry *msg;
+	if (!(msg = msg_queue_head(pvt))) {
+		ast_debug(1, "[%s] error, got sms prompt with no pending sms messages\n", pvt->id);
+		return 0;
+	}
+
+	if (msg->expected != AT_SMS_PROMPT) {
+		ast_debug(1, "[%s] error, got sms prompt but no pending sms messages\n", pvt->id);
+		return 0;
+	}
+
+	if (hfp_send_sms_text(pvt->hfp, msg->data)
+			|| msg_queue_push(pvt, AT_OK, AT_CMGS)) {
+		msg_queue_free_and_pop(pvt);
+		ast_debug(1, "[%s] error sending sms message\n", pvt->id);
+		return 0;
+	}
+
+	msg_queue_free_and_pop(pvt);
+	return 0;
+}
+
+
 static void *do_monitor_phone(void *data)
 {
-
 	struct mbl_pvt *pvt = (struct mbl_pvt *)data;
 	struct hfp_pvt *hfp = pvt->hfp;
-	struct ast_channel *chn;
 	char buf[256];
-	char cid_num[AST_MAX_EXTENSION], *clip;
-	int t, i, smsi;
-	char brsf, nsmode;
-	char *sms_src, *sms_txt;
+	int t;
 	at_message_t at_msg;
 
-	brsf = nsmode = 0;
-
+	ast_mutex_lock(&pvt->lock);
 	if (hfp_init(hfp)) {
+		ast_mutex_unlock(&pvt->lock);
 		ast_verb(3, "Error initializing Bluetooth device %s.\n", pvt->id);
 		goto e_cleanup;
-	} else {
-		pvt->state = MBL_STATE_PREIDLE;
-	}
+	}
+	ast_mutex_unlock(&pvt->lock);
+
+	ast_verb(3, "Bluetooth Device %s initialised and ready.\n", pvt->id);
 
 	while (!check_unloading()) {
-
-		if (pvt->state == MBL_STATE_DIAL1)
-			t = pvt->dial_timeout * 1000;
-		else if (pvt->state == MBL_STATE_HANGUP)
-			t = 2000;
-		else if (pvt->state == MBL_STATE_OUTSMS1)
-			t = 2000;
-		else if (pvt->state == MBL_STATE_OUTSMS2)
-			t = 10000;
-		else
-			t = 1000;
-
-		if (rfcomm_wait(pvt->rfcomm_socket, &t)) {
-			if ((at_msg = at_read_full(hfp->rsock, 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); 
-
-				break;
+		ast_mutex_lock(&pvt->lock);
+		t = pvt->timeout;
+		ast_mutex_unlock(&pvt->lock);
+
+		if (!rfcomm_wait(pvt->rfcomm_socket, &t)) {
+			ast_debug(1, "[%s] timeout waiting for rfcomm data, disconnecting\n", pvt->id);
+			goto e_cleanup;
+		}
+
+		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);
+			break;
+		}
+
+		ast_debug(1, "[%s] %s\n", pvt->id, buf);
+
+		switch (at_msg) {
+		case AT_OK:
+			ast_mutex_lock(&pvt->lock);
+			if (handle_response_ok(pvt, buf)) {
+				ast_mutex_unlock(&pvt->lock);
+				goto e_cleanup;
 			}
-			ast_debug(1, "[%s] %s\n", pvt->id, buf);
-
-			switch (pvt->state) {
-			case MBL_STATE_PREIDLE: /* Nothing handled here, wait for timeout, then off we go... */
-				break;
-			case MBL_STATE_IDLE:
-				if (at_msg == AT_RING) {
-					pvt->state = MBL_STATE_RING;
-				} else if (at_msg == AT_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...
-					 */
-					ast_verb(3, "Disconnecting bluetooth device %s, user has initated a call from the handset.\n", pvt->id); 
-					goto e_cleanup;
-				}
-				break;
-			case MBL_STATE_DIAL: /* Nothing handled here, we need to wait for a timeout */
-				break;
-			case MBL_STATE_DIAL1:
-				if (strstr(buf, "OK")) {
-					if (pvt->no_callsetup) {
-						ast_queue_control(pvt->owner, AST_CONTROL_ANSWER);
-					} else {
-						ast_setstate(pvt->owner, AST_STATE_RINGING);
-					}
-					pvt->state = MBL_STATE_OUTGOING;
-				}
-				break;
-			case MBL_STATE_OUTGOING:
-				if (at_msg == AT_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;
-			case MBL_STATE_RING:
-				cid_num[0] = 0x00;
-				if (at_msg == AT_CLIP) {
-					if ((clip = hfp_parse_clip(hfp, buf))) {
-						ast_copy_string(cid_num, clip, sizeof(cid_num));
-					}
-					chn = mbl_new(AST_STATE_RING, pvt, cid_num);
-					if (chn) {
-						if (ast_pbx_start(chn)) {
-							ast_log(LOG_ERROR, "Unable to start pbx on incoming call.\n");
-							ast_hangup(chn);
-						} else
-							pvt->state = MBL_STATE_RING3;
-					} else {
-						ast_log(LOG_ERROR, "Unable to allocate channel for incoming call.\n");
-						hfp_send_chup(hfp);
-						pvt->state = MBL_STATE_IDLE;
-					}
-				}
-				break;
-			case MBL_STATE_RING2:
-				chn = mbl_new(AST_STATE_RING, pvt, cid_num);
-				if (chn) {
-					if (ast_pbx_start(chn)) {
-						ast_log(LOG_ERROR, "Unable to start pbx on incoming call.\n");
-						ast_hangup(chn);
-					} else
-						pvt->state = MBL_STATE_RING3;
-				} else {
-					ast_log(LOG_ERROR, "Unable to allocate channel for incoming call.\n");
-					hfp_send_chup(hfp);
-					pvt->state = MBL_STATE_IDLE;
-				}
-				break;
-			case MBL_STATE_RING3:
-				if (at_msg == AT_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);
-						}
-						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 (at_msg == AT_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 (at_msg == AT_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;
-				}
-				break;
-			case MBL_STATE_INSMS:
-				if (at_msg == AT_CMGR) {
-					hfp_parse_cmgr(hfp, buf, &sms_src, &sms_txt);
-				} else if (at_msg == AT_OK) {
-					chn = mbl_new(AST_STATE_DOWN, pvt, NULL);
-					strcpy(chn->exten, "sms");
-					pbx_builtin_setvar_helper(chn, "SMSSRC", sms_src);
-					pbx_builtin_setvar_helper(chn, "SMSTXT", sms_txt);
-					if (ast_pbx_start(chn))
-						ast_log(LOG_ERROR, "Unable to start pbx on incoming sms.\n");
-					pvt->state = MBL_STATE_IDLE;
-				}				break;
-			case MBL_STATE_OUTSMS:
-				break;
-			case MBL_STATE_OUTSMS1:
-				break;
-			case MBL_STATE_OUTSMS2:
-				if (strstr(buf, "OK")) {
-					pvt->state = MBL_STATE_IDLE;
-				}
-				break;
+			ast_mutex_unlock(&pvt->lock);
+			break;
+		case AT_ERROR:
+			ast_mutex_lock(&pvt->lock);
+			if (handle_response_error(pvt, buf)) {
+				ast_mutex_unlock(&pvt->lock);
+				goto e_cleanup;
 			}
-			/* Unsolicited responses */
-
- 			if (at_msg == AT_CMTI) {	/* SMS Incoming... */
-				if ((smsi = hfp_parse_cmti(hfp, buf)) > 0) {
-					hfp_send_cmgr(hfp, smsi);
-					pvt->state = MBL_STATE_INSMS;
-				}
+			ast_mutex_unlock(&pvt->lock);
+			break;
+		case AT_RING:
+			ast_mutex_lock(&pvt->lock);
+			if (handle_response_ring(pvt, buf)) {
+				ast_mutex_unlock(&pvt->lock);
+				goto e_cleanup;
 			}
-
-		} else { /* Timeouts */
-			if (pvt->state == MBL_STATE_INIT2) { /* Some devices dont respond to AT+CIND? properly. RIM Blackberry 4 example */
-				pvt->state++;
-				rfcomm_write(pvt->rfcomm_socket, "AT+CMER=3,0,0,1\r");
- 			} else if (pvt->state == MBL_STATE_INIT3) { /* Some devices dont respond to AT+CMER=3,0,0,1 properly. VK 2020 for example */
-				pvt->state++;
-				rfcomm_write(pvt->rfcomm_socket, "AT+CLIP=1\r");
-			} else if (pvt->state == MBL_STATE_PREIDLE) {
-				pvt->connected = 1;
-				ast_verb(3, "Bluetooth Device %s initialised and ready.\n", pvt->id);
-				pvt->state = MBL_STATE_IDLE;
-			} else if (pvt->state == MBL_STATE_DIAL) {
-				if (hfp_send_atd(hfp, pvt->dial_number)) {
-					ast_log(LOG_ERROR, "Dial failed on %s state %d\n", pvt->owner->name, pvt->state);
-					ast_queue_control(pvt->owner, AST_CONTROL_CONGESTION);
-					pvt->state = MBL_STATE_IDLE;
-				} else {
-					pvt->state = MBL_STATE_DIAL1;
-				}
-			} else if (pvt->state == MBL_STATE_DIAL1) {
-				ast_log(LOG_ERROR, "Dial failed on %s state %d\n", pvt->owner->name, pvt->state);
-				ast_queue_control(pvt->owner, AST_CONTROL_CONGESTION);
-				ast_queue_control(pvt->owner, AST_CONTROL_HANGUP);
-				pvt->state = MBL_STATE_IDLE;
-			} else if (pvt->state == MBL_STATE_RING) { /* No CLIP?, bump it */
-				pvt->state = MBL_STATE_RING2;
-			} else if (pvt->state == MBL_STATE_HANGUP) {
-				if (pvt->do_hangup) {
-					if (pvt->hangup_count == 6) {
-						ast_debug(1, "Device %s failed to hangup after 6 tries, disconnecting.\n", pvt->id);
-						break;
-					}
-					hfp_send_chup(hfp);
-					pvt->hangup_count++;
-				} else
-					pvt->state = MBL_STATE_IDLE;
-			} else if (pvt->state == MBL_STATE_OUTSMS) {
-				hfp_send_cmgs(hfp, pvt->dial_number);
-				pvt->state = MBL_STATE_OUTSMS1;
-			} else if (pvt->state == MBL_STATE_OUTSMS1) {
-				if (!strcmp(buf, "> ")) {
-					hfp_send_sms_text(hfp, pvt->sms_txt);
-					pvt->state = MBL_STATE_OUTSMS2;
-				} else {
-					ast_log(LOG_ERROR, "Failed to send SMS to %s on device %s\n", pvt->dial_number, pvt->id);
-					pvt->state = MBL_STATE_IDLE;
-				}
-			} else if (pvt->state == MBL_STATE_OUTSMS2) {
-				ast_log(LOG_ERROR, "Failed to send SMS to %s on device %s\n", pvt->dial_number, pvt->id);
-				pvt->state = MBL_STATE_IDLE;
+			ast_mutex_unlock(&pvt->lock);
+			break;
+		case AT_CIEV:
+			ast_mutex_lock(&pvt->lock);
+			if (handle_response_ciev(pvt, buf)) {
+				ast_mutex_unlock(&pvt->lock);
+				goto e_cleanup;
 			}
+			ast_mutex_unlock(&pvt->lock);
+			break;
+		case AT_CLIP:
+			ast_mutex_lock(&pvt->lock);
+			if (handle_response_clip(pvt, buf)) {
+				ast_mutex_unlock(&pvt->lock);
+				goto e_cleanup;
+			}
+			ast_mutex_unlock(&pvt->lock);
+			break;
+		case AT_CMTI:
+			ast_mutex_lock(&pvt->lock);
+			if (handle_response_cmti(pvt, buf)) {
+				ast_mutex_unlock(&pvt->lock);
+				goto e_cleanup;
+			}
+			ast_mutex_unlock(&pvt->lock);
+			break;
+		case AT_CMGR:
+			ast_mutex_lock(&pvt->lock);
+			if (handle_response_cmgr(pvt, buf)) {
+				ast_mutex_unlock(&pvt->lock);
+				goto e_cleanup;
+			}
+			ast_mutex_unlock(&pvt->lock);
+			break;
+		case AT_SMS_PROMPT:
+			ast_mutex_lock(&pvt->lock);
+			if (handle_sms_prompt(pvt, buf)) {
+				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);
+			goto e_cleanup;
+		case AT_PARSE_ERROR:
+			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);
+			goto e_cleanup;
 		}
 	}
 
 e_cleanup:
+
+	ast_mutex_lock(&pvt->lock);
+	if (pvt->owner) {
+		ast_debug(1, "[%s] device disconnected, hanging up owner\n", pvt->id);
+		pvt->needchup = 0;
+		ast_queue_hangup(pvt->owner);
+	}
 
 	close(pvt->rfcomm_socket);
 	close(pvt->sco_socket);
 	pvt->sco_socket = -1;
 
+	msg_queue_flush(pvt);
+
 	pvt->connected = 0;
+
+	pvt->adapter->inuse = 0;
+	ast_mutex_unlock(&pvt->lock);
 

[... 55 lines stripped ...]



More information about the svn-commits mailing list