1 /**
2  * @file
3  * @brief Bluetooth Hearing Access Service (HAS) APIs.
4  */
5 
6 /*
7  * Copyright (c) 2022 Codecoup
8  * Copyright (c) 2024 Nordic Semiconductor ASA
9  *
10  * SPDX-License-Identifier: Apache-2.0
11  */
12 
13 #ifndef ZEPHYR_INCLUDE_BLUETOOTH_AUDIO_HAS_H_
14 #define ZEPHYR_INCLUDE_BLUETOOTH_AUDIO_HAS_H_
15 
16 /**
17  * @brief Hearing Access Service (HAS)
18  *
19  * @defgroup bt_has Hearing Access Service (HAS)
20  *
21  * @since 3.1
22  * @version 0.8.0
23  *
24  * @ingroup bluetooth
25  * @{
26  *
27  * The Hearing Access Service is used to identify a hearing aid and optionally
28  * to control hearing aid presets.
29  */
30 
31 #include <stdint.h>
32 #include <stdbool.h>
33 
34 #include <zephyr/bluetooth/bluetooth.h>
35 #include <zephyr/sys/util.h>
36 #include <zephyr/sys/util_macro.h>
37 
38 #ifdef __cplusplus
39 extern "C" {
40 #endif
41 
42 /**
43  * @name Preset index definitions
44  * @{
45  */
46 /** No index */
47 #define BT_HAS_PRESET_INDEX_NONE 0x00
48 /** First preset index */
49 #define BT_HAS_PRESET_INDEX_FIRST 0x01
50 /** Last preset index */
51 #define BT_HAS_PRESET_INDEX_LAST 0xFF
52 /** @} */
53 
54 /** Preset name minimum length */
55 #define BT_HAS_PRESET_NAME_MIN 1
56 /** Preset name maximum length */
57 #define BT_HAS_PRESET_NAME_MAX 40
58 
59 /** @brief Opaque Hearing Access Service object. */
60 struct bt_has;
61 
62 /** Hearing Aid device type */
63 enum bt_has_hearing_aid_type {
64 	/**
65 	 * Two hearing aids that form a Coordinated Set, one for the right ear and one for the left
66 	 * ear of the user. Typically used by a user with bilateral hearing loss.
67 	 */
68 	BT_HAS_HEARING_AID_TYPE_BINAURAL = 0x00,
69 	/**
70 	 * A single hearing aid for the left or the right ear. Typically used by a user with
71 	 * unilateral hearing loss.
72 	 */
73 	BT_HAS_HEARING_AID_TYPE_MONAURAL = 0x01,
74 	/**
75 	 * Two hearing aids with a connection to one another that expose a single Bluetooth radio
76 	 * interface.
77 	 */
78 	BT_HAS_HEARING_AID_TYPE_BANDED = 0x02,
79 };
80 
81 /** Preset Properties values */
82 enum bt_has_properties {
83 	/** No properties set */
84 	BT_HAS_PROP_NONE = 0,
85 
86 	/** Preset name can be written by the client */
87 	BT_HAS_PROP_WRITABLE = BIT(0),
88 
89 	/** Preset availability */
90 	BT_HAS_PROP_AVAILABLE = BIT(1),
91 };
92 
93 /** Hearing Aid device capabilities */
94 enum bt_has_capabilities {
95 	/** Indicate support for presets */
96 	BT_HAS_PRESET_SUPPORT = BIT(0),
97 };
98 
99 /** @brief Structure for registering features of a Hearing Access Service instance. */
100 struct bt_has_features_param {
101 	/** Hearing Aid Type value */
102 	enum bt_has_hearing_aid_type type;
103 
104 	/**
105 	 * @brief Preset Synchronization Support.
106 	 *
107 	 * Only applicable if @p type is @ref BT_HAS_HEARING_AID_TYPE_BINAURAL
108 	 * and @kconfig{CONFIG_BT_HAS_PRESET_COUNT} is non-zero.
109 	 */
110 	bool preset_sync_support;
111 
112 	/**
113 	 * @brief Independent Presets.
114 	 *
115 	 * Only applicable if @p type is @ref BT_HAS_HEARING_AID_TYPE_BINAURAL
116 	 * and @kconfig{CONFIG_BT_HAS_PRESET_COUNT} is non-zero.
117 	 */
118 	bool independent_presets;
119 };
120 
121 /** @brief Preset record definition */
122 struct bt_has_preset_record {
123 	/** Unique preset index. */
124 	uint8_t index;
125 
126 	/** Bitfield of preset properties. */
127 	enum bt_has_properties properties;
128 
129 	/** Preset name. */
130 	const char *name;
131 };
132 
133 /** @brief Hearing Access Service Client callback structure. */
134 struct bt_has_client_cb {
135 	/**
136 	 * @brief Callback function for bt_has_discover.
137 	 *
138 	 * This callback is called when discovery procedure is complete.
139 	 *
140 	 * @param conn Bluetooth connection object.
141 	 * @param err 0 on success, ATT error or negative errno otherwise.
142 	 * @param has Pointer to the Hearing Access Service object or NULL on errors.
143 	 * @param type Hearing Aid type.
144 	 * @param caps Hearing Aid capabilities.
145 	 */
146 	void (*discover)(struct bt_conn *conn, int err, struct bt_has *has,
147 			 enum bt_has_hearing_aid_type type, enum bt_has_capabilities caps);
148 
149 	/**
150 	 * @brief Callback function for Hearing Access Service active preset changes.
151 	 *
152 	 * Optional callback called when the active preset is changed by the remote server when the
153 	 * preset switch procedure is complete. The callback must be set to receive active preset
154 	 * changes and enable support for switching presets. If the callback is not set, the Active
155 	 * Index and Control Point characteristics will not be discovered by
156 	 * @ref bt_has_client_discover.
157 	 *
158 	 * @param has Pointer to the Hearing Access Service object.
159 	 * @param err 0 on success, ATT error or negative errno otherwise.
160 	 * @param index Active preset index.
161 	 */
162 	void (*preset_switch)(struct bt_has *has, int err, uint8_t index);
163 
164 	/**
165 	 * @brief Callback function for presets read operation.
166 	 *
167 	 * The callback is called when the preset read response is sent by the remote server.
168 	 * The record object as well as its members are temporary and must be copied to in order
169 	 * to cache its information.
170 	 *
171 	 * @param has Pointer to the Hearing Access Service object.
172 	 * @param err 0 on success, ATT error or negative errno otherwise.
173 	 * @param record Preset record or NULL on errors.
174 	 * @param is_last True if Read Presets operation can be considered concluded.
175 	 */
176 	void (*preset_read_rsp)(struct bt_has *has, int err,
177 				const struct bt_has_preset_record *record, bool is_last);
178 
179 	/**
180 	 * @brief Callback function for preset update notifications.
181 	 *
182 	 * The callback is called when the preset record update is notified by the remote server.
183 	 * The record object as well as its objects are temporary and must be copied to in order
184 	 * to cache its information.
185 	 *
186 	 * @param has Pointer to the Hearing Access Service object.
187 	 * @param index_prev Index of the previous preset in the list.
188 	 * @param record Preset record.
189 	 * @param is_last True if preset list update operation can be considered concluded.
190 	 */
191 	void (*preset_update)(struct bt_has *has, uint8_t index_prev,
192 			      const struct bt_has_preset_record *record, bool is_last);
193 
194 	/**
195 	 * @brief Callback function for preset deletion notifications.
196 	 *
197 	 * The callback is called when the preset has been deleted by the remote server.
198 	 *
199 	 * @param has Pointer to the Hearing Access Service object.
200 	 * @param index Preset index.
201 	 * @param is_last True if preset list update operation can be considered concluded.
202 	 */
203 	void (*preset_deleted)(struct bt_has *has, uint8_t index, bool is_last);
204 
205 	/**
206 	 * @brief Callback function for preset availability notifications.
207 	 *
208 	 * The callback is called when the preset availability change is notified by the remote
209 	 * server.
210 	 *
211 	 * @param has Pointer to the Hearing Access Service object.
212 	 * @param index Preset index.
213 	 * @param available True if available, false otherwise.
214 	 * @param is_last True if preset list update operation can be considered concluded.
215 	 */
216 	void (*preset_availability)(struct bt_has *has, uint8_t index, bool available,
217 				    bool is_last);
218 };
219 
220 /**
221  * @brief Registers the callbacks used by the Hearing Access Service client.
222  *
223  * @param cb The callback structure.
224  *
225  * @return 0 in case of success or negative value in case of error.
226  */
227 int bt_has_client_cb_register(const struct bt_has_client_cb *cb);
228 
229 /**
230  * @brief Discover Hearing Access Service on a remote device.
231  *
232  * Client method to find a Hearing Access Service on a server identified by @p conn.
233  * The @ref bt_has_client_cb.discover callback will be called when the discovery procedure
234  * is complete to provide user a @ref bt_has object.
235  *
236  * @param conn Bluetooth connection object.
237  *
238  * @return 0 if success, errno on failure.
239  */
240 int bt_has_client_discover(struct bt_conn *conn);
241 
242 /**
243  * @brief Get the Bluetooth connection object of the service object.
244  *
245  * The caller gets a new reference to the connection object which must be
246  * released with bt_conn_unref() once done using the object.
247  *
248  * @param[in] has Pointer to the Hearing Access Service object.
249  * @param[out] conn Connection object.
250  *
251  * @return 0 in case of success or negative value in case of error.
252  */
253 int bt_has_client_conn_get(const struct bt_has *has, struct bt_conn **conn);
254 
255 /**
256  * @brief Read Preset Records.
257  *
258  * Client method to read up to @p max_count presets starting from given @p index.
259  * The preset records are returned in the @ref bt_has_client_cb.preset_read_rsp callback
260  * (called once for each preset).
261  *
262  * @param has Pointer to the Hearing Access Service object.
263  * @param index The index to start with.
264  * @param max_count Maximum number of presets to read.
265  *
266  * @return 0 in case of success or negative value in case of error.
267  */
268 int bt_has_client_presets_read(struct bt_has *has, uint8_t index, uint8_t max_count);
269 
270 /**
271  * @brief Set Active Preset.
272  *
273  * Client procedure to set preset identified by @p index as active.
274  * The status is returned in the @ref bt_has_client_cb.preset_switch callback.
275  *
276  * @param has Pointer to the Hearing Access Service object.
277  * @param index Preset index to activate.
278  * @param sync Request active preset synchronization in set.
279  *
280  * @return 0 in case of success or negative value in case of error.
281  */
282 int bt_has_client_preset_set(struct bt_has *has, uint8_t index, bool sync);
283 
284 /**
285  * @brief Activate Next Preset.
286  *
287  * Client procedure to set next available preset as active.
288  * The status is returned in the @ref bt_has_client_cb.preset_switch callback.
289  *
290  * @param has Pointer to the Hearing Access Service object.
291  * @param sync Request active preset synchronization in set.
292  *
293  * @return 0 in case of success or negative value in case of error.
294  */
295 int bt_has_client_preset_next(struct bt_has *has, bool sync);
296 
297 /**
298  * @brief Activate Previous Preset.
299  *
300  * Client procedure to set previous available preset as active.
301  * The status is returned in the @ref bt_has_client_cb.preset_switch callback.
302  *
303  * @param has Pointer to the Hearing Access Service object.
304  * @param sync Request active preset synchronization in set.
305  *
306  * @return 0 in case of success or negative value in case of error.
307  */
308 int bt_has_client_preset_prev(struct bt_has *has, bool sync);
309 
310 /** @brief Preset operations structure. */
311 struct bt_has_preset_ops {
312 	/**
313 	 * @brief Preset select callback.
314 	 *
315 	 * This callback is called when the client requests to select preset identified by
316 	 * @p index.
317 	 *
318 	 * @param index Preset index requested to activate.
319 	 * @param sync Whether the server must relay this change to the other member of the
320 	 *             Binaural Hearing Aid Set.
321 	 *
322 	 * @return 0 in case of success or negative value in case of error.
323 	 * @return -EBUSY if operation cannot be performed at the time.
324 	 * @return -EINPROGRESS in case where user has to confirm once the requested preset
325 	 *                      becomes active by calling @ref bt_has_preset_active_set.
326 	 */
327 	int (*select)(uint8_t index, bool sync);
328 
329 	/**
330 	 * @brief Preset name changed callback
331 	 *
332 	 * This callback is called when the name of the preset identified by @p index has changed.
333 	 *
334 	 * @param index Preset index that name has been changed.
335 	 * @param name Preset current name.
336 	 */
337 	void (*name_changed)(uint8_t index, const char *name);
338 };
339 
340 /** @brief Register structure for preset. */
341 struct bt_has_preset_register_param {
342 	/**
343 	 * @brief Preset index.
344 	 *
345 	 * Unique preset identifier. The value shall be other than @ref BT_HAS_PRESET_INDEX_NONE.
346 	 */
347 	uint8_t index;
348 
349 	/**
350 	 * @brief Preset properties.
351 	 *
352 	 * Bitfield of preset properties.
353 	 */
354 	enum bt_has_properties properties;
355 
356 	/**
357 	 * @brief Preset name.
358 	 *
359 	 * Preset name that further can be changed by either server or client if
360 	 * @kconfig{CONFIG_BT_HAS_PRESET_NAME_DYNAMIC} configuration option has been enabled.
361 	 * It's length shall be greater than @ref BT_HAS_PRESET_NAME_MIN. If the length exceeds
362 	 * @ref BT_HAS_PRESET_NAME_MAX, the name will be truncated.
363 	 */
364 	const char *name;
365 
366 	/** Preset operations structure. */
367 	const struct bt_has_preset_ops *ops;
368 };
369 
370 /**
371  * @brief Register the Hearing Access Service instance.
372  *
373  * @param features     Hearing Access Service register parameters.
374  *
375  * @return 0 if success, errno on failure.
376  */
377 int bt_has_register(const struct bt_has_features_param *features);
378 
379 /**
380  * @brief Register preset.
381  *
382  * Register preset. The preset will be a added to the list of exposed preset records.
383  * This symbol is linkable if @kconfig{CONFIG_BT_HAS_PRESET_COUNT} is non-zero.
384  *
385  * @param param Preset registration parameter.
386  *
387  * @return 0 if success, errno on failure.
388  */
389 int bt_has_preset_register(const struct bt_has_preset_register_param *param);
390 
391 /**
392  * @brief Unregister Preset.
393  *
394  * Unregister preset. The preset will be removed from the list of preset records.
395  *
396  * @param index The index of preset that's being requested to unregister.
397  *
398  * @return 0 if success, errno on failure.
399  */
400 int bt_has_preset_unregister(uint8_t index);
401 
402 /**
403  * @brief Set the preset as available.
404  *
405  * Set the @ref BT_HAS_PROP_AVAILABLE property bit. This will notify preset availability
406  * to peer devices. Only available preset can be selected as active preset.
407  *
408  * @param index The index of preset that's became available.
409  *
410  * @return 0 in case of success or negative value in case of error.
411  */
412 int bt_has_preset_available(uint8_t index);
413 
414 /**
415  * @brief Set the preset as unavailable.
416  *
417  * Clear the @ref BT_HAS_PROP_AVAILABLE property bit. This will notify preset availability
418  * to peer devices. Unavailable preset cannot be selected as active preset.
419  *
420  * @param index The index of preset that's became unavailable.
421  *
422  * @return 0 in case of success or negative value in case of error.
423  */
424 int bt_has_preset_unavailable(uint8_t index);
425 
426 /** Enum for return values for @ref bt_has_preset_func_t functions */
427 enum {
428 	/** Stop iterating */
429 	BT_HAS_PRESET_ITER_STOP = 0,
430 	/** Continue iterating */
431 	BT_HAS_PRESET_ITER_CONTINUE,
432 };
433 
434 /**
435  * @typedef bt_has_preset_func_t
436  * @brief Preset iterator callback.
437  *
438  * @param index The index of preset found.
439  * @param properties Preset properties.
440  * @param name Preset name.
441  * @param user_data Data given.
442  *
443  * @return BT_HAS_PRESET_ITER_CONTINUE if should continue to the next preset.
444  * @return BT_HAS_PRESET_ITER_STOP to stop.
445  */
446 typedef uint8_t (*bt_has_preset_func_t)(uint8_t index, enum bt_has_properties properties,
447 					const char *name, void *user_data);
448 
449 /**
450  * @brief Preset iterator.
451  *
452  * Iterate presets. Optionally, match non-zero index if given.
453  *
454  * @param index Preset index, passing @ref BT_HAS_PRESET_INDEX_NONE skips index matching.
455  * @param func Callback function.
456  * @param user_data Data to pass to the callback.
457  */
458 void bt_has_preset_foreach(uint8_t index, bt_has_preset_func_t func, void *user_data);
459 
460 /**
461  * @brief Set active preset.
462  *
463  * Function used to set the preset identified by the @p index as the active preset.
464  * The preset index will be notified to peer devices.
465  *
466  * @param index Preset index.
467  *
468  * @return 0 in case of success or negative value in case of error.
469  */
470 int bt_has_preset_active_set(uint8_t index);
471 
472 /**
473  * @brief Get active preset.
474  *
475  * Function used to get the currently active preset index.
476  *
477  * @return Active preset index.
478  */
479 uint8_t bt_has_preset_active_get(void);
480 
481 /**
482  * @brief Clear out active preset.
483  *
484  * Used by server to deactivate currently active preset.
485  *
486  * @return 0 in case of success or negative value in case of error.
487  */
bt_has_preset_active_clear(void)488 static inline int bt_has_preset_active_clear(void)
489 {
490 	return bt_has_preset_active_set(BT_HAS_PRESET_INDEX_NONE);
491 }
492 
493 /**
494  * @brief Change the Preset Name.
495  *
496  * Change the name of the preset identified by @p index.
497  *
498  * @param index The index of the preset to change the name of.
499  * @param name Name to write.
500  *
501  * @return 0 in case of success or negative value in case of error.
502  */
503 int bt_has_preset_name_change(uint8_t index, const char *name);
504 
505 /**
506  * @brief Change the Hearing Aid Features.
507  *
508  * Change the hearing aid features.
509  *
510  * @param features The features to be set.
511  *
512  * @return 0 in case of success or negative value in case of error.
513  */
514 int bt_has_features_set(const struct bt_has_features_param *features);
515 
516 #ifdef __cplusplus
517 }
518 #endif
519 
520 /**
521  * @}
522  */
523 
524 #endif /* ZEPHYR_INCLUDE_BLUETOOTH_AUDIO_HAS_H_ */
525