diff options
Diffstat (limited to 'libaltos')
-rw-r--r-- | libaltos/Makefile.am | 6 | ||||
-rw-r--r-- | libaltos/btletest.c | 183 | ||||
-rw-r--r-- | libaltos/cjnitest.c | 6 | ||||
-rw-r--r-- | libaltos/libaltos_common.c | 21 | ||||
-rw-r--r-- | libaltos/libaltos_linux.c | 101 | ||||
-rw-r--r-- | libaltos/libaltos_private.h | 3 | ||||
-rw-r--r-- | libaltos/libaltos_windows.c | 2 |
7 files changed, 315 insertions, 7 deletions
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 <keithp@keithp.com> + * + * 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 <stdio.h> +#include <stdlib.h> +#include <string.h> +#include <fcntl.h> +#include <termios.h> +#include <errno.h> +#include <unistd.h> +#include <ctype.h> +#include <dirent.h> +#include <bluetooth/bluetooth.h> +#include <bluetooth/hci.h> +#include <bluetooth/hci_lib.h> +#include <bluetooth/rfcomm.h> +#include <bluetooth/sdp.h> +#include <bluetooth/sdp_lib.h> +#include <bluetooth/l2cap.h> +#include <poll.h> + +#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; +} 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 <stdio.h> #include "libaltos.h" +#include <string.h> + +#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)) { 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 1cade72d..255b9773 100644 --- a/libaltos/libaltos_linux.c +++ b/libaltos/libaltos_linux.c @@ -26,6 +26,8 @@ #include <bluetooth/hci.h> #include <bluetooth/hci_lib.h> #include <bluetooth/rfcomm.h> +#include <bluetooth/sdp.h> +#include <bluetooth/sdp_lib.h> 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; @@ -478,7 +504,68 @@ altos_bt_open(struct altos_bt_device *device) struct sockaddr_rc addr = { 0 }; int status, i; struct altos_file_posix *file; + sdp_session_t *session = NULL; + 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); + + 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 + */ + } +#endif + if (channel == 0) + channel = altos_bt_port(device); + + /* Connect to the channel */ file = calloc(1, sizeof (struct altos_file_posix)); if (!file) { errno = ENOMEM; @@ -486,11 +573,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 +590,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 +613,8 @@ no_link: no_sock: free(file); no_file: + if (session) + sdp_close(session); return NULL; } 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)); |