summaryrefslogtreecommitdiff
path: root/libaltos
diff options
context:
space:
mode:
Diffstat (limited to 'libaltos')
-rw-r--r--libaltos/Makefile.am6
-rw-r--r--libaltos/btletest.c183
-rw-r--r--libaltos/cjnitest.c6
-rw-r--r--libaltos/libaltos_common.c21
-rw-r--r--libaltos/libaltos_linux.c101
-rw-r--r--libaltos/libaltos_private.h3
-rw-r--r--libaltos/libaltos_windows.c2
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));