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