Skip to content

Commit 1789707

Browse files
gzh-terrykartben
authored andcommitted
Bluetooth: AVRCP: implementation for subunit info command.
This patch alllows to acquire the subunit info from the remote device. Signed-off-by: Zihao Gao <[email protected]>
1 parent c666c4d commit 1789707

File tree

4 files changed

+126
-4
lines changed

4 files changed

+126
-4
lines changed

include/zephyr/bluetooth/classic/avrcp.h

Lines changed: 28 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -24,6 +24,13 @@ struct bt_avrcp_unit_info_rsp {
2424
uint32_t company_id;
2525
};
2626

27+
struct bt_avrcp_subunit_info_rsp {
28+
uint8_t subunit_type;
29+
uint8_t max_subunit_id;
30+
const uint8_t *extended_subunit_type; /**< contains max_subunit_id items */
31+
const uint8_t *extended_subunit_id; /**< contains max_subunit_id items */
32+
};
33+
2734
struct bt_avrcp_cb {
2835
/** @brief An AVRCP connection has been established.
2936
*
@@ -41,14 +48,22 @@ struct bt_avrcp_cb {
4148
* @param avrcp AVRCP connection object.
4249
*/
4350
void (*disconnected)(struct bt_avrcp *avrcp);
44-
/** @brief Callback function for bt_avrcp_get_unit_info()
51+
/** @brief Callback function for bt_avrcp_get_unit_info().
4552
*
4653
* Called when the get unit info process is completed.
4754
*
4855
* @param avrcp AVRCP connection object.
4956
* @param rsp The response for UNIT INFO command.
5057
*/
5158
void (*unit_info_rsp)(struct bt_avrcp *avrcp, struct bt_avrcp_unit_info_rsp *rsp);
59+
/** @brief Callback function for bt_avrcp_get_subunit_info().
60+
*
61+
* Called when the get subunit info process is completed.
62+
*
63+
* @param avrcp AVRCP connection object.
64+
* @param rsp The response for SUBUNIT INFO command.
65+
*/
66+
void (*subunit_info_rsp)(struct bt_avrcp *avrcp, struct bt_avrcp_subunit_info_rsp *rsp);
5267
};
5368

5469
/** @brief Connect AVRCP.
@@ -86,14 +101,25 @@ int bt_avrcp_register_cb(const struct bt_avrcp_cb *cb);
86101

87102
/** @brief Get AVRCP Unit Info.
88103
*
89-
* This function obtains information that pertains to the unit as a whole.
104+
* This function obtains information that pertains to the AV/C unit as a whole.
90105
*
91106
* @param avrcp The AVRCP instance.
92107
*
93108
* @return 0 in case of success or error code in case of error.
94109
*/
95110
int bt_avrcp_get_unit_info(struct bt_avrcp *avrcp);
96111

112+
/** @brief Get AVRCP Subunit Info.
113+
*
114+
* This function obtains information about the subunit(s) of an AV/C unit. A device with AVRCP
115+
* may support other subunits than the panel subunit if other profiles co-exist in the device.
116+
*
117+
* @param avrcp The AVRCP instance.
118+
*
119+
* @return 0 in case of success or error code in case of error.
120+
*/
121+
int bt_avrcp_get_subunit_info(struct bt_avrcp *avrcp);
122+
97123
#ifdef __cplusplus
98124
}
99125
#endif

subsys/bluetooth/host/classic/avrcp.c

