diff --git a/include/zephyr/bluetooth/classic/a2dp-codec.h b/include/zephyr/bluetooth/classic/a2dp-codec.h deleted file mode 100644 index 3da36420dcd..00000000000 --- a/include/zephyr/bluetooth/classic/a2dp-codec.h +++ /dev/null @@ -1,115 +0,0 @@ -/** @file - * @brief Advance Audio Distribution Profile - SBC Codec header. - */ -/* - * SPDX-License-Identifier: Apache-2.0 - * Copyright (c) 2015-2016 Intel Corporation - * - * Licensed 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 ZEPHYR_INCLUDE_BLUETOOTH_A2DP_CODEC_H_ -#define ZEPHYR_INCLUDE_BLUETOOTH_A2DP_CODEC_H_ - -#include - -#ifdef __cplusplus -extern "C" { -#endif - -/** - * @name Sampling Frequency - * @{ - */ -#define A2DP_SBC_SAMP_FREQ_16000 BIT(7) /**< 16 kHz */ -#define A2DP_SBC_SAMP_FREQ_32000 BIT(6) /**< 32 kHz */ -#define A2DP_SBC_SAMP_FREQ_44100 BIT(5) /**< 44.1 kHz */ -#define A2DP_SBC_SAMP_FREQ_48000 BIT(4) /**< 48 kHz */ -/** @} */ - -/** - * @name Channel Mode - * @{ - */ -#define A2DP_SBC_CH_MODE_MONO BIT(3) /**< Mono */ -#define A2DP_SBC_CH_MODE_DUAL BIT(2) /**< Dual Channel */ -#define A2DP_SBC_CH_MODE_STREO BIT(1) /**< Stereo */ -#define A2DP_SBC_CH_MODE_JOINT BIT(0) /**< Joint Stereo */ -/** @} */ - -/** - * @name Block Length - * @{ - */ -#define A2DP_SBC_BLK_LEN_4 BIT(7) /**< 4 blocks */ -#define A2DP_SBC_BLK_LEN_8 BIT(6) /**< 8 blocks */ -#define A2DP_SBC_BLK_LEN_12 BIT(5) /**< 12 blocks */ -#define A2DP_SBC_BLK_LEN_16 BIT(4) /**< 16 blocks */ -/** @} */ - -/** - * @name Subbands - * @{ - */ -#define A2DP_SBC_SUBBAND_4 BIT(3) /**< 4 subbands */ -#define A2DP_SBC_SUBBAND_8 BIT(2) /**< 8 subbands */ -/** @} */ - -/** - * @name Bit pool Allocation Method - * @{ - */ -#define A2DP_SBC_ALLOC_MTHD_SNR BIT(1) /**< Allocate based on loudness of the subband signal */ -#define A2DP_SBC_ALLOC_MTHD_LOUDNESS BIT(0) /**< Allocate based on the signal-to-noise ratio */ -/** @} */ - -/** - * Gets the sampling rate from a codec preset - * @param preset Codec preset - */ -#define BT_A2DP_SBC_SAMP_FREQ(preset) ((preset->config[0] >> 4) & 0x0f) -/** - * Gets the channel mode from a codec preset - * @param preset Codec preset - */ -#define BT_A2DP_SBC_CHAN_MODE(preset) ((preset->config[0]) & 0x0f) -/** - * Gets the block length from a codec preset - * @param preset Codec preset - */ -#define BT_A2DP_SBC_BLK_LEN(preset) ((preset->config[1] >> 4) & 0x0f) -/** - * Gets the number subbands from a codec preset - * @param preset Codec preset - */ -#define BT_A2DP_SBC_SUB_BAND(preset) ((preset->config[1] >> 2) & 0x03) -/** - * Gets the bitpool allocation method from a codec preset - * @param preset Codec preset - */ -#define BT_A2DP_SBC_ALLOC_MTHD(preset) ((preset->config[1]) & 0x03) - -/** @brief SBC Codec */ -struct bt_a2dp_codec_sbc_params { - /** First two octets of configuration */ - uint8_t config[2]; - /** Minimum Bitpool Value */ - uint8_t min_bitpool; - /** Maximum Bitpool Value */ - uint8_t max_bitpool; -} __packed; - -#ifdef __cplusplus -} -#endif - -#endif /* ZEPHYR_INCLUDE_BLUETOOTH_A2DP_CODEC_H_ */ diff --git a/include/zephyr/bluetooth/classic/a2dp.h b/include/zephyr/bluetooth/classic/a2dp.h index a0650122802..540899c2197 100644 --- a/include/zephyr/bluetooth/classic/a2dp.h +++ b/include/zephyr/bluetooth/classic/a2dp.h @@ -4,6 +4,7 @@ /* * Copyright (c) 2015-2016 Intel Corporation + * Copyright 2024 NXP * * SPDX-License-Identifier: Apache-2.0 */ @@ -12,19 +13,262 @@ #include +#include +#include #include #ifdef __cplusplus extern "C" { #endif -/** @brief Stream Structure */ -struct bt_a2dp_stream { - /* TODO */ +#define BT_A2DP_STREAM_BUF_RESERVE (12u + BT_L2CAP_BUF_SIZE(0)) + +/** SBC IE length */ +#define BT_A2DP_SBC_IE_LENGTH (4u) +/** MPEG1,2 IE length */ +#define BT_A2DP_MPEG_1_2_IE_LENGTH (4u) +/** MPEG2,4 IE length */ +#define BT_A2DP_MPEG_2_4_IE_LENGTH (6u) +/** The max IE (Codec Info Element) length */ +#define A2DP_MAX_IE_LENGTH (8U) + +/** @brief define the audio endpoint + * @param _role BT_AVDTP_SOURCE or BT_AVDTP_SINK. + * @param _codec value of enum bt_a2dp_codec_id. + * @param _capability the codec capability. + */ +#define BT_A2DP_EP_INIT(_role, _codec, _capability)\ +{\ + .codec_type = _codec,\ + .sep = {.sep_info = {.media_type = BT_AVDTP_AUDIO, .tsep = _role}},\ + .codec_cap = _capability,\ + .stream = NULL,\ +} + +/** @brief define the audio sink endpoint + * @param _codec value of enum bt_a2dp_codec_id. + * @param _capability the codec capability. + */ +#define BT_A2DP_SINK_EP_INIT(_codec, _capability)\ +BT_A2DP_EP_INIT(BT_AVDTP_SINK, _codec, _capability) + +/** @brief define the audio source endpoint + * @param _codec value of enum bt_a2dp_codec_id. + * @param _capability the codec capability. + */ +#define BT_A2DP_SOURCE_EP_INIT(_codec, _capability)\ +BT_A2DP_EP_INIT(BT_AVDTP_SOURCE, _codec, _capability) + +/** @brief define the SBC sink endpoint that can be used as + * bt_a2dp_register_endpoint's parameter. + * + * SBC is mandatory as a2dp specification, BT_A2DP_SBC_SINK_EP_DEFAULT + * is more convenient for user to register SBC endpoint. + * + * @param _freq sbc codec frequency. + * for example: A2DP_SBC_SAMP_FREQ_44100 | A2DP_SBC_SAMP_FREQ_48000 + * @param _ch_mode sbc codec channel mode. + * for example: A2DP_SBC_CH_MODE_MONO | A2DP_SBC_CH_MODE_STREO + * @param _blk_len sbc codec block length. + * for example: A2DP_SBC_BLK_LEN_16 + * @param _subband sbc codec subband. + * for example: A2DP_SBC_SUBBAND_8 + * @param _alloc_mthd sbc codec allocate method. + * for example: A2DP_SBC_ALLOC_MTHD_LOUDNESS + * @param _min_bitpool sbc codec min bit pool. for example: 18 + * @param _max_bitpool sbc codec max bit pool. for example: 35 + * @ + */ +#define BT_A2DP_SBC_SINK_EP(_name, _freq, _ch_mode, _blk_len, _subband,\ +_alloc_mthd, _min_bitpool, _max_bitpool)\ +static struct bt_a2dp_codec_ie bt_a2dp_ep_cap_ie##_name =\ +{.len = BT_A2DP_SBC_IE_LENGTH, .codec_ie = {_freq | _ch_mode,\ +_blk_len | _subband | _alloc_mthd, _min_bitpool, _max_bitpool}};\ +static struct bt_a2dp_ep _name = BT_A2DP_SINK_EP_INIT(BT_A2DP_SBC,\ +(&bt_a2dp_ep_cap_ie##_name)) + +/** @brief define the SBC source endpoint that can be used as bt_a2dp_register_endpoint's + * parameter. + * + * SBC is mandatory as a2dp specification, BT_A2DP_SBC_SOURCE_EP_DEFAULT + * is more convenient for user to register SBC endpoint. + * + * @param _name the endpoint variable name. + * @param _freq sbc codec frequency. + * for example: A2DP_SBC_SAMP_FREQ_44100 | A2DP_SBC_SAMP_FREQ_48000 + * @param _ch_mode sbc codec channel mode. + * for example: A2DP_SBC_CH_MODE_MONO | A2DP_SBC_CH_MODE_STREO + * @param _blk_len sbc codec block length. + * for example: A2DP_SBC_BLK_LEN_16 + * @param _subband sbc codec subband. + * for example: A2DP_SBC_SUBBAND_8 + * @param _alloc_mthd sbc codec allocate method. + * for example: A2DP_SBC_ALLOC_MTHD_LOUDNESS + * @param _min_bitpool sbc codec min bit pool. for example: 18 + * @param _max_bitpool sbc codec max bit pool. for example: 35 + */ +#define BT_A2DP_SBC_SOURCE_EP(_name, _freq, _ch_mode, _blk_len, _subband,\ +_alloc_mthd, _min_bitpool, _max_bitpool)\ +static struct bt_a2dp_codec_ie bt_a2dp_ep_cap_ie##_name =\ +{.len = BT_A2DP_SBC_IE_LENGTH, .codec_ie = {_freq | _ch_mode,\ +_blk_len | _subband | _alloc_mthd, _min_bitpool, _max_bitpool}};\ +static struct bt_a2dp_ep _name = BT_A2DP_SOURCE_EP_INIT(BT_A2DP_SBC,\ +&bt_a2dp_ep_cap_ie##_name) + +/** @brief define the default SBC sink endpoint that can be used as + * bt_a2dp_register_endpoint's parameter. + * + * SBC is mandatory as a2dp specification, BT_A2DP_SBC_SINK_EP_DEFAULT + * is more convenient for user to register SBC endpoint. + * + * @param _name the endpoint variable name. + */ +#define BT_A2DP_SBC_SINK_EP_DEFAULT(_name)\ +static struct bt_a2dp_codec_ie bt_a2dp_ep_cap_ie##_name =\ +{.len = BT_A2DP_SBC_IE_LENGTH, .codec_ie = {A2DP_SBC_SAMP_FREQ_44100 |\ +A2DP_SBC_SAMP_FREQ_48000 | A2DP_SBC_CH_MODE_MONO | A2DP_SBC_CH_MODE_STREO |\ +A2DP_SBC_CH_MODE_JOINT, A2DP_SBC_BLK_LEN_16 |\ +A2DP_SBC_SUBBAND_8 | A2DP_SBC_ALLOC_MTHD_LOUDNESS, 18U, 35U}};\ +static struct bt_a2dp_ep _name = BT_A2DP_SINK_EP_INIT(BT_A2DP_SBC,\ +&bt_a2dp_ep_cap_ie##_name) + +/** @brief define the default SBC source endpoint that can be used as bt_a2dp_register_endpoint's + * parameter. + * + * SBC is mandatory as a2dp specification, BT_A2DP_SBC_SOURCE_EP_DEFAULT + * is more convenient for user to register SBC endpoint. + * + * @param _name the endpoint variable name. + */ +#define BT_A2DP_SBC_SOURCE_EP_DEFAULT(_name)\ +static struct bt_a2dp_codec_ie bt_a2dp_ep_cap_ie##_name =\ +{.len = BT_A2DP_SBC_IE_LENGTH, .codec_ie = {A2DP_SBC_SAMP_FREQ_44100 | \ +A2DP_SBC_SAMP_FREQ_48000 | A2DP_SBC_CH_MODE_MONO | A2DP_SBC_CH_MODE_STREO | \ +A2DP_SBC_CH_MODE_JOINT, A2DP_SBC_BLK_LEN_16 | A2DP_SBC_SUBBAND_8 | A2DP_SBC_ALLOC_MTHD_LOUDNESS,\ +18U, 35U},};\ +static struct bt_a2dp_ep _name = BT_A2DP_SOURCE_EP_INIT(BT_A2DP_SBC,\ +&bt_a2dp_ep_cap_ie##_name) + +/** @brief define the SBC default configuration. + * + * @param _freq_cfg sbc codec frequency. + * for example: A2DP_SBC_SAMP_FREQ_44100 + * @param _ch_mode_cfg sbc codec channel mode. + * for example: A2DP_SBC_CH_MODE_JOINT + * @param _blk_len_cfg sbc codec block length. + * for example: A2DP_SBC_BLK_LEN_16 + * @param _subband_cfg sbc codec subband. + * for example: A2DP_SBC_SUBBAND_8 + * @param _alloc_mthd_cfg sbc codec allocate method. + * for example: A2DP_SBC_ALLOC_MTHD_LOUDNESS + * @param _min_bitpool_cfg sbc codec min bit pool. for example: 18 + * @param _max_bitpool_cfg sbc codec max bit pool. for example: 35 + */ +#define BT_A2DP_SBC_EP_CFG(_name, _freq_cfg, _ch_mode_cfg, _blk_len_cfg, _subband_cfg,\ +_alloc_mthd_cfg, _min_bitpool_cfg, _max_bitpool_cfg)\ +static struct bt_a2dp_codec_ie bt_a2dp_codec_ie##_name = {\ +.len = BT_A2DP_SBC_IE_LENGTH, .codec_ie = {_freq_cfg | _ch_mode_cfg,\ +_blk_len_cfg | _subband_cfg | _alloc_mthd_cfg, _min_bitpool_cfg, _max_bitpool_cfg},};\ +struct bt_a2dp_codec_cfg _name = {.codec_config = &bt_a2dp_codec_ie##_name,} + +/** @brief define the SBC default configuration. + * + * @param _freq_cfg the frequency to configure the remote same codec type endpoint. + */ +#define BT_A2DP_SBC_EP_CFG_DEFAULT(_name, _freq_cfg)\ +static struct bt_a2dp_codec_ie bt_a2dp_codec_ie##_name = {\ +.len = BT_A2DP_SBC_IE_LENGTH, .codec_ie = {_freq_cfg | A2DP_SBC_CH_MODE_JOINT,\ +A2DP_SBC_BLK_LEN_16 | A2DP_SBC_SUBBAND_8 | A2DP_SBC_ALLOC_MTHD_LOUDNESS, 18U, 35U},};\ +struct bt_a2dp_codec_cfg _name = {.codec_config = &bt_a2dp_codec_ie##_name,} + +/** + * @brief A2DP error code + */ +enum bt_a2dp_err_code { + /** Media Codec Type is not valid */ + BT_A2DP_INVALID_CODEC_TYPE = 0xC1, + /** Media Codec Type is not supported */ + BT_A2DP_NOT_SUPPORTED_CODEC_TYPE = 0xC2, + /** Sampling Frequency is not valid or multiple values have been selected */ + BT_A2DP_INVALID_SAMPLING_FREQUENCY = 0xC3, + /** Sampling Frequency is not supported */ + BT_A2DP_NOT_SUPPORTED_SAMPLING_FREQUENCY = 0xC4, + /** Channel Mode is not valid or multiple values have been selected */ + BT_A2DP_INVALID_CHANNEL_MODE = 0xC5, + /** Channel Mode is not supported */ + BT_A2DP_NOT_SUPPORTED_CHANNEL_MODE = 0xC6, + /** None or multiple values have been selected for Number of Subbands */ + BT_A2DP_INVALID_SUBBANDS = 0xC7, + /** Number of Subbands is not supported */ + BT_A2DP_NOT_SUPPORTED_SUBBANDS = 0xC8, + /** None or multiple values have been selected for Allocation Method */ + BT_A2DP_INVALID_ALLOCATION_METHOD = 0xC9, + /** Allocation Method is not supported */ + BT_A2DP_NOT_SUPPORTED_ALLOCATION_METHOD = 0xCA, + /** Minimum Bitpool Value is not valid */ + BT_A2DP_INVALID_MINIMUM_BITPOOL_VALUE = 0xCB, + /** Minimum Bitpool Value is not supported */ + BT_A2DP_NOT_SUPPORTED_MINIMUM_BITPOOL_VALUE = 0xCC, + /** Maximum Bitpool Value is not valid */ + BT_A2DP_INVALID_MAXIMUM_BITPOOL_VALUE = 0xCD, + /** Maximum Bitpool Value is not supported */ + BT_A2DP_NOT_SUPPORTED_MAXIMUM_BITPOOL_VALUE = 0xCE, + /** None or multiple values have been selected for Layer */ + BT_A2DP_INVALID_LAYER = 0xCF, + /** Layer is not supported */ + BT_A2DP_NOT_SUPPORTED_LAYER = 0xD0, + /** CRC is not supported */ + BT_A2DP_NOT_SUPPORTED_CRC = 0xD1, + /** MPF-2 is not supported */ + BT_A2DP_NOT_SUPPORTED_MPF = 0xD2, + /** VBR is not supported */ + BT_A2DP_NOT_SUPPORTED_VBR = 0xD3, + /** None or multiple values have been selected for Bit Rate */ + BT_A2DP_INVALID_BIT_RATE = 0xD4, + /** Bit Rate is not supported */ + BT_A2DP_NOT_SUPPORTED_BIT_RATE = 0xD5, + /** Either 1) Object type is not valid or + * 2) None or multiple values have been selected for Object Type + */ + BT_A2DP_INVALID_OBJECT_TYPE = 0xD6, + /** Object Type is not supported */ + BT_A2DP_NOT_SUPPORTED_OBJECT_TYPE = 0xD7, + /** Either 1) Channels is not valid or + * 2) None or multiple values have been selected for Channels + */ + BT_A2DP_INVALID_CHANNELS = 0xD8, + /** Channels is not supported */ + BT_A2DP_NOT_SUPPORTED_CHANNELS = 0xD9, + /** Version is not valid */ + BT_A2DP_INVALID_VERSION = 0xDA, + /** Version is not supported */ + BT_A2DP_NOT_SUPPORTED_VERSION = 0xDB, + /** Maximum SUL is not acceptable for the Decoder in the SNK */ + BT_A2DP_NOT_SUPPORTED_MAXIMUM_SUL = 0xDC, + /** None or multiple values have been selected for Block Length */ + BT_A2DP_INVALID_BLOCK_LENGTH = 0xDD, + /** The requested CP Type is not supported */ + BT_A2DP_INVALID_CP_TYPE = 0xE0, + /** The format of Content Protection Service Capability/Content + * Protection Scheme Dependent Data is not correct + */ + BT_A2DP_INVALID_CP_FORMAT = 0xE1, + /** The codec parameter is invalid. + * Used if a more specific error code does not exist for the codec in use + */ + BT_A2DP_INVALID_CODEC_PARAMETER = 0xE2, + /** The codec parameter is not supported. + * Used if a more specific error code does not exist for the codec in use + */ + BT_A2DP_NOT_SUPPORTED_CODEC_PARAMETER = 0xE3, + /** Combination of Object Type and DRC is invalid */ + BT_A2DP_INVALID_DRC = 0xE4, + /** DRC is not supported */ + BT_A2DP_NOT_SUPPORTED_DRC = 0xE5, }; -/** @brief Codec ID */ -enum bt_a2dp_codec_id { +/** @brief Codec Type */ +enum bt_a2dp_codec_type { /** Codec SBC */ BT_A2DP_SBC = 0x00, /** Codec MPEG-1 */ @@ -37,52 +281,266 @@ enum bt_a2dp_codec_id { BT_A2DP_VENDOR = 0xff }; -/** @brief Preset for the endpoint */ -struct bt_a2dp_preset { - /** Length of preset */ +/** @brief A2DP structure */ +struct bt_a2dp; + +/* Internal to pass build */ +struct bt_a2dp_stream; + +/** @brief codec information elements for the endpoint */ +struct bt_a2dp_codec_ie { + /** Length of codec_cap */ uint8_t len; - /** Preset */ - uint8_t preset[0]; + /** codec information element */ + uint8_t codec_ie[A2DP_MAX_IE_LENGTH]; +}; + +/** @brief The endpoint configuration */ +struct bt_a2dp_codec_cfg { + /** The media codec configuration content */ + struct bt_a2dp_codec_ie *codec_config; }; /** @brief Stream End Point */ -struct bt_a2dp_endpoint { - /** Code ID */ - uint8_t codec_id; - /** Stream End Point Information */ - struct bt_avdtp_seid_lsep info; - /** Pointer to preset codec chosen */ - struct bt_a2dp_preset *preset; +struct bt_a2dp_ep { + /** Code Type @ref bt_a2dp_codec_type */ + uint8_t codec_type; /** Capabilities */ - struct bt_a2dp_preset *caps; + struct bt_a2dp_codec_ie *codec_cap; + /** AVDTP Stream End Point Identifier */ + struct bt_avdtp_sep sep; + /* Internally used stream object pointer */ + struct bt_a2dp_stream *stream; }; -/** @brief Stream End Point Media Type */ -enum MEDIA_TYPE { - /** Audio Media Type */ - BT_A2DP_AUDIO = 0x00, - /** Video Media Type */ - BT_A2DP_VIDEO = 0x01, - /** Multimedia Media Type */ - BT_A2DP_MULTIMEDIA = 0x02 +struct bt_a2dp_ep_info { + /** Code Type @ref bt_a2dp_codec_type */ + uint8_t codec_type; + /** Codec capabilities, if SBC, use function of a2dp_codec_sbc.h to parse it */ + struct bt_a2dp_codec_ie codec_cap; + /** Stream End Point Information */ + struct bt_avdtp_sep_info sep_info; }; -/** @brief Stream End Point Role */ -enum ROLE_TYPE { - /** Source Role */ - BT_A2DP_SOURCE = 0, - /** Sink Role */ - BT_A2DP_SINK = 1 +/** @brief Helper enum to be used as return value of bt_a2dp_discover_ep_cb. + * The value informs the caller to perform further pending actions or stop them. + */ +enum { + BT_A2DP_DISCOVER_EP_STOP = 0, + BT_A2DP_DISCOVER_EP_CONTINUE, }; -/** @brief A2DP structure */ -struct bt_a2dp; +/** @typedef bt_a2dp_discover_ep_cb + * + * @brief Called when a stream endpoint is discovered. + * + * A function of this type is given by the user to the bt_a2dp_discover_param + * object. It'll be called on each valid stream endpoint discovery completion. + * When no more endpoint then NULL is passed to the user. Otherwise user can get + * valid endpoint information from parameter info, user can set parameter ep to + * get the endpoint after the callback is return. + * The returned function value allows the user to control retrieving follow-up + * endpoints if any. If the user doesn't want to read more endpoints since + * current found endpoints fulfill its requirements then should return + * BT_A2DP_DISCOVER_EP_STOP. Otherwise returned value means + * more subcall iterations are allowable. + * + * @param a2dp a2dp connection object identifying a2dp connection to queried remote. + * @param info Object pointing to the information of the callbacked endpoint. + * @param ep If the user want to use this found endpoint, user can set value to it + * to get the ednpoint that can be used futher in other A2DP APIs. It is NULL if info + * is NULL (no more endpoint is found). + * + * @return BT_A2DP_DISCOVER_EP_STOP in case of no more need to continue discovery + * for next endpoint. By returning BT_A2DP_DISCOVER_EP_STOP user allows this + * discovery continuation. + */ +typedef uint8_t (*bt_a2dp_discover_ep_cb)(struct bt_a2dp *a2dp, + struct bt_a2dp_ep_info *info, struct bt_a2dp_ep **ep); + +struct bt_a2dp_discover_param { + /** discover callback */ + bt_a2dp_discover_ep_cb cb; + /** The discovered endpoint info that is callbacked by cb */ + struct bt_a2dp_ep_info info; + /** The max count of remote endpoints that can be got, + * it save endpoint info internally. + */ + struct bt_avdtp_sep_info *seps_info; + /** The max count of seps (strem endpoint) that can be got in this call route */ + uint8_t sep_count; +}; + +/** @brief The connecting callback */ +struct bt_a2dp_cb { + /** @brief A a2dp connection has been established. + * + * This callback notifies the application of a a2dp connection. + * It means the AVDTP L2CAP connection. + * In case the err parameter is non-zero it means that the + * connection establishment failed. + * + * @param a2dp a2dp connection object. + * @param err error code. + */ + void (*connected)(struct bt_a2dp *a2dp, int err); + /** @brief A a2dp connection has been disconnected. + * + * This callback notifies the application that a a2dp connection + * has been disconnected. + * + * @param a2dp a2dp connection object. + */ + void (*disconnected)(struct bt_a2dp *a2dp); + /** + * @brief Endpoint config request callback + * + * The callback is called whenever an endpoint is requested to be + * configured. + * + * @param a2dp a2dp connection object. + * @param[in] ep Local Audio Endpoint being configured. + * @param[in] codec_cfg Codec configuration. + * @param[out] stream Pointer to stream that will be configured for the endpoint. + * @param[out] rsp_err_code give the error code if response error. + * bt_a2dp_err_code or bt_avdtp_err_code + * + * @return 0 in case of success or negative value in case of error. + */ + int (*config_req)(struct bt_a2dp *a2dp, struct bt_a2dp_ep *ep, + struct bt_a2dp_codec_cfg *codec_cfg, struct bt_a2dp_stream **stream, + uint8_t *rsp_err_code); + /** @brief Callback function for bt_a2dp_stream_config() + * + * Called when the codec configure operation is completed. + * + * @param[in] stream Pointer to stream object. + * @param[in] rsp_err_code the remote responsed error code + * bt_a2dp_err_code or bt_avdtp_err_code + */ + void (*config_rsp)(struct bt_a2dp_stream *stream, uint8_t rsp_err_code); + /** + * @brief stream establishment request callback + * + * The callback is called whenever an stream is requested to be + * established (open cmd and create the stream l2cap channel). + * + * @param[in] stream Pointer to stream object. + * @param[out] rsp_err_code give the error code if response error. + * bt_a2dp_err_code or bt_avdtp_err_code + * + * @return 0 in case of success or negative value in case of error. + */ + int (*establish_req)(struct bt_a2dp_stream *stream, uint8_t *rsp_err_code); + /** @brief Callback function for bt_a2dp_stream_establish() + * + * Called when the establishment operation is completed. + * (open cmd and create the stream l2cap channel). + * + * @param[in] stream Pointer to stream object. + * @param[in] rsp_err_code the remote responsed error code + * bt_a2dp_err_code or bt_avdtp_err_code + */ + void (*establish_rsp)(struct bt_a2dp_stream *stream, uint8_t rsp_err_code); + /** + * @brief stream release request callback + * + * The callback is called whenever an stream is requested to be + * released (release cmd and release the l2cap channel) + * + * @param[in] stream Pointer to stream object. + * @param[out] rsp_err_code give the error code if response error. + * bt_a2dp_err_code or bt_avdtp_err_code + * + * @return 0 in case of success or negative value in case of error. + */ + int (*release_req)(struct bt_a2dp_stream *stream, uint8_t *rsp_err_code); + /** @brief Callback function for bt_a2dp_stream_release() + * + * Called when the release operation is completed. + * (release cmd and release the l2cap channel) + * + * @param[in] stream Pointer to stream object. + * @param[in] rsp_err_code the remote responsed error code + * bt_a2dp_err_code or bt_avdtp_err_code + */ + void (*release_rsp)(struct bt_a2dp_stream *stream, uint8_t rsp_err_code); + /** + * @brief stream start request callback + * + * The callback is called whenever an stream is requested to be + * started. + * + * @param[in] stream Pointer to stream object. + * @param[out] rsp_err_code give the error code if response error. + * bt_a2dp_err_code or bt_avdtp_err_code + * + * @return 0 in case of success or negative value in case of error. + */ + int (*start_req)(struct bt_a2dp_stream *stream, uint8_t *rsp_err_code); + /** @brief Callback function for bt_a2dp_stream_start() + * + * Called when the start operation is completed. + * + * @param[in] stream Pointer to stream object. + * @param[in] rsp_err_code the remote responsed error code + * bt_a2dp_err_code or bt_avdtp_err_code + */ + void (*start_rsp)(struct bt_a2dp_stream *stream, uint8_t rsp_err_code); + /** + * @brief Endpoint suspend request callback + * + * The callback is called whenever an stream is requested to be + * suspended. + * + * @param[in] stream Pointer to stream object. + * @param[out] rsp_err_code give the error code if response error. + * bt_a2dp_err_code or bt_avdtp_err_code + * + * @return 0 in case of success or negative value in case of error. + */ + int (*suspend_req)(struct bt_a2dp_stream *stream, uint8_t *rsp_err_code); + /** @brief Callback function for bt_a2dp_stream_suspend() + * + * Called when the suspend operation is completed. + * + * @param[in] stream Pointer to stream object. + * @param[in] rsp_err_code the remote responsed error code + * bt_a2dp_err_code or bt_avdtp_err_code + */ + void (*suspend_rsp)(struct bt_a2dp_stream *stream, uint8_t rsp_err_code); + /** + * @brief Endpoint config request callback + * + * The callback is called whenever an endpoint is requested to be + * reconfigured. + * + * @param[in] stream Pointer to stream object. + * @param[out] rsp_err_code give the error code if response error. + * bt_a2dp_err_code or bt_avdtp_err_code + * + * @return 0 in case of success or negative value in case of error. + */ + int (*reconfig_req)(struct bt_a2dp_stream *stream, uint8_t *rsp_err_code); + /** @brief Callback function for bt_a2dp_stream_reconfig() + * + * Called when the reconfig operation is completed. + * + * @param[in] stream Pointer to stream object. + * @param[in] rsp_err_code the remote responsed error code + * bt_a2dp_err_code or bt_avdtp_err_code + */ + void (*reconfig_rsp)(struct bt_a2dp_stream *stream, uint8_t rsp_err_code); +}; /** @brief A2DP Connect. * * This function is to be called after the conn parameter is obtained by * performing a GAP procedure. The API is to be used to establish A2DP * connection between devices. + * This function only establish AVDTP L2CAP Signaling connection. + * After connection success, the callback that is registered by + * bt_a2dp_register_connect_callback is called. * * @param conn Pointer to bt_conn structure. * @@ -91,20 +549,242 @@ struct bt_a2dp; */ struct bt_a2dp *bt_a2dp_connect(struct bt_conn *conn); -/** @brief Endpoint Registration. +/** @brief disconnect l2cap a2dp + * + * This function close AVDTP L2CAP Signaling connection. + * It closes the AVDTP L2CAP Media connection too if it is established. * - * This function is used for registering the stream end points. The user has - * to take care of allocating the memory, the preset pointer and then pass the - * required arguments. Also, only one sep can be registered at a time. + * @param a2dp The a2dp instance. + * + * @return 0 in case of success and error code in case of error. + */ +int bt_a2dp_disconnect(struct bt_a2dp *a2dp); + +/** @brief Endpoint Registration. * - * @param endpoint Pointer to bt_a2dp_endpoint structure. + * @param ep Pointer to bt_a2dp_ep structure. * @param media_type Media type that the Endpoint is. * @param role Role of Endpoint. * * @return 0 in case of success and error code in case of error. */ -int bt_a2dp_register_endpoint(struct bt_a2dp_endpoint *endpoint, - uint8_t media_type, uint8_t role); +int bt_a2dp_register_ep(struct bt_a2dp_ep *ep, uint8_t media_type, uint8_t role); + +/** @brief register callback. + * + * The cb is called when bt_a2dp_connect is called or it is operated by remote device. + * + * @param cb The callback function. + * + * @return 0 in case of success and error code in case of error. + */ +int bt_a2dp_register_cb(struct bt_a2dp_cb *cb); + +/** @brief Discover remote endpoints. + * + * @param a2dp The a2dp instance. + * @param param the discover used param. + * + * @return 0 in case of success and error code in case of error. + */ +int bt_a2dp_discover(struct bt_a2dp *a2dp, struct bt_a2dp_discover_param *param); + +/** @brief A2DP Stream */ +struct bt_a2dp_stream { + /** local endpoint */ + struct bt_a2dp_ep *local_ep; + /** remote endpoint */ + struct bt_a2dp_ep *remote_ep; + /** remote endpoint's Stream End Point ID */ + uint8_t remote_ep_id; + /** Audio stream operations */ + struct bt_a2dp_stream_ops *ops; + /** the a2dp connection */ + struct bt_a2dp *a2dp; + /** the stream current configuration */ + struct bt_a2dp_codec_ie codec_config; +}; + +/** @brief The stream endpoint related operations */ +struct bt_a2dp_stream_ops { + /** + * @brief Stream configured callback + * + * The callback is called whenever an Audio Stream has been configured. + * + * @param stream Stream object that has been configured. + */ + void (*configured)(struct bt_a2dp_stream *stream); + /** + * @brief Stream establishment callback + * + * The callback is called whenever an Audio Stream has been established. + * + * @param stream Stream object that has been established. + */ + void (*established)(struct bt_a2dp_stream *stream); + /** + * @brief Stream release callback + * + * The callback is called whenever an Audio Stream has been released. + * + * @param stream Stream object that has been released. + */ + void (*released)(struct bt_a2dp_stream *stream); + /** + * @brief Stream start callback + * + * The callback is called whenever an Audio Stream has been started. + * + * @param stream Stream object that has been started. + */ + void (*started)(struct bt_a2dp_stream *stream); + /** + * @brief Stream suspend callback + * + * The callback is called whenever an Audio Stream has been suspended. + * + * @param stream Stream object that has been suspended. + */ + void (*suspended)(struct bt_a2dp_stream *stream); + /** + * @brief Stream reconfigured callback + * + * The callback is called whenever an Audio Stream has been reconfigured. + * + * @param stream Stream object that has been reconfigured. + */ + void (*reconfigured)(struct bt_a2dp_stream *stream); +#if defined(CONFIG_BT_A2DP_SINK) + /** @brief the media streaming data, only for sink + * + * @param buf the data buf + * @param seq_num the seqence number + * @param ts the time stamp + */ + void (*recv)(struct bt_a2dp_stream *stream, + struct net_buf *buf, uint16_t seq_num, uint32_t ts); +#endif +#if defined(CONFIG_BT_A2DP_SOURCE) + /** + * @brief Stream audio HCI sent callback + * + * This callback will be called once the controller marks the SDU + * as completed. When the controller does so is implementation + * dependent. It could be after the SDU is enqueued for transmission, + * or after it is sent on air or flushed. + * + * This callback is only used if the ISO data path is HCI. + * + * @param stream Stream object. + */ + void (*sent)(struct bt_a2dp_stream *stream); +#endif +}; + +/** + * @brief Register Audio callbacks for a stream. + * + * Register Audio callbacks for a stream. + * + * @param stream Stream object. + * @param ops Stream operations structure. + */ +void bt_a2dp_stream_cb_register(struct bt_a2dp_stream *stream, struct bt_a2dp_stream_ops *ops); + +/** @brief configure endpoint. + * + * bt_a2dp_discover can be used to find remote's endpoints. + * This function to configure the selected endpoint that is found by + * bt_a2dp_discover. + * This function sends AVDTP_SET_CONFIGURATION. + * + * @param a2dp The a2dp instance. + * @param stream Stream object. + * @param local_ep The configured endpoint that is registered. + * @param remote_ep The remote endpoint. + * @param config The config to configure the endpoint. + * + * @return 0 in case of success and error code in case of error. + */ +int bt_a2dp_stream_config(struct bt_a2dp *a2dp, struct bt_a2dp_stream *stream, + struct bt_a2dp_ep *local_ep, struct bt_a2dp_ep *remote_ep, + struct bt_a2dp_codec_cfg *config); + +/** @brief establish a2dp streamer. + * + * This function sends the AVDTP_OPEN command and create the l2cap channel. + * + * @param stream The stream object. + * + * @return 0 in case of success and error code in case of error. + */ +int bt_a2dp_stream_establish(struct bt_a2dp_stream *stream); + +/** @brief release a2dp streamer. + * + * This function sends the AVDTP_CLOSE command and release the l2cap channel. + * + * @param stream The stream object. + * + * @return 0 in case of success and error code in case of error. + */ +int bt_a2dp_stream_release(struct bt_a2dp_stream *stream); + +/** @brief start a2dp streamer. + * + * This function sends the AVDTP_START command. + * + * @param stream The stream object. + * + * @return 0 in case of success and error code in case of error. + */ +int bt_a2dp_stream_start(struct bt_a2dp_stream *stream); + +/** @brief suspend a2dp streamer. + * + * This function sends the AVDTP_SUSPEND command. + * + * @param stream The stream object. + * + * @return 0 in case of success and error code in case of error. + */ +int bt_a2dp_stream_suspend(struct bt_a2dp_stream *stream); + +/** @brief re-configure a2dp streamer + * + * This function sends the AVDTP_RECONFIGURE command. + * + * @param stream The stream object. + * @param config The config to configure the stream. + * + * @return 0 in case of success and error code in case of error. + */ +int bt_a2dp_stream_reconfig(struct bt_a2dp_stream *stream, struct bt_a2dp_codec_cfg *config); + +/** @brief get the stream l2cap mtu + * + * @param stream The stream object. + * + * @return mtu value + */ +uint32_t bt_a2dp_get_mtu(struct bt_a2dp_stream *stream); + +#if defined(CONFIG_BT_A2DP_SOURCE) +/** @brief send a2dp media data + * + * Only A2DP source side can call this function. + * + * @param stream The stream object. + * @param buf The data. + * @param seq_num The sequence number. + * @param ts The time stamp. + * + * @return 0 in case of success and error code in case of error. + */ +int bt_a2dp_stream_send(struct bt_a2dp_stream *stream, struct net_buf *buf, + uint16_t seq_num, uint32_t ts); +#endif #ifdef __cplusplus } diff --git a/include/zephyr/bluetooth/classic/a2dp_codec_sbc.h b/include/zephyr/bluetooth/classic/a2dp_codec_sbc.h new file mode 100644 index 00000000000..1a96c914324 --- /dev/null +++ b/include/zephyr/bluetooth/classic/a2dp_codec_sbc.h @@ -0,0 +1,124 @@ +/** @file + * @brief Advance Audio Distribution Profile - SBC Codec header. + */ +/* + * SPDX-License-Identifier: Apache-2.0 + * Copyright (c) 2015-2016 Intel Corporation + * Copyright (c) 2021 NXP + * + * Licensed 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 ZEPHYR_INCLUDE_BLUETOOTH_A2DP_CODEC_H_ +#define ZEPHYR_INCLUDE_BLUETOOTH_A2DP_CODEC_H_ + +#ifdef __cplusplus +extern "C" { +#endif + +/* Sampling Frequency */ +#define A2DP_SBC_SAMP_FREQ_16000 BIT(7) +#define A2DP_SBC_SAMP_FREQ_32000 BIT(6) +#define A2DP_SBC_SAMP_FREQ_44100 BIT(5) +#define A2DP_SBC_SAMP_FREQ_48000 BIT(4) + +/* Channel Mode */ +#define A2DP_SBC_CH_MODE_MONO BIT(3) +#define A2DP_SBC_CH_MODE_DUAL BIT(2) +#define A2DP_SBC_CH_MODE_STREO BIT(1) +#define A2DP_SBC_CH_MODE_JOINT BIT(0) + +/* Block Length */ +#define A2DP_SBC_BLK_LEN_4 BIT(7) +#define A2DP_SBC_BLK_LEN_8 BIT(6) +#define A2DP_SBC_BLK_LEN_12 BIT(5) +#define A2DP_SBC_BLK_LEN_16 BIT(4) + +/* Subbands */ +#define A2DP_SBC_SUBBAND_4 BIT(3) +#define A2DP_SBC_SUBBAND_8 BIT(2) + +/* Allocation Method */ +#define A2DP_SBC_ALLOC_MTHD_SNR BIT(1) +#define A2DP_SBC_ALLOC_MTHD_LOUDNESS BIT(0) + +#define BT_A2DP_SBC_SAMP_FREQ(cap) ((cap->config[0] >> 4) & 0x0f) +#define BT_A2DP_SBC_CHAN_MODE(cap) ((cap->config[0]) & 0x0f) +#define BT_A2DP_SBC_BLK_LEN(cap) ((cap->config[1] >> 4) & 0x0f) +#define BT_A2DP_SBC_SUB_BAND(cap) ((cap->config[1] >> 2) & 0x03) +#define BT_A2DP_SBC_ALLOC_MTHD(cap) ((cap->config[1]) & 0x03) + +/** @brief SBC Codec */ +struct bt_a2dp_codec_sbc_params { + /** First two octets of configuration */ + uint8_t config[2]; + /** Minimum Bitpool Value */ + uint8_t min_bitpool; + /** Maximum Bitpool Value */ + uint8_t max_bitpool; +} __packed; + +/** If the F bit is set to 0, this field indicates the number of frames contained + * in this packet. If the F bit is set to 1, this field indicates the number + * of remaining fragments, including the current fragment. + * Therefore, the last counter value shall be one. + */ +#define BT_A2DP_SBC_MEDIA_HDR_NUM_FRAMES_GET(hdr) FIELD_GET(GENMASK(3, 0), (hdr)) +/** Set to 1 for the last packet of a fragmented SBC frame, otherwise set to 0. */ +#define BT_A2DP_SBC_MEDIA_HDR_L_GET(hdr) FIELD_GET(BIT(5), (hdr)) +/** Set to 1 for the starting packet of a fragmented SBC frame, otherwise set to 0. */ +#define BT_A2DP_SBC_MEDIA_HDR_S_GET(hdr) FIELD_GET(BIT(6), (hdr)) +/** Set to 1 if the SBC frame is fragmented, otherwise set to 0. */ +#define BT_A2DP_SBC_MEDIA_HDR_F_GET(hdr) FIELD_GET(BIT(7), (hdr)) + +/** If the F bit is set to 0, this field indicates the number of frames contained + * in this packet. If the F bit is set to 1, this field indicates the number + * of remaining fragments, including the current fragment. + * Therefore, the last counter value shall be one. + */ +#define BT_A2DP_SBC_MEDIA_HDR_NUM_FRAMES_SET(hdr, val)\ + hdr = ((hdr) & ~GENMASK(3, 0)) | FIELD_PREP(GENMASK(3, 0), (val)) +/** Set to 1 for the last packet of a fragmented SBC frame, otherwise set to 0. */ +#define BT_A2DP_SBC_MEDIA_HDR_L_SET(hdr, val)\ + hdr = ((hdr) & ~BIT(5)) | FIELD_PREP(BIT(5), (val)) +/** Set to 1 for the starting packet of a fragmented SBC frame, otherwise set to 0. */ +#define BT_A2DP_SBC_MEDIA_HDR_S_SET(hdr, val)\ + hdr = ((hdr) & ~BIT(6)) | FIELD_PREP(BIT(6), (val)) +/** Set to 1 if the SBC frame is fragmented, otherwise set to 0. */ +#define BT_A2DP_SBC_MEDIA_HDR_F_SET(hdr, val)\ + hdr = ((hdr) & ~BIT(7)) | FIELD_PREP(BIT(7), (val)) + +#define BT_A2DP_SBC_MEDIA_HDR_ENCODE(num_frames, l, s, f)\ + FIELD_PREP(GENMASK(3, 0), num_frames) | FIELD_PREP(BIT(5), l) |\ + FIELD_PREP(BIT(6), s) | FIELD_PREP(BIT(7), f) + +/** @brief get channel num of a2dp sbc config. + * + * @param sbc_codec The a2dp sbc parameter. + * + * @return the channel num. + */ +uint8_t bt_a2dp_sbc_get_channel_num(struct bt_a2dp_codec_sbc_params *sbc_codec); + +/** @brief get sample rate of a2dp sbc config. + * + * @param sbc_codec The a2dp sbc parameter. + * + * @return the sample rate. + */ +uint32_t bt_a2dp_sbc_get_sampling_frequency(struct bt_a2dp_codec_sbc_params *sbc_codec); + +#ifdef __cplusplus +} +#endif + +#endif /* ZEPHYR_INCLUDE_BLUETOOTH_A2DP_CODEC_H_ */ diff --git a/include/zephyr/bluetooth/classic/avdtp.h b/include/zephyr/bluetooth/classic/avdtp.h index 396025814b8..cfd41923978 100644 --- a/include/zephyr/bluetooth/classic/avdtp.h +++ b/include/zephyr/bluetooth/classic/avdtp.h @@ -4,49 +4,137 @@ /* * Copyright (c) 2015-2016 Intel Corporation + * Copyright 2024 NXP * * SPDX-License-Identifier: Apache-2.0 */ #ifndef ZEPHYR_INCLUDE_BLUETOOTH_AVDTP_H_ #define ZEPHYR_INCLUDE_BLUETOOTH_AVDTP_H_ -#include - #ifdef __cplusplus extern "C" { #endif -/** @brief AVDTP SEID Information */ -struct bt_avdtp_seid_info { - /** Stream End Point ID */ - uint8_t id:6; +/** + * @brief AVDTP error code + */ +enum bt_avdtp_err_code { + /** The response is success, it is not from avdtp spec. */ + BT_AVDTP_SUCCESS = 0x00, + /** The request is time out without response, it is not from avdtp spec. */ + BT_AVDTP_TIME_OUT = 0xFF, + /** The request packet header format error */ + BT_AVDTP_BAD_HEADER_FORMAT = 0x01, + /** The request packet length is not match the assumed length */ + BT_AVDTP_BAD_LENGTH = 0x11, + /** The requested command indicates an invalid ACP SEID (not addressable) */ + BT_AVDTP_BAD_ACP_SEID = 0x12, + /** The SEP is in use */ + BT_AVDTP_SEP_IN_USE = 0x13, + /** The SEP is not in use */ + BT_AVDTP_SEP_NOT_IN_USE = 0x14, + /** The value of Service Category in the request packet is not defined in AVDTP */ + BT_AVDTP_BAD_SERV_CATEGORY = 0x17, + /** The requested command has an incorrect payload format */ + BT_AVDTP_BAD_PAYLOAD_FORMAT = 0x18, + /** The requested command is not supported by the device */ + BT_AVDTP_NOT_SUPPORTED_COMMAND = 0x19, + /** The reconfigure command is an attempt to reconfigure a transport service codec_cap + * of the SEP. Reconfigure is only permitted for application service codec_cap + */ + BT_AVDTP_INVALID_CAPABILITIES = 0x1A, + /** The requested Recovery Type is not defined in AVDTP */ + BT_AVDTP_BAD_RECOVERY_TYPE = 0x22, + /** The format of Media Transport Capability is not correct */ + BT_AVDTP_BAD_MEDIA_TRANSPORT_FORMAT = 0x23, + /** The format of Recovery Service Capability is not correct */ + BT_AVDTP_BAD_RECOVERY_FORMAT = 0x25, + /** The format of Header Compression Service Capability is not correct */ + BT_AVDTP_BAD_ROHC_FORMAT = 0x26, + /** The format of Content Protection Service Capability is not correct */ + BT_AVDTP_BAD_CP_FORMAT = 0x27, + /** The format of Multiplexing Service Capability is not correct */ + BT_AVDTP_BAD_MULTIPLEXING_FORMAT = 0x28, + /** Configuration not supported */ + BT_AVDTP_UNSUPPORTED_CONFIGURAION = 0x29, + /** Indicates that the ACP state machine is in an invalid state in order to process the + * signal. This also includes the situation when an INT receives a request for the + * same command that it is currently expecting a response + */ + BT_AVDTP_BAD_STATE = 0x31, +}; + +/** @brief Stream End Point Type */ +enum bt_avdtp_sep_type { + /** Source Role */ + BT_AVDTP_SOURCE = 0, + /** Sink Role */ + BT_AVDTP_SINK = 1 +}; + +/** @brief Stream End Point Media Type */ +enum bt_avdtp_media_type { + /** Audio Media Type */ + BT_AVDTP_AUDIO = 0x00, + /** Video Media Type */ + BT_AVDTP_VIDEO = 0x01, + /** Multimedia Media Type */ + BT_AVDTP_MULTIMEDIA = 0x02 +}; + +/** @brief AVDTP stream endpoint information. + * Don't need to care endianness because it is not used for data parsing. + */ +struct bt_avdtp_sep_info { /** End Point usage status */ uint8_t inuse:1; + /** Stream End Point ID that is the identifier of the stream endpoint */ + uint8_t id:6; /** Reserved */ - uint8_t rfa0:1; - /** Media-type of the End Point */ - uint8_t media_type:4; - /** TSEP of the End Point */ - uint8_t tsep:1; - /** Reserved */ - uint8_t rfa1:3; -} __packed; + uint8_t reserved:1; + /** Stream End-point Type that indicates if the stream end-point is SNK or SRC */ + enum bt_avdtp_sep_type tsep; + /** Media-type of the End Point + * Only @ref BT_AVDTP_AUDIO is supported now. + */ + enum bt_avdtp_media_type media_type; +}; -/** @brief AVDTP Local SEP*/ -struct bt_avdtp_seid_lsep { - /** Stream End Point information */ - struct bt_avdtp_seid_info sep; - /** Pointer to next local Stream End Point structure */ - struct bt_avdtp_seid_lsep *next; +/** @brief service category Type */ +enum bt_avdtp_service_category { + /** Media Transport */ + BT_AVDTP_SERVICE_MEDIA_TRANSPORT = 0x01, + /** Reporting */ + BT_AVDTP_SERVICE_REPORTING = 0x02, + /** Recovery */ + BT_AVDTP_SERVICE_MEDIA_RECOVERY = 0x03, + /** Content Protection */ + BT_AVDTP_SERVICE_CONTENT_PROTECTION = 0x04, + /** Header Compression */ + BT_AVDTP_SERVICE_HEADER_COMPRESSION = 0x05, + /** Multiplexing */ + BT_AVDTP_SERVICE_MULTIPLEXING = 0x06, + /** Media Codec */ + BT_AVDTP_SERVICE_MEDIA_CODEC = 0x07, + /** Delay Reporting */ + BT_AVDTP_SERVICE_DELAY_REPORTING = 0x08, }; -/** @brief AVDTP Stream */ -struct bt_avdtp_stream { - struct bt_l2cap_br_chan chan; /* Transport Channel*/ - struct bt_avdtp_seid_info lsep; /* Configured Local SEP */ - struct bt_avdtp_seid_info rsep; /* Configured Remote SEP*/ - uint8_t state; /* current state of the stream */ - struct bt_avdtp_stream *next; +/** @brief AVDTP Stream End Point */ +struct bt_avdtp_sep { + /** Stream End Point information */ + struct bt_avdtp_sep_info sep_info; + /** Media Transport Channel*/ + struct bt_l2cap_br_chan chan; + /** the endpoint media data */ + void (*media_data_cb)(struct bt_avdtp_sep *sep, + struct net_buf *buf); + /** avdtp session */ + struct bt_avdtp *session; + /** SEP state */ + uint8_t state; + /* Internally used list node */ + sys_snode_t _node; }; #ifdef __cplusplus diff --git a/subsys/bluetooth/host/classic/CMakeLists.txt b/subsys/bluetooth/host/classic/CMakeLists.txt index 039f961c786..001bdb86f31 100644 --- a/subsys/bluetooth/host/classic/CMakeLists.txt +++ b/subsys/bluetooth/host/classic/CMakeLists.txt @@ -3,7 +3,7 @@ zephyr_library() zephyr_library_link_libraries(subsys__bluetooth) -zephyr_library_sources_ifdef(CONFIG_BT_A2DP a2dp.c) +zephyr_library_sources_ifdef(CONFIG_BT_A2DP a2dp.c a2dp_codec_sbc.c) zephyr_library_sources_ifdef(CONFIG_BT_AVDTP avdtp.c) zephyr_library_sources_ifdef(CONFIG_BT_RFCOMM rfcomm.c) diff --git a/subsys/bluetooth/host/classic/Kconfig b/subsys/bluetooth/host/classic/Kconfig index 2869978f9c7..9c47a8b53f6 100644 --- a/subsys/bluetooth/host/classic/Kconfig +++ b/subsys/bluetooth/host/classic/Kconfig @@ -154,6 +154,30 @@ config BT_A2DP help This option enables the A2DP profile +if BT_AVDTP + +config BT_AVDTP_RTP_VERSION + int "Bluetooth AVDTP RTP version" + default 2 + help + This option sets the AVDTP RTP protocol version + +endif # BT_AVDTP + +if BT_A2DP + +config BT_A2DP_SOURCE + bool "Bluetooth A2DP Profile Source Function" + help + This option enables the A2DP profile source function + +config BT_A2DP_SINK + bool "Bluetooth A2DP Profile Sink Function" + help + This option enables the A2DP profile sink function + +endif # BT_A2DP + config BT_PAGE_TIMEOUT hex "Bluetooth Page Timeout" default 0x2000 diff --git a/subsys/bluetooth/host/classic/a2dp.c b/subsys/bluetooth/host/classic/a2dp.c index 0fb6723480e..a958660f9c5 100644 --- a/subsys/bluetooth/host/classic/a2dp.c +++ b/subsys/bluetooth/host/classic/a2dp.c @@ -4,6 +4,7 @@ /* * Copyright (c) 2015-2016 Intel Corporation + * Copyright 2021,2024 NXP * * SPDX-License-Identifier: Apache-2.0 */ @@ -19,12 +20,31 @@ #include #include #include +#include #include #include "common/assert.h" +#define A2DP_SBC_PAYLOAD_TYPE (0x60u) + +#define A2DP_AVDTP(_avdtp) CONTAINER_OF(_avdtp, struct bt_a2dp, session) +#define DISCOVER_REQ(_req) CONTAINER_OF(_req, struct bt_avdtp_discover_params, req) +#define DISCOVER_PARAM(_discover_param) CONTAINER_OF(_discover_param, struct bt_a2dp,\ + discover_param) +#define GET_CAP_REQ(_req) CONTAINER_OF(_req, struct bt_avdtp_get_capabilities_params, req) +#define GET_CAP_PARAM(_get_cap_param) CONTAINER_OF(_get_cap_param, struct bt_a2dp,\ + get_capabilities_param) +#define SET_CONF_REQ(_req) CONTAINER_OF(_req, struct bt_avdtp_set_configuration_params, req) +#define SET_CONF_PARAM(_set_conf_param) CONTAINER_OF(_set_conf_param, struct bt_a2dp,\ + set_config_param) +#define OPEN_REQ(_req) CONTAINER_OF(_req, struct bt_avdtp_open_params, req) +#define OPEN_PARAM(_open_param) CONTAINER_OF(_open_param, struct bt_a2dp, open_param) +#define START_REQ(_req) CONTAINER_OF(_req, struct bt_avdtp_start_params, req) +#define START_PARAM(_start_param) CONTAINER_OF(_start_param, struct bt_a2dp, start_param) + #include "host/hci_core.h" #include "host/conn_internal.h" +#include "host/l2cap_internal.h" #include "avdtp_internal.h" #include "a2dp_internal.h" @@ -34,19 +54,41 @@ LOG_MODULE_REGISTER(bt_a2dp); #define A2DP_NO_SPACE (-1) +enum bt_a2dp_internal_state { + INTERNAL_STATE_IDLE = 0, + INTERNAL_STATE_AVDTP_CONNECTED, +}; + struct bt_a2dp { struct bt_avdtp session; + struct bt_avdtp_discover_params discover_param; + struct bt_a2dp_discover_param *discover_cb_param; + struct bt_avdtp_get_capabilities_params get_capabilities_param; + struct bt_avdtp_set_configuration_params set_config_param; + struct bt_avdtp_open_params open_param; + struct bt_avdtp_start_params start_param; + uint8_t get_cap_index; + enum bt_a2dp_internal_state a2dp_state; + uint8_t peer_seps_count; }; +static struct bt_a2dp_cb *a2dp_cb; +K_MUTEX_DEFINE(a2dp_mutex); + +#define A2DP_LOCK() k_mutex_lock(&a2dp_mutex, K_FOREVER) +#define A2DP_UNLOCK() k_mutex_unlock(&a2dp_mutex) + /* Connections */ static struct bt_a2dp connection[CONFIG_BT_MAX_CONN]; -void a2d_reset(struct bt_a2dp *a2dp_conn) +static int bt_a2dp_get_sep_caps(struct bt_a2dp *a2dp); + +static void a2dp_reset(struct bt_a2dp *a2dp) { - (void)memset(a2dp_conn, 0, sizeof(struct bt_a2dp)); + (void)memset(a2dp, 0, sizeof(struct bt_a2dp)); } -struct bt_a2dp *get_new_connection(struct bt_conn *conn) +static struct bt_a2dp *get_new_connection(struct bt_conn *conn) { int8_t i, free; @@ -61,12 +103,13 @@ struct bt_a2dp *get_new_connection(struct bt_conn *conn) for (i = 0; i < CONFIG_BT_MAX_CONN; i++) { if (connection[i].session.br_chan.chan.conn == conn) { LOG_DBG("Conn already exists"); - return NULL; + return &connection[i]; } if (!connection[i].session.br_chan.chan.conn && - free == A2DP_NO_SPACE) { + free == A2DP_NO_SPACE) { free = i; + break; } } @@ -76,35 +119,721 @@ struct bt_a2dp *get_new_connection(struct bt_conn *conn) } /* Clean the memory area before returning */ - a2d_reset(&connection[free]); + a2dp_reset(&connection[free]); return &connection[free]; } -int a2dp_accept(struct bt_conn *conn, struct bt_avdtp **session) +/* The AVDTP L2CAP signal channel established */ +static void a2dp_connected(struct bt_avdtp *session) { - struct bt_a2dp *a2dp_conn; + struct bt_a2dp *a2dp = A2DP_AVDTP(session); - a2dp_conn = get_new_connection(conn); - if (!a2dp_conn) { - return -ENOMEM; + a2dp->a2dp_state = INTERNAL_STATE_AVDTP_CONNECTED; + /* notify a2dp app the connection. */ + if ((a2dp_cb != NULL) && (a2dp_cb->connected != NULL)) { + a2dp_cb->connected(a2dp, 0); } +} + +/* The AVDTP L2CAP signal channel released */ +static void a2dp_disconnected(struct bt_avdtp *session) +{ + struct bt_a2dp *a2dp = A2DP_AVDTP(session); + + /* notify a2dp app the disconnection. */ + if ((a2dp_cb != NULL) && (a2dp_cb->disconnected != NULL)) { + a2dp_cb->disconnected(a2dp); + } +} - *session = &(a2dp_conn->session); - LOG_DBG("session: %p", &(a2dp_conn->session)); +/* avdtp alloc buf from upper layer */ +static struct net_buf *a2dp_alloc_buf(struct bt_avdtp *session) +{ + return NULL; +} +static int a2dp_discovery_ind(struct bt_avdtp *session, uint8_t *errcode) +{ + *errcode = 0; + return 0; +} + +static int a2dp_get_capabilities_ind(struct bt_avdtp *session, struct bt_avdtp_sep *sep, + struct net_buf *rsp_buf, uint8_t *errcode) +{ + struct bt_a2dp_ep *ep; + + *errcode = 0; + /* Service Category: Media Transport */ + net_buf_add_u8(rsp_buf, BT_AVDTP_SERVICE_MEDIA_TRANSPORT); + net_buf_add_u8(rsp_buf, 0); + /* Service Category: Media Codec */ + net_buf_add_u8(rsp_buf, BT_AVDTP_SERVICE_MEDIA_CODEC); + ep = CONTAINER_OF(sep, struct bt_a2dp_ep, sep); + /* Length Of Service Capability */ + net_buf_add_u8(rsp_buf, ep->codec_cap->len + 2u); + /* Media Type */ + net_buf_add_u8(rsp_buf, sep->sep_info.media_type << 4u); + /* Media Codec Type */ + net_buf_add_u8(rsp_buf, ep->codec_type); + /* Codec Info Element */ + net_buf_add_mem(rsp_buf, &ep->codec_cap->codec_ie[0], ep->codec_cap->len); return 0; } -/* Callback for incoming requests */ -static struct bt_avdtp_ind_cb cb_ind = { - /*TODO*/ +static int a2dp_set_config_ind(struct bt_avdtp *session, struct bt_avdtp_sep *sep, + uint8_t int_seid, struct net_buf *buf, uint8_t *errcode) +{ + struct bt_a2dp *a2dp = A2DP_AVDTP(session); + struct bt_a2dp_ep *ep; + struct bt_a2dp_stream *stream; + struct bt_a2dp_stream_ops *ops; + uint8_t codec_type; + uint8_t *codec_info_element; + uint16_t codec_info_element_len; + int err; + + *errcode = 0; + ep = CONTAINER_OF(sep, struct bt_a2dp_ep, sep); + if (ep == NULL) { + *errcode = BT_AVDTP_BAD_ACP_SEID; + return -1; + } + + /* parse the configuration */ + codec_info_element_len = 4U; + err = bt_avdtp_parse_capability_codec(buf, + &codec_type, &codec_info_element, + &codec_info_element_len); + if (err) { + *errcode = BT_AVDTP_BAD_ACP_SEID; + return -1; + } + + if (codec_type == BT_A2DP_SBC) { + struct bt_a2dp_codec_sbc_params *sbc_set; + struct bt_a2dp_codec_sbc_params *sbc; + + if (codec_info_element_len != 4U) { + *errcode = BT_AVDTP_BAD_ACP_SEID; + return -1; + } + + sbc_set = (struct bt_a2dp_codec_sbc_params *)codec_info_element; + sbc = (struct bt_a2dp_codec_sbc_params *)&ep->codec_cap->codec_ie[0]; + if (((BT_A2DP_SBC_SAMP_FREQ(sbc_set) & BT_A2DP_SBC_SAMP_FREQ(sbc)) == 0) || + ((BT_A2DP_SBC_CHAN_MODE(sbc_set) & BT_A2DP_SBC_CHAN_MODE(sbc)) == 0) || + ((BT_A2DP_SBC_BLK_LEN(sbc_set) & BT_A2DP_SBC_BLK_LEN(sbc)) == 0) || + ((BT_A2DP_SBC_SUB_BAND(sbc_set) & BT_A2DP_SBC_SUB_BAND(sbc)) == 0) || + ((BT_A2DP_SBC_ALLOC_MTHD(sbc_set) & BT_A2DP_SBC_ALLOC_MTHD(sbc)) == 0)) { + *errcode = BT_AVDTP_BAD_ACP_SEID; + return -1; + } + } + + if ((a2dp_cb != NULL) && (a2dp_cb->config_req != NULL)) { + struct bt_a2dp_codec_cfg cfg; + struct bt_a2dp_codec_ie codec_config; + uint8_t rsp_err_code; + + cfg.codec_config = &codec_config; + cfg.codec_config->len = codec_info_element_len; + memcpy(&cfg.codec_config->codec_ie[0], codec_info_element, + (codec_info_element_len > A2DP_MAX_IE_LENGTH ? + A2DP_MAX_IE_LENGTH : codec_info_element_len)); + err = a2dp_cb->config_req(a2dp, ep, &cfg, &stream, &rsp_err_code); + if (err) { + *errcode = rsp_err_code; + } else if (stream != NULL) { + stream->a2dp = a2dp; + stream->local_ep = ep; + stream->remote_ep_id = int_seid; + stream->remote_ep = NULL; + stream->codec_config = *cfg.codec_config; + ep->stream = stream; + } + } + + ops = stream->ops; + if (*errcode == 0) { + if ((ops != NULL) && (stream->ops->configured != NULL)) { + stream->ops->configured(stream); + } + } + + return (*errcode == 0) ? 0 : -1; +} + +#if defined(CONFIG_BT_A2DP_SINK) +static void bt_a2dp_media_data_callback( + struct bt_avdtp_sep *sep, + struct net_buf *buf) +{ + struct bt_avdtp_media_hdr *media_hdr; + struct bt_a2dp_ep *ep; + struct bt_a2dp_stream *stream; + + ep = CONTAINER_OF(sep, struct bt_a2dp_ep, sep); + if ((ep == NULL) || (ep->stream == NULL)) { + return; + } + stream = ep->stream; + + media_hdr = net_buf_pull_mem(buf, sizeof(*media_hdr)); + + stream->ops->recv(stream, buf, + sys_get_be16((uint8_t *)&media_hdr->sequence_number), + sys_get_be32((uint8_t *)&media_hdr->time_stamp)); +} +#endif + +static int a2dp_open_ind(struct bt_avdtp *session, struct bt_avdtp_sep *sep, uint8_t *errcode) +{ + struct bt_a2dp_ep *ep; + struct bt_a2dp_stream *stream; + struct bt_a2dp_stream_ops *ops; + + *errcode = 0; + ep = CONTAINER_OF(sep, struct bt_a2dp_ep, sep); + if (ep->stream == NULL) { + *errcode = BT_AVDTP_BAD_ACP_SEID; + return -1; + } + stream = ep->stream; + + if ((a2dp_cb != NULL) && (a2dp_cb->establish_req != NULL)) { + uint8_t rsp_err_code; + int err; + + err = a2dp_cb->establish_req(stream, &rsp_err_code); + if (err) { + *errcode = rsp_err_code; + } + } + + ops = stream->ops; + if (*errcode == 0) { + if ((ops != NULL) && (stream->ops->established != NULL)) { + stream->ops->established(stream); + } + } + + return (*errcode == 0) ? 0 : -1; +} + +static int a2dp_start_ind(struct bt_avdtp *session, struct bt_avdtp_sep *sep, uint8_t *errcode) +{ + struct bt_a2dp_ep *ep; + struct bt_a2dp_stream *stream; + struct bt_a2dp_stream_ops *ops; + + *errcode = 0; + ep = CONTAINER_OF(sep, struct bt_a2dp_ep, sep); + if (ep->stream == NULL) { + *errcode = BT_AVDTP_BAD_ACP_SEID; + return -1; + } + stream = ep->stream; + + if ((a2dp_cb != NULL) && (a2dp_cb->start_req != NULL)) { + uint8_t rsp_err_code; + int err; + + err = a2dp_cb->start_req(stream, &rsp_err_code); + if (err) { + *errcode = rsp_err_code; + } + } + + ops = stream->ops; + if (*errcode == 0) { + if ((ops != NULL) && (stream->ops->started != NULL)) { + stream->ops->started(stream); + } + } + + return (*errcode == 0) ? 0 : -1; +} + +static int a2dp_close_ind(struct bt_avdtp *session, struct bt_avdtp_sep *sep, uint8_t *errcode) +{ + struct bt_a2dp_ep *ep; + struct bt_a2dp_stream *stream; + struct bt_a2dp_stream_ops *ops; + + *errcode = 0; + ep = CONTAINER_OF(sep, struct bt_a2dp_ep, sep); + if (ep->stream == NULL) { + *errcode = BT_AVDTP_BAD_ACP_SEID; + return -1; + } + stream = ep->stream; + + if ((a2dp_cb != NULL) && (a2dp_cb->release_req != NULL)) { + uint8_t rsp_err_code; + int err; + + err = a2dp_cb->release_req(stream, &rsp_err_code); + if (err) { + *errcode = rsp_err_code; + } + } + + ops = stream->ops; + if (*errcode == 0) { + if ((ops != NULL) && (stream->ops->released != NULL)) { + stream->ops->released(stream); + } + } + + return (*errcode == 0) ? 0 : -1; +} + +static int a2dp_suspend_ind(struct bt_avdtp *session, struct bt_avdtp_sep *sep, uint8_t *errcode) +{ + struct bt_a2dp_ep *ep; + struct bt_a2dp_stream *stream; + struct bt_a2dp_stream_ops *ops; + + *errcode = 0; + ep = CONTAINER_OF(sep, struct bt_a2dp_ep, sep); + if (ep->stream == NULL) { + *errcode = BT_AVDTP_BAD_ACP_SEID; + return -1; + } + stream = ep->stream; + + if ((a2dp_cb != NULL) && (a2dp_cb->suspend_req != NULL)) { + uint8_t rsp_err_code; + int err; + + err = a2dp_cb->suspend_req(stream, &rsp_err_code); + if (err) { + *errcode = rsp_err_code; + } + } + + ops = stream->ops; + if (*errcode == 0) { + if ((ops != NULL) && (stream->ops->suspended != NULL)) { + stream->ops->suspended(stream); + } + } + + return (*errcode == 0) ? 0 : -1; +} + +static int bt_a2dp_open_cb(struct bt_avdtp_req *req) +{ + struct bt_a2dp *a2dp = OPEN_PARAM(OPEN_REQ(req)); + struct bt_a2dp_ep *ep; + struct bt_a2dp_stream *stream; + struct bt_a2dp_stream_ops *ops; + + ep = CONTAINER_OF(a2dp->open_param.sep, struct bt_a2dp_ep, sep); + if (ep->stream == NULL) { + return -1; + } + stream = ep->stream; + + LOG_DBG("OPEN result:%d", a2dp->open_param.status); + + if ((a2dp_cb != NULL) && (a2dp_cb->establish_rsp != NULL)) { + a2dp_cb->establish_rsp(stream, a2dp->open_param.status); + } + + ops = stream->ops; + if ((!a2dp->open_param.status) && (ops->established != NULL)) { + ops->established(stream); + } + return 0; +} + +static int bt_a2dp_set_config_cb(struct bt_avdtp_req *req) +{ + struct bt_a2dp *a2dp = SET_CONF_PARAM(SET_CONF_REQ(req)); + struct bt_a2dp_ep *ep; + struct bt_a2dp_stream *stream; + struct bt_a2dp_stream_ops *ops; + + ep = CONTAINER_OF(a2dp->set_config_param.sep, struct bt_a2dp_ep, sep); + if (ep->stream == NULL) { + return -1; + } + stream = ep->stream; + + LOG_DBG("SET CONFIGURATION result:%d", a2dp->set_config_param.status); + + if ((a2dp_cb != NULL) && (a2dp_cb->config_rsp != NULL)) { + a2dp_cb->config_rsp(stream, a2dp->set_config_param.status); + } + + ops = stream->ops; + if ((!a2dp->set_config_param.status) && (ops->configured != NULL)) { + ops->configured(stream); + } + return 0; +} + +static int bt_a2dp_get_capabilities_cb(struct bt_avdtp_req *req) +{ + int err; + uint8_t *codec_info_element; + struct bt_a2dp *a2dp = GET_CAP_PARAM(GET_CAP_REQ(req)); + uint16_t codec_info_element_len; + uint8_t codec_type; + uint8_t user_ret; + + LOG_DBG("GET CAPABILITIES result:%d", a2dp->get_capabilities_param.status); + if (a2dp->get_capabilities_param.status) { + if ((a2dp->discover_cb_param != NULL) && + (a2dp->discover_cb_param->cb != NULL)) { + a2dp->discover_cb_param->cb(a2dp, NULL, NULL); + a2dp->discover_cb_param = NULL; + } + return 0; + } + + err = bt_avdtp_parse_capability_codec(a2dp->get_capabilities_param.buf, + &codec_type, &codec_info_element, &codec_info_element_len); + if (err) { + LOG_DBG("codec capability parsing fail"); + return 0; + } + + if (codec_info_element_len > A2DP_MAX_IE_LENGTH) { + codec_info_element_len = A2DP_MAX_IE_LENGTH; + } + + if ((a2dp->discover_cb_param != NULL) && (a2dp->discover_cb_param->cb != NULL)) { + struct bt_a2dp_ep *ep; + struct bt_a2dp_ep_info *info = &a2dp->discover_cb_param->info; + + info->codec_type = codec_type; + info->sep_info = a2dp->discover_cb_param->seps_info[a2dp->get_cap_index]; + memcpy(&info->codec_cap.codec_ie, + codec_info_element, codec_info_element_len); + info->codec_cap.len = codec_info_element_len; + user_ret = a2dp->discover_cb_param->cb(a2dp, info, &ep); + if (ep != NULL) { + ep->codec_type = info->codec_type; + ep->sep.sep_info = info->sep_info; + *ep->codec_cap = info->codec_cap; + ep->stream = NULL; + } + + if (user_ret == BT_A2DP_DISCOVER_EP_CONTINUE) { + if (bt_a2dp_get_sep_caps(a2dp) != 0) { + a2dp->discover_cb_param->cb(a2dp, NULL, NULL); + a2dp->discover_cb_param = NULL; + } + } else { + a2dp->discover_cb_param = NULL; + } + } + + return 0; +} + +static int bt_a2dp_get_sep_caps(struct bt_a2dp *a2dp) +{ + int err; + + if ((a2dp->discover_cb_param == NULL) || (a2dp->discover_cb_param->cb == NULL)) { + return -EINVAL; + } + + if (a2dp->get_cap_index == 0xFFu) { + a2dp->get_cap_index = 0U; + } else { + a2dp->get_cap_index++; + } + + for (; a2dp->get_cap_index < a2dp->peer_seps_count; + a2dp->get_cap_index++) { + if (a2dp->discover_cb_param->seps_info[a2dp->get_cap_index].media_type == + BT_AVDTP_AUDIO) { + a2dp->get_capabilities_param.req.func = bt_a2dp_get_capabilities_cb; + a2dp->get_capabilities_param.buf = NULL; + a2dp->get_capabilities_param.stream_endpoint_id = + a2dp->discover_cb_param->seps_info[a2dp->get_cap_index].id; + err = bt_avdtp_get_capabilities(&a2dp->session, + &a2dp->get_capabilities_param); + if (err) { + LOG_DBG("AVDTP get codec_cap failed"); + a2dp->discover_cb_param->cb(a2dp, NULL, NULL); + a2dp->discover_cb_param = NULL; + } + return 0; + } + } + return -1; +} + +static int bt_a2dp_discover_cb(struct bt_avdtp_req *req) +{ + struct bt_a2dp *a2dp = DISCOVER_PARAM(DISCOVER_REQ(req)); + struct bt_avdtp_sep_info *sep_info; + int err; + + LOG_DBG("DISCOVER result:%d", DISCOVER_REQ(req)->status); + if (a2dp->discover_cb_param == NULL) { + return -EINVAL; + } + a2dp->peer_seps_count = 0U; + if (!(DISCOVER_REQ(req)->status)) { + if (a2dp->discover_cb_param->sep_count == 0) { + if (a2dp->discover_cb_param->cb != NULL) { + a2dp->discover_cb_param->cb(a2dp, NULL, NULL); + a2dp->discover_cb_param = NULL; + } + return -EINVAL; + } + + do { + sep_info = &a2dp->discover_cb_param->seps_info[a2dp->peer_seps_count]; + err = bt_avdtp_parse_sep(DISCOVER_REQ(req)->buf, sep_info); + if (err) { + break; + } + a2dp->peer_seps_count++; + LOG_DBG("id:%d, inuse:%d, media_type:%d, tsep:%d, ", + sep_info->id, + sep_info->inuse, + sep_info->media_type, + sep_info->tsep); + } while (a2dp->peer_seps_count < a2dp->discover_cb_param->sep_count); + + /* trigger the getting capability */ + a2dp->get_cap_index = 0xFFu; + if (bt_a2dp_get_sep_caps(a2dp) != 0) { + /* the peer_seps' caps is done.*/ + if (a2dp->discover_cb_param->cb != NULL) { + a2dp->discover_cb_param->cb(a2dp, NULL, NULL); + a2dp->discover_cb_param = NULL; + } + } + } else { + if (a2dp->discover_cb_param->cb != NULL) { + a2dp->discover_cb_param->cb(a2dp, NULL, NULL); + a2dp->discover_cb_param = NULL; + } + } + + return 0; +} + +static int bt_a2dp_start_cb(struct bt_avdtp_req *req) +{ + struct bt_a2dp *a2dp = START_PARAM(START_REQ(req)); + struct bt_a2dp_ep *ep; + struct bt_a2dp_stream *stream; + struct bt_a2dp_stream_ops *ops; + + ep = CONTAINER_OF(a2dp->start_param.sep, struct bt_a2dp_ep, sep); + if (ep->stream == NULL) { + return -1; + } + stream = ep->stream; + + LOG_DBG("START result:%d", a2dp->start_param.status); + + if ((a2dp_cb != NULL) && (a2dp_cb->start_rsp != NULL)) { + a2dp_cb->start_rsp(stream, a2dp->start_param.status); + } + + ops = stream->ops; + if ((!a2dp->start_param.status) && (ops->started != NULL)) { + ops->started(stream); + } + return 0; +} + +int bt_a2dp_discover(struct bt_a2dp *a2dp, struct bt_a2dp_discover_param *param) +{ + int err; + + if ((a2dp == NULL) || (param == NULL)) { + return -EINVAL; + } + + if (a2dp->a2dp_state != INTERNAL_STATE_AVDTP_CONNECTED) { + return -EIO; + } + + if (a2dp->discover_cb_param != NULL) { + return -EBUSY; + } + + a2dp->discover_cb_param = param; + a2dp->discover_param.req.func = bt_a2dp_discover_cb; + a2dp->discover_param.buf = NULL; + err = bt_avdtp_discover(&a2dp->session, &a2dp->discover_param); + if (err) { + if (a2dp->discover_cb_param->cb != NULL) { + a2dp->discover_cb_param->cb(a2dp, NULL, NULL); + } + a2dp->discover_cb_param = NULL; + } + + return err; +} + +void bt_a2dp_stream_cb_register(struct bt_a2dp_stream *stream, struct bt_a2dp_stream_ops *ops) +{ + BT_ASSERT(stream); + stream->ops = ops; +} + +int bt_a2dp_stream_config(struct bt_a2dp *a2dp, struct bt_a2dp_stream *stream, + struct bt_a2dp_ep *local_ep, struct bt_a2dp_ep *remote_ep, + struct bt_a2dp_codec_cfg *config) +{ + if ((a2dp == NULL) || (stream == NULL) || (local_ep == NULL) || + (remote_ep == NULL) || (config == NULL)) { + return -EINVAL; + } + + if ((local_ep->sep.sep_info.tsep == remote_ep->sep.sep_info.tsep) || + (local_ep->codec_type != remote_ep->codec_type)) { + return -EINVAL; + } + + stream->local_ep = local_ep; + stream->remote_ep = remote_ep; + stream->remote_ep_id = remote_ep->sep.sep_info.id; + stream->a2dp = a2dp; + local_ep->stream = stream; + remote_ep->stream = stream; + a2dp->set_config_param.req.func = bt_a2dp_set_config_cb; + a2dp->set_config_param.acp_stream_ep_id = remote_ep->sep.sep_info.id; + a2dp->set_config_param.int_stream_endpoint_id = local_ep->sep.sep_info.id; + a2dp->set_config_param.media_type = BT_AVDTP_AUDIO; + a2dp->set_config_param.media_codec_type = local_ep->codec_type; + a2dp->set_config_param.codec_specific_ie_len = config->codec_config->len; + a2dp->set_config_param.codec_specific_ie = &config->codec_config->codec_ie[0]; + a2dp->set_config_param.sep = &local_ep->sep; + return bt_avdtp_set_configuration(&a2dp->session, &a2dp->set_config_param); +} + +int bt_a2dp_stream_establish(struct bt_a2dp_stream *stream) +{ + struct bt_a2dp *a2dp; + + if ((stream == NULL) || (stream->local_ep == NULL) || (stream->a2dp == NULL)) { + return -EINVAL; + } + + a2dp = stream->a2dp; + a2dp->open_param.req.func = bt_a2dp_open_cb; + a2dp->open_param.acp_stream_ep_id = stream->remote_ep == NULL ? + stream->remote_ep->sep.sep_info.id : stream->remote_ep_id; + a2dp->open_param.sep = a2dp->set_config_param.sep; + return bt_avdtp_open(&a2dp->session, &a2dp->open_param); +} + +int bt_a2dp_stream_release(struct bt_a2dp_stream *stream) +{ + /* todo: see the API description in a2dp.h */ + return -ENOTSUP; +} + +int bt_a2dp_stream_start(struct bt_a2dp_stream *stream) +{ + struct bt_a2dp *a2dp; + + if ((stream == NULL) || (stream->local_ep == NULL) || (stream->a2dp == NULL)) { + return -EINVAL; + } + + a2dp = stream->a2dp; + a2dp->start_param.req.func = bt_a2dp_start_cb; + a2dp->start_param.acp_stream_ep_id = stream->remote_ep == NULL ? + stream->remote_ep->sep.sep_info.id : stream->remote_ep_id; + a2dp->start_param.sep = &stream->local_ep->sep; + return bt_avdtp_start(&a2dp->session, &a2dp->start_param); +} + +int bt_a2dp_stream_suspend(struct bt_a2dp_stream *stream) +{ + /* todo: see the API description in a2dp.h */ + return -ENOTSUP; +} + +int bt_a2dp_stream_reconfig(struct bt_a2dp_stream *stream, struct bt_a2dp_codec_cfg *config) +{ + /* todo: see the API description in a2dp.h */ + return -ENOTSUP; +} + +uint32_t bt_a2dp_get_mtu(struct bt_a2dp_stream *stream) +{ + if ((stream == NULL) || (stream->local_ep == NULL)) { + return -EINVAL; + } + + return bt_avdtp_get_media_mtu(&stream->local_ep->sep); +} + +#if defined(CONFIG_BT_A2DP_SOURCE) +int bt_a2dp_stream_send(struct bt_a2dp_stream *stream, struct net_buf *buf, + uint16_t seq_num, uint32_t ts) +{ + struct bt_avdtp_media_hdr *media_hdr; + + if (stream == NULL) { + return -EINVAL; + } + + media_hdr = net_buf_push(buf, sizeof(struct bt_avdtp_media_hdr)); + if (stream->local_ep->codec_type == BT_A2DP_SBC) { + media_hdr->playload_type = A2DP_SBC_PAYLOAD_TYPE; + } + + memset(media_hdr, 0, sizeof(struct bt_avdtp_media_hdr)); + media_hdr->synchronization_source = 0U; + /* update time_stamp in the buf */ + sys_put_be32(ts, (uint8_t *)&media_hdr->time_stamp); + /* update sequence_number in the buf */ + sys_put_be16(seq_num, (uint8_t *)&media_hdr->sequence_number); + /* send the buf */ + return bt_avdtp_send_media_data(&stream->local_ep->sep, buf); +} +#endif + +static const struct bt_avdtp_ops_cb signaling_avdtp_ops = { + .connected = a2dp_connected, + .disconnected = a2dp_disconnected, + .alloc_buf = a2dp_alloc_buf, + .discovery_ind = a2dp_discovery_ind, + .get_capabilities_ind = a2dp_get_capabilities_ind, + .set_configuration_ind = a2dp_set_config_ind, + .open_ind = a2dp_open_ind, + .start_ind = a2dp_start_ind, + .close_ind = a2dp_close_ind, + .suspend_ind = a2dp_suspend_ind, }; +int a2dp_accept(struct bt_conn *conn, struct bt_avdtp **session) +{ + struct bt_a2dp *a2dp; + + a2dp = get_new_connection(conn); + if (!a2dp) { + return -ENOMEM; + } + + *session = &(a2dp->session); + a2dp->session.ops = &signaling_avdtp_ops; + LOG_DBG("session: %p", &(a2dp->session)); + + return 0; +} + /* The above callback structures need to be packed and passed to AVDTP */ static struct bt_avdtp_event_cb avdtp_cb = { - .ind = &cb_ind, - .accept = a2dp_accept + .accept = a2dp_accept, }; int bt_a2dp_init(void) @@ -118,46 +847,77 @@ int bt_a2dp_init(void) return err; } + for (uint8_t i = 0; i < CONFIG_BT_MAX_CONN; i++) { + memset(&connection[i], 0, sizeof(struct bt_a2dp)); + } + LOG_DBG("A2DP Initialized successfully."); return 0; } struct bt_a2dp *bt_a2dp_connect(struct bt_conn *conn) { - struct bt_a2dp *a2dp_conn; + struct bt_a2dp *a2dp; int err; - a2dp_conn = get_new_connection(conn); - if (!a2dp_conn) { + A2DP_LOCK(); + a2dp = get_new_connection(conn); + if (!a2dp) { + A2DP_UNLOCK(); LOG_ERR("Cannot allocate memory"); return NULL; } - err = bt_avdtp_connect(conn, &(a2dp_conn->session)); + a2dp->a2dp_state = INTERNAL_STATE_IDLE; + + a2dp->session.ops = &signaling_avdtp_ops; + err = bt_avdtp_connect(conn, &(a2dp->session)); if (err < 0) { /* If error occurs, undo the saving and return the error */ - a2d_reset(a2dp_conn); + a2dp_reset(a2dp); + A2DP_UNLOCK(); LOG_DBG("AVDTP Connect failed"); return NULL; } + A2DP_UNLOCK(); LOG_DBG("Connect request sent"); - return a2dp_conn; + return a2dp; } -int bt_a2dp_register_endpoint(struct bt_a2dp_endpoint *endpoint, - uint8_t media_type, uint8_t role) +int bt_a2dp_disconnect(struct bt_a2dp *a2dp) +{ + /* todo: see the API description in a2dp.h */ + return -ENOTSUP; +} + +int bt_a2dp_register_ep(struct bt_a2dp_ep *ep, uint8_t media_type, uint8_t role) { int err; - BT_ASSERT(endpoint); + BT_ASSERT(ep); - err = bt_avdtp_register_sep(media_type, role, &(endpoint->info)); +#if defined(CONFIG_BT_A2DP_SINK) + if (role == BT_AVDTP_SINK) { + ep->sep.media_data_cb = bt_a2dp_media_data_callback; + } else { + ep->sep.media_data_cb = NULL; + } +#else + ep->sep.media_data_cb = NULL; +#endif + A2DP_LOCK(); + err = bt_avdtp_register_sep(media_type, role, &(ep->sep)); + A2DP_UNLOCK(); if (err < 0) { return err; } - /* TODO: Register SDP record */ + return 0; +} +int bt_a2dp_register_cb(struct bt_a2dp_cb *cb) +{ + a2dp_cb = cb; return 0; } diff --git a/subsys/bluetooth/host/classic/a2dp_codec_sbc.c b/subsys/bluetooth/host/classic/a2dp_codec_sbc.c new file mode 100644 index 00000000000..e5f66cd1e93 --- /dev/null +++ b/subsys/bluetooth/host/classic/a2dp_codec_sbc.c @@ -0,0 +1,51 @@ +/* + * Copyright 2024 NXP + * + * SPDX-License-Identifier: Apache-2.0 + */ + +#include +#include +#include +#include +#include +#include +#include + +#include +#include + + +uint8_t bt_a2dp_sbc_get_channel_num(struct bt_a2dp_codec_sbc_params *sbc_codec) +{ + __ASSERT_NO_MSG(sbc_codec != NULL); + + if (sbc_codec->config[0] & A2DP_SBC_CH_MODE_MONO) { + return 1U; + } else if (sbc_codec->config[0] & A2DP_SBC_CH_MODE_DUAL) { + return 2U; + } else if (sbc_codec->config[0] & A2DP_SBC_CH_MODE_STREO) { + return 2U; + } else if (sbc_codec->config[0] & A2DP_SBC_CH_MODE_JOINT) { + return 2U; + } else { + return 0U; + } +} + +uint32_t bt_a2dp_sbc_get_sampling_frequency(struct bt_a2dp_codec_sbc_params *sbc_codec) +{ + __ASSERT_NO_MSG(sbc_codec != NULL); + + if (sbc_codec->config[0] & A2DP_SBC_SAMP_FREQ_16000) { + return 16000U; + } else if (sbc_codec->config[0] & A2DP_SBC_SAMP_FREQ_32000) { + return 32000U; + } else if (sbc_codec->config[0] & A2DP_SBC_SAMP_FREQ_44100) { + return 44100U; + } else if (sbc_codec->config[0] & A2DP_SBC_SAMP_FREQ_48000) { + return 48000U; + } else { + return 0U; + } +} diff --git a/subsys/bluetooth/host/classic/avdtp.c b/subsys/bluetooth/host/classic/avdtp.c index 5f323bf87c6..e969d1f7696 100644 --- a/subsys/bluetooth/host/classic/avdtp.c +++ b/subsys/bluetooth/host/classic/avdtp.c @@ -38,35 +38,771 @@ LOG_MODULE_REGISTER(bt_avdtp); #define AVDTP_GET_SIG_ID(s) (s & AVDTP_SIGID_MASK) static struct bt_avdtp_event_cb *event_cb; - -static struct bt_avdtp_seid_lsep *lseps; +static sys_slist_t seps; #define AVDTP_CHAN(_ch) CONTAINER_OF(_ch, struct bt_avdtp, br_chan.chan) -#define AVDTP_KWORK(_work) CONTAINER_OF(k_work_delayable_from_work(_work), \ - struct bt_avdtp_req, timeout_work) +#define AVDTP_KWORK(_work) CONTAINER_OF(CONTAINER_OF(_work, struct k_work_delayable, work),\ + struct bt_avdtp, timeout_work) + +#define DISCOVER_REQ(_req) CONTAINER_OF(_req, struct bt_avdtp_discover_params, req) +#define GET_CAP_REQ(_req) CONTAINER_OF(_req, struct bt_avdtp_get_capabilities_params, req) +#define SET_CONF_REQ(_req) CONTAINER_OF(_req, struct bt_avdtp_set_configuration_params, req) +#define OPEN_REQ(_req) CONTAINER_OF(_req, struct bt_avdtp_open_params, req) +#define START_REQ(_req) CONTAINER_OF(_req, struct bt_avdtp_start_params, req) #define AVDTP_TIMEOUT K_SECONDS(6) -static const struct { - uint8_t sig_id; - void (*func)(struct bt_avdtp *session, struct net_buf *buf, - uint8_t msg_type); -} handler[] = { +K_MUTEX_DEFINE(avdtp_mutex); +#define AVDTP_LOCK() k_mutex_lock(&avdtp_mutex, K_FOREVER) +#define AVDTP_UNLOCK() k_mutex_unlock(&avdtp_mutex) + +enum sep_state { + AVDTP_IDLE = 0, + AVDTP_CONFIGURED, + /* establishing the transport sessions. */ + AVDTP_OPENING, + AVDTP_OPEN, + AVDTP_STREAMING, + AVDTP_CLOSING, + AVDTP_ABORTING, }; +/* L2CAP Interface callbacks */ +void bt_avdtp_media_l2cap_connected(struct bt_l2cap_chan *chan) +{ + struct bt_avdtp *session; + struct bt_avdtp_sep *sep = + CONTAINER_OF(chan, struct bt_avdtp_sep, chan.chan); + + if (!chan) { + LOG_ERR("Invalid AVDTP chan"); + return; + } + + session = sep->session; + if (session == NULL) { + return; + } + + LOG_DBG("chan %p session %p", chan, session); + sep->state = AVDTP_OPEN; + if (session->req != NULL) { + struct bt_avdtp_req *req = session->req; + + OPEN_REQ(req)->status = 0; + AVDTP_LOCK(); + session->req = NULL; + AVDTP_UNLOCK(); + if (req->func != NULL) { + req->func(req); + } + } +} + +void bt_avdtp_media_l2cap_disconnected(struct bt_l2cap_chan *chan) +{ + struct bt_avdtp_sep *sep = + CONTAINER_OF(chan, struct bt_avdtp_sep, chan.chan); + + LOG_DBG("chan %p", chan); + chan->conn = NULL; + if (sep->state > AVDTP_OPENING) { + sep->state = AVDTP_OPENING; + } +} + +int bt_avdtp_media_l2cap_recv(struct bt_l2cap_chan *chan, struct net_buf *buf) +{ + /* media data is received */ + struct bt_avdtp_sep *sep = + CONTAINER_OF(chan, struct bt_avdtp_sep, chan.chan); + + if (sep->media_data_cb != NULL) { + sep->media_data_cb(sep, buf); + } + return 0; +} + +static int avdtp_media_connect(struct bt_avdtp *session, struct bt_avdtp_sep *sep) +{ + static const struct bt_l2cap_chan_ops ops = { + .connected = bt_avdtp_media_l2cap_connected, + .disconnected = bt_avdtp_media_l2cap_disconnected, + .recv = bt_avdtp_media_l2cap_recv + }; + + if (!session) { + return -EINVAL; + } + + sep->session = session; + sep->chan.rx.mtu = BT_L2CAP_RX_MTU; + sep->chan.chan.ops = &ops; + sep->chan.required_sec_level = BT_SECURITY_L2; + + return bt_l2cap_chan_connect(session->br_chan.chan.conn, &sep->chan.chan, + BT_L2CAP_PSM_AVDTP); +} + +static struct net_buf *avdtp_create_reply_pdu(uint8_t msg_type, + uint8_t pkt_type, + uint8_t sig_id, + uint8_t tid) +{ + struct net_buf *buf; + struct bt_avdtp_single_sig_hdr *hdr; + + LOG_DBG(""); + + buf = bt_l2cap_create_pdu(NULL, 0); + if (!buf) { + LOG_ERR("Error: No Buff available"); + return NULL; + } + + hdr = net_buf_add(buf, sizeof(*hdr)); + + hdr->hdr = (msg_type | pkt_type << AVDTP_PKT_POSITION | + tid << AVDTP_TID_POSITION); + hdr->signal_id = sig_id & AVDTP_SIGID_MASK; + + LOG_DBG("hdr = 0x%02X, Signal_ID = 0x%02X", hdr->hdr, hdr->signal_id); + return buf; +} + +static void avdtp_discover_handler(struct bt_avdtp *session, + struct net_buf *buf, uint8_t msg_type, + uint8_t tid) +{ + if (msg_type == BT_AVDTP_CMD) { + int err; + struct bt_avdtp_sep *sep; + struct net_buf *rsp_buf; + uint8_t error_code = 0; + + if (session->ops->discovery_ind == NULL) { + err = -ENOTSUP; + } else { + err = session->ops->discovery_ind(session, &error_code); + } + + rsp_buf = avdtp_create_reply_pdu(err ? + BT_AVDTP_REJECT : BT_AVDTP_ACCEPT, + BT_AVDTP_PACKET_TYPE_SINGLE, + BT_AVDTP_DISCOVER, tid); + if (!rsp_buf) { + return; + } + + if (err) { + if (error_code == 0) { + error_code = BT_AVDTP_BAD_STATE; + } + LOG_DBG("discover err code:%d", error_code); + net_buf_add_u8(rsp_buf, error_code); + } else { + struct bt_avdtp_sep_data sep_data; + + SYS_SLIST_FOR_EACH_CONTAINER(&seps, sep, _node) { + memset(&sep_data, 0, sizeof(sep_data)); + sep_data.inuse = sep->sep_info.inuse; + sep_data.id = sep->sep_info.id; + sep_data.tsep = sep->sep_info.tsep; + sep_data.media_type = sep->sep_info.media_type; + net_buf_add_mem(rsp_buf, &sep_data, sizeof(sep_data)); + } + } + + err = bt_l2cap_chan_send(&session->br_chan.chan, rsp_buf); + if (err < 0) { + net_buf_unref(rsp_buf); + LOG_ERR("Error:L2CAP send fail - result = %d", err); + return; + } + } else { + struct bt_avdtp_req *req = session->req; + + if (session->req == NULL) { + return; + } + k_work_cancel_delayable(&session->timeout_work); + if (msg_type == BT_AVDTP_ACCEPT) { + DISCOVER_REQ(session->req)->status = 0; + DISCOVER_REQ(session->req)->buf = buf; + } else if (msg_type == BT_AVDTP_REJECT) { + DISCOVER_REQ(session->req)->status = net_buf_pull_u8(buf); + } else if (msg_type == BT_AVDTP_GEN_REJECT) { + DISCOVER_REQ(session->req)->status = BT_AVDTP_NOT_SUPPORTED_COMMAND; + } + AVDTP_LOCK(); + session->req = NULL; + AVDTP_UNLOCK(); + if (req->func != NULL) { + req->func(req); + } + } +} + +static struct bt_avdtp_sep *avdtp_get_sep(uint8_t stream_endpoint_id) +{ + struct bt_avdtp_sep *sep = NULL; + + SYS_SLIST_FOR_EACH_CONTAINER(&seps, sep, _node) { + if (sep->sep_info.id == stream_endpoint_id) { + break; + } + } + + return sep; +} + +static void avdtp_get_capabilities_handler(struct bt_avdtp *session, + struct net_buf *buf, uint8_t msg_type, uint8_t tid) +{ + if (msg_type == BT_AVDTP_CMD) { + int err = 0; + struct net_buf *rsp_buf; + struct bt_avdtp_sep *sep; + uint8_t error_code = 0; + + sep = avdtp_get_sep(net_buf_pull_u8(buf) >> 2); + if ((sep == NULL) || (session->ops->get_capabilities_ind == NULL)) { + err = -ENOTSUP; + } else { + rsp_buf = avdtp_create_reply_pdu(BT_AVDTP_ACCEPT, + BT_AVDTP_PACKET_TYPE_SINGLE, + BT_AVDTP_GET_CAPABILITIES, + tid); + if (!rsp_buf) { + return; + } + err = session->ops->get_capabilities_ind(session, + sep, rsp_buf, &error_code); + if (err) { + net_buf_unref(rsp_buf); + } + } + + if (err) { + rsp_buf = avdtp_create_reply_pdu(BT_AVDTP_REJECT, + BT_AVDTP_PACKET_TYPE_SINGLE, + BT_AVDTP_GET_CAPABILITIES, tid); + if (!rsp_buf) { + return; + } + + if (error_code == 0) { + error_code = BT_AVDTP_BAD_ACP_SEID; + } + LOG_DBG("get cap err code:%d", error_code); + net_buf_add_u8(rsp_buf, error_code); + } + + err = bt_l2cap_chan_send(&session->br_chan.chan, rsp_buf); + if (err < 0) { + net_buf_unref(rsp_buf); + LOG_ERR("Error:L2CAP send fail - result = %d", err); + return; + } + } else { + struct bt_avdtp_req *req = session->req; + + if (session->req == NULL) { + return; + } + k_work_cancel_delayable(&session->timeout_work); + GET_CAP_REQ(session->req)->buf = NULL; + + if (msg_type == BT_AVDTP_ACCEPT) { + GET_CAP_REQ(session->req)->status = 0; + if (session->req != NULL) { + GET_CAP_REQ(session->req)->buf = buf; + } + } else if (msg_type == BT_AVDTP_REJECT) { + GET_CAP_REQ(session->req)->status = net_buf_pull_u8(buf); + } else if (msg_type == BT_AVDTP_GEN_REJECT) { + GET_CAP_REQ(session->req)->status = BT_AVDTP_NOT_SUPPORTED_COMMAND; + } + AVDTP_LOCK(); + session->req = NULL; + AVDTP_UNLOCK(); + if (req->func != NULL) { + req->func(req); + } + } +} + +static void avdtp_process_configuration(struct bt_avdtp *session, + struct net_buf *buf, uint8_t msg_type, uint8_t tid) +{ + if (msg_type == BT_AVDTP_CMD) { + int err = 0; + struct bt_avdtp_sep *sep; + struct net_buf *rsp_buf; + uint8_t error_code = 0; + + sep = avdtp_get_sep(net_buf_pull_u8(buf) >> 2); + if ((sep == NULL) || (session->ops->set_configuration_ind == NULL)) { + err = -ENOTSUP; + } else { + if (sep->state == AVDTP_STREAMING) { + err = -ENOTSUP; + error_code = BT_AVDTP_BAD_STATE; + } else { + uint8_t int_seid; + + /* INT Stream Endpoint ID */ + int_seid = net_buf_pull_u8(buf); + err = session->ops->set_configuration_ind(session, + sep, int_seid, buf, &error_code); + } + } + + rsp_buf = avdtp_create_reply_pdu(err ? + BT_AVDTP_REJECT : BT_AVDTP_ACCEPT, + BT_AVDTP_PACKET_TYPE_SINGLE, + BT_AVDTP_SET_CONFIGURATION, tid); + if (!rsp_buf) { + return; + } + + if (err) { + if (error_code == 0) { + error_code = BT_AVDTP_BAD_ACP_SEID; + } + LOG_DBG("set configuration err code:%d", error_code); + /* Service Category: Media Codec */ + net_buf_add_u8(rsp_buf, BT_AVDTP_SERVICE_MEDIA_CODEC); + /* Length Of Service Capability */ + net_buf_add_u8(rsp_buf, 0); + /* ERROR CODE */ + net_buf_add_u8(rsp_buf, error_code); + } else { + sep->state = AVDTP_CONFIGURED; + } + + err = bt_l2cap_chan_send(&session->br_chan.chan, rsp_buf); + if (err < 0) { + net_buf_unref(rsp_buf); + LOG_ERR("Error:L2CAP send fail - result = %d", err); + return; + } + } else { + struct bt_avdtp_req *req = session->req; + + if (session->req == NULL) { + return; + } + k_work_cancel_delayable(&session->timeout_work); + if (msg_type == BT_AVDTP_ACCEPT) { + SET_CONF_REQ(req)->status = 0; + SET_CONF_REQ(req)->sep->state = AVDTP_CONFIGURED; + } else if (msg_type == BT_AVDTP_REJECT) { + /* Service Category */ + net_buf_pull_u8(buf); + SET_CONF_REQ(req)->status = net_buf_pull_u8(buf); + } else if (msg_type == BT_AVDTP_GEN_REJECT) { + SET_CONF_REQ(req)->status = BT_AVDTP_NOT_SUPPORTED_COMMAND; + } + AVDTP_LOCK(); + session->req = NULL; + AVDTP_UNLOCK(); + if (req->func != NULL) { + req->func(req); + } + } +} + +static void avdtp_set_configuration_handler(struct bt_avdtp *session, + struct net_buf *buf, uint8_t msg_type, uint8_t tid) +{ + avdtp_process_configuration(session, buf, msg_type, tid); +} + +static void avdtp_get_configuration_handler(struct bt_avdtp *session, + struct net_buf *buf, uint8_t msg_type, uint8_t tid) +{ + /* todo: is not supported now, reply reject */ + struct net_buf *rsp_buf; + int err; + + rsp_buf = avdtp_create_reply_pdu(BT_AVDTP_REJECT, + BT_AVDTP_PACKET_TYPE_SINGLE, + BT_AVDTP_GET_CONFIGURATION, tid); + if (!rsp_buf) { + LOG_ERR("Error: No Buff available"); + return; + } + + err = bt_l2cap_chan_send(&session->br_chan.chan, rsp_buf); + if (err < 0) { + net_buf_unref(rsp_buf); + LOG_ERR("Error:L2CAP send fail - result = %d", err); + return; + } +} + +static void avdtp_re_configure_handler(struct bt_avdtp *session, + struct net_buf *buf, uint8_t msg_type, uint8_t tid) +{ + avdtp_process_configuration(session, buf, msg_type, tid); +} + +static void avdtp_open_handler(struct bt_avdtp *session, + struct net_buf *buf, uint8_t msg_type, uint8_t tid) +{ + if (msg_type == BT_AVDTP_CMD) { + int err = 0; + struct bt_avdtp_sep *sep; + struct net_buf *rsp_buf; + uint8_t error_code = 0; + + sep = avdtp_get_sep(net_buf_pull_u8(buf) >> 2); + if ((sep == NULL) || (session->ops->open_ind == NULL)) { + err = -ENOTSUP; + } else { + if (sep->state != AVDTP_CONFIGURED) { + err = -ENOTSUP; + error_code = BT_AVDTP_BAD_STATE; + } else { + err = session->ops->open_ind(session, sep, &error_code); + } + } + + rsp_buf = avdtp_create_reply_pdu(err ? + BT_AVDTP_REJECT : BT_AVDTP_ACCEPT, + BT_AVDTP_PACKET_TYPE_SINGLE, + BT_AVDTP_OPEN, tid); + if (!rsp_buf) { + return; + } + + if (err) { + if (error_code == 0) { + error_code = BT_AVDTP_BAD_ACP_SEID; + } + LOG_DBG("open_ind err code:%d", error_code); + net_buf_add_u8(rsp_buf, error_code); + } else { + session->current_sep = sep; + sep->state = AVDTP_OPENING; + sep->sep_info.inuse = 1u; + } + + err = bt_l2cap_chan_send(&session->br_chan.chan, rsp_buf); + if (err < 0) { + net_buf_unref(rsp_buf); + LOG_ERR("Error:L2CAP send fail - result = %d", err); + return; + } + } else { + struct bt_avdtp_req *req = session->req; + + if (session->req == NULL) { + return; + } + k_work_cancel_delayable(&session->timeout_work); + if (msg_type == BT_AVDTP_ACCEPT) { + OPEN_REQ(req)->status = 0; + OPEN_REQ(req)->sep->state = AVDTP_OPENING; + if (!avdtp_media_connect(session, OPEN_REQ(req)->sep)) { + return; + } + } else if (msg_type == BT_AVDTP_REJECT) { + OPEN_REQ(req)->status = net_buf_pull_u8(buf); + } else if (msg_type == BT_AVDTP_GEN_REJECT) { + OPEN_REQ(req)->status = BT_AVDTP_NOT_SUPPORTED_COMMAND; + } + if (OPEN_REQ(req)->status) { + /* wait the media l2cap is established */ + AVDTP_LOCK(); + session->req = NULL; + AVDTP_UNLOCK(); + if (req->func != NULL) { + req->func(req); + } + } + } +} + +static void avdtp_start_handler(struct bt_avdtp *session, + struct net_buf *buf, uint8_t msg_type, uint8_t tid) +{ + if (msg_type == BT_AVDTP_CMD) { + int err = 0; + struct bt_avdtp_sep *sep; + struct net_buf *rsp_buf; + uint8_t error_code = 0; + + sep = avdtp_get_sep(net_buf_pull_u8(buf) >> 2); + if ((sep == NULL) || (session->ops->start_ind == NULL)) { + err = -ENOTSUP; + } else { + if (sep->state != AVDTP_OPEN) { + err = -ENOTSUP; + error_code = BT_AVDTP_BAD_STATE; + } else { + err = session->ops->start_ind(session, sep, &error_code); + } + } + + rsp_buf = avdtp_create_reply_pdu(err ? + BT_AVDTP_REJECT : BT_AVDTP_ACCEPT, + BT_AVDTP_PACKET_TYPE_SINGLE, + BT_AVDTP_START, tid); + if (!rsp_buf) { + return; + } + + if (err) { + if (error_code == 0) { + error_code = BT_AVDTP_BAD_ACP_SEID; + } + LOG_DBG("start err code:%d", error_code); + net_buf_add_u8(rsp_buf, error_code); + } else { + sep->state = AVDTP_STREAMING; + } + + err = bt_l2cap_chan_send(&session->br_chan.chan, rsp_buf); + if (err < 0) { + net_buf_unref(rsp_buf); + LOG_ERR("Error:L2CAP send fail - result = %d", err); + return; + } + } else { + struct bt_avdtp_req *req = session->req; + + if (session->req == NULL) { + return; + } + k_work_cancel_delayable(&session->timeout_work); + if (msg_type == BT_AVDTP_ACCEPT) { + START_REQ(req)->status = 0; + START_REQ(req)->sep->state = AVDTP_STREAMING; + } else if (msg_type == BT_AVDTP_REJECT) { + uint8_t acp_seid; + + acp_seid = net_buf_pull_u8(buf); + if (acp_seid != START_REQ(req)->acp_stream_ep_id) { + return; + } + + START_REQ(req)->status = net_buf_pull_u8(buf); + } else if (msg_type == BT_AVDTP_GEN_REJECT) { + START_REQ(req)->status = BT_AVDTP_NOT_SUPPORTED_COMMAND; + } + AVDTP_LOCK(); + session->req = NULL; + AVDTP_UNLOCK(); + if (req->func != NULL) { + req->func(req); + } + } +} + +static void avdtp_close_handler(struct bt_avdtp *session, + struct net_buf *buf, uint8_t msg_type, uint8_t tid) +{ + if (msg_type == BT_AVDTP_CMD) { + int err = 0; + struct bt_avdtp_sep *sep; + struct net_buf *rsp_buf; + uint8_t error_code = 0; + + sep = avdtp_get_sep(net_buf_pull_u8(buf) >> 2); + if ((sep == NULL) || (session->ops->close_ind == NULL)) { + err = -ENOTSUP; + } else { + if (sep->state != AVDTP_OPEN) { + err = -ENOTSUP; + error_code = BT_AVDTP_BAD_STATE; + } else { + err = session->ops->close_ind(session, sep, &error_code); + } + } + + rsp_buf = avdtp_create_reply_pdu(err ? + BT_AVDTP_REJECT : BT_AVDTP_ACCEPT, + BT_AVDTP_PACKET_TYPE_SINGLE, + BT_AVDTP_CLOSE, tid); + if (!rsp_buf) { + return; + } + + if (err) { + if (error_code == 0) { + error_code = BT_AVDTP_BAD_ACP_SEID; + } + LOG_DBG("close err code:%d", error_code); + net_buf_add_u8(rsp_buf, error_code); + } else { + sep->state = AVDTP_CONFIGURED; + sep->sep_info.inuse = 0u; + } + + err = bt_l2cap_chan_send(&session->br_chan.chan, rsp_buf); + if (err < 0) { + net_buf_unref(rsp_buf); + LOG_ERR("Error:L2CAP send fail - result = %d", err); + return; + } + } +} + +static void avdtp_suspend_handler(struct bt_avdtp *session, + struct net_buf *buf, uint8_t msg_type, uint8_t tid) +{ + if (msg_type == BT_AVDTP_CMD) { + int err = 0; + struct bt_avdtp_sep *sep; + struct net_buf *rsp_buf; + uint8_t error_code = 0; + + sep = avdtp_get_sep(net_buf_pull_u8(buf) >> 2); + if ((sep == NULL) || (session->ops->suspend_ind == NULL)) { + err = -ENOTSUP; + } else { + if (sep->state != AVDTP_STREAMING) { + err = -ENOTSUP; + error_code = BT_AVDTP_BAD_STATE; + } else { + err = session->ops->suspend_ind(session, sep, &error_code); + } + } + + rsp_buf = avdtp_create_reply_pdu(err ? + BT_AVDTP_REJECT : BT_AVDTP_ACCEPT, + BT_AVDTP_PACKET_TYPE_SINGLE, + BT_AVDTP_SUSPEND, tid); + if (!rsp_buf) { + return; + } + + if (err) { + if (error_code == 0) { + error_code = BT_AVDTP_BAD_ACP_SEID; + } + LOG_DBG("suspend err code:%d", error_code); + net_buf_add_u8(rsp_buf, error_code); + } else { + sep->state = AVDTP_OPEN; + } + + err = bt_l2cap_chan_send(&session->br_chan.chan, rsp_buf); + if (err < 0) { + net_buf_unref(rsp_buf); + LOG_ERR("Error:L2CAP send fail - result = %d", err); + return; + } + } +} + +static void avdtp_abort_handler(struct bt_avdtp *session, + struct net_buf *buf, uint8_t msg_type, uint8_t tid) +{ + if (msg_type == BT_AVDTP_CMD) { + int err = 0; + struct bt_avdtp_sep *sep; + struct net_buf *rsp_buf; + uint8_t error_code = 0; + + sep = avdtp_get_sep(net_buf_pull_u8(buf) >> 2); + if ((sep == NULL) || (session->ops->abort_ind == NULL)) { + err = -ENOTSUP; + } else { + err = session->ops->abort_ind(session, sep, &error_code); + } + + rsp_buf = avdtp_create_reply_pdu(err ? + BT_AVDTP_REJECT : BT_AVDTP_ACCEPT, + BT_AVDTP_PACKET_TYPE_SINGLE, + BT_AVDTP_ABORT, tid); + if (!rsp_buf) { + return; + } + + if (err) { + if (error_code == 0) { + error_code = BT_AVDTP_BAD_ACP_SEID; + } + LOG_DBG("abort err code:%d", error_code); + net_buf_add_u8(rsp_buf, error_code); + } else { + sep->state = AVDTP_IDLE; + } + + err = bt_l2cap_chan_send(&session->br_chan.chan, rsp_buf); + if (err < 0) { + net_buf_unref(rsp_buf); + LOG_ERR("Error:L2CAP send fail - result = %d", err); + return; + } + } +} + +/* Timeout handler */ +static void avdtp_timeout(struct k_work *work) +{ + struct bt_avdtp_req *req = (AVDTP_KWORK(work))->req; + + /* add process code */ + /* Gracefully Disconnect the Signalling and streaming L2cap chann*/ + if (req) { + LOG_DBG("Failed Signal_id = %d", req->sig); + + switch (req->sig) { + case BT_AVDTP_DISCOVER: + DISCOVER_REQ(req)->status = BT_AVDTP_TIME_OUT; + req->func(req); + break; + case BT_AVDTP_GET_CAPABILITIES: + GET_CAP_REQ(req)->status = BT_AVDTP_TIME_OUT; + req->func(req); + break; + case BT_AVDTP_SET_CONFIGURATION: + SET_CONF_REQ(req)->status = BT_AVDTP_TIME_OUT; + req->func(req); + break; + case BT_AVDTP_OPEN: + OPEN_REQ(req)->status = BT_AVDTP_TIME_OUT; + req->func(req); + break; + case BT_AVDTP_START: + START_REQ(req)->status = BT_AVDTP_TIME_OUT; + req->func(req); + break; + default: + break; + } + + AVDTP_KWORK(work)->req = NULL; + } +} + static int avdtp_send(struct bt_avdtp *session, - struct net_buf *buf, struct bt_avdtp_req *req) + struct net_buf *buf, struct bt_avdtp_req *req) { int result; struct bt_avdtp_single_sig_hdr *hdr; + AVDTP_LOCK(); + if (session->req != NULL) { + AVDTP_UNLOCK(); + return -EBUSY; + } + session->req = req; + AVDTP_UNLOCK(); hdr = (struct bt_avdtp_single_sig_hdr *)buf->data; result = bt_l2cap_chan_send(&session->br_chan.chan, buf); if (result < 0) { LOG_ERR("Error:L2CAP send fail - result = %d", result); net_buf_unref(buf); + AVDTP_LOCK(); + session->req = NULL; + AVDTP_UNLOCK(); return result; } @@ -75,9 +811,10 @@ static int avdtp_send(struct bt_avdtp *session, req->tid = AVDTP_GET_TR_ID(hdr->hdr); LOG_DBG("sig 0x%02X, tid 0x%02X", req->sig, req->tid); - session->req = req; + /* Init the timer */ + k_work_init_delayable(&session->timeout_work, avdtp_timeout); /* Start timeout work */ - k_work_reschedule(&session->req->timeout_work, AVDTP_TIMEOUT); + k_work_reschedule(&session->timeout_work, AVDTP_TIMEOUT); return result; } @@ -96,7 +833,7 @@ static struct net_buf *avdtp_create_pdu(uint8_t msg_type, hdr = net_buf_add(buf, sizeof(*hdr)); hdr->hdr = (msg_type | pkt_type << AVDTP_PKT_POSITION | - tid++ << AVDTP_TID_POSITION); + tid++ << AVDTP_TID_POSITION); tid %= 16; /* Loop for 16*/ hdr->signal_id = sig_id & AVDTP_SIGID_MASK; @@ -104,15 +841,6 @@ static struct net_buf *avdtp_create_pdu(uint8_t msg_type, return buf; } -/* Timeout handler */ -static void avdtp_timeout(struct k_work *work) -{ - LOG_DBG("Failed Signal_id = %d", (AVDTP_KWORK(work))->sig); - - /* Gracefully Disconnect the Signalling and streaming L2cap chann*/ - -} - /* L2CAP Interface callbacks */ void bt_avdtp_l2cap_connected(struct bt_l2cap_chan *chan) { @@ -125,9 +853,9 @@ void bt_avdtp_l2cap_connected(struct bt_l2cap_chan *chan) session = AVDTP_CHAN(chan); LOG_DBG("chan %p session %p", chan, session); - /* Init the timer */ - k_work_init_delayable(&session->req->timeout_work, avdtp_timeout); + /* notify a2dp connection result */ + session->ops->connected(session); } void bt_avdtp_l2cap_disconnected(struct bt_l2cap_chan *chan) @@ -136,7 +864,11 @@ void bt_avdtp_l2cap_disconnected(struct bt_l2cap_chan *chan) LOG_DBG("chan %p session %p", chan, session); session->br_chan.chan.conn = NULL; - /* Clear the Pending req if set*/ + session->signalling_l2cap_connected = 0; + /* todo: Clear the Pending req if set*/ + + /* notify a2dp disconnect */ + session->ops->disconnected(session); } void bt_avdtp_l2cap_encrypt_changed(struct bt_l2cap_chan *chan, uint8_t status) @@ -144,11 +876,28 @@ void bt_avdtp_l2cap_encrypt_changed(struct bt_l2cap_chan *chan, uint8_t status) LOG_DBG(""); } +static const struct { + uint8_t sig_id; + void (*func)(struct bt_avdtp *session, struct net_buf *buf, + uint8_t msg_type, uint8_t tid); +} handler[] = { + {BT_AVDTP_DISCOVER, avdtp_discover_handler}, + {BT_AVDTP_GET_CAPABILITIES, avdtp_get_capabilities_handler}, + {BT_AVDTP_SET_CONFIGURATION, avdtp_set_configuration_handler}, + {BT_AVDTP_GET_CONFIGURATION, avdtp_get_configuration_handler}, + {BT_AVDTP_RECONFIGURE, avdtp_re_configure_handler}, + {BT_AVDTP_OPEN, avdtp_open_handler}, + {BT_AVDTP_START, avdtp_start_handler}, + {BT_AVDTP_CLOSE, avdtp_close_handler}, + {BT_AVDTP_SUSPEND, avdtp_suspend_handler}, + {BT_AVDTP_ABORT, avdtp_abort_handler}, +}; + int bt_avdtp_l2cap_recv(struct bt_l2cap_chan *chan, struct net_buf *buf) { struct bt_avdtp_single_sig_hdr *hdr; struct bt_avdtp *session = AVDTP_CHAN(chan); - uint8_t i, msgtype, sigid, tid; + uint8_t i, msgtype, pack_type, sigid, tid; if (buf->len < sizeof(*hdr)) { LOG_ERR("Recvd Wrong AVDTP Header"); @@ -156,11 +905,36 @@ int bt_avdtp_l2cap_recv(struct bt_l2cap_chan *chan, struct net_buf *buf) } hdr = net_buf_pull_mem(buf, sizeof(*hdr)); + pack_type = AVDTP_GET_PKT_TYPE(hdr->hdr); msgtype = AVDTP_GET_MSG_TYPE(hdr->hdr); sigid = AVDTP_GET_SIG_ID(hdr->signal_id); tid = AVDTP_GET_TR_ID(hdr->hdr); - LOG_DBG("msg_type[0x%02x] sig_id[0x%02x] tid[0x%02x]", msgtype, sigid, tid); + LOG_DBG("pack_type[0x%02x] msg_type[0x%02x] sig_id[0x%02x] tid[0x%02x]", + pack_type, msgtype, sigid, tid); + + /* TODO: only support single packet now */ + if (pack_type != BT_AVDTP_PACKET_TYPE_SINGLE) { + if (pack_type == BT_AVDTP_PACKET_TYPE_START) { + struct net_buf *rsp_buf; + int err; + + sigid = net_buf_pull_u8(buf); + rsp_buf = avdtp_create_reply_pdu(BT_AVDTP_REJECT, + BT_AVDTP_PACKET_TYPE_SINGLE, + sigid, tid); + if (!rsp_buf) { + LOG_ERR("Error: No Buff available"); + return 0; + } + err = bt_l2cap_chan_send(&session->br_chan.chan, rsp_buf); + if (err < 0) { + net_buf_unref(rsp_buf); + LOG_ERR("Error:L2CAP send fail - result = %d", err); + } + } + return 0; + } /* validate if there is an outstanding resp expected*/ if (msgtype != BT_AVDTP_CMD) { @@ -170,7 +944,7 @@ int bt_avdtp_l2cap_recv(struct bt_l2cap_chan *chan, struct net_buf *buf) } if (session->req->sig != sigid || - session->req->tid != tid) { + session->req->tid != tid) { LOG_DBG("Peer mismatch resp, expected sig[0x%02x]" "tid[0x%02x]", session->req->sig, session->req->tid); @@ -178,9 +952,14 @@ int bt_avdtp_l2cap_recv(struct bt_l2cap_chan *chan, struct net_buf *buf) } } + if (!session) { + LOG_DBG("Error: Session not valid"); + return 0; + } + for (i = 0U; i < ARRAY_SIZE(handler); i++) { if (sigid == handler[i].sig_id) { - handler[i].func(session, buf, msgtype); + handler[i].func(session, buf, msgtype, tid); return 0; } } @@ -202,11 +981,13 @@ int bt_avdtp_connect(struct bt_conn *conn, struct bt_avdtp *session) return -EINVAL; } + session->signalling_l2cap_connected = 1; + session->br_chan.rx.mtu = BT_L2CAP_RX_MTU; session->br_chan.chan.ops = &ops; session->br_chan.required_sec_level = BT_SECURITY_L2; return bt_l2cap_chan_connect(conn, &session->br_chan.chan, - BT_L2CAP_PSM_AVDTP); + BT_L2CAP_PSM_AVDTP); } int bt_avdtp_disconnect(struct bt_avdtp *session) @@ -217,19 +998,15 @@ int bt_avdtp_disconnect(struct bt_avdtp *session) LOG_DBG("session %p", session); + session->signalling_l2cap_connected = 0; return bt_l2cap_chan_disconnect(&session->br_chan.chan); } int bt_avdtp_l2cap_accept(struct bt_conn *conn, struct bt_l2cap_server *server, - struct bt_l2cap_chan **chan) + struct bt_l2cap_chan **chan) { struct bt_avdtp *session = NULL; int result; - static const struct bt_l2cap_chan_ops ops = { - .connected = bt_avdtp_l2cap_connected, - .disconnected = bt_avdtp_l2cap_disconnected, - .recv = bt_avdtp_l2cap_recv, - }; LOG_DBG("conn %p", conn); /* Get the AVDTP session from upper layer */ @@ -237,9 +1014,35 @@ int bt_avdtp_l2cap_accept(struct bt_conn *conn, struct bt_l2cap_server *server, if (result < 0) { return result; } - session->br_chan.chan.ops = &ops; - session->br_chan.rx.mtu = BT_AVDTP_MAX_MTU; - *chan = &session->br_chan.chan; + + if (session->signalling_l2cap_connected == 0) { + static const struct bt_l2cap_chan_ops ops = { + .connected = bt_avdtp_l2cap_connected, + .disconnected = bt_avdtp_l2cap_disconnected, + .recv = bt_avdtp_l2cap_recv, + }; + session->signalling_l2cap_connected = 1; + session->br_chan.chan.ops = &ops; + session->br_chan.rx.mtu = BT_L2CAP_RX_MTU; + *chan = &session->br_chan.chan; + } else { + /* get the current opening endpoint */ + if (session->current_sep != NULL) { + static const struct bt_l2cap_chan_ops ops = { + .connected = bt_avdtp_media_l2cap_connected, + .disconnected = bt_avdtp_media_l2cap_disconnected, + .recv = bt_avdtp_media_l2cap_recv + }; + session->current_sep->session = session; + session->current_sep->chan.chan.ops = &ops; + session->current_sep->chan.rx.mtu = BT_L2CAP_RX_MTU; + session->current_sep->chan.required_sec_level = + BT_SECURITY_L2; + *chan = &session->current_sep->chan.chan; + session->current_sep = NULL; + } + } + return 0; } @@ -258,27 +1061,27 @@ int bt_avdtp_register(struct bt_avdtp_event_cb *cb) } int bt_avdtp_register_sep(uint8_t media_type, uint8_t role, - struct bt_avdtp_seid_lsep *lsep) + struct bt_avdtp_sep *sep) { LOG_DBG(""); - static uint8_t bt_avdtp_seid = BT_AVDTP_MIN_SEID; + static uint8_t bt_avdtp_sep = BT_AVDTP_MIN_SEID; - if (!lsep) { + if (!sep) { return -EIO; } - if (bt_avdtp_seid == BT_AVDTP_MAX_SEID) { + if (bt_avdtp_sep == BT_AVDTP_MAX_SEID) { return -EIO; } - lsep->sep.id = bt_avdtp_seid++; - lsep->sep.inuse = 0U; - lsep->sep.media_type = media_type; - lsep->sep.tsep = role; + sep->sep_info.id = bt_avdtp_sep++; + sep->sep_info.inuse = 0U; + sep->sep_info.media_type = media_type; + sep->sep_info.tsep = role; + sep->state = AVDTP_IDLE; - lsep->next = lseps; - lseps = lsep; + sys_slist_append(&seps, &sep->_node); return 0; } @@ -306,7 +1109,7 @@ int bt_avdtp_init(void) /* AVDTP Discover Request */ int bt_avdtp_discover(struct bt_avdtp *session, - struct bt_avdtp_discover_params *param) + struct bt_avdtp_discover_params *param) { struct net_buf *buf; @@ -317,14 +1120,264 @@ int bt_avdtp_discover(struct bt_avdtp *session, } buf = avdtp_create_pdu(BT_AVDTP_CMD, - BT_AVDTP_PACKET_TYPE_SINGLE, - BT_AVDTP_DISCOVER); + BT_AVDTP_PACKET_TYPE_SINGLE, + BT_AVDTP_DISCOVER); + if (!buf) { + LOG_ERR("Error: No Buff available"); + return -ENOMEM; + } + + return avdtp_send(session, buf, ¶m->req); +} + +int bt_avdtp_parse_sep(struct net_buf *buf, struct bt_avdtp_sep_info *sep_info) +{ + struct bt_avdtp_sep_data *sep_data; + + if ((sep_info != NULL) && (buf != NULL)) { + if (buf->len >= sizeof(*sep_data)) { + sep_data = net_buf_pull_mem(buf, sizeof(*sep_data)); + sep_info->inuse = sep_data->inuse; + sep_info->id = sep_data->id; + sep_info->tsep = sep_data->tsep; + sep_info->media_type = sep_data->media_type; + return 0; + } + } + + return -EINVAL; +} + +/* AVDTP Get Capabilities Request */ +int bt_avdtp_get_capabilities(struct bt_avdtp *session, + struct bt_avdtp_get_capabilities_params *param) +{ + struct net_buf *buf; + + LOG_DBG(""); + if (!param || !session) { + LOG_DBG("Error: Callback/Session not valid"); + return -EINVAL; + } + + buf = avdtp_create_pdu(BT_AVDTP_CMD, + BT_AVDTP_PACKET_TYPE_SINGLE, + BT_AVDTP_GET_CAPABILITIES); if (!buf) { LOG_ERR("Error: No Buff available"); return -ENOMEM; } /* Body of the message */ + net_buf_add_u8(buf, (param->stream_endpoint_id << 2u)); return avdtp_send(session, buf, ¶m->req); } + +int bt_avdtp_parse_capability_codec(struct net_buf *buf, + uint8_t *codec_type, uint8_t **codec_info_element, + uint16_t *codec_info_element_len) +{ + uint8_t data; + uint8_t length; + + if (!buf) { + LOG_DBG("Error: buf not valid"); + return -EINVAL; + } + + while (buf->len) { + data = net_buf_pull_u8(buf); + switch (data) { + case BT_AVDTP_SERVICE_MEDIA_TRANSPORT: + case BT_AVDTP_SERVICE_REPORTING: + case BT_AVDTP_SERVICE_MEDIA_RECOVERY: + case BT_AVDTP_SERVICE_CONTENT_PROTECTION: + case BT_AVDTP_SERVICE_HEADER_COMPRESSION: + case BT_AVDTP_SERVICE_MULTIPLEXING: + case BT_AVDTP_SERVICE_DELAY_REPORTING: + length = net_buf_pull_u8(buf); + if (length > 0) { + net_buf_pull_mem(buf, length); + } + break; + + case BT_AVDTP_SERVICE_MEDIA_CODEC: + length = net_buf_pull_u8(buf); + if (length > 3) { + data = net_buf_pull_u8(buf); + if (net_buf_tailroom(buf) < (length - 1)) { + return -EINVAL; + } + if (data == BT_AVDTP_AUDIO) { + data = net_buf_pull_u8(buf); + *codec_type = data; + *codec_info_element_len = (length - 2); + *codec_info_element = + net_buf_pull_mem(buf, (*codec_info_element_len)); + return 0; + } + } + break; + + default: + break; + } + } + return -EIO; +} + +static int avdtp_process_configure_command(struct bt_avdtp *session, + uint8_t cmd, + struct bt_avdtp_set_configuration_params *param) +{ + struct net_buf *buf; + + LOG_DBG(""); + if (!param || !session) { + LOG_DBG("Error: Callback/Session not valid"); + return -EINVAL; + } + + buf = avdtp_create_pdu(BT_AVDTP_CMD, + BT_AVDTP_PACKET_TYPE_SINGLE, + cmd); + if (!buf) { + LOG_ERR("Error: No Buff available"); + return -ENOMEM; + } + + /* Body of the message */ + /* ACP Stream Endpoint ID */ + net_buf_add_u8(buf, (param->acp_stream_ep_id << 2u)); + /* INT Stream Endpoint ID */ + net_buf_add_u8(buf, (param->int_stream_endpoint_id << 2u)); + /* Service Category: Media Transport */ + net_buf_add_u8(buf, BT_AVDTP_SERVICE_MEDIA_TRANSPORT); + /* LOSC */ + net_buf_add_u8(buf, 0); + /* Service Category: Media Codec */ + net_buf_add_u8(buf, BT_AVDTP_SERVICE_MEDIA_CODEC); + /* LOSC */ + net_buf_add_u8(buf, param->codec_specific_ie_len + 2); + /* Media Type */ + net_buf_add_u8(buf, param->media_type << 4U); + /* Media Codec Type */ + net_buf_add_u8(buf, param->media_codec_type); + /* Codec Info Element */ + net_buf_add_mem(buf, param->codec_specific_ie, param->codec_specific_ie_len); + + return avdtp_send(session, buf, ¶m->req); +} + +int bt_avdtp_set_configuration(struct bt_avdtp *session, + struct bt_avdtp_set_configuration_params *param) +{ + if (!param || !session || !param->sep) { + LOG_DBG("Error: parameters not valid"); + return -EINVAL; + } + + if (param->sep->state != AVDTP_IDLE) { + return -EINVAL; + } + + return avdtp_process_configure_command(session, BT_AVDTP_SET_CONFIGURATION, param); +} + +int bt_avdtp_reconfigure(struct bt_avdtp *session, + struct bt_avdtp_set_configuration_params *param) +{ + if (!param || !session || !param->sep) { + LOG_DBG("Error: parameters not valid"); + return -EINVAL; + } + + if (param->sep->state != AVDTP_OPEN) { + return -EINVAL; + } + + return avdtp_process_configure_command(session, BT_AVDTP_RECONFIGURE, param); +} + +int bt_avdtp_open(struct bt_avdtp *session, + struct bt_avdtp_open_params *param) +{ + struct net_buf *buf; + + LOG_DBG(""); + if (!param || !session || !param->sep) { + LOG_DBG("Error: parameters not valid"); + return -EINVAL; + } + + if (param->sep->state != AVDTP_CONFIGURED) { + return -EINVAL; + } + + buf = avdtp_create_pdu(BT_AVDTP_CMD, + BT_AVDTP_PACKET_TYPE_SINGLE, + BT_AVDTP_OPEN); + if (!buf) { + LOG_ERR("Error: No Buff available"); + return -ENOMEM; + } + + /* Body of the message */ + /* ACP Stream Endpoint ID */ + net_buf_add_u8(buf, (param->acp_stream_ep_id << 2u)); + + return avdtp_send(session, buf, ¶m->req); +} + +int bt_avdtp_start(struct bt_avdtp *session, + struct bt_avdtp_start_params *param) +{ + struct net_buf *buf; + + LOG_DBG(""); + if (!param || !session || !param->sep) { + LOG_DBG("Error: parameters not valid"); + return -EINVAL; + } + + if (param->sep->state != AVDTP_OPEN) { + return -EINVAL; + } + + buf = avdtp_create_pdu(BT_AVDTP_CMD, + BT_AVDTP_PACKET_TYPE_SINGLE, + BT_AVDTP_START); + if (!buf) { + LOG_ERR("Error: No Buff available"); + return -ENOMEM; + } + + /* Body of the message */ + /* ACP Stream Endpoint ID */ + net_buf_add_u8(buf, (param->acp_stream_ep_id << 2u)); + + return avdtp_send(session, buf, ¶m->req); +} + +int bt_avdtp_send_media_data(struct bt_avdtp_sep *sep, struct net_buf *buf) +{ + int err; + + if (sep->state != AVDTP_STREAMING) { + return -EIO; + } + + err = bt_l2cap_chan_send(&sep->chan.chan, buf); + if (err < 0) { + LOG_ERR("Error:L2CAP send fail - err = %d", err); + return err; + } + + return err; +} + +uint32_t bt_avdtp_get_media_mtu(struct bt_avdtp_sep *sep) +{ + return sep->chan.tx.mtu; +} diff --git a/subsys/bluetooth/host/classic/avdtp_internal.h b/subsys/bluetooth/host/classic/avdtp_internal.h index 5b971fcaac7..dc756cb11a7 100644 --- a/subsys/bluetooth/host/classic/avdtp_internal.h +++ b/subsys/bluetooth/host/classic/avdtp_internal.h @@ -2,6 +2,7 @@ * avdtp_internal.h - avdtp handling * Copyright (c) 2015-2016 Intel Corporation + * Copyright 2021,2024 NXP * * SPDX-License-Identifier: Apache-2.0 */ @@ -82,22 +83,38 @@ #define BT_AVDTP_ERR_UNSUPPORTED_CONFIGURAION 0x29 #define BT_AVDTP_ERR_BAD_STATE 0x31 -#define BT_AVDTP_MAX_MTU BT_L2CAP_RX_MTU - #define BT_AVDTP_MIN_SEID 0x01 #define BT_AVDTP_MAX_SEID 0x3E struct bt_avdtp; struct bt_avdtp_req; +struct bt_avdtp_sep_info; + +/** @brief AVDTP SEID Information AVDTP_SPEC V13 Table 8.8 */ +struct bt_avdtp_sep_data { +#ifdef CONFIG_LITTLE_ENDIAN + uint8_t rfa0:1; + uint8_t inuse:1; + uint8_t id:6; + uint8_t rfa1:3; + uint8_t tsep:1; + uint8_t media_type:4; +#else + uint8_t id:6; + uint8_t inuse:1; + uint8_t rfa0:1; + uint8_t media_type:4; + uint8_t tsep:1; + uint8_t rfa1:3; +#endif +} __packed; -typedef int (*bt_avdtp_func_t)(struct bt_avdtp *session, - struct bt_avdtp_req *req); +typedef int (*bt_avdtp_func_t)(struct bt_avdtp_req *req); struct bt_avdtp_req { uint8_t sig; uint8_t tid; bt_avdtp_func_t func; - struct k_work_delayable timeout_work; }; struct bt_avdtp_single_sig_hdr { @@ -105,47 +122,108 @@ struct bt_avdtp_single_sig_hdr { uint8_t signal_id; } __packed; -#define BT_AVDTP_SIG_HDR_LEN sizeof(struct bt_avdtp_single_sig_hdr) - -struct bt_avdtp_ind_cb { - /* - * discovery_ind; - * get_capabilities_ind; - * set_configuration_ind; - * open_ind; - * start_ind; - * suspend_ind; - * close_ind; - */ +struct bt_avdtp_media_hdr { +#ifdef CONFIG_LITTLE_ENDIAN + uint8_t CSRC_count:4; + uint8_t header_extension:1; + uint8_t padding:1; + uint8_t RTP_version:2; + uint8_t playload_type:7; + uint8_t marker:1; +#else + uint8_t RTP_version:2; + uint8_t padding:1; + uint8_t header_extension:1; + uint8_t CSRC_count:4; + uint8_t marker:1; + uint8_t playload_type:7; +#endif + uint16_t sequence_number; + uint32_t time_stamp; + uint32_t synchronization_source; +} __packed; + +struct bt_avdtp_discover_params { + struct bt_avdtp_req req; + uint8_t status; + struct net_buf *buf; +}; + +struct bt_avdtp_get_capabilities_params { + struct bt_avdtp_req req; + uint8_t status; + uint8_t stream_endpoint_id; + struct net_buf *buf; }; -struct bt_avdtp_cap { - uint8_t cat; - uint8_t len; - uint8_t data[0]; +struct bt_avdtp_set_configuration_params { + struct bt_avdtp_req req; + struct bt_avdtp_sep *sep; + uint8_t status; + uint8_t acp_stream_ep_id; + uint8_t int_stream_endpoint_id; + uint8_t media_type; + uint8_t media_codec_type; + uint8_t codec_specific_ie_len; + uint8_t *codec_specific_ie; }; -struct bt_avdtp_sep { - uint8_t seid; - uint8_t len; - struct bt_avdtp_cap caps[0]; +struct bt_avdtp_open_params { + struct bt_avdtp_req req; + struct bt_avdtp_sep *sep; + uint8_t status; + uint8_t acp_stream_ep_id; }; -struct bt_avdtp_discover_params { +struct bt_avdtp_start_params { struct bt_avdtp_req req; + struct bt_avdtp_sep *sep; uint8_t status; - struct bt_avdtp_sep *caps; + uint8_t acp_stream_ep_id; +}; + +struct bt_avdtp_ops_cb { + void (*connected)(struct bt_avdtp *session); + + void (*disconnected)(struct bt_avdtp *session); + + struct net_buf *(*alloc_buf)(struct bt_avdtp *session); + + int (*discovery_ind)(struct bt_avdtp *session, uint8_t *errcode); + + int (*get_capabilities_ind)(struct bt_avdtp *session, + struct bt_avdtp_sep *sep, struct net_buf *rsp_buf, uint8_t *errcode); + + int (*set_configuration_ind)(struct bt_avdtp *session, struct bt_avdtp_sep *sep, + uint8_t int_seid, struct net_buf *buf, uint8_t *errcode); + + int (*open_ind)(struct bt_avdtp *session, + struct bt_avdtp_sep *sep, uint8_t *errcode); + + int (*close_ind)(struct bt_avdtp *session, + struct bt_avdtp_sep *sep, uint8_t *errcode); + + int (*start_ind)(struct bt_avdtp *session, + struct bt_avdtp_sep *sep, uint8_t *errcode); + + int (*suspend_ind)(struct bt_avdtp *session, + struct bt_avdtp_sep *sep, uint8_t *errcode); + + int (*abort_ind)(struct bt_avdtp *session, + struct bt_avdtp_sep *sep, uint8_t *errcode); }; /** @brief Global AVDTP session structure. */ struct bt_avdtp { struct bt_l2cap_br_chan br_chan; - struct bt_avdtp_stream *streams; /* List of AV streams */ struct bt_avdtp_req *req; + const struct bt_avdtp_ops_cb *ops; + struct bt_avdtp_sep *current_sep; + struct k_work_delayable timeout_work; + uint8_t signalling_l2cap_connected; }; struct bt_avdtp_event_cb { - struct bt_avdtp_ind_cb *ind; int (*accept)(struct bt_conn *conn, struct bt_avdtp **session); }; @@ -163,8 +241,41 @@ int bt_avdtp_disconnect(struct bt_avdtp *session); /* AVDTP SEP register function */ int bt_avdtp_register_sep(uint8_t media_type, uint8_t role, - struct bt_avdtp_seid_lsep *sep); + struct bt_avdtp_sep *sep); /* AVDTP Discover Request */ int bt_avdtp_discover(struct bt_avdtp *session, struct bt_avdtp_discover_params *param); + +/* Parse the sep of discovered result */ +int bt_avdtp_parse_sep(struct net_buf *buf, struct bt_avdtp_sep_info *sep_info); + +/* AVDTP Get Capabilities */ +int bt_avdtp_get_capabilities(struct bt_avdtp *session, + struct bt_avdtp_get_capabilities_params *param); + +/* Parse the codec type of capabilities */ +int bt_avdtp_parse_capability_codec(struct net_buf *buf, uint8_t *codec_type, + uint8_t **codec_info_element, uint16_t *codec_info_element_len); + +/* AVDTP Set Configuration */ +int bt_avdtp_set_configuration(struct bt_avdtp *session, + struct bt_avdtp_set_configuration_params *param); + +/* AVDTP reconfigure */ +int bt_avdtp_reconfigure(struct bt_avdtp *session, + struct bt_avdtp_set_configuration_params *param); + +/* AVDTP OPEN */ +int bt_avdtp_open(struct bt_avdtp *session, + struct bt_avdtp_open_params *param); + +/* AVDTP START */ +int bt_avdtp_start(struct bt_avdtp *session, + struct bt_avdtp_start_params *param); + +/* AVDTP send data */ +int bt_avdtp_send_media_data(struct bt_avdtp_sep *sep, struct net_buf *buf); + +/* get media l2cap connection MTU */ +uint32_t bt_avdtp_get_media_mtu(struct bt_avdtp_sep *sep);