Re: [PATCH BlueZ 3/3] bap: Do PA Sync for each BAP Broadcast source discovered

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

 



Hi Andrei,

On Thu, Feb 8, 2024 at 6:25 AM Andrei Istodorescu
<andrei.istodorescu@xxxxxxx> wrote:
>
> Hi Luiz,
>
> > -----Original Message-----
> > From: Luiz Augusto von Dentz <luiz.dentz@xxxxxxxxx>
> > Sent: Wednesday, February 7, 2024 4:42 PM
> > To: Andrei Istodorescu <andrei.istodorescu@xxxxxxx>
> > Cc: linux-bluetooth@xxxxxxxxxxxxxxx; Mihai-Octavian Urzica <mihai-
> > octavian.urzica@xxxxxxx>; Silviu Florian Barbulescu
> > <silviu.barbulescu@xxxxxxx>; Vlad Pruteanu <vlad.pruteanu@xxxxxxx>;
> > Iulia Tanasescu <iulia.tanasescu@xxxxxxx>
> > Subject: [EXT] Re: [PATCH BlueZ 3/3] bap: Do PA Sync for each BAP Broadcast
> > source discovered
> >
> > Caution: This is an external email. Please take care when clicking links or
> > opening attachments. When in doubt, report the message using the 'Report
> > this email' button
> >
> >
> > Hi Andrei,
> >
> > On Wed, Feb 7, 2024 at 7:23 AM Andrei Istodorescu
> > <andrei.istodorescu@xxxxxxx> wrote:
> > >
> > > After discovering a BAP Broadcast Source do a short PA sync first to
> > > learn the BASE. After discovering the BASE check how many BISes are
> > > matching the sink capabilities and create endpoints for them. Allow
> > > user to configure one endpoint using "SetConfiguration" causing BIG
> > > synchronization to the corresponding BIS; also this results in
> > > creating a stream and the corresponding transport.
> > > ---
> > >  profiles/audio/bap.c | 324
> > > +++++++++++++++----------------------------
> > >  1 file changed, 110 insertions(+), 214 deletions(-)
> > >
> > > diff --git a/profiles/audio/bap.c b/profiles/audio/bap.c index
> > > 88c93127bea0..8646eae2ed20 100644
> > > --- a/profiles/audio/bap.c
> > > +++ b/profiles/audio/bap.c
> > > @@ -105,6 +105,44 @@ struct bap_data {
> > >         void *user_data;
> > >  };
> > >
> > > +/* Structure holding the parameters for periodic train and BIG
> > > + * synchronization
> > > + */
> > > +static struct bt_iso_qos bap_sink_sync_parameters = {
> > > +       .bcast = {
> > > +               .big                    = BT_ISO_QOS_BIG_UNSET,
> > > +               .bis                    = BT_ISO_QOS_BIS_UNSET,
> > > +               /* HCI_LE_Periodic_Advertising_Create_Sync */
> > > +               .options                = 0x00,
> > > +               .skip                   = 0x0000,
> > > +               .sync_timeout   = 0x4000,
> > > +               .sync_cte_type  = 0x00,
> > > +               /* HCI_LE_BIG_Create_Sync */
> > > +               .encryption             = 0x00,
> > > +               .bcode                  = {0x00},
> > > +               .mse                    = 0x00,
> > > +               .timeout                = 0x4000,
> > > +               /* to remove from kernel check */
> > > +               .sync_factor    = 0x07,
> > > +               .packing                = 0x00,
> > > +               .framing                = 0x00,
> > > +               .in = {
> > > +                       .interval       = 10000,
> > > +                       .latency        = 10,
> > > +                       .sdu            = 40,
> > > +                       .phy            = 0x02,
> > > +                       .rtn            = 2,
> > > +               },
> > > +               .out = {
> > > +                       .interval       = 10000,
> > > +                       .latency        = 10,
> > > +                       .sdu            = 40,
> > > +                       .phy            = 0x02,
> > > +                       .rtn            = 2,
> > > +               }
> > > +       }
> > > +};
> >
> > This cannot be global, it needs to be stored on a per device basis so it doesn't
> > get overwritten.
>
> I will submit an update for this.
>
> >
> > >  static struct queue *sessions;
> > >
> > >  static bool bap_data_set_user_data(struct bap_data *data, void
> > > *user_data) @@ -422,113 +460,6 @@ static int
> > parse_array(DBusMessageIter *iter, struct iovec *iov)
> > >         return 0;
> > >  }
> > >
> > > -static bool parse_base(void *data, size_t len, util_debug_func_t func,
> > > -               uint32_t *presDelay, uint8_t *numSubgroups, uint8_t *numBis,
> > > -               struct bt_bap_codec *codec, struct iovec **caps,
> > > -               struct iovec **meta)
> > > -{
> > > -       struct iovec iov = {
> > > -               .iov_base = data,
> > > -               .iov_len = len,
> > > -       };
> > > -
> > > -       uint8_t capsLen, metaLen;
> > > -       struct iovec cc;
> > > -       struct iovec metadata;
> > > -
> > > -       if (presDelay) {
> > > -               if (!util_iov_pull_le24(&iov, presDelay))
> > > -                       return false;
> > > -               util_debug(func, NULL, "PresentationDelay %d", *presDelay);
> > > -       }
> > > -
> > > -       if (numSubgroups) {
> > > -               if (!util_iov_pull_u8(&iov, numSubgroups))
> > > -                       return false;
> > > -               util_debug(func, NULL, "NumSubgroups %d", *numSubgroups);
> > > -       }
> > > -
> > > -       if (numBis) {
> > > -               if (!util_iov_pull_u8(&iov, numBis))
> > > -                       return false;
> > > -               util_debug(func, NULL, "NumBis %d", *numBis);
> > > -       }
> > > -
> > > -       if (codec) {
> > > -               codec = util_iov_pull_mem(&iov, sizeof(*codec));
> > > -               if (!codec)
> > > -                       return false;
> > > -               util_debug(func, NULL, "%s: ID %d CID 0x%2.2x VID 0x%2.2x",
> > > -                               "Codec", codec->id, codec->cid, codec->vid);
> > > -       }
> > > -
> > > -       if (!util_iov_pull_u8(&iov, &capsLen))
> > > -               return false;
> > > -       util_debug(func, NULL, "CC Len %d", capsLen);
> > > -
> > > -       if (!capsLen)
> > > -               return false;
> > > -
> > > -       cc.iov_len = capsLen;
> > > -       cc.iov_base = util_iov_pull_mem(&iov, capsLen);
> > > -       if (!cc.iov_base)
> > > -               return false;
> > > -
> > > -       if (caps) {
> > > -               if (*caps)
> > > -                       util_iov_free(*caps, 1);
> > > -
> > > -               *caps = util_iov_dup(&cc, 1);
> > > -       }
> > > -
> > > -       for (int i = 0; capsLen > 1; i++) {
> > > -               struct bt_ltv *ltv = util_iov_pull_mem(&cc, sizeof(*ltv));
> > > -               uint8_t *caps;
> > > -
> > > -               if (!ltv) {
> > > -                       util_debug(func, NULL, "Unable to parse %s",
> > > -                                                               "Capabilities");
> > > -                       return false;
> > > -               }
> > > -
> > > -               util_debug(func, NULL, "%s #%u: len %u type %u",
> > > -                                       "CC", i, ltv->len, ltv->type);
> > > -
> > > -               caps = util_iov_pull_mem(&cc, ltv->len - 1);
> > > -               if (!caps) {
> > > -                       util_debug(func, NULL, "Unable to parse %s",
> > > -                                                               "CC");
> > > -                       return false;
> > > -               }
> > > -               util_hexdump(' ', caps, ltv->len - 1, func, NULL);
> > > -
> > > -               capsLen -= (ltv->len + 1);
> > > -       }
> > > -
> > > -       if (!util_iov_pull_u8(&iov, &metaLen))
> > > -               return false;
> > > -       util_debug(func, NULL, "Metadata Len %d", metaLen);
> > > -
> > > -       if (!metaLen)
> > > -               return false;
> > > -
> > > -       metadata.iov_len = metaLen;
> > > -       metadata.iov_base = util_iov_pull_mem(&iov, metaLen);
> > > -       if (!metadata.iov_base)
> > > -               return false;
> > > -
> > > -       if (meta) {
> > > -               if (*meta)
> > > -                       util_iov_free(*meta, 1);
> > > -
> > > -               *meta = util_iov_dup(&metadata, 1);
> > > -       }
> > > -
> > > -       util_hexdump(' ', metadata.iov_base, metaLen, func, NULL);
> > > -
> > > -       return true;
> > > -}
> > > -
> > >  static int parse_io_qos(const char *key, int var, DBusMessageIter *iter,
> > >                                 struct bt_bap_io_qos *qos)  { @@
> > > -954,6 +885,17 @@ static DBusMessage
> > *set_configuration(DBusConnection *conn, DBusMessage *msg,
> > >                 return btd_error_invalid_args(msg);
> > >         }
> > >
> > > +       /* For BAP Broadcast Sink, the capabilities and metadata are coming
> > > +        * from the source's BIS, which are present in the remote PAC
> > > +        */
> > > +       if (bt_bap_pac_get_type(ep->lpac) == BT_BAP_BCAST_SINK) {
> > > +               util_iov_free(setup->caps, 1);
> > > +               setup->caps = util_iov_dup(bt_bap_pac_get_data(ep->rpac), 1);
> > > +               util_iov_free(setup->metadata, 1);
> > > +               setup->metadata = util_iov_dup(
> > > +                               bt_bap_pac_get_metadata(ep->rpac), 1);
> > > +       }
> > > +
> > >         setup->stream = bt_bap_stream_new(ep->data->bap, ep->lpac, ep-
> > >rpac,
> > >                                                 &setup->qos,
> > > setup->caps);
> > >
> > > @@ -977,95 +919,27 @@ static DBusMessage
> > *set_configuration(DBusConnection *conn, DBusMessage *msg,
> > >                 break;
> > >         case BT_BAP_STREAM_TYPE_BCAST:
> > >                 /* No message sent over the air for broadcast */
> > > -               if (bt_bap_pac_get_type(ep->lpac) == BT_BAP_BCAST_SINK)
> > > -                       setup->msg = dbus_message_ref(msg);
> > > -               else {
> > > +               if (bt_bap_pac_get_type(ep->lpac) ==
> > > + BT_BAP_BCAST_SOURCE)
> > >                         setup->base = bt_bap_stream_get_base(setup->stream);
> > > -                       setup->id = 0;
> > >                 }
> > > +               setup->id = 0;
> > >
> > >                 if (ep->data->service)
> > >                         service_set_connecting(ep->data->service);
> > >
> > >                 return g_dbus_create_reply(msg, DBUS_TYPE_INVALID);
> > > -       }
> > >
> > >         return NULL;
> > >  }
> > >
> > > -static void update_bcast_qos(struct bt_iso_qos *qos,
> > > -                       struct bt_bap_qos *bap_qos)
> > > -{
> > > -       bap_qos->bcast.big = qos->bcast.big;
> > > -       bap_qos->bcast.bis = qos->bcast.bis;
> > > -       bap_qos->bcast.sync_factor = qos->bcast.sync_factor;
> > > -       bap_qos->bcast.packing = qos->bcast.packing;
> > > -       bap_qos->bcast.framing = qos->bcast.framing;
> > > -       bap_qos->bcast.encryption = qos->bcast.encryption;
> > > -       bap_qos->bcast.options = qos->bcast.options;
> > > -       bap_qos->bcast.skip = qos->bcast.skip;
> > > -       bap_qos->bcast.sync_timeout = qos->bcast.sync_timeout;
> > > -       bap_qos->bcast.sync_cte_type = qos->bcast.sync_cte_type;
> > > -       bap_qos->bcast.mse = qos->bcast.mse;
> > > -       bap_qos->bcast.timeout = qos->bcast.timeout;
> > > -       bap_qos->bcast.io_qos.interval = qos->bcast.in.interval;
> > > -       bap_qos->bcast.io_qos.latency = qos->bcast.in.latency;
> > > -       bap_qos->bcast.io_qos.phy = qos->bcast.in.phy;
> > > -       bap_qos->bcast.io_qos.sdu = qos->bcast.in.sdu;
> > > -       bap_qos->bcast.io_qos.rtn = qos->bcast.in.rtn;
> > > -       if (!bap_qos->bcast.bcode)
> > > -               bap_qos->bcast.bcode = new0(struct iovec, 1);
> > > -       util_iov_memcpy(bap_qos->bcast.bcode, qos->bcast.bcode,
> > > -                                               sizeof(qos->bcast.bcode));
> > > -}
> > > -
> > >  static void iso_bcast_confirm_cb(GIOChannel *io, GError *err, void
> > > *user_data)  {
> > >         struct bap_setup *setup = user_data;
> > > -       struct bap_ep *ep = setup->ep;
> > > -       struct bap_data *data = ep->data;
> > > -       struct bt_iso_qos qos;
> > > -       struct bt_iso_base base;
> > > -       char address[18];
> > >         int fd;
> > > -       struct iovec *base_io;
> > > -       uint32_t presDelay;
> > > -       uint8_t numSubgroups;
> > > -       uint8_t numBis;
> > > -       struct bt_bap_codec codec;
> > > -
> > > -       bt_io_get(io, &err,
> > > -                       BT_IO_OPT_DEST, address,
> > > -                       BT_IO_OPT_QOS, &qos,
> > > -                       BT_IO_OPT_BASE, &base,
> > > -                       BT_IO_OPT_INVALID);
> > > -       if (err) {
> > > -               error("%s", err->message);
> > > -               g_error_free(err);
> > > -               goto drop;
> > > -       }
> > >
> > > -       g_io_channel_ref(io);
> > > -       btd_service_connecting_complete(data->service, 0);
> > > -       DBG("BCAST ISO: sync with %s (BIG 0x%02x BIS 0x%02x)",
> > > -                                       address, qos.bcast.big, qos.bcast.bis);
> > > -
> > > -       update_bcast_qos(&qos, &setup->qos);
> > > -
> > > -       base_io = new0(struct iovec, 1);
> > > -       util_iov_memcpy(base_io, base.base, base.base_len);
> > > -
> > > -       parse_base(base_io->iov_base, base_io->iov_len, bap_debug,
> > > -                       &presDelay, &numSubgroups, &numBis,
> > > -                       &codec, &setup->caps, &setup->metadata);
> > > -
> > > -       /* Update pac with BASE information */
> > > -       bt_bap_update_bcast_source(ep->rpac, &codec, setup->caps,
> > > -                                       setup->metadata);
> > > -       setup->id = bt_bap_stream_config(setup->stream, &setup->qos,
> > > -                                       setup->caps, NULL, NULL);
> > > -
> > > -       bt_bap_stream_set_user_data(setup->stream, ep->path);
> > > +       /* listen channel is not needed anymore */
> > > +       g_io_channel_unref(setup->io);
> > > +       setup->io = NULL;
> > >
> > >         fd = g_io_channel_unix_get_fd(io);
> > >
> > > @@ -1074,26 +948,43 @@ static void iso_bcast_confirm_cb(GIOChannel
> > *io, GError *err, void *user_data)
> > >                 g_io_channel_set_close_on_unref(io, FALSE);
> > >                 return;
> > >         }
> > > -
> > > -
> > > -       return;
> > > -
> > > -drop:
> > > -       g_io_channel_shutdown(io, TRUE, NULL);
> > > -
> > >  }
> > >
> > >  static void iso_pa_sync_confirm_cb(GIOChannel *io, void *user_data)
> > > {
> > >         GError *err = NULL;
> > > +       struct bap_data *data = user_data;
> > > +       struct bt_iso_base base;
> > > +       struct bt_bap_base base_s;
> > > +       struct bt_iso_qos qos;
> > >
> > > -       if (!bt_io_bcast_accept(io, iso_bcast_confirm_cb,
> > > -                               user_data, NULL, &err, BT_IO_OPT_INVALID)) {
> > > -               error("bt_io_bcast_accept: %s", err->message);
> > > +       btd_service_connecting_complete(data->service, 0);
> > > +
> > > +       bt_io_get(io, &err,
> > > +                       BT_IO_OPT_BASE, &base,
> > > +                       BT_IO_OPT_QOS, &qos,
> > > +                       BT_IO_OPT_INVALID);
> > > +       if (err) {
> > > +               error("%s", err->message);
> > >                 g_error_free(err);
> > >                 g_io_channel_shutdown(io, TRUE, NULL);
> > > +               return;
> > >         }
> > >
> > > +       /* The PA Sync channel becomes the new listen_io.
> > > +        * It will be later used to listen for a BIS io.
> > > +        */
> > > +       g_io_channel_unref(data->listen_io);
> > > +       data->listen_io = io;
> > > +       g_io_channel_ref(io);
> > > +
> > > +       /* Analyze received BASE data and create remote media endpoints for
> > each
> > > +        * matching BIS
> > > +        */
> > > +       base_s.subgroups = queue_new();
> > > +       bt_bap_parse_base(data->bap, base.base, base.base_len,
> > bap_debug,
> > > +                       &base_s);
> > > +       queue_foreach(base_s.subgroups, bt_bap_parse_bis, NULL);
> > >  }
> > >
> > >  static bool match_data_bap_data(const void *data, const void
> > > *match_data) @@ -1934,12 +1825,11 @@ static void setup_listen_io(struct
> > bap_data *data, struct bt_bap_stream *stream,
> > >         data->listen_io = io;
> > >  }
> > >
> > > -static void setup_listen_io_broadcast(struct bap_data *data,
> > > +static void setup_accept_io_broadcast(struct bap_data *data,
> > >                                         struct bap_setup *setup,
> > >                                         struct bt_bap_stream *stream,
> > >                                         struct bt_iso_qos *qos)  {
> > > -       GIOChannel *io;
> > >         GError *err = NULL;
> > >         struct sockaddr_iso_bc iso_bc_addr;
> > >
> > > @@ -1951,29 +1841,18 @@ static void setup_listen_io_broadcast(struct
> > > bap_data *data,
> > >
> > >         DBG("stream %p", stream);
> > >
> > > -       /* If IO already set skip creating it again */
> > > -       if (bt_bap_stream_get_io(stream) || data->listen_io)
> > > -               return;
> > > -
> > > -       io = bt_io_listen(NULL, iso_pa_sync_confirm_cb, setup, NULL, &err,
> > > -                       BT_IO_OPT_SOURCE_BDADDR,
> > > -                       btd_adapter_get_address(data->adapter),
> > > -                       BT_IO_OPT_DEST_BDADDR,
> > > -                       device_get_address(data->device),
> > > -                       BT_IO_OPT_DEST_TYPE,
> > > -                       btd_device_get_bdaddr_type(data->device),
> > > -                       BT_IO_OPT_MODE, BT_IO_MODE_ISO,
> > > -                       BT_IO_OPT_QOS, &qos->bcast,
> > > -                       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);
> > > -       if (!io) {
> > > -               error("%s", err->message);
> > > +       if (!bt_io_bcast_accept(data->listen_io,
> > > +                       iso_bcast_confirm_cb,
> > > +                       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)) {
> > > +               error("bt_io_bcast_accept: %s", err->message);
> > >                 g_error_free(err);
> > >         }
> > > -       setup->io = io;
> > > -       data->listen_io = io;
> > >
> > > +       setup->io = data->listen_io;
> > > +       data->listen_io = NULL;
> > >  }
> > >  static void setup_create_ucast_io(struct bap_data *data,
> > >                                         struct bap_setup *setup, @@
> > > -2037,7 +1916,7 @@ done:
> > >         if (bt_bap_pac_get_type(setup->ep->lpac) ==
> > BT_BAP_BCAST_SOURCE)
> > >                 setup_connect_io_broadcast(data, setup, stream, &iso_qos);
> > >         else
> > > -               setup_listen_io_broadcast(data, setup, stream, &iso_qos);
> > > +               setup_accept_io_broadcast(data, setup, stream,
> > > + &iso_qos);
> > >  }
> > >
> > >  static void setup_create_io(struct bap_data *data, struct bap_setup
> > > *setup, @@ -2422,6 +2301,7 @@ static int bap_bcast_probe(struct
> > btd_service *service)
> > >         struct btd_gatt_database *database =
> > btd_adapter_get_database(adapter);
> > >         struct bap_data *data = btd_service_get_user_data(service);
> > >         char addr[18];
> > > +       GError *err = NULL;
> > >
> > >         ba2str(device_get_address(device), addr);
> > >
> > > @@ -2465,7 +2345,23 @@ static int bap_bcast_probe(struct btd_service
> > > *service)
> > >
> > >         bt_bap_set_user_data(data->bap, service);
> > >
> > > -       bt_bap_new_bcast_source(data->bap, device_get_path(device));
> > > +       DBG("Create PA sync with this source");
> > > +       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),
> > > +                       BT_IO_OPT_DEST_BDADDR,
> > > +                       device_get_address(data->device),
> > > +                       BT_IO_OPT_DEST_TYPE,
> > > +                       btd_device_get_bdaddr_type(data->device),
> > > +                       BT_IO_OPT_MODE, BT_IO_MODE_ISO,
> > > +                       BT_IO_OPT_QOS, &bap_sink_sync_parameters,
> > > +                       BT_IO_OPT_INVALID);
> > > +       if (!data->listen_io) {
> > > +               error("%s", err->message);
> > > +               g_error_free(err);
> > > +       }
> >
> > I really doubt this will work in a crowded environment, it seems we would be
> > doing several PA Sync in parallel, one for each announcement found, which
> > not only would overwrite the QOS but also I don't think controller are capable
> > of doing multiple PA Sync like that so we might need to serialize the process
> > of doing short lived PA Syncs to enumerate the BASE.
> >
> > Usually we have dealt with the serialization using an idle timer which can
> > then check services that need to be resolved, once a service is being
> > resolved then the timer shall be stopped, we restart the time everytime
> > something needs to be resolved.
> >
>
> bap_bcast_probe starts a PA Sync controller procedure for each new Broadcast
> source seen.
> This can take several milliseconds with physical controller and runs in an instant
> with the emulator. The problem that can arrive is that another Broadcast source
> gets probed and another bt_io_listen is executed before the previous one is
> completed. In this scenario the controller will return an error for no resources
> and the second PA sync will fail, but the one in progress will complete successfully.
> Given the timings for this to happen, can I submit a different patch for this
> issue, or shall I fix it in this one?

Yes, you can choose to fix it later by adding a TODO comment, that
said I'd fix the QoS being a static global.

> > > +
> > >         return 0;
> > >  }
> > >
> > > --
> > > 2.40.1
> > >
> >
> >
> > --
> > Luiz Augusto von Dentz



-- 
Luiz Augusto von Dentz





[Index of Archives]     [Bluez Devel]     [Linux Wireless Networking]     [Linux Wireless Personal Area Networking]     [Linux ATH6KL]     [Linux USB Devel]     [Linux Media Drivers]     [Linux Audio Users]     [Linux Kernel]     [Linux SCSI]     [Big List of Linux Books]

  Powered by Linux