This is an automated email from the ASF dual-hosted git repository.

kopyscinski pushed a commit to branch master
in repository https://gitbox.apache.org/repos/asf/mynewt-nimble.git


The following commit(s) were added to refs/heads/master by this push:
     new 4ebbbf287 nimble/host: Add Broadcast Audio Scan Service
4ebbbf287 is described below

commit 4ebbbf287b85f019e988a66d45c6d6f7f72a83e7
Author: Krzysztof Kopyściński <[email protected]>
AuthorDate: Fri Feb 9 14:55:20 2024 +0100

    nimble/host: Add Broadcast Audio Scan Service
    
    BASS initial implementation
---
 nimble/host/audio/include/audio/ble_audio.h        |  82 ++
 .../include/services/bass/ble_audio_svc_bass.h     | 466 +++++++++++
 nimble/host/audio/services/bass/pkg.yml            |  33 +
 .../audio/services/bass/src/ble_audio_svc_bass.c   | 890 +++++++++++++++++++++
 nimble/host/audio/services/bass/syscfg.yml         |  38 +
 nimble/host/include/host/ble_att.h                 |  12 +
 6 files changed, 1521 insertions(+)

diff --git a/nimble/host/audio/include/audio/ble_audio.h 
b/nimble/host/audio/include/audio/ble_audio.h
index 05a583be2..836a1ab53 100644
--- a/nimble/host/audio/include/audio/ble_audio.h
+++ b/nimble/host/audio/include/audio/ble_audio.h
@@ -65,6 +65,9 @@
  * @endcond
  */
 
+/** Broadcast Audio Broadcast Code Size. */
+#define BLE_AUDIO_BROADCAST_CODE_SIZE                             16
+
 /** Broadcast Audio Announcement Service UUID. */
 #define BLE_BROADCAST_AUDIO_ANNOUNCEMENT_SVC_UUID                 0x1852
 
@@ -561,6 +564,18 @@ struct ble_audio_broadcast_name {
 /** BLE Audio event: Codec Unregistered */
 #define BLE_AUDIO_EVENT_CODEC_UNREGISTERED                   2
 
+/** BLE Audio event: BASS - Remote Scan Stopped */
+#define BLE_AUDIO_EVENT_BASS_REMOTE_SCAN_STOPPED             3
+
+/** BLE Audio event: BASS - Remote Scan Started */
+#define BLE_AUDIO_EVENT_BASS_REMOTE_SCAN_STARTED             4
+
+/** BLE Audio event: BASS - Operation status */
+#define BLE_AUDIO_EVENT_BASS_OPERATION_STATUS                5
+
+/** BLE Audio event: BASS - Set Broadcast Code */
+#define BLE_AUDIO_EVENT_BASS_BROADCAST_CODE_SET              6
+
 /** @} */
 
 /** @brief Broadcast Announcement */
@@ -596,6 +611,45 @@ struct ble_audio_event_codec_unregistered {
     const struct ble_audio_codec_record *record;
 };
 
+/** @brief BASS Source Removed */
+struct ble_audio_event_bass_remote_scan {
+    /** Connection Handle of Broadcast Assistant that is performing Scan 
procedure for us */
+    uint16_t conn_handle;
+};
+
+/** BASS Operation status OP code */
+enum ble_audio_event_bass_operation_statu_op {
+    /** BLE Audio event: BASS - Add Source */
+    BLE_AUDIO_EVENT_BASS_SOURCE_ADDED,
+
+    /** BLE Audio event: BASS - Modify Source */
+    BLE_AUDIO_EVENT_BASS_SOURCE_MODIFIED,
+
+    /** BLE Audio event: BASS - Remove Source */
+    BLE_AUDIO_EVENT_BASS_SOURCE_REMOVED,
+};
+
+/** @brief BASS operation status */
+struct ble_audio_event_bass_op_status {
+    /** Source ID */
+    uint8_t source_id;
+
+    /** Operation */
+    enum ble_audio_event_bass_operation_statu_op op;
+
+    /** Event status. 0 on success, BLE_HS Error code otherwise */
+    uint8_t status;
+};
+
+/** @brief BASS Set Broadcast Code */
+struct ble_audio_event_bass_set_broadcast_code {
+    /** Source ID */
+    uint8_t source_id;
+
+    /** Source ID */
+    uint8_t broadcast_code[BLE_AUDIO_BROADCAST_CODE_SIZE];
+};
+
 /**
  * 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
@@ -633,6 +687,34 @@ struct ble_audio_event {
          * Represents a codec registration.
          */
         struct ble_audio_event_codec_unregistered codec_unregistered;
+
+        /**
+         * @ref BLE_AUDIO_EVENT_BASS_REMOTE_SCAN_STOPPED
+         *
+         * Represents a Scan procedure termination by Broadcast Assistant.
+         */
+        struct ble_audio_event_bass_remote_scan remote_scan_stopped;
+
+        /**
+         * @ref BLE_AUDIO_EVENT_BASS_REMOTE_SCAN_STARTED
+         *
+         * Represents a Scan procedure start by Broadcast Assistant.
+         */
+        struct ble_audio_event_bass_remote_scan remote_scan_started;
+
+        /**
+         * @ref BLE_AUDIO_EVENT_BASS_SOURCE_ADDED
+         *
+         * Represents a Broadcast Source being added to BASS.
+         */
+        struct ble_audio_event_bass_op_status bass_operation_status;
+
+        /**
+         * @ref BLE_AUDIO_EVENT_BASS_BROADCAST_CODE_SET
+         *
+         * Represents a Broadcast Code baing set in BASS.
+         */
+        struct ble_audio_event_bass_set_broadcast_code bass_set_broadcast_code;
     };
 };
 
