1 /* 2 * Copyright (C) 2014 BlueKitchen GmbH 3 * 4 * Redistribution and use in source and binary forms, with or without 5 * modification, are permitted provided that the following conditions 6 * are met: 7 * 8 * 1. Redistributions of source code must retain the above copyright 9 * notice, this list of conditions and the following disclaimer. 10 * 2. Redistributions in binary form must reproduce the above copyright 11 * notice, this list of conditions and the following disclaimer in the 12 * documentation and/or other materials provided with the distribution. 13 * 3. Neither the name of the copyright holders nor the names of 14 * contributors may be used to endorse or promote products derived 15 * from this software without specific prior written permission. 16 * 4. Any redistribution, use, or modification is done solely for 17 * personal benefit and not for any commercial purpose or for 18 * monetary gain. 19 * 20 * THIS SOFTWARE IS PROVIDED BY BLUEKITCHEN GMBH AND CONTRIBUTORS 21 * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT 22 * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS 23 * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL BLUEKITCHEN 24 * GMBH OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, 25 * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, 26 * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS 27 * OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED 28 * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, 29 * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF 30 * THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 31 * SUCH DAMAGE. 32 * 33 * Please inquire about commercial licensing options at 34 * [email protected] 35 * 36 */ 37 38 #define BTSTACK_FILE__ "hids_device.c" 39 40 /** 41 * Implementation of the GATT HIDS Device 42 * To use with your application, add '#import <hids.gatt>' to your .gatt file 43 */ 44 45 #include "hids_device.h" 46 47 #include "ble/att_db.h" 48 #include "ble/att_server.h" 49 #include "bluetooth_gatt.h" 50 #include "btstack_util.h" 51 #include "btstack_debug.h" 52 53 #define HIDS_DEVICE_ERROR_CODE_INAPPROPRIATE_CONNECTION_PARAMETERS 0x80 54 55 // storage for 'generic' HID Device with single Input, Output, and Feature Report 56 static hids_device_report_t hid_reports_generic_storage[3]; 57 58 typedef struct{ 59 uint16_t con_handle; 60 61 uint8_t hid_country_code; 62 const uint8_t * hid_descriptor; 63 uint16_t hid_descriptor_size; 64 65 uint16_t hid_report_map_handle; 66 uint8_t hid_protocol_mode; 67 uint16_t hid_protocol_mode_value_handle; 68 69 uint16_t hid_boot_mouse_input_value_handle; 70 uint16_t hid_boot_mouse_input_client_configuration_handle; 71 uint16_t hid_boot_mouse_input_client_configuration_value; 72 73 uint16_t hid_boot_keyboard_input_value_handle; 74 uint16_t hid_boot_keyboard_input_client_configuration_handle; 75 uint16_t hid_boot_keyboard_input_client_configuration_value; 76 77 hids_device_report_t * hid_reports; 78 79 uint8_t hid_input_reports_num; 80 uint8_t hid_output_reports_num; 81 uint8_t hid_feature_reports_num; 82 83 uint16_t hid_control_point_value_handle; 84 uint8_t hid_control_point_suspend; 85 86 btstack_context_callback_registration_t can_send_now_callback; 87 } hids_device_t; 88 89 static hids_device_t hids_device; 90 91 static btstack_packet_handler_t packet_handler; 92 static att_service_handler_t hid_service; 93 94 // TODO: store hids device connection into list 95 static hids_device_t * hids_device_get_instance_for_con_handle(uint16_t con_handle){ 96 UNUSED(con_handle); 97 return &hids_device; 98 } 99 100 static hids_device_t * hids_device_create_instance(void){ 101 memset(&hids_device, 0, sizeof(hids_device_t)); 102 return &hids_device; 103 } 104 105 static hids_device_report_t * hids_device_get_report_for_value_handle(hids_device_t * device, uint16_t client_configuration_handle){ 106 uint8_t pos; 107 uint8_t total_reports = device->hid_input_reports_num + device->hid_output_reports_num + device->hid_feature_reports_num; 108 for (pos = 0 ; pos < total_reports ; pos++){ 109 if (device->hid_reports[pos].value_handle == client_configuration_handle){ 110 return &device->hid_reports[pos]; 111 } 112 } 113 return NULL; 114 } 115 116 static hids_device_report_t * hids_device_get_report_for_client_configuration_handle(hids_device_t * device, uint16_t client_configuration_handle){ 117 uint8_t pos; 118 uint8_t total_reports = device->hid_input_reports_num + device->hid_output_reports_num + device->hid_feature_reports_num; 119 for (pos = 0 ; pos < total_reports ; pos++){ 120 if (device->hid_reports[pos].client_configuration_handle == client_configuration_handle){ 121 return &device->hid_reports[pos]; 122 } 123 } 124 return NULL; 125 } 126 127 static hids_device_report_t * 128 hids_device_get_report_for_id_and_type(hids_device_t *device, uint16_t report_id, hid_report_type_t type) { 129 uint8_t pos; 130 uint8_t total_reports = device->hid_input_reports_num + device->hid_output_reports_num + device->hid_feature_reports_num; 131 for (pos = 0 ; pos < total_reports ; pos++){ 132 if ((device->hid_reports[pos].type == type) && (device->hid_reports[pos].id == report_id)){ 133 return &device->hid_reports[pos]; 134 } 135 } 136 return NULL; 137 } 138 139 static void hids_device_emit_event_with_uint8(uint8_t event, hci_con_handle_t con_handle, uint8_t value){ 140 hids_device_t * instance = hids_device_get_instance_for_con_handle(con_handle); 141 if (!instance){ 142 log_error("no instance for handle 0x%02x", con_handle); 143 return; 144 } 145 146 if (!packet_handler) return; 147 uint8_t buffer[6]; 148 buffer[0] = HCI_EVENT_HIDS_META; 149 buffer[1] = 4; 150 buffer[2] = event; 151 little_endian_store_16(buffer, 3, (uint16_t) con_handle); 152 buffer[5] = value; 153 (*packet_handler)(HCI_EVENT_PACKET, 0, buffer, sizeof(buffer)); 154 } 155 156 static void hids_device_emit_event_with_uint8_uint8_t(uint8_t event, hci_con_handle_t con_handle, uint8_t value_1, uint8_t value_2){ 157 hids_device_t * instance = hids_device_get_instance_for_con_handle(con_handle); 158 if (!instance){ 159 log_error("no instance for handle 0x%02x", con_handle); 160 return; 161 } 162 163 if (!packet_handler) return; 164 uint8_t buffer[7]; 165 buffer[0] = HCI_EVENT_HIDS_META; 166 buffer[1] = 4; 167 buffer[2] = event; 168 little_endian_store_16(buffer, 3, (uint16_t) con_handle); 169 buffer[5] = value_1; 170 buffer[6] = value_2; 171 (*packet_handler)(HCI_EVENT_PACKET, 0, buffer, sizeof(buffer)); 172 } 173 174 static void hids_device_emit_event(uint8_t event, hci_con_handle_t con_handle){ 175 hids_device_t * instance = hids_device_get_instance_for_con_handle(con_handle); 176 if (!instance){ 177 log_error("no instance for handle 0x%02x", con_handle); 178 return; 179 } 180 181 if (!packet_handler) return; 182 uint8_t buffer[5]; 183 buffer[0] = HCI_EVENT_HIDS_META; 184 buffer[1] = 4; 185 buffer[2] = event; 186 little_endian_store_16(buffer, 3, (uint16_t) con_handle); 187 (*packet_handler)(HCI_EVENT_PACKET, 0, buffer, sizeof(buffer)); 188 } 189 190 static void hids_device_can_send_now(void * context){ 191 hci_con_handle_t con_handle = (hci_con_handle_t) (uintptr_t) context; 192 // notify client 193 hids_device_t * instance = hids_device_get_instance_for_con_handle(con_handle); 194 if (!instance){ 195 log_error("no instance for handle 0x%02x", con_handle); 196 return; 197 } 198 199 if (!packet_handler) return; 200 uint8_t buffer[5]; 201 buffer[0] = HCI_EVENT_HIDS_META; 202 buffer[1] = 3; 203 buffer[2] = HIDS_SUBEVENT_CAN_SEND_NOW; 204 little_endian_store_16(buffer, 3, (uint16_t) con_handle); 205 (*packet_handler)(HCI_EVENT_PACKET, 0, buffer, sizeof(buffer)); 206 } 207 208 // ATT Client Read Callback for Dynamic Data 209 // - if buffer == NULL, don't copy data, just return size of value 210 // - if buffer != NULL, copy data and return number bytes copied 211 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){ 212 hids_device_t * instance = hids_device_get_instance_for_con_handle(con_handle); 213 if (!instance){ 214 log_error("no instance for handle 0x%02x", con_handle); 215 return HIDS_DEVICE_ERROR_CODE_INAPPROPRIATE_CONNECTION_PARAMETERS; 216 } 217 218 if (att_handle == instance->hid_protocol_mode_value_handle){ 219 log_info("Read protocol mode"); 220 return att_read_callback_handle_byte(instance->hid_protocol_mode, offset, buffer, buffer_size); 221 } 222 223 if (att_handle == instance->hid_report_map_handle){ 224 log_info("Read report map"); 225 return att_read_callback_handle_blob(instance->hid_descriptor, instance->hid_descriptor_size, offset, buffer, buffer_size); 226 } 227 228 if (att_handle == instance->hid_boot_mouse_input_client_configuration_handle){ 229 return att_read_callback_handle_little_endian_16(instance->hid_boot_mouse_input_client_configuration_value, offset, buffer, buffer_size); 230 } 231 232 if (att_handle == instance->hid_boot_keyboard_input_client_configuration_handle){ 233 return att_read_callback_handle_little_endian_16(instance->hid_boot_keyboard_input_client_configuration_value, offset, buffer, buffer_size); 234 } 235 236 if (att_handle == instance->hid_control_point_value_handle){ 237 if (buffer && (buffer_size >= 1u)){ 238 buffer[0] = instance->hid_control_point_suspend; 239 } 240 return 1; 241 } 242 243 hids_device_report_t * report = hids_device_get_report_for_client_configuration_handle(instance, att_handle); 244 if (report != NULL){ 245 return att_read_callback_handle_little_endian_16(report->client_configuration_value, offset, buffer, buffer_size); 246 } 247 return 0; 248 } 249 250 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){ 251 UNUSED(buffer_size); 252 UNUSED(offset); 253 254 if (transaction_mode != ATT_TRANSACTION_MODE_NONE){ 255 return 0; 256 } 257 258 hids_device_t * instance = hids_device_get_instance_for_con_handle(con_handle); 259 if (!instance){ 260 log_error("no instance for handle 0x%02x", con_handle); 261 return HIDS_DEVICE_ERROR_CODE_INAPPROPRIATE_CONNECTION_PARAMETERS; 262 } 263 264 if (att_handle == instance->hid_boot_mouse_input_client_configuration_handle){ 265 uint16_t new_value = little_endian_read_16(buffer, 0); 266 instance->hid_boot_mouse_input_client_configuration_value = new_value; 267 hids_device_emit_event_with_uint8(HIDS_SUBEVENT_BOOT_MOUSE_INPUT_REPORT_ENABLE, con_handle, (uint8_t) new_value); 268 } 269 if (att_handle == instance->hid_boot_keyboard_input_client_configuration_handle){ 270 uint16_t new_value = little_endian_read_16(buffer, 0); 271 instance->hid_boot_keyboard_input_client_configuration_value = new_value; 272 hids_device_emit_event_with_uint8(HIDS_SUBEVENT_BOOT_KEYBOARD_INPUT_REPORT_ENABLE, con_handle, (uint8_t) new_value); 273 } 274 275 if (att_handle == instance->hid_protocol_mode_value_handle){ 276 instance->hid_protocol_mode = buffer[0]; 277 log_info("Set protocol mode: %u", instance->hid_protocol_mode); 278 hids_device_emit_event_with_uint8(HIDS_SUBEVENT_PROTOCOL_MODE, con_handle, instance->hid_protocol_mode); 279 } 280 281 if (att_handle == instance->hid_control_point_value_handle){ 282 if (buffer_size < 1u){ 283 return ATT_ERROR_INVALID_OFFSET; 284 } 285 instance->hid_control_point_suspend = buffer[0]; 286 instance->con_handle = con_handle; 287 log_info("Set suspend tp: %u", instance->hid_control_point_suspend ); 288 if (instance->hid_control_point_suspend == 0u){ 289 hids_device_emit_event(HIDS_SUBEVENT_SUSPEND, con_handle); 290 } else if (instance->hid_control_point_suspend == 1u){ 291 hids_device_emit_event(HIDS_SUBEVENT_EXIT_SUSPEND, con_handle); 292 } 293 } 294 295 hids_device_report_t * report; 296 report = hids_device_get_report_for_value_handle(instance, att_handle); 297 if (report != NULL){ 298 // assemble event in buffer 299 uint8_t event[257]; 300 uint16_t pos = 0; 301 event[pos++] = HCI_EVENT_HIDS_META; 302 // skip length 303 pos++; 304 event[pos++] = HIDS_SUBEVENT_SET_REPORT; 305 little_endian_store_16(event, pos, con_handle); 306 pos += 2; 307 event[pos++] = report->id; 308 event[pos++] = (uint8_t) report->type; 309 uint8_t length_to_copy = btstack_min(buffer_size, 250); 310 event[pos++] = length_to_copy; 311 memcpy(&event[pos], buffer, length_to_copy); 312 pos += length_to_copy; 313 // set event length 314 event[1] = pos - 2; 315 (*packet_handler)(HCI_EVENT_PACKET, 0, event, pos); 316 return 0; 317 } 318 319 report = hids_device_get_report_for_client_configuration_handle(instance, att_handle); 320 if (report != NULL){ 321 uint16_t new_value = little_endian_read_16(buffer, 0); 322 report->client_configuration_value = new_value; 323 log_info("Enable Report (type %u) notifications: %x", (uint8_t) report->type, new_value); 324 325 switch (report->type){ 326 case HID_REPORT_TYPE_INPUT: 327 hids_device_emit_event_with_uint8_uint8_t(HIDS_SUBEVENT_INPUT_REPORT_ENABLE, con_handle, report->id, (uint8_t) new_value); 328 break; 329 case HID_REPORT_TYPE_OUTPUT: 330 hids_device_emit_event_with_uint8_uint8_t(HIDS_SUBEVENT_OUTPUT_REPORT_ENABLE, con_handle, report->id, (uint8_t) new_value); 331 break; 332 case HID_REPORT_TYPE_FEATURE: 333 hids_device_emit_event_with_uint8_uint8_t(HIDS_SUBEVENT_FEATURE_REPORT_ENABLE, con_handle, report->id, (uint8_t) new_value); 334 break; 335 default: 336 btstack_unreachable(); 337 break; 338 } 339 } 340 return 0; 341 } 342 343 void hids_device_init_with_storage(uint8_t hid_country_code, const uint8_t * hid_descriptor, uint16_t hid_descriptor_size, 344 uint16_t num_reports, hids_device_report_t * report_storage){ 345 346 hids_device_t * instance = hids_device_create_instance(); 347 348 btstack_assert(num_reports > 0); 349 btstack_assert(report_storage != NULL); 350 351 instance->hid_country_code = hid_country_code; 352 instance->hid_descriptor = hid_descriptor; 353 instance->hid_descriptor_size = hid_descriptor_size; 354 355 // default 356 instance->hid_protocol_mode = 1; 357 358 // get service handle range 359 uint16_t start_handle = 0; 360 uint16_t end_handle = 0xffff; 361 int service_found = gatt_server_get_handle_range_for_service_with_uuid16(ORG_BLUETOOTH_SERVICE_HUMAN_INTERFACE_DEVICE, &start_handle, &end_handle); 362 btstack_assert(service_found != 0); 363 UNUSED(service_found); 364 365 // get report map handle 366 instance->hid_report_map_handle = gatt_server_get_value_handle_for_characteristic_with_uuid16(start_handle, end_handle, ORG_BLUETOOTH_CHARACTERISTIC_REPORT_MAP); 367 368 // get report map handle 369 instance->hid_protocol_mode_value_handle = gatt_server_get_value_handle_for_characteristic_with_uuid16(start_handle, end_handle, ORG_BLUETOOTH_CHARACTERISTIC_PROTOCOL_MODE); 370 371 // get value and client configuration handles for boot mouse input, boot keyboard input and report input 372 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); 373 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); 374 375 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); 376 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); 377 378 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); 379 380 log_info("hid_report_map_handle 0x%02x", instance->hid_report_map_handle); 381 log_info("hid_protocol_mode_value_handle 0x%02x", instance->hid_protocol_mode_value_handle); 382 log_info("hid_boot_mouse_input_value_handle 0x%02x", instance->hid_boot_mouse_input_value_handle); 383 log_info("hid_boot_mouse_input_client_configuration_handle 0x%02x", instance->hid_boot_mouse_input_client_configuration_handle); 384 log_info("hid_boot_keyboard_input_value_handle 0x%02x", instance->hid_boot_keyboard_input_value_handle); 385 log_info("hid_boot_keyboard_input_client_configuration_handle 0x%02x", instance->hid_boot_keyboard_input_client_configuration_handle); 386 log_info("hid_control_point_value_handle 0x%02x", instance->hid_control_point_value_handle); 387 388 instance->hid_reports = report_storage; 389 390 uint16_t hid_reports_num = num_reports; 391 uint16_t assigned_reports_num = 0; 392 uint16_t start_chr_handle = start_handle; 393 394 while ( (start_chr_handle < end_handle) && (assigned_reports_num < hid_reports_num)) { 395 // mandatory 396 uint16_t chr_value_handle = gatt_server_get_value_handle_for_characteristic_with_uuid16(start_chr_handle, end_handle, ORG_BLUETOOTH_CHARACTERISTIC_REPORT); 397 if (chr_value_handle == 0){ 398 break; 399 } 400 401 // optional 402 uint16_t chr_client_configuration_handle = gatt_server_get_client_configuration_handle_for_characteristic_with_uuid16(start_chr_handle, end_handle, ORG_BLUETOOTH_CHARACTERISTIC_REPORT); 403 404 // mandatory 405 uint16_t report_reference_handle = gatt_server_get_descriptor_handle_for_characteristic_with_uuid16(start_chr_handle, end_handle, ORG_BLUETOOTH_CHARACTERISTIC_REPORT, ORG_BLUETOOTH_DESCRIPTOR_REPORT_REFERENCE); 406 if (report_reference_handle == 0){ 407 break; 408 } 409 410 // get report id and type from report reference 411 uint16_t report_reference_value_len; 412 const uint8_t * report_reference_value = gatt_server_get_const_value_for_handle(report_reference_handle, &report_reference_value_len); 413 if (report_reference_value == NULL){ 414 break; 415 } 416 if (report_reference_value_len != 2){ 417 break; 418 } 419 uint8_t report_id = report_reference_value[0]; 420 hid_report_type_t report_type = (hid_report_type_t) report_reference_value[1]; 421 422 // store report info 423 hids_device_report_t * report = &report_storage[assigned_reports_num]; 424 report->value_handle = chr_value_handle; 425 report->client_configuration_handle = chr_client_configuration_handle; 426 report->client_configuration_value = 0; 427 report->id = report_id; 428 report->type = report_type; 429 430 switch (report->type){ 431 case HID_REPORT_TYPE_INPUT: 432 instance->hid_input_reports_num++; 433 break; 434 case HID_REPORT_TYPE_OUTPUT: 435 instance->hid_output_reports_num++; 436 break; 437 case HID_REPORT_TYPE_FEATURE: 438 instance->hid_feature_reports_num++; 439 break; 440 default: 441 btstack_unreachable(); 442 return; 443 } 444 log_info("hid_report_value_handle 0x%02x, id %u, type %u", report->value_handle, report->id, (uint8_t)report->type); 445 if (report->client_configuration_handle != 0){ 446 log_info("hid_report_client_configuration_handle 0x%02x", report->client_configuration_handle); 447 } 448 449 assigned_reports_num++; 450 start_chr_handle = report_reference_handle + 1; 451 } 452 453 // register service with ATT Server 454 hid_service.start_handle = start_handle; 455 hid_service.end_handle = end_handle; 456 hid_service.read_callback = &att_read_callback; 457 hid_service.write_callback = &att_write_callback; 458 att_server_register_service_handler(&hid_service); 459 } 460 461 /** 462 * @brief Set up HIDS Device 463 */ 464 void hids_device_init(uint8_t country_code, const uint8_t * descriptor, uint16_t descriptor_size){ 465 uint16_t hid_reports_num = sizeof(hid_reports_generic_storage) / sizeof(hids_device_report_t); 466 hids_device_init_with_storage(country_code, descriptor, descriptor_size, hid_reports_num, hid_reports_generic_storage); 467 } 468 469 /** 470 * @brief Register callback for the HIDS Device client. 471 * @param callback 472 */ 473 void hids_device_register_packet_handler(btstack_packet_handler_t callback){ 474 packet_handler = callback; 475 } 476 477 /** 478 * @brief Request can send now event to send HID Report 479 * Generates an HIDS_SUBEVENT_CAN_SEND_NOW subevent 480 * @param hid_cid 481 */ 482 void hids_device_request_can_send_now_event(hci_con_handle_t con_handle){ 483 hids_device_t * instance = hids_device_get_instance_for_con_handle(con_handle); 484 if (!instance){ 485 log_error("no instance for handle 0x%02x", con_handle); 486 return; 487 } 488 489 instance->can_send_now_callback.callback = &hids_device_can_send_now; 490 instance->can_send_now_callback.context = (void*) (uintptr_t) con_handle; 491 att_server_register_can_send_now_callback(&instance->can_send_now_callback, con_handle); 492 } 493 494 uint8_t hids_device_send_input_report_for_id(hci_con_handle_t con_handle, uint16_t report_id, const uint8_t * report, uint16_t report_len){ 495 hids_device_t * instance = hids_device_get_instance_for_con_handle(con_handle); 496 if (!instance){ 497 log_error("no instance for handle 0x%02x", con_handle); 498 return ERROR_CODE_UNKNOWN_CONNECTION_IDENTIFIER; 499 } 500 501 hids_device_report_t * report_storage = hids_device_get_report_for_id_and_type(instance, report_id, 502 HID_REPORT_TYPE_INPUT); 503 if (report_storage == NULL){ 504 return ERROR_CODE_UNSUPPORTED_FEATURE_OR_PARAMETER_VALUE; 505 } 506 507 return att_server_notify(con_handle, report_storage->value_handle, report, report_len); 508 } 509 510 uint8_t hids_device_send_input_report(hci_con_handle_t con_handle, const uint8_t * report, uint16_t report_len){ 511 hids_device_t * device = hids_device_get_instance_for_con_handle(con_handle); 512 if (!device){ 513 log_error("no instance for handle 0x%02x", con_handle); 514 return ERROR_CODE_UNKNOWN_CONNECTION_IDENTIFIER; 515 } 516 517 uint8_t pos; 518 uint8_t total_reports = device->hid_input_reports_num + device->hid_output_reports_num + device->hid_feature_reports_num; 519 for (pos = 0 ; pos < total_reports ; pos++){ 520 hids_device_report_t * report_storage = &device->hid_reports[pos]; 521 if (report_storage->type == HID_REPORT_TYPE_INPUT){ 522 return att_server_notify(con_handle, report_storage->value_handle, report, report_len); 523 } 524 } 525 return ERROR_CODE_UNSUPPORTED_FEATURE_OR_PARAMETER_VALUE; 526 } 527 528 /** 529 * @brief Send HID Boot Mouse Input Report 530 */ 531 uint8_t hids_device_send_boot_mouse_input_report(hci_con_handle_t con_handle, const uint8_t * report, uint16_t report_len){ 532 hids_device_t * instance = hids_device_get_instance_for_con_handle(con_handle); 533 if (!instance){ 534 log_error("no instance for handle 0x%02x", con_handle); 535 return ERROR_CODE_UNKNOWN_CONNECTION_IDENTIFIER; 536 } 537 return att_server_notify(con_handle, instance->hid_boot_mouse_input_value_handle, report, report_len); 538 } 539 540 /** 541 * @brief Send HID Boot Mouse Input Report 542 */ 543 uint8_t hids_device_send_boot_keyboard_input_report(hci_con_handle_t con_handle, const uint8_t * report, uint16_t report_len){ 544 hids_device_t * instance = hids_device_get_instance_for_con_handle(con_handle); 545 if (!instance){ 546 log_error("no instance for handle 0x%02x", con_handle); 547 return ERROR_CODE_UNKNOWN_CONNECTION_IDENTIFIER; 548 } 549 return att_server_notify(con_handle, instance->hid_boot_keyboard_input_value_handle, report, report_len); 550 } 551