On Monday 20 June 2005 01:08 pm, Thomas Strobel wrote:
> Hello,
> 
> I don't really get qemu patched for usb use. I don't know if I have to
> patch qemu-0.7.0 with the original patch form
> http://scaramanga.co.uk/stuff/qemu-usb/ before using the patch added in
> the mailing list. Or do I only have to use the patch from the list?
> The next problem I have is that I have quite a problem with getting the
> patch out of the html page. There are quite a few new line breaks where
> I don't know if they should really be there. So Lonnie Mendez, or
> anybody else, it would be nice if you could put your patch somewhere for
> download.


   Hello.  The archive stores attachments fairly well... so I will send it off 
as one.
You can also find it on http://gnome.dnsalias.net/patches/

   The patch is simply a rediff with some minor changes against 0.7.0, but 
should
also apply against cvs.   I'm fairly close to releasing another update to the 
current
usb-ohci work.
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;
_______________________________________________
Qemu-devel mailing list
Qemu-devel@nongnu.org
http://lists.nongnu.org/mailman/listinfo/qemu-devel

Reply via email to