Bluetooth: AVRCP: Implement Pass Through command reception on TG

Add a new callback to support the situation when a Pass Through command
is received, and add an API to respond to the Pass Through command.

Signed-off-by: Make Shi <make.shi@nxp.com>
This commit is contained in:
Make Shi
2025-06-09 15:10:20 +08:00
committed by Henrik Brix Andersen
parent 33406d50c8
commit 0835fdca08
2 changed files with 123 additions and 9 deletions

View File

@@ -277,14 +277,27 @@ struct bt_avrcp_subunit_info_rsp {
};
#define BT_AVRCP_PASSTHROUGH_GET_STATE(payload) \
((bt_avrcp_button_state_t)(FIELD_GET(BIT(7), ((payload)->byte0))))
((bt_avrcp_opid_t)(FIELD_GET(BIT(7), ((payload)->opid_state))))
#define BT_AVRCP_PASSTHROUGH_GET_OPID(payload) \
((bt_avrcp_opid_t)(FIELD_GET(GENMASK(6, 0), ((payload)->byte0))))
((bt_avrcp_button_state_t)(FIELD_GET(GENMASK(6, 0), ((payload)->opid_state))))
#define BT_AVRCP_PASSTHROUGH_SET_STATE_OPID(payload, state, opid) \
(payload)->opid_state = FIELD_PREP(BIT(7), state) | FIELD_PREP(GENMASK(6, 0), opid)
struct bt_avrcp_passthrough_opvu_data {
uint8_t company_id[BT_AVRCP_COMPANY_ID_SIZE];
uint16_t opid_vu;
} __packed;
struct bt_avrcp_passthrough_cmd {
uint8_t opid_state; /**< [7]: state_flag, [6:0]: opid */
uint8_t data_len;
struct bt_avrcp_passthrough_opvu_data data[0]; /**< opvu data */
} __packed;
struct bt_avrcp_passthrough_rsp {
uint8_t byte0; /**< [7]: state_flag, [6:0]: opid */
uint8_t opid_state; /**< [7]: state_flag, [6:0]: opid */
uint8_t data_len;
uint8_t data[];
struct bt_avrcp_passthrough_opvu_data data[0]; /**< opvu data */
} __packed;
struct bt_avrcp_get_cap_rsp {
@@ -609,6 +622,19 @@ struct bt_avrcp_tg_cb {
* @param player_id The player ID to be set as browsed player.
*/
void (*set_browsed_player_req)(struct bt_avrcp_tg *tg, uint8_t tid, uint16_t player_id);
/** @brief Pass Through command request callback.
*
* This callback is called whenever an AVRCP Pass Through command is request.
*
* @param tg AVRCP TG connection object.
* @param tid The transaction label of the request.
* @param buf The buffer containing the PASS THROUGH command payload.
* The application can parse this payload according to the format defined
* in @ref bt_avrcp_passthrough_rsp. Note that the data is encoded
* in big-endian format.
*/
void (*passthrough_req)(struct bt_avrcp_tg *tg, uint8_t tid, struct net_buf *buf);
};
/** @brief Register callback.
@@ -657,6 +683,24 @@ int bt_avrcp_tg_send_subunit_info_rsp(struct bt_avrcp_tg *tg, uint8_t tid);
*/
int bt_avrcp_tg_send_set_browsed_player_rsp(struct bt_avrcp_tg *tg, uint8_t tid,
struct net_buf *buf);
/** @brief Send AVRCP Pass Through response.
*
* This function is called by the application to send the Pass Through response.
*
* @param tg The AVRCP TG instance.
* @param tid The transaction label of the response, valid from 0 to 15.
* @param result The response code, see @ref bt_avrcp_rsp_t, can support
* 0x8(NOT_IMPLEMENTED), 0x9 (ACCEPTED), 0xA (REJECTED)
* @param buf The buffer containing the PASS THROUGH command payload.
* The application can construct this payload according to the format defined
* in @ref bt_avrcp_passthrough_rsp. Note that the data is encoded
* in big-endian format.
*
* @return 0 in case of success or error code in case of error.
*/
int bt_avrcp_tg_send_passthrough_rsp(struct bt_avrcp_tg *tg, uint8_t tid, bt_avrcp_rsp_t result,
struct net_buf *buf);
#ifdef __cplusplus
}
#endif

View File

