diff options
Diffstat (limited to 'cfe/cfe/usb')
-rw-r--r-- | cfe/cfe/usb/Makefile | 8 | ||||
-rw-r--r-- | cfe/cfe/usb/README | 183 | ||||
-rw-r--r-- | cfe/cfe/usb/ohci.c | 2126 | ||||
-rw-r--r-- | cfe/cfe/usb/ohci.h | 490 | ||||
-rw-r--r-- | cfe/cfe/usb/usbchap9.h | 389 | ||||
-rw-r--r-- | cfe/cfe/usb/usbd.c | 1215 | ||||
-rw-r--r-- | cfe/cfe/usb/usbd.h | 308 | ||||
-rw-r--r-- | cfe/cfe/usb/usbdebug.c | 307 | ||||
-rw-r--r-- | cfe/cfe/usb/usbdevs.c | 168 | ||||
-rw-r--r-- | cfe/cfe/usb/usbeth.c | 850 | ||||
-rw-r--r-- | cfe/cfe/usb/usbeth.h | 112 | ||||
-rw-r--r-- | cfe/cfe/usb/usbhack.c | 528 | ||||
-rw-r--r-- | cfe/cfe/usb/usbhack.h | 66 | ||||
-rw-r--r-- | cfe/cfe/usb/usbhack.mk | 23 | ||||
-rw-r--r-- | cfe/cfe/usb/usbhid.c | 664 | ||||
-rw-r--r-- | cfe/cfe/usb/usbhub.c | 912 | ||||
-rw-r--r-- | cfe/cfe/usb/usbmain.c | 367 | ||||
-rw-r--r-- | cfe/cfe/usb/usbmass.c | 1199 | ||||
-rw-r--r-- | cfe/cfe/usb/usbserial.c | 713 |
19 files changed, 10628 insertions, 0 deletions
diff --git a/cfe/cfe/usb/Makefile b/cfe/cfe/usb/Makefile new file mode 100644 index 0000000..fa38870 --- /dev/null +++ b/cfe/cfe/usb/Makefile @@ -0,0 +1,8 @@ + +# +# Makefile for USB stuff +# + + +ALLOBJS += usbmain.o ohci.o usbd.o usbdevs.o usbhub.o usbdebug.o usbhid.o usbmass.o usbserial.o usbeth.o +CFLAGS += -DCFG_USB=1 diff --git a/cfe/cfe/usb/README b/cfe/cfe/usb/README new file mode 100644 index 0000000..28ac9bf --- /dev/null +++ b/cfe/cfe/usb/README @@ -0,0 +1,183 @@ + +------------------------------------------------------------------------ +This directory contains a basic description of the CFE USB stack, +its current status and features, and what might be done in the +future to improve it. +------------------------------------------------------------------------ + +Question: A USB stack in CFE? But why? + +Answer: Why not? It's not terribly useful on the BCM1250, since we + don't expect many of you to use USB in your boards, but there IS + a USB host controller on the SWARM (BCM1250 reference design). + Besides, CFE is actually being used for other non-SiByte + Broadcom chips, and some of those _do_ have USB on them. + +------------------------------------------------------------------------ + +Source Files +------------ + +ohci.c OHCI USB Host Controller Driver, tested on a BCM1250 + with an Opti FireLink PCI USB controller + +ohci.h Register definitions for ohci.c + +usbchap9.h USB "Chapter 9" definitions (Descriptors, etc.) + +usbd.c USB Basic Request and pipe management routines, to + manage usb devices, do simple requests on the + control pipe, open and manage pipes, etc. + +usbd.h Prototypes and data structures for usbd.c + +usbdevs.c USB Device Driver list - devices we recognize + are listed here - if you add a new USB device, + you can add its class or vendor ID entries + into the table here. + +usbdebug.c Some descriptor dump routines live here. + +usbhub.c Class driver for USB hubs. Because hubs are also + a major player in device discovery, much of the + USB device tree management also lives here. + +usbhid.c Class driver for keyboards and mice. Right now + not much is done with them except echo characters. + +usbmass.c Class driver for USB mass-storage devices. We only + support the "bulk-only-no-interrupt" protocol. + This driver also includes a top half so that + it can be accessed as a CFE mass-storage device. + +usbmain.c Top-level interface into CFE. The "usb start" + command is instantiated here, as well as a + "usb show" command to display attached USB devices. + +usbhack.c Main program for the test harness, which lets you + develop OHCI code under Linux without hacking on + either CFE or the kernel. See the comments in this + file for more information. + +usbhack.h A dumping ground for CFE definitions not otherwise + provided by the standard include files. + +usbhack.mk GNU makefile for the test harness + +------------------------------------------------------------------------ + +Overview +-------- + +The host controller driver is abstracted through a small set of +primitives defined in usbd.h - at present only the OHCI driver +is implemented, but there will eventually be support for the +ScanLogic SL11H part on the BCM1250CPCI board - this is a simple +"generic-bus" (non-pci) host controller. I doubt we'll ever +need EHCI/UHCI, since they are present mostly in Intel chipsets. + +All events are polled by this driver. There are two polling functions +that should be called periodically: + + usb_poll(usbbus_t *bus); + usb_daemon(usbbus_t *bus); + +The "poll" routine handles interrupts from the devices themselves. +The "daemon" routine monitors the bus for topology changes and +instantiates an exploration if something changes. Sometimes "daemon" +needs to do USB I/O, requiring calls to usb_poll() to get the data +to go in/out via the controller, hence the two routines. You should +be careful not to all usb_poll() during polling. + + +Device Drivers +-------------- + +USB Device drivers are currently extremely simple: There are +only two methods that need be exported to the device driver table: + +attach() Called when the device is "discovered" +detach() Called when the device is "removed" + +When a device is removed, pending transfer requests will be +canceled with a "canceled" status. + +There is no standard for the top side (user API side) of the +device driver, that is up to the device class. The bottom half +should make use of the calls in usbd.c + +When a device driver is attached via its attach() method, +it will be in the "addressed" state according to the USB spec. +The exploration code takes care of assigning the USB address +to the device. Devices not otherwise recognized by this code will +be left in the addressed state without any active configurations. + +The descriptors are read by the exploration code and are made +available to the usb_find_cfg_descr() call - you can use this +function to obtain the endpoint and interface descriptors for +your device and then call usb_set_configuration() to activate +the configuration. + +When your detach() method is called, the device should be considered +already gone, so do not attempt to do any I/O to it. Just clean +up the mess and return. + + +------------------------------------------------------------------------ + +What works? +----------- + +* OHCI on a BCM1250 via the Opti Firelink USB controller + +* The OHCI root hub emulation + +* External hubs, and hubs integrated into other devices like + keyboards. + +* Interrupt transfers + +* Transfers (basic requests) on endpoint 0 + +* Basic device discovery and removal + +* Bulk endpoints and transfers + +* Some endpoint stalls are handled. + + +------------------------------------------------------------------------ + +What doesn't work? What is not implemented? +-------------------------------------------- + +* The root hub implementation is really shaky, especially in + matters of plug-and-play (device insertion/removal events, + etc.) Don't be surprised if removing a device from the + root hub causes CFE to freeze. + +* There is no error recovery code whatsoever. This kind of goes + with the above root hub issue. + +* Noncoherent DMA is relatively untested. + +* Isochronous endpoints are completely unimplemented (and will probably + remain that way) + +* Power management (for example, power budget in hubs) is unimplemented. + (this should be easy) + +* Interrupt endpoints are all on the 10ms endpoint in the interrupt + tree (endpoints should be placed at the location to guarantee + bandwidth at 'bInterval' ms) - no bandwidth management is being + done at the moment, but this is pretty simple. + +* The OHCI driver cannot be stopped/unloaded. + + + + + + + + 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; +} diff --git a/cfe/cfe/usb/ohci.h b/cfe/cfe/usb/ohci.h new file mode 100644 index 0000000..06368e5 --- /dev/null +++ b/cfe/cfe/usb/ohci.h @@ -0,0 +1,490 @@ +/* ********************************************************************* + * Broadcom Common Firmware Environment (CFE) + * + * OHCI defs File: ohci.h + * + * Open Host controller interface definitions + * + * 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. + ********************************************************************* */ + + +/* ********************************************************************* + * Macros to muck with bitfields + ********************************************************************* */ + +#define _OHCI_MAKE32(x) ((uint32_t)(x)) + +/* + * Make a mask for 1 bit at position 'n' + */ + +#define _OHCI_MAKEMASK1(n) (_OHCI_MAKE32(1) << _OHCI_MAKE32(n)) + +/* + * Make a mask for 'v' bits at position 'n' + */ + +#define _OHCI_MAKEMASK(v,n) (_OHCI_MAKE32((_OHCI_MAKE32(1)<<(v))-1) << _OHCI_MAKE32(n)) + +/* + * Make a value at 'v' at bit position 'n' + */ + +#define _OHCI_MAKEVALUE(v,n) (_OHCI_MAKE32(v) << _OHCI_MAKE32(n)) +#define _OHCI_GETVALUE(v,n,m) ((_OHCI_MAKE32(v) & _OHCI_MAKE32(m)) >> _OHCI_MAKE32(n)) + + + +/* ********************************************************************* + * Endpoint Descriptor (interrupt, bulk) + ********************************************************************* */ + +#define OHCI_ED_ALIGN 32 + +typedef struct ohci_ed_s { + uint32_t ed_control; + uint32_t ed_tailp; + uint32_t ed_headp; + uint32_t ed_next_ed; +} ohci_ed_t; + +#define S_OHCI_ED_FA 0 +#define M_OHCI_ED_FA _OHCI_MAKEMASK(7,S_OHCI_ED_FA) +#define V_OHCI_ED_FA(x) _OHCI_MAKEVALUE(x,S_OHCI_ED_FA) +#define G_OHCI_ED_FA(x) _OHCI_GETVALUE(x,S_OHCI_ED_FA,M_OHCI_ED_FA) + +#define S_OHCI_ED_EN 7 +#define M_OHCI_ED_EN _OHCI_MAKEMASK(4,S_OHCI_ED_EN) +#define V_OHCI_ED_EN(x) _OHCI_MAKEVALUE(x,S_OHCI_ED_EN) +#define G_OHCI_ED_EN(x) _OHCI_GETVALUE(x,S_OHCI_ED_EN,M_OHCI_ED_EN) + +#define S_OHCI_ED_DIR 11 +#define M_OHCI_ED_DIR _OHCI_MAKEMASK(2,S_OHCI_ED_DIR) +#define V_OHCI_ED_DIR(x) _OHCI_MAKEVALUE(x,S_OHCI_ED_DIR) +#define G_OHCI_ED_DIR(x) _OHCI_GETVALUE(x,S_OHCI_ED_DIR,M_OHCI_ED_DIR) + +#define K_OHCI_ED_DIR_FROMTD 0 +#define K_OHCI_ED_DIR_OUT 1 +#define K_OHCI_ED_DIR_IN 2 + +#define M_OHCI_ED_LOWSPEED _OHCI_MAKEMASK1(13) +#define M_OHCI_ED_SKIP _OHCI_MAKEMASK1(14) +#define M_OHCI_ED_ISOCFMT _OHCI_MAKEMASK1(15) + +#define S_OHCI_ED_MPS 16 +#define M_OHCI_ED_MPS _OHCI_MAKEMASK(11,S_OHCI_ED_MPS) +#define V_OHCI_ED_MPS(x) _OHCI_MAKEVALUE(x,S_OHCI_ED_MPS) +#define G_OHCI_ED_MPS(x) _OHCI_GETVALUE(x,S_OHCI_ED_MPS,M_OHCI_ED_MPS) + +#define M_OHCI_ED_PTRMASK 0xFFFFFFF0 +#define M_OHCI_ED_HALT _OHCI_MAKEMASK1(0) +#define M_OHCI_ED_TOGGLECARRY _OHCI_MAKEMASK1(1) + +/* ********************************************************************* + * Transfer Descriptor + ********************************************************************* */ + +#define OHCI_TD_ALIGN 32 + +typedef struct ohci_td_s { + uint32_t td_control; + uint32_t td_cbp; + uint32_t td_next_td; + uint32_t td_be; +} ohci_td_t; + +#define M_OHCI_TD_SHORTOK _OHCI_MAKEMASK1(18) + +#define S_OHCI_TD_PID 19 +#define M_OHCI_TD_PID _OHCI_MAKEMASK(2,S_OHCI_TD_PID) +#define V_OHCI_TD_PID(x) _OHCI_MAKEVALUE(x,S_OHCI_TD_PID) +#define G_OHCI_TD_PID(x) _OHCI_GETVALUE(x,S_OHCI_TD_PID,M_OHCI_TD_PID) + +#define K_OHCI_TD_SETUP 0 +#define K_OHCI_TD_OUT 1 +#define K_OHCI_TD_IN 2 +#define K_OHCI_TD_RESERVED 3 + +#define V_OHCI_TD_SETUP V_OHCI_TD_PID(K_OHCI_TD_SETUP) +#define V_OHCI_TD_OUT V_OHCI_TD_PID(K_OHCI_TD_OUT) +#define V_OHCI_TD_IN V_OHCI_TD_PID(K_OHCI_TD_IN) +#define V_OHCI_TD_RESERVED V_OHCI_TD_PID(K_OHCI_TD_RESERVED) + +#define S_OHCI_TD_DI 21 +#define M_OHCI_TD_DI _OHCI_MAKEMASK(3,S_OHCI_TD_DI) +#define V_OHCI_TD_DI(x) _OHCI_MAKEVALUE(x,S_OHCI_TD_DI) +#define G_OHCI_TD_DI(x) _OHCI_GETVALUE(x,S_OHCI_TD_DI,M_OHCI_TD_DI) + +#define K_OHCI_TD_NOINTR 7 +#define V_OHCI_TD_NOINTR V_OHCI_TD_DI(K_OHCI_TD_NOINTR) + +#define S_OHCI_TD_DT 24 +#define M_OHCI_TD_DT _OHCI_MAKEMASK(2,S_OHCI_TD_DT) +#define V_OHCI_TD_DT(x) _OHCI_MAKEVALUE(x,S_OHCI_TD_DT) +#define G_OHCI_TD_DT(x) _OHCI_GETVALUE(x,S_OHCI_TD_DT,M_OHCI_TD_DT) + +#define K_OHCI_TD_DT_DATA0 2 +#define K_OHCI_TD_DT_DATA1 3 +#define K_OHCI_TD_DT_TCARRY 0 + +#define S_OHCI_TD_EC 26 +#define M_OHCI_TD_EC _OHCI_MAKEMASK(2,S_OHCI_TD_EC) +#define V_OHCI_TD_EC(x) _OHCI_MAKEVALUE(x,S_OHCI_TD_EC) +#define G_OHCI_TD_EC(x) _OHCI_GETVALUE(x,S_OHCI_TD_EC,M_OHCI_TD_EC) + +#define S_OHCI_TD_CC 28 +#define M_OHCI_TD_CC _OHCI_MAKEMASK(4,S_OHCI_TD_CC) +#define V_OHCI_TD_CC(x) _OHCI_MAKEVALUE(x,S_OHCI_TD_CC) +#define G_OHCI_TD_CC(x) _OHCI_GETVALUE(x,S_OHCI_TD_CC,M_OHCI_TD_CC) + +#define K_OHCI_CC_NOERROR 0 +#define K_OHCI_CC_CRC 1 +#define K_OHCI_CC_BITSTUFFING 2 +#define K_OHCI_CC_DATATOGGLEMISMATCH 3 +#define K_OHCI_CC_STALL 4 +#define K_OHCI_CC_DEVICENOTRESPONDING 5 +#define K_OHCI_CC_PIDCHECKFAILURE 6 +#define K_OHCI_CC_UNEXPECTEDPID 7 +#define K_OHCI_CC_DATAOVERRUN 8 +#define K_OHCI_CC_DATAUNDERRUN 9 +#define K_OHCI_CC_BUFFEROVERRUN 12 +#define K_OHCI_CC_BUFFERUNDERRUN 13 +#define K_OHCI_CC_NOTACCESSED 15 + +#define K_OHCI_CC_CANCELLED 0xFF + +#define OHCI_TD_MAX_DATA 8192 + + +/* ********************************************************************* + * Endpoint descriptor (isochronous) + ********************************************************************* */ + +/* + * TBA + */ + +/* ********************************************************************* + * Host Controller Communications Area (HCCA) + ********************************************************************* */ + +#define OHCI_INTTABLE_SIZE 32 + +#define OHCI_HCCA_ALIGN 256 /* Align on 256-byte boundary */ + +typedef struct ohci_hcca_s { + uint32_t hcca_inttable[OHCI_INTTABLE_SIZE]; + uint32_t hcca_framenum; /* note: actually two 16-bit fields */ + uint32_t hcca_donehead; + uint32_t hcca_reserved[29]; /* round to 256 bytes */ + uint32_t hcca_pad; +} ohci_hcca_t; + +/* ********************************************************************* + * Registers + ********************************************************************* */ + +#define _OHCI_REGIDX(x) ((x)*4) + +#define R_OHCI_REVISION _OHCI_REGIDX(0) +#define R_OHCI_CONTROL _OHCI_REGIDX(1) +#define R_OHCI_CMDSTATUS _OHCI_REGIDX(2) +#define R_OHCI_INTSTATUS _OHCI_REGIDX(3) +#define R_OHCI_INTENABLE _OHCI_REGIDX(4) +#define R_OHCI_INTDISABLE _OHCI_REGIDX(5) +#define R_OHCI_HCCA _OHCI_REGIDX(6) +#define R_OHCI_PERIODCURRENTED _OHCI_REGIDX(7) +#define R_OHCI_CONTROLHEADED _OHCI_REGIDX(8) +#define R_OHCI_CONTROLCURRENTED _OHCI_REGIDX(9) +#define R_OHCI_BULKHEADED _OHCI_REGIDX(10) +#define R_OHCI_BULKCURRENTED _OHCI_REGIDX(11) +#define R_OHCI_DONEHEAD _OHCI_REGIDX(12) +#define R_OHCI_FMINTERVAL _OHCI_REGIDX(13) +#define R_OHCI_FMREMAINING _OHCI_REGIDX(14) +#define R_OHCI_FMNUMBER _OHCI_REGIDX(15) +#define R_OHCI_PERIODICSTART _OHCI_REGIDX(16) +#define R_OHCI_LSTHRESHOLD _OHCI_REGIDX(17) +#define R_OHCI_RHDSCRA _OHCI_REGIDX(18) +#define R_OHCI_RHDSCRB _OHCI_REGIDX(19) +#define R_OHCI_RHSTATUS _OHCI_REGIDX(20) +#define R_OHCI_RHPORTSTATUS(x) _OHCI_REGIDX(20+(x)) /* note: 1-based! */ + + +/* + * R_OHCI_REVISION + */ + +#define S_OHCI_REV_REV 0 +#define M_OHCI_REV_REV _OHCI_MAKEMASK(8,S_OHCI_REV_REV) +#define V_OHCI_REV_REV(x) _OHCI_MAKEVALUE(x,S_OHCI_REV_REV) +#define G_OHCI_REV_REV(x) _OHCI_GETVALUE(x,S_OHCI_REV_REV,M_OHCI_REV_REV) +#define K_OHCI_REV_11 0x10 + +/* + * R_OHCI_CONTROL + */ + +#define S_OHCI_CONTROL_CBSR 0 +#define M_OHCI_CONTROL_CBSR _OHCI_MAKEMASK(2,S_OHCI_CONTROL_CBSR) +#define V_OHCI_CONTROL_CBSR(x) _OHCI_MAKEVALUE(x,S_OHCI_CONTROL_CBSR) +#define G_OHCI_CONTROL_CBSR(x) _OHCI_GETVALUE(x,S_OHCI_CONTROL_CBSR,M_OHCI_CONTROL_CBSR) + +#define K_OHCI_CBSR_11 0 +#define K_OHCI_CBSR_21 1 +#define K_OHCI_CBSR_31 2 +#define K_OHCI_CBSR_41 3 + +#define M_OHCI_CONTROL_PLE _OHCI_MAKEMASK1(2) +#define M_OHCI_CONTROL_IE _OHCI_MAKEMASK1(3) +#define M_OHCI_CONTROL_CLE _OHCI_MAKEMASK1(4) +#define M_OHCI_CONTROL_BLE _OHCI_MAKEMASK1(5) + +#define S_OHCI_CONTROL_HCFS 6 +#define M_OHCI_CONTROL_HCFS _OHCI_MAKEMASK(2,S_OHCI_CONTROL_HCFS) +#define V_OHCI_CONTROL_HCFS(x) _OHCI_MAKEVALUE(x,S_OHCI_CONTROL_HCFS) +#define G_OHCI_CONTROL_HCFS(x) _OHCI_GETVALUE(x,S_OHCI_CONTROL_HCFS,M_OHCI_CONTROL_HCFS) + +#define K_OHCI_HCFS_RESET 0 +#define K_OHCI_HCFS_RESUME 1 +#define K_OHCI_HCFS_OPERATIONAL 2 +#define K_OHCI_HCFS_SUSPEND 3 + +#define M_OHCI_CONTROL_IR _OHCI_MAKEMASK1(8) +#define M_OHCI_CONTROL_RWC _OHCI_MAKEMASK1(9) +#define M_OHCI_CONTROL_RWE _OHCI_MAKEMASK1(10) + +/* + * R_OHCI_CMDSTATUS + */ + +#define M_OHCI_CMDSTATUS_HCR _OHCI_MAKEMASK1(0) +#define M_OHCI_CMDSTATUS_CLF _OHCI_MAKEMASK1(1) +#define M_OHCI_CMDSTATUS_BLF _OHCI_MAKEMASK1(2) +#define M_OHCI_CMDSTATUS_OCR _OHCI_MAKEMASK1(3) + +#define S_OHCI_CMDSTATUS_SOC 16 +#define M_OHCI_CMDSTATUS_SOC _OHCI_MAKEMASK(2,S_OHCI_CMDSTATUS_SOC) +#define V_OHCI_CMDSTATUS_SOC(x) _OHCI_MAKEVALUE(x,S_OHCI_CMDSTATUS_SOC) +#define G_OHCI_CMDSTATUS_SOC(x) _OHCI_GETVALUE(x,S_OHCI_CMDSTATUS_SOC,M_OHCI_CMDSTATUS_SOC) + +/* + * R_OHCI_INTSTATUS, R_OHCI_INTENABLE, R_OHCI_INTDISABLE + */ + + +#define M_OHCI_INT_SO _OHCI_MAKEMASK1(0) +#define M_OHCI_INT_WDH _OHCI_MAKEMASK1(1) +#define M_OHCI_INT_SF _OHCI_MAKEMASK1(2) +#define M_OHCI_INT_RD _OHCI_MAKEMASK1(3) +#define M_OHCI_INT_UE _OHCI_MAKEMASK1(4) +#define M_OHCI_INT_FNO _OHCI_MAKEMASK1(5) +#define M_OHCI_INT_RHSC _OHCI_MAKEMASK1(6) +#define M_OHCI_INT_OC _OHCI_MAKEMASK1(30) +#define M_OHCI_INT_MIE _OHCI_MAKEMASK1(31) + +#define M_OHCI_INT_ALL M_OHCI_INT_SO | M_OHCI_INT_WDH | M_OHCI_INT_SF | \ + M_OHCI_INT_RD | M_OHCI_INT_UE | M_OHCI_INT_FNO | \ + M_OHCI_INT_RHSC | M_OHCI_INT_OC | M_OHCI_INT_MIE + +/* + * R_OHCI_FMINTERVAL + */ + + +#define S_OHCI_FMINTERVAL_FI 0 +#define M_OHCI_FMINTERVAL_FI _OHCI_MAKEMASK(14,S_OHCI_FMINTERVAL_FI) +#define V_OHCI_FMINTERVAL_FI(x) _OHCI_MAKEVALUE(x,S_OHCI_FMINTERVAL_FI) +#define G_OHCI_FMINTERVAL_FI(x) _OHCI_GETVALUE(x,S_OHCI_FMINTERVAL_FI,M_OHCI_FMINTERVAL_FI) + +#define S_OHCI_FMINTERVAL_FSMPS 16 +#define M_OHCI_FMINTERVAL_FSMPS _OHCI_MAKEMASK(15,S_OHCI_FMINTERVAL_FSMPS) +#define V_OHCI_FMINTERVAL_FSMPS(x) _OHCI_MAKEVALUE(x,S_OHCI_FMINTERVAL_FSMPS) +#define G_OHCI_FMINTERVAL_FSMPS(x) _OHCI_GETVALUE(x,S_OHCI_FMINTERVAL_FSMPS,M_OHCI_FMINTERVAL_FSMPS) + +#define OHCI_CALC_FSMPS(x) ((((x)-210)*6/7)) + + +#define M_OHCI_FMINTERVAL_FIT _OHCI_MAKEMASK1(31) + +/* + * R_OHCI_FMREMAINING + */ + + +#define S_OHCI_FMREMAINING_FR 0 +#define M_OHCI_FMREMAINING_FR _OHCI_MAKEMASK(14,S_OHCI_FMREMAINING_FR) +#define V_OHCI_FMREMAINING_FR(x) _OHCI_MAKEVALUE(x,S_OHCI_FMREMAINING_FR) +#define G_OHCI_FMREMAINING_FR(x) _OHCI_GETVALUE(x,S_OHCI_FMREMAINING_FR,M_OHCI_FMREMAINING_FR) + +#define M_OHCI_FMREMAINING_FRT _OHCI_MAKEMASK1(31) + +/* + * R_OHCI_RHDSCRA + */ + + +#define S_OHCI_RHDSCRA_NDP 0 +#define M_OHCI_RHDSCRA_NDP _OHCI_MAKEMASK(8,S_OHCI_RHDSCRA_NDP) +#define V_OHCI_RHDSCRA_NDP(x) _OHCI_MAKEVALUE(x,S_OHCI_RHDSCRA_NDP) +#define G_OHCI_RHDSCRA_NDP(x) _OHCI_GETVALUE(x,S_OHCI_RHDSCRA_NDP,M_OHCI_RHDSCRA_NDP) + +#define M_OHCI_RHDSCRA_PSM _OHCI_MAKEMASK1(8) +#define M_OHCI_RHDSCRA_NPS _OHCI_MAKEMASK1(9) +#define M_OHCI_RHDSCRA_DT _OHCI_MAKEMASK1(10) +#define M_OHCI_RHDSCRA_OCPM _OHCI_MAKEMASK1(11) +#define M_OHCI_RHDSCRA_NOCP _OHCI_MAKEMASK1(12) + +#define S_OHCI_RHDSCRA_POTPGT 24 +#define M_OHCI_RHDSCRA_POTPGT _OHCI_MAKEMASK(8,S_OHCI_RHDSCRA_POTPGT) +#define V_OHCI_RHDSCRA_POTPGT(x) _OHCI_MAKEVALUE(x,S_OHCI_RHDSCRA_POTPGT) +#define G_OHCI_RHDSCRA_POTPGT(x) _OHCI_GETVALUE(x,S_OHCI_RHDSCRA_POTPGT,M_OHCI_RHDSCRA_POTPGT) + +/* + * R_OHCI_RHDSCRB + */ + +#define S_OHCI_RHDSCRB_DR 0 +#define M_OHCI_RHDSCRB_DR _OHCI_MAKEMASK(16,S_OHCI_RHDSCRB_DR) +#define V_OHCI_RHDSCRB_DR(x) _OHCI_MAKEVALUE(x,S_OHCI_RHDSCRB_DR) +#define G_OHCI_RHDSCRB_DR(x) _OHCI_GETVALUE(x,S_OHCI_RHDSCRB_DR,M_OHCI_RHDSCRB_DR) + +#define S_OHCI_RHDSCRB_PPCM 16 +#define M_OHCI_RHDSCRB_PPCM _OHCI_MAKEMASK(16,S_OHCI_RHDSCRB_PPCM) +#define V_OHCI_RHDSCRB_PPCM(x) _OHCI_MAKEVALUE(x,S_OHCI_RHDSCRB_PPCM) +#define G_OHCI_RHDSCRB_PPCM(x) _OHCI_GETVALUE(x,S_OHCI_RHDSCRB_PPCM,M_OHCI_RHDSCRB_PPCM) + +/* + * R_OHCI_RHSTATUS + */ + +#define M_OHCI_RHSTATUS_LPS _OHCI_MAKEMASK1(0) +#define M_OHCI_RHSTATUS_OCI _OHCI_MAKEMASK1(1) +#define M_OHCI_RHSTATUS_DRWE _OHCI_MAKEMASK1(15) +#define M_OHCI_RHSTATUS_LPSC _OHCI_MAKEMASK1(16) +#define M_OHCI_RHSTATUS_OCIC _OHCI_MAKEMASK1(17) +#define M_OHCI_RHSTATUS_CRWE _OHCI_MAKEMASK1(31) + +/* + * R_OHCI_RHPORTSTATUS + */ + +#define M_OHCI_RHPORTSTAT_CCS _OHCI_MAKEMASK1(0) +#define M_OHCI_RHPORTSTAT_PES _OHCI_MAKEMASK1(1) +#define M_OHCI_RHPORTSTAT_PSS _OHCI_MAKEMASK1(2) +#define M_OHCI_RHPORTSTAT_POCI _OHCI_MAKEMASK1(3) +#define M_OHCI_RHPORTSTAT_PRS _OHCI_MAKEMASK1(4) +#define M_OHCI_RHPORTSTAT_PPS _OHCI_MAKEMASK1(8) +#define M_OHCI_RHPORTSTAT_LSDA _OHCI_MAKEMASK1(9) +#define M_OHCI_RHPORTSTAT_CSC _OHCI_MAKEMASK1(16) +#define M_OHCI_RHPORTSTAT_PESC _OHCI_MAKEMASK1(17) +#define M_OHCI_RHPORTSTAT_PSSC _OHCI_MAKEMASK1(18) +#define M_OHCI_RHPORTSTAT_OCIC _OHCI_MAKEMASK1(19) +#define M_OHCI_RHPORTSTAT_PRSC _OHCI_MAKEMASK1(20) + +#define M_OHCI_RHPORTSTAT_ALLC (M_OHCI_RHPORTSTAT_CSC | \ + M_OHCI_RHPORTSTAT_PSSC | \ + M_OHCI_RHPORTSTAT_OCIC | \ + M_OHCI_RHPORTSTAT_PRSC) + +/* ********************************************************************* + * OHCI Structures + ********************************************************************* */ + +#define beginningof(ptr,type,field) ((type *) (((int) (ptr)) - ((int) ((type *) 0)->field))) + +#define OHCI_INTTREE_SIZE 63 + +#define OHCI_EDPOOL_SIZE 128 +#define OHCI_TDPOOL_SIZE 32 + +typedef struct ohci_endpoint_s { + struct ohci_endpoint_s *ep_next; + uint32_t ep_phys; + int ep_flags; + int ep_mps; + int ep_num; +} ohci_endpoint_t; + +typedef struct ohci_transfer_s { + void *t_ref; + int t_length; + struct ohci_transfer_s *t_next; +} ohci_transfer_t; + +typedef struct ohci_softc_s { + ohci_endpoint_t *ohci_edtable[OHCI_INTTREE_SIZE]; + ohci_endpoint_t *ohci_inttable[OHCI_INTTABLE_SIZE]; + ohci_endpoint_t *ohci_isoc_list; + ohci_endpoint_t *ohci_ctl_list; + ohci_endpoint_t *ohci_bulk_list; + ohci_hcca_t *ohci_hcca; + ohci_endpoint_t *ohci_endpoint_pool; + ohci_transfer_t *ohci_transfer_pool; + ohci_ed_t *ohci_hwedpool; + ohci_td_t *ohci_hwtdpool; + ohci_endpoint_t *ohci_endpoint_freelist; + ohci_transfer_t *ohci_transfer_freelist; +#ifdef _CFE_ + physaddr_t ohci_regs; +#else + volatile uint32_t *ohci_regs; +#endif + int ohci_ndp; + long ohci_addr; + uint32_t ohci_intdisable; + + int ohci_rh_newaddr; /* Address to be set on next status update */ + int ohci_rh_addr; /* address of root hub */ + int ohci_rh_conf; /* current configuration # */ + uint8_t ohci_rh_buf[128]; /* buffer to hold hub responses */ + uint8_t *ohci_rh_ptr; /* pointer into buffer */ + int ohci_rh_len; /* remaining bytes to transfer */ + queue_t ohci_rh_intrq; /* Interrupt request queue */ + usbbus_t *ohci_bus; /* owning usbbus structure */ + +} ohci_softc_t; + + +/* + * Misc stuff + */ +#define OHCI_RESET_DELAY 10 + + diff --git a/cfe/cfe/usb/usbchap9.h b/cfe/cfe/usb/usbchap9.h new file mode 100644 index 0000000..1afd0db --- /dev/null +++ b/cfe/cfe/usb/usbchap9.h @@ -0,0 +1,389 @@ +/* ********************************************************************* + * Broadcom Common Firmware Environment (CFE) + * + * Chapter 9 definitions File: usbchap9.h + * + * This module contains definitions from the USB specification, + * chapter 9. + * + * 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 _USBCHAP9_H_ +#define _USBCHAP9_H_ + +#define MAXIMUM_USB_STRING_LENGTH 255 + +/* + * values for the bits returned by the USB GET_STATUS command + */ +#define USB_GETSTATUS_SELF_POWERED 0x01 +#define USB_GETSTATUS_REMOTE_WAKEUP_ENABLED 0x02 + + +#define USB_DEVICE_DESCRIPTOR_TYPE 0x01 +#define USB_CONFIGURATION_DESCRIPTOR_TYPE 0x02 +#define USB_STRING_DESCRIPTOR_TYPE 0x03 +#define USB_INTERFACE_DESCRIPTOR_TYPE 0x04 +#define USB_ENDPOINT_DESCRIPTOR_TYPE 0x05 +#define USB_POWER_DESCRIPTOR_TYPE 0x06 +#define USB_HID_DESCRIPTOR_TYPE 0x21 +#define USB_HUB_DESCRIPTOR_TYPE 0x29 + +#define USB_DESCRIPTOR_TYPEINDEX(d, i) ((uint16_t)((uint16_t)(d)<<8 | (i))) + +/* + * Values for bmAttributes field of an + * endpoint descriptor + */ + +#define USB_ENDPOINT_TYPE_MASK 0x03 + +#define USB_ENDPOINT_TYPE_CONTROL 0x00 +#define USB_ENDPOINT_TYPE_ISOCHRONOUS 0x01 +#define USB_ENDPOINT_TYPE_BULK 0x02 +#define USB_ENDPOINT_TYPE_INTERRUPT 0x03 + + +/* + * definitions for bits in the bmAttributes field of a + * configuration descriptor. + */ +#define USB_CONFIG_POWERED_MASK 0xc0 + +#define USB_CONFIG_BUS_POWERED 0x80 +#define USB_CONFIG_SELF_POWERED 0x40 +#define USB_CONFIG_REMOTE_WAKEUP 0x20 + +/* + * Endpoint direction bit, stored in address + */ + +#define USB_ENDPOINT_DIRECTION_MASK 0x80 +#define USB_ENDPOINT_DIRECTION_IN 0x80 /* bit set means IN */ + +/* + * test direction bit in the bEndpointAddress field of + * an endpoint descriptor. + */ +#define USB_ENDPOINT_DIR_OUT(addr) (!((addr) & USB_ENDPOINT_DIRECTION_MASK)) +#define USB_ENDPOINT_DIR_IN(addr) ((addr) & USB_ENDPOINT_DIRECTION_MASK) + +#define USB_ENDPOINT_ADDRESS(addr) ((addr) & 0x0F) + +/* + * USB defined request codes + * see chapter 9 of the USB 1.0 specifcation for + * more information. + */ + +/* + * These are the correct values based on the USB 1.0 + * specification + */ + +#define USB_REQUEST_GET_STATUS 0x00 +#define USB_REQUEST_CLEAR_FEATURE 0x01 + +#define USB_REQUEST_SET_FEATURE 0x03 + +#define USB_REQUEST_SET_ADDRESS 0x05 +#define USB_REQUEST_GET_DESCRIPTOR 0x06 +#define USB_REQUEST_SET_DESCRIPTOR 0x07 +#define USB_REQUEST_GET_CONFIGURATION 0x08 +#define USB_REQUEST_SET_CONFIGURATION 0x09 +#define USB_REQUEST_GET_INTERFACE 0x0A +#define USB_REQUEST_SET_INTERFACE 0x0B +#define USB_REQUEST_SYNC_FRAME 0x0C + + +/* + * defined USB device classes + */ + + +#define USB_DEVICE_CLASS_RESERVED 0x00 +#define USB_DEVICE_CLASS_AUDIO 0x01 +#define USB_DEVICE_CLASS_COMMUNICATIONS 0x02 +#define USB_DEVICE_CLASS_HUMAN_INTERFACE 0x03 +#define USB_DEVICE_CLASS_MONITOR 0x04 +#define USB_DEVICE_CLASS_PHYSICAL_INTERFACE 0x05 +#define USB_DEVICE_CLASS_POWER 0x06 +#define USB_DEVICE_CLASS_PRINTER 0x07 +#define USB_DEVICE_CLASS_STORAGE 0x08 +#define USB_DEVICE_CLASS_HUB 0x09 +#define USB_DEVICE_CLASS_VENDOR_SPECIFIC 0xFF + +/* + * USB defined Feature selectors + */ + +#define USB_FEATURE_ENDPOINT_STALL 0x0000 +#define USB_FEATURE_REMOTE_WAKEUP 0x0001 +#define USB_FEATURE_POWER_D0 0x0002 +#define USB_FEATURE_POWER_D1 0x0003 +#define USB_FEATURE_POWER_D2 0x0004 +#define USB_FEATURE_POWER_D3 0x0005 + +/* + * USB Device descriptor. + * To reduce problems with compilers trying to optimize + * this structure, all the fields are bytes. + */ + +#define USBWORD(x) ((x) & 0xFF),(((x) >> 8) & 0xFF) + +#define USB_CONTROL_ENDPOINT_MIN_SIZE 8 + +typedef struct usb_device_descr_s { + uint8_t bLength; + uint8_t bDescriptorType; + uint8_t bcdUSBLow,bcdUSBHigh; + uint8_t bDeviceClass; + uint8_t bDeviceSubClass; + uint8_t bDeviceProtocol; + uint8_t bMaxPacketSize0; + uint8_t idVendorLow,idVendorHigh; + uint8_t idProductLow,idProductHigh; + uint8_t bcdDeviceLow,bcdDeviceHigh; + uint8_t iManufacturer; + uint8_t iProduct; + uint8_t iSerialNumber; + uint8_t bNumConfigurations; +} usb_device_descr_t; + +typedef struct usb_endpoint_descr_s { + uint8_t bLength; + uint8_t bDescriptorType; + uint8_t bEndpointAddress; + uint8_t bmAttributes; + uint8_t wMaxPacketSizeLow,wMaxPacketSizeHigh; + uint8_t bInterval; +} usb_endpoint_descr_t; + +/* + * values for bmAttributes Field in + * USB_CONFIGURATION_DESCRIPTOR + */ + +#define CONFIG_BUS_POWERED 0x80 +#define CONFIG_SELF_POWERED 0x40 +#define CONFIG_REMOTE_WAKEUP 0x20 + +typedef struct usb_config_descr_s { + uint8_t bLength; + uint8_t bDescriptorType; + uint8_t wTotalLengthLow,wTotalLengthHigh; + uint8_t bNumInterfaces; + uint8_t bConfigurationValue; + uint8_t iConfiguration; + uint8_t bmAttributes; + uint8_t MaxPower; +} usb_config_descr_t; + +#define USB_INTERFACE_CLASS_HUB 0x09 + +typedef struct usb_interface_descr_s { + uint8_t bLength; + uint8_t bDescriptorType; + uint8_t bInterfaceNumber; + uint8_t bAlternateSetting; + uint8_t bNumEndpoints; + uint8_t bInterfaceClass; + uint8_t bInterfaceSubClass; + uint8_t bInterfaceProtocol; + uint8_t iInterface; +} usb_interface_descr_t; + +typedef struct usb_string_descr_s { + uint8_t bLength; + uint8_t bDescriptorType; + uint8_t bString[1]; +} usb_string_descr_t; + +/* + * USB power descriptor added to core specification + */ + +#define USB_SUPPORT_D0_COMMAND 0x01 +#define USB_SUPPORT_D1_COMMAND 0x02 +#define USB_SUPPORT_D2_COMMAND 0x04 +#define USB_SUPPORT_D3_COMMAND 0x08 + +#define USB_SUPPORT_D1_WAKEUP 0x10 +#define USB_SUPPORT_D2_WAKEUP 0x20 + + +typedef struct usb_power_descr_s { + uint8_t bLength; + uint8_t bDescriptorType; + uint8_t bCapabilitiesFlags; + uint16_t EventNotification; + uint16_t D1LatencyTime; + uint16_t D2LatencyTime; + uint16_t D3LatencyTime; + uint8_t PowerUnit; + uint16_t D0PowerConsumption; + uint16_t D1PowerConsumption; + uint16_t D2PowerConsumption; +} usb_power_descr_t; + + +typedef struct usb_common_descr_s { + uint8_t bLength; + uint8_t bDescriptorType; +} usb_common_descr_t; + +typedef struct usb_device_status_s { + uint8_t wDeviceStatusLow,wDeviceStatusHigh; +} usb_device_status_t; + + +/* + * Standard USB HUB definitions + * + * See Chapter 11 + */ + +#define USB_HUB_DESCR_SIZE 8 +typedef struct usb_hub_descr_s { + uint8_t bDescriptorLength; /* Length of this descriptor */ + uint8_t bDescriptorType; /* Hub configuration type */ + uint8_t bNumberOfPorts; /* number of ports on this hub */ + uint8_t wHubCharacteristicsLow; /* Hub Charateristics */ + uint8_t wHubCharacteristicsHigh; + uint8_t bPowerOnToPowerGood; /* port power on till power good in 2ms */ + uint8_t bHubControlCurrent; /* max current in mA */ + /* room for 255 ports power control and removable bitmask */ + uint8_t bRemoveAndPowerMask[64]; +} usb_hub_descr_t; + +#define USB_HUBCHAR_PWR_GANGED 0 +#define USB_HUBCHAR_PWR_IND 1 +#define USB_HUBCHAR_PWR_NONE 2 + +typedef struct usb_hub_status_s { + uint8_t wHubStatusLow,wHubStatusHigh; + uint8_t wHubChangeLow,wHubChangeHigh; +} usb_hub_status_t; + +#define USB_PORT_STATUS_CONNECT 0x0001 +#define USB_PORT_STATUS_ENABLED 0x0002 +#define USB_PORT_STATUS_SUSPEND 0x0004 +#define USB_PORT_STATUS_OVERCUR 0x0008 +#define USB_PORT_STATUS_RESET 0x0010 +#define USB_PORT_STATUS_POWER 0x0100 +#define USB_PORT_STATUS_LOWSPD 0x0200 + +typedef struct usb_port_status_s { + uint8_t wPortStatusLow,wPortStatusHigh; + uint8_t wPortChangeLow,wPortChangeHigh; +} usb_port_status_t; + + +#define USB_HUBREQ_GET_STATUS 0 +#define USB_HUBREQ_CLEAR_FEATURE 1 +#define USB_HUBREQ_GET_STATE 2 +#define USB_HUBREQ_SET_FEATURE 3 +#define USB_HUBREQ_GET_DESCRIPTOR 6 +#define USB_HUBREQ_SET_DESCRIPTOR 7 + +#define USB_HUB_FEATURE_C_LOCAL_POWER 0 +#define USB_HUB_FEATURE_C_OVER_CURRENT 1 + +#define USB_PORT_FEATURE_CONNECTION 0 +#define USB_PORT_FEATURE_ENABLE 1 +#define USB_PORT_FEATURE_SUSPEND 2 +#define USB_PORT_FEATURE_OVER_CURRENT 3 +#define USB_PORT_FEATURE_RESET 4 +#define USB_PORT_FEATURE_POWER 8 +#define USB_PORT_FEATURE_LOW_SPEED 9 +#define USB_PORT_FEATURE_C_PORT_CONNECTION 16 +#define USB_PORT_FEATURE_C_PORT_ENABLE 17 +#define USB_PORT_FEATURE_C_PORT_SUSPEND 18 +#define USB_PORT_FEATURE_C_PORT_OVER_CURRENT 19 +#define USB_PORT_FEATURE_C_PORT_RESET 20 + + +#define GETUSBFIELD(s,f) (((s)->f##Low) | ((s)->f##High << 8)) +#define PUTUSBFIELD(s,f,v) (s)->f##Low = (v & 0xFF); \ + (s)->f##High = ((v)>>8 & 0xFF) + +typedef struct usb_device_request_s { + uint8_t bmRequestType; + uint8_t bRequest; + uint8_t wValueLow,wValueHigh; + uint8_t wIndexLow,wIndexHigh; + uint8_t wLengthLow,wLengthHigh; +} usb_device_request_t; + +/* + * Values for the bmAttributes field of a request + */ +#define USBREQ_DIR_IN 0x80 +#define USBREQ_DIR_OUT 0x00 +#define USBREQ_TYPE_STD 0x00 +#define USBREQ_TYPE_CLASS 0x20 +#define USBREQ_TYPE_VENDOR 0x40 +#define USBREQ_TYPE_RSVD 0x60 +#define USBREQ_REC_DEVICE 0x00 +#define USBREQ_REC_INTERFACE 0x01 +#define USBREQ_REC_ENDPOINT 0x02 +#define USBREQ_REC_OTHER 0x03 + +#define REQCODE(req,dir,type,rec) (((req) << 8) | (dir) | (type) | (rec)) +#define REQSW(req,attr) (((req) << 8) | (attr)) + +/* ********************************************************************* + * HID stuff + ********************************************************************* */ + +typedef struct usb_hid_descr_s { + uint8_t bLength; + uint8_t bDescriptorType; + uint8_t bcdHIDLow,bcdHIDHigh; + uint8_t bCountryCode; + uint8_t bNumDescriptors; + uint8_t bClassDescrType; + uint8_t wClassDescrLengthLow,wClassDescrLengthHigh; +} usb_hid_descr_t; + +#endif /* _USBCHAP9_H_ */ diff --git a/cfe/cfe/usb/usbd.c b/cfe/cfe/usb/usbd.c new file mode 100644 index 0000000..3e77049 --- /dev/null +++ b/cfe/cfe/usb/usbd.c @@ -0,0 +1,1215 @@ +/* ********************************************************************* + * Broadcom Common Firmware Environment (CFE) + * + * USB device layer File: usbd.c + * + * This module deals with devices (things connected to USB buses) + * + * 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 <time.h> +#include <memory.h> +#include <stdint.h> +#include "usbhack.h" +#else +#include "lib_types.h" +#include "lib_string.h" +#include "lib_printf.h" +#include "cfe_timer.h" +#endif + +#include "lib_malloc.h" +#include "lib_queue.h" +#include "usbchap9.h" +#include "usbd.h" + +/* ********************************************************************* + * Globals + ********************************************************************* */ + +int usb_noisy = 0; + + +/* ********************************************************************* + * usb_create_pipe(dev,epaddr,mps,flags) + * + * Create a pipe, causing the corresponding endpoint to + * be created in the host controller driver. Pipes form the + * basic "handle" for unidirectional communications with a + * USB device. + * + * Input parameters: + * dev - device we're talking about + * epaddr - endpoint address open, usually from the endpoint + * descriptor + * mps - maximum packet size understood by the device + * flags - flags for this pipe (UP_xxx flags) + * + * Return value: + * <0 if error + * 0 if ok + ********************************************************************* */ + +int usb_create_pipe(usbdev_t *dev,int epaddr,int mps,int flags) +{ + usbpipe_t *pipe; + int pipeidx; + + pipeidx = USB_EPADDR_TO_IDX(epaddr); + + if (dev->ud_pipes[pipeidx] != NULL) { + printf("Trying to create a pipe that was already created!\n"); + return 0; + } + + pipe = KMALLOC(sizeof(usbpipe_t),0); + + if (!pipe) return -1; + + pipe->up_flags = flags; + pipe->up_num = pipeidx; + pipe->up_mps = mps; + pipe->up_dev = dev; + if (dev->ud_flags & UD_FLAG_LOWSPEED) flags |= UP_TYPE_LOWSPEED; + pipe->up_hwendpoint = UBEPTCREATE(dev->ud_bus, + dev->ud_address, + USB_ENDPOINT_ADDRESS(epaddr), + mps, + flags); + + dev->ud_pipes[pipeidx] = pipe; + + return 0; +} + +/* ********************************************************************* + * usb_open_pipe(dev,epdesc) + * + * Open a pipe given an endpoint descriptor - this is the + * normal way pipes get open, since you've just selected a + * configuration and have the descriptors handy with all + * the information you need. + * + * Input parameters: + * dev - device we're talking to + * epdesc - endpoint descriptor + * + * Return value: + * <0 if error + * else endpoint/pipe number (from descriptor) + ********************************************************************* */ + +int usb_open_pipe(usbdev_t *dev,usb_endpoint_descr_t *epdesc) +{ + int res; + int flags = 0; + + if (USB_ENDPOINT_DIR_IN(epdesc->bEndpointAddress)) flags |= UP_TYPE_IN; + else flags |= UP_TYPE_OUT; + + switch (epdesc->bmAttributes & USB_ENDPOINT_TYPE_MASK) { + case USB_ENDPOINT_TYPE_CONTROL: + flags |= UP_TYPE_CONTROL; + break; + case USB_ENDPOINT_TYPE_ISOCHRONOUS: + flags |= UP_TYPE_ISOC; + break; + case USB_ENDPOINT_TYPE_BULK: + flags |= UP_TYPE_BULK; + break; + case USB_ENDPOINT_TYPE_INTERRUPT: + flags |= UP_TYPE_INTR; + break; + } + + res = usb_create_pipe(dev, + epdesc->bEndpointAddress, + GETUSBFIELD(epdesc,wMaxPacketSize), + flags); + + if (res < 0) return res; + + return epdesc->bEndpointAddress; +} + + +/* ********************************************************************* + * usb_destroy_pipe(dev,epaddr) + * + * Close(destroy) an open pipe and remove endpoint descriptor + * + * Input parameters: + * dev - device we're talking to + * epaddr - pipe to close + * + * Return value: + * nothing + ********************************************************************* */ + +void usb_destroy_pipe(usbdev_t *dev,int epaddr) +{ + usbpipe_t *pipe; + int pipeidx; + + pipeidx = USB_EPADDR_TO_IDX(epaddr); + + pipe = dev->ud_pipes[pipeidx]; + if (!pipe) return; + + if (dev->ud_pipes[pipeidx]) { + UBEPTDELETE(dev->ud_bus, + dev->ud_pipes[pipeidx]->up_hwendpoint); + } + + KFREE(dev->ud_pipes[pipeidx]); + dev->ud_pipes[pipeidx] = NULL; +} + +/* ********************************************************************* + * usb_destroy_device(dev) + * + * Delete an entire USB device, closing its pipes and freeing + * the device data structure + * + * Input parameters: + * dev - device to destroy + * + * Return value: + * nothing + ********************************************************************* */ + +void usb_destroy_device(usbdev_t *dev) +{ + int idx; + + for (idx = 0; idx < UD_MAX_PIPES; idx++) { + if (dev->ud_pipes[idx]) { + UBEPTDELETE(dev->ud_bus, + dev->ud_pipes[idx]->up_hwendpoint); + KFREE(dev->ud_pipes[idx]); + } + } + + dev->ud_bus->ub_devices[dev->ud_address] = NULL; + + KFREE(dev); +} + + +/* ********************************************************************* + * usb_create_device(bus,lowspeed) + * + * Create a new USB device. This device will be set to + * communicate on address zero (default address) and will be + * ready for basic stuff so we can figure out what it is. + * The control pipe will be open, so you can start requesting + * descriptors right away. + * + * Input parameters: + * bus - bus to create device on + * lowspeed - true if it's a lowspeed device (the hubs tell + * us these things) + * + * Return value: + * usb device structure, or NULL + ********************************************************************* */ + +usbdev_t *usb_create_device(usbbus_t *bus,int lowspeed) +{ + usbdev_t *dev; + int pipeflags; + + /* + * Create the device structure. + */ + + dev = KMALLOC(sizeof(usbdev_t),0); + memset(dev,0,sizeof(usbdev_t)); + + dev->ud_bus = bus; + dev->ud_address = 0; /* default address */ + dev->ud_parent = NULL; + dev->ud_flags = 0; + + /* + * Adjust things based on the target device speed + */ + + pipeflags = UP_TYPE_CONTROL; + if (lowspeed) { + pipeflags |= UP_TYPE_LOWSPEED; + dev->ud_flags |= UD_FLAG_LOWSPEED; + } + + /* + * Create the control pipe. + */ + + usb_create_pipe(dev,0, + USB_CONTROL_ENDPOINT_MIN_SIZE, + pipeflags); + + return dev; +} + +/* ********************************************************************* + * usb_make_request(dev,epaddr,buf,len,flags) + * + * Create a template request structure with basic fields + * ready to go. A shorthand routine. + * + * Input parameters: + * dev- device we're talking to + * epaddr - endpoint address, from usb_open_pipe() + * buf,length - user buffer and buffer length + * flags - transfer direction, etc. (UR_xxx flags) + * + * Return value: + * usbreq_t pointer, or NULL + ********************************************************************* */ + +usbreq_t *usb_make_request(usbdev_t *dev,int epaddr,uint8_t *buf,int length,int flags) +{ + usbreq_t *ur; + usbpipe_t *pipe; + int pipeidx; + + pipeidx = USB_EPADDR_TO_IDX(epaddr); + + pipe = dev->ud_pipes[pipeidx]; + + if (pipe == NULL) return NULL; + + ur = KMALLOC(sizeof(usbreq_t),0); + memset(ur,0,sizeof(usbreq_t)); + + ur->ur_dev = dev; + ur->ur_pipe = pipe; + ur->ur_buffer = buf; + ur->ur_length = length; + ur->ur_flags = flags; + ur->ur_callback = NULL; + + return ur; + +} + +/* ********************************************************************* + * usb_poll(bus) + * + * Handle device-driver polling - simply vectors to host controller + * driver. + * + * Input parameters: + * bus - bus structure + * + * Return value: + * nothing + ********************************************************************* */ + +void usb_poll(usbbus_t *bus) +{ + UBINTR(bus); +} + +/* ********************************************************************* + * usb_daemon(bus) + * + * Polls for topology changes and initiates a bus scan if + * necessary. + * + * Input parameters: + * bus - bus to watch + * + * Return value: + * nothing + ********************************************************************* */ + +void usb_daemon(usbbus_t *bus) +{ + /* + * Just see if someone flagged a need for a scan here + * and start the bus scan if necessary. + * + * The actual scanning is a hub function, starting at the + * root hub, so the code for that is over there. + */ + + if (bus->ub_flags & UB_FLG_NEEDSCAN) { + bus->ub_flags &= ~UB_FLG_NEEDSCAN; + usb_scan(bus); + } +} + +/* ********************************************************************* + * usb_cancel_request(ur) + * + * Cancel a pending usb transfer request. + * + * Input parameters: + * ur - request to cancel + * + * Return value: + * 0 if ok + * else error (could not find request) + ********************************************************************* */ + +int usb_cancel_request(usbreq_t *ur) +{ + printf("usb_cancel_request is not implemented.\n"); + return 0; +} + +/* ********************************************************************* + * usb_free_request(ur) + * + * Return a transfer request to the free pool. + * + * Input parameters: + * ur - request to return + * + * Return value: + * nothing + ********************************************************************* */ + +void usb_free_request(usbreq_t *ur) +{ + if (ur->ur_inprogress) { + printf("Yow! Tried to free a request that was in progress!\n"); + return; + } + KFREE(ur); +} + +/* ********************************************************************* + * usb_delay_ms(bus,ms) + * + * Wait a while, calling the polling routine as we go. + * + * Input parameters: + * bus - bus we're talking to + * ms - how long to wait + * + * Return value: + * nothing + ********************************************************************* */ + + +void usb_delay_ms(usbbus_t *bus,int ms) +{ +#ifdef _CFE_ + cfe_sleep(1+((ms*CFE_HZ)/1000)); +#else + mydelay(ms); +#endif +} + +/* ********************************************************************* + * usb_queue_request(ur) + * + * Call the transfer handler in the host controller driver to + * set up a transfer descriptor + * + * Input parameters: + * ur - request to queue + * + * Return value: + * 0 if ok + * else error + ********************************************************************* */ + +int usb_queue_request(usbreq_t *ur) +{ + int res; + + ur->ur_inprogress = 1; + ur->ur_xferred = 0; + res = UBXFER(ur->ur_dev->ud_bus, + ur->ur_pipe->up_hwendpoint, + ur); + return res; +} + +/* ********************************************************************* + * usb_wait_request(ur) + * + * Wait until a request completes, calling the polling routine + * as we wait. + * + * Input parameters: + * ur - request to wait for + * + * Return value: + * request status + ********************************************************************* */ + +int usb_wait_request(usbreq_t *ur) +{ + while ((volatile int) (ur->ur_inprogress)) { + usb_poll(ur->ur_dev->ud_bus); + } + + return ur->ur_status; +} + +/* ********************************************************************* + * usb_sync_request(ur) + * + * Synchronous request - call usb_queue and then usb_wait + * + * Input parameters: + * ur - request to submit + * + * Return value: + * status of request + ********************************************************************* */ + +int usb_sync_request(usbreq_t *ur) +{ + usb_queue_request(ur); + return usb_wait_request(ur); +} + +/* ********************************************************************* + * usb_simple_request(dev,reqtype,bRequest,wValue,wIndex) + * + * Handle a simple USB control pipe request. These are OUT + * requests with no data phase. + * + * Input parameters: + * dev - device we're talking to + * reqtype - request type (bmRequestType) for descriptor + * wValue - wValue for descriptor + * wIndex - wIndex for descriptor + * + * Return value: + * 0 if ok + * else error + ********************************************************************* */ + +int usb_simple_request(usbdev_t *dev,uint8_t reqtype,int bRequest,int wValue,int wIndex) +{ +#if 0 + uint8_t *requestbuf; + usb_device_request_t *req; + usbreq_t *ur; + int res; + + requestbuf = KMALLOC(32,0); + + req = (usb_device_request_t *) requestbuf; + + req->bmRequestType = reqtype; + req->bRequest = bRequest; + PUTUSBFIELD(req,wValue,wValue); + PUTUSBFIELD(req,wIndex,wIndex); + PUTUSBFIELD(req,wLength,0); + + if (usb_noisy > 1) printf("Request: "); + ur = usb_make_request(dev,0,requestbuf,sizeof(usb_device_request_t),UR_FLAG_SETUP); + res = usb_sync_request(ur); + usb_free_request(ur); + + if (res == 4) { /* STALL on control pipe */ + if (usb_noisy > 1) printf("STALL\n"); + usb_clear_stall(dev,dev->ud_pipes[0]->up_num); + return -1; + } + + res = ur->ur_xferred; + if (usb_noisy > 1) printf("Result %d\n",res); + + if (usb_noisy > 1) printf("Status: "); + ur = usb_make_request(dev,0,requestbuf,0,UR_FLAG_STATUS_IN); + res = usb_sync_request(ur); + usb_free_request(ur); + if (usb_noisy > 1) printf("Result %d\n",res); + + if (res == 4) { /* STALL */ + if (usb_noisy > 1) printf("STALL\n"); + usb_clear_stall(dev,dev->ud_pipes[0]->up_num); + return -1; + } + + KFREE(requestbuf); + + return 0; +#else + return usb_std_request(dev,reqtype,bRequest,wValue,wIndex,NULL,0); +#endif + +} + + +/* ********************************************************************* + * usb_set_configuration(dev,config) + * + * Set the current configuration for a USB device. + * + * Input parameters: + * dev - device we're talking to + * config - bConfigValue for the device + * + * Return value: + * request status + ********************************************************************* */ + +int usb_set_configuration(usbdev_t *dev,int config) +{ + int res; + + res = usb_simple_request(dev,0x00,USB_REQUEST_SET_CONFIGURATION,config,0); + + return res; +} + + +/* ********************************************************************* + * usb_new_address(bus) + * + * Return the next available address for the specified bus + * + * Input parameters: + * bus - bus to assign an address for + * + * Return value: + * new address, <0 if error + ********************************************************************* */ + +int usb_new_address(usbbus_t *bus) +{ + int idx; + + for (idx = 1; idx < USB_MAX_DEVICES; idx++) { + if (bus->ub_devices[idx] == NULL) return idx; + } + + return -1; +} + +/* ********************************************************************* + * usb_set_address(dev,address) + * + * Set the address of a device. This also puts the device + * in the master device table for the bus and reconfigures the + * address of the control pipe. + * + * Input parameters: + * dev - device we're talking to + * address - new address (1..127) + * + * Return value: + * request status + ********************************************************************* */ + +int usb_set_address(usbdev_t *dev,int address) +{ + int res; + int idx; + usbpipe_t *pipe; + + res = usb_simple_request(dev,0x00,USB_REQUEST_SET_ADDRESS,address,0); + + if (res == 0) { + dev->ud_bus->ub_devices[address] = dev; + dev->ud_address = address; + for (idx = 0; idx < UD_MAX_PIPES; idx++) { + pipe = dev->ud_pipes[idx]; + if (pipe && pipe->up_hwendpoint) { + UBEPTSETADDR(dev->ud_bus,pipe->up_hwendpoint,address); + } + } + } + + return res; +} + +/* ********************************************************************* + * usb_set_ep0mps(dev,mps) + * + * Set the maximum packet size of endpoint zero (mucks with the + * endpoint in the host controller) + * + * Input parameters: + * dev - device we're talking to + * mps - max packet size for endpoint zero + * + * Return value: + * request status + ********************************************************************* */ + +int usb_set_ep0mps(usbdev_t *dev,int mps) +{ + usbpipe_t *pipe; + + pipe = dev->ud_pipes[0]; + if (pipe && pipe->up_hwendpoint) { + UBEPTSETMPS(dev->ud_bus,pipe->up_hwendpoint,mps); + } + if (pipe) { + pipe->up_mps = mps; + } + + return 0; +} + +/* ********************************************************************* + * usb_clear_stall(dev,epaddr) + * + * Clear a stall condition on the specified pipe + * + * Input parameters: + * dev - device we're talking to + * epaddr - endpoint address + * + * Return value: + * 0 if ok + * else error + ********************************************************************* */ +int usb_clear_stall(usbdev_t *dev,int epaddr) +{ + uint8_t *requestbuf; + usb_device_request_t *req; + usbreq_t *ur; + int res; + int pipeidx; + + /* + * Clear the stall in the hardware. + */ + + pipeidx = USB_EPADDR_TO_IDX(epaddr); + + UBEPTCLEARTOGGLE(dev->ud_bus,dev->ud_pipes[pipeidx]->up_hwendpoint); + + /* + * Do the "clear stall" request. Note that we should do this + * without calling usb_simple_request, since usb_simple_request + * may itself stall. + */ + + requestbuf = KMALLOC(32,0); + + req = (usb_device_request_t *) requestbuf; + + req->bmRequestType = 0x02; + req->bRequest = USB_REQUEST_CLEAR_FEATURE; + PUTUSBFIELD(req,wValue,0); /* ENDPOINT_HALT */ + PUTUSBFIELD(req,wIndex,epaddr); + PUTUSBFIELD(req,wLength,0); + + ur = usb_make_request(dev,0,requestbuf, + sizeof(usb_device_request_t), + UR_FLAG_SETUP); + res = usb_sync_request(ur); + usb_free_request(ur); + ur = usb_make_request(dev,0,requestbuf,0,UR_FLAG_STATUS_IN); + res = usb_sync_request(ur); + usb_free_request(ur); + + KFREE(requestbuf); + + return 0; +} + + + +/* ********************************************************************* + * usb_std_request(dev,bmRequestType,bRequest,wValue, + * wIndex,buffer,length) + * + * Do a standard control request on the control pipe, + * with the appropriate setup, data, and status phases. + * + * Input parameters: + * dev - dev we're talking to + * bmRequestType,bRequest,wValue,wIndex - fields for the + * USB request structure + * buffer - user buffer + * length - length of user buffer + * + * Return value: + * number of bytes transferred + ********************************************************************* */ + +int usb_std_request(usbdev_t *dev,uint8_t bmRequestType, + uint8_t bRequest,uint16_t wValue, + uint16_t wIndex,uint8_t *buffer,int length) +{ + usbpipe_t *pipe = dev->ud_pipes[0]; + usbreq_t *ur; + int res; + usb_device_request_t *req; + uint8_t *databuf = NULL; + + req = KMALLOC(32,0); + + if ((buffer != NULL) && (length !=0)) { + databuf = KMALLOC(length,0); + if (!(bmRequestType & USBREQ_DIR_IN)) { + memcpy(databuf,buffer,length); + } + else { + memset(databuf,0,length); + } + } + + req->bmRequestType = bmRequestType; + req->bRequest = bRequest; + PUTUSBFIELD(req,wValue,wValue); + PUTUSBFIELD(req,wIndex,wIndex); + PUTUSBFIELD(req,wLength,length); + + ur = usb_make_request(dev,0,(uint8_t *)req,sizeof(usb_device_request_t),UR_FLAG_SETUP); + res = usb_sync_request(ur); + usb_free_request(ur); + + if (length != 0) { + if (bmRequestType & USBREQ_DIR_IN) { + ur = usb_make_request(dev,0,databuf,length,UR_FLAG_IN); + } + else { + ur = usb_make_request(dev,0,databuf,length,UR_FLAG_OUT); + } + + res = usb_sync_request(ur); + + if (res == 4) { /* STALL */ + usb_clear_stall(dev,pipe->up_num); + usb_free_request(ur); + if (databuf) KFREE(databuf); + KFREE(req); + return 0; + } + + length = ur->ur_xferred; + usb_free_request(ur); + } + + if ((length != 0) && (databuf != NULL) && (bmRequestType & USBREQ_DIR_IN)) { + memcpy(buffer,databuf,length); + } + + if (bmRequestType & USBREQ_DIR_IN) { + ur = usb_make_request(dev,0,(uint8_t *)req,0,UR_FLAG_STATUS_OUT); + } + else { + ur = usb_make_request(dev,0,(uint8_t *)req,0,UR_FLAG_STATUS_IN); + } + + res = usb_sync_request(ur); + usb_free_request(ur); + + if (res == 4) { /* STALL */ + usb_clear_stall(dev,pipe->up_num); + if (databuf) KFREE(databuf); + KFREE(req); + return 0; + } + + if (databuf) KFREE(databuf); + KFREE(req); + + return length; +} + + + + +/* ********************************************************************* + * usb_get_descriptor(dev,reqtype,dsctype,dscidx,respbuf,buflen) + * + * Request a descriptor from the device. + * + * Input parameters: + * dev - device we're talking to + * reqtype - bmRequestType field for descriptor we want + * dsctype - descriptor type we want + * dscidx - index of descriptor we want (often zero) + * respbuf - response buffer + * buflen - length of response buffer + * + * Return value: + * number of bytes transferred + ********************************************************************* */ + +int usb_get_descriptor(usbdev_t *dev,uint8_t reqtype,int dsctype,int dscidx, + uint8_t *respbuf,int buflen) +{ + return usb_std_request(dev, + reqtype,USB_REQUEST_GET_DESCRIPTOR, + USB_DESCRIPTOR_TYPEINDEX(dsctype,dscidx), + 0, + respbuf,buflen); +} + +/* ********************************************************************* + * usb_get_string(dev,id,buf,maxlen) + * + * Request a string from the device, converting it from + * unicode to ascii (brutally). + * + * Input parameters: + * dev - device we're talking to + * id - string ID + * buf - buffer to receive string (null terminated) + * maxlen - length of buffer + * + * Return value: + * number of characters in returned string + ********************************************************************* */ + +int usb_get_string(usbdev_t *dev,int id,char *buf,int maxlen) +{ + int amtcopy; + uint8_t *respbuf; + int idx; + usb_string_descr_t *sdscr; + + respbuf = KMALLOC(maxlen*2+2,0); + sdscr = (usb_string_descr_t *) respbuf; + + /* + * First time just get the header of the descriptor so we can + * get the string length + */ + + amtcopy = usb_get_descriptor(dev,USBREQ_DIR_IN,USB_STRING_DESCRIPTOR_TYPE,id, + respbuf,2); + + /* + * now do it again to get the whole string. + */ + + if (maxlen > sdscr->bLength) maxlen = sdscr->bLength; + + amtcopy = usb_get_descriptor(dev,USBREQ_DIR_IN,USB_STRING_DESCRIPTOR_TYPE,id, + respbuf,maxlen); + + *buf = '\0'; + amtcopy = sdscr->bLength - 2; + if (amtcopy <= 0) return amtcopy; + + for (idx = 0; idx < amtcopy; idx+=2) { + *buf++ = sdscr->bString[idx]; + } + + *buf = '\0'; + + KFREE(respbuf); + + return amtcopy; +} + + + +/* ********************************************************************* + * usb_get_device_descriptor(dev,dscr,smallflg) + * + * Request the device descriptor for the device. This is often + * the first descriptor requested, so it needs to be done in + * stages so we can find out how big the control pipe is. + * + * Input parameters: + * dev - device we're talking to + * dscr - pointer to buffer to receive descriptor + * smallflg - TRUE to request just 8 bytes. + * + * Return value: + * number of bytes copied + ********************************************************************* */ + +int usb_get_device_descriptor(usbdev_t *dev,usb_device_descr_t *dscr,int smallflg) +{ + int res; + uint8_t *respbuf; + int amtcopy; + + /* + * Smallflg truncates the request 8 bytes. We need to do this for + * the very first transaction to a USB device in order to determine + * the size of its control pipe. Bad things will happen if you + * try to retrieve more data than the control pipe will hold. + * + * So, be conservative at first and get the first 8 bytes of the + * descriptor. Byte 7 is bMaxPacketSize0, the size of the control + * pipe. Then you can go back and submit a bigger request for + * everything else. + */ + + amtcopy = smallflg ? USB_CONTROL_ENDPOINT_MIN_SIZE : sizeof(usb_device_descr_t); + + respbuf = KMALLOC(64,0); + res = usb_get_descriptor(dev,USBREQ_DIR_IN,USB_DEVICE_DESCRIPTOR_TYPE,0,respbuf,amtcopy); + memcpy(dscr,respbuf,amtcopy); + KFREE(respbuf); + return res; + +} + +/* ********************************************************************* + * usb_get_config_descriptor(dev,dscr,idx,maxlen) + * + * Request the configuration descriptor from the device. + * + * Input parameters: + * dev - device we're talking to + * dscr - descriptor buffer (receives data from device) + * idx - index of config we want (usually zero) + * maxlen - total size of buffer to receive descriptor + * + * Return value: + * number of bytes copied + ********************************************************************* */ + +int usb_get_config_descriptor(usbdev_t *dev,usb_config_descr_t *dscr,int idx,int maxlen) +{ + int res; + uint8_t *respbuf; + + respbuf = KMALLOC(maxlen,0); + res = usb_get_descriptor(dev,USBREQ_DIR_IN, + USB_CONFIGURATION_DESCRIPTOR_TYPE,idx, + respbuf,maxlen); + memcpy(dscr,respbuf,maxlen); + KFREE(respbuf); + return res; + +} + + + +/* ********************************************************************* + * usb_get_device_status(dev,status) + * + * Request status from the device (status descriptor) + * + * Input parameters: + * dev - device we're talking to + * status - receives device_status structure + * + * Return value: + * number of bytes returned + ********************************************************************* */ + +int usb_get_device_status(usbdev_t *dev,usb_device_status_t *status) +{ + return usb_std_request(dev, + USBREQ_DIR_IN, + 0, + 0, + 0, + (uint8_t *) status, + sizeof(usb_device_status_t)); +} + + +/* ********************************************************************* + * usb_complete_request(ur,status) + * + * Called when a usb request completes - pass status to + * caller and call the callback if there is one. + * + * Input parameters: + * ur - usbreq_t to complete + * status - completion status + * + * Return value: + * nothing + ********************************************************************* */ + +void usb_complete_request(usbreq_t *ur,int status) +{ + ur->ur_status = status; + ur->ur_inprogress = 0; + if (ur->ur_callback) (*(ur->ur_callback))(ur); +} + + +/* ********************************************************************* + * usb_initroot(bus) + * + * Initialize the root hub for the bus - we need to do this + * each time a bus is configured. + * + * Input parameters: + * bus - bus to initialize + * + * Return value: + * nothing + ********************************************************************* */ + +void usb_initroot(usbbus_t *bus) +{ + usbdev_t *dev; + usb_driver_t *drv; + int addr; + int res; + uint8_t *buf; + int len; + usb_config_descr_t cfgdescr; + + /* + * Create a device for the root hub. + */ + + dev = usb_create_device(bus,0); + bus->ub_roothub = dev; + + /* + * Get the device descriptor. Make sure it's a hub. + */ + + res = usb_get_device_descriptor(dev,&(dev->ud_devdescr),TRUE); + + if (dev->ud_devdescr.bDeviceClass != USB_DEVICE_CLASS_HUB) { + printf("Error! Root device is not a hub!\n"); + return; + } + + /* + * Set up the max packet size for the control endpoint, + * then get the rest of the descriptor. + */ + + usb_set_ep0mps(dev,dev->ud_devdescr.bMaxPacketSize0); + res = usb_get_device_descriptor(dev,&(dev->ud_devdescr),FALSE); + + /* + * Obtain a new address and set the address of the + * root hub to this address. + */ + + addr = usb_new_address(dev->ud_bus); + res = usb_set_address(dev,addr); + + /* + * Get the configuration descriptor and all the + * associated interface and endpoint descriptors. + */ + + res = usb_get_config_descriptor(dev,&cfgdescr,0, + sizeof(usb_config_descr_t)); + if (res != sizeof(usb_config_descr_t)) { + printf("[a]usb_get_config_descriptor returns %d\n",res); + } + + len = GETUSBFIELD(&cfgdescr,wTotalLength); + buf = KMALLOC(len,0); + + res = usb_get_config_descriptor(dev,(usb_config_descr_t *)buf,0,len); + if (res != len) { + printf("[b]usb_get_config_descriptor returns %d\n",res); + } + + dev->ud_cfgdescr = (usb_config_descr_t *) buf; + + /* + * Select the configuration. Not really needed for our poor + * imitation root hub, but it's the right thing to do. + */ + + usb_set_configuration(dev,cfgdescr.bConfigurationValue); + + /* + * Find the driver for this. It had better be the hub + * driver. + */ + + drv = usb_find_driver(dev); + + /* + * Call the attach method. + */ + + (*(drv->udrv_attach))(dev,drv); + + /* + * Hub should now be operational. + */ + +} + + +/* ********************************************************************* + * usb_find_cfg_descr(dev,dtype,idx) + * + * Find a configuration descriptor - we retrieved all the config + * descriptors during discovery, this lets us dig out the one + * we want. + * + * Input parameters: + * dev - device we are talking to + * dtype - descriptor type to find + * idx - index of descriptor if there's more than one + * + * Return value: + * pointer to descriptor or NULL if not found + ********************************************************************* */ + +void *usb_find_cfg_descr(usbdev_t *dev,int dtype,int idx) +{ + uint8_t *endptr; + uint8_t *ptr; + usb_config_descr_t *cfgdscr; + + if (dev->ud_cfgdescr == NULL) return NULL; + + ptr = (uint8_t *) dev->ud_cfgdescr; + endptr = ptr + GETUSBFIELD((dev->ud_cfgdescr),wTotalLength); + + while (ptr < endptr) { + + cfgdscr = (usb_config_descr_t *) ptr; + + if (cfgdscr->bDescriptorType == dtype) { + if (idx == 0) return (void *) ptr; + else idx--; + } + + ptr += cfgdscr->bLength; + + } + + return NULL; +} diff --git a/cfe/cfe/usb/usbd.h b/cfe/cfe/usb/usbd.h new file mode 100644 index 0000000..73fde86 --- /dev/null +++ b/cfe/cfe/usb/usbd.h @@ -0,0 +1,308 @@ +/* ********************************************************************* + * Broadcom Common Firmware Environment (CFE) + * + * USB Device Layer definitions File: usbd.h + * + * Definitions for the USB device layer. + * + * 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 _PHYSADDR_T_DEFINED_ +#include "lib_physio.h" +#endif + +#include "usbchap9.h" + + +/* ********************************************************************* + * Forward declarations and opaque types + ********************************************************************* */ + +typedef struct usb_hc_s usb_hc_t; +typedef struct usb_ept_s usb_ept_t; +typedef struct usb_hcdrv_s usb_hcdrv_t; +typedef struct usbdev_s usbdev_t; +typedef struct usb_driver_s usb_driver_t; + +/* ********************************************************************* + * USB Bus structure - one of these per host controller + ********************************************************************* */ + +#define USB_MAX_DEVICES 128 + +typedef struct usbbus_s { + struct usbbus_s *ub_next; /* link to other buses */ + usb_hc_t *ub_hwsoftc; /* bus driver softc */ + usb_hcdrv_t *ub_hwdisp; /* bus driver dispatch */ + usbdev_t *ub_roothub; /* root hub device */ + usbdev_t *ub_devices[USB_MAX_DEVICES]; /* pointers to each device, idx by address */ + unsigned int ub_flags; /* flag bits */ + int ub_num; /* bus number */ +} usbbus_t; + +#define UB_FLG_NEEDSCAN 1 /* some device on bus needs scanning */ + +/* ********************************************************************* + * USB Pipe structure - one of these per unidirectional channel + * to an endpoint on a USB device + ********************************************************************* */ + +#define UP_TYPE_CONTROL 1 +#define UP_TYPE_BULK 2 +#define UP_TYPE_INTR 4 +#define UP_TYPE_ISOC 8 + +#define UP_TYPE_IN 128 +#define UP_TYPE_OUT 256 + +#define UP_TYPE_LOWSPEED 16 + +typedef struct usbpipe_s { + usb_ept_t *up_hwendpoint; /* OHCI-specific endpoint pointer */ + usbdev_t *up_dev; /* our device info */ + int up_num; /* pipe number */ + int up_mps; /* max packet size */ + int up_flags; +} usbpipe_t; + +/* ********************************************************************* + * USB device structure - one per device attached to the USB + * This is the basic structure applications will use to + * refer to a device. + ********************************************************************* */ + +#define UD_FLAG_HUB 0x0001 /* this is a hub device */ +#define UD_FLAG_ROOTHUB 0x0002 /* this is a root hub device */ +#define UD_FLAG_LOWSPEED 0x0008 /* this is a lowspeed device */ + +#define UD_MAX_PIPES 32 +#define USB_EPADDR_TO_IDX(addr) ((((addr)&0x80) >> 3) | ((addr) & 0x0F)) +//#define USB_EPADDR_TO_IDX(addr) USB_ENDPOINT_ADDRESS(addr) +//#define UD_MAX_PIPES 16 + +struct usbdev_s { + usb_driver_t *ud_drv; /* Driver's methods */ + usbbus_t *ud_bus; /* owning bus */ + int ud_address; /* USB address */ + usbpipe_t *ud_pipes[UD_MAX_PIPES]; /* pipes, 0 is the control pipe */ + struct usbdev_s *ud_parent; /* used for hubs */ + int ud_flags; + void *ud_private; /* private data for device driver */ + usb_device_descr_t ud_devdescr; /* device descriptor */ + usb_config_descr_t *ud_cfgdescr; /* config, interface, and ep descrs */ +}; + + +/* ********************************************************************* + * USB Request - basic structure to describe an in-progress + * I/O request. It associates buses, pipes, and buffers + * together. + ********************************************************************* */ + + +#define UR_FLAG_SYNC 0x8000 + +#define UR_FLAG_SETUP 0x0001 +#define UR_FLAG_IN 0x0002 +#define UR_FLAG_OUT 0x0004 +#define UR_FLAG_STATUS_IN 0x0008 /* status phase of a control WRITE */ +#define UR_FLAG_STATUS_OUT 0x0010 /* status phase of a control READ */ +#define UR_FLAG_SHORTOK 0x0020 /* short transfers are ok */ + + +typedef struct usbreq_s { + queue_t ur_qblock; + + /* + * pointers to our device and pipe + */ + + usbdev_t *ur_dev; + usbpipe_t *ur_pipe; + + /* + * stuff to keep track of the data we transfer + */ + + uint8_t *ur_buffer; + int ur_length; + int ur_xferred; + int ur_status; + int ur_flags; + + /* + * Stuff needed for the callback + */ + void *ur_ref; + int ur_inprogress; + int (*ur_callback)(struct usbreq_s *req); + + /* + * For use inside the ohci driver + */ + void *ur_tdqueue; + int ur_tdcount; +} usbreq_t; + + +/* ********************************************************************* + * Prototypes + ********************************************************************* */ + +int usb_create_pipe(usbdev_t *dev,int pipenum,int mps,int flags); +void usb_destroy_pipe(usbdev_t *dev,int pipenum); +int usb_set_address(usbdev_t *dev,int addr); +usbdev_t *usb_create_device(usbbus_t *bus,int lowspeed); +void usb_destroy_device(usbdev_t *dev); +usbreq_t *usb_make_request(usbdev_t *dev,int pipenum,uint8_t *buf,int length,int flags); +void usb_poll(usbbus_t *bus); +void usb_daemon(usbbus_t *bus); +int usb_cancel_request(usbreq_t *ur); +void usb_free_request(usbreq_t *ur); +int usb_queue_request(usbreq_t *ur); +int usb_wait_request(usbreq_t *ur); +int usb_sync_request(usbreq_t *ur); +int usb_get_descriptor(usbdev_t *dev,uint8_t reqtype,int dsctype,int dscidx,uint8_t *buffer,int buflen); +int usb_get_config_descriptor(usbdev_t *dev,usb_config_descr_t *dscr,int idx,int maxlen); +int usb_get_device_status(usbdev_t *dev,usb_device_status_t *status); +int usb_set_configuration(usbdev_t *dev,int config); +int usb_open_pipe(usbdev_t *dev,usb_endpoint_descr_t *epdesc); +int usb_simple_request(usbdev_t *dev,uint8_t reqtype,int bRequest,int wValue,int wIndex); +void usb_complete_request(usbreq_t *ur,int status); +int usb_get_device_descriptor(usbdev_t *dev,usb_device_descr_t *dscr,int smallflg); +int usb_set_ep0mps(usbdev_t *dev,int mps); +int usb_new_address(usbbus_t *bus); +int usb_get_string(usbdev_t *dev,int id,char *buf,int maxlen); +int usb_std_request(usbdev_t *dev,uint8_t bmRequestType, + uint8_t bRequest,uint16_t wValue, + uint16_t wIndex,uint8_t *buffer,int length); +void *usb_find_cfg_descr(usbdev_t *dev,int dtype,int idx); +void usb_delay_ms(usbbus_t *bus,int ms); +int usb_clear_stall(usbdev_t *dev,int pipe); + +void usb_scan(usbbus_t *bus); +void usbhub_map_tree(usbbus_t *bus,int (*func)(usbdev_t *dev,void *arg),void *arg); +void usbhub_dumpbus(usbbus_t *bus,uint32_t verbose); + +void usb_initroot(usbbus_t *bus); + + +/* ********************************************************************* + * Host Controller Driver + * Methods for abstracting the USB host controller from the + * rest of the goop. + ********************************************************************* */ + +struct usb_hcdrv_s { + usbbus_t * (*hcdrv_create)(physaddr_t regaddr); + void (*hcdrv_delete)(usbbus_t *); + int (*hcdrv_start)(usbbus_t *); + void (*hcdrv_stop)(usbbus_t *); + int (*hcdrv_intr)(usbbus_t *); + usb_ept_t * (*hcdrv_ept_create)(usbbus_t *,int usbaddr,int eptnum,int mps,int flags); + void (*hcdrv_ept_delete)(usbbus_t *,usb_ept_t *); + void (*hcdrv_ept_setmps)(usbbus_t *,usb_ept_t *,int mps); + void (*hcdrv_ept_setaddr)(usbbus_t *,usb_ept_t *,int addr); + void (*hcdrv_ept_cleartoggle)(usbbus_t *,usb_ept_t *); + int (*hcdrv_xfer)(usbbus_t *,usb_ept_t *uept,usbreq_t *ur); +}; + +#define UBCREATE(driver,addr) (*((driver)->hcdrv_create))(addr) +#define UBDELETE(bus) (*((bus)->ub_hwdisp->hcdrv_delete))(bus) +#define UBSTART(bus) (*((bus)->ub_hwdisp->hcdrv_start))(bus) +#define UBSTOP(bus) (*((bus)->ub_hwdisp->hcdrv_stop))(bus) +#define UBINTR(bus) (*((bus)->ub_hwdisp->hcdrv_intr))(bus) +#define UBEPTCREATE(bus,addr,num,mps,flags) (*((bus)->ub_hwdisp->hcdrv_ept_create))(bus,addr,num,mps,flags) +#define UBEPTDELETE(bus,ept) (*((bus)->ub_hwdisp->hcdrv_ept_delete))(bus,ept) +#define UBEPTSETMPS(bus,ept,mps) (*((bus)->ub_hwdisp->hcdrv_ept_setmps))(bus,ept,mps) +#define UBEPTSETADDR(bus,ept,addr) (*((bus)->ub_hwdisp->hcdrv_ept_setaddr))(bus,ept,addr) +#define UBEPTCLEARTOGGLE(bus,ept) (*((bus)->ub_hwdisp->hcdrv_ept_cleartoggle))(bus,ept) +#define UBXFER(bus,ept,xfer) (*((bus)->ub_hwdisp->hcdrv_xfer))(bus,ept,xfer) + +/* ********************************************************************* + * Devices - methods for abstracting things that _use_ USB + * (devices you can plug into the USB) - the entry points + * here are basically just for device discovery, since the top half + * of the actual driver will be device-specific. + ********************************************************************* */ + +struct usb_driver_s { + char *udrv_name; + int (*udrv_attach)(usbdev_t *,usb_driver_t *); + int (*udrv_detach)(usbdev_t *); +}; + +typedef struct usb_drvlist_s { + int udl_class; + int udl_vendor; + int udl_product; + usb_driver_t *udl_disp; +} usb_drvlist_t; + +extern usb_driver_t *usb_find_driver(usbdev_t *dev); + +#define CLASS_ANY -1 +#define VENDOR_ANY -1 +#define PRODUCT_ANY -1 + +void mydelay(int x); + +#define IS_HUB(dev) ((dev)->ud_devdescr.bDeviceClass == USB_DEVICE_CLASS_HUB) + +/* ********************************************************************* + * Error codes + ********************************************************************* */ + +#define USBD_ERR_OK 0 /* Request ok */ +#define USBD_ERR_STALLED -1 /* Endpoint is stalled */ +#define USBD_ERR_IOERROR -2 /* I/O error */ +#define USBD_ERR_HWERROR -3 /* Hardware failure */ +#define USBD_ERR_CANCELED -4 /* Request canceled */ +#define USBD_ERR_NOMEM -5 /* Out of memory */ +#define USBD_ERR_TIMEOUT -6 /* Request timeout */ + +/* ********************************************************************* + * Debug routines + ********************************************************************* */ + +void usb_dbg_dumpportstatus(int port,usb_port_status_t *portstatus,int level); +void usb_dbg_dumpdescriptors(usbdev_t *dev,uint8_t *ptr,int len); +void usb_dbg_dumpcfgdescr(usbdev_t *dev); +void usb_dbg_dumpeptdescr(usb_endpoint_descr_t * epdscr); diff --git a/cfe/cfe/usb/usbdebug.c b/cfe/cfe/usb/usbdebug.c new file mode 100644 index 0000000..14ed866 --- /dev/null +++ b/cfe/cfe/usb/usbdebug.c @@ -0,0 +1,307 @@ +/* ********************************************************************* + * Broadcom Common Firmware Environment (CFE) + * + * USB debugging code File: usbdebug.c + * + * This module contains debug code for USB. + * + * 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 <time.h> +#include <memory.h> +#include <stdint.h> +#include "usbhack.h" +#else +#include "lib_types.h" +#include "lib_string.h" +#include "lib_printf.h" +#endif + +#include "lib_malloc.h" +#include "lib_queue.h" +#include "usbchap9.h" +#include "usbd.h" + + +void usb_dbg_dumpportstatus(int port,usb_port_status_t *portstatus,int level) +{ + int idx; + uint16_t x; + + for (idx = 0; idx < level; idx++) printf(" "); + printf("PORT %d STATUS\n",port); + + for (idx = 0; idx < level; idx++) printf(" "); + x = GETUSBFIELD((portstatus),wPortStatus); + printf("wPortStatus = %04X ",x); + if (x & 1) printf("DevicePresent "); + if (x & 2) printf("Enabled "); + if (x & 4) printf("Suspend "); + if (x & 8) printf("OverCurrent "); + if (x & 16) printf("InReset "); + if (x & 256) printf("Powered "); + if (x & 512) printf("LowSpeed "); + printf("\n"); + for (idx = 0; idx < level; idx++) printf(" "); + x = GETUSBFIELD((portstatus),wPortChange); + printf("wPortChange = %04X ",x); + if (x & 1) printf("ConnectChange "); + if (x & 2) printf("EnableChange "); + if (x & 4) printf("SuspendChange "); + if (x & 8) printf("OverCurrentChange "); + if (x & 16) printf("ResetChange "); + printf("\n"); +} + +void usb_dbg_dumpeptdescr(usb_endpoint_descr_t * epdscr) +{ + printf("---------------------------------------------------\n"); + printf("ENDPOINT DESCRIPTOR\n"); + + printf("bLength = %d\n",epdscr->bLength); + printf("bDescriptorType = %d\n",epdscr->bDescriptorType); + printf("bEndpointAddr = %02X\n",epdscr->bEndpointAddress); + printf("bmAttrbutes = %02X\n",epdscr->bmAttributes); + printf("wMaxPacketSize = %d\n",GETUSBFIELD(epdscr,wMaxPacketSize)); + printf("bInterval = %d\n",epdscr->bInterval); +} + +static char *getstringmaybe(usbdev_t *dev,int string) +{ + static char buf[256]; + + return ""; + + if (string == 0) { + strcpy(buf,"none"); + return buf; + } + + memset(buf,0,sizeof(buf)); + + usb_get_string(dev,string,buf,sizeof(buf)); + + return buf; +} + +void usb_dbg_dumpdescriptors(usbdev_t *dev,uint8_t *ptr,int len) +{ + uint8_t *endptr; + usb_config_descr_t *cfgdscr; + usb_interface_descr_t *ifdscr; + usb_device_descr_t *devdscr; + usb_endpoint_descr_t *epdscr; + usb_hid_descr_t *hiddscr; + usb_hub_descr_t *hubdscr; + static char *eptattribs[4] = {"Control","Isoc","Bulk","Interrupt"}; + int idx; + + endptr = ptr + len; + + while (ptr < endptr) { + + cfgdscr = (usb_config_descr_t *) ptr; + + switch (cfgdscr->bDescriptorType) { + case USB_DEVICE_DESCRIPTOR_TYPE: + devdscr = (usb_device_descr_t *) ptr; + printf("---------------------------------------------------\n"); + printf("DEVICE DESCRIPTOR\n"); + printf("bLength = %d\n",devdscr->bLength); + printf("bDescriptorType = %d\n",devdscr->bDescriptorType); + printf("bcdUSB = %04X\n",GETUSBFIELD(devdscr,bcdUSB)); + printf("bDeviceClass = %d\n",devdscr->bDeviceClass); + printf("bDeviceSubClass = %d\n",devdscr->bDeviceSubClass); + printf("bDeviceProtocol = %d\n",devdscr->bDeviceProtocol); + printf("bMaxPktSize0 = %d\n",devdscr->bMaxPacketSize0); + if (endptr-ptr <= 8) break; + printf("idVendor = %04X (%d)\n", + GETUSBFIELD(devdscr,idVendor), + GETUSBFIELD(devdscr,idVendor)); + printf("idProduct = %04X (%d)\n", + GETUSBFIELD(devdscr,idProduct), + GETUSBFIELD(devdscr,idProduct)); + printf("bcdDevice = %04X\n",GETUSBFIELD(devdscr,bcdDevice)); + printf("iManufacturer = %d (%s)\n", + devdscr->iManufacturer, + getstringmaybe(dev,devdscr->iManufacturer)); + printf("iProduct = %d (%s)\n", + devdscr->iProduct, + getstringmaybe(dev,devdscr->iProduct)); + printf("iSerialNumber = %d (%s)\n", + devdscr->iSerialNumber, + getstringmaybe(dev,devdscr->iSerialNumber)); + printf("bNumConfigs = %d\n",devdscr->bNumConfigurations); + break; + case USB_CONFIGURATION_DESCRIPTOR_TYPE: + + cfgdscr = (usb_config_descr_t *) ptr; + printf("---------------------------------------------------\n"); + printf("CONFIG DESCRIPTOR\n"); + + printf("bLength = %d\n",cfgdscr->bLength); + printf("bDescriptorType = %d\n",cfgdscr->bDescriptorType); + printf("wTotalLength = %d\n",GETUSBFIELD(cfgdscr,wTotalLength)); + printf("bNumInterfaces = %d\n",cfgdscr->bNumInterfaces); + printf("bConfigValue = %d\n",cfgdscr->bConfigurationValue); + printf("iConfiguration = %d (%s)\n", + cfgdscr->iConfiguration, + getstringmaybe(dev,cfgdscr->iConfiguration)); + printf("bmAttributes = %02X\n",cfgdscr->bmAttributes); + printf("MaxPower = %d (%dma)\n",cfgdscr->MaxPower,cfgdscr->MaxPower*2); + break; + + case USB_INTERFACE_DESCRIPTOR_TYPE: + printf("---------------------------------------------------\n"); + printf("INTERFACE DESCRIPTOR\n"); + + ifdscr = (usb_interface_descr_t *) ptr; + + printf("bLength = %d\n",ifdscr->bLength); + printf("bDescriptorType = %d\n",ifdscr->bDescriptorType); + printf("bInterfaceNum = %d\n",ifdscr->bInterfaceNumber); + printf("bAlternateSet = %d\n",ifdscr->bAlternateSetting); + printf("bNumEndpoints = %d\n",ifdscr->bNumEndpoints); + printf("bInterfaceClass = %d\n",ifdscr->bInterfaceClass); + printf("bInterSubClass = %d\n",ifdscr->bInterfaceSubClass); + printf("bInterfaceProto = %d\n",ifdscr->bInterfaceProtocol); + printf("iInterface = %d (%s)\n", + ifdscr->iInterface, + getstringmaybe(dev,ifdscr->iInterface)); + break; + + case USB_ENDPOINT_DESCRIPTOR_TYPE: + printf("---------------------------------------------------\n"); + printf("ENDPOINT DESCRIPTOR\n"); + + epdscr = (usb_endpoint_descr_t *) ptr; + + printf("bLength = %d\n",epdscr->bLength); + printf("bDescriptorType = %d\n",epdscr->bDescriptorType); + printf("bEndpointAddr = %02X (%d,%s)\n", + epdscr->bEndpointAddress, + epdscr->bEndpointAddress & 0x0F, + (epdscr->bEndpointAddress & USB_ENDPOINT_DIRECTION_IN) ? "IN" : "OUT" + ); + printf("bmAttrbutes = %02X (%s)\n", + epdscr->bmAttributes, + eptattribs[epdscr->bmAttributes&3]); + printf("wMaxPacketSize = %d\n",GETUSBFIELD(epdscr,wMaxPacketSize)); + printf("bInterval = %d\n",epdscr->bInterval); + break; + + case USB_HID_DESCRIPTOR_TYPE: + printf("---------------------------------------------------\n"); + printf("HID DESCRIPTOR\n"); + + hiddscr = (usb_hid_descr_t *) ptr; + + printf("bLength = %d\n",hiddscr->bLength); + printf("bDescriptorType = %d\n",hiddscr->bDescriptorType); + printf("bcdHID = %04X\n",GETUSBFIELD(hiddscr,bcdHID)); + printf("bCountryCode = %d\n",hiddscr->bCountryCode); + printf("bNumDescriptors = %d\n",hiddscr->bNumDescriptors); + printf("bClassDescrType = %d\n",hiddscr->bClassDescrType); + printf("wClassDescrLen = %d\n",GETUSBFIELD(hiddscr,wClassDescrLength)); + break; + + case USB_HUB_DESCRIPTOR_TYPE: + printf("---------------------------------------------------\n"); + printf("HUB DESCRIPTOR\n"); + + hubdscr = (usb_hub_descr_t *) ptr; + + printf("bLength = %d\n",hubdscr->bDescriptorLength); + printf("bDescriptorType = %d\n",hubdscr->bDescriptorType); + printf("bNumberOfPorts = %d\n",hubdscr->bNumberOfPorts); + printf("wHubCharacters = %04X\n",GETUSBFIELD(hubdscr,wHubCharacteristics)); + printf("bPowerOnToPwrGd = %d\n",hubdscr->bPowerOnToPowerGood); + printf("bHubControlCurr = %d (ma)\n",hubdscr->bHubControlCurrent); + printf("bRemPwerMask[0] = %02X\n",hubdscr->bRemoveAndPowerMask[0]); + + break; + + default: + printf("---------------------------------------------------\n"); + printf("UNKNOWN DESCRIPTOR\n"); + printf("bLength = %d\n",cfgdscr->bLength); + printf("bDescriptorType = %d\n",cfgdscr->bDescriptorType); + printf("Data Bytes = "); + for (idx = 0; idx < cfgdscr->bLength; idx++) { + printf("%02X ",ptr[idx]); + } + printf("\n"); + + } + + ptr += cfgdscr->bLength; + + } +} + + +void usb_dbg_dumpcfgdescr(usbdev_t *dev) +{ + uint8_t buffer[512]; + int res; + int len; + usb_config_descr_t *cfgdscr; + + memset(buffer,0,sizeof(buffer)); + + cfgdscr = (usb_config_descr_t *) &buffer[0]; + + res = usb_get_config_descriptor(dev,cfgdscr,0,sizeof(usb_config_descr_t)); + if (res != sizeof(usb_config_descr_t)) { + printf("[a]usb_get_config_descriptor returns %d\n",res); + } + + len = GETUSBFIELD(cfgdscr,wTotalLength); + + res = usb_get_config_descriptor(dev,cfgdscr,0,len); + if (res != len) { + printf("[b]usb_get_config_descriptor returns %d\n",res); + } + + usb_dbg_dumpdescriptors(dev,&buffer[0],res); +} diff --git a/cfe/cfe/usb/usbdevs.c b/cfe/cfe/usb/usbdevs.c new file mode 100644 index 0000000..53a0754 --- /dev/null +++ b/cfe/cfe/usb/usbdevs.c @@ -0,0 +1,168 @@ +/* ********************************************************************* + * Broadcom Common Firmware Environment (CFE) + * + * USB Driver List File: usbdevs.c + * + * This module contains a table of supported USB devices and + * the routines to look up appropriate drivers given + * USB product, device, and class codes. + * + * 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 <memory.h> +#include <stdint.h> +#include "usbhack.h" +#else +#include "lib_types.h" +#include "lib_string.h" +#include "lib_printf.h" +#endif + +#include "lib_malloc.h" +#include "lib_queue.h" +#include "usbchap9.h" +#include "usbd.h" + +/* ********************************************************************* + * The list of drivers we support. If you add more drivers, + * list them here. + ********************************************************************* */ + +extern usb_driver_t usbhub_driver; +extern usb_driver_t usbhid_driver; +extern usb_driver_t usbmass_driver; +extern usb_driver_t usbserial_driver; +extern usb_driver_t usbeth_driver; + +usb_drvlist_t usb_drivers[] = { + + /* + * Hub driver + */ + + {USB_DEVICE_CLASS_HUB, VENDOR_ANY, PRODUCT_ANY, &usbhub_driver}, + + /* + * Keyboards and mice + */ + + {USB_DEVICE_CLASS_HUMAN_INTERFACE, VENDOR_ANY,PRODUCT_ANY, &usbhid_driver}, + + /* + * Mass storage devices + */ + + {USB_DEVICE_CLASS_STORAGE, VENDOR_ANY, PRODUCT_ANY, &usbmass_driver}, + + /* + * Serial ports + */ + + {USB_DEVICE_CLASS_VENDOR_SPECIFIC,0x557,0x2008,&usbserial_driver}, + + /* + * Ethernet Adapters + */ + + {USB_DEVICE_CLASS_VENDOR_SPECIFIC,0x506,0x4601,&usbeth_driver}, /* 3Com */ + {USB_DEVICE_CLASS_VENDOR_SPECIFIC,0x66b,0x2202,&usbeth_driver}, /* Linksys */ + {USB_DEVICE_CLASS_VENDOR_SPECIFIC,0x66b,0x2203,&usbeth_driver}, /* Linksys */ + {USB_DEVICE_CLASS_VENDOR_SPECIFIC,0x66b,0x2204,&usbeth_driver}, /* Linksys */ + {USB_DEVICE_CLASS_VENDOR_SPECIFIC,0x66b,0x2206,&usbeth_driver}, /* Linksys */ + {USB_DEVICE_CLASS_VENDOR_SPECIFIC,0x66b,0x400b,&usbeth_driver}, /* Linksys */ + {USB_DEVICE_CLASS_VENDOR_SPECIFIC,0x66b,0x200c,&usbeth_driver}, /* Linksys */ + {USB_DEVICE_CLASS_RESERVED,0xbda,0x8150,&usbeth_driver}, /* Realtek */ + {USB_DEVICE_CLASS_VENDOR_SPECIFIC,0x423,0x000a,&usbeth_driver}, /* CATC */ + {USB_DEVICE_CLASS_VENDOR_SPECIFIC,0x423,0x000c,&usbeth_driver}, /* Belkin */ + + {0,0,0,NULL} +}; + + +/* ********************************************************************* + * usb_find_driver(class,vendor,product) + * + * Find a suitable device driver to handle the specified + * class, vendor, or product. + * + * Input parameters: + * devdescr - device descriptor + * + * Return value: + * pointer to device driver or NULL + ********************************************************************* */ + +usb_driver_t *usb_find_driver(usbdev_t *dev) +{ + usb_device_descr_t *devdescr; + usb_interface_descr_t *ifdescr; + usb_drvlist_t *list; + int dclass,vendor,product; + + devdescr = &(dev->ud_devdescr); + + dclass = devdescr->bDeviceClass; + if (dclass == 0) { + ifdescr = usb_find_cfg_descr(dev,USB_INTERFACE_DESCRIPTOR_TYPE,0); + if (ifdescr) dclass = ifdescr->bInterfaceClass; + } + + vendor = (int) GETUSBFIELD(devdescr,idVendor); + product = (int) GETUSBFIELD(devdescr,idProduct); + + printf("USB: Locating Class %02X Vendor %04X Product %04X: ",dclass,vendor,product); + + list = usb_drivers; + while (list->udl_disp) { + if (((list->udl_class == dclass) || (list->udl_class == CLASS_ANY)) && + ((list->udl_vendor == vendor) || (list->udl_vendor == VENDOR_ANY)) && + ((list->udl_product == product) || (list->udl_product == PRODUCT_ANY))) { + printf("%s\n",list->udl_disp->udrv_name); + return list->udl_disp; + } + list++; + } + + printf("Not found.\n"); + + return NULL; +} diff --git a/cfe/cfe/usb/usbeth.c b/cfe/cfe/usb/usbeth.c new file mode 100644 index 0000000..bc5aeb0 --- /dev/null +++ b/cfe/cfe/usb/usbeth.c @@ -0,0 +1,850 @@ +/* ********************************************************************* + * Broadcom Common Firmware Environment (CFE) + * + * USB Ethernet File: usbeth.c + * + * Driver for USB Ethernet devices. + * + ********************************************************************* + * + * 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. + ********************************************************************* */ + +/* ********************************************************************* + * USB-Ethernet driver - CFE Network Layer Interfaces + * NOTE: Some of the device setup for the Admtek & Realtek devices + * was derived from reverse engineering! So these interfaces + * assume proper device operation. + ********************************************************************* */ + +#include "lib_types.h" +#include "lib_malloc.h" +#include "lib_string.h" +#include "lib_printf.h" + +#include "cfe_iocb.h" +#include "cfe_ioctl.h" +#include "cfe_timer.h" +#include "cfe_device.h" +#include "cfe_devfuncs.h" +#include "cfe_error.h" + +#include "usbd.h" +#include "usbeth.h" + +#if 0 +#define USBETH_TRACE( x, y ... ) xprintf( x, ##y ) +#else +#define USBETH_TRACE( x, y ... ) +#endif + +#define FAIL -1 + +static int Dev_cnt = 0; + + +/****************************************************************************** + Debug functions +******************************************************************************/ + +//#define DATA_DUMP +#ifdef DATA_DUMP +static void hexdump( unsigned char * src, int srclen, int rowlen, int rows ) +{ + unsigned char * rowptr; + unsigned char * srcstp; + unsigned char * byteptr; + + srcstp = src + srclen; + + for( rowptr = src; rowptr < src + rowlen * rows; rowptr += rowlen ) { + for( byteptr = rowptr; byteptr < rowptr + rowlen && byteptr < srcstp; byteptr++ ) { + xprintf( "%2X ", *byteptr ); + } + xprintf( "\n" ); + } + xprintf( "\n" ); +} +#endif + + +/* ********************************************************************* + * Interface functions for USB-Ethernet adapters + ********************************************************************* */ + +enum { PEGASUS, PEGASUS_II, NETMATE, REALTEK }; +enum { VEN_NONE, _3_COM, LINKSYS, LINKSYS_10, LINKSYS_100, + CATC_NM, BELKIN_CATC, LINKSYS_100M }; +static char * VENDOR_NAMES[] = +{ + "?", "3-COM", "LinkSys", "LinkSys-10TX", "LinkSys-100TX", + "CATC-Netmate", "Belkin/CATC", "Linksys-100M", "Yikes!" +}; + +typedef struct usbeth_softc_s +{ + usbdev_t *dev; + int bulk_inpipe; + int bulk_outpipe; + int dev_id; + int ven_code; + int embed_tx_len; + uint8_t mac_addr[6]; + usbreq_t *rx_ur; + uint8_t rxbuf[1600]; //artbitrary but enough for ethernet packet +} usbeth_softc_t; + + +/* ************************************** + * CATC I/F Functions + ************************************** */ + +#if 0 +static int catc_get_reg( usbdev_t *dev, int16_t reg, uint8_t *val ) +{ + return usb_std_request( dev, (USBREQ_TYPE_VENDOR | USBREQ_DIR_IN), + CATC_GET_REG, 0, reg, val, 1 ); +} +#endif + +static int catc_set_reg( usbdev_t *dev, int16_t reg, int16_t val ) +{ + return usb_std_request( dev, (USBREQ_TYPE_VENDOR | USBREQ_DIR_OUT), + CATC_SET_REG, val, reg, NULL, 0 ); +} + +static int catc_set_mem( usbdev_t *dev, int16_t addr, + uint8_t *data, int16_t len ) +{ + return usb_std_request( dev, (USBREQ_TYPE_VENDOR | USBREQ_DIR_OUT), + CATC_SET_MEM, 0, addr, data, len ); +} + +static int catc_get_mac_addr( usbdev_t *dev, uint8_t *mac_addr ) +{ + int status; + + status = usb_std_request( dev, (USBREQ_TYPE_VENDOR | USBREQ_DIR_IN), + CATC_GET_MAC_ADDR, 0, 0, mac_addr, 6 ); + return( status ); +} + +static void catc_init_device( usbeth_softc_t * softc ) +{ + usbdev_t *dev = softc->dev; + unsigned char *mcast_tbl; + + usb_std_request( dev, (USBREQ_TYPE_STD | USBREQ_REC_INTERFACE), + USB_REQUEST_SET_INTERFACE, + 1, //alt setting 1 + 0, NULL, 0 ); + + catc_set_reg(dev, CATC_TX_BUF_CNT_REG, 0x04 ); + catc_set_reg(dev, CATC_RX_BUF_CNT_REG, 0x10 ); + catc_set_reg(dev, CATC_ADV_OP_MODES_REG, 0x01 ); + catc_set_reg(dev, CATC_LED_CTRL_REG, 0x08 ); + + /* Enable broadcast rx via bit in mutlicast table */ + mcast_tbl = KMALLOC(64, 0); + memset( mcast_tbl, 0, 64 ); + mcast_tbl[31] = 0x80; //i.e. broadcast bit + catc_set_mem( dev, CATC_MCAST_TBL_ADDR, mcast_tbl, 64 ); + KFREE(mcast_tbl); + + //Read the adapter's MAC addr + catc_get_mac_addr( dev, softc->mac_addr ); +} + +static void catc_close_device( usbdev_t *dev ) +{ + // Now disable adapter from receiving packets + catc_set_reg( dev, CATC_ETH_CTRL_REG, 0 ); +} + +static void catc_open_device( usbeth_softc_t * softc ) +{ + int i; + + for(i = 0; i < 6; ++i) + catc_set_reg( softc->dev, (CATC_ETH_ADDR_0_REG - i), softc->mac_addr[i] ); + + // Now enable adapter to receive packets + catc_set_reg( softc->dev, CATC_ETH_CTRL_REG, 0x09 ); +} + +/* ************************************** + * PEGASUS I/F Functions + ************************************** */ + +static int peg_get_reg( usbdev_t *dev, int16_t reg, uint8_t *val, int16_t len ) +{ + return usb_std_request( dev, (USBREQ_TYPE_VENDOR | USBREQ_DIR_IN), + PEG_GET_REG, 0, reg, val, len ); +} + +static int peg_set_reg( usbdev_t *dev, int16_t reg, int16_t val ) +{ + unsigned char data = (uint8_t) val & 0xff; + + return usb_std_request( dev, (USBREQ_TYPE_VENDOR | USBREQ_DIR_OUT), + PEG_SET_REG, val, reg, &data, 1 ); +} + +static int peg_set_regs( usbdev_t *dev, int16_t reg, int8_t *vals, int16_t len ) +{ + return usb_std_request( dev, (USBREQ_TYPE_VENDOR | USBREQ_DIR_OUT), + PEG_SET_REG, 0, reg, vals, len ); +} + +static int peg_get_eep_word( usbdev_t *dev, int16_t ofs, uint8_t *val ) +{ + int status=0, tries=20; + uint8_t data[2]; + + if( peg_set_reg( dev, PEG_EEPROM_CTL_REG, 0 ) == FAIL ) + return FAIL; + if( peg_set_reg( dev, PEG_EEPROM_OFS_REG, ofs ) == FAIL ) + return FAIL; + if( peg_set_reg( dev, PEG_EEPROM_CTL_REG, 0x02 ) == FAIL ) //read + return FAIL; + while( --tries ) + { + if( peg_get_reg( dev, PEG_EEPROM_CTL_REG, data, 1 ) == FAIL ) + return FAIL; + if( data[0] & 0x04 ) + break; //eeprom data ready + } + if( !tries ) + { + xprintf( "Pegasus Eeprom read failed!\n" ); + return FAIL; + } + if( peg_get_reg( dev, PEG_EEPROM_DATA_REG, data, 2 ) == FAIL ) + return FAIL; + val[0] = data[0]; + val[1] = data[1]; + + return( status ); +} + +static int peg_get_mac_addr( usbdev_t *dev, uint8_t *mac_addr ) +{ + int i, status; + + for( i = 0; i < 3; ++i ) + { + status = peg_get_eep_word( dev, i, &mac_addr[i*2] ); + } + return( status ); +} + +static void peg_init_phy( usbdev_t *dev ) +{ //needed for earlier versions (before Rev B) of the USB-100TX adapters + static uint8_t phy_magic_wr[] = { 0, 4, 0, 0x1b }; + static uint8_t read_status[] = { 0, 0, 0, 1 }; + uint8_t data[4]; + + //reset the MAC ans set up GPIOs + peg_set_reg( dev, PEG_ETH_CTL1_REG, 0x08 ); + peg_get_reg( dev, PEG_ETH_CTL1_REG, data, 1 ); + + //do following steps to enable link activitiy LED + peg_set_reg( dev, PEG_GPIO1_REG, 0x26 ); + peg_set_reg( dev, PEG_GPIO0_REG, 0x24 ); + peg_set_reg( dev, PEG_GPIO0_REG, 0x26 ); + + //do following set of steps to enable LINK LED + memcpy( data, phy_magic_wr, 4 ); + peg_set_regs( dev, PEG_PHY_ADDR_REG, data, 4); //set up for magic word + peg_set_reg( dev, PEG_PHY_CTRL_REG, (0x1b|PHY_WRITE) ); + peg_get_reg( dev, PEG_PHY_CTRL_REG, data, 1 ); //read status of write + memcpy( data, read_status, 4 ); + peg_set_regs( dev, PEG_PHY_ADDR_REG, data, 4); //set up for phy status reg + peg_set_reg( dev, PEG_PHY_CTRL_REG, (1|PHY_READ) ); + peg_get_reg( dev, PEG_PHY_CTRL_REG, data, 1 ); //read status of read + peg_get_reg( dev, PEG_PHY_DATA_REG, data, 2 ); //read status regs +} + +static void peg_init_device( usbeth_softc_t * softc ) +{ + usbdev_t *dev = softc->dev; + + if( softc->dev_id == PEGASUS_II ) + peg_set_reg( dev, PEG_INT_PHY_REG, 0x02 ); //enable internal PHY + else + peg_init_phy( dev ); + + //Read the adapter's MAC addr + peg_get_mac_addr( dev, softc->mac_addr ); +} + +static void peg_close_device( usbdev_t *dev ) +{ + //Now disable adapter from receiving or transmitting packets + peg_set_reg( dev, PEG_ETH_CTL1_REG, 0 ); +} + +static void peg_open_device( usbeth_softc_t * softc ) +{ + usbdev_t *dev = softc->dev; + + //Now setup adapter's receiver with MAC address + peg_set_regs( dev, PEG_MAC_ADDR_0_REG, softc->mac_addr, 6 ); + + //Now enable adapter to receive and transmit packets + peg_set_reg( dev, PEG_ETH_CTL0_REG, 0xc1 ); + peg_set_reg( dev, PEG_ETH_CTL1_REG, 0x30 ); +} + +/* ************************************** + * REALTEK I/F Functions + ************************************** */ + +// +// ********** NOT FULLY WORKING YET!!!!!!!!!! *************** +// + +static int rtek_get_reg( usbdev_t *dev, int16_t reg, uint8_t *val, int16_t len ) +{ + return usb_std_request( dev, (USBREQ_TYPE_VENDOR | USBREQ_DIR_IN), + RTEK_REG_ACCESS, reg, 0, val, len ); +} + +static int rtek_set_reg( usbdev_t *dev, int16_t reg, int16_t val ) +{ + unsigned char data = (uint8_t) val & 0xff; + + return usb_std_request( dev, (USBREQ_TYPE_VENDOR | USBREQ_DIR_OUT), + RTEK_REG_ACCESS, reg, 0, &data, 1 ); +} + +static int rtek_get_mac_addr( usbdev_t *dev, uint8_t *mac_addr ) +{ + int status; + + status = rtek_get_reg( dev, RTEK_MAC_REG, mac_addr, 6 ); + + return( status ); +} + +static void rtek_init_device( usbeth_softc_t * softc ) +{ + int i; + usbdev_t *dev = softc->dev; + uint8_t val; + + //Reset the adapter + rtek_set_reg( dev, RTEK_CMD_REG, RTEK_RESET ); + for( i = 0; i < 10; ++i ) + { + rtek_get_reg( dev, RTEK_CMD_REG, &val, 1 ); + if( !(val & RTEK_RESET) ) + break; + usb_delay_ms( NULL, 1 ); + } + + //autoload the internal registers + rtek_set_reg( dev, RTEK_CMD_REG, RTEK_AUTOLOAD ); + for( i = 0; i < 50; ++i ) + { + rtek_get_reg( dev, RTEK_CMD_REG, &val, 1 ); + if( !(val & RTEK_AUTOLOAD) ) + break; + usb_delay_ms( NULL, 1 ); + } + + //Read the adapter's MAC addr + rtek_get_mac_addr( dev, softc->mac_addr ); +} + +static void rtek_close_device( usbdev_t *dev ) +{ + //Now disable adapter from receiving or transmitting packets + rtek_set_reg( dev, RTEK_CMD_REG, 0 ); +} + +static void rtek_open_device( usbeth_softc_t * softc ) +{ + //accept broadcast & own packets + rtek_set_reg( softc->dev, RTEK_RXCFG_REG, 0x0c ); + + //Now enable adapter to receive and transmit packets + rtek_set_reg( softc->dev, RTEK_CMD_REG, 0x0c ); +} + +//*********************** USB-ETH I/F Functions **************************** + +static const int ID_TBL[] = +{ + 0x0506, 0x4601, PEGASUS_II, _3_COM, //3-Com + 0x066b, 0x2202, PEGASUS_II, LINKSYS_10, //LinkSys + 0x066b, 0x2203, PEGASUS, LINKSYS_100, + 0x066b, 0x2204, PEGASUS, LINKSYS_100, + 0x066b, 0x2206, PEGASUS, LINKSYS, + 0x066b, 0x400b, PEGASUS_II, LINKSYS_10, + 0x066b, 0x200c, PEGASUS_II, LINKSYS_10, + 0x0bda, 0x8150, REALTEK, LINKSYS_100M, + 0x0423, 0x000a, NETMATE, CATC_NM, //CATC (Netmate I) + 0x0423, 0x000c, NETMATE, BELKIN_CATC, //Belkin & CATC (Netmate II) + -1 +}; + +static int usbeth_init_device( usbeth_softc_t * softc ) +{ + int i; + usb_device_descr_t dev_desc; + uint16_t vendor_id, device_id; + const int *ptr=ID_TBL; + + //find out which device is connected + usb_get_device_descriptor( softc->dev, &dev_desc, 0 ); + vendor_id = (dev_desc.idVendorHigh << 8) + dev_desc.idVendorLow; + device_id = (dev_desc.idProductHigh << 8) + dev_desc.idProductLow; + xprintf( "USB device: vendor id %04x, device id %04x\n", + vendor_id, device_id ); + + while( *ptr != -1 ) + { + if( (vendor_id == ptr[0]) && (device_id == ptr[1]) ) + { + softc->dev_id = ptr[2]; + softc->ven_code = ptr[3]; + break; + } + ptr += 4; + } + if( *ptr == -1 ) + { + xprintf( "Unrecognized USB-Ethernet device\n" ); + return -1; + } + + //init the adapter + if( softc->dev_id == NETMATE ) + { + catc_init_device( softc ); + softc->embed_tx_len = 1; + } + else + { + if( softc->dev_id == REALTEK ) + { + rtek_init_device( softc ); + softc->embed_tx_len = 0; + } + else + { + peg_init_device( softc ); + softc->embed_tx_len = 1; + } + } + + //display adapter info + xprintf( "%s USB-Ethernet Adapter (", VENDOR_NAMES[softc->ven_code] ); + for( i = 0; i < 6; ++i ) + xprintf( "%02x%s", softc->mac_addr[i], (i == 5) ? ")\n" : ":" ); + + return 0; +} + +static int usbeth_get_dev_addr( usbeth_softc_t * softc, uint8_t *mac_addr ) +{ + memcpy( mac_addr, softc->mac_addr, 6 ); + return 0; +} + +static void usbeth_queue_rx( usbeth_softc_t * softc ) +{ + softc->rx_ur = usb_make_request(softc->dev, softc->bulk_inpipe, + softc->rxbuf, sizeof(softc->rxbuf), + (UR_FLAG_IN | UR_FLAG_SHORTOK)); + usb_queue_request(softc->rx_ur); +} + +static void usbeth_close_device( usbeth_softc_t * softc ) +{ + if( softc->dev_id == NETMATE ) + catc_close_device( softc->dev ); + else if( softc->dev_id == REALTEK ) + rtek_close_device( softc->dev ); + else + peg_close_device( softc->dev ); +} + +static void usbeth_open_device( usbeth_softc_t * softc ) +{ + if( softc->dev_id == NETMATE ) + catc_open_device( softc ); + else if( softc->dev_id == REALTEK ) + rtek_open_device( softc ); + else + peg_open_device( softc ); + + //kick start the receive + usbeth_queue_rx( softc ); +} + +static int usbeth_data_rx( usbeth_softc_t * softc ) +{ + usb_poll(softc->dev->ud_bus); + return( !softc->rx_ur->ur_inprogress ); +} + +static int usbeth_get_eth_frame( usbeth_softc_t * softc, unsigned char * buf ) +{ + int len = 0; + + if( !softc->rx_ur->ur_inprogress ) + { + len = softc->rx_ur->ur_xferred; + memcpy( buf, softc->rxbuf, len ); + usb_free_request(softc->rx_ur); + usbeth_queue_rx( softc ); + } + else + xprintf( "Bulk data is not available yet!\n" ); + + return( len ); +} + +static int usbeth_send_eth_frame( usbeth_softc_t * softc, unsigned char * buf, int len ) +{ + usbreq_t *ur; + int txlen = len; + unsigned char * txbuf; + + if(softc->embed_tx_len) + { + txbuf = KMALLOC((len+2), 0); + txbuf[0] = txlen & 0xff; + txbuf[1] = (txlen >> 8) & 0xff; //1st two bytes...little endian + memcpy( &txbuf[2], buf, txlen ); + txlen += 2; + } + else + { + if( softc->dev_id == REALTEK ) + { + //Now for some Realtek chip workarounds + if( txlen < 60 ) //some strange limitation + txlen = 60; + else if( !(txlen % 64) ) //to handle module 64 packets + ++txlen; + } + txbuf = KMALLOC(txlen, 0); + memcpy( txbuf, buf, txlen ); + } + ur = usb_make_request(softc->dev, softc->bulk_outpipe, + txbuf, txlen, UR_FLAG_OUT); + usb_sync_request(ur); + usb_free_request(ur); + KFREE(txbuf); + + return( len ); +} + + +/* ********************************************************************* + * CFE-USB interfaces + ********************************************************************* */ + +/* ********************************************************************* + * usbeth_attach(dev,drv) + * + * This routine is called when the bus scan stuff finds a usb-ethernet + * device. We finish up the initialization by configuring the + * device and allocating our softc here. + * + * Input parameters: + * dev - usb device, in the "addressed" state. + * drv - the driver table entry that matched + * + * Return value: + * 0 + ********************************************************************* */ + +const cfe_driver_t usbethdrv; //forward declaration + +static int usbeth_attach(usbdev_t *dev,usb_driver_t *drv) +{ + usb_config_descr_t *cfgdscr = dev->ud_cfgdescr; + usb_endpoint_descr_t *epdscr; + usb_endpoint_descr_t *indscr = NULL; + usb_endpoint_descr_t *outdscr = NULL; + usb_interface_descr_t *ifdscr; + usbeth_softc_t *softc; + int idx; + + dev->ud_drv = drv; + + softc = (usbeth_softc_t *) KMALLOC( sizeof(usbeth_softc_t), 0 ); + if( softc == NULL ) + { + xprintf( "Failed to allocate softc memory.\n" ); + return -1; + } + memset( softc, 0, sizeof(usbeth_softc_t) ); + dev->ud_private = softc; + softc->dev = dev; + + ifdscr = usb_find_cfg_descr(dev,USB_INTERFACE_DESCRIPTOR_TYPE,0); + if (ifdscr == NULL) + { + xprintf("USBETH: ERROR...no interace descriptor\n"); + return -1; + } + + + for (idx = 0; idx < 2; idx++) + { + epdscr = usb_find_cfg_descr(dev,USB_ENDPOINT_DESCRIPTOR_TYPE,idx); + if (USB_ENDPOINT_DIR_OUT(epdscr->bEndpointAddress)) + outdscr = epdscr; + else + indscr = epdscr; + } + + if (!indscr || !outdscr) + { + /* + * Could not get descriptors, something is very wrong. + * Leave device addressed but not configured. + */ + xprintf("USBETH: ERROR...no endpoint descriptors\n"); + return -1; + } + + /* + * Choose the standard configuration. + */ + + usb_set_configuration(dev,cfgdscr->bConfigurationValue); + + // Quit if not able to initialize the device + if (usbeth_init_device(softc) < 0) + return -1; + + /* + * Open the pipes. + */ + + softc->bulk_inpipe = usb_open_pipe(dev,indscr); + softc->bulk_outpipe = usb_open_pipe(dev,outdscr); + + //Now attach this device as a CFE Ethernet device + cfe_attach( (cfe_driver_t *) &usbethdrv, softc, NULL, + usbethdrv.drv_description ); + + ++Dev_cnt; + + return 0; +} + +/* ********************************************************************* + * usbeth_detach(dev) + * + * This routine is called when the bus scanner notices that + * this device has been removed from the system. We should + * do any cleanup that is required. The pending requests + * will be cancelled automagically. + * + * Input parameters: + * dev - usb device + * + * Return value: + * 0 + ********************************************************************* */ + +static int usbeth_detach(usbdev_t *dev) +{ + usbeth_softc_t *softc = (usbeth_softc_t *) dev->ud_private; + + --Dev_cnt; + KFREE(softc); + + //*** SHOULD DETACH THE ETHERNET DEVICE TOO...LATER + + return 0; +} + +// CFE USB device interface structure +usb_driver_t usbeth_driver = +{ + "Ethernet Device", + usbeth_attach, + usbeth_detach +}; + + + +/* ********************************************************************* + * CFE-Ethernet device interfaces + ********************************************************************* */ + + +static int usbeth_ether_open(cfe_devctx_t *ctx) +{ + if( !Dev_cnt ) + return CFE_ERR_NOTREADY; + + USBETH_TRACE( "%s called.\n", __FUNCTION__ ); + usbeth_open_device( (usbeth_softc_t *) ctx->dev_softc ); + + return 0; +} + +static int usbeth_ether_read( cfe_devctx_t * ctx, iocb_buffer_t * buffer ) +{ + if( !Dev_cnt ) + return CFE_ERR_NOTREADY; + + buffer->buf_retlen = usbeth_get_eth_frame( (usbeth_softc_t *)ctx->dev_softc, + buffer->buf_ptr ); + +#ifdef DATA_DUMP + xprintf( "Incoming packet :\n" ); + hexdump( buffer->buf_ptr, buffer->buf_retlen, 16, + buffer->buf_retlen / 16 + 1 ); +#endif + + return 0; +} + + +static int usbeth_ether_inpstat( cfe_devctx_t * ctx, iocb_inpstat_t * inpstat ) +{ + if( !Dev_cnt ) + return CFE_ERR_NOTREADY; + + inpstat->inp_status = usbeth_data_rx( (usbeth_softc_t *) ctx->dev_softc ); + + return 0; +} + + +static int usbeth_ether_write(cfe_devctx_t *ctx,iocb_buffer_t *buffer) +{ + if( !Dev_cnt ) + return CFE_ERR_NOTREADY; + + // Block until hw notifies you data is sent. + usbeth_send_eth_frame( (usbeth_softc_t *) ctx->dev_softc, buffer->buf_ptr, + buffer->buf_length ); + + return 0; +} + + +static int usbeth_ether_ioctl(cfe_devctx_t *ctx,iocb_buffer_t *buffer) +{ + int retval = 0; + + if( !Dev_cnt ) + return CFE_ERR_NOTREADY; + + switch( (int)buffer->buf_ioctlcmd ) { + case IOCTL_ETHER_GETHWADDR: + USBETH_TRACE( "IOCTL_ETHER_GETHWADDR called.\n" ); + usbeth_get_dev_addr( (usbeth_softc_t *) ctx->dev_softc, + buffer->buf_ptr ); + break; + case IOCTL_ETHER_SETHWADDR: + xprintf( "IOCTL_ETHER_SETHWADDR not implemented.\n" ); + break; +#if 0 + case IOCTL_ETHER_GETSPEED: + xprintf( "GETSPEED not implemented.\n" ); + retval = -1; + break; + case IOCTL_ETHER_SETSPEED: + xprintf( "SETSPEED not implemented.\n" ); + retval = -1; + break; + case IOCTL_ETHER_GETLINK: + xprintf( "GETLINK not implemented.\n" ); + retval = -1; + break; + case IOCTL_ETHER_GETLOOPBACK: + xprintf( "GETLOOPBACK not implemented.\n" ); + retval = -1; + break; + case IOCTL_ETHER_SETLOOPBACK: + xprintf( "SETLOOPBACK not implemented.\n" ); + retval = -1; + break; +#endif + default: + xprintf( "Invalid IOCTL to usbeth_ether_ioctl.\n" ); + retval = -1; + } + + return retval; +} + + +static int usbeth_ether_close(cfe_devctx_t *ctx) +{ + if( !Dev_cnt ) + return CFE_ERR_NOTREADY; + + USBETH_TRACE( "%s called.\n", __FUNCTION__ ); + usbeth_close_device( (usbeth_softc_t *) ctx->dev_softc ); + + return 0; +} + + +// CFE ethernet device interface structures +const static cfe_devdisp_t usbeth_ether_dispatch = +{ + usbeth_ether_open, + usbeth_ether_read, + usbeth_ether_inpstat, + usbeth_ether_write, + usbeth_ether_ioctl, + usbeth_ether_close, + NULL, + NULL +}; + +const cfe_driver_t usbethdrv = +{ + "USB-Ethernet Device", + "eth", + CFE_DEV_NETWORK, + &usbeth_ether_dispatch, + NULL, //probe...not needed +}; + diff --git a/cfe/cfe/usb/usbeth.h b/cfe/cfe/usb/usbeth.h new file mode 100644 index 0000000..54f32ae --- /dev/null +++ b/cfe/cfe/usb/usbeth.h @@ -0,0 +1,112 @@ +/* ********************************************************************* + * Broadcom Common Firmware Environment (CFE) + * + * USB Ethernet File: usbeth.h + * + * Driver for USB Ethernet devices. + * + ********************************************************************* + * + * 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. + ********************************************************************* */ + +/* ********************************************************************* + * USB-Ethernet adapter driver includes + ********************************************************************* */ + +#ifndef __usbeth_h__ +#define __usbeth_h__ + +/* ************************************** + * CATC Netmate adapter + ************************************** */ + +#define CATC_MCAST_TBL_ADDR 0xFA80 //in Netmate's SRAM +#define CATC_GET_MAC_ADDR 0xF2 +#define CATC_SET_REG 0xFA +#define CATC_GET_REG 0xFB +#define CATC_SET_MEM 0xFC + +#define CATC_TX_BUF_CNT_REG 0x20 +#define CATC_RX_BUF_CNT_REG 0x21 +#define CATC_ADV_OP_MODES_REG 0x22 +#define CATC_RX_FRAME_CNT_REG 0x24 + +#define CATC_ETH_CTRL_REG 0x60 +#define CATC_ENET_STATUS_REG 0x61 +#define CATC_ETH_ADDR_0_REG 0x67 // Byte #0 (leftmost) +#define CATC_LED_CTRL_REG 0x81 + + +/* ************************************** + * Admtek (PEGASUS II) adapter + ************************************** */ + +#define PEG_SET_REG 0xF1 +#define PEG_GET_REG 0xF0 + +#define PEG_MCAST_TBL_REG 0x08 +#define PEG_MAC_ADDR_0_REG 0x10 +#define PEG_EEPROM_OFS_REG 0x20 +#define PEG_EEPROM_DATA_REG 0x21 +#define PEG_EEPROM_CTL_REG 0x23 +#define PEG_PHY_ADDR_REG 0x25 +#define PEG_PHY_DATA_REG 0x26 //& 27 for 2 bytes +#define PEG_PHY_CTRL_REG 0x28 +#define PEG_ETH_CTL0_REG 0x00 +#define PEG_ETH_CTL1_REG 0x01 +#define PEG_ETH_CTL2_REG 0x02 +#define PEG_GPIO0_REG 0x7e +#define PEG_GPIO1_REG 0x7f +#define PEG_INT_PHY_REG 0x7b + +#define PHY_WRITE 0x20 +#define PHY_READ 0x40 + + +/* ************************************** + * Realtek adapter + ************************************** */ + +#define RTEK_REG_ACCESS 0x05 +#define RTEK_MAC_REG 0x0120 +#define RTEK_CMD_REG 0x012E +#define RTEK_RXCFG_REG 0x0130 +#define RTEK_RESET 0x10 +#define RTEK_AUTOLOAD 0x01 + + + +#endif //__usbeth_h_ diff --git a/cfe/cfe/usb/usbhack.c b/cfe/cfe/usb/usbhack.c new file mode 100644 index 0000000..647f213 --- /dev/null +++ b/cfe/cfe/usb/usbhack.c @@ -0,0 +1,528 @@ +/* ********************************************************************* + * Broadcom Common Firmware Environment (CFE) + * + * Main Module File: usbhack.c + * + * A crude test program to let us tinker with a USB controller + * installed in an X86 Linux PC. Eventually we'll clean up + * this stuff and incorporate it into CFE. + * + * 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. + ********************************************************************* */ + + +#include <stdio.h> +#include <unistd.h> +#include <stdlib.h> +#include <stdint.h> +#include <stdarg.h> +#include <sys/mman.h> +#include <sys/fcntl.h> +#include <signal.h> +#include <memory.h> +#include <time.h> + +#include "lib_malloc.h" +#include "lib_queue.h" +#include "usbchap9.h" +#include "usbd.h" +#include "ohci.h" + +/* ********************************************************************* + * Macros + ********************************************************************* */ + +#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)))) + +/* ********************************************************************* + * Externs + ********************************************************************* */ + +extern int usb_noisy; +extern int ohcidebug; + +extern usbdev_t *usbmass_dev; +extern int usbmass_read_sector(usbdev_t *dev,uint32_t sectornum,uint32_t seccnt, + uint8_t *buffer); + + +/* ********************************************************************* + * Play Area definitions + * + * We use /dev/mem to map two areas - the "play" area and the + * "device" area. + * + * The "play" area maps to the upper 1MB of physical memory. + * You need to calculate this address yourself based on your system + * setup. If you have 256MB of memory, in your lilo.conf file put + * the following line: + * + * append="mem=255M" + * + * where '255' is one less than your system memory size. this + * leaves 1MB out of Linux's physical memory map and therefore + * leaves it to you to play with. /dev/mem will map this + * uncached using the Pentium MTRR registers, but for playing + * around this will be fine. + * + * the second area is the "device area" - this is the address + * the PCI USB controller's BARs were mapped to. You can find + * this by looking through /proc/pci until you find: + * + * Bus 0, device 8, function 0: + * USB Controller: OPTi Unknown device (rev 16). + * Vendor id=1045. Device id=c861. + * Medium devsel. Fast back-to-back capable. IRQ 3. + * Master Capable. Latency=32. + * Non-prefetchable 32 bit memory at 0xd9100000 [0xd9100000]. + * + * The 0xd9100000 will probably be different on your system. + * + * Of course, to make this work you'll need to rebuild the kernel + * without USB support, if you're running a recent kernel. + * Fortunately(?), I've been using RH 6.2, no USB support there + * in the old 2.2 kernels. + * + * Finally, you'll need to run this program as root. Even if + * you mess with the permissions on /dev/mem, there are additional + * checks in the kernel, so you will lose. + * + * But, the good news is that it works well - I've never crashed + * my Linux box, and I can use gdb to debug programs. + * You will NOT be able to use GDB to display things in the + * play area - I believe GDB doesn't know how to deal with + * the uncached nature of the memory there. You can see stuff + * in the area by tracing through instructions that read the play + * area, and viewing the register contents. + ********************************************************************* */ + +#define PLAY_AREA_ADDR (255*1024*1024) /* EDIT ME */ +#define PLAY_AREA_SIZE (1024*1024) +int play_fd = -1; +uint8_t *play_area = MAP_FAILED; + +#define DEVICE_AREA_ADDR 0xd9100000 /* EDIT ME */ +#define DEVICE_AREA_SIZE 4096 +int dev_fd = -1; +uint8_t *device_area = MAP_FAILED; + +/* ********************************************************************* + * Globals + ********************************************************************* */ + + +usbbus_t *bus = NULL; +ohci_softc_t *ohci = NULL; +int running = 1; + +/* ********************************************************************* + * vtop(v) + * + * Given a virtual address in the play area, return its physical + * address. + * + * Input parameters: + * v - virtual address + * + * Return value: + * physical address + ********************************************************************* */ + + +uint32_t vtop(void *v) +{ + uint32_t p = (uint32_t) v; + + if (v == 0) return 0; + + p -= (uint32_t) play_area; + p += PLAY_AREA_ADDR; + + return p; +} + +/* ********************************************************************* + * ptov(v) + * + * Given a phyiscal address in the play area, return the virtual + * address. + * + * Input parameters: + * p - physical address + * + * + * Return value: + * virtual address (void pointer) + ********************************************************************* */ + +void *ptov(uint32_t p) +{ + if (p == 0) return 0; + + p -= PLAY_AREA_ADDR; + p += (uint32_t) play_area; + + return (void *) p; +} + +/* ********************************************************************* + * mydelay(x) + * + * delay for 'x' milliseconds. + * + * Input parameters: + * x - milliseconds + * + * Return value: + * nothing + ********************************************************************* */ + +void mydelay(int x) +{ + struct timespec ts; + + ts.tv_sec = 0; + ts.tv_nsec = x * 1000000; /* milliseconds */ + nanosleep(&ts,NULL); +} + +/* ********************************************************************* + * console_log(tmplt,...) + * + * Display a console log message - this is a CFE function + * transplanted here. + * + * Input parameters: + * tmplt - printf string args... + * + * Return value: + * nothing + ********************************************************************* */ + +void console_log(const char *tmplt,...) +{ + char buffer[256]; + va_list marker; + + va_start(marker,tmplt); + vsprintf(buffer,tmplt,marker); + va_end(marker); + printf("%s\n",buffer); +} + +/* ********************************************************************* + * init_devaccess() + * + * Open /dev/mem and create the play area + * + * Input parameters: + * nothing + * + * Return value: + * 0 if ok, else error + ********************************************************************* */ + +int init_devaccess(void) +{ + int idx; + + play_fd = open("/dev/mem",O_RDWR); + + if (play_fd < 0) { + perror("open"); + return -1; + } + + dev_fd = open("/dev/mem",O_RDWR | O_SYNC); + + if (dev_fd < 0) { + perror("open"); + close(play_fd); + play_fd = -1; + return -1; + } + + play_area = mmap(NULL, + PLAY_AREA_SIZE, + PROT_READ|PROT_WRITE,MAP_SHARED, + play_fd, + PLAY_AREA_ADDR); + + if (play_area != MAP_FAILED) { + printf("Play area mapped ok at address %p to %p\n",play_area,play_area+PLAY_AREA_SIZE-1); + for (idx = 0; idx < PLAY_AREA_SIZE; idx++) { + play_area[idx] = 0x55; + if (play_area[idx] != 0x55) printf("Offset %x doesn't work\n",idx); + play_area[idx] = 0xaa; + if (play_area[idx] != 0xaa) printf("Offset %x doesn't work\n",idx); + play_area[idx] = 0x0; + if (play_area[idx] != 0x0) printf("Offset %x doesn't work\n",idx); + } + } + else { + perror("mmap"); + close(play_fd); + close(dev_fd); + play_fd = -1; + dev_fd = -1; + return -1; + } + + device_area = mmap(NULL, + DEVICE_AREA_SIZE, + PROT_READ|PROT_WRITE,MAP_SHARED, + dev_fd, + DEVICE_AREA_ADDR); + + if (device_area != MAP_FAILED) { + printf("Device area mapped ok at address %p\n",device_area); + } + else { + perror("mmap"); + munmap(play_area,PLAY_AREA_SIZE); + play_area = MAP_FAILED; + close(play_fd); + close(dev_fd); + play_fd = -1; + dev_fd = -1; + return -1; + } + + return 0; +} + + +/* ********************************************************************* + * uninit_devaccess() + * + * Turn off access to the /dev/mem area. this will also + * set the OHCI controller's state to reset if we were playing + * with it. + * + * Input parameters: + * nothing + * + * Return value: + * nothing + ********************************************************************* */ + +void uninit_devaccess(void) +{ + if (ohci->ohci_regs) { + OHCI_WRITECSR(ohci,R_OHCI_CONTROL,V_OHCI_CONTROL_HCFS(K_OHCI_HCFS_RESET)); + } + + if (play_area != MAP_FAILED) munmap(play_area,PLAY_AREA_SIZE); + if (device_area != MAP_FAILED) munmap(device_area,DEVICE_AREA_SIZE); + + if (play_fd > 0) close(play_fd); + if (dev_fd > 0) close(dev_fd); + + device_area = MAP_FAILED; + play_area = MAP_FAILED; + + dev_fd = -1; + play_fd = -1; +} + +/* ********************************************************************* + * sighandler() + * + * ^C handler - switch off OHCI controller + * + * Input parameters: + * sig - signal + * + * Return value: + * nothing + ********************************************************************* */ + +void sighandler(int sig) +{ + signal(SIGINT,SIG_DFL); + printf("Interrupted, controller reset\n"); + if (ohci->ohci_regs) { + OHCI_WRITECSR(ohci,R_OHCI_CONTROL,V_OHCI_CONTROL_HCFS(K_OHCI_HCFS_RESET)); + } + running = 0; +} + + +extern usb_hcdrv_t ohci_driver; + +/* ********************************************************************* + * xprintf(str) + * + * Called by lib_malloc, we need to supply it. + * + * Input parameters: + * str - string + * + * Return value: + * 0 + ********************************************************************* */ + +int xprintf(char *str) +{ + printf("%s",str); + return 0; +} + +/* ********************************************************************* + * main(argc,argv) + * + * Main test program + * + * Input parameters: + * argc,argv - guess. + * + * Return value: + * nothing + ********************************************************************* */ + + +int main(int argc,char *argv[]) +{ + int res; + memstats_t stats; + uint8_t *buffer; + + /* + * Parse command line args + */ + + for (res = 1; res < argc; res++) { + if (strcmp(argv[res],"-o") == 0) ohcidebug++; + if (strcmp(argv[res],"-u") == 0) usb_noisy++; + } + + /* + * Open the play area. + */ + + if (init_devaccess() < 0) { + printf("Could not map USB controller\n"); + } + + /* + * Establish signal and exit handlers + */ + + signal(SIGINT,sighandler); + atexit(uninit_devaccess); + + /* + * Initialize a buffer pool to point at the play area. + * the 'malloc' calls inside our driver will therefore + * allocate memory suitable for DMA, just like on real + * hardware. + */ + + KMEMINIT(play_area,PLAY_AREA_SIZE); + + buffer = KMALLOC(512,32); + + printf("-------------------------------------------\n\n"); + + /* + * Create the OHCI driver instance. + */ + + + bus = UBCREATE(&ohci_driver,(void *) device_area); + + /* + * Hack: retrieve copy of softc for our exception handler + */ + + ohci = (ohci_softc_t *) bus->ub_hwsoftc; + + /* + * Start the controller. + */ + + res = UBSTART(bus); + + if (res != 0) { + printf("Could not init hardware\n"); + UBSTOP(bus); + exit(1); + } + + /* + * Init the root hub + */ + + usb_initroot(bus); + + /* + * Main loop - just call interrupt routine to poll + */ + + while (usbmass_dev== NULL) { + usb_poll(bus); + usb_daemon(bus); + } + + for (res = 0; res < 1000; res++) { + usbmass_read_sector(usbmass_dev,0,1,buffer); + } + + printf("----- finished reading all sectors ----\n"); + + while (running) { + usb_poll(bus); + usb_daemon(bus); + } + + /* + * Clean up - get heap statistics to see if we + * screwed up. + */ + + res = KMEMSTATS(&stats); + if (res < 0) printf("Warning: heap is not consistent\n"); + else printf("Heap is ok\n"); + + exit(0); + +} diff --git a/cfe/cfe/usb/usbhack.h b/cfe/cfe/usb/usbhack.h new file mode 100644 index 0000000..9f95840 --- /dev/null +++ b/cfe/cfe/usb/usbhack.h @@ -0,0 +1,66 @@ +/* ********************************************************************* + * Broadcom Common Firmware Environment (CFE) + * + * usb test harness definitions File: usbhack.h + * + * Anything special required by the test harness is defined + * here. + * + * 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. + ********************************************************************* */ + +/* ********************************************************************* + * Constants + ********************************************************************* */ + + +#ifndef TRUE +#define TRUE 1 +#endif + +#ifndef FALSE +#define FALSE 0 +#endif + +/* ********************************************************************* + * Prototypes + ********************************************************************* */ + +void console_log(const char *tmplt,...); + diff --git a/cfe/cfe/usb/usbhack.mk b/cfe/cfe/usb/usbhack.mk new file mode 100644 index 0000000..361973e --- /dev/null +++ b/cfe/cfe/usb/usbhack.mk @@ -0,0 +1,23 @@ + +OBJS = usbhack.o lib_malloc.o lib_queue.o ohci.o usbd.o \ + usbdevs.o usbhub.o usbdebug.o usbhid.o usbmass.o usbserial.o + +CFLAGS = -I../include -g -Wall +VPATH = ../lib + +%.o : %.c + gcc $(CFLAGS) -c -o $@ $< + +all : usbhack + echo done + +usbhack : $(OBJS) + gcc -o usbhack $(OBJS) + +lib_malloc.o : lib_malloc.c + +usbhack.o : usbhack.c + + +clean : + rm -f *.o usbhack diff --git a/cfe/cfe/usb/usbhid.c b/cfe/cfe/usb/usbhid.c new file mode 100644 index 0000000..df84f4e --- /dev/null +++ b/cfe/cfe/usb/usbhid.c @@ -0,0 +1,664 @@ +/* ********************************************************************* + * Broadcom Common Firmware Environment (CFE) + * + * USB Human Interface Driver File: usbhid.c + * + * This module deals with keyboards, mice, etc. It's very simple, + * and only the "boot protocol" is supported. + * + * 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 <time.h> +#include <memory.h> +#include <stdint.h> +#include "usbhack.h" +#else +#include "lib_types.h" +#include "lib_string.h" +#include "lib_printf.h" +#include "cfe_console.h" +#endif + +#include "lib_malloc.h" +#include "lib_queue.h" +#include "usbchap9.h" +#include "usbd.h" + +/* ********************************************************************* + * Constants + ********************************************************************* */ + +#define HID_BOOT_PROTOCOL 0 +#define HID_REPORT_PROTOCOL 1 + +#define HID_DEVTYPE_UNKNOWN 0 +#define HID_DEVTYPE_KBD 1 +#define HID_DEVTYPE_MOUSE 2 +#define HID_DEVTYPE_MAX 2 + +#define UBR_KBD_MODS 0 +#define UBR_KBD_RSVD 1 +#define UBR_KBD_KEYS 2 +#define UBR_KBD_NUMKEYS 6 +#define UBR_KBD_MAX 8 + +#define KBD_MOD_LCTRL 0x01 +#define KBD_MOD_LSHIFT 0x02 +#define KBD_MOD_LALT 0x04 +#define KBD_MOD_LWIN 0x08 + +#define KBD_MOD_RCTRL 0x10 +#define KBD_MOD_RSHIFT 0x20 +#define KBD_MOD_RALT 0x40 +#define KBD_MOD_RWIN 0x80 + +/* ********************************************************************* + * Macros + ********************************************************************* */ + +#define usbhid_set_protocol(dev,protocol,ifc) \ + usb_simple_request(dev,0x21,0x0B,0,ifc) + + +/* ********************************************************************* + * Forward Definitions + ********************************************************************* */ + +static int usbhid_attach(usbdev_t *dev,usb_driver_t *drv); +static int usbhid_detach(usbdev_t *dev); + +/* ********************************************************************* + * Structures + ********************************************************************* */ + +typedef struct usbhid_softc_s { + int uhid_ipipe; + int uhid_ipipemps; + int uhid_devtype; + uint8_t uhid_imsg[UBR_KBD_MAX]; + uint8_t uhid_lastmsg[UBR_KBD_MAX]; + uint32_t uhid_shiftflags; +} usbhid_softc_t; + +usb_driver_t usbhid_driver = { + "Human-Interface Device", + usbhid_attach, + usbhid_detach +}; + +static char *usbhid_devtypes[3] = { + "Unknown", + "Keyboard", + "Mouse"}; + +#ifdef CFG_VGACONSOLE +extern int pcconsole_enqueue(uint8_t ch); +#endif + + +/* ********************************************************************* + * Constants for keyboard table + ********************************************************************* */ + +#define FLG_NUM 0x0001 /* Toggles: same as bits for LEDs */ +#define FLG_CAPS 0x0002 +#define FLG_SCROLL 0x0004 +#define FLG_SHIFT 0x0008 /* Shifts */ +#define FLG_CTRL 0x0100 +#define FLG_ALT 0x0200 +#define FLG_FKEY 0x0400 /* function keys */ +#define FLG_NKPD 0x0800 /* numeric keypad */ +#define FLG_ASCII 0x1000 /* regular ASCII character */ +#define FLG_NONE 0x2000 + + +/* ********************************************************************* + * Structures for keyboard table + ********************************************************************* */ + +#define KC_RESPLEN 4 +typedef struct keycode_s { + int kc_type; + char kc_normal[KC_RESPLEN]; + char kc_shifted[KC_RESPLEN]; + char kc_ctrl[KC_RESPLEN]; +} keycode_t; + + +/* ********************************************************************* + * Scan code conversion table + ********************************************************************* */ + +static keycode_t usbhid_scantable[] = { + { FLG_NONE, "", "", "" }, /* 0 */ + { FLG_NONE, "", "", "" }, /* 1 */ + { FLG_NONE, "", "", "" }, /* 2 */ + { FLG_NONE, "", "", "" }, /* 3 */ + { FLG_ASCII, "a", "A", "\001" }, /* 4 a */ + { FLG_ASCII, "b", "B", "\002" }, /* 5 b */ + { FLG_ASCII, "c", "C", "\003" }, /* 6 c */ + { FLG_ASCII, "d", "D", "\004" }, /* 7 d */ + { FLG_ASCII, "e", "E", "\005" }, /* 8 e */ + { FLG_ASCII, "f", "F", "\006" }, /* 9 f */ + { FLG_ASCII, "g", "G", "\007" }, /* 10 g */ + { FLG_ASCII, "h", "H", "\010" }, /* 11 h */ + { FLG_ASCII, "i", "I", "\011" }, /* 12 i */ + { FLG_ASCII, "j", "J", "\n" }, /* 13 j */ + { FLG_ASCII, "k", "K", "\013" }, /* 14 k */ + { FLG_ASCII, "l", "L", "\014" }, /* 15 l */ + { FLG_ASCII, "m", "M", "\r" }, /* 16 m */ + { FLG_ASCII, "n", "N", "\016" }, /* 17 n */ + { FLG_ASCII, "o", "O", "\017" }, /* 18 o */ + { FLG_ASCII, "p", "P", "\020" }, /* 19 p */ + { FLG_ASCII, "q", "Q", "\021" }, /* 20 q */ + { FLG_ASCII, "r", "R", "\022" }, /* 21 r */ + { FLG_ASCII, "s", "S", "\023" }, /* 22 s */ + { FLG_ASCII, "t", "T", "\024" }, /* 23 t */ + { FLG_ASCII, "u", "U", "\025" }, /* 24 u */ + { FLG_ASCII, "v", "V", "\026" }, /* 25 v */ + { FLG_ASCII, "w", "W", "\027" }, /* 26 w */ + { FLG_ASCII, "x", "X", "\030" }, /* 27 x */ + { FLG_ASCII, "y", "Y", "\031" }, /* 28 y */ + { FLG_ASCII, "z", "Z", "\032" }, /* 29 z */ + + { FLG_ASCII, "1", "!", "!" }, /* 30 1 */ + { FLG_ASCII, "2", "@", "\000" }, /* 31 2 */ + { FLG_ASCII, "3", "#", "#" }, /* 32 3 */ + { FLG_ASCII, "4", "$", "$" }, /* 33 4 */ + { FLG_ASCII, "5", "%", "%" }, /* 34 5 */ + { FLG_ASCII, "6", "^", "\036" }, /* 35 6 */ + { FLG_ASCII, "7", "&", "&" }, /* 36 7 */ + { FLG_ASCII, "8", "*", "\010" }, /* 37 8 */ + { FLG_ASCII, "9", "(", "(" }, /* 38 9 */ + { FLG_ASCII, "0", ")", ")" }, /* 39 0 */ + + { FLG_ASCII, "\r", "\r", "\n" }, /* 40 ENT */ + { FLG_ASCII, "\033", "\033", "\033" }, /* 41 ESC */ + { FLG_ASCII, "\177", "\177", "\010" }, /* 42 <- */ + { FLG_ASCII, "\t", "\177\t", "\t" }, /* 43 ->| */ + { FLG_ASCII, " ", " ", "\000" }, /* 44 SPC */ + + { FLG_ASCII, "-", "_", "\037" }, /* 45 - */ + { FLG_ASCII, "=", "+", "+" }, /* 46 = */ + { FLG_ASCII, "[", "{", "\033" }, /* 47 [ */ + { FLG_ASCII, "]", "}", "\035" }, /* 48 ] */ + { FLG_ASCII, "\\", "|", "\034" }, /* 49 \ */ + + { FLG_NONE, "", "", "" }, /* 50 pound */ + + { FLG_ASCII, ";", ":", ";" }, /* 51 ; */ + { FLG_ASCII, "'", "\"", "'" }, /* 52 ' */ + { FLG_ASCII, "`", "~", "`" }, /* 53 ` */ + { FLG_ASCII, ",", "<", "<" }, /* 54 , */ + { FLG_ASCII, ".", ">", ">" }, /* 55 . */ + { FLG_ASCII, "/", "?", "\037" }, /* 56 / */ + { FLG_CAPS, "", "", "" }, /* 57 CAPS */ + + { FLG_FKEY, "\033[M", "\033[Y", "\033[k" }, /* 58 f1 */ + { FLG_FKEY, "\033[N", "\033[Z", "\033[l" }, /* 59 f2 */ + { FLG_FKEY, "\033[O", "\033[a", "\033[m" }, /* 60 f3 */ + { FLG_FKEY, "\033[P", "\033[b", "\033[n" }, /* 61 f4 */ + { FLG_FKEY, "\033[Q", "\033[c", "\033[o" }, /* 62 f5 */ + { FLG_FKEY, "\033[R", "\033[d", "\033[p" }, /* 63 f6 */ + { FLG_FKEY, "\033[S", "\033[e", "\033[q" }, /* 64 f7 */ + { FLG_FKEY, "\033[T", "\033[f", "\033[r" }, /* 65 f8 */ + { FLG_FKEY, "\033[U", "\033[g", "\033[s" }, /* 66 f9 */ + { FLG_FKEY, "\033[V", "\033[h", "\033[t" }, /* 67 f10 */ + { FLG_FKEY, "\033[W", "\033[i", "\033[u" }, /* 68 f11 */ + { FLG_FKEY, "\033[X", "\033[j", "\033[v" }, /* 69 f12 */ + + { FLG_NONE, "", "", "" }, /* 70 prtsc */ + { FLG_SCROLL, "", "", "" }, /* 71 SCRLK */ + { FLG_NONE, "", "", "" }, /* 72 pause */ + { FLG_NONE, "", "", "" }, /* 73 KPins */ + { FLG_NONE, "", "", "" }, /* 74 KPhome */ + { FLG_NONE, "", "", "" }, /* 75 KPpgup */ + { FLG_NONE, "", "", "" }, /* 76 KPdel */ + { FLG_NONE, "", "", "" }, /* 77 KPend */ + { FLG_NONE, "", "", "" }, /* 78 KPpgdn */ + + { FLG_FKEY, "\033[C", "", "" }, /* 79 KPright */ + { FLG_FKEY, "\033[D", "", "" }, /* 80 KPleft */ + { FLG_FKEY, "\033[B", "", "" }, /* 81 KPdown */ + { FLG_FKEY, "\033[A", "", "" }, /* 82 KPup */ + + { FLG_NUM, "", "", "" }, /* 83 NUMLK */ + { FLG_NKPD, "/", "/", "/" }, /* 84 KP/ */ + { FLG_NKPD, "*", "*", "*" }, /* 85 KP* */ + { FLG_NKPD, "-", "-", "-" }, /* 86 KP- */ + { FLG_NKPD, "+", "+", "+" }, /* 87 KP+ */ + { FLG_NKPD, "\r", "\r", "\n" }, /* 88 KPent */ + + { FLG_NKPD, "1", "\033[F", "1" }, /* 89 KP1 */ + { FLG_NKPD, "2", "\033[B", "2" }, /* 90 KP2 */ + { FLG_NKPD, "3", "\033[G", "3" }, /* 91 KP3 */ + { FLG_NKPD, "4", "\033[D", "4" }, /* 92 KP4 */ + { FLG_NKPD, "5", "\033[E", "5" }, /* 93 KP5 */ + { FLG_NKPD, "6", "\033[C", "6" }, /* 94 KP6 */ + { FLG_NKPD, "7", "\033[H", "7" }, /* 95 KP7 */ + { FLG_NKPD, "8", "\033[A", "8" }, /* 96 KP8 */ + { FLG_NKPD, "9", "\033[I", "9" }, /* 97 KP9 */ + { FLG_NKPD, "0", "\033[L", "0" }, /* 98 KP0 */ + + { FLG_NKPD, ".", "\177", "." }, /* 99 KP. */ + + { FLG_NONE, "", "", "" }, /* 100 non\ */ + +}; + +#define usbhid_scantablesize (sizeof(usbhid_scantable)/sizeof(keycode_t)) + + +/* ********************************************************************* + * usbhid_kbd_mod1(uhid) + * + * Process modifier key changes for the current USB event, + * which was stored in uhid_imsg. Basically all this does + * is update uhid_shiftflags, converting the bits into the ones + * we use in our keyboard table. + * + * Input parameters: + * uhid - the hid softc. + * + * Return value: + * nothing + ********************************************************************* */ + +static void usbhid_kbd_mod1(usbhid_softc_t *uhid) +{ + uint8_t changed; + uint8_t mod; + + /* + * See if anything changed. + */ + + changed = (uhid->uhid_imsg[UBR_KBD_MODS] ^ uhid->uhid_lastmsg[UBR_KBD_MODS]); + if (changed == 0) return; + + /* + * Something changed. Reflect changes in our local copy of the + * shift state. + */ + + mod = uhid->uhid_imsg[UBR_KBD_MODS]; + + uhid->uhid_shiftflags &= ~(FLG_SHIFT|FLG_ALT|FLG_CTRL); + + if (mod & (KBD_MOD_LCTRL|KBD_MOD_RCTRL)) uhid->uhid_shiftflags |= FLG_CTRL; + if (mod & (KBD_MOD_LSHIFT|KBD_MOD_RSHIFT)) uhid->uhid_shiftflags |= FLG_SHIFT; + if (mod & (KBD_MOD_LALT|KBD_MOD_RALT)) uhid->uhid_shiftflags |= FLG_ALT; +} + +/* ********************************************************************* + * usbhid_kbd_scan1(uhid,scan,breakflg) + * + * Handle a single keyboard event. Using the scan code, look up + * the key in the table and convert it to one or more characters + * for the keyboard event queue. + * + * Input parameters: + * uhid - the hid softc + * scan - scan code from keyboard report + * breakflg - true if key is being released, false if pressed + * + * Return value: + * nothing + ********************************************************************* */ + +static void usbhid_kbd_scan1(usbhid_softc_t *uhid,uint8_t scan,int breakflg) +{ + keycode_t *code = 0; + char *str; + + /* + * Check scan code for reality. + */ + + if (scan >= usbhid_scantablesize) return; + code = &usbhid_scantable[scan]; + + /* + * If the change is a toggle, handle the toggle. These + * keys also deal with the LEDs on the keyboard. + */ + + if (code->kc_type & (FLG_CAPS|FLG_SCROLL|FLG_NUM)) { + if (!breakflg) uhid->uhid_shiftflags ^= code->kc_type; +// if (ks->ks_setleds) { +// (*(ks->ks_setleds))(ks,ks->ks_shiftflags & (FLG_CAPS|FLG_SCROLL|FLG_NUM)); +// } + } + + /* + * Regular keys - just look up in table and + * queue the characters to the upper layers. + */ + + if (code->kc_type & (FLG_ASCII | FLG_FKEY | FLG_NKPD)) { + if (uhid->uhid_shiftflags & (FLG_SHIFT|FLG_CAPS)) str = code->kc_shifted; + else if (uhid->uhid_shiftflags & FLG_CTRL) str = code->kc_ctrl; + else str = code->kc_normal; + if (!breakflg) { +#if CFG_VGACONSOLE + while (*str) { + pcconsole_enqueue(*str++); + } +#else + printf("%s",str); +#endif +#ifndef _CFE_ + fflush(stdout); +#endif + } + } + +} + + +/* ********************************************************************* + * usbhid_kbd_scan(uhid) + * + * Main processing routine for keyboard report messages. Once + * we've determined that it is a keyboard mesage, we end up + * here. The work involves seeing what new keys have arrived + * in the list (presses), and which ones are no longer there + * (releases). To do this, we us the current and previous + * report structure. + * + * Input parameters: + * uhid - the hid softc + * + * Return value: + * nothing + ********************************************************************* */ + +static void usbhid_kbd_scan(usbhid_softc_t *uhid) +{ + int n,o; + + /* + * Modifier keys (shift, alt, control) + */ + + if (uhid->uhid_imsg[UBR_KBD_MODS] ^ uhid->uhid_lastmsg[UBR_KBD_MODS]) { + usbhid_kbd_mod1(uhid); + } + + /* + * "Make" codes (keys pressed down) + * Look for keys in 'uhid_imsg' that are not in 'uhid_lastmsg' + */ + + for (n = UBR_KBD_KEYS; n < (UBR_KBD_KEYS + UBR_KBD_NUMKEYS); n++) { + if (uhid->uhid_imsg[n] == 0) break; /* no more keys */ + for (o = UBR_KBD_KEYS; o < (UBR_KBD_KEYS + UBR_KBD_NUMKEYS); o++) { + if (uhid->uhid_imsg[n] == uhid->uhid_lastmsg[o]) break; + } + if (o == (UBR_KBD_KEYS + UBR_KBD_NUMKEYS)) { /* key not found, must be pressed */ + usbhid_kbd_scan1(uhid,uhid->uhid_imsg[n],0); + } + } + + /* + * "Break" codes (keys released) + * Look for keys in 'uhid_lastmsg' that are not in 'uhid_imsg' + */ + + + for (n = UBR_KBD_KEYS; n < (UBR_KBD_KEYS + UBR_KBD_NUMKEYS); n++) { + if (uhid->uhid_lastmsg[n] == 0) break; /* no more keys */ + for (o = UBR_KBD_KEYS; o < (UBR_KBD_KEYS + UBR_KBD_NUMKEYS); o++) { + if (uhid->uhid_lastmsg[n] == uhid->uhid_imsg[o]) break; + } + if (o == (UBR_KBD_KEYS + UBR_KBD_NUMKEYS)) { /* key not found, must be released */ + usbhid_kbd_scan1(uhid,uhid->uhid_lastmsg[n],1); + } + } +} + + +/* ********************************************************************* + * usbhid_ireq_callback(ur) + * + * This routine is called when our interrupt transfer completes + * and there is report data to be processed. + * + * Input parameters: + * ur - usb request + * + * Return value: + * 0 + ********************************************************************* */ + +static int usbhid_ireq_callback(usbreq_t *ur) +{ + usbhid_softc_t *uhid = (ur->ur_dev->ud_private); + + /* + * If the driver is unloaded, the request will be cancelled. + */ + + if (ur->ur_status == 0xFF) { + usb_free_request(ur); + return 0; + } + + /* + * What we do now depends on the type of device. + */ + + switch (uhid->uhid_devtype) { + case HID_DEVTYPE_KBD: + /* + * Handle keyboard event + */ + usbhid_kbd_scan(uhid); + + /* + * Save old event to compare for next time. + */ + memcpy(uhid->uhid_lastmsg,uhid->uhid_imsg,8); + break; + + case HID_DEVTYPE_MOUSE: +#if 0 + /* + * No need to handle mice, but here's the good stuff. + */ + printf("Mouse: [%s %s %s] X:%d Y:%d Wheel:%d\n", + (ur->ur_buffer[0] & 1) ? "left" : "", + (ur->ur_buffer[0] & 4) ? "middle" : "", + (ur->ur_buffer[0] & 2) ? "right" : "", + (int)(signed char)ur->ur_buffer[1], + (int)(signed char)ur->ur_buffer[2], + (int)(signed char)ur->ur_buffer[3]); +#endif + break; + } + + /* + * Re-queue request to get next keyboard event. + */ + + usb_queue_request(ur); + + return 0; +} + + +/* ********************************************************************* + * usbhid_queue_intreq(dev,softc) + * + * Queue an interrupt request for this usb device. The + * driver will place this request on the queue that corresponds + * to the endpoint, and will call the callback routine when + * something happens. + * + * Input parameters: + * dev - usb device + * softc - the usb hid softc + * + * Return value: + * nothing + ********************************************************************* */ + +static void usbhid_queue_intreq(usbdev_t *dev,usbhid_softc_t *softc) +{ + usbreq_t *ur; + + ur = usb_make_request(dev, + softc->uhid_ipipe, + softc->uhid_imsg,softc->uhid_ipipemps, + UR_FLAG_IN); + + ur->ur_callback = usbhid_ireq_callback; + + usb_queue_request(ur); +} + + +/* ********************************************************************* + * usbhid_attach(dev,drv) + * + * This routine is called when the bus scan stuff finds a HID + * device. We finish up the initialization by configuring the + * device and allocating our softc here. + * + * Input parameters: + * dev - usb device, in the "addressed" state. + * drv - the driver table entry that matched + * + * Return value: + * 0 + ********************************************************************* */ + +static int usbhid_attach(usbdev_t *dev,usb_driver_t *drv) +{ + usb_config_descr_t *cfgdscr = dev->ud_cfgdescr; + usb_endpoint_descr_t *epdscr; + usb_interface_descr_t *ifdscr; + usbhid_softc_t *softc; + + dev->ud_drv = drv; + + softc = KMALLOC(sizeof(usbhid_softc_t),0); + memset(softc,0,sizeof(usbhid_softc_t)); + dev->ud_private = softc; + + epdscr = usb_find_cfg_descr(dev,USB_ENDPOINT_DESCRIPTOR_TYPE,0); + ifdscr = usb_find_cfg_descr(dev,USB_INTERFACE_DESCRIPTOR_TYPE,0); + + if (!epdscr || !ifdscr) { + /* + * Could not get descriptors, something is very wrong. + * Leave device addressed but not configured. + */ + return 0; + } + + /* + * Choose the standard configuration. + */ + + usb_set_configuration(dev,cfgdscr->bConfigurationValue); + + /* + * Set the protocol to the "boot" protocol, so we don't + * have to deal with fancy HID stuff. + */ + + usbhid_set_protocol(dev,HID_BOOT_PROTOCOL,ifdscr->bInterfaceNumber); + + /* + * Open the interrupt pipe. + */ + + softc->uhid_ipipe = usb_open_pipe(dev,epdscr); + softc->uhid_ipipemps = GETUSBFIELD(epdscr,wMaxPacketSize); + + /* + * Figure out the device type from the protocol. Keyboards, + * mice use this field to distinguish themselves. + */ + + softc->uhid_devtype = ifdscr->bInterfaceProtocol; + if (softc->uhid_devtype > HID_DEVTYPE_MAX) { + softc->uhid_devtype = HID_DEVTYPE_UNKNOWN; + } + + console_log("USBHID: %s Configured.\n", + usbhid_devtypes[softc->uhid_devtype]); + + /* + * Queue a transfer on the interrupt endpoint to catch + * our first characters. + */ + + usbhid_queue_intreq(dev,softc); + + return 0; +} + +/* ********************************************************************* + * usbhid_detach(dev) + * + * This routine is called when the bus scanner notices that + * this device has been removed from the system. We should + * do any cleanup that is required. The pending requests + * will be cancelled automagically. + * + * Input parameters: + * dev - usb device + * + * Return value: + * 0 + ********************************************************************* */ + +static int usbhid_detach(usbdev_t *dev) +{ + return 0; +} + + + diff --git a/cfe/cfe/usb/usbhub.c b/cfe/cfe/usb/usbhub.c new file mode 100644 index 0000000..1e571ed --- /dev/null +++ b/cfe/cfe/usb/usbhub.c @@ -0,0 +1,912 @@ +/* ********************************************************************* + * Broadcom Common Firmware Environment (CFE) + * + * USB Hub and device discovery code File: usbhub.c + * + * This module deals with hubs and device discovery. + * + * 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 <time.h> +#include <memory.h> +#include <stdint.h> +#include "usbhack.h" +#else +#include "lib_types.h" +#include "lib_string.h" +#include "lib_printf.h" +#include "cfe_console.h" +#endif + +#include "lib_malloc.h" +#include "lib_queue.h" +#include "usbchap9.h" +#include "usbd.h" + +/* ********************************************************************* + * Macros for common hub requests + ********************************************************************* */ + +#define usbhub_set_port_feature(dev,port,feature) \ + usb_simple_request(dev,0x23,USB_HUBREQ_SET_FEATURE,feature,port) + +#define usbhub_set_hub_feature(dev,feature) \ + usb_simple_request(dev,0x20,USB_HUBREQ_SET_FEATURE,feature,0) + +#define usbhub_clear_port_feature(dev,port,feature) \ + usb_simple_request(dev,0x23,USB_HUBREQ_CLEAR_FEATURE,feature,port) + +#define usbhub_clear_hub_feature(dev,feature) \ + usb_simple_request(dev,0x20,USB_HUBREQ_CLEAR_FEATURE,feature,0) + + +/* ********************************************************************* + * Externs + ********************************************************************* */ + +extern int usb_noisy; + +/* ********************************************************************* + * Forward declarations + ********************************************************************* */ + +static int usbhub_attach(usbdev_t *dev,usb_driver_t *drv); +static int usbhub_detach(usbdev_t *dev); + +/* ********************************************************************* + * Hub-specific data structures + ********************************************************************* */ + +#define UHUB_MAX_DEVICES 8 + +#define UHUB_FLG_NEEDSCAN 1 + +typedef struct usbhub_softc_s { + usb_hub_descr_t uhub_descr; + usb_hub_status_t uhub_status; + int uhub_ipipe; + int uhub_ipipemps; + int uhub_nports; + unsigned int uhub_flags; + uint8_t uhub_imsg[8]; + usbdev_t *uhub_devices[UHUB_MAX_DEVICES]; +} usbhub_softc_t; + +usb_driver_t usbhub_driver = { + "USB Hub", + usbhub_attach, + usbhub_detach +}; + + +/* ********************************************************************* + * usbhub_ireq_callback(ur) + * + * this routine is called when the transfer we queued to the + * interrupt endpoint on the hub completes. It means that + * *some* port on the hub needs attention. The data indicates + * which port, but for our purposes we don't really care - if + * we get this callback, we'll set a flag and re-probe the bus. + * + * Input parameters: + * ur - usbreq that completed + * + * Return value: + * 0 + ********************************************************************* */ + +static int usbhub_ireq_callback(usbreq_t *ur) +{ + int idx; + usbhub_softc_t *uhub = (ur->ur_dev->ud_private); + + /* + * Check to see if the request was cancelled by someone + * deleting our endpoint. + */ + + if (ur->ur_status == 0xFF) { + usb_free_request(ur); + return 0; + } + + /* + * Check to see if any of our ports need attention + */ + + for (idx = 1; idx <= uhub->uhub_nports; idx++) { + if (ur->ur_buffer[0] & (1<<idx)) { + + /* + * Mark the hub as needing a scan, and mark the bus as well + * so the top-level polling will notice. + */ + + uhub->uhub_flags |= UHUB_FLG_NEEDSCAN; + ur->ur_dev->ud_bus->ub_flags |= UB_FLG_NEEDSCAN; + } + } + + + /* + * Do NOT requeue the request here. We will do this + * during exploration. + */ + + usb_free_request(ur); + + return 0; +} + + +/* ********************************************************************* + * usbhub_get_hub_descriptor(dev,dscr,idx,maxlen) + * + * Obtain the hub descriptor (special for hubs) from the + * device. + * + * Input parameters: + * dev - usb device + * dscr - place to put hub descriptor + * idx - which hub descriptor to get (usually zero) + * maxlen - max # of bytes to return + * + * Return value: + * # of bytes returned + ********************************************************************* */ + +static int usbhub_get_hub_descriptor(usbdev_t *dev,usb_hub_descr_t *dscr,int idx,int maxlen) +{ + return usb_std_request(dev,0xA0, + USB_HUBREQ_GET_DESCRIPTOR, + 0,0, + (uint8_t *) dscr, + maxlen); +} + + +/* ********************************************************************* + * usbhub_get_hub_status(dev,status) + * + * Obtain the hub status (special for hubs) from the + * device. + * + * Input parameters: + * dev - usb device + * status - where to put hub status structure + * + * Return value: + * # of bytes returned + ********************************************************************* */ + +#if 0 +static int usbhub_get_hub_status(usbdev_t *dev,usb_hub_status_t *status) +{ + return usb_std_request(dev, + 0xA0, + 0x00, + 0, + 0, + (uint8_t *) status, + sizeof(usbhub_status_t)); +} +#endif + + +/* ********************************************************************* + * usbhub_get_port_status(dev,port,status) + * + * Obtain the port status for a particular port from + * device. + * + * Input parameters: + * dev - usb device + * port - 1-based port number + * status - where to put port status structure + * + * Return value: + * # of bytes returned + ********************************************************************* */ + +static int usbhub_get_port_status(usbdev_t *dev,int port,usb_port_status_t *status) +{ + return usb_std_request(dev, + 0xA3, + 0, + 0, + port, + (uint8_t *) status, + sizeof(usb_port_status_t)); +} + + +/* ********************************************************************* + * usbhub_queue_intreq(dev,softc) + * + * Queue the transfer to the interrupt pipe that will catch + * the hub's port status changes + * + * Input parameters: + * dev - usb device + * softc - hub-specific data + * + * Return value: + * nothing + ********************************************************************* */ + +static void usbhub_queue_intreq(usbdev_t *dev,usbhub_softc_t *softc) +{ + usbreq_t *ur; + + ur = usb_make_request(dev, + softc->uhub_ipipe, + softc->uhub_imsg,softc->uhub_ipipemps, + UR_FLAG_IN | UR_FLAG_SHORTOK); + + ur->ur_callback = usbhub_ireq_callback; + + usb_queue_request(ur); +} + + +/* ********************************************************************* + * usbhub_attach(dev,drv) + * + * This routine is called when the hub attaches to the system. + * We complete initialization for the hub and set things up so + * that an explore will happen soon. + * + * Input parameters: + * dev - usb device + * drv - driver structure + * + * Return value: + * 0 + ********************************************************************* */ + +static int usbhub_attach(usbdev_t *dev,usb_driver_t *drv) +{ + usb_device_status_t devstatus; + usb_config_descr_t *cfgdscr; + usb_interface_descr_t *ifdscr; + usb_endpoint_descr_t *epdscr; + usbhub_softc_t *softc; + + /* + * Remember the driver dispatch. + */ + + dev->ud_drv = drv; + + softc = KMALLOC(sizeof(usbhub_softc_t),0); + memset(softc,0,sizeof(usbhub_softc_t)); + dev->ud_private = softc; + + /* + * Dig out the data from the configuration descriptor + * (we got this from the device before attach time) + */ + + cfgdscr = dev->ud_cfgdescr; + epdscr = usb_find_cfg_descr(dev,USB_ENDPOINT_DESCRIPTOR_TYPE,0); + ifdscr = usb_find_cfg_descr(dev,USB_INTERFACE_DESCRIPTOR_TYPE,0); + + /* + * Get device status (is this really necessary?) + */ + + usb_get_device_status(dev,&devstatus); + + /* + * Set us to configuration index 0 + */ + + usb_set_configuration(dev,cfgdscr->bConfigurationValue); + + /* + * Get the hub descriptor. Get the first 8 bytes first, then get the rest + * if there is more. + */ + + if (usbhub_get_hub_descriptor(dev,&(softc->uhub_descr),0,USB_HUB_DESCR_SIZE) > USB_HUB_DESCR_SIZE) { + usbhub_get_hub_descriptor(dev,&(softc->uhub_descr),0,softc->uhub_descr.bDescriptorLength); + } + + /* + * remember stuff from the hub descriptor + */ + + softc->uhub_nports = softc->uhub_descr.bNumberOfPorts; + + /* + * Open the interrupt pipe + */ + + softc->uhub_ipipe = usb_open_pipe(dev,epdscr); + softc->uhub_ipipemps = GETUSBFIELD(epdscr,wMaxPacketSize); + + /* + * Mark the bus and the hub as needing service. + */ + + softc->uhub_flags |= UHUB_FLG_NEEDSCAN; + dev->ud_bus->ub_flags |= UB_FLG_NEEDSCAN; + + /* + * Okay, that's it. The top-level USB daemon will notice + * that the bus needs service and will invoke the exploration code. + * This may in turn require additional explores until + * everything settles down. + */ + + return 0; +} + + +/* ********************************************************************* + * usbhub_detach(dev) + * + * Called when a hub is removed from the system - we remove + * all subordinate devicees. + * + * Input parameters: + * dev - device (hub) that was removed + * + * Return value: + * 0 + ********************************************************************* */ + +static int usbhub_detach(usbdev_t *dev) +{ + usbhub_softc_t *hub; + usbdev_t *deldev; + int idx; + + if (!IS_HUB(dev)) return 0; /* should not happen */ + + hub = dev->ud_private; + for (idx = 0; idx < UHUB_MAX_DEVICES; idx++) { + deldev = hub->uhub_devices[idx]; + if (deldev) { + console_log("USB: Removing device attached to bus %d hub %d port %d", + dev->ud_bus->ub_num, + dev->ud_address,idx+1); + if (deldev->ud_drv) { + (*(deldev->ud_drv->udrv_detach))(deldev); + } + else { + if (usb_noisy > 0) { + console_log("USB: Detached device on bus %d hub %d port %d " + "has no methods", + dev->ud_bus->ub_num, + dev->ud_address,idx+1); + } + } + if (deldev->ud_cfgdescr) KFREE(deldev->ud_cfgdescr); + usb_destroy_device(deldev); + } + } + + KFREE(hub); /* remove softc */ + + return 0; +} + + + +/* ********************************************************************* + * usbhub_map_tree1(dev,level,func,arg) + * + * This routine is used in recursive device tree exploration. + * We call 'func' for each device at this tree, and descend + * when we run into hubs + * + * Input parameters: + * dev - current device pointer + * level - current nesting level + * func - function to call + * arg - argument to pass to function + * + * Return value: + * nothing + ********************************************************************* */ + +static void usbhub_map_tree1(usbdev_t *dev,int level, + int (*func)(usbdev_t *dev,void *arg),void *arg) +{ + usbhub_softc_t *hub; + int idx; + + (*func)(dev,arg); + + if (IS_HUB(dev)) { + hub = dev->ud_private; + for (idx = 0; idx < UHUB_MAX_DEVICES; idx++) { + if (hub->uhub_devices[idx]) { + usbhub_map_tree1(hub->uhub_devices[idx],level+1,func,arg); + } + } + } +} + +/* ********************************************************************* + * usbhub_map_tree(bus,func,arg) + * + * Call a function for each device in the tree + * + * Input parameters: + * bus - bus to scan + * func - function to call + * arg - argument to pass to function + * + * Return value: + * nothing + ********************************************************************* */ + +void usbhub_map_tree(usbbus_t *bus,int (*func)(usbdev_t *dev,void *arg),void *arg) +{ + usbhub_map_tree1(bus->ub_roothub,0,func,arg); +} + + +/* ********************************************************************* + * usbhub_dumpbus1(dev,arg) + * + * map function to dump devices in the device tree + * + * Input parameters: + * dev - device we're working on + * arg - argument from map_tree call + * + * Return value: + * 0 + ********************************************************************* */ + +static int usbhub_dumpbus1(usbdev_t *dev,void *arg) +{ + uint32_t *verbose = (uint32_t *) arg; + + if ((*verbose & 0x00FF) && (dev->ud_address != (*verbose & 0x00FF))) return 0; + + if (*verbose & 0x100) { + printf("============================================================================\n"); + } + + printf("Bus %d Device %d Class %d Vendor %04X Product %04X ", + dev->ud_bus->ub_num, + dev->ud_address, + dev->ud_devdescr.bDeviceClass, + GETUSBFIELD(&(dev->ud_devdescr),idVendor), + GETUSBFIELD(&(dev->ud_devdescr),idProduct)); + + + if (IS_HUB(dev)) { + printf("[HUB]\n"); + } + else { + printf("[DEVICE]\n"); + } + + if (*verbose & 0x100) { + usb_dbg_dumpdescriptors(dev,(uint8_t *) &(dev->ud_devdescr),dev->ud_devdescr.bLength); + usb_dbg_dumpcfgdescr(dev); + } + + return 0; +} + + +/* ********************************************************************* + * usbhub_dumpbus(bus,verbose) + * + * Dump information about devices on the USB bus. + * + * Input parameters: + * bus - bus to dump + * verbose - nonzero to display more info, like descriptors + * + * Return value: + * nothing + ********************************************************************* */ + +void usbhub_dumpbus(usbbus_t *bus,uint32_t verbose) +{ + usbhub_map_tree(bus,usbhub_dumpbus1,&verbose); +} + + + +/* ********************************************************************* + * usbhub_reset_devicee(dev,port,status) + * + * Reset a device on a hub port. This routine does a + * USB_PORT_FEATURE_RESET on the specified port, waits for the + * bit to clear, and returns. It is used to get a device into the + * DEFAULT state according to the spec. + * + * Input parameters: + * dev - hub device + * port - port number(1-based) + * status - place to return port_status structure after + * reset completes + * + * Return value: + * nothing + ********************************************************************* */ + +static void usbhub_reset_device(usbdev_t *dev,int port,usb_port_status_t *portstatus) +{ + console_log("USB: Resetting device on bus %d port %d",dev->ud_bus->ub_num,port); +#ifndef _CFE_ + fflush(stdout); +#endif + + usbhub_set_port_feature(dev,port,USB_PORT_FEATURE_RESET); + + usbhub_get_port_status(dev,port,portstatus); + + for (;;) { + usbhub_get_port_status(dev,port,portstatus); + if ((GETUSBFIELD((portstatus),wPortStatus) & USB_PORT_STATUS_RESET) == 0) break; + usb_delay_ms(dev->ud_bus,250); + } + usb_delay_ms(dev->ud_bus,250); + + usbhub_clear_port_feature(dev,port,USB_PORT_FEATURE_C_PORT_RESET); +} + + + +/* ********************************************************************* + * usbhub_scan_ports(dev,arg) + * + * Scan the ports on this hub for new or removed devices. + * + * Input parameters: + * dev - hub device + * arg - passed from bus scan main routines + * + * Return value: + * nothing + ********************************************************************* */ + +static void usbhub_scan_ports(usbdev_t *dev,void *arg) +{ + uint16_t current; + uint16_t changed; + usbhub_softc_t *softc; + int idx; + int res; + int len; + uint8_t *buf; + usbdev_t *newdev; + usb_driver_t *newdrv; + int addr; + usb_port_status_t portstatus; + usb_config_descr_t cfgdescr; + unsigned int powerondelay; + + if (!IS_HUB(dev)) return; /* should not happen. */ + + /* + * We know this is a hub. Get the softc back. + */ + + softc = (usbhub_softc_t *) dev->ud_private; + + powerondelay = ((unsigned int) softc->uhub_descr.bPowerOnToPowerGood)*2 + 20; + + /* + * Turn on the power to the ports whose power is not yet on. + */ + + for (idx = 0; idx < softc->uhub_nports; idx++) { + + usbhub_get_port_status(dev,idx+1,&portstatus); + + current = GETUSBFIELD((&portstatus),wPortStatus); + changed = GETUSBFIELD((&portstatus),wPortChange); + if (usb_noisy > 1) { + printf("BeforePowerup: port %d status %04X changed %04X\n",idx+1,current,changed); + } + + if (!(current & USB_PORT_STATUS_POWER)) { + if (usb_noisy > 1) console_log("USB: Powering up bus %d port %d", + dev->ud_bus->ub_num,idx+1); + usbhub_set_port_feature(dev,idx+1,USB_PORT_FEATURE_POWER); + usb_delay_ms(dev->ud_bus,powerondelay); + } + } + + /* + * Begin exploration at this level. + */ + + for (idx = 0; idx < softc->uhub_nports; idx++) { + + usbhub_get_port_status(dev,idx+1,&portstatus); + + current = GETUSBFIELD((&portstatus),wPortStatus); + changed = GETUSBFIELD((&portstatus),wPortChange); + + if (usb_noisy > 0) { + printf("USB: Explore: Bus %d Hub %d port %d status %04X changed %04X\n", + dev->ud_bus->ub_num, + dev->ud_address,idx+1,current,changed); + usb_dbg_dumpportstatus(idx+1,&portstatus,1); + } + + +// if (changed & USB_PORT_STATUS_RESET) { +// usbhub_clear_port_feature(dev,idx+1,USB_PORT_FEATURE_C_PORT_RESET); +// } + + if (changed & USB_PORT_STATUS_ENABLED) { + usbhub_clear_port_feature(dev,idx+1,USB_PORT_FEATURE_C_PORT_ENABLE); + } + + if (changed & USB_PORT_STATUS_CONNECT) { + /* + * A device was either connected or disconnected. + * Clear the status change first. + */ + + usbhub_clear_port_feature(dev,idx+1,USB_PORT_FEATURE_C_PORT_CONNECTION); + + if (current & USB_PORT_STATUS_CONNECT) { + + /* + * The device has been CONNECTED. + */ + + console_log("USB: New device connected to bus %d hub %d port %d", + dev->ud_bus->ub_num, + dev->ud_address,idx+1); + + /* + * Reset the device. Reuse our old port status structure + * so we get the latest status. Some devices do not report + * lowspeed until they are reset. + */ + + usbhub_reset_device(dev,idx+1,&portstatus); + current = GETUSBFIELD((&portstatus),wPortStatus); + changed = GETUSBFIELD((&portstatus),wPortChange); + + /* + * Create a device for this port. + */ + + newdev = usb_create_device(dev->ud_bus,(current & USB_PORT_STATUS_LOWSPD) ? 1 : 0); + + /* + * Get the device descriptor. + */ + + res = usb_get_device_descriptor(newdev,&(newdev->ud_devdescr),TRUE); + + if (usb_noisy > 0) usb_dbg_dumpdescriptors(newdev,(uint8_t *) &(newdev->ud_devdescr),8); + + /* + * Set up the max packet size for the control endpoint, + * then get the rest of the descriptor. + */ + + usb_set_ep0mps(newdev,newdev->ud_devdescr.bMaxPacketSize0); + res = usb_get_device_descriptor(newdev,&(newdev->ud_devdescr),FALSE); + + /* + * Obtain a new address and set the address of the + * root hub to this address. + */ + + addr = usb_new_address(newdev->ud_bus); + res = usb_set_address(newdev,addr); + + /* + * Get the configuration descriptor and all the + * associated interface and endpoint descriptors. + */ + + res = usb_get_config_descriptor(newdev,&cfgdescr,0, + sizeof(usb_config_descr_t)); + if (res != sizeof(usb_config_descr_t)) { + printf("[a]usb_get_config_descriptor returns %d\n",res); + } + + len = GETUSBFIELD(&cfgdescr,wTotalLength); + buf = KMALLOC(len,0); + + res = usb_get_config_descriptor(newdev,(usb_config_descr_t *)buf,0,len); + if (res != len) { + printf("[b]usb_get_config_descriptor returns %d\n",res); + } + + newdev->ud_cfgdescr = (usb_config_descr_t *) buf; + + if (usb_noisy > 0) usb_dbg_dumpdescriptors(newdev,buf,len); + + /* + * Point the hub at the devices it owns + */ + + softc->uhub_devices[idx] = newdev; + + /* + * Find the driver for this. It had better be the hub + * driver. + */ + + newdrv = usb_find_driver(newdev); + + /* + * Call the attach method. + */ + + if (newdrv) { + dev->ud_drv = newdrv; /* remember driver dispatch in device */ + (*(newdrv->udrv_attach))(newdev,newdrv); + } + } + + else { + + /* + * The device has been DISCONNECTED. + */ + + console_log("USB: Device disconnected from bus %d hub %d port %d", + dev->ud_bus->ub_num, + dev->ud_address,idx+1); + + /* + * Recover pointer to device below hub and clear + * this pointer. + */ + + newdev = softc->uhub_devices[idx]; /* Get device pointer */ + softc->uhub_devices[idx] = NULL; /* remove device from hub */ + + /* + * Deassign the USB device's address and then + * call detach method to free resources. Devices that + * do not have drivers will not have any methods. + */ + + if (newdev) { + if (newdev->ud_drv) { + (*(newdev->ud_drv->udrv_detach))(newdev); + } + else { + if (usb_noisy > 0) { + console_log("USB: Detached device on bus %d hub %d port %d " + "has no methods", + dev->ud_bus->ub_num, + dev->ud_address,idx+1); + } + } + + if (newdev->ud_cfgdescr) KFREE(newdev->ud_cfgdescr); + + usb_destroy_device(newdev); + } + + } + } + + } + + + /* + * Queue up a request for the interrupt pipe. This will catch further + * changes at this port. + */ + + usbhub_queue_intreq(dev,softc); + +} + +/* ********************************************************************* + * usbhub_scan1(dev,arg) + * + * Scan one device at this level, or descend if we run into a hub + * This is part of the device discovery code. + * + * Input parameters: + * dev - current device, maybe a hub + * arg - passed from main scan routine + * + * Return value: + * 0 + ********************************************************************* */ + + +static int usbhub_scan1(usbdev_t *dev,void *arg) +{ + usbhub_softc_t *hub; + + /* + * If the device is not a hub, we've reached the leaves of the + * tree. + */ + + if (!IS_HUB(dev)) return 0; + + /* + * Otherwise, scan the ports on this hub. + */ + + hub = dev->ud_private; + + if (hub->uhub_flags & UHUB_FLG_NEEDSCAN) { + hub->uhub_flags &= ~UHUB_FLG_NEEDSCAN; + usbhub_scan_ports(dev,arg); + } + + return 0; +} + +/* ********************************************************************* + * usb_scan(bus) + * + * Scan the bus looking for new or removed devices + * + * Input parameters: + * bus - bus to scan + * + * Return value: + * nothing + ********************************************************************* */ + +void usb_scan(usbbus_t *bus) +{ + /* + * Call our tree walker with the scan function. + */ + + usbhub_map_tree(bus,usbhub_scan1,NULL); +} + + + + diff --git a/cfe/cfe/usb/usbmain.c b/cfe/cfe/usb/usbmain.c new file mode 100644 index 0000000..c39a7f6 --- /dev/null +++ b/cfe/cfe/usb/usbmain.c @@ -0,0 +1,367 @@ +/* ********************************************************************* + * Broadcom Common Firmware Environment (CFE) + * + * Main Module File: usbmain.c + * + * Main module that invokes the top of the USB stack from CFE. + * + * 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. + ********************************************************************* */ + + +#include "lib_types.h" +#include "lib_malloc.h" +#include "lib_string.h" +#include "lib_printf.h" +#include "lib_queue.h" +#include "lib_physio.h" + +#include "cfe_timer.h" +#include "ui_command.h" + +#if CFG_PCI +#include "pcireg.h" +#include "pcivar.h" +#endif + +#include "usbchap9.h" +#include "usbd.h" + +#include "bsp_config.h" + + +/* ********************************************************************* + * Externs + ********************************************************************* */ + +extern usb_hcdrv_t ohci_driver; /* OHCI Driver dispatch */ + +extern int ohcidebug; /* OHCI debug control */ +extern int usb_noisy; /* USBD debug control */ + +int usb_init(void); /* forward */ +int ui_init_usbcmds(void); /* forward */ + +/* ********************************************************************* + * Globals + ********************************************************************* */ + +/* + * We keep track of the pointers to USB buses in globals. + * One entry in this array per USB bus (the Opti controller + * on the SWARM has two functions, so it's two buses) + */ + +#define USB_MAX_BUS 4 +int usb_buscnt = 0; +usbbus_t *usb_buses[USB_MAX_BUS]; + + +/* ********************************************************************* + * usb_cfe_timer(arg) + * + * This routine is called periodically by CFE's timer routines + * to give the USB subsystem some time. Basically we scan + * for work to do to manage configuration updates, and handle + * interrupts from the USB controllers. + * + * Input parameters: + * arg - value we passed when the timer was initialized + * (not used) + * + * Return value: + * nothing + ********************************************************************* */ + +static void usb_cfe_timer(void *arg) +{ + int idx; + static int in_poll = 0; + + /* + * We sometimes call the timer routines in here, which calls + * the polling loop. This code is not reentrant, so + * prevent us from running the interrupt routine or + * bus daemon while we are already in there. + */ + + if (in_poll) return; + + /* + * Do not allow nested "interrupts." + */ + + in_poll = 1; + + for (idx = 0; idx < usb_buscnt; idx++) { + if (usb_buses[idx]) { + usb_poll(usb_buses[idx]); + usb_daemon(usb_buses[idx]); + } + } + + /* + * Okay to call polling again. + */ + + in_poll = 0; +} + + +/* ********************************************************************* + * usb_init_one_ohci(addr) + * + * Initialize one USB controller. + * + * Input parameters: + * addr - physical address of OHCI registers + * + * Return value: + * 0 if ok + * else error + ********************************************************************* */ +static int usb_init_one_ohci(uint32_t addr) +{ + usbbus_t *bus; + int res; + + bus = UBCREATE(&ohci_driver, addr); + + if (bus == NULL) { + printf("USB: Could not create OHCI driver structure for controller at 0x%08X\n",addr); + return -1; + } + + bus->ub_num = usb_buscnt; + + res = UBSTART(bus); + + if (res != 0) { + printf("USB: Could not init OHCI controller at 0x%08X\n",addr); + UBSTOP(bus); + return -1; + } + else { + usb_buses[usb_buscnt++] = bus; + usb_initroot(bus); + } + + return 0; +} + +#if CFG_PCI +/* ********************************************************************* + * usb_init_pci_ohci() + * + * Initialize all PCI-based OHCI controllers + * + * Input parameters: + * nothing + * + * Return value: + * 0 if ok + * else error + ********************************************************************* */ +static int usb_init_pci_ohci(void) +{ + int res; + pcitag_t tag; + uint32_t pciclass; + physaddr_t bar; + int idx; + + idx = 0; + + while (pci_find_class(PCI_CLASS_SERIALBUS,idx,&tag) == 0) { + pciclass = pci_conf_read(tag,PCI_CLASS_REG); + if ((PCI_SUBCLASS(pciclass) == PCI_SUBCLASS_SERIALBUS_USB) && + (PCI_INTERFACE(pciclass) == 0x10)) { + bar = (physaddr_t) pci_conf_read(tag,PCI_MAPREG_START); + pci_tagprintf(tag,"OHCI USB controller found at %08X\n",(uint32_t) bar); + + /* On the BCM1250, this sets the address to "match bits" mode, + which eliminates the need for byte swaps of data to/from the registers. */ + bar |= 0x20000000; + + res = usb_init_one_ohci(bar); + if (res < 0) break; + } + idx++; + } + + return 0; +} +#endif + + + +/* ********************************************************************* + * usb_init() + * + * Initialize the USB subsystem + * + * Input parameters: + * nothing + * + * Return value: + * 0 if ok + * else error code + ********************************************************************* */ + +int usb_init(void) +{ + static int initdone = 0; + + if (initdone) { + printf("USB has already been initialized.\n"); + return -1; + } + + printf("Initializing USB.\n"); + + initdone = 1; + + usb_buscnt = 0; + +#if CFG_PCI + usb_init_pci_ohci(); +#endif + +#if CFG_USB_OHCI_BASE + usb_init_one_ohci(CFG_USB_OHCI_BASE); +#endif + + cfe_bg_add(usb_cfe_timer,NULL); + + return 0; +} + + + +static int ui_cmd_usb_start(ui_cmdline_t *cmd,int argc,char *argv[]) +{ + int res = 0; + + if (cmd_sw_isset(cmd,"-o")) ohcidebug++; + if (cmd_sw_isset(cmd,"-oo")) ohcidebug+=2; + if (cmd_sw_isset(cmd,"-u")) usb_noisy++; + if (cmd_sw_isset(cmd,"-uu")) usb_noisy+=2; + + if (usb_buscnt == 0) { + res = usb_init(); + } + + return res; +} + + +static int ui_cmd_usb_show(ui_cmdline_t *cmd,int argc,char *argv[]) +{ + int busnum; + int devnum; + char *x; + int idx; + uint32_t arg; + + x = cmd_getarg(cmd,1); + if (!x) devnum = 0; + else devnum = atoi(x); + + x = cmd_getarg(cmd,0); + if (!x) x = "*"; + busnum = atoi(x); + + if (busnum >= usb_buscnt) { + printf("Invalid bus number, %d USB Buses currently configured.\n",usb_buscnt); + return -1; + } + + arg = cmd_sw_isset(cmd,"-v") ? 0x100 : 0; + arg |= (devnum & 0xFF); + + if (x[0] == '*') { + for (idx = 0; idx < usb_buscnt; idx++) { + usbhub_dumpbus(usb_buses[idx],arg); + } + } + else { + usbhub_dumpbus(usb_buses[busnum],arg); + } + + return 0; + +} + +/* ********************************************************************* + * ui_init_usbcmds(void) + * + * Initialize the USB commands + * + * Input parameters: + * nothing + * + * Return value: + * 0 + ********************************************************************* */ + +int ui_init_usbcmds(void) +{ + cmd_addcmd("usb init", + ui_cmd_usb_start, + NULL, + "Initialize the USB controller.", + "usb init", + "-o;OHCI debug messages|" + "-oo;more OHCI debug messages|" + "-u;USBD debug messages|" + "-uu;more USBD debug messages"); + + + cmd_addcmd("show usb", + ui_cmd_usb_show, + NULL, + "Display devices connected to USB bus.", + "usb show [bus [device]]\n\n" + "Displays the configuration descriptors for devices connected to the USB\n" + "If you specify a bus, the entire bus is displayed. If you specify the\n" + "device number as well, only the specified device is displayed\n", + "-v;Display descriptors from the devices"); + + return 0; +} diff --git a/cfe/cfe/usb/usbmass.c b/cfe/cfe/usb/usbmass.c new file mode 100644 index 0000000..ae3856d --- /dev/null +++ b/cfe/cfe/usb/usbmass.c @@ -0,0 +1,1199 @@ +/* ********************************************************************* + * Broadcom Common Firmware Environment (CFE) + * + * USB Mass-Storage driver File: usbmass.c + * + * This driver deals with mass-storage devices that support + * the SCSI Transparent command set and USB Bulk-Only protocol + * + * 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 <time.h> +#include <memory.h> +#include <stdint.h> +#include "usbhack.h" +#else +#include "lib_types.h" +#include "lib_string.h" +#include "lib_printf.h" +#include "cfe_timer.h" +#include "cfe_iocb.h" +#include "cfe_device.h" +#include "cfe_ioctl.h" +#include "cfe_error.h" +#include "cfe_console.h" +#endif + +#include "lib_malloc.h" +#include "lib_queue.h" +#include "usbchap9.h" +#include "usbd.h" + +/* ********************************************************************* + * USB Mass-Storage class Constants + ********************************************************************* */ + +#define USBMASS_CBI_PROTOCOL 0 +#define USBMASS_CBI_NOCOMPLETE_PROTOCOL 1 +#define USBMASS_BULKONLY_PROTOCOL 0x50 + +#define USBMASS_SUBCLASS_RBC 0x01 +#define USBMASS_SUBCLASS_SFF8020 0x02 +#define USBMASS_SUBCLASS_QIC157 0x03 +#define USBMASS_SUBCLASS_UFI 0x04 +#define USBMASS_SUBCLASS_SFF8070 0x05 +#define USBMASS_SUBCLASS_SCSI 0x06 + +#define USBMASS_CSW_PASS 0x00 +#define USBMASS_CSW_FAILED 0x01 +#define USBMASS_CSW_PHASEERR 0x02 + +#define USBMASS_CBW_SIGNATURE 0x43425355 +#define USBMASS_CSW_SIGNATURE 0x53425355 + +/* ********************************************************************* + * USB Mass-Storage class Structures + ********************************************************************* */ + +typedef struct usbmass_cbw_s { + uint8_t dCBWSignature0,dCBWSignature1,dCBWSignature2,dCBWSignature3; + uint8_t dCBWTag0,dCBWTag1,dCBWTag2,dCBWTag3; + uint8_t dCBWDataTransferLength0,dCBWDataTransferLength1, + dCBWDataTransferLength2,dCBWDataTransferLength3; + uint8_t bmCBWFlags; + uint8_t bCBWLUN; + uint8_t bCBWCBLength; + uint8_t CBWCB[16]; +} usbmass_cbw_t; + +typedef struct usbmass_csw_s { + uint8_t dCSWSignature0,dCSWSignature1,dCSWSignature2,dCSWSignature3; + uint8_t dCSWTag0,dCSWTag1,dCSWTag2,dCSWTag3; + uint8_t dCSWDataResidue0,dCSWDataResidue1,dCSWDataResidue2,dCSWDataResidue3; + uint8_t bCSWStatus; +} usbmass_csw_t; + +#define GETCBWFIELD(s,f) ((uint32_t)((s)->f##0) | ((uint32_t)((s)->f##1) << 8) | \ + ((uint32_t)((s)->f##2) << 16) | ((uint32_t)((s)->f##3) << 24)) +#define PUTCBWFIELD(s,f,v) (s)->f##0 = (v & 0xFF); \ + (s)->f##1 = ((v)>>8 & 0xFF); \ + (s)->f##2 = ((v)>>16 & 0xFF); \ + (s)->f##3 = ((v)>>24 & 0xFF); + + +int usbmass_request_sense(usbdev_t *dev); + +/* ********************************************************************* + * Linkage to CFE + ********************************************************************* */ + +#ifdef _CFE_ + +/* + * Softc for the CFE side of the disk driver. + */ +#define MAX_SECTORSIZE 2048 +typedef struct usbdisk_s { + uint32_t usbdisk_sectorsize; + uint32_t usbdisk_ttlsect; + uint32_t usbdisk_devtype; + int usbdisk_unit; +} usbdisk_t; + +/* + * This table points at the currently configured USB disk + * devices. This lets us leave the CFE half of the driver lying + * around while the USB devices come and go. We use the unit number + * from the original CFE attach to index this table, and devices + * that are not present are "not ready." + */ + +#define USBDISK_MAXUNITS 4 +static usbdev_t *usbdisk_units[USBDISK_MAXUNITS]; + +/* + * CFE device driver routine forwards + */ + +static void usbdisk_probe(cfe_driver_t *drv, + unsigned long probe_a, unsigned long probe_b, + void *probe_ptr); + +static int usbdisk_open(cfe_devctx_t *ctx); +static int usbdisk_read(cfe_devctx_t *ctx,iocb_buffer_t *buffer); +static int usbdisk_inpstat(cfe_devctx_t *ctx,iocb_inpstat_t *inpstat); +static int usbdisk_write(cfe_devctx_t *ctx,iocb_buffer_t *buffer); +static int usbdisk_ioctl(cfe_devctx_t *ctx,iocb_buffer_t *buffer); +static int usbdisk_close(cfe_devctx_t *ctx); + +/* + * CFE device driver descriptor + */ + +const static cfe_devdisp_t usbdisk_dispatch = { + usbdisk_open, + usbdisk_read, + usbdisk_inpstat, + usbdisk_write, + usbdisk_ioctl, + usbdisk_close, + NULL, + NULL +}; + +const cfe_driver_t usb_disk = { + "USB Disk", + "usbdisk", + CFE_DEV_DISK, + &usbdisk_dispatch, + usbdisk_probe +}; + + +#endif + + + +/* ********************************************************************* + * Forward Definitions + ********************************************************************* */ + +static int usbmass_attach(usbdev_t *dev,usb_driver_t *drv); +static int usbmass_detach(usbdev_t *dev); + +/* ********************************************************************* + * Structures + ********************************************************************* */ + +typedef struct usbmass_softc_s { + int umass_inpipe; + int umass_outpipe; + int umass_devtype; + uint32_t umass_curtag; + int umass_unit; +} usbmass_softc_t; + +usb_driver_t usbmass_driver = { + "Mass-Storage Device", + usbmass_attach, + usbmass_detach +}; + +usbdev_t *usbmass_dev = NULL; /* XX hack for testing only */ + +/* ********************************************************************* + * usbmass_mass_storage_reset(dev,ifc) + * + * Do a bulk-only mass-storage reset. + * + * Input parameters: + * dev - device to reset + * ifc - interface number to reset (bInterfaceNum) + * + * Return value: + * status + ********************************************************************* */ + +#define usbmass_mass_storage_reset(dev,ifc) \ + usb_simple_request(dev,0x21,0xFF,ifc,0) + +#if 0 +/* ********************************************************************* + * usbmass_get_max_lun(dev,lunp) + * + * Get maximum LUN from device + * + * Input parameters: + * dev - device to reset + * lunp - pointer to int to receive max lun + * + * Return value: + * status + ********************************************************************* */ + +static int usbmass_get_max_lun(usbdev_t *dev,int *lunp) +{ + uint8_t buf = 0; + int res; + + res = usb_std_request(dev,0xA1,0xFE,0,0,&buf,1); + + if (res < 0) return res; + + if (lunp) *lunp = (int) buf; + return 0; +} + +#endif + + +/* ********************************************************************* + * usbmass_stall_recovery(dev) + * + * Do whatever it takes to unstick a stalled mass-storage device. + * + * Input parameters: + * dev - usb device + * + * Return value: + * nothing + ********************************************************************* */ + +static void usbmass_stall_recovery(usbdev_t *dev) +{ + usbmass_softc_t *softc; + + softc = (usbmass_softc_t *) dev->ud_private; + + usb_clear_stall(dev,softc->umass_inpipe); + + usbmass_request_sense(dev); +} + + +/* ********************************************************************* + * usbmass_read_capacity(dev,sectornum,buffer) + * + * Reads a sector from the device. + * + * Input parameters: + * dev - usb device + * sectornum - sector number to read + * buffer - place to put sector we read + * + * Return value: + * status + ********************************************************************* */ + +int usbmass_request_sense(usbdev_t *dev) +{ + uint8_t *cbwcsw; + uint8_t *sector; + usbmass_cbw_t *cbw; + usbmass_csw_t *csw; + usbreq_t *ur; + usbmass_softc_t *softc; + int res; + + softc = (usbmass_softc_t *) dev->ud_private; + + cbwcsw = KMALLOC(64,32); + sector = KMALLOC(64,32); + + memset(sector,0,64); + + cbw = (usbmass_cbw_t *) cbwcsw; + csw = (usbmass_csw_t *) cbwcsw; + + /* + * Fill in the fields of the CBW + */ + + PUTCBWFIELD(cbw,dCBWSignature,USBMASS_CBW_SIGNATURE); + PUTCBWFIELD(cbw,dCBWTag,softc->umass_curtag); + PUTCBWFIELD(cbw,dCBWDataTransferLength,18); + cbw->bmCBWFlags = 0x80; /* IN */ + cbw->bCBWLUN = 0; + cbw->bCBWCBLength = 12; + cbw->CBWCB[0] = 0x3; /* REQUEST SENSE */ + cbw->CBWCB[1] = 0; + cbw->CBWCB[2] = 0; + cbw->CBWCB[3] = 0; + cbw->CBWCB[4] = 18; /* allocation length */ + cbw->CBWCB[5] = 0; + cbw->CBWCB[6] = 0; + cbw->CBWCB[7] = 0; + cbw->CBWCB[8] = 0; + cbw->CBWCB[9] = 0; + + softc->umass_curtag++; + + /* + * Send the CBW + */ + + ur = usb_make_request(dev,softc->umass_outpipe,(uint8_t *) cbw, + sizeof(usbmass_cbw_t),UR_FLAG_OUT); + res = usb_sync_request(ur); + usb_free_request(ur); + + /* + * Get the data + */ + + memset(sector,0,18); + ur = usb_make_request(dev,softc->umass_inpipe,sector, + 18,UR_FLAG_IN | UR_FLAG_SHORTOK); + res = usb_sync_request(ur); + usb_free_request(ur); + + /* + * Get the Status + */ + + memset(csw,0,sizeof(usbmass_csw_t)); + ur = usb_make_request(dev,softc->umass_inpipe,(uint8_t *) csw, + sizeof(usbmass_csw_t),UR_FLAG_IN); + res = usb_sync_request(ur); + usb_free_request(ur); + + KFREE(cbwcsw); + + KFREE(sector); + + return 0; + +} + +/* ********************************************************************* + * usbmass_read_sector(dev,sectornum,seccnt,buffer) + * + * Reads a sector from the device. + * + * Input parameters: + * dev - usb device + * sectornum - sector number to read + * seccnt - count of sectors to read + * buffer - place to put sector we read + * + * Return value: + * status + ********************************************************************* */ + +int usbmass_read_sector(usbdev_t *dev,uint32_t sectornum,uint32_t seccnt, + uint8_t *buffer); +int usbmass_read_sector(usbdev_t *dev,uint32_t sectornum,uint32_t seccnt, + uint8_t *buffer) +{ + uint8_t *cbwcsw; + uint8_t *sector; + usbmass_cbw_t *cbw; + usbmass_csw_t *csw; + usbreq_t *ur; + usbmass_softc_t *softc; + int res; + + softc = (usbmass_softc_t *) dev->ud_private; + + cbwcsw = KMALLOC(64,32); + sector = buffer; + + cbw = (usbmass_cbw_t *) cbwcsw; + csw = (usbmass_csw_t *) cbwcsw; + + /* + * Fill in the fields of the CBW + */ + + PUTCBWFIELD(cbw,dCBWSignature,USBMASS_CBW_SIGNATURE); + PUTCBWFIELD(cbw,dCBWTag,softc->umass_curtag); + PUTCBWFIELD(cbw,dCBWDataTransferLength,(512*seccnt)); + cbw->bmCBWFlags = 0x80; /* IN */ + cbw->bCBWLUN = 0; + cbw->bCBWCBLength = 10; + cbw->CBWCB[0] = 0x28; /* READ */ + cbw->CBWCB[1] = 0; + cbw->CBWCB[2] = (sectornum >> 24) & 0xFF; /* LUN 0 & MSB's of sector */ + cbw->CBWCB[3] = (sectornum >> 16) & 0xFF; + cbw->CBWCB[4] = (sectornum >> 8) & 0xFF; + cbw->CBWCB[5] = (sectornum >> 0) & 0xFF; + cbw->CBWCB[6] = 0; + cbw->CBWCB[7] = 0; + cbw->CBWCB[8] = seccnt; + cbw->CBWCB[9] = 0; + + softc->umass_curtag++; + + /* + * Send the CBW + */ + + ur = usb_make_request(dev,softc->umass_outpipe,(uint8_t *) cbw, + sizeof(usbmass_cbw_t),UR_FLAG_OUT); + res = usb_sync_request(ur); + usb_free_request(ur); + if (res == 4) { + usbmass_stall_recovery(dev); + KFREE(cbwcsw); + return -1; + } + + + /* + * Get the data + */ + + ur = usb_make_request(dev,softc->umass_inpipe,sector, + 512*seccnt,UR_FLAG_IN | UR_FLAG_SHORTOK); + res = usb_sync_request(ur); + usb_free_request(ur); + if (res == 4) { + usbmass_stall_recovery(dev); + KFREE(cbwcsw); + return -1; + } + + + /* + * Get the Status + */ + + memset(csw,0,sizeof(usbmass_csw_t)); + ur = usb_make_request(dev,softc->umass_inpipe,(uint8_t *) csw, + sizeof(usbmass_csw_t),UR_FLAG_IN); + res = usb_sync_request(ur); + usb_free_request(ur); + if (res == 4) { + usbmass_stall_recovery(dev); + KFREE(cbwcsw); + return -1; + } + + +#if 0 + printf("CSW: Signature=%08X Tag=%08X Residue=%08X Status=%02X\n", + GETCBWFIELD(csw,dCSWSignature), + GETCBWFIELD(csw,dCSWTag), + GETCBWFIELD(csw,dCSWDataResidue), + csw->bCSWStatus); +#endif + + res = (csw->bCSWStatus == USBMASS_CSW_PASS) ? 0 : -1; + + KFREE(cbwcsw); + + return res; + +} + +/* ********************************************************************* + * usbmass_write_sector(dev,sectornum,seccnt,buffer) + * + * Writes a sector to the device + * + * Input parameters: + * dev - usb device + * sectornum - sector number to write + * seccnt - count of sectors to write + * buffer - place to get sector to write + * + * Return value: + * status + ********************************************************************* */ + +static int usbmass_write_sector(usbdev_t *dev,uint32_t sectornum,uint32_t seccnt, + uint8_t *buffer) +{ + uint8_t *cbwcsw; + uint8_t *sector; + usbmass_cbw_t *cbw; + usbmass_csw_t *csw; + usbreq_t *ur; + usbmass_softc_t *softc; + int res; + + softc = (usbmass_softc_t *) dev->ud_private; + + cbwcsw = KMALLOC(64,32); + sector = buffer; + + cbw = (usbmass_cbw_t *) cbwcsw; + csw = (usbmass_csw_t *) cbwcsw; + + /* + * Fill in the fields of the CBW + */ + + PUTCBWFIELD(cbw,dCBWSignature,USBMASS_CBW_SIGNATURE); + PUTCBWFIELD(cbw,dCBWTag,softc->umass_curtag); + PUTCBWFIELD(cbw,dCBWDataTransferLength,(512*seccnt)); + cbw->bmCBWFlags = 0x00; /* OUT */ + cbw->bCBWLUN = 0; + cbw->bCBWCBLength = 10; + cbw->CBWCB[0] = 0x2A; /* WRITE */ + cbw->CBWCB[1] = 0; + cbw->CBWCB[2] = (sectornum >> 24) & 0xFF; /* LUN 0 & MSB's of sector */ + cbw->CBWCB[3] = (sectornum >> 16) & 0xFF; + cbw->CBWCB[4] = (sectornum >> 8) & 0xFF; + cbw->CBWCB[5] = (sectornum >> 0) & 0xFF; + cbw->CBWCB[6] = 0; + cbw->CBWCB[7] = 0; + cbw->CBWCB[8] = seccnt; + cbw->CBWCB[9] = 0; + + softc->umass_curtag++; + + /* + * Send the CBW + */ + + ur = usb_make_request(dev,softc->umass_outpipe,(uint8_t *) cbw, + sizeof(usbmass_cbw_t),UR_FLAG_OUT); + res = usb_sync_request(ur); + usb_free_request(ur); + + /* + * Send the data + */ + + ur = usb_make_request(dev,softc->umass_outpipe,sector, + 512*seccnt,UR_FLAG_OUT); + res = usb_sync_request(ur); + usb_free_request(ur); + + /* + * Get the Status + */ + + memset(csw,0,sizeof(usbmass_csw_t)); + ur = usb_make_request(dev,softc->umass_inpipe,(uint8_t *) csw, + sizeof(usbmass_csw_t),UR_FLAG_IN); + res = usb_sync_request(ur); + usb_free_request(ur); + +#if 0 + printf("CSW: Signature=%08X Tag=%08X Residue=%08X Status=%02X\n", + GETCBWFIELD(csw,dCSWSignature), + GETCBWFIELD(csw,dCSWTag), + GETCBWFIELD(csw,dCSWDataResidue), + csw->bCSWStatus); +#endif + + res = (csw->bCSWStatus == USBMASS_CSW_PASS) ? 0 : -1; + + KFREE(cbwcsw); + + return res; +} + +/* ********************************************************************* + * usbmass_read_capacity(dev,sectornum,buffer) + * + * Reads a sector from the device. + * + * Input parameters: + * dev - usb device + * sectornum - sector number to read + * buffer - place to put sector we read + * + * Return value: + * status + ********************************************************************* */ + +int usbmass_read_capacity(usbdev_t *dev,uint32_t *size); +int usbmass_read_capacity(usbdev_t *dev,uint32_t *size) +{ + uint8_t *cbwcsw; + uint8_t *sector; + usbmass_cbw_t *cbw; + usbmass_csw_t *csw; + usbreq_t *ur; + usbmass_softc_t *softc; + int res; + + softc = (usbmass_softc_t *) dev->ud_private; + + cbwcsw = KMALLOC(64,32); + sector = KMALLOC(64,32); + + memset(sector,0,64); + + cbw = (usbmass_cbw_t *) cbwcsw; + csw = (usbmass_csw_t *) cbwcsw; + + *size = 0; + + /* + * Fill in the fields of the CBW + */ + + PUTCBWFIELD(cbw,dCBWSignature,USBMASS_CBW_SIGNATURE); + PUTCBWFIELD(cbw,dCBWTag,softc->umass_curtag); + PUTCBWFIELD(cbw,dCBWDataTransferLength,8); + cbw->bmCBWFlags = 0x80; /* IN */ + cbw->bCBWLUN = 0; + cbw->bCBWCBLength = 10; + cbw->CBWCB[0] = 0x25; /* READ CAPACITY */ + cbw->CBWCB[1] = 0; + cbw->CBWCB[2] = 0; + cbw->CBWCB[3] = 0; + cbw->CBWCB[4] = 0; + cbw->CBWCB[5] = 0; + cbw->CBWCB[6] = 0; + cbw->CBWCB[7] = 0; + cbw->CBWCB[8] = 0; + cbw->CBWCB[9] = 0; + + softc->umass_curtag++; + + /* + * Send the CBW + */ + + ur = usb_make_request(dev,softc->umass_outpipe,(uint8_t *) cbw, + sizeof(usbmass_cbw_t),UR_FLAG_OUT); + res = usb_sync_request(ur); + usb_free_request(ur); + + if (res == 4) { + usbmass_stall_recovery(dev); + KFREE(cbwcsw); + KFREE(sector); + return -1; + } + + /* + * Get the data + */ + + ur = usb_make_request(dev,softc->umass_inpipe,sector, + 8,UR_FLAG_IN | UR_FLAG_SHORTOK); + res = usb_sync_request(ur); + usb_free_request(ur); + + if (res == 4) { + usbmass_stall_recovery(dev); + KFREE(cbwcsw); + KFREE(sector); + return -1; + } + + /* + * Get the Status + */ + + memset(csw,0,sizeof(usbmass_csw_t)); + ur = usb_make_request(dev,softc->umass_inpipe,(uint8_t *) csw, + sizeof(usbmass_csw_t),UR_FLAG_IN); + res = usb_sync_request(ur); + usb_free_request(ur); + + KFREE(cbwcsw); + + *size = (((uint32_t) sector[0]) << 24) | + (((uint32_t) sector[1]) << 16) | + (((uint32_t) sector[2]) << 8) | + (((uint32_t) sector[3]) << 0); + + KFREE(sector); + + return 0; + +} + + + +/* ********************************************************************* + * usbmass_attach(dev,drv) + * + * This routine is called when the bus scan stuff finds a mass-storage + * device. We finish up the initialization by configuring the + * device and allocating our softc here. + * + * Input parameters: + * dev - usb device, in the "addressed" state. + * drv - the driver table entry that matched + * + * Return value: + * 0 + ********************************************************************* */ + +static int usbmass_attach(usbdev_t *dev,usb_driver_t *drv) +{ + usb_config_descr_t *cfgdscr = dev->ud_cfgdescr; + usb_endpoint_descr_t *epdscr; + usb_endpoint_descr_t *indscr = NULL; + usb_endpoint_descr_t *outdscr = NULL; + usb_interface_descr_t *ifdscr; + usbmass_softc_t *softc; + int idx; + + dev->ud_drv = drv; + + softc = KMALLOC(sizeof(usbmass_softc_t),0); + memset(softc,0,sizeof(usbmass_softc_t)); + dev->ud_private = softc; + + ifdscr = usb_find_cfg_descr(dev,USB_INTERFACE_DESCRIPTOR_TYPE,0); + if (ifdscr == NULL) { + return -1; + } + + if ((ifdscr->bInterfaceSubClass != USBMASS_SUBCLASS_SCSI) || + (ifdscr->bInterfaceProtocol != USBMASS_BULKONLY_PROTOCOL)) { + console_log("USBMASS: Do not understand devices with SubClass 0x%02X, Protocol 0x%02X", + ifdscr->bInterfaceSubClass, + ifdscr->bInterfaceProtocol); + return -1; + } + + for (idx = 0; idx < 2; idx++) { + epdscr = usb_find_cfg_descr(dev,USB_ENDPOINT_DESCRIPTOR_TYPE,idx); + + if (USB_ENDPOINT_DIR_OUT(epdscr->bEndpointAddress)) { + outdscr = epdscr; + } + else { + indscr = epdscr; + } + } + + + if (!indscr || !outdscr) { + /* + * Could not get descriptors, something is very wrong. + * Leave device addressed but not configured. + */ + return -1; + } + + /* + * Choose the standard configuration. + */ + + usb_set_configuration(dev,cfgdscr->bConfigurationValue); + + /* + * Open the pipes. + */ + + softc->umass_inpipe = usb_open_pipe(dev,indscr); + softc->umass_outpipe = usb_open_pipe(dev,outdscr); + softc->umass_curtag = 0x12345678; + + /* + * Save pointer in global unit table so we can + * match CFE devices up with USB ones + */ + + +#ifdef _CFE_ + softc->umass_unit = -1; + for (idx = 0; idx < USBDISK_MAXUNITS; idx++) { + if (usbdisk_units[idx] == NULL) { + softc->umass_unit = idx; + usbdisk_units[idx] = dev; + break; + } + } + + console_log("USBMASS: Unit %d connected",softc->umass_unit); +#endif + + usbmass_dev = dev; + + return 0; +} + +/* ********************************************************************* + * usbmass_detach(dev) + * + * This routine is called when the bus scanner notices that + * this device has been removed from the system. We should + * do any cleanup that is required. The pending requests + * will be cancelled automagically. + * + * Input parameters: + * dev - usb device + * + * Return value: + * 0 + ********************************************************************* */ + +static int usbmass_detach(usbdev_t *dev) +{ + usbmass_softc_t *softc; + softc = (usbmass_softc_t *) dev->ud_private; + +#ifdef _CFE_ + console_log("USBMASS: USB unit %d disconnected",softc->umass_unit); + if (softc->umass_unit >= 0) usbdisk_units[softc->umass_unit] = NULL; +#endif + + KFREE(softc); + return 0; +} + + + +#ifdef _CFE_ + + +/* ********************************************************************* + * usbdisk_sectorshift(size) + * + * Given a sector size, return log2(size). We cheat; this is + * only needed for 2048 and 512-byte sectors. + * Explicitly using shifts and masks in sector number calculations + * helps on 32-bit-only platforms, since we probably won't need + * a helper library. + * + * Input parameters: + * size - sector size + * + * Return value: + * # of bits to shift + ********************************************************************* */ + +#define usbdisk_sectorshift(size) (((size)==2048)?11:9) + + +/* ********************************************************************* + * usbdisk_probe(drv,probe_a,probe_b,probe_ptr) + * + * Our probe routine. Attach an empty USB disk device to the firmware. + * + * Input parameters: + * drv - driver structure + * probe_a - not used + * probe_b - not used + * probe_ptr - not used + * + * Return value: + * nothing + ********************************************************************* */ + +static void usbdisk_probe(cfe_driver_t *drv, + unsigned long probe_a, unsigned long probe_b, + void *probe_ptr) +{ + usbdisk_t *softc; + char descr[128]; + + softc = (usbdisk_t *) KMALLOC(sizeof(usbdisk_t),0); + + memset(softc,0,sizeof(usbdisk_t)); + + softc->usbdisk_sectorsize = 512; + softc->usbdisk_devtype = BLOCK_DEVTYPE_DISK; + softc->usbdisk_ttlsect = 0; /* not calculated yet */ + softc->usbdisk_unit = (int)probe_a; + + xsprintf(descr,"USB Disk unit %d",(int)probe_a); + + cfe_attach(drv,softc,NULL,descr); +} + + +/* ********************************************************************* + * usbdisk_open(ctx) + * + * Process the CFE OPEN call for this device. For IDE disks, + * the device is reset and identified, and the geometry is + * determined. + * + * Input parameters: + * ctx - device context + * + * Return value: + * 0 if ok, else error code + ********************************************************************* */ + + +static int usbdisk_open(cfe_devctx_t *ctx) +{ + usbdisk_t *softc = ctx->dev_softc; + usbdev_t *dev = usbdisk_units[softc->usbdisk_unit]; + uint32_t size; + int res; + + if (!dev) return CFE_ERR_NOTREADY; + + usbmass_request_sense(dev); + + res = usbmass_read_capacity(dev,&size); + if (res < 0) return res; + + softc->usbdisk_ttlsect = size; + + return 0; +} + +/* ********************************************************************* + * usbdisk_read(ctx,buffer) + * + * Process a CFE READ command for the IDE device. This is + * more complex than it looks, since CFE offsets are byte offsets + * and we may need to read partial sectors. + * + * Input parameters: + * ctx - device context + * buffer - buffer descriptor + * + * Return value: + * number of bytes read, or <0 if an error occured + ********************************************************************* */ + +static int usbdisk_read(cfe_devctx_t *ctx,iocb_buffer_t *buffer) +{ + usbdisk_t *softc = ctx->dev_softc; + usbdev_t *dev = usbdisk_units[softc->usbdisk_unit]; + unsigned char *bptr; + int blen; + int numsec; + int res = 0; + int amtcopy; + uint64_t lba; + uint64_t offset; + unsigned char sector[MAX_SECTORSIZE]; + int sectorshift; + + if (!dev) return CFE_ERR_NOTREADY; + + sectorshift = usbdisk_sectorshift(softc->usbdisk_sectorsize); + + bptr = buffer->buf_ptr; + blen = buffer->buf_length; + offset = buffer->buf_offset; + numsec = (blen + softc->usbdisk_sectorsize - 1) >> sectorshift; + + if (offset & (softc->usbdisk_sectorsize-1)) { + lba = (offset >> sectorshift); + res = usbmass_read_sector(dev,lba,1,sector); + if (res < 0) goto out; + amtcopy = softc->usbdisk_sectorsize - (offset & (softc->usbdisk_sectorsize-1)); + if (amtcopy > blen) amtcopy = blen; + memcpy(bptr,§or[offset & (softc->usbdisk_sectorsize-1)],amtcopy); + bptr += amtcopy; + offset += amtcopy; + blen -= amtcopy; + } + + if (blen >= softc->usbdisk_sectorsize) { + int seccnt; + + lba = (offset >> sectorshift); + seccnt = (blen >> sectorshift); + + res = usbmass_read_sector(dev,lba,seccnt,bptr); + if (res < 0) goto out; + + amtcopy = seccnt << sectorshift; + bptr += amtcopy; + offset += amtcopy; + blen -= amtcopy; + } + + if (blen) { + lba = (offset >> sectorshift); + res = usbmass_read_sector(dev,lba,1,sector); + if (res < 0) goto out; + amtcopy = blen; + memcpy(bptr,sector,amtcopy); + bptr += amtcopy; + offset += amtcopy; + blen -= amtcopy; + } + +out: + buffer->buf_retlen = bptr - buffer->buf_ptr; + + return res; +} + +/* ********************************************************************* + * usbdisk_inpstat(ctx,inpstat) + * + * Test input status for the IDE disk. Disks are always ready + * to read. + * + * Input parameters: + * ctx - device context + * inpstat - input status structure + * + * Return value: + * 0 + ********************************************************************* */ + +static int usbdisk_inpstat(cfe_devctx_t *ctx,iocb_inpstat_t *inpstat) +{ + /* usbdisk_t *softc = ctx->dev_softc; */ + + inpstat->inp_status = 1; + return 0; +} + +/* ********************************************************************* + * usbdisk_write(ctx,buffer) + * + * Process a CFE WRITE command for the IDE device. If the write + * involves partial sectors, the affected sectors are read first + * and the changes are merged in. + * + * Input parameters: + * ctx - device context + * buffer - buffer descriptor + * + * Return value: + * number of bytes write, or <0 if an error occured + ********************************************************************* */ + +static int usbdisk_write(cfe_devctx_t *ctx,iocb_buffer_t *buffer) +{ + usbdisk_t *softc = ctx->dev_softc; + usbdev_t *dev = usbdisk_units[softc->usbdisk_unit]; + unsigned char *bptr; + int blen; + int numsec; + int res = 0; + int amtcopy; + uint64_t offset; + uint64_t lba; + unsigned char sector[MAX_SECTORSIZE]; + int sectorshift; + + if (!dev) return CFE_ERR_NOTREADY; + + sectorshift = usbdisk_sectorshift(softc->usbdisk_sectorsize); + + bptr = buffer->buf_ptr; + blen = buffer->buf_length; + offset = buffer->buf_offset; + numsec = (blen + softc->usbdisk_sectorsize - 1) >> sectorshift; + + if (offset & (softc->usbdisk_sectorsize-1)) { + lba = (offset >> sectorshift); + res = usbmass_read_sector(dev,lba,1,sector); + if (res < 0) goto out; + amtcopy = softc->usbdisk_sectorsize - (offset & (softc->usbdisk_sectorsize-1)); + if (amtcopy > blen) amtcopy = blen; + memcpy(§or[offset & (softc->usbdisk_sectorsize-1)],bptr,amtcopy); + res = usbmass_write_sector(dev,lba,1,sector); + if (res < 0) goto out; + bptr += amtcopy; + offset += amtcopy; + blen -= amtcopy; + } + + while (blen >= softc->usbdisk_sectorsize) { + amtcopy = softc->usbdisk_sectorsize; + lba = (offset >> sectorshift); + res = usbmass_write_sector(dev,lba,1,bptr); + if (res < 0) goto out; + bptr += amtcopy; + offset += amtcopy; + blen -= amtcopy; + } + + if (blen) { + lba = (offset >> sectorshift); + res = usbmass_read_sector(dev,lba,1,sector); + if (res < 0) goto out; + amtcopy = blen; + memcpy(sector,bptr,amtcopy); + res = usbmass_write_sector(dev,lba,1,sector); + if (res < 0) goto out; + bptr += amtcopy; + offset += amtcopy; + blen -= amtcopy; + } + +out: + buffer->buf_retlen = bptr - buffer->buf_ptr; + + return res; +} + + +/* ********************************************************************* + * usbdisk_ioctl(ctx,buffer) + * + * Process device I/O control requests for the IDE device. + * + * Input parameters: + * ctx - device context + * buffer - buffer descriptor + * + * Return value: + * 0 if ok + * else error code + ********************************************************************* */ + +static int usbdisk_ioctl(cfe_devctx_t *ctx,iocb_buffer_t *buffer) +{ + usbdisk_t *softc = ctx->dev_softc; + unsigned int *info = (unsigned int *) buffer->buf_ptr; + unsigned long long *linfo = (unsigned long long *) buffer->buf_ptr; + blockdev_info_t *devinfo; + + switch ((int)buffer->buf_ioctlcmd) { + case IOCTL_BLOCK_GETBLOCKSIZE: + *info = softc->usbdisk_sectorsize; + break; + case IOCTL_BLOCK_GETTOTALBLOCKS: + *linfo = softc->usbdisk_ttlsect; + break; + case IOCTL_BLOCK_GETDEVTYPE: + devinfo = (blockdev_info_t *) buffer->buf_ptr; + devinfo->blkdev_totalblocks = softc->usbdisk_ttlsect; + devinfo->blkdev_blocksize = softc->usbdisk_sectorsize; + devinfo->blkdev_devtype = softc->usbdisk_devtype; + break; + default: + return -1; + } + + return 0; +} + +/* ********************************************************************* + * usbdisk_close(ctx) + * + * Close the I/O device. + * + * Input parameters: + * ctx - device context + * + * Return value: + * 0 if ok, else error code + ********************************************************************* */ + +static int usbdisk_close(cfe_devctx_t *ctx) +{ + /* usbdisk_t *softc = ctx->dev_softc; */ + + return 0; +} + + +#endif diff --git a/cfe/cfe/usb/usbserial.c b/cfe/cfe/usb/usbserial.c new file mode 100644 index 0000000..737d8b4 --- /dev/null +++ b/cfe/cfe/usb/usbserial.c @@ -0,0 +1,713 @@ +/* ********************************************************************* + * Broadcom Common Firmware Environment (CFE) + * + * USB Serial Port Driver File: usbserial.c + * + * This device can talk to a few of those usb->serial converters + * out there. + * + * 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 <time.h> +#include <memory.h> +#include <stdint.h> +#include "usbhack.h" +#else +#include "lib_types.h" +#include "lib_string.h" +#include "lib_printf.h" +#include "cfe_iocb.h" +#include "cfe_device.h" +#include "cfe_ioctl.h" +#include "cfe_console.h" +#include "bsp_config.h" +#endif + +#include "lib_malloc.h" +#include "lib_queue.h" +#include "usbchap9.h" +#include "usbd.h" + +/* ********************************************************************* + * Constants + ********************************************************************* */ + +#define USER_FIFOSIZE 256 + +/* ********************************************************************* + * Structures + ********************************************************************* */ + +typedef struct usbser_linedata_s { + uint8_t dLineDataBaud0,dLineDataBaud1,dLineDataBaud2,dLineDataBaud3; + uint8_t bLineDataStopBits; /* 0=1, 1=1.5, 2=2 */ + uint8_t bLineDataParity; /* 0=none, 1=odd, 2=even, 3=mark, 4=space */ + uint8_t bLineDataBits; /* 5,6,7,8 */ +} usbser_linedata_t; + + +/* ********************************************************************* + * Macros + ********************************************************************* */ + +#define GETDWFIELD(s,f) ((uint32_t)((s)->f##0) | ((uint32_t)((s)->f##1) << 8) | \ + ((uint32_t)((s)->f##2) << 16) | ((uint32_t)((s)->f##3) << 24)) +#define PUTDWFIELD(s,f,v) (s)->f##0 = (v & 0xFF); \ + (s)->f##1 = ((v)>>8 & 0xFF); \ + (s)->f##2 = ((v)>>16 & 0xFF); \ + (s)->f##3 = ((v)>>24 & 0xFF); + + + +/* ********************************************************************* + * Forward Definitions + ********************************************************************* */ + +static int usbserial_attach(usbdev_t *dev,usb_driver_t *drv); +static int usbserial_detach(usbdev_t *dev); + +#ifdef _CFE_ +static void usb_uart_probe(cfe_driver_t *drv, + unsigned long probe_a, unsigned long probe_b, + void *probe_ptr); + +static int usb_uart_open(cfe_devctx_t *ctx); +static int usb_uart_read(cfe_devctx_t *ctx,iocb_buffer_t *buffer); +static int usb_uart_inpstat(cfe_devctx_t *ctx,iocb_inpstat_t *inpstat); +static int usb_uart_write(cfe_devctx_t *ctx,iocb_buffer_t *buffer); +static int usb_uart_ioctl(cfe_devctx_t *ctx,iocb_buffer_t *buffer); +static int usb_uart_close(cfe_devctx_t *ctx); + +const static cfe_devdisp_t usb_uart_dispatch = { + usb_uart_open, + usb_uart_read, + usb_uart_inpstat, + usb_uart_write, + usb_uart_ioctl, + usb_uart_close, + NULL, + NULL +}; + +const cfe_driver_t usb_uart = { + "USB UART", + "uart", + CFE_DEV_SERIAL, + &usb_uart_dispatch, + usb_uart_probe +}; + +typedef struct usb_uart_s { + int uart_unit; + int uart_speed; + int uart_flowcontrol; +} usb_uart_t; + +#define USBUART_MAXUNITS 4 +static usbdev_t *usbuart_units[USBUART_MAXUNITS]; +#endif + + +/* ********************************************************************* + * Structures + ********************************************************************* */ + +typedef struct usbserial_softc_s { + int user_inpipe; + int user_outpipe; + int user_outmps; + int user_intpipe; + uint8_t user_inbuf[USER_FIFOSIZE]; + int user_inbuf_in; + int user_inbuf_out; + uint8_t *user_devinbuf; + int user_devinbufsize; + int user_unit; + uint8_t *user_intbuf; + usbser_linedata_t user_linedata; +} usbserial_softc_t; + +usb_driver_t usbserial_driver = { + "USB Serial Port", + usbserial_attach, + usbserial_detach +}; + +usbdev_t *usbserial_dev = NULL; + + +#if 0 +/* ********************************************************************* + * usbserial_get_linedata(dev,linedata) + * + * Request line data from the device. + * + * Input parameters: + * dev - USB device + * linedata - pointer to structure + * + * Return value: + * # of bytes returned + * <0 if error + ********************************************************************* */ + +static int usbserial_get_linedata(usbdev_t *dev,usbser_linedata_t *ldata) +{ + uint8_t *respbuf; + int res; + + respbuf = KMALLOC(32,0); + + res = usb_std_request(dev,0xA1,0x21,0,0,respbuf,sizeof(usbser_linedata_t)); + + KFREE(respbuf); + + if ((res >= 0) && ldata) memcpy(ldata,respbuf,sizeof(usbser_linedata_t)); + + return res; +} +#endif + +/* ********************************************************************* + * usbserial_set_linedata(dev,linedata) + * + * Set line data to the device. + * + * Input parameters: + * dev - USB device + * linedata - pointer to structure + * + * Return value: + * # of bytes returned + * <0 if error + ********************************************************************* */ + +static int usbserial_set_linedata(usbdev_t *dev,usbser_linedata_t *ldata) +{ + int res; + + /* + * Send request to device. + */ + + res = usb_std_request(dev,0x21,0x20,0,0,(uint8_t *) ldata,sizeof(usbser_linedata_t)); + + return res; +} + +#if 0 +/* ********************************************************************* + * usbserial_song_and_dance(usbdev_t *dev) + * + * Magic incantations from using the CATC on this device. + * + * Input parameters: + * dev + * + * Return value: + * 0 + ********************************************************************* */ + +static int usbserial_song_and_dance(usbdev_t *dev) +{ + int res; + char databuf[1]; + + res = usb_std_request(dev,0xc0,0x01,0x8484,0,databuf,1); /* READ */ + + res = usb_std_request(dev,0x40,0x01,0x0404,0,NULL,0); /* WRITE */ + + res = usb_std_request(dev,0xc0,0x01,0x8484,0,databuf,1); /* READ */ + res = usb_std_request(dev,0xc0,0x01,0x8383,0,databuf,1); /* READ */ + res = usb_std_request(dev,0xc0,0x01,0x8484,0,databuf,1); /* READ */ + + res = usb_std_request(dev,0x40,0x01,0x0404,1,NULL,0); /* WRITE */ + + res = usb_std_request(dev,0xc0,0x01,0x8484,0,databuf,1); /* READ */ + res = usb_std_request(dev,0xc0,0x01,0x8383,0,databuf,1); /* READ */ + + res = usb_std_request(dev,0x40,0x01,0x0,1, NULL,0); /* WRITE */ + res = usb_std_request(dev,0x40,0x01,0x1,0xC0,NULL,0); /* WRITE */ + res = usb_std_request(dev,0x40,0x01,0x2,4, NULL,0); /* WRITE */ + + return 0; +} +#endif + +/* ********************************************************************* + * usbserial_tx_data(dev,buffer,len) + * + * Synchronously transmit data via the USB. + * + * Input parameters: + * dev - device pointer + * buffer,len - data we want to send + * + * Return value: + * number of bytes sent. + ********************************************************************* */ + +static int usbserial_tx_data(usbdev_t *dev,uint8_t *buffer,int len) +{ + uint8_t *bptr; + usbreq_t *ur; + usbserial_softc_t *softc = (dev->ud_private); + int res; + + bptr = KMALLOC(len,0); + + memcpy(bptr,buffer,len); + + ur = usb_make_request(dev,softc->user_outpipe,bptr,len,UR_FLAG_OUT); + res = usb_sync_request(ur); + +// printf("Data sent, status=%d, xferred=%d\n",res,ur->ur_xferred); + + res = ur->ur_xferred; + + usb_free_request(ur); + + KFREE(bptr); + + return res; +} + +/* ********************************************************************* + * usbserial_int_callback(ur) + * + * Callback routine for the interrupt request, for devices + * that have an interrupt pipe. We ignore this. + * + * Input parameters: + * ur - usb request + * + * Return value: + * nothing + ********************************************************************* */ + +static int usbserial_int_callback(usbreq_t *ur) +{ +// int idx; + + /* + * Check to see if the request was cancelled by someone + * deleting our endpoint. + */ + + if (ur->ur_status == 0xFF) { + usb_free_request(ur); + return 0; + } + +// printf("serial int msg: "); +// for (idx = 0; idx < ur->ur_xferred; idx++) printf("%02X ",ur->ur_buffer[idx]); +// printf("\n"); + + usb_queue_request(ur); + + return 0; + +} + + +/* ********************************************************************* + * usbserial_rx_callback(ur) + * + * Callback routine for the regular data pipe. + * + * Input parameters: + * ur - usb request + * + * Return value: + * nothing + ********************************************************************* */ + +static int usbserial_rx_callback(usbreq_t *ur) +{ + int idx; + int iptr; + usbserial_softc_t *user = (ur->ur_dev->ud_private); + + /* + * Check to see if the request was cancelled by someone + * deleting our endpoint. + */ + + if (ur->ur_status == 0xFF) { + usb_free_request(ur); + return 0; + } + + /* + * Add characters to the receive fifo + */ + + for (idx = 0; idx < ur->ur_xferred; idx++) { + iptr = (user->user_inbuf_in + 1) & (USER_FIFOSIZE-1); + if (iptr == user->user_inbuf_out) break; /* overflow */ + user->user_inbuf[user->user_inbuf_in] = ur->ur_buffer[idx]; + user->user_inbuf_in = iptr; + } + + /* + * Requeue the request + */ + + usb_queue_request(ur); + + return 0; + +} + + +/* ********************************************************************* + * usbserial_attach(dev,drv) + * + * This routine is called when the bus scan stuff finds a mass-storage + * device. We finish up the initialization by configuring the + * device and allocating our softc here. + * + * Input parameters: + * dev - usb device, in the "addressed" state. + * drv - the driver table entry that matched + * + * Return value: + * 0 + ********************************************************************* */ + +static int usbserial_attach(usbdev_t *dev,usb_driver_t *drv) +{ + usb_config_descr_t *cfgdscr = dev->ud_cfgdescr; + usb_endpoint_descr_t *epdscr; + usb_endpoint_descr_t *indscr = NULL; + usb_endpoint_descr_t *outdscr = NULL; + usb_endpoint_descr_t *intdscr = NULL; + usb_interface_descr_t *ifdscr; + usbser_linedata_t *ldata; + usbserial_softc_t *softc; + usbreq_t *ur; + int idx; + + dev->ud_drv = drv; + + softc = KMALLOC(sizeof(usbserial_softc_t),0); + memset(softc,0,sizeof(usbserial_softc_t)); + dev->ud_private = softc; + + ifdscr = usb_find_cfg_descr(dev,USB_INTERFACE_DESCRIPTOR_TYPE,0); + if (ifdscr == NULL) { + printf("Could not get interface descriptor\n"); + return -1; + } + + for (idx = 0; idx < ifdscr->bNumEndpoints; idx++) { + epdscr = usb_find_cfg_descr(dev,USB_ENDPOINT_DESCRIPTOR_TYPE,idx); + + if ((epdscr->bmAttributes & USB_ENDPOINT_TYPE_MASK) == + USB_ENDPOINT_TYPE_INTERRUPT) { + intdscr = epdscr; + } + else if (USB_ENDPOINT_DIR_OUT(epdscr->bEndpointAddress)) { + outdscr = epdscr; + } + else { + indscr = epdscr; + } + } + + + if (!indscr || !outdscr) { + printf("IN or OUT endpoint descriptors are missing\n"); + /* + * Could not get descriptors, something is very wrong. + * Leave device addressed but not configured. + */ + return 0; + } + + /* + * Choose the standard configuration. + */ + + usb_set_configuration(dev,cfgdscr->bConfigurationValue); + + /* + * Open the pipes. + */ + + softc->user_inpipe = usb_open_pipe(dev,indscr); + softc->user_devinbufsize = GETUSBFIELD(indscr,wMaxPacketSize); + softc->user_devinbuf = KMALLOC(softc->user_devinbufsize,0); + softc->user_outpipe = usb_open_pipe(dev,outdscr); + softc->user_outmps = GETUSBFIELD(outdscr,wMaxPacketSize); + if (intdscr) { + softc->user_intpipe = usb_open_pipe(dev,intdscr); + } + else { + softc->user_intpipe = -1; + } + + ur = usb_make_request(dev,softc->user_inpipe,softc->user_devinbuf, + softc->user_devinbufsize, + UR_FLAG_IN | UR_FLAG_SHORTOK); + ur->ur_callback = usbserial_rx_callback; + usb_queue_request(ur); + + + if (softc->user_intpipe) { + softc->user_intbuf = KMALLOC(32,0); + ur = usb_make_request(dev,softc->user_intpipe,softc->user_intbuf, + GETUSBFIELD(intdscr,wMaxPacketSize), + UR_FLAG_IN | UR_FLAG_SHORTOK); + ur->ur_callback = usbserial_int_callback; + usb_queue_request(ur); + } + +#ifdef _CFE_ + softc->user_unit = -1; + for (idx = 0; idx < USBUART_MAXUNITS; idx++) { + if (usbuart_units[idx] == NULL) { + softc->user_unit = idx; + usbuart_units[idx] = dev; + break; + } + } + + console_log("USBSERIAL: Unit %d connected",softc->user_unit); +#endif + +// usbserial_song_and_dance(dev); + + ldata = &(softc->user_linedata); + PUTDWFIELD(ldata,dLineDataBaud,115200); + ldata->bLineDataStopBits = 0; + ldata->bLineDataParity = 2; + ldata->bLineDataBits = 8; + + usbserial_set_linedata(dev,ldata); +// usbserial_get_linedata(dev,NULL); + + usbserial_dev = dev; + + return 0; +} + +/* ********************************************************************* + * usbserial_detach(dev) + * + * This routine is called when the bus scanner notices that + * this device has been removed from the system. We should + * do any cleanup that is required. The pending requests + * will be cancelled automagically. + * + * Input parameters: + * dev - usb device + * + * Return value: + * 0 + ********************************************************************* */ + +static int usbserial_detach(usbdev_t *dev) +{ + usbserial_softc_t *softc; + + softc = dev->ud_private; + + +#ifdef _CFE_ + console_log("USBSERIAL: USB unit %d disconnected",softc->user_unit); + if (softc->user_unit >= 0) usbuart_units[softc->user_unit] = NULL; +#endif + + if (softc) { + if (softc->user_devinbuf) KFREE(softc->user_devinbuf); + if (softc->user_intbuf) KFREE(softc->user_intbuf); + KFREE(softc); + } + + return 0; +} + + + +#ifdef _CFE_ + + + +static void usb_uart_probe(cfe_driver_t *drv, + unsigned long probe_a, unsigned long probe_b, + void *probe_ptr) +{ + usb_uart_t *softc; + char descr[80]; + + softc = (usb_uart_t *) KMALLOC(sizeof(usb_uart_t),0); + + memset(softc,0,sizeof(usb_uart_t)); + + softc->uart_unit = (int)probe_a; + + xsprintf(descr,"USB UART unit %d",(int)probe_a); + + cfe_attach(drv,softc,NULL,descr); +} + + +static int usb_uart_open(cfe_devctx_t *ctx) +{ +// usb_uart_t *softc = ctx->dev_softc; +// int baudrate = CFG_SERIAL_BAUD_RATE; +// usbdev_t *dev = usbuart_units[softc->uart_unit]; + + /* + * XXX call the uart setup here + */ + + return 0; +} + +static int usb_uart_read(cfe_devctx_t *ctx, iocb_buffer_t *buffer) +{ + usb_uart_t *softc = ctx->dev_softc; + usbdev_t *dev = usbuart_units[softc->uart_unit]; + usbserial_softc_t *user = dev->ud_private; + unsigned char *bptr; + int blen; + + if (!dev) return 0; + + bptr = buffer->buf_ptr; + blen = buffer->buf_length; + + while ((blen > 0) && (user->user_inbuf_out != user->user_inbuf_in)) { + *bptr++ = user->user_inbuf[user->user_inbuf_out]; + user->user_inbuf_out = (user->user_inbuf_out + 1) & (USER_FIFOSIZE-1); + blen--; + } + + buffer->buf_retlen = buffer->buf_length - blen; + return 0; +} + +static int usb_uart_inpstat(cfe_devctx_t *ctx, iocb_inpstat_t *inpstat) +{ + usb_uart_t *softc = ctx->dev_softc; + usbdev_t *dev = usbuart_units[softc->uart_unit]; + usbserial_softc_t *user = dev->ud_private; + + inpstat->inp_status = 0; + + if (!dev) return 0; + + inpstat->inp_status = (user->user_inbuf_in != user->user_inbuf_out); + + return 0; +} + +static int usb_uart_write(cfe_devctx_t *ctx, iocb_buffer_t *buffer) +{ + usb_uart_t *softc = ctx->dev_softc; + unsigned char *bptr; + int blen; + usbdev_t *dev = usbuart_units[softc->uart_unit]; + usbserial_softc_t *user = dev->ud_private; + + bptr = buffer->buf_ptr; + blen = buffer->buf_length; + + if (!dev) { + buffer->buf_retlen = blen; + return 0; + } + + if (blen > user->user_outmps) blen = user->user_outmps; + + usbserial_tx_data(dev,bptr,blen); + + buffer->buf_retlen = blen; + return 0; +} + +static int usb_uart_ioctl(cfe_devctx_t *ctx, iocb_buffer_t *buffer) +{ + usb_uart_t *softc = ctx->dev_softc; + usbdev_t *dev = usbuart_units[softc->uart_unit]; +// usbserial_softc_t *user = dev->ud_private; + + if (!dev) return -1; + + unsigned int *info = (unsigned int *) buffer->buf_ptr; + + switch ((int)buffer->buf_ioctlcmd) { + case IOCTL_SERIAL_GETSPEED: + *info = softc->uart_speed; + break; + case IOCTL_SERIAL_SETSPEED: + softc->uart_speed = *info; + /* NYI */ + break; + case IOCTL_SERIAL_GETFLOW: + *info = softc->uart_flowcontrol; + break; + case IOCTL_SERIAL_SETFLOW: + softc->uart_flowcontrol = *info; + /* NYI */ + break; + default: + return -1; + } + + return 0; +} + +static int usb_uart_close(cfe_devctx_t *ctx) +{ +// usb_uart_t *softc = ctx->dev_softc; + + return 0; +} + + + +#endif + |