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:
committed by
Henrik Brix Andersen
parent
33406d50c8
commit
0835fdca08
@@ -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
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user