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); | ... | ... |