summaryrefslogtreecommitdiffstats
path: root/cfe/cfe/usb/ohci.c
diff options
context:
space:
mode:
Diffstat (limited to 'cfe/cfe/usb/ohci.c')
-rw-r--r--cfe/cfe/usb/ohci.c2126
1 files changed, 2126 insertions, 0 deletions
diff --git a/cfe/cfe/usb/ohci.c b/cfe/cfe/usb/ohci.c
new file mode 100644
index 0000000..c002091
--- /dev/null
+++ b/cfe/cfe/usb/ohci.c
@@ -0,0 +1,2126 @@
+/* *********************************************************************
+ * Broadcom Common Firmware Environment (CFE)
+ *
+ * OHCI device driver File: ohci.c
+ *
+ * Open Host Controller Interface low-level routines
+ *
+ * Author: Mitch Lichtenberg (mpl@broadcom.com)
+ *
+ *********************************************************************
+ *
+ * Copyright 2000,2001,2002,2003
+ * Broadcom Corporation. All rights reserved.
+ *
+ * This software is furnished under license and may be used and
+ * copied only in accordance with the following terms and
+ * conditions. Subject to these conditions, you may download,
+ * copy, install, use, modify and distribute modified or unmodified
+ * copies of this software in source and/or binary form. No title
+ * or ownership is transferred hereby.
+ *
+ * 1) Any source code used, modified or distributed must reproduce
+ * and retain this copyright notice and list of conditions
+ * as they appear in the source file.
+ *
+ * 2) No right is granted to use any trade name, trademark, or
+ * logo of Broadcom Corporation. The "Broadcom Corporation"
+ * name may not be used to endorse or promote products derived
+ * from this software without the prior written permission of
+ * Broadcom Corporation.
+ *
+ * 3) THIS SOFTWARE IS PROVIDED "AS-IS" AND ANY EXPRESS OR
+ * IMPLIED WARRANTIES, INCLUDING BUT NOT LIMITED TO, ANY IMPLIED
+ * WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR
+ * PURPOSE, OR NON-INFRINGEMENT ARE DISCLAIMED. IN NO EVENT
+ * SHALL BROADCOM BE LIABLE FOR ANY DAMAGES WHATSOEVER, AND IN
+ * PARTICULAR, BROADCOM SHALL NOT BE LIABLE FOR DIRECT, INDIRECT,
+ * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
+ * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE
+ * GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR
+ * BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY
+ * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR
+ * TORT (INCLUDING NEGLIGENCE OR OTHERWISE), EVEN IF ADVISED OF
+ * THE POSSIBILITY OF SUCH DAMAGE.
+ ********************************************************************* */
+
+
+#ifndef _CFE_
+#include <stdio.h>
+#include <stdlib.h>
+#include <unistd.h>
+#include <string.h>
+#include <stdint.h>
+#include "usbhack.h"
+#define CPUCFG_COHERENT_DMA 1 /* hack runs on a PC, PCs are coherent */
+#else
+#include "lib_types.h"
+#include "lib_printf.h"
+#include "lib_string.h"
+#include "lib_physio.h"
+#include "addrspace.h"
+#include "cpu_config.h" /* for CPUCFG_COHERENT_DMA */
+#endif
+
+#include "lib_malloc.h"
+#include "lib_queue.h"
+#include "usbchap9.h"
+#include "usbd.h"
+#include "ohci.h"
+
+
+/* *********************************************************************
+ * Macros for dealing with hardware
+ *
+ * This is all yucky stuff that needs to be made more
+ * processor-independent. It's mostly here now to help us with
+ * our test harness.
+ ********************************************************************* */
+
+#if defined(_CFE_) && defined(__MIPSEB)
+#define BSWAP32(x) __swap32(x)
+static inline uint32_t __swap32(uint32_t x)
+{
+ uint32_t y;
+
+ y = ((x & 0xFF) << 24) |
+ ((x & 0xFF00) << 8) |
+ ((x & 0xFF0000) >> 8) |
+ ((x & 0xFF000000) >> 24);
+
+ return y;
+}
+#else
+#define BSWAP32(x) (x)
+#endif
+
+
+#ifndef _CFE_
+extern uint32_t vtop(void *ptr);
+extern void *ptov(uint32_t x);
+#define OHCI_VTOP(ptr) vtop(ptr)
+#define OHCI_PTOV(ptr) ptov(ptr)
+#define OHCI_WRITECSR(softc,x,y) \
+ *((volatile uint32_t *) ((softc)->ohci_regs + ((x)/sizeof(uint32_t)))) = (y)
+#define OHCI_READCSR(softc,x) \
+ *((volatile uint32_t *) ((softc)->ohci_regs + ((x)/sizeof(uint32_t))))
+#else
+#define OHCI_VTOP(ptr) ((uint32_t)PHYSADDR((long)(ptr)))
+
+#if CPUCFG_COHERENT_DMA
+#define OHCI_PTOV(ptr) ((void *)(KERNADDR(ptr)))
+#else
+#define OHCI_PTOV(ptr) ((void *)(UNCADDR(ptr)))
+#endif
+
+#define OHCI_WRITECSR(softc,x,y) \
+ phys_write32(((softc)->ohci_regs + (x)),(y))
+#define OHCI_READCSR(softc,x) \
+ phys_read32(((softc)->ohci_regs + (x)))
+#endif
+
+#if CPUCFG_COHERENT_DMA
+#define OHCI_INVAL_RANGE(s,l)
+#define OHCI_FLUSH_RANGE(s,l)
+#else /* not coherent */
+#define CFE_CACHE_INVAL_RANGE 32 /* XXX belongs in include file */
+#define CFE_CACHE_FLUSH_RANGE 64
+extern void _cfe_flushcache(int,uint8_t *,uint8_t *);
+#define OHCI_INVAL_RANGE(s,l) _cfe_flushcache(CFE_CACHE_INVAL_RANGE,((uint8_t *) (s)),((uint8_t *) (s))+(l))
+#define OHCI_FLUSH_RANGE(s,l) _cfe_flushcache(CFE_CACHE_FLUSH_RANGE,((uint8_t *) (s)),((uint8_t *) (s))+(l))
+#endif
+
+
+/* *********************************************************************
+ * Bit-reverse table - this table consists of the numbers
+ * at its index, listed in reverse. So, the reverse of 0000 0010
+ * is 0100 0000.
+ ********************************************************************* */
+
+const static int ohci_revbits[OHCI_INTTABLE_SIZE] = {
+ 0x00, 0x10, 0x08, 0x18, 0x04, 0x14, 0x0c, 0x1c,
+ 0x02, 0x12, 0x0a, 0x1a, 0x06, 0x16, 0x0e, 0x1e,
+ 0x01, 0x11, 0x09, 0x19, 0x05, 0x15, 0x0d, 0x1d,
+ 0x03, 0x13, 0x0b, 0x1b, 0x07, 0x17, 0x0f, 0x1f
+};
+
+
+/* *********************************************************************
+ * Macros to convert from "hardware" endpoint and transfer
+ * descriptors (ohci_ed_t, ohci_td_t) to "software"
+ * data structures (ohci_transfer_t, ohci_endpoint_t).
+ *
+ * Basically, there are two tables, indexed by the same value
+ * By subtracting the base of one pool from a pointer, we get
+ * the index into the other table.
+ *
+ * We *could* have included the ed and td in the software
+ * data structures, but placing all the hardware stuff in one
+ * pool will make it easier for hardware that does not handle
+ * coherent DMA, since we can be less careful about what we flush
+ * and what we invalidate.
+ ********************************************************************* */
+
+#define ohci_td_from_transfer(softc,transfer) \
+ ((softc)->ohci_hwtdpool + ((transfer) - (softc)->ohci_transfer_pool))
+
+#define ohci_transfer_from_td(softc,td) \
+ ((softc)->ohci_transfer_pool + ((td) - (softc)->ohci_hwtdpool))
+
+#define ohci_ed_from_endpoint(softc,endpoint) \
+ ((softc)->ohci_hwedpool + ((endpoint) - (softc)->ohci_endpoint_pool))
+
+#define ohci_endpoint_from_ed(softc,ed) \
+ ((softc)->ohci_endpoint_pool + ((ed) - (softc)->ohci_hwedpool))
+
+/* *********************************************************************
+ * Forward declarations
+ ********************************************************************* */
+
+static int ohci_roothub_xfer(usbbus_t *bus,usb_ept_t *uept,usbreq_t *ur);
+static void ohci_roothub_statchg(ohci_softc_t *softc);
+extern usb_hcdrv_t ohci_driver;
+
+/* *********************************************************************
+ * Globals
+ ********************************************************************* */
+
+int ohcidebug = 0;
+void ohci_dumprhstat(uint32_t reg);
+void ohci_dumpportstat(int idx,uint32_t reg);
+void ohci_dumptd(ohci_td_t *td);
+void ohci_dumptdchain(ohci_td_t *td);
+void ohci_dumped(ohci_ed_t *ed);
+void ohci_dumpedchain(ohci_ed_t *ed);
+
+
+/* *********************************************************************
+ * Some debug routines
+ ********************************************************************* */
+
+#if 1
+void ohci_dumprhstat(uint32_t reg)
+{
+ printf("HubStatus: %08X ",reg);
+
+ if (reg & M_OHCI_RHSTATUS_LPS) printf("LocalPowerStatus ");
+ if (reg & M_OHCI_RHSTATUS_OCI) printf("OverCurrent ");
+ if (reg & M_OHCI_RHSTATUS_DRWE) printf("DeviceRemoteWakeupEnable ");
+ if (reg & M_OHCI_RHSTATUS_LPSC) printf("LocalPowerStatusChange ");
+ if (reg & M_OHCI_RHSTATUS_OCIC) printf("OverCurrentIndicatorChange ");
+ printf("\n");
+
+}
+
+void ohci_dumpportstat(int idx,uint32_t reg)
+{
+ printf("Port %d: %08X ",idx,reg);
+ if (reg & M_OHCI_RHPORTSTAT_CCS) printf("Connected ");
+ if (reg & M_OHCI_RHPORTSTAT_PES) printf("PortEnabled ");
+ if (reg & M_OHCI_RHPORTSTAT_PSS) printf("PortSuspended ");
+ if (reg & M_OHCI_RHPORTSTAT_POCI) printf("PortOverCurrent ");
+ if (reg & M_OHCI_RHPORTSTAT_PRS) printf("PortReset ");
+ if (reg & M_OHCI_RHPORTSTAT_PPS) printf("PortPowered ");
+ if (reg & M_OHCI_RHPORTSTAT_LSDA) printf("LowSpeed ");
+ if (reg & M_OHCI_RHPORTSTAT_CSC) printf("ConnectStatusChange ");
+ if (reg & M_OHCI_RHPORTSTAT_PESC) printf("PortEnableStatusChange ");
+ if (reg & M_OHCI_RHPORTSTAT_PSSC) printf("PortSuspendStatusChange ");
+ if (reg & M_OHCI_RHPORTSTAT_OCIC) printf("OverCurrentIndicatorChange ");
+ if (reg & M_OHCI_RHPORTSTAT_PRSC) printf("PortResetStatusChange ");
+ printf("\n");
+}
+
+void ohci_dumptd(ohci_td_t *td)
+{
+ uint32_t ctl;
+ static char *pids[4] = {"SETUP","OUT","IN","RSVD"};
+
+ ctl = BSWAP32(td->td_control);
+
+ printf("[%08X] ctl=%08X (DP=%s,DI=%d,T=%d,EC=%d,CC=%d%s) cbp=%08X be=%08X next=%08X\n",
+ OHCI_VTOP(td),
+ ctl,
+ pids[G_OHCI_TD_PID(ctl)],
+ G_OHCI_TD_DI(ctl),
+ G_OHCI_TD_DT(ctl),
+ G_OHCI_TD_EC(ctl),
+ G_OHCI_TD_CC(ctl),
+ (ctl & M_OHCI_TD_SHORTOK) ? ",R" : "",
+ BSWAP32(td->td_cbp),
+ BSWAP32(td->td_be),
+ BSWAP32(td->td_next_td));
+}
+
+void ohci_dumptdchain(ohci_td_t *td)
+{
+ int idx = 0;
+ for (;;) {
+ printf("%d:[%08X] ctl=%08X cbp=%08X be=%08X next=%08X\n",
+ idx,
+ OHCI_VTOP(td),
+ BSWAP32(td->td_control),
+ BSWAP32(td->td_cbp),
+ BSWAP32(td->td_be),
+ BSWAP32(td->td_next_td));
+ if (!td->td_next_td) break;
+ td = (ohci_td_t *) OHCI_PTOV(BSWAP32(td->td_next_td));
+ idx++;
+ }
+}
+
+void ohci_dumped(ohci_ed_t *ed)
+{
+ uint32_t ctl;
+ static char *pids[4] = {"FTD","OUT","IN","FTD"};
+
+ ctl = BSWAP32(ed->ed_control),
+
+ printf("[%08X] Ctl=%08X (MPS=%d%s%s%s,EN=%d,FA=%d,D=%s) Tailp=%08X headp=%08X next=%08X %s\n",
+ OHCI_VTOP(ed),
+ ctl,
+ G_OHCI_ED_MPS(ctl),
+ (ctl & M_OHCI_ED_LOWSPEED) ? ",LS" : "",
+ (ctl & M_OHCI_ED_SKIP) ? ",SKIP" : "",
+ (ctl & M_OHCI_ED_ISOCFMT) ? ",ISOC" : "",
+ G_OHCI_ED_EN(ctl),
+ G_OHCI_ED_FA(ctl),
+ pids[G_OHCI_ED_DIR(ctl)],
+ BSWAP32(ed->ed_tailp),
+ BSWAP32(ed->ed_headp),
+ BSWAP32(ed->ed_next_ed),
+ BSWAP32(ed->ed_headp) & M_OHCI_ED_HALT ? "HALT" : "");
+ if ((ed->ed_headp & M_OHCI_ED_PTRMASK) == 0) return;
+ ohci_dumptdchain(OHCI_PTOV(BSWAP32(ed->ed_headp) & M_OHCI_ED_PTRMASK));
+}
+
+void ohci_dumpedchain(ohci_ed_t *ed)
+{
+ int idx = 0;
+ for (;;) {
+ printf("---\nED#%d -> ",idx);
+ ohci_dumped(ed);
+ if (!ed->ed_next_ed) break;
+ if (idx > 50) break;
+ ed = (ohci_ed_t *) OHCI_PTOV(BSWAP32(ed->ed_next_ed));
+ idx++;
+ }
+}
+
+#endif
+
+
+static void eptstats(ohci_softc_t *softc)
+{
+ int cnt;
+ ohci_endpoint_t *e;
+ cnt = 0;
+
+ e = softc->ohci_endpoint_freelist;
+ while (e) { e = e->ep_next; cnt++; }
+ printf("%d left, %d inuse\n",cnt,OHCI_EDPOOL_SIZE-cnt);
+}
+
+/* *********************************************************************
+ * _ohci_allocept(softc)
+ *
+ * Allocate an endpoint data structure from the pool, and
+ * make it ready for use. The endpoint is NOT attached to
+ * the hardware at this time.
+ *
+ * Input parameters:
+ * softc - our OHCI controller
+ *
+ * Return value:
+ * pointer to endpoint or NULL
+ ********************************************************************* */
+
+static ohci_endpoint_t *_ohci_allocept(ohci_softc_t *softc)
+{
+ ohci_endpoint_t *e;
+ ohci_ed_t *ed;
+
+ if (ohcidebug > 2) {
+ printf("AllocEpt: ");eptstats(softc);
+ }
+
+ e = softc->ohci_endpoint_freelist;
+
+ if (!e) {
+ printf("No endpoints left!\n");
+ return NULL;
+ }
+
+ softc->ohci_endpoint_freelist = e->ep_next;
+
+ ed = ohci_ed_from_endpoint(softc,e);
+
+ ed->ed_control = BSWAP32(M_OHCI_ED_SKIP);
+ ed->ed_tailp = BSWAP32(0);
+ ed->ed_headp = BSWAP32(0);
+ ed->ed_next_ed = BSWAP32(0);
+
+ e->ep_phys = OHCI_VTOP(ed);
+ e->ep_next = NULL;
+
+ return e;
+}
+
+/* *********************************************************************
+ * _ohci_allocxfer(softc)
+ *
+ * Allocate a transfer descriptor. It is prepared for use
+ * but not attached to the hardware.
+ *
+ * Input parameters:
+ * softc - our OHCI controller
+ *
+ * Return value:
+ * transfer descriptor, or NULL
+ ********************************************************************* */
+
+static ohci_transfer_t *_ohci_allocxfer(ohci_softc_t *softc)
+{
+ ohci_transfer_t *t;
+ ohci_td_t *td;
+
+ if (ohcidebug > 2) {
+ int cnt;
+ cnt = 0;
+ t = softc->ohci_transfer_freelist;
+ while (t) { t = t->t_next; cnt++; }
+ printf("AllocXfer: %d left, %d inuse\n",cnt,OHCI_TDPOOL_SIZE-cnt);
+ }
+
+ t = softc->ohci_transfer_freelist;
+
+ if (!t) {
+ printf("No more transfer descriptors!\n");
+ return NULL;
+ }
+
+ softc->ohci_transfer_freelist = t->t_next;
+
+ td = ohci_td_from_transfer(softc,t);
+
+ td->td_control = BSWAP32(0);
+ td->td_cbp = BSWAP32(0);
+ td->td_next_td = BSWAP32(0);
+ td->td_be = BSWAP32(0);
+
+ t->t_ref = NULL;
+ t->t_next = NULL;
+
+ return t;
+}
+
+/* *********************************************************************
+ * _ohci_freeept(softc,e)
+ *
+ * Free an endpoint, returning it to the pool.
+ *
+ * Input parameters:
+ * softc - our OHCI controller
+ * e - endpoint descriptor to return
+ *
+ * Return value:
+ * nothing
+ ********************************************************************* */
+
+static void _ohci_freeept(ohci_softc_t *softc,ohci_endpoint_t *e)
+{
+ if (ohcidebug > 2) {
+ int cnt;
+ ohci_endpoint_t *ee;
+ cnt = 0;
+ ee = softc->ohci_endpoint_freelist;
+ while (ee) { ee = ee->ep_next; cnt++; }
+ printf("FreeEpt[%p]: %d left, %d inuse\n",e,cnt,OHCI_EDPOOL_SIZE-cnt);
+ }
+
+ e->ep_next = softc->ohci_endpoint_freelist;
+ softc->ohci_endpoint_freelist = e;
+}
+
+/* *********************************************************************
+ * _ohci_freexfer(softc,t)
+ *
+ * Free a transfer descriptor, returning it to the pool.
+ *
+ * Input parameters:
+ * softc - our OHCI controller
+ * t - transfer descriptor to return
+ *
+ * Return value:
+ * nothing
+ ********************************************************************* */
+
+static void _ohci_freexfer(ohci_softc_t *softc,ohci_transfer_t *t)
+{
+ t->t_next = softc->ohci_transfer_freelist;
+ softc->ohci_transfer_freelist = t;
+}
+
+/* *********************************************************************
+ * _ohci_initpools(softc)
+ *
+ * Allocate and initialize the various pools of things that
+ * we use in the OHCI driver. We do this by allocating some
+ * big chunks from the heap and carving them up.
+ *
+ * Input parameters:
+ * softc - our OHCI controller
+ *
+ * Return value:
+ * 0 if ok
+ * else error code
+ ********************************************************************* */
+
+static int _ohci_initpools(ohci_softc_t *softc)
+{
+ int idx;
+
+ /*
+ * Do the transfer descriptor pool
+ */
+
+ softc->ohci_transfer_pool = KMALLOC(OHCI_TDPOOL_SIZE*sizeof(ohci_transfer_t),0);
+ softc->ohci_hwtdpool = KMALLOC(OHCI_TDPOOL_SIZE*sizeof(ohci_td_t),OHCI_TD_ALIGN);
+
+ /*
+ * In the case of noncoherent DMA, make these uncached addresses.
+ * This way all our descriptors will be uncached. Makes life easier, as we
+ * do not need to worry about flushing descriptors, etc.
+ */
+
+#if (!CPUCFG_COHERENT_DMA)
+ softc->ohci_hwtdpool = (void *) UNCADDR(PHYSADDR((uint32_t)(softc->ohci_hwtdpool)));
+#endif
+
+ if (!softc->ohci_transfer_pool || !softc->ohci_hwtdpool) {
+ printf("Could not allocate transfer descriptors\n");
+ return -1;
+ }
+
+ softc->ohci_transfer_freelist = NULL;
+
+ for (idx = 0; idx < OHCI_TDPOOL_SIZE; idx++) {
+ _ohci_freexfer(softc,softc->ohci_transfer_pool+idx);
+ }
+
+ /*
+ * Do the endpoint descriptor pool
+ */
+
+ softc->ohci_endpoint_pool = KMALLOC(OHCI_EDPOOL_SIZE*sizeof(ohci_endpoint_t),0);
+
+ softc->ohci_hwedpool = KMALLOC(OHCI_EDPOOL_SIZE*sizeof(ohci_ed_t),OHCI_ED_ALIGN);
+
+#if (!CPUCFG_COHERENT_DMA)
+ softc->ohci_hwedpool = (void *) UNCADDR(PHYSADDR((uint32_t)(softc->ohci_hwedpool)));
+#endif
+
+ if (!softc->ohci_endpoint_pool || !softc->ohci_hwedpool) {
+ printf("Could not allocate transfer descriptors\n");
+ return -1;
+ }
+
+ softc->ohci_endpoint_freelist = NULL;
+
+ for (idx = 0; idx < OHCI_EDPOOL_SIZE; idx++) {
+ _ohci_freeept(softc,softc->ohci_endpoint_pool+idx);
+ }
+
+ /*
+ * Finally the host communications area
+ */
+
+ softc->ohci_hcca = KMALLOC(sizeof(ohci_hcca_t),sizeof(ohci_hcca_t));
+
+#if (!CPUCFG_COHERENT_DMA)
+ softc->ohci_hcca = (void *) UNCADDR(PHYSADDR((uint32_t)(softc->ohci_hcca)));
+#endif
+
+ memset(softc->ohci_hcca,0,sizeof(ohci_hcca_t));
+
+ return 0;
+}
+
+
+/* *********************************************************************
+ * ohci_start(bus)
+ *
+ * Start the OHCI controller. After this routine is called,
+ * the hardware will be operational and ready to accept
+ * descriptors and interrupt calls.
+ *
+ * Input parameters:
+ * bus - bus structure, from ohci_create
+ *
+ * Return value:
+ * 0 if ok
+ * else error code
+ ********************************************************************* */
+
+static int ohci_start(usbbus_t *bus)
+{
+ ohci_softc_t *softc = (ohci_softc_t *) bus->ub_hwsoftc;
+ uint32_t frameint;
+ uint32_t reg;
+ int idx;
+
+ /*
+ * Force a reset to the controller, followed by a short delay
+ */
+
+ OHCI_WRITECSR(softc,R_OHCI_CONTROL,V_OHCI_CONTROL_HCFS(K_OHCI_HCFS_RESET));
+ usb_delay_ms(bus,OHCI_RESET_DELAY);
+
+ /* Host controller state is now "RESET" */
+
+ /*
+ * We need the frame interval later, so get a copy of it now.
+ */
+ frameint = G_OHCI_FMINTERVAL_FI(OHCI_READCSR(softc,R_OHCI_FMINTERVAL));
+
+ /*
+ * Reset the host controller. When you set the HCR bit
+ * if self-clears when the reset is complete.
+ */
+
+ OHCI_WRITECSR(softc,R_OHCI_CMDSTATUS,M_OHCI_CMDSTATUS_HCR);
+ for (idx = 0; idx < 10000; idx++) {
+ if (!(OHCI_READCSR(softc,R_OHCI_CMDSTATUS) & M_OHCI_CMDSTATUS_HCR)) break;
+ }
+
+ if (OHCI_READCSR(softc,R_OHCI_CMDSTATUS) & M_OHCI_CMDSTATUS_HCR) {
+ /* controller never came out of reset */
+ return -1;
+ }
+
+ /*
+ * Host controller state is now "SUSPEND". We must exit
+ * from this state within 2ms. (5.1.1.4)
+ *
+ * Set up pointers to data structures.
+ */
+
+ OHCI_WRITECSR(softc,R_OHCI_HCCA,OHCI_VTOP(softc->ohci_hcca));
+ OHCI_WRITECSR(softc,R_OHCI_CONTROLHEADED,softc->ohci_ctl_list->ep_phys);
+ OHCI_WRITECSR(softc,R_OHCI_BULKHEADED,softc->ohci_bulk_list->ep_phys);
+
+ /*
+ * Our driver is polled, turn off interrupts
+ */
+
+ OHCI_WRITECSR(softc,R_OHCI_INTDISABLE,M_OHCI_INT_ALL);
+
+ /*
+ * Set up the control register.
+ */
+
+ reg = OHCI_READCSR(softc,R_OHCI_CONTROL);
+
+ reg = M_OHCI_CONTROL_PLE | M_OHCI_CONTROL_CLE | M_OHCI_CONTROL_BLE |
+ M_OHCI_CONTROL_IE |
+ V_OHCI_CONTROL_CBSR(K_OHCI_CBSR_41) |
+ V_OHCI_CONTROL_HCFS(K_OHCI_HCFS_OPERATIONAL);
+
+ OHCI_WRITECSR(softc,R_OHCI_CONTROL,reg);
+
+
+ /*
+ * controller state is now OPERATIONAL
+ */
+
+ reg = OHCI_READCSR(softc,R_OHCI_FMINTERVAL);
+ reg &= M_OHCI_FMINTERVAL_FIT;
+ reg ^= M_OHCI_FMINTERVAL_FIT;
+ reg |= V_OHCI_FMINTERVAL_FSMPS(OHCI_CALC_FSMPS(frameint)) |
+ V_OHCI_FMINTERVAL_FI(frameint);
+ OHCI_WRITECSR(softc,R_OHCI_FMINTERVAL,reg);
+
+ reg = frameint * 9 / 10; /* calculate 90% */
+ OHCI_WRITECSR(softc,R_OHCI_PERIODICSTART,reg);
+
+ usb_delay_ms(softc->ohci_bus,10);
+
+ /*
+ * Remember how many ports we have
+ */
+
+ reg = OHCI_READCSR(softc,R_OHCI_RHDSCRA);
+ softc->ohci_ndp = G_OHCI_RHDSCRA_NDP(reg);
+
+
+ /*
+ * Enable port power
+ */
+
+ OHCI_WRITECSR(softc,R_OHCI_RHSTATUS,M_OHCI_RHSTATUS_LPSC);
+ usb_delay_ms(softc->ohci_bus,10);
+
+ return 0;
+}
+
+
+/* *********************************************************************
+ * _ohci_setupepts(softc)
+ *
+ * Set up the endpoint tree, as described in the OHCI manual.
+ * Basically the hardware knows how to scan lists of lists,
+ * so we build a tree where each level is pointed to by two
+ * parent nodes. We can choose our scanning rate by attaching
+ * endpoints anywhere within this tree.
+ *
+ * Input parameters:
+ * softc - our OHCI controller
+ *
+ * Return value:
+ * 0 if ok
+ * else error (out of descriptors)
+ ********************************************************************* */
+
+static int _ohci_setupepts(ohci_softc_t *softc)
+{
+ int idx;
+ ohci_endpoint_t *e;
+ ohci_ed_t *ed;
+ ohci_endpoint_t *child;
+
+ /*
+ * Set up the list heads for the isochronous, control,
+ * and bulk transfer lists. They don't get the same "tree"
+ * treatment that the interrupt devices get.
+ *
+ * For the purposes of CFE, it's probably not necessary
+ * to be this fancy. The only device we're planning to
+ * talk to is the keyboard and some hubs, which should
+ * have pretty minimal requirements. It's conceivable
+ * that this firmware may find a new home in other
+ * devices, so we'll meet halfway and do some things
+ * "fancy."
+ */
+
+ softc->ohci_isoc_list = _ohci_allocept(softc);
+ softc->ohci_ctl_list = _ohci_allocept(softc);
+ softc->ohci_bulk_list = _ohci_allocept(softc);
+
+ /*
+ * Set up a tree of empty endpoint descriptors. This is
+ * tree is scanned by the hardware from the leaves up to
+ * the root. Once a millisecond, the hardware picks the
+ * next leaf and starts scanning descriptors looking
+ * for something to do. It traverses all of the endpoints
+ * along the way until it gets to the root.
+ *
+ * The idea here is if you put a transfer descriptor on the
+ * root node, the hardware will see it every millisecond,
+ * since the root will be examined each time. If you
+ * put the TD on the leaf, it will be 1/32 millisecond.
+ * The tree therefore is six levels deep.
+ */
+
+ for (idx = 0; idx < OHCI_INTTREE_SIZE; idx++) {
+ e = _ohci_allocept(softc); /* allocated with sKip bit set */
+ softc->ohci_edtable[idx] = e;
+ child = (idx == 0) ? softc->ohci_isoc_list : softc->ohci_edtable[(idx-1)/2];
+ ed = ohci_ed_from_endpoint(softc,e);
+ ed->ed_next_ed = BSWAP32(child->ep_phys);
+ e->ep_next = child;
+ }
+
+ /*
+ * We maintain both physical and virtual copies of the interrupt
+ * table (leaves of the tree).
+ */
+
+ for (idx = 0; idx < OHCI_INTTABLE_SIZE; idx++) {
+ child = softc->ohci_edtable[OHCI_INTTREE_SIZE-OHCI_INTTABLE_SIZE+idx];
+ softc->ohci_inttable[ohci_revbits[idx]] = child;
+ softc->ohci_hcca->hcca_inttable[ohci_revbits[idx]] = BSWAP32(child->ep_phys);
+ }
+
+ /*
+ * Okay, at this point the tree is built.
+ */
+ return 0;
+}
+
+/* *********************************************************************
+ * ohci_stop(bus)
+ *
+ * Stop the OHCI hardware.
+ *
+ * Input parameters:
+ * bus - our bus structure
+ *
+ * Return value:
+ * nothing
+ ********************************************************************* */
+
+static void ohci_stop(usbbus_t *bus)
+{
+ ohci_softc_t *softc = (ohci_softc_t *) bus->ub_hwsoftc;
+
+ OHCI_WRITECSR(softc,R_OHCI_CONTROL,V_OHCI_CONTROL_HCFS(K_OHCI_HCFS_RESET));
+}
+
+
+/* *********************************************************************
+ * _ohci_queueept(softc,queue,e)
+ *
+ * Add an endpoint to a list of endpoints. This routine
+ * does things in a particular way according to the OHCI
+ * spec so we can add endpoints while the hardware is running.
+ *
+ * Input parameters:
+ * queue - endpoint descriptor for head of queue
+ * e - endpoint to add to queue
+ *
+ * Return value:
+ * nothing
+ ********************************************************************* */
+
+static void _ohci_queueept(ohci_softc_t *softc,ohci_endpoint_t *queue,ohci_endpoint_t *newept)
+{
+ ohci_ed_t *qed;
+ ohci_ed_t *newed;
+
+ qed = ohci_ed_from_endpoint(softc,queue);
+ newed = ohci_ed_from_endpoint(softc,newept);
+
+ newept->ep_next = queue->ep_next;
+ newed->ed_next_ed = qed->ed_next_ed;
+
+ queue->ep_next = newept;
+ qed->ed_next_ed = BSWAP32(newept->ep_phys);
+
+ if (ohcidebug > 1) ohci_dumped(newed);
+
+}
+
+/* *********************************************************************
+ * _ohci_deqept(queue,e)
+ *
+ * Remove and endpoint from the list of endpoints. This
+ * routine does things in a particular way according to
+ * the OHCI specification, since we are operating on
+ * a running list.
+ *
+ * Input parameters:
+ * queue - base of queue to look for endpoint on
+ * e - endpoint to remove
+ *
+ * Return value:
+ * nothing
+ ********************************************************************* */
+
+static void _ohci_deqept(ohci_softc_t *softc,ohci_endpoint_t *queue,ohci_endpoint_t *e)
+{
+ ohci_endpoint_t *cur;
+ ohci_ed_t *cured;
+ ohci_ed_t *ed;
+
+ cur = queue;
+
+ while (cur && (cur->ep_next != e)) cur = cur->ep_next;
+
+ if (cur == NULL) {
+ printf("Could not remove EP %08X: not on the list!\n",(uint32_t) (intptr_t)e);
+ return;
+ }
+
+ /*
+ * Remove from our regular list
+ */
+
+ cur->ep_next = e->ep_next;
+
+ /*
+ * now remove from the hardware's list
+ */
+
+ cured = ohci_ed_from_endpoint(softc,cur);
+ ed = ohci_ed_from_endpoint(softc,e);
+
+ cured->ed_next_ed = ed->ed_next_ed;
+}
+
+
+/* *********************************************************************
+ * ohci_intr_procdoneq(softc)
+ *
+ * Process the "done" queue for this ohci controller. As
+ * descriptors are retired, the hardware links them to the
+ * "done" queue so we can examine the results.
+ *
+ * Input parameters:
+ * softc - our OHCI controller
+ *
+ * Return value:
+ * nothing
+ ********************************************************************* */
+
+static void ohci_intr_procdoneq(ohci_softc_t *softc)
+{
+ uint32_t doneq;
+ ohci_transfer_t *transfer;
+ ohci_td_t *td;
+ int val;
+ usbreq_t *ur;
+
+ /*
+ * Get the head of the queue
+ */
+
+ doneq = softc->ohci_hcca->hcca_donehead;
+ doneq = BSWAP32(doneq);
+
+ td = (ohci_td_t *) OHCI_PTOV(doneq);
+ transfer = ohci_transfer_from_td(softc,td);
+
+ /*
+ * Process all elements from the queue
+ */
+
+ while (doneq) {
+
+ ohci_ed_t *ed;
+ ohci_endpoint_t *ept;
+ usbreq_t *xur = transfer->t_ref;
+
+ if (ohcidebug > 1) {
+ if (xur) {
+ ept = (ohci_endpoint_t *) xur->ur_pipe->up_hwendpoint;
+ ed = ohci_ed_from_endpoint(softc,ept);
+// printf("ProcDoneQ:ED [%08X] -> ",ept->ep_phys);
+// ohci_dumped(ed);
+ }
+ }
+
+ /*
+ * Get the pointer to next one before freeing this one
+ */
+
+ if (ohcidebug > 1) {
+ ur = transfer->t_ref;
+ printf("Done(%d): ",ur ? ur->ur_tdcount : -1);
+ ohci_dumptd(td);
+ }
+
+ doneq = BSWAP32(td->td_next_td);
+
+ val = G_OHCI_TD_CC(BSWAP32(td->td_control));
+
+ if (val != 0) printf("[Transfer error: %d]\n",val);
+
+ /*
+ * See if it's time to call the callback.
+ */
+ ur = transfer->t_ref;
+ if (ur) {
+ ur->ur_status = val;
+ ur->ur_tdcount--;
+ if (BSWAP32(td->td_cbp) == 0) {
+ ur->ur_xferred += transfer->t_length;
+ }
+ else {
+ ur->ur_xferred += transfer->t_length -
+ (BSWAP32(td->td_be) - BSWAP32(td->td_cbp) + 1);
+ }
+ if (ur->ur_tdcount == 0) {
+ /* Noncoherent DMA: need to invalidate, since data is in phys mem */
+ OHCI_INVAL_RANGE(ur->ur_buffer,ur->ur_xferred);
+ usb_complete_request(ur,val);
+ }
+ }
+
+
+ /*
+ * Free up the request
+ */
+ _ohci_freexfer(softc,transfer);
+
+
+ /*
+ * Advance to the next request.
+ */
+
+ td = (ohci_td_t *) OHCI_PTOV(doneq);
+ transfer = ohci_transfer_from_td(softc,td);
+ }
+
+}
+
+/* *********************************************************************
+ * ohci_intr(bus)
+ *
+ * Process pending interrupts for the OHCI controller.
+ *
+ * Input parameters:
+ * bus - our bus structure
+ *
+ * Return value:
+ * 0 if we did nothing
+ * nonzero if we did something.
+ ********************************************************************* */
+
+static int ohci_intr(usbbus_t *bus)
+{
+ uint32_t reg;
+ ohci_softc_t *softc = (ohci_softc_t *) bus->ub_hwsoftc;
+
+ /*
+ * Read the interrupt status register.
+ */
+
+ reg = OHCI_READCSR(softc,R_OHCI_INTSTATUS);
+
+ /*
+ * Don't bother doing anything if nothing happened.
+ */
+ if (reg == 0) {
+ return 0;
+ }
+
+ /* Scheduling Overruns */
+ if (reg & M_OHCI_INT_SO) {
+ printf("SchedOverrun\n");
+ }
+
+ /* Done Queue */
+ if (reg & M_OHCI_INT_WDH) {
+ /* printf("DoneQueue\n"); */
+ ohci_intr_procdoneq(softc);
+ }
+
+ /* Start of Frame */
+ if (reg & M_OHCI_INT_SF) {
+ /* don't be noisy about this */
+ }
+
+ /* Resume Detect */
+ if (reg & M_OHCI_INT_RD) {
+ printf("ResumeDetect\n");
+ }
+
+ /* Unrecoverable errors */
+ if (reg & M_OHCI_INT_UE) {
+ printf("UnrecoverableError\n");
+ }
+
+ /* Frame number overflow */
+ if (reg & M_OHCI_INT_FNO) {
+ /*printf("FrameNumberOverflow\n"); */
+ }
+
+ /* Root Hub Status Change */
+ if ((reg & ~softc->ohci_intdisable) & M_OHCI_INT_RHSC) {
+ uint32_t reg;
+ if (ohcidebug > 0) {
+ printf("RootHubStatusChange: ");
+ reg = OHCI_READCSR(softc,R_OHCI_RHSTATUS);
+ ohci_dumprhstat(reg);
+ reg = OHCI_READCSR(softc,R_OHCI_RHPORTSTATUS(1));
+ ohci_dumpportstat(1,reg);
+ reg = OHCI_READCSR(softc,R_OHCI_RHPORTSTATUS(2));
+ ohci_dumpportstat(2,reg);
+ }
+ ohci_roothub_statchg(softc);
+ }
+
+ /* Ownership Change */
+ if (reg & M_OHCI_INT_OC) {
+ printf("OwnershipChange\n");
+ }
+
+ /*
+ * Write the value back to the interrupt
+ * register to clear the bits that were set.
+ */
+
+ OHCI_WRITECSR(softc,R_OHCI_INTSTATUS,reg);
+
+ return 1;
+}
+
+
+/* *********************************************************************
+ * ohci_delete(bus)
+ *
+ * Remove an OHCI bus structure and all resources allocated to
+ * it (used when shutting down USB)
+ *
+ * Input parameters:
+ * bus - our USB bus structure
+ *
+ * Return value:
+ * nothing
+ ********************************************************************* */
+
+static void ohci_delete(usbbus_t *bus)
+{
+ // xxx fill in later.
+}
+
+
+/* *********************************************************************
+ * ohci_create(addr)
+ *
+ * Create a USB bus structure and associate it with our OHCI
+ * controller device.
+ *
+ * Input parameters:
+ * addr - physical address of controller
+ *
+ * Return value:
+ * usbbus structure pointer
+ ********************************************************************* */
+
+static usbbus_t *ohci_create(physaddr_t addr)
+{
+ int res;
+ ohci_softc_t *softc;
+ usbbus_t *bus;
+
+ softc = KMALLOC(sizeof(ohci_softc_t),0);
+ if (!softc) return NULL;
+
+ bus = KMALLOC(sizeof(usbbus_t),0);
+ if (!bus) return NULL;
+
+ memset(softc,0,sizeof(ohci_softc_t));
+ memset(bus,0,sizeof(usbbus_t));
+
+ bus->ub_hwsoftc = (usb_hc_t *) softc;
+ bus->ub_hwdisp = &ohci_driver;
+
+ q_init(&(softc->ohci_rh_intrq));
+
+#ifdef _CFE_
+ softc->ohci_regs = addr;
+#else
+ softc->ohci_regs = (volatile uint32_t *) addr;
+#endif
+
+ softc->ohci_rh_newaddr = -1;
+ softc->ohci_bus = bus;
+
+ if ((res = _ohci_initpools(softc)) != 0) goto error;
+ if ((res = _ohci_setupepts(softc)) != 0) goto error;
+
+ OHCI_WRITECSR(softc,R_OHCI_CONTROL,V_OHCI_CONTROL_HCFS(K_OHCI_HCFS_RESET));
+
+ return bus;
+
+error:
+ KFREE(softc);
+ return NULL;
+}
+
+
+/* *********************************************************************
+ * ohci_ept_create(bus,usbaddr,eptnum,mps,flags)
+ *
+ * Create a hardware endpoint structure and attach it to
+ * the hardware's endpoint list. The hardware manages lists
+ * of queues, and this routine adds a new queue to the appropriate
+ * list of queues for the endpoint in question. It roughly
+ * corresponds to the information in the OHCI specification.
+ *
+ * Input parameters:
+ * bus - the USB bus we're dealing with
+ * usbaddr - USB address (0 means default address)
+ * eptnum - the endpoint number
+ * mps - the packet size for this endpoint
+ * flags - various flags to control endpoint creation
+ *
+ * Return value:
+ * endpoint structure poihter, or NULL
+ ********************************************************************* */
+
+static usb_ept_t *ohci_ept_create(usbbus_t *bus,
+ int usbaddr,
+ int eptnum,
+ int mps,
+ int flags)
+{
+ uint32_t eptflags;
+ ohci_endpoint_t *ept;
+ ohci_ed_t *ed;
+ ohci_transfer_t *tailtransfer;
+ ohci_td_t *tailtd;
+ ohci_softc_t *softc = (ohci_softc_t *) bus->ub_hwsoftc;
+
+ ept = _ohci_allocept(softc);
+ ed = ohci_ed_from_endpoint(softc,ept);
+
+ tailtransfer = _ohci_allocxfer(softc);
+ tailtd = ohci_td_from_transfer(softc,tailtransfer);
+
+ /*
+ * Set up functional address, endpoint number, and packet size
+ */
+
+ eptflags = V_OHCI_ED_FA(usbaddr) |
+ V_OHCI_ED_EN(eptnum) |
+ V_OHCI_ED_MPS(mps) |
+ 0;
+
+ /*
+ * Set up the endpoint type based on the flags
+ * passed to us
+ */
+
+ if (flags & UP_TYPE_IN) {
+ eptflags |= V_OHCI_ED_DIR(K_OHCI_ED_DIR_IN);
+ }
+ else if (flags & UP_TYPE_OUT) {
+ eptflags |= V_OHCI_ED_DIR(K_OHCI_ED_DIR_OUT);
+ }
+ else {
+ eptflags |= V_OHCI_ED_DIR(K_OHCI_ED_DIR_FROMTD);
+ }
+
+ /*
+ * Don't forget about lowspeed devices.
+ */
+
+ if (flags & UP_TYPE_LOWSPEED) {
+ eptflags |= M_OHCI_ED_LOWSPEED;
+ }
+
+ if (ohcidebug > 0) {
+ printf("Create endpoint %d addr %d flags %08X mps %d\n",
+ eptnum,usbaddr,eptflags,mps);
+ }
+
+ /*
+ * Transfer this info into the endpoint descriptor.
+ * No need to flush the cache here, it'll get done when
+ * we add to the hardware list.
+ */
+
+ ed->ed_control = BSWAP32(eptflags);
+ ed->ed_tailp = BSWAP32(OHCI_VTOP(tailtd));
+ ed->ed_headp = BSWAP32(OHCI_VTOP(tailtd));
+ ept->ep_flags = flags;
+ ept->ep_mps = mps;
+ ept->ep_num = eptnum;
+
+ /*
+ * Put it on the right queue
+ */
+
+ if (flags & UP_TYPE_CONTROL) {
+ _ohci_queueept(softc,softc->ohci_ctl_list,ept);
+ }
+ else if (flags & UP_TYPE_BULK) {
+ _ohci_queueept(softc,softc->ohci_bulk_list,ept);
+ }
+ else if (flags & UP_TYPE_INTR) {
+ /* XXX Choose place in inttable properly. */
+ _ohci_queueept(softc,softc->ohci_inttable[0],ept);
+ }
+
+ return (usb_ept_t *) ept;
+}
+
+/* *********************************************************************
+ * ohci_ept_setaddr(bus,ept,usbaddr)
+ *
+ * Change the functional address for a USB endpoint. We do this
+ * when we switch the device's state from DEFAULT to ADDRESSED
+ * and we've already got the default pipe open. This
+ * routine mucks with the descriptor and changes its address
+ * bits.
+ *
+ * Input parameters:
+ * bus - usb bus structure
+ * ept - an open endpoint descriptor
+ * usbaddr - new address for this endpoint
+ *
+ * Return value:
+ * nothing
+ ********************************************************************* */
+
+static void ohci_ept_setaddr(usbbus_t *bus,usb_ept_t *uept,int usbaddr)
+{
+ uint32_t eptflags;
+ ohci_endpoint_t *ept = (ohci_endpoint_t *) uept;
+ ohci_softc_t *softc = (ohci_softc_t *) bus->ub_hwsoftc;
+ ohci_ed_t *ed = ohci_ed_from_endpoint(softc,ept);
+
+ eptflags = BSWAP32(ed->ed_control);
+ eptflags &= ~M_OHCI_ED_FA;
+ eptflags |= V_OHCI_ED_FA(usbaddr);
+ ed->ed_control = BSWAP32(eptflags);
+}
+
+
+/* *********************************************************************
+ * ohci_ept_setmps(bus,ept,mps)
+ *
+ * Set the maximum packet size of this endpoint. This is
+ * normally used during the processing of endpoint 0 (default
+ * pipe) after we find out how big ep0's packets can be.
+ *
+ * Input parameters:
+ * bus - our USB bus structure
+ * ept - endpoint structure
+ * mps - new packet size
+ *
+ * Return value:
+ * nothing
+ ********************************************************************* */
+
+static void ohci_ept_setmps(usbbus_t *bus,usb_ept_t *uept,int mps)
+{
+ uint32_t eptflags;
+ ohci_softc_t *softc = (ohci_softc_t *) bus->ub_hwsoftc;
+ ohci_endpoint_t *ept = (ohci_endpoint_t *) uept;
+ ohci_ed_t *ed = ohci_ed_from_endpoint(softc,ept);
+
+ eptflags = BSWAP32(ed->ed_control);
+ eptflags &= ~M_OHCI_ED_MPS;
+ eptflags |= V_OHCI_ED_MPS(mps);
+ ed->ed_control = BSWAP32(eptflags);
+ ept->ep_mps = mps;
+
+}
+
+/* *********************************************************************
+ * ohci_ept_cleartoggle(bus,ept,mps)
+ *
+ * Clear the data toggle for the specified endpoint.
+ *
+ * Input parameters:
+ * bus - our USB bus structure
+ * ept - endpoint structure
+ *
+ * Return value:
+ * nothing
+ ********************************************************************* */
+
+static void ohci_ept_cleartoggle(usbbus_t *bus,usb_ept_t *uept)
+{
+ uint32_t eptflags;
+ ohci_softc_t *softc = (ohci_softc_t *) bus->ub_hwsoftc;
+ ohci_endpoint_t *ept = (ohci_endpoint_t *) uept;
+ ohci_ed_t *ed = ohci_ed_from_endpoint(softc,ept);
+
+ eptflags = BSWAP32(ed->ed_headp);
+ eptflags &= ~(M_OHCI_ED_HALT | M_OHCI_ED_TOGGLECARRY);
+ ed->ed_headp = BSWAP32(eptflags);
+
+ OHCI_WRITECSR(softc,R_OHCI_CMDSTATUS,M_OHCI_CMDSTATUS_CLF);
+}
+
+/* *********************************************************************
+ * ohci_ept_delete(bus,ept)
+ *
+ * Deletes an endpoint from the OHCI controller. This
+ * routine also completes pending transfers for the
+ * endpoint and gets rid of the hardware ept (queue base).
+ *
+ * Input parameters:
+ * bus - ohci bus structure
+ * ept - endpoint to remove
+ *
+ * Return value:
+ * nothing
+ ********************************************************************* */
+
+static void ohci_ept_delete(usbbus_t *bus,usb_ept_t *uept)
+{
+ ohci_endpoint_t *queue;
+ ohci_softc_t *softc = (ohci_softc_t *) bus->ub_hwsoftc;
+ ohci_endpoint_t *ept = (ohci_endpoint_t *) uept;
+ ohci_ed_t *ed = ohci_ed_from_endpoint(softc,ept);
+ uint32_t framenum;
+ uint32_t tdphys;
+ usbreq_t *ur;
+ ohci_td_t *td;
+ ohci_transfer_t *transfer;
+
+ if (ept->ep_flags & UP_TYPE_CONTROL) {
+ queue = softc->ohci_ctl_list;
+ }
+ else if (ept->ep_flags & UP_TYPE_BULK) {
+ queue = softc->ohci_bulk_list;
+ }
+ else if (ept->ep_flags & UP_TYPE_INTR) {
+ queue = softc->ohci_inttable[0];
+ }
+ else {
+ printf("Invalid endpoint\n");
+ return;
+ }
+
+
+ /*
+ * Set the SKIP bit on the endpoint and
+ * wait for two SOFs to guarantee that we're
+ * not processing this ED anymore.
+ */
+
+ ((volatile uint32_t) ed->ed_control) |= BSWAP32(M_OHCI_ED_SKIP);
+
+ framenum = OHCI_READCSR(softc,R_OHCI_FMNUMBER) & 0xFFFF;
+ while ((OHCI_READCSR(softc,R_OHCI_FMNUMBER) & 0xFFFF) == framenum) ; /* NULL LOOP */
+
+ framenum = OHCI_READCSR(softc,R_OHCI_FMNUMBER) & 0xFFFF;
+ while ((OHCI_READCSR(softc,R_OHCI_FMNUMBER) & 0xFFFF) == framenum) ; /* NULL LOOP */
+
+ /*
+ * Remove endpoint from queue
+ */
+
+ _ohci_deqept(softc,queue,ept);
+
+ /*
+ * Free/complete the TDs on the queue
+ */
+
+ tdphys = BSWAP32(ed->ed_headp) & M_OHCI_ED_PTRMASK;
+
+ while (tdphys != BSWAP32(ed->ed_tailp)) {
+ td = (ohci_td_t *) OHCI_PTOV(tdphys);
+ tdphys = BSWAP32(td->td_next_td);
+ transfer = ohci_transfer_from_td(softc,td);
+
+ ur = transfer->t_ref;
+ if (ur) {
+ ur->ur_status = K_OHCI_CC_CANCELLED;
+ ur->ur_tdcount--;
+ if (ur->ur_tdcount == 0) {
+ if (ohcidebug > 0) printf("Completing request due to closed pipe: %p\n",ur);
+ usb_complete_request(ur,K_OHCI_CC_CANCELLED);
+ /* XXX it is expected that the callee will free the usbreq. */
+ }
+ }
+
+ _ohci_freexfer(softc,transfer);
+ }
+
+ /*
+ * tdphys now points at the tail TD. Just free it.
+ */
+
+ td = (ohci_td_t *) OHCI_PTOV(tdphys);
+ _ohci_freexfer(softc,ohci_transfer_from_td(softc,td));
+
+ /*
+ * Return endpoint to free pool
+ */
+
+ _ohci_freeept(softc,ept);
+}
+
+
+
+/* *********************************************************************
+ * ohci_xfer(bus,ept,ur)
+ *
+ * Queue a transfer for the specified endpoint. Depending on
+ * the transfer type, the transfer may go on one of many queues.
+ * When the transfer completes, a callback will be called.
+ *
+ * Input parameters:
+ * bus - bus structure
+ * ept - endpoint descriptor
+ * ur - request (includes pointer to user buffer)
+ *
+ * Return value:
+ * 0 if ok
+ * else error
+ ********************************************************************* */
+
+static int ohci_xfer(usbbus_t *bus,usb_ept_t *uept,usbreq_t *ur)
+{
+ ohci_softc_t *softc = (ohci_softc_t *) bus->ub_hwsoftc;
+ ohci_endpoint_t *ept = (ohci_endpoint_t *) uept;
+ ohci_ed_t *ed = ohci_ed_from_endpoint(softc,ept);
+ ohci_transfer_t *newtailtransfer = 0;
+ ohci_td_t *newtailtd = NULL;
+ ohci_transfer_t *curtransfer;
+ ohci_td_t *curtd;
+ uint8_t *ptr;
+ int len;
+ int amtcopy;
+ int pktlen;
+ uint32_t tdcontrol = 0;
+
+ /*
+ * If the destination USB address matches
+ * the address of the root hub, shunt the request
+ * over to our root hub emulation.
+ */
+
+ if (ur->ur_dev->ud_address == softc->ohci_rh_addr) {
+ return ohci_roothub_xfer(bus,uept,ur);
+ }
+
+ /*
+ * Set up the TD flags based on the
+ * request type.
+ */
+
+// pktlen = ept->ep_mps;
+ pktlen = OHCI_TD_MAX_DATA - 16;
+
+ if (ur->ur_flags & UR_FLAG_SETUP) {
+ tdcontrol = V_OHCI_TD_PID(K_OHCI_TD_SETUP) |
+ V_OHCI_TD_DT(K_OHCI_TD_DT_DATA0) |
+ V_OHCI_TD_CC(K_OHCI_CC_NOTACCESSED) |
+ V_OHCI_TD_DI(1);
+ }
+ else if (ur->ur_flags & UR_FLAG_IN) {
+ tdcontrol = V_OHCI_TD_PID(K_OHCI_TD_IN) |
+ V_OHCI_TD_DT(K_OHCI_TD_DT_TCARRY) |
+ V_OHCI_TD_CC(K_OHCI_CC_NOTACCESSED) |
+ V_OHCI_TD_DI(1);
+ }
+ else if (ur->ur_flags & UR_FLAG_OUT) {
+ tdcontrol = V_OHCI_TD_PID(K_OHCI_TD_OUT) |
+ V_OHCI_TD_DT(K_OHCI_TD_DT_TCARRY) |
+ V_OHCI_TD_CC(K_OHCI_CC_NOTACCESSED) |
+ V_OHCI_TD_DI(1);
+ }
+ else if (ur->ur_flags & UR_FLAG_STATUS_OUT) {
+ tdcontrol = V_OHCI_TD_PID(K_OHCI_TD_OUT) |
+ V_OHCI_TD_DT(K_OHCI_TD_DT_DATA1) |
+ V_OHCI_TD_CC(K_OHCI_CC_NOTACCESSED) |
+ V_OHCI_TD_DI(1);
+ }
+ else if (ur->ur_flags & UR_FLAG_STATUS_IN) {
+ tdcontrol = V_OHCI_TD_PID(K_OHCI_TD_IN) |
+ V_OHCI_TD_DT(K_OHCI_TD_DT_DATA1) |
+ V_OHCI_TD_CC(K_OHCI_CC_NOTACCESSED) |
+ V_OHCI_TD_DI(1);
+ }
+ else {
+ printf("Shouldn't happen!\n");
+ }
+
+ if (ur->ur_flags & UR_FLAG_SHORTOK) {
+ tdcontrol |= M_OHCI_TD_SHORTOK;
+ }
+
+
+ ptr = ur->ur_buffer;
+ len = ur->ur_length;
+ ur->ur_tdcount = 0;
+
+ if (ohcidebug > 1) {
+ printf(">> Queueing xfer addr %d pipe %d ED %08X ptr %016llX length %d\n",
+ ur->ur_dev->ud_address,
+ ur->ur_pipe->up_num,
+ ept->ep_phys,
+ (uint64_t) (uintptr_t) ptr,
+ len);
+// ohci_dumped(ed);
+ }
+
+ curtd = OHCI_PTOV(BSWAP32(ed->ed_tailp));
+ curtransfer = ohci_transfer_from_td(softc,curtd);
+
+ if (len == 0) {
+ newtailtransfer = _ohci_allocxfer(softc);
+ newtailtd = ohci_td_from_transfer(softc,newtailtransfer);
+ curtd->td_cbp = 0;
+ curtd->td_be = 0;
+ curtd->td_next_td = BSWAP32(OHCI_VTOP(newtailtd));
+ curtd->td_control = BSWAP32(tdcontrol);
+ curtransfer->t_next = newtailtransfer;
+ curtransfer->t_ref = ur;
+ curtransfer->t_length = 0;
+ if (ohcidebug > 1) { printf("QueueTD: "); ohci_dumptd(curtd); }
+ ur->ur_tdcount++;
+ }
+ else {
+ /* Noncoherent DMA: need to flush user buffer to real memory first */
+ OHCI_FLUSH_RANGE(ptr,len);
+ while (len > 0) {
+ amtcopy = len;
+ if (amtcopy > pktlen) amtcopy = pktlen;
+ newtailtransfer = _ohci_allocxfer(softc);
+ newtailtd = ohci_td_from_transfer(softc,newtailtransfer);
+ curtd->td_cbp = BSWAP32(OHCI_VTOP(ptr));
+ curtd->td_be = BSWAP32(OHCI_VTOP(ptr+amtcopy)-1);
+ curtd->td_next_td = BSWAP32(OHCI_VTOP(newtailtd));
+ curtd->td_control = BSWAP32(tdcontrol);
+ curtransfer->t_next = newtailtransfer;
+ curtransfer->t_ref = ur;
+ curtransfer->t_length = amtcopy;
+ if (ohcidebug > 1) { printf("QueueTD: "); ohci_dumptd(curtd); }
+ curtd = newtailtd;
+ curtransfer = ohci_transfer_from_td(softc,curtd);
+ ptr += amtcopy;
+ len -= amtcopy;
+ ur->ur_tdcount++;
+ }
+ }
+
+ curtd = OHCI_PTOV(BSWAP32(ed->ed_headp & M_OHCI_ED_PTRMASK));
+ ed->ed_tailp = BSWAP32(OHCI_VTOP(newtailtd));
+
+ /*
+ * Prod the controller depending on what type of list we put
+ * a TD on.
+ */
+
+ if (ept->ep_flags & UP_TYPE_BULK) {
+ OHCI_WRITECSR(softc,R_OHCI_CMDSTATUS,M_OHCI_CMDSTATUS_BLF);
+ }
+ else {
+ /* XXX should probably make sure we're UP_TYPE_CONTROL here */
+ OHCI_WRITECSR(softc,R_OHCI_CMDSTATUS,M_OHCI_CMDSTATUS_CLF);
+ }
+
+ return 0;
+}
+
+/* *********************************************************************
+ * Driver structure
+ ********************************************************************* */
+
+usb_hcdrv_t ohci_driver = {
+ ohci_create,
+ ohci_delete,
+ ohci_start,
+ ohci_stop,
+ ohci_intr,
+ ohci_ept_create,
+ ohci_ept_delete,
+ ohci_ept_setmps,
+ ohci_ept_setaddr,
+ ohci_ept_cleartoggle,
+ ohci_xfer
+};
+
+/* *********************************************************************
+ * Root Hub
+ *
+ * Data structures and functions
+ ********************************************************************* */
+
+/*
+ * Data structures and routines to emulate the root hub.
+ */
+static usb_device_descr_t ohci_root_devdsc = {
+ sizeof(usb_device_descr_t), /* bLength */
+ USB_DEVICE_DESCRIPTOR_TYPE, /* bDescriptorType */
+ USBWORD(0x0100), /* bcdUSB */
+ USB_DEVICE_CLASS_HUB, /* bDeviceClass */
+ 0, /* bDeviceSubClass */
+ 0, /* bDeviceProtocol */
+ 64, /* bMaxPacketSize0 */
+ USBWORD(0), /* idVendor */
+ USBWORD(0), /* idProduct */
+ USBWORD(0x0100), /* bcdDevice */
+ 1, /* iManufacturer */
+ 2, /* iProduct */
+ 0, /* iSerialNumber */
+ 1 /* bNumConfigurations */
+};
+
+static usb_config_descr_t ohci_root_cfgdsc = {
+ sizeof(usb_config_descr_t), /* bLength */
+ USB_CONFIGURATION_DESCRIPTOR_TYPE, /* bDescriptorType */
+ USBWORD(
+ sizeof(usb_config_descr_t) +
+ sizeof(usb_interface_descr_t) +
+ sizeof(usb_endpoint_descr_t)), /* wTotalLength */
+ 1, /* bNumInterfaces */
+ 1, /* bConfigurationValue */
+ 0, /* iConfiguration */
+ USB_CONFIG_SELF_POWERED, /* bmAttributes */
+ 0 /* MaxPower */
+};
+
+static usb_interface_descr_t ohci_root_ifdsc = {
+ sizeof(usb_interface_descr_t), /* bLength */
+ USB_INTERFACE_DESCRIPTOR_TYPE, /* bDescriptorType */
+ 0, /* bInterfaceNumber */
+ 0, /* bAlternateSetting */
+ 1, /* bNumEndpoints */
+ USB_INTERFACE_CLASS_HUB, /* bInterfaceClass */
+ 0, /* bInterfaceSubClass */
+ 0, /* bInterfaceProtocol */
+ 0 /* iInterface */
+};
+
+static usb_endpoint_descr_t ohci_root_epdsc = {
+ sizeof(usb_endpoint_descr_t), /* bLength */
+ USB_ENDPOINT_DESCRIPTOR_TYPE, /* bDescriptorType */
+ (USB_ENDPOINT_DIRECTION_IN | 1), /* bEndpointAddress */
+ USB_ENDPOINT_TYPE_INTERRUPT, /* bmAttributes */
+ USBWORD(8), /* wMaxPacketSize */
+ 255 /* bInterval */
+};
+
+static usb_hub_descr_t ohci_root_hubdsc = {
+ USB_HUB_DESCR_SIZE, /* bLength */
+ USB_HUB_DESCRIPTOR_TYPE, /* bDescriptorType */
+ 0, /* bNumberOfPorts */
+ USBWORD(0), /* wHubCharacteristics */
+ 0, /* bPowreOnToPowerGood */
+ 0, /* bHubControl Current */
+ {0} /* bRemoveAndPowerMask */
+};
+
+/* *********************************************************************
+ * ohci_roothb_strdscr(ptr,str)
+ *
+ * Construct a string descriptor for root hub requests
+ *
+ * Input parameters:
+ * ptr - pointer to where to put descriptor
+ * str - regular string to put into descriptor
+ *
+ * Return value:
+ * number of bytes written to descriptor
+ ********************************************************************* */
+
+static int ohci_roothub_strdscr(uint8_t *ptr,char *str)
+{
+ uint8_t *p = ptr;
+
+ *p++ = strlen(str)*2 + 2; /* Unicode strings */
+ *p++ = USB_STRING_DESCRIPTOR_TYPE;
+ while (*str) {
+ *p++ = *str++;
+ *p++ = 0;
+ }
+ return (p - ptr);
+}
+
+/* *********************************************************************
+ * ohci_roothub_req(softc,req)
+ *
+ * Handle a descriptor request on the control pipe for the
+ * root hub. We pretend to be a real root hub here and
+ * return all the standard descriptors.
+ *
+ * Input parameters:
+ * softc - our OHCI controller
+ * req - a usb request (completed immediately)
+ *
+ * Return value:
+ * 0 if ok
+ * else error code
+ ********************************************************************* */
+
+static int ohci_roothub_req(ohci_softc_t *softc,usb_device_request_t *req)
+{
+ uint8_t *ptr;
+ uint16_t wLength;
+ uint16_t wValue;
+ uint16_t wIndex;
+ usb_port_status_t ups;
+ usb_hub_descr_t hdsc;
+ uint32_t status;
+ uint32_t statport;
+ uint32_t tmpval;
+ int res = 0;
+
+ ptr = softc->ohci_rh_buf;
+
+ wLength = GETUSBFIELD(req,wLength);
+ wValue = GETUSBFIELD(req,wValue);
+ wIndex = GETUSBFIELD(req,wIndex);
+
+ switch (REQSW(req->bRequest,req->bmRequestType)) {
+
+ case REQCODE(USB_REQUEST_GET_STATUS,USBREQ_DIR_IN,USBREQ_TYPE_STD,USBREQ_REC_DEVICE):
+ *ptr++ = (USB_GETSTATUS_SELF_POWERED & 0xFF);
+ *ptr++ = (USB_GETSTATUS_SELF_POWERED >> 8);
+ break;
+
+ case REQCODE(USB_REQUEST_GET_STATUS,USBREQ_DIR_IN,USBREQ_TYPE_STD,USBREQ_REC_ENDPOINT):
+ case REQCODE(USB_REQUEST_GET_STATUS,USBREQ_DIR_IN,USBREQ_TYPE_STD,USBREQ_REC_INTERFACE):
+ *ptr++ = 0;
+ *ptr++ = 0;
+ break;
+
+ case REQCODE(USB_REQUEST_GET_STATUS,USBREQ_DIR_IN,USBREQ_TYPE_CLASS,USBREQ_REC_OTHER):
+ status = OHCI_READCSR(softc,(R_OHCI_RHPORTSTATUS(wIndex)));
+ if (ohcidebug > 0) { printf("RHGetStatus: "); ohci_dumpportstat(wIndex,status);}
+ PUTUSBFIELD((&ups),wPortStatus,(status & 0xFFFF));
+ PUTUSBFIELD((&ups),wPortChange,(status >> 16));
+ memcpy(ptr,&ups,sizeof(ups));
+ ptr += sizeof(ups);
+ break;
+
+ case REQCODE(USB_REQUEST_GET_STATUS,USBREQ_DIR_IN,USBREQ_TYPE_CLASS,USBREQ_REC_DEVICE):
+ *ptr++ = 0;
+ *ptr++ = 0;
+ *ptr++ = 0;
+ *ptr++ = 0;
+ break;
+
+ case REQCODE(USB_REQUEST_CLEAR_FEATURE,USBREQ_DIR_OUT,USBREQ_TYPE_STD,USBREQ_REC_DEVICE):
+ case REQCODE(USB_REQUEST_CLEAR_FEATURE,USBREQ_DIR_OUT,USBREQ_TYPE_STD,USBREQ_REC_INTERFACE):
+ case REQCODE(USB_REQUEST_CLEAR_FEATURE,USBREQ_DIR_OUT,USBREQ_TYPE_STD,USBREQ_REC_ENDPOINT):
+ /* do nothing, not supported */
+ break;
+
+ case REQCODE(USB_REQUEST_CLEAR_FEATURE,USBREQ_DIR_OUT,USBREQ_TYPE_CLASS,USBREQ_REC_OTHER):
+ statport = R_OHCI_RHPORTSTATUS(wIndex);
+ if (ohcidebug> 0) {
+ printf("RHClearFeature(%d): ",wValue); ohci_dumpportstat(wIndex,OHCI_READCSR(softc,statport));
+ }
+ switch (wValue) {
+ case USB_PORT_FEATURE_CONNECTION:
+ break;
+ case USB_PORT_FEATURE_ENABLE:
+ OHCI_WRITECSR(softc,statport,M_OHCI_RHPORTSTAT_CCS);
+ break;
+ case USB_PORT_FEATURE_SUSPEND:
+ OHCI_WRITECSR(softc,statport,M_OHCI_RHPORTSTAT_POCI);
+ break;
+ case USB_PORT_FEATURE_OVER_CURRENT:
+ break;
+ case USB_PORT_FEATURE_RESET:
+ break;
+ case USB_PORT_FEATURE_POWER:
+ OHCI_WRITECSR(softc,statport,M_OHCI_RHPORTSTAT_LSDA);
+ break;
+ case USB_PORT_FEATURE_LOW_SPEED:
+ break;
+ case USB_PORT_FEATURE_C_PORT_CONNECTION:
+ OHCI_WRITECSR(softc,statport,M_OHCI_RHPORTSTAT_CSC);
+ break;
+ case USB_PORT_FEATURE_C_PORT_ENABLE:
+ OHCI_WRITECSR(softc,statport,M_OHCI_RHPORTSTAT_PESC);
+ break;
+ case USB_PORT_FEATURE_C_PORT_SUSPEND:
+ OHCI_WRITECSR(softc,statport,M_OHCI_RHPORTSTAT_PSSC);
+ break;
+ case USB_PORT_FEATURE_C_PORT_OVER_CURRENT:
+ OHCI_WRITECSR(softc,statport,M_OHCI_RHPORTSTAT_OCIC);
+ break;
+ case USB_PORT_FEATURE_C_PORT_RESET:
+ OHCI_WRITECSR(softc,statport,M_OHCI_RHPORTSTAT_PRSC);
+ break;
+
+ }
+
+ /*
+ * If we've cleared all of the conditions that
+ * want our attention on the port status,
+ * then we can accept port status interrupts again.
+ */
+
+ if ((wValue >= USB_PORT_FEATURE_C_PORT_CONNECTION) &&
+ (wValue <= USB_PORT_FEATURE_C_PORT_RESET)) {
+ status = OHCI_READCSR(softc,statport);
+ if ((status & M_OHCI_RHPORTSTAT_ALLC) == 0) {
+ softc->ohci_intdisable &= ~M_OHCI_INT_RHSC;
+ }
+ }
+ break;
+
+ case REQCODE(USB_REQUEST_SET_FEATURE,USBREQ_DIR_OUT,USBREQ_TYPE_STD,USBREQ_REC_DEVICE):
+ case REQCODE(USB_REQUEST_SET_FEATURE,USBREQ_DIR_OUT,USBREQ_TYPE_STD,USBREQ_REC_INTERFACE):
+ case REQCODE(USB_REQUEST_SET_FEATURE,USBREQ_DIR_OUT,USBREQ_TYPE_STD,USBREQ_REC_ENDPOINT):
+ res = -1;
+ break;
+
+ case REQCODE(USB_REQUEST_SET_FEATURE,USBREQ_DIR_OUT,USBREQ_TYPE_CLASS,USBREQ_REC_DEVICE):
+ /* nothing */
+ break;
+
+ case REQCODE(USB_REQUEST_SET_FEATURE,USBREQ_DIR_OUT,USBREQ_TYPE_CLASS,USBREQ_REC_OTHER):
+ statport = R_OHCI_RHPORTSTATUS(wIndex);
+ switch (wValue) {
+ case USB_PORT_FEATURE_CONNECTION:
+ break;
+ case USB_PORT_FEATURE_ENABLE:
+ OHCI_WRITECSR(softc,statport,M_OHCI_RHPORTSTAT_PES);
+ break;
+ case USB_PORT_FEATURE_SUSPEND:
+ OHCI_WRITECSR(softc,statport,M_OHCI_RHPORTSTAT_PSS);
+ break;
+ case USB_PORT_FEATURE_OVER_CURRENT:
+ break;
+ case USB_PORT_FEATURE_RESET:
+ OHCI_WRITECSR(softc,statport,M_OHCI_RHPORTSTAT_PRS);
+ for (;;) { /* XXX timer */
+ usb_delay_ms(softc->ohci_bus,100);
+ if (!(OHCI_READCSR(softc,statport) & M_OHCI_RHPORTSTAT_PRS)) break;
+ }
+ break;
+ case USB_PORT_FEATURE_POWER:
+ OHCI_WRITECSR(softc,statport,M_OHCI_RHPORTSTAT_PPS);
+ break;
+ case USB_PORT_FEATURE_LOW_SPEED:
+ break;
+ case USB_PORT_FEATURE_C_PORT_CONNECTION:
+ break;
+ case USB_PORT_FEATURE_C_PORT_ENABLE:
+ break;
+ case USB_PORT_FEATURE_C_PORT_SUSPEND:
+ break;
+ case USB_PORT_FEATURE_C_PORT_OVER_CURRENT:
+ break;
+ case USB_PORT_FEATURE_C_PORT_RESET:
+ break;
+
+ }
+
+ break;
+
+ case REQCODE(USB_REQUEST_SET_ADDRESS,USBREQ_DIR_OUT,USBREQ_TYPE_STD,USBREQ_REC_DEVICE):
+ softc->ohci_rh_newaddr = wValue;
+ break;
+
+ case REQCODE(USB_REQUEST_GET_DESCRIPTOR,USBREQ_DIR_IN,USBREQ_TYPE_STD,USBREQ_REC_DEVICE):
+ switch (wValue >> 8) {
+ case USB_DEVICE_DESCRIPTOR_TYPE:
+ memcpy(ptr,&ohci_root_devdsc,sizeof(ohci_root_devdsc));
+ ptr += sizeof(ohci_root_devdsc);
+ break;
+ case USB_CONFIGURATION_DESCRIPTOR_TYPE:
+ memcpy(ptr,&ohci_root_cfgdsc,sizeof(ohci_root_cfgdsc));
+ ptr += sizeof(ohci_root_cfgdsc);
+ memcpy(ptr,&ohci_root_ifdsc,sizeof(ohci_root_ifdsc));
+ ptr += sizeof(ohci_root_ifdsc);
+ memcpy(ptr,&ohci_root_epdsc,sizeof(ohci_root_epdsc));
+ ptr += sizeof(ohci_root_epdsc);
+ break;
+ case USB_STRING_DESCRIPTOR_TYPE:
+ switch (wValue & 0xFF) {
+ case 1:
+ ptr += ohci_roothub_strdscr(ptr,"Generic");
+ break;
+ case 2:
+ ptr += ohci_roothub_strdscr(ptr,"Root Hub");
+ break;
+ default:
+ *ptr++ = 0;
+ break;
+ }
+ break;
+ default:
+ res = -1;
+ break;
+ }
+ break;
+
+ case REQCODE(USB_REQUEST_GET_DESCRIPTOR,USBREQ_DIR_IN,USBREQ_TYPE_CLASS,USBREQ_REC_DEVICE):
+ memcpy(&hdsc,&ohci_root_hubdsc,sizeof(hdsc));
+ hdsc.bNumberOfPorts = softc->ohci_ndp;
+ status = OHCI_READCSR(softc,R_OHCI_RHDSCRA);
+ tmpval = 0;
+ if (status & M_OHCI_RHDSCRA_NPS) tmpval |= USB_HUBCHAR_PWR_NONE;
+ if (status & M_OHCI_RHDSCRA_PSM) tmpval |= USB_HUBCHAR_PWR_GANGED;
+ else tmpval |= USB_HUBCHAR_PWR_IND;
+ PUTUSBFIELD((&hdsc),wHubCharacteristics,tmpval);
+ tmpval = G_OHCI_RHDSCRA_POTPGT(status);
+ hdsc.bPowerOnToPowerGood = tmpval;
+ hdsc.bDescriptorLength = USB_HUB_DESCR_SIZE + 1;
+ status = OHCI_READCSR(softc,R_OHCI_RHDSCRB);
+ hdsc.bRemoveAndPowerMask[0] = (uint8_t) status;
+ memcpy(ptr,&hdsc,sizeof(hdsc));
+ ptr += sizeof(hdsc);
+ break;
+
+ case REQCODE(USB_REQUEST_SET_DESCRIPTOR,USBREQ_DIR_OUT,USBREQ_TYPE_CLASS,USBREQ_REC_DEVICE):
+ /* nothing */
+ break;
+
+ case REQCODE(USB_REQUEST_GET_CONFIGURATION,USBREQ_DIR_IN,USBREQ_TYPE_STD,USBREQ_REC_DEVICE):
+ *ptr++ = softc->ohci_rh_conf;
+ break;
+
+ case REQCODE(USB_REQUEST_SET_CONFIGURATION,USBREQ_DIR_OUT,USBREQ_TYPE_STD,USBREQ_REC_DEVICE):
+ softc->ohci_rh_conf = wValue;
+ break;
+
+ case REQCODE(USB_REQUEST_GET_INTERFACE,USBREQ_DIR_IN,USBREQ_TYPE_STD,USBREQ_REC_INTERFACE):
+ *ptr++ = 0;
+ break;
+
+ case REQCODE(USB_REQUEST_SET_INTERFACE,USBREQ_DIR_OUT,USBREQ_TYPE_STD,USBREQ_REC_INTERFACE):
+ /* nothing */
+ break;
+
+ case REQCODE(USB_REQUEST_SYNC_FRAME,USBREQ_DIR_OUT,USBREQ_TYPE_STD,USBREQ_REC_ENDPOINT):
+ /* nothing */
+ break;
+ }
+
+ softc->ohci_rh_ptr = softc->ohci_rh_buf;
+ softc->ohci_rh_len = ptr - softc->ohci_rh_buf;
+
+ return res;
+}
+
+/* *********************************************************************
+ * ohci_roothub_statchg(softc)
+ *
+ * This routine is called from the interrupt service routine
+ * (well, polling routine) for the ohci controller. If the
+ * controller notices a root hub status change, it dequeues an
+ * interrupt transfer from the root hub's queue and completes
+ * it here.
+ *
+ * Input parameters:
+ * softc - our OHCI controller
+ *
+ * Return value:
+ * nothing
+ ********************************************************************* */
+
+static void ohci_roothub_statchg(ohci_softc_t *softc)
+{
+ usbreq_t *ur;
+ uint32_t status;
+ uint8_t portstat = 0;
+ int idx;
+
+ /* Note: this only works up to 8 ports */
+ for (idx = 1; idx <= softc->ohci_ndp; idx++) {
+ status = OHCI_READCSR(softc,R_OHCI_RHPORTSTATUS(idx));
+ if (status & M_OHCI_RHPORTSTAT_ALLC) {
+ portstat = (1<<idx);
+ }
+ }
+
+ if (portstat != 0) {
+ softc->ohci_intdisable |= M_OHCI_INT_RHSC;
+ }
+
+ ur = (usbreq_t *) q_deqnext(&(softc->ohci_rh_intrq));
+ if (!ur) return; /* no requests pending, ignore it */
+
+ memset(ur->ur_buffer,0,ur->ur_length);
+ ur->ur_buffer[0] = portstat;
+ ur->ur_xferred = ur->ur_length;
+
+ usb_complete_request(ur,0);
+}
+
+/* *********************************************************************
+ * ohci_roothub_xfer(softc,req)
+ *
+ * Handle a root hub xfer - ohci_xfer transfers control here
+ * if we detect the address of the root hub - no actual transfers
+ * go out on the wire, we just handle the requests directly to
+ * make it look like a hub is attached.
+ *
+ * This seems to be common practice in the USB world, so we do
+ * it here too.
+ *
+ * Input parameters:
+ * softc - our OHCI controller structure
+ * req - usb request destined for host controller
+ *
+ * Return value:
+ * 0 if ok
+ * else error
+ ********************************************************************* */
+
+static int ohci_roothub_xfer(usbbus_t *bus,usb_ept_t *uept,usbreq_t *ur)
+{
+ ohci_softc_t *softc = (ohci_softc_t *) bus->ub_hwsoftc;
+ ohci_endpoint_t *ept = (ohci_endpoint_t *) uept;
+ int res;
+
+ switch (ept->ep_num) {
+
+ /*
+ * CONTROL ENDPOINT
+ */
+ case 0:
+
+ /*
+ * Three types of transfers: OUT (SETUP), IN (data), or STATUS.
+ * figure out which is which.
+ */
+
+ if (ur->ur_flags & UR_FLAG_SETUP) {
+ /*
+ * SETUP packet - this is an OUT request to the control
+ * pipe. We emulate the hub request here.
+ */
+ usb_device_request_t *req;
+
+ req = (usb_device_request_t *) ur->ur_buffer;
+
+ res = ohci_roothub_req(softc,req);
+ if (res != 0) printf("Root hub request returned an error\n");
+
+ ur->ur_xferred = ur->ur_length;
+ ur->ur_status = 0;
+ usb_complete_request(ur,0);
+ }
+
+ else if (ur->ur_flags & UR_FLAG_STATUS_IN) {
+ /*
+ * STATUS IN : it's sort of like a dummy IN request
+ * to acknowledge a SETUP packet that otherwise has no
+ * status. Just complete the usbreq.
+ */
+
+ if (softc->ohci_rh_newaddr != -1) {
+ softc->ohci_rh_addr = softc->ohci_rh_newaddr;
+ softc->ohci_rh_newaddr = -1;
+ }
+
+ ur->ur_status = 0;
+ ur->ur_xferred = 0;
+ usb_complete_request(ur,0);
+ }
+
+ else if (ur->ur_flags & UR_FLAG_STATUS_OUT) {
+ /*
+ * STATUS OUT : it's sort of like a dummy OUT request
+ */
+ ur->ur_status = 0;
+ ur->ur_xferred = 0;
+ usb_complete_request(ur,0);
+ }
+
+ else if (ur->ur_flags & UR_FLAG_IN) {
+ /*
+ * IN : return data from the root hub
+ */
+ int amtcopy;
+
+ amtcopy = softc->ohci_rh_len;
+ if (amtcopy > ur->ur_length) amtcopy = ur->ur_length;
+
+ memcpy(ur->ur_buffer,softc->ohci_rh_ptr,amtcopy);
+
+ softc->ohci_rh_ptr += amtcopy;
+ softc->ohci_rh_len -= amtcopy;
+
+ ur->ur_status = 0;
+ ur->ur_xferred = amtcopy;
+ usb_complete_request(ur,0);
+ }
+
+ else {
+ printf("Unknown root hub transfer type\n");
+ return -1;
+ }
+ break;
+
+ /*
+ * INTERRUPT ENDPOINT
+ */
+
+ case 1: /* interrupt pipe */
+ if (ur->ur_flags & UR_FLAG_IN) {
+ q_enqueue(&(softc->ohci_rh_intrq),(queue_t *) ur);
+ }
+ break;
+
+ }
+
+
+ return 0;
+}