From 9c6727d39cc1feae3bd0504bcbc910ae832e3651 Mon Sep 17 00:00:00 2001 From: Keith Packard Date: Fri, 12 May 2017 01:35:52 -0700 Subject: Use SDP to locate the service channel on Linux Signed-off-by: Keith Packard --- libaltos/libaltos_linux.c | 97 ++++++++++++++++++++++++++++++++++++++++++++--- 1 file changed, 92 insertions(+), 5 deletions(-) (limited to 'libaltos') diff --git a/libaltos/libaltos_linux.c b/libaltos/libaltos_linux.c index 1cade72d..b187bb19 100644 --- a/libaltos/libaltos_linux.c +++ b/libaltos/libaltos_linux.c @@ -26,6 +26,8 @@ #include #include #include +#include +#include static char * cc_fullname (char *dir, char *file) @@ -380,6 +382,30 @@ bt_func(hci_get_route, int, -1, (bdaddr_t *bdaddr), (bdaddr)) bt_func(hci_inquiry, int, -1, (int adapter_id, int len, int max_rsp, const uint8_t *lap, inquiry_info **devs, long flags), (adapter_id, len, max_rsp, lap, devs, flags)) #define hci_inquiry altos_hci_inquiry +bt_func(sdp_connect, sdp_session_t *, 0, (const bdaddr_t *src, const bdaddr_t *dst, uint32_t flags), (src, dst, flags)) +#define sdp_connect altos_sdp_connect + +bt_func(sdp_uuid16_create, uuid_t *, 0, (uuid_t *uuid, uint16_t data), (uuid, data)) +#define sdp_uuid16_create altos_sdp_uuid16_create + +bt_func(sdp_list_append, sdp_list_t *, 0, (sdp_list_t *list, void *d), (list, d)) +#define sdp_list_append altos_sdp_list_append + +bt_func(sdp_service_search_attr_req, int, -1, (sdp_session_t *session, const sdp_list_t *search, sdp_attrreq_type_t reqtype, const sdp_list_t *attrid_list, sdp_list_t **rsp_list), (session, search, reqtype, attrid_list, rsp_list)) +#define sdp_service_search_attr_req altos_sdp_service_search_attr_req + +bt_func(sdp_uuid_to_proto, int, 0, (uuid_t *uuid), (uuid)) +#define sdp_uuid_to_proto altos_sdp_uuid_to_proto + +bt_func(sdp_get_access_protos, int, 0, (const sdp_record_t *rec, sdp_list_t **protos), (rec, protos)) +#define sdp_get_access_protos altos_sdp_get_access_protos + +bt_func(sdp_get_proto_port, int, 0, (const sdp_list_t *list, int proto), (list, proto)) +#define sdp_get_proto_port altos_sdp_get_proto_port + +bt_func(sdp_close, int, 0, (sdp_session_t *session), (session)) +#define sdp_close altos_sdp_close + struct altos_bt_list { inquiry_info *ii; int sock; @@ -475,10 +501,67 @@ altos_bt_fill_in(char *name, char *addr, struct altos_bt_device *device) struct altos_file * altos_bt_open(struct altos_bt_device *device) { + static const uint8_t svc_uuid_int[] = { + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0x11, 0x01 + }; + uuid_t svc_uuid; struct sockaddr_rc addr = { 0 }; int status, i; struct altos_file_posix *file; + sdp_session_t *session = NULL; + sdp_list_t *search_list, *attrid_list; + sdp_list_t *response_list = NULL, *r; + uint32_t range; + int err; + int channel = 1; + + if (str2ba(device->addr, &addr.rc_bdaddr) < 0) { + altos_set_last_posix_error(); + goto no_file; + } + /* + * Search for the RFCOMM service to get the right channel + */ + session = sdp_connect(BDADDR_ANY, &addr.rc_bdaddr, SDP_RETRY_IF_BUSY); + + if (session) { + sdp_uuid16_create(&svc_uuid, PUBLIC_BROWSE_GROUP); + search_list = sdp_list_append(NULL, &svc_uuid); + + range = 0x0000ffff; + attrid_list = sdp_list_append(NULL, &range); + + err = sdp_service_search_attr_req(session, search_list, + SDP_ATTR_REQ_RANGE, attrid_list, &response_list); + + if (err >= 0) { + for (r = response_list; r; r = r->next) { + sdp_record_t *rec = (sdp_record_t*) r->data; + sdp_list_t *proto_list; + sdp_list_t *access = NULL; + int proto; + + proto = sdp_uuid_to_proto(&rec->svclass); + + if (proto == SERIAL_PORT_SVCLASS_ID) { + sdp_get_access_protos(rec, &access); + if (access) { + int this_chan = sdp_get_proto_port(access, RFCOMM_UUID); + if (this_chan) + channel = this_chan; + } + } + } + } + + /* Leave the session open so we don't disconnect from the device before opening + * the RFCOMM channel + */ + } + + /* Connect to the channel */ file = calloc(1, sizeof (struct altos_file_posix)); if (!file) { errno = ENOMEM; @@ -486,11 +569,7 @@ altos_bt_open(struct altos_bt_device *device) goto no_file; } addr.rc_family = AF_BLUETOOTH; - addr.rc_channel = 1; - if (str2ba(device->addr, &addr.rc_bdaddr) < 0) { - altos_set_last_posix_error(); - goto no_sock; - } + addr.rc_channel = channel; for (i = 0; i < 5; i++) { file->fd = socket(AF_BLUETOOTH, SOCK_STREAM, BTPROTO_RFCOMM); @@ -507,10 +586,16 @@ altos_bt_open(struct altos_bt_device *device) close(file->fd); usleep(100 * 1000); } + + if (status < 0) { altos_set_last_posix_error(); goto no_link; } + + if (session) + sdp_close(session); + usleep(100 * 1000); #ifdef USE_POLL @@ -524,6 +609,8 @@ no_link: no_sock: free(file); no_file: + if (session) + sdp_close(session); return NULL; } -- cgit v1.2.3 From 2536640b852feff41975d2da50befdda91ee6303 Mon Sep 17 00:00:00 2001 From: Keith Packard Date: Sat, 13 May 2017 15:24:20 -0700 Subject: libaltos: Add support for TeleBT-v4.0 bluetooth channel change The RN4678 in TeleBT v4.0 uses channel 6 instead of channel 1. There is code in the linux bits which discovers this value, but that crashes when run under java for unknown reasons. Signed-off-by: Keith Packard --- libaltos/libaltos_common.c | 21 +++++++++++++++++++++ libaltos/libaltos_linux.c | 24 ++++++++++++++---------- libaltos/libaltos_private.h | 3 +++ libaltos/libaltos_windows.c | 2 +- 4 files changed, 39 insertions(+), 11 deletions(-) (limited to 'libaltos') diff --git a/libaltos/libaltos_common.c b/libaltos/libaltos_common.c index dfafcc7a..f577de02 100644 --- a/libaltos/libaltos_common.c +++ b/libaltos/libaltos_common.c @@ -75,6 +75,27 @@ altos_putchar(struct altos_file *file, char c) return ret; } +struct bt_vendor_map { + char vendor[10]; + int port; +}; + +static const struct bt_vendor_map altos_bt_vendor_map[] = { + { .vendor = "00:12:6f:", 1 }, /* Rayson */ + { .vendor = "8C:DE:52:", 6 }, /* ISSC */ + { .vendor = "D8:80:39:", 6 }, /* Microchip */ +}; + +#define NUM_BT_VENDOR_MAP (sizeof altos_bt_vendor_map / sizeof altos_bt_vendor_map[0]) +#define BT_PORT_DEFAULT 1 + +int altos_bt_port(struct altos_bt_device *device) { + unsigned i; + for (i = 0; i < NUM_BT_VENDOR_MAP; i++) + if (strncmp (device->addr, altos_bt_vendor_map[i].vendor, strlen(altos_bt_vendor_map[i].vendor)) == 0) + return altos_bt_vendor_map[i].port; + return BT_PORT_DEFAULT; +} PUBLIC void altos_free(struct altos_file *file) diff --git a/libaltos/libaltos_linux.c b/libaltos/libaltos_linux.c index b187bb19..255b9773 100644 --- a/libaltos/libaltos_linux.c +++ b/libaltos/libaltos_linux.c @@ -501,32 +501,33 @@ altos_bt_fill_in(char *name, char *addr, struct altos_bt_device *device) struct altos_file * altos_bt_open(struct altos_bt_device *device) { - static const uint8_t svc_uuid_int[] = { - 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0x11, 0x01 - }; - uuid_t svc_uuid; struct sockaddr_rc addr = { 0 }; int status, i; struct altos_file_posix *file; sdp_session_t *session = NULL; - sdp_list_t *search_list, *attrid_list; - sdp_list_t *response_list = NULL, *r; - uint32_t range; - int err; - int channel = 1; + int channel = 0; if (str2ba(device->addr, &addr.rc_bdaddr) < 0) { altos_set_last_posix_error(); goto no_file; } +#if 0 /* * Search for the RFCOMM service to get the right channel */ session = sdp_connect(BDADDR_ANY, &addr.rc_bdaddr, SDP_RETRY_IF_BUSY); if (session) { + static const uint8_t svc_uuid_int[] = { + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0x11, 0x01 + }; + int err; + uuid_t svc_uuid; + uint32_t range; + sdp_list_t *search_list, *attrid_list; + sdp_list_t *response_list = NULL, *r; sdp_uuid16_create(&svc_uuid, PUBLIC_BROWSE_GROUP); search_list = sdp_list_append(NULL, &svc_uuid); @@ -560,6 +561,9 @@ altos_bt_open(struct altos_bt_device *device) * the RFCOMM channel */ } +#endif + if (channel == 0) + channel = altos_bt_port(device); /* Connect to the channel */ file = calloc(1, sizeof (struct altos_file_posix)); diff --git a/libaltos/libaltos_private.h b/libaltos/libaltos_private.h index f8e5231b..ee3dd708 100644 --- a/libaltos/libaltos_private.h +++ b/libaltos/libaltos_private.h @@ -61,4 +61,7 @@ altos_flush(struct altos_file *file); int altos_fill(struct altos_file *file, int timeout); +int +altos_bt_port(struct altos_bt_device *device); + #endif /* _LIBALTOS_PRIVATE_H_ */ diff --git a/libaltos/libaltos_windows.c b/libaltos/libaltos_windows.c index e53aa72b..4f9f1807 100644 --- a/libaltos/libaltos_windows.c +++ b/libaltos/libaltos_windows.c @@ -747,7 +747,7 @@ altos_bt_open(struct altos_bt_device *device) memset(&sockaddr_bth, '\0', sizeof (sockaddr_bth)); sockaddr_bth.addressFamily = AF_BTH; sockaddr_bth.btAddr = str2ba(device->addr); - sockaddr_bth.port = 1; + sockaddr_bth.port = altos_bt_port(device); ret = connect(file->socket, (SOCKADDR *) &sockaddr_bth, sizeof (sockaddr_bth)); -- cgit v1.2.3 From ea3b5815b27005b2f4c3034715f656d28ea8534e Mon Sep 17 00:00:00 2001 From: Keith Packard Date: Fri, 21 Jul 2017 16:16:55 -0700 Subject: libaltos: Add btle test program Signed-off-by: Keith Packard --- libaltos/Makefile.am | 6 +- libaltos/btletest.c | 183 +++++++++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 188 insertions(+), 1 deletion(-) create mode 100644 libaltos/btletest.c (limited to 'libaltos') diff --git a/libaltos/Makefile.am b/libaltos/Makefile.am index 8f69c1ad..69fe7a57 100644 --- a/libaltos/Makefile.am +++ b/libaltos/Makefile.am @@ -24,11 +24,15 @@ WINDOWS_SRC=\ WINDOWS_H=\ libaltos.h -noinst_PROGRAMS=cjnitest +noinst_PROGRAMS=cjnitest btletest cjnitest_SOURCES=cjnitest.c cjnitest_LDADD=libaltos.la +btletest_SOURCES=btletest.c + +btletest_LDADD=-lbluetooth + if MULTI_ARCH altoslib_LTLIBRARIES+=libaltos32.la libaltos64.la diff --git a/libaltos/btletest.c b/libaltos/btletest.c new file mode 100644 index 00000000..0e325415 --- /dev/null +++ b/libaltos/btletest.c @@ -0,0 +1,183 @@ +/* + * Copyright © 2017 Keith Packard + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + */ +#define _GNU_SOURCE + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#define ATT_OP_MTU_REQ 0x02 +#define ATT_OP_MTU_RESP 0x03 +#define ATT_OP_WRITE_CMD 0x52 +#define ATT_OP_HANDLE_NOTIFY 0x1b +#define CID_ATT 0x0004 +#define TX_ENDPOINT 0x003a +#define RX_ENDPOINT 0x0037 +#define RX_NOTIFY 0x0038 + +int +main(int argc, char **argv) +{ + int sk; + int psm; + struct sockaddr_l2 src_addr = { 0 }; + struct sockaddr_l2 dst_addr = { 0 }; + char buf[1024]; + struct pollfd fd[2]; + int n, i; + char *btaddr; + int mtu; + + btaddr = argc > 1 ? argv[1] : "D8:80:39:F3:4E:A5"; + + sk = socket(PF_BLUETOOTH, SOCK_SEQPACKET, BTPROTO_L2CAP); + if (sk < 0) { + perror("socket"); + exit(1); + } + + src_addr.l2_family = AF_BLUETOOTH; + /* Leave src_addr.l2_bdaddr all zeros */ + src_addr.l2_cid = htobs(CID_ATT); + src_addr.l2_bdaddr_type = BDADDR_LE_PUBLIC; + if (bind(sk, (struct sockaddr *) &src_addr, sizeof (src_addr)) < 0) { + perror("bind"); + exit(1); + } + + dst_addr.l2_family = AF_BLUETOOTH; + str2ba(btaddr, &dst_addr.l2_bdaddr); + dst_addr.l2_cid = htobs(CID_ATT); + dst_addr.l2_bdaddr_type = BDADDR_LE_PUBLIC; + + if (connect(sk, (struct sockaddr *) &dst_addr, sizeof(dst_addr)) < 0) { + perror("connect"); + exit(1); + } + + buf[0] = ATT_OP_MTU_REQ; + buf[1] = sizeof(buf) & 0xff; + buf[2] = sizeof(buf) >> 8; + n = write(sk, buf, 3); + if (n != 3) { + perror("write mtu\n"); + exit(1); + } + + fd[0].fd = sk; + fd[0].events = POLLIN; + for (;;) { + n = poll(fd, 1, 3000); + if (n <= 0) { + printf("timeout waiting for MTU response\n"); + exit(1); + } + if (fd[0].revents & POLLIN) { + n = read(sk, buf, sizeof(buf)); + printf("read %d\n", n); + for (i = 0; i < n; i++) + printf("%02x\n", buf[i]); + if (buf[0] == ATT_OP_MTU_RESP) { + mtu = (buf[1] & 0xff) | ((buf[2] & 0xff) << 8); + break; + } + } + } + printf("mtu %d\n", mtu); + + buf[0] = ATT_OP_WRITE_CMD; + buf[1] = RX_NOTIFY & 0xff; + buf[2] = RX_NOTIFY >> 8; + buf[3] = 1; + n = write(sk, buf, 4); + if (n != 4) { + perror("write notify"); + exit(1); + } + + fd[0].fd = 0; + fd[0].events = POLLIN; + fd[1].fd = sk; + fd[1].events = POLLIN; + + for (;;) { + n = poll(fd, 2, -1); + if (n == 0) + continue; + if (fd[0].revents & POLLIN) { + char *b; + n = read(0, buf+3, sizeof(buf)-3); + if (n < 0) { + perror("read stdin"); + exit(1); + } + if (n == 0) + break; + + b = buf; + while (n > 0) { + int this = n; + if (this > mtu - 3) + this = mtu - 3; + + b[0] = ATT_OP_WRITE_CMD; + b[1] = TX_ENDPOINT; + b[2] = TX_ENDPOINT >> 8; + if (write(sk, b, this + 3) != this + 3) { + perror("write sk"); + exit(1); + } + b += this; + n -= this; + } + } + if (fd[1].revents & POLLIN) { + uint16_t ch; + + n = read(sk, buf, sizeof(buf)); + if (n < 0) { + perror("read sk"); + exit(1); + } + if (n == 0) + continue; + ch = buf[1] | (buf[2] << 8); + switch (buf[0]) { + case ATT_OP_HANDLE_NOTIFY: + if (ch == RX_ENDPOINT) + write(1, buf+3, n-3); + break; + } + } + if (fd[1].revents & (POLLERR|POLLHUP)) + break; + } + close(sk); + + return 0; +} -- cgit v1.2.3 From c7b34e6691b632199bc7802c2e4dea4de992578e Mon Sep 17 00:00:00 2001 From: Keith Packard Date: Sun, 23 Jul 2017 16:49:26 -0700 Subject: libaltos: Test both bluetooth and USB APIs with cjnitest Signed-off-by: Keith Packard --- libaltos/cjnitest.c | 6 ++++++ 1 file changed, 6 insertions(+) (limited to 'libaltos') diff --git a/libaltos/cjnitest.c b/libaltos/cjnitest.c index 3a65c3d6..7e857275 100644 --- a/libaltos/cjnitest.c +++ b/libaltos/cjnitest.c @@ -1,5 +1,9 @@ #include #include "libaltos.h" +#include + +#define HAS_BLUETOOTH 1 +#define HAS_USB 1 static void altos_puts(struct altos_file *file, char *string) @@ -19,6 +23,7 @@ main (int argc, char **argv) struct altos_bt_list *bt_list; altos_init(); +#if HAS_USB list = altos_list_start(); while (altos_list_next(list, &device)) { struct altos_file *file; @@ -42,6 +47,7 @@ main (int argc, char **argv) altos_close(file); } altos_list_finish(list); +#endif #if HAS_BLUETOOTH bt_list = altos_bt_list_start(8); while (altos_bt_list_next(bt_list, &bt_device)) { -- cgit v1.2.3