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
send_report(uint8_t buttons,int8_t dx,int8_t dy)106 static void send_report(uint8_t buttons, int8_t dx, int8_t dy){
107 // setup HID message: A1 = Input Report, Report ID, Payload
108 uint8_t message[] = {0xa1, buttons, (uint8_t) dx, (uint8_t) dy};
109 hid_device_send_interrupt_message(hid_cid, &message[0], sizeof(message));
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 static int hid_boot_device = 0;
117
mousing_can_send_now(void)118 static void mousing_can_send_now(void){
119 send_report(buttons, dx, dy);
120 // reset
121 dx = 0;
122 dy = 0;
123 if (buttons){
124 buttons = 0;
125 hid_device_request_can_send_now_event(hid_cid);
126 }
127 }
128
129 // Demo Application
130
131 #ifdef HAVE_BTSTACK_STDIN
132
133 static const int MOUSE_SPEED = 30;
134
135 // On systems with STDIN, we can directly type on the console
136
stdin_process(char character)137 static void stdin_process(char character){
138
139 if (!hid_cid) {
140 printf("Mouse not connected, ignoring '%c'\n", character);
141 return;
142 }
143
144 switch (character){
145 case 'a':
146 dx -= MOUSE_SPEED;
147 break;
148 case 's':
149 dy += MOUSE_SPEED;
150 break;
151 case 'd':
152 dx += MOUSE_SPEED;
153 break;
154 case 'w':
155 dy -= MOUSE_SPEED;
156 break;
157 case 'l':
158 buttons |= 1;
159 break;
160 case 'r':
161 buttons |= 2;
162 break;
163 default:
164 return;
165 }
166 hid_device_request_can_send_now_event(hid_cid);
167 }
168
169 #else
170
171 // On embedded systems, simulate clicking on 4 corners of a square
172
173 #define MOUSE_PERIOD_MS 15
174
175 static int step;
176 static const int STEPS_PER_DIRECTION = 50;
177 static const int MOUSE_SPEED = 10;
178
179 static struct {
180 int dx;
181 int dy;
182 } directions[] = {
183 { 1, 0 },
184 { 0, 1 },
185 { -1, 0 },
186 { 0, -1 },
187 };
188
189 static btstack_timer_source_t mousing_timer;
190
mousing_timer_handler(btstack_timer_source_t * ts)191 static void mousing_timer_handler(btstack_timer_source_t * ts){
192
193 if (!hid_cid) return;
194
195 // simulate left click when corner reached
196 if (step % STEPS_PER_DIRECTION == 0){
197 buttons |= 1;
198 }
199 // simulate move
200 int direction_index = step / STEPS_PER_DIRECTION;
201 dx += directions[direction_index].dx * MOUSE_SPEED;
202 dy += directions[direction_index].dy * MOUSE_SPEED;
203
204 // next
205 step++;
206 if (step >= STEPS_PER_DIRECTION * 4) {
207 step = 0;
208 }
209
210 // trigger send
211 hid_device_request_can_send_now_event(hid_cid);
212
213 // set next timer
214 btstack_run_loop_set_timer(ts, MOUSE_PERIOD_MS);
215 btstack_run_loop_add_timer(ts);
216 }
217
hid_embedded_start_mousing(void)218 static void hid_embedded_start_mousing(void){
219 printf("Start mousing..\n");
220
221 step = 0;
222
223 // set one-shot timer
224 mousing_timer.process = &mousing_timer_handler;
225 btstack_run_loop_set_timer(&mousing_timer, MOUSE_PERIOD_MS);
226 btstack_run_loop_add_timer(&mousing_timer);
227 }
228 #endif
229
packet_handler(uint8_t packet_type,uint16_t channel,uint8_t * packet,uint16_t packet_size)230 static void packet_handler(uint8_t packet_type, uint16_t channel, uint8_t * packet, uint16_t packet_size){
231 UNUSED(channel);
232 UNUSED(packet_size);
233 switch (packet_type){
234 case HCI_EVENT_PACKET:
235 switch (hci_event_packet_get_type(packet)){
236 case HCI_EVENT_USER_CONFIRMATION_REQUEST:
237 // ssp: inform about user confirmation request
238 log_info("SSP User Confirmation Request with numeric value '%06"PRIu32"'\n", hci_event_user_confirmation_request_get_numeric_value(packet));
239 log_info("SSP User Confirmation Auto accept\n");
240 break;
241
242 case HCI_EVENT_HID_META:
243 switch (hci_event_hid_meta_get_subevent_code(packet)){
244 case HID_SUBEVENT_CONNECTION_OPENED:
245 if (hid_subevent_connection_opened_get_status(packet) != ERROR_CODE_SUCCESS) return;
246 hid_cid = hid_subevent_connection_opened_get_hid_cid(packet);
247 #ifdef HAVE_BTSTACK_STDIN
248 printf("HID Connected, control mouse using 'a','s',''d', 'w' keys for movement and 'l' and 'r' for buttons...\n");
249 #else
250 printf("HID Connected, simulating mouse movements...\n");
251 hid_embedded_start_mousing();
252 #endif
253 break;
254 case HID_SUBEVENT_CONNECTION_CLOSED:
255 printf("HID Disconnected\n");
256 hid_cid = 0;
257 break;
258 case HID_SUBEVENT_CAN_SEND_NOW:
259 mousing_can_send_now();
260 break;
261 default:
262 break;
263 }
264 break;
265 default:
266 break;
267 }
268 break;
269 default:
270 break;
271 }
272 }
273
274 /* @section Main Application Setup
275 *
276 * @text Listing MainConfiguration shows main application code.
277 * To run a HID Device service you need to initialize the SDP, and to create and register HID Device record with it.
278 * At the end the Bluetooth stack is started.
279 */
280
281 /* LISTING_START(MainConfiguration): Setup HID Device */
282
283 int btstack_main(int argc, const char * argv[]);
btstack_main(int argc,const char * argv[])284 int btstack_main(int argc, const char * argv[]){
285 (void)argc;
286 (void)argv;
287
288 // allow to get found by inquiry
289 gap_discoverable_control(1);
290 // use Limited Discoverable Mode; Peripheral; Pointing Device as CoD
291 gap_set_class_of_device(0x2580);
292 // set local name to be identified - zeroes will be replaced by actual BD ADDR
293 gap_set_local_name("HID Mouse Demo 00:00:00:00:00:00");
294 // allow for role switch in general and sniff mode
295 gap_set_default_link_policy_settings( LM_LINK_POLICY_ENABLE_ROLE_SWITCH | LM_LINK_POLICY_ENABLE_SNIFF_MODE );
296 // allow for role switch on outgoing connections - this allow HID Host to become master when we re-connect to it
297 gap_set_allow_role_switch(true);
298
299 // L2CAP
300 l2cap_init();
301
302 #ifdef ENABLE_BLE
303 // Initialize LE Security Manager. Needed for cross-transport key derivation
304 sm_init();
305 #endif
306
307 // SDP Server
308 sdp_init();
309
310 uint8_t hid_virtual_cable = 0;
311 uint8_t hid_remote_wake = 1;
312 uint8_t hid_reconnect_initiate = 1;
313 uint8_t hid_normally_connectable = 1;
314
315 hid_sdp_record_t hid_params = {
316 // hid sevice subclass 2580 Mouse, hid counntry code 33 US
317 0x2580, 33,
318 hid_virtual_cable, hid_remote_wake,
319 hid_reconnect_initiate, hid_normally_connectable,
320 hid_boot_device,
321 0xFFFF, 0xFFFF, 3200,
322 hid_descriptor_mouse_boot_mode,
323 sizeof(hid_descriptor_mouse_boot_mode),
324 hid_device_name
325 };
326
327 memset(hid_service_buffer, 0, sizeof(hid_service_buffer));
328 hid_create_sdp_record(hid_service_buffer, sdp_create_service_record_handle(), &hid_params);
329 btstack_assert(de_get_len( hid_service_buffer) <= sizeof(hid_service_buffer));
330 sdp_register_service(hid_service_buffer);
331
332 // 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
333 // device info: BlueKitchen GmbH, product 2, version 1
334 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);
335 btstack_assert(de_get_len( device_id_sdp_service_buffer) <= sizeof(device_id_sdp_service_buffer));
336 sdp_register_service(device_id_sdp_service_buffer);
337
338 // HID Device
339 hid_device_init(hid_boot_device, sizeof(hid_descriptor_mouse_boot_mode), hid_descriptor_mouse_boot_mode);
340 // register for HCI events
341 hci_event_callback_registration.callback = &packet_handler;
342 hci_add_event_handler(&hci_event_callback_registration);
343
344 // register for HID
345 hid_device_register_packet_handler(&packet_handler);
346
347 #ifdef HAVE_BTSTACK_STDIN
348 btstack_stdin_setup(stdin_process);
349 #endif
350 // turn on!
351 hci_power_control(HCI_POWER_ON);
352 return 0;
353 }
354 /* LISTING_END */
355 /* EXAMPLE_END */
356