Statistics
| Branch: | Revision:

root / hw / bt-hid.c @ 47e699dc

History | View | Annotate | Download (15.8 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
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
}