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__ "hid_mouse_demo.c" 39 40 // ***************************************************************************** 41 /* EXAMPLE_START(hid_mouse_demo): HID Mouse (Server) Demo 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 * @text Status: Basic implementation. HID Request from Host are not answered yet. Works with iOS. 48 */ 49 // ***************************************************************************** 50 51 52 #include <stdint.h> 53 #include <stdio.h> 54 #include <stdlib.h> 55 #include <string.h> 56 #include <inttypes.h> 57 58 #include "btstack.h" 59 60 #ifdef HAVE_BTSTACK_STDIN 61 #include "btstack_stdin.h" 62 #endif 63 64 // to enable demo text on POSIX systems 65 // #undef HAVE_BTSTACK_STDIN 66 67 static uint8_t hid_service_buffer[250]; 68 static const char hid_device_name[] = "BTstack HID Mouse"; 69 static btstack_packet_callback_registration_t hci_event_callback_registration; 70 static uint16_t hid_cid; 71 72 // from USB HID Specification 1.1, Appendix B.2 73 const uint8_t hid_descriptor_mouse_boot_mode[] = { 74 0x05, 0x01, // USAGE_PAGE (Generic Desktop) 75 0x09, 0x02, // USAGE (Mouse) 76 0xa1, 0x01, // COLLECTION (Application) 77 78 0x09, 0x01, // USAGE (Pointer) 79 0xa1, 0x00, // COLLECTION (Physical) 80 81 0x05, 0x09, // USAGE_PAGE (Button) 82 0x19, 0x01, // USAGE_MINIMUM (Button 1) 83 0x29, 0x03, // USAGE_MAXIMUM (Button 3) 84 0x15, 0x00, // LOGICAL_MINIMUM (0) 85 0x25, 0x01, // LOGICAL_MAXIMUM (1) 86 0x95, 0x03, // REPORT_COUNT (3) 87 0x75, 0x01, // REPORT_SIZE (1) 88 0x81, 0x02, // INPUT (Data,Var,Abs) 89 0x95, 0x01, // REPORT_COUNT (1) 90 0x75, 0x05, // REPORT_SIZE (5) 91 0x81, 0x03, // INPUT (Cnst,Var,Abs) 92 93 0x05, 0x01, // USAGE_PAGE (Generic Desktop) 94 0x09, 0x30, // USAGE (X) 95 0x09, 0x31, // USAGE (Y) 96 0x15, 0x81, // LOGICAL_MINIMUM (-127) 97 0x25, 0x7f, // LOGICAL_MAXIMUM (127) 98 0x75, 0x08, // REPORT_SIZE (8) 99 0x95, 0x02, // REPORT_COUNT (2) 100 0x81, 0x06, // INPUT (Data,Var,Rel) 101 102 0xc0, // END_COLLECTION 103 0xc0 // END_COLLECTION 104 }; 105 106 // HID Report sending 107 static void send_report(uint8_t buttons, int8_t dx, int8_t dy){ 108 uint8_t report[] = { 0xa1, buttons, (uint8_t) dx, (uint8_t) dy}; 109 hid_device_send_interrupt_message(hid_cid, &report[0], sizeof(report)); 110 printf("Mouse: %d/%d - buttons: %02x\n", dx, dy, buttons); 111 } 112 113 static int dx; 114 static int dy; 115 static uint8_t buttons; 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 (packet[0]){ 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)) 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 gap_discoverable_control(1); 288 gap_set_class_of_device(0x2540); 289 gap_set_local_name("HID Mouse Demo 00:00:00:00:00:00"); 290 291 // L2CAP 292 l2cap_init(); 293 294 // SDP Server 295 sdp_init(); 296 memset(hid_service_buffer, 0, sizeof(hid_service_buffer)); 297 // hid sevice subclass 2540 Keyboard, hid counntry code 33 US, hid virtual cable off, hid reconnect initiate off, hid boot device off 298 hid_create_sdp_record(hid_service_buffer, 0x10001, 0x2540, 33, 0, 0, 0, hid_descriptor_mouse_boot_mode, sizeof(hid_descriptor_mouse_boot_mode), hid_device_name); 299 printf("SDP service record size: %u\n", de_get_len( hid_service_buffer)); 300 sdp_register_service(hid_service_buffer); 301 302 // HID Device 303 hid_device_init(); 304 305 // register for HCI events 306 hci_event_callback_registration.callback = &packet_handler; 307 hci_add_event_handler(&hci_event_callback_registration); 308 309 // register for HID 310 hid_device_register_packet_handler(&packet_handler); 311 312 #ifdef HAVE_BTSTACK_STDIN 313 btstack_stdin_setup(stdin_process); 314 #endif 315 // turn on! 316 hci_power_control(HCI_POWER_ON); 317 return 0; 318 } 319 /* LISTING_END */ 320 /* EXAMPLE_END */ 321