Commit 47e699dc804e7b2dd448dafe6f519c8aae2cf8b9

Authored by balrog
1 parent 4d2d181c

Bluetooth HIDP emulation on top of usb-hid.c and L2CAP and SDP.


git-svn-id: svn://svn.savannah.nongnu.org/qemu/trunk@5347 c046a42c-6fe2-441c-8c8c-71466251a162
Makefile
... ... @@ -81,7 +81,7 @@ OBJS+=scsi-generic.o
81 81 OBJS+=usb.o usb-hub.o usb-linux.o usb-hid.o usb-msd.o usb-wacom.o
82 82 OBJS+=usb-serial.o usb-net.o
83 83 OBJS+=sd.o ssi-sd.o
84   -OBJS+=bt.o bt-host.o bt-l2cap.o bt-sdp.o bt-hci.o
  84 +OBJS+=bt.o bt-host.o bt-l2cap.o bt-sdp.o bt-hci.o bt-hid.o
85 85  
86 86 ifdef CONFIG_BRLAPI
87 87 OBJS+= baum.o
... ...
hw/bt-hid.c 0 → 100644
  1 +/*
  2 + * QEMU Bluetooth HID Profile wrapper for USB HID.
  3 + *
  4 + * Copyright (C) 2007-2008 OpenMoko, Inc.
  5 + * Written by Andrzej Zaborowski <andrew@openedhand.com>
  6 + *
  7 + * This program is free software; you can redistribute it and/or
  8 + * modify it under the terms of the GNU General Public License as
  9 + * published by the Free Software Foundation; either version 2 or
  10 + * (at your option) version 3 of the License.
  11 + *
  12 + * This program is distributed in the hope that it will be useful,
  13 + * but WITHOUT ANY WARRANTY; without even the implied warranty of
  14 + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
  15 + * GNU General Public License for more details.
  16 + *
  17 + * You should have received a copy of the GNU General Public License
  18 + * along with this program; if not, write to the Free Software
  19 + * Foundation, Inc., 59 Temple Place, Suite 330, Boston,
  20 + * MA 02111-1307 USA
  21 + */
  22 +
  23 +#include "qemu-common.h"
  24 +#include "usb.h"
  25 +#include "bt.h"
  26 +
  27 +enum hid_transaction_req {
  28 + BT_HANDSHAKE = 0x0,
  29 + BT_HID_CONTROL = 0x1,
  30 + BT_GET_REPORT = 0x4,
  31 + BT_SET_REPORT = 0x5,
  32 + BT_GET_PROTOCOL = 0x6,
  33 + BT_SET_PROTOCOL = 0x7,
  34 + BT_GET_IDLE = 0x8,
  35 + BT_SET_IDLE = 0x9,
  36 + BT_DATA = 0xa,
  37 + BT_DATC = 0xb,
  38 +};
  39 +
  40 +enum hid_transaction_handshake {
  41 + BT_HS_SUCCESSFUL = 0x0,
  42 + BT_HS_NOT_READY = 0x1,
  43 + BT_HS_ERR_INVALID_REPORT_ID = 0x2,
  44 + BT_HS_ERR_UNSUPPORTED_REQUEST = 0x3,
  45 + BT_HS_ERR_INVALID_PARAMETER = 0x4,
  46 + BT_HS_ERR_UNKNOWN = 0xe,
  47 + BT_HS_ERR_FATAL = 0xf,
  48 +};
  49 +
  50 +enum hid_transaction_control {
  51 + BT_HC_NOP = 0x0,
  52 + BT_HC_HARD_RESET = 0x1,
  53 + BT_HC_SOFT_RESET = 0x2,
  54 + BT_HC_SUSPEND = 0x3,
  55 + BT_HC_EXIT_SUSPEND = 0x4,
  56 + BT_HC_VIRTUAL_CABLE_UNPLUG = 0x5,
  57 +};
  58 +
  59 +enum hid_protocol {
  60 + BT_HID_PROTO_BOOT = 0,
  61 + BT_HID_PROTO_REPORT = 1,
  62 +};
  63 +
  64 +enum hid_boot_reportid {
  65 + BT_HID_BOOT_INVALID = 0,
  66 + BT_HID_BOOT_KEYBOARD,
  67 + BT_HID_BOOT_MOUSE,
  68 +};
  69 +
  70 +enum hid_data_pkt {
  71 + BT_DATA_OTHER = 0,
  72 + BT_DATA_INPUT,
  73 + BT_DATA_OUTPUT,
  74 + BT_DATA_FEATURE,
  75 +};
  76 +
  77 +#define BT_HID_MTU 48
  78 +
  79 +/* HID interface requests */
  80 +#define GET_REPORT 0xa101
  81 +#define GET_IDLE 0xa102
  82 +#define GET_PROTOCOL 0xa103
  83 +#define SET_REPORT 0x2109
  84 +#define SET_IDLE 0x210a
  85 +#define SET_PROTOCOL 0x210b
  86 +
  87 +struct bt_hid_device_s {
  88 + struct bt_l2cap_device_s btdev;
  89 + struct bt_l2cap_conn_params_s *control;
  90 + struct bt_l2cap_conn_params_s *interrupt;
  91 + USBDevice *usbdev;
  92 +
  93 + int proto;
  94 + int connected;
  95 + int data_type;
  96 + int intr_state;
  97 + struct {
  98 + int len;
  99 + uint8_t buffer[1024];
  100 + } dataother, datain, dataout, feature, intrdataout;
  101 + enum {
  102 + bt_state_ready,
  103 + bt_state_transaction,
  104 + bt_state_suspend,
  105 + } state;
  106 +};
  107 +
  108 +static void bt_hid_reset(struct bt_hid_device_s *s)
  109 +{
  110 + struct bt_scatternet_s *net = s->btdev.device.net;
  111 +
  112 + /* Go as far as... */
  113 + bt_l2cap_device_done(&s->btdev);
  114 + bt_l2cap_device_init(&s->btdev, net);
  115 +
  116 + s->usbdev->handle_reset(s->usbdev);
  117 + s->proto = BT_HID_PROTO_REPORT;
  118 + s->state = bt_state_ready;
  119 + s->dataother.len = 0;
  120 + s->datain.len = 0;
  121 + s->dataout.len = 0;
  122 + s->feature.len = 0;
  123 + s->intrdataout.len = 0;
  124 + s->intr_state = 0;
  125 +}
  126 +
  127 +static int bt_hid_out(struct bt_hid_device_s *s)
  128 +{
  129 + USBPacket p;
  130 +
  131 + if (s->data_type == BT_DATA_OUTPUT) {
  132 + p.pid = USB_TOKEN_OUT;
  133 + p.devep = 1;
  134 + p.data = s->dataout.buffer;
  135 + p.len = s->dataout.len;
  136 + s->dataout.len = s->usbdev->handle_data(s->usbdev, &p);
  137 +
  138 + return s->dataout.len;
  139 + }
  140 +
  141 + if (s->data_type == BT_DATA_FEATURE) {
  142 + /* XXX:
  143 + * does this send a USB_REQ_CLEAR_FEATURE/USB_REQ_SET_FEATURE
  144 + * or a SET_REPORT? */
  145 + p.devep = 0;
  146 + }
  147 +
  148 + return -1;
  149 +}
  150 +
  151 +static int bt_hid_in(struct bt_hid_device_s *s)
  152 +{
  153 + USBPacket p;
  154 +
  155 + p.pid = USB_TOKEN_IN;
  156 + p.devep = 1;
  157 + p.data = s->datain.buffer;
  158 + p.len = sizeof(s->datain.buffer);
  159 + s->datain.len = s->usbdev->handle_data(s->usbdev, &p);
  160 +
  161 + return s->datain.len;
  162 +}
  163 +
  164 +static void bt_hid_send_handshake(struct bt_hid_device_s *s, int result)
  165 +{
  166 + *s->control->sdu_out(s->control, 1) =
  167 + (BT_HANDSHAKE << 4) | result;
  168 + s->control->sdu_submit(s->control);
  169 +}
  170 +
  171 +static void bt_hid_send_control(struct bt_hid_device_s *s, int operation)
  172 +{
  173 + *s->control->sdu_out(s->control, 1) =
  174 + (BT_HID_CONTROL << 4) | operation;
  175 + s->control->sdu_submit(s->control);
  176 +}
  177 +
  178 +static void bt_hid_disconnect(struct bt_hid_device_s *s)
  179 +{
  180 + /* Disconnect s->control and s->interrupt */
  181 +}
  182 +
  183 +static void bt_hid_send_data(struct bt_l2cap_conn_params_s *ch, int type,
  184 + const uint8_t *data, int len)
  185 +{
  186 + uint8_t *pkt, hdr = (BT_DATA << 4) | type;
  187 + int plen;
  188 +
  189 + do {
  190 + plen = MIN(len, ch->remote_mtu - 1);
  191 + pkt = ch->sdu_out(ch, plen + 1);
  192 +
  193 + pkt[0] = hdr;
  194 + if (plen)
  195 + memcpy(pkt + 1, data, plen);
  196 + ch->sdu_submit(ch);
  197 +
  198 + len -= plen;
  199 + data += plen;
  200 + hdr = (BT_DATC << 4) | type;
  201 + } while (plen == ch->remote_mtu - 1);
  202 +}
  203 +
  204 +static void bt_hid_control_transaction(struct bt_hid_device_s *s,
  205 + const uint8_t *data, int len)
  206 +{
  207 + uint8_t type, parameter;
  208 + int rlen, ret = -1;
  209 + if (len < 1)
  210 + return;
  211 +
  212 + type = data[0] >> 4;
  213 + parameter = data[0] & 0xf;
  214 +
  215 + switch (type) {
  216 + case BT_HANDSHAKE:
  217 + case BT_DATA:
  218 + switch (parameter) {
  219 + default:
  220 + /* These are not expected to be sent this direction. */
  221 + ret = BT_HS_ERR_INVALID_PARAMETER;
  222 + }
  223 + break;
  224 +
  225 + case BT_HID_CONTROL:
  226 + if (len != 1 || (parameter != BT_HC_VIRTUAL_CABLE_UNPLUG &&
  227 + s->state == bt_state_transaction)) {
  228 + ret = BT_HS_ERR_INVALID_PARAMETER;
  229 + break;
  230 + }
  231 + switch (parameter) {
  232 + case BT_HC_NOP:
  233 + break;
  234 + case BT_HC_HARD_RESET:
  235 + case BT_HC_SOFT_RESET:
  236 + bt_hid_reset(s);
  237 + break;
  238 + case BT_HC_SUSPEND:
  239 + if (s->state == bt_state_ready)
  240 + s->state = bt_state_suspend;
  241 + else
  242 + ret = BT_HS_ERR_INVALID_PARAMETER;
  243 + break;
  244 + case BT_HC_EXIT_SUSPEND:
  245 + if (s->state == bt_state_suspend)
  246 + s->state = bt_state_ready;
  247 + else
  248 + ret = BT_HS_ERR_INVALID_PARAMETER;
  249 + break;
  250 + case BT_HC_VIRTUAL_CABLE_UNPLUG:
  251 + bt_hid_disconnect(s);
  252 + break;
  253 + default:
  254 + ret = BT_HS_ERR_INVALID_PARAMETER;
  255 + }
  256 + break;
  257 +
  258 + case BT_GET_REPORT:
  259 + /* No ReportIDs declared. */
  260 + if (((parameter & 8) && len != 3) ||
  261 + (!(parameter & 8) && len != 1) ||
  262 + s->state != bt_state_ready) {
  263 + ret = BT_HS_ERR_INVALID_PARAMETER;
  264 + break;
  265 + }
  266 + if (parameter & 8)
  267 + rlen = data[2] | (data[3] << 8);
  268 + else
  269 + rlen = INT_MAX;
  270 + switch (parameter & 3) {
  271 + case BT_DATA_OTHER:
  272 + ret = BT_HS_ERR_INVALID_PARAMETER;
  273 + break;
  274 + case BT_DATA_INPUT:
  275 + /* Here we can as well poll s->usbdev */
  276 + bt_hid_send_data(s->control, BT_DATA_INPUT,
  277 + s->datain.buffer, MIN(rlen, s->datain.len));
  278 + break;
  279 + case BT_DATA_OUTPUT:
  280 + bt_hid_send_data(s->control, BT_DATA_OUTPUT,
  281 + s->dataout.buffer, MIN(rlen, s->dataout.len));
  282 + break;
  283 + case BT_DATA_FEATURE:
  284 + bt_hid_send_data(s->control, BT_DATA_FEATURE,
  285 + s->feature.buffer, MIN(rlen, s->feature.len));
  286 + break;
  287 + }
  288 + break;
  289 +
  290 + case BT_SET_REPORT:
  291 + if (len < 2 || len > BT_HID_MTU || s->state != bt_state_ready ||
  292 + (parameter & 3) == BT_DATA_OTHER ||
  293 + (parameter & 3) == BT_DATA_INPUT) {
  294 + ret = BT_HS_ERR_INVALID_PARAMETER;
  295 + break;
  296 + }
  297 + s->data_type = parameter & 3;
  298 + if (s->data_type == BT_DATA_OUTPUT) {
  299 + s->dataout.len = len - 1;
  300 + memcpy(s->dataout.buffer, data + 1, s->dataout.len);
  301 + } else {
  302 + s->feature.len = len - 1;
  303 + memcpy(s->feature.buffer, data + 1, s->feature.len);
  304 + }
  305 + if (len == BT_HID_MTU)
  306 + s->state = bt_state_transaction;
  307 + else
  308 + bt_hid_out(s);
  309 + break;
  310 +
  311 + case BT_GET_PROTOCOL:
  312 + if (len != 1 || s->state == bt_state_transaction) {
  313 + ret = BT_HS_ERR_INVALID_PARAMETER;
  314 + break;
  315 + }
  316 + *s->control->sdu_out(s->control, 1) = s->proto;
  317 + s->control->sdu_submit(s->control);
  318 + break;
  319 +
  320 + case BT_SET_PROTOCOL:
  321 + if (len != 1 || s->state == bt_state_transaction ||
  322 + (parameter != BT_HID_PROTO_BOOT &&
  323 + parameter != BT_HID_PROTO_REPORT)) {
  324 + ret = BT_HS_ERR_INVALID_PARAMETER;
  325 + break;
  326 + }
  327 + s->proto = parameter;
  328 + s->usbdev->handle_control(s->usbdev, SET_PROTOCOL, s->proto, 0, 0, 0);
  329 + ret = BT_HS_SUCCESSFUL;
  330 + break;
  331 +
  332 + case BT_GET_IDLE:
  333 + if (len != 1 || s->state == bt_state_transaction) {
  334 + ret = BT_HS_ERR_INVALID_PARAMETER;
  335 + break;
  336 + }
  337 + s->usbdev->handle_control(s->usbdev, GET_IDLE, 0, 0, 1,
  338 + s->control->sdu_out(s->control, 1));
  339 + s->control->sdu_submit(s->control);
  340 + break;
  341 +
  342 + case BT_SET_IDLE:
  343 + if (len != 2 || s->state == bt_state_transaction) {
  344 + ret = BT_HS_ERR_INVALID_PARAMETER;
  345 + break;
  346 + }
  347 +
  348 + /* We don't need to know about the Idle Rate here really,
  349 + * so just pass it on to the device. */
  350 + ret = s->usbdev->handle_control(s->usbdev,
  351 + SET_IDLE, data[1], 0, 0, 0) ?
  352 + BT_HS_SUCCESSFUL : BT_HS_ERR_INVALID_PARAMETER;
  353 + /* XXX: Does this generate a handshake? */
  354 + break;
  355 +
  356 + case BT_DATC:
  357 + if (len > BT_HID_MTU || s->state != bt_state_transaction) {
  358 + ret = BT_HS_ERR_INVALID_PARAMETER;
  359 + break;
  360 + }
  361 + if (s->data_type == BT_DATA_OUTPUT) {
  362 + memcpy(s->dataout.buffer + s->dataout.len, data + 1, len - 1);
  363 + s->dataout.len += len - 1;
  364 + } else {
  365 + memcpy(s->feature.buffer + s->feature.len, data + 1, len - 1);
  366 + s->feature.len += len - 1;
  367 + }
  368 + if (len < BT_HID_MTU) {
  369 + bt_hid_out(s);
  370 + s->state = bt_state_ready;
  371 + }
  372 + break;
  373 +
  374 + default:
  375 + ret = BT_HS_ERR_UNSUPPORTED_REQUEST;
  376 + }
  377 +
  378 + if (ret != -1)
  379 + bt_hid_send_handshake(s, ret);
  380 +}
  381 +
  382 +static void bt_hid_control_sdu(void *opaque, const uint8_t *data, int len)
  383 +{
  384 + struct bt_hid_device_s *hid = opaque;
  385 +
  386 + return bt_hid_control_transaction(hid, data, len);
  387 +}
  388 +
  389 +static void bt_hid_datain(void *opaque)
  390 +{
  391 + struct bt_hid_device_s *hid = opaque;
  392 +
  393 + /* If suspended, wake-up and send a wake-up event first. We might
  394 + * want to also inspect the input report and ignore event like
  395 + * mouse movements until a button event occurs. */
  396 + if (hid->state == bt_state_suspend) {
  397 + hid->state = bt_state_ready;
  398 + }
  399 +
  400 + if (bt_hid_in(hid) > 0)
  401 + /* TODO: when in boot-mode precede any Input reports with the ReportID
  402 + * byte, here and in GetReport/SetReport on the Control channel. */
  403 + bt_hid_send_data(hid->interrupt, BT_DATA_INPUT,
  404 + hid->datain.buffer, hid->datain.len);
  405 +}
  406 +
  407 +static void bt_hid_interrupt_sdu(void *opaque, const uint8_t *data, int len)
  408 +{
  409 + struct bt_hid_device_s *hid = opaque;
  410 +
  411 + if (len > BT_HID_MTU || len < 1)
  412 + goto bad;
  413 + if ((data[0] & 3) != BT_DATA_OUTPUT)
  414 + goto bad;
  415 + if ((data[0] >> 4) == BT_DATA) {
  416 + if (hid->intr_state)
  417 + goto bad;
  418 +
  419 + hid->data_type = BT_DATA_OUTPUT;
  420 + hid->intrdataout.len = 0;
  421 + } else if ((data[0] >> 4) == BT_DATC) {
  422 + if (!hid->intr_state)
  423 + goto bad;
  424 + } else
  425 + goto bad;
  426 +
  427 + memcpy(hid->intrdataout.buffer + hid->intrdataout.len, data + 1, len - 1);
  428 + hid->intrdataout.len += len - 1;
  429 + hid->intr_state = (len == BT_HID_MTU);
  430 + if (!hid->intr_state) {
  431 + memcpy(hid->dataout.buffer, hid->intrdataout.buffer,
  432 + hid->dataout.len = hid->intrdataout.len);
  433 + bt_hid_out(hid);
  434 + }
  435 +
  436 + return;
  437 +bad:
  438 + fprintf(stderr, "%s: bad transaction on Interrupt channel.\n",
  439 + __FUNCTION__);
  440 +}
  441 +
  442 +/* "Virtual cable" plug/unplug event. */
  443 +static void bt_hid_connected_update(struct bt_hid_device_s *hid)
  444 +{
  445 + int prev = hid->connected;
  446 +
  447 + hid->connected = hid->control && hid->interrupt;
  448 +
  449 + /* Stop page-/inquiry-scanning when a host is connected. */
  450 + hid->btdev.device.page_scan = !hid->connected;
  451 + hid->btdev.device.inquiry_scan = !hid->connected;
  452 +
  453 + if (hid->connected && !prev) {
  454 + hid->usbdev->handle_reset(hid->usbdev);
  455 + hid->proto = BT_HID_PROTO_REPORT;
  456 + }
  457 +
  458 + /* Should set HIDVirtualCable in SDP (possibly need to check that SDP
  459 + * isn't destroyed yet, in case we're being called from handle_destroy) */
  460 +}
  461 +
  462 +static void bt_hid_close_control(void *opaque)
  463 +{
  464 + struct bt_hid_device_s *hid = opaque;
  465 +
  466 + hid->control = 0;
  467 + bt_hid_connected_update(hid);
  468 +}
  469 +
  470 +static void bt_hid_close_interrupt(void *opaque)
  471 +{
  472 + struct bt_hid_device_s *hid = opaque;
  473 +
  474 + hid->interrupt = 0;
  475 + bt_hid_connected_update(hid);
  476 +}
  477 +
  478 +static int bt_hid_new_control_ch(struct bt_l2cap_device_s *dev,
  479 + struct bt_l2cap_conn_params_s *params)
  480 +{
  481 + struct bt_hid_device_s *hid = (struct bt_hid_device_s *) dev;
  482 +
  483 + if (hid->control)
  484 + return 1;
  485 +
  486 + hid->control = params;
  487 + hid->control->opaque = hid;
  488 + hid->control->close = bt_hid_close_control;
  489 + hid->control->sdu_in = bt_hid_control_sdu;
  490 +
  491 + bt_hid_connected_update(hid);
  492 +
  493 + return 0;
  494 +}
  495 +
  496 +static int bt_hid_new_interrupt_ch(struct bt_l2cap_device_s *dev,
  497 + struct bt_l2cap_conn_params_s *params)
  498 +{
  499 + struct bt_hid_device_s *hid = (struct bt_hid_device_s *) dev;
  500 +
  501 + if (hid->interrupt)
  502 + return 1;
  503 +
  504 + hid->interrupt = params;
  505 + hid->interrupt->opaque = hid;
  506 + hid->interrupt->close = bt_hid_close_interrupt;
  507 + hid->interrupt->sdu_in = bt_hid_interrupt_sdu;
  508 +
  509 + bt_hid_connected_update(hid);
  510 +
  511 + return 0;
  512 +}
  513 +
  514 +static void bt_hid_destroy(struct bt_device_s *dev)
  515 +{
  516 + struct bt_hid_device_s *hid = (struct bt_hid_device_s *) dev;
  517 +
  518 + if (hid->connected)
  519 + bt_hid_send_control(hid, BT_HC_VIRTUAL_CABLE_UNPLUG);
  520 + bt_l2cap_device_done(&hid->btdev);
  521 +
  522 + hid->usbdev->handle_destroy(hid->usbdev);
  523 +
  524 + qemu_free(hid);
  525 +}
  526 +
  527 +enum peripheral_minor_class {
  528 + class_other = 0 << 4,
  529 + class_keyboard = 1 << 4,
  530 + class_pointing = 2 << 4,
  531 + class_combo = 3 << 4,
  532 +};
  533 +
  534 +static struct bt_device_s *bt_hid_init(struct bt_scatternet_s *net,
  535 + USBDevice *dev, enum peripheral_minor_class minor)
  536 +{
  537 + struct bt_hid_device_s *s = qemu_mallocz(sizeof(*s));
  538 + uint32_t class =
  539 + /* Format type */
  540 + (0 << 0) |
  541 + /* Device class */
  542 + (minor << 2) |
  543 + (5 << 8) | /* "Peripheral" */
  544 + /* Service classes */
  545 + (1 << 13) | /* Limited discoverable mode */
  546 + (1 << 19); /* Capturing device (?) */
  547 +
  548 + bt_l2cap_device_init(&s->btdev, net);
  549 + bt_l2cap_sdp_init(&s->btdev);
  550 + bt_l2cap_psm_register(&s->btdev, BT_PSM_HID_CTRL,
  551 + BT_HID_MTU, bt_hid_new_control_ch);
  552 + bt_l2cap_psm_register(&s->btdev, BT_PSM_HID_INTR,
  553 + BT_HID_MTU, bt_hid_new_interrupt_ch);
  554 +
  555 + s->usbdev = dev;
  556 + s->btdev.device.lmp_name = s->usbdev->devname;
  557 + usb_hid_datain_cb(s->usbdev, s, bt_hid_datain);
  558 +
  559 + s->btdev.device.handle_destroy = bt_hid_destroy;
  560 +
  561 + s->btdev.device.class[0] = (class >> 0) & 0xff;
  562 + s->btdev.device.class[1] = (class >> 8) & 0xff;
  563 + s->btdev.device.class[2] = (class >> 16) & 0xff;
  564 +
  565 + return &s->btdev.device;
  566 +}
  567 +
  568 +struct bt_device_s *bt_keyboard_init(struct bt_scatternet_s *net)
  569 +{
  570 + return bt_hid_init(net, usb_keyboard_init(), class_keyboard);
  571 +}
