From 524ff5384bf48fcdb119cca43723c6d6889fd970 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 | 62 +++ nimble/host/include/host/ble_audio_common.h | 91 +++- .../services/pacs/ble_audio_svc_pacs.h | 39 ++ nimble/host/services/audio/pacs/pkg.yml | 32 ++ .../audio/pacs/src/ble_audio_svc_pacs.c | 432 ++++++++++++++++++ nimble/host/services/audio/pacs/syscfg.yml | 22 + nimble/host/src/ble_audio_codec.c | 125 +++++ 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 + 16 files changed, 851 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/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..12e8b7928c --- /dev/null +++ b/nimble/host/include/host/ble_audio_codec.h @@ -0,0 +1,62 @@ +/* + * 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 + +typedef int ble_audio_codec_parser(uint8_t codec_spec_caps_len, + uint8_t *codec_spec_caps, + void *cb_arg); + +struct ble_audio_codec_record { + STAILQ_ENTRY(ble_audio_codec_record) next; + struct ble_audio_codec_id codec_id; + uint8_t codec_spec_caps_len; + uint8_t *codec_spec_caps; + uint8_t metadata_len; + uint8_t *metadata; + uint8_t flags; + ble_audio_codec_parser *parser; + void *parser_arg; +}; + +/** Type definition for GATT service iteration callback function. */ +typedef int ble_audio_codec_foreach_fn(struct ble_audio_codec_record * + record, void *arg); + +int ble_audio_codec_register(struct ble_audio_codec_id codec_id, + uint8_t codec_spec_caps_len, + uint8_t *codec_spec_caps, + uint8_t metadata_len, + uint8_t *metadata, + uint8_t flags, + ble_audio_codec_parser *cb, + void *cb_arg); + +int ble_audio_codec_unregister(struct ble_audio_codec_record *codec_record); + +int ble_audio_codec_foreach(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..3492b6ff7f 100644 --- a/nimble/host/include/host/ble_audio_common.h +++ b/nimble/host/include/host/ble_audio_common.h @@ -91,12 +91,45 @@ #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_FREQS_TYPE 0x01 +#define BLE_AUDIO_CODEC_CAPS_FRAME_DURATIONS_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 0x0004 +#define BLE_AUDIO_CODEC_PREFERED_FRAME_DURATION_10_MS 0x0008 + +#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 +150,54 @@ _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_FREQS_TYPE, _sampling_freq, \ + 2, BLE_AUDIO_CODEC_CAPS_FRAME_DURATIONS_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) \ + { \ + 2, BLE_AUDIO_CODEC_CAPS_SAMPLING_FREQS_TYPE, _sampling_freq, \ + 2, BLE_AUDIO_CODEC_CAPS_FRAME_DURATIONS_TYPE, _frame_duration, \ + OPTIONAL_FIELD(5, BLE_AUDIO_CODEC_CAPS_SUP_AUDIO_CHANNEL_COUNTS_TYPE, \ + _audio_channel_counts) \ + 3, 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..59584b278f --- /dev/null +++ b/nimble/host/services/audio/pacs/include/services/pacs/ble_audio_svc_pacs.h @@ -0,0 +1,39 @@ +/* + * 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 + +int ble_svc_audio_pacs_set_sink_audio_loc(uint32_t audio_locations); +int ble_svc_audio_pacs_set_source_audio_loc(uint32_t audio_locations); +int ble_svc_audio_pacs_set_sup_contexts(uint16_t sink_contexts, + uint16_t source_contexts); +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/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..eec531c3d0 --- /dev/null +++ b/nimble/host/services/audio/pacs/src/ble_audio_svc_pacs.c @@ -0,0 +1,432 @@ +/* + * 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 "../src/ble_hs_priv.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)] = { + { + .conn_handle = BLE_HS_CONN_HANDLE_NONE, + } +}; +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 { + uint8_t flags; + 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_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_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_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_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_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_ENC, + }, { + 0, /* No more characteristics in this service */ + } + } + }, + { + 0, /* No more services. */ + }, +}; + +static int +codec_record_to_pacs_entry(struct ble_audio_codec_record *record, void *arg) +{ + struct pac_read_cb_arg *cb_arg = (struct pac_read_cb_arg *)arg; + int rc; + + if (!(record->flags & cb_arg->flags)) { + return 0; + } + + rc = os_mbuf_append(cb_arg->om, &record->codec_id, + sizeof(struct ble_audio_codec_id)); + if (rc) { + return BLE_ATT_ERR_INSUFFICIENT_RES; + } + + 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 = { + .flags = BLE_AUDIO_CODEC_FLAG_SINK, + .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(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 = { + .flags = BLE_AUDIO_CODEC_FLAG_SOURCE, + .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(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) +{ + int rc; + + rc = os_mbuf_append(ctxt->om, &ble_svc_audio_pacs_sink_audio_locations, + sizeof ble_svc_audio_pacs_sink_audio_locations); + + return rc == 0 ? 0 : BLE_ATT_ERR_INSUFFICIENT_RES; +} + +static int +ble_svc_audio_pacs_source_audio_loc_read_access(struct ble_gatt_access_ctxt * + ctxt) +{ + int rc; + + rc = os_mbuf_append(ctxt->om, &ble_svc_audio_pacs_source_audio_locations, + sizeof ble_svc_audio_pacs_source_audio_locations); + + return rc == 0 ? 0 : BLE_ATT_ERR_INSUFFICIENT_RES; +} + +static int +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 i; + } + } + return -BLE_HS_ENOENT; +} + +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; + int ctx_idx; + int rc; + + ctx_idx = ble_svc_audio_pacs_find_avail_ctx(conn_handle); + if (ctxt < 0) { + return BLE_ATT_ERR_UNLIKELY; + } + + avail_ctx = &ble_svc_audio_pacs_avail_contexts[ctx_idx]; + + rc = os_mbuf_append(ctxt->om, + &avail_ctx->ble_svc_audio_pacs_avail_sink_contexts, + sizeof(&avail_ctx->ble_svc_audio_pacs_avail_sink_contexts)); + if (rc) { + return BLE_ATT_ERR_INSUFFICIENT_RES; + } + + rc = os_mbuf_append(ctxt->om, + &avail_ctx->ble_svc_audio_pacs_avail_source_contexts, + sizeof(&avail_ctx->ble_svc_audio_pacs_avail_source_contexts)); + + return rc == 0 ? 0 : BLE_ATT_ERR_INSUFFICIENT_RES; +} + +static int +ble_svc_audio_pacs_sup_audio_ctx_read_access(struct ble_gatt_access_ctxt + *ctxt) +{ + int rc; + + rc = os_mbuf_append(ctxt->om, &ble_svc_audio_pacs_sup_sink_contexts, + sizeof ble_svc_audio_pacs_sup_sink_contexts); + if (rc) { + return BLE_ATT_ERR_INSUFFICIENT_RES; + } + + rc = os_mbuf_append(ctxt->om, &ble_svc_audio_pacs_sup_source_contexts, + sizeof ble_svc_audio_pacs_sup_source_contexts); + return rc == 0 ? 0 : BLE_ATT_ERR_INSUFFICIENT_RES; +} + +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_sink_audio_loc(uint32_t audio_locations) +{ + ble_svc_audio_pacs_sink_audio_locations = audio_locations; + + return 0; +} + +int +ble_svc_audio_pacs_set_source_audio_loc(uint32_t audio_locations) +{ + ble_svc_audio_pacs_source_audio_locations = audio_locations; + + return 0; +} + +static int +pac_notify(struct ble_hs_conn *conn, void *arg) +{ + uint16_t *chrc_uuid = (uint16_t *) arg; + 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) { + return ble_gatts_notify(conn->bhc_handle, chr_val_handle); + } + + return 0; +} + +int +ble_svc_audio_pacs_set_avail_contexts(uint16_t conn_handle, + uint16_t sink_contexts, + uint16_t source_contexts) +{ + struct available_ctx *avail_ctx; + uint16_t chr_uuid = BLE_SVC_AUDIO_PACS_CHR_UUID16_AVAILABLE_AUDIO_CONTEXTS; + int ctx_idx = ble_svc_audio_pacs_find_avail_ctx(conn_handle); + + ctx_idx = ble_svc_audio_pacs_find_avail_ctx(conn_handle); + if (ctx_idx < 0) { + return BLE_ATT_ERR_UNLIKELY; + } + + avail_ctx = &ble_svc_audio_pacs_avail_contexts[ctx_idx]; + + avail_ctx->ble_svc_audio_pacs_avail_sink_contexts = sink_contexts; + avail_ctx->ble_svc_audio_pacs_avail_source_contexts = source_contexts; + + ble_hs_conn_foreach(pac_notify, &chr_uuid); + + return 0; +} + +int +ble_svc_audio_pacs_set_sup_contexts(uint16_t sink_contexts, + uint16_t source_contexts) +{ + 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) +{ + int ctx_idx; + + switch (event->type) { + case BLE_GAP_EVENT_CONNECT: + if (event->connect.status != 0) { + break; + } + ctx_idx = ble_svc_audio_pacs_find_avail_ctx( + BLE_HS_CONN_HANDLE_NONE); + if (ctx_idx < 0) { + return BLE_HS_ENOMEM; + } + ble_svc_audio_pacs_avail_contexts[ctx_idx].conn_handle = + event->connect.conn_handle; + case BLE_GAP_EVENT_DISCONNECT: + ctx_idx = ble_svc_audio_pacs_find_avail_ctx( + event->disconnect.conn.conn_handle); + if (ctx_idx >= 0) { + ble_svc_audio_pacs_avail_contexts[ctx_idx].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); + + 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..3d77fe38e5 --- /dev/null +++ b/nimble/host/src/ble_audio_codec.c @@ -0,0 +1,125 @@ +/* + * 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(struct ble_audio_codec_id codec_id, + uint8_t codec_spec_caps_len, + uint8_t *codec_spec_caps, + uint8_t metadata_len, + uint8_t *metadata, + uint8_t flags, + ble_audio_codec_parser *cb, + void *cb_arg) +{ + struct ble_audio_codec_record *record = + os_memblock_get(&ble_audio_codec_pool); + if (!record) { + return BLE_HS_ENOMEM; + } + + record->codec_id = codec_id; + record->codec_spec_caps_len = codec_spec_caps_len; + record->codec_spec_caps = codec_spec_caps; + record->metadata_len = metadata_len; + record->metadata = metadata; + record->flags = flags; + record->parser = cb; + record->parser_arg = cb_arg; + + 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); + } + + 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(ble_audio_codec_foreach_fn *cb, void *arg) +{ + struct ble_audio_codec_record *record; + int rc; + + STAILQ_FOREACH(record, &ble_audio_codec_records, next){ + 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