xref: /btstack/src/ble/gatt-service/hids_device.c (revision c1bc0b8eb5939645f99930167b211d7cbc6b7aff)
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 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 #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 
53 #define HIDS_DEVICE_ERROR_CODE_INAPPROPRIATE_CONNECTION_PARAMETERS    0x80
54 
55 typedef struct{
56     uint16_t        con_handle;
57 
58     uint8_t         hid_country_code;
59     const uint8_t * hid_descriptor;
60     uint16_t        hid_descriptor_size;
61 
62     uint16_t        hid_report_map_handle;
63     uint16_t        hid_protocol_mode;
64     uint16_t        hid_protocol_mode_value_handle;
65 
66     uint16_t        hid_boot_mouse_input_value_handle;
67     uint16_t        hid_boot_mouse_input_client_configuration_handle;
68     uint16_t        hid_boot_mouse_input_client_configuration_value;
69 
70     uint16_t        hid_boot_keyboard_input_value_handle;
71     uint16_t        hid_boot_keyboard_input_client_configuration_handle;
72     uint16_t        hid_boot_keyboard_input_client_configuration_value;
73 
74     uint16_t        hid_report_input_value_handle;
75     uint16_t        hid_report_input_client_configuration_handle;
76     uint16_t        hid_report_input_client_configuration_value;
77 
78     uint16_t        hid_report_output_value_handle;
79     uint16_t        hid_report_output_client_configuration_handle;
80     uint16_t        hid_report_output_client_configuration_value;
81 
82     uint16_t        hid_report_feature_value_handle;
83     uint16_t        hid_report_feature_client_configuration_handle;
84     uint16_t        hid_report_feature_client_configuration_value;
85 
86     uint16_t        hid_control_point_value_handle;
87     // uint16_t        hid_control_point_client_configuration_descriptor_handle;
88     uint8_t         hid_control_point_suspend;
89     // btstack_context_callback_registration_t control_point_callback;
90 
91     btstack_context_callback_registration_t  battery_callback;
92 } hids_device_t;
93 
94 static hids_device_t hids_device;
95 
96 static btstack_packet_handler_t packet_handler;
97 static att_service_handler_t hid_service;
98 
99 // TODO: store hids device connection into list
100 static hids_device_t * hids_device_get_instance_for_con_handle(uint16_t con_handle){
101     UNUSED(con_handle);
102     return &hids_device;
103 }
104 
105 static hids_device_t * hids_device_create_instance(void){
106     return &hids_device;
107 }
108 
109 // static int hids_device_delete_instance(void){
110 //     return 0;
111 // }
112 
113 static void hids_device_emit_event_with_uint8(uint8_t event, hci_con_handle_t con_handle, uint8_t value){
114     hids_device_t * instance = hids_device_get_instance_for_con_handle(con_handle);
115     if (!instance){
116         log_error("no instance for handle 0x%02x", con_handle);
117         return;
118     }
119 
120     if (!packet_handler) return;
121     uint8_t buffer[6];
122     buffer[0] = HCI_EVENT_HIDS_META;
123     buffer[1] = 4;
124     buffer[2] = event;
125     little_endian_store_16(buffer, 3, (uint16_t) con_handle);
126     buffer[5] = value;
127     (*packet_handler)(HCI_EVENT_PACKET, 0, buffer, sizeof(buffer));
128 }
129 
130 static void hids_device_emit_event(uint8_t event, hci_con_handle_t con_handle){
131     hids_device_t * instance = hids_device_get_instance_for_con_handle(con_handle);
132     if (!instance){
133         log_error("no instance for handle 0x%02x", con_handle);
134         return;
135     }
136 
137     if (!packet_handler) return;
138     uint8_t buffer[5];
139     buffer[0] = HCI_EVENT_HIDS_META;
140     buffer[1] = 4;
141     buffer[2] = event;
142     little_endian_store_16(buffer, 3, (uint16_t) con_handle);
143     (*packet_handler)(HCI_EVENT_PACKET, 0, buffer, sizeof(buffer));
144 }
145 
146 static void hids_device_can_send_now(void * context){
147     hci_con_handle_t con_handle = (hci_con_handle_t) (uintptr_t) context;
148     // notify client
149     hids_device_t * instance = hids_device_get_instance_for_con_handle(con_handle);
150     if (!instance){
151         log_error("no instance for handle 0x%02x", con_handle);
152         return;
153     }
154 
155     if (!packet_handler) return;
156     uint8_t buffer[5];
157     buffer[0] = HCI_EVENT_HIDS_META;
158     buffer[1] = 3;
159     buffer[2] = HIDS_SUBEVENT_CAN_SEND_NOW;
160     little_endian_store_16(buffer, 3, (uint16_t) con_handle);
161     (*packet_handler)(HCI_EVENT_PACKET, 0, buffer, sizeof(buffer));
162 }
163 
164 // ATT Client Read Callback for Dynamic Data
165 // - if buffer == NULL, don't copy data, just return size of value
166 // - if buffer != NULL, copy data and return number bytes copied
167 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){
168     hids_device_t * instance = hids_device_get_instance_for_con_handle(con_handle);
169     if (!instance){
170         log_error("no instance for handle 0x%02x", con_handle);
171         return HIDS_DEVICE_ERROR_CODE_INAPPROPRIATE_CONNECTION_PARAMETERS;
172     }
173 
174     printf("att_read_callback att handle 0x%02x\n", att_handle);
175 
176     if (att_handle == instance->hid_protocol_mode_value_handle){
177         log_info("Read protocol mode");
178         return att_read_callback_handle_byte(instance->hid_protocol_mode, offset, buffer, buffer_size);
179     }
180     if (att_handle == instance->hid_report_map_handle){
181         log_info("Read report map");
182         return att_read_callback_handle_blob(instance->hid_descriptor, instance->hid_descriptor_size, offset, buffer, buffer_size);
183     }
184     // if (att_handle == hid_boot_mouse_input_value_handle){
185     // }
186     if (att_handle == instance->hid_boot_mouse_input_client_configuration_handle){
187         return att_read_callback_handle_little_endian_16(instance->hid_boot_mouse_input_client_configuration_value, offset, buffer, buffer_size);
188     }
189     // if (att_handle == hid_boot_keyboard_input_value_handle){
190     // }
191     if (att_handle == instance->hid_boot_keyboard_input_client_configuration_handle){
192         return att_read_callback_handle_little_endian_16(instance->hid_boot_keyboard_input_client_configuration_value, offset, buffer, buffer_size);
193     }
194     // if (att_handle == hid_report_input_value_handle){
195     // }
196     if (att_handle == instance->hid_report_input_client_configuration_handle){
197         return att_read_callback_handle_little_endian_16(instance->hid_report_input_client_configuration_value, offset, buffer, buffer_size);
198     }
199     if (att_handle == instance->hid_report_input_client_configuration_handle){
200         return att_read_callback_handle_little_endian_16(instance->hid_report_output_client_configuration_value, offset, buffer, buffer_size);
201     }
202     if (att_handle == instance->hid_report_input_client_configuration_handle){
203         return att_read_callback_handle_little_endian_16(instance->hid_report_feature_client_configuration_value, offset, buffer, buffer_size);
204     }
205     if (att_handle == instance->hid_control_point_value_handle){
206         if (buffer && buffer_size >= 1){
207             buffer[0] = instance->hid_control_point_suspend;
208         }
209         return 1;
210     }
211     return 0;
212 }
213 
214 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){
215     UNUSED(transaction_mode);
216     UNUSED(buffer_size);
217     UNUSED(offset);
218 
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 HIDS_DEVICE_ERROR_CODE_INAPPROPRIATE_CONNECTION_PARAMETERS;
223     }
224     printf("att_write_callback att handle 0x%02x\n", att_handle);
225 
226     if (att_handle == instance->hid_boot_mouse_input_client_configuration_handle){
227         uint16_t new_value = little_endian_read_16(buffer, 0);
228         // if (new_value == hid_boot_mouse_input_client_configuration_value) return 0;
229         instance->hid_boot_mouse_input_client_configuration_value = new_value;
230         hids_device_emit_event_with_uint8(HIDS_SUBEVENT_BOOT_MOUSE_INPUT_REPORT_ENABLE, con_handle, new_value);
231     }
232     if (att_handle == instance->hid_boot_keyboard_input_client_configuration_handle){
233         uint16_t new_value = little_endian_read_16(buffer, 0);
234         // if (new_value == hid_boot_keyboard_input_client_configuration_value) return 0;
235         instance->hid_boot_keyboard_input_client_configuration_value = new_value;
236         hids_device_emit_event_with_uint8(HIDS_SUBEVENT_BOOT_KEYBOARD_INPUT_REPORT_ENABLE, con_handle, new_value);
237     }
238     if (att_handle == instance->hid_report_input_client_configuration_handle){
239         uint16_t new_value = little_endian_read_16(buffer, 0);
240         // if (new_value == hid_report_input_client_configuration_value) return 0;
241         instance->hid_report_input_client_configuration_value = new_value;
242         log_info("Enable Report Input notifications: %x", new_value);
243         hids_device_emit_event_with_uint8(HIDS_SUBEVENT_INPUT_REPORT_ENABLE, con_handle, new_value);
244     }
245     if (att_handle == instance->hid_report_output_client_configuration_handle){
246         uint16_t new_value = little_endian_read_16(buffer, 0);
247         // if (new_value == hid_report_output_client_configuration_value) return 0;
248         instance->hid_report_output_client_configuration_value = new_value;
249         log_info("Enable Report Output notifications: %x", new_value);
250         hids_device_emit_event_with_uint8(HIDS_SUBEVENT_OUTPUT_REPORT_ENABLE, con_handle, new_value);
251     }
252     if (att_handle == instance->hid_report_feature_client_configuration_handle){
253         uint16_t new_value = little_endian_read_16(buffer, 0);
254         // if (new_value == hid_report_feature_client_configuration_value) return 0;
255         instance->hid_report_feature_client_configuration_value = new_value;
256         log_info("Enable Report Feature notifications: %x", new_value);
257         hids_device_emit_event_with_uint8(HIDS_SUBEVENT_FEATURE_REPORT_ENABLE, con_handle, new_value);
258     }
259 
260     if (att_handle == instance->hid_protocol_mode_value_handle){
261         instance->hid_protocol_mode = buffer[0];
262         log_info("Set protocol mode: %u", instance->hid_protocol_mode);
263         hids_device_emit_event_with_uint8(HIDS_SUBEVENT_PROTOCOL_MODE, con_handle, instance->hid_protocol_mode);
264     }
265 
266     if (att_handle == instance->hid_control_point_value_handle){
267         if (buffer_size < 1){
268             return ATT_ERROR_INVALID_OFFSET;
269         }
270         instance->hid_control_point_suspend = buffer[0];
271         instance->con_handle = con_handle;
272         log_info("Set suspend tp: %u", instance->hid_control_point_suspend );
273         if (instance->hid_control_point_suspend == 0){
274             hids_device_emit_event(HIDS_SUBEVENT_SUSPEND, con_handle);
275         } else if (instance->hid_control_point_suspend == 1){
276             hids_device_emit_event(HIDS_SUBEVENT_EXIT_SUSPEND, con_handle);
277         }
278         // } else {
279         //     return ATT_ERROR_INAPPROPRIATE_CONNECTION_PARAMETERS;
280         // }
281     }
282 
283     return 0;
284 }
285 
286 /**
287  * @brief Set up HIDS Device
288  */
289 void hids_device_init(uint8_t country_code, const uint8_t * descriptor, uint16_t descriptor_size){
290     hids_device_t * instance = hids_device_create_instance();
291     if (!instance){
292         log_error("hids_device_init: instance could not be created, not enough memory");
293         return;
294     }
295 
296     instance->hid_country_code    = country_code;
297     instance->hid_descriptor      = descriptor;
298     instance->hid_descriptor_size = descriptor_size;
299 
300     // default
301     instance->hid_protocol_mode   = 1;
302 
303     // get service handle range
304     uint16_t start_handle = 0;
305     uint16_t end_handle   = 0xfff;
306     int service_found = gatt_server_get_get_handle_range_for_service_with_uuid16(ORG_BLUETOOTH_SERVICE_HUMAN_INTERFACE_DEVICE, &start_handle, &end_handle);
307     if (!service_found) return;
308 
309     // get report map handle
310     instance->hid_report_map_handle                               = gatt_server_get_value_handle_for_characteristic_with_uuid16(start_handle, end_handle, ORG_BLUETOOTH_CHARACTERISTIC_REPORT_MAP);
311 
312     // get report map handle
313     instance->hid_protocol_mode_value_handle                      = gatt_server_get_value_handle_for_characteristic_with_uuid16(start_handle, end_handle, ORG_BLUETOOTH_CHARACTERISTIC_PROTOCOL_MODE);
314 
315     // get value and client configuration handles for boot mouse input, boot keyboard input and report input
316     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);
317     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);
318 
319     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);
320     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);
321 
322     instance->hid_report_input_value_handle                       = gatt_server_get_value_handle_for_characteristic_with_uuid16(start_handle, end_handle, ORG_BLUETOOTH_CHARACTERISTIC_REPORT);
323     instance->hid_report_input_client_configuration_handle        = gatt_server_get_client_configuration_handle_for_characteristic_with_uuid16(start_handle, end_handle, ORG_BLUETOOTH_CHARACTERISTIC_REPORT);
324 
325     instance->hid_report_output_value_handle                      = gatt_server_get_value_handle_for_characteristic_with_uuid16(instance->hid_report_input_client_configuration_handle+1, end_handle, ORG_BLUETOOTH_CHARACTERISTIC_REPORT);
326     instance->hid_report_output_client_configuration_handle       = gatt_server_get_client_configuration_handle_for_characteristic_with_uuid16(instance->hid_report_input_client_configuration_handle, end_handle, ORG_BLUETOOTH_CHARACTERISTIC_REPORT);
327 
328     instance->hid_report_feature_value_handle                     = gatt_server_get_value_handle_for_characteristic_with_uuid16(instance->hid_report_output_client_configuration_handle+1, end_handle, ORG_BLUETOOTH_CHARACTERISTIC_REPORT);
329     instance->hid_report_feature_client_configuration_handle      = gatt_server_get_client_configuration_handle_for_characteristic_with_uuid16(instance->hid_report_output_client_configuration_handle, end_handle, ORG_BLUETOOTH_CHARACTERISTIC_REPORT);
330 
331     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);
332 
333     printf("hid_report_map_handle                               0x%02x\n", instance->hid_report_map_handle);
334     printf("hid_protocol_mode_value_handle                      0x%02x\n", instance->hid_protocol_mode_value_handle);
335     printf("hid_boot_mouse_input_value_handle                   0x%02x\n", instance->hid_boot_mouse_input_value_handle);
336     printf("hid_boot_mouse_input_client_configuration_handle    0x%02x\n", instance->hid_boot_mouse_input_client_configuration_handle);
337     printf("\n");
338     printf("hid_boot_keyboard_input_value_handle                0x%02x\n", instance->hid_boot_keyboard_input_value_handle);
339     printf("hid_boot_keyboard_input_client_configuration_handle 0x%02x\n", instance->hid_boot_keyboard_input_client_configuration_handle);
340     printf("\n");
341     printf("hid_report_input_value_handle                       0x%02x\n", instance->hid_report_input_value_handle);
342     printf("hid_report_input_client_configuration_handle        0x%02x\n", instance->hid_report_input_client_configuration_handle);
343     printf("\n");
344     printf("hid_report_output_value_handle                      0x%02x\n", instance->hid_report_output_value_handle);
345     printf("hid_report_output_client_configuration_handle       0x%02x\n", instance->hid_report_output_client_configuration_handle);
346     printf("\n");
347     printf("hid_report_feature_value_handle                     0x%02x\n", instance->hid_report_feature_value_handle);
348     printf("hid_report_feature_client_configuration_handle      0x%02x\n", instance->hid_report_feature_client_configuration_handle);
349 
350     printf("hid_control_point_value_handle                      0x%02x\n", instance->hid_control_point_value_handle);
351 
352     // register service with ATT Server
353     hid_service.start_handle   = start_handle;
354     hid_service.end_handle     = end_handle;
355     hid_service.read_callback  = &att_read_callback;
356     hid_service.write_callback = &att_write_callback;
357     att_server_register_service_handler(&hid_service);
358 }
359 
360 /**
361  * @brief Register callback for the HIDS Device client.
362  * @param callback
363  */
364 void hids_device_register_packet_handler(btstack_packet_handler_t callback){
365     packet_handler = callback;
366 }
367 
368 /**
369  * @brief Request can send now event to send HID Report
370  * Generates an HIDS_SUBEVENT_CAN_SEND_NOW subevent
371  * @param hid_cid
372  */
373 void hids_device_request_can_send_now_event(hci_con_handle_t con_handle){
374     hids_device_t * instance = hids_device_get_instance_for_con_handle(con_handle);
375     if (!instance){
376         log_error("no instance for handle 0x%02x", con_handle);
377         return;
378     }
379 
380     instance->battery_callback.callback = &hids_device_can_send_now;
381     instance->battery_callback.context  = (void*) (uintptr_t) con_handle;
382     att_server_register_can_send_now_callback(&instance->battery_callback, con_handle);
383 }
384 
385 /**
386  * @brief Send HID Report: Input
387  */
388 void hids_device_send_input_report(hci_con_handle_t con_handle, const uint8_t * report, uint16_t report_len){
389     hids_device_t * instance = hids_device_get_instance_for_con_handle(con_handle);
390     if (!instance){
391         log_error("no instance for handle 0x%02x", con_handle);
392         return;
393     }
394     att_server_notify(con_handle, instance->hid_report_input_value_handle, (uint8_t*) report, report_len);
395 }
396 
397 /**
398  * @brief Send HID Report: Output
399  */
400 void hids_device_send_output_report(hci_con_handle_t con_handle, const uint8_t * report, uint16_t report_len){
401     hids_device_t * instance = hids_device_get_instance_for_con_handle(con_handle);
402     if (!instance){
403         log_error("no instance for handle 0x%02x", con_handle);
404         return;
405     }
406     att_server_notify(con_handle, instance->hid_report_output_value_handle, (uint8_t*) report, report_len);
407 }
408 
409 /**
410  * @brief Send HID Report: Feature
411  */
412 void hids_device_send_feature_report(hci_con_handle_t con_handle, const uint8_t * report, uint16_t report_len){
413     hids_device_t * instance = hids_device_get_instance_for_con_handle(con_handle);
414     if (!instance){
415         log_error("no instance for handle 0x%02x", con_handle);
416         return;
417     }
418     att_server_notify(con_handle, instance->hid_report_feature_value_handle, (uint8_t*) report, report_len);
419 }
420 
421 /**
422  * @brief Send HID Boot Mouse Input Report
423  */
424 void hids_device_send_boot_mouse_input_report(hci_con_handle_t con_handle, const uint8_t * report, uint16_t report_len){
425     hids_device_t * instance = hids_device_get_instance_for_con_handle(con_handle);
426     if (!instance){
427         log_error("no instance for handle 0x%02x", con_handle);
428         return;
429     }
430     att_server_notify(con_handle, instance->hid_boot_mouse_input_value_handle, (uint8_t*) report, report_len);
431 }
432 
433 /**
434  * @brief Send HID Boot Mouse Input Report
435  */
436 void hids_device_send_boot_keyboard_input_report(hci_con_handle_t con_handle, const uint8_t * report, uint16_t report_len){
437     hids_device_t * instance = hids_device_get_instance_for_con_handle(con_handle);
438     if (!instance){
439         log_error("no instance for handle 0x%02x", con_handle);
440         return;
441     }
442     att_server_notify(con_handle, instance->hid_boot_keyboard_input_value_handle, (uint8_t*) report, report_len);
443 }
444