Lines changed: 62 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -260,7 +260,7 @@ static void avrcp_unit_info_handler(struct bt_avrcp *avrcp, struct net_buf *buf,
260260
if ((avrcp_cb != NULL) && (avrcp_cb->unit_info_rsp != NULL)) {
261261
net_buf_pull(buf, sizeof(*avrcp_hdr));
262262
net_buf_pull_u8(buf); /* Always 0x07 */
263-
rsp.unit_type = (net_buf_pull_u8(buf) >> 3); /* Bit [8:4] Shall be 0x09 */
263+
rsp.unit_type = FIELD_GET(GENMASK(7, 3), net_buf_pull_u8(buf));
264264
rsp.company_id = net_buf_pull_be24(buf);
265265
avrcp_cb->unit_info_rsp(avrcp, &rsp);
266266
}
@@ -270,7 +270,28 @@ static void avrcp_unit_info_handler(struct bt_avrcp *avrcp, struct net_buf *buf,
270270
static void avrcp_subunit_info_handler(struct bt_avrcp *avrcp, struct net_buf *buf,
271271
bt_avctp_cr_t cr)
272272
{
273-
/* ToDo */
273+
struct bt_avrcp_header *avrcp_hdr;
274+
struct bt_avrcp_subunit_info_rsp rsp;
275+
uint8_t tmp;
276+
277+
if (cr == BT_AVCTP_CMD) {
278+
/* ToDo */
279+
} else { /* BT_AVCTP_RESPONSE */
280+
if ((avrcp_cb != NULL) && (avrcp_cb->subunit_info_rsp != NULL)) {
281+
net_buf_pull(buf, sizeof(*avrcp_hdr));
282+
net_buf_pull_u8(buf); /* Always 0x07 */
283+
tmp = net_buf_pull_u8(buf);
284+
rsp.subunit_type = FIELD_GET(GENMASK(7, 3), tmp);
285+
rsp.max_subunit_id = FIELD_GET(GENMASK(2, 0), tmp);
286+
if (buf->len < (rsp.max_subunit_id << 1)) {
287+
LOG_ERR("Invalid subunit info response");
288+
return;
289+
}
290+
rsp.extended_subunit_type = buf->data;
291+
rsp.extended_subunit_id = rsp.extended_subunit_type + rsp.max_subunit_id;
292+
avrcp_cb->subunit_info_rsp(avrcp, &rsp);
293+
}
294+
}
274295
}
275296

276297
static void avrcp_pass_through_handler(struct bt_avrcp *avrcp, struct net_buf *buf,
@@ -459,6 +480,27 @@ static struct net_buf *avrcp_create_unit_pdu(struct bt_avrcp *avrcp, bt_avctp_cr
459480
return buf;
460481
}
461482

483+
static struct net_buf *avrcp_create_subunit_pdu(struct bt_avrcp *avrcp, bt_avctp_cr_t cr)
484+
{
485+
struct net_buf *buf;
486+
struct bt_avrcp_unit_info_cmd *cmd;
487+
488+
buf = avrcp_create_pdu(avrcp, cr);
489+
if (!buf) {
490+
return buf;
491+
}
492+
493+
cmd = net_buf_add(buf, sizeof(*cmd));
494+
memset(cmd, 0, sizeof(*cmd));
495+
BT_AVRCP_HDR_SET_CTYPE(&cmd->hdr, cr == BT_AVCTP_CMD ? BT_AVRCP_CTYPE_STATUS
496+
: BT_AVRCP_CTYPE_IMPLEMENTED_STABLE);
497+
BT_AVRCP_HDR_SET_SUBUNIT_ID(&cmd->hdr, BT_AVRCP_SUBUNIT_ID_IGNORE);
498+
BT_AVRCP_HDR_SET_SUBUNIT_TYPE(&cmd->hdr, BT_AVRCP_SUBUNIT_TYPE_UNIT);
499+
cmd->hdr.opcode = BT_AVRCP_OPC_SUBUNIT_INFO;
500+
501+
return buf;
502+
}
503+
462504
static int avrcp_send(struct bt_avrcp *avrcp, struct net_buf *buf)
463505
{
464506
int err;
@@ -504,6 +546,24 @@ int bt_avrcp_get_unit_info(struct bt_avrcp *avrcp)
504546
return avrcp_send(avrcp, buf);
505547
}
506548

549+
int bt_avrcp_get_subunit_info(struct bt_avrcp *avrcp)
550+
{
551+
struct net_buf *buf;
552+
uint8_t param[5];
553+
554+
buf = avrcp_create_subunit_pdu(avrcp, BT_AVCTP_CMD);
555+
if (!buf) {
556+
return -ENOMEM;
557+
}
558+
559+
memset(param, 0xFF, ARRAY_SIZE(param));
560+
param[0] = FIELD_PREP(GENMASK(6, 4), AVRCP_SUBUNIT_PAGE) |
561+
FIELD_PREP(GENMASK(2, 0), AVRCP_SUBUNIT_EXTENSION_COED);
562+
net_buf_add_mem(buf, param, sizeof(param));
563+
564+
return avrcp_send(avrcp, buf);
565+
}
566+
507567
int bt_avrcp_register_cb(const struct bt_avrcp_cb *cb)
508568
{
509569
avrcp_cb = cb;

subsys/bluetooth/host/classic/avrcp_internal.h

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,9 @@
1717
#define AVRCP_CAT_3 BIT(2) /* Tuner */
1818
#define AVRCP_CAT_4 BIT(3) /* Menu */
1919

20+
#define AVRCP_SUBUNIT_PAGE (0) /* Fixed value according to AVRCP */
21+
#define AVRCP_SUBUNIT_EXTENSION_COED (7) /* Fixed value according to TA Document 2001012 */
22+
2023
typedef enum __packed {
2124
BT_AVRCP_CTYPE_CONTROL = 0x0,
2225
BT_AVRCP_CTYPE_STATUS = 0x1,

subsys/bluetooth/host/classic/shell/avrcp.c

Lines changed: 33 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -48,10 +48,24 @@ static void avrcp_unit_info_rsp(struct bt_avrcp *avrcp, struct bt_avrcp_unit_inf
4848
rsp->unit_type, rsp->company_id);
4949
}
5050

51+
static void avrcp_subunit_info_rsp(struct bt_avrcp *avrcp, struct bt_avrcp_subunit_info_rsp *rsp)
52+
{
53+
int i;
54+
55+
shell_print(ctx_shell,
56+
"AVRCP subunit info received, subunit type = 0x%02x, extended subunit = %d",
57+
rsp->subunit_type, rsp->max_subunit_id);
58+
for (i = 0; i < rsp->max_subunit_id; i++) {
59+
shell_print(ctx_shell, "extended subunit id = %d, subunit type = 0x%02x",
60+
rsp->extended_subunit_id[i], rsp->extended_subunit_type[i]);
61+
}
62+
}
63+
5164
static struct bt_avrcp_cb avrcp_cb = {
5265
.connected = avrcp_connected,
5366
.disconnected = avrcp_disconnected,
5467
.unit_info_rsp = avrcp_unit_info_rsp,
68+
.subunit_info_rsp = avrcp_subunit_info_rsp,
5569
};
5670

5771
static int register_cb(const struct shell *sh)
@@ -141,12 +155,31 @@ static int cmd_get_unit_info(const struct shell *sh, int32_t argc, char *argv[])
141155
return 0;
142156
}
143157

158+
static int cmd_get_subunit_info(const struct shell *sh, int32_t argc, char *argv[])
159+
{
160+
if (!avrcp_registered) {
161+
if (register_cb(sh) != 0) {
162+
return -ENOEXEC;
163+
}
164+
}
165+
166+
if (default_avrcp != NULL) {
167+
bt_avrcp_get_subunit_info(default_avrcp);
168+
} else {
169+
shell_error(sh, "AVRCP is not connected");
170+
}
171+
172+
return 0;
173+
}
174+
144175
SHELL_STATIC_SUBCMD_SET_CREATE(avrcp_cmds,
145176
SHELL_CMD_ARG(register_cb, NULL, "register avrcp callbacks",
146177
cmd_register_cb, 1, 0),
147178
SHELL_CMD_ARG(connect, NULL, "<address>", cmd_connect, 2, 0),
148179
SHELL_CMD_ARG(disconnect, NULL, "<address>", cmd_disconnect, 2, 0),
149180
SHELL_CMD_ARG(get_unit, NULL, "<address>", cmd_get_unit_info, 2, 0),
181+
SHELL_CMD_ARG(get_subunit, NULL, "<address>", cmd_get_subunit_info,
182+
2, 0),
150183
SHELL_SUBCMD_SET_END);
151184

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

0 commit comments

Comments
 (0)