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