1 /*
2 * Copyright (C) 2017 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__ "hog_mouse_demo.c"
39
40 // *****************************************************************************
41 /* EXAMPLE_START(hog_mouse_demo): HID Mouse LE
42 */
43 // *****************************************************************************
44
45 #include <stdint.h>
46 #include <stdio.h>
47 #include <stdlib.h>
48 #include <string.h>
49 #include <inttypes.h>
50
51 #include "hog_mouse_demo.h"
52
53 #include "btstack.h"
54
55 #include "ble/gatt-service/battery_service_server.h"
56 #include "ble/gatt-service/device_information_service_server.h"
57 #include "ble/gatt-service/hids_device.h"
58
59 // from USB HID Specification 1.1, Appendix B.2
60 const uint8_t hid_descriptor_mouse_boot_mode[] = {
61 0x05, 0x01, // USAGE_PAGE (Generic Desktop)
62 0x09, 0x02, // USAGE (Mouse)
63 0xa1, 0x01, // COLLECTION (Application)
64
65 0x85, 0x01, // Report ID 1
66
67 0x09, 0x01, // USAGE (Pointer)
68
69 0xa1, 0x00, // COLLECTION (Physical)
70
71 #if 1
72 0x05, 0x09, // USAGE_PAGE (Button)
73 0x19, 0x01, // USAGE_MINIMUM (Button 1)
74 0x29, 0x03, // USAGE_MAXIMUM (Button 3)
75 0x15, 0x00, // LOGICAL_MINIMUM (0)
76 0x25, 0x01, // LOGICAL_MAXIMUM (1)
77 0x95, 0x03, // REPORT_COUNT (3)
78 0x75, 0x01, // REPORT_SIZE (1)
79 0x81, 0x02, // INPUT (Data,Var,Abs)
80 0x95, 0x01, // REPORT_COUNT (1)
81 0x75, 0x05, // REPORT_SIZE (5)
82 0x81, 0x03, // INPUT (Cnst,Var,Abs)
83 #endif
84
85 #if 1
86 0x05, 0x01, // USAGE_PAGE (Generic Desktop)
87 0x09, 0x30, // USAGE (X)
88 0x09, 0x31, // USAGE (Y)
89 0x15, 0x81, // LOGICAL_MINIMUM (-127)
90 0x25, 0x7f, // LOGICAL_MAXIMUM (127)
91 0x75, 0x08, // REPORT_SIZE (8)
92 0x95, 0x02, // REPORT_COUNT (2)
93 0x81, 0x06, // INPUT (Data,Var,Rel)
94 #endif
95
96 0xc0, // END_COLLECTION
97 0xc0 // END_COLLECTION
98 };
99
100 static btstack_packet_callback_registration_t hci_event_callback_registration;
101 static btstack_packet_callback_registration_t l2cap_event_callback_registration;
102 static btstack_packet_callback_registration_t sm_event_callback_registration;
103 static uint8_t battery = 100;
104 static hci_con_handle_t con_handle = HCI_CON_HANDLE_INVALID;
105 static uint8_t protocol_mode = 1;
106
107 static void packet_handler (uint8_t packet_type, uint16_t channel, uint8_t *packet, uint16_t size);
108
109 const uint8_t adv_data[] = {
110 // Flags general discoverable, BR/EDR not supported
111 0x02, BLUETOOTH_DATA_TYPE_FLAGS, 0x06,
112 // Name
113 0x0a, BLUETOOTH_DATA_TYPE_COMPLETE_LOCAL_NAME, 'H', 'I', 'D', ' ', 'M', 'o', 'u', 's', 'e',
114 // 16-bit Service UUIDs
115 0x03, BLUETOOTH_DATA_TYPE_COMPLETE_LIST_OF_16_BIT_SERVICE_CLASS_UUIDS, ORG_BLUETOOTH_SERVICE_HUMAN_INTERFACE_DEVICE & 0xff, ORG_BLUETOOTH_SERVICE_HUMAN_INTERFACE_DEVICE >> 8,
116 // Appearance HID - Mouse (Category 15, Sub-Category 2)
117 0x03, BLUETOOTH_DATA_TYPE_APPEARANCE, 0xC2, 0x03,
118 };
119 const uint8_t adv_data_len = sizeof(adv_data);
120
hog_mouse_setup(void)121 static void hog_mouse_setup(void){
122
123 // setup l2cap and
124 l2cap_init();
125
126 // setup SM: Display only
127 sm_init();
128 sm_set_io_capabilities(IO_CAPABILITY_NO_INPUT_NO_OUTPUT);
129 sm_set_authentication_requirements(SM_AUTHREQ_SECURE_CONNECTION | SM_AUTHREQ_BONDING);
130
131 // setup ATT server
132 att_server_init(profile_data, NULL, NULL);
133
134 // setup battery service
135 battery_service_server_init(battery);
136
137 // setup device information service
138 device_information_service_server_init();
139
140 // setup HID Device service
141 hids_device_init(0, hid_descriptor_mouse_boot_mode, sizeof(hid_descriptor_mouse_boot_mode));
142
143 // setup advertisements
144 uint16_t adv_int_min = 0x0030;
145 uint16_t adv_int_max = 0x0030;
146 uint8_t adv_type = 0;
147 bd_addr_t null_addr;
148 memset(null_addr, 0, 6);
149 gap_advertisements_set_params(adv_int_min, adv_int_max, adv_type, 0, null_addr, 0x07, 0x00);
150 gap_advertisements_set_data(adv_data_len, (uint8_t*) adv_data);
151 gap_advertisements_enable(1);
152
153 // register for events
154 hci_event_callback_registration.callback = &packet_handler;
155 hci_add_event_handler(&hci_event_callback_registration);
156
157 // register for connection parameter updates
158 l2cap_event_callback_registration.callback = &packet_handler;
159 l2cap_add_event_handler(&l2cap_event_callback_registration);
160
161 sm_event_callback_registration.callback = &packet_handler;
162 sm_add_event_handler(&sm_event_callback_registration);
163
164 hids_device_register_packet_handler(packet_handler);
165 }
166
167 // HID Report sending
send_report(uint8_t buttons,int8_t dx,int8_t dy)168 static void send_report(uint8_t buttons, int8_t dx, int8_t dy){
169 uint8_t report[] = { buttons, (uint8_t) dx, (uint8_t) dy };
170 switch (protocol_mode){
171 case 0:
172 hids_device_send_boot_mouse_input_report(con_handle, report, sizeof(report));
173 break;
174 case 1:
175 hids_device_send_input_report(con_handle, report, sizeof(report));
176 break;
177 default:
178 break;
179 }
180 printf("Mouse: %d/%d - buttons: %02x\n", dx, dy, buttons);
181 }
182
183 static int dx;
184 static int dy;
185 static uint8_t buttons;
186
mousing_can_send_now(void)187 static void mousing_can_send_now(void){
188 send_report(buttons, dx, dy);
189 // reset
190 dx = 0;
191 dy = 0;
192 if (buttons){
193 buttons = 0;
194 hids_device_request_can_send_now_event(con_handle);
195 }
196 }
197
198 // Demo Application
199
200 #ifdef HAVE_BTSTACK_STDIN
201
202 static const int MOUSE_SPEED = 30;
203
204 // On systems with STDIN, we can directly type on the console
205
stdin_process(char character)206 static void stdin_process(char character){
207
208 if (con_handle == HCI_CON_HANDLE_INVALID) {
209 printf("Mouse not connected, ignoring '%c'\n", character);
210 return;
211 }
212
213 switch (character){
214 case 'a':
215 dx -= MOUSE_SPEED;
216 break;
217 case 's':
218 dy += MOUSE_SPEED;
219 break;
220 case 'd':
221 dx += MOUSE_SPEED;
222 break;
223 case 'w':
224 dy -= MOUSE_SPEED;
225 break;
226 case 'l':
227 buttons |= 1;
228 break;
229 case 'r':
230 buttons |= 2;
231 break;
232 default:
233 return;
234 }
235 hids_device_request_can_send_now_event(con_handle);
236 }
237
238 #else
239
240 // On embedded systems, simulate clicking on 4 corners of a square
241
242 #define MOUSE_PERIOD_MS 15
243
244 static const int STEPS_PER_DIRECTION = 50;
245 static const int MOUSE_SPEED = 10;
246
247 static btstack_timer_source_t mousing_timer;
248 static int mousing_active = 0;
249 static int step;
250
251 static struct {
252 int dx;
253 int dy;
254 } directions[] = {
255 { 1, 0 },
256 { 0, 1 },
257 { -1, 0 },
258 { 0, -1 },
259 };
260
261
mousing_timer_handler(btstack_timer_source_t * ts)262 static void mousing_timer_handler(btstack_timer_source_t * ts){
263
264 if (con_handle == HCI_CON_HANDLE_INVALID) {
265 mousing_active = 0;
266 return;
267 }
268
269 // simulate left click when corner reached
270 if (step % STEPS_PER_DIRECTION == 0){
271 buttons |= 1;
272 }
273 // simulate move
274 int direction_index = step / STEPS_PER_DIRECTION;
275 dx += directions[direction_index].dx * MOUSE_SPEED;
276 dy += directions[direction_index].dy * MOUSE_SPEED;
277
278 // next
279 step++;
280 if (step >= STEPS_PER_DIRECTION * 4) {
281 step = 0;
282 }
283
284 // trigger send
285 hids_device_request_can_send_now_event(con_handle);
286
287 // set next timer
288 btstack_run_loop_set_timer(ts, MOUSE_PERIOD_MS);
289 btstack_run_loop_add_timer(ts);
290 }
291
hid_embedded_start_mousing(void)292 static void hid_embedded_start_mousing(void){
293 if (mousing_active) return;
294 mousing_active = 1;
295
296 printf("Start mousing..\n");
297
298 step = 0;
299
300 // set one-shot timer
301 mousing_timer.process = &mousing_timer_handler;
302 btstack_run_loop_set_timer(&mousing_timer, MOUSE_PERIOD_MS);
303 btstack_run_loop_add_timer(&mousing_timer);
304 }
305 #endif
306
packet_handler(uint8_t packet_type,uint16_t channel,uint8_t * packet,uint16_t size)307 static void packet_handler (uint8_t packet_type, uint16_t channel, uint8_t *packet, uint16_t size){
308 UNUSED(channel);
309 UNUSED(size);
310 uint16_t conn_interval;
311
312 if (packet_type != HCI_EVENT_PACKET) return;
313
314 switch (hci_event_packet_get_type(packet)) {
315 case HCI_EVENT_DISCONNECTION_COMPLETE:
316 con_handle = HCI_CON_HANDLE_INVALID;
317 printf("Disconnected\n");
318 break;
319 case SM_EVENT_JUST_WORKS_REQUEST:
320 printf("Just Works requested\n");
321 sm_just_works_confirm(sm_event_just_works_request_get_handle(packet));
322 break;
323 case SM_EVENT_NUMERIC_COMPARISON_REQUEST:
324 printf("Confirming numeric comparison: %"PRIu32"\n", sm_event_numeric_comparison_request_get_passkey(packet));
325 sm_numeric_comparison_confirm(sm_event_passkey_display_number_get_handle(packet));
326 break;
327 case SM_EVENT_PASSKEY_DISPLAY_NUMBER:
328 printf("Display Passkey: %"PRIu32"\n", sm_event_passkey_display_number_get_passkey(packet));
329 break;
330 case L2CAP_EVENT_CONNECTION_PARAMETER_UPDATE_RESPONSE:
331 printf("L2CAP Connection Parameter Update Complete, response: %x\n", l2cap_event_connection_parameter_update_response_get_result(packet));
332 break;
333 case HCI_EVENT_META_GAP:
334 switch (hci_event_gap_meta_get_subevent_code(packet)) {
335 case GAP_SUBEVENT_LE_CONNECTION_COMPLETE:
336 // print connection parameters (without using float operations)
337 conn_interval = gap_subevent_le_connection_complete_get_conn_interval(packet);
338 printf("LE Connection Complete:\n");
339 printf("- Connection Interval: %u.%02u ms\n", conn_interval * 125 / 100, 25 * (conn_interval & 3));
340 printf("- Connection Latency: %u\n", gap_subevent_le_connection_complete_get_conn_latency(packet));
341 break;
342 default:
343 break;
344 }
345 break;
346 case HCI_EVENT_LE_META:
347 switch (hci_event_le_meta_get_subevent_code(packet)) {
348 case HCI_SUBEVENT_LE_CONNECTION_UPDATE_COMPLETE:
349 // print connection parameters (without using float operations)
350 conn_interval = hci_subevent_le_connection_update_complete_get_conn_interval(packet);
351 printf("LE Connection Update:\n");
352 printf("- Connection Interval: %u.%02u ms\n", conn_interval * 125 / 100, 25 * (conn_interval & 3));
353 printf("- Connection Latency: %u\n", hci_subevent_le_connection_update_complete_get_conn_latency(packet));
354 break;
355 default:
356 break;
357 }
358 break;
359 case HCI_EVENT_HIDS_META:
360 switch (hci_event_hids_meta_get_subevent_code(packet)){
361 case HIDS_SUBEVENT_INPUT_REPORT_ENABLE:
362 con_handle = hids_subevent_input_report_enable_get_con_handle(packet);
363 printf("Report Characteristic Subscribed %u\n", hids_subevent_input_report_enable_get_enable(packet));
364 #ifndef HAVE_BTSTACK_STDIN
365 hid_embedded_start_mousing();
366 #endif
367 // request connection param update via L2CAP following Apple Bluetooth Design Guidelines
368 // gap_request_connection_parameter_update(con_handle, 12, 12, 4, 100); // 15 ms, 4, 1s
369
370 // directly update connection params via HCI following Apple Bluetooth Design Guidelines
371 // gap_update_connection_parameters(con_handle, 12, 12, 4, 100); // 60-75 ms, 4, 1s
372
373 break;
374 case HIDS_SUBEVENT_BOOT_KEYBOARD_INPUT_REPORT_ENABLE:
375 con_handle = hids_subevent_boot_keyboard_input_report_enable_get_con_handle(packet);
376 printf("Boot Keyboard Characteristic Subscribed %u\n", hids_subevent_boot_keyboard_input_report_enable_get_enable(packet));
377 break;
378 case HIDS_SUBEVENT_BOOT_MOUSE_INPUT_REPORT_ENABLE:
379 con_handle = hids_subevent_boot_mouse_input_report_enable_get_con_handle(packet);
380 printf("Boot Mouse Characteristic Subscribed %u\n", hids_subevent_boot_mouse_input_report_enable_get_enable(packet));
381 break;
382 case HIDS_SUBEVENT_PROTOCOL_MODE:
383 protocol_mode = hids_subevent_protocol_mode_get_protocol_mode(packet);
384 printf("Protocol Mode: %s mode\n", hids_subevent_protocol_mode_get_protocol_mode(packet) ? "Report" : "Boot");
385 break;
386 case HIDS_SUBEVENT_CAN_SEND_NOW:
387 mousing_can_send_now();
388 break;
389 default:
390 break;
391 }
392 break;
393
394 default:
395 break;
396 }
397 }
398
399 int btstack_main(void);
btstack_main(void)400 int btstack_main(void)
401 {
402 hog_mouse_setup();
403
404 #ifdef HAVE_BTSTACK_STDIN
405 btstack_stdin_setup(stdin_process);
406 #endif
407
408 // turn on!
409 hci_power_control(HCI_POWER_ON);
410
411 return 0;
412 }
413