summaryrefslogtreecommitdiff
path: root/hw/usb/hid-logitech-dj.c
diff options
context:
space:
mode:
authorPeter Wu <peter@lekensteyn.nl>2014-03-18 21:21:51 +0100
committerPeter Wu <peter@lekensteyn.nl>2014-03-18 21:21:51 +0100
commit3a5922cc11f73269bfcd762997efc2c029a3b585 (patch)
tree04f129f72b655e1976b0a9456a17b4f93fe9fe7c /hw/usb/hid-logitech-dj.c
parent0bb8d924cbcd02069eb64893a2c2d48a20631866 (diff)
downloadqemu-3a5922cc11f73269bfcd762997efc2c029a3b585.tar.gz
unifying: WIP for HID++ support
Error queue is implemented, possible receiver and device properties are filled in (in the header). Signed-off-by: Peter Wu <peter@lekensteyn.nl>
Diffstat (limited to 'hw/usb/hid-logitech-dj.c')
-rw-r--r--hw/usb/hid-logitech-dj.c145
1 files changed, 145 insertions, 0 deletions
diff --git a/hw/usb/hid-logitech-dj.c b/hw/usb/hid-logitech-dj.c
new file mode 100644
index 0000000000..ab7db1be8a
--- /dev/null
+++ b/hw/usb/hid-logitech-dj.c
@@ -0,0 +1,145 @@
+/*
+ * Logitech Unifying receiver emulation (HID++ protocol).
+ *
+ * Copyright (c) 2014 Peter Wu <peter@lekensteyn.nl>
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
+ * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+ * THE SOFTWARE.
+ */
+#include "hw/hw.h"
+#include "ui/console.h"
+#include "hw/usb.h"
+#include "dump.h"
+#include "hw/input/hid.h"
+#include "hw/usb/hid-logitech-dj.h"
+
+/* Report IDs */
+enum {
+ HIDPP_SHORT = 0x10, /* 7 bytes */
+ HIDPP_LONG = 0x11, /* 20 bytes */
+ DJ_SHORT = 0x20, /* 15 bytes */
+ DJ_LONG = 0x21, /* 32 bytes */
+};
+
+/* returns the expected length of the report or 0 if invalid */
+static unsigned msg_report_length(HidppMsg *msg)
+{
+ switch (msg->report_id) {
+ case HIDPP_SHORT: return 7;
+ case HIDPP_LONG: return 20;
+ case DJ_SHORT: return 15;
+ case DJ_LONG: return 32;
+ default: return 0;
+ }
+}
+
+static void hidpp_queue_error(USBLtunifyState *s, HidppMsg *msg, uint8_t err)
+{
+ HidppMsgShort *report;
+ unsigned slot;
+
+ if (s->error_queue.n < LQUEUE_SIZE(s->error_queue)) {
+ slot = LQUEUE_WRAP(s->error_queue, s->queue.head + s->queue.n);
+ s->error_queue.n++;
+ report = &s->error_queue.reports[slot];
+ report->device_index = msg->device_index;
+ report->sub_id = msg->hidpp_s.sub_id;
+ report->address = msg->hidpp_s.address;
+ report->value[0] = err;
+ report->value[1] = report->value[2] = 0;
+ }
+}
+
+/* process a received report */
+static void hidpp_set_report(USBDevice *dev, USBPacket *p, uint8_t *data, size_t len)
+{
+ USBLtunifyState *s = DO_UPCAST(USBLtunifyState, dev, dev);
+ HidppMsg *msg = (HidppMsg *) data;
+ int report_len;
+
+ if (len < sizeof(HidppMsgShort)) {
+ goto fail;
+ }
+
+ report_len = msg_report_length(msg);
+ if (report_len > 0 && len >= report_len) {
+ if (s->queue.n < LQUEUE_SIZE(s->queue)) {
+ unsigned slot = LQUEUE_WRAP(s->queue, s->queue.head + s->queue.n);
+ s->queue.n++;
+ memcpy((uint8_t *) &s->queue.reports[slot], data, report_len);
+ } else {
+ /* queue full, cannot accept this report */
+ hidpp_queue_error(s, msg, HIDPP_ERR_BUSY);
+ }
+ } else {
+fail:
+ p->status = USB_RET_STALL;
+ }
+}
+
+void usb_ltunify_handle_control_hidpp(USBDevice *dev, USBPacket *p,
+ int request, int value, int index, int length, uint8_t *data)
+{
+ //USBLtunifyState *s = (USBLtunifyState *) dev;
+
+ switch (request) {
+ case HID_GET_REPORT:
+ /* FIXME */
+ break;
+ case HID_SET_REPORT:
+ hidpp_set_report(dev, p, data, length);
+ break;
+ default:
+ p->status = USB_RET_STALL;
+ break;
+ }
+}
+
+void usb_ltunify_handle_datain_hidpp(USBDevice *dev, USBPacket *p)
+{
+ USBLtunifyState *s = (USBLtunifyState *) dev;
+ int slot;
+ int len = 0;
+ HidppMsg *msg = NULL;
+
+
+ assert(p->pid == USB_TOKEN_IN);
+ assert(p->ep->nr == 3);
+
+ /* try to send a reply to a previously sent request if possible. */
+ if (s->error_queue.n > 0) {
+ slot = s->error_queue.head;
+ LQUEUE_INCR(s->error_queue, s->error_queue.head);
+ s->error_queue.n--;
+ msg = (HidppMsg *) &s->error_queue.reports[slot];
+ } else if (s->queue.n > 0) {
+ slot = s->queue.head;
+ LQUEUE_INCR(s->queue, s->queue.head);
+ s->queue.n--;
+ msg = &s->queue.reports[slot];
+ /* TODO: really process message instead of sending it back... */
+ }
+
+ if (msg != NULL) {
+ len = msg_report_length(msg);
+ assert(len > 0);
+ usb_dump_complete_data(s->usb_dump_state, p, (uint8_t *) msg, len);
+ usb_packet_copy(p, (uint8_t *) msg, len);
+ usb_dump_submit(s->usb_dump_state, p);
+ }
+}