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 #include "common/bt_shell_private.h"
31 
32 struct bt_avrcp_ct *default_ct;
33 struct bt_avrcp_tg *default_tg;
34 static bool avrcp_ct_registered;
35 static bool avrcp_tg_registered;
36 static uint8_t local_tid;
37 static uint8_t tg_tid;
38 
get_next_tid(void)39 static uint8_t get_next_tid(void)
40 {
41 	uint8_t ret = local_tid;
42 
43 	local_tid++;
44 	local_tid &= 0x0F;
45 
46 	return ret;
47 }
48 
avrcp_ct_connected(struct bt_conn * conn,struct bt_avrcp_ct * ct)49 static void avrcp_ct_connected(struct bt_conn *conn, struct bt_avrcp_ct *ct)
50 {
51 	bt_shell_print("AVRCP CT connected");
52 	default_ct = ct;
53 	local_tid = 0;
54 }
55 
avrcp_ct_disconnected(struct bt_avrcp_ct * ct)56 static void avrcp_ct_disconnected(struct bt_avrcp_ct *ct)
57 {
58 	bt_shell_print("AVRCP CT disconnected");
59 	local_tid = 0;
60 	default_ct = NULL;
61 }
62 
avrcp_get_cap_rsp(struct bt_avrcp_ct * ct,uint8_t tid,const struct bt_avrcp_get_cap_rsp * rsp)63 static void avrcp_get_cap_rsp(struct bt_avrcp_ct *ct, uint8_t tid,
64 			      const struct bt_avrcp_get_cap_rsp *rsp)
65 {
66 	uint8_t i;
67 
68 	switch (rsp->cap_id) {
69 	case BT_AVRCP_CAP_COMPANY_ID:
70 		for (i = 0; i < rsp->cap_cnt; i++) {
71 			bt_shell_print("Remote CompanyID = 0x%06x",
72 				       sys_get_be24(&rsp->cap[BT_AVRCP_COMPANY_ID_SIZE * i]));
73 		}
74 		break;
75 	case BT_AVRCP_CAP_EVENTS_SUPPORTED:
76 		for (i = 0; i < rsp->cap_cnt; i++) {
77 			bt_shell_print("Remote supported EventID = 0x%02x", rsp->cap[i]);
78 		}
79 		break;
80 	}
81 }
82 
avrcp_unit_info_rsp(struct bt_avrcp_ct * ct,uint8_t tid,struct bt_avrcp_unit_info_rsp * rsp)83 static void avrcp_unit_info_rsp(struct bt_avrcp_ct *ct, uint8_t tid,
84 				struct bt_avrcp_unit_info_rsp *rsp)
85 {
86 	bt_shell_print("AVRCP unit info received, unit type = 0x%02x, company_id = 0x%06x",
87 		       rsp->unit_type, rsp->company_id);
88 }
89 
avrcp_subunit_info_rsp(struct bt_avrcp_ct * ct,uint8_t tid,struct bt_avrcp_subunit_info_rsp * rsp)90 static void avrcp_subunit_info_rsp(struct bt_avrcp_ct *ct, uint8_t tid,
91 				   struct bt_avrcp_subunit_info_rsp *rsp)
92 {
93 	int i;
94 
95 	bt_shell_print("AVRCP subunit info received, subunit type = 0x%02x, extended subunit = %d",
96 		       rsp->subunit_type, rsp->max_subunit_id);
97 	for (i = 0; i < rsp->max_subunit_id; i++) {
98 		bt_shell_print("extended subunit id = %d, subunit type = 0x%02x",
99 			       rsp->extended_subunit_id[i], rsp->extended_subunit_type[i]);
100 	}
101 }
102 
avrcp_passthrough_rsp(struct bt_avrcp_ct * ct,uint8_t tid,bt_avrcp_rsp_t result,const struct bt_avrcp_passthrough_rsp * rsp)103 static void avrcp_passthrough_rsp(struct bt_avrcp_ct *ct, uint8_t tid, bt_avrcp_rsp_t result,
104 				  const struct bt_avrcp_passthrough_rsp *rsp)
105 {
106 	if (result == BT_AVRCP_RSP_ACCEPTED) {
107 		bt_shell_print(
108 			"AVRCP passthough command accepted, operation id = 0x%02x, state = %d",
109 			BT_AVRCP_PASSTHROUGH_GET_OPID(rsp), BT_AVRCP_PASSTHROUGH_GET_STATE(rsp));
110 	} else {
111 		bt_shell_print("AVRCP passthough command rejected, operation id = 0x%02x, state = "
112 			       "%d, response = %d",
113 			       BT_AVRCP_PASSTHROUGH_GET_OPID(rsp),
114 			       BT_AVRCP_PASSTHROUGH_GET_STATE(rsp), result);
115 	}
116 }
117 
118 static struct bt_avrcp_ct_cb app_avrcp_ct_cb = {
119 	.connected = avrcp_ct_connected,
120 	.disconnected = avrcp_ct_disconnected,
121 	.get_cap_rsp = avrcp_get_cap_rsp,
122 	.unit_info_rsp = avrcp_unit_info_rsp,
123 	.subunit_info_rsp = avrcp_subunit_info_rsp,
124 	.passthrough_rsp = avrcp_passthrough_rsp,
125 };
126 
avrcp_tg_connected(struct bt_conn * conn,struct bt_avrcp_tg * tg)127 static void avrcp_tg_connected(struct bt_conn *conn, struct bt_avrcp_tg *tg)
128 {
129 	bt_shell_print("AVRCP TG connected");
130 	default_tg = tg;
131 }
132 
avrcp_tg_disconnected(struct bt_avrcp_tg * tg)133 static void avrcp_tg_disconnected(struct bt_avrcp_tg *tg)
134 {
135 	bt_shell_print("AVRCP TG disconnected");
136 	default_tg = NULL;
137 }
138 
avrcp_unit_info_req(struct bt_avrcp_tg * tg,uint8_t tid)139 static void avrcp_unit_info_req(struct bt_avrcp_tg *tg, uint8_t tid)
140 {
141 	bt_shell_print("AVRCP unit info request received");
142 	tg_tid = tid;
143 }
144 
145 static struct bt_avrcp_tg_cb app_avrcp_tg_cb = {
146 	.connected = avrcp_tg_connected,
147 	.disconnected = avrcp_tg_disconnected,
148 	.unit_info_req = avrcp_unit_info_req,
149 };
150 
register_ct_cb(const struct shell * sh)151 static int register_ct_cb(const struct shell *sh)
152 {
153 	int err;
154 
155 	if (avrcp_ct_registered) {
156 		return 0;
157 	}
158 
159 	err = bt_avrcp_ct_register_cb(&app_avrcp_ct_cb);
160 	if (!err) {
161 		avrcp_ct_registered = true;
162 		shell_print(sh, "AVRCP CT callbacks registered");
163 	} else {
164 		shell_print(sh, "failed to register AVRCP CT callbacks");
165 	}
166 
167 	return err;
168 }
169 
cmd_register_ct_cb(const struct shell * sh,int32_t argc,char * argv[])170 static int cmd_register_ct_cb(const struct shell *sh, int32_t argc, char *argv[])
171 {
172 	if (avrcp_ct_registered) {
173 		shell_print(sh, "already registered");
174 		return 0;
175 	}
176 
177 	register_ct_cb(sh);
178 
179 	return 0;
180 }
181 
register_tg_cb(const struct shell * sh)182 static int register_tg_cb(const struct shell *sh)
183 {
184 	int err;
185 
186 	if (avrcp_tg_registered) {
187 		return 0;
188 	}
189 
190 	err = bt_avrcp_tg_register_cb(&app_avrcp_tg_cb);
191 	if (!err) {
192 		avrcp_tg_registered = true;
193 		shell_print(sh, "AVRCP TG callbacks registered");
194 	} else {
195 		shell_print(sh, "failed to register AVRCP TG callbacks");
196 	}
197 
198 	return err;
199 }
200 
cmd_register_tg_cb(const struct shell * sh,int32_t argc,char * argv[])201 static int cmd_register_tg_cb(const struct shell *sh, int32_t argc, char *argv[])
202 {
203 	if (avrcp_tg_registered) {
204 		shell_print(sh, "already registered");
205 		return 0;
206 	}
207 
208 	register_tg_cb(sh);
209 
210 	return 0;
211 }
212 
cmd_connect(const struct shell * sh,int32_t argc,char * argv[])213 static int cmd_connect(const struct shell *sh, int32_t argc, char *argv[])
214 {
215 	int err;
216 
217 	if (!avrcp_ct_registered && register_ct_cb(sh) != 0) {
218 		return -ENOEXEC;
219 	}
220 
221 	if (!avrcp_tg_registered && register_tg_cb(sh) != 0) {
222 		return -ENOEXEC;
223 	}
224 
225 	if (!default_conn) {
226 		shell_error(sh, "BR/EDR not connected");
227 		return -ENOEXEC;
228 	}
229 
230 	err = bt_avrcp_connect(default_conn);
231 	if (err) {
232 		shell_error(sh, "fail to connect AVRCP");
233 	}
234 
235 	return 0;
236 }
237 
cmd_disconnect(const struct shell * sh,int32_t argc,char * argv[])238 static int cmd_disconnect(const struct shell *sh, int32_t argc, char *argv[])
239 {
240 	if ((!avrcp_ct_registered) && (!avrcp_tg_registered)) {
241 		shell_error(sh, "Neither CT nor TG callbacks are registered.");
242 		return -ENOEXEC;
243 	}
244 
245 	if (!default_conn) {
246 		shell_print(sh, "Not connected");
247 		return -ENOEXEC;
248 	}
249 
250 	if ((default_ct != NULL) || (default_tg != NULL)) {
251 		bt_avrcp_disconnect(default_conn);
252 	} else {
253 		shell_error(sh, "AVRCP is not connected");
254 	}
255 
256 	return 0;
257 }
258 
cmd_get_unit_info(const struct shell * sh,int32_t argc,char * argv[])259 static int cmd_get_unit_info(const struct shell *sh, int32_t argc, char *argv[])
260 {
261 	if (!avrcp_ct_registered && register_ct_cb(sh) != 0) {
262 		return -ENOEXEC;
263 	}
264 
265 	if (default_ct != NULL) {
266 		bt_avrcp_ct_get_unit_info(default_ct, get_next_tid());
267 	} else {
268 		shell_error(sh, "AVRCP is not connected");
269 	}
270 
271 	return 0;
272 }
273 
cmd_send_unit_info_rsp(const struct shell * sh,int32_t argc,char * argv[])274 static int cmd_send_unit_info_rsp(const struct shell *sh, int32_t argc, char *argv[])
275 {
276 	struct bt_avrcp_unit_info_rsp rsp;
277 	int err;
278 
279 	if (!avrcp_tg_registered && register_tg_cb(sh) != 0) {
280 		return -ENOEXEC;
281 	}
282 
283 	rsp.unit_type = BT_AVRCP_SUBUNIT_TYPE_PANEL;
284 	rsp.company_id = BT_AVRCP_COMPANY_ID_BLUETOOTH_SIG;
285 
286 	if (default_tg != NULL) {
287 		err = bt_avrcp_tg_send_unit_info_rsp(default_tg, tg_tid, &rsp);
288 		if (!err) {
289 			shell_print(sh, "AVRCP send unit info response");
290 		} else {
291 			shell_error(sh, "Failed to send unit info response");
292 		}
293 	} else {
294 		shell_error(sh, "AVRCP is not connected");
295 	}
296 
297 	return 0;
298 }
299 
cmd_get_subunit_info(const struct shell * sh,int32_t argc,char * argv[])300 static int cmd_get_subunit_info(const struct shell *sh, int32_t argc, char *argv[])
301 {
302 	if (!avrcp_ct_registered && register_ct_cb(sh) != 0) {
303 		return -ENOEXEC;
304 	}
305 
306 	if (default_ct != NULL) {
307 		bt_avrcp_ct_get_subunit_info(default_ct, get_next_tid());
308 	} else {
309 		shell_error(sh, "AVRCP is not connected");
310 	}
311 
312 	return 0;
313 }
314 
cmd_passthrough(const struct shell * sh,bt_avrcp_opid_t opid,const uint8_t * payload,uint8_t len)315 static int cmd_passthrough(const struct shell *sh, bt_avrcp_opid_t opid, const uint8_t *payload,
316 			   uint8_t len)
317 {
318 	if (!avrcp_ct_registered && register_ct_cb(sh) != 0) {
319 		return -ENOEXEC;
320 	}
321 
322 	if (default_ct != NULL) {
323 		bt_avrcp_ct_passthrough(default_ct, get_next_tid(), opid, BT_AVRCP_BUTTON_PRESSED,
324 					payload, len);
325 		bt_avrcp_ct_passthrough(default_ct, get_next_tid(), opid, BT_AVRCP_BUTTON_RELEASED,
326 					payload, len);
327 	} else {
328 		shell_error(sh, "AVRCP is not connected");
329 	}
330 
331 	return 0;
332 }
333 
cmd_play(const struct shell * sh,int32_t argc,char * argv[])334 static int cmd_play(const struct shell *sh, int32_t argc, char *argv[])
335 {
336 	return cmd_passthrough(sh, BT_AVRCP_OPID_PLAY, NULL, 0);
337 }
338 
cmd_pause(const struct shell * sh,int32_t argc,char * argv[])339 static int cmd_pause(const struct shell *sh, int32_t argc, char *argv[])
340 {
341 	return cmd_passthrough(sh, BT_AVRCP_OPID_PAUSE, NULL, 0);
342 }
343 
cmd_get_cap(const struct shell * sh,int32_t argc,char * argv[])344 static int cmd_get_cap(const struct shell *sh, int32_t argc, char *argv[])
345 {
346 	const char *cap_id;
347 
348 	if (!avrcp_ct_registered && register_ct_cb(sh) != 0) {
349 		return -ENOEXEC;
350 	}
351 
352 	if (default_ct == NULL) {
353 		shell_error(sh, "AVRCP is not connected");
354 		return 0;
355 	}
356 
357 	cap_id = argv[1];
358 	if (!strcmp(cap_id, "company")) {
359 		bt_avrcp_ct_get_cap(default_ct, get_next_tid(), BT_AVRCP_CAP_COMPANY_ID);
360 	} else if (!strcmp(cap_id, "events")) {
361 		bt_avrcp_ct_get_cap(default_ct, get_next_tid(), BT_AVRCP_CAP_EVENTS_SUPPORTED);
362 	}
363 
364 	return 0;
365 }
366 
367 SHELL_STATIC_SUBCMD_SET_CREATE(
368 	ct_cmds,
369 	SHELL_CMD_ARG(register_cb, NULL, "register avrcp ct callbacks", cmd_register_ct_cb, 1, 0),
370 	SHELL_CMD_ARG(get_unit, NULL, "get unit info", cmd_get_unit_info, 1, 0),
371 	SHELL_CMD_ARG(get_subunit, NULL, "get subunit info", cmd_get_subunit_info, 1, 0),
372 	SHELL_CMD_ARG(get_cap, NULL, "get capabilities <cap_id: company or events>", cmd_get_cap, 2,
373 		      0),
374 	SHELL_CMD_ARG(play, NULL, "request a play at the remote player", cmd_play, 1, 0),
375 	SHELL_CMD_ARG(pause, NULL, "request a pause at the remote player", cmd_pause, 1, 0),
376 	SHELL_SUBCMD_SET_END);
377 
378 SHELL_STATIC_SUBCMD_SET_CREATE(
379 	tg_cmds,
380 	SHELL_CMD_ARG(register_cb, NULL, "register avrcp tg callbacks", cmd_register_tg_cb, 1, 0),
381 	SHELL_CMD_ARG(send_unit_rsp, NULL, "send unit info response", cmd_send_unit_info_rsp, 1, 0),
382 	SHELL_SUBCMD_SET_END);
383 
cmd_avrcp(const struct shell * sh,size_t argc,char ** argv)384 static int cmd_avrcp(const struct shell *sh, size_t argc, char **argv)
385 {
386 	if (argc == 1) {
387 		shell_help(sh);
388 		/* sh returns 1 when help is printed */
389 		return 1;
390 	}
391 
392 	shell_error(sh, "%s unknown parameter: %s", argv[0], argv[1]);
393 
394 	return -ENOEXEC;
395 }
396 
397 SHELL_STATIC_SUBCMD_SET_CREATE(
398 	avrcp_cmds,
399 	SHELL_CMD_ARG(connect, NULL, "connect AVRCP", cmd_connect, 1, 0),
400 	SHELL_CMD_ARG(disconnect, NULL, "disconnect AVRCP", cmd_disconnect, 1, 0),
401 	SHELL_CMD(ct, &ct_cmds, "AVRCP CT shell commands", cmd_avrcp),
402 	SHELL_CMD(tg, &tg_cmds, "AVRCP TG shell commands", cmd_avrcp),
403 	SHELL_SUBCMD_SET_END);
404 
405 SHELL_CMD_ARG_REGISTER(avrcp, &avrcp_cmds, "Bluetooth AVRCP sh commands", cmd_avrcp, 1, 1);
406