[asterisk-commits] russell: branch group/addons-merge r204392 - in /team/group/addons-merge: ./ ...
SVN commits to the Asterisk project
asterisk-commits at lists.digium.com
Tue Jun 30 08:41:56 CDT 2009
Author: russell
Date: Tue Jun 30 08:41:52 2009
New Revision: 204392
URL: http://svn.asterisk.org/svn-view/asterisk?view=rev&rev=204392
Log:
Add chan_mobile and associated build system updates
I had to change a lot of code in chan_mobile to eliminate compiler warnings ...
Added:
team/group/addons-merge/addons/chan_mobile.c (with props)
Modified:
team/group/addons-merge/build_tools/menuselect-deps.in
team/group/addons-merge/configure
team/group/addons-merge/configure.ac
team/group/addons-merge/include/asterisk/autoconfig.h.in
team/group/addons-merge/makeopts.in
Added: team/group/addons-merge/addons/chan_mobile.c
URL: http://svn.asterisk.org/svn-view/asterisk/team/group/addons-merge/addons/chan_mobile.c?view=auto&rev=204392
==============================================================================
--- team/group/addons-merge/addons/chan_mobile.c (added)
+++ team/group/addons-merge/addons/chan_mobile.c Tue Jun 30 08:41:52 2009
@@ -1,0 +1,4274 @@
+/*
+ * Asterisk -- An open source telephony toolkit.
+ *
+ * Copyright (C) 1999 - 2006, Digium, Inc.
+ *
+ * Mark Spencer <markster at digium.com>
+ *
+ * See http://www.asterisk.org for more information about
+ * the Asterisk project. Please do not directly contact
+ * any of the maintainers of this project for assistance;
+ * the project provides a web site, mailing lists and IRC
+ * channels for your use.
+ *
+ * This program is free software, distributed under the terms of
+ * the GNU General Public License Version 2. See the LICENSE file
+ * at the top of the source tree.
+ */
+
+/*! \file
+ *
+ * \brief Bluetooth Mobile Device channel driver
+ *
+ * \author Dave Bowerman <david.bowerman at gmail.com>
+ *
+ * \ingroup channel_drivers
+ */
+
+/*** MODULEINFO
+ <depend>bluetooth</depend>
+ <defaultenabled>no</defaultenabled>
+ ***/
+
+#include <asterisk.h>
+
+ASTERISK_FILE_VERSION(__FILE__, "$Revision$")
+
+#include <stdio.h>
+#include <string.h>
+#include <sys/socket.h>
+#include <sys/time.h>
+#include <errno.h>
+#include <unistd.h>
+#include <stdlib.h>
+#include <arpa/inet.h>
+#include <fcntl.h>
+#include <sys/ioctl.h>
+#include <signal.h>
+
+#include <bluetooth/bluetooth.h>
+#include <bluetooth/hci.h>
+#include <bluetooth/hci_lib.h>
+#include <bluetooth/sdp.h>
+#include <bluetooth/sdp_lib.h>
+#include <bluetooth/rfcomm.h>
+#include <bluetooth/sco.h>
+#include <bluetooth/l2cap.h>
+
+#include <asterisk/lock.h>
+#include <asterisk/channel.h>
+#include <asterisk/config.h>
+#include <asterisk/logger.h>
+#include <asterisk/module.h>
+#include <asterisk/pbx.h>
+#include <asterisk/options.h>
+#include <asterisk/utils.h>
+#include <asterisk/linkedlists.h>
+#include <asterisk/cli.h>
+#include <asterisk/devicestate.h>
+#include <asterisk/causes.h>
+#include <asterisk/dsp.h>
+#include <asterisk/app.h>
+#include <asterisk/manager.h>
+#include <asterisk/io.h>
+
+#define MBL_CONFIG "mobile.conf"
+
+#define DEVICE_FRAME_SIZE 48
+#define DEVICE_FRAME_FORMAT AST_FORMAT_SLINEAR
+#define CHANNEL_FRAME_SIZE 320
+
+static int prefformat = DEVICE_FRAME_FORMAT;
+
+static int discovery_interval = 60; /* The device discovery interval, default 60 seconds. */
+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(void);
+static inline void set_unloading(void);
+
+enum mbl_type {
+ MBL_TYPE_PHONE,
+ MBL_TYPE_HEADSET
+};
+
+struct adapter_pvt {
+ int dev_id; /* device id */
+ int hci_socket; /* device descriptor */
+ char id[31]; /* the 'name' from mobile.conf */
+ 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? */
+ 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 */
+ 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];
+ struct ast_smoother *smoother; /* our smoother, for making 48 byte frames */
+ int sco_socket; /* sco socket descriptor */
+ pthread_t monitor_thread; /* monitor thread handle */
+ 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 do_alignment_detection:1;
+ unsigned int alignment_detection_triggered:1;
+ unsigned int blackberry:1;
+ short alignment_samples[4];
+ int alignment_count;
+ int ring_sched_id;
+ struct ast_dsp *dsp;
+ 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);
+static char *handle_cli_mobile_search(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a);
+static char *handle_cli_mobile_rfcomm(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a);
+
+static struct ast_cli_entry mbl_cli[] = {
+ AST_CLI_DEFINE(handle_cli_mobile_show_devices, "Show Bluetooth Cell / Mobile devices"),
+ AST_CLI_DEFINE(handle_cli_mobile_search, "Search for Bluetooth Cell / Mobile devices"),
+ AST_CLI_DEFINE(handle_cli_mobile_rfcomm, "Send commands to the rfcomm port for debugging"),
+};
+
+/* App stuff */
+static char *app_mblstatus = "MobileStatus";
+static char *mblstatus_synopsis = "MobileStatus(Device,Variable)";
+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"
+" In order, Disconnected, Connected & Free, Connected & Busy.\n";
+
+static char *app_mblsendsms = "MobileSendSMS";
+static char *mblsendsms_synopsis = "MobileSendSMS(Device,Dest,Message)";
+static char *mblsendsms_desc =
+"MobileSendSms(Device,Dest,Message)\n"
+" Device - Id of device from mobile.conf\n"
+" Dest - destination\n"
+" Message - text of the message\n";
+
+static struct ast_channel *mbl_new(int state, struct mbl_pvt *pvt, char *cid_num,
+ const struct ast_channel *requestor);
+static struct ast_channel *mbl_request(const char *type, int format,
+ const struct ast_channel *requestor, void *data, int *cause);
+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_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);
+static int mbl_fixup(struct ast_channel *oldchan, struct ast_channel *newchan);
+static int mbl_devicestate(void *data);
+
+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(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_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_UNKNOWN -1
+#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 initialized:1; /*!< whether a service level connection exists or not */
+ 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_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_parse_brsf(struct hfp_pvt *hfp, const char *buf);
+static int hfp_parse_cind(struct hfp_pvt *hfp, char *buf);
+static int hfp_parse_cind_test(struct hfp_pvt *hfp, char *buf);
+
+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);
+
+/*
+ * 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_CMS_ERROR,
+ /* at commands */
+ AT_A,
+ AT_D,
+ AT_CHUP,
+ AT_CKPD,
+ AT_CMGS,
+ AT_VGM,
+ AT_VGS,
+ AT_VTS,
+ AT_CMGF,
+ AT_CNMI,
+ AT_CMER,
+ AT_CIND_TEST,
+} 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 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",
+ .description = "Bluetooth Mobile Device Channel Driver",
+ .capabilities = AST_FORMAT_SLINEAR,
+ .requester = mbl_request,
+ .call = mbl_call,
+ .hangup = mbl_hangup,
+ .answer = mbl_answer,
+ .send_digit_end = mbl_digit_end,
+ .read = mbl_read,
+ .write = mbl_write,
+ .fixup = mbl_fixup,
+ .devicestate = mbl_devicestate
+};
+
+/* CLI Commands implementation */
+
+static char *handle_cli_mobile_show_devices(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a)
+{
+ struct mbl_pvt *pvt;
+ char bdaddr[18];
+ char group[6];
+
+#define FORMAT1 "%-15.15s %-17.17s %-5.5s %-15.15s %-9.9s %-5.5s %-3.3s\n"
+
+ switch (cmd) {
+ case CLI_INIT:
+ e->command = "mobile show devices";
+ e->usage =
+ "Usage: mobile show devices\n"
+ " Shows the state of Bluetooth Cell / Mobile devices.\n";
+ return NULL;
+ case CLI_GENERATE:
+ return NULL;
+ }
+
+ if (a->argc != 3)
+ return CLI_SHOWUSAGE;
+
+ 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, 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);
+
+#undef FORMAT1
+
+ return CLI_SUCCESS;
+}
+
+static char *handle_cli_mobile_search(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a)
+{
+ struct adapter_pvt *adapter;
+ inquiry_info *ii = NULL;
+ int max_rsp, num_rsp;
+ int len, flags;
+ int i, phport, hsport;
+ char addr[19] = {0};
+ char name[31] = {0};
+
+#define FORMAT1 "%-17.17s %-30.30s %-6.6s %-7.7s %-4.4s\n"
+#define FORMAT2 "%-17.17s %-30.30s %-6.6s %-7.7s %d\n"
+
+ switch (cmd) {
+ case CLI_INIT:
+ e->command = "mobile search";
+ e->usage =
+ "Usage: mobile search\n"
+ " Searches for Bluetooth Cell / Mobile devices in range.\n";
+ return NULL;
+ case CLI_GENERATE:
+ return NULL;
+ }
+
+ if (a->argc != 2)
+ return CLI_SHOWUSAGE;
+
+ /* find a free adapter */
+ AST_RWLIST_RDLOCK(&adapters);
+ AST_RWLIST_TRAVERSE(&adapters, adapter, entry) {
+ if (!adapter->inuse)
+ break;
+ }
+ AST_RWLIST_UNLOCK(&adapters);
+
+ if (!adapter) {
+ ast_cli(a->fd, "All Bluetooth adapters are in use at this time.\n");
+ return CLI_SUCCESS;
+ }
+
+ len = 8;
+ max_rsp = 255;
+ flags = IREQ_CACHE_FLUSH;
+
+ ii = alloca(max_rsp * sizeof(inquiry_info));
+ num_rsp = hci_inquiry(adapter->dev_id, len, max_rsp, NULL, &ii, flags);
+ if (num_rsp > 0) {
+ ast_cli(a->fd, FORMAT1, "Address", "Name", "Usable", "Type", "Port");
+ for (i = 0; i < num_rsp; i++) {
+ ba2str(&(ii + i)->bdaddr, addr);
+ name[0] = 0x00;
+ if (hci_read_remote_name(adapter->hci_socket, &(ii + i)->bdaddr, sizeof(name) - 1, name, 0) < 0)
+ strcpy(name, "[unknown]");
+ phport = sdp_search(addr, HANDSFREE_AGW_PROFILE_ID);
+ if (!phport)
+ hsport = sdp_search(addr, HEADSET_PROFILE_ID);
+ else
+ hsport = 0;
+ ast_cli(a->fd, FORMAT2, addr, name, (phport > 0 || hsport > 0) ? "Yes" : "No",
+ (phport > 0) ? "Phone" : "Headset", (phport > 0) ? phport : hsport);
+ }
+ } else
+ ast_cli(a->fd, "No Bluetooth Cell / Mobile devices found.\n");
+
+#undef FORMAT1
+#undef FORMAT2
+
+ return CLI_SUCCESS;
+}
+
+static char *handle_cli_mobile_rfcomm(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a)
+{
+ char buf[128];
+ struct mbl_pvt *pvt = NULL;
+
+ switch (cmd) {
+ case CLI_INIT:
+ e->command = "mobile rfcomm";
+ e->usage =
+ "Usage: mobile rfcomm <device ID> <command>\n"
+ " Send <command> to the rfcomm port on the device\n"
+ " with the specified <device ID>.\n";
+ return NULL;
+ case CLI_GENERATE:
+ return NULL;
+ }
+
+ if (a->argc != 4)
+ return CLI_SHOWUSAGE;
+
+ AST_RWLIST_RDLOCK(&devices);
+ AST_RWLIST_TRAVERSE(&devices, pvt, entry) {
+ if (!strcmp(pvt->id, a->argv[2]))
+ break;
+ }
+ AST_RWLIST_UNLOCK(&devices);
+
+ if (!pvt) {
+ ast_cli(a->fd, "Device %s not found.\n", a->argv[2]);
+ 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;
+}
+
+/*
+
+ Dialplan applications implementation
+
+*/
+
+static int mbl_status_exec(struct ast_channel *ast, const char *data)
+{
+
+ struct mbl_pvt *pvt;
+ char *parse;
+ int stat;
+ char status[2];
+
+ AST_DECLARE_APP_ARGS(args,
+ AST_APP_ARG(device);
+ AST_APP_ARG(variable);
+ );
+
+ if (ast_strlen_zero(data))
+ return -1;
+
+ parse = ast_strdupa(data);
+
+ AST_STANDARD_APP_ARGS(args, parse);
+
+ if (ast_strlen_zero(args.device) || ast_strlen_zero(args.variable))
+ return -1;
+
+ stat = 1;
+
+ AST_RWLIST_RDLOCK(&devices);
+ AST_RWLIST_TRAVERSE(&devices, pvt, entry) {
+ if (!strcmp(pvt->id, args.device))
+ break;
+ }
+ 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);
+ pbx_builtin_setvar_helper(ast, args.variable, status);
+
+ return 0;
+
+}
+
+static int mbl_sendsms_exec(struct ast_channel *ast, const char *data)
+{
+
+ struct mbl_pvt *pvt;
+ char *parse, *message;
+
+ AST_DECLARE_APP_ARGS(args,
+ AST_APP_ARG(device);
+ AST_APP_ARG(dest);
+ AST_APP_ARG(message);
+ );
+
+ if (ast_strlen_zero(data))
+ return -1;
+
+ parse = ast_strdupa(data);
+
+ AST_STANDARD_APP_ARGS(args, parse);
+
+ if (ast_strlen_zero(args.device)) {
+ ast_log(LOG_ERROR,"NULL device for message -- SMS will not be sent.\n");
+ return -1;
+ }
+
+ if (ast_strlen_zero(args.dest)) {
+ ast_log(LOG_ERROR,"NULL destination for message -- SMS will not be sent.\n");
+ return -1;
+ }
+
+ if (ast_strlen_zero(args.message)) {
+ ast_log(LOG_ERROR,"NULL Message to be sent -- SMS will not be sent.\n");
+ return -1;
+ }
+
+ AST_RWLIST_RDLOCK(&devices);
+ AST_RWLIST_TRAVERSE(&devices, pvt, entry) {
+ if (!strcmp(pvt->id, args.device))
+ break;
+ }
+ AST_RWLIST_UNLOCK(&devices);
+
+ if (!pvt) {
+ ast_log(LOG_ERROR,"Bluetooth device %s wasn't found in the list -- SMS will not be sent.\n", args.device);
+ 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);
+ 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);
+ 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;
+}
+
+/*
+
+ Channel Driver callbacks
+
+*/
+
+static struct ast_channel *mbl_new(int state, struct mbl_pvt *pvt, char *cid_num,
+ const struct ast_channel *requestor)
+{
+
+ struct ast_channel *chn;
+
+ 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;
+
+ 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,
+ requestor ? requestor->linkedid : "", 0,
+ "Mobile/%s-%04lx", pvt->id, ast_random() & 0xffff);
+ if (!chn) {
+ goto e_return;
+ }
+
+ chn->tech = &mbl_tech;
+ chn->nativeformats = prefformat;
+ chn->rawreadformat = prefformat;
+ chn->rawwriteformat = prefformat;
+ chn->writeformat = prefformat;
+ chn->readformat = prefformat;
+ chn->tech_pvt = pvt;
+
+ if (state == AST_STATE_RING)
+ chn->rings = 1;
+
+ ast_string_field_set(chn, language, "en");
+ pvt->owner = chn;
+
+ if (pvt->sco_socket != -1) {
+ ast_channel_set_fd(chn, 0, pvt->sco_socket);
+ }
+
+ return chn;
+
+e_return:
+ return NULL;
+}
+
+static struct ast_channel *mbl_request(const char *type, int format,
+ const struct ast_channel *requestor, void *data, int *cause)
+{
+
+ struct ast_channel *chn = NULL;
+ struct mbl_pvt *pvt;
+ char *dest_dev = NULL;
+ char *dest_num = NULL;
+ int oldformat, group = -1;
+
+ if (!data) {
+ ast_log(LOG_WARNING, "Channel requested with no data\n");
+ *cause = AST_CAUSE_INCOMPATIBLE_DESTINATION;
+ return NULL;
+ }
+
+ oldformat = format;
+ format &= (AST_FORMAT_SLINEAR);
+ if (!format) {
+ ast_log(LOG_WARNING, "Asked to get a channel of unsupported format '%d'\n", oldformat);
+ *cause = AST_CAUSE_FACILITY_NOT_IMPLEMENTED;
+ return NULL;
+ }
+
+ dest_dev = ast_strdupa((char *)data);
+
+ dest_num = strchr(dest_dev, '/');
+ if (dest_num)
+ *dest_num++ = 0x00;
+
+ if (((dest_dev[0] == 'g') || (dest_dev[0] == 'G')) && ((dest_dev[1] >= '0') && (dest_dev[1] <= '9'))) {
+ group = atoi(&dest_dev[1]);
+ }
+
+ /* Find requested device and make sure it's connected. */
+ AST_RWLIST_RDLOCK(&devices);
+ AST_RWLIST_TRAVERSE(&devices, pvt, entry) {
+ if (group > -1 && pvt->group == group && pvt->connected && !pvt->owner) {
+ break;
+ } else if (!strcmp(pvt->id, dest_dev)) {
+ break;
+ }
+ }
+ AST_RWLIST_UNLOCK(&devices);
+ if (!pvt || !pvt->connected || pvt->owner) {
+ ast_log(LOG_WARNING, "Request to call on device %s which is not connected / already in use.\n", dest_dev);
+ *cause = AST_CAUSE_REQUESTED_CHAN_UNAVAIL;
+ return NULL;
+ }
+
+ if ((pvt->type == MBL_TYPE_PHONE) && !dest_num) {
+ ast_log(LOG_WARNING, "Can't determine destination number.\n");
+ *cause = AST_CAUSE_INCOMPATIBLE_DESTINATION;
+ return NULL;
+ }
+
+ ast_mutex_lock(&pvt->lock);
+ chn = mbl_new(AST_STATE_DOWN, pvt, NULL, requestor);
+ ast_mutex_unlock(&pvt->lock);
+ if (!chn) {
+ ast_log(LOG_WARNING, "Unable to allocate channel structure.\n");
+ *cause = AST_CAUSE_REQUESTED_CHAN_UNAVAIL;
+ return NULL;
+ }
+
+ return chn;
+
+}
+
+static int mbl_call(struct ast_channel *ast, char *dest, int timeout)
+{
+
+ struct mbl_pvt *pvt;
+ char *dest_dev = NULL;
+ char *dest_num = NULL;
+
+ dest_dev = ast_strdupa((char *)dest);
+
+ pvt = ast->tech_pvt;
+
+ if (pvt->type == MBL_TYPE_PHONE) {
+ dest_num = strchr(dest_dev, '/');
+ if (!dest_num) {
+ ast_log(LOG_WARNING, "Cant determine destination number.\n");
+ return -1;
+ }
+ *dest_num++ = 0x00;
+ }
+
+ if ((ast->_state != AST_STATE_DOWN) && (ast->_state != AST_STATE_RESERVED)) {
+ ast_log(LOG_WARNING, "mbl_call called on %s, neither down nor reserved\n", ast->name);
+ return -1;
+ }
+
+ ast_debug(1, "Calling %s on %s\n", dest, ast->name);
+
+ ast_mutex_lock(&pvt->lock);
+ if (pvt->type == MBL_TYPE_PHONE) {
+ 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;
+ }
+ 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->tech_pvt) {
+ ast_log(LOG_WARNING, "Asked to hangup channel not connected\n");
+ return 0;
+ }
+ pvt = ast->tech_pvt;
+
+ 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->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;
+ 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 = 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;
+
+ 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);
+
+ fr = ast_dsp_process(ast, pvt->dsp, &pvt->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->tech_pvt;
+ 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->smoother, frame);
+
+ while ((f = ast_smoother_read(pvt->smoother))) {
+ sco_write(pvt->sco_socket, f->data.ptr, f->datalen);
+ ast_frfree(f);
+ }
+
+ ast_mutex_unlock(&pvt->lock);
+
+ 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;
+
[... 2894 lines stripped ...]
More information about the asterisk-commits
mailing list