@@ -422,6 +422,14 @@ static struct net_buf *avrcp_create_subunit_pdu(struct bt_avrcp *avrcp, uint8_t
return buf;
}
static void avrcp_set_passthrough_header(struct bt_avrcp_header *hdr, uint8_t ctype_or_rsp)
{
BT_AVRCP_HDR_SET_CTYPE_OR_RSP(hdr, ctype_or_rsp);
BT_AVRCP_HDR_SET_SUBUNIT_ID(hdr, BT_AVRCP_SUBUNIT_ID_ZERO);
BT_AVRCP_HDR_SET_SUBUNIT_TYPE(hdr, BT_AVRCP_SUBUNIT_TYPE_PANEL);
hdr->opcode = BT_AVRCP_OPC_PASS_THROUGH;
}
static struct net_buf *avrcp_create_passthrough_pdu(struct bt_avrcp *avrcp, uint8_t ctype_or_rsp)
{
struct net_buf *buf;
@@ -434,10 +442,7 @@ static struct net_buf *avrcp_create_passthrough_pdu(struct bt_avrcp *avrcp, uint
cmd = net_buf_add(buf, sizeof(*cmd));
memset(cmd, 0, sizeof(*cmd));
BT_AVRCP_HDR_SET_CTYPE_OR_RSP(&cmd->hdr, ctype_or_rsp);
BT_AVRCP_HDR_SET_SUBUNIT_ID(&cmd->hdr, BT_AVRCP_SUBUNIT_ID_ZERO);
BT_AVRCP_HDR_SET_SUBUNIT_TYPE(&cmd->hdr, BT_AVRCP_SUBUNIT_TYPE_PANEL);
cmd->hdr.opcode = BT_AVRCP_OPC_PASS_THROUGH;
avrcp_set_passthrough_header(&cmd->hdr, ctype_or_rsp);
return buf;
}
@@ -827,7 +832,42 @@ err_rsp:
static void avrcp_pass_through_cmd_handler(struct bt_avrcp *avrcp, uint8_t tid,
struct net_buf *buf)
{
/* ToDo */
struct bt_avrcp_header *avrcp_hdr;
struct net_buf *rsp_buf;
int err;
if ((avrcp_tg_cb == NULL) || (avrcp_tg_cb->passthrough_req == NULL)) {
goto err_rsp;
}
if (buf->len < (sizeof(*avrcp_hdr) + sizeof(struct bt_avrcp_passthrough_cmd))) {
LOG_ERR("Invalid passthrough command length: %d", buf->len);
goto err_rsp;
}
avrcp_hdr = net_buf_pull_mem(buf, sizeof(*avrcp_hdr));
if (BT_AVRCP_HDR_GET_SUBUNIT_TYPE(avrcp_hdr) != BT_AVRCP_SUBUNIT_TYPE_PANEL ||
BT_AVRCP_HDR_GET_SUBUNIT_ID(avrcp_hdr) != BT_AVRCP_SUBUNIT_ID_ZERO ||
BT_AVRCP_HDR_GET_CTYPE_OR_RSP(avrcp_hdr) != BT_AVRCP_CTYPE_CONTROL) {
LOG_ERR("Invalid passthrough command ");
goto err_rsp;
}
return avrcp_tg_cb->passthrough_req(get_avrcp_tg(avrcp), tid, buf);
err_rsp:
rsp_buf = bt_avrcp_create_pdu(NULL);
if (rsp_buf == NULL) {
LOG_ERR("Failed to allocate response buffer");
return;
}
err = bt_avrcp_tg_send_passthrough_rsp(get_avrcp_tg(avrcp), tid, BT_AVRCP_RSP_REJECTED,
rsp_buf);
if (err < 0) {
LOG_ERR("Failed to send passthrough error response");
net_buf_unref(rsp_buf);
}
}
static const struct avrcp_handler cmd_handlers[] = {
@@ -1570,3 +1610,33 @@ int bt_avrcp_tg_send_set_browsed_player_rsp(struct bt_avrcp_tg *tg, uint8_t tid,
return err;
}
#endif /* CONFIG_BT_AVRCP_BROWSING */
int bt_avrcp_tg_send_passthrough_rsp(struct bt_avrcp_tg *tg, uint8_t tid, bt_avrcp_rsp_t result,
struct net_buf *buf)
{
struct bt_avrcp_header *avrcp_hdr;
int err;
if ((tg == NULL) || (tg->avrcp == NULL) || (buf == NULL)) {
return -EINVAL;
}
if (!IS_TG_ROLE_SUPPORTED()) {
return -ENOTSUP;
}
if (net_buf_headroom(buf) < sizeof(struct bt_avrcp_header)) {
LOG_ERR("Not enough headroom in buffer for bt_avrcp_header");
return -ENOMEM;
}
avrcp_hdr = net_buf_push(buf, sizeof(struct bt_avrcp_header));
memset(avrcp_hdr, 0, sizeof(struct bt_avrcp_header));
avrcp_set_passthrough_header(avrcp_hdr, result);
err = avrcp_send(tg->avrcp, buf, BT_AVCTP_RESPONSE, tid);
if (err < 0) {
LOG_ERR("Failed to send AVRCP PDU (err: %d)", err);
}
return err;
}