Index: build_tools/menuselect-deps.in =================================================================== --- build_tools/menuselect-deps.in (revision 53142) +++ build_tools/menuselect-deps.in (working copy) @@ -1,4 +1,5 @@ ASOUND=@PBX_ALSA@ +BLUETOOTH=@PBX_BLUETOOTH@ CURL=@PBX_CURL@ FREETDS=@PBX_FREETDS@ GSM=@PBX_GSM@ Index: channels/chan_cellphone.c =================================================================== --- channels/chan_cellphone.c (revision 0) +++ channels/chan_cellphone.c (revision 0) @@ -0,0 +1,1309 @@ +/* + * Asterisk -- An open source telephony toolkit. + * + * Copyright (C) 1999 - 2006, Digium, Inc. + * + * Mark Spencer + * + * 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 Cell / Mobile Phone channel driver + * + * \author Dave Bowerman + * + * \ingroup channel_drivers + */ + +/*** MODULEINFO + bluetooth + ***/ + +#include "asterisk.h" + +ASTERISK_FILE_VERSION(__FILE__, "$Revision: 1 $") + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include + +#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" + +#define CEL_CONFIG "cellphone.conf" + +/* Only linear is allowed */ +static int prefformat = AST_FORMAT_SLINEAR; + +static char context[AST_MAX_EXTENSION] = "default"; +static char type[] = "CELL"; + +static int discovery_interval = 60; /* The device discovery interval, default 60 seconds. */ +static int sco_socket; /* This is global so it can be closed on module unload outside of the listener thread */ + +enum cel_state { + CEL_STATE_INIT = 0, + CEL_STATE_INIT1 = 1, + CEL_STATE_INIT2 = 2, + CEL_STATE_INIT3 = 3, + CEL_STATE_INIT4 = 4, + CEL_STATE_INIT5 = 5, + CEL_STATE_IDLE = 6, + CEL_STATE_DIAL = 7, + CEL_STATE_DIAL1 = 8, + CEL_STATE_DIAL2 = 9, + CEL_STATE_OUTGOING = 10, + CEL_STATE_RING = 11, + CEL_STATE_RING2 = 12, + CEL_STATE_RING3 = 13, + CEL_STATE_INCOMING = 14, + CEL_STATE_HANGUP = 15 +}; + +struct cel_pvt { + struct ast_channel *owner; /* Channel we belong to, possibly NULL */ + struct ast_frame fr; /* "null" frame */ + char id[31]; /* The id from cellphone.conf */ + char bdaddr[18]; /* the bdaddr of the device */ + char context[AST_MAX_CONTEXT]; /* the context for incoming calls */ + char connected; /* is it connected? */ + int rfcomm_port; /* rfcomm port number */ + int rfcomm_socket; /* rfcomm socket descriptor */ + int sco_socket; /* sco socket descriptor */ + enum cel_state state; /* monitor thread current state */ + pthread_t monitor_thread; /* monitor thread handle */ + char sco_in_buf[48]; + char sco_out_buf[352]; + char *sco_out_ptr; + int sco_out_len; + char dial_number[AST_MAX_EXTENSION]; /* number for the monitor thread to dial */ + int dial_timeout; + char ciev_call_0[4]; /* dynamically build reponse strings */ + char ciev_call_1[4]; + char ciev_callsetup_0[4]; + char ciev_callsetup_1[4]; + char ciev_callsetup_2[4]; + char ciev_callsetup_3[4]; + AST_LIST_ENTRY(cel_pvt) entry; +}; + +static AST_LIST_HEAD_STATIC(devices, cel_pvt); + +/* The discovery thread */ +static pthread_t discovery_thread = AST_PTHREADT_NULL; +/* The sco listener thread */ +static pthread_t sco_listener_thread = AST_PTHREADT_NULL; + +/* CLI stuff */ +static const char show_usage[] = +"Usage: cell show devices\n" +" Shows the state of Bluetooth Cell / Mobile devices.\n"; + +static const char search_usage[] = +"Usage: cell search\n" +" Searches for Bluetooth Cell / Mobile devices in range.\n"; + +static int do_show_devices(int, int, char **); +static int do_search_devices(int, int, char **); + +static struct ast_cli_entry cel_cli[] = { + {{"cell", "show", "devices", NULL}, do_show_devices, "Show Bluetooth Cell / Mobile devices", show_usage}, + {{"cell", "search", NULL}, do_search_devices, "Search for Bluetooth Cell / Mobile devices", search_usage} +}; + +/* App stuff */ +static char *app_celstatus = "CellStatus"; +static char *celstatus_synopsis = "CellStatus(Device,Variable)"; +static char *celstatus_desc = +"CellStatus(Device,Variable)\n" +" Device - Id of cell phone from cellphone.conf\n" +" Variable - Variable to store status in will be 1-3.\n" +" In order, Disconnected, Connected & Free, Connected & Busy.\n"; + +static struct ast_channel *cel_request(const char *type, int format, void *data, int *cause); +static int cel_call(struct ast_channel *ast, char *dest, int timeout); +static int cel_hangup(struct ast_channel *ast); +static int cel_answer(struct ast_channel *ast); +static int cel_digit_begin(struct ast_channel *ast, char digit); +static int cel_digit_end(struct ast_channel *ast, char digit, unsigned int duration); +static struct ast_frame *cel_read(struct ast_channel *ast); +static int cel_write(struct ast_channel *ast, struct ast_frame *frame); +static int cel_fixup(struct ast_channel *oldchan, struct ast_channel *newchan); +static int cel_devicestate(void *data); + +static int rfcomm_write(struct cel_pvt *pvt, char *buf); +static int rfcomm_read(struct cel_pvt *pvt, char *buf, char flush, int timeout); + +static int sdp_search(char *addr); + +static const struct ast_channel_tech cel_tech = { + .type = "CELL", + .description = "Bluetooth Cellphone Driver", + .capabilities = AST_FORMAT_SLINEAR, + .requester = cel_request, + .call = cel_call, + .hangup = cel_hangup, + .answer = cel_answer, + .send_digit_begin = cel_digit_begin, + .send_digit_end = cel_digit_end, + .read = cel_read, + .write = cel_write, + .fixup = cel_fixup, + .devicestate = cel_devicestate +}; + +static int do_show_devices(int fd, int argc, char **argv) +{ + + struct cel_pvt *pvt; + + #define FORMAT "%-15.15s %-17.17s %-9.9s %-5.5s\n" + + ast_cli(fd, FORMAT, "ID", "Address", "Connected", "State"); + AST_LIST_TRAVERSE(&devices, pvt, entry) { + ast_cli(fd, FORMAT, pvt->id, pvt->bdaddr, pvt->connected?"Yes":"No", (pvt->state == CEL_STATE_IDLE)?"Free":"Busy"); + } + + return RESULT_SUCCESS; + +} + +static int do_search_devices(int fd, int argc, char **argv) +{ + + int hci_socket; + inquiry_info *ii = NULL; + int max_rsp, num_rsp; + int dev_id, len, flags; + int i, port; + char addr[19] = {0}; + char name[31] = {0}; + + #define FORMAT2 "%-17.17s %-30.30s %-6.6s %-4.4s\n" + #define FORMAT3 "%-17.17s %-30.30s %-6.6s %d\n" + + dev_id = hci_get_route(NULL); + hci_socket = hci_open_dev(dev_id); + len = 8; + max_rsp = 255; + flags = IREQ_CACHE_FLUSH; + + ii = (inquiry_info*)malloc(max_rsp * sizeof(inquiry_info)); + num_rsp = hci_inquiry(dev_id, len, max_rsp, NULL, &ii, flags); + if (num_rsp > 0) { + ast_cli(fd, FORMAT2, "Address", "Name", "Usable", "Port"); + for (i = 0; i < num_rsp; i++) { + ba2str(&(ii+i)->bdaddr, addr); + name[0] = 0x00; + if (hci_read_remote_name(hci_socket, &(ii+i)->bdaddr, sizeof(name)-1, name, 0) < 0) + strcpy(name, "[unknown]"); + port = sdp_search(addr); + ast_cli(fd, FORMAT3, addr, name, port?"Yes":"No", port); + } + } else + ast_cli(fd, "No Bluetooth Cell / Mobile devices found.\n"); + + free(ii); + + hci_close_dev(hci_socket); + + return RESULT_SUCCESS; + +} + +static int cel_status_exec(struct ast_channel *ast, void *data) +{ + + struct cel_pvt *pvt; + char *args = NULL, *device = NULL, *variable = NULL; + int stat; + char status[2]; + + if (!data) + return -1; + + args = ast_strdupa((char *)data); + device = strsep(&args, "|"); + if (device && (device[0] != 0x00)) { + variable = args; + } else + return -1; + + stat = 1; + + AST_LIST_TRAVERSE(&devices, pvt, entry) { + if (!strcmp(pvt->id, device)) + break; + } + + if (pvt) { + if (pvt->connected) + stat = 2; + if (pvt->owner) + stat = 3; + } + + sprintf(status, "%d", stat); + pbx_builtin_setvar_helper(ast, variable, status); + + return 0; + +} + + +static struct ast_channel *cel_request(const char *type, int format, void *data, int *cause) +{ + + struct ast_channel *chn = NULL; + struct cel_pvt *pvt; + char *dest_dev = NULL; + char *dest_num = NULL; + int oldformat; + + if (!data) { + ast_log(LOG_WARNING, "Channel requested with no data\n"); + 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); + return NULL; + } + + dest_dev = ast_strdupa((char *)data); + dest_num = strchr(dest_dev, '/'); + if (!dest_num) { + ast_log(LOG_WARNING, "Cant determine destination number.\n"); + return NULL; + } + *dest_num++ = 0x00; + + /* Find requested device and make sure its connected. */ + AST_LIST_TRAVERSE(&devices, pvt, entry) { + if (!strcmp(pvt->id, dest_dev)) { + break; + } + } + 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); + return NULL; + } + + pvt->sco_out_ptr = pvt->sco_out_buf; + pvt->sco_out_len = 0; + + chn = ast_channel_alloc(1, AST_STATE_DOWN, 0, 0, "CELL/%s-%04lx", dest_dev , ast_random() & 0xffff); + + if (!chn) { + ast_log(LOG_WARNING, "Unable to allocate channel structure\n"); + return NULL; + } + + chn->tech = &cel_tech; + chn->nativeformats = prefformat; + chn->rawreadformat = prefformat; + chn->rawwriteformat = prefformat; + chn->writeformat = prefformat; + chn->readformat = prefformat; + chn->tech_pvt = pvt; + ast_copy_string(chn->context, context, sizeof(chn->context)); + ast_copy_string(chn->exten, "s", sizeof(chn->exten)); + ast_string_field_set(chn, language, "en"); + pvt->owner = chn; + + AST_LIST_HEAD_INIT_NOLOCK(&chn->readq); + + return chn; + +} + +static int cel_call(struct ast_channel *ast, char *dest, int timeout) +{ + + struct cel_pvt *pvt; + char *dest_dev = NULL; + char *dest_num = NULL; + + dest_dev = ast_strdupa((char *)dest); + dest_num = strchr(dest_dev, '/'); + if (!dest_num) { + ast_log(LOG_WARNING, "Cant determine destination number.\n"); + return -1; + } + *dest_num++ = 0x00; + + pvt = ast->tech_pvt; + + if ((ast->_state != AST_STATE_DOWN) && (ast->_state != AST_STATE_RESERVED)) { + ast_log(LOG_WARNING, "cel_call called on %s, neither down nor reserved\n", ast->name); + return -1; + } + + if (option_debug) + ast_log(LOG_DEBUG, "Calling %s on %s\n", dest, ast->name); + + ast_copy_string(pvt->dial_number, dest_num, sizeof(pvt->dial_number)); + pvt->state = CEL_STATE_DIAL; + pvt->dial_timeout = timeout == 0 ? 30 : timeout; + + return 0; + +} + +static int cel_hangup(struct ast_channel *ast) +{ + + struct cel_pvt *pvt; + + if (!ast->tech_pvt) { + ast_log(LOG_WARNING, "Asked to hangup channel not connected\n"); + return 0; + } + pvt = ast->tech_pvt; + + if (option_debug) + ast_log(LOG_DEBUG, "Hanging up device %s.\n", pvt->id); + + if (pvt->state == CEL_STATE_INCOMING || pvt->state == CEL_STATE_OUTGOING || pvt->state == CEL_STATE_DIAL1 || pvt->state == CEL_STATE_DIAL2) { + rfcomm_write(pvt, "AT+CHUP\r"); + pvt->state = CEL_STATE_HANGUP; + } else + pvt->state = CEL_STATE_IDLE; + + pvt->owner = NULL; + ast->tech_pvt = NULL; + ast_setstate(ast, AST_STATE_DOWN); + + return 0; + +} + +static int cel_answer(struct ast_channel *ast) +{ + + struct cel_pvt *pvt; + + pvt = ast->tech_pvt; + + rfcomm_write(pvt, "ATA\r"); + + ast_setstate(ast, AST_STATE_UP); + + return 0; + +} + +static int cel_digit_begin(struct ast_channel *chan, char digit) +{ + + return 0; + +} + +static int cel_digit_end(struct ast_channel *ast, char digit, unsigned int duration) +{ + + struct cel_pvt *pvt; + char buf[11]; + + pvt = ast->tech_pvt; + + if (option_debug) + ast_log(LOG_DEBUG, "Dialed %c\n", digit); + + switch(digit) { + case '0': + case '1': + case '2': + case '3': + case '4': + case '5': + case '6': + case '7': + case '8': + case '9': + case '*': + case '#': + sprintf(buf, "AT+VTS=%c\r", digit); + rfcomm_write(pvt, buf); + break; + default: + ast_log(LOG_WARNING, "Unknown digit '%c'\n", digit); + return -1; + } + + return 0; + +} + +/* + + The SCO protocol basically delivers audio in 48 byte 'frames' in slin format. + Here we just package these into an ast frame and return them. + The SCO connection from the device to Asterisk happens asynchronously, so it is feasible + that Asterisk will call cel_read() before the device has connected. In that case we just return + a null frame. + +*/ + +static struct ast_frame *cel_read(struct ast_channel *ast) +{ + + struct cel_pvt *pvt = ast->tech_pvt; + int r; + + if (pvt->state == CEL_STATE_HANGUP) + return &ast_null_frame; + + if (pvt->sco_socket == -1) + return &ast_null_frame; + + pvt->fr.datalen = 0; + pvt->fr.samples = 0; + pvt->fr.data = NULL; + pvt->fr.src = type; + pvt->fr.offset = 0; + pvt->fr.mallocd=0; + pvt->fr.delivery.tv_sec = 0; + pvt->fr.delivery.tv_usec = 0; + + + if ((r = read(pvt->sco_socket, pvt->sco_in_buf, 48)) == 48) { + pvt->fr.datalen = 48; + pvt->fr.frametype = AST_FRAME_VOICE; + pvt->fr.samples = 24; + pvt->fr.offset = 0; + pvt->fr.subclass = AST_FORMAT_SLINEAR; + pvt->fr.data = pvt->sco_in_buf; + return &pvt->fr; + } else if (r == -1) { + pvt->sco_socket = -1; + } else { + if (option_debug) + ast_log(LOG_DEBUG, "cel_read() read short frame.\n"); + } + + return &ast_null_frame; + +} + +/* + + We need to deliver 48 byte 'frames' of slin format audio to the device. + cel_write() handles this by buffering short frames until the next time we are called. + +*/ + +static int cel_write(struct ast_channel *ast, struct ast_frame *frame) +{ + + struct cel_pvt *pvt = ast->tech_pvt; + int num_frames, i, r; + char *pfr; + + if (frame->frametype != AST_FRAME_VOICE) + return 0; + + if (pvt->sco_socket == -1) + return 0; + + if (pvt->state != CEL_STATE_INCOMING && pvt->state != CEL_STATE_OUTGOING) + return 0; + + if (option_debug) { + if (frame->datalen > sizeof(pvt->sco_out_buf) - pvt->sco_out_len) + ast_log(LOG_DEBUG, "Overrun on sco_out_buf detected.\n"); + } + memmove(pvt->sco_out_ptr, frame->data, frame->datalen); + pvt->sco_out_len += frame->datalen; + num_frames = pvt->sco_out_len / 48; + + pfr = pvt->sco_out_buf; + for (i=0; isco_socket, pfr, 48)) != 48) + ast_log(LOG_ERROR, "Write error on %s (%d).\n", ast->name, r); + pfr += 48; + } + + pvt->sco_out_len = pvt->sco_out_len - (num_frames * 48); + memmove(pvt->sco_out_buf, pfr, pvt->sco_out_len); + pvt->sco_out_ptr = pvt->sco_out_buf + pvt->sco_out_len; + + return 0; + +} + +static int cel_fixup(struct ast_channel *oldchan, struct ast_channel *newchan) +{ + + struct cel_pvt *pvt = oldchan->tech_pvt; + + if (pvt && pvt->owner == oldchan) + pvt->owner = newchan; + + return 0; + +} + +static int cel_devicestate(void *data) +{ + + char *device; + int res = AST_DEVICE_INVALID; + struct cel_pvt *pvt; + + device = ast_strdupa(data ? data : ""); + + if (option_debug) + ast_log(LOG_DEBUG, "Checking device state for device %s\n", device); + + AST_LIST_TRAVERSE(&devices, pvt, entry) { + if (!strcmp(pvt->id, device)) + break; + } + + if (pvt) { + if (pvt->connected) { + if (pvt->owner) + res = AST_DEVICE_INUSE; + else + res = AST_DEVICE_NOT_INUSE; + } + } + + return res; + +} + +static int rfcomm_connect(char *bdaddr, int channel) { + + bdaddr_t dst; + struct sockaddr_rc addr; + int s; + + str2ba(bdaddr, &dst); + + if ((s = socket(PF_BLUETOOTH, SOCK_STREAM, BTPROTO_RFCOMM)) < 0) { + if (option_debug) + ast_log(LOG_DEBUG, "socket() failed (%d).\n", errno); + return -1; + } + memset(&addr, 0, sizeof(addr)); + addr.rc_family = AF_BLUETOOTH; + bacpy(&addr.rc_bdaddr, &dst); + addr.rc_channel = channel; + if (connect(s, (struct sockaddr *)&addr, sizeof(addr)) < 0) { + if (option_debug) + ast_log(LOG_DEBUG, "connect() failed (%d).\n", errno); + close(s); + return -1; + } + + return s; + +} + +static int rfcomm_write(struct cel_pvt *pvt, char *buf) +{ + + char *p; + ssize_t num_write; + int len; + + if (option_debug) + ast_log(LOG_DEBUG, "rfcomm_write() (%d) [%s]\n", pvt->rfcomm_socket, buf); + len = strlen(buf); + p = buf; + while (len > 0) { + if ((num_write = write(pvt->rfcomm_socket, p, len)) == -1) { + ast_log(LOG_DEBUG, "rfcomm_write() error [%d]\n", errno); + return 0; + } + len -= num_write; + p += num_write; + } + + return 1; + +} + +/* + + Here we need to return complete '\r' terminated single responses to the devices monitor thread, or + a timeout if nothing is available. + 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 buffering the input and returning one response + per call, or a timeout if nothing is available. + +*/ + +static int rfcomm_read(struct cel_pvt *pvt, char *buf, char flush, int timeout) +{ + + int sel, rlen, slen; + fd_set rfds; + struct timeval tv; + static char qbuf[256] = {0x00}; + char *p; + + if (!flush) { + if ((p = strchr(qbuf, '\n'))) { + *p++ = 0x00; + memmove(buf, qbuf, strlen(qbuf)); + *(buf + strlen(qbuf)) = 0x00; + memmove(qbuf, p, strlen(p)); + *(qbuf+strlen(p)) = 0x00; + return 1; + } + } else { + qbuf[0] = 0x00; + } + + FD_ZERO(&rfds); + FD_SET(pvt->rfcomm_socket, &rfds); + + tv.tv_sec = timeout; + tv.tv_usec = 0; + + if ((sel = select(pvt->rfcomm_socket + 1, &rfds, NULL, NULL, &tv)) > 0) { + if (FD_ISSET(pvt->rfcomm_socket, &rfds)) { + slen = strlen(qbuf); + rlen = read(pvt->rfcomm_socket, qbuf + slen, sizeof(qbuf) - slen - 1); + if (rlen > 0) { + qbuf[slen+rlen] = 0x00; + if ((p = strchr(qbuf, '\n'))) { + *p++ = 0x00; + memmove(buf, qbuf, strlen(qbuf)); + *(buf + strlen(qbuf)) = 0x00; + memmove(qbuf, p, strlen(p)); + *(qbuf+strlen(p)) = 0x00; + return 1; + } + } else + return rlen; + } + } else if (sel == 0) { /* timeout */ + return 0; + } + + return 1; + +} + +/* + + sdp_search() performs a service discovery on the given device to determine whether + or not it supports the Handsfree Profile. + +*/ + +static int sdp_search(char *addr) +{ + + sdp_session_t *session = 0; + bdaddr_t bdaddr; + uuid_t svc_uuid; + uint32_t range = 0x0000ffff; + sdp_list_t *response_list, *search_list, *attrid_list; + int status, port; + sdp_list_t *proto_list; + sdp_list_t *rl; + sdp_record_t *sdprec; + + str2ba(addr, &bdaddr); + port = 0; + session = sdp_connect(BDADDR_ANY, &bdaddr, 0); + if (!session) + return 0; + + sdp_uuid32_create(&svc_uuid, HANDSFREE_AGW_PROFILE_ID); + search_list = sdp_list_append(0, &svc_uuid); + attrid_list = sdp_list_append(0, &range); + response_list = 0x00; + status = sdp_service_search_attr_req(session, search_list, SDP_ATTR_REQ_RANGE, attrid_list, &response_list); + if (status == 0) { + rl = response_list; + while (rl) { + sdprec = (sdp_record_t *) rl->data; + proto_list = 0x00; + if (sdp_get_access_protos(sdprec, &proto_list) == 0) { + port = sdp_get_proto_port(proto_list, RFCOMM_UUID); + sdp_list_free(proto_list, 0); + } + sdp_record_free(sdprec); + rl = rl->next; + } + sdp_list_free(response_list, 0); + } + + sdp_list_free(search_list, 0); + sdp_list_free(attrid_list, 0); + sdp_close(session); + + return port; + +} + +/* + + Monitor Thread + + This thread is spun once a device is discovered and considered capable of being used, i.e. supports Handsfree Profile, + and its configured in cellphone.conf. + The thread lives for the lifetime of the bluetooth connection, and handles the 'control' side of all calls. + +*/ + +static void *do_monitor(void *data) +{ + + struct cel_pvt *pvt = (struct cel_pvt *)data; + struct ast_channel *chn; + char monitor = 1; + char buf[256]; + char cid_num[AST_MAX_EXTENSION], *pcids, *pcide; + int s, t, i; + int group, group2; + int callp, callsetupp; + +//Ericsson does not support BRSF so change this +// if (!rfcomm_write(pvt, "AT+BRSF=4\r")) + if (!rfcomm_write(pvt, "AT+CIND=?\r")) + monitor = 0; + + while (monitor) { + + if (pvt->state == CEL_STATE_DIAL1) + t = 8; + else if (pvt->state == CEL_STATE_DIAL2) + t = pvt->dial_timeout; + else if (pvt->state == CEL_STATE_HANGUP) + t = 2; + else + t = 1; + + s = rfcomm_read(pvt, buf, 0, t); + + if (s > 0) { + if (option_debug) + ast_log(LOG_DEBUG, "rfcomm_read() (%d) [%s]\n", pvt->rfcomm_socket, buf); + switch (pvt->state) { + case 0: +//Ericsson does not support BRSF so change this too +// if (strstr(buf, "+BRSF:")) { + if (strstr(buf, "+CIND:")) { + } + if (strstr(buf, "OK\r")) { + rfcomm_write(pvt, "AT+CIND=?\r"); + pvt->state++; + } + break; + case 1: + if (strstr(buf, "+CIND:")) { + + group = callp = callsetupp = 0; + group2 = 1; + for (i=0; iciev_call_0, "%d,0", callp); + sprintf(pvt->ciev_call_1, "%d,1", callp); + sprintf(pvt->ciev_callsetup_0, "%d,0", callsetupp); + sprintf(pvt->ciev_callsetup_1, "%d,1", callsetupp); + sprintf(pvt->ciev_callsetup_2, "%d,2", callsetupp); + sprintf(pvt->ciev_callsetup_3, "%d,3", callsetupp); + if (option_debug) + ast_log(LOG_DEBUG, "CIEV_CALL=%d CIEV_CALLSETUP=%d\n", callp, callsetupp); + } + if (strstr(buf, "OK\r")) { + rfcomm_write(pvt, "AT+CIND?\r"); + pvt->state++; + } + break; + case 2: + if (strstr(buf, "+CIND:")) { + } + if (strstr(buf, "OK\r")) { + rfcomm_write(pvt, "AT+CMER=3,0,0,1\r"); + pvt->state++; + } + break; + case 3: + if (strstr(buf, "OK\r")) { + rfcomm_write(pvt, "AT+CLIP=1\r"); + pvt->state++; + } + break; + case 4: + if (strstr(buf, "OK\r")) { + rfcomm_write(pvt, "AT+VGS=7\r"); + pvt->state++; + } + break; + case 5: + if (strstr(buf, "OK\r")) { + pvt->state++; + pvt->connected = 1; + if (option_debug) + ast_log(LOG_DEBUG, "Device %s online and ready.\n", pvt->id); + } + break; + case CEL_STATE_IDLE: + if (option_debug) + ast_log(LOG_DEBUG, "Device %s %s [%s]\n", pvt->bdaddr, pvt->id, buf); + if (strstr(buf, "RING\r")) { + pvt->state = CEL_STATE_RING; + } + break; + case CEL_STATE_DIAL: /* Nothing handled here, we need to wait for a timeout */ + break; + case CEL_STATE_DIAL1: + if (strstr(buf, "OK\r")) { + pvt->state = CEL_STATE_DIAL2; + } + break; + case CEL_STATE_DIAL2: + if (strstr(buf, "+CIEV")) { + if (strstr(buf, pvt->ciev_callsetup_3)) { /* remote party ringing */ + ast_setstate(pvt->owner, AST_STATE_RINGING); + ast_queue_control(pvt->owner, AST_CONTROL_ANSWER); + } + if (strstr(buf, pvt->ciev_call_0)) { /* disconnected for some reason */ + ast_queue_control(pvt->owner, AST_CONTROL_HANGUP); + } + if (strstr(buf, pvt->ciev_call_1)) { + pvt->state = CEL_STATE_OUTGOING; + } + } + break; + case CEL_STATE_OUTGOING: + if (strstr(buf, "+CIEV")) { + if (strstr(buf, pvt->ciev_call_0)) { /* call was hung up */ + ast_queue_control(pvt->owner, AST_CONTROL_HANGUP); + } + } + break; + case CEL_STATE_RING: + cid_num[0] = 0x00; + if ((pcids = strstr(buf, "+CLIP:"))) { + if ((pcids = strchr(pcids, '"'))) { + if ((pcide = strrchr(pcids, '"'))) { + strncpy(cid_num, pcids+1, pcide - pcids - 1); + cid_num[pcide - pcids - 1] = 0x00; + } + } + pvt->state = CEL_STATE_RING2; + } + break; + case CEL_STATE_RING2: + pvt->sco_out_ptr = pvt->sco_out_buf; + pvt->sco_out_len = 0; + chn = ast_channel_alloc(1, AST_STATE_RING, 0, 0, "CELL/%s-%04lx", pvt->id , ast_random() & 0xffff); + if (chn) { + chn->tech = &cel_tech; + chn->nativeformats = prefformat; + chn->rawreadformat = prefformat; + chn->rawwriteformat = prefformat; + chn->writeformat = prefformat; + chn->readformat = prefformat; + chn->tech_pvt = pvt; + chn->rings = 1; + ast_copy_string(chn->context, pvt->context, sizeof(chn->context)); + ast_copy_string(chn->exten, "s", sizeof(chn->exten)); + ast_string_field_set(chn, language, "en"); + chn->cid.cid_num = ast_strdup(cid_num); + chn->cid.cid_name = ast_strdup(pvt->id); + pvt->owner = chn; + if (ast_pbx_start(chn)) { + ast_log(LOG_ERROR, "Unable to start pbx on incoming call.\n"); + ast_hangup(chn); + } else + pvt->state = CEL_STATE_RING3; + } else { + ast_log(LOG_ERROR, "Unable to allocate channel for incoming call.\n"); + rfcomm_write(pvt, "AT+CHUP\r"); + pvt->state = CEL_STATE_IDLE; + } + break; + case CEL_STATE_RING3: + if (strstr(buf, "+CIEV")) { + if (strstr(buf, pvt->ciev_call_1) && pvt->owner) { /* We answered */ + pvt->state = CEL_STATE_INCOMING; + } + if (strstr(buf, pvt->ciev_callsetup_0) && pvt->owner) { /* Caller disconnected */ + ast_queue_control(pvt->owner, AST_CONTROL_HANGUP); + } + } + break; + case CEL_STATE_INCOMING: + if (strstr(buf, "+CIEV")) { + if (strstr(buf, pvt->ciev_call_0) && pvt->owner) { + ast_queue_control(pvt->owner, AST_CONTROL_HANGUP); + } + } + break; + case CEL_STATE_HANGUP: + if (strstr(buf, "OK\r") || strstr(buf, pvt->ciev_call_0)) { + pvt->state = CEL_STATE_IDLE; + } + break; + } + } else if (s == 0) { /* Timeouts */ + if (pvt->state == 2) { /* Some devices dont respond to AT+CIND? properly. RIM Blackberry 4 example */ + pvt->state++; + rfcomm_write(pvt, "AT+CMER=3,0,0,1\r"); + } else if (pvt->state == CEL_STATE_DIAL) { + sprintf(buf, "ATD%s;\r", pvt->dial_number); + if (!rfcomm_write(pvt, buf)) { + ast_log(LOG_ERROR, "Dial failed on %s state %d\n", pvt->owner->name, pvt->state); + ast_queue_control(pvt->owner, AST_CONTROL_CONGESTION); + pvt->state = CEL_STATE_IDLE; + } else { + pvt->state = CEL_STATE_DIAL1; + } + } else if (pvt->state == CEL_STATE_DIAL1) { + ast_log(LOG_ERROR, "Dial failed on %s state %d\n", pvt->owner->name, pvt->state); + ast_queue_control(pvt->owner, AST_CONTROL_CONGESTION); + pvt->state = CEL_STATE_IDLE; + } else if (pvt->state == CEL_STATE_DIAL2) { + ast_log(LOG_ERROR, "Dial failed on %s state %d\n", pvt->owner->name, pvt->state); + ast_queue_control(pvt->owner, AST_CONTROL_HANGUP); + } else if (pvt->state == CEL_STATE_RING) { /* No CLIP?, bump it */ + pvt->state = CEL_STATE_RING2; + } else if (pvt->state == CEL_STATE_HANGUP) { + rfcomm_write(pvt, "AT+CHUP\r"); + } + } else if (s == -1) { + if (option_verbose > 2) + ast_verbose(VERBOSE_PREFIX_3 "Bluetooth Device %s has disconnected.\n", pvt->id); + monitor = 0; + } + + } + + close(pvt->rfcomm_socket); + close(pvt->sco_socket); + pvt->sco_socket = -1; + pvt->connected = 0; + pvt->monitor_thread = AST_PTHREADT_NULL; + + return NULL; + +} + +static int start_monitor(struct cel_pvt *pvt) +{ + + if (ast_pthread_create_background(&pvt->monitor_thread, NULL, do_monitor, pvt) < 0) { + pvt->monitor_thread = AST_PTHREADT_NULL; + return 0; + } + + return 1; + +} + +/* + + Device Discovery Thread. + + This thread wakes every 'discovery_interval' seconds and trys to connect to + those configured devices which are not connected. This saves the cell phone user + from having to manually connect his/her cell phone to the asterisk box. + Once a successful connection is made, a monitor thread is spun on the device which + lives for the lifetime of the connection. + +*/ + +static void *do_discovery(void *data) +{ + + struct cel_pvt *pvt = data; + + for (;;) { + AST_LIST_TRAVERSE(&devices, pvt, entry) { + if (!pvt->connected) { + if ((pvt->rfcomm_socket = rfcomm_connect(pvt->bdaddr, pvt->rfcomm_port)) > -1) { + pvt->state = 0; + if (start_monitor(pvt)) { + pvt->connected = 1; + if (option_verbose > 2) + ast_verbose(VERBOSE_PREFIX_3 "Bluetooth Device %s has connected.\n", pvt->id); + } + } + } + } + /* Go to sleep */ + sleep(discovery_interval); + } + + return NULL; +} + +/* + + This thread listens for incoming sco connections. + Although the Bluetooth Handsfree Profile Specification says that either end may initiate the audio connection, + in practice some devices (LG TU500) get upset unless they initiate the connection. + We leave all sco initiation to the device. + On an inbound sco connection, we need to find the appropriate device, and set the channel fd accordingly. + +*/ + +static void *do_sco_listen(void *data) +{ + + int ns; + bdaddr_t local; + struct sockaddr_sco addr; + char saddr[18]; + socklen_t addrlen; + struct cel_pvt *pvt; + + hci_devba(0, &local); + + if ((sco_socket = socket(PF_BLUETOOTH, SOCK_SEQPACKET, BTPROTO_SCO)) < 0) { + ast_log(LOG_ERROR, "Unable to create sco listener socket.\n"); + return NULL; + } + memset(&addr, 0, sizeof(addr)); + addr.sco_family = AF_BLUETOOTH; + bacpy(&addr.sco_bdaddr, &local); + if (bind(sco_socket, (struct sockaddr *)&addr, sizeof(addr)) < 0) { + ast_log(LOG_ERROR, "Unable to bind sco listener socket.\n"); + close(sco_socket); + return NULL; + } + if (listen(sco_socket, 5) < 0) { + ast_log(LOG_ERROR, "Unable to listen sco listener socket.\n"); + close(sco_socket); + return NULL; + } + while (1) { + addrlen = sizeof(struct sockaddr); + if ((ns = accept(sco_socket, (struct sockaddr *)&addr, &addrlen)) > -1) { + ba2str(&addr.sco_bdaddr, saddr); + if (option_debug) + ast_log(LOG_DEBUG, "Incoming Audio Connection from device %s\n", saddr); + pvt = NULL; + AST_LIST_TRAVERSE(&devices, pvt, entry) { + if (!strcmp(pvt->bdaddr, saddr)) + break; + } + if (pvt) { + if (pvt->sco_socket != -1) + close(pvt->sco_socket); + pvt->sco_socket = ns; + if (pvt->owner) { + ast_channel_lock(pvt->owner); + pvt->owner->fds[0] = ns; + ast_channel_unlock(pvt->owner); + if (option_debug) + ast_log(LOG_DEBUG, "Set Audio Connection fd OK.\n"); + } else if (option_debug) { + ast_log(LOG_DEBUG, "Channel NOT in correct state for incoming sco connection.\n"); + } + } else if (option_debug) + ast_log(LOG_DEBUG, "Could not find device for incoming Audio Connection.\n"); + } + } + + return NULL; + +} + +static int cel_load_config(void) +{ + + struct ast_config *cfg = NULL; + char *cat = NULL; + struct ast_variable *var; + const char *address, *port, *context; + struct cel_pvt *pvt; + + cfg = ast_config_load(CEL_CONFIG); + if (!cfg) + return 0; + + for (var = ast_variable_browse(cfg, "general"); var; var = var->next) { + if (!strcasecmp(var->name, "interval")) + discovery_interval = atoi(var->value); + } + + cat = ast_category_browse(cfg, NULL); + while (cat) { + if (strcasecmp(cat, "general")) { + if (option_debug) + ast_log(LOG_DEBUG, "Loading device %s.\n", cat); + address = ast_variable_retrieve(cfg, cat, "address"); + port = ast_variable_retrieve(cfg, cat, "port"); + context = ast_variable_retrieve(cfg, cat, "context"); + if (address && port) { + if ((pvt = ast_malloc(sizeof(struct cel_pvt)))) { + ast_copy_string(pvt->id, cat, sizeof(pvt->id)); + ast_copy_string(pvt->bdaddr, address, sizeof(pvt->bdaddr)); + if (context) + ast_copy_string(pvt->context, context, sizeof(pvt->context)); + else + ast_copy_string(pvt->context, "default", sizeof(pvt->context)); + pvt->connected = 0; + pvt->state = CEL_STATE_INIT; + pvt->rfcomm_socket = -1; + pvt->rfcomm_port = atoi(port); + pvt->sco_socket = -1; + pvt->monitor_thread = AST_PTHREADT_NULL; + pvt->owner = NULL; + AST_LIST_INSERT_HEAD(&devices, pvt, entry); + } + } else { + ast_log(LOG_ERROR, "Device %s has no address/port configured. It wont be enabled.\n", cat); + } + } + cat = ast_category_browse(cfg, cat); + } + + ast_config_destroy(cfg); + + return 1; + +} + +static int reload_module(void) +{ + + return 0; + +} + +static int unload_module(void) +{ + + struct cel_pvt *pvt; + + /* First, take us out of the channel loop */ + ast_channel_unregister(&cel_tech); + + /* Kill the discovery thread */ + if (discovery_thread != AST_PTHREADT_NULL) { + pthread_cancel(discovery_thread); + pthread_join(discovery_thread, NULL); + } + /* Kill the sco listener thread */ + if (sco_listener_thread != AST_PTHREADT_NULL) { + pthread_cancel(sco_listener_thread); + pthread_join(sco_listener_thread, NULL); + } + if ((close(sco_socket) == -1)) + ast_log(LOG_ERROR, "Unable to close sco_socket %d.\n", errno); + + /* Unregister the CLI & APP */ + ast_cli_unregister_multiple(cel_cli, sizeof(cel_cli) / sizeof(cel_cli[0])); + ast_unregister_application(app_celstatus); + + /* Destroy the device list */ + while ((pvt = AST_LIST_REMOVE_HEAD(&devices, entry))) { + if (pvt->rfcomm_socket > -1) { + close(pvt->rfcomm_socket); + } + if (pvt->monitor_thread != AST_PTHREADT_NULL) { + pthread_cancel(pvt->monitor_thread); + pthread_join(pvt->monitor_thread, NULL); + } + free(pvt); + } + + return 0; + +} + +static int load_module(void) +{ + + int dev_id, s; + + /* Check if we have Bluetooth, no point loading otherwise... */ + dev_id = hci_get_route(NULL); + s = hci_open_dev(dev_id); + if (dev_id < 0 || s < 0) { + ast_log(LOG_ERROR, "No Bluetooth device found. Not loading module.\n"); + return AST_MODULE_LOAD_DECLINE; + } + hci_close_dev(s); + + if (!cel_load_config()) { + ast_log(LOG_ERROR, "Unable to read config file %s. Not loading module.\n", CEL_CONFIG); + return AST_MODULE_LOAD_DECLINE; + } + + /* Spin the discovery thread */ + if (ast_pthread_create_background(&discovery_thread, NULL, do_discovery, NULL) < 0) { + ast_log(LOG_ERROR, "Unable to create discovery thread.\n"); + return AST_MODULE_LOAD_DECLINE; + } + /* Spin the sco listener thread */ + if (ast_pthread_create_background(&sco_listener_thread, NULL, do_sco_listen, NULL) < 0) { + ast_log(LOG_ERROR, "Unable to create sco listener thread.\n"); + pthread_cancel(discovery_thread); + pthread_join(discovery_thread, NULL); + return AST_MODULE_LOAD_DECLINE; + } + + ast_cli_register_multiple(cel_cli, sizeof(cel_cli) / sizeof(cel_cli[0])); + ast_register_application(app_celstatus, cel_status_exec, celstatus_synopsis, celstatus_desc); + + /* Make sure we can register our channel type */ + if (ast_channel_register(&cel_tech)) { + ast_log(LOG_ERROR, "Unable to register channel class %s\n", type); + return -1; + } + return 0; +} + +AST_MODULE_INFO(ASTERISK_GPL_KEY, AST_MODFLAG_DEFAULT, "Bluetooth Cellphone Driver", + .load = load_module, + .unload = unload_module, + .reload = reload_module, +); Index: configure.ac =================================================================== --- configure.ac (revision 53142) +++ configure.ac (working copy) @@ -182,6 +182,7 @@ # BKTR is used for backtrace support on platforms that do not # have it natively. AST_EXT_LIB_SETUP([BKTR], [Stack Backtrace support], [execinfo]) +AST_EXT_LIB_SETUP([BLUETOOTH], [Bluetooth Support], [bluetooth]) AST_EXT_LIB_SETUP([CAP], [POSIX 1.e capabilities], [cap]) AST_EXT_LIB_SETUP([CURL], [cURL], [curl]) AST_EXT_LIB_SETUP([CURSES], [curses], [curses]) @@ -344,6 +345,8 @@ AST_EXT_LIB_CHECK([ALSA], [asound], [snd_spcm_init], [alsa/asoundlib.h], [-lm -ldl]) +AST_EXT_LIB_CHECK([BLUETOOTH], [bluetooth], [ba2str], [bluetooth/bluetooth.h]) + AST_EXT_LIB_CHECK([CURSES], [curses], [initscr], [curses.h]) if test "x${host_os}" = "xlinux-gnu" ; then Index: doc/cellphone.txt =================================================================== --- doc/cellphone.txt (revision 0) +++ doc/cellphone.txt (revision 0) @@ -0,0 +1,196 @@ +chan_cellphone + +Asterisk Channel Driver to allow Bluetooth Cell/Mobile Phones to be used as FXO devices. + +Features :- + +Multiple cell phones can be connected. +Asterisk automatically connects to each cell phone when it comes in range. +Command to discover bluetooth devices. Useful for configuration +Inbound calls to the cell phones are handled by Asterisk, just like inbound calls on a Zap channel. +CLI passed through on inbound calls. +Dial outbound on a cell phone using Dial(CELL/device/nnnnnnn) in the dialplan. +Application CellStatus can be used in the dialplan to see if a cell phone is connected. +Supports devicestate for dialplan hinting. + +Using chan_cellphone :- + +In order to use chan_cellphone, you must have a working bluetooth subsystem on your Asterisk box. +This means a working bluetooth adapter, and the Blue-Z packages. + +Any bluetooth adapter supported by the Linux kernel will do, including usb bluetooth dongles. + +The Blue-Z package you need is bluez-utils and your package manager will probably want to install bluez-pin also. +You also need libbluetooth and libbluetooth-dev if you are compiling Asterisk from source. + +There are sample bluetooth config files in the Asterisk source directory under contrib/bluetooth. I suggest you +use the sample hcid.conf and pinhelper script. Both of these go in /etc/bluetooth and will set your Asterisk box +up with a bluetooth name of "Asterisk PBX" and a default pin of "0000". (change this if you want). Dont forget to +make the /etc/bluetooth/pinhelper script executable. + +See www.bluez.org for other details about setting up Bluetooth under Linux. + +Assuming you have bluetooth working ok:- + +Load chan_cellphone.so + +Search for your Bluetooth Cell Phones using the CLI command 'cell search'. Be patient with this command as +it will take 8 - 10 seconds to do the discovery. + +Also, if this is the first time Asterisk has talked to phone via bluetooth, your phone may/will prompt you to +enter a PIN. Use the number in the /etc/bluetooth/pinhelper script. "0000" is the default. + +This will return something like the following :- + +*CLI> cell search +Address Name Usable Port +00:0F:86:0E:AE:42 Daves Blackberry Yes 2 +00:12:56:90:6E:00 LG TU500 Yes 4 +00:17:44:55:66:12 Toaster No 0 + +This is a list of all bluetooth devices seen and whether or not they are usable with chan_cellphone. +The Address field contains the 'bd address' of the cell phone. This is like an ethernet mac address. +The Name field is whatever is configured into the phone as its name. +The Usable field tells you whether or not the devices supports the Bluetooth Handsfree Profile, and +if so which 'rfcomm port number it uses'. If this field is 'No' (like Toaster in the example above) then +the device is not usable. + +Choose which device(s) you want to use and edit /etc/asterisk/cellphone.conf. There is a sample included +with the Asterisk source under configs/cellphone.conf.sample. + +Assuming we want to use both devices above, cellphone.conf needs to look like this :- + +=================================================================================== +; +; cellphone.conf +; + +[general] +interval=60 ; Number of seconds between trying to connect to devices. + +; The following is a list of the devices we deal with. +; Every device listed below will be available for calls in and out of Asterisk. +; Discovered devices not in this list are not available. +; Use the CLI command 'cell search' to discover devices. +; Use the CLI command 'cell show devices' to see device status. +; +; To place a call use Dial(CELL/[device]/NNN.....) in your dialplan. + +[dave] +address=00:12:56:90:6E:00 +port=4 +context=incoming-mobile + +[blackberry] +address=00:0F:86:0E:AE:42 +port=2 +context=incoming-mobile +=================================================================================== + +Be sure to configure the right bd address and port number from the search. If you want inbound +calls on a device to go to a specific context, add a context= line, otherwise the default will +be used. The 'id' of the device [bitinbrackets] can be anything you like, just make the unique. + +Having done this, unload chan_cellphone and load it again. + +The CLI command 'cell show devices' can be used at any time to show the status of configured devices. + +*CLI> cell show devices +ID Address Connected State +blackberry 00:0F:86:0E:AE:42 Yes Free +dave 00:12:56:90:6E:00 Yes Free +*CLI> + + +All being well Asterisk will now try and establish a connection to each configured device. If it cant +it will retry after 'interval' seconds, infinately. + +This means that as your cell phone comes into range and goes out of range, Asterisk will automatically +connect and disconnect from it. You dont need to worry about it. + +The first time Asterisk trys to connect to your cell phone, probably as a result of the 'cell search' +command, the bluetooth subsystem will ask for a PIN, this will be what ever value is in the pinhelper +script above. This happens only once, and is a 'security mechanism' meaning you need to know the pin +in order to connect your cell phone to the Asterisk box. + +As each phone is connected you will see a message on the Asterisk console :- + + Loaded chan_cellphone.so => (Bluetooth Cellphone Driver) + -- Bluetooth Device blackberry has connected. + -- Bluetooth Device dave has connected. + +If someone calls your cell phone now, Asterisk will handle the call and it will be sent into the +context you specified, or the default context. Mostly likely this means some SIP phone somewhere will +ring, pick it up and take the call. + +To make outbound calls, add something to you Dialplan like the following :- (modify to suit) + +; Calls via TU500 +exten => _9X.,1,Dial(CELL/dave/${EXTEN:1},45,tT) +exten => _9X.,n,Hangup +; Calls via Blackberry +exten => _8X.,1,Dial(CELL/blackberry/${EXTEN:1},45,tT) +exten => _8X.,n,Hangup + +Pick up a SIP phone and dial 9 and the call vill go via the device 'dave' in +cellphone.conf. + + +Dialplan hints :- + +chan_cellphone supports 'device status' so you can do somthing like + +exten => 1234,hint,SIP/30&CELL/dave&CELL/blackberry + + +CellStatus Application :- + +chan_cellphone also registers and application named CellStatus. You can use this in your Dialplan +to determine the 'state' of a cell phone. + +For example, suppose you wanted to call dave's extension, but only if he was in the office. You could +test to see if his cell phone was attached to Asterisk, if it is dial his extension, otherwise dial his +cell phone. + +exten => 40,1,CellStatus(dave,DAVECELL) +exten => 40,2,GotoIf($["${DAVECELL}" = "1"]?3:5) +exten => 40,3,Dial(ZAP/g1/0427466412,45,tT) +exten => 40,4,Hangup +exten => 40,5,Dial(SIP/40,45,tT) +exten => 40,6,Hangup + +CellStatus sets the value of the given variable to :- + +1 = Disconnected. i.e. Cell Phone not in range of Asterisk, or turned off etc etc +2 = Connected and Not on a call. i.e. Free +3 = Connected and on a call. i.e. Busy + + +Debugging :- + +Different phone manufacturers have different interpretations of the Bluetooth Handsfree Profile Spec. +This means that not all phones work the same way, particularly in the connection setup / initialisation +sequence. I've tried to make chan_cellphone as general as possible, but it may need modification to +support some phone i've never tested. + +The RIM Blackberry 7250 works extremely well. +The LG TU500 WCDMA 3G phone works mostly, be seems to do strange things randomly. + +If your phone doesnt behave has expected, turn on Asterisk debugging with 'core set debug 3'. + +This will log a bunch of debug messages indicating what the phone is doing, importantly the rfcomm +conversation between Asterisk and the phone. This can be used to sort out what your phone is doing +and make chan_cellphone support it. + +Be aware also, that just about all cell phones behave differently. For example my LG TU500 wont dial unless +the phone is a the 'idle' screen. i.e. if the phone is showing a 'menu' on the display, when you dial via +Asterisk, the call will not work. chan_cellphone handles this, but there may be other phones that do +other things too... + +Important: Watch what your cell phone is doing the first few times. Asterisk wont make random calls but +if chan_cellphone fails to hangup for some reason and you get a huge bill from your telco, dont blame me. + + +Feedback, Support, Please can you make Cell Phone X work... etc :- + +email me at david.bowerman at gmail.com or dseeb_ on #asterisk & #asterisk-dev irc. Index: makeopts.in =================================================================== --- makeopts.in (revision 53142) +++ makeopts.in (working copy) @@ -64,6 +64,9 @@ ASOUND_INCLUDE=@ALSA_INCLUDE@ ASOUND_LIB=@ALSA_LIB@ +BLUETOOTH_INCLUDE=@BLUETOOTH_INCLUDE@ +BLUETOOTH_LIB=@BLUETOOTH_LIB@ + CURL_INCLUDE=@CURL_INCLUDE@ CURL_LIB=@CURL_LIB@ Index: configs/cellphone.conf.sample =================================================================== --- configs/cellphone.conf.sample (revision 0) +++ configs/cellphone.conf.sample (revision 0) @@ -0,0 +1,24 @@ +; +; cellphone.conf +; + +[general] +interval=60 ; Number of seconds between trying to connect to devices. + +; The following is a list of the devices we deal with. +; Every device listed below will be available for calls in and out of Asterisk. +; Discovered devices not in this list are not available. +; Use the CLI command 'cell search' to discover devices. +; Use the CLI command 'cell show devices' to see device status. +; +; To place a call use Dial(CELL/[device]/NNN.....) in your dialplan. + +;[dave] +address=00:12:56:90:6E:00 +port=4 +context=incoming-mobile + +;[blackberry] +address=00:0F:86:0E:AE:42 +port=2 +context=incoming-mobile Index: contrib/bluettooth/pinhelper =================================================================== --- contrib/bluettooth/pinhelper (revision 0) +++ contrib/bluettooth/pinhelper (revision 0) @@ -0,0 +1,2 @@ +#!/bin/sh +echo "PIN:0000" Property changes on: contrib/bluettooth/pinhelper ___________________________________________________________________ Name: svn:executable + * Index: contrib/bluettooth/hcid.conf =================================================================== --- contrib/bluettooth/hcid.conf (revision 0) +++ contrib/bluettooth/hcid.conf (revision 0) @@ -0,0 +1,67 @@ +# +# HCI daemon configuration file. +# +# $Id: hcid.conf,v 1.7 2004/12/13 14:16:03 holtmann Exp $ +# + +# HCId options +options { + # Automatically initialize new devices + autoinit yes; + + # Security Manager mode + # none - Security manager disabled + # auto - Use local PIN for incoming connections + # user - Always ask user for a PIN + # + security auto; + + # Pairing mode + # none - Pairing disabled + # multi - Allow pairing with already paired devices + # once - Pair once and deny successive attempts + pairing multi; + + # PIN helper + pin_helper /etc/bluetooth/pinhelper; + + # D-Bus PIN helper + #dbus_pin_helper; +} + +# Default settings for HCI devices +device { + # Local device name + # %d - device id + # %h - host name + # name "%h-%d"; + name "Asterisk PBX"; + + # Local device class + class 0x3e0100; + + # Default packet type + #pkt_type DH1,DM1,HV1; + + # Inquiry and Page scan + iscan enable; pscan enable; + + # Default link mode + # none - no specific policy + # accept - always accept incoming connections + # master - become master on incoming connections, + # deny role switch on outgoing connections + lm accept; + + # Default link policy + # none - no specific policy + # rswitch - allow role switch + # hold - allow hold mode + # sniff - allow sniff mode + # park - allow park mode + lp rswitch,hold,sniff,park; + + # Authentication and Encryption (Security Mode 3) + auth enable; + encrypt enable; +}