hid_mouse_demo.c 11 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327
  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. * contact@bluekitchen-gmbh.com
  35. *
  36. */
  37. #define BTSTACK_FILE__ "hid_mouse_demo.c"
  38. // *****************************************************************************
  39. /* EXAMPLE_START(hid_mouse_demo): HID Mouse (Server) Demo
  40. *
  41. * @text This HID Device example demonstrates how to implement
  42. * an HID keyboard. Without a HAVE_BTSTACK_STDIN, a fixed demo text is sent
  43. * If HAVE_BTSTACK_STDIN is defined, you can type from the terminal
  44. *
  45. * @text Status: Basic implementation. HID Request from Host are not answered yet. Works with iOS.
  46. */
  47. // *****************************************************************************
  48. #include <stdint.h>
  49. #include <stdio.h>
  50. #include <stdlib.h>
  51. #include <string.h>
  52. #include <inttypes.h>
  53. #include "btstack.h"
  54. #ifdef HAVE_BTSTACK_STDIN
  55. #include "btstack_stdin.h"
  56. #endif
  57. // to enable demo text on POSIX systems
  58. // #undef HAVE_BTSTACK_STDIN
  59. static uint8_t hid_service_buffer[250];
  60. static const char hid_device_name[] = "BTstack HID Mouse";
  61. static btstack_packet_callback_registration_t hci_event_callback_registration;
  62. static uint16_t hid_cid;
  63. // from USB HID Specification 1.1, Appendix B.2
  64. const uint8_t hid_descriptor_mouse_boot_mode[] = {
  65. 0x05, 0x01, // USAGE_PAGE (Generic Desktop)
  66. 0x09, 0x02, // USAGE (Mouse)
  67. 0xa1, 0x01, // COLLECTION (Application)
  68. 0x09, 0x01, // USAGE (Pointer)
  69. 0xa1, 0x00, // COLLECTION (Physical)
  70. 0x05, 0x09, // USAGE_PAGE (Button)
  71. 0x19, 0x01, // USAGE_MINIMUM (Button 1)
  72. 0x29, 0x03, // USAGE_MAXIMUM (Button 3)
  73. 0x15, 0x00, // LOGICAL_MINIMUM (0)
  74. 0x25, 0x01, // LOGICAL_MAXIMUM (1)
  75. 0x95, 0x03, // REPORT_COUNT (3)
  76. 0x75, 0x01, // REPORT_SIZE (1)
  77. 0x81, 0x02, // INPUT (Data,Var,Abs)
  78. 0x95, 0x01, // REPORT_COUNT (1)
  79. 0x75, 0x05, // REPORT_SIZE (5)
  80. 0x81, 0x03, // INPUT (Cnst,Var,Abs)
  81. 0x05, 0x01, // USAGE_PAGE (Generic Desktop)
  82. 0x09, 0x30, // USAGE (X)
  83. 0x09, 0x31, // USAGE (Y)
  84. 0x15, 0x81, // LOGICAL_MINIMUM (-127)
  85. 0x25, 0x7f, // LOGICAL_MAXIMUM (127)
  86. 0x75, 0x08, // REPORT_SIZE (8)
  87. 0x95, 0x02, // REPORT_COUNT (2)
  88. 0x81, 0x06, // INPUT (Data,Var,Rel)
  89. 0xc0, // END_COLLECTION
  90. 0xc0 // END_COLLECTION
  91. };
  92. // HID Report sending
  93. static void send_report(uint8_t buttons, int8_t dx, int8_t dy){
  94. uint8_t report[] = { 0xa1, buttons, (uint8_t) dx, (uint8_t) dy};
  95. hid_device_send_interrupt_message(hid_cid, &report[0], sizeof(report));
  96. printf("Mouse: %d/%d - buttons: %02x\n", dx, dy, buttons);
  97. }
  98. static int dx;
  99. static int dy;
  100. static uint8_t buttons;
  101. static int hid_boot_device = 0;
  102. static void mousing_can_send_now(void){
  103. send_report(buttons, dx, dy);
  104. // reset
  105. dx = 0;
  106. dy = 0;
  107. if (buttons){
  108. buttons = 0;
  109. hid_device_request_can_send_now_event(hid_cid);
  110. }
  111. }
  112. // Demo Application
  113. #ifdef HAVE_BTSTACK_STDIN
  114. static const int MOUSE_SPEED = 30;
  115. // On systems with STDIN, we can directly type on the console
  116. static void stdin_process(char character){
  117. if (!hid_cid) {
  118. printf("Mouse not connected, ignoring '%c'\n", character);
  119. return;
  120. }
  121. switch (character){
  122. case 'a':
  123. dx -= MOUSE_SPEED;
  124. break;
  125. case 's':
  126. dy += MOUSE_SPEED;
  127. break;
  128. case 'd':
  129. dx += MOUSE_SPEED;
  130. break;
  131. case 'w':
  132. dy -= MOUSE_SPEED;
  133. break;
  134. case 'l':
  135. buttons |= 1;
  136. break;
  137. case 'r':
  138. buttons |= 2;
  139. break;
  140. default:
  141. return;
  142. }
  143. hid_device_request_can_send_now_event(hid_cid);
  144. }
  145. #else
  146. // On embedded systems, simulate clicking on 4 corners of a square
  147. #define MOUSE_PERIOD_MS 15
  148. static int step;
  149. static const int STEPS_PER_DIRECTION = 50;
  150. static const int MOUSE_SPEED = 10;
  151. static struct {
  152. int dx;
  153. int dy;
  154. } directions[] = {
  155. { 1, 0 },
  156. { 0, 1 },
  157. { -1, 0 },
  158. { 0, -1 },
  159. };
  160. static btstack_timer_source_t mousing_timer;
  161. static void mousing_timer_handler(btstack_timer_source_t * ts){
  162. if (!hid_cid) return;
  163. // simulate left click when corner reached
  164. if (step % STEPS_PER_DIRECTION == 0){
  165. buttons |= 1;
  166. }
  167. // simulate move
  168. int direction_index = step / STEPS_PER_DIRECTION;
  169. dx += directions[direction_index].dx * MOUSE_SPEED;
  170. dy += directions[direction_index].dy * MOUSE_SPEED;
  171. // next
  172. step++;
  173. if (step >= STEPS_PER_DIRECTION * 4) {
  174. step = 0;
  175. }
  176. // trigger send
  177. hid_device_request_can_send_now_event(hid_cid);
  178. // set next timer
  179. btstack_run_loop_set_timer(ts, MOUSE_PERIOD_MS);
  180. btstack_run_loop_add_timer(ts);
  181. }
  182. static void hid_embedded_start_mousing(void){
  183. printf("Start mousing..\n");
  184. step = 0;
  185. // set one-shot timer
  186. mousing_timer.process = &mousing_timer_handler;
  187. btstack_run_loop_set_timer(&mousing_timer, MOUSE_PERIOD_MS);
  188. btstack_run_loop_add_timer(&mousing_timer);
  189. }
  190. #endif
  191. static void packet_handler(uint8_t packet_type, uint16_t channel, uint8_t * packet, uint16_t packet_size){
  192. UNUSED(channel);
  193. UNUSED(packet_size);
  194. switch (packet_type){
  195. case HCI_EVENT_PACKET:
  196. switch (packet[0]){
  197. case HCI_EVENT_USER_CONFIRMATION_REQUEST:
  198. // ssp: inform about user confirmation request
  199. log_info("SSP User Confirmation Request with numeric value '%06"PRIu32"'\n", hci_event_user_confirmation_request_get_numeric_value(packet));
  200. log_info("SSP User Confirmation Auto accept\n");
  201. break;
  202. case HCI_EVENT_HID_META:
  203. switch (hci_event_hid_meta_get_subevent_code(packet)){
  204. case HID_SUBEVENT_CONNECTION_OPENED:
  205. if (hid_subevent_connection_opened_get_status(packet)) return;
  206. hid_cid = hid_subevent_connection_opened_get_hid_cid(packet);
  207. #ifdef HAVE_BTSTACK_STDIN
  208. printf("HID Connected, control mouse using 'a','s',''d', 'w' keys for movement and 'l' and 'r' for buttons...\n");
  209. #else
  210. printf("HID Connected, simulating mouse movements...\n");
  211. hid_embedded_start_mousing();
  212. #endif
  213. break;
  214. case HID_SUBEVENT_CONNECTION_CLOSED:
  215. printf("HID Disconnected\n");
  216. hid_cid = 0;
  217. break;
  218. case HID_SUBEVENT_CAN_SEND_NOW:
  219. mousing_can_send_now();
  220. break;
  221. default:
  222. break;
  223. }
  224. break;
  225. default:
  226. break;
  227. }
  228. break;
  229. default:
  230. break;
  231. }
  232. }
  233. /* @section Main Application Setup
  234. *
  235. * @text Listing MainConfiguration shows main application code.
  236. * To run a HID Device service you need to initialize the SDP, and to create and register HID Device record with it.
  237. * At the end the Bluetooth stack is started.
  238. */
  239. /* LISTING_START(MainConfiguration): Setup HID Device */
  240. int btstack_main(int argc, const char * argv[]);
  241. int btstack_main(int argc, const char * argv[]){
  242. (void)argc;
  243. (void)argv;
  244. // allow to get found by inquiry
  245. gap_discoverable_control(1);
  246. // use Limited Discoverable Mode; Peripheral; Pointing Device as CoD
  247. gap_set_class_of_device(0x2580);
  248. // set local name to be identified - zeroes will be replaced by actual BD ADDR
  249. gap_set_local_name("HID Mouse Demo 00:00:00:00:00:00");
  250. // allow for role switch in general and sniff mode
  251. gap_set_default_link_policy_settings( LM_LINK_POLICY_ENABLE_ROLE_SWITCH | LM_LINK_POLICY_ENABLE_SNIFF_MODE );
  252. // allow for role switch on outgoing connections - this allow HID Host to become master when we re-connect to it
  253. gap_set_allow_role_switch(true);
  254. // L2CAP
  255. l2cap_init();
  256. // SDP Server
  257. sdp_init();
  258. memset(hid_service_buffer, 0, sizeof(hid_service_buffer));
  259. // hid sevice subclass 2540 Keyboard, hid counntry code 33 US, hid virtual cable off, hid reconnect initiate off, hid boot device off
  260. hid_create_sdp_record(hid_service_buffer, 0x10001, 0x2540, 33, 0, 0, hid_boot_device, hid_descriptor_mouse_boot_mode, sizeof(hid_descriptor_mouse_boot_mode), hid_device_name);
  261. printf("SDP service record size: %u\n", de_get_len( hid_service_buffer));
  262. sdp_register_service(hid_service_buffer);
  263. // HID Device
  264. hid_device_init(hid_boot_device, sizeof(hid_descriptor_mouse_boot_mode), hid_descriptor_mouse_boot_mode);
  265. // register for HCI events
  266. hci_event_callback_registration.callback = &packet_handler;
  267. hci_add_event_handler(&hci_event_callback_registration);
  268. // register for HID
  269. hid_device_register_packet_handler(&packet_handler);
  270. #ifdef HAVE_BTSTACK_STDIN
  271. btstack_stdin_setup(stdin_process);
  272. #endif
  273. // turn on!
  274. hci_power_control(HCI_POWER_ON);
  275. return 0;
  276. }
  277. /* LISTING_END */
  278. /* EXAMPLE_END */