Commit 53bba452 authored by Zihao Gao's avatar Zihao Gao Committed by Benjamin Cabé
Browse files

Bluetooth: AVRCP: implementation for passthrough commands



This patch alllows to send passthough command from CT to TG.

Signed-off-by: default avatarZihao Gao <gaozihao@xiaomi.com>
parent d01c93c1
Loading
Loading
Loading
Loading
+130 −1
Original line number Diff line number Diff line
@@ -16,6 +16,99 @@
extern "C" {
#endif

/** @brief AV/C command types */
typedef enum __packed {
	BT_AVRCP_CTYPE_CONTROL = 0x0,
	BT_AVRCP_CTYPE_STATUS = 0x1,
	BT_AVRCP_CTYPE_SPECIFIC_INQUIRY = 0x2,
	BT_AVRCP_CTYPE_NOTIFY = 0x3,
	BT_AVRCP_CTYPE_GENERAL_INQUIRY = 0x4,
} bt_avrcp_ctype_t;

/** @brief AV/C response codes */
typedef enum __packed {
	BT_AVRCP_RSP_NOT_IMPLEMENTED = 0x8,
	BT_AVRCP_RSP_ACCEPTED = 0x9,
	BT_AVRCP_RSP_REJECTED = 0xa,
	BT_AVRCP_RSP_IN_TRANSITION = 0xb,
	BT_AVRCP_RSP_IMPLEMENTED = 0xc, /**< For SPECIFIC_INQUIRY and GENERAL_INQUIRY commands */
	BT_AVRCP_RSP_STABLE = 0xc,      /**< For STATUS commands */
	BT_AVRCP_RSP_CHANGED = 0xd,
	BT_AVRCP_RSP_INTERIM = 0xf,
} bt_avrcp_rsp_t;

/** @brief AV/C operation ids used in AVRCP passthrough commands */
typedef enum __packed {
	BT_AVRCP_OPID_SELECT = 0x00,
	BT_AVRCP_OPID_UP = 0x01,
	BT_AVRCP_OPID_DOWN = 0x02,
	BT_AVRCP_OPID_LEFT = 0x03,
	BT_AVRCP_OPID_RIGHT = 0x04,
	BT_AVRCP_OPID_RIGHT_UP = 0x05,
	BT_AVRCP_OPID_RIGHT_DOWN = 0x06,
	BT_AVRCP_OPID_LEFT_UP = 0x07,
	BT_AVRCP_OPID_LEFT_DOWN = 0x08,
	BT_AVRCP_OPID_ROOT_MENU = 0x09,
	BT_AVRCP_OPID_SETUP_MENU = 0x0a,
	BT_AVRCP_OPID_CONTENTS_MENU = 0x0b,
	BT_AVRCP_OPID_FAVORITE_MENU = 0x0c,
	BT_AVRCP_OPID_EXIT = 0x0d,

	BT_AVRCP_OPID_0 = 0x20,
	BT_AVRCP_OPID_1 = 0x21,
	BT_AVRCP_OPID_2 = 0x22,
	BT_AVRCP_OPID_3 = 0x23,
	BT_AVRCP_OPID_4 = 0x24,
	BT_AVRCP_OPID_5 = 0x25,
	BT_AVRCP_OPID_6 = 0x26,
	BT_AVRCP_OPID_7 = 0x27,
	BT_AVRCP_OPID_8 = 0x28,
	BT_AVRCP_OPID_9 = 0x29,
	BT_AVRCP_OPID_DOT = 0x2a,
	BT_AVRCP_OPID_ENTER = 0x2b,
	BT_AVRCP_OPID_CLEAR = 0x2c,

	BT_AVRCP_OPID_CHANNEL_UP = 0x30,
	BT_AVRCP_OPID_CHANNEL_DOWN = 0x31,
	BT_AVRCP_OPID_PREVIOUS_CHANNEL = 0x32,
	BT_AVRCP_OPID_SOUND_SELECT = 0x33,
	BT_AVRCP_OPID_INPUT_SELECT = 0x34,
	BT_AVRCP_OPID_DISPLAY_INFORMATION = 0x35,
	BT_AVRCP_OPID_HELP = 0x36,
	BT_AVRCP_OPID_PAGE_UP = 0x37,
	BT_AVRCP_OPID_PAGE_DOWN = 0x38,

	BT_AVRCP_OPID_POWER = 0x40,
	BT_AVRCP_OPID_VOLUME_UP = 0x41,
	BT_AVRCP_OPID_VOLUME_DOWN = 0x42,
	BT_AVRCP_OPID_MUTE = 0x43,
	BT_AVRCP_OPID_PLAY = 0x44,
	BT_AVRCP_OPID_STOP = 0x45,
	BT_AVRCP_OPID_PAUSE = 0x46,
	BT_AVRCP_OPID_RECORD = 0x47,
	BT_AVRCP_OPID_REWIND = 0x48,
	BT_AVRCP_OPID_FAST_FORWARD = 0x49,
	BT_AVRCP_OPID_EJECT = 0x4a,
	BT_AVRCP_OPID_FORWARD = 0x4b,
	BT_AVRCP_OPID_BACKWARD = 0x4c,

	BT_AVRCP_OPID_ANGLE = 0x50,
	BT_AVRCP_OPID_SUBPICTURE = 0x51,

	BT_AVRCP_OPID_F1 = 0x71,
	BT_AVRCP_OPID_F2 = 0x72,
	BT_AVRCP_OPID_F3 = 0x73,
	BT_AVRCP_OPID_F4 = 0x74,
	BT_AVRCP_OPID_F5 = 0x75,
	BT_AVRCP_OPID_VENDOR_UNIQUE = 0x7e,
} bt_avrcp_opid_t;

/** @brief AVRCP button state flag */
typedef enum __packed {
	BT_AVRCP_BUTTON_PRESSED = 0,
	BT_AVRCP_BUTTON_RELEASED = 1,
} bt_avrcp_button_state_t;

/** @brief AVRCP structure */
struct bt_avrcp;

@@ -31,6 +124,14 @@ struct bt_avrcp_subunit_info_rsp {
	const uint8_t *extended_subunit_id;   /**< contains max_subunit_id items */
};

struct bt_avrcp_passthrough_rsp {
	uint8_t response;     /**< bt_avrcp_rsp_t */
	uint8_t operation_id; /**< bt_avrcp_opid_t */
	uint8_t state;        /**< bt_avrcp_button_state_t */
	uint8_t len;
	const uint8_t *payload;
};

struct bt_avrcp_cb {
	/** @brief An AVRCP connection has been established.
	 *
@@ -40,6 +141,7 @@ struct bt_avrcp_cb {
	 *  @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
@@ -48,6 +150,7 @@ struct bt_avrcp_cb {
	 *  @param avrcp AVRCP connection object.
	 */
	void (*disconnected)(struct bt_avrcp *avrcp);

	/** @brief Callback function for bt_avrcp_get_unit_info().
	 *
	 *  Called when the get unit info process is completed.
@@ -56,6 +159,7 @@ struct bt_avrcp_cb {
	 *  @param rsp The response for UNIT INFO command.
	 */
	void (*unit_info_rsp)(struct bt_avrcp *avrcp, struct bt_avrcp_unit_info_rsp *rsp);

	/** @brief Callback function for bt_avrcp_get_subunit_info().
	 *
	 *  Called when the get subunit info process is completed.
@@ -64,6 +168,15 @@ struct bt_avrcp_cb {
	 *  @param rsp The response for SUBUNIT INFO command.
	 */
	void (*subunit_info_rsp)(struct bt_avrcp *avrcp, struct bt_avrcp_subunit_info_rsp *rsp);

	/** @brief Callback function for bt_avrcp_passthrough().
	 *
	 *  Called when a passthrough response is received.
	 *
	 *  @param avrcp AVRCP connection object.
	 *  @param rsp The response for PASS THROUGH command.
	 */
	void (*passthrough_rsp)(struct bt_avrcp *avrcp, struct bt_avrcp_passthrough_rsp *rsp);
};

/** @brief Connect AVRCP.
@@ -120,6 +233,22 @@ int bt_avrcp_get_unit_info(struct bt_avrcp *avrcp);
 */
int bt_avrcp_get_subunit_info(struct bt_avrcp *avrcp);

/** @brief Send AVRCP Pass Through command.
 *
 *  This function send a pass through command to the remote device. Passsthhrough command is used
 *  to transfer user operation information from a CT to Panel subunit of TG.
 *
 *  @param avrcp The AVRCP instance.
 *  @param operation_id The user operation id.
 *  @param state The button state.
 *  @param payload The payload of the pass through command. Should not be NULL if len is not zero.
 *  @param len The length of the payload.
 *
 *  @return 0 in case of success or error code in case of error.
 */
int bt_avrcp_passthrough(struct bt_avrcp *avrcp, bt_avrcp_opid_t operation_id,
			 bt_avrcp_button_state_t state, const uint8_t *payload, uint8_t len);

#ifdef __cplusplus
}
#endif
+9 −11
Original line number Diff line number Diff line
@@ -82,6 +82,7 @@ static int avctp_l2cap_recv(struct bt_l2cap_chan *chan, struct net_buf *buf)
	uint8_t tid;
	bt_avctp_pkt_type_t pkt_type;
	bt_avctp_cr_t cr;
	int err;

	if (buf->len < sizeof(*hdr)) {
		LOG_ERR("invalid AVCTP header received");
@@ -117,7 +118,13 @@ static int avctp_l2cap_recv(struct bt_l2cap_chan *chan, struct net_buf *buf)
			if (!rsp) {
				return -ENOMEM;
			}
			return bt_avctp_send(session, rsp);

			err = bt_avctp_send(session, rsp);
			if (err < 0) {
				net_buf_unref(rsp);
				LOG_ERR("AVCTP send fail, err = %d", err);
				return err;
			}
		}
		return 0; /* No need to report to the upper layer */
	}
