diff --git a/include/zephyr/bluetooth/classic/avrcp.h b/include/zephyr/bluetooth/classic/avrcp.h index 97c385d7cfd..82405e28d6e 100644 --- a/include/zephyr/bluetooth/classic/avrcp.h +++ b/include/zephyr/bluetooth/classic/avrcp.h @@ -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 diff --git a/subsys/bluetooth/host/classic/avrcp.c b/subsys/bluetooth/host/classic/avrcp.c index 41f382cf4f4..30fa1b3fb51 100644 --- a/subsys/bluetooth/host/classic/avrcp.c +++ b/subsys/bluetooth/host/classic/avrcp.c @@ -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; +}