]> sigrok.org Git - libsigrok.git/blobdiff - src/bt/bt_bluez.c
output/csv: use intermediate time_t var, silence compiler warning
[libsigrok.git] / src / bt / bt_bluez.c
index 644642bae132a521f23225aa457f4bb87a12fbdb..dc3a2636597dde2a525a1d094e618d255826e42e 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
 
-/* Silence warning about (currently) unused routine. */
-#define WITH_WRITE_TYPE_HANDLE 0
+#define CONNECT_RFCOMM_TRIES   3
+#define CONNECT_RFCOMM_RETRY_MS        100
 
 /* {{{ compat decls */
 /*
  * 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 +209,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;
 
@@ -244,6 +237,7 @@ struct sr_bt_desc {
        uint16_t write_handle;
        uint16_t cccd_handle;
        uint16_t cccd_value;
+       uint16_t ble_mtu;
        /* Internal state. */
        int devid;
        int fd;
@@ -254,10 +248,8 @@ static int sr_bt_desc_open(struct sr_bt_desc *desc, int *id_ref);
 static void sr_bt_desc_close(struct sr_bt_desc *desc);
 static int sr_bt_check_socket_usable(struct sr_bt_desc *desc);
 static ssize_t sr_bt_write_type(struct sr_bt_desc *desc, uint8_t type);
-#if WITH_WRITE_TYPE_HANDLE
 static ssize_t sr_bt_write_type_handle(struct sr_bt_desc *desc,
        uint8_t type, uint16_t handle);
-#endif
 static ssize_t sr_bt_write_type_handle_bytes(struct sr_bt_desc *desc,
        uint8_t type, uint16_t handle, const uint8_t *data, size_t len);
 static ssize_t sr_bt_char_write_req(struct sr_bt_desc *desc,
@@ -370,7 +362,8 @@ SR_PRIV int sr_bt_config_rfcomm(struct sr_bt_desc *desc, size_t channel)
 
 SR_PRIV int sr_bt_config_notify(struct sr_bt_desc *desc,
        uint16_t read_handle, uint16_t write_handle,
-       uint16_t cccd_handle, uint16_t cccd_value)
+       uint16_t cccd_handle, uint16_t cccd_value,
+       uint16_t ble_mtu)
 {
 
        if (!desc)
@@ -380,6 +373,7 @@ SR_PRIV int sr_bt_config_notify(struct sr_bt_desc *desc,
        desc->write_handle = write_handle;
        desc->cccd_handle = cccd_handle;
        desc->cccd_value = cccd_value;
+       desc->ble_mtu = ble_mtu;
 
        return 0;
 }
@@ -393,7 +387,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 +396,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 +443,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 +458,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 +468,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 +476,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 +497,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 +506,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 +555,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 +573,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 +602,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 +661,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 +669,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 +676,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 +697,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 +704,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 +729,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 +741,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 +760,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 +778,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 +789,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 +801,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 +851,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 +889,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 +901,129 @@ 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;
+       uint16_t mtu;
 
-       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_EXCHANGE_MTU_REQ:
+               type_text = "MTU exchange request";
+               if (buflen < sizeof(uint16_t)) {
+                       sr_dbg("%s, invalid (size)", type_text);
+                       break;
+               }
+               mtu = read_u16le_inc_len(&bufptr, &buflen);
+               sr_dbg("%s, peripheral value %" PRIu16, type_text, mtu);
+               if (desc->ble_mtu) {
+                       mtu = desc->ble_mtu;
+                       sr_dbg("%s, central value %" PRIu16, type_text, mtu);
+                       sr_bt_write_type_handle(desc,
+                               BLE_ATT_EXCHANGE_MTU_RESP, mtu);
+                       break;
+               }
+               sr_warn("Unhandled BLE %s.", type_text);
+               break;
        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 +1036,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 +1056,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)
@@ -1030,14 +1073,11 @@ static ssize_t sr_bt_write_type(struct sr_bt_desc *desc, uint8_t type)
        return 0;
 }
 
-#if WITH_WRITE_TYPE_HANDLE
 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
 
 static ssize_t sr_bt_write_type_handle_bytes(struct sr_bt_desc *desc,
        uint8_t type, uint16_t handle, const uint8_t *data, size_t len)
@@ -1049,7 +1089,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 +1098,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 +1118,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 +1128,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 +1136,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 +1147,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;
 }