|
1 /* |
|
2 * QEMU Bluetooth HID Profile wrapper for USB HID. |
|
3 * |
|
4 * Copyright (C) 2007-2008 OpenMoko, Inc. |
|
5 * Written by Andrzej Zaborowski <andrew@openedhand.com> |
|
6 * |
|
7 * This program is free software; you can redistribute it and/or |
|
8 * modify it under the terms of the GNU General Public License as |
|
9 * published by the Free Software Foundation; either version 2 or |
|
10 * (at your option) version 3 of the License. |
|
11 * |
|
12 * This program is distributed in the hope that it will be useful, |
|
13 * but WITHOUT ANY WARRANTY; without even the implied warranty of |
|
14 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
|
15 * GNU General Public License for more details. |
|
16 * |
|
17 * You should have received a copy of the GNU General Public License |
|
18 * along with this program; if not, write to the Free Software |
|
19 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, |
|
20 * MA 02111-1307 USA |
|
21 */ |
|
22 |
|
23 #include "qemu-common.h" |
|
24 #include "usb.h" |
|
25 #include "bt.h" |
|
26 |
|
27 enum hid_transaction_req { |
|
28 BT_HANDSHAKE = 0x0, |
|
29 BT_HID_CONTROL = 0x1, |
|
30 BT_GET_REPORT = 0x4, |
|
31 BT_SET_REPORT = 0x5, |
|
32 BT_GET_PROTOCOL = 0x6, |
|
33 BT_SET_PROTOCOL = 0x7, |
|
34 BT_GET_IDLE = 0x8, |
|
35 BT_SET_IDLE = 0x9, |
|
36 BT_DATA = 0xa, |
|
37 BT_DATC = 0xb, |
|
38 }; |
|
39 |
|
40 enum hid_transaction_handshake { |
|
41 BT_HS_SUCCESSFUL = 0x0, |
|
42 BT_HS_NOT_READY = 0x1, |
|
43 BT_HS_ERR_INVALID_REPORT_ID = 0x2, |
|
44 BT_HS_ERR_UNSUPPORTED_REQUEST = 0x3, |
|
45 BT_HS_ERR_INVALID_PARAMETER = 0x4, |
|
46 BT_HS_ERR_UNKNOWN = 0xe, |
|
47 BT_HS_ERR_FATAL = 0xf, |
|
48 }; |
|
49 |
|
50 enum hid_transaction_control { |
|
51 BT_HC_NOP = 0x0, |
|
52 BT_HC_HARD_RESET = 0x1, |
|
53 BT_HC_SOFT_RESET = 0x2, |
|
54 BT_HC_SUSPEND = 0x3, |
|
55 BT_HC_EXIT_SUSPEND = 0x4, |
|
56 BT_HC_VIRTUAL_CABLE_UNPLUG = 0x5, |
|
57 }; |
|
58 |
|
59 enum hid_protocol { |
|
60 BT_HID_PROTO_BOOT = 0, |
|
61 BT_HID_PROTO_REPORT = 1, |
|
62 }; |
|
63 |
|
64 enum hid_boot_reportid { |
|
65 BT_HID_BOOT_INVALID = 0, |
|
66 BT_HID_BOOT_KEYBOARD, |
|
67 BT_HID_BOOT_MOUSE, |
|
68 }; |
|
69 |
|
70 enum hid_data_pkt { |
|
71 BT_DATA_OTHER = 0, |
|
72 BT_DATA_INPUT, |
|
73 BT_DATA_OUTPUT, |
|
74 BT_DATA_FEATURE, |
|
75 }; |
|
76 |
|
77 #define BT_HID_MTU 48 |
|
78 |
|
79 /* HID interface requests */ |
|
80 #define GET_REPORT 0xa101 |
|
81 #define GET_IDLE 0xa102 |
|
82 #define GET_PROTOCOL 0xa103 |
|
83 #define SET_REPORT 0x2109 |
|
84 #define SET_IDLE 0x210a |
|
85 #define SET_PROTOCOL 0x210b |
|
86 |
|
87 struct bt_hid_device_s { |
|
88 struct bt_l2cap_device_s btdev; |
|
89 struct bt_l2cap_conn_params_s *control; |
|
90 struct bt_l2cap_conn_params_s *interrupt; |
|
91 USBDevice *usbdev; |
|
92 |
|
93 int proto; |
|
94 int connected; |
|
95 int data_type; |
|
96 int intr_state; |
|
97 struct { |
|
98 int len; |
|
99 uint8_t buffer[1024]; |
|
100 } dataother, datain, dataout, feature, intrdataout; |
|
101 enum { |
|
102 bt_state_ready, |
|
103 bt_state_transaction, |
|
104 bt_state_suspend, |
|
105 } state; |
|
106 }; |
|
107 |
|
108 static void bt_hid_reset(struct bt_hid_device_s *s) |
|
109 { |
|
110 struct bt_scatternet_s *net = s->btdev.device.net; |
|
111 |
|
112 /* Go as far as... */ |
|
113 bt_l2cap_device_done(&s->btdev); |
|
114 bt_l2cap_device_init(&s->btdev, net); |
|
115 |
|
116 s->usbdev->handle_reset(s->usbdev); |
|
117 s->proto = BT_HID_PROTO_REPORT; |
|
118 s->state = bt_state_ready; |
|
119 s->dataother.len = 0; |
|
120 s->datain.len = 0; |
|
121 s->dataout.len = 0; |
|
122 s->feature.len = 0; |
|
123 s->intrdataout.len = 0; |
|
124 s->intr_state = 0; |
|
125 } |
|
126 |
|
127 static int bt_hid_out(struct bt_hid_device_s *s) |
|
128 { |
|
129 USBPacket p; |
|
130 |
|
131 if (s->data_type == BT_DATA_OUTPUT) { |
|
132 p.pid = USB_TOKEN_OUT; |
|
133 p.devep = 1; |
|
134 p.data = s->dataout.buffer; |
|
135 p.len = s->dataout.len; |
|
136 s->dataout.len = s->usbdev->handle_data(s->usbdev, &p); |
|
137 |
|
138 return s->dataout.len; |
|
139 } |
|
140 |
|
141 if (s->data_type == BT_DATA_FEATURE) { |
|
142 /* XXX: |
|
143 * does this send a USB_REQ_CLEAR_FEATURE/USB_REQ_SET_FEATURE |
|
144 * or a SET_REPORT? */ |
|
145 p.devep = 0; |
|
146 } |
|
147 |
|
148 return -1; |
|
149 } |
|
150 |
|
151 static int bt_hid_in(struct bt_hid_device_s *s) |
|
152 { |
|
153 USBPacket p; |
|
154 |
|
155 p.pid = USB_TOKEN_IN; |
|
156 p.devep = 1; |
|
157 p.data = s->datain.buffer; |
|
158 p.len = sizeof(s->datain.buffer); |
|
159 s->datain.len = s->usbdev->handle_data(s->usbdev, &p); |
|
160 |
|
161 return s->datain.len; |
|
162 } |
|
163 |
|
164 static void bt_hid_send_handshake(struct bt_hid_device_s *s, int result) |
|
165 { |
|
166 *s->control->sdu_out(s->control, 1) = |
|
167 (BT_HANDSHAKE << 4) | result; |
|
168 s->control->sdu_submit(s->control); |
|
169 } |
|
170 |
|
171 static void bt_hid_send_control(struct bt_hid_device_s *s, int operation) |
|
172 { |
|
173 *s->control->sdu_out(s->control, 1) = |
|
174 (BT_HID_CONTROL << 4) | operation; |
|
175 s->control->sdu_submit(s->control); |
|
176 } |
|
177 |
|
178 static void bt_hid_disconnect(struct bt_hid_device_s *s) |
|
179 { |
|
180 /* Disconnect s->control and s->interrupt */ |
|
181 } |
|
182 |
|
183 static void bt_hid_send_data(struct bt_l2cap_conn_params_s *ch, int type, |
|
184 const uint8_t *data, int len) |
|
185 { |
|
186 uint8_t *pkt, hdr = (BT_DATA << 4) | type; |
|
187 int plen; |
|
188 |
|
189 do { |
|
190 plen = MIN(len, ch->remote_mtu - 1); |
|
191 pkt = ch->sdu_out(ch, plen + 1); |
|
192 |
|
193 pkt[0] = hdr; |
|
194 if (plen) |
|
195 memcpy(pkt + 1, data, plen); |
|
196 ch->sdu_submit(ch); |
|
197 |
|
198 len -= plen; |
|
199 data += plen; |
|
200 hdr = (BT_DATC << 4) | type; |
|
201 } while (plen == ch->remote_mtu - 1); |
|
202 } |
|
203 |
|
204 static void bt_hid_control_transaction(struct bt_hid_device_s *s, |
|
205 const uint8_t *data, int len) |
|
206 { |
|
207 uint8_t type, parameter; |
|
208 int rlen, ret = -1; |
|
209 if (len < 1) |
|
210 return; |
|
211 |
|
212 type = data[0] >> 4; |
|
213 parameter = data[0] & 0xf; |
|
214 |
|
215 switch (type) { |
|
216 case BT_HANDSHAKE: |
|
217 case BT_DATA: |
|
218 switch (parameter) { |
|
219 default: |
|
220 /* These are not expected to be sent this direction. */ |
|
221 ret = BT_HS_ERR_INVALID_PARAMETER; |
|
222 } |
|
223 break; |
|
224 |
|
225 case BT_HID_CONTROL: |
|
226 if (len != 1 || (parameter != BT_HC_VIRTUAL_CABLE_UNPLUG && |
|
227 s->state == bt_state_transaction)) { |
|
228 ret = BT_HS_ERR_INVALID_PARAMETER; |
|
229 break; |
|
230 } |
|
231 switch (parameter) { |
|
232 case BT_HC_NOP: |
|
233 break; |
|
234 case BT_HC_HARD_RESET: |
|
235 case BT_HC_SOFT_RESET: |
|
236 bt_hid_reset(s); |
|
237 break; |
|
238 case BT_HC_SUSPEND: |
|
239 if (s->state == bt_state_ready) |
|
240 s->state = bt_state_suspend; |
|
241 else |
|
242 ret = BT_HS_ERR_INVALID_PARAMETER; |
|
243 break; |
|
244 case BT_HC_EXIT_SUSPEND: |
|
245 if (s->state == bt_state_suspend) |
|
246 s->state = bt_state_ready; |
|
247 else |
|
248 ret = BT_HS_ERR_INVALID_PARAMETER; |
|
249 break; |
|
250 case BT_HC_VIRTUAL_CABLE_UNPLUG: |
|
251 bt_hid_disconnect(s); |
|
252 break; |
|
253 default: |
|
254 ret = BT_HS_ERR_INVALID_PARAMETER; |
|
255 } |
|
256 break; |
|
257 |
|
258 case BT_GET_REPORT: |
|
259 /* No ReportIDs declared. */ |
|
260 if (((parameter & 8) && len != 3) || |
|
261 (!(parameter & 8) && len != 1) || |
|
262 s->state != bt_state_ready) { |
|
263 ret = BT_HS_ERR_INVALID_PARAMETER; |
|
264 break; |
|
265 } |
|
266 if (parameter & 8) |
|
267 rlen = data[2] | (data[3] << 8); |
|
268 else |
|
269 rlen = INT_MAX; |
|
270 switch (parameter & 3) { |
|
271 case BT_DATA_OTHER: |
|
272 ret = BT_HS_ERR_INVALID_PARAMETER; |
|
273 break; |
|
274 case BT_DATA_INPUT: |
|
275 /* Here we can as well poll s->usbdev */ |
|
276 bt_hid_send_data(s->control, BT_DATA_INPUT, |
|
277 s->datain.buffer, MIN(rlen, s->datain.len)); |
|
278 break; |
|
279 case BT_DATA_OUTPUT: |
|
280 bt_hid_send_data(s->control, BT_DATA_OUTPUT, |
|
281 s->dataout.buffer, MIN(rlen, s->dataout.len)); |
|
282 break; |
|
283 case BT_DATA_FEATURE: |
|
284 bt_hid_send_data(s->control, BT_DATA_FEATURE, |
|
285 s->feature.buffer, MIN(rlen, s->feature.len)); |
|
286 break; |
|
287 } |
|
288 break; |
|
289 |
|
290 case BT_SET_REPORT: |
|
291 if (len < 2 || len > BT_HID_MTU || s->state != bt_state_ready || |
|
292 (parameter & 3) == BT_DATA_OTHER || |
|
293 (parameter & 3) == BT_DATA_INPUT) { |
|
294 ret = BT_HS_ERR_INVALID_PARAMETER; |
|
295 break; |
|
296 } |
|
297 s->data_type = parameter & 3; |
|
298 if (s->data_type == BT_DATA_OUTPUT) { |
|
299 s->dataout.len = len - 1; |
|
300 memcpy(s->dataout.buffer, data + 1, s->dataout.len); |
|
301 } else { |
|
302 s->feature.len = len - 1; |
|
303 memcpy(s->feature.buffer, data + 1, s->feature.len); |
|
304 } |
|
305 if (len == BT_HID_MTU) |
|
306 s->state = bt_state_transaction; |
|
307 else |
|
308 bt_hid_out(s); |
|
309 break; |
|
310 |
|
311 case BT_GET_PROTOCOL: |
|
312 if (len != 1 || s->state == bt_state_transaction) { |
|
313 ret = BT_HS_ERR_INVALID_PARAMETER; |
|
314 break; |
|
315 } |
|
316 *s->control->sdu_out(s->control, 1) = s->proto; |
|
317 s->control->sdu_submit(s->control); |
|
318 break; |
|
319 |
|
320 case BT_SET_PROTOCOL: |
|
321 if (len != 1 || s->state == bt_state_transaction || |
|
322 (parameter != BT_HID_PROTO_BOOT && |
|
323 parameter != BT_HID_PROTO_REPORT)) { |
|
324 ret = BT_HS_ERR_INVALID_PARAMETER; |
|
325 break; |
|
326 } |
|
327 s->proto = parameter; |
|
328 s->usbdev->handle_control(s->usbdev, SET_PROTOCOL, s->proto, 0, 0, 0); |
|
329 ret = BT_HS_SUCCESSFUL; |
|
330 break; |
|
331 |
|
332 case BT_GET_IDLE: |
|
333 if (len != 1 || s->state == bt_state_transaction) { |
|
334 ret = BT_HS_ERR_INVALID_PARAMETER; |
|
335 break; |
|
336 } |
|
337 s->usbdev->handle_control(s->usbdev, GET_IDLE, 0, 0, 1, |
|
338 s->control->sdu_out(s->control, 1)); |
|
339 s->control->sdu_submit(s->control); |
|
340 break; |
|
341 |
|
342 case BT_SET_IDLE: |
|
343 if (len != 2 || s->state == bt_state_transaction) { |
|
344 ret = BT_HS_ERR_INVALID_PARAMETER; |
|
345 break; |
|
346 } |
|
347 |
|
348 /* We don't need to know about the Idle Rate here really, |
|
349 * so just pass it on to the device. */ |
|
350 ret = s->usbdev->handle_control(s->usbdev, |
|
351 SET_IDLE, data[1], 0, 0, 0) ? |
|
352 BT_HS_SUCCESSFUL : BT_HS_ERR_INVALID_PARAMETER; |
|
353 /* XXX: Does this generate a handshake? */ |
|
354 break; |
|
355 |
|
356 case BT_DATC: |
|
357 if (len > BT_HID_MTU || s->state != bt_state_transaction) { |
|
358 ret = BT_HS_ERR_INVALID_PARAMETER; |
|
359 break; |
|
360 } |
|
361 if (s->data_type == BT_DATA_OUTPUT) { |
|
362 memcpy(s->dataout.buffer + s->dataout.len, data + 1, len - 1); |
|
363 s->dataout.len += len - 1; |
|
364 } else { |
|
365 memcpy(s->feature.buffer + s->feature.len, data + 1, len - 1); |
|
366 s->feature.len += len - 1; |
|
367 } |
|
368 if (len < BT_HID_MTU) { |
|
369 bt_hid_out(s); |
|
370 s->state = bt_state_ready; |
|
371 } |
|
372 break; |
|
373 |
|
374 default: |
|
375 ret = BT_HS_ERR_UNSUPPORTED_REQUEST; |
|
376 } |
|
377 |
|
378 if (ret != -1) |
|
379 bt_hid_send_handshake(s, ret); |
|
380 } |
|
381 |
|
382 static void bt_hid_control_sdu(void *opaque, const uint8_t *data, int len) |
|
383 { |
|
384 struct bt_hid_device_s *hid = opaque; |
|
385 |
|
386 return bt_hid_control_transaction(hid, data, len); |
|
387 } |
|
388 |
|
389 static void bt_hid_datain(void *opaque) |
|
390 { |
|
391 struct bt_hid_device_s *hid = opaque; |
|
392 |
|
393 /* If suspended, wake-up and send a wake-up event first. We might |
|
394 * want to also inspect the input report and ignore event like |
|
395 * mouse movements until a button event occurs. */ |
|
396 if (hid->state == bt_state_suspend) { |
|
397 hid->state = bt_state_ready; |
|
398 } |
|
399 |
|
400 if (bt_hid_in(hid) > 0) |
|
401 /* TODO: when in boot-mode precede any Input reports with the ReportID |
|
402 * byte, here and in GetReport/SetReport on the Control channel. */ |
|
403 bt_hid_send_data(hid->interrupt, BT_DATA_INPUT, |
|
404 hid->datain.buffer, hid->datain.len); |
|
405 } |
|
406 |
|
407 static void bt_hid_interrupt_sdu(void *opaque, const uint8_t *data, int len) |
|
408 { |
|
409 struct bt_hid_device_s *hid = opaque; |
|
410 |
|
411 if (len > BT_HID_MTU || len < 1) |
|
412 goto bad; |
|
413 if ((data[0] & 3) != BT_DATA_OUTPUT) |
|
414 goto bad; |
|
415 if ((data[0] >> 4) == BT_DATA) { |
|
416 if (hid->intr_state) |
|
417 goto bad; |
|
418 |
|
419 hid->data_type = BT_DATA_OUTPUT; |
|
420 hid->intrdataout.len = 0; |
|
421 } else if ((data[0] >> 4) == BT_DATC) { |
|
422 if (!hid->intr_state) |
|
423 goto bad; |
|
424 } else |
|
425 goto bad; |
|
426 |
|
427 memcpy(hid->intrdataout.buffer + hid->intrdataout.len, data + 1, len - 1); |
|
428 hid->intrdataout.len += len - 1; |
|
429 hid->intr_state = (len == BT_HID_MTU); |
|
430 if (!hid->intr_state) { |
|
431 memcpy(hid->dataout.buffer, hid->intrdataout.buffer, |
|
432 hid->dataout.len = hid->intrdataout.len); |
|
433 bt_hid_out(hid); |
|
434 } |
|
435 |
|
436 return; |
|
437 bad: |
|
438 fprintf(stderr, "%s: bad transaction on Interrupt channel.\n", |
|
439 __FUNCTION__); |
|
440 } |
|
441 |
|
442 /* "Virtual cable" plug/unplug event. */ |
|
443 static void bt_hid_connected_update(struct bt_hid_device_s *hid) |
|
444 { |
|
445 int prev = hid->connected; |
|
446 |
|
447 hid->connected = hid->control && hid->interrupt; |
|
448 |
|
449 /* Stop page-/inquiry-scanning when a host is connected. */ |
|
450 hid->btdev.device.page_scan = !hid->connected; |
|
451 hid->btdev.device.inquiry_scan = !hid->connected; |
|
452 |
|
453 if (hid->connected && !prev) { |
|
454 hid->usbdev->handle_reset(hid->usbdev); |
|
455 hid->proto = BT_HID_PROTO_REPORT; |
|
456 } |
|
457 |
|
458 /* Should set HIDVirtualCable in SDP (possibly need to check that SDP |
|
459 * isn't destroyed yet, in case we're being called from handle_destroy) */ |
|
460 } |
|
461 |
|
462 static void bt_hid_close_control(void *opaque) |
|
463 { |
|
464 struct bt_hid_device_s *hid = opaque; |
|
465 |
|
466 hid->control = 0; |
|
467 bt_hid_connected_update(hid); |
|
468 } |
|
469 |
|
470 static void bt_hid_close_interrupt(void *opaque) |
|
471 { |
|
472 struct bt_hid_device_s *hid = opaque; |
|
473 |
|
474 hid->interrupt = 0; |
|
475 bt_hid_connected_update(hid); |
|
476 } |
|
477 |
|
478 static int bt_hid_new_control_ch(struct bt_l2cap_device_s *dev, |
|
479 struct bt_l2cap_conn_params_s *params) |
|
480 { |
|
481 struct bt_hid_device_s *hid = (struct bt_hid_device_s *) dev; |
|
482 |
|
483 if (hid->control) |
|
484 return 1; |
|
485 |
|
486 hid->control = params; |
|
487 hid->control->opaque = hid; |
|
488 hid->control->close = bt_hid_close_control; |
|
489 hid->control->sdu_in = bt_hid_control_sdu; |
|
490 |
|
491 bt_hid_connected_update(hid); |
|
492 |
|
493 return 0; |
|
494 } |
|
495 |
|
496 static int bt_hid_new_interrupt_ch(struct bt_l2cap_device_s *dev, |
|
497 struct bt_l2cap_conn_params_s *params) |
|
498 { |
|
499 struct bt_hid_device_s *hid = (struct bt_hid_device_s *) dev; |
|
500 |
|
501 if (hid->interrupt) |
|
502 return 1; |
|
503 |
|
504 hid->interrupt = params; |
|
505 hid->interrupt->opaque = hid; |
|
506 hid->interrupt->close = bt_hid_close_interrupt; |
|
507 hid->interrupt->sdu_in = bt_hid_interrupt_sdu; |
|
508 |
|
509 bt_hid_connected_update(hid); |
|
510 |
|
511 return 0; |
|
512 } |
|
513 |
|
514 static void bt_hid_destroy(struct bt_device_s *dev) |
|
515 { |
|
516 struct bt_hid_device_s *hid = (struct bt_hid_device_s *) dev; |
|
517 |
|
518 if (hid->connected) |
|
519 bt_hid_send_control(hid, BT_HC_VIRTUAL_CABLE_UNPLUG); |
|
520 bt_l2cap_device_done(&hid->btdev); |
|
521 |
|
522 hid->usbdev->handle_destroy(hid->usbdev); |
|
523 |
|
524 qemu_free(hid); |
|
525 } |
|
526 |
|
527 enum peripheral_minor_class { |
|
528 class_other = 0 << 4, |
|
529 class_keyboard = 1 << 4, |
|
530 class_pointing = 2 << 4, |
|
531 class_combo = 3 << 4, |
|
532 }; |
|
533 |
|
534 static struct bt_device_s *bt_hid_init(struct bt_scatternet_s *net, |
|
535 USBDevice *dev, enum peripheral_minor_class minor) |
|
536 { |
|
537 struct bt_hid_device_s *s = qemu_mallocz(sizeof(*s)); |
|
538 uint32_t class = |
|
539 /* Format type */ |
|
540 (0 << 0) | |
|
541 /* Device class */ |
|
542 (minor << 2) | |
|
543 (5 << 8) | /* "Peripheral" */ |
|
544 /* Service classes */ |
|
545 (1 << 13) | /* Limited discoverable mode */ |
|
546 (1 << 19); /* Capturing device (?) */ |
|
547 |
|
548 bt_l2cap_device_init(&s->btdev, net); |
|
549 bt_l2cap_sdp_init(&s->btdev); |
|
550 bt_l2cap_psm_register(&s->btdev, BT_PSM_HID_CTRL, |
|
551 BT_HID_MTU, bt_hid_new_control_ch); |
|
552 bt_l2cap_psm_register(&s->btdev, BT_PSM_HID_INTR, |
|
553 BT_HID_MTU, bt_hid_new_interrupt_ch); |
|
554 |
|
555 s->usbdev = dev; |
|
556 s->btdev.device.lmp_name = s->usbdev->devname; |
|
557 usb_hid_datain_cb(s->usbdev, s, bt_hid_datain); |
|
558 |
|
559 s->btdev.device.handle_destroy = bt_hid_destroy; |
|
560 |
|
561 s->btdev.device.class[0] = (class >> 0) & 0xff; |
|
562 s->btdev.device.class[1] = (class >> 8) & 0xff; |
|
563 s->btdev.device.class[2] = (class >> 16) & 0xff; |
|
564 |
|
565 return &s->btdev.device; |
|
566 } |
|
567 |
|
568 struct bt_device_s *bt_keyboard_init(struct bt_scatternet_s *net) |
|
569 { |
|
570 return bt_hid_init(net, usb_keyboard_init(), class_keyboard); |
|
571 } |