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 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_client.c"
39
40 #include "btstack_config.h"
41
42 #ifdef ENABLE_TESTING_SUPPORT
43 #include <stdio.h>
44 #endif
45
46 #include <stdint.h>
47 #include <string.h>
48
49 #include "ble/gatt-service/hids_client.h"
50
51 #include "btstack_memory.h"
52 #include "ble/core.h"
53 #include "ble/gatt_client.h"
54 #include "bluetooth_gatt.h"
55 #include "btstack_debug.h"
56 #include "btstack_event.h"
57 #include "btstack_run_loop.h"
58 #include "gap.h"
59 #include "ble/gatt_service_client.h"
60
61 #define HID_REPORT_MODE_REPORT_ID 3
62 #define HID_REPORT_MODE_REPORT_MAP_ID 4
63 #define HID_REPORT_MODE_HID_INFORMATION_ID 5
64 #define HID_REPORT_MODE_HID_CONTROL_POINT_ID 6
65
66 static btstack_packet_callback_registration_t hci_event_callback_registration;
67
68 static btstack_linked_list_t clients;
69 static uint16_t hids_cid_counter = 0;
70
71 static uint8_t * hids_client_descriptor_storage;
72 static uint16_t hids_client_descriptor_storage_len;
73
74 static void handle_gatt_client_event(uint8_t packet_type, uint16_t channel, uint8_t *packet, uint16_t size);
75 static void hids_client_handle_can_write_without_reponse(void * context);
76
77 #ifdef ENABLE_TESTING_SUPPORT
hid_characteristic_name(uint16_t uuid)78 static char * hid_characteristic_name(uint16_t uuid){
79 switch (uuid){
80 case ORG_BLUETOOTH_CHARACTERISTIC_PROTOCOL_MODE:
81 return "PROTOCOL_MODE";
82
83 case ORG_BLUETOOTH_CHARACTERISTIC_BOOT_KEYBOARD_INPUT_REPORT:
84 return "BOOT_KEYBOARD_INPUT_REPORT";
85
86 case ORG_BLUETOOTH_CHARACTERISTIC_BOOT_MOUSE_INPUT_REPORT:
87 return "BOOT_MOUSE_INPUT_REPORT";
88
89 case ORG_BLUETOOTH_CHARACTERISTIC_BOOT_KEYBOARD_OUTPUT_REPORT:
90 return "BOOT_KEYBOARD_OUTPUT_REPORT";
91
92 case ORG_BLUETOOTH_CHARACTERISTIC_REPORT:
93 return "REPORT";
94
95 case ORG_BLUETOOTH_CHARACTERISTIC_REPORT_MAP:
96 return "REPORT_MAP";
97
98 case ORG_BLUETOOTH_CHARACTERISTIC_HID_INFORMATION:
99 return "HID_INFORMATION";
100
101 case ORG_BLUETOOTH_CHARACTERISTIC_HID_CONTROL_POINT:
102 return "HID_CONTROL_POINT";
103 default:
104 return "UKNOWN";
105 }
106 }
107 #endif
108
hids_get_client_for_con_handle(hci_con_handle_t con_handle)109 static hids_client_t * hids_get_client_for_con_handle(hci_con_handle_t con_handle){
110 btstack_linked_list_iterator_t it;
111 btstack_linked_list_iterator_init(&it, &clients);
112 while (btstack_linked_list_iterator_has_next(&it)){
113 hids_client_t * client = (hids_client_t *)btstack_linked_list_iterator_next(&it);
114 if (client->con_handle != con_handle) continue;
115 return client;
116 }
117 return NULL;
118 }
119
hids_get_client_for_cid(uint16_t hids_cid)120 static hids_client_t * hids_get_client_for_cid(uint16_t hids_cid){
121 btstack_linked_list_iterator_t it;
122 btstack_linked_list_iterator_init(&it, &clients);
123 while (btstack_linked_list_iterator_has_next(&it)){
124 hids_client_t * client = (hids_client_t *)btstack_linked_list_iterator_next(&it);
125 if (client->cid != hids_cid) continue;
126 return client;
127 }
128 return NULL;
129 }
130
131
132 // START Descriptor Storage Util
133
hids_client_descriptors_len(hids_client_t * client)134 static uint16_t hids_client_descriptors_len(hids_client_t * client){
135 uint16_t descriptors_len = 0;
136 uint8_t service_index;
137 for (service_index = 0; service_index < client->num_instances; service_index++){
138 descriptors_len += client->services[service_index].hid_descriptor_len;
139 }
140 return descriptors_len;
141 }
142
hids_client_descriptor_storage_get_available_space(void)143 static uint16_t hids_client_descriptor_storage_get_available_space(void){
144 // assumes all descriptors are back to back
145 uint16_t free_space = hids_client_descriptor_storage_len;
146 btstack_linked_list_iterator_t it;
147 btstack_linked_list_iterator_init(&it, &clients);
148 while (btstack_linked_list_iterator_has_next(&it)){
149 hids_client_t * client = (hids_client_t *)btstack_linked_list_iterator_next(&it);
150 free_space -= hids_client_descriptors_len(client);
151 }
152 return free_space;
153 }
154
hids_client_descriptor_storage_init(hids_client_t * client,uint8_t service_index)155 static void hids_client_descriptor_storage_init(hids_client_t * client, uint8_t service_index){
156 // reserve remaining space for this connection
157 uint16_t available_space = hids_client_descriptor_storage_get_available_space();
158 client->services[service_index].hid_descriptor_len = 0;
159 client->services[service_index].hid_descriptor_max_len = available_space;
160 client->services[service_index].hid_descriptor_offset = hids_client_descriptor_storage_len - available_space;
161 }
162
hids_client_descriptor_storage_store(hids_client_t * client,uint8_t service_index,uint8_t byte)163 static bool hids_client_descriptor_storage_store(hids_client_t * client, uint8_t service_index, uint8_t byte){
164 // store single hid descriptor byte
165 if (client->services[service_index].hid_descriptor_len >= client->services[service_index].hid_descriptor_max_len) return false;
166
167 hids_client_descriptor_storage[client->services[service_index].hid_descriptor_offset + client->services[service_index].hid_descriptor_len] = byte;
168 client->services[service_index].hid_descriptor_len++;
169 return true;
170 }
171
hids_client_descriptor_storage_delete(hids_client_t * client)172 static void hids_client_descriptor_storage_delete(hids_client_t * client){
173 uint8_t service_index;
174
175 // calculate descriptors len
176 uint16_t descriptors_len = hids_client_descriptors_len(client);
177
178 if (descriptors_len > 0){
179 // move higher descriptors down
180 uint16_t next_offset = client->services[0].hid_descriptor_offset + descriptors_len;
181 memmove(&hids_client_descriptor_storage[client->services[0].hid_descriptor_offset],
182 &hids_client_descriptor_storage[next_offset],
183 hids_client_descriptor_storage_len - next_offset);
184
185 // fix descriptor offset of higher descriptors
186 btstack_linked_list_iterator_t it;
187 btstack_linked_list_iterator_init(&it, &clients);
188 while (btstack_linked_list_iterator_has_next(&it)){
189 hids_client_t * conn = (hids_client_t *)btstack_linked_list_iterator_next(&it);
190 if (conn == client) continue;
191 for (service_index = 0; service_index < client->num_instances; service_index++){
192 if (conn->services[service_index].hid_descriptor_offset >= next_offset){
193 conn->services[service_index].hid_descriptor_offset -= descriptors_len;
194 }
195 }
196 }
197 }
198
199 // clear descriptors
200 for (service_index = 0; service_index < client->num_instances; service_index++){
201 client->services[service_index].hid_descriptor_len = 0;
202 client->services[service_index].hid_descriptor_offset = 0;
203 }
204 }
205
hids_client_descriptor_storage_get_descriptor_data(uint16_t hids_cid,uint8_t service_index)206 const uint8_t * hids_client_descriptor_storage_get_descriptor_data(uint16_t hids_cid, uint8_t service_index){
207 hids_client_t * client = hids_get_client_for_cid(hids_cid);
208 if (client == NULL){
209 return NULL;
210 }
211 if (service_index >= client->num_instances){
212 return NULL;
213 }
214 return &hids_client_descriptor_storage[client->services[service_index].hid_descriptor_offset];
215 }
216
hids_client_descriptor_storage_get_descriptor_len(uint16_t hids_cid,uint8_t service_index)217 uint16_t hids_client_descriptor_storage_get_descriptor_len(uint16_t hids_cid, uint8_t service_index){
218 hids_client_t * client = hids_get_client_for_cid(hids_cid);
219 if (client == NULL){
220 return 0;
221 }
222 if (service_index >= client->num_instances){
223 return 0;
224 }
225 return client->services[service_index].hid_descriptor_len;
226 }
227
228 // END Descriptor Storage Util
229
hids_get_next_cid(void)230 static uint16_t hids_get_next_cid(void){
231 if (hids_cid_counter == 0xffff) {
232 hids_cid_counter = 1;
233 } else {
234 hids_cid_counter++;
235 }
236 return hids_cid_counter;
237 }
238
find_report_index_for_value_handle(hids_client_t * client,uint16_t value_handle)239 static uint8_t find_report_index_for_value_handle(hids_client_t * client, uint16_t value_handle){
240 uint8_t i;
241 for (i = 0; i < client->num_reports; i++){
242 if (client->reports[i].value_handle == value_handle){
243 return i;
244 }
245 }
246 return HIDS_CLIENT_INVALID_REPORT_INDEX;
247 }
248
find_external_report_index_for_value_handle(hids_client_t * client,uint16_t value_handle)249 static uint8_t find_external_report_index_for_value_handle(hids_client_t * client, uint16_t value_handle){
250 uint8_t i;
251 for (i = 0; i < client->num_external_reports; i++){
252 if (client->external_reports[i].value_handle == value_handle){
253 return i;
254 }
255 }
256 return HIDS_CLIENT_INVALID_REPORT_INDEX;
257 }
258
external_report_index_for_uuid_exists(hids_client_t * client,uint16_t uuid16)259 static bool external_report_index_for_uuid_exists(hids_client_t * client, uint16_t uuid16){
260 uint8_t i;
261 for (i = 0; i < client->num_external_reports; i++){
262 if (client->external_reports[i].external_report_reference_uuid == uuid16){
263 return true;
264 }
265 }
266 return false;
267 }
268
find_report_index_for_report_id_and_report_type(hids_client_t * client,uint8_t report_id,hid_report_type_t report_type)269 static uint8_t find_report_index_for_report_id_and_report_type(hids_client_t * client, uint8_t report_id, hid_report_type_t report_type){
270 uint8_t i;
271
272 for (i = 0; i < client->num_reports; i++){
273 hids_client_report_t report = client->reports[i];
274 hid_protocol_mode_t protocol_mode = client->services[report.service_index].protocol_mode;
275
276 if (protocol_mode == HID_PROTOCOL_MODE_BOOT){
277 if (!client->reports[i].boot_report){
278 continue;
279 }
280 } else if (protocol_mode == HID_PROTOCOL_MODE_REPORT){
281 if (client->reports[i].boot_report){
282 continue;
283 }
284 }
285
286 if (report.report_id == report_id && report.report_type == report_type){
287 return i;
288 }
289 }
290 return HIDS_CLIENT_INVALID_REPORT_INDEX;
291 }
292
hids_client_add_characteristic(hids_client_t * client,gatt_client_characteristic_t * characteristic,uint8_t report_id,hid_report_type_t report_type,bool boot_report)293 static uint8_t hids_client_add_characteristic(hids_client_t * client, gatt_client_characteristic_t * characteristic, uint8_t report_id, hid_report_type_t report_type, bool boot_report){
294
295 uint8_t report_index = find_external_report_index_for_value_handle(client, characteristic->value_handle);
296 if (report_index != HIDS_CLIENT_INVALID_REPORT_INDEX){
297 return report_index;
298 }
299 report_index = client->num_reports;
300
301 if (report_index < HIDS_CLIENT_NUM_REPORTS) {
302 client->reports[report_index].value_handle = characteristic->value_handle;
303 client->reports[report_index].end_handle = characteristic->end_handle;
304 client->reports[report_index].properties = characteristic->properties;
305
306 client->reports[report_index].service_index = client->service_index;
307 client->reports[report_index].report_id = report_id;
308 client->reports[report_index].report_type = report_type;
309 client->reports[report_index].boot_report = boot_report;
310
311 log_info("add index %d, id %d, type %d, value handle 0x%02x, properties 0x%02x", report_index, report_id, report_type, characteristic->value_handle, characteristic->properties);
312 client->num_reports++;
313 return report_index;
314 } else {
315 log_info("not enough storage, increase HIDS_CLIENT_NUM_REPORTS");
316 return HIDS_CLIENT_INVALID_REPORT_INDEX;
317 }
318 }
319
hids_client_add_external_report(hids_client_t * client,gatt_client_characteristic_descriptor_t * characteristic_descriptor)320 static uint8_t hids_client_add_external_report(hids_client_t * client, gatt_client_characteristic_descriptor_t * characteristic_descriptor){
321 uint8_t report_index = client->num_external_reports;
322
323 if (report_index < HIDS_CLIENT_NUM_REPORTS) {
324 client->external_reports[report_index].value_handle = characteristic_descriptor->handle;
325 client->external_reports[report_index].service_index = client->service_index;
326
327 client->num_external_reports++;
328 log_info("add external index %d [%d], value handle 0x%02x", report_index, client->num_external_reports, characteristic_descriptor->handle);
329 return report_index;
330 } else {
331 log_info("not enough storage, increase HIDS_CLIENT_NUM_REPORTS");
332 return HIDS_CLIENT_INVALID_REPORT_INDEX;
333 }
334 }
335
hids_client_get_next_active_report_map_index(hids_client_t * client)336 static uint8_t hids_client_get_next_active_report_map_index(hids_client_t * client){
337 uint8_t i;
338 for (i = client->service_index; i < client->num_instances; i++){
339 if (client->services[i].report_map_value_handle != 0){
340 return i;
341 }
342 }
343 client->service_index = HIDS_CLIENT_INVALID_REPORT_INDEX;
344 return HIDS_CLIENT_INVALID_REPORT_INDEX;
345 }
346
hids_client_report_query_next_report_map(hids_client_t * client)347 static bool hids_client_report_query_next_report_map(hids_client_t * client){
348 client->service_index++;
349 if (hids_client_get_next_active_report_map_index(client) != HIDS_CLIENT_INVALID_REPORT_INDEX){
350 client->state = HIDS_CLIENT_STATE_W2_READ_REPORT_MAP_HID_DESCRIPTOR;
351 return true;
352 }
353 return false;
354 }
355
hids_client_report_map_query_init(hids_client_t * client)356 static bool hids_client_report_map_query_init(hids_client_t * client){
357 client->service_index = 0;
358
359 if (hids_client_get_next_active_report_map_index(client) != HIDS_CLIENT_INVALID_REPORT_INDEX){
360 client->state = HIDS_CLIENT_STATE_W2_READ_REPORT_MAP_HID_DESCRIPTOR;
361 return true;
362 }
363 return false;
364 }
365
hids_client_report_query_next_report_map_uuid(hids_client_t * client)366 static bool hids_client_report_query_next_report_map_uuid(hids_client_t * client){
367 client->report_index++;
368 if (client->report_index < client->num_external_reports){
369 client->state = HIDS_CLIENT_STATE_W2_REPORT_MAP_READ_EXTERNAL_REPORT_REFERENCE_UUID;
370 return true;
371 }
372 return false;
373 }
374
hids_client_report_map_uuid_query_init(hids_client_t * client)375 static bool hids_client_report_map_uuid_query_init(hids_client_t * client){
376 client->report_index = 0;
377 if (client->num_external_reports > 0){
378 client->state = HIDS_CLIENT_STATE_W2_REPORT_MAP_READ_EXTERNAL_REPORT_REFERENCE_UUID;
379 return true;
380 }
381 return false;
382 }
383
hids_client_get_next_report_index(hids_client_t * client)384 static uint8_t hids_client_get_next_report_index(hids_client_t * client){
385 uint8_t i;
386 uint8_t index = HIDS_CLIENT_INVALID_REPORT_INDEX;
387
388 for (i = client->report_index; i < client->num_reports && (index == HIDS_CLIENT_INVALID_REPORT_INDEX); i++){
389 hids_client_report_t report = client->reports[i];
390 if (!report.boot_report){
391 if (report.report_type == HID_REPORT_TYPE_RESERVED && report.report_id == HID_REPORT_MODE_REPORT_ID){
392 index = i;
393 client->service_index = report.service_index;
394 break;
395 }
396 }
397 }
398 client->report_index = index;
399 return index;
400 }
401
hids_client_report_query_next_report(hids_client_t * client)402 static bool hids_client_report_query_next_report(hids_client_t * client){
403 client->report_index++;
404 if (hids_client_get_next_report_index(client) != HIDS_CLIENT_INVALID_REPORT_INDEX){
405 client->state = HIDS_CLIENT_STATE_W2_FIND_REPORT;
406 return true;
407 }
408 return false;
409 }
410
hids_client_report_query_init(hids_client_t * client)411 static bool hids_client_report_query_init(hids_client_t * client){
412 client->report_index = 0;
413
414 if (hids_client_get_next_report_index(client) != HIDS_CLIENT_INVALID_REPORT_INDEX){
415 client->state = HIDS_CLIENT_STATE_W2_FIND_REPORT;
416 return true;
417 }
418 return false;
419 }
420
hids_client_get_next_notification_report_index(hids_client_t * client)421 static uint8_t hids_client_get_next_notification_report_index(hids_client_t * client){
422 uint8_t i;
423 uint8_t index = HIDS_CLIENT_INVALID_REPORT_INDEX;
424
425 for (i = client->report_index; i < client->num_reports && (index == HIDS_CLIENT_INVALID_REPORT_INDEX); i++){
426 hids_client_report_t report = client->reports[i];
427 hid_protocol_mode_t protocol_mode = client->services[report.service_index].protocol_mode;
428
429 if (protocol_mode == HID_PROTOCOL_MODE_BOOT){
430 if (!client->reports[i].boot_report){
431 continue;
432 }
433 } else if (protocol_mode == HID_PROTOCOL_MODE_REPORT){
434 if (client->reports[i].boot_report){
435 continue;
436 }
437 }
438 if (report.report_type == HID_REPORT_TYPE_INPUT){
439 index = i;
440 }
441 }
442 client->report_index = index;
443 return index;
444 }
445
hids_client_report_next_notification_report_index(hids_client_t * client)446 static bool hids_client_report_next_notification_report_index(hids_client_t * client){
447 client->report_index++;
448 if (hids_client_get_next_notification_report_index(client) != HIDS_CLIENT_INVALID_REPORT_INDEX){
449 client->state = HIDS_CLIENT_STATE_W2_ENABLE_INPUT_REPORTS;
450 return true;
451 }
452 return false;
453 }
454
hids_client_report_notifications_init(hids_client_t * client)455 static bool hids_client_report_notifications_init(hids_client_t * client){
456 #ifdef ENABLE_TESTING_SUPPORT
457 printf("\nRegister for Notifications: \n");
458 #endif
459 client->report_index = 0;
460
461 if (hids_client_get_next_notification_report_index(client) != HIDS_CLIENT_INVALID_REPORT_INDEX){
462 client->state = HIDS_CLIENT_STATE_W2_ENABLE_INPUT_REPORTS;
463 return true;
464 }
465 return false;
466 }
467
hids_client_report_next_notifications_configuration_report_index(hids_client_t * client)468 static bool hids_client_report_next_notifications_configuration_report_index(hids_client_t * client){
469 client->report_index++;
470 if (hids_client_get_next_notification_report_index(client) != HIDS_CLIENT_INVALID_REPORT_INDEX){
471 client->state = HIDS_CLIENT_STATE_W2_CONFIGURE_NOTIFICATIONS;
472 return true;
473 }
474 return false;
475 }
476
hids_client_notifications_configuration_init(hids_client_t * client)477 static bool hids_client_notifications_configuration_init(hids_client_t * client){
478 #ifdef ENABLE_TESTING_SUPPORT
479 printf("\nConfigure for Notifications: \n");
480 #endif
481 client->report_index = 0;
482
483 if (hids_client_get_next_notification_report_index(client) != HIDS_CLIENT_INVALID_REPORT_INDEX){
484 client->state = HIDS_CLIENT_STATE_W2_CONFIGURE_NOTIFICATIONS;
485 return true;
486 }
487 return false;
488 }
489
hids_create_client(hci_con_handle_t con_handle,uint16_t cid)490 static hids_client_t * hids_create_client(hci_con_handle_t con_handle, uint16_t cid){
491 hids_client_t * client = btstack_memory_hids_client_get();
492 if (!client){
493 log_error("Not enough memory to create client");
494 return NULL;
495 }
496 client->state = HIDS_CLIENT_STATE_IDLE;
497 client->cid = cid;
498 client->con_handle = con_handle;
499
500 btstack_linked_list_add(&clients, (btstack_linked_item_t *) client);
501 return client;
502 }
503
hids_finalize_client(hids_client_t * client)504 static void hids_finalize_client(hids_client_t * client){
505 // stop listening
506 uint8_t i;
507 for (i = 0; i < client->num_reports; i++){
508 gatt_client_stop_listening_for_characteristic_value_updates(&client->reports[i].notification_listener);
509 }
510
511 hids_client_descriptor_storage_delete(client);
512 btstack_linked_list_remove(&clients, (btstack_linked_item_t *) client);
513 btstack_memory_hids_client_free(client);
514 }
515
516
hids_emit_connection_established(hids_client_t * client,uint8_t status)517 static void hids_emit_connection_established(hids_client_t * client, uint8_t status){
518 uint8_t event[8];
519 int pos = 0;
520 event[pos++] = HCI_EVENT_GATTSERVICE_META;
521 event[pos++] = sizeof(event) - 2;
522 event[pos++] = GATTSERVICE_SUBEVENT_HID_SERVICE_CONNECTED;
523 little_endian_store_16(event, pos, client->cid);
524 pos += 2;
525 event[pos++] = status;
526 event[pos++] = client->services[0].protocol_mode;
527 event[pos++] = client->num_instances;
528 (*client->client_handler)(HCI_EVENT_PACKET, 0, event, sizeof(event));
529 }
530
hids_emit_disconnected(btstack_packet_handler_t packet_handler,uint16_t cid)531 static void hids_emit_disconnected(btstack_packet_handler_t packet_handler, uint16_t cid){
532 uint8_t event[5];
533 int pos = 0;
534 event[pos++] = HCI_EVENT_GATTSERVICE_META;
535 event[pos++] = sizeof(event) - 2;
536 event[pos++] = GATTSERVICE_SUBEVENT_HID_SERVICE_DISCONNECTED;
537 little_endian_store_16(event, pos, cid);
538 pos += 2;
539 btstack_assert(pos == sizeof(event));
540 (*packet_handler)(HCI_EVENT_PACKET, 0, event, sizeof(event));
541 }
542
hids_emit_notifications_configuration(hids_client_t * client)543 static void hids_emit_notifications_configuration(hids_client_t * client){
544 uint8_t event[6];
545 int pos = 0;
546 event[pos++] = HCI_EVENT_GATTSERVICE_META;
547 event[pos++] = sizeof(event) - 2;
548 event[pos++] = GATTSERVICE_SUBEVENT_HID_SERVICE_REPORTS_NOTIFICATION;
549 little_endian_store_16(event, pos, client->cid);
550 pos += 2;
551 event[pos++] = client->value;
552 (*client->client_handler)(HCI_EVENT_PACKET, 0, event, sizeof(event));
553 }
554
hids_client_setup_report_event(uint8_t subevent,hids_client_t * client,uint8_t report_index,uint8_t * buffer,uint16_t report_len)555 static uint16_t hids_client_setup_report_event(uint8_t subevent, hids_client_t *client, uint8_t report_index, uint8_t *buffer,
556 uint16_t report_len) {
557 uint16_t pos = 0;
558 buffer[pos++] = HCI_EVENT_GATTSERVICE_META;
559 pos++; // skip len
560 buffer[pos++] = subevent;
561 little_endian_store_16(buffer, pos, client->cid);
562 pos += 2;
563 buffer[pos++] = client->reports[report_index].service_index;
564 buffer[pos++] = client->reports[report_index].report_id;
565 little_endian_store_16(buffer, pos, report_len);
566 pos += 2;
567 buffer[1] = pos + report_len - 2;
568 return pos;
569 }
570
hids_client_setup_report_event_with_report_id(hids_client_t * client,uint8_t report_index,uint8_t * buffer,uint16_t report_len)571 static void hids_client_setup_report_event_with_report_id(hids_client_t * client, uint8_t report_index, uint8_t *buffer, uint16_t report_len){
572 uint16_t pos = hids_client_setup_report_event(GATTSERVICE_SUBEVENT_HID_REPORT, client, report_index, buffer,
573 report_len + 1);
574 buffer[pos] = client->reports[report_index].report_id;
575 }
576
hids_client_emit_hid_information_event(hids_client_t * client,const uint8_t * value,uint16_t value_len)577 static void hids_client_emit_hid_information_event(hids_client_t * client, const uint8_t *value, uint16_t value_len){
578 if (value_len != 4) return;
579
580 uint8_t event[11];
581 int pos = 0;
582 event[pos++] = HCI_EVENT_GATTSERVICE_META;
583 event[pos++] = sizeof(event) - 2;
584 event[pos++] = GATTSERVICE_SUBEVENT_HID_INFORMATION;
585 little_endian_store_16(event, pos, client->cid);
586 pos += 2;
587 event[pos++] = client->service_index;
588
589 memcpy(event+pos, value, 3);
590 pos += 3;
591 event[pos++] = (value[3] & 0x02) >> 1;
592 event[pos++] = value[3] & 0x01;
593
594 (*client->client_handler)(HCI_EVENT_PACKET, 0, event, sizeof(event));
595 }
596
hids_client_emit_protocol_mode_event(hids_client_t * client,const uint8_t * value,uint16_t value_len)597 static void hids_client_emit_protocol_mode_event(hids_client_t * client, const uint8_t *value, uint16_t value_len){
598 if (value_len != 1) return;
599
600 uint8_t event[11];
601 int pos = 0;
602 event[pos++] = HCI_EVENT_GATTSERVICE_META;
603 event[pos++] = sizeof(event) - 2;
604 event[pos++] = GATTSERVICE_SUBEVENT_HID_PROTOCOL_MODE;
605 little_endian_store_16(event, pos, client->cid);
606 pos += 2;
607 event[pos++] = client->service_index;
608 event[pos++] = value[0];
609 (*client->client_handler)(HCI_EVENT_PACKET, 0, event, sizeof(event));
610 }
611
handle_notification_event(uint8_t packet_type,uint16_t channel,uint8_t * packet,uint16_t size)612 static void handle_notification_event(uint8_t packet_type, uint16_t channel, uint8_t *packet, uint16_t size) {
613 UNUSED(packet_type);
614 UNUSED(channel);
615
616 if (hci_event_packet_get_type(packet) != GATT_EVENT_NOTIFICATION) return;
617
618 hids_client_t * client = hids_get_client_for_con_handle(gatt_event_notification_get_handle(packet));
619 if (client == NULL) return;
620
621 uint8_t report_index = find_report_index_for_value_handle(client, gatt_event_notification_get_value_handle(packet));
622 if (report_index == HIDS_CLIENT_INVALID_REPORT_INDEX){
623 return;
624 }
625
626 // GATT_EVENT_NOTIFICATION has HID report data at offset 12
627 // - see gatt_event_notification_get_value()
628 //
629 // GATTSERVICE_SUBEVENT_HID_REPORT has HID report data at offset 10
630 // - see gattservice_subevent_hid_report_get_report() and add 1 for the inserted Report ID
631 //
632 // => use existing packet from offset 2 = 12 - 10 to setup event
633 const int16_t offset = 2;
634
635 uint8_t * in_place_event = &packet[offset];
636 hids_client_setup_report_event_with_report_id(client, report_index, in_place_event,
637 gatt_event_notification_get_value_length(packet));
638 (*client->client_handler)(HCI_EVENT_GATTSERVICE_META, client->cid, in_place_event, size - offset);
639 }
640
handle_report_event(uint8_t packet_type,uint16_t channel,uint8_t * packet,uint16_t size)641 static void handle_report_event(uint8_t packet_type, uint16_t channel, uint8_t *packet, uint16_t size) {
642 UNUSED(packet_type);
643 UNUSED(channel);
644
645 if (hci_event_packet_get_type(packet) != GATT_EVENT_CHARACTERISTIC_VALUE_QUERY_RESULT) return;
646
647 hids_client_t * client = hids_get_client_for_con_handle(gatt_event_characteristic_value_query_result_get_handle(packet));
648 if (client == NULL) return;
649
650 if (client->state != HIDS_CLIENT_W4_GET_REPORT_RESULT){
651 return;
652 }
653 client->state = HIDS_CLIENT_STATE_CONNECTED;
654
655 uint8_t report_index = find_report_index_for_value_handle(client, gatt_event_characteristic_value_query_result_get_value_handle(packet));
656 if (report_index == HIDS_CLIENT_INVALID_REPORT_INDEX){
657 return;
658 }
659
660 // GATT_EVENT_CHARACTERISTIC_VALUE_QUERY_RESULT has HID report data at offset 12
661 // - see gatt_event_characteristic_value_query_result_get_value()
662 //
663 // GATTSERVICE_SUBEVENT_HID_REPORT has HID report data at offset 10
664 // - see gattservice_subevent_hid_report_get_report() and add 1 for the inserted Report ID
665 //
666 // => use existing packet from offset 2 = 12 - 10 to setup event
667 const int16_t offset = 2;
668
669 uint8_t * in_place_event = &packet[offset];
670 hids_client_setup_report_event_with_report_id(client, report_index, in_place_event,
671 gatt_event_characteristic_value_query_result_get_value_length(packet));
672 (*client->client_handler)(HCI_EVENT_GATTSERVICE_META, client->cid, in_place_event, size - offset);
673 }
674
hids_run_for_client(hids_client_t * client)675 static void hids_run_for_client(hids_client_t * client){
676 uint8_t att_status;
677 gatt_client_service_t service;
678 gatt_client_characteristic_t characteristic;
679
680 switch (client->state){
681 case HIDS_CLIENT_STATE_W2_QUERY_SERVICE:
682 #ifdef ENABLE_TESTING_SUPPORT
683 printf("\n\nQuery Services:\n");
684 #endif
685 client->state = HIDS_CLIENT_STATE_W4_SERVICE_RESULT;
686
687 // result in GATT_EVENT_SERVICE_QUERY_RESULT
688 att_status = gatt_client_discover_primary_services_by_uuid16(handle_gatt_client_event, client->con_handle, ORG_BLUETOOTH_SERVICE_HUMAN_INTERFACE_DEVICE);
689 UNUSED(att_status);
690 break;
691
692 case HIDS_CLIENT_STATE_W2_QUERY_CHARACTERISTIC:
693 #ifdef ENABLE_TESTING_SUPPORT
694 printf("\n\nQuery Characteristics of service %d:\n", client->service_index);
695 #endif
696 client->state = HIDS_CLIENT_STATE_W4_CHARACTERISTIC_RESULT;
697
698 service.start_group_handle = client->services[client->service_index].start_handle;
699 service.end_group_handle = client->services[client->service_index].end_handle;
700
701 // result in GATT_EVENT_CHARACTERISTIC_QUERY_RESULT
702 att_status = gatt_client_discover_characteristics_for_service(&handle_gatt_client_event, client->con_handle, &service);
703
704 UNUSED(att_status);
705 break;
706
707 case HIDS_CLIENT_STATE_W2_READ_REPORT_MAP_HID_DESCRIPTOR:
708 #ifdef ENABLE_TESTING_SUPPORT
709 printf("\n\nRead REPORT_MAP (Handle 0x%04X) HID Descriptor of service %d:\n", client->services[client->service_index].report_map_value_handle, client->service_index);
710 #endif
711 client->state = HIDS_CLIENT_STATE_W4_REPORT_MAP_HID_DESCRIPTOR;
712
713 // result in GATT_EVENT_LONG_CHARACTERISTIC_VALUE_QUERY_RESULT
714 att_status = gatt_client_read_long_value_of_characteristic_using_value_handle(&handle_gatt_client_event, client->con_handle, client->services[client->service_index].report_map_value_handle);
715 UNUSED(att_status);
716 break;
717
718 case HIDS_CLIENT_STATE_W2_REPORT_MAP_DISCOVER_CHARACTERISTIC_DESCRIPTORS:
719 #ifdef ENABLE_TESTING_SUPPORT
720 printf("\nDiscover REPORT_MAP (Handle 0x%04X) Characteristic Descriptors of service %d:\n", client->services[client->service_index].report_map_value_handle, client->service_index);
721 #endif
722 client->state = HIDS_CLIENT_STATE_W4_REPORT_MAP_CHARACTERISTIC_DESCRIPTORS_RESULT;
723
724 characteristic.value_handle = client->services[client->service_index].report_map_value_handle;
725 characteristic.end_handle = client->services[client->service_index].report_map_end_handle;
726
727 // result in GATT_EVENT_ALL_CHARACTERISTIC_DESCRIPTORS_QUERY_RESULT
728 att_status = gatt_client_discover_characteristic_descriptors(&handle_gatt_client_event, client->con_handle, &characteristic);
729 UNUSED(att_status);
730 break;
731
732 case HIDS_CLIENT_STATE_W2_REPORT_MAP_READ_EXTERNAL_REPORT_REFERENCE_UUID:
733 #ifdef ENABLE_TESTING_SUPPORT
734 printf("\nRead external chr UUID (Handle 0x%04X) Characteristic Descriptors, service index %d:\n", client->external_reports[client->report_index].value_handle, client->service_index);
735 #endif
736 client->state = HIDS_CLIENT_STATE_W4_REPORT_MAP_EXTERNAL_REPORT_REFERENCE_UUID;
737
738 // result in GATT_EVENT_CHARACTERISTIC_DESCRIPTOR_QUERY_RESULT
739 att_status = gatt_client_read_characteristic_descriptor_using_descriptor_handle(&handle_gatt_client_event, client->con_handle, client->external_reports[client->report_index].value_handle);
740 UNUSED(att_status);
741 break;
742
743 case HIDS_CLIENT_STATE_W2_DISCOVER_EXTERNAL_REPORT_CHARACTERISTIC:
744 #ifdef ENABLE_TESTING_SUPPORT
745 printf("\nDiscover External Report Characteristic:\n");
746 #endif
747 client->state = HIDS_CLIENT_STATE_W4_EXTERNAL_REPORT_CHARACTERISTIC_RESULT;
748
749 service.start_group_handle = 0x0001;
750 service.end_group_handle = 0xffff;
751
752 // Result in GATT_EVENT_CHARACTERISTIC_QUERY_RESULT
753 att_status = gatt_client_discover_characteristics_for_service(&handle_gatt_client_event, client->con_handle, &service);
754 UNUSED(att_status);
755 break;
756
757 case HIDS_CLIENT_STATE_W2_FIND_REPORT:
758 #ifdef ENABLE_TESTING_SUPPORT
759 printf("\nQuery Report Characteristic Descriptors [%d, %d, 0x%04X]:\n",
760 client->report_index,
761 client->reports[client->report_index].service_index,
762 client->reports[client->report_index].value_handle);
763 #endif
764 client->state = HIDS_CLIENT_STATE_W4_REPORT_FOUND;
765 client->handle = 0;
766
767 characteristic.value_handle = client->reports[client->report_index].value_handle;
768 characteristic.end_handle = client->reports[client->report_index].end_handle;
769 characteristic.properties = client->reports[client->report_index].properties;
770
771 // result in GATT_EVENT_ALL_CHARACTERISTIC_DESCRIPTORS_QUERY_RESULT
772 att_status = gatt_client_discover_characteristic_descriptors(&handle_gatt_client_event, client->con_handle, &characteristic);
773 UNUSED(att_status);
774 break;
775
776 case HIDS_CLIENT_STATE_W2_READ_REPORT_ID_AND_TYPE:
777 client->state = HIDS_CLIENT_STATE_W4_REPORT_ID_AND_TYPE;
778
779 // result in GATT_EVENT_CHARACTERISTIC_DESCRIPTOR_QUERY_RESULT
780 att_status = gatt_client_read_characteristic_descriptor_using_descriptor_handle(&handle_gatt_client_event, client->con_handle, client->handle);
781 client->handle = 0;
782 UNUSED(att_status);
783 break;
784
785 case HIDS_CLIENT_STATE_W2_CONFIGURE_NOTIFICATIONS:
786 #ifdef ENABLE_TESTING_SUPPORT
787 if (client->value > 0){
788 printf(" Notification configuration enable ");
789 } else {
790 printf(" Notification configuration disable ");
791 }
792 printf("[%d, %d, 0x%04X]:\n",
793 client->report_index,
794 client->reports[client->report_index].service_index, client->reports[client->report_index].value_handle);
795 #endif
796
797 client->state = HIDS_CLIENT_STATE_W4_NOTIFICATIONS_CONFIGURED;
798
799 characteristic.value_handle = client->reports[client->report_index].value_handle;
800 characteristic.end_handle = client->reports[client->report_index].end_handle;
801 characteristic.properties = client->reports[client->report_index].properties;
802
803 // end of write marked in GATT_EVENT_QUERY_COMPLETE
804
805 att_status = gatt_client_write_client_characteristic_configuration(&handle_gatt_client_event, client->con_handle, &characteristic, client->value);
806
807 if (att_status == ERROR_CODE_SUCCESS){
808 switch(client->value){
809 case GATT_CLIENT_CHARACTERISTICS_CONFIGURATION_NOTIFICATION:
810 gatt_client_listen_for_characteristic_value_updates(
811 &client->reports[client->report_index].notification_listener,
812 &handle_notification_event, client->con_handle, &characteristic);
813 break;
814 default:
815 gatt_client_stop_listening_for_characteristic_value_updates(&client->reports[client->report_index].notification_listener);
816 break;
817 }
818 } else {
819 if (hids_client_report_next_notifications_configuration_report_index(client)){
820 hids_run_for_client(client);
821 break;
822 }
823 client->state = HIDS_CLIENT_STATE_CONNECTED;
824 }
825 break;
826
827 case HIDS_CLIENT_STATE_W2_ENABLE_INPUT_REPORTS:
828 #ifdef ENABLE_TESTING_SUPPORT
829 printf(" Notification enable [%d, %d, 0x%04X]:\n",
830 client->report_index,
831 client->reports[client->report_index].service_index, client->reports[client->report_index].value_handle);
832 #endif
833 client->state = HIDS_CLIENT_STATE_W4_INPUT_REPORTS_ENABLED;
834
835 characteristic.value_handle = client->reports[client->report_index].value_handle;
836 characteristic.end_handle = client->reports[client->report_index].end_handle;
837 characteristic.properties = client->reports[client->report_index].properties;
838
839 // end of write marked in GATT_EVENT_QUERY_COMPLETE
840 att_status = gatt_client_write_client_characteristic_configuration(&handle_gatt_client_event, client->con_handle, &characteristic, GATT_CLIENT_CHARACTERISTICS_CONFIGURATION_NOTIFICATION);
841
842 if (att_status == ERROR_CODE_SUCCESS){
843 gatt_client_listen_for_characteristic_value_updates(
844 &client->reports[client->report_index].notification_listener,
845 &handle_notification_event, client->con_handle, &characteristic);
846 } else {
847 if (hids_client_report_next_notification_report_index(client)){
848 hids_run_for_client(client);
849 break;
850 }
851 client->state = HIDS_CLIENT_STATE_CONNECTED;
852 hids_emit_connection_established(client, ERROR_CODE_SUCCESS);
853 }
854 break;
855
856
857 case HIDS_CLIENT_W2_SEND_WRITE_REPORT:
858 #ifdef ENABLE_TESTING_SUPPORT
859 printf(" Write report [%d, %d, 0x%04X]:\n",
860 client->report_index,
861 client->reports[client->report_index].service_index, client->reports[client->report_index].value_handle);
862 #endif
863
864 client->state = HIDS_CLIENT_W4_WRITE_REPORT_DONE;
865
866 // see GATT_EVENT_QUERY_COMPLETE for end of write
867 att_status = gatt_client_write_value_of_characteristic(
868 &handle_gatt_client_event, client->con_handle,
869 client->reports[client->report_index].value_handle,
870 client->report_len, (uint8_t *)client->report);
871 UNUSED(att_status);
872 break;
873
874 case HIDS_CLIENT_W2_SEND_GET_REPORT:
875 #ifdef ENABLE_TESTING_SUPPORT
876 printf(" Get report [index %d, ID %d, Service %d, handle 0x%04X]:\n",
877 client->report_index,
878 client->reports[client->report_index].report_id,
879 client->reports[client->report_index].service_index, client->reports[client->report_index].value_handle);
880 #endif
881
882 client->state = HIDS_CLIENT_W4_GET_REPORT_RESULT;
883 // result in GATT_EVENT_CHARACTERISTIC_VALUE_QUERY_RESULT
884 att_status = gatt_client_read_value_of_characteristic_using_value_handle(
885 &handle_report_event,
886 client->con_handle,
887 client->reports[client->report_index].value_handle);
888 UNUSED(att_status);
889 break;
890
891 #ifdef ENABLE_TESTING_SUPPORT
892 case HIDS_CLIENT_W2_READ_CHARACTERISTIC_CONFIGURATION:
893 client->state = HIDS_CLIENT_W4_CHARACTERISTIC_CONFIGURATION_RESULT;
894
895 // result in GATT_EVENT_CHARACTERISTIC_VALUE_QUERY_RESULT
896 att_status = gatt_client_read_value_of_characteristic_using_value_handle(
897 &handle_gatt_client_event,
898 client->con_handle,
899 client->reports[client->report_index].ccc_handle);
900
901 break;
902 #endif
903 case HIDS_CLIENT_W2_READ_VALUE_OF_CHARACTERISTIC:
904 client->state = HIDS_CLIENT_W4_VALUE_OF_CHARACTERISTIC_RESULT;
905
906 // result in GATT_EVENT_CHARACTERISTIC_VALUE_QUERY_RESULT
907 att_status = gatt_client_read_value_of_characteristic_using_value_handle(
908 &handle_gatt_client_event,
909 client->con_handle,
910 client->handle);
911 break;
912
913 case HIDS_CLIENT_STATE_W2_SET_PROTOCOL_MODE_WITHOUT_RESPONSE:
914 case HIDS_CLIENT_W2_WRITE_VALUE_OF_CHARACTERISTIC_WITHOUT_RESPONSE:
915 client->write_without_response_request.callback = &hids_client_handle_can_write_without_reponse;
916 client->write_without_response_request.context = client;
917 (void) gatt_client_request_to_write_without_response(&client->write_without_response_request, client->con_handle);
918 break;
919
920 default:
921 break;
922 }
923 }
924
hids_client_handle_can_write_without_reponse(void * context)925 static void hids_client_handle_can_write_without_reponse(void * context) {
926 hids_client_t *client = (hids_client_t *) context;
927 uint8_t att_status;
928 switch (client->state){
929 case HIDS_CLIENT_STATE_W2_SET_PROTOCOL_MODE_WITHOUT_RESPONSE:
930 att_status = gatt_client_write_value_of_characteristic_without_response(
931 client->con_handle,
932 client->services[client->service_index].protocol_mode_value_handle, 1, (uint8_t *)&client->required_protocol_mode);
933
934 #ifdef ENABLE_TESTING_SUPPORT
935 printf("\n\nSet report mode %d of service %d, status 0x%02x", client->required_protocol_mode, client->service_index, att_status);
936 #endif
937
938 if (att_status == ATT_ERROR_SUCCESS){
939 client->services[client->service_index].protocol_mode = client->required_protocol_mode;
940 if ((client->service_index + 1) < client->num_instances){
941 client->service_index++;
942 hids_run_for_client(client);
943 break;
944 }
945 }
946
947 // read UUIDS for external characteristics
948 if (hids_client_report_map_uuid_query_init(client)){
949 hids_run_for_client(client);
950 break;
951 }
952
953 // discover characteristic descriptor for all Report characteristics,
954 // then read value of characteristic descriptor to get Report ID
955 if (hids_client_report_query_init(client)){
956 hids_run_for_client(client);
957 break;
958 }
959
960 client->state = HIDS_CLIENT_STATE_CONNECTED;
961 hids_emit_connection_established(client, ERROR_CODE_SUCCESS);
962 break;
963
964 case HIDS_CLIENT_W2_WRITE_VALUE_OF_CHARACTERISTIC_WITHOUT_RESPONSE:
965 #ifdef ENABLE_TESTING_SUPPORT
966 printf(" Write characteristic [service %d, handle 0x%04X]:\n", client->service_index, client->handle);
967 #endif
968 client->state = HIDS_CLIENT_STATE_CONNECTED;
969 (void) gatt_client_write_value_of_characteristic_without_response(client->con_handle, client->handle, 1, (uint8_t *) &client->value);
970 break;
971
972 default:
973 break;
974 }
975 }
976
handle_gatt_client_event(uint8_t packet_type,uint16_t channel,uint8_t * packet,uint16_t size)977 static void handle_gatt_client_event(uint8_t packet_type, uint16_t channel, uint8_t *packet, uint16_t size){
978 UNUSED(packet_type);
979 UNUSED(channel);
980 UNUSED(size);
981
982 hids_client_t * client = NULL;
983 uint8_t status;
984 gatt_client_service_t service;
985 gatt_client_characteristic_t characteristic;
986 gatt_client_characteristic_descriptor_t characteristic_descriptor;
987
988 // hids_client_report_t * boot_keyboard_report;
989 // hids_client_report_t * boot_mouse_report;
990 const uint8_t * characteristic_descriptor_value;
991 uint8_t i;
992 uint8_t report_index;
993
994 const uint8_t * value;
995 uint16_t value_len;
996
997 switch(hci_event_packet_get_type(packet)){
998 case GATT_EVENT_SERVICE_QUERY_RESULT:
999 client = hids_get_client_for_con_handle(gatt_event_service_query_result_get_handle(packet));
1000 if (client == NULL) break;
1001
1002 if (client->num_instances < MAX_NUM_HID_SERVICES){
1003 uint8_t index = client->num_instances;
1004 gatt_event_service_query_result_get_service(packet, &service);
1005 client->services[index].start_handle = service.start_group_handle;
1006 client->services[index].end_handle = service.end_group_handle;
1007 client->num_instances++;
1008
1009 #ifdef ENABLE_TESTING_SUPPORT
1010 printf("HID Service: start handle 0x%04X, end handle 0x%04X\n", client->services[index].start_handle, client->services[index].end_handle);
1011 #endif
1012 hids_client_descriptor_storage_init(client, index);
1013 } else {
1014 log_info("%d hid services found, only first %d can be stored, increase MAX_NUM_HID_SERVICES", client->num_instances + 1, MAX_NUM_HID_SERVICES);
1015 }
1016 break;
1017
1018 case GATT_EVENT_CHARACTERISTIC_QUERY_RESULT:
1019 client = hids_get_client_for_con_handle(gatt_event_characteristic_query_result_get_handle(packet));
1020 if (client == NULL) break;
1021
1022 gatt_event_characteristic_query_result_get_characteristic(packet, &characteristic);
1023
1024 report_index = HIDS_CLIENT_INVALID_REPORT_INDEX;
1025 if (client->state == HIDS_CLIENT_STATE_W4_EXTERNAL_REPORT_CHARACTERISTIC_RESULT){
1026 if (!external_report_index_for_uuid_exists(client, characteristic.uuid16)){
1027 break;
1028 }
1029 }
1030
1031 switch (characteristic.uuid16){
1032 case ORG_BLUETOOTH_CHARACTERISTIC_PROTOCOL_MODE:
1033 client->services[client->service_index].protocol_mode_value_handle = characteristic.value_handle;
1034 break;
1035
1036 case ORG_BLUETOOTH_CHARACTERISTIC_BOOT_KEYBOARD_INPUT_REPORT:
1037 report_index = hids_client_add_characteristic(client, &characteristic, HID_BOOT_MODE_KEYBOARD_ID, HID_REPORT_TYPE_INPUT, true);
1038 break;
1039
1040 case ORG_BLUETOOTH_CHARACTERISTIC_BOOT_MOUSE_INPUT_REPORT:
1041 report_index = hids_client_add_characteristic(client, &characteristic, HID_BOOT_MODE_MOUSE_ID, HID_REPORT_TYPE_INPUT, true);
1042 break;
1043
1044 case ORG_BLUETOOTH_CHARACTERISTIC_BOOT_KEYBOARD_OUTPUT_REPORT:
1045 report_index = hids_client_add_characteristic(client, &characteristic, HID_BOOT_MODE_KEYBOARD_ID, HID_REPORT_TYPE_OUTPUT, true);
1046 break;
1047
1048 case ORG_BLUETOOTH_CHARACTERISTIC_REPORT:
1049 report_index = hids_client_add_characteristic(client, &characteristic, HID_REPORT_MODE_REPORT_ID, HID_REPORT_TYPE_RESERVED, false);
1050 break;
1051
1052 case ORG_BLUETOOTH_CHARACTERISTIC_REPORT_MAP:
1053 client->services[client->service_index].report_map_value_handle = characteristic.value_handle;
1054 client->services[client->service_index].report_map_end_handle = characteristic.end_handle;
1055 break;
1056
1057 case ORG_BLUETOOTH_CHARACTERISTIC_HID_INFORMATION:
1058 client->services[client->service_index].hid_information_value_handle = characteristic.value_handle;
1059 break;
1060
1061 case ORG_BLUETOOTH_CHARACTERISTIC_HID_CONTROL_POINT:
1062 client->services[client->service_index].control_point_value_handle = characteristic.value_handle;
1063 break;
1064
1065 default:
1066 #ifdef ENABLE_TESTING_SUPPORT
1067 printf(" TODO: Found external characteristic 0x%04X\n", characteristic.uuid16);
1068 #endif
1069 return;
1070 }
1071
1072 #ifdef ENABLE_TESTING_SUPPORT
1073 printf("HID Characteristic %s: \n Attribute Handle 0x%04X, Properties 0x%02X, Handle 0x%04X, UUID 0x%04X, service %d",
1074 hid_characteristic_name(characteristic.uuid16),
1075 characteristic.start_handle,
1076 characteristic.properties,
1077 characteristic.value_handle, characteristic.uuid16,
1078 client->service_index);
1079
1080 if (report_index != HIDS_CLIENT_INVALID_REPORT_INDEX){
1081 printf(", report index 0x%02X", report_index);
1082 }
1083 printf("\n");
1084 #endif
1085 break;
1086
1087 case GATT_EVENT_LONG_CHARACTERISTIC_VALUE_QUERY_RESULT:
1088 // Map Report characteristic value == HID Descriptor
1089 client = hids_get_client_for_con_handle(gatt_event_long_characteristic_value_query_result_get_handle(packet));
1090 if (client == NULL) break;
1091
1092 value = gatt_event_long_characteristic_value_query_result_get_value(packet);
1093 value_len = gatt_event_long_characteristic_value_query_result_get_value_length(packet);
1094
1095 #ifdef ENABLE_TESTING_SUPPORT
1096 // printf("Report Map HID Desc [%d] for service %d\n", descriptor_len, client->service_index);
1097 printf_hexdump(value, value_len);
1098 #endif
1099 for (i = 0; i < value_len; i++){
1100 bool stored = hids_client_descriptor_storage_store(client, client->service_index, value[i]);
1101 if (!stored){
1102 client->services[client->service_index].hid_descriptor_status = ERROR_CODE_MEMORY_CAPACITY_EXCEEDED;
1103 break;
1104 }
1105 }
1106 break;
1107
1108 case GATT_EVENT_ALL_CHARACTERISTIC_DESCRIPTORS_QUERY_RESULT:
1109 client = hids_get_client_for_con_handle(gatt_event_all_characteristic_descriptors_query_result_get_handle(packet));
1110 if (client == NULL) break;
1111
1112 gatt_event_all_characteristic_descriptors_query_result_get_characteristic_descriptor(packet, &characteristic_descriptor);
1113
1114 switch (client->state) {
1115 case HIDS_CLIENT_STATE_W4_REPORT_MAP_CHARACTERISTIC_DESCRIPTORS_RESULT:
1116 // setup for descriptor value query
1117 if (characteristic_descriptor.uuid16 == ORG_BLUETOOTH_DESCRIPTOR_EXTERNAL_REPORT_REFERENCE){
1118 report_index = hids_client_add_external_report(client, &characteristic_descriptor);
1119
1120 #ifdef ENABLE_TESTING_SUPPORT
1121 if (report_index != HIDS_CLIENT_INVALID_REPORT_INDEX){
1122 printf(" External Report Reference Characteristic Descriptor: Handle 0x%04X, UUID 0x%04X, service %d, report index 0x%02X\n",
1123 characteristic_descriptor.handle,
1124 characteristic_descriptor.uuid16,
1125 client->service_index, report_index);
1126 }
1127 #endif
1128 }
1129 break;
1130 case HIDS_CLIENT_STATE_W4_REPORT_FOUND:
1131 // setup for descriptor value query
1132 if (characteristic_descriptor.uuid16 == ORG_BLUETOOTH_DESCRIPTOR_REPORT_REFERENCE){
1133 client->handle = characteristic_descriptor.handle;
1134 #ifdef ENABLE_TESTING_SUPPORT
1135 printf(" Report Characteristic Report Reference Characteristic Descriptor: Handle 0x%04X, UUID 0x%04X\n",
1136 characteristic_descriptor.handle,
1137 characteristic_descriptor.uuid16);
1138 #endif
1139 }
1140
1141 #ifdef ENABLE_TESTING_SUPPORT
1142 if (characteristic_descriptor.uuid16 == ORG_BLUETOOTH_DESCRIPTOR_GATT_CLIENT_CHARACTERISTIC_CONFIGURATION){
1143 client->reports[client->report_index].ccc_handle = characteristic_descriptor.handle;
1144 printf(" Report Client Characteristic Configuration Descriptor: Handle 0x%04X, UUID 0x%04X\n",
1145 characteristic_descriptor.handle,
1146 characteristic_descriptor.uuid16);
1147 }
1148 #endif
1149 break;
1150
1151 default:
1152 break;
1153 }
1154 break;
1155
1156 case GATT_EVENT_CHARACTERISTIC_VALUE_QUERY_RESULT:
1157 client = hids_get_client_for_con_handle(gatt_event_characteristic_value_query_result_get_handle(packet));
1158 if (client == NULL) break;
1159
1160 value = gatt_event_characteristic_value_query_result_get_value(packet);
1161 value_len = gatt_event_characteristic_value_query_result_get_value_length(packet);
1162
1163
1164 switch (client->state){
1165 #ifdef ENABLE_TESTING_SUPPORT
1166 case HIDS_CLIENT_W4_CHARACTERISTIC_CONFIGURATION_RESULT:
1167 printf(" Received CCC value: ");
1168 printf_hexdump(value, value_len);
1169 break;
1170 #endif
1171 case HIDS_CLIENT_W4_VALUE_OF_CHARACTERISTIC_RESULT:{
1172 uint16_t value_handle = gatt_event_characteristic_value_query_result_get_value_handle(packet);
1173 if (value_handle == client->services[client->service_index].hid_information_value_handle){
1174 hids_client_emit_hid_information_event(client, value, value_len);
1175 break;
1176 }
1177 if (value_handle == client->services[client->service_index].protocol_mode_value_handle){
1178 hids_client_emit_protocol_mode_event(client, value, value_len);
1179 break;
1180 }
1181 break;
1182 }
1183 default:
1184 break;
1185 }
1186
1187 break;
1188
1189 case GATT_EVENT_CHARACTERISTIC_DESCRIPTOR_QUERY_RESULT:
1190 client = hids_get_client_for_con_handle(gatt_event_characteristic_descriptor_query_result_get_handle(packet));
1191 if (client == NULL) break;
1192
1193 if (gatt_event_characteristic_descriptor_query_result_get_descriptor_length(packet) != 2){
1194 break;
1195 }
1196
1197 characteristic_descriptor_value = gatt_event_characteristic_descriptor_query_result_get_descriptor(packet);
1198 switch (client->state) {
1199 case HIDS_CLIENT_STATE_W4_REPORT_MAP_EXTERNAL_REPORT_REFERENCE_UUID:
1200 // get external report characteristic uuid
1201 report_index = find_external_report_index_for_value_handle(client, gatt_event_characteristic_descriptor_query_result_get_descriptor_handle(packet));
1202 if (report_index != HIDS_CLIENT_INVALID_REPORT_INDEX){
1203 client->external_reports[report_index].external_report_reference_uuid = little_endian_read_16(characteristic_descriptor_value, 0);
1204 #ifdef ENABLE_TESTING_SUPPORT
1205 printf(" Update external_report_reference_uuid of report index 0x%02X, service index 0x%02X, UUID 0x%02X\n",
1206 report_index, client->service_index, client->external_reports[report_index].external_report_reference_uuid);
1207 #endif
1208 }
1209 break;
1210
1211 case HIDS_CLIENT_STATE_W4_REPORT_ID_AND_TYPE:
1212
1213 if (client->report_index != HIDS_CLIENT_INVALID_REPORT_INDEX){
1214 client->reports[client->report_index].report_id = characteristic_descriptor_value[0];
1215 client->reports[client->report_index].report_type = (hid_report_type_t)characteristic_descriptor_value[1];
1216 #ifdef ENABLE_TESTING_SUPPORT
1217 printf(" Update report ID and type [%d, %d] of report index 0x%02X, service index 0x%02X\n",
1218 client->reports[client->report_index].report_id,
1219 client->reports[client->report_index].report_type,
1220 client->report_index, client->service_index);
1221 #endif
1222 }
1223 break;
1224
1225 default:
1226 break;
1227 }
1228 break;
1229
1230 case GATT_EVENT_QUERY_COMPLETE:
1231 client = hids_get_client_for_con_handle(gatt_event_query_complete_get_handle(packet));
1232 if (client == NULL) break;
1233
1234 status = gatt_client_att_status_to_error_code(gatt_event_query_complete_get_att_status(packet));
1235
1236 switch (client->state){
1237 case HIDS_CLIENT_STATE_W4_SERVICE_RESULT:
1238 if (status != ERROR_CODE_SUCCESS){
1239 hids_emit_connection_established(client, status);
1240 hids_finalize_client(client);
1241 return;
1242 }
1243
1244 if (client->num_instances == 0){
1245 hids_emit_connection_established(client, ERROR_CODE_UNSUPPORTED_FEATURE_OR_PARAMETER_VALUE);
1246 hids_finalize_client(client);
1247 return;
1248 }
1249
1250 client->service_index = 0;
1251 client->state = HIDS_CLIENT_STATE_W2_QUERY_CHARACTERISTIC;
1252 break;
1253
1254 case HIDS_CLIENT_STATE_W4_CHARACTERISTIC_RESULT:
1255 if (status != ERROR_CODE_SUCCESS){
1256 hids_emit_connection_established(client, status);
1257 hids_finalize_client(client);
1258 return;
1259 }
1260
1261 if ((client->service_index + 1) < client->num_instances){
1262 // discover characteristics of next service
1263 client->service_index++;
1264 client->state = HIDS_CLIENT_STATE_W2_QUERY_CHARACTERISTIC;
1265 break;
1266 }
1267
1268 btstack_assert(client->required_protocol_mode != HID_PROTOCOL_MODE_REPORT_WITH_FALLBACK_TO_BOOT);
1269
1270 switch (client->required_protocol_mode){
1271 case HID_PROTOCOL_MODE_REPORT:
1272 for (i = 0; i < client->num_instances; i++){
1273 client->services[i].protocol_mode = HID_PROTOCOL_MODE_REPORT;
1274 }
1275 // 1. we need to get HID Descriptor and
1276 // 2. get external Report characteristics if referenced from Report Map
1277 if (hids_client_report_map_query_init(client)){
1278 break;
1279 }
1280 hids_emit_connection_established(client, ERROR_CODE_UNSUPPORTED_FEATURE_OR_PARAMETER_VALUE);
1281 hids_finalize_client(client);
1282 return;
1283
1284 default:
1285 // set boot mode
1286 client->service_index = 0;
1287 client->state = HIDS_CLIENT_STATE_W2_SET_PROTOCOL_MODE_WITHOUT_RESPONSE;
1288 break;
1289 }
1290 break;
1291
1292
1293 // HID descriptor found
1294 case HIDS_CLIENT_STATE_W4_REPORT_MAP_HID_DESCRIPTOR:
1295 if (status != ERROR_CODE_SUCCESS){
1296 hids_emit_connection_established(client, status);
1297 hids_finalize_client(client);
1298 return;
1299 }
1300 client->state = HIDS_CLIENT_STATE_W2_REPORT_MAP_DISCOVER_CHARACTERISTIC_DESCRIPTORS;
1301 break;
1302
1303 // found all descriptors, check if there is one with EXTERNAL_REPORT_REFERENCE
1304 case HIDS_CLIENT_STATE_W4_REPORT_MAP_CHARACTERISTIC_DESCRIPTORS_RESULT:
1305 // go for next report map
1306 if (hids_client_report_query_next_report_map(client)){
1307 break;
1308 }
1309
1310 // read UUIDS for external characteristics
1311 if (hids_client_report_map_uuid_query_init(client)){
1312 break;
1313 }
1314
1315 // discover characteristic descriptor for all Report characteristics,
1316 // then read value of characteristic descriptor to get Report ID
1317 if (hids_client_report_query_init(client)){
1318 break;
1319 }
1320
1321 hids_emit_connection_established(client, ERROR_CODE_UNSUPPORTED_FEATURE_OR_PARAMETER_VALUE);
1322 hids_finalize_client(client);
1323 return;
1324
1325 case HIDS_CLIENT_STATE_W4_REPORT_MAP_EXTERNAL_REPORT_REFERENCE_UUID:
1326 // go for next map report
1327 if (hids_client_report_query_next_report_map_uuid(client)){
1328 break;
1329 }
1330
1331 // update external characteristics with correct value handle and end handle
1332 client->state = HIDS_CLIENT_STATE_W2_DISCOVER_EXTERNAL_REPORT_CHARACTERISTIC;
1333 break;
1334
1335 case HIDS_CLIENT_STATE_W4_EXTERNAL_REPORT_CHARACTERISTIC_RESULT:
1336 // discover characteristic descriptor for all Report characteristics,
1337 // then read value of characteristic descriptor to get Report ID
1338 if (hids_client_report_query_init(client)){
1339 break;
1340 }
1341
1342 hids_emit_connection_established(client, ERROR_CODE_UNSUPPORTED_FEATURE_OR_PARAMETER_VALUE);
1343 hids_finalize_client(client);
1344 return;
1345
1346 case HIDS_CLIENT_STATE_W4_REPORT_FOUND:
1347 if (client->handle != 0){
1348 client->state = HIDS_CLIENT_STATE_W2_READ_REPORT_ID_AND_TYPE;
1349 break;
1350 }
1351 // go for next report
1352 if (hids_client_report_query_next_report(client)){
1353 break;
1354 }
1355 client->state = HIDS_CLIENT_STATE_CONNECTED;
1356 hids_emit_connection_established(client, ERROR_CODE_SUCCESS);
1357 break;
1358
1359 case HIDS_CLIENT_STATE_W4_REPORT_ID_AND_TYPE:
1360 // go for next report
1361 if (hids_client_report_query_next_report(client)){
1362 break;
1363 }
1364 if (hids_client_report_notifications_init(client)){
1365 break;
1366 }
1367 client->state = HIDS_CLIENT_STATE_CONNECTED;
1368 hids_emit_connection_established(client, ERROR_CODE_SUCCESS);
1369 break;
1370
1371 case HIDS_CLIENT_STATE_W4_INPUT_REPORTS_ENABLED:
1372 if (hids_client_report_next_notification_report_index(client)){
1373 break;
1374 }
1375 client->state = HIDS_CLIENT_STATE_CONNECTED;
1376 hids_emit_connection_established(client, ERROR_CODE_SUCCESS);
1377 break;
1378
1379 case HIDS_CLIENT_STATE_W4_NOTIFICATIONS_CONFIGURED:
1380 if (hids_client_report_next_notifications_configuration_report_index(client)){
1381 break;
1382 }
1383 hids_emit_notifications_configuration(client);
1384 client->state = HIDS_CLIENT_STATE_CONNECTED;
1385 break;
1386
1387 #ifdef ENABLE_TESTING_SUPPORT
1388 case HIDS_CLIENT_W4_CHARACTERISTIC_CONFIGURATION_RESULT:
1389 client->state = HIDS_CLIENT_W2_SEND_GET_REPORT;
1390 break;
1391 #endif
1392
1393 case HIDS_CLIENT_W4_VALUE_OF_CHARACTERISTIC_RESULT:
1394 client->state = HIDS_CLIENT_STATE_CONNECTED;
1395 break;
1396
1397 case HIDS_CLIENT_W4_WRITE_REPORT_DONE:
1398 {
1399 client->state = HIDS_CLIENT_STATE_CONNECTED;
1400
1401 // emit empty report to signal done
1402 uint8_t event[9];
1403 hids_client_setup_report_event(GATTSERVICE_SUBEVENT_HID_REPORT_WRITTEN, client,
1404 client->report_index, event, 0);
1405 (*client->client_handler)(HCI_EVENT_PACKET, 0, event, sizeof(event));
1406 }
1407 break;
1408
1409 default:
1410 break;
1411 }
1412 break;
1413
1414 default:
1415 break;
1416 }
1417
1418 if (client != NULL){
1419 hids_run_for_client(client);
1420 }
1421 }
1422
handle_hci_event(uint8_t packet_type,uint16_t channel,uint8_t * packet,uint16_t size)1423 static void handle_hci_event(uint8_t packet_type, uint16_t channel, uint8_t *packet, uint16_t size){
1424 UNUSED(packet_type); // ok: only hci events
1425 UNUSED(channel); // ok: there is no channel
1426 UNUSED(size); // ok: fixed format events read from HCI buffer
1427
1428 hci_con_handle_t con_handle;
1429 hids_client_t * client;
1430
1431 switch (hci_event_packet_get_type(packet)) {
1432 case HCI_EVENT_DISCONNECTION_COMPLETE:
1433 con_handle = hci_event_disconnection_complete_get_connection_handle(packet);
1434 client = hids_get_client_for_con_handle(con_handle);
1435 if (client != NULL){
1436 // emit disconnected
1437 btstack_packet_handler_t packet_handler = client->client_handler;
1438 uint16_t cid = client->cid;
1439 hids_emit_disconnected(packet_handler, cid);
1440 // finalize
1441 hids_finalize_client(client);
1442 }
1443 break;
1444 default:
1445 break;
1446 }
1447 }
1448
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)1449 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){
1450 btstack_assert(packet_handler != NULL);
1451
1452 hids_client_t * client = hids_get_client_for_con_handle(con_handle);
1453 if (client != NULL){
1454 return ERROR_CODE_COMMAND_DISALLOWED;
1455 }
1456
1457 uint16_t cid = hids_get_next_cid();
1458 if (hids_cid != NULL) {
1459 *hids_cid = cid;
1460 }
1461
1462 client = hids_create_client(con_handle, cid);
1463 if (client == NULL) {
1464 return BTSTACK_MEMORY_ALLOC_FAILED;
1465 }
1466
1467 client->required_protocol_mode = protocol_mode;
1468 client->client_handler = packet_handler;
1469 client->state = HIDS_CLIENT_STATE_W2_QUERY_SERVICE;
1470
1471 hids_run_for_client(client);
1472 return ERROR_CODE_SUCCESS;
1473 }
1474
hids_client_disconnect(uint16_t hids_cid)1475 uint8_t hids_client_disconnect(uint16_t hids_cid){
1476 hids_client_t * client = hids_get_client_for_cid(hids_cid);
1477 if (client == NULL){
1478 return ERROR_CODE_UNKNOWN_CONNECTION_IDENTIFIER;
1479 }
1480 // finalize connection
1481 hids_finalize_client(client);
1482 return ERROR_CODE_SUCCESS;
1483 }
1484
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)1485 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){
1486 hids_client_t * client = hids_get_client_for_cid(hids_cid);
1487 if (client == NULL){
1488 return ERROR_CODE_UNKNOWN_CONNECTION_IDENTIFIER;
1489 }
1490
1491 if (client->state != HIDS_CLIENT_STATE_CONNECTED) {
1492 return ERROR_CODE_COMMAND_DISALLOWED;
1493 }
1494
1495 uint8_t report_index = find_report_index_for_report_id_and_report_type(client, report_id, report_type);
1496
1497 if (report_index == HIDS_CLIENT_INVALID_REPORT_INDEX){
1498 return ERROR_CODE_UNSUPPORTED_FEATURE_OR_PARAMETER_VALUE;
1499 }
1500
1501 uint16_t mtu;
1502 uint8_t status = gatt_client_get_mtu(client->con_handle, &mtu);
1503
1504 if (status != ERROR_CODE_SUCCESS){
1505 return status;
1506 }
1507
1508 if (mtu - 2 < report_len){
1509 return ERROR_CODE_PARAMETER_OUT_OF_MANDATORY_RANGE;
1510 }
1511
1512 client->state = HIDS_CLIENT_W2_SEND_WRITE_REPORT;
1513 client->report_index = report_index;
1514 client->report = report;
1515 client->report_len = report_len;
1516
1517 hids_run_for_client(client);
1518 return ERROR_CODE_SUCCESS;
1519 }
1520
hids_client_send_get_report(uint16_t hids_cid,uint8_t report_id,hid_report_type_t report_type)1521 uint8_t hids_client_send_get_report(uint16_t hids_cid, uint8_t report_id, hid_report_type_t report_type){
1522 hids_client_t * client = hids_get_client_for_cid(hids_cid);
1523 if (client == NULL){
1524 return ERROR_CODE_UNKNOWN_CONNECTION_IDENTIFIER;
1525 }
1526
1527 if (client->state != HIDS_CLIENT_STATE_CONNECTED) {
1528 return ERROR_CODE_COMMAND_DISALLOWED;
1529 }
1530
1531 uint8_t report_index = find_report_index_for_report_id_and_report_type(client, report_id, report_type);
1532 if (report_index == HIDS_CLIENT_INVALID_REPORT_INDEX){
1533 return ERROR_CODE_UNSUPPORTED_FEATURE_OR_PARAMETER_VALUE;
1534 }
1535
1536 client->report_index = report_index;
1537
1538 #ifdef ENABLE_TESTING_SUPPORT
1539 client->state = HIDS_CLIENT_W2_READ_CHARACTERISTIC_CONFIGURATION;
1540 #else
1541 client->state = HIDS_CLIENT_W2_SEND_GET_REPORT;
1542 #endif
1543 hids_run_for_client(client);
1544 return ERROR_CODE_SUCCESS;
1545 }
1546
1547
hids_client_get_hid_information(uint16_t hids_cid,uint8_t service_index)1548 uint8_t hids_client_get_hid_information(uint16_t hids_cid, uint8_t service_index){
1549 hids_client_t * client = hids_get_client_for_cid(hids_cid);
1550 if (client == NULL){
1551 return ERROR_CODE_UNKNOWN_CONNECTION_IDENTIFIER;
1552 }
1553
1554 if (client->state != HIDS_CLIENT_STATE_CONNECTED) {
1555 return ERROR_CODE_COMMAND_DISALLOWED;
1556 }
1557
1558 if (service_index >= client->num_instances){
1559 return ERROR_CODE_UNSUPPORTED_FEATURE_OR_PARAMETER_VALUE;
1560 }
1561
1562 client->service_index = service_index;
1563 client->handle = client->services[client->service_index].hid_information_value_handle;
1564
1565 client->state = HIDS_CLIENT_W2_READ_VALUE_OF_CHARACTERISTIC;
1566 hids_run_for_client(client);
1567 return ERROR_CODE_SUCCESS;
1568 }
1569
hids_client_get_protocol_mode(uint16_t hids_cid,uint8_t service_index)1570 uint8_t hids_client_get_protocol_mode(uint16_t hids_cid, uint8_t service_index){
1571 hids_client_t * client = hids_get_client_for_cid(hids_cid);
1572 if (client == NULL){
1573 return ERROR_CODE_UNKNOWN_CONNECTION_IDENTIFIER;
1574 }
1575
1576 if (client->state != HIDS_CLIENT_STATE_CONNECTED) {
1577 return ERROR_CODE_COMMAND_DISALLOWED;
1578 }
1579
1580 if (service_index >= client->num_instances){
1581 return ERROR_CODE_UNSUPPORTED_FEATURE_OR_PARAMETER_VALUE;
1582 }
1583
1584 client->service_index = service_index;
1585 client->handle = client->services[client->service_index].protocol_mode_value_handle;
1586
1587 client->state = HIDS_CLIENT_W2_READ_VALUE_OF_CHARACTERISTIC;
1588 hids_run_for_client(client);
1589 return ERROR_CODE_SUCCESS;
1590 }
1591
hids_client_send_set_protocol_mode(uint16_t hids_cid,uint8_t service_index,hid_protocol_mode_t protocol_mode)1592 uint8_t hids_client_send_set_protocol_mode(uint16_t hids_cid, uint8_t service_index, hid_protocol_mode_t protocol_mode){
1593 hids_client_t * client = hids_get_client_for_cid(hids_cid);
1594 if (client == NULL){
1595 return ERROR_CODE_UNKNOWN_CONNECTION_IDENTIFIER;
1596 }
1597
1598 if (client->state != HIDS_CLIENT_STATE_CONNECTED) {
1599 return ERROR_CODE_COMMAND_DISALLOWED;
1600 }
1601
1602 if (service_index >= client->num_instances){
1603 return ERROR_CODE_UNSUPPORTED_FEATURE_OR_PARAMETER_VALUE;
1604 }
1605
1606 client->service_index = service_index;
1607 client->handle = client->services[client->service_index].protocol_mode_value_handle;
1608 client->value = (uint8_t)protocol_mode;
1609
1610 client->state = HIDS_CLIENT_W2_WRITE_VALUE_OF_CHARACTERISTIC_WITHOUT_RESPONSE;
1611 hids_run_for_client(client);
1612 return ERROR_CODE_SUCCESS;
1613 }
1614
1615
hids_client_send_control_point_cmd(uint16_t hids_cid,uint8_t service_index,uint8_t value)1616 static uint8_t hids_client_send_control_point_cmd(uint16_t hids_cid, uint8_t service_index, uint8_t value){
1617 hids_client_t * client = hids_get_client_for_cid(hids_cid);
1618 if (client == NULL){
1619 return ERROR_CODE_UNKNOWN_CONNECTION_IDENTIFIER;
1620 }
1621
1622 if (client->state != HIDS_CLIENT_STATE_CONNECTED) {
1623 return ERROR_CODE_COMMAND_DISALLOWED;
1624 }
1625
1626 if (service_index >= client->num_instances){
1627 return ERROR_CODE_UNSUPPORTED_FEATURE_OR_PARAMETER_VALUE;
1628 }
1629
1630 client->service_index = service_index;
1631 client->handle = client->services[client->service_index].control_point_value_handle;
1632 client->value = value;
1633
1634 client->state = HIDS_CLIENT_W2_WRITE_VALUE_OF_CHARACTERISTIC_WITHOUT_RESPONSE;
1635 hids_run_for_client(client);
1636 return ERROR_CODE_SUCCESS;
1637 }
1638
hids_client_send_suspend(uint16_t hids_cid,uint8_t service_index)1639 uint8_t hids_client_send_suspend(uint16_t hids_cid, uint8_t service_index){
1640 return hids_client_send_control_point_cmd(hids_cid, service_index, 0);
1641 }
1642
hids_client_send_exit_suspend(uint16_t hids_cid,uint8_t service_index)1643 uint8_t hids_client_send_exit_suspend(uint16_t hids_cid, uint8_t service_index){
1644 return hids_client_send_control_point_cmd(hids_cid, service_index, 1);
1645 }
1646
hids_client_enable_notifications(uint16_t hids_cid)1647 uint8_t hids_client_enable_notifications(uint16_t hids_cid){
1648 hids_client_t * client = hids_get_client_for_cid(hids_cid);
1649 if (client == NULL){
1650 return ERROR_CODE_UNKNOWN_CONNECTION_IDENTIFIER;
1651 }
1652
1653 if (client->state != HIDS_CLIENT_STATE_CONNECTED) {
1654 return ERROR_CODE_COMMAND_DISALLOWED;
1655 }
1656 client->value = GATT_CLIENT_CHARACTERISTICS_CONFIGURATION_NOTIFICATION;
1657 if (hids_client_notifications_configuration_init(client)){
1658 hids_run_for_client(client);
1659 return ERROR_CODE_SUCCESS;
1660 }
1661 hids_emit_notifications_configuration(client);
1662 return ERROR_CODE_SUCCESS;
1663 }
1664
hids_client_disable_notifications(uint16_t hids_cid)1665 uint8_t hids_client_disable_notifications(uint16_t hids_cid){
1666 hids_client_t * client = hids_get_client_for_cid(hids_cid);
1667 if (client == NULL){
1668 return ERROR_CODE_UNKNOWN_CONNECTION_IDENTIFIER;
1669 }
1670
1671 if (client->state != HIDS_CLIENT_STATE_CONNECTED) {
1672 return ERROR_CODE_COMMAND_DISALLOWED;
1673 }
1674
1675 client->value = GATT_CLIENT_CHARACTERISTICS_CONFIGURATION_NONE;
1676 if (hids_client_notifications_configuration_init(client)){
1677 hids_run_for_client(client);
1678 return ERROR_CODE_SUCCESS;
1679 }
1680 hids_emit_notifications_configuration(client);
1681 return ERROR_CODE_SUCCESS;
1682 }
1683
hids_client_init(uint8_t * hid_descriptor_storage,uint16_t hid_descriptor_storage_len)1684 void hids_client_init(uint8_t * hid_descriptor_storage, uint16_t hid_descriptor_storage_len){
1685 hids_client_descriptor_storage = hid_descriptor_storage;
1686 hids_client_descriptor_storage_len = hid_descriptor_storage_len;
1687
1688 hci_event_callback_registration.callback = &handle_hci_event;
1689 hci_add_event_handler(&hci_event_callback_registration);
1690 }
1691
hids_client_deinit(void)1692 void hids_client_deinit(void){}
1693