1 /* 2 * Copyright (C) 2014 BlueKitchen GmbH 3 * 4 * Redistribution and use in source and binary forms, with or without 5 * modification, are permitted provided that the following conditions 6 * are met: 7 * 8 * 1. Redistributions of source code must retain the above copyright 9 * notice, this list of conditions and the following disclaimer. 10 * 2. Redistributions in binary form must reproduce the above copyright 11 * notice, this list of conditions and the following disclaimer in the 12 * documentation and/or other materials provided with the distribution. 13 * 3. Neither the name of the copyright holders nor the names of 14 * contributors may be used to endorse or promote products derived 15 * from this software without specific prior written permission. 16 * 4. Any redistribution, use, or modification is done solely for 17 * personal benefit and not for any commercial purpose or for 18 * monetary gain. 19 * 20 * THIS SOFTWARE IS PROVIDED BY BLUEKITCHEN GMBH AND CONTRIBUTORS 21 * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT 22 * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS 23 * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL MATTHIAS 24 * RINGWALD OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, 25 * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, 26 * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS 27 * OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED 28 * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, 29 * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF 30 * THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 31 * SUCH DAMAGE. 32 * 33 * Please inquire about commercial licensing options at 34 * [email protected] 35 * 36 */ 37 38 /** 39 * Implementation of the GATT HIDS Device 40 * To use with your application, add '#import <hids.gatt>' to your .gatt file 41 */ 42 43 #include "hids_device.h" 44 45 #include "att_db.h" 46 #include "att_server.h" 47 #include "bluetooth_gatt.h" 48 #include "btstack_util.h" 49 50 static btstack_packet_handler_t packet_handler; 51 static btstack_context_callback_registration_t battery_callback; 52 53 static uint8_t hid_country_code; 54 static const uint8_t * hid_descriptor; 55 static uint16_t hid_descriptor_size; 56 57 static uint16_t hid_report_map_handle; 58 static uint16_t hid_protocol_mode; 59 static uint16_t hid_protocol_mode_value_handle; 60 61 static uint16_t hid_boot_mouse_input_value_handle; 62 static uint16_t hid_boot_mouse_input_client_configuration_handle; 63 static uint16_t hid_boot_mouse_input_client_configuration_value; 64 65 static uint16_t hid_boot_keyboard_input_value_handle; 66 static uint16_t hid_boot_keyboard_input_client_configuration_handle; 67 static uint16_t hid_boot_keyboard_input_client_configuration_value; 68 69 static uint16_t hid_report_input_value_handle; 70 static uint16_t hid_report_input_client_configuration_handle; 71 static uint16_t hid_report_input_client_configuration_value; 72 73 static att_service_handler_t hid_service; 74 75 static void hids_device_emit_event_with_uint8(uint8_t event, hci_con_handle_t con_handle, uint8_t value){ 76 if (!packet_handler) return; 77 uint8_t buffer[6]; 78 buffer[0] = HCI_EVENT_HIDS_META; 79 buffer[1] = 4; 80 buffer[2] = event; 81 little_endian_store_16(buffer, 3, (uint16_t) con_handle); 82 buffer[5] = value; 83 (*packet_handler)(HCI_EVENT_PACKET, 0, buffer, sizeof(buffer)); 84 } 85 86 static void hids_device_can_send_now(void * context){ 87 hci_con_handle_t con_handle = (hci_con_handle_t) (uintptr_t) context; 88 // notify client 89 if (!packet_handler) return; 90 uint8_t buffer[5]; 91 buffer[0] = HCI_EVENT_HIDS_META; 92 buffer[1] = 3; 93 buffer[2] = HIDS_SUBEVENT_CAN_SEND_NOW; 94 little_endian_store_16(buffer, 3, (uint16_t) con_handle); 95 (*packet_handler)(HCI_EVENT_PACKET, 0, buffer, sizeof(buffer)); 96 } 97 98 // ATT Client Read Callback for Dynamic Data 99 // - if buffer == NULL, don't copy data, just return size of value 100 // - if buffer != NULL, copy data and return number bytes copied 101 static uint16_t att_read_callback(hci_con_handle_t connection_handle, uint16_t att_handle, uint16_t offset, uint8_t * buffer, uint16_t buffer_size){ 102 UNUSED(connection_handle); 103 104 if (att_handle == hid_protocol_mode_value_handle){ 105 return att_read_callback_handle_byte(hid_protocol_mode, offset, buffer, buffer_size); 106 } 107 if (att_handle == hid_report_map_handle){ 108 return att_read_callback_handle_blob(hid_descriptor, hid_descriptor_size, offset, buffer, buffer_size); 109 } 110 // if (att_handle == hid_boot_mouse_input_value_handle){ 111 // } 112 if (att_handle == hid_boot_mouse_input_client_configuration_handle){ 113 return att_read_callback_handle_little_endian_16(hid_boot_keyboard_input_client_configuration_value, offset, buffer, buffer_size); 114 } 115 // if (att_handle == hid_boot_keyboard_input_value_handle){ 116 // } 117 if (att_handle == hid_boot_keyboard_input_client_configuration_handle){ 118 return att_read_callback_handle_little_endian_16(hid_boot_keyboard_input_client_configuration_value, offset, buffer, buffer_size); 119 } 120 // if (att_handle == hid_report_input_value_handle){ 121 // } 122 if (att_handle == hid_report_input_client_configuration_handle){ 123 return att_read_callback_handle_little_endian_16(hid_report_input_client_configuration_value, offset, buffer, buffer_size); 124 } 125 return 0; 126 } 127 128 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){ 129 UNUSED(transaction_mode); 130 UNUSED(buffer_size); 131 UNUSED(offset); 132 133 if (att_handle == hid_boot_mouse_input_client_configuration_handle){ 134 hid_boot_mouse_input_client_configuration_value = little_endian_read_16(buffer, 0); 135 hids_device_emit_event_with_uint8(HIDS_SUBEVENT_BOOT_MOUSE_INPUT_REPORT_ENABLE, con_handle, hid_protocol_mode); 136 } 137 if (att_handle == hid_boot_keyboard_input_client_configuration_handle){ 138 hid_boot_keyboard_input_client_configuration_value = little_endian_read_16(buffer, 0); 139 hids_device_emit_event_with_uint8(HIDS_SUBEVENT_BOOT_KEYBOARD_INPUT_REPORT_ENABLE, con_handle, hid_protocol_mode); 140 } 141 if (att_handle == hid_report_input_client_configuration_handle){ 142 hid_report_input_client_configuration_value = little_endian_read_16(buffer, 0); 143 hids_device_emit_event_with_uint8(HIDS_SUBEVENT_INPUT_REPORT_ENABLE, con_handle, hid_protocol_mode); 144 } 145 if (att_handle == hid_protocol_mode_value_handle){ 146 hid_protocol_mode = buffer[0]; 147 hids_device_emit_event_with_uint8(HIDS_SUBEVENT_PROTOCOL_MODE, con_handle, hid_protocol_mode); 148 } 149 return 0; 150 } 151 152 /** 153 * @brief Set up HIDS Device 154 */ 155 void hids_device_init(uint8_t country_code, const uint8_t * descriptor, uint16_t descriptor_size){ 156 157 hid_country_code = country_code; 158 hid_descriptor = descriptor; 159 hid_descriptor_size = descriptor_size; 160 161 // default 162 hid_protocol_mode = 1; 163 164 // get service handle range 165 uint16_t start_handle = 0; 166 uint16_t end_handle = 0xfff; 167 int service_found = gatt_server_get_get_handle_range_for_service_with_uuid16(ORG_BLUETOOTH_SERVICE_HUMAN_INTERFACE_DEVICE, &start_handle, &end_handle); 168 if (!service_found) return; 169 170 // get report map handle 171 hid_report_map_handle = gatt_server_get_value_handle_for_characteristic_with_uuid16(start_handle, end_handle, ORG_BLUETOOTH_CHARACTERISTIC_REPORT_MAP); 172 173 // get report map handle 174 hid_protocol_mode_value_handle = gatt_server_get_value_handle_for_characteristic_with_uuid16(start_handle, end_handle, ORG_BLUETOOTH_CHARACTERISTIC_PROTOCOL_MODE); 175 176 // get value and client configuration handles for boot mouse input, boot keyboard input and report input 177 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); 178 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); 179 180 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); 181 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); 182 183 hid_report_input_value_handle = gatt_server_get_value_handle_for_characteristic_with_uuid16(start_handle, end_handle, ORG_BLUETOOTH_CHARACTERISTIC_REPORT); 184 hid_report_input_client_configuration_handle = gatt_server_get_client_configuration_handle_for_characteristic_with_uuid16(start_handle, end_handle, ORG_BLUETOOTH_CHARACTERISTIC_REPORT); 185 186 // register service with ATT DB 187 hid_service.start_handle = start_handle; 188 hid_service.end_handle = end_handle; 189 hid_service.read_callback = &att_read_callback; 190 hid_service.write_callback = &att_write_callback; 191 att_register_service_handler(&hid_service); 192 } 193 194 /** 195 * @brief Register callback for the HIDS Device client. 196 * @param callback 197 */ 198 void hids_device_register_packet_handler(btstack_packet_handler_t callback){ 199 packet_handler = callback; 200 } 201 202 /** 203 * @brief Request can send now event to send HID Report 204 * Generates an HIDS_SUBEVENT_CAN_SEND_NOW subevent 205 * @param hid_cid 206 */ 207 void hids_device_request_can_send_now_event(hci_con_handle_t con_handle){ 208 battery_callback.callback = &hids_device_can_send_now; 209 battery_callback.context = (void*) (uintptr_t) con_handle; 210 att_server_register_can_send_now_callback(&battery_callback, con_handle); 211 } 212 213 /** 214 * @brief Send HID Report: Input 215 */ 216 void hids_device_send_input_report(hci_con_handle_t con_handle, const uint8_t * report, uint16_t report_len){ 217 att_server_notify(con_handle, hid_report_input_value_handle, (uint8_t*) report, report_len); 218 } 219 220 /** 221 * @brief Send HID Boot Mouse Input Report 222 */ 223 void hids_device_send_boot_mouse_input_report(hci_con_handle_t con_handle, const uint8_t * report, uint16_t report_len){ 224 att_server_notify(con_handle, hid_boot_mouse_input_value_handle, (uint8_t*) report, report_len); 225 } 226 227 /** 228 * @brief Send HID Boot Mouse Input Report 229 */ 230 void hids_device_send_boot_keyboard_input_report(hci_con_handle_t con_handle, const uint8_t * report, uint16_t report_len){ 231 att_server_notify(con_handle, hid_boot_keyboard_input_value_handle, (uint8_t*) report, report_len); 232 } 233 234