]> sigrok.org Git - libsigrok.git/commitdiff
serial_bt, bluez: support MTU exchange in BLE reception
authorGerhard Sittig <redacted>
Sun, 19 Mar 2023 15:21:06 +0000 (16:21 +0100)
committerGerhard Sittig <redacted>
Sun, 19 Mar 2023 21:40:08 +0000 (22:40 +0100)
The central must respond to peripherals' requests to exchange the MTU
value. If it doesn't then the connection terminates. The central has no
choice whether this exchange happens or when it does, applicability is
the consequence of using a specific device (a firmware implementation).
This was first observed with RDTech TC66C energy meters.

Add support for the mtu= parameter in conn=bt/ specifications. A good
default value is yet to get determined. The connection type or the chip
in use are insufficient conditions AFAICT. Currently it is assumed that
users specify the central's MTU when their device firmware requires the
exchange.

Use the "warning" severity for the diagnostics message when peripherals
request MTU exchange while the central won't respond (due to lack of a
value to respond with).

README.devices
src/bt/bt_bluez.c
src/hardware/mooshimeter-dmm/api.c
src/libsigrok-internal.h
src/serial_bt.c

index fe4d6c8b55eae80407a75c1bb03169096c2a6ad0..7563a57c5e1160a140068e30c3f05c8c234566e5 100644 (file)
@@ -210,7 +210,7 @@ Formal syntax for serial communication:
      <name>:conn=<spec>" example, that is why the dense form and the use
      of dashes for separation are supported)
    additional parameter keywords can be: channel, handle_rx, handle_tx,
-     handle_cccd, value_cccd
+     handle_cccd, value_cccd, mtu
 
 Some of the drivers implement a default for the connection. Some of the
 drivers can auto-detect USB connected devices.
index 71c0dbd4e5b902fbe2126344236c676e816f9548..dc3a2636597dde2a525a1d094e618d255826e42e 100644 (file)
@@ -97,9 +97,6 @@
 #define CONNECT_RFCOMM_TRIES   3
 #define CONNECT_RFCOMM_RETRY_MS        100
 
-/* Silence warning about (currently) unused routine. */
-#define WITH_WRITE_TYPE_HANDLE 0
-
 /* {{{ compat decls */
 /*
  * The availability of conversion helpers in <bluetooth/bluetooth.h>
@@ -240,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;
@@ -250,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,
@@ -366,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)
@@ -376,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;
 }
@@ -911,6 +909,7 @@ SR_PRIV int sr_bt_check_notify(struct sr_bt_desc *desc)
        size_t packet_dlen;
        const char *type_text;
        int ret;
+       uint16_t mtu;
 
        if (!desc)
                return -1;
@@ -968,6 +967,23 @@ SR_PRIV int sr_bt_check_notify(struct sr_bt_desc *desc)
 
        /* 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:
                type_text = "error response";
                if (!buflen) {
@@ -1057,13 +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)
 {
        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)
index b1b940683038400e6e77b6de94e3b2628e02f310..6e0b01847217790298a061da498cb042ddb7b453 100644 (file)
@@ -111,7 +111,7 @@ static GSList *scan(struct sr_dev_driver *di, GSList *options)
          *         descr - handle: 0x0016, uuid: 00002902-0000-1000-8000-00805f9b34fb
          *         descr - handle: 0x0017, uuid: 00002901-0000-1000-8000-00805f9b34fb
         */
-       ret = sr_bt_config_notify(desc, 0x0015, 0x0012, 0x0016, 0x0001);
+       ret = sr_bt_config_notify(desc, 0x0015, 0x0012, 0x0016, 0x0001, 0);
        if (ret < 0)
                goto err;
 
index 06c227bb5b4ceb2d150b8cd06ffc2db4364f7cda..04a08ca83d2247eb0e98b8f8919ecb7ba5e0f75e 100644 (file)
@@ -1630,6 +1630,7 @@ struct sr_serial_dev_inst {
        uint16_t bt_notify_handle_write;
        uint16_t bt_notify_handle_cccd;
        uint16_t bt_notify_value_cccd;
+       uint16_t bt_ble_mtu;
        struct sr_bt_desc *bt_desc;
        GSList *bt_source_args;
 #endif
@@ -2145,7 +2146,8 @@ SR_PRIV int sr_bt_config_addr_remote(struct sr_bt_desc *desc, const char *addr);
 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);
 
 SR_PRIV int sr_bt_scan_le(struct sr_bt_desc *desc, int duration);
 SR_PRIV int sr_bt_scan_bt(struct sr_bt_desc *desc, int duration);
