diff mbox series

[BlueZ,2/2] shared/bass: Add initial code for handling BASS

Message ID 20230424152457.28000-3-iulia.tanasescu@nxp.com
State New
Headers show
Series Add initial code for handling BASS | expand

Commit Message

Iulia Tanasescu April 24, 2023, 3:24 p.m. UTC
This adds initial code for Broadcast Audio Scan Service.

---
 Makefile.am            |   1 +
 src/shared/att-types.h |   4 +-
 src/shared/bap.c       |  57 +---
 src/shared/bap.h       |  51 +++
 src/shared/bass.c      | 703 +++++++++++++++++++++++++++++++++++++++++
 src/shared/bass.h      | 124 ++++++++
 6 files changed, 896 insertions(+), 44 deletions(-)
 create mode 100644 src/shared/bass.c
 create mode 100644 src/shared/bass.h

Comments

Luiz Augusto von Dentz April 25, 2023, 9:25 p.m. UTC | #1
Hi Iulia,

On Mon, Apr 24, 2023 at 8:32 AM Iulia Tanasescu <iulia.tanasescu@nxp.com> wrote:
>
> This adds initial code for Broadcast Audio Scan Service.
>
> ---
>  Makefile.am            |   1 +
>  src/shared/att-types.h |   4 +-
>  src/shared/bap.c       |  57 +---
>  src/shared/bap.h       |  51 +++
>  src/shared/bass.c      | 703 +++++++++++++++++++++++++++++++++++++++++
>  src/shared/bass.h      | 124 ++++++++
>  6 files changed, 896 insertions(+), 44 deletions(-)
>  create mode 100644 src/shared/bass.c
>  create mode 100644 src/shared/bass.h
>
> diff --git a/Makefile.am b/Makefile.am
> index 7ded3ba75..f4425a003 100644
> --- a/Makefile.am
> +++ b/Makefile.am
> @@ -231,6 +231,7 @@ shared_sources = src/shared/io.h src/shared/timeout.h \
>                         src/shared/gap.h src/shared/gap.c \
>                         src/shared/log.h src/shared/log.c \
>                         src/shared/bap.h src/shared/bap.c src/shared/ascs.h \
> +                       src/shared/bass.h src/shared/bass.c \
>                         src/shared/mcs.h src/shared/mcp.h src/shared/mcp.c \
>                         src/shared/vcp.c src/shared/vcp.h \
>                         src/shared/csip.c src/shared/csip.h \
> diff --git a/src/shared/att-types.h b/src/shared/att-types.h
> index a08b24155..6783b0980 100644
> --- a/src/shared/att-types.h
> +++ b/src/shared/att-types.h
> @@ -4,6 +4,7 @@
>   *  BlueZ - Bluetooth protocol stack for Linux
>   *
>   *  Copyright (C) 2014  Google Inc.
> + *  Copyright 2023 NXP
>   *
>   *
>   */
> @@ -101,9 +102,10 @@ struct bt_att_pdu_error_rsp {
>  /*
>   * Common Profile and Service Error Code descriptions (see Supplement to the
>   * Bluetooth Core Specification, sections 1.2 and 2). The error codes within
> - * 0xE0-0xFC are reserved for future use. The remaining 3 are defined as the
> + * 0xE0-0xFB are reserved for future use. The remaining 3 are defined as the
>   * following:
>   */
> +#define BT_ERROR_WRITE_REQUEST_REJECTED         0xfc

Please split this change on its own patch, it is probably a good idea
to update the comments above since there are now 4 errors defined in
this region.

>  #define BT_ERROR_CCC_IMPROPERLY_CONFIGURED      0xfd
>  #define BT_ERROR_ALREADY_IN_PROGRESS            0xfe
>  #define BT_ERROR_OUT_OF_RANGE                   0xff
> diff --git a/src/shared/bap.c b/src/shared/bap.c
> index 1fff7e0fd..70aa89a79 100644
> --- a/src/shared/bap.c
> +++ b/src/shared/bap.c
> @@ -4,6 +4,7 @@
>   *  BlueZ - Bluetooth protocol stack for Linux
>   *
>   *  Copyright (C) 2022  Intel Corporation. All rights reserved.
> + *  Copyright 2023 NXP
>   *
>   */
>
> @@ -28,6 +29,7 @@
>  #include "src/shared/gatt-client.h"
>  #include "src/shared/bap.h"
>  #include "src/shared/ascs.h"
> +#include "src/shared/bass.h"
>
>  /* Maximum number of ASE(s) */
>  #define NUM_SINKS 2
> @@ -114,14 +116,6 @@ struct bt_ascs {
>         struct gatt_db_attribute *ase_cp_ccc;
>  };
>
> -struct bt_bap_db {
> -       struct gatt_db *db;
> -       struct bt_pacs *pacs;
> -       struct bt_ascs *ascs;
> -       struct queue *sinks;
> -       struct queue *sources;
> -};
> -
>  struct bt_bap_req {
>         unsigned int id;
>         struct bt_bap_stream *stream;
> @@ -133,10 +127,6 @@ struct bt_bap_req {
>         void *user_data;
>  };
>
> -typedef void (*bap_notify_t)(struct bt_bap *bap, uint16_t value_handle,
> -                               const uint8_t *value, uint16_t length,
> -                               void *user_data);
> -
>  struct bt_bap_notify {
>         unsigned int id;
>         struct bt_bap *bap;
> @@ -144,35 +134,6 @@ struct bt_bap_notify {
>         void *user_data;
>  };
>
> -struct bt_bap {
> -       int ref_count;
> -       struct bt_bap_db *ldb;
> -       struct bt_bap_db *rdb;
> -       struct bt_gatt_client *client;
> -       struct bt_att *att;
> -       struct bt_bap_req *req;
> -
> -       unsigned int cp_id;
> -       unsigned int process_id;
> -       unsigned int disconn_id;
> -       unsigned int idle_id;
> -
> -       struct queue *reqs;
> -       struct queue *notify;
> -       struct queue *streams;
> -       struct queue *local_eps;
> -       struct queue *remote_eps;
> -
> -       struct queue *pac_cbs;
> -       struct queue *ready_cbs;
> -       struct queue *state_cbs;
> -
> -       bt_bap_debug_func_t debug_func;
> -       bt_bap_destroy_func_t debug_destroy;
> -       void *debug_data;
> -       void *user_data;
> -};
> -
>  struct bt_bap_pac {
>         struct bt_bap_db *bdb;
>         char *name;
> @@ -569,7 +530,7 @@ static void bap_disconnected(int err, void *user_data)
>         bt_bap_detach(bap);
>  }
>
> -static struct bt_bap *bap_get_session(struct bt_att *att, struct gatt_db *db)
> +struct bt_bap *bap_get_session(struct bt_att *att, struct gatt_db *db)
>  {
>         const struct queue_entry *entry;
>         struct bt_bap *bap;
> @@ -2189,6 +2150,7 @@ static struct bt_bap_db *bap_db_new(struct gatt_db *db)
>         bdb->db = gatt_db_ref(db);
>         bdb->sinks = queue_new();
>         bdb->sources = queue_new();
> +       bdb->bass_bcast_srcs = queue_new();
>
>         if (!bap_db)
>                 bap_db = queue_new();
> @@ -2199,6 +2161,9 @@ static struct bt_bap_db *bap_db_new(struct gatt_db *db)
>         bdb->ascs = ascs_new(db);
>         bdb->ascs->bdb = bdb;
>
> +       bdb->bass = bass_new(db);
> +       bdb->bass->bdb = bdb;
> +
>         queue_push_tail(bap_db, bdb);
>
>         return bdb;
> @@ -2518,10 +2483,12 @@ static void bap_db_free(void *data)
>
>         queue_destroy(bdb->sinks, bap_pac_free);
>         queue_destroy(bdb->sources, bap_pac_free);
> +       queue_destroy(bdb->bass_bcast_srcs, bass_bcast_src_free);
>         gatt_db_unref(bdb->db);
>
>         free(bdb->pacs);
>         free(bdb->ascs);
> +       free(bdb->bass);
>         free(bdb);
>  }
>
> @@ -2669,6 +2636,7 @@ struct bt_bap *bt_bap_new(struct gatt_db *ldb, struct gatt_db *rdb)
>         bdb->db = gatt_db_ref(rdb);
>         bdb->sinks = queue_new();
>         bdb->sources = queue_new();
> +       bdb->bass_bcast_srcs = queue_new();
>
>         bap->rdb = bdb;
>         bap->remote_eps = queue_new();
> @@ -3382,7 +3350,7 @@ static void bap_notify_destroy(void *data)
>                 free(notify);
>  }
>
> -static unsigned int bap_register_notify(struct bt_bap *bap,
> +unsigned int bap_register_notify(struct bt_bap *bap,
>                                         uint16_t value_handle,
>                                         bap_notify_t func,
>                                         void *user_data)
> @@ -3835,6 +3803,9 @@ clone:
>         bt_uuid16_create(&uuid, ASCS_UUID);
>         gatt_db_foreach_service(bap->rdb->db, &uuid, foreach_ascs_service, bap);
>
> +       bt_uuid16_create(&uuid, BASS_UUID);
> +       gatt_db_foreach_service(bap->rdb->db, &uuid, foreach_bass_service, bap);
> +
>         return true;
>  }
>
> diff --git a/src/shared/bap.h b/src/shared/bap.h
> index bd13abef9..44b0d1535 100644
> --- a/src/shared/bap.h
> +++ b/src/shared/bap.h
> @@ -4,6 +4,7 @@
>   *  BlueZ - Bluetooth protocol stack for Linux
>   *
>   *  Copyright (C) 2022  Intel Corporation. All rights reserved.
> + *  Copyright 2023 NXP
>   *
>   */
>
> @@ -62,9 +63,49 @@ struct bt_bap_qos {
>         uint8_t  target_latency;        /* Target Latency */
>  };
>
> +struct bt_bap_db {
> +       struct gatt_db *db;
> +       struct bt_pacs *pacs;
> +       struct bt_ascs *ascs;
> +       struct bt_bass *bass;
> +       struct queue *sinks;
> +       struct queue *sources;
> +       struct queue *bass_bcast_srcs;
> +};
> +
>  typedef void (*bt_bap_ready_func_t)(struct bt_bap *bap, void *user_data);
>  typedef void (*bt_bap_destroy_func_t)(void *user_data);
>  typedef void (*bt_bap_debug_func_t)(const char *str, void *user_data);
> +
> +struct bt_bap {
> +       int ref_count;
> +       struct bt_bap_db *ldb;
> +       struct bt_bap_db *rdb;
> +       struct bt_gatt_client *client;
> +       struct bt_att *att;
> +       struct bt_bap_req *req;
> +
> +       unsigned int cp_id;
> +       unsigned int process_id;
> +       unsigned int disconn_id;
> +       unsigned int idle_id;
> +
> +       struct queue *reqs;
> +       struct queue *notify;
> +       struct queue *streams;
> +       struct queue *local_eps;
> +       struct queue *remote_eps;
> +
> +       struct queue *pac_cbs;
> +       struct queue *ready_cbs;
> +       struct queue *state_cbs;
> +
> +       bt_bap_debug_func_t debug_func;
> +       bt_bap_destroy_func_t debug_destroy;
> +       void *debug_data;
> +       void *user_data;
> +};

Don't really like the idea of exposing these as public API, I know BAP
also incorporate the BASS client functionality but BASS server
shouldn't use bt_bap section, so Id keep client and server
independent, client being part of bap.c and server on bass.c, also it
probably makes sense to have a dedicated plugin for bass as standalone
so if the platform is not interested in exposing BASS it can just
disable the plugin so no instance of bt_bass shall exist.

>  typedef void (*bt_bap_pac_func_t)(struct bt_bap_pac *pac, void *user_data);
>  typedef bool (*bt_bap_pac_foreach_t)(struct bt_bap_pac *lpac,
>                                         struct bt_bap_pac *rpac,
> @@ -86,6 +127,10 @@ typedef void (*bt_bap_stream_func_t)(struct bt_bap_stream *stream,
>                                         void *user_data);
>  typedef void (*bt_bap_func_t)(struct bt_bap *bap, void *user_data);
>
> +typedef void (*bap_notify_t)(struct bt_bap *bap, uint16_t value_handle,
> +                               const uint8_t *value, uint16_t length,
> +                               void *user_data);
> +
>  /* Local PAC related functions */
>  struct bt_bap_pac_qos {
>         uint8_t  framing;
> @@ -265,3 +310,9 @@ uint8_t bt_bap_stream_io_dir(struct bt_bap_stream *stream);
>
>  int bt_bap_stream_io_connecting(struct bt_bap_stream *stream, int fd);
>  bool bt_bap_stream_io_is_connecting(struct bt_bap_stream *stream, int *fd);
> +
> +struct bt_bap *bap_get_session(struct bt_att *att, struct gatt_db *db);
> +unsigned int bap_register_notify(struct bt_bap *bap,
> +                                       uint16_t value_handle,
> +                                       bap_notify_t func,
> +                                       void *user_data);
> diff --git a/src/shared/bass.c b/src/shared/bass.c
> new file mode 100644
> index 000000000..1be36ef75
> --- /dev/null
> +++ b/src/shared/bass.c
> @@ -0,0 +1,703 @@
> +// SPDX-License-Identifier: LGPL-2.1-or-later
> +/*
> + *
> + *  BlueZ - Bluetooth protocol stack for Linux
> + *
> + *  Copyright 2023 NXP
> + *
> + */
> +
> +#define _GNU_SOURCE
> +#include <inttypes.h>
> +#include <string.h>
> +#include <stdlib.h>
> +#include <stdbool.h>
> +#include <unistd.h>
> +#include <errno.h>
> +#include <poll.h>
> +
> +#include "lib/bluetooth.h"
> +#include "lib/uuid.h"
> +#include "lib/iso.h"
> +
> +#include "src/shared/queue.h"
> +#include "src/shared/util.h"
> +#include "src/shared/att.h"
> +#include "src/shared/gatt-db.h"
> +#include "src/shared/gatt-client.h"
> +#include "src/shared/bap.h"
> +#include "src/shared/bass.h"
> +
> +#define DBG(_bap, fmt, arg...) \
> +       bass_debug(_bap, "%s:%s() " fmt, __FILE__, __func__, ## arg)
> +
> +static void bass_debug(struct bt_bap *bap, const char *format, ...)
> +{
> +       va_list ap;
> +
> +       if (!bap || !format || !bap->debug_func)
> +               return;
> +
> +       va_start(ap, format);
> +       util_debug_va(bap->debug_func, bap->debug_data, format, ap);
> +       va_end(ap);
> +}
> +
> +static int
> +bass_build_bcast_src_from_notif(struct bt_bcast_src *bcast_src,
> +                               const uint8_t *value, uint16_t length)
> +{
> +       struct bt_bass_subgroup_data *subgroup_data = NULL;
> +       uint8_t *id;
> +       uint8_t *addr_type;
> +       uint8_t *addr;
> +       uint8_t *sid;
> +       uint8_t *bid;
> +       uint8_t *pa_sync_state;
> +       uint8_t *enc;
> +       uint8_t *bad_code = NULL;
> +       uint8_t *num_subgroups;
> +       uint8_t *bis_sync_state;
> +       uint8_t *meta_len;
> +       uint8_t *meta;
> +
> +       struct iovec iov = {
> +               .iov_base = (void *) value,
> +               .iov_len = length,
> +       };
> +
> +       /* Extract all fields from notification */
> +       id = util_iov_pull_mem(&iov, sizeof(*id));
> +       if (!id) {
> +               DBG(bcast_src->bap, "Unable to parse Broadcast Receive State");
> +               return -1;
> +       }
> +
> +       addr_type = util_iov_pull_mem(&iov, sizeof(*addr_type));
> +       if (!addr_type) {
> +               DBG(bcast_src->bap, "Unable to parse Broadcast Receive State");
> +               return -1;
> +       }
> +
> +       addr = util_iov_pull_mem(&iov, sizeof(bdaddr_t));
> +       if (!addr) {
> +               DBG(bcast_src->bap, "Unable to parse Broadcast Receive State");
> +               return -1;
> +       }
> +
> +       sid = util_iov_pull_mem(&iov, sizeof(*sid));
> +       if (!sid) {
> +               DBG(bcast_src->bap, "Unable to parse Broadcast Receive State");
> +               return -1;
> +       }
> +
> +       bid = util_iov_pull_mem(&iov, 3);
> +       if (!bid) {
> +               DBG(bcast_src->bap, "Unable to parse Broadcast Receive State");
> +               return -1;
> +       }
> +
> +       pa_sync_state = util_iov_pull_mem(&iov, sizeof(*pa_sync_state));
> +       if (!pa_sync_state) {
> +               DBG(bcast_src->bap, "Unable to parse Broadcast Receive State");
> +               return -1;
> +       }
> +
> +       enc = util_iov_pull_mem(&iov, sizeof(*enc));
> +       if (!enc) {
> +               DBG(bcast_src->bap, "Unable to parse Broadcast Receive State");
> +               return -1;
> +       }
> +
> +       if (*enc == BT_BASS_BIG_ENC_STATE_BAD_CODE) {
> +               bad_code = util_iov_pull_mem(&iov, BT_BASS_BCAST_CODE_SIZE);
> +               if (!bad_code) {
> +                       DBG(bcast_src->bap, "Unable to parse "
> +                               "Broadcast Receive State");
> +                       return -1;
> +               }
> +       }
> +
> +       num_subgroups = util_iov_pull_mem(&iov, sizeof(*num_subgroups));
> +       if (!num_subgroups) {
> +               DBG(bcast_src->bap, "Unable to parse Broadcast Receive State");
> +               return -1;
> +       }
> +
> +       if (*num_subgroups == 0)
> +               goto done;
> +
> +       subgroup_data = malloc((*num_subgroups) * sizeof(*subgroup_data));
> +       if (!subgroup_data) {
> +               DBG(bcast_src->bap, "Unable to allocate memory");
> +               return -1;
> +       }
> +
> +       memset(subgroup_data, 0, (*num_subgroups) * sizeof(*subgroup_data));
> +
> +       for (int i = 0; i < *num_subgroups; i++) {
> +               bis_sync_state = util_iov_pull_mem(&iov,
> +                                               sizeof(uint32_t));
> +               if (!bis_sync_state) {
> +                       DBG(bcast_src->bap, "Unable to parse "
> +                               "Broadcast Receive State");
> +
> +                       for (int j = 0; j < i; j++)
> +                               free(subgroup_data[j].meta);
> +
> +                       free(subgroup_data);
> +                       return -1;
> +               }
> +
> +               subgroup_data[i].bis_sync = get_le32(bis_sync_state);
> +
> +               meta_len = util_iov_pull_mem(&iov, sizeof(*meta_len));
> +               if (!meta_len) {
> +                       DBG(bcast_src->bap, "Unable to parse "
> +                               "Broadcast Receive State");
> +
> +                       for (int j = 0; j < i; j++)
> +                               free(subgroup_data[j].meta);
> +
> +                       free(subgroup_data);
> +                       return -1;
> +               }
> +
> +               subgroup_data[i].meta_len = *meta_len;
> +
> +               if (*meta_len == 0)
> +                       continue;
> +
> +               subgroup_data[i].meta = malloc(*meta_len);
> +               if (!subgroup_data[i].meta) {
> +                       DBG(bcast_src->bap, "Unable to allocate memory");
> +
> +                       for (int j = 0; j < i; j++)
> +                               free(subgroup_data[j].meta);
> +
> +                       free(subgroup_data);
> +                       return -1;
> +               }
> +
> +               meta = util_iov_pull_mem(&iov, *meta_len);
> +               if (!meta) {
> +                       DBG(bcast_src->bap, "Unable to parse "
> +                               "Broadcast Receive State");
> +
> +                       for (int j = 0; j < i; j++)
> +                               free(subgroup_data[j].meta);
> +
> +                       free(subgroup_data);
> +                       return -1;
> +               }
> +
> +               memcpy(subgroup_data[i].meta, meta, *meta_len);
> +       }
> +
> +done:
> +       /*
> +        * If no errors occurred, copy extracted fields into
> +        * the broadcast source structure
> +        */
> +       if (bcast_src->subgroup_data) {
> +               for (int i = 0; i < bcast_src->num_subgroups; i++)
> +                       free(bcast_src->subgroup_data[i].meta);
> +
> +               free(bcast_src->subgroup_data);
> +       }
> +
> +       bcast_src->id = *id;
> +       bcast_src->addr_type = *addr_type;
> +       memcpy(&bcast_src->addr, addr, sizeof(bdaddr_t));
> +       bcast_src->sid = *sid;
> +       bcast_src->bid = get_le24(bid);
> +       bcast_src->sync_state = *pa_sync_state;
> +       bcast_src->enc = *enc;
> +
> +       if (*enc == BT_BASS_BIG_ENC_STATE_BAD_CODE)
> +               memcpy(bcast_src->bad_code, bad_code, BT_BASS_BCAST_CODE_SIZE);
> +       else
> +               memset(bcast_src->bad_code, 0, BT_BASS_BCAST_CODE_SIZE);
> +
> +       bcast_src->num_subgroups = *num_subgroups;
> +
> +       bcast_src->subgroup_data = subgroup_data;
> +
> +       return 0;
> +}
> +
> +static int
> +bass_build_bcast_src_from_read_rsp(struct bt_bcast_src *bcast_src,
> +                               const uint8_t *value, uint16_t length)
> +{
> +       return bass_build_bcast_src_from_notif(bcast_src, value, length);
> +}
> +
> +static uint8_t *bass_build_notif_from_bcast_src(struct bt_bcast_src *bcast_src,
> +                                                       size_t *notif_len)
> +{
> +       size_t len = 0;
> +       uint8_t *notif = NULL;
> +       struct iovec iov;
> +
> +       *notif_len = 0;
> +
> +       if (!bcast_src)
> +               return NULL;
> +
> +       len = BT_BASS_BCAST_SRC_LEN + bcast_src->num_subgroups *
> +                       BT_BASS_BCAST_SRC_SUBGROUP_LEN;
> +
> +       if (bcast_src->enc == BT_BASS_BIG_ENC_STATE_BAD_CODE)
> +               len += BT_BASS_BCAST_CODE_SIZE;
> +
> +       for (size_t i = 0; i < bcast_src->num_subgroups; i++) {
> +               /* Add length for subgroup metadata */
> +               len += bcast_src->subgroup_data[i].meta_len;
> +       }
> +
> +       notif = malloc(len);
> +       if (!notif)
> +               return NULL;
> +
> +       memset(notif, 0, len);
> +
> +       iov.iov_base = notif;
> +       iov.iov_len = 0;
> +
> +       util_iov_push_mem(&iov, sizeof(bcast_src->id),
> +                       &bcast_src->id);
> +       util_iov_push_mem(&iov, sizeof(bcast_src->addr_type),
> +                       &bcast_src->addr_type);
> +       util_iov_push_mem(&iov, sizeof(bcast_src->addr),
> +                       &bcast_src->addr);
> +       util_iov_push_mem(&iov, sizeof(bcast_src->sid),
> +                       &bcast_src->sid);
> +       util_iov_push_mem(&iov, 3, &bcast_src->bid);
> +       util_iov_push_mem(&iov, sizeof(bcast_src->sync_state),
> +                       &bcast_src->sync_state);

Don't we need to convert the endianess for multibyte fields or are
they stored in LE format? I usually recommend storing in host order so
we only convert when encoding a PDU to be sent over the air, It would
probably be a good idea to add something like util_iov_push_le32
though, etc, which internally could use cpu_to_le32 to convert in
place, this would apply to util_iov_pull_le32 as well.

> +       util_iov_push_mem(&iov, sizeof(bcast_src->enc),
> +                       &bcast_src->enc);
> +
> +       if (bcast_src->enc == BT_BASS_BIG_ENC_STATE_BAD_CODE)
> +               util_iov_push_mem(&iov, sizeof(bcast_src->bad_code),
> +                                       bcast_src->bad_code);
> +
> +       util_iov_push_mem(&iov, sizeof(bcast_src->num_subgroups),
> +                               &bcast_src->num_subgroups);
> +
> +       for (size_t i = 0; i < bcast_src->num_subgroups; i++) {
> +               /* Add subgroup bis_sync */
> +               util_iov_push_mem(&iov,
> +                       sizeof(bcast_src->subgroup_data[i].bis_sync),
> +                       &bcast_src->subgroup_data[i].bis_sync);
> +
> +               /* Add subgroup meta_len */
> +               util_iov_push_mem(&iov,
> +                       sizeof(bcast_src->subgroup_data[i].meta_len),
> +                       &bcast_src->subgroup_data[i].meta_len);
> +
> +               /* Add subgroup metadata */
> +               if (bcast_src->subgroup_data[i].meta_len > 0)
> +                       util_iov_push_mem(&iov,
> +                               bcast_src->subgroup_data[i].meta_len,
> +                               bcast_src->subgroup_data[i].meta);
> +       }
> +
> +       *notif_len = len;
> +       return notif;
> +}
> +
> +static uint8_t *
> +bass_build_read_rsp_from_bcast_src(struct bt_bcast_src *bcast_src,
> +                                       size_t *rsp_len)
> +{
> +       return bass_build_notif_from_bcast_src(bcast_src, rsp_len);
> +}
> +
> +static bool bass_check_cp_command_subgroup_data_len(uint8_t num_subgroups,
> +                                                       struct iovec *iov)
> +{
> +       uint32_t *bis_sync_state;
> +       uint8_t *meta_len;
> +       uint8_t *meta;
> +
> +       for (int i = 0; i < num_subgroups; i++) {
> +               bis_sync_state = util_iov_pull_mem(iov,
> +                                       sizeof(*bis_sync_state));
> +               if (!bis_sync_state)
> +                       return false;
> +
> +               meta_len = util_iov_pull_mem(iov,
> +                                       sizeof(*meta_len));
> +               if (!meta_len)
> +                       return false;
> +
> +               meta = util_iov_pull_mem(iov, *meta_len);
> +               if (!meta)
> +                       return false;
> +       }
> +
> +       return true;
> +}
> +
> +static bool bass_check_cp_command_len(struct iovec *iov)
> +{
> +       struct bt_bass_bcast_audio_scan_cp_hdr *hdr;
> +       union {
> +               struct bt_bass_add_src_params *add_src_params;
> +               struct bt_bass_mod_src_params *mod_src_params;
> +               struct bt_bass_set_bcast_code_params *set_bcast_code_params;
> +               struct bt_bass_remove_src_params *remove_src_params;
> +       } params;
> +
> +       /* Get command header */
> +       hdr = util_iov_pull_mem(iov, sizeof(*hdr));
> +
> +       if (!hdr)
> +               return false;
> +
> +       /* Check command parameters */
> +       switch (hdr->op) {
> +       case BT_BASS_ADD_SRC:
> +               params.add_src_params = util_iov_pull_mem(iov,
> +                                               sizeof(*params.add_src_params));
> +               if (!params.add_src_params)
> +                       return false;
> +
> +               if (!bass_check_cp_command_subgroup_data_len(
> +                                       params.add_src_params->num_subgroups,
> +                                       iov))
> +                       return false;
> +
> +               break;
> +       case BT_BASS_MOD_SRC:
> +               params.mod_src_params = util_iov_pull_mem(iov,
> +                                               sizeof(*params.mod_src_params));
> +               if (!params.mod_src_params)
> +                       return false;
> +
> +               if (!bass_check_cp_command_subgroup_data_len(
> +                                       params.mod_src_params->num_subgroups,
> +                                       iov))
> +                       return false;
> +
> +               break;
> +       case BT_BASS_SET_BCAST_CODE:
> +               params.set_bcast_code_params = util_iov_pull_mem(iov,
> +                                       sizeof(*params.set_bcast_code_params));
> +               if (!params.set_bcast_code_params)
> +                       return false;
> +
> +               break;
> +       case BT_BASS_REMOVE_SRC:
> +               params.remove_src_params = util_iov_pull_mem(iov,
> +                                       sizeof(*params.remove_src_params));
> +               if (!params.remove_src_params)
> +                       return false;
> +
> +               break;
> +       case BT_BASS_REMOTE_SCAN_STOPPED:
> +       case BT_BASS_REMOTE_SCAN_STARTED:
> +               break;
> +       default:
> +               return true;
> +       }
> +
> +       if (iov->iov_len > 0)
> +               return false;
> +
> +       return true;
> +}
> +
> +static void bass_bcast_audio_scan_cp_write(struct gatt_db_attribute *attrib,
> +                               unsigned int id, uint16_t offset,
> +                               const uint8_t *value, size_t len,
> +                               uint8_t opcode, struct bt_att *att,
> +                               void *user_data)
> +{
> +       struct iovec iov = {
> +               .iov_base = (void *)value,
> +               .iov_len = len,
> +       };
> +
> +       /* Validate written command length */
> +       if (!bass_check_cp_command_len(&iov)) {
> +               if (opcode == BT_ATT_OP_WRITE_REQ) {
> +                       gatt_db_attribute_write_result(attrib, id,
> +                                       BT_ERROR_WRITE_REQUEST_REJECTED);
> +               }
> +               return;
> +       }
> +
> +       /* TODO: Implement handlers for the written opcodes */
> +       gatt_db_attribute_write_result(attrib, id,
> +                       BT_BASS_ERROR_OPCODE_NOT_SUPPORTED);
> +}
> +
> +static bool bass_src_match_attrib(const void *data, const void *match_data)
> +{
> +       const struct bt_bcast_src *bcast_src = data;
> +       const struct gatt_db_attribute *attr = match_data;
> +
> +       return (bcast_src->attr == attr);
> +}
> +
> +static void bass_bcast_recv_state_read(struct gatt_db_attribute *attrib,
> +                                       unsigned int id, uint16_t offset,
> +                                       uint8_t opcode, struct bt_att *att,
> +                                       void *user_data)
> +{
> +       struct bt_bass *bass = user_data;
> +       struct bt_bap *bap = bap_get_session(att, bass->bdb->db);
> +       uint8_t *rsp;
> +       size_t rsp_len;
> +       struct bt_bcast_src *bcast_src;
> +
> +       bcast_src = queue_find(bap->ldb->bass_bcast_srcs,
> +                                       bass_src_match_attrib,
> +                                       attrib);
> +
> +       if (!bcast_src) {
> +               gatt_db_attribute_read_result(attrib, id, 0, NULL,
> +                                                       0);
> +               return;
> +       }
> +
> +       /* Build read response */
> +       rsp = bass_build_read_rsp_from_bcast_src(bcast_src, &rsp_len);
> +
> +       if (!rsp) {
> +               gatt_db_attribute_read_result(attrib, id,
> +                                       BT_ATT_ERROR_UNLIKELY,
> +                                       NULL, 0);
> +               return;
> +       }
> +
> +       gatt_db_attribute_read_result(attrib, id, 0, (void *)rsp,
> +                                               rsp_len);
> +
> +       free(rsp);
> +}
> +
> +static void bcast_recv_new(struct bt_bass *bass, int i)
> +{
> +       struct bt_bcast_recv_state *bcast_recv_state;
> +       bt_uuid_t uuid;
> +
> +       if (!bass)
> +               return;
> +
> +       bcast_recv_state = new0(struct bt_bcast_recv_state, 1);
> +       bcast_recv_state->bass = bass;
> +
> +       bt_uuid16_create(&uuid, BCAST_RECV_STATE_UUID);
> +       bcast_recv_state->attr =
> +               gatt_db_service_add_characteristic(bass->service, &uuid,
> +                               BT_ATT_PERM_READ | BT_ATT_PERM_READ_ENCRYPT,
> +                               BT_GATT_CHRC_PROP_READ |
> +                               BT_GATT_CHRC_PROP_NOTIFY,
> +                               bass_bcast_recv_state_read, NULL,
> +                               bass);
> +
> +       bcast_recv_state->ccc = gatt_db_service_add_ccc(bass->service,
> +                                       BT_ATT_PERM_READ | BT_ATT_PERM_WRITE);
> +
> +       bass->bcast_recv_states[i] = bcast_recv_state;
> +}
> +
> +struct bt_bass *bass_new(struct gatt_db *db)
> +{
> +       struct bt_bass *bass;
> +       bt_uuid_t uuid;
> +       int i;
> +
> +       if (!db)
> +               return NULL;
> +
> +       bass = new0(struct bt_bass, 1);
> +
> +       /* Populate DB with BASS attributes */
> +       bt_uuid16_create(&uuid, BASS_UUID);
> +       bass->service = gatt_db_add_service(db, &uuid, true,
> +                                       3 + (NUM_BCAST_RECV_STATES * 3));
> +
> +       for (i = 0; i < NUM_BCAST_RECV_STATES; i++)
> +               bcast_recv_new(bass, i);
> +
> +       bt_uuid16_create(&uuid, BCAST_AUDIO_SCAN_CP_UUID);
> +       bass->bcast_audio_scan_cp =
> +               gatt_db_service_add_characteristic(bass->service,
> +                               &uuid,
> +                               BT_ATT_PERM_WRITE | BT_ATT_PERM_WRITE_ENCRYPT,
> +                               BT_GATT_CHRC_PROP_WRITE,
> +                               NULL, bass_bcast_audio_scan_cp_write,
> +                               bass);
> +
> +       gatt_db_service_set_active(bass->service, true);
> +
> +       return bass;
> +}
> +
> +void bass_bcast_src_free(void *data)
> +{
> +       struct bt_bcast_src *bcast_src = data;
> +
> +       for (int i = 0; i < bcast_src->num_subgroups; i++)
> +               free(bcast_src->subgroup_data[i].meta);
> +
> +       free(bcast_src->subgroup_data);
> +       free(bcast_src);
> +}
> +
> +static void read_bcast_recv_state(bool success, uint8_t att_ecode,
> +                               const uint8_t *value, uint16_t length,
> +                               void *user_data)
> +{
> +       struct bt_bcast_src *bcast_src = user_data;
> +
> +       if (!success) {
> +               DBG(bcast_src->bap, "Unable to read "
> +                       "Broadcast Receive State: error 0x%02x",
> +                       att_ecode);
> +               return;
> +       }
> +
> +       if (length == 0) {
> +               queue_remove(bcast_src->bap->rdb->bass_bcast_srcs, bcast_src);
> +               bass_bcast_src_free(bcast_src);
> +               return;
> +       }
> +
> +       if (bass_build_bcast_src_from_read_rsp(bcast_src, value, length)) {
> +               queue_remove(bcast_src->bap->rdb->bass_bcast_srcs, bcast_src);
> +               bass_bcast_src_free(bcast_src);
> +               return;
> +       }
> +}
> +
> +static void bcast_recv_state_notify(struct bt_bap *bap, uint16_t value_handle,
> +                               const uint8_t *value, uint16_t length,
> +                               void *user_data)
> +{
> +       struct gatt_db_attribute *attr = user_data;
> +       struct bt_bcast_src *bcast_src;
> +       bool new_src = false;
> +
> +       bcast_src = queue_find(bap->rdb->bass_bcast_srcs,
> +                                       bass_src_match_attrib, attr);
> +       if (!bcast_src) {
> +               new_src = true;
> +               bcast_src = malloc(sizeof(*bcast_src));
> +
> +               if (!bcast_src) {
> +                       DBG(bap, "Failed to allocate "
> +                               "memory for broadcast source");
> +                       return;
> +               }
> +
> +               memset(bcast_src, 0, sizeof(struct bt_bcast_src));
> +               bcast_src->bap = bap;
> +               bcast_src->attr = attr;
> +       }
> +
> +       if (bass_build_bcast_src_from_notif(bcast_src, value, length)
> +                                               && new_src) {
> +               bass_bcast_src_free(bcast_src);
> +               return;
> +       }
> +
> +       if (new_src)
> +               queue_push_tail(bap->rdb->bass_bcast_srcs, bcast_src);
> +}
> +
> +static struct bt_bass *bap_get_bass(struct bt_bap *bap)
> +{
> +       if (!bap)
> +               return NULL;
> +
> +       if (bap->rdb->bass)
> +               return bap->rdb->bass;
> +
> +       bap->rdb->bass = new0(struct bt_bass, 1);
> +       bap->rdb->bass->bdb = bap->rdb;
> +
> +       return bap->rdb->bass;
> +}
> +
> +static void foreach_bass_char(struct gatt_db_attribute *attr, void *user_data)
> +{
> +       struct bt_bap *bap = user_data;
> +       uint16_t value_handle;
> +       bt_uuid_t uuid, uuid_bcast_audio_scan_cp, uuid_bcast_recv_state;
> +       struct bt_bass *bass;
> +
> +       /* Get attribute value handle and uuid */
> +       if (!gatt_db_attribute_get_char_data(attr, NULL, &value_handle,
> +                                               NULL, NULL, &uuid))
> +               return;
> +
> +       bt_uuid16_create(&uuid_bcast_audio_scan_cp, BCAST_AUDIO_SCAN_CP_UUID);
> +       bt_uuid16_create(&uuid_bcast_recv_state, BCAST_RECV_STATE_UUID);
> +
> +       if (!bt_uuid_cmp(&uuid, &uuid_bcast_audio_scan_cp)) {
> +               /* Found Broadcast Audio Scan Control Point characteristic */
> +               bass = bap_get_bass(bap);
> +
> +               if (!bass || bass->bcast_audio_scan_cp)
> +                       return;
> +
> +               /* Store characteristic reference */
> +               bass->bcast_audio_scan_cp = attr;
> +
> +               DBG(bap, "Broadcast Audio Scan Control Point "
> +                       "found: handle 0x%04x", value_handle);
> +       }
> +
> +       if (!bt_uuid_cmp(&uuid, &uuid_bcast_recv_state)) {
> +               /* Found Broadcast Receive State characteristic */
> +               struct bt_bcast_src *bcast_src =
> +                               queue_find(bap->rdb->bass_bcast_srcs,
> +                                               bass_src_match_attrib, attr);
> +
> +               if (!bcast_src) {
> +                       bcast_src = malloc(sizeof(struct bt_bcast_src));
> +
> +                       if (bcast_src == NULL) {
> +                               DBG(bap, "Failed to allocate "
> +                                       "memory for broadcast source");
> +                               return;
> +                       }
> +
> +                       memset(bcast_src, 0, sizeof(struct bt_bcast_src));
> +                       bcast_src->bap = bap;
> +                       bcast_src->attr = attr;
> +
> +                       queue_push_tail(bap->rdb->bass_bcast_srcs, bcast_src);
> +               }
> +
> +               bt_gatt_client_read_value(bap->client, value_handle,
> +                                               read_bcast_recv_state,
> +                                               bcast_src, NULL);
> +
> +               (void)bap_register_notify(bap, value_handle,
> +                                               bcast_recv_state_notify,
> +                                               attr);
> +
> +               DBG(bap, "Broadcast Receive State found: handle 0x%04x",
> +                                                       value_handle);
> +       }
> +}
> +
> +void foreach_bass_service(struct gatt_db_attribute *attr,
> +                                               void *user_data)
> +{
> +       struct bt_bap *bap = user_data;
> +       struct bt_bass *bass = bap_get_bass(bap);
> +
> +       /* Store BASS attribute reference */
> +       bass->service = attr;
> +
> +       /* Handle BASS attributes */
> +       gatt_db_service_foreach_char(attr, foreach_bass_char, bap);
> +}
> diff --git a/src/shared/bass.h b/src/shared/bass.h
> new file mode 100644
> index 000000000..8edd73502
> --- /dev/null
> +++ b/src/shared/bass.h
> @@ -0,0 +1,124 @@
> +/* SPDX-License-Identifier: LGPL-2.1-or-later */
> +/*
> + *
> + *  BlueZ - Bluetooth protocol stack for Linux
> + *
> + *  Copyright 2023 NXP
> + *
> + */
> +
> +#define NUM_BCAST_RECV_STATES                          2
> +#define BT_BASS_BCAST_CODE_SIZE                                16
> +#define BT_BASS_BIG_SYNC_FAILED_BITMASK                        0xFFFFFFFF
> +#define BT_BASS_BCAST_SRC_LEN                          15
> +#define BT_BASS_BCAST_SRC_SUBGROUP_LEN                 5
> +
> +/* Application error codes */
> +#define BT_BASS_ERROR_OPCODE_NOT_SUPPORTED             0x80
> +#define BT_BASS_ERROR_INVALID_SOURCE_ID                        0x81
> +
> +/* PA_Sync_State values */
> +#define BT_BASS_NOT_SYNCHRONIZED_TO_PA                 0x00
> +#define BT_BASS_SYNC_INFO_RE                           0x01
> +#define BT_BASS_SYNCHRONIZED_TO_PA                     0x02
> +#define BT_BASS_FAILED_TO_SYNCHRONIZE_TO_PA            0x03
> +#define BT_BASS_NO_PAST                                        0x04
> +
> +/* BIG_Encryption values */
> +#define BT_BASS_BIG_ENC_STATE_NO_ENC                   0x00
> +#define BT_BASS_BIG_ENC_STATE_BCODE_REQ                        0x01
> +#define BT_BASS_BIG_ENC_STATE_DEC                      0x02
> +#define BT_BASS_BIG_ENC_STATE_BAD_CODE                 0x03
> +
> +/* BASS subgroup field of the Broadcast
> + * Receive State characteristic
> + */
> +struct bt_bass_subgroup_data {
> +       uint32_t bis_sync;
> +       uint32_t pending_bis_sync;
> +       uint8_t meta_len;
> +       uint8_t *meta;
> +};
> +
> +/* BASS Broadcast Source structure */
> +struct bt_bcast_src {
> +       struct bt_bap *bap;
> +       struct gatt_db_attribute *attr;
> +       uint8_t id;
> +       uint8_t addr_type;
> +       bdaddr_t addr;
> +       uint8_t sid;
> +       uint32_t bid;
> +       uint8_t sync_state;
> +       uint8_t enc;
> +       uint8_t bad_code[BT_BASS_BCAST_CODE_SIZE];
> +       uint8_t num_subgroups;
> +       struct bt_bass_subgroup_data *subgroup_data;
> +};
> +
> +/* Broadcast Receive State characteristic structure */
> +struct bt_bcast_recv_state {
> +       struct bt_bass *bass;
> +       struct gatt_db_attribute *attr;
> +       struct gatt_db_attribute *ccc;
> +};
> +
> +/* BASS instance structure */
> +struct bt_bass {
> +       struct bt_bap_db *bdb;
> +       struct gatt_db_attribute *service;
> +       struct gatt_db_attribute *bcast_audio_scan_cp;
> +       struct bt_bcast_recv_state *bcast_recv_states[NUM_BCAST_RECV_STATES];
> +};
> +
> +/* Broadcast Audio Scan Control Point
> + * header structure
> + */
> +struct bt_bass_bcast_audio_scan_cp_hdr {
> +       uint8_t op;
> +} __packed;
> +
> +#define BT_BASS_REMOTE_SCAN_STOPPED                    0x00
> +
> +#define BT_BASS_REMOTE_SCAN_STARTED                    0x01
> +
> +#define BT_BASS_ADD_SRC                                        0x02
> +
> +struct bt_bass_add_src_params {
> +       uint8_t addr_type;
> +       bdaddr_t addr;
> +       uint8_t sid;
> +       uint8_t bid[3];
> +       uint8_t pa_sync;
> +       uint16_t pa_interval;
> +       uint8_t num_subgroups;
> +       uint8_t subgroup_data[];
> +} __packed;
> +
> +#define BT_BASS_MOD_SRC                                        0x03
> +
> +struct bt_bass_mod_src_params {
> +       uint8_t id;
> +       uint8_t pa_sync;
> +       uint16_t pa_interval;
> +       uint8_t num_subgroups;
> +       uint8_t subgroup_data[];
> +} __packed;
> +
> +#define BT_BASS_SET_BCAST_CODE                         0x04
> +
> +struct bt_bass_set_bcast_code_params {
> +       uint8_t id;
> +       uint8_t bcast_code[BT_BASS_BCAST_CODE_SIZE];
> +} __packed;
> +
> +#define BT_BASS_REMOVE_SRC                             0x05
> +
> +struct bt_bass_remove_src_params {
> +       uint8_t id;
> +} __packed;
> +
> +struct bt_bass *bass_new(struct gatt_db *db);
> +void bass_bcast_src_free(void *data);
> +void foreach_bass_service(struct gatt_db_attribute *attr,
> +                                               void *user_data);
> --
> 2.34.1
>
diff mbox series

Patch

diff --git a/Makefile.am b/Makefile.am
index 7ded3ba75..f4425a003 100644
--- a/Makefile.am
+++ b/Makefile.am
@@ -231,6 +231,7 @@  shared_sources = src/shared/io.h src/shared/timeout.h \
 			src/shared/gap.h src/shared/gap.c \
 			src/shared/log.h src/shared/log.c \
 			src/shared/bap.h src/shared/bap.c src/shared/ascs.h \
+			src/shared/bass.h src/shared/bass.c \
 			src/shared/mcs.h src/shared/mcp.h src/shared/mcp.c \
 			src/shared/vcp.c src/shared/vcp.h \
 			src/shared/csip.c src/shared/csip.h \
diff --git a/src/shared/att-types.h b/src/shared/att-types.h
index a08b24155..6783b0980 100644
--- a/src/shared/att-types.h
+++ b/src/shared/att-types.h
@@ -4,6 +4,7 @@ 
  *  BlueZ - Bluetooth protocol stack for Linux
  *
  *  Copyright (C) 2014  Google Inc.
+ *  Copyright 2023 NXP
  *
  *
  */
@@ -101,9 +102,10 @@  struct bt_att_pdu_error_rsp {
 /*
  * Common Profile and Service Error Code descriptions (see Supplement to the
  * Bluetooth Core Specification, sections 1.2 and 2). The error codes within
- * 0xE0-0xFC are reserved for future use. The remaining 3 are defined as the
+ * 0xE0-0xFB are reserved for future use. The remaining 3 are defined as the
  * following:
  */
+#define BT_ERROR_WRITE_REQUEST_REJECTED         0xfc
 #define BT_ERROR_CCC_IMPROPERLY_CONFIGURED      0xfd
 #define BT_ERROR_ALREADY_IN_PROGRESS            0xfe
 #define BT_ERROR_OUT_OF_RANGE                   0xff
diff --git a/src/shared/bap.c b/src/shared/bap.c
index 1fff7e0fd..70aa89a79 100644
--- a/src/shared/bap.c
+++ b/src/shared/bap.c
@@ -4,6 +4,7 @@ 
  *  BlueZ - Bluetooth protocol stack for Linux
  *
  *  Copyright (C) 2022  Intel Corporation. All rights reserved.
+ *  Copyright 2023 NXP
  *
  */
 
@@ -28,6 +29,7 @@ 
 #include "src/shared/gatt-client.h"
 #include "src/shared/bap.h"
 #include "src/shared/ascs.h"
+#include "src/shared/bass.h"
 
 /* Maximum number of ASE(s) */
 #define NUM_SINKS 2
@@ -114,14 +116,6 @@  struct bt_ascs {
 	struct gatt_db_attribute *ase_cp_ccc;
 };
 
-struct bt_bap_db {
-	struct gatt_db *db;
-	struct bt_pacs *pacs;
-	struct bt_ascs *ascs;
-	struct queue *sinks;
-	struct queue *sources;
-};
-
 struct bt_bap_req {
 	unsigned int id;
 	struct bt_bap_stream *stream;
@@ -133,10 +127,6 @@  struct bt_bap_req {
 	void *user_data;
 };
 
-typedef void (*bap_notify_t)(struct bt_bap *bap, uint16_t value_handle,
-				const uint8_t *value, uint16_t length,
-				void *user_data);
-
 struct bt_bap_notify {
 	unsigned int id;
 	struct bt_bap *bap;
@@ -144,35 +134,6 @@  struct bt_bap_notify {
 	void *user_data;
 };
 
-struct bt_bap {
-	int ref_count;
-	struct bt_bap_db *ldb;
-	struct bt_bap_db *rdb;
-	struct bt_gatt_client *client;
-	struct bt_att *att;
-	struct bt_bap_req *req;
-
-	unsigned int cp_id;
-	unsigned int process_id;
-	unsigned int disconn_id;
-	unsigned int idle_id;
-
-	struct queue *reqs;
-	struct queue *notify;
-	struct queue *streams;
-	struct queue *local_eps;
-	struct queue *remote_eps;
-
-	struct queue *pac_cbs;
-	struct queue *ready_cbs;
-	struct queue *state_cbs;
-
-	bt_bap_debug_func_t debug_func;
-	bt_bap_destroy_func_t debug_destroy;
-	void *debug_data;
-	void *user_data;
-};
-
 struct bt_bap_pac {
 	struct bt_bap_db *bdb;
 	char *name;
@@ -569,7 +530,7 @@  static void bap_disconnected(int err, void *user_data)
 	bt_bap_detach(bap);
 }
 
-static struct bt_bap *bap_get_session(struct bt_att *att, struct gatt_db *db)
+struct bt_bap *bap_get_session(struct bt_att *att, struct gatt_db *db)
 {
 	const struct queue_entry *entry;
 	struct bt_bap *bap;
@@ -2189,6 +2150,7 @@  static struct bt_bap_db *bap_db_new(struct gatt_db *db)
 	bdb->db = gatt_db_ref(db);
 	bdb->sinks = queue_new();
 	bdb->sources = queue_new();
+	bdb->bass_bcast_srcs = queue_new();
 
 	if (!bap_db)
 		bap_db = queue_new();
@@ -2199,6 +2161,9 @@  static struct bt_bap_db *bap_db_new(struct gatt_db *db)
 	bdb->ascs = ascs_new(db);
 	bdb->ascs->bdb = bdb;
 
+	bdb->bass = bass_new(db);
+	bdb->bass->bdb = bdb;
+
 	queue_push_tail(bap_db, bdb);
 
 	return bdb;
@@ -2518,10 +2483,12 @@  static void bap_db_free(void *data)
 
 	queue_destroy(bdb->sinks, bap_pac_free);
 	queue_destroy(bdb->sources, bap_pac_free);
+	queue_destroy(bdb->bass_bcast_srcs, bass_bcast_src_free);
 	gatt_db_unref(bdb->db);
 
 	free(bdb->pacs);
 	free(bdb->ascs);
+	free(bdb->bass);
 	free(bdb);
 }
 
@@ -2669,6 +2636,7 @@  struct bt_bap *bt_bap_new(struct gatt_db *ldb, struct gatt_db *rdb)
 	bdb->db = gatt_db_ref(rdb);
 	bdb->sinks = queue_new();
 	bdb->sources = queue_new();
+	bdb->bass_bcast_srcs = queue_new();
 
 	bap->rdb = bdb;
 	bap->remote_eps = queue_new();
@@ -3382,7 +3350,7 @@  static void bap_notify_destroy(void *data)
 		free(notify);
 }
 
-static unsigned int bap_register_notify(struct bt_bap *bap,
+unsigned int bap_register_notify(struct bt_bap *bap,
 					uint16_t value_handle,
 					bap_notify_t func,
 					void *user_data)
@@ -3835,6 +3803,9 @@  clone:
 	bt_uuid16_create(&uuid, ASCS_UUID);
 	gatt_db_foreach_service(bap->rdb->db, &uuid, foreach_ascs_service, bap);
 
+	bt_uuid16_create(&uuid, BASS_UUID);
+	gatt_db_foreach_service(bap->rdb->db, &uuid, foreach_bass_service, bap);
+
 	return true;
 }
 
diff --git a/src/shared/bap.h b/src/shared/bap.h
index bd13abef9..44b0d1535 100644
--- a/src/shared/bap.h
+++ b/src/shared/bap.h
@@ -4,6 +4,7 @@ 
  *  BlueZ - Bluetooth protocol stack for Linux
  *
  *  Copyright (C) 2022  Intel Corporation. All rights reserved.
+ *  Copyright 2023 NXP
  *
  */
 
@@ -62,9 +63,49 @@  struct bt_bap_qos {
 	uint8_t  target_latency;	/* Target Latency */
 };
 
+struct bt_bap_db {
+	struct gatt_db *db;
+	struct bt_pacs *pacs;
+	struct bt_ascs *ascs;
+	struct bt_bass *bass;
+	struct queue *sinks;
+	struct queue *sources;
+	struct queue *bass_bcast_srcs;
+};
+
 typedef void (*bt_bap_ready_func_t)(struct bt_bap *bap, void *user_data);
 typedef void (*bt_bap_destroy_func_t)(void *user_data);
 typedef void (*bt_bap_debug_func_t)(const char *str, void *user_data);
+
+struct bt_bap {
+	int ref_count;
+	struct bt_bap_db *ldb;
+	struct bt_bap_db *rdb;
+	struct bt_gatt_client *client;
+	struct bt_att *att;
+	struct bt_bap_req *req;
+
+	unsigned int cp_id;
+	unsigned int process_id;
+	unsigned int disconn_id;
+	unsigned int idle_id;
+
+	struct queue *reqs;
+	struct queue *notify;
+	struct queue *streams;
+	struct queue *local_eps;
+	struct queue *remote_eps;
+
+	struct queue *pac_cbs;
+	struct queue *ready_cbs;
+	struct queue *state_cbs;
+
+	bt_bap_debug_func_t debug_func;
+	bt_bap_destroy_func_t debug_destroy;
+	void *debug_data;
+	void *user_data;
+};
+
 typedef void (*bt_bap_pac_func_t)(struct bt_bap_pac *pac, void *user_data);
 typedef bool (*bt_bap_pac_foreach_t)(struct bt_bap_pac *lpac,
 					struct bt_bap_pac *rpac,
@@ -86,6 +127,10 @@  typedef void (*bt_bap_stream_func_t)(struct bt_bap_stream *stream,
 					void *user_data);
 typedef void (*bt_bap_func_t)(struct bt_bap *bap, void *user_data);
 
+typedef void (*bap_notify_t)(struct bt_bap *bap, uint16_t value_handle,
+				const uint8_t *value, uint16_t length,
+				void *user_data);
+
 /* Local PAC related functions */
 struct bt_bap_pac_qos {
 	uint8_t  framing;
@@ -265,3 +310,9 @@  uint8_t bt_bap_stream_io_dir(struct bt_bap_stream *stream);
 
 int bt_bap_stream_io_connecting(struct bt_bap_stream *stream, int fd);
 bool bt_bap_stream_io_is_connecting(struct bt_bap_stream *stream, int *fd);
+
+struct bt_bap *bap_get_session(struct bt_att *att, struct gatt_db *db);
+unsigned int bap_register_notify(struct bt_bap *bap,
+					uint16_t value_handle,
+					bap_notify_t func,
+					void *user_data);
diff --git a/src/shared/bass.c b/src/shared/bass.c
new file mode 100644
index 000000000..1be36ef75
--- /dev/null
+++ b/src/shared/bass.c
@@ -0,0 +1,703 @@ 
+// SPDX-License-Identifier: LGPL-2.1-or-later
+/*
+ *
+ *  BlueZ - Bluetooth protocol stack for Linux
+ *
+ *  Copyright 2023 NXP
+ *
+ */
+
+#define _GNU_SOURCE
+#include <inttypes.h>
+#include <string.h>
+#include <stdlib.h>
+#include <stdbool.h>
+#include <unistd.h>
+#include <errno.h>
+#include <poll.h>
+
+#include "lib/bluetooth.h"
+#include "lib/uuid.h"
+#include "lib/iso.h"
+
+#include "src/shared/queue.h"
+#include "src/shared/util.h"
+#include "src/shared/att.h"
+#include "src/shared/gatt-db.h"
+#include "src/shared/gatt-client.h"
+#include "src/shared/bap.h"
+#include "src/shared/bass.h"
+
+#define DBG(_bap, fmt, arg...) \
+	bass_debug(_bap, "%s:%s() " fmt, __FILE__, __func__, ## arg)
+
+static void bass_debug(struct bt_bap *bap, const char *format, ...)
+{
+	va_list ap;
+
+	if (!bap || !format || !bap->debug_func)
+		return;
+
+	va_start(ap, format);
+	util_debug_va(bap->debug_func, bap->debug_data, format, ap);
+	va_end(ap);
+}
+
+static int
+bass_build_bcast_src_from_notif(struct bt_bcast_src *bcast_src,
+				const uint8_t *value, uint16_t length)
+{
+	struct bt_bass_subgroup_data *subgroup_data = NULL;
+	uint8_t *id;
+	uint8_t *addr_type;
+	uint8_t *addr;
+	uint8_t *sid;
+	uint8_t *bid;
+	uint8_t *pa_sync_state;
+	uint8_t *enc;
+	uint8_t *bad_code = NULL;
+	uint8_t *num_subgroups;
+	uint8_t *bis_sync_state;
+	uint8_t *meta_len;
+	uint8_t *meta;
+
+	struct iovec iov = {
+		.iov_base = (void *) value,
+		.iov_len = length,
+	};
+
+	/* Extract all fields from notification */
+	id = util_iov_pull_mem(&iov, sizeof(*id));
+	if (!id) {
+		DBG(bcast_src->bap, "Unable to parse Broadcast Receive State");
+		return -1;
+	}
+
+	addr_type = util_iov_pull_mem(&iov, sizeof(*addr_type));
+	if (!addr_type) {
+		DBG(bcast_src->bap, "Unable to parse Broadcast Receive State");
+		return -1;
+	}
+
+	addr = util_iov_pull_mem(&iov, sizeof(bdaddr_t));
+	if (!addr) {
+		DBG(bcast_src->bap, "Unable to parse Broadcast Receive State");
+		return -1;
+	}
+
+	sid = util_iov_pull_mem(&iov, sizeof(*sid));
+	if (!sid) {
+		DBG(bcast_src->bap, "Unable to parse Broadcast Receive State");
+		return -1;
+	}
+
+	bid = util_iov_pull_mem(&iov, 3);
+	if (!bid) {
+		DBG(bcast_src->bap, "Unable to parse Broadcast Receive State");
+		return -1;
+	}
+
+	pa_sync_state = util_iov_pull_mem(&iov, sizeof(*pa_sync_state));
+	if (!pa_sync_state) {
+		DBG(bcast_src->bap, "Unable to parse Broadcast Receive State");
+		return -1;
+	}
+
+	enc = util_iov_pull_mem(&iov, sizeof(*enc));
+	if (!enc) {
+		DBG(bcast_src->bap, "Unable to parse Broadcast Receive State");
+		return -1;
+	}
+
+	if (*enc == BT_BASS_BIG_ENC_STATE_BAD_CODE) {
+		bad_code = util_iov_pull_mem(&iov, BT_BASS_BCAST_CODE_SIZE);
+		if (!bad_code) {
+			DBG(bcast_src->bap, "Unable to parse "
+				"Broadcast Receive State");
+			return -1;
+		}
+	}
+
+	num_subgroups = util_iov_pull_mem(&iov, sizeof(*num_subgroups));
+	if (!num_subgroups) {
+		DBG(bcast_src->bap, "Unable to parse Broadcast Receive State");
+		return -1;
+	}
+
+	if (*num_subgroups == 0)
+		goto done;
+
+	subgroup_data = malloc((*num_subgroups) * sizeof(*subgroup_data));
+	if (!subgroup_data) {
+		DBG(bcast_src->bap, "Unable to allocate memory");
+		return -1;
+	}
+
+	memset(subgroup_data, 0, (*num_subgroups) * sizeof(*subgroup_data));
+
+	for (int i = 0; i < *num_subgroups; i++) {
+		bis_sync_state = util_iov_pull_mem(&iov,
+						sizeof(uint32_t));
+		if (!bis_sync_state) {
+			DBG(bcast_src->bap, "Unable to parse "
+				"Broadcast Receive State");
+
+			for (int j = 0; j < i; j++)
+				free(subgroup_data[j].meta);
+
+			free(subgroup_data);
+			return -1;
+		}
+
+		subgroup_data[i].bis_sync = get_le32(bis_sync_state);
+
+		meta_len = util_iov_pull_mem(&iov, sizeof(*meta_len));
+		if (!meta_len) {
+			DBG(bcast_src->bap, "Unable to parse "
+				"Broadcast Receive State");
+
+			for (int j = 0; j < i; j++)
+				free(subgroup_data[j].meta);
+
+			free(subgroup_data);
+			return -1;
+		}
+
+		subgroup_data[i].meta_len = *meta_len;
+
+		if (*meta_len == 0)
+			continue;
+
+		subgroup_data[i].meta = malloc(*meta_len);
+		if (!subgroup_data[i].meta) {
+			DBG(bcast_src->bap, "Unable to allocate memory");
+
+			for (int j = 0; j < i; j++)
+				free(subgroup_data[j].meta);
+
+			free(subgroup_data);
+			return -1;
+		}
+
+		meta = util_iov_pull_mem(&iov, *meta_len);
+		if (!meta) {
+			DBG(bcast_src->bap, "Unable to parse "
+				"Broadcast Receive State");
+
+			for (int j = 0; j < i; j++)
+				free(subgroup_data[j].meta);
+
+			free(subgroup_data);
+			return -1;
+		}
+
+		memcpy(subgroup_data[i].meta, meta, *meta_len);
+	}
+
+done:
+	/*
+	 * If no errors occurred, copy extracted fields into
+	 * the broadcast source structure
+	 */
+	if (bcast_src->subgroup_data) {
+		for (int i = 0; i < bcast_src->num_subgroups; i++)
+			free(bcast_src->subgroup_data[i].meta);
+
+		free(bcast_src->subgroup_data);
+	}
+
+	bcast_src->id = *id;
+	bcast_src->addr_type = *addr_type;
+	memcpy(&bcast_src->addr, addr, sizeof(bdaddr_t));
+	bcast_src->sid = *sid;
+	bcast_src->bid = get_le24(bid);
+	bcast_src->sync_state = *pa_sync_state;
+	bcast_src->enc = *enc;
+
+	if (*enc == BT_BASS_BIG_ENC_STATE_BAD_CODE)
+		memcpy(bcast_src->bad_code, bad_code, BT_BASS_BCAST_CODE_SIZE);
+	else
+		memset(bcast_src->bad_code, 0, BT_BASS_BCAST_CODE_SIZE);
+
+	bcast_src->num_subgroups = *num_subgroups;
+
+	bcast_src->subgroup_data = subgroup_data;
+
+	return 0;
+}
+
+static int
+bass_build_bcast_src_from_read_rsp(struct bt_bcast_src *bcast_src,
+				const uint8_t *value, uint16_t length)
+{
+	return bass_build_bcast_src_from_notif(bcast_src, value, length);
+}
+
+static uint8_t *bass_build_notif_from_bcast_src(struct bt_bcast_src *bcast_src,
+							size_t *notif_len)
+{
+	size_t len = 0;
+	uint8_t *notif = NULL;
+	struct iovec iov;
+
+	*notif_len = 0;
+
+	if (!bcast_src)
+		return NULL;
+
+	len = BT_BASS_BCAST_SRC_LEN + bcast_src->num_subgroups *
+			BT_BASS_BCAST_SRC_SUBGROUP_LEN;
+
+	if (bcast_src->enc == BT_BASS_BIG_ENC_STATE_BAD_CODE)
+		len += BT_BASS_BCAST_CODE_SIZE;
+
+	for (size_t i = 0; i < bcast_src->num_subgroups; i++) {
+		/* Add length for subgroup metadata */
+		len += bcast_src->subgroup_data[i].meta_len;
+	}
+
+	notif = malloc(len);
+	if (!notif)
+		return NULL;
+
+	memset(notif, 0, len);
+
+	iov.iov_base = notif;
+	iov.iov_len = 0;
+
+	util_iov_push_mem(&iov, sizeof(bcast_src->id),
+			&bcast_src->id);
+	util_iov_push_mem(&iov, sizeof(bcast_src->addr_type),
+			&bcast_src->addr_type);
+	util_iov_push_mem(&iov, sizeof(bcast_src->addr),
+			&bcast_src->addr);
+	util_iov_push_mem(&iov, sizeof(bcast_src->sid),
+			&bcast_src->sid);
+	util_iov_push_mem(&iov, 3, &bcast_src->bid);
+	util_iov_push_mem(&iov, sizeof(bcast_src->sync_state),
+			&bcast_src->sync_state);
+	util_iov_push_mem(&iov, sizeof(bcast_src->enc),
+			&bcast_src->enc);
+
+	if (bcast_src->enc == BT_BASS_BIG_ENC_STATE_BAD_CODE)
+		util_iov_push_mem(&iov, sizeof(bcast_src->bad_code),
+					bcast_src->bad_code);
+
+	util_iov_push_mem(&iov, sizeof(bcast_src->num_subgroups),
+				&bcast_src->num_subgroups);
+
+	for (size_t i = 0; i < bcast_src->num_subgroups; i++) {
+		/* Add subgroup bis_sync */
+		util_iov_push_mem(&iov,
+			sizeof(bcast_src->subgroup_data[i].bis_sync),
+			&bcast_src->subgroup_data[i].bis_sync);
+
+		/* Add subgroup meta_len */
+		util_iov_push_mem(&iov,
+			sizeof(bcast_src->subgroup_data[i].meta_len),
+			&bcast_src->subgroup_data[i].meta_len);
+
+		/* Add subgroup metadata */
+		if (bcast_src->subgroup_data[i].meta_len > 0)
+			util_iov_push_mem(&iov,
+				bcast_src->subgroup_data[i].meta_len,
+				bcast_src->subgroup_data[i].meta);
+	}
+
+	*notif_len = len;
+	return notif;
+}
+
+static uint8_t *
+bass_build_read_rsp_from_bcast_src(struct bt_bcast_src *bcast_src,
+					size_t *rsp_len)
+{
+	return bass_build_notif_from_bcast_src(bcast_src, rsp_len);
+}
+
+static bool bass_check_cp_command_subgroup_data_len(uint8_t num_subgroups,
+							struct iovec *iov)
+{
+	uint32_t *bis_sync_state;
+	uint8_t *meta_len;
+	uint8_t *meta;
+
+	for (int i = 0; i < num_subgroups; i++) {
+		bis_sync_state = util_iov_pull_mem(iov,
+					sizeof(*bis_sync_state));
+		if (!bis_sync_state)
+			return false;
+
+		meta_len = util_iov_pull_mem(iov,
+					sizeof(*meta_len));
+		if (!meta_len)
+			return false;
+
+		meta = util_iov_pull_mem(iov, *meta_len);
+		if (!meta)
+			return false;
+	}
+
+	return true;
+}
+
+static bool bass_check_cp_command_len(struct iovec *iov)
+{
+	struct bt_bass_bcast_audio_scan_cp_hdr *hdr;
+	union {
+		struct bt_bass_add_src_params *add_src_params;
+		struct bt_bass_mod_src_params *mod_src_params;
+		struct bt_bass_set_bcast_code_params *set_bcast_code_params;
+		struct bt_bass_remove_src_params *remove_src_params;
+	} params;
+
+	/* Get command header */
+	hdr = util_iov_pull_mem(iov, sizeof(*hdr));
+
+	if (!hdr)
+		return false;
+
+	/* Check command parameters */
+	switch (hdr->op) {
+	case BT_BASS_ADD_SRC:
+		params.add_src_params = util_iov_pull_mem(iov,
+						sizeof(*params.add_src_params));
+		if (!params.add_src_params)
+			return false;
+
+		if (!bass_check_cp_command_subgroup_data_len(
+					params.add_src_params->num_subgroups,
+					iov))
+			return false;
+
+		break;
+	case BT_BASS_MOD_SRC:
+		params.mod_src_params = util_iov_pull_mem(iov,
+						sizeof(*params.mod_src_params));
+		if (!params.mod_src_params)
+			return false;
+
+		if (!bass_check_cp_command_subgroup_data_len(
+					params.mod_src_params->num_subgroups,
+					iov))
+			return false;
+
+		break;
+	case BT_BASS_SET_BCAST_CODE:
+		params.set_bcast_code_params = util_iov_pull_mem(iov,
+					sizeof(*params.set_bcast_code_params));
+		if (!params.set_bcast_code_params)
+			return false;
+
+		break;
+	case BT_BASS_REMOVE_SRC:
+		params.remove_src_params = util_iov_pull_mem(iov,
+					sizeof(*params.remove_src_params));
+		if (!params.remove_src_params)
+			return false;
+
+		break;
+	case BT_BASS_REMOTE_SCAN_STOPPED:
+	case BT_BASS_REMOTE_SCAN_STARTED:
+		break;
+	default:
+		return true;
+	}
+
+	if (iov->iov_len > 0)
+		return false;
+
+	return true;
+}
+
+static void bass_bcast_audio_scan_cp_write(struct gatt_db_attribute *attrib,
+				unsigned int id, uint16_t offset,
+				const uint8_t *value, size_t len,
+				uint8_t opcode, struct bt_att *att,
+				void *user_data)
+{
+	struct iovec iov = {
+		.iov_base = (void *)value,
+		.iov_len = len,
+	};
+
+	/* Validate written command length */
+	if (!bass_check_cp_command_len(&iov)) {
+		if (opcode == BT_ATT_OP_WRITE_REQ) {
+			gatt_db_attribute_write_result(attrib, id,
+					BT_ERROR_WRITE_REQUEST_REJECTED);
+		}
+		return;
+	}
+
+	/* TODO: Implement handlers for the written opcodes */
+	gatt_db_attribute_write_result(attrib, id,
+			BT_BASS_ERROR_OPCODE_NOT_SUPPORTED);
+}
+
+static bool bass_src_match_attrib(const void *data, const void *match_data)
+{
+	const struct bt_bcast_src *bcast_src = data;
+	const struct gatt_db_attribute *attr = match_data;
+
+	return (bcast_src->attr == attr);
+}
+
+static void bass_bcast_recv_state_read(struct gatt_db_attribute *attrib,
+					unsigned int id, uint16_t offset,
+					uint8_t opcode, struct bt_att *att,
+					void *user_data)
+{
+	struct bt_bass *bass = user_data;
+	struct bt_bap *bap = bap_get_session(att, bass->bdb->db);
+	uint8_t *rsp;
+	size_t rsp_len;
+	struct bt_bcast_src *bcast_src;
+
+	bcast_src = queue_find(bap->ldb->bass_bcast_srcs,
+					bass_src_match_attrib,
+					attrib);
+
+	if (!bcast_src) {
+		gatt_db_attribute_read_result(attrib, id, 0, NULL,
+							0);
+		return;
+	}
+
+	/* Build read response */
+	rsp = bass_build_read_rsp_from_bcast_src(bcast_src, &rsp_len);
+
+	if (!rsp) {
+		gatt_db_attribute_read_result(attrib, id,
+					BT_ATT_ERROR_UNLIKELY,
+					NULL, 0);
+		return;
+	}
+
+	gatt_db_attribute_read_result(attrib, id, 0, (void *)rsp,
+						rsp_len);
+
+	free(rsp);
+}
+
+static void bcast_recv_new(struct bt_bass *bass, int i)
+{
+	struct bt_bcast_recv_state *bcast_recv_state;
+	bt_uuid_t uuid;
+
+	if (!bass)
+		return;
+
+	bcast_recv_state = new0(struct bt_bcast_recv_state, 1);
+	bcast_recv_state->bass = bass;
+
+	bt_uuid16_create(&uuid, BCAST_RECV_STATE_UUID);
+	bcast_recv_state->attr =
+		gatt_db_service_add_characteristic(bass->service, &uuid,
+				BT_ATT_PERM_READ | BT_ATT_PERM_READ_ENCRYPT,
+				BT_GATT_CHRC_PROP_READ |
+				BT_GATT_CHRC_PROP_NOTIFY,
+				bass_bcast_recv_state_read, NULL,
+				bass);
+
+	bcast_recv_state->ccc = gatt_db_service_add_ccc(bass->service,
+					BT_ATT_PERM_READ | BT_ATT_PERM_WRITE);
+
+	bass->bcast_recv_states[i] = bcast_recv_state;
+}
+
+struct bt_bass *bass_new(struct gatt_db *db)
+{
+	struct bt_bass *bass;
+	bt_uuid_t uuid;
+	int i;
+
+	if (!db)
+		return NULL;
+
+	bass = new0(struct bt_bass, 1);
+
+	/* Populate DB with BASS attributes */
+	bt_uuid16_create(&uuid, BASS_UUID);
+	bass->service = gatt_db_add_service(db, &uuid, true,
+					3 + (NUM_BCAST_RECV_STATES * 3));
+
+	for (i = 0; i < NUM_BCAST_RECV_STATES; i++)
+		bcast_recv_new(bass, i);
+
+	bt_uuid16_create(&uuid, BCAST_AUDIO_SCAN_CP_UUID);
+	bass->bcast_audio_scan_cp =
+		gatt_db_service_add_characteristic(bass->service,
+				&uuid,
+				BT_ATT_PERM_WRITE | BT_ATT_PERM_WRITE_ENCRYPT,
+				BT_GATT_CHRC_PROP_WRITE,
+				NULL, bass_bcast_audio_scan_cp_write,
+				bass);
+
+	gatt_db_service_set_active(bass->service, true);
+
+	return bass;
+}
+
+void bass_bcast_src_free(void *data)
+{
+	struct bt_bcast_src *bcast_src = data;
+
+	for (int i = 0; i < bcast_src->num_subgroups; i++)
+		free(bcast_src->subgroup_data[i].meta);
+
+	free(bcast_src->subgroup_data);
+	free(bcast_src);
+}
+
+static void read_bcast_recv_state(bool success, uint8_t att_ecode,
+				const uint8_t *value, uint16_t length,
+				void *user_data)
+{
+	struct bt_bcast_src *bcast_src = user_data;
+
+	if (!success) {
+		DBG(bcast_src->bap, "Unable to read "
+			"Broadcast Receive State: error 0x%02x",
+			att_ecode);
+		return;
+	}
+
+	if (length == 0) {
+		queue_remove(bcast_src->bap->rdb->bass_bcast_srcs, bcast_src);
+		bass_bcast_src_free(bcast_src);
+		return;
+	}
+
+	if (bass_build_bcast_src_from_read_rsp(bcast_src, value, length)) {
+		queue_remove(bcast_src->bap->rdb->bass_bcast_srcs, bcast_src);
+		bass_bcast_src_free(bcast_src);
+		return;
+	}
+}
+
+static void bcast_recv_state_notify(struct bt_bap *bap, uint16_t value_handle,
+				const uint8_t *value, uint16_t length,
+				void *user_data)
+{
+	struct gatt_db_attribute *attr = user_data;
+	struct bt_bcast_src *bcast_src;
+	bool new_src = false;
+
+	bcast_src = queue_find(bap->rdb->bass_bcast_srcs,
+					bass_src_match_attrib, attr);
+	if (!bcast_src) {
+		new_src = true;
+		bcast_src = malloc(sizeof(*bcast_src));
+
+		if (!bcast_src) {
+			DBG(bap, "Failed to allocate "
+				"memory for broadcast source");
+			return;
+		}
+
+		memset(bcast_src, 0, sizeof(struct bt_bcast_src));
+		bcast_src->bap = bap;
+		bcast_src->attr = attr;
+	}
+
+	if (bass_build_bcast_src_from_notif(bcast_src, value, length)
+						&& new_src) {
+		bass_bcast_src_free(bcast_src);
+		return;
+	}
+
+	if (new_src)
+		queue_push_tail(bap->rdb->bass_bcast_srcs, bcast_src);
+}
+
+static struct bt_bass *bap_get_bass(struct bt_bap *bap)
+{
+	if (!bap)
+		return NULL;
+
+	if (bap->rdb->bass)
+		return bap->rdb->bass;
+
+	bap->rdb->bass = new0(struct bt_bass, 1);
+	bap->rdb->bass->bdb = bap->rdb;
+
+	return bap->rdb->bass;
+}
+
+static void foreach_bass_char(struct gatt_db_attribute *attr, void *user_data)
+{
+	struct bt_bap *bap = user_data;
+	uint16_t value_handle;
+	bt_uuid_t uuid, uuid_bcast_audio_scan_cp, uuid_bcast_recv_state;
+	struct bt_bass *bass;
+
+	/* Get attribute value handle and uuid */
+	if (!gatt_db_attribute_get_char_data(attr, NULL, &value_handle,
+						NULL, NULL, &uuid))
+		return;
+
+	bt_uuid16_create(&uuid_bcast_audio_scan_cp, BCAST_AUDIO_SCAN_CP_UUID);
+	bt_uuid16_create(&uuid_bcast_recv_state, BCAST_RECV_STATE_UUID);
+
+	if (!bt_uuid_cmp(&uuid, &uuid_bcast_audio_scan_cp)) {
+		/* Found Broadcast Audio Scan Control Point characteristic */
+		bass = bap_get_bass(bap);
+
+		if (!bass || bass->bcast_audio_scan_cp)
+			return;
+
+		/* Store characteristic reference */
+		bass->bcast_audio_scan_cp = attr;
+
+		DBG(bap, "Broadcast Audio Scan Control Point "
+			"found: handle 0x%04x", value_handle);
+	}
+
+	if (!bt_uuid_cmp(&uuid, &uuid_bcast_recv_state)) {
+		/* Found Broadcast Receive State characteristic */
+		struct bt_bcast_src *bcast_src =
+				queue_find(bap->rdb->bass_bcast_srcs,
+						bass_src_match_attrib, attr);
+
+		if (!bcast_src) {
+			bcast_src = malloc(sizeof(struct bt_bcast_src));
+
+			if (bcast_src == NULL) {
+				DBG(bap, "Failed to allocate "
+					"memory for broadcast source");
+				return;
+			}
+
+			memset(bcast_src, 0, sizeof(struct bt_bcast_src));
+			bcast_src->bap = bap;
+			bcast_src->attr = attr;
+
+			queue_push_tail(bap->rdb->bass_bcast_srcs, bcast_src);
+		}
+
+		bt_gatt_client_read_value(bap->client, value_handle,
+						read_bcast_recv_state,
+						bcast_src, NULL);
+
+		(void)bap_register_notify(bap, value_handle,
+						bcast_recv_state_notify,
+						attr);
+
+		DBG(bap, "Broadcast Receive State found: handle 0x%04x",
+							value_handle);
+	}
+}
+
+void foreach_bass_service(struct gatt_db_attribute *attr,
+						void *user_data)
+{
+	struct bt_bap *bap = user_data;
+	struct bt_bass *bass = bap_get_bass(bap);
+
+	/* Store BASS attribute reference */
+	bass->service = attr;
+
+	/* Handle BASS attributes */
+	gatt_db_service_foreach_char(attr, foreach_bass_char, bap);
+}
diff --git a/src/shared/bass.h b/src/shared/bass.h
new file mode 100644
index 000000000..8edd73502
--- /dev/null
+++ b/src/shared/bass.h
@@ -0,0 +1,124 @@ 
+/* SPDX-License-Identifier: LGPL-2.1-or-later */
+/*
+ *
+ *  BlueZ - Bluetooth protocol stack for Linux
+ *
+ *  Copyright 2023 NXP
+ *
+ */
+
+#define NUM_BCAST_RECV_STATES				2
+#define BT_BASS_BCAST_CODE_SIZE				16
+#define BT_BASS_BIG_SYNC_FAILED_BITMASK			0xFFFFFFFF
+#define BT_BASS_BCAST_SRC_LEN				15
+#define BT_BASS_BCAST_SRC_SUBGROUP_LEN			5
+
+/* Application error codes */
+#define BT_BASS_ERROR_OPCODE_NOT_SUPPORTED		0x80
+#define BT_BASS_ERROR_INVALID_SOURCE_ID			0x81
+
+/* PA_Sync_State values */
+#define BT_BASS_NOT_SYNCHRONIZED_TO_PA			0x00
+#define BT_BASS_SYNC_INFO_RE				0x01
+#define BT_BASS_SYNCHRONIZED_TO_PA			0x02
+#define BT_BASS_FAILED_TO_SYNCHRONIZE_TO_PA		0x03
+#define BT_BASS_NO_PAST					0x04
+
+/* BIG_Encryption values */
+#define BT_BASS_BIG_ENC_STATE_NO_ENC			0x00
+#define BT_BASS_BIG_ENC_STATE_BCODE_REQ			0x01
+#define BT_BASS_BIG_ENC_STATE_DEC			0x02
+#define BT_BASS_BIG_ENC_STATE_BAD_CODE			0x03
+
+/* BASS subgroup field of the Broadcast
+ * Receive State characteristic
+ */
+struct bt_bass_subgroup_data {
+	uint32_t bis_sync;
+	uint32_t pending_bis_sync;
+	uint8_t meta_len;
+	uint8_t *meta;
+};
+
+/* BASS Broadcast Source structure */
+struct bt_bcast_src {
+	struct bt_bap *bap;
+	struct gatt_db_attribute *attr;
+	uint8_t id;
+	uint8_t addr_type;
+	bdaddr_t addr;
+	uint8_t sid;
+	uint32_t bid;
+	uint8_t sync_state;
+	uint8_t enc;
+	uint8_t bad_code[BT_BASS_BCAST_CODE_SIZE];
+	uint8_t num_subgroups;
+	struct bt_bass_subgroup_data *subgroup_data;
+};
+
+/* Broadcast Receive State characteristic structure */
+struct bt_bcast_recv_state {
+	struct bt_bass *bass;
+	struct gatt_db_attribute *attr;
+	struct gatt_db_attribute *ccc;
+};
+
+/* BASS instance structure */
+struct bt_bass {
+	struct bt_bap_db *bdb;
+	struct gatt_db_attribute *service;
+	struct gatt_db_attribute *bcast_audio_scan_cp;
+	struct bt_bcast_recv_state *bcast_recv_states[NUM_BCAST_RECV_STATES];
+};
+
+/* Broadcast Audio Scan Control Point
+ * header structure
+ */
+struct bt_bass_bcast_audio_scan_cp_hdr {
+	uint8_t op;
+} __packed;
+
+#define BT_BASS_REMOTE_SCAN_STOPPED			0x00
+
+#define BT_BASS_REMOTE_SCAN_STARTED			0x01
+
+#define BT_BASS_ADD_SRC					0x02
+
+struct bt_bass_add_src_params {
+	uint8_t addr_type;
+	bdaddr_t addr;
+	uint8_t sid;
+	uint8_t bid[3];
+	uint8_t pa_sync;
+	uint16_t pa_interval;
+	uint8_t num_subgroups;
+	uint8_t subgroup_data[];
+} __packed;
+
+#define BT_BASS_MOD_SRC					0x03
+
+struct bt_bass_mod_src_params {
+	uint8_t id;
+	uint8_t pa_sync;
+	uint16_t pa_interval;
+	uint8_t num_subgroups;
+	uint8_t subgroup_data[];
+} __packed;
+
+#define BT_BASS_SET_BCAST_CODE				0x04
+
+struct bt_bass_set_bcast_code_params {
+	uint8_t id;
+	uint8_t bcast_code[BT_BASS_BCAST_CODE_SIZE];
+} __packed;
+
+#define BT_BASS_REMOVE_SRC				0x05
+
+struct bt_bass_remove_src_params {
+	uint8_t id;
+} __packed;
+
+struct bt_bass *bass_new(struct gatt_db *db);
+void bass_bcast_src_free(void *data);
+void foreach_bass_service(struct gatt_db_attribute *attr,
+						void *user_data);