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