]> sigrok.org Git - libsigrok.git/blobdiff - src/serial_bt.c
rdtech-tc: migrate to feed queue helper
[libsigrok.git] / src / serial_bt.c
index 8ed4523b8167ee1f42686d1ff9309f3b0f9c8c8f..23b4dd6ce159aac263071ad3db6ce65689e5b52d 100644 (file)
@@ -24,9 +24,7 @@
 #include <string.h>
 #include <memory.h>
 
-/** @cond PRIVATE */
 #define LOG_PREFIX "serial-bt"
-/** @endcond */
 
 #ifdef HAVE_SERIAL_COMM
 #ifdef HAVE_BLUETOOTH
 #define SER_BT_CONN_PREFIX     "bt"
 #define SER_BT_CHUNK_SIZE      1200
 
+#define SER_BT_PARAM_PREFIX_CHANNEL    "channel="
+#define SER_BT_PARAM_PREFIX_HDL_RX     "handle_rx="
+#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="
+
 /**
  * @file
  *
 
 /* {{{ support for serial-over-BT channels */
 
+/*
+ * This builtin database of known devices (keyed by their names as
+ * provided during BT/BLE scans) can help improve the presentation of
+ * scan results. Ideally users could take the output and pass it to
+ * subsequent program invocations, not having to "come up with" the
+ * conn= spec, or only having to touch it up minimally. GUI dialogs
+ * could present scan results such that users just need to pick an
+ * item to open a connection.
+ *
+ * The current implementation guesses connection types from device
+ * names, and optionally amends them with additional parameters if
+ * experience shows that individual devices need these extra specs.
+ *
+ * This database may have to move to a separate source file should
+ * its size grow to amounts that are considered inappropriate here
+ * in the serial transport's BT dispatcher. For now the item count
+ * is small.
+ */
+
 static const struct scan_supported_item {
        const char *name;
        enum ser_bt_conn_t type;
+       const char *add_params;
 } scan_supported_items[] = {
-       /* Guess connection types from device names (useful for scans). */
-       { "121GW", SER_BT_CONN_BLE122, },
-       { "Adafruit Bluefruit LE 8134", SER_BT_CONN_NRF51, },
-       { "HC-05", SER_BT_CONN_RFCOMM, },
-       { NULL, SER_BT_CONN_UNKNOWN, },
+       { "121GW", SER_BT_CONN_BLE122, NULL, },
+       { "Adafruit Bluefruit LE 8134", SER_BT_CONN_NRF51, NULL, },
+       { "HC-05", SER_BT_CONN_RFCOMM, NULL, },
+       { "UM25C", SER_BT_CONN_RFCOMM, NULL, },
+       { NULL, SER_BT_CONN_UNKNOWN, NULL, },
 };
 
