Skip to content

Commit 9cfbed6

Browse files
makeshicfriedt
authored andcommitted
Bluetooth: Shell: AVRCP: Shell interface for browsing support
- Add shell interface for browsing connect and disconnect testing and SetBrowsedPlayer command testing. Signed-off-by: Make Shi <[email protected]>
1 parent 240ad5c commit 9cfbed6

File tree

1 file changed

+280
-0
lines changed
  • subsys/bluetooth/host/classic/shell

1 file changed

+280
-0
lines changed

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

Lines changed: 280 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -29,6 +29,12 @@
2929
#include "host/shell/bt.h"
3030
#include "common/bt_shell_private.h"
3131

32+
NET_BUF_POOL_DEFINE(avrcp_tx_pool, CONFIG_BT_MAX_CONN,
33+
BT_L2CAP_BUF_SIZE(CONFIG_BT_L2CAP_TX_MTU),
34+
CONFIG_BT_CONN_TX_USER_DATA_SIZE, NULL);
35+
36+
#define FOLDER_NAME_HEX_BUF_LEN 80
37+
3238
struct bt_avrcp_ct *default_ct;
3339
struct bt_avrcp_tg *default_tg;
3440
static bool avrcp_ct_registered;
@@ -60,6 +66,16 @@ static void avrcp_ct_disconnected(struct bt_avrcp_ct *ct)
6066
default_ct = NULL;
6167
}
6268

69+
static void avrcp_ct_browsing_connected(struct bt_conn *conn, struct bt_avrcp_ct *ct)
70+
{
71+
bt_shell_print("AVRCP CT browsing connected");
72+
}
73+
74+
static void avrcp_ct_browsing_disconnected(struct bt_avrcp_ct *ct)
75+
{
76+
bt_shell_print("AVRCP CT browsing disconnected");
77+
}
78+
6379
static void avrcp_get_cap_rsp(struct bt_avrcp_ct *ct, uint8_t tid,
6480
const struct bt_avrcp_get_cap_rsp *rsp)
6581
{
@@ -115,13 +131,70 @@ static void avrcp_passthrough_rsp(struct bt_avrcp_ct *ct, uint8_t tid, bt_avrcp_
115131
}
116132
}
117133

134+
static void avrcp_browsed_player_rsp(struct bt_avrcp_ct *ct, uint8_t tid,
135+
struct net_buf *buf)
136+
{
137+
struct bt_avrcp_set_browsed_player_rsp *rsp;
138+
struct bt_avrcp_folder_name *folder_name;
139+
140+
rsp = net_buf_pull_mem(buf, sizeof(*rsp));
141+
if (rsp->status != BT_AVRCP_STATUS_OPERATION_COMPLETED) {
142+
bt_shell_print("AVRCP set browsed player failed, tid = %d, status = 0x%02x",
143+
tid, rsp->status);
144+
return;
145+
}
146+
147+
bt_shell_print("AVRCP set browsed player success, tid = %d", tid);
148+
bt_shell_print(" UID Counter: %u", sys_be16_to_cpu(rsp->uid_counter));
149+
bt_shell_print(" Number of Items: %u", sys_be32_to_cpu(rsp->num_items));
150+
bt_shell_print(" Charset ID: 0x%04X", sys_be16_to_cpu(rsp->charset_id));
151+
bt_shell_print(" Folder Depth: %u", rsp->folder_depth);
152+
153+
while (buf->len > 0) {
154+
if (buf->len < sizeof(struct bt_avrcp_folder_name)) {
155+
bt_shell_print("incompleted message");
156+
break;
157+
}
158+
folder_name = net_buf_pull_mem(buf, sizeof(struct bt_avrcp_folder_name));
159+
folder_name->folder_name_len = sys_be16_to_cpu(folder_name->folder_name_len);
160+
if (buf->len < folder_name->folder_name_len) {
161+
bt_shell_print("incompleted message for folder_name");
162+
break;
163+
}
164+
net_buf_pull_mem(buf, folder_name->folder_name_len);
165+
166+
if (sys_be16_to_cpu(rsp->charset_id) == BT_AVRCP_CHARSET_UTF8) {
167+
bt_shell_print("Raw folder name:");
168+
for (int i = 0; i < folder_name->folder_name_len; i++) {
169+
bt_shell_print("%c", folder_name->folder_name[i]);
170+
}
171+
} else {
172+
bt_shell_print(" Get folder Name : ");
173+
bt_shell_hexdump(folder_name->folder_name, folder_name->folder_name_len);
174+
}
175+
if (rsp->folder_depth > 0) {
176+
rsp->folder_depth--;
177+
} else {
178+
bt_shell_warn("Folder depth is mismatched with received data");
179+
break;
180+
}
181+
}
182+
183+
if (rsp->folder_depth > 0) {
184+
bt_shell_print("folder depth mismatch: expected 0, got %u", rsp->folder_depth);
185+
}
186+
}
187+
118188
static struct bt_avrcp_ct_cb app_avrcp_ct_cb = {
119189
.connected = avrcp_ct_connected,
120190
.disconnected = avrcp_ct_disconnected,
191+
.browsing_connected = avrcp_ct_browsing_connected,
192+
.browsing_disconnected = avrcp_ct_browsing_disconnected,
121193
.get_cap_rsp = avrcp_get_cap_rsp,
122194
.unit_info_rsp = avrcp_unit_info_rsp,
123195
.subunit_info_rsp = avrcp_subunit_info_rsp,
124196
.passthrough_rsp = avrcp_passthrough_rsp,
197+
.browsed_player_rsp = avrcp_browsed_player_rsp,
125198
};
126199