... ...
hw/usb-hid.c
... ... @@ -67,6 +67,8 @@ typedef struct USBHIDState {
67 67 int protocol;
68 68 int idle;
69 69 int changed;
  70 + void *datain_opaque;
  71 + void (*datain)(void *);
70 72 } USBHIDState;
71 73  
72 74 /* mostly the same values as the Bochs USB Mouse device */
... ... @@ -402,6 +404,14 @@ static const uint8_t usb_hid_usage_keys[0x100] = {
402 404 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
403 405 };
404 406  
  407 +static void usb_hid_changed(USBHIDState *hs)
  408 +{
  409 + hs->changed = 1;
  410 +
  411 + if (hs->datain)
  412 + hs->datain(hs->datain_opaque);
  413 +}
  414 +
405 415 static void usb_mouse_event(void *opaque,
406 416 int dx1, int dy1, int dz1, int buttons_state)
407 417 {
... ... @@ -412,7 +422,8 @@ static void usb_mouse_event(void *opaque,
412 422 s->dy += dy1;
413 423 s->dz += dz1;
414 424 s->buttons_state = buttons_state;
415   - hs->changed = 1;
  425 +
  426 + usb_hid_changed(hs);
416 427 }
417 428  
418 429 static void usb_tablet_event(void *opaque,
... ... @@ -425,7 +436,8 @@ static void usb_tablet_event(void *opaque,
425 436 s->y = y;
426 437 s->dz += dz;
427 438 s->buttons_state = buttons_state;
428   - hs->changed = 1;
  439 +
  440 + usb_hid_changed(hs);
429 441 }
430 442  
431 443 static void usb_keyboard_event(void *opaque, int keycode)
... ... @@ -439,8 +451,6 @@ static void usb_keyboard_event(void *opaque, int keycode)
439 451 hid_code = usb_hid_usage_keys[key | ((s->modifiers >> 1) & (1 << 7))];
440 452 s->modifiers &= ~(1 << 8);
441 453  
442   - hs->changed = 1;
443   -
444 454 switch (hid_code) {
445 455 case 0x00:
446 456 return;
... ... @@ -465,15 +475,23 @@ static void usb_keyboard_event(void *opaque, int keycode)
465 475 if (s->key[i] == hid_code) {
466 476 s->key[i] = s->key[-- s->keys];
467 477 s->key[s->keys] = 0x00;
468   - return;
  478 + usb_hid_changed(hs);
  479 + break;
469 480 }
  481 + if (i < 0)
  482 + return;
470 483 } else {
471 484 for (i = s->keys - 1; i >= 0; i --)
472 485 if (s->key[i] == hid_code)
473   - return;
474   - if (s->keys < sizeof(s->key))
475   - s->key[s->keys ++] = hid_code;
  486 + break;
  487 + if (i < 0) {
  488 + if (s->keys < sizeof(s->key))
  489 + s->key[s->keys ++] = hid_code;
  490 + } else
  491 + return;
476 492 }
  493 +
  494 + usb_hid_changed(hs);
477 495 }
478 496  
479 497 static inline int int_clamp(int val, int vmin, int vmax)
... ... @@ -894,3 +912,11 @@ USBDevice *usb_keyboard_init(void)
894 912  
895 913 return (USBDevice *) s;
896 914 }
  915 +
  916 +void usb_hid_datain_cb(USBDevice *dev, void *opaque, void (*datain)(void *))
  917 +{
  918 + USBHIDState *s = (USBHIDState *)dev;
  919 +
  920 + s->datain_opaque = opaque;
  921 + s->datain = datain;
  922 +}
... ...
hw/usb.h
... ... @@ -247,6 +247,7 @@ void usb_host_info(void);
247 247 USBDevice *usb_mouse_init(void);
248 248 USBDevice *usb_tablet_init(void);
249 249 USBDevice *usb_keyboard_init(void);
  250 +void usb_hid_datain_cb(USBDevice *dev, void *opaque, void (*datain)(void *));
250 251  
251 252 /* usb-msd.c */
252 253 USBDevice *usb_msd_init(const char *filename);
... ...