]> sigrok.org Git - libsigrok.git/blobdiff - src/bt/bt_bluez.c
serial_bt, bluez: rework diag in BLE reception, accept zero length data
[libsigrok.git] / src / bt / bt_bluez.c
index 644642bae132a521f23225aa457f4bb87a12fbdb..71c0dbd4e5b902fbe2126344236c676e816f9548 100644 (file)
 #include <ctype.h>
 #include <errno.h>
 #include <glib.h>
+#include <inttypes.h>
 #include <poll.h>
 #include <stdarg.h>
 #include <stdio.h>
 #include <stdlib.h>
 #include <string.h>
+#include <sys/uio.h>
 #include <sys/socket.h>
 #include <time.h>
 #include <unistd.h>
 #include <libsigrok/libsigrok.h>
 #include "libsigrok-internal.h"
 
-/** @cond PRIVATE */
 #define LOG_PREFIX "bt-bluez"
-/** @endcond */
 
+#define CONNECT_BLE_TIMEOUT    20      /* Connect timeout in seconds. */
 #define STORE_MAC_REVERSE      1
 #define ACCEPT_NONSEP_MAC      1
 
+#define CONNECT_RFCOMM_TRIES   3
+#define CONNECT_RFCOMM_RETRY_MS        100
+
 /* Silence warning about (currently) unused routine. */
 #define WITH_WRITE_TYPE_HANDLE 0
 
  * the header doesn't.
  */
 
-#if !defined HAVE_BT_PUT_LE16
-static inline void bt_put_le16(uint16_t v, uint8_t *p)
-{
-       p[0] = (v >> 0) & 0xff;
-       p[1] = (v >> 8) & 0xff;
-}
-#endif
-
 /* }}} compat decls */
 /* {{{ Linux socket specific decls */
 
@@ -216,12 +212,12 @@ SR_PRIV const char *sr_bt_adapter_get_address(size_t idx)
        char addr[20];
 
        rc = hci_devinfo(idx, &info);
-       sr_dbg("DIAG: hci_devinfo(%zu) => rc %d", idx, rc);
+       sr_spew("DIAG: hci_devinfo(%zu) => rc %d", idx, rc);
        if (rc < 0)
                return NULL;
 
        rc = ba2str(&info.bdaddr, addr);
-       sr_dbg("DIAG: ba2str() => rc %d", rc);
+       sr_spew("DIAG: ba2str() => rc %d", rc);
        if (rc < 0)
                return NULL;
 
@@ -393,7 +389,6 @@ static int sr_bt_desc_open(struct sr_bt_desc *desc, int *id_ref)
                return -1;
        sr_dbg("BLE open");
 
-       sr_spew("get devid");
        if (desc->local_addr[0]) {
                id = hci_devid(desc->local_addr);
        } else if (desc->remote_addr[0]) {
@@ -403,14 +398,13 @@ static int sr_bt_desc_open(struct sr_bt_desc *desc, int *id_ref)
                id = hci_get_route(NULL);
        }
        if (id < 0) {
-               sr_spew("devid failed");
+               sr_err("devid failed");
                return -1;
        }
        desc->devid = id;
        if (id_ref)
                *id_ref = id;
 
-       sr_spew("open HCI socket");
        sock = hci_open_dev(id);
        if (sock < 0) {
                perror("open HCI socket");
@@ -451,10 +445,8 @@ static int sr_bt_scan_prep(struct sr_bt_desc *desc)
 
        if (!desc)
                return -1;
-       sr_dbg("BLE scan prep");
 
        /* TODO Replace magic values with symbolic identifiers. */
-       sr_spew("set LE scan params");
        type = 0x01;    /* LE public? */
        ival = htobs(0x0010);
        window = htobs(0x0010);
@@ -468,7 +460,6 @@ static int sr_bt_scan_prep(struct sr_bt_desc *desc)
                return -1;
        }
 
-       sr_spew("set LE scan enable");
        enable = 1;
        dup = 1;
        timeout = 1000;
@@ -479,7 +470,6 @@ static int sr_bt_scan_prep(struct sr_bt_desc *desc)
        }
 
        /* Save the current filter. For later restoration. */
-       sr_spew("get HCI filter");
        slen = sizeof(desc->orig_filter);
        rc = getsockopt(desc->fd, SOL_HCI, HCI_FILTER,
                &desc->orig_filter, &slen);
@@ -488,7 +478,6 @@ static int sr_bt_scan_prep(struct sr_bt_desc *desc)
                return -1;
        }
 
