nimble/services: add PACS
This commit adds Published Audio Capabilities Service/Prifile. In pair
ble_audio_codec module is added, that supports registering supported
codecs with their corresponding configurations.
diff --git a/.github/test_build_apps_syscfg.yml b/.github/test_build_apps_syscfg.yml
index aabcc29..9887918 100644
--- a/.github/test_build_apps_syscfg.yml
+++ b/.github/test_build_apps_syscfg.yml
@@ -54,6 +54,7 @@
BLE_EATT_CHAN_NUM: 2
BLE_PHY_2M: 1
BLE_PHY_CODED: 1
+ BLE_AUDIO_MAX_CODEC_RECORDS: 2
# controller
BLE_LL_CFG_FEAT_LL_PRIVACY: 1
diff --git a/nimble/host/audio/include/audio/ble_audio.h b/nimble/host/audio/include/audio/ble_audio.h
index c1ce1dc..288f13e 100644
--- a/nimble/host/audio/include/audio/ble_audio.h
+++ b/nimble/host/audio/include/audio/ble_audio.h
@@ -208,19 +208,184 @@
*/
/** LE Audio Codec Config Type: Sampling Frequency. */
-#define BLE_AUDIO_CODEC_SAMPLING_FREQ_TYPE 0x01
+#define BLE_AUDIO_CODEC_CONF_SAMPLING_FREQ_TYPE 0x01
/** LE Audio Codec Config Type: Frame Duration. */
-#define BLE_AUDIO_CODEC_FRAME_DURATION_TYPE 0x02
+#define BLE_AUDIO_CODEC_CONF_FRAME_DURATION_TYPE 0x02
/** LE Audio Codec Config Type: Channel Allocation. */
-#define BLE_AUDIO_CODEC_AUDIO_CHANNEL_ALLOCATION_TYPE 0x03
+#define BLE_AUDIO_CODEC_CONF_AUDIO_CHANNEL_ALLOCATION_TYPE 0x03
/** LE Audio Codec Config Type: Octets Per Codec Frame. */
-#define BLE_AUDIO_CODEC_OCTETS_PER_CODEC_FRAME_TYPE 0x04
+#define BLE_AUDIO_CODEC_CONF_OCTETS_PER_CODEC_FRAME_TYPE 0x04
/** LE Audio Codec Config Type: Frame Blocks Per SDU. */
-#define BLE_AUDIO_CODEC_FRAME_BLOCKS_PER_SDU_TYPE 0x05
+#define BLE_AUDIO_CODEC_CONF_FRAME_BLOCKS_PER_SDU_TYPE 0x05
+
+/** @} */
+
+/**
+ * @defgroup ble_audio_codec_config Bluetooth Low Energy Audio Codec Specific Capabilities
+ * @{
+ */
+
+
+/** LE Audio Codec Specific Capability: Supported Sampling Frequencies. */
+#define BLE_AUDIO_CODEC_CAPS_SAMPLING_FREQ_TYPE 0x01
+
+/** LE Audio Codec Specific Capability: Supported Frame Durations. */
+#define BLE_AUDIO_CODEC_CAPS_FRAME_DURATION_TYPE 0x02
+
+/** LE Audio Codec Specific Capability: Supported Audio Channel Counts. */
+#define BLE_AUDIO_CODEC_CAPS_SUP_AUDIO_CHANNEL_COUNTS_TYPE 0x03
+
+/** LE Audio Codec Specific Capability: Supported Octets Per Codec Frame. */
+#define BLE_AUDIO_CODEC_CAPS_OCTETS_PER_CODEC_FRAME_TYPE 0x04
+
+/** LE Audio Codec Specific Capability: Supported Codec Frames Per SDU. */
+#define BLE_AUDIO_CODEC_CAPS_FRAMES_PER_SDU_TYPE 0x05
+
+/** @} */
+
+/**
+ * @defgroup ble_audio_contexts Bluetooth Low Energy Audio Context Types
+ * @{
+ */
+
+/** LE Audio Codec Context Type: Prohibited. */
+#define BLE_AUDIO_CONTEXT_TYPE_PROHIBITED 0x0000
+
+/** LE Audio Codec Context Type: Unspecified. */
+#define BLE_AUDIO_CONTEXT_TYPE_UNSPECIFIED 0x0001
+
+/** LE Audio Codec Context Type: Conversational. */
+#define BLE_AUDIO_CONTEXT_TYPE_CONVERSATIONAL 0x0002
+
+/** LE Audio Codec Context Type: Media. */
+#define BLE_AUDIO_CONTEXT_TYPE_MEDIA 0x0004
+
+/** LE Audio Codec Context Type: Game. */
+#define BLE_AUDIO_CONTEXT_TYPE_GAME 0x0008
+
+/** LE Audio Codec Context Type: Instructional. */
+#define BLE_AUDIO_CONTEXT_TYPE_INSTRUCTIONAL 0x0010
+
+/** LE Audio Codec Context Type: Voice Assistants. */
+#define BLE_AUDIO_CONTEXT_TYPE_VOICE_ASSISTANTS 0x0020
+
+/** LE Audio Codec Context Type: Live. */
+#define BLE_AUDIO_CONTEXT_TYPE_LIVE 0x0040
+
+/** LE Audio Codec Context Type: Sound Effects. */
+#define BLE_AUDIO_CONTEXT_TYPE_SOUND_EFFECTS 0x0080
+
+/** LE Audio Codec Context Type: Notifications. */
+#define BLE_AUDIO_CONTEXT_TYPE_NOTIFICATIONS 0x0100
+
+/** LE Audio Codec Context Type: Ringtone. */
+#define BLE_AUDIO_CONTEXT_TYPE_RINGTONE 0x0200
+
+/** LE Audio Codec Context Type: Alerts. */
+#define BLE_AUDIO_CONTEXT_TYPE_ALERTS 0x0400
+
+/** LE Audio Codec Context Type: EmergencyAlarm. */
+#define BLE_AUDIO_CONTEXT_TYPE_EMERGENCY_ALARM 0x0800
+
+/** @} */
+
+/**
+ * @defgroup ble_audio_contexts Bluetooth Low Energy Audio Supported Frame Durations
+ * @{
+ */
+
+/** LE Audio Codec Supported Frame Duration: 7.5 ms frame duration. */
+#define BLE_AUDIO_CODEC_SUPPORTED_FRAME_DURATION_7_5_MS 0x0001
+
+/** LE Audio Codec Supported Frame Duration: 10 ms frame duration. */
+#define BLE_AUDIO_CODEC_SUPPORTED_FRAME_DURATION_10_MS 0x0002
+
+/** LE Audio Codec Supported Frame Duration: 7.5 ms preferred. */
+#define BLE_AUDIO_CODEC_PREFERED_FRAME_DURATION_7_5_MS 0x0010
+
+/** LE Audio Codec Supported Frame Duration: 10 ms preferred. */
+#define BLE_AUDIO_CODEC_PREFERED_FRAME_DURATION_10_MS 0x0020
+
+/** @} */
+
+/**
+ * @defgroup ble_audio_contexts Bluetooth Low Energy Audio Supported Sampling Frequencies
+ * @{
+ */
+
+/** LE Audio Codec Supported Sampling Frequency: 8000 Hz. */
+#define BLE_AUDIO_CODEC_SUPPORTED_SAMPLING_RATE_8000_HZ (1ULL << 0)
+
+/** LE Audio Codec Supported Sampling Frequency: 11025 Hz. */
+#define BLE_AUDIO_CODEC_SUPPORTED_SAMPLING_RATE_11025_HZ (1ULL << 1)
+
+/** LE Audio Codec Supported Sampling Frequency: 16000 Hz. */
+#define BLE_AUDIO_CODEC_SUPPORTED_SAMPLING_RATE_16000_HZ (1ULL << 2)
+
+/** LE Audio Codec Supported Sampling Frequency: 22050 Hz. */
+#define BLE_AUDIO_CODEC_SUPPORTED_SAMPLING_RATE_22050_HZ (1ULL << 3)
+
+/** LE Audio Codec Supported Sampling Frequency: 24000 Hz. */
+#define BLE_AUDIO_CODEC_SUPPORTED_SAMPLING_RATE_24000_HZ (1ULL << 4)
+
+/** LE Audio Codec Supported Sampling Frequency: 32000 Hz. */
+#define BLE_AUDIO_CODEC_SUPPORTED_SAMPLING_RATE_32000_HZ (1ULL << 5)
+
+/** LE Audio Codec Supported Sampling Frequency: 44100 Hz. */
+#define BLE_AUDIO_CODEC_SUPPORTED_SAMPLING_RATE_44100_HZ (1ULL << 6)
+
+/** LE Audio Codec Supported Sampling Frequency: 48000 Hz. */
+#define BLE_AUDIO_CODEC_SUPPORTED_SAMPLING_RATE_48000_HZ (1ULL << 7)
+
+/** LE Audio Codec Supported Sampling Frequency: 88200 Hz. */
+#define BLE_AUDIO_CODEC_SUPPORTED_SAMPLING_RATE_88200_HZ (1ULL << 8)
+
+/** LE Audio Codec Supported Sampling Frequency: 96000 Hz. */
+#define BLE_AUDIO_CODEC_SUPPORTED_SAMPLING_RATE_96000_HZ (1ULL << 9)
+
+/** LE Audio Codec Supported Sampling Frequency: 176400 Hz. */
+#define BLE_AUDIO_CODEC_SUPPORTED_SAMPLING_RATE_176400_HZ (1ULL << 10)
+
+/** LE Audio Codec Supported Sampling Frequency: 192000 Hz. */
+#define BLE_AUDIO_CODEC_SUPPORTED_SAMPLING_RATE_192000_HZ (1ULL << 11)
+
+/** LE Audio Codec Supported Sampling Frequency: 384000 Hz. */
+#define BLE_AUDIO_CODEC_SUPPORTED_SAMPLING_RATE_384000_HZ (1ULL << 12)
+
+/** @} */
+
+/**
+ * @defgroup ble_audio_contexts Bluetooth Low Energy Audio Supported Channel Counts
+ * @{
+ */
+
+/** LE Audio Codec Supported Channel Count: 1. */
+#define BLE_AUDIO_CODEC_SUPPORTED_CHANNEL_COUNT_1 0x0001
+
+/** LE Audio Codec Supported Channel Count: 2. */
+#define BLE_AUDIO_CODEC_SUPPORTED_CHANNEL_COUNT_2 0x0002
+
+/** LE Audio Codec Supported Channel Count: 3. */
+#define BLE_AUDIO_CODEC_SUPPORTED_CHANNEL_COUNT_3 0x0004
+
+/** LE Audio Codec Supported Channel Count: 4. */
+#define BLE_AUDIO_CODEC_SUPPORTED_CHANNEL_COUNT_4 0x0008
+
+/** LE Audio Codec Supported Channel Count: 5. */
+#define BLE_AUDIO_CODEC_SUPPORTED_CHANNEL_COUNT_5 0x0010
+
+/** LE Audio Codec Supported Channel Count: 6. */
+#define BLE_AUDIO_CODEC_SUPPORTED_CHANNEL_COUNT_6 0x0020
+
+/** LE Audio Codec Supported Channel Count: 7. */
+#define BLE_AUDIO_CODEC_SUPPORTED_CHANNEL_COUNT_7 0x0040
+
+/** LE Audio Codec Supported Channel Count: 8. */
+#define BLE_AUDIO_CODEC_SUPPORTED_CHANNEL_COUNT_8 0x0080
/** @} */
@@ -244,16 +409,55 @@
_octets_per_codec_frame, \
_codec_frame_blocks_per_sdu) \
{ \
- 2, BLE_AUDIO_CODEC_SAMPLING_FREQ_TYPE, _sampling_freq, \
- 2, BLE_AUDIO_CODEC_FRAME_DURATION_TYPE, _frame_duration, \
- OPTIONAL_FIELD(5, BLE_AUDIO_CODEC_AUDIO_CHANNEL_ALLOCATION_TYPE, \
+ 2, BLE_AUDIO_CODEC_CONF_SAMPLING_FREQ_TYPE, _sampling_freq, \
+ 2, BLE_AUDIO_CODEC_CONF_FRAME_DURATION_TYPE, _frame_duration, \
+ OPTIONAL_FIELD(5, BLE_AUDIO_CODEC_CONF_AUDIO_CHANNEL_ALLOCATION_TYPE, \
_audio_channel_alloc) \
- 3, BLE_AUDIO_CODEC_OCTETS_PER_CODEC_FRAME_TYPE, \
+ 3, BLE_AUDIO_CODEC_CONF_OCTETS_PER_CODEC_FRAME_TYPE, \
(_octets_per_codec_frame), ((_octets_per_codec_frame) >> 8), \
- OPTIONAL_FIELD(2, BLE_AUDIO_CODEC_FRAME_BLOCKS_PER_SDU_TYPE, \
+ OPTIONAL_FIELD(2, BLE_AUDIO_CODEC_CONF_FRAME_BLOCKS_PER_SDU_TYPE, \
_codec_frame_blocks_per_sdu) \
}
+/**
+ * @brief Helper macro used to build LTV array of Codec_Specific_Capabilities.
+ *
+ * @param _sampling_freq Supported_Sampling_Frequencies -
+ * two octet value
+ * @param _frame_duration Supported_Frame_Durations - single
+ * octet value
+ * @param _audio_channel_counts Supported_Audio_Channel_Counts -
+ * single octet value
+ * @param _min_octets_per_codec_frame minimum value of
+ * Supported_Octets_Per_Codec_Frame -
+ * two octet value
+ * @param _max_octets_per_codec_frame maximum value of
+ * Supported_Octets_Per_Codec_Frame -
+ * two octet value
+ * @param _codec_frames_per_sdu Supported_Max_Codec_Frames_Per_SDU -
+ * single octet value
+ *
+ * @return Pointer to a `ble_uuid16_t` structure.
+ */
+#define BLE_AUDIO_BUILD_CODEC_CAPS(_sampling_freq, \
+ _frame_duration, \
+ _audio_channel_counts, \
+ _min_octets_per_codec_frame, \
+ _max_octets_per_codec_frame, \
+ _codec_frames_per_sdu) \
+ { \
+ 3, BLE_AUDIO_CODEC_CAPS_SAMPLING_FREQ_TYPE, \
+ (_sampling_freq), ((_sampling_freq) >> 8), \
+ 2, BLE_AUDIO_CODEC_CAPS_FRAME_DURATION_TYPE, _frame_duration, \
+ OPTIONAL_FIELD(2, BLE_AUDIO_CODEC_CAPS_SUP_AUDIO_CHANNEL_COUNTS_TYPE, \
+ _audio_channel_counts) \
+ 5, BLE_AUDIO_CODEC_CAPS_OCTETS_PER_CODEC_FRAME_TYPE, \
+ (_min_octets_per_codec_frame), ((_min_octets_per_codec_frame) >> 8), \
+ (_max_octets_per_codec_frame), ((_max_octets_per_codec_frame) >> 8), \
+ OPTIONAL_FIELD(2, BLE_AUDIO_CODEC_CAPS_FRAMES_PER_SDU_TYPE, \
+ _codec_frames_per_sdu) \
+ }
+
/** Codec Information */
struct ble_audio_codec_id {
/** Coding Format */
@@ -306,6 +510,12 @@
/** BLE Audio event: Broadcast Announcement */
#define BLE_AUDIO_EVENT_BROADCAST_ANNOUNCEMENT 0
+/** BLE Audio event: Codec Registered */
+#define BLE_AUDIO_EVENT_CODEC_REGISTERED 1
+
+/** BLE Audio event: Codec Unregistered */
+#define BLE_AUDIO_EVENT_CODEC_UNREGISTERED 2
+
/** @} */
/** @brief Broadcast Announcement */
@@ -329,6 +539,18 @@
struct ble_audio_broadcast_name *name;
};
+/** @brief Codec Registered */
+struct ble_audio_event_codec_registered {
+ /** Codec Record */
+ const struct ble_audio_codec_record *record;
+};
+
+/** @brief Codec Unregistered */
+struct ble_audio_event_codec_unregistered {
+ /** Codec Record */
+ const struct ble_audio_codec_record *record;
+};
+
/**
* 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
@@ -352,6 +574,20 @@
* Represents a received Broadcast Announcement.
*/
struct ble_audio_event_broadcast_announcement broadcast_announcement;
+
+ /**
+ * @ref BLE_AUDIO_EVENT_CODEC_REGISTERED
+ *
+ * Represents a codec registration.
+ */
+ struct ble_audio_event_codec_registered codec_registered;
+
+ /**
+ * @ref BLE_AUDIO_EVENT_CODEC_UNREGISTERED
+ *
+ * Represents a codec registration.
+ */
+ struct ble_audio_event_codec_unregistered codec_unregistered;
};
};
diff --git a/nimble/host/audio/include/audio/ble_audio_codec.h b/nimble/host/audio/include/audio/ble_audio_codec.h
new file mode 100644
index 0000000..dcb1df9
--- /dev/null
+++ b/nimble/host/audio/include/audio/ble_audio_codec.h
@@ -0,0 +1,146 @@
+/*
+ * 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_CODEC_
+#define H_BLE_AUDIO_CODEC_
+
+/**
+ * @file ble_audio_codec.h
+ *
+ * @brief Bluetooth LE Audio Codec
+ *
+ * This header file provides the public API for managing LE Audio Codecs
+ *
+ * @defgroup ble_audio_codec Bluetooth LE Audio Codec
+ * @ingroup bt_host
+ * @{
+ *
+ * This API allows to create and manage list of LE Audio codecs with their capabilities and
+ * metadata. This list can be used by higher level services, like PACS. Memory management of
+ * codec entries is left to application and neither static nor dynamic allocation is enforced.
+ *
+ */
+
+#include "stdint.h"
+#include "ble_audio.h"
+
+/** Bit describing direction of codec configuration - source */
+#define BLE_AUDIO_CODEC_DIR_SOURCE_BIT (1 << 0)
+/** Bit describing direction of codec configuration - sink */
+#define BLE_AUDIO_CODEC_DIR_SINK_BIT (1 << 1)
+
+/** Codec list entry */
+struct ble_audio_codec_record {
+ /* Pointer to next codec list entry */
+ STAILQ_ENTRY(ble_audio_codec_record) next;
+
+ /* Codec ID */
+ struct ble_audio_codec_id codec_id;
+
+ /* Length of Codec Specific Capabilities */
+ uint8_t codec_spec_caps_len;
+
+ /* Codec Specific Capabilities data */
+ const uint8_t *codec_spec_caps;
+
+ /* Metadata length */
+ uint8_t metadata_len;
+
+ /* Metadata */
+ const uint8_t *metadata;
+
+ /* Bitfield describing direction that codec is acting on. It is a logical OR of:
+ * - BLE_AUDIO_CODEC_DIR_SOURCE_BIT
+ * - BLE_AUDIO_CODEC_DIR_SINK_BIT
+ */
+ uint8_t direction;
+};
+
+/** Type definition codec iteration callback function. */
+typedef int ble_audio_codec_foreach_fn(const struct ble_audio_codec_record *record, void *arg);
+
+struct ble_audio_codec_register_params {
+ /* Codec ID structure */
+ struct ble_audio_codec_id codec_id;
+
+ /* Codec Specific Capabilities length */
+ uint8_t codec_spec_caps_len;
+
+ /* Codec Specific Capabilities data */
+ uint8_t *codec_spec_caps;
+
+ /* Metadata length */
+ uint8_t metadata_len;
+
+ /* Metadata */
+ uint8_t *metadata;
+
+ /* Bitfield describing direction that codec is acting on. It is a logical OR of:
+ * - BLE_AUDIO_CODEC_DIR_SOURCE_BIT
+ * - BLE_AUDIO_CODEC_DIR_SINK_BIT
+ */
+ uint8_t direction;
+};
+
+/**
+ * @brief Register codec entry
+ *
+ * @param[in] params Pointer to a `ble_audio_codec_register_params`
+ * structure that defines Codec Specific Capabilities
+ * @param[out] out_record Pointer to registered codec entry.
+ *
+ * @return 0 on success;
+ * A non-zero value on failure.
+ */
+int ble_audio_codec_register(const struct ble_audio_codec_register_params
+ *params,
+ struct ble_audio_codec_record *out_record);
+/**
+ * @brief Remove codec entry from register
+ *
+ * @param[in] codec_record Pointer to registered codec entry.
+ *
+ * @return 0 on success;
+ * A non-zero value on failure.
+ */
+int ble_audio_codec_unregister(struct ble_audio_codec_record *codec_record);
+
+/**
+ * @brief Iterate through all registered codecs and call function on every
+ * one of them.
+ *
+ * @param[in] direction Codec entry direction. It is any
+ * combination of following bits:
+ * - BLE_AUDIO_CODEC_DIR_SOURCE_BIT
+ * - BLE_AUDIO_CODEC_DIR_SINK_BIT
+ * This filters entries so the callback is called
+ * only on these have matching direction bit set.
+ * @param[in] cb Callback to be called on codec entries.
+ * @param[in] arg Optional callback argument.
+ *
+ * @return 0 on success;
+ * A non-zero value on failure.
+ */
+int ble_audio_codec_foreach(uint8_t direction, ble_audio_codec_foreach_fn *cb, void *arg);
+
+/**
+ * @}
+ */
+
+#endif /* H_BLE_AUDIO_CODEC_ */
diff --git a/nimble/host/audio/services/pacs/include/services/pacs/ble_audio_svc_pacs.h b/nimble/host/audio/services/pacs/include/services/pacs/ble_audio_svc_pacs.h
new file mode 100644
index 0000000..fdbcbee
--- /dev/null
+++ b/nimble/host/audio/services/pacs/include/services/pacs/ble_audio_svc_pacs.h
@@ -0,0 +1,95 @@
+/*
+ * 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_PACS_
+#define H_BLE_AUDIO_SVC_PACS_
+
+/**
+ * @file ble_audio_svc_pacs.h
+ *
+ * @brief Bluetooth LE Audio PAC Service
+ *
+ * This header file provides the public API for interacting with the PACS package.
+ *
+ * @defgroup ble_audio_svc_pacs Bluetooth LE Audio PACS package
+ * @ingroup bt_host
+ * @{
+ *
+ * This package is used to setup PACS for codecs registered with @ref ble_audio_codec.
+ * To register a codec create it's definition as `ble_audio_codec_record` structure and register it
+ * using `ble_audio_codec_register()`. Up to BLE_AUDIO_MAX_CODEC_RECORDS entries may be registered.
+ * Registering and unregistering codecs, as well as setting PACS parameters will trigger sending
+ * notifications, if their support is enabled (see pacs/syscfg.yml).
+ *
+ */
+
+#define BLE_SVC_AUDIO_PACS_UUID16 0x1850
+#define BLE_SVC_AUDIO_PACS_CHR_UUID16_SINK_PAC 0x2BC9
+#define BLE_SVC_AUDIO_PACS_CHR_UUID16_SINK_AUDIO_LOCATIONS 0x2BCA
+#define BLE_SVC_AUDIO_PACS_CHR_UUID16_SOURCE_PAC 0x2BCB
+#define BLE_SVC_AUDIO_PACS_CHR_UUID16_SOURCE_AUDIO_LOCATIONS 0x2BCC
+#define BLE_SVC_AUDIO_PACS_CHR_UUID16_AVAILABLE_AUDIO_CONTEXTS 0x2BCD
+#define BLE_SVC_AUDIO_PACS_CHR_UUID16_SUPPORTED_AUDIO_CONTEXTS 0x2BCE
+
+
+struct ble_svc_audio_pacs_set_param {
+ /* Supported Audio Locations */
+ uint32_t audio_locations;
+
+ /* Supported Contexts */
+ uint16_t supported_contexts;
+};
+
+/**
+ * @brief Set PACS params.
+ *
+ * Set device capabilities reported in Published Audio Capabilities Service.
+ *
+ * @param[in] flags Flags that define if capabilities being set are for
+ * Sink or Source. Valid values are either
+ * `BLE_AUDIO_CODEC_FLAG_SOURCE` or `BLE_AUDIO_CODEC_FLAG_SINK`
+ * @param[in] param Pointer to a `ble_svc_audio_pacs_set_param`
+ * structure that defines capabilities supported by
+ * device.
+ *
+ * @return 0 on success;
+ * A non-zero value on failure.
+ */
+int ble_svc_audio_pacs_set(uint8_t flags, struct ble_svc_audio_pacs_set_param *param);
+
+/**
+ * @brief Set available context types.
+ *
+ * @param[in] conn_handle Connection handle identifying connection for which contexts
+ * being set
+ * @param[in] sink_contexts Available Sink Contexts
+ * @param[in] source_contexts Available Source Contexts
+ *
+ * @return 0 on success;
+ * A non-zero value on failure.
+ */
+int ble_svc_audio_pacs_avail_contexts_set(uint16_t conn_handle,
+ uint16_t sink_contexts,
+ uint16_t source_contexts);
+
+/**
+ * @}
+ */
+
+#endif /* H_BLE_AUDIO_SVC_PACS_ */
diff --git a/nimble/host/audio/services/pacs/lc3/include/services/pacs/ble_audio_svc_pacs_lc3.h b/nimble/host/audio/services/pacs/lc3/include/services/pacs/ble_audio_svc_pacs_lc3.h
new file mode 100644
index 0000000..ddd6f54
--- /dev/null
+++ b/nimble/host/audio/services/pacs/lc3/include/services/pacs/ble_audio_svc_pacs_lc3.h
@@ -0,0 +1,63 @@
+/*
+ * 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_PACS_LC3_
+#define H_BLE_AUDIO_SVC_PACS_LC3_
+
+
+/**
+ * @file ble_audio_svc_pacs_lc3.h
+ *
+ * @brief Bluetooth PAC Service for LC3 Codec
+ *
+ * This header file provides the public API for interacting with the PACS LC3 package, that
+ * registers PAC entry for LC3 codec with configurations contained in system configuration file
+ *
+ * @defgroup ble_audio_svc_pacs_lc3 Bluetooth LE Audio PACS LC3 package
+ * @ingroup ble_audio_svc_pacs
+ * @{
+ *
+ * This package is an example how to register codec entry that PACS can use to construct its entries
+ * for GATT database. This is high level package that can be used to construct basic PACS setup for
+ * LC3 codec. This package creates only single PAC entry per source and sink. If more PAC entries
+ * need to be created, with more advanced setup, @ref ble_audio_svc_pacs service shall be used in
+ * combination with @ref ble_audio_codec API.
+ *
+ */
+
+/**
+ * @brief Set available context types.
+ *
+ * @param[in] conn_handle Connection handle identifying connection for which contexts
+ * being set
+ * @param[in] sink_contexts Available Sink Contexts
+ * @param[in] source_contexts Available Source Contexts
+ *
+ * @return 0 on success;
+ * A non-zero value on failure.
+ */
+int ble_svc_audio_pacs_lc3_set_avail_contexts(uint16_t conn_handle,
+ uint16_t sink_contexts,
+ uint16_t source_contexts);
+
+/**
+ * @}
+ */
+
+#endif /* H_BLE_AUDIO_SVC_PACS_LC3_ */
diff --git a/nimble/host/audio/services/pacs/lc3/pkg.yml b/nimble/host/audio/services/pacs/lc3/pkg.yml
new file mode 100644
index 0000000..eb8f6b6
--- /dev/null
+++ b/nimble/host/audio/services/pacs/lc3/pkg.yml
@@ -0,0 +1,34 @@
+# 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/pacs/lc3
+pkg.description: LC3 codec entry for Published Audio Capabilities Service
+pkg.author: "Apache Mynewt <dev@mynewt.apache.org>"
+pkg.homepage: "https://mynewt.apache.org/"
+pkg.keywords:
+ - ble
+ - bluetooth
+ - pacs
+ - nimble
+
+pkg.deps:
+ - nimble/host/audio/services/pacs
+ - nimble/host
+
+pkg.init:
+ ble_svc_audio_pacs_lc3_init:
+ - $after:ble_svc_audio_pacs_init
diff --git a/nimble/host/audio/services/pacs/lc3/src/ble_audio_svc_pacs_lc3.c b/nimble/host/audio/services/pacs/lc3/src/ble_audio_svc_pacs_lc3.c
new file mode 100644
index 0000000..61c7036
--- /dev/null
+++ b/nimble/host/audio/services/pacs/lc3/src/ble_audio_svc_pacs_lc3.c
@@ -0,0 +1,153 @@
+/*
+ * 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 "audio/ble_audio_codec.h"
+#include "services/pacs/ble_audio_svc_pacs.h"
+#include "syscfg/syscfg.h"
+#include "host/ble_hs.h"
+
+/* Below is to unmangle comma separated Metadata octets from MYNEWT_VAL */
+#define _Args(...) __VA_ARGS__
+#define STRIP_PARENS(X) X
+#define UNMANGLE_MYNEWT_VAL(X) STRIP_PARENS(_Args X)
+
+#define BLE_SVC_AUDIO_PACS_LC3_CODEC_ID 0x06
+
+static uint8_t ble_svc_audio_pacs_lc3_src_codec_spec_caps[] = BLE_AUDIO_BUILD_CODEC_CAPS(
+ MYNEWT_VAL(BLE_SVC_AUDIO_PACS_LC3_SRC_SAMPLING_FREQUENCIES),
+ MYNEWT_VAL(BLE_SVC_AUDIO_PACS_LC3_SRC_FRAME_DURATIONS),
+#ifdef MYNEWT_VAL_BLE_SVC_AUDIO_PACS_LC3_SRC_AUDIO_CHANNEL_COUNTS
+ MYNEWT_VAL(BLE_SVC_AUDIO_PACS_LC3_SRC_AUDIO_CHANNEL_COUNTS),
+#else
+ ,
+#endif
+ MYNEWT_VAL(BLE_SVC_AUDIO_PACS_LC3_SRC_MIN_OCTETS_PER_CODEC_FRAME),
+ MYNEWT_VAL(BLE_SVC_AUDIO_PACS_LC3_SRC_MAX_OCTETS_PER_CODEC_FRAME),
+#ifdef MYNEWT_VAL_BLE_SVC_AUDIO_PACS_LC3_SRC_MAX_CODEC_FRAMES_PER_SDU
+ MYNEWT_VAL(BLE_SVC_AUDIO_PACS_LC3_SRC_MAX_CODEC_FRAMES_PER_SDU),
+#endif
+);
+
+static uint8_t ble_svc_audio_pacs_lc3_snk_codec_spec_caps[] = BLE_AUDIO_BUILD_CODEC_CAPS(
+ MYNEWT_VAL(BLE_SVC_AUDIO_PACS_LC3_SNK_SAMPLING_FREQUENCIES),
+ MYNEWT_VAL(BLE_SVC_AUDIO_PACS_LC3_SNK_FRAME_DURATIONS),
+ #ifdef MYNEWT_VAL_BLE_SVC_AUDIO_PACS_LC3_SRC_AUDIO_CHANNEL_COUNTS
+ MYNEWT_VAL(BLE_SVC_AUDIO_PACS_LC3_SNK_AUDIO_CHANNEL_COUNTS),
+ #else
+ ,
+ #endif
+ MYNEWT_VAL(BLE_SVC_AUDIO_PACS_LC3_SNK_MIN_OCTETS_PER_CODEC_FRAME),
+ MYNEWT_VAL(BLE_SVC_AUDIO_PACS_LC3_SRC_MAX_OCTETS_PER_CODEC_FRAME),
+ #ifdef MYNEWT_VAL_BLE_SVC_AUDIO_PACS_LC3_SRC_MAX_CODEC_FRAMES_PER_SDU
+ MYNEWT_VAL(BLE_SVC_AUDIO_PACS_LC3_SNK_MAX_CODEC_FRAMES_PER_SDU),
+ #endif
+);
+
+#ifdef MYNEWT_VAL_BLE_SVC_AUDIO_PACS_LC3_SRC_METADATA
+static uint8_t ble_svc_audio_pacs_lc3_src_metadata[] =
+{ UNMANGLE_MYNEWT_VAL(MYNEWT_VAL(BLE_SVC_AUDIO_PACS_LC3_SRC_METADATA)) };
+#endif
+
+#ifdef MYNEWT_VAL_BLE_SVC_AUDIO_PACS_LC3_SNK_METADATA
+static uint8_t ble_svc_audio_pacs_lc3_snk_metadata[] =
+{ UNMANGLE_MYNEWT_VAL(MYNEWT_VAL(BLE_SVC_AUDIO_PACS_LC3_SNK_METADATA)) };
+#endif
+
+static struct ble_audio_codec_register_params src_codec_params = {
+ .codec_id = {
+ .format = BLE_SVC_AUDIO_PACS_LC3_CODEC_ID,
+ .company_id = 0x00,
+ .vendor_specific = 0x00
+ },
+ .codec_spec_caps_len = sizeof(ble_svc_audio_pacs_lc3_src_codec_spec_caps),
+ .codec_spec_caps = ble_svc_audio_pacs_lc3_src_codec_spec_caps,
+#ifdef MYNEWT_VAL_BLE_SVC_AUDIO_PACS_LC3_SRC_METADATA
+ .metadata_len = sizeof(ble_svc_audio_pacs_lc3_src_metadata),
+ .metadata = ble_svc_audio_pacs_lc3_src_metadata,
+#else
+ .metadata_len = 0,
+#endif
+ .direction = BLE_AUDIO_CODEC_DIR_SOURCE_BIT
+};
+
+static struct ble_audio_codec_register_params snk_codec_params = {
+ .codec_id = {
+ .format = BLE_SVC_AUDIO_PACS_LC3_CODEC_ID,
+ .company_id = 0x00,
+ .vendor_specific = 0x00
+ },
+ .codec_spec_caps_len = sizeof(ble_svc_audio_pacs_lc3_snk_codec_spec_caps),
+ .codec_spec_caps = ble_svc_audio_pacs_lc3_snk_codec_spec_caps,
+#ifdef MYNEWT_VAL_BLE_SVC_AUDIO_PACS_LC3_SNK_METADATA
+ .metadata_len = sizeof(ble_svc_audio_pacs_lc3_snk_metadata),
+ .metadata = ble_svc_audio_pacs_lc3_snk_metadata,
+#else
+ .metadata_len = 0,
+#endif
+
+ .direction = BLE_AUDIO_CODEC_DIR_SINK_BIT
+};
+
+static int
+codec_register(void)
+{
+ int rc;
+
+ rc = ble_audio_codec_register(&src_codec_params, NULL);
+ SYSINIT_PANIC_ASSERT(rc == 0);
+
+ rc = ble_audio_codec_register(&snk_codec_params, NULL);
+ SYSINIT_PANIC_ASSERT(rc == 0);
+
+ return 0;
+}
+
+int
+ble_svc_audio_pacs_lc3_set_avail_contexts(uint16_t conn_handle,
+ uint16_t sink_contexts,
+ uint16_t source_contexts)
+{
+ return ble_svc_audio_pacs_avail_contexts_set(conn_handle, sink_contexts,
+ source_contexts);
+}
+
+void
+ble_svc_audio_pacs_lc3_init(void)
+{
+ struct ble_svc_audio_pacs_set_param src_params = {
+ .audio_locations = MYNEWT_VAL(BLE_SVC_AUDIO_PACS_LC3_SRC_AUDIO_LOCATIONS),
+ .supported_contexts = MYNEWT_VAL(BLE_SVC_AUDIO_PACS_LC3_SRC_SUP_CONTEXTS)
+ };
+ struct ble_svc_audio_pacs_set_param snk_params = {
+ .audio_locations = MYNEWT_VAL(BLE_SVC_AUDIO_PACS_LC3_SNK_SUP_AUDIO_LOCATIONS),
+ .supported_contexts = MYNEWT_VAL(BLE_SVC_AUDIO_PACS_LC3_SNK_SUP_CONTEXTS)
+ };
+ int rc;
+
+ rc = codec_register();
+ SYSINIT_PANIC_ASSERT(rc == 0);
+
+ rc = ble_svc_audio_pacs_set(BLE_AUDIO_CODEC_DIR_SOURCE_BIT, &src_params);
+ SYSINIT_PANIC_ASSERT(rc == 0);
+
+ rc = ble_svc_audio_pacs_set(BLE_AUDIO_CODEC_DIR_SINK_BIT, &snk_params);
+ SYSINIT_PANIC_ASSERT(rc == 0);
+
+ (void)rc;
+}
diff --git a/nimble/host/audio/services/pacs/lc3/syscfg.yml b/nimble/host/audio/services/pacs/lc3/syscfg.yml
new file mode 100644
index 0000000..f2a2ff0
--- /dev/null
+++ b/nimble/host/audio/services/pacs/lc3/syscfg.yml
@@ -0,0 +1,131 @@
+# 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_PACS_LC3_SRC_SAMPLING_FREQUENCIES:
+ description: >
+ Sampling frequencies supported by LC3 codec, as source. This setting is mandatory.
+ Accepts any combination of values defined in Bluetooth Assigned Numbers 6.12.4.1.
+ Default value: 48000Hz
+ value: 0x80
+ restrictions:
+ - $notnull
+ BLE_SVC_AUDIO_PACS_LC3_SRC_FRAME_DURATIONS:
+ description: >
+ Frame Durations supported by LC3 codec, as source. This setting is mandatory.
+ Accepts any combination of values defined in Bluetooth Assigned Numbers 6.12.4.2.
+ Default value: 7.5ms and 10ms supported, 10ms preferred.
+ value: 0x23
+ restrictions:
+ - $notnull
+ BLE_SVC_AUDIO_PACS_LC3_SRC_AUDIO_CHANNEL_COUNTS:
+ description: >
+ Audio Channel Counts supported by LC3 codec, as source. This setting is optional.
+ Accepts any combination of values defined in Bluetooth Assigned Numbers 6.12.4.3.
+ value:
+ BLE_SVC_AUDIO_PACS_LC3_SRC_MIN_OCTETS_PER_CODEC_FRAME:
+ description: >
+ Minimum number of Octets Per Codec Frame supported by LC3 codec, as source.
+ This setting is mandatory. Default value: 80
+ value: 80
+ restrictions:
+ - $notnull
+ BLE_SVC_AUDIO_PACS_LC3_SRC_MAX_OCTETS_PER_CODEC_FRAME:
+ description: >
+ Maximum number of Octets Per Codec Frame supported by LC3 codec, as source.
+ This setting is mandatory. Default value: 120
+ value: 120
+ restrictions:
+ - $notnull
+ BLE_SVC_AUDIO_PACS_LC3_SRC_MAX_CODEC_FRAMES_PER_SDU:
+ description: >
+ Maximum number of Codec Frames Per SDU supported by LC3 codec, as source.
+ This setting is optional.
+ value:
+ BLE_SVC_AUDIO_PACS_LC3_SRC_METADATA:
+ description: >
+ Optional Metadata to be attached to source codec capabilities. This value shall be in
+ form of bytes forming LTVs of Metadata. Example: '0x03, 0x01, 0x00, 0x08'
+ (lenght = 3, type = 0x01 (Preferred_Audio_Contexts), 0x00, 0x04 (Media))
+ value:
+ BLE_SVC_AUDIO_PACS_LC3_SRC_AUDIO_LOCATIONS:
+ description: >
+ Audio Locations supported by source codec. Value is an any combination of values defined
+ in Bluetooth Assigned Numbers 6.12.1. Default: Front Left and Front Right
+ value: 0x00000003
+ BLE_SVC_AUDIO_PACS_LC3_SRC_SUP_CONTEXTS:
+ description: >
+ Audio Locations supported by source codec. Value is an any combination of values defined
+ in Bluetooth Assigned Numbers 6.12.3. Default: Media
+ value: 0x0004
+
+ BLE_SVC_AUDIO_PACS_LC3_SNK_SAMPLING_FREQUENCIES:
+ description: >
+ Sampling frequencies supported by LC3 codec, as sink. This setting is mandatory.
+ Accepts any combination of values defined in Bluetooth Assigned Numbers 6.12.4.1.
+ Default value: 48000Hz
+ value: 0x80
+ restrictions:
+ - $notnull
+ BLE_SVC_AUDIO_PACS_LC3_SNK_FRAME_DURATIONS:
+ description: >
+ Frame Durations supported by LC3 codec, as sink. This setting is mandatory.
+ Accepts any combination of values defined in Bluetooth Assigned Numbers 6.12.4.2.
+ Default value: 7.5ms and 10ms supported, 10ms preferred.
+ value: 0x23
+ restrictions:
+ - $notnull
+ BLE_SVC_AUDIO_PACS_LC3_SNK_AUDIO_CHANNEL_COUNTS:
+ description: >
+ Audio Channel Counts supported by LC3 codec, as sink. This setting is optional.
+ Accepts any combination of values defined in Bluetooth Assigned Numbers 6.12.4.3.
+ value:
+ BLE_SVC_AUDIO_PACS_LC3_SNK_MIN_OCTETS_PER_CODEC_FRAME:
+ description: >
+ Minimum number of Octets Per Codec Frame supported by LC3 codec, as source.
+ This setting is mandatory. Default value: 80
+ value: 80
+ restrictions:
+ - $notnull
+ BLE_SVC_AUDIO_PACS_LC3_SNK_MAX_OCTETS_PER_CODEC_FRAME:
+ description: >
+ Maximum number of Octets Per Codec Frame supported by LC3 codec, as sink.
+ This setting is mandatory. Default value: 120
+ value: 120
+ restrictions:
+ - $notnull
+ BLE_SVC_AUDIO_PACS_LC3_SNK_MAX_CODEC_FRAMES_PER_SDU:
+ description: >
+ Maximum number of Codec Frames Per SDU supported by LC3 codec, as sink.
+ This setting is optional.
+ value:
+ BLE_SVC_AUDIO_PACS_LC3_SNK_METADATA:
+ description: >
+ Optional Metadata to be attached to sink codec capabilities. This value shall be in
+ form of bytes forming LTVs of Metadata. Example: '0x03, 0x01, 0x00, 0x08'
+ (lenght = 3, type = 0x01 (Preferred_Audio_Contexts), 0x00, 0x04 (Media))
+ value:
+ BLE_SVC_AUDIO_PACS_LC3_SNK_SUP_AUDIO_LOCATIONS:
+ description: >
+ Audio Locations supported by sink codec. Value is an any combination of values defined
+ in Bluetooth Assigned Numbers 6.12.1. Default: Front Left and Front Right
+ value: 0x00000003
+ BLE_SVC_AUDIO_PACS_LC3_SNK_SUP_CONTEXTS:
+ description: >
+ Audio Locations supported by sink codec. Value is an any combination of values defined
+ in Bluetooth Assigned Numbers 6.12.3. Default: Media
+ value: 0x0004
diff --git a/nimble/host/audio/services/pacs/pkg.yml b/nimble/host/audio/services/pacs/pkg.yml
new file mode 100644
index 0000000..799378d
--- /dev/null
+++ b/nimble/host/audio/services/pacs/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/pacs
+pkg.description: Published Audio Capabilities Service
+pkg.author: "Apache Mynewt <dev@mynewt.apache.org>"
+pkg.homepage: "https://mynewt.apache.org/"
+pkg.keywords:
+ - ble
+ - bluetooth
+ - pacs
+ - nimble
+
+pkg.deps:
+ - nimble/host
+ - nimble/host/services/gatt
+
+pkg.init:
+ ble_svc_audio_pacs_init: 'MYNEWT_VAL(BLE_SVC_AUDIO_PACS_SYSINIT_STAGE)'
diff --git a/nimble/host/audio/services/pacs/src/ble_audio_svc_pacs.c b/nimble/host/audio/services/pacs/src/ble_audio_svc_pacs.c
new file mode 100644
index 0000000..9b124a5
--- /dev/null
+++ b/nimble/host/audio/services/pacs/src/ble_audio_svc_pacs.c
@@ -0,0 +1,479 @@
+/*
+ * 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 "audio/ble_audio.h"
+#include "host/ble_hs.h"
+#include "host/ble_gatt.h"
+#include "audio/ble_audio_codec.h"
+#include "services/pacs/ble_audio_svc_pacs.h"
+
+static uint32_t ble_svc_audio_pacs_sink_audio_locations;
+static uint32_t ble_svc_audio_pacs_source_audio_locations;
+static struct available_ctx {
+ uint16_t conn_handle;
+ uint16_t ble_svc_audio_pacs_avail_sink_contexts;
+ uint16_t ble_svc_audio_pacs_avail_source_contexts;
+} ble_svc_audio_pacs_avail_contexts[MYNEWT_VAL(BLE_MAX_CONNECTIONS)] = {
+ [0 ... MYNEWT_VAL(BLE_MAX_CONNECTIONS) - 1] = {
+ .conn_handle = BLE_HS_CONN_HANDLE_NONE,
+ .ble_svc_audio_pacs_avail_sink_contexts = 0,
+ .ble_svc_audio_pacs_avail_source_contexts = 0
+ }
+};
+static uint16_t ble_svc_audio_pacs_sup_sink_contexts;
+static uint16_t ble_svc_audio_pacs_sup_source_contexts;
+
+static struct ble_gap_event_listener ble_pacs_listener;
+static struct ble_audio_event_listener ble_audio_listener;
+
+struct pac_read_cb_arg {
+ struct os_mbuf *om;
+ uint8_t pac_count;
+};
+
+static int
+ble_svc_audio_pacs_access(uint16_t conn_handle, uint16_t attr_handle,
+ struct ble_gatt_access_ctxt *ctxt, void *arg);
+
+static const struct ble_gatt_svc_def ble_svc_audio_pacs_defs[] = {
+ { /*** Service: Published Audio Capabilities Service (PACS) */
+ .type = BLE_GATT_SVC_TYPE_PRIMARY,
+ .uuid = BLE_UUID16_DECLARE(BLE_SVC_AUDIO_PACS_UUID16),
+ .characteristics = (struct ble_gatt_chr_def[]) {
+#if MYNEWT_VAL(BLE_SVC_AUDIO_PACS_SINK_PAC)
+ {
+ .uuid = BLE_UUID16_DECLARE(BLE_SVC_AUDIO_PACS_CHR_UUID16_SINK_PAC),
+ .access_cb = ble_svc_audio_pacs_access,
+ .flags = BLE_GATT_CHR_F_READ | BLE_GATT_CHR_F_READ_ENC
+#if MYNEWT_VAL(BLE_SVC_AUDIO_PACS_SINK_PAC_NOTIFY)
+ | BLE_GATT_CHR_F_NOTIFY
+#endif
+ },
+#endif
+#if MYNEWT_VAL(BLE_SVC_AUDIO_PACS_SINK_AUDIO_LOCATIONS)
+ {
+ .uuid = BLE_UUID16_DECLARE(
+ BLE_SVC_AUDIO_PACS_CHR_UUID16_SINK_AUDIO_LOCATIONS),
+ .access_cb = ble_svc_audio_pacs_access,
+ .flags = BLE_GATT_CHR_F_READ | BLE_GATT_CHR_F_READ_ENC
+#if MYNEWT_VAL(BLE_SVC_AUDIO_PACS_SINK_AUDIO_LOCATIONS_NOTIFY)
+ | BLE_GATT_CHR_F_NOTIFY
+#endif
+ },
+#endif
+#if MYNEWT_VAL(BLE_SVC_AUDIO_PACS_SOURCE_PAC)
+ {
+ .uuid = BLE_UUID16_DECLARE(
+ BLE_SVC_AUDIO_PACS_CHR_UUID16_SOURCE_PAC),
+ .access_cb = ble_svc_audio_pacs_access,
+ .flags = BLE_GATT_CHR_F_READ | BLE_GATT_CHR_F_READ_ENC
+#if MYNEWT_VAL(BLE_SVC_AUDIO_PACS_SOURCE_PAC_NOTIFY)
+ | BLE_GATT_CHR_F_NOTIFY
+#endif
+ },
+#endif
+#if MYNEWT_VAL(BLE_SVC_AUDIO_PACS_SOURCE_AUDIO_LOCATIONS)
+ {
+ .uuid = BLE_UUID16_DECLARE(
+ BLE_SVC_AUDIO_PACS_CHR_UUID16_SOURCE_AUDIO_LOCATIONS),
+ .access_cb = ble_svc_audio_pacs_access,
+ .flags = BLE_GATT_CHR_F_READ | BLE_GATT_CHR_F_READ_ENC
+#if MYNEWT_VAL(BLE_SVC_AUDIO_PACS_SOURCE_AUDIO_LOCATIONS_NOTIFY)
+ | BLE_GATT_CHR_F_NOTIFY
+#endif
+ },
+#endif
+ {
+ .uuid = BLE_UUID16_DECLARE(
+ BLE_SVC_AUDIO_PACS_CHR_UUID16_AVAILABLE_AUDIO_CONTEXTS),
+ .access_cb = ble_svc_audio_pacs_access,
+ .flags = BLE_GATT_CHR_F_READ | BLE_GATT_CHR_F_READ_ENC | BLE_GATT_CHR_F_NOTIFY,
+ }, {
+ .uuid = BLE_UUID16_DECLARE(
+ BLE_SVC_AUDIO_PACS_CHR_UUID16_SUPPORTED_AUDIO_CONTEXTS),
+ .access_cb = ble_svc_audio_pacs_access,
+ .flags = BLE_GATT_CHR_F_READ | BLE_GATT_CHR_F_READ_ENC
+#if MYNEWT_VAL(BLE_SVC_AUDIO_PACS_SUP_AUDIO_CTX_NOTIFY)
+ | BLE_GATT_CHR_F_NOTIFY
+#endif
+ }, {
+ 0, /* No more characteristics in this service */
+ }
+ }
+ },
+ {
+ 0, /* No more services. */
+ },
+};
+
+static int
+codec_record_to_pacs_entry(const struct ble_audio_codec_record *record, void *arg)
+{
+ struct pac_read_cb_arg *cb_arg = (struct pac_read_cb_arg *)arg;
+ uint8_t *buf;
+ int rc;
+
+ rc = os_mbuf_append(cb_arg->om, &record->codec_id.format, sizeof(uint8_t));
+ if (rc) {
+ return BLE_ATT_ERR_INSUFFICIENT_RES;
+ }
+
+ buf = os_mbuf_extend(cb_arg->om, 4);
+ if (buf == NULL) {
+ return BLE_ATT_ERR_INSUFFICIENT_RES;
+ }
+ put_le16(buf + 0, record->codec_id.company_id);
+ put_le16(buf + 2, record->codec_id.vendor_specific);
+
+ rc = os_mbuf_append(cb_arg->om, &record->codec_spec_caps_len, sizeof(uint8_t));
+ if (rc) {
+ return BLE_ATT_ERR_INSUFFICIENT_RES;
+ }
+
+ rc = os_mbuf_append(cb_arg->om, record->codec_spec_caps, record->codec_spec_caps_len);
+ if (rc) {
+ return BLE_ATT_ERR_INSUFFICIENT_RES;
+ }
+
+ rc = os_mbuf_append(cb_arg->om, &record->metadata_len, sizeof(uint8_t));
+ if (rc) {
+ return BLE_ATT_ERR_INSUFFICIENT_RES;
+ }
+
+ rc = os_mbuf_append(cb_arg->om, record->metadata, record->metadata_len);
+ if (rc) {
+ return BLE_ATT_ERR_INSUFFICIENT_RES;
+ }
+
+ cb_arg->pac_count++;
+
+ return 0;
+}
+
+static int
+ble_svc_audio_pacs_sink_pac_read_access(struct ble_gatt_access_ctxt *ctxt)
+{
+ struct pac_read_cb_arg cb_arg = {
+ .om = ctxt->om,
+ .pac_count = 0
+ };
+ int rc;
+ uint8_t *pac_count;
+
+ pac_count = os_mbuf_extend(ctxt->om, sizeof(uint8_t));
+ rc = ble_audio_codec_foreach(BLE_AUDIO_CODEC_DIR_SINK_BIT,
+ codec_record_to_pacs_entry, (void *)&cb_arg);
+
+ *pac_count = cb_arg.pac_count;
+
+ return rc == 0 ? 0 : BLE_ATT_ERR_INSUFFICIENT_RES;
+}
+
+static int
+ble_svc_audio_pacs_source_pac_read_access(struct ble_gatt_access_ctxt *ctxt)
+{
+ struct pac_read_cb_arg cb_arg = {
+ .om = ctxt->om,
+ .pac_count = 0
+ };
+ int rc;
+ uint8_t *pac_count;
+
+ pac_count = os_mbuf_extend(ctxt->om, sizeof(uint8_t));
+ rc = ble_audio_codec_foreach(BLE_AUDIO_CODEC_DIR_SOURCE_BIT,
+ codec_record_to_pacs_entry, (void *)&cb_arg);
+
+ *pac_count = cb_arg.pac_count;
+
+ return rc == 0 ? 0 : BLE_ATT_ERR_INSUFFICIENT_RES;
+}
+
+static int
+ble_svc_audio_pacs_sink_audio_loc_read_access(struct ble_gatt_access_ctxt *
+ ctxt)
+{
+ uint8_t *buf;
+
+ buf = os_mbuf_extend(ctxt->om, 4);
+ if (buf == NULL) {
+ return BLE_ATT_ERR_INSUFFICIENT_RES;
+ }
+
+ put_le32(buf + 0, ble_svc_audio_pacs_sink_audio_locations);
+
+ return 0;
+}
+
+static int
+ble_svc_audio_pacs_source_audio_loc_read_access(struct ble_gatt_access_ctxt *
+ ctxt)
+{
+ uint8_t *buf;
+
+ buf = os_mbuf_extend(ctxt->om, 4);
+ if (buf == NULL) {
+ return BLE_ATT_ERR_INSUFFICIENT_RES;
+ }
+
+ put_le32(buf + 0, ble_svc_audio_pacs_source_audio_locations);
+
+ return 0;
+}
+
+static struct available_ctx *
+ble_svc_audio_pacs_find_avail_ctx(uint16_t conn_handle)
+{
+ int i;
+
+ for (i = 0; i < MYNEWT_VAL(BLE_MAX_CONNECTIONS); i++) {
+ if (ble_svc_audio_pacs_avail_contexts[i].conn_handle == conn_handle) {
+ return &ble_svc_audio_pacs_avail_contexts[i];
+ }
+ }
+ return NULL;
+}
+
+static int
+ble_svc_audio_pacs_avail_audio_ctx_read_access(uint16_t conn_handle,
+ struct ble_gatt_access_ctxt *ctxt)
+{
+ struct available_ctx *avail_ctx;
+ uint8_t *buf;
+
+ avail_ctx = ble_svc_audio_pacs_find_avail_ctx(conn_handle);
+
+ buf = os_mbuf_extend(ctxt->om, 4);
+ if (buf == NULL) {
+ return BLE_ATT_ERR_INSUFFICIENT_RES;
+ }
+
+ put_le16(buf + 0, avail_ctx->ble_svc_audio_pacs_avail_sink_contexts);
+ put_le16(buf + 2, avail_ctx->ble_svc_audio_pacs_avail_source_contexts);
+
+ return 0;
+}
+
+static int
+ble_svc_audio_pacs_sup_audio_ctx_read_access(struct ble_gatt_access_ctxt
+ *ctxt)
+{
+ uint8_t *buf;
+
+ buf = os_mbuf_extend(ctxt->om, 4);
+ if (buf == NULL) {
+ return BLE_ATT_ERR_INSUFFICIENT_RES;
+ }
+
+ put_le16(buf + 0, ble_svc_audio_pacs_sup_sink_contexts);
+ put_le16(buf + 2, ble_svc_audio_pacs_sup_source_contexts);
+
+ return 0;
+}
+
+static int
+ble_svc_audio_pacs_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_PACS_CHR_UUID16_SINK_PAC:
+ if (ctxt->op == BLE_GATT_ACCESS_OP_READ_CHR) {
+ rc = ble_svc_audio_pacs_sink_pac_read_access(ctxt);
+ } else {
+ assert(0);
+ rc = BLE_ATT_ERR_UNLIKELY;
+ }
+ return rc;
+ case BLE_SVC_AUDIO_PACS_CHR_UUID16_SINK_AUDIO_LOCATIONS:
+ if (ctxt->op == BLE_GATT_ACCESS_OP_READ_CHR) {
+ rc = ble_svc_audio_pacs_sink_audio_loc_read_access(ctxt);
+ } else {
+ rc = BLE_ATT_ERR_REQ_NOT_SUPPORTED;
+ }
+ return rc;
+ case BLE_SVC_AUDIO_PACS_CHR_UUID16_SOURCE_PAC:
+ if (ctxt->op == BLE_GATT_ACCESS_OP_READ_CHR) {
+ rc = ble_svc_audio_pacs_source_pac_read_access(ctxt);
+ } else {
+ assert(0);
+ rc = BLE_ATT_ERR_UNLIKELY;
+ }
+ return rc;
+ case BLE_SVC_AUDIO_PACS_CHR_UUID16_SOURCE_AUDIO_LOCATIONS:
+ if (ctxt->op == BLE_GATT_ACCESS_OP_READ_CHR) {
+ rc = ble_svc_audio_pacs_source_audio_loc_read_access(ctxt);
+ } else {
+ rc = BLE_ATT_ERR_REQ_NOT_SUPPORTED;
+ }
+ return rc;
+ case BLE_SVC_AUDIO_PACS_CHR_UUID16_AVAILABLE_AUDIO_CONTEXTS:
+ if (ctxt->op == BLE_GATT_ACCESS_OP_READ_CHR) {
+ rc = ble_svc_audio_pacs_avail_audio_ctx_read_access(conn_handle,
+ ctxt);
+ } else {
+ assert(0);
+ rc = BLE_ATT_ERR_UNLIKELY;
+ }
+ return rc;
+ case BLE_SVC_AUDIO_PACS_CHR_UUID16_SUPPORTED_AUDIO_CONTEXTS:
+ if (ctxt->op == BLE_GATT_ACCESS_OP_READ_CHR) {
+ rc = ble_svc_audio_pacs_sup_audio_ctx_read_access(ctxt);
+ } else {
+ assert(0);
+ rc = BLE_ATT_ERR_UNLIKELY;
+ }
+ return rc;
+ default:
+ assert(0);
+ return BLE_ATT_ERR_UNLIKELY;
+ }
+}
+
+static int
+pac_notify(uint16_t chrc_uuid)
+{
+ uint16_t chr_val_handle;
+ int rc;
+
+ if (!ble_hs_is_enabled()) {
+ /* Do not notify if host has not started yet */
+ return 0;
+ }
+
+ rc = ble_gatts_find_chr(BLE_UUID16_DECLARE(BLE_SVC_AUDIO_PACS_UUID16),
+ BLE_UUID16_DECLARE(chrc_uuid), NULL, &chr_val_handle);
+
+ if (!rc) {
+ ble_gatts_chr_updated(chr_val_handle);
+ }
+
+ return rc;
+}
+
+int
+ble_svc_audio_pacs_set(uint8_t flags, struct ble_svc_audio_pacs_set_param *param)
+{
+ int rc;
+
+ if (flags & BLE_AUDIO_CODEC_DIR_SOURCE_BIT) {
+ ble_svc_audio_pacs_source_audio_locations = param->audio_locations;
+ ble_svc_audio_pacs_sup_source_contexts = param->supported_contexts;
+ rc = pac_notify(BLE_SVC_AUDIO_PACS_CHR_UUID16_SOURCE_AUDIO_LOCATIONS);
+ if (rc != 0) {
+ return rc;
+ }
+ }
+
+ if (flags & BLE_AUDIO_CODEC_DIR_SINK_BIT) {
+ ble_svc_audio_pacs_sink_audio_locations = param->audio_locations;
+ ble_svc_audio_pacs_sup_sink_contexts = param->supported_contexts;
+ rc = pac_notify(BLE_SVC_AUDIO_PACS_CHR_UUID16_SOURCE_AUDIO_LOCATIONS);
+ if (rc != 0) {
+ return rc;
+ }
+ }
+
+ return pac_notify(BLE_SVC_AUDIO_PACS_CHR_UUID16_SUPPORTED_AUDIO_CONTEXTS);
+}
+
+int
+ble_svc_audio_pacs_avail_contexts_set(uint16_t conn_handle,
+ uint16_t sink_contexts,
+ uint16_t source_contexts)
+{
+ struct available_ctx *avail_ctx = ble_svc_audio_pacs_find_avail_ctx(conn_handle);
+
+ avail_ctx->ble_svc_audio_pacs_avail_sink_contexts = sink_contexts;
+ avail_ctx->ble_svc_audio_pacs_avail_source_contexts = source_contexts;
+
+ return pac_notify(BLE_SVC_AUDIO_PACS_CHR_UUID16_AVAILABLE_AUDIO_CONTEXTS);
+}
+
+static int
+ble_pacs_audio_event(struct ble_audio_event *event, void *arg)
+{
+ uint8_t codec_dir;
+
+ if (event->type == BLE_AUDIO_EVENT_CODEC_REGISTERED ||
+ event->type == BLE_AUDIO_EVENT_CODEC_UNREGISTERED) {
+ codec_dir = event->type == BLE_AUDIO_EVENT_CODEC_REGISTERED ?
+ event->codec_registered.record->direction :
+ event->codec_unregistered.record->direction;
+
+ if (codec_dir & BLE_AUDIO_CODEC_DIR_SOURCE_BIT) {
+ pac_notify(BLE_SVC_AUDIO_PACS_CHR_UUID16_SOURCE_PAC);
+ }
+
+ if (codec_dir & BLE_AUDIO_CODEC_DIR_SINK_BIT) {
+ pac_notify(BLE_SVC_AUDIO_PACS_CHR_UUID16_SINK_PAC);
+ }
+ }
+
+ return 0;
+}
+
+static int
+ble_pacs_gap_event(struct ble_gap_event *event, void *arg)
+{
+ struct available_ctx *avail_ctx;
+
+ switch (event->type) {
+ case BLE_GAP_EVENT_CONNECT:
+ if (event->connect.status != 0) {
+ break;
+ }
+ avail_ctx = ble_svc_audio_pacs_find_avail_ctx(BLE_HS_CONN_HANDLE_NONE);
+ avail_ctx->conn_handle = event->connect.conn_handle;
+ break;
+ case BLE_GAP_EVENT_DISCONNECT:
+ avail_ctx = ble_svc_audio_pacs_find_avail_ctx(event->disconnect.conn.conn_handle);
+ if (avail_ctx >= 0) {
+ avail_ctx->conn_handle = BLE_HS_CONN_HANDLE_NONE;
+ }
+ break;
+ default:
+ break;
+ }
+ return 0;
+}
+
+void
+ble_svc_audio_pacs_init(void)
+{
+ int rc;
+
+ /* Ensure this function only gets called by sysinit. */
+ SYSINIT_ASSERT_ACTIVE();
+
+ rc = ble_gatts_count_cfg(ble_svc_audio_pacs_defs);
+ SYSINIT_PANIC_ASSERT(rc == 0);
+
+ rc = ble_gatts_add_svcs(ble_svc_audio_pacs_defs);
+ SYSINIT_PANIC_ASSERT(rc == 0);
+
+ rc = ble_gap_event_listener_register(&ble_pacs_listener,
+ ble_pacs_gap_event, NULL);
+ SYSINIT_PANIC_ASSERT(rc == 0);
+
+ rc = ble_audio_event_listener_register(&ble_audio_listener, ble_pacs_audio_event, NULL);
+ SYSINIT_PANIC_ASSERT(rc == 0);
+ (void)rc;
+}
diff --git a/nimble/host/audio/services/pacs/syscfg.yml b/nimble/host/audio/services/pacs/syscfg.yml
new file mode 100644
index 0000000..01481c0
--- /dev/null
+++ b/nimble/host/audio/services/pacs/syscfg.yml
@@ -0,0 +1,69 @@
+# 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_PACS_SYSINIT_STAGE:
+ description: >
+ Sysinit stage for Published Audio Capabilities Service.
+ value: 303
+ BLE_SVC_AUDIO_PACS_SINK_PAC:
+ description: >
+ Enable Sink PAC characteristic.
+ If disabled, BLE_SVC_AUDIO_PACS_SOURCE_PAC must be enabled.
+ value: 1
+ BLE_SVC_AUDIO_PACS_SINK_PAC_NOTIFY:
+ description: >
+ Enable Sink PAC characteristic notifications.
+ value: 1
+ restrictions: BLE_SVC_AUDIO_PACS_SINK_PAC
+ BLE_SVC_AUDIO_PACS_SINK_AUDIO_LOCATIONS:
+ description: >
+ Enable SOURCE Sink Audio Locations characteristic.
+ value: 1
+ restrictions: BLE_SVC_AUDIO_PACS_SINK_PAC
+ BLE_SVC_AUDIO_PACS_SINK_AUDIO_LOCATIONS_NOTIFY:
+ description: >
+ Enable SOURCE Sink Audio Locations characteristic notifications.
+ value: 1
+ restrictions: BLE_SVC_AUDIO_PACS_SINK_AUDIO_LOCATIONS
+ BLE_SVC_AUDIO_PACS_SOURCE_PAC:
+ description: >
+ Enable Source PAC characteristic.
+ If disabled, BLE_SVC_AUDIO_PACS_SINK_PAC must be enabled.
+ value: 1
+ BLE_SVC_AUDIO_PACS_SOURCE_PAC_NOTIFY:
+ description: >
+ Enable Source PAC characteristic notifications.
+ value: 1
+ restrictions: BLE_SVC_AUDIO_PACS_SOURCE_PAC
+ BLE_SVC_AUDIO_PACS_SOURCE_AUDIO_LOCATIONS:
+ description: >
+ Enable Source Audio Locations characteristic.
+ value: 1
+ restrictions: BLE_SVC_AUDIO_PACS_SOURCE_PAC
+ BLE_SVC_AUDIO_PACS_SOURCE_AUDIO_LOCATIONS_NOTIFY:
+ description: >
+ Enable Source Audio Locations characteristic notifications.
+ value: 1
+ restrictions: BLE_SVC_AUDIO_PACS_SOURCE_AUDIO_LOCATIONS
+ BLE_SVC_AUDIO_PACS_SUP_AUDIO_CTX_NOTIFY:
+ description: >
+ Enable Supported Audio Contexts characteristic notifications.
+ value: 1
+
+syscfg.restrictions:
+ - 'BLE_SVC_AUDIO_PACS_SINK_PAC == 1 || BLE_SVC_AUDIO_PACS_SOURCE_PAC == 1'
diff --git a/nimble/host/audio/src/ble_audio_codec.c b/nimble/host/audio/src/ble_audio_codec.c
new file mode 100644
index 0000000..df244f9
--- /dev/null
+++ b/nimble/host/audio/src/ble_audio_codec.c
@@ -0,0 +1,138 @@
+/*
+ * 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 "syscfg/syscfg.h"
+
+#if MYNEWT_VAL(BLE_AUDIO_MAX_CODEC_RECORDS)
+#include "os/os.h"
+#include "audio/ble_audio.h"
+#include "audio/ble_audio_codec.h"
+#include "ble_audio_priv.h"
+#include "host/ble_hs.h"
+#include "sysinit/sysinit.h"
+
+static STAILQ_HEAD(, ble_audio_codec_record) ble_audio_codec_records;
+static struct os_mempool ble_audio_codec_pool;
+
+static os_membuf_t ble_audio_svc_pacs_pac_elem_mem[
+ OS_MEMPOOL_SIZE(MYNEWT_VAL(BLE_AUDIO_MAX_CODEC_RECORDS),
+ sizeof (struct ble_audio_codec_record))
+];
+
+int
+ble_audio_codec_register(const struct ble_audio_codec_register_params *params,
+ struct ble_audio_codec_record *out_record)
+{
+ struct ble_audio_event codec_event = {
+ .type = BLE_AUDIO_EVENT_CODEC_REGISTERED
+ };
+
+ struct ble_audio_codec_record *record =
+ os_memblock_get(&ble_audio_codec_pool);
+ if (!record) {
+ return BLE_HS_ENOMEM;
+ }
+
+ record->codec_id = params->codec_id;
+ record->codec_spec_caps_len = params->codec_spec_caps_len;
+ record->codec_spec_caps = params->codec_spec_caps;
+ record->metadata_len = params->metadata_len;
+ record->metadata = params->metadata;
+ record->direction = params->direction;
+
+ if (STAILQ_EMPTY(&ble_audio_codec_records)) {
+ STAILQ_INSERT_HEAD(&ble_audio_codec_records, record, next);
+ } else {
+ STAILQ_INSERT_TAIL(&ble_audio_codec_records, record, next);
+ }
+
+ out_record = record;
+
+ codec_event.codec_registered.record = record;
+ (void)ble_audio_event_listener_call(&codec_event);
+
+ return 0;
+}
+
+int
+ble_audio_codec_unregister(struct ble_audio_codec_record *codec_record)
+{
+ struct ble_audio_event codec_event = {
+ .type = BLE_AUDIO_EVENT_CODEC_UNREGISTERED
+ };
+
+ STAILQ_REMOVE(&ble_audio_codec_records, codec_record,
+ ble_audio_codec_record, next);
+
+ codec_event.codec_unregistered.record = codec_record;
+ (void)ble_audio_event_listener_call(&codec_event);
+
+ return 0;
+}
+
+int
+ble_audio_codec_foreach(uint8_t flags, ble_audio_codec_foreach_fn *cb, void *arg)
+{
+ struct ble_audio_codec_record *record;
+ int rc;
+
+ STAILQ_FOREACH(record, &ble_audio_codec_records, next) {
+ if (record->direction & flags) {
+ rc = cb(record, arg);
+ if (rc != 0) {
+ return rc;
+ }
+ }
+ }
+ return 0;
+}
+
+struct ble_audio_codec_record *
+ble_audio_codec_find(struct ble_audio_codec_id codec_id, uint8_t flag)
+{
+ struct ble_audio_codec_record *record;
+
+ STAILQ_FOREACH(record, &ble_audio_codec_records, next) {
+ if (!memcmp(&record->codec_id, &codec_id,
+ sizeof(struct ble_audio_codec_id)) &&
+ (flag ? (record->direction & flag) : 1)) {
+ return record;
+ }
+ }
+
+ return NULL;
+}
+
+int
+ble_audio_codec_init(void)
+{
+ int rc;
+
+ STAILQ_INIT(&ble_audio_codec_records);
+
+ rc = os_mempool_init(&ble_audio_codec_pool,
+ MYNEWT_VAL(BLE_AUDIO_MAX_CODEC_RECORDS),
+ sizeof(struct ble_audio_codec_record),
+ ble_audio_svc_pacs_pac_elem_mem,
+ "ble_audio_codec_pool");
+ SYSINIT_PANIC_ASSERT(rc == 0);
+
+ return 0;
+}
+#endif
diff --git a/nimble/host/audio/syscfg.yml b/nimble/host/audio/syscfg.yml
index d7af13d..5f0269e 100644
--- a/nimble/host/audio/syscfg.yml
+++ b/nimble/host/audio/syscfg.yml
@@ -17,5 +17,9 @@
#
syscfg.defs:
+ BLE_AUDIO_MAX_CODEC_RECORDS:
+ description: >
+ Maximum number of registered audio codecs.
+ value: 0
syscfg.logs:
diff --git a/nimble/host/src/ble_audio_codec_priv.h b/nimble/host/src/ble_audio_codec_priv.h
new file mode 100644
index 0000000..64e999e
--- /dev/null
+++ b/nimble/host/src/ble_audio_codec_priv.h
@@ -0,0 +1,25 @@
+/*
+ * 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_CODEC_PRIV_
+#define H_BLE_AUDIO_CODEC_PRIV_
+
+int ble_audio_codec_init(void);
+
+#endif /* H_BLE_AUDIO_CODEC_PRIV_ */
diff --git a/nimble/host/src/ble_hs.c b/nimble/host/src/ble_hs.c
index 7983cca..bac7f02 100644
--- a/nimble/host/src/ble_hs.c
+++ b/nimble/host/src/ble_hs.c
@@ -765,6 +765,11 @@
#endif
#endif
+#if MYNEWT_VAL(BLE_AUDIO_MAX_CODEC_RECORDS)
+ rc = ble_audio_codec_init();
+ SYSINIT_PANIC_ASSERT(rc == 0);
+#endif
+
ble_hs_stop_init();
ble_mqueue_init(&ble_hs_rx_q, ble_hs_event_rx_data, NULL);
diff --git a/nimble/host/src/ble_hs_priv.h b/nimble/host/src/ble_hs_priv.h
index 393b4d4..49022b1 100644
--- a/nimble/host/src/ble_hs_priv.h
+++ b/nimble/host/src/ble_hs_priv.h
@@ -26,6 +26,7 @@
#include "ble_att_priv.h"
#include "ble_eatt_priv.h"
#include "ble_gap_priv.h"
+#include "ble_audio_codec_priv.h"
#include "ble_gatt_priv.h"
#include "ble_hs_hci_priv.h"
#include "ble_hs_atomic_priv.h"
diff --git a/porting/examples/linux/include/syscfg/syscfg.h b/porting/examples/linux/include/syscfg/syscfg.h
index 6571329..e4d2d3a 100644
--- a/porting/examples/linux/include/syscfg/syscfg.h
+++ b/porting/examples/linux/include/syscfg/syscfg.h
@@ -599,6 +599,10 @@
#define MYNEWT_VAL_BLE_ATT_SVR_WRITE_NO_RSP (1)
#endif
+#ifndef MYNEWT_VAL_BLE_AUDIO_MAX_CODEC_RECORDS
+#define MYNEWT_VAL_BLE_AUDIO_MAX_CODEC_RECORDS (0)
+#endif
+
#ifndef MYNEWT_VAL_BLE_EATT_CHAN_NUM
#define MYNEWT_VAL_BLE_EATT_CHAN_NUM (0)
#endif
diff --git a/porting/examples/linux_blemesh/include/syscfg/syscfg.h b/porting/examples/linux_blemesh/include/syscfg/syscfg.h
index 8321199..3919eb1 100644
--- a/porting/examples/linux_blemesh/include/syscfg/syscfg.h
+++ b/porting/examples/linux_blemesh/include/syscfg/syscfg.h
@@ -600,6 +600,10 @@
#define MYNEWT_VAL_BLE_ATT_SVR_WRITE_NO_RSP (1)
#endif
+#ifndef MYNEWT_VAL_BLE_AUDIO_MAX_CODEC_RECORDS
+#define MYNEWT_VAL_BLE_AUDIO_MAX_CODEC_RECORDS (0)
+#endif
+
#ifndef MYNEWT_VAL_BLE_EATT_CHAN_NUM
#define MYNEWT_VAL_BLE_EATT_CHAN_NUM (0)
#endif
diff --git a/porting/examples/nuttx/include/syscfg/syscfg.h b/porting/examples/nuttx/include/syscfg/syscfg.h
index af7f799..16cdc6c 100644
--- a/porting/examples/nuttx/include/syscfg/syscfg.h
+++ b/porting/examples/nuttx/include/syscfg/syscfg.h
@@ -599,6 +599,10 @@
#define MYNEWT_VAL_BLE_ATT_SVR_WRITE_NO_RSP (1)
#endif
+#ifndef MYNEWT_VAL_BLE_AUDIO_MAX_CODEC_RECORDS
+#define MYNEWT_VAL_BLE_AUDIO_MAX_CODEC_RECORDS (0)
+#endif
+
#ifndef MYNEWT_VAL_BLE_EATT_CHAN_NUM
#define MYNEWT_VAL_BLE_EATT_CHAN_NUM (0)
#endif
diff --git a/porting/nimble/include/syscfg/syscfg.h b/porting/nimble/include/syscfg/syscfg.h
index c62bb02..4054f14 100644
--- a/porting/nimble/include/syscfg/syscfg.h
+++ b/porting/nimble/include/syscfg/syscfg.h
@@ -598,6 +598,10 @@
#define MYNEWT_VAL_BLE_ATT_SVR_WRITE_NO_RSP (1)
#endif
+#ifndef MYNEWT_VAL_BLE_AUDIO_MAX_CODEC_RECORDS
+#define MYNEWT_VAL_BLE_AUDIO_MAX_CODEC_RECORDS (0)
+#endif
+
#ifndef MYNEWT_VAL_BLE_EATT_CHAN_NUM
#define MYNEWT_VAL_BLE_EATT_CHAN_NUM (0)
#endif
diff --git a/porting/npl/riot/include/syscfg/syscfg.h b/porting/npl/riot/include/syscfg/syscfg.h
index 27047fe..1c46e12 100644
--- a/porting/npl/riot/include/syscfg/syscfg.h
+++ b/porting/npl/riot/include/syscfg/syscfg.h
@@ -1515,6 +1515,10 @@
#define MYNEWT_VAL_BLE_ATT_SVR_WRITE_NO_RSP (1)
#endif
+#ifndef MYNEWT_VAL_BLE_AUDIO_MAX_CODEC_RECORDS
+#define MYNEWT_VAL_BLE_AUDIO_MAX_CODEC_RECORDS (0)
+#endif
+
#ifndef MYNEWT_VAL_BLE_EATT_CHAN_NUM
#define MYNEWT_VAL_BLE_EATT_CHAN_NUM (0)
#endif