#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
*
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 char *ser_bt_conn_names[SER_BT_CONN_MAX] = {
* - 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
*
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;
if (cccd_val)
*cccd_val = 0;
- type = SER_BT_CONN_UNKNOWN;
- addr = NULL;
-
if (!serial || !spec || !spec[0])
return SR_ERR_ARG;
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,
* 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;
return SR_OK;
}
-static enum ser_bt_conn_t scan_is_supported(const char *name)
+static const struct scan_supported_item *scan_is_supported(const char *name)
{
size_t idx;
const struct scan_supported_item *item;
break;
if (strcmp(name, item->name) != 0)
continue;
- return item->type;
+ return item;
}
- return SER_BT_CONN_UNKNOWN;
+ return NULL;
}
struct bt_scan_args_t {
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;
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);
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;