]> sigrok.org Git - libsigrok.git/blobdiff - src/serial_bt.c
rdtech-tc: migrate to feed queue helper
[libsigrok.git] / src / serial_bt.c
index 42d6289a51126b9f10db2d55e978add48404eaa4..23b4dd6ce159aac263071ad3db6ce65689e5b52d 100644 (file)
 #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",
@@ -119,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
  *
@@ -142,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;
@@ -161,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;
 
@@ -244,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,
@@ -498,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;
@@ -698,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;
@@ -727,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;
@@ -749,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);
@@ -765,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;
@@ -816,6 +920,7 @@ static struct ser_lib_functions serlib_bt = {
         * 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,