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
| @@ -307,7 +307,7 @@ SOUND_HW += fmopl.o adlib.o | @@ -307,7 +307,7 @@ SOUND_HW += fmopl.o adlib.o | ||
| 307 | endif | 307 | endif |
| 308 | 308 | ||
| 309 | # USB layer | 309 | # USB layer |
| 310 | -VL_OBJS+= usb.o usb-hub.o usb-linux.o usb-hid.o | 310 | +VL_OBJS+= usb.o usb-hub.o usb-linux.o usb-hid.o usb-ohci.o |
| 311 | 311 | ||
| 312 | # PCI network cards | 312 | # PCI network cards |
| 313 | VL_OBJS+= ne2000.o rtl8139.o | 313 | VL_OBJS+= ne2000.o rtl8139.o |
hw/pc.c
| @@ -40,7 +40,6 @@ static fdctrl_t *floppy_controller; | @@ -40,7 +40,6 @@ static fdctrl_t *floppy_controller; | ||
| 40 | static RTCState *rtc_state; | 40 | static RTCState *rtc_state; |
| 41 | static PITState *pit; | 41 | static PITState *pit; |
| 42 | static IOAPICState *ioapic; | 42 | static IOAPICState *ioapic; |
| 43 | -static USBPort *usb_root_ports[2]; | ||
| 44 | 43 | ||
| 45 | static void ioport80_write(void *opaque, uint32_t addr, uint32_t data) | 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,8 +832,7 @@ static void pc_init1(int ram_size, int vga_ram_size, int boot_device, | ||
| 833 | cmos_init(ram_size, boot_device, bs_table); | 832 | cmos_init(ram_size, boot_device, bs_table); |
| 834 | 833 | ||
| 835 | if (pci_enabled && usb_enabled) { | 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 | if (pci_enabled && acpi_enabled) { | 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,7 +506,11 @@ static void ppc_chrp_init(int ram_size, int vga_ram_size, int boot_device, | ||
| 506 | 506 | ||
| 507 | arch_name = "MAC99"; | 507 | arch_name = "MAC99"; |
| 508 | } | 508 | } |
| 509 | - | 509 | + |
| 510 | + if (usb_enabled) { | ||
| 511 | + usb_ohci_init(pci_bus, 3, -1); | ||
| 512 | + } | ||
| 513 | + | ||
| 510 | if (graphic_depth != 15 && graphic_depth != 32 && graphic_depth != 8) | 514 | if (graphic_depth != 15 && graphic_depth != 32 && graphic_depth != 8) |
| 511 | graphic_depth = 15; | 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,6 +665,10 @@ static void ppc_prep_init(int ram_size, int vga_ram_size, int boot_device, | ||
| 665 | cpu_register_physical_memory(0xFEFF0000, 0x1000, PPC_io_memory); | 665 | cpu_register_physical_memory(0xFEFF0000, 0x1000, PPC_io_memory); |
| 666 | #endif | 666 | #endif |
| 667 | 667 | ||
| 668 | + if (usb_enabled) { | ||
| 669 | + usb_ohci_init(pci_bus, 3, -1); | ||
| 670 | + } | ||
| 671 | + | ||
| 668 | nvram = m48t59_init(8, 0, 0x0074, NVRAM_SIZE, 59); | 672 | nvram = m48t59_init(8, 0, 0x0074, NVRAM_SIZE, 59); |
| 669 | if (nvram == NULL) | 673 | if (nvram == NULL) |
| 670 | return; | 674 | return; |
hw/usb-hub.c
| @@ -179,6 +179,9 @@ static void usb_hub_attach(USBPort *port1, USBDevice *dev) | @@ -179,6 +179,9 @@ static void usb_hub_attach(USBPort *port1, USBDevice *dev) | ||
| 179 | else | 179 | else |
| 180 | port->wPortStatus &= ~PORT_STAT_LOW_SPEED; | 180 | port->wPortStatus &= ~PORT_STAT_LOW_SPEED; |
| 181 | port->port.dev = dev; | 181 | port->port.dev = dev; |
| 182 | + /* send the attach message */ | ||
| 183 | + dev->handle_packet(dev, | ||
| 184 | + USB_MSG_ATTACH, 0, 0, NULL, 0); | ||
| 182 | } else { | 185 | } else { |
| 183 | dev = port->port.dev; | 186 | dev = port->port.dev; |
| 184 | if (dev) { | 187 | if (dev) { |
| @@ -188,6 +191,9 @@ static void usb_hub_attach(USBPort *port1, USBDevice *dev) | @@ -188,6 +191,9 @@ static void usb_hub_attach(USBPort *port1, USBDevice *dev) | ||
| 188 | port->wPortStatus &= ~PORT_STAT_ENABLE; | 191 | port->wPortStatus &= ~PORT_STAT_ENABLE; |
| 189 | port->wPortChange |= PORT_STAT_C_ENABLE; | 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 | port->port.dev = NULL; | 197 | port->port.dev = NULL; |
| 192 | } | 198 | } |
| 193 | } | 199 | } |
| @@ -517,7 +523,7 @@ static int usb_hub_handle_packet(USBDevice *dev, int pid, | @@ -517,7 +523,7 @@ static int usb_hub_handle_packet(USBDevice *dev, int pid, | ||
| 517 | return usb_generic_handle_packet(dev, pid, devaddr, devep, data, len); | 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 | USBHubState *s; | 528 | USBHubState *s; |
| 523 | USBHubPort *port; | 529 | USBHubPort *port; |
| @@ -539,12 +545,9 @@ USBDevice *usb_hub_init(USBPort **usb_ports, int nb_ports) | @@ -539,12 +545,9 @@ USBDevice *usb_hub_init(USBPort **usb_ports, int nb_ports) | ||
| 539 | s->nb_ports = nb_ports; | 545 | s->nb_ports = nb_ports; |
| 540 | for(i = 0; i < s->nb_ports; i++) { | 546 | for(i = 0; i < s->nb_ports; i++) { |
| 541 | port = &s->ports[i]; | 547 | port = &s->ports[i]; |
| 548 | + qemu_register_usb_port(&port->port, s, i, usb_hub_attach); | ||
| 542 | port->wPortStatus = PORT_STAT_POWER; | 549 | port->wPortStatus = PORT_STAT_POWER; |
| 543 | port->wPortChange = 0; | 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 | return (USBDevice *)s; | 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,11 +638,10 @@ static void uhci_map(PCIDevice *pci_dev, int region_num, | ||
| 638 | register_ioport_read(addr, 32, 1, uhci_ioport_readb, s); | 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 | UHCIState *s; | 643 | UHCIState *s; |
| 644 | uint8_t *pci_conf; | 644 | uint8_t *pci_conf; |
| 645 | - UHCIPort *port; | ||
| 646 | int i; | 645 | int i; |
| 647 | 646 | ||
| 648 | s = (UHCIState *)pci_register_device(bus, | 647 | s = (UHCIState *)pci_register_device(bus, |
| @@ -662,11 +661,7 @@ void usb_uhci_init(PCIBus *bus, USBPort **usb_ports, int devfn) | @@ -662,11 +661,7 @@ void usb_uhci_init(PCIBus *bus, USBPort **usb_ports, int devfn) | ||
| 662 | pci_conf[0x60] = 0x10; // release number | 661 | pci_conf[0x60] = 0x10; // release number |
| 663 | 662 | ||
| 664 | for(i = 0; i < NB_PORTS; i++) { | 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 | s->frame_timer = qemu_new_timer(vm_clock, uhci_frame_timer, s); | 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,12 +137,15 @@ struct USBDevice { | ||
| 137 | int setup_index; | 137 | int setup_index; |
| 138 | }; | 138 | }; |
| 139 | 139 | ||
| 140 | +typedef void (*usb_attachfn)(USBPort *port, USBDevice *dev); | ||
| 141 | + | ||
| 140 | /* USB port on which a device can be connected */ | 142 | /* USB port on which a device can be connected */ |
| 141 | struct USBPort { | 143 | struct USBPort { |
| 142 | USBDevice *dev; | 144 | USBDevice *dev; |
| 143 | - void (*attach)(USBPort *port, USBDevice *dev); | 145 | + usb_attachfn attach; |
| 144 | void *opaque; | 146 | void *opaque; |
| 145 | int index; /* internal port index, may be used with the opaque */ | 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 | void usb_attach(USBPort *port, USBDevice *dev); | 151 | void usb_attach(USBPort *port, USBDevice *dev); |
| @@ -152,10 +155,13 @@ int usb_generic_handle_packet(USBDevice *s, int pid, | @@ -152,10 +155,13 @@ int usb_generic_handle_packet(USBDevice *s, int pid, | ||
| 152 | int set_usb_string(uint8_t *buf, const char *str); | 155 | int set_usb_string(uint8_t *buf, const char *str); |
| 153 | 156 | ||
| 154 | /* usb hub */ | 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 | /* usb-uhci.c */ | 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 | /* usb-linux.c */ | 166 | /* usb-linux.c */ |
| 161 | USBDevice *usb_host_device_open(const char *devname); | 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,6 +374,9 @@ static void versatile_init(int ram_size, int vga_ram_size, int boot_device, | ||
| 374 | pci_nic_init(pci_bus, nd); | 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 | pl011_init(0x101f1000, pic, 12, serial_hds[0]); | 381 | pl011_init(0x101f1000, pic, 12, serial_hds[0]); |
| 379 | pl011_init(0x101f2000, pic, 13, serial_hds[1]); | 382 | pl011_init(0x101f2000, pic, 13, serial_hds[1]); |
vl.c
| @@ -106,6 +106,9 @@ | @@ -106,6 +106,9 @@ | ||
| 106 | /* in ms */ | 106 | /* in ms */ |
| 107 | #define GUI_REFRESH_INTERVAL 30 | 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 | /* XXX: use a two level table to limit memory usage */ | 112 | /* XXX: use a two level table to limit memory usage */ |
| 110 | #define MAX_IOPORTS 65536 | 113 | #define MAX_IOPORTS 65536 |
| 111 | 114 | ||
| @@ -145,8 +148,6 @@ CharDriverState *parallel_hds[MAX_PARALLEL_PORTS]; | @@ -145,8 +148,6 @@ CharDriverState *parallel_hds[MAX_PARALLEL_PORTS]; | ||
| 145 | int win2k_install_hack = 0; | 148 | int win2k_install_hack = 0; |
| 146 | #endif | 149 | #endif |
| 147 | int usb_enabled = 0; | 150 | int usb_enabled = 0; |
| 148 | -USBPort *vm_usb_ports[MAX_VM_USB_PORTS]; | ||
| 149 | -USBDevice *vm_usb_hub; | ||
| 150 | static VLANState *first_vlan; | 151 | static VLANState *first_vlan; |
| 151 | int smp_cpus = 1; | 152 | int smp_cpus = 1; |
| 152 | int vnc_display = -1; | 153 | int vnc_display = -1; |
| @@ -3249,47 +3250,71 @@ void do_info_network(void) | @@ -3249,47 +3250,71 @@ void do_info_network(void) | ||
| 3249 | /***********************************************************/ | 3250 | /***********************************************************/ |
| 3250 | /* USB devices */ | 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 | static int usb_device_add(const char *devname) | 3267 | static int usb_device_add(const char *devname) |
| 3253 | { | 3268 | { |
| 3254 | const char *p; | 3269 | const char *p; |
| 3255 | USBDevice *dev; | 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 | return -1; | 3274 | return -1; |
| 3266 | 3275 | ||
| 3267 | if (strstart(devname, "host:", &p)) { | 3276 | if (strstart(devname, "host:", &p)) { |
| 3268 | dev = usb_host_device_open(p); | 3277 | dev = usb_host_device_open(p); |
| 3269 | - if (!dev) | ||
| 3270 | - return -1; | ||
| 3271 | } else if (!strcmp(devname, "mouse")) { | 3278 | } else if (!strcmp(devname, "mouse")) { |
| 3272 | dev = usb_mouse_init(); | 3279 | dev = usb_mouse_init(); |
| 3273 | - if (!dev) | ||
| 3274 | - return -1; | ||
| 3275 | } else if (!strcmp(devname, "tablet")) { | 3280 | } else if (!strcmp(devname, "tablet")) { |
| 3276 | dev = usb_tablet_init(); | 3281 | dev = usb_tablet_init(); |
| 3277 | - if (!dev) | ||
| 3278 | - return -1; | ||
| 3279 | } else { | 3282 | } else { |
| 3280 | return -1; | 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 | return 0; | 3307 | return 0; |
| 3284 | } | 3308 | } |
| 3285 | 3309 | ||
| 3286 | static int usb_device_del(const char *devname) | 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 | const char *p; | 3315 | const char *p; |
| 3291 | 3316 | ||
| 3292 | - if (!vm_usb_hub) | 3317 | + if (!used_usb_ports) |
| 3293 | return -1; | 3318 | return -1; |
| 3294 | 3319 | ||
| 3295 | p = strchr(devname, '.'); | 3320 | p = strchr(devname, '.'); |
| @@ -3299,14 +3324,21 @@ static int usb_device_del(const char *devname) | @@ -3299,14 +3324,21 @@ static int usb_device_del(const char *devname) | ||
| 3299 | addr = strtoul(p + 1, NULL, 0); | 3324 | addr = strtoul(p + 1, NULL, 0); |
| 3300 | if (bus_num != 0) | 3325 | if (bus_num != 0) |
| 3301 | return -1; | 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 | return -1; | 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 | return 0; | 3342 | return 0; |
| 3311 | } | 3343 | } |
| 3312 | 3344 | ||
| @@ -3329,35 +3361,34 @@ void do_usb_del(const char *devname) | @@ -3329,35 +3361,34 @@ void do_usb_del(const char *devname) | ||
| 3329 | void usb_info(void) | 3361 | void usb_info(void) |
| 3330 | { | 3362 | { |
| 3331 | USBDevice *dev; | 3363 | USBDevice *dev; |
| 3332 | - int i; | 3364 | + USBPort *port; |
| 3333 | const char *speed_str; | 3365 | const char *speed_str; |
| 3334 | 3366 | ||
| 3335 | - if (!vm_usb_hub) { | 3367 | + if (!usb_enabled) { |
| 3336 | term_printf("USB support not enabled\n"); | 3368 | term_printf("USB support not enabled\n"); |
| 3337 | return; | 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,7 +5097,7 @@ int main(int argc, char **argv) | ||
| 5066 | int parallel_device_index; | 5097 | int parallel_device_index; |
| 5067 | const char *loadvm = NULL; | 5098 | const char *loadvm = NULL; |
| 5068 | QEMUMachine *machine; | 5099 | QEMUMachine *machine; |
| 5069 | - char usb_devices[MAX_VM_USB_PORTS][128]; | 5100 | + char usb_devices[MAX_USB_CMDLINE][128]; |
| 5070 | int usb_devices_index; | 5101 | int usb_devices_index; |
| 5071 | 5102 | ||
| 5072 | LIST_INIT (&vm_change_state_head); | 5103 | LIST_INIT (&vm_change_state_head); |
| @@ -5425,7 +5456,7 @@ int main(int argc, char **argv) | @@ -5425,7 +5456,7 @@ int main(int argc, char **argv) | ||
| 5425 | break; | 5456 | break; |
| 5426 | case QEMU_OPTION_usbdevice: | 5457 | case QEMU_OPTION_usbdevice: |
| 5427 | usb_enabled = 1; | 5458 | usb_enabled = 1; |
| 5428 | - if (usb_devices_index >= MAX_VM_USB_PORTS) { | 5459 | + if (usb_devices_index >= MAX_USB_CMDLINE) { |
| 5429 | fprintf(stderr, "Too many USB devices\n"); | 5460 | fprintf(stderr, "Too many USB devices\n"); |
| 5430 | exit(1); | 5461 | exit(1); |
| 5431 | } | 5462 | } |
| @@ -5596,17 +5627,6 @@ int main(int argc, char **argv) | @@ -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 | register_savevm("timer", 0, 1, timer_save, timer_load, NULL); | 5630 | register_savevm("timer", 0, 1, timer_save, timer_load, NULL); |
| 5611 | register_savevm("ram", 0, 1, ram_save, ram_load, NULL); | 5631 | register_savevm("ram", 0, 1, ram_save, ram_load, NULL); |
| 5612 | 5632 | ||
| @@ -5710,6 +5730,16 @@ int main(int argc, char **argv) | @@ -5710,6 +5730,16 @@ int main(int argc, char **argv) | ||
| 5710 | ds, fd_filename, snapshot, | 5730 | ds, fd_filename, snapshot, |
| 5711 | kernel_filename, kernel_cmdline, initrd_filename); | 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 | gui_timer = qemu_new_timer(rt_clock, gui_update, NULL); | 5743 | gui_timer = qemu_new_timer(rt_clock, gui_update, NULL); |
| 5714 | qemu_mod_timer(gui_timer, qemu_get_clock(rt_clock)); | 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,10 +1022,10 @@ int cuda_init(SetIRQFunc *set_irq, void *irq_opaque, int irq); | ||
| 1022 | 1022 | ||
| 1023 | /* usb ports of the VM */ | 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 | void do_usb_add(const char *devname); | 1030 | void do_usb_add(const char *devname); |
| 1031 | void do_usb_del(const char *devname); | 1031 | void do_usb_del(const char *devname); |