15d8f37ad78fc66901af50c762029a501561f3b23David 'Digit' Turner/*
25d8f37ad78fc66901af50c762029a501561f3b23David 'Digit' Turner * QEMU Bluetooth HID Profile wrapper for USB HID.
35d8f37ad78fc66901af50c762029a501561f3b23David 'Digit' Turner *
45d8f37ad78fc66901af50c762029a501561f3b23David 'Digit' Turner * Copyright (C) 2007-2008 OpenMoko, Inc.
55d8f37ad78fc66901af50c762029a501561f3b23David 'Digit' Turner * Written by Andrzej Zaborowski <andrew@openedhand.com>
65d8f37ad78fc66901af50c762029a501561f3b23David 'Digit' Turner *
75d8f37ad78fc66901af50c762029a501561f3b23David 'Digit' Turner * This program is free software; you can redistribute it and/or
85d8f37ad78fc66901af50c762029a501561f3b23David 'Digit' Turner * modify it under the terms of the GNU General Public License as
95d8f37ad78fc66901af50c762029a501561f3b23David 'Digit' Turner * published by the Free Software Foundation; either version 2 or
105d8f37ad78fc66901af50c762029a501561f3b23David 'Digit' Turner * (at your option) version 3 of the License.
115d8f37ad78fc66901af50c762029a501561f3b23David 'Digit' Turner *
125d8f37ad78fc66901af50c762029a501561f3b23David 'Digit' Turner * This program is distributed in the hope that it will be useful,
135d8f37ad78fc66901af50c762029a501561f3b23David 'Digit' Turner * but WITHOUT ANY WARRANTY; without even the implied warranty of
145d8f37ad78fc66901af50c762029a501561f3b23David 'Digit' Turner * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
155d8f37ad78fc66901af50c762029a501561f3b23David 'Digit' Turner * GNU General Public License for more details.
165d8f37ad78fc66901af50c762029a501561f3b23David 'Digit' Turner *
175d8f37ad78fc66901af50c762029a501561f3b23David 'Digit' Turner * You should have received a copy of the GNU General Public License along
18a25351325187eb8eff8b9b090acd8f2d7684c6ffDavid Turner * with this program; if not, if not, see <http://www.gnu.org/licenses/>.
195d8f37ad78fc66901af50c762029a501561f3b23David 'Digit' Turner */
205d8f37ad78fc66901af50c762029a501561f3b23David 'Digit' Turner
215d8f37ad78fc66901af50c762029a501561f3b23David 'Digit' Turner#include "qemu-common.h"
225d8f37ad78fc66901af50c762029a501561f3b23David 'Digit' Turner#include "usb.h"
235d8f37ad78fc66901af50c762029a501561f3b23David 'Digit' Turner#include "bt.h"
245d8f37ad78fc66901af50c762029a501561f3b23David 'Digit' Turner
255d8f37ad78fc66901af50c762029a501561f3b23David 'Digit' Turnerenum hid_transaction_req {
265d8f37ad78fc66901af50c762029a501561f3b23David 'Digit' Turner    BT_HANDSHAKE			= 0x0,
275d8f37ad78fc66901af50c762029a501561f3b23David 'Digit' Turner    BT_HID_CONTROL			= 0x1,
285d8f37ad78fc66901af50c762029a501561f3b23David 'Digit' Turner    BT_GET_REPORT			= 0x4,
295d8f37ad78fc66901af50c762029a501561f3b23David 'Digit' Turner    BT_SET_REPORT			= 0x5,
305d8f37ad78fc66901af50c762029a501561f3b23David 'Digit' Turner    BT_GET_PROTOCOL			= 0x6,
315d8f37ad78fc66901af50c762029a501561f3b23David 'Digit' Turner    BT_SET_PROTOCOL			= 0x7,
325d8f37ad78fc66901af50c762029a501561f3b23David 'Digit' Turner    BT_GET_IDLE				= 0x8,
335d8f37ad78fc66901af50c762029a501561f3b23David 'Digit' Turner    BT_SET_IDLE				= 0x9,
345d8f37ad78fc66901af50c762029a501561f3b23David 'Digit' Turner    BT_DATA				= 0xa,
355d8f37ad78fc66901af50c762029a501561f3b23David 'Digit' Turner    BT_DATC				= 0xb,
365d8f37ad78fc66901af50c762029a501561f3b23David 'Digit' Turner};
375d8f37ad78fc66901af50c762029a501561f3b23David 'Digit' Turner
385d8f37ad78fc66901af50c762029a501561f3b23David 'Digit' Turnerenum hid_transaction_handshake {
395d8f37ad78fc66901af50c762029a501561f3b23David 'Digit' Turner    BT_HS_SUCCESSFUL			= 0x0,
405d8f37ad78fc66901af50c762029a501561f3b23David 'Digit' Turner    BT_HS_NOT_READY			= 0x1,
415d8f37ad78fc66901af50c762029a501561f3b23David 'Digit' Turner    BT_HS_ERR_INVALID_REPORT_ID		= 0x2,
425d8f37ad78fc66901af50c762029a501561f3b23David 'Digit' Turner    BT_HS_ERR_UNSUPPORTED_REQUEST	= 0x3,
435d8f37ad78fc66901af50c762029a501561f3b23David 'Digit' Turner    BT_HS_ERR_INVALID_PARAMETER		= 0x4,
445d8f37ad78fc66901af50c762029a501561f3b23David 'Digit' Turner    BT_HS_ERR_UNKNOWN			= 0xe,
455d8f37ad78fc66901af50c762029a501561f3b23David 'Digit' Turner    BT_HS_ERR_FATAL			= 0xf,
465d8f37ad78fc66901af50c762029a501561f3b23David 'Digit' Turner};
475d8f37ad78fc66901af50c762029a501561f3b23David 'Digit' Turner
485d8f37ad78fc66901af50c762029a501561f3b23David 'Digit' Turnerenum hid_transaction_control {
495d8f37ad78fc66901af50c762029a501561f3b23David 'Digit' Turner    BT_HC_NOP				= 0x0,
505d8f37ad78fc66901af50c762029a501561f3b23David 'Digit' Turner    BT_HC_HARD_RESET			= 0x1,
515d8f37ad78fc66901af50c762029a501561f3b23David 'Digit' Turner    BT_HC_SOFT_RESET			= 0x2,
525d8f37ad78fc66901af50c762029a501561f3b23David 'Digit' Turner    BT_HC_SUSPEND			= 0x3,
535d8f37ad78fc66901af50c762029a501561f3b23David 'Digit' Turner    BT_HC_EXIT_SUSPEND			= 0x4,
545d8f37ad78fc66901af50c762029a501561f3b23David 'Digit' Turner    BT_HC_VIRTUAL_CABLE_UNPLUG		= 0x5,
555d8f37ad78fc66901af50c762029a501561f3b23David 'Digit' Turner};
565d8f37ad78fc66901af50c762029a501561f3b23David 'Digit' Turner
575d8f37ad78fc66901af50c762029a501561f3b23David 'Digit' Turnerenum hid_protocol {
585d8f37ad78fc66901af50c762029a501561f3b23David 'Digit' Turner    BT_HID_PROTO_BOOT			= 0,
595d8f37ad78fc66901af50c762029a501561f3b23David 'Digit' Turner    BT_HID_PROTO_REPORT			= 1,
605d8f37ad78fc66901af50c762029a501561f3b23David 'Digit' Turner};
615d8f37ad78fc66901af50c762029a501561f3b23David 'Digit' Turner
625d8f37ad78fc66901af50c762029a501561f3b23David 'Digit' Turnerenum hid_boot_reportid {
635d8f37ad78fc66901af50c762029a501561f3b23David 'Digit' Turner    BT_HID_BOOT_INVALID			= 0,
645d8f37ad78fc66901af50c762029a501561f3b23David 'Digit' Turner    BT_HID_BOOT_KEYBOARD,
655d8f37ad78fc66901af50c762029a501561f3b23David 'Digit' Turner    BT_HID_BOOT_MOUSE,
665d8f37ad78fc66901af50c762029a501561f3b23David 'Digit' Turner};
675d8f37ad78fc66901af50c762029a501561f3b23David 'Digit' Turner
685d8f37ad78fc66901af50c762029a501561f3b23David 'Digit' Turnerenum hid_data_pkt {
695d8f37ad78fc66901af50c762029a501561f3b23David 'Digit' Turner    BT_DATA_OTHER			= 0,
705d8f37ad78fc66901af50c762029a501561f3b23David 'Digit' Turner    BT_DATA_INPUT,
715d8f37ad78fc66901af50c762029a501561f3b23David 'Digit' Turner    BT_DATA_OUTPUT,
725d8f37ad78fc66901af50c762029a501561f3b23David 'Digit' Turner    BT_DATA_FEATURE,
735d8f37ad78fc66901af50c762029a501561f3b23David 'Digit' Turner};
745d8f37ad78fc66901af50c762029a501561f3b23David 'Digit' Turner
755d8f37ad78fc66901af50c762029a501561f3b23David 'Digit' Turner#define BT_HID_MTU			48
765d8f37ad78fc66901af50c762029a501561f3b23David 'Digit' Turner
775d8f37ad78fc66901af50c762029a501561f3b23David 'Digit' Turner/* HID interface requests */
785d8f37ad78fc66901af50c762029a501561f3b23David 'Digit' Turner#define GET_REPORT			0xa101
795d8f37ad78fc66901af50c762029a501561f3b23David 'Digit' Turner#define GET_IDLE			0xa102
805d8f37ad78fc66901af50c762029a501561f3b23David 'Digit' Turner#define GET_PROTOCOL			0xa103
815d8f37ad78fc66901af50c762029a501561f3b23David 'Digit' Turner#define SET_REPORT			0x2109
825d8f37ad78fc66901af50c762029a501561f3b23David 'Digit' Turner#define SET_IDLE			0x210a
835d8f37ad78fc66901af50c762029a501561f3b23David 'Digit' Turner#define SET_PROTOCOL			0x210b
845d8f37ad78fc66901af50c762029a501561f3b23David 'Digit' Turner
855d8f37ad78fc66901af50c762029a501561f3b23David 'Digit' Turnerstruct bt_hid_device_s {
865d8f37ad78fc66901af50c762029a501561f3b23David 'Digit' Turner    struct bt_l2cap_device_s btdev;
875d8f37ad78fc66901af50c762029a501561f3b23David 'Digit' Turner    struct bt_l2cap_conn_params_s *control;
885d8f37ad78fc66901af50c762029a501561f3b23David 'Digit' Turner    struct bt_l2cap_conn_params_s *interrupt;
895d8f37ad78fc66901af50c762029a501561f3b23David 'Digit' Turner    USBDevice *usbdev;
905d8f37ad78fc66901af50c762029a501561f3b23David 'Digit' Turner
915d8f37ad78fc66901af50c762029a501561f3b23David 'Digit' Turner    int proto;
925d8f37ad78fc66901af50c762029a501561f3b23David 'Digit' Turner    int connected;
935d8f37ad78fc66901af50c762029a501561f3b23David 'Digit' Turner    int data_type;
945d8f37ad78fc66901af50c762029a501561f3b23David 'Digit' Turner    int intr_state;
955d8f37ad78fc66901af50c762029a501561f3b23David 'Digit' Turner    struct {
965d8f37ad78fc66901af50c762029a501561f3b23David 'Digit' Turner        int len;
975d8f37ad78fc66901af50c762029a501561f3b23David 'Digit' Turner        uint8_t buffer[1024];
985d8f37ad78fc66901af50c762029a501561f3b23David 'Digit' Turner    } dataother, datain, dataout, feature, intrdataout;
995d8f37ad78fc66901af50c762029a501561f3b23David 'Digit' Turner    enum {
1005d8f37ad78fc66901af50c762029a501561f3b23David 'Digit' Turner        bt_state_ready,
1015d8f37ad78fc66901af50c762029a501561f3b23David 'Digit' Turner        bt_state_transaction,
1025d8f37ad78fc66901af50c762029a501561f3b23David 'Digit' Turner        bt_state_suspend,
1035d8f37ad78fc66901af50c762029a501561f3b23David 'Digit' Turner    } state;
1045d8f37ad78fc66901af50c762029a501561f3b23David 'Digit' Turner};
1055d8f37ad78fc66901af50c762029a501561f3b23David 'Digit' Turner
1065d8f37ad78fc66901af50c762029a501561f3b23David 'Digit' Turnerstatic void bt_hid_reset(struct bt_hid_device_s *s)
1075d8f37ad78fc66901af50c762029a501561f3b23David 'Digit' Turner{
1085d8f37ad78fc66901af50c762029a501561f3b23David 'Digit' Turner    struct bt_scatternet_s *net = s->btdev.device.net;
1095d8f37ad78fc66901af50c762029a501561f3b23David 'Digit' Turner
1105d8f37ad78fc66901af50c762029a501561f3b23David 'Digit' Turner    /* Go as far as... */
1115d8f37ad78fc66901af50c762029a501561f3b23David 'Digit' Turner    bt_l2cap_device_done(&s->btdev);
1125d8f37ad78fc66901af50c762029a501561f3b23David 'Digit' Turner    bt_l2cap_device_init(&s->btdev, net);
1135d8f37ad78fc66901af50c762029a501561f3b23David 'Digit' Turner
1145d8f37ad78fc66901af50c762029a501561f3b23David 'Digit' Turner    s->usbdev->handle_reset(s->usbdev);
1155d8f37ad78fc66901af50c762029a501561f3b23David 'Digit' Turner    s->proto = BT_HID_PROTO_REPORT;
1165d8f37ad78fc66901af50c762029a501561f3b23David 'Digit' Turner    s->state = bt_state_ready;
1175d8f37ad78fc66901af50c762029a501561f3b23David 'Digit' Turner    s->dataother.len = 0;
1185d8f37ad78fc66901af50c762029a501561f3b23David 'Digit' Turner    s->datain.len = 0;
1195d8f37ad78fc66901af50c762029a501561f3b23David 'Digit' Turner    s->dataout.len = 0;
1205d8f37ad78fc66901af50c762029a501561f3b23David 'Digit' Turner    s->feature.len = 0;
1215d8f37ad78fc66901af50c762029a501561f3b23David 'Digit' Turner    s->intrdataout.len = 0;
1225d8f37ad78fc66901af50c762029a501561f3b23David 'Digit' Turner    s->intr_state = 0;
1235d8f37ad78fc66901af50c762029a501561f3b23David 'Digit' Turner}
1245d8f37ad78fc66901af50c762029a501561f3b23David 'Digit' Turner
1255d8f37ad78fc66901af50c762029a501561f3b23David 'Digit' Turnerstatic int bt_hid_out(struct bt_hid_device_s *s)
1265d8f37ad78fc66901af50c762029a501561f3b23David 'Digit' Turner{
1275d8f37ad78fc66901af50c762029a501561f3b23David 'Digit' Turner    USBPacket p;
1285d8f37ad78fc66901af50c762029a501561f3b23David 'Digit' Turner
1295d8f37ad78fc66901af50c762029a501561f3b23David 'Digit' Turner    if (s->data_type == BT_DATA_OUTPUT) {
1305d8f37ad78fc66901af50c762029a501561f3b23David 'Digit' Turner        p.pid = USB_TOKEN_OUT;
1315d8f37ad78fc66901af50c762029a501561f3b23David 'Digit' Turner        p.devep = 1;
1325d8f37ad78fc66901af50c762029a501561f3b23David 'Digit' Turner        p.data = s->dataout.buffer;
1335d8f37ad78fc66901af50c762029a501561f3b23David 'Digit' Turner        p.len = s->dataout.len;
1345d8f37ad78fc66901af50c762029a501561f3b23David 'Digit' Turner        s->dataout.len = s->usbdev->handle_data(s->usbdev, &p);
1355d8f37ad78fc66901af50c762029a501561f3b23David 'Digit' Turner
1365d8f37ad78fc66901af50c762029a501561f3b23David 'Digit' Turner        return s->dataout.len;
1375d8f37ad78fc66901af50c762029a501561f3b23David 'Digit' Turner    }
1385d8f37ad78fc66901af50c762029a501561f3b23David 'Digit' Turner
1395d8f37ad78fc66901af50c762029a501561f3b23David 'Digit' Turner    if (s->data_type == BT_DATA_FEATURE) {
1405d8f37ad78fc66901af50c762029a501561f3b23David 'Digit' Turner        /* XXX:
1415d8f37ad78fc66901af50c762029a501561f3b23David 'Digit' Turner         * does this send a USB_REQ_CLEAR_FEATURE/USB_REQ_SET_FEATURE
1425d8f37ad78fc66901af50c762029a501561f3b23David 'Digit' Turner         * or a SET_REPORT? */
1435d8f37ad78fc66901af50c762029a501561f3b23David 'Digit' Turner        p.devep = 0;
1445d8f37ad78fc66901af50c762029a501561f3b23David 'Digit' Turner    }
1455d8f37ad78fc66901af50c762029a501561f3b23David 'Digit' Turner
1465d8f37ad78fc66901af50c762029a501561f3b23David 'Digit' Turner    return -1;
1475d8f37ad78fc66901af50c762029a501561f3b23David 'Digit' Turner}
1485d8f37ad78fc66901af50c762029a501561f3b23David 'Digit' Turner
1495d8f37ad78fc66901af50c762029a501561f3b23David 'Digit' Turnerstatic int bt_hid_in(struct bt_hid_device_s *s)
1505d8f37ad78fc66901af50c762029a501561f3b23David 'Digit' Turner{
1515d8f37ad78fc66901af50c762029a501561f3b23David 'Digit' Turner    USBPacket p;
1525d8f37ad78fc66901af50c762029a501561f3b23David 'Digit' Turner
1535d8f37ad78fc66901af50c762029a501561f3b23David 'Digit' Turner    p.pid = USB_TOKEN_IN;
1545d8f37ad78fc66901af50c762029a501561f3b23David 'Digit' Turner    p.devep = 1;
1555d8f37ad78fc66901af50c762029a501561f3b23David 'Digit' Turner    p.data = s->datain.buffer;
1565d8f37ad78fc66901af50c762029a501561f3b23David 'Digit' Turner    p.len = sizeof(s->datain.buffer);
1575d8f37ad78fc66901af50c762029a501561f3b23David 'Digit' Turner    s->datain.len = s->usbdev->handle_data(s->usbdev, &p);
1585d8f37ad78fc66901af50c762029a501561f3b23David 'Digit' Turner
1595d8f37ad78fc66901af50c762029a501561f3b23David 'Digit' Turner    return s->datain.len;
1605d8f37ad78fc66901af50c762029a501561f3b23David 'Digit' Turner}
1615d8f37ad78fc66901af50c762029a501561f3b23David 'Digit' Turner
1625d8f37ad78fc66901af50c762029a501561f3b23David 'Digit' Turnerstatic void bt_hid_send_handshake(struct bt_hid_device_s *s, int result)
1635d8f37ad78fc66901af50c762029a501561f3b23David 'Digit' Turner{
1645d8f37ad78fc66901af50c762029a501561f3b23David 'Digit' Turner    *s->control->sdu_out(s->control, 1) =
1655d8f37ad78fc66901af50c762029a501561f3b23David 'Digit' Turner            (BT_HANDSHAKE << 4) | result;
1665d8f37ad78fc66901af50c762029a501561f3b23David 'Digit' Turner    s->control->sdu_submit(s->control);
1675d8f37ad78fc66901af50c762029a501561f3b23David 'Digit' Turner}
1685d8f37ad78fc66901af50c762029a501561f3b23David 'Digit' Turner
1695d8f37ad78fc66901af50c762029a501561f3b23David 'Digit' Turnerstatic void bt_hid_send_control(struct bt_hid_device_s *s, int operation)
1705d8f37ad78fc66901af50c762029a501561f3b23David 'Digit' Turner{
1715d8f37ad78fc66901af50c762029a501561f3b23David 'Digit' Turner    *s->control->sdu_out(s->control, 1) =
1725d8f37ad78fc66901af50c762029a501561f3b23David 'Digit' Turner            (BT_HID_CONTROL << 4) | operation;
1735d8f37ad78fc66901af50c762029a501561f3b23David 'Digit' Turner    s->control->sdu_submit(s->control);
1745d8f37ad78fc66901af50c762029a501561f3b23David 'Digit' Turner}
1755d8f37ad78fc66901af50c762029a501561f3b23David 'Digit' Turner
1765d8f37ad78fc66901af50c762029a501561f3b23David 'Digit' Turnerstatic void bt_hid_disconnect(struct bt_hid_device_s *s)
1775d8f37ad78fc66901af50c762029a501561f3b23David 'Digit' Turner{
1785d8f37ad78fc66901af50c762029a501561f3b23David 'Digit' Turner    /* Disconnect s->control and s->interrupt */
1795d8f37ad78fc66901af50c762029a501561f3b23David 'Digit' Turner}
1805d8f37ad78fc66901af50c762029a501561f3b23David 'Digit' Turner
1815d8f37ad78fc66901af50c762029a501561f3b23David 'Digit' Turnerstatic void bt_hid_send_data(struct bt_l2cap_conn_params_s *ch, int type,
1825d8f37ad78fc66901af50c762029a501561f3b23David 'Digit' Turner                const uint8_t *data, int len)
1835d8f37ad78fc66901af50c762029a501561f3b23David 'Digit' Turner{
1845d8f37ad78fc66901af50c762029a501561f3b23David 'Digit' Turner    uint8_t *pkt, hdr = (BT_DATA << 4) | type;
1855d8f37ad78fc66901af50c762029a501561f3b23David 'Digit' Turner    int plen;
1865d8f37ad78fc66901af50c762029a501561f3b23David 'Digit' Turner
1875d8f37ad78fc66901af50c762029a501561f3b23David 'Digit' Turner    do {
1885d8f37ad78fc66901af50c762029a501561f3b23David 'Digit' Turner        plen = MIN(len, ch->remote_mtu - 1);
1895d8f37ad78fc66901af50c762029a501561f3b23David 'Digit' Turner        pkt = ch->sdu_out(ch, plen + 1);
1905d8f37ad78fc66901af50c762029a501561f3b23David 'Digit' Turner
1915d8f37ad78fc66901af50c762029a501561f3b23David 'Digit' Turner        pkt[0] = hdr;
1925d8f37ad78fc66901af50c762029a501561f3b23David 'Digit' Turner        if (plen)
1935d8f37ad78fc66901af50c762029a501561f3b23David 'Digit' Turner            memcpy(pkt + 1, data, plen);
1945d8f37ad78fc66901af50c762029a501561f3b23David 'Digit' Turner        ch->sdu_submit(ch);
1955d8f37ad78fc66901af50c762029a501561f3b23David 'Digit' Turner
1965d8f37ad78fc66901af50c762029a501561f3b23David 'Digit' Turner        len -= plen;
1975d8f37ad78fc66901af50c762029a501561f3b23David 'Digit' Turner        data += plen;
1985d8f37ad78fc66901af50c762029a501561f3b23David 'Digit' Turner        hdr = (BT_DATC << 4) | type;
1995d8f37ad78fc66901af50c762029a501561f3b23David 'Digit' Turner    } while (plen == ch->remote_mtu - 1);
2005d8f37ad78fc66901af50c762029a501561f3b23David 'Digit' Turner}
2015d8f37ad78fc66901af50c762029a501561f3b23David 'Digit' Turner
2025d8f37ad78fc66901af50c762029a501561f3b23David 'Digit' Turnerstatic void bt_hid_control_transaction(struct bt_hid_device_s *s,
2035d8f37ad78fc66901af50c762029a501561f3b23David 'Digit' Turner                const uint8_t *data, int len)
2045d8f37ad78fc66901af50c762029a501561f3b23David 'Digit' Turner{
2055d8f37ad78fc66901af50c762029a501561f3b23David 'Digit' Turner    uint8_t type, parameter;
2065d8f37ad78fc66901af50c762029a501561f3b23David 'Digit' Turner    int rlen, ret = -1;
2075d8f37ad78fc66901af50c762029a501561f3b23David 'Digit' Turner    if (len < 1)
2085d8f37ad78fc66901af50c762029a501561f3b23David 'Digit' Turner        return;
2095d8f37ad78fc66901af50c762029a501561f3b23David 'Digit' Turner
2105d8f37ad78fc66901af50c762029a501561f3b23David 'Digit' Turner    type = data[0] >> 4;
2115d8f37ad78fc66901af50c762029a501561f3b23David 'Digit' Turner    parameter = data[0] & 0xf;
2125d8f37ad78fc66901af50c762029a501561f3b23David 'Digit' Turner
2135d8f37ad78fc66901af50c762029a501561f3b23David 'Digit' Turner    switch (type) {
2145d8f37ad78fc66901af50c762029a501561f3b23David 'Digit' Turner    case BT_HANDSHAKE:
2155d8f37ad78fc66901af50c762029a501561f3b23David 'Digit' Turner    case BT_DATA:
2165d8f37ad78fc66901af50c762029a501561f3b23David 'Digit' Turner        switch (parameter) {
2175d8f37ad78fc66901af50c762029a501561f3b23David 'Digit' Turner        default:
2185d8f37ad78fc66901af50c762029a501561f3b23David 'Digit' Turner            /* These are not expected to be sent this direction.  */
2195d8f37ad78fc66901af50c762029a501561f3b23David 'Digit' Turner            ret = BT_HS_ERR_INVALID_PARAMETER;
2205d8f37ad78fc66901af50c762029a501561f3b23David 'Digit' Turner        }
2215d8f37ad78fc66901af50c762029a501561f3b23David 'Digit' Turner        break;
2225d8f37ad78fc66901af50c762029a501561f3b23David 'Digit' Turner
2235d8f37ad78fc66901af50c762029a501561f3b23David 'Digit' Turner    case BT_HID_CONTROL:
2245d8f37ad78fc66901af50c762029a501561f3b23David 'Digit' Turner        if (len != 1 || (parameter != BT_HC_VIRTUAL_CABLE_UNPLUG &&
2255d8f37ad78fc66901af50c762029a501561f3b23David 'Digit' Turner                                s->state == bt_state_transaction)) {
2265d8f37ad78fc66901af50c762029a501561f3b23David 'Digit' Turner            ret = BT_HS_ERR_INVALID_PARAMETER;
2275d8f37ad78fc66901af50c762029a501561f3b23David 'Digit' Turner            break;
2285d8f37ad78fc66901af50c762029a501561f3b23David 'Digit' Turner        }
2295d8f37ad78fc66901af50c762029a501561f3b23David 'Digit' Turner        switch (parameter) {
2305d8f37ad78fc66901af50c762029a501561f3b23David 'Digit' Turner        case BT_HC_NOP:
2315d8f37ad78fc66901af50c762029a501561f3b23David 'Digit' Turner            break;
2325d8f37ad78fc66901af50c762029a501561f3b23David 'Digit' Turner        case BT_HC_HARD_RESET:
2335d8f37ad78fc66901af50c762029a501561f3b23David 'Digit' Turner        case BT_HC_SOFT_RESET:
2345d8f37ad78fc66901af50c762029a501561f3b23David 'Digit' Turner            bt_hid_reset(s);
2355d8f37ad78fc66901af50c762029a501561f3b23David 'Digit' Turner            break;
2365d8f37ad78fc66901af50c762029a501561f3b23David 'Digit' Turner        case BT_HC_SUSPEND:
2375d8f37ad78fc66901af50c762029a501561f3b23David 'Digit' Turner            if (s->state == bt_state_ready)
2385d8f37ad78fc66901af50c762029a501561f3b23David 'Digit' Turner                s->state = bt_state_suspend;
2395d8f37ad78fc66901af50c762029a501561f3b23David 'Digit' Turner            else
2405d8f37ad78fc66901af50c762029a501561f3b23David 'Digit' Turner                ret = BT_HS_ERR_INVALID_PARAMETER;
2415d8f37ad78fc66901af50c762029a501561f3b23David 'Digit' Turner            break;
2425d8f37ad78fc66901af50c762029a501561f3b23David 'Digit' Turner        case BT_HC_EXIT_SUSPEND:
2435d8f37ad78fc66901af50c762029a501561f3b23David 'Digit' Turner            if (s->state == bt_state_suspend)
2445d8f37ad78fc66901af50c762029a501561f3b23David 'Digit' Turner                s->state = bt_state_ready;
2455d8f37ad78fc66901af50c762029a501561f3b23David 'Digit' Turner            else
2465d8f37ad78fc66901af50c762029a501561f3b23David 'Digit' Turner                ret = BT_HS_ERR_INVALID_PARAMETER;
2475d8f37ad78fc66901af50c762029a501561f3b23David 'Digit' Turner            break;
2485d8f37ad78fc66901af50c762029a501561f3b23David 'Digit' Turner        case BT_HC_VIRTUAL_CABLE_UNPLUG:
2495d8f37ad78fc66901af50c762029a501561f3b23David 'Digit' Turner            bt_hid_disconnect(s);
2505d8f37ad78fc66901af50c762029a501561f3b23David 'Digit' Turner            break;
2515d8f37ad78fc66901af50c762029a501561f3b23David 'Digit' Turner        default:
2525d8f37ad78fc66901af50c762029a501561f3b23David 'Digit' Turner            ret = BT_HS_ERR_INVALID_PARAMETER;
2535d8f37ad78fc66901af50c762029a501561f3b23David 'Digit' Turner        }
2545d8f37ad78fc66901af50c762029a501561f3b23David 'Digit' Turner        break;
2555d8f37ad78fc66901af50c762029a501561f3b23David 'Digit' Turner
2565d8f37ad78fc66901af50c762029a501561f3b23David 'Digit' Turner    case BT_GET_REPORT:
2575d8f37ad78fc66901af50c762029a501561f3b23David 'Digit' Turner        /* No ReportIDs declared.  */
2585d8f37ad78fc66901af50c762029a501561f3b23David 'Digit' Turner        if (((parameter & 8) && len != 3) ||
2595d8f37ad78fc66901af50c762029a501561f3b23David 'Digit' Turner                        (!(parameter & 8) && len != 1) ||
2605d8f37ad78fc66901af50c762029a501561f3b23David 'Digit' Turner                        s->state != bt_state_ready) {
2615d8f37ad78fc66901af50c762029a501561f3b23David 'Digit' Turner            ret = BT_HS_ERR_INVALID_PARAMETER;
2625d8f37ad78fc66901af50c762029a501561f3b23David 'Digit' Turner            break;
2635d8f37ad78fc66901af50c762029a501561f3b23David 'Digit' Turner        }
2645d8f37ad78fc66901af50c762029a501561f3b23David 'Digit' Turner        if (parameter & 8)
2655d8f37ad78fc66901af50c762029a501561f3b23David 'Digit' Turner            rlen = data[2] | (data[3] << 8);
2665d8f37ad78fc66901af50c762029a501561f3b23David 'Digit' Turner        else
2675d8f37ad78fc66901af50c762029a501561f3b23David 'Digit' Turner            rlen = INT_MAX;
2685d8f37ad78fc66901af50c762029a501561f3b23David 'Digit' Turner        switch (parameter & 3) {
2695d8f37ad78fc66901af50c762029a501561f3b23David 'Digit' Turner        case BT_DATA_OTHER:
2705d8f37ad78fc66901af50c762029a501561f3b23David 'Digit' Turner            ret = BT_HS_ERR_INVALID_PARAMETER;
2715d8f37ad78fc66901af50c762029a501561f3b23David 'Digit' Turner            break;
2725d8f37ad78fc66901af50c762029a501561f3b23David 'Digit' Turner        case BT_DATA_INPUT:
2735d8f37ad78fc66901af50c762029a501561f3b23David 'Digit' Turner            /* Here we can as well poll s->usbdev */
2745d8f37ad78fc66901af50c762029a501561f3b23David 'Digit' Turner            bt_hid_send_data(s->control, BT_DATA_INPUT,
2755d8f37ad78fc66901af50c762029a501561f3b23David 'Digit' Turner                            s->datain.buffer, MIN(rlen, s->datain.len));
2765d8f37ad78fc66901af50c762029a501561f3b23David 'Digit' Turner            break;
2775d8f37ad78fc66901af50c762029a501561f3b23David 'Digit' Turner        case BT_DATA_OUTPUT:
2785d8f37ad78fc66901af50c762029a501561f3b23David 'Digit' Turner            bt_hid_send_data(s->control, BT_DATA_OUTPUT,
2795d8f37ad78fc66901af50c762029a501561f3b23David 'Digit' Turner                            s->dataout.buffer, MIN(rlen, s->dataout.len));
2805d8f37ad78fc66901af50c762029a501561f3b23David 'Digit' Turner            break;
2815d8f37ad78fc66901af50c762029a501561f3b23David 'Digit' Turner        case BT_DATA_FEATURE:
2825d8f37ad78fc66901af50c762029a501561f3b23David 'Digit' Turner            bt_hid_send_data(s->control, BT_DATA_FEATURE,
2835d8f37ad78fc66901af50c762029a501561f3b23David 'Digit' Turner                            s->feature.buffer, MIN(rlen, s->feature.len));
2845d8f37ad78fc66901af50c762029a501561f3b23David 'Digit' Turner            break;
2855d8f37ad78fc66901af50c762029a501561f3b23David 'Digit' Turner        }
2865d8f37ad78fc66901af50c762029a501561f3b23David 'Digit' Turner        break;
2875d8f37ad78fc66901af50c762029a501561f3b23David 'Digit' Turner
2885d8f37ad78fc66901af50c762029a501561f3b23David 'Digit' Turner    case BT_SET_REPORT:
2895d8f37ad78fc66901af50c762029a501561f3b23David 'Digit' Turner        if (len < 2 || len > BT_HID_MTU || s->state != bt_state_ready ||
2905d8f37ad78fc66901af50c762029a501561f3b23David 'Digit' Turner                        (parameter & 3) == BT_DATA_OTHER ||
2915d8f37ad78fc66901af50c762029a501561f3b23David 'Digit' Turner                        (parameter & 3) == BT_DATA_INPUT) {
2925d8f37ad78fc66901af50c762029a501561f3b23David 'Digit' Turner            ret = BT_HS_ERR_INVALID_PARAMETER;
2935d8f37ad78fc66901af50c762029a501561f3b23David 'Digit' Turner            break;
2945d8f37ad78fc66901af50c762029a501561f3b23David 'Digit' Turner        }
2955d8f37ad78fc66901af50c762029a501561f3b23David 'Digit' Turner        s->data_type = parameter & 3;
2965d8f37ad78fc66901af50c762029a501561f3b23David 'Digit' Turner        if (s->data_type == BT_DATA_OUTPUT) {
2975d8f37ad78fc66901af50c762029a501561f3b23David 'Digit' Turner            s->dataout.len = len - 1;
2985d8f37ad78fc66901af50c762029a501561f3b23David 'Digit' Turner            memcpy(s->dataout.buffer, data + 1, s->dataout.len);
2995d8f37ad78fc66901af50c762029a501561f3b23David 'Digit' Turner        } else {
3005d8f37ad78fc66901af50c762029a501561f3b23David 'Digit' Turner            s->feature.len = len - 1;
3015d8f37ad78fc66901af50c762029a501561f3b23David 'Digit' Turner            memcpy(s->feature.buffer, data + 1, s->feature.len);
3025d8f37ad78fc66901af50c762029a501561f3b23David 'Digit' Turner        }
3035d8f37ad78fc66901af50c762029a501561f3b23David 'Digit' Turner        if (len == BT_HID_MTU)
3045d8f37ad78fc66901af50c762029a501561f3b23David 'Digit' Turner            s->state = bt_state_transaction;
3055d8f37ad78fc66901af50c762029a501561f3b23David 'Digit' Turner        else
3065d8f37ad78fc66901af50c762029a501561f3b23David 'Digit' Turner            bt_hid_out(s);
3075d8f37ad78fc66901af50c762029a501561f3b23David 'Digit' Turner        break;
3085d8f37ad78fc66901af50c762029a501561f3b23David 'Digit' Turner
3095d8f37ad78fc66901af50c762029a501561f3b23David 'Digit' Turner    case BT_GET_PROTOCOL:
3105d8f37ad78fc66901af50c762029a501561f3b23David 'Digit' Turner        if (len != 1 || s->state == bt_state_transaction) {
3115d8f37ad78fc66901af50c762029a501561f3b23David 'Digit' Turner            ret = BT_HS_ERR_INVALID_PARAMETER;
3125d8f37ad78fc66901af50c762029a501561f3b23David 'Digit' Turner            break;
3135d8f37ad78fc66901af50c762029a501561f3b23David 'Digit' Turner        }
3145d8f37ad78fc66901af50c762029a501561f3b23David 'Digit' Turner        *s->control->sdu_out(s->control, 1) = s->proto;
3155d8f37ad78fc66901af50c762029a501561f3b23David 'Digit' Turner        s->control->sdu_submit(s->control);
3165d8f37ad78fc66901af50c762029a501561f3b23David 'Digit' Turner        break;
3175d8f37ad78fc66901af50c762029a501561f3b23David 'Digit' Turner
3185d8f37ad78fc66901af50c762029a501561f3b23David 'Digit' Turner    case BT_SET_PROTOCOL:
3195d8f37ad78fc66901af50c762029a501561f3b23David 'Digit' Turner        if (len != 1 || s->state == bt_state_transaction ||
3205d8f37ad78fc66901af50c762029a501561f3b23David 'Digit' Turner                        (parameter != BT_HID_PROTO_BOOT &&
3215d8f37ad78fc66901af50c762029a501561f3b23David 'Digit' Turner                         parameter != BT_HID_PROTO_REPORT)) {
3225d8f37ad78fc66901af50c762029a501561f3b23David 'Digit' Turner            ret = BT_HS_ERR_INVALID_PARAMETER;
3235d8f37ad78fc66901af50c762029a501561f3b23David 'Digit' Turner            break;
3245d8f37ad78fc66901af50c762029a501561f3b23David 'Digit' Turner        }
3255d8f37ad78fc66901af50c762029a501561f3b23David 'Digit' Turner        s->proto = parameter;
3265d8f37ad78fc66901af50c762029a501561f3b23David 'Digit' Turner        s->usbdev->handle_control(s->usbdev, SET_PROTOCOL, s->proto, 0, 0,
3275d8f37ad78fc66901af50c762029a501561f3b23David 'Digit' Turner                                  NULL);
3285d8f37ad78fc66901af50c762029a501561f3b23David 'Digit' Turner        ret = BT_HS_SUCCESSFUL;
3295d8f37ad78fc66901af50c762029a501561f3b23David 'Digit' Turner        break;
3305d8f37ad78fc66901af50c762029a501561f3b23David 'Digit' Turner
3315d8f37ad78fc66901af50c762029a501561f3b23David 'Digit' Turner    case BT_GET_IDLE:
3325d8f37ad78fc66901af50c762029a501561f3b23David 'Digit' Turner        if (len != 1 || s->state == bt_state_transaction) {
3335d8f37ad78fc66901af50c762029a501561f3b23David 'Digit' Turner            ret = BT_HS_ERR_INVALID_PARAMETER;
3345d8f37ad78fc66901af50c762029a501561f3b23David 'Digit' Turner            break;
3355d8f37ad78fc66901af50c762029a501561f3b23David 'Digit' Turner        }
3365d8f37ad78fc66901af50c762029a501561f3b23David 'Digit' Turner        s->usbdev->handle_control(s->usbdev, GET_IDLE, 0, 0, 1,
3375d8f37ad78fc66901af50c762029a501561f3b23David 'Digit' Turner                        s->control->sdu_out(s->control, 1));
3385d8f37ad78fc66901af50c762029a501561f3b23David 'Digit' Turner        s->control->sdu_submit(s->control);
3395d8f37ad78fc66901af50c762029a501561f3b23David 'Digit' Turner        break;
3405d8f37ad78fc66901af50c762029a501561f3b23David 'Digit' Turner
3415d8f37ad78fc66901af50c762029a501561f3b23David 'Digit' Turner    case BT_SET_IDLE:
3425d8f37ad78fc66901af50c762029a501561f3b23David 'Digit' Turner        if (len != 2 || s->state == bt_state_transaction) {
3435d8f37ad78fc66901af50c762029a501561f3b23David 'Digit' Turner            ret = BT_HS_ERR_INVALID_PARAMETER;
3445d8f37ad78fc66901af50c762029a501561f3b23David 'Digit' Turner            break;
3455d8f37ad78fc66901af50c762029a501561f3b23David 'Digit' Turner        }
3465d8f37ad78fc66901af50c762029a501561f3b23David 'Digit' Turner
3475d8f37ad78fc66901af50c762029a501561f3b23David 'Digit' Turner        /* We don't need to know about the Idle Rate here really,
3485d8f37ad78fc66901af50c762029a501561f3b23David 'Digit' Turner         * so just pass it on to the device.  */
3495d8f37ad78fc66901af50c762029a501561f3b23David 'Digit' Turner        ret = s->usbdev->handle_control(s->usbdev,
3505d8f37ad78fc66901af50c762029a501561f3b23David 'Digit' Turner                        SET_IDLE, data[1], 0, 0, NULL) ?
3515d8f37ad78fc66901af50c762029a501561f3b23David 'Digit' Turner                BT_HS_SUCCESSFUL : BT_HS_ERR_INVALID_PARAMETER;
3525d8f37ad78fc66901af50c762029a501561f3b23David 'Digit' Turner        /* XXX: Does this generate a handshake? */
3535d8f37ad78fc66901af50c762029a501561f3b23David 'Digit' Turner        break;
3545d8f37ad78fc66901af50c762029a501561f3b23David 'Digit' Turner
3555d8f37ad78fc66901af50c762029a501561f3b23David 'Digit' Turner    case BT_DATC:
3565d8f37ad78fc66901af50c762029a501561f3b23David 'Digit' Turner        if (len > BT_HID_MTU || s->state != bt_state_transaction) {
3575d8f37ad78fc66901af50c762029a501561f3b23David 'Digit' Turner            ret = BT_HS_ERR_INVALID_PARAMETER;
3585d8f37ad78fc66901af50c762029a501561f3b23David 'Digit' Turner            break;
3595d8f37ad78fc66901af50c762029a501561f3b23David 'Digit' Turner        }
3605d8f37ad78fc66901af50c762029a501561f3b23David 'Digit' Turner        if (s->data_type == BT_DATA_OUTPUT) {
3615d8f37ad78fc66901af50c762029a501561f3b23David 'Digit' Turner            memcpy(s->dataout.buffer + s->dataout.len, data + 1, len - 1);
3625d8f37ad78fc66901af50c762029a501561f3b23David 'Digit' Turner            s->dataout.len += len - 1;
3635d8f37ad78fc66901af50c762029a501561f3b23David 'Digit' Turner        } else {
3645d8f37ad78fc66901af50c762029a501561f3b23David 'Digit' Turner            memcpy(s->feature.buffer + s->feature.len, data + 1, len - 1);
3655d8f37ad78fc66901af50c762029a501561f3b23David 'Digit' Turner            s->feature.len += len - 1;
3665d8f37ad78fc66901af50c762029a501561f3b23David 'Digit' Turner        }
3675d8f37ad78fc66901af50c762029a501561f3b23David 'Digit' Turner        if (len < BT_HID_MTU) {
3685d8f37ad78fc66901af50c762029a501561f3b23David 'Digit' Turner            bt_hid_out(s);
3695d8f37ad78fc66901af50c762029a501561f3b23David 'Digit' Turner            s->state = bt_state_ready;
3705d8f37ad78fc66901af50c762029a501561f3b23David 'Digit' Turner        }
3715d8f37ad78fc66901af50c762029a501561f3b23David 'Digit' Turner        break;
3725d8f37ad78fc66901af50c762029a501561f3b23David 'Digit' Turner
3735d8f37ad78fc66901af50c762029a501561f3b23David 'Digit' Turner    default:
3745d8f37ad78fc66901af50c762029a501561f3b23David 'Digit' Turner        ret = BT_HS_ERR_UNSUPPORTED_REQUEST;
3755d8f37ad78fc66901af50c762029a501561f3b23David 'Digit' Turner    }
3765d8f37ad78fc66901af50c762029a501561f3b23David 'Digit' Turner
3775d8f37ad78fc66901af50c762029a501561f3b23David 'Digit' Turner    if (ret != -1)
3785d8f37ad78fc66901af50c762029a501561f3b23David 'Digit' Turner        bt_hid_send_handshake(s, ret);
3795d8f37ad78fc66901af50c762029a501561f3b23David 'Digit' Turner}
3805d8f37ad78fc66901af50c762029a501561f3b23David 'Digit' Turner
3815d8f37ad78fc66901af50c762029a501561f3b23David 'Digit' Turnerstatic void bt_hid_control_sdu(void *opaque, const uint8_t *data, int len)
3825d8f37ad78fc66901af50c762029a501561f3b23David 'Digit' Turner{
3835d8f37ad78fc66901af50c762029a501561f3b23David 'Digit' Turner    struct bt_hid_device_s *hid = opaque;
3845d8f37ad78fc66901af50c762029a501561f3b23David 'Digit' Turner
3855d8f37ad78fc66901af50c762029a501561f3b23David 'Digit' Turner    bt_hid_control_transaction(hid, data, len);
3865d8f37ad78fc66901af50c762029a501561f3b23David 'Digit' Turner}
3875d8f37ad78fc66901af50c762029a501561f3b23David 'Digit' Turner
3885d8f37ad78fc66901af50c762029a501561f3b23David 'Digit' Turnerstatic void bt_hid_datain(void *opaque)
3895d8f37ad78fc66901af50c762029a501561f3b23David 'Digit' Turner{
3905d8f37ad78fc66901af50c762029a501561f3b23David 'Digit' Turner    struct bt_hid_device_s *hid = opaque;
3915d8f37ad78fc66901af50c762029a501561f3b23David 'Digit' Turner
3925d8f37ad78fc66901af50c762029a501561f3b23David 'Digit' Turner    /* If suspended, wake-up and send a wake-up event first.  We might
3935d8f37ad78fc66901af50c762029a501561f3b23David 'Digit' Turner     * want to also inspect the input report and ignore event like
3945d8f37ad78fc66901af50c762029a501561f3b23David 'Digit' Turner     * mouse movements until a button event occurs.  */
3955d8f37ad78fc66901af50c762029a501561f3b23David 'Digit' Turner    if (hid->state == bt_state_suspend) {
3965d8f37ad78fc66901af50c762029a501561f3b23David 'Digit' Turner        hid->state = bt_state_ready;
3975d8f37ad78fc66901af50c762029a501561f3b23David 'Digit' Turner    }
3985d8f37ad78fc66901af50c762029a501561f3b23David 'Digit' Turner
3995d8f37ad78fc66901af50c762029a501561f3b23David 'Digit' Turner    if (bt_hid_in(hid) > 0)
4005d8f37ad78fc66901af50c762029a501561f3b23David 'Digit' Turner        /* TODO: when in boot-mode precede any Input reports with the ReportID
4015d8f37ad78fc66901af50c762029a501561f3b23David 'Digit' Turner         * byte, here and in GetReport/SetReport on the Control channel.  */
4025d8f37ad78fc66901af50c762029a501561f3b23David 'Digit' Turner        bt_hid_send_data(hid->interrupt, BT_DATA_INPUT,
4035d8f37ad78fc66901af50c762029a501561f3b23David 'Digit' Turner                        hid->datain.buffer, hid->datain.len);
4045d8f37ad78fc66901af50c762029a501561f3b23David 'Digit' Turner}
4055d8f37ad78fc66901af50c762029a501561f3b23David 'Digit' Turner
4065d8f37ad78fc66901af50c762029a501561f3b23David 'Digit' Turnerstatic void bt_hid_interrupt_sdu(void *opaque, const uint8_t *data, int len)
4075d8f37ad78fc66901af50c762029a501561f3b23David 'Digit' Turner{
4085d8f37ad78fc66901af50c762029a501561f3b23David 'Digit' Turner    struct bt_hid_device_s *hid = opaque;
4095d8f37ad78fc66901af50c762029a501561f3b23David 'Digit' Turner
4105d8f37ad78fc66901af50c762029a501561f3b23David 'Digit' Turner    if (len > BT_HID_MTU || len < 1)
4115d8f37ad78fc66901af50c762029a501561f3b23David 'Digit' Turner        goto bad;
4125d8f37ad78fc66901af50c762029a501561f3b23David 'Digit' Turner    if ((data[0] & 3) != BT_DATA_OUTPUT)
4135d8f37ad78fc66901af50c762029a501561f3b23David 'Digit' Turner        goto bad;
4145d8f37ad78fc66901af50c762029a501561f3b23David 'Digit' Turner    if ((data[0] >> 4) == BT_DATA) {
4155d8f37ad78fc66901af50c762029a501561f3b23David 'Digit' Turner        if (hid->intr_state)
4165d8f37ad78fc66901af50c762029a501561f3b23David 'Digit' Turner            goto bad;
4175d8f37ad78fc66901af50c762029a501561f3b23David 'Digit' Turner
4185d8f37ad78fc66901af50c762029a501561f3b23David 'Digit' Turner        hid->data_type = BT_DATA_OUTPUT;
4195d8f37ad78fc66901af50c762029a501561f3b23David 'Digit' Turner        hid->intrdataout.len = 0;
4205d8f37ad78fc66901af50c762029a501561f3b23David 'Digit' Turner    } else if ((data[0] >> 4) == BT_DATC) {
4215d8f37ad78fc66901af50c762029a501561f3b23David 'Digit' Turner        if (!hid->intr_state)
4225d8f37ad78fc66901af50c762029a501561f3b23David 'Digit' Turner            goto bad;
4235d8f37ad78fc66901af50c762029a501561f3b23David 'Digit' Turner    } else
4245d8f37ad78fc66901af50c762029a501561f3b23David 'Digit' Turner        goto bad;
4255d8f37ad78fc66901af50c762029a501561f3b23David 'Digit' Turner
4265d8f37ad78fc66901af50c762029a501561f3b23David 'Digit' Turner    memcpy(hid->intrdataout.buffer + hid->intrdataout.len, data + 1, len - 1);
4275d8f37ad78fc66901af50c762029a501561f3b23David 'Digit' Turner    hid->intrdataout.len += len - 1;
4285d8f37ad78fc66901af50c762029a501561f3b23David 'Digit' Turner    hid->intr_state = (len == BT_HID_MTU);
4295d8f37ad78fc66901af50c762029a501561f3b23David 'Digit' Turner    if (!hid->intr_state) {
4305d8f37ad78fc66901af50c762029a501561f3b23David 'Digit' Turner        memcpy(hid->dataout.buffer, hid->intrdataout.buffer,
4315d8f37ad78fc66901af50c762029a501561f3b23David 'Digit' Turner                        hid->dataout.len = hid->intrdataout.len);
4325d8f37ad78fc66901af50c762029a501561f3b23David 'Digit' Turner        bt_hid_out(hid);
4335d8f37ad78fc66901af50c762029a501561f3b23David 'Digit' Turner    }
4345d8f37ad78fc66901af50c762029a501561f3b23David 'Digit' Turner
4355d8f37ad78fc66901af50c762029a501561f3b23David 'Digit' Turner    return;
4365d8f37ad78fc66901af50c762029a501561f3b23David 'Digit' Turnerbad:
4375d8f37ad78fc66901af50c762029a501561f3b23David 'Digit' Turner    fprintf(stderr, "%s: bad transaction on Interrupt channel.\n",
4385d8f37ad78fc66901af50c762029a501561f3b23David 'Digit' Turner                    __FUNCTION__);
4395d8f37ad78fc66901af50c762029a501561f3b23David 'Digit' Turner}
4405d8f37ad78fc66901af50c762029a501561f3b23David 'Digit' Turner
4415d8f37ad78fc66901af50c762029a501561f3b23David 'Digit' Turner/* "Virtual cable" plug/unplug event.  */
4425d8f37ad78fc66901af50c762029a501561f3b23David 'Digit' Turnerstatic void bt_hid_connected_update(struct bt_hid_device_s *hid)
4435d8f37ad78fc66901af50c762029a501561f3b23David 'Digit' Turner{
4445d8f37ad78fc66901af50c762029a501561f3b23David 'Digit' Turner    int prev = hid->connected;
4455d8f37ad78fc66901af50c762029a501561f3b23David 'Digit' Turner
4465d8f37ad78fc66901af50c762029a501561f3b23David 'Digit' Turner    hid->connected = hid->control && hid->interrupt;
4475d8f37ad78fc66901af50c762029a501561f3b23David 'Digit' Turner
4485d8f37ad78fc66901af50c762029a501561f3b23David 'Digit' Turner    /* Stop page-/inquiry-scanning when a host is connected.  */
4495d8f37ad78fc66901af50c762029a501561f3b23David 'Digit' Turner    hid->btdev.device.page_scan = !hid->connected;
4505d8f37ad78fc66901af50c762029a501561f3b23David 'Digit' Turner    hid->btdev.device.inquiry_scan = !hid->connected;
4515d8f37ad78fc66901af50c762029a501561f3b23David 'Digit' Turner
4525d8f37ad78fc66901af50c762029a501561f3b23David 'Digit' Turner    if (hid->connected && !prev) {
4535d8f37ad78fc66901af50c762029a501561f3b23David 'Digit' Turner        hid->usbdev->handle_reset(hid->usbdev);
4545d8f37ad78fc66901af50c762029a501561f3b23David 'Digit' Turner        hid->proto = BT_HID_PROTO_REPORT;
4555d8f37ad78fc66901af50c762029a501561f3b23David 'Digit' Turner    }
4565d8f37ad78fc66901af50c762029a501561f3b23David 'Digit' Turner
4575d8f37ad78fc66901af50c762029a501561f3b23David 'Digit' Turner    /* Should set HIDVirtualCable in SDP (possibly need to check that SDP
4585d8f37ad78fc66901af50c762029a501561f3b23David 'Digit' Turner     * isn't destroyed yet, in case we're being called from handle_destroy) */
4595d8f37ad78fc66901af50c762029a501561f3b23David 'Digit' Turner}
4605d8f37ad78fc66901af50c762029a501561f3b23David 'Digit' Turner
4615d8f37ad78fc66901af50c762029a501561f3b23David 'Digit' Turnerstatic void bt_hid_close_control(void *opaque)
4625d8f37ad78fc66901af50c762029a501561f3b23David 'Digit' Turner{
4635d8f37ad78fc66901af50c762029a501561f3b23David 'Digit' Turner    struct bt_hid_device_s *hid = opaque;
4645d8f37ad78fc66901af50c762029a501561f3b23David 'Digit' Turner
4655d8f37ad78fc66901af50c762029a501561f3b23David 'Digit' Turner    hid->control = NULL;
4665d8f37ad78fc66901af50c762029a501561f3b23David 'Digit' Turner    bt_hid_connected_update(hid);
4675d8f37ad78fc66901af50c762029a501561f3b23David 'Digit' Turner}
4685d8f37ad78fc66901af50c762029a501561f3b23David 'Digit' Turner
4695d8f37ad78fc66901af50c762029a501561f3b23David 'Digit' Turnerstatic void bt_hid_close_interrupt(void *opaque)
4705d8f37ad78fc66901af50c762029a501561f3b23David 'Digit' Turner{
4715d8f37ad78fc66901af50c762029a501561f3b23David 'Digit' Turner    struct bt_hid_device_s *hid = opaque;
4725d8f37ad78fc66901af50c762029a501561f3b23David 'Digit' Turner
4735d8f37ad78fc66901af50c762029a501561f3b23David 'Digit' Turner    hid->interrupt = NULL;
4745d8f37ad78fc66901af50c762029a501561f3b23David 'Digit' Turner    bt_hid_connected_update(hid);
4755d8f37ad78fc66901af50c762029a501561f3b23David 'Digit' Turner}
4765d8f37ad78fc66901af50c762029a501561f3b23David 'Digit' Turner
4775d8f37ad78fc66901af50c762029a501561f3b23David 'Digit' Turnerstatic int bt_hid_new_control_ch(struct bt_l2cap_device_s *dev,
4785d8f37ad78fc66901af50c762029a501561f3b23David 'Digit' Turner                struct bt_l2cap_conn_params_s *params)
4795d8f37ad78fc66901af50c762029a501561f3b23David 'Digit' Turner{
4805d8f37ad78fc66901af50c762029a501561f3b23David 'Digit' Turner    struct bt_hid_device_s *hid = (struct bt_hid_device_s *) dev;
4815d8f37ad78fc66901af50c762029a501561f3b23David 'Digit' Turner
4825d8f37ad78fc66901af50c762029a501561f3b23David 'Digit' Turner    if (hid->control)
4835d8f37ad78fc66901af50c762029a501561f3b23David 'Digit' Turner        return 1;
4845d8f37ad78fc66901af50c762029a501561f3b23David 'Digit' Turner
4855d8f37ad78fc66901af50c762029a501561f3b23David 'Digit' Turner    hid->control = params;
4865d8f37ad78fc66901af50c762029a501561f3b23David 'Digit' Turner    hid->control->opaque = hid;
4875d8f37ad78fc66901af50c762029a501561f3b23David 'Digit' Turner    hid->control->close = bt_hid_close_control;
4885d8f37ad78fc66901af50c762029a501561f3b23David 'Digit' Turner    hid->control->sdu_in = bt_hid_control_sdu;
4895d8f37ad78fc66901af50c762029a501561f3b23David 'Digit' Turner
4905d8f37ad78fc66901af50c762029a501561f3b23David 'Digit' Turner    bt_hid_connected_update(hid);
4915d8f37ad78fc66901af50c762029a501561f3b23David 'Digit' Turner
4925d8f37ad78fc66901af50c762029a501561f3b23David 'Digit' Turner    return 0;
4935d8f37ad78fc66901af50c762029a501561f3b23David 'Digit' Turner}
4945d8f37ad78fc66901af50c762029a501561f3b23David 'Digit' Turner
4955d8f37ad78fc66901af50c762029a501561f3b23David 'Digit' Turnerstatic int bt_hid_new_interrupt_ch(struct bt_l2cap_device_s *dev,
4965d8f37ad78fc66901af50c762029a501561f3b23David 'Digit' Turner                struct bt_l2cap_conn_params_s *params)
4975d8f37ad78fc66901af50c762029a501561f3b23David 'Digit' Turner{
4985d8f37ad78fc66901af50c762029a501561f3b23David 'Digit' Turner    struct bt_hid_device_s *hid = (struct bt_hid_device_s *) dev;
4995d8f37ad78fc66901af50c762029a501561f3b23David 'Digit' Turner
5005d8f37ad78fc66901af50c762029a501561f3b23David 'Digit' Turner    if (hid->interrupt)
5015d8f37ad78fc66901af50c762029a501561f3b23David 'Digit' Turner        return 1;
5025d8f37ad78fc66901af50c762029a501561f3b23David 'Digit' Turner
5035d8f37ad78fc66901af50c762029a501561f3b23David 'Digit' Turner    hid->interrupt = params;
5045d8f37ad78fc66901af50c762029a501561f3b23David 'Digit' Turner    hid->interrupt->opaque = hid;
5055d8f37ad78fc66901af50c762029a501561f3b23David 'Digit' Turner    hid->interrupt->close = bt_hid_close_interrupt;
5065d8f37ad78fc66901af50c762029a501561f3b23David 'Digit' Turner    hid->interrupt->sdu_in = bt_hid_interrupt_sdu;
5075d8f37ad78fc66901af50c762029a501561f3b23David 'Digit' Turner
5085d8f37ad78fc66901af50c762029a501561f3b23David 'Digit' Turner    bt_hid_connected_update(hid);
5095d8f37ad78fc66901af50c762029a501561f3b23David 'Digit' Turner
5105d8f37ad78fc66901af50c762029a501561f3b23David 'Digit' Turner    return 0;
5115d8f37ad78fc66901af50c762029a501561f3b23David 'Digit' Turner}
5125d8f37ad78fc66901af50c762029a501561f3b23David 'Digit' Turner
5135d8f37ad78fc66901af50c762029a501561f3b23David 'Digit' Turnerstatic void bt_hid_destroy(struct bt_device_s *dev)
5145d8f37ad78fc66901af50c762029a501561f3b23David 'Digit' Turner{
5155d8f37ad78fc66901af50c762029a501561f3b23David 'Digit' Turner    struct bt_hid_device_s *hid = (struct bt_hid_device_s *) dev;
5165d8f37ad78fc66901af50c762029a501561f3b23David 'Digit' Turner
5175d8f37ad78fc66901af50c762029a501561f3b23David 'Digit' Turner    if (hid->connected)
5185d8f37ad78fc66901af50c762029a501561f3b23David 'Digit' Turner        bt_hid_send_control(hid, BT_HC_VIRTUAL_CABLE_UNPLUG);
5195d8f37ad78fc66901af50c762029a501561f3b23David 'Digit' Turner    bt_l2cap_device_done(&hid->btdev);
5205d8f37ad78fc66901af50c762029a501561f3b23David 'Digit' Turner
5215d8f37ad78fc66901af50c762029a501561f3b23David 'Digit' Turner    hid->usbdev->handle_destroy(hid->usbdev);
5225d8f37ad78fc66901af50c762029a501561f3b23David 'Digit' Turner
5235d8f37ad78fc66901af50c762029a501561f3b23David 'Digit' Turner    qemu_free(hid);
5245d8f37ad78fc66901af50c762029a501561f3b23David 'Digit' Turner}
5255d8f37ad78fc66901af50c762029a501561f3b23David 'Digit' Turner
5265d8f37ad78fc66901af50c762029a501561f3b23David 'Digit' Turnerenum peripheral_minor_class {
5275d8f37ad78fc66901af50c762029a501561f3b23David 'Digit' Turner    class_other		= 0 << 4,
5285d8f37ad78fc66901af50c762029a501561f3b23David 'Digit' Turner    class_keyboard	= 1 << 4,
5295d8f37ad78fc66901af50c762029a501561f3b23David 'Digit' Turner    class_pointing	= 2 << 4,
5305d8f37ad78fc66901af50c762029a501561f3b23David 'Digit' Turner    class_combo		= 3 << 4,
5315d8f37ad78fc66901af50c762029a501561f3b23David 'Digit' Turner};
5325d8f37ad78fc66901af50c762029a501561f3b23David 'Digit' Turner
5335d8f37ad78fc66901af50c762029a501561f3b23David 'Digit' Turnerstatic struct bt_device_s *bt_hid_init(struct bt_scatternet_s *net,
5345d8f37ad78fc66901af50c762029a501561f3b23David 'Digit' Turner                USBDevice *dev, enum peripheral_minor_class minor)
5355d8f37ad78fc66901af50c762029a501561f3b23David 'Digit' Turner{
5365d8f37ad78fc66901af50c762029a501561f3b23David 'Digit' Turner    struct bt_hid_device_s *s = qemu_mallocz(sizeof(*s));
5375d8f37ad78fc66901af50c762029a501561f3b23David 'Digit' Turner    uint32_t class =
5385d8f37ad78fc66901af50c762029a501561f3b23David 'Digit' Turner            /* Format type */
5395d8f37ad78fc66901af50c762029a501561f3b23David 'Digit' Turner            (0 << 0) |
5405d8f37ad78fc66901af50c762029a501561f3b23David 'Digit' Turner            /* Device class */
5415d8f37ad78fc66901af50c762029a501561f3b23David 'Digit' Turner            (minor << 2) |
5425d8f37ad78fc66901af50c762029a501561f3b23David 'Digit' Turner            (5 << 8) |  /* "Peripheral" */
5435d8f37ad78fc66901af50c762029a501561f3b23David 'Digit' Turner            /* Service classes */
5445d8f37ad78fc66901af50c762029a501561f3b23David 'Digit' Turner            (1 << 13) | /* Limited discoverable mode */
5455d8f37ad78fc66901af50c762029a501561f3b23David 'Digit' Turner            (1 << 19);  /* Capturing device (?) */
5465d8f37ad78fc66901af50c762029a501561f3b23David 'Digit' Turner
5475d8f37ad78fc66901af50c762029a501561f3b23David 'Digit' Turner    bt_l2cap_device_init(&s->btdev, net);
5485d8f37ad78fc66901af50c762029a501561f3b23David 'Digit' Turner    bt_l2cap_sdp_init(&s->btdev);
5495d8f37ad78fc66901af50c762029a501561f3b23David 'Digit' Turner    bt_l2cap_psm_register(&s->btdev, BT_PSM_HID_CTRL,
5505d8f37ad78fc66901af50c762029a501561f3b23David 'Digit' Turner                    BT_HID_MTU, bt_hid_new_control_ch);
5515d8f37ad78fc66901af50c762029a501561f3b23David 'Digit' Turner    bt_l2cap_psm_register(&s->btdev, BT_PSM_HID_INTR,
5525d8f37ad78fc66901af50c762029a501561f3b23David 'Digit' Turner                    BT_HID_MTU, bt_hid_new_interrupt_ch);
5535d8f37ad78fc66901af50c762029a501561f3b23David 'Digit' Turner
5545d8f37ad78fc66901af50c762029a501561f3b23David 'Digit' Turner    s->usbdev = dev;
5555d8f37ad78fc66901af50c762029a501561f3b23David 'Digit' Turner    s->btdev.device.lmp_name = s->usbdev->devname;
5565d8f37ad78fc66901af50c762029a501561f3b23David 'Digit' Turner    usb_hid_datain_cb(s->usbdev, s, bt_hid_datain);
5575d8f37ad78fc66901af50c762029a501561f3b23David 'Digit' Turner
5585d8f37ad78fc66901af50c762029a501561f3b23David 'Digit' Turner    s->btdev.device.handle_destroy = bt_hid_destroy;
5595d8f37ad78fc66901af50c762029a501561f3b23David 'Digit' Turner
5605d8f37ad78fc66901af50c762029a501561f3b23David 'Digit' Turner    s->btdev.device.class[0] = (class >>  0) & 0xff;
5615d8f37ad78fc66901af50c762029a501561f3b23David 'Digit' Turner    s->btdev.device.class[1] = (class >>  8) & 0xff;
5625d8f37ad78fc66901af50c762029a501561f3b23David 'Digit' Turner    s->btdev.device.class[2] = (class >> 16) & 0xff;
5635d8f37ad78fc66901af50c762029a501561f3b23David 'Digit' Turner
5645d8f37ad78fc66901af50c762029a501561f3b23David 'Digit' Turner    return &s->btdev.device;
5655d8f37ad78fc66901af50c762029a501561f3b23David 'Digit' Turner}
5665d8f37ad78fc66901af50c762029a501561f3b23David 'Digit' Turner
5675d8f37ad78fc66901af50c762029a501561f3b23David 'Digit' Turnerstruct bt_device_s *bt_keyboard_init(struct bt_scatternet_s *net)
5685d8f37ad78fc66901af50c762029a501561f3b23David 'Digit' Turner{
5695d8f37ad78fc66901af50c762029a501561f3b23David 'Digit' Turner    return bt_hid_init(net, usb_keyboard_init(), class_keyboard);
5705d8f37ad78fc66901af50c762029a501561f3b23David 'Digit' Turner}
571