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(&param);
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(&param);
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(&param);
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