summaryrefslogtreecommitdiff
path: root/deps
diff options
context:
space:
mode:
authorMagnus Feuer <mfeuer@jaguarlandrover.com>2015-06-06 10:36:06 -0700
committerMagnus Feuer <mfeuer@jaguarlandrover.com>2015-06-09 13:53:55 -0700
commita13fd36530c1287ac4a3371cb44841de74333a25 (patch)
tree01df9d03094b4582c1157ac73585a47ab50fb0b4 /deps
parente23ca6ea6aae0c1525c4e783ebbff3781f1e5de4 (diff)
downloadrvi_core-a13fd36530c1287ac4a3371cb44841de74333a25.tar.gz
Bumped to 0.4.0. Added bt deps.
Diffstat (limited to 'deps')
-rw-r--r--deps/bt/.gitignore1
-rw-r--r--deps/bt/README.md5
-rw-r--r--deps/bt/c_src/.gitignore2
-rw-r--r--deps/bt/c_src/bt_drv.h96
-rw-r--r--deps/bt/c_src/bt_linux_drv.c1088
-rw-r--r--deps/bt/c_src/bt_macos_drv.c3066
-rw-r--r--deps/bt/c_src/bt_poll.c142
-rw-r--r--deps/bt/c_src/bt_poll.h17
-rw-r--r--deps/bt/c_src/bt_sub.c236
-rw-r--r--deps/bt/c_src/bt_sub.h82
-rw-r--r--deps/bt/c_src/hci_drv.c1009
-rw-r--r--deps/bt/ebin/.gitignore2
-rw-r--r--deps/bt/include/bt.hrl67
-rw-r--r--deps/bt/include/hci_drv.hrl260
-rw-r--r--deps/bt/include/obex.hrl146
-rw-r--r--deps/bt/include/sdp.hrl262
-rw-r--r--deps/bt/include/uuid.hrl52
-rw-r--r--deps/bt/priv/.gitignore2
-rw-r--r--deps/bt/priv/hci.def2250
-rw-r--r--deps/bt/rebar.config37
-rw-r--r--deps/bt/src/bt.app.src11
-rw-r--r--deps/bt/src/bt.erl355
-rw-r--r--deps/bt/src/bt_app.erl33
-rw-r--r--deps/bt/src/bt_drv.erl1398
-rw-r--r--deps/bt/src/bt_erl_server.erl108
-rw-r--r--deps/bt/src/bt_iset.erl457
-rw-r--r--deps/bt/src/bt_make_hci_api.erl408
-rw-r--r--deps/bt/src/bt_sdp.erl1108
-rw-r--r--deps/bt/src/bt_sdp_srv.erl608
-rw-r--r--deps/bt/src/bt_sup.erl47
-rw-r--r--deps/bt/src/bt_util.erl101
-rw-r--r--deps/bt/src/hci.erl307
-rw-r--r--deps/bt/src/hci_api.erl1462
-rw-r--r--deps/bt/src/hci_api.hrl1935
-rw-r--r--deps/bt/src/hci_drv.erl514
-rw-r--r--deps/bt/src/hci_socket.erl314
-rw-r--r--deps/bt/src/hci_test.erl48
-rw-r--r--deps/bt/src/hci_util.erl221
-rw-r--r--deps/bt/src/l2cap.erl44
-rw-r--r--deps/bt/src/obex.erl1696
-rw-r--r--deps/bt/src/obex_bip.erl243
-rw-r--r--deps/bt/src/obex_dm_server.erl140
-rw-r--r--deps/bt/src/obex_generic.erl293
-rw-r--r--deps/bt/src/obex_rfcomm.erl254
-rw-r--r--deps/bt/src/obex_sync_server.erl137
-rw-r--r--deps/bt/src/obex_tcp.erl224
-rw-r--r--deps/bt/src/rfcomm.erl53
47 files changed, 21341 insertions, 0 deletions
diff --git a/deps/bt/.gitignore b/deps/bt/.gitignore
new file mode 100644
index 0000000..b25c15b
--- /dev/null
+++ b/deps/bt/.gitignore
@@ -0,0 +1 @@
+*~
diff --git a/deps/bt/README.md b/deps/bt/README.md
new file mode 100644
index 0000000..3c32f92
--- /dev/null
+++ b/deps/bt/README.md
@@ -0,0 +1,5 @@
+Bluetooth API for Erlang
+========================
+
+Currently mac os x only.
+
diff --git a/deps/bt/c_src/.gitignore b/deps/bt/c_src/.gitignore
new file mode 100644
index 0000000..6142305
--- /dev/null
+++ b/deps/bt/c_src/.gitignore
@@ -0,0 +1,2 @@
+*.o
+*.d
diff --git a/deps/bt/c_src/bt_drv.h b/deps/bt/c_src/bt_drv.h
new file mode 100644
index 0000000..623f77e
--- /dev/null
+++ b/deps/bt/c_src/bt_drv.h
@@ -0,0 +1,96 @@
+#ifndef __BT_DRV_H__
+#define __BT_DRV_H__
+
+#include <stdint.h>
+
+#include "bt_sub.h"
+
+#define CMD_PING 1
+#define CMD_RECENT_DEVICES 2
+#define CMD_PAIRED_DEVICES 3
+#define CMD_FAVORITE_DEVICES 4
+#define CMD_INQUIRY_START 5
+#define CMD_INQUIRY_STOP 6
+#define CMD_REMOTE_NAME 7
+#define CMD_CONNECT 8
+#define CMD_DISCONNECT 9
+#define CMD_DEVICE_INFO 10
+#define CMD_SERVICE_INFO 11
+#define CMD_SERVICE_QUERY 12
+#define CMD_SERVICE_ADD 13
+#define CMD_SERVICE_DEL 14
+#define CMD_SERVICE_RFCOMM 15
+#define CMD_LOCAL_INFO 16
+#define CMD_DEBUG 17
+
+/* RCCOMM Channels */
+#define CMD_RFCOMM_OPEN 20
+#define CMD_RFCOMM_CLOSE 21
+#define CMD_RFCOMM_LISTEN 22
+#define CMD_RFCOMM_SEND 23
+#define CMD_RFCOMM_ACCEPT 24
+#define CMD_RFCOMM_MTU 25
+#define CMD_RFCOMM_ADDRESS 26
+#define CMD_RFCOMM_CHANNEL 27
+
+/* L2CAP */
+#define CMD_L2CAP_OPEN 30
+#define CMD_L2CAP_CLOSE 31
+#define CMD_L2CAP_LISTEN 32
+#define CMD_L2CAP_SEND 33
+#define CMD_L2CAP_ACCEPT 34
+#define CMD_L2CAP_MTU 35
+#define CMD_L2CAP_ADDRESS 36
+#define CMD_L2CAP_PSM 37
+
+/* device info codes */
+#define NFO_DEVICE_NAME 1 /* string */
+#define NFO_DEVICE_CLASS 2 /* uint32 */
+#define NFO_DEVICE_CLOCK 3 /* uint16 */
+#define NFO_DEVICE_INQUIRY 4 /* date */
+#define NFO_DEVICE_ACCESS 5 /* date */
+#define NFO_DEVICE_UPDATE 6 /* date */
+#define NFO_DEVICE_IS_FAVORITE 7 /* Boolean */
+#define NFO_DEVICE_IS_PAIRED 8 /* Boolean */
+#define NFO_DEVICE_IS_CONNECTED 9 /* Boolean */
+
+/* local info codes */
+#define NFO_LOCAL_NAME 1 /* string */
+#define NFO_LOCAL_CLASS 2 /* uint32 */
+#define NFO_LOCAL_ADDRESS 3 /* addr */
+#define NFO_LOCAL_DISCOVERABLE 4 /* Boolean */
+#define NFO_LOCAL_POWER_STATE 5 /* on | off */
+/* add more */
+
+#define REPLY_OK 1
+#define REPLY_ERROR 2
+#define REPLY_EVENT 3
+
+/* extension data types */
+#define ADDR 100 /* bluetooth address 6 bytes */
+#define DATE 101 /* uint32 seconds since 1970 unix-time */
+
+typedef struct _bt_ctx_t
+{
+ size_t pbuf_len; // number of bytes in pbuf
+ uint8_t pbuf[4]; // packet length bytes
+ const uint8_t* ptr; // data ptr
+ size_t len; // length of data
+ size_t remain; // remaining bytes to read
+ uint8_t* packet; // the data packet being built
+ subscription_list_t list;
+ void* drv_data; // Extra, per-driver specific data.
+} bt_ctx_t;
+
+#define LISTEN_QUEUE_LENGTH 8 /* max connections can only be 7 ? */
+
+typedef struct {
+ bt_ctx_t* ctx; // access to subscription list
+ int qh;
+ int qt;
+ void* qelem[LISTEN_QUEUE_LENGTH];
+ subscription_list_t wait;
+} listen_queue_t;
+
+#endif
+
diff --git a/deps/bt/c_src/bt_linux_drv.c b/deps/bt/c_src/bt_linux_drv.c
new file mode 100644
index 0000000..9dc54b9
--- /dev/null
+++ b/deps/bt/c_src/bt_linux_drv.c
@@ -0,0 +1,1088 @@
+//
+// Work in progress (linux) bluetooth driver
+//
+
+#include <sys/types.h>
+#include <sys/wait.h>
+#include <signal.h>
+#include <stdlib.h>
+#include <stddef.h>
+#include <unistd.h>
+#include <string.h>
+#include <memory.h>
+#include <poll.h>
+#include <errno.h>
+#include <fcntl.h>
+
+#include <bluetooth/bluetooth.h>
+#include <bluetooth/l2cap.h>
+#include <bluetooth/rfcomm.h>
+#include <bluetooth/hci.h>
+#include <bluetooth/hci_lib.h>
+
+#include "dthread/include/dlog.h"
+#include "dthread/include/ddata.h"
+#include "bt_drv.h"
+#include "bt_poll.h"
+
+#define RFCOMM_CHANNEL_ID_IS_VALID(x) (((x) >= 1) && ((x) <= 30))
+#define PSM_IS_VALID(x) (((x) >= 0) && ((x) <= 0xffff))
+
+#define PTR2INT(x) ((int)((long)(x)))
+#define INT2PTR(x) ((void*)((long)(x)))
+
+#define alloc_type(type) calloc(1, sizeof(type))
+
+typedef struct _drv_data_t {
+ int dev_id;
+ int mgmt_sock;
+ int dev_sock;
+ int inquiry_pid;
+ int inquiry_fd;
+} drv_data_t;
+
+int set_nonblock(int fd)
+{
+ int flags;
+#if defined(O_NONBLOCK)
+ if (-1 == (flags = fcntl(fd, F_GETFL, 0)))
+ flags = 0;
+ return fcntl(fd, F_SETFL, flags | O_NONBLOCK);
+#else
+ flags = 1;
+ return ioctl(fd, FIOBIO, &flags);
+#endif
+}
+
+
+#define ERR_SHORT 1
+#define ERR_LONG 2
+
+
+
+static int mgmt_open(void)
+{
+ struct sockaddr_hci addr;
+ int sk;
+
+ sk = socket(AF_BLUETOOTH, SOCK_RAW, BTPROTO_HCI);
+ if (sk < 0) {
+ fprintf(stderr, "socket: %s\n", strerror(errno));
+ return sk;
+ }
+
+ memset(&addr, 0, sizeof(addr));
+ addr.hci_family = AF_BLUETOOTH;
+ addr.hci_dev = HCI_DEV_NONE;
+ addr.hci_channel = HCI_CHANNEL_CONTROL;
+
+ if (bind(sk, (struct sockaddr *) &addr, sizeof(addr)) < 0) {
+ fprintf(stderr, "bind: %s\n", strerror(errno));
+ close(sk);
+ return -1;
+ }
+
+ return sk;
+}
+
+
+static inline int get_address(ddata_t* data, bdaddr_t* val)
+{
+ if (ddata_r_avail(data) >= sizeof(bdaddr_t)) {
+ // For some reason, the address is reversed when it
+ // hits bt_linux_drv.
+ val->b[0] = *(data->rd+5);
+ val->b[1] = *(data->rd+4);
+ val->b[2] = *(data->rd+3);
+ val->b[3] = *(data->rd+2);
+ val->b[4] = *(data->rd+1);
+ val->b[5] = *(data->rd+0);
+
+ // Was: memcpy(val, data->rd, sizeof(bdaddr_t));
+ data->rd += sizeof(bdaddr_t);
+ return 1;
+ }
+ return 0;
+}
+
+static inline void ddata_put_addr(ddata_t* data, bdaddr_t* addr)
+{
+ uint8_t* ptr = ddata_alloc(data, sizeof(bdaddr_t)+1);
+ *ptr++ = ADDR;
+ *ptr++ = addr->b[5];
+ *ptr++ = addr->b[4];
+ *ptr++ = addr->b[3];
+ *ptr++ = addr->b[2];
+ *ptr++ = addr->b[1];
+ *ptr++ = addr->b[0];
+}
+
+
+static void bt_local_info(drv_data_t* lctx, ddata_t* data_in, ddata_t* data_out)
+{
+ uint8_t op_code;
+
+ ddata_put_tag(data_out, LIST);
+
+ while(ddata_get_uint8(data_in, &op_code)) {
+ switch(op_code) {
+ case NFO_LOCAL_NAME: {
+ struct hci_dev_info di;
+
+ DEBUGF("local_info: local name");
+
+ hci_devinfo(lctx->dev_id, &di);
+
+ ddata_put_string(data_out, di.name);
+ break;
+ }
+
+ case NFO_LOCAL_CLASS: {
+ uint8_t cls_str[4];
+ uint32_t cls;
+
+ memset(cls_str, 0, sizeof(cls_str));
+ // Send in the lowest three bytes of the uint32 to
+ // retrieve the three byte class
+ if (hci_read_class_of_dev(lctx->dev_sock, cls_str+1, 0) == -1) {
+ DEBUGF("hci_read_class_of_dev() failed: %s", strerror(errno));
+ break;
+ }
+
+ cls = *((int*)cls_str);
+
+ DEBUGF("local_info: local class: %X", cls);
+ ddata_put_uint32(data_out, cls);
+ break;
+ }
+
+ case NFO_LOCAL_ADDRESS: {
+ struct hci_dev_info di;
+ DEBUGF("local_info: local address");
+ hci_devinfo(lctx->dev_id, &di);
+ ddata_put_addr(data_out, &(di.bdaddr));
+ break;
+ }
+
+ case NFO_LOCAL_DISCOVERABLE: {
+ // Boolean value;
+ // if (IOBluetoothLocalDeviceGetDiscoverable(&value) == kIOReturnSuccess)
+ DEBUGF("local_info: discoverable");
+ ddata_put_boolean(data_out, 1);
+ break;
+ }
+
+ case NFO_LOCAL_POWER_STATE: {
+ /* BluetoothHCIPowerState powerState = */
+ /* [[IOBluetoothHostController defaultController] powerState]; */
+ /* if (powerState == kBluetoothHCIPowerStateON) */
+ /* ddata_put_atom(data_out, "on"); */
+ /* else if (powerState == kBluetoothHCIPowerStateOFF) */
+ /* ddata_put_atom(data_out, "off"); */
+ /* else if (powerState == kBluetoothHCIPowerStateUnintialized) */
+ /* ddata_put_atom(data_out, "unintialized"); */
+ /* else */
+ DEBUGF("local_info: power_state");
+ ddata_put_atom(data_out, "on");
+
+ break;
+ }
+ default:
+ break;
+ }
+ }
+ ddata_put_tag(data_out, LIST_END);
+}
+
+
+static void launch_inquiry_proc(drv_data_t *lctx, uint32_t max_seconds) {
+ inquiry_info *ii = NULL;
+ int32_t max_rsp;
+ int32_t num_rsp;
+ int dev_id, sock, len, flags;
+ int i;
+ int pfd[2];
+ int child_pid;
+
+ pipe(pfd);
+ if ((child_pid = fork()) > 0) {
+ close(pfd[1]);
+ lctx->inquiry_pid = child_pid;
+ lctx->inquiry_fd = pfd[0];
+ return ;
+ }
+
+ // We are child
+ close(pfd[0]);
+
+ // Open a new socket to the adapter
+ // just so that we don't mess up something in the stack
+ // by accessing the same fd from two different processes.
+ dev_id = hci_get_route(NULL);
+ sock = hci_open_dev( dev_id );
+
+ if (dev_id < 0 || sock < 0) {
+ num_rsp = -1;
+ write(pfd[1], &num_rsp, sizeof(num_rsp));
+ close(pfd[1]);
+ close(sock);
+ exit(0);
+
+ }
+
+ len = (int32_t) ((float) max_seconds / 1.28); // 1.28 second per length unit
+ 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 ) {
+ num_rsp = 0;
+ write(pfd[1], &num_rsp, sizeof(num_rsp));
+ close(pfd[1]);
+ free( ii );
+ close( sock );
+ exit(0);
+ }
+
+ bdaddr_t resp[num_rsp];
+
+ // Retrieve address and remote name
+ for (i = 0; i < num_rsp; i++)
+ resp[i] = (ii+i)->bdaddr;
+
+ // Write number of responses that we have.
+ printf("Num_resp: %d\n", num_rsp);
+ write(pfd[1], &num_rsp, sizeof(num_rsp));
+ write(pfd[1], resp, sizeof(resp));
+
+ close(pfd[1]);
+ free( ii );
+ close( sock );
+ exit(0);
+}
+
+
+
+/* type: 1=short-atom 2=string-long 3=both (encapsule in LIST ot TUPLE) */
+static void ddata_put_io_error(ddata_t* data, int errnum, int type)
+{
+ if (errnum == 0) {
+ if (type&1) ddata_put_atom(data, "Success");
+ if (type&2) ddata_put_string(data, "OK");
+ }
+ else {
+ if (type&1) ddata_put_atom(data, "Error"); // fixme
+ if (type&2) ddata_put_string(data, strerror(errnum));
+ }
+}
+
+
+static void cleanup(subscription_t* s)
+{
+ DEBUGF("cleanup: %s", format_subscription(s));
+ drv_data_t* lctx = (drv_data_t*) s->handle;
+
+ switch(s->type) {
+ case INQUIRY: {
+ bt_poll_del(lctx->inquiry_fd);
+ close(lctx->inquiry_fd);
+
+ kill(lctx->inquiry_pid, SIGHUP);
+ lctx->inquiry_pid = -1;
+ lctx->inquiry_fd = -1;
+ wait(0);
+ break;
+ }
+
+ case REMOTE_NAME: break;
+ case CONNECT: break;
+ case SDP_QUERY: break;
+ case SDP: break;
+ case RFCOMM: {
+ bt_poll_del(PTR2INT(s->handle));
+ close(PTR2INT(s->handle));
+ break;
+ }
+ case RFCOMM_LISTEN: {
+ bt_poll_del(PTR2INT(s->handle));
+ close(PTR2INT(s->handle));
+ break;
+ }
+ case L2CAP: break;
+ case L2CAP_LISTEN: break;
+ default: // warn?
+ break;
+ }
+}
+
+// setup "standard" reply buf, with initial 32 but size
+static void mesg_setup(ddata_t* data, uint8_t* buf, size_t size)
+{
+ ddata_init(data, buf, size, 0);
+ ddata_put_UINT32(data, 0);
+}
+
+/* Send OK reply */
+static void reply_ok(uint32_t cmdid)
+{
+ uint8_t buf[16];
+ ddata_t data;
+
+ mesg_setup(&data, buf, sizeof(buf));
+ ddata_put_tag(&data, REPLY_OK);
+ ddata_put_UINT32(&data, cmdid);
+ ddata_send(&data, 1);
+ ddata_final(&data);
+}
+
+/* Send ERROR reply */
+static void reply_error(uint32_t cmdid, int err)
+{
+ uint8_t buf[128];
+ ddata_t data;
+
+ mesg_setup(&data, buf, sizeof(buf));
+ ddata_put_tag(&data, REPLY_ERROR);
+ ddata_put_UINT32(&data, cmdid);
+ ddata_put_io_error(&data, err, ERR_SHORT);
+ ddata_send(&data, 1);
+ ddata_final(&data);
+}
+
+// simple event
+static void send_event(uint32_t sid, const char* evtname)
+{
+ uint8_t buf[64];
+ ddata_t data;
+
+ mesg_setup(&data, buf, sizeof(buf));
+ ddata_put_tag(&data, REPLY_EVENT);
+ ddata_put_UINT32(&data, sid);
+ ddata_put_atom(&data, evtname);
+ ddata_send(&data, 1);
+ ddata_final(&data);
+}
+
+
+// CALLBACK
+static void rfcomm_running(struct pollfd* pfd, void* arg)
+{
+ subscription_t* s = arg;
+ uint8_t buf[1024];
+ uint8_t bt_data[800];
+ int32_t bt_data_len;
+ ddata_t data;
+
+
+ if (pfd->revents & POLLIN) { // input ready
+ DEBUGF("rfcomm_running: %d has input", PTR2INT(s->handle));
+
+ bt_data_len = read(pfd->fd, bt_data, sizeof(bt_data));
+
+ ddata_init(&data, buf, sizeof(buf), 0);
+ ddata_put_UINT32(&data, 0);
+ ddata_put_tag(&data, REPLY_EVENT);
+ ddata_put_UINT32(&data, s->id);
+ ddata_put_tag(&data, TUPLE);
+ ddata_put_atom(&data, "data");
+ ddata_put_binary(&data, bt_data, bt_data_len);
+ ddata_put_tag(&data, TUPLE_END);
+ ddata_send(&data, 1);
+ ddata_final(&data);
+ }
+
+ if (pfd->revents & POLLHUP) { // close
+ DEBUGF("rfcomm_running: %d Hangup", PTR2INT(s->handle));
+ ddata_init(&data, buf, sizeof(buf), 0);
+ ddata_put_UINT32(&data, 0);
+ ddata_put_tag(&data, REPLY_EVENT);
+ ddata_put_UINT32(&data, s->id);
+ ddata_put_atom(&data, "closed");
+ ddata_send(&data, 1);
+ ddata_final(&data);
+
+ release_subscription(s);
+ bt_poll_del(pfd->fd);
+ shutdown(pfd->fd, 2);
+ close(pfd->fd);
+ }
+ if (pfd->revents & POLLOUT) { // output ready
+ DEBUGF("rfcomm_running: %d may output", PTR2INT(s->handle));
+ // FIXME: Send additional pending data.
+ }
+}
+
+// CALLBACK
+static void rfcomm_connected(struct pollfd* pfd, void* arg)
+{
+ subscription_t* s = arg;
+ (void) pfd;
+ DEBUGF("rfcomm_connected: %d", PTR2INT(s->handle));
+ reply_ok(s->cmdid);
+ // FIXME: check error
+ s->cmdid = 0;
+ bt_poll_set_cb(PTR2INT(s->handle), rfcomm_running);
+ bt_poll_set_events(PTR2INT(s->handle), POLLIN);
+}
+
+
+
+// CALLBACK
+static void rfcomm_accept(struct pollfd* pfd, void* arg)
+{
+ subscription_t *accept_s = (subscription_t*) arg;
+
+ // We have a pending client connection
+ struct sockaddr_rc rem_addr = { 0 };
+ int client;
+ socklen_t alen = sizeof(rem_addr);
+ uint8_t buf[64];
+ ddata_t data;
+
+ client = accept(pfd->fd, (struct sockaddr *)&rem_addr, &alen);
+
+ /* send EVENT id {accept,Address,Channel} */
+
+ bt_poll_add(client, POLLIN | POLLHUP, rfcomm_running, accept_s);
+ accept_s->handle = INT2PTR(client);
+
+ ddata_init(&data, buf, sizeof(buf), 0);
+ ddata_put_UINT32(&data, 0);
+ ddata_put_tag(&data, REPLY_EVENT);
+ ddata_put_UINT32(&data, accept_s->id);
+ ddata_put_tag(&data, TUPLE);
+ ddata_put_atom(&data, "accept");
+ ddata_put_addr(&data, &rem_addr.rc_bdaddr);
+ ddata_put_uint8(&data, PTR2INT(accept_s->handle));
+ ddata_put_tag(&data, TUPLE_END);
+ ddata_send(&data, 1);
+ ddata_final(&data);
+}
+
+
+//
+static void hci_inquiry_result(struct pollfd* pfd, void* arg)
+{
+ subscription_t* s = arg;
+ uint8_t out_buf[2048];
+ ddata_t data_out;
+
+ DEBUGF("hci_inquiry_result: %d", PTR2INT(s->handle));
+ int32_t count;
+
+
+ // Read count, which may be -1.
+ read(pfd->fd, &count, sizeof(count));
+
+ DEBUGF("hci_inquiry_res: %d addresses\n", count);
+
+ ddata_init(&data_out, out_buf, sizeof(out_buf), 0);
+
+ ddata_put_tag(&data_out, REPLY_OK);
+ ddata_put_UINT32(&data_out, s->cmdid);
+ ddata_put_tag(&data_out, LIST);
+
+ while(count-- > 0) {
+ bdaddr_t resp;
+ read(pfd->fd, &resp, sizeof(resp));
+ ddata_put_addr(&data_out, &resp);
+ }
+
+ ddata_put_tag(&data_out, LIST_END);
+
+ bt_poll_del(pfd->fd);
+
+ close(pfd->fd);
+
+ // Wait for child process to die.
+ wait(0);
+
+ ddata_send(&data_out, 1);
+ ddata_final(&data_out);
+
+ s->cmdid = 0;
+
+ // only for Inquiry objects
+ release_subscription(s);
+}
+
+
+void bt_command(bt_ctx_t* ctx, const uint8_t* src, uint32_t src_len)
+{
+ uint8_t op = 0;
+ uint32_t cmdid = 0;
+ int bt_error = 0;
+ uint8_t out_buf[2048];
+ ddata_t data_in;
+ ddata_t data_out;
+ drv_data_t* lctx = (drv_data_t*) ctx->drv_data;
+
+ // dump subscription list
+ if (dlog_debug_level == DLOG_DEBUG) {
+ subscription_link_t* p = ctx->list.first;
+ fprintf(stderr, "ctx.list = {");
+ while(p) {
+ fprintf(stderr, " %s,", format_subscription(p->s));
+ p = p->next;
+ }
+ fprintf(stderr, "}\r\n");
+ }
+
+ ddata_r_init(&data_in, (uint8_t*)src, src_len, 0);
+ mesg_setup(&data_out, out_buf, sizeof(out_buf));
+
+ if (!ddata_get_uint8(&data_in, &op))
+ goto badarg;
+ if (!ddata_get_uint32(&data_in, &cmdid))
+ goto badarg;
+
+ switch (op) {
+ case CMD_PING: {
+ DEBUGF("CMD_PING cmdid=%d", cmdid);
+ ddata_put_tag(&data_out, REPLY_OK);
+ ddata_put_UINT32(&data_out, cmdid);
+ ddata_put_string(&data_out, "pong");
+ goto reply;
+ }
+ case CMD_DEBUG: { // <<level>>
+ int level;
+ DEBUGF("CMD_DEBUG cmdid=%d", cmdid);
+ if (!ddata_get_int32(&data_in, &level))
+ goto badarg;
+ dlog_set_debug(level);
+ ddata_put_tag(&data_out, REPLY_OK);
+ ddata_put_UINT32(&data_out, cmdid);
+ goto reply;
+ }
+
+
+ case CMD_RFCOMM_OPEN: { /* id:32 bt-address(6) channel-id:8 */
+ uint32_t sid;
+ bdaddr_t bt_addr;
+ struct sockaddr_rc addr;
+ int sock;
+ int r;
+ uint8_t channel_id;
+ subscription_t* s;
+ char addrstr[40];
+
+ DEBUGF("CMD_RFCOMM_OPEN cmdid=%d", cmdid);
+
+ if (!ddata_get_uint32(&data_in, &sid))
+ goto badarg;
+ if(!get_address(&data_in, &bt_addr))
+ goto badarg;
+ if(!ddata_get_uint8(&data_in, &channel_id))
+ goto badarg;
+ if (!RFCOMM_CHANNEL_ID_IS_VALID(channel_id))
+ goto badarg;
+ if (ddata_r_avail(&data_in) != 0)
+ goto badarg;
+
+ if ((sock = socket(AF_BLUETOOTH, SOCK_STREAM, BTPROTO_RFCOMM)) < 0)
+ goto bt_error;
+
+ addr.rc_family = AF_BLUETOOTH;
+ addr.rc_channel = channel_id;
+ bacpy(&addr.rc_bdaddr, &bt_addr);
+ ba2str(&bt_addr, addrstr );
+ DEBUGF("Will connect to %s channel %d", addrstr, channel_id);
+ if (set_nonblock(sock) < 0) {
+ bt_error = errno;
+ close(sock);
+ goto bt_error;
+ }
+ if ((r = connect(sock, (struct sockaddr*)&addr, sizeof(addr))) < 0) {
+ if (errno != EINPROGRESS) {
+ bt_error = errno;
+ close(sock);
+ goto bt_error;
+ }
+ }
+
+ if ((s = new_subscription(RFCOMM,sid,cmdid,0,cleanup)) == NULL) {
+ close(sock);
+ goto mem_error;
+ }
+ if (r < 0) // inprogress
+ bt_poll_add(sock, POLLOUT, rfcomm_connected, s);
+ else
+ bt_poll_add(sock, POLLIN, rfcomm_running, s);
+ s->handle = INT2PTR(sock);
+ insert_last(&ctx->list, s);
+ break;
+ }
+
+
+ case CMD_RFCOMM_LISTEN: { /* id:32, channel:8 */
+ uint32_t sid = 0;
+ uint8_t channel = 0;
+ subscription_t* listen_sub = 0;
+// listen_queue_t* lq = 0;
+ int listen_desc = 0;
+ struct sockaddr_rc loc_addr = { 0 };
+ int dev_id;
+ struct hci_dev_info dev_info;
+ char buf[32];
+
+ DEBUGF("CMD_RFCOMM_LISTEN cmdid=%d", cmdid);
+
+ if (!ddata_get_uint32(&data_in, &sid))
+ goto badarg;
+
+ if(!ddata_get_uint8(&data_in, &channel))
+ goto badarg;
+
+ // Fixme auto-allocated channel ID.
+ if ((channel == 0) || !RFCOMM_CHANNEL_ID_IS_VALID(channel))
+ goto badarg;
+
+ if (ddata_r_avail(&data_in) != 0)
+ goto badarg;
+
+
+ if ((listen_sub = new_subscription(RFCOMM_LISTEN,
+ sid,
+ cmdid,
+ NULL,
+ cleanup)) == NULL)
+ goto mem_error;
+
+ dev_id = hci_get_route(NULL);
+ hci_devinfo(dev_id, &dev_info);
+ ba2str( &dev_info.bdaddr, buf );
+
+ DEBUGF("Listening on device %s\n", buf);
+
+ // allocate socket
+ listen_desc = socket(AF_BLUETOOTH, SOCK_STREAM, BTPROTO_RFCOMM);
+
+ // bind socket to port 1 of the first available
+ // local bluetooth adapter
+ loc_addr.rc_family = AF_BLUETOOTH;
+ loc_addr.rc_bdaddr = *BDADDR_ANY;
+ loc_addr.rc_channel = (uint8_t) channel;
+
+ if (bind(listen_desc,
+ (struct sockaddr *) &loc_addr,
+ sizeof(loc_addr)) == -1) {
+ DEBUGF("bind(%d) failed: %s", channel, strerror(errno));
+ goto error;
+ }
+
+ // put socket into listening mode
+ if (listen(listen_desc, 1) == -1) {
+ DEBUGF("listen(%d) failed: %s", channel, strerror(errno));
+ goto error;
+ }
+
+ listen_sub->handle = INT2PTR(listen_desc);
+ listen_sub->opaque = INT2PTR(channel);
+ insert_last(&ctx->list, listen_sub);
+
+ ddata_put_tag(&data_out, REPLY_OK);
+ ddata_put_UINT32(&data_out, cmdid);
+ goto reply;
+ }
+
+
+ case CMD_RFCOMM_ACCEPT: { /* id:32 listen_id:32 */
+ uint32_t sid = 0;
+ uint32_t listen_id = 0;
+// listen_queue_t* lq = 0;
+ subscription_t* listen = 0;
+ subscription_t* s = 0;
+
+ DEBUGF("CMD_RFCOMM_ACCEPT cmdid=%d", cmdid);
+
+
+ if (!ddata_get_uint32(&data_in, &sid))
+ goto badarg;
+
+ if (!ddata_get_uint32(&data_in, &listen_id))
+ goto badarg;
+
+ if (ddata_r_avail(&data_in) != 0)
+ goto badarg;
+
+ if (find_subscription(&ctx->list,RFCOMM, sid) != NULL) {
+ DEBUGF("subscription %d already exists", sid);
+ goto badarg;
+ }
+
+ if ((listen = find_subscription(&ctx->list,
+ RFCOMM_LISTEN,listen_id))==NULL) {
+ DEBUGF("listen subscription %d does not exists", listen_id);
+ goto badarg;
+ }
+
+ if ((s = new_subscription(RFCOMM,sid,cmdid,NULL,cleanup)) == NULL)
+ goto mem_error;
+
+ // s->accept = listen; // mark that we are accepting
+
+ bt_poll_add(PTR2INT(listen->handle), POLLIN, rfcomm_accept, s);
+ s->handle = listen->opaque;
+ insert_last(&ctx->list, s);
+
+ ddata_put_tag(&data_out, REPLY_OK);
+ ddata_put_UINT32(&data_out, cmdid);
+ ddata_send(&data_out, 1);
+ goto done;
+ }
+
+ case CMD_RFCOMM_CLOSE: { /* arguments: id:32 */
+ uint32_t sid;
+ subscription_link_t* link;
+
+ DEBUGF("CMD_RFCOMM_CLOSE cmdid=%d", cmdid);
+
+ if (!ddata_get_uint32(&data_in, &sid))
+ goto badarg;
+ if (ddata_r_avail(&data_in) != 0)
+ goto badarg;
+
+ if ((link = find_subscription_link(&ctx->list,RFCOMM,sid)) != NULL) {
+ subscription_t* s = link->s;
+ int sock;
+ s->cmdid = cmdid;
+ sock = PTR2INT(s->handle);
+ if (sock >= 0) {
+ DEBUGF("RFCOMM_CLOSE: channel=%d", sock);
+ bt_poll_del(sock);
+ close(sock);
+ unlink_subscription(link);
+ goto done;
+ }
+ /* else if (s->accept != NULL) { */
+ /* listen_queue_t* lq = (listen_queue_t*)((s->accept)->opaque); */
+ /* remove_subscription(&lq->wait,RFCOMM,sid); */
+ /* unlink_subscription(link); */
+ /* goto ok; */
+ /* } */
+ }
+ else if ((link = find_subscription_link(&ctx->list,RFCOMM_LISTEN,sid)) != NULL) {
+ /* subscription_t* listen = link->s; */
+ /* listen_queue_t* lq = (listen_queue_t*)listen->opaque; */
+ /* subscription_link_t* link1; */
+ /* /\* remove all waiters *\/ */
+ /* while((link1=lq->wait.first) != NULL) { */
+ /* send_event(link1->s->id, "closed"); */
+ /* unlink_subscription(link1); */
+ /* } */
+ /* unlink_subscription(link); */
+ goto ok;
+ }
+ goto error;
+ }
+
+
+ case CMD_RFCOMM_SEND: { /* id:32, data/rest */
+ uint32_t sid;
+ subscription_t* s;
+ int r;
+ int len;
+ int sock;
+
+ DEBUGF("CMD_RFCOMM_SEND cmdid=%d", cmdid);
+
+ if (!ddata_get_uint32(&data_in, &sid))
+ goto badarg;
+ if ((s = find_subscription(&ctx->list,RFCOMM,sid)) == NULL)
+ goto badarg;
+ if ((sock = PTR2INT(s->handle)) < 0)
+ goto badarg;
+ s->cmdid = cmdid;
+
+ if ((len = ddata_r_avail(&data_in)) > 0) {
+ if (s->out == NULL)
+ s->out = ddata_new(data_in.rd, len);
+ else
+ ddata_add(s->out, data_in.rd, len);
+ r = write(sock, s->out->rd, ddata_r_avail(s->out));
+ if (r != -1)
+ DEBUGF("CMD_RFCOMM_SEND wrote=%d", r);
+ else
+ DEBUGF("CMD_RFCOMM_SEND failed: %s", strerror(errno));
+
+ if ((r >= 0) || ((r < 0) && (errno == EINPROGRESS))) {
+ if (r < 0) r = 0;
+ s->out->rd += r;
+ if (ddata_r_avail(s->out) > 0)
+ bt_poll_set_events(PTR2INT(s->handle), POLLIN|POLLOUT);
+ else {
+ ddata_reset(s->out);
+ s->cmdid = 0;
+ goto ok;
+ }
+ }
+ else {
+ s->cmdid = 0;
+ goto error;
+ }
+ }
+ else {
+ s->cmdid = 0;
+ goto ok;
+ }
+ goto done;
+ }
+
+
+ case CMD_INQUIRY_START: { /* arguments: id:32 secs:32 */
+ uint32_t sid;
+ uint32_t secs;
+
+ subscription_t* s;
+ if (!ddata_get_uint32(&data_in, &sid))
+ goto badarg;
+
+ if (!ddata_get_uint32(&data_in, &secs))
+ goto badarg;
+
+ DEBUGF("CMD_INQUIRY_START: %d", sid);
+ if (lctx->inquiry_pid != -1) {
+ DEBUGF("CMD_INQUIRY_START: Already in progress");
+ goto error;
+ }
+
+ // hci_inquiry() is very, very blocking. We need to run it in a separate
+ // process in order to maintain asynchronicity.
+ // Will set ctx->drv_data->inquriry_pid and inquiry_fd
+ launch_inquiry_proc(lctx, secs);
+ DEBUGF("CMD_INQUIRY_START: process launched");
+
+ if ((s = new_subscription(INQUIRY,sid,0,lctx, cleanup)) == NULL)
+ goto mem_error;
+
+
+ bt_poll_add(lctx->inquiry_fd, POLLIN, hci_inquiry_result, ctx);
+ insert_last(&ctx->list, s);
+ DEBUGF("CMD_INQUIRY_START: Done");
+
+ goto ok;
+ }
+
+ case CMD_INQUIRY_STOP: { /* arguments: id:32 secs:32 */
+ uint32_t sid;
+
+ if (!ddata_get_uint32(&data_in, &sid))
+ goto badarg;
+
+ // Will call cleanup, which will handle correct shutdown
+ // of inquiry process.
+ remove_subscription(&ctx->list,INQUIRY,sid);
+
+ goto ok;
+ }
+
+ case CMD_RECENT_DEVICES: {
+ DEBUGF("CMD_RECENT_DEVICES cmdid=%d: Not supported under linux", cmdid);
+ ddata_put_tag(&data_out, REPLY_OK);
+ ddata_put_UINT32(&data_out, cmdid);
+ ddata_put_tag(&data_out, LIST);
+ ddata_put_tag(&data_out, LIST_END);
+ goto reply;
+ }
+
+ case CMD_PAIRED_DEVICES: {
+ DEBUGF("CMD_PAIRED_DEVICES cmdid=%d: Not supported under linux", cmdid);
+ ddata_put_tag(&data_out, REPLY_OK);
+ ddata_put_UINT32(&data_out, cmdid);
+ ddata_put_tag(&data_out, LIST);
+ ddata_put_tag(&data_out, LIST_END);
+ goto reply;
+ }
+
+ case CMD_FAVORITE_DEVICES: {
+ DEBUGF("CMD_FAVORITE_DEVICES cmdid=%d: Not supported under linux", cmdid);
+ ddata_put_tag(&data_out, REPLY_OK);
+ ddata_put_UINT32(&data_out, cmdid);
+ ddata_put_tag(&data_out, LIST);
+ ddata_put_tag(&data_out, LIST_END);
+ goto reply;
+ }
+
+ case CMD_DEVICE_INFO: {
+ bdaddr_t bt_addr;
+ DEBUGF("CMD_DEVICE_INFO cmdid=%d", cmdid);
+
+ if(!get_address(&data_in, &bt_addr))
+ goto badarg;
+
+ ddata_put_tag(&data_out, REPLY_OK);
+ ddata_put_UINT32(&data_out, cmdid);
+ ddata_put_tag(&data_out, LIST);
+ ddata_put_tag(&data_out, LIST_END);
+
+ goto reply;
+ }
+
+ case CMD_LOCAL_INFO: {
+ DEBUGF("CMD_LOCAL_INFO cmdid=%d", cmdid);
+
+ ddata_put_tag(&data_out, REPLY_OK);
+ ddata_put_UINT32(&data_out, cmdid);
+
+ bt_local_info(lctx, &data_in, &data_out);
+
+ goto reply;
+ }
+
+ default:
+ DEBUGF("CMD_UNKNOWN = %d cmdid=%d", op, cmdid);
+ goto badarg;
+ }
+
+ goto done;
+
+ok:
+ if (cmdid == 0)
+ goto done;
+ ddata_put_tag(&data_out, REPLY_OK);
+ ddata_put_UINT32(&data_out, cmdid);
+ goto reply;
+
+mem_error:
+ if (cmdid == 0)
+ goto done;
+ bt_error = ENOMEM;
+ goto bt_error;
+
+badarg:
+ if (cmdid == 0)
+ goto done;
+ bt_error = EINVAL;
+ goto bt_error;
+
+error:
+ bt_error = errno;
+bt_error:
+/* reset, just in case something was inserted */
+ ddata_reset(&data_out);
+ ddata_put_UINT32(&data_out, 0);
+ ddata_put_tag(&data_out, REPLY_ERROR);
+ ddata_put_UINT32(&data_out, cmdid);
+ ddata_put_io_error(&data_out, bt_error, ERR_SHORT);
+reply:
+ ddata_send(&data_out, 1);
+done:
+ ddata_final(&data_out);
+}
+
+// CALLBACK
+void read_callback(struct pollfd* pfd, void* data)
+{
+ bt_ctx_t* ctx = (bt_ctx_t*) data;
+ int fd = pfd->fd;
+ int n;
+
+ DEBUGF("read_callback: %d", pfd->fd);
+ if (pfd->revents & POLLHUP) {
+ DEBUGF("hangup");
+ goto closed;
+ }
+
+ if (ctx->pbuf_len < sizeof(ctx->pbuf)) {
+ int r = sizeof(ctx->pbuf) - ctx->pbuf_len;
+ if ((n = read(fd, ctx->pbuf+ctx->pbuf_len, r)) < 0)
+ goto error;
+ if (n == 0)
+ goto closed;
+ ctx->pbuf_len += n;
+ DEBUGF("READ: %d pbuf_len=%d", n, ctx->pbuf_len);
+ if (ctx->pbuf_len == sizeof(ctx->pbuf)) {
+ ctx->len = (ctx->pbuf[0]<<24) + (ctx->pbuf[1]<<16) +
+ (ctx->pbuf[2]<<8) + ctx->pbuf[3];
+ DEBUGF("READ: %d packet len=%d", n, ctx->len);
+ if (ctx->len > 0) {
+ ctx->remain = ctx->len;
+ ctx->packet = (uint8_t*) malloc(ctx->len);
+ ctx->ptr = ctx->packet;
+ }
+ else {
+ ctx->remain = 0;
+ ctx->pbuf_len = 0;
+ }
+ }
+ }
+ else {
+ if ((n = read(fd, (void*)ctx->ptr, ctx->remain)) < 0)
+ goto error;
+ if (n == 0)
+ goto closed;
+ DEBUGF("READ: %d packet bytes", n);
+ ctx->remain -= n;
+ DEBUGF("PACKET: remain=%d", ctx->remain);
+ ctx->ptr += n;
+ if (ctx->remain == 0) {
+ bt_command(ctx, ctx->packet, ctx->len);
+ free(ctx->packet);
+ ctx->packet = NULL;
+ ctx->len = 0;
+ ctx->pbuf_len = 0;
+ }
+ }
+ return;
+
+error:
+ DEBUGF("pipe read error",0);
+ exit(1);
+
+closed:
+ DEBUGF("eof clean-up",0);
+ exit(0);
+}
+
+static void main_loop(bt_ctx_t* ctx)
+{
+ int dev_id = 0;
+ int sock = 0;
+ drv_data_t local_context;
+
+ // FIXME: How the hell do we handle more than one BT adapter.
+ dev_id = hci_get_route(NULL);
+ sock = hci_open_dev(dev_id);
+ if (sock == -1) {
+ fprintf(stderr, "Failed to open BT device %d: %s\n", dev_id, strerror(errno));
+ exit(0);
+ }
+
+ // Setup local context
+// local_context.mgmt_sock = mgmt_open();
+ local_context.mgmt_sock = -1;
+ local_context.dev_id = dev_id;
+ local_context.dev_sock = sock;
+ local_context.inquiry_fd = -1;
+ local_context.inquiry_pid = -1;
+
+ ctx->drv_data = &local_context;
+
+ while(1) {
+ int r = bt_poll(-1);
+ if (r < 0) {
+ DEBUGF("poll error %s", strerror(errno));
+ }
+ }
+}
+
+int main(int argc, char** argv)
+{
+ bt_ctx_t bt_info;
+ (void) argc;
+ (void) argv;
+
+ dlog_init();
+ dlog_set_debug(DLOG_DEFAULT);
+ memset(&bt_info, 0, sizeof(bt_ctx_t));
+
+ bt_poll_add(0, POLLIN | POLLERR | POLLHUP, read_callback, &bt_info);
+ main_loop(&bt_info);
+
+ dlog_finish();
+ DEBUGF("terminate",0);
+ exit(0);
+}
diff --git a/deps/bt/c_src/bt_macos_drv.c b/deps/bt/c_src/bt_macos_drv.c
new file mode 100644
index 0000000..81c9a3e
--- /dev/null
+++ b/deps/bt/c_src/bt_macos_drv.c
@@ -0,0 +1,3066 @@
+/*
+ * BT driver for Erlang
+ *
+ */
+#include <AvailabilityMacros.h>
+#include <sys/types.h>
+#include <string.h>
+#include <memory.h>
+#include <CoreFoundation/CoreFoundation.h>
+#include <IOBluetooth/IOBluetoothUtilities.h>
+#include <IOBluetooth/IOBluetoothUserLib.h>
+#include <IOBluetooth/objc/IOBluetoothDevice.h>
+#include <IOBluetooth/objc/IOBluetoothSDPServiceRecord.h>
+#include <IOBluetooth/objc/IOBluetoothDeviceInquiry.h>
+#include <IOBluetooth/objc/IOBluetoothRFCOMMChannel.h>
+#include <IOBluetooth/objc/IOBluetoothL2CAPChannel.h>
+#include <IOBluetooth/objc/IOBluetoothSDPDataElement.h>
+#include <IOBluetooth/objc/IOBluetoothSDPUUID.h>
+#include <IOBluetooth/objc/IOBluetoothHostController.h>
+
+#include "dthread/include/dlog.h"
+#include "dthread/include/ddata.h"
+#include "bt_drv.h"
+
+#define alloc_type(type) calloc(1, sizeof(type))
+
+// Object structure
+// +----------+-------------------------------------------v
+// | | +----------+ +----------+ +----------+
+// | ctx.list | <-> | sub_link | <-> | sub_link | <-> | sub_link |
+// +----------+ +----+-----+ +-----+----+ +----------+
+// | |
+// +----v-------+ +-----v------+
+// | subscriber1| | subscriber2|
+// | INQUIRY | | RFCOMM |
+// +----+-------+ +-------+----+
+// | handle | handle
+// +----------------v---------+ +-----v--------------------+
+// | IOBluetoothDeviceInquiry | | IOBluetoothRFCOMMChannel |
+// +-------+------------------+ +-----+--------------------+
+// | |
+// +-------v---------+ +-----v--------------------+
+// | InquiryDelegate | | RFCOMMChannelDelegate |
+// +-------+---------+ +----------+---------------+
+// | |
+// v v
+// subscriber1 ! subscriber2 !
+//
+
+// -----------------------------------------------------------------------
+// Delgate classes
+// -----------------------------------------------------------------------
+
+@interface InquiryDelegate : NSObject <IOBluetoothDeviceInquiryDelegate> {
+ subscription_t* mSub;
+}
+- (instancetype)initWithSub:(subscription_t*)s;
+- (void) dealloc;
+@end
+
+@interface RemoteNameDelegate : NSObject <IOBluetoothDeviceInquiryDelegate> {
+ subscription_t* mSub;
+}
+- (instancetype)initWithSub:(subscription_t*) s;
+- (void) dealloc;
+@end
+
+@interface ConnectDelegate : NSObject <IOBluetoothDeviceInquiryDelegate> {
+ subscription_t* mSub;
+}
+- (instancetype)initWithSub:(subscription_t*) s;
+- (void) dealloc;
+@end
+
+@interface SDPQueryDelegate : NSObject <IOBluetoothDeviceInquiryDelegate> {
+ subscription_t* mSub;
+ IOBluetoothSDPUUID* mUUID;
+}
+- (instancetype)initWithSub:(subscription_t*) s andUUID:(IOBluetoothSDPUUID*)uuid;
+- (void) dealloc;
+@end
+
+@interface RFCOMMChannelDelegate : NSObject <IOBluetoothRFCOMMChannelDelegate> {
+ subscription_t* mSub;
+ bt_ctx_t* mCtx;
+}
+- (instancetype)initWithSub:(subscription_t*) s andCtx:(bt_ctx_t*) ctx;
+- (void) dealloc;
+@end
+
+@interface L2CAPChannelDelegate : NSObject <IOBluetoothL2CAPChannelDelegate> {
+ subscription_t* mSub;
+ bt_ctx_t* mCtx;
+}
+- (instancetype)initWithSub:(subscription_t*) s andCtx:(bt_ctx_t*) ctx;
+- (void) dealloc;
+@end
+
+static inline void put_date(ddata_t* data, NSDate* date)
+{
+ uint8_t* ptr = ddata_alloc(data, 5);
+ NSTimeInterval at = 0;
+ uint32_t secs;
+
+ if (date != NULL)
+ at = [date timeIntervalSince1970];
+ secs = (uint32_t) at;
+ *ptr++ = DATE;
+ DDATA_PUT_UINT32(ptr, secs);
+}
+
+static inline void put_ns_string(ddata_t* data, NSString* str)
+{
+ char buffer[1024];
+ if ([str getCString:buffer maxLength:1024 encoding:NSUTF8StringEncoding])
+ ddata_put_string(data, buffer);
+ else
+ ddata_put_string(data, "?");
+}
+
+static inline void ddata_put_addr(ddata_t* data, const BluetoothDeviceAddress* addr)
+{
+ uint8_t* ptr = ddata_alloc(data, sizeof(BluetoothDeviceAddress)+1);
+ *ptr++ = ADDR;
+ memcpy(ptr, addr, sizeof(BluetoothDeviceAddress));
+}
+
+static inline int get_address(ddata_t* data, BluetoothDeviceAddress* val)
+{
+ if (ddata_r_avail(data) >= sizeof(BluetoothDeviceAddress)) {
+ memcpy(val, data->rd, sizeof(BluetoothDeviceAddress));
+ data->rd += sizeof(BluetoothDeviceAddress);
+ return 1;
+ }
+ return 0;
+}
+
+
+#define ERR_SHORT 1
+#define ERR_LONG 2
+
+/* type: 1=short-atom 2=string-long 3=both (encapsule in LIST ot TUPLE) */
+static void ddata_put_io_error(ddata_t* data, IOReturn error, int type)
+{
+ switch(error) {
+ case kIOReturnSuccess:
+ if (type&1) ddata_put_atom(data, "Success");
+ if (type&2) ddata_put_string(data, "OK");
+ break;
+ case kIOReturnError:
+ if (type&1) ddata_put_atom(data, "Error");
+ if (type&2) ddata_put_string(data, "general error");
+ break;
+ case kIOReturnNoMemory:
+ if (type&1) ddata_put_atom(data, "NoMemory");
+ if (type&2) ddata_put_string(data, "can't allocate memory");
+ break;
+ case kIOReturnNoResources:
+ if (type&1) ddata_put_atom(data, "NoResources");
+ if (type&2) ddata_put_string(data, "resource shortage ");
+ break;
+ case kIOReturnIPCError:
+ if (type&1) ddata_put_atom(data, "IPCError");
+ if (type&2) ddata_put_string(data, "error during IPC ");
+ break;
+ case kIOReturnNoDevice:
+ if (type&1) ddata_put_atom(data, "NoDevice");
+ if (type&2) ddata_put_string(data, "no such device ");
+ break;
+ case kIOReturnNotPrivileged:
+ if (type&1) ddata_put_atom(data, "NotPrivileged");
+ if (type&2) ddata_put_string(data, "privilege violation ");
+ break;
+ case kIOReturnBadArgument:
+ if (type&1) ddata_put_atom(data, "BadArgument");
+ if (type&2) ddata_put_string(data, "invalid argument ");
+ break;
+ case kIOReturnLockedRead:
+ if (type&1) ddata_put_atom(data, "LockedRead");
+ if (type&2) ddata_put_string(data, "device read locked ");
+ break;
+ case kIOReturnLockedWrite:
+ if (type&1) ddata_put_atom(data, "LockedWrite");
+ if (type&2) ddata_put_string(data, "device write locked ");
+ break;
+ case kIOReturnExclusiveAccess:
+ if (type&1) ddata_put_atom(data, "ExclusiveAccess");
+ if (type&2) ddata_put_string(data, "exclusive access and device already open");
+ break;
+ case kIOReturnBadMessageID:
+ if (type&1) ddata_put_atom(data, "BadMessageID");
+ if (type&2) ddata_put_string(data, "sent/received messages had different msg_id");
+ break;
+ case kIOReturnUnsupported:
+ if (type&1) ddata_put_atom(data, "Unsupported");
+ if (type&2) ddata_put_string(data, "unsupported function");
+ break;
+ case kIOReturnVMError:
+ if (type&1) ddata_put_atom(data, "VMError");
+ if (type&2) ddata_put_string(data, "misc. VM failure ");
+ break;
+ case kIOReturnInternalError:
+ if (type&1) ddata_put_atom(data, "InternalError");
+ if (type&2) ddata_put_string(data, "internal error ");
+ break;
+ case kIOReturnIOError:
+ if (type&1) ddata_put_atom(data, "IOError");
+ if (type&2) ddata_put_string(data, "General I/O error ");
+ break;
+ case kIOReturnCannotLock:
+ if (type&1) ddata_put_atom(data, "CannotLock");
+ if (type&2) ddata_put_string(data, "can't acquire lock");
+ break;
+ case kIOReturnNotOpen:
+ if (type&1) ddata_put_atom(data, "NotOpen");
+ if (type&2) ddata_put_string(data, "device not open ");
+ break;
+ case kIOReturnNotReadable:
+ if (type&1) ddata_put_atom(data, "NotReadable");
+ if (type&2) ddata_put_string(data, "read not supported ");
+ break;
+ case kIOReturnNotWritable:
+ if (type&1) ddata_put_atom(data, "NotWritable");
+ if (type&2) ddata_put_string(data, "write not supported");
+ break;
+ case kIOReturnNotAligned:
+ if (type&1) ddata_put_atom(data, "NotAligned");
+ if (type&2) ddata_put_string(data, "alignment error");
+ break;
+ case kIOReturnBadMedia:
+ if (type&1) ddata_put_atom(data, "BadMedia");
+ if (type&2) ddata_put_string(data, "Media Error");
+ break;
+ case kIOReturnStillOpen:
+ if (type&1) ddata_put_atom(data, "StillOpen");
+ if (type&2) ddata_put_string(data, "device(s) still open");
+ break;
+ case kIOReturnRLDError:
+ if (type&1) ddata_put_atom(data, "RLDError");
+ if (type&2) ddata_put_string(data, "rld failure");
+ break;
+ case kIOReturnDMAError:
+ if (type&1) ddata_put_atom(data, "DMAError");
+ if (type&2) ddata_put_string(data, "DMA failure");
+ break;
+ case kIOReturnBusy:
+ if (type&1) ddata_put_atom(data, "Busy");
+ if (type&2) ddata_put_string(data, "Device Busy");
+ break;
+ case kIOReturnTimeout:
+ if (type&1) ddata_put_atom(data, "Timeout");
+ if (type&2) ddata_put_string(data, "I/O Timeout");
+ break;
+ case kIOReturnOffline:
+ if (type&1) ddata_put_atom(data, "Offline");
+ if (type&2) ddata_put_string(data, "device offline");
+ break;
+ case kIOReturnNotReady:
+ if (type&1) ddata_put_atom(data, "NotReady");
+ if (type&2) ddata_put_string(data, "not ready");
+ break;
+ case kIOReturnNotAttached:
+ if (type&1) ddata_put_atom(data, "NotAttached");
+ if (type&2) ddata_put_string(data, "device not attached");
+ break;
+ case kIOReturnNoChannels:
+ if (type&1) ddata_put_atom(data, "NoChannels");
+ if (type&2) ddata_put_string(data, "no DMA channels left");
+ break;
+ case kIOReturnNoSpace:
+ if (type&1) ddata_put_atom(data, "NoSpace");
+ if (type&2) ddata_put_string(data, "no space for data");
+ break;
+ case kIOReturnPortExists:
+ if (type&1) ddata_put_atom(data, "PortExists");
+ if (type&2) ddata_put_string(data, "port already exists");
+ break;
+ case kIOReturnCannotWire:
+ if (type&1) ddata_put_atom(data, "CannotWire");
+ if (type&2) ddata_put_string(data, "can't wire down physical memory");
+ break;
+ case kIOReturnNoInterrupt:
+ if (type&1) ddata_put_atom(data, "NoInterrupt");
+ if (type&2) ddata_put_string(data, "no interrupt attached");
+ break;
+ case kIOReturnNoFrames:
+ if (type&1) ddata_put_atom(data, "NoFrames");
+ if (type&2) ddata_put_string(data, "no DMA frames enqueued");
+ break;
+ case kIOReturnMessageTooLarge:
+ if (type&1) ddata_put_atom(data, "MessageTooLarge");
+ if (type&2) ddata_put_string(data, "oversized msg received");
+ break;
+ case kIOReturnNotPermitted:
+ if (type&1) ddata_put_atom(data, "NotPermitted");
+ if (type&2) ddata_put_string(data, "not permitted");
+ break;
+ case kIOReturnNoPower:
+ if (type&1) ddata_put_atom(data, "NoPower");
+ if (type&2) ddata_put_string(data, "no power to device");
+ break;
+ case kIOReturnNoMedia:
+ if (type&1) ddata_put_atom(data, "NoMedia");
+ if (type&2) ddata_put_string(data, "media not present");
+ break;
+ case kIOReturnUnformattedMedia:
+ if (type&1) ddata_put_atom(data, "UnformattedMedia");
+ if (type&2) ddata_put_string(data, "media not formatted");
+ break;
+ case kIOReturnUnsupportedMode:
+ if (type&1) ddata_put_atom(data, "UnsupportedMode");
+ if (type&2) ddata_put_string(data, "no such mode");
+ break;
+ case kIOReturnUnderrun:
+ if (type&1) ddata_put_atom(data, "Underrun");
+ if (type&2) ddata_put_string(data, "data underrun");
+ break;
+ case kIOReturnOverrun:
+ if (type&1) ddata_put_atom(data, "Overrun");
+ if (type&2) ddata_put_string(data, "data overrun");
+ break;
+ case kIOReturnDeviceError:
+ if (type&1) ddata_put_atom(data, "DeviceError");
+ if (type&2) ddata_put_string(data, "the device is not working properly!");
+ break;
+ case kIOReturnNoCompletion:
+ if (type&1) ddata_put_atom(data, "NoCompletion");
+ if (type&2) ddata_put_string(data, "a completion routine is required");
+ break;
+ case kIOReturnAborted:
+ if (type&1) ddata_put_atom(data, "Aborted");
+ if (type&2) ddata_put_string(data, "operation aborted");
+ break;
+ case kIOReturnNoBandwidth:
+ if (type&1) ddata_put_atom(data, "NoBandwidth");
+ if (type&2) ddata_put_string(data, "bus bandwidth would be exceeded");
+ break;
+ case kIOReturnNotResponding:
+ if (type&1) ddata_put_atom(data, "NotResponding");
+ if (type&2) ddata_put_string(data, "device not responding");
+ break;
+ case kIOReturnIsoTooOld:
+ if (type&1) ddata_put_atom(data, "IsoTooOld");
+ if (type&2) ddata_put_string(data, "isochronous I/O request for distant past!");
+ break;
+ case kIOReturnIsoTooNew:
+ if (type&1) ddata_put_atom(data, "IsoTooNew");
+ if (type&2) ddata_put_string(data, "isochronous I/O request for distant future");
+ break;
+ case kIOReturnNotFound:
+ if (type&1) ddata_put_atom(data, "NotFound");
+ if (type&2) ddata_put_string(data, "data was not found");
+ break;
+ case kIOReturnInvalid:
+ if (type&1) ddata_put_atom(data, "Invalid");
+ if (type&2) ddata_put_string(data, "should never be seen");
+ break;
+ /* Add HCI error codes */
+ default:
+ DEBUGF("ERROR: code=%d, sub=%d, system=%d",
+ err_get_code(error),
+ err_get_sub(error),
+ err_get_system(error));
+ if (type&1) ddata_put_atom(data, "Unknown");
+ if (type&2) ddata_put_string(data, "do not know about this error");
+ break;
+ }
+}
+
+static void cleanup(subscription_t* s)
+{
+ DEBUGF("cleanup: %s", format_subscription(s));
+ if (s->handle != NULL) {
+ switch(s->type) {
+ case INQUIRY: {
+ IOBluetoothDeviceInquiry* obj = s->handle;
+ if (obj) {
+ InquiryDelegate* delegate = [obj delegate];
+ [delegate release];
+ [obj release];
+ }
+ break;
+ }
+
+ case REMOTE_NAME: {
+ IOBluetoothDeviceInquiry* obj = s->handle;
+ if (obj) {
+ RemoteNameDelegate* delegate = [obj delegate];
+ [delegate release];
+ [obj release];
+ }
+ break;
+ }
+
+ case CONNECT: {
+ IOBluetoothDeviceInquiry* obj = s->handle;
+ if (obj) {
+ ConnectDelegate* delegate = [obj delegate];
+ [delegate release];
+ [obj release];
+ }
+ break;
+ }
+
+ case SDP_QUERY: {
+ IOBluetoothDeviceInquiry* obj = s->handle;
+ if (obj) {
+ SDPQueryDelegate* delegate = [obj delegate];
+ [delegate release];
+ [obj release];
+ }
+ break;
+ }
+
+ case SDP: {
+ IOBluetoothSDPServiceRecord* serviceRecord = s->handle;
+ [serviceRecord removeServiceRecord];
+ [serviceRecord release];
+ break;
+ }
+ case RFCOMM: {
+ IOBluetoothRFCOMMChannel* obj = s->handle;
+ if (obj) {
+ RFCOMMChannelDelegate* delegate = [obj delegate];
+ [delegate release];
+ [obj release];
+ }
+ break;
+ }
+ case RFCOMM_LISTEN: {
+ IOBluetoothUserNotification* obj = s->handle;
+ if (obj)
+ [obj unregister];
+ // check all acceptors here?
+ [obj release];
+ break;
+ }
+ case L2CAP: {
+ IOBluetoothL2CAPChannel* obj = s->handle;
+ if (obj) {
+ L2CAPChannelDelegate* delegate = [obj delegate];
+ [delegate release];
+ [obj release];
+ }
+ break;
+ }
+ case L2CAP_LISTEN: {
+ IOBluetoothUserNotification* obj = s->handle;
+ if (obj)
+ [obj unregister];
+ [obj release];
+ break;
+ }
+ default: // warn?
+ break;
+ }
+ }
+}
+
+
+/* Send OK reply */
+void bt_ok(uint32_t cmdid)
+{
+ uint8_t buf[16];
+ ddata_t data;
+
+ ddata_init(&data, buf, sizeof(buf), 0);
+ ddata_put_UINT32(&data, 0);
+ ddata_put_tag(&data, REPLY_OK);
+ ddata_put_UINT32(&data, cmdid);
+ ddata_send(&data, 1);
+ ddata_final(&data);
+}
+
+/* Send ERROR reply */
+void bt_error(uint32_t cmdid, IOReturn err)
+{
+ uint8_t buf[128];
+ ddata_t data;
+
+ ddata_init(&data, buf, sizeof(buf), 0);
+ ddata_put_UINT32(&data, 0);
+ ddata_put_tag(&data, REPLY_ERROR);
+ ddata_put_UINT32(&data, cmdid);
+ ddata_put_io_error(&data, err, ERR_SHORT);
+ ddata_send(&data, 1);
+ ddata_final(&data);
+}
+
+// simple event
+void bt_event(uint32_t sid, const char* evtname)
+{
+ uint8_t buf[64];
+ ddata_t data;
+ ddata_init(&data, buf, sizeof(buf), 0);
+ ddata_put_UINT32(&data, 0);
+ ddata_put_tag(&data, REPLY_EVENT);
+ ddata_put_UINT32(&data, sid);
+ ddata_put_atom(&data, evtname);
+ ddata_send(&data, 1);
+ ddata_final(&data);
+}
+
+/*
+ * Given array of devices reply with
+ * OK LIST ADDR <addr> ... LIST_END
+ */
+void bt_device_list(NSArray* devices, ddata_t* data_out)
+{
+ NSUInteger i, n;
+
+ if (devices == NULL)
+ n = 0;
+ else
+ n = [devices count];
+ /* reply:8 cmdid:32 LIST ADDR <addr> ADDR <addr> LIST_END */
+ DEBUGF("bt_device_list n=%d", n);
+
+ ddata_put_tag(data_out, LIST);
+ for (i = 0; i < n; i++) {
+ DEBUGF("bt_device_list i=%d, n=%d", n);
+ IOBluetoothDevice* device = (IOBluetoothDevice*)
+ [devices objectAtIndex:i];
+ const BluetoothDeviceAddress* bt_addr = [device getAddress];
+ ddata_put_addr(data_out, bt_addr);
+ i++;
+ }
+ ddata_put_tag(data_out, LIST_END);
+}
+
+/*
+ * Given a IOBluetoothSDPDataElementRef produce a SDP attribute value element
+ * Format a binary version of SDP Element
+ */
+
+int ddata_put_sdp_elem(IOBluetoothSDPDataElement* value, ddata_t* data_out, int level)
+{
+ BluetoothSDPDataElementTypeDescriptor sdp_type;
+ BluetoothSDPDataElementSizeDescriptor sdp_size;
+ uint32_t byte_size;
+
+ sdp_type = [value getTypeDescriptor];
+ sdp_size = [value getSizeDescriptor];
+ byte_size = [value getSize];
+ DEBUGF(" %d: sdp:tag=%d,size=%d, avail=%d",
+ level,sdp_type<<3|(sdp_size&7),byte_size,
+ ddata_r_avail(data_out));
+
+ switch(sdp_type) {
+ case kBluetoothSDPDataElementTypeNil: /* only a tag byte */
+ ddata_put_UINT8(data_out, ((sdp_type<<3)|(sdp_size&0x7)));
+ break;
+ case kBluetoothSDPDataElementTypeUnsignedInt:
+ case kBluetoothSDPDataElementTypeSignedInt: {
+ ddata_put_UINT8(data_out, ((sdp_type<<3)|(sdp_size&0x7)));
+ switch(sdp_size) {
+ case 0: {
+ NSNumber* num = [value getNumberValue];
+ ddata_put_UINT8(data_out, num.unsignedCharValue);
+ break;
+ }
+ case 1: {
+ NSNumber* num = [value getNumberValue];
+ ddata_put_UINT16(data_out, num.unsignedShortValue);
+ break;
+ }
+ case 2: {
+ NSNumber* num = [value getNumberValue];
+ ddata_put_UINT32(data_out, num.unsignedLongValue);
+ break;
+ }
+ case 3: {
+ NSNumber* num = [value getNumberValue];
+ ddata_put_UINT64(data_out, num.unsignedLongLongValue);
+ break;
+ }
+ case 4: {
+ NSData* data = [value getDataValue];
+ NSUInteger len = [data length];
+ const void* ptr = [data bytes];
+ ddata_add(data_out, (uint8_t*)ptr, len);
+ break;
+ }
+ default:
+ return -1;
+ }
+ break;
+ }
+
+ case kBluetoothSDPDataElementTypeUUID: {
+ IOBluetoothSDPUUID* uuid = [value getUUIDValue];
+ NSUInteger len = [uuid length];
+ const void* ptr = [uuid bytes];
+ ddata_put_UINT8(data_out, ((sdp_type<<3)|(sdp_size&0x7)));
+ switch(sdp_size) {
+ case 1:
+ ddata_add(data_out, (uint8_t*)ptr, len);
+ break;
+ case 2:
+ ddata_add(data_out, (uint8_t*)ptr, len);
+ break;
+ case 4:
+ ddata_add(data_out, (uint8_t*)ptr, len);
+ break;
+ default:
+ return -1;
+ }
+ break;
+ }
+
+ case kBluetoothSDPDataElementTypeURL:
+ case kBluetoothSDPDataElementTypeString: {
+ NSString* str = [value getStringValue];
+ NSUInteger size = [str lengthOfBytesUsingEncoding:NSUTF8StringEncoding];
+ NSUInteger len = [str length];
+ NSUInteger ulen;
+ NSRange range = NSMakeRange(0, len);
+ uint8_t* ptr;
+
+ if (size < 256) {
+ ddata_put_UINT8(data_out, ((sdp_type<<3)|5));
+ ptr = ddata_alloc(data_out, 1+size);
+ DDATA_PUT_UINT8(ptr, size); ptr += 1;
+ [str getBytes:(void*)ptr maxLength:size
+ usedLength:&ulen encoding:NSUTF8StringEncoding
+ options:0 range:range remainingRange:NULL];
+ }
+ else if (size < 65536) {
+ ddata_put_UINT8(data_out, ((sdp_type<<3)|6));
+ ptr = ddata_alloc(data_out, 2+size);
+ DDATA_PUT_UINT16(ptr, size); ptr += 2;
+ [str getBytes:(void*)ptr maxLength:size
+ usedLength:&ulen encoding:NSUTF8StringEncoding
+ options:0 range:range remainingRange:NULL];
+ }
+ else {
+ ddata_put_UINT8(data_out, ((sdp_type<<3)|7));
+ ptr = ddata_alloc(data_out, 4+size);
+ DDATA_PUT_UINT32(ptr, size); ptr += 4;
+ [str getBytes:(void*)ptr maxLength:size
+ usedLength:&ulen encoding:NSUTF8StringEncoding
+ options:0 range:range remainingRange:NULL];
+ }
+ break;
+ }
+
+ case kBluetoothSDPDataElementTypeBoolean: {
+ NSNumber* num = [value getNumberValue];
+ ddata_put_UINT8(data_out, ((sdp_type<<3)|(sdp_size&0x7)));
+ ddata_put_UINT8(data_out, num.unsignedCharValue);
+ break;
+ }
+
+ case kBluetoothSDPDataElementTypeDataElementSequence:
+ case kBluetoothSDPDataElementTypeDataElementAlternative: {
+ NSArray* array = [value getArrayValue];
+ NSUInteger n = array.count;
+ NSUInteger i;
+ size_t bin_size;
+ size_t used_size;
+ intptr_t patch_offset;
+
+ // always use 4 byte header so we can patch
+ ddata_put_UINT8(data_out, ((sdp_type<<3)|(7)));
+ patch_offset = ddata_used(data_out); /* keep offset */
+ ddata_put_UINT32(data_out, 0); /* patch this later */
+
+ used_size = ddata_used(data_out); // size before
+ for (i = 0; i < n; i++) {
+ IOBluetoothSDPDataElement* elem =
+ (IOBluetoothSDPDataElement*) [array objectAtIndex:i];
+ if (ddata_put_sdp_elem(elem, data_out, level+1) < 0)
+ return -1;
+ }
+ bin_size = (ddata_used(data_out) - used_size); // size after
+ DDATA_PUT_UINT32(data_out->base+patch_offset, bin_size);
+ break;
+ }
+ default:
+ return -1;
+ }
+ return 0;
+}
+
+/*
+ * Ouput a SDP service record
+ * LIST ServiceAttributes LIST_END
+ */
+void ddata_put_sdp_service(IOBluetoothSDPServiceRecord* serv, ddata_t* data_out)
+{
+ NSDictionary* dict;
+ NSNumber* key;
+
+ ddata_put_tag(data_out, LIST);
+ dict = [serv attributes];
+
+ for (key in dict) {
+ uint16_t aid;
+ size_t bin_size;
+ size_t used_size;
+ intptr_t patch_offset;
+ uint8_t type;
+ IOBluetoothSDPDataElement* value = [dict objectForKey:key];
+
+ ddata_put_UINT8(data_out, BINARY);
+ patch_offset = ddata_used(data_out); /* keep offset */
+ ddata_put_UINT32(data_out, 0); /* patch this later */
+
+ // FIXME: do not assume key is a number?
+ aid = key.unsignedShortValue;
+ type=((kBluetoothSDPDataElementTypeUnsignedInt << 3) | 1);
+ ddata_put_UINT8(data_out, type);
+ ddata_put_UINT16(data_out, aid);
+ DEBUGF(" type:%d id=%d avail=%d", type, aid, ddata_r_avail(data_out));
+ used_size = ddata_used(data_out); // size before
+ ddata_put_sdp_elem(value, data_out, 0);
+ bin_size = 3 + (ddata_used(data_out) - used_size); // size after
+ DDATA_PUT_UINT32(data_out->base+patch_offset, bin_size);
+ }
+ ddata_put_tag(data_out, LIST_END);
+}
+
+/* read a UUID from data_in
+ * if data_in == 0 then all records are returned
+ * data_in == 2 UUID16 is assumed and searched for
+ * data_in == 4 UUID32 is assumed and searched for
+ * data_in == 16 UUID is assumed and searched
+ */
+
+int get_uuid(ddata_t* data_in, IOBluetoothSDPUUID** uuid)
+{
+ switch(ddata_r_avail(data_in)) {
+ case 2: {
+ BluetoothSDPUUID16 uuid16;
+ ddata_get_uint16(data_in, &uuid16);
+ *uuid = [IOBluetoothSDPUUID uuid16:uuid16];
+ break;
+ }
+ case 4: {
+ BluetoothSDPUUID32 uuid32;
+ ddata_get_uint32(data_in, &uuid32);
+ *uuid = [IOBluetoothSDPUUID uuid32:uuid32];
+ break;
+ }
+ case 16: {
+ *uuid = [IOBluetoothSDPUUID uuidWithBytes:data_in->rd length:16];
+ break;
+ }
+ case 0:
+ *uuid = NULL;
+ break;
+ default:
+ return 0;
+ }
+ return 1;
+}
+
+
+void bt_sdp_info(IOBluetoothDevice* device, IOBluetoothSDPUUID* uuid,
+ ddata_t* data_out)
+{
+ if (uuid != NULL) {
+ IOBluetoothSDPServiceRecord* serv =
+ [device getServiceRecordForUUID:uuid];
+ if (serv != NULL)
+ ddata_put_sdp_service(serv, data_out);
+ [uuid release];
+ }
+ else {
+ NSArray* services;
+
+ ddata_put_tag(data_out, LIST);
+
+ services = [device services];
+ if (services != NULL) {
+ NSUInteger i, n;
+
+ n = [services count];
+ DEBUGF("bt_sdp_info: %lu services found", n);
+ for (i = 0; i < n; i++) {
+ IOBluetoothSDPServiceRecord* serv = [services objectAtIndex:i];
+ DEBUGF("bt_sdp_info: service %lu %p", i, serv);
+ ddata_put_sdp_service(serv, data_out);
+ }
+ DEBUGF("bt_sdp_info: %lu services done", n);
+ }
+ ddata_put_tag(data_out, LIST_END);
+ }
+}
+
+/* construct a NSDictionary that makes up a Bluetooth service entry
+ * the input format MUST be on form
+ * LIST
+ * BINARY <sdp-attribute> <sdp-value>
+ * BINARY <sdp-attribute> <spd-value>
+ * BINARY <sdp-attribute> <sdp-value>
+ * LIST_END
+ *
+ */
+NSDictionary* bt_type_dict(int sdp_type)
+{
+ id keys[1];
+ id objects[1];
+
+ keys[0] = [NSString stringWithUTF8String:"DataElementType"];
+ objects[0] = [NSNumber numberWithInt:sdp_type];
+
+ return [NSDictionary dictionaryWithObjects:objects forKeys:keys count:1];
+}
+
+NSDictionary* bt_data_dict(int sdp_type, void* value)
+{
+ id keys[2];
+ id objects[2];
+
+ keys[0] = [NSString stringWithUTF8String:"DataElementType"];
+ objects[0] = [NSNumber numberWithInt:sdp_type];
+
+ keys[1] = [NSString stringWithUTF8String:"DataElementValue"];
+ objects[1] = value;
+
+ return [NSDictionary dictionaryWithObjects:objects forKeys:keys count:2];
+}
+
+NSDictionary* bt_value_dict(int sdp_type, int sdp_size, void* value)
+{
+ id keys[3];
+ id objects[3];
+
+ if (value == NULL)
+ return NULL;
+
+ keys[0] = [NSString stringWithUTF8String:"DataElementSize"];
+ objects[0] = [NSNumber numberWithInt:sdp_size];
+
+ keys[1] = [NSString stringWithUTF8String:"DataElementType"];
+ objects[1] = [NSNumber numberWithInt:sdp_type];
+
+ keys[2] = [NSString stringWithUTF8String:"DataElementValue"];
+ objects[2] = value;
+
+ return [NSDictionary dictionaryWithObjects:objects forKeys:keys count:3];
+}
+
+
+int bt_dynamic_len(ddata_t* data_in, int sdp_size, NSUInteger* length)
+{
+ switch(sdp_size) {
+ case 5: {
+ uint8_t len;
+ if (!ddata_get_uint8(data_in, &len)) return 0;
+ *length = (NSUInteger) len;
+ return 1;
+ }
+ case 6: {
+ uint16_t len;
+ if (!ddata_get_uint16(data_in, &len)) return 0;
+ *length = (NSUInteger) len;
+ return 1;
+ }
+ case 7: {
+ uint32_t len;
+ if (!ddata_get_uint32(data_in, &len)) return 0;
+ *length = (NSUInteger) len;
+ return 1;
+ }
+ default:
+ return 0;
+ }
+}
+
+int bt_fixed_len(int sdp_size, NSUInteger* length)
+{
+ switch(sdp_size) {
+ case 0: *length=1; return 1;
+ case 1: *length=2; return 1;
+ case 2: *length=4; return 1;
+ case 3: *length=8; return 1;
+ case 4: *length=16; return 1;
+ default: return 0;
+ }
+}
+
+
+NSObject* bt_get_sdp_value(ddata_t* data_in)
+{
+ uint8_t sdp_tag;
+ int sdp_type;
+ int sdp_size;
+
+ ddata_get_uint8(data_in, &sdp_tag);
+ sdp_type = (sdp_tag >> 3);
+ sdp_size = sdp_tag & 0x7;
+
+ switch(sdp_type) {
+ case kBluetoothSDPDataElementTypeNil:
+ return bt_type_dict(sdp_type);
+
+ case kBluetoothSDPDataElementTypeUnsignedInt:
+ case kBluetoothSDPDataElementTypeSignedInt: {
+ void* value;
+ switch(sdp_size) {
+ case 0: {
+ uint8_t uval;
+ int val;
+ ddata_get_uint8(data_in, &uval);
+ val = uval;
+ value = [NSNumber numberWithInt:val];
+ return bt_value_dict(sdp_type,1,value);
+ }
+ case 1: {
+ uint16_t uval;
+ int val;
+ if (!ddata_get_uint16(data_in, &uval)) return NULL;
+ val = uval;
+ value = [NSNumber numberWithInt:val];
+ return bt_value_dict(sdp_type,2,value);
+ }
+ case 2: {
+ uint32_t uval;
+ int val;
+ if (!ddata_get_uint32(data_in, &uval)) return NULL;
+ val = uval;
+ value = [NSNumber numberWithInt:val];
+ if (sdp_type == kBluetoothSDPDataElementTypeUnsignedInt)
+ return value; /* Special case ... */
+ else
+ return bt_value_dict(sdp_type,4,value);
+ }
+
+ case 3: {
+ value = [NSData dataWithBytes:data_in->rd length:8];
+ ddata_forward(data_in, 8);
+ return bt_data_dict(sdp_type,value);
+ }
+ case 4: {
+ value = [NSData dataWithBytes:data_in->rd length:16];
+ ddata_forward(data_in, 16);
+ return bt_data_dict(sdp_type,value);
+ }
+ default:
+ return NULL;
+ }
+ }
+
+
+ case kBluetoothSDPDataElementTypeUUID: {
+ NSData* data;
+ NSUInteger len;
+ if (!bt_fixed_len(sdp_size, &len)) return NULL;
+ if ((len == 8) || (len==1)) return NULL;
+ data = [NSData dataWithBytes:data_in->rd length:len];
+ ddata_forward(data_in, len);
+ return (void*) data;
+ }
+
+ case kBluetoothSDPDataElementTypeURL:
+ case kBluetoothSDPDataElementTypeString: {
+ NSUInteger len;
+ void* value;
+
+ if (!bt_dynamic_len(data_in, sdp_size, &len)) return NULL;
+
+ value = [[NSString alloc]initWithBytes:(const void*)data_in->rd
+ length:len
+ encoding:NSUTF8StringEncoding];
+ ddata_forward(data_in, len);
+ if (sdp_type == kBluetoothSDPDataElementTypeURL)
+ return bt_value_dict(sdp_type, sdp_size, value);
+ else
+ return value;
+ }
+
+ case kBluetoothSDPDataElementTypeBoolean: {
+ uint8_t bval;
+ int val;
+ void* value;
+
+ ddata_get_uint8(data_in, &bval);
+ val = bval;
+ value = [NSNumber numberWithInt:val];
+ return bt_value_dict(sdp_type,1,value);
+ }
+
+ case kBluetoothSDPDataElementTypeDataElementSequence:
+ case kBluetoothSDPDataElementTypeDataElementAlternative: {
+ NSMutableArray* array;
+ NSUInteger len;
+ uint8_t* end_ptr;
+
+ if (!bt_dynamic_len(data_in, sdp_size, &len)) return NULL;
+
+ end_ptr = data_in->rd + len;
+
+ array = [[NSMutableArray alloc] init];
+ while(data_in->rd < end_ptr) {
+ id value = bt_get_sdp_value(data_in);
+ if (value == NULL) {
+ [array release];
+ return NULL;
+ }
+ [array addObject:value];
+ }
+ if (sdp_type==kBluetoothSDPDataElementTypeDataElementAlternative)
+ return bt_data_dict(sdp_type, array);
+ else
+ return array;
+ }
+
+ default:
+ return NULL;
+ }
+}
+
+NSDictionary* bt_make_sdp_dict(ddata_t* data_in)
+{
+ uint8_t tag;
+ NSMutableDictionary* servDict;
+ void* value;
+
+ if (!ddata_get_uint8(data_in, &tag) || (tag != LIST))
+ return NULL;
+ if ((servDict = [[NSMutableDictionary alloc] initWithCapacity:10]) == NULL)
+ return NULL;
+
+ DEBUGF("Service add: found LIST tag", 0);
+
+ while(ddata_get_uint8(data_in, &tag)) {
+ if (tag == LIST_END) {
+ DEBUGF("Service add: found LIST_END tag", 0);
+ return servDict;
+ }
+ else if (tag == BINARY) {
+ uint32_t bin_sz;
+ uint8_t sdp_tag;
+ uint8_t sdp_type;
+ uint8_t sdp_size;
+ uint16_t attributeID;
+ NSString* key;
+ char attr[5];
+
+ if (!ddata_get_uint32(data_in, &bin_sz)) goto error;
+ DEBUGF("Service add: found BINARY size=%d", bin_sz);
+ if (!ddata_get_uint8(data_in, &sdp_tag)) goto error;
+ sdp_type = (sdp_tag >> 3);
+ sdp_size = sdp_tag & 0x7;
+ if (sdp_type != kBluetoothSDPDataElementTypeUnsignedInt) goto error;
+ if (sdp_size != 1) goto error;
+ if (!ddata_get_uint16(data_in, &attributeID)) goto error;
+
+ /* Generate the attribute ID as Hex String Key 4 digits */
+ snprintf(attr, 5, "%04X", attributeID);
+ DEBUGF("Service add: found attribute %s", attr);
+ key = [[NSString alloc]initWithBytes:(const void*)attr
+ length:4 encoding:NSUTF8StringEncoding];
+ value = bt_get_sdp_value(data_in);
+ [servDict setObject:value forKey:key];
+ }
+ else
+ goto error;
+ }
+
+error:
+ return NULL;
+}
+
+void bt_local_info(ddata_t* data_in, ddata_t* data_out)
+{
+ uint8_t op_code;
+
+ ddata_put_tag(data_out, LIST);
+
+ while(ddata_get_uint8(data_in, &op_code)) {
+ switch(op_code) {
+ case NFO_LOCAL_NAME: {
+ NSString* name =
+ [[IOBluetoothHostController defaultController] nameAsString];
+ put_ns_string(data_out, name);
+ break;
+ }
+
+ case NFO_LOCAL_CLASS: {
+ BluetoothClassOfDevice value =
+ [[IOBluetoothHostController defaultController] classOfDevice];
+ ddata_put_uint32(data_out, value);
+ break;
+ }
+
+ case NFO_LOCAL_ADDRESS: {
+ BluetoothDeviceAddress addr;
+ NSString* addrStr =
+ [[IOBluetoothHostController defaultController] addressAsString];
+ IOBluetoothNSStringToDeviceAddress(addrStr, &addr);
+ ddata_put_addr(data_out, &addr);
+ break;
+ }
+
+ case NFO_LOCAL_DISCOVERABLE: {
+ // Boolean value;
+ // if (IOBluetoothLocalDeviceGetDiscoverable(&value) == kIOReturnSuccess)
+ ddata_put_boolean(data_out, true);
+ break;
+ }
+
+ case NFO_LOCAL_POWER_STATE: {
+ BluetoothHCIPowerState powerState =
+ [[IOBluetoothHostController defaultController] powerState];
+ if (powerState == kBluetoothHCIPowerStateON)
+ ddata_put_atom(data_out, "on");
+ else if (powerState == kBluetoothHCIPowerStateOFF)
+ ddata_put_atom(data_out, "off");
+ else if (powerState == kBluetoothHCIPowerStateUnintialized)
+ ddata_put_atom(data_out, "unintialized");
+ else
+ ddata_put_atom(data_out, "unknown");
+ break;
+ }
+ default:
+ break;
+ }
+ }
+ ddata_put_tag(data_out, LIST_END);
+}
+
+
+void bt_device_info(IOBluetoothDevice* device, ddata_t* data_in, ddata_t* data_out)
+{
+ uint8_t op_code;
+
+ ddata_put_tag(data_out, LIST);
+
+ while(ddata_get_uint8(data_in, &op_code)) {
+ switch(op_code) {
+ case NFO_DEVICE_NAME: {
+ NSString* name = [device name];
+ put_ns_string(data_out, name);
+ break;
+ }
+
+ case NFO_DEVICE_CLASS: {
+ BluetoothClassOfDevice value = [device classOfDevice];
+ ddata_put_uint32(data_out, value);
+ break;
+ }
+
+ case NFO_DEVICE_CLOCK: {
+ BluetoothClockOffset value = [device getClockOffset];
+ ddata_put_uint16(data_out, value);
+ break;
+ }
+
+ case NFO_DEVICE_IS_FAVORITE: {
+ Boolean value = [device isFavorite];
+ ddata_put_boolean(data_out, value);
+ break;
+
+ }
+
+ case NFO_DEVICE_IS_PAIRED: {
+ Boolean value = [device isPaired];
+ ddata_put_boolean(data_out, value);
+ break;
+ }
+
+ case NFO_DEVICE_IS_CONNECTED: {
+ Boolean value = [device isConnected];
+ ddata_put_boolean(data_out, value);
+ break;
+ }
+
+ case NFO_DEVICE_INQUIRY: {
+ NSDate* date = [device getLastInquiryUpdate];
+ put_date(data_out, date);
+ break;
+ }
+ case NFO_DEVICE_ACCESS: {
+ NSDate* date = [device recentAccessDate];
+ put_date(data_out, date);
+ break;
+ }
+ case NFO_DEVICE_UPDATE: {
+ NSDate* date = [device getLastServicesUpdate];
+ put_date(data_out, date);
+ break;
+ }
+ default:
+ break;
+ }
+ }
+ ddata_put_tag(data_out, LIST_END);
+
+}
+
+// -----------------------------------------------------------------------
+// InquiryDelegate
+// -----------------------------------------------------------------------
+
+@implementation InquiryDelegate
+
+- (instancetype)initWithSub:(subscription_t*)s
+{
+ if (self = [super init]) {
+ mSub = s; // weak!
+ }
+ return self;
+}
+
+- (void) dealloc
+{
+ DEBUGF("InquiryDelegate: dealloc");
+ [super dealloc];
+}
+
+- (void) deviceInquiryStarted:(IOBluetoothDeviceInquiry*)sender
+{
+ (void) sender;
+ uint8_t buf[64];
+ ddata_t data;
+
+ DEBUGF("InquiryDelegate: started");
+
+ ddata_init(&data, buf, sizeof(buf), 0);
+ ddata_put_UINT32(&data, 0);
+ ddata_put_tag(&data, REPLY_EVENT);
+ ddata_put_UINT32(&data, mSub->id);
+ ddata_put_atom(&data, "started");
+
+ ddata_send(&data, 1);
+ ddata_final(&data);
+}
+
+- (void) deviceInquiryDeviceFound:(IOBluetoothDeviceInquiry*)sender
+device:(IOBluetoothDevice*)device
+{
+ (void) sender;
+ const BluetoothDeviceAddress* bt_addr = [device getAddress];
+ uint8_t buf[64];
+ ddata_t data;
+
+ DEBUGF("InquiryDelegate: device found");
+
+ ddata_init(&data, buf, sizeof(buf), 0);
+ ddata_put_UINT32(&data, 0);
+ ddata_put_tag(&data, REPLY_EVENT);
+ ddata_put_UINT32(&data, mSub->id);
+ ddata_put_tag(&data, TUPLE);
+ ddata_put_atom(&data, "device");
+ ddata_put_addr(&data, bt_addr);
+ ddata_put_tag(&data, TUPLE_END);
+
+ ddata_send(&data, 1);
+ ddata_final(&data);
+}
+
+- (void) deviceInquiryUpdatingDeviceNamesStarted:(IOBluetoothDeviceInquiry*)sender devicesRemaining:(uint32_t)devicesRemaining
+{
+ (void) sender;
+ (void) devicesRemaining;
+
+ DEBUGF("InquiryDelegate: %d name started: devicesRemaining=%d",
+ mSub->id, devicesRemaining);
+}
+
+
+- (void) deviceInquiryDeviceNameUpdated:(IOBluetoothDeviceInquiry*)sender device:(IOBluetoothDevice*)device devicesRemaining:(uint32_t)devicesRemaining
+{
+ (void) sender;
+ (void) device;
+ (void) devicesRemaining;
+
+ DEBUGF("InquiryDelegate: %d device name updated: deviceRemaining=%d",
+ mSub->id, devicesRemaining);
+}
+
+- (void) deviceInquiryComplete:(IOBluetoothDeviceInquiry*)sender error:(IOReturn)error aborted:(BOOL)aborted
+{
+ (void) sender;
+ (void) error;
+
+ DEBUGF("InquiryDelegate: %d stopped",mSub->id);
+
+ if (!aborted) { /* no need to send event on user abort */
+ uint8_t buf[64];
+ ddata_t data;
+
+ ddata_init(&data, buf, sizeof(buf), 0);
+ ddata_put_UINT32(&data, 0);
+ ddata_put_tag(&data, REPLY_EVENT);
+ ddata_put_UINT32(&data, mSub->id);
+ ddata_put_atom(&data, "stopped");
+ ddata_send(&data, 1);
+ ddata_final(&data);
+ }
+}
+
+@end
+
+// -----------------------------------------------------------------------
+// RemoteNameDelegate
+// -----------------------------------------------------------------------
+
+@implementation RemoteNameDelegate
+
+- (instancetype)initWithSub:(subscription_t*) s
+{
+ if (self = [super init]) {
+ mSub = s; // weak
+ }
+ return self;
+}
+
+- (void) dealloc
+{
+ DEBUGF("RemoteNameDelegate dealloc");
+ [super dealloc];
+}
+
+- (void)remoteNameRequestComplete:(IOBluetoothDevice *)device status:(IOReturn)status
+{
+ if (status == kIOReturnSuccess) {
+ uint8_t buf[265];
+ ddata_t data;
+ NSString* name = [device name];
+
+ ddata_init(&data, buf, sizeof(buf), 0);
+ ddata_put_UINT32(&data, 0);
+
+ ddata_put_tag(&data, REPLY_OK);
+ ddata_put_UINT32(&data, mSub->cmdid);
+ put_ns_string(&data, name);
+ ddata_send(&data, 1);
+ ddata_final(&data);
+ }
+ else
+ bt_error(mSub->cmdid, status);
+ // only for Inquiry objects
+ release_subscription(mSub);
+}
+
+@end
+
+// -----------------------------------------------------------------------
+// SDPQueryDelegate
+// -----------------------------------------------------------------------
+
+@implementation SDPQueryDelegate
+
+- (instancetype)initWithSub:(subscription_t*) s andUUID:(IOBluetoothSDPUUID*) uuid
+{
+ DEBUGF("SDPQueryDelegate: alloc");
+ if (self = [super init]) {
+ mSub = s; // weak
+ mUUID = [uuid retain];
+ }
+ return self;
+}
+
+- (void) dealloc
+{
+ DEBUGF("SDPQueryDelegate: dealloc");
+ [mUUID release];
+ [super dealloc];
+}
+
+- (void)sdpQueryComplete:(IOBluetoothDevice *)device status:(IOReturn)status
+{
+ uint8_t out_buf[2048];
+ ddata_t data_out;
+
+ if (status == kIOReturnSuccess) {
+ ddata_init(&data_out, out_buf, sizeof(out_buf), 0);
+ ddata_put_UINT32(&data_out, 0);
+ ddata_put_tag(&data_out, REPLY_OK);
+ ddata_put_UINT32(&data_out, mSub->cmdid);
+ bt_sdp_info(device, mUUID, &data_out);
+ ddata_send(&data_out, 1);
+ ddata_final(&data_out);
+ }
+ else
+ bt_error(mSub->cmdid, status);
+ // only for Inquiry objects
+ release_subscription(mSub);
+}
+@end
+
+// -----------------------------------------------------------------------
+// Connectelegate
+// -----------------------------------------------------------------------
+
+@implementation ConnectDelegate
+
+- (instancetype)initWithSub:(subscription_t*) s
+{
+ if (self = [super init]) {
+ mSub = s; // weak
+ }
+ return self;
+}
+
+- (void) dealloc
+{
+ DEBUGF("Connectelegate: dealloc");
+ [super dealloc];
+}
+
+
+- (void)connectionComplete:(IOBluetoothDevice *)device status:(IOReturn)status
+{
+ (void) device;
+ if (status == kIOReturnSuccess)
+ bt_ok(mSub->cmdid);
+ else
+ bt_error(mSub->cmdid, status);
+ release_subscription(mSub);
+}
+@end
+
+// -----------------------------------------------------------------------
+// RFCOMMChannelDelegate
+// -----------------------------------------------------------------------
+
+@implementation RFCOMMChannelDelegate
+
+- (instancetype)initWithSub:(subscription_t*) s andCtx:(bt_ctx_t*) ctx
+{
+ if (self = [super init]) {
+ mSub = s; // weak
+ mCtx = ctx;
+ }
+ return self;
+}
+
+- (void) dealloc
+{
+ DEBUGF("RFCOMMChannelDelegate: dealloc");
+ [super dealloc];
+}
+
+- (void)rfcommChannelData:(IOBluetoothRFCOMMChannel*)rfcommChannel data:(void *)dataPointer length:(size_t)dataLength
+{
+ uint8_t buf[128];
+ ddata_t data;
+ (void) rfcommChannel;
+
+ DEBUGF("RFCOMMChannelDelegate: Data size=%d", dataLength);
+ ddata_init(&data, buf, sizeof(buf), 0);
+ ddata_put_UINT32(&data, 0);
+ ddata_put_tag(&data, REPLY_EVENT);
+ ddata_put_UINT32(&data, mSub->id);
+ ddata_put_tag(&data, TUPLE);
+ ddata_put_atom(&data, "data");
+ ddata_put_binary(&data, dataPointer, dataLength);
+ ddata_put_tag(&data, TUPLE_END);
+ ddata_send(&data, 1);
+ ddata_final(&data);
+}
+
+- (void)rfcommChannelOpenComplete:(IOBluetoothRFCOMMChannel*)rfcommChannel status:(IOReturn)error
+{
+ (void) rfcommChannel;
+ (void) error;
+ DEBUGF("RFCOMMChannelDelegate: OpenComplete error=%d", error);
+ bt_ok(mSub->cmdid);
+ mSub->cmdid = 0;
+}
+
+- (void)rfcommChannelClosed:(IOBluetoothRFCOMMChannel*)rfcommChannel
+{
+ (void) rfcommChannel;
+ DEBUGF("RFCOMMChannelDelegate: Closed");
+ if (mSub->cmdid == 0) {
+ uint8_t buf[64];
+ ddata_t data;
+ ddata_init(&data, buf, sizeof(buf), 0);
+ ddata_put_UINT32(&data, 0);
+ ddata_put_tag(&data, REPLY_EVENT);
+ ddata_put_UINT32(&data, mSub->id);
+ ddata_put_atom(&data, "closed");
+ ddata_send(&data, 1);
+ ddata_final(&data);
+ }
+ else {
+ bt_ok(mSub->cmdid);
+ mSub->cmdid = 0;
+ }
+ remove_subscription(&mCtx->list,RFCOMM,mSub->id);
+}
+
+- (void)rfcommChannelControlSignalsChanged:(IOBluetoothRFCOMMChannel*)rfcommChannel
+{
+ (void) rfcommChannel;
+ DEBUGF("RFCOMMChannelDelegate: ControlSignalsChanged");
+}
+
+- (void)rfcommChannelFlowControlChanged:(IOBluetoothRFCOMMChannel*)rfcommChannel
+{
+ if ([rfcommChannel isTransmissionPaused])
+ DEBUGF("RFCOMMChannelDelegate: FlowControlChanged OFF");
+ else
+ DEBUGF("RFCOMMChannelDelegate: FlowControlChanged ON");
+}
+
+- (void)rfcommChannelWriteComplete:(IOBluetoothRFCOMMChannel*)rfcommChannel refcon:(void*)refcon status:(IOReturn)error
+{
+ ddata_t* out = (ddata_t*) refcon;
+ uint32_t len;
+ (void) error;
+
+ // FIXME: handle error!
+
+ DEBUGF("RFCOMMChannelDelegate: WriteComplete");
+ if ((len=ddata_r_avail(out)) == 0) {
+ ddata_free(out);
+ bt_ok(mSub->cmdid);
+ mSub->cmdid = 0;
+ }
+ else {
+ BluetoothRFCOMMMTU mtu = [rfcommChannel getMTU];
+ uint8_t* ptr = out->wr;
+ IOReturn err;
+ if (len > mtu) len = mtu;
+ ddata_forward(out, len); /* move to next block */
+ err = [rfcommChannel writeAsync:ptr length:len refcon:out];
+ if (err != kIOReturnSuccess) {
+ bt_error(mSub->cmdid, err);
+ mSub->cmdid = 0;
+ ddata_free(out);
+ }
+ }
+}
+
+- (void)rfcommChannelQueueSpaceAvailable:(IOBluetoothRFCOMMChannel*)rfcommChannel
+{
+ (void) rfcommChannel;
+ // FIXME: probably a good place to fill output data here ?
+ DEBUGF("RFCOMMChannelDelegate: QueueSpaceAvailable");
+}
+
+@end
+
+// -----------------------------------------------------------------------
+// L2CAPChannelDelegate
+// -----------------------------------------------------------------------
+
+@implementation L2CAPChannelDelegate
+
+- (instancetype)initWithSub:(subscription_t*) s andCtx:(bt_ctx_t*) ctx
+{
+ if (self = [super init]) {
+ mSub = s; // weak
+ mCtx = ctx;
+ }
+ return self;
+}
+
+- (void) dealloc
+{
+ DEBUGF("L2CAPDelegate: dealloc");
+ [super dealloc];
+}
+
+- (void)l2capChannelData:(IOBluetoothL2CAPChannel*)l2capChannel data:(void *)dataPointer length:(size_t)dataLength
+{
+ uint8_t buf[128];
+ ddata_t data;
+ (void) l2capChannel;
+
+ DEBUGF("L2CAPChannelDelegate: Data size=%d", dataLength);
+ ddata_init(&data, buf, sizeof(buf), 0);
+ ddata_put_UINT32(&data, 0);
+ ddata_put_tag(&data, REPLY_EVENT);
+ ddata_put_UINT32(&data, mSub->id);
+ ddata_put_tag(&data, TUPLE);
+ ddata_put_atom(&data, "data");
+ ddata_put_binary(&data, dataPointer, dataLength);
+ ddata_put_tag(&data, TUPLE_END);
+ ddata_send(&data, 1);
+ ddata_final(&data);
+}
+
+- (void)l2capChannelOpenComplete:(IOBluetoothL2CAPChannel*)l2capChannel status:(IOReturn)error
+{
+ (void) l2capChannel;
+ (void) error; // fixme check this
+ DEBUGF("L2CAPChannelDelegate: OpenComplete");
+ bt_ok(mSub->cmdid);
+ mSub->cmdid = 0;
+}
+
+- (void)l2capChannelClosed:(IOBluetoothL2CAPChannel*)l2capChannel
+{
+ (void) l2capChannel;
+ DEBUGF("L2CAPChannelDelegate: Closed this");
+ if (mSub->cmdid == 0) {
+ uint8_t buf[64];
+ ddata_t data;
+
+ ddata_init(&data, buf, sizeof(buf), 0);
+ ddata_put_UINT32(&data, 0);
+ ddata_put_tag(&data, REPLY_EVENT);
+ ddata_put_UINT32(&data, mSub->id);
+ ddata_put_atom(&data, "closed");
+ ddata_send(&data, 1);
+ ddata_final(&data);
+ }
+ else {
+ bt_ok(mSub->cmdid);
+ }
+ remove_subscription(&mCtx->list,L2CAP,mSub->id);
+}
+
+- (void)l2capChannelReconfigured:(IOBluetoothL2CAPChannel*)l2capChannel
+{
+ (void) l2capChannel;
+ DEBUGF("L2CAPChannelDelegate: Reconfigured");
+}
+
+- (void)l2capChannelWriteComplete:(IOBluetoothL2CAPChannel*)l2capChannel refcon:(void*)refcon status:(IOReturn)error
+{
+ ddata_t* out = (ddata_t*) refcon;
+ uint32_t len;
+ (void)error;
+
+ DEBUGF("L2CAPChannelDelegate: WriteComplete");
+ if ((len=ddata_r_avail(out)) == 0) {
+ ddata_free(out);
+ bt_ok(mSub->cmdid);
+ mSub->cmdid = 0;
+ }
+ else {
+ BluetoothL2CAPMTU mtu = [l2capChannel outgoingMTU];
+ uint8_t* ptr = out->wr;
+ IOReturn err;
+ if (len > mtu) len = mtu;
+ ddata_forward(out, len); /* move to next block */
+ err = [l2capChannel writeAsync:ptr length:len refcon:out];
+ if (err != kIOReturnSuccess) {
+ bt_error(mSub->cmdid, err);
+ mSub->cmdid=0;
+ ddata_free(out);
+ }
+ }
+}
+
+- (void)l2capChannelQueueSpaceAvailable:(IOBluetoothL2CAPChannel*)l2capChannel
+{
+ (void) l2capChannel;
+ DEBUGF("L2CAPChannelDelegate: QueueSpaceAvailable");
+}
+
+@end
+
+// -----------------------------------------------------------------------
+
+
+/* check if we have a listner setup channel event listner */
+void rfcomm_accept(subscription_t* listen)
+{
+ listen_queue_t* lq = (listen_queue_t*) listen->opaque;
+ bt_ctx_t* ctx = lq->ctx;
+
+ if (listen->type != RFCOMM_LISTEN) {
+ DEBUGF("RFCOMM: not a listen subscription", 0);
+ }
+ else if (lq->qh == lq->qt) {
+ DEBUGF("RFCOMM: listen queue empty");
+ }
+ else {
+ subscription_link_t* link;
+
+ if ((link = lq->wait.last) == NULL) { // peek
+ DEBUGF("RFCOMM: no acceptor");
+ }
+ else {
+ RFCOMMChannelDelegate* delegate;
+ subscription_t* s = link->s;
+
+ delegate = [[RFCOMMChannelDelegate alloc] initWithSub:s andCtx:ctx];
+
+ /* loop until we find a working channel */
+ while (lq->qh != lq->qt) {
+ IOBluetoothRFCOMMChannel* channel =
+ (IOBluetoothRFCOMMChannel*) lq->qelem[lq->qt];
+ IOReturn err;
+
+ lq->qelem[lq->qt] = NULL;
+ lq->qt = (lq->qt+1) % LISTEN_QUEUE_LENGTH;
+
+ err = [channel setDelegate:delegate];
+
+ if (err != kIOReturnSuccess) {
+ DEBUGF("RFCOMM: accept error %d:", err);
+ [channel closeChannel];
+ }
+ else {
+ const BluetoothDeviceAddress* addr;
+ BluetoothRFCOMMChannelID channel_id;
+ IOBluetoothDevice* device;
+ uint8_t buf[64];
+ ddata_t data;
+
+ if ((device = [channel getDevice]) == NULL) {
+ DEBUGF("RFCOMM: accept no device");
+ [channel closeChannel];
+ [channel release];
+ continue;
+ }
+ if ((addr = [device getAddress]) == NULL) {
+ DEBUGF("RFCOMM: accept no address");
+ [channel closeChannel];
+ [channel release];
+ continue;
+ }
+ s->handle = channel;
+ s->accept = NULL;
+ channel_id = [channel getChannelID];
+
+ DEBUGF("RFCOMM: accept on %d", channel_id);
+
+ unlink_subscription(link);
+
+ /* send EVENT id {accept,Address,Channel} */
+ ddata_init(&data, buf, sizeof(buf), 0);
+ ddata_put_UINT32(&data, 0);
+ ddata_put_tag(&data, REPLY_EVENT);
+ ddata_put_UINT32(&data, s->id);
+ ddata_put_tag(&data, TUPLE);
+ ddata_put_atom(&data, "accept");
+ ddata_put_addr(&data, addr);
+ ddata_put_uint8(&data, channel_id);
+ ddata_put_tag(&data, TUPLE_END);
+ ddata_send(&data, 1);
+ ddata_final(&data);
+ return;
+ }
+ }
+ [delegate release]; // no working channel found
+ }
+ }
+}
+
+// special open callback !
+void cb_rfcomm_open(void * userRefCon, IOBluetoothUserNotificationRef inRef, IOBluetoothObjectRef objectRef )
+{
+ IOBluetoothRFCOMMChannel* channel = (IOBluetoothRFCOMMChannel*) objectRef;
+ subscription_t* listen = (subscription_t*) userRefCon;
+ listen_queue_t* lq = (listen_queue_t*) listen->opaque;
+ int qh_next;
+ (void) inRef;
+
+ DEBUGF("RFCOMM: OpenNotification", 0);
+ qh_next = (lq->qh+1) % LISTEN_QUEUE_LENGTH;
+ if (qh_next == lq->qt) {
+ DEBUGF("RFCOMM: listen queue full");
+ /* If queue is full possibly delete the oldest/newest ? */
+ // close it?
+ return;
+ }
+ else {
+ lq->qelem[lq->qh] = channel;
+ lq->qh = qh_next;
+ [channel retain];
+ rfcomm_accept(listen);
+ }
+}
+
+/* check if we have a listner setup channel event listner */
+void l2cap_accept(subscription_t* listen)
+{
+ listen_queue_t* lq = (listen_queue_t*) listen->opaque;
+ bt_ctx_t* ctx = lq->ctx;
+
+ if (listen->type != L2CAP_LISTEN) {
+ DEBUGF("L2CAP: not a listen subscription", 0);
+ }
+ else if (lq->qh == lq->qt) {
+ DEBUGF("L2CAP: listen queue empty", 0);
+ }
+ else {
+ subscription_link_t* link;
+
+ if ((link = lq->wait.last) == NULL) { // peek
+ DEBUGF("L2CAP: no acceptor");
+ }
+ else {
+ L2CAPChannelDelegate* delegate;
+ subscription_t* s = link->s;
+
+ delegate = [[L2CAPChannelDelegate alloc] initWithSub:s andCtx:ctx];
+
+ /* loop until we find a working channel */
+ while (lq->qh != lq->qt) {
+ IOBluetoothL2CAPChannel* channel =
+ (IOBluetoothL2CAPChannel*) lq->qelem[lq->qt];
+ IOReturn err;
+
+ lq->qelem[lq->qt] = NULL;
+ lq->qt = (lq->qt+1) % LISTEN_QUEUE_LENGTH;
+
+ err = [channel setDelegate:delegate];
+
+ if (err != kIOReturnSuccess) {
+ DEBUGF("L2CAP: accept error %d:", err);
+ [channel closeChannel];
+ [channel release];
+ }
+ else {
+ const BluetoothDeviceAddress* addr;
+ BluetoothL2CAPPSM psm;
+ IOBluetoothDevice* device;
+ uint8_t buf[64];
+ ddata_t data;
+
+ if ((device = [channel device]) == NULL) {
+ DEBUGF("L2CAP: accept no device", 0);
+ [channel closeChannel];
+ [channel release];
+ continue;
+ }
+ psm = [channel PSM];
+ if ((addr = [device getAddress]) == NULL) {
+ DEBUGF("L2CAP: accept no address", 0);
+ [channel closeChannel];
+ [channel release];
+ continue;
+ }
+ DEBUGF("L2CAP: accept psm=%d", psm);
+ s->handle = channel;
+ s->accept = NULL;
+ unlink_subscription(link);
+
+ /* send EVENT id {accept,Address,Psm} */
+ ddata_init(&data, buf, sizeof(buf), 0);
+ ddata_put_UINT32(&data, 0);
+ ddata_put_tag(&data, REPLY_EVENT);
+ ddata_put_UINT32(&data, s->id);
+ ddata_put_tag(&data, TUPLE);
+ ddata_put_atom(&data, "accept");
+ ddata_put_addr(&data, addr);
+ ddata_put_uint16(&data, psm);
+ ddata_put_tag(&data, TUPLE_END);
+ ddata_send(&data, 1);
+ ddata_final(&data);
+ return;
+ }
+ }
+ [delegate release]; // no working channel found
+ }
+ }
+}
+
+void cb_l2cap_open(void * userRefCon, IOBluetoothUserNotificationRef inRef, IOBluetoothObjectRef objectRef )
+{
+ IOBluetoothL2CAPChannel* l2capChannel =
+ (IOBluetoothL2CAPChannel*) objectRef;
+ subscription_t* listen = (subscription_t*) userRefCon;
+ listen_queue_t* lq = (listen_queue_t*) listen->opaque;
+ int qh_next;
+ (void) inRef;
+
+ DEBUGF("L2CAP: OpenNotification", 0);
+ qh_next = (lq->qh+1) % LISTEN_QUEUE_LENGTH;
+ if (qh_next == lq->qt) {
+ DEBUGF("L2CAP: listen queue full", 0);
+ /* If queue is full possibly delete the oldest/newest ? */
+ // close?
+ return;
+ }
+ else {
+ lq->qelem[lq->qh] = l2capChannel;
+ lq->qh = qh_next;
+ [l2capChannel retain];
+ l2cap_accept(listen);
+ }
+}
+
+
+void bt_command(bt_ctx_t* ctx, const uint8_t* src, uint32_t src_len)
+{
+ uint8_t op = 0;
+ uint32_t cmdid = 0;
+ IOReturn bt_error = kIOReturnSuccess;
+ uint8_t out_buf[2048];
+ ddata_t data_in;
+ ddata_t data_out;
+
+ // dump subscription list
+ if (dlog_debug_level == DLOG_DEBUG) {
+ subscription_link_t* p = ctx->list.first;
+ fprintf(stderr, "ctx.list = {");
+ while(p) {
+ fprintf(stderr, " %s,", format_subscription(p->s));
+ p = p->next;
+ }
+ fprintf(stderr, "}\r\n");
+ }
+
+ ddata_r_init(&data_in, (uint8_t*)src, src_len, 0);
+ ddata_init(&data_out, out_buf, sizeof(out_buf), 0);
+ ddata_put_UINT32(&data_out, 0);
+
+ if (!ddata_get_uint8(&data_in, &op))
+ goto badarg;
+ if (!ddata_get_uint32(&data_in, &cmdid))
+ goto badarg;
+
+ switch (op) {
+ case CMD_PING: {
+ DEBUGF("CMD_PING cmdid=%d", cmdid);
+ ddata_put_tag(&data_out, REPLY_OK);
+ ddata_put_UINT32(&data_out, cmdid);
+ ddata_put_string(&data_out, "pong");
+ goto reply;
+ }
+
+ case CMD_DEBUG: { // <<level>>
+ int level;
+ DEBUGF("CMD_DEBUG cmdid=%d", cmdid);
+ if (!ddata_get_int32(&data_in, &level))
+ goto badarg;
+ dlog_set_debug(level);
+ ddata_put_tag(&data_out, REPLY_OK);
+ ddata_put_UINT32(&data_out, cmdid);
+ goto reply;
+ }
+
+ case CMD_PAIRED_DEVICES: {
+ NSArray* devices = [IOBluetoothDevice pairedDevices];
+ DEBUGF("CMD_PAIRED_DEVICE cmdid=%d", cmdid);
+
+ ddata_put_tag(&data_out, REPLY_OK);
+ ddata_put_UINT32(&data_out, cmdid);
+ bt_device_list(devices, &data_out);
+ [devices release];
+ goto reply;
+ }
+
+ case CMD_FAVORITE_DEVICES: {
+ NSArray* devices = [IOBluetoothDevice favoriteDevices];
+
+ DEBUGF("CMD_FAVORITE_DEVICE cmdid=%d", cmdid);
+
+ ddata_put_tag(&data_out, REPLY_OK);
+ ddata_put_UINT32(&data_out, cmdid);
+ bt_device_list(devices, &data_out);
+ [devices release];
+ goto reply;
+ }
+
+ case CMD_RECENT_DEVICES: {
+ NSArray* devices = [IOBluetoothDevice recentDevices:0];
+
+ DEBUGF("CMD_RECENT_DEVICES cmdid=%d", cmdid);
+
+ ddata_put_tag(&data_out, REPLY_OK);
+ ddata_put_UINT32(&data_out, cmdid);
+ bt_device_list(devices, &data_out);
+ [devices release];
+ goto reply;
+ }
+
+ case CMD_INQUIRY_START: { /* arguments: id:32 secs:32 */
+ /* FIXME: check that no inquery is running !!! */
+ uint32_t sid;
+ uint32_t secs;
+ InquiryDelegate* delegate;
+ IOBluetoothDeviceInquiry* inquiry;
+ subscription_t* s;
+
+ /* NOTE: The behavior when running multiple inquery is
+ * random (buggy, undefined?) use one at the time as
+ * a work around
+ */
+ if (!ddata_get_uint32(&data_in, &sid))
+ goto badarg;
+ if (!ddata_get_uint32(&data_in, &secs))
+ goto badarg;
+
+ DEBUGF("CMD_INQUIRY_START cmdid=%d id=%u secs=%u",
+ cmdid, sid, secs);
+
+ if ((s = new_subscription(INQUIRY,sid,0,NULL,cleanup)) == NULL)
+ goto mem_error;
+
+ delegate = [[InquiryDelegate alloc] initWithSub:s];
+ inquiry = [[IOBluetoothDeviceInquiry alloc] initWithDelegate:delegate];
+
+ s->handle = inquiry;
+ inquiry.inquiryLength = secs;
+ inquiry.updateNewDeviceNames = FALSE;
+
+ bt_error=[inquiry start];
+ if (bt_error == kIOReturnSuccess) {
+ insert_last(&ctx->list, s);
+ goto ok;
+ }
+ else {
+ release_subscription(s);
+ [inquiry release];
+ [delegate release];
+ goto error;
+ }
+ break;
+ }
+
+ case CMD_INQUIRY_STOP: { /* arguments: id:32 */
+ uint32_t sid;
+ IOBluetoothDeviceInquiry* inquiry;
+ subscription_t* s;
+
+ if (!ddata_get_uint32(&data_in, &sid))
+ goto badarg;
+
+ DEBUGF("CMD_INQUIRY_STOP cmdid=%d, sid=%u", cmdid, sid);
+
+ if ((s = find_subscription(&ctx->list,INQUIRY,sid)) == NULL)
+ goto badarg;
+ inquiry = (IOBluetoothDeviceInquiry*)(s->handle);
+ [inquiry stop];
+ remove_subscription(&ctx->list,INQUIRY,sid);
+ goto ok;
+ }
+
+ case CMD_CONNECT: { /* arg = bt-address(6) [timeout:32 [auth:8]] */
+ BluetoothDeviceAddress bt_addr;
+ IOBluetoothDevice* device;
+ BluetoothHCIPageTimeout timeout = 0;
+ Boolean auth = FALSE;
+ Boolean opts = FALSE;
+ uint32_t milli;
+ ConnectDelegate* delegate;
+ subscription_t* s;
+
+ DEBUGF("CMD_CONNECT cmdid=%d", cmdid);
+
+ if(!get_address(&data_in, &bt_addr))
+ goto badarg;
+ if (ddata_get_uint32(&data_in, &milli)) {
+ opts = TRUE;
+ if (milli == 0)
+ timeout = 0;
+ else {
+ uint32_t t;
+ if (milli > 40960)
+ milli = 40960;
+ t = ((milli*1600) / 1000);
+ timeout = t;
+ }
+ if (!ddata_get_boolean(&data_in, &auth))
+ auth = FALSE;
+ if (ddata_r_avail(&data_in) != 0)
+ goto badarg;
+ }
+ if ((s = new_subscription(CONNECT,0,cmdid,NULL,cleanup)) == NULL)
+ goto mem_error;
+ if ((device = [IOBluetoothDevice deviceWithAddress:&bt_addr]) == NULL)
+ goto badarg;
+ delegate = [[ConnectDelegate alloc] initWithSub:s];
+ if (opts)
+ bt_error = [device openConnection:delegate
+ withPageTimeout:timeout
+ authenticationRequired: auth];
+ else
+ bt_error = [device openConnection:delegate];
+ if (bt_error != kIOReturnSuccess) {
+ [delegate release];
+ release_subscription(s);
+ goto error;
+ }
+ /* callback will do the rest */
+ break;
+ }
+
+ case CMD_DISCONNECT: { /* arg = bt-address(6) */
+ BluetoothDeviceAddress bt_addr;
+ IOBluetoothDevice* device;
+
+ DEBUGF("CMD_DISCONNECT cmdid=%d", cmdid);
+
+ if(!get_address(&data_in, &bt_addr))
+ goto badarg;
+ if (ddata_r_avail(&data_in) != 0)
+ goto badarg;
+ if ((device = [IOBluetoothDevice deviceWithAddress:&bt_addr]) == NULL)
+ goto badarg;
+ bt_error = [device closeConnection];
+ if (bt_error != kIOReturnSuccess)
+ goto error;
+ goto ok;
+ }
+
+ case CMD_REMOTE_NAME: { /* arg = bt-address(6) timeout:32 */
+ /* FIXME: check that no inquery is running !!! */
+ BluetoothDeviceAddress bt_addr;
+ IOBluetoothDevice* device;
+ Boolean opts = FALSE;
+ uint32_t milli;
+ BluetoothHCIPageTimeout timeout;
+ RemoteNameDelegate* delegate;
+ subscription_t* s;
+
+ DEBUGF("CMD_REMOTE_NAME cmdid=%d", cmdid);
+
+ if(!get_address(&data_in, &bt_addr))
+ goto badarg;
+
+ if (ddata_get_uint32(&data_in, &milli)) {
+ opts = TRUE;
+
+ if (milli == 0)
+ timeout = 0;
+ else {
+ uint32_t t;
+ if (milli > 40960)
+ milli = 40960;
+ t = ((milli*1600) / 1000);
+ timeout = t;
+ }
+ }
+
+ if (ddata_r_avail(&data_in) != 0)
+ goto badarg;
+ if ((s = new_subscription(REMOTE_NAME,0,cmdid,NULL,cleanup)) == NULL)
+ goto mem_error;
+ if ((device = [IOBluetoothDevice deviceWithAddress:&bt_addr]) == NULL)
+ goto badarg;
+ delegate = [[RemoteNameDelegate alloc] initWithSub:s];
+ if (opts && (timeout != 0))
+ bt_error = [device remoteNameRequest:delegate withPageTimeout:timeout];
+ else
+ bt_error = [device remoteNameRequest:delegate];
+ if (bt_error != kIOReturnSuccess) {
+ [delegate release];
+ release_subscription(s);
+ goto error;
+ }
+ /* callback will do the rest */
+ break;
+ }
+
+ case CMD_DEVICE_INFO: { /* arg = bt-address(6) info-items */
+ BluetoothDeviceAddress bt_addr;
+ IOBluetoothDevice* device;
+
+ DEBUGF("CMD_DEVICE_INFO cmdid=%d", cmdid);
+
+ if(!get_address(&data_in, &bt_addr))
+ goto badarg;
+ if ((device = [IOBluetoothDevice deviceWithAddress:&bt_addr]) == NULL)
+ goto badarg;
+ ddata_put_tag(&data_out, REPLY_OK);
+ ddata_put_UINT32(&data_out, cmdid);
+ bt_device_info(device, &data_in, &data_out);
+ goto reply;
+ }
+
+ case CMD_LOCAL_INFO: { /* arg = info-items */
+ DEBUGF("CMD_LOCAL_INFO cmdid=%d", cmdid);
+ ddata_put_tag(&data_out, REPLY_OK);
+ ddata_put_UINT32(&data_out, cmdid);
+ bt_local_info(&data_in, &data_out);
+ goto reply;
+ }
+
+ case CMD_SERVICE_INFO: { /* addr:48 [uuid:16|uuid:32|uuid:128] */
+ BluetoothDeviceAddress bt_addr;
+ IOBluetoothDevice* device;
+ IOBluetoothSDPUUID* uuid;
+ uint32_t n;
+
+ DEBUGF("CMD_SERVICE_INFO cmdid=%d", cmdid);
+
+ if(!get_address(&data_in, &bt_addr))
+ goto badarg;
+ if ((device = [IOBluetoothDevice deviceWithAddress:&bt_addr]) == NULL)
+ goto badarg;
+ n = ddata_r_avail(&data_in);
+ DEBUGF("SERVICE_INFO: avail=%d", n);
+ if (get_uuid(&data_in, &uuid)) {
+ ddata_put_tag(&data_out, REPLY_OK);
+ ddata_put_UINT32(&data_out, cmdid);
+ bt_sdp_info(device, uuid, &data_out);
+ goto reply;
+ }
+ goto badarg;
+ }
+
+ case CMD_SERVICE_QUERY: { /* addr:48 [uuid:16|uuid:32|uuid:128]*/
+ BluetoothDeviceAddress bt_addr;
+ IOBluetoothDevice* device;
+ IOBluetoothSDPUUID* uuid;
+ SDPQueryDelegate* delegate;
+ subscription_t* s;
+
+ DEBUGF("CMD_SERVICE_QUERY cmdid=%d", cmdid);
+
+ if(!get_address(&data_in, &bt_addr))
+ goto badarg;
+ if(!get_uuid(&data_in, &uuid))
+ goto badarg;
+ if ((device = [IOBluetoothDevice deviceWithAddress:&bt_addr]) == NULL)
+ goto badarg;
+ if (ddata_r_avail(&data_in) != 0)
+ goto badarg;
+
+ if ((s = new_subscription(SDP_QUERY,0,cmdid,NULL,cleanup)) == NULL)
+ goto mem_error;
+ delegate = [[SDPQueryDelegate alloc] initWithSub:s andUUID:uuid];
+ // FIXME: add uuid match
+ bt_error = [device performSDPQuery:delegate];
+ if (bt_error != kIOReturnSuccess) {
+ [delegate release];
+ release_subscription(s);
+ goto error;
+ }
+ goto done;
+ }
+
+ case CMD_SERVICE_DEL: { /* id:32 */
+ uint32_t sid;
+ subscription_link_t* link;
+
+ DEBUGF("CMD_SERVICE_DEL cmdid=%d", cmdid);
+
+ if (!ddata_get_uint32(&data_in, &sid))
+ goto badarg;
+ if (ddata_r_avail(&data_in) != 0)
+ goto badarg;
+ DEBUGF("SERVICE_CLOSE: id=%d", sid);
+ if ((link=find_subscription_link(&ctx->list,SDP,sid)) != NULL) {
+ unlink_subscription(link);
+ goto ok;
+ }
+ goto badarg;
+ }
+
+ case CMD_SERVICE_ADD: { /* id:32 sdp-service-data/binary */
+ uint32_t sid;
+ NSDictionary* serviceDict;
+ IOBluetoothSDPServiceRecord* serviceRecord;
+
+ DEBUGF("CMD_SERVICE_ADD cmdid=%d", cmdid);
+
+ if (!ddata_get_uint32(&data_in, &sid))
+ goto badarg;
+
+ if ((serviceDict = bt_make_sdp_dict(&data_in)) == NULL)
+ goto badarg;
+// #ifdef debug
+ /* DEBUG print the plist to file for debugging */
+ {
+ id plist = serviceDict; // Assume this property list exists.
+ NSData *xmlData;
+ NSError *error;
+ NSString *path = [NSString stringWithUTF8String:"Service.plist"];
+ xmlData = [NSPropertyListSerialization
+ dataWithPropertyList:plist
+ format:NSPropertyListXMLFormat_v1_0
+ options:0
+ error:&error];
+ if(xmlData) {
+ [xmlData writeToFile:path atomically:YES];
+ }
+ else {
+ NSLog(@"xmlData error %@", error);
+ [error release];
+ }
+ }
+// #endif
+ // fixme: add "LocalAttributes"
+ // "Persistent" boolean()/integer(),
+ // "TargetApplication"
+ serviceRecord = [IOBluetoothSDPServiceRecord
+ publishedServiceRecordWithDictionary:serviceDict];
+ if (serviceRecord == NULL) {
+ bt_error = kIOReturnNoMemory;
+ goto error;
+ }
+ else {
+ subscription_t* s;
+ BluetoothSDPServiceRecordHandle serviceRecordHandle;
+
+ if ((s = new_subscription(SDP,sid,cmdid,serviceRecord,cleanup))
+ == NULL)
+ goto mem_error;
+ if (insert_last(&ctx->list, s) < 0)
+ goto mem_error;
+ bt_error = [serviceRecord
+ getServiceRecordHandle:&serviceRecordHandle];
+ if (bt_error != kIOReturnSuccess)
+ goto error;
+ ddata_put_tag(&data_out, REPLY_OK);
+ ddata_put_UINT32(&data_out, cmdid);
+ ddata_put_uint32(&data_out, serviceRecordHandle);
+ goto reply;
+ }
+ }
+
+ case CMD_SERVICE_RFCOMM: { /* id:32 */
+ uint32_t sid;
+ subscription_t* s;
+
+ DEBUGF("CMD_SERVICE_RFCOMM cmdid=%d", cmdid);
+
+ if (!ddata_get_uint32(&data_in, &sid))
+ goto badarg;
+ if (ddata_r_avail(&data_in) != 0)
+ goto badarg;
+ if ((s = find_subscription(&ctx->list,SDP,sid)) != NULL) {
+ BluetoothRFCOMMChannelID channelID;
+ IOBluetoothSDPServiceRecord* serviceRecord =
+ (IOBluetoothSDPServiceRecord*) s->handle;
+ bt_error = [serviceRecord getRFCOMMChannelID:&channelID];
+ if (bt_error == kIOReturnSuccess) {
+ ddata_put_tag(&data_out, REPLY_OK);
+ ddata_put_UINT32(&data_out, cmdid);
+ ddata_put_uint8(&data_out, channelID);
+ goto reply;
+ }
+ goto error;
+ }
+ goto badarg;
+ }
+
+ case CMD_RFCOMM_OPEN: { /* id:32 bt-address(6) channel-id:8 */
+ uint32_t sid;
+ BluetoothDeviceAddress bt_addr;
+ IOBluetoothDevice* device;
+ IOBluetoothRFCOMMChannel* channel;
+ BluetoothRFCOMMChannelID channel_id;
+ RFCOMMChannelDelegate* delegate;
+ subscription_t* s;
+
+ DEBUGF("CMD_RFCOMM_OPEN cmdid=%d", cmdid);
+
+ if (!ddata_get_uint32(&data_in, &sid))
+ goto badarg;
+ if(!get_address(&data_in, &bt_addr))
+ goto badarg;
+ if(!ddata_get_uint8(&data_in, &channel_id))
+ goto badarg;
+ if (!RFCOMM_CHANNEL_ID_IS_VALID(channel_id))
+ goto badarg;
+ if (ddata_r_avail(&data_in) != 0)
+ goto badarg;
+ if ((device = [IOBluetoothDevice deviceWithAddress:&bt_addr]) == NULL)
+ goto badarg;
+
+ if ((s = new_subscription(RFCOMM,sid,cmdid,NULL,cleanup)) == NULL)
+ goto mem_error;
+ delegate = [[RFCOMMChannelDelegate alloc] initWithSub:s andCtx:ctx];
+ bt_error = [device openRFCOMMChannelAsync:&channel
+ withChannelID:channel_id
+ delegate:delegate];
+ if (bt_error == kIOReturnSuccess) {
+ s->handle = channel;
+ insert_last(&ctx->list, s);
+ goto done;
+ }
+ else {
+ [delegate release];
+ release_subscription(s);
+ goto error;
+ }
+ break;
+ }
+
+ case CMD_RFCOMM_CLOSE: { /* arguments: id:32 */
+ uint32_t sid;
+ subscription_link_t* link;
+
+ DEBUGF("CMD_RFCOMM_CLOSE cmdid=%d", cmdid);
+
+ if (!ddata_get_uint32(&data_in, &sid))
+ goto badarg;
+ if (ddata_r_avail(&data_in) != 0)
+ goto badarg;
+
+ if ((link = find_subscription_link(&ctx->list,RFCOMM,sid)) != NULL) {
+ IOBluetoothRFCOMMChannel* rfcommChannel;
+ subscription_t* s = link->s;
+ s->cmdid = cmdid;
+ rfcommChannel = (IOBluetoothRFCOMMChannel*) (s->handle);
+ if (rfcommChannel != NULL) {
+ DEBUGF("RFCOMM_CLOSE: channel=%p", rfcommChannel);
+ bt_error = [rfcommChannel closeChannel];
+ if (bt_error == kIOReturnSuccess)
+ goto done;
+ }
+ else if (s->accept != NULL) {
+ listen_queue_t* lq = (listen_queue_t*)((s->accept)->opaque);
+ remove_subscription(&lq->wait,RFCOMM,sid);
+ unlink_subscription(link);
+ goto ok;
+ }
+ }
+ else if ((link = find_subscription_link(&ctx->list,RFCOMM_LISTEN,sid)) != NULL) {
+ subscription_t* listen = link->s;
+ listen_queue_t* lq = (listen_queue_t*)listen->opaque;
+ subscription_link_t* link1;
+
+ /* remove all waiters */
+ while((link1=lq->wait.first) != NULL) {
+ bt_event(link1->s->id, "closed");
+ unlink_subscription(link1);
+ }
+ unlink_subscription(link);
+ goto ok;
+ }
+ goto error;
+ }
+
+ case CMD_RFCOMM_LISTEN: { /* id:32, channel:8 */
+ uint32_t sid;
+ BluetoothRFCOMMChannelID channel_id;
+ IOBluetoothUserNotificationRef ref;
+ subscription_t* listen;
+ listen_queue_t* lq;
+
+ DEBUGF("CMD_RFCOMM_LISTEN cmdid=%d", cmdid);
+
+ if (!ddata_get_uint32(&data_in, &sid))
+ goto badarg;
+ if(!ddata_get_uint8(&data_in, &channel_id))
+ goto badarg;
+ if ((channel_id != 0) && !RFCOMM_CHANNEL_ID_IS_VALID(channel_id))
+ goto badarg;
+ if (ddata_r_avail(&data_in) != 0)
+ goto badarg;
+
+ if ((listen = new_subscription(RFCOMM_LISTEN,sid,cmdid,NULL,cleanup))
+ == NULL)
+ goto mem_error;
+ if ((lq = alloc_type(listen_queue_t)) == NULL) {
+ release_subscription(listen);
+ goto mem_error;
+ }
+ lq->ctx = ctx;
+ listen->opaque = (void*) lq;
+
+ if (channel_id == 0)
+ ref = IOBluetoothRegisterForRFCOMMChannelOpenNotifications(cb_rfcomm_open, (void*)listen);
+ else
+ ref = IOBluetoothRegisterForFilteredRFCOMMChannelOpenNotifications(
+ cb_rfcomm_open, (void*) listen, channel_id,
+ kIOBluetoothUserNotificationChannelDirectionIncoming);
+ if (ref == NULL) {
+ release_subscription(listen);
+ goto mem_error;
+ }
+ listen->handle = ref;
+ insert_last(&ctx->list, listen);
+ goto ok;
+ }
+
+ case CMD_RFCOMM_ACCEPT: { /* id:32 listen_id:32 */
+ uint32_t sid;
+ uint32_t listen_id;
+ listen_queue_t* lq;
+ subscription_t* listen;
+ subscription_t* s;
+
+ DEBUGF("CMD_RFCOMM_ACCEPT cmdid=%d", cmdid);
+
+ if (!ddata_get_uint32(&data_in, &sid))
+ goto badarg;
+ if (!ddata_get_uint32(&data_in, &listen_id))
+ goto badarg;
+ if (ddata_r_avail(&data_in) != 0)
+ goto badarg;
+
+ if (find_subscription(&ctx->list,RFCOMM,sid) != NULL) {
+ DEBUGF("subscription %d already exists", sid);
+ goto badarg;
+ }
+ if ((listen = find_subscription(&ctx->list,
+ RFCOMM_LISTEN,listen_id))==NULL) {
+ DEBUGF("listen subscription %d does not exists", listen_id);
+ goto badarg;
+ }
+ if ((s = new_subscription(RFCOMM,sid,cmdid,NULL,cleanup)) == NULL)
+ goto mem_error;
+ s->accept = listen; // mark that we are accepting
+ lq = (listen_queue_t*) listen->opaque;
+ insert_last(&lq->wait, s);
+ insert_last(&ctx->list, s);
+
+ ddata_put_tag(&data_out, REPLY_OK);
+ ddata_put_UINT32(&data_out, cmdid);
+ ddata_send(&data_out, 1);
+ rfcomm_accept(listen);
+ goto done;
+ }
+
+ case CMD_RFCOMM_SEND: { /* id:32, data/rest */
+ uint32_t sid;
+ subscription_t* s;
+ IOBluetoothRFCOMMChannel* rfcommChannel;
+ uint32_t len;
+ ddata_t* out;
+ uint8_t* ptr;
+ BluetoothRFCOMMMTU mtu;
+
+ DEBUGF("CMD_RFCOMM_SEND cmdid=%d", cmdid);
+
+ if (!ddata_get_uint32(&data_in, &sid))
+ goto badarg;
+ if ((s = find_subscription(&ctx->list,RFCOMM,sid)) == NULL)
+ goto badarg;
+ rfcommChannel = (IOBluetoothRFCOMMChannel*)(s->handle);
+ if (rfcommChannel == NULL)
+ goto badarg;
+ s->cmdid = cmdid;
+ /* we may have to retain the data while sending !!!
+ * create a Data and pace the sending MTU wise...
+ */
+ out = ddata_new(data_in.rd, ddata_r_avail(&data_in));
+ mtu = [rfcommChannel getMTU];
+ if ((len = ddata_r_avail(out)) > mtu)
+ len = mtu;
+ ptr = out->wr;
+ ddata_forward(out, len); /* move to next block */
+ bt_error = [rfcommChannel writeAsync:ptr length:len refcon:out];
+ if (bt_error != kIOReturnSuccess)
+ goto error;
+ /* event call back will do the rest ? */
+ goto done;
+ }
+
+ case CMD_RFCOMM_MTU: { /* id:32 */
+ uint32_t sid;
+ subscription_t* s;
+ IOBluetoothRFCOMMChannel* rfcommChannel;
+ BluetoothRFCOMMMTU mtu;
+
+ DEBUGF("CMD_RFCOMM_MTU cmdid=%d", cmdid);
+
+ if (!ddata_get_uint32(&data_in, &sid))
+ goto badarg;
+ if ((s = find_subscription(&ctx->list,RFCOMM,sid)) == NULL)
+ goto badarg;
+ rfcommChannel = (IOBluetoothRFCOMMChannel*) (s->handle);
+ if (rfcommChannel == NULL)
+ goto badarg;
+ mtu = [rfcommChannel getMTU];
+ ddata_put_tag(&data_out, REPLY_OK);
+ ddata_put_UINT32(&data_out, cmdid);
+ ddata_put_uint16(&data_out, mtu);
+ goto reply;
+ }
+
+ case CMD_RFCOMM_ADDRESS: { /* id:32 */
+ uint32_t sid;
+ subscription_t* s;
+ IOBluetoothRFCOMMChannel* rfcommChannel;
+ const BluetoothDeviceAddress* addr;
+ IOBluetoothDevice* device;
+
+ DEBUGF("CMD_RFCOMM_ADDRESS cmdid=%d", cmdid);
+
+ if (!ddata_get_uint32(&data_in, &sid))
+ goto badarg;
+ if ((s = find_subscription(&ctx->list,RFCOMM,sid)) == NULL)
+ goto badarg;
+ rfcommChannel = (IOBluetoothRFCOMMChannel*)(s->handle);
+ if (rfcommChannel == NULL)
+ goto badarg;
+ if ((device = [rfcommChannel getDevice]) == NULL)
+ goto badarg;
+ if ((addr = [device getAddress]) == NULL)
+ goto badarg;
+ ddata_put_tag(&data_out, REPLY_OK);
+ ddata_put_UINT32(&data_out, cmdid);
+ ddata_put_addr(&data_out, addr);
+ goto reply;
+ }
+
+ case CMD_RFCOMM_CHANNEL: { /* id:32 */
+ uint32_t sid;
+ subscription_t* s;
+ IOBluetoothRFCOMMChannel* rfcommChannel;
+ BluetoothRFCOMMChannelID channel_id;
+
+ DEBUGF("CMD_RFCOMM_CHANNEL cmdid=%d", cmdid);
+
+ if (!ddata_get_uint32(&data_in, &sid))
+ goto badarg;
+ if ((s = find_subscription(&ctx->list,RFCOMM,sid)) == NULL)
+ goto badarg;
+ rfcommChannel = (IOBluetoothRFCOMMChannel*)(s->handle);
+ channel_id = [rfcommChannel getChannelID];
+ ddata_put_tag(&data_out, REPLY_OK);
+ ddata_put_UINT32(&data_out, cmdid);
+ ddata_put_uint8(&data_out, channel_id);
+ goto reply;
+ }
+
+ case CMD_L2CAP_OPEN: { /* id:32 bt-address(6) psm:16 */
+ uint32_t sid;
+ BluetoothDeviceAddress bt_addr;
+ IOBluetoothDevice* device;
+ IOBluetoothL2CAPChannel* channel;
+ BluetoothL2CAPPSM psm;
+ subscription_t* s;
+ L2CAPChannelDelegate* delegate;
+
+ DEBUGF("CMD_L2CAP_OPEN cmdid=%d", cmdid);
+
+ if (!ddata_get_uint32(&data_in, &sid))
+ goto badarg;
+ if(!get_address(&data_in, &bt_addr))
+ goto badarg;
+ if(!ddata_get_uint16(&data_in, &psm))
+ goto badarg;
+ if (ddata_r_avail(&data_in) != 0)
+ goto badarg;
+
+ if ((device = [IOBluetoothDevice deviceWithAddress:&bt_addr]) == NULL)
+ goto badarg;
+
+ DEBUGF("L2CAP_OPEN; %d psm=%d", sid, psm);
+
+ if ((s = new_subscription(L2CAP,sid,cmdid,NULL,cleanup)) == NULL)
+ goto mem_error;
+ delegate = [[L2CAPChannelDelegate alloc] initWithSub:s andCtx:ctx];
+ bt_error = [device openL2CAPChannelAsync:&channel
+ withPSM:psm
+ delegate:delegate];
+ if (bt_error == kIOReturnSuccess) {
+ s->handle = channel;
+ insert_last(&ctx->list, s);
+ goto done;
+ }
+ else {
+ [delegate release];
+ release_subscription(s);
+ goto error;
+ }
+ break;
+ }
+
+ case CMD_L2CAP_CLOSE: { /* arguments: id:32 */
+ uint32_t sid;
+ subscription_link_t* link;
+
+ DEBUGF("CMD_L2CAP_CLOSE cmdid=%d", cmdid);
+
+ if (!ddata_get_uint32(&data_in, &sid))
+ goto badarg;
+ if (ddata_r_avail(&data_in) != 0)
+ goto badarg;
+ DEBUGF("L2CAP_CLOSE: id=%d", sid);
+ if ((link = find_subscription_link(&ctx->list,L2CAP,sid)) != NULL) {
+ IOBluetoothL2CAPChannel* l2capChannel;
+ subscription_t* s = link->s;
+ s->cmdid = cmdid;
+ l2capChannel = (IOBluetoothL2CAPChannel*)(s->handle);
+ if (l2capChannel != NULL) {
+ DEBUGF("L2CAP_CLOSE: channel=%p", l2capChannel);
+ bt_error = [l2capChannel closeChannel];
+ if (bt_error == kIOReturnSuccess)
+ goto done;
+
+ }
+ else if (s->accept != NULL) {
+ listen_queue_t* lq = (listen_queue_t*)(s->accept)->opaque;
+ remove_subscription(&lq->wait,L2CAP,sid);
+ unlink_subscription(link);
+ goto ok;
+ }
+ }
+ else if ((link = find_subscription_link(&ctx->list,L2CAP_LISTEN,sid))
+ != NULL) {
+ subscription_t* listen = link->s;
+ listen_queue_t* lq = (listen_queue_t*)listen->opaque;
+ subscription_link_t* link1;
+
+ // move this code to free subscription ?
+ /* remove all waiters */
+ while((link1=lq->wait.first) != NULL) {
+ uint8_t buf[64];
+ ddata_t data;
+
+ ddata_init(&data, buf, sizeof(buf), 0);
+ ddata_put_UINT32(&data, 0);
+ ddata_put_tag(&data, REPLY_EVENT);
+ ddata_put_UINT32(&data, link1->s->id);
+ ddata_put_atom(&data, "closed");
+ ddata_send(&data, 1);
+ ddata_final(&data);
+ unlink_subscription(link1);
+ }
+ unlink_subscription(link);
+ goto ok;
+ }
+ goto error;
+ }
+
+ case CMD_L2CAP_LISTEN: { /* id:32, psm:16 */
+ uint32_t sid;
+ BluetoothL2CAPPSM psm;
+ IOBluetoothUserNotificationRef ref;
+ subscription_t* listen;
+ listen_queue_t* lq;
+
+ DEBUGF("CMD_L2CAP_LISTEN cmdid=%d", cmdid);
+
+ if (!ddata_get_uint32(&data_in, &sid))
+ goto badarg;
+ if(!ddata_get_uint16(&data_in, &psm))
+ goto badarg;
+ if (ddata_r_avail(&data_in) != 0)
+ goto badarg;
+
+ if ((listen = new_subscription(L2CAP_LISTEN,sid,cmdid,NULL,cleanup))
+ == NULL)
+ goto mem_error;
+ if ((lq = alloc_type(listen_queue_t)) == NULL) {
+ release_subscription(listen);
+ goto mem_error;
+ }
+ lq->ctx = ctx;
+ listen->opaque = (void*) lq;
+ if (psm == 0)
+ ref = IOBluetoothRegisterForL2CAPChannelOpenNotifications(cb_l2cap_open, (void*) listen);
+ else
+ ref = IOBluetoothRegisterForFilteredL2CAPChannelOpenNotifications(
+ cb_l2cap_open, (void*) listen, psm,
+ kIOBluetoothUserNotificationChannelDirectionIncoming);
+ if (ref == NULL) {
+ release_subscription(listen);
+ goto mem_error;
+ }
+ listen->handle = ref;
+ insert_last(&ctx->list, listen);
+ goto ok;
+ }
+
+ case CMD_L2CAP_SEND: { /* id:32, data/rest */
+ uint32_t sid;
+ subscription_t* s;
+ IOBluetoothL2CAPChannel* l2capChannel;
+ uint32_t len;
+ ddata_t* out;
+ uint8_t* ptr;
+ BluetoothL2CAPMTU mtu;
+
+ DEBUGF("CMD_L2CAP_SEND cmdid=%d", cmdid);
+
+ if (!ddata_get_uint32(&data_in, &sid))
+ goto badarg;
+ if ((s = find_subscription(&ctx->list,L2CAP,sid)) == NULL)
+ goto badarg;
+ l2capChannel = (IOBluetoothL2CAPChannel*)(s->handle);
+ if (l2capChannel == NULL)
+ goto badarg;
+ s->cmdid = cmdid;
+ /* we may have to retain the data while sending !!!
+ * create a Data and pace the sending MTU wise...
+ */
+ out = ddata_new(data_in.rd, ddata_r_avail(&data_in));
+ mtu = [l2capChannel outgoingMTU];
+ if ((len = ddata_r_avail(out)) > mtu)
+ len = mtu;
+ ptr = out->wr;
+ ddata_forward(out, len); /* move to next block */
+ bt_error = [l2capChannel writeAsync:ptr length:len refcon:out];
+ if (bt_error != kIOReturnSuccess)
+ goto error;
+ /* event call back will do the rest ? */
+ goto done;
+ }
+
+ case CMD_L2CAP_ACCEPT: { /* id:32 listen_id:32 */
+ uint32_t sid;
+ uint32_t listen_id;
+ listen_queue_t* lq;
+ subscription_t* listen;
+ subscription_t* s;
+
+ DEBUGF("CMD_L2CAP_ACCEPT cmdid=%d", cmdid);
+
+ if (!ddata_get_uint32(&data_in, &sid))
+ goto badarg;
+ if (!ddata_get_uint32(&data_in, &listen_id))
+ goto badarg;
+ if (ddata_r_avail(&data_in) != 0)
+ goto badarg;
+
+ if (find_subscription(&ctx->list,L2CAP,sid) != NULL) {
+ DEBUGF("subscription %d already exists", sid);
+ goto badarg;
+ }
+ if ((listen = find_subscription(&ctx->list,L2CAP_LISTEN,listen_id))
+ ==NULL) {
+ DEBUGF("listen subscription %d does not exists", listen_id);
+ goto badarg;
+ }
+ if ((s = new_subscription(L2CAP,sid,cmdid,NULL,cleanup)) == NULL)
+ goto mem_error;
+ s->accept = listen; // mark that we are accepting
+ lq = (listen_queue_t*) listen->opaque;
+ insert_last(&lq->wait, s);
+ insert_last(&ctx->list, s);
+
+ ddata_put_tag(&data_out, REPLY_OK);
+ ddata_put_UINT32(&data_out, cmdid);
+ ddata_send(&data_out, 1);
+ l2cap_accept(listen);
+ goto done;
+ }
+
+ case CMD_L2CAP_MTU: {/* id:32 */
+ uint32_t sid;
+ subscription_t* s;
+ IOBluetoothL2CAPChannel* l2capChannel;
+ BluetoothL2CAPMTU mtu;
+
+ DEBUGF("CMD_L2CAP_MTU cmdid=%d", cmdid);
+
+ if (!ddata_get_uint32(&data_in, &sid))
+ goto badarg;
+ if ((s = find_subscription(&ctx->list,L2CAP,sid)) == NULL)
+ goto badarg;
+ l2capChannel = (IOBluetoothL2CAPChannel*)(s->handle);
+ if (l2capChannel == NULL)
+ goto badarg;
+ mtu = [l2capChannel outgoingMTU];
+ ddata_put_tag(&data_out, REPLY_OK);
+ ddata_put_UINT32(&data_out, cmdid);
+ ddata_put_uint16(&data_out, mtu);
+ goto reply;
+ }
+
+ case CMD_L2CAP_ADDRESS: { /* id:32 */
+ uint32_t sid;
+ subscription_t* s;
+ IOBluetoothL2CAPChannel* l2capChannel;
+ const BluetoothDeviceAddress* addr;
+ IOBluetoothDevice* device;
+
+ DEBUGF("CMD_L2CAP_ADDRESS cmdid=%d", cmdid);
+
+ if (!ddata_get_uint32(&data_in, &sid))
+ goto badarg;
+ if ((s = find_subscription(&ctx->list,L2CAP,sid)) == NULL)
+ goto badarg;
+ l2capChannel = (IOBluetoothL2CAPChannel*) (s->handle);
+ if (l2capChannel == NULL)
+ goto badarg;
+ if ((device = [l2capChannel device]) == NULL)
+ goto badarg;
+ if ((addr = [device getAddress]) == NULL)
+ goto badarg;
+ ddata_put_tag(&data_out, REPLY_OK);
+ ddata_put_UINT32(&data_out, cmdid);
+ ddata_put_addr(&data_out, addr);
+ goto reply;
+ }
+
+ case CMD_L2CAP_PSM: {/* id:32 */
+ uint32_t sid;
+ subscription_t* s;
+ IOBluetoothL2CAPChannel* l2capChannel;
+ BluetoothL2CAPPSM psm;
+
+ DEBUGF("CMD_L2CAP_PSM cmdid=%d", cmdid);
+
+ if (!ddata_get_uint32(&data_in, &sid))
+ goto badarg;
+ if ((s = find_subscription(&ctx->list,L2CAP,sid)) == NULL)
+ goto badarg;
+ l2capChannel = (IOBluetoothL2CAPChannel*) (s->handle);
+ psm = [l2capChannel PSM];
+ ddata_put_tag(&data_out, REPLY_OK);
+ ddata_put_UINT32(&data_out, cmdid);
+ ddata_put_uint16(&data_out, psm);
+ goto reply;
+ }
+
+ default:
+ DEBUGF("CMD_UNKNOWN = %d cmdid=%d", op, cmdid);
+ goto badarg;
+ }
+
+ goto done;
+
+ok:
+ if (cmdid == 0)
+ goto done;
+ ddata_put_tag(&data_out, REPLY_OK);
+ ddata_put_UINT32(&data_out, cmdid);
+ goto reply;
+
+mem_error:
+ if (cmdid == 0)
+ goto done;
+ bt_error = kIOReturnNoMemory;
+ goto error;
+
+badarg:
+ if (cmdid == 0)
+ goto done;
+ bt_error = kIOReturnBadArgument;
+
+error:
+/* reset, just in case something was inserted */
+ ddata_reset(&data_out);
+ ddata_put_UINT32(&data_out, 0);
+ ddata_put_tag(&data_out, REPLY_ERROR);
+ ddata_put_UINT32(&data_out, cmdid);
+ ddata_put_io_error(&data_out, bt_error, ERR_SHORT);
+reply:
+ ddata_send(&data_out, 1);
+done:
+ ddata_final(&data_out);
+}
+
+typedef CFSocketNativeHandle PipeNativeHandle;
+typedef CFSocketCallBack PipeCallBack;
+typedef CFSocketCallBackType PipeCallBackType;
+typedef CFSocketContext PipeContext;
+typedef CFSocketRef PipeRef;
+
+#define kPipeNoCallBack kCGSocketNoCallBack
+#define kPipeReadCallBack kCFSocketReadCallBack
+#define kPipeDataCallBack kCFSocketDataCallBack
+#define kPipeWriteCallBack kCFSocketWriteCallBack
+
+PipeRef PipeCreateWithNative(CFAllocatorRef allocator,
+ PipeNativeHandle pipe,
+ CFOptionFlags callBackTypes,
+ PipeCallBack callback, const PipeContext *context)
+{
+ return CFSocketCreateWithNative(allocator, pipe,
+ callBackTypes,
+ callback,
+ context);
+}
+
+CFRunLoopSourceRef PipeCreateRunLoopSource(CFAllocatorRef allocator,
+ PipeRef pipe, CFIndex order)
+{
+ return CFSocketCreateRunLoopSource(allocator, pipe, order);
+}
+
+
+/*
+ * Handle input commands from Erlang (or other program)
+ */
+
+void pipe_callback(PipeRef pipe, PipeCallBackType type, CFDataRef address,
+ const void *data, void *info)
+{
+ bt_ctx_t* ctx = (bt_ctx_t*) info;
+ (void) address;
+ (void) data;
+
+ // DEBUGF("PIPE: callback type=%d", type);
+ if (type == kPipeReadCallBack) {
+ int fd = CFSocketGetNative(pipe);
+ int n;
+
+ if (ctx->pbuf_len < sizeof(ctx->pbuf)) {
+ int r = sizeof(ctx->pbuf) - ctx->pbuf_len;
+ if ((n = read(fd, ctx->pbuf+ctx->pbuf_len, r)) < 0)
+ goto error;
+ if (n == 0)
+ goto closed;
+ ctx->pbuf_len += n;
+ // DEBUGF("READ: %d pbuf_len=%d", n, ctx->pbuf_len);
+ if (ctx->pbuf_len == sizeof(ctx->pbuf)) {
+ ctx->len = (ctx->pbuf[0]<<24) + (ctx->pbuf[1]<<16) +
+ (ctx->pbuf[2]<<8) + ctx->pbuf[3];
+ // DEBUGF("READ: %d packet len=%d", n, ctx->len);
+ if (ctx->len > 0) {
+ ctx->remain = ctx->len;
+ ctx->packet = (uint8_t*) malloc(ctx->len);
+ ctx->ptr = ctx->packet;
+ }
+ else {
+ ctx->remain = 0;
+ ctx->pbuf_len = 0;
+ }
+ }
+ }
+ else {
+ if ((n = read(fd, (void*)ctx->ptr, ctx->remain)) < 0)
+ goto error;
+ if (n == 0)
+ goto closed;
+ // DEBUGF("READ: %d packet bytes", n);
+ ctx->remain -= n;
+ // DEBUGF("PACKET: remain=%d", ctx->remain);
+ ctx->ptr += n;
+ if (ctx->remain == 0) {
+ bt_command(ctx, ctx->packet, ctx->len);
+ free(ctx->packet);
+ ctx->packet = NULL;
+ ctx->len = 0;
+ ctx->pbuf_len = 0;
+ }
+ }
+ }
+ // DEBUGF("PIPE: callback done",0);
+ return;
+
+error:
+ DEBUGF("pipe read error",0);
+ exit(1);
+
+closed:
+ DEBUGF("eof clean-up",0);
+ CFRunLoopStop(CFRunLoopGetCurrent());
+}
+
+
+
+int main(int argc, char** argv)
+{
+ PipeRef pipe_in;
+ PipeRef pipe_out;
+ PipeNativeHandle in_fd = 0;
+ PipeNativeHandle out_fd = 1;
+ CFRunLoopSourceRef pipe_ref;
+ NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init];
+ (void) argc;
+ (void) argv;
+// The global context (fixme - remove this)
+ bt_ctx_t bt_info;
+ PipeContext in_ctx;
+ PipeContext out_ctx;
+
+ dlog_init();
+
+ memset(&bt_info, 0, sizeof(bt_ctx_t));
+ memset(&in_ctx, 0, sizeof(PipeContext));
+ memset(&out_ctx, 0, sizeof(PipeContext));
+
+ in_ctx.info = &bt_info;
+ out_ctx.info = &bt_info;
+
+ // kCFSocketWriteCallBack
+
+ pipe_in = PipeCreateWithNative(0, in_fd,
+ kCFSocketReadCallBack,
+ pipe_callback,
+ &in_ctx);
+
+ pipe_out = PipeCreateWithNative(0, out_fd,
+ kCFSocketWriteCallBack,
+ pipe_callback,
+ &out_ctx);
+
+ pipe_ref = PipeCreateRunLoopSource(0, pipe_in, 0);
+
+ CFRunLoopAddSource(CFRunLoopGetCurrent(), pipe_ref, kCFRunLoopDefaultMode);
+
+ CFRunLoopRun();
+
+ [pool release];
+
+ dlog_finish();
+ DEBUGF("terminate",0);
+ exit(0);
+}
diff --git a/deps/bt/c_src/bt_poll.c b/deps/bt/c_src/bt_poll.c
new file mode 100644
index 0000000..306af01
--- /dev/null
+++ b/deps/bt/c_src/bt_poll.c
@@ -0,0 +1,142 @@
+//
+// Utility to handle multiple poll and callbacks
+//
+
+#include <stdio.h>
+#include <stdlib.h>
+
+#include "dthread/include/dlog.h"
+#include "bt_poll.h"
+
+static int poll_sz = 0; // allocated size
+static int poll_n = 0; // used len
+static int poll_fdmapsz = 0; // size of fdmap
+static struct pollfd* poll_fds = NULL;
+static event_cb_t* poll_cb = NULL;
+static void** poll_data = NULL;
+static int* poll_fdmap = NULL; // fd -> poll_fds index
+
+static inline int fd_to_pollix(int fd)
+{
+ if ((fd < 0) || (fd >= poll_fdmapsz))
+ return -1;
+ return poll_fdmap[fd];
+}
+
+// change pollfd data
+int bt_poll_set_events(int fd, short events)
+{
+ int i;
+ if ((i = fd_to_pollix(fd)) < 0) return -1;
+ poll_fds[i].events = events;
+ return 0;
+}
+
+int bt_poll_set_cb(int fd, event_cb_t cb)
+{
+ int i;
+ if ((i = fd_to_pollix(fd)) < 0) return -1;
+ poll_cb[i] = cb;
+ return 0;
+}
+
+int bt_poll_set_data(int fd, void* data)
+{
+ int i;
+ if ((i = fd_to_pollix(fd)) < 0) return -1;
+ poll_data[i] = data;
+ return 0;
+}
+
+int bt_poll_add(int fd, short events,
+ void (*cb)(struct pollfd* pfd,void* data),
+ void* data)
+{
+ int i = poll_n;
+ if (i == poll_sz) {
+ size_t new_sz = 2*poll_sz+1;
+ poll_fds = realloc(poll_fds, new_sz*sizeof(struct pollfd));
+ if (poll_fds == NULL) return -1;
+ poll_cb = realloc(poll_cb, new_sz*sizeof(event_cb_t));
+ if (poll_cb == NULL) return -1;
+ poll_data = realloc(poll_data, new_sz*sizeof(void*));
+ if (poll_data == NULL) return -1;
+ poll_sz = new_sz;
+ }
+ if (fd >= poll_fdmapsz) {
+ int new_sz = fd+64; // add some extra
+ int j;
+ poll_fdmap = realloc(poll_fdmap, new_sz*sizeof(int));
+ if (poll_fdmap == NULL) return -1;
+ for (j = poll_fdmapsz; j < new_sz; j++)
+ poll_fdmap[j] = -1;
+ poll_fdmapsz = new_sz;
+ }
+ poll_fdmap[fd] = i;
+ poll_cb[i] = cb;
+ poll_data[i] = data;
+ poll_fds[i].fd = fd;
+ poll_fds[i].events = events;
+ poll_n++;
+ return 0;
+}
+
+static void bt_poll_swap(int i, int j)
+{
+ if (i != j) {
+ int fdi = poll_fds[i].fd;
+ int fdj = poll_fds[j].fd;
+ struct pollfd fds = poll_fds[i];
+ event_cb_t cb = poll_cb[i];
+ void* data = poll_data[i];
+
+ poll_fds[i] = poll_fds[j];
+ poll_cb[i] = poll_cb[j];
+ poll_data[i] = poll_data[j];
+
+ poll_fds[j] = fds;
+ poll_cb[j] = cb;
+ poll_data[j] = data;
+ // remap indices
+ poll_fdmap[fdi] = j;
+ poll_fdmap[fdj] = i;
+ }
+}
+
+// find and remove the pollfd
+int bt_poll_del(int fd)
+{
+ int i;
+
+ if ((i = fd_to_pollix(fd)) < 0) return -1;
+ bt_poll_swap(i, poll_n-1);
+ poll_n--;
+ poll_fdmap[fd] = -1; // not mapped any more
+ return 0;
+}
+
+int bt_poll(int timeout)
+{
+ int r,r0;
+
+ r = r0 = poll(poll_fds, poll_n, timeout);
+ if (r > 0) {
+ int i = 0;
+ while((r > 0) && (i < (int)poll_n)) {
+ if ((poll_fds[i].revents & poll_fds[i].events) != 0) {
+ int j = poll_n-1;
+ // swap i and the last item, this makes the poll set less
+ // sensitive to starvation, the bt_poll_del may also be
+ // called in the callback without wory, note that
+ // you can not del any fd in a callback and expect to
+ // get a poll on all fds in the same loop (yet)
+ bt_poll_swap(i, j);
+ (*poll_cb[j])(&poll_fds[j], poll_data[j]);
+ r--;
+ }
+ else
+ i++;
+ }
+ }
+ return r0;
+}
diff --git a/deps/bt/c_src/bt_poll.h b/deps/bt/c_src/bt_poll.h
new file mode 100644
index 0000000..bd92479
--- /dev/null
+++ b/deps/bt/c_src/bt_poll.h
@@ -0,0 +1,17 @@
+#ifndef __BT_POLL_H__
+#define __BT_POLL_H__
+
+#include <poll.h>
+
+typedef void (*event_cb_t)(struct pollfd*, void*);
+
+extern int bt_poll_add(int fd, short events, event_cb_t cb, void* data);
+extern int bt_poll_del(int fd);
+
+extern int bt_poll_set_events(int fd, short events);
+extern int bt_poll_set_cb(int fd, event_cb_t cb);
+extern int bt_poll_set_data(int fd, void* data);
+
+extern int bt_poll(int timeout);
+
+#endif
diff --git a/deps/bt/c_src/bt_sub.c b/deps/bt/c_src/bt_sub.c
new file mode 100644
index 0000000..2da8e02
--- /dev/null
+++ b/deps/bt/c_src/bt_sub.c
@@ -0,0 +1,236 @@
+//
+// Subscription handling
+//
+
+#include <stdio.h>
+#include <string.h>
+
+#include "dthread/include/dlog.h"
+#include "bt_sub.h"
+
+#define alloc_type(type) calloc(1, sizeof(type))
+
+static char* format_subscription_type(subscription_type_t type)
+{
+ switch(type) {
+ case INQUIRY: return "INQUIRY";
+ case REMOTE_NAME: return "REMOTE_NAME";
+ case CONNECT: return "CONNECT";
+ case SDP_QUERY: return "SPD_QUERY";
+ case SDP: return "SDP";
+ case RFCOMM: return "RFCOMM";
+ case RFCOMM_LISTEN: return "RFCOMM_LISTEN";
+ case L2CAP: return "LPCAP";
+ case L2CAP_LISTEN: return "LPCAP_LISTEN";
+ default: return "????";
+ }
+}
+
+char* format_subscription(subscription_t* s)
+{
+ static char format_buf[128];
+
+ snprintf(format_buf, sizeof(format_buf),
+ "%s id=%u,handle=%p ref=%u",
+ format_subscription_type(s->type),
+ s->id, s->handle, s->ref);
+ return format_buf;
+}
+
+
+subscription_t* new_subscription(subscription_type_t type,
+ uint32_t id, uint32_t cmdid,
+ void* handle,
+ void (*cleanup)(subscription_t* s))
+{
+ subscription_t* s = alloc_type(subscription_t);
+ s->type = type;
+ s->id = id;
+ s->cmdid = cmdid;
+ s->handle = handle;
+ s->opaque = NULL;
+ s->ref = 0;
+ s->accept = NULL;
+ s->cleanup = cleanup;
+ return s;
+}
+
+subscription_list_t* new_subscription_list()
+{
+ subscription_list_t* list = alloc_type(subscription_list_t);
+ return list;
+}
+
+subscription_t* retain_subscription(subscription_t* s)
+{
+ if (s == NULL) {
+ DEBUGF("retain_subscription: NULL");
+ }
+ else {
+ DEBUGF("retain_subscription: %s", format_subscription(s));
+ s->ref++;
+ }
+ return s;
+}
+
+subscription_link_t* new_subscription_link(subscription_list_t* list,
+ subscription_t* s)
+{
+ subscription_link_t* link = alloc_type(subscription_link_t);
+ if (link) {
+ link->list = list;
+ link->s = retain_subscription(s);
+ }
+ return link;
+}
+
+subscription_t* release_subscription(subscription_t* s)
+{
+ DEBUGF("release_subscription: %s", format_subscription(s));
+ if (s->ref <= 1) {
+ if (s->cleanup)
+ (*s->cleanup)(s);
+ if (s->opaque != NULL)
+ free(s->opaque);
+ free(s);
+ return NULL;
+ }
+ else {
+ s->ref--;
+ return s;
+ }
+}
+
+subscription_link_t* insert_after_link(subscription_link_t* link,
+ subscription_link_t* after_link)
+{
+ subscription_list_t* list = after_link->list;
+ link->next = after_link->next;
+ link->prev = after_link;
+ link->list = list;
+ after_link->next = link;
+ if (link->next == NULL)
+ list->last = link;
+ else
+ link->next->prev = link;
+ list->length++;
+ return link;
+}
+
+subscription_link_t* insert_before_link(subscription_link_t* link,
+ subscription_link_t* before_link)
+{
+ if (before_link->prev != NULL)
+ return insert_after_link(link, before_link->prev);
+ else {
+ subscription_list_t* list = before_link->list;
+ link->next = before_link;
+ link->list = list;
+ before_link->prev = link;
+ list->first = link;
+ list->length++;
+ }
+ return link;
+}
+
+int insert_link_first(subscription_link_t* link)
+{
+ if (link != NULL) {
+ subscription_list_t* list = link->list;
+ link->next = list->first;
+ link->prev = NULL;
+ if (list->first != NULL)
+ list->first->prev = link;
+ else
+ list->last = link;
+ list->first = link;
+ list->length++;
+ return 0;
+ }
+ return -1;
+}
+
+int insert_first(subscription_list_t* list, subscription_t* s)
+{
+ subscription_link_t* link = new_subscription_link(list,s);
+ return insert_link_first(link);
+}
+
+int insert_link_last(subscription_link_t* link)
+{
+ if (link != NULL) {
+ subscription_list_t* list = link->list;
+ link->next = NULL;
+ link->prev = list->last;
+ if (list->last)
+ list->last->next = link;
+ else
+ list->first = link;
+ list->last = link;
+ list->length++;
+ return 0;
+ }
+ return -1;
+}
+
+
+int insert_last(subscription_list_t* list, subscription_t*s)
+{
+ subscription_link_t* link = new_subscription_link(list,s);
+ return insert_link_last(link);
+}
+
+subscription_link_t* find_subscription_link(subscription_list_t* list,
+ subscription_type_t type,
+ uint32_t sid)
+{
+ subscription_link_t* p = list->first;
+ DEBUGF("find_subscription_link: %s id=%u",
+ format_subscription_type(type), sid);
+ while(p) {
+ if ((p->s->id == sid) && (p->s->type == type))
+ return p;
+ p = p->next;
+ }
+ return NULL;
+}
+
+subscription_t* find_subscription(subscription_list_t* list,
+ subscription_type_t type,
+ uint32_t sid)
+{
+ subscription_link_t* p = find_subscription_link(list, type, sid);
+ if (p != NULL)
+ return p->s;
+ return NULL;
+}
+
+void unlink_subscription(subscription_link_t* link)
+{
+ subscription_list_t* list = link->list;
+ DEBUGF("unlink_subscription: %s", format_subscription(link->s));
+ if (link->prev != NULL)
+ link->prev->next = link->next;
+ else
+ list->first = link->next;
+
+ if (link->next != NULL)
+ link->next->prev = link->prev;
+ else
+ list->last = link->prev;
+ list->length--;
+ release_subscription(link->s);
+ free(link);
+}
+
+int remove_subscription(subscription_list_t* list,
+ subscription_type_t type, uint32_t sid)
+{
+ subscription_link_t* link;
+
+ if ((link = find_subscription_link(list, type, sid)) != NULL) {
+ unlink_subscription(link);
+ return 1;
+ }
+ return 0;
+}
diff --git a/deps/bt/c_src/bt_sub.h b/deps/bt/c_src/bt_sub.h
new file mode 100644
index 0000000..75aed8a
--- /dev/null
+++ b/deps/bt/c_src/bt_sub.h
@@ -0,0 +1,82 @@
+#ifndef __BT_SUB_H__
+#define __BT_SUB_H__
+
+#include <stdlib.h>
+#include <stdint.h>
+
+#include "dthread/include/ddata.h"
+
+typedef enum {
+ INQUIRY,
+ REMOTE_NAME,
+ CONNECT,
+ SDP_QUERY,
+ SDP,
+ RFCOMM,
+ RFCOMM_LISTEN,
+ L2CAP,
+ L2CAP_LISTEN
+} subscription_type_t;
+
+typedef struct _subscription_t
+{
+ uint32_t ref; // ref count
+ subscription_type_t type; // type of subscription
+ uint32_t id; // subscription id
+ uint32_t cmdid; // current async cmdid (if any)
+ void* handle; // Bluetooth object handle
+ void* opaque; // subscription data
+ ddata_t* out; // send buffer
+ void (*cleanup)(struct _subscription_t* s); // clean up callback
+ struct _subscription_t* accept; // if on accept list
+} subscription_t;
+
+typedef struct _subscription_link_t
+{
+ struct _subscription_list_t* list;
+ struct _subscription_link_t* next;
+ struct _subscription_link_t* prev;
+ subscription_t* s;
+} subscription_link_t;
+
+typedef struct _subscription_list_t
+{
+ subscription_link_t* first;
+ subscription_link_t* last;
+ size_t length;
+} subscription_list_t;
+
+extern char* format_subscription(subscription_t* s);
+extern subscription_t* new_subscription(subscription_type_t type,
+ uint32_t id, uint32_t cmdid,
+ void* handle,
+ void (*cleanup)(subscription_t* s));
+extern subscription_list_t* new_subscription_list(void);
+extern subscription_t* retain_subscription(subscription_t* s);
+extern subscription_link_t* new_subscription_link(subscription_list_t* list,
+ subscription_t* s);
+extern void free_subscription(subscription_t* s);
+extern subscription_t* release_subscription(subscription_t* s);
+extern subscription_link_t* insert_after_link(subscription_link_t* link,
+ subscription_link_t* after_link);
+extern subscription_link_t* insert_before_link(subscription_link_t* link,
+ subscription_link_t* before_link);
+extern int insert_link_first(subscription_link_t* link);
+extern int insert_first(subscription_list_t* list, subscription_t* s);
+extern int insert_link_last(subscription_link_t* link);
+extern int insert_last(subscription_list_t* list, subscription_t*s);
+extern subscription_link_t* find_subscription_link(subscription_list_t* list,
+ subscription_type_t type,
+ uint32_t sid);
+extern subscription_t* find_subscription(subscription_list_t* list,
+ subscription_type_t type,
+ uint32_t sid);
+extern void unlink_subscription(subscription_link_t* link);
+extern int remove_subscription(subscription_list_t* list,
+ subscription_type_t type, uint32_t sid);
+
+
+
+
+
+#endif
diff --git a/deps/bt/c_src/hci_drv.c b/deps/bt/c_src/hci_drv.c
new file mode 100644
index 0000000..e3e9359
--- /dev/null
+++ b/deps/bt/c_src/hci_drv.c
@@ -0,0 +1,1009 @@
+/**** BEGIN COPYRIGHT ********************************************************
+ *
+ * Copyright (C) 2015 Rogvall Invest AB, <tony@rogvall.se>
+ *
+ * This software is licensed as described in the file COPYRIGHT, which
+ * you should have received as part of this distribution. The terms
+ * are also available at http://www.rogvall.se/docs/copyright.txt.
+ *
+ * You may opt to use, copy, modify, merge, publish, distribute and/or sell
+ * copies of the Software, and permit persons to whom the Software is
+ * furnished to do so, under the terms of the COPYRIGHT file.
+ *
+ * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY
+ * KIND, either express or implied.
+ *
+ **** END COPYRIGHT **********************************************************/
+
+#include <stdio.h>
+#include <stdarg.h>
+#include <stdint.h>
+#include <string.h>
+#include <unistd.h>
+#include <errno.h>
+#include <fcntl.h>
+#include <sys/ioctl.h>
+#include <sys/socket.h>
+#include <asm/types.h>
+#include <bluetooth/bluetooth.h>
+#include <bluetooth/hci.h>
+
+#include "erl_driver.h"
+
+#define ATOM(NAME) am_ ## NAME
+#define INIT_ATOM(NAME) am_ ## NAME = driver_mk_atom(#NAME)
+
+// Hack to handle R15 driver used with pre R15 driver
+#if ERL_DRV_EXTENDED_MAJOR_VERSION == 1
+typedef int ErlDrvSizeT;
+typedef int ErlDrvSSizeT;
+#endif
+
+#if (ERL_DRV_EXTENDED_MAJOR_VERSION > 2) || ((ERL_DRV_EXTENDED_MAJOR_VERSION == 2) && (ERL_DRV_EXTENDED_MINOR_VERSION >= 1))
+#define SEND_TERM(ctx, to, message, len) erl_drv_send_term((ctx)->dport,(to),(message),(len))
+#else
+#define SEND_TERM(ctx, to, message, len) driver_send_term((ctx)->port,(to),(message),(len))
+#endif
+
+#define PORT_CONTROL_BINARY
+
+#define INT_EVENT(ptr) ((int)((long)(ptr)))
+
+typedef struct _hci_drv_ctx_t {
+ ErlDrvPort port;
+ ErlDrvTermData dport;
+ ErlDrvTermData owner;
+ ErlDrvEvent fd; // socket
+ int protocol; // bt protocol
+ int active;
+ int is_selecting;
+ int is_sending;
+ int dev_id; // bound device id
+ void* hcibuf;
+ size_t hcibuf_len;
+} hci_drv_ctx_t;
+
+
+#define CMD_ACTIVE 1
+#define CMD_DEBUG 2
+#define CMD_BIND 3
+#define CMD_GETFILTER 4
+#define CMD_SETFILTER 5
+
+#define CMD_HCIDEVUP 201
+#define CMD_HCIDEVDOWN 202
+#define CMD_HCIDEVRESET 203
+#define CMD_HCIDEVRESTAT 204
+#define CMD_HCIGETDEVLIST 210
+#define CMD_HCIGETDEVINFO 211
+#define CMD_HCIGETCONNLIST 212
+#define CMD_HCIGETCONNINFO 213
+#define CMD_HCIGETAUTHINFO 215
+#define CMD_HCISETRAW 220
+#define CMD_HCISETSCAN 221
+#define CMD_HCISETAUTH 222
+#define CMD_HCISETENCRYPT 223
+#define CMD_HCISETPTYPE 224
+#define CMD_HCISETLINKPOL 225
+#define CMD_HCISETLINKMODE 226
+#define CMD_HCISETACLMTU 227
+#define CMD_HCISETSCOMTU 228
+#define CMD_HCIBLOCKADDR 230
+#define CMD_HCIUNBLOCKADDR 231
+#define CMD_HCIINQUIRY 240
+
+#define CTL_OK 0
+#define CTL_INT 1
+#define CTL_PAIR 2
+#define CTL_BIN 3
+#define CTL_LIST 4
+#define CTL_ERR 255
+#define CTL_STRERR 254
+
+#define MIN_HCI_BUFSIZE (32*1024)
+#define MAX_HCI_BUFSIZE (512*1024)
+
+#define MAX_CONN 10
+
+#define MAX_VSIZE 16
+
+ErlDrvTermData am_ok;
+ErlDrvTermData am_error;
+ErlDrvTermData am_undefined;
+ErlDrvTermData am_true;
+ErlDrvTermData am_false;
+
+#define push_atom(atm) do { \
+ message[i++] = ERL_DRV_ATOM; \
+ message[i++] = (atm); \
+ } while(0)
+
+#define push_port(prt) do { \
+ message[i++] = ERL_DRV_PORT; \
+ message[i++] = (prt); \
+ } while(0)
+
+#define push_pid(pid) do { \
+ message[i++] = ERL_DRV_PID; \
+ message[i++] = (pid); \
+ } while(0)
+
+#define push_bin(buf,len) do { \
+ message[i++] = ERL_DRV_BUF2BINARY; \
+ message[i++] = (ErlDrvTermData)(buf); \
+ message[i++] = (ErlDrvTermData)(len); \
+ } while(0)
+
+#define push_nil() do { \
+ message[i++] = ERL_DRV_NIL; \
+ } while(0)
+
+#define push_string(str) do { \
+ message[i++] = ERL_DRV_STRING; \
+ message[i++] = (ErlDrvTermData) (str); \
+ message[i++] = strlen(str); \
+ } while(0)
+
+#define push_int(val) do { \
+ message[i++] = ERL_DRV_INT; \
+ message[i++] = (val); \
+ } while(0)
+
+#define push_tuple(n) do { \
+ message[i++] = ERL_DRV_TUPLE; \
+ message[i++] = (n); \
+ } while(0)
+
+#define push_list(n) do { \
+ message[i++] = ERL_DRV_LIST; \
+ message[i++] = (n); \
+ } while(0)
+
+
+ErlDrvEntry hci_drv_entry;
+
+static inline uint32_t get_uint32(uint8_t* ptr)
+{
+ uint32_t value = (ptr[0]<<24) | (ptr[1]<<16) | (ptr[2]<<8) | (ptr[3]<<0);
+ return value;
+}
+
+static inline int32_t get_int32(uint8_t* ptr)
+{
+ uint32_t value = (ptr[0]<<24) | (ptr[1]<<16) | (ptr[2]<<8) | (ptr[3]<<0);
+ return (int32_t) value;
+}
+
+static inline uint16_t get_uint16(uint8_t* ptr)
+{
+ uint16_t value = (ptr[0]<<8) | (ptr[1]<<0);
+ return value;
+}
+
+static inline uint8_t get_uint8(uint8_t* ptr)
+{
+ return ptr[0];
+}
+
+static inline int8_t get_int8(uint8_t* ptr)
+{
+ return (int8_t) ptr[0];
+}
+
+static inline void put_uint8(uint8_t* ptr, uint8_t v)
+{
+ ptr[0] = v;
+}
+
+static inline void put_uint16(uint8_t* ptr, uint16_t v)
+{
+ ptr[0] = v>>8;
+ ptr[1] = v;
+}
+
+static inline void put_uint32(uint8_t* ptr, uint32_t v)
+{
+ ptr[0] = v>>24;
+ ptr[1] = v>>16;
+ ptr[2] = v>>8;
+ ptr[3] = v;
+}
+
+static uint8_t* put_hci_dev_stats(uint8_t* ptr, struct hci_dev_stats* sp)
+{
+ put_uint32(ptr, sp->err_rx); ptr += sizeof(sp->err_rx);
+ put_uint32(ptr, sp->err_tx); ptr += sizeof(sp->err_tx);
+ put_uint32(ptr, sp->cmd_tx); ptr += sizeof(sp->cmd_tx);
+ put_uint32(ptr, sp->evt_rx); ptr += sizeof(sp->evt_rx);
+ put_uint32(ptr, sp->acl_tx); ptr += sizeof(sp->acl_tx);
+ put_uint32(ptr, sp->acl_rx); ptr += sizeof(sp->acl_rx);
+ put_uint32(ptr, sp->sco_tx); ptr += sizeof(sp->sco_tx);
+ put_uint32(ptr, sp->sco_rx); ptr += sizeof(sp->sco_rx);
+ put_uint32(ptr, sp->byte_tx); ptr += sizeof(sp->byte_tx);
+ put_uint32(ptr, sp->byte_rx); ptr += sizeof(sp->byte_rx);
+ return ptr;
+};
+
+static uint8_t* put_hci_dev_info(uint8_t* ptr, struct hci_dev_info* di)
+{
+ put_uint16(ptr, di->dev_id); ptr += sizeof(di->dev_id);
+ memcpy(ptr, di->name, sizeof(di->name)); ptr += sizeof(di->name);
+ memcpy(ptr, &di->bdaddr, sizeof(di->bdaddr)); ptr += sizeof(di->bdaddr);
+ put_uint32(ptr, di->flags); ptr += sizeof(di->flags);
+ put_uint8(ptr, di->type); ptr += sizeof(di->type);
+ memcpy(ptr, di->features, sizeof(di->features)); ptr += sizeof(di->features);
+ put_uint32(ptr, di->pkt_type); ptr += sizeof(di->pkt_type);
+ put_uint32(ptr, di->link_policy); ptr += sizeof(di->link_policy);
+ put_uint32(ptr, di->link_mode); ptr += sizeof(di->link_mode);
+
+ put_uint16(ptr, di->acl_mtu); ptr += sizeof(di->acl_mtu);
+ put_uint16(ptr, di->acl_pkts); ptr += sizeof(di->acl_pkts);
+ put_uint16(ptr, di->sco_mtu); ptr += sizeof(di->sco_mtu);
+ put_uint16(ptr, di->sco_pkts); ptr += sizeof(di->sco_pkts);
+
+ return put_hci_dev_stats(ptr, &di->stat);
+}
+
+static uint8_t* put_hci_conn_info(uint8_t* ptr, struct hci_conn_info* ci)
+{
+ put_uint16(ptr, ci->handle);
+ ptr += sizeof(ci->handle);
+ memcpy(ptr, &ci->bdaddr, sizeof(ci->bdaddr));
+ ptr += sizeof(ci->bdaddr);
+ put_uint8(ptr, ci->type);
+ ptr += sizeof(ci->type);
+ put_uint8(ptr, ci->out);
+ ptr += sizeof(ci->out);
+ put_uint16(ptr, ci->state);
+ ptr += sizeof(ci->state);
+ put_uint32(ptr, ci->link_mode);
+ ptr += sizeof(ci->link_mode);
+ return ptr;
+}
+
+static uint8_t* put_hci_filter(uint8_t* ptr, struct hci_filter* fp)
+{
+ put_uint32(ptr, fp->type_mask); ptr += sizeof(fp->type_mask);
+ put_uint32(ptr, fp->event_mask[0]); ptr += sizeof(fp->event_mask[0]);
+ put_uint32(ptr, fp->event_mask[1]); ptr += sizeof(fp->event_mask[1]);
+ put_uint16(ptr, fp->opcode); ptr += sizeof(fp->opcode);
+ return ptr;
+}
+
+static uint8_t* get_hci_filter(uint8_t* ptr, struct hci_filter* fp)
+{
+
+ fp->type_mask = get_uint32(ptr); ptr += sizeof(fp->type_mask);
+ fp->event_mask[0] = get_uint32(ptr); ptr += sizeof(fp->event_mask[0]);
+ fp->event_mask[1] = get_uint32(ptr); ptr += sizeof(fp->event_mask[1]);
+ fp->opcode = get_uint16(ptr); ptr += sizeof(fp->opcode);
+ return ptr;
+}
+
+static int hci_drv_init(void);
+static void hci_drv_finish(void);
+static void hci_drv_stop(ErlDrvData);
+static void hci_drv_output(ErlDrvData,char*,ErlDrvSizeT);
+#if 0
+static void hci_drv_outputv(ErlDrvData, ErlIOVec*);
+#endif
+static void hci_drv_ready_input(ErlDrvData, ErlDrvEvent);
+static void hci_drv_ready_output(ErlDrvData data, ErlDrvEvent event);
+static ErlDrvData hci_drv_start(ErlDrvPort, char* command);
+static ErlDrvSSizeT hci_drv_ctl(ErlDrvData,unsigned int,char*,ErlDrvSizeT,char**,ErlDrvSizeT);
+static void hci_drv_timeout(ErlDrvData);
+static void hci_drv_stop_select(ErlDrvEvent event, void* arg);
+
+#define DLOG_DEBUG 7
+#define DLOG_INFO 6
+#define DLOG_NOTICE 5
+#define DLOG_WARNING 4
+#define DLOG_ERROR 3
+#define DLOG_CRITICAL 2
+#define DLOG_ALERT 1
+#define DLOG_EMERGENCY 0
+#define DLOG_NONE -1
+
+#ifndef DLOG_DEFAULT
+#define DLOG_DEFAULT DLOG_NONE
+#endif
+
+#define DLOG(level,file,line,args...) do { \
+ if (((level) == DLOG_EMERGENCY) || \
+ ((debug_level >= 0) && ((level) <= debug_level))) { \
+ emit_log((level),(file),(line),args); \
+ } \
+ } while(0)
+
+#define DEBUGF(args...) DLOG(DLOG_DEBUG,__FILE__,__LINE__,args)
+#define INFOF(args...) DLOG(DLOG_INFO,__FILE__,__LINE__,args)
+#define NOTICEF(args...) DLOG(DLOG_NOTICE,__FILE__,__LINE__,args)
+#define WARNINGF(args...) DLOG(DLOG_WARNING,__FILE__,__LINE__,args)
+#define ERRORF(args...) DLOG(DLOG_ERROR,__FILE__,__LINE__,args)
+#define CRITICALF(args...) DLOG(DLOG_CRITICAL,__FILE__,__LINE__,args)
+#define ALERTF(args...) DLOG(DLOG_ALERT,__FILE__,__LINE__,args)
+#define EMERGENCYF(args...) DLOG(DLOG_EMERGENCY,__FILE__,__LINE__,args)
+
+static int debug_level = DLOG_DEFAULT;
+
+static void emit_log(int level, char* file, int line, ...)
+{
+ va_list ap;
+ char* fmt;
+
+ if ((level == DLOG_EMERGENCY) ||
+ ((debug_level >= 0) && (level <= debug_level))) {
+ int save_errno = errno;
+ va_start(ap, line);
+ fmt = va_arg(ap, char*);
+ fprintf(stderr, "%s:%d: ", file, line);
+ vfprintf(stderr, fmt, ap);
+ fprintf(stderr, "\r\n");
+ va_end(ap);
+ errno = save_errno;
+ }
+}
+
+/* general control reply function */
+static ErlDrvSSizeT ctl_reply(int rep, void* buf, ErlDrvSizeT len,
+ char** rbuf, ErlDrvSizeT rsize)
+{
+ char* ptr;
+
+ if ((len+1) > rsize) {
+#ifdef PORT_CONTROL_BINARY
+ ErlDrvBinary* bin = driver_alloc_binary(len+1);
+ if (bin == NULL)
+ return -1;
+ ptr = bin->orig_bytes;
+ *rbuf = (char*) bin;
+#else
+ if ((ptr = driver_alloc(len+1)) == NULL)
+ return -1;
+ *rbuf = ptr;
+#endif
+ }
+ else
+ ptr = *rbuf;
+ *ptr++ = rep;
+ memcpy(ptr, buf, len);
+ return len+1;
+}
+
+static void* hci_drv_realloc_buffer(hci_drv_ctx_t* ctx, size_t len)
+{
+ if (ctx->hcibuf_len < len) {
+ ctx->hcibuf = driver_realloc(ctx->hcibuf, len);
+ ctx->hcibuf_len = len;
+ }
+ return ctx->hcibuf;
+}
+
+static int hci_drv_init(void)
+{
+ INIT_ATOM(ok);
+ INIT_ATOM(error);
+ INIT_ATOM(undefined);
+ INIT_ATOM(true);
+ INIT_ATOM(false);
+ return 0;
+}
+
+static void hci_drv_finish(void)
+{
+}
+
+static void hci_drv_stop(ErlDrvData d)
+{
+ hci_drv_ctx_t* ctx = (hci_drv_ctx_t*) d;
+
+ if (ctx) {
+ if (ctx->is_selecting)
+ driver_select(ctx->port, ctx->fd, ERL_DRV_READ, 0);
+ if (ctx->is_sending)
+ driver_select(ctx->port, ctx->fd, ERL_DRV_WRITE, 0);
+ driver_select(ctx->port, ctx->fd, ERL_DRV_USE, 0);
+ driver_free(ctx);
+ }
+}
+
+// #define USE_WRITEV
+
+static void hci_drv_output(ErlDrvData d, char* buf,ErlDrvSizeT len)
+{
+ hci_drv_ctx_t* ctx = (hci_drv_ctx_t*) d;
+ int n;
+
+ if ((n = driver_sizeq(ctx->port)) > 0) {
+ driver_enq(ctx->port, buf, len);
+ DEBUGF("hci_drv_tput: put on queue pending=%d", n+len);
+ }
+ else { // try send directly
+ // data format must be
+ // <<HCI_COMMAND_PKT,Opcode:16,Plen:8,Param:Plen/binary>>
+#ifdef USE_WRITEV
+ struct iovec iv[3];
+ int ivn;
+ // try write with writev !!!
+ iv[0].iov_base = buf;
+ iv[0].iov_len = 1;
+ iv[1].iov_base = buf+1;
+ iv[1].iov_len = HCI_COMMAND_HDR_SIZE;
+ iv[2].iov_base = buf+4;
+ iv[2].iov_len = buf[3];
+ ivn = buf[3] ? 3 : 2;
+ DEBUGF("writev = %d, len=%d", 1+HCI_COMMAND_HDR_SIZE+buf[3], len);
+
+ n = writev(INT_EVENT(ctx->fd), iv, ivn);
+#else
+ n = write(INT_EVENT(ctx->fd), buf, len);
+#endif
+ if (n < 0) {
+ ERRORF("write error=%s", strerror(errno));
+ if ((errno == EAGAIN) || (errno = ENOBUFS)) {
+ DEBUGF("hci_drv: put %d bytes on queue", n);
+ driver_enq(ctx->port, buf, len);
+ driver_select(ctx->port, ctx->fd, ERL_DRV_WRITE, 1);
+ ctx->is_sending = 1;
+ }
+ }
+ else if (n < (int)len) {
+ driver_enq(ctx->port, buf+n, len+n);
+ driver_select(ctx->port, ctx->fd, ERL_DRV_WRITE, 1);
+ ctx->is_sending = 1;
+ }
+ else {
+ DEBUGF("hci_drv: wrote %d bytes", n);
+ }
+ }
+}
+
+// hci socket triggered process data
+static void hci_drv_ready_input(ErlDrvData d, ErlDrvEvent event)
+{
+ hci_drv_ctx_t* ctx = (hci_drv_ctx_t*) d;
+ uint8_t buf[HCI_MAX_EVENT_SIZE];
+ int n;
+ (void) event;
+
+ if ((n = read(INT_EVENT(ctx->fd), buf, sizeof(buf))) < 0) {
+ if ((errno == EAGAIN) || (errno == EINTR))
+ return;
+ DEBUGF("hci_drv_ready_input: read error %d, %s",
+ errno, strerror(errno));
+ return;
+ }
+ DEBUGF("hci_drv_ready_input: got %d bytes", n);
+ if (n > 0)
+ driver_output(ctx->port, (char*) buf, n);
+}
+
+static void hci_drv_ready_output(ErlDrvData d, ErlDrvEvent event)
+{
+ hci_drv_ctx_t* ctx = (hci_drv_ctx_t*) d;
+ int vsize;
+ SysIOVec* iovp;
+ int n;
+
+ DEBUGF("hci_drv_ready_output called");
+
+ if (ctx->fd != event) {
+ DEBUGF("hci_drv_ready_output bad event");
+ return;
+ }
+
+ if ((iovp = driver_peekq(ctx->port, &vsize)) == NULL) {
+ driver_select(ctx->port, ctx->fd, ERL_DRV_WRITE, 0);
+ ctx->is_sending = 0;
+ return;
+ }
+ vsize = vsize > MAX_VSIZE ? MAX_VSIZE : vsize;
+
+ DEBUGF("hci_drv_ready_output: try send vsize=%d", vsize);
+ n = writev(INT_EVENT(ctx->fd), (const struct iovec*) iovp, vsize);
+ if (n < 0) {
+ if ((errno == EAGAIN) || (errno == ENOBUFS))
+ return;
+ ERRORF("write error=%s", strerror(errno));
+ return;
+ }
+ DEBUGF("hci_drv_ready_output: sent %d bytes", n);
+ if (driver_deq(ctx->port, n) == 0) {
+ driver_select(ctx->port, ctx->fd, ERL_DRV_WRITE, 0);
+ ctx->is_sending = 0;
+ }
+}
+
+
+static ErlDrvSSizeT hci_drv_ctl(ErlDrvData d,unsigned int cmd,char* buf0,
+ ErlDrvSizeT len,char** rbuf,ErlDrvSizeT rsize)
+{
+ uint8_t* buf = (uint8_t*) buf0;
+ hci_drv_ctx_t* ctx = (hci_drv_ctx_t*) d;
+
+ DEBUGF("hci_drv_ctl cmd=%d", cmd);
+
+ switch(cmd) {
+ case CMD_ACTIVE: {
+ int active;
+
+ if (len != 4)
+ goto badarg;
+ active = get_int32(buf);
+ if (active) {
+ if (!ctx->is_selecting) {
+ driver_select(ctx->port, ctx->fd, ERL_DRV_READ, 1);
+ DEBUGF("selecting %d for read\n", ctx->fd);
+ }
+ ctx->is_selecting = 1;
+ ctx->active = active;
+ }
+ else {
+ if (ctx->is_selecting) {
+ driver_select(ctx->port, ctx->fd, ERL_DRV_READ, 0);
+ DEBUGF("deselecting %d for read\n", ctx->fd);
+ }
+ ctx->is_selecting = 0;
+ ctx->active = 0;
+ }
+ goto ok;
+ break;
+ }
+
+ case CMD_DEBUG: {
+ if (len != 4)
+ goto badarg;
+ debug_level = get_int32(buf);
+ goto ok;
+ }
+
+ case CMD_BIND: {
+ struct sockaddr_hci a;
+ int dev_id;
+
+ if (len != 4)
+ goto badarg;
+ if ((dev_id = get_int32(buf)) < 0) {
+ errno = ENODEV;
+ goto error;
+ }
+ memset(&a, 0, sizeof(a));
+ a.hci_family = AF_BLUETOOTH;
+ a.hci_dev = dev_id;
+
+ if (bind(INT_EVENT(ctx->fd), (struct sockaddr *) &a, sizeof(a)) < 0)
+ goto error;
+ ctx->dev_id = dev_id;
+ goto ok;
+ }
+
+ case CMD_GETFILTER: {
+ struct hci_filter filt;
+ socklen_t flen;
+ uint8_t lbuf[sizeof(struct hci_filter)];
+ uint8_t* lptr;
+
+ flen = sizeof(filt);
+ if (getsockopt(INT_EVENT(ctx->fd), SOL_HCI, HCI_FILTER, &filt, &flen) < 0)
+ goto error;
+ lptr = put_hci_filter(lbuf, &filt);
+ return ctl_reply(CTL_BIN, lbuf, lptr-lbuf, rbuf, rsize);
+ }
+
+ case CMD_SETFILTER: {
+ struct hci_filter filt;
+
+ if (len != 14)
+ goto badarg;
+ (void) get_hci_filter(buf, &filt);
+ if (setsockopt(INT_EVENT(ctx->fd), SOL_HCI, HCI_FILTER, &filt,
+ sizeof(filt)) < 0)
+ goto error;
+ goto ok;
+ }
+
+ case CMD_HCIDEVUP: {
+ int dev_id;
+ if (len != 4)
+ goto badarg;
+ dev_id = get_int32(buf);
+ if (ioctl(INT_EVENT(ctx->fd), HCIDEVUP, dev_id) < 0)
+ goto error;
+ goto ok;
+ }
+
+ case CMD_HCIDEVDOWN: {
+ int dev_id;
+ if (len != 4)
+ goto badarg;
+ dev_id = get_int32(buf);
+ if (ioctl(INT_EVENT(ctx->fd), HCIDEVDOWN, dev_id) < 0)
+ goto error;
+ goto ok;
+ }
+
+ case CMD_HCIDEVRESET: {
+ int dev_id;
+ if (len != 4)
+ goto badarg;
+ dev_id = get_int32(buf);
+ if (ioctl(INT_EVENT(ctx->fd), HCIDEVRESET, dev_id) < 0)
+ goto error;
+ goto ok;
+ }
+
+ case CMD_HCIDEVRESTAT: {
+ int dev_id;
+ if (len != 4)
+ goto badarg;
+ dev_id = get_int32(buf);
+ if (ioctl(INT_EVENT(ctx->fd), HCIDEVRESTAT, dev_id) < 0)
+ goto error;
+ goto ok;
+ }
+
+ case CMD_HCIGETDEVLIST: {
+ struct hci_dev_list_req *dl;
+ size_t sz = HCI_MAX_DEV*sizeof(struct hci_dev_req) +
+ sizeof(struct hci_dev_list_req);
+ uint8_t lbuf[HCI_MAX_DEV*sizeof(struct hci_dev_req)];
+ uint8_t* lptr;
+ int i;
+
+ if ((dl = hci_drv_realloc_buffer(ctx, sz)) == NULL)
+ goto error;
+ memset(dl, 0, sz);
+ dl->dev_num = HCI_MAX_DEV;
+
+ if (ioctl(INT_EVENT(ctx->fd), HCIGETDEVLIST, (void *) dl) < 0)
+ goto error;
+
+ lptr = lbuf;
+ for (i = 0; i < dl->dev_num; i++) {
+ put_uint16(lptr, dl->dev_req[i].dev_id);
+ lptr += sizeof(dl->dev_req[i].dev_id);
+ put_uint32(lptr, dl->dev_req[i].dev_opt);
+ lptr += sizeof(dl->dev_req[i].dev_opt);
+ }
+ return ctl_reply(CTL_BIN, lbuf, lptr-lbuf, rbuf, rsize);
+ }
+
+ case CMD_HCIGETDEVINFO: {
+ size_t sz = sizeof(struct hci_dev_info);
+ uint8_t lbuf[sizeof(struct hci_dev_info)];
+ uint8_t* lptr;
+ struct hci_dev_info* di;
+ int dev_id;
+
+ if (len != 4)
+ goto badarg;
+ dev_id = get_int32(buf);
+
+ if ((di = hci_drv_realloc_buffer(ctx, sz)) == NULL)
+ goto error;
+ memset(di, 0, sz);
+
+ di->dev_id = dev_id;
+ if (ioctl(INT_EVENT(ctx->fd), HCIGETDEVINFO, (void *) di) < 0)
+ goto error;
+ lptr = put_hci_dev_info(lbuf, di);
+ return ctl_reply(CTL_BIN, lbuf, lptr-lbuf, rbuf, rsize);
+ }
+
+ case CMD_HCIGETCONNLIST: {
+ struct hci_conn_list_req *cl;
+ size_t sz = sizeof(struct hci_conn_list_req) +
+ MAX_CONN*sizeof(struct hci_conn_info);
+ uint8_t lbuf[MAX_CONN*sizeof(struct hci_conn_info)];
+ uint8_t* lptr;
+ int dev_id;
+ int i;
+
+ if (len != 4)
+ goto badarg;
+ dev_id = get_int32(buf);
+
+ if ((cl = hci_drv_realloc_buffer(ctx, sz)) == NULL)
+ goto error;
+ memset(cl, 0, sz);
+ cl->dev_id = dev_id;
+ cl->conn_num = MAX_CONN;
+
+ if (ioctl(INT_EVENT(ctx->fd), HCIGETCONNLIST, (void *)cl) < 0)
+ goto error;
+
+ lptr = lbuf;
+ for (i=0; i< cl->conn_num; i++)
+ lptr = put_hci_conn_info(lptr, &cl->conn_info[i]);
+ return ctl_reply(CTL_BIN, lbuf, lptr-lbuf, rbuf, rsize);
+ }
+
+ case CMD_HCIGETCONNINFO: {
+ struct hci_conn_info_req *ci;
+ size_t sz = sizeof(struct hci_conn_info_req) +
+ sizeof(struct hci_conn_info);
+ uint8_t lbuf[sizeof(struct hci_conn_info)];
+ uint8_t* lptr;
+
+ if (len != 7)
+ goto badarg;
+
+ if ((ci = hci_drv_realloc_buffer(ctx, sz)) == NULL)
+ goto error;
+ memset(ci, 0, sz);
+ memcpy(&ci->bdaddr, buf, sizeof(bdaddr_t));
+ ci->type = buf[6];
+
+ if (ioctl(INT_EVENT(ctx->fd), HCIGETCONNINFO, (void *)ci) < 0)
+ goto error;
+ lptr = put_hci_conn_info(lbuf, &ci->conn_info[0]);
+ return ctl_reply(CTL_BIN, lbuf, lptr-lbuf, rbuf, rsize);
+ }
+
+ case CMD_HCIGETAUTHINFO: {
+ struct hci_auth_info_req ai;
+ if (len != 6)
+ goto badarg;
+ memcpy(&ai.bdaddr, buf, sizeof(ai.bdaddr));
+ if (ioctl(INT_EVENT(ctx->fd), HCIGETAUTHINFO, (void *)&ai) < 0)
+ goto error;
+ return ctl_reply(CTL_BIN, &ai.type, 1, rbuf, rsize);
+ }
+
+ case CMD_HCISETRAW: { // guessing...
+ int dev_id;
+ if (len != 4)
+ goto badarg;
+ dev_id = get_int32(buf);
+ if (ioctl(INT_EVENT(ctx->fd), HCISETRAW, dev_id) < 0)
+ goto error;
+ goto ok;
+ }
+
+ case CMD_HCISETSCAN: {
+ struct hci_dev_req dr;
+ if (len != 8)
+ goto badarg;
+ dr.dev_id = get_int32(buf);
+ dr.dev_opt = get_uint32(buf+4);
+ if (ioctl(INT_EVENT(ctx->fd), HCISETSCAN, (void*) &dr) < 0)
+ goto error;
+ goto ok;
+ }
+
+ case CMD_HCISETAUTH: {
+ struct hci_dev_req dr;
+ if (len != 5)
+ goto badarg;
+ dr.dev_id = get_int32(buf);
+ dr.dev_opt = buf[4] ? AUTH_ENABLED : AUTH_DISABLED;
+ if (ioctl(INT_EVENT(ctx->fd), HCISETAUTH, (void*) &dr) < 0)
+ goto error;
+ goto ok;
+ }
+
+ case CMD_HCISETENCRYPT: {
+ struct hci_dev_req dr;
+ if (len != 5)
+ goto badarg;
+ dr.dev_id = get_int32(buf);
+ dr.dev_opt = buf[4] ? ENCRYPT_P2P : ENCRYPT_DISABLED;
+ if (ioctl(INT_EVENT(ctx->fd), HCISETAUTH, (void*) &dr) < 0)
+ goto error;
+ goto ok;
+ }
+
+ case CMD_HCISETPTYPE: {
+ struct hci_dev_req dr;
+ if (len != 8)
+ goto badarg;
+ dr.dev_id = get_int32(buf);
+ dr.dev_opt = get_uint32(buf+4);
+ if (ioctl(INT_EVENT(ctx->fd), HCISETPTYPE, (void*) &dr) < 0)
+ goto error;
+ goto ok;
+ }
+
+ case CMD_HCISETLINKPOL: {
+ struct hci_dev_req dr;
+ if (len != 8)
+ goto badarg;
+ dr.dev_id = get_int32(buf);
+ dr.dev_opt = get_uint32(buf+4);
+ if (ioctl(INT_EVENT(ctx->fd), HCISETLINKPOL, (void*) &dr) < 0)
+ goto error;
+ goto ok;
+ }
+
+ case CMD_HCISETLINKMODE: {
+ struct hci_dev_req dr;
+ if (len != 8)
+ goto badarg;
+ dr.dev_id = get_int32(buf);
+ dr.dev_opt = get_uint32(buf+4);
+ if (ioctl(INT_EVENT(ctx->fd), HCISETLINKMODE, (void*) &dr) < 0)
+ goto error;
+ goto ok;
+ }
+
+ case CMD_HCISETACLMTU: {
+ struct hci_dev_req dr;
+ uint16_t mtu, mpkt;
+
+ if (len != 8)
+ goto badarg;
+ dr.dev_id = get_int32(buf);
+ mtu = get_uint16(buf+4);
+ mpkt = get_uint16(buf+6);
+ dr.dev_opt = htobl(htobs(mpkt) | (htobs(mtu) << 16));
+ if (ioctl(INT_EVENT(ctx->fd), HCISETACLMTU, (void*) &dr) < 0)
+ goto error;
+ goto ok;
+ }
+
+ case CMD_HCISETSCOMTU: {
+ struct hci_dev_req dr;
+ uint16_t mtu, mpkt;
+
+ if (len != 8)
+ goto badarg;
+ dr.dev_id = get_int32(buf);
+ mtu = get_uint16(buf+4);
+ mpkt = get_uint16(buf+6);
+ dr.dev_opt = htobl(htobs(mpkt) | (htobs(mtu) << 16));
+ if (ioctl(INT_EVENT(ctx->fd), HCISETSCOMTU, (void*) &dr) < 0)
+ goto error;
+ goto ok;
+ }
+
+ case CMD_HCIBLOCKADDR: { // bound
+ bdaddr_t bdaddr;
+ if (len != 6)
+ goto badarg;
+ memcpy(&bdaddr, buf, 6);
+ if (ioctl(INT_EVENT(ctx->fd), HCIBLOCKADDR, (void*) &bdaddr) < 0)
+ goto error;
+ goto ok;
+ }
+
+ case CMD_HCIUNBLOCKADDR: { // bound, bdaddr={0,0,0,0,0,0} => all
+ bdaddr_t bdaddr;
+ if (len != 6)
+ goto badarg;
+ memcpy(&bdaddr, buf, 6);
+ if (ioctl(INT_EVENT(ctx->fd), HCIUNBLOCKADDR, (void*) &bdaddr) < 0)
+ goto error;
+ goto ok;
+ }
+
+ case CMD_HCIINQUIRY: {
+ // blocking! must be in a thread or use the async api
+ uint8_t ibuf[sizeof(struct hci_inquiry_req)+sizeof(inquiry_info)*255];
+ struct hci_inquiry_req* ir;
+
+ if (len != 9)
+ goto badarg;
+ ir = (struct hci_inquiry_req *) ibuf;
+ ir->length = get_uint8(buf);
+ ir->num_rsp = get_uint8(buf+1);
+ ir->lap[0] = get_uint8(buf+2);
+ ir->lap[1] = get_uint8(buf+3);
+ ir->lap[2] = get_uint8(buf+4);
+ ir->flags = get_uint32(buf+5);
+ ir->dev_id = ctx->dev_id;
+
+ if (ioctl(INT_EVENT(ctx->fd), HCIINQUIRY, (unsigned long) ibuf) < 0)
+ goto error;
+
+ // return size is sizoef(inquiry_info) * ir->num_rsp
+ return ctl_reply(CTL_BIN, ibuf + sizeof(struct hci_inquiry_req),
+ sizeof(inquiry_info) * ir->num_rsp, rbuf, rsize);
+ }
+
+ default:
+ return -1;
+ }
+
+ok:
+ return ctl_reply(CTL_OK, NULL, 0, rbuf, rsize);
+badarg:
+ errno = EINVAL;
+error: {
+ char* err_str = erl_errno_id(errno);
+ return ctl_reply(CTL_ERR, err_str, strlen(err_str), rbuf, rsize);
+}
+}
+
+
+static void hci_drv_timeout(ErlDrvData d)
+{
+ (void) d;
+ fprintf(stderr, "hci_drv_timeout called!!!\r\n");
+}
+
+static void hci_drv_stop_select(ErlDrvEvent event, void* arg)
+{
+ (void) arg;
+ DEBUGF("eth_drv: stop_select event=%d", INT_EVENT(event));
+ close(INT_EVENT(event));
+}
+
+
+static ErlDrvData hci_drv_start(ErlDrvPort port, char* command)
+{
+ (void) command;
+ hci_drv_ctx_t* ctx;
+ int fd;
+ int protocol;
+ char* ptr;
+ // char* arg;
+
+ ptr = command;
+ while(*ptr && (*ptr != ' ')) ptr++; // skip command
+ while(*ptr && (*ptr == ' ')) ptr++; // and blanks
+ // arg = ptr;
+ // while(*ptr && (*ptr >= '0') && (*ptr <= '9')) ptr++;
+ // if ((arg == ptr) || (*ptr != '\0')) {
+ // errno = EINVAL;
+ // return ERL_DRV_ERROR_ERRNO;
+ // }
+ // protocol = atoi(arg);
+ protocol = BTPROTO_HCI;
+ if ((fd = socket(PF_BLUETOOTH,
+ SOCK_NONBLOCK| SOCK_RAW | SOCK_CLOEXEC,
+ protocol)) < 0)
+ return ERL_DRV_ERROR_ERRNO;
+
+ // flags = fcntl(fd, F_GETFL, 0);
+ // fcntl(fd, F_SETFL, flags | O_NONBLOCK);
+
+ if (!(ctx = driver_alloc(sizeof(hci_drv_ctx_t))))
+ return ERL_DRV_ERROR_ERRNO;
+ memset(ctx, 0, sizeof(hci_drv_ctx_t));
+ ctx->port = port;
+ ctx->dport = driver_mk_port(port);
+ ctx->owner = driver_caller(port);
+ ctx->protocol = protocol;
+ ctx->fd = (ErlDrvEvent)((long)fd);
+ ctx->active = 0;
+ ctx->is_selecting = 0;
+ ctx->is_sending = 0;
+
+ // create i/o buffer
+ hci_drv_realloc_buffer(ctx, MIN_HCI_BUFSIZE);
+#ifdef PORT_CONTROL_BINARY
+ set_port_control_flags(port, PORT_CONTROL_FLAG_BINARY);
+#endif
+ return (ErlDrvData) ctx;
+}
+
+DRIVER_INIT(hci_drv)
+{
+ ErlDrvEntry* ptr = &hci_drv_entry;
+
+ ptr->driver_name = "hci_drv";
+ ptr->init = hci_drv_init;
+ ptr->start = hci_drv_start;
+ ptr->stop = hci_drv_stop;
+ ptr->output = hci_drv_output;
+ ptr->ready_input = hci_drv_ready_input;
+ ptr->ready_output = hci_drv_ready_output;
+ ptr->finish = hci_drv_finish;
+ ptr->control = hci_drv_ctl;
+ ptr->timeout = hci_drv_timeout;
+#if 0
+ ptr->outputv = hci_drv_outputv;
+#endif
+ ptr->ready_async = 0;
+ ptr->flush = 0;
+ ptr->call = 0;
+ ptr->event = 0;
+ ptr->extended_marker = ERL_DRV_EXTENDED_MARKER;
+ ptr->major_version = ERL_DRV_EXTENDED_MAJOR_VERSION;
+ ptr->minor_version = ERL_DRV_EXTENDED_MINOR_VERSION;
+ ptr->driver_flags = ERL_DRV_FLAG_USE_PORT_LOCKING;
+ ptr->process_exit = 0;
+ ptr->stop_select = hci_drv_stop_select;
+
+ return (ErlDrvEntry*) ptr;
+}
diff --git a/deps/bt/ebin/.gitignore b/deps/bt/ebin/.gitignore
new file mode 100644
index 0000000..120fe3a
--- /dev/null
+++ b/deps/bt/ebin/.gitignore
@@ -0,0 +1,2 @@
+*.beam
+*.app
diff --git a/deps/bt/include/bt.hrl b/deps/bt/include/bt.hrl
new file mode 100644
index 0000000..4e8878a
--- /dev/null
+++ b/deps/bt/include/bt.hrl
@@ -0,0 +1,67 @@
+%%%---- BEGIN COPYRIGHT -------------------------------------------------------
+%%%
+%%% Copyright (C) 2006 - 2014, Rogvall Invest AB, <tony@rogvall.se>
+%%%
+%%% This software is licensed as described in the file COPYRIGHT, which
+%%% you should have received as part of this distribution. The terms
+%%% are also available at http://www.rogvall.se/docs/copyright.txt.
+%%%
+%%% You may opt to use, copy, modify, merge, publish, distribute and/or sell
+%%% copies of the Software, and permit persons to whom the Software is
+%%% furnished to do so, under the terms of the COPYRIGHT file.
+%%%
+%%% This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY
+%%% KIND, either express or implied.
+%%%
+%%%---- END COPYRIGHT ---------------------------------------------------------
+
+%%
+%% Some none-guard macros
+%%
+-ifndef(BT_HRL).
+-define(BT_HRL, true).
+
+-include("uuid.hrl").
+
+%% Bluetooth base uuid = ?BT_UUID(0)
+-define(BT_UUID32(N),
+ ?UUID(N,16#0000,16#1000,16#8000,16#00805f9b34fb)).
+
+%% Speical UUID match for 16 bit BT uuid
+-define(BT_UUID16(N),
+ <<16#0000:16,N:16,16#0000:16,16#1000:16,16#8000:16,16#00805f9b34fb:48>>).
+
+
+-define(is_bt_address(A),
+ is_tuple((A)), size((A)) == 6,
+ ((element(1,(A)) bor element(2,(A)) bor element(3,(A)) bor
+ element(4,(A)) bor element(5,(A)) bor element(6,(A))) band
+ -16#100 == 0)).
+
+-define(never, {{0,0,0},{0,0,0}}).
+
+-define(L2CAP_PSM_None, 16#0000).
+
+%% Range < 0x1000 reserved.
+-define(L2CAP_PSM_ReservedStart, 16#0001).
+-define(L2CAP_PSM_ReservedEnd, 16#1000).
+
+-define(L2CAP_PSM_SDP, 16#0001).
+-define(L2CAP_PSM_RFCOMM, 16#0003).
+-define(L2CAP_PSM_TCS_BIN, 16#0005). %% Telephony Control Specifictation / TCS Binary
+
+-define(L2CAP_PSM_TCS_BIN_Cordless, 16#0007). %% Telephony Control Specifictation / TCS Binary
+-define(L2CAP_PSM_BNEP, 16#000F). %% Bluetooth Network Encapsulation Protocol
+-define(L2CAP_PSM_HIDControl, 16#0011). %% HID profile - control interface
+-define(L2CAP_PSM_HIDInterrupt, 16#0013). %% HID profile - interrupt interface
+-define(L2CAP_PSM_AVCTP, 16#0017). %% Audio/Video Control Transport Protocol
+-define(L2CAP_PSM_AVDTP, 16#0019). %% Audio/Video Distribution Transport Protocol
+-define(L2CAP_PSM_UID_C_Plane, 16#001D). %% Unrestricted Digital Information Profile (UDI)
+
+%% Range 0x1001-0xFFFF dynamically assigned.
+-define(L2CAP_PSM_DynamicStart, 16#1001).
+-define(L2CAP_PSM_DynamicEnd, 16#FFFF).
+
+-endif.
+
+
diff --git a/deps/bt/include/hci_drv.hrl b/deps/bt/include/hci_drv.hrl
new file mode 100644
index 0000000..c13c50c
--- /dev/null
+++ b/deps/bt/include/hci_drv.hrl
@@ -0,0 +1,260 @@
+
+-ifndef(__HCI_DRV_HRL__).
+-define(__HCI_DRV_HRL__, true).
+
+-type bdaddr_t() :: binary(). %% 6 bytes
+-type uint8_t() :: 0..16#ff.
+-type uint16_t() :: 0..16#ffff.
+-type uint32_t() :: 0..16#ffffffff.
+-type uint64_t() :: 0..16#ffffffffffffffff.
+
+%% HCI Packet types
+-define(HCI_COMMAND_PKT, 16#01).
+-define(HCI_ACLDATA_PKT, 16#02).
+-define(HCI_SCODATA_PKT, 16#03).
+-define(HCI_EVENT_PKT, 16#04).
+-define(HCI_VENDOR_PKT, 16#ff).
+
+%% Command opcode pack/unpack
+-define(cmd_opcode_pack(OGF, OCF), (((OCF) band 16#03ff)bor((OGF) bsl 10))).
+-define(cmd_opcode_ogf(OP), ((OP) bsr 10)).
+-define(cmd_opcode_ocf(OP), ((OP) band 16#03ff)).
+
+%% ACL handle and flags pack/unpack
+-define(acl_handle_pack(H, F), (((H) band 16#0fff) bor ((F) bsl 12))).
+-define(acl_handle(H), ((H) band 16#0fff)).
+-define(acl_flags(H), ((H) bsr 12)).
+
+-record(hci_dev_stats,
+ {
+ err_rx::uint32_t(),
+ err_tx::uint32_t(),
+ cmd_tx::uint32_t(),
+ evt_rx::uint32_t(),
+ acl_tx::uint32_t(),
+ acl_rx::uint32_t(),
+ sco_tx::uint32_t(),
+ sco_rx::uint32_t(),
+ byte_rx::uint32_t(),
+ byte_tx::uint32_t()
+ }).
+
+%% HCI device flags
+-define(HCI_UP, 0).
+-define(HCI_INIT, 1).
+-define(HCI_RUNNING, 2).
+-define(HCI_PSCAN, 3).
+-define(HCI_ISCAN, 4).
+-define(HCI_AUTH, 5).
+-define(HCI_ENCRYPT, 6).
+-define(HCI_INQUIRY, 7).
+-define(HCI_RAW, 8).
+
+%% LE address type
+-define(LE_PUBLIC_ADDRESS, 16#00).
+-define(LE_RANDOM_ADDRESS, 16#01).
+
+%% HCI Packet types
+-define(HCI_2DH1, 16#0002).
+-define(HCI_3DH1, 16#0004).
+-define(HCI_DM1, 16#0008).
+-define(HCI_DH1, 16#0010).
+-define(HCI_2DH3, 16#0100).
+-define(HCI_3DH3, 16#0200).
+-define(HCI_DM3, 16#0400).
+-define(HCI_DH3, 16#0800).
+-define(HCI_2DH5, 16#1000).
+-define(HCI_3DH5, 16#2000).
+-define(HCI_DM5, 16#4000).
+-define(HCI_DH5, 16#8000).
+
+-define(HCI_HV1, 16#0020).
+-define(HCI_HV2, 16#0040).
+-define(HCI_HV3, 16#0080).
+
+-define(HCI_EV3, 16#0008).
+-define(HCI_EV4, 16#0010).
+-define(HCI_EV5, 16#0020).
+-define(HCI_2EV3, 16#0040).
+-define(HCI_3EV3, 16#0080).
+-define(HCI_2EV5, 16#0100).
+-define(HCI_3EV5, 16#0200).
+
+-define(SCO_PTYPE_MASK, (?HCI_HV1 bor ?HCI_HV2 bor ?HCI_HV3)).
+-define(ACL_PTYPE_MASK, (?HCI_DM1 bor ?HCI_DH1 bor ?HCI_DM3 bor ?HCI_DH3 bor ?HCI_DM5 bor ?HCI_DH5)).
+
+%% HCI controller types
+-define(HCI_BREDR, 16#00).
+-define(HCI_AMP, 16#01).
+
+
+%% HCI bus types
+-define(HCI_VIRTUAL, 0).
+-define(HCI_USB, 1).
+-define(HCI_PCCARD, 2).
+-define(HCI_UART, 3).
+-define(HCI_RS232, 4).
+-define(HCI_PCI, 5).
+-define(HCI_SDIO, 6).
+
+%% Link policies
+-define(HCI_LP_RSWITCH, 16#0001).
+-define(HCI_LP_HOLD, 16#0002).
+-define(HCI_LP_SNIFF, 16#0004).
+-define(HCI_LP_PARK, 16#0008).
+
+%% Link mode
+-define(HCI_LM_ACCEPT, 16#8000).
+-define(HCI_LM_MASTER, 16#0001).
+-define(HCI_LM_AUTH, 16#0002).
+-define(HCI_LM_ENCRYPT, 16#0004).
+-define(HCI_LM_TRUSTED, 16#0008).
+-define(HCI_LM_RELIABLE, 16#0010).
+-define(HCI_LM_SECURE, 16#0020).
+
+%% Link Key types
+-define(HCI_LK_COMBINATION, 16#00).
+-define(HCI_LK_LOCAL_UNIT, 16#01).
+-define(HCI_LK_REMOTE_UNIT, 16#02).
+-define(HCI_LK_DEBUG_COMBINATION, 16#03).
+-define(HCI_LK_UNAUTH_COMBINATION, 16#04).
+-define(HCI_LK_AUTH_COMBINATION, 16#05).
+-define(HCI_LK_CHANGED_COMBINATION, 16#06).
+-define(HCI_LK_INVALID, 16#FF).
+
+
+%% ACL flags
+-define(ACL_START_NO_FLUSH, 16#00).
+-define(ACL_CONT, 16#01).
+-define(ACL_START, 16#02).
+-define(ACL_ACTIVE_BCAST, 16#04).
+-define(ACL_PICO_BCAST, 16#08).
+
+%% Baseband links
+-define(SCO_LINK, 16#00).
+-define(ACL_LINK, 16#01).
+-define(ESCO_LINK, 16#02).
+
+%% LMP features
+-define(LMP_3SLOT, 16#01).
+-define(LMP_5SLOT, 16#02).
+-define(LMP_ENCRYPT, 16#04).
+-define(LMP_SOFFSET, 16#08).
+-define(LMP_TACCURACY, 16#10).
+-define(LMP_RSWITCH, 16#20).
+-define(LMP_HOLD, 16#40).
+-define(LMP_SNIFF, 16#80).
+
+-define(LMP_PARK, 16#01).
+-define(LMP_RSSI, 16#02).
+-define(LMP_QUALITY, 16#04).
+-define(LMP_SCO, 16#08).
+-define(LMP_HV2, 16#10).
+-define(LMP_HV3, 16#20).
+-define(LMP_ULAW, 16#40).
+-define(LMP_ALAW, 16#80).
+
+-define(LMP_CVSD, 16#01).
+-define(LMP_PSCHEME, 16#02).
+-define(LMP_PCONTROL, 16#04).
+-define(LMP_TRSP_SCO, 16#08).
+-define(LMP_BCAST_ENC, 16#80).
+
+-define(LMP_EDR_ACL_2M, 16#02).
+-define(LMP_EDR_ACL_3M, 16#04).
+-define(LMP_ENH_ISCAN, 16#08).
+-define(LMP_ILACE_ISCAN, 16#10).
+-define(LMP_ILACE_PSCAN, 16#20).
+-define(LMP_RSSI_INQ, 16#40).
+-define(LMP_ESCO, 16#80).
+
+-define(LMP_EV4, 16#01).
+-define(LMP_EV5, 16#02).
+-define(LMP_AFH_CAP_SLV, 16#08).
+-define(LMP_AFH_CLS_SLV, 16#10).
+-define(LMP_NO_BREDR, 16#20).
+-define(LMP_LE, 16#40).
+-define(LMP_EDR_3SLOT, 16#80).
+
+-define(LMP_EDR_5SLOT, 16#01).
+-define(LMP_SNIFF_SUBR, 16#02).
+-define(LMP_PAUSE_ENC, 16#04).
+-define(LMP_AFH_CAP_MST, 16#08).
+-define(LMP_AFH_CLS_MST, 16#10).
+-define(LMP_EDR_ESCO_2M, 16#20).
+-define(LMP_EDR_ESCO_3M, 16#40).
+-define(LMP_EDR_3S_ESCO, 16#80).
+
+-define(LMP_EXT_INQ, 16#01).
+-define(LMP_LE_BREDR, 16#02).
+-define(LMP_SIMPLE_PAIR, 16#08).
+-define(LMP_ENCAPS_PDU, 16#10).
+-define(LMP_ERR_DAT_REP, 16#20).
+-define(LMP_NFLUSH_PKTS, 16#40).
+
+-define(LMP_LSTO, 16#01).
+-define(LMP_INQ_TX_PWR, 16#02).
+-define(LMP_EPC, 16#04).
+-define(LMP_EXT_FEAT, 16#80).
+
+%% Extended LMP features
+-define(LMP_HOST_SSP, 16#01).
+-define(LMP_HOST_LE, 16#02).
+-define(LMP_HOST_LE_BREDR, 16#04).
+
+%% Scan flags used by HCISETSCAN/WRITE_SCAN_ENABLE
+-define(SCAN_DISABLED, 16#00).
+-define(SCAN_INQUIRY, 16#01).
+-define(SCAN_PAGE, 16#02).
+
+
+-record(hci_dev_info,
+ {
+ dev_id::uint16_t(),
+ name::binary(), %% 8 bytes
+ bdaddr::bdaddr_t(), %% 6 bytes [2 pad bytes]
+ flags::uint32_t(),
+ type::uint8_t(),
+ features::binary(), %% 64 bits
+
+ pkt_type::uint32_t(),
+ link_policy::uint32_t(),
+ link_mode::uint32_t(),
+ acl_mtu::uint16_t(),
+ acl_pkts::uint16_t(),
+ sco_mtu::uint16_t(),
+ sco_pkts::uint16_t(),
+ stat::#hci_dev_stats{}
+ }).
+
+-record(hci_conn_info,
+ {
+ handle::uint16_t(),
+ bdaddr::bdaddr_t(),
+ type::uint8_t(),
+ out::uint8_t(),
+ state::uint16_t(),
+ link_mode::uint32_t()
+ }).
+
+-record(hci_filter,
+ {
+ type_mask :: uint32_t(),
+ event_mask :: uint64_t(), %% encoded as 2*uint32_t()
+ opcode :: uint16_t()
+ }).
+
+
+-define(HCI_FLT_TYPE_BITS, 31).
+-define(HCI_FLT_EVENT_BITS, 63).
+-define(HCI_FLT_OGF_BITS, 63).
+-define(HCI_FLT_OCF_BITS, 127).
+
+-type hci_socket_t() :: port().
+-type hci_devid_t() :: integer().
+-type posix() :: atom().
+-type level_t() :: boolean() | debug | info | notice | warning | error |
+ critical | alert | emergency | none.
+
+
+-endif.
diff --git a/deps/bt/include/obex.hrl b/deps/bt/include/obex.hrl
new file mode 100644
index 0000000..d92019d
--- /dev/null
+++ b/deps/bt/include/obex.hrl
@@ -0,0 +1,146 @@
+%%%---- BEGIN COPYRIGHT -------------------------------------------------------
+%%%
+%%% Copyright (C) 2006 - 2014, Rogvall Invest AB, <tony@rogvall.se>
+%%%
+%%% This software is licensed as described in the file COPYRIGHT, which
+%%% you should have received as part of this distribution. The terms
+%%% are also available at http://www.rogvall.se/docs/copyright.txt.
+%%%
+%%% You may opt to use, copy, modify, merge, publish, distribute and/or sell
+%%% copies of the Software, and permit persons to whom the Software is
+%%% furnished to do so, under the terms of the COPYRIGHT file.
+%%%
+%%% This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY
+%%% KIND, either express or implied.
+%%%
+%%%---- END COPYRIGHT ---------------------------------------------------------
+
+-ifndef(OBEX_HRL).
+-define(OBEX_HRL, true).
+
+-define(OBEX_VERSION, 16#10).
+
+%% 0x - Request codes
+-define(OBEX_CMD_CONNECT, 16#00).
+-define(OBEX_CMD_DISCONNECT, 16#01).
+-define(OBEX_CMD_PUT, 16#02).
+-define(OBEX_CMD_GET, 16#03).
+-define(OBEX_CMD_COMMAND, 16#04).
+-define(OBEX_CMD_SETPATH, 16#05).
+
+%% 1x
+-define(OBEX_RSP_CONTINUE, 16#10).
+-define(OBEX_RSP_SWITCH_PRO, 16#11).
+
+%% 2x
+-define(OBEX_RSP_SUCCESS, 16#20).
+-define(OBEX_RSP_CREATED, 16#21).
+-define(OBEX_RSP_ACCEPTED, 16#22).
+-define(OBEX_RSP_NON_AUTHORITATIVE, 16#23).
+-define(OBEX_RSP_NO_CONTENT, 16#24).
+-define(OBEX_RSP_RESET_CONTENT, 16#25).
+-define(OBEX_RSP_PARTIAL_CONTENT, 16#26).
+
+%% 3x
+-define(OBEX_RSP_MULTIPLE_CHOICES, 16#30).
+-define(OBEX_RSP_MOVED_PERMANENTLY, 16#31).
+-define(OBEX_RSP_MOVED_TEMPORARILY, 16#32).
+-define(OBEX_RSP_SEE_OTHER, 16#33).
+-define(OBEX_RSP_NOT_MODIFIED, 16#34).
+-define(OBEX_RSP_USE_PROXY, 16#35).
+
+%% 4x
+-define(OBEX_RSP_BAD_REQUEST, 16#40).
+-define(OBEX_RSP_UNAUTHORIZED, 16#41).
+-define(OBEX_RSP_PAYMENT_REQUIRED, 16#42).
+-define(OBEX_RSP_FORBIDDEN, 16#43).
+-define(OBEX_RSP_NOT_FOUND, 16#44).
+-define(OBEX_RSP_METHOD_NOT_ALLOWED, 16#45).
+-define(OBEX_RSP_NOT_ACCEPTABLE, 16#46).
+-define(OBEX_RSP_PROXY_AUTH, 16#47).
+-define(OBEX_RSP_REQUEST_TIMEOUT, 16#48).
+-define(OBEX_RSP_CONFLICT, 16#49).
+-define(OBEX_RSP_GONE, 16#4A).
+-define(OBEX_RSP_LENGTH_REQUIRED, 16#4B).
+-define(OBEX_RSP_PRECONDITION_FAILED, 16#4C).
+-define(OBEX_RSP_ENTITY_TOO_LARGE, 16#4D).
+-define(OBEX_RSP_URL_TOO_LARGE, 16#4E).
+-define(OBEX_RSP_UNSUPPORTED_MEDIA, 16#4F).
+
+%% 5x
+-define(OBEX_RSP_INTERNAL_SERVER_ERROR, 16#50).
+-define(OBEX_RSP_NOT_IMPLEMENTED, 16#51).
+-define(OBEX_RSP_BAD_GATEWAY, 16#52).
+-define(OBEX_RSP_SERVICE_UNAVAILABLE, 16#53).
+-define(OBEX_RSP_GATEWAY_TIMEOUT, 16#54).
+-define(OBEX_RSP_HTTP_VERSION_NOT_SUPPORTED, 16#55).
+
+%% 6x
+-define(OBEX_RSP_DATABASE_FULL, 16#60).
+-define(OBEX_RSP_DATABASE_LOCKED, 16#61).
+
+%% 7x - command/response
+-define(OBEX_CMD_ABORT, 16#7f).
+
+-define(TARGET_FOLDER_BROWSING,
+ ?UUID(16#F9EC7BC4,16#953C,16#11D2,16#984E,16#525400DC9E09)).
+-define(TARGET_SYNCML_DM, "SYNCML-DM").
+-define(TARGET_SYNCML_SYNC, "SYNCML-SYNC").
+
+%% NOKIA SYNCML-SYNC ????
+-define(UUID_SYNCML_SYNC,
+ ?UUID(16#00005601,16#0000,16#1000,16#8000,16#0002EE000002)).
+
+%% Min, Max and default transport MTU
+-define(OBEX_DEFAULT_MTU, 1024).
+-define(OBEX_MINIMUM_MTU, 255).
+-define(OBEX_MAXIMUM_MTU, 32768).
+
+-define(OBEX_HDR_MASK, 16#C0).
+-define(OBEX_V0, 2#00).
+-define(OBEX_Vn, 2#01).
+-define(OBEX_I1, 2#10).
+-define(OBEX_I4, 2#11).
+
+%% Standard headers
+
+%% null terminated values
+-define(OBEX_HDR_NAME, 16#01). %% Name of the object.
+-define(OBEX_HDR_DESCRIPTION, 16#05). %% Description of object
+
+-define(OBEX_HDR_RESERVED_RANGE_START, 16#10).
+-define(OBEX_HDR_RESERVED_RANGE_END, 16#2F).
+
+-define(OBEX_HDR_USER_DEFINED_RANGE_START, 16#30).
+-define(OBEX_HDR_USER_DEFINED_RANGE_END, 16#3F).
+
+%% bytes sequence values
+-define(OBEX_HDR_TYPE, 16#42). %% Type of the object
+-define(OBEX_HDR_TIME, 16#44). %% Last modification time of (ISO8601)
+-define(OBEX_HDR_TARGET, 16#46). %% Identifies the target for the object
+-define(OBEX_HDR_HTTP, 16#47). %% Data part of the object
+-define(OBEX_HDR_BODY, 16#48). %% Data part of the object
+-define(OBEX_HDR_BODY_END, 16#49). %% Last data part of the object
+-define(OBEX_HDR_WHO, 16#4a). %% Identifies the sender of the object
+-define(OBEX_HDR_APPARAM, 16#4c). %% Application parameters
+-define(OBEX_HDR_AUTHCHAL, 16#4d). %% Authentication challenge
+-define(OBEX_HDR_AUTHRESP, 16#4e). %% Authentication response
+-define(OBEX_HDR_OBJCLASS, 16#4f). %% OBEX Object class of object
+
+%% 4 bytes values
+-define(OBEX_HDR_COUNT, 16#c0). %% Number of objects (used by connect)
+-define(OBEX_HDR_CONNECTION_ID, 16#cb). %% Connection identifier
+-define(OBEX_HDR_LENGTH, 16#c3). %% Total lenght of object
+-define(OBEX_HDR_TIME2, 16#C4). %% Deprecated use HDR_TIME instead
+
+%% vCard charsets
+-define(OBEX_CHARSET_STRING_ISO88591, "CHARSET=ISO-8859-1").
+-define(OBEX_CHARSET_STRING_UTF8, "UTF-8").
+
+%% vEvent encodings
+-define(OBEX_ENCODING_STRING_QUOTED_PRINTABLE, "QUOTED-PRINTABLE").
+-define(OBEX_ENCODING_STRING_BASE64, "BASE-64").
+-define(OBEX_ENCODING_STRING_8BIT, "8BIT").
+
+
+-endif.
diff --git a/deps/bt/include/sdp.hrl b/deps/bt/include/sdp.hrl
new file mode 100644
index 0000000..830c2d7
--- /dev/null
+++ b/deps/bt/include/sdp.hrl
@@ -0,0 +1,262 @@
+%%%---- BEGIN COPYRIGHT -------------------------------------------------------
+%%%
+%%% Copyright (C) 2006 - 2014, Rogvall Invest AB, <tony@rogvall.se>
+%%%
+%%% This software is licensed as described in the file COPYRIGHT, which
+%%% you should have received as part of this distribution. The terms
+%%% are also available at http://www.rogvall.se/docs/copyright.txt.
+%%%
+%%% You may opt to use, copy, modify, merge, publish, distribute and/or sell
+%%% copies of the Software, and permit persons to whom the Software is
+%%% furnished to do so, under the terms of the COPYRIGHT file.
+%%%
+%%% This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY
+%%% KIND, either express or implied.
+%%%
+%%%---- END COPYRIGHT ---------------------------------------------------------
+-ifndef(SDP_HRL).
+-define(SDP_HRL, true).
+
+%% ATTRIBUTES
+-define(ATTR_ServiceRecordHandle, 16#0000).
+-define(ATTR_ServiceClassIDList, 16#0001).
+-define(ATTR_ServiceRecordState, 16#0002).
+-define(ATTR_ServiceID, 16#0003).
+-define(ATTR_ProtocolDescriptorList, 16#0004).
+-define(ATTR_BrowseGroupList, 16#0005).
+-define(ATTR_LanguageBaseAttributeIDList, 16#0006).
+-define(ATTR_ServiceInfoTimeToLive, 16#0007).
+-define(ATTR_ServiceAvailability, 16#0008).
+-define(ATTR_BluetoothProfileDescriptorList, 16#0009).
+-define(ATTR_DocumentationURL, 16#000A).
+-define(ATTR_ClientExecutableURL, 16#000B).
+-define(ATTR_IconURL, 16#000C).
+-define(ATTR_AdditionalProtocolsDescriptorList, 16#000D).
+
+%% ATTRIBUTE
+-define(ATTR_ServiceName, 16#0000).
+-define(ATTR_ServiceDescription, 16#0001).
+-define(ATTR_ProviderName, 16#0002).
+
+%% SDP
+-define(ATTR_SDP_VersionNumberList, 16#0200).
+-define(ATTR_SDP_ServiceDatabaseState, 16#0201).
+
+%% PAN
+-define(ATTR_PAN_IPSubnet, 16#0200).
+
+%% BIP
+-define(ATTR_BIP_GoepL2capPsm, 16#0200).
+-define(ATTR_BIP_SupportedCapabilities, 16#0310).
+-define(ATTR_BIP_SupportedFeatures, 16#0311).
+-define(ATTR_BIP_SupportedFunctions, 16#0312).
+-define(ATTR_BIP_TotalImagingDataCapacity, 16#0313).
+
+%% AVRCP
+-define(ATTR_ACRCP_SupportedFeatures, 16#0311).
+
+%% BPP - basic printing profile, Direct Printing(1119), ReferencePrinting(1119),
+%% DirectPrintingReferenceObjectsService(1120),ReflectedUI(1121),PrintingStatus(1123)
+
+%% UUID Def's
+
+%% PROTOCOLS
+-define(UUID_SDP, <<16#0001:16>>).
+-define(UUID_UDP, <<16#0002:16>>).
+-define(UUID_RFCOMM, <<16#0003:16>>).
+-define(UUID_TCP, <<16#0004:16>>).
+-define(UUID_TCS_BIN, <<16#0005:16>>).
+-define(UUID_TCS_AT, <<16#0006:16>>).
+-define(UUID_OBEX, <<16#0008:16>>).
+-define(UUID_IP, <<16#0009:16>>).
+-define(UUID_FTP, <<16#000A:16>>).
+-define(UUID_HTTP, <<16#000C:16>>).
+-define(UUID_WSP, <<16#000E:16>>).
+-define(UUID_BNEP, <<16#000F:16>>).
+-define(UUID_UPNP, <<16#0010:16>>).
+-define(UUID_HIDP, <<16#0011:16>>).
+-define(UUID_HCRP_CTRL, <<16#0012:16>>).
+-define(UUID_HCRP_DATA, <<16#0014:16>>).
+-define(UUID_HCRP_NOTE, <<16#0016:16>>).
+-define(UUID_AVCTP, <<16#0017:16>>).
+-define(UUID_AVDTP, <<16#0019:16>>).
+-define(UUID_CMPT, <<16#001B:16>>).
+-define(UUID_UDI, <<16#001D:16>>).
+-define(UUID_MCAP_CTRL, <<16#001E:16>>).
+-define(UUID_MCAP_DATA, <<16#001F:16>>).
+-define(UUID_L2CAP, <<16#0100:16>>).
+
+%% SERVICE Classes
+-define(UUID_ServiceDiscoveryServer, <<16#1000:16>>).
+-define(UUID_BrowseGroupDescriptor, <<16#1001:16>>).
+-define(UUID_PublicBrowseGroup, <<16#1002:16>>).
+-define(UUID_SerialPort, <<16#1101:16>>).
+-define(UUID_LANAccessUsingPPP, <<16#1102:16>>).
+-define(UUID_DialupNetworking, <<16#1103:16>>).
+-define(UUID_IrMCSync, <<16#1104:16>>).
+-define(UUID_OBEXObjectPush, <<16#1105:16>>).
+-define(UUID_OBEXFileTransfer, <<16#1106:16>>).
+-define(UUID_IrMCSyncCommand, <<16#1107:16>>).
+-define(UUID_Headset, <<16#1108:16>>).
+-define(UUID_CordlessTelephony, <<16#1109:16>>).
+-define(UUID_AudioSource, <<16#110A:16>>).
+-define(UUID_AudioSink, <<16#110B:16>>).
+-define(UUID_AVRemoteControlTarget, <<16#110C:16>>).
+-define(UUID_AdvancedAudioDistribution, <<16#110D:16>>).
+-define(UUID_AVRemoteControl, <<16#110E:16>>).
+-define(UUID_VideoConferencing, <<16#110F:16>>). %% fixme RemoteControlController?
+-define(UUID_Intercom, <<16#1110:16>>).
+-define(UUID_Fax, <<16#1111:16>>).
+-define(UUID_HeadsetAudioGateway, <<16#1112:16>>).
+-define(UUID_WAP, <<16#1113:16>>).
+-define(UUID_WAPClient, <<16#1114:16>>).
+-define(UUID_PANU, <<16#1115:16>>).
+-define(UUID_NAP, <<16#1116:16>>).
+-define(UUID_GN, <<16#1117:16>>).
+-define(UUID_DirectPrinting, <<16#1118:16>>).
+-define(UUID_ReferencePrinting, <<16#1119:16>>).
+-define(UUID_Imaging, <<16#111A:16>>).
+-define(UUID_ImagingResponder, <<16#111B:16>>).
+-define(UUID_ImagingAutomaticArchive, <<16#111C:16>>).
+-define(UUID_ImagingReferencedObjects, <<16#111D:16>>).
+-define(UUID_Handsfree, <<16#111E:16>>).
+-define(UUID_HandsfreeAudioGateway, <<16#111F:16>>).
+-define(UUID_DirectPrintingReferenceObjectsService, <<16#1120:16>>).
+-define(UUID_ReflectedUI, <<16#1121:16>>).
+-define(UUID_BasicPrinting, <<16#1122:16>>).
+-define(UUID_PrintingStatus, <<16#1123:16>>).
+-define(UUID_HumanInterfaceDeviceService, <<16#1124:16>>).
+-define(UUID_HardcopyCableReplacement, <<16#1125:16>>).
+-define(UUID_HCR_Print, <<16#1126:16>>).
+-define(UUID_HCR_Scan, <<16#1127:16>>).
+-define(UUID_CommonISDNAccess, <<16#1128:16>>).
+-define(UUID_VideoConferencingGW, <<16#1129:16>>).
+-define(UUID_UDI_MT, <<16#112A:16>>).
+-define(UUID_UDI_TA, <<16#112B:16>>).
+-define(UUID_Audio_Video, <<16#112C:16>>).
+-define(UUID_SIM_Access, <<16#112D:16>>).
+-define(UUID_PhonebookAccess_PCE, <<16#112E:16>>). %% Phonebook Access Profile
+-define(UUID_PhonebookAccess_PSE, <<16#112F:16>>). %%Phonebook Access Profile
+-define(UUID_PhonebookAccess, <<16#1130:16>>). %% Phonebook Access Profile
+-define(UUID_Headset_HS, <<16#1131:16>>). %% Headset Profile (HSP)
+-define(UUID_Message_Access_Server, <<16#1132>>). %% Message Access Profile (MAP)
+-define(UUID_Message_Notification_Server, <<16#1133>>). %% Message Access Profile (MAP)
+-define(UUID_Message_Access_Profile, <<16#1134>>). %% Message Access Profile (MAP)
+-define(UUID_GNSS, <<16#1135>>). %% Global Navigation Satellite System Profile (GNSS)
+-define(UUID_GNSS_Server, <<16#1136>>). %% Global Navigation Satellite System Profile (GNSS)
+-define(UUID_3D_Display, <<16#1137>>). %% 3D Synchronization Profile (3DSP)
+-define(UUID_3D_Glasses, <<16#1138>>). %% 3D Synchronization Profile (3DSP)
+-define(UUID_3D_Synchronization, <<16#1139>>). %% 3D Synchronization Profile (3DSP)
+-define(UUID_MPS_Profile, <<16#113A>>). %% Multi-Profile Specification (MPS)
+-define(UUID_MPS_SC, <<16#113B>>). %% Multi-Profile Specification (MPS)
+-define(UUID_CTN_Access_Service, <<16#113C>>). %% Calendar, Task, and Notes (CTN)
+-define(UUID_CTN_Notification_Service, <<16#113D>>). %% Calendar Tasks and Notes (CTN)
+-define(UUID_CTN_Profile, <<16#113E>>). %% Calendar Tasks and Notes (CTN) Profile
+-define(UUID_PnPInformation, <<16#1200:16>>).
+-define(UUID_GenericNetworking, <<16#1201:16>>).
+-define(UUID_GenericFileTransfer, <<16#1202:16>>).
+-define(UUID_GenericAudio, <<16#1203:16>>).
+-define(UUID_GenericTelephony, <<16#1204:16>>).
+-define(UUID_UPNP_Service, <<16#1205:16>>).
+-define(UUID_UPNP_IP_Service, <<16#1206:16>>).
+-define(UUID_ESDP_UPNP_IP_PAN, <<16#1300:16>>).
+-define(UUID_ESDP_UPNP_IP_LAP, <<16#1301:16>>).
+-define(UUID_ESDP_UPNP_L2CAP, <<16#1302:16>>).
+-define(UUID_VideoSource, <<16#1303:16>>).
+-define(UUID_VideoSink, <<16#1304:16>>).
+-define(UUID_VideoDistribution, <<16#1305:16>>).
+-define(UUID_HDP, <<16#1400:16>>). %% Health Device Profile
+-define(UUID_HDP_Source, <<16#1401:16>>). %% Health Device Profile (HDP)
+-define(UUID_HDP_Sink, <<16#1402:16>>). %% Health Device Profile (HDP)
+
+-define(UUID_SyncMLServer,
+ <<16#00000001:32,16#0000:16,16#1000:16,16#8000:16,16#0002EE000002:48>>).
+-define(UUID_SyncMLClient,
+ <<16#00000002:32,16#0000:16,16#1000:16,16#8000:16,16#0002EE000002:48>>).
+-define(UUID_SyncMLDMServer,
+ <<16#00000003:32,16#0000:16,16#1000:16,16#8000:16,16#0002EE000002:48>>).
+-define(UUID_SyncMLDMClient,
+ <<16#00000004:32,16#0000:16,16#1000:16,16#8000:16,16#0002EE000002:48>>).
+
+%% NOKIA
+-define(UUID_NokiaSyncMLServer,
+ ?UUID(16#00005601,16#0000,16#1000,16#8000,16#0002EE000001)).
+-define(UUID_NokiaObexPcSuiteServices,
+ ?UUID(16#00005005,16#0000,16#1000,16#8000,16#0002EE000001)).
+
+%% 00005557-0000-1000-8000-0002EE000001 What is this? E60!
+
+%% SONY-ERICSSON
+-define(UUID_Z520a_SyncMLClient,
+ <<16#00001111:32,16#0000:16,16#0000:16,16#0000:16,16#000222000000:48>>).
+
+
+-define(LANGUAGE(L1,L2), (((L1) bsl 8) + (L2))).
+
+-define(ENCODING_UTF8, 106).
+
+%%
+%% SDP Request/Response codes
+%%
+-define(SDP_ErrorResponse, 16#01).
+-define(SDP_ServiceSearchRequest, 16#02).
+-define(SDP_ServiceSearchResponse, 16#03).
+-define(SDP_ServiceAttributeRequest, 16#04).
+-define(SDP_ServiceAttributeResponse, 16#05).
+-define(SDP_ServiceSearchAttributeRequest, 16#06).
+-define(SDP_ServiceSearchAttributeResponse, 16#07).
+
+-define(SDP_ErrorInvalidVersion, 16#0001).
+-define(SDP_ErrorInvalidHandle, 16#0002).
+-define(SDP_ErrorInvalidSyntax, 16#0003).
+-define(SDP_ErrorInvaludPduSize, 16#0004).
+-define(SDP_ErrorInvalidContinuation, 16#0005).
+-define(SDP_ErrorInsufficientResources, 16#0006).
+
+
+-record(sdpErrorResponse,
+ {
+ errorCode,
+ errorInfo
+ }).
+
+-record(sdpServiceSearchRequest,
+ {
+ serviceSearchPattern,
+ maximumServiceRecordCount
+ }).
+
+-record(sdpServiceSearchResponse,
+ {
+ totalServiceRecordCount,
+ currentServiceRecordCount,
+ serviceRecordHandleList
+ }).
+
+-record(sdpServiceAttributeRequest,
+ {
+ serviceRecordHandle,
+ maximumAttributeByteCount,
+ attributeIDList
+ }).
+
+-record(sdpServiceAttributeResponse,
+ {
+ attributeListByteCount,
+ attributeList
+ }).
+
+-record(sdpServiceSearchAttributeRequest,
+ {
+ serviceSearchPattern,
+ maximumAttributeByteCount,
+ attributeIDList
+ }).
+
+-record(sdpServiceSearchAttributeResponse,
+ {
+ attributeListByteCount,
+ attributeList
+ }).
+
+-endif.
diff --git a/deps/bt/include/uuid.hrl b/deps/bt/include/uuid.hrl
new file mode 100644
index 0000000..b7f4bd0
--- /dev/null
+++ b/deps/bt/include/uuid.hrl
@@ -0,0 +1,52 @@
+%%%---- BEGIN COPYRIGHT -------------------------------------------------------
+%%%
+%%% Copyright (C) 2007 - 2014, Rogvall Invest AB, <tony@rogvall.se>
+%%%
+%%% This software is licensed as described in the file COPYRIGHT, which
+%%% you should have received as part of this distribution. The terms
+%%% are also available at http://www.rogvall.se/docs/copyright.txt.
+%%%
+%%% You may opt to use, copy, modify, merge, publish, distribute and/or sell
+%%% copies of the Software, and permit persons to whom the Software is
+%%% furnished to do so, under the terms of the COPYRIGHT file.
+%%%
+%%% This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY
+%%% KIND, either express or implied.
+%%%
+%%%---- END COPYRIGHT ---------------------------------------------------------
+%%
+%% Some UUID macros
+%%
+-ifndef(UUID_HRL).
+-define(UUID_HRL, true).
+
+-define(is_uuid(UUID), is_binary(UUID), size(UUID) == 16).
+
+-define(is_ieee_802(A),
+ is_tuple((A)), size((A)) == 6,
+ ((element(1,(A)) bor element(2,(A)) bor element(3,(A)) bor
+ element(4,(A)) bor element(5,(A)) bor element(6,(A))) band
+ -16#100 == 0)).
+
+%% UUID all components
+-define(UUIDx(Ver,TimeLow,TimeMid,TimeHi,Var,Clock,Node),
+ <<(TimeLow):32,(TimeMid):16,(Ver):4,(TimeHi):12,
+ (Var):2,(Clock):14,(Node):48>>).
+
+%% UUID major components - for formating etc
+-define(UUID(TimeLow,TimeMid,TimeHigh,Clock,Node),
+ <<TimeLow:32,TimeMid:16,TimeHigh:16, Clock:16, Node:48>>).
+
+-define(NameSpace_DNS,
+ ?UUID(16#6ba7b810, 16#9dad, 16#11d1, 16#80b4, 16#00c04fd430c8)).
+
+-define(NameSpace_URL,
+ ?UUID(16#6ba7b811, 16#9dad, 16#11d1, 16#80b4, 16#00c04fd430c8)).
+
+-define(NameSpace_OID,
+ ?UUID(16#6ba7b812, 16#9dad, 16#11d1, 16#80b4, 16#00c04fd430c8)).
+
+-define(NameSpace_X500,
+ ?UUID(16#6ba7b814, 16#9dad, 16#11d1, 16#80b4, 16#00c04fd430c8)).
+
+-endif.
diff --git a/deps/bt/priv/.gitignore b/deps/bt/priv/.gitignore
new file mode 100644
index 0000000..4b2ef59
--- /dev/null
+++ b/deps/bt/priv/.gitignore
@@ -0,0 +1,2 @@
+bt
+*.so
diff --git a/deps/bt/priv/hci.def b/deps/bt/priv/hci.def
new file mode 100644
index 0000000..f63e1e6
--- /dev/null
+++ b/deps/bt/priv/hci.def
@@ -0,0 +1,2250 @@
+%% -*- erlang -*-
+
+{define, 'HCI_MAX_DEV', 16}.
+
+{define, 'HCI_MAX_ACL_SIZE', 1024}.
+{define, 'HCI_MAX_SCO_SIZE', 255}.
+{define, 'HCI_MAX_EVENT_SIZE', 260}.
+{define, 'HCI_MAX_FRAME_SIZE', "(?HCI_MAX_ACL_SIZE + 4)"}.
+
+%% HCI dev events
+{define, 'HCI_DEV_REG', 1}.
+{define, 'HCI_DEV_UNREG', 2}.
+{define, 'HCI_DEV_UP', 3}.
+{define, 'HCI_DEV_DOWN', 4}.
+{define, 'HCI_DEV_SUSPEND', 5}.
+{define, 'HCI_DEV_RESUME', 6}.
+
+%% HCI Error codes
+{define, 'HCI_UNKNOWN_COMMAND', 16#01}.
+{define, 'HCI_NO_CONNECTION', 16#02}.
+{define, 'HCI_HARDWARE_FAILURE', 16#03}.
+{define, 'HCI_PAGE_TIMEOUT', 16#04}.
+{define, 'HCI_AUTHENTICATION_FAILURE', 16#05}.
+{define, 'HCI_PIN_OR_KEY_MISSING', 16#06}.
+{define, 'HCI_MEMORY_FULL', 16#07}.
+{define, 'HCI_CONNECTION_TIMEOUT', 16#08}.
+{define, 'HCI_MAX_NUMBER_OF_CONNECTIONS', 16#09}.
+{define, 'HCI_MAX_NUMBER_OF_SCO_CONNECTIONS', 16#0a}.
+{define, 'HCI_ACL_CONNECTION_EXISTS', 16#0b}.
+{define, 'HCI_COMMAND_DISALLOWED', 16#0c}.
+{define, 'HCI_REJECTED_LIMITED_RESOURCES', 16#0d}.
+{define, 'HCI_REJECTED_SECURITY', 16#0e}.
+{define, 'HCI_REJECTED_PERSONAL', 16#0f}.
+{define, 'HCI_HOST_TIMEOUT', 16#10}.
+{define, 'HCI_UNSUPPORTED_FEATURE', 16#11}.
+{define, 'HCI_INVALID_PARAMETERS', 16#12}.
+{define, 'HCI_OE_USER_ENDED_CONNECTION', 16#13}.
+{define, 'HCI_OE_LOW_RESOURCES', 16#14}.
+{define, 'HCI_OE_POWER_OFF', 16#15}.
+{define, 'HCI_CONNECTION_TERMINATED', 16#16}.
+{define, 'HCI_REPEATED_ATTEMPTS', 16#17}.
+{define, 'HCI_PAIRING_NOT_ALLOWED', 16#18}.
+{define, 'HCI_UNKNOWN_LMP_PDU', 16#19}.
+{define, 'HCI_UNSUPPORTED_REMOTE_FEATURE', 16#1a}.
+{define, 'HCI_SCO_OFFSET_REJECTED', 16#1b}.
+{define, 'HCI_SCO_INTERVAL_REJECTED', 16#1c}.
+{define, 'HCI_AIR_MODE_REJECTED', 16#1d}.
+{define, 'HCI_INVALID_LMP_PARAMETERS', 16#1e}.
+{define, 'HCI_UNSPECIFIED_ERROR', 16#1f}.
+{define, 'HCI_UNSUPPORTED_LMP_PARAMETER_VALUE', 16#20}.
+{define, 'HCI_ROLE_CHANGE_NOT_ALLOWED', 16#21}.
+{define, 'HCI_LMP_RESPONSE_TIMEOUT', 16#22}.
+{define, 'HCI_LMP_ERROR_TRANSACTION_COLLISION', 16#23}.
+{define, 'HCI_LMP_PDU_NOT_ALLOWED', 16#24}.
+{define, 'HCI_ENCRYPTION_MODE_NOT_ACCEPTED', 16#25}.
+{define, 'HCI_UNIT_LINK_KEY_USED', 16#26}.
+{define, 'HCI_QOS_NOT_SUPPORTED', 16#27}.
+{define, 'HCI_INSTANT_PASSED', 16#28}.
+{define, 'HCI_PAIRING_NOT_SUPPORTED', 16#29}.
+{define, 'HCI_TRANSACTION_COLLISION', 16#2a}.
+{define, 'HCI_QOS_UNACCEPTABLE_PARAMETER', 16#2c}.
+{define, 'HCI_QOS_REJECTED', 16#2d}.
+{define, 'HCI_CLASSIFICATION_NOT_SUPPORTED', 16#2e}.
+{define, 'HCI_INSUFFICIENT_SECURITY', 16#2f}.
+{define, 'HCI_PARAMETER_OUT_OF_RANGE', 16#30}.
+{define, 'HCI_ROLE_SWITCH_PENDING', 16#32}.
+{define, 'HCI_SLOT_VIOLATION', 16#34}.
+{define, 'HCI_ROLE_SWITCH_FAILED', 16#35}.
+{define, 'HCI_EIR_TOO_LARGE', 16#36}.
+{define, 'HCI_SIMPLE_PAIRING_NOT_SUPPORTED', 16#37}.
+{define, 'HCI_HOST_BUSY_PAIRING', 16#38}.
+
+%% Types
+{type, bdaddr_t, {uint8_t,6}}.
+{type, hci_qos_t, {uint8_t,17}}.
+%% {type, dev_class_t, {uint8_t,3}}.
+%% {type, lap_t, {uint8_t,3}}.
+
+
+%% ----- HCI Commands -----
+
+%% Link Control
+{define, 'OGF_LINK_CTL', 16#01}.
+
+{define, 'OCF_INQUIRY', 16#0001}.
+{struct,inquiry_cp,
+ [
+ {{uint8_t,3}, lap},
+ {uint8_t, length}, %% 1.28s units
+ {uint8_t, num_rsp}
+ ]}.
+{define, 'INQUIRY_CP_SIZE', 5}.
+
+{struct, status_bdaddr_rp,
+ [
+ {uint8_t, status},
+ {bdaddr_t, bdaddr}
+]}.
+{define, 'STATUS_BDADDR_RP_SIZE', 7}.
+
+{define, 'OCF_INQUIRY_CANCEL', 16#0002}.
+
+{define, 'OCF_PERIODIC_INQUIRY', 16#0003}.
+{struct, periodic_inquiry_cp,
+ [
+ {uint16_t, max_period}, %% 1.28s units
+ {uint16_t, min_period}, %% 1.28s units
+ {{uint8_t,3}, lap},
+ {uint8_t, length}, %% 1.28s units
+ {uint8_t, num_rsp}
+]}.
+{define, 'PERIODIC_INQUIRY_CP_SIZE', 9}.
+
+{define, 'OCF_EXIT_PERIODIC_INQUIRY', 16#0004}.
+
+{define, 'OCF_CREATE_CONN', 16#0005}.
+{struct, create_conn_cp,
+ [
+ {bdaddr_t, bdaddr},
+ {uint16_t, pkt_type},
+ {uint8_t, pscan_rep_mode},
+ {uint8_t, pscan_mode},
+ {uint16_t, clock_offset},
+ {uint8_t, role_switch}
+]}.
+{define, 'CREATE_CONN_CP_SIZE', 13}.
+
+{define, 'OCF_DISCONNECT', 16#0006}.
+{struct, disconnect_cp,
+ [
+ {uint16_t, handle},
+ {uint8_t, reason}
+]}.
+{define, 'DISCONNECT_CP_SIZE', 3}.
+
+{define, 'OCF_ADD_SCO', 16#0007}.
+{struct, add_sco_cp,
+ [
+ {uint16_t, handle},
+ {uint16_t, pkt_type}
+]}.
+{define, 'ADD_SCO_CP_SIZE', 4}.
+
+{define, 'OCF_CREATE_CONN_CANCEL', 16#0008}.
+{struct, create_conn_cancel_cp,
+ [
+ {bdaddr_t, bdaddr}
+]}.
+{define, 'CREATE_CONN_CANCEL_CP_SIZE', 6}.
+
+{define, 'OCF_ACCEPT_CONN_REQ', 16#0009}.
+{struct, accept_conn_req_cp,
+ [
+ {bdaddr_t, bdaddr},
+ {uint8_t, role}
+]}.
+{define, 'ACCEPT_CONN_REQ_CP_SIZE', 7}.
+
+{define, 'OCF_REJECT_CONN_REQ', 16#000A}.
+{struct, reject_conn_req_cp,
+ [
+ {bdaddr_t, bdaddr},
+ {uint8_t, reason}
+]}.
+{define, 'REJECT_CONN_REQ_CP_SIZE', 7}.
+
+{define, 'OCF_LINK_KEY_REPLY', 16#000B}.
+{struct, link_key_reply_cp,
+ [
+ {bdaddr_t, bdaddr},
+ {{uint8_t,16}, link_key}
+]}.
+{define, 'LINK_KEY_REPLY_CP_SIZE', 22}.
+
+{define, 'OCF_LINK_KEY_NEG_REPLY', 16#000C}.
+{struct, link_key_neg_reply_cp,
+ [
+ {bdaddr_t, bdaddr}
+ ]}.
+{define, 'LINK_KEY_NEG_REPLY_CP_SIZE', 6}.
+
+{define, 'OCF_PIN_CODE_REPLY', 16#000D}.
+{struct, pin_code_reply_cp,
+ [
+ {bdaddr_t, bdaddr},
+ {uint8_t, pin_len},
+ {{uint8_t,16}, pin_code}
+]}.
+{define, 'PIN_CODE_REPLY_CP_SIZE', 23}.
+
+{define, 'OCF_PIN_CODE_NEG_REPLY', 16#000E}.
+
+{define, 'OCF_SET_CONN_PTYPE', 16#000F}.
+{struct, set_conn_ptype_cp,
+ [
+ {uint16_t, handle},
+ {uint16_t, pkt_type}
+]}.
+{define, 'SET_CONN_PTYPE_CP_SIZE', 4}.
+
+{define, 'OCF_AUTH_REQUESTED', 16#0011}.
+{struct, auth_requested_cp,
+ [
+ {uint16_t, handle}
+]}.
+{define, 'AUTH_REQUESTED_CP_SIZE', 2}.
+
+{define, 'OCF_SET_CONN_ENCRYPT', 16#0013}.
+{struct, set_conn_encrypt_cp,
+ [
+ {uint16_t, handle},
+ {uint8_t, encrypt}
+]}.
+{define, 'SET_CONN_ENCRYPT_CP_SIZE', 3}.
+
+{define, 'OCF_CHANGE_CONN_LINK_KEY', 16#0015}.
+{struct, change_conn_link_key_cp,
+ [
+ {uint16_t, handle}
+]}.
+{define, 'CHANGE_CONN_LINK_KEY_CP_SIZE', 2}.
+
+{define, 'OCF_MASTER_LINK_KEY', 16#0017}.
+{struct, master_link_key_cp,
+ [
+ {uint8_t, key_flag}
+]}.
+{define, 'MASTER_LINK_KEY_CP_SIZE', 1}.
+
+{define, 'OCF_REMOTE_NAME_REQ', 16#0019}.
+{struct, remote_name_req_cp,
+ [
+ {bdaddr_t, bdaddr},
+ {uint8_t, pscan_rep_mode},
+ {uint8_t, pscan_mode},
+ {uint16_t, clock_offset}
+]}.
+{define, 'REMOTE_NAME_REQ_CP_SIZE', 10}.
+
+{define, 'OCF_REMOTE_NAME_REQ_CANCEL', 16#001A}.
+{struct, remote_name_req_cancel_cp,
+ [
+ {bdaddr_t, bdaddr}
+]}.
+{define, 'REMOTE_NAME_REQ_CANCEL_CP_SIZE', 6}.
+
+{define, 'OCF_READ_REMOTE_FEATURES', 16#001B}.
+{struct, read_remote_features_cp,
+ [
+ {uint16_t, handle}
+]}.
+{define, 'READ_REMOTE_FEATURES_CP_SIZE', 2}.
+
+{define, 'OCF_READ_REMOTE_EXT_FEATURES', 16#001C}.
+{struct, read_remote_ext_features_cp,
+ [
+ {uint16_t, handle},
+ {uint8_t, page_num}
+]}.
+{define, 'READ_REMOTE_EXT_FEATURES_CP_SIZE', 3}.
+
+{define, 'OCF_READ_REMOTE_VERSION', 16#001D}.
+{struct, read_remote_version_cp,
+ [
+ {uint16_t, handle}
+]}.
+{define, 'READ_REMOTE_VERSION_CP_SIZE', 2}.
+
+{define, 'OCF_READ_CLOCK_OFFSET', 16#001F}.
+{struct, read_clock_offset_cp,
+ [
+ {uint16_t, handle}
+]}.
+{define, 'READ_CLOCK_OFFSET_CP_SIZE', 2}.
+
+{define, 'OCF_READ_LMP_HANDLE', 16#0020}.
+
+{define, 'OCF_SETUP_SYNC_CONN', 16#0028}.
+{struct, setup_sync_conn_cp,
+ [
+ {uint16_t, handle},
+ {uint32_t, tx_bandwith},
+ {uint32_t, rx_bandwith},
+ {uint16_t, max_latency},
+ {uint16_t, voice_setting},
+ {uint8_t, retrans_effort},
+ {uint16_t, pkt_type}
+]}.
+{define, 'SETUP_SYNC_CONN_CP_SIZE', 17}.
+
+{define, 'OCF_ACCEPT_SYNC_CONN_REQ', 16#0029}.
+{struct, accept_sync_conn_req_cp,
+ [
+ {bdaddr_t, bdaddr},
+ {uint32_t, tx_bandwith},
+ {uint32_t, rx_bandwith},
+ {uint16_t, max_latency},
+ {uint16_t, voice_setting},
+ {uint8_t, retrans_effort},
+ {uint16_t, pkt_type}
+]}.
+{define, 'ACCEPT_SYNC_CONN_REQ_CP_SIZE', 21}.
+
+{define, 'OCF_REJECT_SYNC_CONN_REQ', 16#002A}.
+{struct, reject_sync_conn_req_cp,
+ [
+ {bdaddr_t, bdaddr},
+ {uint8_t, reason}
+]}.
+{define, 'REJECT_SYNC_CONN_REQ_CP_SIZE', 7}.
+
+{define, 'OCF_IO_CAPABILITY_REPLY', 16#002B}.
+{struct, io_capability_reply_cp,
+ [
+ {bdaddr_t, bdaddr},
+ {uint8_t, capability},
+ {uint8_t, oob_data},
+ {uint8_t, authentication}
+]}.
+{define, 'IO_CAPABILITY_REPLY_CP_SIZE', 9}.
+
+{define, 'OCF_USER_CONFIRM_REPLY', 16#002C}.
+{struct, user_confirm_reply_cp,
+ [
+ {bdaddr_t, bdaddr}
+]}.
+{define, 'USER_CONFIRM_REPLY_CP_SIZE', 6}.
+
+{define, 'OCF_USER_CONFIRM_NEG_REPLY', 16#002D}.
+
+{define, 'OCF_USER_PASSKEY_REPLY', 16#002E}.
+{struct, user_passkey_reply_cp,
+ [
+ {bdaddr_t, bdaddr},
+ {uint32_t, passkey}
+]}.
+{define, 'USER_PASSKEY_REPLY_CP_SIZE', 10}.
+
+{define, 'OCF_USER_PASSKEY_NEG_REPLY', 16#002F}.
+
+{define, 'OCF_REMOTE_OOB_DATA_REPLY', 16#0030}.
+{struct, remote_oob_data_reply_cp,
+ [
+ {bdaddr_t, bdaddr},
+ {{uint8_t,16}, hash},
+ {{uint8_t,16}, randomizer}
+]}.
+{define, 'REMOTE_OOB_DATA_REPLY_CP_SIZE', 38}.
+
+{define, 'OCF_REMOTE_OOB_DATA_NEG_REPLY', 16#0033}.
+
+{define, 'OCF_IO_CAPABILITY_NEG_REPLY', 16#0034}.
+{struct, io_capability_neg_reply_cp,
+ [
+ {bdaddr_t, bdaddr},
+ {uint8_t, reason}
+]}.
+{define, 'IO_CAPABILITY_NEG_REPLY_CP_SIZE', 7}.
+
+{define, 'OCF_CREATE_PHYSICAL_LINK', 16#0035}.
+{struct, create_physical_link_cp,
+ [
+ {uint8_t, handle},
+ {uint8_t, key_length},
+ {uint8_t, key_type},
+ {{uint8_t,32}, key}
+]}.
+{define, 'CREATE_PHYSICAL_LINK_CP_SIZE', 35}.
+
+{define, 'OCF_ACCEPT_PHYSICAL_LINK', 16#0036}.
+
+{define, 'OCF_DISCONNECT_PHYSICAL_LINK', 16#0037}.
+{struct, disconnect_physical_link_cp,
+ [
+ {uint8_t, handle},
+ {uint8_t, reason}
+]}.
+{define, 'DISCONNECT_PHYSICAL_LINK_CP_SIZE', 2}.
+
+{define, 'OCF_CREATE_LOGICAL_LINK', 16#0038}.
+{struct, create_logical_link_cp,
+ [
+ {uint8_t, handle},
+ {{uint8_t,16}, tx_flow},
+ {{uint8_t,16}, rx_flow}
+]}.
+{define, 'CREATE_LOGICAL_LINK_CP_SIZE', 33}.
+
+{define, 'OCF_ACCEPT_LOGICAL_LINK', 16#0039}.
+
+{define, 'OCF_DISCONNECT_LOGICAL_LINK', 16#003A}.
+{struct, disconnect_logical_link_cp,
+ [
+ {uint16_t, handle}
+]}.
+{define, 'DISCONNECT_LOGICAL_LINK_CP_SIZE', 2}.
+
+{define, 'OCF_LOGICAL_LINK_CANCEL', 16#003B}.
+{struct, cancel_logical_link_cp,
+ [
+ {uint8_t, handle},
+ {uint8_t, tx_flow_id}
+]}.
+{define, 'LOGICAL_LINK_CANCEL_CP_SIZE', 2}.
+{struct, cancel_logical_link_rp,
+ [
+ {uint8_t, status},
+ {uint8_t, handle},
+ {uint8_t, tx_flow_id}
+]}.
+{define, 'LOGICAL_LINK_CANCEL_RP_SIZE', 3}.
+
+{define, 'OCF_FLOW_SPEC_MODIFY', 16#003C}.
+
+%% Link Policy
+{define, 'OGF_LINK_POLICY', 16#02}.
+
+{define, 'OCF_HOLD_MODE', 16#0001}.
+{struct, hold_mode_cp,
+ [
+ {uint16_t, handle},
+ {uint16_t, max_interval},
+ {uint16_t, min_interval}
+]}.
+{define, 'HOLD_MODE_CP_SIZE', 6}.
+
+{define, 'OCF_SNIFF_MODE', 16#0003}.
+{struct, sniff_mode_cp,
+ [
+ {uint16_t, handle},
+ {uint16_t, max_interval},
+ {uint16_t, min_interval},
+ {uint16_t, attempt},
+ {uint16_t, timeout}
+]}.
+{define, 'SNIFF_MODE_CP_SIZE', 10}.
+
+{define, 'OCF_EXIT_SNIFF_MODE', 16#0004}.
+{struct, exit_sniff_mode_cp,
+ [
+ {uint16_t, handle}
+]}.
+{define, 'EXIT_SNIFF_MODE_CP_SIZE', 2}.
+
+{define, 'OCF_PARK_MODE', 16#0005}.
+{struct, park_mode_cp,
+ [
+ {uint16_t, handle},
+ {uint16_t, max_interval},
+ {uint16_t, min_interval}
+]}.
+{define, 'PARK_MODE_CP_SIZE', 6}.
+
+{define, 'OCF_EXIT_PARK_MODE', 16#0006}.
+{struct, exit_park_mode_cp,
+ [
+ {uint16_t, handle}
+]}.
+{define, 'EXIT_PARK_MODE_CP_SIZE', 2}.
+
+{define, 'OCF_QOS_SETUP', 16#0007}.
+{struct, hci_qos,
+ [
+ {uint8_t, service_type}, %% 1 = best effort
+ {uint32_t, token_rate}, %% Byte per seconds
+ {uint32_t, peak_bandwidth}, %% Byte per seconds
+ {uint32_t, latency}, %% Microseconds
+ {uint32_t, delay_variation} %% Microseconds
+]}.
+{define, 'HCI_QOS_CP_SIZE', 17}.
+{struct, qos_setup_cp,
+ [
+ {uint16_t, handle},
+ {uint8_t, flags}, %% Reserved
+ {hci_qos_t, qos}
+]}.
+{define, 'QOS_SETUP_CP_SIZE', "(3 + ?HCI_QOS_CP_SIZE)"}.
+
+{define, 'OCF_ROLE_DISCOVERY', 16#0009}.
+{struct, role_discovery_cp,
+ [
+ {uint16_t, handle}
+]}.
+{define, 'ROLE_DISCOVERY_CP_SIZE', 2}.
+{struct, role_discovery_rp,
+ [
+ {uint8_t, status},
+ {uint16_t, handle},
+ {uint8_t, role}
+]}.
+{define, 'ROLE_DISCOVERY_RP_SIZE', 4}.
+
+{define, 'OCF_SWITCH_ROLE', 16#000B}.
+{struct, switch_role_cp,
+ [
+ {bdaddr_t, bdaddr},
+ {uint8_t, role}
+]}.
+{define, 'SWITCH_ROLE_CP_SIZE', 7}.
+
+{define, 'OCF_READ_LINK_POLICY', 16#000C}.
+{struct, read_link_policy_cp,
+ [
+ {uint16_t, handle}
+]}.
+{define, 'READ_LINK_POLICY_CP_SIZE', 2}.
+{struct, read_link_policy_rp,
+ [
+ {uint8_t, status},
+ {uint16_t, handle},
+ {uint16_t, policy}
+]}.
+{define, 'READ_LINK_POLICY_RP_SIZE', 5}.
+
+{define, 'OCF_WRITE_LINK_POLICY', 16#000D}.
+{struct, write_link_policy_cp,
+ [
+ {uint16_t, handle},
+ {uint16_t, policy}
+]}.
+{define, 'WRITE_LINK_POLICY_CP_SIZE', 4}.
+{struct, write_link_policy_rp,
+ [
+ {uint8_t, status},
+ {uint16_t, handle}
+]}.
+{define, 'WRITE_LINK_POLICY_RP_SIZE', 3}.
+
+{define, 'OCF_READ_DEFAULT_LINK_POLICY', 16#000E}.
+
+{define, 'OCF_WRITE_DEFAULT_LINK_POLICY', 16#000F}.
+
+{define, 'OCF_FLOW_SPECIFICATION', 16#0010}.
+
+{define, 'OCF_SNIFF_SUBRATING', 16#0011}.
+{struct, sniff_subrating_cp,
+ [
+ {uint16_t, handle},
+ {uint16_t, max_latency},
+ {uint16_t, min_remote_timeout},
+ {uint16_t, min_local_timeout}
+]}.
+{define, 'SNIFF_SUBRATING_CP_SIZE', 8}.
+
+%% Host Controller and Baseband
+{define, 'OGF_HOST_CTL', 16#03}.
+
+{define, 'OCF_SET_EVENT_MASK', 16#0001}.
+{struct, set_event_mask_cp,
+ [
+ {{uint8_t,8}, mask}
+]}.
+{define, 'SET_EVENT_MASK_CP_SIZE', 8}.
+
+{define, 'OCF_RESET', 16#0003}.
+
+{define, 'OCF_SET_EVENT_FLT', 16#0005}.
+{struct, set_event_flt_cp,
+ [
+ {uint8_t, flt_type},
+ {uint8_t, cond_type},
+ {{uint8_t,0}, condition}
+]}.
+{define, 'SET_EVENT_FLT_CP_SIZE', 2}.
+
+%% Filter types
+{define, 'FLT_CLEAR_ALL', 16#00}.
+{define, 'FLT_INQ_RESULT', 16#01}.
+{define, 'FLT_CONN_SETUP', 16#02}.
+%% INQ_RESULT Condition types
+{define, 'INQ_RESULT_RETURN_ALL', 16#00}.
+{define, 'INQ_RESULT_RETURN_CLASS', 16#01}.
+{define, 'INQ_RESULT_RETURN_BDADDR', 16#02}.
+%% CONN_SETUP Condition types
+{define, 'CONN_SETUP_ALLOW_ALL', 16#00}.
+{define, 'CONN_SETUP_ALLOW_CLASS', 16#01}.
+{define, 'CONN_SETUP_ALLOW_BDADDR', 16#02}.
+%% CONN_SETUP Conditions
+{define, 'CONN_SETUP_AUTO_OFF', 16#01}.
+{define, 'CONN_SETUP_AUTO_ON', 16#02}.
+
+{define, 'OCF_FLUSH', 16#0008}.
+
+{define, 'OCF_READ_PIN_TYPE', 16#0009}.
+{struct, read_pin_type_rp,
+ [
+ {uint8_t, status},
+ {uint8_t, pin_type}
+]}.
+{define, 'READ_PIN_TYPE_RP_SIZE', 2}.
+
+{define, 'OCF_WRITE_PIN_TYPE', 16#000A}.
+{struct, write_pin_type_cp,
+ [
+ {uint8_t, pin_type}
+]}.
+{define, 'WRITE_PIN_TYPE_CP_SIZE', 1}.
+
+{define, 'OCF_CREATE_NEW_UNIT_KEY', 16#000B}.
+
+{define, 'OCF_READ_STORED_LINK_KEY', 16#000D}.
+{struct, read_stored_link_key_cp,
+ [
+ {bdaddr_t, bdaddr},
+ {uint8_t, read_all}
+]}.
+{define, 'READ_STORED_LINK_KEY_CP_SIZE', 7}.
+{struct, read_stored_link_key_rp,
+ [
+ {uint8_t, status},
+ {uint16_t, max_keys},
+ {uint16_t, num_keys}
+]}.
+{define, 'READ_STORED_LINK_KEY_RP_SIZE', 5}.
+
+{define, 'OCF_WRITE_STORED_LINK_KEY', 16#0011}.
+{struct, write_stored_link_key_cp,
+ [
+ {uint8_t, num_keys}
+ %% variable length part
+]}.
+{define, 'WRITE_STORED_LINK_KEY_CP_SIZE', 1}.
+{struct, write_stored_link_key_rp,
+ [
+ {uint8_t, status},
+ {uint8_t, num_keys}
+]}.
+{define, 'READ_WRITE_LINK_KEY_RP_SIZE', 2}.
+
+{define, 'OCF_DELETE_STORED_LINK_KEY', 16#0012}.
+{struct, delete_stored_link_key_cp,
+ [
+ {bdaddr_t, bdaddr},
+ {uint8_t, delete_all}
+]}.
+{define, 'DELETE_STORED_LINK_KEY_CP_SIZE', 7}.
+{struct, delete_stored_link_key_rp,
+ [
+ {uint8_t, status},
+ {uint16_t, num_keys}
+]}.
+{define, 'DELETE_STORED_LINK_KEY_RP_SIZE', 3}.
+
+{define, 'HCI_MAX_NAME_LENGTH', 248}.
+
+{define, 'OCF_CHANGE_LOCAL_NAME', 16#0013}.
+{struct, change_local_name_cp,
+ [
+ {{char,"?HCI_MAX_NAME_LENGTH"}, name}
+]}.
+{define, 'CHANGE_LOCAL_NAME_CP_SIZE', 248}.
+
+{define, 'OCF_READ_LOCAL_NAME', 16#0014}.
+{struct, read_local_name_rp,
+ [
+ {uint8_t, status},
+ {{char, "?HCI_MAX_NAME_LENGTH"}, name}
+]}.
+{define, 'READ_LOCAL_NAME_RP_SIZE', 249}.
+
+{define, 'OCF_READ_CONN_ACCEPT_TIMEOUT', 16#0015}.
+{struct, read_conn_accept_timeout_rp,
+ [
+ {uint8_t, status},
+ {uint16_t, timeout}
+]}.
+{define, 'READ_CONN_ACCEPT_TIMEOUT_RP_SIZE', 3}.
+
+{define, 'OCF_WRITE_CONN_ACCEPT_TIMEOUT', 16#0016}.
+{struct, write_conn_accept_timeout_cp,
+ [
+ {uint16_t, timeout}
+]}.
+{define, 'WRITE_CONN_ACCEPT_TIMEOUT_CP_SIZE', 2}.
+
+{define, 'OCF_READ_PAGE_TIMEOUT', 16#0017}.
+{struct, read_page_timeout_rp,
+ [
+ {uint8_t, status},
+ {uint16_t, timeout}
+]}.
+{define, 'READ_PAGE_TIMEOUT_RP_SIZE', 3}.
+
+{define, 'OCF_WRITE_PAGE_TIMEOUT', 16#0018}.
+{struct, write_page_timeout_cp,
+ [
+ {uint16_t, timeout}
+]}.
+{define, 'WRITE_PAGE_TIMEOUT_CP_SIZE', 2}.
+
+{define, 'OCF_READ_SCAN_ENABLE', 16#0019}.
+{struct, read_scan_enable_rp,
+ [
+ {uint8_t, status},
+ {uint8_t, enable}
+]}.
+{define, 'READ_SCAN_ENABLE_RP_SIZE', 2}.
+
+{define, 'OCF_WRITE_SCAN_ENABLE', 16#001A}.
+%% {define, 'SCAN_DISABLED', 16#00}.
+%% {define, 'SCAN_INQUIRY', 16#01}.
+%% {define, 'SCAN_PAGE', 16#02}.
+
+{define, 'OCF_READ_PAGE_ACTIVITY', 16#001B}.
+{struct, read_page_activity_rp,
+ [
+ {uint8_t, status},
+ {uint16_t, interval},
+ {uint16_t, window}
+]}.
+{define, 'READ_PAGE_ACTIVITY_RP_SIZE', 5}.
+
+{define, 'OCF_WRITE_PAGE_ACTIVITY', 16#001C}.
+{struct, write_page_activity_cp,
+ [
+ {uint16_t, interval},
+ {uint16_t, window}
+]}.
+{define, 'WRITE_PAGE_ACTIVITY_CP_SIZE', 4}.
+
+{define, 'OCF_READ_INQ_ACTIVITY', 16#001D}.
+{struct, read_inq_activity_rp,
+ [
+ {uint8_t, status},
+ {uint16_t, interval},
+ {uint16_t, window}
+]}.
+{define, 'READ_INQ_ACTIVITY_RP_SIZE', 5}.
+
+{define, 'OCF_WRITE_INQ_ACTIVITY', 16#001E}.
+{struct, write_inq_activity_cp,
+ [
+ {uint16_t, interval},
+ {uint16_t, window}
+]}.
+{define, 'WRITE_INQ_ACTIVITY_CP_SIZE', 4}.
+
+{define, 'OCF_READ_AUTH_ENABLE', 16#001F}.
+
+{define, 'OCF_WRITE_AUTH_ENABLE', 16#0020}.
+ {define, 'AUTH_DISABLED', 16#00}.
+ {define, 'AUTH_ENABLED', 16#01}.
+
+{define, 'OCF_READ_ENCRYPT_MODE', 16#0021}.
+
+{define, 'OCF_WRITE_ENCRYPT_MODE', 16#0022}.
+ {define, 'ENCRYPT_DISABLED', 16#00}.
+ {define, 'ENCRYPT_P2P', 16#01}.
+ {define, 'ENCRYPT_BOTH', 16#02}.
+
+{define, 'OCF_READ_CLASS_OF_DEV', 16#0023}.
+{struct, read_class_of_dev_rp,
+ [
+ {uint8_t, status},
+ {{uint8_t,3}, dev_class}
+]}.
+{define, 'READ_CLASS_OF_DEV_RP_SIZE', 4}.
+
+{define, 'OCF_WRITE_CLASS_OF_DEV', 16#0024}.
+{struct, write_class_of_dev_cp,
+ [
+ {{uint8_t,3}, dev_class}
+]}.
+{define, 'WRITE_CLASS_OF_DEV_CP_SIZE', 3}.
+
+{define, 'OCF_READ_VOICE_SETTING', 16#0025}.
+{struct, read_voice_setting_rp,
+ [
+ {uint8_t, status},
+ {uint16_t, voice_setting}
+]}.
+{define, 'READ_VOICE_SETTING_RP_SIZE', 3}.
+
+{define, 'OCF_WRITE_VOICE_SETTING', 16#0026}.
+{struct, write_voice_setting_cp,
+ [
+ {uint16_t, voice_setting}
+]}.
+{define, 'WRITE_VOICE_SETTING_CP_SIZE', 2}.
+
+{define, 'OCF_READ_AUTOMATIC_FLUSH_TIMEOUT', 16#0027}.
+
+{define, 'OCF_WRITE_AUTOMATIC_FLUSH_TIMEOUT', 16#0028}.
+
+{define, 'OCF_READ_NUM_BROADCAST_RETRANS', 16#0029}.
+
+{define, 'OCF_WRITE_NUM_BROADCAST_RETRANS', 16#002A}.
+
+{define, 'OCF_READ_HOLD_MODE_ACTIVITY', 16#002B}.
+
+{define, 'OCF_WRITE_HOLD_MODE_ACTIVITY', 16#002C}.
+
+{define, 'OCF_READ_TRANSMIT_POWER_LEVEL', 16#002D}.
+{struct, read_transmit_power_level_cp,
+ [
+ {uint16_t, handle},
+ {uint8_t, type}
+]}.
+{define, 'READ_TRANSMIT_POWER_LEVEL_CP_SIZE', 3}.
+{struct, read_transmit_power_level_rp,
+ [
+ {uint8_t, status},
+ {uint16_t, handle},
+ {int8_t, level}
+]}.
+{define, 'READ_TRANSMIT_POWER_LEVEL_RP_SIZE', 4}.
+
+{define, 'OCF_READ_SYNC_FLOW_ENABLE', 16#002E}.
+
+{define, 'OCF_WRITE_SYNC_FLOW_ENABLE', 16#002F}.
+
+{define, 'OCF_SET_CONTROLLER_TO_HOST_FC', 16#0031}.
+
+{define, 'OCF_HOST_BUFFER_SIZE', 16#0033}.
+{struct, host_buffer_size_cp,
+ [
+ {uint16_t, acl_mtu},
+ {uint8_t, sco_mtu},
+ {uint16_t, acl_max_pkt},
+ {uint16_t, sco_max_pkt}
+]}.
+{define, 'HOST_BUFFER_SIZE_CP_SIZE', 7}.
+
+{define, 'OCF_HOST_NUM_COMP_PKTS', 16#0035}.
+{struct, host_num_comp_pkts_cp,
+ [
+ {uint8_t, num_hndl}
+ %% variable length part
+ %% {list, num_hndl, uint16_t, hndls}
+]}.
+{define, 'HOST_NUM_COMP_PKTS_CP_SIZE', 1}.
+
+{define, 'OCF_READ_LINK_SUPERVISION_TIMEOUT', 16#0036}.
+{struct, read_link_supervision_timeout_rp,
+ [
+ {uint8_t, status},
+ {uint16_t, handle},
+ {uint16_t, timeout}
+]}.
+{define, 'READ_LINK_SUPERVISION_TIMEOUT_RP_SIZE', 5}.
+
+{define, 'OCF_WRITE_LINK_SUPERVISION_TIMEOUT', 16#0037}.
+{struct, write_link_supervision_timeout_cp,
+ [
+ {uint16_t, handle},
+ {uint16_t, timeout}
+]}.
+{define, 'WRITE_LINK_SUPERVISION_TIMEOUT_CP_SIZE', 4}.
+{struct, write_link_supervision_timeout_rp,
+ [
+ {uint8_t, status},
+ {uint16_t, handle}
+]}.
+{define, 'WRITE_LINK_SUPERVISION_TIMEOUT_RP_SIZE', 3}.
+
+{define, 'OCF_READ_NUM_SUPPORTED_IAC', 16#0038}.
+
+{define, 'MAX_IAC_LAP', 16#40}.
+{define, 'OCF_READ_CURRENT_IAC_LAP', 16#0039}.
+{struct, read_current_iac_lap_rp,
+ [
+ {uint8_t, status},
+ {uint8_t, num_current_iac},
+ %% {list,num_current_iac,{binary,3},"?MAX_IAC_LAP",lap}
+ {{uint8_t,"3*?MAX_IAC_LAP"}, lap}
+]}.
+{define, 'READ_CURRENT_IAC_LAP_RP_SIZE', "2+3*?MAX_IAC_LAP"}.
+
+{define, 'OCF_WRITE_CURRENT_IAC_LAP', 16#003A}.
+{struct, write_current_iac_lap_cp,
+ [
+ {uint8_t, num_current_iac},
+ %% {list,num_current_iac,{binary,3},"?MAX_IAC_LAP",lap}
+ {{uint8_t,"3*?MAX_IAC_LAP"}, lap}
+]}.
+{define, 'WRITE_CURRENT_IAC_LAP_CP_SIZE', "1+3*?MAX_IAC_LAP"}.
+
+{define, 'OCF_READ_PAGE_SCAN_PERIOD_MODE', 16#003B}.
+
+{define, 'OCF_WRITE_PAGE_SCAN_PERIOD_MODE', 16#003C}.
+
+{define, 'OCF_READ_PAGE_SCAN_MODE', 16#003D}.
+
+{define, 'OCF_WRITE_PAGE_SCAN_MODE', 16#003E}.
+
+{define, 'OCF_SET_AFH_CLASSIFICATION', 16#003F}.
+{struct, set_afh_classification_cp,
+ [
+ {{uint8_t,10}, map}
+]}.
+{define, 'SET_AFH_CLASSIFICATION_CP_SIZE', 10}.
+{struct, set_afh_classification_rp,
+ [
+ {uint8_t, status}
+]}.
+{define, 'SET_AFH_CLASSIFICATION_RP_SIZE', 1}.
+
+{define, 'OCF_READ_INQUIRY_SCAN_TYPE', 16#0042}.
+{struct, read_inquiry_scan_type_rp,
+ [
+ {uint8_t, status},
+ {uint8_t, type}
+]}.
+{define, 'READ_INQUIRY_SCAN_TYPE_RP_SIZE', 2}.
+
+{define, 'OCF_WRITE_INQUIRY_SCAN_TYPE', 16#0043}.
+{struct, write_inquiry_scan_type_cp,
+ [
+ {uint8_t, type}
+]}.
+{define, 'WRITE_INQUIRY_SCAN_TYPE_CP_SIZE', 1}.
+{struct, write_inquiry_scan_type_rp,
+ [
+ {uint8_t, status}
+]}.
+{define, 'WRITE_INQUIRY_SCAN_TYPE_RP_SIZE', 1}.
+
+{define, 'OCF_READ_INQUIRY_MODE', 16#0044}.
+{struct, read_inquiry_mode_rp,
+ [
+ {uint8_t, status},
+ {uint8_t, mode}
+]}.
+{define, 'READ_INQUIRY_MODE_RP_SIZE', 2}.
+
+{define, 'OCF_WRITE_INQUIRY_MODE', 16#0045}.
+{struct, write_inquiry_mode_cp,
+ [
+ {uint8_t, mode}
+]}.
+{define, 'WRITE_INQUIRY_MODE_CP_SIZE', 1}.
+{struct, write_inquiry_mode_rp,
+ [
+ {uint8_t, status}
+]}.
+{define, 'WRITE_INQUIRY_MODE_RP_SIZE', 1}.
+
+{define, 'OCF_READ_PAGE_SCAN_TYPE', 16#0046}.
+
+{define, 'OCF_WRITE_PAGE_SCAN_TYPE', 16#0047}.
+{define, 'PAGE_SCAN_TYPE_STANDARD', 16#00}.
+{define, 'PAGE_SCAN_TYPE_INTERLACED', 16#01}.
+
+{define, 'OCF_READ_AFH_MODE', 16#0048}.
+{struct, read_afh_mode_rp,
+ [
+ {uint8_t, status},
+ {uint8_t, mode}
+]}.
+{define, 'READ_AFH_MODE_RP_SIZE', 2}.
+
+{define, 'OCF_WRITE_AFH_MODE', 16#0049}.
+{struct, write_afh_mode_cp,
+ [
+ {uint8_t, mode}
+]}.
+{define, 'WRITE_AFH_MODE_CP_SIZE', 1}.
+{struct, write_afh_mode_rp,
+ [
+ {uint8_t, status}
+]}.
+{define, 'WRITE_AFH_MODE_RP_SIZE', 1}.
+
+{define, 'HCI_MAX_EIR_LENGTH', 240}.
+
+{define, 'OCF_READ_EXT_INQUIRY_RESPONSE', 16#0051}.
+{struct, read_ext_inquiry_response_rp,
+ [
+ {uint8_t, status},
+ {uint8_t, fec},
+ {{uint8_t,"?HCI_MAX_EIR_LENGTH"}, data}
+]}.
+{define, 'READ_EXT_INQUIRY_RESPONSE_RP_SIZE', 242}.
+
+{define, 'OCF_WRITE_EXT_INQUIRY_RESPONSE', 16#0052}.
+{struct, write_ext_inquiry_response_cp,
+ [
+ {uint8_t, fec},
+ {{uint8_t,"?HCI_MAX_EIR_LENGTH"}, data}
+]}.
+{define, 'WRITE_EXT_INQUIRY_RESPONSE_CP_SIZE', 241}.
+{struct, write_ext_inquiry_response_rp,
+ [
+ {uint8_t, status}
+]}.
+{define, 'WRITE_EXT_INQUIRY_RESPONSE_RP_SIZE', 1}.
+
+{define, 'OCF_REFRESH_ENCRYPTION_KEY', 16#0053}.
+{struct, refresh_encryption_key_cp,
+ [
+ {uint16_t, handle}
+]}.
+{define, 'REFRESH_ENCRYPTION_KEY_CP_SIZE', 2}.
+{struct, refresh_encryption_key_rp,
+ [
+ {uint8_t, status}
+]}.
+{define, 'REFRESH_ENCRYPTION_KEY_RP_SIZE', 1}.
+
+{define, 'OCF_READ_SIMPLE_PAIRING_MODE', 16#0055}.
+{struct, read_simple_pairing_mode_rp,
+ [
+ {uint8_t, status},
+ {uint8_t, mode}
+]}.
+{define, 'READ_SIMPLE_PAIRING_MODE_RP_SIZE', 2}.
+
+{define, 'OCF_WRITE_SIMPLE_PAIRING_MODE', 16#0056}.
+{struct, write_simple_pairing_mode_cp,
+ [
+ {uint8_t, mode}
+]}.
+{define, 'WRITE_SIMPLE_PAIRING_MODE_CP_SIZE', 1}.
+{struct, write_simple_pairing_mode_rp,
+ [
+ {uint8_t, status}
+]}.
+{define, 'WRITE_SIMPLE_PAIRING_MODE_RP_SIZE', 1}.
+
+{define, 'OCF_READ_LOCAL_OOB_DATA', 16#0057}.
+{struct, read_local_oob_data_rp,
+ [
+ {uint8_t, status},
+ {{uint8_t,16}, hash},
+ {{uint8_t,16}, randomizer}
+]}.
+{define, 'READ_LOCAL_OOB_DATA_RP_SIZE', 33}.
+
+{define, 'OCF_READ_INQ_RESPONSE_TX_POWER_LEVEL', 16#0058}.
+{struct, read_inq_response_tx_power_level_rp,
+ [
+ {uint8_t, status},
+ {int8_t, level}
+]}.
+{define, 'READ_INQ_RESPONSE_TX_POWER_LEVEL_RP_SIZE', 2}.
+
+{define, 'OCF_READ_INQUIRY_TRANSMIT_POWER_LEVEL', 16#0058}.
+{struct, read_inquiry_transmit_power_level_rp,
+ [
+ {uint8_t, status},
+ {int8_t, level}
+]}.
+{define, 'READ_INQUIRY_TRANSMIT_POWER_LEVEL_RP_SIZE', 2}.
+
+{define, 'OCF_WRITE_INQUIRY_TRANSMIT_POWER_LEVEL', 16#0059}.
+{struct, write_inquiry_transmit_power_level_cp,
+ [
+ {int8_t, level}
+]}.
+{define, 'WRITE_INQUIRY_TRANSMIT_POWER_LEVEL_CP_SIZE', 1}.
+{struct, write_inquiry_transmit_power_level_rp,
+ [
+ {uint8_t, status}
+]}.
+{define, 'WRITE_INQUIRY_TRANSMIT_POWER_LEVEL_RP_SIZE', 1}.
+
+{define, 'OCF_READ_DEFAULT_ERROR_DATA_REPORTING', 16#005A}.
+{struct, read_default_error_data_reporting_rp,
+ [
+ {uint8_t, status},
+ {uint8_t, reporting}
+]}.
+{define, 'READ_DEFAULT_ERROR_DATA_REPORTING_RP_SIZE', 2}.
+
+{define, 'OCF_WRITE_DEFAULT_ERROR_DATA_REPORTING', 16#005B}.
+{struct, write_default_error_data_reporting_cp,
+ [
+ {uint8_t, reporting}
+]}.
+{define, 'WRITE_DEFAULT_ERROR_DATA_REPORTING_CP_SIZE', 1}.
+{struct, write_default_error_data_reporting_rp,
+ [
+ {uint8_t, status}
+]}.
+{define, 'WRITE_DEFAULT_ERROR_DATA_REPORTING_RP_SIZE', 1}.
+
+{define, 'OCF_ENHANCED_FLUSH', 16#005F}.
+{struct, enhanced_flush_cp,
+ [
+ {uint16_t, handle},
+ {uint8_t, type}
+]}.
+{define, 'ENHANCED_FLUSH_CP_SIZE', 3}.
+
+{define, 'OCF_SEND_KEYPRESS_NOTIFY', 16#0060}.
+{struct, send_keypress_notify_cp,
+ [
+ {bdaddr_t, bdaddr},
+ {uint8_t, type}
+]}.
+{define, 'SEND_KEYPRESS_NOTIFY_CP_SIZE', 7}.
+{struct, send_keypress_notify_rp,
+ [
+ {uint8_t, status}
+]}.
+{define, 'SEND_KEYPRESS_NOTIFY_RP_SIZE', 1}.
+
+{define, 'OCF_READ_LOGICAL_LINK_ACCEPT_TIMEOUT', 16#0061}.
+{struct, read_log_link_accept_timeout_rp,
+ [
+ {uint8_t, status},
+ {uint16_t, timeout}
+]}.
+{define, 'READ_LOGICAL_LINK_ACCEPT_TIMEOUT_RP_SIZE', 3}.
+
+{define, 'OCF_WRITE_LOGICAL_LINK_ACCEPT_TIMEOUT', 16#0062}.
+{struct, write_log_link_accept_timeout_cp,
+ [
+ {uint16_t, timeout}
+]}.
+{define, 'WRITE_LOGICAL_LINK_ACCEPT_TIMEOUT_CP_SIZE', 2}.
+
+{define, 'OCF_SET_EVENT_MASK_PAGE_2', 16#0063}.
+
+{define, 'OCF_READ_LOCATION_DATA', 16#0064}.
+
+{define, 'OCF_WRITE_LOCATION_DATA', 16#0065}.
+
+{define, 'OCF_READ_FLOW_CONTROL_MODE', 16#0066}.
+
+{define, 'OCF_WRITE_FLOW_CONTROL_MODE', 16#0067}.
+
+{define, 'OCF_READ_ENHANCED_TRANSMIT_POWER_LEVEL', 16#0068}.
+{struct, read_enhanced_transmit_power_level_rp,
+ [
+ {uint8_t, status},
+ {uint16_t, handle},
+ {int8_t, level_gfsk},
+ {int8_t, level_dqpsk},
+ {int8_t, level_8dpsk}
+]}.
+{define, 'READ_ENHANCED_TRANSMIT_POWER_LEVEL_RP_SIZE', 6}.
+
+{define, 'OCF_READ_BEST_EFFORT_FLUSH_TIMEOUT', 16#0069}.
+{struct, read_best_effort_flush_timeout_rp,
+ [
+ {uint8_t, status},
+ {uint32_t, timeout}
+]}.
+{define, 'READ_BEST_EFFORT_FLUSH_TIMEOUT_RP_SIZE', 5}.
+
+{define, 'OCF_WRITE_BEST_EFFORT_FLUSH_TIMEOUT', 16#006A}.
+{struct, write_best_effort_flush_timeout_cp,
+ [
+ {uint16_t, handle},
+ {uint32_t, timeout}
+]}.
+{define, 'WRITE_BEST_EFFORT_FLUSH_TIMEOUT_CP_SIZE', 6}.
+{struct, write_best_effort_flush_timeout_rp,
+ [
+ {uint8_t, status}
+]}.
+{define, 'WRITE_BEST_EFFORT_FLUSH_TIMEOUT_RP_SIZE', 1}.
+
+{define, 'OCF_READ_LE_HOST_SUPPORTED', 16#006C}.
+{struct, read_le_host_supported_rp,
+ [
+ {uint8_t, status},
+ {uint8_t, le},
+ {uint8_t, simul}
+]}.
+{define, 'READ_LE_HOST_SUPPORTED_RP_SIZE', 3}.
+
+{define, 'OCF_WRITE_LE_HOST_SUPPORTED', 16#006D}.
+{struct, write_le_host_supported_cp,
+ [
+ {uint8_t, le},
+ {uint8_t, simul}
+]}.
+{define, 'WRITE_LE_HOST_SUPPORTED_CP_SIZE', 2}.
+
+%% Informational Parameters
+{define, 'OGF_INFO_PARAM', 16#04}.
+
+{define, 'OCF_READ_LOCAL_VERSION', 16#0001}.
+{struct, read_local_version_rp,
+ [
+ {uint8_t, status},
+ {uint8_t, hci_ver},
+ {uint16_t, hci_rev},
+ {uint8_t, lmp_ver},
+ {uint16_t, manufacturer},
+ {uint16_t, lmp_subver}
+]}.
+{define, 'READ_LOCAL_VERSION_RP_SIZE', 9}.
+
+{define, 'OCF_READ_LOCAL_COMMANDS', 16#0002}.
+{struct, read_local_commands_rp,
+ [
+ {uint8_t, status},
+ {{uint8_t,64}, commands}
+]}.
+{define, 'READ_LOCAL_COMMANDS_RP_SIZE', 65}.
+
+{define, 'OCF_READ_LOCAL_FEATURES', 16#0003}.
+{struct, read_local_features_rp,
+ [
+ {uint8_t, status},
+ {{uint8_t,8}, features}
+]}.
+{define, 'READ_LOCAL_FEATURES_RP_SIZE', 9}.
+
+{define, 'OCF_READ_LOCAL_EXT_FEATURES', 16#0004}.
+{struct, read_local_ext_features_cp,
+ [
+ {uint8_t, page_num}
+]}.
+{define, 'READ_LOCAL_EXT_FEATURES_CP_SIZE', 1}.
+{struct, read_local_ext_features_rp,
+ [
+ {uint8_t, status},
+ {uint8_t, page_num},
+ {uint8_t, max_page_num},
+ {{uint8_t,8}, features}
+]}.
+{define, 'READ_LOCAL_EXT_FEATURES_RP_SIZE', 11}.
+
+{define, 'OCF_READ_BUFFER_SIZE', 16#0005}.
+{struct, read_buffer_size_rp,
+ [
+ {uint8_t, status},
+ {uint16_t, acl_mtu},
+ {uint8_t, sco_mtu},
+ {uint16_t, acl_max_pkt},
+ {uint16_t, sco_max_pkt}
+]}.
+{define, 'READ_BUFFER_SIZE_RP_SIZE', 8}.
+
+{define, 'OCF_READ_COUNTRY_CODE', 16#0007}.
+
+{define, 'OCF_READ_BD_ADDR', 16#0009}.
+{struct, read_bd_addr_rp,
+ [
+ {uint8_t, status},
+ {bdaddr_t, bdaddr}
+]}.
+{define, 'READ_BD_ADDR_RP_SIZE', 7}.
+
+%% Status params
+{define, 'OGF_STATUS_PARAM', 16#05}.
+
+{define, 'OCF_READ_FAILED_CONTACT_COUNTER', 16#0001}.
+{struct, read_failed_contact_counter_rp,
+ [
+ {uint8_t, status},
+ {uint16_t, handle},
+ {uint8_t, counter}
+]}.
+{define, 'READ_FAILED_CONTACT_COUNTER_RP_SIZE', 4}.
+
+{define, 'OCF_RESET_FAILED_CONTACT_COUNTER', 16#0002}.
+{struct, reset_failed_contact_counter_rp,
+ [
+ {uint8_t, status},
+ {uint16_t, handle}
+]}.
+{define, 'RESET_FAILED_CONTACT_COUNTER_RP_SIZE', 4}.
+
+{define, 'OCF_READ_LINK_QUALITY', 16#0003}.
+{struct, read_link_quality_rp,
+ [
+ {uint8_t, status},
+ {uint16_t, handle},
+ {uint8_t, link_quality}
+]}.
+{define, 'READ_LINK_QUALITY_RP_SIZE', 4}.
+
+{define, 'OCF_READ_RSSI', 16#0005}.
+{struct, read_rssi_rp,
+ [
+ {uint8_t, status},
+ {uint16_t, handle},
+ {int8_t, rssi}
+]}.
+{define, 'READ_RSSI_RP_SIZE', 4}.
+
+{define, 'OCF_READ_AFH_MAP', 16#0006}.
+{struct, read_afh_map_rp,
+ [
+ {uint8_t, status},
+ {uint16_t, handle},
+ {uint8_t, mode},
+ {{uint8_t,10}, map}
+]}.
+{define, 'READ_AFH_MAP_RP_SIZE', 14}.
+
+{define, 'OCF_READ_CLOCK', 16#0007}.
+{struct, read_clock_cp,
+ [
+ {uint16_t, handle},
+ {uint8_t, which_clock}
+]}.
+{define, 'READ_CLOCK_CP_SIZE', 3}.
+{struct, read_clock_rp,
+ [
+ {uint8_t, status},
+ {uint16_t, handle},
+ {uint32_t, clock},
+ {uint16_t, accuracy}
+]}.
+{define, 'READ_CLOCK_RP_SIZE', 9}.
+
+{define, 'OCF_READ_LOCAL_AMP_INFO', 16#0009}.
+{struct, read_local_amp_info_rp,
+ [
+ {uint8_t, status},
+ {uint8_t, amp_status},
+ {uint32_t, total_bandwidth},
+ {uint32_t, max_guaranteed_bandwidth},
+ {uint32_t, min_latency},
+ {uint32_t, max_pdu_size},
+ {uint8_t, controller_type},
+ {uint16_t, pal_caps},
+ {uint16_t, max_amp_assoc_length},
+ {uint32_t, max_flush_timeout},
+ {uint32_t, best_effort_flush_timeout}
+]}.
+{define, 'READ_LOCAL_AMP_INFO_RP_SIZE', 31}.
+
+{define, 'OCF_READ_LOCAL_AMP_ASSOC', 16#000A}.
+{struct, read_local_amp_assoc_cp,
+ [
+ {uint8_t, handle},
+ {uint16_t, len_so_far},
+ {uint16_t, max_len}
+]}.
+
+{struct, read_local_amp_assoc_rp,
+ [
+ {uint8_t, status},
+ {uint8_t, handle},
+ {uint16_t, rem_len},
+ {{uint8_t,0}, frag}
+]}.
+
+{define, 'OCF_WRITE_REMOTE_AMP_ASSOC', 16#000B}.
+{struct, write_remote_amp_assoc_cp,
+ [
+ {uint8_t, handle},
+ {uint16_t, length_so_far},
+ {uint16_t, assoc_length},
+ {{char, "?HCI_MAX_NAME_LENGTH"}, fragment}
+]}.
+{define, 'WRITE_REMOTE_AMP_ASSOC_CP_SIZE', 253}.
+{struct, write_remote_amp_assoc_rp,
+ [
+ {uint8_t, status},
+ {uint8_t, handle}
+]}.
+{define, 'WRITE_REMOTE_AMP_ASSOC_RP_SIZE', 2}.
+
+%% Testing commands
+{define, 'OGF_TESTING_CMD', 16#3e}.
+
+{define, 'OCF_READ_LOOPBACK_MODE', 16#0001}.
+
+{define, 'OCF_WRITE_LOOPBACK_MODE', 16#0002}.
+
+{define, 'OCF_ENABLE_DEVICE_UNDER_TEST_MODE', 16#0003}.
+
+{define, 'OCF_WRITE_SIMPLE_PAIRING_DEBUG_MODE', 16#0004}.
+{struct, write_simple_pairing_debug_mode_cp,
+ [
+ {uint8_t, mode}
+]}.
+{define, 'WRITE_SIMPLE_PAIRING_DEBUG_MODE_CP_SIZE', 1}.
+{struct, write_simple_pairing_debug_mode_rp,
+ [
+ {uint8_t, status}
+]}.
+{define, 'WRITE_SIMPLE_PAIRING_DEBUG_MODE_RP_SIZE', 1}.
+
+%% LE commands
+{define, 'OGF_LE_CTL', 16#08}.
+
+{define, 'OCF_LE_SET_EVENT_MASK', 16#0001}.
+{struct, le_set_event_mask_cp,
+ [
+ {{uint8_t,8}, mask}
+]}.
+{define, 'LE_SET_EVENT_MASK_CP_SIZE', 8}.
+
+{define, 'OCF_LE_READ_BUFFER_SIZE', 16#0002}.
+{struct, le_read_buffer_size_rp,
+ [
+ {uint8_t, status},
+ {uint16_t, pkt_len},
+ {uint8_t, max_pkt}
+]}.
+{define, 'LE_READ_BUFFER_SIZE_RP_SIZE', 4}.
+
+{define, 'OCF_LE_READ_LOCAL_SUPPORTED_FEATURES', 16#0003}.
+{struct, le_read_local_supported_features_rp,
+ [
+ {uint8_t, status},
+ {{uint8_t,8}, features}
+]}.
+{define, 'LE_READ_LOCAL_SUPPORTED_FEATURES_RP_SIZE', 9}.
+
+{define, 'OCF_LE_SET_RANDOM_ADDRESS', 16#0005}.
+{struct, le_set_random_address_cp,
+ [
+ {bdaddr_t, bdaddr}
+]}.
+{define, 'LE_SET_RANDOM_ADDRESS_CP_SIZE', 6}.
+
+{define, 'OCF_LE_SET_ADVERTISING_PARAMETERS', 16#0006}.
+{struct, le_set_advertising_parameters_cp,
+ [
+ {uint16_t, min_interval},
+ {uint16_t, max_interval},
+ {uint8_t, advtype},
+ {uint8_t, own_bdaddr_type},
+ {uint8_t, direct_bdaddr_type},
+ {bdaddr_t, direct_bdaddr},
+ {uint8_t, chan_map},
+ {uint8_t, filter}
+]}.
+{define, 'LE_SET_ADVERTISING_PARAMETERS_CP_SIZE', 15}.
+
+{define, 'OCF_LE_READ_ADVERTISING_CHANNEL_TX_POWER', 16#0007}.
+{struct, le_read_advertising_channel_tx_power_rp,
+ [
+ {uint8_t, status},
+ {uint8_t, level}
+]}.
+{define, 'LE_READ_ADVERTISING_CHANNEL_TX_POWER_RP_SIZE', 2}.
+
+{define, 'OCF_LE_SET_ADVERTISING_DATA', 16#0008}.
+{struct, le_set_advertising_data_cp,
+ [
+ {uint8_t, length},
+ {{uint8_t,31}, data}
+]}.
+{define, 'LE_SET_ADVERTISING_DATA_CP_SIZE', 32}.
+
+{define, 'OCF_LE_SET_SCAN_RESPONSE_DATA', 16#0009}.
+{struct, le_set_scan_response_data_cp,
+ [
+ {uint8_t, length},
+ {{uint8_t,31}, data}
+]}.
+{define, 'LE_SET_SCAN_RESPONSE_DATA_CP_SIZE', 32}.
+
+{define, 'OCF_LE_SET_ADVERTISE_ENABLE', 16#000A}.
+{struct, le_set_advertise_enable_cp,
+ [
+ {uint8_t, enable}
+]}.
+{define, 'LE_SET_ADVERTISE_ENABLE_CP_SIZE', 1}.
+
+{define, 'OCF_LE_SET_SCAN_PARAMETERS', 16#000B}.
+{struct, le_set_scan_parameters_cp,
+ [
+ {uint8_t, type},
+ {uint16_t, interval},
+ {uint16_t, window},
+ {uint8_t, own_bdaddr_type},
+ {uint8_t, filter}
+]}.
+{define, 'LE_SET_SCAN_PARAMETERS_CP_SIZE', 7}.
+
+{define, 'OCF_LE_SET_SCAN_ENABLE', 16#000C}.
+{struct, le_set_scan_enable_cp,
+ [
+ {uint8_t, enable},
+ {uint8_t, filter_dup}
+]}.
+{define, 'LE_SET_SCAN_ENABLE_CP_SIZE', 2}.
+
+{define, 'OCF_LE_CREATE_CONN', 16#000D}.
+{struct, le_create_connection_cp,
+ [
+ {uint16_t, interval},
+ {uint16_t, window},
+ {uint8_t, initiator_filter},
+ {uint8_t, peer_bdaddr_type},
+ {bdaddr_t, peer_bdaddr},
+ {uint8_t, own_bdaddr_type},
+ {uint16_t, min_interval},
+ {uint16_t, max_interval},
+ {uint16_t, latency},
+ {uint16_t, supervision_timeout},
+ {uint16_t, min_ce_length},
+ {uint16_t, max_ce_length}
+]}.
+{define, 'LE_CREATE_CONN_CP_SIZE', 25}.
+
+{define, 'OCF_LE_CREATE_CONN_CANCEL', 16#000E}.
+
+{define, 'OCF_LE_READ_WHITE_LIST_SIZE', 16#000F}.
+{struct, le_read_white_list_size_rp,
+ [
+ {uint8_t, status},
+ {uint8_t, size}
+]}.
+{define, 'LE_READ_WHITE_LIST_SIZE_RP_SIZE', 2}.
+
+{define, 'OCF_LE_CLEAR_WHITE_LIST', 16#0010}.
+
+{define, 'OCF_LE_ADD_DEVICE_TO_WHITE_LIST', 16#0011}.
+{struct, le_add_device_to_white_list_cp,
+ [
+ {uint8_t, bdaddr_type},
+ {bdaddr_t, bdaddr}
+]}.
+{define, 'LE_ADD_DEVICE_TO_WHITE_LIST_CP_SIZE', 7}.
+
+{define, 'OCF_LE_REMOVE_DEVICE_FROM_WHITE_LIST', 16#0012}.
+{struct, le_remove_device_from_white_list_cp,
+ [
+ {uint8_t, bdaddr_type},
+ {bdaddr_t, bdaddr}
+]}.
+{define, 'LE_REMOVE_DEVICE_FROM_WHITE_LIST_CP_SIZE', 7}.
+
+{define, 'OCF_LE_CONN_UPDATE', 16#0013}.
+{struct, le_connection_update_cp,
+ [
+ {uint16_t, handle},
+ {uint16_t, min_interval},
+ {uint16_t, max_interval},
+ {uint16_t, latency},
+ {uint16_t, supervision_timeout},
+ {uint16_t, min_ce_length},
+ {uint16_t, max_ce_length}
+]}.
+{define, 'LE_CONN_UPDATE_CP_SIZE', 14}.
+
+{define, 'OCF_LE_SET_HOST_CHANNEL_CLASSIFICATION', 16#0014}.
+{struct, le_set_host_channel_classification_cp,
+ [
+ {{uint8_t,5}, map}
+]}.
+{define, 'LE_SET_HOST_CHANNEL_CLASSIFICATION_CP_SIZE', 5}.
+
+{define, 'OCF_LE_READ_CHANNEL_MAP', 16#0015}.
+{struct, le_read_channel_map_cp,
+ [
+ {uint16_t, handle}
+]}.
+{define, 'LE_READ_CHANNEL_MAP_CP_SIZE', 2}.
+{struct, le_read_channel_map_rp,
+ [
+ {uint8_t, status},
+ {uint16_t, handle},
+ {{uint8_t,5}, map}
+]}.
+{define, 'LE_READ_CHANNEL_MAP_RP_SIZE', 8}.
+
+{define, 'OCF_LE_READ_REMOTE_USED_FEATURES', 16#0016}.
+{struct, le_read_remote_used_features_cp,
+ [
+ {uint16_t, handle}
+]}.
+{define, 'LE_READ_REMOTE_USED_FEATURES_CP_SIZE', 2}.
+
+{define, 'OCF_LE_ENCRYPT', 16#0017}.
+{struct, le_encrypt_cp,
+ [
+ {{uint8_t,16}, key},
+ {{uint8_t,16}, plaintext}
+]}.
+{define, 'LE_ENCRYPT_CP_SIZE', 32}.
+{struct, le_encrypt_rp,
+ [
+ {uint8_t, status},
+ {{uint8_t,16}, data}
+]}.
+{define, 'LE_ENCRYPT_RP_SIZE', 17}.
+
+{define, 'OCF_LE_RAND', 16#0018}.
+{struct, le_rand_rp,
+ [
+ {uint8_t, status},
+ {uint64_t, random}
+]}.
+{define, 'LE_RAND_RP_SIZE', 9}.
+
+{define, 'OCF_LE_START_ENCRYPTION', 16#0019}.
+{struct, le_start_encryption_cp,
+ [
+ {uint16_t, handle},
+ {uint64_t, random},
+ {uint16_t, diversifier},
+ {{uint8_t,16}, key}
+]}.
+{define, 'LE_START_ENCRYPTION_CP_SIZE', 28}.
+
+{define, 'OCF_LE_LTK_REPLY', 16#001A}.
+{struct, le_ltk_reply_cp,
+ [
+ {uint16_t, handle},
+ {{uint8_t,16}, key}
+]}.
+{define, 'LE_LTK_REPLY_CP_SIZE', 18}.
+{struct, le_ltk_reply_rp,
+ [
+ {uint8_t, status},
+ {uint16_t, handle}
+]}.
+{define, 'LE_LTK_REPLY_RP_SIZE', 3}.
+
+{define, 'OCF_LE_LTK_NEG_REPLY', 16#001B}.
+{struct, le_ltk_neg_reply_cp,
+ [
+ {uint16_t, handle}
+]}.
+{define, 'LE_LTK_NEG_REPLY_CP_SIZE', 2}.
+{struct, le_ltk_neg_reply_rp,
+ [
+ {uint8_t, status},
+ {uint16_t, handle}
+]}.
+{define, 'LE_LTK_NEG_REPLY_RP_SIZE', 3}.
+
+{define, 'OCF_LE_READ_SUPPORTED_STATES', 16#001C}.
+{struct, le_read_supported_states_rp,
+ [
+ {uint8_t, status},
+ {uint64_t, states}
+]}.
+{define, 'LE_READ_SUPPORTED_STATES_RP_SIZE', 9}.
+
+{define, 'OCF_LE_RECEIVER_TEST', 16#001D}.
+{struct, le_receiver_test_cp,
+ [
+ {uint8_t, frequency}
+]}.
+{define, 'LE_RECEIVER_TEST_CP_SIZE', 1}.
+
+{define, 'OCF_LE_TRANSMITTER_TEST', 16#001E}.
+{struct, le_transmitter_test_cp,
+ [
+ {uint8_t, frequency},
+ {uint8_t, length},
+ {uint8_t, payload}
+]}.
+{define, 'LE_TRANSMITTER_TEST_CP_SIZE', 3}.
+
+{define, 'OCF_LE_TEST_END', 16#001F}.
+{struct, le_test_end_rp,
+ [
+ {uint8_t, status},
+ {uint16_t, num_pkts}
+]}.
+{define, 'LE_TEST_END_RP_SIZE', 3}.
+
+%% Vendor specific commands
+{define, 'OGF_VENDOR_CMD', 16#3f}.
+
+%% ---- HCI Events ----
+
+{define, 'EVT_INQUIRY_COMPLETE', 16#01}.
+
+{define, 'EVT_INQUIRY_RESULT', 16#02}.
+{struct, inquiry_info,
+ [
+ {bdaddr_t, bdaddr},
+ {uint8_t, pscan_rep_mode},
+ {uint8_t, pscan_period_mode},
+ {uint8_t, pscan_mode},
+ {{uint8_t,3}, dev_class},
+ {uint16_t, clock_offset}
+]}.
+{define, 'INQUIRY_INFO_SIZE', 14}.
+
+{define, 'EVT_CONN_COMPLETE', 16#03}.
+{struct, evt_conn_complete,
+ [
+ {uint8_t, status},
+ {uint16_t, handle},
+ {bdaddr_t, bdaddr},
+ {uint8_t, link_type},
+ {uint8_t, encr_mode}
+]}.
+{define, 'EVT_CONN_COMPLETE_SIZE', 13}. %% should be 11?
+
+{define, 'EVT_CONN_REQUEST', 16#04}.
+{struct, evt_conn_request,
+ [
+ {bdaddr_t, bdaddr},
+ {{uint8_t,3}, dev_class},
+ {uint8_t, link_type}
+]}.
+{define, 'EVT_CONN_REQUEST_SIZE', 10}.
+
+{define, 'EVT_DISCONN_COMPLETE', 16#05}.
+{struct, evt_disconn_complete,
+ [
+ {uint8_t, status},
+ {uint16_t, handle},
+ {uint8_t, reason}
+]}.
+{define, 'EVT_DISCONN_COMPLETE_SIZE', 4}.
+
+{define, 'EVT_AUTH_COMPLETE', 16#06}.
+{struct, evt_auth_complete,
+ [
+ {uint8_t, status},
+ {uint16_t, handle}
+]}.
+{define, 'EVT_AUTH_COMPLETE_SIZE', 3}.
+
+{define, 'EVT_REMOTE_NAME_REQ_COMPLETE', 16#07}.
+{struct, evt_remote_name_req_complete,
+ [
+ {uint8_t, status},
+ {bdaddr_t, bdaddr},
+ {{char,"?HCI_MAX_NAME_LENGTH"}, name}
+]}.
+{define, 'EVT_REMOTE_NAME_REQ_COMPLETE_SIZE', 255}.
+
+{define, 'EVT_ENCRYPT_CHANGE', 16#08}.
+{struct, evt_encrypt_change,
+ [
+ {uint8_t, status},
+ {uint16_t, handle},
+ {uint8_t, encrypt}
+]}.
+{define, 'EVT_ENCRYPT_CHANGE_SIZE', 5}.
+
+{define, 'EVT_CHANGE_CONN_LINK_KEY_COMPLETE', 16#09}.
+{struct, evt_change_conn_link_key_complete,
+ [
+ {uint8_t, status},
+ {uint16_t, handle}
+]}.
+{define, 'EVT_CHANGE_CONN_LINK_KEY_COMPLETE_SIZE', 3}.
+
+{define, 'EVT_MASTER_LINK_KEY_COMPLETE', 16#0A}.
+{struct, evt_master_link_key_complete,
+ [
+ {uint8_t, status},
+ {uint16_t, handle},
+ {uint8_t, key_flag}
+]}.
+{define, 'EVT_MASTER_LINK_KEY_COMPLETE_SIZE', 4}.
+
+{define, 'EVT_READ_REMOTE_FEATURES_COMPLETE', 16#0B}.
+{struct, evt_read_remote_features_complete,
+ [
+ {uint8_t, status},
+ {uint16_t, handle},
+ {{uint8_t,8}, features}
+]}.
+{define, 'EVT_READ_REMOTE_FEATURES_COMPLETE_SIZE', 11}.
+
+{define, 'EVT_READ_REMOTE_VERSION_COMPLETE', 16#0C}.
+{struct, evt_read_remote_version_complete,
+ [
+ {uint8_t, status},
+ {uint16_t, handle},
+ {uint8_t, lmp_ver},
+ {uint16_t, manufacturer},
+ {uint16_t, lmp_subver}
+]}.
+{define, 'EVT_READ_REMOTE_VERSION_COMPLETE_SIZE', 8}.
+
+{define, 'EVT_QOS_SETUP_COMPLETE', 16#0D}.
+{struct, evt_qos_setup_complete,
+ [
+ {uint8_t, status},
+ {uint16_t, handle},
+ {uint8_t, flags}, %% Reserved
+ {hci_qos_t, qos}
+]}.
+{define, 'EVT_QOS_SETUP_COMPLETE_SIZE', "(4 + ?HCI_QOS_CP_SIZE)"}.
+
+{define, 'EVT_CMD_COMPLETE', 16#0E}.
+{struct, evt_cmd_complete,
+ [
+ {uint8_t, ncmd},
+ {uint16_t, opcode}
+]}.
+{define, 'EVT_CMD_COMPLETE_SIZE', 3}.
+
+{define, 'EVT_CMD_STATUS', 16#0F}.
+{struct, evt_cmd_status,
+ [
+ {uint8_t, status},
+ {uint8_t, ncmd},
+ {uint16_t, opcode}
+]}.
+{define, 'EVT_CMD_STATUS_SIZE', 4}.
+
+{define, 'EVT_HARDWARE_ERROR', 16#10}.
+{struct, evt_hardware_error,
+ [
+ {uint8_t, code}
+]}.
+{define, 'EVT_HARDWARE_ERROR_SIZE', 1}.
+
+{define, 'EVT_FLUSH_OCCURRED', 16#11}.
+{struct, evt_flush_occured,
+ [
+ {uint16_t, handle}
+]}.
+{define, 'EVT_FLUSH_OCCURRED_SIZE', 2}.
+
+{define, 'EVT_ROLE_CHANGE', 16#12}.
+{struct, evt_role_change,
+ [
+ {uint8_t, status},
+ {bdaddr_t, bdaddr},
+ {uint8_t, role}
+]}.
+{define, 'EVT_ROLE_CHANGE_SIZE', 8}.
+
+{define, 'EVT_NUM_COMP_PKTS', 16#13}.
+{struct, evt_num_comp_pkts,
+ [
+ {uint8_t, num_hndl}
+ %% variable length part
+ %% {list, num_hndl, uint16_t, hndls}
+]}.
+{define, 'EVT_NUM_COMP_PKTS_SIZE', 1}.
+
+{define, 'EVT_MODE_CHANGE', 16#14}.
+{struct, evt_mode_change,
+ [
+ {uint8_t, status},
+ {uint16_t, handle},
+ {uint8_t, mode},
+ {uint16_t, interval}
+]}.
+{define, 'EVT_MODE_CHANGE_SIZE', 6}.
+
+{define, 'EVT_RETURN_LINK_KEYS', 16#15}.
+{struct, evt_return_link_keys,
+ [
+ {uint8_t, num_keys}
+ %% variable length part
+ %% {list, num_keys, {binary,16}, keys}
+]}.
+{define, 'EVT_RETURN_LINK_KEYS_SIZE', 1}.
+
+{define, 'EVT_PIN_CODE_REQ', 16#16}.
+{struct, evt_pin_code_req,
+ [
+ {bdaddr_t, bdaddr}
+]}.
+{define, 'EVT_PIN_CODE_REQ_SIZE', 6}.
+
+{define, 'EVT_LINK_KEY_REQ', 16#17}.
+{struct, evt_link_key_req,
+ [
+ {bdaddr_t, bdaddr}
+]}.
+{define, 'EVT_LINK_KEY_REQ_SIZE', 6}.
+
+{define, 'EVT_LINK_KEY_NOTIFY', 16#18}.
+{struct, evt_link_key_notify,
+ [
+ {bdaddr_t, bdaddr},
+ {{uint8_t,16}, link_key},
+ {uint8_t, key_type}
+]}.
+{define, 'EVT_LINK_KEY_NOTIFY_SIZE', 23}.
+
+{define, 'EVT_LOOPBACK_COMMAND', 16#19}.
+
+{define, 'EVT_DATA_BUFFER_OVERFLOW', 16#1A}.
+{struct, evt_data_buffer_overflow,
+ [
+ {uint8_t, link_type}
+]}.
+{define, 'EVT_DATA_BUFFER_OVERFLOW_SIZE', 1}.
+
+{define, 'EVT_MAX_SLOTS_CHANGE', 16#1B}.
+{struct, evt_max_slots_change,
+ [
+ {uint16_t, handle},
+ {uint8_t, max_slots}
+]}.
+{define, 'EVT_MAX_SLOTS_CHANGE_SIZE', 3}.
+
+{define, 'EVT_READ_CLOCK_OFFSET_COMPLETE', 16#1C}.
+{struct, evt_read_clock_offset_complete,
+ [
+ {uint8_t, status},
+ {uint16_t, handle},
+ {uint16_t, clock_offset}
+]}.
+{define, 'EVT_READ_CLOCK_OFFSET_COMPLETE_SIZE', 5}.
+
+{define, 'EVT_CONN_PTYPE_CHANGED', 16#1D}.
+{struct, evt_conn_ptype_changed,
+ [
+ {uint8_t, status},
+ {uint16_t, handle},
+ {uint16_t, ptype}
+]}.
+{define, 'EVT_CONN_PTYPE_CHANGED_SIZE', 5}.
+
+{define, 'EVT_QOS_VIOLATION', 16#1E}.
+{struct, evt_qos_violation,
+ [
+ {uint16_t, handle}
+]}.
+{define, 'EVT_QOS_VIOLATION_SIZE', 2}.
+
+{define, 'EVT_PSCAN_REP_MODE_CHANGE', 16#20}.
+{struct, evt_pscan_rep_mode_change,
+ [
+ {bdaddr_t, bdaddr},
+ {uint8_t, pscan_rep_mode}
+]}.
+{define, 'EVT_PSCAN_REP_MODE_CHANGE_SIZE', 7}.
+
+{define, 'EVT_FLOW_SPEC_COMPLETE', 16#21}.
+{struct, evt_flow_spec_complete,
+ [
+ {uint8_t, status},
+ {uint16_t, handle},
+ {uint8_t, flags},
+ {uint8_t, direction},
+ {hci_qos_t, qos}
+]}.
+{define, 'EVT_FLOW_SPEC_COMPLETE_SIZE', "(5 + ?HCI_QOS_CP_SIZE)"}.
+
+{define, 'EVT_INQUIRY_RESULT_WITH_RSSI', 16#22}.
+{struct, inquiry_info_with_rssi,
+ [
+ {bdaddr_t, bdaddr},
+ {uint8_t, pscan_rep_mode},
+ {uint8_t, pscan_period_mode},
+ {{uint8_t,3}, dev_class},
+ {uint16_t, clock_offset},
+ {int8_t, rssi}
+]}.
+{define, 'INQUIRY_INFO_WITH_RSSI_SIZE', 14}.
+{struct, inquiry_info_with_rssi_and_pscan_mode,
+ [
+ {bdaddr_t, bdaddr},
+ {uint8_t, pscan_rep_mode},
+ {uint8_t, pscan_period_mode},
+ {uint8_t, pscan_mode},
+ {{uint8_t,3}, dev_class},
+ {uint16_t, clock_offset},
+ {int8_t, rssi}
+]}.
+{define, 'INQUIRY_INFO_WITH_RSSI_AND_PSCAN_MODE_SIZE', 15}.
+
+{define, 'EVT_READ_REMOTE_EXT_FEATURES_COMPLETE', 16#23}.
+{struct, evt_read_remote_ext_features_complete,
+ [
+ {uint8_t, status},
+ {uint16_t, handle},
+ {uint8_t, page_num},
+ {uint8_t, max_page_num},
+ {{uint8_t,8}, features}
+]}.
+{define, 'EVT_READ_REMOTE_EXT_FEATURES_COMPLETE_SIZE', 13}.
+
+{define, 'EVT_SYNC_CONN_COMPLETE', 16#2C}.
+{struct, evt_sync_conn_complete,
+ [
+ {uint8_t, status},
+ {uint16_t, handle},
+ {bdaddr_t, bdaddr},
+ {uint8_t, link_type},
+ {uint8_t, trans_interval},
+ {uint8_t, retrans_window},
+ {uint16_t, rx_pkt_len},
+ {uint16_t, tx_pkt_len},
+ {uint8_t, air_mode}
+]}.
+{define, 'EVT_SYNC_CONN_COMPLETE_SIZE', 17}.
+
+{define, 'EVT_SYNC_CONN_CHANGED', 16#2D}.
+{struct, evt_sync_conn_changed,
+ [
+ {uint8_t, status},
+ {uint16_t, handle},
+ {uint8_t, trans_interval},
+ {uint8_t, retrans_window},
+ {uint16_t, rx_pkt_len},
+ {uint16_t, tx_pkt_len}
+]}.
+{define, 'EVT_SYNC_CONN_CHANGED_SIZE', 9}.
+
+{define, 'EVT_SNIFF_SUBRATING', 16#2E}.
+{struct, evt_sniff_subrating,
+ [
+ {uint8_t, status},
+ {uint16_t, handle},
+ {uint16_t, max_tx_latency},
+ {uint16_t, max_rx_latency},
+ {uint16_t, min_remote_timeout},
+ {uint16_t, min_local_timeout}
+]}.
+{define, 'EVT_SNIFF_SUBRATING_SIZE', 11}.
+
+{define, 'EVT_EXTENDED_INQUIRY_RESULT', 16#2F}.
+{struct, extended_inquiry_info,
+ [
+ {bdaddr_t, bdaddr},
+ {uint8_t, pscan_rep_mode},
+ {uint8_t, pscan_period_mode},
+ {{uint8_t,3}, dev_class},
+ {uint16_t, clock_offset},
+ {int8_t, rssi},
+ {{uint8_t,"?HCI_MAX_EIR_LENGTH"}, data}
+]}.
+{define, 'EXTENDED_INQUIRY_INFO_SIZE', 254}.
+
+{define, 'EVT_ENCRYPTION_KEY_REFRESH_COMPLETE', 16#30}.
+{struct, evt_encryption_key_refresh_complete,
+ [
+ {uint8_t, status},
+ {uint16_t, handle}
+]}.
+{define, 'EVT_ENCRYPTION_KEY_REFRESH_COMPLETE_SIZE', 3}.
+
+{define, 'EVT_IO_CAPABILITY_REQUEST', 16#31}.
+{struct, evt_io_capability_request,
+ [
+ {bdaddr_t, bdaddr}
+]}.
+{define, 'EVT_IO_CAPABILITY_REQUEST_SIZE', 6}.
+
+{define, 'EVT_IO_CAPABILITY_RESPONSE', 16#32}.
+{struct, evt_io_capability_response,
+ [
+ {bdaddr_t, bdaddr},
+ {uint8_t, capability},
+ {uint8_t, oob_data},
+ {uint8_t, authentication}
+]}.
+{define, 'EVT_IO_CAPABILITY_RESPONSE_SIZE', 9}.
+
+{define, 'EVT_USER_CONFIRM_REQUEST', 16#33}.
+{struct, evt_user_confirm_request,
+ [
+ {bdaddr_t, bdaddr},
+ {uint32_t, passkey}
+]}.
+{define, 'EVT_USER_CONFIRM_REQUEST_SIZE', 10}.
+
+{define, 'EVT_USER_PASSKEY_REQUEST', 16#34}.
+{struct, evt_user_passkey_request,
+ [
+ {bdaddr_t, bdaddr}
+]}.
+{define, 'EVT_USER_PASSKEY_REQUEST_SIZE', 6}.
+
+{define, 'EVT_REMOTE_OOB_DATA_REQUEST', 16#35}.
+{struct, evt_remote_oob_data_request,
+ [
+ {bdaddr_t, bdaddr}
+]}.
+{define, 'EVT_REMOTE_OOB_DATA_REQUEST_SIZE', 6}.
+
+{define, 'EVT_SIMPLE_PAIRING_COMPLETE', 16#36}.
+{struct, evt_simple_pairing_complete,
+ [
+ {uint8_t, status},
+ {bdaddr_t, bdaddr}
+]}.
+{define, 'EVT_SIMPLE_PAIRING_COMPLETE_SIZE', 7}.
+
+{define, 'EVT_LINK_SUPERVISION_TIMEOUT_CHANGED', 16#38}.
+{struct, evt_link_supervision_timeout_changed,
+ [
+ {uint16_t, handle},
+ {uint16_t, timeout}
+]}.
+{define, 'EVT_LINK_SUPERVISION_TIMEOUT_CHANGED_SIZE', 4}.
+
+{define, 'EVT_ENHANCED_FLUSH_COMPLETE', 16#39}.
+{struct, evt_enhanced_flush_complete,
+ [
+ {uint16_t, handle}
+]}.
+{define, 'EVT_ENHANCED_FLUSH_COMPLETE_SIZE', 2}.
+
+{define, 'EVT_USER_PASSKEY_NOTIFY', 16#3B}.
+{struct, evt_user_passkey_notify,
+ [
+ {bdaddr_t, bdaddr},
+ {uint32_t, passkey},
+ {uint8_t, entered}
+]}.
+{define, 'EVT_USER_PASSKEY_NOTIFY_SIZE', 11}.
+
+{define, 'EVT_KEYPRESS_NOTIFY', 16#3C}.
+{struct, evt_keypress_notify,
+ [
+ {bdaddr_t, bdaddr},
+ {uint8_t, type}
+]}.
+{define, 'EVT_KEYPRESS_NOTIFY_SIZE', 7}.
+
+{define, 'EVT_REMOTE_HOST_FEATURES_NOTIFY', 16#3D}.
+{struct, evt_remote_host_features_notify,
+ [
+ {bdaddr_t, bdaddr},
+ {{uint8_t,8}, features}
+]}.
+{define, 'EVT_REMOTE_HOST_FEATURES_NOTIFY_SIZE', 14}.
+
+{define, 'EVT_LE_META_EVENT', 16#3E}.
+{struct, evt_le_meta_event,
+ [
+ {uint8_t, subevent},
+ {{uint8_t,0}, data}
+]}.
+{define, 'EVT_LE_META_EVENT_SIZE', 1}.
+
+{define, 'EVT_LE_CONN_COMPLETE', 16#01}.
+{struct, evt_le_connection_complete,
+ [
+ {uint8_t, status},
+ {uint16_t, handle},
+ {uint8_t, role},
+ {uint8_t, peer_bdaddr_type},
+ {bdaddr_t, peer_bdaddr},
+ {uint16_t, interval},
+ {uint16_t, latency},
+ {uint16_t, supervision_timeout},
+ {uint8_t, master_clock_accuracy}
+]}.
+{define, 'EVT_LE_CONN_COMPLETE_SIZE', 18}.
+
+{define, 'EVT_LE_ADVERTISING_REPORT', 16#02}.
+{struct, le_advertising_info,
+ [
+ {uint8_t, evt_type},
+ {uint8_t, bdaddr_type},
+ {bdaddr_t, bdaddr},
+ {uint8_t, length},
+ {{uint8_t,0}, data}
+]}.
+{define, 'LE_ADVERTISING_INFO_SIZE', 9}.
+
+{define, 'EVT_LE_CONN_UPDATE_COMPLETE', 16#03}.
+{struct, evt_le_connection_update_complete,
+ [
+ {uint8_t, status},
+ {uint16_t, handle},
+ {uint16_t, interval},
+ {uint16_t, latency},
+ {uint16_t, supervision_timeout}
+]}.
+{define, 'EVT_LE_CONN_UPDATE_COMPLETE_SIZE', 9}.
+
+{define, 'EVT_LE_READ_REMOTE_USED_FEATURES_COMPLETE', 16#04}.
+{struct, evt_le_read_remote_used_features_complete,
+ [
+ {uint8_t, status},
+ {uint16_t, handle},
+ {{uint8_t,8}, features}
+]}.
+{define, 'EVT_LE_READ_REMOTE_USED_FEATURES_COMPLETE_SIZE', 11}.
+
+{define, 'EVT_LE_LTK_REQUEST', 16#05}.
+{struct, evt_le_long_term_key_request,
+ [
+ {uint16_t, handle},
+ {uint64_t, random},
+ {uint16_t, diversifier}
+]}.
+{define, 'EVT_LE_LTK_REQUEST_SIZE', 12}.
+
+{define, 'EVT_PHYSICAL_LINK_COMPLETE', 16#40}.
+{struct, evt_physical_link_complete,
+ [
+ {uint8_t, status},
+ {uint8_t, handle}
+]}.
+{define, 'EVT_PHYSICAL_LINK_COMPLETE_SIZE', 2}.
+
+{define, 'EVT_CHANNEL_SELECTED', 16#41}.
+
+{define, 'EVT_DISCONNECT_PHYSICAL_LINK_COMPLETE', 16#42}.
+{struct, evt_disconn_physical_link_complete,
+ [
+ {uint8_t, status},
+ {uint8_t, handle},
+ {uint8_t, reason}
+]}.
+{define, 'EVT_DISCONNECT_PHYSICAL_LINK_COMPLETE_SIZE', 3}.
+
+{define, 'EVT_PHYSICAL_LINK_LOSS_EARLY_WARNING', 16#43}.
+{struct, evt_physical_link_loss_warning,
+ [
+ {uint8_t, handle},
+ {uint8_t, reason}
+]}.
+{define, 'EVT_PHYSICAL_LINK_LOSS_WARNING_SIZE', 2}.
+
+{define, 'EVT_PHYSICAL_LINK_RECOVERY', 16#44}.
+{struct, evt_physical_link_recovery,
+ [
+ {uint8_t, handle}
+]}.
+{define, 'EVT_PHYSICAL_LINK_RECOVERY_SIZE', 1}.
+
+{define, 'EVT_LOGICAL_LINK_COMPLETE', 16#45}.
+{struct, evt_logical_link_complete,
+ [
+ {uint8_t, status},
+ {uint16_t, log_handle},
+ {uint8_t, handle},
+ {uint8_t, tx_flow_id}
+]}.
+{define, 'EVT_LOGICAL_LINK_COMPLETE_SIZE', 5}.
+
+{define, 'EVT_DISCONNECT_LOGICAL_LINK_COMPLETE', 16#46}.
+
+{define, 'EVT_FLOW_SPEC_MODIFY_COMPLETE', 16#47}.
+{struct, evt_flow_spec_modify_complete,
+ [
+ {uint8_t, status},
+ {uint16_t, handle}
+]}.
+{define, 'EVT_FLOW_SPEC_MODIFY_COMPLETE_SIZE', 3}.
+
+{define, 'EVT_NUMBER_COMPLETED_BLOCKS', 16#48}.
+
+{define, 'EVT_AMP_STATUS_CHANGE', 16#4D}.
+{struct, evt_amp_status_change,
+ [
+ {uint8_t, status},
+ {uint8_t, amp_status}
+]}.
+{define, 'EVT_AMP_STATUS_CHANGE_SIZE', 2}.
+
+{define, 'EVT_TESTING', 16#FE}.
+
+{define, 'EVT_VENDOR', 16#FF}.
+
+%% Internal events generated by BlueZ stack
+{define, 'EVT_STACK_INTERNAL', 16#FD}.
+{struct, evt_stack_internal,
+ [
+ {uint16_t, type},
+ {{uint8_t,0}, data}
+]}.
+{define, 'EVT_STACK_INTERNAL_SIZE', 2}.
+
+{define, 'EVT_SI_DEVICE', 16#01}.
+{struct, evt_si_device,
+ [
+ {uint16_t, event},
+ {uint16_t, dev_id}
+]}.
+{define, 'EVT_SI_DEVICE_SIZE', 4}.
+
diff --git a/deps/bt/rebar.config b/deps/bt/rebar.config
new file mode 100644
index 0000000..b9f2f2a
--- /dev/null
+++ b/deps/bt/rebar.config
@@ -0,0 +1,37 @@
+%% -*- erlang -*-
+%% Config file for bt-application
+%%
+%%
+{deps, [ {dthread, ".*", {git, "https://github.com/tonyrog/dthread.git"}}]}.
+{erl_opts, [debug_info, fail_on_warning]}.
+
+{port_env, [
+ {"CFLAGS", "$CFLAGS -DDLOG_DEFAULT=DLOG_INFO -Wall -Wextra -Wswitch-default -Wswitch-enum -fno-common -DNO_ERL_DRIVER -I $REBAR_DEPS_DIR"},
+ {"(darwin)", "CFLAGS", "$CFLAGS -ObjC"},
+ {"(darwin)", "LDFLAGS", "$LDFLAGS -framework IOBluetooth -framework CoreFoundation -framework Foundation $REBAR_DEPS_DIR/dthread/c_src/dlog.o"},
+
+ {"(linux)", "LDFLAGS", "$LDFLAGS -lbluetooth $REBAR_DEPS_DIR/dthread/c_src/dlog.o"}
+ ]}.
+
+%% first version is a regular port program
+{port_specs, [
+ {"(darwin)", "priv/bt", [ "c_src/bt_macos_drv.c",
+ "c_src/bt_sub.c"
+ ]},
+ {"(linux)", "priv/bt", [ "c_src/bt_linux_drv.c",
+ "c_src/bt_sub.c",
+ "c_src/bt_poll.c"
+ ]},
+
+ {"(linux)", "priv/hci_drv.so",
+ [ "c_src/hci_drv.c" ]}
+
+ ]}.
+
+{erl_first_files,
+ [
+ "src/bt_make_hci_api.erl"
+ ]}.
+
+
+
diff --git a/deps/bt/src/bt.app.src b/deps/bt/src/bt.app.src
new file mode 100644
index 0000000..bbc29a0
--- /dev/null
+++ b/deps/bt/src/bt.app.src
@@ -0,0 +1,11 @@
+
+{application, bt,
+ [{description, "Bluetooth driver"},
+ {vsn, git},
+ {registered, [bt_drv]},
+ {mod,{bt_app,[]}},
+ {env, []},
+ {applications,[kernel,stdlib]}
+ ]}.
+
+
diff --git a/deps/bt/src/bt.erl b/deps/bt/src/bt.erl
new file mode 100644
index 0000000..06beb35
--- /dev/null
+++ b/deps/bt/src/bt.erl
@@ -0,0 +1,355 @@
+%%%---- BEGIN COPYRIGHT -------------------------------------------------------
+%%%
+%%% Copyright (C) 2006 - 2014, Rogvall Invest AB, <tony@rogvall.se>
+%%%
+%%% This software is licensed as described in the file COPYRIGHT, which
+%%% you should have received as part of this distribution. The terms
+%%% are also available at http://www.rogvall.se/docs/copyright.txt.
+%%%
+%%% You may opt to use, copy, modify, merge, publish, distribute and/or sell
+%%% copies of the Software, and permit persons to whom the Software is
+%%% furnished to do so, under the terms of the COPYRIGHT file.
+%%%
+%%% This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY
+%%% KIND, either express or implied.
+%%%
+%%%---- END COPYRIGHT ---------------------------------------------------------
+%%% File : bt.erl
+%%% Author : Tony Rogvall <tony@iMac.local>
+%%% Description : Bluetooth utilities
+%%% Created : 31 Jan 2006 by Tony Rogvall <tony@iMac.local>
+
+-module(bt).
+
+-export([start/0, stop/0]).
+-export([i/0, i/1]).
+-export([li/0]).
+-export([s/1, s/2]).
+-export([debug/0, debug/1]).
+-export([scan/1, scan/3]).
+-export([getaddr/1, getaddr_by_name/1]).
+-export([format_address/1]).
+-export([service_info/1, service_info/2]).
+-export([rfcomm_channel/2]).
+-export([decode_service/1]).
+
+-import(lists, [foreach/2, map/2]).
+
+-include("../include/bt.hrl").
+-include("../include/uuid.hrl").
+-include("../include/sdp.hrl").
+
+start() ->
+ application:start(bt).
+
+stop() -> %% restart=
+ bt_drv:stop().
+
+
+debug() ->
+ bt_drv:debug(debug).
+
+debug(true) ->
+ bt_drv:debug(debug);
+debug(false) ->
+ bt_drv:debug(none);
+debug(Level) ->
+ bt_drv:debug(Level).
+
+%%
+%% Dump device information
+%%
+i() ->
+ {ok,Devices} = bt_drv:devices(),
+ foreach(fun(A) -> i(A) end, Devices).
+
+i(paired) ->
+ {ok, Devices} = bt_drv:paired_devices(),
+ foreach(fun(A) -> i(A) end, Devices);
+i(favorite) ->
+ {ok, Devices} = bt_drv:favorite_devices(),
+ foreach(fun(A) -> i(A) end, Devices);
+i(BtAddr) ->
+ {ok,A} = getaddr(BtAddr),
+ case bt_drv:device_info(A, [inquiry,update]) of
+ {ok, [{inquiry,?never},{update,_}]} ->
+ io:format("Address: ~s\n", [format_address(A)]);
+ {ok, [{inquiry,_InQuiry},{update,Update}]}->
+ io:format("Address: ~s\n", [format_address(A)]),
+ {ok,DevInfo} = bt_drv:device_info(A,[name,
+ is_paired,
+ is_favorite,
+ is_connected,
+ class]),
+ foreach(fun({class,Value}) ->
+ {Service,Major,Minor} = bt_drv:decode_class(Value),
+ io:format(" major: ~p\n", [Major]),
+ io:format(" minor: ~p\n", [Minor]),
+ io:format(" service: ~p\n", [Service]);
+ ({name,Name}) ->
+ io:format(" name: ~s\n", [Name]);
+ ({What,Value}) ->
+ io:format(" ~p: ~p\n", [What,Value])
+ end, DevInfo),
+ if Update == ?never ->
+ ok;
+ true ->
+ {ok,SdpInfo} = bt_drv:service_info(A),
+ io:format(" Profiles:"),
+ foreach(
+ fun(Service) ->
+ As = map(fun(A1) ->
+ bt_sdp:decode(A1)
+ end, Service),
+ case lists:keysearch(256, 1, As) of
+ false -> ok;
+ {value,{_,{text,Name}}} ->
+ io:format(" ~s,", [Name])
+ end
+ end, SdpInfo),
+ io:format("\n")
+ end;
+ Error -> Error
+ end.
+
+%%
+%% Dump information about the local bluetooth adapter
+%%
+li() ->
+ case bt_drv:local_info([name,class,address,discoverable,power_state]) of
+ {ok,Info} ->
+ io:format("Local Adapter:\n",[]),
+ foreach(fun({class,Value}) ->
+ {Service,Major,Minor} = bt_drv:decode_class(Value),
+ io:format(" major: ~p\n", [Major]),
+ io:format(" minor: ~p\n", [Minor]),
+ io:format(" service: ~p\n", [Service]);
+ ({name,Name}) ->
+ io:format(" name: ~s\n", [Name]);
+ ({address,Addr}) ->
+ io:format(" addr: ~s\n", [format_address(Addr)]);
+ ({What,Value}) ->
+ io:format(" ~p: ~p\n", [What,Value])
+ end, Info);
+ Error ->
+ Error
+ end.
+
+%%
+%% Dump service information
+%% s(Addr [UUID])
+%%
+%% Addr is either the Bluetooth address (as string or tuple) or
+%% the name of the device (a bit slow)
+%% UUID is UUID16 | UUID32 | UUID128 | Symbolic-Name
+%%
+s(Addr) ->
+ case bt_drv:service_info(Addr,<<>>) of
+ {ok, Info} when is_list(Info) ->
+ foreach(
+ fun(Service) when is_list(Service) ->
+ s_serv(Service),
+ io:format("\n")
+ end, Info);
+ Error ->
+ Error
+ end.
+
+s(Addr, UUID) when is_binary(UUID), size(UUID) > 0 ->
+ case bt_drv:service_info(Addr,UUID) of
+ {ok, Service} ->
+ s_serv(Service);
+ Error ->
+ Error
+ end;
+s(Addr, Name) when is_list(Name) ->
+ UUID = bt_sdp:string_to_uuid(Name),
+ case bt_drv:service_info(Addr,UUID) of
+ {ok, Service} ->
+ s_serv(Service);
+ Error ->
+ Error
+ end.
+
+s_serv(Attributes=[A1|_]) when is_binary(A1) ->
+ Attrs0 = map(fun(A) -> bt_sdp:decode(A) end, Attributes),
+ LangList = [{_,_,Base}|_] = get_language_base(Attrs0, 256),
+ UUID = case lists:keysearch(?ATTR_ServiceClassIDList, 1, Attrs0) of
+ false -> <<>>;
+ {value,{_,{sequence,[{uuid,UUID0}|_]}}} -> UUID0
+ end,
+ %% Fixme: present ServiceName,ServiceDescription,ProviderName with language base
+ %% and delete them before printing further data.
+ ServiceNames = s_attr(?ATTR_ServiceName, LangList, Attrs0, []),
+ ServiceDescs = s_attr(?ATTR_ServiceDescription, LangList, Attrs0, []),
+ ProviderNames = s_attr(?ATTR_ProviderName, LangList, Attrs0, []),
+
+ case ServiceNames of
+ [{_,{text,Name}}|_] -> io:format("Service Name: ~s\n", [Name]);
+ [] -> io:format("Service Name: None\n", [])
+ end,
+
+ case ServiceDescs of
+ [{_,{text,Desc}}|_] -> io:format(" Service Description: ~s\n", [Desc]);
+ [] -> ok
+ end,
+
+ case ProviderNames of
+ [{_,{text,Prov}}|_] -> io:format(" Provider Name: ~s\n", [Prov]);
+ [] -> ok
+ end,
+ Attrs1 = Attrs0 -- (ServiceNames++ServiceDescs++ProviderNames),
+ Attrs = lists:keydelete(?ATTR_LanguageBaseAttributeIDList, 1, Attrs1),
+ foreach(
+ fun ({ID,Value}) ->
+ AttrName = bt_sdp:attribute_to_string(ID,Base,UUID),
+ io:format(" ~s: ~s\n", [AttrName, bt_sdp:value_to_string(Value)])
+ end, Attrs).
+
+s_attr(Attr, [{_Lang,_Env,Base}|Ls], As, Acc) ->
+ case lists:keysearch(Attr+Base, 1, As) of
+ false ->
+ s_attr(Attr, Ls, As, Acc);
+ {value,Value} ->
+ s_attr(Attr, Ls, As, [Value|Acc])
+ end;
+s_attr(_Attr, [], _As, Acc) ->
+ lists:reverse(Acc).
+
+
+get_language_base(Attrs,Default) ->
+ case get_languages(Attrs) of
+ [] -> [{25966,106,Default}];
+ Ls -> Ls
+ end.
+
+get_languages(Attrs) ->
+ case lists:keysearch(?ATTR_LanguageBaseAttributeIDList, 1, Attrs) of
+ {value,{_,{sequence,Seq}}} -> get_lang3(Seq);
+ _ -> []
+ end.
+
+get_lang3([{uint16,Lang},{uint16,Encoding},{uint16,Base}|Ls]) ->
+ [{Lang,Encoding,Base} | get_lang3(Ls)];
+get_lang3([]) ->
+ [].
+
+
+%%
+%% Decode all services on a device
+%%
+
+service_info(Addr) ->
+ case bt_drv:service_info(Addr,<<>>) of
+ {ok, ServiceList} when is_list(hd(ServiceList)) ->
+ map(fun(Attributes) ->
+ decode_service(Attributes)
+ end, ServiceList);
+ Error ->
+ Error
+ end.
+%%
+%%
+%%
+service_info(Addr, Service) when is_list(Service) ->
+ service_info(Addr, bt_sdp:string_to_uuid(Service));
+service_info(Addr, UUID) when is_binary(UUID), size(UUID) > 0 ->
+ case bt_drv:service_info(Addr,UUID) of
+ {ok, Service} when is_binary(hd(Service)) ->
+ decode_service(Service);
+ Error ->
+ Error
+ end.
+%%
+%% Extract rfcomm channel for the given service
+%%
+rfcomm_channel(Addr, Service) when is_list(Service) ->
+ rfcomm_channel(Addr, bt_sdp:string_to_uuid(Service));
+rfcomm_channel(Addr, UUID) when is_binary(UUID), size(UUID) > 0 ->
+ case bt_drv:service_info(Addr,UUID) of
+ {ok, Service} when is_binary(hd(Service)) ->
+ As = decode_service(Service),
+ case lists:keysearch(?ATTR_ProtocolDescriptorList, 1, As) of
+ {value,{_, {sequence,[{sequence,["L2CAP"]},
+ {sequence,["RFCOMM",Channel]}|_]}}} ->
+ {ok,Channel};
+ _ ->
+ {error, no_channel}
+ end;
+ Error -> Error
+ end.
+
+%%
+%% Decode a binary encoded SDP list:
+%% [ binary(<attribute><value>) ] in SDP format
+%%
+
+decode_service(Attributes) when is_binary(hd(Attributes)) ->
+ Attrs = map(fun(A) -> bt_sdp:decode(A) end, Attributes),
+ map(fun({ID,Value}) ->
+ {ID, bt_sdp:decode_sdp_value(Value)}
+ end, Attrs).
+
+getaddr(Name) ->
+ bt_util:getaddr(Name).
+
+getaddr_by_name(Name) ->
+ bt_util:getaddr_by_name(Name).
+
+%%
+%% Inquiry scan:
+%% Note that the Fun can not make meaningfull remote calls
+%% while inquiry is running.
+%%
+%%
+scan(Timeout) ->
+ scan(Timeout, fun(Addr,Acc) -> {continue,[Addr|Acc]} end, []).
+
+scan(Timeout, Fun, Acc) ->
+ case bt_drv:inquiry_start(Timeout) of
+ {ok,Ref} ->
+ receive
+ {bt,Ref,started} ->
+ scan_loop(Ref, Fun, Acc)
+ end;
+ Error ->
+ Error
+ end.
+
+scan_loop(Ref, Fun, Acc) ->
+ receive
+ {bt,Ref,{device,Addr}} ->
+ case Fun(Addr,Acc) of
+ {continue,Acc1} ->
+ scan_loop(Ref, Fun, Acc1);
+ {stop,Acc1} ->
+ bt_drv:inquiry_stop(Ref),
+ {ok,Acc1}
+ end;
+ {bt,Ref, stopped} ->
+ bt_drv:inquiry_stop(Ref),
+ {ok,Acc}
+ end.
+
+%%
+%% Format bluetooth address into a hex string
+%%
+format_address(A) when ?is_bt_address(A) ->
+ case os:type() of
+ {unix,darwin} ->
+ format_address_(A, $-);
+ _ ->
+ format_address_(A, $:)
+ end;
+format_address(<<F,E,D,C,B,A>>) -> %% binary is reverse format
+ format_address({A,B,C,D,E,F}).
+
+format_address_({A,B,C,D,E,F}, S) ->
+ [hexh(A),hexl(A),S,hexh(B),hexl(B),S,hexh(C),hexl(C),S,
+ hexh(D),hexl(D),S,hexh(E),hexl(E),S,hexh(F),hexl(F)].
+
+hexl(A) -> hex1(A band 16#f).
+hexh(A) -> hex1((A bsr 4) band 16#f).
+
+hex1(A) when A < 10 -> A+$0;
+hex1(A) -> (A-10)+$a.
diff --git a/deps/bt/src/bt_app.erl b/deps/bt/src/bt_app.erl
new file mode 100644
index 0000000..7862c56
--- /dev/null
+++ b/deps/bt/src/bt_app.erl
@@ -0,0 +1,33 @@
+%%%---- BEGIN COPYRIGHT -------------------------------------------------------
+%%%
+%%% Copyright (C) 2006 - 2014, Rogvall Invest AB, <tony@rogvall.se>
+%%%
+%%% This software is licensed as described in the file COPYRIGHT, which
+%%% you should have received as part of this distribution. The terms
+%%% are also available at http://www.rogvall.se/docs/copyright.txt.
+%%%
+%%% You may opt to use, copy, modify, merge, publish, distribute and/or sell
+%%% copies of the Software, and permit persons to whom the Software is
+%%% furnished to do so, under the terms of the COPYRIGHT file.
+%%%
+%%% This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY
+%%% KIND, either express or implied.
+%%%
+%%%---- END COPYRIGHT ---------------------------------------------------------
+%%% File : bt_app.erl
+%%% Author : Tony Rogvall <tony@pbook.synap.se>
+%%% Description :
+%%% Created : 2 Feb 2006 by Tony Rogvall <tony@pbook.synap.se>
+
+-module(bt_app).
+
+-behaviour(application).
+-export([start/2,stop/1]).
+
+%% start
+start(_Type, _StartArgs) ->
+ bt_sup:start_link().
+
+%% stop FIXME
+stop(_State) ->
+ ok.
diff --git a/deps/bt/src/bt_drv.erl b/deps/bt/src/bt_drv.erl
new file mode 100644
index 0000000..6f44ac9
--- /dev/null
+++ b/deps/bt/src/bt_drv.erl
@@ -0,0 +1,1398 @@
+%%%---- BEGIN COPYRIGHT -------------------------------------------------------
+%%%
+%%% Copyright (C) 2006 - 2014, Rogvall Invest AB, <tony@rogvall.se>
+%%%
+%%% This software is licensed as described in the file COPYRIGHT, which
+%%% you should have received as part of this distribution. The terms
+%%% are also available at http://www.rogvall.se/docs/copyright.txt.
+%%%
+%%% You may opt to use, copy, modify, merge, publish, distribute and/or sell
+%%% copies of the Software, and permit persons to whom the Software is
+%%% furnished to do so, under the terms of the COPYRIGHT file.
+%%%
+%%% This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY
+%%% KIND, either express or implied.
+%%%
+%%%---- END COPYRIGHT ---------------------------------------------------------
+%%%-------------------------------------------------------------------
+%%% File : bt_drv.erl
+%%% Author : Tony Rogvall <tony@iMac.local>
+%%% Description : Bluetooth driver control
+%%%
+%%% Created : 28 Jan 2006 by Tony Rogvall <tony@iMac.local>
+%%%-------------------------------------------------------------------
+-module(bt_drv).
+
+-behaviour(gen_server).
+
+-include("../include/bt.hrl").
+
+%% API
+-export([start_link/0, start/0, stop/0]).
+-export([ping/0]).
+-export([register/2, unregister/1]).
+-export([devices/0]).
+-export([recent_devices/0]).
+-export([paired_devices/0]).
+-export([favorite_devices/0]).
+-export([device_info/2]).
+-export([local_info/1]).
+-export([service_info/1, service_info/2]).
+-export([service_query/1, service_query/2]).
+-export([service_add/1, service_add_persist/1]).
+-export([service_del/1]).
+-export([service_rfcomm/1]).
+-export([connect/1, connect/2, connect/3, disconnect/1]).
+-export([remote_name/1, remote_name/2]).
+
+-export([inquiry_start/1,
+ inquiry_stop/1,
+ inquiry_flush/1]).
+
+-export([rfcomm_open/2,
+ rfcomm_close/1,
+ rfcomm_send/2,
+ rfcomm_listen/1,
+ rfcomm_accept/1,
+ rfcomm_accept/2,
+ rfcomm_accept/3,
+ rfcomm_mtu/1,
+ rfcomm_channel/1,
+ rfcomm_address/1]).
+
+-export([l2cap_open/2,
+ l2cap_close/1,
+ l2cap_send/2,
+ l2cap_listen/1,
+ l2cap_accept/1,
+ l2cap_mtu/1,
+ l2cap_psm/1,
+ l2cap_address/1]).
+
+-export([debug/1]).
+
+%% utils
+-export([decode_class/1,
+ encode/1, decode/1]).
+
+%% gen_server callbacks
+-export([init/1, handle_call/3, handle_cast/2, handle_info/2,
+ terminate/2, code_change/3]).
+
+%% -define(debug, true).
+
+-ifdef(debug).
+-define(dbg(Fmt,As), io:format("~s:~w:" Fmt "\n", [?FILE,?LINE | As])).
+-else.
+-define(dbg(Fmt,As), ok).
+-endif.
+
+-define(SERVER, bt_drv).
+
+%% -record(session,
+%% {
+%% id,
+%% owner,
+%% tag,
+%% data
+%% }).
+
+%% event subscriptions
+-record(subscription,
+ {
+ id, %% subscription id (32 bit)
+ subscriber, %% event subsriber
+ monitor, %% monitor on subscriber
+ ref, %% event reference
+ tag, %% event tag
+ data %% subscription data
+ }).
+
+-record(wait,
+ {
+ from, %% gen_server From
+ id, %% command id
+ data %% reply style
+ }).
+
+-record(state,
+ {
+ bt_port,
+ cmd_id = 1, %% 1..16#ffffffff command id
+ evt_id = 1, %% 1..16#ffffffff event id
+ wait = [], %% #wait
+ subscription = [], %% #subscription
+ sessions = [], %% #session
+ reg %% table of registrations
+ }).
+
+-define(CMD_PING, 1).
+-define(CMD_RECENT_DEVICES, 2).
+-define(CMD_PAIRED_DEVICES, 3).
+-define(CMD_FAVORITE_DEVICES, 4).
+-define(CMD_INQUIRY_START, 5).
+-define(CMD_INQUIRY_STOP, 6).
+-define(CMD_REMOTE_NAME, 7).
+-define(CMD_CONNECT, 8).
+-define(CMD_DISCONNECT, 9).
+
+-define(CMD_DEVICE_INFO, 10).
+-define(CMD_SERVICE_INFO, 11).
+-define(CMD_SERVICE_QUERY, 12).
+-define(CMD_SERVICE_ADD, 13).
+-define(CMD_SERVICE_DEL, 14).
+-define(CMD_SERVICE_RFCOMM, 15).
+-define(CMD_LOCAL_INFO, 16).
+-define(CMD_DEBUG, 17).
+
+%% RCCOMM Channels
+-define(CMD_RFCOMM_OPEN, 20).
+-define(CMD_RFCOMM_CLOSE, 21).
+-define(CMD_RFCOMM_LISTEN, 22).
+-define(CMD_RFCOMM_SEND, 23).
+-define(CMD_RFCOMM_ACCEPT, 24).
+-define(CMD_RFCOMM_MTU, 25).
+-define(CMD_RFCOMM_ADDRESS, 26).
+-define(CMD_RFCOMM_CHANNEL, 27).
+
+
+%% L2CAP
+-define(CMD_L2CAP_OPEN, 30).
+-define(CMD_L2CAP_CLOSE, 31).
+-define(CMD_L2CAP_LISTEN, 32).
+-define(CMD_L2CAP_SEND, 33).
+-define(CMD_L2CAP_ACCEPT, 34).
+-define(CMD_L2CAP_MTU, 35).
+-define(CMD_L2CAP_ADDRESS, 36).
+-define(CMD_L2CAP_PSM, 37).
+
+
+-define(REPLY_OK, 1).
+-define(REPLY_ERROR, 2).
+-define(REPLY_EVENT, 3).
+
+-define(BOOLEAN, 0).
+-define(UINT8, 1).
+-define(UINT16, 2).
+-define(UINT32, 3).
+-define(UINT64, 4).
+-define(STRING1, 5).
+-define(LIST, 6).
+-define(LIST_END, 7).
+-define(TUPLE, 8).
+-define(TUPLE_END, 9).
+-define(ATOM, 10).
+-define(BINARY, 11).
+-define(INT8, 12).
+-define(INT16, 13).
+-define(INT32, 14).
+-define(INT64, 15).
+-define(FLOAT32, 16).
+-define(FLOAT64, 17).
+-define(STRING4, 18).
+
+-define(ADDR, 100).
+-define(DATE, 101).
+
+
+%% device info codes
+-define(NFO_DEVICE_NAME, 1).
+-define(NFO_DEVICE_CLASS, 2).
+-define(NFO_DEVICE_CLOCK, 3).
+-define(NFO_DEVICE_INQUIRY, 4).
+-define(NFO_DEVICE_ACCESS, 5).
+-define(NFO_DEVICE_UPDATE, 6).
+-define(NFO_DEVICE_IS_FAVORITE, 7).
+-define(NFO_DEVICE_IS_PAIRED, 8).
+-define(NFO_DEVICE_IS_CONNECTED, 9).
+
+%% local info
+-define(NFO_LOCAL_NAME, 1).
+-define(NFO_LOCAL_CLASS, 2).
+-define(NFO_LOCAL_ADDRESS, 3).
+-define(NFO_LOCAL_DISCOVERABLE, 4).
+-define(NFO_LOCAL_POWER_STATE, 5).
+
+%% Service Classes
+-define(CLASS_LIMITED_DISCOVERABLE, 2#00000000001).
+-define(CLASS_POSITIONING, 2#00000001000).
+-define(CLASS_NETWORKING, 2#00000010000).
+-define(CLASS_RENDERING, 2#00000100000).
+-define(CLASS_CAPTURING, 2#00001000000).
+-define(CLASS_OBJECT_TRANSFER, 2#00010000000).
+-define(CLASS_AUDIO, 2#00100000000).
+-define(CLASS_TELEPHONY, 2#01000000000).
+-define(CLASS_INFORMATION, 2#10000000000).
+
+%% Miscellaneous
+-define(CLASS_MAJOR_MISCELLANEOUS, 16#00).
+%% Desktop, Notebook, PDA, Organizers, etc...
+-define(CLASS_MAJOR_COMPUTER, 16#01).
+%% Cellular, Cordless, Payphone, Modem, etc...
+-define(CLASS_MAJOR_PHONE, 16#02).
+%% LAN Access Point
+-define(CLASS_MAJOR_LAN_ACCESSPOINT, 16#03).
+%% Headset, Speaker, Stereo, etc...
+-define(CLASS_MAJOR_AUDIO, 16#04).
+%% Mouse, Joystick, Keyboards, etc...
+-define(CLASS_MAJOR_PERIPHERAL, 16#05).
+%% Printing, scanner, camera, display, etc...
+-define(CLASS_MAJOR_IMAGING, 16#06).
+
+%% FIXME: Add minor per major ..
+
+-define(NEXT_ID(N),
+ if (N)==16#ffffffff -> 1;
+ true -> (N)+1
+ end).
+
+-define(DLOG_DEBUG, 7).
+-define(DLOG_INFO, 6).
+-define(DLOG_NOTICE, 5).
+-define(DLOG_WARNING, 4).
+-define(DLOG_ERROR, 3).
+-define(DLOG_CRITICAL, 2).
+-define(DLOG_ALERT, 1).
+-define(DLOG_EMERGENCY, 0).
+-define(DLOG_NONE, -1).
+
+%%====================================================================
+%% API
+%%====================================================================
+
+ping() ->
+ gen_server:call(?SERVER, ping).
+
+debug(Level) when is_atom(Level) ->
+ gen_server:call(?SERVER, {debug, level(Level)}).
+
+register(Type, MFA={M,F,As}) when is_atom(M),is_atom(F),is_list(As) ->
+ ets:insert(btreg, {Type,MFA}).
+
+unregister(Type) ->
+ ets:delete(btreg, Type).
+
+devices() ->
+ {ok,D1} = recent_devices(),
+ {ok,D2} = paired_devices(),
+ {ok,D3} = favorite_devices(),
+ {ok,ordsets:to_list(ordsets:union([ordsets:from_list(D1),
+ ordsets:from_list(D2),
+ ordsets:from_list(D3)]))}.
+
+recent_devices() ->
+ gen_server:call(?SERVER, recent_devices).
+
+paired_devices() ->
+ gen_server:call(?SERVER, paired_devices).
+
+favorite_devices() ->
+ gen_server:call(?SERVER, favorite_devices).
+
+device_info(Address, Info) ->
+ {ok,Addr} = bt_util:getaddr(Address),
+ case gen_server:call(?SERVER, {device_info, Addr, Info}) of
+ {ok,InfoReply} ->
+ {ok, map2(fun(A,B) -> {A,B} end, Info, InfoReply)};
+ Error ->
+ Error
+ end.
+
+local_info(Info) ->
+ case gen_server:call(?SERVER, {local_info, Info}) of
+ {ok,InfoReply} ->
+ {ok, map2(fun(A,B) -> {A,B} end, Info, InfoReply)};
+ Error ->
+ Error
+ end.
+
+
+service_info(Address) ->
+ service_info(Address,<<>>).
+
+service_info(Address, UUID) ->
+ case bt_util:getaddr(Address) of
+ {ok,Addr} ->
+ gen_server:call(?SERVER, {service_info, Addr, UUID});
+ Error -> Error
+ end.
+
+service_query(Address) ->
+ service_query(Address, << >>).
+
+service_query(Address, UUID) ->
+ case bt_util:getaddr(Address) of
+ {ok,Addr} ->
+ gen_server:call(?SERVER, {service_query, Addr, UUID}, 20000);
+ Error -> Error
+ end.
+
+%% add a "bound" service
+service_add(ServiceRecord) when is_list(ServiceRecord) ->
+ Service = lists:map(fun({ID,Value}) -> bt_sdp:encode(ID,Value) end,
+ ServiceRecord),
+ gen_server:call(?SERVER, {service_add,self(),Service}, 10000).
+
+%% add persistent service (i.e no owner)
+service_add_persist(ServiceRecord) when is_list(ServiceRecord) ->
+ Service = lists:map(fun({ID,Value}) -> bt_sdp:encode(ID,Value) end,
+ ServiceRecord),
+ gen_server:call(?SERVER, {service_add,undefined,Service}, 10000).
+
+service_del(Handle) when is_integer(Handle) ->
+ gen_server:call(?SERVER, {service_del, Handle}, 10000).
+
+service_rfcomm(Handle) when is_integer(Handle) ->
+ gen_server:call(?SERVER, {service_rfcomm, Handle}, 10000).
+
+
+connect(Address) ->
+ {ok,Addr} = bt_util:getaddr(Address),
+ gen_server:call(?SERVER, {connect, Addr}, 20000).
+
+connect(Address,Timeout) ->
+ {ok,Addr} = bt_util:getaddr(Address),
+ gen_server:call(?SERVER, {connect, Addr, Timeout, false}, 20000).
+
+connect(Address,Timeout,Auth) ->
+ {ok,Addr} = bt_util:getaddr(Address),
+ gen_server:call(?SERVER, {connect, Addr, Timeout, Auth}, 20000).
+
+disconnect(Address) ->
+ {ok,Addr} = bt_util:getaddr(Address),
+ gen_server:call(?SERVER, {disconnect, Addr}, 10000).
+
+remote_name(Address) ->
+ remote_name(Address, 0).
+
+remote_name(Address, Timeout) when is_integer(Timeout), Timeout >= 0 ->
+ gen_server:call(?SERVER, {remote_name, Address, Timeout}, 20000).
+
+%%
+%% start bluetooth scan return {ok, Ref} or {error, Reason}
+%% events sent to caller are:
+%% {bt, Ref, started}
+%% {bt, Ref, {device, Address}}
+%% {bt, Ref, stopped}
+%%
+inquiry_start(Secs) ->
+ gen_server:call(?SERVER, {inquiry_start,self(),Secs}).
+
+%%
+%% Stop bluetooth scanning (early) possibly
+%%
+inquiry_stop(Ref) ->
+ Res = gen_server:call(?SERVER, {inquiry_stop,Ref}),
+ inquiry_flush(Ref),
+ Res.
+
+%% remove (only) stopped messages!
+inquiry_flush(Ref) ->
+ receive
+ {bt,Ref,stopped} ->
+ ok
+ after 0 ->
+ ok
+ end.
+
+rfcomm_open(Address, Channel) ->
+ {ok,Addr} = bt_util:getaddr(Address),
+ gen_server:call(?SERVER, {rfcomm_open, self(), Addr, Channel}, 20000).
+
+rfcomm_close(Ref) ->
+ gen_server:call(?SERVER, {rfcomm_close, Ref}).
+
+rfcomm_send(Ref, Data) ->
+ gen_server:call(?SERVER, {rfcomm_send, Ref, Data}).
+
+rfcomm_listen(Channel) ->
+ gen_server:call(?SERVER, {rfcomm_listen,self(),Channel}).
+
+rfcomm_accept(ListenRef) ->
+ rfcomm_accept(ListenRef, infinity).
+
+rfcomm_accept(ListenRef, Timeout) ->
+ rfcomm_accept(ListenRef, Timeout, self()).
+
+rfcomm_accept(ListenRef, Timeout, CtlPid) ->
+ gen_server:call(?SERVER, {rfcomm_accept, CtlPid, ListenRef, Timeout}).
+
+rfcomm_mtu(Ref) ->
+ gen_server:call(?SERVER, {rfcomm_mtu, Ref}).
+
+rfcomm_channel(Ref) ->
+ gen_server:call(?SERVER, {rfcomm_channel, Ref}).
+
+rfcomm_address(Ref) ->
+ gen_server:call(?SERVER, {rfcomm_address, Ref}).
+
+%% L2CAP
+l2cap_open(Address, Psm) ->
+ {ok,Addr} = bt_util:getaddr(Address),
+ gen_server:call(?SERVER, {l2cap_open, self(), Addr, Psm}, 20000).
+
+l2cap_close(Ref) ->
+ gen_server:call(?SERVER, {l2cap_close, Ref}).
+
+l2cap_send(Ref, Data) ->
+ gen_server:call(?SERVER, {l2cap_send, Ref, Data}).
+
+l2cap_listen(Psm) ->
+ gen_server:call(?SERVER, {l2cap_listen,self(),Psm}).
+
+l2cap_accept(ListenRef) ->
+ l2cap_accept(ListenRef, infinity).
+
+l2cap_accept(ListenRef, Timeout) ->
+ gen_server:call(?SERVER, {l2cap_accept,self(),ListenRef,Timeout}).
+
+l2cap_mtu(Ref) ->
+ gen_server:call(?SERVER, {l2cap_mtu, Ref}).
+
+l2cap_psm(Ref) ->
+ gen_server:call(?SERVER, {l2cap_psm, Ref}).
+
+l2cap_address(Ref) ->
+ gen_server:call(?SERVER, {l2cap_address, Ref}).
+
+
+%%--------------------------------------------------------------------
+%% Function: start_link() -> {ok,Pid} | ignore | {error,Error}
+%% Description: Starts the server
+%%--------------------------------------------------------------------
+start() ->
+ gen_server:start({local, ?SERVER}, ?MODULE, [], []).
+
+start_link() ->
+ gen_server:start_link({local, ?SERVER}, ?MODULE, [], []).
+
+stop() ->
+ gen_server:call(?SERVER, stop).
+
+%%====================================================================
+%% gen_server callbacks
+%%====================================================================
+
+%%--------------------------------------------------------------------
+%% Function: init(Args) -> {ok, State} |
+%% {ok, State, Timeout} |
+%% ignore |
+%% {stop, Reason}
+%% Description: Initiates the server
+%%--------------------------------------------------------------------
+init([]) ->
+ PD = case code:priv_dir(bt) of
+ { error, bad_name } -> "./priv/";
+ Res -> Res
+ end,
+
+ Driver = filename:join([PD,"bt"]),
+ Port = open_port({spawn, Driver}, [{packet,4},binary,eof]),
+ Reg = ets:new(btreg, [public, set, named_table]),
+ {ok, #state{ bt_port = Port, reg = Reg }}.
+
+%%--------------------------------------------------------------------
+%% Function: %% handle_call(Request, From, State) -> {reply, Reply, State} |
+%% {reply, Reply, State, Timeout} |
+%% {noreply, State} |
+%% {noreply, State, Timeout} |
+%% {stop, Reason, Reply, State} |
+%% {stop, Reason, State}
+%% Description: Handling call messages
+%%--------------------------------------------------------------------
+handle_call(ping, From, State) ->
+ CmdId = State#state.cmd_id,
+ State1 = bt_command(From, State, ?CMD_PING, CmdId, []),
+ {noreply, State1};
+handle_call({debug,Level}, From, State) ->
+ CmdId = State#state.cmd_id,
+ State1 = bt_command(From, State, ?CMD_DEBUG, CmdId, <<Level:32/signed>>),
+ {noreply, State1};
+handle_call(recent_devices, From, State) ->
+ CmdId = State#state.cmd_id,
+ State1 = bt_command(From, State, ?CMD_RECENT_DEVICES, CmdId, []),
+ {noreply, State1};
+handle_call(paired_devices, From, State) ->
+ CmdId = State#state.cmd_id,
+ State1 = bt_command(From, State, ?CMD_PAIRED_DEVICES, CmdId, []),
+ {noreply, State1};
+handle_call(favorite_devices, From, State) ->
+ CmdId = State#state.cmd_id,
+ State1 = bt_command(From, State, ?CMD_FAVORITE_DEVICES, CmdId, []),
+ {noreply, State1};
+handle_call({device_info,Address,Info}, From, State) ->
+ case is_address(Address) of
+ true ->
+ AddrArg = tuple_to_list(Address),
+ case catch encode_info(Info) of
+ {'EXIT', _} ->
+ {reply, {error, einval}, State};
+ InfoArg ->
+ CmdId = State#state.cmd_id,
+ State1 = bt_command(From, State, ?CMD_DEVICE_INFO, CmdId,
+ [AddrArg, InfoArg]),
+ {noreply, State1}
+ end;
+ false ->
+ {reply, {error, einval}, State}
+ end;
+handle_call({local_info,Info}, From, State) ->
+ case catch encode_linfo(Info) of
+ {'EXIT', _} ->
+ {reply, {error, einval}, State};
+ InfoArg ->
+ CmdId = State#state.cmd_id,
+ State1 = bt_command(From, State, ?CMD_LOCAL_INFO, CmdId,
+ [InfoArg]),
+ {noreply, State1}
+ end;
+handle_call({service_info,Address,UUID}, From, State) ->
+ case is_address(Address) andalso is_binary(UUID) of
+ true ->
+ AddrArg = tuple_to_list(Address),
+ CmdId = State#state.cmd_id,
+ State1 = bt_command(From, State, ?CMD_SERVICE_INFO, CmdId,
+ [AddrArg, UUID]),
+ {noreply, State1};
+ false ->
+ {reply, {error, einval}, State}
+ end;
+handle_call({service_query,Address,UUID}, From, State) ->
+ case is_address(Address) andalso is_binary(UUID) of
+ true ->
+ AddrArg = tuple_to_list(Address),
+ CmdId = State#state.cmd_id,
+ State1 = bt_command(From, State, ?CMD_SERVICE_QUERY, CmdId,
+ [AddrArg, UUID]),
+ {noreply, State1};
+ false ->
+ {reply, {error, einval}, State}
+ end;
+
+handle_call({service_add,Owner,Service}, From, State) ->
+ case lists:all(fun(B) -> is_binary(B) end, Service) of
+ true ->
+ CmdId = State#state.cmd_id,
+ EvtId = State#state.evt_id,
+ Args = [<< EvtId:32 >>,encode(Service)],
+ EvtRef = make_ref(),
+ Mon = if is_pid(Owner) ->erlang:monitor(process, Owner);
+ true -> undefined
+ end,
+ SList = [#subscription { ref=EvtRef,
+ id=EvtId,
+ subscriber=Owner,
+ monitor=Mon,
+ tag = sdp,
+ data = sdp
+ } | State#state.subscription],
+ State1 = bt_command(From,
+ State#state { evt_id = ?NEXT_ID(EvtId),
+ subscription = SList
+ },
+ ?CMD_SERVICE_ADD, CmdId, Args,
+ {reply, EvtRef}),
+ {noreply, State1};
+ false ->
+ {reply, {error, einval}, State}
+ end;
+handle_call({service_del, Handle}, From, State) ->
+ if is_integer(Handle) ->
+ case lists:keysearch(Handle, #subscription.data,State#state.subscription) of
+ {value, S} when S#subscription.tag == sdp ->
+ SList = State#state.subscription -- [S],
+ unmon(S#subscription.monitor),
+ CmdId = State#state.cmd_id,
+ State1 = bt_command(From,
+ State#state { subscription = SList },
+ ?CMD_SERVICE_DEL, CmdId,
+ <<(S#subscription.id):32>>),
+ {noreply, State1};
+ false ->
+ {reply, {error, einval}, State}
+ end;
+ true ->
+ {reply, {error, einval}, State}
+ end;
+handle_call({service_rfcomm, Handle}, From, State) ->
+ if is_integer(Handle) ->
+ case lists:keysearch(Handle, #subscription.data,State#state.subscription) of
+ {value, S} when S#subscription.tag == sdp ->
+ CmdId = State#state.cmd_id,
+ State1 = bt_command(From,State,?CMD_SERVICE_RFCOMM,CmdId,[<<(S#subscription.id):32>>]),
+ {noreply, State1};
+ _ ->
+ {reply, {error, einval}, State}
+ end;
+ true ->
+ {reply, {error, einval}, State}
+ end;
+handle_call({inquiry_start,Pid,Timeout},From,State) ->
+ case is_pid(Pid) andalso is_timeout(Timeout) of
+ true ->
+ CmdId = State#state.cmd_id,
+ EvtId = State#state.evt_id,
+ EvtRef = make_ref(),
+ Mon = erlang:monitor(process, Pid),
+ SList = [#subscription { ref=EvtRef,
+ id=EvtId,
+ subscriber=Pid,
+ monitor=Mon,
+ tag = bt,
+ data = inquiry
+ } | State#state.subscription],
+ State1 = bt_command(From,
+ State#state { evt_id = ?NEXT_ID(EvtId),
+ subscription = SList
+ },
+ ?CMD_INQUIRY_START, CmdId,
+ <<EvtId:32, Timeout:32>>,
+ {reply, EvtRef}),
+ {noreply, State1};
+ false ->
+ {reply, {error, einval}, State}
+ end;
+handle_call({inquiry_stop,Ref}, From, State) ->
+ case lists:keysearch(Ref,#subscription.ref,State#state.subscription) of
+ false ->
+ {reply, {error, einval}, State};
+ {value,S} ->
+ unmon(S#subscription.monitor),
+ SList = State#state.subscription -- [S],
+ State1 = bt_command(From,
+ State#state { subscription = SList },
+ ?CMD_INQUIRY_STOP,
+ State#state.cmd_id,
+ <<(S#subscription.id):32>>),
+ {noreply, State1}
+ end;
+handle_call({remote_name,Address,Timeout},From,State) ->
+ case is_address(Address) andalso
+ is_timeout(Timeout) of
+ true ->
+ AddrArg = tuple_to_list(Address),
+ CmdId = State#state.cmd_id,
+ State1 = bt_command(From, State, ?CMD_REMOTE_NAME, CmdId,
+ [AddrArg, <<Timeout:32>>]),
+ {noreply, State1};
+ false ->
+ {reply, {error, einval}, State}
+ end;
+handle_call({connect, Address},From,State) ->
+ case is_address(Address) of
+ true ->
+ AddrArg = tuple_to_list(Address),
+ CmdId = State#state.cmd_id,
+ State1 = bt_command(From, State, ?CMD_CONNECT, CmdId, [AddrArg]),
+ {noreply, State1};
+ false ->
+ {reply, {error, einval}, State}
+ end;
+handle_call({connect, Address, Timeout, Auth},From,State) ->
+ case is_address(Address) andalso
+ is_timeout(Timeout) andalso
+ (Auth == true orelse Auth == false) of
+ true ->
+ AddrArg = tuple_to_list(Address),
+ AuthArg = if Auth == true -> 1; Auth == false -> 0 end,
+ CmdId = State#state.cmd_id,
+ State1 = bt_command(From, State, ?CMD_CONNECT, CmdId,
+ [AddrArg, <<Timeout:32, AuthArg:8>>]),
+ {noreply, State1};
+ false ->
+ {reply, {error, einval}, State}
+ end;
+handle_call({disconnect, Address},From,State) ->
+ case is_address(Address) of
+ true ->
+ AddrArg = tuple_to_list(Address),
+ CmdId = State#state.cmd_id,
+ State1 = bt_command(From, State, ?CMD_DISCONNECT, CmdId, [AddrArg]),
+ {noreply, State1};
+ false ->
+ {reply, {error, einval}, State}
+ end;
+%% RFCOMM
+handle_call({rfcomm_open, Owner, Address, Channel}, From, State) ->
+ case is_pid(Owner) andalso
+ is_address(Address) andalso
+ is_channel(Channel) of
+ true ->
+ CmdId = State#state.cmd_id,
+ EvtId = State#state.evt_id,
+ AddrArg = tuple_to_list(Address),
+ Args = [<< EvtId:32 >>,AddrArg,<<Channel:8>>],
+ EvtRef = make_ref(),
+ Mon = erlang:monitor(process, Owner),
+ SList = [#subscription { ref=EvtRef,
+ id=EvtId,
+ subscriber=Owner,
+ monitor=Mon,
+ tag = rfcomm,
+ data = rfcomm
+ } | State#state.subscription],
+ State1 = bt_command(From,
+ State#state { evt_id = ?NEXT_ID(EvtId),
+ subscription = SList
+ },
+ ?CMD_RFCOMM_OPEN, CmdId, Args,
+ {reply, EvtRef}),
+ {noreply, State1};
+ false ->
+ {reply, {error, einval}, State}
+ end;
+
+handle_call({rfcomm_listen, Owner, Channel}, From, State) ->
+ case is_pid(Owner) andalso
+ (is_channel(Channel) orelse (Channel==0)) of
+ true ->
+ CmdId = State#state.cmd_id,
+ EvtId = State#state.evt_id,
+ Args = [<< EvtId:32 >>,<<Channel:8>>],
+ EvtRef = make_ref(),
+ Mon = erlang:monitor(process, Owner),
+ SList = [#subscription { ref=EvtRef,
+ id=EvtId,
+ subscriber=Owner,
+ monitor=Mon,
+ tag = rfcomm_listen,
+ data = rfcomm_listen
+ } | State#state.subscription],
+ State1 = bt_command(From,
+ State#state { evt_id = ?NEXT_ID(EvtId),
+ subscription = SList
+ },
+ ?CMD_RFCOMM_LISTEN, CmdId, Args,
+ {reply, EvtRef}),
+ {noreply, State1};
+ false ->
+ {reply, {error, einval}, State}
+ end;
+
+handle_call({rfcomm_accept,Owner,Ref,_Timeout}, From, State) ->
+ case lists:keysearch(Ref,#subscription.ref,State#state.subscription) of
+ {value,S} when S#subscription.tag == rfcomm_listen ->
+ CmdId = State#state.cmd_id,
+ EvtId = State#state.evt_id,
+ Args = [<<EvtId:32, (S#subscription.id):32 >>],
+ EvtRef = make_ref(),
+ Mon = erlang:monitor(process, Owner),
+ SList = [#subscription { ref=EvtRef,
+ id=EvtId,
+ subscriber=Owner,
+ monitor=Mon,
+ tag = rfcomm,
+ data = rfcomm
+ } | State#state.subscription],
+ State1 = bt_command(From,
+ State#state { evt_id = ?NEXT_ID(EvtId),
+ subscription = SList
+ },
+ ?CMD_RFCOMM_ACCEPT, CmdId, Args,
+ {reply, EvtRef}),
+ {noreply, State1};
+ _ ->
+ {reply, {error, einval}, State}
+ end;
+
+handle_call({rfcomm_close, Ref}, From, State) ->
+ case lists:keysearch(Ref,#subscription.ref,State#state.subscription) of
+ {value,S} when S#subscription.tag == rfcomm; S#subscription.tag == rfcomm_listen ->
+ unmon(S#subscription.monitor),
+ SList = State#state.subscription -- [S],
+ State1 = bt_command(From,
+ State#state { subscription = SList },
+ ?CMD_RFCOMM_CLOSE,
+ State#state.cmd_id,
+ <<(S#subscription.id):32>>),
+ {noreply, State1};
+ false ->
+ {reply, {error, einval}, State}
+ end;
+handle_call({rfcomm_send, Ref, Data}, From, State) ->
+ case lists:keysearch(Ref,#subscription.ref,State#state.subscription) of
+ {value,S} when S#subscription.tag == rfcomm ->
+ State1 = bt_command(From,
+ State,
+ ?CMD_RFCOMM_SEND,
+ State#state.cmd_id,
+ [ <<(S#subscription.id):32 >> , Data]),
+ {noreply, State1};
+ false ->
+ {reply, {error, einval}, State}
+ end;
+handle_call({rfcomm_channel, Ref}, From, State) ->
+ case lists:keysearch(Ref,#subscription.ref,State#state.subscription) of
+ {value,S} when S#subscription.tag == rfcomm ->
+ State1 = bt_command(From,
+ State,
+ ?CMD_RFCOMM_CHANNEL,
+ State#state.cmd_id,
+ [ <<(S#subscription.id):32 >>]),
+ {noreply, State1};
+ false ->
+ {reply, {error, einval}, State}
+ end;
+handle_call({rfcomm_mtu, Ref}, From, State) ->
+ case lists:keysearch(Ref,#subscription.ref,State#state.subscription) of
+ {value,S} when S#subscription.tag == rfcomm ->
+ State1 = bt_command(From,
+ State,
+ ?CMD_RFCOMM_MTU,
+ State#state.cmd_id,
+ [ <<(S#subscription.id):32 >>]),
+ {noreply, State1};
+ false ->
+ {reply, {error, einval}, State}
+ end;
+handle_call({rfcomm_address, Ref}, From, State) ->
+ case lists:keysearch(Ref,#subscription.ref,State#state.subscription) of
+ {value,S} when S#subscription.tag == rfcomm ->
+ State1 = bt_command(From,
+ State,
+ ?CMD_RFCOMM_ADDRESS,
+ State#state.cmd_id,
+ [ <<(S#subscription.id):32 >>]),
+ {noreply, State1};
+ false ->
+ {reply, {error, einval}, State}
+ end;
+%% L2CAP
+handle_call({l2cap_open, Owner, Address, Psm}, From, State) ->
+ case is_pid(Owner) andalso
+ is_address(Address) andalso
+ is_psm(Psm) of
+ true ->
+ CmdId = State#state.cmd_id,
+ EvtId = State#state.evt_id,
+ AddrArg = tuple_to_list(Address),
+ Args = [<< EvtId:32 >>,AddrArg,<<Psm:16>>],
+ EvtRef = make_ref(),
+ Mon = erlang:monitor(process, Owner),
+ SList = [#subscription { ref=EvtRef,
+ id=EvtId,
+ subscriber=Owner,
+ monitor=Mon,
+ tag = l2cap,
+ data = l2cap
+ } | State#state.subscription],
+ State1 = bt_command(From,
+ State#state { evt_id = ?NEXT_ID(EvtId),
+ subscription = SList
+ },
+ ?CMD_L2CAP_OPEN, CmdId, Args,
+ {reply, EvtRef}),
+ {noreply, State1};
+ false ->
+ {reply, {error, einval}, State}
+ end;
+
+handle_call({l2cap_listen, Owner, Psm}, From, State) ->
+ case is_pid(Owner) andalso
+ (is_psm(Psm) orelse (Psm=:=0)) of
+ true ->
+ CmdId = State#state.cmd_id,
+ EvtId = State#state.evt_id,
+ Args = [<< EvtId:32 >>,<<Psm:16>>],
+ EvtRef = make_ref(),
+ Mon = erlang:monitor(process, Owner),
+ SList = [#subscription { ref=EvtRef,
+ id=EvtId,
+ subscriber=Owner,
+ monitor=Mon,
+ tag = l2cap_listen,
+ data = l2cap_listen
+ } | State#state.subscription],
+ State1 = bt_command(From,
+ State#state { evt_id = ?NEXT_ID(EvtId),
+ subscription = SList
+ },
+ ?CMD_L2CAP_LISTEN, CmdId, Args,
+ {reply, EvtRef}),
+ {noreply, State1};
+ false ->
+ {reply, {error, einval}, State}
+ end;
+
+handle_call({l2cap_accept,Owner,Ref,_Timeout}, From, State) ->
+ case lists:keysearch(Ref,#subscription.ref,State#state.subscription) of
+ {value,S} when S#subscription.tag == l2cap_listen ->
+ CmdId = State#state.cmd_id,
+ EvtId = State#state.evt_id,
+ Args = [<<EvtId:32, (S#subscription.id):32 >>],
+ EvtRef = make_ref(),
+ Mon = erlang:monitor(process, Owner),
+ SList = [#subscription { ref=EvtRef,
+ id=EvtId,
+ subscriber=Owner,
+ monitor=Mon,
+ tag = l2cap,
+ data = l2cap
+ } | State#state.subscription],
+ State1 = bt_command(From,
+ State#state { evt_id = ?NEXT_ID(EvtId),
+ subscription = SList
+ },
+ ?CMD_L2CAP_ACCEPT, CmdId, Args,
+ {reply, EvtRef}),
+ {noreply, State1};
+ false ->
+ {reply, {error, einval}, State}
+ end;
+
+handle_call({l2cap_close, Ref}, From, State) ->
+ case lists:keysearch(Ref,#subscription.ref,State#state.subscription) of
+ {value,S} when S#subscription.tag == l2cap; S#subscription.tag == l2cap_listen ->
+ unmon(S#subscription.monitor),
+ SList = State#state.subscription -- [S],
+ State1 = bt_command(From,
+ State#state { subscription = SList },
+ ?CMD_L2CAP_CLOSE,
+ State#state.cmd_id,
+ <<(S#subscription.id):32>>),
+ {noreply, State1};
+ false ->
+ {reply, {error, einval}, State}
+ end;
+handle_call({l2cap_send, Ref, Data}, From, State) ->
+ case lists:keysearch(Ref,#subscription.ref,State#state.subscription) of
+ {value,S} when S#subscription.tag == l2cap ->
+ State1 = bt_command(From,
+ State,
+ ?CMD_L2CAP_SEND,
+ State#state.cmd_id,
+ [ <<(S#subscription.id):32 >> , Data]),
+ {noreply, State1};
+ false ->
+ {reply, {error, einval}, State}
+ end;
+handle_call({l2cap_psm, Ref}, From, State) ->
+ case lists:keysearch(Ref,#subscription.ref,State#state.subscription) of
+ {value,S} when S#subscription.tag == l2cap ->
+ State1 = bt_command(From,
+ State,
+ ?CMD_L2CAP_PSM,
+ State#state.cmd_id,
+ [ <<(S#subscription.id):32 >>]),
+ {noreply, State1};
+ false ->
+ {reply, {error, einval}, State}
+ end;
+handle_call({l2cap_mtu, Ref}, From, State) ->
+ case lists:keysearch(Ref,#subscription.ref,State#state.subscription) of
+ {value,S} when S#subscription.tag == l2cap ->
+ State1 = bt_command(From,
+ State,
+ ?CMD_L2CAP_MTU,
+ State#state.cmd_id,
+ [ <<(S#subscription.id):32 >>]),
+ {noreply, State1};
+ false ->
+ {reply, {error, einval}, State}
+ end;
+handle_call({l2cap_address, Ref}, From, State) ->
+ case lists:keysearch(Ref,#subscription.ref,State#state.subscription) of
+ {value,S} when S#subscription.tag == l2cap ->
+ State1 = bt_command(From,
+ State,
+ ?CMD_L2CAP_ADDRESS,
+ State#state.cmd_id,
+ [ <<(S#subscription.id):32 >>]),
+ {noreply, State1};
+ false ->
+ {reply, {error, einval}, State}
+ end;
+%%
+handle_call(stop, _From, State) ->
+ erlang:port_close(State#state.bt_port),
+ {stop, normal, ok, State};
+handle_call(_Request, _From, State) ->
+ {reply, {error, bad_call}, State}.
+
+%%--------------------------------------------------------------------
+%% Function: handle_cast(Msg, State) -> {noreply, State} |
+%% {noreply, State, Timeout} |
+%% {stop, Reason, State}
+%% Description: Handling cast messages
+%%--------------------------------------------------------------------
+handle_cast(_Msg, State) ->
+ {noreply, State}.
+
+%%--------------------------------------------------------------------
+%% Function: handle_info(Info, State) -> {noreply, State} |
+%% {noreply, State, Timeout} |
+%% {stop, Reason, State}
+%% Description: Handling all non call/cast messages
+%%--------------------------------------------------------------------
+handle_info({Port,{data,Data}},State) when Port == State#state.bt_port ->
+ ?dbg("Port: data=~p", [Data]),
+ case Data of
+ <<?REPLY_OK, CmdId:32, ReplyData/binary>> ->
+ ?dbg("Got: OK cmdid=~w, data=~p", [CmdId,ReplyData]),
+ State1 = case decode(ReplyData) of
+ false ->
+ bt_reply(CmdId, ok, State);
+ {value,Decoded} ->
+ bt_reply(CmdId, {ok,Decoded}, State)
+ end,
+ {noreply, State1};
+ <<?REPLY_ERROR, CmdId:32, ReplyData/binary>> ->
+ ?dbg("Got: ERROR cmdid=~w, data=~p", [CmdId,ReplyData]),
+ State1 = case decode(ReplyData) of
+ false ->
+ bt_reply(CmdId, error, State);
+ {value,Decoded} ->
+ bt_reply(CmdId, {error,Decoded}, State)
+ end,
+ {noreply, State1};
+ <<?REPLY_EVENT, EvtId:32, EventData/binary>> ->
+ ?dbg("Got: EVENT evtid=~w, data=~p", [EvtId,EventData]),
+ case lists:keysearch(EvtId,#subscription.id,
+ State#state.subscription) of
+ false ->
+ ?dbg("no receipient for evtid=~p data=~p",
+ [EvtId, decode(EventData)]),
+ {noreply, State};
+ {value,S} ->
+ case decode(EventData) of
+ false ->
+ ?dbg("bad event data ~p", [EventData]),
+ {noreply, State};
+ {value, Decoded} ->
+ S#subscription.subscriber !
+ {S#subscription.tag,S#subscription.ref,Decoded},
+ if Decoded == closed ->
+ unmon(S#subscription.monitor),
+ SList = State#state.subscription -- [S],
+ State1 = State#state { subscription = SList },
+ {noreply, State1};
+ true ->
+ {noreply, State}
+ end
+ end
+ end;
+ _Other ->
+ ?dbg("Unexpected data=~999p", [_Other]),
+ {noreply, State}
+ end;
+handle_info({Port,eof}, State) when Port == State#state.bt_port ->
+ ?dbg("bt_drv closed",[]),
+ erlang:port_close(Port),
+ {stop, closed, State};
+handle_info({'DOWN', Ref, process, _Pid, _Reason}, State) ->
+ case lists:keysearch(Ref, #subscription.monitor,State#state.subscription) of
+ false ->
+ ?dbg("handle_info: Down process ~p not subsribing\n", [_Pid]),
+ {noreply, State};
+ {value, S} ->
+ SList = State#state.subscription -- [S],
+ State1 =
+ if S#subscription.data == inquiry ->
+ bt_command(undefined,
+ State#state { subscription = SList },
+ ?CMD_INQUIRY_STOP, 0,
+ <<(S#subscription.id):32>>);
+ S#subscription.data == rfcomm ->
+ bt_command(undefined,
+ State#state { subscription = SList },
+ ?CMD_RFCOMM_CLOSE, 0,
+ <<(S#subscription.id):32>>);
+ S#subscription.data == rfcomm_listen ->
+ bt_command(undefined,
+ State#state { subscription = SList },
+ ?CMD_RFCOMM_CLOSE, 0,
+ <<(S#subscription.id):32>>);
+ S#subscription.data == l2cap ->
+ bt_command(undefined,
+ State#state { subscription = SList },
+ ?CMD_L2CAP_CLOSE, 0,
+ <<(S#subscription.id):32>>);
+ S#subscription.data == l2cap_listen ->
+ bt_command(undefined,
+ State#state { subscription = SList },
+ ?CMD_L2CAP_CLOSE, 0,
+ <<(S#subscription.id):32>>);
+ true ->
+ State#state { subscription = SList }
+ end,
+ {noreply, State1}
+ end;
+handle_info(Info, State) ->
+ io:format("handle_info: got ~p\n", [Info]),
+ {noreply, State}.
+
+%%--------------------------------------------------------------------
+%% Function: terminate(Reason, State) -> void()
+%% Description: This function is called by a gen_server when it is about to
+%% terminate. It should be the opposite of Module:init/1 and do any necessary
+%% cleaning up. When it returns, the gen_server terminates with Reason.
+%% The return value is ignored.
+%%--------------------------------------------------------------------
+terminate(_Reason, _State) ->
+ ok.
+
+%%--------------------------------------------------------------------
+%% Func: code_change(OldVsn, State, Extra) -> {ok, NewState}
+%% Description: Convert process state when code is changed
+%%--------------------------------------------------------------------
+code_change(_OldVsn, State, _Extra) ->
+ {ok, State}.
+
+%%--------------------------------------------------------------------
+%%% Internal functions
+%%--------------------------------------------------------------------
+bt_command(From, State, OpCode, CmdId, Data) ->
+ bt_command(From, State, OpCode, CmdId, Data, reply).
+
+bt_command(From, State, OpCode, CmdId, Data, ReplyData) ->
+ ?dbg("Command: ~w ~w ~w", [OpCode, CmdId, Data]),
+ erlang:port_command(State#state.bt_port, [OpCode, <<CmdId:32>>, Data]),
+ if CmdId == 0 -> %% no reply expected
+ State;
+ true ->
+ Wait = [#wait { from=From, id=CmdId, data=ReplyData} |
+ State#state.wait],
+ State#state { cmd_id = ?NEXT_ID(CmdId), wait = Wait }
+ end.
+
+bt_reply(0, _Reply, State) -> %% global reply
+ ?dbg("bt_drv: error: ~p", [_Reply]),
+ %% maybe send this error to all, and terminate ?
+ State;
+bt_reply(CmdId, Reply, State) ->
+ case lists:keysearch(CmdId, #wait.id, State#state.wait) of
+ {value,W = #wait { data = reply }} ->
+ gen_server:reply(W#wait.from, Reply),
+ Wait = State#state.wait -- [W],
+ State#state { wait = Wait };
+
+ {value,W = #wait { data={reply,EvtRef} }} ->
+ case Reply of
+ ok ->
+ gen_server:reply(W#wait.from, {ok,EvtRef}),
+ Wait = State#state.wait -- [W],
+ State#state { wait = Wait };
+ Other ->
+ ?dbg("other reply: ~p", [Other]),
+ Wait = State#state.wait -- [W],
+ case lists:keysearch(EvtRef, #subscription.ref,
+ State#state.subscription) of
+ false ->
+ gen_server:reply(W#wait.from, Other),
+ State#state { wait = Wait };
+ {value,S} when S#subscription.data == sdp ->
+ %% special case
+ case Other of
+ {ok,Handle} ->
+ S1 = S#subscription { data = Handle }, %% switch to handle
+ gen_server:reply(W#wait.from, Other),
+ SList = lists:keyreplace(EvtRef, #subscription.ref,
+ State#state.subscription, S1),
+ State#state { wait = Wait, subscription = SList };
+ _ ->
+ gen_server:reply(W#wait.from, Other),
+ unmon(S#subscription.monitor),
+ SList = State#state.subscription -- [S],
+ State#state { wait = Wait, subscription = SList }
+ end;
+ {value,S} ->
+ gen_server:reply(W#wait.from, Other),
+ unmon(S#subscription.monitor),
+ SList = State#state.subscription -- [S],
+ State#state { wait = Wait, subscription = SList }
+ end
+ end;
+ false ->
+ io:format("Did not find waiter for cmdid ~w\n", [CmdId]),
+ State
+ end.
+
+%% unmon - ignore if reference is undefined
+unmon(undefined) -> ok;
+unmon(Ref) -> erlang:demonitor(Ref, [flush]).
+
+encode_info([What | Info]) ->
+ case What of
+ name -> [?NFO_DEVICE_NAME | encode_info(Info)];
+ class -> [?NFO_DEVICE_CLASS | encode_info(Info)];
+ clock -> [?NFO_DEVICE_CLOCK | encode_info(Info)];
+ inquiry -> [?NFO_DEVICE_INQUIRY | encode_info(Info)];
+ access -> [?NFO_DEVICE_ACCESS | encode_info(Info)];
+ update -> [?NFO_DEVICE_UPDATE | encode_info(Info)];
+ is_favorite -> [?NFO_DEVICE_IS_FAVORITE | encode_info(Info)];
+ is_paired -> [?NFO_DEVICE_IS_PAIRED | encode_info(Info)];
+ is_connected -> [?NFO_DEVICE_IS_CONNECTED | encode_info(Info)]
+ end;
+encode_info([]) -> [].
+
+encode_linfo([What | Info]) ->
+ case What of
+ name -> [?NFO_LOCAL_NAME | encode_linfo(Info)];
+ class -> [?NFO_LOCAL_CLASS | encode_linfo(Info)];
+ address -> [?NFO_LOCAL_ADDRESS | encode_linfo(Info)];
+ discoverable -> [?NFO_LOCAL_DISCOVERABLE | encode_linfo(Info)];
+ power_state -> [?NFO_LOCAL_POWER_STATE | encode_linfo(Info)]
+ end;
+encode_linfo([]) -> [].
+
+
+decode_class(Class) ->
+ <<Service:11, Major:5, Minor:6,_:2>> = <<Class:24>>,
+ {Service,Major,Minor}.
+
+%% encode into Data format
+encode(Term) ->
+ list_to_binary([enc(Term)]).
+
+enc(true) ->
+ <<?BOOLEAN, 1>>;
+enc(false) ->
+ <<?BOOLEAN, 0>>;
+enc(X) when is_atom(X) ->
+ Val = atom_to_list(X),
+ [<<?ATOM, (length(Val)):8>>, Val];
+enc(X) when is_integer(X) ->
+ if X =< 16#ffffffff -> <<?UINT32, X:32>> ;
+ X >= -16#8000000 -> <<?INT32, X:32>> ;
+ X > 0 -> <<?UINT64, X:64>>;
+ true -> (<<?INT64, X:64>>)
+ end;
+enc(X) when is_float(X) ->
+ <<?FLOAT64, X:64>>;
+enc(X) when is_list(X) ->
+ case is_string(X) of
+ true ->
+ Len = length(X),
+ if Len =< 255 ->
+ [<<?STRING1, Len:8>>, X];
+ true ->
+ [<<?STRING4, Len:32>>, X]
+ end;
+ false ->
+ [?LIST, lists:map(fun(E) -> enc(E) end, X), ?LIST_END]
+ end;
+enc(X) when is_tuple(X) ->
+ [?TUPLE, lists:map(fun(E) -> enc(E) end, tuple_to_list(X)), ?TUPLE_END];
+enc(X) when is_binary(X) ->
+ [<<?BINARY,(size(X)):32>>, X].
+
+is_string([X|Xs]) when X >= 0, X =< 255 -> is_string(Xs);
+is_string([]) -> true;
+is_string(_) -> false.
+
+
+%% decode reply data
+decode(<<>>) -> false;
+decode(Data) -> {value, decode(Data, [])}.
+
+decode(<<>>, [Hd]) -> Hd;
+decode(Data, Stack) ->
+ case Data of
+ <<?LIST, Rest/binary>> ->
+ ?dbg("LIST",[]),
+ decode(Rest, [list|Stack]);
+ <<?TUPLE, Rest/binary>> ->
+ ?dbg("TUPLE",[]),
+ decode(Rest, [tuple|Stack]);
+ <<?BOOLEAN, B, Rest/binary>> ->
+ ?dbg("BOOLEAN:~w",[B]),
+ decode(Rest, [B =/= 0 | Stack]);
+ <<?UINT8, I:8, Rest/binary>> ->
+ ?dbg("UINT8:~w",[I]),
+ decode(Rest, [I|Stack]);
+ <<?UINT16, I:16, Rest/binary>> ->
+ ?dbg("UINT16:~w",[I]),
+ decode(Rest, [I|Stack]);
+ <<?UINT32, I:32, Rest/binary>> ->
+ ?dbg("UINT32:~w",[I]),
+ decode(Rest, [I|Stack]);
+ <<?UINT64, I:64, Rest/binary>> ->
+ ?dbg("UINT64:~w",[I]),
+ decode(Rest, [I|Stack]);
+ <<?INT8, I:8/signed, Rest/binary>> ->
+ ?dbg("INT8:~w",[I]),
+ decode(Rest, [I|Stack]);
+ <<?INT16, I:16/signed, Rest/binary>> ->
+ ?dbg("INT16:~w",[I]),
+ decode(Rest, [I|Stack]);
+ <<?INT32, I:32/signed, Rest/binary>> ->
+ ?dbg("INT32:~w",[I]),
+ decode(Rest, [I|Stack]);
+ <<?INT64, I:64/signed, Rest/binary>> ->
+ ?dbg("INT64:~w",[I]),
+ decode(Rest, [I|Stack]);
+ <<?FLOAT32, F:32/float, Rest/binary>> ->
+ ?dbg("FLOAT32:~w",[F]),
+ decode(Rest, [F|Stack]);
+ <<?FLOAT64, F:64/float, Rest/binary>> ->
+ ?dbg("FLOAT64:~w",[F]),
+ decode(Rest, [F|Stack]);
+ <<?STRING1, Len:8, String:Len/binary, Rest/binary>> ->
+ ?dbg("STRING1: len=~w, ~w",[Len,String]),
+ decode(Rest, [binary_to_list(String) | Stack]);
+ <<?STRING4, Len:32, String:Len/binary, Rest/binary>> ->
+ ?dbg("STRING4: len=~w, ~w",[Len,String]),
+ decode(Rest, [binary_to_list(String) | Stack]);
+ <<?BINARY, Len:32, Bin:Len/binary, Rest/binary>> ->
+ ?dbg("BINARY: len=~w, ~w",[Len,Bin]),
+ decode(Rest, [Bin | Stack]);
+ <<?ATOM, Len:8, Atom:Len/binary, Rest/binary>> ->
+ ?dbg("ATOM: len=~w, ~w",[Len,Atom]),
+ decode(Rest, [list_to_atom(binary_to_list(Atom)) | Stack]);
+ <<?LIST_END, Rest/binary>> ->
+ ?dbg("LIST_END",[]),
+ {L,[_|Stack1]} = lists:splitwith(fun(X) -> X =/= list end, Stack),
+ decode(Rest, [lists:reverse(L) | Stack1]);
+ <<?TUPLE_END, Rest/binary>> ->
+ ?dbg("TUPLE_END",[]),
+ {L,[_|Stack1]}=lists:splitwith(fun(X) -> X =/= tuple end, Stack),
+ decode(Rest, [list_to_tuple(lists:reverse(L)) | Stack1]);
+ %% EXTENSIONS
+ <<?DATE, Date:32, Rest/binary>> ->
+ ?dbg("DATE: ~w",[Date]),
+ DateTime = if Date == 0 -> {{0,0,0},{0,0,0}};
+ true ->
+ Now = {Date div 1000000, Date rem 1000000, 0},
+ calendar:now_to_datetime(Now)
+ end,
+ decode(Rest,[DateTime|Stack]);
+ <<?ADDR, Addr:6/binary, Rest/binary>> ->
+ ?dbg("ADDR: ~w",[Addr]),
+ decode(Rest, [list_to_tuple(binary_to_list(Addr)) | Stack])
+ end.
+
+map2(Fun, [A|As], [B|Bs]) ->
+ [ Fun(A, B) | map2(Fun, As, Bs)];
+map2(_Fun, [], []) ->
+ [].
+
+is_address(Addr) ->
+ if ?is_bt_address(Addr) -> true;
+ true -> false
+ end.
+
+is_channel(C) when is_integer(C), C >= 1, C =< 30 ->
+ true;
+is_channel(_) -> false.
+
+is_psm(C) when is_integer(C), C >= 0, C =< 16#ffff ->
+ true;
+is_psm(_) -> false.
+
+
+is_timeout(T) when is_integer(T), T>=0 ->
+ true;
+is_timeout(_) -> false.
+
+%% convert symbolic to numeric level
+level(true) -> ?DLOG_DEBUG;
+level(false) -> ?DLOG_NONE;
+level(debug) -> ?DLOG_DEBUG;
+level(info) -> ?DLOG_INFO;
+level(notice) -> ?DLOG_NOTICE;
+level(warning) -> ?DLOG_WARNING;
+level(error) -> ?DLOG_ERROR;
+level(critical) -> ?DLOG_CRITICAL;
+level(alert) -> ?DLOG_ALERT;
+level(emergency) -> ?DLOG_EMERGENCY;
+level(none) -> ?DLOG_NONE.
diff --git a/deps/bt/src/bt_erl_server.erl b/deps/bt/src/bt_erl_server.erl
new file mode 100644
index 0000000..f879c92
--- /dev/null
+++ b/deps/bt/src/bt_erl_server.erl
@@ -0,0 +1,108 @@
+%%% @author Tony Rogvall <tony@rogvall.se>
+%%% @copyright (C) 2014, Tony Rogvall
+%%% @doc
+%%% Bluetooth distribution
+%%% @end
+%%% Created : 3 Dec 2014 by Tony Rogvall <tony@rogvall.se>
+
+-module(bt_erl_server).
+
+-export([sdp_info/2]).
+-compile(export_all).
+
+-include("../include/sdp.hrl").
+
+-define(UUID_ErlangNode,
+ <<16#0f1662dc:32,16#0c1e:16,16#4e18:16,16#acd7:16,16#da61165bcd29:48>>).
+
+sdp_info(NodeName,Channel) ->
+ Base1 = 16#0100,
+ [
+ {?ATTR_ServiceRecordHandle, {uint32,0}}, %% Should not be needed ?
+ {?ATTR_ServiceClassIDList, {sequence,[{uuid,?UUID_ErlangNode}]}},
+ {?ATTR_ProtocolDescriptorList,
+ {sequence,[{sequence,[{uuid,?UUID_L2CAP}]},
+ {sequence,[{uuid,?UUID_RFCOMM},{uint8,Channel}]}]}},
+ {?ATTR_BrowseGroupList,
+ {sequence,[{uuid,?UUID_PublicBrowseGroup}]}},
+ {?ATTR_LanguageBaseAttributeIDList,
+ {sequence, [{uint16, ?LANGUAGE($e,$n)},
+ {uint16, ?ENCODING_UTF8},
+ {uint16, Base1}]}},
+ {?ATTR_ServiceName+Base1, {text,NodeName}}].
+
+
+start(NodeName) ->
+ start(NodeName,1).
+start(NodeName, ChannelHint) ->
+ spawn(fun() -> init(NodeName,ChannelHint) end).
+
+init(NodeName, ChannelHint) ->
+ case bt_drv:service_add(sdp_info(NodeName,ChannelHint)) of
+ {ok,Handle} ->
+ case bt_drv:service_rfcomm(Handle) of
+ {ok,RealChannel} ->
+ io:format("Registered server: ~p rfcomm=~p\n",
+ [Handle,RealChannel]),
+ init_rfcomm(RealChannel);
+ Error ->
+ Error
+ end;
+ Error ->
+ Error
+ end.
+
+init_rfcomm(Channel) ->
+ process_flag(trap_exit, true),
+ case rfcomm:listen(Channel) of
+ {ok,ListenRef} ->
+ listen_loop(ListenRef);
+ Error ->
+ Error
+ end.
+
+listen_loop(ListenRef) ->
+ Pid = spawn_link(?MODULE, accept, [self(), ListenRef]),
+ receive
+ {accept,Pid,ok} ->
+ listen_loop(ListenRef);
+ {accept,Pid,{error,Reason}} ->
+ io:format("Accept error ~p\n", [Reason]),
+ listen_loop(ListenRef)
+ end.
+
+accept(Listen, ListenRef) ->
+ case rfcomm:accept(ListenRef) of
+ {ok,ARef} ->
+ receive
+ {rfcomm,ARef,{accept,Address,Channel}} ->
+ Listen ! {accept, self(), ok},
+ io:format("connection from ~w:~w\n", [Address,Channel]),
+ main(ARef,Address,Channel);
+ {rfcomm,ARef,{data,_Data}} ->
+ io:format("error: got data ~p\n", [_Data]),
+ rfcomm:close(ARef),
+ Listen ! {accept, self(), {error,data}},
+ error;
+ {rfcomm,ARef,closed} ->
+ rfcomm:close(ARef),
+ Listen ! {accept, self(), {error,closed}},
+ closed
+ end;
+ Error ->
+ Error
+ end.
+
+
+main(Ref,Address,Channel) ->
+ receive
+ {rfcomm,Ref,{data,Data}} ->
+ rfcomm:send(Ref, ["Echo:",Data]),
+ main(Ref,Address,Channel);
+ {rfcomm,Ref,closed} ->
+ rfcomm:close(Ref),
+ normal;
+ Other ->
+ io:format("bt_erl_server: main other ~w\n", [Other]),
+ main(Ref,Address,Channel)
+ end.
diff --git a/deps/bt/src/bt_iset.erl b/deps/bt/src/bt_iset.erl
new file mode 100644
index 0000000..32c7137
--- /dev/null
+++ b/deps/bt/src/bt_iset.erl
@@ -0,0 +1,457 @@
+%%%---- BEGIN COPYRIGHT -------------------------------------------------------
+%%%
+%%% Copyright (C) 2006 - 2014, Rogvall Invest AB, <tony@rogvall.se>
+%%%
+%%% This software is licensed as described in the file COPYRIGHT, which
+%%% you should have received as part of this distribution. The terms
+%%% are also available at http://www.rogvall.se/docs/copyright.txt.
+%%%
+%%% You may opt to use, copy, modify, merge, publish, distribute and/or sell
+%%% copies of the Software, and permit persons to whom the Software is
+%%% furnished to do so, under the terms of the COPYRIGHT file.
+%%%
+%%% This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY
+%%% KIND, either express or implied.
+%%%
+%%%---- END COPYRIGHT ---------------------------------------------------------
+%%% File : iset.erl
+%%% Author : Tony Rogvall <tony@iMac.local>
+%%% Description : Integer Set operations
+%%% Created : 26 Jan 2006 by Tony Rogvall <tony@iMac.local>
+
+-module(bt_iset).
+
+%%
+%% bt_iset:
+%%
+%% representation:
+%% List of interval and points
+%%
+%% Points
+%% A sorted list of values and/or closed intervals where
+%% intervals are represented as pairs with as {Low,High}.
+%% It may also be the empty list if no specific points are
+%% given even though Max and Min has values.
+%%
+%% operations:
+%% new() -> ISet
+%% from_list([Integer|{RangeStart,RangeStop}]) -> ISet
+%% to_list(ISet) -> List
+%%
+%% add_element(Element, ISet) -> ISet U [Element]
+%% del_element(Element, ISet) -> ISet - [Element]
+%% intersection(ISet1, ISet2) -> (ISet1 ^ ISet2)
+%% subtract(ISet1, ISet2) -> (ISet1 - ISet2)
+%% union(ISet1, ISet2) -> (ISet1 U ISet2)
+%% is_subset(ISet1, ISet2) -> boolean()
+%% is_psubset(ISet1,ISet2) -> boolean()
+%% size(ISet) -> integer()
+%% is_element(Element, ISet) -> boolean()
+%% first(ISet) -> integer() | 'EXIT'
+%% last(ISet) -> integer() | 'EXIT'
+%% format(ISet)
+%%
+
+-export([new/0]).
+-export([add_element/2, del_element/2]).
+-export([add_elements/2, del_elements/2]).
+-export([from_list/1, to_list/1]).
+-export([is_element/2, size/1, is_subset/2, is_psubset/2]).
+-export([first/1, last/1]).
+-export([format/1]).
+-export([union/2, intersection/2, subtract/2]).
+-export([sum/2, product/2, negate/1]).
+-export([map/2, fold/3, filter/2]).
+
+-import(lists, [reverse/1]).
+
+%%
+%% Create a new iset
+%%
+new() -> [].
+
+%%
+%% is_element(N, Domain)
+%%
+%% returns:
+%% true if N is a member of Domain
+%% false otherwise
+%%
+is_element(N, [N | _]) -> true;
+is_element(N, [V | _]) when is_integer(V), N < V -> false;
+is_element(N, [V | L]) when is_integer(V), N > V -> is_element(N,L);
+is_element(N, [{Low,High}|_]) when N >= Low, N =< High -> true;
+is_element(N, [{Low,_High} | _]) when N < Low -> false;
+is_element(N, [{_Low,High} | L]) when N > High -> is_element(N, L);
+is_element(_N, []) -> false.
+
+%%
+%% Calculate size of domain
+%%
+%% returns:
+%% 0 if empty set
+%% N number of members
+%%
+size(L) ->
+ size(L, 0).
+
+size([V | L], N) when is_integer(V) -> size(L, N+1);
+size([{Low, High}|L], N) -> size(L, N+(High-Low)+1);
+size([], N) -> N.
+
+%%
+%% Get maximum and minimum value
+%%
+first([{V,_} | _]) -> V;
+first([V | _]) -> V.
+
+last(D) ->
+ case lists:reverse(D) of
+ [{_,V} | _] -> V;
+ [V | _] -> V
+ end.
+
+%%
+%% Map over iset
+%%
+map(_F, []) ->
+ [];
+map(F, [D|Ds]) when is_integer(D) ->
+ [F(D) | map(F,Ds)];
+map(F, [{L,H}|Ds]) ->
+ map_range(F, L, H, Ds).
+
+map_range(F, I, N, Ds) when I > N ->
+ map(F, Ds);
+map_range(F, I, N, Ds) ->
+ [F(I) | map_range(F,I+1,N,Ds)].
+
+fold(F, Acc, [{A,B}|Es]) ->
+ fold(F,fold_range(F, Acc, A, B),Es);
+fold(F, Acc, [A|Es]) ->
+ fold(F, F(A,Acc), Es);
+fold(_F, Acc, []) ->
+ Acc.
+
+fold_range(_Fun, Acc, I, N) when I > N ->
+ Acc;
+fold_range(Fun, Acc, I, N) ->
+ fold_range(Fun, Fun(I,Acc), I+1, N).
+
+
+filter(F, ISet) ->
+ filter(F, ISet, new()).
+
+filter(F, [{A,B}|Es], ISet) ->
+ filter(F,Es,filter_range(F,A,B,ISet));
+filter(F, [A|Es], ISet) ->
+ case F(A) of
+ true ->
+ filter(F, Es, add_element(A,ISet));
+ false ->
+ filter(F, Es, ISet)
+ end;
+filter(_F, [], ISet) ->
+ ISet.
+
+filter_range(_F, I, N, ISet) when I > N ->
+ ISet;
+filter_range(F, I, N, ISet) ->
+ case F(I) of
+ true -> filter_range(F, I+1, N, add_element(I, ISet));
+ false -> filter_range(F, I+1, N, ISet)
+ end.
+
+%%
+%% Utilities:
+%%
+%% value(A,B) make A-B a domain value
+%% min(A,B) return the minimum of A and B
+%% max(A,B) return the maximum of A and B
+%% min_max(A,B) returns {Min,Max}
+%%
+-define(low(X), if is_tuple((X)) -> element(1,(X));
+ true -> (X)
+ end).
+-define(high(X), if is_tuple((X)) -> element(2,(X));
+ true -> (X)
+ end).
+
+value(N,N) -> N;
+value(Low, High) -> {Low,High}.
+
+min_max(A,B) when A < B -> {A,B};
+min_max(A,B) -> {B,A}.
+
+%%
+%% Check if D1 is a subset D2
+%%
+is_subset(D1,D2) ->
+ case intersection(D1, D2) of
+ D1 -> true;
+ _ -> false
+ end.
+
+%%
+%% Check if D1 is a proper subset of D2
+%%
+is_psubset(D1, D1) -> false;
+is_psubset(D1, D2) -> is_subset(D1,D2).
+
+
+%%
+%% from_list(List, ISet)
+%%
+%% description:
+%% Updates a domain given a list of integers and
+%% intervals and simplifies them as much as possible
+%%
+%% returns:
+%% Domain
+%%
+from_list(List) ->
+ add_elements(List, new()).
+
+to_list(ISet) ->
+ ISet.
+
+add_elements([E|List], ISet) ->
+ add_elements(List, add_element(E,ISet));
+add_elements([], ISet) ->
+ ISet.
+
+%%
+%% add_element(Element, ISet) -> ISet'
+%%
+add_element(E={A,B},ISet) when is_integer(A),is_integer(B),A<B ->
+ union([E], ISet);
+add_element({A,A},ISet) when is_integer(A) ->
+ union([A], ISet);
+add_element(A,ISet) when is_integer(A) ->
+ union([A], ISet).
+
+%%
+%% delete(Points, Domain)
+%%
+%% description:
+%% Remove values from a domain given a list of integers and
+%% intervals and simplifies them as much as possible
+%%
+%% returns:
+%% Domain
+%%
+
+del_elements([E|List], ISet) ->
+ del_elements(List, del_element(E,ISet));
+del_elements([], ISet) -> ISet.
+
+del_element(E={A,B},ISet) when is_integer(A),is_integer(B),A<B ->
+ subtract(ISet, [E]);
+del_element({A,A},ISet) when is_integer(A) ->
+ subtract(ISet, [A]);
+del_element(A,ISet) when is_integer(A) ->
+ subtract(ISet, [A]).
+
+%%
+%% union(Domain1, Domain2)
+%%
+%% description:
+%% Create a union of two domains
+%%
+%%
+union(D,D) -> D;
+union(D1,D2) ->
+ union(D1,D2,new()).
+
+union(D, [], U) -> cons(U,D);
+union([],D, U) -> cons(U,D);
+union(S1=[D1 | D1s], S2=[D2 | D2s], U) ->
+ Min1 = ?low(D1),
+ Max1 = ?high(D1),
+ Min2 = ?low(D2),
+ Max2 = ?high(D2),
+ if Min2 == Max1+1 ->
+ union(D1s,[value(Min1,Max2) | D2s], U);
+ Min1 == Max2+1 ->
+ union([value(Min2,Max1)|D1s], D2s, U);
+ Min2 > Max1 ->
+ union(D1s, S2, [D1|U]);
+ Min1 > Max2 ->
+ union(S1, D2s, [D2|U]);
+ Max1 > Max2 ->
+ union([value(min(Min1,Min2),Max1)|D1s], D2s, U);
+ true ->
+ union(D1s,[value(min(Min1,Min2),Max2)|D2s], U)
+ end.
+
+%%
+%% intersection(ISet1, ISet2)
+%%
+%% description:
+%% Create the intersection between two domains
+%%
+%%
+
+intersection([], _) -> [];
+intersection(_, []) -> [];
+intersection(D1, D2) ->
+ intersection(D1, D2, new()).
+
+intersection(_D, [], I) -> reverse(I);
+intersection([], _D, I) -> reverse(I);
+intersection(S1=[D1|D1s], S2=[D2|D2s], I) ->
+ Min1 = ?low(D1),
+ Max1 = ?high(D1),
+ Min2 = ?low(D2),
+ Max2 = ?high(D2),
+ if Min2 > Max1 ->
+ intersection(D1s, S2, I);
+ Min1 > Max2 ->
+ intersection(S1, D2s, I);
+ Max1<Max2 ->
+ intersection(D1s,S2,
+ [value(max(Min1,Min2),Max1)|I]);
+ true ->
+ intersection(S1, D2s,
+ [value(max(Min1,Min2),Max2)|I])
+ end.
+
+%%
+%% subtract(ISet1,ISet2)
+%%
+%% returns:
+%% The difference between Iset1 and ISet2, by removeing the
+%% values in ISet1 from ISet2 i.e D1 - D2.
+%%
+%%
+
+subtract(D, []) -> D;
+subtract([], _) -> [];
+subtract(D1, D2) ->
+ subtract(D1,D2,new()).
+
+subtract(D1s,[],Diff) -> cons(Diff,D1s);
+subtract(D1s,[D2|D2s],Diff) ->
+ Min2 = ?low(D2),
+ Max2 = ?high(D2),
+ ddiff(D1s, Min2, Max2, D2, D2s, Diff).
+
+ddiff([], _Min2, _Max2, _D2, _D2s, Diff) ->
+ reverse(Diff);
+ddiff([D1 | D1s], Min2, Max2, D2, D2s, Diff) ->
+ Min1 = ?low(D1),
+ Max1 = ?high(D1),
+ ddiffer(Min1, Max1, Min2, Max2, D1, D2, D1s,D2s,Diff).
+
+ddiffer(Min1,Max1,Min2,Max2,D1,D2,D1s,D2s,Diff) ->
+ if Min2 > Max1 ->
+ ddiff(D1s, Min2, Max2, D2, D2s, [D1 | Diff]);
+ Min1 > Max2 ->
+ subtract([D1 | D1s], D2s, Diff);
+ Min1<Min2 ->
+ D = value(Min1, Min2-1),
+ NewD1 = value(Min2, Max1),
+ ddiffer(Min2, Max1, Min2, Max2,
+ NewD1, D2, D1s, D2s, [D|Diff]);
+ Max1 > Max2 ->
+ NewD1 = value(Max2+1, Max1),
+ ddiffer(Max2+1, Max1, Min2, Max2,
+ NewD1, D2, D1s, D2s, Diff);
+ true ->
+ ddiff(D1s, Min2, Max2, D2, D2s, Diff)
+ end.
+
+%%
+%% Arithmetic in universe
+%%
+%% negate(A) Integer negate values in domain A
+%% sum(A,B) Integer addition of values in domains A and B
+%% multiply(A,B) Integer multiplication of values in domains A and B
+%% divide(A,B) Integer division of values in domain A with values in B
+%%
+
+%%
+%% Negate domain
+%% returns:
+%% { x | x = -a, a in A}
+%%
+negate(A) ->
+ negate(A, new()).
+
+negate([{I,J} | As], Neg) ->
+ negate(As, [value(-J,-I)|Neg]);
+negate([I | As], Neg) ->
+ negate(As, [-I|Neg]);
+negate([], Neg) ->
+ Neg.
+
+%%
+%% Sum of domains
+%% returns:
+%% { x | x = a+b, a in A, b in B }
+%%
+sum(A, B) ->
+ sum(A,B,new()).
+
+sum([A|As], Bs, Sum) ->
+ MinA = ?low(A),
+ MaxA = ?high(A),
+ sum(As,Bs,union(sum1(Bs,MinA,MaxA,new()), Sum));
+sum([], _, Sum) ->
+ Sum.
+
+sum1([B|Bs],MinA,MaxA,Sum) ->
+ MinB = ?low(B),
+ MaxB = ?high(B),
+ sum1(Bs, MinA, MaxA, union([value(MinA+MinB,MaxA+MaxB)], Sum));
+sum1([],_,_,Sum) ->
+ Sum.
+
+%%
+%% Product of domains
+%% returns:
+%% { x | x = a*b, a in A, b in B }
+%%
+product(A, B) ->
+ product(A,B,new()).
+
+product([A|As], Bs, Prod) ->
+ product(As,Bs,union(prod(Bs,A,new()), Prod));
+product([], _, Prod) ->
+ Prod.
+
+prod([B|Bs],A,Prod) ->
+ MinA = ?low(A),
+ MaxA = ?high(A),
+ MinB = ?low(B),
+ MaxB = ?high(B),
+ P1 = MinA*MinB,
+ P2 = MinA*MaxB,
+ P3 = MaxA*MinB,
+ P4 = MaxA*MaxB,
+ {Min1,Max1} = min_max(P1,P2),
+ {Min2,Max2} = min_max(P3,P4),
+ N = add_element({min(Min1,Min2),max(Max1,Max2)}, new()),
+ prod(Bs, A, union(N, Prod));
+prod([],_, Prod) ->
+ Prod.
+
+
+cons([E | L1], L2) ->
+ cons(L1, [E | L2]);
+cons([], L) ->
+ L.
+
+%%
+%% format(D)
+%%
+%% output a domain
+%%
+format([]) -> "{}";
+format([D]) -> ["{", format1(D), "}"];
+format([D|Ds]) ->
+ ["{", format1(D), lists:map(fun(D1) -> [",",format1(D1)] end, Ds), "}"].
+
+format1({Low,High}) -> [$[,integer_to_list(Low),"..",integer_to_list(High),$]];
+format1(Value) -> [integer_to_list(Value)].
+
diff --git a/deps/bt/src/bt_make_hci_api.erl b/deps/bt/src/bt_make_hci_api.erl
new file mode 100644
index 0000000..e108494
--- /dev/null
+++ b/deps/bt/src/bt_make_hci_api.erl
@@ -0,0 +1,408 @@
+%%% @author Tony Rogvall <tony@up13>
+%%% @copyright (C) 2015, Tony Rogvall
+%%% @doc
+%%% Create header/erlang file from def file
+%%% @end
+%%% Created : 3 Apr 2015 by Tony Rogvall <tony@up13>
+
+-module(bt_make_hci_api).
+
+-compile(export_all).
+
+%%
+%% the def file content consist of:
+%%
+%% {define, Name::atom(), Value::integer() | string()}
+%%
+%% <NAME>_SIZE are really not needed since the size may be
+%% computed from the struct?
+%% OGF_<Group?-Name>
+%% OCF_<Command-Name>
+%%
+%% A command OCG_<COMMAND-NAME> is also in company with
+%% command structure: <command_name>_cp or <command_name>_rp
+%%
+%% {type, name(), type()} type definition
+%%
+%% {enum, [name()]} | {enum, [{name(),value()}]}
+%%
+%% {struct,name(),[{type(),name()}]}
+%% {struct,name(),[{type(),size(),name()}]}
+%%
+%% <name>_cp is command struct
+%% <name>_rp is reply struct
+%%
+%%
+%%
+%% Generate include file hrl
+%% Generate source file erl
+%%
+main() ->
+ {ok,Defs0} = file:consult(filename:join(code:priv_dir(bt), "hci.def")),
+ {ok,Erl} = file:open("hci_api.erl", [write]),
+ {ok,Hrl} = file:open("hci_api.hrl", [write]),
+ write_erl_header(Erl),
+ write_hrl_header(Hrl),
+ R = try write(Hrl, Erl, Defs0, Defs0, "", "", [], [], []) of
+ Result -> Result
+ catch
+ error:Reason ->
+ io:format("crash: ~p\n", [erlang:get_stacktrace()]),
+ {error,Reason}
+ end,
+ file:close(Erl),
+ write_hrl_footer(Hrl),
+ file:close(Hrl),
+ R.
+
+write_erl_header(Erl) ->
+ io:format(Erl, "%% -*- erlang -*-\n", []),
+ io:format(Erl, "-module(hci_api).\n", []),
+ io:format(Erl, "-compile(export_all).\n", []),
+ io:format(Erl, "-include(\"hci_api.hrl\").\n", []),
+ io:format(Erl, "\n", []),
+ io:format(Erl, "~s\n\n", ["
+cname(<<0,_/binary>>) -> [];
+cname(<<C,Cs/binary>>) -> [C|cname(Cs)];
+cname(<<>>) -> [].
+"]),
+ ok.
+
+write_hrl_header(Hrl) ->
+ io:format(Hrl, "%% -*- erlang -*-\n", []),
+ io:format(Hrl, "-ifndef(__HCI_HRL__).\n", []),
+ io:format(Hrl, "-define(__HCI_HRL__,true).\n", []),
+ io:format(Hrl, "\n\n", []),
+ ok.
+
+write_hrl_footer(Erl) ->
+ io:format(Erl, "-endif.\n", []),
+ ok.
+
+%%
+%% Generate erlang files
+%%
+write(Hrl,Erl,[Def|Defs],Defs0,OGF0,OCF0,EVTs,Funcs,Ds) ->
+ case write_def(Hrl,Erl,Def,Defs0) of
+ {ogf,OGF} -> write(Hrl,Erl,Defs,Defs0,OGF,OCF0,EVTs,Funcs,Ds);
+ {ocf,OCF} -> write(Hrl,Erl,Defs,Defs0,OGF0,OCF,EVTs,
+ [{OGF0,OCF}|Funcs],Ds);
+ {evt,EVT} -> write(Hrl,Erl,Defs,Defs0,OGF0,OCF0,[EVT|EVTs],Funcs,Ds);
+ {evt_decode,Name} ->
+ D = [{Evt,Name} || Evt <- EVTs],
+ Ds1 = lists:reverse(D, Ds),
+ write(Hrl,Erl,Defs,Defs0,OGF0,OCF0,[],Funcs,Ds1);
+ _ -> write(Hrl,Erl,Defs,Defs0,OGF0,OCF0,EVTs,Funcs,Ds)
+ end;
+write(_Hrl,Erl,[],Defs0,_OGF0,_OCF0,_EVTs,Funcs,Ds) ->
+ %% do "normal" events
+ io:format(Erl,
+ "decode(Evt,_Data) ->\n"
+ " case Evt of\n", []),
+ lists:foreach(
+ fun({"EVT_TESTING", _Name}) -> ok;
+ ({"EVT_VENDOR", _Name}) -> ok;
+ ({"EVT_SI_"++_, _Name}) -> ok;
+ ({Evt="EVT_INQUIRY_COMPLETE", _Name}) ->
+ write_clause(Erl, Evt, inquiry_info);
+ ({Evt="EVT_INQUIRY_RESULT", _Name}) ->
+ write_clause(Erl, Evt, inquiry_info);
+ ({Evt="EVT_LE_META_EVENT",Name}) ->
+ write_clause(Erl, Evt, Name);
+ ({"EVT_LE"++_,_Name}) -> ok;
+ ({Evt,Name}) ->
+ write_clause(Erl, Evt, Name)
+ end, Ds),
+ io:format(Erl,
+ " _ -> erlang:error(bad_event)\n", []),
+ io:format(Erl,
+ " end.\n\n", []),
+ %% do "LE" events
+ io:format(Erl,
+ "decode_le(Evt,_Data) ->\n"
+ " case Evt of\n", []),
+ lists:foreach(
+ fun({"EVT_LE_META_EVENT",_Name}) -> ok;
+ ({Evt="EVT_LE"++_,Name}) ->
+ write_clause(Erl, Evt, Name);
+ ({_, _}) -> ok
+ end, Ds),
+ io:format(Erl,
+ " _ -> erlang:error(bad_event)\n", []),
+ io:format(Erl,
+ " end.\n\n", []),
+ %% Do hci calls
+ write_calls(Erl, Funcs, Defs0),
+ ok.
+
+write_clause(Erl, Evt, Name) ->
+ io:format(Erl, " ?~s -> decode_~s(_Data);\n",
+ [Evt,Name]).
+
+%% Generate send function
+write_send(Erl,OGF,OCF,Defs) ->
+ "ocf_"++ FuncName = string:to_lower(atom_to_list(OCF)),
+ Cp = list_to_atom(FuncName++"_cp"),
+ case find_struct(Cp, Defs) of
+ false ->
+ io:format(Erl, "send_~s(Socket) ->\n",
+ [FuncName]),
+ io:format(Erl, " hci_socket:send(Socket,?~s,?~s,<<>>).\n\n",
+ [OGF,OCF]);
+ {struct,Cp,Fields} ->
+ FieldNames = field_names(Fields),
+ MacroArgs = [ var_name(F) || F <- FieldNames ],
+ %% generate the argument structure
+ io:format(Erl,
+ "send_~s(Socket,~s) ->\n",
+ [FuncName, join(MacroArgs,",")]),
+ io:format(Erl,
+ " hci_socket:send(Socket,?~s,?~s,<<?~s_bin(~s)>>).\n\n",
+ [OGF,OCF,Cp,join(MacroArgs,",")])
+ end.
+
+%% For each {OGF, OCF} generate a send_<ocf>
+%% and also generate a call_<ocf>
+write_calls(Erl,[{OGF,OCF}|Funcs],Defs) ->
+ write_call(Erl, OGF, OCF, Defs),
+ write_calls(Erl,Funcs,Defs);
+write_calls(_Erl,[],_Defs) ->
+ ok.
+
+%% Emit call function
+write_call(Erl,OGF,OCF,Defs) ->
+ "ocf_"++ FuncName = string:to_lower(atom_to_list(OCF)),
+ Cp = list_to_atom(FuncName++"_cp"),
+ Rp = list_to_atom(FuncName++"_rp"),
+ Decoder = case find_struct(Rp, Defs) of
+ false -> "undefined";
+ {struct,Rp,_} -> "fun decode_"++FuncName++"_rp/1"
+ end,
+ case find_struct(Cp, Defs) of
+ false ->
+ io:format(Erl, "~s(Socket) ->\n",
+ [FuncName]),
+ io:format(Erl, " hci_socket:call(Socket,?~s,?~s,<<>>,~s).\n\n",
+ [OGF,OCF,Decoder]);
+ {struct,Cp,Fields} ->
+ FieldNames = field_names(Fields),
+ MacroArgs = [ var_name(F) || F <- FieldNames ],
+ %% generate the argument structure
+ io:format(Erl,
+ "~s(Socket,~s) ->\n",
+ [FuncName, join(MacroArgs,",")]),
+ io:format(Erl,
+ " hci_socket:call(Socket,?~s,?~s,<<?~s_bin(~s)>>,~s).\n\n",
+ [OGF,OCF,Cp,join(MacroArgs,","),Decoder])
+ end.
+
+%%
+%% Write definition to header file.
+%% Write decode functions to erlang file.
+%%
+write_def(Hrl, Erl, Def, Defs0) ->
+ io:format("Def = ~p\n", [Def]),
+ case Def of
+ {define,Name,Value} when is_integer(Value) ->
+ NameString = atom_to_list(Name),
+ io:format(Hrl, "-define(~s, ~w).\n", [NameString, Value]),
+ case NameString of
+ "OGF_"++_ -> {ogf,Name};
+ "OCF_"++_ -> {ocf,Name};
+ "EVT_"++_ ->
+ case lists:suffix("_SIZE", NameString) of
+ true -> ok;
+ false -> {evt,NameString}
+ end;
+ _ -> ok
+ end;
+ {define,Name,Value} when is_list(Value) ->
+ NameString = atom_to_list(Name),
+ io:format(Hrl, "-define(~s, ~s).\n", [NameString, Value]),
+ case NameString of
+ "OGF_"++_ -> {ogf,Name};
+ "OCF_"++_ -> {ocf,Name};
+ "EVT_"++_ ->
+ case lists:suffix("_SIZE", NameString) of
+ true -> ok;
+ false -> {evt,NameString}
+ end;
+ _ -> ok
+ end;
+
+ {type, _Name, _Type} -> %% used internally
+ ok;
+
+ {enum,Es} ->
+ lists:foreach(
+ fun({Name,Value}) when is_integer(Value) ->
+ io:format(Hrl, "-define(~s, ~w).\n", [Name, Value])
+ end, enum_es(Es));
+
+ {struct,Name,Fields} ->
+ NameString = atom_to_list(Name),
+ %% generate a record
+ io:format(Hrl, "-record(~s, {\n ", [Name]),
+ FieldNames = field_names(Fields),
+ StrFieldNames = [atom_to_list(Nm) || Nm <- FieldNames],
+ io:format(Hrl, "~s", [join(StrFieldNames, ",\n ")]),
+ io:format(Hrl, "\n}).\n", []),
+ %% generate a binary match sequence
+ MacroArgs = [ var_name(F) || F <- FieldNames ],
+ BinBody = field_match_list(Fields, Defs0),
+ io:format(Hrl, "-define(~s_bin(~s),~s).\n",
+ [Name, join(MacroArgs,","),
+ join(BinBody,",")]),
+ case lists:reverse(NameString) of
+ "pc_"++_ -> %% <name>_cp async command
+ ok;
+ "pr_" ++ _ -> %% reply data structure
+ decode_function(Erl, Name, Fields, MacroArgs);
+ "ofni_yriuqni" ->
+ decode_function(Erl, Name, Fields, MacroArgs);
+ _ ->
+ case lists:prefix("evt_", NameString) of
+ true ->
+ decode_function(Erl, Name, Fields, MacroArgs),
+ {evt_decode, Name};
+ false ->
+ ok
+ end
+ end
+ end.
+
+decode_function(Erl, Name, Fields, MacroArgs) ->
+ FsList = format_field_assign_list(Fields, MacroArgs),
+ %% generate a decode function
+ io:format(Erl, "decode_~s(_Data) ->\n", [Name]),
+ io:format(Erl,
+ " case _Data of\n"
+ " <<?~s_bin(~s)>> ->\n"
+ " #~s { ~s }\n"
+ " end.\n\n",
+ [Name,
+ join(MacroArgs,","),
+ Name,
+ FsList]).
+
+
+format_field_assign_list([F], [Arg]) ->
+ format_field_assign(F, Arg);
+format_field_assign_list([F|Fs], [Arg|As]) ->
+ [format_field_assign(F, Arg), ",",
+ format_field_assign_list(Fs,As)];
+format_field_assign_list([], []) ->
+ [].
+
+format_field_assign({char,_,Name}, Arg) ->
+ [atom_to_list(Name)," = cname(", Arg, ")"];
+format_field_assign({_Type,Name}, Arg) ->
+ [atom_to_list(Name)," = ", Arg];
+format_field_assign({_Type,_,Name}, Arg) ->
+ [atom_to_list(Name)," = ", Arg].
+
+
+var_name(Name) when is_atom(Name) ->
+ var_name_(atom_to_list(Name));
+var_name(Name) when is_list(Name) ->
+ var_name_(Name).
+
+var_name_([H|T]) ->
+ [string:to_upper(H) | T].
+
+remove_suffix(Suffix, List) ->
+ RSuffix = lists:reverse(Suffix),
+ RList = lists:reverse(List),
+ case lists:prefix(RSuffix, RList) of
+ true ->
+ lists:reverse(lists:nthtail(length(RSuffix), RList));
+ false ->
+ List
+ end.
+
+find_define(Name,[Def={define,Name,_}|_Defs]) ->
+ Def;
+find_define(Name,[_|Defs]) ->
+ find_define(Name,Defs);
+find_define(_, []) ->
+ false.
+
+find_struct(Name,[Def={struct,Name,_}|_Defs]) ->
+ Def;
+find_struct(Name,[_|Defs]) ->
+ find_struct(Name,Defs);
+find_struct(_, []) ->
+ false.
+
+find_type(char,_) -> true;
+find_type(uint8_t,_) -> true;
+find_type(int8_t,_) -> true;
+find_type(uint16_t,_) -> true;
+find_type(uint32_t,_) -> true;
+find_type(uint64_t,_) -> true;
+find_type(Name,[TypeDef={type,Name,_}|_Defs]) ->
+ TypeDef;
+find_type(Name,[_|Defs]) ->
+ find_type(Name,Defs);
+find_type(_, []) ->
+ false.
+
+field_match_list([{Type,Name}|Fs], Defs) ->
+ [ field_match(Type,Name,Defs) | field_match_list(Fs,Defs)];
+field_match_list([], _Defs) ->
+ [].
+
+field_match(Type,Name,Defs) when is_atom(Type) ->
+ case find_type(Type,Defs) of
+ true -> %% basic type
+ var_name(Name)++type_unit(Type);
+ {type,_,Type1} ->
+ field_match(Type1,Name,Defs)
+ end;
+field_match({Type,Size},Name,Defs) when is_atom(Type) ->
+ case find_type(Type,Defs) of
+ true ->
+ var_name(Name)++":"++
+ if is_integer(Size) ->
+ integer_to_list(Size);
+ is_list(Size) ->
+ "("++Size++")"
+ end ++ "/" ++ unit(Type)++"-binary";
+ {type,_,Type1} ->
+ field_match({Type1,Size}, Name, Defs)
+ end.
+%% more cases are neede here at some point
+
+type_unit(char) -> ":1/unsigned-unit:8";
+type_unit(uint8_t) -> ":1/unsigned-unit:8";
+type_unit(int8_t) -> ":1/signed-unit:8";
+type_unit(uint16_t) -> ":1/little-unsigned-unit:16";
+type_unit(uint32_t) -> ":1/little-unsigned-unit:32";
+type_unit(uint64_t) -> ":1/little-unsigned-unit:64".
+
+%% element unit
+unit(char) -> "unit:8";
+unit(uint8_t) -> "unit:8";
+unit(int8_t) -> "unit:8";
+unit(uint16_t) -> "unit:16";
+unit(uint32_t) -> "unit:32";
+unit(uint64_t) -> "unit:64".
+
+
+field_names(Fs) ->
+ [ Name || {_Type, Name} <- Fs].
+
+join([], _Sep) -> [];
+join([A], _Sep) -> [A];
+join([A|As], Sep) -> [A,Sep|join(As,Sep)].
+
+
+enum_es(Enums) ->
+ enum_es(Enums, 0).
+
+enum_es([Name|Enums], I) when is_atom(Name) ->
+ [{Name,I} | enum_es(Enums, I+1)];
+enum_es([{Name,Value}|Enums], I) ->
+ [{Name,Value}|enum_es(Enums, erlang:max(Value,I)+1)];
+enum_es([], _) ->
+ [].
diff --git a/deps/bt/src/bt_sdp.erl b/deps/bt/src/bt_sdp.erl
new file mode 100644
index 0000000..3d77dae
--- /dev/null
+++ b/deps/bt/src/bt_sdp.erl
@@ -0,0 +1,1108 @@
+%%%---- BEGIN COPYRIGHT -------------------------------------------------------
+%%%
+%%% Copyright (C) 2006 - 2014, Rogvall Invest AB, <tony@rogvall.se>
+%%%
+%%% This software is licensed as described in the file COPYRIGHT, which
+%%% you should have received as part of this distribution. The terms
+%%% are also available at http://www.rogvall.se/docs/copyright.txt.
+%%%
+%%% You may opt to use, copy, modify, merge, publish, distribute and/or sell
+%%% copies of the Software, and permit persons to whom the Software is
+%%% furnished to do so, under the terms of the COPYRIGHT file.
+%%%
+%%% This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY
+%%% KIND, either express or implied.
+%%%
+%%%---- END COPYRIGHT ---------------------------------------------------------
+%%% File : bt_sdp.erl
+%%% Author : Tony Rogvall <tony@PBook.local>
+%%% Description : SDP functions
+%%% Created : 28 May 2006 by Tony Rogvall <tony@PBook.local>
+
+-module(bt_sdp).
+
+-export([attribute_to_string/1, attribute_to_string/2, attribute_to_string/3]).
+-export([string_to_attribute/1, string_to_attribute/2]).
+-export([value_to_string/1]).
+-export([uuid_to_string/1, string_to_uuid/1]).
+
+-export([is_value/1, encode_value/1, decode_value/1, decode_value/2]).
+-export([fold_value/3]).
+-export([encode/2, decode/1]).
+-export([uuid_128/1]).
+-export([decode_sdp_value/1]).
+
+%% Native Client SDP/L2CAP API
+-export([service_search/2,
+ service_attribute/3,
+ service_search_attribute/3
+ ]).
+-export([encode_pdu/2,encode_pdu/3,
+ decode_pdu/1]).
+
+-import(lists, [reverse/1, map/2]).
+
+-include("../include/bt.hrl").
+-include("../include/sdp.hrl").
+
+
+-define(SDP_NIL, 0).
+-define(SDP_UNSIGNED, 1).
+-define(SDP_SIGNED, 2).
+-define(SDP_UUID, 3).
+-define(SDP_TEXT, 4).
+-define(SDP_BOOLEAN, 5).
+-define(SDP_SEQUENCE, 6).
+-define(SDP_ALTERNATIVE, 7).
+-define(SDP_URL, 8).
+
+
+%% L2CAP implementation of SDP query (will be)
+service_search(Address, UUIDList) ->
+ SearchPattern = cvt_uuid_list(UUIDList),
+ PDU = #sdpServiceSearchRequest { serviceSearchPattern=SearchPattern,
+ maximumServiceRecordCount=16#ffff
+ },
+ sdp_request(Address, PDU).
+
+service_attribute(Address, Handle, AttributeList) ->
+ AttributeIDList= cvt_attribute_id_list(AttributeList),
+ PDU = #sdpServiceAttributeRequest { serviceRecordHandle=Handle,
+ maximumAttributeByteCount=16#ffff,
+ attributeIDList=AttributeIDList },
+ sdp_request(Address, PDU).
+
+service_search_attribute(Address, UUIDList, AttributeList) ->
+ SearchPattern = cvt_uuid_list(UUIDList),
+ AttributeIDList= cvt_attribute_id_list(AttributeList),
+ PDU=#sdpServiceSearchAttributeRequest { serviceSearchPattern=SearchPattern,
+ maximumAttributeByteCount=16#ffff,
+ attributeIDList=AttributeIDList },
+ sdp_request(Address, PDU).
+
+
+sdp_request(Address, PDU) ->
+ case l2cap:open(Address, ?L2CAP_PSM_SDP) of
+ {ok,L2CAP} ->
+ Result = sdp_l2cap_request(L2CAP,<<>>,[],PDU),
+ l2cap:close(L2CAP),
+ Result;
+ Error -> Error
+ end.
+
+sdp_l2cap_request(L2CAP,Continuation,Acc,Request) ->
+ Transaction = next_transaction_id(),
+ io:format("Transaction ~w\n", [Transaction]),
+ Bin = encode_pdu(Transaction,Continuation,Request),
+ l2cap:send(L2CAP, Bin),
+ receive
+ {l2cap, L2CAP, {data,Data}} ->
+ case decode_pdu(Data,Acc) of
+ {ok,{Transaction,<<>>,_Acc1,
+ #sdpErrorResponse { errorCode=ErrorCode,
+ errorInfo=ErrorInfo }}} ->
+ {error,{ErrorCode,ErrorInfo}};
+ {ok,{Transaction,<<>>,_Acc1,Response}} ->
+ {ok,Response};
+ {ok,{Transaction,Continuation1,Acc1,_Response}} ->
+ io:format("get more\n",[]),
+ sdp_l2cap_request(L2CAP,Continuation1,Acc1,Request);
+ Error ->
+ Error
+ end;
+ {l2cap, L2CAP, closed} ->
+ {error, closed}
+ end.
+
+%%
+%% Encode PDU
+%%
+encode_pdu(Transaction,PDU) ->
+ encode_pdu(Transaction,<<>>,PDU).
+
+encode_pdu(Transaction,Continuation,
+ #sdpServiceSearchRequest
+ { serviceSearchPattern=ServiceSearchPattern,
+ maximumServiceRecordCount=MaximumServiceRecordCount
+ }) ->
+ EServiceSearchPattern = encode_value(ServiceSearchPattern),
+ ParameterLen = size(EServiceSearchPattern)+2+1+size(Continuation),
+ <<?SDP_ServiceSearchRequest,
+ Transaction:16/unsigned-integer,
+ ParameterLen:16/unsigned-integer,
+ EServiceSearchPattern/binary,
+ MaximumServiceRecordCount:16/unsigned-integer,
+ (size(Continuation)):8/unsigned-integer, Continuation/binary>>;
+encode_pdu(Transaction,Continuation,
+ #sdpServiceSearchResponse
+ { totalServiceRecordCount=TotalServiceRecordCount,
+ currentServiceRecordCount=CurrentServiceRecordCount,
+ serviceRecordHandleList=Handles
+ }) ->
+ ParameterLen = 2+2+4*length(Handles),
+ ServiceRecordHandleList =
+ list_to_binary(map(fun(H) -> <<H:32/unsigned-integer>> end, Handles)),
+ <<?SDP_ServiceSearchResponse,
+ Transaction:16/unsigned-integer,
+ ParameterLen:16/unsigned-integer,
+ TotalServiceRecordCount:16/unsigned-integer,
+ CurrentServiceRecordCount:16/unsigned-integer,
+ ServiceRecordHandleList/binary,
+ (size(Continuation)):8, Continuation/binary>>;
+encode_pdu(Transaction,Continuation,
+ #sdpServiceAttributeRequest
+ { serviceRecordHandle=Handle,
+ maximumAttributeByteCount=MaximumAttributeByteCount,
+ attributeIDList=AttributeIDList
+ }) ->
+ EAttributeIDList = encode_value(AttributeIDList),
+ ParameterLen = 4+2+size(EAttributeIDList)+1+size(Continuation),
+ <<?SDP_ServiceAttributeRequest,
+ Transaction:16/unsigned-integer,
+ ParameterLen:16/unsigned-integer,
+ Handle:32/unsigned-integer,
+ MaximumAttributeByteCount:16/unsigned-integer,
+ EAttributeIDList/binary,
+ (size(Continuation)):8/unsigned-integer, Continuation/binary>>;
+encode_pdu(Transaction,Continuation,
+ #sdpServiceAttributeResponse
+ { attributeListByteCount=AttributeListByteCount,
+ attributeList=AttributeList }) ->
+ ParameterLen = 2 + size(AttributeList)+1+size(Continuation),
+ <<?SDP_ServiceAttributeResponse,
+ Transaction:16/unsigned-integer,
+ ParameterLen:16/unsigned-integer,
+ AttributeListByteCount:16/unsigned-integer,
+ AttributeList/binary,
+ (size(Continuation)):8, Continuation/binary>>;
+encode_pdu(Transaction,Continuation,
+ #sdpServiceSearchAttributeRequest
+ { serviceSearchPattern=ServiceSearchPattern,
+ maximumAttributeByteCount=MaximumAttributeByteCount,
+ attributeIDList=AttributeIDList
+ }) ->
+ EServiceSearchPattern = encode_value(ServiceSearchPattern),
+ EAttributeIDList = encode_value(AttributeIDList),
+ ParameterLen = size(EServiceSearchPattern)+2+
+ size(EAttributeIDList)+1+size(Continuation),
+ <<?SDP_ServiceSearchAttributeRequest,
+ Transaction:16/unsigned-integer,
+ ParameterLen:16/unsigned-integer,
+ EServiceSearchPattern/binary,
+ MaximumAttributeByteCount:16/unsigned-integer,
+ EAttributeIDList/binary,
+ (size(Continuation)):8/unsigned-integer, Continuation/binary>>;
+encode_pdu(Transaction,Continuation,
+ #sdpServiceSearchAttributeResponse
+ { attributeListByteCount=AttributeListByteCount,
+ attributeList=AttributeList }) ->
+ ParameterLen = 2+size(AttributeList)+1+size(Continuation),
+ <<?SDP_ServiceSearchAttributeResponse,
+ Transaction:16/unsigned-integer,
+ ParameterLen:16/unsigned-integer,
+ AttributeListByteCount:16/unsigned-integer,
+ AttributeList/binary,
+ (size(Continuation)):8, Continuation/binary>>;
+encode_pdu(Transaction,_Continuation,
+ #sdpErrorResponse { errorCode=ECode,
+ errorInfo=ErrorInfo }) ->
+ ErrorCode =
+ case ECode of
+ invalid_version -> ?SDP_ErrorInvalidVersion;
+ invalid_handle -> ?SDP_ErrorInvalidHandle;
+ invalid_syntax -> ?SDP_ErrorInvalidSyntax;
+ invalid_pdu_size -> ?SDP_ErrorInvaludPduSize;
+ invalid_continuation -> ?SDP_ErrorInvalidContinuation;
+ insufficient_resources ->?SDP_ErrorInsufficientResources;
+ _ when is_integer(ECode) -> ECode
+ end,
+ ParameterLen = 2+size(ErrorInfo),
+ <<?SDP_ErrorResponse,
+ Transaction:16/unsigned-integer,
+ ParameterLen:16/unsigned-integer,
+ ErrorCode:16/unsigned-integer,
+ ErrorInfo/binary>>;
+
+encode_pdu(_Transaction,_Continuation,PDU) ->
+ %% add response pdus.
+ erlang:error({bad_pdu, PDU}).
+
+decode_pdu(Bin) ->
+ decode_pdu(Bin, []).
+
+decode_pdu(<<ID,
+ Transaction:16/unsigned-integer,
+ ParameterLen:16/unsigned-integer,
+ Parameters:ParameterLen/binary>>, Acc) ->
+ case catch decode_pdu(ID,Transaction,Parameters,Acc) of
+ {'EXIT',_} ->
+ {error,invalid_request_syntax};
+ Result -> Result
+ end;
+decode_pdu(PDU, _Acc) ->
+ erlang:error({bad_pdu,PDU}).
+
+%% decode pdu return {Transaction,Continuation,Pdu}
+decode_pdu(?SDP_ServiceSearchRequest,Transaction,Parameters,_Acc) ->
+ PatternSize = sizeof(Parameters,0),
+
+ <<Patterns:PatternSize/binary,
+ MaximumServiceRecordCount:16/unsigned-integer,
+ CSize:8, Continuation:CSize/binary>> = Parameters,
+
+ {ok,{Transaction,Continuation
+ #sdpServiceSearchRequest
+ { serviceSearchPattern=decode(Patterns),
+ maximumServiceRecordCount=MaximumServiceRecordCount}}};
+
+decode_pdu(?SDP_ServiceSearchResponse,Transaction,
+ <<TotalServiceRecordCount:16/unsigned-integer,
+ CurrentServiceRecordCount:16/unsigned-integer,
+ ServiceRecordHandleList:CurrentServiceRecordCount/binary-unit:32,
+ CSize:8, Continuation:CSize/binary>>, Acc) ->
+ HandleList = uint32_list(ServiceRecordHandleList),
+ if Continuation == <<>> ->
+ {ok,{Transaction,Continuation,[],
+ #sdpServiceSearchResponse
+ { totalServiceRecordCount=TotalServiceRecordCount,
+ currentServiceRecordCount=CurrentServiceRecordCount,
+ serviceRecordHandleList=Acc++HandleList}}};
+ true ->
+ {ok,{Transaction,Continuation,Acc++HandleList,
+ #sdpServiceSearchResponse
+ { totalServiceRecordCount=TotalServiceRecordCount,
+ currentServiceRecordCount=CurrentServiceRecordCount,
+ serviceRecordHandleList=HandleList}}}
+ end;
+
+decode_pdu(?SDP_ServiceAttributeRequest,Transaction,Parameters,_Acc) ->
+ AttributeIDSize = sizeof(Parameters,6),
+ <<Handle:32/unsigned-integer,
+ MaximumAttributeByteCount:16/unsigned-integer,
+ AttributeIDList:AttributeIDSize/binary,
+ CSize:8, Continuation:CSize/binary>> = Parameters,
+ {ok,{Transaction,Continuation,[],
+ #sdpServiceAttributeRequest
+ { serviceRecordHandle=Handle,
+ maximumAttributeByteCount=MaximumAttributeByteCount,
+ attributeIDList = decode(AttributeIDList) }}};
+
+decode_pdu(?SDP_ServiceAttributeResponse,Transaction,
+ <<AttributeListByteCount:16/unsigned-integer,
+ AttributeList:AttributeListByteCount/binary,
+ CSize:8, Continuation:CSize/binary>>, Acc) ->
+ if Continuation == <<>> ->
+ TotalAttributeList = list_to_binary([Acc,AttributeList]),
+ AList = decode_value(TotalAttributeList),
+ IAttributeList=attribute_id_list(AList),
+ {ok,{Transaction,Continuation,[],
+ #sdpServiceAttributeResponse
+ { attributeListByteCount=size(TotalAttributeList),
+ attributeList=IAttributeList}}};
+ true ->
+ {ok,{Transaction,Continuation,Acc++[AttributeList],
+ #sdpServiceAttributeResponse
+ { attributeListByteCount=AttributeListByteCount,
+ attributeList=AttributeList}}}
+ end;
+decode_pdu(?SDP_ServiceSearchAttributeRequest,Transaction, Parameters, _Acc) ->
+ PatternSize = sizeof(Parameters,0),
+ AttributeIDSize = sizeof(Parameters,PatternSize+2),
+ <<Pattern:PatternSize/binary,
+ MaximumAttributeByteCount:16/unsigned-integer,
+ AttributeIDList:AttributeIDSize/binary,
+ CSize:8, Continuation:CSize/binary>> = Parameters,
+
+ {ok,{Transaction,Continuation,[],
+ #sdpServiceSearchAttributeRequest
+ { serviceSearchPattern=decode(Pattern),
+ maximumAttributeByteCount=MaximumAttributeByteCount,
+ attributeIDList = decode(AttributeIDList) }}};
+
+decode_pdu(?SDP_ServiceSearchAttributeResponse,Transaction,
+ <<AttributeListByteCount:16/unsigned-integer,
+ AttributeList:AttributeListByteCount/binary,
+ CSize:8, Continuation:CSize/binary>> , Acc) ->
+ if Continuation == <<>> ->
+ TotalAttributeList = list_to_binary([Acc,AttributeList]),
+ AList = decode_value(TotalAttributeList),
+ RAttributeList = service_id_list(AList),
+ {ok,{Transaction,Continuation,[],
+ #sdpServiceSearchAttributeResponse
+ { attributeListByteCount=size(TotalAttributeList),
+ attributeList=RAttributeList}}};
+ true ->
+ {ok,{Transaction,Continuation,Acc++[AttributeList],
+ #sdpServiceSearchAttributeResponse
+ { attributeListByteCount=AttributeListByteCount,
+ attributeList=AttributeList}}}
+ end;
+decode_pdu(?SDP_ErrorResponse,Transaction,
+ <<ErrorCode:16/unsigned-integer,
+ ErrorInfo/binary>>, _Acc) ->
+ ECode =
+ case ErrorCode of
+ ?SDP_ErrorInvalidVersion -> invalid_version;
+ ?SDP_ErrorInvalidHandle -> invalid_handle;
+ ?SDP_ErrorInvalidSyntax -> invalid_syntax;
+ ?SDP_ErrorInvaludPduSize -> invalid_pdu_size;
+ ?SDP_ErrorInvalidContinuation -> invalid_continuation;
+ ?SDP_ErrorInsufficientResources -> insufficient_resources;
+ _ -> ErrorCode
+ end,
+ {ok,{Transaction,<<>>,[],
+ #sdpErrorResponse { errorCode=ECode,
+ errorInfo=ErrorInfo }}};
+decode_pdu(_ID, _Transaction, _Parameters, _Acc) ->
+ {error, invalid_pdu}.
+
+
+
+
+uint32_list(<<X:32/unsigned-integer, T/binary>>) ->
+ [X | uint32_list(T)];
+uint32_list(<<>>) ->
+ [].
+
+%% convert search attribute response into attribute list
+service_id_list({sequence,List}) ->
+ map(fun(H) -> attribute_id_list(H) end, List);
+service_id_list(_) ->
+ [].
+
+
+%% convert attribute response into attribute list
+attribute_id_list({sequence,List}) ->
+ attribute_id_list1(List);
+attribute_id_list(_) -> [].
+
+attribute_id_list1([{uint16,ID},Elem|T]) ->
+ [{ID,Elem} | attribute_id_list1(T)];
+attribute_id_list1([{uint16,ID}]) ->
+ [{ID,nil}];
+attribute_id_list1([]) ->
+ [];
+attribute_id_list1(_) ->
+ [].
+
+
+
+cvt_uuid_list(UUIDList) ->
+ {sequence,
+ map(
+ fun(UUID) when is_binary(UUID) ->
+ {uuid,UUID};
+ (UUID) when is_list(UUID); is_atom(UUID) ->
+ {uuid,string_to_uuid(UUID)}
+ end,
+ UUIDList)}.
+
+cvt_attribute_id_list(AttributeList) ->
+ {sequence,
+ map(
+ fun({A1,A2}) ->
+ A11 = if is_list(A1); is_atom(A1) ->
+ string_to_attribute(A1);
+ is_integer(A1) ->
+ A1 band 16#ffff
+ end,
+ A22 = if is_list(A2); is_atom(A2) ->
+ string_to_attribute(A2);
+ is_integer(A2) ->
+ A2 band 16#ffff
+ end,
+ {uint32, (A11 bsl 16) + A22};
+ (A) when is_list(A); is_atom(A) ->
+ {uint16, string_to_attribute(A)};
+ (A) when is_integer(A) ->
+ {uint16, A band 16#ffff}
+ end, AttributeList)}.
+
+
+next_transaction_id() ->
+ N = case get('$sdp_transaction_id') of
+ undefined -> 1;
+ T -> T
+ end,
+ put('$sdp_transaction_id', N+1),
+ N.
+
+
+
+encode(Attribute, Value) ->
+ <<?SDP_UNSIGNED:5, 1:3, Attribute:16, (encode_value(Value))/binary>>.
+
+decode(Bin = <<?SDP_UNSIGNED:5,1:3,Attribute:16,_/binary>>) ->
+ {Attribute, decode_value(Bin, 3)}.
+
+
+%%
+%% return size of tag & value (including length bytes if present)
+%%
+sizeof(Bin, Offs) ->
+ case Bin of
+ <<_:Offs/binary, ?SDP_NIL:5, 0:3, _/binary>> -> 1;
+ <<_:Offs/binary, _:5, 0:3, _/binary>> -> 2;
+ <<_:Offs/binary, _:5, 1:3, _/binary>> -> 3;
+ <<_:Offs/binary, _:5, 2:3, _/binary>> -> 5;
+ <<_:Offs/binary, _:5, 3:3, _/binary>> -> 9;
+ <<_:Offs/binary, _:5, 4:3, _/binary>> -> 17;
+ <<_:Offs/binary, _:5, 5:3, Len:8, _/binary>> -> Len+2;
+ <<_:Offs/binary, _:5, 6:3, Len:16, _/binary>> -> Len+3;
+ <<_:Offs/binary, _:5, 7:3, Len:32, _/binary>> -> Len+5
+ end.
+
+decode_value(Bin) ->
+ decode_value(Bin, 0).
+
+decode_value(Bin, Offs) ->
+ case Bin of
+ <<_:Offs/binary,?SDP_NIL:5, 0:3,_/binary>> -> nil;
+ %% type=1 unsigned integer
+ <<_:Offs/binary,?SDP_UNSIGNED:5, 0:3, X:8/unsigned,_/binary>> ->
+ {uint8,X};
+ <<_:Offs/binary,?SDP_UNSIGNED:5, 1:3, X:16/unsigned,_/binary>> ->
+ {uint16,X};
+ <<_:Offs/binary,?SDP_UNSIGNED:5, 2:3, X:32/unsigned,_/binary>> ->
+ {uint32,X};
+ <<_:Offs/binary,?SDP_UNSIGNED:5, 3:3, X:64/unsigned,_/binary>> ->
+ {uint64,X};
+ <<_:Offs/binary,?SDP_UNSIGNED:5, 4:3, X:128/unsigned,_/binary>> ->
+ {uint128,X};
+ %% type=2 signed integer
+ <<_:Offs/binary,?SDP_SIGNED:5, 0:3, X:8/signed,_/binary>> ->
+ {int8,X};
+ <<_:Offs/binary,?SDP_SIGNED:5, 1:3, X:16/signed,_/binary>> ->
+ {int16,X};
+ <<_:Offs/binary,?SDP_SIGNED:5, 2:3, X:32/signed,_/binary>> ->
+ {int32,X};
+ <<_:Offs/binary,?SDP_SIGNED:5, 3:3, X:64/signed,_/binary>> ->
+ {int64,X};
+ <<_:Offs/binary,?SDP_SIGNED:5, 4:3, X:128/signed,_/binary>> ->
+ {int128,X};
+ %% type=3 UUID
+ <<_:Offs/binary,?SDP_UUID:5, 1:3, X:2/binary,_/binary>> ->
+ {uuid,X};
+ <<_:Offs/binary,?SDP_UUID:5, 2:3, X:4/binary,_/binary>> ->
+ {uuid,X};
+ <<_:Offs/binary,?SDP_UUID:5, 4:3, X:16/binary,_/binary>> ->
+ {uuid,X};
+ %% type=4 Text string
+ <<_:Offs/binary,?SDP_TEXT:5, 5:3, Len:8, X:Len/binary,_/binary>> ->
+ {text,binary_to_list(X)};
+ <<_:Offs/binary,?SDP_TEXT:5, 6:3, Len:16, X:Len/binary,_/binary>> ->
+ {text,binary_to_list(X)};
+ <<_:Offs/binary,?SDP_TEXT:5, 7:3, Len:32, X:Len/binary,_/binary>> ->
+ {text,binary_to_list(X)};
+ %% type=5 Boolean
+ <<_:Offs/binary,?SDP_BOOLEAN:5, 0:3, 0,_/binary>> ->
+ {boolean,false};
+ <<_:Offs/binary,?SDP_BOOLEAN:5, 0:3, _,_/binary>> ->
+ {boolean,true};
+ %% type=6 Sequence
+ <<_:Offs/binary,?SDP_SEQUENCE:5, 5:3, Len:8, _/binary>> ->
+ {sequence, decode_sequence(Bin,Offs+2, Len, [])};
+ <<_:Offs/binary,?SDP_SEQUENCE:5, 6:3, Len:16, _/binary>> ->
+ {sequence, decode_sequence(Bin,Offs+3, Len, [])};
+ <<_:Offs/binary,?SDP_SEQUENCE:5, 7:3, Len:32, _/binary>> ->
+ {sequence, decode_sequence(Bin,Offs+5, Len, [])};
+ %% type=7 Alternative
+ <<_:Offs/binary,?SDP_ALTERNATIVE:5, 5:3, Len:8, _/binary>> ->
+ {alternative, decode_sequence(Bin,Offs+2, Len, [])};
+ <<_:Offs/binary,?SDP_ALTERNATIVE:5, 6:3, Len:16, _/binary>> ->
+ {alternative, decode_sequence(Bin,Offs+3, Len, [])};
+ <<_:Offs/binary,?SDP_ALTERNATIVE:5, 7:3, Len:32, _/binary>> ->
+ {alternative, decode_sequence(Bin,Offs+5, Len, [])};
+ %% type=8 URL
+ <<_:Offs/binary,?SDP_URL:5, 5:3, Len:8, X:Len/binary,_/binary>> ->
+ {url,binary_to_list(X)};
+ <<_:Offs/binary,?SDP_URL:5, 6:3, Len:16, X:Len/binary,_/binary>> ->
+ {url,binary_to_list(X)};
+ <<_:Offs/binary,?SDP_URL:5, 7:3, Len:32, X:Len/binary,_/binary>> ->
+ {url,binary_to_list(X)};
+ %% 9-31 reserved
+ <<_:Offs/binary,Code:5, _:3, _/binary>> when Code >= 9 ->
+ Sz = sizeof(Bin, Offs),
+ <<_:Offs/binary,_,Reserved:Sz/binary,_/binary>> = Bin,
+ %% The reserved includes the tag byte
+ {reserved, Code, Reserved}
+ end.
+
+decode_sequence(Bin,Offset,Len,Acc) when Len > 0 ->
+ Elem = decode_value(Bin, Offset),
+ Sz = sizeof(Bin, Offset),
+ decode_sequence(Bin,Offset+Sz,Len-Sz,[Elem|Acc]);
+decode_sequence(_Bin,_Offset,0,Acc) ->
+ reverse(Acc).
+
+encode_value(nil) -> <<?SDP_NIL:5, 0:3>>;
+
+encode_value({uint8,X}) -> <<?SDP_UNSIGNED:5, 0:3, X:8>>;
+encode_value({uint16,X}) -> <<?SDP_UNSIGNED:5, 1:3, X:16>>;
+encode_value({uint32,X}) -> <<?SDP_UNSIGNED:5, 2:3, X:32>>;
+encode_value({uint64,X}) -> <<?SDP_UNSIGNED:5, 3:3, X:64>>;
+encode_value({uint128,X}) -> <<?SDP_UNSIGNED:5, 4:3, X:128>>;
+encode_value({uint,X}) when X>=0 ->
+ if X<16#100 -> <<?SDP_UNSIGNED:5, 0:3, X:8>>;
+ X<16#10000 -> <<?SDP_UNSIGNED:5, 1:3, X:16>>;
+ X<16#100000000 -> <<?SDP_UNSIGNED:5, 2:3, X:32>>;
+ X<16#10000000000000000 -> <<?SDP_UNSIGNED:5, 3:3, X:64>>;
+ true -> (<<?SDP_UNSIGNED:5, 4:3, X:128>>)
+ end;
+encode_value({int8,X}) -> <<?SDP_SIGNED:5, 0:3, X:8>>;
+encode_value({int16,X}) -> <<?SDP_SIGNED:5, 1:3, X:16>>;
+encode_value({int32,X}) -> <<?SDP_SIGNED:5, 2:3, X:32>>;
+encode_value({int64,X}) -> <<?SDP_SIGNED:5, 3:3, X:64>>;
+encode_value({int128,X}) -> <<?SDP_SIGNED:5, 4:3, X:128>>;
+encode_value({int,X}) ->
+ if X >= -16#100, X<16#100 ->
+ <<?SDP_SIGNED:5, 0:3, X:8>>;
+ X >= -16#10000, X<16#10000 ->
+ <<?SDP_SIGNED:5, 1:3, X:16>>;
+ X >= -16#100000000, X<16#100000000 ->
+ <<?SDP_SIGNED:5, 2:3, X:32>>;
+ X >= -16#10000000000000000, X<16#10000000000000000 ->
+ <<?SDP_SIGNED:5,3:3,X:64>>;
+ true -> (<<?SDP_SIGNED:5, 4:3, X:128>>)
+ end;
+encode_value({uuid,UUID}) when is_binary(UUID) ->
+ case size(UUID) of
+ 2 -> <<?SDP_UUID:5, 1:3, UUID/binary>>;
+ 4 -> <<?SDP_UUID:5, 2:3, UUID/binary>>;
+ 16 -> (<<?SDP_UUID:5, 4:3, UUID/binary>>)
+ end;
+encode_value({text,Text}) when is_list(Text) ->
+ Bin = list_to_binary(Text),
+ Len = size(Bin),
+ if Len =< 16#100 ->
+ <<?SDP_TEXT:5, 5:3, Len:8, Bin/binary>>;
+ Len =< 16#10000 ->
+ <<?SDP_TEXT:5, 6:3, Len:16, Bin/binary>>;
+ true ->
+ (<<?SDP_TEXT:5, 6:3, Len:32, Bin/binary>>)
+ end;
+encode_value({boolean,Bool}) ->
+ case Bool of
+ true -> <<?SDP_BOOLEAN:5, 0:3, 1>>;
+ false -> (<<?SDP_BOOLEAN:5, 0:3, 0>>)
+ end;
+encode_value({url,URL}) when is_list(URL) ->
+ Bin = list_to_binary(URL),
+ Len = size(Bin),
+ if Len =< 16#100 ->
+ <<?SDP_URL:5, 5:3, Len:8, Bin/binary>>;
+ Len =< 16#10000 ->
+ <<?SDP_URL:5, 6:3, Len:16, Bin/binary>>;
+ true ->
+ (<<?SDP_URL:5, 6:3, Len:32, Bin/binary>>)
+ end;
+encode_value({sequence, List}) ->
+ Bin = list_to_binary(map(fun(E) -> encode_value(E) end, List)),
+ Len = size(Bin),
+ if Len =< 16#100 ->
+ <<?SDP_SEQUENCE:5, 5:3, Len:8, Bin/binary>>;
+ Len =< 16#10000 ->
+ <<?SDP_SEQUENCE:5, 6:3, Len:16, Bin/binary>>;
+ true ->
+ (<<?SDP_SEQUENCE:5, 6:3, Len:32, Bin/binary>>)
+ end;
+encode_value({alternative, List}) ->
+ Bin = list_to_binary(map(fun(E) -> encode_value(E) end, List)),
+ Len = size(Bin),
+ if Len =< 16#100 ->
+ <<?SDP_ALTERNATIVE:5, 5:3, Len:8, Bin/binary>>;
+ Len =< 16#10000 ->
+ <<?SDP_ALTERNATIVE:5, 6:3, Len:16, Bin/binary>>;
+ true ->
+ (<<?SDP_ALTERNATIVE:5, 6:3, Len:32, Bin/binary>>)
+ end;
+encode_value({reserved,_Code,Bin}) when is_binary(Bin) ->
+ Bin.
+
+fold_value(Fun, Acc, Value={alternative,List}) ->
+ fold_value(Fun, Fun(Value,Acc), List);
+fold_value(Fun, Acc, Value={sequence,List}) ->
+ fold_value(Fun, Fun(Value,Acc), List);
+fold_value(Fun, Acc,[H|T]) ->
+ fold_value(Fun, fold_value(H, Fun, Acc), T);
+fold_value(_Fun, Acc, []) ->
+ Acc;
+fold_value(Fun, Acc, Value) ->
+ Fun(Value, Acc).
+
+
+
+%% make a "decode" of binary sdp value
+decode_sdp_value({uuid,Bin}) when is_binary(Bin) -> uuid_to_string(Bin);
+decode_sdp_value({uint8,Int}) when is_integer(Int) -> Int;
+decode_sdp_value({uint16,Int}) when is_integer(Int) -> Int;
+decode_sdp_value({uint32,Int}) when is_integer(Int) -> Int;
+decode_sdp_value({uint64,Int}) when is_integer(Int) -> Int;
+decode_sdp_value({uint128,Int}) when is_integer(Int) -> Int;
+decode_sdp_value({int8,Int}) when is_integer(Int) -> Int;
+decode_sdp_value({int16,Int}) when is_integer(Int) -> Int;
+decode_sdp_value({int32,Int}) when is_integer(Int) -> Int;
+decode_sdp_value({int64,Int}) when is_integer(Int) -> Int;
+decode_sdp_value({int128,Int}) when is_integer(Int) -> Int;
+decode_sdp_value({boolean,Bool}) -> Bool;
+decode_sdp_value({text,Text}) -> Text;
+decode_sdp_value({url,Url}) -> Url;
+decode_sdp_value({sequence,Seq}) ->
+ {sequence,map(fun decode_sdp_value/1, Seq)};
+decode_sdp_value({alternative,Alt}) ->
+ {alternative,map(fun decode_sdp_value/1, Alt)}.
+
+is_value(nil) -> true;
+is_value({uint8,X}) when is_integer(X), X>=0 -> true;
+is_value({uint16,X}) when is_integer(X), X>=0 -> true;
+is_value({uint32,X}) when is_integer(X), X>=0 -> true;
+is_value({uint64,X}) when is_integer(X), X>=0 -> true;
+is_value({uint128,X}) when is_integer(X), X>=0 -> true;
+is_value({uint,X}) when is_integer(X), X>=0 -> true;
+is_value({int8,X}) when is_integer(X) -> true;
+is_value({int16,X}) when is_integer(X) -> true;
+is_value({int32,X}) when is_integer(X) -> true;
+is_value({int64,X}) when is_integer(X) -> true;
+is_value({int128,X}) when is_integer(X) -> true;
+is_value({int,X}) when is_integer(X) -> true;
+is_value({uuid,UUID}) when is_binary(UUID) ->
+ case size(UUID) of
+ 2 -> true;
+ 4 -> true;
+ 16 -> true;
+ _ -> false
+ end;
+is_value({text,Text}) when is_list(Text) ->
+ lists:all(fun(X) -> is_integer(X) end, Text);
+is_value({boolean,true}) -> true;
+is_value({boolean,false}) -> true;
+is_value({url,URL}) when is_list(URL) ->
+ case catch list_to_binary(URL) of
+ {'EXIT', _} -> false;
+ _ -> true
+ end;
+is_value({sequence, List}) ->
+ lists:all(fun is_value/1, List);
+is_value({alternative, List}) ->
+ lists:all(fun is_value/1, List);
+is_value({reserved,_Code,Bin}) when is_binary(Bin) ->
+ Bin;
+is_value(_) -> false.
+
+
+
+
+value_to_string({uuid,Bin}) -> uuid_to_string(Bin);
+value_to_string({uint8,Int}) -> integer_to_list(Int);
+value_to_string({uint16,Int}) -> integer_to_list(Int);
+value_to_string({uint32,Int}) -> integer_to_list(Int);
+value_to_string({uint64,Int}) -> integer_to_list(Int);
+value_to_string({uint128,Int}) -> integer_to_list(Int);
+value_to_string({int8,Int}) -> integer_to_list(Int);
+value_to_string({int16,Int}) -> integer_to_list(Int);
+value_to_string({int32,Int}) -> integer_to_list(Int);
+value_to_string({int64,Int}) -> integer_to_list(Int);
+value_to_string({int128,Int}) -> integer_to_list(Int);
+value_to_string({boolean,Bool}) -> atom_to_list(Bool);
+value_to_string({sequence,Seq}) ->
+ map_fmt(fun value_to_string/1, ",", Seq);
+value_to_string({alternative,Alt}) ->
+ map_fmt(fun value_to_string/1, "|", Alt);
+value_to_string({url,URL}) -> URL;
+value_to_string({text,Text}) -> [$\",Text, $\"].
+
+map_fmt(Fun, _Sep, [H]) -> Fun(H);
+map_fmt(Fun, Sep, [H|T]) -> [Fun(H),Sep | map_fmt(Fun, Sep, T)];
+map_fmt(_Fun, _Sep, []) -> [].
+
+%% convert to full uuid
+uuid_128(<<UUID:16>>) -> ?BT_UUID16(UUID);
+uuid_128(<<UUID:32>>) -> ?BT_UUID32(UUID);
+uuid_128(<<>>) -> <<>>;
+uuid_128(UUID) when ?is_uuid(UUID) -> UUID.
+
+%% Try convert UUID into symboic name
+uuid_to_string(UUID) ->
+ case uuid_128(UUID) of
+ UUID128 = ?BT_UUID16(UUID16) ->
+ case <<UUID16:16>> of
+ %% PROTOCOLS
+ ?UUID_SDP -> "SDP";
+ ?UUID_UDP -> "UDP";
+ ?UUID_RFCOMM -> "RFCOMM";
+ ?UUID_TCP -> "TCP";
+ ?UUID_TCS_BIN -> "TCS_BIN";
+ ?UUID_TCS_AT -> "TCS_AT";
+ ?UUID_OBEX -> "OBEX";
+ ?UUID_IP -> "IP";
+ ?UUID_FTP -> "FTP";
+ ?UUID_HTTP -> "HTTP";
+ ?UUID_WSP -> "WSP";
+ ?UUID_BNEP -> "BNEP";
+ ?UUID_UPNP -> "UPNP";
+ ?UUID_HIDP -> "HIDP";
+ ?UUID_HCRP_CTRL -> "HCRP_CTRL";
+ ?UUID_HCRP_DATA -> "HCRP_DATA";
+ ?UUID_HCRP_NOTE -> "HRCP_NOTE";
+ ?UUID_AVCTP -> "AVCTP";
+ ?UUID_AVDTP -> "AVDTP";
+ ?UUID_CMPT -> "CMPT";
+ ?UUID_UDI -> "UDI";
+ ?UUID_MCAP_CTRL -> "MCAP_CTRL";
+ ?UUID_MCAP_DATA -> "MCAP_DATA";
+ ?UUID_L2CAP -> "L2CAP";
+
+ %% SERVICE Classes
+ ?UUID_ServiceDiscoveryServer -> "ServiceDiscoveryServer";
+ ?UUID_BrowseGroupDescriptor -> "BrowseGroupDescriptor";
+ ?UUID_PublicBrowseGroup -> "PublicBrowseGroup";
+ ?UUID_SerialPort -> "SerialPort";
+ ?UUID_LANAccessUsingPPP -> "LANAccessUsingPPP";
+ ?UUID_DialupNetworking -> "DialupNetworking";
+ ?UUID_IrMCSync -> "IrMCSync";
+ ?UUID_OBEXObjectPush -> "OBEXObjectPush";
+ ?UUID_OBEXFileTransfer -> "OBEXFileTransfer";
+ ?UUID_IrMCSyncCommand -> "IrMCSyncCommand";
+ ?UUID_Headset -> "Headset";
+ ?UUID_CordlessTelephony -> "CordlessTelephony";
+ ?UUID_AudioSource -> "AudioSource";
+ ?UUID_AudioSink -> "AudioSink";
+ ?UUID_AVRemoteControlTarget -> "AVRemoteControlTarget";
+ ?UUID_AdvancedAudioDistribution -> "AdvancedAudioDistribution";
+ ?UUID_AVRemoteControl -> "AVRemoteControl";
+ ?UUID_VideoConferencing -> "VideoConferencing";
+ ?UUID_Intercom -> "Intercom";
+ ?UUID_Fax -> "Fax";
+ ?UUID_HeadsetAudioGateway -> "HeadsetAudioGateway";
+ ?UUID_WAP -> "WAP";
+ ?UUID_WAPClient -> "WAPClient";
+ ?UUID_PANU -> "PANU";
+ ?UUID_NAP -> "NAP";
+ ?UUID_GN -> "GN";
+ ?UUID_DirectPrinting -> "DirectPrinting";
+ ?UUID_ReferencePrinting -> "ReferencePrinting";
+ ?UUID_Imaging -> "Imaging";
+ ?UUID_ImagingResponder -> "ImagingResponder";
+ ?UUID_ImagingAutomaticArchive -> "ImagingAutomaticArchive";
+ ?UUID_ImagingReferencedObjects -> "ImagingReferencedObjects";
+ ?UUID_Handsfree -> "Handsfree";
+ ?UUID_HandsfreeAudioGateway -> "HandsfreeAudioGateway";
+ ?UUID_DirectPrintingReferenceObjectsService -> "DirectPrintingReferenceObjectsService";
+ ?UUID_ReflectedUI -> "ReflectedUI";
+ ?UUID_BasicPrinting -> "BasicPrinting";
+ ?UUID_PrintingStatus -> "PrintingStatus";
+ ?UUID_HumanInterfaceDeviceService -> "HumanInterfaceDeviceService";
+ ?UUID_HardcopyCableReplacement -> "HardcopyCableReplacement";
+ ?UUID_HCR_Print -> "HCR_Print";
+ ?UUID_HCR_Scan -> "HCR_Scan";
+ ?UUID_CommonISDNAccess -> "CommonISDNAccess";
+ ?UUID_VideoConferencingGW -> "VideoConferencingGW";
+ ?UUID_UDI_MT -> "UDI_MT";
+ ?UUID_UDI_TA -> "UDI_TA";
+ ?UUID_Audio_Video -> "Audio/Video";
+ ?UUID_SIM_Access -> "SIM_Access";
+ ?UUID_PhonebookAccess_PCE -> "PhonebookAccess-PCE";
+ ?UUID_PhonebookAccess_PSE -> "PhonebookAccess-PSE";
+
+ ?UUID_PhonebookAccess -> "PhonebookAccessProfile";
+ ?UUID_Headset_HS -> "Headset Profile";
+ ?UUID_Message_Access_Server -> "MessageAccessProfile";
+ ?UUID_Message_Notification_Server -> "MessageNotificationServer";
+ ?UUID_Message_Access_Profile -> "MessageAccessProfile";
+ ?UUID_GNSS -> "GNSSProfile";
+ ?UUID_GNSS_Server -> "GNSSSever";
+ ?UUID_3D_Display -> "3D-Display";
+ ?UUID_3D_Glasses -> "3D-Glasses";
+ ?UUID_3D_Synchronization -> "3D-Synchronization";
+ ?UUID_MPS_Profile -> "MPS";
+ ?UUID_MPS_SC -> "MPS-SC";
+ ?UUID_CTN_Access_Service -> "CTNAccessService";
+ ?UUID_CTN_Notification_Service ->"CTNNotificationService";
+ ?UUID_CTN_Profile -> "CTNProfile";
+ ?UUID_PnPInformation -> "PnPInformation";
+ ?UUID_GenericNetworking -> "GenericNetworking";
+ ?UUID_GenericFileTransfer -> "GenericFileTransfer";
+ ?UUID_GenericAudio -> "GenericAudio";
+ ?UUID_GenericTelephony -> "GenericTelephony";
+ ?UUID_UPNP_Service -> "UPNP_Service";
+ ?UUID_UPNP_IP_Service -> "UPNP_IP_Service";
+ ?UUID_ESDP_UPNP_IP_PAN -> "ESDP_UPNP_IP_PAN";
+ ?UUID_ESDP_UPNP_IP_LAP -> "ESDP_UPNP_IP_LAP";
+ ?UUID_ESDP_UPNP_L2CAP -> "ESDP_UPNP_L2CAP";
+ ?UUID_VideoSource -> "VideoSource";
+ ?UUID_VideoSink -> "VideoSink";
+ ?UUID_VideoDistribution -> "VideoDistribution";
+ ?UUID_HDP -> "HDP";
+ ?UUID_HDP_Source -> "HDP-Source";
+ ?UUID_HDP_Sink -> "HDP-Sink";
+ _ -> bt_util:uuid_to_string(UUID128)
+ end;
+ ?UUID_SyncMLServer -> "SyncMLServer";
+ ?UUID_SyncMLClient -> "SyncMLClient";
+ ?UUID_SyncMLDMServer -> "SyncMLDMServer";
+ ?UUID_SyncMLDMClient -> "SyncMLDMClient";
+ ?UUID_NokiaSyncMLServer -> "NokiaSyncMLServer";
+ ?UUID_NokiaObexPcSuiteServices -> "NokiaObexPcSuiteService";
+ UUID128 -> bt_util:uuid_to_string(UUID128)
+ end.
+
+
+
+%% Try convert symboic name into UUID
+string_to_uuid(Name) when is_list(Name) ->
+ case tolower(Name) of
+ %% PROTOCOLS
+ "sdp" -> ?UUID_SDP;
+ "udp" -> ?UUID_UDP;
+ "rfcomm" -> ?UUID_RFCOMM;
+ "tcp" -> ?UUID_TCP;
+ "tcs_bin" -> ?UUID_TCS_BIN;
+ "tcs_at" -> ?UUID_TCS_AT;
+ "obex" -> ?UUID_OBEX;
+ "ip" -> ?UUID_IP;
+ "ftp" -> ?UUID_FTP;
+ "http" -> ?UUID_HTTP;
+ "wsp" -> ?UUID_WSP;
+ "bnep" -> ?UUID_BNEP;
+ "upnp" -> ?UUID_UPNP;
+ "hidp" -> ?UUID_HIDP;
+ "hardcopycontrolchannel" -> ?UUID_HCRP_CTRL;
+ "hardcopydatachannel" -> ?UUID_HCRP_DATA;
+ "hardcopynotification" -> ?UUID_HCRP_NOTE;
+ "avctp" -> ?UUID_AVCTP;
+ "avdtp" -> ?UUID_AVDTP;
+ "cmpt" -> ?UUID_CMPT;
+ "udi" -> ?UUID_UDI;
+ "l2cap" -> ?UUID_L2CAP;
+ %% SERVICE Classes
+ "servicediscoveryserver" -> ?UUID_ServiceDiscoveryServer;
+ "browsegroupdescriptor" -> ?UUID_BrowseGroupDescriptor;
+ "publicbrowsegroup" -> ?UUID_PublicBrowseGroup;
+ "serialport" -> ?UUID_SerialPort;
+ "lanaccessusingppp" -> ?UUID_LANAccessUsingPPP;
+ "dialupnetworking" -> ?UUID_DialupNetworking;
+ "irmcsync" -> ?UUID_IrMCSync;
+ "obexobjectpush" -> ?UUID_OBEXObjectPush;
+ "obexfiletransfer" -> ?UUID_OBEXFileTransfer;
+ "irmcsynccommand" -> ?UUID_IrMCSyncCommand;
+ "headset" -> ?UUID_Headset;
+ "cordlesstelephony" -> ?UUID_CordlessTelephony;
+ "audiosource" -> ?UUID_AudioSource;
+ "audiosink" -> ?UUID_AudioSink;
+ "avremotecontroltarget" -> ?UUID_AVRemoteControlTarget;
+ "advancedaudiodistribution" -> ?UUID_AdvancedAudioDistribution;
+ "avremotecontrol" -> ?UUID_AVRemoteControl;
+ "videoconferencing" -> ?UUID_VideoConferencing;
+ "intercom" -> ?UUID_Intercom;
+ "fax" -> ?UUID_Fax;
+ "headsetaudiogateway" -> ?UUID_HeadsetAudioGateway;
+ "wap" -> ?UUID_WAP;
+ "wapclient" -> ?UUID_WAPClient;
+ "panu" -> ?UUID_PANU;
+ "nap" -> ?UUID_NAP;
+ "gn" -> ?UUID_GN;
+ "directprinting" -> ?UUID_DirectPrinting;
+ "referenceprinting" -> ?UUID_ReferencePrinting;
+ "imaging" -> ?UUID_Imaging;
+ "imagingresponder" -> ?UUID_ImagingResponder;
+ "imagingautomaticarchive" -> ?UUID_ImagingAutomaticArchive;
+ "imagingreferencedobjects" -> ?UUID_ImagingReferencedObjects;
+ "handsfree" -> ?UUID_Handsfree;
+ "handsfreeaudiogateway" -> ?UUID_HandsfreeAudioGateway;
+ "directprintingreferenceobjectsservice" -> ?UUID_DirectPrintingReferenceObjectsService;
+ "reflectedui" -> ?UUID_ReflectedUI;
+ "basicprinting" -> ?UUID_BasicPrinting;
+ "printingstatus" -> ?UUID_PrintingStatus;
+ "humaninterfacedeviceservice" -> ?UUID_HumanInterfaceDeviceService;
+ "hardcopycablereplacement" -> ?UUID_HardcopyCableReplacement;
+ "hcr_print" -> ?UUID_HCR_Print;
+ "hcr_scan" -> ?UUID_HCR_Scan;
+ "commonisdnaccess" -> ?UUID_CommonISDNAccess;
+ "videoconferencinggw" -> ?UUID_VideoConferencingGW;
+ "udi_mt" -> ?UUID_UDI_MT;
+ "udi_ta" -> ?UUID_UDI_TA;
+ "audio/video" -> ?UUID_Audio_Video;
+ "sim_access" -> ?UUID_SIM_Access;
+ "phonebookaccess-pce" -> ?UUID_PhonebookAccess_PCE;
+ "phonebookaccess-pse" -> ?UUID_PhonebookAccess_PSE;
+ "pnpinformation" -> ?UUID_PnPInformation;
+ "genericnetworking" -> ?UUID_GenericNetworking;
+ "genericfiletransfer" -> ?UUID_GenericFileTransfer;
+ "genericaudio" -> ?UUID_GenericAudio;
+ "generictelephony" -> ?UUID_GenericTelephony;
+ "upnp_service" -> ?UUID_UPNP_Service;
+ "upnp_ip_service" -> ?UUID_UPNP_IP_Service;
+ "esdp_upnp_ip_pan" -> ?UUID_ESDP_UPNP_IP_PAN;
+ "esdp_upnp_ip_lap" -> ?UUID_ESDP_UPNP_IP_LAP;
+ "esdp_upnp_l2cap" -> ?UUID_ESDP_UPNP_L2CAP;
+ "videosource" -> ?UUID_VideoSource;
+ "videosink" -> ?UUID_VideoSink;
+ "videodistribution" -> ?UUID_VideoDistribution;
+
+ %% SyncML
+ "syncmlserver" -> ?UUID_SyncMLServer;
+ "syncmlclient" -> ?UUID_SyncMLClient;
+ "syncmldmserver" -> ?UUID_SyncMLDMServer;
+ "syncmldmclient" -> ?UUID_SyncMLDMClient;
+ _ -> bt_util:string_to_uuid(Name)
+ end;
+string_to_uuid(Name) when is_atom(Name) ->
+ string_to_uuid(atom_to_list(Name)).
+
+
+string_to_attribute(Name) ->
+ string_to_attribute(Name, 16#0100).
+
+string_to_attribute(Name,LanguageBase) when is_list(Name) ->
+ case tolower(Name) of
+ "servicerecordhandle" -> ?ATTR_ServiceRecordHandle;
+ "serviceclassidlist" -> ?ATTR_ServiceClassIDList;
+ "servicerecordstate" -> ?ATTR_ServiceRecordState;
+ "serviceid" -> ?ATTR_ServiceID;
+ "protocoldescriptorlist" -> ?ATTR_ProtocolDescriptorList;
+ "browsegrouplist" -> ?ATTR_BrowseGroupList;
+ "languagebaseattributeidlist" ->
+ ?ATTR_LanguageBaseAttributeIDList;
+ "serviceinfotimetolive" -> ?ATTR_ServiceInfoTimeToLive;
+ "serviceavailability" -> ?ATTR_ServiceAvailability;
+ "bluetoothprofiledescriptorlist" ->
+ ?ATTR_BluetoothProfileDescriptorList;
+ "documentationurl" -> ?ATTR_DocumentationURL;
+ "clientexecutableurl" -> ?ATTR_ClientExecutableURL;
+ "iconurl" -> ?ATTR_IconURL;
+ "additionalprotocolsdescriptorlist" ->
+ ?ATTR_AdditionalProtocolsDescriptorList;
+ %%
+ "servicename" -> ?ATTR_ServiceName+LanguageBase;
+ "servicedescription" -> ?ATTR_ServiceDescription+LanguageBase;
+ "providername" -> ?ATTR_ProviderName+LanguageBase;
+
+ %% SDP
+ "servicedatabasestate" -> 16#0201;
+ %% PAN
+ "ipsubnet" -> 16#0200;
+ %% OTHER
+ "externalnetwork" -> 16#0301;
+ "network" -> 16#0301;
+ "supporteddatastoreslist" -> 16#0301;
+ "faxclass1support" -> 16#0302;
+ "faxclass2support" -> 16#0303;
+ "remoteaudiovolumecontrol" -> 16#0302;
+ "supporterformatslist" -> 16#0303;
+ %% Check hex
+ _ ->
+ case catch erlang:list_to_integer(Name, 16) of
+ {'EXIT',_} ->
+ erlang:error(badarg);
+ ID -> ID band 16#ffff
+ end
+ end;
+string_to_attribute(Name,LanguageBase) when is_atom(Name) ->
+ string_to_attribute(atom_to_list(Name),LanguageBase).
+
+
+attribute_to_string(ID) ->
+ attribute_to_string(ID,16#0100).
+
+attribute_to_string(ID,LanguageBase) ->
+ attribute_to_string(ID,LanguageBase,<<>>).
+
+attribute_to_string(ID,LanguageBase,UUID) ->
+ case uuid_128(UUID) of
+ ?BT_UUID16(UUID16) ->
+ case UUID16-LanguageBase of
+ 16#0000 -> "ServiceName";
+ 16#0001 -> "ServiceDescription";
+ 16#0002 -> "ProviderName";
+ _ -> attr16(ID,<<UUID16:16>>)
+ end;
+ UUID128 ->
+ attr(ID, LanguageBase,UUID128)
+ end.
+
+attr(ID,_LanuageBase,_UUID) ->
+ attr16_id(ID).
+
+%%
+%% Universal Attributes
+%%
+attr16(?ATTR_ServiceRecordHandle,_UUID) -> "ServiceRecordHandle";
+attr16(?ATTR_ServiceClassIDList,_UUID) -> "ServiceClassIDList";
+attr16(?ATTR_ServiceRecordState,_UUID) -> "ServiceRecordState";
+attr16(?ATTR_ServiceID,_UUID) -> "ServiceID";
+attr16(?ATTR_ProtocolDescriptorList,_UUID) -> "ProtocolDescriptorList";
+attr16(?ATTR_BrowseGroupList,_UUID) -> "BrowseGroupList";
+attr16(?ATTR_LanguageBaseAttributeIDList,_UUID) -> "LanguageBaseAttributeIDList";
+attr16(?ATTR_ServiceInfoTimeToLive,_UUID) -> "ServiceInfoTimeToLive";
+attr16(?ATTR_ServiceAvailability,_UUID) -> "ServiceAvailability";
+attr16(?ATTR_BluetoothProfileDescriptorList,_UUID) -> "BluetoothProfileDescriptorList";
+attr16(?ATTR_DocumentationURL,_UUID) -> "DocumentationURL";
+attr16(?ATTR_ClientExecutableURL,_UUID) -> "ClientExecutableURL";
+attr16(?ATTR_IconURL,_UUID) -> "IconURL";
+attr16(?ATTR_AdditionalProtocolsDescriptorList,_UUID) ->"AdditionalProtocolsDescriptorList";
+
+attr16(16#0200, ?UUID_BrowseGroupDescriptor) -> "GroupID";
+
+%% SDP 1000
+attr16(ID,?UUID_ServiceDiscoveryServer) -> attr16_sdp(ID);
+
+%% AVRCP
+attr16(ID,?UUID_AVRemoteControlTarget) -> attr16_avrcp(ID);
+attr16(ID,?UUID_AVRemoteControl) -> attr16_avrcp(ID);
+attr16(ID,?UUID_VideoConferencing) -> attr16_avrcp(ID);
+
+%% PAN
+attr16(ID,?UUID_PANU) -> attr16_pan(ID);
+attr16(ID,?UUID_NAP) -> attr16_pan(ID);
+attr16(ID,?UUID_GN) -> attr16_pan(ID);
+
+%% BIP 0x111B, 0x111C, 0x111D
+attr16(ID,?UUID_ImagingResponder) -> attr16_bip(ID);
+attr16(ID,?UUID_ImagingAutomaticArchive) -> attr16_bip(ID);
+attr16(ID,?UUID_ImagingReferencedObjects) -> attr16_bip(ID);
+
+attr16(ID,?UUID_PnPInformation) -> attr16_pn(ID);
+
+attr16(ID,_UUID) -> attr16_id(ID).
+
+
+attr16_sdp(?ATTR_SDP_VersionNumberList) -> "VersionNumberList";
+attr16_sdp(?ATTR_SDP_ServiceDatabaseState) -> "ServiceDatabaseState";
+attr16_sdp(ID) -> attr16_id(ID).
+
+attr16_bip(?ATTR_BIP_GoepL2capPsm) -> "GoepL2capPsm";
+attr16_bip(?ATTR_BIP_SupportedCapabilities) -> "SupportedCapabilities";
+attr16_bip(?ATTR_BIP_SupportedFeatures) -> "SupportedFeatures";
+attr16_bip(?ATTR_BIP_SupportedFunctions) -> "SupportedFunctions";
+attr16_bip(?ATTR_BIP_TotalImagingDataCapacity) -> "TotalImagingDataCapacity";
+attr16_bip(ID) -> attr16_id(ID).
+
+attr16_pan(16#0200) -> "IpSubnet";
+attr16_pan(16#030A) -> "SecurityDescription";
+attr16_pan(16#030B) -> "NetAccessType";
+attr16_pan(16#030C) -> "MaxNetAccessrate";
+attr16_pan(16#030D) -> "IPv4Subnet";
+attr16_pan(16#030E) -> "IPv6Subnet";
+attr16_pan(ID) -> attr16_id(ID).
+
+attr16_avrcp(16#0311) -> "SupportedFeatures";
+attr16_avrcp(ID) -> attr16_id(ID).
+
+attr16_pn(16#0200) -> "SpecificationID";
+attr16_pn(16#0201) -> "VendorID";
+attr16_pn(16#0202) -> "ProductID";
+attr16_pn(16#0203) -> "Version";
+attr16_pn(16#0204) -> "PrimaryRecord";
+attr16_pn(16#0205) -> "VendorIDSource";
+attr16_pn(ID) -> attr16_id(ID).
+
+%% convert to 0xABCD format
+attr16_id(ID) ->
+ "0x"++tl(integer_to_list((ID band 16#ffff) bor 16#10000, 16)).
+
+tolower([H|T]) when H>=$A, H=<$Z ->
+ [(H-$A)+$a | tolower(T)];
+tolower([H|T]) ->
+ [H|tolower(T)];
+tolower([]) ->
+ [].
diff --git a/deps/bt/src/bt_sdp_srv.erl b/deps/bt/src/bt_sdp_srv.erl
new file mode 100644
index 0000000..05530c4
--- /dev/null
+++ b/deps/bt/src/bt_sdp_srv.erl
@@ -0,0 +1,608 @@
+%%%---- BEGIN COPYRIGHT -------------------------------------------------------
+%%%
+%%% Copyright (C) 2006 - 2014, Rogvall Invest AB, <tony@rogvall.se>
+%%%
+%%% This software is licensed as described in the file COPYRIGHT, which
+%%% you should have received as part of this distribution. The terms
+%%% are also available at http://www.rogvall.se/docs/copyright.txt.
+%%%
+%%% You may opt to use, copy, modify, merge, publish, distribute and/or sell
+%%% copies of the Software, and permit persons to whom the Software is
+%%% furnished to do so, under the terms of the COPYRIGHT file.
+%%%
+%%% This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY
+%%% KIND, either express or implied.
+%%%
+%%%---- END COPYRIGHT ---------------------------------------------------------
+%%%-------------------------------------------------------------------
+%%% File : bt_sdp_srv.erl
+%%% Author : Tony Rogvall <tony@PBook.local>
+%%% Description : BLuetooth SDP server
+%%%
+%%% Created : 31 Jul 2006 by Tony Rogvall <tony@PBook.local>
+%%%-------------------------------------------------------------------
+-module(bt_sdp_srv).
+
+-behaviour(gen_server).
+
+-include("../include/bt.hrl").
+-include("../include/sdp.hrl").
+
+-compile(export_all).
+
+%% API
+-export([start/0, start_link/0, stop/0]).
+
+-export([service_add/1,
+ service_add_persist/1,
+ service_del/1]).
+
+-export([rfcomm_channel/1,
+ service_search/1,
+ service_attribute/2,
+ service_search_attribute/2]).
+
+
+%% gen_server callbacks
+-export([init/1, handle_call/3, handle_cast/2, handle_info/2,
+ terminate/2, code_change/3]).
+
+-import(lists, [reverse/1, map/2, foldl/3, filter/2]).
+
+-record(serviceRecord,
+ {
+ handle, %% generated 32 bit handle
+ monitor, %% creator monitor (or undefined for persist)
+ uuid_set=[], %% list of uuid included in this record
+ rfcomm_list=[], %% list of used rfcomm channels
+ psm_list=[], %% list of used psm (dynamic)
+ attributes=[]
+ }).
+
+-record(state,
+ {
+ %% rfcomm free list (iset)
+ rfcomm = [],
+ %% dynamic psm free list (iset)
+ psm = [],
+ records = []
+ }).
+
+-define(SERVER, bt_sdp_srv).
+%%====================================================================
+%% API
+%%====================================================================
+%%--------------------------------------------------------------------
+%% Function: start_link() -> {ok,Pid} | ignore | {error,Error}
+%% Description: Starts the server
+%%--------------------------------------------------------------------
+start_link() ->
+ gen_server:start_link({local, ?SERVER}, ?MODULE, [], []).
+
+start() ->
+ gen_server:start({local, ?SERVER}, ?MODULE, [], []).
+
+stop() ->
+ gen_server:call(?SERVER, stop).
+
+service_add(ServiceRecord) when is_list(ServiceRecord) ->
+ gen_server:call(?SERVER, {service_add,self(),ServiceRecord}).
+
+service_add_persist(ServiceRecord) when is_list(ServiceRecord) ->
+ gen_server:call(?SERVER, {service_add,undefined,ServiceRecord}).
+
+service_del(Handle) when is_integer(Handle) ->
+ gen_server:call(?SERVER, {service_del, Handle}).
+
+rfcomm_channel(UUID) when is_binary(UUID) ->
+ gen_server:call(?SERVER, {rfcomm_channel, UUID});
+rfcomm_channel(Handle) when is_integer(Handle) ->
+ gen_server:call(?SERVER, {rfcomm_channel, Handle}).
+
+service_search(UUIDList) when is_list(UUIDList) ->
+ gen_server:call(?SERVER, {service_search, uuid_set(UUIDList)}).
+
+service_attribute(Handle, AttributeList)
+ when is_integer(Handle), is_list(AttributeList) ->
+ gen_server:call(?SERVER, {service_attribute,Handle,
+ attribute_iset(AttributeList)}).
+
+service_search_attribute(UUIDList,AttributeList)
+ when is_list(UUIDList), is_list(AttributeList) ->
+ gen_server:call(?SERVER, {service_search_attribute,
+ uuid_set(UUIDList),
+ attribute_iset(AttributeList)}).
+
+
+%%====================================================================
+%% gen_server callbacks
+%%====================================================================
+
+%%--------------------------------------------------------------------
+%% Function: init(Args) -> {ok, State} |
+%% {ok, State, Timeout} |
+%% ignore |
+%% {stop, Reason}
+%% Description: Initiates the server
+%%--------------------------------------------------------------------
+init([]) ->
+ {A,B,C} = now(),
+ random:seed(A,B,C),
+ SDP = #serviceRecord { handle = 0,
+ monitor = undefined,
+ uuid_set=uuid_set(
+ [?UUID_ServiceDiscoveryServer,
+ ?UUID_L2CAP]),
+ rfcomm_list=[],
+ psm_list=[],
+ attributes=
+ [{?ATTR_ServiceRecordHandle,{uint32,0}},
+ {?ATTR_ServiceClassIDList,
+ {sequence,[{uuid,?UUID_ServiceDiscoveryServer}]}},
+ {?ATTR_ServiceRecordState,{uint32,0}},
+ {?ATTR_ProtocolDescriptorList,
+ {sequence,
+ [{sequence,[{uuid,?UUID_L2CAP}]}]}},
+ {?ATTR_BrowseGroupList,
+ {sequence,[{uuid,?UUID_ServiceDiscoveryServer}]}},
+ {?ATTR_LanguageBaseAttributeIDList,
+ {sequence, [{uint16, ?LANGUAGE($e,$n)},
+ {uint16, ?ENCODING_UTF8},
+ {uint16, 16#0100}]}},
+ {?ATTR_ServiceInfoTimeToLive,
+ {uint32, 1200}},
+ {?ATTR_ServiceAvailability,
+ {uint8, 255}},
+ {?ATTR_ServiceName+16#0100, {text,"SDP Server"}}
+ ]},
+ {ok,
+ #state { rfcomm = bt_iset:from_list([{1,30}]),
+ psm = bt_iset:from_list([{?L2CAP_PSM_DynamicStart,
+ ?L2CAP_PSM_DynamicEnd}]),
+ records = [SDP]
+ }}.
+
+%%--------------------------------------------------------------------
+%% Function: %% handle_call(Request, From, State) -> {reply, Reply, State} |
+%% {reply, Reply, State, Timeout} |
+%% {noreply, State} |
+%% {noreply, State, Timeout} |
+%% {stop, Reason, Reply, State} |
+%% {stop, Reason, State}
+%% Description: Handling call messages
+%%--------------------------------------------------------------------
+
+handle_call({service_add,Pid,ServiceRecord}, _From, State) ->
+ case is_service_record(ServiceRecord) of
+ true ->
+ case add_service(ServiceRecord, Pid, State) of
+ {ok,{Handle,State1}} ->
+ {reply,{ok,Handle},State1};
+ {Error,State1} ->
+ {reply,Error,State1}
+ end;
+ false ->
+ {reply,{error,einval},State}
+ end;
+handle_call({service_del,Handle}, _From, State) ->
+ {reply, ok, remove_service(Handle, State)};
+handle_call(stop, _From, State) ->
+ {stop, normal, ok, State};
+
+handle_call({rfcomm_channel,UUID},_From,State) when is_binary(UUID) ->
+ case match_service(ordsets:from_list([UUID]), State#state.records) of
+ [] ->
+ {reply, {error, not_found}, State};
+ [R] ->
+ {reply,{ok,get_rfcomm_channels(R#serviceRecord.attributes)},State};
+ _Rs ->
+ {reply, {error,multiple_match},State}
+ end;
+handle_call({rfcomm_channel,Handle},_From,State) when is_integer(Handle) ->
+ case lists:keysearch(Handle,#serviceRecord.handle,State#state.records) of
+ false ->
+ {reply, {error, not_found}, State};
+ {value,R} ->
+ {reply,{ok,get_rfcomm_channels(R#serviceRecord.attributes)},State}
+ end;
+handle_call({service_search, UUIDSet}, _From, State) ->
+ Rs = match_service(UUIDSet, State#state.records),
+ {reply, {ok,map(fun(R) -> R#serviceRecord.handle end,Rs)}, State};
+handle_call({service_attribute,Handle,AttributeISet}, _From, State) ->
+ case lists:keysearch(Handle,#serviceRecord.handle,State#state.records) of
+ false ->
+ {reply,{error,bad_handle},State};
+ {value,R} ->
+ Is = filter(fun({ID,_}) ->
+ bt_iset:is_element(ID, AttributeISet)
+ end, R#serviceRecord.attributes),
+ {reply,{ok,Is},State}
+ end;
+handle_call({service_search_attribute,UUIDSet,AttributeISet}, _From, State) ->
+ Rs = match_service(UUIDSet, State#state.records),
+ IRs =
+ map(fun(R) ->
+ filter(fun({ID,_}) ->
+ bt_iset:is_element(ID, AttributeISet)
+ end, R#serviceRecord.attributes)
+ end, Rs),
+ {reply,{ok,IRs},State};
+handle_call(_Request, _From, State) ->
+ {reply, {error,bad_call}, State}.
+
+%%--------------------------------------------------------------------
+%% Function: handle_cast(Msg, State) -> {noreply, State} |
+%% {noreply, State, Timeout} |
+%% {stop, Reason, State}
+%% Description: Handling cast messages
+%%--------------------------------------------------------------------
+handle_cast(_Msg, State) ->
+ {noreply, State}.
+
+%%--------------------------------------------------------------------
+%% Function: handle_info(Info, State) -> {noreply, State} |
+%% {noreply, State, Timeout} |
+%% {stop, Reason, State}
+%% Description: Handling all non call/cast messages
+%%--------------------------------------------------------------------
+handle_info({'DOWN',Ref,_Pid,process,_Reason}, State) ->
+ State1 = remove_service(Ref, State),
+ {noreply, State1};
+handle_info(_Info, State) ->
+ {noreply, State}.
+
+%%--------------------------------------------------------------------
+%% Function: terminate(Reason, State) -> void()
+%% Description: This function is called by a gen_server when it is about to
+%% terminate. It should be the opposite of Module:init/1 and do any necessary
+%% cleaning up. When it returns, the gen_server terminates with Reason.
+%% The return value is ignored.
+%%--------------------------------------------------------------------
+terminate(_Reason, _State) ->
+ ok.
+
+%%--------------------------------------------------------------------
+%% Func: code_change(OldVsn, State, Extra) -> {ok, NewState}
+%% Description: Convert process state when code is changed
+%%--------------------------------------------------------------------
+code_change(_OldVsn, State, _Extra) ->
+ {ok, State}.
+
+%%--------------------------------------------------------------------
+%%% Internal functions
+%%--------------------------------------------------------------------
+
+add_monitor(undefined) ->
+ undefined;
+add_monitor(Pid) when is_pid(Pid) ->
+ erlang:monitor(process, Pid).
+
+del_monitor(undefined) ->
+ undefined;
+del_monitor(Mon) when is_reference(Mon) ->
+ erlang:demonitor(Mon).
+
+new_handle(State) ->
+ Handle = random:uniform(16#100000000)-1,
+ case lists:keysearch(Handle,#serviceRecord.handle,State#state.records) of
+ false ->
+ Handle;
+ {value,_} ->
+ new_handle(State)
+ end.
+
+%% match all services that have the UUIDSet as a subset.
+match_service(UUIDSet, Records) ->
+ lists:filter(fun(R) ->
+ ordsets:is_subset(UUIDSet, R#serviceRecord.uuid_set)
+ end, Records).
+
+%%
+%% Add a service
+%%
+add_service(ServiceRecord, Pid, State) ->
+ %% Collect all UUID's for search operations
+ UUIDSet =
+ foldl(fun({_ID,Value}, Acc) ->
+ bt_sdp:fold_value(
+ fun({uuid,UUID},Acc1) when is_binary(UUID) ->
+ UUID128 = bt_sdp:uuid_128(UUID),
+ ordsets:add_element(UUID128,Acc1);
+ ({uuid,UUID},Acc1) when
+ is_list(UUID); is_atom(UUID) ->
+ UUID1 = bt_sdp:string_to_uuid(UUID),
+ UUID128 = bt_sdp:uuid_128(UUID1),
+ ordsets:add_element(UUID128,Acc1);
+ (_, Acc1) -> Acc1
+ end, Acc, Value)
+ end, ordsets:new(), ServiceRecord),
+ RequestCn = get_rfcomm_channels(ServiceRecord),
+ RequestPsm = get_l2cap_psms(ServiceRecord),
+ case avail(RequestCn, State#state.rfcomm) andalso
+ avail(RequestPsm, State#state.psm) of
+ true ->
+ {CnMap,RfComm} = allocate(RequestCn, State#state.rfcomm),
+ ServiceRecord1 = set_rfcomm_channels(ServiceRecord, CnMap),
+
+ {PsmMap,Psm} = allocate(RequestPsm, State#state.psm),
+ ServiceRecord2 = set_l2cap_psms(ServiceRecord1, PsmMap),
+
+ Handle = new_handle(State),
+ ServiceRecord3 = set_attribute({?ATTR_ServiceRecordHandle,
+ {uint32,Handle}},
+ ServiceRecord2),
+ R = #serviceRecord { handle = Handle,
+ monitor = add_monitor(Pid),
+ uuid_set = UUIDSet,
+ rfcomm_list = map(fun({_,CN}) -> CN end, CnMap),
+ psm_list = map(fun({_,PSM}) -> PSM end, PsmMap),
+ attributes = ServiceRecord3 },
+ Rs = [R | State#state.records],
+ {ok,{Handle,State#state { records = Rs,
+ rfcomm = RfComm,
+ psm = Psm }}};
+ false ->
+ {{error,insufficient_resources},State}
+ end.
+
+
+remove_service(Handle, State) when is_integer(Handle) ->
+ remove_service(Handle, #serviceRecord.handle, State);
+remove_service(Monitor, State) when is_reference(Monitor) ->
+ remove_service(Monitor, #serviceRecord.monitor, State).
+
+remove_service(Key, Pos, State) ->
+ case lists:keysearch(Key, Pos, State#state.records) of
+ false ->
+ State;
+ {value,R} ->
+ del_monitor(R#serviceRecord.monitor),
+ RFComm = bt_iset:add_elements(R#serviceRecord.rfcomm_list,
+ State#state.rfcomm),
+ PSM = bt_iset:add_elements(R#serviceRecord.psm_list,
+ State#state.psm),
+ Rs = lists:keydelete(Key, Pos, State#state.records),
+ State#state { rfcomm=RFComm, psm=PSM, records = Rs }
+ end.
+
+%% find attribute
+lookup_attribute(ID, ServiceRecord) ->
+ lists:keysearch(ID,1,ServiceRecord).
+
+%% add or replace an attribute
+set_attribute(Attribute={ID,_}, ServiceRecord) ->
+ case lists:keysearch(ID, 1, ServiceRecord) of
+ false ->
+ [Attribute | ServiceRecord];
+ {value,_} ->
+ lists:keyreplace(ID,1,ServiceRecord,Attribute)
+ end.
+
+del_attribute(ID, ServiceRecord) ->
+ lists:keydelete(ID, 1, ServiceRecord).
+
+is_service_record([{ID,Elem}|IDList]) when ID>=0,ID=<16#ffff ->
+ case bt_sdp:is_value(Elem) of
+ true ->
+ is_service_record(IDList);
+ false -> false
+ end;
+is_service_record([_|_]) -> false;
+is_service_record([]) -> true.
+
+
+
+
+
+avail(Channels, FreeSet) ->
+ Sz = bt_iset:size(FreeSet),
+ Len = length(Channels),
+ if Sz < Len ->
+ false;
+ true -> true
+ end.
+
+allocate(Channels,FreeSet) ->
+ %% make dynamic channals (0) last in list
+ RSortedChannels = reverse(lists:sort(Channels)),
+ Sz = bt_iset:size(FreeSet),
+ Len = length(RSortedChannels),
+ if Sz < Len ->
+ io:format("Warning: can not allocate all channels\n");
+ true ->
+ ok
+ end,
+ allocate(RSortedChannels,[],FreeSet).
+
+allocate([0|Cns],Map,FreeSet) ->
+ case bt_iset:size(FreeSet) of
+ 0 -> Map;
+ _ ->
+ Cn1 = bt_iset:first(FreeSet),
+ allocate(Cns,[{0,Cn1}|Map],bt_iset:del_element(Cn1,FreeSet))
+ end;
+allocate([Cn|Cns],Map,FreeSet) ->
+ case bt_iset:is_element(Cn,FreeSet) of
+ true ->
+ allocate(Cns,[{Cn,Cn}|Map],bt_iset:del_element(Cn,FreeSet));
+ false ->
+ case bt_iset:size(FreeSet) of
+ 0 -> Map;
+ _ ->
+ Cn1 = bt_iset:first(FreeSet),
+ allocate(Cns,[{Cn,Cn1}|Map],bt_iset:del_element(Cn1,FreeSet))
+ end
+ end;
+allocate([], Map, FreeSet) ->
+ {Map,FreeSet}.
+
+
+
+get_rfcomm_channels(ServiceRecord) ->
+ case lookup_attribute(?ATTR_ProtocolDescriptorList,ServiceRecord) of
+ false -> [];
+ {value,{_,Value}} ->
+ get_cn(Value)
+ end.
+
+
+set_rfcomm_channels(ServiceRecord, CnMap) ->
+ case lookup_attribute(?ATTR_ProtocolDescriptorList,ServiceRecord) of
+ false ->
+ ServiceRecord;
+ {value,{_,Value}} ->
+ case set_cn(Value, CnMap) of
+ {Value1,[]} ->
+ set_attribute({?ATTR_ProtocolDescriptorList,Value1},
+ ServiceRecord);
+ {Value1,CnMap1} ->
+ io:format("Warning: unable to assign cn's=~p to ~p\n",
+ [CnMap1, Value1]),
+ set_attribute({?ATTR_ProtocolDescriptorList,Value1},
+ ServiceRecord)
+ end
+ end.
+
+
+get_l2cap_psms(ServiceRecord) ->
+ case lookup_attribute(?ATTR_ProtocolDescriptorList,ServiceRecord) of
+ false -> [];
+ {value,{_,Value}} ->
+ get_psm(Value)
+ end.
+
+set_l2cap_psms(ServiceRecord, PsmMap) ->
+ case lookup_attribute(?ATTR_ProtocolDescriptorList,ServiceRecord) of
+ false ->
+ ServiceRecord;
+ {value,{_,Value}} ->
+ case set_psm(Value, PsmMap) of
+ {Value1,[]} ->
+ set_attribute({?ATTR_ProtocolDescriptorList,Value1},
+ ServiceRecord);
+ {Value1,PsmMap1} ->
+ io:format("Warning: unable to assign psm's=~p to ~p\n",
+ [PsmMap1, Value1]),
+ set_attribute({?ATTR_ProtocolDescriptorList,Value1},
+ ServiceRecord)
+ end
+ end.
+
+
+
+%% locate rfcomm channels (0 = allocate)
+get_cn({alternative,List}) ->
+ lists:flatmap(fun(E) -> get_cn(E) end, List);
+get_cn({sequence,[{sequence,[{uuid,?UUID_L2CAP}|_]},
+ {sequence,[{uuid,?UUID_RFCOMM},
+ {uint8,Channel}|_]} | _]}) ->
+ [Channel];
+get_cn({sequence,[{sequence,[{uuid,?UUID_L2CAP}|_]},
+ {sequence,[{uuid,?UUID_RFCOMM}]} | _]}) ->
+ %% need allocation
+ [0];
+get_cn(_) ->
+ [].
+
+
+%% assign rfcomm channels
+set_cn({alternative,List}, CnMap) ->
+ {List1, CnMap1} = set_cn_list(List,[],CnMap),
+ {{alternative,List1},CnMap1};
+set_cn(Value=
+ {sequence,[{sequence,L2CAP}, %% assume L2CAP
+ {sequence,[{uuid,?UUID_RFCOMM},
+ {uint8,Cn}|RFParam]} | T]}, CnMap) ->
+ case lists:keysearch(Cn,1,CnMap) of
+ {value,{Cn,Cn1}} ->
+ {{sequence,[{sequence,L2CAP},
+ {sequence,[{uuid,?UUID_RFCOMM},
+ {uint8,Cn1}|RFParam]} | T]},
+ lists:keydelete(Cn,1,CnMap)};
+ false ->
+ {Value, CnMap}
+ end;
+set_cn(Value={sequence,[{sequence,L2CAP}, %% assume L2CAP
+ {sequence,[{uuid,?UUID_RFCOMM}]} | T]},CnMap) ->
+ case lists:keysearch(0,1,CnMap) of
+ {value,{0,Cn1}} ->
+ {{sequence,[{sequence,L2CAP},
+ {sequence,[{uuid,?UUID_RFCOMM},
+ {uint8,Cn1}]} | T]},
+ lists:keydelete(0,1,CnMap)};
+ false ->
+ {Value,CnMap}
+ end;
+set_cn(Value,CnMap) ->
+ {Value,CnMap}.
+
+set_cn_list([V|Vs],Acc,CnMap) ->
+ {V1, CnMap1} = set_cn(V, CnMap),
+ set_cn_list(Vs,[V1|Acc],CnMap1);
+set_cn_list([],Acc,CnMap) ->
+ {reverse(Acc), CnMap}.
+
+
+%% locate l2cap psm (0 = allocate)
+get_psm({alternative,List}) ->
+ lists:flatmap(fun(E) -> get_psm(E) end, List);
+get_psm({sequence,[{sequence,[{uuid,?UUID_L2CAP},{uint16,PSM}|_]} | _]}) ->
+ [PSM];
+get_psm(_) ->
+ [].
+
+
+%% assign psm
+set_psm({alternative,List}, PsmMap) ->
+ {List1, PsmMap1} = set_psm_list(List,[],PsmMap),
+ {{alternative,List1},PsmMap1};
+set_psm(Value={sequence,[{sequence,[{uuid,?UUID_L2CAP},
+ {uint16,PSM}|L2CAP]} | T]},PsmMap) ->
+ case lists:keysearch(PSM,1,PsmMap) of
+ {value,{PSM,PSM1}} ->
+ {{sequence,[{sequence,[{uuid,?UUID_L2CAP},
+ {uint16,PSM1}|L2CAP]} | T]},
+ lists:keydelete(PSM,1,PsmMap)};
+ false ->
+ {Value, PsmMap}
+ end;
+set_psm(Value,PsmMap) ->
+ {Value,PsmMap}.
+
+set_psm_list([V|Vs],Acc,PsmMap) ->
+ {V1, PsmMap1} = set_psm(V, PsmMap),
+ set_psm_list(Vs,[V1|Acc],PsmMap1);
+set_psm_list([],Acc,PsmMap) ->
+ {reverse(Acc), PsmMap}.
+
+
+%% convert a list of UUID (Mix of binary|string|atom) into
+%% a list of 128 full UUID
+uuid_set(UUIDList) ->
+ ordsets:from_list(
+ map(
+ fun(UUID) when is_binary(UUID) ->
+ bt_sdp:uuid_128(UUID);
+ (UUID) when is_list(UUID); is_atom(UUID) ->
+ bt_sdp:uuid_128(bt_sdp:string_to_uuid(UUID))
+ end, UUIDList)).
+
+attribute_iset(AttributeList) ->
+ bt_iset:from_list(
+ map(
+ fun({A1,A2}) ->
+ {if is_list(A1); is_atom(A1) ->
+ bt_sdp:string_to_attribute(A1);
+ is_integer(A1) ->
+ A1 band 16#ffff
+ end,
+ if is_list(A2); is_atom(A2) ->
+ bt_sdp:string_to_attribute(A2);
+ is_integer(A2) ->
+ A2 band 16#ffff
+ end};
+ (A) when is_list(A); is_atom(A) ->
+ bt_sdp:string_to_attribute(A);
+ (A) when is_integer(A) ->
+ A band 16#ffff
+ end, AttributeList)).
diff --git a/deps/bt/src/bt_sup.erl b/deps/bt/src/bt_sup.erl
new file mode 100644
index 0000000..c23cbdc
--- /dev/null
+++ b/deps/bt/src/bt_sup.erl
@@ -0,0 +1,47 @@
+%%%---- BEGIN COPYRIGHT -------------------------------------------------------
+%%%
+%%% Copyright (C) 2006 - 2014, Rogvall Invest AB, <tony@rogvall.se>
+%%%
+%%% This software is licensed as described in the file COPYRIGHT, which
+%%% you should have received as part of this distribution. The terms
+%%% are also available at http://www.rogvall.se/docs/copyright.txt.
+%%%
+%%% You may opt to use, copy, modify, merge, publish, distribute and/or sell
+%%% copies of the Software, and permit persons to whom the Software is
+%%% furnished to do so, under the terms of the COPYRIGHT file.
+%%%
+%%% This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY
+%%% KIND, either express or implied.
+%%%
+%%%---- END COPYRIGHT ---------------------------------------------------------
+%%% File : bt_sup.erl
+%%% Author : Tony Rogvall <tony@pbook.synap.se>
+%%% Description : bt supervisor
+%%% Created : 2 Feb 2006 by Tony Rogvall <tony@pbook.synap.se>
+
+-module(bt_sup).
+
+-behaviour(supervisor).
+
+%% External exports
+-export([start_link/0]).
+
+%% supervisor callbacks
+-export([init/1]).
+
+%%%----------------------------------------------------------------------
+%%% API
+%%%----------------------------------------------------------------------
+start_link() ->
+ supervisor:start_link({local, ?MODULE}, ?MODULE, []).
+
+%%%----------------------------------------------------------------------
+%%% Callback functions from supervisor
+%%%----------------------------------------------------------------------
+
+%%----------------------------------------------------------------------
+%%----------------------------------------------------------------------
+init([]) ->
+ BtDrv = {bt_drv, {bt_drv, start_link, []},
+ permanent, 5000, worker, [bt_drv]},
+ {ok,{{one_for_all,0,300}, [BtDrv]}}.
diff --git a/deps/bt/src/bt_util.erl b/deps/bt/src/bt_util.erl
new file mode 100644
index 0000000..e4a2821
--- /dev/null
+++ b/deps/bt/src/bt_util.erl
@@ -0,0 +1,101 @@
+%%%---- BEGIN COPYRIGHT -------------------------------------------------------
+%%%
+%%% Copyright (C) 2006 - 2014, Rogvall Invest AB, <tony@rogvall.se>
+%%%
+%%% This software is licensed as described in the file COPYRIGHT, which
+%%% you should have received as part of this distribution. The terms
+%%% are also available at http://www.rogvall.se/docs/copyright.txt.
+%%%
+%%% You may opt to use, copy, modify, merge, publish, distribute and/or sell
+%%% copies of the Software, and permit persons to whom the Software is
+%%% furnished to do so, under the terms of the COPYRIGHT file.
+%%%
+%%% This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY
+%%% KIND, either express or implied.
+%%%
+%%%---- END COPYRIGHT ---------------------------------------------------------
+%%% File : bt.erl
+%%% Author : Tony Rogvall <tony@iMac.local>
+%%% Description : Bluetooth utilities
+%%% Created : 31 Jan 2006 by Tony Rogvall <tony@iMac.local>
+
+-module(bt_util).
+
+-export([getaddr/1]).
+-export([getaddr_by_name/1]).
+-export([uuid_to_string/1]).
+-export([string_to_uuid/1]).
+
+-include("../include/bt.hrl").
+%%
+%% getaddr(Name) -> {ok,Addr} | {error, Reason}
+%%
+%% Convert address into a bluetooth address
+%% either {A,B,C,D,E,F}
+%% or "AA-BB-CC-DD-EE-FF" (hex)
+%% or "AA:BB:CC:DD:EE:FF" (hex)
+%% or "Name" in case the name is resolve
+%%
+getaddr(Addr) when ?is_bt_address(Addr) ->
+ {ok, Addr};
+getaddr(Addr) when is_list(Addr) ->
+ case string:tokens(Addr, ":-") of
+ [As,Bs,Cs,Ds,Es,Fs] ->
+ Res = (catch lists:map(fun(Hx) -> erlang:list_to_integer(Hx,16) end,
+ [As,Bs,Cs,Ds,Es,Fs])),
+ case Res of
+ {'EXIT',_} ->
+ getaddr_by_name(Addr);
+ [A,B,C,D,E,F] ->
+ {ok,{A,B,C,D,E,F}}
+ end;
+ _ ->
+ getaddr_by_name(Addr)
+ end;
+getaddr(Addr) when is_list(Addr) ->
+ getaddr_by_name(Addr);
+getaddr(_) ->
+ {error, einval}.
+
+
+%%
+%% getaddr_by_name(Name) -> {ok,Addr} | {error, Reason}
+%% Find address by name (may be wastlty improved)
+%%
+getaddr_by_name(Name) ->
+ {ok,Devices} = bt_drv:devices(),
+ getaddr_by_name(Devices, Name).
+
+getaddr_by_name([A|As], Name) ->
+ case bt_drv:device_info(A, [name]) of
+ {ok,[{name,Name}]} ->
+ {ok, A};
+ _ ->
+ getaddr_by_name(As, Name)
+ end;
+getaddr_by_name([], _Name) ->
+ {error, einval}.
+
+%% convert uuid to string format
+uuid_to_string(UUID) when ?is_uuid(UUID) ->
+ ?UUID(TLow,TMid,THigh,Clock,Node) = UUID,
+ Fmt =
+ io_lib:format("~8.16.0B-~4.16.0B-~4.16.0B-~4.16.0B-~12.16.0B",
+ [TLow,TMid,THigh,Clock,Node]),
+ lists:flatten(Fmt).
+
+%% convert uuid string format to bina
+string_to_uuid([X1,X2,X3,X4,X5,X6,X7,X8,$-,
+ Y1,Y2,Y3,Y4,$-,
+ Z1,Z2,Z3,Z4,$-,
+ C1,C2,C3,C4,$-,
+ N1,N2,N3,N4,N5,N6,N7,N8,N9,N10,N11,N12]) ->
+ TimeLow = erlang:list_to_integer([X1,X2,X3,X4,X5,X6,X7,X8],16),
+ TimeMid = erlang:list_to_integer([Y1,Y2,Y3,Y4],16),
+ TimeHigh = erlang:list_to_integer([Z1,Z2,Z3,Z4],16),
+ Clock = erlang:list_to_integer([C1,C2,C3,C4],16),
+ Node = erlang:list_to_integer([N1,N2,N3,N4,N5,N6,N7,N8,
+ N9,N10,N11,N12],16),
+ ?UUID(TimeLow,TimeMid,TimeHigh,Clock,Node);
+string_to_uuid(_) ->
+ erlang:error(bad_arg).
diff --git a/deps/bt/src/hci.erl b/deps/bt/src/hci.erl
new file mode 100644
index 0000000..8e12125
--- /dev/null
+++ b/deps/bt/src/hci.erl
@@ -0,0 +1,307 @@
+%%% @author Tony Rogvall <tony@up13>
+%%% @copyright (C) 2015, Tony Rogvall
+%%% @doc
+%%% HCI commands
+%%% @end
+%%% Created : 18 Apr 2015 by Tony Rogvall <tony@up13>
+
+-module(hci).
+
+-compile(export_all).
+
+-include("../include/hci_drv.hrl").
+-include("hci_api.hrl").
+
+-define(DEFAULT_TIMEOUT, 5000).
+-define(INQUIRY_TIMEOUT, 10000).
+
+open(DevID) ->
+ Hci = hci_drv:open(),
+ case hci_drv:bind(Hci, DevID) of
+ ok ->
+ hci_drv:activate(Hci),
+ {ok,Hci};
+ Error -> Error
+ end.
+
+open() ->
+ Hci = hci_drv:open(),
+ case hci_drv:get_dev_list(Hci) of
+ {ok,[]} -> {error, enoent};
+ {ok,[{DevID,_}|_]} ->
+ case hci_drv:bind(Hci, DevID) of
+ ok ->
+ hci_drv:activate(Hci),
+ {ok,Hci};
+ Error -> Error
+ end;
+ Error ->
+ Error
+ end.
+
+close(Hci) ->
+ hci_drv:close(Hci).
+
+%% dump information about bluetooth devices
+i() ->
+ Hci = hci_drv:open(),
+ case hci_drv:get_dev_list(Hci) of
+ {ok,Devs} ->
+ lists:foreach(
+ fun({DevID,_DevFlags}) ->
+ case hci_drv:get_dev_info(Hci, DevID) of
+ {ok, Info} ->
+ io:format("~s\n",
+ [hci_util:format_hci_dev_info(Info)]);
+ Error ->
+ io:format("error: ~p\n", [Error])
+ end
+ end, Devs);
+ Error ->
+ Error
+ end.
+
+scan() ->
+ scan(?INQUIRY_TIMEOUT).
+
+scan(Timeout) ->
+ {ok,Hci} = open(),
+ case hci_drv:inquiry(Hci, Timeout, 10, 0, 0) of
+ {ok,Is} ->
+ lists:foreach(
+ fun(I) ->
+ io:format("~s: dev_class=~w, clock_offset=~w\n",
+ [bt:format_address(I#inquiry_info.bdaddr),
+ I#inquiry_info.dev_class,
+ I#inquiry_info.clock_offset]),
+ io:format(" pscan_rep_mode:~w\n"
+ " pscan_period_mode:~w\n"
+ " pscan_mode:~w\n",
+ [ I#inquiry_info.pscan_rep_mode,
+ I#inquiry_info.pscan_period_mode,
+ I#inquiry_info.pscan_mode]),
+ case read_remote_name(Hci, I, ?DEFAULT_TIMEOUT) of
+ {ok,Name} ->
+ io:format(" Name: ~s\n", [Name]);
+ _Error ->
+ ok
+ end
+ end, Is);
+ Error ->
+ Error
+ end.
+
+
+create_connection(Hci, Bdaddr, Pkt_type, Clock_offset, Role_switch, Timeout) ->
+ Pscan_rep_mode = 16#02,
+ Pscan_mode = 0,
+ case call(Hci,?OGF_LINK_CTL,?OCF_CREATE_CONN,
+ <<?create_conn_cp_bin(Bdaddr,Pkt_type,Pscan_rep_mode,Pscan_mode,Clock_offset,Role_switch)>>,
+ ?EVT_CONN_COMPLETE, Timeout) of
+ {ok, <<?evt_conn_complete_bin(0,Handle,_Bdaddr,_Link_type,_Encr_mode)>>} ->
+ {ok, Handle};
+ {ok, <<?evt_conn_complete_bin(Status,_Handle,_Bdaddr,_Link_type,_Encr_mode)>>} ->
+ %% fixme: decode status
+ {error, Status};
+ Error ->
+ Error
+ end.
+
+disconnect(Hci, Handle, Reason, Timeout) ->
+ case call(Hci, ?OGF_LINK_CTL, ?OCF_DISCONNECT,
+ <<?disconnect_cp_bin(Handle,Reason)>>,
+ ?EVT_DISCONN_COMPLETE, Timeout) of
+ {ok, <<?evt_disconn_complete_bin(0,Handle,Reason)>>} ->
+ ok;
+ {ok, <<?evt_disconn_complete_bin(_Status,Handle,Reason)>>} ->
+ {error,eio};
+ Error ->
+ Error
+ end.
+
+read_remote_name(Bdaddr) ->
+ with_socket(fun(Hci) -> read_remote_name(Hci,Bdaddr,?DEFAULT_TIMEOUT) end).
+
+
+read_remote_name(Hci,
+ #inquiry_info {
+ bdaddr=Bdaddr,
+ pscan_rep_mode=Pscan_rep_mode,
+ pscan_mode=Pscan_mode,
+ clock_offset=Clock_offset }, Timeout) ->
+ read_remote_name(Hci,Bdaddr,Pscan_rep_mode,Pscan_mode,
+ Clock_offset bor 16#8000,Timeout);
+read_remote_name(Hci, Bdaddr, Timeout) when is_binary(Bdaddr) ->
+ read_remote_name(Hci,Bdaddr,16#02,16#00,16#0000,Timeout);
+read_remote_name(Hci, Bdaddr, Timeout) when is_list(Bdaddr) ->
+ case bt:getaddr(Bdaddr) of
+ {ok,{A,B,C,D,E,F}} ->
+ read_remote_name(Hci, <<F,E,D,C,B,A>>, Timeout);
+ Error ->
+ Error
+ end.
+
+read_remote_name(Hci,Bdaddr,Pscan_rep_mode,Pscan_mode,Clock_offset,Timeout) ->
+ case call(Hci,?OGF_LINK_CTL,?OCF_REMOTE_NAME_REQ,
+ <<?remote_name_req_cp_bin(Bdaddr,Pscan_rep_mode,Pscan_mode,
+ Clock_offset)>>,
+ ?EVT_REMOTE_NAME_REQ_COMPLETE,Timeout) of
+ {ok, <<?evt_remote_name_req_complete_bin(0,_Bdaddr,Name)>>} ->
+ {ok,hci_util:c_string(Name)};
+ {ok, <<?evt_remote_name_req_complete_bin(_Status,_Bdaddr,_Name)>>} ->
+ {error, eio};
+ Error -> Error
+ end.
+
+read_local_name() ->
+ with_socket(fun(Hci) -> read_local_name(Hci, ?DEFAULT_TIMEOUT) end).
+
+read_local_name(Hci, Timeout) ->
+ case call(Hci,?OGF_HOST_CTL,?OCF_READ_LOCAL_NAME,<<>>,0,Timeout) of
+ {ok, <<?read_local_name_rp_bin(0,Name)>>} ->
+ {ok, hci_util:c_string(Name)};
+ {ok, <<?read_local_name_rp_bin(_Status,_Name)>>} ->
+ {error, eio};
+ Error ->
+ Error
+ end.
+
+
+%% sending data and calling procedures over HCI socket
+
+send(Hci,Opcode,Data) ->
+ Pkt = <<?HCI_COMMAND_PKT,Opcode:16/little,
+ (byte_size(Data)):8,Data/binary>>,
+ R = hci_drv:send(Hci,Pkt),
+ io:format("send ~p = ~p\n", [Pkt,R]),
+ R.
+
+send(Hci, OGF, OCF, Data) ->
+ send(Hci, ?cmd_opcode_pack(OGF,OCF), Data).
+
+call(Hci,OGF,OCF,Data,Event,Timeout) ->
+ Opcode = ?cmd_opcode_pack(OGF,OCF),
+ {ok,OldFilter} = hci_drv:get_filter(Hci),
+ %% io:format("call: saved_filter = ~p\n", [OldFilter]),
+ NewFilter = hci_drv:make_filter(Opcode,
+ [?HCI_EVENT_PKT],
+ [?EVT_CMD_STATUS,
+ ?EVT_CMD_COMPLETE,
+ ?EVT_LE_META_EVENT,
+ Event]),
+ %% io:format("call: new_filter = ~p\n", [NewFilter]),
+ case hci_drv:set_filter(Hci, NewFilter) of
+ ok ->
+ true = send(Hci,Opcode,Data),
+ Reply = wait(Hci,Opcode,Event,10,Timeout),
+ hci_drv:set_filter(Hci, OldFilter),
+ Reply;
+ Error ->
+ Error
+ end.
+
+wait(Hci,Opcode,Event,Try,Timeout) ->
+ TRef = start_timer(Timeout),
+ Result = wait_(Hci,Opcode,Event,Try,TRef),
+ case Result of
+ timeout ->
+ cancel_timer(TRef),
+ {error, timedout};
+ Result ->
+ Result
+ end.
+
+wait_(_Hci,_Opcode,_Event,0,TRef) ->
+ cancel_timer(TRef),
+ {error, timedout};
+
+wait_(Hci,Opcode,Event,Try,TRef) ->
+ receive
+ {Hci,Data0={data,<<_:8,Evt:8,Plen:8,Packet:Plen/binary,_/binary>>}} ->
+ io:format("Got data ~p\n", [Data0]),
+ case Evt of
+ ?EVT_CMD_STATUS ->
+ io:format("got cmd_status\n", []),
+ case Packet of
+ <<?evt_cmd_status_bin(0,_Ncmd,Opcode),R/binary>> ->
+ if Evt =/= Event ->
+ wait_(Hci,Opcode,Event,Try-1,TRef);
+ true ->
+ {ok,R}
+ end;
+ <<?evt_cmd_status_bin(_Status,_Ncmd,Opcode),_R/binary>> ->
+ {error, eio}; %% other status?
+ <<?evt_cmd_status_bin(_S,_Ncmd,_Opcode),_R/binary>> ->
+ wait_(Hci,Opcode,Event,Try-1,TRef)
+ end;
+ ?EVT_CMD_COMPLETE ->
+ io:format("got cmd_complete\n", []),
+ case Packet of
+ <<?evt_cmd_complete_bin(_Ncmd,Opcode),R/binary>> ->
+ {ok,R};
+ <<?evt_cmd_complete_bin(_Ncmd,_Opcode),_R/binary>> ->
+ wait_(Hci,Opcode,Event,Try-1,TRef)
+ end;
+
+ ?EVT_REMOTE_NAME_REQ_COMPLETE when Evt =:= Event ->
+ io:format("got remote_name_req_complete\n", []),
+ case Packet of
+ <<?evt_remote_name_req_complete_bin(_Status,_Bdaddr,_Name)>> ->
+ %% fixme: check Bdaddr!
+ {ok,Packet}
+ end;
+ ?EVT_LE_META_EVENT ->
+ io:format("got evt_le_meta_event\n", []),
+ case Packet of
+ <<?evt_le_meta_event_bin(SEvt,_D1),LePacket/binary>> ->
+ if SEvt =:= Event ->
+ {ok,LePacket};
+ true ->
+ wait_(Hci,Opcode,Event,Try-1,TRef)
+ end
+ end;
+ _ ->
+ io:format("got event ~p\n", [Evt]),
+ {ok,Packet}
+ end;
+
+ {timeout,TRef,_} ->
+ timeout
+ end.
+
+rev_bin(Bin) ->
+ list_to_binary(lists:reverse(binary_to_list(Bin))).
+
+start_timer(0) -> undefined;
+start_timer(Timeout) -> erlang:start_timer(Timeout, self(), timeout).
+
+cancel_timer(undefined) -> false;
+cancel_timer(TRef) ->
+ erlang:cancel_timer(TRef),
+ receive {timeout,TRef,_} -> ok
+ after 0 -> ok
+ end.
+
+with_socket(Fun) ->
+ {ok,S} = open(),
+ try Fun(S) of
+ Result ->
+ Result
+ catch
+ error:Error ->
+ io:format("hci: with_socket crash: ~p\n", [Error]),
+ lists:foreach(fun(Item) -> print_item(Item) end,
+ erlang:get_stacktrace()),
+ {error,Error}
+ after
+ close(S)
+ end.
+
+print_item({Module,Function,ArityArgs,Location}) ->
+ File = proplists:get_value(file,Location,""),
+ Line = proplists:get_value(line,Location,0),
+ {Arity,Args} =
+ if is_list(ArityArgs) -> {length(ArityArgs),ArityArgs};
+ is_integer(ArityArgs) -> {ArityArgs, []}
+ end,
+ io:format(" ~s:~w: ~s:~s/~w ~p\n", [File,Line,Module,Function,Arity,Args]).
diff --git a/deps/bt/src/hci_api.erl b/deps/bt/src/hci_api.erl
new file mode 100644
index 0000000..a923a71
--- /dev/null
+++ b/deps/bt/src/hci_api.erl
@@ -0,0 +1,1462 @@
+%% -*- erlang -*-
+-module(hci_api).
+-compile(export_all).
+-include("hci_api.hrl").
+
+
+cname(<<0,_/binary>>) -> [];
+cname(<<C,Cs/binary>>) -> [C|cname(Cs)];
+cname(<<>>) -> [].
+
+
+decode_status_bdaddr_rp(_Data) ->
+ case _Data of
+ <<?status_bdaddr_rp_bin(Status,Bdaddr)>> ->
+ #status_bdaddr_rp { status = Status,bdaddr = Bdaddr }
+ end.
+
+decode_cancel_logical_link_rp(_Data) ->
+ case _Data of
+ <<?cancel_logical_link_rp_bin(Status,Handle,Tx_flow_id)>> ->
+ #cancel_logical_link_rp { status = Status,handle = Handle,tx_flow_id = Tx_flow_id }
+ end.
+
+decode_role_discovery_rp(_Data) ->
+ case _Data of
+ <<?role_discovery_rp_bin(Status,Handle,Role)>> ->
+ #role_discovery_rp { status = Status,handle = Handle,role = Role }
+ end.
+
+decode_read_link_policy_rp(_Data) ->
+ case _Data of
+ <<?read_link_policy_rp_bin(Status,Handle,Policy)>> ->
+ #read_link_policy_rp { status = Status,handle = Handle,policy = Policy }
+ end.
+
+decode_write_link_policy_rp(_Data) ->
+ case _Data of
+ <<?write_link_policy_rp_bin(Status,Handle)>> ->
+ #write_link_policy_rp { status = Status,handle = Handle }
+ end.
+
+decode_read_pin_type_rp(_Data) ->
+ case _Data of
+ <<?read_pin_type_rp_bin(Status,Pin_type)>> ->
+ #read_pin_type_rp { status = Status,pin_type = Pin_type }
+ end.
+
+decode_read_stored_link_key_rp(_Data) ->
+ case _Data of
+ <<?read_stored_link_key_rp_bin(Status,Max_keys,Num_keys)>> ->
+ #read_stored_link_key_rp { status = Status,max_keys = Max_keys,num_keys = Num_keys }
+ end.
+
+decode_write_stored_link_key_rp(_Data) ->
+ case _Data of
+ <<?write_stored_link_key_rp_bin(Status,Num_keys)>> ->
+ #write_stored_link_key_rp { status = Status,num_keys = Num_keys }
+ end.
+
+decode_delete_stored_link_key_rp(_Data) ->
+ case _Data of
+ <<?delete_stored_link_key_rp_bin(Status,Num_keys)>> ->
+ #delete_stored_link_key_rp { status = Status,num_keys = Num_keys }
+ end.
+
+decode_read_local_name_rp(_Data) ->
+ case _Data of
+ <<?read_local_name_rp_bin(Status,Name)>> ->
+ #read_local_name_rp { status = Status,name = Name }
+ end.
+
+decode_read_conn_accept_timeout_rp(_Data) ->
+ case _Data of
+ <<?read_conn_accept_timeout_rp_bin(Status,Timeout)>> ->
+ #read_conn_accept_timeout_rp { status = Status,timeout = Timeout }
+ end.
+
+decode_read_page_timeout_rp(_Data) ->
+ case _Data of
+ <<?read_page_timeout_rp_bin(Status,Timeout)>> ->
+ #read_page_timeout_rp { status = Status,timeout = Timeout }
+ end.
+
+decode_read_scan_enable_rp(_Data) ->
+ case _Data of
+ <<?read_scan_enable_rp_bin(Status,Enable)>> ->
+ #read_scan_enable_rp { status = Status,enable = Enable }
+ end.
+
+decode_read_page_activity_rp(_Data) ->
+ case _Data of
+ <<?read_page_activity_rp_bin(Status,Interval,Window)>> ->
+ #read_page_activity_rp { status = Status,interval = Interval,window = Window }
+ end.
+
+decode_read_inq_activity_rp(_Data) ->
+ case _Data of
+ <<?read_inq_activity_rp_bin(Status,Interval,Window)>> ->
+ #read_inq_activity_rp { status = Status,interval = Interval,window = Window }
+ end.
+
+decode_read_class_of_dev_rp(_Data) ->
+ case _Data of
+ <<?read_class_of_dev_rp_bin(Status,Dev_class)>> ->
+ #read_class_of_dev_rp { status = Status,dev_class = Dev_class }
+ end.
+
+decode_read_voice_setting_rp(_Data) ->
+ case _Data of
+ <<?read_voice_setting_rp_bin(Status,Voice_setting)>> ->
+ #read_voice_setting_rp { status = Status,voice_setting = Voice_setting }
+ end.
+
+decode_read_transmit_power_level_rp(_Data) ->
+ case _Data of
+ <<?read_transmit_power_level_rp_bin(Status,Handle,Level)>> ->
+ #read_transmit_power_level_rp { status = Status,handle = Handle,level = Level }
+ end.
+
+decode_read_link_supervision_timeout_rp(_Data) ->
+ case _Data of
+ <<?read_link_supervision_timeout_rp_bin(Status,Handle,Timeout)>> ->
+ #read_link_supervision_timeout_rp { status = Status,handle = Handle,timeout = Timeout }
+ end.
+
+decode_write_link_supervision_timeout_rp(_Data) ->
+ case _Data of
+ <<?write_link_supervision_timeout_rp_bin(Status,Handle)>> ->
+ #write_link_supervision_timeout_rp { status = Status,handle = Handle }
+ end.
+
+decode_read_current_iac_lap_rp(_Data) ->
+ case _Data of
+ <<?read_current_iac_lap_rp_bin(Status,Num_current_iac,Lap)>> ->
+ #read_current_iac_lap_rp { status = Status,num_current_iac = Num_current_iac,lap = Lap }
+ end.
+
+decode_set_afh_classification_rp(_Data) ->
+ case _Data of
+ <<?set_afh_classification_rp_bin(Status)>> ->
+ #set_afh_classification_rp { status = Status }
+ end.
+
+decode_read_inquiry_scan_type_rp(_Data) ->
+ case _Data of
+ <<?read_inquiry_scan_type_rp_bin(Status,Type)>> ->
+ #read_inquiry_scan_type_rp { status = Status,type = Type }
+ end.
+
+decode_write_inquiry_scan_type_rp(_Data) ->
+ case _Data of
+ <<?write_inquiry_scan_type_rp_bin(Status)>> ->
+ #write_inquiry_scan_type_rp { status = Status }
+ end.
+
+decode_read_inquiry_mode_rp(_Data) ->
+ case _Data of
+ <<?read_inquiry_mode_rp_bin(Status,Mode)>> ->
+ #read_inquiry_mode_rp { status = Status,mode = Mode }
+ end.
+
+decode_write_inquiry_mode_rp(_Data) ->
+ case _Data of
+ <<?write_inquiry_mode_rp_bin(Status)>> ->
+ #write_inquiry_mode_rp { status = Status }
+ end.
+
+decode_read_afh_mode_rp(_Data) ->
+ case _Data of
+ <<?read_afh_mode_rp_bin(Status,Mode)>> ->
+ #read_afh_mode_rp { status = Status,mode = Mode }
+ end.
+
+decode_write_afh_mode_rp(_Data) ->
+ case _Data of
+ <<?write_afh_mode_rp_bin(Status)>> ->
+ #write_afh_mode_rp { status = Status }
+ end.
+
+decode_read_ext_inquiry_response_rp(_Data) ->
+ case _Data of
+ <<?read_ext_inquiry_response_rp_bin(Status,Fec,Data)>> ->
+ #read_ext_inquiry_response_rp { status = Status,fec = Fec,data = Data }
+ end.
+
+decode_write_ext_inquiry_response_rp(_Data) ->
+ case _Data of
+ <<?write_ext_inquiry_response_rp_bin(Status)>> ->
+ #write_ext_inquiry_response_rp { status = Status }
+ end.
+
+decode_refresh_encryption_key_rp(_Data) ->
+ case _Data of
+ <<?refresh_encryption_key_rp_bin(Status)>> ->
+ #refresh_encryption_key_rp { status = Status }
+ end.
+
+decode_read_simple_pairing_mode_rp(_Data) ->
+ case _Data of
+ <<?read_simple_pairing_mode_rp_bin(Status,Mode)>> ->
+ #read_simple_pairing_mode_rp { status = Status,mode = Mode }
+ end.
+
+decode_write_simple_pairing_mode_rp(_Data) ->
+ case _Data of
+ <<?write_simple_pairing_mode_rp_bin(Status)>> ->
+ #write_simple_pairing_mode_rp { status = Status }
+ end.
+
+decode_read_local_oob_data_rp(_Data) ->
+ case _Data of
+ <<?read_local_oob_data_rp_bin(Status,Hash,Randomizer)>> ->
+ #read_local_oob_data_rp { status = Status,hash = Hash,randomizer = Randomizer }
+ end.
+
+decode_read_inq_response_tx_power_level_rp(_Data) ->
+ case _Data of
+ <<?read_inq_response_tx_power_level_rp_bin(Status,Level)>> ->
+ #read_inq_response_tx_power_level_rp { status = Status,level = Level }
+ end.
+
+decode_read_inquiry_transmit_power_level_rp(_Data) ->
+ case _Data of
+ <<?read_inquiry_transmit_power_level_rp_bin(Status,Level)>> ->
+ #read_inquiry_transmit_power_level_rp { status = Status,level = Level }
+ end.
+
+decode_write_inquiry_transmit_power_level_rp(_Data) ->
+ case _Data of
+ <<?write_inquiry_transmit_power_level_rp_bin(Status)>> ->
+ #write_inquiry_transmit_power_level_rp { status = Status }
+ end.
+
+decode_read_default_error_data_reporting_rp(_Data) ->
+ case _Data of
+ <<?read_default_error_data_reporting_rp_bin(Status,Reporting)>> ->
+ #read_default_error_data_reporting_rp { status = Status,reporting = Reporting }
+ end.
+
+decode_write_default_error_data_reporting_rp(_Data) ->
+ case _Data of
+ <<?write_default_error_data_reporting_rp_bin(Status)>> ->
+ #write_default_error_data_reporting_rp { status = Status }
+ end.
+
+decode_send_keypress_notify_rp(_Data) ->
+ case _Data of
+ <<?send_keypress_notify_rp_bin(Status)>> ->
+ #send_keypress_notify_rp { status = Status }
+ end.
+
+decode_read_log_link_accept_timeout_rp(_Data) ->
+ case _Data of
+ <<?read_log_link_accept_timeout_rp_bin(Status,Timeout)>> ->
+ #read_log_link_accept_timeout_rp { status = Status,timeout = Timeout }
+ end.
+
+decode_read_enhanced_transmit_power_level_rp(_Data) ->
+ case _Data of
+ <<?read_enhanced_transmit_power_level_rp_bin(Status,Handle,Level_gfsk,Level_dqpsk,Level_8dpsk)>> ->
+ #read_enhanced_transmit_power_level_rp { status = Status,handle = Handle,level_gfsk = Level_gfsk,level_dqpsk = Level_dqpsk,level_8dpsk = Level_8dpsk }
+ end.
+
+decode_read_best_effort_flush_timeout_rp(_Data) ->
+ case _Data of
+ <<?read_best_effort_flush_timeout_rp_bin(Status,Timeout)>> ->
+ #read_best_effort_flush_timeout_rp { status = Status,timeout = Timeout }
+ end.
+
+decode_write_best_effort_flush_timeout_rp(_Data) ->
+ case _Data of
+ <<?write_best_effort_flush_timeout_rp_bin(Status)>> ->
+ #write_best_effort_flush_timeout_rp { status = Status }
+ end.
+
+decode_read_le_host_supported_rp(_Data) ->
+ case _Data of
+ <<?read_le_host_supported_rp_bin(Status,Le,Simul)>> ->
+ #read_le_host_supported_rp { status = Status,le = Le,simul = Simul }
+ end.
+
+decode_read_local_version_rp(_Data) ->
+ case _Data of
+ <<?read_local_version_rp_bin(Status,Hci_ver,Hci_rev,Lmp_ver,Manufacturer,Lmp_subver)>> ->
+ #read_local_version_rp { status = Status,hci_ver = Hci_ver,hci_rev = Hci_rev,lmp_ver = Lmp_ver,manufacturer = Manufacturer,lmp_subver = Lmp_subver }
+ end.
+
+decode_read_local_commands_rp(_Data) ->
+ case _Data of
+ <<?read_local_commands_rp_bin(Status,Commands)>> ->
+ #read_local_commands_rp { status = Status,commands = Commands }
+ end.
+
+decode_read_local_features_rp(_Data) ->
+ case _Data of
+ <<?read_local_features_rp_bin(Status,Features)>> ->
+ #read_local_features_rp { status = Status,features = Features }
+ end.
+
+decode_read_local_ext_features_rp(_Data) ->
+ case _Data of
+ <<?read_local_ext_features_rp_bin(Status,Page_num,Max_page_num,Features)>> ->
+ #read_local_ext_features_rp { status = Status,page_num = Page_num,max_page_num = Max_page_num,features = Features }
+ end.
+
+decode_read_buffer_size_rp(_Data) ->
+ case _Data of
+ <<?read_buffer_size_rp_bin(Status,Acl_mtu,Sco_mtu,Acl_max_pkt,Sco_max_pkt)>> ->
+ #read_buffer_size_rp { status = Status,acl_mtu = Acl_mtu,sco_mtu = Sco_mtu,acl_max_pkt = Acl_max_pkt,sco_max_pkt = Sco_max_pkt }
+ end.
+
+decode_read_bd_addr_rp(_Data) ->
+ case _Data of
+ <<?read_bd_addr_rp_bin(Status,Bdaddr)>> ->
+ #read_bd_addr_rp { status = Status,bdaddr = Bdaddr }
+ end.
+
+decode_read_failed_contact_counter_rp(_Data) ->
+ case _Data of
+ <<?read_failed_contact_counter_rp_bin(Status,Handle,Counter)>> ->
+ #read_failed_contact_counter_rp { status = Status,handle = Handle,counter = Counter }
+ end.
+
+decode_reset_failed_contact_counter_rp(_Data) ->
+ case _Data of
+ <<?reset_failed_contact_counter_rp_bin(Status,Handle)>> ->
+ #reset_failed_contact_counter_rp { status = Status,handle = Handle }
+ end.
+
+decode_read_link_quality_rp(_Data) ->
+ case _Data of
+ <<?read_link_quality_rp_bin(Status,Handle,Link_quality)>> ->
+ #read_link_quality_rp { status = Status,handle = Handle,link_quality = Link_quality }
+ end.
+
+decode_read_rssi_rp(_Data) ->
+ case _Data of
+ <<?read_rssi_rp_bin(Status,Handle,Rssi)>> ->
+ #read_rssi_rp { status = Status,handle = Handle,rssi = Rssi }
+ end.
+
+decode_read_afh_map_rp(_Data) ->
+ case _Data of
+ <<?read_afh_map_rp_bin(Status,Handle,Mode,Map)>> ->
+ #read_afh_map_rp { status = Status,handle = Handle,mode = Mode,map = Map }
+ end.
+
+decode_read_clock_rp(_Data) ->
+ case _Data of
+ <<?read_clock_rp_bin(Status,Handle,Clock,Accuracy)>> ->
+ #read_clock_rp { status = Status,handle = Handle,clock = Clock,accuracy = Accuracy }
+ end.
+
+decode_read_local_amp_info_rp(_Data) ->
+ case _Data of
+ <<?read_local_amp_info_rp_bin(Status,Amp_status,Total_bandwidth,Max_guaranteed_bandwidth,Min_latency,Max_pdu_size,Controller_type,Pal_caps,Max_amp_assoc_length,Max_flush_timeout,Best_effort_flush_timeout)>> ->
+ #read_local_amp_info_rp { status = Status,amp_status = Amp_status,total_bandwidth = Total_bandwidth,max_guaranteed_bandwidth = Max_guaranteed_bandwidth,min_latency = Min_latency,max_pdu_size = Max_pdu_size,controller_type = Controller_type,pal_caps = Pal_caps,max_amp_assoc_length = Max_amp_assoc_length,max_flush_timeout = Max_flush_timeout,best_effort_flush_timeout = Best_effort_flush_timeout }
+ end.
+
+decode_read_local_amp_assoc_rp(_Data) ->
+ case _Data of
+ <<?read_local_amp_assoc_rp_bin(Status,Handle,Rem_len,Frag)>> ->
+ #read_local_amp_assoc_rp { status = Status,handle = Handle,rem_len = Rem_len,frag = Frag }
+ end.
+
+decode_write_remote_amp_assoc_rp(_Data) ->
+ case _Data of
+ <<?write_remote_amp_assoc_rp_bin(Status,Handle)>> ->
+ #write_remote_amp_assoc_rp { status = Status,handle = Handle }
+ end.
+
+decode_write_simple_pairing_debug_mode_rp(_Data) ->
+ case _Data of
+ <<?write_simple_pairing_debug_mode_rp_bin(Status)>> ->
+ #write_simple_pairing_debug_mode_rp { status = Status }
+ end.
+
+decode_le_read_buffer_size_rp(_Data) ->
+ case _Data of
+ <<?le_read_buffer_size_rp_bin(Status,Pkt_len,Max_pkt)>> ->
+ #le_read_buffer_size_rp { status = Status,pkt_len = Pkt_len,max_pkt = Max_pkt }
+ end.
+
+decode_le_read_local_supported_features_rp(_Data) ->
+ case _Data of
+ <<?le_read_local_supported_features_rp_bin(Status,Features)>> ->
+ #le_read_local_supported_features_rp { status = Status,features = Features }
+ end.
+
+decode_le_read_advertising_channel_tx_power_rp(_Data) ->
+ case _Data of
+ <<?le_read_advertising_channel_tx_power_rp_bin(Status,Level)>> ->
+ #le_read_advertising_channel_tx_power_rp { status = Status,level = Level }
+ end.
+
+decode_le_read_white_list_size_rp(_Data) ->
+ case _Data of
+ <<?le_read_white_list_size_rp_bin(Status,Size)>> ->
+ #le_read_white_list_size_rp { status = Status,size = Size }
+ end.
+
+decode_le_read_channel_map_rp(_Data) ->
+ case _Data of
+ <<?le_read_channel_map_rp_bin(Status,Handle,Map)>> ->
+ #le_read_channel_map_rp { status = Status,handle = Handle,map = Map }
+ end.
+
+decode_le_encrypt_rp(_Data) ->
+ case _Data of
+ <<?le_encrypt_rp_bin(Status,Data)>> ->
+ #le_encrypt_rp { status = Status,data = Data }
+ end.
+
+decode_le_rand_rp(_Data) ->
+ case _Data of
+ <<?le_rand_rp_bin(Status,Random)>> ->
+ #le_rand_rp { status = Status,random = Random }
+ end.
+
+decode_le_ltk_reply_rp(_Data) ->
+ case _Data of
+ <<?le_ltk_reply_rp_bin(Status,Handle)>> ->
+ #le_ltk_reply_rp { status = Status,handle = Handle }
+ end.
+
+decode_le_ltk_neg_reply_rp(_Data) ->
+ case _Data of
+ <<?le_ltk_neg_reply_rp_bin(Status,Handle)>> ->
+ #le_ltk_neg_reply_rp { status = Status,handle = Handle }
+ end.
+
+decode_le_read_supported_states_rp(_Data) ->
+ case _Data of
+ <<?le_read_supported_states_rp_bin(Status,States)>> ->
+ #le_read_supported_states_rp { status = Status,states = States }
+ end.
+
+decode_le_test_end_rp(_Data) ->
+ case _Data of
+ <<?le_test_end_rp_bin(Status,Num_pkts)>> ->
+ #le_test_end_rp { status = Status,num_pkts = Num_pkts }
+ end.
+
+decode_inquiry_info(_Data) ->
+ case _Data of
+ <<?inquiry_info_bin(Bdaddr,Pscan_rep_mode,Pscan_period_mode,Pscan_mode,Dev_class,Clock_offset)>> ->
+ #inquiry_info { bdaddr = Bdaddr,pscan_rep_mode = Pscan_rep_mode,pscan_period_mode = Pscan_period_mode,pscan_mode = Pscan_mode,dev_class = Dev_class,clock_offset = Clock_offset }
+ end.
+
+decode_evt_conn_complete(_Data) ->
+ case _Data of
+ <<?evt_conn_complete_bin(Status,Handle,Bdaddr,Link_type,Encr_mode)>> ->
+ #evt_conn_complete { status = Status,handle = Handle,bdaddr = Bdaddr,link_type = Link_type,encr_mode = Encr_mode }
+ end.
+
+decode_evt_conn_request(_Data) ->
+ case _Data of
+ <<?evt_conn_request_bin(Bdaddr,Dev_class,Link_type)>> ->
+ #evt_conn_request { bdaddr = Bdaddr,dev_class = Dev_class,link_type = Link_type }
+ end.
+
+decode_evt_disconn_complete(_Data) ->
+ case _Data of
+ <<?evt_disconn_complete_bin(Status,Handle,Reason)>> ->
+ #evt_disconn_complete { status = Status,handle = Handle,reason = Reason }
+ end.
+
+decode_evt_auth_complete(_Data) ->
+ case _Data of
+ <<?evt_auth_complete_bin(Status,Handle)>> ->
+ #evt_auth_complete { status = Status,handle = Handle }
+ end.
+
+decode_evt_remote_name_req_complete(_Data) ->
+ case _Data of
+ <<?evt_remote_name_req_complete_bin(Status,Bdaddr,Name)>> ->
+ #evt_remote_name_req_complete { status = Status,bdaddr = Bdaddr,name = Name }
+ end.
+
+decode_evt_encrypt_change(_Data) ->
+ case _Data of
+ <<?evt_encrypt_change_bin(Status,Handle,Encrypt)>> ->
+ #evt_encrypt_change { status = Status,handle = Handle,encrypt = Encrypt }
+ end.
+
+decode_evt_change_conn_link_key_complete(_Data) ->
+ case _Data of
+ <<?evt_change_conn_link_key_complete_bin(Status,Handle)>> ->
+ #evt_change_conn_link_key_complete { status = Status,handle = Handle }
+ end.
+
+decode_evt_master_link_key_complete(_Data) ->
+ case _Data of
+ <<?evt_master_link_key_complete_bin(Status,Handle,Key_flag)>> ->
+ #evt_master_link_key_complete { status = Status,handle = Handle,key_flag = Key_flag }
+ end.
+
+decode_evt_read_remote_features_complete(_Data) ->
+ case _Data of
+ <<?evt_read_remote_features_complete_bin(Status,Handle,Features)>> ->
+ #evt_read_remote_features_complete { status = Status,handle = Handle,features = Features }
+ end.
+
+decode_evt_read_remote_version_complete(_Data) ->
+ case _Data of
+ <<?evt_read_remote_version_complete_bin(Status,Handle,Lmp_ver,Manufacturer,Lmp_subver)>> ->
+ #evt_read_remote_version_complete { status = Status,handle = Handle,lmp_ver = Lmp_ver,manufacturer = Manufacturer,lmp_subver = Lmp_subver }
+ end.
+
+decode_evt_qos_setup_complete(_Data) ->
+ case _Data of
+ <<?evt_qos_setup_complete_bin(Status,Handle,Flags,Qos)>> ->
+ #evt_qos_setup_complete { status = Status,handle = Handle,flags = Flags,qos = Qos }
+ end.
+
+decode_evt_cmd_complete(_Data) ->
+ case _Data of
+ <<?evt_cmd_complete_bin(Ncmd,Opcode)>> ->
+ #evt_cmd_complete { ncmd = Ncmd,opcode = Opcode }
+ end.
+
+decode_evt_cmd_status(_Data) ->
+ case _Data of
+ <<?evt_cmd_status_bin(Status,Ncmd,Opcode)>> ->
+ #evt_cmd_status { status = Status,ncmd = Ncmd,opcode = Opcode }
+ end.
+
+decode_evt_hardware_error(_Data) ->
+ case _Data of
+ <<?evt_hardware_error_bin(Code)>> ->
+ #evt_hardware_error { code = Code }
+ end.
+
+decode_evt_flush_occured(_Data) ->
+ case _Data of
+ <<?evt_flush_occured_bin(Handle)>> ->
+ #evt_flush_occured { handle = Handle }
+ end.
+
+decode_evt_role_change(_Data) ->
+ case _Data of
+ <<?evt_role_change_bin(Status,Bdaddr,Role)>> ->
+ #evt_role_change { status = Status,bdaddr = Bdaddr,role = Role }
+ end.
+
+decode_evt_num_comp_pkts(_Data) ->
+ case _Data of
+ <<?evt_num_comp_pkts_bin(Num_hndl)>> ->
+ #evt_num_comp_pkts { num_hndl = Num_hndl }
+ end.
+
+decode_evt_mode_change(_Data) ->
+ case _Data of
+ <<?evt_mode_change_bin(Status,Handle,Mode,Interval)>> ->
+ #evt_mode_change { status = Status,handle = Handle,mode = Mode,interval = Interval }
+ end.
+
+decode_evt_return_link_keys(_Data) ->
+ case _Data of
+ <<?evt_return_link_keys_bin(Num_keys)>> ->
+ #evt_return_link_keys { num_keys = Num_keys }
+ end.
+
+decode_evt_pin_code_req(_Data) ->
+ case _Data of
+ <<?evt_pin_code_req_bin(Bdaddr)>> ->
+ #evt_pin_code_req { bdaddr = Bdaddr }
+ end.
+
+decode_evt_link_key_req(_Data) ->
+ case _Data of
+ <<?evt_link_key_req_bin(Bdaddr)>> ->
+ #evt_link_key_req { bdaddr = Bdaddr }
+ end.
+
+decode_evt_link_key_notify(_Data) ->
+ case _Data of
+ <<?evt_link_key_notify_bin(Bdaddr,Link_key,Key_type)>> ->
+ #evt_link_key_notify { bdaddr = Bdaddr,link_key = Link_key,key_type = Key_type }
+ end.
+
+decode_evt_data_buffer_overflow(_Data) ->
+ case _Data of
+ <<?evt_data_buffer_overflow_bin(Link_type)>> ->
+ #evt_data_buffer_overflow { link_type = Link_type }
+ end.
+
+decode_evt_max_slots_change(_Data) ->
+ case _Data of
+ <<?evt_max_slots_change_bin(Handle,Max_slots)>> ->
+ #evt_max_slots_change { handle = Handle,max_slots = Max_slots }
+ end.
+
+decode_evt_read_clock_offset_complete(_Data) ->
+ case _Data of
+ <<?evt_read_clock_offset_complete_bin(Status,Handle,Clock_offset)>> ->
+ #evt_read_clock_offset_complete { status = Status,handle = Handle,clock_offset = Clock_offset }
+ end.
+
+decode_evt_conn_ptype_changed(_Data) ->
+ case _Data of
+ <<?evt_conn_ptype_changed_bin(Status,Handle,Ptype)>> ->
+ #evt_conn_ptype_changed { status = Status,handle = Handle,ptype = Ptype }
+ end.
+
+decode_evt_qos_violation(_Data) ->
+ case _Data of
+ <<?evt_qos_violation_bin(Handle)>> ->
+ #evt_qos_violation { handle = Handle }
+ end.
+
+decode_evt_pscan_rep_mode_change(_Data) ->
+ case _Data of
+ <<?evt_pscan_rep_mode_change_bin(Bdaddr,Pscan_rep_mode)>> ->
+ #evt_pscan_rep_mode_change { bdaddr = Bdaddr,pscan_rep_mode = Pscan_rep_mode }
+ end.
+
+decode_evt_flow_spec_complete(_Data) ->
+ case _Data of
+ <<?evt_flow_spec_complete_bin(Status,Handle,Flags,Direction,Qos)>> ->
+ #evt_flow_spec_complete { status = Status,handle = Handle,flags = Flags,direction = Direction,qos = Qos }
+ end.
+
+decode_evt_read_remote_ext_features_complete(_Data) ->
+ case _Data of
+ <<?evt_read_remote_ext_features_complete_bin(Status,Handle,Page_num,Max_page_num,Features)>> ->
+ #evt_read_remote_ext_features_complete { status = Status,handle = Handle,page_num = Page_num,max_page_num = Max_page_num,features = Features }
+ end.
+
+decode_evt_sync_conn_complete(_Data) ->
+ case _Data of
+ <<?evt_sync_conn_complete_bin(Status,Handle,Bdaddr,Link_type,Trans_interval,Retrans_window,Rx_pkt_len,Tx_pkt_len,Air_mode)>> ->
+ #evt_sync_conn_complete { status = Status,handle = Handle,bdaddr = Bdaddr,link_type = Link_type,trans_interval = Trans_interval,retrans_window = Retrans_window,rx_pkt_len = Rx_pkt_len,tx_pkt_len = Tx_pkt_len,air_mode = Air_mode }
+ end.
+
+decode_evt_sync_conn_changed(_Data) ->
+ case _Data of
+ <<?evt_sync_conn_changed_bin(Status,Handle,Trans_interval,Retrans_window,Rx_pkt_len,Tx_pkt_len)>> ->
+ #evt_sync_conn_changed { status = Status,handle = Handle,trans_interval = Trans_interval,retrans_window = Retrans_window,rx_pkt_len = Rx_pkt_len,tx_pkt_len = Tx_pkt_len }
+ end.
+
+decode_evt_sniff_subrating(_Data) ->
+ case _Data of
+ <<?evt_sniff_subrating_bin(Status,Handle,Max_tx_latency,Max_rx_latency,Min_remote_timeout,Min_local_timeout)>> ->
+ #evt_sniff_subrating { status = Status,handle = Handle,max_tx_latency = Max_tx_latency,max_rx_latency = Max_rx_latency,min_remote_timeout = Min_remote_timeout,min_local_timeout = Min_local_timeout }
+ end.
+
+decode_evt_encryption_key_refresh_complete(_Data) ->
+ case _Data of
+ <<?evt_encryption_key_refresh_complete_bin(Status,Handle)>> ->
+ #evt_encryption_key_refresh_complete { status = Status,handle = Handle }
+ end.
+
+decode_evt_io_capability_request(_Data) ->
+ case _Data of
+ <<?evt_io_capability_request_bin(Bdaddr)>> ->
+ #evt_io_capability_request { bdaddr = Bdaddr }
+ end.
+
+decode_evt_io_capability_response(_Data) ->
+ case _Data of
+ <<?evt_io_capability_response_bin(Bdaddr,Capability,Oob_data,Authentication)>> ->
+ #evt_io_capability_response { bdaddr = Bdaddr,capability = Capability,oob_data = Oob_data,authentication = Authentication }
+ end.
+
+decode_evt_user_confirm_request(_Data) ->
+ case _Data of
+ <<?evt_user_confirm_request_bin(Bdaddr,Passkey)>> ->
+ #evt_user_confirm_request { bdaddr = Bdaddr,passkey = Passkey }
+ end.
+
+decode_evt_user_passkey_request(_Data) ->
+ case _Data of
+ <<?evt_user_passkey_request_bin(Bdaddr)>> ->
+ #evt_user_passkey_request { bdaddr = Bdaddr }
+ end.
+
+decode_evt_remote_oob_data_request(_Data) ->
+ case _Data of
+ <<?evt_remote_oob_data_request_bin(Bdaddr)>> ->
+ #evt_remote_oob_data_request { bdaddr = Bdaddr }
+ end.
+
+decode_evt_simple_pairing_complete(_Data) ->
+ case _Data of
+ <<?evt_simple_pairing_complete_bin(Status,Bdaddr)>> ->
+ #evt_simple_pairing_complete { status = Status,bdaddr = Bdaddr }
+ end.
+
+decode_evt_link_supervision_timeout_changed(_Data) ->
+ case _Data of
+ <<?evt_link_supervision_timeout_changed_bin(Handle,Timeout)>> ->
+ #evt_link_supervision_timeout_changed { handle = Handle,timeout = Timeout }
+ end.
+
+decode_evt_enhanced_flush_complete(_Data) ->
+ case _Data of
+ <<?evt_enhanced_flush_complete_bin(Handle)>> ->
+ #evt_enhanced_flush_complete { handle = Handle }
+ end.
+
+decode_evt_user_passkey_notify(_Data) ->
+ case _Data of
+ <<?evt_user_passkey_notify_bin(Bdaddr,Passkey,Entered)>> ->
+ #evt_user_passkey_notify { bdaddr = Bdaddr,passkey = Passkey,entered = Entered }
+ end.
+
+decode_evt_keypress_notify(_Data) ->
+ case _Data of
+ <<?evt_keypress_notify_bin(Bdaddr,Type)>> ->
+ #evt_keypress_notify { bdaddr = Bdaddr,type = Type }
+ end.
+
+decode_evt_remote_host_features_notify(_Data) ->
+ case _Data of
+ <<?evt_remote_host_features_notify_bin(Bdaddr,Features)>> ->
+ #evt_remote_host_features_notify { bdaddr = Bdaddr,features = Features }
+ end.
+
+decode_evt_le_meta_event(_Data) ->
+ case _Data of
+ <<?evt_le_meta_event_bin(Subevent,Data)>> ->
+ #evt_le_meta_event { subevent = Subevent,data = Data }
+ end.
+
+decode_evt_le_connection_complete(_Data) ->
+ case _Data of
+ <<?evt_le_connection_complete_bin(Status,Handle,Role,Peer_bdaddr_type,Peer_bdaddr,Interval,Latency,Supervision_timeout,Master_clock_accuracy)>> ->
+ #evt_le_connection_complete { status = Status,handle = Handle,role = Role,peer_bdaddr_type = Peer_bdaddr_type,peer_bdaddr = Peer_bdaddr,interval = Interval,latency = Latency,supervision_timeout = Supervision_timeout,master_clock_accuracy = Master_clock_accuracy }
+ end.
+
+decode_evt_le_connection_update_complete(_Data) ->
+ case _Data of
+ <<?evt_le_connection_update_complete_bin(Status,Handle,Interval,Latency,Supervision_timeout)>> ->
+ #evt_le_connection_update_complete { status = Status,handle = Handle,interval = Interval,latency = Latency,supervision_timeout = Supervision_timeout }
+ end.
+
+decode_evt_le_read_remote_used_features_complete(_Data) ->
+ case _Data of
+ <<?evt_le_read_remote_used_features_complete_bin(Status,Handle,Features)>> ->
+ #evt_le_read_remote_used_features_complete { status = Status,handle = Handle,features = Features }
+ end.
+
+decode_evt_le_long_term_key_request(_Data) ->
+ case _Data of
+ <<?evt_le_long_term_key_request_bin(Handle,Random,Diversifier)>> ->
+ #evt_le_long_term_key_request { handle = Handle,random = Random,diversifier = Diversifier }
+ end.
+
+decode_evt_physical_link_complete(_Data) ->
+ case _Data of
+ <<?evt_physical_link_complete_bin(Status,Handle)>> ->
+ #evt_physical_link_complete { status = Status,handle = Handle }
+ end.
+
+decode_evt_disconn_physical_link_complete(_Data) ->
+ case _Data of
+ <<?evt_disconn_physical_link_complete_bin(Status,Handle,Reason)>> ->
+ #evt_disconn_physical_link_complete { status = Status,handle = Handle,reason = Reason }
+ end.
+
+decode_evt_physical_link_loss_warning(_Data) ->
+ case _Data of
+ <<?evt_physical_link_loss_warning_bin(Handle,Reason)>> ->
+ #evt_physical_link_loss_warning { handle = Handle,reason = Reason }
+ end.
+
+decode_evt_physical_link_recovery(_Data) ->
+ case _Data of
+ <<?evt_physical_link_recovery_bin(Handle)>> ->
+ #evt_physical_link_recovery { handle = Handle }
+ end.
+
+decode_evt_logical_link_complete(_Data) ->
+ case _Data of
+ <<?evt_logical_link_complete_bin(Status,Log_handle,Handle,Tx_flow_id)>> ->
+ #evt_logical_link_complete { status = Status,log_handle = Log_handle,handle = Handle,tx_flow_id = Tx_flow_id }
+ end.
+
+decode_evt_flow_spec_modify_complete(_Data) ->
+ case _Data of
+ <<?evt_flow_spec_modify_complete_bin(Status,Handle)>> ->
+ #evt_flow_spec_modify_complete { status = Status,handle = Handle }
+ end.
+
+decode_evt_amp_status_change(_Data) ->
+ case _Data of
+ <<?evt_amp_status_change_bin(Status,Amp_status)>> ->
+ #evt_amp_status_change { status = Status,amp_status = Amp_status }
+ end.
+
+decode_evt_stack_internal(_Data) ->
+ case _Data of
+ <<?evt_stack_internal_bin(Type,Data)>> ->
+ #evt_stack_internal { type = Type,data = Data }
+ end.
+
+decode_evt_si_device(_Data) ->
+ case _Data of
+ <<?evt_si_device_bin(Event,Dev_id)>> ->
+ #evt_si_device { event = Event,dev_id = Dev_id }
+ end.
+
+decode(Evt,_Data) ->
+ case Evt of
+ ?EVT_STACK_INTERNAL -> decode_evt_stack_internal(_Data);
+ ?EVT_NUMBER_COMPLETED_BLOCKS -> decode_evt_amp_status_change(_Data);
+ ?EVT_AMP_STATUS_CHANGE -> decode_evt_amp_status_change(_Data);
+ ?EVT_DISCONNECT_LOGICAL_LINK_COMPLETE -> decode_evt_flow_spec_modify_complete(_Data);
+ ?EVT_FLOW_SPEC_MODIFY_COMPLETE -> decode_evt_flow_spec_modify_complete(_Data);
+ ?EVT_LOGICAL_LINK_COMPLETE -> decode_evt_logical_link_complete(_Data);
+ ?EVT_PHYSICAL_LINK_RECOVERY -> decode_evt_physical_link_recovery(_Data);
+ ?EVT_PHYSICAL_LINK_LOSS_EARLY_WARNING -> decode_evt_physical_link_loss_warning(_Data);
+ ?EVT_CHANNEL_SELECTED -> decode_evt_disconn_physical_link_complete(_Data);
+ ?EVT_DISCONNECT_PHYSICAL_LINK_COMPLETE -> decode_evt_disconn_physical_link_complete(_Data);
+ ?EVT_PHYSICAL_LINK_COMPLETE -> decode_evt_physical_link_complete(_Data);
+ ?EVT_LE_META_EVENT -> decode_evt_le_meta_event(_Data);
+ ?EVT_REMOTE_HOST_FEATURES_NOTIFY -> decode_evt_remote_host_features_notify(_Data);
+ ?EVT_KEYPRESS_NOTIFY -> decode_evt_keypress_notify(_Data);
+ ?EVT_USER_PASSKEY_NOTIFY -> decode_evt_user_passkey_notify(_Data);
+ ?EVT_ENHANCED_FLUSH_COMPLETE -> decode_evt_enhanced_flush_complete(_Data);
+ ?EVT_LINK_SUPERVISION_TIMEOUT_CHANGED -> decode_evt_link_supervision_timeout_changed(_Data);
+ ?EVT_SIMPLE_PAIRING_COMPLETE -> decode_evt_simple_pairing_complete(_Data);
+ ?EVT_REMOTE_OOB_DATA_REQUEST -> decode_evt_remote_oob_data_request(_Data);
+ ?EVT_USER_PASSKEY_REQUEST -> decode_evt_user_passkey_request(_Data);
+ ?EVT_USER_CONFIRM_REQUEST -> decode_evt_user_confirm_request(_Data);
+ ?EVT_IO_CAPABILITY_RESPONSE -> decode_evt_io_capability_response(_Data);
+ ?EVT_IO_CAPABILITY_REQUEST -> decode_evt_io_capability_request(_Data);
+ ?EVT_EXTENDED_INQUIRY_RESULT -> decode_evt_encryption_key_refresh_complete(_Data);
+ ?EVT_ENCRYPTION_KEY_REFRESH_COMPLETE -> decode_evt_encryption_key_refresh_complete(_Data);
+ ?EVT_SNIFF_SUBRATING -> decode_evt_sniff_subrating(_Data);
+ ?EVT_SYNC_CONN_CHANGED -> decode_evt_sync_conn_changed(_Data);
+ ?EVT_SYNC_CONN_COMPLETE -> decode_evt_sync_conn_complete(_Data);
+ ?EVT_INQUIRY_RESULT_WITH_RSSI -> decode_evt_read_remote_ext_features_complete(_Data);
+ ?EVT_READ_REMOTE_EXT_FEATURES_COMPLETE -> decode_evt_read_remote_ext_features_complete(_Data);
+ ?EVT_FLOW_SPEC_COMPLETE -> decode_evt_flow_spec_complete(_Data);
+ ?EVT_PSCAN_REP_MODE_CHANGE -> decode_evt_pscan_rep_mode_change(_Data);
+ ?EVT_QOS_VIOLATION -> decode_evt_qos_violation(_Data);
+ ?EVT_CONN_PTYPE_CHANGED -> decode_evt_conn_ptype_changed(_Data);
+ ?EVT_READ_CLOCK_OFFSET_COMPLETE -> decode_evt_read_clock_offset_complete(_Data);
+ ?EVT_MAX_SLOTS_CHANGE -> decode_evt_max_slots_change(_Data);
+ ?EVT_LOOPBACK_COMMAND -> decode_evt_data_buffer_overflow(_Data);
+ ?EVT_DATA_BUFFER_OVERFLOW -> decode_evt_data_buffer_overflow(_Data);
+ ?EVT_LINK_KEY_NOTIFY -> decode_evt_link_key_notify(_Data);
+ ?EVT_LINK_KEY_REQ -> decode_evt_link_key_req(_Data);
+ ?EVT_PIN_CODE_REQ -> decode_evt_pin_code_req(_Data);
+ ?EVT_RETURN_LINK_KEYS -> decode_evt_return_link_keys(_Data);
+ ?EVT_MODE_CHANGE -> decode_evt_mode_change(_Data);
+ ?EVT_NUM_COMP_PKTS -> decode_evt_num_comp_pkts(_Data);
+ ?EVT_ROLE_CHANGE -> decode_evt_role_change(_Data);
+ ?EVT_FLUSH_OCCURRED -> decode_evt_flush_occured(_Data);
+ ?EVT_HARDWARE_ERROR -> decode_evt_hardware_error(_Data);
+ ?EVT_CMD_STATUS -> decode_evt_cmd_status(_Data);
+ ?EVT_CMD_COMPLETE -> decode_evt_cmd_complete(_Data);
+ ?EVT_QOS_SETUP_COMPLETE -> decode_evt_qos_setup_complete(_Data);
+ ?EVT_READ_REMOTE_VERSION_COMPLETE -> decode_evt_read_remote_version_complete(_Data);
+ ?EVT_READ_REMOTE_FEATURES_COMPLETE -> decode_evt_read_remote_features_complete(_Data);
+ ?EVT_MASTER_LINK_KEY_COMPLETE -> decode_evt_master_link_key_complete(_Data);
+ ?EVT_CHANGE_CONN_LINK_KEY_COMPLETE -> decode_evt_change_conn_link_key_complete(_Data);
+ ?EVT_ENCRYPT_CHANGE -> decode_evt_encrypt_change(_Data);
+ ?EVT_REMOTE_NAME_REQ_COMPLETE -> decode_evt_remote_name_req_complete(_Data);
+ ?EVT_AUTH_COMPLETE -> decode_evt_auth_complete(_Data);
+ ?EVT_DISCONN_COMPLETE -> decode_evt_disconn_complete(_Data);
+ ?EVT_CONN_REQUEST -> decode_evt_conn_request(_Data);
+ ?EVT_INQUIRY_COMPLETE -> decode_inquiry_info(_Data);
+ ?EVT_INQUIRY_RESULT -> decode_inquiry_info(_Data);
+ ?EVT_CONN_COMPLETE -> decode_evt_conn_complete(_Data);
+ _ -> erlang:error(bad_event)
+ end.
+
+decode_le(Evt,_Data) ->
+ case Evt of
+ ?EVT_LE_LTK_REQUEST -> decode_evt_le_long_term_key_request(_Data);
+ ?EVT_LE_READ_REMOTE_USED_FEATURES_COMPLETE -> decode_evt_le_read_remote_used_features_complete(_Data);
+ ?EVT_LE_ADVERTISING_REPORT -> decode_evt_le_connection_update_complete(_Data);
+ ?EVT_LE_CONN_UPDATE_COMPLETE -> decode_evt_le_connection_update_complete(_Data);
+ ?EVT_LE_CONN_COMPLETE -> decode_evt_le_connection_complete(_Data);
+ _ -> erlang:error(bad_event)
+ end.
+
+le_test_end(Socket) ->
+ hci_socket:call(Socket,?OGF_LE_CTL,?OCF_LE_TEST_END,<<>>,fun decode_le_test_end_rp/1).
+
+le_transmitter_test(Socket,Frequency,Length,Payload) ->
+ hci_socket:call(Socket,?OGF_LE_CTL,?OCF_LE_TRANSMITTER_TEST,<<?le_transmitter_test_cp_bin(Frequency,Length,Payload)>>,undefined).
+
+le_receiver_test(Socket,Frequency) ->
+ hci_socket:call(Socket,?OGF_LE_CTL,?OCF_LE_RECEIVER_TEST,<<?le_receiver_test_cp_bin(Frequency)>>,undefined).
+
+le_read_supported_states(Socket) ->
+ hci_socket:call(Socket,?OGF_LE_CTL,?OCF_LE_READ_SUPPORTED_STATES,<<>>,fun decode_le_read_supported_states_rp/1).
+
+le_ltk_neg_reply(Socket,Handle) ->
+ hci_socket:call(Socket,?OGF_LE_CTL,?OCF_LE_LTK_NEG_REPLY,<<?le_ltk_neg_reply_cp_bin(Handle)>>,fun decode_le_ltk_neg_reply_rp/1).
+
+le_ltk_reply(Socket,Handle,Key) ->
+ hci_socket:call(Socket,?OGF_LE_CTL,?OCF_LE_LTK_REPLY,<<?le_ltk_reply_cp_bin(Handle,Key)>>,fun decode_le_ltk_reply_rp/1).
+
+le_start_encryption(Socket,Handle,Random,Diversifier,Key) ->
+ hci_socket:call(Socket,?OGF_LE_CTL,?OCF_LE_START_ENCRYPTION,<<?le_start_encryption_cp_bin(Handle,Random,Diversifier,Key)>>,undefined).
+
+le_rand(Socket) ->
+ hci_socket:call(Socket,?OGF_LE_CTL,?OCF_LE_RAND,<<>>,fun decode_le_rand_rp/1).
+
+le_encrypt(Socket,Key,Plaintext) ->
+ hci_socket:call(Socket,?OGF_LE_CTL,?OCF_LE_ENCRYPT,<<?le_encrypt_cp_bin(Key,Plaintext)>>,fun decode_le_encrypt_rp/1).
+
+le_read_remote_used_features(Socket,Handle) ->
+ hci_socket:call(Socket,?OGF_LE_CTL,?OCF_LE_READ_REMOTE_USED_FEATURES,<<?le_read_remote_used_features_cp_bin(Handle)>>,undefined).
+
+le_read_channel_map(Socket,Handle) ->
+ hci_socket:call(Socket,?OGF_LE_CTL,?OCF_LE_READ_CHANNEL_MAP,<<?le_read_channel_map_cp_bin(Handle)>>,fun decode_le_read_channel_map_rp/1).
+
+le_set_host_channel_classification(Socket,Map) ->
+ hci_socket:call(Socket,?OGF_LE_CTL,?OCF_LE_SET_HOST_CHANNEL_CLASSIFICATION,<<?le_set_host_channel_classification_cp_bin(Map)>>,undefined).
+
+le_conn_update(Socket) ->
+ hci_socket:call(Socket,?OGF_LE_CTL,?OCF_LE_CONN_UPDATE,<<>>,undefined).
+
+le_remove_device_from_white_list(Socket,Bdaddr_type,Bdaddr) ->
+ hci_socket:call(Socket,?OGF_LE_CTL,?OCF_LE_REMOVE_DEVICE_FROM_WHITE_LIST,<<?le_remove_device_from_white_list_cp_bin(Bdaddr_type,Bdaddr)>>,undefined).
+
+le_add_device_to_white_list(Socket,Bdaddr_type,Bdaddr) ->
+ hci_socket:call(Socket,?OGF_LE_CTL,?OCF_LE_ADD_DEVICE_TO_WHITE_LIST,<<?le_add_device_to_white_list_cp_bin(Bdaddr_type,Bdaddr)>>,undefined).
+
+le_clear_white_list(Socket) ->
+ hci_socket:call(Socket,?OGF_LE_CTL,?OCF_LE_CLEAR_WHITE_LIST,<<>>,undefined).
+
+le_read_white_list_size(Socket) ->
+ hci_socket:call(Socket,?OGF_LE_CTL,?OCF_LE_READ_WHITE_LIST_SIZE,<<>>,fun decode_le_read_white_list_size_rp/1).
+
+le_create_conn_cancel(Socket) ->
+ hci_socket:call(Socket,?OGF_LE_CTL,?OCF_LE_CREATE_CONN_CANCEL,<<>>,undefined).
+
+le_create_conn(Socket) ->
+ hci_socket:call(Socket,?OGF_LE_CTL,?OCF_LE_CREATE_CONN,<<>>,undefined).
+
+le_set_scan_enable(Socket,Enable,Filter_dup) ->
+ hci_socket:call(Socket,?OGF_LE_CTL,?OCF_LE_SET_SCAN_ENABLE,<<?le_set_scan_enable_cp_bin(Enable,Filter_dup)>>,undefined).
+
+le_set_scan_parameters(Socket,Type,Interval,Window,Own_bdaddr_type,Filter) ->
+ hci_socket:call(Socket,?OGF_LE_CTL,?OCF_LE_SET_SCAN_PARAMETERS,<<?le_set_scan_parameters_cp_bin(Type,Interval,Window,Own_bdaddr_type,Filter)>>,undefined).
+
+le_set_advertise_enable(Socket,Enable) ->
+ hci_socket:call(Socket,?OGF_LE_CTL,?OCF_LE_SET_ADVERTISE_ENABLE,<<?le_set_advertise_enable_cp_bin(Enable)>>,undefined).
+
+le_set_scan_response_data(Socket,Length,Data) ->
+ hci_socket:call(Socket,?OGF_LE_CTL,?OCF_LE_SET_SCAN_RESPONSE_DATA,<<?le_set_scan_response_data_cp_bin(Length,Data)>>,undefined).
+
+le_set_advertising_data(Socket,Length,Data) ->
+ hci_socket:call(Socket,?OGF_LE_CTL,?OCF_LE_SET_ADVERTISING_DATA,<<?le_set_advertising_data_cp_bin(Length,Data)>>,undefined).
+
+le_read_advertising_channel_tx_power(Socket) ->
+ hci_socket:call(Socket,?OGF_LE_CTL,?OCF_LE_READ_ADVERTISING_CHANNEL_TX_POWER,<<>>,fun decode_le_read_advertising_channel_tx_power_rp/1).
+
+le_set_advertising_parameters(Socket,Min_interval,Max_interval,Advtype,Own_bdaddr_type,Direct_bdaddr_type,Direct_bdaddr,Chan_map,Filter) ->
+ hci_socket:call(Socket,?OGF_LE_CTL,?OCF_LE_SET_ADVERTISING_PARAMETERS,<<?le_set_advertising_parameters_cp_bin(Min_interval,Max_interval,Advtype,Own_bdaddr_type,Direct_bdaddr_type,Direct_bdaddr,Chan_map,Filter)>>,undefined).
+
+le_set_random_address(Socket,Bdaddr) ->
+ hci_socket:call(Socket,?OGF_LE_CTL,?OCF_LE_SET_RANDOM_ADDRESS,<<?le_set_random_address_cp_bin(Bdaddr)>>,undefined).
+
+le_read_local_supported_features(Socket) ->
+ hci_socket:call(Socket,?OGF_LE_CTL,?OCF_LE_READ_LOCAL_SUPPORTED_FEATURES,<<>>,fun decode_le_read_local_supported_features_rp/1).
+
+le_read_buffer_size(Socket) ->
+ hci_socket:call(Socket,?OGF_LE_CTL,?OCF_LE_READ_BUFFER_SIZE,<<>>,fun decode_le_read_buffer_size_rp/1).
+
+le_set_event_mask(Socket,Mask) ->
+ hci_socket:call(Socket,?OGF_LE_CTL,?OCF_LE_SET_EVENT_MASK,<<?le_set_event_mask_cp_bin(Mask)>>,undefined).
+
+write_simple_pairing_debug_mode(Socket,Mode) ->
+ hci_socket:call(Socket,?OGF_TESTING_CMD,?OCF_WRITE_SIMPLE_PAIRING_DEBUG_MODE,<<?write_simple_pairing_debug_mode_cp_bin(Mode)>>,fun decode_write_simple_pairing_debug_mode_rp/1).
+
+enable_device_under_test_mode(Socket) ->
+ hci_socket:call(Socket,?OGF_TESTING_CMD,?OCF_ENABLE_DEVICE_UNDER_TEST_MODE,<<>>,undefined).
+
+write_loopback_mode(Socket) ->
+ hci_socket:call(Socket,?OGF_TESTING_CMD,?OCF_WRITE_LOOPBACK_MODE,<<>>,undefined).
+
+read_loopback_mode(Socket) ->
+ hci_socket:call(Socket,?OGF_TESTING_CMD,?OCF_READ_LOOPBACK_MODE,<<>>,undefined).
+
+write_remote_amp_assoc(Socket,Handle,Length_so_far,Assoc_length,Fragment) ->
+ hci_socket:call(Socket,?OGF_STATUS_PARAM,?OCF_WRITE_REMOTE_AMP_ASSOC,<<?write_remote_amp_assoc_cp_bin(Handle,Length_so_far,Assoc_length,Fragment)>>,fun decode_write_remote_amp_assoc_rp/1).
+
+read_local_amp_assoc(Socket,Handle,Len_so_far,Max_len) ->
+ hci_socket:call(Socket,?OGF_STATUS_PARAM,?OCF_READ_LOCAL_AMP_ASSOC,<<?read_local_amp_assoc_cp_bin(Handle,Len_so_far,Max_len)>>,fun decode_read_local_amp_assoc_rp/1).
+
+read_local_amp_info(Socket) ->
+ hci_socket:call(Socket,?OGF_STATUS_PARAM,?OCF_READ_LOCAL_AMP_INFO,<<>>,fun decode_read_local_amp_info_rp/1).
+
+read_clock(Socket,Handle,Which_clock) ->
+ hci_socket:call(Socket,?OGF_STATUS_PARAM,?OCF_READ_CLOCK,<<?read_clock_cp_bin(Handle,Which_clock)>>,fun decode_read_clock_rp/1).
+
+read_afh_map(Socket) ->
+ hci_socket:call(Socket,?OGF_STATUS_PARAM,?OCF_READ_AFH_MAP,<<>>,fun decode_read_afh_map_rp/1).
+
+read_rssi(Socket) ->
+ hci_socket:call(Socket,?OGF_STATUS_PARAM,?OCF_READ_RSSI,<<>>,fun decode_read_rssi_rp/1).
+
+read_link_quality(Socket) ->
+ hci_socket:call(Socket,?OGF_STATUS_PARAM,?OCF_READ_LINK_QUALITY,<<>>,fun decode_read_link_quality_rp/1).
+
+reset_failed_contact_counter(Socket) ->
+ hci_socket:call(Socket,?OGF_STATUS_PARAM,?OCF_RESET_FAILED_CONTACT_COUNTER,<<>>,fun decode_reset_failed_contact_counter_rp/1).
+
+read_failed_contact_counter(Socket) ->
+ hci_socket:call(Socket,?OGF_STATUS_PARAM,?OCF_READ_FAILED_CONTACT_COUNTER,<<>>,fun decode_read_failed_contact_counter_rp/1).
+
+read_bd_addr(Socket) ->
+ hci_socket:call(Socket,?OGF_INFO_PARAM,?OCF_READ_BD_ADDR,<<>>,fun decode_read_bd_addr_rp/1).
+
+read_country_code(Socket) ->
+ hci_socket:call(Socket,?OGF_INFO_PARAM,?OCF_READ_COUNTRY_CODE,<<>>,undefined).
+
+read_buffer_size(Socket) ->
+ hci_socket:call(Socket,?OGF_INFO_PARAM,?OCF_READ_BUFFER_SIZE,<<>>,fun decode_read_buffer_size_rp/1).
+
+read_local_ext_features(Socket,Page_num) ->
+ hci_socket:call(Socket,?OGF_INFO_PARAM,?OCF_READ_LOCAL_EXT_FEATURES,<<?read_local_ext_features_cp_bin(Page_num)>>,fun decode_read_local_ext_features_rp/1).
+
+read_local_features(Socket) ->
+ hci_socket:call(Socket,?OGF_INFO_PARAM,?OCF_READ_LOCAL_FEATURES,<<>>,fun decode_read_local_features_rp/1).
+
+read_local_commands(Socket) ->
+ hci_socket:call(Socket,?OGF_INFO_PARAM,?OCF_READ_LOCAL_COMMANDS,<<>>,fun decode_read_local_commands_rp/1).
+
+read_local_version(Socket) ->
+ hci_socket:call(Socket,?OGF_INFO_PARAM,?OCF_READ_LOCAL_VERSION,<<>>,fun decode_read_local_version_rp/1).
+
+write_le_host_supported(Socket,Le,Simul) ->
+ hci_socket:call(Socket,?OGF_HOST_CTL,?OCF_WRITE_LE_HOST_SUPPORTED,<<?write_le_host_supported_cp_bin(Le,Simul)>>,undefined).
+
+read_le_host_supported(Socket) ->
+ hci_socket:call(Socket,?OGF_HOST_CTL,?OCF_READ_LE_HOST_SUPPORTED,<<>>,fun decode_read_le_host_supported_rp/1).
+
+write_best_effort_flush_timeout(Socket,Handle,Timeout) ->
+ hci_socket:call(Socket,?OGF_HOST_CTL,?OCF_WRITE_BEST_EFFORT_FLUSH_TIMEOUT,<<?write_best_effort_flush_timeout_cp_bin(Handle,Timeout)>>,fun decode_write_best_effort_flush_timeout_rp/1).
+
+read_best_effort_flush_timeout(Socket) ->
+ hci_socket:call(Socket,?OGF_HOST_CTL,?OCF_READ_BEST_EFFORT_FLUSH_TIMEOUT,<<>>,fun decode_read_best_effort_flush_timeout_rp/1).
+
+read_enhanced_transmit_power_level(Socket) ->
+ hci_socket:call(Socket,?OGF_HOST_CTL,?OCF_READ_ENHANCED_TRANSMIT_POWER_LEVEL,<<>>,fun decode_read_enhanced_transmit_power_level_rp/1).
+
+write_flow_control_mode(Socket) ->
+ hci_socket:call(Socket,?OGF_HOST_CTL,?OCF_WRITE_FLOW_CONTROL_MODE,<<>>,undefined).
+
+read_flow_control_mode(Socket) ->
+ hci_socket:call(Socket,?OGF_HOST_CTL,?OCF_READ_FLOW_CONTROL_MODE,<<>>,undefined).
+
+write_location_data(Socket) ->
+ hci_socket:call(Socket,?OGF_HOST_CTL,?OCF_WRITE_LOCATION_DATA,<<>>,undefined).
+
+read_location_data(Socket) ->
+ hci_socket:call(Socket,?OGF_HOST_CTL,?OCF_READ_LOCATION_DATA,<<>>,undefined).
+
+set_event_mask_page_2(Socket) ->
+ hci_socket:call(Socket,?OGF_HOST_CTL,?OCF_SET_EVENT_MASK_PAGE_2,<<>>,undefined).
+
+write_logical_link_accept_timeout(Socket) ->
+ hci_socket:call(Socket,?OGF_HOST_CTL,?OCF_WRITE_LOGICAL_LINK_ACCEPT_TIMEOUT,<<>>,undefined).
+
+read_logical_link_accept_timeout(Socket) ->
+ hci_socket:call(Socket,?OGF_HOST_CTL,?OCF_READ_LOGICAL_LINK_ACCEPT_TIMEOUT,<<>>,undefined).
+
+send_keypress_notify(Socket,Bdaddr,Type) ->
+ hci_socket:call(Socket,?OGF_HOST_CTL,?OCF_SEND_KEYPRESS_NOTIFY,<<?send_keypress_notify_cp_bin(Bdaddr,Type)>>,fun decode_send_keypress_notify_rp/1).
+
+enhanced_flush(Socket,Handle,Type) ->
+ hci_socket:call(Socket,?OGF_HOST_CTL,?OCF_ENHANCED_FLUSH,<<?enhanced_flush_cp_bin(Handle,Type)>>,undefined).
+
+write_default_error_data_reporting(Socket,Reporting) ->
+ hci_socket:call(Socket,?OGF_HOST_CTL,?OCF_WRITE_DEFAULT_ERROR_DATA_REPORTING,<<?write_default_error_data_reporting_cp_bin(Reporting)>>,fun decode_write_default_error_data_reporting_rp/1).
+
+read_default_error_data_reporting(Socket) ->
+ hci_socket:call(Socket,?OGF_HOST_CTL,?OCF_READ_DEFAULT_ERROR_DATA_REPORTING,<<>>,fun decode_read_default_error_data_reporting_rp/1).
+
+write_inquiry_transmit_power_level(Socket,Level) ->
+ hci_socket:call(Socket,?OGF_HOST_CTL,?OCF_WRITE_INQUIRY_TRANSMIT_POWER_LEVEL,<<?write_inquiry_transmit_power_level_cp_bin(Level)>>,fun decode_write_inquiry_transmit_power_level_rp/1).
+
+read_inquiry_transmit_power_level(Socket) ->
+ hci_socket:call(Socket,?OGF_HOST_CTL,?OCF_READ_INQUIRY_TRANSMIT_POWER_LEVEL,<<>>,fun decode_read_inquiry_transmit_power_level_rp/1).
+
+read_inq_response_tx_power_level(Socket) ->
+ hci_socket:call(Socket,?OGF_HOST_CTL,?OCF_READ_INQ_RESPONSE_TX_POWER_LEVEL,<<>>,fun decode_read_inq_response_tx_power_level_rp/1).
+
+read_local_oob_data(Socket) ->
+ hci_socket:call(Socket,?OGF_HOST_CTL,?OCF_READ_LOCAL_OOB_DATA,<<>>,fun decode_read_local_oob_data_rp/1).
+
+write_simple_pairing_mode(Socket,Mode) ->
+ hci_socket:call(Socket,?OGF_HOST_CTL,?OCF_WRITE_SIMPLE_PAIRING_MODE,<<?write_simple_pairing_mode_cp_bin(Mode)>>,fun decode_write_simple_pairing_mode_rp/1).
+
+read_simple_pairing_mode(Socket) ->
+ hci_socket:call(Socket,?OGF_HOST_CTL,?OCF_READ_SIMPLE_PAIRING_MODE,<<>>,fun decode_read_simple_pairing_mode_rp/1).
+
+refresh_encryption_key(Socket,Handle) ->
+ hci_socket:call(Socket,?OGF_HOST_CTL,?OCF_REFRESH_ENCRYPTION_KEY,<<?refresh_encryption_key_cp_bin(Handle)>>,fun decode_refresh_encryption_key_rp/1).
+
+write_ext_inquiry_response(Socket,Fec,Data) ->
+ hci_socket:call(Socket,?OGF_HOST_CTL,?OCF_WRITE_EXT_INQUIRY_RESPONSE,<<?write_ext_inquiry_response_cp_bin(Fec,Data)>>,fun decode_write_ext_inquiry_response_rp/1).
+
+read_ext_inquiry_response(Socket) ->
+ hci_socket:call(Socket,?OGF_HOST_CTL,?OCF_READ_EXT_INQUIRY_RESPONSE,<<>>,fun decode_read_ext_inquiry_response_rp/1).
+
+write_afh_mode(Socket,Mode) ->
+ hci_socket:call(Socket,?OGF_HOST_CTL,?OCF_WRITE_AFH_MODE,<<?write_afh_mode_cp_bin(Mode)>>,fun decode_write_afh_mode_rp/1).
+
+read_afh_mode(Socket) ->
+ hci_socket:call(Socket,?OGF_HOST_CTL,?OCF_READ_AFH_MODE,<<>>,fun decode_read_afh_mode_rp/1).
+
+write_page_scan_type(Socket) ->
+ hci_socket:call(Socket,?OGF_HOST_CTL,?OCF_WRITE_PAGE_SCAN_TYPE,<<>>,undefined).
+
+read_page_scan_type(Socket) ->
+ hci_socket:call(Socket,?OGF_HOST_CTL,?OCF_READ_PAGE_SCAN_TYPE,<<>>,undefined).
+
+write_inquiry_mode(Socket,Mode) ->
+ hci_socket:call(Socket,?OGF_HOST_CTL,?OCF_WRITE_INQUIRY_MODE,<<?write_inquiry_mode_cp_bin(Mode)>>,fun decode_write_inquiry_mode_rp/1).
+
+read_inquiry_mode(Socket) ->
+ hci_socket:call(Socket,?OGF_HOST_CTL,?OCF_READ_INQUIRY_MODE,<<>>,fun decode_read_inquiry_mode_rp/1).
+
+write_inquiry_scan_type(Socket,Type) ->
+ hci_socket:call(Socket,?OGF_HOST_CTL,?OCF_WRITE_INQUIRY_SCAN_TYPE,<<?write_inquiry_scan_type_cp_bin(Type)>>,fun decode_write_inquiry_scan_type_rp/1).
+
+read_inquiry_scan_type(Socket) ->
+ hci_socket:call(Socket,?OGF_HOST_CTL,?OCF_READ_INQUIRY_SCAN_TYPE,<<>>,fun decode_read_inquiry_scan_type_rp/1).
+
+set_afh_classification(Socket,Map) ->
+ hci_socket:call(Socket,?OGF_HOST_CTL,?OCF_SET_AFH_CLASSIFICATION,<<?set_afh_classification_cp_bin(Map)>>,fun decode_set_afh_classification_rp/1).
+
+write_page_scan_mode(Socket) ->
+ hci_socket:call(Socket,?OGF_HOST_CTL,?OCF_WRITE_PAGE_SCAN_MODE,<<>>,undefined).
+
+read_page_scan_mode(Socket) ->
+ hci_socket:call(Socket,?OGF_HOST_CTL,?OCF_READ_PAGE_SCAN_MODE,<<>>,undefined).
+
+write_page_scan_period_mode(Socket) ->
+ hci_socket:call(Socket,?OGF_HOST_CTL,?OCF_WRITE_PAGE_SCAN_PERIOD_MODE,<<>>,undefined).
+
+read_page_scan_period_mode(Socket) ->
+ hci_socket:call(Socket,?OGF_HOST_CTL,?OCF_READ_PAGE_SCAN_PERIOD_MODE,<<>>,undefined).
+
+write_current_iac_lap(Socket,Num_current_iac,Lap) ->
+ hci_socket:call(Socket,?OGF_HOST_CTL,?OCF_WRITE_CURRENT_IAC_LAP,<<?write_current_iac_lap_cp_bin(Num_current_iac,Lap)>>,undefined).
+
+read_current_iac_lap(Socket) ->
+ hci_socket:call(Socket,?OGF_HOST_CTL,?OCF_READ_CURRENT_IAC_LAP,<<>>,fun decode_read_current_iac_lap_rp/1).
+
+read_num_supported_iac(Socket) ->
+ hci_socket:call(Socket,?OGF_HOST_CTL,?OCF_READ_NUM_SUPPORTED_IAC,<<>>,undefined).
+
+write_link_supervision_timeout(Socket,Handle,Timeout) ->
+ hci_socket:call(Socket,?OGF_HOST_CTL,?OCF_WRITE_LINK_SUPERVISION_TIMEOUT,<<?write_link_supervision_timeout_cp_bin(Handle,Timeout)>>,fun decode_write_link_supervision_timeout_rp/1).
+
+read_link_supervision_timeout(Socket) ->
+ hci_socket:call(Socket,?OGF_HOST_CTL,?OCF_READ_LINK_SUPERVISION_TIMEOUT,<<>>,fun decode_read_link_supervision_timeout_rp/1).
+
+host_num_comp_pkts(Socket,Num_hndl) ->
+ hci_socket:call(Socket,?OGF_HOST_CTL,?OCF_HOST_NUM_COMP_PKTS,<<?host_num_comp_pkts_cp_bin(Num_hndl)>>,undefined).
+
+host_buffer_size(Socket,Acl_mtu,Sco_mtu,Acl_max_pkt,Sco_max_pkt) ->
+ hci_socket:call(Socket,?OGF_HOST_CTL,?OCF_HOST_BUFFER_SIZE,<<?host_buffer_size_cp_bin(Acl_mtu,Sco_mtu,Acl_max_pkt,Sco_max_pkt)>>,undefined).
+
+set_controller_to_host_fc(Socket) ->
+ hci_socket:call(Socket,?OGF_HOST_CTL,?OCF_SET_CONTROLLER_TO_HOST_FC,<<>>,undefined).
+
+write_sync_flow_enable(Socket) ->
+ hci_socket:call(Socket,?OGF_HOST_CTL,?OCF_WRITE_SYNC_FLOW_ENABLE,<<>>,undefined).
+
+read_sync_flow_enable(Socket) ->
+ hci_socket:call(Socket,?OGF_HOST_CTL,?OCF_READ_SYNC_FLOW_ENABLE,<<>>,undefined).
+
+read_transmit_power_level(Socket,Handle,Type) ->
+ hci_socket:call(Socket,?OGF_HOST_CTL,?OCF_READ_TRANSMIT_POWER_LEVEL,<<?read_transmit_power_level_cp_bin(Handle,Type)>>,fun decode_read_transmit_power_level_rp/1).
+
+write_hold_mode_activity(Socket) ->
+ hci_socket:call(Socket,?OGF_HOST_CTL,?OCF_WRITE_HOLD_MODE_ACTIVITY,<<>>,undefined).
+
+read_hold_mode_activity(Socket) ->
+ hci_socket:call(Socket,?OGF_HOST_CTL,?OCF_READ_HOLD_MODE_ACTIVITY,<<>>,undefined).
+
+write_num_broadcast_retrans(Socket) ->
+ hci_socket:call(Socket,?OGF_HOST_CTL,?OCF_WRITE_NUM_BROADCAST_RETRANS,<<>>,undefined).
+
+read_num_broadcast_retrans(Socket) ->
+ hci_socket:call(Socket,?OGF_HOST_CTL,?OCF_READ_NUM_BROADCAST_RETRANS,<<>>,undefined).
+
+write_automatic_flush_timeout(Socket) ->
+ hci_socket:call(Socket,?OGF_HOST_CTL,?OCF_WRITE_AUTOMATIC_FLUSH_TIMEOUT,<<>>,undefined).
+
+read_automatic_flush_timeout(Socket) ->
+ hci_socket:call(Socket,?OGF_HOST_CTL,?OCF_READ_AUTOMATIC_FLUSH_TIMEOUT,<<>>,undefined).
+
+write_voice_setting(Socket,Voice_setting) ->
+ hci_socket:call(Socket,?OGF_HOST_CTL,?OCF_WRITE_VOICE_SETTING,<<?write_voice_setting_cp_bin(Voice_setting)>>,undefined).
+
+read_voice_setting(Socket) ->
+ hci_socket:call(Socket,?OGF_HOST_CTL,?OCF_READ_VOICE_SETTING,<<>>,fun decode_read_voice_setting_rp/1).
+
+write_class_of_dev(Socket,Dev_class) ->
+ hci_socket:call(Socket,?OGF_HOST_CTL,?OCF_WRITE_CLASS_OF_DEV,<<?write_class_of_dev_cp_bin(Dev_class)>>,undefined).
+
+read_class_of_dev(Socket) ->
+ hci_socket:call(Socket,?OGF_HOST_CTL,?OCF_READ_CLASS_OF_DEV,<<>>,fun decode_read_class_of_dev_rp/1).
+
+write_encrypt_mode(Socket) ->
+ hci_socket:call(Socket,?OGF_HOST_CTL,?OCF_WRITE_ENCRYPT_MODE,<<>>,undefined).
+
+read_encrypt_mode(Socket) ->
+ hci_socket:call(Socket,?OGF_HOST_CTL,?OCF_READ_ENCRYPT_MODE,<<>>,undefined).
+
+write_auth_enable(Socket) ->
+ hci_socket:call(Socket,?OGF_HOST_CTL,?OCF_WRITE_AUTH_ENABLE,<<>>,undefined).
+
+read_auth_enable(Socket) ->
+ hci_socket:call(Socket,?OGF_HOST_CTL,?OCF_READ_AUTH_ENABLE,<<>>,undefined).
+
+write_inq_activity(Socket,Interval,Window) ->
+ hci_socket:call(Socket,?OGF_HOST_CTL,?OCF_WRITE_INQ_ACTIVITY,<<?write_inq_activity_cp_bin(Interval,Window)>>,undefined).
+
+read_inq_activity(Socket) ->
+ hci_socket:call(Socket,?OGF_HOST_CTL,?OCF_READ_INQ_ACTIVITY,<<>>,fun decode_read_inq_activity_rp/1).
+
+write_page_activity(Socket,Interval,Window) ->
+ hci_socket:call(Socket,?OGF_HOST_CTL,?OCF_WRITE_PAGE_ACTIVITY,<<?write_page_activity_cp_bin(Interval,Window)>>,undefined).
+
+read_page_activity(Socket) ->
+ hci_socket:call(Socket,?OGF_HOST_CTL,?OCF_READ_PAGE_ACTIVITY,<<>>,fun decode_read_page_activity_rp/1).
+
+write_scan_enable(Socket) ->
+ hci_socket:call(Socket,?OGF_HOST_CTL,?OCF_WRITE_SCAN_ENABLE,<<>>,undefined).
+
+read_scan_enable(Socket) ->
+ hci_socket:call(Socket,?OGF_HOST_CTL,?OCF_READ_SCAN_ENABLE,<<>>,fun decode_read_scan_enable_rp/1).
+
+write_page_timeout(Socket,Timeout) ->
+ hci_socket:call(Socket,?OGF_HOST_CTL,?OCF_WRITE_PAGE_TIMEOUT,<<?write_page_timeout_cp_bin(Timeout)>>,undefined).
+
+read_page_timeout(Socket) ->
+ hci_socket:call(Socket,?OGF_HOST_CTL,?OCF_READ_PAGE_TIMEOUT,<<>>,fun decode_read_page_timeout_rp/1).
+
+write_conn_accept_timeout(Socket,Timeout) ->
+ hci_socket:call(Socket,?OGF_HOST_CTL,?OCF_WRITE_CONN_ACCEPT_TIMEOUT,<<?write_conn_accept_timeout_cp_bin(Timeout)>>,undefined).
+
+read_conn_accept_timeout(Socket) ->
+ hci_socket:call(Socket,?OGF_HOST_CTL,?OCF_READ_CONN_ACCEPT_TIMEOUT,<<>>,fun decode_read_conn_accept_timeout_rp/1).
+
+read_local_name(Socket) ->
+ hci_socket:call(Socket,?OGF_HOST_CTL,?OCF_READ_LOCAL_NAME,<<>>,fun decode_read_local_name_rp/1).
+
+change_local_name(Socket,Name) ->
+ hci_socket:call(Socket,?OGF_HOST_CTL,?OCF_CHANGE_LOCAL_NAME,<<?change_local_name_cp_bin(Name)>>,undefined).
+
+delete_stored_link_key(Socket,Bdaddr,Delete_all) ->
+ hci_socket:call(Socket,?OGF_HOST_CTL,?OCF_DELETE_STORED_LINK_KEY,<<?delete_stored_link_key_cp_bin(Bdaddr,Delete_all)>>,fun decode_delete_stored_link_key_rp/1).
+
+write_stored_link_key(Socket,Num_keys) ->
+ hci_socket:call(Socket,?OGF_HOST_CTL,?OCF_WRITE_STORED_LINK_KEY,<<?write_stored_link_key_cp_bin(Num_keys)>>,fun decode_write_stored_link_key_rp/1).
+
+read_stored_link_key(Socket,Bdaddr,Read_all) ->
+ hci_socket:call(Socket,?OGF_HOST_CTL,?OCF_READ_STORED_LINK_KEY,<<?read_stored_link_key_cp_bin(Bdaddr,Read_all)>>,fun decode_read_stored_link_key_rp/1).
+
+create_new_unit_key(Socket) ->
+ hci_socket:call(Socket,?OGF_HOST_CTL,?OCF_CREATE_NEW_UNIT_KEY,<<>>,undefined).
+
+write_pin_type(Socket,Pin_type) ->
+ hci_socket:call(Socket,?OGF_HOST_CTL,?OCF_WRITE_PIN_TYPE,<<?write_pin_type_cp_bin(Pin_type)>>,undefined).
+
+read_pin_type(Socket) ->
+ hci_socket:call(Socket,?OGF_HOST_CTL,?OCF_READ_PIN_TYPE,<<>>,fun decode_read_pin_type_rp/1).
+
+flush(Socket) ->
+ hci_socket:call(Socket,?OGF_HOST_CTL,?OCF_FLUSH,<<>>,undefined).
+
+set_event_flt(Socket,Flt_type,Cond_type,Condition) ->
+ hci_socket:call(Socket,?OGF_HOST_CTL,?OCF_SET_EVENT_FLT,<<?set_event_flt_cp_bin(Flt_type,Cond_type,Condition)>>,undefined).
+
+reset(Socket) ->
+ hci_socket:call(Socket,?OGF_HOST_CTL,?OCF_RESET,<<>>,undefined).
+
+set_event_mask(Socket,Mask) ->
+ hci_socket:call(Socket,?OGF_HOST_CTL,?OCF_SET_EVENT_MASK,<<?set_event_mask_cp_bin(Mask)>>,undefined).
+
+sniff_subrating(Socket,Handle,Max_latency,Min_remote_timeout,Min_local_timeout) ->
+ hci_socket:call(Socket,?OGF_LINK_POLICY,?OCF_SNIFF_SUBRATING,<<?sniff_subrating_cp_bin(Handle,Max_latency,Min_remote_timeout,Min_local_timeout)>>,undefined).
+
+flow_specification(Socket) ->
+ hci_socket:call(Socket,?OGF_LINK_POLICY,?OCF_FLOW_SPECIFICATION,<<>>,undefined).
+
+write_default_link_policy(Socket) ->
+ hci_socket:call(Socket,?OGF_LINK_POLICY,?OCF_WRITE_DEFAULT_LINK_POLICY,<<>>,undefined).
+
+read_default_link_policy(Socket) ->
+ hci_socket:call(Socket,?OGF_LINK_POLICY,?OCF_READ_DEFAULT_LINK_POLICY,<<>>,undefined).
+
+write_link_policy(Socket,Handle,Policy) ->
+ hci_socket:call(Socket,?OGF_LINK_POLICY,?OCF_WRITE_LINK_POLICY,<<?write_link_policy_cp_bin(Handle,Policy)>>,fun decode_write_link_policy_rp/1).
+
+read_link_policy(Socket,Handle) ->
+ hci_socket:call(Socket,?OGF_LINK_POLICY,?OCF_READ_LINK_POLICY,<<?read_link_policy_cp_bin(Handle)>>,fun decode_read_link_policy_rp/1).
+
+switch_role(Socket,Bdaddr,Role) ->
+ hci_socket:call(Socket,?OGF_LINK_POLICY,?OCF_SWITCH_ROLE,<<?switch_role_cp_bin(Bdaddr,Role)>>,undefined).
+
+role_discovery(Socket,Handle) ->
+ hci_socket:call(Socket,?OGF_LINK_POLICY,?OCF_ROLE_DISCOVERY,<<?role_discovery_cp_bin(Handle)>>,fun decode_role_discovery_rp/1).
+
+qos_setup(Socket,Handle,Flags,Qos) ->
+ hci_socket:call(Socket,?OGF_LINK_POLICY,?OCF_QOS_SETUP,<<?qos_setup_cp_bin(Handle,Flags,Qos)>>,undefined).
+
+exit_park_mode(Socket,Handle) ->
+ hci_socket:call(Socket,?OGF_LINK_POLICY,?OCF_EXIT_PARK_MODE,<<?exit_park_mode_cp_bin(Handle)>>,undefined).
+
+park_mode(Socket,Handle,Max_interval,Min_interval) ->
+ hci_socket:call(Socket,?OGF_LINK_POLICY,?OCF_PARK_MODE,<<?park_mode_cp_bin(Handle,Max_interval,Min_interval)>>,undefined).
+
+exit_sniff_mode(Socket,Handle) ->
+ hci_socket:call(Socket,?OGF_LINK_POLICY,?OCF_EXIT_SNIFF_MODE,<<?exit_sniff_mode_cp_bin(Handle)>>,undefined).
+
+sniff_mode(Socket,Handle,Max_interval,Min_interval,Attempt,Timeout) ->
+ hci_socket:call(Socket,?OGF_LINK_POLICY,?OCF_SNIFF_MODE,<<?sniff_mode_cp_bin(Handle,Max_interval,Min_interval,Attempt,Timeout)>>,undefined).
+
+hold_mode(Socket,Handle,Max_interval,Min_interval) ->
+ hci_socket:call(Socket,?OGF_LINK_POLICY,?OCF_HOLD_MODE,<<?hold_mode_cp_bin(Handle,Max_interval,Min_interval)>>,undefined).
+
+flow_spec_modify(Socket) ->
+ hci_socket:call(Socket,?OGF_LINK_CTL,?OCF_FLOW_SPEC_MODIFY,<<>>,undefined).
+
+logical_link_cancel(Socket) ->
+ hci_socket:call(Socket,?OGF_LINK_CTL,?OCF_LOGICAL_LINK_CANCEL,<<>>,undefined).
+
+disconnect_logical_link(Socket,Handle) ->
+ hci_socket:call(Socket,?OGF_LINK_CTL,?OCF_DISCONNECT_LOGICAL_LINK,<<?disconnect_logical_link_cp_bin(Handle)>>,undefined).
+
+accept_logical_link(Socket) ->
+ hci_socket:call(Socket,?OGF_LINK_CTL,?OCF_ACCEPT_LOGICAL_LINK,<<>>,undefined).
+
+create_logical_link(Socket,Handle,Tx_flow,Rx_flow) ->
+ hci_socket:call(Socket,?OGF_LINK_CTL,?OCF_CREATE_LOGICAL_LINK,<<?create_logical_link_cp_bin(Handle,Tx_flow,Rx_flow)>>,undefined).
+
+disconnect_physical_link(Socket,Handle,Reason) ->
+ hci_socket:call(Socket,?OGF_LINK_CTL,?OCF_DISCONNECT_PHYSICAL_LINK,<<?disconnect_physical_link_cp_bin(Handle,Reason)>>,undefined).
+
+accept_physical_link(Socket) ->
+ hci_socket:call(Socket,?OGF_LINK_CTL,?OCF_ACCEPT_PHYSICAL_LINK,<<>>,undefined).
+
+create_physical_link(Socket,Handle,Key_length,Key_type,Key) ->
+ hci_socket:call(Socket,?OGF_LINK_CTL,?OCF_CREATE_PHYSICAL_LINK,<<?create_physical_link_cp_bin(Handle,Key_length,Key_type,Key)>>,undefined).
+
+io_capability_neg_reply(Socket,Bdaddr,Reason) ->
+ hci_socket:call(Socket,?OGF_LINK_CTL,?OCF_IO_CAPABILITY_NEG_REPLY,<<?io_capability_neg_reply_cp_bin(Bdaddr,Reason)>>,undefined).
+
+remote_oob_data_neg_reply(Socket) ->
+ hci_socket:call(Socket,?OGF_LINK_CTL,?OCF_REMOTE_OOB_DATA_NEG_REPLY,<<>>,undefined).
+
+remote_oob_data_reply(Socket,Bdaddr,Hash,Randomizer) ->
+ hci_socket:call(Socket,?OGF_LINK_CTL,?OCF_REMOTE_OOB_DATA_REPLY,<<?remote_oob_data_reply_cp_bin(Bdaddr,Hash,Randomizer)>>,undefined).
+
+user_passkey_neg_reply(Socket) ->
+ hci_socket:call(Socket,?OGF_LINK_CTL,?OCF_USER_PASSKEY_NEG_REPLY,<<>>,undefined).
+
+user_passkey_reply(Socket,Bdaddr,Passkey) ->
+ hci_socket:call(Socket,?OGF_LINK_CTL,?OCF_USER_PASSKEY_REPLY,<<?user_passkey_reply_cp_bin(Bdaddr,Passkey)>>,undefined).
+
+user_confirm_neg_reply(Socket) ->
+ hci_socket:call(Socket,?OGF_LINK_CTL,?OCF_USER_CONFIRM_NEG_REPLY,<<>>,undefined).
+
+user_confirm_reply(Socket,Bdaddr) ->
+ hci_socket:call(Socket,?OGF_LINK_CTL,?OCF_USER_CONFIRM_REPLY,<<?user_confirm_reply_cp_bin(Bdaddr)>>,undefined).
+
+io_capability_reply(Socket,Bdaddr,Capability,Oob_data,Authentication) ->
+ hci_socket:call(Socket,?OGF_LINK_CTL,?OCF_IO_CAPABILITY_REPLY,<<?io_capability_reply_cp_bin(Bdaddr,Capability,Oob_data,Authentication)>>,undefined).
+
+reject_sync_conn_req(Socket,Bdaddr,Reason) ->
+ hci_socket:call(Socket,?OGF_LINK_CTL,?OCF_REJECT_SYNC_CONN_REQ,<<?reject_sync_conn_req_cp_bin(Bdaddr,Reason)>>,undefined).
+
+accept_sync_conn_req(Socket,Bdaddr,Tx_bandwith,Rx_bandwith,Max_latency,Voice_setting,Retrans_effort,Pkt_type) ->
+ hci_socket:call(Socket,?OGF_LINK_CTL,?OCF_ACCEPT_SYNC_CONN_REQ,<<?accept_sync_conn_req_cp_bin(Bdaddr,Tx_bandwith,Rx_bandwith,Max_latency,Voice_setting,Retrans_effort,Pkt_type)>>,undefined).
+
+setup_sync_conn(Socket,Handle,Tx_bandwith,Rx_bandwith,Max_latency,Voice_setting,Retrans_effort,Pkt_type) ->
+ hci_socket:call(Socket,?OGF_LINK_CTL,?OCF_SETUP_SYNC_CONN,<<?setup_sync_conn_cp_bin(Handle,Tx_bandwith,Rx_bandwith,Max_latency,Voice_setting,Retrans_effort,Pkt_type)>>,undefined).
+
+read_lmp_handle(Socket) ->
+ hci_socket:call(Socket,?OGF_LINK_CTL,?OCF_READ_LMP_HANDLE,<<>>,undefined).
+
+read_clock_offset(Socket,Handle) ->
+ hci_socket:call(Socket,?OGF_LINK_CTL,?OCF_READ_CLOCK_OFFSET,<<?read_clock_offset_cp_bin(Handle)>>,undefined).
+
+read_remote_version(Socket,Handle) ->
+ hci_socket:call(Socket,?OGF_LINK_CTL,?OCF_READ_REMOTE_VERSION,<<?read_remote_version_cp_bin(Handle)>>,undefined).
+
+read_remote_ext_features(Socket,Handle,Page_num) ->
+ hci_socket:call(Socket,?OGF_LINK_CTL,?OCF_READ_REMOTE_EXT_FEATURES,<<?read_remote_ext_features_cp_bin(Handle,Page_num)>>,undefined).
+
+read_remote_features(Socket,Handle) ->
+ hci_socket:call(Socket,?OGF_LINK_CTL,?OCF_READ_REMOTE_FEATURES,<<?read_remote_features_cp_bin(Handle)>>,undefined).
+
+remote_name_req_cancel(Socket,Bdaddr) ->
+ hci_socket:call(Socket,?OGF_LINK_CTL,?OCF_REMOTE_NAME_REQ_CANCEL,<<?remote_name_req_cancel_cp_bin(Bdaddr)>>,undefined).
+
+remote_name_req(Socket,Bdaddr,Pscan_rep_mode,Pscan_mode,Clock_offset) ->
+ hci_socket:call(Socket,?OGF_LINK_CTL,?OCF_REMOTE_NAME_REQ,<<?remote_name_req_cp_bin(Bdaddr,Pscan_rep_mode,Pscan_mode,Clock_offset)>>,undefined).
+
+master_link_key(Socket,Key_flag) ->
+ hci_socket:call(Socket,?OGF_LINK_CTL,?OCF_MASTER_LINK_KEY,<<?master_link_key_cp_bin(Key_flag)>>,undefined).
+
+change_conn_link_key(Socket,Handle) ->
+ hci_socket:call(Socket,?OGF_LINK_CTL,?OCF_CHANGE_CONN_LINK_KEY,<<?change_conn_link_key_cp_bin(Handle)>>,undefined).
+
+set_conn_encrypt(Socket,Handle,Encrypt) ->
+ hci_socket:call(Socket,?OGF_LINK_CTL,?OCF_SET_CONN_ENCRYPT,<<?set_conn_encrypt_cp_bin(Handle,Encrypt)>>,undefined).
+
+auth_requested(Socket,Handle) ->
+ hci_socket:call(Socket,?OGF_LINK_CTL,?OCF_AUTH_REQUESTED,<<?auth_requested_cp_bin(Handle)>>,undefined).
+
+set_conn_ptype(Socket,Handle,Pkt_type) ->
+ hci_socket:call(Socket,?OGF_LINK_CTL,?OCF_SET_CONN_PTYPE,<<?set_conn_ptype_cp_bin(Handle,Pkt_type)>>,undefined).
+
+pin_code_neg_reply(Socket) ->
+ hci_socket:call(Socket,?OGF_LINK_CTL,?OCF_PIN_CODE_NEG_REPLY,<<>>,undefined).
+
+pin_code_reply(Socket,Bdaddr,Pin_len,Pin_code) ->
+ hci_socket:call(Socket,?OGF_LINK_CTL,?OCF_PIN_CODE_REPLY,<<?pin_code_reply_cp_bin(Bdaddr,Pin_len,Pin_code)>>,undefined).
+
+link_key_neg_reply(Socket,Bdaddr) ->
+ hci_socket:call(Socket,?OGF_LINK_CTL,?OCF_LINK_KEY_NEG_REPLY,<<?link_key_neg_reply_cp_bin(Bdaddr)>>,undefined).
+
+link_key_reply(Socket,Bdaddr,Link_key) ->
+ hci_socket:call(Socket,?OGF_LINK_CTL,?OCF_LINK_KEY_REPLY,<<?link_key_reply_cp_bin(Bdaddr,Link_key)>>,undefined).
+
+reject_conn_req(Socket,Bdaddr,Reason) ->
+ hci_socket:call(Socket,?OGF_LINK_CTL,?OCF_REJECT_CONN_REQ,<<?reject_conn_req_cp_bin(Bdaddr,Reason)>>,undefined).
+
+accept_conn_req(Socket,Bdaddr,Role) ->
+ hci_socket:call(Socket,?OGF_LINK_CTL,?OCF_ACCEPT_CONN_REQ,<<?accept_conn_req_cp_bin(Bdaddr,Role)>>,undefined).
+
+create_conn_cancel(Socket,Bdaddr) ->
+ hci_socket:call(Socket,?OGF_LINK_CTL,?OCF_CREATE_CONN_CANCEL,<<?create_conn_cancel_cp_bin(Bdaddr)>>,undefined).
+
+add_sco(Socket,Handle,Pkt_type) ->
+ hci_socket:call(Socket,?OGF_LINK_CTL,?OCF_ADD_SCO,<<?add_sco_cp_bin(Handle,Pkt_type)>>,undefined).
+
+disconnect(Socket,Handle,Reason) ->
+ hci_socket:call(Socket,?OGF_LINK_CTL,?OCF_DISCONNECT,<<?disconnect_cp_bin(Handle,Reason)>>,undefined).
+
+create_conn(Socket,Bdaddr,Pkt_type,Pscan_rep_mode,Pscan_mode,Clock_offset,Role_switch) ->
+ hci_socket:call(Socket,?OGF_LINK_CTL,?OCF_CREATE_CONN,<<?create_conn_cp_bin(Bdaddr,Pkt_type,Pscan_rep_mode,Pscan_mode,Clock_offset,Role_switch)>>,undefined).
+
+exit_periodic_inquiry(Socket) ->
+ hci_socket:call(Socket,?OGF_LINK_CTL,?OCF_EXIT_PERIODIC_INQUIRY,<<>>,undefined).
+
+periodic_inquiry(Socket,Max_period,Min_period,Lap,Length,Num_rsp) ->
+ hci_socket:call(Socket,?OGF_LINK_CTL,?OCF_PERIODIC_INQUIRY,<<?periodic_inquiry_cp_bin(Max_period,Min_period,Lap,Length,Num_rsp)>>,undefined).
+
+inquiry_cancel(Socket) ->
+ hci_socket:call(Socket,?OGF_LINK_CTL,?OCF_INQUIRY_CANCEL,<<>>,undefined).
+
+inquiry(Socket,Lap,Length,Num_rsp) ->
+ hci_socket:call(Socket,?OGF_LINK_CTL,?OCF_INQUIRY,<<?inquiry_cp_bin(Lap,Length,Num_rsp)>>,undefined).
+
diff --git a/deps/bt/src/hci_api.hrl b/deps/bt/src/hci_api.hrl
new file mode 100644
index 0000000..4cf89d6
--- /dev/null
+++ b/deps/bt/src/hci_api.hrl
@@ -0,0 +1,1935 @@
+%% -*- erlang -*-
+-ifndef(__HCI_HRL__).
+-define(__HCI_HRL__,true).
+
+
+-define(HCI_MAX_DEV, 16).
+-define(HCI_MAX_ACL_SIZE, 1024).
+-define(HCI_MAX_SCO_SIZE, 255).
+-define(HCI_MAX_EVENT_SIZE, 260).
+-define(HCI_MAX_FRAME_SIZE, (?HCI_MAX_ACL_SIZE + 4)).
+-define(HCI_DEV_REG, 1).
+-define(HCI_DEV_UNREG, 2).
+-define(HCI_DEV_UP, 3).
+-define(HCI_DEV_DOWN, 4).
+-define(HCI_DEV_SUSPEND, 5).
+-define(HCI_DEV_RESUME, 6).
+-define(HCI_UNKNOWN_COMMAND, 1).
+-define(HCI_NO_CONNECTION, 2).
+-define(HCI_HARDWARE_FAILURE, 3).
+-define(HCI_PAGE_TIMEOUT, 4).
+-define(HCI_AUTHENTICATION_FAILURE, 5).
+-define(HCI_PIN_OR_KEY_MISSING, 6).
+-define(HCI_MEMORY_FULL, 7).
+-define(HCI_CONNECTION_TIMEOUT, 8).
+-define(HCI_MAX_NUMBER_OF_CONNECTIONS, 9).
+-define(HCI_MAX_NUMBER_OF_SCO_CONNECTIONS, 10).
+-define(HCI_ACL_CONNECTION_EXISTS, 11).
+-define(HCI_COMMAND_DISALLOWED, 12).
+-define(HCI_REJECTED_LIMITED_RESOURCES, 13).
+-define(HCI_REJECTED_SECURITY, 14).
+-define(HCI_REJECTED_PERSONAL, 15).
+-define(HCI_HOST_TIMEOUT, 16).
+-define(HCI_UNSUPPORTED_FEATURE, 17).
+-define(HCI_INVALID_PARAMETERS, 18).
+-define(HCI_OE_USER_ENDED_CONNECTION, 19).
+-define(HCI_OE_LOW_RESOURCES, 20).
+-define(HCI_OE_POWER_OFF, 21).
+-define(HCI_CONNECTION_TERMINATED, 22).
+-define(HCI_REPEATED_ATTEMPTS, 23).
+-define(HCI_PAIRING_NOT_ALLOWED, 24).
+-define(HCI_UNKNOWN_LMP_PDU, 25).
+-define(HCI_UNSUPPORTED_REMOTE_FEATURE, 26).
+-define(HCI_SCO_OFFSET_REJECTED, 27).
+-define(HCI_SCO_INTERVAL_REJECTED, 28).
+-define(HCI_AIR_MODE_REJECTED, 29).
+-define(HCI_INVALID_LMP_PARAMETERS, 30).
+-define(HCI_UNSPECIFIED_ERROR, 31).
+-define(HCI_UNSUPPORTED_LMP_PARAMETER_VALUE, 32).
+-define(HCI_ROLE_CHANGE_NOT_ALLOWED, 33).
+-define(HCI_LMP_RESPONSE_TIMEOUT, 34).
+-define(HCI_LMP_ERROR_TRANSACTION_COLLISION, 35).
+-define(HCI_LMP_PDU_NOT_ALLOWED, 36).
+-define(HCI_ENCRYPTION_MODE_NOT_ACCEPTED, 37).
+-define(HCI_UNIT_LINK_KEY_USED, 38).
+-define(HCI_QOS_NOT_SUPPORTED, 39).
+-define(HCI_INSTANT_PASSED, 40).
+-define(HCI_PAIRING_NOT_SUPPORTED, 41).
+-define(HCI_TRANSACTION_COLLISION, 42).
+-define(HCI_QOS_UNACCEPTABLE_PARAMETER, 44).
+-define(HCI_QOS_REJECTED, 45).
+-define(HCI_CLASSIFICATION_NOT_SUPPORTED, 46).
+-define(HCI_INSUFFICIENT_SECURITY, 47).
+-define(HCI_PARAMETER_OUT_OF_RANGE, 48).
+-define(HCI_ROLE_SWITCH_PENDING, 50).
+-define(HCI_SLOT_VIOLATION, 52).
+-define(HCI_ROLE_SWITCH_FAILED, 53).
+-define(HCI_EIR_TOO_LARGE, 54).
+-define(HCI_SIMPLE_PAIRING_NOT_SUPPORTED, 55).
+-define(HCI_HOST_BUSY_PAIRING, 56).
+-define(OGF_LINK_CTL, 1).
+-define(OCF_INQUIRY, 1).
+-record(inquiry_cp, {
+ lap,
+ length,
+ num_rsp
+}).
+-define(inquiry_cp_bin(Lap,Length,Num_rsp),Lap:3/unit:8-binary,Length:1/unsigned-unit:8,Num_rsp:1/unsigned-unit:8).
+-define(INQUIRY_CP_SIZE, 5).
+-record(status_bdaddr_rp, {
+ status,
+ bdaddr
+}).
+-define(status_bdaddr_rp_bin(Status,Bdaddr),Status:1/unsigned-unit:8,Bdaddr:6/unit:8-binary).
+-define(STATUS_BDADDR_RP_SIZE, 7).
+-define(OCF_INQUIRY_CANCEL, 2).
+-define(OCF_PERIODIC_INQUIRY, 3).
+-record(periodic_inquiry_cp, {
+ max_period,
+ min_period,
+ lap,
+ length,
+ num_rsp
+}).
+-define(periodic_inquiry_cp_bin(Max_period,Min_period,Lap,Length,Num_rsp),Max_period:1/little-unsigned-unit:16,Min_period:1/little-unsigned-unit:16,Lap:3/unit:8-binary,Length:1/unsigned-unit:8,Num_rsp:1/unsigned-unit:8).
+-define(PERIODIC_INQUIRY_CP_SIZE, 9).
+-define(OCF_EXIT_PERIODIC_INQUIRY, 4).
+-define(OCF_CREATE_CONN, 5).
+-record(create_conn_cp, {
+ bdaddr,
+ pkt_type,
+ pscan_rep_mode,
+ pscan_mode,
+ clock_offset,
+ role_switch
+}).
+-define(create_conn_cp_bin(Bdaddr,Pkt_type,Pscan_rep_mode,Pscan_mode,Clock_offset,Role_switch),Bdaddr:6/unit:8-binary,Pkt_type:1/little-unsigned-unit:16,Pscan_rep_mode:1/unsigned-unit:8,Pscan_mode:1/unsigned-unit:8,Clock_offset:1/little-unsigned-unit:16,Role_switch:1/unsigned-unit:8).
+-define(CREATE_CONN_CP_SIZE, 13).
+-define(OCF_DISCONNECT, 6).
+-record(disconnect_cp, {
+ handle,
+ reason
+}).
+-define(disconnect_cp_bin(Handle,Reason),Handle:1/little-unsigned-unit:16,Reason:1/unsigned-unit:8).
+-define(DISCONNECT_CP_SIZE, 3).
+-define(OCF_ADD_SCO, 7).
+-record(add_sco_cp, {
+ handle,
+ pkt_type
+}).
+-define(add_sco_cp_bin(Handle,Pkt_type),Handle:1/little-unsigned-unit:16,Pkt_type:1/little-unsigned-unit:16).
+-define(ADD_SCO_CP_SIZE, 4).
+-define(OCF_CREATE_CONN_CANCEL, 8).
+-record(create_conn_cancel_cp, {
+ bdaddr
+}).
+-define(create_conn_cancel_cp_bin(Bdaddr),Bdaddr:6/unit:8-binary).
+-define(CREATE_CONN_CANCEL_CP_SIZE, 6).
+-define(OCF_ACCEPT_CONN_REQ, 9).
+-record(accept_conn_req_cp, {
+ bdaddr,
+ role
+}).
+-define(accept_conn_req_cp_bin(Bdaddr,Role),Bdaddr:6/unit:8-binary,Role:1/unsigned-unit:8).
+-define(ACCEPT_CONN_REQ_CP_SIZE, 7).
+-define(OCF_REJECT_CONN_REQ, 10).
+-record(reject_conn_req_cp, {
+ bdaddr,
+ reason
+}).
+-define(reject_conn_req_cp_bin(Bdaddr,Reason),Bdaddr:6/unit:8-binary,Reason:1/unsigned-unit:8).
+-define(REJECT_CONN_REQ_CP_SIZE, 7).
+-define(OCF_LINK_KEY_REPLY, 11).
+-record(link_key_reply_cp, {
+ bdaddr,
+ link_key
+}).
+-define(link_key_reply_cp_bin(Bdaddr,Link_key),Bdaddr:6/unit:8-binary,Link_key:16/unit:8-binary).
+-define(LINK_KEY_REPLY_CP_SIZE, 22).
+-define(OCF_LINK_KEY_NEG_REPLY, 12).
+-record(link_key_neg_reply_cp, {
+ bdaddr
+}).
+-define(link_key_neg_reply_cp_bin(Bdaddr),Bdaddr:6/unit:8-binary).
+-define(LINK_KEY_NEG_REPLY_CP_SIZE, 6).
+-define(OCF_PIN_CODE_REPLY, 13).
+-record(pin_code_reply_cp, {
+ bdaddr,
+ pin_len,
+ pin_code
+}).
+-define(pin_code_reply_cp_bin(Bdaddr,Pin_len,Pin_code),Bdaddr:6/unit:8-binary,Pin_len:1/unsigned-unit:8,Pin_code:16/unit:8-binary).
+-define(PIN_CODE_REPLY_CP_SIZE, 23).
+-define(OCF_PIN_CODE_NEG_REPLY, 14).
+-define(OCF_SET_CONN_PTYPE, 15).
+-record(set_conn_ptype_cp, {
+ handle,
+ pkt_type
+}).
+-define(set_conn_ptype_cp_bin(Handle,Pkt_type),Handle:1/little-unsigned-unit:16,Pkt_type:1/little-unsigned-unit:16).
+-define(SET_CONN_PTYPE_CP_SIZE, 4).
+-define(OCF_AUTH_REQUESTED, 17).
+-record(auth_requested_cp, {
+ handle
+}).
+-define(auth_requested_cp_bin(Handle),Handle:1/little-unsigned-unit:16).
+-define(AUTH_REQUESTED_CP_SIZE, 2).
+-define(OCF_SET_CONN_ENCRYPT, 19).
+-record(set_conn_encrypt_cp, {
+ handle,
+ encrypt
+}).
+-define(set_conn_encrypt_cp_bin(Handle,Encrypt),Handle:1/little-unsigned-unit:16,Encrypt:1/unsigned-unit:8).
+-define(SET_CONN_ENCRYPT_CP_SIZE, 3).
+-define(OCF_CHANGE_CONN_LINK_KEY, 21).
+-record(change_conn_link_key_cp, {
+ handle
+}).
+-define(change_conn_link_key_cp_bin(Handle),Handle:1/little-unsigned-unit:16).
+-define(CHANGE_CONN_LINK_KEY_CP_SIZE, 2).
+-define(OCF_MASTER_LINK_KEY, 23).
+-record(master_link_key_cp, {
+ key_flag
+}).
+-define(master_link_key_cp_bin(Key_flag),Key_flag:1/unsigned-unit:8).
+-define(MASTER_LINK_KEY_CP_SIZE, 1).
+-define(OCF_REMOTE_NAME_REQ, 25).
+-record(remote_name_req_cp, {
+ bdaddr,
+ pscan_rep_mode,
+ pscan_mode,
+ clock_offset
+}).
+-define(remote_name_req_cp_bin(Bdaddr,Pscan_rep_mode,Pscan_mode,Clock_offset),Bdaddr:6/unit:8-binary,Pscan_rep_mode:1/unsigned-unit:8,Pscan_mode:1/unsigned-unit:8,Clock_offset:1/little-unsigned-unit:16).
+-define(REMOTE_NAME_REQ_CP_SIZE, 10).
+-define(OCF_REMOTE_NAME_REQ_CANCEL, 26).
+-record(remote_name_req_cancel_cp, {
+ bdaddr
+}).
+-define(remote_name_req_cancel_cp_bin(Bdaddr),Bdaddr:6/unit:8-binary).
+-define(REMOTE_NAME_REQ_CANCEL_CP_SIZE, 6).
+-define(OCF_READ_REMOTE_FEATURES, 27).
+-record(read_remote_features_cp, {
+ handle
+}).
+-define(read_remote_features_cp_bin(Handle),Handle:1/little-unsigned-unit:16).
+-define(READ_REMOTE_FEATURES_CP_SIZE, 2).
+-define(OCF_READ_REMOTE_EXT_FEATURES, 28).
+-record(read_remote_ext_features_cp, {
+ handle,
+ page_num
+}).
+-define(read_remote_ext_features_cp_bin(Handle,Page_num),Handle:1/little-unsigned-unit:16,Page_num:1/unsigned-unit:8).
+-define(READ_REMOTE_EXT_FEATURES_CP_SIZE, 3).
+-define(OCF_READ_REMOTE_VERSION, 29).
+-record(read_remote_version_cp, {
+ handle
+}).
+-define(read_remote_version_cp_bin(Handle),Handle:1/little-unsigned-unit:16).
+-define(READ_REMOTE_VERSION_CP_SIZE, 2).
+-define(OCF_READ_CLOCK_OFFSET, 31).
+-record(read_clock_offset_cp, {
+ handle
+}).
+-define(read_clock_offset_cp_bin(Handle),Handle:1/little-unsigned-unit:16).
+-define(READ_CLOCK_OFFSET_CP_SIZE, 2).
+-define(OCF_READ_LMP_HANDLE, 32).
+-define(OCF_SETUP_SYNC_CONN, 40).
+-record(setup_sync_conn_cp, {
+ handle,
+ tx_bandwith,
+ rx_bandwith,
+ max_latency,
+ voice_setting,
+ retrans_effort,
+ pkt_type
+}).
+-define(setup_sync_conn_cp_bin(Handle,Tx_bandwith,Rx_bandwith,Max_latency,Voice_setting,Retrans_effort,Pkt_type),Handle:1/little-unsigned-unit:16,Tx_bandwith:1/little-unsigned-unit:32,Rx_bandwith:1/little-unsigned-unit:32,Max_latency:1/little-unsigned-unit:16,Voice_setting:1/little-unsigned-unit:16,Retrans_effort:1/unsigned-unit:8,Pkt_type:1/little-unsigned-unit:16).
+-define(SETUP_SYNC_CONN_CP_SIZE, 17).
+-define(OCF_ACCEPT_SYNC_CONN_REQ, 41).
+-record(accept_sync_conn_req_cp, {
+ bdaddr,
+ tx_bandwith,
+ rx_bandwith,
+ max_latency,
+ voice_setting,
+ retrans_effort,
+ pkt_type
+}).
+-define(accept_sync_conn_req_cp_bin(Bdaddr,Tx_bandwith,Rx_bandwith,Max_latency,Voice_setting,Retrans_effort,Pkt_type),Bdaddr:6/unit:8-binary,Tx_bandwith:1/little-unsigned-unit:32,Rx_bandwith:1/little-unsigned-unit:32,Max_latency:1/little-unsigned-unit:16,Voice_setting:1/little-unsigned-unit:16,Retrans_effort:1/unsigned-unit:8,Pkt_type:1/little-unsigned-unit:16).
+-define(ACCEPT_SYNC_CONN_REQ_CP_SIZE, 21).
+-define(OCF_REJECT_SYNC_CONN_REQ, 42).
+-record(reject_sync_conn_req_cp, {
+ bdaddr,
+ reason
+}).
+-define(reject_sync_conn_req_cp_bin(Bdaddr,Reason),Bdaddr:6/unit:8-binary,Reason:1/unsigned-unit:8).
+-define(REJECT_SYNC_CONN_REQ_CP_SIZE, 7).
+-define(OCF_IO_CAPABILITY_REPLY, 43).
+-record(io_capability_reply_cp, {
+ bdaddr,
+ capability,
+ oob_data,
+ authentication
+}).
+-define(io_capability_reply_cp_bin(Bdaddr,Capability,Oob_data,Authentication),Bdaddr:6/unit:8-binary,Capability:1/unsigned-unit:8,Oob_data:1/unsigned-unit:8,Authentication:1/unsigned-unit:8).
+-define(IO_CAPABILITY_REPLY_CP_SIZE, 9).
+-define(OCF_USER_CONFIRM_REPLY, 44).
+-record(user_confirm_reply_cp, {
+ bdaddr
+}).
+-define(user_confirm_reply_cp_bin(Bdaddr),Bdaddr:6/unit:8-binary).
+-define(USER_CONFIRM_REPLY_CP_SIZE, 6).
+-define(OCF_USER_CONFIRM_NEG_REPLY, 45).
+-define(OCF_USER_PASSKEY_REPLY, 46).
+-record(user_passkey_reply_cp, {
+ bdaddr,
+ passkey
+}).
+-define(user_passkey_reply_cp_bin(Bdaddr,Passkey),Bdaddr:6/unit:8-binary,Passkey:1/little-unsigned-unit:32).
+-define(USER_PASSKEY_REPLY_CP_SIZE, 10).
+-define(OCF_USER_PASSKEY_NEG_REPLY, 47).
+-define(OCF_REMOTE_OOB_DATA_REPLY, 48).
+-record(remote_oob_data_reply_cp, {
+ bdaddr,
+ hash,
+ randomizer
+}).
+-define(remote_oob_data_reply_cp_bin(Bdaddr,Hash,Randomizer),Bdaddr:6/unit:8-binary,Hash:16/unit:8-binary,Randomizer:16/unit:8-binary).
+-define(REMOTE_OOB_DATA_REPLY_CP_SIZE, 38).
+-define(OCF_REMOTE_OOB_DATA_NEG_REPLY, 51).
+-define(OCF_IO_CAPABILITY_NEG_REPLY, 52).
+-record(io_capability_neg_reply_cp, {
+ bdaddr,
+ reason
+}).
+-define(io_capability_neg_reply_cp_bin(Bdaddr,Reason),Bdaddr:6/unit:8-binary,Reason:1/unsigned-unit:8).
+-define(IO_CAPABILITY_NEG_REPLY_CP_SIZE, 7).
+-define(OCF_CREATE_PHYSICAL_LINK, 53).
+-record(create_physical_link_cp, {
+ handle,
+ key_length,
+ key_type,
+ key
+}).
+-define(create_physical_link_cp_bin(Handle,Key_length,Key_type,Key),Handle:1/unsigned-unit:8,Key_length:1/unsigned-unit:8,Key_type:1/unsigned-unit:8,Key:32/unit:8-binary).
+-define(CREATE_PHYSICAL_LINK_CP_SIZE, 35).
+-define(OCF_ACCEPT_PHYSICAL_LINK, 54).
+-define(OCF_DISCONNECT_PHYSICAL_LINK, 55).
+-record(disconnect_physical_link_cp, {
+ handle,
+ reason
+}).
+-define(disconnect_physical_link_cp_bin(Handle,Reason),Handle:1/unsigned-unit:8,Reason:1/unsigned-unit:8).
+-define(DISCONNECT_PHYSICAL_LINK_CP_SIZE, 2).
+-define(OCF_CREATE_LOGICAL_LINK, 56).
+-record(create_logical_link_cp, {
+ handle,
+ tx_flow,
+ rx_flow
+}).
+-define(create_logical_link_cp_bin(Handle,Tx_flow,Rx_flow),Handle:1/unsigned-unit:8,Tx_flow:16/unit:8-binary,Rx_flow:16/unit:8-binary).
+-define(CREATE_LOGICAL_LINK_CP_SIZE, 33).
+-define(OCF_ACCEPT_LOGICAL_LINK, 57).
+-define(OCF_DISCONNECT_LOGICAL_LINK, 58).
+-record(disconnect_logical_link_cp, {
+ handle
+}).
+-define(disconnect_logical_link_cp_bin(Handle),Handle:1/little-unsigned-unit:16).
+-define(DISCONNECT_LOGICAL_LINK_CP_SIZE, 2).
+-define(OCF_LOGICAL_LINK_CANCEL, 59).
+-record(cancel_logical_link_cp, {
+ handle,
+ tx_flow_id
+}).
+-define(cancel_logical_link_cp_bin(Handle,Tx_flow_id),Handle:1/unsigned-unit:8,Tx_flow_id:1/unsigned-unit:8).
+-define(LOGICAL_LINK_CANCEL_CP_SIZE, 2).
+-record(cancel_logical_link_rp, {
+ status,
+ handle,
+ tx_flow_id
+}).
+-define(cancel_logical_link_rp_bin(Status,Handle,Tx_flow_id),Status:1/unsigned-unit:8,Handle:1/unsigned-unit:8,Tx_flow_id:1/unsigned-unit:8).
+-define(LOGICAL_LINK_CANCEL_RP_SIZE, 3).
+-define(OCF_FLOW_SPEC_MODIFY, 60).
+-define(OGF_LINK_POLICY, 2).
+-define(OCF_HOLD_MODE, 1).
+-record(hold_mode_cp, {
+ handle,
+ max_interval,
+ min_interval
+}).
+-define(hold_mode_cp_bin(Handle,Max_interval,Min_interval),Handle:1/little-unsigned-unit:16,Max_interval:1/little-unsigned-unit:16,Min_interval:1/little-unsigned-unit:16).
+-define(HOLD_MODE_CP_SIZE, 6).
+-define(OCF_SNIFF_MODE, 3).
+-record(sniff_mode_cp, {
+ handle,
+ max_interval,
+ min_interval,
+ attempt,
+ timeout
+}).
+-define(sniff_mode_cp_bin(Handle,Max_interval,Min_interval,Attempt,Timeout),Handle:1/little-unsigned-unit:16,Max_interval:1/little-unsigned-unit:16,Min_interval:1/little-unsigned-unit:16,Attempt:1/little-unsigned-unit:16,Timeout:1/little-unsigned-unit:16).
+-define(SNIFF_MODE_CP_SIZE, 10).
+-define(OCF_EXIT_SNIFF_MODE, 4).
+-record(exit_sniff_mode_cp, {
+ handle
+}).
+-define(exit_sniff_mode_cp_bin(Handle),Handle:1/little-unsigned-unit:16).
+-define(EXIT_SNIFF_MODE_CP_SIZE, 2).
+-define(OCF_PARK_MODE, 5).
+-record(park_mode_cp, {
+ handle,
+ max_interval,
+ min_interval
+}).
+-define(park_mode_cp_bin(Handle,Max_interval,Min_interval),Handle:1/little-unsigned-unit:16,Max_interval:1/little-unsigned-unit:16,Min_interval:1/little-unsigned-unit:16).
+-define(PARK_MODE_CP_SIZE, 6).
+-define(OCF_EXIT_PARK_MODE, 6).
+-record(exit_park_mode_cp, {
+ handle
+}).
+-define(exit_park_mode_cp_bin(Handle),Handle:1/little-unsigned-unit:16).
+-define(EXIT_PARK_MODE_CP_SIZE, 2).
+-define(OCF_QOS_SETUP, 7).
+-record(hci_qos, {
+ service_type,
+ token_rate,
+ peak_bandwidth,
+ latency,
+ delay_variation
+}).
+-define(hci_qos_bin(Service_type,Token_rate,Peak_bandwidth,Latency,Delay_variation),Service_type:1/unsigned-unit:8,Token_rate:1/little-unsigned-unit:32,Peak_bandwidth:1/little-unsigned-unit:32,Latency:1/little-unsigned-unit:32,Delay_variation:1/little-unsigned-unit:32).
+-define(HCI_QOS_CP_SIZE, 17).
+-record(qos_setup_cp, {
+ handle,
+ flags,
+ qos
+}).
+-define(qos_setup_cp_bin(Handle,Flags,Qos),Handle:1/little-unsigned-unit:16,Flags:1/unsigned-unit:8,Qos:17/unit:8-binary).
+-define(QOS_SETUP_CP_SIZE, (3 + ?HCI_QOS_CP_SIZE)).
+-define(OCF_ROLE_DISCOVERY, 9).
+-record(role_discovery_cp, {
+ handle
+}).
+-define(role_discovery_cp_bin(Handle),Handle:1/little-unsigned-unit:16).
+-define(ROLE_DISCOVERY_CP_SIZE, 2).
+-record(role_discovery_rp, {
+ status,
+ handle,
+ role
+}).
+-define(role_discovery_rp_bin(Status,Handle,Role),Status:1/unsigned-unit:8,Handle:1/little-unsigned-unit:16,Role:1/unsigned-unit:8).
+-define(ROLE_DISCOVERY_RP_SIZE, 4).
+-define(OCF_SWITCH_ROLE, 11).
+-record(switch_role_cp, {
+ bdaddr,
+ role
+}).
+-define(switch_role_cp_bin(Bdaddr,Role),Bdaddr:6/unit:8-binary,Role:1/unsigned-unit:8).
+-define(SWITCH_ROLE_CP_SIZE, 7).
+-define(OCF_READ_LINK_POLICY, 12).
+-record(read_link_policy_cp, {
+ handle
+}).
+-define(read_link_policy_cp_bin(Handle),Handle:1/little-unsigned-unit:16).
+-define(READ_LINK_POLICY_CP_SIZE, 2).
+-record(read_link_policy_rp, {
+ status,
+ handle,
+ policy
+}).
+-define(read_link_policy_rp_bin(Status,Handle,Policy),Status:1/unsigned-unit:8,Handle:1/little-unsigned-unit:16,Policy:1/little-unsigned-unit:16).
+-define(READ_LINK_POLICY_RP_SIZE, 5).
+-define(OCF_WRITE_LINK_POLICY, 13).
+-record(write_link_policy_cp, {
+ handle,
+ policy
+}).
+-define(write_link_policy_cp_bin(Handle,Policy),Handle:1/little-unsigned-unit:16,Policy:1/little-unsigned-unit:16).
+-define(WRITE_LINK_POLICY_CP_SIZE, 4).
+-record(write_link_policy_rp, {
+ status,
+ handle
+}).
+-define(write_link_policy_rp_bin(Status,Handle),Status:1/unsigned-unit:8,Handle:1/little-unsigned-unit:16).
+-define(WRITE_LINK_POLICY_RP_SIZE, 3).
+-define(OCF_READ_DEFAULT_LINK_POLICY, 14).
+-define(OCF_WRITE_DEFAULT_LINK_POLICY, 15).
+-define(OCF_FLOW_SPECIFICATION, 16).
+-define(OCF_SNIFF_SUBRATING, 17).
+-record(sniff_subrating_cp, {
+ handle,
+ max_latency,
+ min_remote_timeout,
+ min_local_timeout
+}).
+-define(sniff_subrating_cp_bin(Handle,Max_latency,Min_remote_timeout,Min_local_timeout),Handle:1/little-unsigned-unit:16,Max_latency:1/little-unsigned-unit:16,Min_remote_timeout:1/little-unsigned-unit:16,Min_local_timeout:1/little-unsigned-unit:16).
+-define(SNIFF_SUBRATING_CP_SIZE, 8).
+-define(OGF_HOST_CTL, 3).
+-define(OCF_SET_EVENT_MASK, 1).
+-record(set_event_mask_cp, {
+ mask
+}).
+-define(set_event_mask_cp_bin(Mask),Mask:8/unit:8-binary).
+-define(SET_EVENT_MASK_CP_SIZE, 8).
+-define(OCF_RESET, 3).
+-define(OCF_SET_EVENT_FLT, 5).
+-record(set_event_flt_cp, {
+ flt_type,
+ cond_type,
+ condition
+}).
+-define(set_event_flt_cp_bin(Flt_type,Cond_type,Condition),Flt_type:1/unsigned-unit:8,Cond_type:1/unsigned-unit:8,Condition:0/unit:8-binary).
+-define(SET_EVENT_FLT_CP_SIZE, 2).
+-define(FLT_CLEAR_ALL, 0).
+-define(FLT_INQ_RESULT, 1).
+-define(FLT_CONN_SETUP, 2).
+-define(INQ_RESULT_RETURN_ALL, 0).
+-define(INQ_RESULT_RETURN_CLASS, 1).
+-define(INQ_RESULT_RETURN_BDADDR, 2).
+-define(CONN_SETUP_ALLOW_ALL, 0).
+-define(CONN_SETUP_ALLOW_CLASS, 1).
+-define(CONN_SETUP_ALLOW_BDADDR, 2).
+-define(CONN_SETUP_AUTO_OFF, 1).
+-define(CONN_SETUP_AUTO_ON, 2).
+-define(OCF_FLUSH, 8).
+-define(OCF_READ_PIN_TYPE, 9).
+-record(read_pin_type_rp, {
+ status,
+ pin_type
+}).
+-define(read_pin_type_rp_bin(Status,Pin_type),Status:1/unsigned-unit:8,Pin_type:1/unsigned-unit:8).
+-define(READ_PIN_TYPE_RP_SIZE, 2).
+-define(OCF_WRITE_PIN_TYPE, 10).
+-record(write_pin_type_cp, {
+ pin_type
+}).
+-define(write_pin_type_cp_bin(Pin_type),Pin_type:1/unsigned-unit:8).
+-define(WRITE_PIN_TYPE_CP_SIZE, 1).
+-define(OCF_CREATE_NEW_UNIT_KEY, 11).
+-define(OCF_READ_STORED_LINK_KEY, 13).
+-record(read_stored_link_key_cp, {
+ bdaddr,
+ read_all
+}).
+-define(read_stored_link_key_cp_bin(Bdaddr,Read_all),Bdaddr:6/unit:8-binary,Read_all:1/unsigned-unit:8).
+-define(READ_STORED_LINK_KEY_CP_SIZE, 7).
+-record(read_stored_link_key_rp, {
+ status,
+ max_keys,
+ num_keys
+}).
+-define(read_stored_link_key_rp_bin(Status,Max_keys,Num_keys),Status:1/unsigned-unit:8,Max_keys:1/little-unsigned-unit:16,Num_keys:1/little-unsigned-unit:16).
+-define(READ_STORED_LINK_KEY_RP_SIZE, 5).
+-define(OCF_WRITE_STORED_LINK_KEY, 17).
+-record(write_stored_link_key_cp, {
+ num_keys
+}).
+-define(write_stored_link_key_cp_bin(Num_keys),Num_keys:1/unsigned-unit:8).
+-define(WRITE_STORED_LINK_KEY_CP_SIZE, 1).
+-record(write_stored_link_key_rp, {
+ status,
+ num_keys
+}).
+-define(write_stored_link_key_rp_bin(Status,Num_keys),Status:1/unsigned-unit:8,Num_keys:1/unsigned-unit:8).
+-define(READ_WRITE_LINK_KEY_RP_SIZE, 2).
+-define(OCF_DELETE_STORED_LINK_KEY, 18).
+-record(delete_stored_link_key_cp, {
+ bdaddr,
+ delete_all
+}).
+-define(delete_stored_link_key_cp_bin(Bdaddr,Delete_all),Bdaddr:6/unit:8-binary,Delete_all:1/unsigned-unit:8).
+-define(DELETE_STORED_LINK_KEY_CP_SIZE, 7).
+-record(delete_stored_link_key_rp, {
+ status,
+ num_keys
+}).
+-define(delete_stored_link_key_rp_bin(Status,Num_keys),Status:1/unsigned-unit:8,Num_keys:1/little-unsigned-unit:16).
+-define(DELETE_STORED_LINK_KEY_RP_SIZE, 3).
+-define(HCI_MAX_NAME_LENGTH, 248).
+-define(OCF_CHANGE_LOCAL_NAME, 19).
+-record(change_local_name_cp, {
+ name
+}).
+-define(change_local_name_cp_bin(Name),Name:(?HCI_MAX_NAME_LENGTH)/unit:8-binary).
+-define(CHANGE_LOCAL_NAME_CP_SIZE, 248).
+-define(OCF_READ_LOCAL_NAME, 20).
+-record(read_local_name_rp, {
+ status,
+ name
+}).
+-define(read_local_name_rp_bin(Status,Name),Status:1/unsigned-unit:8,Name:(?HCI_MAX_NAME_LENGTH)/unit:8-binary).
+-define(READ_LOCAL_NAME_RP_SIZE, 249).
+-define(OCF_READ_CONN_ACCEPT_TIMEOUT, 21).
+-record(read_conn_accept_timeout_rp, {
+ status,
+ timeout
+}).
+-define(read_conn_accept_timeout_rp_bin(Status,Timeout),Status:1/unsigned-unit:8,Timeout:1/little-unsigned-unit:16).
+-define(READ_CONN_ACCEPT_TIMEOUT_RP_SIZE, 3).
+-define(OCF_WRITE_CONN_ACCEPT_TIMEOUT, 22).
+-record(write_conn_accept_timeout_cp, {
+ timeout
+}).
+-define(write_conn_accept_timeout_cp_bin(Timeout),Timeout:1/little-unsigned-unit:16).
+-define(WRITE_CONN_ACCEPT_TIMEOUT_CP_SIZE, 2).
+-define(OCF_READ_PAGE_TIMEOUT, 23).
+-record(read_page_timeout_rp, {
+ status,
+ timeout
+}).
+-define(read_page_timeout_rp_bin(Status,Timeout),Status:1/unsigned-unit:8,Timeout:1/little-unsigned-unit:16).
+-define(READ_PAGE_TIMEOUT_RP_SIZE, 3).
+-define(OCF_WRITE_PAGE_TIMEOUT, 24).
+-record(write_page_timeout_cp, {
+ timeout
+}).
+-define(write_page_timeout_cp_bin(Timeout),Timeout:1/little-unsigned-unit:16).
+-define(WRITE_PAGE_TIMEOUT_CP_SIZE, 2).
+-define(OCF_READ_SCAN_ENABLE, 25).
+-record(read_scan_enable_rp, {
+ status,
+ enable
+}).
+-define(read_scan_enable_rp_bin(Status,Enable),Status:1/unsigned-unit:8,Enable:1/unsigned-unit:8).
+-define(READ_SCAN_ENABLE_RP_SIZE, 2).
+-define(OCF_WRITE_SCAN_ENABLE, 26).
+-define(OCF_READ_PAGE_ACTIVITY, 27).
+-record(read_page_activity_rp, {
+ status,
+ interval,
+ window
+}).
+-define(read_page_activity_rp_bin(Status,Interval,Window),Status:1/unsigned-unit:8,Interval:1/little-unsigned-unit:16,Window:1/little-unsigned-unit:16).
+-define(READ_PAGE_ACTIVITY_RP_SIZE, 5).
+-define(OCF_WRITE_PAGE_ACTIVITY, 28).
+-record(write_page_activity_cp, {
+ interval,
+ window
+}).
+-define(write_page_activity_cp_bin(Interval,Window),Interval:1/little-unsigned-unit:16,Window:1/little-unsigned-unit:16).
+-define(WRITE_PAGE_ACTIVITY_CP_SIZE, 4).
+-define(OCF_READ_INQ_ACTIVITY, 29).
+-record(read_inq_activity_rp, {
+ status,
+ interval,
+ window
+}).
+-define(read_inq_activity_rp_bin(Status,Interval,Window),Status:1/unsigned-unit:8,Interval:1/little-unsigned-unit:16,Window:1/little-unsigned-unit:16).
+-define(READ_INQ_ACTIVITY_RP_SIZE, 5).
+-define(OCF_WRITE_INQ_ACTIVITY, 30).
+-record(write_inq_activity_cp, {
+ interval,
+ window
+}).
+-define(write_inq_activity_cp_bin(Interval,Window),Interval:1/little-unsigned-unit:16,Window:1/little-unsigned-unit:16).
+-define(WRITE_INQ_ACTIVITY_CP_SIZE, 4).
+-define(OCF_READ_AUTH_ENABLE, 31).
+-define(OCF_WRITE_AUTH_ENABLE, 32).
+-define(AUTH_DISABLED, 0).
+-define(AUTH_ENABLED, 1).
+-define(OCF_READ_ENCRYPT_MODE, 33).
+-define(OCF_WRITE_ENCRYPT_MODE, 34).
+-define(ENCRYPT_DISABLED, 0).
+-define(ENCRYPT_P2P, 1).
+-define(ENCRYPT_BOTH, 2).
+-define(OCF_READ_CLASS_OF_DEV, 35).
+-record(read_class_of_dev_rp, {
+ status,
+ dev_class
+}).
+-define(read_class_of_dev_rp_bin(Status,Dev_class),Status:1/unsigned-unit:8,Dev_class:3/unit:8-binary).
+-define(READ_CLASS_OF_DEV_RP_SIZE, 4).
+-define(OCF_WRITE_CLASS_OF_DEV, 36).
+-record(write_class_of_dev_cp, {
+ dev_class
+}).
+-define(write_class_of_dev_cp_bin(Dev_class),Dev_class:3/unit:8-binary).
+-define(WRITE_CLASS_OF_DEV_CP_SIZE, 3).
+-define(OCF_READ_VOICE_SETTING, 37).
+-record(read_voice_setting_rp, {
+ status,
+ voice_setting
+}).
+-define(read_voice_setting_rp_bin(Status,Voice_setting),Status:1/unsigned-unit:8,Voice_setting:1/little-unsigned-unit:16).
+-define(READ_VOICE_SETTING_RP_SIZE, 3).
+-define(OCF_WRITE_VOICE_SETTING, 38).
+-record(write_voice_setting_cp, {
+ voice_setting
+}).
+-define(write_voice_setting_cp_bin(Voice_setting),Voice_setting:1/little-unsigned-unit:16).
+-define(WRITE_VOICE_SETTING_CP_SIZE, 2).
+-define(OCF_READ_AUTOMATIC_FLUSH_TIMEOUT, 39).
+-define(OCF_WRITE_AUTOMATIC_FLUSH_TIMEOUT, 40).
+-define(OCF_READ_NUM_BROADCAST_RETRANS, 41).
+-define(OCF_WRITE_NUM_BROADCAST_RETRANS, 42).
+-define(OCF_READ_HOLD_MODE_ACTIVITY, 43).
+-define(OCF_WRITE_HOLD_MODE_ACTIVITY, 44).
+-define(OCF_READ_TRANSMIT_POWER_LEVEL, 45).
+-record(read_transmit_power_level_cp, {
+ handle,
+ type
+}).
+-define(read_transmit_power_level_cp_bin(Handle,Type),Handle:1/little-unsigned-unit:16,Type:1/unsigned-unit:8).
+-define(READ_TRANSMIT_POWER_LEVEL_CP_SIZE, 3).
+-record(read_transmit_power_level_rp, {
+ status,
+ handle,
+ level
+}).
+-define(read_transmit_power_level_rp_bin(Status,Handle,Level),Status:1/unsigned-unit:8,Handle:1/little-unsigned-unit:16,Level:1/signed-unit:8).
+-define(READ_TRANSMIT_POWER_LEVEL_RP_SIZE, 4).
+-define(OCF_READ_SYNC_FLOW_ENABLE, 46).
+-define(OCF_WRITE_SYNC_FLOW_ENABLE, 47).
+-define(OCF_SET_CONTROLLER_TO_HOST_FC, 49).
+-define(OCF_HOST_BUFFER_SIZE, 51).
+-record(host_buffer_size_cp, {
+ acl_mtu,
+ sco_mtu,
+ acl_max_pkt,
+ sco_max_pkt
+}).
+-define(host_buffer_size_cp_bin(Acl_mtu,Sco_mtu,Acl_max_pkt,Sco_max_pkt),Acl_mtu:1/little-unsigned-unit:16,Sco_mtu:1/unsigned-unit:8,Acl_max_pkt:1/little-unsigned-unit:16,Sco_max_pkt:1/little-unsigned-unit:16).
+-define(HOST_BUFFER_SIZE_CP_SIZE, 7).
+-define(OCF_HOST_NUM_COMP_PKTS, 53).
+-record(host_num_comp_pkts_cp, {
+ num_hndl
+}).
+-define(host_num_comp_pkts_cp_bin(Num_hndl),Num_hndl:1/unsigned-unit:8).
+-define(HOST_NUM_COMP_PKTS_CP_SIZE, 1).
+-define(OCF_READ_LINK_SUPERVISION_TIMEOUT, 54).
+-record(read_link_supervision_timeout_rp, {
+ status,
+ handle,
+ timeout
+}).
+-define(read_link_supervision_timeout_rp_bin(Status,Handle,Timeout),Status:1/unsigned-unit:8,Handle:1/little-unsigned-unit:16,Timeout:1/little-unsigned-unit:16).
+-define(READ_LINK_SUPERVISION_TIMEOUT_RP_SIZE, 5).
+-define(OCF_WRITE_LINK_SUPERVISION_TIMEOUT, 55).
+-record(write_link_supervision_timeout_cp, {
+ handle,
+ timeout
+}).
+-define(write_link_supervision_timeout_cp_bin(Handle,Timeout),Handle:1/little-unsigned-unit:16,Timeout:1/little-unsigned-unit:16).
+-define(WRITE_LINK_SUPERVISION_TIMEOUT_CP_SIZE, 4).
+-record(write_link_supervision_timeout_rp, {
+ status,
+ handle
+}).
+-define(write_link_supervision_timeout_rp_bin(Status,Handle),Status:1/unsigned-unit:8,Handle:1/little-unsigned-unit:16).
+-define(WRITE_LINK_SUPERVISION_TIMEOUT_RP_SIZE, 3).
+-define(OCF_READ_NUM_SUPPORTED_IAC, 56).
+-define(MAX_IAC_LAP, 64).
+-define(OCF_READ_CURRENT_IAC_LAP, 57).
+-record(read_current_iac_lap_rp, {
+ status,
+ num_current_iac,
+ lap
+}).
+-define(read_current_iac_lap_rp_bin(Status,Num_current_iac,Lap),Status:1/unsigned-unit:8,Num_current_iac:1/unsigned-unit:8,Lap:(3*?MAX_IAC_LAP)/unit:8-binary).
+-define(READ_CURRENT_IAC_LAP_RP_SIZE, 2+3*?MAX_IAC_LAP).
+-define(OCF_WRITE_CURRENT_IAC_LAP, 58).
+-record(write_current_iac_lap_cp, {
+ num_current_iac,
+ lap
+}).
+-define(write_current_iac_lap_cp_bin(Num_current_iac,Lap),Num_current_iac:1/unsigned-unit:8,Lap:(3*?MAX_IAC_LAP)/unit:8-binary).
+-define(WRITE_CURRENT_IAC_LAP_CP_SIZE, 1+3*?MAX_IAC_LAP).
+-define(OCF_READ_PAGE_SCAN_PERIOD_MODE, 59).
+-define(OCF_WRITE_PAGE_SCAN_PERIOD_MODE, 60).
+-define(OCF_READ_PAGE_SCAN_MODE, 61).
+-define(OCF_WRITE_PAGE_SCAN_MODE, 62).
+-define(OCF_SET_AFH_CLASSIFICATION, 63).
+-record(set_afh_classification_cp, {
+ map
+}).
+-define(set_afh_classification_cp_bin(Map),Map:10/unit:8-binary).
+-define(SET_AFH_CLASSIFICATION_CP_SIZE, 10).
+-record(set_afh_classification_rp, {
+ status
+}).
+-define(set_afh_classification_rp_bin(Status),Status:1/unsigned-unit:8).
+-define(SET_AFH_CLASSIFICATION_RP_SIZE, 1).
+-define(OCF_READ_INQUIRY_SCAN_TYPE, 66).
+-record(read_inquiry_scan_type_rp, {
+ status,
+ type
+}).
+-define(read_inquiry_scan_type_rp_bin(Status,Type),Status:1/unsigned-unit:8,Type:1/unsigned-unit:8).
+-define(READ_INQUIRY_SCAN_TYPE_RP_SIZE, 2).
+-define(OCF_WRITE_INQUIRY_SCAN_TYPE, 67).
+-record(write_inquiry_scan_type_cp, {
+ type
+}).
+-define(write_inquiry_scan_type_cp_bin(Type),Type:1/unsigned-unit:8).
+-define(WRITE_INQUIRY_SCAN_TYPE_CP_SIZE, 1).
+-record(write_inquiry_scan_type_rp, {
+ status
+}).
+-define(write_inquiry_scan_type_rp_bin(Status),Status:1/unsigned-unit:8).
+-define(WRITE_INQUIRY_SCAN_TYPE_RP_SIZE, 1).
+-define(OCF_READ_INQUIRY_MODE, 68).
+-record(read_inquiry_mode_rp, {
+ status,
+ mode
+}).
+-define(read_inquiry_mode_rp_bin(Status,Mode),Status:1/unsigned-unit:8,Mode:1/unsigned-unit:8).
+-define(READ_INQUIRY_MODE_RP_SIZE, 2).
+-define(OCF_WRITE_INQUIRY_MODE, 69).
+-record(write_inquiry_mode_cp, {
+ mode
+}).
+-define(write_inquiry_mode_cp_bin(Mode),Mode:1/unsigned-unit:8).
+-define(WRITE_INQUIRY_MODE_CP_SIZE, 1).
+-record(write_inquiry_mode_rp, {
+ status
+}).
+-define(write_inquiry_mode_rp_bin(Status),Status:1/unsigned-unit:8).
+-define(WRITE_INQUIRY_MODE_RP_SIZE, 1).
+-define(OCF_READ_PAGE_SCAN_TYPE, 70).
+-define(OCF_WRITE_PAGE_SCAN_TYPE, 71).
+-define(PAGE_SCAN_TYPE_STANDARD, 0).
+-define(PAGE_SCAN_TYPE_INTERLACED, 1).
+-define(OCF_READ_AFH_MODE, 72).
+-record(read_afh_mode_rp, {
+ status,
+ mode
+}).
+-define(read_afh_mode_rp_bin(Status,Mode),Status:1/unsigned-unit:8,Mode:1/unsigned-unit:8).
+-define(READ_AFH_MODE_RP_SIZE, 2).
+-define(OCF_WRITE_AFH_MODE, 73).
+-record(write_afh_mode_cp, {
+ mode
+}).
+-define(write_afh_mode_cp_bin(Mode),Mode:1/unsigned-unit:8).
+-define(WRITE_AFH_MODE_CP_SIZE, 1).
+-record(write_afh_mode_rp, {
+ status
+}).
+-define(write_afh_mode_rp_bin(Status),Status:1/unsigned-unit:8).
+-define(WRITE_AFH_MODE_RP_SIZE, 1).
+-define(HCI_MAX_EIR_LENGTH, 240).
+-define(OCF_READ_EXT_INQUIRY_RESPONSE, 81).
+-record(read_ext_inquiry_response_rp, {
+ status,
+ fec,
+ data
+}).
+-define(read_ext_inquiry_response_rp_bin(Status,Fec,Data),Status:1/unsigned-unit:8,Fec:1/unsigned-unit:8,Data:(?HCI_MAX_EIR_LENGTH)/unit:8-binary).
+-define(READ_EXT_INQUIRY_RESPONSE_RP_SIZE, 242).
+-define(OCF_WRITE_EXT_INQUIRY_RESPONSE, 82).
+-record(write_ext_inquiry_response_cp, {
+ fec,
+ data
+}).
+-define(write_ext_inquiry_response_cp_bin(Fec,Data),Fec:1/unsigned-unit:8,Data:(?HCI_MAX_EIR_LENGTH)/unit:8-binary).
+-define(WRITE_EXT_INQUIRY_RESPONSE_CP_SIZE, 241).
+-record(write_ext_inquiry_response_rp, {
+ status
+}).
+-define(write_ext_inquiry_response_rp_bin(Status),Status:1/unsigned-unit:8).
+-define(WRITE_EXT_INQUIRY_RESPONSE_RP_SIZE, 1).
+-define(OCF_REFRESH_ENCRYPTION_KEY, 83).
+-record(refresh_encryption_key_cp, {
+ handle
+}).
+-define(refresh_encryption_key_cp_bin(Handle),Handle:1/little-unsigned-unit:16).
+-define(REFRESH_ENCRYPTION_KEY_CP_SIZE, 2).
+-record(refresh_encryption_key_rp, {
+ status
+}).
+-define(refresh_encryption_key_rp_bin(Status),Status:1/unsigned-unit:8).
+-define(REFRESH_ENCRYPTION_KEY_RP_SIZE, 1).
+-define(OCF_READ_SIMPLE_PAIRING_MODE, 85).
+-record(read_simple_pairing_mode_rp, {
+ status,
+ mode
+}).
+-define(read_simple_pairing_mode_rp_bin(Status,Mode),Status:1/unsigned-unit:8,Mode:1/unsigned-unit:8).
+-define(READ_SIMPLE_PAIRING_MODE_RP_SIZE, 2).
+-define(OCF_WRITE_SIMPLE_PAIRING_MODE, 86).
+-record(write_simple_pairing_mode_cp, {
+ mode
+}).
+-define(write_simple_pairing_mode_cp_bin(Mode),Mode:1/unsigned-unit:8).
+-define(WRITE_SIMPLE_PAIRING_MODE_CP_SIZE, 1).
+-record(write_simple_pairing_mode_rp, {
+ status
+}).
+-define(write_simple_pairing_mode_rp_bin(Status),Status:1/unsigned-unit:8).
+-define(WRITE_SIMPLE_PAIRING_MODE_RP_SIZE, 1).
+-define(OCF_READ_LOCAL_OOB_DATA, 87).
+-record(read_local_oob_data_rp, {
+ status,
+ hash,
+ randomizer
+}).
+-define(read_local_oob_data_rp_bin(Status,Hash,Randomizer),Status:1/unsigned-unit:8,Hash:16/unit:8-binary,Randomizer:16/unit:8-binary).
+-define(READ_LOCAL_OOB_DATA_RP_SIZE, 33).
+-define(OCF_READ_INQ_RESPONSE_TX_POWER_LEVEL, 88).
+-record(read_inq_response_tx_power_level_rp, {
+ status,
+ level
+}).
+-define(read_inq_response_tx_power_level_rp_bin(Status,Level),Status:1/unsigned-unit:8,Level:1/signed-unit:8).
+-define(READ_INQ_RESPONSE_TX_POWER_LEVEL_RP_SIZE, 2).
+-define(OCF_READ_INQUIRY_TRANSMIT_POWER_LEVEL, 88).
+-record(read_inquiry_transmit_power_level_rp, {
+ status,
+ level
+}).
+-define(read_inquiry_transmit_power_level_rp_bin(Status,Level),Status:1/unsigned-unit:8,Level:1/signed-unit:8).
+-define(READ_INQUIRY_TRANSMIT_POWER_LEVEL_RP_SIZE, 2).
+-define(OCF_WRITE_INQUIRY_TRANSMIT_POWER_LEVEL, 89).
+-record(write_inquiry_transmit_power_level_cp, {
+ level
+}).
+-define(write_inquiry_transmit_power_level_cp_bin(Level),Level:1/signed-unit:8).
+-define(WRITE_INQUIRY_TRANSMIT_POWER_LEVEL_CP_SIZE, 1).
+-record(write_inquiry_transmit_power_level_rp, {
+ status
+}).
+-define(write_inquiry_transmit_power_level_rp_bin(Status),Status:1/unsigned-unit:8).
+-define(WRITE_INQUIRY_TRANSMIT_POWER_LEVEL_RP_SIZE, 1).
+-define(OCF_READ_DEFAULT_ERROR_DATA_REPORTING, 90).
+-record(read_default_error_data_reporting_rp, {
+ status,
+ reporting
+}).
+-define(read_default_error_data_reporting_rp_bin(Status,Reporting),Status:1/unsigned-unit:8,Reporting:1/unsigned-unit:8).
+-define(READ_DEFAULT_ERROR_DATA_REPORTING_RP_SIZE, 2).
+-define(OCF_WRITE_DEFAULT_ERROR_DATA_REPORTING, 91).
+-record(write_default_error_data_reporting_cp, {
+ reporting
+}).
+-define(write_default_error_data_reporting_cp_bin(Reporting),Reporting:1/unsigned-unit:8).
+-define(WRITE_DEFAULT_ERROR_DATA_REPORTING_CP_SIZE, 1).
+-record(write_default_error_data_reporting_rp, {
+ status
+}).
+-define(write_default_error_data_reporting_rp_bin(Status),Status:1/unsigned-unit:8).
+-define(WRITE_DEFAULT_ERROR_DATA_REPORTING_RP_SIZE, 1).
+-define(OCF_ENHANCED_FLUSH, 95).
+-record(enhanced_flush_cp, {
+ handle,
+ type
+}).
+-define(enhanced_flush_cp_bin(Handle,Type),Handle:1/little-unsigned-unit:16,Type:1/unsigned-unit:8).
+-define(ENHANCED_FLUSH_CP_SIZE, 3).
+-define(OCF_SEND_KEYPRESS_NOTIFY, 96).
+-record(send_keypress_notify_cp, {
+ bdaddr,
+ type
+}).
+-define(send_keypress_notify_cp_bin(Bdaddr,Type),Bdaddr:6/unit:8-binary,Type:1/unsigned-unit:8).
+-define(SEND_KEYPRESS_NOTIFY_CP_SIZE, 7).
+-record(send_keypress_notify_rp, {
+ status
+}).
+-define(send_keypress_notify_rp_bin(Status),Status:1/unsigned-unit:8).
+-define(SEND_KEYPRESS_NOTIFY_RP_SIZE, 1).
+-define(OCF_READ_LOGICAL_LINK_ACCEPT_TIMEOUT, 97).
+-record(read_log_link_accept_timeout_rp, {
+ status,
+ timeout
+}).
+-define(read_log_link_accept_timeout_rp_bin(Status,Timeout),Status:1/unsigned-unit:8,Timeout:1/little-unsigned-unit:16).
+-define(READ_LOGICAL_LINK_ACCEPT_TIMEOUT_RP_SIZE, 3).
+-define(OCF_WRITE_LOGICAL_LINK_ACCEPT_TIMEOUT, 98).
+-record(write_log_link_accept_timeout_cp, {
+ timeout
+}).
+-define(write_log_link_accept_timeout_cp_bin(Timeout),Timeout:1/little-unsigned-unit:16).
+-define(WRITE_LOGICAL_LINK_ACCEPT_TIMEOUT_CP_SIZE, 2).
+-define(OCF_SET_EVENT_MASK_PAGE_2, 99).
+-define(OCF_READ_LOCATION_DATA, 100).
+-define(OCF_WRITE_LOCATION_DATA, 101).
+-define(OCF_READ_FLOW_CONTROL_MODE, 102).
+-define(OCF_WRITE_FLOW_CONTROL_MODE, 103).
+-define(OCF_READ_ENHANCED_TRANSMIT_POWER_LEVEL, 104).
+-record(read_enhanced_transmit_power_level_rp, {
+ status,
+ handle,
+ level_gfsk,
+ level_dqpsk,
+ level_8dpsk
+}).
+-define(read_enhanced_transmit_power_level_rp_bin(Status,Handle,Level_gfsk,Level_dqpsk,Level_8dpsk),Status:1/unsigned-unit:8,Handle:1/little-unsigned-unit:16,Level_gfsk:1/signed-unit:8,Level_dqpsk:1/signed-unit:8,Level_8dpsk:1/signed-unit:8).
+-define(READ_ENHANCED_TRANSMIT_POWER_LEVEL_RP_SIZE, 6).
+-define(OCF_READ_BEST_EFFORT_FLUSH_TIMEOUT, 105).
+-record(read_best_effort_flush_timeout_rp, {
+ status,
+ timeout
+}).
+-define(read_best_effort_flush_timeout_rp_bin(Status,Timeout),Status:1/unsigned-unit:8,Timeout:1/little-unsigned-unit:32).
+-define(READ_BEST_EFFORT_FLUSH_TIMEOUT_RP_SIZE, 5).
+-define(OCF_WRITE_BEST_EFFORT_FLUSH_TIMEOUT, 106).
+-record(write_best_effort_flush_timeout_cp, {
+ handle,
+ timeout
+}).
+-define(write_best_effort_flush_timeout_cp_bin(Handle,Timeout),Handle:1/little-unsigned-unit:16,Timeout:1/little-unsigned-unit:32).
+-define(WRITE_BEST_EFFORT_FLUSH_TIMEOUT_CP_SIZE, 6).
+-record(write_best_effort_flush_timeout_rp, {
+ status
+}).
+-define(write_best_effort_flush_timeout_rp_bin(Status),Status:1/unsigned-unit:8).
+-define(WRITE_BEST_EFFORT_FLUSH_TIMEOUT_RP_SIZE, 1).
+-define(OCF_READ_LE_HOST_SUPPORTED, 108).
+-record(read_le_host_supported_rp, {
+ status,
+ le,
+ simul
+}).
+-define(read_le_host_supported_rp_bin(Status,Le,Simul),Status:1/unsigned-unit:8,Le:1/unsigned-unit:8,Simul:1/unsigned-unit:8).
+-define(READ_LE_HOST_SUPPORTED_RP_SIZE, 3).
+-define(OCF_WRITE_LE_HOST_SUPPORTED, 109).
+-record(write_le_host_supported_cp, {
+ le,
+ simul
+}).
+-define(write_le_host_supported_cp_bin(Le,Simul),Le:1/unsigned-unit:8,Simul:1/unsigned-unit:8).
+-define(WRITE_LE_HOST_SUPPORTED_CP_SIZE, 2).
+-define(OGF_INFO_PARAM, 4).
+-define(OCF_READ_LOCAL_VERSION, 1).
+-record(read_local_version_rp, {
+ status,
+ hci_ver,
+ hci_rev,
+ lmp_ver,
+ manufacturer,
+ lmp_subver
+}).
+-define(read_local_version_rp_bin(Status,Hci_ver,Hci_rev,Lmp_ver,Manufacturer,Lmp_subver),Status:1/unsigned-unit:8,Hci_ver:1/unsigned-unit:8,Hci_rev:1/little-unsigned-unit:16,Lmp_ver:1/unsigned-unit:8,Manufacturer:1/little-unsigned-unit:16,Lmp_subver:1/little-unsigned-unit:16).
+-define(READ_LOCAL_VERSION_RP_SIZE, 9).
+-define(OCF_READ_LOCAL_COMMANDS, 2).
+-record(read_local_commands_rp, {
+ status,
+ commands
+}).
+-define(read_local_commands_rp_bin(Status,Commands),Status:1/unsigned-unit:8,Commands:64/unit:8-binary).
+-define(READ_LOCAL_COMMANDS_RP_SIZE, 65).
+-define(OCF_READ_LOCAL_FEATURES, 3).
+-record(read_local_features_rp, {
+ status,
+ features
+}).
+-define(read_local_features_rp_bin(Status,Features),Status:1/unsigned-unit:8,Features:8/unit:8-binary).
+-define(READ_LOCAL_FEATURES_RP_SIZE, 9).
+-define(OCF_READ_LOCAL_EXT_FEATURES, 4).
+-record(read_local_ext_features_cp, {
+ page_num
+}).
+-define(read_local_ext_features_cp_bin(Page_num),Page_num:1/unsigned-unit:8).
+-define(READ_LOCAL_EXT_FEATURES_CP_SIZE, 1).
+-record(read_local_ext_features_rp, {
+ status,
+ page_num,
+ max_page_num,
+ features
+}).
+-define(read_local_ext_features_rp_bin(Status,Page_num,Max_page_num,Features),Status:1/unsigned-unit:8,Page_num:1/unsigned-unit:8,Max_page_num:1/unsigned-unit:8,Features:8/unit:8-binary).
+-define(READ_LOCAL_EXT_FEATURES_RP_SIZE, 11).
+-define(OCF_READ_BUFFER_SIZE, 5).
+-record(read_buffer_size_rp, {
+ status,
+ acl_mtu,
+ sco_mtu,
+ acl_max_pkt,
+ sco_max_pkt
+}).
+-define(read_buffer_size_rp_bin(Status,Acl_mtu,Sco_mtu,Acl_max_pkt,Sco_max_pkt),Status:1/unsigned-unit:8,Acl_mtu:1/little-unsigned-unit:16,Sco_mtu:1/unsigned-unit:8,Acl_max_pkt:1/little-unsigned-unit:16,Sco_max_pkt:1/little-unsigned-unit:16).
+-define(READ_BUFFER_SIZE_RP_SIZE, 8).
+-define(OCF_READ_COUNTRY_CODE, 7).
+-define(OCF_READ_BD_ADDR, 9).
+-record(read_bd_addr_rp, {
+ status,
+ bdaddr
+}).
+-define(read_bd_addr_rp_bin(Status,Bdaddr),Status:1/unsigned-unit:8,Bdaddr:6/unit:8-binary).
+-define(READ_BD_ADDR_RP_SIZE, 7).
+-define(OGF_STATUS_PARAM, 5).
+-define(OCF_READ_FAILED_CONTACT_COUNTER, 1).
+-record(read_failed_contact_counter_rp, {
+ status,
+ handle,
+ counter
+}).
+-define(read_failed_contact_counter_rp_bin(Status,Handle,Counter),Status:1/unsigned-unit:8,Handle:1/little-unsigned-unit:16,Counter:1/unsigned-unit:8).
+-define(READ_FAILED_CONTACT_COUNTER_RP_SIZE, 4).
+-define(OCF_RESET_FAILED_CONTACT_COUNTER, 2).
+-record(reset_failed_contact_counter_rp, {
+ status,
+ handle
+}).
+-define(reset_failed_contact_counter_rp_bin(Status,Handle),Status:1/unsigned-unit:8,Handle:1/little-unsigned-unit:16).
+-define(RESET_FAILED_CONTACT_COUNTER_RP_SIZE, 4).
+-define(OCF_READ_LINK_QUALITY, 3).
+-record(read_link_quality_rp, {
+ status,
+ handle,
+ link_quality
+}).
+-define(read_link_quality_rp_bin(Status,Handle,Link_quality),Status:1/unsigned-unit:8,Handle:1/little-unsigned-unit:16,Link_quality:1/unsigned-unit:8).
+-define(READ_LINK_QUALITY_RP_SIZE, 4).
+-define(OCF_READ_RSSI, 5).
+-record(read_rssi_rp, {
+ status,
+ handle,
+ rssi
+}).
+-define(read_rssi_rp_bin(Status,Handle,Rssi),Status:1/unsigned-unit:8,Handle:1/little-unsigned-unit:16,Rssi:1/signed-unit:8).
+-define(READ_RSSI_RP_SIZE, 4).
+-define(OCF_READ_AFH_MAP, 6).
+-record(read_afh_map_rp, {
+ status,
+ handle,
+ mode,
+ map
+}).
+-define(read_afh_map_rp_bin(Status,Handle,Mode,Map),Status:1/unsigned-unit:8,Handle:1/little-unsigned-unit:16,Mode:1/unsigned-unit:8,Map:10/unit:8-binary).
+-define(READ_AFH_MAP_RP_SIZE, 14).
+-define(OCF_READ_CLOCK, 7).
+-record(read_clock_cp, {
+ handle,
+ which_clock
+}).
+-define(read_clock_cp_bin(Handle,Which_clock),Handle:1/little-unsigned-unit:16,Which_clock:1/unsigned-unit:8).
+-define(READ_CLOCK_CP_SIZE, 3).
+-record(read_clock_rp, {
+ status,
+ handle,
+ clock,
+ accuracy
+}).
+-define(read_clock_rp_bin(Status,Handle,Clock,Accuracy),Status:1/unsigned-unit:8,Handle:1/little-unsigned-unit:16,Clock:1/little-unsigned-unit:32,Accuracy:1/little-unsigned-unit:16).
+-define(READ_CLOCK_RP_SIZE, 9).
+-define(OCF_READ_LOCAL_AMP_INFO, 9).
+-record(read_local_amp_info_rp, {
+ status,
+ amp_status,
+ total_bandwidth,
+ max_guaranteed_bandwidth,
+ min_latency,
+ max_pdu_size,
+ controller_type,
+ pal_caps,
+ max_amp_assoc_length,
+ max_flush_timeout,
+ best_effort_flush_timeout
+}).
+-define(read_local_amp_info_rp_bin(Status,Amp_status,Total_bandwidth,Max_guaranteed_bandwidth,Min_latency,Max_pdu_size,Controller_type,Pal_caps,Max_amp_assoc_length,Max_flush_timeout,Best_effort_flush_timeout),Status:1/unsigned-unit:8,Amp_status:1/unsigned-unit:8,Total_bandwidth:1/little-unsigned-unit:32,Max_guaranteed_bandwidth:1/little-unsigned-unit:32,Min_latency:1/little-unsigned-unit:32,Max_pdu_size:1/little-unsigned-unit:32,Controller_type:1/unsigned-unit:8,Pal_caps:1/little-unsigned-unit:16,Max_amp_assoc_length:1/little-unsigned-unit:16,Max_flush_timeout:1/little-unsigned-unit:32,Best_effort_flush_timeout:1/little-unsigned-unit:32).
+-define(READ_LOCAL_AMP_INFO_RP_SIZE, 31).
+-define(OCF_READ_LOCAL_AMP_ASSOC, 10).
+-record(read_local_amp_assoc_cp, {
+ handle,
+ len_so_far,
+ max_len
+}).
+-define(read_local_amp_assoc_cp_bin(Handle,Len_so_far,Max_len),Handle:1/unsigned-unit:8,Len_so_far:1/little-unsigned-unit:16,Max_len:1/little-unsigned-unit:16).
+-record(read_local_amp_assoc_rp, {
+ status,
+ handle,
+ rem_len,
+ frag
+}).
+-define(read_local_amp_assoc_rp_bin(Status,Handle,Rem_len,Frag),Status:1/unsigned-unit:8,Handle:1/unsigned-unit:8,Rem_len:1/little-unsigned-unit:16,Frag:0/unit:8-binary).
+-define(OCF_WRITE_REMOTE_AMP_ASSOC, 11).
+-record(write_remote_amp_assoc_cp, {
+ handle,
+ length_so_far,
+ assoc_length,
+ fragment
+}).
+-define(write_remote_amp_assoc_cp_bin(Handle,Length_so_far,Assoc_length,Fragment),Handle:1/unsigned-unit:8,Length_so_far:1/little-unsigned-unit:16,Assoc_length:1/little-unsigned-unit:16,Fragment:(?HCI_MAX_NAME_LENGTH)/unit:8-binary).
+-define(WRITE_REMOTE_AMP_ASSOC_CP_SIZE, 253).
+-record(write_remote_amp_assoc_rp, {
+ status,
+ handle
+}).
+-define(write_remote_amp_assoc_rp_bin(Status,Handle),Status:1/unsigned-unit:8,Handle:1/unsigned-unit:8).
+-define(WRITE_REMOTE_AMP_ASSOC_RP_SIZE, 2).
+-define(OGF_TESTING_CMD, 62).
+-define(OCF_READ_LOOPBACK_MODE, 1).
+-define(OCF_WRITE_LOOPBACK_MODE, 2).
+-define(OCF_ENABLE_DEVICE_UNDER_TEST_MODE, 3).
+-define(OCF_WRITE_SIMPLE_PAIRING_DEBUG_MODE, 4).
+-record(write_simple_pairing_debug_mode_cp, {
+ mode
+}).
+-define(write_simple_pairing_debug_mode_cp_bin(Mode),Mode:1/unsigned-unit:8).
+-define(WRITE_SIMPLE_PAIRING_DEBUG_MODE_CP_SIZE, 1).
+-record(write_simple_pairing_debug_mode_rp, {
+ status
+}).
+-define(write_simple_pairing_debug_mode_rp_bin(Status),Status:1/unsigned-unit:8).
+-define(WRITE_SIMPLE_PAIRING_DEBUG_MODE_RP_SIZE, 1).
+-define(OGF_LE_CTL, 8).
+-define(OCF_LE_SET_EVENT_MASK, 1).
+-record(le_set_event_mask_cp, {
+ mask
+}).
+-define(le_set_event_mask_cp_bin(Mask),Mask:8/unit:8-binary).
+-define(LE_SET_EVENT_MASK_CP_SIZE, 8).
+-define(OCF_LE_READ_BUFFER_SIZE, 2).
+-record(le_read_buffer_size_rp, {
+ status,
+ pkt_len,
+ max_pkt
+}).
+-define(le_read_buffer_size_rp_bin(Status,Pkt_len,Max_pkt),Status:1/unsigned-unit:8,Pkt_len:1/little-unsigned-unit:16,Max_pkt:1/unsigned-unit:8).
+-define(LE_READ_BUFFER_SIZE_RP_SIZE, 4).
+-define(OCF_LE_READ_LOCAL_SUPPORTED_FEATURES, 3).
+-record(le_read_local_supported_features_rp, {
+ status,
+ features
+}).
+-define(le_read_local_supported_features_rp_bin(Status,Features),Status:1/unsigned-unit:8,Features:8/unit:8-binary).
+-define(LE_READ_LOCAL_SUPPORTED_FEATURES_RP_SIZE, 9).
+-define(OCF_LE_SET_RANDOM_ADDRESS, 5).
+-record(le_set_random_address_cp, {
+ bdaddr
+}).
+-define(le_set_random_address_cp_bin(Bdaddr),Bdaddr:6/unit:8-binary).
+-define(LE_SET_RANDOM_ADDRESS_CP_SIZE, 6).
+-define(OCF_LE_SET_ADVERTISING_PARAMETERS, 6).
+-record(le_set_advertising_parameters_cp, {
+ min_interval,
+ max_interval,
+ advtype,
+ own_bdaddr_type,
+ direct_bdaddr_type,
+ direct_bdaddr,
+ chan_map,
+ filter
+}).
+-define(le_set_advertising_parameters_cp_bin(Min_interval,Max_interval,Advtype,Own_bdaddr_type,Direct_bdaddr_type,Direct_bdaddr,Chan_map,Filter),Min_interval:1/little-unsigned-unit:16,Max_interval:1/little-unsigned-unit:16,Advtype:1/unsigned-unit:8,Own_bdaddr_type:1/unsigned-unit:8,Direct_bdaddr_type:1/unsigned-unit:8,Direct_bdaddr:6/unit:8-binary,Chan_map:1/unsigned-unit:8,Filter:1/unsigned-unit:8).
+-define(LE_SET_ADVERTISING_PARAMETERS_CP_SIZE, 15).
+-define(OCF_LE_READ_ADVERTISING_CHANNEL_TX_POWER, 7).
+-record(le_read_advertising_channel_tx_power_rp, {
+ status,
+ level
+}).
+-define(le_read_advertising_channel_tx_power_rp_bin(Status,Level),Status:1/unsigned-unit:8,Level:1/unsigned-unit:8).
+-define(LE_READ_ADVERTISING_CHANNEL_TX_POWER_RP_SIZE, 2).
+-define(OCF_LE_SET_ADVERTISING_DATA, 8).
+-record(le_set_advertising_data_cp, {
+ length,
+ data
+}).
+-define(le_set_advertising_data_cp_bin(Length,Data),Length:1/unsigned-unit:8,Data:31/unit:8-binary).
+-define(LE_SET_ADVERTISING_DATA_CP_SIZE, 32).
+-define(OCF_LE_SET_SCAN_RESPONSE_DATA, 9).
+-record(le_set_scan_response_data_cp, {
+ length,
+ data
+}).
+-define(le_set_scan_response_data_cp_bin(Length,Data),Length:1/unsigned-unit:8,Data:31/unit:8-binary).
+-define(LE_SET_SCAN_RESPONSE_DATA_CP_SIZE, 32).
+-define(OCF_LE_SET_ADVERTISE_ENABLE, 10).
+-record(le_set_advertise_enable_cp, {
+ enable
+}).
+-define(le_set_advertise_enable_cp_bin(Enable),Enable:1/unsigned-unit:8).
+-define(LE_SET_ADVERTISE_ENABLE_CP_SIZE, 1).
+-define(OCF_LE_SET_SCAN_PARAMETERS, 11).
+-record(le_set_scan_parameters_cp, {
+ type,
+ interval,
+ window,
+ own_bdaddr_type,
+ filter
+}).
+-define(le_set_scan_parameters_cp_bin(Type,Interval,Window,Own_bdaddr_type,Filter),Type:1/unsigned-unit:8,Interval:1/little-unsigned-unit:16,Window:1/little-unsigned-unit:16,Own_bdaddr_type:1/unsigned-unit:8,Filter:1/unsigned-unit:8).
+-define(LE_SET_SCAN_PARAMETERS_CP_SIZE, 7).
+-define(OCF_LE_SET_SCAN_ENABLE, 12).
+-record(le_set_scan_enable_cp, {
+ enable,
+ filter_dup
+}).
+-define(le_set_scan_enable_cp_bin(Enable,Filter_dup),Enable:1/unsigned-unit:8,Filter_dup:1/unsigned-unit:8).
+-define(LE_SET_SCAN_ENABLE_CP_SIZE, 2).
+-define(OCF_LE_CREATE_CONN, 13).
+-record(le_create_connection_cp, {
+ interval,
+ window,
+ initiator_filter,
+ peer_bdaddr_type,
+ peer_bdaddr,
+ own_bdaddr_type,
+ min_interval,
+ max_interval,
+ latency,
+ supervision_timeout,
+ min_ce_length,
+ max_ce_length
+}).
+-define(le_create_connection_cp_bin(Interval,Window,Initiator_filter,Peer_bdaddr_type,Peer_bdaddr,Own_bdaddr_type,Min_interval,Max_interval,Latency,Supervision_timeout,Min_ce_length,Max_ce_length),Interval:1/little-unsigned-unit:16,Window:1/little-unsigned-unit:16,Initiator_filter:1/unsigned-unit:8,Peer_bdaddr_type:1/unsigned-unit:8,Peer_bdaddr:6/unit:8-binary,Own_bdaddr_type:1/unsigned-unit:8,Min_interval:1/little-unsigned-unit:16,Max_interval:1/little-unsigned-unit:16,Latency:1/little-unsigned-unit:16,Supervision_timeout:1/little-unsigned-unit:16,Min_ce_length:1/little-unsigned-unit:16,Max_ce_length:1/little-unsigned-unit:16).
+-define(LE_CREATE_CONN_CP_SIZE, 25).
+-define(OCF_LE_CREATE_CONN_CANCEL, 14).
+-define(OCF_LE_READ_WHITE_LIST_SIZE, 15).
+-record(le_read_white_list_size_rp, {
+ status,
+ size
+}).
+-define(le_read_white_list_size_rp_bin(Status,Size),Status:1/unsigned-unit:8,Size:1/unsigned-unit:8).
+-define(LE_READ_WHITE_LIST_SIZE_RP_SIZE, 2).
+-define(OCF_LE_CLEAR_WHITE_LIST, 16).
+-define(OCF_LE_ADD_DEVICE_TO_WHITE_LIST, 17).
+-record(le_add_device_to_white_list_cp, {
+ bdaddr_type,
+ bdaddr
+}).
+-define(le_add_device_to_white_list_cp_bin(Bdaddr_type,Bdaddr),Bdaddr_type:1/unsigned-unit:8,Bdaddr:6/unit:8-binary).
+-define(LE_ADD_DEVICE_TO_WHITE_LIST_CP_SIZE, 7).
+-define(OCF_LE_REMOVE_DEVICE_FROM_WHITE_LIST, 18).
+-record(le_remove_device_from_white_list_cp, {
+ bdaddr_type,
+ bdaddr
+}).
+-define(le_remove_device_from_white_list_cp_bin(Bdaddr_type,Bdaddr),Bdaddr_type:1/unsigned-unit:8,Bdaddr:6/unit:8-binary).
+-define(LE_REMOVE_DEVICE_FROM_WHITE_LIST_CP_SIZE, 7).
+-define(OCF_LE_CONN_UPDATE, 19).
+-record(le_connection_update_cp, {
+ handle,
+ min_interval,
+ max_interval,
+ latency,
+ supervision_timeout,
+ min_ce_length,
+ max_ce_length
+}).
+-define(le_connection_update_cp_bin(Handle,Min_interval,Max_interval,Latency,Supervision_timeout,Min_ce_length,Max_ce_length),Handle:1/little-unsigned-unit:16,Min_interval:1/little-unsigned-unit:16,Max_interval:1/little-unsigned-unit:16,Latency:1/little-unsigned-unit:16,Supervision_timeout:1/little-unsigned-unit:16,Min_ce_length:1/little-unsigned-unit:16,Max_ce_length:1/little-unsigned-unit:16).
+-define(LE_CONN_UPDATE_CP_SIZE, 14).
+-define(OCF_LE_SET_HOST_CHANNEL_CLASSIFICATION, 20).
+-record(le_set_host_channel_classification_cp, {
+ map
+}).
+-define(le_set_host_channel_classification_cp_bin(Map),Map:5/unit:8-binary).
+-define(LE_SET_HOST_CHANNEL_CLASSIFICATION_CP_SIZE, 5).
+-define(OCF_LE_READ_CHANNEL_MAP, 21).
+-record(le_read_channel_map_cp, {
+ handle
+}).
+-define(le_read_channel_map_cp_bin(Handle),Handle:1/little-unsigned-unit:16).
+-define(LE_READ_CHANNEL_MAP_CP_SIZE, 2).
+-record(le_read_channel_map_rp, {
+ status,
+ handle,
+ map
+}).
+-define(le_read_channel_map_rp_bin(Status,Handle,Map),Status:1/unsigned-unit:8,Handle:1/little-unsigned-unit:16,Map:5/unit:8-binary).
+-define(LE_READ_CHANNEL_MAP_RP_SIZE, 8).
+-define(OCF_LE_READ_REMOTE_USED_FEATURES, 22).
+-record(le_read_remote_used_features_cp, {
+ handle
+}).
+-define(le_read_remote_used_features_cp_bin(Handle),Handle:1/little-unsigned-unit:16).
+-define(LE_READ_REMOTE_USED_FEATURES_CP_SIZE, 2).
+-define(OCF_LE_ENCRYPT, 23).
+-record(le_encrypt_cp, {
+ key,
+ plaintext
+}).
+-define(le_encrypt_cp_bin(Key,Plaintext),Key:16/unit:8-binary,Plaintext:16/unit:8-binary).
+-define(LE_ENCRYPT_CP_SIZE, 32).
+-record(le_encrypt_rp, {
+ status,
+ data
+}).
+-define(le_encrypt_rp_bin(Status,Data),Status:1/unsigned-unit:8,Data:16/unit:8-binary).
+-define(LE_ENCRYPT_RP_SIZE, 17).
+-define(OCF_LE_RAND, 24).
+-record(le_rand_rp, {
+ status,
+ random
+}).
+-define(le_rand_rp_bin(Status,Random),Status:1/unsigned-unit:8,Random:1/little-unsigned-unit:64).
+-define(LE_RAND_RP_SIZE, 9).
+-define(OCF_LE_START_ENCRYPTION, 25).
+-record(le_start_encryption_cp, {
+ handle,
+ random,
+ diversifier,
+ key
+}).
+-define(le_start_encryption_cp_bin(Handle,Random,Diversifier,Key),Handle:1/little-unsigned-unit:16,Random:1/little-unsigned-unit:64,Diversifier:1/little-unsigned-unit:16,Key:16/unit:8-binary).
+-define(LE_START_ENCRYPTION_CP_SIZE, 28).
+-define(OCF_LE_LTK_REPLY, 26).
+-record(le_ltk_reply_cp, {
+ handle,
+ key
+}).
+-define(le_ltk_reply_cp_bin(Handle,Key),Handle:1/little-unsigned-unit:16,Key:16/unit:8-binary).
+-define(LE_LTK_REPLY_CP_SIZE, 18).
+-record(le_ltk_reply_rp, {
+ status,
+ handle
+}).
+-define(le_ltk_reply_rp_bin(Status,Handle),Status:1/unsigned-unit:8,Handle:1/little-unsigned-unit:16).
+-define(LE_LTK_REPLY_RP_SIZE, 3).
+-define(OCF_LE_LTK_NEG_REPLY, 27).
+-record(le_ltk_neg_reply_cp, {
+ handle
+}).
+-define(le_ltk_neg_reply_cp_bin(Handle),Handle:1/little-unsigned-unit:16).
+-define(LE_LTK_NEG_REPLY_CP_SIZE, 2).
+-record(le_ltk_neg_reply_rp, {
+ status,
+ handle
+}).
+-define(le_ltk_neg_reply_rp_bin(Status,Handle),Status:1/unsigned-unit:8,Handle:1/little-unsigned-unit:16).
+-define(LE_LTK_NEG_REPLY_RP_SIZE, 3).
+-define(OCF_LE_READ_SUPPORTED_STATES, 28).
+-record(le_read_supported_states_rp, {
+ status,
+ states
+}).
+-define(le_read_supported_states_rp_bin(Status,States),Status:1/unsigned-unit:8,States:1/little-unsigned-unit:64).
+-define(LE_READ_SUPPORTED_STATES_RP_SIZE, 9).
+-define(OCF_LE_RECEIVER_TEST, 29).
+-record(le_receiver_test_cp, {
+ frequency
+}).
+-define(le_receiver_test_cp_bin(Frequency),Frequency:1/unsigned-unit:8).
+-define(LE_RECEIVER_TEST_CP_SIZE, 1).
+-define(OCF_LE_TRANSMITTER_TEST, 30).
+-record(le_transmitter_test_cp, {
+ frequency,
+ length,
+ payload
+}).
+-define(le_transmitter_test_cp_bin(Frequency,Length,Payload),Frequency:1/unsigned-unit:8,Length:1/unsigned-unit:8,Payload:1/unsigned-unit:8).
+-define(LE_TRANSMITTER_TEST_CP_SIZE, 3).
+-define(OCF_LE_TEST_END, 31).
+-record(le_test_end_rp, {
+ status,
+ num_pkts
+}).
+-define(le_test_end_rp_bin(Status,Num_pkts),Status:1/unsigned-unit:8,Num_pkts:1/little-unsigned-unit:16).
+-define(LE_TEST_END_RP_SIZE, 3).
+-define(OGF_VENDOR_CMD, 63).
+-define(EVT_INQUIRY_COMPLETE, 1).
+-define(EVT_INQUIRY_RESULT, 2).
+-record(inquiry_info, {
+ bdaddr,
+ pscan_rep_mode,
+ pscan_period_mode,
+ pscan_mode,
+ dev_class,
+ clock_offset
+}).
+-define(inquiry_info_bin(Bdaddr,Pscan_rep_mode,Pscan_period_mode,Pscan_mode,Dev_class,Clock_offset),Bdaddr:6/unit:8-binary,Pscan_rep_mode:1/unsigned-unit:8,Pscan_period_mode:1/unsigned-unit:8,Pscan_mode:1/unsigned-unit:8,Dev_class:3/unit:8-binary,Clock_offset:1/little-unsigned-unit:16).
+-define(INQUIRY_INFO_SIZE, 14).
+-define(EVT_CONN_COMPLETE, 3).
+-record(evt_conn_complete, {
+ status,
+ handle,
+ bdaddr,
+ link_type,
+ encr_mode
+}).
+-define(evt_conn_complete_bin(Status,Handle,Bdaddr,Link_type,Encr_mode),Status:1/unsigned-unit:8,Handle:1/little-unsigned-unit:16,Bdaddr:6/unit:8-binary,Link_type:1/unsigned-unit:8,Encr_mode:1/unsigned-unit:8).
+-define(EVT_CONN_COMPLETE_SIZE, 13).
+-define(EVT_CONN_REQUEST, 4).
+-record(evt_conn_request, {
+ bdaddr,
+ dev_class,
+ link_type
+}).
+-define(evt_conn_request_bin(Bdaddr,Dev_class,Link_type),Bdaddr:6/unit:8-binary,Dev_class:3/unit:8-binary,Link_type:1/unsigned-unit:8).
+-define(EVT_CONN_REQUEST_SIZE, 10).
+-define(EVT_DISCONN_COMPLETE, 5).
+-record(evt_disconn_complete, {
+ status,
+ handle,
+ reason
+}).
+-define(evt_disconn_complete_bin(Status,Handle,Reason),Status:1/unsigned-unit:8,Handle:1/little-unsigned-unit:16,Reason:1/unsigned-unit:8).
+-define(EVT_DISCONN_COMPLETE_SIZE, 4).
+-define(EVT_AUTH_COMPLETE, 6).
+-record(evt_auth_complete, {
+ status,
+ handle
+}).
+-define(evt_auth_complete_bin(Status,Handle),Status:1/unsigned-unit:8,Handle:1/little-unsigned-unit:16).
+-define(EVT_AUTH_COMPLETE_SIZE, 3).
+-define(EVT_REMOTE_NAME_REQ_COMPLETE, 7).
+-record(evt_remote_name_req_complete, {
+ status,
+ bdaddr,
+ name
+}).
+-define(evt_remote_name_req_complete_bin(Status,Bdaddr,Name),Status:1/unsigned-unit:8,Bdaddr:6/unit:8-binary,Name:(?HCI_MAX_NAME_LENGTH)/unit:8-binary).
+-define(EVT_REMOTE_NAME_REQ_COMPLETE_SIZE, 255).
+-define(EVT_ENCRYPT_CHANGE, 8).
+-record(evt_encrypt_change, {
+ status,
+ handle,
+ encrypt
+}).
+-define(evt_encrypt_change_bin(Status,Handle,Encrypt),Status:1/unsigned-unit:8,Handle:1/little-unsigned-unit:16,Encrypt:1/unsigned-unit:8).
+-define(EVT_ENCRYPT_CHANGE_SIZE, 5).
+-define(EVT_CHANGE_CONN_LINK_KEY_COMPLETE, 9).
+-record(evt_change_conn_link_key_complete, {
+ status,
+ handle
+}).
+-define(evt_change_conn_link_key_complete_bin(Status,Handle),Status:1/unsigned-unit:8,Handle:1/little-unsigned-unit:16).
+-define(EVT_CHANGE_CONN_LINK_KEY_COMPLETE_SIZE, 3).
+-define(EVT_MASTER_LINK_KEY_COMPLETE, 10).
+-record(evt_master_link_key_complete, {
+ status,
+ handle,
+ key_flag
+}).
+-define(evt_master_link_key_complete_bin(Status,Handle,Key_flag),Status:1/unsigned-unit:8,Handle:1/little-unsigned-unit:16,Key_flag:1/unsigned-unit:8).
+-define(EVT_MASTER_LINK_KEY_COMPLETE_SIZE, 4).
+-define(EVT_READ_REMOTE_FEATURES_COMPLETE, 11).
+-record(evt_read_remote_features_complete, {
+ status,
+ handle,
+ features
+}).
+-define(evt_read_remote_features_complete_bin(Status,Handle,Features),Status:1/unsigned-unit:8,Handle:1/little-unsigned-unit:16,Features:8/unit:8-binary).
+-define(EVT_READ_REMOTE_FEATURES_COMPLETE_SIZE, 11).
+-define(EVT_READ_REMOTE_VERSION_COMPLETE, 12).
+-record(evt_read_remote_version_complete, {
+ status,
+ handle,
+ lmp_ver,
+ manufacturer,
+ lmp_subver
+}).
+-define(evt_read_remote_version_complete_bin(Status,Handle,Lmp_ver,Manufacturer,Lmp_subver),Status:1/unsigned-unit:8,Handle:1/little-unsigned-unit:16,Lmp_ver:1/unsigned-unit:8,Manufacturer:1/little-unsigned-unit:16,Lmp_subver:1/little-unsigned-unit:16).
+-define(EVT_READ_REMOTE_VERSION_COMPLETE_SIZE, 8).
+-define(EVT_QOS_SETUP_COMPLETE, 13).
+-record(evt_qos_setup_complete, {
+ status,
+ handle,
+ flags,
+ qos
+}).
+-define(evt_qos_setup_complete_bin(Status,Handle,Flags,Qos),Status:1/unsigned-unit:8,Handle:1/little-unsigned-unit:16,Flags:1/unsigned-unit:8,Qos:17/unit:8-binary).
+-define(EVT_QOS_SETUP_COMPLETE_SIZE, (4 + ?HCI_QOS_CP_SIZE)).
+-define(EVT_CMD_COMPLETE, 14).
+-record(evt_cmd_complete, {
+ ncmd,
+ opcode
+}).
+-define(evt_cmd_complete_bin(Ncmd,Opcode),Ncmd:1/unsigned-unit:8,Opcode:1/little-unsigned-unit:16).
+-define(EVT_CMD_COMPLETE_SIZE, 3).
+-define(EVT_CMD_STATUS, 15).
+-record(evt_cmd_status, {
+ status,
+ ncmd,
+ opcode
+}).
+-define(evt_cmd_status_bin(Status,Ncmd,Opcode),Status:1/unsigned-unit:8,Ncmd:1/unsigned-unit:8,Opcode:1/little-unsigned-unit:16).
+-define(EVT_CMD_STATUS_SIZE, 4).
+-define(EVT_HARDWARE_ERROR, 16).
+-record(evt_hardware_error, {
+ code
+}).
+-define(evt_hardware_error_bin(Code),Code:1/unsigned-unit:8).
+-define(EVT_HARDWARE_ERROR_SIZE, 1).
+-define(EVT_FLUSH_OCCURRED, 17).
+-record(evt_flush_occured, {
+ handle
+}).
+-define(evt_flush_occured_bin(Handle),Handle:1/little-unsigned-unit:16).
+-define(EVT_FLUSH_OCCURRED_SIZE, 2).
+-define(EVT_ROLE_CHANGE, 18).
+-record(evt_role_change, {
+ status,
+ bdaddr,
+ role
+}).
+-define(evt_role_change_bin(Status,Bdaddr,Role),Status:1/unsigned-unit:8,Bdaddr:6/unit:8-binary,Role:1/unsigned-unit:8).
+-define(EVT_ROLE_CHANGE_SIZE, 8).
+-define(EVT_NUM_COMP_PKTS, 19).
+-record(evt_num_comp_pkts, {
+ num_hndl
+}).
+-define(evt_num_comp_pkts_bin(Num_hndl),Num_hndl:1/unsigned-unit:8).
+-define(EVT_NUM_COMP_PKTS_SIZE, 1).
+-define(EVT_MODE_CHANGE, 20).
+-record(evt_mode_change, {
+ status,
+ handle,
+ mode,
+ interval
+}).
+-define(evt_mode_change_bin(Status,Handle,Mode,Interval),Status:1/unsigned-unit:8,Handle:1/little-unsigned-unit:16,Mode:1/unsigned-unit:8,Interval:1/little-unsigned-unit:16).
+-define(EVT_MODE_CHANGE_SIZE, 6).
+-define(EVT_RETURN_LINK_KEYS, 21).
+-record(evt_return_link_keys, {
+ num_keys
+}).
+-define(evt_return_link_keys_bin(Num_keys),Num_keys:1/unsigned-unit:8).
+-define(EVT_RETURN_LINK_KEYS_SIZE, 1).
+-define(EVT_PIN_CODE_REQ, 22).
+-record(evt_pin_code_req, {
+ bdaddr
+}).
+-define(evt_pin_code_req_bin(Bdaddr),Bdaddr:6/unit:8-binary).
+-define(EVT_PIN_CODE_REQ_SIZE, 6).
+-define(EVT_LINK_KEY_REQ, 23).
+-record(evt_link_key_req, {
+ bdaddr
+}).
+-define(evt_link_key_req_bin(Bdaddr),Bdaddr:6/unit:8-binary).
+-define(EVT_LINK_KEY_REQ_SIZE, 6).
+-define(EVT_LINK_KEY_NOTIFY, 24).
+-record(evt_link_key_notify, {
+ bdaddr,
+ link_key,
+ key_type
+}).
+-define(evt_link_key_notify_bin(Bdaddr,Link_key,Key_type),Bdaddr:6/unit:8-binary,Link_key:16/unit:8-binary,Key_type:1/unsigned-unit:8).
+-define(EVT_LINK_KEY_NOTIFY_SIZE, 23).
+-define(EVT_LOOPBACK_COMMAND, 25).
+-define(EVT_DATA_BUFFER_OVERFLOW, 26).
+-record(evt_data_buffer_overflow, {
+ link_type
+}).
+-define(evt_data_buffer_overflow_bin(Link_type),Link_type:1/unsigned-unit:8).
+-define(EVT_DATA_BUFFER_OVERFLOW_SIZE, 1).
+-define(EVT_MAX_SLOTS_CHANGE, 27).
+-record(evt_max_slots_change, {
+ handle,
+ max_slots
+}).
+-define(evt_max_slots_change_bin(Handle,Max_slots),Handle:1/little-unsigned-unit:16,Max_slots:1/unsigned-unit:8).
+-define(EVT_MAX_SLOTS_CHANGE_SIZE, 3).
+-define(EVT_READ_CLOCK_OFFSET_COMPLETE, 28).
+-record(evt_read_clock_offset_complete, {
+ status,
+ handle,
+ clock_offset
+}).
+-define(evt_read_clock_offset_complete_bin(Status,Handle,Clock_offset),Status:1/unsigned-unit:8,Handle:1/little-unsigned-unit:16,Clock_offset:1/little-unsigned-unit:16).
+-define(EVT_READ_CLOCK_OFFSET_COMPLETE_SIZE, 5).
+-define(EVT_CONN_PTYPE_CHANGED, 29).
+-record(evt_conn_ptype_changed, {
+ status,
+ handle,
+ ptype
+}).
+-define(evt_conn_ptype_changed_bin(Status,Handle,Ptype),Status:1/unsigned-unit:8,Handle:1/little-unsigned-unit:16,Ptype:1/little-unsigned-unit:16).
+-define(EVT_CONN_PTYPE_CHANGED_SIZE, 5).
+-define(EVT_QOS_VIOLATION, 30).
+-record(evt_qos_violation, {
+ handle
+}).
+-define(evt_qos_violation_bin(Handle),Handle:1/little-unsigned-unit:16).
+-define(EVT_QOS_VIOLATION_SIZE, 2).
+-define(EVT_PSCAN_REP_MODE_CHANGE, 32).
+-record(evt_pscan_rep_mode_change, {
+ bdaddr,
+ pscan_rep_mode
+}).
+-define(evt_pscan_rep_mode_change_bin(Bdaddr,Pscan_rep_mode),Bdaddr:6/unit:8-binary,Pscan_rep_mode:1/unsigned-unit:8).
+-define(EVT_PSCAN_REP_MODE_CHANGE_SIZE, 7).
+-define(EVT_FLOW_SPEC_COMPLETE, 33).
+-record(evt_flow_spec_complete, {
+ status,
+ handle,
+ flags,
+ direction,
+ qos
+}).
+-define(evt_flow_spec_complete_bin(Status,Handle,Flags,Direction,Qos),Status:1/unsigned-unit:8,Handle:1/little-unsigned-unit:16,Flags:1/unsigned-unit:8,Direction:1/unsigned-unit:8,Qos:17/unit:8-binary).
+-define(EVT_FLOW_SPEC_COMPLETE_SIZE, (5 + ?HCI_QOS_CP_SIZE)).
+-define(EVT_INQUIRY_RESULT_WITH_RSSI, 34).
+-record(inquiry_info_with_rssi, {
+ bdaddr,
+ pscan_rep_mode,
+ pscan_period_mode,
+ dev_class,
+ clock_offset,
+ rssi
+}).
+-define(inquiry_info_with_rssi_bin(Bdaddr,Pscan_rep_mode,Pscan_period_mode,Dev_class,Clock_offset,Rssi),Bdaddr:6/unit:8-binary,Pscan_rep_mode:1/unsigned-unit:8,Pscan_period_mode:1/unsigned-unit:8,Dev_class:3/unit:8-binary,Clock_offset:1/little-unsigned-unit:16,Rssi:1/signed-unit:8).
+-define(INQUIRY_INFO_WITH_RSSI_SIZE, 14).
+-record(inquiry_info_with_rssi_and_pscan_mode, {
+ bdaddr,
+ pscan_rep_mode,
+ pscan_period_mode,
+ pscan_mode,
+ dev_class,
+ clock_offset,
+ rssi
+}).
+-define(inquiry_info_with_rssi_and_pscan_mode_bin(Bdaddr,Pscan_rep_mode,Pscan_period_mode,Pscan_mode,Dev_class,Clock_offset,Rssi),Bdaddr:6/unit:8-binary,Pscan_rep_mode:1/unsigned-unit:8,Pscan_period_mode:1/unsigned-unit:8,Pscan_mode:1/unsigned-unit:8,Dev_class:3/unit:8-binary,Clock_offset:1/little-unsigned-unit:16,Rssi:1/signed-unit:8).
+-define(INQUIRY_INFO_WITH_RSSI_AND_PSCAN_MODE_SIZE, 15).
+-define(EVT_READ_REMOTE_EXT_FEATURES_COMPLETE, 35).
+-record(evt_read_remote_ext_features_complete, {
+ status,
+ handle,
+ page_num,
+ max_page_num,
+ features
+}).
+-define(evt_read_remote_ext_features_complete_bin(Status,Handle,Page_num,Max_page_num,Features),Status:1/unsigned-unit:8,Handle:1/little-unsigned-unit:16,Page_num:1/unsigned-unit:8,Max_page_num:1/unsigned-unit:8,Features:8/unit:8-binary).
+-define(EVT_READ_REMOTE_EXT_FEATURES_COMPLETE_SIZE, 13).
+-define(EVT_SYNC_CONN_COMPLETE, 44).
+-record(evt_sync_conn_complete, {
+ status,
+ handle,
+ bdaddr,
+ link_type,
+ trans_interval,
+ retrans_window,
+ rx_pkt_len,
+ tx_pkt_len,
+ air_mode
+}).
+-define(evt_sync_conn_complete_bin(Status,Handle,Bdaddr,Link_type,Trans_interval,Retrans_window,Rx_pkt_len,Tx_pkt_len,Air_mode),Status:1/unsigned-unit:8,Handle:1/little-unsigned-unit:16,Bdaddr:6/unit:8-binary,Link_type:1/unsigned-unit:8,Trans_interval:1/unsigned-unit:8,Retrans_window:1/unsigned-unit:8,Rx_pkt_len:1/little-unsigned-unit:16,Tx_pkt_len:1/little-unsigned-unit:16,Air_mode:1/unsigned-unit:8).
+-define(EVT_SYNC_CONN_COMPLETE_SIZE, 17).
+-define(EVT_SYNC_CONN_CHANGED, 45).
+-record(evt_sync_conn_changed, {
+ status,
+ handle,
+ trans_interval,
+ retrans_window,
+ rx_pkt_len,
+ tx_pkt_len
+}).
+-define(evt_sync_conn_changed_bin(Status,Handle,Trans_interval,Retrans_window,Rx_pkt_len,Tx_pkt_len),Status:1/unsigned-unit:8,Handle:1/little-unsigned-unit:16,Trans_interval:1/unsigned-unit:8,Retrans_window:1/unsigned-unit:8,Rx_pkt_len:1/little-unsigned-unit:16,Tx_pkt_len:1/little-unsigned-unit:16).
+-define(EVT_SYNC_CONN_CHANGED_SIZE, 9).
+-define(EVT_SNIFF_SUBRATING, 46).
+-record(evt_sniff_subrating, {
+ status,
+ handle,
+ max_tx_latency,
+ max_rx_latency,
+ min_remote_timeout,
+ min_local_timeout
+}).
+-define(evt_sniff_subrating_bin(Status,Handle,Max_tx_latency,Max_rx_latency,Min_remote_timeout,Min_local_timeout),Status:1/unsigned-unit:8,Handle:1/little-unsigned-unit:16,Max_tx_latency:1/little-unsigned-unit:16,Max_rx_latency:1/little-unsigned-unit:16,Min_remote_timeout:1/little-unsigned-unit:16,Min_local_timeout:1/little-unsigned-unit:16).
+-define(EVT_SNIFF_SUBRATING_SIZE, 11).
+-define(EVT_EXTENDED_INQUIRY_RESULT, 47).
+-record(extended_inquiry_info, {
+ bdaddr,
+ pscan_rep_mode,
+ pscan_period_mode,
+ dev_class,
+ clock_offset,
+ rssi,
+ data
+}).
+-define(extended_inquiry_info_bin(Bdaddr,Pscan_rep_mode,Pscan_period_mode,Dev_class,Clock_offset,Rssi,Data),Bdaddr:6/unit:8-binary,Pscan_rep_mode:1/unsigned-unit:8,Pscan_period_mode:1/unsigned-unit:8,Dev_class:3/unit:8-binary,Clock_offset:1/little-unsigned-unit:16,Rssi:1/signed-unit:8,Data:(?HCI_MAX_EIR_LENGTH)/unit:8-binary).
+-define(EXTENDED_INQUIRY_INFO_SIZE, 254).
+-define(EVT_ENCRYPTION_KEY_REFRESH_COMPLETE, 48).
+-record(evt_encryption_key_refresh_complete, {
+ status,
+ handle
+}).
+-define(evt_encryption_key_refresh_complete_bin(Status,Handle),Status:1/unsigned-unit:8,Handle:1/little-unsigned-unit:16).
+-define(EVT_ENCRYPTION_KEY_REFRESH_COMPLETE_SIZE, 3).
+-define(EVT_IO_CAPABILITY_REQUEST, 49).
+-record(evt_io_capability_request, {
+ bdaddr
+}).
+-define(evt_io_capability_request_bin(Bdaddr),Bdaddr:6/unit:8-binary).
+-define(EVT_IO_CAPABILITY_REQUEST_SIZE, 6).
+-define(EVT_IO_CAPABILITY_RESPONSE, 50).
+-record(evt_io_capability_response, {
+ bdaddr,
+ capability,
+ oob_data,
+ authentication
+}).
+-define(evt_io_capability_response_bin(Bdaddr,Capability,Oob_data,Authentication),Bdaddr:6/unit:8-binary,Capability:1/unsigned-unit:8,Oob_data:1/unsigned-unit:8,Authentication:1/unsigned-unit:8).
+-define(EVT_IO_CAPABILITY_RESPONSE_SIZE, 9).
+-define(EVT_USER_CONFIRM_REQUEST, 51).
+-record(evt_user_confirm_request, {
+ bdaddr,
+ passkey
+}).
+-define(evt_user_confirm_request_bin(Bdaddr,Passkey),Bdaddr:6/unit:8-binary,Passkey:1/little-unsigned-unit:32).
+-define(EVT_USER_CONFIRM_REQUEST_SIZE, 10).
+-define(EVT_USER_PASSKEY_REQUEST, 52).
+-record(evt_user_passkey_request, {
+ bdaddr
+}).
+-define(evt_user_passkey_request_bin(Bdaddr),Bdaddr:6/unit:8-binary).
+-define(EVT_USER_PASSKEY_REQUEST_SIZE, 6).
+-define(EVT_REMOTE_OOB_DATA_REQUEST, 53).
+-record(evt_remote_oob_data_request, {
+ bdaddr
+}).
+-define(evt_remote_oob_data_request_bin(Bdaddr),Bdaddr:6/unit:8-binary).
+-define(EVT_REMOTE_OOB_DATA_REQUEST_SIZE, 6).
+-define(EVT_SIMPLE_PAIRING_COMPLETE, 54).
+-record(evt_simple_pairing_complete, {
+ status,
+ bdaddr
+}).
+-define(evt_simple_pairing_complete_bin(Status,Bdaddr),Status:1/unsigned-unit:8,Bdaddr:6/unit:8-binary).
+-define(EVT_SIMPLE_PAIRING_COMPLETE_SIZE, 7).
+-define(EVT_LINK_SUPERVISION_TIMEOUT_CHANGED, 56).
+-record(evt_link_supervision_timeout_changed, {
+ handle,
+ timeout
+}).
+-define(evt_link_supervision_timeout_changed_bin(Handle,Timeout),Handle:1/little-unsigned-unit:16,Timeout:1/little-unsigned-unit:16).
+-define(EVT_LINK_SUPERVISION_TIMEOUT_CHANGED_SIZE, 4).
+-define(EVT_ENHANCED_FLUSH_COMPLETE, 57).
+-record(evt_enhanced_flush_complete, {
+ handle
+}).
+-define(evt_enhanced_flush_complete_bin(Handle),Handle:1/little-unsigned-unit:16).
+-define(EVT_ENHANCED_FLUSH_COMPLETE_SIZE, 2).
+-define(EVT_USER_PASSKEY_NOTIFY, 59).
+-record(evt_user_passkey_notify, {
+ bdaddr,
+ passkey,
+ entered
+}).
+-define(evt_user_passkey_notify_bin(Bdaddr,Passkey,Entered),Bdaddr:6/unit:8-binary,Passkey:1/little-unsigned-unit:32,Entered:1/unsigned-unit:8).
+-define(EVT_USER_PASSKEY_NOTIFY_SIZE, 11).
+-define(EVT_KEYPRESS_NOTIFY, 60).
+-record(evt_keypress_notify, {
+ bdaddr,
+ type
+}).
+-define(evt_keypress_notify_bin(Bdaddr,Type),Bdaddr:6/unit:8-binary,Type:1/unsigned-unit:8).
+-define(EVT_KEYPRESS_NOTIFY_SIZE, 7).
+-define(EVT_REMOTE_HOST_FEATURES_NOTIFY, 61).
+-record(evt_remote_host_features_notify, {
+ bdaddr,
+ features
+}).
+-define(evt_remote_host_features_notify_bin(Bdaddr,Features),Bdaddr:6/unit:8-binary,Features:8/unit:8-binary).
+-define(EVT_REMOTE_HOST_FEATURES_NOTIFY_SIZE, 14).
+-define(EVT_LE_META_EVENT, 62).
+-record(evt_le_meta_event, {
+ subevent,
+ data
+}).
+-define(evt_le_meta_event_bin(Subevent,Data),Subevent:1/unsigned-unit:8,Data:0/unit:8-binary).
+-define(EVT_LE_META_EVENT_SIZE, 1).
+-define(EVT_LE_CONN_COMPLETE, 1).
+-record(evt_le_connection_complete, {
+ status,
+ handle,
+ role,
+ peer_bdaddr_type,
+ peer_bdaddr,
+ interval,
+ latency,
+ supervision_timeout,
+ master_clock_accuracy
+}).
+-define(evt_le_connection_complete_bin(Status,Handle,Role,Peer_bdaddr_type,Peer_bdaddr,Interval,Latency,Supervision_timeout,Master_clock_accuracy),Status:1/unsigned-unit:8,Handle:1/little-unsigned-unit:16,Role:1/unsigned-unit:8,Peer_bdaddr_type:1/unsigned-unit:8,Peer_bdaddr:6/unit:8-binary,Interval:1/little-unsigned-unit:16,Latency:1/little-unsigned-unit:16,Supervision_timeout:1/little-unsigned-unit:16,Master_clock_accuracy:1/unsigned-unit:8).
+-define(EVT_LE_CONN_COMPLETE_SIZE, 18).
+-define(EVT_LE_ADVERTISING_REPORT, 2).
+-record(le_advertising_info, {
+ evt_type,
+ bdaddr_type,
+ bdaddr,
+ length,
+ data
+}).
+-define(le_advertising_info_bin(Evt_type,Bdaddr_type,Bdaddr,Length,Data),Evt_type:1/unsigned-unit:8,Bdaddr_type:1/unsigned-unit:8,Bdaddr:6/unit:8-binary,Length:1/unsigned-unit:8,Data:0/unit:8-binary).
+-define(LE_ADVERTISING_INFO_SIZE, 9).
+-define(EVT_LE_CONN_UPDATE_COMPLETE, 3).
+-record(evt_le_connection_update_complete, {
+ status,
+ handle,
+ interval,
+ latency,
+ supervision_timeout
+}).
+-define(evt_le_connection_update_complete_bin(Status,Handle,Interval,Latency,Supervision_timeout),Status:1/unsigned-unit:8,Handle:1/little-unsigned-unit:16,Interval:1/little-unsigned-unit:16,Latency:1/little-unsigned-unit:16,Supervision_timeout:1/little-unsigned-unit:16).
+-define(EVT_LE_CONN_UPDATE_COMPLETE_SIZE, 9).
+-define(EVT_LE_READ_REMOTE_USED_FEATURES_COMPLETE, 4).
+-record(evt_le_read_remote_used_features_complete, {
+ status,
+ handle,
+ features
+}).
+-define(evt_le_read_remote_used_features_complete_bin(Status,Handle,Features),Status:1/unsigned-unit:8,Handle:1/little-unsigned-unit:16,Features:8/unit:8-binary).
+-define(EVT_LE_READ_REMOTE_USED_FEATURES_COMPLETE_SIZE, 11).
+-define(EVT_LE_LTK_REQUEST, 5).
+-record(evt_le_long_term_key_request, {
+ handle,
+ random,
+ diversifier
+}).
+-define(evt_le_long_term_key_request_bin(Handle,Random,Diversifier),Handle:1/little-unsigned-unit:16,Random:1/little-unsigned-unit:64,Diversifier:1/little-unsigned-unit:16).
+-define(EVT_LE_LTK_REQUEST_SIZE, 12).
+-define(EVT_PHYSICAL_LINK_COMPLETE, 64).
+-record(evt_physical_link_complete, {
+ status,
+ handle
+}).
+-define(evt_physical_link_complete_bin(Status,Handle),Status:1/unsigned-unit:8,Handle:1/unsigned-unit:8).
+-define(EVT_PHYSICAL_LINK_COMPLETE_SIZE, 2).
+-define(EVT_CHANNEL_SELECTED, 65).
+-define(EVT_DISCONNECT_PHYSICAL_LINK_COMPLETE, 66).
+-record(evt_disconn_physical_link_complete, {
+ status,
+ handle,
+ reason
+}).
+-define(evt_disconn_physical_link_complete_bin(Status,Handle,Reason),Status:1/unsigned-unit:8,Handle:1/unsigned-unit:8,Reason:1/unsigned-unit:8).
+-define(EVT_DISCONNECT_PHYSICAL_LINK_COMPLETE_SIZE, 3).
+-define(EVT_PHYSICAL_LINK_LOSS_EARLY_WARNING, 67).
+-record(evt_physical_link_loss_warning, {
+ handle,
+ reason
+}).
+-define(evt_physical_link_loss_warning_bin(Handle,Reason),Handle:1/unsigned-unit:8,Reason:1/unsigned-unit:8).
+-define(EVT_PHYSICAL_LINK_LOSS_WARNING_SIZE, 2).
+-define(EVT_PHYSICAL_LINK_RECOVERY, 68).
+-record(evt_physical_link_recovery, {
+ handle
+}).
+-define(evt_physical_link_recovery_bin(Handle),Handle:1/unsigned-unit:8).
+-define(EVT_PHYSICAL_LINK_RECOVERY_SIZE, 1).
+-define(EVT_LOGICAL_LINK_COMPLETE, 69).
+-record(evt_logical_link_complete, {
+ status,
+ log_handle,
+ handle,
+ tx_flow_id
+}).
+-define(evt_logical_link_complete_bin(Status,Log_handle,Handle,Tx_flow_id),Status:1/unsigned-unit:8,Log_handle:1/little-unsigned-unit:16,Handle:1/unsigned-unit:8,Tx_flow_id:1/unsigned-unit:8).
+-define(EVT_LOGICAL_LINK_COMPLETE_SIZE, 5).
+-define(EVT_DISCONNECT_LOGICAL_LINK_COMPLETE, 70).
+-define(EVT_FLOW_SPEC_MODIFY_COMPLETE, 71).
+-record(evt_flow_spec_modify_complete, {
+ status,
+ handle
+}).
+-define(evt_flow_spec_modify_complete_bin(Status,Handle),Status:1/unsigned-unit:8,Handle:1/little-unsigned-unit:16).
+-define(EVT_FLOW_SPEC_MODIFY_COMPLETE_SIZE, 3).
+-define(EVT_NUMBER_COMPLETED_BLOCKS, 72).
+-define(EVT_AMP_STATUS_CHANGE, 77).
+-record(evt_amp_status_change, {
+ status,
+ amp_status
+}).
+-define(evt_amp_status_change_bin(Status,Amp_status),Status:1/unsigned-unit:8,Amp_status:1/unsigned-unit:8).
+-define(EVT_AMP_STATUS_CHANGE_SIZE, 2).
+-define(EVT_TESTING, 254).
+-define(EVT_VENDOR, 255).
+-define(EVT_STACK_INTERNAL, 253).
+-record(evt_stack_internal, {
+ type,
+ data
+}).
+-define(evt_stack_internal_bin(Type,Data),Type:1/little-unsigned-unit:16,Data:0/unit:8-binary).
+-define(EVT_STACK_INTERNAL_SIZE, 2).
+-define(EVT_SI_DEVICE, 1).
+-record(evt_si_device, {
+ event,
+ dev_id
+}).
+-define(evt_si_device_bin(Event,Dev_id),Event:1/little-unsigned-unit:16,Dev_id:1/little-unsigned-unit:16).
+-define(EVT_SI_DEVICE_SIZE, 4).
+-endif.
diff --git a/deps/bt/src/hci_drv.erl b/deps/bt/src/hci_drv.erl
new file mode 100644
index 0000000..8f3d062
--- /dev/null
+++ b/deps/bt/src/hci_drv.erl
@@ -0,0 +1,514 @@
+%%% @author Tony Rogvall <tony@up13>
+%%% @copyright (C) 2015, Tony Rogvall
+%%% @doc
+%%% hci_drv
+%%% @end
+%%% Created : 5 Apr 2015 by Tony Rogvall <tony@up13>
+
+-module(hci_drv).
+
+-export([open/0]).
+-export([bind/2]).
+-export([close/1]).
+-export([send/2]).
+-export([activate/1]).
+-export([deactivate/1]).
+-export([activate/2]).
+-export([debug/2]).
+-export([dev_up/2]).
+-export([dev_down/2]).
+-export([dev_reset/2]). %% down,up?
+-export([dev_restat/2]). %% reset statistics
+-export([get_dev_list/1]).
+-export([get_dev_info/2]).
+-export([get_conn_list/2]).
+-export([get_conn_info/3]).
+-export([get_auth_info/2]).
+-export([set_raw/2]).
+-export([set_auth/3]).
+-export([set_encrypt/3]).
+-export([set_ptype/3]).
+-export([set_link_policy/3]).
+-export([set_link_mode/3]).
+-export([set_scan/3]).
+-export([set_acl_mtu/4]).
+-export([set_sco_mtu/4]).
+-export([block/2]).
+-export([unblock/2]).
+-export([inquiry/5]).
+
+-export([set_filter/2]).
+-export([get_filter/1]).
+-export([set_filter_ptype/2]).
+-export([clr_filter_ptype/2]).
+-export([set_filter_event/2]).
+-export([clr_filter_event/2]).
+-export([set_filter_opcode/2]).
+-export([make_filter/3]).
+
+-include("../include/hci_drv.hrl").
+-include("hci_api.hrl").
+
+%% deugging
+-compile(export_all).
+
+-define(CMD_ACTIVE, 1).
+-define(CMD_DEBUG, 2).
+-define(CMD_BIND, 3).
+-define(CMD_GETFILTER, 4).
+-define(CMD_SETFILTER, 5).
+
+-define(CMD_HCIDEVUP, 201).
+-define(CMD_HCIDEVDOWN, 202).
+-define(CMD_HCIDEVRESET, 203).
+-define(CMD_HCIDEVRESTAT, 204).
+-define(CMD_HCIGETDEVLIST, 210).
+-define(CMD_HCIGETDEVINFO, 211).
+-define(CMD_HCIGETCONNLIST, 212).
+-define(CMD_HCIGETCONNINFO, 213).
+-define(CMD_HCIGETAUTHINFO, 215).
+-define(CMD_HCISETRAW, 220).
+-define(CMD_HCISETSCAN, 221).
+-define(CMD_HCISETAUTH, 222).
+-define(CMD_HCISETENCRYPT, 223).
+-define(CMD_HCISETPTYPE, 224).
+-define(CMD_HCISETLINKPOL, 225).
+-define(CMD_HCISETLINKMODE, 226).
+-define(CMD_HCISETACLMTU, 227).
+-define(CMD_HCISETSCOMTU, 228).
+-define(CMD_HCIBLOCKADDR, 230).
+-define(CMD_HCIUNBLOCKADDR, 231).
+-define(CMD_HCIINQUIRY, 240).
+
+
+-define(DLOG_DEBUG, 7).
+-define(DLOG_INFO, 6).
+-define(DLOG_NOTICE, 5).
+-define(DLOG_WARNING, 4).
+-define(DLOG_ERROR, 3).
+-define(DLOG_CRITICAL, 2).
+-define(DLOG_ALERT, 1).
+-define(DLOG_EMERGENCY, 0).
+-define(DLOG_NONE, -1).
+
+
+-spec open() -> hci_socket_t().
+open() ->
+ Driver = "hci_drv",
+ Path = code:priv_dir(bt),
+ %% io:format("load_driver '~s' from: '~s'\n", [Driver, Path]),
+ case erl_ddll:load_driver(Path, Driver) of
+ ok ->
+ erlang:open_port({spawn_driver, Driver}, [binary]);
+ {error,Error} ->
+ io:format("Error: ~s\n", [erl_ddll:format_error_int(Error)]),
+ erlang:error(Error)
+ end.
+
+%% Close the HCI socket
+-spec close(Hci::hci_socket_t()) -> boolean().
+close(Hci) when is_port(Hci) ->
+ erlang:port_close(Hci).
+
+%% Bind HCI socket to device
+-spec bind(Hci::hci_socket_t(), DevID::hci_devid_t()) -> ok | {error,posix()}.
+
+bind(Hci, DevID) when is_port(Hci), is_integer(DevID) ->
+ port_call(Hci, ?CMD_BIND, <<DevID:32/signed>>).
+
+%% Bring the device UP
+-spec dev_up(Hci::hci_socket_t(), DevID::hci_devid_t()) -> ok | {error,posix()}.
+
+dev_up(Hci,DevID) when is_port(Hci), is_integer(DevID) ->
+ port_call(Hci, ?CMD_HCIDEVUP, <<DevID:32/signed>>).
+
+%% Bring the device DOWN
+-spec dev_down(Hci::hci_socket_t(), DevID::hci_devid_t()) -> ok | {error,posix()}.
+
+dev_down(Hci,DevID) when is_port(Hci), is_integer(DevID) ->
+ port_call(Hci, ?CMD_HCIDEVDOWN, <<DevID:32/signed>>).
+
+%% Reset the device, maybe do down/up? as seen in library code elsewhere
+-spec dev_reset(Hci::hci_socket_t(), DevID::hci_devid_t()) -> ok | {error,posix()}.
+
+dev_reset(Hci,DevID) when is_port(Hci), is_integer(DevID) ->
+ port_call(Hci, ?CMD_HCIDEVRESET, <<DevID:32/signed>>).
+
+%% Reset device statistics
+-spec dev_restat(Hci::hci_socket_t(), DevID::hci_devid_t()) -> ok | {error,posix()}.
+
+dev_restat(Hci,DevID) when is_port(Hci), is_integer(DevID) ->
+ port_call(Hci, ?CMD_HCIDEVRESTAT, <<DevID:32/signed>>).
+
+%% Get list of [{device(),options()}]
+-spec get_dev_list(Hci::hci_socket_t()) ->
+ {ok,[{DevID::hci_devid_t(), Opt::integer()}]} |
+ {error,posix()}.
+get_dev_list(Hci) when is_port(Hci) ->
+ case port_call(Hci, ?CMD_HCIGETDEVLIST, <<>>) of
+ {ok, DevList} ->
+ {ok,
+ [{DevID,DevOpt} ||
+ <<DevID:16, DevOpt:32>>
+ <= DevList ]};
+ Error ->
+ Error
+ end.
+
+%% get device info
+-spec get_dev_info(Hci::hci_socket_t(), DevID::hci_devid_t()) ->
+ {ok,[#hci_dev_info{}]} | {error,posix()}.
+
+get_dev_info(Hci, DevID) ->
+ case port_call(Hci, ?CMD_HCIGETDEVINFO, <<DevID:32/signed>>) of
+ {ok, Info} ->
+ {ok, decode_hci_dev_info(Info)};
+ Error ->
+ Error
+ end.
+
+%% get device info
+-spec get_conn_list(Hci::hci_socket_t(), DevID::hci_devid_t()) ->
+ {ok,[#hci_conn_info{}]} | {error,posix()}.
+get_conn_list(Hci, DevID) ->
+ case port_call(Hci, ?CMD_HCIGETCONNLIST, <<DevID:32/signed>>) of
+ {ok, Data} ->
+ {ok, decode_hci_conn_list(Data)};
+ Error ->
+ Error
+ end.
+
+-spec get_conn_info(Hci::hci_socket_t(), Addr::bdaddr_t(), Type::uint8_t()) ->
+ {ok,#hci_conn_info{}} | {error,posix()}.
+
+get_conn_info(Hci, Addr, Type) when is_port(Hci) ->
+ case bt:getaddr(Addr) of
+ {ok,{A,B,C,D,E,F}} ->
+ case port_call(Hci, ?CMD_HCIGETCONNINFO, <<F,E,D,C,B,A,Type>>) of
+ {ok, Info} ->
+ {ok, decode_hci_conn_info(Info)};
+ Error ->
+ Error
+ end;
+ Error ->
+ Error
+ end.
+
+-spec get_auth_info(Hci::hci_socket_t(), Addr::bdaddr_t()) ->
+ {ok, Type::uint8_t()} | {error,posix()}.
+
+get_auth_info(Hci, Addr) when is_port(Hci) ->
+ case bt:getaddr(Addr) of
+ {ok,{A,B,C,D,E,F}} ->
+ port_call(Hci, ?CMD_HCIGETAUTHINFO, <<F,E,D,C,B,A>>);
+ Error ->
+ Error
+ end.
+
+%% Set raw processing?
+-spec set_raw(Hci::hci_socket_t(), DevID::hci_devid_t()) -> ok | {error,posix()}.
+
+set_raw(Hci,DevID) when is_port(Hci), is_integer(DevID) ->
+ port_call(Hci, ?CMD_HCISETRAW, <<DevID:32/signed>>).
+
+%% Enable/Disable authentication
+-spec set_auth(Hci::hci_socket_t(), DevID::hci_devid_t(), DoAuth::boolean()) ->
+ ok | {error,posix()}.
+set_auth(Hci,DevID,DoAuth) when is_port(Hci), is_integer(DevID),
+ is_boolean(DoAuth) ->
+ port_call(Hci, ?CMD_HCISETAUTH, <<DevID:32/signed,DoAuth:8>>).
+
+%% Enable/Disable authentication
+-spec set_encrypt(Hci::hci_socket_t(), DevID::hci_devid_t(), DoAuth::boolean()) ->
+ ok | {error,posix()}.
+set_encrypt(Hci,DevID,DoEncrypt) when is_port(Hci), is_integer(DevID),
+ is_boolean(DoEncrypt) ->
+ port_call(Hci, ?CMD_HCISETENCRYPT, <<DevID:32/signed,DoEncrypt:8>>).
+
+%% Set device packet type
+-spec set_ptype(Hci::hci_socket_t(),DevID::hci_devid_t(),PType::[string()]) ->
+ ok | {error,posix()}.
+set_ptype(Hci,DevID,PType0) when is_port(Hci), is_integer(DevID),
+ is_list(PType0) ->
+ PType = set_bits(PType0, hci_util:kv_pkt_ptype()),
+ port_call(Hci, ?CMD_HCISETPTYPE, <<DevID:32/signed,PType:32>>).
+
+-spec set_link_policy(Hci::hci_socket_t(),DevID::hci_devid_t(),Pol::string()) ->
+ ok | {error,posix()}.
+set_link_policy(Hci,DevID,Pol0) when is_port(Hci), is_integer(DevID) ->
+ LinkPolicy = hci_util:find_enum_value(Pol0, hci_util:kv_link_policy()),
+ port_call(Hci, ?CMD_HCISETLINKPOL, <<DevID:32/signed,LinkPolicy:32>>).
+
+-spec set_link_mode(Hci::hci_socket_t(),DevID::hci_devid_t(),Mode::string()) ->
+ ok | {error,posix()}.
+set_link_mode(Hci,DevID,Mode0) when is_port(Hci), is_integer(DevID) ->
+ LinkMode = hci_util:find_enum_value(Mode0, hci_util:kv_link_mode()),
+ port_call(Hci, ?CMD_HCISETLINKMODE, <<DevID:32/signed,LinkMode:32>>).
+
+-spec set_scan(Hci::hci_socket_t(),DevID::hci_devid_t(),Scan::string()) ->
+ ok | {error,posix()}.
+set_scan(Hci,DevID,Scan0) when is_port(Hci) ->
+ Scan = hci_util:find_enum_value(Scan0, hci_util:kv_scan()),
+ port_call(Hci, ?CMD_HCISETSCAN, <<DevID:32/signed,Scan:32>>).
+
+-spec set_acl_mtu(Hci::hci_socket_t(),DevID::hci_devid_t(),
+ Mtu::integer(),Mpkt::integer()) ->
+ ok | {error,posix()}.
+set_acl_mtu(Hci,DevID,Mtu,Mpkt) when is_port(Hci) ->
+ port_call(Hci, ?CMD_HCISETACLMTU, <<DevID:32/signed,Mtu:16,Mpkt:16>>).
+
+-spec set_sco_mtu(Hci::hci_socket_t(),DevID::hci_devid_t(),
+ Mtu::integer(),Mpkt::integer()) ->
+ ok | {error,posix()}.
+set_sco_mtu(Hci,DevID,Mtu,Mpkt) when is_port(Hci) ->
+ port_call(Hci, ?CMD_HCISETSCOMTU, <<DevID:32/signed,Mtu:16,Mpkt:16>>).
+
+%% The Hci socket must be bound before this operation
+-spec block(Hci::hci_socket_t(), Addr::bdaddr_t()) ->
+ ok | {error,posix()}.
+block(Hci, Addr) when is_port(Hci) ->
+ case bt:getaddr(Addr) of
+ {ok,{A,B,C,D,E,F}} ->
+ port_call(Hci, ?CMD_HCIBLOCKADDR, <<F,E,D,C,B,A>>);
+ Error ->
+ Error
+ end.
+
+%% The Hci socket must be bound before this operation
+-spec unblock(Hci::hci_socket_t(), Addr:: all | bdaddr_t()) ->
+ ok | {error,posix()}.
+unblock(Hci, all) when is_port(Hci) ->
+ unblock(Hci, {0,0,0,0,0,0});
+unblock(Hci,Addr) when is_port(Hci) ->
+ case bt:getaddr(Addr) of
+ {ok, {A,B,C,D,E,F}} ->
+ port_call(Hci, ?CMD_HCIUNBLOCKADDR, <<F,E,D,C,B,A>>);
+ Error ->
+ Error
+ end.
+
+%% Timeout is in millisecs but is converted into 1.28s units
+%% 1 len = 1.28s = 1280 ms
+inquiry(Hci, Timeout, NumRsp, Lap0, Flags) ->
+ Len = (Timeout div 1280) + (if Timeout rem 1280 =:= 0 -> 0; true -> 1 end),
+ Lap = if Lap0 =:= 0; Lap0 =:= <<>> -> <<16#33,16#8b,16#9e>>;
+ byte_size(Lap0) =:= 3 -> Lap0
+ end,
+ Args = <<Len, NumRsp, Lap/binary, Flags:32>>,
+ case port_call(Hci, ?CMD_HCIINQUIRY, Args) of
+ {ok, Bin} ->
+ {ok, [hci_api:decode_inquiry_info(Info) ||
+ <<Info:?INQUIRY_INFO_SIZE/binary>> <= Bin]};
+ Error ->
+ Error
+ end.
+
+
+-spec deactivate(Hci::hci_socket_t()) -> ok | {error,posix()}.
+
+deactivate(Hci) when is_port(Hci) ->
+ activate(Hci, 0).
+
+-spec activate(Hci::hci_socket_t()) -> ok | {error,posix()}.
+activate(Hci) when is_port(Hci) ->
+ activate(Hci, -1).
+
+-spec activate(Hci::hci_socket_t(),N::integer()) -> ok | {error,posix()}.
+activate(Hci, N) when is_port(Hci),
+ is_integer(N), N >= -1, N < 16#7fffffff ->
+ port_call(Hci, ?CMD_ACTIVE, <<N:32>>).
+
+-spec debug(Hci::hci_socket_t(), Level::level_t()) -> ok.
+debug(Hci,Level) when is_port(Hci), is_atom(Level) ->
+ L = level(Level),
+ port_call(Hci, ?CMD_DEBUG, <<L:32>>).
+
+set_filter_ptype(T, F = #hci_filter { type_mask = M}) ->
+ Bit = if T =:= ?HCI_VENDOR_PKT -> 1;
+ true -> 1 bsl (T band ?HCI_FLT_TYPE_BITS)
+ end,
+ F#hci_filter { type_mask = M bor Bit }.
+
+clr_filter_ptype(T, F = #hci_filter { type_mask = M}) ->
+ Bit = if T =:= ?HCI_VENDOR_PKT -> 1;
+ true -> 1 bsl (T band ?HCI_FLT_TYPE_BITS)
+ end,
+ F#hci_filter { type_mask = M band (bnot Bit) }.
+
+set_filter_event(E, F = #hci_filter { event_mask = M}) ->
+ Bit = 1 bsl (E band ?HCI_FLT_EVENT_BITS),
+ F#hci_filter { event_mask = M bor Bit }.
+
+clr_filter_event(E, F = #hci_filter { event_mask = M}) ->
+ Bit = 1 bsl (E band ?HCI_FLT_EVENT_BITS),
+ F#hci_filter { event_mask = M band (bnot Bit) }.
+
+set_filter_opcode(Opcode, F = #hci_filter { }) ->
+ F#hci_filter { opcode = Opcode }.
+
+make_filter(Opcode, Ts, Es) when is_integer(Opcode),
+ is_list(Ts),
+ is_list(Es) ->
+
+ Type_mask = make_bits(Ts, 0) band 16#ffffffff,
+ Event_mask = make_bits(Es, 0) band 16#ffffffffffffffff,
+ #hci_filter { type_mask = Type_mask,
+ event_mask = Event_mask,
+ opcode = Opcode }.
+
+make_bits([255|Ns], Bits) -> %% ?HCI_VENDOR_PKT! -> 1
+ make_bits(Ns, Bits bor 1);
+make_bits([-1|_], _Bits) -> 16#ffffffffffffffff;
+make_bits([Nr|Ns], Bits) when is_integer(Nr), Nr >= 0 ->
+ make_bits(Ns, Bits bor (1 bsl Nr));
+make_bits([], Bits) ->
+ Bits.
+
+
+-spec set_filter(Hci::hci_socket_t(), Filter::#hci_filter{}) ->
+ ok | {error,posix()}.
+set_filter(Hci,Filter) when is_port(Hci), is_record(Filter, hci_filter) ->
+ port_call(Hci, ?CMD_SETFILTER, encode_hci_filter(Filter)).
+
+-spec get_filter(Hci::hci_socket_t()) ->
+ {ok, Filter::#hci_filter{}} | {error,posix()}.
+
+get_filter(Hci) when is_port(Hci) ->
+ case port_call(Hci, ?CMD_GETFILTER, <<>>) of
+ {ok, Data} -> {ok, decode_hci_filter(Data)};
+ Error -> Error
+ end.
+
+-spec send(Hci::hci_socket_t(), Command::iolist()) ->
+ boolean().
+send(Hci, Command) ->
+ erlang:port_command(Hci, Command).
+
+port_call(Hci, Cmd, Data) ->
+ case erlang:port_control(Hci, Cmd, Data) of
+ <<0>> ->
+ ok;
+ <<255,E/binary>> ->
+ {error, erlang:binary_to_atom(E, latin1)};
+ <<254,E/binary>> ->
+ {error, binary_to_list(E)};
+ <<1,Y:8>> -> {ok,Y};
+ <<1,Y:16/native-unsigned>> -> {ok, Y};
+ <<1,Y:32/native-unsigned>> -> {ok, Y};
+ <<1,Y:64/native-unsigned>> -> {ok, Y};
+ <<2,X:32/native-unsigned,Y:32/native-unsigned>> -> {ok,{X,Y}};
+ <<3,X/binary>> -> {ok,X};
+ <<4,X/binary>> -> {ok,binary_to_list(X)}
+ end.
+
+%% convert symbolic to numeric level
+level(true) -> ?DLOG_DEBUG;
+level(false) -> ?DLOG_NONE;
+level(debug) -> ?DLOG_DEBUG;
+level(info) -> ?DLOG_INFO;
+level(notice) -> ?DLOG_NOTICE;
+level(warning) -> ?DLOG_WARNING;
+level(error) -> ?DLOG_ERROR;
+level(critical) -> ?DLOG_CRITICAL;
+level(alert) -> ?DLOG_ALERT;
+level(emergency) -> ?DLOG_EMERGENCY;
+level(none) -> ?DLOG_NONE.
+
+%% note: bluetooth addresses are reversed in hci
+decode_hci_dev_info(
+ <<Dev_id:16, Name0:8/binary, F,E,D,C,B,A,
+ Flags:32, Type:8, Features:8/binary,
+ Pkt_type:32, Link_policy:32, Link_mode:32,
+ Acl_mtu:16, Acl_pkts:16,
+ Sco_mtu:16, Sco_pkts:16,
+ Stat/binary>>) ->
+ Name = hci_util:c_string(Name0),
+ BdAddr = {A,B,C,D,E,F},
+ S = decode_hci_dev_stats(Stat),
+ #hci_dev_info {
+ dev_id = Dev_id,
+ name = Name,
+ bdaddr = BdAddr,
+ flags = Flags,
+ type = Type,
+ features = Features,
+ pkt_type = Pkt_type,
+ link_policy = Link_policy,
+ link_mode = Link_mode,
+ acl_mtu = Acl_mtu,
+ acl_pkts = Acl_pkts,
+ sco_mtu = Sco_mtu,
+ sco_pkts = Sco_pkts,
+ stat = S
+ }.
+
+%% 10*4 = 40 bytes
+decode_hci_dev_stats(
+ << Err_rx:32, Err_tx:32,
+ Cmd_tx:32, Evt_rx:32,
+ Acl_tx:32, Acl_rx:32,
+ Sco_tx:32, Sco_rx:32,
+ Byte_rx:32, Byte_tx:32>>) ->
+ #hci_dev_stats {
+ err_rx = Err_rx,
+ err_tx = Err_tx,
+ cmd_tx = Cmd_tx,
+ evt_rx = Evt_rx,
+ acl_tx = Acl_tx,
+ acl_rx = Acl_rx,
+ sco_tx = Sco_tx,
+ sco_rx = Sco_rx,
+ byte_rx = Byte_rx,
+ byte_tx = Byte_tx }.
+
+%% bdaddr is reverse in hci
+decode_hci_conn_info(<<Handle:16,
+ F,E,D,C,B,A,
+ Type:8, Out:8,
+ State:16, Link_mode:32>>) ->
+ #hci_conn_info { handle=Handle, bdaddr={A,B,C,D,E,F},
+ type=Type, out=Out,
+ state=State, link_mode = Link_mode }.
+
+decode_hci_conn_list(<<Item:16/binary, List/binary>>) ->
+ [decode_hci_conn_info(Item) | decode_hci_conn_list(List)];
+decode_hci_conn_list(<<>>) ->
+ [].
+
+encode_hci_filter(#hci_filter { type_mask = Type_mask,
+ event_mask = Event_mask,
+ opcode = Opcode }) ->
+ Event0 = Event_mask band 16#ffffffff,
+ Event1 = (Event_mask bsr 32) band 16#ffffffff,
+ <<Type_mask:32, Event0:32, Event1:32, Opcode:16>>.
+
+decode_hci_filter(<<Type_mask:32, Event0:32, Event1:32, Opcode:16>>) ->
+ Event_mask = Event0 + (Event1 bsl 32),
+ #hci_filter { type_mask = Type_mask,
+ event_mask = Event_mask,
+ opcode = Opcode }.
+
+%% given a list of atoms/strings/integers
+%% build a bitmask from the values
+
+set_bits(Names, Flags) ->
+ set_bits(Names, Flags, 0).
+
+set_bits([Name|Names], Flags, Acc) when is_atom(Name) ->
+ case lists:keyfind(atom_to_list(Name), 1, Flags) of
+ false ->
+ exit(badarg);
+ {_, Bit} ->
+ set_bits(Names, Flags, Bit bor Acc)
+ end;
+set_bits([Name|Names], Flags, Acc) when is_list(Name) ->
+ case lists:keyfind(Name, 1, Flags) of
+ false ->
+ exit(badarg);
+ {_, Bit} ->
+ set_bits(Names, Flags, Bit bor Acc)
+ end;
+set_bits([Bits|Names], Flags, Acc) when is_integer(Bits) ->
+ set_bits(Names, Flags, Bits bor Acc);
+set_bits([], _Flags, Acc) ->
+ Acc.
diff --git a/deps/bt/src/hci_socket.erl b/deps/bt/src/hci_socket.erl
new file mode 100644
index 0000000..4549c44
--- /dev/null
+++ b/deps/bt/src/hci_socket.erl
@@ -0,0 +1,314 @@
+%%%-------------------------------------------------------------------
+%%% @author Tony Rogvall <tony@up13>
+%%% @copyright (C) 2015, Tony Rogvall
+%%% @doc
+%%% HCI socket server
+%%% @end
+%%% Created : 6 Apr 2015 by Tony Rogvall <tony@up13>
+%%%-------------------------------------------------------------------
+-module(hci_socket).
+
+-behaviour(gen_server).
+
+%% API
+-export([open/1]).
+-export([close/1]).
+-export([send/4]).
+-export([call/5]).
+-export([get_info/1]).
+-export([get_conn_list/1]).
+-export([get_conn_info/3]).
+-export([get_auth_info/2]).
+-export([debug/2]).
+
+%% gen_server callbacks
+-export([init/1, handle_call/3, handle_cast/2, handle_info/2,
+ terminate/2, code_change/3]).
+
+-define(SERVER, ?MODULE).
+
+-include("../include/hci_drv.hrl").
+-include("hci_api.hrl").
+
+-record(state,
+ {
+ hci :: hci_socket_t(),
+ devid :: hci_devid_t(),
+ from,
+ saved_filter :: #hci_filter {},
+ current_opcode :: uint16_t(),
+ current_event :: uint16_t(),
+ current_decode :: function()
+ }).
+
+%%%===================================================================
+%%% API
+%%%===================================================================
+
+%%--------------------------------------------------------------------
+%% @doc
+%% Starts the server
+%%
+%% @spec start_link() -> {ok, Pid} | ignore | {error, Error}
+%% @end
+%%--------------------------------------------------------------------
+open(DevID) ->
+ gen_server:start_link(?MODULE, [DevID], []).
+
+close(Pid) ->
+ gen_server:call(Pid, close).
+
+send(Pid, OGF, OCG, Data) ->
+ gen_server:call(Pid, {send, OGF, OCG, Data}).
+
+call(Pid, OGF, OCG, Data, Event) ->
+ gen_server:call(Pid, {call, OGF, OCG, Data, Event}).
+
+get_info(Pid) ->
+ gen_server:call(Pid, get_info).
+
+get_conn_list(Pid) ->
+ gen_server:call(Pid, get_conn_list).
+
+get_conn_info(Pid,Addr,Type) ->
+ gen_server:call(Pid, {get_conn_info,Addr,Type}).
+
+get_auth_info(Pid,Addr) ->
+ gen_server:call(Pid, {get_auth_info,Addr}).
+
+debug(Pid, Level) ->
+ gen_server:call(Pid, {debug, Level}).
+
+%%%===================================================================
+%%% gen_server callbacks
+%%%===================================================================
+
+%%--------------------------------------------------------------------
+%% @private
+%% @doc
+%% Initializes the server
+%%
+%% @spec init(Args) -> {ok, State} |
+%% {ok, State, Timeout} |
+%% ignore |
+%% {stop, Reason}
+%% @end
+%%--------------------------------------------------------------------
+init([DevID]) ->
+ Hci = hci_drv:open(),
+ case hci_drv:bind(Hci, DevID) of
+ ok ->
+ hci_drv:activate(Hci), %% receive async data
+ {ok, #state{ hci = Hci, devid = DevID }};
+ Error -> {stop,Error}
+ end.
+%%--------------------------------------------------------------------
+%% @private
+%% @doc
+%% Handling call messages
+%%
+%% @spec handle_call(Request, From, State) ->
+%% {reply, Reply, State} |
+%% {reply, Reply, State, Timeout} |
+%% {noreply, State} |
+%% {noreply, State, Timeout} |
+%% {stop, Reason, Reply, State} |
+%% {stop, Reason, State}
+%% @end
+%%--------------------------------------------------------------------
+handle_call({send,OGF,OCF,Data}, _From, State) ->
+ Pkt = [?HCI_COMMAND_PKT,
+ <<(?cmd_opcode_pack(OGF,OCF)):16/little>>,
+ <<(byte_size(Data)):8>>,
+ Data],
+ R = hci_drv:send(State#state.hci,Pkt),
+ %% io:format("send pkt ~p = ~p\n", [Pkt,R]),
+ {reply, R, State};
+
+handle_call({call,OGF,OCF,Data,Decode}, From, State) ->
+ Opcode = ?cmd_opcode_pack(OGF,OCF),
+ {ok,OldFilter} = hci_drv:get_filter(State#state.hci),
+ %% io:format("call: saved_filter = ~p\n", [OldFilter]),
+ Event = 0, %% fixme, use ?EVT_xxxx
+ NewFilter = hci_drv:make_filter(Opcode,
+ [?HCI_EVENT_PKT],
+ [?EVT_CMD_STATUS,
+ ?EVT_CMD_COMPLETE,
+ ?EVT_LE_META_EVENT,
+ ?EVT_REMOTE_NAME_REQ_COMPLETE,
+ Event]),
+ %% io:format("call: new_filter = ~p\n", [NewFilter]),
+ case hci_drv:set_filter(State#state.hci, NewFilter) of
+ ok ->
+ Pkt = [?HCI_COMMAND_PKT,
+ <<Opcode:16/little>>,
+ <<(byte_size(Data)):8>>,
+ Data],
+ _R = hci_drv:send(State#state.hci,Pkt),
+ %% io:format("send pkt ~p = ~p\n", [Pkt,_R]),
+ {noreply, State#state { from = From,
+ saved_filter = OldFilter,
+ current_opcode = Opcode,
+ current_event = Event,
+ current_decode = Decode
+ }};
+ Error ->
+ {reply, Error, State}
+ end;
+
+handle_call(get_info, _From, State) ->
+ {reply, hci_drv:get_dev_info(State#state.hci, State#state.devid), State};
+handle_call(get_conn_list, _From, State) ->
+ {reply, hci_drv:get_conn_list(State#state.hci, State#state.devid), State};
+handle_call({get_conn_info,Addr,Type}, _From, State) ->
+ {reply, hci_drv:get_conn_info(State#state.hci,Addr,Type), State};
+handle_call({get_auth_info,Addr},_From, State) ->
+ {reply, hci_drv:get_auth_info(State#state.hci, Addr), State};
+handle_call(close, _From, State) ->
+ hci_drv:close(State#state.hci),
+ {stop, normal, ok, State#state { hci=undefined }};
+handle_call({debug,Level}, _From, State) ->
+ {reply, hci_drv:debug(State#state.hci, Level), State};
+handle_call(_Request, _From, State) ->
+ {reply, {error,bad_call}, State}.
+
+%%--------------------------------------------------------------------
+%% @private
+%% @doc
+%% Handling cast messages
+%%
+%% @spec handle_cast(Msg, State) -> {noreply, State} |
+%% {noreply, State, Timeout} |
+%% {stop, Reason, State}
+%% @end
+%%--------------------------------------------------------------------
+handle_cast(_Msg, State) ->
+ {noreply, State}.
+
+%%--------------------------------------------------------------------
+%% @private
+%% @doc
+%% Handling all non call/cast messages
+%%
+%% @spec handle_info(Info, State) -> {noreply, State} |
+%% {noreply, State, Timeout} |
+%% {stop, Reason, State}
+%% @end
+%%--------------------------------------------------------------------
+handle_info({Hci,{data,_D0= <<_:8,Evt:8,Plen:8,Packet:Plen/binary,_R/binary>>}},
+ State) when Hci =:= State#state.hci ->
+ case Evt of
+ ?EVT_CMD_COMPLETE -> %% Fixme: match opcode
+ %%io:format("EVT_CMD_COMPLETE, data ~p, rest=~p\n", [_D0,_R]),
+ <<?evt_cmd_complete_bin(_Ncmd,_Opcode),Reply/binary>> = Packet,
+ %% io:format("Ncmd=~w,Opcode=~p,Reply=~p\n",[_Ncmd,_Opcode,Reply]),
+ State1 = do_reply(Reply, State),
+ {noreply, State1 };
+
+ ?EVT_CMD_STATUS -> %% Fixme: match opcode
+ %% io:format("EVT_CMD_STATUS, data ~p, rest=~p\n", [_D0,_R]),
+ <<?evt_cmd_status_bin(_Status,_Ncmd,_Opcode),Reply/binary>> = Packet,
+ %% io:format("Status=~w,Ncmd=~w,Opcode=~p,Reply=~p\n",[_Status,_Ncmd,_Opcode,Reply]),
+ State1 = do_reply(Reply, State),
+ {noreply, State1 };
+
+ ?EVT_REMOTE_NAME_REQ_COMPLETE ->
+ %% io:format("EVT_REMOTE_NAME_REQ_COMPLETE,data ~p,rest=~p\n", [_D0,_R]),
+ <<?evt_remote_name_req_complete_bin(Status,Bdaddr,Name)>> = Packet,
+ NameComplete = #evt_remote_name_req_complete { status=Status,
+ bdaddr=Bdaddr,
+ name=cname(Name)},
+ gen_server:reply(State#state.from, {ok, NameComplete}),
+ hci_drv:set_filter(State#state.hci,
+ State#state.saved_filter),
+ {noreply, State#state { from = undefined,
+ saved_filter = undefined }};
+ ?EVT_LE_META_EVENT ->
+ %% io:format("EVT_LE_META_EVENT, data ~p, rest=~p\n", [_D0,_R]),
+ <<?evt_le_meta_event_bin(Subevent,_D1),LePacket/binary>> = Packet,
+ Le = hci_api:decode_le(Subevent, LePacket),
+ gen_server:reply(State#state.from, {ok, Le}),
+ hci_drv:set_filter(State#state.hci,
+ State#state.saved_filter),
+ {noreply, State#state { from = undefined,
+ saved_filter = undefined }};
+ _ ->
+ try hci_api:decode(Evt,Packet) of
+ ReplyEvent ->
+ %% io:format("ReplyEvent ~p\n", [ReplyEvent]),
+ %% fixme: check that event match
+ gen_server:reply(State#state.from, {ok, ReplyEvent}),
+ hci_drv:set_filter(State#state.hci,
+ State#state.saved_filter),
+ {noreply, State#state { from = undefined,
+ saved_filter = undefined }}
+ catch
+ error:Reason ->
+ io:format("decode error: ~p ~p\n",
+ [Reason, erlang:get_stacktrace()]),
+ if State#state.from =:= undefined ->
+ {noreply, State};
+ true ->
+ hci_drv:set_filter(State#state.hci,
+ State#state.saved_filter),
+ gen_server:reply(State#state.from, {error, Reason}),
+ {noreply, State#state { from = undefined,
+ saved_filter = undefined }}
+ end
+ end
+ end;
+handle_info(_Info, State) ->
+ io:format("Got info: ~p\n", [_Info]),
+ {noreply, State}.
+
+%%--------------------------------------------------------------------
+%% @private
+%% @doc
+%% This function is called by a gen_server when it is about to
+%% terminate. It should be the opposite of Module:init/1 and do any
+%% necessary cleaning up. When it returns, the gen_server terminates
+%% with Reason. The return value is ignored.
+%%
+%% @spec terminate(Reason, State) -> void()
+%% @end
+%%--------------------------------------------------------------------
+terminate(_Reason, _State) ->
+ ok.
+
+%%--------------------------------------------------------------------
+%% @private
+%% @doc
+%% Convert process state when code is changed
+%%
+%% @spec code_change(OldVsn, State, Extra) -> {ok, NewState}
+%% @end
+%%--------------------------------------------------------------------
+code_change(_OldVsn, State, _Extra) ->
+ {ok, State}.
+
+%%%===================================================================
+%%% Internal functions
+%%%===================================================================
+
+cname(<<0,_/binary>>) -> [];
+cname(<<C,Cs/binary>>) -> [C|cname(Cs)];
+cname(<<>>) -> [].
+
+
+do_reply(Binary, State) ->
+ case State#state.current_decode of
+ undefined ->
+ hci_drv:set_filter(State#state.hci, State#state.saved_filter),
+ gen_server:reply(State#state.from, {ok, Binary}),
+ State#state { from=undefined, saved_filter=undefined };
+ F when is_function(F,1) ->
+ Decoded = F(Binary),
+ gen_server:reply(State#state.from, {ok, Decoded}),
+ hci_drv:set_filter(State#state.hci, State#state.saved_filter),
+ State#state { from = undefined,
+ current_event = undefined,
+ current_opcode = undefined,
+ current_decode = undefined,
+ saved_filter = undefined
+ }
+ end.
diff --git a/deps/bt/src/hci_test.erl b/deps/bt/src/hci_test.erl
new file mode 100644
index 0000000..015e98f
--- /dev/null
+++ b/deps/bt/src/hci_test.erl
@@ -0,0 +1,48 @@
+%%% @author Tony Rogvall <tony@up13>
+%%% @copyright (C) 2015, Tony Rogvall
+%%% @doc
+%%% Testing HCI socket
+%%% @end
+%%% Created : 14 Apr 2015 by Tony Rogvall <tony@up13>
+
+-module(hci_test).
+
+-compile(export_all).
+-include("hci_api.hrl").
+
+inquiry() ->
+ with_socket(0, fun(S) -> inquiry(S) end).
+
+pinquiry() ->
+ with_socket(0, fun(S) -> pinquiry(S) end).
+
+%% scan for 10*1.28 seconds, wait for max 5 replies
+inquiry(Hci) ->
+ Lap = <<16#33,16#8b,16#9e>>,
+ hci_api:inquiry(Hci, Lap, 10, 5).
+
+%% scan for 10*1.28 seconds, wait for max 5 replies
+pinquiry(Hci) ->
+ Lap = <<16#33,16#8b,16#9e>>,
+ Max = 100, Min = 50,
+ hci_api:periodic_inquiry(Hci, Max, Min, Lap, 10, 2).
+
+
+local_name() -> local_name(0).
+local_name(DevID) ->
+ with_socket(DevID, fun(S) -> hci_api:read_local_name(S) end).
+
+
+
+with_socket(DevID, Fun) ->
+ {ok,S} = hci_socket:open(DevID),
+ %% hci_socket:debug(S, debug),
+ try Fun(S) of
+ Result ->
+ hci_socket:close(S),
+ Result
+ catch
+ error:Error ->
+ hci_socket:close(S),
+ {error,Error}
+ end.
diff --git a/deps/bt/src/hci_util.erl b/deps/bt/src/hci_util.erl
new file mode 100644
index 0000000..626ab19
--- /dev/null
+++ b/deps/bt/src/hci_util.erl
@@ -0,0 +1,221 @@
+%%
+%% Utils used by HCI
+%%
+-module(hci_util).
+-compile(export_all).
+
+-include("include/hci_drv.hrl").
+
+
+c_string(Data) when is_binary(Data) ->
+ c_string_(binary_to_list(Data));
+c_string(Data) when is_list(Data) ->
+ c_string_(Data).
+
+c_string_([0|_]) -> [];
+c_string_([H|T]) -> [H|c_string_(T)].
+
+
+find_enum_name(Value, Flags, Default) when is_integer(Value) ->
+ case lists:keyfind(Value, 2, Flags) of
+ false -> Default;
+ {Name, _Value} -> Name
+ end.
+
+find_enum_name(Value, Flags) ->
+ case find_enum_name(Value, Flags, undefined) of
+ undefined -> erlang:error(badarg);
+ Name -> Name
+ end.
+
+find_enum_value(Name, Flags, Default) when is_atom(Name) ->
+ case lists:keyfind(atom_to_list(Name), 1, Flags) of
+ false -> Default;
+ {_, Value} -> Value
+ end;
+find_enum_value(Name, Flags, Default) when is_list(Name) ->
+ case lists:keyfind(Name, 1, Flags) of
+ false -> Default;
+ {_, Value} -> Value
+ end.
+
+find_enum_value(Name, Flags) ->
+ case find_enum_value(Name, Flags, undefined) of
+ undefined -> erlang:error(badarg);
+ Value -> Value
+ end.
+
+
+format_enum(Value, Kv) ->
+ case lists:keyfind(Value, 2, Kv) of
+ false -> "";
+ {Name, Value} -> Name
+ end.
+
+format_bits(Value, KvList) ->
+ format_bits_(Value, KvList, []).
+
+format_bits_(0, _Kv, Acc) ->
+ string:join(lists:reverse(Acc), ",");
+format_bits_(Value, [{Key,Bits}|Kv], Acc) ->
+ if Value band Bits =:= Bits ->
+ format_bits_(Value band (bnot Bits),Kv,[Key|Acc]);
+ true ->
+ format_bits_(Value, Kv, Acc)
+ end;
+format_bits_(Value, [], Acc) ->
+ Acc1 = [integer_to_list(Value) | Acc],
+ string:join(lists:reverse(Acc1), ",").
+
+format_hci_dev_info(#hci_dev_info {
+ dev_id = _Dev_id,
+ name = Name,
+ bdaddr = BdAddr,
+ flags = Flags,
+ type = Type,
+ features = _Features,
+ pkt_type = _Pkt_type,
+ link_policy = _Link_policy,
+ link_mode = _Link_mode,
+ acl_mtu = Acl_mtu,
+ acl_pkts = Acl_pkts,
+ sco_mtu = Sco_mtu,
+ sco_pkts = Sco_pkts,
+ stat = S}) ->
+ Bus = find_enum_name(Type band 16#0f,
+ kv_dev_bus(), "UNKNOWN"),
+ Type1 = find_enum_name((Type band 16#30) bsr 4,
+ kv_dev_type(), "UNKNOWN"),
+ io_lib:format(
+ "~s: Type: ~s Bus: ~s\n"
+ "\tBD Address: ~s ACL MTU: ~w:~w SCO MTU: ~w:~w\n"
+ "\t~s\n"
+ "~s\n",
+ [Name, Type1, Bus,
+ bt:format_address(BdAddr),
+ Acl_mtu, Acl_pkts, Sco_mtu, Sco_pkts,
+ format_bits(Flags, kv_hci_dev_info_flags()),
+ format_hci_dev_stats(S)]).
+
+format_hci_dev_stats(#hci_dev_stats {
+ err_rx = Err_rx,
+ err_tx = Err_tx,
+ cmd_tx = Cmd_tx,
+ evt_rx = Evt_rx,
+ acl_tx = Acl_tx,
+ acl_rx = Acl_rx,
+ sco_tx = Sco_tx,
+ sco_rx = Sco_rx,
+ byte_rx = Byte_rx,
+ byte_tx = Byte_tx }) ->
+ io_lib:format(
+ "\tRX bytes:~w acl:~w sco:~w events:~w errors:~w\n"
+ "\tTX bytes:~w acl:~w sco:~w commands:~w errors:~w",
+ [Byte_rx, Acl_rx, Sco_rx, Evt_rx, Err_rx,
+ Byte_tx, Acl_tx, Sco_tx, Cmd_tx, Err_tx]).
+
+bdaddr({A,B,C,D,E,F}) ->
+ {ok,<<F,E,D,C,B,A>>};
+bdaddr(Bin = <<_,_,_,_,_,_>>) -> {ok,Bin}; %% assume binary format is internal!
+bdaddr(Addr) when is_list(Addr) ->
+ case bt:getaddr(Addr) of
+ {ok,{A,B,C,D,E,F}} ->
+ {ok,<<F,E,D,C,B,A>>};
+ Error ->
+ Error
+ end.
+
+kv_dev_type() ->
+[
+ {"BR/EDR", ?HCI_BREDR},
+ {"AMP", ?HCI_AMP}
+].
+
+kv_dev_bus() ->
+[
+ {"VIRTUAL",?HCI_VIRTUAL},
+ {"USB", ?HCI_USB },
+ {"PCCARD", ?HCI_PCCARD },
+ {"UART", ?HCI_UART },
+ {"RS232",?HCI_RS232},
+ {"PCI", ?HCI_PCI},
+ {"SDIO", ?HCI_SDIO}
+].
+
+
+%% scan modes
+kv_scan() ->
+[
+ { "OFF", ?SCAN_DISABLED },
+ { "ISCAN", ?SCAN_INQUIRY },
+ { "PSCAN", ?SCAN_PAGE },
+ { "PISCAN", ?SCAN_PAGE bor ?SCAN_INQUIRY }
+].
+
+%% HCI device flags
+kv_hci_dev_info_flags() ->
+[
+ { "UP", (1 bsl ?HCI_UP) },
+ { "INIT", (1 bsl ?HCI_INIT) },
+ { "RUNNING", (1 bsl ?HCI_RUNNING) },
+ { "PSCAN", (1 bsl ?HCI_PSCAN) },
+ { "ISCAN", (1 bsl ?HCI_ISCAN) },
+ { "AUTH", (1 bsl ?HCI_AUTH) },
+ { "ENCRYPT", (1 bsl ?HCI_ENCRYPT) },
+ { "INQUIRY", (1 bsl ?HCI_INQUIRY) },
+ { "RAW", (1 bsl ?HCI_RAW) }
+].
+
+kv_pkt_ptype() ->
+[
+ { "DM1", ?HCI_DM1 },
+ { "DM3", ?HCI_DM3 },
+ { "DM5", ?HCI_DM5 },
+ { "DH1", ?HCI_DH1 },
+ { "DH3", ?HCI_DH3 },
+ { "DH5", ?HCI_DH5 },
+ { "HV1", ?HCI_HV1 },
+ { "HV2", ?HCI_HV2 },
+ { "HV3", ?HCI_HV3 },
+ { "2-DH1", ?HCI_2DH1 },
+ { "2-DH3", ?HCI_2DH3 },
+ { "2-DH5", ?HCI_2DH5 },
+ { "3-DH1", ?HCI_3DH1 },
+ { "3-DH3", ?HCI_3DH3 },
+ { "3-DH5", ?HCI_3DH5 }
+].
+
+kv_sco_ptype() ->
+[
+ { "HV1", 16#0001 },
+ { "HV2", 16#0002 },
+ { "HV3", 16#0004 },
+ { "EV3", ?HCI_EV3 },
+ { "EV4", ?HCI_EV4 },
+ { "EV5", ?HCI_EV5 },
+ { "2-EV3", ?HCI_2EV3 },
+ { "2-EV5", ?HCI_2EV5 },
+ { "3-EV3", ?HCI_3EV3 },
+ { "3-EV5", ?HCI_3EV5 }
+].
+
+kv_link_policy() ->
+[
+ { "NONE", 0 },
+ { "RSWITCH", ?HCI_LP_RSWITCH },
+ { "HOLD", ?HCI_LP_HOLD },
+ { "SNIFF", ?HCI_LP_SNIFF },
+ { "PARK", ?HCI_LP_PARK }
+].
+
+kv_link_mode() ->
+[
+ { "NONE", 0 },
+ { "ACCEPT", ?HCI_LM_ACCEPT },
+ { "MASTER", ?HCI_LM_MASTER },
+ { "AUTH", ?HCI_LM_AUTH },
+ { "ENCRYPT", ?HCI_LM_ENCRYPT },
+ { "TRUSTED", ?HCI_LM_TRUSTED },
+ { "RELIABLE", ?HCI_LM_RELIABLE },
+ { "SECURE", ?HCI_LM_SECURE }
+].
diff --git a/deps/bt/src/l2cap.erl b/deps/bt/src/l2cap.erl
new file mode 100644
index 0000000..3bdf96a
--- /dev/null
+++ b/deps/bt/src/l2cap.erl
@@ -0,0 +1,44 @@
+%%%---- BEGIN COPYRIGHT -------------------------------------------------------
+%%%
+%%% Copyright (C) 2006 - 2014, Rogvall Invest AB, <tony@rogvall.se>
+%%%
+%%% This software is licensed as described in the file COPYRIGHT, which
+%%% you should have received as part of this distribution. The terms
+%%% are also available at http://www.rogvall.se/docs/copyright.txt.
+%%%
+%%% You may opt to use, copy, modify, merge, publish, distribute and/or sell
+%%% copies of the Software, and permit persons to whom the Software is
+%%% furnished to do so, under the terms of the COPYRIGHT file.
+%%%
+%%% This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY
+%%% KIND, either express or implied.
+%%%
+%%%---- END COPYRIGHT ---------------------------------------------------------
+%%% File : l2cap.erl
+%%% Author : Tony Rogvall <tony@pbook.synap.se>
+%%% Description : L2CAP wrapper
+%%% Created : 19 Jul 2006 by Tony Rogvall <tony@pbook.synap.se>
+
+-module(l2cap).
+
+-export([open/2, close/1, send/2, listen/1, accept/1, accept/2]).
+
+open(Address, Psm) ->
+ bt_drv:l2cap_open(Address, Psm).
+
+close(L2CAP) ->
+ bt_drv:l2cap_close(L2CAP).
+
+send(L2CAP, Data) ->
+ bt_drv:l2cap_send(L2CAP, Data).
+
+listen(Psm) ->
+ bt_drv:l2cap_listen(Psm).
+
+accept(ListenRef) ->
+ bt_drv:l2cap_accept(ListenRef).
+
+accept(ListenRef,Timeout) when Timeout==infinity;
+ is_integer(Timeout),Timeout>0 ->
+ bt_drv:l2cap_accept(ListenRef,Timeout).
+
diff --git a/deps/bt/src/obex.erl b/deps/bt/src/obex.erl
new file mode 100644
index 0000000..c523413
--- /dev/null
+++ b/deps/bt/src/obex.erl
@@ -0,0 +1,1696 @@
+%%%---- BEGIN COPYRIGHT -------------------------------------------------------
+%%%
+%%% Copyright (C) 2006 - 2014, Rogvall Invest AB, <tony@rogvall.se>
+%%%
+%%% This software is licensed as described in the file COPYRIGHT, which
+%%% you should have received as part of this distribution. The terms
+%%% are also available at http://www.rogvall.se/docs/copyright.txt.
+%%%
+%%% You may opt to use, copy, modify, merge, publish, distribute and/or sell
+%%% copies of the Software, and permit persons to whom the Software is
+%%% furnished to do so, under the terms of the COPYRIGHT file.
+%%%
+%%% This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY
+%%% KIND, either express or implied.
+%%%
+%%%---- END COPYRIGHT ---------------------------------------------------------
+%%%-------------------------------------------------------------------
+%%% File : obex.erl
+%%% Author : Tony Rogvall <tony@PBook.local>
+%%% Description : OBEX implemenation
+%%%
+%%% Created : 19 May 2006 by Tony Rogvall <tony@PBook.local>
+%%%-------------------------------------------------------------------
+-module(obex).
+
+-behaviour(gen_server).
+
+%% API
+-export([open/2, open/3, close/1]).
+-export([connect/1, connect/2]).
+-export([disconnect/1, disconnect/2]).
+-export([put_chunk_start/2, put_chunk/2, put_chunk_end/2]).
+-export([put/3]).
+-export([get_chunk_start/2, get_chunk/1]).
+-export([get/2]).
+-export([abort/2]).
+-export([setpath/2, setpath/3]).
+-export([command/2]).
+%% SERVER API
+-export([server/2, server_link/2]).
+-export([server_init/6]).
+
+
+%% Xtra - API
+-export([connect_dm_client/1]).
+-export([connect_syncml_client/1]).
+-export([connect_folder_browsing/1]).
+-export([set_cwd/2, make_dir/2]).
+-export([list_dir/1]).
+-export([capability/1, object_profile/2]).
+-export([client/4, client_link/4]).
+-export([client/3, client_link/3]).
+-export([time_iso/1, utc_time_iso/1]).
+
+%% Debug
+-export([encode_headers/1, decode_headers/1, decode_packet/2]).
+-export([size_header/2, size_headers/1]).
+-export([encode_header/2]).
+-export([make_packets/4, make_get_packets/2]).
+
+-import(lists, [reverse/1, member/2, foldl/3]).
+
+-include("../include/obex.hrl").
+-include("../include/uuid.hrl").
+-include("../include/sdp.hrl").
+
+%% gen_server callbacks
+-export([init/1, handle_call/3, handle_cast/2, handle_info/2,
+ terminate/2, code_change/3]).
+
+-ifdef(debug).
+-define(dbg(Fmt,As), io:format("~s:~w:" Fmt "\n", [?FILE,?LINE | As])).
+-else.
+-define(dbg(Fmt,As), ok).
+-endif.
+
+
+-record(obex_packet,
+ {
+ final, %% 0 or 1
+ opcode, %% 7 bit opcode
+ args, %% extra args (list)
+ headers, %% headers
+ callback %% packet reply callback
+ }).
+
+-record(s,
+ {
+ role, %% client | server
+ transport, %% transport module
+ ref, %% transport reference
+ sref, %% pid - server handle
+ smod, %% server module
+ sstate, %% server state
+ channel, %% channel/port number
+ id, %% connection ID when connected
+ %% Host side info
+ host_mtu = ?OBEX_DEFAULT_MTU, %% our recieve buffer size
+ host_flags = 0, %% out Flags
+ host_version = ?OBEX_VERSION, %% our OBEX version
+ %% Peer side info
+ peer_mtu = ?OBEX_DEFAULT_MTU, %% peer recive buffer size
+ peer_flags = 0, %% peer flags
+ peer_version = 0, %% peer OBEX version
+ %% Receive buffer
+ recv_remain = -3, %% data remain to be received
+ recv_buffer = <<>>, %% input buffer
+ recv_queue = [], %% input packet queue
+ %% Send buffer
+ send_queue = [], %% output packet queue
+ %%
+ opts=[] %% obex options
+ }).
+
+
+-define(size_byte_sequence(Value),
+ if is_binary((Value)) -> size((Value));
+ is_list((Value)) -> length((Value))
+ end).
+
+-define(size_unicode(Value),
+ if (Value) == "" -> 0;
+ true -> 2*(length((Value))+1)
+ end).
+
+%% Note: The length field includes it self and the tag!
+-define(enc_byte_sequence(ID, Value),
+ if is_binary((Value)) ->
+ [<<(ID):8, (size((Value))+3):16>>, Value];
+ is_list((Value)) ->
+ [<<(ID):8, (length((Value))+3):16>>, Value]
+ end).
+
+%% For null terminate byte-ascii sequences
+-define(enc_null_sequence(ID, Value),
+ if is_binary((Value)) ->
+ [<<(ID):8, (size((Value))+4):16>>, Value, 0];
+ is_list((Value)) ->
+ [<<(ID):8, (length((Value))+4):16>>, Value, 0]
+ end).
+
+%%====================================================================
+%% API
+%%====================================================================
+open(Address, Channel) ->
+ open(Address, Channel, [{transport,rfcomm}]).
+
+open(Address, Channel, Opts) ->
+ client_link(Address, Channel, Opts).
+
+close(Obex) ->
+ gen_server:call(Obex, close).
+
+%%
+%% OBEX CONNECT
+%%
+connect(Obex) ->
+ connect(Obex, []).
+
+connect(Obex, Headers) ->
+ gen_server:call(Obex, {connect,Headers}, 10000).
+
+%%
+%% OBEX DISCONNECT
+%%
+disconnect(Obex) ->
+ disconnect(Obex, []).
+
+disconnect(Obex, Headers) ->
+ gen_server:call(Obex, {disconnect,Headers}, 10000).
+
+%%
+%% OBEX PUT
+%%
+put_chunk_start(Obex, Headers) ->
+ gen_server:call(Obex, {put_chunk_start, Headers}, 10000).
+put_chunk(Obex, Data) ->
+ gen_server:call(Obex, {put_chunk, Data}, 10000).
+put_chunk_end(Obex, Data) ->
+ gen_server:call(Obex, {put_chunk_end, Data}, 10000).
+
+put(Obex, Headers, Body) ->
+ case put_chunk_start(Obex, Headers) of
+ continue ->
+ put_chunk_end(Obex,Body);
+ Error -> Error
+ end.
+
+%%
+%% OBEX GET
+%%
+get_chunk_start(Obex, Headers) ->
+ gen_server:call(Obex, {get_chunk_start, Headers}, 10000).
+get_chunk(Obex) ->
+ gen_server:call(Obex, get_chunk, 10000).
+
+get(Obex, Headers) ->
+ case get_chunk_start(Obex, Headers) of
+ {continue,Hs} ->
+ get_continue(Obex,[Hs]);
+ {ok,Hs} -> get_reply([Hs]);
+ Error -> Error
+ end.
+
+get_continue(Obex, Acc) ->
+ case get_chunk(Obex) of
+ {continue,Hs} -> get_continue(Obex,[Hs|Acc]);
+ {ok,Hs} -> get_reply([Hs|Acc]);
+ Error -> Error
+ end.
+
+%% Generate a sorted and useful combined reply
+get_reply([]) ->
+ {ok,[],<<>>};
+get_reply([Hs|HsList]) ->
+ get_reply(reverse(Hs),HsList,[],[]).
+
+get_reply([],[],HAcc,BAcc) ->
+ {ok,reverse(HAcc),list_to_binary(BAcc)};
+get_reply([{bodyEnd,B}|Hs],HsList,HAcc,BAcc) ->
+ get_reply(Hs, HsList, HAcc, [B | BAcc]);
+get_reply([{body,B}|Hs],HsList,HAcc,BAcc) ->
+ get_reply(Hs, HsList, HAcc, [B | BAcc]);
+get_reply([{H,V}|Hs],HsList,HAcc,BAcc) ->
+ get_reply(Hs, HsList, [{H,V}|HAcc], BAcc);
+get_reply([], [Hs|HsList], HAcc, BAcc) ->
+ get_reply(reverse(Hs), HsList, HAcc, BAcc).
+
+%%
+%% OBEX ABORT
+%%
+abort(Obex, Headers) ->
+ gen_server:call(Obex, {abort,Headers}, 10000).
+
+%%
+%% OBEX SETPATH
+%%
+setpath(Obex,Hs) ->
+ setpath(Obex,[],Hs).
+
+setpath(Obex,Opts,Hs) ->
+ gen_server:call(Obex, {setpath,Opts,Hs}, 10000).
+
+%%
+%% COMMAND
+%%
+command(Obex, Hs) ->
+ gen_server:call(Obex, {command, Hs}, 10000).
+
+%%
+%% UTIL COMMANDS
+%%
+
+%% Special - simpified connectes
+connect_dm_client(Obex) ->
+ connect(Obex, [{target,?TARGET_SYNCML_DM}]).
+
+connect_syncml_client(Obex) ->
+ connect(Obex, [{target,?TARGET_SYNCML_SYNC}]).
+
+connect_folder_browsing(Obex) ->
+ connect(Obex, [{target,?TARGET_FOLDER_BROWSING}]).
+
+%% requires - connect_folder_browsing
+set_cwd(Obex, "..") ->
+ setpath(Obex,[backup,dont_create],[]);
+set_cwd(Obex, Dir) ->
+ setpath(Obex,[dont_create],[{name,Dir}]).
+
+%% both connect_folder_browsing and file transfer
+make_dir(Obex, Dir) ->
+ setpath(Obex,[],[{name,Dir}]).
+
+%% requires - connect_folder_browsing
+list_dir(Obex) ->
+ case get(Obex, [{type,"x-obex/folder-listing"}]) of
+ {ok,_Hs,Bin} ->
+ makeup:string(Bin);
+ Error ->
+ Error
+ end.
+
+%% requires - connect []
+capability(Obex) ->
+ get(Obex, [{type,"x-obex/capability"}]).
+
+%% requires - connect []
+object_profile(Obex, MimeType) ->
+ get(Obex, [{type,"x-obex/object-profile"},
+ {name,MimeType}]).
+
+%%
+%% START Functions
+%%
+client(Address, Channel, Opts) ->
+ gen_server:start(?MODULE, [Address,Channel,Opts], []).
+
+client(Name, Address, Channel, Opts) ->
+ gen_server:start({local,Name},?MODULE,[Address,Channel,Opts],[]).
+
+client_link(Address, Channel, Opts) ->
+ gen_server:start_link(?MODULE, [Address,Channel,Opts], []).
+
+client_link(Name, Address, Channel, Opts) ->
+ gen_server:start_link({local,Name},?MODULE,[Address,Channel,Opts],[]).
+
+%%====================================================================
+%% gen_server callbacks
+%%====================================================================
+
+%%--------------------------------------------------------------------
+%% Function: init(Args) -> {ok, State} |
+%% {ok, State, Timeout} |
+%% ignore |
+%% {stop, Reason}
+%% Description: Initiates the server
+%%--------------------------------------------------------------------
+init([Address,Channel,Opts]) ->
+ case transport(Opts, Address) of
+ undefined -> {stop, {error, unknown_transport}};
+ Transport ->
+ Mod = transport_module(Transport),
+ S0 = #s { role = client,
+ transport = Mod,
+ host_version = ?OBEX_VERSION,
+ host_mtu = getopt(mtu,Opts,?OBEX_DEFAULT_MTU),
+ host_flags = getopt(mtu,Opts,0),
+ opts = Opts
+ },
+ TransOpts = getopt(transport_options, Opts, [binary]),
+ case catch Mod:open(Address,Channel,TransOpts) of
+ {ok,Ref} ->
+ {ok, S0#s{ ref=Ref }};
+ {'EXIT',Reason} ->
+ {stop, {error, Reason}};
+ Error ->
+ {stop, Error}
+ end
+ end.
+
+%%--------------------------------------------------------------------
+%% Function: %% handle_call(Request, From, State) -> {reply, Reply, State} |
+%% {reply, Reply, State, Timeout} |
+%% {noreply, State} |
+%% {noreply, State, Timeout} |
+%% {stop, Reason, Reply, State} |
+%% {stop, Reason, State}
+%% Description: Handling call messages
+%%--------------------------------------------------------------------
+
+handle_call({connect,Hs},From,S) ->
+ Args = [S#s.host_version, S#s.host_flags, S#s.host_mtu],
+ P = #obex_packet { final = 1,
+ opcode = connect,
+ args = Args,
+ headers = Hs,
+ callback = fun(S0,#obex_packet { opcode={ok,What},
+ headers=Hs1,
+ args=[Version,Flags1,Mtu]
+ }) ->
+ %% Keep track on connectionID
+ %% this simplifies alot...
+ ID=getopt(connectionID,Hs1),
+ gen_server:reply(From, {ok,What}),
+ {noreply,S0#s { id = ID,
+ peer_mtu = Mtu,
+ peer_flags=Flags1,
+ peer_version = Version }};
+ (S0,#obex_packet { opcode=Error }) ->
+ gen_server:reply(From, Error),
+ {noreply, S0}
+ end
+ },
+ send_packet(P, S);
+
+handle_call({disconnect,Hs}, From, S) ->
+ P = #obex_packet { final = 1,
+ opcode = disconnect,
+ headers = prepare_headers(Hs,S),
+ callback = fun(S0,#obex_packet { opcode=Reply }) ->
+ gen_server:reply(From, Reply),
+ {noreply,S0#s { id=undefined }}
+ end
+ },
+ send_packet(P, S);
+
+handle_call({abort,Hs}, From, S) ->
+ P = #obex_packet { final = 1,
+ opcode = abort,
+ headers = prepare_headers(Hs,S), %% needed?
+ callback = fun(S0,#obex_packet { opcode=Reply }) ->
+ gen_server:reply(From, Reply),
+ {noreply,S0}
+ end
+ },
+ send_packet(P, S);
+
+handle_call({get_chunk_start,Hs}, From, S) ->
+ Hs1 = prepare_headers(Hs, S),
+ handle_get_chunk_start(Hs1, From, S);
+
+handle_call(get_chunk, From, S) ->
+ handle_get_chunk(From, S);
+
+handle_call({put_chunk_start,Hs}, From, S) ->
+ Hs1 = prepare_headers(Hs, S),
+ handle_put_chunk_start(Hs1, From, S);
+
+handle_call({put_chunk,Data}, From, S) ->
+ handle_put_chunk(Data, From, S);
+
+handle_call({put_chunk_end,Data}, From, S) ->
+ handle_put_chunk_end(Data, From, S);
+
+handle_call({setpath,Opts,Hs}, From, S) ->
+ %% MUST FIT in one packet
+ Constants = 0, %% reserved MUST be 0 (version=1.2)
+ Flags = case member(backup, Opts) of
+ true -> 16#01;
+ false -> 16#00
+ end bor
+ case member(dont_create, Opts) of
+ true -> 16#02;
+ false -> 16#00
+ end,
+ P = #obex_packet { final = 1,
+ opcode = setpath,
+ args = [Flags, Constants],
+ headers = prepare_headers(Hs, S),
+ callback = fun(S0,#obex_packet { opcode=Reply }) ->
+ gen_server:reply(From, Reply),
+ {noreply,S0}
+ end
+ },
+ send_packet(P, S);
+
+handle_call({command,Hs}, From, S) ->
+ P = #obex_packet { final = 1,
+ opcode = command,
+ headers = prepare_headers(Hs, S),
+ callback = fun (S0,_) ->
+ gen_server:reply(From, ok),
+ {noreply,S0}
+ end
+ },
+ send_packet(P, S);
+
+handle_call(close, From, S) ->
+ if S#s.ref == undefined ->
+ {stop, normal, ok, S};
+ true ->
+ Callback = fun(S0,_) ->
+ gen_server:reply(From, ok),
+ ?dbg("Transport close: ~w\n", [S#s.ref]),
+ (S#s.transport):close(S#s.ref),
+ {stop, normal, S0#s { ref=undefined }}
+ end,
+ P = #obex_packet { final = 1,
+ callback = Callback,
+ opcode = undefined, %% sync packet
+ headers = [] },
+ send_packet(P, S)
+ end;
+
+handle_call(_Request, _From, S) ->
+ {reply, {error, bad_call}, S}.
+
+%%--------------------------------------------------------------------
+%% Function: handle_cast(Msg, State) -> {noreply, State} |
+%% {noreply, State, Timeout} |
+%% {stop, Reason, State}
+%% Description: Handling cast messages
+%%--------------------------------------------------------------------
+handle_cast(_Msg, S) ->
+ {noreply, S}.
+
+%%--------------------------------------------------------------------
+%% Function: handle_info(Info, State) -> {noreply, State} |
+%% {noreply, State, Timeout} |
+%% {stop, Reason, State}
+%% Description: Handling all non call/cast messages
+%%--------------------------------------------------------------------
+
+handle_info({rfcomm,Ref,{data,Data}}, S) when S#s.ref == Ref ->
+ ?dbg("RFCOMM: Packet received: ~p\n", [Data]),
+ recv(Data, S);
+handle_info({tcp,Ref,Data}, S) when S#s.ref == Ref ->
+ ?dbg("TCP: Packet received: ~p\n", [Data]),
+ recv(Data, S);
+handle_info({rfcomm,Ref,closed}, S) when S#s.ref == Ref ->
+ ?dbg("RFCOMM: closed\n", []),
+ recv_closed(S);
+handle_info({tcp_closed,Ref}, S) when S#s.ref == Ref ->
+ ?dbg("TCP: closed\n", []),
+ recv_closed(S);
+handle_info(_Info, S) ->
+ ?dbg("Unhandled INFO ~p\n", [_Info]),
+ {noreply, S}.
+
+%%--------------------------------------------------------------------
+%% Function: terminate(Reason, State) -> void()
+%% Description: This function is called by a gen_server when it is about to
+%% terminate. It should be the opposite of Module:init/1 and do any necessary
+%% cleaning up. When it returns, the gen_server terminates with Reason.
+%% The return value is ignored.
+%%--------------------------------------------------------------------
+terminate(_Reason, S) ->
+ if S#s.ref == undefined ->
+ ok;
+ true ->
+ ?dbg("Transport close: ~w\n", [S#s.ref]),
+ (S#s.transport):close(S#s.ref)
+ end.
+
+
+%%--------------------------------------------------------------------
+%% Func: code_change(OldVsn, State, Extra) -> {ok, NewState}
+%% Description: Convert process state when code is changed
+%%--------------------------------------------------------------------
+code_change(_OldVsn, State, _Extra) ->
+ {ok, State}.
+
+%%--------------------------------------------------------------------
+%%% Internal functions
+%%--------------------------------------------------------------------
+
+%% Get transport from options
+transport(Opts) ->
+ transport(Opts, undefined).
+
+transport(Opts, Address) ->
+ case getopt(transport, Opts) of
+ undefined ->
+ if Address == undefined -> rfcomm;
+ true ->
+ case inet_parse:address(Address) of
+ {ok, _} -> tcp;
+ _ ->
+ case bt_util:getaddr(Address) of
+ {ok,_} -> rfcomm;
+ _ ->
+ case inet_parse:domain(Address) of
+ true -> tcp;
+ _ -> undefined
+ end
+ end
+ end
+ end;
+ Transport when is_atom(Transport) -> Transport;
+ _ -> undefined
+ end.
+
+transport_module(rfcomm) -> obex_rfcomm;
+transport_module(tcp) -> obex_tcp;
+transport_module(T) -> list_to_atom("obex_" ++ atom_to_list(T)).
+
+server_link(Channel, Opts) ->
+ case transport(Opts) of
+ undefined ->
+ {error, unknown_transport};
+ Transport ->
+ Mod = transport_module(Transport),
+ Mod:server_link(Channel, Opts)
+ end.
+
+server(Channel, Opts) ->
+ case transport(Opts) of
+ undefined ->
+ {error, unknown_transport};
+ Transport ->
+ Mod = transport_module(Transport),
+ Mod:server(Channel, Opts)
+ end.
+
+
+%% A new connection from Transport backend
+server_init(SrvHandle, Ref, _Address, Channel, Transport, Opts) ->
+ ?dbg("obex [~w] Accept from ~w on channel=~w\n",
+ [Transport,_Address,Channel]),
+ Wait = #obex_packet { final = 1,
+ opcode = wait,
+ callback = fun handle_connect/2 },
+ S = #s { role = server,
+ sref = SrvHandle,
+ transport = Transport,
+ ref = Ref,
+ channel = Channel,
+ host_mtu = getopt(mtu,Opts,?OBEX_DEFAULT_MTU),
+ host_flags = getopt(flags,Opts,0),
+ host_version = ?OBEX_VERSION,
+ opts = Opts,
+ send_queue = [Wait]
+ },
+ gen_server:enter_loop(?MODULE, [], S).
+
+%% handle connect request
+handle_connect(S0,#obex_packet { opcode=connect,
+ headers=Hs,
+ args=[Version,Flags,Mtu] }) ->
+ ?dbg("handle_connect: version=~w, flags=~w, mtu=~w, hs=~w",
+ [Version,Flags,Mtu,Hs]),
+ S = S0#s { peer_version=Version, peer_mtu=Mtu,peer_flags=Flags },
+ case lists:keysearch(target, 1, Hs) of
+ false ->
+ handle_connect_target(S, undefined, obex_generic);
+ {value,{_,Target}} ->
+ case (S#s.transport):lookup(S#s.sref, Target) of
+ {error, not_found} ->
+ Packet = #obex_packet { final = 1,
+ opcode = {error,not_found},
+ headers = [],
+ callback = fun handle_connect/2 },
+ send_packet(Packet, S);
+ {ok,Module} ->
+ handle_connect_target(S, Target, Module)
+ end
+ end;
+handle_connect(S,P=#obex_packet { opcode=_Opcode }) ->
+ ?dbg("handle_connect: opcode ~w", [_Opcode]),
+ TS = obex_generic:init(S#s.sref, S#s.opts, S#s.peer_mtu),
+ handle_request(S#s { smod=obex_generic, sstate=TS},P).
+
+%%
+%% Connected to target handled by Mod
+%%
+handle_connect_target(S, Target, Mod) ->
+ TS = Mod:init(S#s.sref, S#s.opts, S#s.peer_mtu),
+ case target_connect(Target, S#s { smod=Mod, sstate=TS}) of
+ {reply,{ok,Hs}, S1} ->
+ Packet = #obex_packet { final = 1,
+ opcode = connect_response,
+ args=[S#s.host_version,
+ S#s.host_flags,
+ S#s.host_mtu],
+ headers = Hs,
+ callback = fun handle_request/2 },
+ send_packet(Packet, S1);
+ {reply,Error,S1} ->
+ Packet = #obex_packet { final = 1,
+ opcode = Error,
+ headers = [],
+ callback = fun handle_request/2 },
+ send_packet(Packet, S1)
+ end.
+
+%%
+%% Collect ALL get headers before doing the target callback
+%%
+next_get(HsAcc) ->
+ fun(S, #obex_packet { final=0, opcode=get, headers=Hs }) ->
+ ?dbg("GET continue request ~p\n", [Hs]),
+ Packet = #obex_packet { final=0, opcode=continue, headers=[],
+ callback=next_get(HsAcc++Hs) },
+ send_packet(Packet, S);
+ (S, P=#obex_packet { final=1, opcode=get, headers=Hs }) ->
+ handle_request(S,P#obex_packet { headers=HsAcc++Hs });
+ (S, _P=#obex_packet { final=1, opcode=abort }) ->
+ Packet = #obex_packet { final=0, opcode={ok,success}, headers=[],
+ callback=fun handle_request/2 },
+ send_packet(Packet, S);
+ (S, _P) ->
+ ?dbg("Got ~p while collecting GET headers",[_P#obex_packet.opcode]),
+ Packet = #obex_packet { final=0, opcode={error,conflict}, headers=[],
+ callback=fun handle_request/2 },
+ send_packet(Packet, S)
+ end.
+%%
+%% Collect ALL put headers before doing the target callback
+%%
+next_put(HsAcc) ->
+ fun(S, #obex_packet { final=0, opcode=put, headers=Hs }) ->
+ ?dbg("PUT continue request ~p\n", [Hs]),
+ continue_put(S,Hs,HsAcc);
+ (S, #obex_packet { final=1, opcode=put, headers=Hs }) ->
+ final_put(S,Hs,HsAcc);
+ (S, _P=#obex_packet { final=1, opcode=abort }) ->
+ Packet = #obex_packet { final=0, opcode={ok,success}, headers=[],
+ callback=fun handle_request/2 },
+ send_packet(Packet, S);
+ (S, _P) ->
+ ?dbg("Got ~p while collecting PUT headers",[_P#obex_packet.opcode]),
+ Packet = #obex_packet { final=0, opcode={error,conflict},
+ headers=[],
+ callback=fun handle_request/2 },
+ send_packet(Packet, S)
+ end.
+
+
+continue_put(S,Hs,HsAcc) ->
+ case split_headers(Hs) of
+ {true,Hs1,Data} -> %% bodyEnd in non final? trailer?
+ ?dbg("got bodyEnd in continue put packet!", []),
+ {reply,Reply,S1} = target_put(HsAcc++Hs1, Data, S),
+ Packet = #obex_packet { final=1, opcode=Reply,
+ headers=[],
+ callback=fun handle_request/2
+ },
+ send_packet(Packet, S1);
+ {false,Hs1,<<>>} -> %% no data no end
+ Packet = #obex_packet { final=1, opcode=continue,
+ headers=[],
+ callback=next_put(HsAcc++Hs1)
+ },
+ send_packet(Packet,S);
+ {false,Hs1,Data} -> %% data found not end
+ {reply,Reply,S1} = target_put(HsAcc++Hs1, Data, S),
+ Packet = #obex_packet { final=1, opcode=Reply,
+ headers=[],
+ callback=fun handle_request/2 },
+ send_packet(Packet,S1)
+ end.
+
+final_put(S,Hs,HsAcc) ->
+ case split_headers(Hs) of
+ {true,Hs1,Data} ->
+ case target_put(HsAcc++Hs1, Data, S) of
+ {reply,continue,S1} ->
+ {reply,Reply,S2} = target_put([],<<>>,S1),
+ Packet = #obex_packet { final=1, opcode=Reply,
+ headers=[],
+ callback=fun handle_request/2
+ },
+ send_packet(Packet,S2);
+ {reply,Reply,S1} ->
+ Packet = #obex_packet { final=1, opcode=Reply,
+ headers=[],
+ callback=fun handle_request/2
+ },
+ send_packet(Packet,S1)
+ end;
+ {false,_Hs1,_Data} ->
+ ?dbg("missing bodyEnd in final put packet!", []),
+ Packet = #obex_packet { final=1, opcode={error,bad_request},
+ headers=[],
+ callback=fun handle_request/2},
+ send_packet(Packet, S)
+ end.
+
+%%
+%% Send GET reply packets
+%%
+send_reply_packets([], _End, S) ->
+ ?dbg("send_reply_packets: NO packets to send???",[]),
+ {noreply, S};
+send_reply_packets([P], End, S) ->
+ P1 = if End == true ->
+ P#obex_packet { final=1,opcode={ok,success},
+ callback=fun handle_request/2};
+ true ->
+ P#obex_packet { final=1, opcode=continue,
+ callback=fun handle_request/2}
+ end,
+ send_packet(P1, S);
+send_reply_packets([P|Ps], End, S) ->
+ Callback = fun(S0,_Pi=#obex_packet { opcode=get }) -> %% ignoring final!!!
+ send_reply_packets(Ps, End, S0);
+ (S0,Pi) ->
+ handle_request(S0, Pi)
+ end,
+ P1 = P#obex_packet { final=1, opcode=continue, callback=Callback },
+ send_packet(P1, S).
+
+%%
+%% Handle GET
+%%
+handle_request(S,#obex_packet { final=1, opcode=get, headers=Hs }) ->
+ ?dbg("GET final=1 request ~p\n", [Hs]),
+ case target_get(Hs, S) of
+ {reply,{continue,ReplyHs,Data},S1} ->
+ Ps = make_packets(ReplyHs, Data, false, S1#s.peer_mtu),
+ send_reply_packets(Ps, false, S1);
+ {reply,{ok,ReplyHs,Data},S1} ->
+ Ps = make_packets(ReplyHs, Data, true, S1#s.peer_mtu),
+ send_reply_packets(Ps, true, S1);
+ {reply,Error,S1} ->
+ Packet = #obex_packet { final=1,
+ opcode=Error,
+ headers=[],
+ callback=fun handle_request/2},
+ send_packet(Packet, S1)
+ end;
+handle_request(S,#obex_packet { final=0, opcode=get, headers=Hs }) ->
+ ?dbg("GET continue request ~p\n", [Hs]),
+ Packet = #obex_packet { final=0, opcode=continue, headers=[],
+ callback=next_get(Hs) },
+ send_packet(Packet, S);
+%%
+%% Handle PUT (collect headers until we find body or bodyEnd)
+%%
+handle_request(S,#obex_packet { final=0, opcode=put, headers=Hs}) ->
+ ?dbg("PUT continue request ~p\n", [Hs]),
+ continue_put(S,Hs,[]);
+handle_request(S,#obex_packet { final=1, opcode=put, headers=Hs}) ->
+ ?dbg("PUT final request ~p\n", [Hs]),
+ final_put(S,Hs,[]);
+
+%%
+%% Handle SETPATH
+%%
+handle_request(S,#obex_packet { final=1, opcode=setpath, args=Args, headers=Hs }) ->
+ ?dbg("SETPATH hs=~p\n", [Hs]),
+ {reply,Reply,S1} = target_setpath(Hs,Args,S),
+ Packet = #obex_packet { final=1, opcode=Reply, headers=[],
+ callback=fun handle_request/2 },
+ send_packet(Packet, S1);
+%%
+%% ABORT
+%%
+handle_request(S,#obex_packet { final=1, opcode=abort, headers=Hs }) ->
+ ?dbg("ABORT hs=~p\n", [Hs]),
+ {reply,Reply,S1} = target_abort(Hs, S),
+ Packet = #obex_packet { final=1, opcode=Reply, headers=[],
+ callback=fun handle_request/2 },
+ send_packet(Packet, S1);
+%%
+%% DISCONNECT
+%%
+handle_request(S,#obex_packet { final=1, opcode=disconnect, headers=Hs }) ->
+ ?dbg("DISCONNECT hs=~p\n", [Hs]),
+ {reply,Reply,S1} = target_disconnect(Hs, S),
+ Packet = #obex_packet { final=1, opcode=Reply, headers=[],
+ callback=fun handle_request/2 },
+ send_packet(Packet, S1);
+%%
+%% COMMAND
+%%
+handle_request(S,#obex_packet { final=1, opcode=command, headers=Hs }) ->
+ ?dbg("COMMAND hs=~p\n", [Hs]),
+ {reply,Reply,S1} = target_command(Hs, S),
+ Packet = #obex_packet { final=1, opcode=Reply, headers=[],
+ callback=fun handle_request/2 },
+ send_packet(Packet, S1);
+%%
+%% OTHER packets (final=0 strange opcodes etc)
+%%
+handle_request(S,_P) ->
+ ?dbg("OTHER packet=~p", [_P]),
+ Packet = #obex_packet { final=1, opcode={error,bad_request}, headers=[],
+ callback=fun handle_request/2 },
+ send_packet(Packet, S).
+
+
+%% Wrap calls to target
+target_connect(Target, S) ->
+ {reply,Reply,TS} = (S#s.smod):handle_connect(Target,S#s.sstate),
+ {reply,Reply,S#s { sstate = TS}}.
+
+target_disconnect(Hs, S) ->
+ {reply,Reply,TS} = (S#s.smod):handle_disconnect(Hs,S#s.sstate),
+ {reply,Reply,S#s { sstate = TS}}.
+
+target_get(Hs, S) ->
+ {reply,Reply,TS} = (S#s.smod):handle_get(Hs,S#s.sstate),
+ {reply,Reply,S#s { sstate = TS}}.
+
+target_put(Hs, Data, S) ->
+ {reply,Reply,TS} = (S#s.smod):handle_put(Hs,Data,S#s.sstate),
+ {reply,Reply,S#s { sstate = TS}}.
+
+target_abort(Hs, S) ->
+ {reply,Reply,TS} = (S#s.smod):handle_abort(Hs,S#s.sstate),
+ {reply,Reply,S#s { sstate = TS}}.
+
+target_setpath(Hs,Args,S) ->
+ {reply,Reply,TS} = (S#s.smod):handle_setpath(Hs,Args,S#s.sstate),
+ {reply,Reply,S#s { sstate = TS}}.
+
+target_command(Hs, S) ->
+ {reply,Reply,TS} = (S#s.smod):handle_command(Hs,S#s.sstate),
+ {reply,Reply,S#s { sstate = TS}}.
+
+
+%% update header with connection id
+prepare_headers(Hs, S) ->
+ if S#s.id == undefined ->
+ Hs;
+ true ->
+ [{connectionID,S#s.id}|Hs]
+ end.
+
+handle_get_chunk_start(Hs, From, S) ->
+ Packets = make_get_packets(Hs, S#s.peer_mtu),
+ send_get_packets(Packets, From, S).
+
+handle_get_chunk(From, S) ->
+ send_get_packet_1(#obex_packet { opcode=get, headers=[] }, From, S).
+
+handle_put_chunk_start(Hs, From, S) ->
+ Packets = make_packets(Hs, <<>>, false, S#s.peer_mtu),
+ send_put_packets(Packets, 0, From, S).
+
+handle_put_chunk(Data, From, S) ->
+ Packets = make_packets([], Data, false, S#s.peer_mtu),
+ send_put_packets(Packets, 0, From, S).
+
+handle_put_chunk_end(Data, From, S) ->
+ Packets = make_packets([], Data, true, S#s.peer_mtu),
+ send_put_packets(Packets, 1, From, S).
+
+
+
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+%% send_get_packets:
+%% Send the GET header packets util all packets have been sent
+%%
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+
+send_get_packets([], From, S) ->
+ %% send a empty get packet
+ send_get_packet_1(#obex_packet { opcode=get, headers=[] }, From, S);
+send_get_packets([P], From, S) ->
+ send_get_packet_1(P, From, S);
+send_get_packets([P|Ps], From, S) ->
+ Callback = fun(S0,_Pi=#obex_packet { final=0, opcode=_Reply}) ->
+ ?dbg("send_get_packets: input=~p\n", [_Pi]),
+ send_get_packets(Ps, From, S0);
+ (S0, _Pi=#obex_packet { final=1, opcode=Reply}) ->
+ ?dbg("send_get_packets: input=~p\n", [_Pi]),
+ gen_server:reply(From, Reply),
+ {noreply,S0}
+ end,
+ Pout = P#obex_packet { final=0, callback=Callback},
+ send_packet(Pout, S).
+
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+%%
+%% Send the GET final=1 packet
+%%
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+
+send_get_packet_1(P, From, S) ->
+ Callback =
+ fun (S0,_P=#obex_packet { final=1,opcode=continue,headers=Hs}) ->
+ ?dbg("send_get_packet_1: input=~p\n", [_P]),
+ gen_server:reply(From, {continue,Hs}),
+ {noreply,S0};
+ (S0, _P=#obex_packet { final=1,opcode=Reply,headers=Hs}) ->
+ ?dbg("send_get_packet_1: input=~p\n", [_P]),
+ case Reply of
+ {ok,success} ->
+ gen_server:reply(From, {ok,Hs});
+ Error ->
+ gen_server:reply(From, Error)
+ end,
+ {noreply,S0};
+ (S0,_P=#obex_packet { final=0,opcode=_Reply,headers=Hs}) ->
+ ?dbg("send_get_packet_1: input=~p\n", [_P]),
+ %% request more request headers !!!
+ gen_server:reply(From, {more, Hs}),
+ {noreply,S0}
+ end,
+ Packet = P#obex_packet { final= 1, callback = Callback },
+ send_packet(Packet, S).
+
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+%% send_put_packets:
+%% Send the PUT packets util all packets have been sent
+%%
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+
+send_put_packets([], _Finally, _From, S) ->
+ {noreply, S};
+send_put_packets([P], Finally, From, S) ->
+ Callback = fun(S0,_Pi=#obex_packet { final=0, opcode=Reply}) ->
+ ?dbg("handle_put: input=~p\n", [_Pi]),
+ gen_server:reply(From, Reply),
+ {noreply,S0};
+ (S0, _Pi=#obex_packet { final=1, opcode=Reply}) ->
+ ?dbg("handle_put: input=~p\n", [_Pi]),
+ gen_server:reply(From, Reply),
+ {noreply,S0}
+ end,
+ Pout = P#obex_packet { opcode=put, final=Finally, callback=Callback},
+ send_packet(Pout, S);
+send_put_packets([P|Ps], Finally, From, S) ->
+ Callback = fun(S0,_Pi=#obex_packet { final=0, opcode=_Reply}) ->
+ ?dbg("handle_put: input=~p\n", [_Pi]),
+ send_put_packets(Ps, Finally, From, S0);
+ (S0, _Pi=#obex_packet { final=1, opcode=Reply}) ->
+ ?dbg("handle_put: input=~p\n", [_Pi]),
+ gen_server:reply(From, Reply),
+ {noreply,S0}
+ end,
+ Pout = P#obex_packet { opcode=put, final=0, callback=Callback},
+ send_packet(Pout, S).
+
+recv_closed(S) when S#s.role == server ->
+ ?dbg("server connection closed (normal)\n", []),
+ {stop, normal, S};
+recv_closed(S) when S#s.role == client ->
+ ?dbg("client connection closed\n", []),
+ {noreply, S#s { ref=undefined, id=undefined }}.
+
+recv(Data, S) ->
+ Buf = recv_concat(Data, S#s.recv_buffer),
+ BufLen = size(Buf),
+ Remain = S#s.recv_remain,
+ if Remain < 0 ->
+ if BufLen + Remain < 0 ->
+ {noreply, S#s { recv_remain=BufLen+Remain,
+ recv_buffer = Buf }};
+ true ->
+ <<_OP:8,PacketLen:16,_/binary>> = Buf,
+ if BufLen < PacketLen ->
+ {noreply, S#s { recv_remain=PacketLen-BufLen,
+ recv_buffer=Buf }};
+ BufLen >= PacketLen ->
+ <<Packet:PacketLen/binary,Buf1/binary>> = Buf,
+ recv_packet(Packet, Buf1, S#s { recv_remain=0,
+ recv_buffer=(<<>>)
+ })
+ end
+ end;
+ Remain > 0 ->
+ if BufLen < Remain ->
+ {noreply, S#s { recv_remain=Remain - BufLen,
+ recv_buffer=Buf }};
+ true ->
+ <<_OP:8,PacketLen:16,_/binary>> = Buf,
+ <<Packet:PacketLen/binary, Buf1/binary>> = Buf,
+ recv_packet(Packet, Buf1, S#s { recv_remain=0,
+ recv_buffer=(<<>>) })
+ end
+ end.
+
+recv_packet(Data, Buf, S) ->
+ Cmd = case S#s.send_queue of
+ [P|_] -> P#obex_packet.opcode;
+ [] -> undefined
+ end,
+ Reply = decode_packet(Data, Cmd),
+ ?dbg("recv_packet: ~p\n", [Reply]),
+ S1 = push_packets(Buf, S), %% push rest of packet data
+ case S1#s.send_queue of
+ [#obex_packet { callback=Callback }|_] ->
+ %% Note that the callback may send packets, but they
+ %% are queued after current packet.
+ case Callback(S1, Reply) of
+ {noreply, S2} ->
+ Q = tl(S2#s.send_queue),
+ send_packet(S2#s { send_queue = Q });
+ {stop,Reason,Reply,S2} ->
+ {stop,Reason,Reply,S2};
+ {stop,Reason,S2} ->
+ {stop,Reason,S2}
+ end;
+ [] ->
+ ?dbg("Reply not expected\n",[]),
+ {noreply, S1}
+ end.
+
+decode_opcode(Code) ->
+ case Code band 16#7f of
+ ?OBEX_CMD_CONNECT -> connect;
+ ?OBEX_CMD_DISCONNECT -> disconnect;
+ ?OBEX_CMD_PUT -> put;
+ ?OBEX_CMD_GET -> get;
+ ?OBEX_CMD_COMMAND -> command;
+ ?OBEX_CMD_SETPATH -> setpath;
+ ?OBEX_CMD_ABORT -> abort;
+
+ %% 1x
+ ?OBEX_RSP_CONTINUE -> continue;
+ ?OBEX_RSP_SWITCH_PRO -> switch_protocols;
+
+ %% 2x
+ ?OBEX_RSP_SUCCESS -> {ok,success};
+ ?OBEX_RSP_CREATED -> {ok,created};
+ ?OBEX_RSP_ACCEPTED -> {ok,accepted};
+ ?OBEX_RSP_NO_CONTENT -> {ok,no_content};
+ ?OBEX_RSP_RESET_CONTENT -> {ok, reset_content};
+ ?OBEX_RSP_PARTIAL_CONTENT -> {ok, partial_content};
+
+ %% 3x
+ ?OBEX_RSP_MULTIPLE_CHOICES -> {error,multiple_choices};
+ ?OBEX_RSP_MOVED_PERMANENTLY -> {error,moved_permanently};
+ ?OBEX_RSP_MOVED_TEMPORARILY -> {error,moved_temporarily};
+ ?OBEX_RSP_SEE_OTHER -> {error,see_other};
+ ?OBEX_RSP_NOT_MODIFIED -> {error,not_modified};
+ ?OBEX_RSP_USE_PROXY -> {error,use_proxy};
+
+ %% 4x
+ ?OBEX_RSP_BAD_REQUEST -> {error,bad_request};
+ ?OBEX_RSP_UNAUTHORIZED -> {error,unauthorized};
+ ?OBEX_RSP_PAYMENT_REQUIRED -> {error,payment_required};
+ ?OBEX_RSP_FORBIDDEN -> {error, forbidden};
+ ?OBEX_RSP_NOT_FOUND -> {error, not_found};
+ ?OBEX_RSP_METHOD_NOT_ALLOWED -> {error,not_allowed};
+ ?OBEX_RSP_CONFLICT -> {error, conflict};
+ ?OBEX_RSP_GONE -> {error, gone};
+ ?OBEX_RSP_LENGTH_REQUIRED -> {error, length_required};
+ ?OBEX_RSP_PRECONDITION_FAILED -> {error, precondition_failed};
+ ?OBEX_RSP_ENTITY_TOO_LARGE -> {error, entity_too_large};
+ ?OBEX_RSP_URL_TOO_LARGE -> {error, url_too_large};
+ ?OBEX_RSP_UNSUPPORTED_MEDIA -> {error, unsupported_media};
+
+ %%
+ ?OBEX_RSP_INTERNAL_SERVER_ERROR -> {error, internal_server_error};
+ ?OBEX_RSP_NOT_IMPLEMENTED -> {error, not_implemented};
+ ?OBEX_RSP_BAD_GATEWAY -> {error, bad_gateway};
+ ?OBEX_RSP_SERVICE_UNAVAILABLE -> {error, service_unavailable};
+ ?OBEX_RSP_GATEWAY_TIMEOUT -> {error, gateway_timeout};
+ ?OBEX_RSP_HTTP_VERSION_NOT_SUPPORTED -> {error, http_version_not_supported};
+
+ %%
+ ?OBEX_RSP_DATABASE_FULL -> {error, database_full};
+ ?OBEX_RSP_DATABASE_LOCKED -> {error, database_locked};
+ %%
+ Code1 ->
+ case (Code1 bsr 4) of
+ 2 -> {ok,Code1};
+ _ -> {error,Code1}
+ end
+ end.
+
+
+encode_opcode(Code) ->
+ case Code of
+ %% 0x
+ connect -> ?OBEX_CMD_CONNECT;
+ disconnect -> ?OBEX_CMD_DISCONNECT;
+ put -> ?OBEX_CMD_PUT;
+ get -> ?OBEX_CMD_GET;
+ command -> ?OBEX_CMD_COMMAND;
+ setpath -> ?OBEX_CMD_SETPATH;
+ abort -> ?OBEX_CMD_ABORT;
+ %% 1x
+ continue -> ?OBEX_RSP_CONTINUE;
+ switch_protocols -> ?OBEX_RSP_SWITCH_PRO;
+ %% 2x
+ {ok,success} -> ?OBEX_RSP_SUCCESS;
+ {ok,created} -> ?OBEX_RSP_CREATED;
+ {ok,accetped} -> ?OBEX_RSP_ACCEPTED;
+ {ok,no_content} -> ?OBEX_RSP_NO_CONTENT;
+ %% 3x
+ {error,multiple_choices} -> ?OBEX_RSP_MULTIPLE_CHOICES;
+ {error,moved_permanently} -> ?OBEX_RSP_MOVED_PERMANENTLY;
+ {error,moved_temporarily} -> ?OBEX_RSP_MOVED_TEMPORARILY;
+ {error,see_other} -> ?OBEX_RSP_SEE_OTHER;
+ {error,not_modified} -> ?OBEX_RSP_NOT_MODIFIED;
+ {error,use_proxy} -> ?OBEX_RSP_USE_PROXY;
+
+ %% 4x
+ {error,bad_request} -> ?OBEX_RSP_BAD_REQUEST;
+ {error,unauthorized} -> ?OBEX_RSP_UNAUTHORIZED;
+ {error,payment_required} -> ?OBEX_RSP_PAYMENT_REQUIRED;
+ {error,forbidden} -> ?OBEX_RSP_FORBIDDEN;
+ {error,not_found} -> ?OBEX_RSP_NOT_FOUND;
+ {error,not_allowed} -> ?OBEX_RSP_METHOD_NOT_ALLOWED;
+ {error,conflict} -> ?OBEX_RSP_CONFLICT;
+ {error, gone} -> ?OBEX_RSP_GONE;
+ {error, length_required} -> ?OBEX_RSP_LENGTH_REQUIRED;
+ {error, precondition_failed} -> ?OBEX_RSP_PRECONDITION_FAILED;
+ {error, entity_too_large} -> ?OBEX_RSP_ENTITY_TOO_LARGE;
+ {error, url_too_large} -> ?OBEX_RSP_URL_TOO_LARGE;
+ {error, unsupported_media} -> ?OBEX_RSP_UNSUPPORTED_MEDIA;
+ %% 5x
+ {error,internal_server_error} -> ?OBEX_RSP_INTERNAL_SERVER_ERROR;
+ {error,not_implemented} -> ?OBEX_RSP_NOT_IMPLEMENTED;
+ %% 6x
+ {error,database_full} -> ?OBEX_RSP_DATABASE_FULL;
+ {error,database_locked} -> ?OBEX_RSP_DATABASE_LOCKED;
+ %%
+ {ok,Code} when Code >= 0, Code =< 15 -> Code;
+ {error,Code} -> Code
+ end.
+
+%% push back extra packet data (ABORT?)
+%% (should never happend but why should we not try to support pipelining ...)
+push_packets(Buf, S) when size(Buf) < 3 ->
+ S#s { recv_remain = -3 + size(Buf), recv_buffer = Buf };
+push_packets(Buf = <<_Op:8, PacketLen:16,_>>, S) ->
+ DataLen = size(Buf),
+ if DataLen >= PacketLen ->
+ <<Packet:PacketLen/binary, Buf1/binary>> = Buf,
+ Qs = S#s.recv_queue ++ [Packet],
+ push_packets(Buf1, S#s { recv_queue = Qs });
+ true ->
+ S#s { recv_remain = PacketLen-DataLen,
+ recv_buffer = Buf }
+ end.
+
+%%
+%% Generate a sequence of packets with
+%% Headers body and bodyEnd
+%% End == false - Do not generated bodyEnd headers
+%% Hs == [] - Only generate body or bodyEnd headers
+%% Data == <<>> - No data possible a empty dataEnd is generated
+%%
+make_packets(Hs, Data, End, Mtu) ->
+ make_headers(Hs,Mtu-3,Mtu-3,End,Data,[],[]).
+
+make_headers([{Key,Value}|Hs],Remain,Mtu,End,Data,HsAcc,PsAcc) ->
+ Sz = size_header(Key,Value),
+ if Sz =< Remain ->
+ make_headers(Hs,Remain-Sz,Mtu,End,Data,
+ [{Key,Value}|HsAcc],PsAcc);
+ Sz > Mtu ->
+ io:format("WARNING: SIZE(~p,~p)=~w,MTU=~w\n",[Key,Value,Sz,Mtu]),
+ if HsAcc == [] ->
+ P = #obex_packet { headers=[{Key,Value}]},
+ make_headers(Hs,Mtu,Mtu,End,Data,[],[P|PsAcc]);
+ true ->
+ P1 = #obex_packet { headers=reverse(HsAcc) },
+ P2 = #obex_packet { headers=[{Key,Value}]},
+ make_headers(Hs,Mtu,Mtu,End,Data,[],[P2,P1|PsAcc])
+ end;
+ true ->
+ P = #obex_packet { headers=reverse(HsAcc) },
+ make_headers(Hs,Mtu-Sz,Mtu,End,Data,[{Key,Value}],[P|PsAcc])
+ end;
+make_headers([],Remain,Mtu,End,Data,HsAcc,PsAcc) ->
+ make_data(Data,Remain,Mtu,End,HsAcc,PsAcc).
+
+make_data(Data,Remain,Mtu,End,HsAcc,PsAcc) ->
+ Sz = size(Data)+3, %% DataSize + Tag & length
+ if
+ Sz =< Remain ->
+ if End == true ->
+ HsAcc1 = [{bodyEnd,Data}|HsAcc],
+ P = #obex_packet { headers=reverse(HsAcc1) },
+ reverse([P|PsAcc]);
+ Sz == 3, HsAcc == [] -> %% No data and No End
+ reverse(PsAcc);
+ Sz == 3 ->
+ P = #obex_packet { headers=reverse(HsAcc) },
+ reverse([P|PsAcc]);
+ true -> %% Data but not Last Chunk
+ HsAcc1 = [{body,Data}|HsAcc],
+ P = #obex_packet { headers=reverse(HsAcc1) },
+ reverse([P|PsAcc])
+ end;
+ Remain < 4 ->
+ P = #obex_packet { headers=reverse(HsAcc) },
+ make_data(Data,Mtu,Mtu,End,[],[P|PsAcc]);
+ Sz > Remain ->
+ Len = Remain - 3,
+ <<Data1:Len/binary, Data2/binary>> = Data,
+ HsAcc1 = [{body,Data1} | HsAcc],
+ P = #obex_packet { headers=reverse(HsAcc1) },
+ make_data(Data2,Mtu,Mtu,End,[],[P|PsAcc])
+ end.
+
+%%
+%% Create a sequence of GET packets covering
+%% the sending of the initial headers
+%%
+make_get_packets(Hs, Mtu) ->
+ make_get_headers(Hs,Mtu-3,Mtu-3,[],[]).
+
+make_get_headers([{Key,Value}|Hs],Remain,Mtu,HsAcc,PsAcc) ->
+ Sz = size_header(Key,Value),
+ if Sz =< Remain ->
+ make_get_headers(Hs,Remain-Sz,Mtu,
+ [{Key,Value}|HsAcc],PsAcc);
+ Sz > Mtu ->
+ io:format("WARNING: SIZE(~p,~p)=~w,MTU=~w\n", [Key,Value,Sz,Mtu]),
+ if HsAcc == [] ->
+ P = #obex_packet { opcode=get, headers=[{Key,Value}]},
+ make_get_headers(Hs,Mtu,Mtu,[],[P|PsAcc]);
+ true ->
+ P1 = #obex_packet { opcode=get, headers=reverse(HsAcc) },
+ P2 = #obex_packet { opcode=get, headers=[{Key,Value}]},
+ make_get_headers(Hs,Mtu,Mtu,[],[P2,P1|PsAcc])
+ end;
+ true ->
+ P = #obex_packet { opcode=get, headers=reverse(HsAcc) },
+ make_get_headers(Hs,Mtu-Sz,Mtu,[{Key,Value}],[P|PsAcc])
+ end;
+make_get_headers([],_Remain,_Mtu,[],PsAcc) ->
+ reverse(PsAcc);
+make_get_headers([],_Remain,_Mtu,HsAcc,PsAcc) ->
+ P = #obex_packet { opcode=get,headers=reverse(HsAcc)},
+ reverse([P|PsAcc]).
+
+%%
+%% split headers info {Regular, Body}
+%%
+split_headers(Hs) ->
+ split_headers(Hs, [], [], false).
+
+split_headers([{body,Data}|Hs], Regular, Body, End) ->
+ split_headers(Hs, Regular, [Data|Body], End);
+split_headers([{bodyEnd,Data}|Hs], Regular, Body, _End) ->
+ split_headers(Hs, Regular, [Data|Body], true);
+split_headers([H | Hs], Regular, Body, End) ->
+ split_headers(Hs, [H|Regular], Body, End);
+split_headers([], Regular, Body, End) ->
+ {End, reverse(Regular), list_to_binary(reverse(Body))}.
+
+%%
+%% Decode packet
+%%
+
+%% 0x - commands
+decode_packet(Data = <<Final:1,?OBEX_CMD_CONNECT:7,_:16,
+ Version:8,Flags:8,MaxPacketLength:16,_/binary>>, _Cmd) ->
+ #obex_packet { final = Final,
+ opcode = connect,
+ args = [Version,Flags,MaxPacketLength],
+ headers = decode_headers(Data, 7) };
+decode_packet(Data = <<Final:1,?OBEX_CMD_SETPATH:7,_:16,
+ Flags:8,Constants:8,_/binary>>, _Cmd) ->
+ #obex_packet { final = Final,
+ opcode = setpath,
+ args = [Flags,Constants],
+ headers = decode_headers(Data, 5) };
+%% 2x - ok (connect reply)
+decode_packet(Data = <<Final:1,Code:7,_:16,
+ Version,Flags,MaxPacketLength:16,_/binary>>, connect) ->
+ #obex_packet { final=Final,
+ opcode = decode_opcode(Code),
+ args = [Version,Flags,MaxPacketLength],
+ headers = decode_headers(Data, 7)};
+decode_packet(Data = <<Final:1,Code:7,_:16,_/binary>>,_Cmd) ->
+ #obex_packet { final = Final,
+ opcode = decode_opcode(Code),
+ headers = decode_headers(Data, 3) }.
+
+
+recv_concat(Data, []) -> Data;
+recv_concat(Data, Buffer) -> <<Buffer/binary, Data/binary>>.
+
+%% send or buffer a packet
+send_packet(_P, S) when S#s.ref == undefined ->
+ ?dbg("send_packet: transport closed\n", []),
+ {reply, {error,closed}, S};
+send_packet(P, S) when is_record(P, obex_packet) ->
+ if S#s.send_queue == [] ->
+ send_packet(S#s { send_queue = [P]});
+ true ->
+ ?dbg("send_packet: queued ~p\n", [P]),
+ Q = S#s.send_queue ++ [P],
+ {noreply, S#s { send_queue = Q }}
+ end.
+
+%% send the first packet on the queue
+send_packet(S) ->
+ case S#s.send_queue of
+ [] ->
+ {noreply, S};
+ [P=#obex_packet { opcode = undefined, callback=Callback } | Q] ->
+ %% sync / close / packet
+ Callback(S#s { send_queue=Q} , P);
+
+ [P=#obex_packet { final=Final, opcode=command, callback=Callback,
+ headers=Hs }| Q] ->
+ ?dbg("send_packet: ~p\n", [P]),
+ Data = encode_packet(Final, ?OBEX_CMD_COMMAND,<<>>, Hs,
+ S#s.peer_mtu),
+ (S#s.transport):send(S#s.ref, Data),
+ Callback(S#s { send_queue=Q }, P);
+
+ [_P=#obex_packet { final=Final, opcode=connect,
+ args=[Version,Flags,MaxPacketLength],
+ headers=Hs }|_Q] ->
+ ?dbg("send_packet: ~p\n", [_P]),
+ Data = encode_packet(Final, ?OBEX_CMD_CONNECT,
+ <<Version:8,Flags:8,MaxPacketLength:16>>,
+ Hs, 0),
+ (S#s.transport):send(S#s.ref, Data),
+ {noreply,S};
+
+ %% Synthetic opcode!!!
+ [_P=#obex_packet { final=Final, opcode=connect_response,
+ args=[Version,Flags,MaxPacketLength],
+ headers=Hs }|_Q] ->
+ ?dbg("send_packet: ~p\n", [_P]),
+ Data = encode_packet(Final, ?OBEX_RSP_SUCCESS,
+ <<Version:8,Flags:8,MaxPacketLength:16>>,
+ Hs, 0),
+ (S#s.transport):send(S#s.ref, Data),
+ {noreply,S};
+
+ [_P=#obex_packet { final=Final, opcode=setpath,
+ args=[Flags,Constants],
+ headers=Hs } | _Q] ->
+ ?dbg("send_packet: ~p\n", [_P]),
+ Data = encode_packet(Final, ?OBEX_CMD_SETPATH,
+ <<Flags:8, Constants:8>>,
+ Hs, S#s.peer_mtu),
+ (S#s.transport):send(S#s.ref, Data),
+ {noreply,S};
+
+
+
+ [_P=#obex_packet { final=Final, opcode=Op,
+ args=undefined,
+ headers=Hs } | _Q] ->
+ ?dbg("send_packet: ~p\n", [_P]),
+ OpCode = encode_opcode(Op),
+ Data = encode_packet(Final, OpCode, <<>>,
+ Hs, S#s.peer_mtu),
+ (S#s.transport):send(S#s.ref, Data),
+ {noreply,S}
+ end.
+
+
+
+getopt(Key, Opts) ->
+ getopt(Key, Opts, undefined).
+
+getopt(Key, Opts, Default) ->
+ case lists:keysearch(Key, 1, Opts) of
+ {value, {_,Value}} ->
+ Value;
+ false ->
+ Default
+ end.
+
+decode_headers(Bin) ->
+ decode_headers(Bin, 0).
+
+decode_headers(Bin, Offset) ->
+ ?dbg("decode_headers: offset=~p\n", [Offset]),
+ decode_headers(Bin, Offset, []).
+
+decode_headers(Bin, Offset, Hs) when Offset >= size(Bin) ->
+ reverse(Hs);
+decode_headers(Bin, Offset, Hs) ->
+ ?dbg("decode_headers: offset=~p\n", [Offset]),
+ case Bin of
+ <<_:Offset/binary,?OBEX_V0:2, ID:6,HeaderLen:16,_/binary>> ->
+ Offset1 = Offset+3,
+ ValueLen = HeaderLen - 3, %% not counting tag and length
+ <<_:Offset1/binary, Value:ValueLen/binary, _/binary>> = Bin,
+ Offset2 = Offset1+ValueLen,
+ Val = decode_unicode(Value, 0, []),
+ case ((?OBEX_V0 bsl 6) bor ID) of
+ ?OBEX_HDR_NAME ->
+ ?dbg("header: name=~p\n", [Val]),
+ decode_headers(Bin,Offset2,
+ [{name,Val}|Hs]);
+ ?OBEX_HDR_DESCRIPTION ->
+ ?dbg("header: desciption=~p\n", [Val]),
+ decode_headers(Bin,Offset2,
+ [{description,Val}|Hs]);
+ ID1 ->
+ ?dbg("header: ~w=~p\n", [ID1,Val]),
+ decode_headers(Bin,Offset2,
+ [{ID1,Val}|Hs])
+ end;
+
+ <<_:Offset/binary,?OBEX_Vn:2,ID:6,HeaderLen:16,_/binary>> ->
+ Offset1 = Offset+3,
+ ValueLen = HeaderLen - 3,
+ <<_:Offset1/binary,Value:ValueLen/binary,_/binary>> = Bin,
+ Offset2 = Offset1+ValueLen,
+ case ((?OBEX_Vn bsl 6) bor ID) of
+ ?OBEX_HDR_TYPE ->
+ Val = decode_ascii(Value, 0, []),
+ ?dbg("header: type=~p\n", [Val]),
+ decode_headers(Bin,Offset2,[{type,Val}|Hs]);
+ ?OBEX_HDR_TIME ->
+ Val = binary_to_list(Value),
+ ?dbg("header: time=~p\n", [Val]),
+ decode_headers(Bin,Offset2,[{time,Val}|Hs]);
+ ?OBEX_HDR_TARGET ->
+ Val = binary_to_list(Value),
+ ?dbg("header: target=~p\n", [Val]),
+ decode_headers(Bin,Offset2,[{target,Val}|Hs]);
+ ?OBEX_HDR_HTTP ->
+ Val = binary_to_list(Value),
+ ?dbg("header: http=~p\n", [Val]),
+ decode_headers(Bin,Offset2,[{http,Val}|Hs]);
+ ?OBEX_HDR_BODY ->
+ ?dbg("header: body=~p\n", [Value]),
+ decode_headers(Bin,Offset2,[{body,Value}|Hs]);
+ ?OBEX_HDR_BODY_END ->
+ ?dbg("header: bodyEnd=~p\n", [Value]),
+ decode_headers(Bin,Offset2,[{bodyEnd,Value}|Hs]);
+ ?OBEX_HDR_WHO ->
+ Val = binary_to_list(Value),
+ ?dbg("header: who=~p\n", [Val]),
+ decode_headers(Bin,Offset2,[{who,Val}|Hs]);
+
+ ?OBEX_HDR_APPARAM ->
+ ?dbg("header: appparam=~p\n", [Value]),
+ decode_headers(Bin,Offset2,
+ [{appParameters,Value}|Hs]);
+
+ ?OBEX_HDR_AUTHCHAL ->
+ ?dbg("header: authChal=~p\n", [Value]),
+ decode_headers(Bin,Offset2,
+ [{authorizationChallange,Value}|Hs]);
+
+ ?OBEX_HDR_AUTHRESP ->
+ ?dbg("header: authResp=~p\n", [Value]),
+ decode_headers(Bin,Offset2,
+ [{authorizationResponse,Value}|Hs]);
+
+ ?OBEX_HDR_OBJCLASS ->
+ Val = binary_to_list(Value),
+ ?dbg("header: objClass=~p\n", [Val]),
+ decode_headers(Bin,Offset2,[{objectClass,Val}|Hs]);
+
+ ID1 ->
+ ?dbg("header: ~p=~p\n", [ID1,Value]),
+ decode_headers(Bin,Offset2, [{ID1,Value} | Hs])
+ end;
+
+ <<_:Offset/binary,?OBEX_I4:2,ID:6,Value:32,_/binary>> ->
+ Offset1 = Offset+1+4,
+ case ((?OBEX_I4 bsl 6) bor ID) of
+ ?OBEX_HDR_COUNT ->
+ ?dbg("header: count=~p\n", [Value]),
+ decode_headers(Bin,Offset1,
+ [{count,Value}|Hs]);
+ ?OBEX_HDR_CONNECTION_ID ->
+ ?dbg("header: connectionID=~p\n", [Value]),
+ decode_headers(Bin,Offset1,
+ [{connectionID,Value}|Hs]);
+ ?OBEX_HDR_LENGTH ->
+ ?dbg("header: length=~p\n", [Value]),
+ decode_headers(Bin,Offset1,
+ [{length,Value}|Hs]);
+ ?OBEX_HDR_TIME2 ->
+ ?dbg("header: time2=~p\n", [Value]),
+ decode_headers(Bin,Offset1,
+ [{time2,Value}|Hs]);
+ ID1 ->
+ ?dbg("header: ~p=~p\n", [ID1,Value]),
+ decode_headers(Bin,Offset1,
+ [{ID1,Value} | Hs])
+ end;
+
+ <<_:Offset/binary,?OBEX_I1:2,ID:6,Value:8,_/binary>> ->
+ Offset1 = Offset+1+1,
+ case ((?OBEX_I1 bsl 6) bor ID) of
+ ID1 ->
+ ?dbg("header: ~p=~p\n", [ID1,Value]),
+ decode_headers(Bin,Offset1,
+ [{ID1,Value} | Hs])
+ end
+ end.
+
+
+decode_unicode(Bin, Offset, Acc) when Offset >= size(Bin) ->
+ ?dbg("Warning: unicode string not null terminated\n", []),
+ reverse(Acc);
+decode_unicode(Bin, Offset, Acc) ->
+ case Bin of
+ <<_:Offset/binary, 0, 0>> -> reverse(Acc);
+ <<_:Offset/binary, Char:16, _/binary>> ->
+ decode_unicode(Bin, Offset+2, [Char|Acc])
+ end.
+
+decode_ascii(Bin, Offset, Acc) when Offset >= size(Bin) ->
+ reverse(Acc);
+decode_ascii(Bin, Offset, Acc) ->
+ case Bin of
+ <<_:Offset/binary, 0>> ->
+ reverse(Acc);
+ <<_:Offset/binary, Char:8, _/binary>> ->
+ decode_ascii(Bin, Offset+1, [Char|Acc])
+ end.
+
+encode_packet(Final, Opcode, BinArgs, Headers, Mtu) ->
+ BinHs = encode_headers(Headers),
+ PacketLen = 3+size(BinArgs)+size(BinHs),
+ if Mtu == 0 ->
+ ok;
+ PacketLen > Mtu ->
+ ?dbg("Waring: packet bigger than peer mtu (~w)\n", [Mtu]);
+ true -> ok
+ end,
+ [<<Final:1, Opcode:7, PacketLen:16>>, BinArgs, BinHs].
+
+
+encode_headers(Hs) ->
+ list_to_binary(enc_headers(Hs)).
+
+enc_headers([{Key,Value}|Hs]) ->
+ [encode_header(Key,Value) | enc_headers(Hs)];
+enc_headers([]) ->
+ [].
+
+encode_header(Key,Value) ->
+ case Key of
+ %% null terminated unicode unicode
+ name ->
+ encode_unicode(?OBEX_HDR_NAME, Value);
+ description ->
+ encode_unicode(?OBEX_HDR_DESCRIPTION,Value);
+ ID when is_integer(ID), (ID band ?OBEX_HDR_MASK) == ?OBEX_V0 ->
+ encode_unicode(ID,Value);
+ %% byte sequence
+ type ->
+ ?enc_null_sequence(?OBEX_HDR_TYPE,Value);
+ time ->
+ ?enc_byte_sequence(?OBEX_HDR_TIME,Value);
+ target ->
+ ?enc_byte_sequence(?OBEX_HDR_TARGET,Value);
+ http ->
+ ?enc_byte_sequence(?OBEX_HDR_HTTP,Value);
+ body ->
+ ?enc_byte_sequence(?OBEX_HDR_BODY,Value);
+ bodyEnd ->
+ ?enc_byte_sequence(?OBEX_HDR_BODY_END,Value);
+ who ->
+ ?enc_byte_sequence(?OBEX_HDR_WHO,Value);
+ appParameters ->
+ ?enc_byte_sequence(?OBEX_HDR_APPARAM,Value);
+ authorizationChallange ->
+ ?enc_byte_sequence(?OBEX_HDR_AUTHCHAL,Value);
+ authorizationResponse ->
+ ?enc_byte_sequence(?OBEX_HDR_AUTHRESP,Value);
+ objectClass ->
+ ?enc_byte_sequence(?OBEX_HDR_OBJCLASS,Value);
+ ID when is_integer(ID), (ID band ?OBEX_HDR_MASK) == ?OBEX_Vn ->
+ ?enc_byte_sequence(ID, Value);
+ %% 4 byte values
+ count -> <<?OBEX_HDR_COUNT,Value:32>>;
+ length -> <<?OBEX_HDR_LENGTH,Value:32>>;
+ time2 -> <<?OBEX_HDR_TIME2, Value:32>>;
+ connectionID -> (<<?OBEX_HDR_CONNECTION_ID,Value:32>>);
+ ID when is_integer(ID), (ID band ?OBEX_HDR_MASK) == ?OBEX_I4 ->
+ (<<ID, Value:32>>);
+ ID when is_integer(ID), (ID band ?OBEX_HDR_MASK) == ?OBEX_I1 ->
+ (<<ID, Value:8>>)
+ end.
+
+%% time header should be in UTC time
+utc_time_iso(Time) when is_list(Time) ->
+ Time;
+utc_time_iso(Time={Ms,S,Us}) when is_integer(Ms),is_integer(S),is_integer(Us) ->
+ utc_time_iso(calendar:now_to_universal_time(Time));
+utc_time_iso({{YYYY,Mon,Day},{H,M,S}}) ->
+ lists:flatten(io_lib:format("~4..0w~2..0w~2..0wT~2..0w~2..0w~2..0wZ",
+ [YYYY,Mon,Day,H,M,S])).
+
+%% but here is a local version
+time_iso(Time) when is_list(Time) ->
+ Time;
+time_iso(Time={Ms,S,Us})
+ when is_integer(Ms),is_integer(S),is_integer(Us) ->
+ time_iso(calendar:now_to_local_time(Time));
+time_iso({{YYYY,Mon,Day},{H,M,S}}) ->
+ lists:flatten(io_lib:format("~4..0w~2..0w~2..0wT~2..0w~2..0w~2..0w",
+ [YYYY,Mon,Day,H,M,S])).
+%%
+%% Calculate size for a header list
+%%
+size_headers(Hs) when is_list(Hs) ->
+ size_headers(Hs, 0).
+
+size_headers([{Key,Value}|Hs], Sz) ->
+ size_headers(Hs, size_header(Key,Value)+Sz);
+size_headers([], Sz) ->
+ Sz.
+
+%%
+%% Calculate the size of header
+%% Including <tag> <len:16> <data>
+%%
+size_header(Key,Value) ->
+ case Key of
+ %% null terminates
+ name -> 3+?size_unicode(Value);
+ description -> 3+?size_unicode(Value);
+ ID when is_integer(ID), (ID band ?OBEX_HDR_MASK) == ?OBEX_V0 ->
+ 3+?size_unicode(Value);
+ %% byte sequence
+ type -> 4+?size_byte_sequence(Value); %% (+1 for null termination)
+ time -> 3+?size_byte_sequence(Value);
+ target -> 3+?size_byte_sequence(Value);
+ http -> 3+?size_byte_sequence(Value);
+ body -> 3+?size_byte_sequence(Value);
+ bodyEnd -> 3+?size_byte_sequence(Value);
+ who -> 3+?size_byte_sequence(Value);
+ appParameters -> 3+?size_byte_sequence(Value);
+ authorizationChallange -> 3+?size_byte_sequence(Value);
+ authorizationResponse -> 3+?size_byte_sequence(Value);
+ objectClass -> 3+?size_byte_sequence(Value);
+ ID when is_integer(ID), (ID band ?OBEX_HDR_MASK) == ?OBEX_Vn ->
+ 3+?size_byte_sequence(Value);
+ %% 4 byte values
+ count -> 5;
+ length -> 5;
+ time2 -> 5;
+ connectionID -> 5;
+ ID when is_integer(ID), (ID band ?OBEX_HDR_MASK) == ?OBEX_I4 -> 5;
+ ID when is_integer(ID), (ID band ?OBEX_HDR_MASK) == ?OBEX_I1 -> 2
+ end.
+
+%% Note: length field include it self and tag + unicode null termination 0,0
+encode_unicode(ID, "") -> %% speical (test)
+ [ID, <<3:16>>];
+encode_unicode(ID, Value) ->
+ Len = (length(Value)+1)*2 + 3,
+ [ID, <<Len:16>> | encode_chars(Value)].
+
+encode_chars([H|T]) ->
+ [<<H:16>> | encode_chars(T)];
+encode_chars([]) ->
+ [<<0:16>>].
diff --git a/deps/bt/src/obex_bip.erl b/deps/bt/src/obex_bip.erl
new file mode 100644
index 0000000..c215c49
--- /dev/null
+++ b/deps/bt/src/obex_bip.erl
@@ -0,0 +1,243 @@
+%%%---- BEGIN COPYRIGHT -------------------------------------------------------
+%%%
+%%% Copyright (C) 2006 - 2014, Rogvall Invest AB, <tony@rogvall.se>
+%%%
+%%% This software is licensed as described in the file COPYRIGHT, which
+%%% you should have received as part of this distribution. The terms
+%%% are also available at http://www.rogvall.se/docs/copyright.txt.
+%%%
+%%% You may opt to use, copy, modify, merge, publish, distribute and/or sell
+%%% copies of the Software, and permit persons to whom the Software is
+%%% furnished to do so, under the terms of the COPYRIGHT file.
+%%%
+%%% This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY
+%%% KIND, either express or implied.
+%%%
+%%%---- END COPYRIGHT ---------------------------------------------------------
+%%% File : obex_bip.erl
+%%% Author : Tony Rogvall <tony@PBook.local>
+%%% Description : Basic Imaging profile
+%%% Created : 2 Jun 2006 by Tony Rogvall <tony@PBook.local>
+
+-module(obex_bip).
+
+-include("../include/uuid.hrl").
+-include("../include/obex.hrl").
+
+-compile(export_all).
+
+-export([start/0, start/1]).
+-export([init/3,
+ terminate/1,
+ handle_connect/2,
+ handle_disconnect/2,
+ handle_get/2,
+ handle_put/3,
+ handle_abort/2,
+ handle_setpath/3,
+ handle_command/2]).
+-export([sdp_info/1]).
+
+-include("../include/sdp.hrl").
+
+-record(s,
+ {
+ sref,
+ opts
+ }).
+
+-ifdef(debug).
+-define(dbg(Fmt,As), io:format("~s:~w:" Fmt "\n", [?FILE,?LINE | As])).
+-else.
+-define(dbg(Fmt,As), ok).
+-endif.
+
+
+%% Basic Imaging primary session UUID
+-define(Basic_Imaging_Image_Push,
+ ?UUID(16#E33D9545,16#8374,16#4AD7,16#9EC5,16#C16BE31EDE8E)).
+-define(Basic_Imaging_Image_Pull,
+ ?UUID(16#8EE9B3D0,16#4608,16#11D5,16#841A,16#0002A5325B4E)).
+%% Basic Imaging Advanced Image
+-define(Basic_Imaging_Advanced_Image_Printing,
+ ?UUID(16#92353350,16#4608,16#11D5,16#841A,16#0002A5325B4E)).
+-define(Basic_Imaging_Automatic_Archive,
+ ?UUID(16#940126C0,16#4608,16#11D5,16#841A,16#0002A5325B4E)).
+-define(Basic_Imaging_Remote_Camera,
+ ?UUID(16#947E7420,16#4608,16#11D5,16#841A,16#0002A5325B4E)).
+-define(Basic_Imaging_Remote_Display,
+ ?UUID(16#94C7CD20,16#4608,16#11D5,16#841A,16#0002A5325B4E)).
+
+%% Basic Imaging secondary session UUID
+-define(Basic_Imaging_Referenced_Objects,
+ ?UUID(16#8E61F95D,16#1A79,16#11D4,16#8EA4,16#00805F9B9834)).
+-define(Basic_Imaging_Archived_Objects,
+ ?UUID(16#8E61F95E,16#1A79,16#11D4,16#8EA4,16#00805F9B9834)).
+
+-define(BIP_HDR_IMG_HANDLE, 16#30). %% unicode null terminated
+-define(BIP_HDR_IMG_DESCRIPTOR, 16#71). %% bytes sequence
+
+%% Capabilities
+-define(CAPA_GENERIC_IMAGING, 2#0001).
+-define(CAPA_CAPTURING, 2#0010).
+-define(CAPA_PRINING, 2#0100).
+-define(CAPA_DISPLAYING, 2#1000).
+
+%% Features
+-define(FEATURE_IMAGE_PUSH, 2#000000001).
+-define(FEATURE_IMAGE_PUSH_STORE, 2#000000010).
+-define(FEATURE_IMAGE_PUSH_PRINT, 2#000000100).
+-define(FEATURE_IMAGE_PUSH_DISPLAY, 2#000001000).
+-define(FEATURE_IMAGE_PULL, 2#000010000).
+-define(FEATURE_ADVANCED_IMAGE_PRINING, 2#000100000).
+-define(FEATURE_AUTOMATIC_ARCHIVE, 2#001000000).
+-define(FEATURE_REMOTE_CAMERA, 2#001000000).
+-define(FEATURE_REMOTE_DISPLAY, 2#010000000).
+
+%% Functions
+-define(FUNC_GetCapabilities, 2#00000000000000001).
+-define(FUNC_PutImage, 2#00000000000000010).
+-define(FUNC_PutLinkedAttachment, 2#00000000000000100).
+-define(FUNC_PutLinkedThumbnail, 2#00000000000001000).
+-define(FUNC_RemoteDisplay, 2#00000000000010000).
+-define(FUNC_GetImagesList, 2#00000000000100000).
+-define(FUNC_GetImageProperties, 2#00000000001000000).
+-define(FUNC_GetImage, 2#00000000010000000).
+-define(FUNC_GetLinkedThumbnail, 2#00000000100000000).
+-define(FUNC_GetLinkedAttachment, 2#00000001000000000).
+-define(FUNC_DeleteImage, 2#00000010000000000).
+-define(FUNC_StartPrint, 2#00000100000000000).
+-define(FUNC_Reserved_12, 2#00001000000000000).
+-define(FUNC_StartArchive, 2#00010000000000000).
+-define(FUNC_GetMonitoringImage, 2#00100000000000000).
+-define(FUNC_Reserved_15, 2#01000000000000000).
+-define(FUNC_GetStatus, 2#10000000000000000).
+
+
+sdp_info(Channel) ->
+ Base1 = 16#0100,
+ [{?ATTR_ServiceRecordHandle, {uint32, 65000}}, %% Should not be needed ?
+ {?ATTR_ServiceClassIDList,
+ {sequence,[{uuid,?UUID_ImagingResponder}]}},
+ {?ATTR_ProtocolDescriptorList,
+ {sequence,[{sequence,[{uuid,?UUID_L2CAP}]},
+ {sequence,[{uuid,?UUID_RFCOMM},{uint8,Channel}]},
+ {sequence,[{uuid,?UUID_OBEX}]}]}},
+ {?ATTR_BrowseGroupList,
+ {sequence,[{uuid,?UUID_PublicBrowseGroup}]}},
+ {?ATTR_LanguageBaseAttributeIDList,
+ {sequence, [{uint16, ?LANGUAGE($e,$n)},
+ {uint16, ?ENCODING_UTF8},
+ {uint16, Base1}]}},
+ {?ATTR_ServiceName+Base1, {text,"Imaging"}},
+ {?ATTR_BluetoothProfileDescriptorList,
+ {sequence,[{uuid,?UUID_Imaging},
+ {uint16, 16#0100}]}},
+ {?ATTR_BIP_SupportedCapabilities,
+ {uint8, ?CAPA_GENERIC_IMAGING bor ?CAPA_DISPLAYING}},
+ {?ATTR_BIP_SupportedFeatures,
+ {uint16, ?FEATURE_IMAGE_PUSH bor ?FEATURE_IMAGE_PUSH_DISPLAY}},
+ {?ATTR_BIP_SupportedFunctions,
+ {uint32, ?FUNC_GetCapabilities bor ?FUNC_RemoteDisplay}},
+ {?ATTR_BIP_TotalImagingDataCapacity,{uint64, (1 bsl 32)}}
+ ].
+
+start() ->
+ start(15).
+
+start(Channel) ->
+ %% NOTE: The Channel number is just a hint, the
+ %% number may be any in the range 1-30
+ obex:server(Channel, [{transport,rfcomm},
+ {sdp, sdp_info(Channel)},
+ {target,{?Basic_Imaging_Remote_Display,?MODULE}}]).
+
+
+
+
+%% Basic Imaging Image Push
+connect_push(Obex) ->
+ obex:connect(Obex, [{target, ?Basic_Imaging_Image_Push}]).
+
+connect_remote_display(Obex) ->
+ obex:connect(Obex, [{target, ?Basic_Imaging_Remote_Display}]).
+
+getCapabilites(Obex) ->
+ case obex:get(Obex, [{type,"x-bt/img-capabilities"}]) of
+ {ok,_Hs,Bin} ->
+ makeup:string(Bin);
+ Error ->
+ Error
+ end.
+
+getListing(Obex) ->
+ obex:get(Obex, [{type,"x-bt/img-listing"}]).
+
+getProperties(Obex,Name) ->
+ obex:get(Obex, [{type,"x-bt/img-properties"},{name,Name}]).
+
+getImage(Obex,Name) ->
+ obex:get(Obex, [{type,"x-bt/img-imq"},{name,Name}]).
+
+getThumbnail(Obex,Name) ->
+ obex:get(Obex, [{type,"x-bt/img-thm"},{name,Name}]).
+
+remoteDisplay(Obex) ->
+ obex:get(Obex, [{type,"x-bt/img-display"}]).
+
+
+putImage(Obex,Name,Descriptor,Image) ->
+ obex:put(Obex, [{type,"x-bt/img-img"},
+ {name,Name},
+ {?BIP_HDR_IMG_DESCRIPTOR, Descriptor}], Image).
+
+%%
+%% Imageing server functions
+%%
+
+
+init(SrvRef, SrvOpts, _Mtu) ->
+ ?dbg("init: ~p, opts=~p, peer_mtu=~p", [SrvRef, SrvOpts, _Mtu]),
+ #s { sref = SrvRef, opts = SrvOpts}.
+
+terminate(_S) ->
+ ?dbg("termiate", []),
+ ok.
+
+handle_connect(Target,S) ->
+ ?dbg("handle_connect: ~p", [Target]),
+ ID = 1235, %% FIXME
+ {reply, {ok,[{connectionID,ID},{who,Target}]}, S}.
+
+handle_disconnect(_Hs, S) ->
+ ?dbg("handle_disconnect: ~p", [_Hs]),
+ {reply, {ok,success}, S}.
+
+handle_get(_Hs, S) ->
+ ?dbg("handle_get: ~p", [_Hs]),
+ {reply, {ok,[],<<"Hello">>}, S}.
+
+handle_put(_Hs,<<>>,S) ->
+ ?dbg("handle_put: ~p data=~p", [_Hs,<<>>]),
+ {reply, {ok,success}, S};
+handle_put(_Hs,_Data,S) ->
+ ?dbg("handle_put: ~p data=~p", [_Hs,_Data]),
+ {reply, continue, S}.
+
+handle_abort(_Hs, S) ->
+ ?dbg("handle_abort: ~p", [_Hs]),
+ {reply, {ok,success}, S}.
+
+handle_setpath(_Hs, _Args, S) ->
+ ?dbg("handle_setpath: ~p args=~p", [_Hs,_Args]),
+ {reply, {error,not_allowed}, S}.
+
+handle_command(_Hs, S) ->
+ ?dbg("handle_command: ~p", [_Hs]),
+ {reply, {error,not_allowed}, S}.
+
+
+
+
+
+
diff --git a/deps/bt/src/obex_dm_server.erl b/deps/bt/src/obex_dm_server.erl
new file mode 100644
index 0000000..99e5d68
--- /dev/null
+++ b/deps/bt/src/obex_dm_server.erl
@@ -0,0 +1,140 @@
+%%%---- BEGIN COPYRIGHT -------------------------------------------------------
+%%%
+%%% Copyright (C) 2006 - 2014, Rogvall Invest AB, <tony@rogvall.se>
+%%%
+%%% This software is licensed as described in the file COPYRIGHT, which
+%%% you should have received as part of this distribution. The terms
+%%% are also available at http://www.rogvall.se/docs/copyright.txt.
+%%%
+%%% You may opt to use, copy, modify, merge, publish, distribute and/or sell
+%%% copies of the Software, and permit persons to whom the Software is
+%%% furnished to do so, under the terms of the COPYRIGHT file.
+%%%
+%%% This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY
+%%% KIND, either express or implied.
+%%%
+%%%---- END COPYRIGHT ---------------------------------------------------------
+%%% File : obex_dm_server.erl
+%%% Author : Tony Rogvall <tony@PBook.local>
+%%% Description : OBEX DM Server
+%%% Created : 29 May 2006 by Tony Rogvall <tony@PBook.local>
+
+-module(obex_dm_server).
+
+-export([start/0, start/1]).
+-export([init/3,
+ terminate/1,
+ handle_connect/2,
+ handle_disconnect/2,
+ handle_get/2,
+ handle_put/3,
+ handle_abort/2,
+ handle_setpath/3,
+ handle_command/2]).
+-export([sdp_info/1]).
+
+-include("../include/sdp.hrl").
+
+-record(s,
+ {
+ sref,
+ opts
+ }).
+
+-ifdef(debug).
+-define(dbg(Fmt,As), io:format("~s:~w:" Fmt "\n", [?FILE,?LINE | As])).
+-else.
+-define(dbg(Fmt,As), ok).
+-endif.
+
+
+sdp_info(Channel) ->
+ Base1 = 16#0100,
+ [{?ATTR_ServiceRecordHandle, {uint32, 65000}}, %% Should not be needed ?
+ {?ATTR_ServiceClassIDList,
+ {sequence,[{uuid,?UUID_SyncMLDMServer}]}},
+ {?ATTR_ProtocolDescriptorList,
+ {sequence,[{sequence,[{uuid,?UUID_L2CAP}]},
+ {sequence,[{uuid,?UUID_RFCOMM},{uint8,Channel}]},
+ {sequence,[{uuid,?UUID_OBEX}]}]}},
+ {?ATTR_BrowseGroupList,
+ {sequence,[{uuid,?UUID_PublicBrowseGroup}]}},
+ {?ATTR_LanguageBaseAttributeIDList,
+ {sequence, [{uint16, ?LANGUAGE($e,$n)},
+ {uint16, ?ENCODING_UTF8},
+ {uint16, Base1}]}},
+ {?ATTR_ServiceName+Base1, {text,"SyncML DM Server"}}].
+
+
+start() ->
+ start(14).
+
+start(Channel) ->
+ %% NOTE: The Channel number is just a hint, the
+ %% number may be any in the range 1-30
+ obex:server(Channel, [{transport,rfcomm},
+ {sdp, sdp_info(Channel)},
+ {target,{"SYNCML-DM",?MODULE}}]).
+
+%%
+%% Below is NOT server functions
+%% but the connection side of the server
+%%
+
+%%
+%% Handle GET
+%%
+init(SrvRef, SrvOpts, _Mtu) ->
+ ?dbg("init: ~p, opts=~p, peer_mtu=~p", [SrvRef, SrvOpts, _Mtu]),
+ #s { sref = SrvRef, opts = SrvOpts}.
+
+terminate(_S) ->
+ ?dbg("termiate", []),
+ ok.
+
+handle_connect(Target,S) ->
+ ?dbg("handle_connect: ~p", [Target]),
+ ID = 1235,
+ {reply, {ok,[{connectionID,ID},{who,Target}]}, S}.
+
+handle_disconnect(_Hs, S) ->
+ ?dbg("handle_disconnect: ~p", [_Hs]),
+ {reply, {ok,success}, S}.
+
+handle_get(_Hs, S) ->
+ ?dbg("handle_get: ~p", [_Hs]),
+ {reply, {ok,[],<<"Hello">>}, S}.
+
+handle_put(_Hs,<<>>,S) ->
+ ?dbg("handle_put: ~p data=~p", [_Hs,<<>>]),
+ {reply, {ok,success}, S};
+handle_put(_Hs,_Data,S) ->
+ ?dbg("handle_put: ~p data=~p", [_Hs,_Data]),
+ {reply, continue, S}.
+
+
+handle_abort(_Hs, S) ->
+ ?dbg("handle_abort: ~p", [_Hs]),
+ {reply, {ok,success}, S}.
+
+handle_setpath(_Hs, _Args, S) ->
+ ?dbg("handle_setpath: ~p args=~p", [_Hs,_Args]),
+ {reply, {error,not_allowed}, S}.
+
+handle_command(_Hs, S) ->
+ ?dbg("handle_command: ~p", [_Hs]),
+ {reply, {error,not_allowed}, S}.
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/deps/bt/src/obex_generic.erl b/deps/bt/src/obex_generic.erl
new file mode 100644
index 0000000..dca928a
--- /dev/null
+++ b/deps/bt/src/obex_generic.erl
@@ -0,0 +1,293 @@
+%%%---- BEGIN COPYRIGHT -------------------------------------------------------
+%%%
+%%% Copyright (C) 2006 - 2014, Rogvall Invest AB, <tony@rogvall.se>
+%%%
+%%% This software is licensed as described in the file COPYRIGHT, which
+%%% you should have received as part of this distribution. The terms
+%%% are also available at http://www.rogvall.se/docs/copyright.txt.
+%%%
+%%% You may opt to use, copy, modify, merge, publish, distribute and/or sell
+%%% copies of the Software, and permit persons to whom the Software is
+%%% furnished to do so, under the terms of the COPYRIGHT file.
+%%%
+%%% This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY
+%%% KIND, either express or implied.
+%%%
+%%%---- END COPYRIGHT ---------------------------------------------------------
+%%% File : obex_generic.erl
+%%% Author : Tony Rogvall <tony@PBook.local>
+%%% Description : Generic OBEX server
+%%% Created : 31 May 2006 by Tony Rogvall <tony@PBook.local>
+
+-module(obex_generic).
+
+-export([init/3,
+ terminate/1,
+ handle_connect/2,
+ handle_disconnect/2,
+ handle_get/2,
+ handle_put/3,
+ handle_abort/2,
+ handle_setpath/3,
+ handle_command/2]).
+
+-include_lib("kernel/include/file.hrl").
+
+-record(s,
+ {
+ path, %% current path
+ id, %% connection id
+ sref, %% server reference
+ fd, %% current file
+ opts, %% server options
+ mtu %% peer mtu
+ }).
+
+-ifdef(debug).
+-define(dbg(Fmt,As), io:format("~s:~w:" Fmt "\n", [?FILE,?LINE | As])).
+-else.
+-define(dbg(Fmt,As), ok).
+-endif.
+
+%%
+%% init(SrvRef, SrvOpts) -> S
+%%
+init(SrvRef, SrvOpts, PeerMtu) ->
+ ?dbg("init: ~p, opts=~p, peer_mtu=~p", [SrvRef, SrvOpts, PeerMtu]),
+ {ok,Path} = file:get_cwd(),
+ #s { path=Path, sref = SrvRef, opts = SrvOpts, mtu=PeerMtu }.
+
+%%
+%% Terminate
+%%
+terminate(S) ->
+ ?dbg("termiate", []),
+ if S#s.fd == undefined ->
+ ok;
+ true ->
+ file:close(S#s.fd)
+ end.
+%%
+%% handle_connect(Target,S) ->
+%% {reply, {ok,ConnectHeaders}, S'}
+%% | {reply, {error,Reason}, S'}
+%%
+handle_connect(Target,S) ->
+ ?dbg("handle_connect: ~p", [Target]),
+ if Target == undefined ->
+ {reply, {ok,[]}, S};
+ true ->
+ ID = 1234,
+ {reply, {ok,[{connectionID,ID}, {who,Target}]},
+ S#s { id = ID }}
+ end.
+
+%%
+%% handle_disconnect(Headers,S) ->
+%% {reply, {ok,success}, S'}
+%% | {reply, {error,Reason}, S'}
+%%
+handle_disconnect(_Hs, S) ->
+ ?dbg("handle_disconnect: ~p", [_Hs]),
+ {reply, {ok,success}, S}.
+
+%%
+%% handle_get(Headers, S) ->
+%%
+%% {reply, {ok,Headers,Data}, S'}
+%% | {reply, {continue,Headers,Data}, S'}
+%% | {reply, {error,Reason}, S'}
+%%
+%% handle_get is called the first time after all request headers
+%% has been read.
+%% After that handle_get will be until bodyEnd is returned
+%%
+%%
+handle_get(Hs, S) ->
+ ?dbg("handle_get: ~p", [Hs]),
+ if S#s.fd == undefined ->
+ get_init(Hs, S);
+ true ->
+ get_continue(Hs, S)
+ end.
+
+%%
+%% handle_put(Headers, Data, S) ->
+%% {reply, continue, S'}
+%% | {reply, {error,Reason}, S'}
+%% | {reply, {ok,success}, S'}
+%%
+%% handle_put will be called after all request headers
+%% (not including body nor bodyEnd)
+%% Data found among inital request headers are put in the Data field
+%%
+%% The last call will have Headers=[] and Data=<<>>
+%%
+handle_put(Hs, Data, S) ->
+ ?dbg("handle_put: ~p data=~p", [Hs,Data]),
+ if S#s.fd == undefined ->
+ put_init(Hs, Data, S);
+ true ->
+ put_continue(Hs,Data,S)
+ end.
+
+%%
+%% handle_abort(Headers, S) ->
+%% {reply, {ok,success}, S}
+%% | {reply, {error,Reason}, S}
+%%
+handle_abort(_Hs, S) ->
+ ?dbg("handle_abort: ~p", [_Hs]),
+ if S#s.fd == undefined ->
+ {reply, {ok,success}, S};
+ true ->
+ file:close(S#s.fd),
+ {reply, {ok,success}, S#s { fd = undefined} }
+ end.
+
+%%
+%% handle_setpath(Header, Args, S) ->
+%% {reply, {ok,success}, S}
+%% | {reply, {error,Reason}, S}
+%%
+handle_setpath(Hs, _Args, S) ->
+ ?dbg("handle_setpath: ~p args=~p", [Hs,_Args]),
+ %% FIXME: use Args to create path and do ..
+ case lists:keysearch(name, 1, Hs) of
+ false ->
+ {reply, {error, conflict}, S};
+ {value,{_,Path}} ->
+ Path1 = filename:join(S#s.path, Path),
+ {reply, {ok, success}, S#s { path=Path1}}
+ end.
+
+%%
+%% handle_command(Headers, S) ->
+%% {reply, {ok,sucess}, S'}
+%% | {reply, {error,Reason}, S'}
+%%
+%%
+%%
+handle_command(_Hs, S) ->
+ ?dbg("handle_command: ~p", [_Hs]),
+ {reply, {ok,success}, S}.
+
+%%
+%% Return first headers
+%% Normally return things like:
+%% [{name, FileName},
+%% {type,MimeType},
+%% {length,Size},
+%% {time,IsoTimeStamp},
+%% {body,Data}, {bodyEnd,Data}]
+%%
+get_init(Hs, S) ->
+ case lists:keysearch(name, 1, Hs) of
+ false ->
+ {reply, {error, conflict}, S};
+ {value,{_,File}} ->
+ Path = filename:join(S#s.path, File),
+ case get_info(Path) of
+ {ok,Info} ->
+ case file:open(Path, [read,binary]) of
+ {ok,Fd} ->
+ Hs = [{name,File}|Info],
+ RdSz = data_mtu(Hs, S#s.mtu),
+ ?dbg("obex_generic: data_mtu: ~w\n", [RdSz]),
+ case file:read(Fd, RdSz) of
+ eof ->
+ file:close(Fd),
+ {reply,{ok,Hs,<<>>},S};
+ {ok,Data} ->
+ {reply,{continue,Hs,Data},S#s{fd=Fd}}
+ end;
+ _Error ->
+ {reply,{error,bad_request}, S}
+ end;
+ Error ->
+ {reply, Error, S}
+ end
+ end.
+
+%%
+%% Continue
+%%
+get_continue(_Hs, S) ->
+ RdSz = data_mtu([], S#s.mtu),
+ ?dbg("obex_generic: data_mtu: ~w\n", [RdSz]),
+ case file:read(S#s.fd, RdSz) of
+ eof ->
+ file:close(S#s.fd),
+ {reply,{ok,[],<<>>}, S#s { fd = undefined }};
+ {ok,Data} ->
+ {reply,{continue,[],Data}, S}
+ end.
+
+%%
+%% Get info headers
+%%
+get_info(File) ->
+ case file:read_file_info(File) of
+ {error,enoent} ->
+ {error,not_found};
+ {error,eaccess} ->
+ {error, unauthorized};
+ {ok,I} ->
+ if I#file_info.type =/= regular ->
+ {error, not_allowed};
+ I#file_info.access == none; I#file_info.access == write ->
+ {error, unauthorized};
+ true ->
+ {ok,[{length,I#file_info.size},
+ {time,obex:time_iso(I#file_info.mtime)}]}
+ end
+ end.
+
+%%
+%% Calculate usable data MTU
+%%
+data_mtu(Hs, Mtu) ->
+ Sz = obex:size_headers(Hs),
+ %% remove 3 byte for data header and 3 byte for response header
+ (Mtu - (Sz rem Mtu))-6.
+
+%%
+%% Put init: process initial put request
+%%
+put_init(Hs, Data, S) ->
+ case lists:keysearch(name, 1, Hs) of
+ false ->
+ {reply, {error, conflict}, S};
+ {value, {_,File}} ->
+ Path = filename:join(S#s.path, File),
+ case file:open(Path, [write,binary]) of
+ {ok,Fd} ->
+ put_write(Fd, Data, S);
+ _Error ->
+ %% FIXME: check reason
+ {reply, {error,unauthorized}, S}
+ end
+ end.
+
+put_continue(_Hs, <<>>, S) ->
+ file:close(S#s.fd),
+ {reply, {ok,success}, S#s {fd = undefined}};
+put_continue(_Hs, Data, S) ->
+ put_write(S#s.fd, Data, S).
+
+put_write(Fd, <<>>, S) ->
+ {reply, continue, S#s { fd=Fd }};
+put_write(Fd, Data, S) ->
+ file:write(Fd, Data), %% FIXME: handle error
+ {reply, continue, S#s { fd=Fd }}.
+
+
+
+
+
+
+
+
+
+
+
diff --git a/deps/bt/src/obex_rfcomm.erl b/deps/bt/src/obex_rfcomm.erl
new file mode 100644
index 0000000..cec5bb2
--- /dev/null
+++ b/deps/bt/src/obex_rfcomm.erl
@@ -0,0 +1,254 @@
+%%%---- BEGIN COPYRIGHT -------------------------------------------------------
+%%%
+%%% Copyright (C) 2006 - 2014, Rogvall Invest AB, <tony@rogvall.se>
+%%%
+%%% This software is licensed as described in the file COPYRIGHT, which
+%%% you should have received as part of this distribution. The terms
+%%% are also available at http://www.rogvall.se/docs/copyright.txt.
+%%%
+%%% You may opt to use, copy, modify, merge, publish, distribute and/or sell
+%%% copies of the Software, and permit persons to whom the Software is
+%%% furnished to do so, under the terms of the COPYRIGHT file.
+%%%
+%%% This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY
+%%% KIND, either express or implied.
+%%%
+%%%---- END COPYRIGHT ---------------------------------------------------------
+%%%-------------------------------------------------------------------
+%%% File : obex_rfcomm.erl
+%%% Author : Tony Rogvall <tony@PBook.local>
+%%% Description : OBEX RFCOMM API
+%%%
+%%% Created : 31 May 2006 by Tony Rogvall <tony@PBook.local>
+%%%-------------------------------------------------------------------
+-module(obex_rfcomm).
+
+-behaviour(gen_server).
+
+%% SERVER
+-export([server_link/2, server/2]).
+-export([stop/1]).
+-export([server_accept/3]).
+-export([register/3, unregister/2, lookup/2]).
+
+-export([open/3, close/1, listen/2, accept/1, send/2]).
+
+%% gen_server callbacks
+-export([init/1, handle_call/3, handle_cast/2, handle_info/2,
+ terminate/2, code_change/3]).
+
+-ifdef(debug).
+-define(dbg(Fmt,As), io:format("~s:~w:" Fmt "\n", [?FILE,?LINE | As])).
+-else.
+-define(dbg(Fmt,As), ok).
+-endif.
+
+-record(state,
+ {
+ channel, %% listen channel
+ ref, %% ListenRef
+ apid, %% Current accept process
+ targets=[], %% Targets associated
+ opts %% Options
+ }).
+
+%%====================================================================
+%% API
+%%====================================================================
+
+open(Address, Channel, _Opts) ->
+ rfcomm:open(Address, Channel).
+
+listen(Channel, _Opts) ->
+ rfcomm:listen(Channel).
+
+accept(ListenRef) -> %% async accept!!
+ rfcomm:accept(ListenRef).
+
+close(RFComm) ->
+ rfcomm:close(RFComm).
+
+send(RFComm, Data) ->
+ rfcomm:send(RFComm, Data).
+
+register(Server, Target, Module) ->
+ gen_server:call(Server, {register, Target, Module}).
+
+unregister(Server, Target) ->
+ gen_server:call(Server, {register, Target}).
+
+lookup(Server, Target) ->
+ gen_server:call(Server, {lookup, Target}).
+
+%%--------------------------------------------------------------------
+%% Function: start_link() -> {ok,Pid} | ignore | {error,Error}
+%% Description: Starts the server
+%%--------------------------------------------------------------------
+server_link(Port, Opts) ->
+ gen_server:start_link(?MODULE, [Port,Opts], []).
+
+server(Port, Opts) ->
+ gen_server:start(?MODULE, [Port,Opts], []).
+
+stop(Server) ->
+ gen_server:call(Server, stop).
+
+%%====================================================================
+%% gen_server callbacks
+%%====================================================================
+
+%%--------------------------------------------------------------------
+%% Function: init(Args) -> {ok, State} |
+%% {ok, State, Timeout} |
+%% ignore |
+%% {stop, Reason}
+%% Description: Initiates the server
+%%--------------------------------------------------------------------
+init([Channel,Opts]) ->
+ Channel1 =
+ case lists:keysearch(sdp, 1, Opts) of
+ false ->
+ Channel;
+ {value,{_,ServiceRecord}} ->
+ case bt_drv:service_add(ServiceRecord) of
+ {ok,Handle} ->
+ case bt_drv:service_rfcomm(Handle) of
+ {ok,RealChannel} ->
+ io:format("Registered server: ~p rfcomm=~p\n",
+ [Handle,RealChannel]),
+ RealChannel;
+ _Error ->
+ io:format("No RFCOMM channel found,handle=~p\n",
+ [Handle]),
+ Channel
+ end;
+ SdpError ->
+ io:format("Unable to registered DM server: ~p\n",
+ [SdpError]),
+ Channel
+ end
+ end,
+ case listen(Channel1, [binary]) of
+ {ok,ListenRef} ->
+ process_flag(trap_exit, true),
+ APid = proc_lib:spawn_link(?MODULE, server_accept,
+ [self(), ListenRef,Opts]),
+ {ok, #state { channel=Channel,
+ ref = ListenRef,
+ apid = APid,
+ targets=opt_targets(Opts),
+ opts = Opts }};
+ Error ->
+ {stop, Error}
+ end.
+
+%%--------------------------------------------------------------------
+%% Function: %% handle_call(Request, From, State) -> {reply, Reply, State} |
+%% {reply, Reply, State, Timeout} |
+%% {noreply, State} |
+%% {noreply, State, Timeout} |
+%% {stop, Reason, Reply, State} |
+%% {stop, Reason, State}
+%% Description: Handling call messages
+%%--------------------------------------------------------------------
+handle_call(stop, _From, State) ->
+ {stop, normal, ok, State};
+handle_call({register, Target, Module}, _From, State) ->
+ Targets = State#state.targets,
+ case lists:keysearch(Target, 1, Targets) of
+ false ->
+ State1 = State#state { targets = [{Target,Module}|Targets]},
+ {reply, ok, State1};
+ {value,_} ->
+ {reply, {error, already_registered}, State}
+ end;
+handle_call({unregister, Target}, _From, State) ->
+ Targets = lists:keydelete(Target, 1, State#state.targets),
+ {reply, ok, State#state { targets = Targets }};
+handle_call({lookup, Target}, _From, State) ->
+ case lists:keysearch(Target, 1, State#state.targets) of
+ false -> {reply, {error, not_found}, State};
+ {value,{_, Module}} -> {reply, {ok, Module}, State}
+ end;
+handle_call(_Request, _From, State) ->
+ {reply, {error, bad_call}, State}.
+
+%%--------------------------------------------------------------------
+%% Function: handle_cast(Msg, State) -> {noreply, State} |
+%% {noreply, State, Timeout} |
+%% {stop, Reason, State}
+%% Description: Handling cast messages
+%%--------------------------------------------------------------------
+handle_cast({accept,Pid,{error,closed}}, State) when Pid == State#state.apid ->
+ {stop, closed, State}; %% Listen socket has closed
+handle_cast({accept,Pid,Reason}, State) when Pid == State#state.apid ->
+ io:format("~s: accept ~p\n", [?MODULE, Reason]),
+ unlink(State#state.apid),
+ APid = proc_lib:spawn_link(?MODULE, server_accept, [self(), State#state.ref, State#state.opts]),
+ {noreply, State#state { apid = APid }};
+handle_cast({'EXIT',Pid,Reason}, State) when Pid == State#state.apid ->
+ io:format("~s: EXIT ~p\n", [?MODULE, Reason]),
+ APid = proc_lib:spawn_link(?MODULE, server_accept, [self(), State#state.ref, State#state.opts]),
+ {noreply, State#state { apid = APid }};
+handle_cast(_Msg, State) ->
+ {noreply, State}.
+
+%%--------------------------------------------------------------------
+%% Function: handle_info(Info, State) -> {noreply, State} |
+%% {noreply, State, Timeout} |
+%% {stop, Reason, State}
+%% Description: Handling all non call/cast messages
+%%--------------------------------------------------------------------
+handle_info(_Info, State) ->
+ {noreply, State}.
+
+%%--------------------------------------------------------------------
+%% Function: terminate(Reason, State) -> void()
+%% Description: This function is called by a gen_server when it is about to
+%% terminate. It should be the opposite of Module:init/1 and do any necessary
+%% cleaning up. When it returns, the gen_server terminates with Reason.
+%% The return value is ignored.
+%%--------------------------------------------------------------------
+terminate(_Reason, _State) ->
+ ok.
+
+%%--------------------------------------------------------------------
+%% Func: code_change(OldVsn, State, Extra) -> {ok, NewState}
+%% Description: Convert process state when code is changed
+%%--------------------------------------------------------------------
+code_change(_OldVsn, State, _Extra) ->
+ {ok, State}.
+
+%%--------------------------------------------------------------------
+%%% Internal functions
+%%--------------------------------------------------------------------
+
+opt_targets(Opts) ->
+ opt_targets(Opts,[]).
+
+opt_targets([{target,{Target,Module}}|Opts], Ts)
+ when is_list(Target), is_atom(Module) ->
+ opt_targets(Opts, [{Target,Module}|Ts]);
+opt_targets([_|Opts], Ts) ->
+ opt_targets(Opts, Ts);
+opt_targets([], Ts) ->
+ Ts.
+
+
+server_accept(Server, ListenRef, Opts) ->
+ case accept(ListenRef) of
+ {ok,ARef} ->
+ receive
+ {rfcomm,ARef,{accept,Address,Channel}} ->
+ gen_server:cast(Server, {accept, self(), ok}),
+ obex:server_init(Server, ARef, Address, Channel, ?MODULE, Opts);
+ {rfcomm,ARef,{data,_Data}} ->
+ ?dbg("Error: got data ~p\n", [_Data]),
+ close(ARef),
+ gen_server:cast(Server, {accept, self(), ok});
+ {rfcomm,ARef,closed} ->
+ gen_server:cast(Server, {accept, self(), {error,closed}})
+ end;
+ Error ->
+ gen_server:cast(Server, {accept, self(), Error})
+ end.
diff --git a/deps/bt/src/obex_sync_server.erl b/deps/bt/src/obex_sync_server.erl
new file mode 100644
index 0000000..f966a88
--- /dev/null
+++ b/deps/bt/src/obex_sync_server.erl
@@ -0,0 +1,137 @@
+%%%---- BEGIN COPYRIGHT -------------------------------------------------------
+%%%
+%%% Copyright (C) 2006 - 2014, Rogvall Invest AB, <tony@rogvall.se>
+%%%
+%%% This software is licensed as described in the file COPYRIGHT, which
+%%% you should have received as part of this distribution. The terms
+%%% are also available at http://www.rogvall.se/docs/copyright.txt.
+%%%
+%%% You may opt to use, copy, modify, merge, publish, distribute and/or sell
+%%% copies of the Software, and permit persons to whom the Software is
+%%% furnished to do so, under the terms of the COPYRIGHT file.
+%%%
+%%% This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY
+%%% KIND, either express or implied.
+%%%
+%%%---- END COPYRIGHT ---------------------------------------------------------
+%%% File : obex_dm_server.erl
+%%% Author : Tony Rogvall <tony@PBook.local>
+%%% Description : OBEX SYNCML Server
+%%% Created : 29 May 2006 by Tony Rogvall <tony@PBook.local>
+
+-module(obex_sync_server).
+
+-export([start/0, start/1]).
+-export([init/3,
+ terminate/1,
+ handle_connect/2,
+ handle_disconnect/2,
+ handle_get/2,
+ handle_put/3,
+ handle_abort/2,
+ handle_setpath/3,
+ handle_command/2]).
+-export([sdp_info/1]).
+
+-include("../include/sdp.hrl").
+
+-record(s,
+ {
+ sref,
+ opts
+ }).
+
+-ifdef(debug).
+-define(dbg(Fmt,As), io:format("~s:~w:" Fmt "\n", [?FILE,?LINE | As])).
+-else.
+-define(dbg(Fmt,As), ok).
+-endif.
+
+
+sdp_info(Channel) ->
+ Base1 = 16#0100,
+ [{?ATTR_ServiceRecordHandle, {uint32, 65000}}, %% Should not be needed ?
+ {?ATTR_ServiceClassIDList,
+ {sequence,[{uuid,?UUID_SyncMLServer}]}},
+ {?ATTR_ProtocolDescriptorList,
+ {sequence,[{sequence,[{uuid,?UUID_L2CAP}]},
+ {sequence,[{uuid,?UUID_RFCOMM},{uint8,Channel}]},
+ {sequence,[{uuid,?UUID_OBEX}]}]}},
+ {?ATTR_BrowseGroupList,
+ {sequence,[{uuid,?UUID_PublicBrowseGroup}]}},
+ {?ATTR_LanguageBaseAttributeIDList,
+ {sequence, [{uint16, ?LANGUAGE($e,$n)},
+ {uint16, ?ENCODING_UTF8},
+ {uint16, Base1}]}},
+ {?ATTR_ServiceName+Base1, {text,"SyncMLServer"}}].
+
+
+start() ->
+ start(14).
+
+start(Channel) ->
+ %% NOTE: The Channel number is just a hint, the
+ %% number may be any in the range 1-30
+ obex:server(Channel, [{transport,rfcomm},
+ {sdp, sdp_info(Channel)},
+ {target,{"SYNCML-SYNC",?MODULE}}]).
+
+%%
+%% Below is NOT server functions
+%% but the connection side of the server
+%%
+
+
+init(SrvRef, SrvOpts, _Mtu) ->
+ ?dbg("init: ~p, opts=~p, peer_mtu=~p", [SrvRef, SrvOpts, _Mtu]),
+ #s { sref = SrvRef, opts = SrvOpts}.
+
+terminate(_S) ->
+ ?dbg("termiate", []),
+ ok.
+
+handle_connect(Target,S) ->
+ ?dbg("handle_connect: ~p", [Target]),
+ ID = 1235, %% FIXME
+ {reply, {ok,[{connectionID,ID},{who,Target}]}, S}.
+
+handle_disconnect(_Hs, S) ->
+ ?dbg("handle_disconnect: ~p", [_Hs]),
+ {reply, {ok,success}, S}.
+
+handle_get(_Hs, S) ->
+ ?dbg("handle_get: ~p", [_Hs]),
+ {reply, {ok,[],<<"Hello">>}, S}.
+
+handle_put(_Hs,<<>>,S) ->
+ ?dbg("handle_put: ~p data=~p", [_Hs,<<>>]),
+ {reply, {ok,success}, S};
+handle_put(_Hs,_Data,S) ->
+ ?dbg("handle_put: ~p data=~p", [_Hs,_Data]),
+ {reply, continue, S}.
+
+handle_abort(_Hs, S) ->
+ ?dbg("handle_abort: ~p", [_Hs]),
+ {reply, {ok,success}, S}.
+
+handle_setpath(_Hs, _Args, S) ->
+ ?dbg("handle_setpath: ~p args=~p", [_Hs,_Args]),
+ {reply, {error,not_allowed}, S}.
+
+handle_command(_Hs, S) ->
+ ?dbg("handle_command: ~p", [_Hs]),
+ {reply, {error,not_allowed}, S}.
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/deps/bt/src/obex_tcp.erl b/deps/bt/src/obex_tcp.erl
new file mode 100644
index 0000000..9067a3d
--- /dev/null
+++ b/deps/bt/src/obex_tcp.erl
@@ -0,0 +1,224 @@
+%%%---- BEGIN COPYRIGHT -------------------------------------------------------
+%%%
+%%% Copyright (C) 2006 - 2014, Rogvall Invest AB, <tony@rogvall.se>
+%%%
+%%% This software is licensed as described in the file COPYRIGHT, which
+%%% you should have received as part of this distribution. The terms
+%%% are also available at http://www.rogvall.se/docs/copyright.txt.
+%%%
+%%% You may opt to use, copy, modify, merge, publish, distribute and/or sell
+%%% copies of the Software, and permit persons to whom the Software is
+%%% furnished to do so, under the terms of the COPYRIGHT file.
+%%%
+%%% This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY
+%%% KIND, either express or implied.
+%%%
+%%%---- END COPYRIGHT ---------------------------------------------------------
+%%%-------------------------------------------------------------------
+%%% File : obex_tcp.erl
+%%% Author : Tony Rogvall <tony@PBook.local>
+%%% Description : OBEX TCP API
+%%%
+%%% Created : 31 May 2006 by Tony Rogvall <tony@PBook.local>
+%%%-------------------------------------------------------------------
+-module(obex_tcp).
+
+-behaviour(gen_server).
+
+%% SERVER
+-export([server_link/2, server/2]).
+-export([stop/1]).
+-export([server_accept/3]).
+-export([register/3, unregister/2, lookup/2]).
+
+-export([open/3, close/1, listen/2, accept/1, send/2]).
+
+%% gen_server callbacks
+-export([init/1, handle_call/3, handle_cast/2, handle_info/2,
+ terminate/2, code_change/3]).
+
+-ifdef(debug).
+-define(dbg(Fmt,As), io:format("~s:~w:" Fmt "\n", [?FILE,?LINE | As])).
+-else.
+-define(dbg(Fmt,As), ok).
+-endif.
+
+
+-record(state,
+ {
+ channel, %% listen channel
+ ref, %% ListenRef
+ apid, %% Current accept process
+ targets=[], %% Targets associated
+ opts %% Options
+ }).
+
+%%====================================================================
+%% API
+%%====================================================================
+
+open(Address, Port, Opts) ->
+ gen_tcp:connect(Address, Port, Opts).
+
+listen(Port, Opts) ->
+ gen_tcp:listen(Port, Opts).
+
+accept(Listen) ->
+ gen_tcp:accept(Listen).
+
+close(Tcp) ->
+ gen_tcp:close(Tcp).
+
+send(Tcp, Data) ->
+ gen_tcp:send(Tcp, Data).
+
+register(Server, Target, Module) ->
+ gen_server:call(Server, {register, Target, Module}).
+
+unregister(Server, Target) ->
+ gen_server:call(Server, {register, Target}).
+
+lookup(Server, Target) ->
+ gen_server:call(Server, {lookup, Target}).
+
+%%--------------------------------------------------------------------
+%% Function: start_link() -> {ok,Pid} | ignore | {error,Error}
+%% Description: Starts the server
+%%--------------------------------------------------------------------
+
+server_link(Port, Opts) ->
+ gen_server:start_link(?MODULE, [Port,Opts], []).
+
+server(Port, Opts) ->
+ gen_server:start(?MODULE, [Port,Opts], []).
+
+stop(Server) ->
+ gen_server:call(Server, stop).
+%%====================================================================
+%% gen_server callbacks
+%%====================================================================
+
+%%--------------------------------------------------------------------
+%% Function: init(Args) -> {ok, State} |
+%% {ok, State, Timeout} |
+%% ignore |
+%% {stop, Reason}
+%% Description: Initiates the server
+%%--------------------------------------------------------------------
+init([Port,Opts]) ->
+ case listen(Port, [binary]) of
+ {ok,ListenRef} ->
+ process_flag(trap_exit, true),
+ APid = proc_lib:spawn_link(?MODULE, server_accept, [self(), ListenRef,Opts]),
+ {ok, #state { channel=Port,
+ ref = ListenRef,
+ apid = APid,
+ targets=opt_targets(Opts),
+ opts = Opts }};
+ Error ->
+ {stop, Error}
+ end.
+
+%%--------------------------------------------------------------------
+%% Function: %% handle_call(Request, From, State) -> {reply, Reply, State} |
+%% {reply, Reply, State, Timeout} |
+%% {noreply, State} |
+%% {noreply, State, Timeout} |
+%% {stop, Reason, Reply, State} |
+%% {stop, Reason, State}
+%% Description: Handling call messages
+%%--------------------------------------------------------------------
+handle_call(stop, _From, State) ->
+ {stop, normal, ok, State};
+handle_call({register, Target, Module}, _From, State) ->
+ Targets = State#state.targets,
+ case lists:keysearch(Target, 1, Targets) of
+ false ->
+ State1 = State#state { targets = [{Target,Module}|Targets]},
+ {reply, ok, State1};
+ {value,_} ->
+ {reply, {error, already_registered}, State}
+ end;
+handle_call({unregister, Target}, _From, State) ->
+ Targets = lists:keydelete(Target, 1, State#state.targets),
+ {reply, ok, State#state { targets = Targets }};
+handle_call({lookup, Target}, _From, State) ->
+ case lists:keysearch(Target, 1, State#state.targets) of
+ false -> {reply, {error, not_found}, State};
+ {value,{_, Module}} -> {reply, {ok, Module}, State}
+ end;
+handle_call(_Request, _From, State) ->
+ {reply, {error, bad_call}, State}.
+
+%%--------------------------------------------------------------------
+%% Function: handle_cast(Msg, State) -> {noreply, State} |
+%% {noreply, State, Timeout} |
+%% {stop, Reason, State}
+%% Description: Handling cast messages
+%%--------------------------------------------------------------------
+handle_cast({accept,Pid,{error,closed}}, State) when Pid == State#state.apid ->
+ {stop, closed, State}; %% Listen socket has closed
+handle_cast({accept,Pid,Reason}, State) when Pid == State#state.apid ->
+ io:format("~s: accept ~p\n", [?MODULE, Reason]),
+ unlink(State#state.apid),
+ APid = proc_lib:spawn_link(?MODULE, server_accept, [self(), State#state.ref, State#state.opts]),
+ {noreply, State#state { apid = APid }};
+handle_cast({'EXIT',Pid,Reason}, State) when Pid == State#state.apid ->
+ io:format("~s: EXIT ~p\n", [?MODULE, Reason]),
+ APid = proc_lib:spawn_link(?MODULE, server_accept, [self(), State#state.ref, State#state.opts]),
+ {noreply, State#state { apid = APid }};
+handle_cast(_Msg, State) ->
+ {noreply, State}.
+
+%%--------------------------------------------------------------------
+%% Function: handle_info(Info, State) -> {noreply, State} |
+%% {noreply, State, Timeout} |
+%% {stop, Reason, State}
+%% Description: Handling all non call/cast messages
+%%--------------------------------------------------------------------
+handle_info(_Info, State) ->
+ {noreply, State}.
+
+%%--------------------------------------------------------------------
+%% Function: terminate(Reason, State) -> void()
+%% Description: This function is called by a gen_server when it is about to
+%% terminate. It should be the opposite of Module:init/1 and do any necessary
+%% cleaning up. When it returns, the gen_server terminates with Reason.
+%% The return value is ignored.
+%%--------------------------------------------------------------------
+terminate(_Reason, _State) ->
+ ok.
+
+%%--------------------------------------------------------------------
+%% Func: code_change(OldVsn, State, Extra) -> {ok, NewState}
+%% Description: Convert process state when code is changed
+%%--------------------------------------------------------------------
+code_change(_OldVsn, State, _Extra) ->
+ {ok, State}.
+
+%%--------------------------------------------------------------------
+%%% Internal functions
+%%--------------------------------------------------------------------
+
+opt_targets(Opts) ->
+ opt_targets(Opts,[]).
+
+opt_targets([{target,{Target,Module}}|Opts], Ts)
+ when is_list(Target), is_atom(Module) ->
+ opt_targets(Opts, [{Target,Module}|Ts]);
+opt_targets([_|Opts], Ts) ->
+ opt_targets(Opts, Ts);
+opt_targets([], Ts) ->
+ Ts.
+
+
+server_accept(Server, ListenRef, Opts) ->
+ case accept(ListenRef) of
+ {ok,ARef} ->
+ gen_server:cast(Server, {accept, self(), ok}),
+ {ok,{Address,_Port}} = inet:peername(ARef),
+ {ok,{_Address,Port}} = inet:sockname(ListenRef),
+ obex:server_init(Server, ARef, Address, Port, ?MODULE, Opts);
+ Error ->
+ gen_server:cast(Server, {accept, self(), Error})
+ end.
diff --git a/deps/bt/src/rfcomm.erl b/deps/bt/src/rfcomm.erl
new file mode 100644
index 0000000..9b15d72
--- /dev/null
+++ b/deps/bt/src/rfcomm.erl
@@ -0,0 +1,53 @@
+%%%---- BEGIN COPYRIGHT -------------------------------------------------------
+%%%
+%%% Copyright (C) 2006 - 2014, Rogvall Invest AB, <tony@rogvall.se>
+%%%
+%%% This software is licensed as described in the file COPYRIGHT, which
+%%% you should have received as part of this distribution. The terms
+%%% are also available at http://www.rogvall.se/docs/copyright.txt.
+%%%
+%%% You may opt to use, copy, modify, merge, publish, distribute and/or sell
+%%% copies of the Software, and permit persons to whom the Software is
+%%% furnished to do so, under the terms of the COPYRIGHT file.
+%%%
+%%% This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY
+%%% KIND, either express or implied.
+%%%
+%%%---- END COPYRIGHT ---------------------------------------------------------
+%%% File : rfcomm.erl
+%%% Author : Tony Rogvall <tony@pbook.synap.se>
+%%% Description : RFCOMM wrapper
+%%% Created : 2 Feb 2006 by Tony Rogvall <tony@pbook.synap.se>
+
+-module(rfcomm).
+
+-export([open/2,
+ close/1,
+ send/2,
+ listen/1,
+ accept/1,
+ accept/2,
+ accept/3]).
+
+open(Address, Channel) ->
+ bt_drv:rfcomm_open(Address, Channel).
+
+close(RFComm) ->
+ bt_drv:rfcomm_close(RFComm).
+
+send(RFComm, Data) ->
+ bt_drv:rfcomm_send(RFComm, Data).
+
+listen(Channel) ->
+ bt_drv:rfcomm_listen(Channel).
+
+accept(ListenRef) ->
+ accept(ListenRef, infinity, self() ).
+
+accept(ListenRef, Timeout) ->
+ accept(ListenRef, Timeout, self()).
+
+accept(ListenRef,Timeout, CtlPid) when Timeout==infinity;
+ is_integer(Timeout),Timeout>0 ->
+ bt_drv:rfcomm_accept(ListenRef,Timeout, CtlPid).
+