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