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