@@ -189,16 +196,7 @@ struct net_buf *bt_avctp_create_pdu(struct bt_avctp *session, bt_avctp_cr_t cr,

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;
	return bt_l2cap_chan_send(&session->br_chan.chan, buf);
}

int bt_avctp_register(const struct bt_avctp_event_cb *cb)
+84 −16
Original line number Diff line number Diff line
@@ -260,7 +260,7 @@ static void avrcp_unit_info_handler(struct bt_avrcp *avrcp, struct net_buf *buf,
		if ((avrcp_cb != NULL) && (avrcp_cb->unit_info_rsp != NULL)) {
			net_buf_pull(buf, sizeof(*avrcp_hdr));
			if (buf->len != 5) {
				LOG_ERR("Invalid unit info length");
				LOG_ERR("Invalid unit info length: %d", buf->len);
				return;
			}
			net_buf_pull_u8(buf); /* Always 0x07 */
@@ -284,7 +284,7 @@ static void avrcp_subunit_info_handler(struct bt_avrcp *avrcp, struct net_buf *b
		if ((avrcp_cb != NULL) && (avrcp_cb->subunit_info_rsp != NULL)) {
			net_buf_pull(buf, sizeof(*avrcp_hdr));
			if (buf->len < 5) {
				LOG_ERR("Invalid subunit info length");
				LOG_ERR("Invalid subunit info length: %d", buf->len);
				return;
			}
			net_buf_pull_u8(buf); /* Always 0x07 */
@@ -305,7 +305,31 @@ static void avrcp_subunit_info_handler(struct bt_avrcp *avrcp, struct net_buf *b
static void avrcp_pass_through_handler(struct bt_avrcp *avrcp, struct net_buf *buf,
					   bt_avctp_cr_t cr)
{
	struct bt_avrcp_header *avrcp_hdr;
	struct bt_avrcp_passthrough_rsp rsp;
	uint8_t tmp;

	if (cr == BT_AVCTP_CMD) {
		/* ToDo */
	} else { /* BT_AVCTP_RESPONSE */
		if ((avrcp_cb != NULL) && (avrcp_cb->subunit_info_rsp != NULL)) {
			avrcp_hdr = (struct bt_avrcp_header *)buf->data;
			net_buf_pull(buf, sizeof(*avrcp_hdr));
			if (buf->len < 2) {
				LOG_ERR("Invalid passthrough length: %d", buf->len);
				return;
			}

			rsp.response = BT_AVRCP_HDR_GET_CTYPE_OR_RSP(avrcp_hdr);
			tmp = net_buf_pull_u8(buf);
			rsp.operation_id = FIELD_GET(GENMASK(6, 0), tmp);
			rsp.state = FIELD_GET(GENMASK(7, 7), tmp);
			rsp.len = net_buf_pull_u8(buf);
			rsp.payload = rsp.len ? buf->data : NULL;

			avrcp_cb->passthrough_rsp(avrcp, &rsp);
		}
	}
}

static const struct avrcp_handler handler[] = {
@@ -323,7 +347,7 @@ static int avrcp_recv(struct bt_avctp *session, struct net_buf *buf)
	struct bt_avrcp_header *avrcp_hdr;
	uint8_t tid, i;
	bt_avctp_cr_t cr;
	bt_avrcp_ctype_t ctype;
	bt_avrcp_rsp_t rsp;
	bt_avrcp_subunit_id_t subunit_id;
	bt_avrcp_subunit_type_t subunit_type;

@@ -337,7 +361,7 @@ static int avrcp_recv(struct bt_avctp *session, struct net_buf *buf)
	avrcp_hdr = (void *)buf->data;
	tid = BT_AVCTP_HDR_GET_TRANSACTION_LABLE(avctp_hdr);
	cr = BT_AVCTP_HDR_GET_CR(avctp_hdr);
	ctype = BT_AVRCP_HDR_GET_CTYPE(avrcp_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);

@@ -345,11 +369,11 @@ static int avrcp_recv(struct bt_avctp *session, struct net_buf *buf)
		return -EINVAL; /* Ignore other profile */
	}

	LOG_DBG("AVRCP msg received, cr:0x%X, tid:0x%X, ctype: 0x%X, opc:0x%02X,", cr, tid, ctype,
	LOG_DBG("AVRCP msg received, cr:0x%X, tid:0x%X, rsp: 0x%X, opc:0x%02X,", cr, tid, rsp,
		avrcp_hdr->opcode);
	if (cr == BT_AVCTP_RESPONSE) {
		if (avrcp_hdr->opcode == BT_AVRCP_OPC_VENDOR_DEPENDENT &&
		    ctype == BT_AVRCP_CTYPE_CHANGED) {
		    rsp == BT_AVRCP_RSP_CHANGED) {
			/* Status changed notifiation, do not reset timer */
		} else if (avrcp_hdr->opcode == BT_AVRCP_OPC_PASS_THROUGH) {
			/* No max response time for pass through commands  */
@@ -474,17 +498,17 @@ static struct net_buf *avrcp_create_pdu(struct bt_avrcp *avrcp, bt_avctp_cr_t cr
static struct net_buf *avrcp_create_unit_pdu(struct bt_avrcp *avrcp, bt_avctp_cr_t cr)
{
	struct net_buf *buf;
	struct bt_avrcp_unit_info_cmd *cmd;
	struct bt_avrcp_frame *cmd;

	buf = avrcp_create_pdu(avrcp, cr);
	if (!buf) {
		return buf;
		return NULL;
	}

	cmd = net_buf_add(buf, sizeof(*cmd));
	memset(cmd, 0, sizeof(*cmd));
	BT_AVRCP_HDR_SET_CTYPE(&cmd->hdr, cr == BT_AVCTP_CMD ? BT_AVRCP_CTYPE_STATUS
							     : BT_AVRCP_CTYPE_IMPLEMENTED_STABLE);
	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;
@@ -495,17 +519,17 @@ static struct net_buf *avrcp_create_unit_pdu(struct bt_avrcp *avrcp, bt_avctp_cr
static struct net_buf *avrcp_create_subunit_pdu(struct bt_avrcp *avrcp, bt_avctp_cr_t cr)
{
	struct net_buf *buf;
	struct bt_avrcp_unit_info_cmd *cmd;
	struct bt_avrcp_frame *cmd;

	buf = avrcp_create_pdu(avrcp, cr);
	if (!buf) {
		return buf;
		return NULL;
	}

	cmd = net_buf_add(buf, sizeof(*cmd));
	memset(cmd, 0, sizeof(*cmd));
	BT_AVRCP_HDR_SET_CTYPE(&cmd->hdr, cr == BT_AVCTP_CMD ? BT_AVRCP_CTYPE_STATUS
							     : BT_AVRCP_CTYPE_IMPLEMENTED_STABLE);
	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;
@@ -513,6 +537,27 @@ static struct net_buf *avrcp_create_subunit_pdu(struct bt_avrcp *avrcp, bt_avctp
	return buf;
}

static struct net_buf *avrcp_create_passthrough_pdu(struct bt_avrcp *avrcp, bt_avctp_cr_t cr,
						    bt_avrcp_ctype_t ctype)
{
	struct net_buf *buf;
	struct bt_avrcp_frame *cmd;

	buf = avrcp_create_pdu(avrcp, 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);
	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 int avrcp_send(struct bt_avrcp *avrcp, struct net_buf *buf)
{
	int err;
@@ -521,13 +566,15 @@ static int avrcp_send(struct bt_avrcp *avrcp, struct net_buf *buf)
		(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(avrcp_hdr);
	bt_avrcp_ctype_t ctype = BT_AVRCP_HDR_GET_CTYPE_OR_RSP(avrcp_hdr);
	bt_avrcp_subunit_type_t subunit_type = BT_AVRCP_HDR_GET_SUBUNIT_TYPE(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;
	}

@@ -576,6 +623,27 @@ int bt_avrcp_get_subunit_info(struct bt_avrcp *avrcp)
	return avrcp_send(avrcp, buf);
}

int bt_avrcp_passthrough(struct bt_avrcp *avrcp, bt_avrcp_opid_t operation_id,
			 bt_avrcp_button_state_t state, const uint8_t *payload, uint8_t len)
{
	struct net_buf *buf;
	uint8_t param[2];

	buf = avrcp_create_passthrough_pdu(avrcp, BT_AVCTP_CMD, BT_AVRCP_CTYPE_CONTROL);
	if (!buf) {
		return -ENOMEM;
	}

	param[0] = FIELD_PREP(GENMASK(7, 7), state) | FIELD_PREP(GENMASK(6, 0), operation_id);
	param[1] = len;
	net_buf_add_mem(buf, param, sizeof(param));
	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;
+3 −18
Original line number Diff line number Diff line
@@ -20,21 +20,6 @@
#define AVRCP_SUBUNIT_PAGE           (0) /* Fixed value according to AVRCP */
#define AVRCP_SUBUNIT_EXTENSION_COED (7) /* Fixed value according to TA Document 2001012 */

typedef enum __packed {
	BT_AVRCP_CTYPE_CONTROL = 0x0,
	BT_AVRCP_CTYPE_STATUS = 0x1,
	BT_AVRCP_CTYPE_SPECIFIC_INQUIRY = 0x2,
	BT_AVRCP_CTYPE_NOTIFY = 0x3,
	BT_AVRCP_CTYPE_GENERAL_INQUIRY = 0x4,
	BT_AVRCP_CTYPE_NOT_IMPLEMENTED = 0x8,
	BT_AVRCP_CTYPE_ACCEPTED = 0x9,
	BT_AVRCP_CTYPE_REJECTED = 0xA,
	BT_AVRCP_CTYPE_IN_TRANSITION = 0xB,
	BT_AVRCP_CTYPE_IMPLEMENTED_STABLE = 0xC,
	BT_AVRCP_CTYPE_CHANGED = 0xD,
	BT_AVRCP_CTYPE_INTERIM = 0xF,
} bt_avrcp_ctype_t;

typedef enum __packed {
	BT_AVRCP_SUBUNIT_ID_ZERO = 0x0,
	BT_AVRCP_SUBUNIT_ID_IGNORE = 0x7,
@@ -65,7 +50,7 @@ struct bt_avrcp_header {
} __packed;

/** The 4-bit command type or the 4-bit response code. */
#define BT_AVRCP_HDR_GET_CTYPE(hdr)        FIELD_GET(GENMASK(3, 0), ((hdr)->byte0))
#define BT_AVRCP_HDR_GET_CTYPE_OR_RSP(hdr)        FIELD_GET(GENMASK(3, 0), ((hdr)->byte0))
/** Taken together, the subunit_type and subunit_ID fields define the command recipient’s  address
 *  within the target. These fields enable the target to determine  whether the command is
 *  addressed to the target  unit, or to a specific subunit within the target. The values in these
@@ -80,7 +65,7 @@ struct bt_avrcp_header {
#define BT_AVRCP_HDR_GET_SUBUNIT_TYPE(hdr) FIELD_GET(GENMASK(7, 3), ((hdr)->byte1))

/** The 4-bit command type or the 4-bit response code. */
#define BT_AVRCP_HDR_SET_CTYPE(hdr, ctype)                                                         \
#define BT_AVRCP_HDR_SET_CTYPE_OR_RSP(hdr, ctype)                                                  \
	(hdr)->byte0 = (((hdr)->byte0) & ~GENMASK(3, 0)) | FIELD_PREP(GENMASK(3, 0), (ctype))
/** Taken together, the subunit_type and subunit_ID fields define the command recipient’s  address
 *  within the target. These fields enable the target to determine  whether the command is
@@ -97,7 +82,7 @@ struct bt_avrcp_header {
#define BT_AVRCP_HDR_SET_SUBUNIT_TYPE(hdr, subunit_type)                                           \
	(hdr)->byte1 = (((hdr)->byte1) & ~GENMASK(7, 3)) | FIELD_PREP(GENMASK(7, 3), (subunit_type))

struct bt_avrcp_unit_info_cmd {
struct bt_avrcp_frame {
	struct bt_avrcp_header hdr;
	uint8_t data[0];
} __packed;
+55 −9
Original line number Diff line number Diff line
@@ -61,11 +61,25 @@ static void avrcp_subunit_info_rsp(struct bt_avrcp *avrcp, struct bt_avrcp_subun
	}
}

static void avrcp_passthrough_rsp(struct bt_avrcp *avrcp, struct bt_avrcp_passthrough_rsp *rsp)
{
	if (rsp->response == BT_AVRCP_RSP_ACCEPTED) {
		bt_shell_print(
			"AVRCP passthough command accepted, operation id = 0x%02x, state = %d",
			rsp->operation_id, rsp->state);
	} else {
		bt_shell_print("AVRCP passthough command rejected, operation id = 0x%02x, state = "
			       "%d, response = %d",
			       rsp->operation_id, rsp->state, rsp->response);
	}
}

static struct bt_avrcp_cb avrcp_cb = {
	.connected = avrcp_connected,
	.disconnected = avrcp_disconnected,
	.unit_info_rsp = avrcp_unit_info_rsp,
	.subunit_info_rsp = avrcp_subunit_info_rsp,
	.passthrough_rsp = avrcp_passthrough_rsp,
};

static int register_cb(const struct shell *sh)
@@ -172,14 +186,46 @@ static int cmd_get_subunit_info(const struct shell *sh, int32_t argc, char *argv
	return 0;
}

SHELL_STATIC_SUBCMD_SET_CREATE(avrcp_cmds,
			       SHELL_CMD_ARG(register_cb, NULL, "register avrcp callbacks",
					     cmd_register_cb, 1, 0),
			       SHELL_CMD_ARG(connect, NULL, "<address>", cmd_connect, 2, 0),
			       SHELL_CMD_ARG(disconnect, NULL, "<address>", cmd_disconnect, 2, 0),
			       SHELL_CMD_ARG(get_unit, NULL, "<address>", cmd_get_unit_info, 2, 0),
			       SHELL_CMD_ARG(get_subunit, NULL, "<address>", cmd_get_subunit_info,
					     2, 0),
static int cmd_passthrough(const struct shell *sh, bt_avrcp_opid_t operation_id,
			   const uint8_t *payload, uint8_t len)
{
	if (!avrcp_registered) {
		if (register_cb(sh) != 0) {
			return -ENOEXEC;
		}
	}

	if (default_avrcp != NULL) {
		bt_avrcp_passthrough(default_avrcp, operation_id, BT_AVRCP_BUTTON_PRESSED, payload,
				     len);
		bt_avrcp_passthrough(default_avrcp, operation_id, BT_AVRCP_BUTTON_RELEASED, payload,
				     len);
	} else {
		shell_error(sh, "AVRCP is not connected");
	}

	return 0;
}

static int cmd_play(const struct shell *sh, int32_t argc, char *argv[])
{
	return cmd_passthrough(sh, BT_AVRCP_OPID_PLAY, NULL, 0);
}

static int cmd_pause(const struct shell *sh, int32_t argc, char *argv[])
{
	return cmd_passthrough(sh, BT_AVRCP_OPID_PAUSE, NULL, 0);
}

SHELL_STATIC_SUBCMD_SET_CREATE(
	avrcp_cmds,
	SHELL_CMD_ARG(register_cb, NULL, "register avrcp callbacks", cmd_register_cb, 1, 0),
	SHELL_CMD_ARG(connect, NULL, "connect AVRCP", cmd_connect, 1, 0),
	SHELL_CMD_ARG(disconnect, NULL, "disconnect AVRCP", cmd_disconnect, 1, 0),
	SHELL_CMD_ARG(get_unit, NULL, "get unit info", cmd_get_unit_info, 1, 0),
	SHELL_CMD_ARG(get_subunit, NULL, "get subunit info", cmd_get_subunit_info, 1, 0),
	SHELL_CMD_ARG(play, NULL, "request a play at the remote player", cmd_play, 1, 0),
	SHELL_CMD_ARG(pause, NULL, "request a pause at the remote player", cmd_pause, 1, 0),
	SHELL_SUBCMD_SET_END);

static int cmd_avrcp(const struct shell *sh, size_t argc, char **argv)