summary refs log tree commit diff stats
path: root/hw/bt/hid.c
diff options
context:
space:
mode:
Diffstat (limited to 'hw/bt/hid.c')
-rw-r--r--hw/bt/hid.c553
1 files changed, 0 insertions, 553 deletions
diff --git a/hw/bt/hid.c b/hw/bt/hid.c
deleted file mode 100644
index 066ca99ed2..0000000000
--- a/hw/bt/hid.c
+++ /dev/null
@@ -1,553 +0,0 @@
-/*
- * QEMU Bluetooth HID Profile wrapper for USB HID.
- *
- * Copyright (C) 2007-2008 OpenMoko, Inc.
- * Written by Andrzej Zaborowski <andrew@openedhand.com>
- *
- * This program is free software; you can redistribute it and/or
- * modify it under the terms of the GNU General Public License as
- * published by the Free Software Foundation; either version 2 or
- * (at your option) version 3 of the License.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License along
- * with this program; if not, if not, see <http://www.gnu.org/licenses/>.
- */
-
-#include "qemu/osdep.h"
-#include "qemu/timer.h"
-#include "ui/console.h"
-#include "hw/input/hid.h"
-#include "hw/bt.h"
-
-enum hid_transaction_req {
-    BT_HANDSHAKE			= 0x0,
-    BT_HID_CONTROL			= 0x1,
-    BT_GET_REPORT			= 0x4,
-    BT_SET_REPORT			= 0x5,
-    BT_GET_PROTOCOL			= 0x6,
-    BT_SET_PROTOCOL			= 0x7,
-    BT_GET_IDLE				= 0x8,
-    BT_SET_IDLE				= 0x9,
-    BT_DATA				= 0xa,
-    BT_DATC				= 0xb,
-};
-
-enum hid_transaction_handshake {
-    BT_HS_SUCCESSFUL			= 0x0,
-    BT_HS_NOT_READY			= 0x1,
-    BT_HS_ERR_INVALID_REPORT_ID		= 0x2,
-    BT_HS_ERR_UNSUPPORTED_REQUEST	= 0x3,
-    BT_HS_ERR_INVALID_PARAMETER		= 0x4,
-    BT_HS_ERR_UNKNOWN			= 0xe,
-    BT_HS_ERR_FATAL			= 0xf,
-};
-
-enum hid_transaction_control {
-    BT_HC_NOP				= 0x0,
-    BT_HC_HARD_RESET			= 0x1,
-    BT_HC_SOFT_RESET			= 0x2,
-    BT_HC_SUSPEND			= 0x3,
-    BT_HC_EXIT_SUSPEND			= 0x4,
-    BT_HC_VIRTUAL_CABLE_UNPLUG		= 0x5,
-};
-
-enum hid_protocol {
-    BT_HID_PROTO_BOOT			= 0,
-    BT_HID_PROTO_REPORT			= 1,
-};
-
-enum hid_boot_reportid {
-    BT_HID_BOOT_INVALID			= 0,
-    BT_HID_BOOT_KEYBOARD,
-    BT_HID_BOOT_MOUSE,
-};
-
-enum hid_data_pkt {
-    BT_DATA_OTHER			= 0,
-    BT_DATA_INPUT,
-    BT_DATA_OUTPUT,
-    BT_DATA_FEATURE,
-};
-
-#define BT_HID_MTU			48
-
-/* HID interface requests */
-#define GET_REPORT			0xa101
-#define GET_IDLE			0xa102
-#define GET_PROTOCOL			0xa103
-#define SET_REPORT			0x2109
-#define SET_IDLE			0x210a
-#define SET_PROTOCOL			0x210b
-
-struct bt_hid_device_s {
-    struct bt_l2cap_device_s btdev;
-    struct bt_l2cap_conn_params_s *control;
-    struct bt_l2cap_conn_params_s *interrupt;
-    HIDState hid;
-
-    int proto;
-    int connected;
-    int data_type;
-    int intr_state;
-    struct {
-        int len;
-        uint8_t buffer[1024];
-    } dataother, datain, dataout, feature, intrdataout;
-    enum {
-        bt_state_ready,
-        bt_state_transaction,
-        bt_state_suspend,
-    } state;
-};
-
-static void bt_hid_reset(struct bt_hid_device_s *s)
-{
-    struct bt_scatternet_s *net = s->btdev.device.net;
-
-    /* Go as far as... */
-    bt_l2cap_device_done(&s->btdev);
-    bt_l2cap_device_init(&s->btdev, net);
-
-    hid_reset(&s->hid);
-    s->proto = BT_HID_PROTO_REPORT;
-    s->state = bt_state_ready;
-    s->dataother.len = 0;
-    s->datain.len = 0;
-    s->dataout.len = 0;
-    s->feature.len = 0;
-    s->intrdataout.len = 0;
-    s->intr_state = 0;
-}
-
-static int bt_hid_out(struct bt_hid_device_s *s)
-{
-    if (s->data_type == BT_DATA_OUTPUT) {
-        /* nothing */
-        ;
-    }
-
-    if (s->data_type == BT_DATA_FEATURE) {
-        /* XXX:
-         * does this send a USB_REQ_CLEAR_FEATURE/USB_REQ_SET_FEATURE
-         * or a SET_REPORT? */
-        ;
-    }
-
-    return -1;
-}
-
-static int bt_hid_in(struct bt_hid_device_s *s)
-{
-    s->datain.len = hid_keyboard_poll(&s->hid, s->datain.buffer,
-                                      sizeof(s->datain.buffer));
-    return s->datain.len;
-}
-
-static void bt_hid_send_handshake(struct bt_hid_device_s *s, int result)
-{
-    *s->control->sdu_out(s->control, 1) =
-            (BT_HANDSHAKE << 4) | result;
-    s->control->sdu_submit(s->control);
-}
-
-static void bt_hid_send_control(struct bt_hid_device_s *s, int operation)
-{
-    *s->control->sdu_out(s->control, 1) =
-            (BT_HID_CONTROL << 4) | operation;
-    s->control->sdu_submit(s->control);
-}
-
-static void bt_hid_disconnect(struct bt_hid_device_s *s)
-{
-    /* Disconnect s->control and s->interrupt */
-}
-
-static void bt_hid_send_data(struct bt_l2cap_conn_params_s *ch, int type,
-                const uint8_t *data, int len)
-{
-    uint8_t *pkt, hdr = (BT_DATA << 4) | type;
-    int plen;
-
-    do {
-        plen = MIN(len, ch->remote_mtu - 1);
-        pkt = ch->sdu_out(ch, plen + 1);
-
-        pkt[0] = hdr;
-        if (plen)
-            memcpy(pkt + 1, data, plen);
-        ch->sdu_submit(ch);
-
-        len -= plen;
-        data += plen;
-        hdr = (BT_DATC << 4) | type;
-    } while (plen == ch->remote_mtu - 1);
-}
-
-static void bt_hid_control_transaction(struct bt_hid_device_s *s,
-                const uint8_t *data, int len)
-{
-    uint8_t type, parameter;
-    int rlen, ret = -1;
-    if (len < 1)
-        return;
-
-    type = data[0] >> 4;
-    parameter = data[0] & 0xf;
-
-    switch (type) {
-    case BT_HANDSHAKE:
-    case BT_DATA:
-        switch (parameter) {
-        default:
-            /* These are not expected to be sent this direction.  */
-            ret = BT_HS_ERR_INVALID_PARAMETER;
-        }
-        break;
-
-    case BT_HID_CONTROL:
-        if (len != 1 || (parameter != BT_HC_VIRTUAL_CABLE_UNPLUG &&
-                                s->state == bt_state_transaction)) {
-            ret = BT_HS_ERR_INVALID_PARAMETER;
-            break;
-        }
-        switch (parameter) {
-        case BT_HC_NOP:
-            break;
-        case BT_HC_HARD_RESET:
-        case BT_HC_SOFT_RESET:
-            bt_hid_reset(s);
-            break;
-        case BT_HC_SUSPEND:
-            if (s->state == bt_state_ready)
-                s->state = bt_state_suspend;
-            else
-                ret = BT_HS_ERR_INVALID_PARAMETER;
-            break;
-        case BT_HC_EXIT_SUSPEND:
-            if (s->state == bt_state_suspend)
-                s->state = bt_state_ready;
-            else
-                ret = BT_HS_ERR_INVALID_PARAMETER;
-            break;
-        case BT_HC_VIRTUAL_CABLE_UNPLUG:
-            bt_hid_disconnect(s);
-            break;
-        default:
-            ret = BT_HS_ERR_INVALID_PARAMETER;
-        }
-        break;
-
-    case BT_GET_REPORT:
-        /* No ReportIDs declared.  */
-        if (((parameter & 8) && len != 3) ||
-                        (!(parameter & 8) && len != 1) ||
-                        s->state != bt_state_ready) {
-            ret = BT_HS_ERR_INVALID_PARAMETER;
-            break;
-        }
-        if (parameter & 8)
-            rlen = data[2] | (data[3] << 8);
-        else
-            rlen = INT_MAX;
-        switch (parameter & 3) {
-        case BT_DATA_OTHER:
-            ret = BT_HS_ERR_INVALID_PARAMETER;
-            break;
-        case BT_DATA_INPUT:
-            /* Here we can as well poll s->usbdev */
-            bt_hid_send_data(s->control, BT_DATA_INPUT,
-                            s->datain.buffer, MIN(rlen, s->datain.len));
-            break;
-        case BT_DATA_OUTPUT:
-            bt_hid_send_data(s->control, BT_DATA_OUTPUT,
-                            s->dataout.buffer, MIN(rlen, s->dataout.len));
-            break;
-        case BT_DATA_FEATURE:
-            bt_hid_send_data(s->control, BT_DATA_FEATURE,
-                            s->feature.buffer, MIN(rlen, s->feature.len));
-            break;
-        }
-        break;
-
-    case BT_SET_REPORT:
-        if (len < 2 || len > BT_HID_MTU || s->state != bt_state_ready ||
-                        (parameter & 3) == BT_DATA_OTHER ||
-                        (parameter & 3) == BT_DATA_INPUT) {
-            ret = BT_HS_ERR_INVALID_PARAMETER;
-            break;
-        }
-        s->data_type = parameter & 3;
-        if (s->data_type == BT_DATA_OUTPUT) {
-            s->dataout.len = len - 1;
-            memcpy(s->dataout.buffer, data + 1, s->dataout.len);
-        } else {
-            s->feature.len = len - 1;
-            memcpy(s->feature.buffer, data + 1, s->feature.len);
-        }
-        if (len == BT_HID_MTU)
-            s->state = bt_state_transaction;
-        else
-            bt_hid_out(s);
-        break;
-
-    case BT_GET_PROTOCOL:
-        if (len != 1 || s->state == bt_state_transaction) {
-            ret = BT_HS_ERR_INVALID_PARAMETER;
-            break;
-        }
-        *s->control->sdu_out(s->control, 1) = s->proto;
-        s->control->sdu_submit(s->control);
-        break;
-
-    case BT_SET_PROTOCOL:
-        if (len != 1 || s->state == bt_state_transaction ||
-                        (parameter != BT_HID_PROTO_BOOT &&
-                         parameter != BT_HID_PROTO_REPORT)) {
-            ret = BT_HS_ERR_INVALID_PARAMETER;
-            break;
-        }
-        s->proto = parameter;
-        s->hid.protocol = parameter;
-        ret = BT_HS_SUCCESSFUL;
-        break;
-
-    case BT_GET_IDLE:
-        if (len != 1 || s->state == bt_state_transaction) {
-            ret = BT_HS_ERR_INVALID_PARAMETER;
-            break;
-        }
-        *s->control->sdu_out(s->control, 1) = s->hid.idle;
-        s->control->sdu_submit(s->control);
-        break;
-
-    case BT_SET_IDLE:
-        if (len != 2 || s->state == bt_state_transaction) {
-            ret = BT_HS_ERR_INVALID_PARAMETER;
-            break;
-        }
-
-        s->hid.idle = data[1];
-        /* XXX: Does this generate a handshake? */
-        break;
-
-    case BT_DATC:
-        if (len > BT_HID_MTU || s->state != bt_state_transaction) {
-            ret = BT_HS_ERR_INVALID_PARAMETER;
-            break;
-        }
-        if (s->data_type == BT_DATA_OUTPUT) {
-            memcpy(s->dataout.buffer + s->dataout.len, data + 1, len - 1);
-            s->dataout.len += len - 1;
-        } else {
-            memcpy(s->feature.buffer + s->feature.len, data + 1, len - 1);
-            s->feature.len += len - 1;
-        }
-        if (len < BT_HID_MTU) {
-            bt_hid_out(s);
-            s->state = bt_state_ready;
-        }
-        break;
-
-    default:
-        ret = BT_HS_ERR_UNSUPPORTED_REQUEST;
-    }
-
-    if (ret != -1)
-        bt_hid_send_handshake(s, ret);
-}
-
-static void bt_hid_control_sdu(void *opaque, const uint8_t *data, int len)
-{
-    struct bt_hid_device_s *hid = opaque;
-
-    bt_hid_control_transaction(hid, data, len);
-}
-
-static void bt_hid_datain(HIDState *hs)
-{
-    struct bt_hid_device_s *hid =
-        container_of(hs, struct bt_hid_device_s, hid);
-
-    /* If suspended, wake-up and send a wake-up event first.  We might
-     * want to also inspect the input report and ignore event like
-     * mouse movements until a button event occurs.  */
-    if (hid->state == bt_state_suspend) {
-        hid->state = bt_state_ready;
-    }
-
-    if (bt_hid_in(hid) > 0)
-        /* TODO: when in boot-mode precede any Input reports with the ReportID
-         * byte, here and in GetReport/SetReport on the Control channel.  */
-        bt_hid_send_data(hid->interrupt, BT_DATA_INPUT,
-                        hid->datain.buffer, hid->datain.len);
-}
-
-static void bt_hid_interrupt_sdu(void *opaque, const uint8_t *data, int len)
-{
-    struct bt_hid_device_s *hid = opaque;
-
-    if (len > BT_HID_MTU || len < 1)
-        goto bad;
-    if ((data[0] & 3) != BT_DATA_OUTPUT)
-        goto bad;
-    if ((data[0] >> 4) == BT_DATA) {
-        if (hid->intr_state)
-            goto bad;
-
-        hid->data_type = BT_DATA_OUTPUT;
-        hid->intrdataout.len = 0;
-    } else if ((data[0] >> 4) == BT_DATC) {
-        if (!hid->intr_state)
-            goto bad;
-    } else
-        goto bad;
-
-    memcpy(hid->intrdataout.buffer + hid->intrdataout.len, data + 1, len - 1);
-    hid->intrdataout.len += len - 1;
-    hid->intr_state = (len == BT_HID_MTU);
-    if (!hid->intr_state) {
-        memcpy(hid->dataout.buffer, hid->intrdataout.buffer,
-                        hid->dataout.len = hid->intrdataout.len);
-        bt_hid_out(hid);
-    }
-
-    return;
-bad:
-    error_report("%s: bad transaction on Interrupt channel.",
-                    __func__);
-}
-
-/* "Virtual cable" plug/unplug event.  */
-static void bt_hid_connected_update(struct bt_hid_device_s *hid)
-{
-    int prev = hid->connected;
-
-    hid->connected = hid->control && hid->interrupt;
-
-    /* Stop page-/inquiry-scanning when a host is connected.  */
-    hid->btdev.device.page_scan = !hid->connected;
-    hid->btdev.device.inquiry_scan = !hid->connected;
-
-    if (hid->connected && !prev) {
-        hid_reset(&hid->hid);
-        hid->proto = BT_HID_PROTO_REPORT;
-    }
-
-    /* Should set HIDVirtualCable in SDP (possibly need to check that SDP
-     * isn't destroyed yet, in case we're being called from handle_destroy) */
-}
-
-static void bt_hid_close_control(void *opaque)
-{
-    struct bt_hid_device_s *hid = opaque;
-
-    hid->control = NULL;
-    bt_hid_connected_update(hid);
-}
-
-static void bt_hid_close_interrupt(void *opaque)
-{
-    struct bt_hid_device_s *hid = opaque;
-
-    hid->interrupt = NULL;
-    bt_hid_connected_update(hid);
-}
-
-static int bt_hid_new_control_ch(struct bt_l2cap_device_s *dev,
-                struct bt_l2cap_conn_params_s *params)
-{
-    struct bt_hid_device_s *hid = (struct bt_hid_device_s *) dev;
-
-    if (hid->control)
-        return 1;
-
-    hid->control = params;
-    hid->control->opaque = hid;
-    hid->control->close = bt_hid_close_control;
-    hid->control->sdu_in = bt_hid_control_sdu;
-
-    bt_hid_connected_update(hid);
-
-    return 0;
-}
-
-static int bt_hid_new_interrupt_ch(struct bt_l2cap_device_s *dev,
-                struct bt_l2cap_conn_params_s *params)
-{
-    struct bt_hid_device_s *hid = (struct bt_hid_device_s *) dev;
-
-    if (hid->interrupt)
-        return 1;
-
-    hid->interrupt = params;
-    hid->interrupt->opaque = hid;
-    hid->interrupt->close = bt_hid_close_interrupt;
-    hid->interrupt->sdu_in = bt_hid_interrupt_sdu;
-
-    bt_hid_connected_update(hid);
-
-    return 0;
-}
-
-static void bt_hid_destroy(struct bt_device_s *dev)
-{
-    struct bt_hid_device_s *hid = (struct bt_hid_device_s *) dev;
-
-    if (hid->connected)
-        bt_hid_send_control(hid, BT_HC_VIRTUAL_CABLE_UNPLUG);
-    bt_l2cap_device_done(&hid->btdev);
-
-    hid_free(&hid->hid);
-
-    g_free(hid);
-}
-
-enum peripheral_minor_class {
-    class_other		= 0 << 4,
-    class_keyboard	= 1 << 4,
-    class_pointing	= 2 << 4,
-    class_combo		= 3 << 4,
-};
-
-static struct bt_device_s *bt_hid_init(struct bt_scatternet_s *net,
-                                       enum peripheral_minor_class minor)
-{
-    struct bt_hid_device_s *s = g_malloc0(sizeof(*s));
-    uint32_t class =
-            /* Format type */
-            (0 << 0) |
-            /* Device class */
-            (minor << 2) |
-            (5 << 8) |  /* "Peripheral" */
-            /* Service classes */
-            (1 << 13) | /* Limited discoverable mode */
-            (1 << 19);  /* Capturing device (?) */
-
-    bt_l2cap_device_init(&s->btdev, net);
-    bt_l2cap_sdp_init(&s->btdev);
-    bt_l2cap_psm_register(&s->btdev, BT_PSM_HID_CTRL,
-                    BT_HID_MTU, bt_hid_new_control_ch);
-    bt_l2cap_psm_register(&s->btdev, BT_PSM_HID_INTR,
-                    BT_HID_MTU, bt_hid_new_interrupt_ch);
-
-    hid_init(&s->hid, HID_KEYBOARD, bt_hid_datain);
-    s->btdev.device.lmp_name = "BT Keyboard";
-
-    s->btdev.device.handle_destroy = bt_hid_destroy;
-
-    s->btdev.device.class[0] = (class >>  0) & 0xff;
-    s->btdev.device.class[1] = (class >>  8) & 0xff;
-    s->btdev.device.class[2] = (class >> 16) & 0xff;
-
-    return &s->btdev.device;
-}
-
-struct bt_device_s *bt_keyboard_init(struct bt_scatternet_s *net)
-{
-    return bt_hid_init(net, class_keyboard);
-}