mirror of
https://git.kernel.org/pub/scm/bluetooth/bluez.git
synced 2024-11-15 08:14:28 +08:00
client/player: Add support for broadcast source
This adds bluetoothctl support for broadcast source. To test the current implementation use bluetoothctl with the commands: endpoint.register 00001852-0000-1000-8000-00805f9b34fb 0x06 endpoint.config <created endpoint> <local endpoint> 16_2_1 transport.acquire <created transport> transport.send <created transport> <file.wav>
This commit is contained in:
parent
eeb349bbab
commit
eb821743f9
226
client/player.c
226
client/player.c
@ -74,11 +74,13 @@ struct endpoint {
|
||||
bool auto_accept;
|
||||
bool acquiring;
|
||||
uint8_t max_transports;
|
||||
uint8_t cig;
|
||||
uint8_t cis;
|
||||
uint8_t iso_group;
|
||||
uint8_t iso_stream;
|
||||
char *transport;
|
||||
DBusMessage *msg;
|
||||
struct preset *preset;
|
||||
bool broadcast;
|
||||
struct iovec *bcode;
|
||||
};
|
||||
|
||||
static DBusConnection *dbus_conn;
|
||||
@ -104,6 +106,22 @@ 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) */
|
||||
0x11, /* 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_unregister(void *data)
|
||||
{
|
||||
struct endpoint *ep = data;
|
||||
@ -1154,6 +1172,16 @@ static const struct capabilities {
|
||||
CODEC_CAPABILITIES(PAC_SOURCE_UUID, LC3_ID,
|
||||
LC3_DATA(LC3_FREQ_ANY, LC3_DURATION_ANY,
|
||||
3u, 30, 240)),
|
||||
/* Broadcast LC3 Source:
|
||||
*
|
||||
* Frequencies: 8Khz 11Khz 16Khz 22Khz 24Khz 32Khz 44.1Khz 48Khz
|
||||
* Duration: 7.5 ms 10 ms
|
||||
* Channel count: 3
|
||||
* Frame length: 30-240
|
||||
*/
|
||||
CODEC_CAPABILITIES(BAA_SERVICE_UUID, LC3_ID,
|
||||
LC3_DATA(LC3_FREQ_ANY, LC3_DURATION_ANY,
|
||||
3u, 30, 240)),
|
||||
};
|
||||
|
||||
struct codec_qos {
|
||||
@ -1435,6 +1463,7 @@ static struct preset {
|
||||
PRESET(A2DP_SINK_UUID, A2DP_CODEC_SBC, sbc_presets, 6),
|
||||
PRESET(PAC_SINK_UUID, LC3_ID, lc3_presets, 3),
|
||||
PRESET(PAC_SOURCE_UUID, LC3_ID, lc3_presets, 3),
|
||||
PRESET(BAA_SERVICE_UUID, LC3_ID, lc3_presets, 3),
|
||||
};
|
||||
|
||||
static void parse_vendor_codec(const char *codec, uint16_t *vid, uint16_t *cid)
|
||||
@ -1707,6 +1736,27 @@ struct endpoint_config {
|
||||
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_interval = 0x07,
|
||||
.packing = 0x00,
|
||||
.framing = 0x00,
|
||||
.encryption = 0x00,
|
||||
.bcode = BCODE,
|
||||
.options = 0x00,
|
||||
.skip = 0x0000,
|
||||
.sync_timeout = 0x4000,
|
||||
.sync_cte_type = 0x00,
|
||||
.mse = 0x00,
|
||||
.timeout = 0x4000,
|
||||
}
|
||||
};
|
||||
|
||||
static void append_properties(DBusMessageIter *iter,
|
||||
struct endpoint_config *cfg)
|
||||
{
|
||||
@ -1714,6 +1764,7 @@ static void append_properties(DBusMessageIter *iter,
|
||||
struct codec_qos *qos = (void *)cfg->qos;
|
||||
const char *key = "Capabilities";
|
||||
const char *meta = "Metadata";
|
||||
const char *keyBCode = "BroadcastCode";
|
||||
|
||||
dbus_message_iter_open_container(iter, DBUS_TYPE_ARRAY, "{sv}", &dict);
|
||||
|
||||
@ -1742,16 +1793,27 @@ static void append_properties(DBusMessageIter *iter,
|
||||
DBUS_TYPE_BYTE, &cfg->target_latency);
|
||||
}
|
||||
|
||||
if (cfg->ep->cig != BT_ISO_QOS_CIG_UNSET) {
|
||||
bt_shell_printf("CIG 0x%2.2x\n", cfg->ep->cig);
|
||||
if ((!cfg->ep->broadcast) &&
|
||||
(cfg->ep->iso_group != BT_ISO_QOS_GROUP_UNSET)) {
|
||||
bt_shell_printf("CIG 0x%2.2x\n", cfg->ep->iso_group);
|
||||
g_dbus_dict_append_entry(&dict, "CIG", DBUS_TYPE_BYTE,
|
||||
&cfg->ep->cig);
|
||||
&cfg->ep->iso_group);
|
||||
} else {
|
||||
bt_shell_printf("BIG 0x%2.2x\n", bcast_qos.bcast.big);
|
||||
g_dbus_dict_append_entry(&dict, "BIG", DBUS_TYPE_BYTE,
|
||||
&bcast_qos.bcast.big);
|
||||
}
|
||||
|
||||
if (cfg->ep->cis != BT_ISO_QOS_CIS_UNSET) {
|
||||
bt_shell_printf("CIS 0x%2.2x\n", cfg->ep->cis);
|
||||
if ((!cfg->ep->broadcast) &&
|
||||
(cfg->ep->iso_stream != BT_ISO_QOS_STREAM_UNSET)) {
|
||||
bt_shell_printf("CIS 0x%2.2x\n", cfg->ep->iso_stream);
|
||||
g_dbus_dict_append_entry(&dict, "CIS", DBUS_TYPE_BYTE,
|
||||
&cfg->ep->cis);
|
||||
&cfg->ep->iso_stream);
|
||||
|
||||
} else {
|
||||
bt_shell_printf("BIS 0x%2.2x\n", bcast_qos.bcast.bis);
|
||||
g_dbus_dict_append_entry(&dict, "BIS", DBUS_TYPE_BYTE,
|
||||
&bcast_qos.bcast.bis);
|
||||
}
|
||||
|
||||
bt_shell_printf("Interval %u\n", qos->interval);
|
||||
@ -1759,10 +1821,19 @@ static void append_properties(DBusMessageIter *iter,
|
||||
g_dbus_dict_append_entry(&dict, "Interval", DBUS_TYPE_UINT32,
|
||||
&qos->interval);
|
||||
|
||||
bt_shell_printf("Framing %s\n", qos->framing ? "true" : "false");
|
||||
if (!cfg->ep->broadcast) {
|
||||
bt_shell_printf("Framing %s\n",
|
||||
qos->framing ? "true" : "false");
|
||||
|
||||
g_dbus_dict_append_entry(&dict, "Framing", DBUS_TYPE_BOOLEAN,
|
||||
&qos->framing);
|
||||
g_dbus_dict_append_entry(&dict, "Framing", DBUS_TYPE_BOOLEAN,
|
||||
&qos->framing);
|
||||
} else {
|
||||
bt_shell_printf("Framing %s\n",
|
||||
bcast_qos.bcast.framing ? "true" : "false");
|
||||
|
||||
g_dbus_dict_append_entry(&dict, "Framing", DBUS_TYPE_BOOLEAN,
|
||||
&bcast_qos.bcast.framing);
|
||||
}
|
||||
|
||||
bt_shell_printf("PHY %s\n", qos->phy);
|
||||
|
||||
@ -1787,6 +1858,57 @@ static void append_properties(DBusMessageIter *iter,
|
||||
g_dbus_dict_append_entry(&dict, "Delay", DBUS_TYPE_UINT32,
|
||||
&qos->delay);
|
||||
|
||||
if (!cfg->ep->broadcast)
|
||||
goto done;
|
||||
|
||||
bt_shell_printf("SyncInterval %u\n", bcast_qos.bcast.sync_interval);
|
||||
|
||||
g_dbus_dict_append_entry(&dict, "SyncInterval", DBUS_TYPE_BYTE,
|
||||
&bcast_qos.bcast.sync_interval);
|
||||
|
||||
bt_shell_printf("Encryption %u\n", bcast_qos.bcast.encryption);
|
||||
|
||||
g_dbus_dict_append_entry(&dict, "Encryption", DBUS_TYPE_BYTE,
|
||||
&bcast_qos.bcast.encryption);
|
||||
|
||||
bt_shell_printf("Options %u\n", bcast_qos.bcast.options);
|
||||
|
||||
g_dbus_dict_append_entry(&dict, "Options", DBUS_TYPE_BYTE,
|
||||
&bcast_qos.bcast.options);
|
||||
|
||||
bt_shell_printf("Skip %u\n", bcast_qos.bcast.skip);
|
||||
|
||||
g_dbus_dict_append_entry(&dict, "Skip", DBUS_TYPE_UINT16,
|
||||
&bcast_qos.bcast.skip);
|
||||
|
||||
bt_shell_printf("SyncTimeout %u\n", bcast_qos.bcast.sync_timeout);
|
||||
|
||||
g_dbus_dict_append_entry(&dict, "SyncTimeout", DBUS_TYPE_UINT16,
|
||||
&bcast_qos.bcast.sync_timeout);
|
||||
|
||||
bt_shell_printf("SyncCteType %u\n", bcast_qos.bcast.sync_cte_type);
|
||||
|
||||
g_dbus_dict_append_entry(&dict, "SyncCteType", DBUS_TYPE_BYTE,
|
||||
&bcast_qos.bcast.sync_cte_type);
|
||||
|
||||
bt_shell_printf("MSE %u\n", bcast_qos.bcast.mse);
|
||||
|
||||
g_dbus_dict_append_entry(&dict, "MSE", DBUS_TYPE_BYTE,
|
||||
&bcast_qos.bcast.mse);
|
||||
|
||||
bt_shell_printf("Timeout %u\n", bcast_qos.bcast.timeout);
|
||||
|
||||
g_dbus_dict_append_entry(&dict, "Timeout", DBUS_TYPE_UINT16,
|
||||
&bcast_qos.bcast.timeout);
|
||||
|
||||
bt_shell_printf("BroadcastCode:\n");
|
||||
bt_shell_hexdump(cfg->ep->bcode->iov_base, cfg->ep->bcode->iov_len);
|
||||
|
||||
g_dbus_dict_append_basic_array(&dict, DBUS_TYPE_STRING, &keyBCode,
|
||||
DBUS_TYPE_BYTE,
|
||||
&cfg->ep->bcode->iov_base,
|
||||
cfg->ep->bcode->iov_len);
|
||||
|
||||
done:
|
||||
dbus_message_iter_close_container(iter, &dict);
|
||||
}
|
||||
@ -2239,14 +2361,14 @@ fail:
|
||||
|
||||
}
|
||||
|
||||
static void endpoint_cis(const char *input, void *user_data)
|
||||
static void endpoint_iso_stream(const char *input, void *user_data)
|
||||
{
|
||||
struct endpoint *ep = user_data;
|
||||
char *endptr = NULL;
|
||||
int value;
|
||||
|
||||
if (!strcasecmp(input, "a") || !strcasecmp(input, "auto")) {
|
||||
ep->cis = BT_ISO_QOS_CIS_UNSET;
|
||||
ep->iso_stream = BT_ISO_QOS_STREAM_UNSET;
|
||||
} else {
|
||||
value = strtol(input, &endptr, 0);
|
||||
|
||||
@ -2255,20 +2377,20 @@ static void endpoint_cis(const char *input, void *user_data)
|
||||
return bt_shell_noninteractive_quit(EXIT_FAILURE);
|
||||
}
|
||||
|
||||
ep->cis = value;
|
||||
ep->iso_stream = value;
|
||||
}
|
||||
|
||||
endpoint_register(ep);
|
||||
}
|
||||
|
||||
static void endpoint_cig(const char *input, void *user_data)
|
||||
static void endpoint_iso_group(const char *input, void *user_data)
|
||||
{
|
||||
struct endpoint *ep = user_data;
|
||||
char *endptr = NULL;
|
||||
int value;
|
||||
|
||||
if (!strcasecmp(input, "a") || !strcasecmp(input, "auto")) {
|
||||
ep->cig = BT_ISO_QOS_CIG_UNSET;
|
||||
ep->iso_group = BT_ISO_QOS_GROUP_UNSET;
|
||||
} else {
|
||||
value = strtol(input, &endptr, 0);
|
||||
|
||||
@ -2277,10 +2399,15 @@ static void endpoint_cig(const char *input, void *user_data)
|
||||
return bt_shell_noninteractive_quit(EXIT_FAILURE);
|
||||
}
|
||||
|
||||
ep->cig = value;
|
||||
ep->iso_group = value;
|
||||
}
|
||||
|
||||
bt_shell_prompt_input(ep->path, "CIS (auto/value):", endpoint_cis, ep);
|
||||
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_max_transports(const char *input, void *user_data)
|
||||
@ -2302,13 +2429,24 @@ static void endpoint_max_transports(const char *input, void *user_data)
|
||||
ep->max_transports = value;
|
||||
}
|
||||
|
||||
bt_shell_prompt_input(ep->path, "CIG (auto/value):", endpoint_cig, ep);
|
||||
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);
|
||||
}
|
||||
|
||||
static void endpoint_auto_accept(const char *input, void *user_data)
|
||||
{
|
||||
struct endpoint *ep = user_data;
|
||||
|
||||
if (!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):",
|
||||
@ -2321,7 +2459,12 @@ static void endpoint_auto_accept(const char *input, void *user_data)
|
||||
return bt_shell_noninteractive_quit(EXIT_FAILURE);
|
||||
}
|
||||
|
||||
bt_shell_prompt_input(ep->path, "CIG (auto/value):", endpoint_cig, ep);
|
||||
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);
|
||||
}
|
||||
|
||||
static void endpoint_set_metadata(const char *input, void *user_data)
|
||||
@ -2598,9 +2741,17 @@ static void cmd_config_endpoint(int argc, char *argv[])
|
||||
goto fail;
|
||||
}
|
||||
|
||||
/* Copy capabilities */
|
||||
iov_append(&cfg->caps, preset->data.iov_base,
|
||||
preset->data.iov_len);
|
||||
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);
|
||||
}
|
||||
|
||||
/* Set QoS parameters */
|
||||
cfg->qos = &preset->qos;
|
||||
@ -3050,8 +3201,8 @@ static void register_endpoints(GDBusProxy *proxy)
|
||||
ep->cid);
|
||||
ep->max_transports = UINT8_MAX;
|
||||
ep->auto_accept = true;
|
||||
ep->cig = BT_ISO_QOS_CIG_UNSET;
|
||||
ep->cis = BT_ISO_QOS_CIS_UNSET;
|
||||
ep->iso_group = BT_ISO_QOS_GROUP_UNSET;
|
||||
ep->iso_stream = BT_ISO_QOS_STREAM_UNSET;
|
||||
endpoint_register(ep);
|
||||
}
|
||||
}
|
||||
@ -3595,6 +3746,7 @@ static void cmd_acquire_transport(int argc, char *argv[])
|
||||
{
|
||||
GDBusProxy *proxy;
|
||||
int i;
|
||||
struct endpoint *ep, *link;
|
||||
|
||||
for (i = 1; i < argc; i++) {
|
||||
proxy = g_dbus_proxy_lookup(transports, NULL, argv[i],
|
||||
@ -3610,6 +3762,30 @@ static void cmd_acquire_transport(int argc, char *argv[])
|
||||
return bt_shell_noninteractive_quit(EXIT_FAILURE);
|
||||
}
|
||||
|
||||
ep = find_ep_by_transport(g_dbus_proxy_get_path(proxy));
|
||||
if (!ep || ep->acquiring) {
|
||||
bt_shell_printf(
|
||||
"Transport %s already in acquiring process\n",
|
||||
argv[i]);
|
||||
return bt_shell_noninteractive_quit(EXIT_FAILURE);
|
||||
}
|
||||
|
||||
ep->acquiring = true;
|
||||
|
||||
link = find_link_by_proxy(proxy);
|
||||
if (link) {
|
||||
bt_shell_printf("Link %s found\n", link->transport);
|
||||
/* If link already acquiring wait it to be complete */
|
||||
if (link->acquiring) {
|
||||
bt_shell_printf(
|
||||
"Link %s is in acquiring process\n",
|
||||
argv[i]);
|
||||
return bt_shell_noninteractive_quit(
|
||||
EXIT_FAILURE);
|
||||
}
|
||||
link->acquiring = true;
|
||||
}
|
||||
|
||||
if (!g_dbus_proxy_method_call(proxy, "Acquire", NULL,
|
||||
acquire_reply, proxy, NULL)) {
|
||||
bt_shell_printf("Failed acquire transport\n");
|
||||
|
Loading…
Reference in New Issue
Block a user