-       sr_spew("set HCI filter");
        hci_filter_clear(&scan_filter);
        hci_filter_set_ptype(HCI_EVENT_PKT, &scan_filter);
        hci_filter_set_event(EVT_LE_META_EVENT, &scan_filter);
@@ -510,10 +499,8 @@ static int sr_bt_scan_post(struct sr_bt_desc *desc)
 
        if (!desc)
                return -1;
-       sr_dbg("BLE scan post");
 
        /* Restore previous HCI filter. */
-       sr_spew("set HCI filter");
        rc = setsockopt(desc->fd, SOL_HCI, HCI_FILTER,
                &desc->orig_filter, sizeof(desc->orig_filter));
        if (rc < 0) {
@@ -521,7 +508,6 @@ static int sr_bt_scan_post(struct sr_bt_desc *desc)
                return -1;
        }
 
-       sr_spew("set LE scan enable");
        enable = 0;
        dup = 1;
        timeout = 1000;
@@ -571,17 +557,14 @@ SR_PRIV int sr_bt_scan_le(struct sr_bt_desc *desc, int duration)
                return -1;
        sr_dbg("BLE scan (LE)");
 
-       sr_spew("desc open");
        rc = sr_bt_desc_open(desc, NULL);
        if (rc < 0)
                return -1;
 
-       sr_spew("scan prep");
        rc = sr_bt_scan_prep(desc);
        if (rc < 0)
                return -1;
 
-       sr_spew("scan loop");
        deadline = time(NULL);
        deadline += duration;
        while (time(NULL) <= deadline) {
@@ -592,9 +575,7 @@ SR_PRIV int sr_bt_scan_le(struct sr_bt_desc *desc, int duration)
                if (rdlen < 0)
                        break;
                if (!rdlen) {
-                       sr_spew("usleep() start");
                        g_usleep(50000);
-                       sr_spew("usleep() done");
                        continue;
                }
                if (rdlen < 1 + HCI_EVENT_HDR_SIZE)
@@ -623,7 +604,6 @@ SR_PRIV int sr_bt_scan_le(struct sr_bt_desc *desc, int duration)
                }
        }
 
-       sr_spew("scan post");
        rc = sr_bt_scan_post(desc);
        if (rc < 0)
                return -1;
@@ -683,6 +663,7 @@ SR_PRIV int sr_bt_connect_ble(struct sr_bt_desc *desc)
        struct sockaddr_l2 sl2;
        bdaddr_t mac;
        int s, ret;
+       gint64 deadline;
 
        if (!desc)
                return -1;
@@ -690,7 +671,6 @@ SR_PRIV int sr_bt_connect_ble(struct sr_bt_desc *desc)
                return -1;
        sr_dbg("BLE connect, remote addr %s", desc->remote_addr);
 
-       sr_spew("socket()");
        s = socket(AF_BLUETOOTH, SOCK_SEQPACKET, 0);
        if (s < 0) {
                perror("socket create");
@@ -698,7 +678,6 @@ SR_PRIV int sr_bt_connect_ble(struct sr_bt_desc *desc)
        }
        desc->fd = s;
 
-       sr_spew("bind()");
        memset(&sl2, 0, sizeof(sl2));
        sl2.l2_family = AF_BLUETOOTH;
        sl2.l2_psm = 0;
@@ -720,7 +699,6 @@ SR_PRIV int sr_bt_connect_ble(struct sr_bt_desc *desc)
                        .level = BT_SECURITY_LOW,
                        .key_size = 0,
                };
-               sr_spew("security");
                ret = setsockopt(s, SOL_BLUETOOTH, BT_SECURITY, &buf, sizeof(buf));
                if (ret < 0) {
                        perror("setsockopt");
@@ -728,7 +706,8 @@ SR_PRIV int sr_bt_connect_ble(struct sr_bt_desc *desc)
                }
        }
 
-       sr_spew("connect()");
+       deadline = g_get_monotonic_time();
+       deadline += CONNECT_BLE_TIMEOUT * 1000 * 1000;
        str2ba(desc->remote_addr, &mac);
        memcpy(&sl2.l2_bdaddr, &mac, sizeof(sl2.l2_bdaddr));
        sl2.l2_bdaddr_type = BDADDR_LE_PUBLIC;
