From 198529586fff48da10e4c5d56c9a44db2b6a0a1d Mon Sep 17 00:00:00 2001 From: Peter Wu Date: Wed, 19 Mar 2014 15:18:50 +0100 Subject: 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 --- hw/usb/hid-logitech-dj.c | 157 +++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 157 insertions(+) (limited to 'hw/usb/hid-logitech-dj.c') 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); +} -- cgit v1.2.1