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