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