]> git.proxmox.com Git - qemu.git/blob - hw/bt-hid.c
Implement tick interrupt disable bits
[qemu.git] / hw / bt-hid.c
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 }