Bluetooth: A2DP: implement close, suspend and abort

implement avdtp close, suspend and abort and the a2dp
interfaces.

Signed-off-by: Mark Wang <yichang.wang@nxp.com>
This commit is contained in:
Mark Wang 2024-09-29 11:38:37 +08:00 committed by Benjamin Cabé
parent 6a6f2c2fca
commit 70c73aa317
4 changed files with 353 additions and 21 deletions

View file

@ -545,6 +545,28 @@ struct bt_a2dp_cb {
* bt_a2dp_err_code or bt_avdtp_err_code * bt_a2dp_err_code or bt_avdtp_err_code
*/ */
void (*suspend_rsp)(struct bt_a2dp_stream *stream, uint8_t rsp_err_code); void (*suspend_rsp)(struct bt_a2dp_stream *stream, uint8_t rsp_err_code);
/**
* @brief Stream abort request callback
*
* The callback is called whenever an stream is requested to be
* aborted.
*
* @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 (*abort_req)(struct bt_a2dp_stream *stream, uint8_t *rsp_err_code);
/** @brief Callback function for bt_a2dp_stream_abort()
*
* Called when the abort operation is completed.
*
* @param[in] stream Pointer to stream object.
* @param[in] rsp_err_code the remote responded error code
* bt_a2dp_err_code or bt_avdtp_err_code
*/
void (*abort_rsp)(struct bt_a2dp_stream *stream, uint8_t rsp_err_code);
}; };
/** @brief A2DP Connect. /** @brief A2DP Connect.
@ -662,6 +684,15 @@ struct bt_a2dp_stream_ops {
* @param stream Stream object that has been suspended. * @param stream Stream object that has been suspended.
*/ */
void (*suspended)(struct bt_a2dp_stream *stream); void (*suspended)(struct bt_a2dp_stream *stream);
/**
* @brief Stream abort callback
*
* The callback is called whenever an Audio Stream has been aborted.
* After aborted, the stream becomes invalid.
*
* @param stream Stream object that has been aborted.
*/
void (*aborted)(struct bt_a2dp_stream *stream);
#if defined(CONFIG_BT_A2DP_SINK) #if defined(CONFIG_BT_A2DP_SINK)
/** @brief the media streaming data, only for sink /** @brief the media streaming data, only for sink
* *
@ -770,6 +801,17 @@ int bt_a2dp_stream_suspend(struct bt_a2dp_stream *stream);
*/ */
int bt_a2dp_stream_reconfig(struct bt_a2dp_stream *stream, struct bt_a2dp_codec_cfg *config); int bt_a2dp_stream_reconfig(struct bt_a2dp_stream *stream, struct bt_a2dp_codec_cfg *config);
/** @brief abort a2dp streamer.
*
* This function sends the AVDTP_ABORT command.
* After abort, the stream becomes invalid.
*
* @param stream The stream object.
*
* @return 0 in case of success and error code in case of error.
*/
int bt_a2dp_stream_abort(struct bt_a2dp_stream *stream);
/** @brief get the stream l2cap mtu /** @brief get the stream l2cap mtu
* *
* @param stream The stream object. * @param stream The stream object.

View file

@ -351,6 +351,44 @@ static int a2dp_start_ind(struct bt_avdtp *session, struct bt_avdtp_sep *sep, ui
return a2dp_ctrl_ind(session, sep, errcode, req_cb, done_cb, false); return a2dp_ctrl_ind(session, sep, errcode, req_cb, done_cb, false);
} }
static int a2dp_suspend_ind(struct bt_avdtp *session, struct bt_avdtp_sep *sep, uint8_t *errcode)
{
struct bt_a2dp_ep *ep = CONTAINER_OF(sep, struct bt_a2dp_ep, sep);
bt_a2dp_ctrl_req_cb req_cb;
bt_a2dp_ctrl_done_cb done_cb;
__ASSERT(sep, "Invalid sep");
req_cb = a2dp_cb != NULL ? a2dp_cb->suspend_req : NULL;
done_cb =
(ep->stream != NULL && ep->stream->ops != NULL) ? ep->stream->ops->suspended : NULL;
return a2dp_ctrl_ind(session, sep, errcode, req_cb, done_cb, false);
}
static int a2dp_close_ind(struct bt_avdtp *session, struct bt_avdtp_sep *sep, uint8_t *errcode)
{
struct bt_a2dp_ep *ep = CONTAINER_OF(sep, struct bt_a2dp_ep, sep);
bt_a2dp_ctrl_req_cb req_cb;
bt_a2dp_ctrl_done_cb done_cb;
__ASSERT(sep, "Invalid sep");
req_cb = a2dp_cb != NULL ? a2dp_cb->release_req : NULL;
done_cb =
(ep->stream != NULL && ep->stream->ops != NULL) ? ep->stream->ops->released : NULL;
return a2dp_ctrl_ind(session, sep, errcode, req_cb, done_cb, true);
}
static int a2dp_abort_ind(struct bt_avdtp *session, struct bt_avdtp_sep *sep, uint8_t *errcode)
{
struct bt_a2dp_ep *ep = CONTAINER_OF(sep, struct bt_a2dp_ep, sep);
bt_a2dp_ctrl_req_cb req_cb;
bt_a2dp_ctrl_done_cb done_cb;
__ASSERT(sep, "Invalid sep");
req_cb = a2dp_cb != NULL ? a2dp_cb->abort_req : NULL;
done_cb = (ep->stream != NULL && ep->stream->ops != NULL) ? ep->stream->ops->aborted : NULL;
return a2dp_ctrl_ind(session, sep, errcode, req_cb, done_cb, true);
}
static int bt_a2dp_set_config_cb(struct bt_avdtp_req *req) 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 *a2dp = SET_CONF_PARAM(SET_CONF_REQ(req));
@ -656,6 +694,36 @@ static int bt_a2dp_start_cb(struct bt_avdtp_req *req)
return bt_a2dp_ctrl_cb(req, rsp_cb, done_cb, false); return bt_a2dp_ctrl_cb(req, rsp_cb, done_cb, false);
} }
static int bt_a2dp_suspend_cb(struct bt_avdtp_req *req)
{
struct bt_a2dp_ep *ep = CONTAINER_OF(CTRL_REQ(req)->sep, struct bt_a2dp_ep, sep);
bt_a2dp_rsp_cb rsp_cb = a2dp_cb != NULL ? a2dp_cb->suspend_rsp : NULL;
bt_a2dp_done_cb done_cb =
(ep->stream != NULL && ep->stream->ops != NULL) ? ep->stream->ops->suspended : NULL;
return bt_a2dp_ctrl_cb(req, rsp_cb, done_cb, false);
}
static int bt_a2dp_close_cb(struct bt_avdtp_req *req)
{
struct bt_a2dp_ep *ep = CONTAINER_OF(CTRL_REQ(req)->sep, struct bt_a2dp_ep, sep);
bt_a2dp_rsp_cb rsp_cb = a2dp_cb != NULL ? a2dp_cb->release_rsp : NULL;
bt_a2dp_done_cb done_cb =
(ep->stream != NULL && ep->stream->ops != NULL) ? ep->stream->ops->released : NULL;
return bt_a2dp_ctrl_cb(req, rsp_cb, done_cb, true);
}
static int bt_a2dp_abort_cb(struct bt_avdtp_req *req)
{
struct bt_a2dp_ep *ep = CONTAINER_OF(CTRL_REQ(req)->sep, struct bt_a2dp_ep, sep);
bt_a2dp_rsp_cb rsp_cb = a2dp_cb != NULL ? a2dp_cb->abort_rsp : NULL;
bt_a2dp_done_cb done_cb =
(ep->stream != NULL && ep->stream->ops != NULL) ? ep->stream->ops->aborted : NULL;
return bt_a2dp_ctrl_cb(req, rsp_cb, done_cb, true);
}
static int bt_a2dp_stream_ctrl_pre(struct bt_a2dp_stream *stream, bt_avdtp_func_t cb) static int bt_a2dp_stream_ctrl_pre(struct bt_a2dp_stream *stream, bt_avdtp_func_t cb)
{ {
struct bt_a2dp *a2dp; struct bt_a2dp *a2dp;
@ -685,6 +753,18 @@ int bt_a2dp_stream_establish(struct bt_a2dp_stream *stream)
return bt_avdtp_open(&a2dp->session, &a2dp->ctrl_param); return bt_avdtp_open(&a2dp->session, &a2dp->ctrl_param);
} }
int bt_a2dp_stream_release(struct bt_a2dp_stream *stream)
{
int err;
struct bt_a2dp *a2dp = stream->a2dp;
err = bt_a2dp_stream_ctrl_pre(stream, bt_a2dp_close_cb);
if (err) {
return err;
}
return bt_avdtp_close(&a2dp->session, &a2dp->ctrl_param);
}
int bt_a2dp_stream_start(struct bt_a2dp_stream *stream) int bt_a2dp_stream_start(struct bt_a2dp_stream *stream)
{ {
int err; int err;
@ -697,6 +777,30 @@ int bt_a2dp_stream_start(struct bt_a2dp_stream *stream)
return bt_avdtp_start(&a2dp->session, &a2dp->ctrl_param); return bt_avdtp_start(&a2dp->session, &a2dp->ctrl_param);
} }
int bt_a2dp_stream_suspend(struct bt_a2dp_stream *stream)
{
int err;
struct bt_a2dp *a2dp = stream->a2dp;
err = bt_a2dp_stream_ctrl_pre(stream, bt_a2dp_suspend_cb);
if (err) {
return err;
}
return bt_avdtp_suspend(&a2dp->session, &a2dp->ctrl_param);
}
int bt_a2dp_stream_abort(struct bt_a2dp_stream *stream)
{
int err;
struct bt_a2dp *a2dp = stream->a2dp;
err = bt_a2dp_stream_ctrl_pre(stream, bt_a2dp_abort_cb);
if (err) {
return err;
}
return bt_avdtp_abort(&a2dp->session, &a2dp->ctrl_param);
}
int bt_a2dp_stream_reconfig(struct bt_a2dp_stream *stream, struct bt_a2dp_codec_cfg *config) int bt_a2dp_stream_reconfig(struct bt_a2dp_stream *stream, struct bt_a2dp_codec_cfg *config)
{ {
uint8_t remote_id; uint8_t remote_id;
@ -749,6 +853,30 @@ int bt_a2dp_stream_send(struct bt_a2dp_stream *stream, struct net_buf *buf, uint
} }
#endif #endif
int a2dp_stream_l2cap_disconnected(struct bt_avdtp *session, struct bt_avdtp_sep *sep)
{
struct bt_a2dp_ep *ep;
__ASSERT(sep, "Invalid sep");
ep = CONTAINER_OF(sep, struct bt_a2dp_ep, sep);
if (ep->stream != NULL) {
struct bt_a2dp_stream_ops *ops;
struct bt_a2dp_stream *stream = ep->stream;
ops = stream->ops;
/* Many places set ep->stream as NULL like abort and close.
* it should be OK without lock protection because
* all the related callbacks are in the same zephyr task context.
*/
ep->stream = NULL;
if ((ops != NULL) && (ops->released != NULL)) {
ops->released(stream);
}
}
return 0;
}
static const struct bt_avdtp_ops_cb signaling_avdtp_ops = { static const struct bt_avdtp_ops_cb signaling_avdtp_ops = {
.connected = a2dp_connected, .connected = a2dp_connected,
.disconnected = a2dp_disconnected, .disconnected = a2dp_disconnected,
@ -759,6 +887,10 @@ static const struct bt_avdtp_ops_cb signaling_avdtp_ops = {
.re_configuration_ind = a2dp_re_config_ind, .re_configuration_ind = a2dp_re_config_ind,
.open_ind = a2dp_open_ind, .open_ind = a2dp_open_ind,
.start_ind = a2dp_start_ind, .start_ind = a2dp_start_ind,
.close_ind = a2dp_close_ind,
.suspend_ind = a2dp_suspend_ind,
.abort_ind = a2dp_abort_ind,
.stream_l2cap_disconnected = a2dp_stream_l2cap_disconnected,
}; };
int a2dp_accept(struct bt_conn *conn, struct bt_avdtp **session) int a2dp_accept(struct bt_conn *conn, struct bt_avdtp **session)

View file

@ -145,12 +145,36 @@ void bt_avdtp_media_l2cap_connected(struct bt_l2cap_chan *chan)
void bt_avdtp_media_l2cap_disconnected(struct bt_l2cap_chan *chan) void bt_avdtp_media_l2cap_disconnected(struct bt_l2cap_chan *chan)
{ {
struct bt_avdtp *session;
struct bt_avdtp_sep *sep = CONTAINER_OF(chan, struct bt_avdtp_sep, chan.chan); struct bt_avdtp_sep *sep = CONTAINER_OF(chan, struct bt_avdtp_sep, chan.chan);
session = sep->session;
if (session == NULL) {
return;
}
LOG_DBG("chan %p", chan); LOG_DBG("chan %p", chan);
chan->conn = NULL; chan->conn = NULL;
if (sep->state > AVDTP_OPENING) { avdtp_sep_lock(sep);
sep->state = AVDTP_OPENING; if ((sep->state == AVDTP_CLOSING) && (session->req != NULL) &&
(session->req->sig == BT_AVDTP_CLOSE)) {
/* closing the stream */
struct bt_avdtp_req *req = session->req;
bt_avdtp_set_state(sep, AVDTP_IDLE);
avdtp_sep_unlock(sep);
req->status = 0;
bt_avdtp_clear_req(session);
if (req->func != NULL) {
req->func(req);
}
} else if (sep->state > AVDTP_OPENING) {
bt_avdtp_set_state(sep, AVDTP_IDLE);
avdtp_sep_unlock(sep);
/* the l2cap is disconnected by other unexpected reasons */
session->ops->stream_l2cap_disconnected(session, sep);
} else {
avdtp_sep_unlock(sep);
} }
} }
@ -186,6 +210,15 @@ static int avdtp_media_connect(struct bt_avdtp *session, struct bt_avdtp_sep *se
BT_L2CAP_PSM_AVDTP); BT_L2CAP_PSM_AVDTP);
} }
static int avdtp_media_disconnect(struct bt_avdtp_sep *sep)
{
if (sep == NULL || sep->chan.chan.conn == NULL || sep->chan.chan.ops == NULL) {
return -EINVAL;
}
return bt_l2cap_chan_disconnect(&sep->chan.chan);
}
static struct net_buf *avdtp_create_reply_pdu(uint8_t msg_type, uint8_t pkt_type, uint8_t sig_id, static struct net_buf *avdtp_create_reply_pdu(uint8_t msg_type, uint8_t pkt_type, uint8_t sig_id,
uint8_t tid) uint8_t tid)
{ {
@ -680,15 +713,17 @@ static void avdtp_close_handler(struct bt_avdtp *session, struct net_buf *buf, u
{ {
if (msg_type == BT_AVDTP_CMD) { if (msg_type == BT_AVDTP_CMD) {
int err = 0; int err = 0;
int ret;
struct bt_avdtp_sep *sep; struct bt_avdtp_sep *sep;
struct net_buf *rsp_buf; struct net_buf *rsp_buf;
uint8_t error_code = 0; uint8_t error_code = 0;
sep = avdtp_get_sep(net_buf_pull_u8(buf) >> 2); sep = avdtp_get_cmd_sep(buf);
avdtp_sep_lock(sep);
if ((sep == NULL) || (session->ops->close_ind == NULL)) { if ((sep == NULL) || (session->ops->close_ind == NULL)) {
err = -ENOTSUP; err = -ENOTSUP;
} else { } else {
if (sep->state != AVDTP_OPEN) { if (!(sep->state & (AVDTP_OPEN | AVDTP_STREAMING))) {
err = -ENOTSUP; err = -ENOTSUP;
error_code = BT_AVDTP_BAD_STATE; error_code = BT_AVDTP_BAD_STATE;
} else { } else {
@ -699,6 +734,7 @@ static void avdtp_close_handler(struct bt_avdtp *session, struct net_buf *buf, u
rsp_buf = avdtp_create_reply_pdu(err ? BT_AVDTP_REJECT : BT_AVDTP_ACCEPT, rsp_buf = avdtp_create_reply_pdu(err ? BT_AVDTP_REJECT : BT_AVDTP_ACCEPT,
BT_AVDTP_PACKET_TYPE_SINGLE, BT_AVDTP_CLOSE, tid); BT_AVDTP_PACKET_TYPE_SINGLE, BT_AVDTP_CLOSE, tid);
if (!rsp_buf) { if (!rsp_buf) {
avdtp_sep_unlock(sep);
return; return;
} }
@ -709,16 +745,36 @@ static void avdtp_close_handler(struct bt_avdtp *session, struct net_buf *buf, u
LOG_DBG("close err code:%d", error_code); LOG_DBG("close err code:%d", error_code);
net_buf_add_u8(rsp_buf, error_code); net_buf_add_u8(rsp_buf, error_code);
} else { } else {
sep->state = AVDTP_CONFIGURED; bt_avdtp_set_state(sep, AVDTP_CLOSING);
sep->sep_info.inuse = 0u;
} }
err = bt_l2cap_chan_send(&session->br_chan.chan, rsp_buf); ret = bt_l2cap_chan_send(&session->br_chan.chan, rsp_buf);
if (err < 0) { if (ret) {
net_buf_unref(rsp_buf); net_buf_unref(rsp_buf);
LOG_ERR("Error:L2CAP send fail - result = %d", err); LOG_ERR("Error:L2CAP send fail - result = %d", ret);
}
if (!err && !ret) {
bt_avdtp_set_state(sep, AVDTP_IDLE);
}
avdtp_sep_unlock(sep);
} else {
struct bt_avdtp_req *req = session->req;
if (req == NULL) {
return; return;
} }
k_work_cancel_delayable(&session->timeout_work);
avdtp_set_status(req, buf, msg_type);
if (msg_type == BT_AVDTP_ACCEPT) {
bt_avdtp_set_state_lock(CTRL_REQ(req)->sep, AVDTP_CLOSING);
if (!avdtp_media_disconnect(CTRL_REQ(req)->sep)) {
return;
}
}
bt_avdtp_clear_req(session);
if (req->func != NULL) {
req->func(req);
}
} }
} }
@ -727,11 +783,13 @@ static void avdtp_suspend_handler(struct bt_avdtp *session, struct net_buf *buf,
{ {
if (msg_type == BT_AVDTP_CMD) { if (msg_type == BT_AVDTP_CMD) {
int err = 0; int err = 0;
int ret;
struct bt_avdtp_sep *sep; struct bt_avdtp_sep *sep;
struct net_buf *rsp_buf; struct net_buf *rsp_buf;
uint8_t error_code = 0; uint8_t error_code = 0;
sep = avdtp_get_sep(net_buf_pull_u8(buf) >> 2); sep = avdtp_get_cmd_sep(buf);
avdtp_sep_lock(sep);
if ((sep == NULL) || (session->ops->suspend_ind == NULL)) { if ((sep == NULL) || (session->ops->suspend_ind == NULL)) {
err = -ENOTSUP; err = -ENOTSUP;
} else { } else {
@ -747,6 +805,7 @@ static void avdtp_suspend_handler(struct bt_avdtp *session, struct net_buf *buf,
avdtp_create_reply_pdu(err ? BT_AVDTP_REJECT : BT_AVDTP_ACCEPT, avdtp_create_reply_pdu(err ? BT_AVDTP_REJECT : BT_AVDTP_ACCEPT,
BT_AVDTP_PACKET_TYPE_SINGLE, BT_AVDTP_SUSPEND, tid); BT_AVDTP_PACKET_TYPE_SINGLE, BT_AVDTP_SUSPEND, tid);
if (!rsp_buf) { if (!rsp_buf) {
avdtp_sep_unlock(sep);
return; return;
} }
@ -756,16 +815,41 @@ static void avdtp_suspend_handler(struct bt_avdtp *session, struct net_buf *buf,
} }
LOG_DBG("suspend err code:%d", error_code); LOG_DBG("suspend err code:%d", error_code);
net_buf_add_u8(rsp_buf, 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); ret = bt_l2cap_chan_send(&session->br_chan.chan, rsp_buf);
if (err < 0) { if (ret) {
net_buf_unref(rsp_buf); net_buf_unref(rsp_buf);
LOG_ERR("Error:L2CAP send fail - result = %d", err); LOG_ERR("Error:L2CAP send fail - result = %d", ret);
}
if (!err && !ret) {
bt_avdtp_set_state(sep, AVDTP_OPEN);
}
avdtp_sep_unlock(sep);
} else {
struct bt_avdtp_req *req = session->req;
if (req == NULL) {
return; return;
} }
k_work_cancel_delayable(&session->timeout_work);
if (msg_type == BT_AVDTP_ACCEPT) {
bt_avdtp_set_state_lock(CTRL_REQ(req)->sep, AVDTP_OPEN);
} else if (msg_type == BT_AVDTP_REJECT) {
if (buf->len >= 1U) {
uint8_t acp_seid;
acp_seid = net_buf_pull_u8(buf);
if (acp_seid != CTRL_REQ(req)->acp_stream_ep_id) {
return;
}
}
}
avdtp_set_status(req, buf, msg_type);
bt_avdtp_clear_req(session);
if (req->func != NULL) {
req->func(req);
}
} }
} }
@ -774,20 +858,24 @@ static void avdtp_abort_handler(struct bt_avdtp *session, struct net_buf *buf, u
{ {
if (msg_type == BT_AVDTP_CMD) { if (msg_type == BT_AVDTP_CMD) {
int err = 0; int err = 0;
int ret;
struct bt_avdtp_sep *sep; struct bt_avdtp_sep *sep;
struct net_buf *rsp_buf; struct net_buf *rsp_buf;
uint8_t error_code = 0; uint8_t error_code = 0;
sep = avdtp_get_sep(net_buf_pull_u8(buf) >> 2); sep = avdtp_get_cmd_sep(buf);
avdtp_sep_lock(sep);
if ((sep == NULL) || (session->ops->abort_ind == NULL)) { if ((sep == NULL) || (session->ops->abort_ind == NULL)) {
err = -ENOTSUP; err = -ENOTSUP;
} else { } else {
/* all current sep state is OK for abort operation */
err = session->ops->abort_ind(session, sep, &error_code); err = session->ops->abort_ind(session, sep, &error_code);
} }
rsp_buf = avdtp_create_reply_pdu(err ? BT_AVDTP_REJECT : BT_AVDTP_ACCEPT, rsp_buf = avdtp_create_reply_pdu(err ? BT_AVDTP_REJECT : BT_AVDTP_ACCEPT,
BT_AVDTP_PACKET_TYPE_SINGLE, BT_AVDTP_ABORT, tid); BT_AVDTP_PACKET_TYPE_SINGLE, BT_AVDTP_ABORT, tid);
if (!rsp_buf) { if (!rsp_buf) {
avdtp_sep_unlock(sep);
return; return;
} }
@ -797,16 +885,57 @@ static void avdtp_abort_handler(struct bt_avdtp *session, struct net_buf *buf, u
} }
LOG_DBG("abort err code:%d", error_code); LOG_DBG("abort err code:%d", error_code);
net_buf_add_u8(rsp_buf, 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); ret = bt_l2cap_chan_send(&session->br_chan.chan, rsp_buf);
if (err < 0) { if (ret) {
net_buf_unref(rsp_buf); net_buf_unref(rsp_buf);
LOG_ERR("Error:L2CAP send fail - result = %d", err); LOG_ERR("Error:L2CAP send fail - result = %d", ret);
}
if (!err && !ret) {
if ((sep->state & (AVDTP_OPEN | AVDTP_STREAMING)) &&
(sep->chan.state == BT_L2CAP_CONNECTED)) {
bt_avdtp_set_state(sep, AVDTP_ABORTING);
} else {
bt_avdtp_set_state(sep, AVDTP_IDLE);
}
}
avdtp_sep_unlock(sep);
} else {
struct bt_avdtp_req *req = session->req;
if (req == NULL) {
return; return;
} }
k_work_cancel_delayable(&session->timeout_work);
if (msg_type == BT_AVDTP_ACCEPT) {
uint8_t pre_state = CTRL_REQ(req)->sep->state;
bt_avdtp_set_state_lock(CTRL_REQ(req)->sep, AVDTP_ABORTING);
/* release stream */
if (pre_state & (AVDTP_OPEN | AVDTP_STREAMING)) {
avdtp_media_disconnect(CTRL_REQ(req)->sep);
}
/* For abort, make sure the state revert to IDLE state after
* releasing l2cap channel.
*/
bt_avdtp_set_state_lock(CTRL_REQ(req)->sep, AVDTP_IDLE);
} else if (msg_type == BT_AVDTP_REJECT) {
if (buf->len >= 1U) {
uint8_t acp_seid;
acp_seid = net_buf_pull_u8(buf);
if (acp_seid != CTRL_REQ(req)->acp_stream_ep_id) {
return;
}
}
}
avdtp_set_status(req, buf, msg_type);
bt_avdtp_clear_req(session);
if (req->func != NULL) {
req->func(req);
}
} }
} }
@ -1402,6 +1531,11 @@ int bt_avdtp_open(struct bt_avdtp *session, struct bt_avdtp_ctrl_params *param)
return bt_avdtp_ctrl(session, param, BT_AVDTP_OPEN, AVDTP_CONFIGURED); return bt_avdtp_ctrl(session, param, BT_AVDTP_OPEN, AVDTP_CONFIGURED);
} }
int bt_avdtp_close(struct bt_avdtp *session, struct bt_avdtp_ctrl_params *param)
{
return bt_avdtp_ctrl(session, param, BT_AVDTP_CLOSE, AVDTP_OPEN | AVDTP_STREAMING);
}
int bt_avdtp_start(struct bt_avdtp *session, struct bt_avdtp_ctrl_params *param) int bt_avdtp_start(struct bt_avdtp *session, struct bt_avdtp_ctrl_params *param)
{ {
int err; int err;
@ -1414,6 +1548,18 @@ int bt_avdtp_start(struct bt_avdtp *session, struct bt_avdtp_ctrl_params *param)
return err; return err;
} }
int bt_avdtp_suspend(struct bt_avdtp *session, struct bt_avdtp_ctrl_params *param)
{
return bt_avdtp_ctrl(session, param, BT_AVDTP_SUSPEND, AVDTP_STREAMING);
}
int bt_avdtp_abort(struct bt_avdtp *session, struct bt_avdtp_ctrl_params *param)
{
return bt_avdtp_ctrl(session, param, BT_AVDTP_ABORT,
AVDTP_CONFIGURED | AVDTP_OPENING | AVDTP_OPEN | AVDTP_STREAMING |
AVDTP_CLOSING);
}
int bt_avdtp_send_media_data(struct bt_avdtp_sep *sep, struct net_buf *buf) int bt_avdtp_send_media_data(struct bt_avdtp_sep *sep, struct net_buf *buf)
{ {
int err; int err;

View file

@ -202,6 +202,9 @@ struct bt_avdtp_ops_cb {
int (*suspend_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); int (*abort_ind)(struct bt_avdtp *session, struct bt_avdtp_sep *sep, uint8_t *errcode);
/* stream l2cap is closed */
int (*stream_l2cap_disconnected)(struct bt_avdtp *session, struct bt_avdtp_sep *sep);
}; };
/** @brief Global AVDTP session structure. */ /** @brief Global AVDTP session structure. */
@ -258,9 +261,18 @@ int bt_avdtp_reconfigure(struct bt_avdtp *session, struct bt_avdtp_set_configura
/* AVDTP OPEN */ /* AVDTP OPEN */
int bt_avdtp_open(struct bt_avdtp *session, struct bt_avdtp_ctrl_params *param); int bt_avdtp_open(struct bt_avdtp *session, struct bt_avdtp_ctrl_params *param);
/* AVDTP CLOSE */
int bt_avdtp_close(struct bt_avdtp *session, struct bt_avdtp_ctrl_params *param);
/* AVDTP START */ /* AVDTP START */
int bt_avdtp_start(struct bt_avdtp *session, struct bt_avdtp_ctrl_params *param); int bt_avdtp_start(struct bt_avdtp *session, struct bt_avdtp_ctrl_params *param);
/* AVDTP SUSPEND */
int bt_avdtp_suspend(struct bt_avdtp *session, struct bt_avdtp_ctrl_params *param);
/* AVDTP ABORT */
int bt_avdtp_abort(struct bt_avdtp *session, struct bt_avdtp_ctrl_params *param);
/* AVDTP send data */ /* AVDTP send data */
int bt_avdtp_send_media_data(struct bt_avdtp_sep *sep, struct net_buf *buf); int bt_avdtp_send_media_data(struct bt_avdtp_sep *sep, struct net_buf *buf);