* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
* THE SOFTWARE.
*/
-#include "vl.h"
+#include "hw.h"
+#include "console.h"
+#include "usb.h"
/* HID interface requests */
#define GET_REPORT 0xa101
};
int kind;
int protocol;
- int idle;
+ uint8_t idle;
+ int64_t next_idle_clock;
int changed;
+ void *datain_opaque;
+ void (*datain)(void *);
} USBHIDState;
/* mostly the same values as the Bochs USB Mouse device */
0x01, /* u8 bNumInterfaces; (1) */
0x01, /* u8 bConfigurationValue; */
0x04, /* u8 iConfiguration; */
- 0xa0, /* u8 bmAttributes;
+ 0xe0, /* u8 bmAttributes;
Bit 7: must be set,
6: Self-powered,
5: Remote wakeup,
0x00, /* u8 country_code */
0x01, /* u8 num_descriptors */
0x22, /* u8 type; Report */
- 50, 0, /* u16 len */
+ 52, 0, /* u16 len */
/* one endpoint (status change endpoint) */
0x07, /* u8 ep_bLength; */
0x05, /* u8 ep_bDescriptorType; Endpoint */
0x81, /* u8 ep_bEndpointAddress; IN Endpoint 1 */
0x03, /* u8 ep_bmAttributes; Interrupt */
- 0x03, 0x00, /* u16 ep_wMaxPacketSize; */
+ 0x04, 0x00, /* u16 ep_wMaxPacketSize; */
0x0a, /* u8 ep_bInterval; (255ms -- usb 2.0 spec) */
};
};
static const uint8_t qemu_mouse_hid_report_descriptor[] = {
- 0x05, 0x01, 0x09, 0x02, 0xA1, 0x01, 0x09, 0x01,
- 0xA1, 0x00, 0x05, 0x09, 0x19, 0x01, 0x29, 0x03,
- 0x15, 0x00, 0x25, 0x01, 0x95, 0x03, 0x75, 0x01,
- 0x81, 0x02, 0x95, 0x01, 0x75, 0x05, 0x81, 0x01,
- 0x05, 0x01, 0x09, 0x30, 0x09, 0x31, 0x15, 0x81,
- 0x25, 0x7F, 0x75, 0x08, 0x95, 0x02, 0x81, 0x06,
- 0xC0, 0xC0,
+ 0x05, 0x01, /* Usage Page (Generic Desktop) */
+ 0x09, 0x02, /* Usage (Mouse) */
+ 0xa1, 0x01, /* Collection (Application) */
+ 0x09, 0x01, /* Usage (Pointer) */
+ 0xa1, 0x00, /* Collection (Physical) */
+ 0x05, 0x09, /* Usage Page (Button) */
+ 0x19, 0x01, /* Usage Minimum (1) */
+ 0x29, 0x03, /* Usage Maximum (3) */
+ 0x15, 0x00, /* Logical Minimum (0) */
+ 0x25, 0x01, /* Logical Maximum (1) */
+ 0x95, 0x03, /* Report Count (3) */
+ 0x75, 0x01, /* Report Size (1) */
+ 0x81, 0x02, /* Input (Data, Variable, Absolute) */
+ 0x95, 0x01, /* Report Count (1) */
+ 0x75, 0x05, /* Report Size (5) */
+ 0x81, 0x01, /* Input (Constant) */
+ 0x05, 0x01, /* Usage Page (Generic Desktop) */
+ 0x09, 0x30, /* Usage (X) */
+ 0x09, 0x31, /* Usage (Y) */
+ 0x09, 0x38, /* Usage (Wheel) */
+ 0x15, 0x81, /* Logical Minimum (-0x7f) */
+ 0x25, 0x7f, /* Logical Maximum (0x7f) */
+ 0x75, 0x08, /* Report Size (8) */
+ 0x95, 0x03, /* Report Count (3) */
+ 0x81, 0x06, /* Input (Data, Variable, Relative) */
+ 0xc0, /* End Collection */
+ 0xc0, /* End Collection */
};
static const uint8_t qemu_tablet_hid_report_descriptor[] = {
- 0x05, 0x01, /* Usage Page Generic Desktop */
- 0x09, 0x01, /* Usage Mouse */
- 0xA1, 0x01, /* Collection Application */
- 0x09, 0x01, /* Usage Pointer */
- 0xA1, 0x00, /* Collection Physical */
- 0x05, 0x09, /* Usage Page Button */
- 0x19, 0x01, /* Usage Minimum Button 1 */
- 0x29, 0x03, /* Usage Maximum Button 3 */
- 0x15, 0x00, /* Logical Minimum 0 */
- 0x25, 0x01, /* Logical Maximum 1 */
- 0x95, 0x03, /* Report Count 3 */
- 0x75, 0x01, /* Report Size 1 */
- 0x81, 0x02, /* Input (Data, Var, Abs) */
- 0x95, 0x01, /* Report Count 1 */
- 0x75, 0x05, /* Report Size 5 */
- 0x81, 0x01, /* Input (Cnst, Var, Abs) */
- 0x05, 0x01, /* Usage Page Generic Desktop */
- 0x09, 0x30, /* Usage X */
- 0x09, 0x31, /* Usage Y */
- 0x15, 0x00, /* Logical Minimum 0 */
- 0x26, 0xFF, 0x7F, /* Logical Maximum 0x7fff */
- 0x35, 0x00, /* Physical Minimum 0 */
- 0x46, 0xFE, 0x7F, /* Physical Maximum 0x7fff */
- 0x75, 0x10, /* Report Size 16 */
- 0x95, 0x02, /* Report Count 2 */
- 0x81, 0x02, /* Input (Data, Var, Abs) */
- 0x05, 0x01, /* Usage Page Generic Desktop */
- 0x09, 0x38, /* Usage Wheel */
- 0x15, 0x81, /* Logical Minimum -127 */
- 0x25, 0x7F, /* Logical Maximum 127 */
- 0x35, 0x00, /* Physical Minimum 0 (same as logical) */
- 0x45, 0x00, /* Physical Maximum 0 (same as logical) */
- 0x75, 0x08, /* Report Size 8 */
- 0x95, 0x01, /* Report Count 1 */
- 0x81, 0x02, /* Input (Data, Var, Rel) */
- 0xC0, /* End Collection */
- 0xC0, /* End Collection */
+ 0x05, 0x01, /* Usage Page (Generic Desktop) */
+ 0x09, 0x01, /* Usage (Pointer) */
+ 0xa1, 0x01, /* Collection (Application) */
+ 0x09, 0x01, /* Usage (Pointer) */
+ 0xa1, 0x00, /* Collection (Physical) */
+ 0x05, 0x09, /* Usage Page (Button) */
+ 0x19, 0x01, /* Usage Minimum (1) */
+ 0x29, 0x03, /* Usage Maximum (3) */
+ 0x15, 0x00, /* Logical Minimum (0) */
+ 0x25, 0x01, /* Logical Maximum (1) */
+ 0x95, 0x03, /* Report Count (3) */
+ 0x75, 0x01, /* Report Size (1) */
+ 0x81, 0x02, /* Input (Data, Variable, Absolute) */
+ 0x95, 0x01, /* Report Count (1) */
+ 0x75, 0x05, /* Report Size (5) */
+ 0x81, 0x01, /* Input (Constant) */
+ 0x05, 0x01, /* Usage Page (Generic Desktop) */
+ 0x09, 0x30, /* Usage (X) */
+ 0x09, 0x31, /* Usage (Y) */
+ 0x15, 0x00, /* Logical Minimum (0) */
+ 0x26, 0xff, 0x7f, /* Logical Maximum (0x7fff) */
+ 0x35, 0x00, /* Physical Minimum (0) */
+ 0x46, 0xff, 0x7f, /* Physical Maximum (0x7fff) */
+ 0x75, 0x10, /* Report Size (16) */
+ 0x95, 0x02, /* Report Count (2) */
+ 0x81, 0x02, /* Input (Data, Variable, Absolute) */
+ 0x05, 0x01, /* Usage Page (Generic Desktop) */
+ 0x09, 0x38, /* Usage (Wheel) */
+ 0x15, 0x81, /* Logical Minimum (-0x7f) */
+ 0x25, 0x7f, /* Logical Maximum (0x7f) */
+ 0x35, 0x00, /* Physical Minimum (same as logical) */
+ 0x45, 0x00, /* Physical Maximum (same as logical) */
+ 0x75, 0x08, /* Report Size (8) */
+ 0x95, 0x01, /* Report Count (1) */
+ 0x81, 0x06, /* Input (Data, Variable, Relative) */
+ 0xc0, /* End Collection */
+ 0xc0, /* End Collection */
};
static const uint8_t qemu_keyboard_hid_report_descriptor[] = {
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
};
+static void usb_hid_changed(USBHIDState *hs)
+{
+ hs->changed = 1;
+
+ if (hs->datain)
+ hs->datain(hs->datain_opaque);
+}
+
static void usb_mouse_event(void *opaque,
int dx1, int dy1, int dz1, int buttons_state)
{
s->dy += dy1;
s->dz += dz1;
s->buttons_state = buttons_state;
- hs->changed = 1;
+
+ usb_hid_changed(hs);
}
static void usb_tablet_event(void *opaque,
s->y = y;
s->dz += dz;
s->buttons_state = buttons_state;
- hs->changed = 1;
+
+ usb_hid_changed(hs);
}
static void usb_keyboard_event(void *opaque, int keycode)
hid_code = usb_hid_usage_keys[key | ((s->modifiers >> 1) & (1 << 7))];
s->modifiers &= ~(1 << 8);
- hs->changed = 1;
-
switch (hid_code) {
case 0x00:
return;
if (s->key[i] == hid_code) {
s->key[i] = s->key[-- s->keys];
s->key[s->keys] = 0x00;
- return;
+ usb_hid_changed(hs);
+ break;
}
+ if (i < 0)
+ return;
} else {
for (i = s->keys - 1; i >= 0; i --)
if (s->key[i] == hid_code)
- return;
- if (s->keys < sizeof(s->key))
- s->key[s->keys ++] = hid_code;
+ break;
+ if (i < 0) {
+ if (s->keys < sizeof(s->key))
+ s->key[s->keys ++] = hid_code;
+ } else
+ return;
}
+
+ usb_hid_changed(hs);
}
static inline int int_clamp(int val, int vmin, int vmax)
USBMouseState *s = &hs->ptr;
if (!s->mouse_grabbed) {
- s->eh_entry = qemu_add_mouse_event_handler(usb_mouse_event, hs,
- 0, "QEMU USB Mouse");
+ qemu_activate_mouse_event_handler(s->eh_entry);
s->mouse_grabbed = 1;
}
- dx = int_clamp(s->dx, -128, 127);
- dy = int_clamp(s->dy, -128, 127);
- dz = int_clamp(s->dz, -128, 127);
+ dx = int_clamp(s->dx, -127, 127);
+ dy = int_clamp(s->dy, -127, 127);
+ dz = int_clamp(s->dz, -127, 127);
s->dx -= dx;
s->dy -= dy;
s->dz -= dz;
+ /* Appears we have to invert the wheel direction */
+ dz = 0 - dz;
+
b = 0;
if (s->buttons_state & MOUSE_EVENT_LBUTTON)
b |= 0x01;
if (s->buttons_state & MOUSE_EVENT_MBUTTON)
b |= 0x04;
- buf[0] = b;
- buf[1] = dx;
- buf[2] = dy;
- l = 3;
- if (len >= 4) {
- buf[3] = dz;
- l = 4;
- }
+ l = 0;
+ if (len > l)
+ buf[l ++] = b;
+ if (len > l)
+ buf[l ++] = dx;
+ if (len > l)
+ buf[l ++] = dy;
+ if (len > l)
+ buf[l ++] = dz;
return l;
}
USBMouseState *s = &hs->ptr;
if (!s->mouse_grabbed) {
- s->eh_entry = qemu_add_mouse_event_handler(usb_tablet_event, hs,
- 1, "QEMU USB Tablet");
+ qemu_activate_mouse_event_handler(s->eh_entry);
s->mouse_grabbed = 1;
}
- dz = int_clamp(s->dz, -128, 127);
+ dz = int_clamp(s->dz, -127, 127);
s->dz -= dz;
/* Appears we have to invert the wheel direction */
static int usb_keyboard_write(USBKeyboardState *s, uint8_t *buf, int len)
{
if (len > 0) {
+ int ledstate = 0;
/* 0x01: Num Lock LED
* 0x02: Caps Lock LED
* 0x04: Scroll Lock LED
* 0x08: Compose LED
* 0x10: Kana LED */
s->leds = buf[0];
+ if (s->leds & 0x04)
+ ledstate |= QEMU_SCROLL_LOCK_LED;
+ if (s->leds & 0x01)
+ ledstate |= QEMU_NUM_LOCK_LED;
+ if (s->leds & 0x02)
+ ledstate |= QEMU_CAPS_LOCK_LED;
+ kbd_put_ledstate(ledstate);
}
return 0;
}
{
USBHIDState *s = (USBHIDState *)dev;
- qemu_add_kbd_event_handler(usb_keyboard_event, &s->kbd);
+ qemu_add_kbd_event_handler(usb_keyboard_event, s);
s->protocol = 1;
}
+static void usb_hid_set_next_idle(USBHIDState *s, int64_t curtime)
+{
+ s->next_idle_clock = curtime + (get_ticks_per_sec() * s->idle * 4) / 1000;
+}
+
static int usb_hid_handle_control(USBDevice *dev, int request, int value,
int index, int length, uint8_t *data)
{
break;
case 2:
/* product description */
- ret = set_usb_string(data, s->dev.devname);
+ ret = set_usb_string(data, s->dev.product_desc);
break;
case 3:
/* vendor description */
data[0] = s->idle;
break;
case SET_IDLE:
- s->idle = value;
+ s->idle = (uint8_t) (value >> 8);
+ usb_hid_set_next_idle(s, qemu_get_clock(vm_clock));
ret = 0;
break;
default:
switch(p->pid) {
case USB_TOKEN_IN:
if (p->devep == 1) {
- /* TODO: Implement finite idle delays. */
- if (!(s->changed || s->idle))
+ int64_t curtime = qemu_get_clock(vm_clock);
+ if (!s->changed && (!s->idle || s->next_idle_clock - curtime > 0))
return USB_RET_NAK;
+ usb_hid_set_next_idle(s, curtime);
s->changed = 0;
if (s->kind == USB_MOUSE)
ret = usb_mouse_poll(s, p->data, p->len);
if (s->kind != USB_KEYBOARD)
qemu_remove_mouse_event_handler(s->ptr.eh_entry);
/* TODO: else */
- qemu_free(s);
}
-USBDevice *usb_tablet_init(void)
+static int usb_hid_initfn(USBDevice *dev, int kind)
{
- USBHIDState *s;
-
- s = qemu_mallocz(sizeof(USBHIDState));
- if (!s)
- return NULL;
+ USBHIDState *s = DO_UPCAST(USBHIDState, dev, dev);
s->dev.speed = USB_SPEED_FULL;
- s->dev.handle_packet = usb_generic_handle_packet;
-
- s->dev.handle_reset = usb_mouse_handle_reset;
- s->dev.handle_control = usb_hid_handle_control;
- s->dev.handle_data = usb_hid_handle_data;
- s->dev.handle_destroy = usb_hid_handle_destroy;
- s->kind = USB_TABLET;
+ s->kind = kind;
+
+ if (s->kind == USB_MOUSE) {
+ s->ptr.eh_entry = qemu_add_mouse_event_handler(usb_mouse_event, s,
+ 0, "QEMU USB Mouse");
+ } else if (s->kind == USB_TABLET) {
+ s->ptr.eh_entry = qemu_add_mouse_event_handler(usb_tablet_event, s,
+ 1, "QEMU USB Tablet");
+ }
+
/* Force poll routine to be run and grab input the first time. */
s->changed = 1;
-
- pstrcpy(s->dev.devname, sizeof(s->dev.devname), "QEMU USB Tablet");
-
- return (USBDevice *)s;
+ return 0;
}
-USBDevice *usb_mouse_init(void)
+static int usb_tablet_initfn(USBDevice *dev)
{
- USBHIDState *s;
-
- s = qemu_mallocz(sizeof(USBHIDState));
- if (!s)
- return NULL;
- s->dev.speed = USB_SPEED_FULL;
- s->dev.handle_packet = usb_generic_handle_packet;
-
- s->dev.handle_reset = usb_mouse_handle_reset;
- s->dev.handle_control = usb_hid_handle_control;
- s->dev.handle_data = usb_hid_handle_data;
- s->dev.handle_destroy = usb_hid_handle_destroy;
- s->kind = USB_MOUSE;
- /* Force poll routine to be run and grab input the first time. */
- s->changed = 1;
-
- pstrcpy(s->dev.devname, sizeof(s->dev.devname), "QEMU USB Mouse");
+ return usb_hid_initfn(dev, USB_TABLET);
+}
- return (USBDevice *)s;
+static int usb_mouse_initfn(USBDevice *dev)
+{
+ return usb_hid_initfn(dev, USB_MOUSE);
}
-USBDevice *usb_keyboard_init(void)
+static int usb_keyboard_initfn(USBDevice *dev)
{
- USBHIDState *s;
+ return usb_hid_initfn(dev, USB_KEYBOARD);
+}
- s = qemu_mallocz(sizeof(USBHIDState));
- if (!s)
- return NULL;
- s->dev.speed = USB_SPEED_FULL;
- s->dev.handle_packet = usb_generic_handle_packet;
+void usb_hid_datain_cb(USBDevice *dev, void *opaque, void (*datain)(void *))
+{
+ USBHIDState *s = (USBHIDState *)dev;
- s->dev.handle_reset = usb_keyboard_handle_reset;
- s->dev.handle_control = usb_hid_handle_control;
- s->dev.handle_data = usb_hid_handle_data;
- s->dev.handle_destroy = usb_hid_handle_destroy;
- s->kind = USB_KEYBOARD;
+ s->datain_opaque = opaque;
+ s->datain = datain;
+}
- pstrcpy(s->dev.devname, sizeof(s->dev.devname), "QEMU USB Keyboard");
+static struct USBDeviceInfo hid_info[] = {
+ {
+ .product_desc = "QEMU USB Tablet",
+ .qdev.name = "usb-tablet",
+ .usbdevice_name = "tablet",
+ .qdev.size = sizeof(USBHIDState),
+ .init = usb_tablet_initfn,
+ .handle_packet = usb_generic_handle_packet,
+ .handle_reset = usb_mouse_handle_reset,
+ .handle_control = usb_hid_handle_control,
+ .handle_data = usb_hid_handle_data,
+ .handle_destroy = usb_hid_handle_destroy,
+ },{
+ .product_desc = "QEMU USB Mouse",
+ .qdev.name = "usb-mouse",
+ .usbdevice_name = "mouse",
+ .qdev.size = sizeof(USBHIDState),
+ .init = usb_mouse_initfn,
+ .handle_packet = usb_generic_handle_packet,
+ .handle_reset = usb_mouse_handle_reset,
+ .handle_control = usb_hid_handle_control,
+ .handle_data = usb_hid_handle_data,
+ .handle_destroy = usb_hid_handle_destroy,
+ },{
+ .product_desc = "QEMU USB Keyboard",
+ .qdev.name = "usb-kbd",
+ .usbdevice_name = "keyboard",
+ .qdev.size = sizeof(USBHIDState),
+ .init = usb_keyboard_initfn,
+ .handle_packet = usb_generic_handle_packet,
+ .handle_reset = usb_keyboard_handle_reset,
+ .handle_control = usb_hid_handle_control,
+ .handle_data = usb_hid_handle_data,
+ .handle_destroy = usb_hid_handle_destroy,
+ },{
+ /* end of list */
+ }
+};
- return (USBDevice *) s;
+static void usb_hid_register_devices(void)
+{
+ usb_qdev_register_many(hid_info);
}
+device_init(usb_hid_register_devices)