From daa4f278d356803aa0ddb26686e4946558af4cc1 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Krzysztof=20Kopy=C5=9Bci=C5=84ski?= Date: Mon, 22 Jan 2024 13:35:47 +0100 Subject: [PATCH] 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. --- nimble/host/include/host/ble_audio_codec.h | 126 +++++ nimble/host/include/host/ble_audio_common.h | 106 ++++- .../services/pacs/ble_audio_svc_pacs.h | 42 ++ .../services/pacs/ble_audio_svc_pacs_lc3.h | 27 ++ nimble/host/services/audio/pacs/lc3/pkg.yml | 33 ++ .../pacs/lc3/src/ble_audio_svc_pacs_lc3.c | 144 ++++++ .../host/services/audio/pacs/lc3/syscfg.yml | 136 ++++++ nimble/host/services/audio/pacs/pkg.yml | 32 ++ .../audio/pacs/src/ble_audio_svc_pacs.c | 433 ++++++++++++++++++ nimble/host/services/audio/pacs/syscfg.yml | 22 + nimble/host/src/ble_audio_codec.c | 119 +++++ nimble/host/src/ble_audio_codec_priv.h | 25 + nimble/host/src/ble_hs.c | 5 + nimble/host/src/ble_hs_priv.h | 1 + nimble/host/syscfg.yml | 7 + .../examples/linux/include/syscfg/syscfg.h | 4 + .../linux_blemesh/include/syscfg/syscfg.h | 4 + .../examples/nuttx/include/syscfg/syscfg.h | 4 + porting/nimble/include/syscfg/syscfg.h | 4 + porting/npl/riot/include/syscfg/syscfg.h | 4 + 20 files changed, 1268 insertions(+), 10 deletions(-) create mode 100644 nimble/host/include/host/ble_audio_codec.h create mode 100644 nimble/host/services/audio/pacs/include/services/pacs/ble_audio_svc_pacs.h create mode 100644 nimble/host/services/audio/pacs/lc3/include/services/pacs/ble_audio_svc_pacs_lc3.h create mode 100644 nimble/host/services/audio/pacs/lc3/pkg.yml create mode 100644 nimble/host/services/audio/pacs/lc3/src/ble_audio_svc_pacs_lc3.c create mode 100644 nimble/host/services/audio/pacs/lc3/syscfg.yml create mode 100644 nimble/host/services/audio/pacs/pkg.yml create mode 100644 nimble/host/services/audio/pacs/src/ble_audio_svc_pacs.c create mode 100644 nimble/host/services/audio/pacs/syscfg.yml create mode 100644 nimble/host/src/ble_audio_codec.c create mode 100644 nimble/host/src/ble_audio_codec_priv.h diff --git a/nimble/host/include/host/ble_audio_codec.h b/nimble/host/include/host/ble_audio_codec.h new file mode 100644 index 0000000000..a32324226e --- /dev/null +++ b/nimble/host/include/host/ble_audio_codec.h @@ -0,0 +1,126 @@ +/* + * 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_ + +#include "stdint.h" +#include "ble_audio_common.h" + +#define BLE_AUDIO_CODEC_FLAG_SOURCE 0x01 +#define BLE_AUDIO_CODEC_FLAG_SINK 0x02 + +/** 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; + + /* Codec entry flags. It is any combination of following flags: + * - BLE_AUDIO_CODEC_FLAG_SOURCE + * - BLE_AUDIO_CODEC_FLAG_SINK + */ + uint8_t flags; +}; + +/** 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; + + /* Codec entry flags. It is any combination of following flags: + * - BLE_AUDIO_CODEC_FLAG_SOURCE + * - BLE_AUDIO_CODEC_FLAG_SINK + */ + uint8_t flags; +}; + +/** + * @brief Register codec entry + * + * @param[in] params Pointer to a `ble_audio_codec_register_params` + * structure that defines Codec Specific Capabilities + * @param[in] cb Function used to parse Codec + * Specific Capabilities + * @param[in] cb_arg Optional callback argument. + * @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] cb Callback to be called on codec entries. + * @param[in] arg Optional callback argument. + * @param[in] flags Codec entry flags. It is any + * combination of following flags: + * - BLE_AUDIO_CODEC_FLAG_SOURCE + * - BLE_AUDIO_CODEC_FLAG_SINK + * This filters entries so the callback is called + * only on these that match the flag. + * + * @return 0 on success; + * A non-zero value on failure. + */ +int ble_audio_codec_foreach(uint8_t flags, ble_audio_codec_foreach_fn *cb, void *arg); + +#endif /* H_BLE_AUDIO_CODEC_ */ diff --git a/nimble/host/include/host/ble_audio_common.h b/nimble/host/include/host/ble_audio_common.h index 43f88ea2ba..917af064b4 100644 --- a/nimble/host/include/host/ble_audio_common.h +++ b/nimble/host/include/host/ble_audio_common.h @@ -91,12 +91,59 @@ #define BLE_AUDIO_LOCATION_LEFT_SURROUND (1ULL << 23) #define BLE_AUDIO_LOCATION_RIGHT_SURROUND (1ULL << 24) -#define BLE_AUDIO_CODEC_SAMPLING_FREQ_TYPE 0x01 -#define BLE_AUDIO_CODEC_FRAME_DURATION_TYPE 0x02 -#define BLE_AUDIO_CODEC_AUDIO_CHANNEL_ALLOCATION_TYPE 0x03 -#define BLE_AUDIO_CODEC_OCTETS_PER_CODEC_FRAME_TYPE 0x04 -#define BLE_AUDIO_CODEC_FRAME_BLOCKS_PER_SDU_TYPE 0x05 +#define BLE_AUDIO_CODEC_CONF_SAMPLING_FREQ_TYPE 0x01 +#define BLE_AUDIO_CODEC_CONF_FRAME_DURATION_TYPE 0x02 +#define BLE_AUDIO_CODEC_CONF_AUDIO_CHANNEL_ALLOCATION_TYPE 0x03 +#define BLE_AUDIO_CODEC_CONF_OCTETS_PER_CODEC_FRAME_TYPE 0x04 +#define BLE_AUDIO_CODEC_CONF_FRAME_BLOCKS_PER_SDU_TYPE 0x05 +#define BLE_AUDIO_CODEC_CAPS_SAMPLING_FREQ_TYPE 0x01 +#define BLE_AUDIO_CODEC_CAPS_FRAME_DURATION_TYPE 0x02 +#define BLE_AUDIO_CODEC_CAPS_SUP_AUDIO_CHANNEL_COUNTS_TYPE 0x03 +#define BLE_AUDIO_CODEC_CAPS_OCTETS_PER_CODEC_FRAME_TYPE 0x04 +#define BLE_AUDIO_CODEC_CAPS_FRAMES_PER_SDU_TYPE 0x05 + +#define BLE_AUDIO_CONTEXT_TYPE_PROHIBITED 0x0000 +#define BLE_AUDIO_CONTEXT_TYPE_UNSPECIFIED 0x0001 +#define BLE_AUDIO_CONTEXT_TYPE_CONVERSATIONAL 0x0002 +#define BLE_AUDIO_CONTEXT_TYPE_MEDIA 0x0004 +#define BLE_AUDIO_CONTEXT_TYPE_GAME 0x0008 +#define BLE_AUDIO_CONTEXT_TYPE_INSTRUCTIONAL 0x0010 +#define BLE_AUDIO_CONTEXT_TYPE_VOICE_ASSISTANTS 0x0020 +#define BLE_AUDIO_CONTEXT_TYPE_LIVE 0x0040 +#define BLE_AUDIO_CONTEXT_TYPE_SOUND_EFFECTS 0x0080 +#define BLE_AUDIO_CONTEXT_TYPE_NOTIFICATIONS 0x0100 +#define BLE_AUDIO_CONTEXT_TYPE_RINGTONE 0x0200 +#define BLE_AUDIO_CONTEXT_TYPE_ALERTS 0x0400 +#define BLE_AUDIO_CONTEXT_TYPE_EMERGENCY_ALARM 0x0800 + +#define BLE_AUDIO_CODEC_SUPPORTED_FRAME_DURATION_7_5_MS 0x0001 +#define BLE_AUDIO_CODEC_SUPPORTED_FRAME_DURATION_10_MS 0x0002 +#define BLE_AUDIO_CODEC_PREFERED_FRAME_DURATION_7_5_MS 0x0010 +#define BLE_AUDIO_CODEC_PREFERED_FRAME_DURATION_10_MS 0x0020 + +#define BLE_AUDIO_CODEC_SUPPORTED_SAMPLING_RATE_8000_HZ (1ULL) +#define BLE_AUDIO_CODEC_SUPPORTED_SAMPLING_RATE_11025_HZ (1ULL << 1) +#define BLE_AUDIO_CODEC_SUPPORTED_SAMPLING_RATE_16000_HZ (1ULL << 2) +#define BLE_AUDIO_CODEC_SUPPORTED_SAMPLING_RATE_22050_HZ (1ULL << 3) +#define BLE_AUDIO_CODEC_SUPPORTED_SAMPLING_RATE_24000_HZ (1ULL << 4) +#define BLE_AUDIO_CODEC_SUPPORTED_SAMPLING_RATE_32000_HZ (1ULL << 5) +#define BLE_AUDIO_CODEC_SUPPORTED_SAMPLING_RATE_44100_HZ (1ULL << 6) +#define BLE_AUDIO_CODEC_SUPPORTED_SAMPLING_RATE_48000_HZ (1ULL << 7) +#define BLE_AUDIO_CODEC_SUPPORTED_SAMPLING_RATE_88200_HZ (1ULL << 8) +#define BLE_AUDIO_CODEC_SUPPORTED_SAMPLING_RATE_96000_HZ (1ULL << 9) +#define BLE_AUDIO_CODEC_SUPPORTED_SAMPLING_RATE_176400_HZ (1ULL << 10) +#define BLE_AUDIO_CODEC_SUPPORTED_SAMPLING_RATE_192000_HZ (1ULL << 11) +#define BLE_AUDIO_CODEC_SUPPORTED_SAMPLING_RATE_384000_HZ (1ULL << 12) + +#define BLE_AUDIO_CODEC_SUPPORTED_CHANNEL_COUNT_1 0x0001 +#define BLE_AUDIO_CODEC_SUPPORTED_CHANNEL_COUNT_2 0x0002 +#define BLE_AUDIO_CODEC_SUPPORTED_CHANNEL_COUNT_3 0x0004 +#define BLE_AUDIO_CODEC_SUPPORTED_CHANNEL_COUNT_4 0x0008 +#define BLE_AUDIO_CODEC_SUPPORTED_CHANNEL_COUNT_5 0x0010 +#define BLE_AUDIO_CODEC_SUPPORTED_CHANNEL_COUNT_6 0x0020 +#define BLE_AUDIO_CODEC_SUPPORTED_CHANNEL_COUNT_7 0x0040 +#define BLE_AUDIO_CODEC_SUPPORTED_CHANNEL_COUNT_8 0x0080 /** * @brief Helper macro used to build LTV array of Codec_Specific_Configuration. * @@ -117,16 +164,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_CAPS_SAMPLING_FREQ_TYPE, _sampling_freq, \ + 2, BLE_AUDIO_CODEC_CAPS_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 - + * single 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) \ + } + struct ble_audio_codec_id { /** Coding Fromat */ uint8_t format; diff --git a/nimble/host/services/audio/pacs/include/services/pacs/ble_audio_svc_pacs.h b/nimble/host/services/audio/pacs/include/services/pacs/ble_audio_svc_pacs.h new file mode 100644 index 0000000000..d8612badf7 --- /dev/null +++ b/nimble/host/services/audio/pacs/include/services/pacs/ble_audio_svc_pacs.h @@ -0,0 +1,42 @@ +/* + * 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_ + +#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 { + uint32_t audio_locations; + uint16_t supported_contexts; +}; + +int ble_svc_audio_pacs_set(uint8_t flags, struct ble_svc_audio_pacs_set_param *param); + +int ble_svc_audio_pacs_set_avail_contexts(uint16_t conn_handle, + uint16_t sink_contexts, + uint16_t source_contexts); + +#endif /* H_BLE_AUDIO_SVC_PACS_ */ diff --git a/nimble/host/services/audio/pacs/lc3/include/services/pacs/ble_audio_svc_pacs_lc3.h b/nimble/host/services/audio/pacs/lc3/include/services/pacs/ble_audio_svc_pacs_lc3.h new file mode 100644 index 0000000000..08b4c4e1e6 --- /dev/null +++ b/nimble/host/services/audio/pacs/lc3/include/services/pacs/ble_audio_svc_pacs_lc3.h @@ -0,0 +1,27 @@ +/* + * 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_ + +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/services/audio/pacs/lc3/pkg.yml b/nimble/host/services/audio/pacs/lc3/pkg.yml new file mode 100644 index 0000000000..66f470b949 --- /dev/null +++ b/nimble/host/services/audio/pacs/lc3/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/services/audio/pacs/lc3 +pkg.description: LC3 codec entry for Published Audio Capabilities Service +pkg.author: "Apache Mynewt " +pkg.homepage: "http://mynewt.apache.org/" +pkg.keywords: + - ble + - bluetooth + - pacs + - nimble + +pkg.deps: + - nimble/host/services/audio/pacs + - nimble/host + +pkg.init: + ble_svc_audio_pacs_lc3_init: 'MYNEWT_VAL(BLE_SVC_AUDIO_PACS_LC3_SYSINIT_STAGE)' diff --git a/nimble/host/services/audio/pacs/lc3/src/ble_audio_svc_pacs_lc3.c b/nimble/host/services/audio/pacs/lc3/src/ble_audio_svc_pacs_lc3.c new file mode 100644 index 0000000000..784cfcbdf5 --- /dev/null +++ b/nimble/host/services/audio/pacs/lc3/src/ble_audio_svc_pacs_lc3.c @@ -0,0 +1,144 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ + +#include "host/ble_audio_codec.h" +#include "host/ble_audio_common.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 + .flags = BLE_AUDIO_CODEC_FLAG_SOURCE +}; + +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 + .flags = BLE_AUDIO_CODEC_FLAG_SINK +}; + +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; +} + +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_FLAG_SOURCE, &src_params); + SYSINIT_PANIC_ASSERT(rc == 0); + + rc = ble_svc_audio_pacs_set(BLE_AUDIO_CODEC_FLAG_SINK, &snk_params); + SYSINIT_PANIC_ASSERT(rc == 0); + + (void)rc; +} diff --git a/nimble/host/services/audio/pacs/lc3/syscfg.yml b/nimble/host/services/audio/pacs/lc3/syscfg.yml new file mode 100644 index 0000000000..edb3e44858 --- /dev/null +++ b/nimble/host/services/audio/pacs/lc3/syscfg.yml @@ -0,0 +1,136 @@ +# 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 + + BLE_SVC_AUDIO_PACS_LC3_SYSINIT_STAGE: + description: > + Sysinit stage for Published Audio Capabilities Service. + value: 304 \ No newline at end of file diff --git a/nimble/host/services/audio/pacs/pkg.yml b/nimble/host/services/audio/pacs/pkg.yml new file mode 100644 index 0000000000..d2aaab0a22 --- /dev/null +++ b/nimble/host/services/audio/pacs/pkg.yml @@ -0,0 +1,32 @@ +# 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/services/audio/pacs +pkg.description: Published Audio Capabilities Service +pkg.author: "Apache Mynewt " +pkg.homepage: "http://mynewt.apache.org/" +pkg.keywords: + - ble + - bluetooth + - pacs + - nimble + +pkg.deps: + - nimble/host + +pkg.init: + ble_svc_audio_pacs_init: 'MYNEWT_VAL(BLE_SVC_AUDIO_PACS_SYSINIT_STAGE)' diff --git a/nimble/host/services/audio/pacs/src/ble_audio_svc_pacs.c b/nimble/host/services/audio/pacs/src/ble_audio_svc_pacs.c new file mode 100644 index 0000000000..e2958f78ed --- /dev/null +++ b/nimble/host/services/audio/pacs/src/ble_audio_svc_pacs.c @@ -0,0 +1,433 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ + +#include "host/ble_hs.h" +#include "host/ble_gatt.h" +#include "host/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; + +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[]) { { + .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, + } , { + .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, + } , { + .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 + } , { + .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, + }, { + .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, + }, { + 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; + uint8_t flag = BLE_AUDIO_CODEC_FLAG_SINK; + + pac_count = os_mbuf_extend(ctxt->om, sizeof(uint8_t)); + rc = ble_audio_codec_foreach(flag, 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; + uint8_t flag = BLE_AUDIO_CODEC_FLAG_SOURCE; + + pac_count = os_mbuf_extend(ctxt->om, sizeof(uint8_t)); + rc = ble_audio_codec_foreach(flag, 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); + } + 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); + } + 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); + } + return rc; + default: + assert(0); + } +} + +int +ble_svc_audio_pacs_set(uint8_t flags, struct ble_svc_audio_pacs_set_param *param) +{ + switch (flags) { + case BLE_AUDIO_CODEC_FLAG_SOURCE: + ble_svc_audio_pacs_source_audio_locations = param->audio_locations; + ble_svc_audio_pacs_sup_source_contexts = param->supported_contexts; + return 0; + case BLE_AUDIO_CODEC_FLAG_SINK: + ble_svc_audio_pacs_sink_audio_locations = param->audio_locations; + ble_svc_audio_pacs_sup_sink_contexts = param->supported_contexts; + return 0; + default: + return BLE_HS_EINVAL; + } +} + +static int +pac_notify(uint16_t chrc_uuid) +{ + uint16_t chr_val_handle; + int rc; + + 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_avail_contexts(uint16_t conn_handle, + uint16_t sink_contexts, + uint16_t source_contexts) +{ + uint16_t chr_uuid = BLE_SVC_AUDIO_PACS_CHR_UUID16_AVAILABLE_AUDIO_CONTEXTS; + struct available_ctx *avail_ctx = ble_svc_audio_pacs_find_avail_ctx(conn_handle); + + if (avail_ctx->ble_svc_audio_pacs_avail_sink_contexts || + avail_ctx->ble_svc_audio_pacs_avail_source_contexts) { + return BLE_HS_EALREADY; + } + + 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(chr_uuid); +} + +int +ble_svc_audio_pacs_set_sup_contexts(uint16_t sink_contexts, + uint16_t source_contexts) +{ + if (ble_svc_audio_pacs_sup_sink_contexts || + ble_svc_audio_pacs_sup_source_contexts) { + return BLE_HS_EALREADY; + } + + ble_svc_audio_pacs_sup_sink_contexts = sink_contexts; + ble_svc_audio_pacs_sup_source_contexts = source_contexts; + + 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; +} + +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_set_avail_contexts(conn_handle, sink_contexts, source_contexts); +} + +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); + + ble_gap_event_listener_register(&ble_pacs_listener, + ble_pacs_gap_event, NULL); + (void)rc; +} diff --git a/nimble/host/services/audio/pacs/syscfg.yml b/nimble/host/services/audio/pacs/syscfg.yml new file mode 100644 index 0000000000..4126e7d007 --- /dev/null +++ b/nimble/host/services/audio/pacs/syscfg.yml @@ -0,0 +1,22 @@ +# 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 \ No newline at end of file diff --git a/nimble/host/src/ble_audio_codec.c b/nimble/host/src/ble_audio_codec.c new file mode 100644 index 0000000000..3b080c298b --- /dev/null +++ b/nimble/host/src/ble_audio_codec.c @@ -0,0 +1,119 @@ +/* + * 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 "os/os.h" +#include "host/ble_audio_codec.h" +#include "host/ble_hs.h" +#include "ble_hs_priv.h" +#include "syscfg/syscfg.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_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->flags = params->flags; + + 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; + return 0; +} + +int +ble_audio_codec_unregister(struct ble_audio_codec_record *codec_record) +{ + STAILQ_REMOVE(&ble_audio_codec_records, codec_record, + ble_audio_codec_record, next); + + 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->flags & 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->flags & 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; +} 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 0000000000..64e999efeb --- /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 41bf5e2c3a..8f2f0816b0 100644 --- a/nimble/host/src/ble_hs.c +++ b/nimble/host/src/ble_hs.c @@ -762,6 +762,11 @@ ble_hs_init(void) #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 393b4d4323..49022b17b7 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/nimble/host/syscfg.yml b/nimble/host/syscfg.yml index 558e31e8d2..d262a760d9 100644 --- a/nimble/host/syscfg.yml +++ b/nimble/host/syscfg.yml @@ -500,6 +500,13 @@ syscfg.defs: restrictions: - 'BLE_ISO_BROADCASTER if 0' + BLE_AUDIO_MAX_CODEC_RECORDS: + description: > + Maximum number of registered audio codecs. + value: 0 + restrictions: + - 'BLE_ISO' + ### Log settings. BLE_HS_LOG_MOD: diff --git a/porting/examples/linux/include/syscfg/syscfg.h b/porting/examples/linux/include/syscfg/syscfg.h index 85697d5f9e..c097f988b2 100644 --- a/porting/examples/linux/include/syscfg/syscfg.h +++ b/porting/examples/linux/include/syscfg/syscfg.h @@ -591,6 +591,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 (1) +#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 750334d954..8ba4a7b103 100644 --- a/porting/examples/linux_blemesh/include/syscfg/syscfg.h +++ b/porting/examples/linux_blemesh/include/syscfg/syscfg.h @@ -592,6 +592,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 (1) +#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 0f599f7be4..ace23dad9a 100644 --- a/porting/examples/nuttx/include/syscfg/syscfg.h +++ b/porting/examples/nuttx/include/syscfg/syscfg.h @@ -591,6 +591,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 (1) +#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 ed59c0eada..f023673cf5 100644 --- a/porting/nimble/include/syscfg/syscfg.h +++ b/porting/nimble/include/syscfg/syscfg.h @@ -590,6 +590,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 (1) +#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 4b8cde70e6..9ac1f0e819 100644 --- a/porting/npl/riot/include/syscfg/syscfg.h +++ b/porting/npl/riot/include/syscfg/syscfg.h @@ -1506,6 +1506,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 (1) +#endif + #ifndef MYNEWT_VAL_BLE_EATT_CHAN_NUM #define MYNEWT_VAL_BLE_EATT_CHAN_NUM (0) #endif