Commit 59ae540c3dbd1ab3fb004e85eaf3aa4f3520a308
1 parent
92414fdc
added virtual USB mouse support
git-svn-id: svn://svn.savannah.nongnu.org/qemu/trunk@1599 c046a42c-6fe2-441c-8c8c-71466251a162
Showing
4 changed files
with
373 additions
and
8 deletions
Makefile.target
| ... | ... | @@ -294,7 +294,8 @@ ifeq ($(TARGET_BASE_ARCH), i386) |
| 294 | 294 | # Hardware support |
| 295 | 295 | VL_OBJS+= ide.o ne2000.o pckbd.o vga.o $(SOUND_HW) dma.o $(AUDIODRV) |
| 296 | 296 | VL_OBJS+= fdc.o mc146818rtc.o serial.o i8259.o i8254.o pc.o |
| 297 | -VL_OBJS+= cirrus_vga.o mixeng.o apic.o parallel.o usb.o usb-uhci.o usb-linux.o | |
| 297 | +VL_OBJS+= cirrus_vga.o mixeng.o apic.o parallel.o | |
| 298 | +VL_OBJS+= usb.o usb-uhci.o usb-linux.o usb-hid.o | |
| 298 | 299 | DEFINES += -DHAS_AUDIO |
| 299 | 300 | endif |
| 300 | 301 | ifeq ($(TARGET_BASE_ARCH), ppc) | ... | ... |
hw/pc.c
| ... | ... | @@ -622,24 +622,31 @@ static void pc_init1(int ram_size, int vga_ram_size, int boot_device, |
| 622 | 622 | |
| 623 | 623 | if (pci_enabled && usb_enabled) { |
| 624 | 624 | USBPort *usb_root_ports[2]; |
| 625 | - USBDevice *usb_hub; | |
| 625 | + USBDevice *usb_dev; | |
| 626 | 626 | usb_uhci_init(pci_bus, usb_root_ports); |
| 627 | 627 | #if 0 |
| 628 | 628 | { |
| 629 | 629 | USBPort *usb_hub1_ports[4]; |
| 630 | 630 | USBPort *usb_hub2_ports[2]; |
| 631 | 631 | /* test: we simulate a USB hub */ |
| 632 | - usb_hub = usb_hub_init(usb_hub1_ports, 4); | |
| 633 | - usb_attach(usb_root_ports[0], usb_hub); | |
| 632 | + usb_dev = usb_hub_init(usb_hub1_ports, 4); | |
| 633 | + usb_attach(usb_root_ports[0], usb_dev); | |
| 634 | 634 | |
| 635 | 635 | /* test: we simulate a USB hub */ |
| 636 | - usb_hub = usb_hub_init(usb_hub2_ports, 2); | |
| 637 | - usb_attach(usb_hub1_ports[0], usb_hub); | |
| 636 | + usb_dev = usb_hub_init(usb_hub2_ports, 2); | |
| 637 | + usb_attach(usb_hub1_ports[0], usb_dev); | |
| 638 | 638 | } |
| 639 | 639 | #endif |
| 640 | +#if 0 | |
| 641 | + /* USB mouse */ | |
| 642 | + usb_dev = usb_mouse_init(); | |
| 643 | + usb_attach(usb_root_ports[0], usb_dev); | |
| 644 | +#endif | |
| 645 | +#if 1 | |
| 640 | 646 | /* simulated hub with the host USB devices connected to it */ |
| 641 | - usb_hub = usb_host_hub_init(); | |
| 642 | - usb_attach(usb_root_ports[0], usb_hub); | |
| 647 | + usb_dev = usb_host_hub_init(); | |
| 648 | + usb_attach(usb_root_ports[0], usb_dev); | |
| 649 | +#endif | |
| 643 | 650 | } |
| 644 | 651 | |
| 645 | 652 | /* must be done after all PCI devices are instanciated */ | ... | ... |
hw/usb-hid.c
0 → 100644
| 1 | +/* | |
| 2 | + * QEMU USB HID devices | |
| 3 | + * | |
| 4 | + * Copyright (c) 2005 Fabrice Bellard | |
| 5 | + * | |
| 6 | + * Permission is hereby granted, free of charge, to any person obtaining a copy | |
| 7 | + * of this software and associated documentation files (the "Software"), to deal | |
| 8 | + * in the Software without restriction, including without limitation the rights | |
| 9 | + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell | |
| 10 | + * copies of the Software, and to permit persons to whom the Software is | |
| 11 | + * furnished to do so, subject to the following conditions: | |
| 12 | + * | |
| 13 | + * The above copyright notice and this permission notice shall be included in | |
| 14 | + * all copies or substantial portions of the Software. | |
| 15 | + * | |
| 16 | + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR | |
| 17 | + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, | |
| 18 | + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL | |
| 19 | + * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER | |
| 20 | + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, | |
| 21 | + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN | |
| 22 | + * THE SOFTWARE. | |
| 23 | + */ | |
| 24 | +#include "vl.h" | |
| 25 | + | |
| 26 | +/* HID interface requests */ | |
| 27 | +#define GET_REPORT 0xa101 | |
| 28 | +#define GET_IDLE 0xa102 | |
| 29 | +#define GET_PROTOCOL 0xa103 | |
| 30 | +#define SET_IDLE 0x210a | |
| 31 | +#define SET_PROTOCOL 0x210b | |
| 32 | + | |
| 33 | +typedef struct USBMouseState { | |
| 34 | + USBDevice dev; | |
| 35 | + int dx, dy, dz, buttons_state; | |
| 36 | +} USBMouseState; | |
| 37 | + | |
| 38 | +/* mostly the same values as the Bochs USB Mouse device */ | |
| 39 | +static const uint8_t qemu_mouse_dev_descriptor[] = { | |
| 40 | + 0x12, /* u8 bLength; */ | |
| 41 | + 0x01, /* u8 bDescriptorType; Device */ | |
| 42 | + 0x10, 0x00, /* u16 bcdUSB; v1.0 */ | |
| 43 | + | |
| 44 | + 0x00, /* u8 bDeviceClass; */ | |
| 45 | + 0x00, /* u8 bDeviceSubClass; */ | |
| 46 | + 0x00, /* u8 bDeviceProtocol; [ low/full speeds only ] */ | |
| 47 | + 0x08, /* u8 bMaxPacketSize0; 8 Bytes */ | |
| 48 | + | |
| 49 | + 0x27, 0x06, /* u16 idVendor; */ | |
| 50 | + 0x01, 0x00, /* u16 idProduct; */ | |
| 51 | + 0x00, 0x00, /* u16 bcdDevice */ | |
| 52 | + | |
| 53 | + 0x03, /* u8 iManufacturer; */ | |
| 54 | + 0x02, /* u8 iProduct; */ | |
| 55 | + 0x01, /* u8 iSerialNumber; */ | |
| 56 | + 0x01 /* u8 bNumConfigurations; */ | |
| 57 | +}; | |
| 58 | + | |
| 59 | +static const uint8_t qemu_mouse_config_descriptor[] = { | |
| 60 | + /* one configuration */ | |
| 61 | + 0x09, /* u8 bLength; */ | |
| 62 | + 0x02, /* u8 bDescriptorType; Configuration */ | |
| 63 | + 0x22, 0x00, /* u16 wTotalLength; */ | |
| 64 | + 0x01, /* u8 bNumInterfaces; (1) */ | |
| 65 | + 0x01, /* u8 bConfigurationValue; */ | |
| 66 | + 0x04, /* u8 iConfiguration; */ | |
| 67 | + 0xa0, /* u8 bmAttributes; | |
| 68 | + Bit 7: must be set, | |
| 69 | + 6: Self-powered, | |
| 70 | + 5: Remote wakeup, | |
| 71 | + 4..0: resvd */ | |
| 72 | + 50, /* u8 MaxPower; */ | |
| 73 | + | |
| 74 | + /* USB 1.1: | |
| 75 | + * USB 2.0, single TT organization (mandatory): | |
| 76 | + * one interface, protocol 0 | |
| 77 | + * | |
| 78 | + * USB 2.0, multiple TT organization (optional): | |
| 79 | + * two interfaces, protocols 1 (like single TT) | |
| 80 | + * and 2 (multiple TT mode) ... config is | |
| 81 | + * sometimes settable | |
| 82 | + * NOT IMPLEMENTED | |
| 83 | + */ | |
| 84 | + | |
| 85 | + /* one interface */ | |
| 86 | + 0x09, /* u8 if_bLength; */ | |
| 87 | + 0x04, /* u8 if_bDescriptorType; Interface */ | |
| 88 | + 0x00, /* u8 if_bInterfaceNumber; */ | |
| 89 | + 0x00, /* u8 if_bAlternateSetting; */ | |
| 90 | + 0x01, /* u8 if_bNumEndpoints; */ | |
| 91 | + 0x03, /* u8 if_bInterfaceClass; */ | |
| 92 | + 0x01, /* u8 if_bInterfaceSubClass; */ | |
| 93 | + 0x02, /* u8 if_bInterfaceProtocol; [usb1.1 or single tt] */ | |
| 94 | + 0x05, /* u8 if_iInterface; */ | |
| 95 | + | |
| 96 | + /* one endpoint (status change endpoint) */ | |
| 97 | + 0x07, /* u8 ep_bLength; */ | |
| 98 | + 0x05, /* u8 ep_bDescriptorType; Endpoint */ | |
| 99 | + 0x81, /* u8 ep_bEndpointAddress; IN Endpoint 1 */ | |
| 100 | + 0x03, /* u8 ep_bmAttributes; Interrupt */ | |
| 101 | + 0x03, 0x00, /* u16 ep_wMaxPacketSize; */ | |
| 102 | + 0x0a, /* u8 ep_bInterval; (255ms -- usb 2.0 spec) */ | |
| 103 | + | |
| 104 | + /* HID descriptor */ | |
| 105 | + 0x09, /* u8 bLength; */ | |
| 106 | + 0x21, /* u8 bDescriptorType; */ | |
| 107 | + 0x01, 0x00, /* u16 HID_class */ | |
| 108 | + 0x00, /* u8 country_code */ | |
| 109 | + 0x01, /* u8 num_descriptors */ | |
| 110 | + 0x22, /* u8 type; Report */ | |
| 111 | + 50, 0, /* u16 len */ | |
| 112 | +}; | |
| 113 | + | |
| 114 | +static const uint8_t qemu_mouse_hid_report_descriptor[] = { | |
| 115 | + 0x05, 0x01, 0x09, 0x02, 0xA1, 0x01, 0x09, 0x01, | |
| 116 | + 0xA1, 0x00, 0x05, 0x09, 0x19, 0x01, 0x29, 0x03, | |
| 117 | + 0x15, 0x00, 0x25, 0x01, 0x95, 0x03, 0x75, 0x01, | |
| 118 | + 0x81, 0x02, 0x95, 0x01, 0x75, 0x05, 0x81, 0x01, | |
| 119 | + 0x05, 0x01, 0x09, 0x30, 0x09, 0x31, 0x15, 0x81, | |
| 120 | + 0x25, 0x7F, 0x75, 0x08, 0x95, 0x02, 0x81, 0x06, | |
| 121 | + 0xC0, 0xC0, | |
| 122 | +}; | |
| 123 | + | |
| 124 | +static void usb_mouse_event(void *opaque, | |
| 125 | + int dx1, int dy1, int dz1, int buttons_state) | |
| 126 | +{ | |
| 127 | + USBMouseState *s = opaque; | |
| 128 | + | |
| 129 | + s->dx += dx1; | |
| 130 | + s->dy += dy1; | |
| 131 | + s->dz += dz1; | |
| 132 | + s->buttons_state = buttons_state; | |
| 133 | +} | |
| 134 | + | |
| 135 | +static inline int int_clamp(int val, int vmin, int vmax) | |
| 136 | +{ | |
| 137 | + if (val < vmin) | |
| 138 | + return vmin; | |
| 139 | + else if (val > vmax) | |
| 140 | + return vmax; | |
| 141 | + else | |
| 142 | + return val; | |
| 143 | +} | |
| 144 | + | |
| 145 | +static int usb_mouse_poll(USBMouseState *s, uint8_t *buf, int len) | |
| 146 | +{ | |
| 147 | + int dx, dy, dz, b, l; | |
| 148 | + | |
| 149 | + dx = int_clamp(s->dx, -128, 127); | |
| 150 | + dy = int_clamp(s->dy, -128, 127); | |
| 151 | + dz = int_clamp(s->dz, -128, 127); | |
| 152 | + | |
| 153 | + s->dx -= dx; | |
| 154 | + s->dy -= dy; | |
| 155 | + s->dz -= dz; | |
| 156 | + | |
| 157 | + b = 0; | |
| 158 | + if (s->buttons_state & MOUSE_EVENT_LBUTTON) | |
| 159 | + b |= 0x01; | |
| 160 | + if (s->buttons_state & MOUSE_EVENT_RBUTTON) | |
| 161 | + b |= 0x02; | |
| 162 | + if (s->buttons_state & MOUSE_EVENT_MBUTTON) | |
| 163 | + b |= 0x04; | |
| 164 | + | |
| 165 | + buf[0] = b; | |
| 166 | + buf[1] = dx; | |
| 167 | + buf[2] = dy; | |
| 168 | + l = 3; | |
| 169 | + if (len >= 4) { | |
| 170 | + buf[3] = dz; | |
| 171 | + l = 4; | |
| 172 | + } | |
| 173 | + return l; | |
| 174 | +} | |
| 175 | + | |
| 176 | +static void usb_mouse_handle_reset(USBDevice *dev) | |
| 177 | +{ | |
| 178 | + USBMouseState *s = (USBMouseState *)dev; | |
| 179 | + | |
| 180 | + s->dx = 0; | |
| 181 | + s->dy = 0; | |
| 182 | + s->dz = 0; | |
| 183 | + s->buttons_state = 0; | |
| 184 | +} | |
| 185 | + | |
| 186 | +static int usb_mouse_handle_control(USBDevice *dev, int request, int value, | |
| 187 | + int index, int length, uint8_t *data) | |
| 188 | +{ | |
| 189 | + USBMouseState *s = (USBMouseState *)dev; | |
| 190 | + int ret; | |
| 191 | + | |
| 192 | + switch(request) { | |
| 193 | + case DeviceRequest | USB_REQ_GET_STATUS: | |
| 194 | + data[0] = (1 << USB_DEVICE_SELF_POWERED) | | |
| 195 | + (dev->remote_wakeup << USB_DEVICE_REMOTE_WAKEUP); | |
| 196 | + data[1] = 0x00; | |
| 197 | + ret = 2; | |
| 198 | + break; | |
| 199 | + case DeviceOutRequest | USB_REQ_CLEAR_FEATURE: | |
| 200 | + if (value == USB_DEVICE_REMOTE_WAKEUP) { | |
| 201 | + dev->remote_wakeup = 0; | |
| 202 | + } else { | |
| 203 | + goto fail; | |
| 204 | + } | |
| 205 | + ret = 0; | |
| 206 | + break; | |
| 207 | + case DeviceOutRequest | USB_REQ_SET_FEATURE: | |
| 208 | + if (value == USB_DEVICE_REMOTE_WAKEUP) { | |
| 209 | + dev->remote_wakeup = 1; | |
| 210 | + } else { | |
| 211 | + goto fail; | |
| 212 | + } | |
| 213 | + ret = 0; | |
| 214 | + break; | |
| 215 | + case DeviceOutRequest | USB_REQ_SET_ADDRESS: | |
| 216 | + dev->addr = value; | |
| 217 | + ret = 0; | |
| 218 | + break; | |
| 219 | + case DeviceRequest | USB_REQ_GET_DESCRIPTOR: | |
| 220 | + switch(value >> 8) { | |
| 221 | + case USB_DT_DEVICE: | |
| 222 | + memcpy(data, qemu_mouse_dev_descriptor, | |
| 223 | + sizeof(qemu_mouse_dev_descriptor)); | |
| 224 | + ret = sizeof(qemu_mouse_dev_descriptor); | |
| 225 | + break; | |
| 226 | + case USB_DT_CONFIG: | |
| 227 | + memcpy(data, qemu_mouse_config_descriptor, | |
| 228 | + sizeof(qemu_mouse_config_descriptor)); | |
| 229 | + ret = sizeof(qemu_mouse_config_descriptor); | |
| 230 | + break; | |
| 231 | + case USB_DT_STRING: | |
| 232 | + switch(value & 0xff) { | |
| 233 | + case 0: | |
| 234 | + /* language ids */ | |
| 235 | + data[0] = 4; | |
| 236 | + data[1] = 3; | |
| 237 | + data[2] = 0x09; | |
| 238 | + data[3] = 0x04; | |
| 239 | + ret = 4; | |
| 240 | + break; | |
| 241 | + case 1: | |
| 242 | + /* serial number */ | |
| 243 | + ret = set_usb_string(data, "1"); | |
| 244 | + break; | |
| 245 | + case 2: | |
| 246 | + /* product description */ | |
| 247 | + ret = set_usb_string(data, "QEMU USB Mouse"); | |
| 248 | + break; | |
| 249 | + case 3: | |
| 250 | + /* vendor description */ | |
| 251 | + ret = set_usb_string(data, "QEMU " QEMU_VERSION); | |
| 252 | + break; | |
| 253 | + case 4: | |
| 254 | + ret = set_usb_string(data, "HID Mouse"); | |
| 255 | + break; | |
| 256 | + case 5: | |
| 257 | + ret = set_usb_string(data, "Endpoint1 Interrupt Pipe"); | |
| 258 | + break; | |
| 259 | + default: | |
| 260 | + goto fail; | |
| 261 | + } | |
| 262 | + break; | |
| 263 | + default: | |
| 264 | + goto fail; | |
| 265 | + } | |
| 266 | + break; | |
| 267 | + case DeviceRequest | USB_REQ_GET_CONFIGURATION: | |
| 268 | + data[0] = 1; | |
| 269 | + ret = 1; | |
| 270 | + break; | |
| 271 | + case DeviceOutRequest | USB_REQ_SET_CONFIGURATION: | |
| 272 | + ret = 0; | |
| 273 | + break; | |
| 274 | + case DeviceRequest | USB_REQ_GET_INTERFACE: | |
| 275 | + data[0] = 0; | |
| 276 | + ret = 1; | |
| 277 | + break; | |
| 278 | + case DeviceOutRequest | USB_REQ_SET_INTERFACE: | |
| 279 | + ret = 0; | |
| 280 | + break; | |
| 281 | + /* hid specific requests */ | |
| 282 | + case InterfaceRequest | USB_REQ_GET_DESCRIPTOR: | |
| 283 | + switch(value >> 8) { | |
| 284 | + case 0x22: | |
| 285 | + memcpy(data, qemu_mouse_hid_report_descriptor, | |
| 286 | + sizeof(qemu_mouse_hid_report_descriptor)); | |
| 287 | + ret = sizeof(qemu_mouse_hid_report_descriptor); | |
| 288 | + break; | |
| 289 | + default: | |
| 290 | + goto fail; | |
| 291 | + } | |
| 292 | + break; | |
| 293 | + case GET_REPORT: | |
| 294 | + ret = usb_mouse_poll(s, data, length); | |
| 295 | + break; | |
| 296 | + case SET_IDLE: | |
| 297 | + ret = 0; | |
| 298 | + break; | |
| 299 | + default: | |
| 300 | + fail: | |
| 301 | + ret = USB_RET_STALL; | |
| 302 | + break; | |
| 303 | + } | |
| 304 | + return ret; | |
| 305 | +} | |
| 306 | + | |
| 307 | +static int usb_mouse_handle_data(USBDevice *dev, int pid, | |
| 308 | + uint8_t devep, uint8_t *data, int len) | |
| 309 | +{ | |
| 310 | + USBMouseState *s = (USBMouseState *)dev; | |
| 311 | + int ret; | |
| 312 | + | |
| 313 | + switch(pid) { | |
| 314 | + case USB_TOKEN_IN: | |
| 315 | + if (devep == 1) { | |
| 316 | + ret = usb_mouse_poll(s, data, len); | |
| 317 | + } else { | |
| 318 | + goto fail; | |
| 319 | + } | |
| 320 | + break; | |
| 321 | + case USB_TOKEN_OUT: | |
| 322 | + default: | |
| 323 | + fail: | |
| 324 | + ret = USB_RET_STALL; | |
| 325 | + break; | |
| 326 | + } | |
| 327 | + return ret; | |
| 328 | +} | |
| 329 | + | |
| 330 | +USBDevice *usb_mouse_init(void) | |
| 331 | +{ | |
| 332 | + USBMouseState *s; | |
| 333 | + | |
| 334 | + s = qemu_mallocz(sizeof(USBMouseState)); | |
| 335 | + if (!s) | |
| 336 | + return NULL; | |
| 337 | + s->dev.speed = USB_SPEED_FULL; | |
| 338 | + s->dev.handle_packet = usb_generic_handle_packet; | |
| 339 | + | |
| 340 | + s->dev.handle_reset = usb_mouse_handle_reset; | |
| 341 | + s->dev.handle_control = usb_mouse_handle_control; | |
| 342 | + s->dev.handle_data = usb_mouse_handle_data; | |
| 343 | + | |
| 344 | + qemu_add_mouse_event_handler(usb_mouse_event, s); | |
| 345 | + | |
| 346 | + return (USBDevice *)s; | |
| 347 | +} | ... | ... |
hw/usb.h
| ... | ... | @@ -64,6 +64,13 @@ |
| 64 | 64 | |
| 65 | 65 | #define DeviceRequest ((USB_DIR_IN|USB_TYPE_STANDARD|USB_RECIP_DEVICE)<<8) |
| 66 | 66 | #define DeviceOutRequest ((USB_DIR_OUT|USB_TYPE_STANDARD|USB_RECIP_DEVICE)<<8) |
| 67 | +#define InterfaceRequest \ | |
| 68 | + ((USB_DIR_IN|USB_TYPE_STANDARD|USB_RECIP_INTERFACE)<<8) | |
| 69 | +#define InterfaceOutRequest \ | |
| 70 | + ((USB_DIR_OUT|USB_TYPE_STANDARD|USB_RECIP_INTERFACE)<<8) | |
| 71 | +#define EndpointRequest ((USB_DIR_IN|USB_TYPE_STANDARD|USB_RECIP_ENDPOINT)<<8) | |
| 72 | +#define EndpointOutRequest \ | |
| 73 | + ((USB_DIR_OUT|USB_TYPE_STANDARD|USB_RECIP_ENDPOINT)<<8) | |
| 67 | 74 | |
| 68 | 75 | #define USB_REQ_GET_STATUS 0x00 |
| 69 | 76 | #define USB_REQ_CLEAR_FEATURE 0x01 |
| ... | ... | @@ -137,3 +144,6 @@ void usb_uhci_init(PCIBus *bus, USBPort **usb_ports); |
| 137 | 144 | |
| 138 | 145 | /* usb-linux.c */ |
| 139 | 146 | USBDevice *usb_host_hub_init(void); |
| 147 | + | |
| 148 | +/* usb-hid.c */ | |
| 149 | +USBDevice *usb_mouse_init(void); | ... | ... |