Statistics
| Branch: | Revision:

root / hw / bt-hid.c @ 806b6024

History | View | Annotate | Download (15.9 kB)

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 along
18
 * with this program; if not, if not, see <http://www.gnu.org/licenses/>.
19
 */
20

    
21
#include "qemu-common.h"
22
#include "usb.h"
23
#include "bt.h"
24

    
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,
36
};
37

    
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,
46
};
47

    
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,
55
};
56

    
57
enum hid_protocol {
58
    BT_HID_PROTO_BOOT                        = 0,
59
    BT_HID_PROTO_REPORT                        = 1,
60
};
61

    
62
enum hid_boot_reportid {
63
    BT_HID_BOOT_INVALID                        = 0,
64
    BT_HID_BOOT_KEYBOARD,
65
    BT_HID_BOOT_MOUSE,
66
};
67

    
68
enum hid_data_pkt {
69
    BT_DATA_OTHER                        = 0,
70
    BT_DATA_INPUT,
71
    BT_DATA_OUTPUT,
72
    BT_DATA_FEATURE,
73
};
74

    
75
#define BT_HID_MTU                        48
76

    
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
84

    
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;
90

    
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;
104
};
105

    
106
static void bt_hid_reset(struct bt_hid_device_s *s)
107
{
108
    struct bt_scatternet_s *net = s->btdev.device.net;
109

    
110
    /* Go as far as... */
111
    bt_l2cap_device_done(&s->btdev);
112
    bt_l2cap_device_init(&s->btdev, net);
113

    
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;
123
}
124

    
125
static int bt_hid_out(struct bt_hid_device_s *s)
126
{
127
    USBPacket p;
128

    
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);
135

    
136
        return s->dataout.len;
137
    }
138

    
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;
144
    }
145

    
146
    return -1;
147
}
148

    
149
static int bt_hid_in(struct bt_hid_device_s *s)
150
{
151
    USBPacket p;
152

    
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);
158

    
159
    return s->datain.len;
160
}
161

    
162
static void bt_hid_send_handshake(struct bt_hid_device_s *s, int result)
163
{
164
    *s->control->sdu_out(s->control, 1) =
165
            (BT_HANDSHAKE << 4) | result;
166
    s->control->sdu_submit(s->control);
167
}
168

    
169
static void bt_hid_send_control(struct bt_hid_device_s *s, int operation)
170
{
171
    *s->control->sdu_out(s->control, 1) =
172
            (BT_HID_CONTROL << 4) | operation;
173
    s->control->sdu_submit(s->control);
174
}
175

    
176
static void bt_hid_disconnect(struct bt_hid_device_s *s)
177
{
178
    /* Disconnect s->control and s->interrupt */
179
}
180

    
181
static void bt_hid_send_data(struct bt_l2cap_conn_params_s *ch, int type,
182
                const uint8_t *data, int len)
183
{
184
    uint8_t *pkt, hdr = (BT_DATA << 4) | type;
185
    int plen;
186

    
187
    do {
188
        plen = MIN(len, ch->remote_mtu - 1);
189
        pkt = ch->sdu_out(ch, plen + 1);
190

    
191
        pkt[0] = hdr;
192
        if (plen)
193
            memcpy(pkt + 1, data, plen);
194
        ch->sdu_submit(ch);
195

    
196
        len -= plen;
197
        data += plen;
198
        hdr = (BT_DATC << 4) | type;
199
    } while (plen == ch->remote_mtu - 1);
200
}
201

    
202
static void bt_hid_control_transaction(struct bt_hid_device_s *s,
203
                const uint8_t *data, int len)
204
{
205
    uint8_t type, parameter;
206
    int rlen, ret = -1;
207
    if (len < 1)
208
        return;
209

    
210
    type = data[0] >> 4;
211
    parameter = data[0] & 0xf;
212

    
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;
220
        }
221
        break;
222

    
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;
228
        }
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;
253
        }
254
        break;
255

    
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;
263
        }
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;
285
        }
286
        break;
287

    
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;
294
        }
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);
302
        }
303
        if (len == BT_HID_MTU)
304
            s->state = bt_state_transaction;
305
        else
306
            bt_hid_out(s);
307
        break;
308

    
309
    case BT_GET_PROTOCOL:
310
        if (len != 1 || s->state == bt_state_transaction) {
311
            ret = BT_HS_ERR_INVALID_PARAMETER;
312
            break;
313
        }
314
        *s->control->sdu_out(s->control, 1) = s->proto;
315
        s->control->sdu_submit(s->control);
316
        break;
317

    
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;
324
        }
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;
330

    
331
    case BT_GET_IDLE:
332
        if (len != 1 || s->state == bt_state_transaction) {
333
            ret = BT_HS_ERR_INVALID_PARAMETER;
334
            break;
335
        }
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;
340

    
341
    case BT_SET_IDLE:
