xref: /btstack/src/ble/gatt-service/hids_device.c (revision bbf45f896fd139c6f30b084f4679e6de4ef829d8)
1 /*
2  * Copyright (C) 2014 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 BLUEKITCHEN
24  * GMBH 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 #define BTSTACK_FILE__ "hids_device.c"
39 
40 /**
41  * Implementation of the GATT HIDS Device
42  * To use with your application, add '#import <hids.gatt>' to your .gatt file
43  */
44 
45 #include "hids_device.h"
46 
47 #include "ble/att_db.h"
48 #include "ble/att_server.h"
49 #include "bluetooth_gatt.h"
50 #include "btstack_util.h"
51 #include "btstack_debug.h"
52 #include "btstack_hid_parser.h"
53 
54 #define HIDS_DEVICE_ERROR_CODE_INAPPROPRIATE_CONNECTION_PARAMETERS    0x80
55 
56 // storage for 'generic' HID Device with single Input, Output, and Feature Report
57 static hids_device_report_t hid_reports_generic_storage[3];
58 
59 typedef struct{
60     uint16_t        con_handle;
61 
62     uint8_t         hid_country_code;
63     const uint8_t * hid_descriptor;
64     uint16_t        hid_descriptor_size;
65 
66     uint16_t        hid_report_map_handle;
67     uint8_t         hid_protocol_mode;
68     uint16_t        hid_protocol_mode_value_handle;
69 
70     uint16_t        hid_boot_mouse_input_value_handle;
71     uint16_t        hid_boot_mouse_input_client_configuration_handle;
72     uint16_t        hid_boot_mouse_input_client_configuration_value;
73 
74     uint16_t        hid_boot_keyboard_input_value_handle;
75     uint16_t        hid_boot_keyboard_input_client_configuration_handle;
76     uint16_t        hid_boot_keyboard_input_client_configuration_value;
77 
78     uint16_t        hid_boot_keyboard_output_value_handle;
79 
80     hids_device_report_t * hid_reports;
81 
82     uint8_t         hid_input_reports_num;
83     uint8_t         hid_output_reports_num;
84     uint8_t         hid_feature_reports_num;
85 
86     uint16_t        hid_control_point_value_handle;
87     uint8_t         hid_control_point_suspend;
88 
89     btstack_context_callback_registration_t  can_send_now_callback;
90 } hids_device_t;
91 
92 static hids_device_t hids_device;
93 
94 static btstack_packet_handler_t packet_handler;
95 static att_service_handler_t hid_service;
96 static void (*hids_device_get_report_callback)(hci_con_handle_t hid_cid, hid_report_type_t report_type, uint16_t report_id, uint16_t max_report_size, uint8_t * out_report);
97 
98 // TODO: store hids device connection into list
99 static hids_device_t * hids_device_get_instance_for_con_handle(uint16_t con_handle){
100     UNUSED(con_handle);
101     return &hids_device;
102 }
103 
104 static hids_device_t * hids_device_create_instance(void){
105     memset(&hids_device, 0, sizeof(hids_device_t));
106     return &hids_device;
107 }
108 
109 static hids_device_report_t * hids_device_get_report_for_value_handle(hids_device_t * device, uint16_t value_handle){
110     uint8_t pos;
111     uint8_t total_reports =  device->hid_input_reports_num + device->hid_output_reports_num + device->hid_feature_reports_num;
112     for (pos = 0 ; pos < total_reports ; pos++){
113         if (device->hid_reports[pos].value_handle == value_handle){
114             return &device->hid_reports[pos];
115         }
116     }
117     return NULL;
118 }
119 
120 static hids_device_report_t * hids_device_get_report_for_client_configuration_handle(hids_device_t * device, uint16_t client_configuration_handle){
121     uint8_t pos;
122     uint8_t total_reports =  device->hid_input_reports_num + device->hid_output_reports_num + device->hid_feature_reports_num;
123     for (pos = 0 ; pos < total_reports ; pos++){
124         if (device->hid_reports[pos].client_configuration_handle == client_configuration_handle){
125             return &device->hid_reports[pos];
126         }
127     }
128     return NULL;
129 }
130 
131 static hids_device_report_t *
132 hids_device_get_report_for_id_and_type(hids_device_t *device, uint16_t report_id, hid_report_type_t type) {
133     uint8_t pos;
134     uint8_t total_reports =  device->hid_input_reports_num + device->hid_output_reports_num + device->hid_feature_reports_num;
135     for (pos = 0 ; pos < total_reports ; pos++){
136         if ((device->hid_reports[pos].type == type) && (device->hid_reports[pos].id == report_id)){
137             return &device->hid_reports[pos];
138         }
139     }
140     return NULL;
141 }
142 
143 static void hids_device_emit_event_with_uint8(uint8_t event, hci_con_handle_t con_handle, uint8_t value){
144     hids_device_t * instance = hids_device_get_instance_for_con_handle(con_handle);
145     if (!instance){
146         log_error("no instance for handle 0x%02x", con_handle);
147         return;
148     }
149 
150     if (!packet_handler) return;
151     uint8_t buffer[6];
152     buffer[0] = HCI_EVENT_HIDS_META;
153     buffer[1] = 4;
154     buffer[2] = event;
155     little_endian_store_16(buffer, 3, (uint16_t) con_handle);
156     buffer[5] = value;
157     (*packet_handler)(HCI_EVENT_PACKET, 0, buffer, sizeof(buffer));
158 }
159 
160 static void hids_device_emit_event_with_uint8_uint8_t(uint8_t event, hci_con_handle_t con_handle, uint8_t value_1, uint8_t value_2){
161     hids_device_t * instance = hids_device_get_instance_for_con_handle(con_handle);
162     if (!instance){
163         log_error("no instance for handle 0x%02x", con_handle);
164         return;
165     }
166 
167     if (!packet_handler) return;
168     uint8_t buffer[7];
169     buffer[0] = HCI_EVENT_HIDS_META;
170     buffer[1] = 4;
171     buffer[2] = event;
172     little_endian_store_16(buffer, 3, (uint16_t) con_handle);
173     buffer[5] = value_1;
174     buffer[6] = value_2;
175     (*packet_handler)(HCI_EVENT_PACKET, 0, buffer, sizeof(buffer));
176 }
177 
178 static void hids_device_emit_event(uint8_t event, hci_con_handle_t con_handle){
179     hids_device_t * instance = hids_device_get_instance_for_con_handle(con_handle);
180     if (!instance){
181         log_error("no instance for handle 0x%02x", con_handle);
182         return;
183     }
184 
185     if (!packet_handler) return;
186     uint8_t buffer[5];
187     buffer[0] = HCI_EVENT_HIDS_META;
188     buffer[1] = 4;
189     buffer[2] = event;
190     little_endian_store_16(buffer, 3, (uint16_t) con_handle);
191     (*packet_handler)(HCI_EVENT_PACKET, 0, buffer, sizeof(buffer));
192 }
193 
194 static void hids_device_can_send_now(void * context){
195     hci_con_handle_t con_handle = (hci_con_handle_t) (uintptr_t) context;
196     // notify client
197     hids_device_t * instance = hids_device_get_instance_for_con_handle(con_handle);
198     if (!instance){
199         log_error("no instance for handle 0x%02x", con_handle);
200         return;
201     }
202 
203     if (!packet_handler) return;
204     uint8_t buffer[5];
205     buffer[0] = HCI_EVENT_HIDS_META;
206     buffer[1] = 3;
207     buffer[2] = HIDS_SUBEVENT_CAN_SEND_NOW;
208     little_endian_store_16(buffer, 3, (uint16_t) con_handle);
209     (*packet_handler)(HCI_EVENT_PACKET, 0, buffer, sizeof(buffer));
210 }
211 
212 // ATT Client Read Callback for Dynamic Data
213 // - if buffer == NULL, don't copy data, just return size of value
214 // - if buffer != NULL, copy data and return number bytes copied
215 static uint16_t att_read_callback(hci_con_handle_t con_handle, uint16_t att_handle, uint16_t offset, uint8_t * buffer, uint16_t buffer_size){
216     hids_device_t * instance = hids_device_get_instance_for_con_handle(con_handle);
217     if (!instance){
218         log_error("no instance for handle 0x%02x", con_handle);
219         return HIDS_DEVICE_ERROR_CODE_INAPPROPRIATE_CONNECTION_PARAMETERS;
220     }
221 
222     if (att_handle == instance->hid_protocol_mode_value_handle){
223         log_info("Read protocol mode");
224         return att_read_callback_handle_byte(instance->hid_protocol_mode, offset, buffer, buffer_size);
225     }
226 
227     if (att_handle == instance->hid_report_map_handle){
228         log_info("Read report map");
229         return att_read_callback_handle_blob(instance->hid_descriptor, instance->hid_descriptor_size, offset, buffer, buffer_size);
230     }
231 
232     if (att_handle == instance->hid_boot_mouse_input_client_configuration_handle){
233         return att_read_callback_handle_little_endian_16(instance->hid_boot_mouse_input_client_configuration_value, offset, buffer, buffer_size);
234     }
235 
236     if (att_handle == instance->hid_boot_keyboard_input_client_configuration_handle){
237         return att_read_callback_handle_little_endian_16(instance->hid_boot_keyboard_input_client_configuration_value, offset, buffer, buffer_size);
238     }
239 
240     if (att_handle == instance->hid_control_point_value_handle){
241         if (buffer && (buffer_size >= 1u)){
242             buffer[0] = instance->hid_control_point_suspend;
243         }
244         return 1;
245     }
246 
247     if (att_handle == instance->hid_boot_keyboard_output_value_handle){
248         // TODO
249         return 0;
250     }
251 
252     uint8_t boot_report_size = 0;
253     if (att_handle == instance->hid_boot_mouse_input_value_handle){
254         boot_report_size = 3;
255     }
256     if (att_handle == instance->hid_boot_keyboard_input_value_handle){
257         boot_report_size = 8;
258     }
259     if (boot_report_size != 0){
260         // no callback, no report
261         if (hids_device_get_report_callback == NULL){
262             return 0;
263         }
264         // answer length request by ATT Server
265         if (buffer == NULL){
266             return boot_report_size;
267         } else {
268             // Report ID 0, Type Input
269             (*hids_device_get_report_callback)(con_handle, HID_REPORT_TYPE_INPUT, 0, boot_report_size, buffer);
270             return boot_report_size;
271         }
272     }
273 
274     hids_device_report_t * report;
275     report = hids_device_get_report_for_value_handle(instance, att_handle);
276     if (report != NULL){
277         // no callback, no report
278         if (hids_device_get_report_callback == NULL){
279             return 0;
280         }
281         // answer length request by ATT Server
282         if (buffer == NULL){
283             return report->size;
284         } else {
285             uint16_t max_size = btstack_min(report->size, buffer_size);
286             (*hids_device_get_report_callback)(con_handle, report->type, report->id, max_size, buffer);
287             return report->size;
288         }
289     }
290 
291     report = hids_device_get_report_for_client_configuration_handle(instance, att_handle);
292     if (report != NULL){
293         return att_read_callback_handle_little_endian_16(report->client_configuration_value, offset, buffer, buffer_size);
294     }
295     return 0;
296 }
297 
298 static int att_write_callback(hci_con_handle_t con_handle, uint16_t att_handle, uint16_t transaction_mode, uint16_t offset, uint8_t *buffer, uint16_t buffer_size){
299     UNUSED(buffer_size);
300     UNUSED(offset);
301 
302     if (transaction_mode != ATT_TRANSACTION_MODE_NONE){
303         return 0;
304     }
305 
306     hids_device_t * instance = hids_device_get_instance_for_con_handle(con_handle);
307     if (!instance){
308         log_error("no instance for handle 0x%02x", con_handle);
309         return HIDS_DEVICE_ERROR_CODE_INAPPROPRIATE_CONNECTION_PARAMETERS;
310     }
311 
312     if (att_handle == instance->hid_boot_mouse_input_client_configuration_handle){
313         uint16_t new_value = little_endian_read_16(buffer, 0);
314         instance->hid_boot_mouse_input_client_configuration_value = new_value;
315         hids_device_emit_event_with_uint8(HIDS_SUBEVENT_BOOT_MOUSE_INPUT_REPORT_ENABLE, con_handle, (uint8_t) new_value);
316         return 0;
317     }
318     if (att_handle == instance->hid_boot_keyboard_input_client_configuration_handle){
319         uint16_t new_value = little_endian_read_16(buffer, 0);
320         instance->hid_boot_keyboard_input_client_configuration_value = new_value;
321         hids_device_emit_event_with_uint8(HIDS_SUBEVENT_BOOT_KEYBOARD_INPUT_REPORT_ENABLE, con_handle, (uint8_t) new_value);
322         return 0;
323     }
324 
325     if (att_handle == instance->hid_protocol_mode_value_handle){
326         instance->hid_protocol_mode = buffer[0];
327         log_info("Set protocol mode: %u", instance->hid_protocol_mode);
328         hids_device_emit_event_with_uint8(HIDS_SUBEVENT_PROTOCOL_MODE, con_handle, instance->hid_protocol_mode);
329         return 0;
330     }
331 
332     if (att_handle == instance->hid_control_point_value_handle){
333         if (buffer_size < 1u){
334             return ATT_ERROR_INVALID_OFFSET;
335         }
336         instance->hid_control_point_suspend = buffer[0];
337         instance->con_handle = con_handle;
338         log_info("Set suspend tp: %u", instance->hid_control_point_suspend );
339         if (instance->hid_control_point_suspend == 0u){
340             hids_device_emit_event(HIDS_SUBEVENT_SUSPEND, con_handle);
341         } else if (instance->hid_control_point_suspend == 1u){
342             hids_device_emit_event(HIDS_SUBEVENT_EXIT_SUSPEND, con_handle);
343         }
344         return 0;
345     }
346 
347     if (att_handle == instance->hid_boot_keyboard_output_value_handle){
348         // TODO
349         return 0;
350     }
351 
352     hids_device_report_t * report;
353     report = hids_device_get_report_for_value_handle(instance, att_handle);
354     if (report != NULL){
355         // assemble event in buffer
356         uint8_t event[257];
357         uint16_t pos = 0;
358         event[pos++] = HCI_EVENT_HIDS_META;
359         // skip length
360         pos++;
361         event[pos++] = HIDS_SUBEVENT_SET_REPORT;
362         little_endian_store_16(event, pos, con_handle);
363         pos += 2;
364         event[pos++] = report->id;
365         event[pos++] = (uint8_t) report->type;
366         uint8_t length_to_copy = btstack_min(buffer_size, 250);
367         event[pos++] = length_to_copy;
368         memcpy(&event[pos], buffer, length_to_copy);
369         pos += length_to_copy;
370         // set event length
371         event[1] = pos - 2;
372         (*packet_handler)(HCI_EVENT_PACKET, 0, event, pos);
373         return 0;
374     }
375 
376     report = hids_device_get_report_for_client_configuration_handle(instance, att_handle);
377     if (report != NULL){
378         uint16_t new_value = little_endian_read_16(buffer, 0);
379         report->client_configuration_value = new_value;
380         log_info("Enable Report (type %u) notifications: %x", (uint8_t) report->type, new_value);
381 
382         switch (report->type){
383             case HID_REPORT_TYPE_INPUT:
384                 hids_device_emit_event_with_uint8_uint8_t(HIDS_SUBEVENT_INPUT_REPORT_ENABLE, con_handle, report->id, (uint8_t) new_value);
385                 break;
386             case HID_REPORT_TYPE_OUTPUT:
387                 hids_device_emit_event_with_uint8_uint8_t(HIDS_SUBEVENT_OUTPUT_REPORT_ENABLE, con_handle, report->id, (uint8_t) new_value);
388                 break;
389             case HID_REPORT_TYPE_FEATURE:
390                 hids_device_emit_event_with_uint8_uint8_t(HIDS_SUBEVENT_FEATURE_REPORT_ENABLE, con_handle, report->id, (uint8_t) new_value);
391                 break;
392             default:
393                 btstack_unreachable();
394                 break;
395         }
396     }
397     return 0;
398 }
399 
400 void hids_device_init_with_storage(uint8_t hid_country_code, const uint8_t * hid_descriptor, uint16_t hid_descriptor_size,
401     uint16_t num_reports, hids_device_report_t * report_storage){
402 
403     hids_device_t * instance = hids_device_create_instance();
404 
405     btstack_assert(num_reports > 0);
406     btstack_assert(report_storage != NULL);
407 
408     instance->hid_country_code    = hid_country_code;
409     instance->hid_descriptor      = hid_descriptor;
410     instance->hid_descriptor_size = hid_descriptor_size;
411 
412     // default
413     instance->hid_protocol_mode   = 1;
414 
415     // get service handle range
416     uint16_t start_handle = 0;
417     uint16_t end_handle   = 0xffff;
418     int service_found = gatt_server_get_handle_range_for_service_with_uuid16(ORG_BLUETOOTH_SERVICE_HUMAN_INTERFACE_DEVICE, &start_handle, &end_handle);
419     btstack_assert(service_found != 0);
420     UNUSED(service_found);
421 
422     // get report map handle
423     instance->hid_report_map_handle                               = gatt_server_get_value_handle_for_characteristic_with_uuid16(start_handle, end_handle, ORG_BLUETOOTH_CHARACTERISTIC_REPORT_MAP);
424 
425     // get report map handle
426     instance->hid_protocol_mode_value_handle                      = gatt_server_get_value_handle_for_characteristic_with_uuid16(start_handle, end_handle, ORG_BLUETOOTH_CHARACTERISTIC_PROTOCOL_MODE);
427 
428     // get value and client configuration handles for boot mouse input, boot keyboard input and report input
429     instance->hid_boot_mouse_input_value_handle                   = gatt_server_get_value_handle_for_characteristic_with_uuid16(start_handle, end_handle, ORG_BLUETOOTH_CHARACTERISTIC_BOOT_MOUSE_INPUT_REPORT);
430     instance->hid_boot_mouse_input_client_configuration_handle    = gatt_server_get_client_configuration_handle_for_characteristic_with_uuid16(start_handle, end_handle, ORG_BLUETOOTH_CHARACTERISTIC_BOOT_MOUSE_INPUT_REPORT);
431 
432     instance->hid_boot_keyboard_input_value_handle                = gatt_server_get_value_handle_for_characteristic_with_uuid16(start_handle, end_handle, ORG_BLUETOOTH_CHARACTERISTIC_BOOT_KEYBOARD_INPUT_REPORT);
433     instance->hid_boot_keyboard_input_client_configuration_handle = gatt_server_get_client_configuration_handle_for_characteristic_with_uuid16(start_handle, end_handle, ORG_BLUETOOTH_CHARACTERISTIC_BOOT_KEYBOARD_INPUT_REPORT);
434 
435     instance->hid_boot_keyboard_output_value_handle               = gatt_server_get_value_handle_for_characteristic_with_uuid16(start_handle, end_handle, ORG_BLUETOOTH_CHARACTERISTIC_BOOT_KEYBOARD_OUTPUT_REPORT);
436 
437     instance->hid_control_point_value_handle = gatt_server_get_value_handle_for_characteristic_with_uuid16(start_handle, end_handle, ORG_BLUETOOTH_CHARACTERISTIC_HID_CONTROL_POINT);
438 
439     log_info("hid_report_map_handle                               0x%02x", instance->hid_report_map_handle);
440     log_info("hid_protocol_mode_value_handle                      0x%02x", instance->hid_protocol_mode_value_handle);
441     log_info("hid_boot_mouse_input_value_handle                   0x%02x", instance->hid_boot_mouse_input_value_handle);
442     log_info("hid_boot_mouse_input_client_configuration_handle    0x%02x", instance->hid_boot_mouse_input_client_configuration_handle);
443     log_info("hid_boot_keyboard_input_value_handle                0x%02x", instance->hid_boot_keyboard_input_value_handle);
444     log_info("hid_boot_keyboard_output_value_handle               0x%02x", instance->hid_boot_keyboard_output_value_handle);
445     log_info("hid_boot_keyboard_input_client_configuration_handle 0x%02x", instance->hid_boot_keyboard_input_client_configuration_handle);
446     log_info("hid_control_point_value_handle                      0x%02x", instance->hid_control_point_value_handle);
447 
448     instance->hid_reports = report_storage;
449 
450     uint16_t hid_reports_num = num_reports;
451     uint16_t assigned_reports_num = 0;
452     uint16_t start_chr_handle = start_handle;
453 
454     while ( (start_chr_handle < end_handle) && (assigned_reports_num < hid_reports_num)) {
455         // mandatory
456         uint16_t chr_value_handle = gatt_server_get_value_handle_for_characteristic_with_uuid16(start_chr_handle, end_handle, ORG_BLUETOOTH_CHARACTERISTIC_REPORT);
457         if (chr_value_handle == 0){
458             break;
459         }
460 
461         // optional
462         uint16_t chr_client_configuration_handle = gatt_server_get_client_configuration_handle_for_characteristic_with_uuid16(start_chr_handle, end_handle, ORG_BLUETOOTH_CHARACTERISTIC_REPORT);
463 
464         // mandatory
465         uint16_t report_reference_handle = gatt_server_get_descriptor_handle_for_characteristic_with_uuid16(start_chr_handle, end_handle, ORG_BLUETOOTH_CHARACTERISTIC_REPORT, ORG_BLUETOOTH_DESCRIPTOR_REPORT_REFERENCE);
466         if (report_reference_handle == 0){
467             break;
468         }
469 
470         // get report id and type from report reference
471         uint16_t report_reference_value_len;
472         const uint8_t * report_reference_value = gatt_server_get_const_value_for_handle(report_reference_handle, &report_reference_value_len);
473         if (report_reference_value == NULL){
474             break;
475         }
476         if (report_reference_value_len != 2){
477             break;
478         }
479         uint8_t report_id = report_reference_value[0];
480         hid_report_type_t report_type = (hid_report_type_t) report_reference_value[1];
481 
482         // store report info
483         hids_device_report_t * report = &report_storage[assigned_reports_num];
484         report->value_handle = chr_value_handle;
485         report->client_configuration_handle = chr_client_configuration_handle;
486         report->client_configuration_value = 0;
487         report->id   = report_id;
488         report->type = report_type;
489         report->size = btstack_hid_get_report_size_for_id(report_id, report_type, hid_descriptor_size, hid_descriptor);
490 
491         switch (report->type){
492             case HID_REPORT_TYPE_INPUT:
493                 instance->hid_input_reports_num++;
494                 break;
495             case HID_REPORT_TYPE_OUTPUT:
496                 instance->hid_output_reports_num++;
497                 break;
498             case HID_REPORT_TYPE_FEATURE:
499                 instance->hid_feature_reports_num++;
500                 break;
501             default:
502                 btstack_unreachable();
503                 return;
504         }
505         log_info("hid_report_value_handle                       0x%02x, id %u, type %u", report->value_handle, report->id, (uint8_t)report->type);
506         if (report->client_configuration_handle != 0){
507             log_info("hid_report_client_configuration_handle        0x%02x", report->client_configuration_handle);
508         }
509 
510         assigned_reports_num++;
511         start_chr_handle = report_reference_handle + 1;
512     }
513 
514     // register service with ATT Server
515     hid_service.start_handle   = start_handle;
516     hid_service.end_handle     = end_handle;
517     hid_service.read_callback  = &att_read_callback;
518     hid_service.write_callback = &att_write_callback;
519     att_server_register_service_handler(&hid_service);
520 }
521 
522 /**
523  * @brief Set up HIDS Device
524  */
525 void hids_device_init(uint8_t country_code, const uint8_t * descriptor, uint16_t descriptor_size){
526     uint16_t hid_reports_num = sizeof(hid_reports_generic_storage) / sizeof(hids_device_report_t);
527     hids_device_init_with_storage(country_code, descriptor, descriptor_size, hid_reports_num, hid_reports_generic_storage);
528 }
529 
530 /**
531  * @brief Register callback for the HIDS Device client.
532  * @param callback
533  */
534 void hids_device_register_packet_handler(btstack_packet_handler_t callback){
535     packet_handler = callback;
536 }
537 
538 void hids_device_register_get_report_callback(void (*callback)(hci_con_handle_t con_handle, hid_report_type_t report_type, uint16_t report_id, uint16_t max_report_size, uint8_t * out_report)){
539     hids_device_get_report_callback = callback;
540 }
541 
542 /**
543  * @brief Request can send now event to send HID Report
544  * Generates an HIDS_SUBEVENT_CAN_SEND_NOW subevent
545  * @param hid_cid
546  */
547 void hids_device_request_can_send_now_event(hci_con_handle_t con_handle){
548     hids_device_t * instance = hids_device_get_instance_for_con_handle(con_handle);
549     if (!instance){
550         log_error("no instance for handle 0x%02x", con_handle);
551         return;
552     }
553 
554     instance->can_send_now_callback.callback = &hids_device_can_send_now;
555     instance->can_send_now_callback.context  = (void*) (uintptr_t) con_handle;
556     att_server_register_can_send_now_callback(&instance->can_send_now_callback, con_handle);
557 }
558 
559 uint8_t hids_device_send_input_report_for_id(hci_con_handle_t con_handle, uint16_t report_id, const uint8_t * report, uint16_t report_len){
560     hids_device_t * instance = hids_device_get_instance_for_con_handle(con_handle);
561     if (!instance){
562         log_error("no instance for handle 0x%02x", con_handle);
563         return ERROR_CODE_UNKNOWN_CONNECTION_IDENTIFIER;
564     }
565 
566     hids_device_report_t * report_storage = hids_device_get_report_for_id_and_type(instance, report_id,
567                                                                                    HID_REPORT_TYPE_INPUT);
568     if (report_storage == NULL){
569         return ERROR_CODE_UNSUPPORTED_FEATURE_OR_PARAMETER_VALUE;
570     }
571 
572     return att_server_notify(con_handle, report_storage->value_handle, report, report_len);
573 }
574 
575 uint8_t hids_device_send_input_report(hci_con_handle_t con_handle, const uint8_t * report, uint16_t report_len){
576     hids_device_t * device = hids_device_get_instance_for_con_handle(con_handle);
577     if (!device){
578         log_error("no instance for handle 0x%02x", con_handle);
579         return ERROR_CODE_UNKNOWN_CONNECTION_IDENTIFIER;
580     }
581 
582     uint8_t pos;
583     uint8_t total_reports =  device->hid_input_reports_num + device->hid_output_reports_num + device->hid_feature_reports_num;
584     for (pos = 0 ; pos < total_reports ; pos++){
585         hids_device_report_t * report_storage = &device->hid_reports[pos];
586         if (report_storage->type == HID_REPORT_TYPE_INPUT){
587             return att_server_notify(con_handle, report_storage->value_handle, report, report_len);
588         }
589     }
590     return ERROR_CODE_UNSUPPORTED_FEATURE_OR_PARAMETER_VALUE;
591 }
592 
593 /**
594  * @brief Send HID Boot Mouse Input Report
595  */
596 uint8_t hids_device_send_boot_mouse_input_report(hci_con_handle_t con_handle, const uint8_t * report, uint16_t report_len){
597     hids_device_t * instance = hids_device_get_instance_for_con_handle(con_handle);
598     if (!instance){
599         log_error("no instance for handle 0x%02x", con_handle);
600         return ERROR_CODE_UNKNOWN_CONNECTION_IDENTIFIER;
601     }
602     return att_server_notify(con_handle, instance->hid_boot_mouse_input_value_handle, report, report_len);
603 }
604 
605 /**
606  * @brief Send HID Boot Mouse Input Report
607  */
608 uint8_t hids_device_send_boot_keyboard_input_report(hci_con_handle_t con_handle, const uint8_t * report, uint16_t report_len){
609     hids_device_t * instance = hids_device_get_instance_for_con_handle(con_handle);
610     if (!instance){
611         log_error("no instance for handle 0x%02x", con_handle);
612         return ERROR_CODE_UNKNOWN_CONNECTION_IDENTIFIER;
613     }
614     return att_server_notify(con_handle, instance->hid_boot_keyboard_input_value_handle, report, report_len);
615 }
616