From 3a5922cc11f73269bfcd762997efc2c029a3b585 Mon Sep 17 00:00:00 2001 From: Peter Wu Date: Tue, 18 Mar 2014 21:21:51 +0100 Subject: 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 --- hw/usb/hid-logitech-dj.c | 145 +++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 145 insertions(+) create mode 100644 hw/usb/hid-logitech-dj.c (limited to 'hw/usb/hid-logitech-dj.c') 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 + * + * 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); + } +} -- cgit v1.2.1