Skip to content

Commit

Permalink
Bluetooth: BAP: Unicast client Split start and connect
Browse files Browse the repository at this point in the history
Removes the CIS connection establishment from bt_bap_stream_start
and move the behavior to a new funciton bt_bap_stream_connect.

This has 2 advantages:
1) The behavior of bt_bap_stream_start is much more clear and more aligned
with the spec's behavior for the receiver start ready opcode.
2) It is possible to connect streams in both the enabling
and the QoS configured state with bt_bap_stream_connect as
per the spec. This allows us to pass additional PTS test cases.

To implement this new behavior, samples and tests have been updated.

The CAP Initiator implementation has also been updated
to accomodate for the change in BAP, but the CAP
initiator implementation should work the same for application, except
that it's now possible to do unicast start on ASEs in any order
(#72138).

Signed-off-by: Emil Gydesen <emil.gydesen@nordicsemi.no>
  • Loading branch information
Thalley committed May 30, 2024
1 parent 3122296 commit 1c26a48
Show file tree
Hide file tree
Showing 17 changed files with 953 additions and 214 deletions.
47 changes: 33 additions & 14 deletions doc/connectivity/bluetooth/api/audio/shell/bap.rst
Original file line number Diff line number Diff line change
Expand Up @@ -34,6 +34,7 @@ Commands
stream_qos : interval [framing] [latency] [pd] [sdu] [phy] [rtn]
qos : Send QoS configure for Unicast Group
enable : [context]
connect : Connect the CIS of the stream
stop
list
print_ase_info : Print ASE info for default connection
Expand Down Expand Up @@ -62,10 +63,8 @@ Commands
[extended <meta>]
[vendor <meta>]]
send : Send to Audio Stream [data]
start_sine : Start sending a LC3 encoded sine wave [all]
stop_sine : Stop sending a LC3 encoded sine wave [all]
recv_stats : Sets or gets the receive statistics reporting interval
in # of packets
bap_stats : Sets or gets the statistics reporting interval in # of
packets
set_location : <direction: sink, source> <location bitmask>
set_context : <direction: sink, source><context bitmask> <type:
supported, available>
Expand All @@ -80,19 +79,19 @@ Commands
"config","discover","idle/codec-configured/qos-configured","codec-configured"
"qos","config","codec-configured/qos-configured","qos-configured"
"enable","qos","qos-configured","enabling"
"[start]","enable","enabling","streaming"
"connect","qos/enable","qos-configured/enabling","qos-configured/enabling"
"[start]","enable/connect","enabling","streaming"
"disable","enable", "enabling/streaming","disabling"
"[stop]","disable","disabling","qos-configure/idle"
"release","config","any","releasing/codec-configure/idle"
"list","none","any","none"
"select_unicast","none","any","none"
"connect","discover","idle/codec-configured/qos-configured","codec-configured"
"send","enable","streaming","none"

Example Central
***************

Connect and establish a stream:
Connect and establish a sink stream:

.. code-block:: console
Expand All @@ -104,8 +103,9 @@ Connect and establish a stream:
uart:~$ bap config sink 0
uart:~$ bap qos
uart:~$ bap enable
uart:~$ bap connect
Or using connect command:
Connect and establish a source stream:

.. code-block:: console
Expand All @@ -114,7 +114,11 @@ Or using connect command:
uart:~$ bt connect <address>
uart:~$ gatt exchange-mtu
uart:~$ bap discover sink
uart:~$ bap connect sink 0
uart:~$ bap config sink 0
uart:~$ bap qos
uart:~$ bap enable
uart:~$ bap connect
uart:~$ bap start
Disconnect and release:

Expand Down Expand Up @@ -479,8 +483,7 @@ parameters.
Enable
******

The :code:`enable` command attempts to enable the stream previously configured,
if the remote peer accepts then the ISO connection procedure is also initiated.
The :code:`enable` command attempts to enable the stream previously configured.

.. csv-table:: State Machine Transitions
:header: "Depends", "Allowed States", "Next States"
Expand All @@ -493,17 +496,33 @@ if the remote peer accepts then the ISO connection procedure is also initiated.
uart:~$ bap enable [context]
uart:~$ bap enable Media
Connect
*******

The :code:`connect` command attempts to connect the stream previously configured.
Sink streams will have to be started by the unicast server, and source streams will have to be
started by the unicast client.

.. csv-table:: State Machine Transitions
:header: "Depends", "Allowed States", "Next States"
:widths: auto

"qos/enable","qos-configured/enabling","qos-configured/enabling"

.. code-block:: console
uart:~$ bap connect
Start
*****

The :code:`start` command is only necessary when acting as a sink as it
indicates to the source the stack is ready to start receiving data.
The :code:`start` command is only necessary when starting a source stream.

.. csv-table:: State Machine Transitions
:header: "Depends", "Allowed States", "Next States"
:widths: auto