diff --git 
a/nimble/host/audio/services/bass/include/services/bass/ble_audio_svc_bass.h 
b/nimble/host/audio/services/bass/include/services/bass/ble_audio_svc_bass.h
new file mode 100644
index 000000000..431ecb968
--- /dev/null
+++ b/nimble/host/audio/services/bass/include/services/bass/ble_audio_svc_bass.h
@@ -0,0 +1,466 @@
+/**
+ * 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_SVC_BASS_
+#define H_BLE_AUDIO_SVC_BASS_
+
+#include <stdint.h>
+#include "audio/ble_audio.h"
+#include "syscfg/syscfg.h"
+
+/**
+ * @file ble_audio_svc_bass.h
+ *
+ * @brief Bluetooth LE Audio BAS Service
+ *
+ * This header file provides the public API for interacting with the BASS 
package.
+ *
+ * @defgroup ble_audio_svc_bass Bluetooth LE Audio BASS package
+ * @ingroup bt_host
+ * @{
+ *
+ * This package implements BASS service. Receiver states can be modified with 
setter functions
+ * or GATT writes to Control Point characteristic. Operations on Control Point 
like Add Source or
+ * Modify Source can be accepted or rejected by application by registering 
accept function callback.
+ * Accessing Control Point characteristic, or Successful modification or 
Receiver State will lead to
+ * emission of one of BLE_SVC_AUDIO_BASS events.
+ *
+ */
+
+/** BLE AUDIO BASS Maximum Subgroup Number */
+#define BLE_SVC_AUDIO_BASS_SUB_NUM_MAX \
+    MYNEWT_VAL(BLE_SVC_AUDIO_BASS_SUB_NUM_MAX)
+
+/** BLE AUDIO BASS characteristic UUID */
+#define BLE_SVC_AUDIO_BASS_UUID16                                        0x184F
+
+/** BLE AUDIO BASS Control Point characteristic UUID */
+#define BLE_SVC_AUDIO_BASS_CHR_UUID16_BASS_CP                            0x2BC7
+
+/** BLE AUDIO BASS Broadcast Receiver State characteristic UUID */
+#define BLE_SVC_AUDIO_BASS_CHR_UUID16_BROADCAST_RECEIVE_STATE            0x2BC8
+
+/** BLE AUDIO BASS Add Source operation OP code */
+#define BLE_SVC_AUDIO_BASS_OPERATION_ADD_SOURCE                          0x01
+
+/** BLE AUDIO BASS Modify Source operation OP code */
+#define BLE_SVC_AUDIO_BASS_OPERATION_MODIFY_SOURCE                       0x02
+
+/** BLE AUDIO BASS Remove Source operation OP code */
+#define BLE_SVC_AUDIO_BASS_OPERATION_REMOVE_SOURCE                       0x03
+
+/** BLE AUDIO BASS Error: OP Code not supported */
+#define BLE_SVC_AUDIO_BASS_ERR_OPCODE_NOT_SUPPORTED                      0x80
+
+/** BLE AUDIO BASS Error: Invalid Source ID */
+#define BLE_SVC_AUDIO_BASS_ERR_INVALID_SOURCE_ID                         0x81
+
+/** BLE AUDIO BASS Encryption States */
+enum ble_svc_audio_bass_big_enc {
+    /** BLE AUDIO BASS BIG Encryption: Not Encrypted */
+    BLE_SVC_AUDIO_BASS_BIG_ENC_NOT_ENCRYPTED,
+
+    /** BLE AUDIO BASS BIG Encryption: Broadcast Code Required */
+    BLE_SVC_AUDIO_BASS_BIG_ENC_BROADCAST_CODE_REQ,
+
+    /** BLE AUDIO BASS BIG Encryption: Decrypting */
+    BLE_SVC_AUDIO_BASS_BIG_ENC_DECRYPTING,
+
+    /** BLE AUDIO BASS BIG Encryption: Bad Code */
+    BLE_SVC_AUDIO_BASS_BIG_ENC_BAD_CODE
+};
+
+/** BLE AUDIO BASS PA Sync parameters, valid fo Modify Source operation */
+enum ble_svc_audio_bass_pa_sync {
+    /** BLE AUDIO BASS PA Sync: Do not synchronize to PA */
+    BLE_SVC_AUDIO_BASS_PA_SYNC_DO_NOT_SYNC,
+
+    /** BLE AUDIO BASS PA Sync: Synchronize to PA – PAST available */
+    BLE_SVC_AUDIO_BASS_PA_SYNC_SYNC_PAST_AVAILABLE,
+
+    /** BLE AUDIO BASS PA Sync: Synchronize to PA – PAST not available */
+    BLE_SVC_AUDIO_BASS_PA_SYNC_SYNC_PAST_NOT_AVAILABLE,
+
+    /**
+     * BLE AUDIO BASS PA Sync: reserved for future use.
+     * This shall be always last value in this enum
+     */
+    BLE_SVC_AUDIO_BASS_PA_SYNC_RFU
+};
+
+/** BLE AUDIO BASS Broadcast Receiver: PA Sync States */
+enum ble_svc_audio_bass_pa_sync_state {
+    /** BLE AUDIO BASS PA Sync State: Not synchronized to PA */
+    BLE_SVC_AUDIO_BASS_PA_SYNC_STATE_NOT_SYNCED,
+
+    /** BLE AUDIO BASS PA Sync State: SyncInfo Request */
+    BLE_SVC_AUDIO_BASS_PA_SYNC_STATE_SYNC_INFO_REQ,
+
+    /** BLE AUDIO BASS PA Sync State: Synchronized to PA */
+    BLE_SVC_AUDIO_BASS_PA_SYNC_STATE_SYNCED,
+
+    /** BLE AUDIO BASS PA Sync State: Failed to synchronize to PAA */
+    BLE_SVC_AUDIO_BASS_PA_SYNC_STATE_SYNCED_FAILED,
+
+    /** BLE AUDIO BASS PA Sync State: No PAST */
+    BLE_SVC_AUDIO_BASS_PA_SYNC_STATE_NO_PAST
+};
+
+/** BLE AUDIO BASS Broadcast Receiver State: Subgroup entry */
+struct ble_svc_audio_bass_subgroup {
+    /** BLE AUDIO BASS Subgroup entry: Bis Synchronization State */
+    uint32_t bis_sync_state;
+
+    /** BLE AUDIO BASS Subgroup entry: Metadata length */
+    uint8_t metadata_length;
+
+    /** BLE AUDIO BASS Subgroup entry: Metadata */
+    uint8_t *metadata;
+};
+
+/** BLE AUDIO BASS Broadcast Receiver State */
+struct ble_svc_audio_bass_receiver_state {
+    /** BLE AUDIO BASS Broadcast Receiver State: Source ID */
+    uint8_t source_id;
+
+    /** BLE AUDIO BASS Broadcast Receiver State: Source BLE Address */
+    ble_addr_t source_addr;
+
+    /** BLE AUDIO BASS Broadcast Receiver State: Source Advertising SID */
+    uint8_t source_adv_sid;
+
+    /** BLE AUDIO BASS Broadcast Receiver State: Broadcast ID */
+    uint32_t broadcast_id;
+
+    /** BLE AUDIO BASS Broadcast Receiver State: PA Sync state */
+    enum ble_svc_audio_bass_pa_sync_state pa_sync_state;
+
+    /** BLE AUDIO BASS Broadcast Receiver State: BIG Encryption */
+    enum ble_svc_audio_bass_big_enc big_encryption;
+
+    /**
+     * BLE AUDIO BASS Broadcast Receiver State: Bad Code.
+     * On GATT Read access, this value is ignored if big_encryption
+     * is not set to BLE_SVC_AUDIO_BASS_BIG_ENC_BAD_CODE
+     */
+    uint8_t bad_code[BLE_AUDIO_BROADCAST_CODE_SIZE];
+
+    /** BLE AUDIO BASS Broadcast Receiver State: Number of subgroups */
+    uint8_t num_subgroups;
+
+    /** BLE AUDIO BASS Broadcast Receiver State: subgroup entries */
+    struct ble_svc_audio_bass_subgroup
+        subgroups[BLE_SVC_AUDIO_BASS_SUB_NUM_MAX];
+};
+
+/** BLE AUDIO BASS Broadcast Receiver State add parameters */
+struct ble_svc_audio_bass_receiver_state_add_params {
+    /** BLE AUDIO BASS Broadcast Receiver State: Source BLE Address */
+    ble_addr_t source_addr;
+
+    /** BLE AUDIO BASS Broadcast Receiver State: Source Advertising SID */
+    uint8_t source_adv_sid;
+
+    /** BLE AUDIO BASS Broadcast Receiver State: Broadcast ID */
+    uint32_t broadcast_id;
+
+    /** BLE AUDIO BASS Broadcast Receiver State: PA Sync state */
+    enum ble_svc_audio_bass_pa_sync_state pa_sync_state;
+
+    /** BLE AUDIO BASS Broadcast Receiver State: BIG Encryption */
+    enum ble_svc_audio_bass_big_enc big_encryption;
+
+    /**
+     * BLE AUDIO BASS Broadcast Receiver State: Bad Code.
+     * On GATT Read access, this value is ignored if big_encryption
+     * is not set to BLE_SVC_AUDIO_BASS_BIG_ENC_BAD_CODE
+     */
+    const uint8_t *bad_code;
+
+    /** BLE AUDIO BASS Broadcast Receiver State: Number of subgroups */
+    uint8_t num_subgroups;
+
+    /** BLE AUDIO BASS Broadcast Receiver State: subgroup entries */
+    struct ble_svc_audio_bass_subgroup
+        subgroups[BLE_SVC_AUDIO_BASS_SUB_NUM_MAX];
+};
+
+/** Parameters used for updating Metadata in Receiver State. */
+struct ble_svc_audio_bass_metadata_params {
+    /** Subgroup index */
+    uint8_t subgroup_idx;
+
+    /** Metadata length */
+    uint8_t metadata_length;
+
+    /** Metadata */
+    const uint8_t *metadata;
+};
+
+/** Parameters used for updating Receiver State. */
+struct ble_svc_audio_bass_update_params {
+    /** PA Sync state */
+    enum ble_svc_audio_bass_pa_sync_state pa_sync_state;
+
+    /** BIG encryption state */
+    enum ble_svc_audio_bass_big_enc big_encryption;
+
+    /** Incorrect Bad Broadcast Code. Valid for 
BLE_SVC_AUDIO_BASS_BIG_ENC_BAD_CODE */
+    const uint8_t *bad_code;
+
+    /** BLE AUDIO BASS Broadcast Receiver State: Number of subgroups */
+    uint8_t num_subgroups;
+
+    /** BLE AUDIO BASS Broadcast Receiver State: subgroup entries */
+    struct ble_svc_audio_bass_subgroup
+        subgroups[BLE_SVC_AUDIO_BASS_SUB_NUM_MAX];
+};
+
+/**
+ *  Structure describing operation attempted by write on
+ *  BASS Control Point characteristic
+ */
+struct ble_svc_audio_bass_operation {
+    /**
+     * Indicates the type of BASS operation that occurred.  This is one of the
+     * ble_svc_audio_bass_operation codes.
+     */
+    uint8_t op;
+
+    /** Connection handle for which the operation was performed */
+    uint16_t conn_handle;
+
+    /**
+     * A discriminated union containing additional details concerning the BASS 
Control Point
+     * event.  The 'type' field indicates which member of the union is valid.
+     */
+    union {
+        /**
+         * Represents Add Source operation.  Valid for the following event
+         * types:
+         *     o BLE_SVC_AUDIO_BASS_OPERATION_ADD_SOURCE
+         * Application can accept or reject Add Source operation. If no 
application callback is set
+         * and free Receive State characteristic exists operation is 
automatically accepted.
+         * If application callback exists and returns 0 operation is accepted.
+         * Otherwise, operation is rejected.
+         * If operation is accepted by application, it may select receiver 
state to be filled.
+         * If application doesnt select characteristic, BASS Server falls back
+         * to searching free one. If none is found, operation is rejected.
+         * After Add Source operation is accepted, BLE_AUDIO_EVENT is emitted.
+         */
+        struct {
+            /** Source ID */
+            uint8_t source_id;
+
+            /** Advertiser Address */
+            ble_addr_t adv_addr;
+
+            /** Advertising SID */
+            uint8_t adv_sid;
+
+            /** Broadcast ID */
+            uint32_t broadcast_id : 24;
+
+            /** PA Sync */
+            enum ble_svc_audio_bass_pa_sync pa_sync;
+
+            /** PA Interval */
+            uint16_t pa_interval;
+
+            /** Number of subgroups */
+            uint8_t num_subgroups;
+
+            /** Subgroup entries */
+            struct ble_svc_audio_bass_subgroup
+                subgroups[BLE_SVC_AUDIO_BASS_SUB_NUM_MAX];
+
+            /**
+             * Pointer to provide source ID to be swapped or NULL.
+             *
+             * Valid only if all other receive states are used.
+             *
+             * If there are insufficient resources to handle the operation,
+             * the application is requested to provide source ID to be
+             * removed once accepted.
+             */
+            uint8_t *out_source_id_to_swap;
+        } add_source;
+
+        /**
+         * Represents Modify Source operation. Valid for the following event
+         * types:
+         *     o BLE_SVC_AUDIO_BASS_OPERATION_MODIFY_SOURCE
+         * Application can accept or reject Add Source operation.
+         *  If no application callback is set
+         * or application callback returns 0 operation is automatically 
accepted.
+         * If application callback returns non-zero value operation is 
rejected.
+         */
+        struct {
+            /** Source ID */
+            uint8_t source_id;
+
+            /** PA Sync */
+            enum ble_svc_audio_bass_pa_sync pa_sync;
+
+            /** PA Interval */
+            uint16_t pa_interval;
+
+            /** Number of subgroups */
+            uint16_t num_subgroups;
+
+            /** BIS Synchronisation of subgroups */
+            uint32_t bis_sync[BLE_SVC_AUDIO_BASS_SUB_NUM_MAX];
+        } modify_source;
+
+        /**
+         * Represents Remove Source operation. Valid for the following event
+         * types:
+         *     o BLE_SVC_AUDIO_BASS_OPERATION_REMOVE_SOURCE
+         */
+        struct {
+            /** Source ID */
+            uint8_t source_id;
+        } remove_source;
+    };
+};
+
+/**
+ * Prototype of Accept Function callback for BASS Control Point operations.
+ * This function shall return 0 if operation is accepted,
+ * and error code if rejected.
+ */
+typedef int ble_svc_audio_bass_accept_fn(struct ble_svc_audio_bass_operation
+                                         *operation, void *arg);
+
+/**
+ * @brief Set Accept Function callback.
+ *
+ * Set Accept Function callback that will be used to accept or reject
+ * operations queried on BASS Control Point characteristic. If no function
+ * is registered, operations are accepted by default. Only one Accept
+ * Function can be registered at once.
+ *
+ * @param[in] fn                        ble_svc_audio_bass_accept_fn
+ *                                      to be registered.
+ * @param[in] arg                       Optional ble_svc_audio_bass_accept_fn
+ *                                      argument.
+ *
+ * @return                              0 on success;
+ *                                      A non-zero value on failure.
+ */
+int
+ble_svc_audio_bass_accept_fn_set(ble_svc_audio_bass_accept_fn *fn, void *arg);
+
+/**
+ * @brief Add Broadcast Receive State.
+ *
+ * Add new Broadcast Receive State to BASS.
+ *
+ * @param[in] params                    Parameters of new
+ *                                      Broadcast Receive State.
+ * @param[out] source_id                Source ID assigned by BASS to new
+ *                                      Broadcast Receive State
+ *
+ * @return                              0 on success;
+ *                                      A non-zero value on failure.
+ */
+int
+ble_svc_audio_bass_receive_state_add(const struct 
ble_svc_audio_bass_receiver_state_add_params *params,
+                                     uint8_t *source_id);
+
+/**
+ * @brief Remove Broadcast Receive State.
+ *
+ * Remove Broadcast Receive State from BASS.
+ *
+ * @param[in] source_id                 Source ID of Broadcast Receive State
+ *                                      to be removed
+ *
+ * @return                              0 on success;
+ *                                      A non-zero value on failure.
+ */
+int
+ble_svc_audio_bass_receive_state_remove(uint8_t source_id);
+
+/**
+ * @brief Update Broadcast Receive State metadata.
+ *
+ * Set Broadcast Receive State metadata to new value.
+ *
+ * @param[in] params                   Parameter structure with new metadata.
+ * @param[in] source_id                Source ID of Broadcast Receive State
+ *
+ * @return                              0 on success;
+ *                                      A non-zero value on failure.
+ */
+int
+ble_svc_audio_bass_update_metadata(const struct 
ble_svc_audio_bass_metadata_params *params,
+                                   uint8_t source_id);
+
+/**
+ * @brief Update Broadcast Receive State.
+ *
+ * Set Broadcast Receive State to new value.
+ *
+ * @param[in] params                   Parameter structure with new
+ *                                     Receive State.
+ * @param[in] source_id                Source ID of Broadcast Receive State
+ *
+ * @return                              0 on success;
+ *                                      A non-zero value on failure.
+ */
+int
+ble_svc_audio_bass_receive_state_update(const struct
+                                        ble_svc_audio_bass_update_params 
*params,
+                                        uint8_t source_id);
+
+/**
+ * @brief Find Broadcast Receive State by Source ID.
+ *
+ * Get Broadcast Receive State characteristic value by Source ID.
+ *
+ * @param[in] source_id                Source ID of Broadcast Receive State
+ * @param[out] state                   Pointer to Broadcast Receive State
+ *                                     characteristic value
+ *
+ * @return                              0 on success;
+ *                                      A non-zero value on failure.
+ */
+int
+ble_svc_audio_bass_receiver_state_get(uint8_t source_id,
+                                      struct ble_svc_audio_bass_receiver_state 
**state);
+
+/**
+ * @brief Get the source ID for given Receive State index.
+ *
+ * @param[in] index                     Receive State index.
+ * @param[in,out] source_id             Pointer to the variable where the
+ *                                      Source ID will be stored.
+ *
+ * @return                              0 on success;
+ *                                      A non-zero value on failure.
+ */
+int
+ble_svc_audio_bass_source_id_get(uint8_t index, uint8_t *source_id);
+
+/**
+ * @}
+ */
+
+#endif /* H_BLE_AUDIO_SVC_BASS_ */
diff --git a/nimble/host/audio/services/bass/pkg.yml 
b/nimble/host/audio/services/bass/pkg.yml
new file mode 100644
index 000000000..e92ab6dd0
--- /dev/null
+++ b/nimble/host/audio/services/bass/pkg.yml
@@ -0,0 +1,33 @@
+# 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.
+
+pkg.name: nimble/host/audio/services/bass
+pkg.description: Broadcast Audio Scan Service
+pkg.author: "Apache Mynewt <[email protected]>"
+pkg.homepage: "http://mynewt.apache.org/";
+pkg.keywords:
+  - ble
+  - bluetooth
+  - pacs
+  - nimble
+
+pkg.deps:
+  - nimble/host
+  - nimble/host/services/gatt
+
+pkg.init:
+  ble_svc_audio_bass_init: 'MYNEWT_VAL(BLE_SVC_AUDIO_BASS_SYSINIT_STAGE)'
diff --git a/nimble/host/audio/services/bass/src/ble_audio_svc_bass.c 
b/nimble/host/audio/services/bass/src/ble_audio_svc_bass.c
new file mode 100644
index 000000000..6a40c17a8
--- /dev/null
+++ b/nimble/host/audio/services/bass/src/ble_audio_svc_bass.c
@@ -0,0 +1,890 @@
+/*
+ * 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 "host/ble_hs.h"
+#include "host/ble_gatt.h"
+#include "../../host/src/ble_gatt_priv.h"
+#include "../../host/src/ble_att_priv.h"
+#include "services/bass/ble_audio_svc_bass.h"
+#include "../../../src/ble_audio_priv.h"
+
+#define BLE_SVC_AUDIO_BASS_CHR_LEN_UNLIMITED                    (-1)
+#define BLE_SVC_AUDIO_BASS_RECEIVE_STATE_SRC_ID_NONE            0xFF
+
+const ble_uuid_t * bass_receive_state_uuid =
+    BLE_UUID16_DECLARE(BLE_SVC_AUDIO_BASS_CHR_UUID16_BROADCAST_RECEIVE_STATE);
+const ble_uuid_t * bass_cp_uuid =
+    BLE_UUID16_DECLARE(BLE_SVC_AUDIO_BASS_CHR_UUID16_BASS_CP);
+
+enum ble_svc_audio_bass_ctrl_point_op_code {
+    BLE_AUDIO_SVC_BASS_REMOTE_SCAN_STOPPED,
+    BLE_AUDIO_SVC_BASS_REMOTE_SCAN_STARTED,
+    BLE_AUDIO_SVC_BASS_ADD_SOURCE,
+    BLE_AUDIO_SVC_BASS_MODIFY_SOURCE,
+    BLE_AUDIO_SVC_BASS_SET_BROADCAST_CODE,
+    BLE_AUDIO_SVC_BASS_REMOVE_SOURCE
+};
+
+typedef int ble_svc_audio_bass_ctrl_point_handler_cb(uint8_t *data,
+                                                     uint16_t data_len,
+                                                     uint16_t conn_handle);
+
+static struct ble_svc_audio_bass_ctrl_point_ev {
+    ble_svc_audio_bass_accept_fn *ctrl_point_ev_fn;
+    void *arg;
+} accept_fn;
+
+struct ble_svc_audio_bass_rcv_state_entry {
+    uint8_t source_id;
+    uint16_t chr_val;
+    struct ble_svc_audio_bass_receiver_state state;
+};
+
+static struct ble_svc_audio_bass_rcv_state_entry
+    receiver_states[MYNEWT_VAL(BLE_SVC_AUDIO_BASS_RECEIVE_STATE_MAX)] = {
+    [0 ... MYNEWT_VAL(BLE_SVC_AUDIO_BASS_RECEIVE_STATE_MAX) - 1] = {
+        .source_id = BLE_SVC_AUDIO_BASS_RECEIVE_STATE_SRC_ID_NONE
+    }
+};
+
+static struct os_mempool ble_audio_svc_bass_metadata_pool;
+static os_membuf_t ble_audio_svc_bass_metadata_mem[
+    OS_MEMPOOL_SIZE(MYNEWT_VAL(BLE_SVC_AUDIO_BASS_RECEIVE_STATE_MAX) *
+                    BLE_SVC_AUDIO_BASS_SUB_NUM_MAX,
+                    MYNEWT_VAL(BLE_SVC_AUDIO_BASS_METADATA_MAX_SZ))];
+
+static int
+ble_svc_audio_bass_remote_scan_stopped(uint8_t *data, uint16_t data_len, 
uint16_t conn_handle);
+static int
+ble_svc_audio_bass_remote_scan_started(uint8_t *data, uint16_t data_len, 
uint16_t conn_handle);
+static int
+ble_svc_audio_bass_add_source(uint8_t *data, uint16_t data_len, uint16_t 
conn_handle);
+static int
+ble_svc_audio_bass_modify_source(uint8_t *data, uint16_t data_len, uint16_t 
conn_handle);
+static int
+ble_svc_audio_bass_set_broadcast_code(uint8_t *data, uint16_t data_len, 
uint16_t conn_handle);
+static int
+ble_svc_audio_bass_remove_source(uint8_t *data, uint16_t data_len, uint16_t 
conn_handle);
+
+static int
+ble_svc_audio_bass_ctrl_point_write_access(struct ble_gatt_access_ctxt *ctxt, 
uint16_t conn_handle);
+static int
+ble_svc_audio_bass_rcv_state_read_access(struct ble_gatt_access_ctxt *ctxt, 
void *arg);
+
+static struct ble_svc_audio_bass_ctrl_point_handler {
+    uint8_t op_code;
+    uint8_t length_min;
+    uint8_t length_max;
+    ble_svc_audio_bass_ctrl_point_handler_cb *handler_cb;
+} ble_svc_audio_bass_ctrl_point_handlers[] = {
+    {
+        .op_code = BLE_AUDIO_SVC_BASS_REMOTE_SCAN_STOPPED,
+        .length_min = 0,
+        .length_max = 0,
+        .handler_cb = ble_svc_audio_bass_remote_scan_stopped
+    }, {
+        .op_code = BLE_AUDIO_SVC_BASS_REMOTE_SCAN_STARTED,
+        .length_min = 0,
+        .length_max = 0,
+        .handler_cb = ble_svc_audio_bass_remote_scan_started
+    }, {
+        .op_code = BLE_AUDIO_SVC_BASS_ADD_SOURCE,
+        .length_min = 15,
+        .length_max = BLE_SVC_AUDIO_BASS_CHR_LEN_UNLIMITED,
+        .handler_cb = ble_svc_audio_bass_add_source
+    }, {
+        .op_code = BLE_AUDIO_SVC_BASS_MODIFY_SOURCE,
+        .length_min = 5,
+        .length_max = BLE_SVC_AUDIO_BASS_CHR_LEN_UNLIMITED,
+        .handler_cb = ble_svc_audio_bass_modify_source
+    }, {
+        .op_code = BLE_AUDIO_SVC_BASS_SET_BROADCAST_CODE,
+        .length_min = 17,
+        .length_max = 17,
+        .handler_cb = ble_svc_audio_bass_set_broadcast_code
+    }, {
+        .op_code = BLE_AUDIO_SVC_BASS_REMOVE_SOURCE,
+        .length_min = 1,
+        .length_max = 1,
+        .handler_cb = ble_svc_audio_bass_remove_source
+    }
+};
+
+static struct ble_gatt_chr_def 
ble_svc_audio_bass_chrs[MYNEWT_VAL(BLE_SVC_AUDIO_BASS_RECEIVE_STATE_MAX) + 2];
+
+static const struct ble_gatt_svc_def 
ble_svc_audio_bass_defs[MYNEWT_VAL(BLE_SVC_AUDIO_BASS_RECEIVE_STATE_MAX) + 2] = 
{
+    { /*** Service: Published Audio Capabilities Service (bass) */
+        .type = BLE_GATT_SVC_TYPE_PRIMARY,
+        .uuid = BLE_UUID16_DECLARE(BLE_SVC_AUDIO_BASS_UUID16),
+        .characteristics = ble_svc_audio_bass_chrs,
+    },
+    {
+        0, /* No more services. */
+    },
+};
+
+static uint8_t free_source_id = 0;
+
+static int
+ble_svc_audio_bass_access(uint16_t conn_handle, uint16_t attr_handle,
+                          struct ble_gatt_access_ctxt *ctxt, void *arg)
+{
+    uint16_t uuid16 = ble_uuid_u16(ctxt->chr->uuid);
+    int rc;
+
+    switch (uuid16) {
+    case BLE_SVC_AUDIO_BASS_CHR_UUID16_BASS_CP:
+        if (ctxt->op == BLE_GATT_ACCESS_OP_WRITE_CHR) {
+            rc = ble_svc_audio_bass_ctrl_point_write_access(ctxt, conn_handle);
+        } else {
+            assert(0);
+        }
+        return rc;
+    case BLE_SVC_AUDIO_BASS_CHR_UUID16_BROADCAST_RECEIVE_STATE:
+        if (ctxt->op == BLE_GATT_ACCESS_OP_READ_CHR) {
+            rc = ble_svc_audio_bass_rcv_state_read_access(ctxt, arg);
+        } else {
+            assert(0);
+        }
+        return rc;
+    default:
+        assert(0);
+    }
+}
+
+static bool
+ble_svc_audio_bass_source_id_free(uint8_t source_id)
+{
+    int i;
+    for (i = 0; i < MYNEWT_VAL(BLE_SVC_AUDIO_BASS_RECEIVE_STATE_MAX); i++) {
+        if (receiver_states[i].source_id == source_id) {
+            return false;
+        }
+    }
+
+    return true;
+};
+
+static uint8_t
+ble_svc_audio_bass_get_new_source_id(void)
+{
+    /** Wrap around after all Source IDs were used */
+    if (free_source_id == BLE_SVC_AUDIO_BASS_RECEIVE_STATE_SRC_ID_NONE) {
+        free_source_id = 0;
+    }
+
+    while (!ble_svc_audio_bass_source_id_free(free_source_id)) {
+        free_source_id++;
+    }
+
+    return free_source_id;
+}
+
+static int
+ble_svc_audio_bass_receive_state_notify(struct 
ble_svc_audio_bass_rcv_state_entry *state)
+{
+    int i;
+
+    for (i = 0; i < sizeof(ble_svc_audio_bass_chrs); i++) {
+        if (ble_svc_audio_bass_chrs[i].arg == state) {
+            ble_gatts_chr_updated(*ble_svc_audio_bass_chrs[i].val_handle);
+            return 0;
+        }
+    }
+
+    return BLE_HS_ENOENT;
+}
+
+static int
+ble_svc_audio_bass_receive_state_find_by_source_id(struct 
ble_svc_audio_bass_rcv_state_entry **out_state,
+                                                   uint8_t source_id)
+{
+    int i;
+
+    for (i = 0; i < sizeof(receiver_states); i++) {
+        if (receiver_states[i].source_id == source_id) {
+            *out_state = &receiver_states[i];
+            return 0;
+        }
+    }
+
+    return BLE_HS_ENOMEM;
+}
+
+int
+ble_svc_audio_bass_receive_state_find_free(struct 
ble_svc_audio_bass_rcv_state_entry **out_state)
+{
+    int i;
+
+    for (i = 0; i < sizeof(receiver_states); i++) {
+        if (receiver_states[i].source_id == 
BLE_SVC_AUDIO_BASS_RECEIVE_STATE_SRC_ID_NONE) {
+            *out_state = &receiver_states[i];
+            return 0;
+        }
+    }
+
+    return BLE_HS_ENOMEM;
+}
+
+static void
+ble_svc_audio_bass_receive_state_free(struct 
ble_svc_audio_bass_rcv_state_entry *state)
+{
+    state->source_id = BLE_SVC_AUDIO_BASS_RECEIVE_STATE_SRC_ID_NONE;
+}
+
+static int
+ble_svc_audio_bass_remote_scan_stopped(uint8_t *data, uint16_t data_len, 
uint16_t conn_handle)
+{
+    struct ble_audio_event ev = {
+        .type = BLE_AUDIO_EVENT_BASS_REMOTE_SCAN_STOPPED
+    };
+
+    ev.remote_scan_stopped.conn_handle = conn_handle;
+    ble_audio_event_listener_call(&ev);
+
+    return 0;
+}
+
+static int
+ble_svc_audio_bass_remote_scan_started(uint8_t *data, uint16_t data_len, 
uint16_t conn_handle)
+{
+    struct ble_audio_event ev = {
+        .type = BLE_AUDIO_EVENT_BASS_REMOTE_SCAN_STARTED
+    };
+
+    ev.remote_scan_started.conn_handle = conn_handle;
+    ble_audio_event_listener_call(&ev);
+
+    return 0;
+}
+
+static int
+ble_svc_audio_bass_add_source(uint8_t *data, uint16_t data_len, uint16_t 
conn_handle)
+{
+    struct ble_audio_event ev = {
+        .type = BLE_AUDIO_EVENT_BASS_OPERATION_STATUS,
+        .bass_operation_status = {
+            .op = BLE_AUDIO_EVENT_BASS_SOURCE_ADDED,
+            .status = 0
+        }
+    };
+    struct ble_svc_audio_bass_operation operation;
+    struct ble_svc_audio_bass_rcv_state_entry *rcv_state = NULL;
+    uint8_t offset = 0;
+    uint8_t *metadata_ptr;
+    uint8_t source_id_new;
+    uint8_t source_id_to_remove = BLE_SVC_AUDIO_BASS_RECEIVE_STATE_SRC_ID_NONE;
+    int rc = 0;
+    int i;
+
+    memset(&operation, 0, sizeof(operation));
+
+    operation.op = BLE_SVC_AUDIO_BASS_OPERATION_ADD_SOURCE;
+    operation.conn_handle = conn_handle;
+
+    operation.add_source.adv_addr.type = data[offset++];
+    memcpy(operation.add_source.adv_addr.val, &data[offset], 6);
+    offset += 6;
+    operation.add_source.adv_sid = data[offset++];
+    operation.add_source.broadcast_id = get_le24(&data[offset]);
+    offset += 3;
+    operation.add_source.pa_sync = data[offset++];
+    if (operation.add_source.pa_sync >= BLE_SVC_AUDIO_BASS_PA_SYNC_RFU) {
+        rc = BLE_HS_EINVAL;
+        ev.bass_operation_status.status = BLE_HS_EINVAL;
+        goto done;
+    }
+
+    operation.add_source.pa_interval = get_le16(&data[offset]);
+    offset += 2;
+    operation.add_source.num_subgroups = data[offset++];
+
+    /**
+     * Previous data was checked for it's size in 
`ble_svc_audio_bass_ctrl_point_write_access`.
+     * As bis_sync_state array may be of variable length, we need to check it 
separately
+     */
+    data_len -= offset;
+    for (i = 0; i < operation.add_source.num_subgroups; i++) {
+        if (data_len < sizeof(uint32_t)) {
+            rc = BLE_ATT_ERR_WRITE_REQ_REJECTED;
+            ev.bass_operation_status.status = BLE_HS_EREJECT;
+            goto done;
+        }
+        operation.add_source.subgroups[i].bis_sync_state = 
get_le32(&data[offset]);
+        offset += 4;
+        operation.add_source.subgroups[i].metadata_length = data[offset++];
+        data_len -= 5;
+        if (data_len < operation.add_source.subgroups[i].metadata_length) {
+            rc = BLE_ATT_ERR_WRITE_REQ_REJECTED;
+            ev.bass_operation_status.status = BLE_HS_EREJECT;
+            goto done;
+        }
+        operation.add_source.subgroups[i].metadata = &data[offset];
+        offset += operation.add_source.subgroups[i].metadata_length;
+        data_len -= operation.add_source.subgroups[i].metadata_length;
+    }
+
+    source_id_new = ble_svc_audio_bass_get_new_source_id();
+    operation.add_source.source_id = source_id_new;
+
+    ble_svc_audio_bass_receive_state_find_free(&rcv_state);
+    if (rcv_state == NULL) {
+        operation.add_source.out_source_id_to_swap = &source_id_to_remove;
+    } else {
+        operation.add_source.out_source_id_to_swap = NULL;
+        rcv_state->source_id = operation.add_source.source_id;
+    }
+
+    if (accept_fn.ctrl_point_ev_fn) {
+        rc = accept_fn.ctrl_point_ev_fn(&operation, accept_fn.arg);
+        if (rc != 0) {
+            if (rcv_state != NULL) {
+                ble_svc_audio_bass_receive_state_free(rcv_state);
+            }
+            rc = BLE_ATT_ERR_WRITE_REQ_REJECTED;
+            ev.bass_operation_status.status = BLE_HS_EREJECT;
+            goto done;
+        }
+    }
+
+    if (rcv_state == NULL) {
+        if (source_id_to_remove != 
BLE_SVC_AUDIO_BASS_RECEIVE_STATE_SRC_ID_NONE) {
+            ble_svc_audio_bass_receive_state_find_by_source_id(&rcv_state, 
source_id_to_remove);
+            if (rcv_state == NULL) {
+                rc = BLE_HS_EAPP;
+                ev.bass_operation_status.status = BLE_HS_EAPP;
+                goto done;
+            }
+
+            /* Swap Source ID */
+            rcv_state->source_id = operation.add_source.source_id;
+        } else {
+            rc = BLE_HS_ENOMEM;
+            ev.bass_operation_status.status = BLE_HS_ENOMEM;
+            goto done;
+        }
+    } else {
+        rcv_state->source_id = operation.add_source.source_id;
+    }
+
+    ev.bass_operation_status.source_id = rcv_state->source_id;
+    rcv_state->state.source_addr.type = operation.add_source.adv_addr.type;
+    memcpy(&rcv_state->state.source_addr.type, 
operation.add_source.adv_addr.val, 6);
+    rcv_state->state.source_adv_sid = operation.add_source.adv_sid;
+    rcv_state->state.broadcast_id = operation.add_source.broadcast_id;
+
+    for (i = 0; i < operation.add_source.num_subgroups; i++) {
+        metadata_ptr = os_memblock_get(&ble_audio_svc_bass_metadata_pool);
+        if (!metadata_ptr) {
+            rc = BLE_HS_ENOMEM;
+            ev.bass_operation_status.status = BLE_HS_ENOMEM;
+            goto done;
+        }
+        rcv_state->state.subgroups[i].metadata_length = 
operation.add_source.subgroups[i].metadata_length;
+        memcpy(metadata_ptr, operation.add_source.subgroups[i].metadata,
+               min(operation.add_source.subgroups[i].metadata_length,
+                   MYNEWT_VAL(BLE_SVC_AUDIO_BASS_METADATA_MAX_SZ)));
+
+        rcv_state->state.subgroups[i].metadata = metadata_ptr;
+    }
+
+done:
+    if (!rc) {
+        rc = ble_svc_audio_bass_receive_state_notify(rcv_state);
+        ev.bass_operation_status.status = rc;
+        goto done;
+    }
+
+    ble_audio_event_listener_call(&ev);
+
+    return rc;
+}
+
+static int
+check_bis_sync(uint16_t num_subgroups, const uint32_t *bis_sync_list)
+{
+    uint32_t bis_sync_mask = 0;
+    int i;
+    int j;
+
+    for (i = 0; i < num_subgroups; i++) {
+        if (bis_sync_list[i] != 0xFFFFFFFF) {
+            for (j = 0; j < num_subgroups; j++) {
+                if (bis_sync_list[i] & bis_sync_mask) {
+                    return BLE_HS_EINVAL;
+                }
+
+                bis_sync_mask |= bis_sync_list[i];
+            }
+        }
+    }
+
+    return 0;
+}
+
+static int
+ble_svc_audio_bass_modify_source(uint8_t *data, uint16_t data_len, uint16_t 
conn_handle)
+{
+    struct ble_svc_audio_bass_operation operation;
+    struct ble_svc_audio_bass_rcv_state_entry *rcv_state = NULL;
+    struct ble_audio_event ev = {
+        .type = BLE_AUDIO_EVENT_BASS_OPERATION_STATUS,
+        .bass_operation_status = {
+            .op = BLE_AUDIO_EVENT_BASS_SOURCE_MODIFIED,
+            .status = 0
+        }
+    };
+    uint8_t offset = 0;
+    int rc = 0;
+    int i;
+
+    memset(&operation, 0, sizeof(operation));
+
+    operation.op = BLE_SVC_AUDIO_BASS_OPERATION_MODIFY_SOURCE;
+    operation.conn_handle = conn_handle;
+
+    operation.modify_source.source_id = data[offset++];
+
+    ble_svc_audio_bass_receive_state_find_by_source_id(&rcv_state,
+                                                       
operation.modify_source.source_id);
+    if (rcv_state == NULL) {
+        rc = BLE_SVC_AUDIO_BASS_ERR_INVALID_SOURCE_ID;
+        ev.bass_operation_status.status = BLE_HS_EINVAL;
+        goto done;
+    }
+
+    operation.modify_source.pa_sync = data[offset++];
+    if (operation.modify_source.pa_sync >= BLE_SVC_AUDIO_BASS_PA_SYNC_RFU) {
+        rc = BLE_HS_EINVAL;
+        ev.bass_operation_status.status = BLE_HS_EREJECT;
+        goto done;
+    }
+
+    operation.modify_source.pa_interval = get_le16(&data[offset]);
+    offset += 2;
+    operation.modify_source.num_subgroups = get_le16(&data[offset]);
+    offset += 2;
+
+    data_len -= offset;
+    if (data_len < operation.modify_source.num_subgroups * sizeof(uint32_t)) {
+        rc = BLE_ATT_ERR_WRITE_REQ_REJECTED;
+        ev.bass_operation_status.status = BLE_HS_EREJECT;
+        goto done;
+    }
+
+    for (i = 0; i < operation.modify_source.num_subgroups; i++) {
+        operation.modify_source.bis_sync[i] = get_le32(&data[offset]);
+        offset += 4;
+    }
+
+    if (check_bis_sync(operation.modify_source.num_subgroups,
+                       operation.modify_source.bis_sync)) {
+        rc = BLE_HS_EINVAL;
+        ev.bass_operation_status.status = BLE_HS_EREJECT;
+        goto done;
+    }
+
+    if (accept_fn.ctrl_point_ev_fn) {
+        rc = accept_fn.ctrl_point_ev_fn(&operation, accept_fn.arg);
+        if (rc != 0) {
+            rc = BLE_ATT_ERR_WRITE_REQ_REJECTED;
+            ev.bass_operation_status.status = BLE_HS_EREJECT;
+            goto done;
+        }
+    }
+
+    ev.bass_operation_status.source_id = operation.modify_source.source_id;
+
+done:
+    if (!rc) {
+        rc = ble_svc_audio_bass_receive_state_notify(rcv_state);
+        ev.bass_operation_status.status = rc;
+        goto done;
+    }
+
+    ble_audio_event_listener_call(&ev);
+
+    return rc;
+}
+
+static int
+ble_svc_audio_bass_set_broadcast_code(uint8_t *data, uint16_t data_len, 
uint16_t conn_handle)
+{
+    struct ble_svc_audio_bass_rcv_state_entry *rcv_state = NULL;
+    struct ble_audio_event ev = {
+        .type = BLE_AUDIO_EVENT_BASS_BROADCAST_CODE_SET,
+    };
+
+    ev.bass_set_broadcast_code.source_id = data[0];
+
+    ble_svc_audio_bass_receive_state_find_by_source_id(&rcv_state,
+                                                       
ev.bass_set_broadcast_code.source_id);
+    if (rcv_state == NULL) {
+        return BLE_SVC_AUDIO_BASS_ERR_INVALID_SOURCE_ID;
+    }
+
+    memcpy(ev.bass_set_broadcast_code.broadcast_code, &data[1], 
BLE_AUDIO_BROADCAST_CODE_SIZE);
+
+    ble_audio_event_listener_call(&ev);
+
+    return 0;
+}
+
+static int
+ble_svc_audio_bass_remove_source(uint8_t *data, uint16_t data_len, uint16_t 
conn_handle)
+{
+    struct ble_audio_event ev = {
+        .type = BLE_AUDIO_EVENT_BASS_BROADCAST_CODE_SET,
+        .bass_operation_status = {
+            .op = BLE_AUDIO_EVENT_BASS_SOURCE_REMOVED,
+            .status = 0
+        }
+    };
+    struct ble_svc_audio_bass_rcv_state_entry *rcv_state = NULL;
+    struct ble_svc_audio_bass_operation operation;
+    int rc = 0;
+    int i;
+
+    ev.bass_set_broadcast_code.source_id = data[0];
+
+    ble_svc_audio_bass_receive_state_find_by_source_id(&rcv_state,
+                                                       
ev.bass_operation_status.source_id);
+    if (rcv_state == NULL) {
+        rc = BLE_SVC_AUDIO_BASS_ERR_INVALID_SOURCE_ID;
+        ev.bass_operation_status.status = BLE_HS_ENOENT;
+        goto done;
+    }
+
+    operation.remove_source.source_id = ev.bass_operation_status.source_id;
+    operation.conn_handle = conn_handle;
+    if (accept_fn.ctrl_point_ev_fn) {
+        rc = accept_fn.ctrl_point_ev_fn(&operation, accept_fn.arg);
+        if (rc != 0) {
+            rc = BLE_HS_EREJECT;
+            ev.bass_operation_status.status = BLE_HS_EREJECT;
+            goto done;
+        }
+    }
+
+    for (i = 0; i < rcv_state->state.num_subgroups; i++) {
+        os_memblock_put(&ble_audio_svc_bass_metadata_pool, 
rcv_state->state.subgroups[i].metadata);
+    }
+
+    memset(rcv_state, 0, sizeof(*rcv_state));
+    rcv_state->source_id = BLE_SVC_AUDIO_BASS_RECEIVE_STATE_SRC_ID_NONE;
+
+done:
+    if (!rc) {
+        rc = ble_svc_audio_bass_receive_state_notify(rcv_state);
+        ev.bass_operation_status.status = rc;
+        goto done;
+    }
+
+    ble_audio_event_listener_call(&ev);
+
+    return rc;
+}
+
+static struct ble_svc_audio_bass_ctrl_point_handler *
+ble_svc_audio_bass_find_handler(uint8_t opcode)
+{
+    int i;
+
+    for (i = 0; i < sizeof(ble_svc_audio_bass_ctrl_point_handlers); i++) {
+        if (ble_svc_audio_bass_ctrl_point_handlers[i].op_code == opcode) {
+            return &ble_svc_audio_bass_ctrl_point_handlers[i];
+        }
+    }
+
+    return NULL;
+}
+
+static int
+ble_svc_audio_bass_ctrl_point_write_access(struct ble_gatt_access_ctxt *ctxt, 
uint16_t conn_handle)
+{
+    struct ble_svc_audio_bass_ctrl_point_handler *handler;
+
+    uint8_t opcode = ctxt->om->om_data[0];
+
+    handler = ble_svc_audio_bass_find_handler(opcode);
+
+    if (!handler) {
+        return BLE_SVC_AUDIO_BASS_ERR_OPCODE_NOT_SUPPORTED;
+    }
+
+    if (ctxt->om->om_len - 1 < handler->length_min &&
+        handler->length_max >= 0 ?
+        ctxt->om->om_len > handler->length_max : 0) {
+        return BLE_ATT_ERR_WRITE_REQ_REJECTED;
+    }
+
+    return handler->handler_cb(&ctxt->om->om_data[1], ctxt->om->om_len - 1, 
conn_handle);
+}
+
+static int
+ble_svc_audio_bass_rcv_state_read_access(struct ble_gatt_access_ctxt *ctxt, 
void *arg)
+{
+    struct ble_svc_audio_bass_rcv_state_entry *state = arg;
+    uint8_t *buf;
+    int i;
+
+    /* Nothing set, return empty buffer */
+    if (state->source_id == BLE_SVC_AUDIO_BASS_RECEIVE_STATE_SRC_ID_NONE) {
+        return 0;
+    }
+
+    os_mbuf_append(ctxt->om, &state->source_id, 1);
+    os_mbuf_append(ctxt->om, &state->state.source_addr.type, 1);
+    os_mbuf_append(ctxt->om, &state->state.source_addr.val, 6);
+    os_mbuf_append(ctxt->om, &state->state.source_adv_sid, 1);
+    buf = os_mbuf_extend(ctxt->om, 3);
+    if (buf == NULL) {
+        return BLE_ATT_ERR_INSUFFICIENT_RES;
+    }
+
+    put_le24(buf, state->state.broadcast_id);
+    os_mbuf_append(ctxt->om, &state->state.pa_sync_state, 1);
+    os_mbuf_append(ctxt->om, &state->state.big_encryption, 1);
+
+    if (state->state.big_encryption == BLE_SVC_AUDIO_BASS_BIG_ENC_BAD_CODE) {
+        os_mbuf_append(ctxt->om, &state->state.bad_code, 
BLE_AUDIO_BROADCAST_CODE_SIZE);
+    }
+
+    os_mbuf_append(ctxt->om, &state->state.num_subgroups, 1);
+
+    for (i = 0; i < state->state.num_subgroups; i++) {
+        buf = os_mbuf_extend(ctxt->om, 4);
+        if (buf == NULL) {
+            return BLE_ATT_ERR_INSUFFICIENT_RES;
+        }
+
+        put_le32(buf, state->state.subgroups[i].bis_sync_state);
+        os_mbuf_append(ctxt->om, &state->state.subgroups[i].metadata_length, 
1);
+        os_mbuf_append(ctxt->om, state->state.subgroups[i].metadata,
+                       state->state.subgroups[i].metadata_length);
+    }
+
+    return 0;
+}
+
+int
+ble_svc_audio_bass_accept_fn_set(ble_svc_audio_bass_accept_fn *fn, void *arg)
+{
+    if (accept_fn.ctrl_point_ev_fn) {
+        return BLE_HS_EALREADY;
+    }
+
+    accept_fn.ctrl_point_ev_fn = fn;
+    accept_fn.arg = arg;
+    return 0;
+}
+
+int
+ble_svc_audio_bass_receive_state_add(const struct 
ble_svc_audio_bass_receiver_state_add_params *params,
+                                     uint8_t *source_id)
+{
+    struct ble_svc_audio_bass_rcv_state_entry *rcv_state;
+    int i;
+    int rc;
+
+    rc = ble_svc_audio_bass_receive_state_find_free(&rcv_state);
+    if (rc) {
+        return rc;
+    }
+
+    rcv_state->source_id = ble_svc_audio_bass_get_new_source_id();
+    rcv_state->state.source_addr = params->source_addr;
+    rcv_state->state.source_adv_sid = params->source_adv_sid;
+    rcv_state->state.broadcast_id = params->broadcast_id;
+    rcv_state->state.pa_sync_state = params->pa_sync_state;
+    rcv_state->state.big_encryption = params->big_encryption;
+    if (rcv_state->state.big_encryption ==
+        BLE_SVC_AUDIO_BASS_BIG_ENC_BAD_CODE) {
+        memcpy(&rcv_state->state.bad_code, params->bad_code,
+               BLE_AUDIO_BROADCAST_CODE_SIZE);
+    }
+    rcv_state->state.num_subgroups = params->num_subgroups;
+
+    for (i = 0; i < rcv_state->state.num_subgroups; i++) {
+        rcv_state->state.subgroups[i].metadata =
+            os_memblock_get(&ble_audio_svc_bass_metadata_pool);
+
+        if (!rcv_state->state.subgroups[i].metadata) {
+            return 0;
+        }
+
+        rcv_state->state.subgroups[i].metadata_length =
+            min(params->subgroups[i].metadata_length,
+                ble_audio_svc_bass_metadata_pool.mp_block_size);
+        memcpy(rcv_state->state.subgroups[i].metadata, 
params->subgroups[i].metadata,
+               rcv_state->state.subgroups[i].metadata_length);
+    }
+
+    *source_id = rcv_state->source_id;
+
+    return ble_svc_audio_bass_receive_state_notify(rcv_state);
+}
+
+int
+ble_svc_audio_bass_receive_state_remove(uint8_t source_id)
+{
+    struct ble_svc_audio_bass_rcv_state_entry *rcv_state = NULL;
+    int rc, i;
+
+    rc = ble_svc_audio_bass_receive_state_find_by_source_id(&rcv_state, 
source_id);
+    if (rc) {
+        return rc;
+    }
+
+    memset(&rcv_state->state, 0, sizeof(rcv_state->state));
+    rcv_state->source_id = BLE_SVC_AUDIO_BASS_RECEIVE_STATE_SRC_ID_NONE;
+
+    for (i = 0; i < rcv_state->state.num_subgroups; i++) {
+        os_memblock_put(&ble_audio_svc_bass_metadata_pool, 
rcv_state->state.subgroups[i].metadata);
+    }
+
+    return ble_svc_audio_bass_receive_state_notify(rcv_state);
+}
+
+int
+ble_svc_audio_bass_update_metadata(const struct 
ble_svc_audio_bass_metadata_params *params,
+                                   uint8_t source_id)
+{
+    struct ble_svc_audio_bass_rcv_state_entry *rcv_state = NULL;
+    int rc;
+
+    rc = ble_svc_audio_bass_receive_state_find_by_source_id(&rcv_state, 
source_id);
+    if (rc) {
+        return rc;
+    }
+
+    rcv_state->state.subgroups[params->subgroup_idx].metadata_length = 
params->metadata_length;
+    memcpy(rcv_state->state.subgroups[params->subgroup_idx].metadata,
+           params->metadata, params->metadata_length);
+
+    return ble_svc_audio_bass_receive_state_notify(rcv_state);
+}
+
+int
+ble_svc_audio_bass_receive_state_update(const struct
+                                        ble_svc_audio_bass_update_params 
*params,
+                                        uint8_t source_id)
+{
+    struct ble_svc_audio_bass_rcv_state_entry *rcv_state = NULL;
+    int rc;
+
+    rc = ble_svc_audio_bass_receive_state_find_by_source_id(&rcv_state,
+                                                            source_id);
+    if (rc) {
+        return rc;
+    }
+
+    if (rcv_state->state.num_subgroups < params->num_subgroups) {
+        return BLE_HS_ENOMEM;
+    }
+
+    rcv_state->state.pa_sync_state = params->pa_sync_state;
+    rcv_state->state.big_encryption = params->big_encryption;
+    if (params->bad_code) {
+        memcpy(rcv_state->state.bad_code,
+               params->bad_code,
+               BLE_AUDIO_BROADCAST_CODE_SIZE);
+    }
+
+    int i;
+    for (i = 0; i < params->num_subgroups; i++) {
+        rcv_state->state.subgroups[i].bis_sync_state =
+            params->subgroups[i].bis_sync_state;
+        memcpy(rcv_state->state.subgroups[i].metadata,
+               params->subgroups[i].metadata,
+               params->subgroups[i].metadata_length);
+    }
+
+    return ble_svc_audio_bass_receive_state_notify(rcv_state);
+}
+
+int
+ble_svc_audio_bass_receiver_state_get(uint8_t source_id,
+                                      struct ble_svc_audio_bass_receiver_state 
**state)
+{
+    struct ble_svc_audio_bass_rcv_state_entry *rcv_state = NULL;
+
+    ble_svc_audio_bass_receive_state_find_by_source_id(&rcv_state, source_id);
+
+    if (!rcv_state) {
+        return BLE_HS_ENOENT;
+    }
+
+    *state = &rcv_state->state;
+
+    return 0;
+}
+
+int
+ble_svc_audio_bass_source_id_get(uint8_t index, uint8_t *source_id)
+{
+    if (index >= ARRAY_SIZE(receiver_states) ||
+        receiver_states[index].source_id ==
+        BLE_SVC_AUDIO_BASS_RECEIVE_STATE_SRC_ID_NONE) {
+        return BLE_HS_ENOENT;
+    }
+
+    *source_id = receiver_states[index].source_id;
+
+    return 0;
+}
+
+void
+ble_svc_audio_bass_init(void)
+{
+    int rc;
+    int i;
+
+    /* Ensure this function only gets called by sysinit. */
+    SYSINIT_ASSERT_ACTIVE();
+
+    ble_svc_audio_bass_chrs[0].uuid = bass_cp_uuid;
+    ble_svc_audio_bass_chrs[0].access_cb = ble_svc_audio_bass_access;
+    ble_svc_audio_bass_chrs[0].flags = BLE_GATT_CHR_F_WRITE_NO_RSP | 
BLE_GATT_CHR_F_WRITE_ENC;
+
+    for (i = 1; i <= MYNEWT_VAL(BLE_SVC_AUDIO_BASS_RECEIVE_STATE_MAX); i++) {
+        ble_svc_audio_bass_chrs[i].uuid = bass_receive_state_uuid;
+        ble_svc_audio_bass_chrs[i].access_cb = ble_svc_audio_bass_access;
+        ble_svc_audio_bass_chrs[i].arg = &receiver_states[i-1];
+        ble_svc_audio_bass_chrs[i].val_handle = &receiver_states[i-1].chr_val;
+        ble_svc_audio_bass_chrs[i].flags = BLE_GATT_CHR_F_READ |
+                                           BLE_GATT_CHR_F_READ_ENC |
+                                           BLE_GATT_CHR_F_NOTIFY;
+    }
+
+    rc = ble_gatts_count_cfg(ble_svc_audio_bass_defs);
+    SYSINIT_PANIC_ASSERT(rc == 0);
+
+    rc = ble_gatts_add_svcs(ble_svc_audio_bass_defs);
+    SYSINIT_PANIC_ASSERT(rc == 0);
+
+    rc = os_mempool_init(&ble_audio_svc_bass_metadata_pool,
+                         MYNEWT_VAL(BLE_SVC_AUDIO_BASS_RECEIVE_STATE_MAX) *
+                         BLE_SVC_AUDIO_BASS_SUB_NUM_MAX,
+                         MYNEWT_VAL(BLE_SVC_AUDIO_BASS_METADATA_MAX_SZ),
+                         ble_audio_svc_bass_metadata_mem, 
"ble_audio_svc_bass_metadata_pool");
+    SYSINIT_PANIC_ASSERT(rc == 0);
+
+    (void)rc;
+}
diff --git a/nimble/host/audio/services/bass/syscfg.yml 
b/nimble/host/audio/services/bass/syscfg.yml
new file mode 100644
index 000000000..2be3f497c
--- /dev/null
+++ b/nimble/host/audio/services/bass/syscfg.yml
@@ -0,0 +1,38 @@
+# 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.
+
+syscfg.defs:
+    BLE_SVC_AUDIO_BASS_SYSINIT_STAGE:
+        description: >
+            Sysinit stage for Published Audio Capabilities Service.
+        value: 303
+    BLE_SVC_AUDIO_BASS_RECEIVE_STATE_MAX:
+        description: >
+            Maximum number of Broadcast Receive State characteristics.
+        value: 2
+    BLE_SVC_AUDIO_BASS_METADATA_MAX_SZ:
+        description: >
+            Maximum size of metadata that can be saved for subgroup. If set to 
0 metadata is
+            not being saved.
+        value: 32
+    BLE_SVC_AUDIO_BASS_SUB_NUM_MAX:
+        description: >
+            Maximum number of Subgroups supported per Receive State.
+        value: 3
+
+syscfg.restrictions:
+    - 'BLE_SVC_AUDIO_BASS_RECEIVE_STATE_MAX < 0xFE'
diff --git a/nimble/host/include/host/ble_att.h 
b/nimble/host/include/host/ble_att.h
index bfd7adf10..8323c9d76 100644
--- a/nimble/host/include/host/ble_att.h
+++ b/nimble/host/include/host/ble_att.h
@@ -113,6 +113,18 @@ struct os_mbuf;
 /**Requested value is not allowed. */
 #define BLE_ATT_ERR_VALUE_NOT_ALLOWED       0x13
 
+/**Write Request Rejected. */
+#define BLE_ATT_ERR_WRITE_REQ_REJECTED      0xFC
+
+/**Client Characteristic Configuration Descriptor Improperly Configured. */
+#define BLE_ATT_ERR_CCCD_IMPORER_CONF       0xFD
+
+/**Procedure Already in Progress. */
+#define BLE_ATT_ERR_PROC_IN_PROGRESS        0xFE
+
+/**Out of Range. */
+#define BLE_ATT_ERR_OUT_OF_RANGE            0xFF
+
 /** @} */
 
 /**

Reply via email to