From: Luiz Augusto von Dentz <luiz.von.dentz@xxxxxxxxx> This allows the user to enable a subset of the experimental features to be enabled instead of all of them and also change -E to work in the same way so a list of UUIDs can also be given at the command line. --- src/adapter.c | 106 ++++++++++++++++++++++++++----------------------- src/btd.h | 5 ++- src/main.c | 108 ++++++++++++++++++++++++++++++++++++++++++++++---- src/main.conf | 10 ++++- 4 files changed, 169 insertions(+), 60 deletions(-) diff --git a/src/adapter.c b/src/adapter.c index dd187f847..bc6469e0a 100644 --- a/src/adapter.c +++ b/src/adapter.c @@ -102,28 +102,37 @@ static const struct mgmt_blocked_key_info blocked_keys[] = { 0x22, 0x8e, 0x07, 0x56, 0xb4, 0xe8, 0x5f, 0x01}}, }; +struct mgmt_exp_uuid { + uint8_t val[16]; + const char *str; +}; + /* d4992530-b9ec-469f-ab01-6c481c47da1c */ -static const uint8_t debug_uuid[16] = { - 0x1c, 0xda, 0x47, 0x1c, 0x48, 0x6c, 0x01, 0xab, - 0x9f, 0x46, 0xec, 0xb9, 0x30, 0x25, 0x99, 0xd4, +static const struct mgmt_exp_uuid debug_uuid = { + .val = { 0x1c, 0xda, 0x47, 0x1c, 0x48, 0x6c, 0x01, 0xab, + 0x9f, 0x46, 0xec, 0xb9, 0x30, 0x25, 0x99, 0xd4 }, + .str = "d4992530-b9ec-469f-ab01-6c481c47da1c" }; /* 671b10b5-42c0-4696-9227-eb28d1b049d6 */ -static const uint8_t le_simult_central_peripheral_uuid[16] = { - 0xd6, 0x49, 0xb0, 0xd1, 0x28, 0xeb, 0x27, 0x92, - 0x96, 0x46, 0xc0, 0x42, 0xb5, 0x10, 0x1b, 0x67, +static const struct mgmt_exp_uuid le_simult_central_peripheral_uuid = { + .val = { 0xd6, 0x49, 0xb0, 0xd1, 0x28, 0xeb, 0x27, 0x92, + 0x96, 0x46, 0xc0, 0x42, 0xb5, 0x10, 0x1b, 0x67 }, + .str = "671b10b5-42c0-4696-9227-eb28d1b049d6" }; /* 330859bc-7506-492d-9370-9a6f0614037f */ -static const uint8_t quality_report_uuid[16] = { - 0x7f, 0x03, 0x14, 0x06, 0x6f, 0x9a, 0x70, 0x93, - 0x2d, 0x49, 0x06, 0x75, 0xbc, 0x59, 0x08, 0x33, +static const struct mgmt_exp_uuid quality_report_uuid = { + .val = { 0x7f, 0x03, 0x14, 0x06, 0x6f, 0x9a, 0x70, 0x93, + 0x2d, 0x49, 0x06, 0x75, 0xbc, 0x59, 0x08, 0x33 }, + .str = "330859bc-7506-492d-9370-9a6f0614037f" }; /* 15c0a148-c273-11ea-b3de-0242ac130004 */ -static const uint8_t rpa_resolution_uuid[16] = { - 0x04, 0x00, 0x13, 0xac, 0x42, 0x02, 0xde, 0xb3, - 0xea, 0x11, 0x73, 0xc2, 0x48, 0xa1, 0xc0, 0x15, +static const struct mgmt_exp_uuid rpa_resolution_uuid = { + .val = { 0x04, 0x00, 0x13, 0xac, 0x42, 0x02, 0xde, 0xb3, + 0xea, 0x11, 0x73, 0xc2, 0x48, 0xa1, 0xc0, 0x15 }, + .str = "15c0a148-c273-11ea-b3de-0242ac130004" }; static DBusConnection *dbus_conn = NULL; @@ -3274,7 +3283,7 @@ static gboolean property_get_roles(const GDBusPropertyTable *property, } if (queue_find(adapter->exps, NULL, - le_simult_central_peripheral_uuid)) { + le_simult_central_peripheral_uuid.val)) { const char *str = "central-peripheral"; dbus_message_iter_append_basic(&entry, DBUS_TYPE_STRING, &str); } @@ -9479,23 +9488,15 @@ static void set_exp_debug_complete(uint8_t status, uint16_t len, DBG("Experimental Debug successfully set"); if (action) - queue_push_tail(adapter->exps, (void *)debug_uuid); + queue_push_tail(adapter->exps, (void *)debug_uuid.val); } -static void exp_debug_func(struct btd_adapter *adapter, uint32_t flags) +static void exp_debug_func(struct btd_adapter *adapter, uint8_t action) { struct mgmt_cp_set_exp_feature cp; - uint8_t action = btd_opts.experimental ? 0x01 : 0x00; - - /* If already set don't attempt to set it again */ - if (action == (flags & BIT(0))) { - if (action) - queue_push_tail(adapter->exps, (void *)debug_uuid); - return; - } memset(&cp, 0, sizeof(cp)); - memcpy(cp.uuid, debug_uuid, 16); + memcpy(cp.uuid, debug_uuid.val, 16); cp.action = action; if (mgmt_send(adapter->mgmt, MGMT_OP_SET_EXP_FEATURE, @@ -9507,17 +9508,17 @@ static void exp_debug_func(struct btd_adapter *adapter, uint32_t flags) } static void le_simult_central_peripheral_func(struct btd_adapter *adapter, - uint32_t flags) + uint8_t action) { - if (flags & 0x01) + if (action) queue_push_tail(adapter->exps, - (void *)le_simult_central_peripheral_uuid); + (void *)le_simult_central_peripheral_uuid.val); } -static void quality_report_func(struct btd_adapter *adapter, uint32_t flags) +static void quality_report_func(struct btd_adapter *adapter, uint8_t action) { - if (flags & 0x01) - queue_push_tail(adapter->exps, (void *)quality_report_uuid); + if (action) + queue_push_tail(adapter->exps, (void *)quality_report_uuid.val); } static void set_rpa_resolution_complete(uint8_t status, uint16_t len, @@ -9535,24 +9536,15 @@ static void set_rpa_resolution_complete(uint8_t status, uint16_t len, DBG("RPA Resolution successfully set"); if (action) - queue_push_tail(adapter->exps, (void *)rpa_resolution_uuid); + queue_push_tail(adapter->exps, (void *)rpa_resolution_uuid.val); } -static void rpa_resolution_func(struct btd_adapter *adapter, uint32_t flags) +static void rpa_resolution_func(struct btd_adapter *adapter, uint8_t action) { struct mgmt_cp_set_exp_feature cp; - uint8_t action = btd_opts.experimental ? 0x01 : 0x00; - - /* If already set don't attempt to set it again */ - if (action == (flags & BIT(0))) { - if (action) - queue_push_tail(adapter->exps, - (void *)rpa_resolution_uuid); - return; - } memset(&cp, 0, sizeof(cp)); - memcpy(cp.uuid, rpa_resolution_uuid, 16); + memcpy(cp.uuid, rpa_resolution_uuid.val, 16); cp.action = action; if (mgmt_send(adapter->mgmt, MGMT_OP_SET_EXP_FEATURE, @@ -9564,14 +9556,14 @@ static void rpa_resolution_func(struct btd_adapter *adapter, uint32_t flags) } static const struct exp_feat { - const uint8_t *uuid; - void (*func)(struct btd_adapter *adapter, uint32_t flags); + const struct mgmt_exp_uuid *uuid; + void (*func)(struct btd_adapter *adapter, uint8_t action); } exp_table[] = { - EXP_FEAT(debug_uuid, exp_debug_func), - EXP_FEAT(le_simult_central_peripheral_uuid, + EXP_FEAT(&debug_uuid, exp_debug_func), + EXP_FEAT(&le_simult_central_peripheral_uuid, le_simult_central_peripheral_func), - EXP_FEAT(quality_report_uuid, quality_report_func), - EXP_FEAT(rpa_resolution_uuid, rpa_resolution_func), + EXP_FEAT(&quality_report_uuid, quality_report_func), + EXP_FEAT(&rpa_resolution_uuid, rpa_resolution_func), }; static void read_exp_features_complete(uint8_t status, uint16_t length, @@ -9608,13 +9600,27 @@ static void read_exp_features_complete(uint8_t status, uint16_t length, for (j = 0; j < ARRAY_SIZE(exp_table); j++) { const struct exp_feat *feat = &exp_table[j]; + uint8_t action; - if (memcmp(rp->features[i].uuid, feat->uuid, + if (memcmp(rp->features[i].uuid, feat->uuid->val, sizeof(rp->features[i].uuid))) continue; + action = btd_experimental_enabled(feat->uuid->str); + + DBG("%s flags %u action %u", feat->uuid->str, + rp->features[i].flags, action); + + /* If already set don't attempt to set it again */ + if (action == (rp->features[i].flags & BIT(0))) { + if (action) + queue_push_tail(adapter->exps, + (void *)feat->uuid->val); + continue; + } + if (feat->func) - feat->func(adapter, rp->features[i].flags); + feat->func(adapter, action); } } } diff --git a/src/btd.h b/src/btd.h index d72883546..f83591f8f 100644 --- a/src/btd.h +++ b/src/btd.h @@ -10,6 +10,8 @@ * */ +#include <stdbool.h> + typedef enum { BT_MODE_DUAL, BT_MODE_BREDR, @@ -109,7 +111,7 @@ struct btd_opts { gboolean debug_keys; gboolean fast_conn; gboolean refresh_discovery; - gboolean experimental; + struct queue *experimental; uint16_t did_source; uint16_t did_vendor; @@ -140,5 +142,6 @@ void rfkill_init(void); void rfkill_exit(void); GKeyFile *btd_get_main_conf(void); +bool btd_experimental_enabled(const char *uuid); void btd_exit(void); diff --git a/src/main.c b/src/main.c index bf8b8dca7..5ca8d5644 100644 --- a/src/main.c +++ b/src/main.c @@ -42,6 +42,7 @@ #include "shared/att-types.h" #include "shared/mainloop.h" #include "shared/timeout.h" +#include "shared/queue.h" #include "lib/uuid.h" #include "shared/util.h" #include "btd.h" @@ -545,10 +546,78 @@ static void parse_le_config(GKeyFile *config) parse_mode_config(config, "LE", params, ARRAY_SIZE(params)); } +static bool match_experimental(const void *data, const void *match_data) +{ + const char *value = data; + const char *uuid = match_data; + + if (!strcmp(value, "*")) + return true; + + return !strcasecmp(value, uuid); +} + +bool btd_experimental_enabled(const char *uuid) +{ + if (!btd_opts.experimental) + false; + + return queue_find(btd_opts.experimental, match_experimental, uuid); +} + +static const char *valid_uuids[] = { + "d4992530-b9ec-469f-ab01-6c481c47da1c", + "671b10b5-42c0-4696-9227-eb28d1b049d6", + "15c0a148-c273-11ea-b3de-0242ac130004", + "330859bc-7506-492d-9370-9a6f0614037f", + "a6695ace-ee7f-4fb9-881a-5fac66c629af", + "*" +}; + +static void btd_parse_experimental(char **list) +{ + int i; + + if (btd_opts.experimental) { + warn("Unable to parse Experimental: list already set"); + return; + } + + btd_opts.experimental = queue_new(); + + for (i = 0; list[i]; i++) { + size_t j; + const char *uuid = list[i]; + + if (!strcasecmp("false", uuid) || !strcasecmp("off", uuid)) { + queue_destroy(btd_opts.experimental, free); + btd_opts.experimental = NULL; + } + + if (!strcasecmp("true", uuid) || !strcasecmp("on", uuid)) + uuid = "*"; + + for (j = 0; j < ARRAY_SIZE(valid_uuids); j++) { + if (!strcasecmp(valid_uuids[j], uuid)) + break; + } + + /* Ignored if UUID is considered invalid */ + if (j == ARRAY_SIZE(valid_uuids)) { + warn("Invalid Experimental UUID: %s", uuid); + continue; + } + + DBG("%s", uuid); + + queue_push_tail(btd_opts.experimental, strdup(uuid)); + } +} + static void parse_config(GKeyFile *config) { GError *err = NULL; - char *str; + char *str, **strlist; int val; gboolean boolean; @@ -722,12 +791,14 @@ static void parse_config(GKeyFile *config) else btd_opts.refresh_discovery = boolean; - boolean = g_key_file_get_boolean(config, "General", - "Experimental", &err); + strlist = g_key_file_get_string_list(config, "General", "Experimental", + NULL, &err); if (err) g_clear_error(&err); - else - btd_opts.experimental = boolean; + else { + btd_parse_experimental(strlist); + g_strfreev(strlist); + } str = g_key_file_get_string(config, "GATT", "Cache", &err); if (err) { @@ -840,7 +911,6 @@ static void init_defaults(void) btd_opts.name_resolv = TRUE; btd_opts.debug_keys = FALSE; btd_opts.refresh_discovery = TRUE; - btd_opts.experimental = false; btd_opts.defaults.num_entries = 0; btd_opts.defaults.br.page_scan_type = 0xFFFF; @@ -993,6 +1063,24 @@ static gboolean parse_debug(const char *key, const char *value, return TRUE; } +static gboolean parse_experimental(const char *key, const char *value, + gpointer user_data, GError **error) +{ + char **strlist; + + if (value) { + strlist = g_strsplit(value, ",", -1); + btd_parse_experimental(strlist); + g_strfreev(strlist); + } else { + if (!btd_opts.experimental) + btd_opts.experimental = queue_new(); + queue_push_head(btd_opts.experimental, strdup("*")); + } + + return TRUE; +} + static GOptionEntry options[] = { { "debug", 'd', G_OPTION_FLAG_OPTIONAL_ARG, G_OPTION_ARG_CALLBACK, parse_debug, @@ -1005,8 +1093,9 @@ static GOptionEntry options[] = { "Specify an explicit path to the config file", "FILE"}, { "compat", 'C', 0, G_OPTION_ARG_NONE, &option_compat, "Provide deprecated command line interfaces" }, - { "experimental", 'E', 0, G_OPTION_ARG_NONE, &btd_opts.experimental, - "Enable experimental interfaces" }, + { "experimental", 'E', G_OPTION_FLAG_OPTIONAL_ARG, + G_OPTION_ARG_CALLBACK, parse_experimental, + "Enable experimental features/interfaces" }, { "nodetach", 'n', G_OPTION_FLAG_REVERSE, G_OPTION_ARG_NONE, &option_detach, "Run with logging in foreground" }, @@ -1135,6 +1224,9 @@ int main(int argc, char *argv[]) if (btd_opts.mode != BT_MODE_LE) stop_sdp_server(); + if (btd_opts.experimental) + queue_destroy(btd_opts.experimental, free); + if (main_conf) g_key_file_free(main_conf); diff --git a/src/main.conf b/src/main.conf index 71924cb17..e05291d8e 100644 --- a/src/main.conf +++ b/src/main.conf @@ -86,7 +86,15 @@ # profile is connected. Defaults to true. #RefreshDiscovery = true -# Enables experimental features and interfaces. +# Enables experimental features and interfaces, alternatively a list of UUIDs +# can be given. +# Possible values: true,false,<UUID List> +# Possible UUIDS: +# d4992530-b9ec-469f-ab01-6c481c47da1c (BlueZ Experimental Debug) +# 671b10b5-42c0-4696-9227-eb28d1b049d6 (BlueZ Experimental Simultaneous Central and Peripheral) +# 15c0a148-c273-11ea-b3de-0242ac130004 (BlueZ Experimental LL privacy) +# 330859bc-7506-492d-9370-9a6f0614037f (BlueZ Experimental Bluetooth Quality Report) +# a6695ace-ee7f-4fb9-881a-5fac66c629af (BlueZ Experimental Offload Codecs) # Defaults to false. #Experimental = false -- 2.31.1