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