📄 usb-hid.c
字号:
static void usb_keyboard_event(void *opaque, int keycode){ USBHIDState *hs = opaque; USBKeyboardState *s = &hs->kbd; uint8_t hid_code, key; int i; key = keycode & 0x7f; hid_code = usb_hid_usage_keys[key | ((s->modifiers >> 1) & (1 << 7))]; s->modifiers &= ~(1 << 8); s->changed = 1; switch (hid_code) { case 0x00: return; case 0xe0: if (s->modifiers & (1 << 9)) { s->modifiers ^= 3 << 8; return; } case 0xe1 ... 0xe7: if (keycode & (1 << 7)) { s->modifiers &= ~(1 << (hid_code & 0x0f)); return; } case 0xe8 ... 0xef: s->modifiers |= 1 << (hid_code & 0x0f); return; } if (keycode & (1 << 7)) { for (i = s->keys - 1; i >= 0; i --) if (s->key[i] == hid_code) { s->key[i] = s->key[-- s->keys]; s->key[s->keys] = 0x00; 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; }}static inline int int_clamp(int val, int vmin, int vmax){ if (val < vmin) return vmin; else if (val > vmax) return vmax; else return val;}static int usb_suppress_report(USBHIDState *hs, int unchanged) { /* TODO: Implement finite idle delays. */ if (!hs->idle) return 0; /* SET_IDLE 0 means always report */ return unchanged;}static int usb_pointer_poll(USBHIDState *hs, uint8_t *buf, int len){ int dx, dy, dz, b, l; USBPointerState *s = &hs->ptr; USBPointerEvent *e; if (usb_suppress_report(hs, s->head == s->tail)) return USB_RET_NAK; if (!s->mouse_grabbed) { s->eh_entry = qemu_add_mouse_event_handler(usb_pointer_event, hs, !s->xyrel, "QEMU USB Pointer"); s->mouse_grabbed = 1; } if (s->head == s->tail) /* use the last report */ s->head = (s->head - 1) & QUEUEINDEXMASK; e = &s->queue[s->head]; dz = int_clamp(e->dz, -127, 127); if (s->xyrel) { dx = int_clamp(e->xdx, -127, 127); dy = int_clamp(e->ydy, -127, 127); e->xdx -= dx; e->ydy -= dy; } else { dx = e->xdx; dy = e->ydy; } /* Appears we have to invert the wheel direction */ dz = 0 - dz; if (!(e->dz || (s->xyrel && (e->xdx || e->ydy)))) { /* that deals with this event */ QUEUE_INCR(s->head); } b = 0; if (e->buttons_state & MOUSE_EVENT_LBUTTON) b |= 0x01; if (e->buttons_state & MOUSE_EVENT_RBUTTON) b |= 0x02; if (e->buttons_state & MOUSE_EVENT_MBUTTON) b |= 0x04; switch (hs->kind) { case USB_MOUSE: 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; break; case USB_TABLET: /* Appears we have to invert the wheel direction */ dz = 0 - dz; buf[0] = b; buf[1] = dx & 0xff; buf[2] = dx >> 8; buf[3] = dy & 0xff; buf[4] = dy >> 8; buf[5] = dz; l = 6; break; default: abort(); } return l;}static int usb_keyboard_poll(USBHIDState *hs, uint8_t *buf, int len){ USBKeyboardState *s= &hs->kbd; if (usb_suppress_report(hs, !s->changed)) return USB_RET_NAK; s->changed= 0; if (len < 2) return 0; buf[0] = s->modifiers & 0xff; buf[1] = 0; if (s->keys > 6) memset(buf + 2, USB_HID_USAGE_ERROR_ROLLOVER, MIN(8, len) - 2); else memcpy(buf + 2, s->key, MIN(8, len) - 2); return MIN(8, len);}static int usb_keyboard_write(USBKeyboardState *s, uint8_t *buf, int len){ if (len > 0) { /* 0x01: Num Lock LED * 0x02: Caps Lock LED * 0x04: Scroll Lock LED * 0x08: Compose LED * 0x10: Kana LED */ s->leds = buf[0]; } return 0;}static void usb_pointer_handle_reset(USBDevice *dev){ USBHIDState *s = (USBHIDState *)dev; s->ptr.head= s->ptr.tail = 0; s->protocol = 1;}static void usb_keyboard_handle_reset(USBDevice *dev){ USBHIDState *s = (USBHIDState *)dev; qemu_add_kbd_event_handler(usb_keyboard_event, s); s->protocol = 1;}static int usb_hid_handle_control(USBDevice *dev, int request, int value, int index, int length, uint8_t *data){ USBHIDState *s = (USBHIDState *)dev; int ret = 0; switch(request) { case DeviceRequest | USB_REQ_GET_STATUS: data[0] = (1 << USB_DEVICE_SELF_POWERED) | (dev->remote_wakeup << USB_DEVICE_REMOTE_WAKEUP); data[1] = 0x00; ret = 2; break; case DeviceOutRequest | USB_REQ_CLEAR_FEATURE: if (value == USB_DEVICE_REMOTE_WAKEUP) { dev->remote_wakeup = 0; } else { goto fail; } ret = 0; break; case DeviceOutRequest | USB_REQ_SET_FEATURE: if (value == USB_DEVICE_REMOTE_WAKEUP) { dev->remote_wakeup = 1; } else { goto fail; } ret = 0; break; case DeviceOutRequest | USB_REQ_SET_ADDRESS: dev->addr = value; ret = 0; break; case DeviceRequest | USB_REQ_GET_DESCRIPTOR: switch(value >> 8) { case USB_DT_DEVICE: memcpy(data, qemu_mouse_dev_descriptor, sizeof(qemu_mouse_dev_descriptor)); ret = sizeof(qemu_mouse_dev_descriptor); break; case USB_DT_CONFIG: if (s->kind == USB_MOUSE) { memcpy(data, qemu_mouse_config_descriptor, sizeof(qemu_mouse_config_descriptor)); ret = sizeof(qemu_mouse_config_descriptor); } else if (s->kind == USB_TABLET) { memcpy(data, qemu_tablet_config_descriptor, sizeof(qemu_tablet_config_descriptor)); ret = sizeof(qemu_tablet_config_descriptor); } else if (s->kind == USB_KEYBOARD) { memcpy(data, qemu_keyboard_config_descriptor, sizeof(qemu_keyboard_config_descriptor)); ret = sizeof(qemu_keyboard_config_descriptor); } break; case USB_DT_STRING: switch(value & 0xff) { case 0: /* language ids */ data[0] = 4; data[1] = 3; data[2] = 0x09; data[3] = 0x04; ret = 4; break; case 1: /* serial number */ ret = set_usb_string(data, "1"); break; case 2: /* product description */ ret = set_usb_string(data, s->dev.devname); break; case 3: /* vendor description */ ret = set_usb_string(data, "QEMU " QEMU_VERSION); break; case 4: ret = set_usb_string(data, "HID Mouse"); break; case 5: ret = set_usb_string(data, "HID Tablet"); break; case 6: ret = set_usb_string(data, "HID Keyboard"); break; case 7: ret = set_usb_string(data, "Endpoint1 Interrupt Pipe"); break; default: goto fail; } break; default: goto fail; } break; case DeviceRequest | USB_REQ_GET_CONFIGURATION: data[0] = 1; ret = 1; break; case DeviceOutRequest | USB_REQ_SET_CONFIGURATION: ret = 0; break; case DeviceRequest | USB_REQ_GET_INTERFACE: data[0] = 0; ret = 1; break; case DeviceOutRequest | USB_REQ_SET_INTERFACE: ret = 0; break; /* hid specific requests */ case InterfaceRequest | USB_REQ_GET_DESCRIPTOR: switch(value >> 8) { case 0x22: if (s->kind == USB_MOUSE) { memcpy(data, qemu_mouse_hid_report_descriptor, sizeof(qemu_mouse_hid_report_descriptor)); ret = sizeof(qemu_mouse_hid_report_descriptor); } else if (s->kind == USB_TABLET) { memcpy(data, qemu_tablet_hid_report_descriptor, sizeof(qemu_tablet_hid_report_descriptor)); ret = sizeof(qemu_tablet_hid_report_descriptor); } else if (s->kind == USB_KEYBOARD) { memcpy(data, qemu_keyboard_hid_report_descriptor, sizeof(qemu_keyboard_hid_report_descriptor)); ret = sizeof(qemu_keyboard_hid_report_descriptor); } break; default: goto fail; } break; case GET_REPORT: if (s->kind == USB_MOUSE || s->kind == USB_TABLET) ret = usb_pointer_poll(s, data, length); else if (s->kind == USB_KEYBOARD) ret = usb_keyboard_poll(s, data, length); break; case SET_REPORT: if (s->kind == USB_KEYBOARD) ret = usb_keyboard_write(&s->kbd, data, length); else goto fail; break; case GET_PROTOCOL: if (s->kind != USB_KEYBOARD) goto fail; ret = 1; data[0] = s->protocol; break; case SET_PROTOCOL: if (s->kind != USB_KEYBOARD) goto fail; ret = 0; s->protocol = value; break; case GET_IDLE: ret = 1; data[0] = s->idle; break; case SET_IDLE: s->idle = value; ret = 0; break; default: fail: ret = USB_RET_STALL; break; } return ret;}static int usb_hid_handle_data(USBDevice *dev, USBPacket *p){ USBHIDState *s = (USBHIDState *)dev; int ret = 0; switch(p->pid) { case USB_TOKEN_IN: if (p->devep == 1) { if (s->kind == USB_MOUSE || s->kind == USB_TABLET) ret = usb_pointer_poll(s, p->data, p->len); else if (s->kind == USB_KEYBOARD) ret = usb_keyboard_poll(s, p->data, p->len); } else { goto fail; } break; case USB_TOKEN_OUT: default: fail: ret = USB_RET_STALL; break; } return ret;}static void usb_hid_handle_destroy(USBDevice *dev){ USBHIDState *s = (USBHIDState *)dev; if (s->kind != USB_KEYBOARD) qemu_remove_mouse_event_handler(s->ptr.eh_entry); /* TODO: else */ qemu_free(s);}static USBDevice *usb_pointer_init(int kind, int xyrel, const char *devname){ 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_pointer_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 = kind; s->ptr.xyrel = xyrel; usb_pointer_handle_reset((USBDevice*)s); /* Force poll routine to be run and grab input the first time. */ usb_pointer_event_clear(&s->ptr.queue[0], 0); s->ptr.tail = 1; pstrcpy(s->dev.devname, sizeof(s->dev.devname), devname); return (USBDevice *)s;}USBDevice *usb_tablet_init(void){ return usb_pointer_init(USB_TABLET, 0, "QEMU USB Tablet");}USBDevice *usb_mouse_init(void){ return usb_pointer_init(USB_MOUSE, 1, "QEMU USB Mouse");}USBDevice *usb_keyboard_init(void){ 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_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->kbd.changed= 0; pstrcpy(s->dev.devname, sizeof(s->dev.devname), "QEMU USB Keyboard"); return (USBDevice *) s;}
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -