bap: Remove PA idle timer logic

This removes BAP support for long-lived PA sync (added for Scan Delegator
support), since it is now handled inside the BASS plugin. This also
removes the PA idle timer logic, since PA/BIG sync requests are now
ordered inside the kernel.
This commit is contained in:
Iulia Tanasescu 2024-11-20 12:26:01 +02:00 committed by Luiz Augusto von Dentz
parent e618932b96
commit e59a915db9

View File

@ -65,15 +65,9 @@
#define MEDIA_ENDPOINT_INTERFACE "org.bluez.MediaEndpoint1"
#define MEDIA_INTERFACE "org.bluez.Media1"
/* Periodic advertisments are performed by an idle timer, which,
* at every tick, checks a queue for pending PA requests.
* When there is no pending requests, an item is popped from the
* queue, marked as pending and then it gets processed.
*/
#define PA_IDLE_TIMEOUT 2
struct bap_setup {
struct bap_ep *ep;
struct bap_data *data;
struct bt_bap_stream *stream;
struct bt_bap_qos qos;
int (*qos_parser)(struct bap_setup *setup, const char *key, int var,
@ -101,15 +95,9 @@ struct bap_ep {
struct queue *setups;
};
struct bap_adapter {
struct btd_adapter *adapter;
unsigned int pa_timer_id;
struct queue *bcast_pa_requests;
};
struct bap_data {
struct btd_device *device;
struct bap_adapter *adapter;
struct btd_adapter *adapter;
struct btd_service *service;
struct bt_bap *bap;
unsigned int ready_id;
@ -121,30 +109,12 @@ struct bap_data {
struct queue *bcast_snks;
struct queue *streams;
GIOChannel *listen_io;
unsigned int io_id;
int selecting;
void *user_data;
};
enum {
BAP_PA_SHORT_REQ = 0, /* Request for short PA sync */
BAP_PA_LONG_REQ, /* Request for long PA sync */
BAP_PA_BIG_SYNC_REQ, /* Request for PA Sync and BIG Sync */
};
struct bap_bcast_pa_req {
uint8_t type;
bool in_progress;
struct bap_data *bap_data;
union {
struct btd_service *service;
struct queue *setups;
} data;
unsigned int io_id; /* io_id for BIG Info watch */
GIOChannel *io;
};
static struct queue *sessions;
static struct queue *adapters;
/* Structure holding the parameters for Periodic Advertisement create sync.
* The full QOS is populated at the time the user selects and endpoint and
@ -219,6 +189,9 @@ static void bap_data_free(struct bap_data *data)
g_io_channel_unref(data->listen_io);
}
if (data->io_id)
g_source_remove(data->io_id);
if (data->service) {
btd_service_set_user_data(data->service, NULL);
bt_bap_set_user_data(data->bap, NULL);
@ -382,7 +355,7 @@ static gboolean get_device(const GDBusPropertyTable *property,
const char *path;
if (bt_bap_pac_get_type(ep->lpac) == BT_BAP_BCAST_SOURCE)
path = adapter_get_path(ep->data->adapter->adapter);
path = adapter_get_path(ep->data->adapter);
else
path = device_get_path(ep->data->device);
@ -996,12 +969,19 @@ static DBusMessage *set_configuration(DBusConnection *conn, DBusMessage *msg,
return NULL;
}
static bool stream_io_unset(const void *data, const void *user_data)
{
struct bt_bap_stream *stream = (struct bt_bap_stream *)data;
return !bt_bap_stream_get_io(stream);
}
static void iso_bcast_confirm_cb(GIOChannel *io, GError *err, void *user_data)
{
struct bap_bcast_pa_req *req = user_data;
struct bap_setup *setup;
struct bap_setup *setup = user_data;
struct bt_bap_stream *stream = setup->stream;
int fd;
struct bap_data *bap_data = req->bap_data;
struct bap_data *bap_data = setup->data;
DBG("BIG Sync completed");
@ -1009,28 +989,23 @@ static void iso_bcast_confirm_cb(GIOChannel *io, GError *err, void *user_data)
* to the order of the BISes that were enqueued before
* calling bt_io_bcast_accept.
*/
setup = queue_pop_head(req->data.setups);
if (queue_isempty(req->data.setups)) {
/* All fds have been notified. Mark service as connected. */
btd_service_connecting_complete(bap_data->service, 0);
if (req->io) {
g_io_channel_unref(req->io);
g_io_channel_shutdown(req->io, TRUE, NULL);
req->io = NULL;
}
queue_remove(bap_data->adapter->bcast_pa_requests, req);
queue_destroy(req->data.setups, NULL);
free(req);
}
if (bt_bap_stream_get_io(stream))
stream = queue_find(bt_bap_stream_io_get_links(stream),
stream_io_unset, NULL);
fd = g_io_channel_unix_get_fd(io);
if (bt_bap_stream_set_io(setup->stream, fd)) {
if (bt_bap_stream_set_io(stream, fd))
g_io_channel_set_close_on_unref(io, FALSE);
return;
if (!queue_find(bt_bap_stream_io_get_links(stream),
stream_io_unset, NULL)) {
/* All fds have been notified. Mark service as connected. */
btd_service_connecting_complete(bap_data->service, 0);
g_io_channel_unref(bap_data->listen_io);
g_io_channel_shutdown(bap_data->listen_io, TRUE, NULL);
bap_data->listen_io = NULL;
}
}
@ -1097,6 +1072,8 @@ static void create_stream_for_bis(struct bap_data *bap_data,
/* Create an internal copy for bcode */
setup->qos.bcast.bcode = util_iov_dup(qos->bcast.bcode, 1);
setup->data = bap_data;
queue_push_tail(bap_data->bcast_snks, setup);
/* Create and configure stream */
@ -1139,8 +1116,7 @@ static gboolean big_info_report_cb(GIOChannel *io, GIOCondition cond,
gpointer user_data)
{
GError *err = NULL;
struct bap_bcast_pa_req *req = user_data;
struct bap_data *data = btd_service_get_user_data(req->data.service);
struct bap_data *data = user_data;
struct bt_iso_base base;
struct bt_iso_qos qos;
struct iovec iov;
@ -1156,7 +1132,7 @@ static gboolean big_info_report_cb(GIOChannel *io, GIOCondition cond,
error("%s", err->message);
g_error_free(err);
g_io_channel_shutdown(io, TRUE, NULL);
req->io_id = 0;
data->io_id = 0;
return FALSE;
}
@ -1165,18 +1141,10 @@ static gboolean big_info_report_cb(GIOChannel *io, GIOCondition cond,
g_io_channel_unref(data->listen_io);
data->listen_io = NULL;
if (req->type == BAP_PA_LONG_REQ) {
/* If long-lived PA sync was requested, keep a reference
* to the PA sync io to keep the sync active.
*/
data->listen_io = io;
g_io_channel_ref(io);
} else {
/* For short-lived PA, the sync is no longer needed at
* this point, so the io can be closed.
*/
g_io_channel_shutdown(io, TRUE, NULL);
}
/* For short-lived PA, the sync is no longer needed at
* this point, so the io can be closed.
*/
g_io_channel_shutdown(io, TRUE, NULL);
/* Analyze received BASE data and create remote media endpoints for each
* BIS matching our capabilities
@ -1191,23 +1159,21 @@ static gboolean big_info_report_cb(GIOChannel *io, GIOCondition cond,
util_iov_free(bap_qos.bcast.bcode, 1);
service_set_connecting(req->data.service);
service_set_connecting(data->service);
queue_remove(data->adapter->bcast_pa_requests, req);
req->io_id = 0;
free(req);
data->io_id = 0;
return FALSE;
}
static void iso_pa_sync_confirm_cb(GIOChannel *io, void *user_data)
{
struct bap_bcast_pa_req *req = user_data;
struct bap_data *data = user_data;
/* PA Sync was established, wait for BIG Info report so that the
* encryption flag is also available.
*/
DBG("PA Sync done");
req->io_id = g_io_add_watch(io, G_IO_OUT, big_info_report_cb,
data->io_id = g_io_add_watch(io, G_IO_OUT, big_info_report_cb,
user_data);
}
@ -1262,7 +1228,7 @@ static struct bap_ep *ep_register_bcast(struct bap_data *data,
struct bt_bap_pac *lpac,
struct bt_bap_pac *rpac)
{
struct btd_adapter *adapter = data->adapter->adapter;
struct btd_adapter *adapter = data->adapter;
struct btd_device *device = data->device;
struct bap_ep *ep;
struct queue *queue;
@ -2109,95 +2075,13 @@ static void setup_listen_io(struct bap_data *data, struct bt_bap_stream *stream,
data->listen_io = io;
}
static void check_pa_req_in_progress(void *data, void *user_data)
{
struct bap_bcast_pa_req *req = data;
if (req->in_progress == TRUE)
*((bool *)user_data) = TRUE;
}
static int pa_sync(struct bap_bcast_pa_req *req);
static void pa_and_big_sync(struct bap_bcast_pa_req *req);
static gboolean pa_idle_timer(gpointer user_data)
{
struct bap_adapter *adapter = user_data;
struct bap_bcast_pa_req *req;
bool in_progress = FALSE;
/* Handle timer if no request is in progress */
queue_foreach(adapter->bcast_pa_requests, check_pa_req_in_progress,
&in_progress);
if (in_progress == FALSE) {
req = queue_peek_head(adapter->bcast_pa_requests);
if (req != NULL)
switch (req->type) {
case BAP_PA_SHORT_REQ:
DBG("do short lived PA Sync");
pa_sync(req);
break;
case BAP_PA_LONG_REQ:
DBG("do long lived PA Sync");
pa_sync(req);
break;
case BAP_PA_BIG_SYNC_REQ:
DBG("do PA Sync and BIG Sync");
pa_and_big_sync(req);
break;
}
else {
/* pa_req queue is empty, stop the timer by returning
* FALSE and set the pa_timer_id to 0. This will later
* be used to check if the timer is active.
*/
adapter->pa_timer_id = 0;
return FALSE;
}
}
return TRUE;
}
static int pa_sync(struct bap_data *data);
static void pa_and_big_sync(struct bap_setup *setup);
static void setup_accept_io_broadcast(struct bap_data *data,
struct bap_setup *setup)
{
struct bap_bcast_pa_req *req = new0(struct bap_bcast_pa_req, 1);
struct bap_adapter *adapter = data->adapter;
struct queue *links = bt_bap_stream_io_get_links(setup->stream);
const struct queue_entry *entry;
/* Timer could be stopped if all other requests were treated.
* Check the state of the timer and turn it on so that this request
* can also be treated.
*/
if (adapter->pa_timer_id == 0)
adapter->pa_timer_id = g_timeout_add_seconds(PA_IDLE_TIMEOUT,
pa_idle_timer,
adapter);
/* Add this request to the PA queue.
* We don't need to check the queue here, as we cannot have
* BAP_PA_BIG_SYNC_REQ before a short PA (BAP_PA_SHORT_REQ)
*/
req->type = BAP_PA_BIG_SYNC_REQ;
req->in_progress = FALSE;
req->bap_data = data;
req->data.setups = queue_new();
/* Enqueue all linked setups to the request */
queue_push_tail(req->data.setups, setup);
for (entry = queue_get_entries(links); entry;
entry = entry->next) {
struct bt_bap_stream *stream = entry->data;
queue_push_tail(req->data.setups,
bap_find_setup_by_stream(data, stream));
}
queue_push_tail(adapter->bcast_pa_requests, req);
pa_and_big_sync(setup);
}
static void setup_create_ucast_io(struct bap_data *data,
@ -2952,10 +2836,8 @@ static void bap_detached(struct bt_bap *bap, void *user_data)
bap_data_remove(data);
}
static int pa_sync(struct bap_bcast_pa_req *req)
static int pa_sync(struct bap_data *data)
{
struct btd_service *service = req->data.service;
struct bap_data *data = btd_service_get_user_data(service);
GError *err = NULL;
if (data->listen_io) {
@ -2964,13 +2846,13 @@ static int pa_sync(struct bap_bcast_pa_req *req)
}
DBG("Create PA sync with this source");
req->in_progress = TRUE;
data->listen_io = bt_io_listen(NULL, iso_pa_sync_confirm_cb, req,
data->listen_io = bt_io_listen(NULL, iso_pa_sync_confirm_cb, data,
NULL, &err,
BT_IO_OPT_SOURCE_BDADDR,
btd_adapter_get_address(data->adapter->adapter),
btd_adapter_get_address(data->adapter),
BT_IO_OPT_SOURCE_TYPE,
btd_adapter_get_address_type(data->adapter->adapter),
btd_adapter_get_address_type(data->adapter),
BT_IO_OPT_DEST_BDADDR,
device_get_address(data->device),
BT_IO_OPT_DEST_TYPE,
@ -2988,9 +2870,9 @@ static int pa_sync(struct bap_bcast_pa_req *req)
static void append_setup(void *data, void *user_data)
{
struct bap_setup *setup = data;
struct bt_bap_stream *stream = data;
struct sockaddr_iso_bc *addr = user_data;
char *path = bt_bap_stream_get_user_data(setup->stream);
char *path = bt_bap_stream_get_user_data(stream);
int bis = 1;
int s_err;
const char *strbis = NULL;
@ -3015,39 +2897,36 @@ static void append_setup(void *data, void *user_data)
static void setup_refresh_qos(void *data, void *user_data)
{
struct bap_setup *setup = data;
struct bt_bap_stream *stream = data;
struct bap_data *bap_data = user_data;
struct bap_setup *setup = bap_find_setup_by_stream(bap_data, stream);
setup->qos = *bt_bap_stream_get_qos(setup->stream);
setup->qos = *bt_bap_stream_get_qos(stream);
}
static void iso_do_big_sync(GIOChannel *io, void *user_data)
{
GError *err = NULL;
struct bap_bcast_pa_req *req = user_data;
struct queue *setups = req->data.setups;
struct bap_setup *setup = queue_peek_head(setups);
struct bap_data *data = req->bap_data;
struct bap_setup *setup = user_data;
struct bap_data *data = setup->data;
struct sockaddr_iso_bc iso_bc_addr = {0};
struct bt_iso_qos qos;
struct queue *links = bt_bap_stream_io_get_links(setup->stream);
DBG("PA Sync done");
if (req->io) {
g_io_channel_unref(req->io);
g_io_channel_shutdown(req->io, TRUE, NULL);
req->io = io;
g_io_channel_ref(req->io);
}
iso_bc_addr.bc_bdaddr_type = btd_device_get_bdaddr_type(data->device);
memcpy(&iso_bc_addr.bc_bdaddr, device_get_address(data->device),
sizeof(bdaddr_t));
g_io_channel_unref(data->listen_io);
g_io_channel_shutdown(data->listen_io, TRUE, NULL);
data->listen_io = io;
g_io_channel_ref(data->listen_io);
/* Append each linked BIS to the BIG sync request */
queue_foreach(setups, append_setup, &iso_bc_addr);
append_setup(setup->stream, &iso_bc_addr);
queue_foreach(links, append_setup, &iso_bc_addr);
/* Refresh qos stored in setups */
queue_foreach(setups, setup_refresh_qos, NULL);
setup->qos = *bt_bap_stream_get_qos(setup->stream);
queue_foreach(links, setup_refresh_qos, data);
/* Set the user requested QOS */
bap_qos_to_iso_qos(&setup->qos, &qos);
@ -3061,7 +2940,7 @@ static void iso_do_big_sync(GIOChannel *io, void *user_data)
if (!bt_io_bcast_accept(io,
iso_bcast_confirm_cb,
req, NULL, &err,
setup, NULL, &err,
BT_IO_OPT_ISO_BC_NUM_BIS,
iso_bc_addr.bc_num_bis, BT_IO_OPT_ISO_BC_BIS,
iso_bc_addr.bc_bis, BT_IO_OPT_INVALID)) {
@ -3070,28 +2949,16 @@ static void iso_do_big_sync(GIOChannel *io, void *user_data)
}
}
static void pa_and_big_sync(struct bap_bcast_pa_req *req)
static void pa_and_big_sync(struct bap_setup *setup)
{
GError *err = NULL;
struct bap_data *bap_data = req->bap_data;
req->in_progress = TRUE;
if (bap_data->listen_io) {
/* If there is an active listen io for the BAP session
* with the Broadcast Source, it means that PA sync is
* already established. Go straight to establishing BIG
* sync.
*/
iso_do_big_sync(bap_data->listen_io, req);
return;
}
struct bap_data *bap_data = setup->data;
DBG("Create PA sync with this source");
req->io = bt_io_listen(NULL, iso_do_big_sync, req,
bap_data->listen_io = bt_io_listen(NULL, iso_do_big_sync, setup,
NULL, &err,
BT_IO_OPT_SOURCE_BDADDR,
btd_adapter_get_address(bap_data->adapter->adapter),
btd_adapter_get_address(bap_data->adapter),
BT_IO_OPT_DEST_BDADDR,
device_get_address(bap_data->device),
BT_IO_OPT_DEST_TYPE,
@ -3099,26 +2966,17 @@ static void pa_and_big_sync(struct bap_bcast_pa_req *req)
BT_IO_OPT_MODE, BT_IO_MODE_ISO,
BT_IO_OPT_QOS, &bap_sink_pa_qos,
BT_IO_OPT_INVALID);
if (!req->io) {
if (!bap_data->listen_io) {
error("%s", err->message);
g_error_free(err);
}
}
static bool match_bap_adapter(const void *data, const void *match_data)
{
struct bap_adapter *adapter = (struct bap_adapter *)data;
return adapter->adapter == match_data;
}
static int bap_bcast_probe(struct btd_service *service)
{
struct btd_device *device = btd_service_get_device(service);
struct btd_adapter *adapter = device_get_adapter(device);
struct btd_gatt_database *database = btd_adapter_get_database(adapter);
struct bap_bcast_pa_req *req;
uint8_t type = BAP_PA_LONG_REQ;
struct bap_data *data;
int ret = 0;
@ -3127,27 +2985,10 @@ static int bap_bcast_probe(struct btd_service *service)
return -ENOTSUP;
}
data = queue_find(sessions, match_device, device);
if (data && data->service) {
error("Profile probed twice for the same device!");
return -EINVAL;
}
if (!data) {
data = bap_data_new(device);
data->device = device;
bap_data_add(data);
/* The Broadcaster was scanned autonomously,
* so it should be probed short-lived.
*/
type = BAP_PA_SHORT_REQ;
}
data = bap_data_new(device);
data->service = service;
btd_service_set_user_data(service, data);
data->adapter = queue_find(adapters, match_bap_adapter, adapter);
data->adapter = adapter;
data->device = device;
data->bap = bt_bap_new(btd_gatt_database_get_db(database),
btd_gatt_database_get_db(database));
if (!data->bap) {
@ -3155,9 +2996,6 @@ static int bap_bcast_probe(struct btd_service *service)
free(data);
return -EINVAL;
}
bt_bap_set_debug(data->bap, bap_debug, NULL, NULL);
data->bcast_snks = queue_new();
if (!bt_bap_attach(data->bap, NULL)) {
@ -3165,6 +3003,8 @@ static int bap_bcast_probe(struct btd_service *service)
return -EINVAL;
}
bap_data_add(data);
data->ready_id = bt_bap_ready_register(data->bap, bap_ready, service,
NULL);
data->state_id = bt_bap_state_register(data->bap, bap_state_bcast_sink,
@ -3178,36 +3018,15 @@ static int bap_bcast_probe(struct btd_service *service)
/* Return if probed device was handled inside BASS. */
return ret;
/* Start the PA timer if it hasn't been started yet */
if (data->adapter->pa_timer_id == 0)
data->adapter->pa_timer_id = g_timeout_add_seconds(
PA_IDLE_TIMEOUT,
pa_idle_timer,
data->adapter);
/* Enqueue this device advertisement so that we can create PA sync. */
DBG("enqueue service: %p", service);
req = new0(struct bap_bcast_pa_req, 1);
req->type = type;
req->in_progress = FALSE;
req->data.service = service;
queue_push_tail(data->adapter->bcast_pa_requests, req);
pa_sync(data);
return 0;
}
static bool match_service(const void *data, const void *match_data)
{
struct bap_bcast_pa_req *req = (struct bap_bcast_pa_req *)data;
return req->data.service == match_data;
}
static void bap_bcast_remove(struct btd_service *service)
{
struct btd_device *device = btd_service_get_device(service);
struct bap_data *data;
struct bap_bcast_pa_req *req;
char addr[18];
ba2str(device_get_address(device), addr);
@ -3218,17 +3037,6 @@ static void bap_bcast_remove(struct btd_service *service)
error("BAP service not handled by profile");
return;
}
/* Remove the corresponding entry from the pa_req queue. Any pa_req that
* are in progress will be stopped by bap_data_remove which calls
* bap_data_free.
*/
req = queue_remove_if(data->adapter->bcast_pa_requests,
match_service, service);
if (req && req->io_id) {
g_source_remove(req->io_id);
req->io_id = 0;
}
free(req);
/* Notify the BASS plugin about the removed session. */
bass_bcast_remove(device);
@ -3370,13 +3178,7 @@ static int bap_adapter_probe(struct btd_profile *p, struct btd_adapter *adapter)
bt_bap_set_user_data(data->bap, adapter);
bap_data_set_user_data(data, adapter);
data->adapter = new0(struct bap_adapter, 1);
data->adapter->adapter = adapter;
if (adapters == NULL)
adapters = queue_new();
data->adapter->bcast_pa_requests = queue_new();
queue_push_tail(adapters, data->adapter);
data->adapter = adapter;
return 0;
}
@ -3391,15 +3193,6 @@ static void bap_adapter_remove(struct btd_profile *p,
ba2str(btd_adapter_get_address(adapter), addr);
DBG("%s", addr);
queue_destroy(data->adapter->bcast_pa_requests, free);
queue_remove(adapters, data->adapter);
free(data->adapter);
if (queue_isempty(adapters)) {
queue_destroy(adapters, NULL);
adapters = NULL;
}
if (!data) {
error("BAP service not handled by profile");
return;