Primary Git Repository for the Zephyr Project. Zephyr is a new generation, scalable, optimized, secure RTOS for multiple hardware architectures.
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
 
 
 
 
 
 

731 lines
18 KiB

/** @file
* @brief Audio Video Remote Control Profile
*/
/*
* Copyright (c) 2015-2016 Intel Corporation
* Copyright (C) 2024 Xiaomi Corporation
*
* SPDX-License-Identifier: Apache-2.0
*/
#include <string.h>
#include <errno.h>
#include <zephyr/sys/atomic.h>
#include <zephyr/sys/byteorder.h>
#include <zephyr/sys/util.h>
#include <zephyr/sys/printk.h>
#include <zephyr/bluetooth/bluetooth.h>
#include <zephyr/bluetooth/classic/avrcp.h>
#include <zephyr/bluetooth/classic/sdp.h>
#include <zephyr/bluetooth/l2cap.h>
#include "host/hci_core.h"
#include "host/conn_internal.h"
#include "host/l2cap_internal.h"
#include "avctp_internal.h"
#include "avrcp_internal.h"
#define LOG_LEVEL CONFIG_BT_AVRCP_LOG_LEVEL
#include <zephyr/logging/log.h>
LOG_MODULE_REGISTER(bt_avrcp);
struct bt_avrcp {
struct bt_avctp session;
};
struct avrcp_handler {
bt_avrcp_opcode_t opcode;
void (*func)(struct bt_avrcp *avrcp, uint8_t tid, struct net_buf *buf, bt_avctp_cr_t cr);
};
#define AVRCP_AVCTP(_avctp) CONTAINER_OF(_avctp, struct bt_avrcp, session)
static const struct bt_avrcp_cb *avrcp_cb;
static struct bt_avrcp avrcp_connection[CONFIG_BT_MAX_CONN];
#if defined(CONFIG_BT_AVRCP_TARGET)
static struct bt_sdp_attribute avrcp_tg_attrs[] = {
BT_SDP_NEW_SERVICE,
BT_SDP_LIST(
BT_SDP_ATTR_SVCLASS_ID_LIST,
BT_SDP_TYPE_SIZE_VAR(BT_SDP_SEQ8, 3),
BT_SDP_DATA_ELEM_LIST(
{
BT_SDP_TYPE_SIZE(BT_SDP_UUID16),
BT_SDP_ARRAY_16(BT_SDP_AV_REMOTE_TARGET_SVCLASS)
},
)
),
BT_SDP_LIST(
BT_SDP_ATTR_PROTO_DESC_LIST,
BT_SDP_TYPE_SIZE_VAR(BT_SDP_SEQ8, 16),
BT_SDP_DATA_ELEM_LIST(
{
BT_SDP_TYPE_SIZE_VAR(BT_SDP_SEQ8, 6),
BT_SDP_DATA_ELEM_LIST({BT_SDP_TYPE_SIZE(BT_SDP_UUID16),
BT_SDP_ARRAY_16(BT_SDP_PROTO_L2CAP)},
{
BT_SDP_TYPE_SIZE(BT_SDP_UINT16),
BT_SDP_ARRAY_16(BT_UUID_AVCTP_VAL)
},
)
},
{
BT_SDP_TYPE_SIZE_VAR(BT_SDP_SEQ8, 6),
BT_SDP_DATA_ELEM_LIST(
{
BT_SDP_TYPE_SIZE(BT_SDP_UUID16),
BT_SDP_ARRAY_16(BT_UUID_AVCTP_VAL)
},
{
BT_SDP_TYPE_SIZE(BT_SDP_UINT16),
BT_SDP_ARRAY_16(AVCTP_VER_1_4)
},
)
},
)
),
/* C1: Browsing not supported */
/* C2: Cover Art not supported */
BT_SDP_LIST(
BT_SDP_ATTR_PROFILE_DESC_LIST,
BT_SDP_TYPE_SIZE_VAR(BT_SDP_SEQ8, 8),
BT_SDP_DATA_ELEM_LIST(
{
BT_SDP_TYPE_SIZE_VAR(BT_SDP_SEQ8, 6),
BT_SDP_DATA_ELEM_LIST(
{
BT_SDP_TYPE_SIZE(BT_SDP_UUID16),
BT_SDP_ARRAY_16(BT_SDP_AV_REMOTE_SVCLASS)
},
{
BT_SDP_TYPE_SIZE(BT_SDP_UINT16),
BT_SDP_ARRAY_16(AVRCP_VER_1_6)
},
)
},
)
),
BT_SDP_SUPPORTED_FEATURES(AVRCP_CAT_1 | AVRCP_CAT_2),
/* O: Provider Name not presented */
BT_SDP_SERVICE_NAME("AVRCP Target"),
};
static struct bt_sdp_record avrcp_tg_rec = BT_SDP_RECORD(avrcp_tg_attrs);
#endif /* CONFIG_BT_AVRCP_TARGET */
#if defined(CONFIG_BT_AVRCP_CONTROLLER)
static struct bt_sdp_attribute avrcp_ct_attrs[] = {
BT_SDP_NEW_SERVICE,
BT_SDP_LIST(
BT_SDP_ATTR_SVCLASS_ID_LIST,
BT_SDP_TYPE_SIZE_VAR(BT_SDP_SEQ8, 6),
BT_SDP_DATA_ELEM_LIST(
{
BT_SDP_TYPE_SIZE(BT_SDP_UUID16),
BT_SDP_ARRAY_16(BT_SDP_AV_REMOTE_SVCLASS)
},
{
BT_SDP_TYPE_SIZE(BT_SDP_UUID16),
BT_SDP_ARRAY_16(BT_SDP_AV_REMOTE_CONTROLLER_SVCLASS)
},
)
),
BT_SDP_LIST(
BT_SDP_ATTR_PROTO_DESC_LIST,
BT_SDP_TYPE_SIZE_VAR(BT_SDP_SEQ8, 16),
BT_SDP_DATA_ELEM_LIST(
{
BT_SDP_TYPE_SIZE_VAR(BT_SDP_SEQ8, 6),
BT_SDP_DATA_ELEM_LIST(
{
BT_SDP_TYPE_SIZE(BT_SDP_UUID16),
BT_SDP_ARRAY_16(BT_SDP_PROTO_L2CAP)
},
{
BT_SDP_TYPE_SIZE(BT_SDP_UINT16),
BT_SDP_ARRAY_16(BT_UUID_AVCTP_VAL)
},
)
},
{
BT_SDP_TYPE_SIZE_VAR(BT_SDP_SEQ8, 6),
BT_SDP_DATA_ELEM_LIST(
{
BT_SDP_TYPE_SIZE(BT_SDP_UUID16),
BT_SDP_ARRAY_16(BT_UUID_AVCTP_VAL)
},
{
BT_SDP_TYPE_SIZE(BT_SDP_UINT16),
BT_SDP_ARRAY_16(AVCTP_VER_1_4)
},
)
},
)
),
/* C1: Browsing not supported */
BT_SDP_LIST(
BT_SDP_ATTR_PROFILE_DESC_LIST,
BT_SDP_TYPE_SIZE_VAR(BT_SDP_SEQ8, 8),
BT_SDP_DATA_ELEM_LIST(
{
BT_SDP_TYPE_SIZE_VAR(BT_SDP_SEQ8, 6),
BT_SDP_DATA_ELEM_LIST(
{
BT_SDP_TYPE_SIZE(BT_SDP_UUID16),
BT_SDP_ARRAY_16(BT_SDP_AV_REMOTE_SVCLASS)
},
{
BT_SDP_TYPE_SIZE(BT_SDP_UINT16),
BT_SDP_ARRAY_16(AVRCP_VER_1_6)
},
)
},
)
),
BT_SDP_SUPPORTED_FEATURES(AVRCP_CAT_1 | AVRCP_CAT_2),
/* O: Provider Name not presented */
BT_SDP_SERVICE_NAME("AVRCP Controller"),
};
static struct bt_sdp_record avrcp_ct_rec = BT_SDP_RECORD(avrcp_ct_attrs);
#endif /* CONFIG_BT_AVRCP_CONTROLLER */
static struct bt_avrcp *get_new_connection(struct bt_conn *conn)
{
struct bt_avrcp *avrcp;
if (!conn) {
LOG_ERR("Invalid Input (err: %d)", -EINVAL);
return NULL;
}
avrcp = &avrcp_connection[bt_conn_index(conn)];
memset(avrcp, 0, sizeof(struct bt_avrcp));
return avrcp;
}
/* The AVCTP L2CAP channel established */
static void avrcp_connected(struct bt_avctp *session)
{
struct bt_avrcp *avrcp = AVRCP_AVCTP(session);
if ((avrcp_cb != NULL) && (avrcp_cb->connected != NULL)) {
avrcp_cb->connected(avrcp);
}
}
/* The AVCTP L2CAP channel released */
static void avrcp_disconnected(struct bt_avctp *session)
{
struct bt_avrcp *avrcp = AVRCP_AVCTP(session);
if ((avrcp_cb != NULL) && (avrcp_cb->disconnected != NULL)) {
avrcp_cb->disconnected(avrcp);
}
}
static void process_get_cap_rsp(struct bt_avrcp *avrcp, uint8_t tid, struct net_buf *buf)
{
struct bt_avrcp_avc_pdu *pdu;
struct bt_avrcp_get_cap_rsp *rsp;
uint16_t len;
uint16_t expected_len;
if (buf->len < sizeof(*pdu)) {
LOG_ERR("Invalid vendor payload length: %d", buf->len);
return;
}
pdu = net_buf_pull_mem(buf, sizeof(*pdu));
if (BT_AVRCP_AVC_PDU_GET_PACKET_TYPE(pdu) != BT_AVRVP_PKT_TYPE_SINGLE) {
LOG_ERR("Invalid packet type");
return;
}
len = sys_be16_to_cpu(pdu->param_len);
if (len < sizeof(*rsp) || buf->len < len) {
LOG_ERR("Invalid capability length: %d, buf length = %d", len, buf->len);
return;
}
if ((avrcp_cb == NULL) || (avrcp_cb->get_cap_rsp == NULL)) {
return;
}
rsp = (struct bt_avrcp_get_cap_rsp *)buf->data;
switch (rsp->cap_id) {
case BT_AVRCP_CAP_COMPANY_ID:
expected_len = rsp->cap_cnt * BT_AVRCP_COMPANY_ID_SIZE;
break;
case BT_AVRCP_CAP_EVENTS_SUPPORTED:
expected_len = rsp->cap_cnt;
break;
default:
LOG_ERR("Unrecognized capability = 0x%x", rsp->cap_id);
return;
}
if (buf->len < sizeof(*rsp) + expected_len) {
LOG_ERR("Invalid capability payload length: %d", buf->len);
return;
}
avrcp_cb->get_cap_rsp(avrcp, tid, rsp);
}
static void avrcp_vendor_dependent_handler(struct bt_avrcp *avrcp, uint8_t tid, struct net_buf *buf,
bt_avctp_cr_t cr)
{
struct bt_avrcp_avc_pdu *pdu;
struct bt_avrcp_header *avrcp_hdr;
uint32_t company_id;
uint8_t pdu_id;
if (buf->len < (sizeof(*avrcp_hdr) + BT_AVRCP_COMPANY_ID_SIZE + sizeof(pdu_id))) {
LOG_ERR("Invalid vendor frame length: %d", buf->len);
return;
}
avrcp_hdr = net_buf_pull_mem(buf, sizeof(*avrcp_hdr));
company_id = net_buf_pull_be24(buf);
if (company_id != BT_AVRCP_COMPANY_ID_BLUETOOTH_SIG) {
LOG_ERR("Invalid company id: 0x%06x", company_id);
return;
}
pdu = (struct bt_avrcp_avc_pdu *)buf->data;
if (cr == BT_AVCTP_CMD) {
LOG_DBG("Unhandled command: 0x%02x", pdu->pdu_id);
} else { /* BT_AVCTP_RESPONSE */
switch (pdu->pdu_id) {
case BT_AVRCP_PDU_ID_GET_CAPS:
process_get_cap_rsp(avrcp, tid, buf);
break;
default:
LOG_DBG("Unhandled response: 0x%02x", pdu->pdu_id);
break;
}
}
}
static void avrcp_unit_info_handler(struct bt_avrcp *avrcp, uint8_t tid, struct net_buf *buf,
bt_avctp_cr_t cr)
{
struct bt_avrcp_header *avrcp_hdr;
struct bt_avrcp_unit_info_rsp rsp;
avrcp_hdr = net_buf_pull_mem(buf, sizeof(*avrcp_hdr));
if (cr == BT_AVCTP_CMD) {
/* ToDo */
} else { /* BT_AVCTP_RESPONSE */
if ((avrcp_cb != NULL) && (avrcp_cb->unit_info_rsp != NULL)) {
if (buf->len != 5) {
LOG_ERR("Invalid unit info length: %d", buf->len);
return;
}
net_buf_pull_u8(buf); /* Always 0x07 */
rsp.unit_type = FIELD_GET(GENMASK(7, 3), net_buf_pull_u8(buf));
rsp.company_id = net_buf_pull_be24(buf);
avrcp_cb->unit_info_rsp(avrcp, tid, &rsp);
}
}
}
static void avrcp_subunit_info_handler(struct bt_avrcp *avrcp, uint8_t tid, struct net_buf *buf,
bt_avctp_cr_t cr)
{
struct bt_avrcp_header *avrcp_hdr;
struct bt_avrcp_subunit_info_rsp rsp;
uint8_t tmp;
avrcp_hdr = net_buf_pull_mem(buf, sizeof(*avrcp_hdr));
if (cr == BT_AVCTP_CMD) {
/* ToDo */
} else { /* BT_AVCTP_RESPONSE */
if ((avrcp_cb != NULL) && (avrcp_cb->subunit_info_rsp != NULL)) {
if (buf->len < 5) {
LOG_ERR("Invalid subunit info length: %d", buf->len);
return;
}
net_buf_pull_u8(buf); /* Always 0x07 */
tmp = net_buf_pull_u8(buf);
rsp.subunit_type = FIELD_GET(GENMASK(7, 3), tmp);
rsp.max_subunit_id = FIELD_GET(GENMASK(2, 0), tmp);
if (buf->len < (rsp.max_subunit_id << 1)) {
LOG_ERR("Invalid subunit info response");
return;
}
rsp.extended_subunit_type = buf->data;
rsp.extended_subunit_id = rsp.extended_subunit_type + rsp.max_subunit_id;
avrcp_cb->subunit_info_rsp(avrcp, tid, &rsp);
}
}
}
static void avrcp_pass_through_handler(struct bt_avrcp *avrcp, uint8_t tid, struct net_buf *buf,
bt_avctp_cr_t cr)
{
struct bt_avrcp_header *avrcp_hdr;
struct bt_avrcp_passthrough_rsp *rsp;
bt_avrcp_rsp_t result;
avrcp_hdr = net_buf_pull_mem(buf, sizeof(*avrcp_hdr));
if (cr == BT_AVCTP_CMD) {
/* ToDo */
} else { /* BT_AVCTP_RESPONSE */
if ((avrcp_cb != NULL) && (avrcp_cb->subunit_info_rsp != NULL)) {
if (buf->len < sizeof(*rsp)) {
LOG_ERR("Invalid passthrough length: %d", buf->len);
return;
}
result = BT_AVRCP_HDR_GET_CTYPE_OR_RSP(avrcp_hdr);
rsp = (struct bt_avrcp_passthrough_rsp *)buf->data;
avrcp_cb->passthrough_rsp(avrcp, tid, result, rsp);
}
}
}
static const struct avrcp_handler handler[] = {
{BT_AVRCP_OPC_VENDOR_DEPENDENT, avrcp_vendor_dependent_handler},
{BT_AVRCP_OPC_UNIT_INFO, avrcp_unit_info_handler},
{BT_AVRCP_OPC_SUBUNIT_INFO, avrcp_subunit_info_handler},
{BT_AVRCP_OPC_PASS_THROUGH, avrcp_pass_through_handler},
};
/* An AVRCP message received */
static int avrcp_recv(struct bt_avctp *session, struct net_buf *buf)
{
struct bt_avrcp *avrcp = AVRCP_AVCTP(session);
struct bt_avctp_header *avctp_hdr;
struct bt_avrcp_header *avrcp_hdr;
uint8_t tid, i;
bt_avctp_cr_t cr;
bt_avrcp_rsp_t rsp;
bt_avrcp_subunit_id_t subunit_id;
bt_avrcp_subunit_type_t subunit_type;
avctp_hdr = net_buf_pull_mem(buf, sizeof(*avctp_hdr));
if (buf->len < sizeof(*avrcp_hdr)) {
LOG_ERR("invalid AVRCP header received");
return -EINVAL;
}
avrcp_hdr = (void *)buf->data;
tid = BT_AVCTP_HDR_GET_TRANSACTION_LABLE(avctp_hdr);
cr = BT_AVCTP_HDR_GET_CR(avctp_hdr);
rsp = BT_AVRCP_HDR_GET_CTYPE_OR_RSP(avrcp_hdr);
subunit_id = BT_AVRCP_HDR_GET_SUBUNIT_ID(avrcp_hdr);
subunit_type = BT_AVRCP_HDR_GET_SUBUNIT_TYPE(avrcp_hdr);
if (avctp_hdr->pid != sys_cpu_to_be16(BT_SDP_AV_REMOTE_SVCLASS)) {
return -EINVAL; /* Ignore other profile */
}
LOG_DBG("AVRCP msg received, cr:0x%X, tid:0x%X, rsp: 0x%X, opc:0x%02X,", cr, tid, rsp,
avrcp_hdr->opcode);
for (i = 0U; i < ARRAY_SIZE(handler); i++) {
if (avrcp_hdr->opcode == handler[i].opcode) {
handler[i].func(avrcp, tid, buf, cr);
return 0;
}
}
return 0;
}
static const struct bt_avctp_ops_cb avctp_ops = {
.connected = avrcp_connected,
.disconnected = avrcp_disconnected,
.recv = avrcp_recv,
};
static int avrcp_accept(struct bt_conn *conn, struct bt_avctp **session)
{
struct bt_avrcp *avrcp;
avrcp = get_new_connection(conn);
if (!avrcp) {
return -ENOMEM;
}
*session = &(avrcp->session);
avrcp->session.ops = &avctp_ops;
LOG_DBG("session: %p", &(avrcp->session));
return 0;
}
static struct bt_avctp_event_cb avctp_cb = {
.accept = avrcp_accept,
};
int bt_avrcp_init(void)
{
int err;
/* Register event handlers with AVCTP */
err = bt_avctp_register(&avctp_cb);
if (err < 0) {
LOG_ERR("AVRCP registration failed");
return err;
}
#if defined(CONFIG_BT_AVRCP_TARGET)
bt_sdp_register_service(&avrcp_tg_rec);
#endif /* CONFIG_BT_AVRCP_CONTROLLER */
#if defined(CONFIG_BT_AVRCP_CONTROLLER)
bt_sdp_register_service(&avrcp_ct_rec);
#endif /* CONFIG_BT_AVRCP_CONTROLLER */
LOG_DBG("AVRCP Initialized successfully.");
return 0;
}
struct bt_avrcp *bt_avrcp_connect(struct bt_conn *conn)
{
struct bt_avrcp *avrcp;
int err;
avrcp = get_new_connection(conn);
if (!avrcp) {
LOG_ERR("Cannot allocate memory");
return NULL;
}
avrcp->session.ops = &avctp_ops;
err = bt_avctp_connect(conn, &(avrcp->session));
if (err < 0) {
/* If error occurs, undo the saving and return the error */
memset(avrcp, 0, sizeof(struct bt_avrcp));
LOG_DBG("AVCTP Connect failed");
return NULL;
}
LOG_DBG("Connection request sent");
return avrcp;
}
int bt_avrcp_disconnect(struct bt_avrcp *avrcp)
{
int err;
err = bt_avctp_disconnect(&(avrcp->session));
if (err < 0) {
LOG_DBG("AVCTP Disconnect failed");
return err;
}
return err;
}
static struct net_buf *avrcp_create_pdu(struct bt_avrcp *avrcp, uint8_t tid, bt_avctp_cr_t cr)
{
struct net_buf *buf;
buf = bt_avctp_create_pdu(&(avrcp->session), cr, BT_AVCTP_PKT_TYPE_SINGLE,
BT_AVCTP_IPID_NONE, tid,
sys_cpu_to_be16(BT_SDP_AV_REMOTE_SVCLASS));
return buf;
}
static struct net_buf *avrcp_create_unit_pdu(struct bt_avrcp *avrcp, uint8_t tid, bt_avctp_cr_t cr)
{
struct net_buf *buf;
struct bt_avrcp_frame *cmd;
buf = avrcp_create_pdu(avrcp, tid, cr);
if (!buf) {
return NULL;
}
cmd = net_buf_add(buf, sizeof(*cmd));
memset(cmd, 0, sizeof(*cmd));
BT_AVRCP_HDR_SET_CTYPE_OR_RSP(&cmd->hdr, cr == BT_AVCTP_CMD ? BT_AVRCP_CTYPE_STATUS
: BT_AVRCP_RSP_STABLE);
BT_AVRCP_HDR_SET_SUBUNIT_ID(&cmd->hdr, BT_AVRCP_SUBUNIT_ID_IGNORE);
BT_AVRCP_HDR_SET_SUBUNIT_TYPE(&cmd->hdr, BT_AVRCP_SUBUNIT_TYPE_UNIT);
cmd->hdr.opcode = BT_AVRCP_OPC_UNIT_INFO;
return buf;
}
static struct net_buf *avrcp_create_subunit_pdu(struct bt_avrcp *avrcp, uint8_t tid,
bt_avctp_cr_t cr)
{
struct net_buf *buf;
struct bt_avrcp_frame *cmd;
buf = avrcp_create_pdu(avrcp, tid, cr);
if (!buf) {
return NULL;
}
cmd = net_buf_add(buf, sizeof(*cmd));
memset(cmd, 0, sizeof(*cmd));
BT_AVRCP_HDR_SET_CTYPE_OR_RSP(&cmd->hdr, cr == BT_AVCTP_CMD ? BT_AVRCP_CTYPE_STATUS
: BT_AVRCP_RSP_STABLE);
BT_AVRCP_HDR_SET_SUBUNIT_ID(&cmd->hdr, BT_AVRCP_SUBUNIT_ID_IGNORE);
BT_AVRCP_HDR_SET_SUBUNIT_TYPE(&cmd->hdr, BT_AVRCP_SUBUNIT_TYPE_UNIT);
cmd->hdr.opcode = BT_AVRCP_OPC_SUBUNIT_INFO;
return buf;
}
static struct net_buf *avrcp_create_passthrough_pdu(struct bt_avrcp *avrcp, uint8_t tid,
bt_avctp_cr_t cr, uint8_t ctype_or_rsp)
{
struct net_buf *buf;
struct bt_avrcp_frame *cmd;
buf = avrcp_create_pdu(avrcp, tid, cr);
if (!buf) {
return NULL;
}
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;
return buf;
}
static struct net_buf *avrcp_create_vendor_pdu(struct bt_avrcp *avrcp, uint8_t tid,
bt_avctp_cr_t cr, uint8_t ctype_or_rsp)
{
struct net_buf *buf;
struct bt_avrcp_frame *cmd;
buf = avrcp_create_pdu(avrcp, tid, cr);
if (!buf) {
return NULL;
}
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_VENDOR_DEPENDENT;
return buf;
}
static int avrcp_send(struct bt_avrcp *avrcp, struct net_buf *buf)
{
int err;
struct bt_avctp_header *avctp_hdr = (struct bt_avctp_header *)(buf->data);
struct bt_avrcp_header *avrcp_hdr =
(struct bt_avrcp_header *)(buf->data + sizeof(*avctp_hdr));
uint8_t tid = BT_AVCTP_HDR_GET_TRANSACTION_LABLE(avctp_hdr);
bt_avctp_cr_t cr = BT_AVCTP_HDR_GET_CR(avctp_hdr);
bt_avrcp_ctype_t ctype = BT_AVRCP_HDR_GET_CTYPE_OR_RSP(avrcp_hdr);
LOG_DBG("AVRCP send cr:0x%X, tid:0x%X, ctype: 0x%X, opc:0x%02X\n", cr, tid, ctype,
avrcp_hdr->opcode);
err = bt_avctp_send(&(avrcp->session), buf);
if (err < 0) {
net_buf_unref(buf);
LOG_ERR("AVCTP send fail, err = %d", err);
return err;
}
return 0;
}
int bt_avrcp_get_cap(struct bt_avrcp *avrcp, uint8_t tid, uint8_t cap_id)
{
struct net_buf *buf;
struct bt_avrcp_avc_pdu *pdu;
buf = avrcp_create_vendor_pdu(avrcp, tid, BT_AVCTP_CMD, BT_AVRCP_CTYPE_STATUS);
if (!buf) {
return -ENOMEM;
}
net_buf_add_be24(buf, BT_AVRCP_COMPANY_ID_BLUETOOTH_SIG);
pdu = net_buf_add(buf, sizeof(*pdu));
pdu->pdu_id = BT_AVRCP_PDU_ID_GET_CAPS;
BT_AVRCP_AVC_PDU_SET_PACKET_TYPE(pdu, BT_AVRVP_PKT_TYPE_SINGLE);
pdu->param_len = sys_cpu_to_be16(sizeof(cap_id));
net_buf_add_u8(buf, cap_id);
return avrcp_send(avrcp, buf);
}
int bt_avrcp_get_unit_info(struct bt_avrcp *avrcp, uint8_t tid)
{
struct net_buf *buf;
uint8_t param[5];
buf = avrcp_create_unit_pdu(avrcp, tid, BT_AVCTP_CMD);
if (!buf) {
return -ENOMEM;
}
memset(param, 0xFF, ARRAY_SIZE(param));
net_buf_add_mem(buf, param, sizeof(param));
return avrcp_send(avrcp, buf);
}
int bt_avrcp_get_subunit_info(struct bt_avrcp *avrcp, uint8_t tid)
{
struct net_buf *buf;
uint8_t param[5];
buf = avrcp_create_subunit_pdu(avrcp, tid, BT_AVCTP_CMD);
if (!buf) {
return -ENOMEM;
}
memset(param, 0xFF, ARRAY_SIZE(param));
param[0] = FIELD_PREP(GENMASK(6, 4), AVRCP_SUBUNIT_PAGE) |
FIELD_PREP(GENMASK(2, 0), AVRCP_SUBUNIT_EXTENSION_CODE);
net_buf_add_mem(buf, param, sizeof(param));
return avrcp_send(avrcp, buf);
}
int bt_avrcp_passthrough(struct bt_avrcp *avrcp, uint8_t tid, uint8_t opid, uint8_t state,
const uint8_t *payload, uint8_t len)
{
struct net_buf *buf;
buf = avrcp_create_passthrough_pdu(avrcp, tid, BT_AVCTP_CMD, BT_AVRCP_CTYPE_CONTROL);
if (!buf) {
return -ENOMEM;
}
net_buf_add_u8(buf, FIELD_PREP(BIT(7), state) | FIELD_PREP(GENMASK(6, 0), opid));
net_buf_add_u8(buf, len);
if (len) {
net_buf_add_mem(buf, payload, len);
}
return avrcp_send(avrcp, buf);
}
int bt_avrcp_register_cb(const struct bt_avrcp_cb *cb)
{
avrcp_cb = cb;
return 0;
}