Skip to content
Snippets Groups Projects
chan_mobile.c 125 KiB
Newer Older
  • Learn to ignore specific revisions
  • 		pvt->hangupcause = 0;
    
    		pvt->needchup = 1;
    		msg_queue_push(pvt, AT_OK, AT_D);
    	} else {
    		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;
    
    }
    
    static int mbl_hangup(struct ast_channel *ast)
    {
    
    	struct mbl_pvt *pvt;
    
    
    	if (!ast_channel_tech_pvt(ast)) {
    
    		ast_log(LOG_WARNING, "Asked to hangup channel not connected\n");
    		return 0;
    	}
    
    	pvt = ast_channel_tech_pvt(ast);
    
    
    	ast_debug(1, "[%s] hanging up device\n", pvt->id);
    
    	ast_mutex_lock(&pvt->lock);
    	ast_channel_set_fd(ast, 0, -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->needring = 0;
    	pvt->owner = NULL;
    
    	ast_channel_tech_pvt_set(ast, 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_channel_tech_pvt(ast);
    
    
    	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_channel_tech_pvt(ast);
    
    
    	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_channel_tech_pvt(ast);
    
    	struct ast_frame *fr = &ast_null_frame;
    	int r;
    
    	ast_debug(3, "*** mbl_read()\n");
    
    	while (ast_mutex_trylock(&pvt->lock)) {
    		CHANNEL_DEADLOCK_AVOIDANCE(ast);
    	}
    
    	if (!pvt->owner || pvt->sco_socket == -1) {
    		goto e_return;
    	}
    
    	memset(&pvt->fr, 0x00, sizeof(struct ast_frame));
    	pvt->fr.frametype = AST_FRAME_VOICE;
    
    	pvt->fr.subclass.format = DEVICE_FRAME_FORMAT;
    
    	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;
    
    
    	do {
    		if ((r = read(pvt->sco_socket, pvt->fr.data.ptr, DEVICE_FRAME_SIZE)) == -1) {
    			if (errno != EAGAIN && errno != EINTR) {
    				ast_debug(1, "[%s] read error %d, going to wait for new connection\n", pvt->id, errno);
    				close(pvt->sco_socket);
    				pvt->sco_socket = -1;
    				ast_channel_set_fd(ast, 0, -1);
    			}
    			goto e_return;
    
    		pvt->fr.datalen = r;
    		pvt->fr.samples = r / 2;
    
    		if (pvt->do_alignment_detection)
    			do_alignment_detection(pvt, pvt->fr.data.ptr, r);
    
    		ast_smoother_feed(pvt->bt_in_smoother, &pvt->fr);
    		fr = ast_smoother_read(pvt->bt_in_smoother);
    	} while (fr == NULL);
    	fr = ast_dsp_process(ast, pvt->dsp, fr);
    
    
    	ast_mutex_unlock(&pvt->lock);
    
    	return fr;
    
    e_return:
    	ast_mutex_unlock(&pvt->lock);
    	return fr;
    }
    
    static int mbl_write(struct ast_channel *ast, struct ast_frame *frame)
    {
    
    
    	struct mbl_pvt *pvt = ast_channel_tech_pvt(ast);
    
    	struct ast_frame *f;
    
    	ast_debug(3, "*** mbl_write\n");
    
    	if (frame->frametype != AST_FRAME_VOICE) {
    		return 0;
    	}
    
    	while (ast_mutex_trylock(&pvt->lock)) {
    		CHANNEL_DEADLOCK_AVOIDANCE(ast);
    	}
    
    
    	ast_smoother_feed(pvt->bt_out_smoother, frame);
    
    	while ((f = ast_smoother_read(pvt->bt_out_smoother))) {
    
    		sco_write(pvt->sco_socket, f->data.ptr, f->datalen);
    	}
    
    	ast_mutex_unlock(&pvt->lock);
    
    	return 0;
    
    }
    
    static int mbl_fixup(struct ast_channel *oldchan, struct ast_channel *newchan)
    {
    
    
    	struct mbl_pvt *pvt = ast_channel_tech_pvt(newchan);
    
    		ast_debug(1, "fixup failed, no pvt on newchan\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(const char *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;
    
    
    		if (!mbl_has_service(pvt))
    			res = AST_DEVICE_UNAVAILABLE;
    
    	}
    	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 don't 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 {
    
    				if (pvt->hangupcause != 0) {
    					ast_channel_hangupcause_set(pvt->owner, pvt->hangupcause);
    				}
    
    				ast_queue_hangup(pvt->owner);
    				ast_channel_unlock(pvt->owner);
    				break;
    			}
    		} else
    			break;
    	}
    	return 0;
    }
    
    static int mbl_ast_hangup(struct mbl_pvt *pvt)
    {
    
    	ast_hangup(pvt->owner);
    
    /*!
     * \brief Check if a mobile device has service.
     * \param pvt a mbl_pvt struct
     * \retval 1 this device has service
     * \retval 0 no service
     *
     * \note This function will always indicate that service is available if the
     * given device does not support service indication.
     */
    static int mbl_has_service(struct mbl_pvt *pvt)
    {
    
    	if (pvt->type != MBL_TYPE_PHONE)
    		return 1;
    
    	if (!pvt->hfp->cind_map.service)
    		return 1;
    
    	if (pvt->hfp->cind_state[pvt->hfp->cind_map.service] == HFP_CIND_SERVICE_AVAILABLE)
    		return 1;
    
    	return 0;
    }
    
    
    /*
    
    	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);
    
    	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 \verbatim '\r\n'. \endverbatim
     * This function consumes the \verbatim'\r\n'\endverbatim but does not add it to buf.
    
     */
    static int rfcomm_read_until_crlf(int rsock, char **buf, size_t count, size_t *in_count)
    {
    	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 \verbatim '\r\n> ' \endverbatim
    
     *
     * 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 until a \verbatim \r\nOK\r\n \endverbatim message.
    
     */
    static int rfcomm_read_until_ok(int rsock, char **buf, size_t count, size_t *in_count)
    {
    	int res;
    	char c;
    
    	/* here, we read until finding a \r\n, then we read one character at a
    	 * time looking for the string '\r\nOK\r\n'.  If we only find a partial
    	 * match, we place that in the buffer and try again. */
    
    	for (;;) {
    		if ((res = rfcomm_read_until_crlf(rsock, buf, count, in_count)) != 1) {
    			break;
    		}
    
    		rfcomm_append_buf(buf, count, in_count, '\r');
    		rfcomm_append_buf(buf, count, in_count, '\n');
    
    		if ((res = rfcomm_read_and_expect_char(rsock, &c, '\r')) != 1) {
    			if (res != -2) {
    				break;
    			}
    
    			rfcomm_append_buf(buf, count, in_count, c);
    			continue;
    		}
    
    		if ((res = rfcomm_read_and_expect_char(rsock, &c, '\n')) != 1) {
    			if (res != -2) {
    				break;
    			}
    
    			rfcomm_append_buf(buf, count, in_count, '\r');
    			rfcomm_append_buf(buf, count, in_count, c);
    			continue;
    		}
    		if ((res = rfcomm_read_and_expect_char(rsock, &c, 'O')) != 1) {
    			if (res != -2) {
    				break;
    			}
    
    			rfcomm_append_buf(buf, count, in_count, '\r');
    			rfcomm_append_buf(buf, count, in_count, '\n');
    			rfcomm_append_buf(buf, count, in_count, c);
    			continue;
    		}
    
    		if ((res = rfcomm_read_and_expect_char(rsock, &c, 'K')) != 1) {
    			if (res != -2) {
    				break;
    			}
    
    			rfcomm_append_buf(buf, count, in_count, '\r');
    			rfcomm_append_buf(buf, count, in_count, '\n');
    			rfcomm_append_buf(buf, count, in_count, 'O');
    			rfcomm_append_buf(buf, count, in_count, c);
    			continue;
    		}
    
    		if ((res = rfcomm_read_and_expect_char(rsock, &c, '\r')) != 1) {
    			if (res != -2) {
    				break;
    			}
    
    			rfcomm_append_buf(buf, count, in_count, '\r');
    			rfcomm_append_buf(buf, count, in_count, '\n');
    			rfcomm_append_buf(buf, count, in_count, 'O');
    			rfcomm_append_buf(buf, count, in_count, 'K');
    			rfcomm_append_buf(buf, count, in_count, c);
    			continue;
    		}
    
    		if ((res = rfcomm_read_and_expect_char(rsock, &c, '\n')) != 1) {
    			if (res != -2) {
    				break;
    			}
    
    			rfcomm_append_buf(buf, count, in_count, '\r');
    			rfcomm_append_buf(buf, count, in_count, '\n');
    			rfcomm_append_buf(buf, count, in_count, 'O');
    			rfcomm_append_buf(buf, count, in_count, 'K');
    			rfcomm_append_buf(buf, count, in_count, '\r');
    			rfcomm_append_buf(buf, count, in_count, c);
    			continue;
    		}
    
    		/* we have successfully parsed a '\r\nOK\r\n' string */
    		return 1;
    	}
    
    	return res;
    }
    
    
    /*!
     * \brief Read the remainder of a +CMGR message.
    
     * \note the entire parsed string is \verbatim '+CMGR: ...\r\n...\r\n...\r\n...\r\nOK\r\n' \endverbatim
    
     */
    static int rfcomm_read_cmgr(int rsock, char **buf, size_t count, size_t *in_count)
    {
    	int res;
    
    	/* append the \r\n that was stripped by the calling function */
    	rfcomm_append_buf(buf, count, in_count, '\r');
    	rfcomm_append_buf(buf, count, in_count, '\n');
    
    	if ((res = rfcomm_read_until_ok(rsock, buf, count, in_count)) != 1) {
    		ast_log(LOG_ERROR, "error reading +CMGR message on rfcomm socket\n");
    	}
    
    	return res;
    }
    
    
    /*!
     * \brief Read and AT result code.
    
     * \note the entire parsed string is \verbatim '\r\n<result code>\r\n' \endverbatim
    
     */
    static int rfcomm_read_result(int rsock, char **buf, size_t count, size_t *in_count)
    {
    	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 pairs terminated by
    	 * an \r\nOK\r\n message */
    
    	if (*in_count >= 5 && !strncmp(*buf - *in_count, "+CMGR", 5)) {
    
    		return rfcomm_read_cmgr(rsock, buf, count, in_count);
    
    	ast_log(LOG_ERROR, "error parsing AT result on rfcomm socket\n");
    
    	return res;
    }
    
    /*!
     * \brief Read the remainder of an AT command.
    
     * \note the entire parsed string is \verbatim '<at command>\r' \endverbatim
    
     */
    static int rfcomm_read_command(int rsock, char **buf, size_t count, size_t *in_count)
    {
    	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
    
     * \endverbatim
     *
     * These formats correspond to AT result codes, AT commands, and the AT SMS
    
     * prompt respectively.  When messages are read the leading and trailing \verbatim '\r' \endverbatim
     * and \verbatim '\n' \endverbatim characters are discarded.  If the given buffer is not large enough
    
     * to hold the response, what does not fit in the buffer will be dropped.
     *
     * \note The rfcomm connection to the device is asynchronous, so there is no
     * 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 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);
    	AST_RWLIST_TRAVERSE(&devices, pvt, entry) {
    		if (!bacmp(&pvt->addr, &addr.sco_bdaddr))
    			break;
    	}
    	AST_RWLIST_UNLOCK(&devices);
    	if (!pvt) {
    		ast_log(LOG_WARNING, "could not find device for incoming audio connection\n");
    		close(sock);
    		return 1;
    	}
    
    	ast_mutex_lock(&pvt->lock);
    	if (pvt->sco_socket != -1) {
    		close(pvt->sco_socket);
    		pvt->sco_socket = -1;
    	}
    
    	pvt->sco_socket = sock;
    	if (pvt->owner) {
    		ast_channel_set_fd(pvt->owner, 0, sock);
    	} else {
    		ast_debug(1, "incoming audio connection for pvt without owner\n");
    	}
    
    	ast_mutex_unlock(&pvt->lock);
    
    	return 1;
    }
    
    /*!
     * \brief Bind an SCO listener socket for the given adapter.
     * \param adapter an adapter_pvt
     * \return -1 on error, non zero on success
     */
    static int sco_bind(struct adapter_pvt *adapter)
    {
    	struct sockaddr_sco addr;
    	int opt = 1;
    
    	if ((adapter->sco_socket = socket(PF_BLUETOOTH, SOCK_SEQPACKET, BTPROTO_SCO)) < 0) {
    		ast_log(LOG_ERROR, "Unable to create sco listener socket for adapter %s.\n", adapter->id);
    		goto e_return;
    	}
    
    	memset(&addr, 0, sizeof(addr));
    	addr.sco_family = AF_BLUETOOTH;
    	bacpy(&addr.sco_bdaddr, &adapter->addr);
    	if (bind(adapter->sco_socket, (struct sockaddr *)&addr, sizeof(addr)) < 0) {
    		ast_log(LOG_ERROR, "Unable to bind sco listener socket. (%d)\n", errno);
    		goto e_close_socket;
    	}
    	if (setsockopt(adapter->sco_socket, SOL_SOCKET, SO_REUSEADDR, &opt, sizeof(opt)) == -1) {
    		ast_log(LOG_ERROR, "Unable to setsockopt sco listener socket.\n");
    		goto e_close_socket;
    	}
    	if (listen(adapter->sco_socket, 5) < 0) {
    		ast_log(LOG_ERROR, "Unable to listen sco listener socket.\n");
    		goto e_close_socket;
    	}
    
    	return adapter->sco_socket;
    
    e_close_socket:
    	close(adapter->sco_socket);
    	adapter->sco_socket = -1;
    e_return:
    	return -1;
    }
    
    
    /*
     * Hayes AT command helpers.
     */
    
    /*!
     * \brief Match the given buffer with the given prefix.
     * \param buf the buffer to match