+static const struct scan_supported_item *scan_is_supported(const char *name)
+{
+       size_t idx;
+       const struct scan_supported_item *item;
+
+       for (idx = 0; idx < ARRAY_SIZE(scan_supported_items); idx++) {
+               item = &scan_supported_items[idx];
+               if (!item->name)
+                       break;
+               if (strcmp(name, item->name) != 0)
+                       continue;
+               return item;
+       }
+
+       return NULL;
+}
+
 static const char *ser_bt_conn_names[SER_BT_CONN_MAX] = {
        [SER_BT_CONN_UNKNOWN] = "<type>",
        [SER_BT_CONN_RFCOMM] = "rfcomm",
@@ -109,8 +150,6 @@ static const char *conn_name_text(enum ser_bt_conn_t type)
  *
  * @return 0 upon success, non-zero upon failure.
  *
- * @internal
- *
  * Summary of parsing rules as they are implemented:
  * - Implementor's note: Automatic scan for available devices is not
  *   yet implemented. So strictly speaking some parts of the input
@@ -123,14 +162,16 @@ static const char *conn_name_text(enum ser_bt_conn_t type)
  * - The next field is the remote device's address, either separated
  *   by colons or dashes or spaces, or not separated at all.
  * - Other parameters (RFCOMM channel, notify handles and write values)
- *   get derived from the connection type. A future implementation may
- *   accept more fields, but the syntax is yet to get developed.
+ *   get derived from the connection type.
+ * - More fields after the remote address are options which override
+ *   builtin defaults (RFCOMM channels, BLE handles, etc).
  *
  * Supported formats resulting from these rules:
- *   bt/<conn>/<addr>
+ *   bt/<conn>/<addr>[/<param>]...
  *
  * Examples:
  *   bt/rfcomm/11-22-33-44-55-66
+ *   bt/rfcomm/11-22-33-44-55-66/channel=2
  *   bt/ble122/88:6b:12:34:56:78
  *   bt/cc254x/0123456789ab
  *
@@ -146,9 +187,13 @@ static int ser_bt_parse_conn_spec(
        uint16_t *read_hdl, uint16_t *write_hdl,
        uint16_t *cccd_hdl, uint16_t *cccd_val)
 {
+       char **fields, *field;
        enum ser_bt_conn_t type;
        const char *addr;
-       char **fields, *field;
+       int ret_parse, ret;
+       size_t fields_count, field_idx;
+       char *endp;
+       unsigned long parm_val;
 
        if (conn_type)
                *conn_type = SER_BT_CONN_UNKNOWN;
@@ -165,9 +210,6 @@ static int ser_bt_parse_conn_spec(
        if (cccd_val)
                *cccd_val = 0;
 
-       type = SER_BT_CONN_UNKNOWN;
-       addr = NULL;
-
        if (!serial || !spec || !spec[0])
                return SR_ERR_ARG;
 
@@ -248,10 +290,83 @@ static int ser_bt_parse_conn_spec(
                return SR_ERR_ARG;
        }
 
-       /* TODO Evaluate optionally trailing fields, override defaults? */
+       /*
+        * Preset a successful return value for the conn= parse call.
+        * Scan optional additional fields which specify more params.
+        * Update the defaults which were setup above. Pessimize the
+        * routine's return value in error paths.
+        */
+       ret_parse = SR_OK;
+       fields_count = g_strv_length(fields);
+       for (field_idx = 3; field_idx < fields_count; field_idx++) {
+               field = fields[field_idx];
+               if (!field || !*field)
+                       continue;
+               if (g_str_has_prefix(field, SER_BT_PARAM_PREFIX_CHANNEL)) {
+                       field += strlen(SER_BT_PARAM_PREFIX_CHANNEL);
+                       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 (rfcomm_channel)
+                               *rfcomm_channel = parm_val;
+                       continue;
+               }
+               if (g_str_has_prefix(field, SER_BT_PARAM_PREFIX_HDL_RX)) {
+                       field += strlen(SER_BT_PARAM_PREFIX_HDL_RX);
+                       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 (read_hdl)
+                               *read_hdl = parm_val;
+                       continue;
+               }
+               if (g_str_has_prefix(field, SER_BT_PARAM_PREFIX_HDL_TX)) {
+                       field += strlen(SER_BT_PARAM_PREFIX_HDL_TX);
+                       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 (write_hdl)
+                               *write_hdl = parm_val;
+                       continue;
+               }
+               if (g_str_has_prefix(field, SER_BT_PARAM_PREFIX_HDL_CCCD)) {
+                       field += strlen(SER_BT_PARAM_PREFIX_HDL_CCCD);
+                       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 (cccd_hdl)
+                               *cccd_hdl = parm_val;
+                       continue;
+               }
+               if (g_str_has_prefix(field, SER_BT_PARAM_PREFIX_VAL_CCCD)) {
+                       field += strlen(SER_BT_PARAM_PREFIX_VAL_CCCD);
+                       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 (cccd_val)
+                               *cccd_val = parm_val;
+                       continue;
+               }
+               return SR_ERR_DATA;
+       }
 
        g_strfreev(fields);
-       return SR_OK;
+       return ret_parse;
 }
 
 static void ser_bt_mask_databits(struct sr_serial_dev_inst *serial,
@@ -502,7 +617,6 @@ static int ser_bt_read(struct sr_serial_dev_inst *serial,
         * where to stop reception.
         */
        deadline_us = 0;
-       now_us = 0;     /* Silence a (false) compiler warning. */
        if (timeout_ms) {
                now_us = g_get_monotonic_time();
                deadline_us = now_us + timeout_ms * 1000;
@@ -574,28 +688,6 @@ static int ser_bt_read(struct sr_serial_dev_inst *serial,
        return sr_ser_unqueue_rx_data(serial, buf, dlen);
 }
 
-static int ser_bt_set_params(struct sr_serial_dev_inst *serial,
-               int baudrate, int bits, int parity, int stopbits,
-               int flowcontrol, int rts, int dtr)
-{
-       /*
-        * Bluetooth communication has no concept of bitrate, so ignore
-        * these arguments silently. Neither need we pass the frame format
-        * down to internal BT comm routines, nor need we keep the values
-        * here, since the caller will cache/register them already.
-        */
-       (void)serial;
-       (void)baudrate;
-       (void)bits;
-       (void)parity;
-       (void)stopbits;
-       (void)flowcontrol;
-       (void)rts;
-       (void)dtr;
-
-       return SR_OK;
-}
-
 struct bt_source_args_t {
        /* The application callback. */
        sr_receive_data_callback cb;
@@ -724,23 +816,6 @@ static int ser_bt_setup_source_remove(struct sr_session *session,
        return SR_OK;
 }
 
-static enum ser_bt_conn_t scan_is_supported(const char *name)
-{
-       size_t idx;
-       const struct scan_supported_item *item;
-
-       for (idx = 0; idx < ARRAY_SIZE(scan_supported_items); idx++) {
-               item = &scan_supported_items[idx];
-               if (!item->name)
-                       break;
-               if (strcmp(name, item->name) != 0)
-                       continue;
-               return item->type;
-       }
-
-       return SER_BT_CONN_UNKNOWN;
-}
-
 struct bt_scan_args_t {
        GSList *port_list;
        sr_ser_list_append_t append;
@@ -753,6 +828,7 @@ static void scan_cb(void *cb_args, const char *addr, const char *name)
        struct bt_scan_args_t *scan_args;
        GSList *l;
        char addr_text[20];
+       const struct scan_supported_item *item;
        enum ser_bt_conn_t type;
        char *port_name, *port_desc;
        char *addr_copy;
@@ -775,9 +851,11 @@ static void scan_cb(void *cb_args, const char *addr, const char *name)
        g_strcanon(addr_text, "0123456789abcdefABCDEF", '-');
 
        /* Create a port name, and a description. */
-       type = scan_is_supported(name);
-       port_name = g_strdup_printf("%s/%s/%s",
-               SER_BT_CONN_PREFIX, conn_name_text(type), addr_text);
+       item = scan_is_supported(name);
+       type = item ? item->type : SER_BT_CONN_UNKNOWN;
+       port_name = g_strdup_printf("%s/%s/%s%s",
+               SER_BT_CONN_PREFIX, conn_name_text(type), addr_text,
+               (item && item->add_params) ? item->add_params : "");
        port_desc = g_strdup_printf("%s (%s)", name, scan_args->bt_type);
 
        scan_args->port_list = scan_args->append(scan_args->port_list, port_name, port_desc);
@@ -791,7 +869,7 @@ static void scan_cb(void *cb_args, const char *addr, const char *name)
 
 static GSList *ser_bt_list(GSList *list, sr_ser_list_append_t append)
 {
-       static const int scan_duration = 2;
+       static const int scan_duration = 3;
 
        struct bt_scan_args_t scan_args;
        struct sr_bt_desc *desc;
@@ -835,7 +913,14 @@ static struct ser_lib_functions serlib_bt = {
        .drain = ser_bt_drain,
        .write = ser_bt_write,
        .read = ser_bt_read,
-       .set_params = ser_bt_set_params,
+       /*
+        * Bluetooth communication has no concept of bitrate, so ignore
+        * these arguments silently. Neither need we pass the frame format
+        * down to internal BT comm routines, nor need we keep the values
+        * here, since the caller will cache/register them already.
+        */
+       .set_params = std_dummy_set_params,
+       .set_handshake = std_dummy_set_handshake,
        .setup_source_add = ser_bt_setup_source_add,
        .setup_source_remove = ser_bt_setup_source_remove,
        .list = ser_bt_list,