index 33abec0ff481715993920aed1fc57a59433c44a7..67b4e6a5f45808b40e49e9cd64ff58ee0b1c5cd5 100644 (file)
@@ -37,6 +37,7 @@
 #define SER_BT_PARAM_PREFIX_HDL_TX     "handle_tx="
 #define SER_BT_PARAM_PREFIX_HDL_CCCD   "handle_cccd="
 #define SER_BT_PARAM_PREFIX_VAL_CCCD   "value_cccd="
+#define SER_BT_PARAM_PREFIX_BLE_MTU    "mtu="
 
 /**
  * @file
@@ -190,7 +191,8 @@ static int ser_bt_parse_conn_spec(
        enum ser_bt_conn_t *conn_type, const char **remote_addr,
        size_t *rfcomm_channel,
        uint16_t *read_hdl, uint16_t *write_hdl,
-       uint16_t *cccd_hdl, uint16_t *cccd_val)
+       uint16_t *cccd_hdl, uint16_t *cccd_val,
+       uint16_t *ble_mtu)
 {
        char **fields, *field;
        enum ser_bt_conn_t type;
@@ -214,6 +216,8 @@ static int ser_bt_parse_conn_spec(
                *cccd_hdl = 0;
        if (cccd_val)
                *cccd_val = 0;
+       if (ble_mtu)
+               *ble_mtu = 0;
 
        if (!serial || !spec || !spec[0])
                return SR_ERR_ARG;
@@ -377,6 +381,18 @@ static int ser_bt_parse_conn_spec(
                                *cccd_val = parm_val;
                        continue;
                }
+               if (g_str_has_prefix(field, SER_BT_PARAM_PREFIX_BLE_MTU)) {
+                       field += strlen(SER_BT_PARAM_PREFIX_BLE_MTU);
+                       endp = NULL;
+                       ret = sr_atoul_base(field, &parm_val, &endp, 0);
+                       if (ret != SR_OK || !endp || *endp != '\0') {
+                               ret_parse = SR_ERR_ARG;
+                               break;
+                       }
+                       if (ble_mtu)
+                               *ble_mtu = parm_val;
+                       continue;
+               }
                return SR_ERR_DATA;
        }
 
@@ -451,6 +467,7 @@ static int ser_bt_open(struct sr_serial_dev_inst *serial, int flags)
        const char *remote_addr;
        size_t rfcomm_channel;
        uint16_t read_hdl, write_hdl, cccd_hdl, cccd_val;
+       uint16_t ble_mtu;
        int rc;
        struct sr_bt_desc *desc;
 
@@ -461,7 +478,8 @@ static int ser_bt_open(struct sr_serial_dev_inst *serial, int flags)
                        &conn_type, &remote_addr,
                        &rfcomm_channel,
                        &read_hdl, &write_hdl,
-                       &cccd_hdl, &cccd_val);
+                       &cccd_hdl, &cccd_val,
+                       &ble_mtu);
        if (rc != SR_OK)
                return SR_ERR_ARG;
 
@@ -491,13 +509,15 @@ static int ser_bt_open(struct sr_serial_dev_inst *serial, int flags)
        case SER_BT_CONN_CC254x:
        case SER_BT_CONN_AC6328:
                rc = sr_bt_config_notify(desc,
-                       read_hdl, write_hdl, cccd_hdl, cccd_val);
+                       read_hdl, write_hdl, cccd_hdl, cccd_val,
+                       ble_mtu);
                if (rc < 0)
                        return SR_ERR;
                serial->bt_notify_handle_read = read_hdl;
                serial->bt_notify_handle_write = write_hdl;
                serial->bt_notify_handle_cccd = cccd_hdl;
                serial->bt_notify_value_cccd = cccd_val;
+               serial->bt_ble_mtu = ble_mtu;
                break;
        default:
                /* Unsupported type, or incomplete implementation. */