@@ -752,7 +731,6 @@ SR_PRIV int sr_bt_connect_ble(struct sr_bt_desc *desc)
                sr_spew("in progress ...");
 
                do {
-                       sr_spew("poll(OUT)");
                        memset(fds, 0, sizeof(fds));
                        fds[0].fd = s;
                        fds[0].events = POLLOUT;
@@ -765,8 +743,11 @@ SR_PRIV int sr_bt_connect_ble(struct sr_bt_desc *desc)
                                continue;
                        if (!(fds[0].revents & POLLOUT))
                                continue;
+                       if (g_get_monotonic_time() >= deadline) {
+                               sr_warn("Connect attempt timed out");
+                               return SR_ERR_IO;
+                       }
                } while (1);
-               sr_spew("poll(INVAL)");
                memset(fds, 0, sizeof(fds));
                fds[0].fd = s;
                fds[0].events = POLLNVAL;
@@ -781,7 +762,6 @@ SR_PRIV int sr_bt_connect_ble(struct sr_bt_desc *desc)
                        close(s);
                        return -1;
                }
-               sr_spew("getsocktop(SO_ERROR)");
                solen = sizeof(soerror);
                ret = getsockopt(s, SOL_SOCKET, SO_ERROR, &soerror, &solen);
                if (ret < 0) {
@@ -800,7 +780,6 @@ SR_PRIV int sr_bt_connect_ble(struct sr_bt_desc *desc)
                 * getsockopt(SOL_BLUETOOTH, BT_RCVMTU, u16);
                 */
        }
