Commit 0d92ed3022694aa6ec9172938e999871fa04f711
1 parent
6650ee6d
OHCI USB host emulation.
git-svn-id: svn://svn.savannah.nongnu.org/qemu/trunk@1928 c046a42c-6fe2-441c-8c8c-71466251a162
Showing
11 changed files
with
1306 additions
and
84 deletions
Makefile.target
hw/pc.c
... | ... | @@ -40,7 +40,6 @@ static fdctrl_t *floppy_controller; |
40 | 40 | static RTCState *rtc_state; |
41 | 41 | static PITState *pit; |
42 | 42 | static IOAPICState *ioapic; |
43 | -static USBPort *usb_root_ports[2]; | |
44 | 43 | |
45 | 44 | static void ioport80_write(void *opaque, uint32_t addr, uint32_t data) |
46 | 45 | { |
... | ... | @@ -833,8 +832,7 @@ static void pc_init1(int ram_size, int vga_ram_size, int boot_device, |
833 | 832 | cmos_init(ram_size, boot_device, bs_table); |
834 | 833 | |
835 | 834 | if (pci_enabled && usb_enabled) { |
836 | - usb_uhci_init(pci_bus, usb_root_ports, piix3_devfn + 2); | |
837 | - usb_attach(usb_root_ports[0], vm_usb_hub); | |
835 | + usb_uhci_init(pci_bus, piix3_devfn + 2); | |
838 | 836 | } |
839 | 837 | |
840 | 838 | if (pci_enabled && acpi_enabled) { | ... | ... |
hw/ppc_chrp.c
... | ... | @@ -506,7 +506,11 @@ static void ppc_chrp_init(int ram_size, int vga_ram_size, int boot_device, |
506 | 506 | |
507 | 507 | arch_name = "MAC99"; |
508 | 508 | } |
509 | - | |
509 | + | |
510 | + if (usb_enabled) { | |
511 | + usb_ohci_init(pci_bus, 3, -1); | |
512 | + } | |
513 | + | |
510 | 514 | if (graphic_depth != 15 && graphic_depth != 32 && graphic_depth != 8) |
511 | 515 | graphic_depth = 15; |
512 | 516 | ... | ... |
hw/ppc_prep.c
... | ... | @@ -665,6 +665,10 @@ static void ppc_prep_init(int ram_size, int vga_ram_size, int boot_device, |
665 | 665 | cpu_register_physical_memory(0xFEFF0000, 0x1000, PPC_io_memory); |
666 | 666 | #endif |
667 | 667 | |
668 | + if (usb_enabled) { | |
669 | + usb_ohci_init(pci_bus, 3, -1); | |
670 | + } | |
671 | + | |
668 | 672 | nvram = m48t59_init(8, 0, 0x0074, NVRAM_SIZE, 59); |
669 | 673 | if (nvram == NULL) |
670 | 674 | return; | ... | ... |
hw/usb-hub.c
... | ... | @@ -179,6 +179,9 @@ static void usb_hub_attach(USBPort *port1, USBDevice *dev) |
179 | 179 | else |
180 | 180 | port->wPortStatus &= ~PORT_STAT_LOW_SPEED; |
181 | 181 | port->port.dev = dev; |
182 | + /* send the attach message */ | |
183 | + dev->handle_packet(dev, | |
184 | + USB_MSG_ATTACH, 0, 0, NULL, 0); | |
182 | 185 | } else { |
183 | 186 | dev = port->port.dev; |
184 | 187 | if (dev) { |
... | ... | @@ -188,6 +191,9 @@ static void usb_hub_attach(USBPort *port1, USBDevice *dev) |
188 | 191 | port->wPortStatus &= ~PORT_STAT_ENABLE; |
189 | 192 | port->wPortChange |= PORT_STAT_C_ENABLE; |
190 | 193 | } |
194 | + /* send the detach message */ | |
195 | + dev->handle_packet(dev, | |
196 | + USB_MSG_DETACH, 0, 0, NULL, 0); | |
191 | 197 | port->port.dev = NULL; |
192 | 198 | } |
193 | 199 | } |
... | ... | @@ -517,7 +523,7 @@ static int usb_hub_handle_packet(USBDevice *dev, int pid, |
517 | 523 | return usb_generic_handle_packet(dev, pid, devaddr, devep, data, len); |
518 | 524 | } |
519 | 525 | |
520 | -USBDevice *usb_hub_init(USBPort **usb_ports, int nb_ports) | |
526 | +USBDevice *usb_hub_init(int nb_ports) | |
521 | 527 | { |
522 | 528 | USBHubState *s; |
523 | 529 | USBHubPort *port; |
... | ... | @@ -539,12 +545,9 @@ USBDevice *usb_hub_init(USBPort **usb_ports, int nb_ports) |
539 | 545 | s->nb_ports = nb_ports; |
540 | 546 | for(i = 0; i < s->nb_ports; i++) { |
541 | 547 | port = &s->ports[i]; |
548 | + qemu_register_usb_port(&port->port, s, i, usb_hub_attach); | |
542 | 549 | port->wPortStatus = PORT_STAT_POWER; |
543 | 550 | port->wPortChange = 0; |
544 | - port->port.attach = usb_hub_attach; | |
545 | - port->port.opaque = s; | |
546 | - port->port.index = i; | |
547 | - usb_ports[i] = &port->port; | |
548 | 551 | } |
549 | 552 | return (USBDevice *)s; |
550 | 553 | } | ... | ... |
hw/usb-ohci.c
0 โ 100644
1 | +/* | |
2 | + * QEMU USB OHCI Emulation | |
3 | + * Copyright (c) 2004 Gianni Tedesco | |
4 | + * Copyright (c) 2006 CodeSourcery | |
5 | + * | |
6 | + * This library is free software; you can redistribute it and/or | |
7 | + * modify it under the terms of the GNU Lesser General Public | |
8 | + * License as published by the Free Software Foundation; either | |
9 | + * version 2 of the License, or (at your option) any later version. | |
10 | + * | |
11 | + * This library is distributed in the hope that it will be useful, | |
12 | + * but WITHOUT ANY WARRANTY; without even the implied warranty of | |
13 | + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU | |
14 | + * Lesser General Public License for more details. | |
15 | + * | |
16 | + * You should have received a copy of the GNU Lesser General Public | |
17 | + * License along with this library; if not, write to the Free Software | |
18 | + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA | |
19 | + * | |
20 | + * TODO: | |
21 | + * o Isochronous transfers | |
22 | + * o Allocate bandwidth in frames properly | |
23 | + * o Disable timers when nothing needs to be done, or remove timer usage | |
24 | + * all together. | |
25 | + * o Handle unrecoverable errors properly | |
26 | + * o BIOS work to boot from USB storage | |
27 | +*/ | |
28 | + | |
29 | +#include "vl.h" | |
30 | + | |
31 | +//#define DEBUG_OHCI | |
32 | +/* Dump packet contents. */ | |
33 | +//#define DEBUG_PACKET | |
34 | +/* This causes frames to occur 1000x slower */ | |
35 | +//#define OHCI_TIME_WARP 1 | |
36 | + | |
37 | +#ifdef DEBUG_OHCI | |
38 | +#define dprintf printf | |
39 | +#else | |
40 | +#define dprintf(...) | |
41 | +#endif | |
42 | + | |
43 | +/* Number of Downstream Ports on the root hub. */ | |
44 | + | |
45 | +#define OHCI_MAX_PORTS 15 | |
46 | + | |
47 | +static int64_t usb_frame_time; | |
48 | +static int64_t usb_bit_time; | |
49 | + | |
50 | +typedef struct OHCIPort { | |
51 | + USBPort port; | |
52 | + uint32_t ctrl; | |
53 | +} OHCIPort; | |
54 | + | |
55 | +typedef struct { | |
56 | + struct PCIDevice pci_dev; | |
57 | + target_phys_addr_t mem_base; | |
58 | + int mem; | |
59 | + int num_ports; | |
60 | + | |
61 | + QEMUTimer *eof_timer; | |
62 | + int64_t sof_time; | |
63 | + | |
64 | + /* OHCI state */ | |
65 | + /* Control partition */ | |
66 | + uint32_t ctl, status; | |
67 | + uint32_t intr_status; | |
68 | + uint32_t intr; | |
69 | + | |
70 | + /* memory pointer partition */ | |
71 | + uint32_t hcca; | |
72 | + uint32_t ctrl_head, ctrl_cur; | |
73 | + uint32_t bulk_head, bulk_cur; | |
74 | + uint32_t per_cur; | |
75 | + uint32_t done; | |
76 | + int done_count; | |
77 | + | |
78 | + /* Frame counter partition */ | |
79 | + uint32_t fsmps:15; | |
80 | + uint32_t fit:1; | |
81 | + uint32_t fi:14; | |
82 | + uint32_t frt:1; | |
83 | + uint16_t frame_number; | |
84 | + uint16_t padding; | |
85 | + uint32_t pstart; | |
86 | + uint32_t lst; | |
87 | + | |
88 | + /* Root Hub partition */ | |
89 | + uint32_t rhdesc_a, rhdesc_b; | |
90 | + uint32_t rhstatus; | |
91 | + OHCIPort rhport[OHCI_MAX_PORTS]; | |
92 | +} OHCIState; | |
93 | + | |
94 | +/* Host Controller Communications Area */ | |
95 | +struct ohci_hcca { | |
96 | + uint32_t intr[32]; | |
97 | + uint16_t frame, pad; | |
98 | + uint32_t done; | |
99 | +}; | |
100 | + | |
101 | +/* Bitfields for the first word of an Endpoint Desciptor. */ | |
102 | +#define OHCI_ED_FA_SHIFT 0 | |
103 | +#define OHCI_ED_FA_MASK (0x7f<<OHCI_ED_FA_SHIFT) | |
104 | +#define OHCI_ED_EN_SHIFT 7 | |
105 | +#define OHCI_ED_EN_MASK (0xf<<OHCI_ED_EN_SHIFT) | |
106 | +#define OHCI_ED_D_SHIFT 11 | |
107 | +#define OHCI_ED_D_MASK (3<<OHCI_ED_D_SHIFT) | |
108 | +#define OHCI_ED_S (1<<13) | |
109 | +#define OHCI_ED_K (1<<14) | |
110 | +#define OHCI_ED_F (1<<15) | |
111 | +#define OHCI_ED_MPS_SHIFT 7 | |
112 | +#define OHCI_ED_MPS_MASK (0xf<<OHCI_ED_FA_SHIFT) | |
113 | + | |
114 | +/* Flags in the head field of an Endpoint Desciptor. */ | |
115 | +#define OHCI_ED_H 1 | |
116 | +#define OHCI_ED_C 2 | |
117 | + | |
118 | +/* Bitfields for the first word of a Transfer Desciptor. */ | |
119 | +#define OHCI_TD_R (1<<18) | |
120 | +#define OHCI_TD_DP_SHIFT 19 | |
121 | +#define OHCI_TD_DP_MASK (3<<OHCI_TD_DP_SHIFT) | |
122 | +#define OHCI_TD_DI_SHIFT 21 | |
123 | +#define OHCI_TD_DI_MASK (7<<OHCI_TD_DI_SHIFT) | |
124 | +#define OHCI_TD_T0 (1<<24) | |
125 | +#define OHCI_TD_T1 (1<<24) | |
126 | +#define OHCI_TD_EC_SHIFT 26 | |
127 | +#define OHCI_TD_EC_MASK (3<<OHCI_TD_EC_SHIFT) | |
128 | +#define OHCI_TD_CC_SHIFT 28 | |
129 | +#define OHCI_TD_CC_MASK (0xf<<OHCI_TD_CC_SHIFT) | |
130 | + | |
131 | +#define OHCI_DPTR_MASK 0xfffffff0 | |
132 | + | |
133 | +#define OHCI_BM(val, field) \ | |
134 | + (((val) & OHCI_##field##_MASK) >> OHCI_##field##_SHIFT) | |
135 | + | |
136 | +#define OHCI_SET_BM(val, field, newval) do { \ | |
137 | + val &= ~OHCI_##field##_MASK; \ | |
138 | + val |= ((newval) << OHCI_##field##_SHIFT) & OHCI_##field##_MASK; \ | |
139 | + } while(0) | |
140 | + | |
141 | +/* endpoint descriptor */ | |
142 | +struct ohci_ed { | |
143 | + uint32_t flags; | |
144 | + uint32_t tail; | |
145 | + uint32_t head; | |
146 | + uint32_t next; | |
147 | +}; | |
148 | + | |
149 | +/* General transfer descriptor */ | |
150 | +struct ohci_td { | |
151 | + uint32_t flags; | |
152 | + uint32_t cbp; | |
153 | + uint32_t next; | |
154 | + uint32_t be; | |
155 | +}; | |
156 | + | |
157 | +#define USB_HZ 12000000 | |
158 | + | |
159 | +/* OHCI Local stuff */ | |
160 | +#define OHCI_CTL_CBSR ((1<<0)|(1<<1)) | |
161 | +#define OHCI_CTL_PLE (1<<2) | |
162 | +#define OHCI_CTL_IE (1<<3) | |
163 | +#define OHCI_CTL_CLE (1<<4) | |
164 | +#define OHCI_CTL_BLE (1<<5) | |
165 | +#define OHCI_CTL_HCFS ((1<<6)|(1<<7)) | |
166 | +#define OHCI_USB_RESET 0x00 | |
167 | +#define OHCI_USB_RESUME 0x40 | |
168 | +#define OHCI_USB_OPERATIONAL 0x80 | |
169 | +#define OHCI_USB_SUSPEND 0xc0 | |
170 | +#define OHCI_CTL_IR (1<<8) | |
171 | +#define OHCI_CTL_RWC (1<<9) | |
172 | +#define OHCI_CTL_RWE (1<<10) | |
173 | + | |
174 | +#define OHCI_STATUS_HCR (1<<0) | |
175 | +#define OHCI_STATUS_CLF (1<<1) | |
176 | +#define OHCI_STATUS_BLF (1<<2) | |
177 | +#define OHCI_STATUS_OCR (1<<3) | |
178 | +#define OHCI_STATUS_SOC ((1<<6)|(1<<7)) | |
179 | + | |
180 | +#define OHCI_INTR_SO (1<<0) /* Scheduling overrun */ | |
181 | +#define OHCI_INTR_WD (1<<1) /* HcDoneHead writeback */ | |
182 | +#define OHCI_INTR_SF (1<<2) /* Start of frame */ | |
183 | +#define OHCI_INTR_RD (1<<3) /* Resume detect */ | |
184 | +#define OHCI_INTR_UE (1<<4) /* Unrecoverable error */ | |
185 | +#define OHCI_INTR_FNO (1<<5) /* Frame number overflow */ | |
186 | +#define OHCI_INTR_RHSC (1<<6) /* Root hub status change */ | |
187 | +#define OHCI_INTR_OC (1<<30) /* Ownership change */ | |
188 | +#define OHCI_INTR_MIE (1<<31) /* Master Interrupt Enable */ | |
189 | + | |
190 | +#define OHCI_HCCA_SIZE 0x100 | |
191 | +#define OHCI_HCCA_MASK 0xffffff00 | |
192 | + | |
193 | +#define OHCI_EDPTR_MASK 0xfffffff0 | |
194 | + | |
195 | +#define OHCI_FMI_FI 0x00003fff | |
196 | +#define OHCI_FMI_FSMPS 0xffff0000 | |
197 | +#define OHCI_FMI_FIT 0x80000000 | |
198 | + | |
199 | +#define OHCI_FR_RT (1<<31) | |
200 | + | |
201 | +#define OHCI_LS_THRESH 0x628 | |
202 | + | |
203 | +#define OHCI_RHA_RW_MASK 0x00000000 /* Mask of supported features. */ | |
204 | +#define OHCI_RHA_PSM (1<<8) | |
205 | +#define OHCI_RHA_NPS (1<<9) | |
206 | +#define OHCI_RHA_DT (1<<10) | |
207 | +#define OHCI_RHA_OCPM (1<<11) | |
208 | +#define OHCI_RHA_NOCP (1<<12) | |
209 | +#define OHCI_RHA_POTPGT_MASK 0xff000000 | |
210 | + | |
211 | +#define OHCI_RHS_LPS (1<<0) | |
212 | +#define OHCI_RHS_OCI (1<<1) | |
213 | +#define OHCI_RHS_DRWE (1<<15) | |
214 | +#define OHCI_RHS_LPSC (1<<16) | |
215 | +#define OHCI_RHS_OCIC (1<<17) | |
216 | +#define OHCI_RHS_CRWE (1<<31) | |
217 | + | |
218 | +#define OHCI_PORT_CCS (1<<0) | |
219 | +#define OHCI_PORT_PES (1<<1) | |
220 | +#define OHCI_PORT_PSS (1<<2) | |
221 | +#define OHCI_PORT_POCI (1<<3) | |
222 | +#define OHCI_PORT_PRS (1<<4) | |
223 | +#define OHCI_PORT_PPS (1<<8) | |
224 | +#define OHCI_PORT_LSDA (1<<9) | |
225 | +#define OHCI_PORT_CSC (1<<16) | |
226 | +#define OHCI_PORT_PESC (1<<17) | |
227 | +#define OHCI_PORT_PSSC (1<<18) | |
228 | +#define OHCI_PORT_OCIC (1<<19) | |
229 | +#define OHCI_PORT_PRSC (1<<20) | |
230 | +#define OHCI_PORT_WTC (OHCI_PORT_CSC|OHCI_PORT_PESC|OHCI_PORT_PSSC \ | |
231 | + |OHCI_PORT_OCIC|OHCI_PORT_PRSC) | |
232 | + | |
233 | +#define OHCI_TD_DIR_SETUP 0x0 | |
234 | +#define OHCI_TD_DIR_OUT 0x1 | |
235 | +#define OHCI_TD_DIR_IN 0x2 | |
236 | +#define OHCI_TD_DIR_RESERVED 0x3 | |
237 | + | |
238 | +#define OHCI_CC_NOERROR 0x0 | |
239 | +#define OHCI_CC_CRC 0x1 | |
240 | +#define OHCI_CC_BITSTUFFING 0x2 | |
241 | +#define OHCI_CC_DATATOGGLEMISMATCH 0x3 | |
242 | +#define OHCI_CC_STALL 0x4 | |
243 | +#define OHCI_CC_DEVICENOTRESPONDING 0x5 | |
244 | +#define OHCI_CC_PIDCHECKFAILURE 0x6 | |
245 | +#define OHCI_CC_UNDEXPETEDPID 0x7 | |
246 | +#define OHCI_CC_DATAOVERRUN 0x8 | |
247 | +#define OHCI_CC_DATAUNDERRUN 0x9 | |
248 | +#define OHCI_CC_BUFFEROVERRUN 0xc | |
249 | +#define OHCI_CC_BUFFERUNDERRUN 0xd | |
250 | + | |
251 | +static void ohci_attach(USBPort *port1, USBDevice *dev) | |
252 | +{ | |
253 | + OHCIState *s = port1->opaque; | |
254 | + OHCIPort *port = &s->rhport[port1->index]; | |
255 | + | |
256 | + if (dev) { | |
257 | + if (port->port.dev) { | |
258 | + usb_attach(port1, NULL); | |
259 | + } | |
260 | + /* set connect status */ | |
261 | + if (!(port->ctrl & OHCI_PORT_CCS)) { | |
262 | + port->ctrl |= OHCI_PORT_CCS | OHCI_PORT_CSC; | |
263 | + } | |
264 | + /* update speed */ | |
265 | + if (dev->speed == USB_SPEED_LOW) | |
266 | + port->ctrl |= OHCI_PORT_LSDA; | |
267 | + else | |
268 | + port->ctrl &= ~OHCI_PORT_LSDA; | |
269 | + port->port.dev = dev; | |
270 | + /* send the attach message */ | |
271 | + dev->handle_packet(dev, | |
272 | + USB_MSG_ATTACH, 0, 0, NULL, 0); | |
273 | + dprintf("usb-ohci: Attached port %d\n", port1->index); | |
274 | + } else { | |
275 | + /* set connect status */ | |
276 | + if (!(port->ctrl & OHCI_PORT_CCS)) { | |
277 | + port->ctrl |= OHCI_PORT_CCS | OHCI_PORT_CSC; | |
278 | + } | |
279 | + /* disable port */ | |
280 | + if (port->ctrl & OHCI_PORT_PES) { | |
281 | + port->ctrl &= ~OHCI_PORT_PES; | |
282 | + port->ctrl |= OHCI_PORT_PESC; | |
283 | + } | |
284 | + dev = port->port.dev; | |
285 | + if (dev) { | |
286 | + /* send the detach message */ | |
287 | + dev->handle_packet(dev, | |
288 | + USB_MSG_DETACH, 0, 0, NULL, 0); | |
289 | + } | |
290 | + port->port.dev = NULL; | |
291 | + dprintf("usb-ohci: Detached port %d\n", port1->index); | |
292 | + } | |
293 | +} | |
294 | + | |
295 | +/* Reset the controller */ | |
296 | +static void ohci_reset(OHCIState *ohci) | |
297 | +{ | |
298 | + OHCIPort *port; | |
299 | + int i; | |
300 | + | |
301 | + ohci->ctl = 0; | |
302 | + ohci->status = 0; | |
303 | + ohci->intr_status = 0; | |
304 | + ohci->intr = OHCI_INTR_MIE; | |
305 | + | |
306 | + ohci->hcca = 0; | |
307 | + ohci->ctrl_head = ohci->ctrl_cur = 0; | |
308 | + ohci->bulk_head = ohci->bulk_cur = 0; | |
309 | + ohci->per_cur = 0; | |
310 | + ohci->done = 0; | |
311 | + ohci->done_count = 7; | |
312 | + | |
313 | + /* FSMPS is marked TBD in OCHI 1.0, what gives ffs? | |
314 | + * I took the value linux sets ... | |
315 | + */ | |
316 | + ohci->fsmps = 0x2778; | |
317 | + ohci->fi = 0x2edf; | |
318 | + ohci->fit = 0; | |
319 | + ohci->frt = 0; | |
320 | + ohci->frame_number = 0; | |
321 | + ohci->pstart = 0; | |
322 | + ohci->lst = OHCI_LS_THRESH; | |
323 | + | |
324 | + ohci->rhdesc_a = OHCI_RHA_NPS | ohci->num_ports; | |
325 | + ohci->rhdesc_b = 0x0; /* Impl. specific */ | |
326 | + ohci->rhstatus = 0; | |
327 | + | |
328 | + for (i = 0; i < ohci->num_ports; i++) | |
329 | + { | |
330 | + port = &ohci->rhport[i]; | |
331 | + port->ctrl = 0; | |
332 | + if (port->port.dev) | |
333 | + ohci_attach(&port->port, port->port.dev); | |
334 | + } | |
335 | + dprintf("usb-ohci: Reset %s\n", ohci->pci_dev.name); | |
336 | +} | |
337 | + | |
338 | +/* Update IRQ levels */ | |
339 | +static inline void ohci_intr_update(OHCIState *ohci) | |
340 | +{ | |
341 | + int level = 0; | |
342 | + | |
343 | + if ((ohci->intr & OHCI_INTR_MIE) && | |
344 | + (ohci->intr_status & ohci->intr)) | |
345 | + level = 1; | |
346 | + | |
347 | + pci_set_irq(&ohci->pci_dev, 0, level); | |
348 | +} | |
349 | + | |
350 | +/* Set an interrupt */ | |
351 | +static inline void ohci_set_interrupt(OHCIState *ohci, uint32_t intr) | |
352 | +{ | |
353 | + ohci->intr_status |= intr; | |
354 | + ohci_intr_update(ohci); | |
355 | +} | |
356 | + | |
357 | +/* Get an array of dwords from main memory */ | |
358 | +static inline int get_dwords(uint32_t addr, uint32_t *buf, int num) | |
359 | +{ | |
360 | + int i; | |
361 | + | |
362 | + for (i = 0; i < num; i++, buf++, addr += sizeof(*buf)) { | |
363 | + cpu_physical_memory_rw(addr, (uint8_t *)buf, sizeof(*buf), 0); | |
364 | + *buf = le32_to_cpu(*buf); | |
365 | + } | |
366 | + | |
367 | + return 1; | |
368 | +} | |
369 | + | |
370 | +/* Put an array of dwords in to main memory */ | |
371 | +static inline int put_dwords(uint32_t addr, uint32_t *buf, int num) | |
372 | +{ | |
373 | + int i; | |
374 | + | |
375 | + for (i = 0; i < num; i++, buf++, addr += sizeof(*buf)) { | |
376 | + uint32_t tmp = cpu_to_le32(*buf); | |
377 | + cpu_physical_memory_rw(addr, (uint8_t *)&tmp, sizeof(tmp), 1); | |
378 | + } | |
379 | + | |
380 | + return 1; | |
381 | +} | |
382 | + | |
383 | +static inline int ohci_read_ed(uint32_t addr, struct ohci_ed *ed) | |
384 | +{ | |
385 | + return get_dwords(addr, (uint32_t *)ed, sizeof(*ed) >> 2); | |
386 | +} | |
387 | + | |
388 | +static inline int ohci_read_td(uint32_t addr, struct ohci_td *td) | |
389 | +{ | |
390 | + return get_dwords(addr, (uint32_t *)td, sizeof(*td) >> 2); | |
391 | +} | |
392 | + | |
393 | +static inline int ohci_put_ed(uint32_t addr, struct ohci_ed *ed) | |
394 | +{ | |
395 | + return put_dwords(addr, (uint32_t *)ed, sizeof(*ed) >> 2); | |
396 | +} | |
397 | + | |
398 | +static inline int ohci_put_td(uint32_t addr, struct ohci_td *td) | |
399 | +{ | |
400 | + return put_dwords(addr, (uint32_t *)td, sizeof(*td) >> 2); | |
401 | +} | |
402 | + | |
403 | +/* Read/Write the contents of a TD from/to main memory. */ | |
404 | +static void ohci_copy_td(struct ohci_td *td, uint8_t *buf, int len, int write) | |
405 | +{ | |
406 | + uint32_t ptr; | |
407 | + uint32_t n; | |
408 | + | |
409 | + ptr = td->cbp; | |
410 | + n = 0x1000 - (ptr & 0xfff); | |
411 | + if (n > len) | |
412 | + n = len; | |
413 | + cpu_physical_memory_rw(ptr, buf, n, write); | |
414 | + if (n == len) | |
415 | + return; | |
416 | + ptr = td->be & ~0xfffu; | |
417 | + cpu_physical_memory_rw(ptr, buf, len - n, write); | |
418 | +} | |
419 | + | |
420 | +/* Service a transport descriptor. | |
421 | + Returns nonzero to terminate processing of this endpoint. */ | |
422 | + | |
423 | +static int ohci_service_td(OHCIState *ohci, struct ohci_ed *ed) | |
424 | +{ | |
425 | + int dir; | |
426 | + size_t len = 0; | |
427 | + uint8_t buf[8192]; | |
428 | + char *str = NULL; | |
429 | + int pid; | |
430 | + int ret; | |
431 | + int i; | |
432 | + USBDevice *dev; | |
433 | + struct ohci_td td; | |
434 | + uint32_t addr; | |
435 | + int flag_r; | |
436 | + | |
437 | + addr = ed->head & OHCI_DPTR_MASK; | |
438 | + if (!ohci_read_td(addr, &td)) { | |
439 | + fprintf(stderr, "usb-ohci: TD read error at %x\n", addr); | |
440 | + return 0; | |
441 | + } | |
442 | + | |
443 | + dir = OHCI_BM(ed->flags, ED_D); | |
444 | + switch (dir) { | |
445 | + case OHCI_TD_DIR_OUT: | |
446 | + case OHCI_TD_DIR_IN: | |
447 | + /* Same value. */ | |
448 | + break; | |
449 | + default: | |
450 | + dir = OHCI_BM(td.flags, TD_DP); | |
451 | + break; | |
452 | + } | |
453 | + | |
454 | + switch (dir) { | |
455 | + case OHCI_TD_DIR_IN: | |
456 | + str = "in"; | |
457 | + pid = USB_TOKEN_IN; | |
458 | + break; | |
459 | + case OHCI_TD_DIR_OUT: | |
460 | + str = "out"; | |
461 | + pid = USB_TOKEN_OUT; | |
462 | + break; | |
463 | + case OHCI_TD_DIR_SETUP: | |
464 | + str = "setup"; | |
465 | + pid = USB_TOKEN_SETUP; | |
466 | + break; | |
467 | + default: | |
468 | + fprintf(stderr, "usb-ohci: Bad direction\n"); | |
469 | + return 1; | |
470 | + } | |
471 | + if (td.cbp && td.be) { | |
472 | + len = (td.be - td.cbp) + 1; | |
473 | + if (len && dir != OHCI_TD_DIR_IN) { | |
474 | + ohci_copy_td(&td, buf, len, 0); | |
475 | + } | |
476 | + } | |
477 | + | |
478 | + flag_r = (td.flags & OHCI_TD_R) != 0; | |
479 | +#ifdef DEBUG_PACKET | |
480 | + dprintf(" TD @ 0x%.8x %u bytes %s r=%d cbp=0x%.8x be=0x%.8x\n", | |
481 | + addr, len, str, flag_r, td.cbp, td.be); | |
482 | + | |
483 | + if (len >= 0 && dir != OHCI_TD_DIR_IN) { | |
484 | + dprintf(" data:"); | |
485 | + for (i = 0; i < len; i++) | |
486 | + printf(" %.2x", buf[i]); | |
487 | + dprintf("\n"); | |
488 | + } | |
489 | +#endif | |
490 | + ret = USB_RET_NODEV; | |
491 | + for (i = 0; i < ohci->num_ports; i++) { | |
492 | + dev = ohci->rhport[i].port.dev; | |
493 | + if ((ohci->rhport[i].ctrl & OHCI_PORT_PES) == 0) | |
494 | + continue; | |
495 | + | |
496 | + ret = dev->handle_packet(dev, pid, OHCI_BM(ed->flags, ED_FA), | |
497 | + OHCI_BM(ed->flags, ED_EN), buf, len); | |
498 | + if (ret != USB_RET_NODEV) | |
499 | + break; | |
500 | + } | |
501 | +#ifdef DEBUG_PACKET | |
502 | + dprintf("ret=%d\n", ret); | |
503 | +#endif | |
504 | + if (ret >= 0) { | |
505 | + if (dir == OHCI_TD_DIR_IN) { | |
506 | + ohci_copy_td(&td, buf, ret, 1); | |
507 | +#ifdef DEBUG_PACKET | |
508 | + dprintf(" data:"); | |
509 | + for (i = 0; i < ret; i++) | |
510 | + printf(" %.2x", buf[i]); | |
511 | + dprintf("\n"); | |
512 | +#endif | |
513 | + } else { | |
514 | + ret = len; | |
515 | + } | |
516 | + } | |
517 | + | |
518 | + /* Writeback */ | |
519 | + if (ret == len || (dir == OHCI_TD_DIR_IN && ret >= 0 && flag_r)) { | |
520 | + /* Transmission succeeded. */ | |
521 | + if (ret == len) { | |
522 | + td.cbp = 0; | |
523 | + } else { | |
524 | + td.cbp += ret; | |
525 | + if ((td.cbp & 0xfff) + ret > 0xfff) { | |
526 | + td.cbp &= 0xfff; | |
527 | + td.cbp |= td.be & ~0xfff; | |
528 | + } | |
529 | + } | |
530 | + td.flags |= OHCI_TD_T1; | |
531 | + td.flags ^= OHCI_TD_T0; | |
532 | + OHCI_SET_BM(td.flags, TD_CC, OHCI_CC_NOERROR); | |
533 | + OHCI_SET_BM(td.flags, TD_EC, 0); | |
534 | + | |
535 | + ed->head &= ~OHCI_ED_C; | |
536 | + if (td.flags & OHCI_TD_T0) | |
537 | + ed->head |= OHCI_ED_C; | |
538 | + } else { | |
539 | + if (ret >= 0) { | |
540 | + dprintf("usb-ohci: Underrun\n"); | |
541 | + OHCI_SET_BM(td.flags, TD_CC, OHCI_CC_DATAUNDERRUN); | |
542 | + } else { | |
543 | + switch (ret) { | |
544 | + case USB_RET_NODEV: | |
545 | + OHCI_SET_BM(td.flags, TD_CC, OHCI_CC_DEVICENOTRESPONDING); | |
546 | + case USB_RET_NAK: | |
547 | + dprintf("usb-ohci: got NAK\n"); | |
548 | + return 1; | |
549 | + case USB_RET_STALL: | |
550 | + dprintf("usb-ohci: got STALL\n"); | |
551 | + OHCI_SET_BM(td.flags, TD_CC, OHCI_CC_STALL); | |
552 | + break; | |
553 | + case USB_RET_BABBLE: | |
554 | + dprintf("usb-ohci: got BABBLE\n"); | |
555 | + OHCI_SET_BM(td.flags, TD_CC, OHCI_CC_DATAOVERRUN); | |
556 | + break; | |
557 | + default: | |
558 | + fprintf(stderr, "usb-ohci: Bad device response %d\n", ret); | |
559 | + OHCI_SET_BM(td.flags, TD_CC, OHCI_CC_UNDEXPETEDPID); | |
560 | + OHCI_SET_BM(td.flags, TD_EC, 3); | |
561 | + break; | |
562 | + } | |
563 | + } | |
564 | + ed->head |= OHCI_ED_H; | |
565 | + } | |
566 | + | |
567 | + /* Retire this TD */ | |
568 | + ed->head &= ~OHCI_DPTR_MASK; | |
569 | + ed->head |= td.next & OHCI_DPTR_MASK; | |
570 | + td.next = ohci->done; | |
571 | + ohci->done = addr; | |
572 | + i = OHCI_BM(td.flags, TD_DI); | |
573 | + if (i < ohci->done_count) | |
574 | + ohci->done_count = i; | |
575 | + ohci_put_td(addr, &td); | |
576 | + return OHCI_BM(td.flags, TD_CC) != OHCI_CC_NOERROR; | |
577 | +} | |
578 | + | |
579 | +/* Service an endpoint list. Returns nonzero if active TD were found. */ | |
580 | +static int ohci_service_ed_list(OHCIState *ohci, uint32_t head) | |
581 | +{ | |
582 | + struct ohci_ed ed; | |
583 | + uint32_t next_ed; | |
584 | + uint32_t cur; | |
585 | + int active; | |
586 | + | |
587 | + active = 0; | |
588 | + | |
589 | + if (head == 0) | |
590 | + return 0; | |
591 | + | |
592 | + for (cur = head; cur; cur = next_ed) { | |
593 | + if (!ohci_read_ed(cur, &ed)) { | |
594 | + fprintf(stderr, "usb-ohci: ED read error at %x\n", cur); | |
595 | + return 0; | |
596 | + } | |
597 | + | |
598 | + next_ed = ed.next & OHCI_DPTR_MASK; | |
599 | + | |
600 | + if ((ed.head & OHCI_ED_H) || (ed.flags & OHCI_ED_K)) | |
601 | + continue; | |
602 | + | |
603 | + /* Skip isochronous endpoints. */ | |
604 | + if (ed.flags & OHCI_ED_F) | |
605 | + continue; | |
606 | + | |
607 | + while ((ed.head & OHCI_DPTR_MASK) != ed.tail) { | |
608 | +#ifdef DEBUG_PACKET | |
609 | + dprintf("ED @ 0x%.8x fa=%u en=%u d=%u s=%u k=%u f=%u mps=%u " | |
610 | + "h=%u c=%u\n head=0x%.8x tailp=0x%.8x next=0x%.8x\n", cur, | |
611 | + OHCI_BM(ed.flags, ED_FA), OHCI_BM(ed.flags, ED_EN), | |
612 | + OHCI_BM(ed.flags, ED_D), (ed.flags & OHCI_ED_S)!= 0, | |
613 | + (ed.flags & OHCI_ED_K) != 0, (ed.flags & OHCI_ED_F) != 0, | |
614 | + OHCI_BM(ed.flags, ED_MPS), (ed.head & OHCI_ED_H) != 0, | |
615 | + (ed.head & OHCI_ED_C) != 0, ed.head & OHCI_DPTR_MASK, | |
616 | + ed.tail & OHCI_DPTR_MASK, ed.next & OHCI_DPTR_MASK); | |
617 | +#endif | |
618 | + active = 1; | |
619 | + | |
620 | + if (ohci_service_td(ohci, &ed)) | |
621 | + break; | |
622 | + } | |
623 | + | |
624 | + ohci_put_ed(cur, &ed); | |
625 | + } | |
626 | + | |
627 | + return active; | |
628 | +} | |
629 | + | |
630 | +/* Generate a SOF event, and set a timer for EOF */ | |
631 | +static void ohci_sof(OHCIState *ohci) | |
632 | +{ | |
633 | + ohci->sof_time = qemu_get_clock(vm_clock); | |
634 | + qemu_mod_timer(ohci->eof_timer, ohci->sof_time + usb_frame_time); | |
635 | + ohci_set_interrupt(ohci, OHCI_INTR_SF); | |
636 | +} | |
637 | + | |
638 | +/* Do frame processing on frame boundary */ | |
639 | +static void ohci_frame_boundary(void *opaque) | |
640 | +{ | |
641 | + OHCIState *ohci = opaque; | |
642 | + struct ohci_hcca hcca; | |
643 | + | |
644 | + cpu_physical_memory_rw(ohci->hcca, (uint8_t *)&hcca, sizeof(hcca), 0); | |
645 | + | |
646 | + /* Process all the lists at the end of the frame */ | |
647 | + if (ohci->ctl & OHCI_CTL_PLE) { | |
648 | + int n; | |
649 | + | |
650 | + n = ohci->frame_number & 0x1f; | |
651 | + ohci_service_ed_list(ohci, le32_to_cpu(hcca.intr[n])); | |
652 | + } | |
653 | + if ((ohci->ctl & OHCI_CTL_CLE) && (ohci->status & OHCI_STATUS_CLF)) { | |
654 | + if (ohci->ctrl_cur && ohci->ctrl_cur != ohci->ctrl_head) | |
655 | + dprintf("usb-ohci: head %x, cur %x\n", ohci->ctrl_head, ohci->ctrl_cur); | |
656 | + if (!ohci_service_ed_list(ohci, ohci->ctrl_head)) { | |
657 | + ohci->ctrl_cur = 0; | |
658 | + ohci->status &= ~OHCI_STATUS_CLF; | |
659 | + } | |
660 | + } | |
661 | + | |
662 | + if ((ohci->ctl & OHCI_CTL_BLE) && (ohci->status & OHCI_STATUS_BLF)) { | |
663 | + if (!ohci_service_ed_list(ohci, ohci->bulk_head)) { | |
664 | + ohci->bulk_cur = 0; | |
665 | + ohci->status &= ~OHCI_STATUS_BLF; | |
666 | + } | |
667 | + } | |
668 | + | |
669 | + /* Frame boundary, so do EOF stuf here */ | |
670 | + ohci->frt = ohci->fit; | |
671 | + | |
672 | + /* XXX: endianness */ | |
673 | + ohci->frame_number = (ohci->frame_number + 1) & 0xffff; | |
674 | + hcca.frame = cpu_to_le32(ohci->frame_number); | |
675 | + | |
676 | + if (ohci->done_count == 0 && !(ohci->intr_status & OHCI_INTR_WD)) { | |
677 | + if (!ohci->done) | |
678 | + abort(); | |
679 | + if (ohci->intr & ohci->intr_status) | |
680 | + ohci->done |= 1; | |
681 | + hcca.done = cpu_to_le32(ohci->done); | |
682 | + ohci->done = 0; | |
683 | + ohci->done_count = 7; | |
684 | + ohci_set_interrupt(ohci, OHCI_INTR_WD); | |
685 | + } | |
686 | + | |
687 | + if (ohci->done_count != 7 && ohci->done_count != 0) | |
688 | + ohci->done_count--; | |
689 | + | |
690 | + /* Do SOF stuff here */ | |
691 | + ohci_sof(ohci); | |
692 | + | |
693 | + /* Writeback HCCA */ | |
694 | + cpu_physical_memory_rw(ohci->hcca, (uint8_t *)&hcca, sizeof(hcca), 1); | |
695 | +} | |
696 | + | |
697 | +/* Start sending SOF tokens across the USB bus, lists are processed in | |
698 | + * next frame | |
699 | + */ | |
700 | +static int ohci_bus_start(OHCIState *ohci) | |
701 | +{ | |
702 | + ohci->eof_timer = qemu_new_timer(vm_clock, | |
703 | + ohci_frame_boundary, | |
704 | + ohci); | |
705 | + | |
706 | + if (ohci->eof_timer == NULL) { | |
707 | + fprintf(stderr, "usb-ohci: %s: qemu_new_timer failed\n", | |
708 | + ohci->pci_dev.name); | |
709 | + /* TODO: Signal unrecoverable error */ | |
710 | + return 0; | |
711 | + } | |
712 | + | |
713 | + dprintf("usb-ohci: %s: USB Operational\n", ohci->pci_dev.name); | |
714 | + | |
715 | + ohci_sof(ohci); | |
716 | + | |
717 | + return 1; | |
718 | +} | |
719 | + | |
720 | +/* Stop sending SOF tokens on the bus */ | |
721 | +static void ohci_bus_stop(OHCIState *ohci) | |
722 | +{ | |
723 | + if (ohci->eof_timer) | |
724 | + qemu_del_timer(ohci->eof_timer); | |
725 | +} | |
726 | + | |
727 | +/* Sets a flag in a port status register but only set it if the port is | |
728 | + * connected, if not set ConnectStatusChange flag. If flag is enabled | |
729 | + * return 1. | |
730 | + */ | |
731 | +static int ohci_port_set_if_connected(OHCIState *ohci, int i, uint32_t val) | |
732 | +{ | |
733 | + int ret = 1; | |
734 | + | |
735 | + /* writing a 0 has no effect */ | |
736 | + if (val == 0) | |
737 | + return 0; | |
738 | + | |
739 | + /* If CurrentConnectStatus is cleared we set | |
740 | + * ConnectStatusChange | |
741 | + */ | |
742 | + if (!(ohci->rhport[i].ctrl & OHCI_PORT_CCS)) { | |
743 | + ohci->rhport[i].ctrl |= OHCI_PORT_CSC; | |
744 | + if (ohci->rhstatus & OHCI_RHS_DRWE) { | |
745 | + /* TODO: CSC is a wakeup event */ | |
746 | + } | |
747 | + return 0; | |
748 | + } | |
749 | + | |
750 | + if (ohci->rhport[i].ctrl & val) | |
751 | + ret = 0; | |
752 | + | |
753 | + /* set the bit */ | |
754 | + ohci->rhport[i].ctrl |= val; | |
755 | + | |
756 | + return ret; | |
757 | +} | |
758 | + | |
759 | +/* Set the frame interval - frame interval toggle is manipulated by the hcd only */ | |
760 | +static void ohci_set_frame_interval(OHCIState *ohci, uint16_t val) | |
761 | +{ | |
762 | + val &= OHCI_FMI_FI; | |
763 | + | |
764 | + if (val != ohci->fi) { | |
765 | + dprintf("usb-ohci: %s: FrameInterval = 0x%x (%u)\n", | |
766 | + ohci->pci_dev.name, ohci->fi, ohci->fi); | |
767 | + } | |
768 | + | |
769 | + ohci->fi = val; | |
770 | +} | |
771 | + | |
772 | +static void ohci_port_power(OHCIState *ohci, int i, int p) | |
773 | +{ | |
774 | + if (p) { | |
775 | + ohci->rhport[i].ctrl |= OHCI_PORT_PPS; | |
776 | + } else { | |
777 | + ohci->rhport[i].ctrl &= ~(OHCI_PORT_PPS| | |
778 | + OHCI_PORT_CCS| | |
779 | + OHCI_PORT_PSS| | |
780 | + OHCI_PORT_PRS); | |
781 | + } | |
782 | +} | |
783 | + | |
784 | +/* Set HcControlRegister */ | |
785 | +static void ohci_set_ctl(OHCIState *ohci, uint32_t val) | |
786 | +{ | |
787 | + uint32_t old_state; | |
788 | + uint32_t new_state; | |
789 | + | |
790 | + old_state = ohci->ctl & OHCI_CTL_HCFS; | |
791 | + ohci->ctl = val; | |
792 | + new_state = ohci->ctl & OHCI_CTL_HCFS; | |
793 | + | |
794 | + /* no state change */ | |
795 | + if (old_state == new_state) | |
796 | + return; | |
797 | + | |
798 | + switch (new_state) { | |
799 | + case OHCI_USB_OPERATIONAL: | |
800 | + ohci_bus_start(ohci); | |
801 | + break; | |
802 | + case OHCI_USB_SUSPEND: | |
803 | + ohci_bus_stop(ohci); | |
804 | + dprintf("usb-ohci: %s: USB Suspended\n", ohci->pci_dev.name); | |
805 | + break; | |
806 | + case OHCI_USB_RESUME: | |
807 | + dprintf("usb-ohci: %s: USB Resume\n", ohci->pci_dev.name); | |
808 | + break; | |
809 | + case OHCI_USB_RESET: | |
810 | + dprintf("usb-ohci: %s: USB Reset\n", ohci->pci_dev.name); | |
811 | + break; | |
812 | + } | |
813 | +} | |
814 | + | |
815 | +static uint32_t ohci_get_frame_remaining(OHCIState *ohci) | |
816 | +{ | |
817 | + uint16_t fr; | |
818 | + int64_t tks; | |
819 | + | |
820 | + if ((ohci->ctl & OHCI_CTL_HCFS) != OHCI_USB_OPERATIONAL) | |
821 | + return (ohci->frt << 31); | |
822 | + | |
823 | + /* Being in USB operational state guarnatees sof_time was | |
824 | + * set already. | |
825 | + */ | |
826 | + tks = qemu_get_clock(vm_clock) - ohci->sof_time; | |
827 | + | |
828 | + /* avoid muldiv if possible */ | |
829 | + if (tks >= usb_frame_time) | |
830 | + return (ohci->frt << 31); | |
831 | + | |
832 | + tks = muldiv64(1, tks, usb_bit_time); | |
833 | + fr = (uint16_t)(ohci->fi - tks); | |
834 | + | |
835 | + return (ohci->frt << 31) | fr; | |
836 | +} | |
837 | + | |
838 | + | |
839 | +/* Set root hub status */ | |
840 | +static void ohci_set_hub_status(OHCIState *ohci, uint32_t val) | |
841 | +{ | |
842 | + uint32_t old_state; | |
843 | + | |
844 | + old_state = ohci->rhstatus; | |
845 | + | |
846 | + /* write 1 to clear OCIC */ | |
847 | + if (val & OHCI_RHS_OCIC) | |
848 | + ohci->rhstatus &= ~OHCI_RHS_OCIC; | |
849 | + | |
850 | + if (val & OHCI_RHS_LPS) { | |
851 | + int i; | |
852 | + | |
853 | + for (i = 0; i < ohci->num_ports; i++) | |
854 | + ohci_port_power(ohci, i, 0); | |
855 | + dprintf("usb-ohci: powered down all ports\n"); | |
856 | + } | |
857 | + | |
858 | + if (val & OHCI_RHS_LPSC) { | |
859 | + int i; | |
860 | + | |
861 | + for (i = 0; i < ohci->num_ports; i++) | |
862 | + ohci_port_power(ohci, i, 1); | |
863 | + dprintf("usb-ohci: powered up all ports\n"); | |
864 | + } | |
865 | + | |
866 | + if (val & OHCI_RHS_DRWE) | |
867 | + ohci->rhstatus |= OHCI_RHS_DRWE; | |
868 | + | |
869 | + if (val & OHCI_RHS_CRWE) | |
870 | + ohci->rhstatus &= ~OHCI_RHS_DRWE; | |
871 | + | |
872 | + if (old_state != ohci->rhstatus) | |
873 | + ohci_set_interrupt(ohci, OHCI_INTR_RHSC); | |
874 | +} | |
875 | + | |
876 | +/* Set root hub port status */ | |
877 | +static void ohci_port_set_status(OHCIState *ohci, int portnum, uint32_t val) | |
878 | +{ | |
879 | + uint32_t old_state; | |
880 | + OHCIPort *port; | |
881 | + | |
882 | + port = &ohci->rhport[portnum]; | |
883 | + old_state = port->ctrl; | |
884 | + | |
885 | + /* Write to clear CSC, PESC, PSSC, OCIC, PRSC */ | |
886 | + if (val & OHCI_PORT_WTC) | |
887 | + port->ctrl &= ~(val & OHCI_PORT_WTC); | |
888 | + | |
889 | + if (val & OHCI_PORT_CCS) | |
890 | + port->ctrl &= ~OHCI_PORT_PES; | |
891 | + | |
892 | + ohci_port_set_if_connected(ohci, portnum, val & OHCI_PORT_PES); | |
893 | + | |
894 | + if (ohci_port_set_if_connected(ohci, portnum, val & OHCI_PORT_PSS)) | |
895 | + dprintf("usb-ohci: port %d: SUSPEND\n", portnum); | |
896 | + | |
897 | + if (ohci_port_set_if_connected(ohci, portnum, val & OHCI_PORT_PRS)) { | |
898 | + dprintf("usb-ohci: port %d: RESET\n", portnum); | |
899 | + port->port.dev->handle_packet(port->port.dev, USB_MSG_RESET, | |
900 | + 0, 0, NULL, 0); | |
901 | + port->ctrl &= ~OHCI_PORT_PRS; | |
902 | + /* ??? Should this also set OHCI_PORT_PESC. */ | |
903 | + port->ctrl |= OHCI_PORT_PES | OHCI_PORT_PRSC; | |
904 | + } | |
905 | + | |
906 | + /* Invert order here to ensure in ambiguous case, device is | |
907 | + * powered up... | |
908 | + */ | |
909 | + if (val & OHCI_PORT_LSDA) | |
910 | + ohci_port_power(ohci, portnum, 0); | |
911 | + if (val & OHCI_PORT_PPS) | |
912 | + ohci_port_power(ohci, portnum, 1); | |
913 | + | |
914 | + if (old_state != port->ctrl) | |
915 | + ohci_set_interrupt(ohci, OHCI_INTR_RHSC); | |
916 | + | |
917 | + return; | |
918 | +} | |
919 | + | |
920 | +static uint32_t ohci_mem_read(void *ptr, target_phys_addr_t addr) | |
921 | +{ | |
922 | + OHCIState *ohci = ptr; | |
923 | + | |
924 | + addr -= ohci->mem_base; | |
925 | + | |
926 | + /* Only aligned reads are allowed on OHCI */ | |
927 | + if (addr & 3) { | |
928 | + fprintf(stderr, "usb-ohci: Mis-aligned read\n"); | |
929 | + return 0xffffffff; | |
930 | + } | |
931 | + | |
932 | + if (addr >= 0x54 && addr < 0x54 + ohci->num_ports * 4) { | |
933 | + /* HcRhPortStatus */ | |
934 | + return ohci->rhport[(addr - 0x54) >> 2].ctrl | OHCI_PORT_PPS; | |
935 | + } | |
936 | + | |
937 | + switch (addr >> 2) { | |
938 | + case 0: /* HcRevision */ | |
939 | + return 0x10; | |
940 | + | |
941 | + case 1: /* HcControl */ | |
942 | + return ohci->ctl; | |
943 | + | |
944 | + case 2: /* HcCommandStatus */ | |
945 | + return ohci->status; | |
946 | + | |
947 | + case 3: /* HcInterruptStatus */ | |
948 | + return ohci->intr_status; | |
949 | + | |
950 | + case 4: /* HcInterruptEnable */ | |
951 | + case 5: /* HcInterruptDisable */ | |
952 | + return ohci->intr; | |
953 | + | |
954 | + case 6: /* HcHCCA */ | |
955 | + return ohci->hcca; | |
956 | + | |
957 | + case 7: /* HcPeriodCurrentED */ | |
958 | + return ohci->per_cur; | |
959 | + | |
960 | + case 8: /* HcControlHeadED */ | |
961 | + return ohci->ctrl_head; | |
962 | + | |
963 | + case 9: /* HcControlCurrentED */ | |
964 | + return ohci->ctrl_cur; | |
965 | + | |
966 | + case 10: /* HcBulkHeadED */ | |
967 | + return ohci->bulk_head; | |
968 | + | |
969 | + case 11: /* HcBulkCurrentED */ | |
970 | + return ohci->bulk_cur; | |
971 | + | |
972 | + case 12: /* HcDoneHead */ | |
973 | + return ohci->done; | |
974 | + | |
975 | + case 13: /* HcFmInterval */ | |
976 | + return (ohci->fit << 31) | (ohci->fsmps << 16) | (ohci->fi); | |
977 | + | |
978 | + case 14: /* HcFmRemaining */ | |
979 | + return ohci_get_frame_remaining(ohci); | |
980 | + | |
981 | + case 15: /* HcFmNumber */ | |
982 | + return ohci->frame_number; | |
983 | + | |
984 | + case 16: /* HcPeriodicStart */ | |
985 | + return ohci->pstart; | |
986 | + | |
987 | + case 17: /* HcLSThreshold */ | |
988 | + return ohci->lst; | |
989 | + | |
990 | + case 18: /* HcRhDescriptorA */ | |
991 | + return ohci->rhdesc_a; | |
992 | + | |
993 | + case 19: /* HcRhDescriptorB */ | |
994 | + return ohci->rhdesc_b; | |
995 | + | |
996 | + case 20: /* HcRhStatus */ | |
997 | + return ohci->rhstatus; | |
998 | + | |
999 | + default: | |
1000 | + fprintf(stderr, "ohci_read: Bad offset %x\n", (int)addr); | |
1001 | + return 0xffffffff; | |
1002 | + } | |
1003 | +} | |
1004 | + | |
1005 | +static void ohci_mem_write(void *ptr, target_phys_addr_t addr, uint32_t val) | |
1006 | +{ | |
1007 | + OHCIState *ohci = ptr; | |
1008 | + | |
1009 | + addr -= ohci->mem_base; | |
1010 | + | |
1011 | + /* Only aligned reads are allowed on OHCI */ | |
1012 | + if (addr & 3) { | |
1013 | + fprintf(stderr, "usb-ohci: Mis-aligned write\n"); | |
1014 | + return; | |
1015 | + } | |
1016 | + | |
1017 | + if (addr >= 0x54 && addr < 0x54 + ohci->num_ports * 4) { | |
1018 | + /* HcRhPortStatus */ | |
1019 | + ohci_port_set_status(ohci, (addr - 0x54) >> 2, val); | |
1020 | + return; | |
1021 | + } | |
1022 | + | |
1023 | + switch (addr >> 2) { | |
1024 | + case 1: /* HcControl */ | |
1025 | + ohci_set_ctl(ohci, val); | |
1026 | + break; | |
1027 | + | |
1028 | + case 2: /* HcCommandStatus */ | |
1029 | + /* SOC is read-only */ | |
1030 | + val = (val & ~OHCI_STATUS_SOC); | |
1031 | + | |
1032 | + /* Bits written as '0' remain unchanged in the register */ | |
1033 | + ohci->status |= val; | |
1034 | + | |
1035 | + if (ohci->status & OHCI_STATUS_HCR) | |
1036 | + ohci_reset(ohci); | |
1037 | + break; | |
1038 | + | |
1039 | + case 3: /* HcInterruptStatus */ | |
1040 | + ohci->intr_status &= ~val; | |
1041 | + ohci_intr_update(ohci); | |
1042 | + break; | |
1043 | + | |
1044 | + case 4: /* HcInterruptEnable */ | |
1045 | + ohci->intr |= val; | |
1046 | + ohci_intr_update(ohci); | |
1047 | + break; | |
1048 | + | |
1049 | + case 5: /* HcInterruptDisable */ | |
1050 | + ohci->intr &= ~val; | |
1051 | + ohci_intr_update(ohci); | |
1052 | + break; | |
1053 | + | |
1054 | + case 6: /* HcHCCA */ | |
1055 | + ohci->hcca = val & OHCI_HCCA_MASK; | |
1056 | + break; | |
1057 | + | |
1058 | + case 8: /* HcControlHeadED */ | |
1059 | + ohci->ctrl_head = val & OHCI_EDPTR_MASK; | |
1060 | + break; | |
1061 | + | |
1062 | + case 9: /* HcControlCurrentED */ | |
1063 | + ohci->ctrl_cur = val & OHCI_EDPTR_MASK; | |
1064 | + break; | |
1065 | + | |
1066 | + case 10: /* HcBulkHeadED */ | |
1067 | + ohci->bulk_head = val & OHCI_EDPTR_MASK; | |
1068 | + break; | |
1069 | + | |
1070 | + case 11: /* HcBulkCurrentED */ | |
1071 | + ohci->bulk_cur = val & OHCI_EDPTR_MASK; | |
1072 | + break; | |
1073 | + | |
1074 | + case 13: /* HcFmInterval */ | |
1075 | + ohci->fsmps = (val & OHCI_FMI_FSMPS) >> 16; | |
1076 | + ohci->fit = (val & OHCI_FMI_FIT) >> 31; | |
1077 | + ohci_set_frame_interval(ohci, val); | |
1078 | + break; | |
1079 | + | |
1080 | + case 16: /* HcPeriodicStart */ | |
1081 | + ohci->pstart = val & 0xffff; | |
1082 | + break; | |
1083 | + | |
1084 | + case 17: /* HcLSThreshold */ | |
1085 | + ohci->lst = val & 0xffff; | |
1086 | + break; | |
1087 | + | |
1088 | + case 18: /* HcRhDescriptorA */ | |
1089 | + ohci->rhdesc_a &= ~OHCI_RHA_RW_MASK; | |
1090 | + ohci->rhdesc_a |= val & OHCI_RHA_RW_MASK; | |
1091 | + break; | |
1092 | + | |
1093 | + case 19: /* HcRhDescriptorB */ | |
1094 | + break; | |
1095 | + | |
1096 | + case 20: /* HcRhStatus */ | |
1097 | + ohci_set_hub_status(ohci, val); | |
1098 | + break; | |
1099 | + | |
1100 | + default: | |
1101 | + fprintf(stderr, "ohci_write: Bad offset %x\n", (int)addr); | |
1102 | + break; | |
1103 | + } | |
1104 | +} | |
1105 | + | |
1106 | +/* Only dword reads are defined on OHCI register space */ | |
1107 | +static CPUReadMemoryFunc *ohci_readfn[3]={ | |
1108 | + ohci_mem_read, | |
1109 | + ohci_mem_read, | |
1110 | + ohci_mem_read | |
1111 | +}; | |
1112 | + | |
1113 | +/* Only dword writes are defined on OHCI register space */ | |
1114 | +static CPUWriteMemoryFunc *ohci_writefn[3]={ | |
1115 | + ohci_mem_write, | |
1116 | + ohci_mem_write, | |
1117 | + ohci_mem_write | |
1118 | +}; | |
1119 | + | |
1120 | +static void ohci_mapfunc(PCIDevice *pci_dev, int i, | |
1121 | + uint32_t addr, uint32_t size, int type) | |
1122 | +{ | |
1123 | + OHCIState *ohci = (OHCIState *)pci_dev; | |
1124 | + ohci->mem_base = addr; | |
1125 | + cpu_register_physical_memory(addr, size, ohci->mem); | |
1126 | +} | |
1127 | + | |
1128 | +void usb_ohci_init(struct PCIBus *bus, int num_ports, int devfn) | |
1129 | +{ | |
1130 | + OHCIState *ohci; | |
1131 | + int vid = 0x106b; | |
1132 | + int did = 0x003f; | |
1133 | + int i; | |
1134 | + | |
1135 | + | |
1136 | + if (usb_frame_time == 0) { | |
1137 | +#if OHCI_TIME_WARP | |
1138 | + usb_frame_time = ticks_per_sec; | |
1139 | + usb_bit_time = muldiv64(1, ticks_per_sec, USB_HZ/1000); | |
1140 | +#else | |
1141 | + usb_frame_time = muldiv64(1, ticks_per_sec, 1000); | |
1142 | + if (ticks_per_sec >= USB_HZ) { | |
1143 | + usb_bit_time = muldiv64(1, ticks_per_sec, USB_HZ); | |
1144 | + } else { | |
1145 | + usb_bit_time = 1; | |
1146 | + } | |
1147 | +#endif | |
1148 | + dprintf("usb-ohci: usb_bit_time=%lli usb_frame_time=%lli\n", | |
1149 | + usb_frame_time, usb_bit_time); | |
1150 | + } | |
1151 | + | |
1152 | + ohci = (OHCIState *)pci_register_device(bus, "OHCI USB", sizeof(*ohci), | |
1153 | + devfn, NULL, NULL); | |
1154 | + if (ohci == NULL) { | |
1155 | + fprintf(stderr, "usb-ohci: Failed to register PCI device\n"); | |
1156 | + return; | |
1157 | + } | |
1158 | + | |
1159 | + ohci->pci_dev.config[0x00] = vid & 0xff; | |
1160 | + ohci->pci_dev.config[0x01] = (vid >> 8) & 0xff; | |
1161 | + ohci->pci_dev.config[0x02] = did & 0xff; | |
1162 | + ohci->pci_dev.config[0x03] = (did >> 8) & 0xff; | |
1163 | + ohci->pci_dev.config[0x09] = 0x10; /* OHCI */ | |
1164 | + ohci->pci_dev.config[0x0a] = 0x3; | |
1165 | + ohci->pci_dev.config[0x0b] = 0xc; | |
1166 | + ohci->pci_dev.config[0x3d] = 0x01; /* interrupt pin 1 */ | |
1167 | + | |
1168 | + ohci->mem = cpu_register_io_memory(0, ohci_readfn, ohci_writefn, ohci); | |
1169 | + | |
1170 | + pci_register_io_region((struct PCIDevice *)ohci, 0, 256, | |
1171 | + PCI_ADDRESS_SPACE_MEM, ohci_mapfunc); | |
1172 | + | |
1173 | + ohci->num_ports = num_ports; | |
1174 | + for (i = 0; i < num_ports; i++) { | |
1175 | + qemu_register_usb_port(&ohci->rhport[i].port, ohci, i, ohci_attach); | |
1176 | + } | |
1177 | + | |
1178 | + ohci_reset(ohci); | |
1179 | +} | ... | ... |
hw/usb-uhci.c
... | ... | @@ -638,11 +638,10 @@ static void uhci_map(PCIDevice *pci_dev, int region_num, |
638 | 638 | register_ioport_read(addr, 32, 1, uhci_ioport_readb, s); |
639 | 639 | } |
640 | 640 | |
641 | -void usb_uhci_init(PCIBus *bus, USBPort **usb_ports, int devfn) | |
641 | +void usb_uhci_init(PCIBus *bus, int devfn) | |
642 | 642 | { |
643 | 643 | UHCIState *s; |
644 | 644 | uint8_t *pci_conf; |
645 | - UHCIPort *port; | |
646 | 645 | int i; |
647 | 646 | |
648 | 647 | s = (UHCIState *)pci_register_device(bus, |
... | ... | @@ -662,11 +661,7 @@ void usb_uhci_init(PCIBus *bus, USBPort **usb_ports, int devfn) |
662 | 661 | pci_conf[0x60] = 0x10; // release number |
663 | 662 | |
664 | 663 | for(i = 0; i < NB_PORTS; i++) { |
665 | - port = &s->ports[i]; | |
666 | - port->port.opaque = s; | |
667 | - port->port.index = i; | |
668 | - port->port.attach = uhci_attach; | |
669 | - usb_ports[i] = &port->port; | |
664 | + qemu_register_usb_port(&s->ports[i].port, s, i, uhci_attach); | |
670 | 665 | } |
671 | 666 | s->frame_timer = qemu_new_timer(vm_clock, uhci_frame_timer, s); |
672 | 667 | ... | ... |
hw/usb.h
... | ... | @@ -137,12 +137,15 @@ struct USBDevice { |
137 | 137 | int setup_index; |
138 | 138 | }; |
139 | 139 | |
140 | +typedef void (*usb_attachfn)(USBPort *port, USBDevice *dev); | |
141 | + | |
140 | 142 | /* USB port on which a device can be connected */ |
141 | 143 | struct USBPort { |
142 | 144 | USBDevice *dev; |
143 | - void (*attach)(USBPort *port, USBDevice *dev); | |
145 | + usb_attachfn attach; | |
144 | 146 | void *opaque; |
145 | 147 | int index; /* internal port index, may be used with the opaque */ |
148 | + struct USBPort *next; /* Used internally by qemu. */ | |
146 | 149 | }; |
147 | 150 | |
148 | 151 | void usb_attach(USBPort *port, USBDevice *dev); |
... | ... | @@ -152,10 +155,13 @@ int usb_generic_handle_packet(USBDevice *s, int pid, |
152 | 155 | int set_usb_string(uint8_t *buf, const char *str); |
153 | 156 | |
154 | 157 | /* usb hub */ |
155 | -USBDevice *usb_hub_init(USBPort **usb_ports, int nb_ports); | |
158 | +USBDevice *usb_hub_init(int nb_ports); | |
156 | 159 | |
157 | 160 | /* usb-uhci.c */ |
158 | -void usb_uhci_init(PCIBus *bus, USBPort **usb_ports, int devfn); | |
161 | +void usb_uhci_init(PCIBus *bus, int devfn); | |
162 | + | |
163 | +/* usb-ohci.c */ | |
164 | +void usb_ohci_init(struct PCIBus *bus, int num_ports, int devfn); | |
159 | 165 | |
160 | 166 | /* usb-linux.c */ |
161 | 167 | USBDevice *usb_host_device_open(const char *devname); | ... | ... |
hw/versatilepb.c
... | ... | @@ -374,6 +374,9 @@ static void versatile_init(int ram_size, int vga_ram_size, int boot_device, |
374 | 374 | pci_nic_init(pci_bus, nd); |
375 | 375 | } |
376 | 376 | } |
377 | + if (usb_enabled) { | |
378 | + usb_ohci_init(pci_bus, 3, -1); | |
379 | + } | |
377 | 380 | |
378 | 381 | pl011_init(0x101f1000, pic, 12, serial_hds[0]); |
379 | 382 | pl011_init(0x101f2000, pic, 13, serial_hds[1]); | ... | ... |
vl.c
... | ... | @@ -106,6 +106,9 @@ |
106 | 106 | /* in ms */ |
107 | 107 | #define GUI_REFRESH_INTERVAL 30 |
108 | 108 | |
109 | +/* Max number of USB devices that can be specified on the commandline. */ | |
110 | +#define MAX_USB_CMDLINE 8 | |
111 | + | |
109 | 112 | /* XXX: use a two level table to limit memory usage */ |
110 | 113 | #define MAX_IOPORTS 65536 |
111 | 114 | |
... | ... | @@ -145,8 +148,6 @@ CharDriverState *parallel_hds[MAX_PARALLEL_PORTS]; |
145 | 148 | int win2k_install_hack = 0; |
146 | 149 | #endif |
147 | 150 | int usb_enabled = 0; |
148 | -USBPort *vm_usb_ports[MAX_VM_USB_PORTS]; | |
149 | -USBDevice *vm_usb_hub; | |
150 | 151 | static VLANState *first_vlan; |
151 | 152 | int smp_cpus = 1; |
152 | 153 | int vnc_display = -1; |
... | ... | @@ -3249,47 +3250,71 @@ void do_info_network(void) |
3249 | 3250 | /***********************************************************/ |
3250 | 3251 | /* USB devices */ |
3251 | 3252 | |
3253 | +static USBPort *used_usb_ports; | |
3254 | +static USBPort *free_usb_ports; | |
3255 | + | |
3256 | +/* ??? Maybe change this to register a hub to keep track of the topology. */ | |
3257 | +void qemu_register_usb_port(USBPort *port, void *opaque, int index, | |
3258 | + usb_attachfn attach) | |
3259 | +{ | |
3260 | + port->opaque = opaque; | |
3261 | + port->index = index; | |
3262 | + port->attach = attach; | |
3263 | + port->next = free_usb_ports; | |
3264 | + free_usb_ports = port; | |
3265 | +} | |
3266 | + | |
3252 | 3267 | static int usb_device_add(const char *devname) |
3253 | 3268 | { |
3254 | 3269 | const char *p; |
3255 | 3270 | USBDevice *dev; |
3256 | - int i; | |
3271 | + USBPort *port; | |
3257 | 3272 | |
3258 | - if (!vm_usb_hub) | |
3259 | - return -1; | |
3260 | - for(i = 0;i < MAX_VM_USB_PORTS; i++) { | |
3261 | - if (!vm_usb_ports[i]->dev) | |
3262 | - break; | |
3263 | - } | |
3264 | - if (i == MAX_VM_USB_PORTS) | |
3273 | + if (!free_usb_ports) | |
3265 | 3274 | return -1; |
3266 | 3275 | |
3267 | 3276 | if (strstart(devname, "host:", &p)) { |
3268 | 3277 | dev = usb_host_device_open(p); |
3269 | - if (!dev) | |
3270 | - return -1; | |
3271 | 3278 | } else if (!strcmp(devname, "mouse")) { |
3272 | 3279 | dev = usb_mouse_init(); |
3273 | - if (!dev) | |
3274 | - return -1; | |
3275 | 3280 | } else if (!strcmp(devname, "tablet")) { |
3276 | 3281 | dev = usb_tablet_init(); |
3277 | - if (!dev) | |
3278 | - return -1; | |
3279 | 3282 | } else { |
3280 | 3283 | return -1; |
3281 | 3284 | } |
3282 | - usb_attach(vm_usb_ports[i], dev); | |
3285 | + if (!dev) | |
3286 | + return -1; | |
3287 | + | |
3288 | + /* Find a USB port to add the device to. */ | |
3289 | + port = free_usb_ports; | |
3290 | + if (!port->next) { | |
3291 | + USBDevice *hub; | |
3292 | + | |
3293 | + /* Create a new hub and chain it on. */ | |
3294 | + free_usb_ports = NULL; | |
3295 | + port->next = used_usb_ports; | |
3296 | + used_usb_ports = port; | |
3297 | + | |
3298 | + hub = usb_hub_init(VM_USB_HUB_SIZE); | |
3299 | + usb_attach(port, hub); | |
3300 | + port = free_usb_ports; | |
3301 | + } | |
3302 | + | |
3303 | + free_usb_ports = port->next; | |
3304 | + port->next = used_usb_ports; | |
3305 | + used_usb_ports = port; | |
3306 | + usb_attach(port, dev); | |
3283 | 3307 | return 0; |
3284 | 3308 | } |
3285 | 3309 | |
3286 | 3310 | static int usb_device_del(const char *devname) |
3287 | 3311 | { |
3288 | - USBDevice *dev; | |
3289 | - int bus_num, addr, i; | |
3312 | + USBPort *port; | |
3313 | + USBPort **lastp; | |
3314 | + int bus_num, addr; | |
3290 | 3315 | const char *p; |
3291 | 3316 | |
3292 | - if (!vm_usb_hub) | |
3317 | + if (!used_usb_ports) | |
3293 | 3318 | return -1; |
3294 | 3319 | |
3295 | 3320 | p = strchr(devname, '.'); |
... | ... | @@ -3299,14 +3324,21 @@ static int usb_device_del(const char *devname) |
3299 | 3324 | addr = strtoul(p + 1, NULL, 0); |
3300 | 3325 | if (bus_num != 0) |
3301 | 3326 | return -1; |
3302 | - for(i = 0;i < MAX_VM_USB_PORTS; i++) { | |
3303 | - dev = vm_usb_ports[i]->dev; | |
3304 | - if (dev && dev->addr == addr) | |
3305 | - break; | |
3327 | + | |
3328 | + lastp = &used_usb_ports; | |
3329 | + port = used_usb_ports; | |
3330 | + while (port && port->dev->addr != addr) { | |
3331 | + lastp = &port->next; | |
3332 | + port = port->next; | |
3306 | 3333 | } |
3307 | - if (i == MAX_VM_USB_PORTS) | |
3334 | + | |
3335 | + if (!port) | |
3308 | 3336 | return -1; |
3309 | - usb_attach(vm_usb_ports[i], NULL); | |
3337 | + | |
3338 | + *lastp = port->next; | |
3339 | + usb_attach(port, NULL); | |
3340 | + port->next = free_usb_ports; | |
3341 | + free_usb_ports = port; | |
3310 | 3342 | return 0; |
3311 | 3343 | } |
3312 | 3344 | |
... | ... | @@ -3329,35 +3361,34 @@ void do_usb_del(const char *devname) |
3329 | 3361 | void usb_info(void) |
3330 | 3362 | { |
3331 | 3363 | USBDevice *dev; |
3332 | - int i; | |
3364 | + USBPort *port; | |
3333 | 3365 | const char *speed_str; |
3334 | 3366 | |
3335 | - if (!vm_usb_hub) { | |
3367 | + if (!usb_enabled) { | |
3336 | 3368 | term_printf("USB support not enabled\n"); |
3337 | 3369 | return; |
3338 | 3370 | } |
3339 | 3371 | |
3340 | - for(i = 0; i < MAX_VM_USB_PORTS; i++) { | |
3341 | - dev = vm_usb_ports[i]->dev; | |
3342 | - if (dev) { | |
3343 | - term_printf("Hub port %d:\n", i); | |
3344 | - switch(dev->speed) { | |
3345 | - case USB_SPEED_LOW: | |
3346 | - speed_str = "1.5"; | |
3347 | - break; | |
3348 | - case USB_SPEED_FULL: | |
3349 | - speed_str = "12"; | |
3350 | - break; | |
3351 | - case USB_SPEED_HIGH: | |
3352 | - speed_str = "480"; | |
3353 | - break; | |
3354 | - default: | |
3355 | - speed_str = "?"; | |
3356 | - break; | |
3357 | - } | |
3358 | - term_printf(" Device %d.%d, speed %s Mb/s\n", | |
3359 | - 0, dev->addr, speed_str); | |
3372 | + for (port = used_usb_ports; port; port = port->next) { | |
3373 | + dev = port->dev; | |
3374 | + if (!dev) | |
3375 | + continue; | |
3376 | + switch(dev->speed) { | |
3377 | + case USB_SPEED_LOW: | |
3378 | + speed_str = "1.5"; | |
3379 | + break; | |
3380 | + case USB_SPEED_FULL: | |
3381 | + speed_str = "12"; | |
3382 | + break; | |
3383 | + case USB_SPEED_HIGH: | |
3384 | + speed_str = "480"; | |
3385 | + break; | |
3386 | + default: | |
3387 | + speed_str = "?"; | |
3388 | + break; | |
3360 | 3389 | } |
3390 | + term_printf(" Device %d.%d, speed %s Mb/s\n", | |
3391 | + 0, dev->addr, speed_str); | |
3361 | 3392 | } |
3362 | 3393 | } |
3363 | 3394 | |
... | ... | @@ -5066,7 +5097,7 @@ int main(int argc, char **argv) |
5066 | 5097 | int parallel_device_index; |
5067 | 5098 | const char *loadvm = NULL; |
5068 | 5099 | QEMUMachine *machine; |
5069 | - char usb_devices[MAX_VM_USB_PORTS][128]; | |
5100 | + char usb_devices[MAX_USB_CMDLINE][128]; | |
5070 | 5101 | int usb_devices_index; |
5071 | 5102 | |
5072 | 5103 | LIST_INIT (&vm_change_state_head); |
... | ... | @@ -5425,7 +5456,7 @@ int main(int argc, char **argv) |
5425 | 5456 | break; |
5426 | 5457 | case QEMU_OPTION_usbdevice: |
5427 | 5458 | usb_enabled = 1; |
5428 | - if (usb_devices_index >= MAX_VM_USB_PORTS) { | |
5459 | + if (usb_devices_index >= MAX_USB_CMDLINE) { | |
5429 | 5460 | fprintf(stderr, "Too many USB devices\n"); |
5430 | 5461 | exit(1); |
5431 | 5462 | } |
... | ... | @@ -5596,17 +5627,6 @@ int main(int argc, char **argv) |
5596 | 5627 | } |
5597 | 5628 | } |
5598 | 5629 | |
5599 | - /* init USB devices */ | |
5600 | - if (usb_enabled) { | |
5601 | - vm_usb_hub = usb_hub_init(vm_usb_ports, MAX_VM_USB_PORTS); | |
5602 | - for(i = 0; i < usb_devices_index; i++) { | |
5603 | - if (usb_device_add(usb_devices[i]) < 0) { | |
5604 | - fprintf(stderr, "Warning: could not add USB device %s\n", | |
5605 | - usb_devices[i]); | |
5606 | - } | |
5607 | - } | |
5608 | - } | |
5609 | - | |
5610 | 5630 | register_savevm("timer", 0, 1, timer_save, timer_load, NULL); |
5611 | 5631 | register_savevm("ram", 0, 1, ram_save, ram_load, NULL); |
5612 | 5632 | |
... | ... | @@ -5710,6 +5730,16 @@ int main(int argc, char **argv) |
5710 | 5730 | ds, fd_filename, snapshot, |
5711 | 5731 | kernel_filename, kernel_cmdline, initrd_filename); |
5712 | 5732 | |
5733 | + /* init USB devices */ | |
5734 | + if (usb_enabled) { | |
5735 | + for(i = 0; i < usb_devices_index; i++) { | |
5736 | + if (usb_device_add(usb_devices[i]) < 0) { | |
5737 | + fprintf(stderr, "Warning: could not add USB device %s\n", | |
5738 | + usb_devices[i]); | |
5739 | + } | |
5740 | + } | |
5741 | + } | |
5742 | + | |
5713 | 5743 | gui_timer = qemu_new_timer(rt_clock, gui_update, NULL); |
5714 | 5744 | qemu_mod_timer(gui_timer, qemu_get_clock(rt_clock)); |
5715 | 5745 | ... | ... |
vl.h
... | ... | @@ -1022,10 +1022,10 @@ int cuda_init(SetIRQFunc *set_irq, void *irq_opaque, int irq); |
1022 | 1022 | |
1023 | 1023 | /* usb ports of the VM */ |
1024 | 1024 | |
1025 | -#define MAX_VM_USB_PORTS 8 | |
1025 | +void qemu_register_usb_port(USBPort *port, void *opaque, int index, | |
1026 | + usb_attachfn attach); | |
1026 | 1027 | |
1027 | -extern USBPort *vm_usb_ports[MAX_VM_USB_PORTS]; | |
1028 | -extern USBDevice *vm_usb_hub; | |
1028 | +#define VM_USB_HUB_SIZE 8 | |
1029 | 1029 | |
1030 | 1030 | void do_usb_add(const char *devname); |
1031 | 1031 | void do_usb_del(const char *devname); | ... | ... |