127200
static void avrcp_tg_connected(struct bt_conn *conn, struct bt_avrcp_tg *tg)
@@ -136,16 +209,36 @@ static void avrcp_tg_disconnected(struct bt_avrcp_tg *tg)
136209
default_tg = NULL;
137210
}
138211

212+
static void avrcp_tg_browsing_connected(struct bt_conn *conn, struct bt_avrcp_tg *tg)
213+
{
214+
bt_shell_print("AVRCP TG browsing connected");
215+
}
216+
139217
static void avrcp_unit_info_req(struct bt_avrcp_tg *tg, uint8_t tid)
140218
{
141219
bt_shell_print("AVRCP unit info request received");
142220
tg_tid = tid;
143221
}
144222

223+
static void avrcp_tg_browsing_disconnected(struct bt_avrcp_tg *tg)
224+
{
225+
bt_shell_print("AVRCP TG browsing disconnected");
226+
}
227+
228+
static void avrcp_set_browsed_player_req(struct bt_avrcp_tg *tg, uint8_t tid,
229+
uint16_t player_id)
230+
{
231+
bt_shell_print("AVRCP set browsed player request received, player_id = %u", player_id);
232+
tg_tid = tid;
233+
}
234+
145235
static struct bt_avrcp_tg_cb app_avrcp_tg_cb = {
146236
.connected = avrcp_tg_connected,
147237
.disconnected = avrcp_tg_disconnected,
238+
.browsing_connected = avrcp_tg_browsing_connected,
239+
.browsing_disconnected = avrcp_tg_browsing_disconnected,
148240
.unit_info_req = avrcp_unit_info_req,
241+
.set_browsed_player_req = avrcp_set_browsed_player_req,
149242
};
150243

151244
static int register_ct_cb(const struct shell *sh)
@@ -256,6 +349,53 @@ static int cmd_disconnect(const struct shell *sh, int32_t argc, char *argv[])
256349
return 0;
257350
}
258351

