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