nimble/audio: Add LE Audio event listener

This adds dedicated event listener for LE Audio events.
diff --git a/nimble/host/audio/include/host/audio/ble_audio.h b/nimble/host/audio/include/host/audio/ble_audio.h
index 52d1e8a..7d4200b 100644
--- a/nimble/host/audio/include/host/audio/ble_audio.h
+++ b/nimble/host/audio/include/host/audio/ble_audio.h
@@ -21,9 +21,147 @@
 #define H_BLE_AUDIO_
 
 #include <stdint.h>
+#include <sys/queue.h>
 
 #include "host/ble_audio_common.h"
 
+/** @brief Public Broadcast Announcement features bits */
+enum ble_audio_pub_bcst_announcement_feat {
+    /** Broadcast Stream Encryption */
+    BLE_AUDIO_PUB_BCST_ANNOUNCEMENT_FEAT_ENCRYPTION = 1 << 0,
+
+    /** Standard Quality Public Broadcast Audio */
+    BLE_AUDIO_PUB_BCST_ANNOUNCEMENT_FEAT_SQ = 1 << 1,
+
+    /** High Quality Public Broadcast Audio */
+    BLE_AUDIO_PUB_BCST_ANNOUNCEMENT_FEAT_HQ = 1 << 2,
+};
+
+/** @brief Public Broadcast Announcement structure */
+struct ble_audio_pub_bcst_announcement {
+    /** Public Broadcast Announcement features bitfield */
+    enum ble_audio_pub_bcst_announcement_feat features;
+
+    /** Metadata length */
+    uint8_t metadata_len;
+
+    /** Metadata */
+    const uint8_t *metadata;
+};
+
+struct ble_audio_bcst_name {
+    /** Broadcast Name length */
+    uint8_t name_len;
+
+    /** Broadcast Name */
+    const char *name;
+};
+
+/**
+ * @defgroup ble_audio_events Bluetooth Low Energy Audio Events
+ * @{
+ */
+
+/** BLE Audio event: Broadcast Announcement */
+#define BLE_AUDIO_EVENT_BCST_ANNOUNCEMENT               0
+
+/** @} */
+
+/** @brief Broadcast Announcement */
+struct ble_audio_event_bcst_announcement {
+    /** Extended advertising report */
+    const struct ble_gap_ext_disc_desc *ext_disc;
+
+    /** Broadcast ID */
+    uint32_t broadcast_id;
+
+    /** Additional service data included in Broadcast Audio Announcement */
+    const uint8_t *svc_data;
+
+    /** Additional service data length  */
+    uint16_t svc_data_len;
+
+    /** Optional Public Broadcast Announcement data */
+    struct ble_audio_pub_bcst_announcement *pub_announcement_data;
+
+    /** Optional Broadcast Name */
+    struct ble_audio_bcst_name *name;
+};
+
+/**
+ * Represents a BLE Audio related event. When such an event occurs, the host
+ * notifies the application by passing an instance of this structure to an
+ * application-specified callback.
+ */
+struct ble_audio_event {
+    /**
+     * Indicates the type of BLE Audio event that occurred. This is one of the
+     * BLE_AUDIO_EVENT codes.
+     */
+    uint8_t type;
+
+    /**
+     * A discriminated union containing additional details concerning the event.
+     * The 'type' field indicates which member of the union is valid.
+     */
+    union {
+        /**
+         * @ref BLE_AUDIO_EVENT_BCST_ANNOUNCEMENT
+         *
+         * Represents a received Broadcast Announcement.
+         */
+        struct ble_audio_event_bcst_announcement bcst_announcement;
+    };
+};
+
+/** Callback function type for handling BLE Audio events. */
+typedef int ble_audio_event_fn(struct ble_audio_event *event, void *arg);
+
+/**
+ * Event listener structure
+ *
+ * This should be used as an opaque structure and not modified manually.
+ */
+struct ble_audio_event_listener {
+    /** The function to call when a BLE Audio event occurs. */
+    ble_audio_event_fn *fn;
+
+    /** An optional argument to pass to the event handler function. */
+    void *arg;
+
+    /** Singly-linked list entry. */
+    SLIST_ENTRY(ble_audio_event_listener) next;
+};
+
+/**
+ * Registers listener for BLE Audio events
+ *
+ * On success listener structure will be initialized automatically and does not
+ * need to be initialized prior to calling this function. To change callback
+ * and/or argument unregister listener first and register it again.
+ *
+ * @param[in] listener          Listener structure
+ * @param[in] event_mask        Optional event mask
+ * @param[in] fn                Callback function
+ * @param[in] arg               Optional callback argument
+ *
+ * @return                      0 on success
+ *                              BLE_HS_EINVAL if no callback is specified
+ *                              BLE_HS_EALREADY if listener is already registered
+ */
+int ble_audio_event_listener_register(struct ble_audio_event_listener *listener,
+                                      ble_audio_event_fn *fn, void *arg);
+
+/**
+ * Unregisters listener for BLE Audio events
+ *
+ * @param[in] listener          Listener structure
+ *
+ * @return                      0 on success
+ *                              BLE_HS_ENOENT if listener was not registered
+ */
+int ble_audio_event_listener_unregister(struct ble_audio_event_listener *listener);
+
 /**
  * BASE iterator
  *
diff --git a/nimble/host/audio/src/ble_audio.c b/nimble/host/audio/src/ble_audio.c
index 2ac5c68..61c42e4 100644
--- a/nimble/host/audio/src/ble_audio.c
+++ b/nimble/host/audio/src/ble_audio.c
@@ -23,6 +23,238 @@
 #include "host/ble_hs.h"
 #include "host/audio/ble_audio.h"
 
+#include "ble_audio_priv.h"
+
+static struct ble_gap_event_listener ble_audio_gap_event_listener;
+static SLIST_HEAD(, ble_audio_event_listener) ble_audio_event_listener_list =
+    SLIST_HEAD_INITIALIZER(ble_audio_event_listener_list);
+
+struct ble_audio_adv_parse_bcst_announcement_data {
+    struct ble_audio_event event;
+    struct ble_audio_pub_bcst_announcement pub;
+    struct ble_audio_bcst_name name;
+    bool success;
+};
+
+static int
+ble_audio_adv_parse_bcst_announcement(const struct ble_hs_adv_field *field,
+                                      void *user_data)
+{
+    struct ble_audio_adv_parse_bcst_announcement_data *data = user_data;
+    struct ble_audio_event_bcst_announcement *event;
+    const uint8_t value_len = field->length - sizeof(field->length);
+    ble_uuid16_t uuid16 = BLE_UUID16_INIT(0);
+    uint8_t offset = 0;
+
+    event = &data->event.bcst_announcement;
+
+    data->success = false;
+
+    switch (field->type) {
+    case BLE_HS_ADV_TYPE_SVC_DATA_UUID16:
+        if (value_len < 2) {
+            break;
+        }
+
+        uuid16.value = get_le16(&field->value[offset]);
+        offset += 2;
+
+        switch (uuid16.value) {
+        case BLE_BROADCAST_AUDIO_ANNOUNCEMENT_SVC_UUID:
+            if ((value_len - offset) < 3) {
+                /* Stop parsing */
+                data->success = false;
+                return 0;
+            }
+
+            event->broadcast_id = get_le24(&field->value[offset]);
+            offset += 3;
+
+            if (value_len > offset) {
+                event->svc_data = &field->value[offset];
+                event->svc_data_len = value_len - offset;
+            }
+
+            data->success = true;
+            break;
+
+        case BLE_BROADCAST_PUB_ANNOUNCEMENT_SVC_UUID:
+            if (event->pub_announcement_data != NULL) {
+                /* Ignore */
+                break;
+            }
+
+            if ((value_len - offset) < 2) {
+                /* Stop parsing */
+                data->success = false;
+                return 0;
+            }
+
+            data->pub.features = field->value[offset++];
+            data->pub.metadata_len = field->value[offset++];
+
+            if ((value_len - offset) < data->pub.metadata_len) {
+                break;
+            }
+
+            data->pub.metadata = &field->value[offset];
+
+            event->pub_announcement_data = &data->pub;
+            break;
+
+        default:
+            break;
+        }
+
+        break;
+
+    case BLE_HS_ADV_TYPE_BROADCAST_NAME:
+        if (event->name != NULL) {
+            /* Ignore */
+            break;
+        }
+
+        if (value_len < 4 || value_len > 32) {
+            /* Stop parsing */
+            data->success = false;
+            return 0;
+        }
+
+        data->name.name = (char *)field->value;
+        data->name.name_len = value_len;
+
+        event->name = &data->name;
+        break;
+
+    default:
+        break;
+    }
+
+    /* Continue parsing */
+    return BLE_HS_ENOENT;
+}
+
+static int
+ble_audio_gap_event(struct ble_gap_event *gap_event, void *arg)
+{
+    switch (gap_event->type) {
+    case BLE_GAP_EVENT_EXT_DISC: {
+        struct ble_audio_adv_parse_bcst_announcement_data data = { 0 };
+        int rc;
+
+        rc = ble_hs_adv_parse(gap_event->ext_disc.data,
+                              gap_event->ext_disc.length_data,
+                              ble_audio_adv_parse_bcst_announcement, &data);
+        if (rc == 0 && data.success) {
+            data.event.type = BLE_AUDIO_EVENT_BCST_ANNOUNCEMENT;
+            data.event.bcst_announcement.ext_disc = &gap_event->ext_disc;
+
+            (void)ble_audio_event_listener_call(&data.event);
+        }
+        break;
+    }
+
+    default:
+        break;
+    }
+
+    return 0;
+}
+
+int
+ble_audio_event_listener_register(struct ble_audio_event_listener *listener,
+                                  ble_audio_event_fn *fn, void *arg)
+{
+    struct ble_audio_event_listener *evl = NULL;
+    int rc;
+
+    if (listener == NULL) {
+        BLE_HS_LOG_ERROR("NULL listener!\n");
+        return BLE_HS_EINVAL;
+    }
+
+    if (fn == NULL) {
+        BLE_HS_LOG_ERROR("NULL fn!\n");
+        return BLE_HS_EINVAL;
+    }
+
+    SLIST_FOREACH(evl, &ble_audio_event_listener_list, next) {
+        if (evl == listener) {
+            break;
+        }
+    }
+
+    if (evl) {
+        return BLE_HS_EALREADY;
+    }
+
+    if (SLIST_EMPTY(&ble_audio_event_listener_list)) {
+        rc = ble_gap_event_listener_register(
+                &ble_audio_gap_event_listener,
+                ble_audio_gap_event, NULL);
+        if (rc != 0) {
+            return rc;
+        }
+    }
+
+    memset(listener, 0, sizeof(*listener));
+    listener->fn = fn;
+    listener->arg = arg;
+    SLIST_INSERT_HEAD(&ble_audio_event_listener_list, listener, next);
+
+    return 0;
+}
+
+int
+ble_audio_event_listener_unregister(struct ble_audio_event_listener *listener)
+{
+    struct ble_audio_event_listener *evl = NULL;
+    int rc;
+
+    if (listener == NULL) {
+        BLE_HS_LOG_ERROR("NULL listener!\n");
+        return BLE_HS_EINVAL;
+    }
+
+    /* We check if element exists on the list only for sanity to let caller
+     * know whether it registered its listener before.
+     */
+    SLIST_FOREACH(evl, &ble_audio_event_listener_list, next) {
+        if (evl == listener) {
+            break;
+        }
+    }
+
+    if (!evl) {
+        return BLE_HS_ENOENT;
+    }
+
+    SLIST_REMOVE(&ble_audio_event_listener_list, listener,
+                 ble_audio_event_listener, next);
+
+    if (SLIST_EMPTY(&ble_audio_event_listener_list)) {
+        rc = ble_gap_event_listener_unregister(
+                &ble_audio_gap_event_listener);
+        if (rc != 0) {
+            return rc;
+        }
+    }
+
+    return 0;
+}
+
+int
+ble_audio_event_listener_call(struct ble_audio_event *event)
+{
+    struct ble_audio_event_listener *evl = NULL;
+
+    SLIST_FOREACH(evl, &ble_audio_event_listener_list, next) {
+        evl->fn(event, evl->arg);
+    }
+
+    return 0;
+}
+
 /* Get the next subgroup data pointer */
 static const uint8_t *
 ble_audio_base_subgroup_next(uint8_t num_bis, const uint8_t *data,
diff --git a/nimble/host/audio/src/ble_audio_priv.h b/nimble/host/audio/src/ble_audio_priv.h
new file mode 100644
index 0000000..bedbdaf
--- /dev/null
+++ b/nimble/host/audio/src/ble_audio_priv.h
@@ -0,0 +1,27 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *  http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+#ifndef H_BLE_AUDIO_PRIV_
+#define H_BLE_AUDIO_PRIV_
+
+#include "host/audio/ble_audio.h"
+
+int ble_audio_event_listener_call(struct ble_audio_event *event);
+
+#endif /* H_BLE_AUDIO_PRIV_ */
diff --git a/nimble/host/audio/test/src/ble_audio_test.c b/nimble/host/audio/test/src/ble_audio_test.c
index 14fcf15..91e4a91 100644
--- a/nimble/host/audio/test/src/ble_audio_test.c
+++ b/nimble/host/audio/test/src/ble_audio_test.c
@@ -21,10 +21,12 @@
 #include "testutil/testutil.h"
 
 TEST_SUITE_DECL(ble_audio_base_parse_test_suite);
+TEST_CASE_DECL(ble_audio_listener_register_test);
 
 TEST_SUITE(ble_audio_test)
 {
     ble_audio_base_parse_test_suite();
+    ble_audio_listener_register_test();
 }
 
 int
diff --git a/nimble/host/audio/test/src/testcases/ble_audio_listener_register_test.c b/nimble/host/audio/test/src/testcases/ble_audio_listener_register_test.c
new file mode 100644
index 0000000..f55b51a
--- /dev/null
+++ b/nimble/host/audio/test/src/testcases/ble_audio_listener_register_test.c
@@ -0,0 +1,59 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *  http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+#include "testutil/testutil.h"
+
+#include "host/ble_hs.h"
+#include "host/audio/ble_audio.h"
+
+static struct ble_audio_event_listener event_listener;
+
+static int
+event_handler(struct ble_audio_event *event, void *arg)
+{
+    return 0;
+}
+
+TEST_CASE_SELF(ble_audio_listener_register_test)
+{
+    int rc;
+
+    rc = ble_audio_event_listener_register(&event_listener, event_handler,
+                                           NULL);
+    TEST_ASSERT(rc == 0);
+
+    rc = ble_audio_event_listener_register(&event_listener, event_handler,
+    NULL);
+    TEST_ASSERT(rc != 0);
+
+    rc = ble_audio_event_listener_unregister(&event_listener);
+    TEST_ASSERT(rc == 0);
+
+    rc = ble_audio_event_listener_register(NULL, event_handler, NULL);
+    TEST_ASSERT(rc != 0);
+
+    rc = ble_audio_event_listener_register(&event_listener, NULL, NULL);
+    TEST_ASSERT(rc != 0);
+
+    rc = ble_audio_event_listener_unregister(NULL);
+    TEST_ASSERT(rc != 0);
+
+    rc = ble_audio_event_listener_unregister(&event_listener);
+    TEST_ASSERT(rc != 0);
+}
diff --git a/nimble/host/audio/test/syscfg.yml b/nimble/host/audio/test/syscfg.yml
index 3c76059..7fad93f 100644
--- a/nimble/host/audio/test/syscfg.yml
+++ b/nimble/host/audio/test/syscfg.yml
@@ -27,3 +27,5 @@
 
   BLE_VERSION: 54
   BLE_HS_DEBUG: 1
+
+  BLE_EXT_ADV: 1
diff --git a/nimble/host/include/host/ble_audio_common.h b/nimble/host/include/host/ble_audio_common.h
index 815d3f4..05780c0 100644
--- a/nimble/host/include/host/ble_audio_common.h
+++ b/nimble/host/include/host/ble_audio_common.h
@@ -67,7 +67,9 @@
 
 /** Broadcast Audio Announcement Service UUID. */
 #define BLE_BROADCAST_AUDIO_ANNOUNCEMENT_SVC_UUID                 0x1852
-#define BLE_BROADCAST_PUBLIC_BROADCAST_ANNOUNCEMENT_SVC_UUID      0x1856
+
+/** Public Broadcast Announcement Service UUID. */
+#define BLE_BROADCAST_PUB_ANNOUNCEMENT_SVC_UUID                   0x1856
 
 /**
  * @defgroup ble_audio_sampling_rates Bluetooth Low Energy Audio Sampling Rates
diff --git a/nimble/host/services/auracast/src/ble_svc_auracast.c b/nimble/host/services/auracast/src/ble_svc_auracast.c
index b1bc7d3..7bd9112 100644
--- a/nimble/host/services/auracast/src/ble_svc_auracast.c
+++ b/nimble/host/services/auracast/src/ble_svc_auracast.c
@@ -62,7 +62,7 @@
     auracast_svc_data[data_offset] = BLE_HS_ADV_TYPE_SVC_DATA_UUID16;
     data_offset++;
     put_le16(&auracast_svc_data[data_offset],
-             BLE_BROADCAST_PUBLIC_BROADCAST_ANNOUNCEMENT_SVC_UUID);
+             BLE_BROADCAST_PUB_ANNOUNCEMENT_SVC_UUID);
     data_offset += 2;
     auracast_svc_data[data_offset] = features;
     data_offset++;