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__ "hid_mouse_demo.c" 39 40 // ***************************************************************************** 41 /* EXAMPLE_START(hid_mouse_demo): HID Mouse Classic 42 * 43 * @text This HID Device example demonstrates how to implement 44 * an HID keyboard. Without a HAVE_BTSTACK_STDIN, a fixed demo text is sent 45 * If HAVE_BTSTACK_STDIN is defined, you can type from the terminal 46 */ 47 // ***************************************************************************** 48 49 50 #include <stdint.h> 51 #include <stdio.h> 52 #include <stdlib.h> 53 #include <string.h> 54 #include <inttypes.h> 55 56 #include "btstack.h" 57 58 #ifdef HAVE_BTSTACK_STDIN 59 #include "btstack_stdin.h" 60 #endif 61 62 // to enable demo text on POSIX systems 63 // #undef HAVE_BTSTACK_STDIN 64 65 static uint8_t hid_service_buffer[270]; 66 static uint8_t device_id_sdp_service_buffer[100]; 67 static const char hid_device_name[] = "BTstack HID Mouse"; 68 static btstack_packet_callback_registration_t hci_event_callback_registration; 69 static uint16_t hid_cid; 70 71 // from USB HID Specification 1.1, Appendix B.2 72 const uint8_t hid_descriptor_mouse_boot_mode[] = { 73 0x05, 0x01, // USAGE_PAGE (Generic Desktop) 74 0x09, 0x02, // USAGE (Mouse) 75 0xa1, 0x01, // COLLECTION (Application) 76 77 0x09, 0x01, // USAGE (Pointer) 78 0xa1, 0x00, // COLLECTION (Physical) 79 80 0x05, 0x09, // USAGE_PAGE (Button) 81 0x19, 0x01, // USAGE_MINIMUM (Button 1) 82 0x29, 0x03, // USAGE_MAXIMUM (Button 3) 83 0x15, 0x00, // LOGICAL_MINIMUM (0) 84 0x25, 0x01, // LOGICAL_MAXIMUM (1) 85 0x95, 0x03, // REPORT_COUNT (3) 86 0x75, 0x01, // REPORT_SIZE (1) 87 0x81, 0x02, // INPUT (Data,Var,Abs) 88 0x95, 0x01, // REPORT_COUNT (1) 89 0x75, 0x05, // REPORT_SIZE (5) 90 0x81, 0x03, // INPUT (Cnst,Var,Abs) 91 92 0x05, 0x01, // USAGE_PAGE (Generic Desktop) 93 0x09, 0x30, // USAGE (X) 94 0x09, 0x31, // USAGE (Y) 95 0x15, 0x81, // LOGICAL_MINIMUM (-127) 96 0x25, 0x7f, // LOGICAL_MAXIMUM (127) 97 0x75, 0x08, // REPORT_SIZE (8) 98 0x95, 0x02, // REPORT_COUNT (2) 99 0x81, 0x06, // INPUT (Data,Var,Rel) 100 101 0xc0, // END_COLLECTION 102 0xc0 // END_COLLECTION 103 }; 104 105 // HID Report sending 106 static void send_report(uint8_t buttons, int8_t dx, int8_t dy){ 107 uint8_t report[] = { 0xa1, buttons, (uint8_t) dx, (uint8_t) dy}; 108 hid_device_send_interrupt_message(hid_cid, &report[0], sizeof(report)); 109 printf("Mouse: %d/%d - buttons: %02x\n", dx, dy, buttons); 110 } 111 112 static int dx; 113 static int dy; 114 static uint8_t buttons; 115 static int hid_boot_device = 0; 116 117 static void mousing_can_send_now(void){ 118 send_report(buttons, dx, dy); 119 // reset 120 dx = 0; 121 dy = 0; 122 if (buttons){ 123 buttons = 0; 124 hid_device_request_can_send_now_event(hid_cid); 125 } 126 } 127 128 // Demo Application 129 130 #ifdef HAVE_BTSTACK_STDIN 131 132 static const int MOUSE_SPEED = 30; 133 134 // On systems with STDIN, we can directly type on the console 135 136 static void stdin_process(char character){ 137 138 if (!hid_cid) { 139 printf("Mouse not connected, ignoring '%c'\n", character); 140 return; 141 } 142 143 switch (character){ 144 case 'a': 145 dx -= MOUSE_SPEED; 146 break; 147 case 's': 148 dy += MOUSE_SPEED; 149 break; 150 case 'd': 151 dx += MOUSE_SPEED; 152 break; 153 case 'w': 154 dy -= MOUSE_SPEED; 155 break; 156 case 'l': 157 buttons |= 1; 158 break; 159 case 'r': 160 buttons |= 2; 161 break; 162 default: 163 return; 164 } 165 hid_device_request_can_send_now_event(hid_cid); 166 } 167 168 #else 169 170 // On embedded systems, simulate clicking on 4 corners of a square 171 172 #define MOUSE_PERIOD_MS 15 173 174 static int step; 175 static const int STEPS_PER_DIRECTION = 50; 176 static const int MOUSE_SPEED = 10; 177 178 static struct { 179 int dx; 180 int dy; 181 } directions[] = { 182 { 1, 0 }, 183 { 0, 1 }, 184 { -1, 0 }, 185 { 0, -1 }, 186 }; 187 188 static btstack_timer_source_t mousing_timer; 189 190 static void mousing_timer_handler(btstack_timer_source_t * ts){ 191 192 if (!hid_cid) return; 193 194 // simulate left click when corner reached 195 if (step % STEPS_PER_DIRECTION == 0){ 196 buttons |= 1; 197 } 198 // simulate move 199 int direction_index = step / STEPS_PER_DIRECTION; 200 dx += directions[direction_index].dx * MOUSE_SPEED; 201 dy += directions[direction_index].dy * MOUSE_SPEED; 202 203 // next 204 step++; 205 if (step >= STEPS_PER_DIRECTION * 4) { 206 step = 0; 207 } 208 209 // trigger send 210 hid_device_request_can_send_now_event(hid_cid); 211 212 // set next timer 213 btstack_run_loop_set_timer(ts, MOUSE_PERIOD_MS); 214 btstack_run_loop_add_timer(ts); 215 } 216 217 static void hid_embedded_start_mousing(void){ 218 printf("Start mousing..\n"); 219 220 step = 0; 221 222 // set one-shot timer 223 mousing_timer.process = &mousing_timer_handler; 224 btstack_run_loop_set_timer(&mousing_timer, MOUSE_PERIOD_MS); 225 btstack_run_loop_add_timer(&mousing_timer); 226 } 227 #endif 228 229 static void packet_handler(uint8_t packet_type, uint16_t channel, uint8_t * packet, uint16_t packet_size){ 230 UNUSED(channel); 231 UNUSED(packet_size); 232 switch (packet_type){ 233 case HCI_EVENT_PACKET: 234 switch (hci_event_packet_get_type(packet)){ 235 case HCI_EVENT_USER_CONFIRMATION_REQUEST: 236 // ssp: inform about user confirmation request 237 log_info("SSP User Confirmation Request with numeric value '%06"PRIu32"'\n", hci_event_user_confirmation_request_get_numeric_value(packet)); 238 log_info("SSP User Confirmation Auto accept\n"); 239 break; 240 241 case HCI_EVENT_HID_META: 242 switch (hci_event_hid_meta_get_subevent_code(packet)){ 243 case HID_SUBEVENT_CONNECTION_OPENED: 244 if (hid_subevent_connection_opened_get_status(packet) != ERROR_CODE_SUCCESS) return; 245 hid_cid = hid_subevent_connection_opened_get_hid_cid(packet); 246 #ifdef HAVE_BTSTACK_STDIN 247 printf("HID Connected, control mouse using 'a','s',''d', 'w' keys for movement and 'l' and 'r' for buttons...\n"); 248 #else 249 printf("HID Connected, simulating mouse movements...\n"); 250 hid_embedded_start_mousing(); 251 #endif 252 break; 253 case HID_SUBEVENT_CONNECTION_CLOSED: 254 printf("HID Disconnected\n"); 255 hid_cid = 0; 256 break; 257 case HID_SUBEVENT_CAN_SEND_NOW: 258 mousing_can_send_now(); 259 break; 260 default: 261 break; 262 } 263 break; 264 default: 265 break; 266 } 267 break; 268 default: 269 break; 270 } 271 } 272 273 /* @section Main Application Setup 274 * 275 * @text Listing MainConfiguration shows main application code. 276 * To run a HID Device service you need to initialize the SDP, and to create and register HID Device record with it. 277 * At the end the Bluetooth stack is started. 278 */ 279 280 /* LISTING_START(MainConfiguration): Setup HID Device */ 281 282 int btstack_main(int argc, const char * argv[]); 283 int btstack_main(int argc, const char * argv[]){ 284 (void)argc; 285 (void)argv; 286 287 // allow to get found by inquiry 288 gap_discoverable_control(1); 289 // use Limited Discoverable Mode; Peripheral; Pointing Device as CoD 290 gap_set_class_of_device(0x2580); 291 // set local name to be identified - zeroes will be replaced by actual BD ADDR 292 gap_set_local_name("HID Mouse Demo 00:00:00:00:00:00"); 293 // allow for role switch in general and sniff mode 294 gap_set_default_link_policy_settings( LM_LINK_POLICY_ENABLE_ROLE_SWITCH | LM_LINK_POLICY_ENABLE_SNIFF_MODE ); 295 // allow for role switch on outgoing connections - this allow HID Host to become master when we re-connect to it 296 gap_set_allow_role_switch(true); 297 298 // L2CAP 299 l2cap_init(); 300 301 #ifdef ENABLE_BLE 302 // Initialize LE Security Manager. Needed for cross-transport key derivation 303 sm_init(); 304 #endif 305 306 // SDP Server 307 sdp_init(); 308 309 uint8_t hid_virtual_cable = 0; 310 uint8_t hid_remote_wake = 1; 311 uint8_t hid_reconnect_initiate = 1; 312 uint8_t hid_normally_connectable = 1; 313 314 hid_sdp_record_t hid_params = { 315 // hid sevice subclass 2580 Mouse, hid counntry code 33 US 316 0x2580, 33, 317 hid_virtual_cable, hid_remote_wake, 318 hid_reconnect_initiate, hid_normally_connectable, 319 hid_boot_device, 320 0xFFFF, 0xFFFF, 3200, 321 hid_descriptor_mouse_boot_mode, 322 sizeof(hid_descriptor_mouse_boot_mode), 323 hid_device_name 324 }; 325 326 memset(hid_service_buffer, 0, sizeof(hid_service_buffer)); 327 hid_create_sdp_record(hid_service_buffer, sdp_create_service_record_handle(), &hid_params); 328 btstack_assert(de_get_len( hid_service_buffer) <= sizeof(hid_service_buffer)); 329 sdp_register_service(hid_service_buffer); 330 331 // See https://www.bluetooth.com/specifications/assigned-numbers/company-identifiers if you don't have a USB Vendor ID and need a Bluetooth Vendor ID 332 // device info: BlueKitchen GmbH, product 2, version 1 333 device_id_create_sdp_record(device_id_sdp_service_buffer, sdp_create_service_record_handle(), DEVICE_ID_VENDOR_ID_SOURCE_BLUETOOTH, BLUETOOTH_COMPANY_ID_BLUEKITCHEN_GMBH, 2, 1); 334 btstack_assert(de_get_len( device_id_sdp_service_buffer) <= sizeof(device_id_sdp_service_buffer)); 335 sdp_register_service(device_id_sdp_service_buffer); 336 337 // HID Device 338 hid_device_init(hid_boot_device, sizeof(hid_descriptor_mouse_boot_mode), hid_descriptor_mouse_boot_mode); 339 // register for HCI events 340 hci_event_callback_registration.callback = &packet_handler; 341 hci_add_event_handler(&hci_event_callback_registration); 342 343 // register for HID 344 hid_device_register_packet_handler(&packet_handler); 345 346 #ifdef HAVE_BTSTACK_STDIN 347 btstack_stdin_setup(stdin_process); 348 #endif 349 // turn on! 350 hci_power_control(HCI_POWER_ON); 351 return 0; 352 } 353 /* LISTING_END */ 354 /* EXAMPLE_END */ 355