[svn-commits] mnicholson: trunk r815 - /trunk/channels/chan_mobile.c

SVN commits to the Digium repositories svn-commits at lists.digium.com
Wed Mar 18 16:53:40 CDT 2009


Author: mnicholson
Date: Wed Mar 18 16:53:37 2009
New Revision: 815

URL: http://svn.digium.com/svn-view/asterisk-addons?view=rev&rev=815
Log:
Merged in changes from the chan_mobile refactor branch.

With these changes the code should be easier to read and maintain, mobile
connections should be more stable, and audio quality should be much better.

Modified:
    trunk/channels/chan_mobile.c

Modified: trunk/channels/chan_mobile.c
URL: http://svn.digium.com/svn-view/asterisk-addons/trunk/channels/chan_mobile.c?view=diff&rev=815&r1=814&r2=815
==============================================================================
--- trunk/channels/chan_mobile.c (original)
+++ trunk/channels/chan_mobile.c Wed Mar 18 16:53:37 2009
@@ -69,6 +69,7 @@
 #include <asterisk/dsp.h>
 #include <asterisk/app.h>
 #include <asterisk/manager.h>
+#include <asterisk/io.h>
 
 #define MBL_CONFIG "mobile.conf"
 
@@ -82,34 +83,14 @@
 static pthread_t discovery_thread = AST_PTHREADT_NULL;	/* The discovery thread */
 static sdp_session_t *sdp_session;
 
+AST_MUTEX_DEFINE_STATIC(unload_mutex);
+static int unloading_flag = 0;
+static inline int check_unloading();
+static inline void set_unloading();
+
 enum mbl_type {
 	MBL_TYPE_PHONE,
 	MBL_TYPE_HEADSET
-};
-
-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 {
@@ -119,60 +100,75 @@
 	bdaddr_t addr;					/* adddress of adapter */
 	unsigned int inuse:1;				/* are we in use ? */
 	unsigned int alignment_detection:1;		/* do alignment detection on this adpater? */
-	int sco_socket;
+	struct io_context *io;				/*!< io context for audio connections */
+	struct io_context *accept_io;			/*!< io context for sco listener */
+	int *sco_id;					/*!< the io context id of the sco listener socket */
+	int sco_socket;					/*!< sco listener socket */
+	pthread_t sco_listener_thread;			/*!< sco listener thread */
 	AST_LIST_ENTRY(adapter_pvt) entry;
 };
 
 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 */
 	char rfcomm_buf[256];
 	char io_buf[CHANNEL_FRAME_SIZE + AST_FRIENDLY_OFFSET];
-	char io_save_buf[DEVICE_FRAME_SIZE];
-	int io_save_len;
 	int io_pipe[2];
+	struct ast_smoother *smoother;			/* our smoother, for making 48 byte frames */
 	int sco_socket;					/* sco socket descriptor */
-	pthread_t sco_listener_thread;			/* inbound sco listener for this device */
-	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;
-	char ciev_call_0[5];				/* dynamically built reponse strings */
-	char ciev_call_1[5];
-	char ciev_callsetup_0[5];
-	char ciev_callsetup_1[5];
-	char ciev_callsetup_2[5];
-	char ciev_callsetup_3[5];
+	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];
+	int ring_sched_id;
 	struct ast_dsp *dsp;
