-
Notifications
You must be signed in to change notification settings - Fork 6.5k
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Bluetooth: AVRCP: connection and disconnection of AVRCP #75637
Open
gzh-terry
wants to merge
10
commits into
zephyrproject-rtos:main
Choose a base branch
from
gzh-terry:fea_avrcp
base: main
Could not load branches
Branch not found: {{ refName }}
Loading
Could not load tags
Nothing to show
Loading
Are you sure you want to change the base?
Some commits from the old base branch may be removed from the timeline,
and old review comments may become outdated.
+1,187
−0
Open
Changes from all commits
Commits
Show all changes
10 commits
Select commit
Hold shift + click to select a range
f5927a9
Bluetooth: AVCTP: Implementation of AVCTP.
d02b8d0
Bluetooth: AVRCP: Implemation of AVRCP.
02601b9
Bluetooth: AVRCP: Add SDP attributes.
e06c43f
Bluetooth: AVRCP: add shell tools for AVRCP functions.
a222d41
Bluetooth: AVCTP: allow to create and send AVCTP message
4dcb840
Bluetooth: AVRCP: allow to create and send AVRCP unit message
99ec248
Bluetooth: Shell: add command to obtain unit info
dcba86f
Bluetooth: AVCTP: allow to receive an AVCTP message.
8cb6634
Bluetooth : AVRCP: allow to receive an AVRCP message.
d0cfd32
Bluetooth: AVRCP: fix bitfield issue.
File filter
Filter by extension
Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,88 @@ | ||
/** @file | ||
* @brief Audio Video Remote Control Profile header. | ||
*/ | ||
|
||
/* | ||
* Copyright (c) 2015-2016 Intel Corporation | ||
* Copyright (C) 2024 Xiaomi Corporation | ||
* | ||
* SPDX-License-Identifier: Apache-2.0 | ||
*/ | ||
|
||
#ifndef ZEPHYR_INCLUDE_BLUETOOTH_AVRCP_H_ | ||
#define ZEPHYR_INCLUDE_BLUETOOTH_AVRCP_H_ | ||
|
||
#ifdef __cplusplus | ||
extern "C" { | ||
#endif | ||
|
||
/** @brief AVRCP structure */ | ||
struct bt_avrcp; | ||
|
||
struct bt_avrcp_cb { | ||
/** @brief An AVRCP connection has been established. | ||
* | ||
* This callback notifies the application of an avrcp connection, | ||
* i.e., an AVCTP L2CAP connection. | ||
* | ||
* @param avrcp AVRCP connection object. | ||
*/ | ||
void (*connected)(struct bt_avrcp *avrcp); | ||
/** @brief An AVRCP connection has been disconnected. | ||
* | ||
* This callback notifies the application that an avrcp connection | ||
* has been disconnected. | ||
* | ||
* @param avrcp AVRCP connection object. | ||
*/ | ||
void (*disconnected)(struct bt_avrcp *avrcp); | ||
}; | ||
|
||
/** @brief Connect AVRCP. | ||
* | ||
* 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 AVRCP | ||
* connection between devices. | ||
* | ||
* @param conn Pointer to bt_conn structure. | ||
* | ||
* @return pointer to struct bt_avrcp in case of success or NULL in case | ||
* of error. | ||
*/ | ||
struct bt_avrcp *bt_avrcp_connect(struct bt_conn *conn); | ||
|
||
/** @brief Disconnect AVRCP. | ||
* | ||
* This function close AVCTP L2CAP connection. | ||
* | ||
* @param avrcp The AVRCP instance. | ||
* | ||
* @return 0 in case of success or error code in case of error. | ||
*/ | ||
int bt_avrcp_disconnect(struct bt_avrcp *avrcp); | ||
|
||
/** @brief Register callback. | ||
* | ||
* Register AVRCP callbacks to monitor the state and interact with the remote device. | ||
* | ||
* @param cb The callback function. | ||
* | ||
* @return 0 in case of success or error code in case of error. | ||
*/ | ||
int bt_avrcp_register_cb(struct bt_avrcp_cb *cb); | ||
|
||
/** @brief Disconnect AVRCP Unit Info. | ||
* | ||
* This function obtains information that pertains to the unit as a whole. | ||
* | ||
* @param avrcp The AVRCP instance. | ||
* | ||
* @return 0 in case of success or error code in case of error. | ||
*/ | ||
int bt_avrcp_get_unit_info(struct bt_avrcp *avrcp); | ||
|
||
#ifdef __cplusplus | ||
} | ||
#endif | ||
|
||
#endif /* ZEPHYR_INCLUDE_BLUETOOTH_AVRCP_H_ */ |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,221 @@ | ||
/** @file | ||
* @brief Audio Video Control Transport Protocol | ||
*/ | ||
|
||
/* | ||
* Copyright (c) 2015-2016 Intel Corporation | ||
* Copyright (C) 2024 Xiaomi Corporation | ||
* | ||
* SPDX-License-Identifier: Apache-2.0 | ||
*/ | ||
|
||
#include <string.h> | ||
#include <strings.h> | ||
#include <errno.h> | ||
#include <zephyr/sys/atomic.h> | ||
#include <zephyr/sys/byteorder.h> | ||
#include <zephyr/sys/util.h> | ||
|
||
#include <zephyr/bluetooth/hci.h> | ||
#include <zephyr/bluetooth/bluetooth.h> | ||
#include <zephyr/bluetooth/l2cap.h> | ||
#include <zephyr/bluetooth/classic/sdp.h> | ||
|
||
#include "avctp_internal.h" | ||
#include "host/hci_core.h" | ||
#include "host/conn_internal.h" | ||
#include "l2cap_br_internal.h" | ||
|
||
#define LOG_LEVEL CONFIG_BT_AVCTP_LOG_LEVEL | ||
#include <zephyr/logging/log.h> | ||
LOG_MODULE_REGISTER(bt_avctp); | ||
|
||
#define AVCTP_CHAN(_ch) CONTAINER_OF(_ch, struct bt_avctp, br_chan.chan) | ||
|
||
static struct bt_avctp_event_cb *event_cb; | ||
|
||
static void avctp_l2cap_connected(struct bt_l2cap_chan *chan) | ||
{ | ||
struct bt_avctp *session; | ||
|
||
if (!chan) { | ||
LOG_ERR("Invalid AVCTP chan"); | ||
return; | ||
} | ||
|
||
session = AVCTP_CHAN(chan); | ||
LOG_DBG("chan %p session %p", chan, session); | ||
|
||
if (session->ops && session->ops->connected) { | ||
session->ops->connected(session); | ||
} | ||
} | ||
|
||
static void avctp_l2cap_disconnected(struct bt_l2cap_chan *chan) | ||
{ | ||
struct bt_avctp *session = AVCTP_CHAN(chan); | ||
|
||
LOG_DBG("chan %p session %p", chan, session); | ||
session->br_chan.chan.conn = NULL; | ||
|
||
if (session->ops && session->ops->disconnected) { | ||
session->ops->disconnected(session); | ||
} | ||
} | ||
|
||
static void avctp_l2cap_encrypt_changed(struct bt_l2cap_chan *chan, uint8_t status) | ||
{ | ||
LOG_DBG(""); | ||
} | ||
|
||
static int avctp_l2cap_recv(struct bt_l2cap_chan *chan, struct net_buf *buf) | ||
{ | ||
struct net_buf *rsp; | ||
struct bt_avctp *session = AVCTP_CHAN(chan); | ||
struct bt_avctp_header *hdr = (void *)buf->data; | ||
uint8_t tid = BT_AVCTP_HDR_GET_TRANSACTION_LABLE(hdr); | ||
bt_avctp_cr_t cr = BT_AVCTP_HDR_GET_CR(hdr); | ||
|
||
if (buf->len < sizeof(*hdr)) { | ||
LOG_ERR("invalid AVCTP header received"); | ||
return -EINVAL; | ||
} | ||
|
||
switch (hdr->pid) { | ||
#if defined(CONFIG_BT_AVRCP) | ||
case sys_cpu_to_be16(BT_SDP_AV_REMOTE_SVCLASS): | ||
break; | ||
#endif | ||
default: | ||
LOG_ERR("unsupported AVCTP PID received: 0x%04x", sys_be16_to_cpu(hdr->pid)); | ||
if (cr == BT_AVCTP_CMD) { | ||
rsp = bt_avctp_create_pdu(session, BT_AVCTP_RESPONSE, | ||
BT_AVCTP_PKT_TYPE_SINGLE, BT_AVCTP_IPID_INVALID, | ||
&tid, hdr->pid); | ||
if (!rsp) { | ||
return -ENOMEM; | ||
} | ||
return bt_avctp_send(session, rsp); | ||
} | ||
return 0; /* No need to report to the upper layer */ | ||
} | ||
|
||
return session->ops->recv(session, buf); | ||
} | ||
|
||
int bt_avctp_connect(struct bt_conn *conn, struct bt_avctp *session) | ||
{ | ||
static const struct bt_l2cap_chan_ops ops = { | ||
.connected = avctp_l2cap_connected, | ||
.disconnected = avctp_l2cap_disconnected, | ||
.encrypt_change = avctp_l2cap_encrypt_changed, | ||
.recv = avctp_l2cap_recv, | ||
}; | ||
|
||
if (!session) { | ||
return -EINVAL; | ||
} | ||
|
||
session->br_chan.rx.mtu = BT_L2CAP_RX_MTU; | ||
session->br_chan.chan.ops = &ops; | ||
session->br_chan.required_sec_level = BT_SECURITY_L2; | ||
|
||
return bt_l2cap_chan_connect(conn, &session->br_chan.chan, BT_L2CAP_PSM_AVCTP); | ||
} | ||
|
||
int bt_avctp_disconnect(struct bt_avctp *session) | ||
{ | ||
if (!session) { | ||
return -EINVAL; | ||
} | ||
|
||
LOG_DBG("session %p", session); | ||
|
||
return bt_l2cap_chan_disconnect(&session->br_chan.chan); | ||
} | ||
|
||
struct net_buf *bt_avctp_create_pdu(struct bt_avctp *session, bt_avctp_cr_t cr, | ||
bt_avctp_pkt_type_t pkt_type, bt_avctp_ipid_t ipid, | ||
uint8_t *tid, uint16_t pid) | ||
{ | ||
struct net_buf *buf; | ||
struct bt_avctp_header *hdr; | ||
|
||
LOG_DBG(""); | ||
|
||
buf = bt_l2cap_create_pdu(NULL, 0); | ||
if (!buf) { | ||
LOG_ERR("No buff available"); | ||
return buf; | ||
} | ||
|
||
hdr = net_buf_add(buf, sizeof(*hdr)); | ||
BT_AVCTP_HDR_SET_TRANSACTION_LABLE(hdr, *tid); | ||
BT_AVCTP_HDR_SET_PACKET_TYPE(hdr, pkt_type); | ||
BT_AVCTP_HDR_SET_CR(hdr, cr); | ||
BT_AVCTP_HDR_SET_IPID(hdr, ipid); | ||
hdr->pid = pid; | ||
|
||
if (cr == BT_AVCTP_CMD) { | ||
*tid = (*tid + 1) & 0x0F; /* Incremented by one */ | ||
} | ||
|
||
LOG_DBG("cr:0x%lX, tid:0x%02lX", BT_AVCTP_HDR_GET_CR(hdr), | ||
BT_AVCTP_HDR_GET_TRANSACTION_LABLE(hdr)); | ||
return buf; | ||
} | ||
|
||
int bt_avctp_send(struct bt_avctp *session, struct net_buf *buf) | ||
{ | ||
int err; | ||
|
||
err = bt_l2cap_chan_send(&session->br_chan.chan, buf); | ||
if (err < 0) { | ||
net_buf_unref(buf); | ||
LOG_ERR("L2CAP send fail err = %d", err); | ||
return err; | ||
} | ||
|
||
return err; | ||
} | ||
|
||
int bt_avctp_register(struct bt_avctp_event_cb *cb) | ||
{ | ||
LOG_DBG(""); | ||
|
||
if (event_cb) { | ||
return -EALREADY; | ||
} | ||
|
||
event_cb = cb; | ||
|
||
return 0; | ||
} | ||
|
||
static int avctp_l2cap_accept(struct bt_conn *conn, struct bt_l2cap_server *server, | ||
struct bt_l2cap_chan **chan) | ||
{ | ||
/* TODO */ | ||
|
||
return -ENOTSUP; | ||
} | ||
|
||
int bt_avctp_init(void) | ||
{ | ||
int err; | ||
static struct bt_l2cap_server avctp_l2cap = { | ||
.psm = BT_L2CAP_PSM_AVCTP, | ||
.sec_level = BT_SECURITY_L2, | ||
.accept = avctp_l2cap_accept, | ||
}; | ||
|
||
LOG_DBG(""); | ||
|
||
/* Register AVCTP PSM with L2CAP */ | ||
err = bt_l2cap_br_server_register(&avctp_l2cap); | ||
if (err < 0) { | ||
LOG_ERR("AVCTP L2CAP registration failed %d", err); | ||
} | ||
|
||
return err; | ||
} |
Oops, something went wrong.
Oops, something went wrong.
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Since this is a singleton (e.g. you don't have a linked list pointer in the struct), I think
const struct bt_avrcp_cb *cb
is probably more correct. That said, have you considered whether you want to keep this a singleton, i.e. that there aren't any valid use cases where an app might be interested in registering multiple instances of the callbacks?There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Hmm, technically the Bluetooth Spec allows multiple instances, however, none of the applications did so.
As for the target side, one AVRCP instance can control multiple players, thus multiple instances is meaningless.
As for the controller side, we don't really need two distinct control panels on one product.
I saw both A2DP and HFP assume singleton, I choose to follow. 'const' is not used as well at these profiles.