Remove hardcoded base and QoS for broadcast and use presets instead. Remove BIG, BIS, and context from the register endpoint command for Broadcast. Add Location and Metadata support for endpoint config for broadcast source. --- client/player.c | 301 ++++++++++++++++++++++++++++++++---------------- 1 file changed, 201 insertions(+), 100 deletions(-) diff --git a/client/player.c b/client/player.c index 92fc91f92..cf4d9a4f2 100644 --- a/client/player.c +++ b/client/player.c @@ -72,6 +72,17 @@ #define EP_SNK_CTXT 0x0fff #define EP_SUPPORTED_SNK_CTXT EP_SNK_CTXT +/* BAP Broadcast parameters */ +#define BCAST_SYNC_FACTOR 24 /* PA params */ +#define BCAST_OPTIONS 0x00 /* PA Create Sync */ +#define BCAST_SKIP 0x0000 /* PA Create Sync */ +#define BCAST_SYNC_TIMEOUT 0x4000 /* PA Create Sync */ +#define BCAST_SYNC_CTE_TYPE 0x00 /* PA Create Sync */ +#define BCAST_MSE 0x00 /* BIG Create Sync */ +#define BCAST_TIMEOUT 0x4000 /* BIG Create Sync */ +#define BCAST_CODE {0x01, 0x02, 0x68, 0x05, 0x53, 0xf1, 0x41, 0x5a, \ + 0xa2, 0x65, 0xbb, 0xaf, 0xc6, 0xea, 0x03, 0xb8} + struct endpoint { char *path; char *uuid; @@ -92,7 +103,7 @@ struct endpoint { DBusMessage *msg; struct preset *preset; bool broadcast; - struct iovec *bcode; + struct iovec bcode; }; static DBusConnection *dbus_conn; @@ -105,6 +116,7 @@ static GList *endpoints = NULL; static GList *local_endpoints = NULL; static GList *transports = NULL; static struct queue *ios = NULL; +static uint8_t bcast_code[] = BCAST_CODE; struct transport { GDBusProxy *proxy; @@ -118,21 +130,7 @@ struct transport { struct io *timer_io; }; -static const uint8_t base_lc3_16_2_1[] = { - 0x28, 0x00, 0x00, /* Presentation Delay */ - 0x01, /* Number of Subgroups */ - 0x01, /* Number of BIS */ - 0x06, 0x00, 0x00, 0x00, 0x00, /* Code ID = LC3 (0x06) */ - 0x10, /* Codec Specific Configuration */ - 0x02, 0x01, 0x03, /* 16 KHZ */ - 0x02, 0x02, 0x01, /* 10 ms */ - 0x05, 0x03, 0x01, 0x00, 0x00, 0x00, /* Front Left */ - 0x03, 0x04, 0x28, 0x00, /* Frame Length 40 bytes */ - 0x04, /* Metadata */ - 0x03, 0x02, 0x02, 0x00, /* Audio Context: Convertional */ - 0x01, /* BIS */ - 0x00, /* Codec Specific Configuration */ -}; +static void endpoint_set_metadata_cfg(const char *input, void *user_data); static void endpoint_unregister(void *data) { @@ -1857,37 +1855,33 @@ static DBusMessage *endpoint_select_configuration(DBusConnection *conn, struct endpoint_config { GDBusProxy *proxy; struct endpoint *ep; - struct iovec *caps; - struct iovec *meta; + struct iovec *caps; /* Codec Specific Configuration LTVs */ + struct iovec *meta; /* Metadata LTVs*/ uint8_t target_latency; - const struct codec_qos *qos; -}; - -#define BCODE {0x01, 0x02, 0x68, 0x05, 0x53, 0xf1, 0x41, 0x5a, \ - 0xa2, 0x65, 0xbb, 0xaf, 0xc6, 0xea, 0x03, 0xb8} - -static struct bt_iso_qos bcast_qos = { - .bcast = { - .big = BT_ISO_QOS_BIG_UNSET, - .bis = BT_ISO_QOS_BIS_UNSET, - .sync_factor = 24, - .packing = 0x00, - .framing = 0x00, - .encryption = 0x00, - .bcode = BCODE, - .options = 0x00, - .skip = 0x0000, - .sync_timeout = 0x4000, - .sync_cte_type = 0x00, - .mse = 0x00, - .timeout = 0x4000, - } + const struct codec_qos *qos; /* BAP QOS configuration parameters */ + uint8_t sync_factor; /* PA parameter */ + uint8_t options; /* PA create sync parameter */ + uint16_t skip; /* PA create sync parameter */ + uint16_t sync_timeout; /* PA create sync parameter */ + uint8_t sync_cte_type; /* PA create sync parameter */ + uint8_t mse; /* BIG create sync parameter */ + uint16_t timeout; /* BIG create sync parameter */ }; static void append_io_qos(DBusMessageIter *iter, struct endpoint_config *cfg) { struct codec_qos *qos = (void *)cfg->qos; + bt_shell_printf("Framing 0x%02x\n", qos->framing); + + g_dbus_dict_append_entry(iter, "Framing", DBUS_TYPE_BYTE, + &qos->framing); + + bt_shell_printf("PresentationDelay %u\n", qos->delay); + + g_dbus_dict_append_entry(iter, "PresentationDelay", + DBUS_TYPE_UINT32, &qos->delay); + bt_shell_printf("Interval %u\n", qos->interval); g_dbus_dict_append_entry(iter, "Interval", DBUS_TYPE_UINT32, @@ -1949,71 +1943,67 @@ static void append_ucast_qos(DBusMessageIter *iter, struct endpoint_config *cfg) static void append_bcast_qos(DBusMessageIter *iter, struct endpoint_config *cfg) { - if (bcast_qos.bcast.big != BT_ISO_QOS_BIG_UNSET) { - bt_shell_printf("BIG 0x%2.2x\n", bcast_qos.bcast.big); + if (cfg->ep->iso_group != BT_ISO_QOS_BIG_UNSET) { + bt_shell_printf("BIG 0x%2.2x\n", cfg->ep->iso_group); g_dbus_dict_append_entry(iter, "BIG", DBUS_TYPE_BYTE, - &bcast_qos.bcast.big); + &cfg->ep->iso_group); } - if (bcast_qos.bcast.bis != BT_ISO_QOS_BIS_UNSET) { - bt_shell_printf("BIS 0x%2.2x\n", bcast_qos.bcast.bis); + if (cfg->ep->iso_stream != BT_ISO_QOS_BIS_UNSET) { + bt_shell_printf("BIS 0x%2.2x\n", cfg->ep->iso_stream); g_dbus_dict_append_entry(iter, "BIS", DBUS_TYPE_BYTE, - &bcast_qos.bcast.bis); + &cfg->ep->iso_stream); } - bt_shell_printf("Framing 0x%02x\n", bcast_qos.bcast.framing); - - g_dbus_dict_append_entry(iter, "Framing", DBUS_TYPE_BYTE, - &bcast_qos.bcast.framing); - - bt_shell_printf("SyncFactor %u\n", bcast_qos.bcast.sync_factor); + bt_shell_printf("SyncFactor %u\n", cfg->sync_factor); g_dbus_dict_append_entry(iter, "SyncFactor", DBUS_TYPE_BYTE, - &bcast_qos.bcast.sync_factor); + &cfg->sync_factor); - bt_shell_printf("Options %u\n", bcast_qos.bcast.options); + bt_shell_printf("Options %u\n", cfg->options); g_dbus_dict_append_entry(iter, "Options", DBUS_TYPE_BYTE, - &bcast_qos.bcast.options); + &cfg->options); - bt_shell_printf("Skip %u\n", bcast_qos.bcast.skip); + bt_shell_printf("Skip %u\n", cfg->skip); g_dbus_dict_append_entry(iter, "Skip", DBUS_TYPE_UINT16, - &bcast_qos.bcast.skip); + &cfg->skip); - bt_shell_printf("SyncTimeout %u\n", bcast_qos.bcast.sync_timeout); + bt_shell_printf("SyncTimeout %u\n", cfg->sync_timeout); g_dbus_dict_append_entry(iter, "SyncTimeout", DBUS_TYPE_UINT16, - &bcast_qos.bcast.sync_timeout); + &cfg->sync_timeout); - bt_shell_printf("SyncCteType %u\n", bcast_qos.bcast.sync_cte_type); + bt_shell_printf("SyncCteType %u\n", cfg->sync_cte_type); g_dbus_dict_append_entry(iter, "SyncType", DBUS_TYPE_BYTE, - &bcast_qos.bcast.sync_cte_type); + &cfg->sync_cte_type); - bt_shell_printf("MSE %u\n", bcast_qos.bcast.mse); + bt_shell_printf("MSE %u\n", cfg->mse); g_dbus_dict_append_entry(iter, "MSE", DBUS_TYPE_BYTE, - &bcast_qos.bcast.mse); + &cfg->mse); - bt_shell_printf("Timeout %u\n", bcast_qos.bcast.timeout); + bt_shell_printf("Timeout %u\n", cfg->timeout); g_dbus_dict_append_entry(iter, "Timeout", DBUS_TYPE_UINT16, - &bcast_qos.bcast.timeout); + &cfg->timeout); - if (cfg->ep->bcode) { + if (cfg->ep->bcode.iov_len != 0) { const char *key = "BCode"; bt_shell_printf("BCode:\n"); - bt_shell_hexdump(cfg->ep->bcode->iov_base, - cfg->ep->bcode->iov_len); + bt_shell_hexdump(cfg->ep->bcode.iov_base, + cfg->ep->bcode.iov_len); g_dbus_dict_append_basic_array(iter, DBUS_TYPE_STRING, &key, DBUS_TYPE_BYTE, - &cfg->ep->bcode->iov_base, - cfg->ep->bcode->iov_len); + &cfg->ep->bcode.iov_base, + cfg->ep->bcode.iov_len); } + /* Add BAP codec QOS configuration */ append_io_qos(iter, cfg); } @@ -2748,9 +2738,6 @@ static void endpoint_iso_group(const char *input, void *user_data) if (!ep->broadcast) bt_shell_prompt_input(ep->path, "CIS (auto/value):", endpoint_iso_stream, ep); - else - bt_shell_prompt_input(ep->path, "BIS (auto/value):", - endpoint_iso_stream, ep); } static void endpoint_context(const char *input, void *user_data) @@ -2768,12 +2755,8 @@ static void endpoint_context(const char *input, void *user_data) ep->context = value; - if (ep->broadcast) - bt_shell_prompt_input(ep->path, "BIG (auto/value):", - endpoint_iso_group, ep); - else - bt_shell_prompt_input(ep->path, "CIG (auto/value):", - endpoint_iso_group, ep); + bt_shell_prompt_input(ep->path, "CIG (auto/value):", + endpoint_iso_group, ep); } static void endpoint_supported_context(const char *input, void *user_data) @@ -2810,8 +2793,11 @@ static void endpoint_locations(const char *input, void *user_data) ep->locations = value; - bt_shell_prompt_input(ep->path, "Supported Context (value):", - endpoint_supported_context, ep); + if (ep->broadcast) + endpoint_register(ep); + else + bt_shell_prompt_input(ep->path, "Supported Context (value):", + endpoint_supported_context, ep); } static void endpoint_max_transports(const char *input, void *user_data) @@ -2840,13 +2826,6 @@ static void endpoint_auto_accept(const char *input, void *user_data) { struct endpoint *ep = user_data; - if (!strcmp(ep->uuid, BCAA_SERVICE_UUID) || - !strcmp(ep->uuid, BAA_SERVICE_UUID)) { - ep->broadcast = true; - } else { - ep->broadcast = false; - } - if (!strcasecmp(input, "y") || !strcasecmp(input, "yes")) { ep->auto_accept = true; bt_shell_prompt_input(ep->path, "Max Transports (auto/value):", @@ -2887,6 +2866,34 @@ done: endpoint_auto_accept, ep); } +static void config_endpoint_channel_location(const char *input, void *user_data) +{ + struct endpoint_config *cfg = user_data; + char *endptr = NULL; + int value; + uint32_t location; + + value = strtol(input, &endptr, 0); + + if (!endptr || *endptr != '\0' || value > UINT8_MAX) { + bt_shell_printf("Invalid argument: %s\n", input); + return bt_shell_noninteractive_quit(EXIT_FAILURE); + } + + /* Add Audio Allocation LTV in Capabilities */ + { + uint8_t ltv[6] = { 0x05, LC3_CONFIG_CHAN_ALLOC }; + + location = cpu_to_le32(value); + memcpy(<v[2], &location, sizeof(location)); + iov_append(&cfg->caps, ltv, sizeof(ltv)); + } + + /* Add metadata */ + bt_shell_prompt_input(cfg->ep->path, "Enter Metadata (value/no):", + endpoint_set_metadata_cfg, cfg); +} + static void endpoint_set_capabilities(const char *input, void *user_data) { struct endpoint *ep = user_data; @@ -2964,6 +2971,13 @@ static void cmd_register_endpoint(int argc, char *argv[]) g_list_length(local_endpoints)); local_endpoints = g_list_append(local_endpoints, ep); + if (!strcmp(ep->uuid, BCAA_SERVICE_UUID) || + !strcmp(ep->uuid, BAA_SERVICE_UUID)) { + ep->broadcast = true; + } else { + ep->broadcast = false; + } + if (strrchr(argv[2], ':')) { ep->codec = 0xff; parse_vendor_codec(argv[2], &ep->cid, &ep->vid); @@ -3110,7 +3124,81 @@ static void endpoint_config(const char *input, void *user_data) endpoint_set_config(cfg); } + +static void endpoint_set_metadata_cfg(const char *input, void *user_data) +{ + struct endpoint_config *cfg = user_data; + + if (!strcasecmp(input, "n") || !strcasecmp(input, "no")) + goto done; + + if (!cfg->meta) + cfg->meta = g_new0(struct iovec, 1); + + cfg->meta->iov_base = str2bytearray((char *) input, + &cfg->meta->iov_len); + if (!cfg->meta->iov_base) { + free(cfg->meta); + cfg->meta = NULL; + } + +done: + endpoint_set_config(cfg); +} + +static void config_endpoint_iso_group(const char *input, void *user_data) +{ + struct endpoint_config *cfg = user_data; + char *endptr = NULL; + int value; + bool found = false; + + value = strtol(input, &endptr, 0); + + if (!endptr || *endptr != '\0' || value > UINT8_MAX) { + bt_shell_printf("Invalid argument: %s\n", input); + return bt_shell_noninteractive_quit(EXIT_FAILURE); + } + + cfg->ep->iso_group = value; + + /* Check if Channel allocation is present in caps */ + { + struct iovec iov; + int i; + + iov.iov_base = (void *) cfg->caps->iov_base; + iov.iov_len = cfg->caps->iov_len; + + for (i = 0; iov.iov_len; i++) { + uint8_t l, t; + + util_iov_pull_u8(&iov, &l); + util_iov_pull_u8(&iov, &t); + l--; + (void *)util_iov_pull_mem(&iov, l); + if (t == LC3_CONFIG_CHAN_ALLOC) { + found = true; + break; + } + } + } + + /* Add channel allocation if it is not present in caps */ + if (!found) { + bt_shell_prompt_input(cfg->ep->path, + "Enter channel location (value):", + config_endpoint_channel_location, cfg); + } else { + /* Add metadata */ + bt_shell_prompt_input(cfg->ep->path, + "Enter Metadata (value/no):", + endpoint_set_metadata_cfg, cfg); + } +} + static struct endpoint *endpoint_new(const struct capabilities *cap); +static void endpoint_init_defaults(struct endpoint *ep); static void cmd_config_endpoint(int argc, char *argv[]) { @@ -3144,6 +3232,7 @@ static void cmd_config_endpoint(int argc, char *argv[]) if (cap) { broadcast = true; cfg->ep = endpoint_new(cap); + endpoint_init_defaults(cfg->ep); cfg->ep->preset = find_presets_name(uuid, argv[3]); if (!cfg->ep->preset) bt_shell_printf("Preset not found\n"); @@ -3164,22 +3253,34 @@ static void cmd_config_endpoint(int argc, char *argv[]) goto fail; } - if (cfg->ep->broadcast) { - iov_append(&cfg->ep->bcode, bcast_qos.bcast.bcode, - sizeof(bcast_qos.bcast.bcode)); - /* Copy capabilities for broadcast*/ - iov_append(&cfg->caps, base_lc3_16_2_1, - sizeof(base_lc3_16_2_1)); - } else { - /* Copy capabilities */ - iov_append(&cfg->caps, preset->data.iov_base, - preset->data.iov_len); - } + /* Copy capabilities */ + iov_append(&cfg->caps, preset->data.iov_base, + preset->data.iov_len); /* Set QoS parameters */ cfg->qos = &preset->qos; - endpoint_set_config(cfg); + if (cfg->ep->broadcast) { + cfg->ep->bcode.iov_base = bcast_code; + cfg->ep->bcode.iov_len = sizeof(bcast_code); + + /* Add periodic advertisement parameters */ + cfg->sync_factor = BCAST_SYNC_FACTOR; + cfg->options = BCAST_OPTIONS; + cfg->skip = BCAST_SKIP; + cfg->sync_timeout = BCAST_SYNC_TIMEOUT; + cfg->sync_cte_type = BCAST_SYNC_CTE_TYPE; + /* Add BIG create sync parameters */ + cfg->mse = BCAST_MSE; + cfg->timeout = BCAST_TIMEOUT; + if ((strcmp(cfg->ep->uuid, BCAA_SERVICE_UUID) == 0)) + bt_shell_prompt_input(cfg->ep->path, + "BIG (value):", + config_endpoint_iso_group, cfg); + else + endpoint_set_config(cfg); + } else + endpoint_set_config(cfg); return; } -- 2.39.2