-	struct ast_frame *dsp_fr;
-	int dtmf_skip;
-	int skip_frames;
-	char hangup_count;
+	struct sched_context *sched;
+
+	/* 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 needring:1;	/*!< we need to send a RING */
+	unsigned int answered:1;	/*!< we sent/recieved 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);
@@ -191,7 +187,7 @@
 static char *mblstatus_desc =
 "MobileStatus(Device,Variable)\n"
 "  Device - Id of mobile device from mobile.conf\n"
-"  Variable - Variable to store status in will be 1-3.\n" 
+"  Variable - Variable to store status in will be 1-3.\n"
 "             In order, Disconnected, Connected & Free, Connected & Busy.\n";
 
 static char *app_mblsendsms = "MobileSendSMS";
@@ -207,7 +203,6 @@
 static int mbl_call(struct ast_channel *ast, char *dest, int timeout);
 static int mbl_hangup(struct ast_channel *ast);
 static int mbl_answer(struct ast_channel *ast);
-static int mbl_digit_begin(struct ast_channel *ast, char digit);
 static int mbl_digit_end(struct ast_channel *ast, char digit, unsigned int duration);
 static struct ast_frame *mbl_read(struct ast_channel *ast);
 static int mbl_write(struct ast_channel *ast, struct ast_frame *frame);
@@ -216,16 +211,238 @@
 
 static void do_alignment_detection(struct mbl_pvt *pvt, char *buf, int buflen);
 
+static int mbl_queue_control(struct mbl_pvt *pvt, enum ast_control_frame_type control);
+static int mbl_queue_hangup(struct mbl_pvt *pvt);
+static int mbl_ast_hangup(struct mbl_pvt *pvt);
+
 static int rfcomm_connect(bdaddr_t src, bdaddr_t dst, int remote_channel);
-static int rfcomm_write(struct mbl_pvt *pvt, char *buf);
-static int rfcomm_read(struct mbl_pvt *pvt, char *buf, char flush, int timeout);
+static int rfcomm_write(int rsock, char *buf);
+static int rfcomm_write_full(int rsock, char *buf, size_t count);
+static int rfcomm_wait(int rsock, int *ms);
+static ssize_t rfcomm_read(int rsock, char *buf, size_t count);
 
 static int sco_connect(bdaddr_t src, bdaddr_t dst);
 static int sco_write(int s, char *buf, int len);
-static int sco_read(int s, char *buf, int len);
+static int sco_read(int *id, int fd, short events, void *data);
+static int sco_accept(int *id, int fd, short events, void *data);
+static int sco_bind(struct adapter_pvt *adapter);
 
 static void *do_sco_listen(void *data);
 static int sdp_search(char *addr, int profile);
+
+static int headset_send_ring(const void *data);
+
+/*
+ * 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
+
+/*!
+ * \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 */
+	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 *hfp);
+static int hfp_init_sms(struct hfp_pvt *hfp);
+static int hfp_wait(struct hfp_pvt *hfp);
+static int hfp_parse_ciev(struct hfp_pvt *hfp, char *buf, int *value);
+static char *hfp_parse_clip(struct hfp_pvt *hfp, char *buf);
+static int hfp_parse_cmti(struct hfp_pvt *hfp, char *buf);
+static int hfp_parse_cmgr(struct hfp_pvt *hfp, char *buf, char **from_number, char **text);
+
+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 *hfp, struct hfp_hf *brsf);
+static int hfp_send_cind(struct hfp_pvt *hfp);
+static int hfp_send_cind_test(struct hfp_pvt *hfp);
+static int hfp_send_cmer(struct hfp_pvt *hfp, int status);
+static int hfp_send_clip(struct hfp_pvt *hfp, int status);
+static int hfp_send_vgs(struct hfp_pvt *hfp, int value);
+#if 0
+static int hfp_send_vgm(struct hfp_pvt *hfp, int value);
+#endif
+static int hfp_send_dtmf(struct hfp_pvt *hfp, char digit);
+static int hfp_send_cmgf(struct hfp_pvt *hfp, int mode);
+static int hfp_send_cnmi(struct hfp_pvt *hfp);
+static int hfp_send_cmgr(struct hfp_pvt *hfp, int index);
+static int hfp_send_cmgs(struct hfp_pvt *hfp, const char *number);
+static int hfp_send_sms_text(struct hfp_pvt *hfp, const char *message);
+static int hfp_send_chup(struct hfp_pvt *hfp);
+static int hfp_send_atd(struct hfp_pvt *hfp, const char *number);
+static int hfp_send_ata(struct hfp_pvt *hfp);
+
+static int hfp_read_brsf(struct hfp_pvt *hfp);
+static int hfp_read_cind(struct hfp_pvt *hfp);
+static int hfp_read_cind_test(struct hfp_pvt *hfp);
+
+/*
+ * bluetooth headset profile helpers
+ */
+static int hsp_send_ok(int rsock);
+static int hsp_send_error(int rsock);
+static int hsp_send_vgs(int rsock, int gain);
+static int hsp_send_vgm(int rsock, int gain);
+static int hsp_send_ring(int rsock);
+
+
+/*
+ * 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,
+	AT_BRSF,
+	AT_CIND,
+	AT_CIEV,
+	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_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
+ */
 
 static const struct ast_channel_tech mbl_tech = {
 	.type = "Mobile",
@@ -235,7 +452,6 @@
 	.call = mbl_call,
 	.hangup = mbl_hangup,
 	.answer = mbl_answer,
-	.send_digit_begin = mbl_digit_begin,
 	.send_digit_end = mbl_digit_end,
 	.read = mbl_read,
 	.write = mbl_write,
@@ -257,7 +473,7 @@
 	case CLI_INIT:
 		e->command = "mobile show devices";
 		e->usage =
-			"Usage: mobile show devices\n" 
+			"Usage: mobile show devices\n"
 			"       Shows the state of Bluetooth Cell / Mobile devices.\n";
 		return NULL;
 	case CLI_GENERATE:
@@ -270,11 +486,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);
 
