diff --git a/nimble/host/audio/include/audio/ble_audio.h b/nimble/host/audio/include/audio/ble_audio.h index 59122cfa8f..99dcf619ad 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 @@ -527,6 +530,24 @@ 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 - Add Source */ +#define BLE_AUDIO_EVENT_BASS_ADD_SOURCE 5 + +/** BLE Audio event: BASS - Modify Source */ +#define BLE_AUDIO_EVENT_BASS_MODIFY_SOURCE 6 + +/** BLE Audio event: BASS - Set Broadcast Code */ +#define BLE_AUDIO_EVENT_BASS_SET_BROADCAST_CODE 7 + +/** BLE Audio event: BASS - Remove Source */ +#define BLE_AUDIO_EVENT_BASS_REMOVE_SOURCE 8 + /** @} */ /** @brief Broadcast Announcement */ @@ -562,6 +583,66 @@ 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; +}; + +/** @brief BASS Source Added */ +struct ble_audio_event_bass_source_added { + /** Source ID */ + uint8_t source_id; + + /** Event status. 0 on success, BLE_HS Error code otherwise */ + uint8_t status; +}; + +/** @brief BASS Source Modify */ +struct ble_audio_event_bass_source_modify { + /** Source ID */ + uint8_t source_id; + + /** Event status. 0 on success, BLE_HS Error code otherwise */ + uint8_t status; + + /** PA Sync operation to be performed by Broadcast Sink */ + enum { + /** Do not synchronise to PA */ + BLE_AUDIO_EVENT_BASS_NO_SYNC, + + /** Synchronise to PA without using PAST */ + BLE_AUDIO_EVENT_BASS_SYNC, + + /** Terminate PA Sync */ + BLE_AUDIO_EVENT_BASS_SYNC_TERMINATE, + + /** Synchronise to PA using PAST */ + BLE_AUDIO_EVENT_BASS_SYNC_PAST, + } pa_sync_operation; +}; + +/** @brief BASS Set Broadcast Code */ +struct ble_audio_event_bass_set_broadcast_code { + /** Source ID */ + uint8_t source_id; + + /** Event status. 0 on success, BLE_HS Error code otherwise */ + uint8_t status; + + /** Source ID */ + uint8_t broadcast_code[BLE_AUDIO_BROADCAST_CODE_SIZE]; +}; + +/** @brief BASS Source Removed */ +struct ble_audio_event_bass_source_removed { + /** Source ID */ + uint8_t source_id; + + /** Event status. 0 on success, BLE_HS Error code otherwise */ + uint8_t status; +}; + /** * 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 @@ -599,6 +680,48 @@ 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_source_added bass_source_added; + + /** + * @ref BLE_AUDIO_EVENT_BASS_MODIFY_SOURCE + * + * Represents a Broadcast Source being modified in BASS. + */ + struct ble_audio_event_bass_source_modify bass_source_modify; + + /** + * @ref BLE_AUDIO_EVENT_BASS_SET_BROADCAST_CODE + * + * Represents a Broadcast Code baing set in BASS. + */ + struct ble_audio_event_bass_set_broadcast_code bass_set_broadcast_code; + + /** + * @ref BLE_AUDIO_EVENT_BASS_SOURCE_REMOVED + * + * Represents a Broadcast Source being removed in BASS. + */ + struct ble_audio_event_bass_source_removed bass_source_removed; }; }; 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 0000000000..847d74da80 --- /dev/null +++ b/nimble/host/audio/services/bass/include/services/bass/ble_audio_svc_bass.h @@ -0,0 +1,450 @@ +/** + * 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 +#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 + +/** Value of not assigned Source ID of Receiver State */ +#define BLE_SVC_AUDIO_BASS_RECEIVE_STATE_SRC_ID_NONE 0xFF + +/** 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 + */ + 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]; +}; + +/** 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; + + /** Subgroup index of BIS Sync State to be updated */ + uint8_t subgroup_idx; + + /** BIS Sync State */ + uint32_t bis_sync_state; +}; + +/** + * 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]; + + /** + * Valid pointer to provide source ID to be swapped or NULL. + * + * 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 0000000000..e92ab6dd04 --- /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 " +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 0000000000..5e17fadcb0 --- /dev/null +++ b/nimble/host/audio/services/bass/src/ble_audio_svc_bass.c @@ -0,0 +1,878 @@ +/* + * 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_BIS_SYNC_STATE_ANY 0xFFFFFFFF + +ble_uuid_t * bass_receive_state_uuid = + BLE_UUID16_DECLARE(BLE_SVC_AUDIO_BASS_CHR_UUID16_BROADCAST_RECEIVE_STATE); +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 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; + } + + 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_ADD_SOURCE, + .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_source_added.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_source_added.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_source_added.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_source_added.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_source_added.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_source_added.status = BLE_HS_ENOMEM; + goto done; + } + } else { + rcv_state->source_id = operation.add_source.source_id; + } + + ev.bass_source_added.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_source_added.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_source_added.status = rc; + goto done; + } + + ble_audio_event_listener_call(&ev); + + return rc; +} + +static int +check_bis_sync(uint16_t num_subgroups, 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_MODIFY_SOURCE, + .bass_source_modify = { + .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_source_added.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) { + return BLE_HS_EINVAL; + } + + 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_source_added.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)) { + return 0; + } + + 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_source_added.status = BLE_HS_EREJECT; + goto done; + } + } + + ev.bass_source_modify.source_id = operation.modify_source.source_id; + + if (operation.modify_source.pa_sync == BLE_SVC_AUDIO_BASS_PA_SYNC_DO_NOT_SYNC && + rcv_state->state.pa_sync_state != BLE_SVC_AUDIO_BASS_PA_SYNC_STATE_SYNCED) { + rcv_state->state.pa_sync_state = BLE_SVC_AUDIO_BASS_PA_SYNC_STATE_NOT_SYNCED; + ev.bass_source_modify.pa_sync_operation = BLE_AUDIO_EVENT_BASS_NO_SYNC; + } else if (operation.modify_source.pa_sync == BLE_SVC_AUDIO_BASS_PA_SYNC_DO_NOT_SYNC && + rcv_state->state.pa_sync_state == BLE_SVC_AUDIO_BASS_PA_SYNC_STATE_SYNCED) { + rcv_state->state.pa_sync_state = BLE_SVC_AUDIO_BASS_PA_SYNC_STATE_NOT_SYNCED; + ev.bass_source_modify.pa_sync_operation = BLE_AUDIO_EVENT_BASS_SYNC_TERMINATE; + } else if (operation.modify_source.pa_sync == BLE_SVC_AUDIO_BASS_PA_SYNC_SYNC_PAST_AVAILABLE) { + if (MYNEWT_VAL(BLE_PERIODIC_ADV_SYNC_TRANSFER)) { + ev.bass_source_modify.pa_sync_operation = BLE_AUDIO_EVENT_BASS_SYNC_PAST; + rcv_state->state.pa_sync_state = BLE_SVC_AUDIO_BASS_PA_SYNC_STATE_SYNC_INFO_REQ; + } else { + ev.bass_source_modify.pa_sync_operation = BLE_AUDIO_EVENT_BASS_SYNC; + } + } else if (operation.modify_source.pa_sync == + BLE_SVC_AUDIO_BASS_PA_SYNC_SYNC_PAST_NOT_AVAILABLE) { + ev.bass_source_modify.pa_sync_operation = BLE_AUDIO_EVENT_BASS_SYNC; + } + +done: + if (!rc) { + rc = ble_svc_audio_bass_receive_state_notify(rcv_state); + ev.bass_source_added.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_SET_BROADCAST_CODE, + .bass_set_broadcast_code = { + .status = 0 + } + }; + int rc = 0; + + 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) { + rc = BLE_SVC_AUDIO_BASS_ERR_INVALID_SOURCE_ID; + ev.bass_set_broadcast_code.status = BLE_HS_ENOENT; + goto done; + } + + memcpy(ev.bass_set_broadcast_code.broadcast_code, &data[1], BLE_AUDIO_BROADCAST_CODE_SIZE); + +done: + ble_audio_event_listener_call(&ev); + + return rc; +} + +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_REMOVE_SOURCE, + .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_source_removed.source_id); + if (rcv_state == NULL) { + rc = BLE_SVC_AUDIO_BASS_ERR_INVALID_SOURCE_ID; + ev.bass_set_broadcast_code.status = BLE_HS_ENOENT; + goto done; + } + + operation.remove_source.source_id = ev.bass_source_removed.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_source_removed.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_source_removed.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; + 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; + } + + 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); + } + rcv_state->state.subgroups[params->subgroup_idx].bis_sync_state = + params->bis_sync_state; + + 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 0000000000..c054e7b5b0 --- /dev/null +++ b/nimble/host/audio/services/bass/syscfg.yml @@ -0,0 +1,35 @@ +# 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 diff --git a/nimble/host/include/host/ble_att.h b/nimble/host/include/host/ble_att.h index bfd7adf10f..8323c9d764 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 + /** @} */ /**