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"
25
enum hid_transaction_req {
30
BT_GET_PROTOCOL = 0x6,
31
BT_SET_PROTOCOL = 0x7,
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 {
50
BT_HC_HARD_RESET = 0x1,
51
BT_HC_SOFT_RESET = 0x2,
53
BT_HC_EXIT_SUSPEND = 0x4,
54
BT_HC_VIRTUAL_CABLE_UNPLUG = 0x5,
58
BT_HID_PROTO_BOOT = 0,
59
BT_HID_PROTO_REPORT = 1,
62
enum hid_boot_reportid {
63
BT_HID_BOOT_INVALID = 0,
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;
98
} dataother, datain, dataout, feature, intrdataout;
101
bt_state_transaction,
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;
121
s->intrdataout.len = 0;
125
static int bt_hid_out(struct bt_hid_device_s *s)
129
if (s->data_type == BT_DATA_OUTPUT) {
130
p.pid = USB_TOKEN_OUT;
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) {
141
* does this send a USB_REQ_CLEAR_FEATURE/USB_REQ_SET_FEATURE
142
* or a SET_REPORT? */
149
static int bt_hid_in(struct bt_hid_device_s *s)
153
p.pid = USB_TOKEN_IN;
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;
188
plen = MIN(len, ch->remote_mtu - 1);
189
pkt = ch->sdu_out(ch, plen + 1);
193
memcpy(pkt + 1, 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;
211
parameter = data[0] & 0xf;
218
/* These are not expected to be sent this direction. */
219
ret = BT_HS_ERR_INVALID_PARAMETER;
224
if (len != 1 || (parameter != BT_HC_VIRTUAL_CABLE_UNPLUG &&
225
s->state == bt_state_transaction)) {
226
ret = BT_HS_ERR_INVALID_PARAMETER;
232
case BT_HC_HARD_RESET:
233
case BT_HC_SOFT_RESET:
237
if (s->state == bt_state_ready)
238
s->state = bt_state_suspend;
240
ret = BT_HS_ERR_INVALID_PARAMETER;
242
case BT_HC_EXIT_SUSPEND:
243
if (s->state == bt_state_suspend)
244
s->state = bt_state_ready;
246
ret = BT_HS_ERR_INVALID_PARAMETER;
248
case BT_HC_VIRTUAL_CABLE_UNPLUG:
249
bt_hid_disconnect(s);
252
ret = BT_HS_ERR_INVALID_PARAMETER;
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;
265
rlen = data[2] | (data[3] << 8);
268
switch (parameter & 3) {
270
ret = BT_HS_ERR_INVALID_PARAMETER;
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));
278
bt_hid_send_data(s->control, BT_DATA_OUTPUT,
279
s->dataout.buffer, MIN(rlen, s->dataout.len));
281
case BT_DATA_FEATURE:
282
bt_hid_send_data(s->control, BT_DATA_FEATURE,
283
s->feature.buffer, MIN(rlen, s->feature.len));
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;
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);
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;
309
case BT_GET_PROTOCOL:
310
if (len != 1 || s->state == bt_state_transaction) {
311
ret = BT_HS_ERR_INVALID_PARAMETER;
314
*s->control->sdu_out(s->control, 1) = s->proto;
315
s->control->sdu_submit(s->control);
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;
325
s->proto = parameter;
326
s->usbdev->info->handle_control(s->usbdev, NULL, SET_PROTOCOL, s->proto, 0, 0,
328
ret = BT_HS_SUCCESSFUL;
332
if (len != 1 || s->state == bt_state_transaction) {
333
ret = BT_HS_ERR_INVALID_PARAMETER;
336
s->usbdev->info->handle_control(s->usbdev, NULL, GET_IDLE, 0, 0, 1,
337
s->control->sdu_out(s->control, 1));
338
s->control->sdu_submit(s->control);
342
if (len != 2 || s->state == bt_state_transaction) {
343
ret = BT_HS_ERR_INVALID_PARAMETER;
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, NULL,
350
SET_IDLE, data[1], 0, 0, NULL) ?
351
BT_HS_SUCCESSFUL : BT_HS_ERR_INVALID_PARAMETER;
352
/* XXX: Does this generate a handshake? */
356
if (len > BT_HID_MTU || s->state != bt_state_transaction) {
357
ret = BT_HS_ERR_INVALID_PARAMETER;
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;
364
memcpy(s->feature.buffer + s->feature.len, data + 1, len - 1);
365
s->feature.len += len - 1;
367
if (len < BT_HID_MTU) {
369
s->state = bt_state_ready;
374
ret = BT_HS_ERR_UNSUPPORTED_REQUEST;
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)
412
if ((data[0] & 3) != BT_DATA_OUTPUT)
414
if ((data[0] >> 4) == BT_DATA) {
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)
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);
437
fprintf(stderr, "%s: bad transaction on Interrupt channel.\n",
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;
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;
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);
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;
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);
513
static void bt_hid_destroy(struct bt_device_s *dev)
515
struct bt_hid_device_s *hid = (struct bt_hid_device_s *) dev;
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);
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));
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);
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);