@@ -300,7 +524,7 @@
 	case CLI_INIT:
 		e->command = "mobile search";
 		e->usage =
-			"Usage: mobile search\n" 
+			"Usage: mobile search\n"
 			"       Searches for Bluetooth Cell / Mobile devices in range.\n";
 		return NULL;
 	case CLI_GENERATE:
@@ -380,14 +604,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, buf);
-
+	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;
 }
 
@@ -430,10 +664,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);
@@ -447,7 +683,7 @@
 {
 
 	struct mbl_pvt *pvt;
-	char *parse;
+	char *parse, *message;
 
 	AST_DECLARE_APP_ARGS(args,
 		AST_APP_ARG(device);
@@ -486,30 +722,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;
 }
 
 /*
@@ -524,42 +769,48 @@
 	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;
-	pvt->io_save_len = 0;
-	pvt->sent_answer = 0;
-	pvt->skip_frames = 0;
+		ast_log(LOG_ERROR, "[%s] failed to create io_pipe for audio data\n", pvt->id);
+		goto e_return;
+	}
+
+	pvt->answered = 0;
 	pvt->alignment_count = 0;
 	pvt->alignment_detection_triggered = 0;
 	if (pvt->adapter->alignment_detection)
 		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)
@@ -617,7 +868,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;
@@ -655,13 +908,31 @@
 
 	ast_debug(1, "Calling %s on %s\n", dest, ast->name);
 
+	ast_mutex_lock(&pvt->lock);
 	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;
+		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);
 	} else {
-		pvt->state = MBL_STATE_RING;
-	}
+		if (hsp_send_ring(pvt->rfcomm_socket)) {
+			ast_log(LOG_ERROR, "[%s] error ringing device\n", pvt->id);
+			ast_mutex_unlock(&pvt->lock);
+			return -1;
+		}
+
+		if ((pvt->ring_sched_id = ast_sched_add(pvt->sched, 6000, headset_send_ring, pvt)) == -1) {
+			ast_log(LOG_ERROR, "[%s] error ringing device\n", pvt->id);
+			ast_mutex_unlock(&pvt->lock);
+			return -1;
+		}
+
+		pvt->outgoing = 1;
+		pvt->needring = 1;
+	}
+	ast_mutex_unlock(&pvt->lock);
 
 	return 0;
 
@@ -678,70 +949,1383 @@
 	}
 	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]);
 
-	if (pvt->type == MBL_TYPE_HEADSET && pvt->sco_socket != -1) {
+	close(pvt->sco_socket);
+	pvt->sco_socket = -1;
+
+	if (pvt->needchup) {
+		hfp_send_chup(pvt->hfp);
+		msg_queue_push(pvt, AT_OK, AT_CHUP);
+		pvt->needchup = 0;
+	}
+
+	pvt->outgoing = 0;
+	pvt->incoming = 0;
+	pvt->owner = NULL;
+	ast->tech_pvt = NULL;
+
+	ast_mutex_unlock(&pvt->lock);
+
+	ast_setstate(ast, AST_STATE_DOWN);
+
+	return 0;
+
+}
+
+static int mbl_answer(struct ast_channel *ast)
+{
+
+	struct mbl_pvt *pvt;
+
+	pvt = ast->tech_pvt;
+
+	if (pvt->type == MBL_TYPE_HEADSET)
+		return 0;
+
+	ast_mutex_lock(&pvt->lock);
+	if (pvt->incoming) {
+		hfp_send_ata(pvt->hfp);
+		msg_queue_push(pvt, AT_OK, AT_A);
+		pvt->answered = 1;
+	}
+	ast_mutex_unlock(&pvt->lock);
+
+	return 0;
+
+}
+
+static int mbl_digit_end(struct ast_channel *ast, char digit, unsigned int duration)
+{
+	struct mbl_pvt *pvt = ast->tech_pvt;
+
+	if (pvt->type == MBL_TYPE_HEADSET)
+		return 0;
+
+	ast_mutex_lock(&pvt->lock);
+	if (hfp_send_dtmf(pvt->hfp, digit)) {
+		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;
+}
+
+static struct ast_frame *mbl_read(struct ast_channel *ast)
+{
+
+	struct mbl_pvt *pvt = ast->tech_pvt;
+	int r;
+
+	ast_debug(3, "*** mbl_read()\n");
+
+	if (!pvt->owner) {
+		return &ast_null_frame;
+	}
+	memset(&pvt->fr, 0x00, sizeof(struct ast_frame));
+	pvt->fr.frametype = AST_FRAME_VOICE;
+	pvt->fr.subclass = DEVICE_FRAME_FORMAT;
+	pvt->fr.datalen = CHANNEL_FRAME_SIZE;
+	pvt->fr.samples = CHANNEL_FRAME_SIZE / 2;
+	pvt->fr.src = "Mobile";
+	pvt->fr.offset = AST_FRIENDLY_OFFSET;
+	pvt->fr.mallocd = 0;
+	pvt->fr.delivery.tv_sec = 0;
+	pvt->fr.delivery.tv_usec = 0;
+	pvt->fr.data.ptr = pvt->io_buf + AST_FRIENDLY_OFFSET;
+
+	if ((r = read(pvt->io_pipe[0], pvt->fr.data.ptr, CHANNEL_FRAME_SIZE)) != CHANNEL_FRAME_SIZE) {
+		if (r == -1) {
+			ast_log(LOG_ERROR, "read error %d\n", errno);
+			return &ast_null_frame;
+		} else {
+			pvt->fr.datalen = r;
+			pvt->fr.samples = r / 2;
+		}
+	}
+
+	return ast_dsp_process(ast, pvt->dsp, &pvt->fr);
+
+}
+
+static int mbl_write(struct ast_channel *ast, struct ast_frame *frame)
+{
+
+	struct mbl_pvt *pvt = ast->tech_pvt;
+	struct ast_frame *f;
+
+	ast_debug(3, "*** mbl_write\n");
+
+	if (frame->frametype != AST_FRAME_VOICE) {
+		return 0;
+	}
+
+	ast_smoother_feed(pvt->smoother, frame);
+
+	while ((f = ast_smoother_read(pvt->smoother))) {
+		sco_write(pvt->sco_socket, f->data.ptr, f->datalen);
+		ast_frfree(f);
+	}
+
+	return 0;
+
+}
+
+static int mbl_fixup(struct ast_channel *oldchan, struct ast_channel *newchan)
+{
+
+	struct mbl_pvt *pvt = oldchan->tech_pvt;
+
+	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;
+
+}
+
+static int mbl_devicestate(void *data)
+{
+
+	char *device;
+	int res = AST_DEVICE_INVALID;
+	struct mbl_pvt *pvt;
+
+	device = ast_strdupa(S_OR(data, ""));
+
+	ast_debug(1, "Checking device state for device %s\n", device);
+
+	AST_RWLIST_RDLOCK(&devices);
+	AST_RWLIST_TRAVERSE(&devices, pvt, entry) {
+		if (!strcmp(pvt->id, device))
+			break;
+	}
+	AST_RWLIST_UNLOCK(&devices);
+
+	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;
+
+}
+
+/*
+
+	Callback helpers
+
+*/
+
+/*
+
+	do_alignment_detection()
+
+	This routine attempts to detect where we get misaligned sco audio data from the bluetooth adaptor.
+
+	Its enabled by alignmentdetect=yes under the adapter entry in mobile.conf
+
+	Some adapters suffer a problem where occasionally they will byte shift the audio stream one byte to the right.
+	The result is static or white noise on the inbound (from the adapter) leg of the call.
+	This is characterised by a sudden jump in magnitude of the value of the 16 bit samples.
+
+	Here we look at the first 4 48 byte frames. We average the absolute values of each sample in the frame,
+	then average the sum of the averages of frames 1, 2, and 3.
+	Frame zero is usually zero.
+	If the end result > 100, and it usually is if we have the problem, set a flag and compensate by shifting the bytes
+	for each subsequent frame during the call.
+
+	If the result is <= 100 then clear the flag so we dont come back in here...
+
+	This seems to work OK....
+
+*/
+
+static void do_alignment_detection(struct mbl_pvt *pvt, char *buf, int buflen)
+{
+
+	int i;
+	short a, *s;
+	char *p;
+
+	if (pvt->alignment_detection_triggered) {
+		for (i=buflen, p=buf+buflen-1; i>0; i--, p--)
+			*p = *(p-1);
+		*(p+1) = 0;
+		return;
+	}
+
+	if (pvt->alignment_count < 4) {
+		s = (short *)buf;
+		for (i=0, a=0; i<buflen/2; i++) {
+			a += *s++;
+			a /= i+1;
+		}
+		pvt->alignment_samples[pvt->alignment_count++] = a;
+		return;
+	}
+
+	ast_debug(1, "Alignment Detection result is [%-d %-d %-d %-d]\n", pvt->alignment_samples[0], pvt->alignment_samples[1], pvt->alignment_samples[2], pvt->alignment_samples[3]);
+
+	a = abs(pvt->alignment_samples[1]) + abs(pvt->alignment_samples[2]) + abs(pvt->alignment_samples[3]);
+	a /= 3;
+	if (a > 100) {
+		pvt->alignment_detection_triggered = 1;
+		ast_debug(1, "Alignment Detection Triggered.\n");
+	} else
+		pvt->do_alignment_detection = 0;
+
+}
+
+static int mbl_queue_control(struct mbl_pvt *pvt, enum ast_control_frame_type control)
+{
+	for (;;) {
+		if (pvt->owner) {
+			if (ast_channel_trylock(pvt->owner)) {
+				DEADLOCK_AVOIDANCE(&pvt->lock);
+			} else {
+				ast_queue_control(pvt->owner, control);
+				ast_channel_unlock(pvt->owner);
+				break;
+			}
+		} else
+			break;
+	}
+	return 0;
+}
+
+static int mbl_queue_hangup(struct mbl_pvt *pvt)
+{
+	for (;;) {
+		if (pvt->owner) {
+			if (ast_channel_trylock(pvt->owner)) {
+				DEADLOCK_AVOIDANCE(&pvt->lock);
+			} else {
+				ast_queue_hangup(pvt->owner);
+				ast_channel_unlock(pvt->owner);
+				break;
+			}
+		} else
+			break;
+	}
+	return 0;
+}
+
+static int mbl_ast_hangup(struct mbl_pvt *pvt)
+{
+	int res = 0;
+	for (;;) {
+		if (pvt->owner) {
+			if (ast_channel_trylock(pvt->owner)) {
+				DEADLOCK_AVOIDANCE(&pvt->lock);
+			} else {
+				res = ast_hangup(pvt->owner);
+				/* no need to unlock, ast_hangup() frees the
+				 * channel */
+				break;
+			}
+		} else
+			break;
+	}
+	return res;
+}
+
+/*
+
+	rfcomm helpers
+
+*/
+
+static int rfcomm_connect(bdaddr_t src, bdaddr_t dst, int remote_channel)
+{
+
+	struct sockaddr_rc addr;
+	int s;
+
+	if ((s = socket(PF_BLUETOOTH, SOCK_STREAM, BTPROTO_RFCOMM)) < 0) {
+		ast_debug(1, "socket() failed (%d).\n", errno);
+		return -1;
+	}
+
+	memset(&addr, 0, sizeof(addr));
+	addr.rc_family = AF_BLUETOOTH;
+	bacpy(&addr.rc_bdaddr, &src);
+	addr.rc_channel = (uint8_t) 1;
+	if (bind(s, (struct sockaddr *)&addr, sizeof(addr)) < 0) {
+		ast_debug(1, "bind() failed (%d).\n", errno);
+		close(s);
+		return -1;
+	}
+
+	memset(&addr, 0, sizeof(addr));
+	addr.rc_family = AF_BLUETOOTH;
+	bacpy(&addr.rc_bdaddr, &dst);
+	addr.rc_channel = remote_channel;
+	if (connect(s, (struct sockaddr *)&addr, sizeof(addr)) < 0) {
+		ast_debug(1, "connect() failed (%d).\n", errno);
+		close(s);
+		return -1;
+	}
+
+	return s;
+
+}
+
+/*!
+ * \brief Write to an rfcomm socket.
+ * \param rsock the socket to write to
+ * \param buf the null terminated buffer to write
+ *
+ * This function will write characters from buf.  The buffer must be null
+ * terminated.
+ *
+ * \retval -1 error
+ * \retval 0 success
+ */
+static int rfcomm_write(int rsock, char *buf)
+{
+	return rfcomm_write_full(rsock, buf, strlen(buf));
+}
+
+
+/*!
+ * \brief Write to an rfcomm socket.
+ * \param rsock the socket to write to
+ * \param buf the buffer to write
+ * \param count the number of characters from the buffer to write
+ *
+ * This function will write count characters from buf.  It will always write
+ * count chars unless it encounters an error.
+ *
+ * \retval -1 error
+ * \retval 0 success
+ */
+static int rfcomm_write_full(int rsock, char *buf, size_t count)
+{
+	char *p = buf;
+	ssize_t out_count;
+
+	ast_debug(1, "rfcomm_write() (%d) [%.*s]\n", rsock, (int) count, buf);
+	while (count > 0) {
+		if ((out_count = write(rsock, p, count)) == -1) {
+			ast_debug(1, "rfcomm_write() error [%d]\n", errno);
+			return -1;
+		}
+		count -= out_count;
+		p += out_count;
+	}
+
+	return 0;
+}
+
+/*!
+ * \brief Wait for activity on an rfcomm socket.
+ * \param rsock the socket to watch
+ * \param ms a pointer to an int containing a timeout in ms
+ * \return zero on timeout and the socket fd (non-zero) otherwise
+ * \retval 0 timeout
+ */
+static int rfcomm_wait(int rsock, int *ms)
+{
+	int exception, outfd;
+	outfd = ast_waitfor_n_fd(&rsock, 1, ms, &exception);
+	if (outfd < 0)
+		outfd = 0;
+
+	return outfd;
+}
+
+#ifdef RFCOMM_READ_DEBUG
+#define rfcomm_read_debug(c) __rfcomm_read_debug(c)
+static void __rfcomm_read_debug(char c)
+{
+	if (c == '\r')
+		ast_debug(2, "rfcomm_read: \\r\n");
+	else if (c == '\n')
+		ast_debug(2, "rfcomm_read: \\n\n");
+	else
+		ast_debug(2, "rfcomm_read: %c\n", c);
+}
+#else
+#define rfcomm_read_debug(c)
+#endif
+
+/*!
+ * \brief Append the given character to the given buffer and increase the
+ * in_count.
+ */
+static void inline rfcomm_append_buf(char **buf, size_t count, size_t *in_count, char c)
+{
+	if (*in_count < count) {
+		(*in_count)++;
+		*(*buf)++ = c;
+	}
+}
+
+/*!
+ * \brief Read a character from the given stream and check if it matches what
+ * we expected.
+ */
+static int rfcomm_read_and_expect_char(int rsock, char *result, char expected)
+{
+	int res;
+	char c;
+
+	if (!result)
+		result = &c;
+
+	if ((res = read(rsock, result, 1)) < 1) {
+		return res;
+	}
+	rfcomm_read_debug(*result);
+
+	if (*result != expected) {
+		return -2;
+	}
+
+	return 1;
+}
+
+/*!
+ * \brief Read a character from the given stream and append it to the given
+ * buffer if it matches the expected character.
+ */
+static int rfcomm_read_and_append_char(int rsock, char **buf, size_t count, size_t *in_count, char *result, char expected)
+{
+	int res;
+	char c;
+
+	if (!result)
+		result = &c;
+
+	if ((res = rfcomm_read_and_expect_char(rsock, result, expected)) < 1) {
+		return res;
+	}
+
+	rfcomm_append_buf(buf, count, in_count, *result);
+	return 1;
+}
+
+/*!
+ * \brief Read until '\r\n'.
+ * This function consumes the '\r\n' but does not add it to buf.
+ */
+static int rfcomm_read_until_crlf(int rsock, char **buf, size_t count, size_t *in_count)
+{
+	int res;
+	char c;
+
+	while ((res = read(rsock, &c, 1)) == 1) {
+		rfcomm_read_debug(c);
+		if (c == '\r') {
+			if ((res = rfcomm_read_and_expect_char(rsock, &c, '\n')) == 1) {
+				break;
+			} else if (res == -2) {
+				rfcomm_append_buf(buf, count, in_count, '\r');
+			} else {
+				rfcomm_append_buf(buf, count, in_count, '\r');
+				break;
+			}
+		}
+
+		rfcomm_append_buf(buf, count, in_count, c);
+	}
+	return res;
+}
+
+/*!
+ * \brief Read the remainder of an AT SMS prompt.
+ * \note the entire parsed string is '\r\n> '
+ *
+ * By the time this function is executed, only a ' ' is left to read.
+ */
+static int rfcomm_read_sms_prompt(int rsock, char **buf, size_t count, size_t *in_count)
+{
+	int res;
+	if ((res = rfcomm_read_and_append_char(rsock, buf, count, in_count, NULL, ' ')) < 1)
+	       goto e_return;
+
+	return 1;
+
+e_return:
+	ast_log(LOG_ERROR, "error parsing SMS prompt on rfcomm socket\n");
+	return res;
+}
+
+/*!
+ * \brief Read and AT result code.
+ * \note the entire parsed string is '\r\n<result code>\r\n'
+ */
+static int rfcomm_read_result(int rsock, char **buf, size_t count, size_t *in_count)
+{
+	int res;
+	char c;
+
+	if ((res = rfcomm_read_and_expect_char(rsock, &c, '\n')) < 1) {
+		goto e_return;
+	}
+
+	if ((res = rfcomm_read_and_append_char(rsock, buf, count, in_count, &c, '>')) == 1) {
+		return rfcomm_read_sms_prompt(rsock, buf, count, in_count);
+	} else if (res != -2) {
+		goto e_return;
+	}
+
+	rfcomm_append_buf(buf, count, in_count, c);
+	res = rfcomm_read_until_crlf(rsock, buf, count, in_count);
+
+	if (res != 1)
+		return res;
+
+	/* check for CMGR, which contains an embedded \r\n */
+	if (*in_count >= 5 && !strncmp(*buf - *in_count, "+CMGR", 5)) {
+		rfcomm_append_buf(buf, count, in_count, '\r');
+		rfcomm_append_buf(buf, count, in_count, '\n');
+		return rfcomm_read_until_crlf(rsock, buf, count, in_count);
+	}
+
+	return 1;
+
+e_return:
+	ast_log(LOG_ERROR, "error parsing AT result on rfcomm socket");
+	return res;
+}
+
+/*!
+ * \brief Read the remainder of an AT command.
+ * \note the entire parsed string is '<at command>\r'
+ */
+static int rfcomm_read_command(int rsock, char **buf, size_t count, size_t *in_count)
+{
+	int res;
+	char c;
+
+	while ((res = read(rsock, &c, 1)) == 1) {
+		rfcomm_read_debug(c);
+		/* stop when we get to '\r' */
+		if (c == '\r')
+			break;
+
+		rfcomm_append_buf(buf, count, in_count, c);
+	}
+	return res;
+}
+
+/*!
+ * \brief Read one Hayes AT message from an rfcomm socket.
+ * \param rsock the rfcomm socket to read from
+ * \param buf the buffer to store the result in
+ * \param count the size of the buffer or the maximum number of characters to read
+ *
+ * Here we need to read complete Hayes AT messages.  The AT message formats we
+ * support are listed below.
+ *
+ * \verbatim
+ * \r\n<result code>\r\n
+ * <at command>\r
+ * \r\n> 
+ * \endverbatim
+ *
+ * These formats correspond to AT result codes, AT commands, and the AT SMS
+ * prompt respectively.  When messages are read the leading and trailing '\r'
+ * and '\n' characters are discarded.  If the given buffer is not large enough
+ * to hold the response, what does not fit in the buffer will be dropped.
+ *
+ * \note The rfcomm connection to the device is asynchronous, so there is no
+ * guarantee that responses will be returned in a single read() call. We handle
+ * this by blocking until we can read an entire response.
+ *
+ * \retval 0 end of file
+ * \retval -1 read error
+ * \retval -2 parse error
+ * \retval other the number of characters added to buf
+ */
+static ssize_t rfcomm_read(int rsock, char *buf, size_t count)
+{
+	ssize_t res;
+	size_t in_count = 0;
+	char c;
+
+	if ((res = rfcomm_read_and_expect_char(rsock, &c, '\r')) == 1) {
+		res = rfcomm_read_result(rsock, &buf, count, &in_count);
+	} else if (res == -2) {
+		rfcomm_append_buf(&buf, count, &in_count, c);
+		res = rfcomm_read_command(rsock, &buf, count, &in_count);
+	}
+
+	if (res < 1)
+		return res;
+	else
+		return in_count;
+}
+
+/*
+
+	sco helpers and callbacks
+
+*/
+
+static int sco_connect(bdaddr_t src, bdaddr_t dst)
+{
+
+	struct sockaddr_sco addr;
+	int s;
+
+	if ((s = socket(PF_BLUETOOTH, SOCK_SEQPACKET, BTPROTO_SCO)) < 0) {
+		ast_debug(1, "socket() failed (%d).\n", errno);
+		return -1;
+	}
+
+/* XXX this does not work with the do_sco_listen() thread (which also bind()s
+ * to this address).  Also I am not sure if it is necessary. */
+#if 0
+	memset(&addr, 0, sizeof(addr));
+	addr.sco_family = AF_BLUETOOTH;
+	bacpy(&addr.sco_bdaddr, &src);
+	if (bind(s, (struct sockaddr *)&addr, sizeof(addr)) < 0) {
+		ast_debug(1, "bind() failed (%d).\n", errno);
+		close(s);
+		return -1;
+	}
+#endif
+
+	memset(&addr, 0, sizeof(addr));
+	addr.sco_family = AF_BLUETOOTH;
+	bacpy(&addr.sco_bdaddr, &dst);
+
+	if (connect(s, (struct sockaddr *)&addr, sizeof(addr)) < 0) {
+		ast_debug(1, "sco connect() failed (%d).\n", errno);
+		close(s);
+		return -1;
+	}
+
+	return s;
+
+}
+
+static int sco_write(int s, char *buf, int len)
+{
+
+	int r;
+
+	if (s == -1) {
+		ast_debug(3, "sco_write() not ready\n");
+		return 0;
+	}
+
+	ast_debug(3, "sco_write()\n");
+
+	r = write(s, buf, len);
+	if (r == -1) {
+		ast_debug(3, "sco write error %d\n", errno);
+		return 0;
+	}
+
+	return 1;
+
+}
+
+/*!
+ * \brief Read data from the given sco connection.
+ * This function is an ast_io callback function that will read data from the
+ * given sco socket and write it to the corrisponding pvt io_pipe.
+ */
+static int sco_read(int *id, int fd, short events, void *data)
+{
+	struct mbl_pvt *pvt = (struct mbl_pvt *) data;
+	int res;
+	char buf[DEVICE_FRAME_SIZE];
+
+	ast_debug(3, "sco_read()\n");
+
+	if ((res = read(fd, buf, DEVICE_FRAME_SIZE)) <= 0) {
+		ast_debug(3, "error reading sco audio data\n");
+		close(fd);
+		/* don't set pvt->sco_sock = -1 here, there may already be a
+		 * new pvt->sco_sock */
+		return 0;
+	}
+
+	if (pvt->do_alignment_detection)
+		do_alignment_detection(pvt, 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;
+}
+
+/*!
+ * \brief Accept SCO connections.
+ * This function is an ast_io callback function used to accept incoming sco
+ * audio connections.
+ */
+static int sco_accept(int *id, int fd, short events, void *data)
+{
+	struct adapter_pvt *adapter = (struct adapter_pvt *) data;
+	struct sockaddr_sco addr;
+	socklen_t addrlen;
+	struct mbl_pvt *pvt;
+	socklen_t len;
+	char saddr[18];
+	struct sco_options so;
+	int sock;
+
+	addrlen = sizeof(struct sockaddr_sco);
+	if ((sock = accept(fd, (struct sockaddr *)&addr, &addrlen)) == -1) {
+		ast_log(LOG_ERROR, "error accepting audio connection on adapter %s\n", adapter->id);
+		return 0;
+	}
+
+	len = sizeof(so);
+	getsockopt(sock, SOL_SCO, SCO_OPTIONS, &so, &len);
+
+	ba2str(&addr.sco_bdaddr, saddr);
+	ast_debug(1, "Incoming Audio Connection from device %s MTU is %d\n", saddr, so.mtu);
+
+	/* figure out which device this sco connection belongs to */
+	pvt = NULL;
+	AST_RWLIST_RDLOCK(&devices);

[... 3453 lines stripped ...]



More information about the svn-commits mailing list