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
+
/** @} */
/**