Browse Source

Bluetooth: A2DP: Implement the a2dp and avdtp

implement a2dp.c and avdtp.c
add a2dp related Kconfig:
BT_AVDTP_RTP_VERSION, BT_A2DP_SOURCE and BT_A2DP_SINK
a2dp_codec_sbc.c/h are used to provide some APIs to get
A2DP SBC codec information. (like: channel num).

Signed-off-by: Mark Wang <yichang.wang@nxp.com>
pull/73011/head
Mark Wang 1 year ago committed by Alberto Escolar
parent
commit
bdca41d0bf
  1. 115
      include/zephyr/bluetooth/classic/a2dp-codec.h
  2. 762
      include/zephyr/bluetooth/classic/a2dp.h
  3. 124
      include/zephyr/bluetooth/classic/a2dp_codec_sbc.h
  4. 142
      include/zephyr/bluetooth/classic/avdtp.h
  5. 2
      subsys/bluetooth/host/classic/CMakeLists.txt
  6. 24
      subsys/bluetooth/host/classic/Kconfig
  7. 818
      subsys/bluetooth/host/classic/a2dp.c
  8. 51
      subsys/bluetooth/host/classic/a2dp_codec_sbc.c
  9. 1157
      subsys/bluetooth/host/classic/avdtp.c
  10. 171
      subsys/bluetooth/host/classic/avdtp_internal.h

115
include/zephyr/bluetooth/classic/a2dp-codec.h

@ -1,115 +0,0 @@ @@ -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 <stdint.h>
#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_ */

762
include/zephyr/bluetooth/classic/a2dp.h

@ -4,6 +4,7 @@ @@ -4,6 +4,7 @@
/*
* Copyright (c) 2015-2016 Intel Corporation
* Copyright 2024 NXP
*
* SPDX-License-Identifier: Apache-2.0
*/
@ -12,19 +13,262 @@ @@ -12,19 +13,262 @@
#include <stdint.h>
#include <zephyr/bluetooth/bluetooth.h>
#include <zephyr/bluetooth/l2cap.h>
#include <zephyr/bluetooth/classic/avdtp.h>
#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 { @@ -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; @@ -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
}

124
include/zephyr/bluetooth/classic/a2dp_codec_sbc.h

@ -0,0 +1,124 @@ @@ -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_ */

142
include/zephyr/bluetooth/classic/avdtp.h

@ -4,49 +4,137 @@ @@ -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 <zephyr/bluetooth/l2cap.h>
#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

2
subsys/bluetooth/host/classic/CMakeLists.txt

@ -3,7 +3,7 @@ @@ -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)

24
subsys/bluetooth/host/classic/Kconfig

@ -154,6 +154,30 @@ config BT_A2DP @@ -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

818
subsys/bluetooth/host/classic/a2dp.c

@ -4,6 +4,7 @@ @@ -4,6 +4,7 @@
/*
* Copyright (c) 2015-2016 Intel Corporation
* Copyright 2021,2024 NXP
*
* SPDX-License-Identifier: Apache-2.0
*/
@ -19,12 +20,31 @@ @@ -19,12 +20,31 @@
#include <zephyr/bluetooth/bluetooth.h>
#include <zephyr/bluetooth/l2cap.h>
#include <zephyr/bluetooth/classic/avdtp.h>
#include <zephyr/bluetooth/classic/a2dp_codec_sbc.h>
#include <zephyr/bluetooth/classic/a2dp.h>
#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); @@ -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) @@ -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) @@ -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) @@ -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;
}

51
subsys/bluetooth/host/classic/a2dp_codec_sbc.c

@ -0,0 +1,51 @@ @@ -0,0 +1,51 @@
/*
* Copyright 2024 NXP
*
* SPDX-License-Identifier: Apache-2.0
*/
#include <zephyr/kernel.h>
#include <string.h>
#include <errno.h>
#include <zephyr/sys/atomic.h>
#include <zephyr/sys/byteorder.h>
#include <zephyr/sys/util.h>
#include <zephyr/sys/printk.h>
#include <zephyr/bluetooth/classic/a2dp_codec_sbc.h>
#include <zephyr/bluetooth/classic/a2dp.h>
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;
}
}

1157
subsys/bluetooth/host/classic/avdtp.c

File diff suppressed because it is too large Load Diff

171
subsys/bluetooth/host/classic/avdtp_internal.h

@ -2,6 +2,7 @@ @@ -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 @@ @@ -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 { @@ -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); @@ -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);

Loading…
Cancel
Save