1 /**
2 * @file
3 * @brief Bluetooth Hearing Access Service (HAS) shell.
4 *
5 * Copyright (c) 2022 Codecoup
6 *
7 * SPDX-License-Identifier: Apache-2.0
8 */
9
10 #include <errno.h>
11 #include <stdbool.h>
12 #include <stddef.h>
13 #include <stdint.h>
14 #include <string.h>
15
16 #include <zephyr/bluetooth/audio/has.h>
17 #include <zephyr/bluetooth/bluetooth.h>
18 #include <zephyr/bluetooth/conn.h>
19 #include <zephyr/kernel.h>
20 #include <zephyr/shell/shell.h>
21 #include <zephyr/shell/shell_string_conv.h>
22
23 #include "host/shell/bt.h"
24
preset_select(uint8_t index,bool sync)25 static int preset_select(uint8_t index, bool sync)
26 {
27 return 0;
28 }
29
preset_name_changed(uint8_t index,const char * name)30 static void preset_name_changed(uint8_t index, const char *name)
31 {
32 shell_print(ctx_shell, "Preset name changed index %u name %s", index, name);
33 }
34
35 static const struct bt_has_preset_ops preset_ops = {
36 .select = preset_select,
37 .name_changed = preset_name_changed,
38 };
39
cmd_preset_reg(const struct shell * sh,size_t argc,char ** argv)40 static int cmd_preset_reg(const struct shell *sh, size_t argc, char **argv)
41 {
42 int err = 0;
43 struct bt_has_preset_register_param param = {
44 .index = shell_strtoul(argv[1], 16, &err),
45 .properties = shell_strtoul(argv[2], 16, &err),
46 .name = argv[3],
47 .ops = &preset_ops,
48 };
49
50 if (err < 0) {
51 shell_print(sh, "Invalid command parameter (err %d)", err);
52 return err;
53 }
54
55 err = bt_has_preset_register(¶m);
56 if (err < 0) {
57 shell_error(sh, "Preset register failed (err %d)", err);
58 return err;
59 }
60
61 return 0;
62 }
63
cmd_preset_unreg(const struct shell * sh,size_t argc,char ** argv)64 static int cmd_preset_unreg(const struct shell *sh, size_t argc, char **argv)
65 {
66 int err = 0;
67 const uint8_t index = shell_strtoul(argv[1], 16, &err);
68
69 if (err < 0) {
70 shell_print(sh, "Invalid command parameter (err %d)", err);
71 return err;
72 }
73
74 err = bt_has_preset_unregister(index);
75 if (err < 0) {
76 shell_print(sh, "Preset unregister failed (err %d)", err);
77 return err;
78 }
79
80 return 0;
81 }
82
cmd_features_set(const struct shell * sh,size_t argc,char ** argv)83 static int cmd_features_set(const struct shell *sh, size_t argc, char **argv)
84 {
85 int err;
86 struct bt_has_features_param param = {
87 .type = BT_HAS_HEARING_AID_TYPE_MONAURAL,
88 .preset_sync_support = false,
89 .independent_presets = false
90 };
91
92 for (size_t argn = 1; argn < argc; argn++) {
93 const char *arg = argv[argn];
94
95 if (strcmp(arg, "binaural") == 0) {
96 param.type = BT_HAS_HEARING_AID_TYPE_BINAURAL;
97 } else if (strcmp(arg, "monaural") == 0) {
98 param.type = BT_HAS_HEARING_AID_TYPE_MONAURAL;
99 } else if (strcmp(arg, "banded") == 0) {
100 param.type = BT_HAS_HEARING_AID_TYPE_BANDED;
101 } else if (strcmp(arg, "sync") == 0) {
102 param.preset_sync_support = true;
103 } else if (strcmp(arg, "independent") == 0) {
104 param.independent_presets = true;
105 } else {
106 shell_help(sh);
107 return SHELL_CMD_HELP_PRINTED;
108 }
109 }
110
111 err = bt_has_features_set(¶m);
112 if (err != 0) {
113 shell_error(sh, "Could not set features: %d", err);
114 return err;
115 }
116
117 return 0;
118 }
119
cmd_has_register(const struct shell * sh,size_t argc,char ** argv)120 static int cmd_has_register(const struct shell *sh, size_t argc, char **argv)
121 {
122 int err;
123 struct bt_has_features_param param = {
124 .type = BT_HAS_HEARING_AID_TYPE_MONAURAL,
125 .preset_sync_support = false,
126 .independent_presets = false
127 };
128
129 for (size_t argn = 1; argn < argc; argn++) {
130 const char *arg = argv[argn];
131
132 if (strcmp(arg, "binaural") == 0) {
133 param.type = BT_HAS_HEARING_AID_TYPE_BINAURAL;
134 } else if (strcmp(arg, "monaural") == 0) {
135 param.type = BT_HAS_HEARING_AID_TYPE_MONAURAL;
136 } else if (strcmp(arg, "banded") == 0) {
137 param.type = BT_HAS_HEARING_AID_TYPE_BANDED;
138 } else if (strcmp(arg, "sync") == 0) {
139 param.preset_sync_support = true;
140 } else if (strcmp(arg, "independent") == 0) {
141 param.independent_presets = true;
142 } else {
143 shell_help(sh);
144 return SHELL_CMD_HELP_PRINTED;
145 }
146 }
147
148 err = bt_has_register(¶m);
149 if (err != 0) {
150 shell_error(sh, "Could not register HAS: %d", err);
151 return err;
152 }
153
154 return 0;
155 }
156
157 struct print_list_entry_data {
158 int num;
159 const struct shell *sh;
160 };
161
print_list_entry(uint8_t index,enum bt_has_properties properties,const char * name,void * user_data)162 static uint8_t print_list_entry(uint8_t index, enum bt_has_properties properties,
163 const char *name, void *user_data)
164 {
165 struct print_list_entry_data *data = user_data;
166
167 shell_print(data->sh, "%d: index 0x%02x prop 0x%02x name %s", ++data->num, index,
168 properties, name);
169
170 return BT_HAS_PRESET_ITER_CONTINUE;
171 }
172
cmd_preset_list(const struct shell * sh,size_t argc,char ** argv)173 static int cmd_preset_list(const struct shell *sh, size_t argc, char **argv)
174 {
175 struct print_list_entry_data data = {
176 .sh = sh,
177 };
178
179 bt_has_preset_foreach(0, print_list_entry, &data);
180
181 if (data.num == 0) {
182 shell_print(sh, "No presets registered");
183 }
184
185 return 0;
186 }
187
cmd_preset_avail(const struct shell * sh,size_t argc,char ** argv)188 static int cmd_preset_avail(const struct shell *sh, size_t argc, char **argv)
189 {
190 int err = 0;
191 const uint8_t index = shell_strtoul(argv[1], 16, &err);
192
193 if (err < 0) {
194 shell_print(sh, "Invalid command parameter (err %d)", err);
195 return err;
196 }
197
198 err = bt_has_preset_available(index);
199 if (err < 0) {
200 shell_print(sh, "Preset availability set failed (err %d)", err);
201 return err;
202 }
203
204 return 0;
205 }
206
cmd_preset_unavail(const struct shell * sh,size_t argc,char ** argv)207 static int cmd_preset_unavail(const struct shell *sh, size_t argc, char **argv)
208 {
209 int err = 0;
210 const uint8_t index = shell_strtoul(argv[1], 16, &err);
211
212 if (err < 0) {
213 shell_print(sh, "Invalid command parameter (err %d)", err);
214 return err;
215 }
216
217 err = bt_has_preset_unavailable(index);
218 if (err < 0) {
219 shell_print(sh, "Preset availability set failed (err %d)", err);
220 return err;
221 }
222
223 return 0;
224 }
225
cmd_preset_active_set(const struct shell * sh,size_t argc,char ** argv)226 static int cmd_preset_active_set(const struct shell *sh, size_t argc, char **argv)
227 {
228 int err = 0;
229 const uint8_t index = shell_strtoul(argv[1], 16, &err);
230
231 if (err < 0) {
232 shell_print(sh, "Invalid command parameter (err %d)", err);
233 return err;
234 }
235
236 err = bt_has_preset_active_set(index);
237 if (err < 0) {
238 shell_print(sh, "Preset selection failed (err %d)", err);
239 return err;
240 }
241
242 return 0;
243 }
244
cmd_preset_active_get(const struct shell * sh,size_t argc,char ** argv)245 static int cmd_preset_active_get(const struct shell *sh, size_t argc, char **argv)
246 {
247 const uint8_t index = bt_has_preset_active_get();
248
249 shell_print(sh, "Active index 0x%02x", index);
250
251 return 0;
252 }
253
cmd_preset_active_clear(const struct shell * sh,size_t argc,char ** argv)254 static int cmd_preset_active_clear(const struct shell *sh, size_t argc, char **argv)
255 {
256 int err;
257
258 err = bt_has_preset_active_clear();
259 if (err < 0) {
260 shell_print(sh, "Preset selection failed (err %d)", err);
261 return err;
262 }
263
264 return 0;
265 }
266
cmd_preset_name_set(const struct shell * sh,size_t argc,char ** argv)267 static int cmd_preset_name_set(const struct shell *sh, size_t argc, char **argv)
268 {
269 int err = 0;
270 const uint8_t index = shell_strtoul(argv[1], 16, &err);
271
272 if (err < 0) {
273 shell_print(sh, "Invalid command parameter (err %d)", err);
274 return err;
275 }
276
277 err = bt_has_preset_name_change(index, argv[2]);
278 if (err < 0) {
279 shell_print(sh, "Preset name change failed (err %d)", err);
280 return err;
281 }
282
283 return 0;
284 }
285
cmd_has(const struct shell * sh,size_t argc,char ** argv)286 static int cmd_has(const struct shell *sh, size_t argc, char **argv)
287 {
288 if (argc > 1) {
289 shell_error(sh, "%s unknown parameter: %s", argv[0], argv[1]);
290 } else {
291 shell_error(sh, "%s missing subcomand", argv[0]);
292 }
293
294 return -ENOEXEC;
295 }
296
297 SHELL_STATIC_SUBCMD_SET_CREATE(has_cmds,
298 SHELL_CMD_ARG(register, NULL,
299 "Initialize the service and register type "
300 "[binaural | monaural(default) | banded] [sync] [independent]",
301 cmd_has_register, 1, 3),
302 SHELL_CMD_ARG(preset_reg, NULL, "Register preset <index> <properties> <name>",
303 cmd_preset_reg, 4, 0),
304 SHELL_CMD_ARG(preset_unreg, NULL, "Unregister preset <index>", cmd_preset_unreg, 2, 0),
305 SHELL_CMD_ARG(preset_list, NULL, "List all presets", cmd_preset_list, 1, 0),
306 SHELL_CMD_ARG(preset_set_avail, NULL, "Set preset as available <index>",
307 cmd_preset_avail, 2, 0),
308 SHELL_CMD_ARG(preset_set_unavail, NULL, "Set preset as unavailable <index>",
309 cmd_preset_unavail, 2, 0),
310 SHELL_CMD_ARG(preset_active_set, NULL, "Set active preset <index>",
311 cmd_preset_active_set, 2, 0),
312 SHELL_CMD_ARG(preset_active_get, NULL, "Get active preset", cmd_preset_active_get, 1, 0),
313 SHELL_CMD_ARG(preset_active_clear, NULL, "Clear selected preset",
314 cmd_preset_active_clear, 1, 0),
315 SHELL_CMD_ARG(set_name, NULL, "Set preset name <index> <name>", cmd_preset_name_set, 3, 0),
316 SHELL_CMD_ARG(features_set, NULL, "Set hearing aid features "
317 "[binaural | monaural(default) | banded] [sync] [independent]",
318 cmd_features_set, 1, 3),
319 SHELL_SUBCMD_SET_END
320 );
321
322 SHELL_CMD_ARG_REGISTER(has, &has_cmds, "Bluetooth HAS shell commands", cmd_has, 1, 1);
323