342
        if (len != 2 || s->state == bt_state_transaction) {
343
            ret = BT_HS_ERR_INVALID_PARAMETER;
344
            break;
345
        }
346

    
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;
354

    
355
    case BT_DATC:
356
        if (len > BT_HID_MTU || s->state != bt_state_transaction) {
357
            ret = BT_HS_ERR_INVALID_PARAMETER;
358
            break;
359
        }
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;
366
        }
367
        if (len < BT_HID_MTU) {
368
            bt_hid_out(s);
369
            s->state = bt_state_ready;
370
        }
371
        break;
372

    
373
    default:
374
        ret = BT_HS_ERR_UNSUPPORTED_REQUEST;
375
    }
376

    
377
    if (ret != -1)
378
        bt_hid_send_handshake(s, ret);
379
}
380

    
381
static void bt_hid_control_sdu(void *opaque, const uint8_t *data, int len)
382
{
383
    struct bt_hid_device_s *hid = opaque;
384

    
385
    bt_hid_control_transaction(hid, data, len);
386
}
387

    
388
static void bt_hid_datain(void *opaque)
389
{
390
    struct bt_hid_device_s *hid = opaque;
391

    
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;
397
    }
398

    
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);
404
}
405

    
406
static void bt_hid_interrupt_sdu(void *opaque, const uint8_t *data, int len)
407
{
408
    struct bt_hid_device_s *hid = opaque;
409

    
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;
417

    
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;
425

    
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);
433
    }
434

    
435
    return;
436
bad:
437
    fprintf(stderr, "%s: bad transaction on Interrupt channel.\n",
438
                    __FUNCTION__);
439
}
440

    
441
/* "Virtual cable" plug/unplug event.  */
442
static void bt_hid_connected_update(struct bt_hid_device_s *hid)
443
{
444
    int prev = hid->connected;
445

    
446
    hid->connected = hid->control && hid->interrupt;
447

    
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;
451

    
452
    if (hid->connected && !prev) {
453
        hid->usbdev->info->handle_reset(hid->usbdev);
454
        hid->proto = BT_HID_PROTO_REPORT;
455
    }
456

    
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) */
459
}
460

    
461
static void bt_hid_close_control(void *opaque)
462
{
463
    struct bt_hid_device_s *hid = opaque;
464

    
465
    hid->control = NULL;
466
    bt_hid_connected_update(hid);
467
}
468

    
469
static void bt_hid_close_interrupt(void *opaque)
470
{
471
    struct bt_hid_device_s *hid = opaque;
472

    
473
    hid->interrupt = NULL;
474
    bt_hid_connected_update(hid);
475
}
476

    
477
static int bt_hid_new_control_ch(struct bt_l2cap_device_s *dev,
478
                struct bt_l2cap_conn_params_s *params)
479
{
480
    struct bt_hid_device_s *hid = (struct bt_hid_device_s *) dev;
481

    
482
    if (hid->control)
483
        return 1;
484

    
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;
489

    
490
    bt_hid_connected_update(hid);
491

    
492
    return 0;
493
}
494

    
495
static int bt_hid_new_interrupt_ch(struct bt_l2cap_device_s *dev,
496
                struct bt_l2cap_conn_params_s *params)
497
{
498
    struct bt_hid_device_s *hid = (struct bt_hid_device_s *) dev;
499

    
500
    if (hid->interrupt)
501
        return 1;
502

    
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;
507

    
508
    bt_hid_connected_update(hid);
509

    
510
    return 0;
511
}
512

    
513
static void bt_hid_destroy(struct bt_device_s *dev)
514
{
515
    struct bt_hid_device_s *hid = (struct bt_hid_device_s *) dev;
516

    
517
    if (hid->connected)
518
        bt_hid_send_control(hid, BT_HC_VIRTUAL_CABLE_UNPLUG);
519
    bt_l2cap_device_done(&hid->btdev);
520

    
521
    hid->usbdev->info->handle_destroy(hid->usbdev);
522

    
523
    qemu_free(hid);
524
}
525

    
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,
531
};
532

    
533
static struct bt_device_s *bt_hid_init(struct bt_scatternet_s *net,
534
                USBDevice *dev, enum peripheral_minor_class minor)
535
{
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 (?) */
546

    
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);
553

    
554
    s->usbdev = dev;
555
    s->btdev.device.lmp_name = s->usbdev->devname;
556
    usb_hid_datain_cb(s->usbdev, s, bt_hid_datain);
557

    
558
    s->btdev.device.handle_destroy = bt_hid_destroy;
559

    
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;
563

    
564
    return &s->btdev.device;
565
}
566

    
567
struct bt_device_s *bt_keyboard_init(struct bt_scatternet_s *net)
568
{
569
    return bt_hid_init(net, usb_keyboard_init(), class_keyboard);
570
}