xref: /btstack/src/ble/gatt-service/hids_client.h (revision 41d889f346c8c7033115b3f0769b8cf1743fd415)
1 /*
2  * Copyright (C) 2021 BlueKitchen GmbH
3  *
4  * Redistribution and use in source and binary forms, with or without
5  * modification, are permitted provided that the following conditions
6  * are met:
7  *
8  * 1. Redistributions of source code must retain the above copyright
9  *    notice, this list of conditions and the following disclaimer.
10  * 2. Redistributions in binary form must reproduce the above copyright
11  *    notice, this list of conditions and the following disclaimer in the
12  *    documentation and/or other materials provided with the distribution.
13  * 3. Neither the name of the copyright holders nor the names of
14  *    contributors may be used to endorse or promote products derived
15  *    from this software without specific prior written permission.
16  * 4. Any redistribution, use, or modification is done solely for
17  *    personal benefit and not for any commercial purpose or for
18  *    monetary gain.
19  *
20  * THIS SOFTWARE IS PROVIDED BY BLUEKITCHEN GMBH AND CONTRIBUTORS
21  * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
22  * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
23  * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL MATTHIAS
24  * RINGWALD OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
25  * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
26  * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS
27  * OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED
28  * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
29  * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF
30  * THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
31  * SUCH DAMAGE.
32  *
33  * Please inquire about commercial licensing options at
34  * [email protected]
35  *
36  */
37 
38 #ifndef HIDS_CLIENT_H
39 #define HIDS_CLIENT_H
40 
41 #include <stdint.h>
42 #include "btstack_defines.h"
43 #include "btstack_hid.h"
44 #include "bluetooth.h"
45 #include "btstack_linked_list.h"
46 #include "ble/gatt_client.h"
47 
48 #if defined __cplusplus
49 extern "C" {
50 #endif
51 
52 /**
53  * @text The HID Service Client is used on the HID Host to receive reports and other HID data.
54  */
55 
56 #ifndef MAX_NUM_HID_SERVICES
57 #define MAX_NUM_HID_SERVICES 3
58 #endif
59 #define HIDS_CLIENT_INVALID_REPORT_INDEX 0xFF
60 
61 #define HIDS_CLIENT_NUM_REPORTS 15
62 
63 typedef enum {
64     HIDS_CLIENT_STATE_IDLE,
65 
66     // get all HID services
67     HIDS_CLIENT_STATE_W2_QUERY_SERVICE,
68     HIDS_CLIENT_STATE_W4_SERVICE_RESULT,
69 
70     // for each service, discover all characteristics
71     HIDS_CLIENT_STATE_W2_QUERY_CHARACTERISTIC,
72     HIDS_CLIENT_STATE_W4_CHARACTERISTIC_RESULT,
73 
74     // called if BOOT mode
75     HIDS_CLIENT_STATE_W2_SET_PROTOCOL_MODE_WITHOUT_RESPONSE,
76 
77     // for each REPORT_MAP characteristic, read HID Descriptor (Report Map Characteristic Value)
78     HIDS_CLIENT_STATE_W2_READ_REPORT_MAP_HID_DESCRIPTOR,
79     HIDS_CLIENT_STATE_W4_REPORT_MAP_HID_DESCRIPTOR,
80 
81     // for REPORT_MAP characteristic, discover descriptor
82     HIDS_CLIENT_STATE_W2_REPORT_MAP_DISCOVER_CHARACTERISTIC_DESCRIPTORS,
83     HIDS_CLIENT_STATE_W4_REPORT_MAP_CHARACTERISTIC_DESCRIPTORS_RESULT,
84 
85     // for REPORT_MAP characteristic, read External Report Reference Characteristic Descriptor
86     HIDS_CLIENT_STATE_W2_REPORT_MAP_READ_EXTERNAL_REPORT_REFERENCE_UUID,
87     HIDS_CLIENT_STATE_W4_REPORT_MAP_EXTERNAL_REPORT_REFERENCE_UUID,
88 
89     // for every external report reference uuid, discover external Report characteristic
90     HIDS_CLIENT_STATE_W2_DISCOVER_EXTERNAL_REPORT_CHARACTERISTIC,
91     HIDS_CLIENT_STATE_W4_EXTERNAL_REPORT_CHARACTERISTIC_RESULT,
92 
93     // for each Report characteristics, discover Report characteristic descriptor
94     HIDS_CLIENT_STATE_W2_FIND_REPORT,
95     HIDS_CLIENT_STATE_W4_REPORT_FOUND,
96 
97     // then read value of Report characteristic descriptor to get report ID and type
98     HIDS_CLIENT_STATE_W2_READ_REPORT_ID_AND_TYPE,
99     HIDS_CLIENT_STATE_W4_REPORT_ID_AND_TYPE,
100 
101     HIDS_CLIENT_STATE_W2_ENABLE_INPUT_REPORTS,
102     HIDS_CLIENT_STATE_W4_INPUT_REPORTS_ENABLED,
103 
104     HIDS_CLIENT_STATE_CONNECTED,
105 
106     HIDS_CLIENT_W2_SEND_WRITE_REPORT,
107     HIDS_CLIENT_W4_WRITE_REPORT_DONE,
108 
109     HIDS_CLIENT_W2_SEND_GET_REPORT,
110     HIDS_CLIENT_W4_GET_REPORT_RESULT,
111 
112     HIDS_CLIENT_W2_READ_VALUE_OF_CHARACTERISTIC,
113     HIDS_CLIENT_W4_VALUE_OF_CHARACTERISTIC_RESULT,
114 
115     HIDS_CLIENT_W2_WRITE_VALUE_OF_CHARACTERISTIC_WITHOUT_RESPONSE,
116 
117     HIDS_CLIENT_STATE_W2_CONFIGURE_NOTIFICATIONS,
118     HIDS_CLIENT_STATE_W4_NOTIFICATIONS_CONFIGURED,
119 
120 #ifdef ENABLE_TESTING_SUPPORT
121     HIDS_CLIENT_W2_READ_CHARACTERISTIC_CONFIGURATION,
122     HIDS_CLIENT_W4_CHARACTERISTIC_CONFIGURATION_RESULT,
123 #endif
124 
125 } hid_service_client_state_t;
126 
127 
128 typedef struct {
129     // GATT cache
130 
131     //reuse as descriptor_handle
132     uint16_t value_handle;
133     uint16_t end_handle;
134     uint16_t properties;
135 
136     // UUID of external Report characteristic, stored in Report Map descriptor EXTERNAL_REPORT_REFERENCE
137     uint16_t external_report_reference_uuid;
138 
139 #ifdef ENABLE_TESTING_SUPPORT
140     uint16_t ccc_handle;
141 #endif
142 
143     // service mapping
144     uint8_t service_index;
145     uint8_t report_id;
146     hid_report_type_t report_type;
147     uint8_t boot_report;
148     gatt_client_notification_t notification_listener;
149 } hids_client_report_t;
150 
151 typedef struct {
152     hid_protocol_mode_t protocol_mode;
153 
154     uint16_t start_handle;
155     uint16_t end_handle;
156 
157     uint16_t report_map_value_handle;
158     uint16_t report_map_end_handle;
159 
160     uint16_t hid_information_value_handle;
161     uint16_t control_point_value_handle;
162     uint16_t protocol_mode_value_handle;
163 
164     // descriptor storage
165     uint16_t hid_descriptor_offset;
166     uint16_t hid_descriptor_len;
167     uint16_t hid_descriptor_max_len;
168     uint8_t  hid_descriptor_status;     // ERROR_CODE_SUCCESS if descriptor available,
169                                         // ERROR_CODE_UNSUPPORTED_FEATURE_OR_PARAMETER_VALUE if not, and
170                                         // ERROR_CODE_MEMORY_CAPACITY_EXCEEDED if descriptor is larger then the available space
171 } hid_service_t;
172 
173 typedef struct {
174     btstack_linked_item_t item;
175 
176     hci_con_handle_t  con_handle;
177     uint16_t          cid;
178 
179     hid_service_client_state_t state;
180     btstack_packet_handler_t   client_handler;
181 
182     uint8_t num_instances;
183     hid_service_t services[MAX_NUM_HID_SERVICES];
184 
185     // used for discovering characteristics
186     uint8_t service_index;
187     hid_protocol_mode_t required_protocol_mode;
188 
189     // send report
190     hids_client_report_t reports[HIDS_CLIENT_NUM_REPORTS];
191     uint8_t num_reports;
192 
193     hids_client_report_t external_reports[HIDS_CLIENT_NUM_REPORTS];
194     uint8_t num_external_reports;
195 
196     // index used for report and report map search
197     uint8_t   report_index;
198     uint16_t  report_len;
199     const uint8_t * report;
200     // used to write control_point and  protocol_mode
201     uint16_t handle;
202     uint8_t  value;
203 } hids_client_t;
204 
205 /* API_START */
206 
207 /**
208  * @brief Initialize HID Service Client. The HID Descriptor storage is shared between all connections.
209  *
210  * @param hid_descriptor_storage
211  * @param hid_descriptor_storage_len
212  */
213 void hids_client_init(uint8_t * hid_descriptor_storage, uint16_t hid_descriptor_storage_len);
214 
215 /* @brief Connect to HID Services of remote device. Event GATTSERVICE_SUBEVENT_HID_SERVICE_CONNECTED will be emitted
216  * after all remote HID services and characteristics are found, and notifications for all input reports are enabled.
217  * Status code can be ERROR_CODE_SUCCES if at least one HID service is found, otherwise either ATT errors or
218  * ERROR_CODE_UNSUPPORTED_FEATURE_OR_PARAMETER_VALUE if no service or report map or report are found.
219  * It also contains the number of individual HIDS Services.
220  *
221  * @param con_handle
222  * @param packet_handler
223  * @param protocol_mode see hid_protocol_mode_t in btstack_hid.h
224  * @param hids_cid (out) to use for other functions
225  * @return status ERROR_CODE_SUCCESS on success, otherwise ERROR_CODE_COMMAND_DISALLOWED if there is already a client
226  *         associated with con_handle, or BTSTACK_MEMORY_ALLOC_FAILED.
227  */
228 uint8_t hids_client_connect(hci_con_handle_t con_handle, btstack_packet_handler_t packet_handler, hid_protocol_mode_t protocol_mode, uint16_t * hids_cid);
229 
230 /**
231  * @brief Send HID report.
232  *
233  * @param hids_cid
234  * @param report_id
235  * @param report_type
236  * @param report
237  * @param report_len
238  * @return status ERROR_CODE_SUCCESS on success, otherwise ERROR_CODE_UNKNOWN_CONNECTION_IDENTIFIER,
239  * ERROR_CODE_COMMAND_DISALLOWED if client is in wrong state,
240  * ERROR_CODE_UNSUPPORTED_FEATURE_OR_PARAMETER_VALUE if no report with given type and ID is found, or
241  * ERROR_CODE_PARAMETER_OUT_OF_MANDATORY_RANGE if report length exceeds MTU.
242  */
243 uint8_t hids_client_send_write_report(uint16_t hids_cid, uint8_t report_id, hid_report_type_t report_type, const uint8_t * report, uint8_t report_len);
244 
245 /**
246  * @brief Get HID report. Event GATTSERVICE_SUBEVENT_HID_REPORT is emitted.
247  *
248  * @param hids_cid
249  * @param report_id
250  * @param report_type
251  * @return status ERROR_CODE_SUCCESS on success, otherwise ERROR_CODE_UNKNOWN_CONNECTION_IDENTIFIER,
252  * ERROR_CODE_COMMAND_DISALLOWED if client is in wrong state,
253  * ERROR_CODE_UNSUPPORTED_FEATURE_OR_PARAMETER_VALUE if no report with given type and ID is found, or
254  * ERROR_CODE_PARAMETER_OUT_OF_MANDATORY_RANGE if report length exceeds MTU.
255  */
256 uint8_t hids_client_send_get_report(uint16_t hids_cid, uint8_t report_id, hid_report_type_t report_type);
257 
258 /**
259  * @brief Get HID Information. Event GATTSERVICE_SUBEVENT_HID_INFORMATION is emitted.
260  *
261  * @param hids_cid
262  * @param service_index
263  * @return status ERROR_CODE_SUCCESS on success, otherwise ERROR_CODE_UNKNOWN_CONNECTION_IDENTIFIER,
264  * ERROR_CODE_COMMAND_DISALLOWED if client is in wrong state, or
265  * ERROR_CODE_UNSUPPORTED_FEATURE_OR_PARAMETER_VALUE if no report with given type and ID is found.
266  */
267 uint8_t hids_client_get_hid_information(uint16_t hids_cid, uint8_t service_index);
268 
269 /**
270  * @brief Get Protocol Mode. Event GATTSERVICE_SUBEVENT_HID_PROTOCOL_MODE is emitted.
271  *
272  * @param hids_cid
273  * @param service_index
274  * @return status ERROR_CODE_SUCCESS on success, otherwise ERROR_CODE_UNKNOWN_CONNECTION_IDENTIFIER,
275  * ERROR_CODE_COMMAND_DISALLOWED if client is in wrong state, or
276  * ERROR_CODE_UNSUPPORTED_FEATURE_OR_PARAMETER_VALUE if no report with given type and ID is found.
277  */
278 uint8_t hids_client_get_protocol_mode(uint16_t hids_cid, uint8_t service_index);
279 
280 /**
281  * @brief Set Protocol Mode.
282  *
283  * @param hids_cid
284  * @param service_index
285  * @param protocol_mode
286  * @return status ERROR_CODE_SUCCESS on success, otherwise ERROR_CODE_UNKNOWN_CONNECTION_IDENTIFIER,
287  * ERROR_CODE_COMMAND_DISALLOWED if client is in wrong state, or
288  * ERROR_CODE_UNSUPPORTED_FEATURE_OR_PARAMETER_VALUE if no report with given type and ID is found.
289  */
290 uint8_t hids_client_send_set_protocol_mode(uint16_t hids_cid, uint8_t service_index, hid_protocol_mode_t protocol_mode);
291 
292 /**
293  * @brief Send Suspend to remote HID service.
294  *
295  * @param hids_cid
296  * @param service_index
297  * @return status ERROR_CODE_SUCCESS on success, otherwise ERROR_CODE_UNKNOWN_CONNECTION_IDENTIFIER,
298  * ERROR_CODE_COMMAND_DISALLOWED if client is in wrong state, or
299  * ERROR_CODE_UNSUPPORTED_FEATURE_OR_PARAMETER_VALUE if no report with given type and ID is found.
300  */
301 uint8_t hids_client_send_suspend(uint16_t hids_cid, uint8_t service_index);
302 
303 /**
304  * @brief Send Exit Suspend to remote HID service.
305  *
306  * @param hids_cid
307  * @param service_index
308  * @return status ERROR_CODE_SUCCESS on success, otherwise ERROR_CODE_UNKNOWN_CONNECTION_IDENTIFIER,
309  * ERROR_CODE_COMMAND_DISALLOWED if client is in wrong state, or
310  * ERROR_CODE_UNSUPPORTED_FEATURE_OR_PARAMETER_VALUE if no report with given type and ID is found.
311  */
312 uint8_t hids_client_send_exit_suspend(uint16_t hids_cid, uint8_t service_index);
313 
314 /**
315  * @brief Enable all notifications. Event GATTSERVICE_SUBEVENT_HID_SERVICE_REPORTS_NOTIFICATION reports current configuration.
316  *
317  * @param hids_cid
318  * @return status ERROR_CODE_SUCCESS on success, otherwise ERROR_CODE_UNKNOWN_CONNECTION_IDENTIFIER, or
319  * ERROR_CODE_COMMAND_DISALLOWED if client is in wrong state.
320  */
321 uint8_t hids_client_enable_notifications(uint16_t hids_cid);
322 
323 /**
324  * @brief Disable all notifications. Event GATTSERVICE_SUBEVENT_HID_SERVICE_REPORTS_NOTIFICATION reports current configuration.
325  *
326  * @param hids_cid
327  * @return status ERROR_CODE_SUCCESS on success, otherwise ERROR_CODE_UNKNOWN_CONNECTION_IDENTIFIER, or
328  * ERROR_CODE_COMMAND_DISALLOWED if client is in wrong state.
329  */
330 uint8_t hids_client_disable_notifications(uint16_t hids_cid);
331 
332 /**
333  * @brief Disconnect from HID Service.
334  *
335  * @param hids_cid
336  * @return status ERROR_CODE_SUCCESS on success, otherwise ERROR_CODE_UNKNOWN_CONNECTION_IDENTIFIER
337  */
338 uint8_t hids_client_disconnect(uint16_t hids_cid);
339 
340 /*
341  * @brief Get descriptor data. For services in boot mode without a Report Map, a default HID Descriptor for Keyboard/Mouse is provided.
342  *
343  * @param hid_cid
344  * @return data
345  */
346 const uint8_t * hids_client_descriptor_storage_get_descriptor_data(uint16_t hids_cid, uint8_t service_index);
347 
348 /*
349  * @brief Get descriptor length
350  *
351  * @param hid_cid
352  * @return length
353  */
354 uint16_t hids_client_descriptor_storage_get_descriptor_len(uint16_t hids_cid, uint8_t service_index);
355 
356 /**
357  * @brief De-initialize HID Service Client.
358  *
359  */
360 void hids_client_deinit(void);
361 
362 /* API_END */
363 
364 #if defined __cplusplus
365 }
366 #endif
367 
368 #endif
369