1 /** @file
2 * @brief Audio Video Remote Control Profile shell functions.
3 */
4
5 /*
6 * Copyright (c) 2024 Xiaomi InC.
7 *
8 * SPDX-License-Identifier: Apache-2.0
9 */
10
11 #include <errno.h>
12 #include <zephyr/types.h>
13 #include <stddef.h>
14 #include <stdlib.h>
15 #include <string.h>
16 #include <zephyr/sys/byteorder.h>
17 #include <zephyr/kernel.h>
18
19 #include <zephyr/settings/settings.h>
20
21 #include <zephyr/bluetooth/bluetooth.h>
22 #include <zephyr/bluetooth/classic/avrcp.h>
23 #include <zephyr/bluetooth/conn.h>
24 #include <zephyr/bluetooth/hci.h>
25 #include <zephyr/bluetooth/l2cap.h>
26
27 #include <zephyr/shell/shell.h>
28
29 #include "host/shell/bt.h"
30
31 struct bt_avrcp *default_avrcp;
32 static bool avrcp_registered;
33
avrcp_connected(struct bt_avrcp * avrcp)34 static void avrcp_connected(struct bt_avrcp *avrcp)
35 {
36 default_avrcp = avrcp;
37 shell_print(ctx_shell, "AVRCP connected");
38 }
39
avrcp_disconnected(struct bt_avrcp * avrcp)40 static void avrcp_disconnected(struct bt_avrcp *avrcp)
41 {
42 shell_print(ctx_shell, "AVRCP disconnected");
43 }
44
avrcp_unit_info_rsp(struct bt_avrcp * avrcp,struct bt_avrcp_unit_info_rsp * rsp)45 static void avrcp_unit_info_rsp(struct bt_avrcp *avrcp, struct bt_avrcp_unit_info_rsp *rsp)
46 {
47 shell_print(ctx_shell, "AVRCP unit info received, unit type = 0x%02x, company_id = 0x%06x",
48 rsp->unit_type, rsp->company_id);
49 }
50
avrcp_subunit_info_rsp(struct bt_avrcp * avrcp,struct bt_avrcp_subunit_info_rsp * rsp)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
64 static struct bt_avrcp_cb avrcp_cb = {
65 .connected = avrcp_connected,
66 .disconnected = avrcp_disconnected,
67 .unit_info_rsp = avrcp_unit_info_rsp,
68 .subunit_info_rsp = avrcp_subunit_info_rsp,
69 };
70
register_cb(const struct shell * sh)71 static int register_cb(const struct shell *sh)
72 {
73 int err;
74
75 if (avrcp_registered) {
76 return 0;
77 }
78
79 err = bt_avrcp_register_cb(&avrcp_cb);
80 if (!err) {
81 avrcp_registered = true;
82 shell_print(sh, "AVRCP callbacks registered");
83 } else {
84 shell_print(sh, "failed to register callbacks");
85 }
86
87 return err;
88 }
89
cmd_register_cb(const struct shell * sh,int32_t argc,char * argv[])90 static int cmd_register_cb(const struct shell *sh, int32_t argc, char *argv[])
91 {
92 if (avrcp_registered) {
93 shell_print(sh, "already registered");
94 return 0;
95 }
96
97 register_cb(sh);
98
99 return 0;
100 }
101
cmd_connect(const struct shell * sh,int32_t argc,char * argv[])102 static int cmd_connect(const struct shell *sh, int32_t argc, char *argv[])
103 {
104 if (!avrcp_registered) {
105 if (register_cb(sh) != 0) {
106 return -ENOEXEC;
107 }
108 }
109
110 if (!default_conn) {
111 shell_error(sh, "BR/EDR not connected");
112 return -ENOEXEC;
113 }
114
115 default_avrcp = bt_avrcp_connect(default_conn);
116 if (NULL == default_avrcp) {
117 shell_error(sh, "fail to connect AVRCP");
118 }
119
120 return 0;
121 }
122
cmd_disconnect(const struct shell * sh,int32_t argc,char * argv[])123 static int cmd_disconnect(const struct shell *sh, int32_t argc, char *argv[])
124 {
125 if (!avrcp_registered) {
126 if (register_cb(sh) != 0) {
127 return -ENOEXEC;
128 }
129 }
130
131 if (default_avrcp != NULL) {
132 bt_avrcp_disconnect(default_avrcp);
133 default_avrcp = NULL;
134 } else {
135 shell_error(sh, "AVRCP is not connected");
136 }
137
138 return 0;
139 }
140
cmd_get_unit_info(const struct shell * sh,int32_t argc,char * argv[])141 static int cmd_get_unit_info(const struct shell *sh, int32_t argc, char *argv[])
142 {
143 if (!avrcp_registered) {
144 if (register_cb(sh) != 0) {
145 return -ENOEXEC;
146 }
147 }
148
149 if (default_avrcp != NULL) {
150 bt_avrcp_get_unit_info(default_avrcp);
151 } else {
152 shell_error(sh, "AVRCP is not connected");
153 }
154
155 return 0;
156 }
157
cmd_get_subunit_info(const struct shell * sh,int32_t argc,char * argv[])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
175 SHELL_STATIC_SUBCMD_SET_CREATE(avrcp_cmds,
176 SHELL_CMD_ARG(register_cb, NULL, "register avrcp callbacks",
177 cmd_register_cb, 1, 0),
178 SHELL_CMD_ARG(connect, NULL, "<address>", cmd_connect, 2, 0),
179 SHELL_CMD_ARG(disconnect, NULL, "<address>", cmd_disconnect, 2, 0),
180 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),
183 SHELL_SUBCMD_SET_END);
184
cmd_avrcp(const struct shell * sh,size_t argc,char ** argv)185 static int cmd_avrcp(const struct shell *sh, size_t argc, char **argv)
186 {
187 if (argc == 1) {
188 shell_help(sh);
189 /* sh returns 1 when help is printed */
190 return 1;
191 }
192
193 shell_error(sh, "%s unknown parameter: %s", argv[0], argv[1]);
194
195 return -ENOEXEC;
196 }
197
198 SHELL_CMD_ARG_REGISTER(avrcp, &avrcp_cmds, "Bluetooth AVRCP sh commands", cmd_avrcp, 1, 1);
199