"enable","enabling","streaming"
"enable/connect","enabling","streaming"

.. code-block:: console
Expand Down
4 changes: 4 additions & 0 deletions doc/releases/migration-guide-3.7.rst
Original file line number Diff line number Diff line change
Expand Up @@ -388,6 +388,10 @@ Bluetooth Audio
This needs to be added to all instances of CAP discovery callback functions defined.
(:github:`72797`)

* :c:func:`bt_bap_stream_start` no longer connects the CIS. To connect the CIS,
the :c:func:`bt_bap_stream_connect` shall now be called before :c:func:`bt_bap_stream_start`.
(:github:`73032`)

Bluetooth Classic
=================

Expand Down
41 changes: 32 additions & 9 deletions include/zephyr/bluetooth/audio/bap.h
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@
* @brief Header for Bluetooth BAP.
*
* Copyright (c) 2020 Bose Corporation
* Copyright (c) 2021-2023 Nordic Semiconductor ASA
* Copyright (c) 2021-2024 Nordic Semiconductor ASA
*
* SPDX-License-Identifier: Apache-2.0
*/
Expand Down Expand Up @@ -715,20 +715,43 @@ int bt_bap_stream_metadata(struct bt_bap_stream *stream, const uint8_t meta[], s
*/
int bt_bap_stream_disable(struct bt_bap_stream *stream);

/**
* @brief Connect unicast audio stream
*
* This procedure is used by a unicast client to connect the connected isochronous stream (CIS)
* associated with the audio stream. If two audio streams share a CIS, then this only needs to be
* done once for those streams. This can only be done for streams in the QoS configured or enabled
* states.
*
* The bt_bap_stream_ops.connected() callback will be called on the streams once this has finished.
*
* This shall only be called for unicast streams, and only as the unicast client
* (@kconfig{CONFIG_BT_BAP_UNICAST_CLIENT}).
*
* @param stream Stream object
*
* @retval 0 in case of success
* @retval -EINVAL if the stream, endpoint, ISO channel or connection is NULL
* @retval -EBADMSG if the stream or ISO channel is in an invalid state for connection
* @retval -EOPNOTSUPP if the role of the stream is not @ref BT_HCI_ROLE_CENTRAL
* @retval -EALREADY if the ISO channel is already connecting or connected
* @retval -EBUSY if another ISO channel is connecting
* @retval -ENOEXEC if otherwise rejected by the ISO layer
*/
int bt_bap_stream_connect(struct bt_bap_stream *stream);

/**
* @brief Start Audio Stream
*
* This procedure is used by a unicast client or unicast server to make a stream start streaming.
*
* For the unicast client, this will connect the CIS for the stream before
* sending the start command.
* For the unicast client, this will send the receiver start ready command to the unicast server for
* @ref BT_AUDIO_DIR_SOURCE ASEs. The CIS is required to be connected first by
* bt_bap_stream_connect() before the command can be sent.
*
* For the unicast server, this will put a @ref BT_AUDIO_DIR_SINK stream into the streaming state if
* the CIS is connected (initialized by the unicast client). If the CIS is not connected yet, the
* stream will go into the streaming state as soon as the CIS is connected.
* @ref BT_AUDIO_DIR_SOURCE streams will go into the streaming state when the unicast client sends
* the Receiver Start Ready operation, which will trigger the @ref bt_bap_unicast_server_cb.start()
* callback.
* For the unicast server, this will execute the receiver start ready command on the unicast server
* for @ref BT_AUDIO_DIR_SINK ASEs. If the CIS is not connected yet, the stream will go into the
* streaming state as soon as the CIS is connected.
*
* This shall only be called for unicast streams.
*
Expand Down
19 changes: 16 additions & 3 deletions samples/bluetooth/tmap_peripheral/src/bap_unicast_sr.c
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
/** @file
* @brief Bluetooth Basic Audio Profile (BAP) Unicast Server role.
*
* Copyright (c) 2021-2023 Nordic Semiconductor ASA
* Copyright (c) 2021-2024 Nordic Semiconductor ASA
* Copyright (c) 2022 Codecoup
* Copyright (c) 2023 NXP
*
Expand Down Expand Up @@ -293,10 +293,23 @@ static void stream_recv(struct bt_bap_stream *stream, const struct bt_iso_recv_i

static void stream_enabled(struct bt_bap_stream *stream)
{
const int err = bt_bap_stream_start(stream);
struct bt_bap_ep_info ep_info;
int err;

err = bt_bap_ep_get_info(stream->ep, &ep_info);
if (err != 0) {
printk("Failed to start stream %p: %d", stream, err);
printk("Failed to get ep info: %d\n", err);
return;
}

/* The unicast server is responsible for starting the sink streams */
if (ep_info.dir == BT_AUDIO_DIR_SINK) {
/* Automatically do the receiver start ready operation */
err = bt_bap_stream_start(stream);

if (err != 0) {
printk("Failed to start stream %p: %d", stream, err);
}
}
}