-       sr_spew("connect() => rc %d, fd %d", ret, desc->fd);
        if (ret < 0) {
                perror("connect");
                return ret;
@@ -812,7 +791,7 @@ SR_PRIV int sr_bt_connect_ble(struct sr_bt_desc *desc)
 SR_PRIV int sr_bt_connect_rfcomm(struct sr_bt_desc *desc)
 {
        struct sockaddr_rc addr;
-       int fd, rc;
+       int i, fd, rc;
 
        if (!desc)
                return -1;
@@ -824,27 +803,40 @@ SR_PRIV int sr_bt_connect_rfcomm(struct sr_bt_desc *desc)
        if (!desc->rfcomm_channel)
                desc->rfcomm_channel = 1;
 
-       sr_spew("socket()");
-       fd = socket(AF_BLUETOOTH, SOCK_STREAM, BTPROTO_RFCOMM);
-       if (fd < 0) {
-               perror("socket");
-               return -1;
-       }
-       desc->fd = fd;
-
-       sr_spew("connect()");
        memset(&addr, 0, sizeof(addr));
        addr.rc_family = AF_BLUETOOTH;
        str2ba(desc->remote_addr, &addr.rc_bdaddr);
        addr.rc_channel = desc->rfcomm_channel;
-       rc = connect(fd, (struct sockaddr *)&addr, sizeof(addr));
-       if (rc < 0) {
-               perror("connect");
-               return -2;
+
+       /*
+        * There are cases where connect returns EBUSY if we are re-connecting
+        * to a device. Try multiple times to work around this issue.
+        */
+       for (i = 0; i < CONNECT_RFCOMM_TRIES; i++) {
+               fd = socket(AF_BLUETOOTH, SOCK_STREAM, BTPROTO_RFCOMM);
+               if (fd < 0) {
+                       perror("socket");
+                       return -1;
+               }
+
+               rc = connect(fd, (struct sockaddr *)&addr, sizeof(addr));
+               if (rc >= 0) {
+                       sr_spew("connected");
+                       desc->fd = fd;
+                       return 0;
+               } else if (rc < 0 && errno == EBUSY) {
+                       close(fd);
+                       g_usleep(CONNECT_RFCOMM_RETRY_MS * 1000);
+               } else {
+                       close(fd);
+                       perror("connect");
+                       return -2;
+               }
        }
-       sr_spew("connected");
 
-       return 0;
+       sr_err("Connect failed, device busy.");
+
+       return -2;
 }
 
 SR_PRIV void sr_bt_disconnect(struct sr_bt_desc *desc)
@@ -861,7 +853,6 @@ static int sr_bt_check_socket_usable(struct sr_bt_desc *desc)
        struct pollfd fds[1];
        int ret;
 
-       sr_spew("socket usability check");
        if (!desc)
                return -1;
        if (desc->fd < 0)
@@ -900,8 +891,7 @@ SR_PRIV int sr_bt_start_notify(struct sr_bt_desc *desc)
        if (sr_bt_check_socket_usable(desc) < 0)
                return -2;
 
-       sr_spew("write()");
-       bt_put_le16(desc->cccd_value, buf);
+       write_u16le(buf, desc->cccd_value);
        wrlen = sr_bt_char_write_req(desc, desc->cccd_handle, buf, sizeof(buf));
        if (wrlen != sizeof(buf))
                return -2;
@@ -913,72 +903,111 @@ SR_PRIV int sr_bt_check_notify(struct sr_bt_desc *desc)
 {
        uint8_t buf[1024];
        ssize_t rdlen;
+       const uint8_t *bufptr;
+       size_t buflen;
        uint8_t packet_type;
        uint16_t packet_handle;
        uint8_t *packet_data;
        size_t packet_dlen;
+       const char *type_text;
+       int ret;
 
-       sr_dbg("BLE check notify");
        if (!desc)
                return -1;
 
        if (sr_bt_check_socket_usable(desc) < 0)
                return -2;
 
-       /* Get another message from the Bluetooth socket. */
-       sr_spew("read() non-blocking");
+       /*
+        * Get another message from the Bluetooth socket.
+        *
+        * TODO Can we assume that every "message" comes in a separate
+        * read(2) call, or can data combine at the caller's? Need we
+        * loop over the received content until all was consumed?
+        */
        rdlen = sr_bt_read(desc, buf, sizeof(buf));
-       sr_spew("read() => %zd", rdlen);
-       if (rdlen < 0)
+       if (rdlen < 0) {
+               sr_dbg("check notifiy, read error, %zd", rdlen);
                return -2;
-       if (!rdlen)
+       }
+       if (!rdlen) {
+               if (0) sr_spew("check notifiy, empty read");
                return 0;
-       sr_spew("read() len %zd, type 0x%02x", rdlen, buf[0]);
+       }
+       bufptr = &buf[0];
+       buflen = (size_t)rdlen;
+       if (sr_log_loglevel_get() >= SR_LOG_SPEW) {
+               GString *txt;
+               txt = sr_hexdump_new(bufptr, buflen);
+               sr_spew("check notifiy, read succes, length %zd, data: %s",
+                       rdlen, txt->str);
+               sr_hexdump_free(txt);
+       }
 
-       /* Get header fields and references to the payload data. */
-       packet_type = 0x00;
+       /*
+        * Get header fields and references to the payload data. Notice
+        * that the first 16bit item after the packet type often is the
+        * handle, but need not always be. That is why the read position
+        * is kept, so that individual packet type handlers can either
+        * read _their_ layout strictly sequentially, or can conveniently
+        * access what a common preparation step has provided to them.
+        */
        packet_handle = 0x0000;
        packet_data = NULL;
        packet_dlen = 0;
-       if (rdlen >= 1)
-               packet_type = buf[0];
-       if (rdlen >= 3) {
-               packet_handle = bt_get_le16(&buf[1]);
-               packet_data = &buf[3];
-               packet_dlen = rdlen - 3;
+       packet_type = read_u8_inc_len(&bufptr, &buflen);
+       if (buflen >= sizeof(uint16_t)) {
+               packet_handle = read_u16le(bufptr);
+               packet_data = (void *)&bufptr[sizeof(uint16_t)];
+               packet_dlen = buflen - sizeof(uint16_t);
+               if (!packet_dlen)
+                       packet_data = NULL;
        }
+       if (0) sr_spew("check notifiy, prep, hdl %" PRIu16 ", data %p len %zu",
+               packet_handle, packet_data, packet_dlen);
 
        /* Dispatch according to the message type. */
        switch (packet_type) {
        case BLE_ATT_ERROR_RESP:
-               sr_spew("error response");
+               type_text = "error response";
+               if (!buflen) {
+                       sr_dbg("%s, no payload", type_text);
+                       break;
+               }
                /* EMPTY */
+               sr_dbg("%s, not handled here", type_text);
                break;
        case BLE_ATT_WRITE_RESP:
-               sr_spew("write response");
-               /* EMPTY */
+               type_text = "write response";
+               sr_dbg("%s, note taken", type_text);
                break;
        case BLE_ATT_HANDLE_INDICATION:
-               sr_spew("handle indication");
+               type_text = "handle indication";
+               sr_dbg("%s, data len %zu", type_text, packet_dlen);
                sr_bt_write_type(desc, BLE_ATT_HANDLE_CONFIRMATION);
+               sr_spew("%s, confirmation sent", type_text);
                if (packet_handle != desc->read_handle)
                        return -4;
-               if (!packet_data)
-                       return -4;
                if (!desc->data_cb)
                        return 0;
-               return desc->data_cb(desc->data_cb_data, packet_data, packet_dlen);
+               ret = desc->data_cb(desc->data_cb_data,
+                       packet_data, packet_dlen);
+               sr_spew("%s, data cb ret %d", type_text, ret);
+               return ret;
        case BLE_ATT_HANDLE_NOTIFICATION:
-               sr_spew("handle notification");
+               type_text = "handle notification";
+               sr_dbg("%s, data len %zu", type_text, packet_dlen);
                if (packet_handle != desc->read_handle)
                        return -4;
-               if (!packet_data)
-                       return -4;
                if (!desc->data_cb)
                        return 0;
-               return desc->data_cb(desc->data_cb_data, packet_data, packet_dlen);
+               ret = desc->data_cb(desc->data_cb_data,
+                       packet_data, packet_dlen);
+               sr_spew("%s, data cb ret %d", type_text, ret);
+               return ret;
        default:
-               sr_spew("unsupported type 0x%02x", packet_type);
+               sr_dbg("unhandled type 0x%02x, len %zu",
+                       packet_type, buflen);
                return -3;
        }
 
@@ -991,7 +1020,6 @@ SR_PRIV int sr_bt_check_notify(struct sr_bt_desc *desc)
 SR_PRIV ssize_t sr_bt_write(struct sr_bt_desc *desc,
        const void *data, size_t len)
 {
-       sr_dbg("BLE write (raw)");
        if (!desc)
                return -1;
        if (desc->fd < 0)
@@ -1012,7 +1040,6 @@ static ssize_t sr_bt_write_type(struct sr_bt_desc *desc, uint8_t type)
 {
        ssize_t wrlen;
 
-       sr_dbg("BLE write (type)");
        if (!desc)
                return -1;
        if (desc->fd < 0)
@@ -1034,7 +1061,6 @@ static ssize_t sr_bt_write_type(struct sr_bt_desc *desc, uint8_t type)
 static ssize_t sr_bt_write_type_handle(struct sr_bt_desc *desc,
        uint8_t type, uint16_t handle)
 {
-       sr_dbg("BLE write (type, handle)");
        return sr_bt_write_type_handle_bytes(desc, type, handle, NULL, 0);
 }
 #endif
@@ -1049,7 +1075,6 @@ static ssize_t sr_bt_write_type_handle_bytes(struct sr_bt_desc *desc,
        };
        ssize_t wrlen;
 
-       sr_dbg("BLE write (type, handle, data)");
        if (!desc)
                return -1;
        if (desc->fd < 0)
@@ -1059,7 +1084,7 @@ static ssize_t sr_bt_write_type_handle_bytes(struct sr_bt_desc *desc,
                return -2;
 
        header[0] = type;
-       bt_put_le16(handle, &header[1]);
+       write_u16le(&header[1], handle);
 
        if (data && len)
                wrlen = writev(desc->fd, iov, ARRAY_SIZE(iov));
@@ -1079,8 +1104,6 @@ static ssize_t sr_bt_write_type_handle_bytes(struct sr_bt_desc *desc,
 static ssize_t sr_bt_char_write_req(struct sr_bt_desc *desc,
        uint16_t handle, const void *data, size_t len)
 {
-       sr_dbg("BLE write-char req");
-
        return sr_bt_write_type_handle_bytes(desc, BLE_ATT_WRITE_REQ,
                handle, data, len);
 }
@@ -1091,7 +1114,6 @@ SR_PRIV ssize_t sr_bt_read(struct sr_bt_desc *desc, void *data, size_t len)
        int ret;
        ssize_t rdlen;
 
-       sr_dbg("BLE read (non-blocking)");
        if (!desc)
                return -1;
        if (desc->fd < 0)
@@ -1100,12 +1122,10 @@ SR_PRIV ssize_t sr_bt_read(struct sr_bt_desc *desc, void *data, size_t len)
        if (sr_bt_check_socket_usable(desc) < 0)
                return -2;
 
-       sr_spew("poll(POLLIN)");
        memset(fds, 0, sizeof(fds));
        fds[0].fd = desc->fd;
        fds[0].events = POLLIN;
        ret = poll(fds, ARRAY_SIZE(fds), 0);
-       sr_spew("poll(%d, POLLIN) => 0x%x", desc->fd, fds[0].revents);
        if (ret < 0)
                return ret;
        if (!ret)
@@ -1113,9 +1133,7 @@ SR_PRIV ssize_t sr_bt_read(struct sr_bt_desc *desc, void *data, size_t len)
        if (!(fds[0].revents & POLLIN))
                return 0;
 
-       sr_spew("read()");
        rdlen = read(desc->fd, data, len);
-       sr_spew("read() => %zd", rdlen);
 
        return rdlen;
 }