summaryrefslogtreecommitdiff
path: root/hw/usb/hid-logitech-dj.c
diff options
context:
space:
mode:
authorPeter Wu <peter@lekensteyn.nl>2014-03-19 15:18:50 +0100
committerPeter Wu <peter@lekensteyn.nl>2014-03-19 15:18:50 +0100
commit198529586fff48da10e4c5d56c9a44db2b6a0a1d (patch)
tree573ee7ae04f3f96b0a24e666396ffef75eb0839a /hw/usb/hid-logitech-dj.c
parent4e6cc45456993307d605dfe50b5e370ba93fac9d (diff)
downloadqemu-198529586fff48da10e4c5d56c9a44db2b6a0a1d.tar.gz
unifying: move mode to LHIdDevice, init/reset functionality
WIP (partially untested). Handle input reports, respond with HID events if there are no queued output reports. Implemented input report processing for receiver: 0x80 (Switch and Keep-Alive), 0x81 (Get Paired Devices). 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.c157
1 files changed, 157 insertions, 0 deletions
diff --git a/hw/usb/hid-logitech-dj.c b/hw/usb/hid-logitech-dj.c
index 6b49e816c0..c421b6639f 100644
--- a/hw/usb/hid-logitech-dj.c
+++ b/hw/usb/hid-logitech-dj.c
@@ -79,6 +79,70 @@ static void hidpp_queue_output_report(USBLtunifyState *s, HidppMsg *msg)
}
}
+static bool hidpp_device_available(USBLtunifyState *s, uint8_t device_index)
+{
+ /* TODO: consider unpaired / powered off */
+ return device_index >= 1 && device_index <= MAX_DEVICES &&
+ s->devices[device_index - 1].powered_on;
+}
+
+static void hidpp_process_input_report(USBLtunifyState *s, HidppMsg msg)
+{
+ LHidDevice *hd;
+ int i;
+ uint8_t *parms = msg.dj_s.payload;
+
+ /* reports for receiver */
+ if (msg.device_index == 0xFF) {
+ switch (msg.dj_s.report_type) {
+ case 0x80: /* Switch and Keep-Alive */
+ for (i = 0; i < MAX_DEVICES; i++) {
+ hd = &s->devices[i];
+ if ((parms[0] >> i) & 1) {
+ hd->mode = LTUNIFY_MODE_DJ;
+ } else {
+ hd->mode = LTUNIFY_MODE_HID;
+ }
+ }
+ // not implemented: keep-alive timeout params[1];
+ break;
+ case 0x81: /* Get Paired Devices */
+ memset(&msg, 0, sizeof(msg));
+ msg.report_id = DJ_SHORT;
+ msg.dj_s.report_type = 0x41; /* Device Paired notif */
+ parms[0] = 1; /* more notifications will follow bit. */
+ for (i = 0; i < MAX_DEVICES; i++) {
+ hd = &s->devices[i];
+ if (!hidpp_device_available(s, i)) {
+ continue;
+ }
+
+ msg.device_index = i + 1;
+ parms[1] = (uint8_t) hd->info.wireless_pid;
+ parms[2] = (uint8_t) (hd->info.wireless_pid >> 8);
+ parms[3] = (uint8_t) hd->info.report_types;
+ parms[4] = (uint8_t) (hd->info.report_types >> 8);
+ parms[5] = (uint8_t) (hd->info.report_types >> 16);
+ parms[6] = (uint8_t) (hd->info.report_types >> 24);
+ hidpp_queue_output_report(s, &msg);
+ }
+ /* end of notifications list */
+ msg.device_index = 0x00;
+ parms[0] = 2;
+ memset(parms + 1, 0, 6);
+ hidpp_queue_output_report(s, &msg);
+ break;
+ default:
+ hidpp_queue_error(s, &msg, HIDPP_ERR_INVALID_SUBID);
+ }
+ } else if (hidpp_device_available(s, msg.device_index)) {
+ /* TODO: implement for devices */
+ hidpp_queue_error(s, &msg, HIDPP_ERR_REQUEST_UNAVAILABLE);
+ } else {
+ hidpp_queue_error(s, &msg, HIDPP_ERR_RESOURCE_ERROR);
+ }
+}
+
/* process a received report */
static void hidpp_set_report(USBDevice *dev, USBPacket *p, uint8_t *data, size_t len)
{
@@ -124,6 +188,54 @@ void usb_ltunify_handle_control_hidpp(USBDevice *dev, USBPacket *p,
}
}
+static void hidpp_handle_hid(USBDevice *dev, USBPacket *p)
+{
+ USBLtunifyState *s = (USBLtunifyState *) dev;
+ DjMsgShort msg = { 0 };
+ uint8_t *data = (uint8_t *) &msg;
+ uint8_t *hid_data = msg.payload;
+ int i;
+
+ msg.report_id = DJ_SHORT;
+
+ /* TODO: be more fair, right now the first device always takes all events,
+ * causing delays for other devices. */
+ for (i = 0; i < MAX_DEVICES; i++) {
+ LHidDevice *hd = &s->devices[i];
+
+ if (!hd->info.device_type) {
+ continue;
+ }
+
+ if (hd->hid->kind == HID_MOUSE) {
+ /* start accepting pointer events if not already */
+ hid_pointer_activate(hd->hid);
+ }
+
+ if (!hid_has_events(hd->hid)) {
+ continue;
+ }
+
+ hid_set_next_idle(hd->hid);
+
+ msg.device_index = i + 1;
+ if (hd->hid->kind == HID_MOUSE) {
+ msg.report_type = 0x02;
+ hid_pointer_poll(hd->hid, hid_data, sizeof(msg.payload));
+ } else if (hd->hid->kind == HID_KEYBOARD) {
+ msg.report_type = 0x01;
+ hid_keyboard_poll(hd->hid, hid_data, sizeof(msg.payload));
+ }
+
+ usb_dump_complete_data(s->usb_dump_state, p, data, sizeof(msg));
+ usb_packet_copy(p, data, sizeof(msg));
+ usb_dump_submit(s->usb_dump_state, p);
+ return;
+ }
+
+ p->status = USB_RET_NAK;
+}
+
void usb_ltunify_handle_datain_hidpp(USBDevice *dev, USBPacket *p)
{
USBLtunifyState *s = (USBLtunifyState *) dev;
@@ -139,6 +251,7 @@ void usb_ltunify_handle_datain_hidpp(USBDevice *dev, USBPacket *p)
slot = s->input_queue.head;
LQUEUE_INCR(s->input_queue, s->input_queue.head);
s->input_queue.n--;
+ hidpp_process_input_report(s, s->input_queue.reports[slot]);
}
/* try to send a reply to a previously sent request if possible. */
@@ -154,6 +267,11 @@ void usb_ltunify_handle_datain_hidpp(USBDevice *dev, USBPacket *p)
msg = &s->output_queue.reports[slot];
}
+ if (msg == NULL) {
+ hidpp_handle_hid(dev, p);
+ return;
+ }
+
if (msg != NULL) {
len = msg_report_length(msg);
assert(len > 0);
@@ -164,3 +282,42 @@ void usb_ltunify_handle_datain_hidpp(USBDevice *dev, USBPacket *p)
p->status = USB_RET_NAK;
}
}
+
+static void hidpp_init_device(USBLtunifyState *s, int device_index, int devtype)
+{
+ LHidDevice *hd = &s->devices[device_index];
+
+ memset(hd, 0, sizeof(*hd));
+
+ hd->info.device_type = devtype;
+ switch (devtype) {
+ case DEVTYPE_KEYBOARD:
+ hd->hid = &s->hid[IFACE_KBD];
+ hd->info.report_types = 0x1a; /* Keyboard, Consumer Control, System Control */
+ break;
+ case DEVTYPE_MOUSE:
+ hd->hid = &s->hid[IFACE_MSE];
+ hd->info.report_types = 4; /* HID Collection: Mouse */
+ break;
+ }
+ hd->mode = LTUNIFY_MODE_HID;
+ hd->powered_on = true;
+}
+
+void hidpp_reset(USBLtunifyState *s)
+{
+ int i;
+
+ for (i = 0; i < MAX_DEVICES; i++) {
+ LHidDevice *hd = &s->devices[i];
+ if (hd->info.device_type) {
+ hidpp_init_device(s, i, hd->info.device_type);
+ }
+ }
+}
+
+void hidpp_init(USBLtunifyState *s)
+{
+ hidpp_init_device(s, 1, DEVTYPE_KEYBOARD);
+ hidpp_init_device(s, 2, DEVTYPE_MOUSE);
+}