diff -uNr qemu-orig/qemu-0.7.0/Makefile.target qemu-0.7.0/Makefile.target --- qemu-orig/qemu-0.7.0/Makefile.target 2005-06-09 00:48:18.000000000 -0500 +++ qemu-0.7.0/Makefile.target 2005-06-09 00:35:58.000000000 -0500 @@ -313,7 +313,7 @@ endif # must use static linking to avoid leaving stuff in virtual address space -VL_OBJS=vl.o osdep.o block.o readline.o monitor.o pci.o console.o +VL_OBJS=vl.o osdep.o block.o readline.o monitor.o pci.o console.o usb-ohci.o VL_OBJS+=block-cow.o block-qcow.o aes.o block-vmdk.o block-cloop.o block-dmg.o block-bochs.o block-vpc.o SOUND_HW = sb16.o diff -uNr qemu-orig/qemu-0.7.0/hw/pc.c qemu-0.7.0/hw/pc.c --- qemu-orig/qemu-0.7.0/hw/pc.c 2005-04-27 15:52:05.000000000 -0500 +++ qemu-0.7.0/hw/pc.c 2005-06-09 00:37:42.000000000 -0500 @@ -500,6 +500,9 @@ if (pci_enabled) { pci_bus = i440fx_init(); piix3_init(pci_bus); + + /* Shut up! */ + usb_ohci_init("Apple KeyLargo/Intrepid", 0x106b, 0x003f, pci_bus, -1); } else { pci_bus = NULL; } diff -uNr qemu-orig/qemu-0.7.0/hw/usb-ohci.c qemu-0.7.0/hw/usb-ohci.c --- qemu-orig/qemu-0.7.0/hw/usb-ohci.c 1969-12-31 18:00:00.000000000 -0600 +++ qemu-0.7.0/hw/usb-ohci.c 2005-06-09 00:45:29.000000000 -0500 @@ -0,0 +1,1163 @@ +/* + * QEMU USB OHCI Interface v0.2 + * Copyright (c) 2004 Gianni Tedesco + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * + * TODO: + * o Service general transfer descriptors + * o Service isochronous/interrupt lists + * o Allocate bandwidth in frames properly + * o Disable timers when nothing needs to be done, or remove timer usage + * all together. + * o Handle unrecoverable errors properly + * o USB device API and generic hub code + * o Mouse/Keyboard/Storage devices + * o BIOS work to boot from USB storage + * o libusb based host proxy (with logging) +*/ + +static const char copyright[] = + "usb-ohci.c: v0.2 Copyright (c) Gianni Tedesco 2004"; + +#include "vl.h" +#include "cpu.h" + +#if 0 +#define dprintf(...) +#else +#define dprintf printf +#endif + +/* This causes frames to occur 1000x slower */ +#define OHCI_TIME_WARP 1 + +/* Number of Downstream Ports on the root hub, if you change this + * you need to add more status register words to the 'opreg' array + */ +#define OHCI_NDP 8 + +static int64_t usb_frame_time; +static int64_t usb_bit_time; + +struct ohci { + struct PCIDevice pci_dev; + target_phys_addr_t mem_base; + int mem; + + QEMUTimer *eof_timer; + int64_t sof_time; + + /* OHCI state */ + /* Control partition */ + uint32_t ctl, status; + uint32_t intr_status; + uint32_t intr; + + /* memory pointer partition */ + uint32_t hcca; + uint32_t ctrl_head, ctrl_cur; + uint32_t bulk_head, bulk_cur; + uint32_t per_cur; + uint32_t done; + + /* Frame counter partition */ + uint32_t fsmps:15; + uint32_t fit:1; + uint32_t fi:14; + uint32_t frt:1; + uint16_t frame_number; + uint16_t padding; + uint32_t pstart; + + /* Root Hub partition */ + uint32_t rhdesc_a, rhdesc_b; + uint32_t rhstatus; + uint32_t rhport[OHCI_NDP]; +}; + +/* Host Controller Communications Area */ +struct ohci_hcca { + uint32_t intr[32]; + uint16_t frame, pad; + uint32_t done; +}; + +/* endpoint descriptor */ +struct ohci_ed { + uint32_t fa:7, en:4, d:2, s:1, k:1, f:1, mps:11, res0:5; + uint32_t res1:4, tailp:28; + uint32_t h:1, c:1, res2:2, headp:28; + uint32_t res3:4, next:28; +}; + +/* General transfer descriptor */ +struct ohci_td { + uint32_t res0:17, r:1, dp:2, di:3, t:2, ec:2, cc:4; + uint32_t cbp; + uint32_t res1:4, next:28; + uint32_t be; +}; + +/* Isochronous transfer descriptor */ +struct ohci_isochronous_td { + uint32_t cc:4, res0:1, fc:3, di:3, res1:5, sf:16; + uint32_t bp0:20, res2:12; + uint32_t next; + uint32_t be; + uint16_t ofs[8]; /* 1 0 3 2 5 5 7 6 */ +}; + +extern struct ohci_opreg opreg[]; +struct ohci_opreg { + const char *name; + uint32_t (*read)(struct ohci *ohci, uint32_t ofs); + void (*write)(struct ohci *ohci, uint32_t ofs, uint32_t val); +}; + +/* Global USB stuff - move to a seperate file eventually */ +#define USB_HZ 12000000 +#define USB_REQ_GET_STATUS 0x00 +#define USB_REQ_CLEAR_FEATURE 0x01 +#define USB_REQ_SET_FEATURE 0x03 +#define USB_REQ_SET_ADDRESS 0x05 +#define USB_REQ_GET_DESCRIPTOR 0x06 +#define USB_REQ_SET_DESCRIPTOR 0x07 +#define USB_REQ_GET_CONFIGURATION 0x08 +#define USB_REQ_SET_CONFIGURATION 0x09 +#define USB_REQ_GET_INTERFACE 0x0A +#define USB_REQ_SET_INTERFACE 0x0B +#define USB_REQ_SYNCH_FRAME 0x0C + +/* OHCI Local stuff */ +#define OHCI_CTL_CBSR ((1<<0)|(1<<1)) +#define OHCI_CTL_PLE (1<<2) +#define OHCI_CTL_IE (1<<3) +#define OHCI_CTL_CLE (1<<4) +#define OHCI_CTL_BLE (1<<5) +#define OHCI_CTL_HCFS ((1<<6)|(1<<7)) +#define OHCI_USB_RESET 0x00 +#define OHCI_USB_RESUME 0x40 +#define OHCI_USB_OPERATIONAL 0x80 +#define OHCI_USB_SUSPEND 0xc0 +#define OHCI_CTL_IR (1<<8) +#define OHCI_CTL_RWC (1<<9) +#define OHCI_CTL_RWE (1<<10) + +#define OHCI_STATUS_HCR (1<<0) +#define OHCI_STATUS_CLF (1<<1) +#define OHCI_STATUS_BLF (1<<2) +#define OHCI_STATUS_OCR (1<<3) +#define OHCI_STATUS_SOC ((1<<6)|(1<<7)) + +#define OHCI_INTR_SO (1<<0) /* Scheduling overrun */ +#define OHCI_INTR_WD (1<<1) /* HcDoneHead writeback */ +#define OHCI_INTR_SF (1<<2) /* Start of frame */ +#define OHCI_INTR_RD (1<<3) /* Resume detect */ +#define OHCI_INTR_UE (1<<4) /* Unrecoverable error */ +#define OHCI_INTR_FNO (1<<5) /* Frame number overflow */ +#define OHCI_INTR_RHSC (1<<6) /* Root hub status change */ +#define OHCI_INTR_OC (1<<30) /* Ownership change */ +#define OHCI_INTR_MIE (1<<31) /* Master Interrupt Enable */ + +#define OHCI_HCCA_SIZE 0x100 +#define OHCI_HCCA_MASK 0xffffff00 + +#define OHCI_FMI_FI 0x00003fff +#define OHCI_FMI_FSMPS 0xffff0000 +#define OHCI_FMI_FIT 0x80000000 + +#define OHCI_FR_RT (1<<31) + +#define OHCI_LS_THRESH 0x628 + +#define OHCI_RHA_NPS (1<<9) + +#define OHCI_RHS_LPS (1<<0) +#define OHCI_RHS_OCI (1<<1) +#define OHCI_RHS_DRWE (1<<15) +#define OHCI_RHS_LPSC (1<<16) +#define OHCI_RHS_OCIC (1<<17) +#define OHCI_RHS_CRWE (1<<31) + +#define OHCI_PORT_CCS (1<<0) +#define OHCI_PORT_PES (1<<1) +#define OHCI_PORT_PSS (1<<2) +#define OHCI_PORT_POCI (1<<3) +#define OHCI_PORT_PRS (1<<4) +#define OHCI_PORT_PPS (1<<8) +#define OHCI_PORT_LSDA (1<<9) +#define OHCI_PORT_CSC (1<<16) +#define OHCI_PORT_PESC (1<<17) +#define OHCI_PORT_PSSC (1<<18) +#define OHCI_PORT_OCIC (1<<19) +#define OHCI_PORT_PRSC (1<<20) +#define OHCI_PORT_WTC (OHCI_PORT_CSC|OHCI_PORT_PESC|OHCI_PORT_PSSC|\ + OHCI_PORT_OCIC|OHCI_PORT_PRSC) + +#define OHCI_TD_DIR_SETUP 0x0 +#define OHCI_TD_DIR_OUT 0x1 +#define OHCI_TD_DIR_IN 0x2 +#define OHCI_TD_DIR_RESERVED 0x3 + +#define OHCI_CC_NO_ERROR 0x00 + +/* Reset the controller */ +static void ohci_reset(struct ohci *ohci) +{ + int i; + + ohci->ctl = 0; + ohci->status = 0; + ohci->intr_status = 0; + ohci->intr = OHCI_INTR_MIE; + + ohci->hcca = 0; + ohci->ctrl_head = ohci->ctrl_cur = 0; + ohci->bulk_head = ohci->bulk_cur = 0; + ohci->per_cur = 0; + ohci->done = 0; + + /* FSMPS is marked TBD in OCHI 1.0, what gives ffs? + * I took the value linux sets ... + */ + ohci->fsmps = 0x2778; + ohci->fi = 0x2edf; + ohci->fit = 0; + ohci->frt = 0; + ohci->frame_number = 0; + ohci->pstart = 0; + + ohci->rhdesc_a = OHCI_RHA_NPS | OHCI_NDP; + ohci->rhdesc_b = 0x0; /* Impl. specific */ + ohci->rhstatus = 0; + + for(i=0; i < OHCI_NDP; i++) + ohci->rhport[i] = 0; +/* ohci->rhport[0] = OHCI_PORT_CCS|OHCI_PORT_PES|OHCI_PORT_PPS|OHCI_PORT_CSC; */ + + dprintf("usb-ohci: Reset %s\n", ohci->pci_dev.name); +} + +/* Update IRQ levels */ +static inline void ohci_intr_update(struct ohci *ohci) +{ + int level = 0; + + if ( (ohci->intr & OHCI_INTR_MIE) && + (ohci->intr_status & ohci->intr) ) + level = 1; + + pci_set_irq(&ohci->pci_dev, 0, level); + if ( level ) + dprintf("usb-ohci: Fired off interrupt 0x%.8x\n", + ohci->intr_status & ohci->intr); +} + +/* Set an interrupt */ +static inline void ohci_set_interrupt(struct ohci *ohci, uint32_t intr) +{ + ohci->intr_status |= intr; + ohci_intr_update(ohci); +} + +/* Get an array of dwords from main memory */ +static inline int get_dwords(uint32_t addr, uint32_t *buf, int num) +{ + int i; + + for(i=0; i < num; i++, buf++, addr += sizeof(*buf)) { + cpu_physical_memory_rw(addr, (uint8_t *)buf, sizeof(*buf), 0); + *buf = tswap32(*buf); + } + + return 1; +} + +/* Put an array of dwords in to main memory */ +static inline int put_dwords(uint32_t addr, uint32_t *buf, int num) +{ + int i; + + for(i=0; i < num; i++, buf++, addr += sizeof(*buf)) { + uint32_t tmp = tswap32(*buf); + cpu_physical_memory_rw(addr, (uint8_t *)&tmp, sizeof(tmp), 1); + } + + return 1; +} + +static inline int ohci_read_ed(uint32_t addr, struct ohci_ed *ed) +{ + return get_dwords(addr, (uint32_t *)ed, sizeof(*ed) >> 2); +} + +static inline int ohci_read_td(uint32_t addr, struct ohci_td *td) +{ + return get_dwords(addr, (uint32_t *)td, sizeof(*td) >> 2); +} + +static inline int ohci_put_ed(uint32_t addr, struct ohci_ed *ed) +{ + return put_dwords(addr, (uint32_t *)ed, sizeof(*ed) >> 2); +} + +static inline int ohci_put_td(uint32_t addr, struct ohci_td *td) +{ + return put_dwords(addr, (uint32_t *)td, sizeof(*td) >> 2); +} + +/* Service a transport descriptor */ +static void ohci_service_td(struct ohci *ohci, + struct ohci_ed *ed, + struct ohci_td *td, + uint32_t tdp) +{ + int dir; + size_t len = 0; + uint8_t *buf = NULL; + char *str = NULL; + + switch ( ed->d ) { + case OHCI_TD_DIR_OUT: + case OHCI_TD_DIR_IN: + dir = ed->d; + break; + default: + dir = td->dp; + break; + } + + if ( dir == OHCI_TD_DIR_RESERVED ) { + printf("RESERVED DIRECTION\n"); + return; + } + + if ( td->cbp && td->be ) { + len = (td->be - td->cbp) + 1; + if ( len ) { + buf = malloc(len); + if ( buf ) + cpu_physical_memory_rw(td->cbp, buf, len, 0); + } + + /* XXX: Check max packet size and warn if OS is buggy */ + } + + /* TODO: Send/Recv the packet to/from device */ + switch ( dir ) { + case OHCI_TD_DIR_IN: + str = "in"; + break; + case OHCI_TD_DIR_OUT: + str = "out"; + break; + case OHCI_TD_DIR_SETUP: + str = "setup"; + break; + } + + dprintf(" TD @ 0x%.8x %u bytes %s cbp=0x%.8x be=0x%.8x\n", + tdp, len, str, td->cbp, td->be); + + if ( len ) { + int i; + printf(" data:"); + for(i = 0; i < len; i++) + printf(" %.2x", buf[i]); + printf("\n"); + } + + /* Writeback */ + td->cbp = 0; + td->t = 0x2 | ~(td->t & 0x1); + td->cc = OHCI_CC_NO_ERROR; + + ed->h = 0; + ed->c = td->t & 0x1; + + /* Retire this TD */ + td->next = ohci->done >> 4; + ohci->done = ed->headp << 4; + ohci_put_td(ed->headp << 4, td); + + if ( buf ) + free(buf); +} + +/* Service a control/bulk list */ +static void ohci_service_cb_list(struct ohci *ohci, + uint32_t head, + uint32_t cur, + uint32_t lf) +{ + struct ohci_ed ed; + struct ohci_td td; + + /* Clear list fill flag */ + ohci->status &= ~lf; + + if ( head == 0 ) + return; + +next_ed: + if ( cur == 0 ) { + cur = head; + }else if ( cur == head ) { + return; + } + + if ( !ohci_read_ed(cur, &ed) ) { + printf("EEK\n"); + return; + } + + dprintf("ED @ 0x%.8x fa=%u en=%u d=%u s=%u k=%u f=%u mps=%u " + "tailp=0x%.8x h=%u c=%u head=0x%.8x next=0x%.8x\n", cur, + ed.fa, ed.en, ed.d, ed.s, ed.k, ed.f, ed.mps, + ed.tailp, ed.h, ed.c, ed.headp, ed.next); + + while(ed.headp != ed.tailp) { + uint32_t nxt; + + /* Update filled bit */ + ohci->status |= ~lf; + + if ( !ohci_read_td(ed.headp << 4, &td) ) { + printf("EEK2\n"); + goto next_ed; + } + + nxt = td.next; + ohci_service_td(ohci, &ed, &td, ed.headp << 4); + ed.headp = nxt; + } + + ohci_put_ed(cur, &ed); + goto next_ed; +} + +/* Generate a SOF event, and set a timer for EOF */ +static void ohci_sof(struct ohci *ohci) +{ + ohci->sof_time = qemu_get_clock(vm_clock); + qemu_mod_timer(ohci->eof_timer, ohci->sof_time + usb_frame_time); + ohci_set_interrupt(ohci, OHCI_INTR_SF); +} + +/* Do frame processing on frame boundary */ +static void ohci_frame_boundary(void *opaque) +{ + struct ohci *ohci = opaque; + struct ohci_hcca hcca; + + cpu_physical_memory_rw(ohci->hcca, (uint8_t *)&hcca, sizeof(hcca), 0); + + /* Process all the lists at the end of the frame */ + if ( ohci->ctl & OHCI_CTL_PLE ) { + dprintf("usb-ohci: %s: servicing periodic list\n", + ohci->pci_dev.name); + } + if ( ohci->ctl & OHCI_CTL_CLE ) { + dprintf("usb-ohci: %s: servicing control list\n", + ohci->pci_dev.name); + ohci_service_cb_list(ohci, + ohci->ctrl_head, + ohci->ctrl_cur, + OHCI_STATUS_CLF); + } + + if ( ohci->ctl & OHCI_CTL_BLE ) { + dprintf("usb-ohci: %s: servicing bulk list\n", + ohci->pci_dev.name); + ohci_service_cb_list(ohci, + ohci->bulk_head, + ohci->bulk_cur, + OHCI_STATUS_BLF); + } + + /* Frame boundary, so do EOF stuf here */ + ohci->frt = ohci->fit; + + /* XXX: endianness */ + hcca.frame = tswap32(++ohci->frame_number); + + if ( !(ohci->intr_status & OHCI_INTR_WD) ) { + hcca.done = ohci->done; + ohci->done = 0; + ohci_set_interrupt(ohci, OHCI_INTR_WD); + } + + /* Do SOF stuff here */ + ohci_sof(ohci); + + /* Writeback HCCA */ + cpu_physical_memory_rw(ohci->hcca, (uint8_t *)&hcca, sizeof(hcca), 1); +} + +/* Start sending SOF tokens across the USB bus, lists are processed in + * next frame + */ +static int ohci_bus_start(struct ohci *ohci) +{ + ohci->eof_timer = qemu_new_timer(vm_clock, + ohci_frame_boundary, + ohci); + + if ( ohci->eof_timer == NULL ) { + fprintf(stderr, "usb-ohci: %s: qemu_new_timer failed\n", + ohci->pci_dev.name); + /* TODO: Signal unrecoverable error */ + return 0; + } + + dprintf("usb-ohci: %s: USB Operational\n", ohci->pci_dev.name); + + ohci_sof(ohci); + + return 1; +} + +/* Stop sending SOF tokens on the bus */ +static void ohci_bus_stop(struct ohci *ohci) +{ + if ( ohci->eof_timer ) + qemu_del_timer(ohci->eof_timer); +} + +/* Sets a flag in a port status register but only set it if the port is + * connected, if not set ConnectStatusChange flag. If flag is enabled + * return 1. + */ +static int ohci_port_set_if_connected(struct ohci *ohci, int i, uint32_t val) +{ + int ret = 1; + + /* writing a 0 has no effect */ + if ( val == 0 ) + return 0; + + /* If CurrentConnectStatus is cleared we set + * ConnectStatusChange + */ + if ( !(ohci->rhport[i] & OHCI_PORT_CCS) ) { + ohci->rhport[i] |= OHCI_PORT_CSC; + if ( ohci->rhstatus & OHCI_RHS_DRWE ) { + /* TODO: CSC is a wakeup event */ + } + return 0; + } + + if ( ohci->rhport[i] & val ) + ret = 0; + + /* set the bit */ + ohci->rhport[i] |= val; + + return ret; +} + +/* Set the frame interval - frame interval toggle is manipulated by the hcd only */ +static void ohci_set_frame_interval(struct ohci *ohci, uint16_t val) +{ + val &= OHCI_FMI_FI; + + if ( val != ohci->fi ) { + dprintf("usb-ohci: %s: FrameInterval = 0x%x (%u)\n", + ohci->pci_dev.name, ohci->fi, ohci->fi); + } + + ohci->fi = val; +} + +static void ohci_port_power(struct ohci *ohci, int i, int p) +{ + if ( p ) { + ohci->rhport[i] |= OHCI_PORT_PPS; + }else{ + ohci->rhport[i] &= ~(OHCI_PORT_PPS| + OHCI_PORT_CCS| + OHCI_PORT_PSS| + OHCI_PORT_PRS); + } +} + +/* Revision register */ +static uint32_t rev_r(struct ohci *ohci, uint32_t reg) +{ + return 0x10; +} + +/* HcControlRegister */ +static uint32_t ctl_r(struct ohci *ohci, uint32_t reg) +{ + return ohci->ctl; +} + +static void ctl_w(struct ohci *ohci, uint32_t reg, uint32_t val) +{ + uint32_t old_state; + uint32_t new_state; + + old_state = ohci->ctl & OHCI_CTL_HCFS; + ohci->ctl = val; + new_state = ohci->ctl & OHCI_CTL_HCFS; + + /* no state change */ + if ( old_state == new_state ) + return; + + switch ( new_state ) { + case OHCI_USB_OPERATIONAL: + ohci_bus_start(ohci); + break; + case OHCI_USB_SUSPEND: + ohci_bus_stop(ohci); + dprintf("usb-ohci: %s: USB Suspended\n", ohci->pci_dev.name); + break; + case OHCI_USB_RESUME: + dprintf("usb-ohci: %s: USB Resume\n", ohci->pci_dev.name); + break; + case OHCI_USB_RESET: + dprintf("usb-ohci: %s: USB Reset\n", ohci->pci_dev.name); + break; + } +} + +/* HcCommandStatus */ +static uint32_t status_r(struct ohci *ohci, uint32_t reg) +{ + return ohci->status; +} + +static void status_w(struct ohci *ohci, uint32_t reg, uint32_t val) +{ + /* SOC is read-only */ + val = (val & ~OHCI_STATUS_SOC); + + /* "bits written as '0' remain unchanged in the register */ + ohci->status |= val; + + if ( ohci->status & OHCI_STATUS_HCR ) + ohci_reset(ohci); +} + +/* HcInterruptStatus */ +static uint32_t is_r(struct ohci *ohci, uint32_t reg) +{ + return ohci->intr_status; +} + +static void is_w(struct ohci *ohci, uint32_t reg, uint32_t val) +{ + /* "The Host Controller Driver may clear specific bits in this + * register by writing '1' to bit positions to be cleared + */ + ohci->intr_status &= ~val; + ohci_intr_update(ohci); +} + +/* HcInterruptEnable */ +static uint32_t ie_r(struct ohci *ohci, uint32_t reg) +{ + return ohci->intr; +} + +static void ie_w(struct ohci *ohci, uint32_t reg, uint32_t val) +{ + ohci->intr |= val; + ohci_intr_update(ohci); +} + +/* HcInterruptDisable */ +static uint32_t id_r(struct ohci *ohci, uint32_t reg) +{ + return ~ohci->intr; +} + +static void id_w(struct ohci *ohci, uint32_t reg, uint32_t val) +{ + ohci->intr &= ~val; + ohci_intr_update(ohci); +} + +/* Host Controller Communications Area */ +static uint32_t hcca_r(struct ohci *ohci, uint32_t reg) +{ + /* We return minimum allowed alignment, because we don't care */ + if ( ohci->hcca & 0x1 ) { + ohci->hcca &= ~1; + return ~OHCI_HCCA_MASK; + } + + return ohci->hcca; +} + +static void hcca_w(struct ohci *ohci, uint32_t reg, uint32_t val) +{ + /* HCCA alignment querying mechanism, low order bits + * are safe for us to use privately as they can't ever + * be set... + */ + if ( val == 0xffffffff ) { + ohci->hcca |= 0x1; + return; + } + + ohci->hcca = val & OHCI_HCCA_MASK; +} + +/* Bulk and control head ED's */ +static uint32_t head_r(struct ohci *ohci, uint32_t reg) +{ + uint32_t *head; + + switch ( reg ) { + case 7: + head = &ohci->per_cur; + break; + case 8: + head = &ohci->ctrl_head; + break; + case 9: + head = &ohci->ctrl_cur; + break; + case 10: + head = &ohci->bulk_head; + break; + case 11: + head = &ohci->bulk_cur; + break; + default: + abort(); + } + + return *head; +} + +static void head_w(struct ohci *ohci, uint32_t reg, uint32_t val) +{ + uint32_t *head; + + switch ( reg ) { + case 7: + head = &ohci->per_cur; + break; + case 8: + head = &ohci->ctrl_head; + break; + case 9: + head = &ohci->ctrl_cur; + break; + case 10: + head = &ohci->bulk_head; + break; + case 11: + head = &ohci->bulk_cur; + break; + default: + abort(); + } + + if ( val & 0x7 ) + fprintf(stderr, "usb-ohci: Mis-aligned %s pointer\n", + opreg[reg].name); + + *head = val & ~0x7; +} + +/* DoneHead */ +static uint32_t done_r(struct ohci *ohci, uint32_t reg) +{ + return ohci->done; +} + +/* Frame Interval */ +static uint32_t fmi_r(struct ohci *ohci, uint32_t reg) +{ + return (ohci->fit << 31) | (ohci->fsmps << 16) | (ohci->fi); +} + +static void fmi_w(struct ohci *ohci, uint32_t reg, uint32_t val) +{ + ohci->fsmps = (val & OHCI_FMI_FSMPS) >> 16; + ohci->fit = (val & OHCI_FMI_FIT) >> 31; + ohci_set_frame_interval(ohci, val); +} + +/* Periodic Start */ +static uint32_t pstart_r(struct ohci *ohci, uint32_t reg) +{ + return ohci->pstart; +} + +static void pstart_w(struct ohci *ohci, uint32_t reg, uint32_t val) +{ + ohci->pstart = val; +} + +/* LS threshold */ +static uint32_t lst_r(struct ohci *ohci, uint32_t reg) +{ + return OHCI_LS_THRESH; +} + +/* Frame number */ +static uint32_t fn_r(struct ohci *ohci, uint32_t reg) +{ + return ohci->frame_number; +} + +/* Frame remaining */ +static uint32_t fr_r(struct ohci *ohci, uint32_t reg) +{ + uint16_t fr; + int64_t tks; + + if ( (ohci->ctl & OHCI_CTL_HCFS) != OHCI_USB_OPERATIONAL ) + return (ohci->frt << 31); + + /* Being in USB operational state guarnatees sof_time was + * set already. + */ + tks = qemu_get_clock(vm_clock) - ohci->sof_time; + + /* avoid muldiv if possible */ + if ( tks >= usb_frame_time ) + return (ohci->frt << 31); + + tks = muldiv64(1, tks, usb_bit_time); + fr = (uint16_t)(ohci->fi - tks); + + return (ohci->frt << 31) | fr; +} + +static void lst_w(struct ohci *ohci, uint32_t reg, uint32_t val) +{ + /* "Neither the Host Controller nor the Host Controller Driver + * are allowed to change this value." -- However, they are allowed + * to write to it (WTF?!), so supress a warning if they write the + * correct value. + */ + if ( val != OHCI_LS_THRESH ) { + fprintf(stderr, "usb-ohci: HCD tried to write bad LS " + "threshold: 0x%x\n", val); + } +} + +/* Root Hub Descriptor A */ +static uint32_t rhda_r(struct ohci *ohci, uint32_t reg) +{ + return ohci->rhdesc_a; +} + +static void rhda_w(struct ohci *ohci, uint32_t reg, uint32_t val) +{ + /* All the optional features are not supported */ + if ( ohci->rhdesc_a != val ) + fprintf(stderr, "usb-ohci: %s: invalid write to " + "root decriptor A: " + "0x%.8x -> 0x%.8x\n", + ohci->pci_dev.name, + ohci->rhdesc_a, val); +} + +/* Root Hub Descriptor B */ +static uint32_t rhdb_r(struct ohci *ohci, uint32_t reg) +{ + return ohci->rhdesc_b; +} + +static void rhdb_w(struct ohci *ohci, uint32_t reg, uint32_t val) +{ + if ( ohci->rhdesc_b != val ) + fprintf(stderr, "usb-ohci: %s: invalid write to " + "root decriptor B: " + "0x%.8x -> 0x%.8x\n", + ohci->pci_dev.name, + ohci->rhdesc_b, val); +} + +/* Root hub status */ +static uint32_t rhstatus_r(struct ohci *ohci, uint32_t reg) +{ + return ohci->rhstatus; +} + +static void rhstatus_w(struct ohci *ohci, uint32_t reg, uint32_t val) +{ + uint32_t old_state; + + old_state = ohci->rhstatus; + + /* write 1 to clear OCIC */ + if ( val & OHCI_RHS_OCIC ) + ohci->rhstatus &= ~OHCI_RHS_OCIC; + + if ( val & OHCI_RHS_LPS ) { + int i; + + for(i=0; i < OHCI_NDP; i++) + ohci_port_power(ohci, i, 0); + dprintf("usb-ohci: %s: powered down all ports\n", + ohci->pci_dev.name); + } + + if ( val & OHCI_RHS_LPSC ) { + int i; + + for(i=0; i < OHCI_NDP; i++) + ohci_port_power(ohci, i, 1); + dprintf("usb-ohci: %s: powered up all ports\n", + ohci->pci_dev.name); + } + + if ( val & OHCI_RHS_DRWE ) + ohci->rhstatus |= OHCI_RHS_DRWE; + + if ( val & OHCI_RHS_CRWE ) + ohci->rhstatus &= ~OHCI_RHS_DRWE; + + if ( old_state != ohci->rhstatus ) + ohci_set_interrupt(ohci, OHCI_INTR_RHSC); +} + +/* Root hub port status */ +static uint32_t ps_r(struct ohci *ohci, uint32_t reg) +{ + int i = reg - 21; + return ohci->rhport[i] | OHCI_PORT_PPS; +} + +static void ps_w(struct ohci *ohci, uint32_t reg, uint32_t val) +{ + int i = reg - 21; + uint32_t old_state; + + old_state = ohci->rhport[i]; + + /* Write to clear CSC, PESC, PSSC, OCIC, PRSC */ + if ( val & OHCI_PORT_WTC ) + ohci->rhport[i] &= ~(val & OHCI_PORT_WTC); + + if ( val & OHCI_PORT_CCS ) + ohci->rhport[i] &= ~OHCI_PORT_PES; + + ohci_port_set_if_connected(ohci, i, val & OHCI_PORT_PES); + + if ( ohci_port_set_if_connected(ohci, i, val & OHCI_PORT_PSS) ) + dprintf("usb-ohci: %s: port %i: SUSPEND\n", + ohci->pci_dev.name, i); + + if ( ohci_port_set_if_connected(ohci, i, val & OHCI_PORT_PRS) ) { + ohci->rhport[i] &= ~OHCI_PORT_PRS; + ohci->rhport[i] |= OHCI_PORT_PRSC; + dprintf("usb-ohci: %s: port %i: RESET\n", + ohci->pci_dev.name, i); + } + + /* Invert order here to ensure in ambiguous case, device is + * powered up... + */ + if ( val & OHCI_PORT_LSDA ) + ohci_port_power(ohci, i, 0); + if ( val & OHCI_PORT_PPS ) + ohci_port_power(ohci, i, 1); + + if ( old_state != ohci->rhport[i] ) + ohci_set_interrupt(ohci, OHCI_INTR_RHSC); + + return; +} + +/* Register descriptor table */ +static struct ohci_opreg opreg[] = { + {.name = "HcRevision", .read = rev_r}, + {.name = "HcControl", .read = ctl_r, .write = ctl_w}, + {.name = "HcCommandStatus", .read = status_r, .write = status_w}, + {.name = "HcInterruptStatus", .read = is_r, .write = is_w}, + {.name = "HcInterruptEnable", .read = ie_r, .write = ie_w}, + {.name = "HcInterruptDisable", .read = id_r, .write = id_w}, + {.name = "HcHCCA", .read = hcca_r, .write = hcca_w}, + {.name = "HcPeriodCurrentED", .read = head_r, .write = head_w}, + {.name = "HcControlHeadED", .read = head_r, .write = head_w}, + {.name = "HcControlCurrentED", .read = head_r, .write = head_w}, + {.name = "HcBulkHeadED", .read = head_r, .write = head_w}, + {.name = "HcBulkCurrentED", .read = head_r, .write = head_w}, + {.name = "HcDoneHead", .read = done_r}, + {.name = "HcFmInterval", .read = fmi_r, .write = fmi_w}, + {.name = "HcFmRemaining", .read = fr_r}, + {.name = "HcFmNumber", .read = fn_r}, + {.name = "HcPeriodicStart", .read = pstart_r, .write = pstart_w}, + {.name = "HcLSThreshold", .read = lst_r, .write = lst_w}, + {.name = "HcRhDescriptorA", .read = rhda_r, .write = rhda_w}, + {.name = "HcRhDescriptorB", .read = rhdb_r, .write = rhdb_w}, + {.name = "HcRhStatus", .read = rhstatus_r, .write = rhstatus_w}, + + /* The number of port status register depends on the definition + * of OHCI_NDP macro + */ + {.name = "HcRhPortStatus[0]", .read = ps_r, .write = ps_w}, + {.name = "HcRhPortStatus[1]", .read = ps_r, .write = ps_w}, + {.name = "HcRhPortStatus[2]", .read = ps_r, .write = ps_w}, + {.name = "HcRhPortStatus[3]", .read = ps_r, .write = ps_w}, + {.name = "HcRhPortStatus[4]", .read = ps_r, .write = ps_w}, + {.name = "HcRhPortStatus[5]", .read = ps_r, .write = ps_w}, + {.name = "HcRhPortStatus[6]", .read = ps_r, .write = ps_w}, + {.name = "HcRhPortStatus[7]", .read = ps_r, .write = ps_w}, +}; + +static size_t num_opreg = sizeof(opreg)/sizeof(*opreg); + +static uint32_t mem_read(void *ptr, target_phys_addr_t addr) +{ + struct ohci *ohci = ptr; + uint32_t ofs = (addr - ohci->mem_base) >> 2; + struct ohci_opreg *reg; + uint32_t ret; + + /* Only aligned reads are allowed on OHCI */ + if ( addr & 3 ) { + fprintf(stderr, "usb-ohci: Mis-aligned read\n"); + return 0xffffffff; + } + + if ( ofs >= num_opreg ) { + fprintf(stderr, "usb-ohci: Trying to read register %u/%u\n", + ofs, num_opreg); + return 0xffffffff; + } + + reg = &opreg[ofs]; + + if ( !reg->read ) { + fprintf(stderr, "usb-ohci: register %u (%s) is not readable\n", + ofs, reg->name); + return 0xffffffff; + } + + ret = reg->read(ohci, ofs); + +// if ( ofs < 18 ) +// dprintf("usb-ohci: read 0x%.8x from %u (%s)\n", +// ret, ofs, reg->name); + + return ret; +} + +static void mem_write(void *ptr, target_phys_addr_t addr, uint32_t value) +{ + struct ohci *ohci = ptr; + uint32_t ofs = (addr - ohci->mem_base) >> 2; + struct ohci_opreg *reg; + + /* Only aligned writes are allowed on OHCI */ + if ( addr & 3 ) { + fprintf(stderr, "usb-ohci: Mis-aligned write\n"); + return; + } + + if ( ofs >= num_opreg ) { + fprintf(stderr, "usb-ohci: Trying to write register %u/%u\n", + ofs, num_opreg); + return; + } + + reg = &opreg[ofs]; + + if ( !reg->write ) { + fprintf(stderr, "usb-ohci: register %u (%s) is not writable\n", + ofs, reg->name); + return; + } + +// if ( ofs < 18 ) +// dprintf("usb-ohci: write 0x%.8x to %u (%s)\n", +// value, ofs, reg->name); + + reg->write(ohci, ofs, value); +} + +/* Only dword reads are defined on OHCI register space */ +static CPUReadMemoryFunc *cpu_callback_read[3]={ + NULL, + NULL, + mem_read, +}; + +/* Only dword writes are defined on OHCI register space */ +static CPUWriteMemoryFunc *cpu_callback_write[3]={ + NULL, + NULL, + mem_write, +}; + +static void mapfunc(PCIDevice *pci_dev, int i, + uint32_t addr, uint32_t size, int type) +{ + struct ohci *ohci = (struct ohci *)pci_dev; + ohci->mem_base = addr; + cpu_register_physical_memory(addr, size, ohci->mem); +} + +void usb_ohci_init(const char *name, uint16_t vid, uint16_t did, + struct PCIBus *bus, int devfn) +{ + struct ohci *ohci; + + if ( usb_frame_time == 0 ) { +#if OHCI_TIME_WARP + usb_frame_time = ticks_per_sec; + usb_bit_time = muldiv64(1, ticks_per_sec, USB_HZ/1000); +#else + usb_frame_time = muldiv64(1, ticks_per_sec, 1000); + if ( ticks_per_sec >= USB_HZ ) { + usb_bit_time = muldiv64(1, ticks_per_sec, USB_HZ); + }else{ + usb_bit_time = 1; + } +#endif + dprintf("usb-ohci: usb_bit_time=%lli usb_frame_time=%lli\n", + usb_frame_time, usb_bit_time); + } + + ohci = (struct ohci *)pci_register_device(bus, name, sizeof(*ohci), + devfn, + NULL, NULL); + if ( ohci == NULL ) { + fprintf(stderr, "usb-ohci: %s: Failed to register PCI device\n", + name); + return; + } + + ohci->pci_dev.config[0x00] = vid & 0xff; + ohci->pci_dev.config[0x01] = (vid >> 8) & 0xff; + ohci->pci_dev.config[0x02] = did & 0xff; + ohci->pci_dev.config[0x03] = (did >> 8) & 0xff; + ohci->pci_dev.config[0x09] = 0x10; /* OHCI */ + ohci->pci_dev.config[0x0a] = 0x3; + ohci->pci_dev.config[0x0b] = 0xc; + ohci->pci_dev.config[0x3d] = 0x01; /* interrupt pin 1 */ + + ohci->mem = cpu_register_io_memory(0, + cpu_callback_read, + cpu_callback_write, + ohci); + + pci_register_io_region((struct PCIDevice *)ohci, 0, 256, + PCI_ADDRESS_SPACE_MEM, mapfunc); + + ohci_reset(ohci); +} diff -uNr qemu-orig/qemu-0.7.0/vl.h qemu-0.7.0/vl.h --- qemu-orig/qemu-0.7.0/vl.h 2005-06-09 00:48:18.000000000 -0500 +++ qemu-0.7.0/vl.h 2005-06-09 00:37:42.000000000 -0500 @@ -524,6 +524,9 @@ void pci_bios_init(void); void pci_info(void); +void usb_ohci_init(const char *name, uint16_t vid, uint16_t did, + struct PCIBus *bus, int devfn); + /* temporary: will be moved in platform specific file */ PCIBus *pci_prep_init(void); struct openpic_t;