Expand Down
76 changes: 69 additions & 7 deletions samples/bluetooth/unicast_audio_client/src/main.c
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
/*
* Copyright (c) 2021-2023 Nordic Semiconductor ASA
* Copyright (c) 2021-2024 Nordic Semiconductor ASA
*
* SPDX-License-Identifier: Apache-2.0
*/
Expand Down Expand Up @@ -59,6 +59,7 @@ static K_SEM_DEFINE(sem_stream_configured, 0, 1);
static K_SEM_DEFINE(sem_stream_qos, 0, ARRAY_SIZE(sinks) + ARRAY_SIZE(sources));
static K_SEM_DEFINE(sem_stream_enabled, 0, 1);
static K_SEM_DEFINE(sem_stream_started, 0, 1);
static K_SEM_DEFINE(sem_stream_connected, 0, 1);

#define AUDIO_DATA_TIMEOUT_US 1000000UL /* Send data every 1 second */

Expand Down Expand Up @@ -518,9 +519,9 @@ static void stream_enabled(struct bt_bap_stream *stream)
k_sem_give(&sem_stream_enabled);
}

static void stream_started(struct bt_bap_stream *stream)
static void stream_connected_cb(struct bt_bap_stream *stream)
{
printk("Audio Stream %p started\n", stream);
printk("Audio Stream %p connected\n", stream);

/* Reset sequence number for sinks */
for (size_t i = 0U; i < configured_sink_stream_count; i++) {
Expand All @@ -530,6 +531,13 @@ static void stream_started(struct bt_bap_stream *stream)
}
}

k_sem_give(&sem_stream_connected);
}

static void stream_started(struct bt_bap_stream *stream)
{
printk("Audio Stream %p started\n", stream);

k_sem_give(&sem_stream_started);
}

Expand Down Expand Up @@ -576,7 +584,8 @@ static struct bt_bap_stream_ops stream_ops = {
.disabled = stream_disabled,
.stopped = stream_stopped,
.released = stream_released,
.recv = stream_recv
.recv = stream_recv,
.connected = stream_connected_cb,
};

static void add_remote_source(struct bt_bap_ep *ep)
Expand Down Expand Up @@ -1018,17 +1027,62 @@ static int enable_streams(void)
return 0;
}

static int start_streams(void)
static int connect_streams(void)
{
for (size_t i = 0U; i < configured_stream_count; i++) {
int err;

err = bt_bap_stream_start(&streams[i]);
if (err != 0) {
k_sem_reset(&sem_stream_connected);

err = bt_bap_stream_connect(&streams[i]);
if (err == -EALREADY) {
/* We have already connected a paired stream */
continue;
} else if (err != 0) {
printk("Unable to start stream: %d\n", err);
return err;
}

err = k_sem_take(&sem_stream_connected, K_FOREVER);
if (err != 0) {
printk("failed to take sem_stream_connected (err %d)\n", err);
return err;
}
}

return 0;
}

static enum bt_audio_dir stream_dir(const struct bt_bap_stream *stream)
{
struct bt_bap_ep_info ep_info;
int err;

err = bt_bap_ep_get_info(stream->ep, &ep_info);
if (err != 0) {
printk("Failed to get ep info for %p: %d\n", stream, err);
__ASSERT_NO_MSG(false);

return 0;
}

return ep_info.dir;
}

static int start_streams(void)
{
for (size_t i = 0U; i < configured_stream_count; i++) {
struct bt_bap_stream *stream = &streams[i];
int err;

if (stream_dir(stream) == BT_AUDIO_DIR_SOURCE) {
err = bt_bap_stream_start(&streams[i]);
if (err != 0) {
printk("Unable to start stream: %d\n", err);
return err;
}
} /* Sink streams are started by the unicast server */

err = k_sem_take(&sem_stream_started, K_FOREVER);
if (err != 0) {
printk("failed to take sem_stream_started (err %d)\n", err);
Expand All @@ -1051,6 +1105,7 @@ static void reset_data(void)
k_sem_reset(&sem_stream_qos);
k_sem_reset(&sem_stream_enabled);
k_sem_reset(&sem_stream_started);
k_sem_reset(&sem_stream_connected);

configured_sink_stream_count = 0;
configured_source_stream_count = 0;
Expand Down Expand Up @@ -1131,6 +1186,13 @@ int main(void)
}
printk("Streams enabled\n");

printk("Connecting streams\n");
err = connect_streams();
if (err != 0) {
return 0;
}
printk("Streams connected\n");

printk("Starting streams\n");
err = start_streams();
if (err != 0) {
Expand Down
Loading

0 comments on commit 1c26a48

Please sign in to comment.