savevm: Add DeviceState param
[qemu/kraxel.git] / hw / bt-hid.c
blobabdfd35e8647f1a1cda651bf34112b33265d5d0e
1 /*
2 * QEMU Bluetooth HID Profile wrapper for USB HID.
4 * Copyright (C) 2007-2008 OpenMoko, Inc.
5 * Written by Andrzej Zaborowski <andrew@openedhand.com>
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.
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.
17 * You should have received a copy of the GNU General Public License along
18 * with this program; if not, if not, see <http://www.gnu.org/licenses/>.
21 #include "qemu-common.h"
22 #include "usb.h"
23 #include "bt.h"
25 enum hid_transaction_req {
26 BT_HANDSHAKE = 0x0,
27 BT_HID_CONTROL = 0x1,
28 BT_GET_REPORT = 0x4,
29 BT_SET_REPORT = 0x5,
30 BT_GET_PROTOCOL = 0x6,
31 BT_SET_PROTOCOL = 0x7,
32 BT_GET_IDLE = 0x8,
33 BT_SET_IDLE = 0x9,
34 BT_DATA = 0xa,
35 BT_DATC = 0xb,
38 enum hid_transaction_handshake {
39 BT_HS_SUCCESSFUL = 0x0,
40 BT_HS_NOT_READY = 0x1,
41 BT_HS_ERR_INVALID_REPORT_ID = 0x2,
42 BT_HS_ERR_UNSUPPORTED_REQUEST = 0x3,
43 BT_HS_ERR_INVALID_PARAMETER = 0x4,
44 BT_HS_ERR_UNKNOWN = 0xe,
45 BT_HS_ERR_FATAL = 0xf,
48 enum hid_transaction_control {
49 BT_HC_NOP = 0x0,
50 BT_HC_HARD_RESET = 0x1,
51 BT_HC_SOFT_RESET = 0x2,
52 BT_HC_SUSPEND = 0x3,
53 BT_HC_EXIT_SUSPEND = 0x4,
54 BT_HC_VIRTUAL_CABLE_UNPLUG = 0x5,
57 enum hid_protocol {
58 BT_HID_PROTO_BOOT = 0,
59 BT_HID_PROTO_REPORT = 1,
62 enum hid_boot_reportid {
63 BT_HID_BOOT_INVALID = 0,
64 BT_HID_BOOT_KEYBOARD,
65 BT_HID_BOOT_MOUSE,
68 enum hid_data_pkt {
69 BT_DATA_OTHER = 0,
70 BT_DATA_INPUT,
71 BT_DATA_OUTPUT,
72 BT_DATA_FEATURE,
75 #define BT_HID_MTU 48
77 /* HID interface requests */
78 #define GET_REPORT 0xa101
79 #define GET_IDLE 0xa102
80 #define GET_PROTOCOL 0xa103
81 #define SET_REPORT 0x2109
82 #define SET_IDLE 0x210a
83 #define SET_PROTOCOL 0x210b
85 struct bt_hid_device_s {
86 struct bt_l2cap_device_s btdev;
87 struct bt_l2cap_conn_params_s *control;
88 struct bt_l2cap_conn_params_s *interrupt;
89 USBDevice *usbdev;
91 int proto;
92 int connected;
93 int data_type;
94 int intr_state;
95 struct {
96 int len;
97 uint8_t buffer[1024];
98 } dataother, datain, dataout, feature, intrdataout;
99 enum {
100 bt_state_ready,
101 bt_state_transaction,
102 bt_state_suspend,
103 } state;
106 static void bt_hid_reset(struct bt_hid_device_s *s)
108 struct bt_scatternet_s *net = s->btdev.device.net;
110 /* Go as far as... */
111 bt_l2cap_device_done(&s->btdev);
112 bt_l2cap_device_init(&s->btdev, net);
114 s->usbdev->info->handle_reset(s->usbdev);
115 s->proto = BT_HID_PROTO_REPORT;
116 s->state = bt_state_ready;
117 s->dataother.len = 0;
118 s->datain.len = 0;
119 s->dataout.len = 0;
120 s->feature.len = 0;
121 s->intrdataout.len = 0;
122 s->intr_state = 0;
125 static int bt_hid_out(struct bt_hid_device_s *s)
127 USBPacket p;
129 if (s->data_type == BT_DATA_OUTPUT) {
130 p.pid = USB_TOKEN_OUT;
131 p.devep = 1;
132 p.data = s->dataout.buffer;
133 p.len = s->dataout.len;
134 s->dataout.len = s->usbdev->info->handle_data(s->usbdev, &p);
136 return s->dataout.len;
139 if (s->data_type == BT_DATA_FEATURE) {
140 /* XXX:
141 * does this send a USB_REQ_CLEAR_FEATURE/USB_REQ_SET_FEATURE
142 * or a SET_REPORT? */
143 p.devep = 0;
146 return -1;
149 static int bt_hid_in(struct bt_hid_device_s *s)
151 USBPacket p;
153 p.pid = USB_TOKEN_IN;
154 p.devep = 1;
155 p.data = s->datain.buffer;
156 p.len = sizeof(s->datain.buffer);
157 s->datain.len = s->usbdev->info->handle_data(s->usbdev, &p);
159 return s->datain.len;
162 static void bt_hid_send_handshake(struct bt_hid_device_s *s, int result)
164 *s->control->sdu_out(s->control, 1) =
165 (BT_HANDSHAKE << 4) | result;
166 s->control->sdu_submit(s->control);
169 static void bt_hid_send_control(struct bt_hid_device_s *s, int operation)
171 *s->control->sdu_out(s->control, 1) =
172 (BT_HID_CONTROL << 4) | operation;
173 s->control->sdu_submit(s->control);
176 static void bt_hid_disconnect(struct bt_hid_device_s *s)
178 /* Disconnect s->control and s->interrupt */
181 static void bt_hid_send_data(struct bt_l2cap_conn_params_s *ch, int type,
182 const uint8_t *data, int len)
184 uint8_t *pkt, hdr = (BT_DATA << 4) | type;
185 int plen;
187 do {
188 plen = MIN(len, ch->remote_mtu - 1);
189 pkt = ch->sdu_out(ch, plen + 1);
191 pkt[0] = hdr;
192 if (plen)
193 memcpy(pkt + 1, data, plen);
194 ch->sdu_submit(ch);
196 len -= plen;
197 data += plen;
198 hdr = (BT_DATC << 4) | type;
199 } while (plen == ch->remote_mtu - 1);
202 static void bt_hid_control_transaction(struct bt_hid_device_s *s,
203 const uint8_t *data, int len)
205 uint8_t type, parameter;
206 int rlen, ret = -1;
207 if (len < 1)
208 return;
210 type = data[0] >> 4;
211 parameter = data[0] & 0xf;
213 switch (type) {
214 case BT_HANDSHAKE:
215 case BT_DATA:
216 switch (parameter) {
217 default:
218 /* These are not expected to be sent this direction. */
219 ret = BT_HS_ERR_INVALID_PARAMETER;
221 break;
223 case BT_HID_CONTROL:
224 if (len != 1 || (parameter != BT_HC_VIRTUAL_CABLE_UNPLUG &&
225 s->state == bt_state_transaction)) {
226 ret = BT_HS_ERR_INVALID_PARAMETER;
227 break;
229 switch (parameter) {
230 case BT_HC_NOP:
231 break;
232 case BT_HC_HARD_RESET:
233 case BT_HC_SOFT_RESET:
234 bt_hid_reset(s);
235 break;
236 case BT_HC_SUSPEND:
237 if (s->state == bt_state_ready)
238 s->state = bt_state_suspend;
239 else
240 ret = BT_HS_ERR_INVALID_PARAMETER;
241 break;
242 case BT_HC_EXIT_SUSPEND:
243 if (s->state == bt_state_suspend)
244 s->state = bt_state_ready;
245 else
246 ret = BT_HS_ERR_INVALID_PARAMETER;
247 break;
248 case BT_HC_VIRTUAL_CABLE_UNPLUG:
249 bt_hid_disconnect(s);
250 break;
251 default:
252 ret = BT_HS_ERR_INVALID_PARAMETER;
254 break;
256 case BT_GET_REPORT:
257 /* No ReportIDs declared. */
258 if (((parameter & 8) && len != 3) ||
259 (!(parameter & 8) && len != 1) ||
260 s->state != bt_state_ready) {
261 ret = BT_HS_ERR_INVALID_PARAMETER;
262 break;
264 if (parameter & 8)
265 rlen = data[2] | (data[3] << 8);
266 else
267 rlen = INT_MAX;
268 switch (parameter & 3) {
269 case BT_DATA_OTHER:
270 ret = BT_HS_ERR_INVALID_PARAMETER;
271 break;
272 case BT_DATA_INPUT:
273 /* Here we can as well poll s->usbdev */
274 bt_hid_send_data(s->control, BT_DATA_INPUT,
275 s->datain.buffer, MIN(rlen, s->datain.len));
276 break;
277 case BT_DATA_OUTPUT:
278 bt_hid_send_data(s->control, BT_DATA_OUTPUT,
279 s->dataout.buffer, MIN(rlen, s->dataout.len));
280 break;
281 case BT_DATA_FEATURE:
282 bt_hid_send_data(s->control, BT_DATA_FEATURE,
283 s->feature.buffer, MIN(rlen, s->feature.len));
284 break;
286 break;
288 case BT_SET_REPORT:
289 if (len < 2 || len > BT_HID_MTU || s->state != bt_state_ready ||
290 (parameter & 3) == BT_DATA_OTHER ||
291 (parameter & 3) == BT_DATA_INPUT) {
292 ret = BT_HS_ERR_INVALID_PARAMETER;
293 break;
295 s->data_type = parameter & 3;
296 if (s->data_type == BT_DATA_OUTPUT) {
297 s->dataout.len = len - 1;
298 memcpy(s->dataout.buffer, data + 1, s->dataout.len);
299 } else {
300 s->feature.len = len - 1;
301 memcpy(s->feature.buffer, data + 1, s->feature.len);
303 if (len == BT_HID_MTU)
304 s->state = bt_state_transaction;
305 else
306 bt_hid_out(s);
307 break;
309 case BT_GET_PROTOCOL:
310 if (len != 1 || s->state == bt_state_transaction) {
311 ret = BT_HS_ERR_INVALID_PARAMETER;
312 break;
314 *s->control->sdu_out(s->control, 1) = s->proto;
315 s->control->sdu_submit(s->control);
316 break;
318 case BT_SET_PROTOCOL:
319 if (len != 1 || s->state == bt_state_transaction ||
320 (parameter != BT_HID_PROTO_BOOT &&
321 parameter != BT_HID_PROTO_REPORT)) {
322 ret = BT_HS_ERR_INVALID_PARAMETER;
323 break;
325 s->proto = parameter;
326 s->usbdev->info->handle_control(s->usbdev, SET_PROTOCOL, s->proto, 0, 0,
327 NULL);
328 ret = BT_HS_SUCCESSFUL;
329 break;
331 case BT_GET_IDLE:
332 if (len != 1 || s->state == bt_state_transaction) {
333 ret = BT_HS_ERR_INVALID_PARAMETER;
334 break;
336 s->usbdev->info->handle_control(s->usbdev, GET_IDLE, 0, 0, 1,
337 s->control->sdu_out(s->control, 1));
338 s->control->sdu_submit(s->control);
339 break;
341 case BT_SET_IDLE:
342 if (len != 2 || s->state == bt_state_transaction) {
343 ret = BT_HS_ERR_INVALID_PARAMETER;
344 break;
347 /* We don't need to know about the Idle Rate here really,
348 * so just pass it on to the device. */
349 ret = s->usbdev->info->handle_control(s->usbdev,
350 SET_IDLE, data[1], 0, 0, NULL) ?
351 BT_HS_SUCCESSFUL : BT_HS_ERR_INVALID_PARAMETER;
352 /* XXX: Does this generate a handshake? */
353 break;
355 case BT_DATC:
356 if (len > BT_HID_MTU || s->state != bt_state_transaction) {
357 ret = BT_HS_ERR_INVALID_PARAMETER;
358 break;
360 if (s->data_type == BT_DATA_OUTPUT) {
361 memcpy(s->dataout.buffer + s->dataout.len, data + 1, len - 1);
362 s->dataout.len += len - 1;
363 } else {
364 memcpy(s->feature.buffer + s->feature.len, data + 1, len - 1);
365 s->feature.len += len - 1;
367 if (len < BT_HID_MTU) {
368 bt_hid_out(s);
369 s->state = bt_state_ready;
371 break;
373 default:
374 ret = BT_HS_ERR_UNSUPPORTED_REQUEST;
377 if (ret != -1)
378 bt_hid_send_handshake(s, ret);
381 static void bt_hid_control_sdu(void *opaque, const uint8_t *data, int len)
383 struct bt_hid_device_s *hid = opaque;
385 bt_hid_control_transaction(hid, data, len);
388 static void bt_hid_datain(void *opaque)
390 struct bt_hid_device_s *hid = opaque;
392 /* If suspended, wake-up and send a wake-up event first. We might
393 * want to also inspect the input report and ignore event like
394 * mouse movements until a button event occurs. */
395 if (hid->state == bt_state_suspend) {
396 hid->state = bt_state_ready;
399 if (bt_hid_in(hid) > 0)
400 /* TODO: when in boot-mode precede any Input reports with the ReportID
401 * byte, here and in GetReport/SetReport on the Control channel. */
402 bt_hid_send_data(hid->interrupt, BT_DATA_INPUT,
403 hid->datain.buffer, hid->datain.len);
406 static void bt_hid_interrupt_sdu(void *opaque, const uint8_t *data, int len)
408 struct bt_hid_device_s *hid = opaque;
410 if (len > BT_HID_MTU || len < 1)
411 goto bad;
412 if ((data[0] & 3) != BT_DATA_OUTPUT)
413 goto bad;
414 if ((data[0] >> 4) == BT_DATA) {
415 if (hid->intr_state)
416 goto bad;
418 hid->data_type = BT_DATA_OUTPUT;
419 hid->intrdataout.len = 0;
420 } else if ((data[0] >> 4) == BT_DATC) {
421 if (!hid->intr_state)
422 goto bad;
423 } else
424 goto bad;
426 memcpy(hid->intrdataout.buffer + hid->intrdataout.len, data + 1, len - 1);
427 hid->intrdataout.len += len - 1;
428 hid->intr_state = (len == BT_HID_MTU);
429 if (!hid->intr_state) {
430 memcpy(hid->dataout.buffer, hid->intrdataout.buffer,
431 hid->dataout.len = hid->intrdataout.len);
432 bt_hid_out(hid);
435 return;
436 bad:
437 fprintf(stderr, "%s: bad transaction on Interrupt channel.\n",
438 __FUNCTION__);
441 /* "Virtual cable" plug/unplug event. */
442 static void bt_hid_connected_update(struct bt_hid_device_s *hid)
444 int prev = hid->connected;
446 hid->connected = hid->control && hid->interrupt;
448 /* Stop page-/inquiry-scanning when a host is connected. */
449 hid->btdev.device.page_scan = !hid->connected;
450 hid->btdev.device.inquiry_scan = !hid->connected;
452 if (hid->connected && !prev) {
453 hid->usbdev->info->handle_reset(hid->usbdev);
454 hid->proto = BT_HID_PROTO_REPORT;
457 /* Should set HIDVirtualCable in SDP (possibly need to check that SDP
458 * isn't destroyed yet, in case we're being called from handle_destroy) */
461 static void bt_hid_close_control(void *opaque)
463 struct bt_hid_device_s *hid = opaque;
465 hid->control = NULL;
466 bt_hid_connected_update(hid);
469 static void bt_hid_close_interrupt(void *opaque)
471 struct bt_hid_device_s *hid = opaque;
473 hid->interrupt = NULL;
474 bt_hid_connected_update(hid);
477 static int bt_hid_new_control_ch(struct bt_l2cap_device_s *dev,
478 struct bt_l2cap_conn_params_s *params)
480 struct bt_hid_device_s *hid = (struct bt_hid_device_s *) dev;
482 if (hid->control)
483 return 1;
485 hid->control = params;
486 hid->control->opaque = hid;
487 hid->control->close = bt_hid_close_control;
488 hid->control->sdu_in = bt_hid_control_sdu;
490 bt_hid_connected_update(hid);
492 return 0;
495 static int bt_hid_new_interrupt_ch(struct bt_l2cap_device_s *dev,
496 struct bt_l2cap_conn_params_s *params)
498 struct bt_hid_device_s *hid = (struct bt_hid_device_s *) dev;
500 if (hid->interrupt)
501 return 1;
503 hid->interrupt = params;
504 hid->interrupt->opaque = hid;
505 hid->interrupt->close = bt_hid_close_interrupt;
506 hid->interrupt->sdu_in = bt_hid_interrupt_sdu;
508 bt_hid_connected_update(hid);
510 return 0;
513 static void bt_hid_destroy(struct bt_device_s *dev)
515 struct bt_hid_device_s *hid = (struct bt_hid_device_s *) dev;
517 if (hid->connected)
518 bt_hid_send_control(hid, BT_HC_VIRTUAL_CABLE_UNPLUG);
519 bt_l2cap_device_done(&hid->btdev);
521 hid->usbdev->info->handle_destroy(hid->usbdev);
523 qemu_free(hid);
526 enum peripheral_minor_class {
527 class_other = 0 << 4,
528 class_keyboard = 1 << 4,
529 class_pointing = 2 << 4,
530 class_combo = 3 << 4,
533 static struct bt_device_s *bt_hid_init(struct bt_scatternet_s *net,
534 USBDevice *dev, enum peripheral_minor_class minor)
536 struct bt_hid_device_s *s = qemu_mallocz(sizeof(*s));
537 uint32_t class =
538 /* Format type */
539 (0 << 0) |
540 /* Device class */
541 (minor << 2) |
542 (5 << 8) | /* "Peripheral" */
543 /* Service classes */
544 (1 << 13) | /* Limited discoverable mode */
545 (1 << 19); /* Capturing device (?) */
547 bt_l2cap_device_init(&s->btdev, net);
548 bt_l2cap_sdp_init(&s->btdev);
549 bt_l2cap_psm_register(&s->btdev, BT_PSM_HID_CTRL,
550 BT_HID_MTU, bt_hid_new_control_ch);
551 bt_l2cap_psm_register(&s->btdev, BT_PSM_HID_INTR,
552 BT_HID_MTU, bt_hid_new_interrupt_ch);
554 s->usbdev = dev;
555 s->btdev.device.lmp_name = s->usbdev->product_desc;
556 usb_hid_datain_cb(s->usbdev, s, bt_hid_datain);
558 s->btdev.device.handle_destroy = bt_hid_destroy;
560 s->btdev.device.class[0] = (class >> 0) & 0xff;
561 s->btdev.device.class[1] = (class >> 8) & 0xff;
562 s->btdev.device.class[2] = (class >> 16) & 0xff;
564 return &s->btdev.device;
567 struct bt_device_s *bt_keyboard_init(struct bt_scatternet_s *net)
569 USBDevice *dev = usb_create_simple(NULL /* FIXME */, "usb-kbd");
570 return bt_hid_init(net, dev, class_keyboard);