~ubuntu-branches/ubuntu/wily/qemu-kvm-spice/wily

« back to all changes in this revision

Viewing changes to hw/bt-hid.c

  • Committer: Bazaar Package Importer
  • Author(s): Serge Hallyn
  • Date: 2011-10-19 10:44:56 UTC
  • Revision ID: james.westby@ubuntu.com-20111019104456-xgvskumk3sxi97f4
Tags: upstream-0.15.0+noroms
ImportĀ upstreamĀ versionĀ 0.15.0+noroms

Show diffs side-by-side

added added

removed removed

Lines of Context:
 
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, NULL, 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, NULL, 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, 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? */
 
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->product_desc;
 
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
    USBDevice *dev = usb_create_simple(NULL /* FIXME */, "usb-kbd");
 
570
    return bt_hid_init(net, dev, class_keyboard);
 
571
}