xref: /btstack/src/ble/gatt-service/hids_device.c (revision d72e9d4b1d8cea3c48e9897368c446f554491bbf)
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_emit_report(hci_con_handle_t con_handle, const uint8_t report_id, const uint8_t report_type,
195                                     const uint8_t *buffer, uint16_t buffer_size) {
196     // assemble event in buffer
197     uint8_t event[257];
198     uint16_t pos = 0;
199     event[pos++] = HCI_EVENT_HIDS_META;
200     // skip length
201     pos++;
202     event[pos++] = HIDS_SUBEVENT_SET_REPORT;
203     little_endian_store_16(event, pos, con_handle);
204     pos += 2;
205     event[pos++] = report_id;
206     event[pos++] = report_type;
207     uint8_t length_to_copy = btstack_min(buffer_size, 250);
208     event[pos++] = length_to_copy;
209     memcpy(&event[pos], buffer, length_to_copy);
210     pos += length_to_copy;
211     // set event length
212     event[1] = pos - 2;
213     (*packet_handler)(HCI_EVENT_PACKET, 0, event, pos);
214 }
215 
216 static void hids_device_can_send_now(void * context){
217     hci_con_handle_t con_handle = (hci_con_handle_t) (uintptr_t) context;
218     // notify client
219     hids_device_t * instance = hids_device_get_instance_for_con_handle(con_handle);
220     if (!instance){
221         log_error("no instance for handle 0x%02x", con_handle);
222         return;
223     }
224 
225     if (!packet_handler) return;
226     uint8_t buffer[5];
227     buffer[0] = HCI_EVENT_HIDS_META;
228     buffer[1] = 3;
229     buffer[2] = HIDS_SUBEVENT_CAN_SEND_NOW;
230     little_endian_store_16(buffer, 3, (uint16_t) con_handle);
231     (*packet_handler)(HCI_EVENT_PACKET, 0, buffer, sizeof(buffer));
232 }
233 
234 // ATT Client Read Callback for Dynamic Data
235 // - if buffer == NULL, don't copy data, just return size of value
236 // - if buffer != NULL, copy data and return number bytes copied
237 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){
238     hids_device_t * instance = hids_device_get_instance_for_con_handle(con_handle);
239     if (!instance){
240         log_error("no instance for handle 0x%02x", con_handle);
241         return HIDS_DEVICE_ERROR_CODE_INAPPROPRIATE_CONNECTION_PARAMETERS;
242     }
243 
244     if (att_handle == instance->hid_protocol_mode_value_handle){
245         log_info("Read protocol mode");
246         return att_read_callback_handle_byte(instance->hid_protocol_mode, offset, buffer, buffer_size);
247     }
248 
249     if (att_handle == instance->hid_report_map_handle){
250         log_info("Read report map");
251         return att_read_callback_handle_blob(instance->hid_descriptor, instance->hid_descriptor_size, offset, buffer, buffer_size);
252     }
253 
254     if (att_handle == instance->hid_boot_mouse_input_client_configuration_handle){
255         return att_read_callback_handle_little_endian_16(instance->hid_boot_mouse_input_client_configuration_value, offset, buffer, buffer_size);
256     }
257 
258     if (att_handle == instance->hid_boot_keyboard_input_client_configuration_handle){
259         return att_read_callback_handle_little_endian_16(instance->hid_boot_keyboard_input_client_configuration_value, offset, buffer, buffer_size);
260     }
261 
262     if (att_handle == instance->hid_control_point_value_handle){
263         if (buffer && (buffer_size >= 1u)){
264             buffer[0] = instance->hid_control_point_suspend;
265         }
266         return 1;
267     }
268 
269     uint8_t boot_report_size = 0;
270     // avoid "value unused" warning by smart compiler
271     // not so smart compiler will complain that value is used without initialization
272     UNUSED(boot_report_size);
273     uint8_t boot_report_id = 0;
274     hid_report_type_t boot_report_type = HID_REPORT_TYPE_RESERVED;
275     if (att_handle == instance->hid_boot_mouse_input_value_handle){
276         boot_report_type = HID_REPORT_TYPE_INPUT;
277         boot_report_size = 3;
278     }
279     if (att_handle == instance->hid_boot_keyboard_input_value_handle){
280         boot_report_type = HID_REPORT_TYPE_INPUT;
281         boot_report_size = 8;
282     }
283     if (att_handle == instance->hid_boot_keyboard_output_value_handle){
284         boot_report_type = HID_REPORT_TYPE_OUTPUT;
285         boot_report_size = 1;
286     }
287     if (boot_report_type != HID_REPORT_TYPE_RESERVED){
288         // no callback, no report
289         if (hids_device_get_report_callback == NULL){
290             return 0;
291         }
292         // answer length request by ATT Server
293         if (buffer == NULL){
294             return boot_report_size;
295         } else {
296             // Report ID 0, Type Input
297             (*hids_device_get_report_callback)(con_handle, boot_report_type, boot_report_id, boot_report_size, buffer);
298             return boot_report_size;
299         }
300     }
301 
302     hids_device_report_t * report;
303     report = hids_device_get_report_for_value_handle(instance, att_handle);
304     if (report != NULL){
305         // no callback, no report
306         if (hids_device_get_report_callback == NULL){
307             return 0;
308         }
309         // answer length request by ATT Server
310         if (buffer == NULL){
311             return report->size;
312         } else {
313             uint16_t max_size = btstack_min(report->size, buffer_size);
314             (*hids_device_get_report_callback)(con_handle, report->type, report->id, max_size, buffer);
315             return report->size;
316         }
317     }
318 
319     report = hids_device_get_report_for_client_configuration_handle(instance, att_handle);
320     if (report != NULL){
321         return att_read_callback_handle_little_endian_16(report->client_configuration_value, offset, buffer, buffer_size);
322     }
323     return 0;
324 }
325 
326 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){
327     UNUSED(buffer_size);
328     UNUSED(offset);
329 
330     if (transaction_mode != ATT_TRANSACTION_MODE_NONE){
331         return 0;
332     }
333 
334     hids_device_t * instance = hids_device_get_instance_for_con_handle(con_handle);
335     if (!instance){
336         log_error("no instance for handle 0x%02x", con_handle);
337         return HIDS_DEVICE_ERROR_CODE_INAPPROPRIATE_CONNECTION_PARAMETERS;
338     }
339 
340     if (att_handle == instance->hid_boot_mouse_input_client_configuration_handle){
341         uint16_t new_value = little_endian_read_16(buffer, 0);
342         instance->hid_boot_mouse_input_client_configuration_value = new_value;
343         hids_device_emit_event_with_uint8(HIDS_SUBEVENT_BOOT_MOUSE_INPUT_REPORT_ENABLE, con_handle, (uint8_t) new_value);
344         return 0;
345     }
346     if (att_handle == instance->hid_boot_keyboard_input_client_configuration_handle){
347         uint16_t new_value = little_endian_read_16(buffer, 0);
348         instance->hid_boot_keyboard_input_client_configuration_value = new_value;
349         hids_device_emit_event_with_uint8(HIDS_SUBEVENT_BOOT_KEYBOARD_INPUT_REPORT_ENABLE, con_handle, (uint8_t) new_value);
350         return 0;
351     }
352 
353     if (att_handle == instance->hid_protocol_mode_value_handle){
354         instance->hid_protocol_mode = buffer[0];
355         log_info("Set protocol mode: %u", instance->hid_protocol_mode);
356         hids_device_emit_event_with_uint8(HIDS_SUBEVENT_PROTOCOL_MODE, con_handle, instance->hid_protocol_mode);
357         return 0;
358     }
359 
360     if (att_handle == instance->hid_control_point_value_handle){
361         if (buffer_size < 1u){
362             return ATT_ERROR_INVALID_OFFSET;
363         }
364         instance->hid_control_point_suspend = buffer[0];
365         instance->con_handle = con_handle;
366         log_info("Set suspend tp: %u", instance->hid_control_point_suspend );
367         if (instance->hid_control_point_suspend == 0u){
368             hids_device_emit_event(HIDS_SUBEVENT_SUSPEND, con_handle);
369         } else if (instance->hid_control_point_suspend == 1u){
370             hids_device_emit_event(HIDS_SUBEVENT_EXIT_SUSPEND, con_handle);
371         }
372         return 0;
373     }
374 
375     if (att_handle == instance->hid_boot_keyboard_output_value_handle){
376         hids_device_emit_report(con_handle, 0, HID_REPORT_TYPE_OUTPUT, buffer, buffer_size);
377         return 0;
378     }
379 
380     hids_device_report_t * report;
381     report = hids_device_get_report_for_value_handle(instance, att_handle);
382     if (report != NULL){
383         hids_device_emit_report(con_handle, report->id, (uint8_t) report->type, buffer, buffer_size);
384         return 0;
385     }
386 
387     report = hids_device_get_report_for_client_configuration_handle(instance, att_handle);
388     if (report != NULL){
389         uint16_t new_value = little_endian_read_16(buffer, 0);
390         report->client_configuration_value = new_value;
391         log_info("Enable Report (type %u) notifications: %x", (uint8_t) report->type, new_value);
392 
393         switch (report->type){
394             case HID_REPORT_TYPE_INPUT:
395                 hids_device_emit_event_with_uint8_uint8_t(HIDS_SUBEVENT_INPUT_REPORT_ENABLE, con_handle, report->id, (uint8_t) new_value);
396                 break;
397             case HID_REPORT_TYPE_OUTPUT:
398                 hids_device_emit_event_with_uint8_uint8_t(HIDS_SUBEVENT_OUTPUT_REPORT_ENABLE, con_handle, report->id, (uint8_t) new_value);
399                 break;
400             case HID_REPORT_TYPE_FEATURE:
401                 hids_device_emit_event_with_uint8_uint8_t(HIDS_SUBEVENT_FEATURE_REPORT_ENABLE, con_handle, report->id, (uint8_t) new_value);
402                 break;
403             default:
404                 btstack_unreachable();
405                 break;
406         }
407     }
408     return 0;
409 }
410 
411 void hids_device_init_with_storage(uint8_t hid_country_code, const uint8_t * hid_descriptor, uint16_t hid_descriptor_size,
412     uint16_t num_reports, hids_device_report_t * report_storage){
413 
414     hids_device_t * instance = hids_device_create_instance();
415 
416     btstack_assert(num_reports > 0);
417     btstack_assert(report_storage != NULL);
418 
419     instance->hid_country_code    = hid_country_code;
420     instance->hid_descriptor      = hid_descriptor;
421     instance->hid_descriptor_size = hid_descriptor_size;
422 
423     // default
424     instance->hid_protocol_mode   = 1;
425 
426     // get service handle range
427     uint16_t start_handle = 0;
428     uint16_t end_handle   = 0xffff;
429     int service_found = gatt_server_get_handle_range_for_service_with_uuid16(ORG_BLUETOOTH_SERVICE_HUMAN_INTERFACE_DEVICE, &start_handle, &end_handle);
430     btstack_assert(service_found != 0);
431     UNUSED(service_found);
432 
433     // get report map handle
434     instance->hid_report_map_handle                               = gatt_server_get_value_handle_for_characteristic_with_uuid16(start_handle, end_handle, ORG_BLUETOOTH_CHARACTERISTIC_REPORT_MAP);
435 
436     // get report map handle
437     instance->hid_protocol_mode_value_handle                      = gatt_server_get_value_handle_for_characteristic_with_uuid16(start_handle, end_handle, ORG_BLUETOOTH_CHARACTERISTIC_PROTOCOL_MODE);
438 
439     // get value and client configuration handles for boot mouse input, boot keyboard input and report input
440     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);
441     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);
442 
443     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);
444     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);
445 
446     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);
447 
448     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);
449 
450     log_info("hid_report_map_handle                               0x%02x", instance->hid_report_map_handle);
451     log_info("hid_protocol_mode_value_handle                      0x%02x", instance->hid_protocol_mode_value_handle);
452     log_info("hid_boot_mouse_input_value_handle                   0x%02x", instance->hid_boot_mouse_input_value_handle);
453     log_info("hid_boot_mouse_input_client_configuration_handle    0x%02x", instance->hid_boot_mouse_input_client_configuration_handle);
454     log_info("hid_boot_keyboard_input_value_handle                0x%02x", instance->hid_boot_keyboard_input_value_handle);
455     log_info("hid_boot_keyboard_output_value_handle               0x%02x", instance->hid_boot_keyboard_output_value_handle);
456     log_info("hid_boot_keyboard_input_client_configuration_handle 0x%02x", instance->hid_boot_keyboard_input_client_configuration_handle);
457     log_info("hid_control_point_value_handle                      0x%02x", instance->hid_control_point_value_handle);
458 
459     instance->hid_reports = report_storage;
460 
461     uint16_t hid_reports_num = num_reports;
462     uint16_t assigned_reports_num = 0;
463     uint16_t start_chr_handle = start_handle;
464 
465     while ( (start_chr_handle < end_handle) && (assigned_reports_num < hid_reports_num)) {
466         // mandatory
467         uint16_t chr_value_handle = gatt_server_get_value_handle_for_characteristic_with_uuid16(start_chr_handle, end_handle, ORG_BLUETOOTH_CHARACTERISTIC_REPORT);
468         if (chr_value_handle == 0){
469             break;
470         }
471 
472         // optional
473         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);
474 
475         // mandatory
476         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);
477         if (report_reference_handle == 0){
478             break;
479         }
480 
481         // get report id and type from report reference
482         uint16_t report_reference_value_len;
483         const uint8_t * report_reference_value = gatt_server_get_const_value_for_handle(report_reference_handle, &report_reference_value_len);
484         if (report_reference_value == NULL){
485             break;
486         }
487         if (report_reference_value_len != 2){
488             break;
489         }
490         uint8_t report_id = report_reference_value[0];
491         hid_report_type_t report_type = (hid_report_type_t) report_reference_value[1];
492 
493         // store report info
494         hids_device_report_t * report = &report_storage[assigned_reports_num];
495         report->value_handle = chr_value_handle;
496         report->client_configuration_handle = chr_client_configuration_handle;
497         report->client_configuration_value = 0;
498         report->id   = report_id;
499         report->type = report_type;
500         report->size = btstack_hid_get_report_size_for_id(report_id, report_type, hid_descriptor_size, hid_descriptor);
501 
502         switch (report->type){
503             case HID_REPORT_TYPE_INPUT:
504                 instance->hid_input_reports_num++;
505                 break;
506             case HID_REPORT_TYPE_OUTPUT:
507                 instance->hid_output_reports_num++;
508                 break;
509             case HID_REPORT_TYPE_FEATURE:
510                 instance->hid_feature_reports_num++;
511                 break;
512             default:
513                 btstack_unreachable();
514                 return;
515         }
516         log_info("hid_report_value_handle                       0x%02x, id %u, type %u", report->value_handle, report->id, (uint8_t)report->type);
517         if (report->client_configuration_handle != 0){
518             log_info("hid_report_client_configuration_handle        0x%02x", report->client_configuration_handle);
519         }
520 
521         assigned_reports_num++;
522         start_chr_handle = report_reference_handle + 1;
523     }
524 
525     // register service with ATT Server
526     hid_service.start_handle   = start_handle;
527     hid_service.end_handle     = end_handle;
528     hid_service.read_callback  = &att_read_callback;
529     hid_service.write_callback = &att_write_callback;
530     att_server_register_service_handler(&hid_service);
531 }
532 
533 /**
534  * @brief Set up HIDS Device
535  */
536 void hids_device_init(uint8_t country_code, const uint8_t * descriptor, uint16_t descriptor_size){
537     uint16_t hid_reports_num = sizeof(hid_reports_generic_storage) / sizeof(hids_device_report_t);
538     hids_device_init_with_storage(country_code, descriptor, descriptor_size, hid_reports_num, hid_reports_generic_storage);
539 }
540 
541 /**
542  * @brief Register callback for the HIDS Device client.
543  * @param callback
544  */
545 void hids_device_register_packet_handler(btstack_packet_handler_t callback){
546     packet_handler = callback;
547 }
548 
549 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)){
550     hids_device_get_report_callback = callback;
551 }
552 
553 /**
554  * @brief Request can send now event to send HID Report
555  * Generates an HIDS_SUBEVENT_CAN_SEND_NOW subevent
556  * @param hid_cid
557  */
558 uint8_t hids_device_request_can_send_now_event(hci_con_handle_t con_handle){
559     hids_device_t * instance = hids_device_get_instance_for_con_handle(con_handle);
560     if (!instance){
561         log_error("no instance for handle 0x%02x", con_handle);
562         return ERROR_CODE_UNKNOWN_CONNECTION_IDENTIFIER;
563     }
564 
565     instance->can_send_now_callback.callback = &hids_device_can_send_now;
566     instance->can_send_now_callback.context  = (void*) (uintptr_t) con_handle;
567     return att_server_register_can_send_now_callback(&instance->can_send_now_callback, con_handle);
568 }
569 
570 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){
571     hids_device_t * instance = hids_device_get_instance_for_con_handle(con_handle);
572     if (!instance){
573         log_error("no instance for handle 0x%02x", con_handle);
574         return ERROR_CODE_UNKNOWN_CONNECTION_IDENTIFIER;
575     }
576 
577     hids_device_report_t * report_storage = hids_device_get_report_for_id_and_type(instance, report_id,
578                                                                                    HID_REPORT_TYPE_INPUT);
579     if (report_storage == NULL){
580         return ERROR_CODE_UNSUPPORTED_FEATURE_OR_PARAMETER_VALUE;
581     }
582 
583     return att_server_notify(con_handle, report_storage->value_handle, report, report_len);
584 }
585 
586 uint8_t hids_device_send_input_report(hci_con_handle_t con_handle, const uint8_t * report, uint16_t report_len){
587     hids_device_t * device = hids_device_get_instance_for_con_handle(con_handle);
588     if (!device){
589         log_error("no instance for handle 0x%02x", con_handle);
590         return ERROR_CODE_UNKNOWN_CONNECTION_IDENTIFIER;
591     }
592 
593     uint8_t pos;
594     uint8_t total_reports =  device->hid_input_reports_num + device->hid_output_reports_num + device->hid_feature_reports_num;
595     for (pos = 0 ; pos < total_reports ; pos++){
596         hids_device_report_t * report_storage = &device->hid_reports[pos];
597         if (report_storage->type == HID_REPORT_TYPE_INPUT){
598             return att_server_notify(con_handle, report_storage->value_handle, report, report_len);
599         }
600     }
601     return ERROR_CODE_UNSUPPORTED_FEATURE_OR_PARAMETER_VALUE;
602 }
603 
604 /**
605  * @brief Send HID Boot Mouse Input Report
606  */
607 uint8_t hids_device_send_boot_mouse_input_report(hci_con_handle_t con_handle, const uint8_t * report, uint16_t report_len){
608     hids_device_t * instance = hids_device_get_instance_for_con_handle(con_handle);
609     if (!instance){
610         log_error("no instance for handle 0x%02x", con_handle);
611         return ERROR_CODE_UNKNOWN_CONNECTION_IDENTIFIER;
612     }
613     return att_server_notify(con_handle, instance->hid_boot_mouse_input_value_handle, report, report_len);
614 }
615 
616 /**
617  * @brief Send HID Boot Mouse Input Report
618  */
619 uint8_t hids_device_send_boot_keyboard_input_report(hci_con_handle_t con_handle, const uint8_t * report, uint16_t report_len){
620     hids_device_t * instance = hids_device_get_instance_for_con_handle(con_handle);
621     if (!instance){
622         log_error("no instance for handle 0x%02x", con_handle);
623         return ERROR_CODE_UNKNOWN_CONNECTION_IDENTIFIER;
624     }
625     return att_server_notify(con_handle, instance->hid_boot_keyboard_input_value_handle, report, report_len);
626 }
627