352+
static int cmd_browsing_connect(const struct shell *sh, int32_t argc, char *argv[])
353+
{
354+
int err;
355+
356+
if (!avrcp_ct_registered && register_ct_cb(sh) != 0) {
357+
return -ENOEXEC;
358+
}
359+
360+
if (default_conn == NULL) {
361+
shell_error(sh, "BR/EDR not connected");
362+
return -ENOEXEC;
363+
}
364+
365+
err = bt_avrcp_browsing_connect(default_conn);
366+
if (err < 0) {
367+
shell_error(sh, "fail to connect AVRCP browsing");
368+
} else {
369+
shell_print(sh, "AVRCP browsing connect request sent");
370+
}
371+
372+
return err;
373+
}
374+
375+
static int cmd_browsing_disconnect(const struct shell *sh, int32_t argc, char *argv[])
376+
{
377+
int err;
378+
379+
if (default_conn == NULL) {
380+
shell_print(sh, "Not connected");
381+
return -ENOEXEC;
382+
}
383+
384+
if ((default_ct != NULL) || (default_tg != NULL)) {
385+
err = bt_avrcp_browsing_disconnect(default_conn);
386+
if (err < 0) {
387+
shell_error(sh, "fail to disconnect AVRCP browsing");
388+
} else {
389+
shell_print(sh, "AVRCP browsing disconnect request sent");
390+
}
391+
} else {
392+
shell_error(sh, "AVRCP is not connected");
393+
err = -ENOEXEC;
394+
}
395+
396+
return err;
397+
}
398+
259399
static int cmd_get_unit_info(const struct shell *sh, int32_t argc, char *argv[])
260400
{
261401
if (!avrcp_ct_registered && register_ct_cb(sh) != 0) {
@@ -364,6 +504,139 @@ static int cmd_get_cap(const struct shell *sh, int32_t argc, char *argv[])
364504
return 0;
365505
}
366506

507+
static int cmd_set_browsed_player(const struct shell *sh, int32_t argc, char *argv[])
508+
{
509+
uint16_t player_id;
510+
int err;
511+
512+
if (!avrcp_ct_registered && register_ct_cb(sh) != 0) {
513+
return -ENOEXEC;
514+
}
515+
516+
if (default_ct == NULL) {
517+
shell_error(sh, "AVRCP is not connected");
518+
return -ENOEXEC;
519+
}
520+
521+
player_id = (uint16_t)strtoul(argv[1], NULL, 0);
522+
523+
err = bt_avrcp_ct_set_browsed_player(default_ct, get_next_tid(), player_id);
524+
if (err < 0) {
525+
shell_error(sh, "fail to set browsed player");
526+
} else {
527+
shell_print(sh, "AVRCP send set browsed player req");
528+
}
529+
530+
return 0;
531+
}
532+
533+
static int cmd_send_set_browsed_player_rsp(const struct shell *sh, int32_t argc, char *argv[])
534+
{
535+
struct bt_avrcp_set_browsed_player_rsp *rsp;
536+
struct bt_avrcp_folder_name *folder_name;
537+
char *folder_name_str = "Music";
538+
uint8_t folder_name_hex[FOLDER_NAME_HEX_BUF_LEN];
539+
uint16_t folder_name_len = 0;
540+
struct net_buf *buf;
541+
uint16_t param_len;
542+
int err;
543+
544+
if (!avrcp_tg_registered && register_tg_cb(sh) != 0) {
545+
return -ENOEXEC;
546+
}
547+
548+
if (default_tg == NULL) {
549+
shell_error(sh, "AVRCP TG is not connected");
550+
return -ENOEXEC;
551+
}
552+
553+
buf = bt_avrcp_create_pdu(&avrcp_tx_pool);
554+
if (buf == NULL) {
555+
shell_error(sh, "Failed to allocate buffer for AVRCP browsing response");
556+
return -ENOMEM;
557+
}
558+
559+
if (net_buf_tailroom(buf) < sizeof(struct bt_avrcp_set_browsed_player_rsp)) {
560+
shell_error(sh, "Not enough tailroom in buffer for browsed player rsp");
561+
goto failed;
562+
}
563+
564+
rsp = net_buf_add(buf, sizeof(*rsp));
565+
/* Set default rsp */
566+
rsp->status = BT_AVRCP_STATUS_OPERATION_COMPLETED;
567+
rsp->uid_counter = sys_cpu_to_be16(0x0001U);
568+
rsp->num_items = sys_cpu_to_be32(100U);
569+
rsp->charset_id = sys_cpu_to_be16(BT_AVRCP_CHARSET_UTF8);
570+
rsp->folder_depth = 1;
571+
572+
/* Parse command line arguments or use default values */
573+
if (argc >= 2) {
574+
rsp->status = (uint8_t)strtoul(argv[1], NULL, 0);
575+
}
576+
577+
if (argc >= 3) {
578+
rsp->uid_counter = sys_cpu_to_be16((uint16_t)strtoul(argv[2], NULL, 0));
579+
}
580+
581+
if (argc >= 4) {
582+
rsp->num_items = sys_cpu_to_be32((uint32_t)strtoul(argv[3], NULL, 0));
583+
}
584+
585+
if (argc >= 5) {
586+
rsp->charset_id = sys_cpu_to_be16((uint16_t)strtoul(argv[4], NULL, 0));
587+
}
588+
589+
if (rsp->charset_id == sys_cpu_to_be16(BT_AVRCP_CHARSET_UTF8)) {
590+
if (argc >= 6) {
591+
folder_name_str = argv[5];
592+
}
593+
folder_name_len = strlen(folder_name_str);
594+
} else {
595+
if (argc >= 6) {
596+
folder_name_len = hex2bin(argv[5], strlen(argv[5]), folder_name_hex,
597+
sizeof(folder_name_hex));
598+
if (folder_name_len == 0) {
599+
shell_error(sh, "Failed to get folder_name from %s", argv[5]);
600+
}
601+
} else {
602+
shell_error(sh, "Please input hex string for folder_name");
603+
goto failed;
604+
}
605+
}
606+
607+
param_len = folder_name_len + sizeof(struct bt_avrcp_folder_name);
608+
if (net_buf_tailroom(buf) < param_len) {
609+
shell_error(sh, "Not enough tailroom in buffer for param");
610+
goto failed;
611+
}
612+
613+
folder_name = net_buf_add(buf, sizeof(*folder_name));
614+
folder_name->folder_name_len = sys_cpu_to_be16(folder_name_len);
615+
if (rsp->charset_id == sys_cpu_to_be16(BT_AVRCP_CHARSET_UTF8)) {
616+
net_buf_add_mem(buf, folder_name_str, folder_name_len);
617+
} else {
618+
net_buf_add_mem(buf, folder_name_hex, folder_name_len);
619+
}
620+
621+
err = bt_avrcp_tg_send_set_browsed_player_rsp(default_tg, tg_tid, buf);
622+
if (err == 0) {
623+
shell_print(sh, "Send set browsed player response, status = 0x%02x", rsp->status);
624+
} else {
625+
shell_error(sh, "Failed to send set browsed player response, err = %d", err);
626+
goto failed;
627+
}
628+
629+
return 0;
630+
failed:
631+
net_buf_unref(buf);
632+
return -ENOEXEC;
633+
}
634+
635+
#define HELP_BROWSED_PLAYER_RSP \
636+
"Send SetBrowsedPlayer response\n" \
637+
"Usage: send_browsed_player_rsp [status] [uid_counter] [num_items] " \
638+
"[charset_id] [folder_name]"
639+
367640
SHELL_STATIC_SUBCMD_SET_CREATE(
368641
ct_cmds,
369642
SHELL_CMD_ARG(register_cb, NULL, "register avrcp ct callbacks", cmd_register_ct_cb, 1, 0),
@@ -373,12 +646,16 @@ SHELL_STATIC_SUBCMD_SET_CREATE(
373646
0),
374647
SHELL_CMD_ARG(play, NULL, "request a play at the remote player", cmd_play, 1, 0),
375648
SHELL_CMD_ARG(pause, NULL, "request a pause at the remote player", cmd_pause, 1, 0),
649+
SHELL_CMD_ARG(set_browsed_player, NULL, "set browsed player <player_id>",
650+
cmd_set_browsed_player, 2, 0),
376651
SHELL_SUBCMD_SET_END);
377652

378653
SHELL_STATIC_SUBCMD_SET_CREATE(
379654
tg_cmds,
380655
SHELL_CMD_ARG(register_cb, NULL, "register avrcp tg callbacks", cmd_register_tg_cb, 1, 0),
381656
SHELL_CMD_ARG(send_unit_rsp, NULL, "send unit info response", cmd_send_unit_info_rsp, 1, 0),
657+
SHELL_CMD_ARG(send_browsed_player_rsp, NULL, HELP_BROWSED_PLAYER_RSP,
658+
cmd_send_set_browsed_player_rsp, 1, 5),
382659
SHELL_SUBCMD_SET_END);
383660

384661
static int cmd_avrcp(const struct shell *sh, size_t argc, char **argv)
@@ -398,6 +675,9 @@ SHELL_STATIC_SUBCMD_SET_CREATE(
398675
avrcp_cmds,
399676
SHELL_CMD_ARG(connect, NULL, "connect AVRCP", cmd_connect, 1, 0),
400677
SHELL_CMD_ARG(disconnect, NULL, "disconnect AVRCP", cmd_disconnect, 1, 0),
678+
SHELL_CMD_ARG(browsing_connect, NULL, "connect browsing AVRCP", cmd_browsing_connect, 1, 0),
679+
SHELL_CMD_ARG(browsing_disconnect, NULL, "disconnect browsing AVRCP",
680+
cmd_browsing_disconnect, 1, 0),
401681
SHELL_CMD(ct, &ct_cmds, "AVRCP CT shell commands", cmd_avrcp),
402682
SHELL_CMD(tg, &tg_cmds, "AVRCP TG shell commands", cmd_avrcp),
403683
SHELL_SUBCMD_SET_END);

0 commit comments

Comments
 (0)