diff -uNr linux.org/arch/arm/def-configs/lubbock linux/arch/arm/def-configs/lubbock --- linux.org/arch/arm/def-configs/lubbock Mon May 12 14:25:50 2003 +++ linux/arch/arm/def-configs/lubbock Tue May 20 09:44:26 2003 @@ -216,7 +216,7 @@ # CONFIG_BINFMT_MISC is not set CONFIG_PM=y # CONFIG_ARTHUR is not set -CONFIG_CMDLINE="root=/dev/nfs ip=bootp console=ttyS0,115200 mem=64M" +CONFIG_CMDLINE="root=/dev/mtdblock2 ip=off console=ttyS0,115200" CONFIG_LEDS=y CONFIG_LEDS_TIMER=y CONFIG_LEDS_CPU=y @@ -234,7 +234,7 @@ # CONFIG_MTD_DEBUG is not set CONFIG_MTD_PARTITIONS=y # CONFIG_MTD_CONCAT is not set -CONFIG_MTD_REDBOOT_PARTS=y +# CONFIG_MTD_REDBOOT_PARTS is not set # CONFIG_MTD_CMDLINE_PARTS is not set # CONFIG_MTD_AFS_PARTS is not set @@ -504,7 +504,49 @@ # # IrDA (infrared) support # -# CONFIG_IRDA is not set +CONFIG_IRDA=y + +# +# IrDA protocols +# +# CONFIG_IRLAN is not set +# CONFIG_IRNET is not set +# CONFIG_IRCOMM is not set +# CONFIG_IRDA_ULTRA is not set + +# +# IrDA options +# +# CONFIG_IRDA_CACHE_LAST_LSAP is not set +# CONFIG_IRDA_FAST_RR is not set +# CONFIG_IRDA_DEBUG is not set + +# +# Infrared-port device drivers +# + +# +# SIR device drivers +# +# CONFIG_IRTTY_SIR is not set +# CONFIG_IRPORT_SIR is not set + +# +# Dongle support +# +# CONFIG_DONGLE is not set + +# +# FIR device drivers +# +# CONFIG_USB_IRDA is not set +# CONFIG_NSC_FIR is not set +# CONFIG_WINBOND_FIR is not set +# CONFIG_TOSHIBA_FIR is not set +# CONFIG_SMC_IRCC_FIR is not set +# CONFIG_ALI_FIR is not set +# CONFIG_VLSI_FIR is not set +CONFIG_PXA_FIR=y # # ATA/ATAPI/MFM/RLL support @@ -612,6 +654,8 @@ # CONFIG_SERIAL_OMAHA_CONSOLE is not set # CONFIG_SERIAL_AT91US3 is not set # CONFIG_SERIAL_AT91US3_CONSOLE is not set +CONFIG_SERIAL_PXA=m +CONFIG_PXA_DEFAULT_BAUDRATE=38400 # CONFIG_SERIAL_8250 is not set # CONFIG_SERIAL_8250_CONSOLE is not set # CONFIG_SERIAL_8250_EXTENDED is not set @@ -620,6 +664,7 @@ # CONFIG_SERIAL_8250_DETECT_IRQ is not set # CONFIG_SERIAL_8250_MULTIPORT is not set # CONFIG_SERIAL_8250_HUB6 is not set +CONFIG_SERIAL_CORE=m CONFIG_UNIX98_PTYS=y CONFIG_UNIX98_PTY_COUNT=256 @@ -775,7 +820,7 @@ # CONFIG_CODA_FS is not set # CONFIG_INTERMEZZO_FS is not set CONFIG_NFS_FS=y -# CONFIG_NFS_V3 is not set +CONFIG_NFS_V3=y CONFIG_ROOT_NFS=y # CONFIG_NFSD is not set # CONFIG_NFSD_V3 is not set @@ -917,7 +962,202 @@ # # USB support # -# CONFIG_USB is not set +CONFIG_USB=m +# CONFIG_USB_DEBUG is not set + +# +# Miscellaneous USB options +# +# CONFIG_USB_DEVICEFS is not set +# CONFIG_USB_BANDWIDTH is not set +# CONFIG_USB_LONG_TIMEOUT is not set + +# +# USB Host Controller Drivers +# +# CONFIG_USB_EHCI_HCD is not set +# CONFIG_USB_UHCI is not set +# CONFIG_USB_UHCI_ALT is not set +# CONFIG_USB_OHCI is not set +# CONFIG_USB_OHCI_SA1111 is not set + +# +# USB Device Class drivers +# +# CONFIG_USB_AUDIO is not set +# CONFIG_USB_EMI26 is not set +# CONFIG_USB_BLUETOOTH is not set + +# +# SCSI support is needed for USB Storage +# +# CONFIG_USB_STORAGE is not set +# CONFIG_USB_STORAGE_DEBUG is not set +# CONFIG_USB_STORAGE_DATAFAB is not set +# CONFIG_USB_STORAGE_FREECOM is not set +# CONFIG_USB_STORAGE_ISD200 is not set +# CONFIG_USB_STORAGE_DPCM is not set +# CONFIG_USB_STORAGE_HP8200e is not set +# CONFIG_USB_STORAGE_SDDR09 is not set +# CONFIG_USB_STORAGE_JUMPSHOT is not set +# CONFIG_USB_ACM is not set +# CONFIG_USB_PRINTER is not set + +# +# USB Human Interface Devices (HID) +# +# CONFIG_USB_HID is not set +# CONFIG_USB_HIDINPUT is not set +# CONFIG_USB_HIDDEV is not set +# CONFIG_USB_KBD is not set +# CONFIG_USB_MOUSE is not set +# CONFIG_USB_WACOM is not set + +# +# USB Imaging devices +# +# CONFIG_USB_DC2XX is not set +# CONFIG_USB_MDC800 is not set +# CONFIG_USB_SCANNER is not set +# CONFIG_USB_MICROTEK is not set +# CONFIG_USB_HPUSBSCSI is not set + +# +# USB Multimedia devices +# + +# +# Video4Linux support is needed for USB Multimedia device support +# + +# +# USB Network adaptors +# +# CONFIG_USB_PEGASUS is not set +# CONFIG_USB_RTL8150 is not set +# CONFIG_USB_KAWETH is not set +# CONFIG_USB_CATC is not set +# CONFIG_USB_CDCETHER is not set +# CONFIG_USB_USBNET is not set + +# +# USB port drivers +# +# CONFIG_USB_USS720 is not set + +# +# USB Serial Converter support +# +# CONFIG_USB_SERIAL is not set +# CONFIG_USB_SERIAL_GENERIC is not set +# CONFIG_USB_SERIAL_BELKIN is not set +# CONFIG_USB_SERIAL_WHITEHEAT is not set +# CONFIG_USB_SERIAL_DIGI_ACCELEPORT is not set +# CONFIG_USB_SERIAL_EMPEG is not set +# CONFIG_USB_SERIAL_FTDI_SIO is not set +# CONFIG_USB_SERIAL_VISOR is not set +# CONFIG_USB_SERIAL_IPAQ is not set +# CONFIG_USB_SERIAL_IR is not set +# CONFIG_USB_SERIAL_EDGEPORT is not set +# CONFIG_USB_SERIAL_KEYSPAN_PDA is not set +# CONFIG_USB_SERIAL_KEYSPAN is not set +# CONFIG_USB_SERIAL_KEYSPAN_USA28 is not set +# CONFIG_USB_SERIAL_KEYSPAN_USA28X is not set +# CONFIG_USB_SERIAL_KEYSPAN_USA28XA is not set +# CONFIG_USB_SERIAL_KEYSPAN_USA28XB is not set +# CONFIG_USB_SERIAL_KEYSPAN_USA19 is not set +# CONFIG_USB_SERIAL_KEYSPAN_USA18X is not set +# CONFIG_USB_SERIAL_KEYSPAN_USA19W is not set +# CONFIG_USB_SERIAL_KEYSPAN_USA49W is not set +# CONFIG_USB_SERIAL_MCT_U232 is not set +# CONFIG_USB_SERIAL_KLSI is not set +# CONFIG_USB_SERIAL_PL2303 is not set +# CONFIG_USB_SERIAL_CYBERJACK is not set +# CONFIG_USB_SERIAL_XIRCOM is not set +# CONFIG_USB_SERIAL_OMNINET is not set + +# +# USB Miscellaneous drivers +# +# CONFIG_USB_RIO500 is not set +# CONFIG_USB_AUERSWALD is not set +# CONFIG_USB_BRLVGER is not set + +# +# USB client driver +# + +# +# USB Device Support +# +CONFIG_USBD=m +CONFIG_USBD_VENDORID=0000 +CONFIG_USBD_PRODUCTID=0000 +CONFIG_USBD_PRODUCT_NAME="Lubbock" +CONFIG_USBD_MANUFACTURER="Intel" +CONFIG_USBD_USE_SERIAL_NUMBER=y +CONFIG_USBD_SERIAL_NUMBER_STR="12345678" +# CONFIG_USBD_SELFPOWERED is not set +CONFIG_USBD_MAXPOWER=0 +# CONFIG_USBD_MONITOR is not set + +# +# +# +# CONFIG_USBD_PROCFS is not set + +# +# USB Device functions +# + +# +# Network Function +# +CONFIG_USBD_NET=m +CONFIG_USBD_NET_VENDORID=dead +CONFIG_USBD_NET_PRODUCTID=beef +CONFIG_USBD_NET_IFNAME="usbd" +CONFIG_USBD_NET_OUT_ENDPOINT=2 +CONFIG_USBD_NET_OUT_PKTSIZE=64 +CONFIG_USBD_NET_IN_ENDPOINT=1 +CONFIG_USBD_NET_IN_PKTSIZE=64 +CONFIG_USBD_NET_INT_ENDPOINT=3 +CONFIG_USBD_NET_INT_PKTSIZE=16 +# CONFIG_USBD_NET_NFS_SUPPORT is not set +# CONFIG_USBD_NET_ALWAYSUP is not set +# CONFIG_USBD_NET_SAFE is not set +# CONFIG_USBD_NET_MDLM is not set +CONFIG_USBD_NET_CDC=y +CONFIG_USBD_NET_REMOTE_MACADDR="400002000001" +CONFIG_USBD_NET_REMOTE_OUI=400002 +# CONFIG_USBD_MAC_AS_SERIAL_NUMBER is not set +CONFIG_USBD_NET_LOCAL_MACADDR="400001000001" +CONFIG_USBD_NET_LOCAL_OUI=400001 + +# +# Serial Function +# +CONFIG_USBD_SERIAL=m +CONFIG_USBD_SERIAL_VENDORID=1 +CONFIG_USBD_SERIAL_PRODUCTID=1 +CONFIG_USBD_SERIAL_CDC=y +CONFIG_USBD_SERIAL_OUT_ENDPOINT=2 +CONFIG_USBD_SERIAL_IN_PKTSIZE=64 +CONFIG_USBD_SERIAL_IN_ENDPOINT=1 +CONFIG_USBD_SERIAL_OUT_PKTSIZE=64 +CONFIG_USBD_SERIAL_INT_ENDPOINT=3 +CONFIG_USBD_SERIAL_INT_PKTSIZE=16 +# CONFIG_USBD_SERIAL_SAFE is not set + +# +# USB Device bus interfaces +# + +# +# USB Device Bus Interface Support +# +CONFIG_USBD_PXA_BUS=m +# CONFIG_USBD_GENERIC_BUS is not set # # Bluetooth support diff -uNr linux.org/arch/arm/lib/copy_page.S linux/arch/arm/lib/copy_page.S --- linux.org/arch/arm/lib/copy_page.S Mon May 12 14:25:50 2003 +++ linux/arch/arm/lib/copy_page.S Tue May 20 16:54:45 2003 @@ -22,6 +22,50 @@ * the core clock switching. */ ENTRY(copy_page) + +#ifdef CONFIG_CPU_XSCALE + @ Tests show an average 10% performance improvement on PXA260 + + pld [r1] @ start preloading - source first + pld [r0] + pld [r1, #0x20] + pld [r0, #0x20] + + stmfd sp!, {r4-r8, lr} @ no need to save r3 and ip regs + + mov r2, #PAGE_SZ/128 + +1: pld [r1, #0x40] + pld [r0, #0x40] + pld [r1, #0x60] + pld [r0, #0x60] + + ldmia r1!, {r3, r4-r8, ip, lr} + stmia r0!, {r3, r4-r8, ip, lr} + + ldmia r1!, {r3, r4-r8, ip, lr} + stmia r0!, {r3, r4-r8, ip, lr} + + + pld [r1, #0x40] + pld [r0, #0x40] + pld [r1, #0x60] + pld [r0, #0x60] + + subs r2, r2, #1 + + ldmia r1!, {r3, r4-r8, ip, lr} + stmia r0!, {r3, r4-r8, ip, lr} + + ldmia r1!, {r3, r4-r8, ip, lr} + stmia r0!, {r3, r4-r8, ip, lr} + + bne 1b + + LOADREGS(fd, sp!, {r4-r8, pc}) + +#else + stmfd sp!, {r4, lr} @ 2 mov r2, #PAGE_SZ/64 @ 1 ldmia r1!, {r3, r4, ip, lr} @ 4+1 @@ -36,3 +80,4 @@ ldmneia r1!, {r3, r4, ip, lr} @ 4 bne 1b @ 1 LOADREGS(fd, sp!, {r4, pc}) @ 3 +#endif diff -uNr linux.org/arch/arm/mach-pxa/lubbock.c linux/arch/arm/mach-pxa/lubbock.c --- linux.org/arch/arm/mach-pxa/lubbock.c Mon May 12 14:25:50 2003 +++ linux/arch/arm/mach-pxa/lubbock.c Fri May 16 17:46:58 2003 @@ -152,14 +152,18 @@ /* This is for the SMC chip select */ set_GPIO_mode(GPIO79_nCS_3_MD); - /* setup sleep mode values */ + /* setup sleep mode values */ PWER = 0x00000002; - PFER = 0x00000000; - PRER = 0x00000002; - PGSR0 = 0x00008000; - PGSR1 = 0x003F0202; - PGSR2 = 0x0001C000; + PFER = 0x00000000; + PRER = 0x00000002; + /* low power mode setting */ + /* if you can make the power consumption less, change the setting below */ + PGSR0 = 0xC3E39FFC; + PGSR1 = 0xFCFFAB8C; + PGSR2 = 0x0001FFFF; + /* setup the 32.7686KHz crystal in sleep mode */ PCFR |= PCFR_OPDE; + OSCC |= OSCC_OON; } MACHINE_START(LUBBOCK, "Intel DBPXA250 Development Platform") diff -uNr linux.org/arch/arm/mach-pxa/pm.c linux/arch/arm/mach-pxa/pm.c --- linux.org/arch/arm/mach-pxa/pm.c Mon May 12 14:25:50 2003 +++ linux/arch/arm/mach-pxa/pm.c Fri May 16 17:46:58 2003 @@ -117,6 +117,10 @@ CKEN = 0; /* Note: wake up source are set up in each machine specific files */ + /* if you can make the power consumption less, change the setting below */ + GPDR2=0x0001FFFF; + GPDR1=0xFCFFAB83; + GPDR0=0xC3E3BFFC; /* clear GPIO transition detect bits */ GEDR0 = GEDR0; GEDR1 = GEDR1; GEDR2 = GEDR2; diff -uNr linux.org/arch/arm/mm/consistent.c linux/arch/arm/mm/consistent.c --- linux.org/arch/arm/mm/consistent.c Mon May 12 14:25:50 2003 +++ linux/arch/arm/mm/consistent.c Fri May 16 17:46:58 2003 @@ -55,7 +55,15 @@ goto no_page; *dma_handle = page_to_bus(page); - ret = __ioremap(page_to_pfn(page) << PAGE_SHIFT, size, 0); + { + /* added by Nick Bao to support non-cacheable, bufferable framebuffer */ + int flag; + flag = 0; + if ( gfp & __GFP_FRAMEBUF ) { + flag = PTE_BUFFERABLE; + } + ret = __ioremap(page_to_pfn(page) << PAGE_SHIFT, size, flag); + } if (!ret) goto no_remap; diff -uNr linux.org/arch/arm/mm/proc-xscale.S linux/arch/arm/mm/proc-xscale.S --- linux.org/arch/arm/mm/proc-xscale.S Mon May 12 14:25:50 2003 +++ linux/arch/arm/mm/proc-xscale.S Fri May 16 17:46:58 2003 @@ -34,8 +34,8 @@ * usage pattern of your main application. Write through cache is definitely * a performance loss in most cases, but might be used for special purposes. */ -#define PMD_CACHE_WRITE_ALLOCATE 1 -#define PTE_CACHE_WRITE_ALLOCATE 1 +#define PMD_CACHE_WRITE_ALLOCATE 0 +#define PTE_CACHE_WRITE_ALLOCATE 0 #define CACHE_WRITE_THROUGH 0 /* diff -uNr linux.org/drivers/mtd/chips/cfi_cmdset_0001.c linux/drivers/mtd/chips/cfi_cmdset_0001.c --- linux.org/drivers/mtd/chips/cfi_cmdset_0001.c Mon May 12 14:25:48 2003 +++ linux/drivers/mtd/chips/cfi_cmdset_0001.c Mon May 12 14:36:58 2003 @@ -1581,6 +1581,12 @@ struct flchip *chip; int ret = 0; + /* Force into READ mode */ + if (mtd->read) { + int count,unused[2]; + mtd->read(mtd, 0, sizeof(unused), &count, (u_char*)unused); + } + for (i=0; !ret && inumchips; i++) { chip = &cfi->chips[i]; diff -uNr linux.org/drivers/serial/Config.in linux/drivers/serial/Config.in --- linux.org/drivers/serial/Config.in Mon May 12 14:25:48 2003 +++ linux/drivers/serial/Config.in Mon May 19 10:17:37 2003 @@ -43,6 +43,10 @@ dep_bool 'AT91RM9200 serial port support' CONFIG_SERIAL_AT91US3 $CONFIG_ARCH_AT91RM9200DK dep_bool ' Support for console on AT91RM9200 serial port' CONFIG_SERIAL_AT91US3_CONSOLE $CONFIG_SERIAL_AT91US3 + dep_tristate 'PXA26x serial port support' CONFIG_SERIAL_PXA $CONFIG_ARCH_PXA + if [ "$CONFIG_SERIAL_PXA" != "n" ]; then + int ' Default PXA serial baudrate' CONFIG_PXA_DEFAULT_BAUDRATE 115200 + fi fi # # The new 8250/16550 serial drivers @@ -59,6 +63,7 @@ if [ "$CONFIG_SERIAL_AMBA" = "y" -o \ "$CONFIG_SERIAL_CLPS711X" = "y" -o \ "$CONFIG_SERIAL_SA1100" = "y" -o \ + "$CONFIG_SERIAL_PXA" = "y" -o \ "$CONFIG_SERIAL_ANAKIN" = "y" -o \ "$CONFIG_SERIAL_UART00" = "y" -o \ "$CONFIG_SERIAL_8250" = "y" -o \ @@ -69,6 +74,7 @@ if [ "$CONFIG_SERIAL_AMBA" = "m" -o \ "$CONFIG_SERIAL_CLPS711X" = "m" -o \ "$CONFIG_SERIAL_SA1100" = "m" -o \ + "$CONFIG_SERIAL_PXA" = "m" -o \ "$CONFIG_SERIAL_ANAKIN" = "m" -o \ "$CONFIG_SERIAL_UART00" = "m" -o \ "$CONFIG_SERIAL_8250" = "m" -o \ diff -uNr linux.org/drivers/serial/Makefile linux/drivers/serial/Makefile --- linux.org/drivers/serial/Makefile Mon May 12 14:25:48 2003 +++ linux/drivers/serial/Makefile Mon May 19 10:17:37 2003 @@ -29,6 +29,7 @@ obj-$(CONFIG_SERIAL_AMBA) += amba.o obj-$(CONFIG_SERIAL_CLPS711X) += clps711x.o obj-$(CONFIG_SERIAL_SA1100) += sa1100.o +obj-$(CONFIG_SERIAL_PXA) += serial_pxa.o obj-$(CONFIG_SERIAL_UART00) += uart00.o obj-$(CONFIG_SERIAL_OMAHA) += omaha.o obj-$(CONFIG_SERIAL_AT91US3) += at91us3.o diff -uNr linux.org/drivers/serial/serial_pxa.c linux/drivers/serial/serial_pxa.c --- linux.org/drivers/serial/serial_pxa.c Thu Jan 1 08:00:00 1970 +++ linux/drivers/serial/serial_pxa.c Mon May 19 10:17:24 2003 @@ -0,0 +1,877 @@ +/* + * linux/drivers/serial/serial_pxa.c + * + * Driver for PXA26x HWUART serial port. + * + * Based on drivers/serial/serial_sa1100.c, by Deep Blue Solutions Ltd. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * + * + */ +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include + + + +#define HWUART_DMA_ENABLED /* enable DMA for hardware uart */ +#define ENABLE_HWFC /* enable hardware flow-control */ +/* #define LED_DEBUG */ +/* #define PRINTK_DEBUG */ + +/* mknod /dev/hwuart c 206 0 */ +#define SERIAL_PXA_MAJOR 206 +#define CALLOUT_PXA_MAJOR 207 +#define MINOR_START 0 +#define HWUART_NORMAL_NAME_FS "hwuart%d" +#define HWUART_NORMAL_NAME "hwuart" + +#ifdef PRINTK_DEBUG +#define HDEBUG(s...) printk("<0>[HDEBUG] Line %4i : %s\n",__LINE__ , ##s) +#else +#define HDEBUG(s...) +#endif + +#define HWMCR_RTS (1<<1) +#define HWMCR_OUT2 (1<<3) +#define HWMCR_LOOP (1<<4) +#define HWMCR_AFE (1<<5) + +#define HWIER_RAVIE (1<<0) +#define HWIER_TIE (1<<1) +#define HWIER_RLSE (1<<2) +#define HWIER_MIE (1<<3) +#define HWIER_RTOIE (1<<4) +#define HWIER_NRZE (1<<5) +#define HWIER_UUE (1<<6) +#define HWIER_DMAE (1<<7) + +#define HWLSR_DR (1<<0) +#define HWLSR_OE (1<<1) +#define HWLSR_PE (1<<2) +#define HWLSR_FE (1<<3) +#define HWLSR_BI (1<<4) +#define HWLSR_TDRQ (1<<5) +#define HWLSR_TEMT (1<<6) +#define HWLSR_FIFOE (1<<7) + +#define HWFCR_ITL_1 (0) +#define HWFCR_ITL_8 (1<<6) +#define HWFCR_ITL_16 (1<<7) +#define HWFCR_ITL_32 (3<<6) +#define HWFCR_TIL (1<<3) +#define HWFCR_RESETTF (1<<2) +#define HWFCR_RESETRF (1<<1) +#define HWFCR_TRFIFOE (1<<0) + +#define HWIIR_TOD (1<<3) +#define HWIIR_IID (3<<1) +#define HWIIR_NIP (1<<0) + +#define HWLCR_WLS_8 (3) +#define HWLCR_WLS_7 (2) +#define HWLCR_STB (1<<2) +#define HWLCR_PEN (1<<3) +#define HWLCR_EPS (1<<4) +#define HWLCR_STKYP (1<<5) +#define HWLCR_SB (1<<6) +#define HWLCR_DLAB (1<<7) + + +#define CONFIG_SERIAL_PXA +#define NR_PORTS 1 /* only HWUART */ +#define PXA_ISR_PASS_LIMIT 256 /* to avoid long time ISR */ +#define FIFO_THRESHHOLD 32 /* threshhold for rx fifo */ +#define UART_PORT_SIZE 0x30 /* This is the size of our serial port register set.*/ + +static struct tty_driver normal, callout; +static struct tty_struct *pxa_table[NR_PORTS]; +static struct termios *pxa_termios[NR_PORTS], *pxa_termios_locked[NR_PORTS]; +static struct uart_driver pxa_reg; + +#ifdef HWUART_DMA_ENABLED +#define DMA_BUFF_SIZE 2048 /* limited by UART_XMIT_SIZE (1024 bytes by default) */ +#define DMA_RX_SIZE 256 /* limited by TTY_FLIPBUF_SIZE(512 bytes by default) */ +static unsigned char *dma_rx_buff; +static unsigned char *dma_tx_buff; +static dma_addr_t dma_rx_buff_phy; +static dma_addr_t dma_tx_buff_phy; +static int txdma; +static int rxdma; +static int tx_num; +static DECLARE_MUTEX(sem_dma_tx); + +static void pxa_hwuart_dma_rx_start(void) +{ + unsigned long flags; + local_irq_save(flags); + HWIER |= HWIER_DMAE; + consistent_sync(dma_rx_buff, DMA_BUFF_SIZE, PCI_DMA_FROMDEVICE); + dma_rx_buff_phy = virt_to_bus(dma_rx_buff); + DCSR(rxdma) = DCSR_NODESC; + DSADR(rxdma) = __PREG(HWRBR); + DTADR(rxdma) = dma_rx_buff_phy; + DCMD(rxdma) = DCMD_INCTRGADDR | DCMD_FLOWSRC | DCMD_ENDIRQEN | DCMD_WIDTH1 | DCMD_BURST8 | DMA_RX_SIZE; + DCSR(rxdma) |= DCSR_RUN; + local_irq_restore(flags); +} +#endif + +inline static int pxa_open(struct uart_port * port, struct uart_info * info) +{ + CKEN |= CKEN4_HWUART; + HDEBUG("HWUART power on"); +#ifdef LED_DEBUG + HEXLEDS_BASE = 0; +#endif + return 0; +} + +inline static void pxa_close(struct uart_port * port, struct uart_info * info) +{ + CKEN &= ~CKEN4_HWUART; + HDEBUG("HWUART power off"); +} + +/* + * interrupts disabled on entry + */ +static void pxa_stop_tx(struct uart_port *port, u_int from_tty) +{ +#ifdef HWUART_DMA_ENABLED + unsigned long flags; + HDEBUG("pxa_stop_tx"); + local_irq_save(flags); + DCSR(txdma) &= ~DCSR_RUN; + HWIER &= ~HWIER_DMAE; + up(&sem_dma_tx); + local_irq_restore(flags); +#else + HWIER &= ~HWIER_TIE; + port->read_status_mask &= ~HWLSR_TDRQ; +#endif +} + +/* + * Interrupts enabled + */ +static void pxa_stop_rx(struct uart_port *port) +{ +#ifdef HWUART_DMA_ENABLED + DCSR(rxdma) &= ~DCSR_RUN; + HWIER &= ~HWIER_DMAE; +#else + HWIER &= ~HWIER_RAVIE; +#endif +} + + +/* + * interrupts may not be disabled on entry + */ +static void pxa_start_tx(struct uart_port *port, u_int nonempty, u_int from_tty) +{ + +#ifdef HWUART_DMA_ENABLED + struct uart_info* info=pxa_reg.state->info; + + if (nonempty) { + unsigned long flags; + + down(&sem_dma_tx); + tx_num = CIRC_CNT(info->xmit.head, info->xmit.tail, UART_XMIT_SIZE); + port->icount.tx += tx_num; + if ( tx_num > 0 ) { + local_irq_save(flags); + pxa_stop_rx(port); + + if (info->xmit.tail > info->xmit.head) { + memcpy(dma_tx_buff, info->xmit.buf+info->xmit.tail, UART_XMIT_SIZE - info->xmit.tail); + memcpy(dma_tx_buff + (UART_XMIT_SIZE - info->xmit.tail), info->xmit.buf,info->xmit.head); + } else { + memcpy(dma_tx_buff, info->xmit.buf+info->xmit.tail, tx_num); + } + + HWIER |= HWIER_DMAE; + consistent_sync(dma_tx_buff, DMA_BUFF_SIZE, PCI_DMA_TODEVICE); + dma_tx_buff_phy = virt_to_bus(dma_tx_buff); + DCSR(txdma) = DCSR_NODESC; + DSADR(txdma) = dma_tx_buff_phy; + DTADR(txdma) = __PREG(HWTHR); + DCMD(txdma) = DCMD_INCSRCADDR | DCMD_FLOWTRG | DCMD_ENDIRQEN | DCMD_WIDTH1 | DCMD_BURST32 | tx_num; + DCSR(txdma) |= DCSR_RUN; + local_irq_restore(flags); + } else { + up(&sem_dma_tx); + } + } +#else + if (nonempty) { + unsigned long flags; + local_irq_save(flags); + port->read_status_mask |= HWLSR_TDRQ; + HWIER |= HWIER_TIE; + local_irq_restore(flags); + } +#endif +} + +/* + * No modem control lines + */ +static void pxa_enable_ms(struct uart_port *port) +{ +} + + +static void pxa_rx_chars(struct uart_info *info, struct pt_regs *regs) +{ + struct tty_struct *tty = info->tty; + unsigned int status, ch, flg, ignored = 0; + struct uart_port *port = info->port; + +#ifdef HWUART_DMA_ENABLED + unsigned int len; + + len = DTADR(rxdma) - dma_rx_buff_phy; + if (len>0) { + memcpy(tty->flip.char_buf_ptr, dma_rx_buff, len); + memset(tty->flip.flag_buf_ptr,TTY_NORMAL, len); + tty->flip.char_buf_ptr += len; + tty->flip.flag_buf_ptr += len; + port->icount.rx +=len; + tty->flip.count +=len; +#ifdef LED_DEBUG + HEXLEDS_BASE += len; +#endif + } +#endif + + status = HWLSR; + while (status & HWLSR_DR) { + ch = HWRBR; + if (tty->flip.count >= TTY_FLIPBUF_SIZE) + goto ignore_char; + port->icount.rx++; +#ifdef LED_DEBUG + HEXLEDS_BASE ++; +#endif + + flg = TTY_NORMAL; + + if (status & ( HWLSR_OE | HWLSR_PE | HWLSR_FE ) ) + goto handle_error; + + if (uart_handle_sysrq_char(info, ch, regs)) + goto ignore_char; + + error_return: + *tty->flip.flag_buf_ptr++ = flg; + *tty->flip.char_buf_ptr++ = ch; + tty->flip.count++; + ignore_char: + status = HWLSR; + } +out: + tty_flip_buffer_push(tty); + return; + +handle_error: + if (status & HWLSR_PE) + port->icount.parity++; + else if (status & HWLSR_FE) + port->icount.frame++; + if (status & HWLSR_OE) + port->icount.overrun++; + + if (status & port->ignore_status_mask) { + if (++ignored > 100) + goto out; + goto ignore_char; + } + + status &= port->read_status_mask; + + if (status & HWLSR_PE) + flg = TTY_PARITY; + else if (status & HWLSR_FE) + flg = TTY_FRAME; + + if (status & HWLSR_OE) { + + /* overrun does *not* affect the character we read from the FIFO */ + *tty->flip.flag_buf_ptr++ = flg; + *tty->flip.char_buf_ptr++ = ch; + tty->flip.count++; + if (tty->flip.count >= TTY_FLIPBUF_SIZE) + goto ignore_char; + ch = 0; + flg = TTY_OVERRUN; + } +#ifdef SUPPORT_SYSRQ + info->sysrq = 0; +#endif + goto error_return; + +} + + +static void pxa_tx_chars(struct uart_info *info) +{ + + struct uart_port *port = info->port; + + if (port->x_char) { + HWTHR = port->x_char; + port->icount.tx++; + port->x_char = 0; + return; + } + if (info->xmit.head == info->xmit.tail + || info->tty->stopped + || info->tty->hw_stopped) { + pxa_stop_tx(info->port, 0); + return; + } + + /* Tried using FIFO (not checking TNF) for fifo fill: + still had the '4 bytes repeated' problem. */ + + while (HWLSR & HWLSR_TDRQ) { + HWTHR = info->xmit.buf[info->xmit.tail]; + info->xmit.tail = (info->xmit.tail + 1) & (UART_XMIT_SIZE - 1); + port->icount.tx++; + if (info->xmit.head == info->xmit.tail) + break; + } + + if (CIRC_CNT(info->xmit.head, info->xmit.tail, UART_XMIT_SIZE) < + WAKEUP_CHARS) + uart_event(info, EVT_WRITE_WAKEUP); + + if (info->xmit.head == info->xmit.tail) + pxa_stop_tx(info->port, 0); +} + +static void pxa_int(int irq, void *dev_id, struct pt_regs *regs) +{ + + struct uart_info *info = dev_id; + unsigned int status, pass_counter = 0; + unsigned int lsr; + + status = HWIIR; + +#ifdef HWUART_DMA_ENABLED + pxa_stop_rx(info->port); +#endif + do { + + if ( (status & HWIIR_IID) == 0x6 ) { + lsr = HWLSR; + HDEBUG("Line Status Error. HWLSR=0x%x",lsr); + while (lsr & HWLSR_FIFOE) { + HDEBUG("FIFO Error. HWLSR=0x%x, HWRBR=0x%x",lsr,HWRBR); + lsr = HWLSR; + } + } + + if ( (status & HWIIR_TOD) || ( (status & HWIIR_IID) == 0x4) ) { + /* Time Out Detected or Received Data Available */ + pxa_rx_chars(info, regs); + } + + if ( (status & HWIIR_IID) == 0x2 ) { + pxa_tx_chars(info); + } + + if (pass_counter++ > PXA_ISR_PASS_LIMIT) + break; + + status = HWIIR; + } while (!(status & HWIIR_NIP)); + +#ifdef HWUART_DMA_ENABLED + pxa_hwuart_dma_rx_start(); +#endif +} + +/* + * Return TIOCSER_TEMT when transmitter is not busy. + */ +static u_int pxa_tx_empty(struct uart_port *port) +{ + return HWLSR & HWLSR_TEMT ? TIOCSER_TEMT : 0; +} + +static u_int pxa_get_mctrl(struct uart_port *port) +{ + return TIOCM_CTS | TIOCM_DSR | TIOCM_CAR; +} + +static void pxa_set_mctrl(struct uart_port *port, u_int mctrl) +{ +} + +/* + * break disabled. + */ +static void pxa_break_ctl(struct uart_port *port, int break_state) +{ + HDEBUG("pxa_break_ctl"); +} + +static int pxa_startup(struct uart_port *port, struct uart_info *info) +{ + + int retval; + + /* Allocate the IRQ */ + + retval = request_irq(port->irq, pxa_int, 0, "serial_pxa", info); + if (retval) + return retval; + + + /* If there is a specific "open" function (to register control line interrupts) */ + + if (pxa_open) { + retval = pxa_open(port, info); + if (retval) { + free_irq(port->irq, info); + return retval; + } + } + + + /* Finally, clear and enable interrupts */ + +#ifdef HWUART_DMA_ENABLED + + HWIER |= HWIER_RTOIE | HWIER_RLSE; + pxa_hwuart_dma_rx_start(); +#else + HWIER |= HWIER_RTOIE | HWIER_TIE | HWIER_RLSE | HWIER_RAVIE; +#endif + return 0; +} + +static void pxa_shutdown(struct uart_port *port, struct uart_info *info) +{ + + /* Free the interrupt */ + free_irq(port->irq, info); + + /* If there is a specific "close" function (to unregister control line interrupts) */ + if (pxa_close) + pxa_close(port, info); + + /* Disable all interrupts, port and break condition. */ + HWIER &= ~(HWIER_RTOIE | HWIER_TIE | HWIER_RLSE | HWIER_RAVIE); +} + +static void pxa_change_speed(struct uart_port *port, u_int cflag, u_int iflag, u_int quot) +{ + + HWIER &= ~HWIER_UUE; + +#ifdef HWUART_DMA_ENABLED + pxa_stop_rx(port); +#endif + /* byte size and parity */ + switch (cflag & CSIZE) { + case CS7: HWLCR |= HWLCR_WLS_7; break; + default: HWLCR |= HWLCR_WLS_8; break; + } + if (cflag & CSTOPB) + HWLCR |= HWLCR_STB; + if (cflag & PARENB) { + HWLCR |= HWLCR_PEN; + if (!(cflag & PARODD)) + HWLCR |= HWLCR_EPS; + } + + + port->read_status_mask = HWLSR_OE; + if (iflag & INPCK) + port->read_status_mask |= HWLSR_FE | HWLSR_PE; + if (iflag & (BRKINT | PARMRK)) + port->read_status_mask |= HWLSR_BI; + + /* Characters to ignore */ + port->ignore_status_mask = 0; + if (iflag & IGNPAR) + port->ignore_status_mask |= HWLSR_FE | HWLSR_PE; + if (iflag & IGNBRK) { + port->ignore_status_mask |= HWLSR_BI; + + /* If we're ignoring parity and break indicators, + ignore overruns too (for real raw support). */ + + if (iflag & IGNPAR) + port->ignore_status_mask |= HWLSR_OE; + } + + + HWFCR |= HWFCR_RESETTF | HWFCR_RESETRF | HWFCR_TRFIFOE; + + /* set the baud rate */ + HWLCR |= HWLCR_DLAB; + HWDLL = quot; + HWDLH = 0; + HWLCR &= ~HWLCR_DLAB; + + HWIER |= HWIER_UUE; + +#ifdef HWUART_DMA_ENABLED + HWIER |= HWIER_RTOIE | HWIER_RLSE; + pxa_hwuart_dma_rx_start(); +#else + HWIER |= HWIER_RTOIE | HWIER_TIE | HWIER_RLSE | HWIER_RAVIE; +#endif + +} + +static const char *pxa_type(struct uart_port *port) +{ + return port->type == PORT_PXA ? "PXA HWUART" : NULL; + +} + +/* + * Release the memory region(s) being used by 'port'. + */ +static void pxa_release_port(struct uart_port *port) +{ + release_mem_region(port->mapbase, UART_PORT_SIZE); +} + +/* + * Request the memory region(s) being used by 'port'. + */ +static int pxa_request_port(struct uart_port *port) +{ + return request_mem_region(port->mapbase, UART_PORT_SIZE,"serial_pxa") != NULL ? 0 : -EBUSY; +} + +/* + * Configure/autoconfigure the port. + */ +static void pxa_config_port(struct uart_port *port, int flags) +{ + if (flags & UART_CONFIG_TYPE && pxa_request_port(port) == 0) port->type = PORT_PXA; +} + +/* + * Verify the new serial_struct (for TIOCSSERIAL). + * The only change we allow are to the flags and type, and + * even then only between PORT_PXA and PORT_UNKNOWN + */ +static int pxa_verify_port(struct uart_port *port, struct serial_struct *ser) +{ + + int ret = 0; + + if (ser->type != PORT_UNKNOWN && ser->type != PORT_PXA) + ret = -EINVAL; + if (port->irq != ser->irq) + ret = -EINVAL; + if (ser->io_type != SERIAL_IO_MEM) + ret = -EINVAL; + if (port->uartclk / 16 != ser->baud_base) + ret = -EINVAL; + if ((void *)port->mapbase != ser->iomem_base) + ret = -EINVAL; + if (port->iobase != ser->port) + ret = -EINVAL; + if (ser->hub6 != 0) + ret = -EINVAL; + return ret; + +} + +static void pxa_pm(struct uart_port* port, u_int state, u_int oldstate) +{ + + if (state) { + CKEN &= ~CKEN4_HWUART; + HDEBUG("hwuart suspend"); + } else { + CKEN |= CKEN4_HWUART; + HDEBUG("hwuart resume"); + } +} + + +static struct uart_ops pxa_pops = { + tx_empty: pxa_tx_empty, + set_mctrl: pxa_set_mctrl, + get_mctrl: pxa_get_mctrl, + stop_tx: pxa_stop_tx, + start_tx: pxa_start_tx, + stop_rx: pxa_stop_rx, + enable_ms: pxa_enable_ms, + break_ctl: pxa_break_ctl, + startup: pxa_startup, + shutdown: pxa_shutdown, + change_speed: pxa_change_speed, + pm: pxa_pm, + type: pxa_type, + release_port: pxa_release_port, + request_port: pxa_request_port, + config_port: pxa_config_port, + verify_port: pxa_verify_port +}; + +static struct uart_port pxa_ports[NR_PORTS]; + +/* + * Setup the PXA serial ports. Only HWUART is enabled in this driver. + */ +static void pxa_init_ports(void) +{ + + static int first = 1; + int i; + + if (!first) + return; + first = 0; + + for (i = 0; i < NR_PORTS; i++) { + /* Intel PXA26x Processor Family Developer's Manual p3-6 */ + pxa_ports[i].uartclk = 14745600; + pxa_ports[i].ops = &pxa_pops; + pxa_ports[i].fifosize = FIFO_THRESHHOLD; + pxa_ports[i].flags = ASYNC_BOOT_AUTOCONF; + + pxa_ports[i].membase =(void*)&HWUART; + pxa_ports[i].mapbase = __PREG(HWUART); + pxa_ports[i].irq = IRQ_HWUART; + pxa_ports[i].iotype = SERIAL_IO_MEM; + } + + /* This enables the HWUART */ + CKEN |= CKEN4_HWUART; + + set_GPIO_mode(GPIO42_HWRXD_MD); + set_GPIO_mode(GPIO43_HWTXD_MD); + set_GPIO_mode(GPIO44_HWCTS_MD); + set_GPIO_mode(GPIO45_HWRTS_MD); + + HWIER |= HWIER_UUE; + HWFCR = HWFCR_RESETTF | HWFCR_RESETRF | HWFCR_TRFIFOE; + switch ( FIFO_THRESHHOLD ) { + case 1: HWFCR |= HWFCR_ITL_1; break; + case 8: HWFCR |= HWFCR_ITL_8; break; + case 16: HWFCR |= HWFCR_ITL_16; break; + case 32: HWFCR |= HWFCR_ITL_32; break; + default: HWFCR |= HWFCR_ITL_8; + } + + HWLCR = HWLCR_WLS_8; + +#ifdef HWUART_DMA_ENABLED + HWIER |= HWIER_RTOIE | HWIER_RLSE ; +#else + HWIER |= HWIER_RTOIE | HWIER_TIE | HWIER_RLSE | HWIER_RAVIE; +#endif + +#ifdef ENABLE_HWFC + HWMCR = HWMCR_OUT2 | HWMCR_AFE | HWMCR_RTS; /* Hardware flow control enabled */ + HDEBUG("Hardware Flow Control Enabled!"); +#else + HWMCR = HWMCR_OUT2; + HDEBUG("Hardware Flow Control Disabled!"); +#endif + enable_irq(IRQ_HWUART); +} + +static struct uart_driver pxa_reg = { + owner: THIS_MODULE, + normal_major: SERIAL_PXA_MAJOR, +#ifdef CONFIG_DEVFS_FS + normal_name: HWUART_NORMAL_NAME_FS, + callout_name: "cuhw%d", +#else + normal_name: HWUART_NORMAL_NAME, + callout_name: "cuhw", +#endif + normal_driver: &normal, + callout_major: CALLOUT_PXA_MAJOR, + callout_driver: &callout, + table: pxa_table, + termios: pxa_termios, + termios_locked: pxa_termios_locked, + minor: MINOR_START, + nr: NR_PORTS, + port: pxa_ports, + cons: NULL, +}; + +#ifdef HWUART_DMA_ENABLED +static void pxa_hwuart_dma_rx_irq(int channel, void *data, struct pt_regs *regs) +{ + + int dcsr; + struct uart_info* info=pxa_reg.state->info; + struct tty_struct *tty = info->tty; + struct uart_port *port = info->port; + + disable_irq(IRQ_HWUART); + dcsr = DCSR(channel); + DCSR(channel) &= ~DCSR_RUN; + DCSR(channel) |= DCSR_STARTINTR|DCSR_ENDINTR|DCSR_BUSERR; + + if ( dcsr & DCSR_ENDINTR ) { + memcpy(tty->flip.char_buf_ptr, dma_rx_buff, DMA_RX_SIZE); + memset(tty->flip.flag_buf_ptr,TTY_NORMAL, DMA_RX_SIZE); + tty->flip.char_buf_ptr += DMA_RX_SIZE; + tty->flip.flag_buf_ptr += DMA_RX_SIZE; + port->icount.rx +=DMA_RX_SIZE; + tty->flip.count +=DMA_RX_SIZE; +#ifdef LED_DEBUG + HEXLEDS_BASE += DMA_RX_SIZE; +#endif + tty_flip_buffer_push(tty); + } else { + printk(KERN_DEBUG "serial_pxa: rx dma bus error.\n"); + } + pxa_hwuart_dma_rx_start(); + enable_irq(IRQ_HWUART); +} + +static void pxa_hwuart_dma_tx_irq(int channel, void *data, struct pt_regs *regs) +{ + + int dcsr; + struct uart_info* info=pxa_reg.state->info; + + disable_irq(IRQ_HWUART); + dcsr = DCSR(channel); + DCSR(channel) &= ~DCSR_RUN; + DCSR(channel) |= DCSR_STARTINTR|DCSR_ENDINTR|DCSR_BUSERR; + + if ( dcsr & DCSR_ENDINTR ) { + while (!( HWLSR & HWLSR_TEMT )) { + rmb(); + } + info->xmit.tail = (info->xmit.tail + tx_num) & (UART_XMIT_SIZE - 1); + HWIER &= ~HWIER_DMAE; + up(&sem_dma_tx); + } else { + printk(KERN_DEBUG "serial_pxa: tx dma bus error.\n"); + } + pxa_hwuart_dma_rx_start(); + enable_irq(IRQ_HWUART); +} +#endif + +static int __init pxa_serial_init(void) +{ + + pxa_init_ports(); + +#ifdef HWUART_DMA_ENABLED + /* init DMA support -- begin */ + disable_irq(IRQ_HWUART); + rxdma = pxa_request_dma("HWUART_RX",DMA_PRIO_LOW, pxa_hwuart_dma_rx_irq, 0); + if (rxdma < 0) + goto err_rx_dma; + + txdma = pxa_request_dma("HWUART_TX",DMA_PRIO_LOW, pxa_hwuart_dma_tx_irq, 0); + if (txdma < 0) + goto err_tx_dma; + + if (! ( dma_rx_buff = kmalloc(DMA_BUFF_SIZE, GFP_KERNEL | GFP_DMA) ) ) { + goto err_dma_rx_buff; + } + if (! ( dma_tx_buff = kmalloc(DMA_BUFF_SIZE, GFP_KERNEL | GFP_DMA) ) ) { + goto err_dma_tx_buff; + } + + DRCMR29 = rxdma | DRCMR_MAPVLD; + DRCMR34 = txdma | DRCMR_MAPVLD; + enable_irq(IRQ_HWUART); + + HDEBUG("HWUART DMA support enabled!"); + /* printk("rx dma : %d, buff %x\n",rxdma,dma_rx_buff); */ + /* printk("tx dma : %d, buff %x\n",txdma,dma_tx_buff); */ + /* init DMA support -- end */ +#endif + return uart_register_driver(&pxa_reg); + +#ifdef HWUART_DMA_ENABLED +err_dma_tx_buff: + kfree(dma_rx_buff); +err_dma_rx_buff: + pxa_free_dma(txdma); +err_tx_dma: + pxa_free_dma(rxdma); +err_rx_dma: + enable_irq(IRQ_HWUART); + return 1; +#endif +} + +static void __exit pxa_serial_exit(void) +{ +#ifdef HWUART_DMA_ENABLED + /* deinit DMA support -- begin */ + pxa_free_dma(rxdma); + pxa_free_dma(txdma); + if (dma_rx_buff) kfree(dma_rx_buff); + if (dma_tx_buff) kfree(dma_tx_buff); + /* deinit DMA support -- end */ +#endif + uart_unregister_driver(&pxa_reg); +} + +module_init(pxa_serial_init); +module_exit(pxa_serial_exit); + +EXPORT_NO_SYMBOLS; + +MODULE_DESCRIPTION("PXA26x HWUART Serial Port Driver"); +MODULE_LICENSE("GPL"); + diff -uNr linux.org/drivers/sound/pxa-ac97.c linux/drivers/sound/pxa-ac97.c --- linux.org/drivers/sound/pxa-ac97.c Mon May 12 14:25:27 2003 +++ linux/drivers/sound/pxa-ac97.c Mon May 12 14:37:42 2003 @@ -28,11 +28,72 @@ #include #include +#ifdef CONFIG_PM +#include +static struct pm_dev *pxa_ac97_pm_dev = NULL; +struct ac97_codec *ac_pm_use_point; +#endif + + #include "pxa-audio.h" static struct completion CAR_completion; static int waitingForMask; static DECLARE_MUTEX(CAR_mutex); +static DECLARE_MUTEX(pxa_ac97_mutex); +static int pxa_ac97_refcount; + + +#ifdef CONFIG_PM +static int pxa_ac97_pm_callback(struct pm_dev *dev,pm_request_t rqst,void *data) +{ + struct ac97_codec *codec =( struct ac97_codec *)(dev->data); + int ret; + down(&pxa_ac97_mutex); + + if (rqst == PM_RESUME ) { + LUB_HEXLED = 0x10097; + printk(KERN_INFO "In file %s, pm_callback called rqst == PM_RESUME.\n", + __FILE__); + CKEN |= CKEN2_AC97; + set_GPIO_mode(GPIO31_SYNC_AC97_MD); + set_GPIO_mode(GPIO30_SDATA_OUT_AC97_MD); + set_GPIO_mode(GPIO28_BITCLK_AC97_MD); + set_GPIO_mode(GPIO29_SDATA_IN_AC97_MD); + GCR = 0; + udelay(10); + GCR = GCR_COLD_RST|GCR_CDONE_IE|GCR_SDONE_IE; + udelay(30); + + ret = ac97_probe_codec(codec); + if (ret != 1) + printk(KERN_INFO "After resume AC97 codec probe fail.\n"); + // need little hack for UCB1400 (should be moved elsewhere) + pxa_ac97_write(codec,AC97_EXTENDED_STATUS,1); + //pxa_ac97_write(&pxa_ac97_codec, 0x6a, 0x1ff7); + pxa_ac97_write(codec, 0x6a, 0x0050); + pxa_ac97_write(codec, 0x6c, 0x0030); + + } + if (rqst == PM_SUSPEND) { + /* + * Shutdown ACLINK and Disable clock should be delayed + * until other device, such as ts, etc shutdown. + * Clock is also disabled in pm.c + */ + LUB_HEXLED = 0x97; + if (!pxa_ac97_refcount) { + GCR = GCR_ACLINK_OFF; + CKEN &= ~CKEN2_AC97; + } + } + up(&pxa_ac97_mutex); + return 0; +} +#endif + + + static u16 pxa_ac97_read(struct ac97_codec *codec, u8 reg) { @@ -97,8 +158,6 @@ codec_write: pxa_ac97_write, }; -static DECLARE_MUTEX(pxa_ac97_mutex); -static int pxa_ac97_refcount; int pxa_ac97_get(struct ac97_codec **codec) { @@ -327,6 +386,16 @@ ac97_audio_state.dev_dsp = register_sound_dsp(&ac97_audio_fops, -1); pxa_ac97_codec.dev_mixer = register_sound_mixer(&mixer_fops, -1); +#ifdef CONFIG_PM + ac_pm_use_point = dummy; + dummy->pmdev=pm_register(PM_SYS_DEV, PM_SYS_UNKNOWN, pxa_ac97_pm_callback); + printk(KERN_INFO "In file %s, pm_registered.\n", __FILE__); + if (dummy->pmdev == NULL) + printk("PXA AC97: unable to register in PM.\n"); + else { + dummy->pmdev->data = dummy; + } +#endif return 0; } @@ -335,7 +404,12 @@ { unregister_sound_dsp(ac97_audio_state.dev_dsp); unregister_sound_mixer(pxa_ac97_codec.dev_mixer); +#ifdef CONFIG_PM + if ( ac_pm_use_point->pmdev) + pm_unregister( ac_pm_use_point->pmdev); +#endif pxa_ac97_put(); + } diff -uNr linux.org/drivers/usb/Config.in linux/drivers/usb/Config.in --- linux.org/drivers/usb/Config.in Mon May 12 14:25:28 2003 +++ linux/drivers/usb/Config.in Thu May 15 15:54:48 2003 @@ -110,4 +110,7 @@ dep_tristate ' USB Auerswald ISDN support (EXPERIMENTAL)' CONFIG_USB_AUERSWALD $CONFIG_USB $CONFIG_EXPERIMENTAL dep_tristate ' Tieman Voyager USB Braille display support (EXPERIMENTAL)' CONFIG_USB_BRLVGER $CONFIG_USB $CONFIG_EXPERIMENTAL fi + + comment 'USB client driver' + source drivers/usb/device/Config.in endmenu diff -uNr linux.org/drivers/usb/Makefile linux/drivers/usb/Makefile --- linux.org/drivers/usb/Makefile Mon May 12 14:25:28 2003 +++ linux/drivers/usb/Makefile Thu May 15 15:54:48 2003 @@ -95,10 +95,11 @@ obj-$(CONFIG_USB_BRLVGER) += brlvger.o # Object files in subdirectories -mod-subdirs := serial hcd +mod-subdirs := serial hcd device subdir-$(CONFIG_USB_SERIAL) += serial subdir-$(CONFIG_USB_STORAGE) += storage +subdir-$(CONFIG_USBD) += device ifeq ($(CONFIG_USB_SERIAL),y) obj-y += serial/usb-serial.o diff -uNr linux.org/drivers/usb/device/Config.help linux/drivers/usb/device/Config.help --- linux.org/drivers/usb/device/Config.help Thu Jan 1 08:00:00 1970 +++ linux/drivers/usb/device/Config.help Thu May 15 15:54:48 2003 @@ -0,0 +1,76 @@ +CONFIG_USBD + Universal Serial Bus (USB) is a specification for a serial bus + subsystem which offers higher speeds and more features than the + traditional PC serial port. The bus supplies power to peripherals + and allows for hot swapping. Up to 127 USB peripherals can be + connected to a single USB port in a tree structure. The USB port is + the root of the tree, the peripherals are the leaves and the inner + nodes are special USB devices called hubs. Many newer PC's have USB + ports and newer peripherals such as scanners, keyboards, mice, + modems, and printers support the USB protocol and can be connected + to the PC via those ports. + + Say Y here if your computer has a USB Function / Device port and you + want to plug it into a USB Host. You then need to say Y to a MINIMUM + OF ONE of "USB Function Drivers" and a MAXIMUM of one of the "USB + Device Bus Interface Drivers". Normally only one bus interface + driver is available for any architecture. + + If you have any doubt about what this is, say N here. + + This code is also available as a module ( = code which can be + inserted in and removed from the running kernel whenever you want). + The module will be called usbd.o. If you want to compile it as a + module, say M here and read Documentation/modules.txt. + +CONFIG_USBD_VENDORID + Specify the USB Device Vendor ID. + +CONFIG_USBD_PRODUCTID + Specify the USB Device Product ID. + +CONFIG_USBD_PRODUCT_NAME + Specify the USB Device Product name. + +CONFIG_USBD_MANUFACTURER + Specify the USB Device Manufacturer name. + +CONFIG_USBD_USE_SERIAL_NUMBER + Specify whether the device serial number should be used in the device + descriptor. + +CONFIG_USBD_SERIAL_NUMBER_STR + Specify a default serial number. This will be used if the bus interface + driver cannot determine a serial number for the device. + +CONFIG_USBD_SELFPOWERED + Is the device self powered. This is used to set the appropriate flag + in the configuration descriptor. + +CONFIG_USBD_MAXPOWER + Specify the maximum power draw for this device in milli-amps (ma). + This is used in the configuration descriptor if the Self Powered + flag is NOT set. + +CONFIG_USBD_MONITOR + Enable a small module that will monitor for the USB Device being + connected to a host. It will load and unload the rest of the USB + Device software via the hotplug facility. + + Hotplug will call the usbd.agent with: + + INTERFACE=monitor + + And one of: + + ACTION=load # called when connected to USB + ACTION=unload # called when dis-connected from USB + ACTION=off # called when power down invoked + + The power down action is invoked if the local power management software + is putting the local system into standby or power off mode. This is + not invoked for a USB suspend event (which would be handled by the + USB Device software). + +CONFIG_USBD_PROCFS + Enable a proc file system for the USB Device support. diff -uNr linux.org/drivers/usb/device/Config.in linux/drivers/usb/device/Config.in --- linux.org/drivers/usb/device/Config.in Thu Jan 1 08:00:00 1970 +++ linux/drivers/usb/device/Config.in Thu May 15 15:54:48 2003 @@ -0,0 +1,42 @@ +# +# USB device configuration from the device viewpoint (e.g. Linux running inside a USB device, not as host) +# +# Copyright (C) 2001 Lineo, Inc. +# Copyright (C) 2001 Hewlett-Packard Co. +mainmenu_option next_comment + +comment 'USB Device Support' + +dep_tristate 'Support for USB Devices (as device, not host) (EXPERIMENTAL)' CONFIG_USBD $CONFIG_EXPERIMENTAL + +if [ "$CONFIG_USBD" = "y" -o "$CONFIG_USBD" = "m" ]; then + hex 'VendorID (hex value)' CONFIG_USBD_VENDORID "0000" + hex 'ProductID (hex value)' CONFIG_USBD_PRODUCTID "0000" + string ' Product Name' CONFIG_USBD_PRODUCT_NAME "" + string ' Manufacturer' CONFIG_USBD_MANUFACTURER "" + + bool ' Use Serial Number in Device Descriptor' CONFIG_USBD_USE_SERIAL_NUMBER + if [ "$CONFIG_USBD_USE_SERIAL_NUMBER" = "y" ]; then + string ' Default Serial number (string)' CONFIG_USBD_SERIAL_NUMBER_STR "" + fi + + bool ' Self Powered' CONFIG_USBD_SELFPOWERED + if [ ! "$CONFIG_USBD_SELFPOWERED" = "y" ]; then + int ' Max Power (mA)' CONFIG_USBD_MAXPOWER "0" + fi + + tristate ' USB Device Monitor' CONFIG_USBD_MONITOR + + comment '' + + bool ' USBD Proc FS' CONFIG_USBD_PROCFS + + comment 'USB Device functions' + source drivers/usb/device/net_fd/Config.in + source drivers/usb/device/serial_fd/Config.in + + comment 'USB Device bus interfaces' + source drivers/usb/device/bi/Config.in +fi + +endmenu diff -uNr linux.org/drivers/usb/device/Makefile linux/drivers/usb/device/Makefile --- linux.org/drivers/usb/device/Makefile Thu Jan 1 08:00:00 1970 +++ linux/drivers/usb/device/Makefile Thu May 15 15:54:48 2003 @@ -0,0 +1,131 @@ +# +# Makefile for the kernel USBD (device not host) drivers. +# +# Copyright (C) 2001 Lineo, Inc. +# Copyright (C) 2001 Hewlett-Packard Co. + +# Subdirs. +# This is a bit complex, because some subdirs are for +# proprietary code, and are simply not present in a +# general distribution. + +TOPDIR ?= ../../.. + +# The all-CAPS *_DIRS get nuked in the new versions +# of Rules.make, so use only the subdir-* methods. +subdir-y := +subdir-m := bi +subdir-n := +subdir- := + +subdir-$(CONFIG_USBD_NET) += net_fd +subdir-$(CONFIG_USBD_SERIAL) += serial_fd + +#subdir-$(CONFIG_USBD_GENERIC_BUS) += gen_bi +#subdir-$(CONFIG_USBD_L7205_BUS) += l7205_bi +#subdir-$(CONFIG_USBD_SA1100_BUS) += sa1100_bi +#subdir-$(CONFIG_USBD_SL11_BUS) += sl11_bi +#subdir-$(CONFIG_USBD_SUPERH_BUS) += superh_bi + +# The target object and module list name. + +# Objects that export symbols. + +export-objs := usbd.o + +# Multipart objects. + +list-multi := usbdcore.o usbdmonitor.o usbdserial.o +usbdcore-objs := usbd.o ep0.o usbd-debug.o hotplug.o usbd-func.o usbd-bus.o +usbdmonitor-objs := usbd-monitor.o hotplug.o +usbdserial-objs := usbd-serialnumber.o hotplug.o + +# Optional parts of multipart objects. + +# Object file lists. + +obj-y := usbd-bus.o usbd-func.o crc8.o crc10.o crc16.o crc32.o +obj-m := +obj-n := +obj- := + +# Each configuration option enables a list of files. + +obj-$(CONFIG_USBD) += usbdcore.o +obj-$(CONFIG_USBD_MONITOR) += usbdmonitor.o +obj-$(CONFIG_USBD_MONITOR) += usbdserial.o + +# Object files in subdirectories + + +# Extract lists of the multi-part drivers. +# The 'int-*' lists are the intermediate files used to build the multi's. + +multi-y := $(filter $(list-multi), $(obj-y)) +multi-m := $(filter $(list-multi), $(obj-m)) +int-y := $(sort $(foreach m, $(multi-y), $($(basename $(m))-objs))) +int-m := $(sort $(foreach m, $(multi-m), $($(basename $(m))-objs))) + +# Files that are both resident and modular: remove from modular. + +obj-m := $(filter-out $(obj-y), $(obj-m)) +int-m := $(filter-out $(int-y), $(int-m)) + +# Translate to Rules.make lists. + +O_OBJS := $(filter-out $(export-objs), $(obj-y)) +OX_OBJS := $(filter $(export-objs), $(obj-y)) +M_OBJS := $(sort $(filter-out $(export-objs), $(obj-m))) +MX_OBJS := $(sort $(filter $(export-objs), $(obj-m))) +MI_OBJS := $(sort $(filter-out $(export-objs), $(int-m))) +MIX_OBJS := $(sort $(filter $(export-objs), $(int-m))) + +# The global Rules.make. + +include $(TOPDIR)/Rules.make + +# Link rules for multi-part drivers. + +usbdmonitor.o: $(usbdmonitor-objs) + $(LD) -r -o $@ $(usbdmonitor-objs) + +usbdserial.o: $(usbdserial-objs) + $(LD) -r -o $@ $(usbdserial-objs) + +usbdcore.o: $(usbdcore-objs) + $(LD) -r -o $@ $(usbdcore-objs) + +# dependencies: + +usbd-monitor.o: usbd-export.h usbd-build.h +usbd-serial.o: usbd-export.h usbd-build.h +usbd.o: usbd-export.h usbd-build.h + +usbd-export.h: + echo "#define USBD_EXPORT_DATE \"`date '+%Y-%m-%d %H:%M'`\"" > $@ + +usbd-build.h: + echo "#define USBD_BUILD \"000\"" > $@ + + +# local + +%.h:%.p + release inc build < $< > $@ + cp $@ $< + +release.h: release.p + +inc-build: + release inc build < release.p > release.h + cp release.h release.p + + +menuconfig: + cd $(TOPDIR); make menuconfig + +xconfig: + cd $(TOPDIR); make xconfig + + + diff -uNr linux.org/drivers/usb/device/bi/Config.in linux/drivers/usb/device/bi/Config.in --- linux.org/drivers/usb/device/bi/Config.in Thu Jan 1 08:00:00 1970 +++ linux/drivers/usb/device/bi/Config.in Thu May 15 15:54:48 2003 @@ -0,0 +1,43 @@ +# +# USB device configuration from the device viewpoint (e.g. Linux running inside a USB device, not as host) +# +# Copyright (C) 2001 Lineo, Inc. +# Copyright (C) 2001 Hewlett-Packard Co. +mainmenu_option next_comment +comment 'USB Device Bus Interface Support' + +# StrongArm +if [ "$CONFIG_ARCH_SA1100" = "y" ]; then + dep_tristate ' Intel StrongArm SA-1110 (Assabet, Ipaq) support' CONFIG_USBD_SA1100_BUS $CONFIG_USBD + dep_mbool ' USB traffic keeps device awake' CONFIG_USBD_TRAFFIC_KEEPAWAKE $CONFIG_SA1110_CALYPSO + + if [ "$CONFIG_USBD_SA1100_BUS" = "y" -o "$CONFIG_USBD_SA1100_BUS" = "m" ]; then + int ' USBD Stall watchdog timeout (seconds, 0 for none)' CONFIG_USBD_STALL_TIMEOUT "0" $CONFIG_USBD_SA1100_BUS + int ' USBD Stall disconnect duration (seconds)' CONFIG_USBD_STALL_DISCONNECT_DURATION "2" $CONFIG_USBD_SA1100_BUS + fi +fi + +# PXA250 - Xscale +if [ "$CONFIG_ARCH_PXA" = "y" -o "$CONFIG_CPU_XSCALE" = "y" ]; then + dep_tristate ' PXA (Xscale) support' CONFIG_USBD_PXA_BUS $CONFIG_USBD +fi + +# Linkup +if [ "$CONFIG_ARCH_L7200" = "y" ]; then + dep_tristate ' L7205 (Linkup) support' CONFIG_USBD_L7205_BUS $CONFIG_USBD + dep_mbool ' L7210-AB fix' CONFIG_USBD_L7210AB_FIX $CONFIG_USBD_L7205_BUS +fi + +# Scanlogic +if [ "$CONFIG_SUPERH" = "y" -o "$CONFIG_X86" = "y" ]; then + dep_tristate ' Scanlogic SL11 Device support' CONFIG_USBD_SL11_BUS $CONFIG_USBD +fi + +# Hitachi SH7622 or SH7727 +if [ "$CONFIG_CPU_SUBTYPE_SH7622" = "y" -o "$CONFIG_CPU_SUBTYPE_SH7727" = "y" ]; then + dep_tristate ' SuperH Device support' CONFIG_USBD_SUPERH_BUS $CONFIG_USBD +fi + +dep_tristate ' Generic Bus Interfade support' CONFIG_USBD_GENERIC_BUS $CONFIG_USBD + +endmenu diff -uNr linux.org/drivers/usb/device/bi/Makefile linux/drivers/usb/device/bi/Makefile --- linux.org/drivers/usb/device/bi/Makefile Thu Jan 1 08:00:00 1970 +++ linux/drivers/usb/device/bi/Makefile Thu May 15 15:54:48 2003 @@ -0,0 +1,105 @@ +# +# Makefile for the kernel USBD (device not host) drivers. +# +# Copyright (C) 2001 Lineo, Inc. +# Copyright (C) 2001 Hewlett-Packard Co. + +# Subdirs. +# This is a bit complex, because some subdirs are for +# proprietary code, and are simply not present in a +# general distribution. + +# The all-CAPS *_DIRS get nuked in the new versions +# of Rules.make, so use only the subdir-* methods. +subdir-y := +subdir-m := +subdir-n := +subdir- := + +# The target object and module list name. + +# Objects that export symbols. + +export-objs := + +# Multipart objects. + +list-multi := gen_bi.o sl11_bi.o +gen_bi-objs := gen.o usbd-bi.o +sl11_bi-objs := sl11.o usbd-bi.o +sa1100_bi-objs := sa1100.o usbd-bi.o +superh_bi-objs := superh.o usbd-bi.o +pxa_bi-objs := pxa.o usbd-bi.o + +# Optional parts of multipart objects. + +# Object file lists. + +obj-y := usbd-bi.o +obj-m := +obj-n := +obj- := + +# Each configuration option enables a list of files. + +obj-$(CONFIG_USBD_SL11_BUS) += sl11_bi.o +obj-$(CONFIG_USBD_GENERIC_BUS) += sl11_bi.o +obj-$(CONFIG_USBD_SA1100_BUS) += sa1100_bi.o +obj-$(CONFIG_USBD_SUPERH_BUS) += superh_bi.o +obj-$(CONFIG_USBD_L7205_BUS) += l7205_bi.o +obj-$(CONFIG_USBD_PXA_BUS) += pxa_bi.o + +# Object files in subdirectories + + +# Extract lists of the multi-part drivers. +# The 'int-*' lists are the intermediate files used to build the multi's. + +multi-y := $(filter $(list-multi), $(obj-y)) +multi-m := $(filter $(list-multi), $(obj-m)) +int-y := $(sort $(foreach m, $(multi-y), $($(basename $(m))-objs))) +int-m := $(sort $(foreach m, $(multi-m), $($(basename $(m))-objs))) + +# Files that are both resident and modular: remove from modular. + +obj-m := $(filter-out $(obj-y), $(obj-m)) +int-m := $(filter-out $(int-y), $(int-m)) + +# Translate to Rules.make lists. + +O_OBJS := $(filter-out $(export-objs), $(obj-y)) +OX_OBJS := $(filter $(export-objs), $(obj-y)) +M_OBJS := $(sort $(filter-out $(export-objs), $(obj-m))) +MX_OBJS := $(sort $(filter $(export-objs), $(obj-m))) +MI_OBJS := $(sort $(filter-out $(export-objs), $(int-m))) +MIX_OBJS := $(sort $(filter $(export-objs), $(int-m))) + +# The global Rules.make. + +include $(TOPDIR)/Rules.make + +# Link rules for multi-part drivers. + +gen_bi.o: $(gen_bi-objs) + $(LD) -r -o $@ $(gen_bi-objs) + +sl11_bi.o: $(sl11_bi-objs) + $(LD) -r -o $@ $(sl11_bi-objs) + +sa1100_bi.o: $(sa1100_bi-objs) + $(LD) -r -o $@ $(sa1100_bi-objs) + +superh_bi.o: $(superh_bi-objs) + $(LD) -r -o $@ $(superh_bi-objs) + +l7205_bi.o: $(l7205_bi-objs) + $(LD) -r -o $@ $(l7205_bi-objs) + +pxa_bi.o: $(pxa_bi-objs) + $(LD) -r -o $@ $(pxa_bi-objs) + +# dependencies: + +# local + + diff -uNr linux.org/drivers/usb/device/bi/gen.c linux/drivers/usb/device/bi/gen.c --- linux.org/drivers/usb/device/bi/gen.c Thu Jan 1 08:00:00 1970 +++ linux/drivers/usb/device/bi/gen.c Thu May 15 15:54:48 2003 @@ -0,0 +1,602 @@ +/* + * linux/drivers/usbd/gen_bi/udc.c -- USB Device Controller driver. + * + * Copyright (c) 2000, 2001, 2002 Lineo + * + * By: + * Stuart Lynne , + * Tom Rushworth , + * Bruce Balden + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + * + */ + +/*****************************************************************************/ + +#include +#include + +#include "../usbd-export.h" +#include "../usbd-build.h" +#include "../usbd-module.h" + +MODULE_AUTHOR ("sl@lineo.com, tbr@lineo.com"); +MODULE_DESCRIPTION ("USB Device Prototype Bus Interface"); + +USBD_MODULE_INFO ("gen_bi 0.1-alpha"); + +#include +#include +#include +#include +#include + +#include +#include + +#include + +#include +#include + +#include + +#include +#include +#include + +#include "../usbd.h" +#include "../usbd-func.h" +#include "../usbd-bus.h" +#include "../usbd-inline.h" +#include "usbd-bi.h" + +//#include "xxxx.h" +#include "gen.h" + +#define MIN(a,b) ((a) < (b) ? (a) : (b)) +#define MAX(a,b) ((a) > (b) ? (a) : (b)) + + +static struct usb_device_instance *udc_device; // required for the interrupt handler + +/* + * ep_endpoints - map physical endpoints to logical endpoints + */ +static struct usb_endpoint_instance *ep_endpoints[UDC_MAX_ENDPOINTS]; + +static struct urb ep0_urb; +static unsigned char usb_address; + +extern unsigned int udc_interrupts; + +/* ********************************************************************************************* */ +/* IO + */ + +/* ********************************************************************************************* */ +/* Control (endpoint zero) + */ + +/* ********************************************************************************************* */ +/* Bulk OUT (recv) + */ + +/* ********************************************************************************************* */ +/* Bulk IN (tx) + */ + +/** + * send_data - send packet via endpoint + * @ep: logical endpoint number + * @bp: pointer to data + * @size: bytes to write + */ +static void __inline__ send_data (unsigned char ep, unsigned char *bp, unsigned char size) +{ + + if (bp && size) { + // copy data from buffer to chip + } + // arm +} + + +/** + * start_in - start transmit + * @ep: + */ +static void __inline__ start_in (unsigned int ep, struct usb_endpoint_instance *endpoint, + int restart) +{ + if (endpoint->tx_urb) { + struct urb *urb = endpoint->tx_urb; + + if ((urb->actual_length - endpoint->sent) > 0) { + endpoint->last = + MIN (urb->actual_length - endpoint->sent, endpoint->tx_packetSize); + send_data (ep, urb->buffer + endpoint->sent, endpoint->last); + } else { + // XXX ZLP + endpoint->last = 0; + send_data (ep, urb->buffer + endpoint->sent, 0); + } + } +} + + +/* ********************************************************************************************* */ +/* Interrupt Handler + */ + +#if 0 +/** + * int_hndlr - interrupt handler + * + */ +static void int_hndlr (int irq, void *dev_id, struct pt_regs *regs) +{ + udc_interrupts++; + +} +#endif + + +/* ********************************************************************************************* */ + + +/* ********************************************************************************************* */ +/* + * Start of public functions. + */ + +/** + * udc_start_in_irq - start transmit + * @eendpoint: endpoint instance + * + * Called by bus interface driver to see if we need to start a data transmission. + */ +void udc_start_in_irq (struct usb_endpoint_instance *endpoint) +{ + +} + +/** + * udc_init - initialize + * + * Return non-zero if we cannot see device. + **/ +int udc_init (void) +{ + // reset + + return 0; +} + + +/** + * udc_start_in - start transmit + * @eendpoint: endpoint instance + * + * Called by bus interface driver to see if we need to start a data transmission. + */ +void udc_start_in (struct usb_endpoint_instance *endpoint) +{ + if (endpoint) { + unsigned long flags; + local_irq_save (flags); + if (!endpoint->tx_urb) { + usbd_tx_complete_irq (endpoint, 0); + start_in (endpoint->endpoint_address & 0x7f, endpoint, 0); + } + local_irq_restore (flags); + } +} + + +/** + * udc_stall_ep - stall endpoint + * @ep: physical endpoint + * + * Stall the endpoint. + */ +void udc_stall_ep (unsigned int ep) +{ + if (ep < UDC_MAX_ENDPOINTS) { + // stall + } +} + + +/** + * udc_reset_ep - reset endpoint + * @ep: physical endpoint + * reset the endpoint. + * + * returns : 0 if ok, -1 otherwise + */ +void udc_reset_ep (unsigned int ep) +{ + if (ep < UDC_MAX_ENDPOINTS) { + // reset + } +} + + +/** + * udc_endpoint_halted - is endpoint halted + * @ep: + * + * Return non-zero if endpoint is halted + */ +int udc_endpoint_halted (unsigned int ep) +{ + return 0; +} + + +/** + * udc_set_address - set the USB address for this device + * @address: + * + * Called from control endpoint function after it decodes a set address setup packet. + */ +void udc_set_address (unsigned char address) +{ + // address cannot be setup until ack received + usb_address = address; +} + +#if 0 +/** + * udc_serial_init - set a serial number if available + */ +static int __init udc_serial_init (struct usb_bus_instance *bus) +{ + return -EINVAL; +} +#endif + +/* ********************************************************************************************* */ + +/** + * udc_max_endpoints - max physical endpoints + * + * Return number of physical endpoints. + */ +int udc_max_endpoints (void) +{ + return UDC_MAX_ENDPOINTS; +} + + +/** + * udc_check_ep - check logical endpoint + * @lep: + * + * Return physical endpoint number to use for this logical endpoint or zero if not valid. + */ +int udc_check_ep (int logical_endpoint, int packetsize) +{ + + return (((logical_endpoint & 0xf) >= UDC_MAX_ENDPOINTS) || (packetsize > 64)) ? + 0 : (logical_endpoint & 0xf); +} + + +/** + * udc_set_ep - setup endpoint + * @ep: + * @endpoint: + * + * Associate a physical endpoint with endpoint_instance + */ +void udc_setup_ep (struct usb_device_instance *device, unsigned int ep, + struct usb_endpoint_instance *endpoint) +{ + if (ep < UDC_MAX_ENDPOINTS) { + + ep_endpoints[ep] = endpoint; + + + // ep0 + if (ep == 0) { + } + // IN + else if (endpoint->endpoint_address & 0x80) { + } + // OUT + else if (endpoint->endpoint_address) { + + usbd_fill_rcv (device, endpoint, 5); + endpoint->rcv_urb = first_urb_detached (&endpoint->rdy); + } + } +} + + +/** + * udc_disable_ep - disable endpoint + * @ep: + * + * Disable specified endpoint + */ +void udc_disable_ep (unsigned int ep) +{ + if (ep < UDC_MAX_ENDPOINTS) { + struct usb_endpoint_instance *endpoint; + + if ((endpoint = ep_endpoints[ep])) { + ep_endpoints[ep] = NULL; + usbd_flush_ep (endpoint); + } + } +} + +/* ********************************************************************************************* */ + +/** + * udc_connected - is the USB cable connected + * + * Return non-zeron if cable is connected. + */ +int udc_connected () +{ + return 1; +} + + +/** + * udc_connect - enable pullup resistor + * + * Turn on the USB connection by enabling the pullup resistor. + */ +void udc_connect (void) +{ +} + + +/** + * udc_disconnect - disable pullup resistor + * + * Turn off the USB connection by disabling the pullup resistor. + */ +void udc_disconnect (void) +{ +} + +#if 0 +/** + * udc_int_hndlr_cable - interrupt handler for cable + */ +static void udc_int_hndlr_cable (int irq, void *dev_id, struct pt_regs *regs) +{ +} +#endif + +/* ********************************************************************************************* */ + +/** + * udc_enable_interrupts - enable interrupts + * + * Switch on UDC interrupts. + * + */ +void udc_all_interrupts (struct usb_device_instance *device) +{ + printk (KERN_DEBUG "udc_enable_interrupts:\n"); + // set interrupt mask +} + + +/** + * udc_suspended_interrupts - enable suspended interrupts + * + * Switch on only UDC resume interrupt. + * + */ +void udc_suspended_interrupts (struct usb_device_instance *device) +{ + printk (KERN_DEBUG "udc_enable_interrupts:\n"); + // set interrupt mask +} + + +/** + * udc_disable_interrupts - disable interrupts. + * + * switch off interrupts + */ +void udc_disable_interrupts (struct usb_device_instance *device) +{ + printk (KERN_DEBUG "udc_disable_interrupts:\n"); + // reset interrupt mask +} + +/* ********************************************************************************************* */ + +/** + * udc_ep0_packetsize - return ep0 packetsize + */ +int udc_ep0_packetsize (void) +{ + return EP0_PACKETSIZE; +} + +/** + * udc_enable - enable the UDC + * + * Switch on the UDC + */ +void udc_enable (struct usb_device_instance *device) +{ + + printk (KERN_DEBUG "\n"); + printk (KERN_DEBUG "udc_enable: device: %p\n", device); + + // save the device structure pointer + udc_device = device; + + // ep0 urb + ep0_urb.device = device; + usbd_alloc_urb_data (&ep0_urb, 512); + + // enable UDC + +} + + +/** + * udc_disable - disable the UDC + * + * Switch off the UDC + */ +void udc_disable (void) +{ + printk (KERN_DEBUG "************************* udc_disable:\n"); + + // disable UDC + + // reset device pointer + udc_device = NULL; + + // ep0 urb + kfree (ep0_urb.buffer); +} + + +/** + * udc_startup - allow udc code to do any additional startup + */ +void udc_startup_events (struct usb_device_instance *device) +{ + usbd_device_event (device, DEVICE_INIT, 0); + usbd_device_event (device, DEVICE_CREATE, 0); + usbd_device_event (device, DEVICE_HUB_CONFIGURED, 0); + usbd_device_event (device, DEVICE_RESET, 0); // XXX should be done from device event +} + + +/* ********************************************************************************************* */ + +/** + * udc_name - return name of USB Device Controller + */ +char *udc_name (void) +{ + return UDC_NAME; +} + +/** + * udc_request_udc_irq - request UDC interrupt + * + * Return non-zero if not successful. + */ +int udc_request_udc_irq () +{ +#ifdef XXXX_UDC_IRQ + // request IRQ and IO region + if (request_irq + (XXXX_UDC_IRQ, int_hndlr, SA_INTERRUPT | SA_SAMPLE_RANDOM, + UDC_NAME " USBD Bus Interface", NULL) != 0) { + printk (KERN_DEBUG "usb_ctl: Couldn't request USB irq\n"); + return -EINVAL; + } +#endif + return 0; +} + +/** + * udc_request_cable_irq - request Cable interrupt + * + * Return non-zero if not successful. + */ +int udc_request_cable_irq () +{ +#ifdef XXXX_CABLE_IRQ + // request IRQ and IO region + if (request_irq + (XXXX_CABLE_IRQ, int_hndlr, SA_INTERRUPT | SA_SAMPLE_RANDOM, UDC_NAME " Cable", + NULL) != 0) { + printk (KERN_DEBUG "usb_ctl: Couldn't request USB irq\n"); + return -EINVAL; + } +#endif + return 0; +} + +/** + * udc_request_udc_io - request UDC io region + * + * Return non-zero if not successful. + */ +int udc_request_io () +{ +#if defined(XXXX_ADDR) && defined(XXXX_ADDR_SIZE) + // reserve io + { + unsigned long flags; + local_irq_save (flags); + if (check_region (XXXX_ADDR, XXXX_ADDR_SIZE)) { + return -EINVAL; + } + request_region (XXXX_ADDR, 0x04, UDC_NAME " USBD Bus Interface"); + local_irq_restore (flags); + } +#endif + return 0; +} + +/** + * udc_release_udc_irq - release UDC irq + */ +void udc_release_udc_irq () +{ +#ifdef XXXX_IRQ + free_irq (XXXX_UDC_IRQ, NULL); +#endif +} + +/** + * udc_release_cable_irq - release Cable irq + */ +void udc_release_cable_irq () +{ +#ifdef XXXX_IRQ + free_irq (XXXX_CABLE_IRQ, NULL); +#endif +} + +/** + * udc_release_release_io - release UDC io region + */ +void udc_release_io () +{ +#if defined(XXXX_ADDR) && defined(XXXX_ADDR_SIZE) + release_region (XXXX_ADDR, XXXX_ADDR_SIZE); +#endif +} + + +/** + * udc_regs - dump registers + * + * Dump registers with printk + */ +void udc_regs (void) +{ + printk ("\n"); +} diff -uNr linux.org/drivers/usb/device/bi/gen.h linux/drivers/usb/device/bi/gen.h --- linux.org/drivers/usb/device/bi/gen.h Thu Jan 1 08:00:00 1970 +++ linux/drivers/usb/device/bi/gen.h Thu May 15 15:54:48 2003 @@ -0,0 +1,37 @@ +/* + * linux/drivers/usbd/gen_bi/udc.h + * + * Copyright (c) 2000, 2001, 2002 Lineo + * + * By: + * Stuart Lynne , + * Tom Rushworth , + * Bruce Balden + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + * + */ + + +#define EP0_PACKETSIZE 0x8 + +#define UDC_MAX_ENDPOINTS 4 + + +#undef XXXX_IRQ 1 +#undef XXXX_ADDR +#undef XXXX_ADDR_SIZE + +#define UDC_NAME "GenericUSBD" diff -uNr linux.org/drivers/usb/device/bi/l7205-hardware.h linux/drivers/usb/device/bi/l7205-hardware.h --- linux.org/drivers/usb/device/bi/l7205-hardware.h Thu Jan 1 08:00:00 1970 +++ linux/drivers/usb/device/bi/l7205-hardware.h Thu May 15 15:54:48 2003 @@ -0,0 +1,623 @@ +/* + * linux/drivers/usbd/l7205_bi/hardware.h -- L7205 USB controller driver. + * + * Copyright (c) 2000, 2001, 2002 Lineo + * + * By: + * Stuart Lynne , + * Tom Rushworth , + * Bruce Balden + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + * + */ + + +#define __IOA(x) ( (x) < IO_START_2 ? \ + (volatile unsigned char*) (IO_BASE + (x) - IO_START) : \ + (volatile unsigned char*) (IO_BASE_2 + (x) - IO_START_2) ) + + +/* + * usb interrupt + */ + +#define IRQ_USBF 32 + +/* + * PMU Software + */ + +#define SYSTEM_BASE 0x80050000 + +#if 0 +#define SYS_CONFIG_CURRENT (SYSTEM_BASE+0x000) +#define SYS_CONFIG_NEXT (SYSTEM_BASE+0x004) +#define SYS_CONFIG_RUN (SYSTEM_BASE+0x00c) +#define SYS_CONFIG_COMM (SYSTEM_BASE+0x010) +#define SYS_CONFIG_SDRAM (SYSTEM_BASE+0x014) + +#define IO_SYS_CONFIG_CURRENT __IOL(SYSTEM_BASE+0x000) +#define IO_SYS_CONFIG_NEXT __IOL(SYSTEM_BASE+0x004) +#define IO_SYS_CONFIG_RUN __IOL(SYSTEM_BASE+0x00c) +#define IO_SYS_CONFIG_COMM __IOL(SYSTEM_BASE+0x010) +#define IO_SYS_CONFIG_SDRAM __IOL(SYSTEM_BASE+0x014) + +#endif + + +/* + * Current Configuration Register - SYS_CONFIG_CURRENT + */ + +#define SYSC_CURR_NFASTBUS 0x00000001 +#define SYSC_CURR_DSRF_SEL 0x00000002 +#define SYSC_CURR_SDRB_SEL 0x00000004 +#define SYSC_CURR_BCLK_DIV 0x00000018 + +#define SYSC_CURR_PLLMUX 0x00000080 +#define SYSC_CURR_PLLEN 0x00000100 +#define SYSC_CURR_PLLMUL 0x00007e00 +#define SYSC_CURR_OSCMUX 0x00080000 +#define SYSC_CURR_OSCEN 0x00100000 +#define SYSC_CURR_TRANSOP 0x06000000 + + +/* + * Next Configuration Register - SYS_CONFIG_NEXT + */ + +#define SYSC_NEXT_NFASTBUS 0x00000001 +#define SYSC_NEXT_SDRF_SEL 0x00000002 +#define SYSC_NEXT_SDRB_SEL 0x00000004 +#define SYSC_NEXT_BCLK_DIV 0x00000018 + +#define SYSC_NEXT_SYSCLKEN 0x00000020 +#define SYSC_NEXT_SDR_STOP 0x00000040 + +#define SYSC_NEXT_PLLMUX 0x00000080 +#define SYSC_NEXT_PLLEN 0x00000100 +#define SYSC_NEXT_PLLMUL 0x00007e00 +#define SYSC_NEXT_OSCMUX 0x00008000 +#define SYSC_NEXT_OSCEN 0x00010000 + +#define SYSC_NEXT_INTRET 0x00020000 +#define SYSC_NEXT_CLOCKRECOVERY 0x01fc0000 +#define SYSC_NEXT_TRANSOP 0x06000000 + +/* + * Run Configuration Register - SYS_CONFIG_RUN + */ + +#define SYSC_RUN_NFASTBUS 0x00000001 +#define SYSC_RUN_DSRF_SEL 0x00000002 +#define SYSC_RUN_SDRB_SEL 0x00000004 +#define SYSC_RUN_BCLK_DIV 0x00000018 + +#define SYSC_RUN_PLLMUX 0x00000080 +#define SYSC_RUN_PLLEN 0x00000100 +#define SYSC_RUN_PLLMUL 0x00007e00 +#define SYSC_RUN_OSCMUX 0x00080000 +#define SYSC_RUN_OSCEN 0x00100000 + + +/* + * System Control + */ +#if 0 +#define SYS_CLOCK_ENABLE (SYSTEM_BASE+0x030) +#define SYS_CLOCK_ESYNC (SYSTEM_BASE+0x034) +#define SYS_CLOCK_SELECT (SYSTEM_BASE+0x038) + +#define IO_SYS_CLOCK_ENABLE __IOL(SYSTEM_BASE+0x030) +#define IO_SYS_CLOCK_ESYNC __IOL(SYSTEM_BASE+0x034) +#define IO_SYS_CLOCK_SELECT __IOL(SYSTEM_BASE+0x038) +#endif +#define SYS_CLOCK_AUX (SYSTEM_BASE+0x03c) +#define IO_SYS_CLOCK_AUX __IOL(SYSTEM_BASE+0x03c) + + +/* + * Clock Enable Register - SYS_CLOCK_ENABLE + */ + +#define SYS_CLOCK_SYN_EN 0x00000001 +#define SYS_CLOCK_B18M_EN 0x00000002 +#define SYS_CLOCK_3M6_EN 0x00000004 + +#define SYS_CLOCK_FIR_EN 0x00000020 +#define SYS_CLOCK_MIRM_EN 0x00000040 +#define SYS_CLOCK_UARTM_EN 0x00000080 +#define SYS_CLOCK_SIBADC_EN 0x00000100 +#define SYS_CLOCK_ALTD_EN 0x00000200 +#define SYS_CLOCK_CLCLK_EN 0x00000400 +#define SYS_CLOCK_STCLK_EN 0x00000800 +#define SYS_CLOCK_AUXPLL_EN 0x00001000 +#define SYS_CLOCK_AUXCLK_EN 0x00002000 +#define SYS_CLOCK_18SYN_EN 0x00004000 +#define SYS_CLOCK_SICSYN_EN 0x00008000 +#define SYS_CLOCK_NDMA_EN 0x00010000 +#define SYS_CLOCK_USBFUNC_EN 0x00020000 +#define SYS_CLOCK_USBHOST_EN 0x00080000 + + +/* + * Clock Multiplexor and Divider Control Register - SYS_CLOCK_SELECT + */ + +#define SYS_CLOCK_18M_DIV 0x00000001 +#define SYS_CLOCK_MIR_SEL 0x00000002 + +#define SYS_CLOCK_UART_SEL 0x0000000c + +#define SYS_CLOCK_ALT_CLCLK_SEL 0x00000010 +#define SYS_CLOCK_MM_DIV 0x00000020 +#define SYS_CLOCK_MM_SEL 0x00000030 + +#define SYS_CLOCK_ADC_SEL 0x00001f80 + +#define SYS_CLOCK_ALTD_SEL 0x00002000 +#define SYS_CLOCK_CL_SEL 0x00004000 +#define SYS_CLOCK_18SRC_SEL 0x00008000 +#define SYS_CLOCK_18BY_SEL 0x00010000 + +#define SYS_CLOCK_SICDIV_SEL 0x000e0000 + +#define SYS_CLOCK_SICBY_SEL 0x00100000 +#define SYS_CLOCK_SICSYN_SEL 0x00600000 +#define SYS_CLOCK_18_AUX_SEL 0x00800000 +#define SYS_CLOCK_FIRAUX_SEL 0x01000000 +#define SYS_CLOCK_SICAUX_SEL 0x02000000 + +/* + * Auxillary PLL Configuration Register - SYS_CLOCK_AUX + */ + +#define SYS_CLOCK_AUXOSCMUX 0x00000001 +#define SYS_CLOCK_AUXPLLMUX 0x00000002 +#define SYS_CLOCK_AUXPLLMUL 0x000000fc +#define SYS_CLOCK_AUXPLLMUL_18 0x00000014 +#define SYS_CLOCK_AUXPLLMUL_48 0x00000034 + + +/* + * USB Function Controller + */ + +#define USB_FUNCTION_BASE 0x8004b000 + +#if 0 + +#define USBF_REVISION (USB_FUNCTION_BASE+0x000) +#define USBF_CONTROL (USB_FUNCTION_BASE+0x004) +#define USBF_STATUS (USB_FUNCTION_BASE+0x008) +#define USBF_RAWSTATUS (USB_FUNCTION_BASE+0x00c) + +#define USBF_INTENA (USB_FUNCTION_BASE+0x010) +#define USBF_INTDIS (USB_FUNCTION_BASE+0x014) +#define USBF_INTCLR (USB_FUNCTION_BASE+0x018) +// reserved (USB_FUNCTION_BASE+0x01c) + +#define USBF_CONFIGBUF1 (USB_FUNCTION_BASE+0x020) +#define USBF_ENDPTBUF0 (USB_FUNCTION_BASE+0x024) +#define USBF_ENDPTBUF1 (USB_FUNCTION_BASE+0x028) +#define USBF_ENDPTBUF2 (USB_FUNCTION_BASE+0x02c) + +#define USBF_ENDPTBUF3 (USB_FUNCTION_BASE+0x030) +#define USBF_STRINGBUF0 (USB_FUNCTION_BASE+0x034) +#define USBF_STRINGBUF1 (USB_FUNCTION_BASE+0x038) +#define USBF_STRINGBUF2 (USB_FUNCTION_BASE+0x03c) + +#define USBF_STRINGBUF3 (USB_FUNCTION_BASE+0x040) +#define USBF_STRINGBUF4 (USB_FUNCTION_BASE+0x044) +#define USBF_F0BCNT (USB_FUNCTION_BASE+0x048) +#define USBF_F1BCNT (USB_FUNCTION_BASE+0x04c) + +#define USBF_F1TOUT (USB_FUNCTION_BASE+0x050) +#define USBF_F2BCNT (USB_FUNCTION_BASE+0x054) +#define USBF_F3BCNT (USB_FUNCTION_BASE+0x058) + +#define USBF_F2TAIL (USB_FUNCTION_BASE+0x05c) + + +#define USBF_DESCRIPTORS (USB_FUNCTION_BASE+0x100) +#endif +#define USBF_DESCRIPTORS_MAX 168 + +#if 0 +#define USBF_FIFO0 (USB_FUNCTION_BASE+0x070) +#define USBF_FIFO1 (USB_FUNCTION_BASE+0x080) +#define USBF_FIFO2 (USB_FUNCTION_BASE+0x0a0) +#define USBF_FIFO3 (USB_FUNCTION_BASE+0x0c0) +#endif + +#define USBF_FIFOS (USBF_DESCRIPTORS+USBF_DESCRIPTORS_MAX) + +#define USBF_RFIFO0 (USBF_DESCRIPTORS+USBF_DESCRIPTORS_MAX+8) +#define USBF_RFIFO1 (USBF_DESCRIPTORS+USBF_DESCRIPTORS_MAX+8+8) +#define USBF_RFIFO2 (USBF_DESCRIPTORS+USBF_DESCRIPTORS_MAX+8+8+32) +#define USBF_RFIFO3 (USBF_DESCRIPTORS+USBF_DESCRIPTORS_MAX+8+8+32+32) + + +#define IO_USBF_REVISION __IOL(USB_FUNCTION_BASE+0x000) +#define IO_USBF_CONTROL __IOL(USB_FUNCTION_BASE+0x004) +#define IO_USBF_STATUS __IOL(USB_FUNCTION_BASE+0x008) +#define IO_USBF_RAWSTATUS __IOL(USB_FUNCTION_BASE+0x00c) + +#define IO_USBF_INTENA __IOL(USB_FUNCTION_BASE+0x010) +#define IO_USBF_INTDIS __IOL(USB_FUNCTION_BASE+0x014) +#define IO_USBF_INTCLR __IOL(USB_FUNCTION_BASE+0x018) +// reserved __IOL(USB_FUNCTION_BASE+0x01c) + +#define IO_USBF_CONFIGBUF1 __IOL(USB_FUNCTION_BASE+0x020) +#define IO_USBF_ENDPTBUF0 __IOL(USB_FUNCTION_BASE+0x024) +#define IO_USBF_ENDPTBUF1 __IOL(USB_FUNCTION_BASE+0x028) +#define IO_USBF_ENDPTBUF2 __IOL(USB_FUNCTION_BASE+0x02c) + +#define IO_USBF_ENDPTBUF3 __IOL(USB_FUNCTION_BASE+0x030) +#define IO_USBF_STRINGBUF0 __IOL(USB_FUNCTION_BASE+0x034) +#define IO_USBF_STRINGBUF1 __IOL(USB_FUNCTION_BASE+0x038) +#define IO_USBF_STRINGBUF2 __IOL(USB_FUNCTION_BASE+0x03c) + +#define IO_USBF_STRINGBUF3 __IOL(USB_FUNCTION_BASE+0x040) +#define IO_USBF_STRINGBUF4 __IOL(USB_FUNCTION_BASE+0x044) +#define IO_USBF_F0BCNT __IOL(USB_FUNCTION_BASE+0x048) +#define IO_USBF_F1BCNT __IOL(USB_FUNCTION_BASE+0x04c) + +#define IO_USBF_F1TOUT __IOL(USB_FUNCTION_BASE+0x050) +#define IO_USBF_F2BCNT __IOL(USB_FUNCTION_BASE+0x054) +#define IO_USBF_F3BCNT __IOL(USB_FUNCTION_BASE+0x058) + +#define IO_USBF_F2TAIL __IOL(USB_FUNCTION_BASE+0x05c) +#define IO_USBF_F2TAIL_1 __IOL(USB_FUNCTION_BASE+0x05c) +#define IO_USBF_F2TAIL_2 __IOL(USB_FUNCTION_BASE+0x060) +#define IO_USBF_F2TAIL_3 __IOL(USB_FUNCTION_BASE+0x064) + +#define IO_USBF_FIFO0 __IOL(USB_FUNCTION_BASE+0x070) +#define IO_USBF_FIFO1 __IOL(USB_FUNCTION_BASE+0x080) +#define IO_USBF_FIFO2 __IOL(USB_FUNCTION_BASE+0x0a0) +#define IO_USBF_FIFO3 __IOL(USB_FUNCTION_BASE+0x0c0) + +#define IO_USBF_DESCRIPTORS __IOA(USB_FUNCTION_BASE+0x100) + +#define IO_USBF_FIFOS __IOA(USB_FUNCTION_BASE+0x100+USBF_DESCRIPTORS_MAX) +#define IO_USBF_FIFO_RX __IOA(USB_FUNCTION_BASE+0x100+USBF_DESCRIPTORS_MAX+16) +#define IO_USBF_FIFO_TX __IOA(USB_FUNCTION_BASE+0x100+USBF_DESCRIPTORS_MAX+48) + + +/* + * Revision Register - USBF_REVISION + */ + +#define USBF_REVISION_11 0x3 + +/* + * Control Register - USBF_CONTROL + */ + +#define USBF_CONTROL_ENBL 0x00000001 +#define USBF_CONTROL_INTD 0x00000002 +#define USBF_CONTROL_FRST 0x00000004 +#define USBF_CONTROL_RWAK 0x00000008 + +#define USBF_CONTROL_F0CLR 0x00000010 +#define USBF_CONTROL_F1CLR 0x00000020 +#define USBF_CONTROL_F2CLR 0x00000040 +#define USBF_CONTROL_F3CLR 0x00000080 + +#define USBF_CONTROL_F1MOD 0x00000300 + +#define USBF_CONTROL_F1MOD_DMA 0x00000100 +#define USBF_CONTROL_F1MOD_IO 0x00000200 + +#define USBF_CONTROL_F2MOD 0x00000c00 + +#define USBF_CONTROL_F2MOD_DMA 0x00000400 +#define USBF_CONTROL_F2MOD_IO 0x00000800 + +#define USBF_CONTROL_STAL0 0x00001000 +#define USBF_CONTROL_STAL1 0x00002000 +#define USBF_CONTROL_STAL2 0x00004000 +#define USBF_CONTROL_STAL3 0x00008000 + +#define USBF_CONTROL_F0L 0x00010000 +#define USBF_CONTROL_F2L 0x00020000 +#define USBF_CONTROL_F3L 0x00040000 + + + +/* + * Status Register - USBF_STATUS + */ + +#define USBF_STATUS_SUS 0x00000001 +#define USBF_STATUS_F0ERR 0x00000002 +#define USBF_STATUS_F1OR 0x00000004 +#define USBF_STATUS_F1ERR 0x00000008 + +#define USBF_STATUS_F2UR 0x00000010 +#define USBF_STATUS_F2ERR 0x00000020 +#define USBF_STATUS_F3ERR 0x00000040 +#define USBF_STATUS_GRSM 0x00000080 + +#define USBF_STATUS_F0RQ 0x00000100 +#define USBF_STATUS_F1RQ 0x00000200 +#define USBF_STATUS_F2RQ 0x00000400 +#define USBF_STATUS_F3RQ 0x00000800 + +#define USBF_STATUS_SOF 0x00001000 +#define USBF_STATUS_HRST 0x00002000 +#define USBF_STATUS_F1NE 0x00004000 +#define USBF_STATUS_F2NF 0x00008000 + +#define USBF_STATUS_F2NE 0x00010000 +#define USBF_STATUS_F2BSY 0x00020000 +#define USBF_STATUS_VCCMD 0x00040000 + + +/* + * Raw Status Register - USBF_RAWSTATUS + */ + +#define USBF_RAWSTATUS_RSUS 0x00000001 +#define USBF_RAWSTATUS_RF0ERR 0x00000002 +#define USBF_RAWSTATUS_RF1OR 0x00000004 +#define USBF_RAWSTATUS_RF1ERR 0x00000008 + +#define USBF_RAWSTATUS_RF2UR 0x00000010 +#define USBF_RAWSTATUS_RF2ERR 0x00000020 +#define USBF_RAWSTATUS_RF3ERR 0x00000040 +#define USBF_RAWSTATUS_RGRSM 0x00000080 + +#define USBF_RAWSTATUS_RF0RQ 0x00000100 +#define USBF_RAWSTATUS_RF1RQ 0x00000200 +#define USBF_RAWSTATUS_RF2RQ 0x00000400 +#define USBF_RAWSTATUS_RF3RQ 0x00000800 + +#define USBF_RAWSTATUS_RSOF 0x00001000 +#define USBF_RAWSTATUS_RHRST 0x00002000 + + +/* + * Interrupt Enable Register - USBF_INTENA + */ + +#define USBF_INTENA_ENSUS 0x00000001 +#define USBF_INTENA_ENF0ERR 0x00000002 +#define USBF_INTENA_ENF1OR 0x00000004 +#define USBF_INTENA_ENF1ERR 0x00000008 + +#define USBF_INTENA_ENF2UR 0x00000010 +#define USBF_INTENA_ENF2ERR 0x00000020 +#define USBF_INTENA_ENF3ERR 0x00000040 +#define USBF_INTENA_ENGRSM 0x00000080 + +#define USBF_INTENA_ENF0RQ 0x00000100 +#define USBF_INTENA_ENF1RQ 0x00000200 +#define USBF_INTENA_ENF2RQ 0x00000400 +#define USBF_INTENA_ENF3RQ 0x00000800 + +#define USBF_INTENA_ENSOF 0x00001000 +#define USBF_INTENA_ENHRST 0x00002000 + +/* + * Interrupt Disable Register - USBF_INTDIS + */ + +#define USBF_INTDIS_DNSUS 0x00000001 +#define USBF_INTDIS_DNF0ERR 0x00000002 +#define USBF_INTDIS_DNF1OR 0x00000004 +#define USBF_INTDIS_DNF1ERR 0x00000008 + +#define USBF_INTDIS_DNF2UR 0x00000010 +#define USBF_INTDIS_DNF2ERR 0x00000020 +#define USBF_INTDIS_DNF3ERR 0x00000040 +#define USBF_INTDIS_DNGRSM 0x00000080 + +#define USBF_INTDIS_DNF0RQ 0x00000100 +#define USBF_INTDIS_DNF1RQ 0x00000200 +#define USBF_INTDIS_DNF2RQ 0x00000400 +#define USBF_INTDIS_DNF3RQ 0x00000800 + +#define USBF_INTDIS_DNSOF 0x00001000 +#define USBF_INTDIS_DNHRST 0x00002000 + +/* + * Interrupt Clear Register - USBF_INTCLR + */ + +#define USBF_INTCLR_CSUS 0x00000001 +#define USBF_INTCLR_CF0ERR 0x00000002 +#define USBF_INTCLR_CF1OR 0x00000004 +#define USBF_INTCLR_CF1ERR 0x00000008 + +#define USBF_INTCLR_CF2UR 0x00000010 +#define USBF_INTCLR_CF2ERR 0x00000020 +#define USBF_INTCLR_CF3ERR 0x00000040 +#define USBF_INTCLR_CGRSM 0x00000080 + +#define USBF_INTCLR_CF0RQ 0x00000100 +#define USBF_INTCLR_CF1RQ 0x00000200 +#define USBF_INTCLR_CF2RQ 0x00000400 +#define USBF_INTCLR_CF3RQ 0x00000800 + +#define USBF_INTCLR_CSOF 0x00001000 +#define USBF_INTCLR_CHRST 0x00002000 + +/* + * Endpoint 0 Buffer Register - USBF_ENDPTBUF0 + */ + +#define USBF_ENDPTBUF0_EP0MSIZE 0x00000001 +#define USBF_ENDPTBUF0_EP0MSIZE_8 0x00000000 +#define USBF_ENDPTBUF0_EP0MSIZE_16 0x00000001 + +/* + * Endpoint 1 Buffer Register - USBF_ENDPTBUF1 + */ + +#define USBF_ENDPTBUF1_EP1MSIZE 0x000003ff + +#define USBF_ENDPTBUF1_EP1TYPE 0x00000c00 +#define USBF_ENDPTBUF1_EP1TYPE_BULK 0x00000800 + +#define USBF_ENDPTBUF1_EP1ASET 0x00003000 +#define USBF_ENDPTBUF1_EP1XFCE 0x0000c000 + +/* + * Endpoint 2 Buffer Register - USBF_ENDPTBUF2 + */ + +#define USBF_ENDPTBUF2_EP2MSIZE 0x000003ff + +#define USBF_ENDPTBUF2_EP2TYPE 0x00000c00 +#define USBF_ENDPTBUF2_EP2TYPE_BULK 0x00000800 + +#define USBF_ENDPTBUF2_EP2ASET 0x00003000 +#define USBF_ENDPTBUF2_EP2XFCE 0x0000c000 + +/* + * Endpoint 3 Buffer Register - USBF_ENDPTBUF3 + */ + +#define USBF_ENDPTBUF3_EP3TYPE 0x00000003 +#define USBF_ENDPTBUF3_EP3TYPE_INT 0x00000003 + +#define USBF_ENDPTBUF3_EP3ASET 0x0000000c +#define USBF_ENDPTBUF3_EP3XFCE 0x00000030 + +/* + * String 0 Register - USBF_STRINGBUF0 + */ + +#define USBF_STRINGBUF0_ST0ADR 0x000001ff +#define USBF_STRINGBUF0_ST0LNTH 0x0001fe00 + +/* + * String 1 Register - USBF_STRINGBUF1 + */ + +#define USBF_STRINGBUF1_ST1ADR 0x000001ff +#define USBF_STRINGBUF1_ST1LNTH 0x0001fe00 + +/* + * String 2 Register - USBF_STRINGBUF2 + */ + +#define USBF_STRINGBUF2_ST2ADR 0x000001ff +#define USBF_STRINGBUF2_ST2LNTH 0x0001fe00 + +/* + * String 3 Register - USBF_STRINGBUF3 + */ + +#define USBF_STRINGBUF3_ST3ADR 0x000001ff +#define USBF_STRINGBUF3_ST3LNTH 0x0001fe00 + +/* + * String 4 Register - USBF_STRINGBUF4 + */ + +#define USBF_STRINGBUF4_ST4ADR 0x000001ff +#define USBF_STRINGBUF4_ST4LNTH 0x0001fe00 + +/* + * FIFO0 Byte Count Register - USBF_F0BCNT + */ + +#define USBF_F0BCNT_F0BCNT 0x0000001f + +/* + * FIFO1 Byte Count Register - USBF_F1BCNT + */ + +#define USBF_F1BCNT_F1BCNT 0x0000003f + +/* + * FIFO1 Time Out Register - USBF_F1TOUT + */ + +#define USBF_F1TOUT_F1TOUT 0x0000000f + +/* + * FIFO2 Byte Count Register - USBF_F2BCNT + */ + +#define USBF_F2BCNT_F2BCNT 0x0000003f + +/* + * FIFO3 Byte Count Register - USBF_F3BCNT + */ + +#define USBF_F3BCNT_F3BCNT 0x0000003f + + + + +/* + + 0 0000 0001 + 1 0000 0002 + 2 0000 0004 + 3 0000 0008 + + 4 0000 0010 + 5 0000 0020 + 6 0000 0040 + 7 0000 0080 + + 8 0000 0100 + 9 0000 0200 + 10 0000 0400 + 11 0000 0800 + + 12 0000 1000 + 13 0000 2000 + 14 0000 4000 + 15 0000 8000 + + 16 0001 0000 + 17 0002 0000 + 18 0004 0000 + 19 0008 0000 + + 20 0010 0000 + 21 0020 0000 + 22 0040 0000 + 23 0080 0000 + + 24 0100 0000 + 25 0200 0000 + 26 0400 0000 + 27 0800 0000 + + 28 1000 0000 + 29 2000 0000 + 30 4000 0000 + 31 8000 0000 + + + 0 0000 4 0100 8 1000 c 1100 + 1 0001 5 0101 9 1001 d 1101 + 2 0010 6 0110 a 1010 e 1110 + 3 0100 7 0111 b 1011 f 1111 + + + + 0000 0000 0000 0000 0000 0000 0000 0000 + | | | | | | | | + 28 24 20 16 12 8 4 0 + +*/ diff -uNr linux.org/drivers/usb/device/bi/l7205-info.h linux/drivers/usb/device/bi/l7205-info.h --- linux.org/drivers/usb/device/bi/l7205-info.h Thu Jan 1 08:00:00 1970 +++ linux/drivers/usb/device/bi/l7205-info.h Thu May 15 15:54:48 2003 @@ -0,0 +1,179 @@ +/* + * linux/drivers/usbd/l7205_bi/l7205.h -- L7205 USB controller driver. + * + * Copyright (c) 2000, 2001, 2002 Lineo + * + * By: + * Stuart Lynne , + * Tom Rushworth , + * Bruce Balden + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + * + */ + + +#define MAX_DEVICES 1 +#undef USE_DMA + +#include + +//#define DOLOOPBACK 1 +//#define DOLOOPDUMP 1 + +/* struct l7205_bi_data + * + * private data structure for this bus interface driver + */ +struct l7205_bi_data { + int num; + //struct tq_struct sent_bh; + struct tq_struct cradle_bh; +}; + + + +int l7205_bi_send_urb (struct urb *); + +int udc_init (void); +void udc_cleanup (void); +void udc_irq_enable (void); + +//int udc_stall_ep(int); +//int udc_reset_ep(int); +void udc_dma_init (void); +void udc_disable (void); +void udc_enable (struct usb_device_instance *device); +//void udc_enable_interrupts(int); +//void udc_disable_interrupts(int); +//int udc_endpoint_halted(int); +void udc_set_address (unsigned char); + +static __inline__ void l7205_toggle (volatile unsigned long reg, unsigned int bits) +{ + reg |= bits; + udelay (10); + reg &= ~bits; +} + + +void udc_dump_fifos (char *, int); + +//int ep0_init(struct usb_device_instance *); +//int ep1_enable(struct usb_device_instance *, struct usb_endpoint_instance *); +//int ep2_enable(struct usb_device_instance *, struct usb_endpoint_instance *); + +//void ep0_int_hndlr(unsigned int); +//void ep1_int_hndlr(unsigned int, unsigned int, int); +//void ep2_int_hndlr(unsigned int, unsigned int, int, int, int, int); + +//int ep1_restart(void); +//void ep1_clear(void); +//void ep1_tick(void); + +//int ep0_reset(void); +//void ep1_reset(void); +//void ep2_reset(void); + +//int ep0_enable(struct usb_device_instance *); + + +//void ep1_disable(void); +//void ep2_disable(void); + +//void ep2_start_tx(void); + +//void ep2_send(void); + +void udc_connect (void); +void udc_disconnect (void); + +volatile int udc (volatile unsigned int *); + +void l7205_poke (void); +void l7205_cradle_event (void); + +extern unsigned int udc_interrupts; +extern unsigned int ep0_interrupts; +extern unsigned int tx_interrupts; +extern unsigned int rx_interrupts; +extern unsigned int udc_address_errors; +extern unsigned int f1rq_errors; +extern unsigned int f1err_count; +extern unsigned int udc_rpe_errors; +extern unsigned int udc_fcs_errors; +extern unsigned int udc_ep1_errors; +extern unsigned int udc_ep2_errors; + +extern int udc_address; + +void l7205_kickoff (void); +void l7205_killoff (void); +void l7205_toggle (volatile unsigned long reg, unsigned int bits); + +int l7205_bi_receive_data (int, void *, int, int); + +void l7205_bi_do_event (int, usb_device_event_t); + +#define SET_AND_TEST(s,t,c) for (c=10; ((s), (t)) && c--;) + +extern int udc_incradle (void); + +extern struct usb_device_instance *device_array[MAX_DEVICES];; + +extern int usbd_tx_dma; +extern int usbd_rx_dma; + +#include "../../usbd-debug.h" // the price of doing inlines... + +extern int dbgflg_usbdbi_init; +extern int dbgflg_usbdbi_intr; +extern int dbgflg_usbdbi_tick; +extern int dbgflg_usbdbi_usbe; +extern int dbgflg_usbdbi_rx; +extern int dbgflg_usbdbi_tx; +extern int dbgflg_usbdbi_setup; +extern int dbgflg_usbdbi_dma_flg; +extern int dbgflg_usbdbi_ep0; +extern int dbgflg_usbdbi_udc; +extern int dbgflg_usbdbi_stall; +extern int dbgflg_usbdbi_pm; + +#define dbg_init(lvl,fmt,args...) dbgPRINT(dbgflg_usbdbi_init,lvl,fmt,##args) +#define dbg_intr(lvl,fmt,args...) dbgPRINT(dbgflg_usbdbi_intr,lvl,fmt,##args) +#define dbg_tick(lvl,fmt,args...) dbgPRINT(dbgflg_usbdbi_tick,lvl,fmt,##args) +#define dbg_usbe(lvl,fmt,args...) dbgPRINT(dbgflg_usbdbi_usbe,lvl,fmt,##args) +#define dbg_rx(lvl,fmt,args...) dbgPRINT(dbgflg_usbdbi_rx,lvl,fmt,##args) +#define dbg_tx(lvl,fmt,args...) dbgPRINT(dbgflg_usbdbi_tx,lvl,fmt,##args) +#define dbg_dma_flg(lvl,fmt,args...) dbgPRINT(dbgflg_usbdbi_dma_flg,lvl,fmt,##args) +#define dbg_setup(lvl,fmt,args...) dbgPRINT(dbgflg_usbdbi_setup,lvl,fmt,##args) +#define dbg_ep0(lvl,fmt,args...) dbgPRINT(dbgflg_usbdbi_ep0,lvl,fmt,##args) +#define dbg_udc(lvl,fmt,args...) dbgPRINT(dbgflg_usbdbi_udc,lvl,fmt,##args) +#define dbg_stall(lvl,fmt,args...) dbgPRINT(dbgflg_usbdbi_stall,lvl,fmt,##args) +#define dbg_pm(lvl,fmt,args...) dbgPRINT(dbgflg_usbdbi_pm,lvl,fmt,##args) + + +#if 1 +static __inline__ volatile int _udc (volatile unsigned int *regaddr) +{ + volatile unsigned int value; + int ok; + for (ok = 1000, value = *(regaddr); value != *(regaddr) && ok--; value = *(regaddr)); + if (!ok) { + dbg_udc (0, "NOT OK: %p %x\n", regaddr, value); + } + return value; +} +#endif diff -uNr linux.org/drivers/usb/device/bi/l7205.c linux/drivers/usb/device/bi/l7205.c --- linux.org/drivers/usb/device/bi/l7205.c Thu Jan 1 08:00:00 1970 +++ linux/drivers/usb/device/bi/l7205.c Thu May 15 15:54:48 2003 @@ -0,0 +1,1494 @@ +/* + * linux/drivers/usbd/l7205_bi/udc.c -- L7205 USB controller driver. + * + * Copyright (c) 2000, 2001, 2002 Lineo + * + * By: + * Stuart Lynne , + * Tom Rushworth , + * Bruce Balden + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + * + */ + + +// Debug enabling defines +//#define TRIGGER 1 +//#define FLAG_F1ERR 1 +// End of debug enabling defines + +#include +#include + +#include +#include +#include +#include +#include + +#include +#include + +#include +#include + +#include +#include +#include +#include + +#include +#include +#include +#include +#include + +#include + +#include +#include +#include +#include + +#include + +#include "../usbd.h" +#include "../usbd-func.h" +#include "../usbd-bus.h" +#include "../usbd-inline.h" +#include "usbd-bi.h" + +#ifdef CONFIG_IRIS +#include +#include +#include +#include +#endif + +#include "hardware.h" +#include "l7205.h" +#include "udc.h" + +int sof_saw_tx_active; +int sof_saw_rx_f1ne; // track if we have seen F1NE at SOF +int host_sus_interrupt_disabled; // set if SUS interrupt received and disabled, waiting for SOF +int host_reset_interrupt_disabled; // set if HRST interrupt received and disabled, waiting for SOF +int ep2_active; // set if transmit on ep2 is active, used to track missing interrupts + +extern unsigned int udc_interrupts; +extern unsigned int udc_interrupts_last; + +static struct usb_device_instance *udc_device; // for interrupt handler + +struct usb_device_instance *ep1_device; +struct usb_endpoint_instance *ep1_endpoint; + + +#ifdef CONFIG_IRIS +#define USB_PULLUP_GPIO bitPB6 +// USB pullup resistor control. +// USB cable and AC power connect status +extern int iris_read_usb_sync (void); +extern int iris_read_ac_sync (void); +extern void iris_AUXPLL_ON_for_usb (void); +extern void iris_AUXPLL_OFF_for_usb (void); +#endif + +/* + * ep_endpoints - map physical endpoints to logical endpoints + */ +static struct usb_endpoint_instance *ep_endpoints[UDC_MAX_ENDPOINTS]; + +struct usb_device_instance *ep2_device; +struct usb_endpoint_instance *ep2_endpoint; + +#define MIN(a,b) ((a)<(b))?(a):(b) + +/* ep1 functions ******************************************************************************* */ + +/** + * ep1_clear + * + * Clear ep1 fifo by reading all data and resetting F1BCNT to 32 + */ +static /* __inline__ */ void ep1_clear (void) +{ + int j; + volatile unsigned long junk; + for (j = 0; j < 8; j++) { + junk = IO_USBF_FIFO1; + } + IO_USBF_F1BCNT = 32; +} + +/* + * Endpoint 1 interrupts will be directed here. + * + */ +static __inline__ void ep1_int_hndlr (void) +{ + if (ep1_endpoint) { + + if (!ep1_endpoint->rcv_urb) { + + ep1_endpoint->rcv_urb = first_urb_detached (&ep1_endpoint->rdy); + + // XXX could we call usbd_fill_rcv() here? + } + + if (ep1_endpoint->rcv_urb) { + + int len = IO_USBF_F1BCNT; + unsigned long *lp = + (unsigned long *) (ep1_endpoint->rcv_urb->buffer + + ep1_endpoint->rcv_urb->actual_length); + + // copy 4 bytes at a time, fetching more than available seems ok, use unrolled loop + lp[0] = IO_USBF_FIFO1; + lp[1] = IO_USBF_FIFO1; + lp[2] = IO_USBF_FIFO1; + lp[3] = IO_USBF_FIFO1; + + lp[4] = IO_USBF_FIFO1; + lp[5] = IO_USBF_FIFO1; + lp[6] = IO_USBF_FIFO1; + lp[7] = IO_USBF_FIFO1; + + usbd_rcv_complete_irq (ep1_endpoint, len, 0); + + // reset F1BCNT + IO_USBF_F1BCNT = 32; + return; + } + // fall through if error of any type + usbd_rcv_complete_irq (ep1_endpoint, 0, 0); + } + ep1_clear (); +} + +/* + * Endpoint 1 interrupts will be directed here if there was an error. + * + */ +void ep1_int_hndlr_error (void) +{ +#if 1 + int i; + volatile unsigned char *cp = (volatile unsigned char *) IO_USBF_FIFO_RX; + printk (KERN_DEBUG "ep1_int_hndlr: ERROR "); + for (i = 0; i < 32; i++) { + printk ("%02x ", cp[i]); + } + printk ("\n"); +#endif + + if (ep1_endpoint) { + usbd_rcv_complete_irq (ep1_endpoint, 0, 0); + } + ep1_clear (); +} + +/* ep2 functions ******************************************************************************* */ + +static int __inline__ ep2_fill (unsigned char *cp, int len, int delay) +{ + unsigned long *buf = (unsigned long *) cp; + volatile unsigned long *p2 = (volatile unsigned long *) IO_USBF_FIFO_TX; + + // unrolled loop to fill fifo + IO_USBF_FIFO2 = buf[0]; + IO_USBF_FIFO2 = buf[1]; + IO_USBF_FIFO2 = buf[2]; + IO_USBF_FIFO2 = buf[3]; + IO_USBF_FIFO2 = buf[4]; + IO_USBF_FIFO2 = buf[5]; + IO_USBF_FIFO2 = buf[6]; + IO_USBF_FIFO2 = buf[7]; + + if ((buf[0] != p2[0]) || (buf[1] != p2[1]) || (buf[2] != p2[2]) || (buf[3] != p2[3]) || + (buf[4] != p2[4]) || (buf[5] != p2[5]) || (buf[6] != p2[6]) || (buf[7] != p2[7])) { + udelay (8); // + // reset fifo read pointer + l7205_toggle (IO_USBF_CONTROL, USBF_CONTROL_F2CLR); + + // unrolled loop to fill fifo + IO_USBF_FIFO2 = buf[0]; + IO_USBF_FIFO2 = buf[1]; + IO_USBF_FIFO2 = buf[2]; + IO_USBF_FIFO2 = buf[3]; + IO_USBF_FIFO2 = buf[4]; + IO_USBF_FIFO2 = buf[5]; + IO_USBF_FIFO2 = buf[6]; + IO_USBF_FIFO2 = buf[7]; + + return (buf[0] != p2[0]) || (buf[1] != p2[1]) || (buf[2] != p2[2]) + || (buf[3] != p2[3]) || (buf[4] != p2[4]) || (buf[5] != p2[5]) + || (buf[6] != p2[6]) || (buf[7] != p2[7]); + } + return 0; +} + +/** + * ep2_start + * + * Fill ep2 fifo with data. + */ +static void ep2_start (void) +{ + int len; + + if (ep2_endpoint->tx_urb && + (len = MIN ((ep2_endpoint->tx_urb->actual_length - ep2_endpoint->sent), + ep2_endpoint->tx_urb->endpoint->tx_packetSize))) { + ep2_active = 1; + + // fill the FIFO, if this fails send a short packet to end of this + // attempt of the BULK transfer and force the restart of this urb at + // th beginning, the host will see a CRC error and drop the + // intermediate results + + if (ep2_fill (ep2_endpoint->tx_urb->buffer + ep2_endpoint->sent, len, 0)) { + + // ep2_fill failed, reset bulk transfer and decrement len to + // send current usb packet one byte short to guarantee host will + // terminate bulk transfer and it will fail CRC check + + IO_USBF_F2BCNT = len - 1; + ep2_endpoint->last = ep2_endpoint->sent = 0; + } else { + ep2_endpoint->last = IO_USBF_F2BCNT = len; + } + } else { + ep2_active = 0; + } +} + +/** + * ep2_int_hndlr - transmit interrupt handler, fast version + */ +static __inline__ void ep2_int_hndlr (unsigned int status) +{ + // if we have active buffer, umap the buffer and update position if previous send was successful + if (!(status & (USBF_STATUS_F2ERR | USBF_STATUS_F2BSY))) { + if (ep2_endpoint->tx_urb) { + + ep2_endpoint->sent += ep2_endpoint->last; + ep2_endpoint->last = 0; + + // if current buffer is finished call urb sent and advance to next urb + if ((ep2_endpoint->tx_urb->actual_length - ep2_endpoint->sent) <= 0) { + usbd_urb_sent_irq (ep2_endpoint->tx_urb, SEND_FINISHED_OK); + ep2_endpoint->tx_urb = NULL; + } + + ep2_start (); + } + } +} + +/** + * ep2_int_hndlr_error - transmit interrupt handler, slow version + */ +static void ep2_int_hndlr_error (unsigned int status, int error, int skip) +{ + if (!skip) { + if ((status & (USBF_STATUS_F2ERR | USBF_STATUS_F2BSY | USBF_STATUS_F2NE)) || error) { + + if (!(status & USBF_STATUS_F2BSY)) { + + dbg_tx (0, "[%d]: F2ERR %08lx", udc_interrupts, IO_USBF_STATUS); + + l7205_toggle (IO_USBF_CONTROL, USBF_CONTROL_F2CLR); + } + } else { + ep2_int_hndlr (status); + } + } +} + +/* + * l7205 + */ +void l7205_dump_fifos (char *msg, int max) +{ + int i; + + dbg_udc (9, " "); + dbg_udc (9, "*********** %s ***********", msg); + + if (7 <= dbgflg_usbdbi_udc) { + for (i = 0; i < max; i++) { + if ((i % 32) == 0) { + printk ("\nFF[%02x]: ", i); + } + printk ("%02x ", __IOB (USBF_FIFOS + i)); + } + printk ("\n"); + } +} + +void l7205_regs (char *msg) +{ + if (1 <= dbgflg_usbdbi_udc) { + int i; + printk (KERN_DEBUG "\n"); + printk (KERN_DEBUG "*********** %s ***********\n", msg); + printk (KERN_DEBUG "\n"); + printk (KERN_DEBUG "udc: Rev: %08lx Ctl: %08lx Sts: %08lx\n", + IO_USBF_REVISION, IO_USBF_CONTROL, IO_USBF_STATUS); + printk (KERN_DEBUG "udc: IntE: %08lx IntD: %08lx Ep0: %08lx\n", + IO_USBF_INTENA, IO_USBF_INTDIS, IO_USBF_ENDPTBUF0); + printk (KERN_DEBUG "udc: Ep1: %08lx Ep2: %08lx Ep3: %08lx\n", + IO_USBF_ENDPTBUF1, IO_USBF_ENDPTBUF2, IO_USBF_ENDPTBUF3); + printk (KERN_DEBUG "udc: CFG: %08lx ST0: %08lx ST1: %08lx\n", + IO_USBF_CONFIGBUF1, IO_USBF_STRINGBUF0, IO_USBF_STRINGBUF1); + printk (KERN_DEBUG "udc: ST2: %08lx ST3: %08lx ST4: %08lx\n", + IO_USBF_STRINGBUF2, IO_USBF_STRINGBUF3, IO_USBF_STRINGBUF4); + printk (KERN_DEBUG "\n"); + + printk (KERN_DEBUG "CLK ENA: %08lx AUX: %08lx SEL: %08lx\n", + IO_SYS_CLOCK_ENABLE, IO_SYS_CLOCK_AUX, IO_SYS_CLOCK_SELECT); + printk (KERN_DEBUG "\n"); + + printk (KERN_DEBUG + "udc: CFG: %02lx:%02lx ST0: %02lx:%02lx ST1: %02lx:%02lx\n", + __IOL (USBF_CONFIGBUF1) >> 9, __IOL (USBF_CONFIGBUF1) & 0x1ff, + __IOL (USBF_STRINGBUF0) >> 9, __IOL (USBF_STRINGBUF0) & 0x1ff, + __IOL (USBF_STRINGBUF1) >> 9, __IOL (USBF_STRINGBUF1) & 0x1ff); + + printk (KERN_DEBUG + "udc: ST2: %02lx:%02lx ST3: %02lx:%02lx ST4: %02lx:%02lx\n", + __IOL (USBF_STRINGBUF2) >> 9, __IOL (USBF_STRINGBUF2) & 0x1ff, + __IOL (USBF_STRINGBUF3) >> 9, __IOL (USBF_STRINGBUF3) & 0x1ff, + __IOL (USBF_STRINGBUF4) >> 9, __IOL (USBF_STRINGBUF4) & 0x1ff); + + for (i = 0; i < 168; i++) { + if ((i % 32) == 0) { + printk ("\nDS[%02x]: ", i); + } + printk ("%02x ", __IOB (USBF_DESCRIPTORS + i)); + } + printk ("\n"); + printk ("\n"); + } +} + +void udc_regs (void) +{ + if (1 <= dbgflg_usbdbi_tick) { + printk (KERN_DEBUG + "%u Ctl: %08lx Sts: %08lx Raw: %08lx Ena: %08lx EP0: %08lx EP1: %08lx EP2: %08lx " + "int: %2d\n", + udc_interrupts, + IO_USBF_CONTROL, IO_USBF_STATUS, IO_USBF_RAWSTATUS, IO_USBF_INTENA, + IO_USBF_F0BCNT, IO_USBF_F1BCNT, IO_USBF_F2BCNT, + udc_interrupts - udc_interrupts_last); + udc_interrupts_last = udc_interrupts; + } +} + + +/* + * l7205_copy_descriptor + * + * Copy descriptor to shared SRAM and optionally set descriptor register to point + * to where it is located. + * + * Note that reg CAN be NULL (there is no device descriptor register it is + * always at zero offset) + */ +static int l7205_copy_descriptor (unsigned char *descriptors, volatile unsigned long reg, + int offset, struct usb_descriptor *descriptor, int size) +{ + // set default + if (reg) { + __IOL (reg) = 0; + } + + if (!descriptors || !descriptor) { + return offset; + } + + if (!size) { + if (!(size = descriptor->descriptor.generic.bLength)) { + return offset; + } + } + + if (((offset + size) >= (USBF_DESCRIPTORS_MAX - 4))) { + dbg_udc (0, "string too large to copy: offset: %x size: %d", offset, size); + if (reg) { + __IOL (reg) = (4 << 9) | (USBF_DESCRIPTORS_MAX - 4); + } + descriptors[USBF_DESCRIPTORS_MAX - 4] = 4; + descriptors[USBF_DESCRIPTORS_MAX - 3] = 3; + descriptors[USBF_DESCRIPTORS_MAX - 2] = 'A'; + descriptors[USBF_DESCRIPTORS_MAX - 1] = 0; + return offset; + } + + dbg_udc (3, "reg: %08lx offset: %02x descriptor: %p size: %02x", reg, offset, descriptor, + size); + + // set size and offset if we have a register + if (reg) { + __IOL (reg) = (size << 9) | offset; + dbg_udc (3, "reg: %8lx %8lx size: %2lx off: %2lx", reg, __IOL (reg), + __IOL (reg) >> 9, __IOL (reg) & 0x1ff); + } + + memcpy (descriptors + offset, descriptor, size); + + return (offset + size + 3) & 0x1fc; // 4 +} + + +static int l7205_copy_request (unsigned char *descriptors, struct usb_device_instance *device, + volatile unsigned long reg, int offset, int request) +{ + struct urb *udc_urb; + usb_device_state_t device_state; + + /* + * fake a setup request to get descriptors + */ + if (!device || !(udc_urb = usbd_alloc_urb (device, NULL, 0, 512))) { + dbg_udc (0, "cannot alloc urb"); + return offset; + } + + udc_urb->device_request.bmRequestType = 0x80; + udc_urb->device_request.bRequest = USB_REQ_GET_DESCRIPTOR; + udc_urb->device_request.wValue = cpu_to_le16 ((request << 8)); + udc_urb->device_request.wLength = cpu_to_le16 (200); // max 168? + + device_state = device->device_state; + device->device_state = STATE_ADDRESSED; + + if (usbd_recv_setup (udc_urb)) { + dbg_udc (0, "cannot process setup request"); + usbd_dealloc_urb (udc_urb); + return offset; + } + + device->device_state = device_state; + offset = + l7205_copy_descriptor (descriptors, reg, offset, + (struct usb_descriptor *) udc_urb->buffer, + udc_urb->actual_length); + usbd_dealloc_urb (udc_urb); + + return offset; +} + +/** + * udc_init - initialize L7205 USB Controller + * + * Get ready to use the L7205 USB Controller. + * + * Register an interrupt handler and initialize dma. + */ +int udc_init (void) +{ + // This should never happen! + if (IO_USBF_REVISION != USBF_REVISION_11) { + dbg_udc (0, "incorrect or not found version: %02lx should be %02x", + IO_USBF_REVISION, USBF_REVISION_11); + return -EINVAL; + } + // disable the udc just in case + udc_disable_interrupts (0); + udc_disconnect (); + udc_disable (); + return 0; +} + +/* + * Switch off the L7205 USB Function + */ +void udc_disable (void) +{ + udc_device = NULL; + + // disable interrupts + IO_USBF_INTDIS = + USBF_INTDIS_DNSUS | + USBF_INTDIS_DNF0ERR | + USBF_INTDIS_DNF1OR | + USBF_INTDIS_DNF1ERR | + USBF_INTDIS_DNF2UR | + USBF_INTDIS_DNF2ERR | + USBF_INTDIS_DNF3ERR | + USBF_INTDIS_DNGRSM | + USBF_INTDIS_DNF0RQ | + USBF_INTDIS_DNF1RQ | + USBF_INTDIS_DNF2RQ | USBF_INTDIS_DNF3RQ | USBF_INTDIS_DNSOF | USBF_INTDIS_DNHRST; + + // set USBFUNC_EN + IO_SYS_CLOCK_ENABLE &= ~SYS_CLOCK_USBFUNC_EN; + + // reset USB function + // IO_USBF_CONTROL |= USBF_CONTROL_FRST; + + IO_USBF_CONTROL = 0; + + // turn off clocks +#ifdef xCONFIG_IRIS + iris_AUXPLL_OFF_for_usb (); +#else + IO_SYS_CLOCK_ENABLE &= ~(SYS_CLOCK_AUXPLL_EN | SYS_CLOCK_AUXCLK_EN); +#endif +} + +/* + * initialize L7205 USB Function + */ +void l7205_init (struct usb_device_instance *device) +{ + //int i; + int offset; + unsigned char *descriptors; + + // XXX disable everything + IO_USBF_CONTROL = 0; + udelay (20); + +#if defined(CONFIG_L7205SDB) + /* + * Linkup reports that there may be a problem with video conflicting with + * USB Function, this will disable it. + */ + + dbg_udc (2, "1. CLCDCON: %08lx SYS_CLOCK_ENABLE: %08lx", IO_CLCDCON, IO_SYS_CLOCK_ENABLE); + + // disable LCD power (bit 22) and wait 20MS + IO_CLCDCON &= ~0x20000; + udelay (100); + dbg_udc (2, "2. CLCDCON: %08lx SYS_CLOCK_ENABLE: %08lx", IO_CLCDCON, IO_SYS_CLOCK_ENABLE); + + // disable LCD (bit 0) + IO_CLCDCON &= ~0x1; + dbg_udc (2, "3. CLCDCON: %08lx SYS_CLOCK_ENABLE: %08lx", IO_CLCDCON, IO_SYS_CLOCK_ENABLE); + + // disable Vee and backlight bits 1, 2 + IO_SYS_CLOCK_ENABLE &= ~0x3; + dbg_udc (2, "4. CLCDCON: %08lx SYS_CLOCK_ENABLE: %08lx", IO_CLCDCON, IO_SYS_CLOCK_ENABLE); +#endif + + /* + * Setup FIRCLK to use internal clock - c.f. 13.1.2 Clocks + */ + dbg_udc (5, "setting clocks (internal PLL)"); + + // clear AUXOSCMUX, AUXPLLMUX and AUXPLLMUL bits of CLOCK_AUX + IO_SYS_CLOCK_AUX = 0; + + // clear AUX0SCMUX and set AUXPLLMUL to 13 for 48Mhz + IO_SYS_CLOCK_AUX |= SYS_CLOCK_AUXPLLMUL_48; + + // set FIRAUX_SEL + IO_SYS_CLOCK_SELECT |= SYS_CLOCK_FIRAUX_SEL; + + // set AUXPLL_EN and AUXCLK_EN +#ifdef xCONFIG_IRIS + iris_AUXPLL_ON_for_usb (); +#else + IO_SYS_CLOCK_ENABLE |= SYS_CLOCK_AUXPLL_EN | SYS_CLOCK_AUXCLK_EN; +#endif + + // wait 8us + udelay (10); + + // set USBFUNC_EN to enable clock + IO_SYS_CLOCK_ENABLE |= SYS_CLOCK_USBFUNC_EN; + + // wait 8us + udelay (10); + + // enable USB Function + IO_USBF_CONTROL |= USBF_CONTROL_ENBL; + udelay (20); + + // set program modes + IO_USBF_CONTROL |= USBF_CONTROL_F1MOD_IO | USBF_CONTROL_F2MOD_IO; + + // Force USB function core reset + // l7205_toggle(IO_USBF_CONTROL, USBF_CONTROL_FRST); + + // Reset and clear all FIFO's + // l7205_toggle(IO_USBF_CONTROL, USBF_CONTROL_F0CLR | USBF_CONTROL_F1CLR | USBF_CONTROL_F2CLR | USBF_CONTROL_F3CLR); + + // allocate tmp buffer + if ((descriptors = kmalloc (USBF_DESCRIPTORS_MAX, GFP_ATOMIC))) { + + // clear the descriptor space + memset (descriptors, 0, USBF_DESCRIPTORS_MAX); + + // XXX memset((void *)(__IOA(USBF_DESCRIPTORS)), 0, USBF_DESCRIPTORS_MAX); + + // copy in device, configuration, langid and up to four strings + offset = l7205_copy_request (descriptors, device, 0, 0, USB_DESCRIPTOR_TYPE_DEVICE); + offset = + l7205_copy_request (descriptors, device, USBF_CONFIGBUF1, offset, + USB_DESCRIPTOR_TYPE_CONFIGURATION); + offset = + l7205_copy_descriptor (descriptors, USBF_STRINGBUF0, offset, + (struct usb_descriptor *) usbd_get_string (0), 0); + offset = + l7205_copy_descriptor (descriptors, USBF_STRINGBUF1, offset, + (struct usb_descriptor *) usbd_get_string (1), 0); + offset = + l7205_copy_descriptor (descriptors, USBF_STRINGBUF2, offset, + (struct usb_descriptor *) usbd_get_string (2), 0); + offset = + l7205_copy_descriptor (descriptors, USBF_STRINGBUF3, offset, + (struct usb_descriptor *) usbd_get_string (3), 0); + offset = + l7205_copy_descriptor (descriptors, USBF_STRINGBUF4, offset, + (struct usb_descriptor *) usbd_get_string (4), 0); + + // copy descriptors to shared RAM + memcpy ((unsigned char *) __IOA (USBF_DESCRIPTORS), descriptors, + USBF_DESCRIPTORS_MAX); + kfree (descriptors); + } else { + // should never happen + dbg_udc (0, "cannot malloc descriptors"); + } + + /* + * Configuration and Intialization - c.f. 13.1.4 and c.f. 13.1.1 + */ + IO_USBF_ENDPTBUF0 = USBF_ENDPTBUF0_EP0MSIZE_8; + IO_USBF_ENDPTBUF1 = USBF_ENDPTBUF1_EP1TYPE_BULK | 0x20; + IO_USBF_ENDPTBUF2 = USBF_ENDPTBUF2_EP2TYPE_BULK | 0x20; + IO_USBF_ENDPTBUF3 = USBF_ENDPTBUF3_EP3TYPE_INT; + + IO_USBF_F1TOUT = 1; + IO_USBF_F1BCNT = 0x20; + IO_USBF_F2BCNT = 0; + + // Force USB function core reset + l7205_toggle (IO_USBF_CONTROL, USBF_CONTROL_FRST); + + // Reset and clear all FIFO's + l7205_toggle (IO_USBF_CONTROL, + USBF_CONTROL_F0CLR | USBF_CONTROL_F1CLR | USBF_CONTROL_F2CLR | + USBF_CONTROL_F3CLR); + + // Clear interrupts + IO_USBF_INTCLR = 0x3fff; + + // set initialization done bit to indicate descriptors and registers are ready + IO_USBF_CONTROL |= USBF_CONTROL_INTD; + + // enable interrupts + IO_USBF_INTENA = + USBF_INTENA_ENSUS | + USBF_INTENA_ENF0ERR | + USBF_INTENA_ENF1OR | + USBF_INTENA_ENF1ERR | + USBF_INTENA_ENF2UR | + USBF_INTENA_ENF2ERR | + USBF_INTENA_ENF3ERR | + USBF_INTENA_ENGRSM | + USBF_INTENA_ENF0RQ | + USBF_INTENA_ENF1RQ | + USBF_INTENA_ENF2RQ | USBF_INTENA_ENF3RQ | USBF_INTENA_ENSOF | USBF_INTENA_ENHRST; + + // wait + udelay (60); // XXX + + l7205_regs ("finished"); +} + + +/* + * Switch on the L7205 USB Function + */ +void udc_enable (struct usb_device_instance *device) +{ + udc_device = device; + l7205_init (udc_device); // XXX do when Vbus is present.... + return; +} + +#if defined(TRIGGER) + +static unsigned int max_triggers = 64; + +static void send_usb_trigger (unsigned int status, char *id) +{ + /* This code is to insert a "trigger" into the outgoing data stream + so that the a USB protocol anlyser can capture the data surrounding + it. It is only used when debugging strange hardware states. + The "trigger" is data that looks like "0xdeadbeef". */ + if (max_triggers == 0) { + printk (KERN_DEBUG "%s & no more triggers\n", id); + return; + } + if (status & USBF_STATUS_F2BSY) { + printk (KERN_DEBUG "%s & F2BSY\n", id); + } else { + static char flag_bytes[48] = { + 0xde, 0xad, 0xbe, 0xef, 0xde, 0xad, 0xbe, 0xef, 0xde, 0xad, 0xbe, 0xef, + 0xde, 0xad, 0xbe, 0xef, + 0xde, 0xad, 0xbe, 0xef, 0xde, 0xad, 0xbe, 0xef, 0xde, 0xad, 0xbe, 0xef, + 0xde, 0xad, 0xbe, 0xef, + 0xde, 0xad, 0xbe, 0xef, 0xde, 0xad, 0xbe, 0xef, 0xde, 0xad, 0xbe, 0xef, + 0xde, 0xad, 0xbe, 0xef + }; + unsigned long *slp; + unsigned long *txf = IO_USBF_FIFO_TX; + int i, j, fifo_good; + slp = + (unsigned long *) (void *) (0xfffffffc & + (unsigned long) (3 + (void *) flag_bytes)); + j = 0; + do { + for (i = 0; i < 8; i++) { + IO_USBF_FIFO2 = slp[i]; + } + fifo_good = 1; + for (i = 0; i < 8; i++) { + fifo_good &= (slp[i] == txf[i]); + } + } while (++j < 10 && !fifo_good); + if (fifo_good) { + IO_USBF_F2BCNT = 32; + printk (KERN_DEBUG "deadbeef %u %s\n", max_triggers, id); + max_triggers -= 1; + } else { + // too busy receiving? + printk (KERN_DEBUG "%s & F1BSY\n", id); + } + } +} +#endif + +#define STATUS_NO_INTR_MASK ~(USBF_STATUS_F1NE | USBF_STATUS_F2NF | USBF_STATUS_F2NE | USBF_STATUS_F2BSY | USBF_STATUS_VCCMD); + + +/** + * udc_int_hndlr - interrupt handler + * + */ +void udc_int_hndlr (int irq, void *dev_id, struct pt_regs *regs) +{ + int loopcount; + unsigned int rawstatus; + int ep2_int_hndlr_called = 0; + + udc_interrupts++; + // XXX rawstatus = IO_USBF_RAWSTATUS; + for (loopcount = 0; 0 != (rawstatus = IO_USBF_RAWSTATUS) && (loopcount < 4); loopcount++) { + unsigned int status = IO_USBF_STATUS; + + IO_USBF_INTCLR = rawstatus; + + /* + * Handle high priority interrupts first to reduce latency and + * overhead. + */ + + if (rawstatus & USBF_RAWSTATUS_RF1RQ) { + ep1_int_hndlr (); + sof_saw_rx_f1ne = 0; + } + + if (rawstatus & USBF_RAWSTATUS_RF2RQ) { + ep2_int_hndlr (status); + ep2_int_hndlr_called++; + sof_saw_tx_active = 0; + } + + /* + * check if we have one of the un-common interrupts, this is faster + * than testing for each one sequentially. Note that we will do the + * SOF test first, and then another general test for the really + * un-common interrupts. + */ + if (rawstatus & + (USBF_RAWSTATUS_RSUS | USBF_RAWSTATUS_RF0ERR | USBF_RAWSTATUS_RF1OR | + USBF_RAWSTATUS_RF1ERR | USBF_RAWSTATUS_RF2ERR | USBF_RAWSTATUS_RF3ERR | + USBF_RAWSTATUS_RGRSM | USBF_RAWSTATUS_RF0RQ | USBF_RAWSTATUS_RF3RQ | + USBF_RAWSTATUS_RHRST | USBF_RAWSTATUS_RSOF)) { + /* + * SOF - Start of frame interrupt, use as a safe place to check + * for missed transmit, receive interrupts, turn back on SUS or + * RST interrupts etc. + */ + if (rawstatus & USBF_RAWSTATUS_RSOF) { + + /* + * Simulate an rx interrupt, if we have not seen a rcv + * interrupt recently. + */ + if (IO_USBF_STATUS & USBF_STATUS_F1NE) { + if ((sof_saw_rx_f1ne++ > 2)) { + dbg_tick (1, "[%d]: F1NE %ld", udc_interrupts, + IO_USBF_F1BCNT); + ep1_clear (); + sof_saw_rx_f1ne = 0; + } + } else { + sof_saw_rx_f1ne = 0; + } + + /* + * This is required in case we miss a tx interrupt or we + * abandoned a tx attempt due to contention with rx fifo + */ + if ((sof_saw_tx_active++ > 10) && ep2_active) { + ep2_int_hndlr_error (status, 0, ep2_int_hndlr_called++); + sof_saw_tx_active = 0; + } + + /* + * The SUS and HRST interrupts disable themselves, now is + * the time to re-enable them. + */ + if (host_sus_interrupt_disabled) { + IO_USBF_INTENA = USBF_INTENA_ENSUS; + host_sus_interrupt_disabled = 0; + usbd_device_event (udc_device, DEVICE_BUS_ACTIVITY, 0); + } + if (host_reset_interrupt_disabled) { + IO_USBF_INTENA = USBF_INTENA_ENHRST; + host_reset_interrupt_disabled = 0; + usbd_device_event (udc_device, DEVICE_ADDRESS_ASSIGNED, 0); + udc_device->configuration = 0; + usbd_device_event (udc_device, DEVICE_CONFIGURED, 0); + udc_device->interface = 0; + udc_device->alternate = 0; + usbd_device_event (udc_device, DEVICE_SET_INTERFACE, 0); + } + + /* + * If these three bits are on all is lost, we will + * disconnect. + */ + if ((status & USBF_STATUS_F2BSY) && (status & USBF_STATUS_F2NE) + && (status & USBF_STATUS_F2NF)) { + // disconnect and reset udc + // XXX udc_disconnect(); + // XXX udc_connect(); + } + } + + /* + * check if we have a really uncommon interrupt, this saves + * sequential testing of all of these during SOF only + * interrupts. + */ + if (rawstatus & + (USBF_RAWSTATUS_RSUS | USBF_RAWSTATUS_RF0ERR | USBF_RAWSTATUS_RF1OR | + USBF_RAWSTATUS_RF1ERR | USBF_RAWSTATUS_RF2ERR | USBF_RAWSTATUS_RF3ERR | + USBF_RAWSTATUS_RGRSM | USBF_RAWSTATUS_RF0RQ | USBF_RAWSTATUS_RF3RQ | + USBF_RAWSTATUS_RHRST)) { + /* + * SUS - suspend interrupt, issued device event and disable SUS interrupt + * until we see an SOF interrupt + */ + if (rawstatus & USBF_RAWSTATUS_RSUS) { + dbg_intr (0, "[%u]: SUS Sts: %08x Raw: %08x", + udc_interrupts, status, rawstatus); + // disable host interrupt + IO_USBF_INTDIS = USBF_INTDIS_DNSUS; + host_sus_interrupt_disabled++; + usbd_device_event (udc_device, DEVICE_BUS_INACTIVE, 0); + } + + /* + * HRST - Host Reset interrupt, issued device event and disable SUS interrupt + * until we see an SOF interrupt + */ + if (rawstatus & USBF_RAWSTATUS_RHRST) { + // L7210 AB version would give multiple HRST when the first is cleared, + // ignore them until SOF interrupt + + dbg_intr (0, "[%u]: HRST", udc_interrupts); + // XXX l7205_toggle(IO_USBF_CONTROL, USBF_CONTROL_FRST); + + // reset udc + l7205_init (udc_device); // XXX + + // disable host interrupt + IO_USBF_INTDIS = USBF_INTDIS_DNHRST; + host_reset_interrupt_disabled++; + usbd_device_event (udc_device, DEVICE_RESET, 0); + } + + /* + * F1ERR - FIFO 1 error, a receive error, bad news, usually once + * this has happened we cannot continue or reset to normal + * state. In desparation we will drop connection and hope that + * HRST will restore things. + */ + if (rawstatus & USBF_RAWSTATUS_RF1ERR) { + dbg_intr (0, "[%u]: F1ERR Sts: %08x Raw: %08x F1Bcnt: %ld", + udc_interrupts, status, rawstatus, + IO_USBF_F1BCNT); + sof_saw_rx_f1ne = 0; +#if defined(FLAG_F1ERR) && defined(TRIGGER) + send_usb_trigger (status, "F1ERR"); +#endif + udc_disconnect (); + ep1_int_hndlr_error (); + udelay (100); + udc_connect (); + udelay (100); + udc_disconnect (); + + // XXX udc_disconnect(); + // XXX l7205_init(udc_device); // XXX + } + + /* + * Misc Error interrupts - have never seen any of these. + */ + if (rawstatus & USBF_RAWSTATUS_RF0ERR) { + dbg_intr (0, "[%u]: F0ERR Sts: %08x Raw: %08x", + udc_interrupts, status, rawstatus); + } + if (rawstatus & USBF_RAWSTATUS_RF1OR) { + dbg_intr (0, "[%u]: F1OR Sts: %08x Raw: %08x", + udc_interrupts, status, rawstatus); + ep1_int_hndlr_error (); + sof_saw_rx_f1ne = 0; + } + if (rawstatus & USBF_RAWSTATUS_RF2UR) { + dbg_intr (0, "[%u]: F2UR Sts: %08x Raw: %08x", + udc_interrupts, status, rawstatus); + sof_saw_tx_active = 0; + ep2_int_hndlr_error (status, 1, ep2_int_hndlr_called++); + } + if (rawstatus & USBF_RAWSTATUS_RF2ERR) { + dbg_intr (0, "[%u]: F2ERR Sts: %08x Raw: %08x", + udc_interrupts, status, rawstatus); + sof_saw_tx_active = 0; + ep2_int_hndlr_error (status, 1, ep2_int_hndlr_called++); + } + if (rawstatus & USBF_RAWSTATUS_RF3ERR) { + dbg_intr (0, "[%u]: F3ERR Sts: %08x Raw: %08x", + udc_interrupts, status, rawstatus); + } + if (rawstatus & USBF_RAWSTATUS_RGRSM) { + dbg_intr (0, "[%u]: GRSM Sts: %08x Raw: %08x", + udc_interrupts, status, rawstatus); + } + if (rawstatus & USBF_RAWSTATUS_RF0RQ) { + dbg_intr (0, "[%u]: F0RQ Sts: %08x Raw: %08x", + udc_interrupts, status, rawstatus); + } + if (rawstatus & USBF_RAWSTATUS_RF3RQ) { + dbg_intr (0, "[%u]: F3RQ Sts: %08x Raw: %08x", + udc_interrupts, status, rawstatus); + } + } + } + } // end of loopcount loop +} + +/* ********************************************************************************************* */ +/* + * Start of public functions. + */ + +/** + * udc_request_udc_irq - request UDC interrupt + * + * Return non-zero if not successful. + */ +int udc_request_udc_irq () +{ + // request IRQ + if (request_irq + (IRQ_USBF, udc_int_hndlr, SA_INTERRUPT | SA_SAMPLE_RANDOM, "L7205 USBD Bus Interface", + NULL) != 0) { + dbg_init (0, "Couldn't request USB irq"); + return -EINVAL; + } + return 0; +} + +/** + * udc_start_in_irq - start transmit + * @eendpoint: endpoint instance + * + * Called by bus interface driver to see if we need to start a data transmission. + */ +void udc_start_in_irq (struct usb_endpoint_instance *endpoint) +{ + if (!(IO_USBF_STATUS & USBF_STATUS_F2BSY)) { + ep2_start (); + } +} + +/** + * udc_stall_ep - stall endpoint + * @ep: physical endpoint + * + * Stall the endpoint. + */ +void udc_stall_ep (unsigned int ep) +{ +} + +/** + * udc_reset_ep - reset endpoint + * @ep: physical endpoint + * reset the endpoint. + * + * returns : 0 if ok, -1 otherwise + */ +void udc_reset_ep (unsigned int ep) +{ + if (ep < UDC_MAX_ENDPOINTS) { + // reset + + switch (ep) { + case 0: + case 1: + case 0x82: + /* do nothing */ + break; + } + } +} + +/** + * udc_endpoint_halted - is endpoint halted + * @ep: + * + * Return non-zero if endpoint is halted + */ +int udc_endpoint_halted (unsigned int ep) +{ + return 0; +} + +/** + * udc_set_address - set the USB address for this device + * @address: + * + * Called from control endpoint function after it decodes a set address setup packet. + */ +void udc_set_address (unsigned char address) +{ +} + + +static int crc32 (char *s, int length) +{ + /* indices */ + int pByte; + int pBit; + + const unsigned long poly = 0xedb88320; + unsigned long crc_value = 0xffffffff; + + for (pByte = 0; pByte < length; pByte++) { + unsigned char c = *(s++); + for (pBit = 0; pBit < 8; pBit++) { + crc_value = (crc_value >> 1) ^ (((crc_value ^ c) & 0x01) ? poly : 0); + c >>= 1; + } + } + return crc_value; +} + +/** + * udc_serial_init - set a serial number if available + */ +int __init udc_serial_init (struct usb_bus_instance *bus) +{ +#ifdef CONFIG_IRIS + iris_serial_number_struct serial; + + if (!iris_get_serial_number (&serial) + && (bus->serial_number_str = kmalloc (36, GFP_KERNEL))) { + sprintf (bus->serial_number_str, "%08x-%08x-%08x-%08x", serial.b3, serial.b2, + serial.b1, serial.b0); + bus->serial_number = crc32 (bus->serial_number_str, 32); + return 0; + } +#endif + return -EINVAL; +} + +/** + * udc_max_endpoints - max physical endpoints + * + * Return number of physical endpoints. + */ +int udc_max_endpoints (void) +{ + return UDC_MAX_ENDPOINTS; +} + +/** + * udc_check_ep - check logical endpoint + * @lep: + * + * Return physical endpoint number to use for this logical endpoint or zero if not valid. + */ +int udc_check_ep (int logical_endpoint, int packetsize) +{ + if (packetsize > 32) { + return 0; + } + switch (logical_endpoint) { + case 1: + return 1; + case 0x82: + return 2; + case 0x83: + return 3; + default: + return 0; + } +} + +/** + * udc_set_ep - setup endpoint + * @ep: + * @endpoint: + * + * Associate a physical endpoint with endpoint_instance + */ +void udc_setup_ep (struct usb_device_instance *device, unsigned int ep, + struct usb_endpoint_instance *endpoint) +{ + dbg_init (1, "ep: %d", ep); + if (ep < UDC_MAX_ENDPOINTS) { + ep_endpoints[ep] = endpoint; + switch (ep) { + case 0: // Control + break; + case 1: // OUT + usbd_fill_rcv (device, endpoint, 10); // XXX + endpoint->rcv_urb = first_urb_detached (&endpoint->rdy); + ep1_device = device; + ep1_endpoint = endpoint; + break; + case 2: // IN + ep2_device = device; + ep2_endpoint = endpoint; + break; + } + } +} + +/** + * udc_disable_ep - disable endpoint + * @ep: + * + * Disable specified endpoint + */ +void udc_disable_ep (unsigned int ep) +{ + if (ep < UDC_MAX_ENDPOINTS) { + struct usb_endpoint_instance *endpoint; + + switch (ep) { + case 0: // Control + break; + case 1: // OUT + ep1_endpoint = NULL; + break; + case 2: // IN + ep2_device = NULL; + ep2_endpoint = NULL; + break; + } + + if ((endpoint = ep_endpoints[ep])) { + ep_endpoints[ep] = NULL; + usbd_flush_ep (endpoint); + } + + } +} + +/** + * udc_connected - is the USB cable connected + * + * Return non-zeron if cable is connected. + */ +int udc_connected () +{ +#ifdef CONFIG_IRIS + // Return TRUE if USB cable shows Vbus _and_ AC power present + dbg_pur (0, "Vbus: %x AC: %x", iris_read_usb_sync (), iris_read_ac_sync ()); + return (iris_read_usb_sync () && iris_read_ac_sync ()); +#else + return 1; +#endif +} + +/** + * udc_connect - enable pullup resistor + * + * Turn on the USB connection by enabling the pullup resistor. + */ +void udc_connect (void) +{ +#ifdef CONFIG_IRIS + // Enable the pullup resistor. + dbg_pur (0, "Enabling IRIS USB pullup resistor"); + SET_PB_OUT (USB_PULLUP_GPIO); + SET_PBDR_HI (USB_PULLUP_GPIO); + SET_PBSBSR_LO (USB_PULLUP_GPIO); +#endif +} + +/** + * udc_disconnect - disable pullup resistor + * + * Turn off the USB connection by disabling the pullup resistor. + */ +void udc_disconnect (void) +{ +#ifdef CONFIG_IRIS + // Disable the pullup resistor. + dbg_pur (0, "Disabling IRIS USB pullup resistor"); + SET_PB_OUT (USB_PULLUP_GPIO); + SET_PBDR_LO (USB_PULLUP_GPIO); + SET_PBSBSR_LO (USB_PULLUP_GPIO); +#endif +} + +/** + * udc_all_interrupts - enable interrupts + * + * Switch on UDC interrupts. + * + */ +void udc_all_interrupts (struct usb_device_instance *device) +{ + dbg_init (1, " "); + // set interrupt mask +} + +/** + * udc_suspended_interrupts - enable suspended interrupts + * + * Switch on only UDC resume interrupt. + * + */ +void udc_suspended_interrupts (struct usb_device_instance *device) +{ + dbg_init (1, " "); + // set interrupt mask +} + +/** + * udc_disable_interrupts - disable interrupts. + * + * switch off interrupts + */ +void udc_disable_interrupts (struct usb_device_instance *device) +{ + dbg_init (1, " "); + // reset interrupt mask +} + +/** + * udc_ep0_packetsize - return ep0 packetsize + */ +int udc_ep0_packetsize (void) +{ + return EP0_PACKETSIZE; +} + +/** + * udc_startup_events - allow udc code to do any additional startup + */ +void udc_startup_events (struct usb_device_instance *device) +{ + usbd_device_event (device, DEVICE_INIT, 0); + usbd_device_event (device, DEVICE_CREATE, 0); + usbd_device_event (device, DEVICE_HUB_CONFIGURED, 0); + + // XXX moved to first SOF after HRST + //usbd_device_event(device, DEVICE_RESET, 0); // XXX should be done from device event + // XXX usbd_device_event(device, DEVICE_ADDRESS_ASSIGNED, 0); + //device->configuration = 0; + // XXX usbd_device_event(device, DEVICE_CONFIGURED, 0); + //device->interface = 1; + //device->alternate = 0; + // XXX usbd_device_event(device, DEVICE_SET_INTERFACE, 0); +} + +/** + * udc_name - return name of USB Device Controller + */ +char *udc_name (void) +{ + return UDC_NAME; +} + +/** + * udc_request_cable_irq - request Cable interrupt + * + * Return non-zero if not successful. + */ +int udc_request_cable_irq () +{ + return 0; +} + +#ifdef CONFIG_USBD_PROCFS +/* Proc Filesystem *************************************************************************** */ + +/* * + * l7205_proc_read - implement proc file system read. + * @file + * @buf + * @count + * @pos + * + * Standard proc file system read function. + */ +static ssize_t l7205_proc_read (struct file *file, char *buf, size_t count, loff_t * pos) +{ + unsigned long page; + int len = 0; + int index; + + MOD_INC_USE_COUNT; + // get a page, max 4095 bytes of data... + if (!(page = get_free_page (GFP_KERNEL))) { + MOD_DEC_USE_COUNT; + return -ENOMEM; + } + + len = 0; + index = (*pos)++; + + if (index == 0) { + len += sprintf ((char *) page + len, "l7205 UDC status\n"); + } + + if (index == 1) { + len += + sprintf ((char *) page + len, "%s\n", + udc_connected ()? "Connected" : "Not connected"); + } + + if (len > count) { + len = -EINVAL; + } else if (len > 0 && copy_to_user (buf, (char *) page, len)) { + len = -EFAULT; + } + free_page (page); + MOD_DEC_USE_COUNT; + return len; +} + +/* * + * l7205_proc_write - implement proc file system write. + * @file + * @buf + * @count + * @pos + * + * Proc file system write function, used to signal monitor actions complete. + * (Hotplug script (or whatever) writes to the file to signal the completion + * of the script.) An ugly hack. + */ +static ssize_t l7205_proc_write (struct file *file, const char *buf, size_t count, loff_t * pos) +{ + size_t n = count; + char command[64]; + char *cp = command; + int i = 0; + MOD_INC_USE_COUNT; + //printk(KERN_DEBUG "%s: count=%u\n",__FUNCTION__,count); + while ((n > 0) && (i < 64)) { + // Not too efficient, but it shouldn't matter + if (copy_from_user (cp++, buf + (count - n), 1)) { + count = -EFAULT; + break; + } + *cp = '\0'; + i++; + n -= 1; + //printk(KERN_DEBUG "%s: %u/%u %02x\n",__FUNCTION__,count-n,count,c); + } + if (!strncmp (command, "plug", 4)) { + udc_connect (); + } else if (!strncmp (command, "unplug", 6)) { + udc_disconnect (); + } + MOD_DEC_USE_COUNT; + return (count); +} + +static struct file_operations l7205_proc_operations_functions = { + read:l7205_proc_read, + write:l7205_proc_write, +}; + +#endif + +/** + * udc_request_udc_io - request UDC io region + * + * Return non-zero if not successful. + */ +int udc_request_io () +{ +#ifdef CONFIG_USBD_PROCFS + { + struct proc_dir_entry *p; + + // create proc filesystem entries + if ((p = create_proc_entry ("l7205", 0, 0)) == NULL) { + return -ENOMEM; + } + p->proc_fops = &l7205_proc_operations_functions; + } +#endif + return 0; +} + +/** + * udc_release_udc_irq - release UDC irq + */ +void udc_release_udc_irq () +{ + free_irq (IRQ_USBF, NULL); +} + +/** + * udc_release_cable_irq - release Cable irq + */ +void udc_release_cable_irq () +{ +} + +/** + * udc_release_release_io - release UDC io region + */ +void udc_release_io () +{ +#ifdef CONFIG_USBD_PROCFS + remove_proc_entry ("l7205", NULL); +#endif + +} diff -uNr linux.org/drivers/usb/device/bi/l7205.h linux/drivers/usb/device/bi/l7205.h --- linux.org/drivers/usb/device/bi/l7205.h Thu Jan 1 08:00:00 1970 +++ linux/drivers/usb/device/bi/l7205.h Thu May 15 15:54:48 2003 @@ -0,0 +1,38 @@ +/* + * linux/drivers/usbd/l7205_bi/udc.h -- L7205 USB controller driver. + * + * Copyright (c) 2000, 2001, 2002 Lineo + * + * By: + * Stuart Lynne , + * Tom Rushworth , + * Bruce Balden + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + * + */ + + +#define EP0_PACKETSIZE 0x8 + +#define UDC_MAX_ENDPOINTS 4 + + +#define UDC_IRQ 1 + +#undef UDC_ADDR +#undef UDC_ADDR_SIZE + +#define UDC_NAME "L7205/L7210 USBD" diff -uNr linux.org/drivers/usb/device/bi/pxa.c linux/drivers/usb/device/bi/pxa.c --- linux.org/drivers/usb/device/bi/pxa.c Thu Jan 1 08:00:00 1970 +++ linux/drivers/usb/device/bi/pxa.c Thu May 15 15:54:48 2003 @@ -0,0 +1,1868 @@ +/* + * linux/drivers/usb/device/bi/pxa.c -- Xscale USB Device Controller driver. + * + * Copyright (c) 2000, 2001, 2002 Lineo + * + * By: + * Stuart Lynne , + * Tom Rushworth , + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + * + */ + + +/* + * Testing notes + * + * This code was developed on the Intel Lubbock with Cotulla PXA250 processor. + * + * The default + * + */ + +/*****************************************************************************/ + +#include +#include + +#include "../usbd-export.h" +#include "../usbd-build.h" +#include "../usbd-module.h" + +MODULE_AUTHOR ("sl@lineo.com, tbr@lineo.com"); +MODULE_DESCRIPTION ("Xscale USB Device Bus Interface"); + +USBD_MODULE_INFO ("pxa_bi 0.1-alpha"); + +#include +#include +#include +#include +#include + +#include +#include + +#include +#include + +#include + +#include +#include + +#include + +#include +#include +#include +#include +#include + +#include "../usbd.h" +#include "../usbd-func.h" +#include "../usbd-bus.h" +#include "../usbd-inline.h" +#include "usbd-bi.h" + +#include "pxa.h" + + +#undef USE_DMA_OUT +#undef USE_DMA_IN + +unsigned int int_oscr; + +#undef PXA_TRACE +#ifdef PXA_TRACE +typedef enum pxa_trace_type { + pxa_regs, pxa_setup, pxa_xmit, pxa_ccr, pxa_iro +} pxa_trace_type_t; + + +typedef struct pxa_regs { + u32 cs0; + char * msg; +} pxa_regs_t; + +typedef struct pxa_xmit { + u32 size; +} pxa_xmit_t; + +typedef struct pxa_ccr { + u32 ccr; + char * msg; +} pxa_ccr_t; + +typedef struct pxa_iro { + u32 iro; + char * msg; +} pxa_iro_t; + +typedef struct pxa_trace { + pxa_trace_type_t trace_type; + u32 interrupts; + u32 ocsr; + u64 jiffies; + union { + pxa_regs_t regs; + pxa_xmit_t xmit; + pxa_ccr_t ccr; + pxa_iro_t iro; + struct usb_device_request setup; + } trace; +} pxa_trace_t; + + +#define TRACE_MAX 10000 + +int trace_next; +pxa_trace_t *pxa_traces; + +static __inline__ void PXA_REGS(u32 cs0, char *msg) +{ + if ((trace_next < TRACE_MAX) && pxa_traces) { + pxa_trace_t *p = pxa_traces + trace_next++; + p->ocsr = OSCR; + p->jiffies = jiffies; + p->interrupts = udc_interrupts; + p->trace_type = pxa_regs; + p->trace.regs.cs0 = cs0; + p->trace.regs.msg = msg; + } +} + +static __inline__ void PXA_SETUP(struct usb_device_request *setup) +{ + if ((trace_next < TRACE_MAX) && pxa_traces) { + pxa_trace_t *p = pxa_traces + trace_next++; + p->ocsr = OSCR; + p->jiffies = jiffies; + p->interrupts = udc_interrupts; + p->trace_type = pxa_setup; + memcpy(&p->trace.setup, setup, sizeof(struct usb_device_request)); + } +} + +static __inline__ void PXA_XMIT(u32 size) +{ + if ((trace_next < TRACE_MAX) && pxa_traces) { + pxa_trace_t *p = pxa_traces + trace_next++; + p->ocsr = OSCR; + p->jiffies = jiffies; + p->interrupts = udc_interrupts; + p->trace_type = pxa_xmit; + p->trace.xmit.size = size; + } +} + +static __inline__ void PXA_CCR(u32 ccr, char *msg) +{ + if ((trace_next < TRACE_MAX) && pxa_traces) { + pxa_trace_t *p = pxa_traces + trace_next++; + p->ocsr = OSCR; + p->jiffies = jiffies; + p->interrupts = udc_interrupts; + p->trace_type = pxa_ccr; + p->trace.ccr.ccr = ccr; + p->trace.ccr.msg = msg; + } +} + +static __inline__ void PXA_IRO(u32 iro, char *msg) +{ + if ((trace_next < TRACE_MAX) && pxa_traces) { + pxa_trace_t *p = pxa_traces + trace_next++; + p->ocsr = OSCR; + p->jiffies = jiffies; + p->interrupts = udc_interrupts; + p->trace_type = pxa_iro; + p->trace.iro.iro = iro; + p->trace.iro.msg = msg; + } +} +#else +static __inline__ void PXA_REGS(u32 cs0, char *msg) +{ +} + +static __inline__ void PXA_SETUP(struct usb_device_request *setup) +{ +} + +static __inline__ void PXA_XMIT(u32 size) +{ +} + +static __inline__ void PXA_CCR(u32 ccr, char *msg) +{ +} + +static __inline__ void PXA_IRO(u32 iro, char *msg) +{ +} +#endif + + +#if defined(USE_DMA_IN) || defined(USE_DMA_OUT) +unsigned int dma_interrupts; + +void pxa_kill(void) +{ + if ((udc_interrupts > 1000) || (dma_interrupts > 100)) { + + if ((udc_interrupts%100) == 0) { + + printk(KERN_INFO + "int[%d] %d disabling USIR[%02x %02x] " + "CCR[%02x] UICR[%02x %02x] UFNHR[%02x %02x]\n", + udc_interrupts, dma_interrupts, USIR1, USIR0, UDCCR, UICR1, UICR0, UFNHR, UFNLR); + + } + + UDCCR &= ~( UDCCR_UDE | UDCCR_REM ); + UICR1 = UICR0 = 0xff; + UFNHR |= UFNHR_SIM; + USIR1 = USIR1; + USIR0 = USIR0; + UDCCS0 = UDCCS0_FTF | UDCCS0_FST | UDCCS0_SA | UDCCS0_OPR; + CKEN &= ~CKEN11_USB; + return; + + } + +} +#endif /* defined(USE_DMA_IN) || defined(USE_DMA_OUT) */ + +static int udc_suspended; +static struct usb_device_instance *udc_device; // required for the interrupt handler + +/* + * ep_endpoints - map physical endpoints to logical endpoints + */ +static struct usb_endpoint_instance *ep_endpoints[UDC_MAX_ENDPOINTS]; + +static struct urb *ep0_urb; + +extern unsigned int udc_interrupts; + +#if 0 +int jifs(void) +{ + static unsigned long jiffies_last; + int elapsed = jiffies - jiffies_last; + + jiffies_last = jiffies; + return elapsed; +} +#else +int jifs(void) +{ + static unsigned long jiffies_last; + int elapsed = OSCR - jiffies_last; + + jiffies_last = OSCR; + return elapsed; +} +#endif + + + +/* ********************************************************************************************* */ +/* IO + */ + +/* + * Map logical to physical + */ + +typedef enum ep { + ep_control, ep_bulk_in, ep_bulk_out, ep_iso_in, ep_iso_out, ep_interrupt +} ep_t; + +/* + * PXA has lots of endpoints, but they have fixed address and type + * so logical to physical map is limited to masking top bits so we + * can find appropriate info. + */ + + +u32 _UDDRN[16] = { + 0x40600080, 0x40600100, 0x40600180, 0x40600200, + 0x40600400, 0x406000A0, 0x40600600, 0x40600680, + 0x40600700, 0x40600900, 0x406000C0, 0x40600B00, + 0x40600B80, 0x40600C00, 0x40600E00, 0x406000E0, +}; + +u32 _UBCRN[16] = { + 0, 0, 0x40600068, 0, + 0x4060006c, 0, 0, 0x40600070, + 0, 0x40600074, 0, 0, + 0x40600078, 0, 0x4060007c, 0, +}; + + +#define UDCCSN(x) __REG2(0x40600010, (x) << 2) + +#define UDDRN(x) __REG(_UDDRN[x]) +#define UBCRN(x) __REG(_UBCRN[x]) + + +#if defined(USE_DMA_IN) || defined(USE_DMA_OUT) + +struct ep_map { + int logical; + ep_t eptype; + int size; + int dma_chan; + volatile u32 *drcmr; + u_long dev_addr; +}; + +#ifdef USE_DMA_OUT +static usb_stream_t usb_stream_out = { + name: "PXA USB out", + dcmd: (DCMD_INCTRGADDR|DCMD_FLOWSRC|DCMD_BURST32|DCMD_WIDTH1), + dma_ch: -1, + //drcmr: // copy from ep_maps + //dev_addr: // get from UUDRN() +}; +#endif /* USE_DMA_OUT */ + +#ifdef USE_DMA_IN +static usb_stream_t usb_stream_in = { + name: "PXA USB in", + dcmd: ((DCMD_INCSRCADDR|DCMD_FLOWTRG|DCMD_BURST32|DCMD_WIDTH1)), + dma_ch: -1, + //drcmr: // copy from ep_maps + //dev_addr: // get from UUDRN() +}; +#endif /* USE_DMA_IN */ + +static struct ep_map ep_maps[16] = { + { logical: 0, eptype: ep_control, size: 16, }, + + { logical: 1, eptype: ep_bulk_in, size: 64, drcmr: &DRCMR25 }, + { logical: 2, eptype: ep_bulk_out, size: 64, drcmr: &DRCMR26 }, + + { logical: 3, eptype: ep_iso_in, size: 256, drcmr: &DRCMR27 }, + { logical: 4, eptype: ep_iso_out, size: 256, drcmr: &DRCMR28 }, + + { logical: 5, eptype: ep_interrupt , size: 8, }, + + { logical: 6, eptype: ep_bulk_in, size: 64, drcmr: &DRCMR30 }, + { logical: 7, eptype: ep_bulk_out, size: 64, drcmr: &DRCMR31 }, + + { logical: 8, eptype: ep_iso_in, size: 256, drcmr: &DRCMR33 }, + { logical: 9, eptype: ep_iso_out, size: 256, drcmr: &DRCMR34 }, + + { logical: 10, eptype: ep_interrupt, size: 8, }, + + { logical: 11, eptype: ep_bulk_in, size: 64, drcmr: &DRCMR35 }, + { logical: 12, eptype: ep_bulk_out, size: 64, drcmr: &DRCMR36 }, + + { logical: 13, eptype: ep_iso_in, size: 256, drcmr: &DRCMR37 }, + { logical: 14, eptype: ep_iso_out, size: 256, drcmr: &DRCMR38 }, + + { logical: 15, eptype: ep_interrupt, size: 8, }, +}; +#else /* defined(USE_DMA_IN) || defined(USE_DMA_OUT) */ + +struct ep_map { + int logical; + ep_t eptype; + int size; + int dma_chan; +}; + +static struct ep_map ep_maps[16] = { + { logical: 0, eptype: ep_control, size: 16, }, + { logical: 1, eptype: ep_bulk_in, size: 64, }, + + { logical: 2, eptype: ep_bulk_out, size: 64, }, + + { logical: 3, eptype: ep_iso_in, size: 256, }, + { logical: 4, eptype: ep_iso_out, size: 256, }, + + { logical: 5, eptype: ep_interrupt , size: 8, }, + + { logical: 6, eptype: ep_bulk_in, size: 64, }, + { logical: 7, eptype: ep_bulk_out, size: 64, }, + + { logical: 8, eptype: ep_iso_in, size: 256, }, + { logical: 9, eptype: ep_iso_out, size: 256, }, + + { logical: 10, eptype: ep_interrupt, size: 8, }, + + { logical: 11, eptype: ep_bulk_in, size: 64, }, + { logical: 12, eptype: ep_bulk_out, size: 64, }, + + { logical: 13, eptype: ep_iso_in, size: 256, }, + { logical: 14, eptype: ep_iso_out, size: 256, }, + + { logical: 15, eptype: ep_interrupt, size: 8, }, +}; + +#endif /* defined(USE_DMA_IN) || defined(USE_DMA_OUT) */ + + +static __inline__ void pxa_enable_ep_interrupt(int ep) +{ + ep &= 0xf; + if (ep < 8) { + UICR0 &= ~(1<rcv_urb) { + endpoint->rcv_urb = first_urb_detached (&endpoint->rdy); + } + if (endpoint->rcv_urb) { + + int len = 0; + unsigned char *cp = endpoint->rcv_urb->buffer + endpoint->rcv_urb->actual_length; + + if (cp) { + // read available bytes (max packetsize) into urb buffer + int count = MIN(UBCRN(ep) + 1, endpoint->rcv_packetSize); + while (count--) { + len++; + *cp++ = UDDRN(ep); + } + } + else { + printk(KERN_INFO"read[%d:%d] bad arguements\n", udc_interrupts, jifs()); + pxa_out_flush(ep); + } + // fall through if error of any type, len = 0 + usbd_rcv_complete_irq (endpoint, len, 0); + } + else { + pxa_out_flush(ep); + } + } + } + // clear RPC and interrupt + UDCCSN(ep) = UDCCS_BO_RPC; + pxa_ep_reset_irs(ep); +} +#else /* !defined(USE_DMA_OUT) */ + +static void pxa_usb_out_dma_irq(int ch, void *dev_id, struct pt_regs *regs) +{ + usb_stream_t *usb_stream = dev_id; + u_int dcsr; + + dma_interrupts++; + + pxa_kill(); + + printk(KERN_INFO"DMA[%d:%d]\n", udc_interrupts, dma_interrupts); + + // Stop DMA + dcsr = DCSR(ch); + DCSR(ch) = dcsr & ~DCSR_STOPIRQEN; + + if (!usb_stream->buffers) { + printk(KERN_INFO"pxa_usb_out_dma: no buffers\n"); + return; + } + + if (dcsr & DCSR_BUSERR) { + printk(KERN_INFO"pxa_usb_out_dma: bus error %d\n",ch); + return; + } +#if 0 + if (dcsr & DCSR_ENDINTR) { + + u_long cur_dma_desc; + + // derive DMA descriptor, DDADR points to next descriptor, not current + + cur_dma_desc = DDADR(ch) - usb_stream->dma_desc_phys - sizeof(pxa_dma_desc); + } +#endif +} + +void pxa_start_out_dma(int ep, struct usb_endpoint_instance *endpoint, usb_stream_t *usb_stream) +{ + + DCSR(usb_stream->dma_ch) = DCSR_RUN; + + *usb_stream->drcmr = usb_stream->dma_ch | DRCMR_MAPVLD; + + printk(KERN_INFO"start out dma [%d:%d] dma_ch: %d drcmr: %p %x DCSR: %x DDADR: %x DSADR: %x DTADR: %x DCMD: %x\n", + udc_interrupts, dma_interrupts, usb_stream->dma_ch, + usb_stream->drcmr, *usb_stream->drcmr, + DCSR(usb_stream->dma_ch), + DDADR(usb_stream->dma_ch), + DSADR(usb_stream->dma_ch), + DTADR(usb_stream->dma_ch), + DCMD(usb_stream->dma_ch)); +} + +#endif /* !defined(USE_DMA_OUT) */ + +/* ********************************************************************************************* */ +/* Bulk IN (tx) + */ + +static void __inline__ pxa_start_n (unsigned int ep, struct usb_endpoint_instance *endpoint) +{ + if (endpoint->tx_urb) { + int last; + struct urb *urb = endpoint->tx_urb; + + if (( last = MIN (urb->actual_length - (endpoint->sent + endpoint->last), endpoint->tx_packetSize))) + { + int size = last; + unsigned char *cp = urb->buffer + endpoint->sent + endpoint->last; + + while (size--) { + UDDRN(ep) = *cp++; + } + + if (( last < endpoint->tx_packetSize ) || + ( (endpoint->tx_urb->actual_length - endpoint->sent ) == last )) + { + UDCCSN(ep) = UDCCS_BI_TSP; + } + endpoint->last += last; + } + } +} + +static void __inline__ pxa_in_n (unsigned int ep, struct usb_endpoint_instance *endpoint) +{ + int udccsn; + + pxa_ep_reset_irs(ep); + + // if TPC update tx urb and clear TPC + if ((udccsn = UDCCSN(ep)) & UDCCS_BI_TPC) { + + UDCCSN(ep) = UDCCS_BI_TPC; + usbd_tx_complete_irq(endpoint, 0); + } + + if (udccsn & UDCCS_BI_TFS) { + pxa_start_n(ep, endpoint); + } + + // clear underrun, not much we can do about it + if (udccsn & UDCCS_BI_TUR) { + UDCCSN(ep) = UDCCS_BI_TUR; + } +} + + +#ifdef USE_DMA_IN +static void pxa_usb_in_dma_irq(int ch, void *dev_id, struct pt_regs *regs) +{ + u_int dcsr; + dcsr = DCSR(ch); + DCSR(ch) = dcsr & ~DCSR_STOPIRQEN; +} +#endif /* USE_DMA_IN */ + + +/* ********************************************************************************************* */ +/* Control (endpoint zero) + */ + + +void pxa_ep0xmit(struct usb_endpoint_instance *endpoint, volatile u32 udccs0) +{ + int short_packet; + int size; + struct urb *urb = endpoint->tx_urb; + + PXA_REGS(udccs0, " --> xmit"); + + //printk(KERN_INFO"tx[%d:%d] CS0[%02x]\n", udc_interrupts, jifs(), UDCCS0); + + // check for premature status stage - host abandoned previous IN + if ((udccs0 & UDCCS0_OPR) && !(UDCCS0 & UDCCS0_SA) ) { + + // clear tx fifo and opr + UDCCS0 = UDCCS0_FTF | UDCCS0_OPR; + endpoint->state = WAIT_FOR_SETUP; + endpoint->tx_urb = NULL; + PXA_REGS(UDCCS0, " <-- xmit premature status"); + return; + } + + // check for stall + if (udccs0 & UDCCS0_SST) { + // clear stall and tx fifo + UDCCS0 = UDCCS0_SST | UDCCS0_FTF; + endpoint->state = WAIT_FOR_SETUP; + endpoint->tx_urb = NULL; + PXA_REGS(UDCCS0, " <-- xmit stall"); + return; + } + + /* How much are we sending this time? (May be zero!) + (Note that later call of tx_complete() will add last to sent.) */ + if (NULL == urb) { + size = 0; + } else { + endpoint->last = size = MIN (urb->actual_length - endpoint->sent, endpoint->tx_packetSize); + } + + /* Will this be a short packet? + (It may be the last, but still be full size, in which case we will need a ZLP later.) */ + short_packet = (size < endpoint->tx_packetSize); + + PXA_XMIT(size); + + if (size > 0 && urb->buffer) { + // Stuff the FIFO + unsigned char *cp = urb->buffer + endpoint->sent; + + while (size--) { + UDDRN(0) = *cp++; + } + } + + // Is this the end of the data state? (We've sent all the data, plus any required ZLP.) + if (!endpoint->tx_urb || (endpoint->last < endpoint->tx_packetSize)) { + // Tell the UDC we are at the end of the packet. + UDCCS0 = UDCCS0_IPR; + endpoint->state = WAIT_FOR_OUT_STATUS; + PXA_REGS(UDCCS0, " <-- xmit wait for status"); + } + + else if ((endpoint->last == endpoint->tx_packetSize) && + ((endpoint->last + endpoint->sent) == ep0_urb->actual_length) && + ((ep0_urb->actual_length) < le16_to_cpu(ep0_urb->device_request.wLength)) + ) + { + // Tell the UDC we are at the end of the packet. + endpoint->state = DATA_STATE_NEED_ZLP; + PXA_REGS(UDCCS0, " <-- xmit need zlp"); + } + else { + PXA_REGS(UDCCS0, " <-- xmit not finished"); + } +} + +void __inline__ pxa_ep0setup(struct usb_endpoint_instance *endpoint, volatile u32 udccs0) +{ + if ((udccs0 & (UDCCS0_SA | UDCCS0_OPR | UDCCS0_RNE)) == (UDCCS0_SA | UDCCS0_OPR | UDCCS0_RNE)) { + + int len = 0; + int max = 8; + unsigned char *cp = (unsigned char *)&ep0_urb->device_request; + + PXA_REGS(udccs0, " --> setup"); + + //memset(cp, 0, max); + + while (max-- /*&& (UDCCS0 & UDCCS0_RNE)*/) { + len++; + *cp++ = UDDR0; + } + + PXA_SETUP(&ep0_urb->device_request); + + // process setup packet + if (usbd_recv_setup(ep0_urb)) { + // setup processing failed + UDCCS0 = UDCCS0_FST; + endpoint->state = WAIT_FOR_SETUP; + PXA_REGS(udccs0, " --> bad setup FST"); + return; + } + + // check direction + if ((ep0_urb->device_request.bmRequestType & USB_REQ_DIRECTION_MASK) == USB_REQ_HOST2DEVICE) + { + // Control Write - we are receiving more data from the host + + // should we setup to receive data + if (le16_to_cpu (ep0_urb->device_request.wLength)) { + //printk(KERN_INFO"sl11_ep0: read data %d\n", + // le16_to_cpu(ep0_urb->device_request.wLength)); + endpoint->rcv_urb = ep0_urb; + endpoint->rcv_urb->actual_length = 0; + // XXX this has not been tested + // pxa_out_0 (0, endpoint); + PXA_REGS(UDCCS0," <-- setup no response"); + return; + } + + // allow for premature IN (c.f. 12.5.3 #8) + UDCCS0 = UDCCS0_IPR; + PXA_REGS(UDCCS0," <-- setup nodata"); + } + else { + // Control Read - we are sending data to the host + + // verify that we have non-zero request length + if (!le16_to_cpu (ep0_urb->device_request.wLength)) { + udc_stall_ep (0); + PXA_REGS(UDCCS0," <-- setup stalling zero wLength"); + return; + } + // verify that we have non-zero length response + if (!ep0_urb->actual_length) { + udc_stall_ep (0); + PXA_REGS(UDCCS0," <-- setup stalling zero response"); + return; + } + + // start sending + endpoint->tx_urb = ep0_urb; + endpoint->sent = 0; + endpoint->last = 0; + endpoint->state = DATA_STATE_XMIT; + pxa_ep0xmit(endpoint, UDCCS0); + + // Clear SA and OPR bits + UDCCS0 = UDCCS0_SA | UDCCS0_OPR; + PXA_REGS(UDCCS0," <-- setup data"); + } + } +} + + +static __inline__ void pxa_ep0(struct usb_endpoint_instance *endpoint, volatile u32 udccs0) +{ + int j = 0; + + PXA_REGS(udccs0," --> ep0"); + + if (udccs0 & UDCCS0_SST) { + pxa_ep_reset_irs(0); + UDCCS0 = UDCCS0_SST; + PXA_REGS(udccs0," --> ep0 clear SST"); + if (endpoint) { + endpoint->state = WAIT_FOR_SETUP; + endpoint->tx_urb = NULL; + } + return; + } + + + if (!endpoint) { + printk(KERN_INFO"ep0[%d:%d] endpoint Zero is NULL CS0[%02x]\n", udc_interrupts, jifs(), UDCCS0); + pxa_ep_reset_irs(0); + UDCCS0 = UDCCS0_IPR | UDCCS0_OPR | UDCCS0_SA; + PXA_REGS(UDCCS0," ep0 NULL"); + return; + } + + if (endpoint->tx_urb) { + usbd_tx_complete_irq (endpoint, 0); + if (!endpoint->tx_urb) { + if (endpoint->state != DATA_STATE_NEED_ZLP) { + endpoint->state = WAIT_FOR_OUT_STATUS; + } + } + } + + if ((endpoint->state != WAIT_FOR_SETUP) && (udccs0 & UDCCS0_SA)) { + PXA_REGS(udccs0," --> ep0 early SA"); + endpoint->state = WAIT_FOR_SETUP; + endpoint->tx_urb = NULL; + } + + switch (endpoint->state) { + case DATA_STATE_NEED_ZLP: + UDCCS0 = UDCCS0_IPR; + endpoint->state = WAIT_FOR_OUT_STATUS; + break; + + case WAIT_FOR_OUT_STATUS: + if ((udccs0 & (UDCCS0_OPR | UDCCS0_SA)) == UDCCS0_OPR) { + UDCCS0 |= UDCCS0_OPR; + } + PXA_REGS(UDCCS0," --> ep0 WAIT for STATUS"); + endpoint->state = WAIT_FOR_SETUP; + + case WAIT_FOR_SETUP: + do { + pxa_ep0setup(endpoint, UDCCS0); + + if (udccs0 & UDCCS0_SST) { + pxa_ep_reset_irs(0); + UDCCS0 = UDCCS0_SST | UDCCS0_OPR | UDCCS0_SA; + PXA_REGS(udccs0," --> ep0 clear SST"); + if (endpoint) { + endpoint->state = WAIT_FOR_SETUP; + endpoint->tx_urb = NULL; + } + return; + } + + if (j++ > 2) { + u32 udccs0 = UDCCS0; + PXA_REGS(udccs0," ep0 wait"); + if ((udccs0 & (UDCCS0_OPR | UDCCS0_SA | UDCCS0_RNE)) == (UDCCS0_OPR | UDCCS0_SA)) { + UDCCS0 = UDCCS0_OPR | UDCCS0_SA; + PXA_REGS(UDCCS0," ep0 force"); + } + else { + UDCCS0 = UDCCS0_OPR | UDCCS0_SA; + PXA_REGS(UDCCS0," ep0 force and return"); + break; + } + } + + } while (UDCCS0 & (UDCCS0_OPR | UDCCS0_RNE)); + break; + + case DATA_STATE_XMIT: + pxa_ep0xmit(endpoint, UDCCS0); + break; + + } + + pxa_ep_reset_irs(0); + PXA_REGS(UDCCS0," <-- ep0"); +} + +/* ********************************************************************************************* */ +/* Interrupt Handler + */ + +/** + * int_hndlr - interrupt handler + * + */ +static void int_hndlr (int irq, void *dev_id, struct pt_regs *regs) +{ + int usiro; + int udccr; + int ep; + + int_oscr = OSCR; + udc_interrupts++; + + +#if defined(USE_DMA_IN) || defined(USE_DMA_OUT) + pxa_kill(); +#endif /* defined(USE_DMA_IN) || defined(USE_DMA_OUT) */ + + // check for common, high priority interrupts first, i.e. per endpoint service requests + // XXX if ISO supported it might be necessary to give the ISO endpoints priority + + while ((usiro = USIR0)) { + u32 udccs0 = UDCCS0; + PXA_IRO(usiro, "------------------------> Interrupt"); + for (ep = 0; usiro; usiro >>= 1, ep++) { + if (usiro & 1) { + switch (ep_maps[ep].eptype) { + case ep_control: + pxa_ep0(ep_endpoints[0], udccs0); + //PXA_IRO(USIRO, "<-- Interrupt"); + break; + + case ep_bulk_in: + case ep_interrupt: + pxa_in_n(ep, ep_endpoints[ep]); + break; + + case ep_bulk_out: +#ifndef USE_DMA_OUT + pxa_out_n(ep, ep_endpoints[ep]); + break; +#endif /* USE_DMA_OUT */ + case ep_iso_in: + case ep_iso_out: + pxa_ep_reset_irs(ep); + break; + } + } + } + } + + // sof interrupt + if (UFNHR & UFNHR_SIR) { + UFNHR = UFNHR_SIR; + } + + // uncommon interrupts + if ((udccr = UDCCR) & (UDCCR_RSTIR | UDCCR_RESIR | UDCCR_SUSIR)) { + + // UDC Reset + if (udccr & UDCCR_RSTIR) { + PXA_CCR(udccr, "------------------------> Reset"); + //printk(KERN_INFO"int_hndlr[%d:%d] Reset\n", udc_interrupts, jifs()); + + udc_suspended = 0; + usbd_device_event (udc_device, DEVICE_RESET, 0); + usbd_device_event (udc_device, DEVICE_ADDRESS_ASSIGNED, 0); + UDCCR |= UDCCR_RSTIR; + } + + // UDC Resume + if (udccr & UDCCR_RESIR) { + PXA_CCR(udccr, "------------------------> Resume"); + if (udc_suspended) { + udc_suspended = 0; + usbd_device_event (udc_device, DEVICE_BUS_ACTIVITY, 0); + } + UDCCR |= UDCCR_RESIR; + } + + // UDC Suspend + if (udccr & UDCCR_SUSIR) { + PXA_CCR(udccr, "------------------------> Suspend"); + if (!udc_suspended) { + udc_suspended = 1; + usbd_device_event (udc_device, DEVICE_BUS_INACTIVE, 0); + } + UDCCR |= UDCCR_SUSIR; + } + } + PXA_CCR(UDCCR, "<-- Interrupt"); +} + + +/* ********************************************************************************************* */ + + +/* ********************************************************************************************* */ +/* + * Start of public functions. + */ + +/** + * udc_start_in_irq - start transmit + * @eendpoint: endpoint instance + * + * Called by bus interface driver to see if we need to start a data transmission. + */ +void udc_start_in_irq (struct usb_endpoint_instance *endpoint) +{ + if (UDCCSN(endpoint->endpoint_address & 0xf) & UDCCS_BI_TFS) { + pxa_start_n (endpoint->endpoint_address & 0xf, endpoint); + } +} + +/** + * udc_init - initialize + * + * Return non-zero if we cannot see device. + **/ +int udc_init (void) +{ + udc_disable_interrupts (NULL); + return 0; +} + + +/** + * udc_start_in - start transmit + * @eendpoint: endpoint instance + * + * Called by bus interface driver to see if we need to start a data transmission. + */ +void udc_start_in (struct usb_endpoint_instance *endpoint) +{ + if (endpoint) { + unsigned long flags; + local_irq_save (flags); + udc_start_in_irq(endpoint); + local_irq_restore (flags); + } +} + + +/** + * udc_stall_ep - stall endpoint + * @ep: physical endpoint + * + * Stall the endpoint. + */ +void udc_stall_ep (unsigned int ep) +{ + if (ep < UDC_MAX_ENDPOINTS) { + // stall + } +} + + +/** + * udc_reset_ep - reset endpoint + * @ep: physical endpoint + * reset the endpoint. + * + * returns : 0 if ok, -1 otherwise + */ +void udc_reset_ep (unsigned int ep) +{ + if (ep < UDC_MAX_ENDPOINTS) { + } +} + + +/** + * udc_endpoint_halted - is endpoint halted + * @ep: + * + * Return non-zero if endpoint is halted + */ +int udc_endpoint_halted (unsigned int ep) +{ + return 0; +} + + +/** + * udc_set_address - set the USB address for this device + * @address: + * + * Called from control endpoint function after it decodes a set address setup packet. + */ +void udc_set_address (unsigned char address) +{ +} + +/** + * udc_serial_init - set a serial number if available + */ +int __init udc_serial_init (struct usb_bus_instance *bus) +{ + return -EINVAL; +} + +/* ********************************************************************************************* */ + +/** + * udc_max_endpoints - max physical endpoints + * + * Return number of physical endpoints. + */ +int udc_max_endpoints (void) +{ + return UDC_MAX_ENDPOINTS; +} + + +/** + * udc_check_ep - check logical endpoint + * @lep: + * + * Return physical endpoint number to use for this logical endpoint or zero if not valid. + */ +int udc_check_ep (int logical_endpoint, int packetsize) +{ + // XXX check ep table + + return ( ((logical_endpoint & 0xf) >= UDC_MAX_ENDPOINTS) || (packetsize > 64)) + ? 0 : (logical_endpoint & 0xf); +} + + +/** + * udc_set_ep - setup endpoint + * @ep: + * @endpoint: + * + * Associate a physical endpoint with endpoint_instance + */ +void udc_setup_ep (struct usb_device_instance *device, unsigned int ep, + struct usb_endpoint_instance *endpoint) +{ + if (ep < UDC_MAX_ENDPOINTS) { + + ep_endpoints[ep] = endpoint; + + // ep0 + if (ep == 0) { + } + // IN + else if (endpoint->endpoint_address & 0x80) { +#ifdef USE_DMA_IN + usb_stream_in.ep = ep; + usb_stream_in.endpoint = endpoint; + usb_stream_in.drcmr = ep_maps[ep].drcmr; + usb_stream_in.dev_addr = UDDRN(ep); + for (i = 0; i < usb_stream_in.num; i++) { + usb_stream_in.dma_desc[i].dsadr = usb_stream_in.dma_buf_phys + (i * usb_stream_in.size); + usb_stream_in.dma_desc[i].dtadr = ep_maps[ep].dev_addr; + usb_stream_in.dma_desc[i].dcmd = usb_stream_in.size; + } +#endif /* USE_DMA_IN */ + } + // OUT + else if (endpoint->endpoint_address) { + + + usbd_fill_rcv (device, endpoint, 5); + endpoint->rcv_urb = first_urb_detached (&endpoint->rdy); +#ifdef USE_DMA_OUT + //printk(KERN_INFO"udc_setup_ep: OUT alloc urbs ep: %d addr: %d num: %d size: %d dma_ch: %d\n", + // ep, endpoint->endpoint_address, usb_stream_out.num, + // usb_stream_out.size, usb_stream_out.dma_ch); + usb_stream_out.ep = ep; + usb_stream_out.endpoint = endpoint; + usb_stream_out.drcmr = ep_maps[ep].drcmr; + usb_stream_out.dev_addr = _UDDRN[ep]; + for (i = 0; i < usb_stream_out.num; i++) { + + usb_stream_out.dma_desc[i].ddadr = usb_stream_out.dma_desc_phys + + ((i < (usb_stream_out.num - 1)) ? (i + 1) * sizeof(pxa_dma_desc) : 0); + + usb_stream_out.dma_desc[i].dsadr = usb_stream_out.dev_addr; + usb_stream_out.dma_desc[i].dtadr = usb_stream_out.dma_buf_phys + (i * usb_stream_out.size); + usb_stream_out.dma_desc[i].dcmd = usb_stream_out.size | usb_stream_out.dcmd; + + //printk(KERN_INFO"udc_setup_ep: dma_desc[%d] ddadr: %x dsadr: %x dtadr: %x dcmd: %x\n", + // i, + // usb_stream_out.dma_desc[i].ddadr, + // usb_stream_out.dma_desc[i].dsadr, + // usb_stream_out.dma_desc[i].dtadr, + // usb_stream_out.dma_desc[i].dcmd + // ); + } +#if 1 + DDADR(usb_stream_out.dma_ch) = usb_stream_out.buffers[0].dma_desc->ddadr; + //printk(KERN_INFO"udc_setup_ep: DDAR: %p %p\n", + // DDADR(usb_stream_out.dma_ch), usb_stream_out.buffers[0].dma_desc->ddadr); + pxa_start_out_dma(ep, endpoint, &usb_stream_out); +#endif +#endif /* USE_DMA_OUT */ + } + pxa_enable_ep_interrupt(ep_endpoints[ep]->endpoint_address); + } +} + +/** + * udc_disable_ep - disable endpoint + * @ep: + * + * Disable specified endpoint + */ +void udc_disable_ep (unsigned int ep) +{ + if (ep < UDC_MAX_ENDPOINTS) { + struct usb_endpoint_instance *endpoint; + + if ((endpoint = ep_endpoints[ep])) { + ep_endpoints[ep] = NULL; + usbd_flush_ep (endpoint); + } + if (ep == 0) { + //printk(KERN_INFO"udc_setup_ep: 0 do nothing\n"); + } + // IN + else if (endpoint->endpoint_address & 0x80) { +#ifdef USE_DMA_IN + usb_stream_in.ep = 0; + usb_stream_in.endpoint = NULL; +#endif /* USE_DMA_IN */ + } + // OUT + else if (endpoint->endpoint_address) { +#if 0 +#ifdef USE_DMA_OUT + usb_stream_out.ep = 0; + usb_stream_out.endpoint = NULL; +#endif +#endif + } + } +} + +/* ********************************************************************************************* */ + +/** + * udc_connected - is the USB cable connected + * + * Return non-zeron if cable is connected. + */ +int udc_connected () +{ + return 1; +} + +/** + * udc_connect - enable pullup resistor + * + * Turn on the USB connection by enabling the pullup resistor. + */ +void udc_connect (void) +{ +#ifdef CONFIG_SABINAL_DISCOVERY + /* Set GPIO pin function to I/O */ + GPAFR1_U &= ~(0x3 << ((USBD_CONNECT_GPIO & 0xF) << 1)); + /* Set pin direction to output */ + GPDR(1) |= GPIO_GPIO(USBD_CONNECT_GPIO); +#if defined(USBD_CONNECT_HIGH) + /* Set GPIO pin high to connect */ + GPSR(1) = GPIO_GPIO(USBD_CONNECT_GPIO); +#else + /* Set GPIO pin low to connect */ + GPCR(1) = GPIO_GPIO(USBD_CONNECT_GPIO); +#endif +#else +#if defined(CONFIG_ARCH_PXA) +#else +#warning NO USB Device connect +#endif +#endif +} + +/** + * udc_disconnect - disable pullup resistor + * + * Turn off the USB connection by disabling the pullup resistor. + */ +void udc_disconnect (void) +{ +#ifdef CONFIG_SABINAL_DISCOVERY + /* Set GPIO pin function to I/O */ + GPAFR1_U &= ~(0x3 << ((USBD_CONNECT_GPIO & 0xF) << 1)); + /* Set pin direction to output */ + GPDR(1) |= GPIO_GPIO(USBD_CONNECT_GPIO); +#if defined(USBD_CONNECT_HIGH) + /* Set GPIO pin low to disconnect */ + GPCR(1) = GPIO_GPIO(USBD_CONNECT_GPIO); +#else + /* Set GPIO pin high to disconnect */ + GPSR(1) = GPIO_GPIO(USBD_CONNECT_GPIO); +#endif +#else +#if defined(CONFIG_ARCH_PXA) +#else +#warning NO USB Device connect +#endif +#endif +} + +#if 0 +/** + * udc_int_hndlr_cable - interrupt handler for cable + */ +static void udc_int_hndlr_cable (int irq, void *dev_id, struct pt_regs *regs) +{ + // GPIOn interrupt +} +#endif + +/* ********************************************************************************************* */ + +/** + * udc_enable_interrupts - enable interrupts + * + * Switch on UDC interrupts. + * + */ +void udc_all_interrupts (struct usb_device_instance *device) +{ + int i; + + UDCCR &= ~UDCCR_SRM; // enable suspend interrupt + UDCCR |= UDCCR_REM; // disable resume interrupt + + // XXX SOF UFNHR &= ~UFNHR_SIM; + + // always enable control endpoint + pxa_enable_ep_interrupt(0); + + for (i = 1; i < UDC_MAX_ENDPOINTS; i++) { + if (ep_endpoints[i] && ep_endpoints[i]->endpoint_address) { + pxa_enable_ep_interrupt(ep_endpoints[i]->endpoint_address); + } + } +} + + +/** + * udc_suspended_interrupts - enable suspended interrupts + * + * Switch on only UDC resume interrupt. + * + */ +void udc_suspended_interrupts (struct usb_device_instance *device) +{ + UDCCR &= ~UDCCR_REM; // enable resume interrupt + UDCCR |= UDCCR_SRM; // disable suspend interrupt +} + + +/** + * udc_disable_interrupts - disable interrupts. + * + * switch off interrupts + */ +void udc_disable_interrupts (struct usb_device_instance *device) +{ + UICR0 = UICR1 = 0xff; // disable endpoint interrupts + UFNHR |= UFNHR_SIM; // disable sof interrupt + UDCCR |= UDCCR_REM | UDCCR_SRM; // disable suspend and resume interrupt +} + +/* ********************************************************************************************* */ + +/** + * udc_ep0_packetsize - return ep0 packetsize + */ +int udc_ep0_packetsize (void) +{ + return EP0_PACKETSIZE; +} + +/** + * udc_enable - enable the UDC + * + * Switch on the UDC + */ +void udc_enable (struct usb_device_instance *device) +{ + // save the device structure pointer + udc_device = device; + + // ep0 urb + if (!ep0_urb) { + if (!(ep0_urb = usbd_alloc_urb (device, device->function_instance_array, 0, 512))) { + printk (KERN_ERR "udc_enable: usbd_alloc_urb failed\n"); + } + } + else { + printk (KERN_ERR "udc_enable: ep0_urb already allocated\n"); + } + + + // enable UDC + + // c.f. 3.6.2 Clock Enable Register + CKEN |= CKEN11_USB; + + // c.f. 12.4.11 GPIOn and GPIOx + // enable cable interrupt + + + // c.f. 12.5 UDC Operation - after reset on EP0 interrupt is enabled. + + // c.f. 12.6.1.1 UDC Enable + UDCCR |= UDCCR_UDE | UDCCR_REM; +} + + +/** + * udc_disable - disable the UDC + * + * Switch off the UDC + */ +void udc_disable (void) +{ + // disable UDC + // c.f. 12.6.1.1 UDC Enable + UDCCR &= ~( UDCCR_UDE | UDCCR_REM ); + + // c.f. 3.6.2 Clock Enable Register + CKEN &= ~CKEN11_USB; + + // disable cable interrupt + + // reset device pointer + udc_device = NULL; + + // ep0 urb + + if (ep0_urb) { + usbd_dealloc_urb (ep0_urb); + ep0_urb = 0; + } else { + printk (KERN_ERR "udc_disable: ep0_urb already NULL\n"); + } +} + + +/** + * udc_startup - allow udc code to do any additional startup + */ +void udc_startup_events (struct usb_device_instance *device) +{ + usbd_device_event (device, DEVICE_INIT, 0); + usbd_device_event (device, DEVICE_CREATE, 0); + usbd_device_event (device, DEVICE_HUB_CONFIGURED, 0); + usbd_device_event (device, DEVICE_RESET, 0); +} + + +/* ********************************************************************************************* */ +#ifdef PXA_TRACE +#ifdef CONFIG_USBD_PROCFS +/* Proc Filesystem *************************************************************************** */ + +/* * + * pxa_proc_read - implement proc file system read. + * @file + * @buf + * @count + * @pos + * + * Standard proc file system read function. + */ +static ssize_t pxa_proc_read (struct file *file, char *buf, size_t count, loff_t * pos) +{ + unsigned long page; + int len = 0; + int index; + + MOD_INC_USE_COUNT; + // get a page, max 4095 bytes of data... + if (!(page = get_free_page (GFP_KERNEL))) { + MOD_DEC_USE_COUNT; + return -ENOMEM; + } + + len = 0; + index = (*pos)++; + + if (index == 0) { + len += sprintf ((char *) page + len, " Index Ints Jifs Ticks\n"); + } + + + if (index < trace_next) { + + u64 jifs = 1111; + u32 ticks = 2222; + pxa_trace_t *p = pxa_traces + index; + unsigned char *cp; + + if (index > 0) { + u32 ocsr = pxa_traces[index-1].ocsr; + ticks = (p->ocsr > ocsr) ? (p->ocsr - ocsr) : (ocsr - p->ocsr) ; + jifs = p->jiffies - pxa_traces[index-1].jiffies; + } + + len += sprintf ((char *) page + len, "%8d %8d %8lu ", index, p->interrupts, jifs); + + if (ticks > 1024*1024) { + len += sprintf ((char *) page + len, "%8dM ", ticks>>20); + } + else { + len += sprintf ((char *) page + len, "%8d ", ticks); + } + switch (p->trace_type) { + case pxa_regs: + len += sprintf ((char *) page + len, "CS0[%02x] %s\n", p->trace.regs.cs0, p->trace.regs.msg); + break; + case pxa_setup: + cp = (unsigned char *)&p->trace.setup; + len += sprintf ((char *) page + len, + " -- request [%02x %02x %02x %02x %02x %02x %02x %02x]\n", + cp[0], cp[1], cp[2], cp[3], cp[4], cp[5], cp[6], cp[7]); + break; + case pxa_xmit: + len += sprintf ((char *) page + len, + " -- sending [%02x]\n", p->trace.xmit.size); + break; + case pxa_ccr: + len += sprintf ((char *) page + len, + "CCR[%02x] %s\n", p->trace.ccr.ccr, p->trace.ccr.msg); + break; + case pxa_iro: + len += sprintf ((char *) page + len, + "IRO[%02x] %s\n", p->trace.iro.iro, p->trace.iro.msg); + break; + } + } + + if (len > count) { + len = -EINVAL; + } else if (len > 0 && copy_to_user (buf, (char *) page, len)) { + len = -EFAULT; + } + free_page (page); + MOD_DEC_USE_COUNT; + return len; +} +/* * + * pxa_proc_write - implement proc file system write. + * @file + * @buf + * @count + * @pos + * + * Proc file system write function, used to signal monitor actions complete. + * (Hotplug script (or whatever) writes to the file to signal the completion + * of the script.) An ugly hack. + */ +static ssize_t pxa_proc_write (struct file *file, const char *buf, size_t count, loff_t * pos) +{ + return count; +} + +static struct file_operations pxa_proc_operations_functions = { + read:pxa_proc_read, + write:pxa_proc_write, +}; + +#endif +#endif + + +/* ********************************************************************************************* */ +/** + * udc_name - return name of USB Device Controller + */ +char *udc_name (void) +{ + return UDC_NAME; +} + +/** + * udc_request_udc_irq - request UDC interrupt + * + * Return non-zero if not successful. + */ +int udc_request_udc_irq () +{ + // request IRQ and IO region + if (request_irq (IRQ_USB, int_hndlr, SA_INTERRUPT | SA_SAMPLE_RANDOM, + UDC_NAME " USBD Bus Interface", NULL) != 0) + { + printk (KERN_INFO "usb_ctl: Couldn't request USB irq\n"); + return -EINVAL; + } + return 0; +} + +/** + * udc_request_cable_irq - request Cable interrupt + * + * Return non-zero if not successful. + */ +int udc_request_cable_irq () +{ +#ifdef XXXX_CABLE_IRQ + // request IRQ and IO region + if (request_irq + (XXXX_CABLE_IRQ, int_hndlr, SA_INTERRUPT | SA_SAMPLE_RANDOM, UDC_NAME " Cable", + NULL) != 0) { + printk (KERN_INFO "usb_ctl: Couldn't request USB irq\n"); + return -EINVAL; + } +#endif + return 0; +} + +#if defined(USE_DMA_IN) || defined(USE_DMA_OUT) + + +void pxa_free_dma_stream(usb_stream_t *usb_stream) +{ + printk(KERN_INFO"pxa_free_dma_stream:\n"); + if (usb_stream->dma_ch >= 0) { + printk(KERN_INFO"pxa_free_dma_stream: pxa_free_dma\n"); + pxa_free_dma(usb_stream->dma_ch); + usb_stream->dma_ch = -1; + } +#if 1 + printk(KERN_INFO"pxa_free_dma_stream: dma_buf: %p\n", usb_stream->dma_buf); + if (usb_stream->dma_buf) { + printk(KERN_INFO"pxa_free_dma_stream: consistent_free dma_buf\n"); + consistent_free(usb_stream->dma_buf, usb_stream->num * usb_stream->size, usb_stream->dma_buf_phys); + usb_stream->dma_buf = NULL; + } + + printk(KERN_INFO"pxa_free_dma_stream: dma_desc: %p\n", usb_stream->dma_desc); + if (usb_stream->dma_desc) { + printk(KERN_INFO"pxa_free_dma_stream: consistent_free dma_desc\n"); + consistent_free(usb_stream->dma_desc, usb_stream->num * sizeof(pxa_dma_desc), usb_stream->dma_desc_phys); + usb_stream->dma_desc = NULL; + } + + printk(KERN_INFO"pxa_free_dma_stream: buffers: %p\n", usb_stream->buffers); + if (usb_stream->buffers) { + printk(KERN_INFO"pxa_free_dma_stream: kfree buffers\n"); + kfree(usb_stream->buffers); + usb_stream->buffers = NULL; + } +#endif + printk(KERN_INFO"pxa_free_dma_stream: done\n"); +} + + +int pxa_setup_dma(/*int ep, struct usb_endpoint_instance *endpoint,*/ usb_stream_t *usb_stream) +{ + usb_buf_t *buffers; + + pxa_dma_desc *dma_desc = NULL; + dma_addr_t dma_desc_phys = 0; + + unsigned char *dma_buf = NULL; + dma_addr_t dma_buf_phys = 0; + + int i; + int num; + int size; + + usb_stream->num = num = 4; + usb_stream->size = size = 64; + + // DMA buffer structure array + if (!(buffers = kmalloc(sizeof(usb_buf_t) * usb_stream->num, GFP_KERNEL))) { + goto err; + } + memset(buffers, 0, sizeof(usb_buf_t) * usb_stream->num); + usb_stream->buffers = buffers; + + + // DMA Buffer + if (!(dma_buf = consistent_alloc(GFP_KERNEL, num * size, &dma_buf_phys))) { + goto err; + } + usb_stream->dma_buf = dma_buf; + usb_stream->dma_buf_phys = dma_buf_phys; + + + // DMA descriptor array + if (!(dma_desc = consistent_alloc(GFP_KERNEL, num * sizeof(pxa_dma_desc), &dma_desc_phys))) { + goto err; + } + usb_stream->dma_desc = dma_desc; + usb_stream->dma_desc_phys = dma_desc_phys; + + printk(KERN_INFO"dma stream dma_buf: %p dma_buf_phys: %p dma_desc: %p dma_desc_phys: %p\n", + usb_stream->dma_buf, + usb_stream->dma_buf_phys, + usb_stream->dma_desc, + usb_stream->dma_desc_phys + ); + // setup arrays + // + for (i = 0; i < num; i++) { + + // usb_stream->dma_desc[i] + + //dma_desc[i].ddadr = dma_desc_phys + (i < (num - 1)) ? (i + 1) * sizeof(pxa_dma_desc) : 0; + + // usb_stream->buffers[i] + buffers[i].dma_buf = dma_buf + (i * size); + buffers[i].dma_buf_phys = dma_buf_phys + (i * size); + buffers[i].dma_desc = dma_desc + (i * sizeof(pxa_dma_desc)); + buffers[i].dma_desc_phys = dma_desc_phys + (i * sizeof(pxa_dma_desc)); + + printk(KERN_INFO"dma buf[%d] dma_buf: %p dma_buf_phys: %p dma_desc: %p dma_desc_phys: %p\n", + i, + buffers[i].dma_buf, + buffers[i].dma_buf_phys, + buffers[i].dma_desc, + buffers[i].dma_desc_phys + ); + } + + // prime DMA + + //DDADR(usb_stream->dma_ch) = buffers[0].dma_desc->ddadr; + + return 0; +err: + return -ENOMEM; +} +/** + * udc_request_udc_io - request UDC io region + * + * Return non-zero if not successful. + */ +int udc_request_io () +{ +#ifdef USE_DMA_IN + // XXX verify prio + if ((usb_stream_in.dma_ch = pxa_request_dma(usb_stream_in.name, + DMA_PRIO_HIGH, pxa_usb_in_dma_irq, &usb_stream_in)) < 0) + { + printk(KERN_INFO"pxa: failed to allocate IN dma channel\n"); + return -EINVAL; + } + if (!pxa_setup_dma(&usb_stream_in)) { + pxa_free_dma_stream(&usb_stream_in); + } + printk(KERN_INFO"pxa: DMA channels in: %d\n", usb_stream_in.dma_ch); +#endif /* USE_DMA_IN */ + +#ifdef USE_DMA_OUT + if ((usb_stream_out.dma_ch = pxa_request_dma(usb_stream_out.name, + DMA_PRIO_HIGH, pxa_usb_out_dma_irq, &usb_stream_out)) < 0) + { + printk(KERN_INFO"pxa: failed to allocate OUT dma channel\n"); +#ifdef USE_DMA_IN + pxa_free_dma_stream(&usb_stream_in); +#endif /* USE_DMA_IN */ + return -EINVAL; + } + if (!pxa_setup_dma(&usb_stream_out)) { +#ifdef USE_DMA_IN + pxa_free_dma_stream(&usb_stream_in); +#endif /* USE_DMA_IN */ + pxa_free_dma_stream(&usb_stream_out); + return -EINVAL; + } + + printk(KERN_INFO"pxa: DMA channels out: %d\n", usb_stream_out.dma_ch); +#endif /* USE_DMA_OUT */ + + return 0; +} + +/** + * udc_release_release_io - release UDC io region + */ +void udc_release_io () +{ + +#ifdef USE_DMA_IN + pxa_free_dma_stream(&usb_stream_in); +#endif /* USE_DMA_IN */ +#ifdef USE_DMA_OUT + pxa_free_dma_stream(&usb_stream_out); +#endif /* USE_DMA_OUT */ +} + +#else /* defined(USE_DMA_IN) || defined(USE_DMA_OUT) */ + +/** + * udc_request_udc_io - request UDC io region + * + * Return non-zero if not successful. + */ +int udc_request_io () +{ +#ifdef PXA_TRACE +#ifdef CONFIG_USBD_PROCFS + if (!(pxa_traces = vmalloc(sizeof(pxa_trace_t) * TRACE_MAX))) { + printk(KERN_ERR"PXA_TRACE malloc failed %p %d\n", pxa_traces, sizeof(pxa_trace_t) * TRACE_MAX); + } + else { + printk(KERN_ERR"PXA_TRACE malloc ok %p\n", pxa_traces); + } + PXA_REGS(0,"init"); + PXA_REGS(0,"test"); + { + struct proc_dir_entry *p; + + // create proc filesystem entries + if ((p = create_proc_entry ("pxa", 0, 0)) == NULL) { + printk(KERN_INFO"PXA PROC FS failed\n"); + } + else { + printk(KERN_INFO"PXA PROC FS Created\n"); + p->proc_fops = &pxa_proc_operations_functions; + } + } +#endif +#endif + return 0; +} + +/** + * udc_release_release_io - release UDC io region + */ +void udc_release_io () +{ +#ifdef PXA_TRACE +#ifdef CONFIG_USBD_PROCFS + unsigned long flags; + save_flags_cli (flags); + remove_proc_entry ("pxa", NULL); + if (pxa_traces) { + pxa_trace_t *p = pxa_traces; + pxa_traces = 0; + vfree(p); + } + restore_flags (flags); +#endif +#endif + +} + +#endif /* defined(USE_DMA_IN) || defined(USE_DMA_OUT) */ + + +/** + * udc_release_udc_irq - release UDC irq + */ +void udc_release_udc_irq () +{ + free_irq (IRQ_USB, NULL); +} + +/** + * udc_release_cable_irq - release Cable irq + */ +void udc_release_cable_irq () +{ +#ifdef XXXX_IRQ + free_irq (XXXX_CABLE_IRQ, NULL); +#endif +} + + +/** + * udc_regs - dump registers + * + * Dump registers with printk + */ +void udc_regs (void) +{ + printk ("[%d:%d] CCR[%02x] UICR[%02x %02x] UFNH[%02x %02x] UDCCS[%02x %02x %02x %02x]\n", + udc_interrupts, jifs(), UDCCR, UICR1, UICR0, UFNHR, UFNLR, UDCCS0, UDCCS1, UDCCS2, UDCCS5); +} + + diff -uNr linux.org/drivers/usb/device/bi/pxa.h linux/drivers/usb/device/bi/pxa.h --- linux.org/drivers/usb/device/bi/pxa.h Thu Jan 1 08:00:00 1970 +++ linux/drivers/usb/device/bi/pxa.h Thu May 15 15:54:48 2003 @@ -0,0 +1,147 @@ +/* + * linux/drivers/usb/device/bi/pxa.h + * + * Copyright (c) 2000, 2001, 2002 Lineo + * + * By: + * Stuart Lynne , + * Tom Rushworth , + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + * + */ + + +#define EP0_PACKETSIZE 0x10 + +#define UDC_MAX_ENDPOINTS 6 + +#define UDC_NAME "PXA" + +#ifdef CONFIG_SABINAL_DISCOVERY +/* SHARP A3 specific GPIO assignments */ +#define USBD_CONNECT_GPIO 50 +#if defined(CONFIG_SABINAL_DISCOVERY_DVT) || defined(CONFIG_SABINAL_DISCOVERY_DVT2) +#define USBD_CONNECT_HIGH 1 /* Pin high for connect (undef for pin low) */ +#else +#undef USBD_CONNECT_HIGH +#endif + +#define CKEN11_USB USB_CE /* USB Unit Clock Enable */ +#endif + +/* + * DMA Notes + * + * DCSRX DMA Channel Control/Status register + * DRCMRX DMA Request to Channel Map register + * DDADRX DMA Descriptor Address register + * + * DSADRX DMA Source Address register (RO) + * DTADRX DMA Target Address register (RO) + * DCMCDX DMA Command register (RO) + * + * + */ + +typedef enum { + DMA_READY, + DMA_ACTIVE, + DMA_DONE +} dma_status_t; + +typedef struct { + int offset; + dma_status_t dma_status; + + unsigned char *dma_buf; + dma_addr_t dma_buf_phys; + +#ifndef CONFIG_SABINAL_DISCOVERY + pxa_dma_desc *dma_desc; + dma_addr_t dma_desc_phys; +#endif + +} usb_buf_t; + +typedef struct { + char *name; + u_long dcmd; + volatile u32 *drcmr; + u_long dev_addr; + + u_int dma_ch; + usb_buf_t *buffers; + + unsigned char *dma_buf; + dma_addr_t dma_buf_phys; + +#ifndef CONFIG_SABINAL_DISCOVERY + pxa_dma_desc *dma_desc; + dma_addr_t dma_desc_phys; +#endif + int num; + int size; + + int ep; + struct usb_endpoint_instance *endpoint; +} usb_stream_t; + + +/* + * Reserved Register Definitions + */ +#define UDCRSVD0 __REG(0x40600004) /* UDC Reserved Register 0 */ +#define UDCRSVD1 __REG(0x40600008) /* UDC Reserved Register 1 */ +#define UDCRSVD2 __REG(0x4060000c) /* UDC Reserved Register 2 */ + +#define UFNHR __REG(0x40600060) /* UDC Frame Number High Register */ +#define UFNLR __REG(0x40600064) /* UDC Frame Number Low Register */ + + + +/* + * 7654 3210 + * de 1101 1110 + * + * fedc ba98 + * 7b 0111 1011 + * + * + */ + +#define UDCRSVD0_DBL0 (1 << 0) /* enable endpoint 0 double buffer (always zero)) */ +#define UDCRSVD0_DBL1 (1 << 1) /* enable endpoint 1 double buffer (read/write) */ +#define UDCRSVD0_DBL2 (1 << 2) /* enable endpoint 2 double buffer (read/write) */ +#define UDCRSVD0_DBL3 (1 << 3) /* enable endpoint 3 double buffer (read/write) */ +#define UDCRSVD0_DBL4 (1 << 4) /* enable endpoint 4 double buffer (read/write) */ +#define UDCRSVD0_DBL5 (1 << 5) /* enable endpoint 5 double buffer (always zero)) */ +#define UDCRSVD0_DBL6 (1 << 6) /* enable endpoint 6 double buffer (read/write) */ +#define UDCRSVD0_DBL7 (1 << 7) /* enable endpoint 7 double buffer (read/write) */ + +#define UDCRSVD0_DBL8 (1 << 0) /* enable endpoint 8 double buffer (read/write) */ +#define UDCRSVD0_DBL9 (1 << 1) /* enable endpoint 9 double buffer (read/write) */ +#define UDCRSVD0_DBL10 (1 << 2) /* enable endpoint 10 double buffer (always zero)) */ +#define UDCRSVD0_DBL11 (1 << 3) /* enable endpoint 11 double buffer (read/write) */ +#define UDCRSVD0_DBL12 (1 << 4) /* enable endpoint 12 double buffer (read/write) */ +#define UDCRSVD0_DBL13 (1 << 5) /* enable endpoint 13 double buffer (read/write) */ +#define UDCRSVD0_DBL14 (1 << 6) /* enable endpoint 14 double buffer (read/write) */ +#define UDCRSVD0_DBL15 (1 << 7) /* enable endpoint 15 double buffer (always zero)) */ + +/* ep0 states */ +#define WAIT_FOR_SETUP 0 +#define DATA_STATE_XMIT 1 +#define DATA_STATE_NEED_ZLP 2 +#define WAIT_FOR_OUT_STATUS 3 diff -uNr linux.org/drivers/usb/device/bi/sa1100.c linux/drivers/usb/device/bi/sa1100.c --- linux.org/drivers/usb/device/bi/sa1100.c Thu Jan 1 08:00:00 1970 +++ linux/drivers/usb/device/bi/sa1100.c Thu May 15 15:54:48 2003 @@ -0,0 +1,2378 @@ +/* + * linux/drivers/usbd/sa1100_bi/udc.c + * + * Copyright (c) 2000, 2001, 2002 Lineo + * Copyright (c) 2001 Hewlett Packard + * + * By: + * Stuart Lynne , + * Tom Rushworth , + * Bruce Balden + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + * + */ + +/* + * Please note that this driver works reasonably well with StrongARM parts + * running at 206Mhz. + * + * It is not possible to run the USB with DMA on the StrongARM running at 133Mhz. + * Running without DMA is possible and reasonably reliable. This can be done by + * restricting the USB packetsize to 16bytes for your bulk endpoints. + */ + + + +#include +#include + +#include "../usbd-export.h" +#include "../usbd-build.h" +#include "../usbd-module.h" + +MODULE_AUTHOR ("sl@lineo.com, tbr@lineo.com"); +MODULE_DESCRIPTION ("USB Device SA-1100 Bus Interface"); + +USBD_MODULE_INFO ("sa1100_bi 0.2"); + +#include +#include +#include +#include +#include +#include + +#include +#include + +#include +#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,4,6) +#define USE_ADD_DEL_TIMER_FOR_USBADDR_CHECK 1 +#include +#else +#undef USE_ADD_DEL_TIMER_FOR_USBADDR_CHECK +#include +#endif + +#include +#include +#include +#include + + +#include +#include +#include +#include +#include + +#include + +#include +#include +#include +#include + + +#include "../usbd-debug.h" + +#include "../usbd.h" +#include "../usbd-func.h" +#include "../usbd-bus.h" +#include "../usbd-inline.h" +#include "usbd-bi.h" + +#include "sa1100.h" + + +#if defined(CONFIG_SA1100_COLLIE) // XXX change to 5500 +#include +#include +#include +#endif + + + +#define MIN(a,b) ((a) < (b) ? (a) : (b)) +#define MAX(a,b) ((a) > (b) ? (a) : (b)) + +#define ACTIVEA 1 +#define ACTIVEB 2 + +static struct usb_device_instance *udc_device; // required for the interrupt handler + +/* + * ep_endpoints - map physical endpoints to logical endpoints + */ +static struct usb_endpoint_instance *ep_endpoints[UDC_MAX_ENDPOINTS]; + +//static struct urb ep0_urb; +unsigned char usb_address; + +extern unsigned int udc_interrupts; +extern unsigned int udc_interrupts_last; + +unsigned int ep0_interrupts; +unsigned int tx_interrupts; +unsigned int rx_interrupts; +unsigned int sus_interrupts; +unsigned int res_interrupts; +unsigned int udc_address_errors; +unsigned int udc_ticks; +unsigned int udc_fixed; + +unsigned int ep0_interrupts_last; +unsigned int rx_interrupts_last; +unsigned int tx_interrupts_last; + +unsigned int udc_rpe_errors; +unsigned int udc_ep1_errors; +unsigned int udc_ep2_errors; +unsigned int udc_ep2_tpe; +unsigned int udc_ep2_tur; +unsigned int udc_ep2_sst; +unsigned int udc_ep2_fst; + + + + +int usbd_rcv_dma; +int usbd_tx_dma; + +static int udc_saw_sof; + +#ifdef USE_ADD_DEL_TIMER_FOR_USBADDR_CHECK +static struct timer_list sa1100_usb_dev_addr_check; +static int usb_addr_check_initialized = 0; +#define CHECK_INTERVAL 1 +#else +static struct tq_struct sa1100_tq; +#endif + + +/* + * DMA control register structure + */ +typedef struct { + volatile u_long DDAR; + volatile u_long SetDCSR; + volatile u_long ClrDCSR; + volatile u_long RdDCSR; + volatile dma_addr_t DBSA; + volatile u_long DBTA; + volatile dma_addr_t DBSB; + volatile u_long DBTB; +} dma_regs_t; + +/* ********************************************************************************************* */ +/* IO + */ + +volatile int udc (volatile unsigned int *regaddr) +{ + volatile unsigned int value; + int ok; + for (ok = 1000, value = *(regaddr); value != *(regaddr) && ok--; value = *(regaddr)); + if (!ok) { + dbg_udc (0, "NOT OK: %p %x", regaddr, value); + } + return value; +} + +static __inline__ volatile int _udc (volatile unsigned int *regaddr) +{ + volatile unsigned int value; + int ok; + for (ok = 1000, value = *(regaddr); value != *(regaddr) && ok--; value = *(regaddr)); + if (!ok) { + printk (KERN_ERR "NOT OK: %p %x\n", regaddr, value); + } + return value; +} + + +static __inline__ void _sa1100_bi_dma_run (dmach_t channel, int active) +{ + dma_regs_t *regs = (dma_regs_t *) io_p2v (_DDAR (channel)); + if (active == ACTIVEA) { + regs->SetDCSR = DCSR_STRTA | DCSR_RUN; + } else { + regs->SetDCSR = DCSR_STRTB | DCSR_RUN; + } +} + +static __inline__ int _sa1100_bi_dma_queue_buffer_irq (dmach_t channel, dma_addr_t data, int size) +{ + int status; + dma_regs_t *regs = (dma_regs_t *) io_p2v (_DDAR (channel)); + + status = regs->RdDCSR; + if (((status & DCSR_BIU) && (status & DCSR_STRTB)) || (!(status & DCSR_BIU) && !(status & DCSR_STRTA))) { + regs->ClrDCSR = DCSR_DONEA | DCSR_STRTA; + regs->DBSA = data; + regs->DBTA = size; + // Once is good, twice is better.... + regs->DBSA = data; + regs->DBTA = size; + return ACTIVEA; + } else { + regs->ClrDCSR = DCSR_DONEB | DCSR_STRTB; + regs->DBSB = data; + regs->DBTB = size; + // Once is good, twice is better.... + regs->DBSB = data; + regs->DBTB = size; + return ACTIVEB; + } +} + +static __inline__ int _sa1100_bi_dma_flush_all_irq (dmach_t channel) +{ + dma_regs_t *regs = (dma_regs_t *) io_p2v (_DDAR (channel)); + regs->ClrDCSR = DCSR_STRTA | DCSR_STRTB | DCSR_RUN | DCSR_IE; + return 0; +} + +static __inline__ int _sa1100_bi_dma_stop_get_current_irq (dmach_t channel, dma_addr_t * addr, int active) +{ + dma_regs_t *regs = (dma_regs_t *) io_p2v (_DDAR (channel)); + + // addr sometimes can be set incorrectly, the caller must do bounds checking + switch (active) { + case ACTIVEA: + regs->ClrDCSR = DCSR_RUN | DCSR_STRTA | DCSR_RUN | DCSR_IE; + *addr = regs->DBSA; // not reliable + return 0; + case ACTIVEB: + regs->ClrDCSR = DCSR_RUN | DCSR_STRTB | DCSR_RUN | DCSR_IE; + *addr = regs->DBSB; // not reliable + return 0; + default: + *addr = 0; + return -ENXIO; + } +} + + + +/* ********************************************************************************************* */ + + +static u32 getCPUID (char **stepping) +{ + u32 cpuID; + + __asm__ __volatile__ (" mrc p15, 0, %0, c0, c0":"=r" (cpuID) + :); + + if (NULL != stepping) { + switch (cpuID & 0xf) { + case 0: + *stepping = "A0"; + break; + case 4: + *stepping = "B0"; + break; + case 5: + *stepping = "B1"; + break; + case 6: + *stepping = "B2"; + break; + case 8: + *stepping = "B4"; + break; + case 9: + *stepping = "B5"; + break; + default: + *stepping = "??"; + dbg_udc (0, "stepping unknown, ID#%x", (cpuID & 0xf)); + } + } + + return (cpuID); +} + + + +void udc_fix_errata_29 (char *msg) +{ + int ok; + u32 cpuID; // from init.c + + cpuID = getCPUID (NULL); + + // Set errata 29 fix bit, if possible + if (cpuID == 0 || (cpuID & 0xF) >= 9) { + // Unknown CPU, or B5 stepping and above + // set errata 29 fix enable (for B5 and above, B4 will ignore) + int udc_cr; + for (ok = 10; ok > 0; ok--) { + *(UDCCR) |= /*UDCCR_ERR29 */ 0x80; + // Do some dummy reads.... + udc_cr = *(UDCCR); + udc_cr = *(UDCCR); + udc_cr = *(UDCCR); + if (udc (UDCCR) & /*UDCCR_ERR29 */ 0x80) { + dbg_udc (0, + "%s: set errata 29 fix bit worked, UDCCR#%02x ok=%d cpuID#%08x", + msg, udc (UDCCR), ok, cpuID); + ok = -2; + break; + } + } + if (ok != -2) { + dbg_udc (0, "%s: set errata 29 fix bit failed, UDCCR#%02x ok=%d cpuID#%08x", + msg, udc (UDCCR), ok, cpuID); + } + } else { + dbg_udc (0, "%s: errata 29 fix bit not available, cpuID#%08x", msg, cpuID); + } +} + +/* ********************************************************************************************* */ + +/** + * sa1100_tick - clock timer task + * @data: + * + * Run from global clock tick to check if we are suspended. + */ +#ifdef USE_ADD_DEL_TIMER_FOR_USBADDR_CHECK +static void sa1100_tick (unsigned long data) +#else +static void sa1100_tick (void *data) +#endif +{ + udc_ticks++; + + // is driver active + if (data) { + + if (_udc (UDCAR) != usb_address) { + dbg_udc (0, "sa1100_tick: ADDRESS ERROR DETECTED %02x %02x", *(UDCAR), usb_address); + udc_address_errors++; + udc_fixed++; + } + *(UDCAR) = usb_address; + + // re-queue task +#ifdef USE_ADD_DEL_TIMER_FOR_USBADDR_CHECK + sa1100_usb_dev_addr_check.expires = jiffies + CHECK_INTERVAL; + add_timer (&sa1100_usb_dev_addr_check); +#else + queue_task (&sa1100_tq, &tq_timer); +#endif + + } +} + + +/* ********************************************************************************************* */ +/* Interrupt Handler + */ + +/** + * int_hndlr_cable - interrupt handler for cable + */ +static void int_hndlr_cable (int irq, void *dev_id, struct pt_regs *regs) +{ +#ifdef CONFIG_SA1100_USBCABLE_GPIO + dbg_udc (1, "udc_cradle_interrupt:"); + udc_cable_event (); +#endif +} + + +/** + * int_hndlr_device - interrupt handler + * + */ +static void int_hndlr_device (int irq, void *dev_id, struct pt_regs *regs) +{ + unsigned int status; + + status = *(UDCSR); + *(UDCSR) = status; + + udc_interrupts++; + + //dbg_udc(0, ""); + dbg_intr(2, "[%d]: CSR: %02x CCR: %02x CAR: %02x:%02x", udc_interrupts, status, *(UDCCR), *(UDCAR), usb_address); + + // Handle common interrupts first, IN (tx) and OUT (recv) + + if (status & UDCSR_RIR) { + ep1_int_hndlr (status); + } + + if (status & UDCSR_TIR) { + ep2_int_hndlr (status, 1); + } + // handle less common interrupts + if (status & (UDCSR_EIR | UDCSR_RSTIR | UDCSR_SUSIR | UDCSR_RESIR)) { + if (status & UDCSR_EIR) { + ep0_int_hndlr (status); + } + + if (status & UDCSR_RSTIR) { + dbg_intr (1, "[%d] DEVICE_RESET: CSR: %02x CS0: %02x CAR: %02x", + udc_interrupts, *(UDCSR), *(UDCCS0), *(UDCAR)); + usbd_device_event (udc_device, DEVICE_RESET, 0); + } + + if (status & UDCSR_SUSIR) { + dbg_intr (1, "[%d] SUSPEND address: %02x irq: %02x status: %02x", + udc_interrupts, *(UDCAR), irq, status); + sus_interrupts++; +#if defined(CONFIG_SA1100_COLLIE) // XXX change to 5500 + usbd_device_event (udc_device, DEVICE_BUS_INACTIVE, 0); +#else + usbd_device_event (udc_device, DEVICE_BUS_INACTIVE, 0); + //usbd_device_event (udc_device, DEVICE_RESET, 0); +#endif + udc_suspended_interrupts (udc_device); + udc_ticker_poke (); + } + + if (status & UDCSR_RESIR) { + dbg_intr (1, "[%d] RESUME address: %02x irq: %02x status: %02x", udc_interrupts, *(UDCAR), irq, status); + *(UDCAR) = usb_address; + res_interrupts++; +#if defined(CONFIG_SA1100_COLLIE) // XXX change to 5500 + usbd_device_event (udc_device, DEVICE_BUS_ACTIVITY, 0); +#endif + udc_all_interrupts (udc_device); + udc_ticker_poke (); + } + } + // Check that the UDC has not forgotton it's address, force it back to correct value + //if (_udc (UDCAR) != usb_address) { + // udc_address_errors++; + //} + *(UDCAR) = usb_address; + +} + + +/* ********************************************************************************************* */ +/* + * Start of public functions. + */ + +/** + * udc_init - initialize + * + * Return non-zero if we cannot see device. + **/ +int udc_init (void) +{ + // reset + + return 0; +} + +/** + * udc_start_in_irq - start transmit + * @endpoint: + * + * Called with interrupts disabled. + */ +void udc_start_in_irq (struct usb_endpoint_instance *endpoint) +{ + ep2_int_hndlr (0, 0); +} + + +void udc_stall_ep0 (void) +{ + int ok = 0; + // QQQ eh? + dbg_udc (0, "stalling ep0 (UDCCS0_FST,UDCCS0_FST,UDCCS0_SST)"); + + // write 1 to set FST + SET_AND_TEST ((*(UDCCS0) &= UDCCS0_FST), !(udc (UDCCS0) & UDCCS0_FST), ok); + if (!ok) { + dbg_udc (0, "cannot stall !(UDCCS0&UDCCS0_FST) UDCCS0: %02x", *(UDCCS0)); + } + // write 0 to reset FST + SET_AND_TEST ((*(UDCCS0) &= ~UDCCS0_FST), (udc (UDCCS0) & UDCCS0_FST), ok); + if (!ok) { + dbg_udc (0, "cannot stall (UDCCS0&UDCCS0_FST) UDCCS0: %02x", *(UDCCS0)); + } + // write 1 to reset SST + SET_AND_TEST ((*(UDCCS0) = UDCCS0_SST), (udc (UDCCS0) & UDCCS0_SST), ok); + if (!ok) { + dbg_udc (0, "cannot stall (UDCCS0&UDCCS0_SST) UDCCS0: %02x", *(UDCCS0)); + } +} + +static void stall_ep_n (int ep, volatile unsigned int *regaddr, int fst) +{ + int ok; + dbg_udc (0, "stalling ep %d (FST)", ep); + + // write 1 to set FST + SET_AND_TEST ((*(regaddr) = fst), !(udc (regaddr) & fst), ok); + if (!ok) { + dbg_udc (0, "cannot stall !(reg&fst) UDCCS%d: %02x", ep, *(regaddr)); + } +} + +static void reset_ep_n (int ep, volatile unsigned int *regaddr, int fst, int sst) +{ + int ok; + dbg_udc (1, "reset ep %d (FST)", ep); + + // write 0 to reset FST + SET_AND_TEST ((*(regaddr) &= ~fst), (udc (regaddr) & fst), ok); + if (!ok) { + dbg_udc (0, "cannot stall !(reg&fst) UDCCS%d: %02x", ep, *(regaddr)); + } + // write 1 to reset SST + SET_AND_TEST ((*(regaddr) = sst), (udc (regaddr) & sst), ok); + if (!ok) { + dbg_udc (0, "cannot stall (reg&sst) UDCCS%d: %02x", ep, *(UDCCS0)); + } +} + +/** + * udc_stall_ep - stall endpoint + * @ep: physical endpoint + * + * Stall the endpoint. + */ +void udc_stall_ep (unsigned int ep) +{ + dbg_udc (0, "STALLING %d (FST)", ep); + switch (ep) { + case 1: + stall_ep_n (1, UDCCS1, UDCCS1_FST); + break; + case 2: + stall_ep_n (2, UDCCS2, UDCCS2_FST); + break; + } +} + + +/** + * udc_reset_ep - reset endpoint + * @ep: physical endpoint + * reset the endpoint. + * + * returns : 0 if ok, -1 otherwise + */ +void udc_reset_ep (unsigned int ep) +{ + dbg_udc (1, "RESETING %d (FST)", ep); + switch (ep) { + case 0: + reset_ep_n (1, UDCCS0, UDCCS0_FST, UDCCS0_SST); + break; + case 1: + reset_ep_n (1, UDCCS1, UDCCS1_FST, UDCCS1_SST); + break; + case 2: + reset_ep_n (2, UDCCS2, UDCCS2_FST, UDCCS2_SST); + break; + } +} + + +/** + * udc_endpoint_halted - is endpoint halted + * @ep: + * + * Return non-zero if endpoint is halted + */ +int udc_endpoint_halted (unsigned int ep) +{ + switch (ep) { + case 0: + return (udc (UDCCS0) & UDCCS0_FST) != 0; + case 1: + return (udc (UDCCS1) & UDCCS1_FST) != 0; + case 2: + return (udc (UDCCS2) & UDCCS2_FST) != 0; + } + return 0; +} + + +/** + * udc_set_address - set the USB address for this device + * @address: + * + * Called from control endpoint function after it decodes a set address setup packet. + */ +void udc_set_address (unsigned char address) +{ + usb_address = address; +#if 0 + int ok; + // address can be setup, udc will wait until ack received + dbg_udc(1, "%02d", address); + IOIOIO(UDCAR, usb_address, *(UDCAR) = address , (udc(UDCAR) != address), ok); + + if (*(UDCAR) != address) { + dbg_udc(0, "AA %02d %02d ok: %d", udc(UDCAR), address, ok); + udelay(20); + IOIOIO(UDCAR, usb_address, *(UDCAR) = address , (udc(UDCAR) != address), ok); + if (*(UDCAR) != address) { + dbg_udc(0, "BB %02d %02d ok: %d", udc(UDCAR), address, ok); + udelay(40); + IOIOIO(UDCAR, usb_address, *(UDCAR) = address , (udc(UDCAR) != address), ok); + } + } + dbg_udc(0, "CC %02d %02d ok: %d", udc(UDCAR), address, ok); +#endif +} + +#ifdef CONFIG_SA1100_BITSY +static int crc32 (char *s, int length) +{ + /* indices */ + int pByte; + int pBit; + + const unsigned long poly = 0xedb88320; + unsigned long crc_value = 0xffffffff; + + for (pByte = 0; pByte < length; pByte++) { + unsigned char c = *(s++); + for (pBit = 0; pBit < 8; pBit++) { + crc_value = (crc_value >> 1) ^ (((crc_value ^ c) & 0x01) ? poly : 0); + c >>= 1; + } + } + return crc_value; +} +#endif + +/** + * udc_serial_init - set a serial number if available + */ +int __init udc_serial_init (struct usb_bus_instance *bus) +{ +#if defined(SHOW_CPU_STEPPING) + char *stepping; + u32 id = getCPUID (&stepping); + + dbg_init (0, "CPU ID #%08x stepping %s",id,stepping); +#else + dbg_init (2, "serial number"); +#endif + +#ifdef CONFIG_SA1100_BITSY + if (machine_is_bitsy ()) { + char serial_number[22]; + int i; + int j; + memset (&serial_number, 0, sizeof (serial_number)); + for (i = 0, j = 0; i < 20; i++, j++) { + char buf[4]; + h3600_eeprom_read (5 + i, buf, 2); + serial_number[j] = buf[1]; + } + serial_number[j] = '\0'; + bus->serial_number = crc32 (serial_number, 22); + if (bus->serial_number_str = kmalloc (strlen (serial_number) + 1, GFP_KERNEL)) { + strcpy (bus->serial_number_str, serial_number); + } + //dbg_udc(0, "serial: %s %08x", bus->serial_number_str, bus->serial_number); + return 0; + } +#endif + +#ifdef CONFIG_SA1100_CALYPSO + if (machine_is_calypso ()) { + __u32 eerom_serial; + int i; + if ((i = + CalypsoIicGet (IIC_ADDRESS_SERIAL0, (unsigned char *) &eerom_serial, + sizeof (eerom_serial)))) { + bus->serial_number = eerom_serial; + } + if (bus->serial_number_str = kmalloc (9, GFP_KERNEL)) { + sprintf (bus->serial_number_str, "%08X", eerom_serial & 0xffffffff); + } + //dbg_udc(0, "serial: %s %08x", bus->serial_number_str, bus->serial_number); + return 0; + } +#endif + return -EINVAL; + +} + + +/* ********************************************************************************************* */ + +/** + * udc_max_endpoints - max physical endpoints + * + * Return number of physical endpoints. + */ +int udc_max_endpoints (void) +{ + return UDC_MAX_ENDPOINTS; +} + + +/** + * udc_check_ep - check logical endpoint + * @logical_endpoint: + * + * Return physical endpoint number to use for this logical endpoint or zero if not valid. + */ +int udc_check_ep (int logical_endpoint, int packetsize) +{ + if (packetsize > 64) { + return 0; + } + switch (logical_endpoint) { + case 1: + return 1; + case 0x82: + return 2; + default: + return 0; + } +} + + +/** + * udc_set_ep - setup endpoint + * @ep: + * @endpoint: + * + * Associate a physical endpoint with endpoint_instance + */ +void udc_setup_ep (struct usb_device_instance *device, unsigned int ep, + struct usb_endpoint_instance *endpoint) +{ + dbg_udc (1, "[%d]:", ep); + + if (ep < UDC_MAX_ENDPOINTS) { + + ep_endpoints[ep] = endpoint; + dbg_udc (1, "[%d] ep_endpoint %p endpoint: %p", ep, ep_endpoints[ep], endpoint); + + + if (endpoint) { + switch (ep) { + case 0: // Control + ep0_enable (device, endpoint); + break; + + case 1: // OUT + dbg_udc (1, "[%d]: CHECKING rcv_urb: %p", ep, endpoint->rcv_urb); + // XXX XXX + if (!endpoint->rcv_urb) { + usbd_fill_rcv (device, endpoint, 5); + endpoint->rcv_urb = first_urb_detached (&endpoint->rdy); + dbg_udc (1, "[%d]: SETTING rcv_urb: %p", ep, endpoint->rcv_urb); + } + ep1_enable (device, endpoint, usbd_rcv_dma); + break; + + case 2: // IN + ep2_enable (device, endpoint, usbd_tx_dma); + break; + } + } + } +} + + +/** + * udc_disable_ep - disable endpoint + * @ep: + * + * Disable specified endpoint + */ +void udc_disable_ep (unsigned int ep) +{ + dbg_udc (1, "[%d]:", ep); + if (ep < UDC_MAX_ENDPOINTS) { + struct usb_endpoint_instance *endpoint; + + if ((endpoint = ep_endpoints[ep])) { + ep_endpoints[ep] = NULL; + usbd_flush_ep (endpoint); + } + switch (ep) { + case 0: + ep0_disable (); + break; + case 1: + ep1_disable (); + break; + case 2: + ep2_disable (); + break; + } + } +} + +/* ********************************************************************************************* */ + +/** + * udc_incradle - is the USB cable connected + * + * Return non-zeron if cable is connected. + */ +int udc_connected () +{ + int rc = 1; +#ifdef CONFIG_SA1100_USBCABLE_GPIO + +#ifdef CONFIG_SA1100_USBCABLE_ACTIVE_HIGH + rc = (GPLR & GPIO_GPIO (23)) != 0; + dbg_udc (1, "udc_connected: ACTIVE_HIGH: %d", rc); +#else + rc = (GPLR & GPIO_GPIO (23)) == 0; + dbg_udc (1, "udc_connected: ACTIVE_LOW: %d", rc); +#endif +#else +#warning UDC_CONNECTED not implemented +#endif + return rc; +} + + +/** + * udc_connect - enable pullup resistor + * + * Turn on the USB connection by enabling the pullup resistor. + */ +void udc_connect (void) +{ + +#if defined(CONFIG_SA1100_COLLIE) // XXX change to 5500 + + #warning COLLIE CONNECT + if (ucb1200_test_io(TC35143_GPIO_VERSION0) != 0 || + ucb1200_test_io(TC35143_GPIO_VERSION1) != 0) { + SCP_REG_GPCR |= SCP_GPCR_PA19; /* direction : out mode */ + SCP_REG_GPWR |= SCP_GPCR_PA19; /* set Hi */ + dbg_udc(1, "udc_connect: %d GPCR: %x GPWR: %x GPRR: %x", + SCP_GPCR_PA19, SCP_REG_GPCR, SCP_REG_GPWR, SCP_REG_GPRR); + } + +#elif defined(CONFIG_SA1100_CONNECT_GPIO) + + GAFR |= GPIO_GPIO (CONFIG_SA1100_CONNECT_GPIO); + GPDR |= GPIO_GPIO (CONFIG_SA1100_CONNECT_GPIO); + + #ifdef CONFIG_SA1100_CONNECT_ACTIVE_HIGH + GPSR = GPIO_GPIO (CONFIG_SA1100_CONNECT_GPIO); + dbg_udc (1, "udc_connect: %d ACTIVE_HIGH %x %x %x", CONFIG_SA1100_CONNECT_GPIO, + GPIO_GPIO (CONFIG_SA1100_CONNECT_GPIO), GPDR, GPLR); + #else + GPCR = GPIO_GPIO (CONFIG_SA1100_CONNECT_GPIO); + dbg_udc (1, "udc_connect: %d ACTIVE_LOW %x %x %x", CONFIG_SA1100_CONNECT_GPIO, + GPIO_GPIO (CONFIG_SA1100_CONNECT_GPIO), GPDR, GPLR); + #endif + +#else /* defined(CONFIG_SA1100_CONNECT_GPIO) */ + +#warning UDC_CONNECT not implemented + +#endif + +} + + +/** + * udc_disconnect - disable pullup resistor + * + * Turn off the USB connection by disabling the pullup resistor. + */ +void udc_disconnect (void) +{ + +#if defined(CONFIG_SA1100_COLLIE) // XXX change to 5500 + + if (ucb1200_test_io(TC35143_GPIO_VERSION0) != 0 || + ucb1200_test_io(TC35143_GPIO_VERSION1) != 0) { + SCP_REG_GPWR &= ~SCP_GPCR_PA19; /* set Hi */ + SCP_REG_GPCR &= ~SCP_GPCR_PA19; /* direction : out mode */ + + dbg_udc(1, "udc_disconnect: %d GPCR: %x GPWR: %x GPRR: %x", + SCP_GPCR_PA19, SCP_REG_GPCR, SCP_REG_GPWR, SCP_REG_GPRR); + } + + +#elif defined(CONFIG_SA1100_CONNECT_GPIO) + +#ifdef CONFIG_SA1100_CONNECT_ACTIVE_HIGH + GPCR = GPIO_GPIO (CONFIG_SA1100_CONNECT_GPIO); + dbg_udc (1, "udc_disconnect: %d ACTIVE_HIGH", CONFIG_SA1100_CONNECT_GPIO); +#else + GPSR = GPIO_GPIO (CONFIG_SA1100_CONNECT_GPIO); + dbg_udc (1, "udc_disconnect: %d ACTIVE_LOW", CONFIG_SA1100_CONNECT_GPIO); +#endif + + GPDR &= ~GPIO_GPIO (CONFIG_SA1100_CONNECT_GPIO); + +#endif +} + + +/* ********************************************************************************************* */ + +static __inline__ void set_interrupts (int flags) +{ + int ok; + //dbg_udc(1, "udc_set_interrupts: %02x", flags); + + // set interrupt mask + SET_AND_TEST ((*(UDCCR) = flags), (udc (UDCCR) != flags), ok); + if (!ok) { + dbg_intr (0, "failed to set UDCCR %02x to %02x", *(UDCCR), flags); + } +} + +/** + * udc_enable_interrupts - enable interrupts + * + * Switch on UDC interrupts. + * + */ +void udc_all_interrupts (struct usb_device_instance *device) +{ + set_interrupts (0); + dbg_udc (1, "%02x", *(UDCCR)); + + // setup tick +#ifdef USE_ADD_DEL_TIMER_FOR_USBADDR_CHECK + if (!usb_addr_check_initialized) { + init_timer (&sa1100_usb_dev_addr_check); + usb_addr_check_initialized = 1; + sa1100_usb_dev_addr_check.function = sa1100_tick; + sa1100_usb_dev_addr_check.data = 1; + sa1100_usb_dev_addr_check.expires = jiffies + CHECK_INTERVAL; + add_timer (&sa1100_usb_dev_addr_check); + } +#else + // XXX sa1100_tq.sync = 0; + sa1100_tq.routine = sa1100_tick; + sa1100_tq.data = &udc_saw_sof; + queue_task (&sa1100_tq, &tq_timer); +#endif + +} + + +/** + * udc_suspended_interrupts - enable suspended interrupts + * + * Switch on only UDC resume interrupt. + * + */ +void udc_suspended_interrupts (struct usb_device_instance *device) +{ + set_interrupts (UDCCR_SRM); + dbg_udc (1, "%02x", *(UDCCR)); +} + + +/** + * udc_disable_interrupts - disable interrupts. + * + * switch off interrupts + */ +void udc_disable_interrupts (struct usb_device_instance *device) +{ + set_interrupts (UDCCR_SRM | UDCCR_EIM | UDCCR_RIM | UDCCR_TIM | UDCCR_SRM | UDCCR_REM); + dbg_udc (1, "%02x", *(UDCCR)); +} + +/* ********************************************************************************************* */ + +/** + * udc_ep0_packetsize - return ep0 packetsize + */ +int udc_ep0_packetsize (void) +{ + return EP0_PACKETSIZE; +} + +/** + * udc_enable - enable the UDC + * + * Switch on the UDC + */ +void udc_enable (struct usb_device_instance *device) +{ + int ok; + dbg_udc (1, "************************* udc_enable:"); + + //dbg_udc(0, ""); + //dbg_udc(0, "udc_enable: device: %p", device); + + // save the device structure pointer + udc_device = device; + + // enable UDC + for (ok = 0; (*(UDCCR) & UDCCR_UDA) && ok < 100000; ok++); + if (ok > 10000) { + dbg_udc (0, "Waiting too long to go inactive: UDCCR: %02x", *(UDCCR)); + } + // set UDD + SET_AND_TEST ((*(UDCCR) |= UDCCR_UDD), (!udc (UDCCR) & UDCCR_UDD), ok); + if (!ok) { + dbg_udc (0, "Waiting too long to disable: UDCCR: %02x", *(UDCCR)); + } + // reset UDD + SET_AND_TEST ((*(UDCCR) &= ~UDCCR_UDD), (udc (UDCCR) & UDCCR_UDD), ok); + if (!ok) { + dbg_udc (0, "can't enable udc, FAILED: %02x", *(UDCCR)); + } +} + + +/** + * udc_disable - disable the UDC + * + * Switch off the UDC + */ +void udc_disable (void) +{ + int ok; + dbg_udc (1, "************************* udc_disable:"); + + // tell tick task to stop +#ifdef USE_ADD_DEL_TIMER_FOR_USBADDR_CHECK + del_timer (&sa1100_usb_dev_addr_check); + sa1100_usb_dev_addr_check.data = 0; + usb_addr_check_initialized = 0; +#else + sa1100_tq.data = NULL; + while (sa1100_tq.sync) { + //printk(KERN_DEBUG"waiting for sa1100_tq to stop\n"); + schedule_timeout (10 * HZ); + } +#endif + + // disable UDC + + for (ok = 0; (*(UDCCR) & UDCCR_UDA) && ok < 100000; ok++); + if (ok > 10000) { + dbg_udc (0, "Waiting too long to go inactive: UDCCR: %02x", *(UDCCR)); + } + // set UDD + SET_AND_TEST ((*(UDCCR) |= UDCCR_UDD), (!udc (UDCCR) & UDCCR_UDD), ok); + if (!ok) { + dbg_udc (0, "Waiting too long to disable: UDCCR: %02x", *(UDCCR)); + } + + dbg_udc (7, "CCR: %02x", *(UDCCR)); + + // reset device pointer + udc_device = NULL; +} + + +/** + * udc_startup - allow udc code to do any additional startup + */ +void udc_startup_events (struct usb_device_instance *device) +{ + usbd_device_event (device, DEVICE_INIT, 0); + usbd_device_event (device, DEVICE_CREATE, 0); + usbd_device_event (device, DEVICE_HUB_CONFIGURED, 0); + usbd_device_event (device, DEVICE_RESET, 0); // XXX should be done from device event +} + + +/* ********************************************************************************************* */ + +/** + * udc_name - return name of USB Device Controller + */ +char *udc_name (void) +{ + return UDC_NAME; +} + +/** + * udc_request_udc_irq - request UDC interrupt + * + * Return non-zero if not successful. + */ +int udc_request_udc_irq () +{ + // request IRQ and two dma channels + // XXX GPDR &= ~GPIO_GPIO(23); + // XXX set_GPIO_IRQ_edge(GPIO_GPIO(CONFIG_SA1100_USBCABLE_GPIO), GPIO_BOTH_EDGES); + + dbg_init (2, "requesting udc irq: %d %d", 13, IRQ_Ser0UDC); + + return request_irq (IRQ_Ser0UDC, int_hndlr_device, SA_INTERRUPT | SA_SAMPLE_RANDOM, + "SA1100 USBD Bus Interface", NULL); +} + +/** + * udc_request_cable_irq - request Cable interrupt + * + * Return non-zero if not successful. + */ +int udc_request_cable_irq () +{ +#ifdef CONFIG_SA1100_USBCABLE_GPIO + dbg_init (2, "requesting cable irq: %d %d", + CONFIG_SA1100_USBCABLE_GPIO, SA1100_GPIO_TO_IRQ (CONFIG_SA1100_USBCABLE_GPIO)); + GPDR &= ~GPIO_GPIO (23); + set_GPIO_IRQ_edge (GPIO_GPIO (CONFIG_SA1100_USBCABLE_GPIO), GPIO_BOTH_EDGES); + return request_irq (SA1100_GPIO_TO_IRQ (CONFIG_SA1100_USBCABLE_GPIO), int_hndlr_cable, + SA_INTERRUPT | SA_SAMPLE_RANDOM, "SA1100 Monitor", NULL); +#else + return 0; +#endif +} + +/** + * udc_request_udc_io - request UDC io region + * + * Return non-zero if not successful. + */ +int udc_request_io () +{ + //sa1100_bi_init_dma (); + + usbd_rcv_dma = usbd_tx_dma = -1; + + if (sa1100_request_dma (&usbd_rcv_dma, "SA1100 USBD OUT - rcv dma", DMA_Ser0UDCRd)) { + dbg_init (2, "channel %d taken", usbd_rcv_dma); + return -EINVAL; + } + + + if (sa1100_request_dma (&usbd_tx_dma, "SA1100 USBD IN - tx dma", DMA_Ser0UDCWr)) { + dbg_init (2, "channel %d taken", usbd_tx_dma); + sa1100_free_dma (usbd_rcv_dma); + return -EINVAL; + } + + return 0; +} + +/** + * udc_release_udc_irq - release UDC irq + */ +void udc_release_udc_irq () +{ + free_irq (IRQ_Ser0UDC, NULL); +} + +/** + * udc_release_cable_irq - release Cable irq + */ +void udc_release_cable_irq () +{ +#ifdef CONFIG_SA1100_USBCABLE_GPIO + dbg_init (2, "freeing cable irq: %d", CONFIG_SA1100_USBCABLE_GPIO); + free_irq (SA1100_GPIO_TO_IRQ (CONFIG_SA1100_USBCABLE_GPIO), NULL); +#endif +} + +/** + * udc_release_io - release UDC io region + */ +void udc_release_io () +{ + sa1100_free_dma (usbd_rcv_dma); + sa1100_free_dma (usbd_tx_dma); +} + +/* ep0 dma engine functions ******************************************************************** */ + +/* + * EP0 State - See the Intel Application Note + */ + +#define EP0_STATE_IDLE 0 +#define EP0_STATE_IN_DATA_PHASE 1 +#define EP0_STATE_END_IN 2 + +char *ep0_state_desc[] = { + "idle", "in", "end" +}; + +static int ep0_state = EP0_STATE_IDLE; + +//struct urb ep0_urb; +struct urb *ep0_urb; + + +static unsigned char *buffer; +static unsigned int buffer_cnt; + + +/* + * There are two scenarios: + * + * 1. Data to be sent is an exact multiple of the packetsize and less than + * the requested size. An empty packet needs to be sent. + * + * 2. Everything else. The last packet will be recognized as the last + * because it is less than the packetsize OR because we have sent exactly + * the required amount of data. + */ + +static unsigned int send_zlp; + +struct usb_endpoint_instance *ep0_endpoint; + +char *send_zlp_description[] = { + "exact", "forced" +}; + +int ep0_enable (struct usb_device_instance *device, struct usb_endpoint_instance *endpoint) +{ + ep0_endpoint = endpoint; + + dbg_ep0(0, "ep0_urb: %p", ep0_urb); + if (!ep0_urb) { + if (!(ep0_urb = usbd_alloc_urb (device, device->function_instance_array, 0, 512))) { + dbg_ep0 (1, "ep0_enable: usbd_alloc_urb failed\n"); + } + } else { + dbg_ep0 (1, "ep0_enable: ep0_urb already allocated\n"); + } + return 0; +} + +void ep0_disable (void) +{ + dbg_ep0(0, "ep0_urb: %p", ep0_urb); + if (ep0_urb) { + usbd_dealloc_urb (ep0_urb); + ep0_urb = 0; + } else { + dbg_ep0 (1, "ep0_disable: ep0_urb already NULL\n"); + } +} + +int ep0_reset (void) +{ + dbgENTER (dbgflg_usbdbi_ep0, 1); + ep0_state = EP0_STATE_IDLE; + udc_reset_ep (0); + return 0; +} + +/* + * ep0_set_opr + * + * Set OPR and optionally DE. + */ +static void ep0_set_opr (void) +{ + int ok; + IOIOIO(UDCCS0, UDCCS0_S0, *(UDCCS0) = UDCCS0_SO , (*(UDCCS0) & UDCCS0_OPR), ok); + if (!ok) { + dbg_ep0(0, "failed to reset OPR"); + } + dbg_ep0(1,"CS0: %02x ok: %d", *(UDCCS0), ok); +} + +/* + * ep0_set_opr_and_de + * + * Set OPR and optionally DE. + */ +static void ep0_set_opr_and_de (void) +{ + int ok; + IOIOIO(UDCCS0, UDCCS0_S0, *(UDCCS0) = (UDCCS0_SO | UDCCS0_DE) , (*(UDCCS0) & UDCCS0_OPR), ok); + if (!ok) { + dbg_ep0(0, "failed to reset OPR"); + } + dbg_ep0(1,"CS0: %02x ok: %d", *(UDCCS0), ok); +} + + +/* + * ep0_read_data + * + * Retreive setup data from FIFO. + */ +__inline__ static int ep0_read_data (struct usb_device_request *request, int max) +{ + int fifo_count; + int bytes_read; + int i; + int reads; + unsigned char *cp = (unsigned char *) request; + int udcwc; + int udccs0; + + udccs0 = *(UDCCS0); + udcwc = fifo_count = udc(UDCWC) & 0xff; + + if (fifo_count != max) { + dbg_ep0 (0, "ep0_read_data: have %d bytes; want max\n", fifo_count); + } + + reads = 0; + bytes_read = 0; + + for (reads = 0, bytes_read = 0; udcwc && fifo_count--;) { + i = 0; + do { + *cp = (unsigned char) *(UDCD0); + // XXX without this reads may be corrupt, typically the wLength field + // will contain bad data, which in turn will cause get descriptor config + // to return the full set of descriptors in response to limited first + // get descriptor 9, which will BSOD windows + udelay(10); + reads++; + i++; + } while ((((udcwc = udc(UDCWC) & 0xff)) > fifo_count) && (i < 10)); + + if (i == 10) { + dbg_ep0 (0, "failed: UDCWC: %2x %2x bytes: %d fifo: %d reads: %d no change\n", + udcwc, *(UDCWC), bytes_read, fifo_count, reads); + } + + if (udcwc < (fifo_count - 1)) { + dbg_ep0 (0, "failed: UDCWC: %2x %2x bytes: %d fifo: %d reads: %d too low\n", + udcwc, *(UDCWC), bytes_read, fifo_count, reads); + } + + cp++; + bytes_read++; + + if ((bytes_read >= max) && (fifo_count > 0)) { + dbg_ep0 (0, "reading too much data\n"); + break; + } + } + + dbg_ep0 ((bytes_read == max)?2:0, + "ep0_read_data: reads: %d want: %d fifo_count: %d bytes_read: %d CS0: %02x:%02x CWC: %02x", + reads, max, fifo_count, bytes_read, *(UDCCS0), udccs0, udcwc); + + return bytes_read; +} + + +/* * + * ep0_read_request + * + * Called when in IDLE state to get a setup packet. + */ +static void ep0_read_request (unsigned int udccs0, unsigned int udccr) +{ + int i; + int ok; + + if (!(udccr & UDCCR_UDA)) { + dbg_ep0 (3, "warning, UDC not active"); + } + // reset SST + if (udccs0 & UDCCS0_SST) { + IOIOIO (UDCCS0, UDCCS0_SST, *(UDCCS0) = UDCCS0_SST, *(UDCCS0) & UDCCS0_SST, ok); + if (!ok) { + dbg_ep0 (0, "failed to reset SST CS0: %02x ok: %d", *(UDCCS0), ok); + } + } + // check UDC Endpoint 0 Control/Status register for OUT Packet Ready (OPR) (c.f. 11.8.7.1) + if (!(udccs0 & UDCCS0_OPR)) { + // wait for another interrupt + dbg_ep0 (3, "wait for another interrupt CS0: %02x", udccs0); + return; + } + + // read a device request + ep0_urb->actual_length = 0; + if ((i = ep0_read_data (&ep0_urb->device_request, 8)) < 8) { + ep0_set_opr (); + dbg_ep0 (2, "not enough data: %d", i); + return; + } + //ep0_need_data = le16_to_cpu(ep0_urb->device_request.wLength); + dbg_ep0 (3, "bmRequestType:%02x bRequest:%02x wValue:%04x wIndex:%04x wLength:%04x", + ep0_urb->device_request.bmRequestType, + ep0_urb->device_request.bRequest, + le16_to_cpu (ep0_urb->device_request.wValue), + le16_to_cpu (ep0_urb->device_request.wIndex), + le16_to_cpu (ep0_urb->device_request.wLength)); + + { + char *cp = (char *) &ep0_urb->device_request; + dbg_ep0 (1, "%02x %02x %02x %02x %02x %02x %02x %02x", + cp[0], cp[1], cp[2], cp[3], cp[4], cp[5], cp[6], cp[7] + ); + } + + // check if we have been initialized + if (!ep0_urb->device) { + ep0_set_opr_and_de (); + udc_stall_ep (0); + return; + } + + // check direction of data transfer + if ((ep0_urb->device_request.bmRequestType & USB_REQ_DIRECTION_MASK) == USB_REQ_HOST2DEVICE) { + + // if this is a set address save it + if ((ep0_urb->device_request.bmRequestType == 0) && (ep0_urb->device_request.bRequest == USB_REQ_SET_ADDRESS)) { + + usb_address = le16_to_cpu(ep0_urb->device_request.wValue) & 0xff; + + /* + * very occasionally register write will fail, delay and redo twice + * to ensure that it actually got through, cannot check as UDC + * will not propagate until SO and DE are set. We also need a delay + * after setting SO and DE, exiting too soon can also result in + * problems if address has not propagated. + */ + + + *(UDCAR) = usb_address; + udelay(2); + *(UDCAR) = usb_address; + udelay(2); + *(UDCAR) = usb_address; + ep0_set_opr_and_de(); + udelay(40); + + if (usbd_recv_setup(ep0_urb)) { + dbg_ep0(5, "usb_recv_setup failed, not stalling"); + } + } + else { + //ep0_set_opr_and_de(); + // submit urb to ep0, stall if non-zero result + if (usbd_recv_setup(ep0_urb)) { + dbg_ep0(1, "usb_recv_setup failed, stalling"); + udc_stall_ep(0); + } + ep0_set_opr_and_de(); // tbr getting better results with this. + } + return; + } + + // submit urb to ep0, stall if non-zero result + if (usbd_recv_setup (ep0_urb)) { + dbg_ep0 (2, "usb_recv_setup failed, stalling"); + ep0_set_opr_and_de (); + udc_stall_ep (0); + return; + } + + // device reqeust has specified Device-to-Host, so we should be returning data + + // check request length, zero is not legal + if (!le16_to_cpu (ep0_urb->device_request.wLength)) { + dbg_ep0 (0, "wLength zero, stall"); + ep0_set_opr_and_de (); + udc_stall_ep (0); + return; + } + // check that we have some data to send back, zero should not be possible + if (!ep0_urb->actual_length) { + dbg_ep0 (0, "no data, stall"); + ep0_set_opr_and_de (); + udc_stall_ep (0); + return; + } + // everything looks sane, so set buffer pointers and setup for data transfer + buffer = ep0_urb->buffer; + buffer_cnt = ep0_urb->actual_length; + + /* + * IFF we are sending a multiple of the packet size AND less than was + * requested we will need to send an empty packet after all the data packets. + */ + if (!(buffer_cnt % 8) && (buffer_cnt < le16_to_cpu (ep0_urb->device_request.wLength))) { + send_zlp = 1; + dbg_ep0 (1, "DEVICE2HOST: SEND ZLP %x %x", ep0_urb->actual_length, + le16_to_cpu (ep0_urb->device_request.wLength)); + } else { + send_zlp = 0; + dbg_ep0 (2, "DEVICE2HOST: NO ZLP %x %x", ep0_urb->actual_length, + le16_to_cpu (ep0_urb->device_request.wLength)); + } + + ep0_state = EP0_STATE_IN_DATA_PHASE; +} + +/* + * ep0_in + * + * Send some data to host. + */ +static void ep0_in (unsigned int udccs0) +{ + int transfer_cnt = 0; + unsigned int fifo_count; + volatile int writes = 0; + unsigned long start = jiffies; + int ok; + + udccs0 = *(UDCCS0); + dbg_ep0 (2, "CS0: %02x", udccs0); + + // check for non empty FIFO + if (*(UDCWC)) { + dbg_ep0 (0, "FIFO not empty"); + return; + } + // process iff ep0 is not stalled, no premature end and IPR is clear + if (udccs0 & (UDCCS0_SE | UDCCS0_SST)) { + dbg_ep0 (1, "ep0_in: SE or SST set\n"); + return; + } + // first check that IPR is not set, if it is then we wait for it to clear. + if (udccs0 & UDCCS0_IPR) { + + for (ok = 0; (*(UDCCS0) & UDCCS0_IPR) && ok < 2000; ok++) { + udelay (25); // XXX + } + if (ok == 2000) { + dbg_ep0 (0, "IPR clear timed out"); + } + dbg_ep0 (0, "IPR was set count: %d", ok); + } + // get up to first 8 bytes of data + transfer_cnt = MIN (8, buffer_cnt); + buffer_cnt -= transfer_cnt; + fifo_count = 0; + + do { + int count = 0; + int udcwc; + do { + *(UDCD0) = (unsigned long) buffer[fifo_count]; + count++; + writes++; + } while (((udcwc = udc (UDCWC)) == fifo_count) && (count < 2000)); + + if (udcwc <= fifo_count) { + dbg_ep0 (0, + "sending STALL, failed writing FIFO transfer: %d fifo_count: %x CS0: %02x CWC: %02x", + transfer_cnt, fifo_count, *(UDCCS0), udcwc); + udc_stall_ep (0); + ep0_state = EP0_STATE_IDLE; + return; + } + + fifo_count++; + + } while (fifo_count < transfer_cnt); + + dbg_ep0 (3, "elapsed: %lx writes: %d fifo_count: %d transfer_cnt: %d buffer_cnt: %d CS0: %02x CWC: %02x", + jiffies - start, writes, fifo_count, transfer_cnt, buffer_cnt, *(UDCCS0), *(UDCWC)); + + buffer += transfer_cnt; + + // set data end, and start + if (buffer_cnt == 0) { + ep0_state = EP0_STATE_END_IN; + } + + IOIOIO (UDCCS0, UDCCS0_IPR, + *(UDCCS0) = (UDCCS0_IPR | ((((!buffer_cnt) && (send_zlp != 1)) ? UDCCS0_DE : 0))), + !(*(UDCCS0) & UDCCS0_IPR), ok); + + if (!ok) { + dbg_ep0 (0, "failed to set IPR CS0: %02x ok: %d", *(UDCCS0), ok); + } +} + +/* + * ep0_end_in + * + * Handle end of IN, possibly with ZLP. + */ +static void ep0_end_in (unsigned int udccs0) +{ + int ok; + + dbg_ep0 (2, " status: %02x CS0: %02x", udccs0, *(UDCCS0)); + + // reset SST if necessary + if (udccs0 & UDCCS0_SST) { + IOIOIO (UDCCS0, UDCCS0_SST, *(UDCCS0) = UDCCS0_SST, *(UDCCS0) & UDCCS0_SST, ok); + if (!ok) { + dbg_ep0 (0, "failed to reset SST"); + } + } + // reset SE if necessary + if (udccs0 & UDCCS0_SE) { + IOIOIO (UDCCS0, UDCCS0_SSE, *(UDCCS0) = UDCCS0_SSE, *(UDCCS0) & UDCCS0_SSE, ok); + if (!ok) { + dbg_ep0 (0, "failed to reset SE"); + } + } + // ensure that IPR is not set and send zero length packet if necessary + if (!(udc (UDCCS0) & UDCCS0_IPR) && (send_zlp == 1)) { + IOIOIO (UDCCS0, UDCCS0_DE | UDCCS0_IPR, + *(UDCCS0) = (UDCCS0_DE | UDCCS0_IPR), + (*(UDCCS0) & (UDCCS0_DE | UDCCS0_IPR)) != (UDCCS0_DE | UDCCS0_IPR), ok); + if (!ok) { + dbg_ep0 (0, "failed to set DE and IPR CS0: %02x", *(UDCCS0)); + } + else { + dbg_ep0 (1, "set DE and IPR, ZLP sent CS0: %02x", *(UDCCS0)); + } + } + // set state to IDLE + ep0_state = EP0_STATE_IDLE; + send_zlp = 0; +} + + + +/* + * ep0_int_hndlr + * + * Handle ep0 interrupts. + * + * Generally follows the example in the Intel Application Note. + * + */ +void ep0_int_hndlr (unsigned int status) +{ + unsigned int udccs0; + unsigned int udccr; + + udccs0 = udc (UDCCS0); + udccr = udc (UDCCR); + + ep0_interrupts++; + + dbg_ep0 (4, "------------>[%d:%s:%s CCR: %02x CS0: %02x CWC: %02x CAR: %02x]", + ep0_interrupts, ep0_state_desc[ep0_state], send_zlp_description[send_zlp], udccr, + udccs0, *(UDCWC), *(UDCAR)); + + if (udccs0 == UDCCS0_IPR) { + dbg_ep0 (3, "IPR SET [%d:%02x]", ep0_state, udccs0); + return; + } + + if (udccs0 & UDCCS0_SST) { + dbg_ep0 (3, "previously sent stall [%d:%02x]", ep0_state, udccs0); + } + // SE + if (udccs0 & UDCCS0_SE) { + int ok; + dbg_ep0 (3, "unloading, found UDCCS0_SE: CS0: %02x", udccs0); + + // reset SE + IOIOIO (UDCCS0, UDCCS0_SSE, *(UDCCS0) = UDCCS0_SSE, *(UDCCS0) & UDCCS0_SE, ok); + if (!ok) { + dbg_ep0 (0, "failed to reset SE CS0: %02x ok: %d", *(UDCCS0), ok); + } + // clear SE before unloading any setup packets + if (udccs0 & UDCCS0_OPR) { + // reset OPR + *(UDCCS0) = UDCCS0_SO; + } + ep0_state = EP0_STATE_IDLE; + if (send_zlp) { + dbg_ep0 (0, "not sending ZLP CS0: %02x", *(UDCCS0)); + } + send_zlp = 0; + } + + if ((ep0_state != EP0_STATE_IDLE) && (udccs0 & UDCCS0_OPR)) { + dbg_ep0 (3, "Forcing EP0_STATE_IDLE CS0: %02x", udccs0); + ep0_state = EP0_STATE_IDLE; + } + + switch (ep0_state) { + case EP0_STATE_IDLE: + dbg_ep0 (4, "state: EP0_STATE_IDLE CS0: %02x", udccs0); + ep0_read_request (udccs0, udccr); + if (ep0_state != EP0_STATE_IN_DATA_PHASE) { + break; + } + // fall through to data phase + ep0_set_opr (); + + case EP0_STATE_IN_DATA_PHASE: + dbg_ep0 (4, "state: EP0_STATE_IN_DATA_PHASE %x", udccs0); + ep0_in (udccs0); + break; + + case EP0_STATE_END_IN: + dbg_ep0 (4, "state: EP0_STATE_END_IN %x", udccs0); + ep0_end_in (udccs0); + break; + } + dbg_ep0 (4, "<------------[%s:%s CCR: %02x CS0: %02x CWC: %02x]", + ep0_state_desc[ep0_state], send_zlp_description[send_zlp], *(UDCCR), *(UDCCS0), + *(UDCWC)); +} + +/* ep1 dma engine functions ******************************************************************** */ + +static int dmachn_rx; // rx dma channel +static int dma_enabled; // dma channel has been setup +static int dma_is_active; // dma engine is running +static int dma_rx_active; // dma engine is running + +static dma_addr_t dma_rx_curpos; +struct usb_device_instance *ep1_device; +struct usb_endpoint_instance *ep1_endpoint; + +/* * + * ep1_stop_dma - stop the DMA engine + * + * Stop the DMA engine. + */ +static void ep1_stop_dma (struct urb *urb) +{ + if (urb && dma_enabled) { + unsigned long flags; + save_flags_cli (flags); + if (!dma_is_active) { + restore_flags (flags); + dbg_rx (1, "dma OFF: %x %x %x", dmachn_rx, dma_enabled, dma_is_active); + return; + } + + dma_is_active = 0; + restore_flags (flags); + + if (ep1_endpoint->rcv_packetSize > 20) { + _sa1100_bi_dma_flush_all_irq(dmachn_rx); + } + } +} + + +/* * + * ep1_start_dma - start the DMA engine running + * + * Start the DMA engine. This should allow incoming data to be stored + * in the DMA receive buffer. + * + */ +static void ep1_start_dma (struct urb *urb) +{ + if (urb && dma_enabled) { + unsigned long flags; + int ok; + save_flags_cli (flags); + if (dma_is_active) { + restore_flags (flags); + dbg_rx (1, "dma ON: %x %x %x", dmachn_rx, dma_enabled, dma_is_active); + return; + } + + dma_is_active = 1; + + dbg_rx (1, "urb: %p urb->buffer: %p actual_length: %d rcv_packetSize: %d", + urb, urb->buffer, urb->actual_length, urb->endpoint->rcv_packetSize); + + if (!(dma_rx_curpos = pci_map_single (NULL, (void *) urb->buffer + urb->actual_length, + urb->endpoint->rcv_packetSize, + PCI_DMA_FROMDEVICE))) { + restore_flags (flags); + dbg_rx (0, "pci_map_single failed"); + return; + } + + SET_AND_TEST (*(UDCCS1) = UDCCS1_RPC, _udc (UDCCS1) & UDCCS1_RPC, ok); + + + // enable DMA only if packet to big for FIFO (twenty bytes) + if (ep1_endpoint->rcv_packetSize > 20) { + dma_rx_active = + _sa1100_bi_dma_queue_buffer_irq (dmachn_rx, dma_rx_curpos, urb->endpoint->rcv_packetSize); + _sa1100_bi_dma_run (dmachn_rx, dma_rx_active); + } + + restore_flags (flags); + } +} + + +/* ep1 public functions ************************************************************************ */ + +/** + * ep1_init - initialize the endpoint + * + */ +void ep1_enable (struct usb_device_instance *device, struct usb_endpoint_instance *endpoint, + int chn) +{ + if (endpoint) { + int ok; + ep1_device = device; + ep1_endpoint = endpoint; + dmachn_rx = chn; + + dbg_rx (1, "ep1_endpoint %p", ep1_endpoint); + + //usbd_fill_rcv(device, endpoint, 5); + + SET_AND_TEST (*(UDCOMP) = + ep1_endpoint->rcv_packetSize - 1, + udc (UDCOMP) != ep1_endpoint->rcv_packetSize - 1, ok); + if (!ok) { + dbg_rx (0, "FAILED setting pktsize %d", *(UDCOMP)); + } + // setup the dma engine operating parameters + dma_is_active = 0; + dma_enabled = 1; + if (!ep1_endpoint->rcv_urb) { + ep1_endpoint->rcv_urb = first_urb_detached (&ep1_endpoint->rdy); + dbg_rx (1, "SETTING ep1_endpoint->rcv_urb: %p", ep1_endpoint->rcv_urb); + } + else { + dbg_rx (1, "CHECKING ep1_endpoint->rcv_urb: %p", ep1_endpoint->rcv_urb); + } + ep1_start_dma (ep1_endpoint->rcv_urb); + } +} + + +/** + * ep1_reset - reset the endpoint + * + * Return non-zero if error. + */ +void ep1_reset (void) +{ + int ok; + + dbg_rx (1, "CHECKING ep1_endpoint->rcv_urb: %p", ep1_endpoint->rcv_urb); + + ep1_stop_dma (ep1_endpoint->rcv_urb); + + if (ep1_endpoint->rcv_urb) { + ep1_endpoint->rcv_urb->actual_length = 0; + } + ep1_start_dma (ep1_endpoint->rcv_urb); + + udc_reset_ep (1); + SET_AND_TEST (*(UDCOMP) = + ep1_endpoint->rcv_packetSize - 1, + udc (UDCOMP) != ep1_endpoint->rcv_packetSize - 1, ok); + if (!ok) { + dbg_rx (0, "FAILED setting pktsize %d", *(UDCOMP)); + } +} + + +/** + * ep1_disable - disable endpoint for use + * + */ +void ep1_disable () +{ + dbg_rx (1, "dma_is_active: %d dma_enabled: %d", dma_is_active, dma_enabled); + + if (dma_is_active) { + ep1_stop_dma (ep1_endpoint->rcv_urb); + } + + if (!dma_enabled) { + dbg_rx (1, "!dma_enabled: %x %x %x", dmachn_rx, dma_enabled, dma_is_active); + return; + } + { + unsigned long flags; + save_flags_cli (flags); + if (dma_is_active) { + restore_flags (flags); + dbg_rx (1, "dma_is_active: %x %x %x", dmachn_rx, dma_enabled, dma_is_active); + ep1_stop_dma (ep1_endpoint->rcv_urb); + usbd_dealloc_urb (ep1_endpoint->rcv_urb); + dbg_rx(1, "CLEARING rcv_urb %p", ep1_endpoint->rcv_urb); + ep1_endpoint->rcv_urb = NULL; + } + dma_enabled = 0; + restore_flags (flags); + } + + usbd_flush_rcv (ep1_endpoint); + ep1_endpoint = NULL; +} + + +/* + * Endpoint 1 interrupts will be directed here. + * + * status : the contents of the UDC Status/Interrupt Register. + */ +void ep1_int_hndlr (unsigned int status) +{ + dma_addr_t dma_address; + unsigned int udccs1; + int ok; + unsigned int len = 0; + unsigned int urb_bad = 0; + + rx_interrupts++; + + if (!((udccs1 = *(UDCCS1)) & UDCCS1_RPC)) { + SET_AND_TEST (*(UDCCS1) = UDCCS1_SST, _udc (UDCCS1) & UDCCS1_SST, ok); + SET_AND_TEST (*(UDCCS1) = UDCCS1_RPC, _udc (UDCCS1) & UDCCS1_RPC, ok); + return; + } + //dbg_rx(4, "udccs1: %02x", udccs1); + + dma_is_active = 0; + + // DMA or non-DMA + // XXX - we should be able to collapse these together + if (ep1_endpoint->rcv_packetSize <= 20) { + + // non-DMA version + if (!(udccs1 & (UDCCS1_RPE))) { + + // get residual data from fifo + if (ep1_endpoint->rcv_urb) { + unsigned char *dmabuf; + + dmabuf = ep1_endpoint->rcv_urb->buffer + ep1_endpoint->rcv_urb->actual_length; + + // XXX Note that it is apparantly impossible to do this with 100% + // reliability. Only copy data in if/while length is less than packet size + + for (; + (len < (unsigned) ep1_endpoint->rcv_packetSize) + && *(UDCCS1) & UDCCS1_RNE; len++) { + *dmabuf++ = *(UDCDR); + //udelay(1); + } + // record an error if the FIFO is not empty + udc_ep1_errors += ((urb_bad = *(UDCCS1) & UDCCS1_RNE)) ? 1 : 0; + } else { + dbg_rx (0, "NO RCV URB"); + } + } else { + udc_rpe_errors++; + } + } + // retrieve dma_address, note that this is not reliable + else if (!_sa1100_bi_dma_stop_get_current_irq (dmachn_rx, &dma_address, dma_rx_active)) { + + // DMA version + // stop dma engine, if we can get the current address, unmap, get residual data, and give to bottom half + pci_unmap_single (NULL, dma_rx_curpos, ep1_endpoint->rcv_packetSize, + PCI_DMA_FROMDEVICE); + + if (!(udccs1 & (UDCCS1_RPE))) { + + // get residual data from fifo + if (ep1_endpoint->rcv_urb) { + unsigned char *dmabuf; + + len = dma_address - dma_rx_curpos; + dmabuf = ep1_endpoint->rcv_urb->buffer + ep1_endpoint->rcv_urb->actual_length + len; + + /* WARNING - it is impossible empty the FIFO with 100% accuracy. + * + * We only copy data in if/while length is less than packet size + * + * There are two problems. First fetching the dma address can fail. In this case len will + * be too large and we do not attempt to read any data. + * + * Second, reads sometimes fail to update the FIFO read address resulting in too much + * data being retrieved. Slightly delaying between each read helps to reduce this problem + * at the expense of increasing latency. + * + * This (and the similiar problem with the TX FIFO) are the reason that it is NOT SAFE + * to operate the SA1100 without a CRC across all bulk transfers. The normal USB CRC which + * the hardware uses to protect each packet being sent is not sufficent. The data is being + * damaged getting it into and out of the FIFO(s). + */ + + for (; + (len < (unsigned) ep1_endpoint->rcv_packetSize) + && *(UDCCS1) & UDCCS1_RNE; len++) { + *dmabuf++ = *(UDCDR); + udelay (0); + } + + // record an error if the FIFO is not empty + udc_ep1_errors += ((urb_bad = *(UDCCS1) & UDCCS1_RNE)) ? 1 : 0; + } else { + dbg_rx (0, "NO RCV URB"); + } + } else { + udc_rpe_errors++; + } + } else { + //dbg_rx(0, "nothing to unmap"); + } + + // let the upper levels handle the urb + usbd_rcv_complete_irq (ep1_endpoint, len, urb_bad); + + // clear SST if necessary + if (udccs1 & UDCCS1_SST) { + SET_AND_TEST (*(UDCCS1) = UDCCS1_SST, _udc (UDCCS1) & UDCCS1_SST, ok); + if (!ok) { + dbg_rx (0, "could not clear SST: %02x", *(UDCCS1)); + } + } + // start DMA and reset RPC + SET_AND_TEST (*(UDCCS1) = UDCCS1_RPC, _udc (UDCCS1) & UDCCS1_RPC, ok); + if (!ok) { + dbg_rx (0, "could not clear RPC: %02x", *(UDCCS1)); + } + + if (dma_enabled && ep1_endpoint->rcv_urb) { + dma_is_active = 1; + + // start DMA if packetsize to big for FIFO (twenty bytes) + if ((dma_rx_curpos = + pci_map_single (NULL, + (void *) ep1_endpoint->rcv_urb->buffer + + ep1_endpoint->rcv_urb->actual_length, + ep1_endpoint->rcv_packetSize, PCI_DMA_FROMDEVICE)) + && (ep1_endpoint->rcv_packetSize > 20)) + { + dma_rx_active = _sa1100_bi_dma_queue_buffer_irq (dmachn_rx, dma_rx_curpos, ep1_endpoint->rcv_packetSize); + _sa1100_bi_dma_run (dmachn_rx, dma_rx_active); + } + } +} + +/* ep2 dma engine functions ******************************************************************** */ + + + +static int dmachn_tx; // tx dma channel +static int dma_tx_active; +dma_addr_t dma_tx_curpos; + +static struct usb_endpoint_instance *ep2_endpoint; + +/* * + * ep2_reset_dma - + * @restart: + * + * Reset the DMA engine parameters. + */ +static void ep2_reset_dma (void) +{ + unsigned long flags; + + local_irq_save (flags); + + // stop current dma + _sa1100_bi_dma_flush_all_irq (dmachn_tx); + udelay (10); // XXX + + ep2_endpoint->tx_urb = NULL; + if (dma_tx_curpos) { + pci_unmap_single (NULL, dma_tx_curpos, ep2_endpoint->last, PCI_DMA_TODEVICE); + } + dma_tx_curpos = (dma_addr_t) NULL; + ep2_endpoint->tx_urb = NULL; + ep2_endpoint->sent = 0; + + usbd_flush_tx (ep2_endpoint); + + local_irq_restore (flags); +} + + +/* ep2 public functions ************************************************************************ */ + +/** + * ep2_enable - + * + * Initialize the dma variables. Called once. + * + */ +int ep2_enable (struct usb_device_instance *device, struct usb_endpoint_instance *endpoint, int chn) +{ + dbgENTER (dbgflg_usbdbi_tx, 1); + dmachn_tx = chn; + ep2_disable (); + //ep2_device = device; + { + unsigned long flags; + local_irq_save (flags); + ep2_endpoint = endpoint; + dma_tx_curpos = 0; + ep2_endpoint->sent = 0; + ep2_endpoint->tx_urb = NULL; + local_irq_restore (flags); + } + return 0; +} + + +/* + * Initialise the DMA system to use the given chn for receiving + * packets over endpoint 2. + * + * chn : the dma channel to use. + * returns : 0 if ok, -1 if channel couldn't be used. + */ +void ep2_disable (void) +{ + unsigned long flags; + dbgENTER (dbgflg_usbdbi_tx, 1); + local_irq_save (flags); + if (ep2_endpoint) { + ep2_reset_dma (); + ep2_endpoint = NULL; + } + local_irq_restore (flags); +} + + +/* + * reset any txpkts, it will be up to the client driver to free up the send + * queue. + */ +void ep2_reset (void) +{ + dbgENTER (dbgflg_usbdbi_tx, 1); + ep2_reset_dma (); + udc_reset_ep (2); +} + + +/* Interrupt service routines ****************************************************************** */ + + +/* + * + * Interrupt handler - called with interrupts disabled. + */ +void ep2_int_hndlr (unsigned int status, int in_interrupt) +{ + unsigned int udccs2; + int ok; + int restart = 0; + + udccs2 = *(UDCCS2); + + // do not do anything if FST is set + if (udccs2 & UDCCS2_FST) { + dbg_tx (0, "FST set! (%sint)", in_interrupt ? "" : "not-"); + udc_ep2_fst++; + return; + } else if (udccs2 & UDCCS2_SST) { + dbg_tx (0, "SST set, stalling! (%sint)", in_interrupt ? "" : "not-"); + udc_stall_ep (2); + return; + } + // if in interrupt we have to stop any current activity + if (in_interrupt) { + + tx_interrupts++; + + if (!((udccs2 = *(UDCCS2)) & UDCCS2_TPC)) { + return; + } + + _sa1100_bi_dma_flush_all_irq (dmachn_tx); + + // if requred reset stall and restart + if (udccs2 & (UDCCS2_TPE | UDCCS2_TUR | UDCCS2_SST)) { + + if (udccs2 & UDCCS2_SST) { + SET_AND_TEST (*(UDCCS2) = + UDCCS2_SST, _udc (UDCCS2) & UDCCS2_SST, ok); + if (!ok) { + dbg_tx (0, "Waiting too long to set SST %02x", *(UDCCS2)); + } + } + udc_ep2_errors++; + restart = 1; + if (udccs2 & UDCCS2_TPE) { + udc_ep2_tpe++; + } + //QQQ was: if (udccs2 & UDCCS2_TUR | UDCCS2_SST) + if (udccs2 & UDCCS2_TUR) { + udc_ep2_tur++; + } + if (udccs2 & UDCCS2_SST) { + udc_ep2_sst++; + } + + } + // if we have an active buffer, umap the buffer and update position if previous send was successful + if (dma_tx_curpos) { + pci_unmap_single (NULL, dma_tx_curpos, ep2_endpoint->last, PCI_DMA_TODEVICE); + dma_tx_curpos = (dma_addr_t) NULL; + } + } + + usbd_tx_complete_irq (ep2_endpoint, restart); + + // start dma if we have something to send + if (ep2_endpoint->tx_urb + && ((ep2_endpoint->tx_urb->actual_length - ep2_endpoint->sent) > 0)) { + int ok1, ok2; + //int count; + ep2_endpoint->last = MIN (ep2_endpoint->tx_urb->actual_length - ep2_endpoint->sent, + ep2_endpoint->tx_urb->endpoint->tx_packetSize); + + if (! + (dma_tx_curpos = + pci_map_single (NULL, ep2_endpoint->tx_urb->buffer + ep2_endpoint->sent, + ep2_endpoint->last, PCI_DMA_TODEVICE))) { + dbg_tx (0, "pci_map_single failed"); + } + + /* The order of the following is critical.... + * Reseting the TPC clears the FIFO, so the DMA engine cannot be started until after + * TPC is set. But this opens a window where the USB UCD can fail because it is + * waiting for the DMA engine to fill the FIFO and it receives an IN packet from + * the host. The best we can do is to minimize the window. [Note that this effect + * is most noticeable on slow, 133Mhz, processors.] + * + * 1. Set IMP and all DMA registers, but do not start DMA yet + * 2. reset TPC + * 3. start DMA + */ + SET_AND_TEST (*(UDCIMP) = + ep2_endpoint->last - 1, _udc (UDCIMP) != (ep2_endpoint->last - 1), + ok1); + if (ep2_endpoint->last > 16) { + dma_tx_active = _sa1100_bi_dma_queue_buffer_irq (dmachn_tx, dma_tx_curpos, ep2_endpoint->last); + + //for (count = 8; count--; *(UDCDR) = '\0'); + + SET_AND_TEST (*(UDCCS2) = UDCCS2_TPC, _udc (UDCCS2) & UDCCS2_TPC, ok2); + _sa1100_bi_dma_run (dmachn_tx, dma_tx_active); + + if (!ok1 || !ok2) { + dbg_tx (0, "Waiting too long to set UDCIMP %02x or TPC: %02x", + *(UDCIMP), *(UDCCS2)); + } + dbg_tx (4, "queued %d bytes using DMA", ep2_endpoint->last); + } else { + + // Entire packet is small enough to fit in FIFO, stuff it + // in directly and forget DMA. + + unsigned char *dmabuf = (unsigned char *) dma_tx_curpos; + int count = ep2_endpoint->last; + + SET_AND_TEST (*(UDCCS2) = UDCCS2_TPC, _udc (UDCCS2) & UDCCS2_TPC, ok2); + for (; count--; *(UDCDR) = *dmabuf++); + + dbg_tx (4, "queued %d bytes directly to FIFO", ep2_endpoint->last); + } + } +} + +/* Clock Tick Debug support ****************************************************************** */ + + +#define RETRYTIME 10 +#if defined(CONFIG_USBD_STALL_TIMEOUT) + // If an URB waits more than... +#define USBD_STALL_TIMEOUT_SECONDS CONFIG_USBD_STALL_TIMEOUT +#else +#define USBD_STALL_TIMEOUT_SECONDS 0 // Stall watchdog hobbled +#endif + +#if defined(CONFIG_USBD_STALL_DISCONNECT_DURATION) +#define USBD_STALL_DISCONNECT_DURATION CONFIG_USBD_STALL_DISCONNECT_DURATION +#else +#define USBD_STALL_DISCONNECT_DURATION 2 // seconds +#endif + +char hexdigit (int hex) +{ + hex &= 0xf; + return (hex < 0xa) ? ('0' + hex) : ('a' + hex - 0xa); +} + +int hex2buf (char *buf, char *str, int num) +{ + char *cp = buf; + while (*str) { + *cp++ = *str++; + } + *cp++ = hexdigit (num >> 4); + *cp++ = hexdigit (num & 0xf); + *cp++ = ' '; + return (cp - buf); +} + +extern int send_length; +extern int send_todo; +extern int sent_last; + +static void show_info (void) +{ + char buf[100]; + char *cp; + volatile short int gpdr; +/* + if (udc_interrupts_last == udc_interrupts) { + udc_irq_enable(); + } +*/ + if (udc_interrupts != udc_interrupts_last) { + printk (KERN_DEBUG "--------------\n"); + } + // do some work + memset (buf, 0, sizeof (buf)); + cp = buf; + cp += hex2buf (cp, "CCR:", *(UDCCR)); + cp += hex2buf (cp, "CSR:", *(UDCSR)); + cp += hex2buf (cp, "CAR:", *(UDCAR)); + cp += hex2buf (cp, "CS0:", *(UDCCS0)); + cp += hex2buf (cp, "CS1:", *(UDCCS1)); + cp += hex2buf (cp, "CS2:", *(UDCCS2)); + cp += hex2buf (cp, "IMP:", *(UDCIMP)); + cp += hex2buf (cp, "OMP:", *(UDCOMP)); + cp += hex2buf (cp, "WC:", *(UDCWC)); + cp += hex2buf (cp, "SR:", *(UDCSR)); + + gpdr = GPDR; + printk (KERN_DEBUG + "[%u] %s int:%2d ep0:%d rx:%d tx:%d sus:%d res:%d er:%d re:%d e1:%d e2:%d tpe:%d tur:%d sst:%d fst:%d tck:%d fix:%d\n", + udc_interrupts, buf, udc_interrupts - udc_interrupts_last, + ep0_interrupts - ep0_interrupts_last, + rx_interrupts - rx_interrupts_last, + tx_interrupts - tx_interrupts_last, + sus_interrupts, res_interrupts, + udc_address_errors, + udc_rpe_errors, + udc_ep1_errors, + udc_ep2_errors, + udc_ep2_tpe, udc_ep2_tur, udc_ep2_sst, udc_ep2_fst, udc_ticks, udc_fixed); + udc_interrupts_last = udc_interrupts; + ep0_interrupts_last = ep0_interrupts; + rx_interrupts_last = rx_interrupts; + tx_interrupts_last = tx_interrupts; +} + +void udc_regs (void) +{ + if (_udc (UDCAR) != usb_address) { + printk (KERN_DEBUG "ADDRESS ERROR DETECTED\n"); + udc_address_errors++; + } + *(UDCAR) = usb_address; + + show_info (); +} + diff -uNr linux.org/drivers/usb/device/bi/sa1100.h linux/drivers/usb/device/bi/sa1100.h --- linux.org/drivers/usb/device/bi/sa1100.h Thu Jan 1 08:00:00 1970 +++ linux/drivers/usb/device/bi/sa1100.h Thu May 15 15:54:48 2003 @@ -0,0 +1,127 @@ +/* + * sa1100_bi/udc.h + * + * Copyright (c) 2000, 2001, 2002 Lineo + * Copyright (c) 2001 Hewlett Packard + * + * By: + * Stuart Lynne , + * Tom Rushworth , + * Bruce Balden + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + * + */ + + +#define EP0_PACKETSIZE 0x8 + +#define UDC_MAX_ENDPOINTS 3 + +#define UDC_NAME "Intel SA-1110 USBD" + +#define MAX_DEVICES 1 + +#define EP1_RX_LOGICAL 1 +#define EP2_TX_LOGICAL 2 + +#define EP1_RX_PHYSICAL 0 +#define EP2_TX_PHYSICAL 1 + +#if defined(CONFIG_SA1100_ASSABET) || defined(CONFIG_SA1100_BITSY) || defined(CONFIG_SA1100_H3XXX) || defined(CONFIG_SA1100_COLLIE) +#define CONFIG_SA1100_USBCABLE_GPIO 23 +#define CONFIG_SA1100_USBCABLE_ACTIVE_HIGH 1 +#undef CONFIG_SA1100_CONNECT_GPIO +#undef CONFIG_SA1100_CONNECT_ACTIVE_HIGH +#endif + +#if defined(CONFIG_SA1110_CALYPSO) +#define CONFIG_SA1100_USBCABLE_GPIO 12 +#undef CONFIG_SA1100_USBCABLE_ACTIVE_HIGH +#define CONFIG_SA1100_CONNECT_GPIO 27 +#define CONFIG_SA1100_CONNECT_ACTIVE_HIGH 1 +#endif + +int ep0_reset (void); + +void ep0_int_hndlr (unsigned int); +void ep1_int_hndlr (unsigned int); +void ep2_int_hndlr (unsigned int, int); + +int ep1_restart (void); + +void ep1_reset (void); +void ep2_reset (void); + +int ep0_enable (struct usb_device_instance *device, struct usb_endpoint_instance *endpoint); +void ep1_enable (struct usb_device_instance *, struct usb_endpoint_instance *, int); +int ep2_enable (struct usb_device_instance *device, struct usb_endpoint_instance *, int); + +void ep0_disable (void); +void ep1_disable (void); +void ep2_disable (void); + +void ep2_send (void); + +extern unsigned int udc_interrupts; +extern unsigned int ep0_interrupts; +extern unsigned int tx_interrupts; +extern unsigned int rx_interrupts; +extern unsigned int udc_address_errors; +extern unsigned int udc_rpe_errors; +extern unsigned int udc_fcs_errors; +extern unsigned int udc_ep1_errors; +extern unsigned int udc_ep2_errors; +extern unsigned int udc_ep2_tpe; +extern unsigned int udc_ep2_tur; +extern unsigned int udc_ep2_sst; +extern unsigned int udc_ep2_fst; + +#define SET_AND_TEST(s,t,c) for (c=20; ((s), (t)) && c--;udelay(0)) + +#define IOIOIO(reg,val,set,test,rc) do { SET_AND_TEST(set,test,rc); } while(0) + +#define _DDAR(Nb) (0xB0000000 + (Nb)*DMASp) + +// Ser0UDCCR +#define UDCCR ((volatile unsigned int *)io_p2v(0x80000000)) +#define UDCCR_INTS (UDCCR_EIM | UDCCR_RIM | UDCCR_TIM | UDCCR_SRM | UDCCR_REM) +#define UDCCR_ERR29 0x80 // enable ERRATA 29 fix for B5 stepping + +#define UDCAR ((volatile unsigned int *)io_p2v(0x80000004)) +#define UDCOMP ((volatile unsigned int *)io_p2v(0x80000008)) +#define UDCIMP ((volatile unsigned int *)io_p2v(0x8000000C)) + +// Ser0UDCCS0 +#define UDCCS0 ((volatile unsigned int *)io_p2v(0x80000010)) + +#define UDCD0 ((volatile unsigned int *)io_p2v(0x8000001c)) +#define UDCDR ((volatile unsigned int *)io_p2v(0x80000028)) +#define UDCWC ((volatile unsigned int *)io_p2v(0x80000020)) +#define UDCSR ((volatile unsigned int *)io_p2v(0x80000030)) + +// Ser0UDCCS1 +#define UDCCS1 ((volatile unsigned int *)io_p2v(0x80000014)) + +// Ser0UDCCS2 +#define UDCCS2 ((volatile unsigned int *)io_p2v(0x80000018)) + +#define UDCCR ((volatile unsigned int *)io_p2v(0x80000000)) +#define UDCAR ((volatile unsigned int *)io_p2v(0x80000004)) + +#define SA1100_IRQ(x) (0 + (x)) +#define IRQ_GPIO_11_27(x) (32 + (x) - 11) +#define SA1100_GPIO_TO_IRQ(i) (((i) < 11) ? SA1100_IRQ(i) : IRQ_GPIO_11_27(i)) + diff -uNr linux.org/drivers/usb/device/bi/sl11-info.h linux/drivers/usb/device/bi/sl11-info.h --- linux.org/drivers/usb/device/bi/sl11-info.h Thu Jan 1 08:00:00 1970 +++ linux/drivers/usb/device/bi/sl11-info.h Thu May 15 15:54:48 2003 @@ -0,0 +1,222 @@ +/* + * sl11_bi/sl11.h + * + * Copyright (c) 2000, 2001, 2002 Lineo + * + * By: + * Stuart Lynne , + * Tom Rushworth , + * Bruce Balden + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + * + */ + +#define SL11_ADDR 0x390 +#define SL11_ADDR_SIZE 4 + +#define SL11_IRQ 3 + +#define PC_ADDR 390h +#define outportb _outp +#define inportb _inp + +/*------------------------------------------------------------------------- + * EP0 use for configuration and Vendor Specific command interface + *------------------------------------------------------------------------- + */ +#define EP0Buf 0x40 /* SL11 memory start at 0x40 */ +#define EP0Len 0x40 /* Length of config buffer EP0Buf */ +#define EP1Buf 0x60 +#define EP1Len 0x40 + +/* + * We have three 64 byte buffers. The first buffer is shared by + * the control endpoint, all Bulk IN endpoints and Interrupt endpoints. + * + * The other two buffers are used as ping pong buffers for a single + * OUT bulk endpoint. + * + * TODO Allow an IN bulk endpoint to borrow one of the ping pong + * buffers so it can setup ping pong send buffers. This would result + * in the OUT bulk endpoint only having a single buffer. But if + * both IN and OUT are in use at the same time then there should + * be sufficent time for a single OUT buffer to be emptied in time. + * + * TODO Allow two IN bulk endpoints by setting them up as A-B and B-A + * such that each can operate. + */ + +#define EP_S_BUF 0x40 // shared buffer, Bulk IN, Interrupt, Control + +#define EP_A_BUF 0x80 // ping pong A - OUT +#define EP_B_BUF 0xc0 // ping pong B - OUT + + +/*------------------------------------------------------------------------- + * SL11 memory from 80h-ffh use as ping-pong buffer for endpoint1-endpoint3 + * These buffers are share for endpoint 1-endpoint 3. + * For DMA: endpoint3 will be used + *------------------------------------------------------------------------- + */ +#define uBufA 0x80 /* buffer A address for DATA0 */ +#define uBufB 0xc0 /* buffer B address for DATA1 */ +#define uXferLen 0x40 /* xfer length */ +#define sMemSize 0xc0 /* Total SL11 memory size */ + +/*------------------------------------------------------------------------- + * SL11 Register Control memory map + *------------------------------------------------------------------------- + */ +#define CtrlReg 0x05 +#define IntEna 0x06 +#define USBAdd 0x07 + +#define IntStatus 0x0d +#define DATASet 0x0e + +#define SOFLow 0x15 +#define SOFHigh 0x16 + +#define sDMACntLow 0x35 +#define sDMACntHigh 0x36 + +#define IntMask 0x57 /* Reset|DMA|EP0|EP2|EP1 for IntEna */ +#define HostMask 0x47 /* Host request command for IntStatus */ +#define ReadMask 0xd7 /* Read mask interrupt for IntStatus */ + +#define EP0_Control 0x00 + +#define EP1_Control 0x10 +#define EP2_Control 0x20 +#define EP3_Control 0x30 + +#define EPN_A 0x00 +#define EPN_B 0x08 + +#define EPN_Control 0x00 +#define EPN_Address 0x01 +#define EPN_XferLen 0x02 +#define EPN_Status 0x03 +#define EPN_Counter 0x04 + +/* + * EP control register masks + */ +#define EPN_CTRL_ARM 0x01 +#define EPN_CTRL_ENABLE 0x02 + +#define EPN_CTRL_DIRECTION 0x04 +#define EPN_CTRL_DIRECTION_IN 0x04 +#define EPN_CTRL_DIRECTION_OUT 0x00 + +#define EPN_CTRL_NEXTDATA 0x08 +#define EPN_CTRL_ISO 0x10 +#define EPN_CTRL_SENDSTALL 0x20 +#define EPN_CTRL_SEQUENCE 0x40 +#define EPN_CTRL_RESERVERED 0x80 + +/* + * EP Packet status masks + */ +#define EPN_PSTS_ACK 0x01 +#define EPN_PSTS_ERROR 0x02 +#define EPN_PSTS_TIMEOUT 0x04 +#define EPN_PSTS_SEQUENCE 0x08 +#define EPN_PSTS_SETUP 0x10 +#define EPN_PSTS_OVERFLOW 0x20 +#define EPN_PSTS_NAK 0x40 +#define EPN_PSTS_STALL 0x80 + + +/* + * Control Register masks + */ +#define CTRL_USB_ENABLE 0x01 +#define CTRL_DMA_ENABLE 0x02 +#define CTRL_USB_RESET 0x08 + +/* + * Interrupt enable register mask + */ +#define INT_EP0_DONE 0x01 +#define INT_EP1_DONE 0x02 +#define INT_EP2_DONE 0x04 +#define INT_EP3_DONE 0x08 +#define INT_DMA_DONE 0x10 +#define INT_SOF_RECEIVED 0x20 +#define INT_USB_RESET 0x40 +#define INT_DMA_STATUS 0x80 + +/* + * Current Data Set Register masks + */ +#define CDS_EP0 0x01 +#define CDS_EP1 0x02 +#define CDS_EP2 0x04 +#define CDS_EP3 0x08 +#define CDS_RESERVED 0x10 + + +#if 0 +/** + * sl11write_byte - write a byte to the sl11 + * @a: sl11 address + * @b: byte to write + */ +__inline__ void sl11write_byte (unsigned char, unsigned char); + +/** + * sl11write_buffer - write a buffer to the sl11 using auto-increment mode + * @a: sl11 address + * @b: pointer to buffer to write + * @size: number of bytes to write + */ +__inline__ void sl11write_buffer (unsigned char, unsigned char *, unsigned char); + +/** + * sl11read_byte - read a byte from the sl11 and return + * @a: sl11 address + */ +__inline__ unsigned char sl11read_byte (unsigned char); + +/** + * sl11read_buffer - fill a buffer from the sl11 using auto-increment mode + * @a: sl11 address + * @b: pointer to buffer to fill + * @size: number of bytes to read + */ +__inline__ void sl11read_buffer (unsigned char, unsigned char *, unsigned char); + + +/** + * sl11write_epn - write a byte to a sl11 endpoint register + * @a: endpoint + * @b: A or B + * @c: endpoint register + * @d: byte to write + */ +__inline__ void sl11write_epn (unsigned char, unsigned char, unsigned char); + +/** + * sl11read_epn - read a byte from a sl11 endpoint register + * @a: endpoint + * @b: A or B + * @c: endpoint register + * @d: byte to write + */ +__inline__ void sl11read_epn (unsigned char, unsigned char); + +#endif diff -uNr linux.org/drivers/usb/device/bi/sl11.c linux/drivers/usb/device/bi/sl11.c --- linux.org/drivers/usb/device/bi/sl11.c Thu Jan 1 08:00:00 1970 +++ linux/drivers/usb/device/bi/sl11.c Thu May 15 15:54:48 2003 @@ -0,0 +1,1387 @@ +/* + * sl11_bi/udc.c + * + * Copyright (c) 2000, 2001, 2002 Lineo + * + * By: + * Stuart Lynne , + * Tom Rushworth , + * Bruce Balden + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + * + */ + + +/*****************************************************************************/ + +#include +#include + +#include "../usbd-export.h" +#include "../usbd-build.h" +#include "../usbd-module.h" + +MODULE_AUTHOR ("sl@lineo.com, tbr@lineo.com"); +MODULE_DESCRIPTION ("USB Device SL11 Bus Interface"); + +USBD_MODULE_INFO ("sl11_bi 0.1-alpha"); + +#include +#include +#include +#include +#include + +#include +#include + +#include + +#include +#include + +#include + +#include +#include +#include + +#include "../usbd.h" +#include "../usbd-func.h" +#include "../usbd-bus.h" +#include "../usbd-inline.h" +#include "usbd-bi.h" + +#include "sl11.h" +#include "sl11-info.h" + +#define MIN(a,b) ((a) < (b) ? (a) : (b)) +#define MAX(a,b) ((a) > (b) ? (a) : (b)) + +/* + * ep_address - endpoint buffer addresses + * + * 0-3 epNa + * 4-7 epNb + */ +static unsigned char ep_address[UDC_MAX_ENDPOINTS * 2] = { + EP_S_BUF, EP_S_BUF, EP_S_BUF, EP_S_BUF, + EP_S_BUF, EP_S_BUF, EP_S_BUF, EP_S_BUF +}; + +/* + * ep_register - endpoint register addresses + * + * 0-3 epNa + * 4-7 epNb + */ +static unsigned char ep_register[UDC_MAX_ENDPOINTS * 2] = { + EP0_Control, EP1_Control, EP2_Control, EP3_Control, + EP0_Control + 8, EP1_Control + 8, EP2_Control + 8, EP3_Control + 8 +}; + +static struct usb_device_instance *udc_device; // required for the interrupt handler + +/* + * ep_sequence - current sequence number for each endpoint + */ +static unsigned char ep_sequence[UDC_MAX_ENDPOINTS] = { + 0, 0, 0, 0 +}; + +/* + * ep_next - next ping pong buffer + */ +static unsigned char ep_next[UDC_MAX_ENDPOINTS] = { + 0, 0, 0, 0 +}; + +/* + * ep_int_mask - interrupt status register endpoint bit masks + */ +static unsigned char ep_int_mask[UDC_MAX_ENDPOINTS] = { + INT_EP0_DONE, INT_EP1_DONE, INT_EP2_DONE, INT_EP3_DONE +}; + +/* + * ep_endpoints - map physical endpoints to logical endpoints + */ +static struct usb_endpoint_instance *ep_endpoints[UDC_MAX_ENDPOINTS]; + +// static struct urb ep0_urb; +static struct urb *ep0_urb; +static unsigned char usb_address; + +static int udc_saw_sof; +static int udc_suspended; +static int udc_addressed; +static struct tq_struct sl11_tq; + +extern unsigned int udc_interrupts; +unsigned int udc_ticks; + +/* ********************************************************************************************* */ + +#ifdef CONFIG_X86 +/** + * sl11write_byte - write a byte to the sl11 + * @a: sl11 address + * @b: byte to write + */ +static __inline__ void sl11write_byte (unsigned char a, unsigned char b) +{ + outb (a, UDC_ADDR); + outb (b, UDC_ADDR + 1); +} + +static __inline__ void _sl11write_byte (unsigned char a, unsigned char b) +{ + printk (KERN_DEBUG "sl11write_byte: Reg: %02x Val: %02x\n", a, b); + outb (a, UDC_ADDR); + outb (b, UDC_ADDR + 1); +} + + +/** + * sl11write_buffer - write a buffer to the sl11 using auto-increment mode + * @a: sl11 address + * @b: pointer to buffer to write + * @size: number of bytes to write + */ +static __inline__ void sl11write_buffer (unsigned char a, unsigned char *b, unsigned char size) +{ + outb (a, UDC_ADDR); + while (size--) { + outb (*b++, UDC_ADDR + 1); + } +} + + +/** + * sl11read_byte - read a byte from the sl11 and return + * @a: sl11 address + */ +static __inline__ unsigned char sl11read_byte (unsigned char a) +{ + outb (a, UDC_ADDR); + return inb (UDC_ADDR + 1); +} + + +/** + * sl11read_buffer - fill a buffer from the sl11 using auto-increment mode + * @a: sl11 address + * @b: pointer to buffer to fill + * @size: number of bytes to read + */ +static __inline__ void sl11read_buffer (unsigned char a, unsigned char *b, unsigned char size) +{ + outb (a, UDC_ADDR); + while (size--) { + *b++ = inb (UDC_ADDR + 1); + } +} + +/** + * sl11clear - clear sl11 memory + * @a: sl11 address + * @size: bytes to clear + */ +static void sl11clear (unsigned char a, unsigned char size) +{ + outb (a, UDC_ADDR); + while (size--) { + outb (0, UDC_ADDR + 1); + } +} + + +#else +#abort SL11 memory mapped IO not implemented +#endif + +/* ********************************************************************************************* */ + + +/** + * sl11write_epn - write a byte to a sl11 endpoint register + * @a: endpoint + * @b: endpoint register + * @d: byte to write + */ +static __inline__ void sl11write_epn (unsigned char ep, unsigned char b, unsigned char c) +{ + sl11write_byte (ep_register[ep] + b, c); +} + +static __inline__ void _sl11write_epn (unsigned char ep, unsigned char b, unsigned char c) +{ + printk (KERN_DEBUG "sl11write_epn: Reg: %02x Val: %02x\n", ep_register[ep] + b, c); + _sl11write_byte (ep_register[ep] + b, c); +} + + +/** + * sl11read_epn - read a byte from a sl11 endpoint register + * @a: endpoint + * @b: endpoint register + * @d: byte to write + */ +__inline__ unsigned char sl11read_epn (unsigned char ep, unsigned char b) +{ + return sl11read_byte (ep_register[ep] + b); +} + +/* ********************************************************************************************* */ + +/** + * sl11_dump + */ +static void sl11_dump (unsigned char a, unsigned char size) +{ + int i; + for (i = 0; i < size; i++) { + if ((i % 8) == 0) { + printk ("\n[%02x]: ", a + i); + } + printk ("%02x ", sl11read_byte (a + i)); + } + printk ("\n"); +} + +static void sl11_dump_epn (unsigned char ep, char *m) +{ + printk ("ep[%d]: Ctl:%02x Adr:%02x Xfr:%02x Sts:%02x Cnt:%02x Seq:%d%s", ep, + sl11read_epn (ep, EPN_Control), + sl11read_epn (ep, EPN_Address), + sl11read_epn (ep, EPN_XferLen), + sl11read_epn (ep, EPN_Status), + sl11read_epn (ep, EPN_Counter), ep_sequence[ep & 0x3], m); +} + + +/** + * sl11_probe - initialize + * + * 0. Issue a reset to CtrlReg + * + * 1. After reset we typically see: + * + * IntStatus 0x40 or 0x60 + * DATASet 0x10 + * + * 2. We test that IntStatus can be changed to 0x00 by writing 0xff to it. + * + * 3. If successful clear all chip memory. + * + **/ +static int sl11_probe (void) +{ + sl11_dump (0, 64); + printk (KERN_DEBUG "sl11_probe: start\n"); + + // reset + sl11write_byte (CtrlReg, CTRL_USB_RESET); + udelay (100); // XXX may not be needed + sl11write_byte (CtrlReg, 0); + + sl11_dump (0, 64); + printk (KERN_DEBUG "sl11_probe: RESET\n"); + + /* + * XXX + * + * If device is present DATASet&CDS_RESERVED will be true. + * + * After reset we will see IntStatus & INT_USB_RESET if cable is plugged in. + * + */ + + if (((sl11read_byte (IntStatus) & INT_USB_RESET) != INT_USB_RESET) + || ((sl11read_byte (DATASet) & 0x10) != 0x10)) { + sl11_dump (0, 64); + printk (KERN_DEBUG "sl11_probe: cannot see " UDC_NAME ", failed initial probe\n"); + //udc_regs(); + //return -EINVAL; + } + // reset interrupt status + sl11write_byte (IntStatus, 0xff); + if ((sl11read_byte (IntStatus) != 0x00)) { + sl11_dump (0, 64); + printk (KERN_DEBUG "sl11_probe: cannot see " UDC_NAME + ", failed interrupt status reset\n"); + udc_regs (); + return -EINVAL; + } + // success! + + // clear all memory + sl11clear (0, 255); + + + printk (KERN_DEBUG "sl11_probe: found and setup\n"); + + //sl11_dump(0, 64); + //udc_regs(); + + return 0; +} + +/* ********************************************************************************************* */ + +/** + * sl11send_data - send packet via endpoint + * @ep: logical endpoint number + * @bp: pointer to data + * @size: bytes to write + */ +static void __inline__ sl11send_data (unsigned char ep, unsigned char *bp, unsigned char size) +{ + + // copy data from buffer to chip + if (bp && size) { + sl11write_buffer (ep_address[ep], bp, size); + } + + sl11write_epn (ep, EPN_XferLen, size); + + // arm + sl11write_epn (ep, EPN_Control, + EPN_CTRL_ARM | EPN_CTRL_ENABLE | EPN_CTRL_DIRECTION_IN | + (ep_sequence[ep] ? EPN_CTRL_SEQUENCE : 0) + ); +} + + +/** + * s111send_done + * @ep + */ +static void __inline__ sl11send_done (unsigned ep) +{ + // disarm + sl11write_epn (ep, EPN_Control, EPN_CTRL_ENABLE | EPN_CTRL_DIRECTION_IN); + + // flip sequence + ep_sequence[ep] = (~ep_sequence[ep]) & 0x1; +} + +/* ********************************************************************************************* */ + +/** + * s111read_init + * @ep: endpoint + */ +static void sl11read_init (unsigned char ep, unsigned char size) +{ + // setup packet size + sl11write_epn (ep, EPN_XferLen, size); + + // arm + sl11write_epn (ep, EPN_Control, EPN_CTRL_ARM | EPN_CTRL_ENABLE | EPN_CTRL_DIRECTION_OUT); + +} + + +/** + * sl11read_done + * @ep: endpoint + */ +static void sl11read_done (unsigned char ep, unsigned char *bp, unsigned char size) +{ + // disarm + sl11write_epn (ep, EPN_Control, EPN_CTRL_ENABLE | EPN_CTRL_DIRECTION_OUT); + + // copy data from chip to buffer + sl11read_buffer (ep_address[ep], bp, size); + +} + +/* ********************************************************************************************* */ + +/** + * sl11_out - process rcv interrupt + * + * Queue received data. + */ +static void __inline__ sl11_out (unsigned int ep, struct usb_endpoint_instance *endpoint) +{ + int len; + int cnt; + int error = 0; + int adr; + int i; + unsigned char status; + unsigned char ctl; + unsigned int cep; + + + // loop alternating between A-B register sets looking for ACK in packet status + // + for (i = 0; i < 8; i++) { + + cep = ep + (ep_next[ep] ? 4 : 0); + ep_next[ep] = ~ep_next[ep] & 0x1; + + status = sl11read_epn (cep, EPN_Status); + + //printk(KERN_DEBUG"[%d] out[%d]: seq: %d next: %d sts: %02x\n", udc_interrupts, ep, ep_sequence[ep], ep_next[ep], status); + + if (!(status & EPN_PSTS_ACK)) { + continue; //return; + } + // check packet status bits for errors + ctl = sl11read_epn (cep, EPN_Control); + len = sl11read_epn (cep, EPN_XferLen); + cnt = sl11read_epn (cep, EPN_Counter); + adr = sl11read_epn (cep, EPN_Address); + + //printk(KERN_DEBUG"[%d] out[%d]: sts: %02x len: %d cnt: %d adr: %02x seq: %02x bytes: %0d\n", + // udc_interrupts, cep, status, len, cnt, adr, ep_sequence[ep], len - cnt); + + //sl11_dump_epn(ep, " "); + //sl11_dump_epn(ep + 4, "\n"); + + len -= cnt; + + if (status & + (EPN_PSTS_ERROR | EPN_PSTS_TIMEOUT | EPN_PSTS_SETUP | EPN_PSTS_OVERFLOW)) { + printk (KERN_DEBUG "sl11_out[%d]: ERROR\n", cep); + error = 1; + } + + if (((status & EPN_PSTS_SEQUENCE) && ep_sequence[ep]) || + (!(status & EPN_PSTS_SEQUENCE) && !ep_sequence[ep])) { + ep_sequence[ep] = (~ep_sequence[ep]) & 0x1; + } else { + // sequence error + printk (KERN_DEBUG "sl11_out[%d]: SEQ\n", cep); + error = 1; + } + + if (endpoint->rcv_urb && len) { + sl11read_buffer (adr, + endpoint->rcv_urb->buffer + + endpoint->rcv_urb->actual_length, len); + } + // re-arm + sl11write_epn (cep, EPN_Status, 0); + sl11write_epn (cep, EPN_Counter, 0); + sl11write_epn (cep, EPN_XferLen, endpoint->rcv_packetSize); + //sl11_dump_epn(ep, "\n"); + sl11write_epn (cep, EPN_Control, ctl | EPN_CTRL_ARM); + + if (endpoint->endpoint_address) { + //printk(KERN_DEBUG"sl11_out: receive len: %d\n", len); + usbd_rcv_complete_irq (endpoint, len, 0); + } else { + printk (KERN_DEBUG "sl11_out: cannot receive no endpoint address len: %d\n", + len); + + } + } +} + +/* ********************************************************************************************* */ + +/** + * sl11_start - start transmit + * @ep: + */ +static void __inline__ sl11_start (unsigned int ep, struct usb_endpoint_instance *endpoint, + int restart) +{ + //printk(KERN_DEBUG"sl11_start: tx_urb: %p sent: %d\n", endpoint->tx_urb, endpoint->sent); + + if (endpoint->tx_urb) { + struct urb *urb = endpoint->tx_urb; + + //printk(KERN_DEBUG"sl11_start: length: %d\n", endpoint->tx_urb->actual_length); + + if ((urb->actual_length - endpoint->sent) > 0) { + endpoint->last = + MIN (urb->actual_length - endpoint->sent, endpoint->tx_packetSize); + sl11send_data (ep, urb->buffer + endpoint->sent, endpoint->last); + } else { + // XXX ZLP + endpoint->last = 0; + sl11send_data (ep, urb->buffer + endpoint->sent, 0); + } + } +} + + +/** + * sl11_in - process tx interrupt + * @ep: + * @endpoint: + * + * Determine status of last data sent, queue new data. + */ +static __inline__ void sl11_in (unsigned int ep, struct usb_endpoint_instance *endpoint) +{ + int restart = 0; + unsigned char status; + + restart = 0; + + //printk(KERN_DEBUG"in[%d]: seq: %d next: %d\n", ep, ep_sequence[ep], ep_next[ep]); + + // check packet status bits for errors + status = sl11read_epn (ep, EPN_Status); + + if (status & (EPN_PSTS_ERROR | EPN_PSTS_TIMEOUT | EPN_PSTS_SETUP | EPN_PSTS_OVERFLOW)) { + printk (KERN_DEBUG "sl11_in[%d]: ERROR status: %0x\n", ep, status); + restart = 1; + } else { + // flip sequence + ep_sequence[ep] = (~ep_sequence[ep]) & 0x1; + } + + usbd_tx_complete_irq (endpoint, restart); + sl11_start (ep, endpoint, restart); + +} + + +/* ********************************************************************************************* */ + +static void sl11_ep0_setaddress (struct usb_endpoint_instance *endpoint) +{ + //printk(KERN_DEBUG"sl11_ep0: finished set address: %d\n", usb_address); + sl11write_byte (USBAdd, usb_address); + udc_addressed = usb_address; + usb_address = 0; + // send ack + sl11write_epn (0, EPN_XferLen, endpoint->rcv_packetSize); + sl11write_epn (0, EPN_Control, EPN_CTRL_ENABLE | EPN_CTRL_ARM); + printk ("\n"); + sl11_dump_epn (0, "\n"); +} + +static void sl11_ep0_ending (struct usb_endpoint_instance *endpoint) +{ + + + //ep_sequence[0] = ~ep_sequence[0]; + + if (endpoint->tx_urb) { + //printk(KERN_DEBUG"sl11_ep0: continue IN actual: %d sent: %d last: %d seq: %d\n", + // endpoint->tx_urb->actual_length, endpoint->sent, endpoint->last, ep_sequence[0]); + sl11_in (0, endpoint); + } + + if (endpoint->tx_urb) { + //printk(KERN_DEBUG"sl11_ep0: still sending\n"); + return; + } + //printk(KERN_DEBUG"sl11_ep0: finished sending\n"); + + // send ack + sl11write_epn (0, EPN_XferLen, endpoint->rcv_packetSize); + sl11write_epn (0, EPN_Control, EPN_CTRL_ENABLE | EPN_CTRL_ARM); +} + +static void sl11_ep0_receiving (struct usb_endpoint_instance *endpoint) +{ + + // XXX check sequence + //ep_sequence[ep] = ~ep_sequence[ep]; + + // are we trying to read data? + if (endpoint->rcv_urb) { + + // ok, handle incoming data + sl11_out (0, endpoint); + + // more to send? + if (endpoint->rcv_urb) { + return; + } + // send ack + sl11write_epn (0, EPN_XferLen, 0); + sl11write_epn (0, EPN_Control, EPN_CTRL_ENABLE | EPN_CTRL_ARM); + return; + } + //printk(KERN_DEBUG"sl11_ep0: received ACK\n"); + + sl11write_epn (0, EPN_XferLen, endpoint->rcv_packetSize); + sl11write_epn (0, EPN_Control, EPN_CTRL_ENABLE | EPN_CTRL_ARM); + + // stall if we where not waiting for data + //udc_stall_ep(0); +} + +/** + * sl11_ep0 - process an endpoint 0 interrupt + * + * Read and process a setup packet + */ +static void sl11_ep0 (struct usb_endpoint_instance *endpoint) +{ + unsigned char control; + unsigned char status; + unsigned char *cp; + + + //sl11_dump_epn(0, "\n"); + + // delayed set address, cannot set until we have received IN + if (usb_address) { + sl11_ep0_setaddress (endpoint); + return; + } + // are we sending a reply + control = sl11read_epn (0, EPN_Control); + + if (control & EPN_CTRL_DIRECTION_IN) { + sl11_ep0_ending (endpoint); + return; + } + + status = sl11read_epn (0, EPN_Status); + + // is the host sending data? + if (!(status & EPN_PSTS_SETUP)) { + sl11_ep0_receiving (endpoint); + return; + } + + // check if host changed it's mind + if (endpoint->tx_urb) { + endpoint->tx_urb = NULL; + endpoint->last = endpoint->sent = 0; + printk (KERN_DEBUG "sl11_ep0: host cancelled tx\n"); + } + if (endpoint->rcv_urb) { + endpoint->rcv_urb = NULL; + ep0_urb->actual_length = 0; + printk (KERN_DEBUG "sl11_ep0: host cancelled rcv\n"); + } + // read setup packet + //len = sl11read_epn(0, EPN_XferLen); + //printk(KERN_DEBUG"sl11_ep0: reading setup\n"); + + sl11read_buffer (ep_address[0], (unsigned char *) &ep0_urb->device_request, 8); + + //sl11_dump(0, 255); + + cp = (unsigned char *) &ep0_urb->device_request; + //printk(KERN_DEBUG"setup: %02x %02x %02x %02x %02x %02x %02x %02x\n", + // cp[0], cp[1], cp[2], cp[3], cp[4], cp[5], cp[6], cp[7]); + + // process setup packet + if (usbd_recv_setup (ep0_urb)) { + printk (KERN_DEBUG "sl11_ep0: setup failed\n"); + //sl11write_epn(0, ) + //udc_stall_ep(0); + return; + } + //sl11write_epn(0, EPN_XferLen, 0); + //sl11write_epn(0, EPN_Control, EPN_CTRL_SEQUENCE | EPN_CTRL_DIRECTION_IN | EPN_CTRL_ENABLE | EPN_CTRL_ARM); + //return; // XXX + + // check data direction + if ((ep0_urb->device_request.bmRequestType & USB_REQ_DIRECTION_MASK) == USB_REQ_HOST2DEVICE) { + + + // should we setup to receive data + if (le16_to_cpu (ep0_urb->device_request.wLength)) { + //printk(KERN_DEBUG"sl11_ep0: read data %d\n",le16_to_cpu(ep0_urb->device_request.wLength)); + endpoint->rcv_urb = ep0_urb; + endpoint->rcv_urb->actual_length = 0; + sl11_out (0, endpoint); + return; + } + //printk(KERN_DEBUG"sl11_ep0: send ack %d\n",le16_to_cpu(ep0_urb->device_request.wLength)); + + // should be finished, send ack + sl11write_epn (0, EPN_XferLen, 0); + sl11write_epn (0, EPN_Control, + EPN_CTRL_SEQUENCE | EPN_CTRL_DIRECTION_IN | EPN_CTRL_ENABLE | + EPN_CTRL_ARM); + return; + } + // we should be sending data back + //printk(KERN_DEBUG"sl11_ep0: send data: %d\n", le16_to_cpu(ep0_urb->device_request.wLength)); + + // verify that we have non-zero request length + if (!le16_to_cpu (ep0_urb->device_request.wLength)) { + udc_stall_ep (0); + return; + } + // verify that we have non-zero length response + if (!ep0_urb->actual_length) { + udc_stall_ep (0); + return; + } + // start sending + //printk(KERN_DEBUG"sl11_ep0: start sending\n"); + endpoint->tx_urb = ep0_urb; + endpoint->sent = 0; + endpoint->last = 0; + ep_sequence[0] = 1; + sl11_start (0, endpoint, 0); + +} + +/* ********************************************************************************************* */ + +/** + * sl11_int_hndlr - interrupt handler + * + */ +static void sl11_int_hndlr (int irq, void *dev_id, struct pt_regs *regs) +{ + int i; + unsigned char status; + + int ep; + + udc_interrupts++; + + // loop while interrupt status register is non-zero + for (i = 0; (i < 10) && (status = sl11read_byte (IntStatus)); i++) { + + //if ((status & ~INT_SOF_RECEIVED)) { + // printk(KERN_DEBUG"[%d] %02x\n", udc_interrupts, status); + //} + + // Common interrupts - Endpoint done + for (ep = 1; ep < UDC_MAX_ENDPOINTS; ep++) { + struct usb_endpoint_instance *endpoint; + if ((status & ep_int_mask[ep]) && (endpoint = ep_endpoints[ep])) { + + // transmit on other than endpoint zero + if (endpoint->endpoint_address & IN) { + endpoint->tx_interrupts++; + sl11_in (ep, endpoint); + } + // receive on other than endpoint zero + else { + endpoint->rcv_interrupts++; + sl11_out (ep, endpoint); + } + } + } + + // uncommon interrupts + if (status & (INT_USB_RESET | INT_EP0_DONE | INT_DMA_DONE | INT_SOF_RECEIVED)) { + + // Reset + if (status & INT_USB_RESET) { + printk (KERN_DEBUG "sl11_int_hndlr: RESET: %02x\n", status); + udc_suspended = 0; + usbd_device_event (udc_device, DEVICE_RESET, 0); + } + // endpoint zero + if ((status & INT_EP0_DONE)) { + struct usb_endpoint_instance *endpoint; + endpoint = ep_endpoints[0]; + endpoint->ep0_interrupts++; + sl11_ep0 (endpoint); + } + // DMA done + if (status & INT_DMA_DONE) { + printk (KERN_DEBUG "sl11_int_hndlr: DMA: %02x\n", status); + } + // SOF frame + if (status & INT_SOF_RECEIVED) { + udc_saw_sof = 1; + + if (udc_suspended) { + udc_suspended = 0; + usbd_device_event (udc_device, DEVICE_BUS_ACTIVITY, 0); + printk (KERN_DEBUG "sl11_tick: RESUMED\n"); + } + } + } + // clear all available status bits + sl11write_byte (IntStatus, status); + } +} + +/* ********************************************************************************************* */ + +/** + * sl11_tick - clock timer task + * @data: + * + * Run from global clock tick to check if we are suspended. + */ +static void sl11_tick (void *data) +{ + udc_ticks++; + + // is driver active + if (data) { + + // if not suspended check if we should suspend + if (udc_addressed && !udc_suspended) { + + int saw_sof = *((int *) data); + + if (!saw_sof) { + + // XXX + // + usbd_device_event (udc_device, DEVICE_BUS_INACTIVE, 0); + udc_suspended = 1; + printk (KERN_DEBUG "sl11_tick: SUSPENDED\n"); + } + + *(int *) data = 0; + } + // re-queue task + queue_task (&sl11_tq, &tq_timer); + } else { + printk (KERN_DEBUG "sl11_tick: not restarting\n"); + } +} + +/* ********************************************************************************************* */ +/* + * Start of public functions. + */ + + +/** + * udc_start_in_irq - start transmit + * @eendpoint: endpoint instance + * + * Called by bus interface driver to see if we need to start a data transmission. + */ +void udc_start_in_irq (struct usb_endpoint_instance *endpoint) +{ + sl11_start (endpoint->endpoint_address & 0xf, endpoint, 0); +} + + +/** + * udc_stall_ep - stall endpoint + * @ep: physical endpoint + * + * Stall the endpoint. + */ +void udc_stall_ep (unsigned int ep) +{ + unsigned char status; + + if (ep < UDC_MAX_ENDPOINTS) { + status = sl11read_epn (ep, EPN_Control); + sl11write_epn (ep, EPN_Control, status | EPN_CTRL_SENDSTALL); + udelay (10); // XXX + sl11write_epn (ep, EPN_Control, status); + } +} + + +/** + * udc_reset_ep - reset endpoint + * @ep: physical endpoint + * reset the endpoint. + * + * returns : 0 if ok, -1 otherwise + */ +void udc_reset_ep (unsigned int ep) +{ + if (ep < UDC_MAX_ENDPOINTS) { + ep_sequence[ep] = 0; + } + printk (KERN_DEBUG "udc_reset_ep[%d]:\n", ep); + if (!ep) { + if (udc_addressed) { + printk (KERN_DEBUG "udc_reset_ep[%d]: reseting address\n", ep); + usb_address = 0; + udc_addressed = 0; + sl11write_byte (USBAdd, 0); + + // reset UDC + sl11write_byte (CtrlReg, CTRL_USB_RESET); + udelay (100); // XXX may not be needed + sl11write_byte (CtrlReg, 0); + + // enable UDC + sl11write_byte (CtrlReg, CTRL_USB_ENABLE); + + } + // arm EP0 + sl11write_epn (0, EPN_Control, + EPN_CTRL_ARM | EPN_CTRL_ENABLE | EPN_CTRL_DIRECTION_OUT); + + sl11_dump_epn (ep + 0, " "); + sl11_dump_epn (ep + 4, "\n"); + } +} + + +/** +* udc_endpoint_halted - is endpoint halted +* @ep: +* +* Return non-zero if endpoint is halted + */ +int udc_endpoint_halted (unsigned int ep) +{ + return 0; +} + + +/** + * udc_set_address - set the USB address for this device + * @address: + * + * Called from control endpoint function after it decodes a set address setup packet. + */ +void udc_set_address (unsigned char address) +{ + // address cannot be setup until ack received + usb_address = address; +} + +/** + * udc_serial_init - set a serial number if available + */ +static int __init udc_serial_init (struct usb_bus_instance *bus) +{ + return -EINVAL; +} + +/* ********************************************************************************************* */ + +/** + * udc_max_endpoints - max physical endpoints + * + * Return number of physical endpoints. + */ +int udc_max_endpoints (void) +{ + return UDC_MAX_ENDPOINTS; +} + + +/** + * udc_check_ep - check logical endpoint + * @lep: + * + * Return physical endpoint number to use for this logical endpoint or zero if not valid. + */ +int udc_check_ep (int logical_endpoint, int packetsize) +{ + + return (((logical_endpoint & 0xf) >= UDC_MAX_ENDPOINTS) || (packetsize > 64)) ? + 0 : (logical_endpoint & 0xf); +} + + +/** + * udc_set_ep - setup endpoint + * @ep: + * @endpoint: + * + * Associate a physical endpoint with endpoint_instance + */ +void udc_setup_ep (struct usb_device_instance *device, unsigned int ep, + struct usb_endpoint_instance *endpoint) +{ + if (ep < UDC_MAX_ENDPOINTS) { + + ep_endpoints[ep] = endpoint; + + sl11write_epn (ep, EPN_Control, 0); + sl11write_epn (ep, EPN_XferLen, 0); + sl11write_epn (ep, EPN_Status, 0); + sl11write_epn (ep, EPN_Counter, 0); + + ep_address[ep] = EP_S_BUF; + ep_address[ep * 2] = EP_S_BUF; + + // ep0 + if (ep == 0) { + sl11write_epn (ep, EPN_Address, EP_S_BUF); + sl11write_epn (ep, EPN_Control, + EPN_CTRL_ARM | EPN_CTRL_ENABLE | EPN_CTRL_DIRECTION_OUT); + sl11write_epn (ep, EPN_XferLen, endpoint->rcv_packetSize); + sl11write_epn (ep, EPN_Status, 0); + sl11write_epn (ep, EPN_Counter, 0); + } + // IN + else if (endpoint->endpoint_address & 0x80) { + sl11write_epn (ep, EPN_Address, EP_S_BUF); + sl11write_epn (ep, EPN_Control, + EPN_CTRL_ARM | EPN_CTRL_ENABLE | EPN_CTRL_DIRECTION_IN); + } + // OUT + else if (endpoint->endpoint_address) { + usbd_fill_rcv (device, endpoint, 5); + endpoint->rcv_urb = first_urb_detached (&endpoint->rdy); + + ep_sequence[ep] = 0; + + sl11write_epn (ep + 0, EPN_Address, EP_A_BUF); + sl11write_epn (ep + 4, EPN_Address, EP_B_BUF); + + ep_address[ep + 0] = EP_A_BUF; + ep_address[ep + 4] = EP_B_BUF; + + sl11write_epn (ep + 0, EPN_XferLen, endpoint->rcv_packetSize); + sl11write_epn (ep + 4, EPN_XferLen, endpoint->rcv_packetSize); + + sl11write_epn (ep + 0, EPN_Control, + EPN_CTRL_ARM | EPN_CTRL_ENABLE | EPN_CTRL_DIRECTION_OUT); + sl11write_epn (ep + 0, EPN_Control, + EPN_CTRL_ARM | EPN_CTRL_ENABLE | EPN_CTRL_DIRECTION_OUT | + EPN_CTRL_NEXTDATA); + + sl11write_epn (ep + 4, EPN_Control, + EPN_CTRL_ARM | EPN_CTRL_ENABLE | EPN_CTRL_DIRECTION_OUT); + + printk (KERN_DEBUG "udc_setup_ep[%d]: setup OUT\n", ep); + sl11_dump_epn (ep + 0, " "); + sl11_dump_epn (ep + 4, "\n"); + } + } + //sl11_dump(0, 64); +} + +/** + * udc_disable_ep - disable endpoint + * @ep: + * + * Disable specified endpoint + */ +void udc_disable_ep (unsigned int ep) +{ +#if 0 + if (ep < UDC_MAX_ENDPOINTS) { + struct usb_endpoint_instance *endpoint; + + if ((endpoint = ep_endpoints[ep])) { + ep_endpoints[ep] = NULL; + usbd_flush_ep (endpoint); + } + } +#endif +} + +/* ********************************************************************************************* */ + +/** + * udc_connected - is the USB cable connected + * + * Return non-zeron if cable is connected. + */ +int udc_connected (void) +{ + return 1; +} + + +/** + * udc_connect - enable pullup resistor + * + * Turn on the USB connection by enabling the pullup resistor. + */ +void udc_connect (void) +{ + // enable UDC + sl11write_byte (CtrlReg, CTRL_USB_ENABLE); +} + + +/** + * udc_disconnect - disable pullup resistor + * + * Turn off the USB connection by disabling the pullup resistor. + */ +void udc_disconnect (void) +{ + // disable UDC + sl11write_byte (CtrlReg, 0); +} + +/* ********************************************************************************************* */ + +/** + * udc_enable_interrupts - enable interrupts + * + * Switch on UDC interrupts. + * + */ +void udc_all_interrupts (struct usb_device_instance *device) +{ + printk (KERN_DEBUG "udc_enable_interrupts:\n"); + + // set interrupt mask + sl11write_byte (IntEna, + INT_EP0_DONE | INT_EP1_DONE | INT_EP2_DONE | INT_EP3_DONE | + INT_SOF_RECEIVED /*| INT_USB_RESET */ ); + +} + + +/** + * udc_suspended_interrupts - enable suspended interrupts + * + * Switch on only UDC resume interrupt. + * + */ +void udc_suspended_interrupts (struct usb_device_instance *device) +{ + printk (KERN_DEBUG "udc_enable_interrupts:\n"); + + // set interrupt mask + sl11write_byte (IntEna, INT_SOF_RECEIVED | INT_USB_RESET); + +} + + +/** + * udc_disable_interrupts - disable interrupts. + * + * switch off interrupts + */ +void udc_disable_interrupts (struct usb_device_instance *device) +{ + + printk (KERN_DEBUG "udc_disable_interrupts:\n"); + + // reset interrupt mask + sl11write_byte (IntEna, 0); +} + +/* ********************************************************************************************* */ + +/** + * udc_ep0_packetsize - return ep0 packetsize + */ +int udc_ep0_packetsize (void) +{ + return EP0_PACKETSIZE; +} + +/** + * udc_enable - enable the UDC + * + * Switch on the UDC + */ +void udc_enable (struct usb_device_instance *device) +{ + + printk (KERN_DEBUG "\n"); + printk (KERN_DEBUG "udc_enable: device: %p\n", device); + + // save the device structure pointer + udc_device = device; + + // ep0 urb + if (!ep0_urb) { + if (!(ep0_urb = usbd_alloc_urb (device, device->function_instance_array, 0, 512))) { + printk (KERN_ERR "udc_enable: usbd_alloc_urb failed\n"); + } + } else { + printk (KERN_ERR "udc_enable: ep0_urb already allocated\n"); + } + + //ep0_urb->device = device; + //usbd_alloc_urb_data(&ep0_urb, 512); + + // enable UDC + // sl11write_byte(CtrlReg, CTRL_USB_ENABLE); + + // setup tick + // XXX sl11_tq.sync = 0; + sl11_tq.routine = sl11_tick; + sl11_tq.data = &udc_saw_sof; + queue_task (&sl11_tq, &tq_timer); + + udc_suspended = 1; +} + + +/** + * udc_disable - disable the UDC + * + * Switch off the UDC + */ +void udc_disable (void) +{ + printk (KERN_DEBUG "************************* udc_disable:\n"); + + // tell tick task to stop + sl11_tq.data = NULL; + while (sl11_tq.sync) { + printk (KERN_DEBUG "waiting for sl11_tq to stop\n"); + schedule_timeout (10 * HZ); + } + + // XXX del_timer() or wait + + // disable UDC + // sl11write_byte(CtrlReg, 0); + + // reset device pointer + udc_device = NULL; + + // ep0 urb + //kfree(ep0_urb.buffer); + + if (ep0_urb) { + usbd_dealloc_urb (ep0_urb); + ep0_urb = 0; + } else { + printk (KERN_ERR "udc_disable: ep0_urb already NULL\n"); + } + +} + + +/** + * udc_startup - allow udc code to do any additional startup + */ +void udc_startup_events (struct usb_device_instance *device) +{ + usbd_device_event (device, DEVICE_INIT, 0); + usbd_device_event (device, DEVICE_CREATE, 0); + usbd_device_event (device, DEVICE_HUB_CONFIGURED, 0); + usbd_device_event (device, DEVICE_RESET, 0); // XXX should be done from device event +} + + +/* ********************************************************************************************* */ + +/** + * udc_init - initialize USB Device Controller + * + * Get ready to use the USB Device Controller. + * + * Register an interrupt handler and IO region. Return non-zero for error. + */ +int udc_init (void) +{ + printk (KERN_DEBUG "udc_init:\n"); + + // probe for Sl11 + return sl11_probe (); +} + + +/** + * udc_regs - dump registers + * + * Dump registers with printk + */ +void udc_regs (void) +{ + sl11_dump_epn (0, " "); + + printk (" Ctrl: %02x Addr: %02x IntE: %02x IntS: %02x Data: %02x SOF: %02x %02x Sus: %d\n", + sl11read_byte (CtrlReg), sl11read_byte (USBAdd), sl11read_byte (IntEna), + sl11read_byte (IntStatus), sl11read_byte (DATASet), + sl11read_byte (SOFHigh), sl11read_byte (SOFLow), udc_suspended); + + sl11_dump_epn (1, " "); + sl11_dump_epn (5, "\n"); + + sl11_dump_epn (2, " "); + sl11_dump_epn (3, "\n"); +} + +/* ********************************************************************************************* */ + +/** + * udc_name - return name of USB Device Controller + */ +char *udc_name (void) +{ + return UDC_NAME; +} + +/** + * udc_request_udc_irq - request UDC interrupt + * + * Return non-zero if not successful. + */ +int udc_request_udc_irq () +{ + // request IRQ and IO region + if (request_irq (UDC_IRQ, sl11_int_hndlr, SA_INTERRUPT | SA_SAMPLE_RANDOM, UDC_NAME, NULL) + != 0) { + printk (KERN_DEBUG "usb_ctl: Couldn't request USB irq\n"); + return -EINVAL; + } + return 0; +} + +/** + * udc_request_cable_irq - request Cable interrupt + * + * Return non-zero if not successful. + */ +int udc_request_cable_irq () +{ + return 0; +} + +/** + * udc_request_udc_io - request UDC io region + * + * Return non-zero if not successful. + */ +int udc_request_io () +{ + int rc = 0; +#ifdef CONFIG_X86 + // reserve io + { + unsigned long flags; + local_irq_save (flags); + if (check_region (UDC_ADDR, UDC_ADDR_SIZE)) { + printk (KERN_DEBUG "udc_request_io: failed %x %x\n", UDC_ADDR, + UDC_ADDR_SIZE); + rc = -EINVAL; + } + request_region (UDC_ADDR, UDC_ADDR_SIZE, UDC_NAME); + local_irq_restore (flags); + } +#endif + return rc; +} + +/** + * udc_release_udc_irq - release UDC irq + */ +void udc_release_udc_irq () +{ + free_irq (UDC_IRQ, NULL); +} + +/** + * udc_release_cable_irq - release Cable irq + */ +void udc_release_cable_irq () +{ +} + +/** + * udc_release_io - release UDC io region + */ +void udc_release_io () +{ +#ifdef CONFIG_X86 + release_region (UDC_ADDR, UDC_ADDR_SIZE); +#endif +} diff -uNr linux.org/drivers/usb/device/bi/sl11.h linux/drivers/usb/device/bi/sl11.h --- linux.org/drivers/usb/device/bi/sl11.h Thu Jan 1 08:00:00 1970 +++ linux/drivers/usb/device/bi/sl11.h Thu May 15 15:54:48 2003 @@ -0,0 +1,37 @@ +/* + * sl11_bi/udc.h + * + * Copyright (c) 2000, 2001, 2002 Lineo + * + * By: + * Stuart Lynne , + * Tom Rushworth , + * Bruce Balden + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + * + */ + + +#define EP0_PACKETSIZE 0x8 + +#define UDC_MAX_ENDPOINTS 4 + + +#define UDC_IRQ SL11_IRQ +#define UDC_ADDR SL11_ADDR +#define UDC_ADDR_SIZE 4 + +#define UDC_NAME "SL11 USBD" diff -uNr linux.org/drivers/usb/device/bi/superh-hardware.h linux/drivers/usb/device/bi/superh-hardware.h --- linux.org/drivers/usb/device/bi/superh-hardware.h Thu Jan 1 08:00:00 1970 +++ linux/drivers/usb/device/bi/superh-hardware.h Thu May 15 15:54:48 2003 @@ -0,0 +1,173 @@ +/* + * superh_bi/hardware.h + * + * Copyright (c) 2002 Lineo + * + * By: + * Stuart Lynne , + * Tom Rushworth , + * Bruce Balden + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + * + */ + + +/* + * Standby Control Register (STBCR3) c.f. 9.2.3 + */ + +#define MSTP14 0x10 + +/* + * EXCPG Control Register (EXCPGCR) c.f. Section 11.3.1 + */ + +#define USBDIVS_EL0 0x00 +#define USBDIVS_EL1 0x01 +#define USBDIVS_EL2 0x02 + +#define USBCKS_EL1 0x04 +#define USBCKS_EL2 0x10 +#define USBCKS_EL3 0x20 + +#define USBDIV_11 0x00 +#define USBDIV_12 0x01 +#define USBDIV_13 0x02 + +#define USBCKS_PC 0x00 +#define USBCKS_IC 0x20 +#define USBCKS_BC 0x24 +#define USBCKS_EC 0x30 + + +/* + * Extra Pin Function Controller (EXPFC) c.f. Section 22.2.1 + */ + +#define USB_TRANS_TRAN 0x00 +#define USB_TRANS_DIG 0x02 + +#define USB_SEL_HOST 0x00 +#define USB_SEL_FUNC 0x01 + + +/* + * USBDMA Setting Register (USBDMAR) c.f. Section 23.5.19 + */ + +#define EP1_DMAE 0x01 +#define EP2_DMAE 0x02 +#define PULLUP_E 0x04 + +/* + * USB Interrupt Flag Register 0 (USBIFR0) c.f. Section 23.5.7 + */ + +#define BRST 0x80 +#define EP1_FULL 0x40 +#define EP2_TR 0x20 +#define EP2_EMPTY 0x10 +#define SETUP_TS 0x08 +#define EP0o_TS 0x04 +#define EP0i_TR 0x02 +#define EP0i_TS 0x01 + + +/* + * USB Interrupt Flag Register 1 (USBIFR0) c.f. Section 23.5.8 + */ + +#define VBUSMN 0x08 +#define EP3_TR 0x04 +#define EP3_TS 0x02 +#define VBUSF 0x01 + +/* + * USB Trigger Register (USBTRG) c.f. Section 23.5.9 + */ + +#define EP0i_PKTE 0x01 +#define EP0o_PKTE 0x02 +#define EP0s_PKTE 0x04 + +#define EP2_PKTE 0x10 +#define EP1_PKTE 0x20 +#define EP3_PKTE 0x40 + + +/* + * USBFIFO Clear Register (USBFCLR) c.f. Section 23.5.10 + */ + +#define EP3_CLEAR 0x40 +#define EP1_CLEAR 0x20 +#define EP2_CLEAR 0x10 +#define EP0o_CLEAR 0x02 +#define EP0i_CLEAR 0x01 + + +/* + * Port Control Registers (PNCR) c.f. Section 26.2 + */ + + +#define PN_PB0_OF 0x0000 +#define PN_PB0_PO 0x0001 +#define PN_PB0_PI_ON 0x0002 +#define PN_PB0_PI_OFF 0x0003 +#define PN_PB0_MSK ~0x0003 + +#define PN_PB1_OF 0x0000 +#define PN_PB1_PO 0x0004 +#define PN_PB1_PI_ON 0x0008 +#define PN_PB1_PI_OFF 0x000c +#define PN_PB1_MSK ~0x000c + +#define PN_PB2_OF 0x0000 +#define PN_PB2_PO 0x0010 +#define PN_PB2_PI_ON 0x0020 +#define PN_PB2_PI_OFF 0x0030 +#define PN_PB2_MSK ~0x0030 + +#define PN_PB3_OF 0x0000 +#define PN_PB3_PO 0x0040 +#define PN_PB3_PI_ON 0x0080 +#define PN_PB3_PI_OFF 0x00c0 +#define PN_PB3_MSK ~0x00c0 + +#define PN_PB4_OF 0x0000 +#define PN_PB4_PO 0x0100 +#define PN_PB4_PI_ON 0x0200 +#define PN_PB4_PI_OFF 0x0300 +#define PN_PB4_MSK ~0x0300 + +#define PN_PB5_OF 0x0000 +#define PN_PB5_PO 0x0400 +#define PN_PB5_PI_ON 0x0800 +#define PN_PB5_PI_OFF 0x0c00 +#define PN_PB5_MSK ~0x0c00 + +#define PN_PB6_OF 0x0000 +#define PN_PB6_PO 0x1000 +#define PN_PB6_PI_ON 0x2000 +#define PN_PB6_PI_OFF 0x3000 +#define PN_PB6_MSK ~0x3000 + +#define PN_PB7_OF 0x0000 +#define PN_PB7_PO 0x4000 +#define PN_PB7_PI_ON 0x8000 +#define PN_PB7_PI_OFF 0xc000 +#define PN_PB7_MSK ~0xc000 diff -uNr linux.org/drivers/usb/device/bi/superh.c linux/drivers/usb/device/bi/superh.c --- linux.org/drivers/usb/device/bi/superh.c Thu Jan 1 08:00:00 1970 +++ linux/drivers/usb/device/bi/superh.c Thu May 15 15:54:48 2003 @@ -0,0 +1,877 @@ +/* + * linux/drivers/usbd/superh_bi/udc.c -- USB Device Controller driver. + * + * Copyright (c) 2000, 2001, 2002 Lineo + * + * By: + * Stuart Lynne , + * Tom Rushworth , + * Bruce Balden + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + * + */ + +/*****************************************************************************/ + +#include +#include + +#include "../usbd-export.h" +#include "../usbd-build.h" +#include "../usbd-module.h" + +MODULE_AUTHOR ("sl@lineo.com, tbr@lineo.com"); +MODULE_DESCRIPTION ("USB Device SuperH Bus Interface"); +USBD_MODULE_INFO ("superh_bi 0.1-alpha"); + +#include +#include +#include +#include +#include + +#include +#include +#include +#include + +#include + +#include "../usbd.h" +#include "../usbd-func.h" +#include "../usbd-bus.h" +#include "../usbd-inline.h" +#include "usbd-bi.h" + +#include "superh.h" +#include "superh-hardware.h" + +#define MIN(a,b) ((a) < (b) ? (a) : (b)) +#define MAX(a,b) ((a) > (b) ? (a) : (b)) + +/* + * Define what interrupts are high versus low priority + */ + +#define F0_HIGH (EP1_FULL | EP2_TR | EP2_EMPTY ) +#define F0_LOW (BRST | SETUP_TS | EP0o_TS | EP0i_TR | EP0i_TS) + +#define F1_HIGH (0) +#define F1_LOW (EP3_TR | EP3_TS | VBUSF) + + +static struct usb_device_instance *udc_device; // required for the interrupt handler + +/* + * ep_endpoints - map physical endpoints to logical endpoints + */ +static struct usb_endpoint_instance *ep_endpoints[UDC_MAX_ENDPOINTS]; + +static struct urb *ep0_urb; +static unsigned char usb_address; + +extern unsigned int udc_interrupts; +unsigned int udc_f0_interrupts; +unsigned int udc_f1_interrupts; +unsigned long udc_vbusf_time; + +/* ********************************************************************************************* */ + +/* + * IO + */ + +void and_b (unsigned short mask, unsigned long addr) +{ + ctrl_outb (ctrl_inw (addr) & mask, addr); +} + +void or_b (unsigned short mask, unsigned long addr) +{ + ctrl_outb (ctrl_inw (addr) | mask, addr); +} + +void and_w (unsigned short mask, unsigned long addr) +{ + ctrl_outw (ctrl_inw (addr) & mask, addr); +} + +void or_w (unsigned short mask, unsigned long addr) +{ + ctrl_outw (ctrl_inw (addr) | mask, addr); +} + +// IO addresses for each physical ports FIFO +unsigned long ep_address_o[4] = { USBEPDR0O, USBEPDR1, 0L, 0L, }; +unsigned long ep_address_i[4] = { USBEPDR0I, 0L, USBEPDR2, USBEPDR3, }; + +// IO addresses for each physical ports trigger +unsigned char ep_trigger_o[4] = { EP0o_PKTE, EP1_PKTE, 0L, 0L, }; +unsigned char ep_trigger_i[4] = { EP0i_PKTE, 0L, EP2_PKTE, EP3_PKTE, }; + + +/** + * superh_write_buffer - write a buffer to the superh fifo + * @ep: endpoint + * @b: pointer to buffer to write + * @size: number of bytes to write + */ +static /*__inline__*/ void superh_write_buffer (unsigned char ep, unsigned char *b, + unsigned char size) +{ + if ((ep < UDC_MAX_ENDPOINTS) && ep_address_i[ep]) { + while (size-- > 0) { + ctrl_outb (*b++, ep_address_i[ep]); + } + ctrl_outb (ep_trigger_i[ep], USBTRG); + } +} + +/** + * superh_read_buffer - fill a buffer from the superh fifo + * @ep: endpoint + * @b: pointer to buffer to fill + * @size: number of bytes to read + */ +static /*__inline__*/ void superh_read_buffer (unsigned char ep, unsigned char *b, + unsigned char size) +{ + if ((ep < UDC_MAX_ENDPOINTS) && ep_address_o[ep]) { + while (size-- > 0) { + *b++ = ctrl_inb (ep_address_o[ep]); + } + ctrl_outb (ep_trigger_o[ep], USBTRG); + } +} + +/* ********************************************************************************************* */ +/* Bulk OUT (recv) + */ +static void /*__inline__*/ superh_out_ep1 (struct usb_endpoint_instance *endpoint) +{ + int size = ctrl_inb (USBEPSZ1); + + if (endpoint && endpoint->rcv_urb && size) { + // read data + superh_read_buffer (1, endpoint->rcv_urb->buffer + endpoint->rcv_urb->actual_length, + size); + usbd_rcv_complete_irq (endpoint, size, 0); + } else { + // reset fifo + or_b (EP1_CLEAR, USBFCLR); + usbd_rcv_complete_irq (endpoint, 0, EINVAL); + } +} + +/* ********************************************************************************************* */ +/* Bulk IN (tx) + */ + +/** + * superh_in_epn - process tx interrupt + * @ep: + * @endpoint: + * + * Determine status of last data sent, queue new data. + */ +static /* __inline__ */ void superh_in_epn (int ep, int restart) +{ + if (ctrl_inb (USBIFR0) & EP2_EMPTY) { + + if ((ep < UDC_MAX_ENDPOINTS) && ep_endpoints[ep]) { + struct usb_endpoint_instance *endpoint = ep_endpoints[ep]; + usbd_tx_complete_irq (endpoint, restart); + if (endpoint->tx_urb) { + struct urb *urb = endpoint->tx_urb; + + if ((urb->actual_length - endpoint->sent) > 0) { + endpoint->last = + MIN (urb->actual_length - endpoint->sent, + endpoint->tx_packetSize); + superh_write_buffer (ep, urb->buffer + endpoint->sent, + endpoint->last); + } else { + // XXX ZLP + endpoint->last = 0; + superh_write_buffer (ep, urb->buffer + endpoint->sent, 0); + } + // enable transmit done interrupt + switch (ep) { + case 2: + or_b (EP2_TR | EP2_EMPTY, USBIER0); + break; + case 3: + or_b (EP3_TR | EP3_TS, USBIER1); + break; + } + } else { + // disable transmit done interrupt + switch (ep) { + case 2: + if (ctrl_inb (USBIER0) & (EP2_TR | EP2_EMPTY)) { + and_b (~(EP2_TR | EP2_EMPTY), USBIER0); + } + break; + case 3: + and_b (~(EP3_TR | EP3_TS), USBIER1); + break; + } + } + } + } +} + +/* ********************************************************************************************* */ +/* Control (endpoint zero) + */ + +/** + * superh_in_ep0 - start transmit + * @ep: + */ +static void /*__inline__*/ superh_in_ep0 (struct usb_endpoint_instance *endpoint) +{ + if (endpoint && endpoint->tx_urb) { + struct urb *urb = endpoint->tx_urb; + + printk (KERN_DEBUG "superh_in_ep0: length: %d\n", endpoint->tx_urb->actual_length); // XXX + + if ((urb->actual_length - endpoint->sent) > 0) { + endpoint->last = + MIN (urb->actual_length - endpoint->sent, endpoint->tx_packetSize); + superh_write_buffer (0, urb->buffer + endpoint->sent, endpoint->last); + } else { + // XXX ZLP + endpoint->last = 0; + superh_write_buffer (0, urb->buffer + endpoint->sent, 0); + } + } +} + +/** + * superh_ep0_setup + */ +static void superh_ep0_setup (void) +{ + if (ep_endpoints[0]) { + int i; + struct usb_endpoint_instance *endpoint = ep_endpoints[0]; + unsigned char *cp = (unsigned char *) &ep0_urb->device_request; + + for (i = 0; i < 8; i++) { + cp[i] = ctrl_inb (USBEPDR0S); + } + + // process setup packet + if (usbd_recv_setup (ep0_urb)) { + printk (KERN_DEBUG "superh_ep0: setup failed\n"); + return; + } + // check data direction + if ((ep0_urb->device_request.bmRequestType & USB_REQ_DIRECTION_MASK) == + USB_REQ_HOST2DEVICE) { + + // should we setup to receive data + if (le16_to_cpu (ep0_urb->device_request.wLength)) { + printk (KERN_DEBUG "superh_ep0: setup to read data %d\n", + le16_to_cpu (ep0_urb->device_request.wLength)); + endpoint->rcv_urb = ep0_urb; + endpoint->rcv_urb->actual_length = 0; + //superh_out(0, endpoint); + return; + } + + printk (KERN_DEBUG "superh_ep0: send ack %d\n", le16_to_cpu (ep0_urb->device_request.wLength)); // XXX + + // should be finished, send ack + ctrl_outb (EP0s_PKTE, USBTRG); + return; + } + // we should be sending data back + + // verify that we have non-zero request length + if (!le16_to_cpu (ep0_urb->device_request.wLength)) { + udc_stall_ep (0); + return; + } + // verify that we have non-zero length response + if (!ep0_urb->actual_length) { + udc_stall_ep (0); + return; + } + // send ack prior to sending data + ctrl_outb (EP0s_PKTE, USBTRG); + + // start sending + endpoint->tx_urb = ep0_urb; + endpoint->sent = 0; + endpoint->last = 0; + superh_in_ep0 (endpoint); + } +} + + +/* ********************************************************************************************* */ +/* Interrupt Handler(s) + */ + + +/** + * superh_int_hndlr_f0 - high priority interrupt handler + * + */ +static void superh_int_hndlr_f0 (int irq, void *dev_id, struct pt_regs *regs) +{ + + unsigned char f0_status; + + udc_interrupts++; + udc_f0_interrupts++; + f0_status = ctrl_inb (USBIFR0); + + if (f0_status & EP1_FULL) { + superh_out_ep1 (ep_endpoints[1]); + f0_status = ctrl_inb (USBIFR0); + if (f0_status & EP1_FULL) { + superh_out_ep1 (ep_endpoints[1]); + } + } else if (f0_status & (EP2_TR | EP2_EMPTY)) { + superh_in_epn (2, 0); // XXX status? + ctrl_outb (~(f0_status & EP2_TR), USBIFR0); + } +} + +/** + * superh_int_hndlr_f1 - low priority interrupt handler + * + */ +static void superh_int_hndlr_f1 (int irq, void *dev_id, struct pt_regs *regs) +{ + unsigned char f0_status; + unsigned char f1_status; + + udc_interrupts++; + udc_f1_interrupts++; + f0_status = ctrl_inb (USBIFR0); + f1_status = ctrl_inb (USBIFR1); + ctrl_outb (~(f0_status & F0_LOW), USBIFR0); + ctrl_outb (~(f1_status & F1_LOW), USBIFR1); + + //printk(KERN_DEBUG"superh_int_hndlr_f1[%x] status %02x %02x\n", udc_interrupts, f0_status, f1_status); // XXX + + if (f1_status & VBUSF) { + /* + * XXX vbusf can bounce, probably should disarm vbusf interrupt, for now, just check + * that we don't handle it more than once. + */ + if (!udc_vbusf_time || ((jiffies - udc_vbusf_time) > 1)) { + if (udc_device) { + if (f1_status & VBUSMN) { + printk (KERN_DEBUG + "superh_int_hndlr_f1[%x]: VBUSF VBUSMN set\n", + udc_interrupts); + } else { + printk (KERN_DEBUG + "superh_int_hndlr_f1[%x]: VBUSF VBUSMN reset\n", + udc_interrupts); + } + } + } + udc_vbusf_time = jiffies; + } else { + udc_vbusf_time = 0; + } + if (f0_status & BRST) { + printk (KERN_DEBUG "superh_int_hndlr_f1[%x]: BRST bus reset\n", udc_interrupts); + + // reset fifo's and stall's + or_b (EP3_CLEAR | EP1_CLEAR | EP2_CLEAR | EP0o_CLEAR | EP0i_CLEAR, USBFCLR); + or_b (0, USBEPSTL); + + if (udc_device->device_state != DEVICE_RESET) { + printk (KERN_DEBUG "superh_int_hndlr_f1[%x]: DEVICE RESET\n", + udc_interrupts); + usbd_device_event_irq (udc_device, DEVICE_RESET, 0); + } + } + if (f0_status & SETUP_TS) { + printk (KERN_DEBUG "superh_int_hndlr_f1[%x]: SETUP TS\n", udc_interrupts); + or_b (EP0o_CLEAR | EP0i_CLEAR, USBFCLR); + superh_ep0_setup (); + if (udc_device->device_state == STATE_DEFAULT) { + /* + * superh won't give us set address, configuration or interface, but at least + * we know that at this point we know that a host is talking to us, close + * enough. + */ + usbd_device_event (udc_device, DEVICE_ADDRESS_ASSIGNED, 0); + udc_device->configuration = 0; + usbd_device_event (udc_device, DEVICE_CONFIGURED, 0); + udc_device->interface = 1; // no options + udc_device->alternate = 0; // no options + usbd_device_event (udc_device, DEVICE_SET_INTERFACE, 0); + } + } + if (f0_status & EP0i_TR) { + usbd_tx_complete_irq (ep_endpoints[0], 0); + superh_in_ep0 (ep_endpoints[0]); + } + if (f0_status & EP0o_TS) { + printk (KERN_DEBUG "superh_int_hndlr_f1[%x]: ep0o TS\n", udc_interrupts); + } + if (f0_status & EP0i_TS) { + printk (KERN_DEBUG "superh_int_hndlr_f1[%x]: ep0iTS\n", udc_interrupts); + } + if (f1_status & EP3_TR) { + printk (KERN_DEBUG "superh_int_hndlr_f1[%x]: EP3 TR\n", udc_interrupts); + superh_in_epn (3, 0); // XXX status? + } + if (f1_status & EP3_TS) { + printk (KERN_DEBUG "superh_int_hndlr_f1[%x]: EP3 TS\n", udc_interrupts); + } + superh_in_epn (3, 0); // XXX status? +} + + +/* ********************************************************************************************* */ + + +/* ********************************************************************************************* */ +/* Start of public functions. */ + +/** + * udc_start_in_irq - start transmit + * @endpoint: endpoint instance + * + * Called by bus interface driver to see if we need to start a data transmission. + */ +void udc_start_in_irq (struct usb_endpoint_instance *endpoint) +{ + if (endpoint) { + // XXX should verify logical address mapping to physical 2 + superh_in_epn (2, 0); + } +} + +/** + * udc_init - initialize + * + * Return non-zero if we cannot see device. + **/ +int udc_init (void) +{ + // XXX move clock enable to udc_enable, add code to udc_disable to disable them + + // Reset and then Select Function USB1_pwr_en out (USB) c.f. Section 26, Table 26.1 PTE2 + and_w (PN_PB2_MSK, PECR); + or_w (PN_PB2_OF, PECR); + + // Reset and then Select Function UCLK c.f. Section 26, Table 26.1, PTD6 + and_w (PN_PB6_MSK, PDCR); + or_w (PN_PB6_OF, PDCR); + + // Stop USB module prior to setting clocks c.f. Section 9.2.3 + and_b (~MSTP14, STBCR3); + or_b (MSTP14, STBCR3); + + // Select external clock, 1/1 divisor c.f. Section 11.3.1 + or_b (USBDIV_11 | USBCKS_EC, EXCPGCR); + + // Start USB c.f. Section 9.2.3 + and_b (~MSTP14, STBCR3); + + // Disable pullup c.f. Section 23.5.19 + printk (KERN_DEBUG "udc_init:\n"); + printk (KERN_DEBUG "udc_init: Disable Pullup\n"); + or_b (PULLUP_E, USBDMA); + //and_b(~PULLUP_E, USBDMA); + + // Set port 1 to function, disabled c.f. Section 22.2.1 + or_w (USB_TRANS_TRAN | USB_SEL_FUNC, EXPFC); + + // Enable pullup c.f. Section 23.5.19 + printk (KERN_DEBUG "udc_init:\n"); + printk (KERN_DEBUG "udc_init: Enable Pullup\n"); + and_b (~PULLUP_E, USBDMA); + //or_b(PULLUP_E, USBDMA); + + // reset fifo's and stall's + or_b (EP3_CLEAR | EP1_CLEAR | EP2_CLEAR | EP0o_CLEAR | EP0i_CLEAR, USBFCLR); + or_b (0, USBEPSTL); + + // setup interrupt priority by using the interrupt select registers + ctrl_outb (F0_LOW, USBISR0); + ctrl_outb (F1_LOW, USBISR1); + + printk (KERN_DEBUG "udc_init:\n"); + return 0; +} + +/** + * udc_stall_ep - stall endpoint + * @ep: physical endpoint + * + * Stall the endpoint. + */ +void udc_stall_ep (unsigned int ep) +{ + if (ep < UDC_MAX_ENDPOINTS) { + // stall + } +} + +/** + * udc_reset_ep - reset endpoint + * @ep: physical endpoint + * reset the endpoint. + * + * returns : 0 if ok, -1 otherwise + */ +void udc_reset_ep (unsigned int ep) +{ + if (ep < UDC_MAX_ENDPOINTS) { + // reset + } +} + +/** + * udc_endpoint_halted - is endpoint halted + * @ep: + * + * Return non-zero if endpoint is halted + */ +int udc_endpoint_halted (unsigned int ep) +{ + return 0; +} + +/** + * udc_set_address - set the USB address for this device + * @address: + * + * Called from control endpoint function after it decodes a set address setup packet. + */ +void udc_set_address (unsigned char address) +{ + // address cannot be setup until ack received + usb_address = address; +} + +/** + * udc_serial_init - set a serial number if available + */ +int __init udc_serial_init (struct usb_bus_instance *bus) +{ + return -EINVAL; +} + +/* ********************************************************************************************* */ + +/** + * udc_max_endpoints - max physical endpoints + * + * Return number of physical endpoints. + */ +int udc_max_endpoints (void) +{ + return UDC_MAX_ENDPOINTS; +} + +/** + * udc_check_ep - check logical endpoint + * @lep: + * + * Return physical endpoint number to use for this logical endpoint or zero if not valid. + */ +int udc_check_ep (int logical_endpoint, int packetsize) +{ + return (((logical_endpoint & 0xf) >= UDC_MAX_ENDPOINTS) + || (packetsize > 64)) ? 0 : (logical_endpoint & 0xf); +} + +/** + * udc_set_ep - setup endpoint + * @ep: + * @endpoint: + * + * Associate a physical endpoint with endpoint_instance + */ +void udc_setup_ep (struct usb_device_instance *device, unsigned int ep, + struct usb_endpoint_instance *endpoint) +{ + printk (KERN_DEBUG "udc_setup_ep: ep: %d\n", ep); + if (ep < UDC_MAX_ENDPOINTS) { + + ep_endpoints[ep] = endpoint; + // ep0 + if (ep == 0) { + } + // IN + else if (endpoint->endpoint_address & 0x80) { + } + // OUT + else if (endpoint->endpoint_address) { + usbd_fill_rcv (device, endpoint, 5); + endpoint->rcv_urb = first_urb_detached (&endpoint->rdy); + } + } +} + +/** + * udc_disable_ep - disable endpoint + * @ep: + * + * Disable specified endpoint + */ +void udc_disable_ep (unsigned int ep) +{ + if (ep < UDC_MAX_ENDPOINTS) { + struct usb_endpoint_instance *endpoint; + + if ((endpoint = ep_endpoints[ep])) { + ep_endpoints[ep] = NULL; + usbd_flush_ep (endpoint); + } + } +} + +/* ********************************************************************************************* */ + +/** + * udc_connected - is the USB cable connected + * + * Return non-zero if cable is connected. + */ +int udc_connected () +{ + return (ctrl_inb (USBIFR1) & VBUSMN) ? 1 : 0; +} + +/** + * udc_connect - enable pullup resistor + * + * Turn on the USB connection by enabling the pullup resistor. + */ +void udc_connect (void) +{ + // enable pullup + printk (KERN_DEBUG "udc_connect:\n"); + and_b (~PULLUP_E, USBDMA); +} + +/** + * udc_disconnect - disable pullup resistor + * + * Turn off the USB connection by disabling the pullup resistor. + */ +void udc_disconnect (void) +{ + // enable pullup + printk (KERN_DEBUG "udc_disconnect:\n"); + or_b (PULLUP_E, USBDMA); +} + +/* ********************************************************************************************* */ + +/** + * udc_enable_interrupts - enable interrupts + * + * Switch on UDC interrupts. + * + */ +void udc_all_interrupts (struct usb_device_instance *device) +{ + // set interrupt mask + or_b (BRST | EP1_FULL | SETUP_TS | EP0o_TS | EP0i_TR | EP0i_TS, USBIER0); + or_b (EP3_TR | EP3_TS | VBUSF, USBIER1); +} + +/** + * udc_suspended_interrupts - enable suspended interrupts + * + * Switch on only UDC resume interrupt. + * + */ +void udc_suspended_interrupts (struct usb_device_instance *device) +{ +} + +/** + * udc_disable_interrupts - disable interrupts. + * + * switch off interrupts + */ +void udc_disable_interrupts (struct usb_device_instance *device) +{ + // reset interrupt mask + ctrl_outb (0x0, USBIER0); + ctrl_outb (0x0, USBIER1); +} + +/* ********************************************************************************************* */ + +/** + * udc_ep0_packetsize - return ep0 packetsize + */ +int udc_ep0_packetsize (void) +{ + return EP0_PACKETSIZE; +} + +/** + * udc_enable - enable the UDC + * + * Switch on the UDC + */ +void udc_enable (struct usb_device_instance *device) +{ + // save the device structure pointer + udc_device = device; + + // ep0 urb + if (!ep0_urb) { + if ((ep0_urb = usbd_alloc_urb (device, device->function_instance_array, 0, 512))) { + printk (KERN_ERR "ep0_enable: usbd_alloc_urb failed\n"); + } + } else { + printk (KERN_ERR "udc_enable: ep0_urb already allocated\n"); + } + + // XXX enable UDC +} + +/** + * udc_disable - disable the UDC + * + * Switch off the UDC + */ +void udc_disable (void) +{ + // XXX disable UDC + + // reset device pointer + udc_device = NULL; + + // ep0 urb + if (ep0_urb) { + usbd_dealloc_urb (ep0_urb); + ep0_urb = NULL; + } +} + +/** + * udc_startup_events - allow udc code to do any additional startup + */ +void udc_startup_events (struct usb_device_instance *device) +{ + printk (KERN_DEBUG "udc_startup_events:\n"); + usbd_device_event (device, DEVICE_INIT, 0); + usbd_device_event (device, DEVICE_CREATE, 0); + usbd_device_event (device, DEVICE_HUB_CONFIGURED, 0); + + // XXX the following could be snuck into get descriptor LANGID + +#if 0 + usbd_device_event (device, DEVICE_ADDRESS_ASSIGNED, 0); + device->configuration = 0; + usbd_device_event (device, DEVICE_CONFIGURED, 0); + device->interface = 1; + device->alternate = 0; + usbd_device_event (device, DEVICE_SET_INTERFACE, 0); +#endif +} + +/* ********************************************************************************************* */ + +/** + * udc_name - return name of USB Device Controller + */ +char *udc_name (void) +{ + return UDC_NAME; +} + +/** + * udc_request_udc_irq - request UDC interrupt + */ +int udc_request_udc_irq () +{ + if (request_irq (USBF0_IRQ, superh_int_hndlr_f0, SA_INTERRUPT | SA_SAMPLE_RANDOM, UDC_NAME + " USBD Bus Interface (high priority)", NULL) != 0) { + printk (KERN_DEBUG "usb_ctl: Couldn't request USB irq F0\n"); + return -EINVAL; + } + return 0; +} + +/** + * udc_request_cable_irq - request Cable interrupt + */ +int udc_request_cable_irq () +{ + if (request_irq (USBF1_IRQ, superh_int_hndlr_f1, SA_INTERRUPT | SA_SAMPLE_RANDOM, + UDC_NAME " USBD Bus Interface (low priority)", NULL) != 0) { + printk (KERN_DEBUG "usb_ctl: Couldn't request USB irq F1\n"); + free_irq (USBF0_IRQ, NULL); + return -EINVAL; + } + return 0; +} + +/** + * udc_request_udc_io - request UDC io region + */ +int udc_request_io () +{ + return 0; +} + +/** + * udc_release_udc_irq - release UDC irq + */ +void udc_release_udc_irq () +{ + free_irq (USBF0_IRQ, NULL); +} + +/** + * udc_release_cable_irq - release Cable irq + */ +void udc_release_cable_irq () +{ + free_irq (USBF1_IRQ, NULL); +} + +/** + * udc_release_release_io - release UDC io region + */ +void udc_release_io () +{ +} + +/** + * udc_regs - dump registers + */ +void udc_regs (void) +{ + printk (KERN_DEBUG + "[%d:%d:%d] IFR[%02x:%02x] IER[%02x:%02x] ISR[%02x:%02x] DASTS[%02x] EPSTL[%02x] EPSZ1[%02x] DMA[%02x]\n", + udc_interrupts, udc_f0_interrupts, udc_f1_interrupts, + ctrl_inb (USBIFR0), ctrl_inb (USBIFR1), ctrl_inb (USBIER0), ctrl_inb (USBIER1), + ctrl_inb (USBISR0), ctrl_inb (USBISR1), ctrl_inb (USBDASTS), ctrl_inb (USBEPSTL), + ctrl_inb (USBEPSZ1), ctrl_inb (USBDMA) + ); +} diff -uNr linux.org/drivers/usb/device/bi/superh.h linux/drivers/usb/device/bi/superh.h --- linux.org/drivers/usb/device/bi/superh.h Thu Jan 1 08:00:00 1970 +++ linux/drivers/usb/device/bi/superh.h Thu May 15 15:54:48 2003 @@ -0,0 +1,37 @@ +/* + * linux/drivers/usbd/gen_bi/udc.h + * + * Copyright (c) 2000, 2001, 2002 Lineo + * + * By: + * Stuart Lynne , + * Tom Rushworth , + * Bruce Balden + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + * + */ + + +#define EP0_PACKETSIZE 0x8 + +#define UDC_MAX_ENDPOINTS 4 + + +#undef XXXX_IRQ +#undef XXXX_ADDR +#undef XXXX_ADDR_SIZE + +#define UDC_NAME "SuperH 7727" diff -uNr linux.org/drivers/usb/device/bi/usbd-bi.c linux/drivers/usb/device/bi/usbd-bi.c --- linux.org/drivers/usb/device/bi/usbd-bi.c Thu Jan 1 08:00:00 1970 +++ linux/drivers/usb/device/bi/usbd-bi.c Thu May 15 15:54:48 2003 @@ -0,0 +1,1236 @@ +/* + * linux/drivers/usbd/usbd-bi.c - USB Bus Interface Driver + * + * Copyright (c) 2000, 2001, 2002 Lineo + * Copyright (c) 2001 Hewlett Packard + * + * By: + * Stuart Lynne , + * Tom Rushworth , + * Bruce Balden + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + * + */ + +#include +#include + +#include +#include +#include + +#include + +#include +#include +#include +#include + +#include +#include + +#include + +#if defined(CONFIG_PM) && !defined(CONFIG_USBD_MONITOR) && !defined(CONFIG_USBD_MONITOR_MODULE) +#include +#endif + + +#include "../usbd.h" +#include "../usbd-debug.h" +#include "../usbd-func.h" +#include "../usbd-bus.h" +#include "../usbd-inline.h" +#include "usbd-bi.h" + + + +/* Module Parameters ************************************************************************* */ + +static char *dbg = NULL; + +MODULE_PARM (dbg, "s"); + +/* Debug switches (module parameter "dbg=...") *********************************************** */ + +extern int dbgflg_usbdbi_init; +int dbgflg_usbdbi_intr; +int dbgflg_usbdbi_tick; +int dbgflg_usbdbi_usbe; +int dbgflg_usbdbi_rx; +int dbgflg_usbdbi_tx; +int dbgflg_usbdbi_dma_flg; +int dbgflg_usbdbi_setup; +int dbgflg_usbdbi_ep0; +int dbgflg_usbdbi_udc; +int dbgflg_usbdbi_stall; +int dbgflg_usbdbi_pm; +int dbgflg_usbdbi_pur; + +static debug_option dbg_table[] = { + {&dbgflg_usbdbi_init, NULL, "init", "initialization and termination"}, + {&dbgflg_usbdbi_intr, NULL, "intr", "interrupt handling"}, + {&dbgflg_usbdbi_tick, NULL, "tick", "interrupt status monitoring on clock tick"}, + {&dbgflg_usbdbi_usbe, NULL, "usbe", "USB events"}, + {&dbgflg_usbdbi_rx, NULL, "rx", "USB RX (host->device) handling"}, + {&dbgflg_usbdbi_tx, NULL, "tx", "USB TX (device->host) handling"}, + {&dbgflg_usbdbi_dma_flg, NULL, "dma", "DMA handling"}, + {&dbgflg_usbdbi_setup, NULL, "setup", "Setup packet handling"}, + {&dbgflg_usbdbi_ep0, NULL, "ep0", "End Point 0 packet handling"}, + {&dbgflg_usbdbi_udc, NULL, "udc", "USB Device"}, + {&dbgflg_usbdbi_stall, NULL, "stall", "Testing"}, + {&dbgflg_usbdbi_pm, NULL, "pm", "Power Management"}, + {&dbgflg_usbdbi_pur, NULL, "pur", "USB cable Pullup Resistor"}, + {NULL, NULL, NULL, NULL} +}; + +/* XXX +static __initdata unsigned char default_dev_addr[ETH_ALEN] = { + 0x40, 0x00, 0x00, 0x00, 0x00, 0x01 +}; +*/ + +/* globals */ + +int have_cable_irq; + +/* ticker */ + +int ticker_terminating; +int ticker_timer_set; + +/** + * kickoff_thread - start management thread + */ +void ticker_kickoff (void); + +/** + * killoff_thread - stop management thread + */ +void ticker_killoff (void); + +#if defined(CONFIG_PM) && !defined(CONFIG_USBD_MONITOR) && !defined(CONFIG_USBD_MONITOR_MODULE) +struct pm_dev *pm_dev; // power management +#endif + + +/* Bus Interface Callback Functions ********************************************************** */ + +/** + * bi_cancel_urb - cancel sending an urb + * @urb: data urb to cancel + * + * Used by the USB Device Core to cancel an urb. + */ +int bi_cancel_urb (struct urb *urb) +{ + dbgENTER (dbgflg_usbdbi_tx, 1); + //ep2_reset(); + return 0; +} + +/** + * bi_find_endpoint - find endpoint + * @device: device + * @endpoint_address: endpoint address + */ +struct usb_endpoint_instance *bi_find_endpoint(struct usb_device_instance *device, int endpoint_address) +{ + if (device && device->bus && device->bus->endpoint_array) { + int i; + for (i = 0; i < udc_max_endpoints (); i++) { + struct usb_endpoint_instance *endpoint; + if ((endpoint = device->bus->endpoint_array + i)) { + if ((endpoint->endpoint_address & 0x7f) == (endpoint_address & 0x7f)) { + return endpoint; + } + } + } + } + return NULL; +} + +/** + * bi_endpoint_halted - check if endpoint halted + * @device: device + * @endpoint: endpoint to check + * + * Used by the USB Device Core to check endpoint halt status. + */ +int bi_endpoint_halted (struct usb_device_instance *device, int endpoint_address) +{ + struct usb_endpoint_instance *endpoint; + if ((endpoint = bi_find_endpoint(device, endpoint_address))) { + dbg_ep0 (1, "endpoint: %d status: %d", endpoint_address, endpoint->status); + return endpoint->status; + } + dbg_ep0 (0, "endpoint: %02x NOT FOUND", endpoint_address); + return 0; +} + + +/** + * bi_device_feature - handle set/clear feature requests + * @device: device + * @endpoint: endpoint to check + * @flag: set or clear + * + * Used by the USB Device Core to check endpoint halt status. + */ +int bi_device_feature (struct usb_device_instance *device, int endpoint_address, int flag) +{ + struct usb_endpoint_instance *endpoint; + dbg_ep0 (0, "endpoint: %d flag: %d", endpoint_address, flag); + if ((endpoint = bi_find_endpoint(device, endpoint_address))) { + + dbg_ep0 (1, "endpoint: %d status: %d", endpoint_address, endpoint->status); + if (flag && !endpoint->status) { + dbg_ep0 (1, "stalling endpoint"); + // XXX logical to physical? + udc_stall_ep (endpoint_address); + endpoint->status = 1; + } + else if (!flag && endpoint->status){ + dbg_ep0 (1, "reseting endpoint %d", endpoint_address); + udc_reset_ep (endpoint_address); + endpoint->status = 0; + } + return 0; + } + dbg_ep0 (0, "endpoint: %d NOT FOUND", endpoint_address); + return -EINVAL; +} + +/* + * bi_disable_endpoints - disable udc and all endpoints + */ +static void bi_disable_endpoints (struct usb_device_instance *device) +{ + int i; + if (device && device->bus && device->bus->endpoint_array) { + for (i = 0; i < udc_max_endpoints (); i++) { + struct usb_endpoint_instance *endpoint; + if ((endpoint = device->bus->endpoint_array + i)) { + usbd_flush_ep (endpoint); + } + } + } +} + +/* bi_config - commission bus interface driver + */ +static int bi_config (struct usb_device_instance *device) +{ + int i; + + struct usb_interface_descriptor *interface; + struct usb_endpoint_descriptor *endpoint_descriptor; + + int found_tx = 0; + int found_rx = 0; + + dbg_init (1, "checking config: config: %d interface: %d alternate: %d", + device->configuration, device->interface, device->alternate); + + bi_disable_endpoints (device); + + // audit configuration for compatibility + if (!(interface = usbd_device_interface_descriptor (device, + 0, device->configuration, device->interface, device->alternate))) + { + dbg_init (0, "cannot fetch interface descriptor c:%d i:%d a:%d", + device->configuration, device->interface, device->alternate); + return -EINVAL; + } + + + dbg_init (2, "---> endpoints: %d", interface->bNumEndpoints); + + // iterate across all endpoints for this configuration and verify they are valid + for (i = 0; i < interface->bNumEndpoints; i++) { + int transfersize; + + int physical_endpoint; + int logical_endpoint; + + //dbg_init(0, "fetching endpoint %d", i); + if (!(endpoint_descriptor = usbd_device_endpoint_descriptor_index (device, 0, + device-> configuration, device-> interface, device-> alternate, i))) + { + dbg_init (0, "cannot fetch endpoint descriptor: %d", i); + continue; + } + // XXX check this + transfersize = usbd_device_endpoint_transfersize (device, 0, + device->configuration, device->interface, device->alternate, i); + + + logical_endpoint = endpoint_descriptor->bEndpointAddress; + + if (! + (physical_endpoint = + udc_check_ep (logical_endpoint, endpoint_descriptor->wMaxPacketSize))) { + dbg_init (2, + "endpoint[%d]: l: %02x p: %02x transferSize: %d packetSize: %02x INVALID", + i, logical_endpoint, physical_endpoint, transfersize, + endpoint_descriptor->wMaxPacketSize); + + dbg_init (0, "invalid endpoint: %d %d", logical_endpoint, physical_endpoint); + return -EINVAL; + } + else { + struct usb_endpoint_instance *endpoint = + device->bus->endpoint_array + physical_endpoint; + + dbg_init (2, "endpoint[%d]: l: %02x p: %02x transferSize: %d packetSize: %02x FOUND", + i, logical_endpoint, physical_endpoint, transfersize, + endpoint_descriptor->wMaxPacketSize); + + dbg_init (2, "epd->bEndpointAddress=%02x", endpoint_descriptor->bEndpointAddress); + endpoint->endpoint_address = endpoint_descriptor->bEndpointAddress; + + if (endpoint_descriptor->wMaxPacketSize > 64) { + dbg_init (0, "incompatible with endpoint size: %x", endpoint_descriptor->wMaxPacketSize); + return -EINVAL; + } + + if (endpoint_descriptor->bEndpointAddress & IN) { + found_tx++; + endpoint->tx_attributes = endpoint_descriptor->bmAttributes; + endpoint->tx_transferSize = transfersize & 0xfff; + endpoint->tx_packetSize = endpoint_descriptor->wMaxPacketSize; + endpoint->last = 0; + if (endpoint->tx_urb) { + dbg_init (1, "CLEARING tx_urb: %p", endpoint->tx_urb); + usbd_dealloc_urb (endpoint->tx_urb); + endpoint->tx_urb = NULL; + } + } else { + found_rx++; + endpoint->rcv_attributes = endpoint_descriptor->bmAttributes; + endpoint->rcv_transferSize = transfersize & 0xfff; + endpoint->rcv_packetSize = endpoint_descriptor->wMaxPacketSize; + if (endpoint->rcv_urb) { + dbg_init (1, "CLEARING rcv_urb: %p", endpoint->tx_urb); + usbd_dealloc_urb (endpoint->rcv_urb); + endpoint->rcv_urb = NULL; + } + } + } + } + + // iterate across all endpoints and enable them + + dbg_init(1, "---> device->status: %d", device->status); + + if (device->status == USBD_OK) { + dbg_init (1, "enabling endpoints"); + for (i = 1; i < device->bus->driver->max_endpoints; i++) { + + struct usb_endpoint_instance *endpoint = device->bus->endpoint_array + i; + dbg_init (1, "endpoint[%d]: %p addr: %02x transferSize: %d:%d packetSize: %d:%d SETUP", + i, endpoint, endpoint->endpoint_address, + endpoint->rcv_transferSize, endpoint->tx_transferSize, + endpoint->rcv_packetSize, endpoint->tx_packetSize); + + //udc_setup_ep(device, i, endpoint->endpoint_address? endpoint : NULL); + udc_setup_ep (device, i, endpoint); + } + } + + return 0; +} + + +/** + * bi_device_event - handle generic bus event + * @device: device pointer + * @event: interrupt event + * + * Called by usb core layer to inform bus of an event. + */ +int bi_device_event (struct usb_device_instance *device, usb_device_event_t event, int data) +{ + + //printk(KERN_DEBUG "bi_device_event: event: %d\n", event); + + if (!device) { + return 0; + } + + dbg_usbe (1,"%s", USBD_DEVICE_EVENTS(event)); + + switch (event) { + case DEVICE_UNKNOWN: + break; + case DEVICE_INIT: + break; + case DEVICE_CREATE: // XXX should this stuff be in DEVICE_INIT? + // enable upstream port + + //ep0_enable(device); + + // for net_create + bi_config (device); + + // enable udc, enable interrupts, enable connect + printk(KERN_INFO"bi_device_event: call udc_enable\n"); + udc_enable (device); + printk(KERN_INFO"bi_device_event: call udc_all_interrupts\n"); + + // XXX verify + udc_suspended_interrupts (device); + //udc_all_interrupts (device); + + dbg_usbe (1, "CREATE done"); + break; + + case DEVICE_HUB_CONFIGURED: + udc_connect (); + break; + + case DEVICE_RESET: + device->address = 0; + udc_set_address (device->address); + udc_reset_ep (0); + + // XXX verify + udc_suspended_interrupts (device); + dbg_usbe (1, "DEVICE RESET done: %d", device->address); + break; + + + case DEVICE_ADDRESS_ASSIGNED: + udc_set_address (device->address); + device->status = USBD_OK; + + // XXX verify + udc_all_interrupts (device); // XXX + break; + + case DEVICE_CONFIGURED: + device->status = USBD_OK; + bi_config (device); + break; + + case DEVICE_DE_CONFIGURED: + udc_reset_ep (1); + udc_reset_ep (2); + udc_reset_ep (3); + break; + + + case DEVICE_SET_INTERFACE: + bi_config (device); + break; + + case DEVICE_SET_FEATURE: + break; + + case DEVICE_CLEAR_FEATURE: + break; + + case DEVICE_BUS_INACTIVE: + // disable suspend interrupt + udc_suspended_interrupts (device); + + // XXX check on linkup and sl11 + // if we are no longer connected then force a reset + if (!udc_connected ()) { + usbd_device_event_irq (device, DEVICE_RESET, 0); + } + break; + + case DEVICE_BUS_ACTIVITY: + // enable suspend interrupt + udc_all_interrupts (device); + break; + + case DEVICE_POWER_INTERRUPTION: + break; + + case DEVICE_HUB_RESET: + break; + + case DEVICE_DESTROY: + udc_disconnect (); + bi_disable_endpoints (device); + udc_disable_interrupts (device); + udc_disable (); + break; + + case DEVICE_FUNCTION_PRIVATE: + break; + } + return 0; +} + +/** + * bi_send_urb - start transmit + * @urb: + */ +int bi_send_urb (struct urb *urb) +{ + unsigned long flags; + dbg_tx (4, "urb: %p", urb); + local_irq_save (flags); + if (urb && urb->endpoint && !urb->endpoint->tx_urb) { + dbg_tx (2, "urb: %p endpoint: %x", urb, urb->endpoint->endpoint_address); + usbd_tx_complete_irq (urb->endpoint, 0); + udc_start_in_irq (urb->endpoint); + } + local_irq_restore (flags); + + // Shouldn't need to make this atomic, all we need is a change indicator + urb->device->usbd_rxtx_timestamp = jiffies; + + return 0; +} + + +#if defined(CONFIG_PM) && !defined(CONFIG_USBD_MONITOR) && !defined(CONFIG_USBD_MONITOR_MODULE) +static int bi_pm_event (struct pm_dev *pm_dev, pm_request_t request, void *unused); +#endif + + + +struct usb_bus_operations bi_ops = { + send_urb:bi_send_urb, + cancel_urb:bi_cancel_urb, + endpoint_halted:bi_endpoint_halted, + device_feature:bi_device_feature, + device_event:bi_device_event, +}; + +struct usb_bus_driver bi_driver = { + name:"", + max_endpoints:0, + ops:&bi_ops, + this_module:THIS_MODULE, +}; + + +/* Bus Interface Received Data *************************************************************** */ + +struct usb_device_instance *device_array[MAX_DEVICES]; + + +/* Bus Interface Support Functions *********************************************************** */ + +/** + * udc_cable_event - called from cradle interrupt handler + */ +void udc_cable_event (void) +{ + struct usb_bus_instance *bus; + struct usb_device_instance *device; + struct bi_data *data; + + dbgENTER (dbgflg_usbdbi_init, 1); + + // sanity check + if (!(device = device_array[0]) || !(bus = device->bus) || !(data = bus->privdata)) { + return; + } + + { + unsigned long flags; + local_irq_save (flags); + if (udc_connected ()) { + dbg_init (1, "state: %d connected: %d", device->device_state, 1);; + if (device->device_state == STATE_ATTACHED) { + dbg_init (1, "LOADING"); + usbd_device_event_irq (device, DEVICE_HUB_CONFIGURED, 0); + usbd_device_event_irq (device, DEVICE_RESET, 0); + } + } else { + dbg_init (1, "state: %d connected: %d", device->device_state, 0);; + if (device->device_state != STATE_ATTACHED) { + dbg_init (1, "UNLOADING"); + usbd_device_event_irq (device, DEVICE_RESET, 0); + usbd_device_event_irq (device, DEVICE_POWER_INTERRUPTION, 0); + usbd_device_event_irq (device, DEVICE_HUB_RESET, 0); + } + } + local_irq_restore (flags); + } + dbgLEAVE (dbgflg_usbdbi_init, 1); +} + + +/* Module Init - the device init and exit routines ******************************************* */ + +/** + * bi_udc_init - initialize USB Device Controller + * + * Get ready to use the USB Device Controller. + * + * Register an interrupt handler and IO region. Return non-zero for error. + */ +int bi_udc_init (void) +{ + dbg_init (1, "Loading %s", udc_name ()); + + bi_driver.name = udc_name (); + bi_driver.max_endpoints = udc_max_endpoints (); + bi_driver.maxpacketsize = udc_ep0_packetsize (); + + dbg_init (1, "name: %s endpoints: %d ep0: %d", bi_driver.name, bi_driver.max_endpoints, + bi_driver.maxpacketsize); + + // request device IRQ + if (udc_request_udc_irq ()) { + dbg_init (0, "name: %s request udc irq failed", udc_name ()); + return -EINVAL; + } + // request device IO + if (udc_request_io ()) { + udc_release_udc_irq (); + dbg_init (0, "name: %s request udc io failed", udc_name ()); + return -EINVAL; + } + // probe for device + if (udc_init ()) { + udc_release_udc_irq (); + udc_release_io (); + dbg_init (1, "name: %s probe failed", udc_name ()); + return -EINVAL; + } + + // optional cable IRQ + have_cable_irq = !udc_request_cable_irq (); + dbg_init (1, "name: %s request cable irq %d", udc_name (), have_cable_irq); + + return 0; +} + + +/** + * bi_udc_exit - Stop using the USB Device Controller + * + * Stop using the USB Device Controller. + * + * Shutdown and free dma channels, de-register the interrupt handler. + */ +void bi_udc_exit (void) +{ + int i; + + dbg_init (1, "Unloading %s", udc_name ()); + + for (i = 0; i < udc_max_endpoints (); i++) { + udc_disable_ep (i); + } + + // free io and irq + udc_disconnect (); + udc_disable (); + udc_release_io (); + udc_release_udc_irq (); + + if (have_cable_irq) { + udc_release_cable_irq (); + } +} + + +/* Module Init - the module init and exit routines ************************************* */ + +#if 0 +/* bi_init - commission bus interface driver + */ +static int bi_init (void) +{ + return 0; +} +#endif + +/* bi_exit - decommission bus interface driver + */ +static void bi_exit (void) +{ +} + +#if defined(CONFIG_PM) && !defined(CONFIG_USBD_MONITOR) && !defined(CONFIG_USBD_MONITOR_MODULE) +/* Module Init - Power management ************************************************************ */ + +/* The power management scheme is simple. Simply do the following: + * + * Event Call Equivalent + * ------------------------------------------ + * PM_SUSPEND bi_exit(); rmmod + * PM_RESUME bi_init(); insmod + * + */ + +static int pm_suspended; + +/* + * usbd_pm_callback + * @dev: + * @rqst: + * @unused: + * + * Used to signal power management events. + */ +static int bi_pm_event (struct pm_dev *pm_dev, pm_request_t request, void *unused) +{ + struct usb_device_instance *device; + + dbg_pm (0, "request: %d pm_dev: %p data: %p", request, pm_dev, pm_dev->data); + + if (!(device = pm_dev->data)) { + dbg_pm (0, "DATA NULL, NO DEVICE"); + return 0; + } + + switch (request) { +#if defined(CONFIG_IRIS) + case PM_STANDBY: + case PM_BLANK: +#endif + case PM_SUSPEND: + dbg_pm (0, "PM_SUSPEND"); + if (!pm_suspended) { + pm_suspended = 1; + dbg_init (1, "MOD_INC_USE_COUNT %d", GET_USE_COUNT (THIS_MODULE)); + udc_disconnect (); // disable USB pullup if we can + udc_disable_interrupts (device); // disable interupts + udc_disable (); // disable UDC + dbg_pm (0, "PM_SUSPEND: finished"); + } + break; +#if defined(CONFIG_IRIS) + case PM_UNBLANK: +#endif + case PM_RESUME: + dbg_pm (0, "PM_RESUME"); + if (pm_suspended) { + // probe for device + if (udc_init ()) { + dbg_init (0, "udc_init failed"); + //return -EINVAL; + } + udc_enable (device); // enable UDC + udc_all_interrupts (device); // enable interrupts + udc_connect (); // enable USB pullup if we can + //udc_set_address(device->address); + //udc_reset_ep(0); + + pm_suspended = 0; + dbg_init (1, "MOD_INC_USE_COUNT %d", GET_USE_COUNT (THIS_MODULE)); + dbg_pm (0, "PM_RESUME: finished"); + } + break; + } + return 0; +} +#endif + +#ifdef CONFIG_USBD_PROCFS +/* Proc Filesystem *************************************************************************** */ + +/* * + * usbd_proc_read - implement proc file system read. + * @file + * @buf + * @count + * @pos + * + * Standard proc file system read function. + */ +static ssize_t usbd_proc_read (struct file *file, char *buf, size_t count, loff_t * pos) +{ + struct usb_device_instance *device; + unsigned long page; + int len = 0; + int index; + + MOD_INC_USE_COUNT; + // get a page, max 4095 bytes of data... + if (!(page = get_free_page (GFP_KERNEL))) { + MOD_DEC_USE_COUNT; + return -ENOMEM; + } + + len = 0; + index = (*pos)++; + + switch (index) { + case 0: + len += sprintf ((char *) page + len, "USBD Status\n"); + break; + + case 1: + len += sprintf ((char *) page + len, "Cable: %s\n", udc_connected ()? "Plugged" : "Unplugged"); + break; + + case 2: + if ((device = device_array[0])) { + struct usb_function_instance * function_instance; + struct usb_bus_instance * bus; + + len += sprintf ((char *) page + len, "Device status: %s\n", USBD_DEVICE_STATUS(device->status)); + len += sprintf ((char *) page + len, "Device state: %s\n", USBD_DEVICE_STATE(device->device_state)); + + if ((function_instance = device->function_instance_array+0)) { + len += sprintf ((char *) page + len, "Function: %s\n", + function_instance->function_driver->name); + } + if ((bus= device->bus)) { + len += sprintf ((char *) page + len, "Bus interface: %s\n", + bus->driver->name); + } + } + break; + + default: + break; + } + + + if (len > count) { + len = -EINVAL; + } else if (len > 0 && copy_to_user (buf, (char *) page, len)) { + len = -EFAULT; + } + free_page (page); + MOD_DEC_USE_COUNT; + return len; +} + +/* * + * usbd_proc_write - implement proc file system write. + * @file + * @buf + * @count + * @pos + * + * Proc file system write function, used to signal monitor actions complete. + * (Hotplug script (or whatever) writes to the file to signal the completion + * of the script.) An ugly hack. + */ +static ssize_t usbd_proc_write (struct file *file, const char *buf, size_t count, loff_t * pos) +{ + struct usb_device_instance *device; + size_t n = count; + char command[64]; + char *cp = command; + int i = 0; + + MOD_INC_USE_COUNT; + //printk(KERN_DEBUG "%s: count=%u\n",__FUNCTION__,count); + while ((n > 0) && (i < 64)) { + // Not too efficient, but it shouldn't matter + if (copy_from_user (cp++, buf + (count - n), 1)) { + count = -EFAULT; + break; + } + *cp = '\0'; + i++; + n -= 1; + //printk(KERN_DEBUG "%s: %u/%u %02x\n",__FUNCTION__,count-n,count,c); + } + if (!strncmp (command, "plug", 4)) { + udc_connect (); + } + else if (!strncmp (command, "unplug", 6)) { + udc_disconnect (); + if ((device = device_array[0])) { + usbd_device_event (device, DEVICE_RESET, 0); + } + } + MOD_DEC_USE_COUNT; + return (count); +} + +static struct file_operations usbd_proc_operations_functions = { + read:usbd_proc_read, + write:usbd_proc_write, +}; + +#endif + +/* Module Init - module loading init and exit routines *************************************** */ + +/* We must be able to use the real bi_init() and bi_exit() functions + * from here and the power management event function. + * + * We handle power management registration issues from here. + * + */ + +/* bi_modinit - commission bus interface driver + */ +static int __init bi_modinit (void) +{ + extern const char __usbd_module_info[]; + struct usb_bus_instance *bus; + struct usb_device_instance *device; + struct bi_data *data; + + + printk (KERN_INFO "%s (dbg=\"%s\")\n", __usbd_module_info, dbg ? dbg : ""); + + // process debug options + if (0 != scan_debug_options ("usbd-bi", dbg_table, dbg)) { + return -EINVAL; + } + // check if we can see the UDC + if (bi_udc_init ()) { + return -EINVAL; + } + // allocate a bi private data structure + if ((data = kmalloc (sizeof (struct bi_data), GFP_KERNEL)) == NULL) { + bi_udc_exit (); + return -EINVAL; + } + memset (data, 0, sizeof (struct bi_data)); + + // register this bus interface driver and create the device driver instance + if ((bus = usbd_register_bus (&bi_driver)) == NULL) { + kfree (data); + bi_udc_exit (); + return -EINVAL; + } + + bus->privdata = data; + + // see if we can scrounge up something to set a sort of unique device address + if (!udc_serial_init (bus)) { + dbg_init (1, "serial: %s %04x\n", bus->serial_number_str, bus->serial_number); + } + + + if ((device = usbd_register_device (NULL, bus, 8)) == NULL) { + usbd_deregister_bus (bus); + kfree (data); + bi_udc_exit (); + return -EINVAL; + } +#if defined(CONFIG_PM) && !defined(CONFIG_USBD_MONITOR) && !defined(CONFIG_USBD_MONITOR_MODULE) + // register with power management + pm_dev = pm_register (PM_USB_DEV, PM_SYS_UNKNOWN, bi_pm_event); + pm_dev->data = device; +#endif + + bus->device = device; + device_array[0] = device; + + // initialize the device + { + struct usb_endpoint_instance *endpoint = device->bus->endpoint_array + 0; + + // setup endpoint zero + + endpoint->endpoint_address = 0; + + endpoint->tx_attributes = 0; + endpoint->tx_transferSize = 255; + endpoint->tx_packetSize = udc_ep0_packetsize (); + + endpoint->rcv_attributes = 0; + endpoint->rcv_transferSize = 0x8; + endpoint->rcv_packetSize = udc_ep0_packetsize (); + + udc_setup_ep (device, 0, endpoint); + } + + // hopefully device enumeration will finish this process + printk(KERN_INFO"bi_modinit: call udc_startup_events\n"); + udc_startup_events (device); + +#if defined(CONFIG_PM) && !defined(CONFIG_USBD_MONITOR) && !defined(CONFIG_USBD_MONITOR_MODULE) + dbg_pm (0, "pm_dev->callback#%p", pm_dev->callback); + if (!udc_connected ()) { + /* Fake a call from the PM system to suspend the UDC until it + is needed (cable connect, etc) */ + (void) bi_pm_event (pm_dev, PM_SUSPEND, NULL); + /* There appears to be no constant for this, but inspection + of arch/arm/mach-l7200/apm.c:send_event() shows that the + suspended state is 3 (i.e. pm_send_all(PM_SUSPEND, (void *)3)) + corresponding to ACPI_D3. */ + pm_dev->state = 3; + } +#endif + if (dbgflg_usbdbi_tick > 0) { + // start ticker + ticker_kickoff (); + } + +#ifdef CONFIG_USBD_PROCFS + { + struct proc_dir_entry *p; + + // create proc filesystem entries + if ((p = create_proc_entry ("usbd", 0, 0)) == NULL) { + return -ENOMEM; + } + p->proc_fops = &usbd_proc_operations_functions; + } +#endif + dbgLEAVE (dbgflg_usbdbi_init, 1); + return 0; +} + +/* bi_modexit - decommission bus interface driver + */ +static void __exit bi_modexit (void) +{ + struct usb_bus_instance *bus; + struct usb_device_instance *device; + struct bi_data *data; + + dbgENTER (dbgflg_usbdbi_init, 1); + +#ifdef CONFIG_USBD_PROCFS + remove_proc_entry ("usbd", NULL); +#endif + + udc_disconnect (); + udc_disable (); + + if ((device = device_array[0])) { + + // XXX moved to usbd_deregister_device() + //device->status = USBD_CLOSING; + + // XXX XXX + if (dbgflg_usbdbi_tick > 0) { + ticker_killoff (); + } + + bus = device->bus; + data = bus->privdata; + + // XXX + usbd_device_event (device, DEVICE_RESET, 0); + usbd_device_event (device, DEVICE_POWER_INTERRUPTION, 0); + usbd_device_event (device, DEVICE_HUB_RESET, 0); + + dbg_init (1, "DEVICE_DESTROY"); + usbd_device_event (device, DEVICE_DESTROY, 0); + + + dbg_init (1, "DISABLE ENDPOINTS"); + bi_disable_endpoints (device); + + //dbg_init(1,"UDC_DISABLE"); + //udc_disable(); + + dbg_init (1, "BI_UDC_EXIT"); + bi_udc_exit (); + + device_array[0] = NULL; + //bus->privdata = NULL; // XXX moved to usbd-bus.c usbd_deregister_device() + + +#if defined(CONFIG_PM) && !defined(CONFIG_USBD_MONITOR) && !defined(CONFIG_USBD_MONITOR_MODULE) + dbg_init (1, "PM_UNREGISTER(pm_dev#%p)", pm_dev); + if (pm_dev) { + pm_unregister_all(bi_pm_event); + } +#endif + dbg_init (1, "DEREGISTER DEVICE"); + usbd_deregister_device (device); + bus->device = NULL; + + dbg_init (1, "kfree(data#%p)", data); + if (data) { + kfree (data); + } + + if (bus->serial_number_str) { + kfree (bus->serial_number_str); + } + + dbg_init (1, "DEREGISTER BUS"); + usbd_deregister_bus (bus); + + } else { + dbg_init (0, "device is NULL"); + } + dbg_init (1, "BI_EXIT"); + bi_exit (); + dbgLEAVE (dbgflg_usbdbi_init, 1); +} + + + +/* ticker */ + +/* Clock Tick Debug support ****************************************************************** */ + + +#define RETRYTIME 10 + +int ticker_terminating; +int ticker_timer_set; + +unsigned int udc_interrupts; +unsigned int udc_interrupts_last; + +static DECLARE_MUTEX_LOCKED (ticker_sem_start); +static DECLARE_MUTEX_LOCKED (ticker_sem_work); + +void ticker_tick (unsigned long data) +{ + ticker_timer_set = 0; + up (&ticker_sem_work); +} + +void udc_ticker_poke (void) +{ + up (&ticker_sem_work); +} + +int ticker_thread (void *data) +{ + struct timer_list ticker; + + // detach + + lock_kernel (); + exit_mm (current); + exit_files (current); + exit_fs (current); + + // setsid equivalent, used at start of kernel thread, no error checks needed, or at least none made :). + current->leader = 1; + current->session = current->pgrp = current->pid; + current->tty = NULL; + current->tty_old_pgrp = 0; + + // Name this thread + sprintf (current->comm, "usbd-bi"); + + // setup signal handler + current->exit_signal = SIGCHLD; + spin_lock (¤t->sigmask_lock); + flush_signals (current); + spin_unlock (¤t->sigmask_lock); + + // XXX Run at a high priority, ahead of sync and friends + // current->nice = -20; + current->policy = SCHED_OTHER; + + unlock_kernel (); + + // setup timer + init_timer (&ticker); + ticker.data = 0; + ticker.function = ticker_tick; + + // let startup continue + up (&ticker_sem_start); + + // process loop + for (ticker_timer_set = ticker_terminating = 0; !ticker_terminating;) { + + char buf[100]; + char *cp; + + if (!ticker_timer_set) { + mod_timer (&ticker, jiffies + HZ * RETRYTIME); + } + // wait for someone to tell us to do something + down (&ticker_sem_work); + + if (udc_interrupts != udc_interrupts_last) { + dbg_tick (3, "--------------"); + } + // do some work + memset (buf, 0, sizeof (buf)); + cp = buf; + + if (dbgflg_usbdbi_tick) { + unsigned long flags; + local_irq_save (flags); + dbg_tick (2, "[%d]", udc_interrupts); + udc_regs (); + local_irq_restore (flags); + } + +#if 0 + // XXX +#if defined(CONFIG_SA1110_CALYPSO) && defined(CONFIG_PM) && defined(CONFIG_USBD_TRAFFIC_KEEPAWAKE) + /* Check for rx/tx activity, and reset the sleep timer if present */ + if (device->usbd_rxtx_timestamp != device->usbd_last_rxtx_timestamp) { + extern void resetSleepTimer (void); + dbg_tick (7, "resetting sleep timer"); + resetSleepTimer (); + device->usbd_last_rxtx_timestamp = device->usbd_rxtx_timestamp; + } +#endif +#endif +#if 0 + /* Check for TX endpoint stall. If the endpoint has + stalled, we have probably run into the DMA/TCP window + problem, and the only thing we can do is disconnect + from the bus, then reconnect (and re-enumerate...). */ + if (reconnect > 0 && 0 >= --reconnect) { + dbg_init (0, "TX stall disconnect finished"); + udc_connect (); + } else if (0 != USBD_STALL_TIMEOUT_SECONDS) { + // Stall watchdog unleashed + unsigned long now, tx_waiting; + now = jiffies; + tx_waiting = (now - tx_queue_head_timestamp (now)) / HZ; + if (tx_waiting > USBD_STALL_TIMEOUT_SECONDS) { + /* The URB at the head of the queue has waited too long */ + reconnect = USBD_STALL_DISCONNECT_DURATION; + dbg_init (0, "TX stalled, disconnecting for %d seconds", reconnect); + udc_disconnect (); + } + } +#endif + } + + // remove timer + del_timer (&ticker); + + // let the process stopping us know we are done and return + up (&ticker_sem_start); + return 0; +} + +/** + * kickoff_thread - start management thread + */ +void ticker_kickoff (void) +{ + ticker_terminating = 0; + kernel_thread (&ticker_thread, NULL, 0); + down (&ticker_sem_start); +} + +/** + * killoff_thread - stop management thread + */ +void ticker_killoff (void) +{ + if (!ticker_terminating) { + ticker_terminating = 1; + up (&ticker_sem_work); + down (&ticker_sem_start); + } +} + + +/* module */ + +module_init (bi_modinit); +module_exit (bi_modexit); diff -uNr linux.org/drivers/usb/device/bi/usbd-bi.h linux/drivers/usb/device/bi/usbd-bi.h --- linux.org/drivers/usb/device/bi/usbd-bi.h Thu Jan 1 08:00:00 1970 +++ linux/drivers/usb/device/bi/usbd-bi.h Thu May 15 15:54:48 2003 @@ -0,0 +1,302 @@ +/* + * linux/drivers/usbd/usbd-bi.h + * + * Copyright (c) 2000, 2001, 2002 Lineo + * Copyright (c) 2001 Hewlett Packard + * + * By: + * Stuart Lynne , + * Tom Rushworth , + * Bruce Balden + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + * + */ + +#define MAX_DEVICES 1 + +/* struct udc_bi_data + * + * private data structure for this bus interface driver + */ +struct bi_data { + int num; + struct tq_struct cradle_bh; +}; + + +extern unsigned int udc_interrupts; + +extern int dbgflg_usbdbi_init; +extern int dbgflg_usbdbi_intr; +extern int dbgflg_usbdbi_tick; +extern int dbgflg_usbdbi_usbe; +extern int dbgflg_usbdbi_rx; +extern int dbgflg_usbdbi_tx; +extern int dbgflg_usbdbi_dma_flg; +extern int dbgflg_usbdbi_setup; +extern int dbgflg_usbdbi_ep0; +extern int dbgflg_usbdbi_udc; +extern int dbgflg_usbdbi_stall; +extern int dbgflg_usbdbi_pm; +extern int dbgflg_usbdbi_pur; + +#define dbg_init(lvl,fmt,args...) dbgPRINT(dbgflg_usbdbi_init,lvl,fmt,##args) +#define dbg_intr(lvl,fmt,args...) dbgPRINT(dbgflg_usbdbi_intr,lvl,fmt,##args) +#define dbg_tick(lvl,fmt,args...) dbgPRINT(dbgflg_usbdbi_tick,lvl,fmt,##args) +#define dbg_usbe(lvl,fmt,args...) dbgPRINT(dbgflg_usbdbi_usbe,lvl,fmt,##args) +#define dbg_rx(lvl,fmt,args...) dbgPRINT(dbgflg_usbdbi_rx,lvl,fmt,##args) +#define dbg_tx(lvl,fmt,args...) dbgPRINT(dbgflg_usbdbi_tx,lvl,fmt,##args) +#define dbg_dmaflg(lvl,fmt,args...) dbgPRINT(dbgflg_usbdbi_dma_flg,lvl,fmt,##args) +#define dbg_setup(lvl,fmt,args...) dbgPRINT(dbgflg_usbdbi_setup,lvl,fmt,##args) +#define dbg_ep0(lvl,fmt,args...) dbgPRINT(dbgflg_usbdbi_ep0,lvl,fmt,##args) +#define dbg_udc(lvl,fmt,args...) dbgPRINT(dbgflg_usbdbi_udc,lvl,fmt,##args) +#define dbg_stall(lvl,fmt,args...) dbgPRINT(dbgflg_usbdbi_stall,lvl,fmt,##args) +#define dbg_pm(lvl,fmt,args...) dbgPRINT(dbgflg_usbdbi_pm,lvl,fmt,##args) +#define dbg_pur(lvl,fmt,args...) dbgPRINT(dbgflg_usbdbi_pur,lvl,fmt,##args) + + + +/** + * udc_start_in_irq - start transmit + * @endpoint: + * + * Called with interrupts disabled. + */ +void udc_start_in_irq (struct usb_endpoint_instance *endpoint); + +/** + * udc_stall_ep - stall endpoint + * @ep: physical endpoint + * + * Stall the endpoint. + */ +void udc_stall_ep (unsigned int ep); + +/** + * udc_reset_ep - reset endpoint + * @ep: physical endpoint + * reset the endpoint. + * + * returns : 0 if ok, -1 otherwise + */ +void udc_reset_ep (unsigned int ep); + +/** + * udc_endpoint_halted - is endpoint halted + * @ep: + * + * Return non-zero if endpoint is halted + */ +int udc_endpoint_halted (unsigned int ep); + +/** + * udc_set_address - set the USB address for this device + * @address: + * + * Called from control endpoint function after it decodes a set address setup packet. + */ +void udc_set_address (unsigned char address); + +/** + * udc_serial_init - set a serial number if available + */ +int __init udc_serial_init (struct usb_bus_instance *); + +/** + * udc_max_endpoints - max physical endpoints + * + * Return number of physical endpoints. + */ +int udc_max_endpoints (void); + +/** + * udc_check_ep - check logical endpoint + * @lep: + * @packetsize: + * + * Return physical endpoint number to use for this logical endpoint or zero if not valid. + */ +int udc_check_ep (int logical_endpoint, int packetsize); + +/** + * udc_set_ep - setup endpoint + * @ep: + * @endpoint: + * + * Associate a physical endpoint with endpoint_instance + */ +void udc_setup_ep (struct usb_device_instance *device, unsigned int ep, + struct usb_endpoint_instance *endpoint); + + +/** + * udc_disable_ep - disable endpoint + * @ep: + * + * Disable specified endpoint + */ +void udc_disable_ep (unsigned int ep); + + +/** + * udc_connected - is the USB cable connected + * + * Return non-zeron if cable is connected. + */ +int udc_connected (void); + + +/** + * udc_connect - enable pullup resistor + * + * Turn on the USB connection by enabling the pullup resistor. + */ +void udc_connect (void); + + +/** + * udc_disconnect - disable pullup resistor + * + * Turn off the USB connection by disabling the pullup resistor. + */ +void udc_disconnect (void); + +/** + * udc_all_interrupts - enable interrupts + * + * Switch on UDC interrupts. + * + */ +void udc_all_interrupts (struct usb_device_instance *); + + +/** + * udc_suspended_interrupts - enable suspended interrupts + * + * Switch on only UDC resume interrupt. + * + */ +void udc_suspended_interrupts (struct usb_device_instance *); + +/** + * udc_disable_interrupts - disable interrupts. + * + * switch off interrupts + */ +void udc_disable_interrupts (struct usb_device_instance *); + +/** + * udc_ep0_packetsize - return ep0 packetsize + */ +int udc_ep0_packetsize (void); + +/** + * udc_enable - enable the UDC + * + * Switch on the UDC + */ +void udc_enable (struct usb_device_instance *device); + +/** + * udc_disable - disable the UDC + * + * Switch off the UDC + */ +void udc_disable (void); + +/** + * udc_startup - allow udc code to do any additional startup + */ +void udc_startup (struct usb_device_instance *device); + +/** + * udc_startup - allow udc code to do any additional startup + */ +void udc_startup_events (struct usb_device_instance *); + +/** + * udc_init - initialize USB Controller + * + * Get ready to use the USB Controller. + * + * Register an interrupt handler and IO region. Return non-zero for error. + */ +int udc_init (void); + +/** + * udc_exit - Stop using the USB Controller + * + * Stop using the USB Controller. + * + * Shutdown and free dma channels, de-register the interrupt handler. + */ +void udc_exit (void); + +/** + * udc_regs - dump registers + * + * Dump registers with printk + */ +void udc_regs (void); + +/** + * udc_name - return name of USB Device Controller + */ +char *udc_name (void); + +/** + * udc_request_udc_irq - request UDC interrupt + * + * Return non-zero if not successful. + */ +int udc_request_udc_irq (void); + +/** + * udc_request_cable_irq - request Cable interrupt + * + * Return non-zero if not successful. + */ +int udc_request_cable_irq (void); + +/** + * udc_request_udc_io - request UDC io region + * + * Return non-zero if not successful. + */ +int udc_request_io (void); + +/** + * udc_release_udc_irq - release UDC irq + */ +void udc_release_udc_irq (void); + +/** + * udc_release_cable_irq - release Cable irq + */ +void udc_release_cable_irq (void); + +/** + * udc_release_io - release UDC io region + */ +void udc_release_io (void); + +/** + * udc_connect_event - called from cradle interrupt handler + * @connected - true if we are in cradle + */ +void udc_cable_event (void); + +void udc_ticker_poke (void); diff -uNr linux.org/drivers/usb/device/ep0.c linux/drivers/usb/device/ep0.c --- linux.org/drivers/usb/device/ep0.c Thu Jan 1 08:00:00 1970 +++ linux/drivers/usb/device/ep0.c Thu May 15 15:54:48 2003 @@ -0,0 +1,654 @@ +/* + * linux/drivers/usbd/ep0.c + * + * Copyright (c) 2000, 2001, 2002 Lineo + * Copyright (c) 2001 Hewlett Packard + * + * By: + * Stuart Lynne , + * Tom Rushworth , + * Bruce Balden + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + * + */ + +/* + * This is the builtin ep0 control function. It implements all required functionality + * for responding to control requests (SETUP packets). + * + * XXX + * + * Currently we do not pass any SETUP packets (or other) to the configured + * function driver. This may need to change. + * + * XXX + */ + +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "usbd.h" +#include "usbd-debug.h" +#include "usbd-func.h" +#include "usbd-bus.h" +#include "usbd-inline.h" + +extern int dbgflg_usbdcore_ep0; +#define dbg_ep0(lvl,fmt,args...) dbgPRINT(dbgflg_usbdcore_ep0,lvl,fmt,##args) + +/* EP0 Configuration Set ********************************************************************* */ + +#if 0 + +// XXX ep0 does not have or need any configuration + +static struct usb_endpoint_description ep0_endpoints[] = { + {bEndpointAddress: 0x00, attributes: 0, max_size: 8, polling_interval:0} +}; + + +static struct usb_interface_description ep0_interfaces[] = { + {class: 0, sub_class: 0, protocol:0, + endpoints:sizeof (ep0_endpoints) / sizeof (struct usb_endpoint_description), + endpoint_list:ep0_endpoints}, +}; + +struct usb_configuration_description ep0_description[] = { + {name: "EP0", attributes: 0, max_power:0, + interfaces:sizeof (ep0_interfaces) / + sizeof (struct usb_interface_description), + interface_list:ep0_interfaces}, +}; + +#endif + +/** + * ep0_event - respond to USB event + * @device: + * @event: + * + * Called by lower layers to indicate some event has taken place. Typically + * this is reset, suspend, resume and close. + */ +static void ep0_event (struct usb_device_instance *device, + usb_device_event_t event, int dummy /* to make fn signature correct */ ) +{ + // do nothing +} + +/** + * ep0_get_status - fill in URB data with appropriate status + * @device: + * @urb: + * @index: + * @requesttype: + * + */ +static int ep0_get_status (struct usb_device_instance *device, struct urb *urb, int index, int requesttype) +{ + char *cp; + + urb->actual_length = 2; + cp = urb->buffer; + cp[0] = cp[1] = 0; + + switch (requesttype) { + case USB_REQ_RECIPIENT_DEVICE: + cp[0] = USB_STATUS_SELFPOWERED; + break; + case USB_REQ_RECIPIENT_INTERFACE: + break; + case USB_REQ_RECIPIENT_ENDPOINT: + cp[0] = usbd_endpoint_halted (device, index); + break; + case USB_REQ_RECIPIENT_OTHER: + urb->actual_length = 0; + default: + break; + } + dbg_ep0(2, "%02x %02x", cp[0], cp[1]); + return 0; +} + +/** + * ep0_get_one + * @device: + * @urb: + * @result: + * + * Set a single byte value in the urb send buffer. Return non-zero to signal + * a request error. + */ +static int ep0_get_one (struct usb_device_instance *device, struct urb *urb, __u8 result) +{ + urb->actual_length = 1; // XXX 2? + ((char *) urb->buffer)[0] = result; + return 0; +} + +/** + * copy_config + * @urb: pointer to urb + * @data: pointer to configuration data + * @length: length of data + * + * Copy configuration data to urb transfer buffer if there is room for it. + */ +static void copy_config (struct urb *urb, void *data, int max_length, int max_buf) +{ + int available; + int length; + + //dbg_ep0(3, "-> actual: %d buf: %d max_buf: %d max_length: %d data: %p", + // urb->actual_length, urb->buffer_length, max_buf, max_length, data); + + if (!data) { + dbg_ep0 (1, "data is NULL"); + return; + } + if (!(length = *(unsigned char *) data)) { + dbg_ep0 (1, "length is zero"); + return; + } + + if (length > max_length) { + dbg_ep0 (1, "length: %d >= max_length: %d", length, max_length); + return; + } + //dbg_ep0(1, " actual: %d buf: %d max_buf: %d max_length: %d length: %d", + // urb->actual_length, urb->buffer_length, max_buf, max_length, length); + + if ((available = /*urb->buffer_length */ max_buf - urb->actual_length) <= 0) { + return; + } + //dbg_ep0(1, "actual: %d buf: %d max_buf: %d length: %d available: %d", + // urb->actual_length, urb->buffer_length, max_buf, length, available); + + if (length > available) { + length = available; + } + //dbg_ep0(1, "actual: %d buf: %d max_buf: %d length: %d available: %d", + // urb->actual_length, urb->buffer_length, max_buf, length, available); + + memcpy (urb->buffer + urb->actual_length, data, length); + urb->actual_length += length; + + //dbg_ep0(3, "<- actual: %d buf: %d max_buf: %d max_length: %d available: %d", + // urb->actual_length, urb->buffer_length, max_buf, max_length, available); +} + +/** + * ep0_get_descriptor + * @device: + * @urb: + * @max: + * @descriptor_type: + * @index: + * + * Called by ep0_rx_process for a get descriptor device command. Determine what + * descriptor is being requested, copy to send buffer. Return zero if ok to send, + * return non-zero to signal a request error. + */ +static int ep0_get_descriptor (struct usb_device_instance *device, struct urb *urb, int max, + int descriptor_type, int index) +{ + int port = 0; // XXX compound device + char *cp; + + //dbg_ep0(3, "max: %x type: %x index: %x", max, descriptor_type, index); + + if (!urb || !urb->buffer || !urb->buffer_length || (urb->buffer_length < 255)) { + dbg_ep0 (2, "invalid urb %p", urb); + return -EINVAL; + } + + // setup tx urb + urb->actual_length = 0; + cp = urb->buffer; + + dbg_ep0(2, "%s", USBD_DEVICE_DESCRIPTORS(descriptor_type)); + + switch (descriptor_type) { + case USB_DESCRIPTOR_TYPE_DEVICE: + { + struct usb_device_descriptor *device_descriptor; + + if (!(device_descriptor = usbd_device_device_descriptor (device, port))) { + return -EINVAL; + } + // copy descriptor for this device + copy_config (urb, device_descriptor, sizeof (struct usb_device_descriptor), + max); + + // correct the correct control endpoint 0 max packet size into the descriptor + device_descriptor = (struct usb_device_descriptor *) urb->buffer; + device_descriptor->bMaxPacketSize0 = + urb->device->bus->driver->maxpacketsize; + + } + //dbg_ep0(3, "copied device configuration, actual_length: %x", urb->actual_length); + break; + + case USB_DESCRIPTOR_TYPE_CONFIGURATION: + { + int bNumInterface; + struct usb_configuration_descriptor *configuration_descriptor; + struct usb_device_descriptor *device_descriptor; + if (!(device_descriptor = usbd_device_device_descriptor (device, port))) { + return -EINVAL; + } + //dbg_ep0(2, "%d %d", index, device_descriptor->bNumConfigurations); + if (index > device_descriptor->bNumConfigurations) { + dbg_ep0 (0, "index too large: %d > %d", index, device_descriptor->bNumConfigurations); + return -EINVAL; + } + + if (! + (configuration_descriptor = + usbd_device_configuration_descriptor (device, port, index))) { + dbg_ep0 (0, "usbd_device_configuration_descriptor failed: %d", index); + return -EINVAL; + } + copy_config (urb, configuration_descriptor, + sizeof (struct usb_configuration_descriptor), max); + + + // iterate across interfaces for specified configuration + for (bNumInterface = 0; + bNumInterface < configuration_descriptor->bNumInterfaces; + bNumInterface++) { + + int bAlternateSetting; + struct usb_interface_instance *interface_instance; + + //dbg_ep0(3, "[%d] bNumInterfaces: %d", bNumInterface, configuration_descriptor->bNumInterfaces); + + if (! + (interface_instance = + usbd_device_interface_instance (device, port, index, + bNumInterface))) { + dbg_ep0 (3, "[%d] interface_instance NULL", bNumInterface); + return -EINVAL; + } + // iterate across interface alternates + for (bAlternateSetting = 0; + bAlternateSetting < interface_instance->alternates; + bAlternateSetting++) { + int class; + int bNumEndpoint; + struct usb_interface_descriptor *interface_descriptor; + + struct usb_alternate_instance *alternate_instance; + + //dbg_ep0(3, "[%d:%d] alternates: %d", bNumInterface, bAlternateSetting, interface_instance->alternates); + + if (! + (alternate_instance = + usbd_device_alternate_instance (device, port, index, bNumInterface, + bAlternateSetting))) + { + dbg_ep0 (3, "[%d] alternate_instance NULL", + bNumInterface); + return -EINVAL; + } + // copy descriptor for this interface + copy_config (urb, alternate_instance->interface_descriptor, + sizeof (struct usb_interface_descriptor), max); + + //dbg_ep0(3, "[%d:%d] classes: %d endpoints: %d", bNumInterface, bAlternateSetting, + // alternate_instance->classes, alternate_instance->endpoints); + + // iterate across classes for this alternate interface + for (class = 0; class < alternate_instance->classes; + class++) { + struct usb_class_descriptor *class_descriptor; + //dbg_ep0(3, "[%d:%d:%d] classes: %d", bNumInterface, bAlternateSetting, + // class, alternate_instance->classes); + if (!(class_descriptor = + usbd_device_class_descriptor_index (device, + port, + index, + bNumInterface, + bAlternateSetting, + class))) { + dbg_ep0 (3, "[%d] class NULL", class); + return -EINVAL; + } + // copy descriptor for this class + copy_config (urb, class_descriptor, + sizeof (struct usb_class_descriptor), + max); + } + + // iterate across endpoints for this alternate interface + interface_descriptor = + alternate_instance->interface_descriptor; + for (bNumEndpoint = 0; + bNumEndpoint < alternate_instance->endpoints; + bNumEndpoint++) { + struct usb_endpoint_descriptor *endpoint_descriptor; + //dbg_ep0(3, "[%d:%d:%d] endpoint: %d", bNumInterface, bAlternateSetting, + // bNumEndpoint, interface_descriptor->bNumEndpoints); + if (!(endpoint_descriptor = + usbd_device_endpoint_descriptor_index (device, + port, + index, + bNumInterface, + bAlternateSetting, + bNumEndpoint))) + { + dbg_ep0 (3, "[%d] endpoint NULL", + bNumEndpoint); + return -EINVAL; + } + // copy descriptor for this endpoint + copy_config (urb, endpoint_descriptor, + sizeof (struct + usb_endpoint_descriptor), max); + } + } + } + //dbg_ep0(3, "lengths: %d %d", le16_to_cpu(configuration_descriptor->wTotalLength), urb->actual_length); + } + break; + + case USB_DESCRIPTOR_TYPE_STRING: + { + struct usb_string_descriptor *string_descriptor; + if (!(string_descriptor = usbd_get_string (index))) { + return -EINVAL; + } + //dbg_ep0(3, "string_descriptor: %p", string_descriptor); + copy_config (urb, string_descriptor, string_descriptor->bLength, max); + } + break; + case USB_DESCRIPTOR_TYPE_INTERFACE: + return -EINVAL; + case USB_DESCRIPTOR_TYPE_ENDPOINT: + return -EINVAL; + default: + return -EINVAL; + } + + + //dbg_ep0(1, "urb: buffer: %p buffer_length: %2d actual_length: %2d packet size: %2d", + // urb->buffer, urb->buffer_length, urb->actual_length, device->bus->endpoint_array[0].tx_packetSize); +/* + if ((urb->actual_length < max) && !(urb->actual_length % device->bus->endpoint_array[0].tx_packetSize)) { + dbg_ep0(0, "adding null byte"); + urb->buffer[urb->actual_length++] = 0; + dbg_ep0(0, "urb: buffer_length: %2d actual_length: %2d packet size: %2d", + urb->buffer_length, urb->actual_length device->bus->endpoint_array[0].tx_packetSize); + } +*/ + return 0; + +} + +/** + * ep0_recv_setup - called to indicate URB has been received + * @urb: pointer to struct urb + * + * Check if this is a setup packet, process the device request, put results + * back into the urb and return zero or non-zero to indicate success (DATA) + * or failure (STALL). + * + */ +static int ep0_recv_setup (struct urb *urb) +{ + //struct usb_device_request *request = urb->buffer; + //struct usb_device_instance *device = urb->device; + + struct usb_device_request *request; + struct usb_device_instance *device; + int address; + + if (!urb || !urb->device) { + dbg_ep0 (3, "invalid URB %p", urb); + return -EINVAL; + } + + request = &urb->device_request; + device = urb->device; + + //dbg_ep0(3, "urb: %p device: %p", urb, urb->device); + + + //dbg_ep0(2, "- - - - - - - - - -"); + + dbg_ep0(2, "bmRequestType:%02x bRequest:%02x wValue:%04x wIndex:%04x wLength:%04x %s", + request->bmRequestType, + request->bRequest, + le16_to_cpu(request->wValue), + le16_to_cpu(request->wIndex), + le16_to_cpu(request->wLength), + USBD_DEVICE_REQUESTS(request->bRequest)); + + // handle USB Standard Request (c.f. USB Spec table 9-2) + if ((request->bmRequestType & USB_REQ_TYPE_MASK) != 0) { + dbg_ep0 (1, "non standard request: %x", request->bmRequestType & USB_REQ_TYPE_MASK); + return 0; // XXX + } +#if 0 + // check that this is for endpoint zero and is a SETUP packet + if (urb->endpoint->endpoint_address != 0) { + dbg_ep0 (1, "endpoint non zero: %d or not setup packet: %x", + urb->endpoint->endpoint_address, urb->pid); + return -EINVAL; + } +#endif + + switch (device->device_state) { + case STATE_CREATED: + case STATE_ATTACHED: + case STATE_POWERED: + dbg_ep0 (1, "request %s not allowed in this state: %s", + USBD_DEVICE_REQUESTS(request->bRequest), + usbd_device_states[device->device_state]); + return -EINVAL; + + case STATE_INIT: + case STATE_DEFAULT: + switch (request->bRequest) { + case USB_REQ_GET_STATUS: + case USB_REQ_GET_INTERFACE: + case USB_REQ_SYNCH_FRAME: // XXX should never see this (?) + case USB_REQ_CLEAR_FEATURE: + case USB_REQ_SET_FEATURE: + case USB_REQ_SET_DESCRIPTOR: + // case USB_REQ_SET_CONFIGURATION: + case USB_REQ_SET_INTERFACE: + dbg_ep0 (1, "request %s not allowed in DEFAULT state: %s", + USBD_DEVICE_REQUESTS(request->bRequest), usbd_device_states[device->device_state]); + return -EINVAL; + + case USB_REQ_SET_CONFIGURATION: + case USB_REQ_SET_ADDRESS: + case USB_REQ_GET_DESCRIPTOR: + case USB_REQ_GET_CONFIGURATION: + break; + } + case STATE_ADDRESSED: + case STATE_CONFIGURED: + break; + case STATE_UNKNOWN: + dbg_ep0 (1, "request %s not allowed in UNKNOWN state: %s", + USBD_DEVICE_REQUESTS(request->bRequest), usbd_device_states[device->device_state]); + return -EINVAL; + } + + // handle all requests that return data (direction bit set on bm RequestType) + if ((request->bmRequestType & USB_REQ_DIRECTION_MASK)) { + + //dbg_ep0(3, "Device-to-Host"); + + switch (request->bRequest) { + + case USB_REQ_GET_STATUS: + return ep0_get_status (device, urb, request->wIndex, request->bmRequestType & USB_REQ_RECIPIENT_MASK); + + case USB_REQ_GET_DESCRIPTOR: + return ep0_get_descriptor (device, urb, + le16_to_cpu (request->wLength), + le16_to_cpu (request->wValue >> 8), + le16_to_cpu (request->wValue) & 0xff); + + case USB_REQ_GET_CONFIGURATION: + return ep0_get_one (device, urb, device->configuration); + + case USB_REQ_GET_INTERFACE: + return ep0_get_one (device, urb, device->alternate); + + case USB_REQ_SYNCH_FRAME: // XXX should never see this (?) + return -EINVAL; + + case USB_REQ_CLEAR_FEATURE: + case USB_REQ_SET_FEATURE: + case USB_REQ_SET_ADDRESS: + case USB_REQ_SET_DESCRIPTOR: + case USB_REQ_SET_CONFIGURATION: + case USB_REQ_SET_INTERFACE: + return -EINVAL; + } + } + // handle the requests that do not return data + else { + + + //dbg_ep0(3, "Host-to-Device"); + switch (request->bRequest) { + + case USB_REQ_CLEAR_FEATURE: + case USB_REQ_SET_FEATURE: + dbg_ep0(0, "Host-to-Device"); + switch (request->bmRequestType & USB_REQ_RECIPIENT_MASK) { + case USB_REQ_RECIPIENT_DEVICE: + // XXX DEVICE_REMOTE_WAKEUP or TEST_MODE would be added here + // XXX fall through for now as we do not support either + case USB_REQ_RECIPIENT_INTERFACE: + case USB_REQ_RECIPIENT_OTHER: + dbg_ep0 (0, "request %s not", USBD_DEVICE_REQUESTS(request->bRequest)); + default: + return -EINVAL; + + case USB_REQ_RECIPIENT_ENDPOINT: + dbg_ep0(0, "ENDPOINT: %x", le16_to_cpu(request->wValue)); + if (le16_to_cpu(request->wValue) == USB_ENDPOINT_HALT) { + return usbd_device_feature (device, le16_to_cpu (request->wIndex) & 0x7f, + request->bRequest == USB_REQ_SET_FEATURE); + } + else { + dbg_ep0 (1, "request %s bad wValue: %04x", + USBD_DEVICE_REQUESTS(request->bRequest), le16_to_cpu(request->wValue)); + return -EINVAL ; + } + } + + case USB_REQ_SET_ADDRESS: + // check if this is a re-address, reset first if it is (this shouldn't be possible) + if (device->device_state != STATE_DEFAULT) { + dbg_ep0 (1, "set_address: %02x state: %s", + le16_to_cpu (request->wValue), usbd_device_states[device->device_state]); + return -EINVAL; + } + address = le16_to_cpu (request->wValue); + if ((address & 0x7f) != address) { + dbg_ep0 (1, "invalid address %04x %04x", address, address & 0x7f); + return -EINVAL; + } + device->address = address; + + //dbg_ep0(2, "address: %d %d %d", + // request->wValue, le16_to_cpu(request->wValue), device->address); + + usbd_device_event (device, DEVICE_ADDRESS_ASSIGNED, 0); + return 0; + + case USB_REQ_SET_DESCRIPTOR: // XXX should we support this? + dbg_ep0 (0, "set descriptor: NOT SUPPORTED"); + return -EINVAL; + + case USB_REQ_SET_CONFIGURATION: + // c.f. 9.4.7 - the top half of wValue is reserved + // + if ((device->configuration = le16_to_cpu (request->wValue) & 0x7f) != 0) { + // c.f. 9.4.7 - zero is the default or addressed state, in our case this + // is the same is configuration zero + device->configuration = 0; // TBR - ?????? + } + // reset interface and alternate settings + device->interface = device->alternate = 0; + + //dbg_ep0(2, "set configuration: %d", device->configuration); + usbd_device_event (device, DEVICE_CONFIGURED, 0); + return 0; + + case USB_REQ_SET_INTERFACE: + device->interface = le16_to_cpu (request->wIndex); + device->alternate = le16_to_cpu (request->wValue); + //dbg_ep0(2, "set interface: %d alternate: %d", device->interface, device->alternate); + usbd_device_event (device, DEVICE_SET_INTERFACE, 0); + return 0; + + case USB_REQ_GET_STATUS: + case USB_REQ_GET_DESCRIPTOR: + case USB_REQ_GET_CONFIGURATION: + case USB_REQ_GET_INTERFACE: + case USB_REQ_SYNCH_FRAME: // XXX should never see this (?) + return -EINVAL; + } + } + return -EINVAL; +} + +/** + * ep0_urb_sent - called to indicate URB transmit finished + * @urb: pointer to struct urb + * @rc: result + */ +static int ep0_urb_sent (struct urb *urb, int rc) +{ + //dbg_ep0(2, "%s rc: %d", urb->device->name, rc); + //usbd_dealloc_urb(urb); + return 0; +} + +static struct usb_function_operations ep0_ops = { + event: ep0_event, + recv_setup: ep0_recv_setup, + urb_sent: ep0_urb_sent, +}; + +struct usb_function_driver ep0_driver = { + name: "EP0", + ops: &ep0_ops, +}; diff -uNr linux.org/drivers/usb/device/hotplug.c linux/drivers/usb/device/hotplug.c --- linux.org/drivers/usb/device/hotplug.c Thu Jan 1 08:00:00 1970 +++ linux/drivers/usb/device/hotplug.c Thu May 15 15:54:48 2003 @@ -0,0 +1,92 @@ +/* + * linux/drivers/usbd/usbd-hotplug.c + * + * Copyright (c) 2000, 2001, 2002 Lineo + * Copyright (c) 2001 Hewlett Packard + * + * By: + * Stuart Lynne , + * Tom Rushworth , + * Bruce Balden + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + * + */ + + +#include +#include + +#include "usbd-export.h" +#include "usbd-build.h" +#include "usbd-module.h" + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + + + +#if defined(CONFIG_PM) +#include +#endif + +#ifdef CONFIG_HOTPLUG + +int hotplug (char *agent, char *interface, char *action) +{ + char *argv[3], *envp[5], ifname[12 + IFNAMSIZ], action_str[32]; + + if (!hotplug_path[0]) { + printk (KERN_ERR "hotplug: hotplug_path empty\n"); + return -EINVAL; + } + //printk(KERN_DEBUG "hotplug: %s\n", hotplug_path); + + if (in_interrupt ()) { + printk (KERN_ERR "hotplug: in_interrupt\n"); + return -EINVAL; + } + sprintf (ifname, "INTERFACE=%s", interface); + sprintf (action_str, "ACTION=%s", action); + + argv[0] = hotplug_path; + argv[1] = agent; + argv[2] = 0; + + envp[0] = "HOME=/"; + envp[1] = "PATH=/sbin:/bin:/usr/sbin:/usr/bin"; + envp[2] = ifname; + envp[3] = action_str; + envp[4] = 0; + + return call_usermodehelper (argv[0], argv, envp); +} + +#endif /* CONFIG_HOTPLUG */ diff -uNr linux.org/drivers/usb/device/hotplug.h linux/drivers/usb/device/hotplug.h --- linux.org/drivers/usb/device/hotplug.h Thu Jan 1 08:00:00 1970 +++ linux/drivers/usb/device/hotplug.h Thu May 15 15:54:48 2003 @@ -0,0 +1,28 @@ +/* + * linux/drivers/usbd/usbd-hotplug.h + * + * Copyright (c) 2000, 2001, 2002 Lineo + * Copyright (c) 2001 Hewlett Packard + * + * By: + * Stuart Lynne , + * Tom Rushworth , + * Bruce Balden + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + * + */ + +extern int hotplug (char *name, char *interface, char *action); diff -uNr linux.org/drivers/usb/device/net_fd/Config.help linux/drivers/usb/device/net_fd/Config.help --- linux.org/drivers/usb/device/net_fd/Config.help Thu Jan 1 08:00:00 1970 +++ linux/drivers/usb/device/net_fd/Config.help Thu May 15 15:54:48 2003 @@ -0,0 +1,83 @@ +CONFIG_USBD_NET + Enable the generic network function driver. This function is used + emulating a Linux network driver. It can be configured to be used + with various Linux host class drivers, custom Windows CDC ethernet + drivers and Windows RNDIS drivers. + +CONFIG_USBD_NET_VENDORID + Optionally specify the network USB Device Vendor ID. The top level + Vendor ID will be used if this is not specified. + +CONFIG_USBD_NET_PRODUCTID + Optionally specify the network USB Device Product ID. The top level + Vendor ID will be used if this is not specified. + +CONFIG_USBD_NET_IFNAME + Specify the prefix for the network interface name. E.g. "usb" or + "eth". + +CONFIG_USBD_NET_OUT_ENDPOINT + Specify the preferred OUT (received data) endpoint number. This is a + number from 0-15 and must be allowed by the bus interface device. + + Some devices such as the SA-1110 or L7205/L7210 may override this + value with a fixed value. + +CONFIG_USBD_NET_IN_ENDPOINT + Specify the preferred IN (transmit data) endpoint number. This is a + number from 0-15 and must be allowed by the bus interface device. + + Some devices such as the SA-1110 or L7205/L7210 may override this + value with a fixed value. + +CONFIG_USBD_NET_INT_ENDPOINT + Specify the preferred INT (interrupt) endpoint number. This is a + number from 0-15 and must be allowed by the bus interface device. + + Some devices such as the L7205/L7210 may override this value with a + fixed value. Others such as the SA-1110 do not allow an interrupt + value. + +CONFIG_USBD_NET_OUT_PKTSIZE + Specify the maximum packet size for the OUT endpoint. This allowable + values are normally 16, 32 and 64. + + Some devices such as the Linkup L7205/L7210 may override this value + with a lower maximum value (such as 32). + +CONFIG_USBD_NET_IN_PKTSIZE + Specify the maximum packet size for the IN endpoint. This allowable + values are normally 16, 32 and 64. + + Some devices such as the Linkup L7205/L7210 may override this value + with a lower maximum value (such as 32). + +CONFIG_USBD_NET_INT_PKTSIZE + Specify the maximum packet size for the INT endpoint. This allowable + values are normally 8 and 16. Some bus interface devices may not + support all values. + +CONFIG_USBD_NET_EARLY + If this is enabled the network interface will be created when the + module is loaded. The default is to create the network interface + when the USB Device has been enumerated by the USB Host. + +CONFIG_USBD_NET_CDC + Enable CDC mode. This configures the network driver to support the + networking model specified in the USB Class Definitions. + +CONFIG_USBD_NET_RNDIS + Enable RNDIS networking. This allows the network function driver to + work with Windows RNDIS networking. + +CONFIG_USBD_NET_REMOTE_MACADDR + Specify a default MAC address to provide the USB Host. + +CONFIG_USBD_NET_REMOTE_OUI + Specify a OUI for generating a MAC address for the USB Host. + +CONFIG_USBD_NET_LOCAL_OUI + Specify a OUI for generating a MAC address for the local interface. + +CONFIG_USBD_NET_LOCAL_MACADDR + Specify a default MAC address to use on the local interface. diff -uNr linux.org/drivers/usb/device/net_fd/Config.in linux/drivers/usb/device/net_fd/Config.in --- linux.org/drivers/usb/device/net_fd/Config.in Thu Jan 1 08:00:00 1970 +++ linux/drivers/usb/device/net_fd/Config.in Thu May 15 15:54:48 2003 @@ -0,0 +1,46 @@ +# +# Generic Network Function Driver +# +# Copyright (C) 2001 Lineo, Inc. +# Copyright (C) 2001 Hewlett-Packard Co. +mainmenu_option next_comment +comment "Network Function" + +dep_tristate ' Network Function Driver' CONFIG_USBD_NET $CONFIG_USBD +if [ "$CONFIG_USBD_NET" = "y" -o "$CONFIG_USBD_NET" = "m" ]; then + hex ' Overide VendorID (hex value)' CONFIG_USBD_NET_VENDORID "0000" + hex ' Overide ProductID (hex value)' CONFIG_USBD_NET_PRODUCTID "0000" + string ' Default Network interface name' CONFIG_USBD_NET_IFNAME "usbd" + + # allow setting of endpoint configurations for some architectures + int ' OUT Endpoint (0-15)' CONFIG_USBD_NET_OUT_ENDPOINT "1" + int ' OUT PacketSize (16, 32, 64)' CONFIG_USBD_NET_OUT_PKTSIZE "64" + int ' IN Endpoint (0-15)' CONFIG_USBD_NET_IN_ENDPOINT "2" + int ' IN PacketSize (16, 32, 64)' CONFIG_USBD_NET_IN_PKTSIZE "64" + + if [ ! "$CONFIG_ARCH_SA1100" = "y" -a ! "$CONFIG_ARCH_L7200" = "y" ]; then + int ' INT Endpoint (0-15)' CONFIG_USBD_NET_INT_ENDPOINT "3" + int ' INT PacketSize (8, 16)' CONFIG_USBD_NET_INT_PKTSIZE "16" + fi + + bool ' USB NFS Support(Please dont choose Interface always up)' CONFIG_USBD_NET_NFS_SUPPORT + + bool ' Interface Always Up' CONFIG_USBD_NET_ALWAYSUP + bool ' Safe Mode' CONFIG_USBD_NET_SAFE + bool ' MDLM Mode' CONFIG_USBD_NET_MDLM + bool ' CDC Mode' CONFIG_USBD_NET_CDC + + if [ "$CONFIG_USBD_NET_CDC" = "y" -o "$CONFIG_USBD_NET_MDLM" = "y" ]; then + if [ "$CONFIG_ARCH_SA1110" = "n" ]; then + bool ' RNDIS Mode (not working yet)' CONFIG_USBD_NET_RNDIS + fi + string ' Default Remote MAC Address (e.g. 400002000001)' CONFIG_USBD_NET_REMOTE_MACADDR "" + hex ' RemoteNetwork OUI (e.g. 400002)' CONFIG_USBD_NET_REMOTE_OUI "400002" + bool ' Use MAC Address as device Serial Number' CONFIG_USBD_MAC_AS_SERIAL_NUMBER + fi + + string ' Default Local MAC Address (e.g. 400001000001)' CONFIG_USBD_NET_LOCAL_MACADDR "400001000001" + hex ' Local Network OUI (e.g. 400001)' CONFIG_USBD_NET_LOCAL_OUI "400001" +fi + +endmenu diff -uNr linux.org/drivers/usb/device/net_fd/Makefile linux/drivers/usb/device/net_fd/Makefile --- linux.org/drivers/usb/device/net_fd/Makefile Thu Jan 1 08:00:00 1970 +++ linux/drivers/usb/device/net_fd/Makefile Thu May 15 15:54:48 2003 @@ -0,0 +1,58 @@ +# +# SA1100 Function driver for a network USB Device +# +# Copyright (C) 2001 Lineo, Inc. +# Copyright (C) 2001 Hewlett-Packard Co. + + +O_TARGET := net_fd_drv.o +list-multi := net_fd.o + +net_fd-objs := net-fd.o netproto.o crc32.o + +# Object file lists. + +obj-y := +obj-m := +obj-n := +obj- := + +# Each configuration option enables a list of files. + +obj-$(CONFIG_USBD_NET) += net_fd.o + +# Extract lists of the multi-part drivers. +# The 'int-*' lists are the intermediate files used to build the multi's. + +multi-y := $(filter $(list-multi), $(obj-y)) +multi-m := $(filter $(list-multi), $(obj-m)) +int-y := $(sort $(foreach m, $(multi-y), $($(basename $(m))-objs))) +int-m := $(sort $(foreach m, $(multi-m), $($(basename $(m))-objs))) + +# Files that are both resident and modular: remove from modular. + +obj-m := $(filter-out $(obj-y), $(obj-m)) +int-m := $(filter-out $(int-y), $(int-m)) + +# Translate to Rules.make lists. + +O_OBJS := $(filter-out $(export-objs), $(obj-y)) +OX_OBJS := $(filter $(export-objs), $(obj-y)) +M_OBJS := $(sort $(filter-out $(export-objs), $(obj-m))) +MX_OBJS := $(sort $(filter $(export-objs), $(obj-m))) +MI_OBJS := $(sort $(filter-out $(export-objs), $(int-m))) +MIX_OBJS := $(sort $(filter $(export-objs), $(int-m))) + +# The global Rules.make. + +include $(TOPDIR)/Rules.make + +# Link rules for multi-part drivers. + +net_fd.o: $(net_fd-objs) + $(LD) -r -o $@ $(net_fd-objs) + +# dependencies: + +net-fd.o: netproto.h ../usbd.h ../usbd-bus.h ../usbd-func.h + diff -uNr linux.org/drivers/usb/device/net_fd/crc32.c linux/drivers/usb/device/net_fd/crc32.c --- linux.org/drivers/usb/device/net_fd/crc32.c Thu Jan 1 08:00:00 1970 +++ linux/drivers/usb/device/net_fd/crc32.c Thu May 15 15:54:48 2003 @@ -0,0 +1,63 @@ +/* + * linux/drivers/usbd/net_fd/crc32.c + * + * Copyright (c) 2000, 2001, 2002 Lineo + * Copyright (c) 2001 Hewlett Packard + * + * By: + * Stuart Lynne , + * Tom Rushworth , + * Bruce Balden + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + * + */ + +#include + +__u32 crc32_table[256] = { + 0x00000000, 0x77073096, 0xee0e612c, 0x990951ba, 0x076dc419, 0x706af48f, 0xe963a535, 0x9e6495a3, + 0x0edb8832, 0x79dcb8a4, 0xe0d5e91e, 0x97d2d988, 0x09b64c2b, 0x7eb17cbd, 0xe7b82d07, 0x90bf1d91, + 0x1db71064, 0x6ab020f2, 0xf3b97148, 0x84be41de, 0x1adad47d, 0x6ddde4eb, 0xf4d4b551, 0x83d385c7, + 0x136c9856, 0x646ba8c0, 0xfd62f97a, 0x8a65c9ec, 0x14015c4f, 0x63066cd9, 0xfa0f3d63, 0x8d080df5, + 0x3b6e20c8, 0x4c69105e, 0xd56041e4, 0xa2677172, 0x3c03e4d1, 0x4b04d447, 0xd20d85fd, 0xa50ab56b, + 0x35b5a8fa, 0x42b2986c, 0xdbbbc9d6, 0xacbcf940, 0x32d86ce3, 0x45df5c75, 0xdcd60dcf, 0xabd13d59, + 0x26d930ac, 0x51de003a, 0xc8d75180, 0xbfd06116, 0x21b4f4b5, 0x56b3c423, 0xcfba9599, 0xb8bda50f, + 0x2802b89e, 0x5f058808, 0xc60cd9b2, 0xb10be924, 0x2f6f7c87, 0x58684c11, 0xc1611dab, 0xb6662d3d, + 0x76dc4190, 0x01db7106, 0x98d220bc, 0xefd5102a, 0x71b18589, 0x06b6b51f, 0x9fbfe4a5, 0xe8b8d433, + 0x7807c9a2, 0x0f00f934, 0x9609a88e, 0xe10e9818, 0x7f6a0dbb, 0x086d3d2d, 0x91646c97, 0xe6635c01, + 0x6b6b51f4, 0x1c6c6162, 0x856530d8, 0xf262004e, 0x6c0695ed, 0x1b01a57b, 0x8208f4c1, 0xf50fc457, + 0x65b0d9c6, 0x12b7e950, 0x8bbeb8ea, 0xfcb9887c, 0x62dd1ddf, 0x15da2d49, 0x8cd37cf3, 0xfbd44c65, + 0x4db26158, 0x3ab551ce, 0xa3bc0074, 0xd4bb30e2, 0x4adfa541, 0x3dd895d7, 0xa4d1c46d, 0xd3d6f4fb, + 0x4369e96a, 0x346ed9fc, 0xad678846, 0xda60b8d0, 0x44042d73, 0x33031de5, 0xaa0a4c5f, 0xdd0d7cc9, + 0x5005713c, 0x270241aa, 0xbe0b1010, 0xc90c2086, 0x5768b525, 0x206f85b3, 0xb966d409, 0xce61e49f, + 0x5edef90e, 0x29d9c998, 0xb0d09822, 0xc7d7a8b4, 0x59b33d17, 0x2eb40d81, 0xb7bd5c3b, 0xc0ba6cad, + 0xedb88320, 0x9abfb3b6, 0x03b6e20c, 0x74b1d29a, 0xead54739, 0x9dd277af, 0x04db2615, 0x73dc1683, + 0xe3630b12, 0x94643b84, 0x0d6d6a3e, 0x7a6a5aa8, 0xe40ecf0b, 0x9309ff9d, 0x0a00ae27, 0x7d079eb1, + 0xf00f9344, 0x8708a3d2, 0x1e01f268, 0x6906c2fe, 0xf762575d, 0x806567cb, 0x196c3671, 0x6e6b06e7, + 0xfed41b76, 0x89d32be0, 0x10da7a5a, 0x67dd4acc, 0xf9b9df6f, 0x8ebeeff9, 0x17b7be43, 0x60b08ed5, + 0xd6d6a3e8, 0xa1d1937e, 0x38d8c2c4, 0x4fdff252, 0xd1bb67f1, 0xa6bc5767, 0x3fb506dd, 0x48b2364b, + 0xd80d2bda, 0xaf0a1b4c, 0x36034af6, 0x41047a60, 0xdf60efc3, 0xa867df55, 0x316e8eef, 0x4669be79, + 0xcb61b38c, 0xbc66831a, 0x256fd2a0, 0x5268e236, 0xcc0c7795, 0xbb0b4703, 0x220216b9, 0x5505262f, + 0xc5ba3bbe, 0xb2bd0b28, 0x2bb45a92, 0x5cb36a04, 0xc2d7ffa7, 0xb5d0cf31, 0x2cd99e8b, 0x5bdeae1d, + 0x9b64c2b0, 0xec63f226, 0x756aa39c, 0x026d930a, 0x9c0906a9, 0xeb0e363f, 0x72076785, 0x05005713, + 0x95bf4a82, 0xe2b87a14, 0x7bb12bae, 0x0cb61b38, 0x92d28e9b, 0xe5d5be0d, 0x7cdcefb7, 0x0bdbdf21, + 0x86d3d2d4, 0xf1d4e242, 0x68ddb3f8, 0x1fda836e, 0x81be16cd, 0xf6b9265b, 0x6fb077e1, 0x18b74777, + 0x88085ae6, 0xff0f6a70, 0x66063bca, 0x11010b5c, 0x8f659eff, 0xf862ae69, 0x616bffd3, 0x166ccf45, + 0xa00ae278, 0xd70dd2ee, 0x4e048354, 0x3903b3c2, 0xa7672661, 0xd06016f7, 0x4969474d, 0x3e6e77db, + 0xaed16a4a, 0xd9d65adc, 0x40df0b66, 0x37d83bf0, 0xa9bcae53, 0xdebb9ec5, 0x47b2cf7f, 0x30b5ffe9, + 0xbdbdf21c, 0xcabac28a, 0x53b39330, 0x24b4a3a6, 0xbad03605, 0xcdd70693, 0x54de5729, 0x23d967bf, + 0xb3667a2e, 0xc4614ab8, 0x5d681b02, 0x2a6f2b94, 0xb40bbe37, 0xc30c8ea1, 0x5a05df1b, 0x2d02ef8d +}; diff -uNr linux.org/drivers/usb/device/net_fd/crc32.h linux/drivers/usb/device/net_fd/crc32.h --- linux.org/drivers/usb/device/net_fd/crc32.h Thu Jan 1 08:00:00 1970 +++ linux/drivers/usb/device/net_fd/crc32.h Thu May 15 15:54:48 2003 @@ -0,0 +1,77 @@ +/* + * linux/drivers/usbd/net_fd/crc32.h + * + * Copyright (c) 2000, 2001, 2002 Lineo + * Copyright (c) 2001 Hewlett Packard + * + * By: + * Stuart Lynne , + * Tom Rushworth , + * Bruce Balden + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + * + */ + + +extern __u32 crc32_table[256]; + +#define CRC32_INITFCS 0xffffffff // Initial FCS value +#define CRC32_GOODFCS 0xdebb20e3 // Good final FCS value + +#define CRC32_FCS(fcs, c) (((fcs) >> 8) ^ crc32_table[((fcs) ^ (c)) & 0xff]) + +/** + * fcs_memcpy32 - memcpy and calculate fcs + * @dp: + * @sp: + * @len: + * @fcs: + * + * Perform a memcpy and calculate fcs using ppp 32bit CRC algorithm. + */ +static __u32 __inline__ fcs_memcpy32 (unsigned char *dp, unsigned char *sp, int len, __u32 fcs) +{ + for (; len-- > 0; fcs = CRC32_FCS (fcs, *dp++ = *sp++)); + return fcs; +} + +/** + * fcs_pad32 - pad and calculate fcs + * @dp: + * @len: + * @fcs: + * + * Pad and calculate fcs using ppp 32bit CRC algorithm. + */ +static __u32 __inline__ fcs_pad32 (unsigned char *dp, int len, __u32 fcs) +{ + for (; len-- > 0; fcs = CRC32_FCS (fcs, *dp++ = '\0')); + return fcs; +} + +/** + * fcs_compute32 - memcpy and calculate fcs + * @sp: + * @len: + * @fcs: + * + * Perform a memcpy and calculate fcs using ppp 32bit CRC algorithm. + */ +static __u32 __inline__ fcs_compute32 (unsigned char *sp, int len, __u32 fcs) +{ + for (; len-- > 0; fcs = CRC32_FCS (fcs, *sp++)); + return fcs; +} diff -uNr linux.org/drivers/usb/device/net_fd/net-fd.c linux/drivers/usb/device/net_fd/net-fd.c --- linux.org/drivers/usb/device/net_fd/net-fd.c Thu Jan 1 08:00:00 1970 +++ linux/drivers/usb/device/net_fd/net-fd.c Thu May 15 15:54:48 2003 @@ -0,0 +1,1821 @@ +/* + * linux/drivers/usbd/net_fd/net-fd.c - network function driver + * + * Copyright (c) 2000, 2001, 2002 Lineo + * Copyright (c) 2001 Hewlett Packard + * + * By: + * Stuart Lynne , + * Tom Rushworth , + * Bruce Balden + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + * + */ + + +#include +#include + +#include "../usbd-export.h" +#include "../usbd-build.h" +#include "../usbd-module.h" + +MODULE_AUTHOR ("sl@lineo.com, tbr@lineo.com"); +MODULE_DESCRIPTION ("USB Device Network Function"); + +USBD_MODULE_INFO ("net_fd 0.1"); + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include + +#include "../usbd.h" +#include "../usbd-func.h" +#include "../usbd-bus.h" +#include "../usbd-inline.h" +#include "../usbd-arch.h" + +#include "crc32.h" +#include "../hotplug.h" + +#include "netproto.h" + +#ifdef CONFIG_USBD_NET_NFS_SUPPORT +wait_queue_head_t usb_netif_wq; +int usb_is_configured = 0; +#endif + +#define MCCI_ENABLE_CRC 0x03 + + +#if !defined (CONFIG_USBD_VENDORID) && !defined(CONFIG_USBD_NET_VENDORID) +#error No Vendor ID +#endif +#if !defined (CONFIG_USBD_PRODUCTID) && !defined(CONFIG_USBD_NET_PRODUCTID) +#error No Product ID +#endif + +#if !defined(CONFIG_USBD_NET_ALWAYSUP) +#define CONFIG_USBD_NET_ALWAYSUP 0; +#endif + + +#if defined(CONFIG_USBD_NET_VENDORID) && (CONFIG_USBD_NET_VENDORID > 0) +#undef CONFIG_USBD_VENDORID +#define CONFIG_USBD_VENDORID CONFIG_USBD_NET_VENDORID +#endif + +#if defined(CONFIG_USBD_NET_PRODUCTID) && (CONFIG_USBD_NET_PRODUCTID > 0) +#undef CONFIG_USBD_PRODUCTID +#define CONFIG_USBD_PRODUCTID CONFIG_USBD_NET_PRODUCTID +#endif + +#ifndef CONFIG_USBD_NET_LOCAL_OUI +#define CONFIG_USBD_NET_LOCAL_OUI 0x400001 + //#warning Setting default MAC LOCAL_OUI +#endif + +#ifndef CONFIG_USBD_NET_REMOTE_OUI +#define CONFIG_USBD_NET_REMOTE_OUI 0x400002 + //#warning Setting default MAC REMOTE_OUI +#endif + +#ifndef CONFIG_USBD_NET_LOCAL_MACADDR +#define CONFIG_USBD_NET_LOCAL_MACADDR "400001000001" + //#warning Setting default Local MAC Address +#endif + +#ifndef CONFIG_USBD_NET_REMOTE_MACADDR +#define CONFIG_USBD_NET_REMOTE_MACADDR "400002000001" + //#warning Setting default Remote MAC Address +#endif + +#ifndef CONFIG_USBD_MAXPOWER +#define CONFIG_USBD_MAXPOWER 0 +#endif + +#ifndef CONFIG_USBD_MANUFACTURER +#define CONFIG_USBD_MANUFACTURER "Lineo" +#endif + +#define MAXTRANSFER 1514 + +#ifndef CONFIG_USBD_VENDORID +#error "CONFIG_USBD_VENDORID not defined" +#endif + +#ifndef CONFIG_USBD_PRODUCTID +#error "CONFIG_USBD_PRODUCTID not defined" +#endif + +#ifndef CONFIG_USBD_NET_IFNAME +#define CONFIG_USBD_NET_IFNAME "usbd" +#endif + +#ifndef CONFIG_USBD_PRODUCT_NAME +#ifndef CONFIG_USBD_NET_CDC +#define CONFIG_USBD_PRODUCT_NAME "CDC Network Driver" +#else +#define CONFIG_USBD_PRODUCT_NAME "Linux Network Driver" +#endif +#endif + +#ifndef CONFIG_USBD_SERIAL_NUMBER_STR +#define CONFIG_USBD_SERIAL_NUMBER_STR "" +#endif + +/* + * USB 2.0 spec does not mention it, but MaxPower is expected to be at least one + * and is tested for in USB configuration tests. + */ +#ifdef CONFIG_USBD_SELFPOWERED +#define BMATTRIBUTE BMATTRIBUTE_RESERVED | BMATTRIBUTE_SELF_POWERED +#define BMAXPOWER 1 +#else +#define BMATTRIBUTE BMATTRIBUTE_RESERVED +#define BMAXPOWER CONFIG_USBD_MAXPOWER +#endif + + + +/* + * setup some default values for pktsizes and endpoint addresses. + */ + +#ifndef CONFIG_USBD_NET_OUT_PKTSIZE +#define CONFIG_USBD_NET_OUT_PKTSIZE 64 +#endif + +#ifndef CONFIG_USBD_NET_IN_PKTSIZE +#define CONFIG_USBD_NET_IN_PKTSIZE 64 +#endif + +#ifndef CONFIG_USBD_NET_INT_PKTSIZE +#define CONFIG_USBD_NET_INT_PKTSIZE 16 +#endif + +#ifndef CONFIG_USBD_NET_OUT_ENDPOINT +#define CONFIG_USBD_NET_OUT_ENDPOINT 1 +#endif + +#ifndef CONFIG_USBD_NET_IN_ENDPOINT +#define CONFIG_USBD_NET_IN_ENDPOINT 2 +#endif + +#ifndef CONFIG_USBD_NET_INT_ENDPOINT +#define CONFIG_USBD_NET_INT_ENDPOINT 3 +#endif + + +/* + * check for architecture specific endpoint configurations + */ + +#if defined(ABS_OUT_ADDR) + //#warning + //#warning USING ABS ENDPOINT OUT ADDRESS + #undef CONFIG_USBD_NET_OUT_ENDPOINT + + #if ABS_OUT_ADDR > 0 + #define CONFIG_USBD_NET_OUT_ENDPOINT ABS_OUT_ADDR + #endif + +#elif defined(MAX_OUT_ADDR) && defined(CONFIG_USBD_NET_OUT_ENDPOINT) && (CONFIG_USBD_NET_OUT_ENDPOINT > MAX_OUT_ADDR) + //#warning + //#warning USING DEFAULT ENDPOINT OUT ADDRESS + #undef CONFIG_USBD_NET_OUT_ENDPOINT + #define CONFIG_USBD_NET_OUT_ENDPOINT DFL_OUT_ADDR + +#endif /* elif */ + + +#if defined(ABS_IN_ADDR) + //#warning + //#warning USING ABS ENDPOINT IN ADDRESS + #undef CONFIG_USBD_NET_IN_ENDPOINT + + #if ABS_IN_ADDR > 0 + #define CONFIG_USBD_NET_IN_ENDPOINT ABS_IN_ADDR + #endif + +#elif defined(MAX_IN_ADDR) && defined(CONFIG_USBD_NET_IN_ENDPOINT) && (CONFIG_USBD_NET_IN_ENDPOINT > MAX_IN_ADDR) + //#warning + //#warning USING DEFAULT ENDPOINT IN ADDRESS + #undef CONFIG_USBD_NET_IN_ENDPOINT + #define CONFIG_USBD_NET_IN_ENDPOINT DFL_IN_ADDR + +#endif /* elif */ + + +#if defined(ABS_INT_ADDR) + //#warning + //#warning USING ABS ENDPOINT INT ADDRESS + #undef CONFIG_USBD_NET_INT_ENDPOINT + + #if ABS_INT_ADDR + #define CONFIG_USBD_NET_INT_ENDPOINT ABS_INT_ADDR + #endif + +#elif defined(MAX_INT_ADDR) && defined(CONFIG_USBD_NET_INT_ENDPOINT) && (CONFIG_USBD_NET_INT_ENDPOINT > MAX_INT_ADDR) + //#warning + //#warning USING DEFAULT ENDPOINT INT ADDRESS + #undef CONFIG_USBD_NET_INT_ENDPOINT + #define CONFIG_USBD_NET_INT_ENDPOINT DFL_INT_ADDR + +#endif /* elif */ + + + +#if defined(MAX_OUT_PKTSIZE) && defined(CONFIG_USBD_NET_OUT_PKTSIZE) && CONFIG_USBD_NET_OUT_PKTSIZE > MAX_OUT_PKTSIZE + //#warning + //#warning OVERIDING ENDPOINT OUT PKTSIZE + #undef CONFIG_USBD_NET_OUT_PKTSIZE + #define CONFIG_USBD_NET_OUT_PKTSIZE MAX_OUT_PKTSIZE +#endif + +#if defined(MAX_IN_PKTSIZE) && defined(CONFIG_USBD_NET_IN_PKTSIZE) && CONFIG_USBD_NET_IN_PKTSIZE > MAX_IN_PKTSIZE + //#warning + //#warning OVERIDING ENDPOINT IN PKTSIZE + #undef CONFIG_USBD_NET_IN_PKTSIZE + #define CONFIG_USBD_NET_IN_PKTSIZE MAX_IN_PKTSIZE +#endif + +#if defined(MAX_INT_PKTSIZE) && defined(CONFIG_USBD_NET_INT_PKTSIZE) && CONFIG_USBD_NET_INT_PKTSIZE > MAX_INT_PKTSIZE + //#warning + //#warning OVERIDING ENDPOINT INT PKTSIZE + #undef CONFIG_USBD_NET_INT_PKTSIZE + #define CONFIG_USBD_NET_INT_PKTSIZE MAX_INT_PKTSIZE +#endif + + + +/* Module Parameters ************************************************************************* */ + +#define MAX_INTERFACES 1 + +static char *if_name = CONFIG_USBD_NET_IFNAME; +#if defined(CONFIG_USBD_NET_CDC) || defined(CONFIG_USBD_NET_MDLM) +static char *remote_mac_address; +#endif +static char *local_mac_address; + +static char *dbg = NULL; + +#if defined(CONFIG_USBD_NET_CDC) || defined(CONFIG_USBD_NET_MDLM) +static char remote_mac_address_buffer[14]; +#endif +static u32 vendor_id; +static u32 product_id; +static int alwaysup = CONFIG_USBD_NET_ALWAYSUP; +static int out_pkt_sz = CONFIG_USBD_NET_OUT_PKTSIZE; +static int in_pkt_sz = CONFIG_USBD_NET_IN_PKTSIZE; + + +MODULE_PARM (if_name, "s"); +#if defined(CONFIG_USBD_NET_CDC) || defined(CONFIG_USBD_NET_MDLM) +MODULE_PARM (remote_mac_address, "s"); +#endif +MODULE_PARM (local_mac_address, "s"); +MODULE_PARM (vendor_id, "i"); +MODULE_PARM (product_id, "i"); +MODULE_PARM (alwaysup, "i"); +MODULE_PARM (out_pkt_sz, "i"); +MODULE_PARM (in_pkt_sz, "i"); +MODULE_PARM (dbg, "s"); + +MODULE_PARM_DESC (if_name, "Network Interface name prefix"); +#if defined(CONFIG_USBD_NET_CDC) || defined(CONFIG_USBD_NET_MDLM) +MODULE_PARM_DESC (remote_mac_address, "Remote MAC"); +#endif +MODULE_PARM_DESC (local_mac_address, "Local MAC"); +MODULE_PARM_DESC (vendor_id, "vendor id"); +MODULE_PARM_DESC (product_id, "product id"); +MODULE_PARM_DESC (alwaysup, "always up"); +MODULE_PARM_DESC (dbg, "dbg string"); + +/* Debug switches (module parameter "dbg=...") *********************************************** */ + +extern int dbgflg_usbdfd_init; +int dbgflg_usbdfd_usbe; +int dbgflg_usbdfd_rx; +int dbgflg_usbdfd_tx; +int dbgflg_usbdfd_ep0; + +static debug_option dbg_table[] = { + {&dbgflg_usbdfd_init, NULL, "init", "initialization and termination"}, + {&dbgflg_usbdfd_ep0, NULL, "ep0", "End Point 0 (setup) packet handling"}, + {&dbgflg_usbdfd_rx, NULL, "rx", "USB RX (host->device) handling"}, + {&dbgflg_usbdfd_tx, NULL, "tx", "USB TX (device->host) handling"}, + {&dbgflg_usbdfd_usbe, NULL, "usbe", "USB events"}, + {NULL, NULL, "net", "network device handling"}, + {NULL, NULL, NULL, NULL}, +}; + +#define dbg_init(lvl,fmt,args...) dbgPRINT(dbgflg_usbdfd_init,lvl,fmt,##args) +#define dbg_ep0(lvl,fmt,args...) dbgPRINT(dbgflg_usbdfd_ep0,lvl,fmt,##args) +#define dbg_rx(lvl,fmt,args...) dbgPRINT(dbgflg_usbdfd_rx,lvl,fmt,##args) +#define dbg_tx(lvl,fmt,args...) dbgPRINT(dbgflg_usbdfd_tx,lvl,fmt,##args) +#define dbg_usbe(lvl,fmt,args...) dbgPRINT(dbgflg_usbdfd_usbe,lvl,fmt,##args) + +#define NET_INUSE 0x01 +#define NET_ATTACHED 0x02 + +struct usb_net_private { + int flags; + int interface; + struct usb_device_instance *device; + int crc; + unsigned int maxtransfer; + char name[IFNAMSIZ]; + int index; +#ifdef CONFIG_ARCH_SA1100 + int first; +#endif +}; + +static struct usb_net_private net_private_array[MAX_INTERFACES]; + +static rwlock_t net_rwlock = RW_LOCK_UNLOCKED; // lock for netproto device array access + +#define RXECHO +#define TXECHO + +typedef enum net_device_event { + + NET_UNKOWN, // net - unknown + NET_SET_CRC, // net - vendor command + +} net_device_event_t; + + + +static __initdata unsigned char default_dev_addr[ETH_ALEN] = { + 0x40, 0x00, 0x00, 0x00, 0x00, 0x01 +}; + + +/* proc file system ********************************************************************** */ +static void net_device_check_condition(usb_device_event_t event); + + + +/* USB Configuration Description ************************************************************* */ + +#if !defined(CONFIG_USBD_NET_SAFE) && !defined(CONFIG_USBD_NET_CDC) && !defined(CONFIG_USBD_NET_MDLM) + #error One of CONFIG_USBD_NET_{SAFE,CDC,MDLM} must be set +#endif + +#if defined(CONFIG_USBD_NET_MDLM) && defined(CONFIG_USBD_NET_SAFE) + #error Only one of CONFIG_USBD_NET_SAFE or CONFIG_USBD_NET_MDLM can be set +#endif + +#if defined(CONFIG_USBD_NET_MDLM) && defined(CONFIG_USBD_NET_CDC) + #error One of CONFIG_USBD_NET_CDC or CONFIG_USBD_NET_MDLM can be set +#endif + + +/* USB Safe Configuration ******************************************************************** */ +#ifdef CONFIG_USBD_NET_SAFE + +/* + * Simple Ethernet Configuration + */ + + +/* Communication Interface Class descriptions + */ + +static __initdata struct usb_endpoint_description net_default[] = { + {bEndpointAddress:CONFIG_USBD_NET_OUT_ENDPOINT, + bmAttributes:BULK, + wMaxPacketSize:CONFIG_USBD_NET_OUT_PKTSIZE, + bInterval:0, + direction:OUT, + transferSize:MAXTRANSFER + 4,}, + + {bEndpointAddress:CONFIG_USBD_NET_IN_ENDPOINT, + bmAttributes:BULK, + wMaxPacketSize:CONFIG_USBD_NET_IN_PKTSIZE, + bInterval:0, + direction:IN, + transferSize:MAXTRANSFER + 4,}, + +#if defined(CONFIG_USBD_NET_INT_ENDPOINT) && (CONFIG_USBD_NET_INT_ENDPOINT > 0) + {bEndpointAddress:CONFIG_USBD_NET_INT_ENDPOINT, + bmAttributes:INTERRUPT, + wMaxPacketSize:CONFIG_USBD_NET_INT_PKTSIZE, + bInterval:10, + direction:IN, + transferSize:CONFIG_USBD_NET_INT_PKTSIZE,}, +#endif + +}; + +/* Data Interface Alternate description(s) + */ +static __initdata struct usb_alternate_description net_data_alternate_descriptions[] = { + { + #if defined(CONFIG_USBD_NET_NO_STRINGS) + iInterface:"", + #else + iInterface:"Simple Network Data Interface - Bulk mode", + #endif + bAlternateSetting:0, + endpoints:sizeof (net_default) / sizeof (struct usb_endpoint_description), + endpoint_list:net_default,}, +}; + +/* Interface description(s) + */ +static __initdata struct usb_interface_description net_interfaces[] = { + { + #if defined(CONFIG_USBD_NET_NO_STRINGS) + iInterface:"", + #else + iInterface:"Simple Network Data Interface", + #endif + bInterfaceClass:LINEO_CLASS, + bInterfaceSubClass:LINEO_SUBCLASS_SAFENET, + #if defined(CONFIG_USBD_NET_NO_STRINGS) + bInterfaceProtocol:LINEO_SAFENET_CRC_PADDED, + #else + bInterfaceProtocol:LINEO_SAFENET_CRC, + #endif + alternates:sizeof (net_data_alternate_descriptions) / sizeof (struct usb_alternate_description), + alternate_list:net_data_alternate_descriptions,}, +}; +#endif /* CONFIG_USBD_NET_SAFE */ + +/* USB CDC Configuration ********************************************************************* */ +#ifdef CONFIG_USBD_NET_CDC + +/* + * CDC Ethernet Configuration + */ + +/* Communication Interface Class descriptions + */ +static struct usb_class_description cdc_comm_class_descriptions[] = { + {USB_ST_HEADER, 0, {header: {bcdCDC:CLASS_BCD_VERSION,}}}, + + {USB_ST_ENF, 0, + {ethernet_networking: + {iMACAddress:NULL, + bmEthernetStatistics: 0, wMaxSegmentSize:1514, + wNumberMCFilters: 0, bNumberPowerFilters:0,}}}, + + {USB_ST_UF, 1, {union_function: {bMasterInterface: 0, bSlaveInterface:{1},}}}, +}; + +/* Data Interface Alternate 1 endpoints + */ +static __initdata struct usb_endpoint_description net_alt_1_endpoints[] = { + {bEndpointAddress:CONFIG_USBD_NET_OUT_ENDPOINT, + bmAttributes:BULK, + wMaxPacketSize:CONFIG_USBD_NET_OUT_PKTSIZE, + bInterval:0, + direction:OUT, + transferSize:MAXTRANSFER + 2,}, + + {bEndpointAddress:CONFIG_USBD_NET_IN_ENDPOINT, + bmAttributes:BULK, + wMaxPacketSize:CONFIG_USBD_NET_IN_PKTSIZE, + bInterval:0, + direction:IN, + transferSize:MAXTRANSFER + 2,}, + +#if defined(CONFIG_USBD_NET_INT_ENDPOINT) && (CONFIG_USBD_NET_INT_ENDPOINT > 0) + {bEndpointAddress:CONFIG_USBD_NET_INT_ENDPOINT, + bmAttributes:INTERRUPT, + wMaxPacketSize:CONFIG_USBD_NET_INT_PKTSIZE, + bInterval:10, + direction:IN, + transferSize:CONFIG_USBD_NET_INT_PKTSIZE,}, +#endif +}; + + +/* Data Interface Alternate description(s) + */ +static __initdata struct usb_alternate_description cdc_comm_alternate_descriptions[] = { + { + #if defined(CONFIG_USBD_NET_NO_STRINGS) + iInterface:"", + #else + iInterface:"CDC Network Comm Interface", + #endif + bAlternateSetting:0, + classes:sizeof (cdc_comm_class_descriptions) / sizeof (struct usb_class_description), + class_list:cdc_comm_class_descriptions,}, +}; + +static __initdata struct usb_alternate_description cdc_data_alternate_descriptions[] = { + { + #if defined(CONFIG_USBD_NET_NO_STRINGS) + iInterface:"Bulk Disabled", + #else + iInterface:"CDC Network Data Interface - Disabled mode", + #endif + bAlternateSetting:0,}, + + { + #if defined(CONFIG_USBD_NET_NO_STRINGS) + iInterface:"Data Enabled", + #else + iInterface:"CDC Network Data Interface - Bulk mode", + #endif + bAlternateSetting:1, + endpoints:sizeof (net_alt_1_endpoints) / sizeof (struct usb_endpoint_description), + endpoint_list:net_alt_1_endpoints,}, +}; + +/* Interface description(s) + */ +static __initdata struct usb_interface_description cdc_interfaces[] = { + { + #if defined(CONFIG_USBD_NET_NO_STRINGS) + iInterface:"", + #else + iInterface:"CDC Network Communication Interface", + #endif + bInterfaceClass:COMMUNICATIONS_INTERFACE_CLASS, + bInterfaceSubClass:COMMUNICATIONS_ENCM_SUBCLASS, + bInterfaceProtocol:COMMUNICATIONS_NO_PROTOCOL, + alternates:sizeof (cdc_comm_alternate_descriptions) / sizeof (struct usb_alternate_description), + alternate_list:cdc_comm_alternate_descriptions,}, + + { + #if defined(CONFIG_USBD_NET_NO_STRINGS) + iInterface:"", + #else + iInterface:"CDC Network Data Interface", + #endif + bInterfaceClass:DATA_INTERFACE_CLASS, + bInterfaceSubClass:COMMUNICATIONS_NO_SUBCLASS, + bInterfaceProtocol:COMMUNICATIONS_NO_PROTOCOL, + alternates:sizeof (cdc_data_alternate_descriptions) / sizeof (struct usb_alternate_description), + alternate_list:cdc_data_alternate_descriptions,}, +}; + + +#endif /* CONFIG_USBD_NET_CDC */ + +/* USB MDLM Configuration ******************************************************************** */ +#ifdef CONFIG_USBD_NET_MDLM + +/* + * MDML Ethernet Configuration + */ + +/* Communication Interface Class descriptions + */ +static struct usb_class_description mdlm_comm_class_descriptions[] = { + {USB_ST_HEADER, 0, {header: {bcdCDC:CLASS_BCD_VERSION,}}}, + + {USB_ST_MDLM, 0, {mobile_direct:{ + + bcdVersion:0x0100, + bGUID:{ + 0x5d, 0x34, 0xcf, 0x66, 0x11, 0x18, 0x11, 0xd6, + 0xa2, 0x1a, 0x00, 0x01, 0x02, 0xca, 0x9a, 0x7f}}} + }, + + {USB_ST_MDLMD, 2, {mobile_direct_detail:{ + // XXX FIXME + bGuidDescriptorType:0x00, + bDetailData:{0x0, 0x3}}} + }, + + //{ USB_ST_MDLMD, 1, { mobile_direct_detail: { + // bGuidDescriptorType: 0x01, + // bDetailData: { 0x02 } } } + //}, + + {USB_ST_ENF, 0, + {ethernet_networking: + {iMACAddress:"402233445566", + bmEthernetStatistics: 0, wMaxSegmentSize:1514, + wNumberMCFilters: 0, bNumberPowerFilters:0,}} + }, + + //{ USB_ST_UF, 1, { union_function: { bMasterInterface: 0, bSlaveInterface: { 0 }, }}}, +}; + + +/* Data Interface Alternate 1 endpoints + */ +static __initdata struct usb_endpoint_description mdlm_alt_1_endpoints[] = { + {bEndpointAddress:CONFIG_USBD_NET_OUT_ENDPOINT, + bmAttributes:BULK, + wMaxPacketSize:CONFIG_USBD_NET_OUT_PKTSIZE, + bInterval:0, + direction:OUT, + transferSize:MAXTRANSFER + 2,}, + + {bEndpointAddress:CONFIG_USBD_NET_IN_ENDPOINT, + bmAttributes:BULK, + wMaxPacketSize:CONFIG_USBD_NET_IN_PKTSIZE, + bInterval:0, + direction:IN, + transferSize:MAXTRANSFER + 2,}, + +#if defined(CONFIG_USBD_NET_INT_ENDPOINT) && (CONFIG_USBD_NET_INT_ENDPOINT > 0) + {bEndpointAddress:CONFIG_USBD_NET_INT_ENDPOINT, + bmAttributes:INTERRUPT, + wMaxPacketSize:CONFIG_USBD_NET_INT_PKTSIZE, + bInterval:10, + direction:IN, + transferSize:CONFIG_USBD_NET_INT_PKTSIZE,}, +#endif +}; + + +/* Data Interface Alternate description(s) + */ +static __initdata struct usb_alternate_description mdlm_alternate_descriptions[] = { + { + #if defined(CONFIG_USBD_NET_NO_STRINGS) + iInterface:"", + #else + iInterface:"MDLM Network Communication Interface", + #endif + bAlternateSetting:0, + classes:sizeof (mdlm_comm_class_descriptions) / sizeof (struct usb_class_description), + class_list:mdlm_comm_class_descriptions, + endpoints:sizeof (mdlm_alt_1_endpoints) / sizeof (struct usb_endpoint_description), + endpoint_list:mdlm_alt_1_endpoints,}, +}; + +/* Interface description(s) + */ +static __initdata struct usb_interface_description mdlm_interfaces[] = { + { + #if defined(CONFIG_USBD_NET_NO_STRINGS) + iInterface:"", + #else + iInterface:"MDLM Network Interface", + #endif + bInterfaceClass:COMMUNICATIONS_INTERFACE_CLASS, + bInterfaceSubClass:COMMUNICATIONS_MDLM_SUBCLASS, + bInterfaceProtocol:COMMUNICATIONS_NO_PROTOCOL, + alternates:sizeof (mdlm_alternate_descriptions) / sizeof (struct usb_alternate_description), + alternate_list:mdlm_alternate_descriptions,}, +}; + + +#endif /* CONFIG_USBD_NET_CDC */ + +/* USB Configuration ************************************************************************* */ + +/* Configuration description(s) + */ +struct __initdata usb_configuration_description net_description[] = { +#ifdef CONFIG_USBD_NET_CDC + { + #if defined(CONFIG_USBD_NET_NO_STRINGS) + iConfiguration:"", + #else + iConfiguration:"CDC 1.1 Configuration", + #endif + bmAttributes:BMATTRIBUTE, + bMaxPower:BMAXPOWER, + interfaces:sizeof (cdc_interfaces) / sizeof (struct usb_interface_description), + interface_list:cdc_interfaces,}, +#endif /* CONFIG_USBD_NET_CDC */ + +#ifdef CONFIG_USBD_NET_MDLM + { + #if defined(CONFIG_USBD_NET_NO_STRINGS) + iConfiguration:"", + #else + iConfiguration:"MDLM Network Configuration", + #endif + bmAttributes:BMATTRIBUTE, + bMaxPower:BMAXPOWER, + interfaces:sizeof (mdlm_interfaces) / sizeof (struct usb_interface_description), + interface_list:mdlm_interfaces,}, +#endif /* CONFIG_USBD_NET_SAFE */ + +#ifdef CONFIG_USBD_NET_SAFE + { + #if defined(CONFIG_USBD_NET_NO_STRINGS) + iConfiguration:"", + #else + iConfiguration:"USB Simple Ethernet Configuration", + #endif + bmAttributes:BMATTRIBUTE, + bMaxPower:BMAXPOWER, + interfaces:sizeof (net_interfaces) / sizeof (struct usb_interface_description), + interface_list:net_interfaces,}, +#endif /* CONFIG_USBD_NET_SAFE */ + +}; + +/* Device Description + */ +struct __initdata usb_device_description net_device_description = { + bDeviceClass:COMMUNICATIONS_DEVICE_CLASS, + bDeviceSubClass:0, // XXX + bDeviceProtocol:0, // XXX + idVendor:CONFIG_USBD_VENDORID, + idProduct:CONFIG_USBD_PRODUCTID, + iManufacturer:CONFIG_USBD_MANUFACTURER, + iProduct:CONFIG_USBD_PRODUCT_NAME, + iSerialNumber:CONFIG_USBD_SERIAL_NUMBER_STR, +}; + + +#undef NET_DESTRUCTOR +#ifdef NET_DESTRUCTOR +#warning DESTRUCTOR +void net_dev_kfree_skb_any (struct sk_buff *skb) +{ + printk (KERN_DEBUG + "net_dev_kfree_skb: skb: %p head: %p data: %p tail: %p end: %p len: %d\n", skb, + skb->head, skb->data, skb->tail, skb->end, skb->len); +} +#endif + +/* * + * net_dev_alloc_skb - allocate an skb + * @len: + * + * Allocate an skb. + */ +static __inline__ struct sk_buff *net_dev_alloc_skb (int len) +{ + struct sk_buff *skb; + if ((skb = netproto_dev_alloc_skb (len))) { +#ifdef NET_DESTRUCTOR + skb->destructor = net_dev_kfree_skb_any; +#endif + } + return skb; +} + + +static int net_xmit_skb (int, struct sk_buff *); +static int net_set_addr (int, void *, int); +static int net_tx_timeout (int); + +/* Intialize and destroy network interfaces ************************************************** */ + +/* * + * net_create - create an interface + * @device: usb device instance + * + */ +void net_create (struct usb_device_instance *device, struct usb_function_instance *function) +{ + int i; + struct usb_net_private *net_private; + + dbg_init (1, "---> privdata: %p", function->privdata); + // lock and find an empty slot + { // get module lock, search for empty device slot, if successful allocate and save + unsigned long flags; + write_lock_irqsave (&net_rwlock, flags); + + // check if we already have an interface + if ((net_private = function->privdata) && (net_private->flags & NET_INUSE)) { + write_unlock_irqrestore (&net_rwlock, flags); + return; + } + // serial number munge + + for (i = 0; i < MAX_INTERFACES; i++) { + dbg_init (1, "i: %d net_private_array[i].device: %p\n", i, net_private_array[i].device); + if (!net_private_array[i].flags) { + break; + } + } + if (i >= MAX_INTERFACES) { + dbg_init (1, "%s i >= MAX_INTERFACES %d %d", device->name, i, MAX_INTERFACES); + write_unlock_irqrestore (&net_rwlock, flags); + return; + } + + net_private = &net_private_array[i]; + net_private->flags |= NET_INUSE; + net_private->index = i; + + write_unlock_irqrestore (&net_rwlock, flags); + } + + function->privdata = net_private; + + dbg_init (1, "function->privdata set to: %p\n", function->privdata); + + net_private->device = device; +#ifdef CONFIG_ARCH_SA1100 + net_private->first = 1; +#endif + + MOD_INC_USE_COUNT; + + + + // should check for buffer overflow... + if (strlen (if_name) < (IFNAMSIZ - 3)) { + sprintf (net_private->name, "%s%d", if_name, net_private->index); + } else { + dbg_init (0, "if_name too long or too short\n"); + net_private->name[0] = '\0'; + } + + // create network interface + if ((net_private->interface = netproto_create (net_private->name, net_xmit_skb, net_set_addr, + net_tx_timeout, default_dev_addr, ETH_ALEN, 1500, 1, 4000, 0)) < 0) + { // lock and modify device array + dbg_init (0, "FAILED\n"); + return; + } + + net_private->flags |= NET_ATTACHED; + +#if 0 +#ifdef CONFIG_USBD_NET_CDC + switch (device->configuration) { + case 1: + net_private->crc = 1; + case 0: + net_private->maxtransfer = MAXTRANSFER + 4 + in_pkt_sz; + break; + } +#else /* CONFIG_USBD_NET_CDC */ + device->interface = 0; + device->alternate = 0; + + net_private->crc = 1; + net_private->maxtransfer = MAXTRANSFER + 4 + in_pkt_sz; +#endif /* CONFIG_USBD_NET_CDC */ +#else + net_private->crc = 1; + net_private->maxtransfer = MAXTRANSFER + 4 + in_pkt_sz; +#endif + + net_private->device = device; +#if 0 + netproto_on (net_private->interface); +#endif +} + +/* * + * net_destroy - destroy an interface + * @device: usb device instance + * + */ +void net_destroy (struct usb_device_instance *device) +{ + struct usb_net_private *net_private; + struct usb_function_instance *function; + int port = 0; // XXX compound device + + dbg_init (1, "- - - "); + + if (!(function = device->function_instance_array + port)) { + return; + } + + dbg_init (1, "destroying"); + + if (!(net_private = function->privdata)) { + dbg_init (1, "%s net_private null", device->name); + return; + } + if (net_private->flags & NET_ATTACHED) { + if (netproto_destroy (net_private->interface)) { + dbg_init (1, "error destroying %d", net_private->index); + } + } + + { // get module lock and delete from device array + unsigned int flags; + write_lock_irqsave (&net_rwlock, flags); + net_private->device = NULL; + net_private->flags = 0; + function->privdata = NULL; + write_unlock_irqrestore (&net_rwlock, flags); + } + + netproto_off (net_private->interface); + + MOD_DEC_USE_COUNT; + return; +} + + +/* Called when a USB Device is created or destroyed ***************************************** */ + + +// XXX this should get passed a device structure, not bus +static void net_function_init (struct usb_bus_instance *bus, + struct usb_device_instance *device, + struct usb_function_driver *function_driver) +{ +#if defined(CONFIG_USBD_NET_MDLM) || defined(CONFIG_USBD_NET_CDC) + int i; +#endif + + struct usb_function_instance *function; + + __u32 serial; // trim for use in MAC addr + + int port = 0; // XXX compound device + + //dbg_init(1, "-------------------------------------------------------------------------------"); + + if (!(function = device->function_instance_array + port)) { + return; + } + + serial = bus->serial_number & 0x00ffffff; + +#if defined(CONFIG_USBD_NET_CDC) || defined(CONFIG_USBD_NET_MDLM) + if (remote_mac_address && (strlen (remote_mac_address) == 12)) { + memcpy (remote_mac_address_buffer, remote_mac_address, 12); + } else if (serial) { + sprintf (remote_mac_address_buffer, "%06X%06X", CONFIG_USBD_NET_REMOTE_OUI, serial); + } else { + if (strlen (CONFIG_USBD_NET_REMOTE_MACADDR)) { + memcpy (remote_mac_address_buffer, CONFIG_USBD_NET_REMOTE_MACADDR, 12); + } else { + memcpy (remote_mac_address_buffer, "400002000001", 12); + } + } + remote_mac_address_buffer[12] = '\0'; + + // sanity check + if (strlen (remote_mac_address_buffer) != 12) { + dbg_init (0, "mac_address has wrong length: %s %d", remote_mac_address_buffer, + strlen (remote_mac_address_buffer)); + //return -EINVAL; + return; + } + dbg_init (1, "remote mac_address: %s", remote_mac_address_buffer); + +#if defined(CONFIG_USBD_NET_CDC) + // set the mac address in the descriptor + for (i = 0; + i < sizeof (cdc_comm_class_descriptions) / sizeof (struct usb_class_description); + i++) { + struct usb_class_description *class_description = cdc_comm_class_descriptions + i; + if (class_description->bDescriptorSubtype == USB_ST_ENF) { + class_description->description.ethernet_networking.iMACAddress = + remote_mac_address_buffer; + break; + } + } +#endif +#if defined(CONFIG_USBD_NET_MDLM) + // set the mac address in the descriptor + for (i = 0; + i < sizeof (mdlm_comm_class_descriptions) / sizeof (struct usb_class_description); + i++) { + struct usb_class_description *class_description = mdlm_comm_class_descriptions + i; + if (class_description->bDescriptorSubtype == USB_ST_ENF) { + class_description->description.ethernet_networking.iMACAddress = + remote_mac_address_buffer; + break; + } + } +#endif +#endif /* defined(CONFIG_USBD_NET_CDC) || defined(CONFIG_USBD_NET_MDLM) */ + +#if defined(CONFIG_USBD_MAC_AS_SERIAL_NUMBER) + if (bus->serial_number_str) { + kfree (bus->serial_number_str); + bus->serial_number_str = NULL; + } + bus->serial_number_str = strdup (remote_mac_address_buffer); + //net_device_description.iSerialNumber = remote_mac_address_buffer; + dbg_init (1, "setting iSerialNumber to remote mac_address: %s", remote_mac_address_buffer); +#endif + default_dev_addr[ETH_ALEN - 1] = 1; + +#ifdef CONFIG_USBD_NET_NFS_SUPPORT + init_waitqueue_head( &usb_netif_wq ); + net_create( device, function ); +#endif +} + +static void net_function_exit (struct usb_device_instance *device) +{ + dbg_init (1, "CLOSING **************************"); + net_destroy (device); +} + +/* Called to handle USB Events ************************************************************** */ + +/** + * net_event - process a device event + * @device: usb device + * @event: the event that happened + * + * Called by the usb device core layer to respond to various USB events. + * + * This routine IS called at interrupt time. Please use the usual precautions. + * + */ +void net_event (struct usb_device_instance *device, usb_device_event_t event, int data) +{ + int port = 0; + struct usb_function_instance *function; + struct usb_net_private *net_private; + + if (!(function = device->function_instance_array + port)) { + dbg_usbe (1, "function NULL"); + return; + } + + dbg_usbe (1,"%s data: %d privdata: %p", USBD_DEVICE_EVENTS(event), data, function->privdata); + + dbg_usbe (7, "%p %s %d previous_state: %d device_state: %d", + device, device->name, event, device->device_previous_state, device->device_state); + + + switch (event) { + + case DEVICE_CREATE: // a bus interface driver has created a usb device +#ifndef CONFIG_USBD_NET_NFS_SUPPORT + if (alwaysup) { + net_create (device, function); + } +#endif + break; + + case DEVICE_RESET: + net_device_check_condition(DEVICE_RESET); +#ifndef CONFIG_USBD_NET_NFS_SUPPORT + if (!alwaysup) { + net_destroy (device); + } +#endif + break; + + case DEVICE_CONFIGURED: // the host has found a driver that matches + net_device_check_condition(DEVICE_CONFIGURED); +#ifdef CONFIG_USBD_NET_NFS_SUPPORT + if ( !usb_is_configured ) { + wake_up( &usb_netif_wq ); + usb_is_configured = 1; + } +#endif + +#if defined(CONFIG_USBD_NET_CDC) + break; +#else + device->interface = 0; + device->alternate = 0; +#endif + + case DEVICE_SET_INTERFACE: // the host driver has selected an interface +#ifndef CONFIG_USBD_NET_NFS_SUPPORT + if (!alwaysup) { + net_create (device, function); + } +#endif + break; + + case DEVICE_BUS_INACTIVE: // suspend + net_device_check_condition(DEVICE_BUS_INACTIVE); + // XXX + if ((net_private = function->privdata)) { + //netproto_off (net_private->interface); + } + break; + + case DEVICE_BUS_ACTIVITY: // resume + net_device_check_condition(DEVICE_BUS_ACTIVITY); + // XXX + if ((net_private = function->privdata)) { + //netproto_on (net_private->interface); + } + break; + + case DEVICE_DESTROY: // the bus interface driver is unloading + net_destroy (device); + break; + + case DEVICE_FUNCTION_PRIVATE: + switch (data) { + case NET_SET_CRC: + break; + } + break; + + default: + break; + } +} + +/** + * net_recv_setup - called with a control URB + * @urb - pointer to struct urb + * + * Check if this is a setup packet, process the device request, put results + * back into the urb and return zero or non-zero to indicate success (DATA) + * or failure (STALL). + * + * This routine IS called at interrupt time. Please use the usual precautions. + * + */ +int net_recv_setup (struct urb *urb) +{ + struct usb_device_request *request; + struct usb_device_instance *device = urb->device; + int port = 0; + struct usb_net_private *net_private = (device->function_instance_array + port)->privdata; + + request = &urb->device_request; + + // handle USB Standard Request (c.f. USB Spec table 9-2) + if ((request->bmRequestType & USB_REQ_TYPE_MASK) != USB_REQ_TYPE_VENDOR) { + dbg_ep0 (1, "not vendor request: %x %x", request->bmRequestType, + request->bmRequestType & USB_REQ_TYPE_MASK); + return 0; // XXX + } + // handle all requests that return data (direction bit set on bm RequestType) + if ((request->bmRequestType & USB_REQ_DIRECTION_MASK)) { + dbg_ep0 (1, "Device-to-Host"); + } else { + dbg_ep0 (1, "Host-to-Device"); + switch (request->bRequest) { + case MCCI_ENABLE_CRC: + dbg_ep0 (1, "Enabling CRC"); + net_private->crc = 1; + usbd_device_event_irq (device, DEVICE_FUNCTION_PRIVATE, NET_SET_CRC); + break; + } + } + + return 0; +} + +/** + * net_recv_urb - called with a received URB + * @urb - pointer to struct urb + * + * Return non-zero if we failed and urb is still valid (not disposed) + * + * This routine IS called at interrupt time. Please use the usual precautions. + * + */ +int net_recv_urb (struct urb *urb) +{ + int port = 0; // XXX compound device + struct usb_device_instance *device; + struct usb_function_instance *function; + struct usb_net_private *net_private; + unsigned int length; + + struct sk_buff *skb = NULL; + int mtu; + + if (!urb || !(device = urb->device) || + !(function = device->function_instance_array + port) || + !(net_private = function->privdata)) { + return -EINVAL; + } + + if (!(net_private->flags & NET_INUSE)) { + netproto_rx_missed_error (net_private->interface); + return -EINVAL; + } + + + dbg_rx (5, "urb: %p len: %d maxtransfer: %d\n", urb, urb->actual_length, + net_private->maxtransfer); + + if (urb->status != RECV_OK) { + dbg_rx (0, "!RECV_OK urb->actual_length: %d maxtransfer: %d", urb->actual_length, + net_private->maxtransfer); + netproto_rx_fifo_error (net_private->interface); + return -EINVAL; + } + + length = urb->actual_length; + + if (length > net_private->maxtransfer) { + dbg_rx (0, "TOO LARGE urb->actual_length: %d maxtransfer: %d", urb->actual_length, + net_private->maxtransfer); + netproto_rx_over_error (net_private->interface); + return -EINVAL; + } + + if (!(skb = net_dev_alloc_skb (length))) { + dbg_rx (0, "cannot malloc skb"); + netproto_rx_missed_error (net_private->interface); + return -EINVAL; + } + + dbgPRINTmem (dbgflg_usbdfd_rx, 4, urb->buffer, length); + + if (net_private->crc) { + __u32 fcs; + + // do we need to check for extra null byte + if ((length % out_pkt_sz) == 1) { + + // fcs and copy length minus one byte first, if CRC32 is ok, then assume that we have an extra byte + if (((fcs = + fcs_memcpy32 (skb_put (skb, length - 1), urb->buffer, length - 1, + CRC32_INITFCS)) != CRC32_GOODFCS)) { + + // CRC32 check failed, fcs and copy last byte, if ok then continue otherwise fail + if ((fcs_memcpy32 + (skb_put (skb, 1), urb->buffer + length - 1, 1, + fcs) != CRC32_GOODFCS)) { + dbg_rx (1, "CRC32 failed on extra byte len: %d fcs: %08x", + length, fcs); + dbgPRINTmem (dbgflg_usbdfd_rx, 1, urb->buffer, length); + dev_kfree_skb_any (skb); + netproto_rx_crc_error (net_private->interface); + return -EINVAL; + } + } + } else { + if (((fcs = + fcs_memcpy32 (skb_put (skb, length), urb->buffer, length, + CRC32_INITFCS)) != CRC32_GOODFCS)) { + dbg_rx (1, "CRC32 failed len: %d fcs: %08x", length, fcs); + dbgPRINTmem (dbgflg_usbdfd_rx, 4, urb->buffer, length); + dev_kfree_skb_any (skb); + netproto_rx_crc_error (net_private->interface); + return -EINVAL; + } + } + skb_trim (skb, skb->len - 4); + } + + else { + memcpy (skb_put (skb, length), urb->buffer, length); + } + + if ((skb->len > net_private->maxtransfer) || (skb->tail > skb->end)) { + dbg_rx (0, "ERROR skb: %p head: %p data: %p tail: %p end: %p len: %d", + skb, skb->head, skb->data, skb->tail, skb->end, skb->len); + dev_kfree_skb_any (skb); + netproto_rx_length_error (net_private->interface); + return -EINVAL; + } + // XXX should not be needed + mtu = netproto_mtu (net_private->interface); + if (skb->len > (mtu + 14)) { + skb_trim (skb, (netproto_mtu (net_private->interface) + 14)); + } + + usbd_recycle_urb (urb); + +#ifdef RXECHO + dbg_rx (3, "skb: %p head: %p data: %p tail: %p end: %p len: %d", + skb, skb->head, skb->data, skb->tail, skb->end, skb->len); + dbgPRINTmem (dbgflg_usbdfd_rx, 3, skb->data, skb->len); +#endif + + + // pass it up, free skb if non zero + if (netproto_recv (net_private->interface, skb)) { + dev_kfree_skb_any (skb); + //netproto_rx_dropped(net_private->interface); + } + return 0; +} + + +/** + * net_urb_sent - called to indicate URB transmit finished + * @urb: pointer to struct urb + * @rc: result + * + * The usb device core layer will use this to let us know when an URB has + * been finished with. + * + * This routine IS called at interrupt time. Please use the usual precautions. + * + */ +int net_urb_sent (struct urb *urb, int rc) +{ + int port = 0; // XXX compound device + struct usb_device_instance *device; + struct usb_function_instance *function; + struct usb_net_private *net_private; + int interface; + struct sk_buff *skb; + + if (!urb || !(device = urb->device) || + !(function = device->function_instance_array + port) || + !(net_private = function->privdata)) { + return -EINVAL; + } + if (!(net_private->flags & NET_INUSE)) { + // The interface is being/has been shutdown. + // XXX Check this logic for mem leaks, but the + // shutdown should have taken care of it, so + // a leak is not likely. + return 0; + } + interface = net_private->interface; + + // retrieve skb pointer and unlink from urb pointers + skb = (struct sk_buff *) urb->privdata; + + // if no CRC then we are pointing at skb->buffer, so don't attempt to free urb->buffer + if (!net_private->crc) { + urb->buffer = NULL; + urb->actual_length = 0; + } + urb->privdata = NULL; + usbd_dealloc_urb (urb); + + // tell netproto we are done with the skb, it will test for NULL + netproto_done (interface, skb, rc != SEND_FINISHED_OK); + return 0; +} + + +/* * + * net_xmit_skb - called to transmit an skb + * @interface: which interface + * @skb: skb to send + * + */ +static int net_xmit_skb (int interface, struct sk_buff *skb) +{ + int port = 0; // XXX compound device + struct usb_net_private *net_private; + struct urb *urb; + + //dbg_tx(5,"skb: %p", skb); + + // XXX XXX + // return -EINVAL; + + net_private = &net_private_array[interface]; + + if (!net_private->device) { + dbg_tx (0, "net_private NULL"); + return -EUNATCH; + } +#if 0 +#ifdef CONFIG_ARCH_SA1100 + // compensate for SA-1110 DMA setup problems by sending three dummy urbs before + // any real traffic, these will be dropped at the far end + if (net_private->first) { + int i; + dbg_tx (1, "sending three init URBS"); + for (i = 0; i < 3; i++) { + if ((urb = + usbd_alloc_urb (net_private->device, + (net_private->device->function_instance_array + port), + CONFIG_USBD_NET_IN_ENDPOINT, 200))) { + memset (urb->buffer, i, 200); + urb->actual_length = 100; + usbd_send_urb (urb); + } + } + net_private->first = 0; + } +#endif +#endif + + if (! + (urb = + usbd_alloc_urb (net_private->device, + (net_private->device->function_instance_array + port), + CONFIG_USBD_NET_IN_ENDPOINT, skb->len + 5 + in_pkt_sz))) { + dbg_tx (0, "urb alloc failed len: %d", skb->len); + return -ENOMEM; + } + + if (net_private->crc) { + u32 send_fcs; + send_fcs = fcs_memcpy32 (urb->buffer, skb->data, skb->len, CRC32_INITFCS); + urb->actual_length = skb->len; + + // check if we need to pre-pend a NULL byte before CRC + if ((urb->actual_length % in_pkt_sz) == (in_pkt_sz - 4)) { + send_fcs = fcs_pad32 (urb->buffer + urb->actual_length, 1, send_fcs); + urb->actual_length++; + dbg_tx (2, "do_fcs32: adding NULL byte before CRC"); + } + // if transfer is less than packetsize guarantee that is at lest two packets + else if (skb->len < (in_pkt_sz - 3)) { + dbg_tx (2, "do_fcs32: len: %d padding; %d", skb->len, + (in_pkt_sz - skb->len - 3)); + send_fcs = + fcs_pad32 (urb->buffer + urb->actual_length, (in_pkt_sz - skb->len - 3), + send_fcs); + urb->actual_length = in_pkt_sz - 3; + } +#if defined(CONFIG_USBD_NET_PADDED) +//#warning LINKUP Padding defined + // Linkup - minimum size of last packet + dbg_tx (2, "do_fcs32: checking actual: %d pktsize: %d remainder: %d", + urb->actual_length, in_pkt_sz, (urb->actual_length + 4) % in_pkt_sz); + + while (((urb->actual_length + 4) % in_pkt_sz) <= (in_pkt_sz - 2)) { + send_fcs = fcs_pad32 (urb->buffer + urb->actual_length, 1, send_fcs); + urb->actual_length++; + //dbg_tx(3, "do_fcs32: adding NULL byte(s) before CRC"); + } +#endif + + send_fcs = ~send_fcs; + urb->buffer[urb->actual_length++] = send_fcs & 0xff; + urb->buffer[urb->actual_length++] = (send_fcs >> 8) & 0xff; + urb->buffer[urb->actual_length++] = (send_fcs >> 16) & 0xff; + urb->buffer[urb->actual_length++] = (send_fcs >> 24) & 0xff; + } + + else { + urb->buffer = skb->data; + urb->actual_length = skb->len; + } + +#ifdef CONFIG_USBD_NO_ZLP_SUPPORT +//#warning - +//#warning NO ZLP SUPPORT DEFINED + // check if we need to append a NULL byte + if (!(urb->actual_length % in_pkt_sz)) { + dbg_tx (3, "adding NULL byte after CRC"); + urb->buffer[urb->actual_length++] = 0; + } +#endif + + // save skb for netproto_done + urb->privdata = (void *) skb; + + dbg_tx (4, "skb: %p head: %p data: %p tail: %p len: %d endpoint: %x", + skb, skb->head, skb->data, skb->tail, skb->len, CONFIG_USBD_NET_IN_ENDPOINT); + dbg_tx (4, "urb: %p buffer: %p acutal_length: %d", urb, urb->buffer, urb->actual_length); + dbgPRINTmem (dbgflg_usbdfd_tx, 4, urb->buffer, urb->actual_length); + + if (usbd_send_urb (urb)) { + dbg_tx (1, "usbd_send_urb failed"); + urb->privdata = NULL; + usbd_dealloc_urb (urb); + return -ECOMM; + } + return 0; +} + +static int net_set_addr (int dev, void *addr, int len) +{ + return 0; +} + +/** + * net_tx_timeout - called by netproto if transmit times out + * @dev: which device + * + * Called to cancel an in progress transmit. Note that this routine is advisory, + * it just sends a message to the lower layer which may or may not actually do + * anything about it. + */ +static int net_tx_timeout (int interface) +{ + struct usb_net_private *net_private; + + dbg_tx (1, "if=%d", interface); + + net_private = &net_private_array[interface]; + + if (!net_private->device) { + return -EINVAL; + } + + return usbd_cancel_urb (net_private->device, NULL); +} + +struct usb_function_operations function_ops = { + event:net_event, + recv_urb:net_recv_urb, + recv_setup:net_recv_setup, + urb_sent:net_urb_sent, +#if 0 + alloc_urb_data:net_alloc_urb_data, + dealloc_urb_data:net_dealloc_urb_data +#endif + function_init:net_function_init, + function_exit:net_function_exit, +}; + +struct usb_function_driver function_driver = { + name:"Generic Network", + ops:&function_ops, + device_description:&net_device_description, + configurations:sizeof (net_description) / sizeof (struct usb_configuration_description), + configuration_description:net_description, + this_module:THIS_MODULE, +}; + + + +/* proc file system ********************************************************************** */ +int net_device_condition = NET_DEVICE_CONDITION_UNKNOWN; +static int net_device_received_command = 0; +static int net_device_bus_active = 0; +static struct timer_list net_device_fail_check_timer; + +static void net_device_fail_check(unsigned long data) +{ + if (net_device_condition == NET_DEVICE_CONDITION_RESET) { + net_device_condition = NET_DEVICE_CONDITION_FAIL; + } +} + +static void net_device_check_condition(usb_device_event_t event) +{ + + switch (event) { + case DEVICE_RESET: + del_timer(&net_device_fail_check_timer); + + net_device_condition = NET_DEVICE_CONDITION_RESET; + net_device_received_command = 0; + net_device_bus_active = 1; + + net_device_fail_check_timer.function = net_device_fail_check; + net_device_fail_check_timer.expires = jiffies + 30 * 100; + add_timer(&net_device_fail_check_timer); + break; + + case DEVICE_CONFIGURED: + net_device_received_command = 1; + break; + + case DEVICE_BUS_INACTIVE: + net_device_bus_active = 0; + break; + + case DEVICE_BUS_ACTIVITY: + net_device_bus_active = 1; + break; + + default: + break; + } +} + +/* * + * net_device_proc_read - implement proc file system read. + * @file: xx + * @buf: xx + * @count: xx + * @pos: xx + * + * Standard proc file system read function. + * + * We let upper layers iterate for us, *pos will indicate which device to return * statistics for. + */ +static ssize_t +net_device_proc_read_condition(struct file *file, char *buf, size_t count, loff_t * pos) +{ + int len = 0; + char *p; + + if (*pos > 0) + return 0; + + switch (net_device_condition) { + case NET_DEVICE_CONDITION_UNKNOWN: + default: + p = "Unknown\n"; + break; + case NET_DEVICE_CONDITION_RESET: + p = "Enumeration Start\n"; + break; + case NET_DEVICE_CONDITION_OK: + if ( !net_device_bus_active ) { + p = "Unknown -- bus inactive\n"; + } else { + p = "Enumeration OK\n"; + } + break; + case NET_DEVICE_CONDITION_FAIL: + if ( !net_device_bus_active ) { + p = "Unknown -- bus inactive\n"; + } else + if ( !net_device_received_command ) { + p = "Unknown -- command wasn't received from host\n"; + // USB cable not connected + } else { + p = "Enumeration Fail\n"; + } + break; + } + + len = strlen(p); + *pos += len; + if (len > count) { + len = -EINVAL; + } + else if (len > 0 && copy_to_user(buf, p, len)) { + len = -EFAULT; + } + return len; +} + +/* * + * usbd_monitor_proc_write - implement proc file system write. + * @file + * @buf + * @count + * @pos + * + * Proc file system write function, used to signal monitor actions complete. + * (Hotplug script (or whatever) writes to the file to signal the completion + * of the script.) An ugly hack. + */ +static ssize_t +net_device_proc_write_condition(struct file *file, const char *buf, size_t count, loff_t * pos) +{ + if (net_device_condition != NET_DEVICE_CONDITION_RESET) + net_device_condition = NET_DEVICE_CONDITION_UNKNOWN; + return count; +} + +static struct file_operations net_device_proc_operations_condition = { + read: net_device_proc_read_condition, + write:net_device_proc_write_condition, +}; + + + +/* Module init and exit ********************************************************************** */ + +unsigned char hexdigit (char c) +{ + return isxdigit (c) ? (isdigit (c) ? (c - '0') : (c - 'A' + 10)) + : 0; +} + +/* + * net_modinit - module init + * + */ +static int __init net_modinit (void) +{ + char local_mac_address_buffer[13]; + int i; + + debug_option *op = find_debug_option (dbg_table, "net"); + + printk (KERN_INFO "%s (dbg=\"%s\",alwaysup=%d,OUT=%d,IN=%d)\n", + __usbd_module_info, dbg ? dbg : "", alwaysup, out_pkt_sz, in_pkt_sz); + + printk (KERN_INFO "vendorID: %x productID: %x\n", CONFIG_USBD_VENDORID, + CONFIG_USBD_PRODUCTID); + + if (NULL != op) { + op->sub_table = netproto_get_dbg_table (); + } + if (0 != scan_debug_options ("net_fd", dbg_table, dbg)) { + return (-EINVAL); + } + + memset (net_private_array, 0, sizeof (net_private_array)); + + // verify pkt sizes not too small + if (out_pkt_sz < 3 || in_pkt_sz < 3) { + dbg_init (0, "Rx pkt size %d or Tx pkt size %d too small", out_pkt_sz, in_pkt_sz); + return (-EINVAL); + } + // Check for non-default packet sizes + if (out_pkt_sz != CONFIG_USBD_NET_OUT_PKTSIZE) { +#ifdef CONFIG_USBD_NET_SAFE + net_default[0].wMaxPacketSize = out_pkt_sz; +#endif /* CONFIG_USBD_NET_SAFE */ +#ifdef CONFIG_USBD_NET_CDC + net_alt_1_endpoints[0].wMaxPacketSize = out_pkt_sz; +#endif + } + + if (in_pkt_sz != CONFIG_USBD_NET_IN_PKTSIZE) { +#ifdef CONFIG_USBD_NET_SAFE + net_default[1].wMaxPacketSize = in_pkt_sz; +#endif /* CONFIG_USBD_NET_SAFE */ +#ifdef CONFIG_USBD_NET_CDC + net_alt_1_endpoints[1].wMaxPacketSize = in_pkt_sz; +#endif + } + + dbg_init (1, "Rx pkt size %d, Tx pkt size %d", out_pkt_sz, in_pkt_sz); + + if (vendor_id) { + net_device_description.idVendor = vendor_id; + } + if (product_id) { + net_device_description.idProduct = product_id; + } + // initialize the netproto library + if (netproto_modinit (if_name, MAX_INTERFACES)) { + dbg_init (0, "netproto_modinit failed"); + return -EINVAL; + } + + if (local_mac_address && (strlen (local_mac_address) == 12)) { + dbg_init (1, "using local_mac_address: %s", local_mac_address); + memcpy (local_mac_address_buffer, local_mac_address, 12); + } else { + dbg_init (1, "using LOCAL_MACADDR: %s", CONFIG_USBD_NET_LOCAL_MACADDR); + memcpy (local_mac_address_buffer, CONFIG_USBD_NET_LOCAL_MACADDR, 12); + } + + local_mac_address_buffer[12] = '\0'; + + dbg_init (1, "local_mac_address_buffer: %s", local_mac_address_buffer); + dbg_init (1, "LOCAL_MACADDR: %s", CONFIG_USBD_NET_LOCAL_MACADDR); + if (local_mac_address) { + dbg_init (1, "localmac_address: %s len: %d", local_mac_address, + strlen (local_mac_address)); + } + // save the mac address for the network device + for (i = 0; i < ETH_ALEN; i++) { + default_dev_addr[i] = + hexdigit (local_mac_address_buffer[i * 2]) << 4 | + hexdigit (local_mac_address_buffer[i * 2 + 1]); + //dbg_init(0, "local_mac_address[%d]: %02x %02x %d %d %02x", i, + // local_mac_address_buffer[i*2], local_mac_address_buffer[i*2+1], + // hexdigit(local_mac_address_buffer[i*2]), hexdigit(local_mac_address_buffer[i*2+1]), + // default_dev_addr[i]); + } + + // create network interface + //net_create(0); + + // register us with the usb device support layer + if (usbd_register_function (&function_driver)) { + dbg_init (0, "usbd_register_function failed"); + netproto_modexit (); + return -EINVAL; + } + + { + struct proc_dir_entry *p; + if ((p = create_proc_entry("usb-condition", 0, 0)) == NULL) { + netproto_modexit(); + return -ENOMEM; + } + p->proc_fops = &net_device_proc_operations_condition; + + init_timer(&net_device_fail_check_timer); + } + + return 0; +} + +/* + * function_exit - module cleanup + * + */ +static void __exit net_modexit (void) +{ + + dbg_init (1, "exiting"); + + // de-register us with the usb device support layer + usbd_deregister_function (&function_driver); + + // destroy network interface + //net_destroy(0); + + // tell the netproto library to exit + netproto_modexit (); + + remove_proc_entry("usb-condition", NULL); + del_timer(&net_device_fail_check_timer); + +} + +module_init (net_modinit); +module_exit (net_modexit); diff -uNr linux.org/drivers/usb/device/net_fd/net-fd.h linux/drivers/usb/device/net_fd/net-fd.h --- linux.org/drivers/usb/device/net_fd/net-fd.h Thu Jan 1 08:00:00 1970 +++ linux/drivers/usb/device/net_fd/net-fd.h Thu May 15 15:54:48 2003 @@ -0,0 +1,30 @@ +/* + * linux/drivers/usbd/net_fd/net-fd.h -- L7205 USB controller driver. + * + * Copyright (c) 2000, 2001, 2002 Lineo + * Copyright (c) 2001 Hewlett Packard + * + * By: + * Stuart Lynne , + * Tom Rushworth , + * Bruce Balden + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + * + */ + +#ifndef NET_FD_H +#define NET_FD_H 1 +#endif diff -uNr linux.org/drivers/usb/device/net_fd/netproto.c linux/drivers/usb/device/net_fd/netproto.c --- linux.org/drivers/usb/device/net_fd/netproto.c Thu Jan 1 08:00:00 1970 +++ linux/drivers/usb/device/net_fd/netproto.c Thu May 15 15:54:48 2003 @@ -0,0 +1,1137 @@ +/* + * linux/drivers/usbd/net_fd/netproto.c - network prototype library + * + * Copyright (c) 2000, 2001, 2002 Lineo + * Copyright (c) 2001 Hewlett Packard + * + * By: + * Stuart Lynne , + * Tom Rushworth , + * Bruce Balden + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + * + */ + +/* netproto + * + * This is a simple linux network driver library that is suitable for + * implementing layered network drivers. + * + * E.g. networking over adsl or usb. + * + */ + +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "../usbd-debug.h" + +#ifndef MODULE +#undef GET_USE_COUNT +#undef THIS_MODULE +#define GET_USE_COUNT(foo) 0 +#define THIS_MODULE 0 +#endif + +/* Debug switches ****************************************************************************** */ + +int dbgflg_usbdfd_pinit = 0; +int dbgflg_usbdfd_poc = 0; +int dbgflg_usbdfd_prx = 0; +int dbgflg_usbdfd_ptx = 0; +int dbgflg_usbdfd_pmgmt = 0; + +static debug_option dbg_table[] = { + {&dbgflg_usbdfd_pinit, NULL, "init", "initialization/termination handling"}, + {&dbgflg_usbdfd_poc, NULL, "oc", "open/close handling"}, + {&dbgflg_usbdfd_prx, NULL, "rx", "receive (from host)"}, + {&dbgflg_usbdfd_ptx, NULL, "tx", "transmit (to host)"}, + {&dbgflg_usbdfd_pmgmt, NULL, "mgmt", "management (ioctl) handling"}, + {NULL, NULL, NULL, NULL} +}; + +#define dbg_pinit(lvl,fmt,args...) dbgPRINT(dbgflg_usbdfd_pinit,lvl,fmt,##args) +#define dbg_poc(lvl,fmt,args...) dbgPRINT(dbgflg_usbdfd_poc,lvl,fmt,##args) +#define dbg_prx(lvl,fmt,args...) dbgPRINT(dbgflg_usbdfd_prx,lvl,fmt,##args) +#define dbg_ptx(lvl,fmt,args...) dbgPRINT(dbgflg_usbdfd_ptx,lvl,fmt,##args) +#define dbg_pmgmt(lvl,fmt,args...) dbgPRINT(dbgflg_usbdfd_pmgmt,lvl,fmt,##args) + +debug_option *netproto_get_dbg_table (void) +{ + return (dbg_table); +} + + +// This needs to be after the dbg_* macros because of inlines.... +#include "netproto.h" + + +char *netproto_procname; // name to use for proc filesystem +int netproto_devices; // maximum number of interfaces that can be created +struct netproto_dev **netproto_device_array; // pointer to array of pointers to netproto dev structures +rwlock_t netproto_rwlock = RW_LOCK_UNLOCKED; // lock for netproto device array access + + +/* Network Device support functions *************************************************************** + * + * net_dev.get_stats = netproto_get_stats; + * net_dev.set_mac_address = netproto_set_mac_addr + * net_dev.hard_start_xmit = netproto_start_xmit + * net_dev.do_ioctl = netproto_do_ioctl + * net_dev.set_multicast = netproto_set_multicast // + * net_dev.open = netproto_open // tell management thread to start ATM connection + * net_dev.stop = netproto_stop // tell management thread to close ATM connection + * net_dev.tx_timeout = netproto_tx_timeout // tell the driver that tx has timed out + * net_dev.set_config = netproto_set_config // set ATM configuration details + */ + +/* * + * netproto_init - network device open function + * @net_dev: pointer to &struct net_device + * + * Initialize the device. + */ +static int netproto_init (struct net_device *net_dev) +{ + dbg_pinit (1, "%s", net_dev->name); + //if (netif_running(net_dev)) { + // return -EBUSY; + //} + + return 0; +} + +/* * + * netproto_uninit - network device uninit function + * @net_dev: pointer to &struct net_device + * + * UnInitialize the device. + */ +static void netproto_uninit (struct net_device *net_dev) +{ + dbg_pinit (1, "%s", net_dev->name); + return; +} + +/* * + * netproto_open - network device open function + * @net_dev: pointer to &struct net_device + * + * Start the device. + */ +static int netproto_open (struct net_device *net_dev) +{ + //struct netproto_dev *dev = net_dev->priv; + + dbg_poc (1, "%s", net_dev->name); + if (netif_running (net_dev)) { + dbg_poc (1, "%s", net_dev->name); + return -EBUSY; + } + + netif_wake_queue (net_dev); +#ifdef CONFIG_USBD_NET_NFS_SUPPORT + if ( !usb_is_configured ) { + if ( !in_interrupt() ) { + printk("Please replug USB cable and then ifconfig host interface.\n"); + interruptible_sleep_on( &usb_netif_wq ); + } else { + printk("Warning! In interrupt!\n"); + } + } +#endif + + dbg_poc (2, "OK %s", net_dev->name); + + return 0; +} + + +/* * + * netproto_stop - network device stop function + * @net_dev: pointer to &struct net_device + * + * Stop the device. + */ +static int netproto_stop (struct net_device *net_dev) +{ + //struct netproto_dev *device = net_dev->priv; + + dbg_poc (1, "%s", net_dev->name); + netif_stop_queue (net_dev); + + return 0; +} + + +/* * + * netproto_get_stats - network device get stats function + * @net_dev: pointer to &struct net_device + * + * Retreive network device stats structure. + */ +static struct net_device_stats *netproto_get_stats (struct net_device *net_dev) +{ + struct netproto_dev *device = net_dev->priv; + return &device->stats; +} + + +#if 0 +/* * + * netproto_set_multicast - + * @net_dev: pointer to &struct net_device + * + * Set device to match multicast status + */ +static void netproto_set_multicast (struct net_device *net_dev) +{ + //struct netproto_dev *device = net_dev->priv; + + if (net_dev->flags & IFF_PROMISC) { + // enable promiscuous mode + } +#if 0 + else if ((net_dev->flags & IFF_ALLMULTI) || (net_dev->mc_count > HW_MAX_ADDRS)) { + // disable promiscous mode, set filter + } else if (net_dev->mc_count) { + // walk address list and load filters + } +#endif + else { + // disable promiscuous mode + } + return; +} +#endif + +/* * + * netproto_set_mac_addr - network device set mac address function + * @net_dev: pointer to &struct net_device + * @p: pointer to sockaddr structure + * + * Set mac address. + */ +static int netproto_set_mac_addr (struct net_device *net_dev, void *p) +{ + struct netproto_dev *device = net_dev->priv; + struct sockaddr *addr = p; + + if (netif_running (net_dev)) { + return -EBUSY; + } + + { + unsigned long flags; + write_lock_irqsave (&device->rwlock, flags); + memcpy (net_dev->dev_addr, addr->sa_data, net_dev->addr_len); + device->addr_set = 1; + write_unlock_irqrestore (&device->rwlock, flags); + } + + // push down + if (device->set_addr) { + device->set_addr (device->interface, p, ETH_ALEN); + } + + return 0; +} + +/* * + * netproto_tx_timeout - network device transmit timeout function + * @net_dev: pointer to &struct net_device + * + * Called by network layer to tell the driver that transmit has timed out. + */ +static void netproto_tx_timeout (struct net_device *net_dev) +{ + struct netproto_dev *device = net_dev->priv; + + dbg_ptx (7, ""); + + if (device->tx_timeout) { + device->tx_timeout (device->interface); + } +} + + +/* * + * netproto_set_config - network device set config function + * @net_dev: pointer to &struct net_device + * @map: pointer to &struct ifmap + * + * Only allow things to be set if not running. + */ +static int netproto_set_config (struct net_device *net_dev, struct ifmap *map) +{ + if (netif_running (net_dev)) { + return -EBUSY; + } + if (map->mem_start) { + net_dev->mem_start = map->mem_start; + } + if (map->irq) { + net_dev->irq = map->irq; + } + if (map->base_addr) { + net_dev->base_addr = map->base_addr; + } + // XXX copy to device + return 0; +} + + +/* * + * netproto_change_mtu - network device set config function + * @net_dev: pointer to &struct net_device + * @mtu: max transmission unit + * + * Set MTU, if running we can only change it to something less + * than or equal to MTU when PVC opened. + */ +static int netproto_change_mtu (struct net_device *net_dev, int mtu) +{ + struct netproto_dev *dev = net_dev->priv; + + if (netif_running (net_dev)) { + if (mtu > dev->mtu) { + return -EBUSY; + } + net_dev->mtu = mtu; + return 0; + } + net_dev->mtu = mtu; + // XXX copy to device + return 0; +} + + +/* * + * netproto_start_xmit - network device start xmit function + * @skb: pointer to &struct sk_buff to send + * @net_dev: pointer to &struct net_device + * + * Called by kernel network layer to transmit a frame on this interface, + * grab locks and pass to netproto_do_xmit(). + * + * The network layer flow control is managed to prevent more than + * device->max_queue_entries from being outstanding. See also + * netproto_done(). + * + * + */ +static int netproto_start_xmit (struct sk_buff *skb, struct net_device *net_dev) +{ + struct netproto_dev *device = net_dev->priv; + int len = skb->len; + int rc; + + dbg_ptx (7, ""); + dbg_ptx (7, "skb: %p", skb); + + if (!netif_carrier_ok (net_dev)) { + dbg_ptx (0, "no carrier %p", skb); + dev_kfree_skb_any (skb); + //netif_stop_queue(net_dev); + return 0; + } + // stop queue, it will be restart only when we are ready for another skb + netif_stop_queue (net_dev); + + // lock and update some stats + { + unsigned long flags; + write_lock_irqsave (&device->rwlock, flags); + device->stopped++; + device->queued_entries++; + device->queued_bytes += skb->len; + write_unlock_irqrestore (&device->rwlock, flags); + } + + // Set the timestamp for tx timeout + net_dev->trans_start = jiffies; + + // XXX should we use skb->stamp here + *(time_t *) (&skb->cb) = jiffies; + +#if 0 + dbg_ptx (7, "skb: %p head: %p data: %p tail: %p len: %d", + skb, skb->head, skb->data, skb->tail, skb->len); + dbgPRINTmem (dbgflg_usbdfd_ptx, 7, skb->data, skb->len); +#endif + // push it down to next layer, handle results + if ((rc = device->xmit_skb (device->interface, skb))) { + + switch (rc) { + + case -EINVAL: + case -EUNATCH: + dbg_ptx (0, "not attached, send failed: %d", rc); + netproto_tx_carrier_errors (device->interface); + netif_wake_queue (net_dev); + break; + + case -ENOMEM: + dbg_ptx (0, "no mem, send failed: %d", rc); + netproto_tx_fifo_errors (device->interface); + netif_wake_queue (net_dev); + break; + + case -ECOMM: + dbg_ptx (0, "comm failure, send failed: %d %p", rc, net_dev); + netproto_tx_dropped (device->interface); + break; + + } + dev_kfree_skb_any (skb); + } else { + + // possibly restart queue + netproto_tx_packets (device->interface, len); + + { + read_lock (&device->rwlock); + if ((device->queued_entries < device->max_queue_entries) + && (device->queued_bytes < device->max_queue_bytes)) { + read_unlock (&device->rwlock); + netif_wake_queue (net_dev); + } + read_unlock (&device->rwlock); + } + } + return 0; +} + + + + +/* Ioctl support functions ************************************************************************ + */ + + +/* * +* netproto_do_ioctl - network device do_ioctl function +* @net_dev: pointer to &struct net_device +* @rp: pointer to &struct ifreq +* @cmd: ioctl cmd +* +* This handles ioctls actually performed on our device - we must return +* -ENOIOCTLCMD for any unrecognized ioctl + */ +static int netproto_do_ioctl (struct net_device *net_dev, struct ifreq *rp, int cmd) +{ + //int rc; + + dbg_pmgmt (1, "%s cmd=%d", net_dev->name, cmd); + + return -ENOIOCTLCMD; +} + + +/* Proc Filesystem support ********************************************************************* */ + + +/* * +* netproto_proc_read - implement proc file system read. +* @file: file +* @buf: buffer +* @count: character count +* @pos: file position +* +* Standard proc file system read function. +* +* We let upper layers iterate for us, *pos will indicate which device to return +* statistics for. + */ +static ssize_t netproto_proc_read (struct file *file, char *buf, size_t count, loff_t * pos) +{ + unsigned long page; + int len = 0; + int index; + + struct netproto_dev *dev; + int i; + + // get a page, max 4095 bytes of data... + if (!(page = get_free_page (GFP_ATOMIC))) { + return -ENOMEM; + } + + len = 0; + index = (*pos)++; + if (index == 0) { + len += sprintf ((char *) page + len, + " MAC ESI VCC Skbs Forwarded " + "Stopped Deferred TimeInQ uS Avg Q QueueLen MaxQ Conn\n"); + len += sprintf ((char *) page + len, + "%48slo hi lpkt:cel hpkt:cel lo hi lo hi lo hi\n", + ""); + } + // lock and iterate across devices to get access to N'th device, return it's stats + read_lock (&netproto_rwlock); + + for (i = 0; i < netproto_devices; i++) { + + dev = netproto_device_array[i]; + + if (index-- == 0) { + // max 4095 bytes of data... + len += + sprintf ((char *) page + len, + "%.16s %2d [%02X:%02X:%02X:%02X:%02X:%02X]", + dev->net_dev->name, dev->interface, dev->net_dev->dev_addr[0], + dev->net_dev->dev_addr[1], dev->net_dev->dev_addr[2], + dev->net_dev->dev_addr[3], dev->net_dev->dev_addr[4], + dev->net_dev->dev_addr[5]); + + len += sprintf ((char *) page + len, " %s ", dev->addr_mac ? "y" : "n"); + + len += sprintf ((char *) page + len, "[%02d.%02d.%02d]", + (int) (dev->net_dev->mem_start >> 24) & 0xff, + (int) (dev->net_dev->mem_start >> 16) & 0xff, + (int) (dev->net_dev->mem_start) & 0xffff); + + len += + sprintf ((char *) page + len, " [%6d %2d]", dev->stopped, + dev->stopped - dev->restarts); + len += + sprintf ((char *) page + len, " [%4ld]", + dev->samples ? ((dev->jiffies * 1000) / dev->samples) : 0); + + len += + sprintf ((char *) page + len, " [%3ld]", + dev->samples ? ((dev->avg_queue_entries) / dev->samples) : 0); + + len += sprintf ((char *) page + len, " [%3d]", dev->queued_entries); + len += sprintf ((char *) page + len, " [%3d]", dev->max_queue_entries); + + len += sprintf ((char *) page + len, "J[%6ld]", dev->jiffies); + len += sprintf ((char *) page + len, "S[%6ld]", dev->samples); + + len += sprintf ((char *) page + len, "\n"); + break; + } + } + read_unlock (&netproto_rwlock); + + if (len > count) { + len = -EINVAL; + } + + else if (len > 0 && copy_to_user (buf, (char *) page, len)) { + len = -EFAULT; + } + + free_page (page); + return len; +} + +static struct file_operations netproto_proc_operations = { + read:netproto_proc_read, +}; + + +/* Library Interface functions ***************************************************************** */ + + +/* * +* netproto_rx_dropped +* @int - network interface +* + */ +void netproto_rx_dropped (int interface) +{ + struct netproto_dev *device; + + if ((interface < netproto_devices) && (device = netproto_device_array[interface])) { + device->stats.rx_dropped++; + dbg_pinit (1, "%d %ld %ld", interface, + device->stats.rx_dropped, device->stats.rx_errors); + } +} + +/* * +* netproto_rx_length_error +* @int - network interface +* + */ +void netproto_rx_length_error (int interface) +{ + struct netproto_dev *device; + + if ((interface < netproto_devices) && (device = netproto_device_array[interface])) { + device->stats.rx_length_errors++; + device->stats.rx_dropped++; + device->stats.rx_errors++; + dbg_pinit (1, "%d %ld %ld %ld", interface, + device->stats.rx_length_errors, device->stats.rx_dropped, + device->stats.rx_errors); + } +} + +/* * +* netproto_rx_over_error +* @int - network interface +* + */ +void netproto_rx_over_error (int interface) +{ + struct netproto_dev *device; + + if ((interface < netproto_devices) && (device = netproto_device_array[interface])) { + device->stats.rx_over_errors++; + device->stats.rx_dropped++; + device->stats.rx_errors++; + dbg_pinit (1, "%d %ld %ld %ld", interface, device->stats.rx_over_errors, + device->stats.rx_dropped, device->stats.rx_errors); + } +} + +/* * +* netproto_rx_crc_error +* @int - network interface +* + */ +void netproto_rx_crc_error (int interface) +{ + struct netproto_dev *device; + //return; + + if ((interface < netproto_devices) && (device = netproto_device_array[interface])) { + device->stats.rx_crc_errors++; + device->stats.rx_dropped++; + device->stats.rx_errors++; + dbg_pinit (3, "%d %ld %ld %ld", interface, device->stats.rx_crc_errors, + device->stats.rx_dropped, device->stats.rx_errors); + } +} + +/* * +* netproto_rx_frame_error +* @int - network interface +* + */ +void netproto_rx_frame_error (int interface) +{ + struct netproto_dev *device; + //return; + + if ((interface < netproto_devices) && (device = netproto_device_array[interface])) { + device->stats.rx_frame_errors++; + device->stats.rx_dropped++; + device->stats.rx_errors++; + dbg_pinit (1, "%d %ld %ld %ld", interface, + device->stats.rx_frame_errors, device->stats.rx_dropped, + device->stats.rx_errors); + } +} + +/* * +* netproto_rx_fifo_error +* @int - network interface +* + */ +void netproto_rx_fifo_error (int interface) +{ + struct netproto_dev *device; + //return; + + if ((interface < netproto_devices) && (device = netproto_device_array[interface])) { + device->stats.rx_fifo_errors++; + device->stats.rx_dropped++; + device->stats.rx_errors++; + dbg_pinit (1, "%d %ld %ld %ld", interface, device->stats.rx_fifo_errors, + device->stats.rx_dropped, device->stats.rx_errors); + } +} + +/* * +* netproto_rx_missed_error +* @int - network interface +* + */ +void netproto_rx_missed_error (int interface) +{ + struct netproto_dev *device; + //return; + + if ((interface < netproto_devices) && (device = netproto_device_array[interface])) { + device->stats.rx_missed_errors++; + device->stats.rx_dropped++; + device->stats.rx_errors++; + dbg_pinit (1, "%d %ld %ld %ld", interface, + device->stats.rx_missed_errors, device->stats.rx_dropped, + device->stats.rx_errors); + } +} + + +/* * +* netproto_rx_packets +* @int - network interface +* + */ +void netproto_rx_packets (int interface, int len) +{ + struct netproto_dev *device; + //return; + + if ((interface < netproto_devices) && (device = netproto_device_array[interface])) { + device->stats.rx_packets++; + device->stats.rx_bytes += len; + } +} + + +/* * +* netproto_tx_dropped +* @int - network interface +* + */ +void netproto_tx_dropped (int interface) +{ + struct netproto_dev *device; + //return; + + if ((interface < netproto_devices) && (device = netproto_device_array[interface])) { + device->stats.tx_dropped++; + } +} + +/* * +* netproto_tx_carrier_error +* @int - network interface +* + */ +void netproto_tx_carrier_errors (int interface) +{ + struct netproto_dev *device; + //return; + + if ((interface < netproto_devices) && (device = netproto_device_array[interface])) { + device->stats.tx_errors++; + device->stats.tx_carrier_errors++; + } +} + +/* * +* netproto_tx_fifo_error +* @int - network interface +* + */ +void netproto_tx_fifo_errors (int interface) +{ + struct netproto_dev *device; + //return; + + if ((interface < netproto_devices) && (device = netproto_device_array[interface])) { + device->stats.tx_errors++; + device->stats.tx_fifo_errors++; + } +} + +/* * +* netproto_tx_packets +* @int - network interface +* + */ +void netproto_tx_packets (int interface, int len) +{ + struct netproto_dev *device; + //return; + + if ((interface < netproto_devices) && (device = netproto_device_array[interface])) { + device->stats.tx_packets++; + device->stats.tx_bytes += len; + } +} + +/* * +* netproto_name +* @int - network interface +* + */ +char *netproto_name (int interface) +{ + struct netproto_dev *device; + if ((interface < netproto_devices) && (device = netproto_device_array[interface]) && (device->net_dev)) { + return device->net_dev->name; + } + return NULL; +} + +/* * +* netproto_mtu +* @int - network interface +* + */ +int netproto_mtu (int interface) +{ + struct netproto_dev *device; + if (0 <= interface && (interface < netproto_devices) && + (device = netproto_device_array[interface]) && (device->net_dev)) { + return device->net_dev->mtu; + } + return 0; +} + +/* * +* netproto_on +* @int - network interface +* + */ +void netproto_on (int interface) +{ + struct netproto_dev *device; + dbg_pinit (1, "%d", interface); + if ((interface < netproto_devices) && (device = netproto_device_array[interface]) && (device->net_dev)) { + dbg_pinit (1, "calling netif_carrier_on" ); + netif_carrier_on (device->net_dev); + netif_wake_queue (device->net_dev); + } +} + +/* * +* netproto_off +* @int - network interface +* + */ +void netproto_off (int interface) +{ + struct netproto_dev *device; + dbg_pinit (1, "%d", interface); + if ((interface < netproto_devices) && (device = netproto_device_array[interface]) && (device->net_dev)) { + netif_stop_queue (device->net_dev); + netif_carrier_off (device->net_dev); + } +} + + + +/** +* netproto_modinit - initialize netproto library +* @name: name for proc file system +* @num: number of interfaces to allow +* +* Initialize the netproto library setting the maximum number of network +* interfaces that may be created. + */ + +int netproto_modinit (char *name, int maximum_interfaces) +{ + struct proc_dir_entry *p; + struct netproto_dev **nda; // pointer to array of pointers to netproto dev structures + + dbg_pinit (1, "%s maximum_interfaces: %d", name, maximum_interfaces); + + if (!name || !strlen (name)) { + dbg_pinit (0, "name null or zero length"); + return -EINVAL; + } + // verify that netproto_devices not previously set, then allocate array and set it + + if ((nda = + kmalloc (sizeof (struct netproto_dev *) * maximum_interfaces, GFP_ATOMIC)) == NULL) { + dbg_pinit (0, "kmalloc failed"); + return -EINVAL; + } + memset (nda, 0, sizeof (struct netproto_dev *) * maximum_interfaces); + + { + unsigned long flags; + write_lock_irqsave (&netproto_rwlock, flags); + if (netproto_devices) { + write_unlock_irqrestore (&netproto_rwlock, flags); + kfree (nda); + dbg_pinit (0, "netproto_devices already set"); + return -EINVAL; + } + netproto_device_array = nda; + netproto_devices = maximum_interfaces; + write_unlock_irqrestore (&netproto_rwlock, flags); + } + + return 0; + + // create proc filesystem entry + if ((p = create_proc_entry (name, 0, NULL)) == NULL) + return -ENOMEM; + p->proc_fops = &netproto_proc_operations; + + return 0; +} + + + +/** +* netproto_create - create network interface +* @name: name of interface (use " " to get default "ethN") +* @xmit_skb: call back to transmit skb +* @set_addr: callback to set mac address +* @addr: - pointer to mac address buffer +* @addrlen: - length of address buffer +* @mtu: length of mac address buffer +* @max_queue_entries: max number of outstanding skbs allow +* @max_queue_bytes: max number of outstanding bytes allow +* @flags: +* +* Create a network interface, providing an xmit skb function and an +* optional mac address. +* +* A returned value greater or equal to zero indicates success and can be +* used with subsequent function calls to indicate the created network +* interface. + */ + +int netproto_create (char *name, + int (*xmit_skb) (int, struct sk_buff *), + int (*set_addr) (int, void *, int), + int (*tx_timeout) (int), + void *addr, + int addrlen, int mtu, int max_queue_entries, int max_queue_bytes, int flags) +{ + struct netproto_dev *device; + struct net_device *net_dev; + int i; + unsigned char *cp = addr; + + dbg_pinit (1, "name: %s", name); + dbg_pinit (1, "addr: %x:%x:%x:%x:%x:%x len: %d", + cp[0], cp[1], cp[2], cp[3], cp[4], cp[5], addrlen); + + if (!name || !strlen (name) || !xmit_skb) { + dbg_pinit (0, "invalid args"); + return -EINVAL; + } + + if ((net_dev = kmalloc (sizeof (struct net_device), GFP_ATOMIC)) == NULL) { + dbg_pinit (0, "name: %s kmalloc failed", name); + return -ENOMEM; + } + if ((device = kmalloc (sizeof (struct netproto_dev), GFP_ATOMIC)) == NULL) { + dbg_pinit (0, "name: %s kmalloc failed", name); + kfree (net_dev); + return -ENOMEM; + } + dbg_pinit (1, "kmalloc device: %p net_dev: %p", device, net_dev); + + memset (net_dev, 0, sizeof (struct net_device)); + memset (device, 0, sizeof (struct netproto_dev)); + + device->net_dev = net_dev; + device->ops_in_progress = 0; + device->cmd_queue_count = 0; + device->state = 0; + + device->xmit_skb = xmit_skb; + device->set_addr = set_addr; + device->tx_timeout = tx_timeout; + + // trim name to less then net_dev.name + if ((i = strlen (name)) >= sizeof (net_dev->name)) { + i = sizeof (net_dev->name) - 2; + } + memcpy (net_dev->name, name, i); // XXX + + ether_setup (net_dev); + + if (addrlen && addrlen <= ETH_ALEN) { + memcpy (&(net_dev->dev_addr), addr, addrlen); + device->mac_was_set = 1; + } + // set net_dev + net_dev->priv = device; + net_dev->do_ioctl = netproto_do_ioctl; + net_dev->set_config = netproto_set_config; + net_dev->set_mac_address = netproto_set_mac_addr; + net_dev->hard_start_xmit = netproto_start_xmit; + net_dev->get_stats = netproto_get_stats; + //net_dev->set_multicast = netproto_set_multicast; + net_dev->change_mtu = netproto_change_mtu; + net_dev->init = netproto_init; + net_dev->uninit = netproto_uninit; + net_dev->open = netproto_open; + net_dev->stop = netproto_stop; + + net_dev->tx_timeout = netproto_tx_timeout; + + device->mtu = mtu ? mtu : net_dev->mtu; + + device->max_queue_entries = max_queue_entries ? max_queue_entries : 1; + device->max_queue_bytes = max_queue_bytes ? max_queue_bytes : 2000; + + // get global lock, + // derive device number by seeing how many devices we already have + { + unsigned long flags; + write_lock_irqsave (&netproto_rwlock, flags); + for (i = 0; i < netproto_devices; i++) { + if (netproto_device_array[i] == NULL) { + break; + } + } + if (i == netproto_devices) { + dbg_pinit (0, "name: %s cannot find netproto_devices", name); + write_unlock_irqrestore (&netproto_rwlock, flags); + dbg_pinit (2, "kfree device: %p", device); + kfree (net_dev); ///// QQQ + kfree (device); ///// QQQ + return -ENOMEM; + } + device->interface = i; + netproto_device_array[i] = device; + write_unlock_irqrestore (&netproto_rwlock, flags); + + } + + if (!register_netdev (net_dev)) { + device->state |= NP_REGISTERED | NP_ENABLED; + dbg_pinit (1, "REGISTERED DEVICE->STATE: #%x", device->state); + } else { + dbg_pinit (0, "REGISTER_NETDEV FAILED"); + } + + // set state to inactive and request to desired value + //netif_device_detach(net_dev); + + //netif_carrier_off(net_dev); + + //netif_stop_queue(net_dev); + + return i; +} + + + +/** +* netproto_control - control network interface +* @interface: network interface +* @operation: operation +* +* Control a network interface, + */ + +int netproto_control (int interface, int operation) +{ + dbg_pmgmt (1, "interface: %d operation:%d", interface, operation); + + return 0; +} + + +/** +* netproto_destroy - destroy a network interface +* @interface: network interface +* +* Call to tear down a previously created network interface + */ + +int netproto_destroy (int interface) +{ + struct netproto_dev *device; + struct net_device *net_dev; + + dbg_pinit (1, "interface: %d", interface); + + { + unsigned long flags; + write_lock_irqsave (&netproto_rwlock, flags); + if ((interface >= netproto_devices) || !(device = netproto_device_array[interface])) { + write_lock_irqsave (&netproto_rwlock, flags); + dbg_pinit (0, "interface: %d netproto_device_array bad", interface); + return 0; + } + netproto_device_array[interface] = NULL; + write_lock_irqsave (&netproto_rwlock, flags); + + } + + net_dev = device->net_dev; + device->net_dev = NULL; + + dbg_pinit (1, "state: %x", device->state); + + device->state &= ~NP_ENABLED; + + if ((device->state & NP_REGISTERED) && net_dev) { + device->state &= ~NP_REGISTERED; + dbg_pinit (1, "unregistering: %p", net_dev); + unregister_netdev (net_dev); + kfree (net_dev); + } + + kfree (device); + + return 0; +} + + +/** +* netproto_modexit +* +* Call to unload the library. + */ + +int netproto_modexit (void) +{ + int devices; + struct netproto_dev **device_array; + + dbg_pinit (1, ""); + + if (netproto_devices) { + return 0; + } + // remove proc filesystem entry + //remove_proc_entry(netproto_procname, NULL); + netproto_procname = NULL; + + // XXX need to lock here + + devices = netproto_devices; + netproto_devices = 0; + + //for (i = 0; i < devices; i++) { + // netproto_destroy(i); + //} + + { + unsigned long flags; + write_lock_irqsave (&netproto_rwlock, flags); + device_array = netproto_device_array; + netproto_device_array = NULL; + write_lock_irqsave (&netproto_rwlock, flags); + } + kfree (netproto_device_array); + + return 0; +} diff -uNr linux.org/drivers/usb/device/net_fd/netproto.h linux/drivers/usb/device/net_fd/netproto.h --- linux.org/drivers/usb/device/net_fd/netproto.h Thu Jan 1 08:00:00 1970 +++ linux/drivers/usb/device/net_fd/netproto.h Thu May 15 15:54:48 2003 @@ -0,0 +1,516 @@ +/* + * linux/drivers/usbd/net_fd/netproto.h + * + * Copyright (c) 2000, 2001, 2002 Lineo + * Copyright (c) 2001 Hewlett Packard + * + * By: + * Stuart Lynne , + * Tom Rushworth , + * Bruce Balden + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + * + */ + +//#define NP_ENABLED 0x01 +//#define NP_REGISTERED 0x01 +extern int usb_is_configured; +extern wait_queue_head_t usb_netif_wq; + +/* netproto_dev support routines ***************************************************************** */ + + +#define MAX_NP_CMDS 16 // length of queue of commands for bh + +#define NPC_CREATE 0x01 // not presently used +#define NPC_REGISTER 0x02 +#define NPC_ENABLE 0x03 +#define NPC_DISABLE 0x04 +#define NPC_UNREGISTER 0x05 +#define NPC_CLOSE 0x07 + + /* These should really be in the bus interface, but in order to + avoid putting a whole task structure into there like the + netproto_registration_task, it's included here. */ + +#define NPC_SUSPEND 0x08 // bus inactive - host has suspended +#define NPC_RESUME 0x09 // bus active - host has resumed + + /* Values for device state */ + +#define NP_REGISTERED 0x01 // device has been registered +#define NP_ENABLED 0x02 +#define NP_DESTROYED 0x04 +#define NP_CLOSING 0x08 +#define NP_LOCKED 0x10 + + +//#define NP_ENABLE 0x10 +//#define NP_DISABLE 0x20 +//#define NP_ENABLED 0x40 + +/* per network interface device structure + */ + +struct netproto_dev { + struct net_device *net_dev; // network device information + struct net_device_stats stats; // network device statistics + + int interface; // interface number + int mac_was_set; + + + int state; + int ops_in_progress; + int cmd_queue_count; + unsigned char queued_cmds[MAX_NP_CMDS]; + + int (*xmit_skb) (int, struct sk_buff *); // use to send a network buffer + int (*set_addr) (int, void *, int); // set address + int (*tx_timeout) (int); // timeout + + int addr_set; // addr set by ioctl flag + int addr_mac; // addr provided flag + + // XXX is this sufficent should we just use + // module netproto_rwlock for everything + // lock for reading/writing + rwlock_t rwlock; + + int mtu; + + unsigned int stopped; + unsigned int restarts; + + unsigned int max_queue_entries; + unsigned int max_queue_bytes; + + unsigned int queued_entries; + unsigned int queued_bytes; + + time_t avg_queue_entries; + + time_t jiffies; + unsigned long samples; + +}; + + +extern char *netproto_procname; // name to use for proc filesystem +extern int netproto_devices; // maximum number of interfaces that can be created +extern struct netproto_dev **netproto_device_array; // pointer to array of pointers to netproto dev structures +extern rwlock_t netproto_rwlock; // lock for netproto device array access + +#include "../usbd-debug.h" + +// Some debug macros for the inlines... +extern int dbgflg_usbdfd_prx; +extern int dbgflg_usbdfd_ptx; +#ifndef dbg_prx +#define dbg_prx(lvl,fmt,args...) dbgPRINT(dbgflg_usbdfd_prx,lvl,fmt,##args) +#endif +#ifndef dbg_ptx +#define dbg_ptx(lvl,fmt,args...) dbgPRINT(dbgflg_usbdfd_ptx,lvl,fmt,##args) +#endif + +extern debug_option *netproto_get_dbg_table (void); + + +/* proc file system */ +#define NET_DEVICE_CONDITION_UNKNOWN 0 +#define NET_DEVICE_CONDITION_RESET 1 +#define NET_DEVICE_CONDITION_OK 2 +#define NET_DEVICE_CONDITION_FAIL 3 + + +/** + * netproto + * + * This is a simple linux network driver library that is suitable for + * implementing layered network drivers. I.e. networking over adsl or usb. + * + */ + + +/** + * netproto_modinit - initialize netproto library + * @int: number of interfaces to allow + * + * Initialize the netproto library setting the maximum number of network + * interfaces that may be created. + */ +int netproto_modinit (char *, int); + +/* * + * netproto_create - create network interface + * @name: name + * @int (*)(struct sk_buff *) + * @int (*)(void *,int) + * @void * - pointer to mac address buffer + * @int - length of mac address buffer + * @mtu: maximum mtu + * @max_queue_entries: + * @max_queue_bytes: + * @flags + * + * Create a network interface, providing an xmit skb function, an option + * function to set network addreses and an optional mac address. + * + * A returned value greater than zero indicates success and can be used with + * subsequent function calls to indicate the created network interface. + */ + +int netproto_create (char *, + int (*)(int, struct sk_buff *), + int (*)(int, void *, int), int (*)(int), void *, int, int, int, int, int); + +/* * + * netproto_command - + * @device + */ +//void netproto_command(int, int); + + +/* * + * netproto_enable - + * @int + * @flags + */ +void netproto_enable (int, int); + +/* * + * netproto_recv - receive a network buffer + * @int - network interface + * @struct sk_buff * - network buffer + * + * Call to pass up a network buffer. + */ + +static __inline__ int netproto_recv (int, struct sk_buff *); + + +/* * + * netproto_done - transmit done + * @int - network interface + * @struct sk_buff * - network buffer + * @int - non-zero indicates failure + * + * Call to indicate that a network buffer has been transferred. + */ + +static __inline__ int netproto_done (int, struct sk_buff *, int); + + +/* * + * netproto_control - control network interface + * @int - network interface + * @int - operation + * + * Control a network interface, + */ + +int netproto_control (int, int); + + +/* * + * netproto_destroy - destroy a network interface + * @int - network interface + * + * Call to tear down a previously created network interface + */ + +int netproto_destroy (int); + +/* * +* netproto_rx_dropped +* @int - network interface +* + */ +void netproto_rx_dropped (int); + +/* * +* netproto_rx_length_error +* @int - network interface +* + */ +void netproto_rx_length_error (int); + +/* * +* netproto_rx_over_error +* @int - network interface +* + */ +void netproto_rx_over_error (int); + +/* * +* netproto_rx_crc_error +* @int - network interface +* + */ +void netproto_rx_crc_error (int); + +/* * +* netproto_rx_frame_error +* @int - network interface +* + */ +void netproto_rx_frame_error (int); + +/* * +* netproto_rx_fifo_error +* @int - network interface +* + */ +void netproto_rx_fifo_error (int); + +/* * +* netproto_rx_missed_error +* @int - network interface +* + */ +void netproto_rx_missed_error (int); + +/* * + * netproto_rx_packets + * @int - network interface + * @len: + * + */ +void netproto_rx_packets (int, int); + +/* * + * netproto_tx_dropped + * @int - network interface + * + */ +void netproto_tx_dropped (int); + +/* * + * netproto_tx_carrier_errors + * @int - network interface + * + */ +void netproto_tx_carrier_errors (int); + +/* * + * netproto_tx_fifo_errors + * @int - network interface + * + */ +void netproto_tx_fifo_errors (int); + +/* * + * netproto_tx_packets + * @int - network interface + * @len: + * + */ +void netproto_tx_packets (int, int); + +/* * + * netproto_name + * @int - network interface + * + */ +char *netproto_name (int interface); + +/* * +* netproto_mtu +* @int - network interface +* + */ +int netproto_mtu (int interface); + +/* * + * netproto_modexit + * + * Call to unload the library. + */ + +/* * + * netproto_on + * @int - network interface + * + */ +void netproto_on (int interface); + +/* * + * netproto_off + * @int - network interface + * + */ +void netproto_off (int interface); + + +int netproto_modexit (void); + + +/** + * netproto_recv - receive a network buffer + * @interface: network interface + * @skb: pointer to network buffer + * + * Call to pass up a network buffer. + * + * Called by netproto_recv() to process a received skb and push it to the + * network layer. Return non-zero if the skb was not freed. + */ + +static __inline__ int netproto_recv (int interface, struct sk_buff *skb) +{ + struct netproto_dev *device; + struct net_device *net_dev; + int rc; + + //printk(KERN_DEBUG"netproto_recv\n"); + //dbg_prx(0, "receiving %x %d", skb, skb->len); + + if ((interface >= netproto_devices) || (device = netproto_device_array[interface]) == NULL) { + dbg_prx (0, "bad interface: %d", interface); + return -EINVAL; + } + + if (device->state != (NP_REGISTERED | NP_ENABLED)) { + dbg_prx (0, "not registered: state: %x", device->state); + return -EINVAL; + } + + if (!(net_dev = device->net_dev)) { + dbg_prx (0, "bad net_dev: %p", net_dev); + return -EINVAL; + } + // return -EINVAL; + + if (!netif_device_present (net_dev)) { + dbg_prx (0, "device not present"); + return -EINVAL; + } + + if (!netif_carrier_ok (net_dev)) { + dbg_prx (0, "no carrier"); + return -EINVAL; + } + // if the net device is down, refuse the skb + if (!(net_dev->flags & IFF_UP)) { + dbg_prx (0, "not up net_dev->flags: %x", net_dev->flags); + netproto_rx_dropped (interface); + return -EINVAL; + } + + skb->dev = net_dev; + skb->pkt_type = PACKET_HOST; + skb->protocol = eth_type_trans (skb, net_dev); + skb->ip_summed = CHECKSUM_UNNECESSARY; + + dbg_prx (5, "skb: %p head: %p data: %p tail: %p end: %p len: %d", skb, skb->head, skb->data, + skb->tail, skb->end, skb->len); + + // pass it up to kernel networking layer + if ((rc = netif_rx (skb))) { + dbg_prx (0, "netif_rx rc: %d", rc); + } + + netproto_rx_packets (interface, skb->len); + + { + extern int net_device_condition; + if (net_device_condition == NET_DEVICE_CONDITION_RESET) + net_device_condition = NET_DEVICE_CONDITION_OK; + } + + return 0; +} + +/** + * netproto_done - transmit done + * @interface: network interface + * @skb: network buffer + * @rc: non-zero indicates failure + * + * Call to indicate that a network buffer has been transferred. + * + * The network layer will be restarted. + */ + +static __inline__ int netproto_done (int interface, struct sk_buff *skb, int rc) +{ + struct netproto_dev *device; + struct net_device *net_dev; + time_t elapsed = 0; + + dbg_ptx (5, "skb: %p", skb); + + if (!skb) { + return -EINVAL; + } + + if ((interface >= netproto_devices) || (device = netproto_device_array[interface]) == NULL) { + dbg_prx (0, "bad interface: %d", interface); + return -EINVAL; + } + + if (!(net_dev = device->net_dev)) { + dbg_prx (0, "bad net_dev: %p", net_dev); + return -EINVAL; + } + + if (rc) { + netproto_tx_dropped (interface); + } + + { // lock and update stats + unsigned long flags; + write_lock_irqsave (&device->rwlock, flags); + + device->avg_queue_entries += device->queued_entries; + device->queued_entries--; + if (skb) { + device->samples++; + device->jiffies += jiffies - *(time_t *) (&skb->cb); + device->queued_bytes -= skb->len; + elapsed = jiffies - *(time_t *) (&skb->cb); + } + write_unlock_irqrestore (&device->rwlock, flags); + } + + if (skb) { + dev_kfree_skb_any (skb); + } + + if (netif_queue_stopped (net_dev)) { + unsigned long flags; + netif_wake_queue (net_dev); + write_lock_irqsave (&device->rwlock, flags); + device->restarts++; + write_unlock_irqrestore (&device->rwlock, flags); + } + return 0; +} + +/* netproto_dev_alloc_skb - allocate an skb suitable for IP with alignment + * @len: + * + * IP headers need to be aligned on 16 byte boundary. + */ +static __inline__ struct sk_buff *netproto_dev_alloc_skb (unsigned int length) +{ + struct sk_buff *skb; + if ((skb = dev_alloc_skb (length + 2))) { + skb_reserve (skb, 2); + } + dbg_prx (5, "skb: %p len:%d adding 2 for IP alignment", skb, length); + return skb; +} diff -uNr linux.org/drivers/usb/device/net_fd/rndis.c linux/drivers/usb/device/net_fd/rndis.c --- linux.org/drivers/usb/device/net_fd/rndis.c Thu Jan 1 08:00:00 1970 +++ linux/drivers/usb/device/net_fd/rndis.c Thu May 15 15:54:48 2003 @@ -0,0 +1,148 @@ +/* + * linux/drivers/usbd/net_fd/rndis.c + * + * Copyright (c) 2000, 2001, 2002 Lineo + * Copyright (c) 2001 Hewlett Packard + * + * By: + * Stuart Lynne , + * Tom Rushworth , + * Bruce Balden + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + * + */ + +#ifdef MODULE +#include +#else +#error MODULE not defined +#endif +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "usbd.h" +#include "usbd-func.h" + +//#include "rndis.h" +#include "netproto.h" + + +/* Module Parameters ************************************************************************* */ + +#define MAX_INTERFACES 2 + + +struct usb_net_private { + int interface; + struct usb_device_instance *device; + struct urb *urb; + rwlock_t rwlock; +}; + + + +#define MAX_INTERFACES 2 + +static int rndis_created; + +static struct usb_net_private *net_private_array[MAX_INTERFACES]; + +static rwlock_t netproto_rwlock = RW_LOCK_UNLOCKED; // lock for netproto device array access + + + +/* USB Configuration Description ************************************************************* */ + +#define VENDOR_SPECIFIC_CLASS 0xff +#define VENDOR_SPECIFIC_SUBCLASS 0xff +#define VENDOR_SPECIFIC_PROTOCOL 0xff + +#define MTU 1500+100 + + +/* Configuration Set - endpoints and interface(s)*/ + +static struct usb_endpoint_description rndis_endpoints[] = { + {bEndpointAddress: 1, bmAttributes: BULK, wMaxPacketSize: 64, bInterval: 1, direction:IN}, + {bEndpointAddress: 2, bmAttributes: BULK, wMaxPacketSize: 64, bInterval: 0, direction:OUT}, + {bEndpointAddress: 3, bmAttributes: INTERRUPT, wMaxPacketSize: 64, bInterval: 1, direction:IN} +}; + + + +static struct usb_class_description rndis_class_descriptions[0] = { + {USB_ST_CMF, 0, {call_management: {bmCapabilities: 0, bDataInterface:1}}}, + {USB_ST_ACMF, 0, {abstract_control: {bmCapabilities:0}}}, + {USB_ST_UF, 1, {union_function: {bMasterInterface: 0, bSlaveInterface:{1}}}} +}; + +static struct usb_interface_description rndis_interfaces[] = { + {iInterface:"Default interface", + bInterfaceClass:VENDOR_SPECIFIC_CLASS, + bInterfaceSubClass:VENDOR_SPECIFIC_SUBCLASS, + bInterfaceProtocol:VENDOR_SPECIFIC_PROTOCOL, + + classes:sizeof (rndis_class_descriptions) / + sizeof (struct usb_class_description), + class_list:rndis_class_descriptions, + + endpoints:sizeof (rndis_endpoints) / + sizeof (struct usb_endpoint_description), + endpoint_list:rndis_endpoints, + } +}; + + +/* Configuration description list */ + +struct usb_configuration_description rndis_description[] = { + {iConfiguration: "RNDIS Network Function", bmAttributes: 0, bMaxPower:0, + interfaces:sizeof (rndis_interfaces) / + sizeof (struct usb_interface_description), + interface_list:rndis_interfaces} +}; + +struct usb_device_description rndis_device_description = { + bDeviceClass:VENDOR, + bDeviceSubClass:0, + bDeviceProtocol:0, + idVendor:USB_VENDOR_COMPAQ, + idProduct:COMPAQ_ITSY_ID, + iManufacturer:"Lineo", + iProduct:"RNDIS Network Driver", + iSerialNumber:"0123456789" +}; + + +module_init (function_modinit); +module_exit (function_modexit); + +MODULE_AUTHOR ("sl@lineo.com"); +MODULE_DESCRIPTION ("USB DEVICE Network Function Driver Prototype"); diff -uNr linux.org/drivers/usb/device/serial_fd/Config.help linux/drivers/usb/device/serial_fd/Config.help --- linux.org/drivers/usb/device/serial_fd/Config.help Thu Jan 1 08:00:00 1970 +++ linux/drivers/usb/device/serial_fd/Config.help Thu May 15 15:54:48 2003 @@ -0,0 +1,68 @@ +CONFIG_USBD_SERIAL + Enable the generic serial function driver. + +CONFIG_USBD_SERIAL_VENDORID + Specify the USB Device Vendor ID for the serial function driver. The + top level Vendor ID will be used if this is not specified. + +CONFIG_USBD_SERIAL_PRODUCTID + Specify the USB Device Product ID for the serial function driver. + The top level Vendor ID will be used if this is not specified. + +CONFIG_USBD_SERIAL_CDC + Enable CDC mode. This configures the network driver to support the + ACM model specified in the USB Class Definitions. + +CONFIG_USBD_SERIAL_OUT_ENDPOINT + Specify the preferred OUT (received data) endpoint number. This is a + number from 0-15 and must be allowed by the bus interface device. + + Some devices such as the SA-1110 and Linkup L7205/L7210 will override + this selection with a fixed address. + +CONFIG_USBD_SERIAL_IN_ENDPOINT + Specify the preferred IN (transmit data) endpoint number. This is a + number from 0-15 and must be allowed by the bus interface device. + + Some devices such as the SA-1110 and Linkup L7205/L7210 will + override this selection with a fixed address. + +CONFIG_USBD_SERIAL_INT_ENDPOINT + Specify the preferred INT (interrupt) endpoint number. This is a + number from 0-15 and must be allowed by the bus interface device. + + Some devices such as the SA-1110 and Linkup L7205/L7210 will + override this selection with a fixed address. + +CONFIG_USBD_SERIAL_IN_PKTSIZE + Specify the preferred INT (interrupt) endpoint number. This is a + number from 0-15 and must be allowed by the bus interface device. + + Some devices such as the Linkup L7205/L7210 may override this value + with a lower maximum value (such as 32). + +CONFIG_USBD_SERIAL_OUT_PKTSIZE + Specify the maximum packet size for the OUT endpoint. This allowable + values are normally 16, 32 and 64. Some bus interface devices may + not support all values. + + Some devices such as the Linkup L7205/L7210 may override this value + with a lower maximum value (such as 32). + +CONFIG_USBD_SERIAL_INT_PKTSIZE + Specify the maximum packet size for the INT endpoint. This allowable + values are normally 8 and 16. Some bus interface devices may not + support all values. + + Some devices such as the L7205/L7210 may override this value with a + fixed value. Others such as the SA-1110 do not allow an interrupt + value. + +CONFIG_USBD_SERIAL_SAFE + Enable optional Safe Encapsulation layer for the serial driver. + +CONFIG_USBD_SERIAL_SAFE_DEFAULT + Set Secure option default value. + +CONFIG_USBD_SERIAL_SAFE_PADDED + Force all Secure mode packets to be padded to the full packetsize. diff -uNr linux.org/drivers/usb/device/serial_fd/Config.in linux/drivers/usb/device/serial_fd/Config.in --- linux.org/drivers/usb/device/serial_fd/Config.in Thu Jan 1 08:00:00 1970 +++ linux/drivers/usb/device/serial_fd/Config.in Thu May 15 15:54:48 2003 @@ -0,0 +1,29 @@ +# +# Generic Serial Function Driver +# +# Copyright (C) 2001 Lineo, Inc. +# Copyright (C) 2001 Hewlett-Packard Co. + +mainmenu_option next_comment +comment "Serial Function" + +dep_tristate ' Generic Serial Function' CONFIG_USBD_SERIAL $CONFIG_USBD +if [ "$CONFIG_USBD_SERIAL" = "y" -o "$CONFIG_USBD_SERIAL" = "m" ]; then + hex ' Overide VendorID (hex value)' CONFIG_USBD_SERIAL_VENDORID "0000" + hex ' Overide ProductID (hex value)' CONFIG_USBD_SERIAL_PRODUCTID "0000" + bool ' CDC Mode' CONFIG_USBD_SERIAL_CDC + int ' OUT Endpoint (0-15)' CONFIG_USBD_SERIAL_OUT_ENDPOINT "1" + int ' IN PacketSize (16, 32, 64)' CONFIG_USBD_SERIAL_IN_PKTSIZE "64" + int ' IN Endpoint (0-15)' CONFIG_USBD_SERIAL_IN_ENDPOINT "2" + int ' OUT PacketSize (16, 32, 64)' CONFIG_USBD_SERIAL_OUT_PKTSIZE "64" + if [ ! "$CONFIG_ARCH_SA1100" = "y" -a ! "$CONFIG_ARCH_L7200" = "y" ]; then + int ' INT Endpoint (0-15)' CONFIG_USBD_SERIAL_INT_ENDPOINT "3" + int ' INT PacketSize (8, 16)' CONFIG_USBD_SERIAL_INT_PKTSIZE "16" + fi + bool ' Enable Safe (Encapsulated)' CONFIG_USBD_SERIAL_SAFE + if [ "$CONFIG_USBD_SERIAL_SAFE" = "y" ]; then + bool ' Safe defaults to On' CONFIG_USBD_SERIAL_SAFE_DEFAULT + bool ' Safe Padded defaults to On' CONFIG_USBD_SERIAL_SAFE_PADDED + fi +fi +endmenu diff -uNr linux.org/drivers/usb/device/serial_fd/Makefile linux/drivers/usb/device/serial_fd/Makefile --- linux.org/drivers/usb/device/serial_fd/Makefile Thu Jan 1 08:00:00 1970 +++ linux/drivers/usb/device/serial_fd/Makefile Thu May 15 15:54:48 2003 @@ -0,0 +1,59 @@ +# +# SA1100 Function driver for a network USB Device +# +# Copyright (C) 2001 Lineo, Inc. +# Copyright (C) 2001 Hewlett-Packard Co. + +# Multipart objects. + +O_TARGET := serial_fd_drv.o +list-multi := serial_fd.o + +serial_fd-objs := serial.o serproto.o crc10.o + +# Object file lists. + +obj-y := +obj-m := +obj-n := +obj- := + +# Each configuration option enables a list of files. + +obj-$(CONFIG_USBD_SERIAL) += serial_fd.o + +# Extract lists of the multi-part drivers. +# The 'int-*' lists are the intermediate files used to build the multi's. + +multi-y := $(filter $(list-multi), $(obj-y)) +multi-m := $(filter $(list-multi), $(obj-m)) +int-y := $(sort $(foreach m, $(multi-y), $($(basename $(m))-objs))) +int-m := $(sort $(foreach m, $(multi-m), $($(basename $(m))-objs))) + +# Files that are both resident and modular: remove from modular. + +obj-m := $(filter-out $(obj-y), $(obj-m)) +int-m := $(filter-out $(int-y), $(int-m)) + +# Translate to Rules.make lists. + +O_OBJS := $(filter-out $(export-objs), $(obj-y)) +OX_OBJS := $(filter $(export-objs), $(obj-y)) +M_OBJS := $(sort $(filter-out $(export-objs), $(obj-m))) +MX_OBJS := $(sort $(filter $(export-objs), $(obj-m))) +MI_OBJS := $(sort $(filter-out $(export-objs), $(int-m))) +MIX_OBJS := $(sort $(filter $(export-objs), $(int-m))) + +# The global Rules.make. + +include $(TOPDIR)/Rules.make + +# Link rules for multi-part drivers. + +serial_fd.o: $(serial_fd-objs) + $(LD) -r -o $@ $(serial_fd-objs) + +# dependencies: + +serial.o: serproto.h ../usbd.h ../usbd-bus.h ../usbd-func.h + diff -uNr linux.org/drivers/usb/device/serial_fd/crc10.c linux/drivers/usb/device/serial_fd/crc10.c --- linux.org/drivers/usb/device/serial_fd/crc10.c Thu Jan 1 08:00:00 1970 +++ linux/drivers/usb/device/serial_fd/crc10.c Thu May 15 15:54:48 2003 @@ -0,0 +1,48 @@ +/* + * linux/drivers/usbd/net_fd/crc10.c + * + * Copyright (c) 2000, 2001, 2002 Lineo + * Copyright (c) 2001 Hewlett Packard + * + * By: + * Stuart Lynne , + * Tom Rushworth , + * Bruce Balden + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + * + * + */ + +#include + +__u16 crc10_table[256] = { + 0x000, 0x233, 0x255, 0x066, 0x299, 0x0aa, 0x0cc, 0x2ff, 0x301, 0x132, 0x154, 0x367, 0x198, 0x3ab, 0x3cd, 0x1fe, + 0x031, 0x202, 0x264, 0x057, 0x2a8, 0x09b, 0x0fd, 0x2ce, 0x330, 0x103, 0x165, 0x356, 0x1a9, 0x39a, 0x3fc, 0x1cf, + 0x062, 0x251, 0x237, 0x004, 0x2fb, 0x0c8, 0x0ae, 0x29d, 0x363, 0x150, 0x136, 0x305, 0x1fa, 0x3c9, 0x3af, 0x19c, + 0x053, 0x260, 0x206, 0x035, 0x2ca, 0x0f9, 0x09f, 0x2ac, 0x352, 0x161, 0x107, 0x334, 0x1cb, 0x3f8, 0x39e, 0x1ad, + 0x0c4, 0x2f7, 0x291, 0x0a2, 0x25d, 0x06e, 0x008, 0x23b, 0x3c5, 0x1f6, 0x190, 0x3a3, 0x15c, 0x36f, 0x309, 0x13a, + 0x0f5, 0x2c6, 0x2a0, 0x093, 0x26c, 0x05f, 0x039, 0x20a, 0x3f4, 0x1c7, 0x1a1, 0x392, 0x16d, 0x35e, 0x338, 0x10b, + 0x0a6, 0x295, 0x2f3, 0x0c0, 0x23f, 0x00c, 0x06a, 0x259, 0x3a7, 0x194, 0x1f2, 0x3c1, 0x13e, 0x30d, 0x36b, 0x158, + 0x097, 0x2a4, 0x2c2, 0x0f1, 0x20e, 0x03d, 0x05b, 0x268, 0x396, 0x1a5, 0x1c3, 0x3f0, 0x10f, 0x33c, 0x35a, 0x169, + 0x188, 0x3bb, 0x3dd, 0x1ee, 0x311, 0x122, 0x144, 0x377, 0x289, 0x0ba, 0x0dc, 0x2ef, 0x010, 0x223, 0x245, 0x076, + 0x1b9, 0x38a, 0x3ec, 0x1df, 0x320, 0x113, 0x175, 0x346, 0x2b8, 0x08b, 0x0ed, 0x2de, 0x021, 0x212, 0x274, 0x047, + 0x1ea, 0x3d9, 0x3bf, 0x18c, 0x373, 0x140, 0x126, 0x315, 0x2eb, 0x0d8, 0x0be, 0x28d, 0x072, 0x241, 0x227, 0x014, + 0x1db, 0x3e8, 0x38e, 0x1bd, 0x342, 0x171, 0x117, 0x324, 0x2da, 0x0e9, 0x08f, 0x2bc, 0x043, 0x270, 0x216, 0x025, + 0x14c, 0x37f, 0x319, 0x12a, 0x3d5, 0x1e6, 0x180, 0x3b3, 0x24d, 0x07e, 0x018, 0x22b, 0x0d4, 0x2e7, 0x281, 0x0b2, + 0x17d, 0x34e, 0x328, 0x11b, 0x3e4, 0x1d7, 0x1b1, 0x382, 0x27c, 0x04f, 0x029, 0x21a, 0x0e5, 0x2d6, 0x2b0, 0x083, + 0x12e, 0x31d, 0x37b, 0x148, 0x3b7, 0x184, 0x1e2, 0x3d1, 0x22f, 0x01c, 0x07a, 0x249, 0x0b6, 0x285, 0x2e3, 0x0d0, + 0x11f, 0x32c, 0x34a, 0x179, 0x386, 0x1b5, 0x1d3, 0x3e0, 0x21e, 0x02d, 0x04b, 0x278, 0x087, 0x2b4, 0x2d2, 0x0e1, +}; diff -uNr linux.org/drivers/usb/device/serial_fd/crc10.h linux/drivers/usb/device/serial_fd/crc10.h --- linux.org/drivers/usb/device/serial_fd/crc10.h Thu Jan 1 08:00:00 1970 +++ linux/drivers/usb/device/serial_fd/crc10.h Thu May 15 15:54:48 2003 @@ -0,0 +1,83 @@ +/* + * linux/drivers/usbd/net_fd/crc10.h + * + * Copyright (c) 2000, 2001, 2002 Lineo + * Copyright (c) 2001 Hewlett Packard + * + * By: + * Stuart Lynne , + * Tom Rushworth , + * Bruce Balden + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + * + */ + +extern __u16 crc10_table[256]; + +#define CRC10_INITFCS 0x000 // Initial FCS value +#define CRC10_GOODFCS 0x000 // Good final FCS value + +//#define CRC10_FCS(fcs, c) ((((fcs) << 8) && 0x3ff) ^ crc10_table[((fcs) >> 2) & 0xff]) ^ (c) +//#define CRC16_FCS(fcs, c) (((fcs) >> 8) ^ crc16_table[((fcs) ^ (c)) & 0xff]) + +#define CRC10_FCS(fcs, c) ( \ + (((fcs) << 8) & 0x3ff) \ + ^ crc10_table[((fcs) >> 2) & 0xff] \ + ^ (c) \ + ) + +/** + * fcs_memcpy10 - memcpy and calculate fcs + * @dp: + * @sp: + * @len: + * @fcs: + * + * Perform a memcpy and calculate fcs using ppp 10bit CRC algorithm. + */ +static __u16 __inline__ fcs_memcpy10 (unsigned char *dp, unsigned char *sp, int len, __u16 fcs) +{ + for (; len-- > 0; fcs = CRC10_FCS (fcs, *dp++ = *sp++)); + return fcs; +} + +/** + * fcs_pad10 - pad and calculate fcs + * @dp: + * @len: + * @fcs: + * + * Pad and calculate fcs using ppp 10bit CRC algorithm. + */ +static __u16 __inline__ fcs_pad10 (unsigned char *dp, int len, __u16 fcs) +{ + for (; len-- > 0; fcs = CRC10_FCS (fcs, *dp++ = '\0')); + return fcs; +} + +/** + * fcs_compute10 - memcpy and calculate fcs + * @sp: + * @len: + * @fcs: + * + * Perform a memcpy and calculate fcs using ppp 10bit CRC algorithm. + */ +static __u16 __inline__ fcs_compute10 (unsigned char *sp, int len, __u16 fcs) +{ + for (; len-- > 0; fcs = CRC10_FCS (fcs, *sp++)); + return fcs; +} diff -uNr linux.org/drivers/usb/device/serial_fd/serial.c linux/drivers/usb/device/serial_fd/serial.c --- linux.org/drivers/usb/device/serial_fd/serial.c Thu Jan 1 08:00:00 1970 +++ linux/drivers/usb/device/serial_fd/serial.c Thu May 15 15:54:48 2003 @@ -0,0 +1,1013 @@ +/* + * serial_fd/serial.c + * + * Copyright (c) 2000, 2001, 2002 Lineo + * Copyright (c) 2001 Hewlett Packard + * + * By: + * Stuart Lynne , + * Tom Rushworth , + * Bruce Balden + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + * + */ + +/* + * The encapsultaion is designed to overcome difficulties with some USB hardware. + * + * While the USB protocol has a CRC over the data while in transit, i.e. while + * being carried over the bus, there is no end to end protection. If the hardware + * has any problems getting the data into or out of the USB transmit and receive + * FIFO's then data can be lost. + * + * This protocol adds a two byte trailer to each USB packet to specify the number + * of bytes of valid data and a 10 bit CRC that will allow the receiver to verify + * that the entire USB packet was received without error. + * + * This means we now have end to end protection from the class driver to the function + * driver and back. + * + * There is an additional option that can be used to force all transmitted packets + * to be padded to the maximum packet size. This provides a work around for some + * devices which have problems with small USB packets. + * + * Assuming a packetsize of N: + * + * 0..N-2 data and optional padding + * + * N-2 bits 7-2 - number of bytes of valid data + * bits 1-0 top two bits of 10 bit CRC + * N-1 bottom 8 bits of 10 bit CRC + * + * + * | Data Length | 10 bit CRC | + * + 7 . 6 . 5 . 4 . 3 . 2 . 1 . 0 | 7 . 6 . 5 . 4 . 3 . 2 . 1 . 0 + + * + * The 10 bit CRC is computed across the sent data, followed by the trailer with + * the length set and the CRC set to zero. The CRC is then OR'd into the trailer. + * + * When received a 10 bit CRC is computed over the entire frame including the trailer + * and should be equal to zero. + * + * Two module parameters are used to control the encapsulation, if both are + * turned of the module works as a simple serial device with NO + * encapsulation. + * + * See linux/drivers/usb/serial/safe_serial.c for a host class driver + * implementation of this. + * + */ + + +#include +#include + +#include "../usbd-export.h" +#include "../usbd-build.h" +#include "../usbd-module.h" + + +MODULE_AUTHOR ("sl@lineo.com, tbr@lineo.com"); +MODULE_DESCRIPTION ("USB Device Serial Function"); + +USBD_MODULE_INFO ("serial_fd 0.1-beta"); + +#ifndef MODULE +#undef GET_USE_COUNT +#define GET_USE_COUNT(foo) 1 +#endif + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "../usbd.h" +#include "../usbd-func.h" +#include "../usbd-bus.h" +#include "../usbd-debug.h" +#include "../usbd-inline.h" +#include "../usbd-arch.h" + +#include "crc10.h" + +#include "serproto.h" + +#define MIN(a,b) (((a)<(b))?(a):(b)) +#define MAX(a,b) (((a)>(b))?(a):(b)) + +#if 0 +#define MIN(a, b) ({ \ + typeof(a) _a = (a); \ + typeof(b) _b = (b); \ + _a < _b ? _a : _b; \ +}) +#endif + + +#define MAX_INTERFACES 1 + +#define MTU 1500+100 + + +#if !defined (CONFIG_USBD_VENDORID) && !defined(CONFIG_USBD_SERIAL_VENDORID) +#error No Vendor ID +#endif +#if !defined (CONFIG_USBD_PRODUCTID) && !defined(CONFIG_USBD_SERIAL_PRODUCTID) +#error No Product ID +#endif + + +#if CONFIG_USBD_SERIAL_VENDORID +#undef CONFIG_USBD_VENDORID +#define CONFIG_USBD_VENDORID CONFIG_USBD_SERIAL_VENDORID +#endif + +#if CONFIG_USBD_SERIAL_PRODUCTID +#undef CONFIG_USBD_PRODUCTID +#define CONFIG_USBD_PRODUCTID CONFIG_USBD_SERIAL_PRODUCTID +#endif + +#ifndef CONFIG_USBD_SERIAL_NUMBER_STR +#define CONFIG_USBD_SERIAL_NUMBER_STR "" +#endif + + +#ifdef CONFIG_USBD_SELFPOWERED +#define BMATTRIBUTE BMATTRIBUTE_RESERVED | BMATTRIBUTE_SELF_POWERED +#define BMAXPOWER 0 +#else +#define BMATTRIBUTE BMATTRIBUTE_RESERVED +#define BMAXPOWER CONFIG_USBD_MAXPOWER +#endif + + +/* + * setup some default values for pktsizes and endpoint addresses. + */ + +#ifndef CONFIG_USBD_SERIAL_OUT_PKTSIZE +#define CONFIG_USBD_SERIAL_OUT_PKTSIZE 64 +#endif + +#ifndef CONFIG_USBD_SERIAL_IN_PKTSIZE +#define CONFIG_USBD_SERIAL_IN_PKTSIZE 64 +#endif + +#ifndef CONFIG_USBD_SERIAL_INT_PKTSIZE +#define CONFIG_USBD_SERIAL_INT_PKTSIZE 16 +#endif + +#ifndef CONFIG_USBD_SERIAL_OUT_ENDPOINT +#define CONFIG_USBD_SERIAL_OUT_ENDPOINT 1 +#endif + +#ifndef CONFIG_USBD_SERIAL_IN_ENDPOINT +#define CONFIG_USBD_SERIAL_IN_ENDPOINT 2 +#endif + +#ifndef CONFIG_USBD_SERIAL_INT_ENDPOINT +#define CONFIG_USBD_SERIAL_INT_ENDPOINT 3 +#endif + + +/* + * check for architecture specific endpoint configurations + */ + +#if defined(ABS_OUT_ADDR) +#warning +#warning USING ABS ENDPOINT OUT ADDRESS +#undef CONFIG_USBD_SERIAL_OUT_ENDPOINT + +#if ABS_OUT_ADDR > 0 +#define CONFIG_USBD_SERIAL_OUT_ENDPOINT ABS_OUT_ADDR +#endif + +#elif defined(MAX_OUT_ADDR) && defined(CONFIG_USBD_SERIAL_OUT_ENDPOINT) && (CONFIG_USBD_SERIAL_OUT_ENDPOINT > MAX_OUT_ADDR) +#warning +#warning USING DEFAULT ENDPOINT OUT ADDRESS +#undef CONFIG_USBD_SERIAL_OUT_ENDPOINT +#define CONFIG_USBD_SERIAL_OUT_ENDPOINT DFL_OUT_ADDR +#endif + + +#if defined(ABS_IN_ADDR) +#warning +#warning USING ABS ENDPOINT IN ADDRESS +#undef CONFIG_USBD_SERIAL_IN_ENDPOINT + +#if ABS_IN_ADDR +#define CONFIG_USBD_SERIAL_IN_ENDPOINT ABS_IN_ADDR +#endif + +#elif defined(MAX_IN_ADDR) && defined(CONFIG_USBD_SERIAL_IN_ENDPOINT) && (CONFIG_USBD_SERIAL_IN_ENDPOINT > MAX_IN_ADDR) +#warning +#warning USING DEFAULT ENDPOINT IN ADDRESS +#undef CONFIG_USBD_SERIAL_IN_ENDPOINT +#define CONFIG_USBD_SERIAL_IN_ENDPOINT DFL_IN_ADDR +#endif + + +#if defined(ABS_INT_ADDR) +#warning +#warning USING ABS ENDPOINT INT ADDRESS +#undef CONFIG_USBD_SERIAL_INT_ENDPOINT + +#if ABS_INT_ADDR +#define CONFIG_USBD_SERIAL_INT_ENDPOINT ABS_INT_ADDR +#endif + +#elif defined(MAX_INT_ADDR) && defined(CONFIG_USBD_SERIAL_INT_ENDPOINT) && (CONFIG_USBD_SERIAL_INT_ENDPOINT > MAX_INT_ADDR) +#warning +#warning USING DEFAULT ENDPOINT INT ADDRESS +#undef CONFIG_USBD_SERIAL_INT_ENDPOINT +#define CONFIG_USBD_SERIAL_INT_ENDPOINT DFL_INT_ADDR +#endif + + + +#if defined(MAX_OUT_PKTSIZE) && defined(CONFIG_USBD_SERIAL_OUT_PKTSIZE) && CONFIG_USBD_SERIAL_OUT_PKTSIZE > MAX_OUT_PKTSIZE +#warning +#warning OVERIDING ENDPOINT OUT PKTSIZE +#undef CONFIG_USBD_SERIAL_OUT_PKTSIZE +#define CONFIG_USBD_SERIAL_OUT_PKTSIZE MAX_OUT_PKTSIZE +#endif + +#if defined(MAX_IN_PKTSIZE) && defined(CONFIG_USBD_SERIAL_IN_PKTSIZE) && CONFIG_USBD_SERIAL_IN_PKTSIZE > MAX_IN_PKTSIZE +#warning +#warning OVERIDING ENDPOINT IN PKTSIZE +#undef CONFIG_USBD_SERIAL_IN_PKTSIZE +#define CONFIG_USBD_SERIAL_IN_PKTSIZE MAX_IN_PKTSIZE +#endif + +#if defined(MAX_INT_PKTSIZE) && defined(CONFIG_USBD_SERIAL_INT_PKTSIZE) && CONFIG_USBD_SERIAL_INT_PKTSIZE > MAX_INT_PKTSIZE +#warning +#warning OVERIDING ENDPOINT INT PKTSIZE +#undef CONFIG_USBD_SERIAL_INT_PKTSIZE +#define CONFIG_USBD_SERIAL_INT_PKTSIZE MAX_INT_PKTSIZE +#endif + + + + +struct usb_serial_private { + int interface; + struct usb_device_instance *device; + rwlock_t rwlock; +}; + +/* Module Parameters ************************************************************************* */ + +static char *dbg = NULL; +static u32 vendor_id; +static u32 product_id; +static u32 txqueue_urbs; +static u32 txqueue_bytes = 3032; + +#ifndef CONFIG_USBD_SERIAL_SAFE_DEFAULT +#define CONFIG_USBD_SERIAL_SAFE_DEFAULT 0 +#endif +#ifndef CONFIG_USBD_SERIAL_SAFE_PADDED +#define CONFIG_USBD_SERIAL_SAFE_PADDED 0 +#endif + +static int safe = CONFIG_USBD_SERIAL_SAFE_DEFAULT; +static int padded = CONFIG_USBD_SERIAL_SAFE_PADDED; + +MODULE_PARM (dbg, "s"); +MODULE_PARM (vendor_id, "i"); +MODULE_PARM (product_id, "i"); +MODULE_PARM (txqueue_urbs, "i"); +MODULE_PARM (txqueue_bytes, "i"); +MODULE_PARM (safe, "i"); +MODULE_PARM (padded, "i"); + +MODULE_PARM_DESC (dbg, "USB Device Debug options"); +MODULE_PARM_DESC (vendor_id, "USB Device Vendor ID"); +MODULE_PARM_DESC (product_id, "USB Device Product ID"); +MODULE_PARM_DESC (txqueue_urbs, "Maximum TX Queue Urbs"); +MODULE_PARM_DESC (txqueue_bytes, "Maximum TX Queue Bytes"); +MODULE_PARM_DESC (safe, "Safe Encapsulation"); +MODULE_PARM_DESC (padded, "Safe Encapsulation Padding"); + + + +/* Debug switches (module parameter "dbg=...") *********************************************** */ + +extern int dbgflg_usbdfd_init; +int dbgflg_usbdfd_usbe; +int dbgflg_usbdfd_tx; +int dbgflg_usbdfd_rx; +int dbgflg_usbdfd_loopback; + +static debug_option dbg_table[] = { + {&dbgflg_usbdfd_init, NULL, "init", "initialization and termination"}, + {&dbgflg_usbdfd_usbe, NULL, "usbe", "USB events"}, + {&dbgflg_usbdfd_rx, NULL, "rx", "receive (from host)"}, + {&dbgflg_usbdfd_tx, NULL, "tx", "transmit (to host)"}, + {&dbgflg_usbdfd_loopback, NULL, "loop", "loopback mode if non-zero"}, + {NULL, NULL, "ser", "serial device (tty) handling"}, + {NULL, NULL, NULL, NULL} +}; + +#define dbg_init(lvl,fmt,args...) dbgPRINT(dbgflg_usbdfd_init,lvl,fmt,##args) +#define dbg_usbe(lvl,fmt,args...) dbgPRINT(dbgflg_usbdfd_usbe,lvl,fmt,##args) +#define dbg_rx(lvl,fmt,args...) dbgPRINT(dbgflg_usbdfd_rx,lvl,fmt,##args) +#define dbg_tx(lvl,fmt,args...) dbgPRINT(dbgflg_usbdfd_tx,lvl,fmt,##args) +#define dbg_loop(lvl,fmt,args...) dbgPRINT(dbgflg_usbdfd_loopback,lvl,fmt,##args) + +/* ******************************************************************************************* */ + +static int serial_created; + +static struct usb_serial_private *serial_private_array[MAX_INTERFACES]; + +static rwlock_t serial_rwlock = RW_LOCK_UNLOCKED; // lock for serproto device array access + +static __inline__ struct usb_serial_private *get_serial_private (int interface) +{ + if (interface < 0 || interface >= MAX_INTERFACES) { + return NULL; + } + return serial_private_array[interface]; +} + + +/* usb-func.c ******************************************************************************** */ + + +/* Communications Interface Class descriptions + */ + +static struct usb_endpoint_description serial_default[] = { + {bEndpointAddress:CONFIG_USBD_SERIAL_OUT_ENDPOINT, + bmAttributes:BULK, + wMaxPacketSize:CONFIG_USBD_SERIAL_OUT_PKTSIZE, + bInterval:0, + direction:OUT, + transferSize:CONFIG_USBD_SERIAL_OUT_PKTSIZE,}, + + {bEndpointAddress:CONFIG_USBD_SERIAL_IN_ENDPOINT, + bmAttributes:BULK, + wMaxPacketSize:CONFIG_USBD_SERIAL_IN_PKTSIZE, + bInterval:0, + direction:IN, + transferSize:CONFIG_USBD_SERIAL_IN_PKTSIZE,}, + +#if defined(CONFIG_USBD_SERIAL_INT_ENDPOINT) && (CONFIG_USBD_SERIAL_INT_ENDPOINT > 0) + {bEndpointAddress:CONFIG_USBD_SERIAL_INT_ENDPOINT, + bmAttributes:INTERRUPT, + wMaxPacketSize:CONFIG_USBD_SERIAL_INT_PKTSIZE, + bInterval:0, + direction:IN, + transferSize:CONFIG_USBD_SERIAL_INT_PKTSIZE,}, +#endif + +}; + + +/* Data Interface Alternate description(s) + */ +static __initdata struct usb_alternate_description serial_data_alternate_descriptions[] = { + {iInterface:"Simple Serial Data Interface - Bulk mode", + bAlternateSetting:0, + endpoints:sizeof (serial_default) / + sizeof (struct usb_endpoint_description), + endpoint_list:serial_default,}, +}; + +/* Interface description(s) + */ +static __initdata struct usb_interface_description serial_interfaces[] = { + {iInterface:"Simple Serial Data Interface", + bInterfaceClass:LINEO_CLASS, + bInterfaceSubClass:LINEO_SUBCLASS_SAFESERIAL, + bInterfaceProtocol:LINEO_SAFESERIAL_CRC, + alternates:sizeof (serial_data_alternate_descriptions) / + sizeof (struct usb_alternate_description), + alternate_list:serial_data_alternate_descriptions,}, +}; + + +#ifdef CONFIG_USBD_SERIAL_CDC +/* + * CDC ACM Configuration + */ + + +/* Communication Interface Class descriptions + */ +static struct usb_class_description cdc_comm_class_descriptions[] = { + {USB_ST_HEADER, 0, {header: {bcdCDC:CLASS_BCD_VERSION,}}}, + {USB_ST_UF, 1, {union_function: {bMasterInterface: 0, bSlaveInterface:{1},}}}, + {USB_ST_CMF, 0, {call_management: {bmCapabilities: 0, bDataInterface:1,}}}, + {USB_ST_ACMF, 0, {abstract_control: {bmCapabilities:0,}}}, +}; + + +/* Data Interface Alternate 1 endpoints + */ +static __initdata struct usb_endpoint_description serial_alt_1_endpoints[] = { + {bEndpointAddress:CONFIG_USBD_SERIAL_OUT_ENDPOINT, + bmAttributes:BULK, + wMaxPacketSize:CONFIG_USBD_SERIAL_OUT_PKTSIZE, + bInterval:0, + direction:OUT, + transferSize:CONFIG_USBD_SERIAL_OUT_PKTSIZE,}, + + {bEndpointAddress:CONFIG_USBD_SERIAL_IN_ENDPOINT, + bmAttributes:BULK, + wMaxPacketSize:CONFIG_USBD_SERIAL_IN_PKTSIZE, + bInterval:0, + direction:IN, + transferSize:CONFIG_USBD_SERIAL_OUT_PKTSIZE,}, + +#if defined(CONFIG_USBD_SERIAL_INT_ENDPOINT) && (CONFIG_USBD_SERIAL_INT_ENDPOINT > 0) + {bEndpointAddress:CONFIG_USBD_SERIAL_INT_ENDPOINT, + bmAttributes:INTERRUPT, + wMaxPacketSize:CONFIG_USBD_SERIAL_INT_PKTSIZE, + bInterval:0, + direction:IN, + transferSize:CONFIG_USBD_SERIAL_INT_PKTSIZE,}, +#endif +}; + + +/* Data Interface Alternate description(s) + */ +static __initdata struct usb_alternate_description cdc_comm_alternate_descriptions[] = { + {iInterface:"CDC ACM Comm Interface", + bAlternateSetting:0, + classes:sizeof (cdc_comm_class_descriptions) / + sizeof (struct usb_class_description), + class_list:cdc_comm_class_descriptions,}, +}; + +static __initdata struct usb_alternate_description cdc_data_alternate_descriptions[] = { + {iInterface:"CDC ACM Data Interface - Disabled mode", + bAlternateSetting:0,}, + + {iInterface:"CDC ACM Data Interface - Bulk mode", + bAlternateSetting:1, + endpoints:sizeof (serial_alt_1_endpoints) / + sizeof (struct usb_endpoint_description), + endpoint_list:serial_alt_1_endpoints,}, +}; + + +/* Interface description(s) + */ +static __initdata struct usb_interface_description cdc_interfaces[] = { + {iInterface:"CDC ACM Communication Interface", + bInterfaceClass:COMMUNICATIONS_INTERFACE_CLASS, + bInterfaceSubClass:COMMUNICATIONS_ACM_SUBCLASS, + bInterfaceProtocol:COMMUNICATIONS_NO_PROTOCOL, + alternates:sizeof (cdc_comm_alternate_descriptions) / + sizeof (struct usb_alternate_description), + alternate_list:cdc_comm_alternate_descriptions,}, + + {iInterface:"CDC ACM Data Interface", + bInterfaceClass:DATA_INTERFACE_CLASS, + bInterfaceSubClass:COMMUNICATIONS_NO_SUBCLASS, + bInterfaceProtocol:COMMUNICATIONS_NO_PROTOCOL, + alternates:sizeof (cdc_data_alternate_descriptions) / + sizeof (struct usb_alternate_description), + alternate_list:cdc_data_alternate_descriptions,}, +}; + +#endif /* CONFIG_USBD_SERIAL_CDC */ + +/* Configuration description(s) + */ +struct __initdata usb_configuration_description serial_description[] = { +#ifdef CONFIG_USBD_SERIAL_CDC + {iConfiguration:"CDC 1.1 ACM Configuration", + bmAttributes:BMATTRIBUTE, + bMaxPower:BMAXPOWER, + interfaces:sizeof (cdc_interfaces) / + sizeof (struct usb_interface_description), + interface_list:cdc_interfaces,}, +#endif + + {iConfiguration:"USB Simple Serial Configuration", + bmAttributes:BMATTRIBUTE, + bMaxPower:BMAXPOWER, + interfaces:sizeof (serial_interfaces) / + sizeof (struct usb_interface_description), + interface_list:serial_interfaces,}, +}; + +/* Device Description + */ +struct __initdata usb_device_description serial_device_description = { + bDeviceClass:COMMUNICATIONS_DEVICE_CLASS, + bDeviceSubClass:0, // XXX + bDeviceProtocol:0, // XXX + idVendor:CONFIG_USBD_VENDORID, + idProduct:CONFIG_USBD_PRODUCTID, + iManufacturer:CONFIG_USBD_MANUFACTURER, + iProduct:CONFIG_USBD_PRODUCT_NAME, + iSerialNumber:CONFIG_USBD_SERIAL_NUMBER_STR, +}; + + + +/* Transmit Function - called by serproto ****************************************************** */ + +static int serial_xmit_data (int interface, unsigned char *data, int data_length) +{ + int port = 0; // XXX compound device + struct usb_serial_private *serial_private; + struct usb_device_instance *device; + struct urb *urb; + int packet_length = data_length; + + dbg_tx (2, "data: %p data_length: %d", data, data_length); + + if ((serial_private = get_serial_private (interface)) == NULL) { + dbg_tx (0, "cannot recover serial private"); + return -EINVAL; + } + + if ((device = serial_private->device) == NULL) { + dbg_tx (0, "cannot recover serial private device"); + return -EINVAL; + } + // XXX Check if we are busy + + if ((urb = usbd_alloc_urb (serial_private->device, + (serial_private->device->function_instance_array + port), + CONFIG_USBD_SERIAL_IN_ENDPOINT, 0)) == NULL) { + dbg_tx (0, "failed to alloc urb"); + return -EINVAL; + } +#ifdef CONFIG_USBD_SERIAL_SAFE + if (safe) { + __u16 fcs; + + dbg_tx (1, "safe mode: padded: %d data_length: %d packet_length: %d", padded, + data_length, packet_length); + + // extend length to pad if required + if (padded) { + //packet_length = MAX(packet_length, CONFIG_USBD_SERIAL_IN_PKTSIZE - 2); + //packet_length = MIN(MAX(packet_length, packet_length), CONFIG_USBD_SERIAL_IN_PKTSIZE - 2); + + packet_length = CONFIG_USBD_SERIAL_IN_PKTSIZE - 2; + //packet_length = 2; + //packet_length++; + } + // set length and a null byte + data[packet_length] = data_length << 2; + data[packet_length + 1] = 0; + + // compute CRC across data, padding, data_length and null byte + fcs = fcs_compute10 (data, packet_length + 2, CRC10_INITFCS); + + // OR CRC into last two bytes + data[packet_length] |= fcs >> 8; + data[packet_length + 1] = fcs & 0xff; + packet_length += 2; + dbg_tx (1, "safe mode: data_length: %d packet_length: %d", data_length, + packet_length); + } +#endif + + urb->buffer = data; + urb->actual_length = packet_length; + + + //dbgPRINTmem(dbgflg_usbdfd_tx,3,urb->buffer,urb->actual_length); + dbgPRINTmem (dbgflg_usbdfd_tx, 3, urb->buffer, CONFIG_USBD_SERIAL_IN_PKTSIZE + 4); + + // push it down into the usb-device layer + return usbd_send_urb (urb); +} + +/* serial_urb_sent - called to indicate URB transmit finished + * @urb: pointer to struct urb + * @rc: result + */ +int serial_urb_sent (struct urb *urb) +{ + int port = 0; // XXX compound device + struct usb_device_instance *device = urb->device; + struct usb_serial_private *serial_private = + (device->function_instance_array + port)->privdata; + + dbg_tx (2, "%s safe: %d length: %d", urb->device->name, safe, urb->actual_length); + serproto_done (serial_private->interface, + urb->buffer, + safe ? (urb->buffer[urb->actual_length - 2] >> 2) : urb->actual_length, 0); + usbd_dealloc_urb (urb); + return 0; +} + + +/* USB Device Functions ************************************************************************ */ + +/* serial_event - process a device event + * + */ +void serial_event (struct usb_device_instance *device, usb_device_event_t event, int data) +{ + int port = 0; // XXX compound device + struct usb_function_instance *function; + unsigned int flags; + + if ((function = device->function_instance_array + port) == NULL) { + dbg_usbe (1, "no function"); + return; + } + + dbg_usbe (3, ""); + switch (event) { + case DEVICE_UNKNOWN: + case DEVICE_INIT: + case DEVICE_CREATE: + case DEVICE_HUB_CONFIGURED: + case DEVICE_RESET: + case DEVICE_ADDRESS_ASSIGNED: + case DEVICE_CONFIGURED: + case DEVICE_DE_CONFIGURED: + case DEVICE_SET_INTERFACE: + case DEVICE_SET_FEATURE: + case DEVICE_CLEAR_FEATURE: + case DEVICE_BUS_INACTIVE: + case DEVICE_BUS_ACTIVITY: + case DEVICE_POWER_INTERRUPTION: + case DEVICE_HUB_RESET: + case DEVICE_DESTROY: + case DEVICE_FUNCTION_PRIVATE: + dbg_usbe (1,"%s data: %d", usbd_device_events[event], data); + break; + default: + dbg_usbe (1,"%s", usbd_device_events[DEVICE_UNKNOWN]); + break; + } + switch (event) { + + case DEVICE_UNKNOWN: + case DEVICE_INIT: + break; + + case DEVICE_CREATE: + { + int i; + int interface; + struct usb_serial_private *serial_private; + + // There is no way to indicate error, so make this unconditional + // and undo it in the DESTROY event unconditionally as well. + // It the responsibility of the USBD core and the bus interface + // to see that there is a matching DESTROY for every CREATE. + // XXX XXX MOD_INC_USE_COUNT; // Before any sleepable fns such as kmalloc + // XXX XXX dbg_init(0,"CREATE sc=%d uc=%d",serial_created,GET_USE_COUNT(THIS_MODULE)); + + // sanity checks + if (serial_created >= MAX_INTERFACES) { + dbg_usbe (1, + "---> CREATE %s serial_created >= MAX_INTERFACES %d %d", + device->name, serial_created, MAX_INTERFACES); + dbg_init (0, "CREATE Z1 sc=%d uc=%d", serial_created, + GET_USE_COUNT (THIS_MODULE)); + return; + } + + write_lock_irqsave (&serial_rwlock, flags); + for (i = 0; i < MAX_INTERFACES; i++) { + if (serial_private_array[i] == NULL) { + break; + } + } + if (i >= MAX_INTERFACES) { + write_unlock_irqrestore (&serial_rwlock, flags); + dbg_usbe (1, "---> CREATE %s no free interfaces %d %d", + device->name, i, MAX_INTERFACES); + dbg_init (0, "CREATE Z2 sc=%d uc=%d", serial_created, + GET_USE_COUNT (THIS_MODULE)); + return; + } + serial_created++; + + // allocate private data + if ((serial_private = + kmalloc (sizeof (struct usb_serial_private), GFP_ATOMIC)) == NULL) { + serial_created--; + //MOD_DEC_USE_COUNT; + //dbg_init(0,"CREATE X1 sc=%d uc=%d",serial_created,GET_USE_COUNT(THIS_MODULE)); + write_unlock_irqrestore (&serial_rwlock, flags); + dbg_usbe (1, "---> CREATE malloc failed %s", device->name); + return; + } + + serial_private->rwlock = RW_LOCK_UNLOCKED; + serial_private_array[i] = serial_private; + write_unlock_irqrestore (&serial_rwlock, flags); + + dbg_usbe (1, "---> calling serproto_create(%s,-,%u,%u,%u,%u,%u)", "ttyUSBx", + CONFIG_USBD_SERIAL_IN_PKTSIZE, txqueue_urbs, txqueue_bytes, safe, + safe ? 2 : 0); + + if ((interface = serproto_create ("ttyUSBx", serial_xmit_data, + (safe + ? (CONFIG_USBD_SERIAL_IN_PKTSIZE - + 2) : CONFIG_USBD_SERIAL_IN_PKTSIZE), + txqueue_urbs, txqueue_bytes, safe, + safe ? 2 : 0)) < 0) { + // lock and modify device array + write_lock_irqsave (&serial_rwlock, flags); + kfree (serial_private); + serial_created--; + //MOD_DEC_USE_COUNT; + //dbg_init(0,"CREATE X2 sc=%d uc=%d",serial_created,GET_USE_COUNT(THIS_MODULE)); + write_unlock_irqrestore (&serial_rwlock, flags); + dbg_usbe (1, "---> serproto_create FAILED"); + return; + } + // lock and modify device array + write_lock_irqsave (&serial_rwlock, flags); + serial_private->interface = interface; + serial_private->device = device; + write_unlock_irqrestore (&serial_rwlock, flags); + + function->privdata = serial_private; + + dbg_usbe (1, "---> START %s privdata assigned: %p interface: %d", + device->name, serial_private, interface); + + return; + } + break; + + case DEVICE_HUB_CONFIGURED: + break; + case DEVICE_RESET: + break; + case DEVICE_ADDRESS_ASSIGNED: + break; + case DEVICE_CONFIGURED: + { + struct usb_serial_private *serial_private; + int interface; + if ((serial_private = + (device->function_instance_array + port)->privdata) == NULL) { + return; + } + interface = serial_private->interface; + if (interface < 0 || interface >= MAX_INTERFACES) { + return; + } + serproto_control (interface, SERPROTO_CONNECT); + } + break; + case DEVICE_SET_INTERFACE: +#ifdef CONFIG_USBD_SERIAL_CDC +#endif + break; + case DEVICE_SET_FEATURE: + break; + case DEVICE_CLEAR_FEATURE: + break; + case DEVICE_DE_CONFIGURED: + { + struct usb_serial_private *serial_private; + int interface; + if ((serial_private = + (device->function_instance_array + port)->privdata) == NULL) { + return; + } + interface = serial_private->interface; + if (interface < 0 || interface >= MAX_INTERFACES) { + return; + } + serproto_control (interface, SERPROTO_DISCONNECT); + } + break; + case DEVICE_BUS_INACTIVE: + break; + case DEVICE_BUS_ACTIVITY: + break; + case DEVICE_POWER_INTERRUPTION: + break; + case DEVICE_HUB_RESET: + break; + + case DEVICE_DESTROY: + { + struct usb_serial_private *serial_private; + int interface; + + if ((serial_private = + (device->function_instance_array + port)->privdata) == NULL) { + dbg_usbe (1, "---> DESTROY %s serial_private null", device->name); + // XXX XXX MOD_DEC_USE_COUNT; + // XXX XXX dbg_init(0,"DESTROY Z1 sc=%d uc=%d",serial_created,GET_USE_COUNT(THIS_MODULE)); + return; + } + dbg_usbe (1, "---> DESTROY %s serial_private %p", device->name, + serial_private); + interface = serial_private->interface; + + if (interface < 0 || interface >= MAX_INTERFACES) { + // XXX XXX MOD_DEC_USE_COUNT; + // XXX XXX dbg_init(0,"DESTROY Z2 sc=%d uc=%d",serial_created,GET_USE_COUNT(THIS_MODULE)); + return; + } + + if (serial_private_array[interface] != serial_private) { + // XXX XXX MOD_DEC_USE_COUNT; + // XXX XXX dbg_init(0,"DESTROY Z3 sc=%d uc=%d",serial_created,GET_USE_COUNT(THIS_MODULE)); + return; + } + + write_lock_irqsave (&serial_rwlock, flags); + serial_private_array[interface] = NULL; + kfree (serial_private); + serial_created--; + write_unlock_irqrestore (&serial_rwlock, flags); + + serproto_destroy (interface); + // XXX XXX MOD_DEC_USE_COUNT; + // XXX XXX dbg_init(0,"DESTROY sc=%d uc=%d",serial_created,GET_USE_COUNT(THIS_MODULE)); + + dbg_usbe (1, "---> STOP %s", device->name); + return; + } + break; + + case DEVICE_FUNCTION_PRIVATE: + break; + } +} + + + +/* serial_recv_urb - called to indicate URB has been received + * @urb - pointer to struct urb + * + * Return non-zero if we failed and urb is still valid (not disposed) + */ +int serial_recv_urb (struct urb *urb) +{ + int port = 0; // XXX compound device + struct usb_device_instance *device = urb->device; + struct usb_serial_private *serial_private = + (device->function_instance_array + port)->privdata; + int interface = serial_private->interface; + + dbg_rx (2, "length=%d", urb->actual_length); + dbgPRINTmem (dbgflg_usbdfd_rx, 3, urb->buffer, urb->actual_length); + +#ifdef CONFIG_USBD_SERIAL_SAFE + if (safe) { + __u16 fcs; + + if ((fcs = fcs_compute10 (urb->buffer, urb->actual_length, CRC10_INITFCS))) { + dbg_rx (0, "CRC check failed"); + //return -EINVAL; + } else { + // recover the length from the trailer + urb->actual_length = (urb->buffer[urb->actual_length - 2] >> 2); + } + } + dbg_rx (2, "revised length=%d", urb->actual_length); +#endif +#if 0 + if (0 != dbgflg_usbdfd_loopback) { + if (usbd_send_urb (urb)) { + //XXX verify not freed in usbd_send_urb() + //urb->buffer = NULL; + //urb->actual_length = 0; + //usbd_dealloc_urb(urb); + } + return 0; + } +#endif + + // push the data up + if (serproto_recv (interface, urb->buffer, urb->actual_length)) { + return (1); + } + // free urb + usbd_recycle_urb (urb); + + return (0); +} + + +struct usb_function_operations function_ops = { + event:serial_event, + recv_urb:serial_recv_urb, + urb_sent:serial_urb_sent +}; + +struct usb_function_driver function_driver = { + name:"function prototype", + ops:&function_ops, + device_description:&serial_device_description, + configurations:sizeof (serial_description) / sizeof (struct usb_configuration_description), + configuration_description:serial_description, + this_module:THIS_MODULE, +}; + + +/* + * serial_modinit - module init + * + */ +static int __init serial_modinit (void) +{ + debug_option *op = find_debug_option (dbg_table, "ser"); + + printk (KERN_INFO "%s (%s)\n", __usbd_module_info, dbg); + +#ifdef CONFIG_USBD_SERIAL_SAFE + printk (KERN_INFO "vendor_id: %04x product_id: %04x safe: %d padded: %d\n", vendor_id, + product_id, safe, padded); +#else + printk (KERN_INFO "vendor_id: %04x product_id: %04x\n", vendor_id, product_id); + if (safe || padded) { + printk (KERN_ERR "serial_fd: not compiled for safe mode\n"); + return -EINVAL; + } +#endif + printk (KERN_INFO "dbg: %s\n", dbg); + + if (NULL != op) { + op->sub_table = serproto_get_dbg_table (); + } + if (0 != scan_debug_options ("serial_fd", dbg_table, dbg)) { + return (-EINVAL); + } + + if (vendor_id) { + serial_device_description.idVendor = vendor_id; + } + if (product_id) { + serial_device_description.idProduct = product_id; + } + // Initialize the function registration code. + // + //if (usbd_strings_init()) { + // return -EINVAL; + //} + // initialize the serproto library + // + if (serproto_modinit ("ttyUSBn", MAX_INTERFACES)) { + return -EINVAL; + } + // register us with the usb device support layer + // + if (usbd_register_function (&function_driver)) { + serproto_modexit (); + return -EINVAL; + } + dbg_loop (1, "LOOPBACK mode"); + + // return + return 0; +} + + +/* serial_modexit - module cleanup + */ +static void __exit serial_modexit (void) +{ + // de-register us with the usb device support layer + // + usbd_deregister_function (&function_driver); + + // tell the serproto library to exit + // + serproto_modexit (); + +} + +module_init (serial_modinit); +module_exit (serial_modexit); diff -uNr linux.org/drivers/usb/device/serial_fd/serproto.c linux/drivers/usb/device/serial_fd/serproto.c --- linux.org/drivers/usb/device/serial_fd/serproto.c Thu Jan 1 08:00:00 1970 +++ linux/drivers/usb/device/serial_fd/serproto.c Thu May 15 15:54:48 2003 @@ -0,0 +1,812 @@ +/* + * serial_fd/serproto.h + * + * Copyright (c) 2000, 2001, 2002 Lineo + * Copyright (c) 2001 Hewlett Packard + * + * By: + * Stuart Lynne , + * Tom Rushworth , + * Bruce Balden + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + * + */ + + +#include +#include +#ifndef MODULE +#undef GET_USE_COUNT +#define GET_USE_COUNT(foo) 1 +#endif + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + + +#include "serproto.h" + + +//#define SERIAL_TTY_MAJOR 222 +#define SERIAL_TTY_MAJOR 188 // re-use USB host tty dev number +#define SERIAL_TTY_MINORS 1 + +static int serial_refcount; +static struct tty_struct *serial_tty[SERIAL_TTY_MINORS]; +static struct termios *serial_termios[SERIAL_TTY_MINORS]; +static struct termios *serial_termios_locked[SERIAL_TTY_MINORS]; +extern int usb; + + +#define MIN(a,b) ((a>b) ? b : a) +#ifndef TRUE +#define TRUE 1 +#endif +#ifndef FALSE +#define FALSE 0 +#endif + +struct serproto_dev { + int number; // serial device number (index in serproto_device_array) + int opencnt; // number of opens + int connected; // TRUE if USB has connected. + unsigned int clocal; + + struct tq_struct write_wakeup_task; // task queue for line discipline waking up + + struct tty_struct *tty; // serial tty structure + + struct tty_driver tty_driver; + + int (*xmit_data) (int, unsigned char *, int); // callback to send data + + int tx_size; // maximum transmit size + + rwlock_t rwlock; // lock changing this structure + //static DECLARE_MUTEX_LOCKED(busy);// semaphore to wait if busy + + unsigned int max_queue_entries; // maximum queue entries + unsigned int max_queue_bytes; // maximum queued data + + unsigned int queued_entries; // current queued entries + unsigned int queued_bytes; // current queued data + + int blocked; + int trailer; + +}; + +int serproto_devices; // maximum number of interaces + +static struct serproto_dev **serproto_device_array; // pointer to active interaces + +static rwlock_t serproto_rwlock = RW_LOCK_UNLOCKED; // lock for changing global structures + +/* Debug switches ****************************************************************************** */ + +static int dbgflg_init = 0; +static int dbgflg_oc = 0; +static int dbgflg_rx = 0; +static int dbgflg_tx = 0; +static int dbgflg_mgmt = 0; +static int dbgflg_loopback = 0; + +static debug_option dbg_table[] = { + {&dbgflg_init, NULL, "init", "initialization/termination handling"}, + {&dbgflg_oc, NULL, "opcl", "open/close handling"}, + {&dbgflg_rx, NULL, "rx", "receive (from host)"}, + {&dbgflg_tx, NULL, "tx", "transmit (to host)"}, + {&dbgflg_mgmt, NULL, "mgmt", "ioctl, termios, etc."}, + {&dbgflg_loopback, NULL, "loop", "enable loopback if non-zero"}, + {NULL, NULL, NULL, NULL} +}; + +#define dbg_init(lvl,fmt,args...) dbgPRINT(dbgflg_init,lvl,fmt,##args) +#define dbg_oc(lvl,fmt,args...) dbgPRINT(dbgflg_oc,lvl,fmt,##args) +#define dbg_rx(lvl,fmt,args...) dbgPRINT(dbgflg_rx,lvl,fmt,##args) +#define dbg_tx(lvl,fmt,args...) dbgPRINT(dbgflg_tx,lvl,fmt,##args) +#define dbg_mgmt(lvl,fmt,args...) dbgPRINT(dbgflg_mgmt,lvl,fmt,##args) +#define dbg_loop(lvl,fmt,args...) dbgPRINT(dbgflg_loopback,lvl,fmt,##args) + +debug_option *serproto_get_dbg_table (void) +{ + return (dbg_table); +} + +/* Serial Driver Support Functions ************************************************************* */ + + +/* * + * serial_open - open serial device + * @tty: tty device + * @filp: file structure + * + * Called to open serial device. + */ +static int serial_open (struct tty_struct *tty, struct file *filp) +{ + unsigned long flags; + int n = 0, rc = 0; + struct serproto_dev *device = NULL; + + dbg_oc (3, "tty #%p file #%p", tty, filp); + + if (NULL == tty || 0 > (n = MINOR (tty->device) - tty->driver.minor_start) || + n >= serproto_devices || NULL == (device = serproto_device_array[n])) { + dbg_oc (1, "FAIL ENODEV"); + return -ENODEV; + } + + MOD_INC_USE_COUNT; + dbg_init (0, "OPEN uc=%d", GET_USE_COUNT (THIS_MODULE)); + write_lock_irqsave (&device->rwlock, flags); + + if (1 == ++device->opencnt) { + // First open + tty->driver_data = device; + device->tty = tty; + tty->low_latency = 1; + + + /* force low_latency on so that our tty_push actually forces the data through, + * otherwise it is scheduled, and with high data rates (like with OHCI) data + * can get lost. + * */ + tty->low_latency = 1; + + } else if (tty->driver_data != device || device->tty != tty) { + // Second or later open, different tty/device combo + rc = -EBUSY; + } + // XXX Should extract info from somewhere to see if receive is OK + write_unlock_irqrestore (&device->rwlock, flags); + + if (0 != rc) { + if (-EBUSY == rc) { + dbg_oc (1, "2nd, conflict: old dev #%p new #%p, old tty #%p new #%p", + tty->driver_data, device, device->tty, tty); + } + MOD_DEC_USE_COUNT; + dbg_init (0, "OPEN rc=%d uc=%d", rc, GET_USE_COUNT (THIS_MODULE)); + } + dbg_oc (3, "->%d n=%d", rc, n); + return (rc); +} + +static void serial_close (struct tty_struct *tty, struct file *filp) +{ + unsigned long flags; + struct serproto_dev *device; + int uc; + + uc = GET_USE_COUNT (THIS_MODULE); + dbg_oc (3, "tty #%p file #%p uc=%d", tty, filp, uc); + + if ((device = tty->driver_data) != NULL) { + write_lock_irqsave (&device->rwlock, flags); + if (0 >= --device->opencnt) { + // Last (or extra) close + dbg_oc (1, "Last: old tty #%p new #%p oc=%d", + device->tty, tty, device->opencnt); + tty->driver_data = NULL; + device->tty = NULL; + device->opencnt = 0; + } + write_unlock_irqrestore (&device->rwlock, flags); + } else { + dbg_oc (1, "not presently connected"); + } + if (uc > 0) { + // Should really check that uc hasn't changed since start of fn... + MOD_DEC_USE_COUNT; + dbg_init (0, "CLOSE uc=%d", GET_USE_COUNT (THIS_MODULE)); + } + dbg_oc (3, "OK"); + return; +} + +static void serial_flush (struct tty_struct *tty) +{ + dbg_mgmt (1, "tty#%p", tty); +} + +static int serial_write (struct tty_struct *tty, int from_user, const unsigned char *buf, int count) +{ + // Return the number of bytes (out of count) that get written, + // or negative for error. + unsigned long flags; + struct serproto_dev *device; + int cnt = count; + int size; + const unsigned char *currpos = buf; + + unsigned char *buffer; + + dbg_tx (3, "count=%d", count); + + if ((device = tty->driver_data) == NULL) { + dbg_tx (1, "not presently connected -> FAIL"); + return -EINVAL; + } + dbgPRINTmem (dbgflg_tx, 4, buf, count); + + // loop on data + while (cnt > 0) { + + int length; + + // send at most tx_size bytes + size = MIN (device->tx_size, cnt); + + write_lock_irqsave (&device->rwlock, flags); + // Make sure we can send. + if (!device->connected || + (device->max_queue_entries > 0 + && (device->queued_entries >= device->max_queue_entries)) + || (device->queued_bytes >= device->max_queue_bytes)) { + // Can't write any more, + // return the number that we did manage to send. + write_unlock_irqrestore (&device->rwlock, flags); + dbg_tx (2, "->%d/%d", (count - cnt), count); + return (count - cnt); + } + size = MIN ((device->max_queue_bytes - device->queued_bytes), size); + + // allocate a buffer + + length = (device->blocked ? device->tx_size : size) + 1 + device->trailer; + dbg_tx (1, "------> blocked: %d tx_size: %d size: %d trailer: %d, length: %d", + device->blocked, device->tx_size, size, device->trailer, length); + + if ((buffer = kmalloc (length, GFP_KERNEL)) == NULL) { + write_unlock_irqrestore (&device->rwlock, flags); + dbg_tx (2, "->ENOMEM"); + return -ENOMEM; + } + memset (buffer, '\0', length); + + + // copy data + if (from_user) { + copy_from_user ((void *) buffer, currpos, size); + } else { + memcpy ((void *) buffer, currpos, size); + } + + currpos += size; + cnt -= size; + + device->xmit_data (device->number, buffer, size); + + device->queued_entries++; + device->queued_bytes += size; + write_unlock_irqrestore (&device->rwlock, flags); + } + + // Everything went out. + dbg_tx (5, "->%d (all)", count); + + return count; +} + + +static int serial_write_room (struct tty_struct *tty) +{ + /* Return the amount of room for writing. */ + unsigned long flags; + struct serproto_dev *device; + int n = 0; + + dbg_tx (7, "entered"); + + if ((device = tty->driver_data) == NULL) { + dbg_tx (1, "not presently connected -> FAIL"); + return (-EINVAL); + } + read_lock_irqsave (&device->rwlock, flags); + if (device->connected && + (device->queued_bytes < device->max_queue_bytes) && + (device->max_queue_entries == 0 + || device->queued_entries < device->max_queue_entries)) { +#if 0 + if (device->tx_size < (n = device->max_queue_bytes - device->queued_bytes)) { + n = device->tx_size; + } +#else + n = device->max_queue_bytes - device->queued_bytes; +#endif + } + read_unlock_irqrestore (&device->rwlock, flags); + // Shouldn't really access these outside the lock, but only the dbg msg can go wrong. + dbg_tx (6, "c:%c b=%u/%u e=%u/%u -> %d", (device->connected ? 'T' : 'F'), + device->queued_bytes, device->max_queue_bytes, + device->queued_entries, device->max_queue_entries, n); + return (n); +} + + +static int +serial_ioctl (struct tty_struct *tty, struct file *file, unsigned int cmd, unsigned long arg) +{ + int rc, type; + + type = _IOC_TYPE (cmd); + dbg_mgmt (2, "type#%02x cmd#%08x arg#%08lx", type, cmd, arg); + + switch (cmd) { + case /* TCSETATTR */ 3: + rc = 0; + break; + default: + rc = -ENOIOCTLCMD; + } + + return (rc); +} + + +static void serial_set_termios (struct tty_struct *tty, struct termios *old) +{ + struct serproto_dev *device = tty->driver_data; + struct termios *tio = tty->termios; + + device->clocal = tio->c_cflag & CLOCAL; + dbg_mgmt (2, "clocal->%c", (device->clocal ? 'T' : 'F')); + + return; +} + + +static void serial_throttle (struct tty_struct *tty) +{ + dbg_mgmt (1, "entered"); + + return; +} + +static void serial_unthrottle (struct tty_struct *tty) +{ + dbg_mgmt (1, "entered"); + + return; +} + +static int serial_chars_in_buffer (struct tty_struct *tty) +{ + struct serproto_dev *device; + int n; + + //dbg_rx(4,"entered"); + + if ((device = tty->driver_data) == NULL) { + dbg_rx (1, "not presently connected -> FAIL"); + return (-EINVAL); + } + read_lock (&device->rwlock); + n = device->queued_bytes; + read_unlock (&device->rwlock); + //dbg_rx(4,"->%d",n); + return (n); +} + + + +static struct tty_driver serial_tty_driver = { + magic:TTY_DRIVER_MAGIC, + driver_name:"usbd-serial", + name:"usb", + major:SERIAL_TTY_MAJOR, + minor_start:0, + num:SERIAL_TTY_MINORS, + type:TTY_DRIVER_TYPE_SERIAL, + subtype:SERIAL_TYPE_NORMAL, + flags:TTY_DRIVER_REAL_RAW | TTY_DRIVER_NO_DEVFS, + + refcount:&serial_refcount, + table:serial_tty, + termios:serial_termios, + termios_locked:serial_termios_locked, + + open:serial_open, + close:serial_close, + flush_buffer:serial_flush, + write:serial_write, + write_room:serial_write_room, + ioctl:serial_ioctl, + set_termios:serial_set_termios, + throttle:serial_throttle, + unthrottle:serial_unthrottle, + chars_in_buffer:serial_chars_in_buffer, +}; + + +/* Library Interface Functions ***************************************************************** */ + +/** + * serproto_modinit - initialize the serproto library + * @name: name + * @num: number of interfaces to allow + * + */ +int serproto_modinit (char *name, int num) +{ + dbg_init (1, "%s[%d]", name, num); + + rwlock_init (&serproto_rwlock); + serproto_devices = num; + + if (!(serproto_device_array = kmalloc (sizeof (struct serproto_dev *) * num, GFP_KERNEL))) { + dbg_init (0, "kmalloc failed"); + return -EINVAL; + } + memset (serproto_device_array, 0, sizeof (struct serproto_dev *) * num); + + dbg_loop (1, "LOOPBACK mode"); + + return 0; +} + +static void wakeup_writers (void *private) +{ + struct serproto_dev *device = (struct serproto_dev *) private; + struct tty_struct *tty; + + if (NULL != device && NULL != (tty = device->tty)) { + if ((tty->flags & (1 << TTY_DO_WRITE_WAKEUP)) && NULL != tty->ldisc.write_wakeup) { + (tty->ldisc.write_wakeup) (tty); + } + wake_up_interruptible (&tty->write_wait); + } + MOD_DEC_USE_COUNT; +} + +/** + * serproto_create - create a serial interface + * @name: name + * @xmit_data: callback to transmit data + * @max_queue_entries: maximum number of outstanding requests to allow + * @max_queue_bytes: maximum number of bytes to allow + * + * Create a serial interface, providing an xmit data function. + * + * A returned value greater or equal to zero indicates success. This value can be + * used with subsequent function calls to indicate the created serial interface. + */ + +int serproto_create (char *name, + int (*xmit_data) (int, unsigned char *, int), + int tx_size, + int max_queue_entries, int max_queue_bytes, int blocked, int trailer) +{ + struct serproto_dev *device; + int i; + + dbg_init (1, "array: %p name: %p[%s] xmit: %p", serproto_device_array, name, name, + xmit_data); + + if (!serproto_device_array) { + dbg_init (1, "invalid args -> FAIL"); + return (-EINVAL); + } + + /* Note: the tty layer assumes that if *_write_room() returns n > 0, that + n more bytes can be queued up in UPTO n CALLS TO *_write(). It does not + re-verify the available room. This means that if we are limiting the + number of queued entries, and we indicate that 64 _bytes_ can be queued, + there may be upto 64 *_write(1) calls, which is probably going to overflow + the queue. It would also seem that the tty layer does not check the + return value of the *_write() calls, so that calls which fail due to + insufficient queue entries are simply lost. For this reason, setting + max_queue_entries to 0 allows unlimitied queueing. */ + if (!serproto_device_array || !name || !strlen (name) || !xmit_data || + max_queue_entries < 0 || max_queue_bytes <= 0) { + dbg_init (1, "invalid args -> FAIL"); + return (-EINVAL); + } + + { + unsigned long flags; + write_lock_irqsave (&serproto_rwlock, flags); + for (i = 0; i < serproto_devices; i++) { + if (!(device = serproto_device_array[i])) { + break; + } + } + if (i == serproto_devices) { + write_unlock_irqrestore (&serproto_rwlock, flags); + dbg_init (1, "name: %s cannot find empty serproto_device slot", name); + return (-ENOMEM); + } + + if (!(device = kmalloc (sizeof (struct serproto_dev), GFP_ATOMIC))) { + write_unlock_irqrestore (&serproto_rwlock, flags); + dbg_init (1, "name: %s kmalloc failed", name); + return (-ENOMEM); + } + + memset (device, 0, sizeof (struct serproto_dev)); + rwlock_init (&device->rwlock); + + device->xmit_data = xmit_data; + + device->tx_size = MIN (256, tx_size); // maximum 256 bytes + device->max_queue_entries = max_queue_entries; + device->max_queue_bytes = max_queue_bytes; + + device->write_wakeup_task.routine = wakeup_writers; + device->write_wakeup_task.data = device; + + device->blocked = blocked; + device->trailer = trailer; + + memcpy (&device->tty_driver, &serial_tty_driver, sizeof (struct tty_driver)); + device->tty_driver.init_termios = tty_std_termios; + device->tty_driver.init_termios.c_cflag = B9600 | CS8 | CREAD | HUPCL | CLOCAL; + // Turn off echo fight with standard UNIX USB Host serial drivers. + device->tty_driver.init_termios.c_lflag &= ~(ECHO | ICANON); + serproto_device_array[i] = device; + write_unlock_irqrestore (&serproto_rwlock, flags); + } + + if (tty_register_driver (&device->tty_driver)) { + unsigned long flags; + write_lock_irqsave (&serproto_rwlock, flags); + serproto_device_array[i] = NULL; + kfree (device); + write_unlock_irqrestore (&serproto_rwlock, flags); + printk (KERN_ERR __FUNCTION__ "(%s) - failed to register tty driver\n", name); + dbg_init (1, "try registration failed"); + return (-EINVAL); + } + + dbg_init (2, "name: %s -> %d", name, i); + return (i); +} + +/** + * serproto_done - transmit done + * @interface: device interface + * @data: data buffer + * @len: data length + * @rc: non-zero indicates failure + * + */ +int serproto_done (int interface, void *data, int len, int rc) +{ + struct serproto_dev *device; + unsigned long flags; + + dbg_tx (4, "interface: %d", interface); + + // Must be done with interrupts off, since sync may + // be set by another (different) call to queue_task() + + write_lock_irqsave (&device->rwlock, flags); + + if ((device = serproto_device_array[interface]) == NULL) { + write_unlock_irqrestore (&device->rwlock, flags); + dbg_tx (1, "no device -> FAIL"); + return (-EINVAL); + } + + device->queued_entries--; + device->queued_bytes -= len; + + if (!device->write_wakeup_task.sync) { + // Queue a wakeup for anyone waiting to write, + // but lock the module in place first. + MOD_INC_USE_COUNT; + queue_task (&device->write_wakeup_task, &tq_immediate); + mark_bh (IMMEDIATE_BH); + } + write_unlock_irqrestore (&device->rwlock, flags); + + return 0; +} + + +/** + * serproto_recv - receive data + * @data: data buffer + * @length: length of valid data + */ +int serproto_recv (int interface, unsigned char *data, int length) +{ + struct serproto_dev *device; + struct tty_struct *tty; + unsigned long flags; + int i; + + dbg_rx (4, "length=%d", length); + dbg_rx (5, "interface: %d data: %p serproto_device_array: %p", + interface, data, serproto_device_array); + dbg_rx (5, "device: %p", serproto_device_array[interface]); + + if ((device = serproto_device_array[interface]) == NULL) { + dbg_rx (1, "no device -> FAIL"); + return (-EINVAL); + } +#if 0 + if (0 != dbgflg_loopback) { + // serproto_done() will free data when transmission complete + device->xmit_data (device->number, data, length); + write_lock_irqsave (&device->rwlock, flags); + device->queued_entries++; + device->queued_bytes += length; + write_unlock_irqrestore (&device->rwlock, flags); + dbg_rx (3, "loopback->0"); + return (0); + } +#endif + + if (device->opencnt <= 0 || (tty = device->tty) == NULL) { + dbg_rx (1, "no tty -> FAIL"); + //kfree(data); + return (-EINVAL); + } + // XXX bail if not open for reading.... + for (i = 0; i < length; ++i) { + tty_insert_flip_char (tty, data[i], 0); + } + tty_flip_buffer_push (tty); + //kfree(data); + dbg_rx (4, "->0"); + return 0; +} + + +/** + * serproto_control - control serial interface + * @interface: serial interface + * @operation: operation + * + * Control a serial interface, + */ +int serproto_control (int interface, int operation) +{ + struct serproto_dev *device; + unsigned long flags; + dbg_mgmt (1, "interface: %d op=%d", interface, operation); + if ((device = serproto_device_array[interface]) == NULL) { + dbg_mgmt (1, "no device"); + return (-ENODEV); + } + switch (operation) { + case SERPROTO_CONNECT: + write_lock_irqsave (&device->rwlock, flags); + device->connected = TRUE; + /* Wake up anybody who might have blocked waiting + for the connection. */ + // Must be done with interrupts off, since sync may + // be set by another (different) call to queue_task() + if (!device->write_wakeup_task.sync) { + // Queue a wakeup for anyone waiting to write, + // but lock the module in place first. + MOD_INC_USE_COUNT; + queue_task (&device->write_wakeup_task, &tq_immediate); + mark_bh (IMMEDIATE_BH); + } + write_unlock_irqrestore (&device->rwlock, flags); + break; + case SERPROTO_DISCONNECT: + device->connected = FALSE; + /* Send a HANGUP to anybody involved with this device. */ + if (device->tty && !device->clocal) { + dbg_mgmt (1, "calling hangup"); + tty_hangup (device->tty); + } + + } + + return 0; +} + + +/** + * serproto_destroy - destroy a serial interface + * @interface: serial interface + * + * Call to tear down a previously created serial interface + */ + +int serproto_destroy (int interface) +{ + struct serproto_dev *device; + unsigned int flags; + + dbg_init (1, "interface: %d", interface); + + if ((device = serproto_device_array[interface]) == NULL) { + dbg_init (1, "no device"); + return (0); + } + // grab device lock and delete device entry from serproto device array + write_lock_irqsave (&serproto_rwlock, flags); + device = serproto_device_array[interface]; + serproto_device_array[interface] = NULL; + // Let write_wakeup_task know this device is toast. + device->tty = NULL; + write_unlock_irqrestore (&serproto_rwlock, flags); + + // remove serial interace + tty_unregister_driver (&device->tty_driver); + // XXXX make sure write_wakeup_task is not scheduled!!! + kfree (device); + + dbg_init (2, "->0"); + return 0; +} + + +/** + * serproto_modexit - unload library + * + */ +void serproto_modexit (void) +{ + int i; + int devices; + struct serproto_dev **device_array; + unsigned int flags; + + dbg_init (1, "entered"); + + if (0 == serproto_devices) { + // nothing to do + return; + } + + /* This should never be called from + an interrupt context, so irq save/restore + shouldn't be required, but it isn't going + to be called often enough for the extra + overhead to hurt either. */ + write_lock_irqsave (&serproto_rwlock, flags); + devices = serproto_devices; + serproto_devices = 0; + write_unlock_irqrestore (&serproto_rwlock, flags); + + for (i = 0; i < devices; i++) { + serproto_destroy (i); + } + + write_lock_irqsave (&serproto_rwlock, flags); + device_array = serproto_device_array; + serproto_device_array = NULL; + write_unlock_irqrestore (&serproto_rwlock, flags); + kfree (serproto_device_array); +} diff -uNr linux.org/drivers/usb/device/serial_fd/serproto.h linux/drivers/usb/device/serial_fd/serproto.h --- linux.org/drivers/usb/device/serial_fd/serproto.h Thu Jan 1 08:00:00 1970 +++ linux/drivers/usb/device/serial_fd/serproto.h Thu May 15 15:54:48 2003 @@ -0,0 +1,44 @@ +/* + * serial_fd/serproto.h + * + * Copyright (c) 2000, 2001, 2002 Lineo + * Copyright (c) 2001 Hewlett Packard + * + * By: + * Stuart Lynne , + * Tom Rushworth , + * Bruce Balden + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + * + */ + +#ifndef SERPROTO_H +#define SERPROTO_H 1 +#include "../usbd-debug.h" + +extern debug_option *serproto_get_dbg_table (void); +extern int serproto_modinit (char *, int); +extern int serproto_create (char *, int (*xmit_data) (int, unsigned char *, int), int, int, int, + int, int); +extern int serproto_done (int, void *, int, int); +extern int serproto_recv (int, unsigned char *, int); +extern int serproto_control (int, int); +#define SERPROTO_DISCONNECT 0 +#define SERPROTO_CONNECT 1 +extern int serproto_destroy (int); +extern void serproto_modexit (void); + +#endif diff -uNr linux.org/drivers/usb/device/usbd-arch.h linux/drivers/usb/device/usbd-arch.h --- linux.org/drivers/usb/device/usbd-arch.h Thu Jan 1 08:00:00 1970 +++ linux/drivers/usb/device/usbd-arch.h Thu May 15 15:54:48 2003 @@ -0,0 +1,374 @@ +/* + * linux/drivers/usbd/usbd-arch.h + * + * Copyright (c) 2000, 2001, 2002 Lineo + * Copyright (c) 2001 Hewlett Packard + * + * By: + * Stuart Lynne , + * Tom Rushworth , + * Bruce Balden + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + * + */ + +/* + * This file contains pre-canned configurations for specific architectures + * and systems. + * + * The architecture specific areas concentrates on setting defaults that + * will work with each architecture (endpoint restrictions etc). + * + * The system specific areas set defaults appropriate for a specific + * system or board (vendor id etc). + * + * Typically during early development you will set these via the kernel + * configuration and migrate the values into here once specific values + * have been tested and will no longer change. + */ + + +/* + * Lineo specific + */ + +#define VENDOR_SPECIFIC_CLASS 0xff +#define VENDOR_SPECIFIC_SUBCLASS 0xff +#define VENDOR_SPECIFIC_PROTOCOL 0xff + +/* + * Lineo Classes + */ +#define LINEO_CLASS 0xff + +#define LINEO_SUBCLASS_SAFENET 0x01 +#define LINEO_SUBCLASS_SAFESERIAL 0x02 + +/* + * Lineo Protocols + */ +#define LINEO_SAFENET_CRC 0x01 +#define LINEO_SAFENET_CRC_PADDED 0x02 + +#define LINEO_SAFESERIAL_CRC 0x01 +#define LINEO_SAFESERIAL_CRC_PADDED 0x02 + + +/* + * Architecture specific endpoint configurations + */ + +#if defined(CONFIG_ARCH_SA1100) || defined(CONFIG_ARCH_SA1100_MODULE) +#warning +#warning SETTING DEFAULTS FOR SA1110 +/* + * The StrongArm SA-1110 has fixed endpoints and the bulk endpoints + * are limited to 64 byte packets and does not support ZLP. + */ + + #define ABS_OUT_ADDR 1 + #define ABS_IN_ADDR 2 + #define ABS_INT_ADDR 0 + + #define MAX_OUT_PKTSIZE 64 + #define MAX_IN_PKTSIZE 64 + #undef MAX_INT_PKTSIZE + + #undef CONFIG_USBD_SERIAL_OUT_ENDPOINT + #undef CONFIG_USBD_SERIAL_IN_ENDPOINT + #undef CONFIG_USBD_SERIAL_INT_ENDPOINT + + #define CONFIG_USBD_NO_ZLP_SUPPORT + + #define CONFIG_USBD_MAC_AS_SERIAL_NUMBER 1 + +#endif + +#if defined(CONFIG_ARCH_L7200)|| defined(CONFIG_ARCH_L7200_MODULE) +#warning +#warning SETTING DEFAULTS FOR L7200 +/* + * The Linkup L7205/L7210 has fixed endpoints and the bulk endpoints + * are limited to 32 byte packets and does not support ZLP. + * + * To get around specific hardware problems the Linkup eliminates optional + * strings in the configuration, reverses the CDC data interfaces. + * + * It also requires all USB packets sent and received to be padded. They + * must be either 31 or 32 bytes in length. + * + * XXX The new version of the L7210 may not require padding. + */ + #define ABS_OUT_ADDR 1 + #define ABS_IN_ADDR 2 + #define ABS_INT_ADDR 3 + + #define MAX_OUT_PKTSIZE 32 + #define MAX_IN_PKTSIZE 32 + #define MAX_INT_PKTSIZE 16 + + #undef CONFIG_USBD_SERIAL_OUT_ENDPOINT + #undef CONFIG_USBD_SERIAL_IN_ENDPOINT + #undef CONFIG_USBD_SERIAL_INT_ENDPOINT + + #define CONFIG_USBD_NO_ZLP_SUPPORT + #define CONFIG_USBD_NET_NO_STRINGS + #define CONFIG_USBD_NET_PADDED + #define CONFIG_USBD_NET_CDC_DATA_REVERSED + + #undef CONFIG_USBD_NET_CDC + #undef CONFIG_USBD_NET_MDLM + #undef CONFIG_USBD_NET_SAFE + + #define CONFIG_USBD_NET_MDLM 1 + #define CONFIG_USBD_MAC_AS_SERIAL_NUMBER 1 + +#endif + +#if defined(CONFIG_USBD_SL11_BUS)|| defined(CONFIG_USBD_SL11_BUS_MODULE) +#warning +#warning SETTING DEFAULTS FOR SL11 +/* + * The SL11 endpoints can be a mix of 1, 2, or 3. The SL11 endpoints have + * fixed addresses but each can be used for Bulk IN, Bulk OUT and Interrupt. + * + * The default addresses can be overridden by using the kernel configuration + * and setting the net-fd IN, OUT and INT addresses. See Config.in + */ + + #define DFL_OUT_ADDR 1 + #define DFL_IN_ADDR 2 + #define DFL_INT_ADDR 3 + + #define MAX_OUT_ADDR 3 + #define MAX_IN_ADDR 3 + #define MAX_INT_ADDR 3 + + #define MAX_OUT_PKTSIZE 64 + #define MAX_IN_PKTSIZE 64 + #define MAX_INT_PKTSIZE 16 + +#endif + +#if defined(CONFIG_USBD_SUPERH_BUS)|| defined(CONFIG_USBD_SUPERH_BUS_MODULE) +#warning +#warning SETTING DEFAULTS FOR SUPERH +/* + * The SuperH 7727 has fixed endpoints and does not support ZLP. + */ + + #define ABS_OUT_ADDR 1 + #define ABS_IN_ADDR 2 + #define ABS_INT_ADDR 3 + + #define MAX_OUT_PKTSIZE 64 + #define MAX_IN_PKTSIZE 64 + #define MAX_INT_PKTSIZE 16 + + #undef CONFIG_USBD_NET_CDC + #undef CONFIG_USBD_NET_MDLM + #undef CONFIG_USBD_NET_SAFE + + #define CONFIG_USBD_NET_MDLM 1 + #define CONFIG_USBD_MAC_AS_SERIAL_NUMBER 1 +#endif + +#if defined(CONFIG_USBD_PXA_BUS)|| defined(CONFIG_USBD_PXA_BUS_MODULE) +/* + * The PXA has fixed endpoints + */ + #define ABS_IN_ADDR 1 + #define ABS_OUT_ADDR 2 + #define ABS_INT_ADDR 5 + + #define MAX_OUT_PKTSIZE 64 + #define MAX_IN_PKTSIZE 64 + #define MAX_INT_PKTSIZE 8 + + #undef CONFIG_USBD_NET_CDC + #undef CONFIG_USBD_NET_MDLM + #undef CONFIG_USBD_NET_SAFE + + #define CONFIG_USBD_NET_MDLM 1 + #define CONFIG_USBD_MAC_AS_SERIAL_NUMBER 1 +#endif + +/* ********************************************************************************************* */ + +/* + * known vendor and product ids + * Serial and network vendor ids can be overidden with kernel config. + */ + +/* + * For itsy we will use Compaq iPaq vendor ID and 0xffff for product id + */ +#if defined(CONFIG_SA1100_ITSY) || defined(CONFIG_SA1100_H3XXX) || defined(CONFIG_SA1100_ITSY_MODULE) || defined(CONFIG_SA1100_H3XXX_MODULE) + + #warning CONFIGURING FOR IPAQ H3XXX + + #undef CONFIG_USBD_VENDORID + #undef CONFIG_USBD_PRODUCTID + #define CONFIG_USBD_VENDORID 0x049f + #define CONFIG_USBD_PRODUCTID 0xffff + + #undef CONFIG_USBD_VENDORID + #define CONFIG_USBD_VENDORID 0x04dd + #undef CONFIG_USBD_SERIAL_PRODUCTID + #define CONFIG_USBD_SERIAL_PRODUCTID 0x8001 + #undef CONFIG_USBD_NET_PRODUCTID + #define CONFIG_USBD_NET_PRODUCTID 0x8003 + + + #undef CONFIG_USBD_SELFPOWERED + #undef CONFIG_USBD_MANUFACTURER + #undef CONFIG_USBD_PRODUCT_NAME + + #define CONFIG_USBD_SELFPOWERED 1 + #define CONFIG_USBD_MANUFACTURER "Compaq" + #define CONFIG_USBD_PRODUCT_NAME "iPaq" + + #undef CONFIG_USBD_NET_CDC + #undef CONFIG_USBD_NET_MDLM + #undef CONFIG_USBD_NET_SAFE + + #define CONFIG_USBD_NET_MDLM 1 + #define CONFIG_USBD_MAC_AS_SERIAL_NUMBER 1 +#endif + +/* + * Assigned vendor and product ids for HP Calypso + */ +#if defined(CONFIG_SA1100_CALYPSO) || defined(CONFIG_SA1110_CALYPSO) || defined(CONFIG_SA1100_CALYPSO_MODULE) || defined(CONFIG_SA1110_CALYPSO_MODULE) + + #warning CONFIGURING FOR CALYPSO + + #undef CONFIG_USBD_VENDORID + #undef CONFIG_USBD_PRODUCTID + #define CONFIG_USBD_VENDORID 0x03f0 + #define CONFIG_USBD_PRODUCTID 0x2101 + + #undef CONFIG_USBD_SELFPOWERED + #undef CONFIG_USBD_MANUFACTURER + #undef CONFIG_USBD_PRODUCT_NAME + + #define CONFIG_USBD_SELFPOWERED 1 + #define CONFIG_USBD_MANUFACTURER "HP" + #define CONFIG_USBD_PRODUCT_NAME "Journada X25" +#endif + +/* + * Assigned vendor and serial/network product ids for Sharp Iris + */ +#if defined(CONFIG_ARCH_L7200) && defined(CONFIG_IRIS) || defined(CONFIG_ARCH_L7200_MODULE) && defined(CONFIG_IRIS_MODULE) + + #warning CONFIGURING FOR LINKUP + + #undef CONFIG_USBD_VENDORID + #define CONFIG_USBD_VENDORID 0x04dd + #undef CONFIG_USBD_SERIAL_PRODUCTID + #define CONFIG_USBD_SERIAL_PRODUCTID 0x8001 + #undef CONFIG_USBD_NET_PRODUCTID + #define CONFIG_USBD_NET_PRODUCTID 0x8003 + + #undef CONFIG_USBD_SELFPOWERED + #undef CONFIG_USBD_MANUFACTURER + #undef CONFIG_USBD_PRODUCT_NAME + + #define CONFIG_USBD_SELFPOWERED 1 + #define CONFIG_USBD_MANUFACTURER "Sharp" + #define CONFIG_USBD_PRODUCT_NAME "Iris" + + #undef CONFIG_USBD_NET_CDC + #undef CONFIG_USBD_NET_MDLM + #undef CONFIG_USBD_NET_SAFE + + #define CONFIG_USBD_NET_MDLM 1 + #define CONFIG_USBD_MAC_AS_SERIAL_NUMBER 1 +#endif + + +/* + * Assigned vendor and serial/network product ids for Sharp Collie + */ +#if defined(CONFIG_SA1100_COLLIE) || defined(CONFIG_SA1100_COLLIE_MODULE) + + #warning CONFIGURING FOR COLLIE + + #undef CONFIG_USBD_VENDORID + #define CONFIG_USBD_VENDORID 0x04dd + #undef CONFIG_USBD_SERIAL_PRODUCT ID + #define CONFIG_USBD_SERIAL_PRODUCT ID 0x8002 + #undef CONFIG_USBD_NET_PRODUCTID + //#define CONFIG_USBD_NET_PRODUCTID 0x8004 + #define CONFIG_USBD_NET_PRODUCTID 0x8003 + + #undef CONFIG_USBD_SELFPOWERED + #undef CONFIG_USBD_MANUFACTURER + #undef CONFIG_USBD_PRODUCT_NAME + + #define CONFIG_USBD_SELFPOWERED 1 + #define CONFIG_USBD_MANUFACTURER "Sharp" + #define CONFIG_USBD_PRODUCT_NAME "Zaurus" + + #undef CONFIG_USBD_NET_CDC + #undef CONFIG_USBD_NET_MDLM + #undef CONFIG_USBD_NET_SAFE + + #define CONFIG_USBD_NET_MDLM 1 + #define CONFIG_USBD_MAC_AS_SERIAL_NUMBER 1 + +#endif + + +/* + * Assigned vendor and serial/network product ids for Vercel UD1 + */ +#if defined(CONFIG_SA1100_IDR) + + #warning CONFIGURING FOR Vercel UD1 + + #define CONFIG_USBD_MAC_AS_SERIAL_NUMBER 1 + +#endif + +#ifdef CONFIG_SABINAL_DISCOVERY + + #warning CONFIGURING FOR DISCOVERY + + #undef CONFIG_USBD_VENDORID + #define CONFIG_USBD_VENDORID 0x04dd + #undef CONFIG_USBD_SERIAL_PRODUCT ID + #define CONFIG_USBD_SERIAL_PRODUCT ID 0x8005 + #undef CONFIG_USBD_NET_PRODUCTID + #define CONFIG_USBD_NET_PRODUCTID 0x8005 + + #undef CONFIG_USBD_SELFPOWERED + #undef CONFIG_USBD_MANUFACTURER + #undef CONFIG_USBD_PRODUCT_NAME + + #define CONFIG_USBD_SELFPOWERED 1 + #define CONFIG_USBD_MANUFACTURER "Sharp" + #define CONFIG_USBD_PRODUCT_NAME "SL-A300" + + #undef CONFIG_USBD_NET_CDC + #undef CONFIG_USBD_NET_MDLM + #undef CONFIG_USBD_NET_SAFE + + #define CONFIG_USBD_NET_MDLM 1 + #define CONFIG_USBD_MAC_AS_SERIAL_NUMBER 1 + +#endif diff -uNr linux.org/drivers/usb/device/usbd-build.h linux/drivers/usb/device/usbd-build.h --- linux.org/drivers/usb/device/usbd-build.h Thu Jan 1 08:00:00 1970 +++ linux/drivers/usb/device/usbd-build.h Thu May 15 15:54:48 2003 @@ -0,0 +1 @@ +#define USBD_BUILD "035" diff -uNr linux.org/drivers/usb/device/usbd-bus.c linux/drivers/usb/device/usbd-bus.c --- linux.org/drivers/usb/device/usbd-bus.c Thu Jan 1 08:00:00 1970 +++ linux/drivers/usb/device/usbd-bus.c Thu May 15 15:54:48 2003 @@ -0,0 +1,564 @@ +/* + * linux/drivers/usbd/usbd-bus.c - USB Device Prototype + * + * Copyright (c) 2000, 2001, 2002 Lineo + * Copyright (c) 2001 Hewlett Packard + * + * By: + * Stuart Lynne , + * Tom Rushworth , + * Bruce Balden + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + * + */ + +#include +#define USBD_CONFIG_NOWAIT_DEREGISTER_DEVICE 1 +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "usbd.h" +#include "usbd-debug.h" +#include "usbd-func.h" +#include "usbd-bus.h" +#include "usbd-inline.h" + +extern int usb_devices; +extern struct usb_function_driver ep0_driver; +extern int registered_functions; + +extern struct list_head function_drivers; +extern struct list_head devices; + +static __inline__ struct usb_function_driver *list_entry_func (const struct list_head *le) +{ + return list_entry (le, struct usb_function_driver, drivers); +} + +static __inline__ struct usb_device_instance *list_entry_device (const struct list_head *le) +{ + return list_entry (le, struct usb_device_instance, devices); +} + +// Debug stuff - dbgflg_usbdbi_init must be defined in the main part of the bus driver (e.g. *_bi/init.c) +int dbgflg_usbdbi_init; +#define dbg_init(lvl,fmt,args...) dbgPRINT(dbgflg_usbdbi_init,lvl,fmt,##args) + +extern int registered_functions; +extern int registered_devices; + + +/** + * usbd_fill_rcv - fill the rx recyle queue with empty urbs + * @endpoint: + * + * Fill the recycle queue so that receive has some empty urbs to use. + */ +void usbd_fill_rcv (struct usb_device_instance *device, struct usb_endpoint_instance *endpoint, + int num) +{ + int i; + unsigned buffersize; + + dbg_init (1, "endpoint: %d", endpoint->endpoint_address); + + if (endpoint->rcv_packetSize) { + + // we need to allocate URBs with enough room for the endpoints transfersize plus one rounded + // up to the next packetsize + + buffersize = + ((endpoint->rcv_transferSize + + endpoint->rcv_packetSize) / endpoint->rcv_packetSize) * + endpoint->rcv_packetSize; + + dbg_init (1, "endpoint: %d packetSize: %d transferSize: %d buffersize: %d", + endpoint->endpoint_address, endpoint->rcv_packetSize, + endpoint->rcv_transferSize, buffersize); + + // XXX should we do this here? + usbd_flush_rcv(endpoint); + for (i = 0; i < num; i++) { + usbd_recycle_urb (usbd_alloc_urb + (device, device->function_instance_array, + endpoint->endpoint_address, buffersize)); + } + } else { + dbg_init (0, "endpoint: %d packetSize is Zero!", endpoint->endpoint_address); + } + +} + +/** + * usbd_flush_rcv - flush rcv + * @endpoint: + * + * Iterate across the rx list and dispose of any urbs. + */ +void usbd_flush_rcv (struct usb_endpoint_instance *endpoint) +{ + struct urb *rcv_urb = NULL; + unsigned long flags; + + dbg_init (1, "endpoint: %d", endpoint->endpoint_address); + if (endpoint) { + local_irq_save (flags); + + if ((rcv_urb = endpoint->rcv_urb)) { + endpoint->rcv_urb = NULL; + usbd_dealloc_urb (rcv_urb); + } + + while ((rcv_urb = first_urb_detached (&endpoint->rdy))) { + usbd_dealloc_urb (rcv_urb); + } + + local_irq_restore (flags); + } +} + +/** + * usbd_flush_tx - flush tx urbs from endpoint + * @endpoint: + * + * Iterate across the tx list and cancel any outstanding urbs. + */ +void usbd_flush_tx (struct usb_endpoint_instance *endpoint) +{ + struct urb *send_urb = NULL; + unsigned long flags; + + if (endpoint) { + local_irq_save (flags); + + if ((send_urb = endpoint->tx_urb)) { + endpoint->tx_urb = NULL; + usbd_urb_sent_irq (send_urb, SEND_FINISHED_ERROR); + } + + while ((send_urb = first_urb_detached (&endpoint->tx))) { + usbd_urb_sent_irq (send_urb, SEND_FINISHED_ERROR); + } + + local_irq_restore (flags); + } +} + +/** + * usbd_flush_ep - flush urbs from endpoint + * @endpoint: + * + * Iterate across the approrpiate tx or rcv list and cancel any outstanding urbs. + */ +void usbd_flush_ep (struct usb_endpoint_instance *endpoint) +{ + if (endpoint) { + if (endpoint->endpoint_address & 0x7f) { + usbd_flush_tx (endpoint); + } else { + usbd_flush_rcv (endpoint); + } + } +} + +/** + * usbd_device_bh - + * @data: + * + * Bottom half handler to process sent or received urbs. + */ +void usbd_device_bh (void *data) +{ + struct usb_device_instance *device; + + if ((device = data)) { + int i; + + for (i = 0; i < device->bus->driver->max_endpoints; i++) { + struct usb_endpoint_instance *endpoint = device->bus->endpoint_array + i; + + // process received urbs + if (endpoint->endpoint_address + && (endpoint->endpoint_address & USB_REQ_DIRECTION_MASK) == + USB_REQ_HOST2DEVICE) { + struct urb *urb; + while ((urb = first_urb_detached (&endpoint->rcv))) { + if (!urb->function_instance || + !urb->function_instance->function_driver->ops->recv_urb) + { + dbg_init (0, "usbd_device_bh: no recv_urb function"); + usbd_recycle_urb (urb); + } + else if (urb->function_instance->function_driver->ops->recv_urb (urb)) { + // XXX printk(KERN_ERR"usbd_device_bh: recv_urb failed\n"); + usbd_recycle_urb (urb); + } + } + } + // process sent urbs + if (endpoint->endpoint_address + && (endpoint->endpoint_address & USB_REQ_DIRECTION_MASK) == + USB_REQ_DEVICE2HOST) { + struct urb *urb; + while ((urb = first_urb_detached (&endpoint->done))) { + if (!urb->function_instance || + !urb->function_instance->function_driver->ops->urb_sent || + urb->function_instance->function_driver->ops->urb_sent + (urb, urb-> status) + ) + { + dbg_init (0, "usbd_device_bh: no urb_sent function"); + usbd_dealloc_urb (urb); + } + } + } + + } + +#if defined(CONFIG_SA1100_COLLIE) && defined(CONFIG_PM) + { + // Please clear autoPowerCancel flag during the transmission and the reception. + // XXX XXX extern + int autoPowerCancel; + autoPowerCancel = 0; // Auto Power Off Cancel + } +#endif + +#if defined(CONFIG_SA1110_CALYPSO) && defined(CONFIG_PM) + // Shouldn't need to make this atomic, all we need is a change indicator + device->usbd_rxtx_timestamp = jiffies; +#endif + + if (device->status == USBD_CLOSING) { + device->device_bh.data = NULL; + } + } + else { + dbg_init (0, "usbd_device_bh: device NULL"); + } +} + +/** + * usbd_function_bh - + * @data: + * + * Bottom half handler to process events for functions. + */ +void usbd_function_bh (void *data) +{ + // Process control (ep0) events for the function layer. + struct usb_device_instance *device; + + if ((device = data)) { + // Pick up endpoint 0. + struct usb_endpoint_instance *endpoint = device->bus->endpoint_array + 0; + + // process event urbs + struct urb *urb; + while ((urb = first_urb_detached (&endpoint->events))) { + if (device->status != USBD_CLOSING) { + if (device->function_instance_array && + (device->function_instance_array + 0)->function_driver->ops->event) + { + (device->function_instance_array + 0)->function_driver->ops->event + (device, urb->event, urb->data); + } + } + usbd_dealloc_urb (urb); + } + if (device->status == USBD_CLOSING) { + device->function_bh.data = NULL; + } + } + // Not an error if closing in progress + else { + dbg_init (1, "usbd_function_bh: device NULL"); + } +} + +/* usb-device USB BUS INTERFACE generic functions ******************************************** */ + +/** + * usbd_register_bus - called by a USB BUS INTERFACE driver to register a bus driver + * @driver: pointer to bus driver structure + * + * Used by a USB Bus interface driver to register itself with the usb device + * layer. + */ +struct __init usb_bus_instance *usbd_register_bus (struct usb_bus_driver *driver) +{ + struct usb_bus_instance *bus; + int i; + + dbg_init (2, "-"); + + if ((bus = ckmalloc (sizeof (struct usb_bus_instance), GFP_ATOMIC)) == NULL) { + return NULL; + } + bus->driver = driver; + if (! + (bus->endpoint_array = + ckmalloc (sizeof (struct usb_endpoint_instance) * bus->driver->max_endpoints, + GFP_ATOMIC))) { + kfree (bus); + return NULL; + } + + for (i = 0; i < bus->driver->max_endpoints; i++) { + + struct usb_endpoint_instance *endpoint = bus->endpoint_array + i; + + urb_link_init (&endpoint->events); + urb_link_init (&endpoint->rcv); + urb_link_init (&endpoint->rdy); + urb_link_init (&endpoint->tx); + urb_link_init (&endpoint->done); + } + + return bus; +} + +/** + * usbd_deregister_bus - called by a USB BUS INTERFACE driver to deregister a bus driver + * @bus: pointer to bus driver instance + * + * Used by a USB Bus interface driver to de-register itself with the usb device + * layer. + */ +void __exit usbd_deregister_bus (struct usb_bus_instance *bus) +{ + dbg_init (3, "%s", bus->driver->name); + kfree (bus->endpoint_array); + kfree (bus); +} + +/* usb-device USB Device generic functions *************************************************** */ + +/** + * usbd_register_device - called to create a virtual device + * @name: name + * @bus: pointer to struct usb_device_instance + * @maxpacketsize: ep0 maximum packetsize + * + * Used by a USB Bus interface driver to create a virtual device. + */ +struct usb_device_instance *__init usbd_register_device (char *name, struct usb_bus_instance *bus, + int maxpacketsize) +{ + struct usb_device_instance *device; + struct usb_function_instance *function_instance_array; + struct list_head *lhd; + int num = usb_devices++; + char buf[32]; + int function; + + dbg_init (3, "- - - - - - -"); + + // allocate a usb_device_instance structure + if (!(device = ckmalloc (sizeof (struct usb_device_instance), GFP_ATOMIC))) { + dbg_init (0, "ckmalloc device failed"); + return NULL; + } + // create a name + if (!name || !strlen (name)) { + sprintf (buf, "usb%d", num); + name = buf; + } + if ((device->name = strdup (name)) == NULL) { + kfree (device); + dbg_init (0, "strdup name failed"); + return NULL; + } + + device->device_state = STATE_CREATED; + device->status = USBD_OPENING; + + // allocate a usb_function_instance for ep0 default control function driver + if ((device->ep0 = ckmalloc (sizeof (struct usb_function_instance), GFP_ATOMIC)) == NULL) { + kfree (device->name); + kfree (device); + dbg_init (0, "ckmalloc device failed"); + return NULL; + } + device->ep0->function_driver = &ep0_driver; + + // allocate an array of usb configuration instances + if ((function_instance_array = + ckmalloc (sizeof (struct usb_function_instance) * registered_functions, + GFP_ATOMIC)) == NULL) { + kfree (device->ep0); + kfree (device->name); + kfree (device); + dbg_init (0, "ckmalloc function_instance_array failed"); + return NULL; + } + + device->functions = registered_functions; + device->function_instance_array = function_instance_array; + + dbg_init (2, "device: %p function_instance[]: %p registered_functions: %d", + device, device->function_instance_array, registered_functions); + + // connect us to bus + device->bus = bus; + + // iterate across all of the function drivers to construct a complete list of configuration descriptors + // XXX there is currently only one XXX + function = 0; + dbg_init (1, "function init"); + list_for_each (lhd, &function_drivers) { + struct usb_function_driver *function_driver; + function_driver = list_entry_func (lhd); + // build descriptors + dbg_init (1, "calling function_init"); + usbd_function_init (bus, device, function_driver); + // save + function_instance_array[function].function_driver = function_driver; + } + + // device bottom half + device->device_bh.routine = usbd_device_bh; + device->device_bh.data = device; + // XXX device->device_bh.sync = 0; + + // function bottom half + device->function_bh.routine = usbd_function_bh; + device->function_bh.data = device; + // XXX device->function_bh.sync = 0; + + dbg_init (3, "%p %p", device, device->device_bh.data); + + // add to devices queue + list_add_tail (&device->devices, &devices); + registered_devices++; + + dbg_init (3, "%s finished", bus->driver->name); + return device; +} + +/** + * usbd_deregister_device - called by a USB BUS INTERFACE driver to deregister a physical interface + * @device: pointer to struct usb_device_instance + * + * Used by a USB Bus interface driver to destroy a virtual device. + */ +void __exit usbd_deregister_device (struct usb_device_instance *device) +{ + struct usb_function_instance *function_instance; + + dbg_init (3, "%s start", device->bus->driver->name); + + // prevent any more bottom half scheduling + device->status = USBD_CLOSING; + +#ifdef USBD_CONFIG_NOWAIT_DEREGISTER_DEVICE + /* We need to run the bottom halves at least once with the + status == USBD_CLOSING in order to make sure all the + URBS are freed (otherwise we have a memleak). The + bh's set the data pointer to NULL when they notice + the USBD_CLOSING status. */ + if (device->device_bh.sync) { + // There is a bh queued. The only way deal with this other + // than waiting is to do the task. + run_task_queue(&tq_immediate); + } + // Verify the status flag was detected. + if (NULL != device->device_bh.data) { + // It hasn't been detected yet, so queue the bh fn, + queue_task(&device->device_bh, &tq_immediate); + // then run it. + run_task_queue(&tq_immediate); + /* If the second time fails, something is wrong. */ + if (NULL != device->device_bh.data) { + dbg_init(0, "run_task_queue(&tq_immediate) fails to clear device_bh"); + } + } + // Similar logic but a different queue for the function bh. + if (device->function_bh.sync) { + flush_scheduled_tasks(); + } + if (NULL != device->function_bh.data) { + schedule_task(&device->function_bh); + flush_scheduled_tasks(); + /* If the second time fails, something is wrong. */ + if (NULL != device->function_bh.data) { + dbg_init (0,"flush_scheduled_tasks() fails to clear function_bh"); + } + } + /* Leave the wait in as paranoia - in case one of the task operations + above fails to clear the queued task. It should never happen, but ... */ +#endif + // wait for pending device and function bottom halfs to finish + //while (device->device_bh.sync || device->function_bh.sync) { + while (device->device_bh.data || device->function_bh.data) { + + if (device->device_bh.data) { + dbg_init(0, "waiting for usbd_device_bh %ld %p", device->device_bh.sync, device->device_bh.data); + // This can probably be either, but for consistency's sake... + queue_task(&device->device_bh, &tq_immediate); + // schedule_task(&device->device_bh); + } + if (device->function_bh.data) { + dbg_init(0, "waiting for usbd_function_bh %ld %p", device->function_bh.sync, device->function_bh.data); + schedule_task(&device->function_bh); + } + schedule_timeout (10 * HZ); + } + + // tell the function driver to close + usbd_function_close (device); + + // disconnect from bus + device->bus = NULL; + + // remove from devices queue + list_del (&device->devices); + + // free function_instances + + if ((function_instance = device->function_instance_array)) { + device->function_instance_array = NULL; + dbg_init (3, "freeing function instances: %p", function_instance); + kfree (function_instance); + } + // de-configured ep0 + if ((function_instance = device->ep0)) { + device->ep0 = NULL; + dbg_init (3, "freeing ep0 instance: %p", function_instance); + kfree (function_instance); + } + + kfree (device->name); + kfree (device); + + registered_devices--; + dbg_init (3, "finished"); +} diff -uNr linux.org/drivers/usb/device/usbd-bus.h linux/drivers/usb/device/usbd-bus.h --- linux.org/drivers/usb/device/usbd-bus.h Thu Jan 1 08:00:00 1970 +++ linux/drivers/usb/device/usbd-bus.h Thu May 15 15:54:48 2003 @@ -0,0 +1,91 @@ +/* + * linux/drivers/usbd/usb-bus.c - basic bus interface support + * + * Copyright (c) 2000, 2001, 2002 Lineo + * Copyright (c) 2001 Hewlett Packard + * + * By: + * Stuart Lynne , + * Tom Rushworth , + * Bruce Balden + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + * + */ + +/* + * USB Bus Interface Driver structures + * + * Driver description: + * + * struct usb_bus_operations + * struct usb_bus_driver + * + */ + + +struct usb_device_description; + +/* Operations that the slave layer or function driver can use to interact + * with the bus interface driver. + * + * The send_urb() function is used by the usb-device endpoint 0 driver and + * function drivers to submit data to be sent. + * + * The cancel_urb() function is used by the usb-device endpoint 0 driver and + * function drivers to remove previously queued data to be sent. + * + * The endpoint_halted() function is used by the ep0 control function to + * check if an endpoint is halted. + * + * The device_feature() function is used by the ep0 control function to + * set/reset device features on an endpoint. + * + * The device_event() function is used by the usb device core to tell the + * bus interface driver about various events. + */ +struct usb_bus_operations { + int (*send_urb) (struct urb *); + int (*cancel_urb) (struct urb *); + int (*endpoint_halted) (struct usb_device_instance *, int); + int (*device_feature) (struct usb_device_instance *, int, int); + int (*device_event) (struct usb_device_instance *, usb_device_event_t, int); +}; + +/* Bus Interface data structure + * + * Keep track of specific bus interface. + * + * This is passed to the usb-device layer when registering. It contains all + * required information about each real bus interface found such that the + * usb-device layer can create and maintain a usb-device structure. + * + * Note that bus interface registration is incumbent on finding specific + * actual real bus interfaces. There will be a registration for each such + * device found. + * + * The max_tx_endpoints and max_rx_endpoints are the maximum number of + * possible endpoints that this bus interface can support. The default + * endpoint 0 is not included in these counts. + * + */ +struct usb_bus_driver { + char *name; + unsigned char max_endpoints; // maximimum number of rx enpoints + unsigned char maxpacketsize; + struct usb_device_description *device_description; + struct usb_bus_operations *ops; + struct module *this_module; // manage inc use counts to prevent unload races +}; diff -uNr linux.org/drivers/usb/device/usbd-debug.c linux/drivers/usb/device/usbd-debug.c --- linux.org/drivers/usb/device/usbd-debug.c Thu Jan 1 08:00:00 1970 +++ linux/drivers/usb/device/usbd-debug.c Thu May 15 15:54:48 2003 @@ -0,0 +1,262 @@ +/* + * linux/drivers/usbd/usbd-debug.c + * + * Copyright (c) 2000, 2001, 2002 Lineo + * Copyright (c) 2001 Hewlett Packard + * + * By: + * Stuart Lynne , + * Tom Rushworth , + * Bruce Balden + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + * + */ +#include +#include +#include +#include "usbd-debug.h" +static int gn (char *cp) +{ + /* Dumb little number scanner to avoid stdlib (atoi). */ + int val = 0; + char d; + while ('0' <= (d = *cp++) && d <= '9') { + val = val * 10 + (d - '0'); + } + return (val); +} + +static char *ss (char *str, char c) +{ + /* Dumb little string scanner to avoid stdlib (strchr). */ + char d; + do { + if (c == (d = *str)) { + return (str); + } + str += 1; + } while (0 != d); + return (NULL); +} + +static char *sc (char *dst, char *src) +{ + /* Dumb little string copier to avoid stdlib (~strcpy). */ + char c; + while (0 != (c = *src++)) { + *dst++ = c; + } + *dst = c; // Terminate destination str, but don't point past 0 + return (dst); +} + +static int se (char *s1, char *s2) +{ + /* Dumb little string comparer to avoid stdlib (strcmp). */ + char c1, c2; + do { + if ((c1 = *s1++) != (c2 = *s2++)) { + return (c1 - c2); + } + } while (0 != c1); + return (0); +} + +debug_option *find_debug_option (debug_option * options, char *opt2find) +{ + debug_option *op; + char *sl; + int r; + for (op = options; NULL != op && NULL != op->name; op++) { + if (NULL != op->sub_table) { + /* This option is the name of a sub-table, check + the prefix of opt2find (using ss, to avoid stdlib) */ + if (NULL != (sl = ss (opt2find, '/'))) { + *sl = 0; + } + r = se (opt2find, op->name); + if (NULL != sl) { + *sl = '/'; + } + if (0 == r) { + /* Got a match. */ + if (NULL == sl) { + /* Match was to name of entire table. */ + return (op); + } + return (find_debug_option (op->sub_table, sl + 1)); + } + } else if (0 == se (opt2find, op->name)) { + return (op); + } + } + return (NULL); +} + +static void print_options (debug_option * options, char *ps, char *pe) +{ + debug_option *op; + char *npe; + for (op = options; NULL != op && NULL != op->name; op++) { + if (NULL == op->sub_table) { + printk (KERN_ERR "%s%s - %s\n", ps, op->name, op->description); + + } else { + npe = sc (pe, op->name); + npe = sc (npe, "/"); + print_options (op->sub_table, ps, npe); + *pe = 0; + } + } +} + +static void print_all_options (char *caller_name, debug_option * options) +{ + char opt_prefix[100], *prefix_end; + prefix_end = sc (opt_prefix, caller_name); + prefix_end = sc (prefix_end, ": "); + print_options (options, opt_prefix, prefix_end); +} + +static void set_all_options (debug_option * options, int level) +{ + debug_option *op; + for (op = options; NULL != op && NULL != op->name; op++) { + if (NULL == op->sub_table) { + *(op->level) = level; + } else { + set_all_options (op->sub_table, level); + } + } +} + +static int set_debug_option (char *caller_name, debug_option * options, char *value) +{ + debug_option *op; + int level; + char *eq; + if (NULL != (eq = ss (value, '='))) { + /* There is an '='. */ + *eq = 0; + level = gn (eq + 1); + } else if (!('0' <= *value && *value <= '9')) { + /* name with no '=', default to level 1. */ + level = 1; + } else { + /* level with no name, set all options to level. */ + level = gn (value); + set_all_options (options, level); + return (0); + } + op = find_debug_option (options, value); + if (NULL != eq) { + *eq = '='; + } + if (NULL != op) { + /* Got a match. */ + if (NULL != op->level) { + *(op->level) = level; + } else { + /* Value matched an entire sub_table */ + set_all_options (op, level); + } + return (0); + } + /* Unknown debug option. */ + if (NULL == caller_name) { + /* Silently ignore it. */ + return (0); + } + printk (KERN_ERR "%s: unknown dbg option `%s', valid options are:\n", caller_name, value); + print_all_options (caller_name, options); + return (1); +} + +int scan_debug_options (char *caller_name, debug_option * options, char *values) +{ + char *cp; + int rc = 0; + if (NULL == values || NULL == options || NULL == caller_name) { + return (0); + } + if ('~' == *values) { + /* Ignore unknown options. */ + values += 1; + caller_name = NULL; + } + /* Pick apart a colon separated list of option=value. */ + while (NULL != values && 0 != *values) { + if (NULL != (cp = ss (values, ':'))) { + *cp = 0; + } + rc += set_debug_option (caller_name, options, values); + if (NULL != cp) { + *cp++ = ':'; + } + values = cp; + } + return (rc); +} + +static char to_hex[] = { "0123456789ABCDEF" }; + +void dbgPRINT_mem (u8 * mem, u32 len) +{ + /* Display a portion of memory. */ + u8 dbg_xbuff[52]; + u8 dbg_sbuff[52]; + u8 dbg_cbuff[20]; + u8 b, *end, *mark; + u8 *dp, *cp; + int byte_count; + end = (mark = mem) + len; + dp = dbg_xbuff; + cp = dbg_cbuff; + byte_count = 0; + while (mem < end) { + if (byte_count > 0) { + /* Space between bytes. */ + *dp++ = ' '; + if (!(0x3 & (unsigned) (void *) mem)) { + /* Add an extra space on 4-byte boundary. */ + *dp++ = ' '; + } + } + b = *mem++; + *dp++ = to_hex[b >> 4]; + *dp++ = to_hex[b & 15]; + *cp++ = (b < ' ' || '~' < b) ? '.' : b; + byte_count += 1; + if (byte_count >= 16) { + /* Dump this line. */ + *cp = *dp = 0; + PRINTK (" #%08x |%s| |%s|\n", (unsigned) (void *) mark, dbg_xbuff, dbg_cbuff); + byte_count = 0; + dp = dbg_xbuff; + cp = dbg_cbuff; + mark = mem; + } + } + if (byte_count > 0) { + /* Dump last line. */ + *cp = *dp = 0; + // Find number of spaces needed for vertical alignment + // Why am I wasting time on this? :) + memset (dbg_sbuff, ' ', sizeof (dbg_sbuff)); + dbg_sbuff[50 - (byte_count * 3 - 1 + ((byte_count - 1) / 4))] = 0; + PRINTK (" #%08x |%s|%s |%s|\n", (unsigned) (void *) mark, dbg_xbuff, dbg_sbuff, dbg_cbuff); + } +} diff -uNr linux.org/drivers/usb/device/usbd-debug.h linux/drivers/usb/device/usbd-debug.h --- linux.org/drivers/usb/device/usbd-debug.h Thu Jan 1 08:00:00 1970 +++ linux/drivers/usb/device/usbd-debug.h Thu May 15 15:54:48 2003 @@ -0,0 +1,112 @@ +/* + * linux/drivers/usbd/usbd-debug.h + * + * Copyright (c) 2000, 2001, 2002 Lineo + * Copyright (c) 2001 Hewlett Packard + * + * By: + * Stuart Lynne , + * Tom Rushworth , + * Bruce Balden + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + * + */ +#ifndef USBD_DEBUG_H +#define USBD_DEBUG_H 1 +/* + * Generic, multi-area, multi-level, runtime settable debug printing. + * It isn't the greatest by any means, but it's relatively simple + * and it does the job for code collections of < 20K lines. + */ + +#define DBG_ENABLE 1 +// #undef DBG_ENABLE + +typedef struct debug_option { + int *level; + struct debug_option *sub_table; // used instead of level if name starts with / + char *name; + char *description; +} debug_option; + +extern int scan_debug_options (char *caller_name, debug_option * options, char *values); + +extern debug_option *find_debug_option (debug_option * options, char *opt2find); + +#define KLEVEL KERN_INFO +#define PRINTK(fmt,args...) printk(KLEVEL fmt,##args) + +#if DBG_ENABLE + +/* Debug enabled, macros are real. */ + +#define dbgENTER(flg,lvl) \ + do { \ + if (flg >= lvl) { \ + PRINTK("Enter %s\n",__FUNCTION__); \ + } \ + } while (0) + +#define dbgLEAVE(flg,lvl) \ + do { \ + if (flg >= lvl) { \ + PRINTK("Leave %s\n",__FUNCTION__); \ + } \ + } while (0) + +extern void dbgPRINT_mem (u8 * mem, u32 len); +#define dbgPRINTmem(flg,lvl,addr,len) \ + do { \ + if (flg >= lvl) { \ + dbgPRINT_mem((u8*)(void*)(addr),len); \ + } \ + } while (0) + +// Since PRINTK adds KLEVEL, we might as well add the newline here, +// because continued lines will look a mess anyway. +#define dbgPRINT(flg,lvl,fmt,args...) \ + do { \ + if (flg >= lvl) { \ + PRINTK("%s: " fmt "\n" , __FUNCTION__ , ##args); \ + } \ + } while (0) + +#define dbgLEAVEF(flg,lvl,fmt,args...) \ + do { \ + if (flg >= lvl) { \ + PRINTK("Leave %s: " fmt "\n" ,__FUNCTION__ , ##args); \ + } \ + } while (0) + +#define DBGprint(fmt,args...) printk(KERN_INFO "%s: " fmt "\n",__FUNCTION__,##args) +#define DBGmark printk(KERN_INFO "%s: line %d\n",__FUNCTION__,__LINE__) +#define DBGmarks(str) printk(KERN_INFO "%s: %s\n",__FUNCTION__,str) + +#else + +/* Debug disabled, define the macros to be empty. This will leave + a bunch of single semi-colons in the post-processed source, but + the C language allows it, so compilers had better be able to + handle it. It's also another good reason for compulsory {}'s :). */ + +#define dbgENTER(flg,lvl) +#define dbgLEAVE(flg,lvl) +#define dbgPRINTmem(flg,lvl,addr,len) +#define dbgPRINT(flg,lvl,fmt,args...) +#define dbgLEAVEF(flg,lvl,fmt,args...) +#endif + +#endif diff -uNr linux.org/drivers/usb/device/usbd-export.h linux/drivers/usb/device/usbd-export.h --- linux.org/drivers/usb/device/usbd-export.h Thu Jan 1 08:00:00 1970 +++ linux/drivers/usb/device/usbd-export.h Thu May 15 15:54:48 2003 @@ -0,0 +1 @@ +#define USBD_EXPORT_DATE "2002-06-12 20:00" diff -uNr linux.org/drivers/usb/device/usbd-func.c linux/drivers/usb/device/usbd-func.c --- linux.org/drivers/usb/device/usbd-func.c Thu Jan 1 08:00:00 1970 +++ linux/drivers/usb/device/usbd-func.c Thu May 15 15:54:48 2003 @@ -0,0 +1,1098 @@ +/* + * linux/drivers/usbd/usbd-func.c - USB Function support + * + * Copyright (c) 2000, 2001, 2002 Lineo + * Copyright (c) 2001 Hewlett Packard + * + * By: + * Stuart Lynne , + * Tom Rushworth , + * Bruce Balden + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + * + */ + + +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "usbd.h" +#include "usbd-debug.h" +#include "usbd-func.h" +#include "usbd-bus.h" +#include "usbd-inline.h" + + +#define LANGID_ENGLISH "\011" +#define LANGID_US_ENGLISH "\004" + +//#define LANGIDs LANGID_US_ENGLISH LANGID_ENGLISH LANGID_US_ENGLISH LANGID_ENGLISH LANGID_US_ENGLISH LANGID_ENGLISH +#define LANGIDs LANGID_US_ENGLISH LANGID_ENGLISH + +//#define LANGIDs LANGID_ENGLISH LANGID_US_ENGLISH LANGID_US_ENGLISH + +extern int maxstrings; +extern struct usb_string_descriptor **usb_strings; +extern int usb_devices; +extern struct usb_function_driver ep0_driver; + +extern struct list_head function_drivers; +extern struct list_head devices; + +static __inline__ struct usb_function_driver *list_entry_func (const struct list_head *le) +{ + return list_entry (le, struct usb_function_driver, drivers); +} + +static __inline__ struct usb_device_instance *list_entry_device (const struct list_head *le) +{ + return list_entry (le, struct usb_device_instance, devices); +} + +void lkfree (void *p) +{ + if (p) { + kfree (p); + } +} + +int dbgflg_usbdfd_init; + +extern int registered_functions; +extern int registered_devices; + + +#define dbg_init(lvl,fmt,args...) dbgPRINT(dbgflg_usbdfd_init,lvl,fmt,##args) + + + +/* * + * usbd_alloc_string_zero - allocate a string descriptor and return index number + * @str: pointer to string + * + * Find an empty slot in index string array, create a corresponding descriptor + * and return the slot number. + */ +__init __u8 usbd_alloc_string_zero (char *str) +{ + struct usb_string_descriptor *string; + __u8 bLength; + __u16 *wData; + + dbg_init (1, "%02x %02x %02x %02x", str[0], str[1], str[2], str[3]); + + if (usb_strings[0] == NULL) { + + bLength = sizeof (struct usb_string_descriptor) + strlen (str); + dbg_init (1, "blength: %d strlen: %d", bLength, strlen (str)); + + if (!(string = ckmalloc (bLength, GFP_KERNEL))) { + return 0; + } + + string->bLength = bLength; + string->bDescriptorType = USB_DT_STRING; + + for (wData = string->wData; *str;) { + *wData = (__u16) ((str[0] << 8 | str[1])); + dbg_init (1, "%p %04x %04x", wData, *wData, (((__u16) str[0] << 8 | str[1]))); + str += 2; + wData++; + } + + // store in string index array + usb_strings[0] = string; + } + return 0; +} + +/* * + * usbd_alloc_string - allocate a string descriptor and return index number + * @str: pointer to string + * + * Find an empty slot in index string array, create a corresponding descriptor + * and return the slot number. + */ +__init __u8 usbd_alloc_string (char *str) +{ + int i; + struct usb_string_descriptor *string; + __u8 bLength; + __u16 *wData; + + //return 0; + + if (!str || !strlen (str)) { + return 0; + } + dbg_init (1, "%d: %s", maxstrings, str); + + // find an empty string descriptor slot + for (i = 1; i < maxstrings; i++) { + + if (usb_strings[i] == NULL) { + + + bLength = sizeof (struct usb_string_descriptor) + 2 * strlen (str); + + dbg_init (1, "%s -> %d strlen: %d bLength: %d", str, i, strlen (str), bLength); + + if (!(string = ckmalloc (bLength, GFP_KERNEL))) { + return 0; + } + + string->bLength = bLength; + string->bDescriptorType = USB_DT_STRING; + + for (wData = string->wData; *str;) { + *wData++ = (__u16) (*str++); + } + + // store in string index array + usb_strings[i] = string; + return i; + } +#if 1 + else { + char *cp = str; + int j; + for (j = 0; (j < ((usb_strings[i]->bLength - 1) / 2)) && ((usb_strings[i]->wData[j] & 0xff) == cp[j]); j++) { + + dbg_init (4, "checking for duplicate %d:%d: strlen: %d bLength: %d bLength-1/2: %d wData[%04x] cp[%02x]", + i, j, strlen (str), usb_strings[i]->bLength, (usb_strings[i]->bLength - 1) / 2, (usb_strings[i]->wData[j]), cp[j] + ); + } + dbg_init (1, "finished %d:%d: strlen: %d bLength: %d bLength-1/2: %d wData[%04x] cp[%02x]", + i, j, strlen (str), usb_strings[i]->bLength, (usb_strings[i]->bLength - 1) / 2, (usb_strings[i]->wData[j]), cp[j] + ); + + + if ((j >= ((usb_strings[i]->bLength - 1) / 2)) /*&& ((usb_strings[i]->wData[j]&0xff) == cp[j]) */ ) { + dbg_init (1, "%s -> %d (duplicate)", str, i); + return i; + } + } +#endif + } + return 0; +} + + +/* * + * usbd_dealloc_string - deallocate a string descriptor + * @index: index into strings array to deallocte + * + * Find and remove an allocated string. + */ +void __exit usbd_dealloc_string (__u8 index) +{ + struct usb_string_descriptor *string; + + dbg_init (1, "%d", index); + + if ((index > 0) && (index < maxstrings) && (string = usb_strings[index])) { + usb_strings[index] = NULL; + dbg_init (1, "%p", string); + lkfree (string); + } +} + +__u8 device_descriptor_sizes[] = { + 0, + sizeof (struct usb_device_descriptor), + sizeof (struct usb_configuration_descriptor), + sizeof (struct usb_string_descriptor), + sizeof (struct usb_interface_descriptor), + sizeof (struct usb_endpoint_descriptor) +}; + +__u8 class_descriptor_sizes[] = { + sizeof (struct usb_class_header_function_descriptor), // 0x0 + sizeof (struct usb_class_call_management_descriptor), // 0x1 + sizeof (struct usb_class_abstract_control_descriptor), // 0x2 + sizeof (struct usb_class_direct_line_descriptor), // 0x3 + sizeof (struct usb_class_telephone_ringer_descriptor), // 0x4 + sizeof (struct usb_class_telephone_operational_descriptor), // 0x5 + sizeof (struct usb_class_union_function_descriptor), // 0x6 + sizeof (struct usb_class_country_selection_descriptor), // 0x7 + sizeof (struct usb_class_telephone_operational_descriptor), // 0x8 + sizeof (struct usb_class_usb_terminal_descriptor), // 0x9 + sizeof (struct usb_class_network_channel_descriptor), // 0xa + sizeof (struct usb_class_protocol_unit_function_descriptor), // 0xb + sizeof (struct usb_class_extension_unit_descriptor), // 0xc + sizeof (struct usb_class_multi_channel_descriptor), // 0xd + sizeof (struct usb_class_capi_control_descriptor), // 0xe + sizeof (struct usb_class_ethernet_networking_descriptor), // 0xf + sizeof (struct usb_class_atm_networking_descriptor), // 0x10 + 0, // 0x11 + sizeof (struct usb_class_mdlm_descriptor), // 0x12 + sizeof (struct usb_class_mdlmd_descriptor), // 0x13 + +}; + + +/* * + * usbd_alloc_descriptor - allocate a usb descriptor + * bDescriptorType + * + * Allocate and initialize a generic usb descriptor. + */ +__init static void *usbd_alloc_descriptor (__u8 bDescriptorType, __u8 bDescriptorSubtype, __u8 elements) +{ + struct usb_descriptor *descriptor; + int length; + + + dbg_init (1, "type: %d subtype: %d elements: %d", bDescriptorType, bDescriptorSubtype, elements); + + switch (bDescriptorType) { + case USB_DT_DEVICE: + case USB_DT_CONFIG: + case USB_DT_INTERFACE: + case USB_DT_ENDPOINT: + length = device_descriptor_sizes[bDescriptorType]; + break; + + case CS_INTERFACE: + switch (bDescriptorSubtype) { + + case USB_ST_HEADER: + case USB_ST_CMF: + case USB_ST_ACMF: + case USB_ST_DLMF: + case USB_ST_TRF: + case USB_ST_TCLF: + + case USB_ST_NCT: + case USB_ST_MCMF: + case USB_ST_CCMF: + case USB_ST_ENF: + case USB_ST_ATMNF: + case USB_ST_MDLM: + length = class_descriptor_sizes[bDescriptorSubtype]; + break; + + case USB_ST_UF: + case USB_ST_CSF: + case USB_ST_TOMF: + case USB_ST_USBTF: + case USB_ST_PUF: + case USB_ST_EUF: + case USB_ST_MDLMD: + length = class_descriptor_sizes[bDescriptorSubtype] + elements; // XXX check me + break; + + default: + return NULL; + + } + break; + + case USB_DT_STRING: + default: + return NULL; + } + + if (!(descriptor = ckmalloc (length, GFP_KERNEL))) { + return NULL; + } + + switch (bDescriptorType) { + case CS_INTERFACE: + descriptor->descriptor.generic.bDescriptorSubtype = bDescriptorSubtype; + + case USB_DT_DEVICE: + case USB_DT_CONFIG: + case USB_DT_INTERFACE: + case USB_DT_ENDPOINT: + descriptor->descriptor.generic.bLength = length; + descriptor->descriptor.generic.bDescriptorType = bDescriptorType; + break; + + + case USB_DT_STRING: + break; + } + + dbg_init (1, "descriptor: %p length: %d", descriptor, length); + + return descriptor; +} + + +/* * + * usbd_dealloc_descriptor - deallocate a usb descriptor + * @descriptor: pointer to usb descriptor to deallocate + * + * Deallocate a descriptor, first deallocate the index string descriptors. + */ +static __exit void usbd_dealloc_descriptor (struct usb_descriptor *descriptor) +{ + dbg_init (2, "descriptor: %p type: %d", descriptor, descriptor->descriptor.generic.bDescriptorType); + if (descriptor) { + switch (descriptor->descriptor.generic.bDescriptorType) { + case USB_DT_DEVICE: + dbg_init (2, "USB_DT_DEVICE"); + usbd_dealloc_string (descriptor->descriptor.device.iManufacturer); + usbd_dealloc_string (descriptor->descriptor.device.iProduct); + usbd_dealloc_string (descriptor->descriptor.device.iManufacturer); + break; + case USB_DT_CONFIG: + dbg_init (2, "USB_DT_CONFIG"); + usbd_dealloc_string (descriptor->descriptor.configuration.iConfiguration); + break; + case USB_DT_INTERFACE: + dbg_init (2, "USB_DT_INTERFACE"); + usbd_dealloc_string (descriptor->descriptor.interface.iInterface); + break; + case CS_INTERFACE: + dbg_init (2, "class descriptor: %p type: %d subtype: %d", descriptor, descriptor->descriptor.generic.bDescriptorType, descriptor->descriptor.generic.bDescriptorSubtype); + dbg_init (2, "USB_CS_INTERFACE"); + { + struct usb_class_descriptor *class_descriptor = (struct usb_class_descriptor *) descriptor; + switch (descriptor->descriptor.generic.bDescriptorSubtype) { + + case USB_ST_HEADER: + case USB_ST_CMF: + case USB_ST_ACMF: + case USB_ST_DLMF: + case USB_ST_TRF: + case USB_ST_TCLF: + case USB_ST_UF: + case USB_ST_CSF: + case USB_ST_TOMF: + case USB_ST_USBTF: + case USB_ST_NCT: + case USB_ST_PUF: + case USB_ST_EUF: + case USB_ST_MCMF: + case USB_ST_CCMF: + break; + case USB_ST_ENF: + dbg_init (2, "USB_ST_ENF"); + usbd_dealloc_string (class_descriptor->descriptor.ethernet_networking.iMACAddress); + break; + case USB_ST_ATMNF: + break; + } + } + } + lkfree (descriptor); + } +} + +/* usb-device USB FUNCTION generic functions ************************************************* */ + + +/* * +* alloc_function_classes - allocate class descriptor array +* @classes: number of classes +* @class_description_array: pointer to an array of class descriptions +* + * Return a pointer to an array of pointers to class descriptors. The descriptors + * will be filled in with information from the class description array. + * + * Returning NULL will cause the caller to cleanup all previously allocated memory. + */ +__init static struct usb_class_descriptor **alloc_function_classes (int classes, struct usb_class_description *class_description_array) +{ + int class; + struct usb_class_descriptor **classes_descriptor_array; + + dbg_init (1, "classes: %d", classes); + + if (!classes || !class_description_array) { + return NULL; + } + // allocate the class descriptor array + if (!(classes_descriptor_array = ckmalloc (sizeof (struct usb_class_descriptor *) * classes, GFP_KERNEL))) { + return NULL; + } + for (class = 0; class < classes; class++) { + struct usb_class_description *class_description = class_description_array + class; + struct usb_class_descriptor *class_descriptor; + //int elements; + + dbg_init (1, "class: %d Subtype: %d elements: %d", class, class_description->bDescriptorSubtype, class_description->elements); + + // allocate an class descriptor to use + if (!(class_descriptor = usbd_alloc_descriptor (CS_INTERFACE, class_description->bDescriptorSubtype, class_description->elements))) { + dbg_init (0, "usbd_alloc_descriptor failed"); + return NULL; + } + switch (class_description->bDescriptorSubtype) { + + case USB_ST_HEADER: + class_descriptor->descriptor.header_function.bcdCDC = cpu_to_le16 (CLASS_BCD_VERSION); + break; + case USB_ST_CMF: + case USB_ST_ACMF: + class_descriptor->descriptor.abstract_control.bmCapabilities = class_description->description.abstract_control.bmCapabilities; + break; + case USB_ST_DLMF: + case USB_ST_TRF: + case USB_ST_TCLF: + // XXX generalize USB_ST_UF here + dbg_init (0, "NOT IMPLEMENTED"); + break; + + case USB_ST_UF: + + dbg_init (1, "USB_ST_UF: bMasterInterface: %02x", class_description->description.union_function.bMasterInterface); + dbg_init (1, "USB_ST_UF: bSlaveInterface[0]: %02x", class_description->description.union_function.bSlaveInterface[0]); + + class_descriptor->descriptor.union_function.bMasterInterface = class_description->description.union_function.bMasterInterface; + + class_descriptor->descriptor.union_function.bSlaveInterface0[0] = class_description->description.union_function.bSlaveInterface[0]; +#if 0 + for (elements = 0; elements < class_description->elements; elements++) { + + dbg_init (1, "USB_ST_UF: copying bSlaveInterface[%d] %02x", elements, class_description->description.union_function.bSlaveInterface[elements]); + + class_descriptor->descriptor.union_function.bSlaveInterface0[elements] = class_description->description.union_function.bSlaveInterface[elements]; + } +#endif + break; + case USB_ST_CSF: + case USB_ST_TOMF: + case USB_ST_USBTF: + case USB_ST_NCT: + case USB_ST_PUF: + case USB_ST_EUF: + case USB_ST_MCMF: + case USB_ST_CCMF: + break; + case USB_ST_ENF: + class_descriptor->descriptor.ethernet_networking.bmEthernetStatistics = class_description->description.ethernet_networking.bmEthernetStatistics; + + class_descriptor->descriptor.ethernet_networking.iMACAddress = usbd_alloc_string (class_description->description.ethernet_networking.iMACAddress); + + class_descriptor->descriptor.ethernet_networking.wMaxSegmentSize = cpu_to_le16 (class_description->description.ethernet_networking.wMaxSegmentSize); + + class_descriptor->descriptor.ethernet_networking.wNumberMCFilters = cpu_to_le16 (class_description->description.ethernet_networking.wNumberMCFilters); + + class_descriptor->descriptor.ethernet_networking.bNumberPowerFilters = class_description->description.ethernet_networking.bNumberPowerFilters; + + break; + + case USB_ST_ATMNF: + break; + + case USB_ST_MDLM: + dbg_init (1, "USB_ST_MDLM:"); + class_descriptor->descriptor.mobile_direct.bcdVersion = class_description->description.mobile_direct.bcdVersion; + + memcpy (class_descriptor->descriptor.mobile_direct.bGUID, class_description->description.mobile_direct.bGUID, sizeof (class_descriptor->descriptor.mobile_direct.bGUID)); + + break; + + case USB_ST_MDLMD: + + dbg_init (1, "USB_ST_MDLMD: sizeof descriptor: %d %d length: %d elements: %d", + sizeof (struct usb_class_mdlmd_descriptor), + class_descriptor_sizes[USB_ST_MDLMD], class_descriptor->descriptor.mobile_direct_detail.bFunctionLength, class_description->elements); + + { + unsigned char *cp; + cp = (unsigned char *) class_descriptor; + dbg_init (1, "descriptor: %02x %02x %02x %02x %02x %02x", cp[0], cp[1], cp[2], cp[3], cp[4], cp[5]); + } + class_descriptor->descriptor.mobile_direct_detail.bGuidDescriptorType = class_description->description.mobile_direct_detail.bGuidDescriptorType; + + //memcpy(class_descriptor->descriptor.mobile_direct_detail.bDetailData, + // class_description->description.mobile_direct_detail.bDetailData, + // class_description->elements + // ); + + class_descriptor->descriptor.mobile_direct_detail.bDetailData[0] = class_description->description.mobile_direct_detail.bDetailData[0]; + + class_descriptor->descriptor.mobile_direct_detail.bDetailData[1] = class_description->description.mobile_direct_detail.bDetailData[1]; + + dbg_init (1, "USB_ST_MDLMD: detaildata[0] %02x %02x", + class_descriptor->descriptor.mobile_direct_detail.bDetailData[0], class_description->description.mobile_direct_detail.bDetailData[0] + ); + + dbg_init (1, "USB_ST_MDLMD: detaildata[1] %02x %02x", + class_descriptor->descriptor.mobile_direct_detail.bDetailData[1], class_description->description.mobile_direct_detail.bDetailData[1] + ); + + { + unsigned char *cp; + cp = (unsigned char *) class_descriptor; + dbg_init (1, "descriptor: %02x %02x %02x %02x %02x %02x", cp[0], cp[1], cp[2], cp[3], cp[4], cp[5]); + } + break; + } + + // save in class descriptor array + classes_descriptor_array[class] = class_descriptor; + } + return classes_descriptor_array; +} + + +/* * + * alloc_function_endpoints - allocate endpoint descriptor array + * @endpoints: number of endpoints + * @endpoint_description_array: pointer to an array of endpoint descriptions + * + * Return a pointer to an array of pointers to endpoint descriptors. The descriptors + * will be filled in with information from the endpoint description array. + * + * Returning NULL will cause the caller to cleanup all previously allocated memory. + */ +__init static struct usb_endpoint_descriptor **alloc_function_endpoints (int endpoints, struct usb_endpoint_description *endpoint_description_array) +{ + int i; + struct usb_endpoint_descriptor **endpoint_descriptor_array; + + + if (!endpoints || !endpoint_description_array) { + return NULL; + } + // allocate the endpoint descriptor array + if (!(endpoint_descriptor_array = ckmalloc (sizeof (struct usb_endpoint_descriptor *) * endpoints, GFP_KERNEL))) { + return NULL; + } + for (i = 0; i < endpoints; i++) { + struct usb_endpoint_description *endpoint_description = endpoint_description_array + i; + struct usb_endpoint_descriptor *endpoint_descriptor; + + dbg_init (1, "endpoint: %d:%d", i, endpoints); + + // allocate an endpoint descriptor to use + if (!(endpoint_descriptor = usbd_alloc_descriptor (USB_DT_ENDPOINT, 0, 0))) { + return NULL; + } + endpoint_descriptor->bEndpointAddress = endpoint_description->bEndpointAddress | endpoint_description->direction; + endpoint_descriptor->bmAttributes = endpoint_description->bmAttributes; + // XXX cpu_to_le16()? + endpoint_descriptor->wMaxPacketSize = endpoint_description->wMaxPacketSize; + endpoint_descriptor->bInterval = endpoint_description->bInterval; + + // save in endpoint descriptor array + endpoint_descriptor_array[i] = endpoint_descriptor; + } + return endpoint_descriptor_array; +} + + +/* * + * alloc_endpoint_transfer - allocate endpoint transfer array + * @endpoints: number of endpoints + * @endpoint_description_array: pointer to an array of endpoint descriptions + * + * Return a pointer to an array of transfers sizes for each endpointi. + * + * Returning NULL will cause the caller to cleanup all previously allocated memory. + */ +__init static int *alloc_endpoint_transfer (int endpoints, struct usb_endpoint_description *endpoint_description_array) +{ + int i; + int *endpoint_transfersize; + + if (!endpoints || !endpoint_description_array) { + return NULL; + } + // allocate the endpoint descriptor array + if (!(endpoint_transfersize = ckmalloc (sizeof (int) * endpoints, GFP_KERNEL))) { + return NULL; + } + for (i = 0; i < endpoints; i++) { + struct usb_endpoint_description *endpoint_description = endpoint_description_array + i; + dbg_init (1, "endpoint: %d:%d", i, endpoints); + endpoint_transfersize[i] = endpoint_description->transferSize; + } + return endpoint_transfersize; +} + +#if 0 +/* * + * alloc_function_interface - allocate alternate instance array + * + * Return a pointer to an array of alternate instances. Each instance contains a pointer + * to a filled in alternate descriptor and a pointer to the endpoint descriptor array. + * + * Returning NULL will cause the caller to cleanup all previously allocated memory. + */ +__init static struct usb_alternate_instance *alloc_function_interface (int bInterfaceNumber, struct usb_interface_description *interface_description) +{ + struct usb_alternate_instance *alternate_instance; + struct usb_interface_descriptor *interface_descriptor; + + dbg_init (1, "interface: %d", bInterfaceNumber); + + // allocate array of alternate instances + if (!(alternate_instance = ckmalloc (sizeof (struct usb_alternate_instance), GFP_KERNEL))) { + dbg_init (0, "ckmalloc failed"); + return NULL; + } + // allocate a descriptor for this interface + if (!(interface_descriptor = usbd_alloc_descriptor (USB_DT_INTERFACE, 0, 0))) { + dbg_init (0, "usbd_alloc_descriptor failed"); + return NULL; + } + + interface_descriptor->bInterfaceNumber = bInterfaceNumber + 1; // XXX + interface_descriptor->bInterfaceClass = interface_description->bInterfaceClass; + + interface_descriptor->bInterfaceSubClass = interface_description->bInterfaceSubClass; + interface_descriptor->bInterfaceProtocol = interface_description->bInterfaceProtocol; + + interface_descriptor->bAlternateSetting = 0; + interface_descriptor->iInterface = usbd_alloc_string (interface_description->iInterface); + + // XXX it should be possible to collapse the next two functions into one. + alternate_instance->endpoints_descriptor_array = NULL; + alternate_instance->endpoint_transfersize_array = NULL; + + // save number of alternates, classes and endpoints for this alternate + alternate_instance->endpoints = 0; + alternate_instance->interface_descriptor = interface_descriptor; + return alternate_instance; +} +#endif + +/* * + * alloc_function_alternates - allocate alternate instance array + * @alternates: number of alternates + * @alternate_description_array: pointer to an array of alternate descriptions + * + * Return a pointer to an array of alternate instances. Each instance contains a pointer + * to a filled in alternate descriptor and a pointer to the endpoint descriptor array. + * + * Returning NULL will cause the caller to cleanup all previously allocated memory. + */ +__init static struct usb_alternate_instance *alloc_function_alternates (int bInterfaceNumber, + struct usb_interface_description *interface_description, + int alternates, struct usb_alternate_description *alternate_description_array) +{ + int i; + struct usb_alternate_instance *alternate_instance_array; + + dbg_init (1, "bInterfaceNumber: %d, alternates: %d", bInterfaceNumber, alternates); + + // allocate array of alternate instances + if (!(alternate_instance_array = ckmalloc (sizeof (struct usb_alternate_instance) * alternates, GFP_KERNEL))) { + return NULL; + } + // iterate across the alternate descriptions + for (i = 0; i < alternates; i++) { + + struct usb_alternate_description *alternate_description = alternate_description_array + i; + struct usb_alternate_instance *alternate_instance = alternate_instance_array + i; + struct usb_interface_descriptor *interface_descriptor; + + dbg_init (1, "alternate: %d:%d", i, alternates); + + // allocate a descriptor for this interface + if (!(interface_descriptor = usbd_alloc_descriptor (USB_DT_INTERFACE, 0, 0))) { + return NULL; + } + + interface_descriptor->bInterfaceNumber = bInterfaceNumber; // XXX + interface_descriptor->bInterfaceClass = interface_description->bInterfaceClass; + + interface_descriptor->bInterfaceSubClass = interface_description->bInterfaceSubClass; + interface_descriptor->bInterfaceProtocol = interface_description->bInterfaceProtocol; + + interface_descriptor->bAlternateSetting = alternate_description->bAlternateSetting; + interface_descriptor->bNumEndpoints = alternate_description->endpoints; + interface_descriptor->iInterface = usbd_alloc_string (alternate_description->iInterface ? alternate_description->iInterface : interface_description->iInterface); + + // XXX Classes + + alternate_instance->classes_descriptor_array = alloc_function_classes (alternate_description->classes, alternate_description->class_list); + + alternate_instance->classes = alternate_description->classes; + + // XXX it should be possible to collapse the next two functions into one. + alternate_instance->endpoints_descriptor_array = alloc_function_endpoints (alternate_description->endpoints, alternate_description->endpoint_list); + + alternate_instance->endpoint_transfersize_array = alloc_endpoint_transfer (alternate_description->endpoints, alternate_description->endpoint_list); + + + // save number of alternates, classes and endpoints for this alternate + alternate_instance->endpoints = alternate_description->endpoints; + alternate_instance->interface_descriptor = interface_descriptor; + } + return alternate_instance_array; +} + +/* * + * alloc_function_interfaces - allocate interface instance array + * @interfaces: number of interfaces + * @interface_description_array: pointer to an array of interface descriptions + * + * Return a pointer to an array of interface instances. Each instance contains a pointer + * to a filled in interface descriptor and a pointer to the endpoint descriptor array. + * + * Returning NULL will cause the caller to cleanup all previously allocated memory. + */ +__init static struct usb_interface_instance *alloc_function_interfaces (int interfaces, struct usb_interface_description *interface_description_array) +{ + int interface; + struct usb_interface_instance *interface_instance_array; + + // allocate array of interface instances + if (!(interface_instance_array = ckmalloc (sizeof (struct usb_interface_instance) * interfaces, GFP_KERNEL))) { + return NULL; + } + // iterate across the interface descriptions + for (interface = 0; interface < interfaces; interface++) { + + struct usb_interface_description *interface_description = interface_description_array + interface; + struct usb_interface_instance *interface_instance = interface_instance_array + interface; + + dbg_init (1, "_"); + dbg_init (1, "interface: %d:%d alternates: %d", interface, interfaces, interface_description->alternates); + + // save the interface description in the interface instance array + //interface_instance_array[interface].interface_descriptor = interface_descriptor; + + interface_instance->alternates_instance_array = alloc_function_alternates (interface, interface_description, interface_description->alternates, interface_description->alternate_list); + interface_instance->alternates = interface_description->alternates; + + } + return interface_instance_array; +} + + +/* * + * alloc_function_configurations - allocate configuration instance array + * @configurations: number of configurations + * @configuration_description_array: pointer to an array of configuration descriptions + * + * Return a pointer to an array of configuration instances. Each instance contains a pointer + * to a filled in configuration descriptor and a pointer to the interface instances array. + * + * Returning NULL will cause the caller to cleanup all previously allocated memory. + */ +__init static struct usb_configuration_instance *alloc_function_configurations (int configurations, struct usb_configuration_description *configuration_description_array) +{ + int i; + struct usb_configuration_instance *configuration_instance_array; + + + // allocate array + if (!(configuration_instance_array = ckmalloc (sizeof (struct usb_configuration_instance) * configurations, GFP_KERNEL))) { + return NULL; + } + // fill in array + for (i = 0; i < configurations; i++) { + int j; + int length; + + struct usb_configuration_description *configuration_description = configuration_description_array + i; + struct usb_configuration_descriptor *configuration_descriptor; + + dbg_init (1, "configurations: %d:%d", i, configurations); + + // allocate a descriptor + if (!(configuration_descriptor = usbd_alloc_descriptor (USB_DT_CONFIG, 0, 0))) { + return NULL; + } + // setup fields in configuration descriptor + // XXX c.f. 9.4.7 zero is default, so config MUST BE 1 to n, not 0 to n-1 + // XXX N.B. the configuration itself is fetched 0 to n-1. + // + configuration_descriptor->bConfigurationValue = i + 1; + configuration_descriptor->wTotalLength = 0; + configuration_descriptor->bNumInterfaces = configuration_description->interfaces; + configuration_descriptor->iConfiguration = usbd_alloc_string (configuration_description->iConfiguration); + configuration_descriptor->bmAttributes = configuration_description->bmAttributes; + configuration_descriptor->bMaxPower = configuration_description->bMaxPower; + + // save the configuration descriptor in the configuration instance array + configuration_instance_array[i].configuration_descriptor = configuration_descriptor; + + if (!(configuration_instance_array[i].interface_instance_array = alloc_function_interfaces (configuration_description->interfaces, configuration_description->interface_list)) + ) { + // XXX + return NULL; + } + + for (length = 0, j = 0; j < configuration_descriptor->bNumInterfaces; j++) { + int alternate; + struct usb_interface_instance *interface_instance = configuration_instance_array[i].interface_instance_array + j; + + + for (alternate = 0; alternate < interface_instance->alternates; alternate++) { + int class; + struct usb_alternate_instance *alternate_instance = interface_instance->alternates_instance_array + alternate; + + for (class = 0; class < alternate_instance->classes; class++) { + struct usb_class_descriptor *class_descriptor = alternate_instance->classes_descriptor_array[class]; + + length += class_descriptor->descriptor.generic.bFunctionLength; + } + + length += sizeof (struct usb_interface_descriptor) + alternate_instance->endpoints * sizeof (struct usb_endpoint_descriptor); + } + } + configuration_descriptor->wTotalLength = cpu_to_le16 (sizeof (struct usb_configuration_descriptor) + length); + + dbg_init (1, "-------> [%d] %d %d %d length: %d", i, + configuration_descriptor->bNumInterfaces, configuration_descriptor->bConfigurationValue, configuration_descriptor->iConfiguration, length); + configuration_instance_array[i].interfaces = configuration_description->interfaces; + + } + return configuration_instance_array; +} + + +/* * + * usbd_dealloc_function - deallocate a function driver + * @function_driver: pointer to a function driver structure + * + * Find and free all descriptor structures associated with a function structure. + */ +static void __exit usbd_dealloc_function (struct usb_function_driver *function_driver) +{ + int configuration; + struct usb_configuration_instance *configuration_instance_array = function_driver->configuration_instance_array; + + dbg_init (1, "%s", function_driver->name); + + // iterate across the descriptors list and de-allocate all structures + if (function_driver->configuration_instance_array) { + for (configuration = 0; configuration < function_driver->configurations; configuration++) { + int interface; + struct usb_configuration_instance *configuration_instance = configuration_instance_array + configuration; + + dbg_init (1, "%s configuration: %d", function_driver->name, configuration); + + for (interface = 0; interface < configuration_instance->interfaces; interface++) { + + int alternate; + struct usb_interface_instance *interface_instance = configuration_instance->interface_instance_array + interface; + + dbg_init (1, "%s configuration: %d interface: %d", function_driver->name, configuration, interface); + + for (alternate = 0; alternate < interface_instance->alternates; alternate++) { + int class; + int endpoint; + struct usb_alternate_instance *alternate_instance = interface_instance->alternates_instance_array + alternate; + + dbg_init (1, "%s configuration: %d interface: %d alternate: %d", function_driver->name, configuration, interface, alternate); + + for (class = 0; class < alternate_instance->classes; class++) { + + dbg_init (1, "%s configuration: %d interface: %d alternate: %d class: %d", function_driver->name, configuration, interface, alternate, class); + + usbd_dealloc_descriptor ((struct usb_descriptor *) + alternate_instance->classes_descriptor_array[class]); + } + for (endpoint = 0; endpoint < alternate_instance->endpoints; endpoint++) { + + dbg_init (1, "%s configuration: %d interface: %d alternate: %d endpoint: %d", function_driver->name, configuration, interface, alternate, endpoint); + + usbd_dealloc_descriptor ((struct usb_descriptor *) + alternate_instance->endpoints_descriptor_array[endpoint]); + } + lkfree (alternate_instance->endpoints_descriptor_array); + lkfree (alternate_instance->endpoint_transfersize_array); + lkfree (alternate_instance->classes_descriptor_array); + usbd_dealloc_descriptor ((struct usb_descriptor *) alternate_instance->interface_descriptor); + } + + lkfree (interface_instance->alternates_instance_array); + } + + lkfree (configuration_instance->interface_instance_array); + usbd_dealloc_descriptor ((struct usb_descriptor *) + configuration_instance_array[configuration].configuration_descriptor); + } + } + usbd_dealloc_descriptor ((struct usb_descriptor *) function_driver->device_descriptor); + lkfree (configuration_instance_array); +} + +__init static int usbd_strings_init (void) +{ + if (maxstrings > 254) { + dbg_init (1, "setting maxstrings to maximum value of 254"); + maxstrings = 254; + } + if (!(usb_strings = ckmalloc (sizeof (struct usb_string_descriptor *) * maxstrings, GFP_KERNEL))) { + return -1; + } + if (usbd_alloc_string_zero (LANGIDs) != 0) { + lkfree (usb_strings); + return -1; + } + return 0; +} + + + +/* * + * usbd_register_function - register a usb function driver + * @function_driver: pointer to function driver structure + * + * Used by a USB Function driver to register itself with the usb device layer. + * + * It will create a usb_function_instance structure. + * + * The user friendly configuration/interface/endpoint descriptions are compiled into + * the equivalent ready to use descriptor records. + * + * All function drivers must register before any bus interface drivers. + * + */ +__init int usbd_register_function (struct usb_function_driver *function_driver) +{ + //struct usb_device_descriptor *device_descriptor; + + /* Create a function driver structure, copy the configuration, + * interface and endpoint descriptions into descriptors, add to the + * function drivers list. + */ + + dbg_init (1, "--"); + + if (registered_devices) { + dbg_init (1, "cannot register after device created: %s", function_driver->name); + return -EINVAL; + } + // initialize the strings pool + if (usbd_strings_init ()) { + dbg_init (0, "usbd_strings_init failed"); + return -EINVAL; + } + + + dbg_init (9, "USE_COUNT %d", GET_USE_COUNT (THIS_MODULE)); + list_add_tail (&function_driver->drivers, &function_drivers); + registered_functions++; + dbg_init (1, "registered_functions: %d", registered_functions); + dbg_init (1, "--------------"); + + return 0; +} + + +/* * + * usbd_deregister_function - called by a USB FUNCTION driver to deregister itself + * @function_driver: pointer to struct usb_function_driver + * + * Called by a USB Function driver De-register a usb function driver. + */ +void __exit usbd_deregister_function (struct usb_function_driver *function_driver) +{ + dbg_init (1, "%s", function_driver->name); + + list_del (&function_driver->drivers); + registered_functions--; + + //usbd_dealloc_function(function_driver); + + dbg_init (9, "USE_COUNT %d", GET_USE_COUNT (THIS_MODULE)); +} + + + + +void usbd_function_init (struct usb_bus_instance *bus, struct usb_device_instance *device, struct usb_function_driver *function_driver) +{ + struct usb_device_descriptor *device_descriptor; + + dbg_init (1, "-"); + + if (function_driver->ops->function_init) { + function_driver->ops->function_init (bus, device, function_driver); + } + + if (!(device_descriptor = usbd_alloc_descriptor (USB_DT_DEVICE, 0, 0))) { + return; + } + + device_descriptor->bcdUSB = USB_BCD_VERSION; + device_descriptor->bDeviceClass = function_driver->device_description->bDeviceClass; + device_descriptor->bDeviceSubClass = function_driver->device_description->bDeviceSubClass; + device_descriptor->bDeviceProtocol = function_driver->device_description->bDeviceProtocol; + + //device_descriptor->bMaxPacketSize0 = 0; // this will be set in ep0 + // XXX test that this works, should be able to remove from ep0 + device_descriptor->bMaxPacketSize0 = bus->driver->maxpacketsize; + + device_descriptor->idVendor = function_driver->device_description->idVendor; + device_descriptor->idProduct = function_driver->device_description->idProduct; + + device_descriptor->bcdDevice = 0; // XXX + + device_descriptor->iManufacturer = usbd_alloc_string (function_driver->device_description->iManufacturer); + device_descriptor->iProduct = usbd_alloc_string (function_driver->device_description->iProduct); + + dbg_init (1, "* * * *"); +#ifdef CONFIG_USBD_SERIAL_NUMBER_STR + dbg_init (1, "SERIAL_NUMBER"); + if (bus->serial_number_str && strlen (bus->serial_number_str)) { + dbg_init (0, "bus->serial_number_str: %s", bus->serial_number_str); + device_descriptor->iSerialNumber = usbd_alloc_string (bus->serial_number_str); + } else { + dbg_init (0, "function_driver->device_description->iSerialNumber: %s", function_driver->device_description->iSerialNumber); + device_descriptor->iSerialNumber = usbd_alloc_string (function_driver->device_description->iSerialNumber); + } + dbg_init (1, "device_descriptor->iSerialNumber: %d", device_descriptor->iSerialNumber); +#else + // XXX should not need to do anything here +#endif + device_descriptor->bNumConfigurations = function_driver->configurations; + + dbg_init (3, "configurations: %d", device_descriptor->bNumConfigurations); + + + + // allocate the configuration descriptor array + if (!(function_driver->configuration_instance_array = alloc_function_configurations (function_driver->configurations, function_driver->configuration_description))) { + dbg_init (1, "failed during function configuration %s", function_driver->name); + usbd_dealloc_descriptor ((struct usb_descriptor *) device_descriptor); + return; + } + + function_driver->device_descriptor = device_descriptor; + + dbg_init (1, "- finished"); +} + +void usbd_function_close (struct usb_device_instance *device) +{ + struct usb_function_instance *function; + + if ((function = usbd_device_function_instance (device, 0))) { + if (function->function_driver->ops->function_exit) { + function->function_driver->ops->function_exit (device); + } + } + usbd_dealloc_function (function->function_driver); +} diff -uNr linux.org/drivers/usb/device/usbd-func.h linux/drivers/usb/device/usbd-func.h --- linux.org/drivers/usb/device/usbd-func.h Thu Jan 1 08:00:00 1970 +++ linux/drivers/usb/device/usbd-func.h Thu May 15 15:54:48 2003 @@ -0,0 +1,713 @@ +/* + * linux/drivers/usbd/usb-function.h - USB Function + * + * Copyright (c) 2000, 2001, 2002 Lineo + * Copyright (c) 2001 Hewlett Packard + * + * By: + * Stuart Lynne , + * Tom Rushworth , + * Bruce Balden + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + * + */ + +/* + * USB Function Driver structures + * + * Descriptors: + * struct usb_endpoint_description + * struct usb_interface_description + * struct usb_configuration_description + * + * Driver description: + * struct usb_function_driver + * struct usb_function_operations + * + */ + + +/* USB Descriptors - Create a complete description of all of the + * function driver capabilities. These map directly to the USB descriptors. + * + * This heirarchy is created by the functions drivers and is passed to the + * usb-device driver when the function driver is registered. + * + * device + * configuration + * interface + * alternate + * class + * class + * alternate + * endpoint + * endpoint + * interface + * alternate + * endpoint + * endpoint + * configuration + * interface + * alternate + * endpoint + * endpoint + * + * + * The configuration structures refer to the USB Configurations that will be + * made available to a USB HOST during the enumeration process. + * + * The USB HOST will select a configuration and optionally an interface with + * the usb set configuration and set interface commands. + * + * The selected interface (or the default interface if not specifically + * selected) will define the list of endpoints that will be used. + * + * The configuration and interfaces are stored in an array that is indexed + * by the specified configuratin or interface number minus one. + * + * A configuration number of zero is used to specify a return to the unconfigured + * state. + * + */ + + + + +/* + * communications class types + * + * c.f. CDC USB Class Definitions for Communications Devices + * c.f. WMCD USB CDC Subclass Specification for Wireless Mobile Communications Devices + * + */ + +#define CLASS_BCD_VERSION 0x0110 + +// c.f. CDC 4.1 Table 14 +#define COMMUNICATIONS_DEVICE_CLASS 0x02 + +// c.f. CDC 4.2 Table 15 +#define COMMUNICATIONS_INTERFACE_CLASS 0x02 + +// c.f. CDC 4.3 Table 16 +#define COMMUNICATIONS_NO_SUBCLASS 0x00 +#define COMMUNICATIONS_DLCM_SUBCLASS 0x01 +#define COMMUNICATIONS_ACM_SUBCLASS 0x02 +#define COMMUNICATIONS_TCM_SUBCLASS 0x03 +#define COMMUNICATIONS_MCCM_SUBCLASS 0x04 +#define COMMUNICATIONS_CCM_SUBCLASS 0x05 +#define COMMUNICATIONS_ENCM_SUBCLASS 0x06 +#define COMMUNICATIONS_ANCM_SUBCLASS 0x07 + +// c.f. WMCD 5.1 +#define COMMUNICATIONS_WHCM_SUBCLASS 0x08 +#define COMMUNICATIONS_DMM_SUBCLASS 0x09 +#define COMMUNICATIONS_MDLM_SUBCLASS 0x0a +#define COMMUNICATIONS_OBEX_SUBCLASS 0x0b + +// c.f. CDC 4.6 Table 18 +#define DATA_INTERFACE_CLASS 0x0a + +// c.f. CDC 4.7 Table 19 +#define COMMUNICATIONS_NO_PROTOCOL 0x00 + + +// c.f. CDC 5.2.3 Table 24 +#define CS_INTERFACE 0x24 +#define CS_ENDPOINT 0x25 + +/* + * bDescriptorSubtypes + * + * c.f. CDC 5.2.3 Table 25 + * c.f. WMCD 5.3 Table 5.3 + */ + +#define USB_ST_HEADER 0x00 +#define USB_ST_CMF 0x01 +#define USB_ST_ACMF 0x02 +#define USB_ST_DLMF 0x03 +#define USB_ST_TRF 0x04 +#define USB_ST_TCLF 0x05 +#define USB_ST_UF 0x06 +#define USB_ST_CSF 0x07 +#define USB_ST_TOMF 0x08 +#define USB_ST_USBTF 0x09 +#define USB_ST_NCT 0x0a +#define USB_ST_PUF 0x0b +#define USB_ST_EUF 0x0c +#define USB_ST_MCMF 0x0d +#define USB_ST_CCMF 0x0e +#define USB_ST_ENF 0x0f +#define USB_ST_ATMNF 0x10 + +#define USB_ST_WHCM 0x11 +#define USB_ST_MDLM 0x12 +#define USB_ST_MDLMD 0x13 +#define USB_ST_DMM 0x14 +#define USB_ST_OBEX 0x15 +#define USB_ST_CS 0x16 +#define USB_ST_CSD 0x17 +#define USB_ST_TCM 0x18 + +/* + * commuications class description structures + * + * c.f. CDC 5.1 + * c.f. WCMC 6.7.2 + * + * XXX add the other dozen class descriptor description structures.... + */ + +struct usb_header_description { + __u8 bDescriptorSubtype; + __u16 bcdCDC; +}; + +struct usb_call_management_description { + __u8 bmCapabilities; + __u8 bDataInterface; +}; + +struct usb_abstract_control_description { + __u8 bmCapabilities; +}; + +struct usb_union_function_description { + __u8 bMasterInterface; + __u8 bSlaveInterface[1]; + //__u8 bSlaveInterface[0]; // XXX FIXME +}; + +struct usb_ethernet_networking_description { + char *iMACAddress; + __u8 bmEthernetStatistics; + __u16 wMaxSegmentSize; + __u16 wNumberMCFilters; + __u8 bNumberPowerFilters; +}; + +struct usb_mobile_direct_line_model_description { + __u16 bcdVersion; + __u8 bGUID[16]; +}; + +struct usb_mobile_direct_line_model_detail_description { + __u8 bGuidDescriptorType; + __u8 bDetailData[2]; + //__u8 bDetailData[0]; // XXX FIXME +}; + +struct usb_class_description { + __u8 bDescriptorSubtype; + __u8 elements; + union { + struct usb_header_description header; + struct usb_call_management_description call_management; + struct usb_abstract_control_description abstract_control; + struct usb_union_function_description union_function; + struct usb_ethernet_networking_description ethernet_networking; + struct usb_mobile_direct_line_model_description mobile_direct; + struct usb_mobile_direct_line_model_detail_description mobile_direct_detail; + } description; +}; + +/* endpoint modifiers + * static struct usb_endpoint_description function_default_A_1[] = { + * + * {this_endpoint: 0, attributes: CONTROL, max_size: 8, polling_interval: 0 }, + * {this_endpoint: 1, attributes: BULK, max_size: 64, polling_interval: 0, direction: IN}, + * {this_endpoint: 2, attributes: BULK, max_size: 64, polling_interval: 0, direction: OUT}, + * {this_endpoint: 3, attributes: INTERRUPT, max_size: 8, polling_interval: 0}, + * + * + */ +#define OUT 0x00 +#define IN 0x80 + +#define CONTROL 0x00 +#define ISOCHRONOUS 0x01 +#define BULK 0x02 +#define INTERRUPT 0x03 + + +/* configuration modifiers + */ +#define BMATTRIBUTE_RESERVED 0x80 +#define BMATTRIBUTE_SELF_POWERED 0x40 + +/* + * usb device description structures + */ + +struct usb_endpoint_description { + __u8 bEndpointAddress; + __u8 bmAttributes; + __u16 wMaxPacketSize; + __u8 bInterval; + __u8 direction; + __u32 transferSize; // maximum bulk transfer size +}; + +struct usb_alternate_description { + char *iInterface; + __u8 bAlternateSetting; + // list of CDC class descriptions for this alternate interface + __u8 classes; + struct usb_class_description *class_list; + // list of endpoint descriptions for this alternate interface + __u8 endpoints; + struct usb_endpoint_description *endpoint_list; +}; + +struct usb_interface_description { + + __u8 bInterfaceClass; + __u8 bInterfaceSubClass; + __u8 bInterfaceProtocol; + char *iInterface; + // list of alternate interface descriptions for this interface + __u8 alternates; + struct usb_alternate_description *alternate_list; +}; + +struct usb_configuration_description { + char *iConfiguration; + __u8 bmAttributes; + __u8 bMaxPower; + // list of interface descriptons for this configuration + __u8 interfaces; + struct usb_interface_description *interface_list; + int configuration_type; +}; + +#define VENDOR 0xff + +struct usb_device_description { + __u8 bDeviceClass; + __u8 bDeviceSubClass; + __u8 bDeviceProtocol; + + __u16 idVendor; + __u16 idProduct; + + char *iManufacturer; + char *iProduct; + char *iSerialNumber; +}; + + + +/* + * standard usb descriptor structures + */ + +struct usb_endpoint_descriptor { + __u8 bLength; + __u8 bDescriptorType; // 0x5 + __u8 bEndpointAddress; + __u8 bmAttributes; + __u16 wMaxPacketSize; + __u8 bInterval; +} __attribute__ ((packed)); + +struct usb_interface_descriptor { + __u8 bLength; + __u8 bDescriptorType; // 0x04 + __u8 bInterfaceNumber; + __u8 bAlternateSetting; + __u8 bNumEndpoints; + __u8 bInterfaceClass; + __u8 bInterfaceSubClass; + __u8 bInterfaceProtocol; + __u8 iInterface; +} __attribute__ ((packed)); + +struct usb_configuration_descriptor { + __u8 bLength; + __u8 bDescriptorType; // 0x2 + __u16 wTotalLength; + __u8 bNumInterfaces; + __u8 bConfigurationValue; + __u8 iConfiguration; + __u8 bmAttributes; + __u8 bMaxPower; +} __attribute__ ((packed)); + +struct usb_device_descriptor { + __u8 bLength; + __u8 bDescriptorType; // 0x01 + __u16 bcdUSB; + __u8 bDeviceClass; + __u8 bDeviceSubClass; + __u8 bDeviceProtocol; + __u8 bMaxPacketSize0; + __u16 idVendor; + __u16 idProduct; + __u16 bcdDevice; + __u8 iManufacturer; + __u8 iProduct; + __u8 iSerialNumber; + __u8 bNumConfigurations; +} __attribute__ ((packed)); + +struct usb_string_descriptor { + __u8 bLength; + __u8 bDescriptorType; // 0x03 + __u16 wData[0]; +} __attribute__ ((packed)); + +struct usb_generic_descriptor { + __u8 bLength; + __u8 bDescriptorType; + __u8 bDescriptorSubtype; +} __attribute__ ((packed)); + + +/* + * communications class descriptor structures + * + * c.f. CDC 5.2 Table 25c + */ + +struct usb_class_function_descriptor { + __u8 bFunctionLength; + __u8 bDescriptorType; + __u8 bDescriptorSubtype; +} __attribute__ ((packed)); + +struct usb_class_function_descriptor_generic { + __u8 bFunctionLength; + __u8 bDescriptorType; + __u8 bDescriptorSubtype; + __u8 bmCapabilities; +} __attribute__ ((packed)); + +struct usb_class_header_function_descriptor { + __u8 bFunctionLength; + __u8 bDescriptorType; + __u8 bDescriptorSubtype; // 0x00 + __u16 bcdCDC; +} __attribute__ ((packed)); + +struct usb_class_call_management_descriptor { + __u8 bFunctionLength; + __u8 bDescriptorType; + __u8 bDescriptorSubtype; // 0x01 + __u8 bmCapabilities; + __u8 bDataInterface; +} __attribute__ ((packed)); + +struct usb_class_abstract_control_descriptor { + __u8 bFunctionLength; + __u8 bDescriptorType; + __u8 bDescriptorSubtype; // 0x02 + __u8 bmCapabilities; +} __attribute__ ((packed)); + +struct usb_class_direct_line_descriptor { + __u8 bFunctionLength; + __u8 bDescriptorType; + __u8 bDescriptorSubtype; // 0x03 +} __attribute__ ((packed)); + +struct usb_class_telephone_ringer_descriptor { + __u8 bFunctionLength; + __u8 bDescriptorType; + __u8 bDescriptorSubtype; // 0x04 + __u8 bRingerVolSeps; + __u8 bNumRingerPatterns; +} __attribute__ ((packed)); + +struct usb_class_telephone_call_descriptor { + __u8 bFunctionLength; + __u8 bDescriptorType; + __u8 bDescriptorSubtype; // 0x05 + __u8 bmCapabilities; +} __attribute__ ((packed)); + +struct usb_class_union_function_descriptor { + __u8 bFunctionLength; + __u8 bDescriptorType; + __u8 bDescriptorSubtype; // 0x06 + __u8 bMasterInterface; + __u8 bSlaveInterface0[0]; +} __attribute__ ((packed)); + +struct usb_class_country_selection_descriptor { + __u8 bFunctionLength; + __u8 bDescriptorType; + __u8 bDescriptorSubtype; // 0x07 + __u8 iCountryCodeRelDate; + __u16 wCountryCode0[0]; +} __attribute__ ((packed)); + + +struct usb_class_telephone_operational_descriptor { + __u8 bFunctionLength; + __u8 bDescriptorType; + __u8 bDescriptorSubtype; // 0x08 + __u8 bmCapabilities; +} __attribute__ ((packed)); + + +struct usb_class_usb_terminal_descriptor { + __u8 bFunctionLength; + __u8 bDescriptorType; + __u8 bDescriptorSubtype; // 0x09 + __u8 bEntityId; + __u8 bInterfaceNo; + __u8 bOutInterfaceNo; + __u8 bmOptions; + __u8 bChild0[0]; +} __attribute__ ((packed)); + +struct usb_class_network_channel_descriptor { + __u8 bFunctionLength; + __u8 bDescriptorType; + __u8 bDescriptorSubtype; // 0x0a + __u8 bEntityId; + __u8 iName; + __u8 bChannelIndex; + __u8 bPhysicalInterface; +} __attribute__ ((packed)); + +struct usb_class_protocol_unit_function_descriptor { + __u8 bFunctionLength; + __u8 bDescriptorType; + __u8 bDescriptorSubtype; // 0x0b + __u8 bEntityId; + __u8 bProtocol; + __u8 bChild0[0]; +} __attribute__ ((packed)); + +struct usb_class_extension_unit_descriptor { + __u8 bFunctionLength; + __u8 bDescriptorType; + __u8 bDescriptorSubtype; // 0x0c + __u8 bEntityId; + __u8 bExtensionCode; + __u8 iName; + __u8 bChild0[0]; +} __attribute__ ((packed)); + +struct usb_class_multi_channel_descriptor { + __u8 bFunctionLength; + __u8 bDescriptorType; + __u8 bDescriptorSubtype; // 0x0d + __u8 bmCapabilities; +} __attribute__ ((packed)); + +struct usb_class_capi_control_descriptor { + __u8 bFunctionLength; + __u8 bDescriptorType; + __u8 bDescriptorSubtype; // 0x0e + __u8 bmCapabilities; +} __attribute__ ((packed)); + +struct usb_class_ethernet_networking_descriptor { + __u8 bFunctionLength; + __u8 bDescriptorType; + __u8 bDescriptorSubtype; // 0x0f + __u8 iMACAddress; + __u32 bmEthernetStatistics; + __u16 wMaxSegmentSize; + __u16 wNumberMCFilters; + __u8 bNumberPowerFilters; +} __attribute__ ((packed)); + +struct usb_class_atm_networking_descriptor { + __u8 bFunctionLength; + __u8 bDescriptorType; + __u8 bDescriptorSubtype; // 0x10 + __u8 iEndSystermIdentifier; + __u8 bmDataCapabilities; + __u8 bmATMDeviceStatistics; + __u16 wType2MaxSegmentSize; + __u16 wType3MaxSegmentSize; + __u16 wMaxVC; +} __attribute__ ((packed)); + + +struct usb_class_mdlm_descriptor { + __u8 bFunctionLength; + __u8 bDescriptorType; + __u8 bDescriptorSubtype; // 0x12 + __u16 bcdVersion; + __u8 bGUID[16]; +} __attribute__ ((packed)); + +struct usb_class_mdlmd_descriptor { + __u8 bFunctionLength; + __u8 bDescriptorType; + __u8 bDescriptorSubtype; // 0x13 + __u8 bGuidDescriptorType; + __u8 bDetailData[0]; + +} __attribute__ ((packed)); + + +/* + * descriptor union structures + */ + +struct usb_descriptor { + union { + struct usb_generic_descriptor generic; + struct usb_endpoint_descriptor endpoint; + struct usb_interface_descriptor interface; + struct usb_configuration_descriptor configuration; + struct usb_device_descriptor device; + struct usb_string_descriptor string; + } descriptor; + +} __attribute__ ((packed)); + +struct usb_class_descriptor { + union { + struct usb_class_function_descriptor function; + struct usb_class_function_descriptor_generic generic; + struct usb_class_header_function_descriptor header_function; + struct usb_class_call_management_descriptor call_management; + struct usb_class_abstract_control_descriptor abstract_control; + struct usb_class_direct_line_descriptor direct_line; + struct usb_class_telephone_ringer_descriptor telephone_ringer; + struct usb_class_telephone_operational_descriptor telephone_operational; + struct usb_class_telephone_call_descriptor telephone_call; + struct usb_class_union_function_descriptor union_function; + struct usb_class_country_selection_descriptor country_selection; + struct usb_class_usb_terminal_descriptor usb_terminal; + struct usb_class_network_channel_descriptor network_channel; + struct usb_class_extension_unit_descriptor extension_unit; + struct usb_class_multi_channel_descriptor multi_channel; + struct usb_class_capi_control_descriptor capi_control; + struct usb_class_ethernet_networking_descriptor ethernet_networking; + struct usb_class_atm_networking_descriptor atm_networking; + struct usb_class_mdlm_descriptor mobile_direct; + struct usb_class_mdlmd_descriptor mobile_direct_detail; + } descriptor; + +} __attribute__ ((packed)); + + +struct usb_alternate_instance { + struct usb_interface_descriptor *interface_descriptor; + + int classes; + struct usb_class_descriptor **classes_descriptor_array; + + int endpoints; + int *endpoint_transfersize_array; + struct usb_endpoint_descriptor **endpoints_descriptor_array; +}; + +struct usb_interface_instance { + + + int alternates; + //struct usb_interface_descriptor **alternates_descriptor_array; + struct usb_alternate_instance *alternates_instance_array; + + +}; + + + +/* Operations that the bus interface driver can use to interact with a + * function driver. + * + * Typically these are called to deal with either specific changes of state + * to an endpoint: * reset, suspend, resume; or to process received data. + * + * The default endpoint 0 driver will use the configure entry point to tell + * a function driver that it has been configured for use. + * + * The urb sent callback is specified in the urb structure so that specific + * completion routines can be specified for each type of data being + * transmitted. + * + * The receive urb function is called when there is data to be processed. + * + * The receive setup function is called when there is an endpoint zero + * setup packet to be processed. + * + * The alloc urb data function is called to allocate urb data buffers. + * + */ + + +struct usb_function_operations { + void (*event) (struct usb_device_instance *, usb_device_event_t, int); + int (*urb_sent) (struct urb *, int); + int (*recv_urb) (struct urb *); + int (*recv_setup) (struct urb *); + int (*alloc_urb_data) (struct urb *, int); + void (*dealloc_urb_data) (struct urb *); + void (*function_init) (struct usb_bus_instance *, struct usb_device_instance *, + struct usb_function_driver *); + void (*function_exit) (struct usb_device_instance *); +}; + + +struct usb_configuration_instance { + int interfaces; + struct usb_configuration_descriptor *configuration_descriptor; + struct usb_interface_instance *interface_instance_array; + struct usb_function_driver *function_driver; +}; + + + + +/* Function Driver data structure + * + * Function driver and its configuration descriptors. + * + * This is passed to the usb-device layer when registering. It contains all + * required information about the function driver for the usb-device layer + * to use the function drivers configuration data and to configure this + * function driver an active configuration. + * + * Note that each function driver registers itself on a speculative basis. + * Whether a function driver is actually configured will depend on the USB + * HOST selecting one of the function drivers configurations. + * + * This may be done multiple times WRT to either a single bus interface + * instance or WRT to multiple bus interface instances. In other words a + * multiple configurations may be selected for a specific bus interface. Or + * the same configuration may be selected for multiple bus interfaces. + * + */ +struct usb_function_driver { + const char *name; + struct usb_function_operations *ops; // functions + + // device & configuration descriptions + struct usb_device_description *device_description; + struct usb_configuration_description *configuration_description; + int configurations; + + // constructed descriptors + struct usb_device_descriptor *device_descriptor; + struct usb_configuration_instance *configuration_instance_array; + + struct list_head drivers; // linked list + struct module *this_module; // manage inc use counts to prevent unload races +}; + +void usbd_function_init (struct usb_bus_instance *, struct usb_device_instance *, + struct usb_function_driver *); +void usbd_function_close (struct usb_device_instance *); diff -uNr linux.org/drivers/usb/device/usbd-inline.h linux/drivers/usb/device/usbd-inline.h --- linux.org/drivers/usb/device/usbd-inline.h Thu Jan 1 08:00:00 1970 +++ linux/drivers/usb/device/usbd-inline.h Thu May 15 15:54:48 2003 @@ -0,0 +1,502 @@ +/* + * linux/drivers/usbd/usb-inline.h - additional USB Device Core support + * + * Copyright (c) 2000, 2001, 2002 Lineo + * Copyright (c) 2001 Hewlett Packard + * + * By: + * Stuart Lynne , + * Tom Rushworth , + * Bruce Balden + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + * + */ + +/* + * Initialize an urb_link to be a single element list. + * If the urb_link is being used as a distinguished list head + * the list is empty when the head is the only link in the list. + */ +static __inline__ void urb_link_init (urb_link * ul) +{ + if (ul) { + ul->prev = ul->next = ul; + } +} + +/* + * Detach an urb_link from a list, and set it + * up as a single element list, so no dangling + * pointers can be followed, and so it can be + * joined to another list if so desired. + * + * Called from interrupt. + */ +static __inline__ void urb_detach_irq (struct urb *urb) +{ + if (urb) { + urb_link *ul = &urb->link; + ul->next->prev = ul->prev; + ul->prev->next = ul->next; + urb_link_init (ul); + } +} + +/* + * Return the first urb_link in a list with a distinguished + * head "hd", or NULL if the list is empty. This will also + * work as a predicate, returning NULL if empty, and non-NULL + * otherwise. + * + * Called from interrupt. + */ +static __inline__ urb_link *first_urb_link_irq (urb_link * hd) +{ + urb_link *nx; + if (NULL != hd && NULL != (nx = hd->next) && nx != hd) { + // There is at least one element in the list + // (besides the distinguished head). + return (nx); + } + // The list is empty + return (NULL); +} + +/* + * Return the first urb in a list with a distinguished + * head "hd", or NULL if the list is empty. + * + * Called from interrupt. + */ +static __inline__ struct urb *first_urb_irq (urb_link * hd) +{ + urb_link *nx; + if (NULL == (nx = first_urb_link_irq (hd))) { + // The list is empty + return (NULL); + } + return (p2surround (struct urb, link, nx)); +} + +/* + * Detach and return the first urb in a list with a distinguished + * head "hd", or NULL if the list is empty. + */ +static __inline__ struct urb *first_urb_detached_irq (urb_link * hd) +{ + struct urb *urb; + if ((urb = first_urb_irq (hd))) { + urb_detach_irq (urb); + } + return urb; +} + +/* + * Detach and return the first urb in a list with a distinguished + * head "hd", or NULL if the list is empty. + * + * Can be called from non-interrupt context. + * + */ +static __inline__ struct urb *first_urb_detached (urb_link * hd) +{ + struct urb *urb; + unsigned long flags; + + local_irq_save (flags); + urb = first_urb_detached_irq (hd); + local_irq_restore (flags); + return urb; +} + + +/* + * Append an urb_link (or a whole list of + * urb_links) to the tail of another list + * of urb_links. + */ +static __inline__ void urb_append_irq (urb_link * hd, struct urb *urb) +{ + // XXX XXX _urb_detach(urb); + if (hd && urb) { + urb_link *new = &urb->link; + +#ifdef C2L_SINGLETON_ONLY + // This _assumes_ the new urb is a singleton, + // but allows it to have an uninitialized link. + //printk(KERN_DEBUG"urb_append: hd: %p n:%p p:%p new: %p n:%p p:%p\n", hd, hd->next, hd->prev, new, new->next, new->prev); + + new->prev = hd->prev; + new->next = hd; + + hd->prev->next = new; + hd->prev = new; +#else + // This allows the new urb to be a list of urbs, + // with new pointing at the first, but the link + // must be initialized. + // Order is important here... + urb_link *pul = hd->prev; + new->prev->next = hd; + hd->prev = new->prev; + new->prev = pul; + pul->next = new; +#endif + } +} + + +#if 0 +/** + * usbd_dealloc_urb - deallocate an URB and associated buffer + * @urb: pointer to an urb structure + * + * Deallocate an urb structure and associated data. + */ +static __inline__ void usbd_dealloc_urb (struct urb *urb) +{ + if (urb) { + if (urb->buffer) { + if (urb->function_instance && urb->function_instance->function_driver->ops->dealloc_urb_data) { + urb->function_instance->function_driver->ops->dealloc_urb_data (urb); + } + else { + kfree (urb->buffer); + } + } + kfree (urb); + } +} +#else +void usbd_dealloc_urb (struct urb *urb); +#endif + +/** + * usbd_schedule_device_bh - schedule bottom half + * + * Schedule the device bottom half. This runs from timer tick so is equivalent + * to interrupt but at lower priority. + * + */ +static __inline__ void usbd_schedule_device_bh (struct usb_device_instance *device) +{ + if (device && (device->status != USBD_CLOSING) && !device->device_bh.sync) { + queue_task (&device->device_bh, &tq_immediate); + mark_bh (IMMEDIATE_BH); + } +} + +/** + * usbd_schedule_function_bh - schedule bottom half + * + * Schedule the function bottom half. This runs as a process. + * + */ +static __inline__ void usbd_schedule_function_bh (struct usb_device_instance *device) +{ + if (device && (device->status != USBD_CLOSING) && !device->function_bh.sync) { + schedule_task (&device->function_bh); + } +} + +#if 0 +/** + * usbd_recv_urb_irq - process a received urb + * @urb: pointer to an urb structure + * + * Used by a USB Bus interface driver to pass received data in a URB to the + * appropriate USB Function driver via the endpoints receive queue. + * + * Called from interrupt. + * + */ +static __inline__ void usbd_recv_urb_irq (struct urb *urb) +{ + if (urb) { + urb->status = RECV_OK; + urb->jiffies = jiffies; + /* XXX XXX + { + unsigned long flags; + local_irq_save(flags); + */ + urb_append_irq (&(urb->endpoint->rcv), urb); + /* + local_irq_restore(flags); + } + */ + usbd_schedule_device_bh (urb->device); + } +} +#endif + +/** + * usbd_recycle_urb - recycle a received urb + * @urb: pointer to an urb structure + * + * Used by a USB Function interface driver to recycle an urb. + * + */ +static __inline__ void usbd_recycle_urb (struct urb *urb) +{ + if (urb) { + urb->actual_length = 0; + urb->status = RECV_READY; + urb->jiffies = jiffies; + { + unsigned long flags; + local_irq_save (flags); + urb_append_irq (&(urb->endpoint->rdy), urb); + local_irq_restore (flags); + } + } +} + +/** + * usbd_send_urb - submit a urb to send + * @urb: pointer to an urb structure + * + * Used by a USB Function driver to submit data to be sent in an urb to the + * appropriate USB Bus driver via the endpoints transmit queue. + * + * This is NOT called from interrupts. + * + */ +static __inline__ int usbd_send_urb (struct urb *urb) +{ + + if (urb && urb->endpoint && (urb->device->status == USBD_OK)) { + //urb->device->urbs_queued++; + urb->status = SEND_IN_PROGRESS; + urb->jiffies = jiffies; + { + unsigned long flags; + local_irq_save (flags); + urb_append_irq (&(urb->endpoint->tx), urb); + local_irq_restore (flags); + } + return urb->device->bus->driver->ops->send_urb (urb); + } + printk (KERN_ERR "usbd_send_urb: !USBD_OK\n"); + return -EINVAL; +} + + +/* * + * usbd_urb_sent_irq - tell function that an urb has been transmitted. + * @urb: pointer to an urb structure + * + * Must be called from an interrupt or with interrupts disabled. + * + * Used by a USB Bus driver to pass a sent urb back to the function + * driver via the endpoints done queue. + * + * Called from interrupt. + */ +static __inline__ void usbd_urb_sent_irq (struct urb *urb, int rc) +{ + if (urb) { + + urb->status = rc; + urb->jiffies = jiffies; + /* XXX XXX + { + unsigned long flags; + local_irq_save(flags); + */ + urb_append_irq (&(urb->endpoint->done), urb); + /* + local_irq_restore(flags); + } + */ + usbd_schedule_device_bh (urb->device); + } +} + +/** + * usbd_recv_setup - process a received urb + * @urb: pointer to an urb structure + * + * Used by a USB Bus interface driver to pass received data in a URB to the + * appropriate USB Function driver. + */ +static __inline__ int usbd_recv_setup (struct urb *urb) +{ + struct usb_function_instance *function_instance; + int port = 0; // XXX compound devices + + if (urb && !(urb->device->status == USBD_CLOSING)) { + + //printk(KERN_DEBUG"usbd_recv_setup: %p request: %x vendor: %d\n", + // urb, urb->device_request.bmRequestType, (urb->device_request.bmRequestType&USB_REQ_TYPE_MASK)!=0); + + function_instance = ((urb->device_request.bmRequestType & USB_REQ_TYPE_MASK) == 0) ? + urb->device->ep0 : urb->device->function_instance_array + port; + + if (function_instance == NULL) { + printk (KERN_ERR "usbd_recv_setup: endpoint: %d function_instance NULL\n", + urb->endpoint->endpoint_address); + return 1; + } + + if (function_instance->function_driver->ops->recv_setup == NULL) { + printk (KERN_ERR "usbd_recv_setup: recv_setup NULL\n"); + return 1; + } + + return function_instance->function_driver->ops->recv_setup (urb); + } + printk (KERN_ERR "usbd_recv_setup: !USBD_OK\n"); + return -EINVAL; +} + + +/** + * usbd_rcv_complete_irq - complete a receive + * @endpoint: + * @len: + * @urb_bad: + * + * Called from rcv interrupt to complete. + */ +static __inline__ void usbd_rcv_complete_irq (struct usb_endpoint_instance *endpoint, int len, int urb_bad) +{ + + // XXX XXX + //return; + + if (endpoint) { + struct urb *rcv_urb; + + //printk(KERN_ERR"usbd_rcv_complete: len: %d urb: %p\n", len, endpoint->rcv_urb); + + // if we had an urb then update actual_length, dispatch if neccessary + if ((rcv_urb = endpoint->rcv_urb)) { + + //printk(KERN_ERR"usbd_rcv_complete: actual: %d buffer: %d\n", + //rcv_urb->actual_length, rcv_urb->buffer_length); + + // check the urb is ok, are we adding data less than the packetsize + if (!urb_bad && (rcv_urb->device->status == USBD_OK) && (len <= endpoint->rcv_packetSize)) { + + // increment the received data size + rcv_urb->actual_length += len; + + // if the current received data is short (less than full packetsize) which + // indicates the end of the bulk transfer, we have received the maximum + // transfersize, or if we do not have enough room to receive another packet + // then pass this data up to the function driver + + if (((len < endpoint->rcv_packetSize) || + (rcv_urb->actual_length >= endpoint->rcv_transferSize) || + (rcv_urb->actual_length >= + (rcv_urb->buffer_length - endpoint->rcv_packetSize)))) + { + //printk(KERN_ERR"usbd_rcv_complete: finishing %p\n", rcv_urb); + endpoint->rcv_urb = NULL; + + rcv_urb->status = RECV_OK; + rcv_urb->jiffies = jiffies; + urb_append_irq (&(rcv_urb->endpoint->rcv), rcv_urb); + usbd_schedule_device_bh (rcv_urb->device); + //printk(KERN_INFO"cmplt: %p\n", rcv_urb); + + rcv_urb = NULL; + } + } + else { + + printk (KERN_ERR "usbd_rcv_complete: RECV_ERROR actual: %d buffer: %d urb_bad: %d\n", + rcv_urb->actual_length, rcv_urb->buffer_length, urb_bad); + + // XXX TEST rcv_urb->actual_length = 0; + // XXX TEST printk(KERN_ERR"too large %d %d\n", len, endpoint->rcv_packetSize); + + rcv_urb->actual_length = 0; + rcv_urb->status = RECV_ERROR; + rcv_urb->jiffies = jiffies; + urb_append_irq (&(rcv_urb->endpoint->rcv), rcv_urb); + usbd_schedule_device_bh (rcv_urb->device); + rcv_urb = NULL; + + } + } + else { + printk (KERN_ERR "usbd_rcv_complete: no rcv_urb!\n"); + } + + // if we don't have an urb see if we can get one + if (!rcv_urb) { + endpoint->rcv_urb = first_urb_detached_irq (&endpoint->rdy); + } + } + else { + printk (KERN_ERR "usbd_rcv_complete: no endpoint!\n"); + } + +} + + +/** + * usbd_tx_complete_irq - complete a transmit + * @endpoint: + * @resetart: + * + * Called from tx interrupt to complete. + */ +static __inline__ void usbd_tx_complete_irq (struct usb_endpoint_instance *endpoint, int restart) +{ + if (endpoint) { + + struct urb *tx_urb; + + // if we have a tx_urb advance or reset, finish if complete + if ((tx_urb = endpoint->tx_urb)) { + + if (!restart) { + //int sent = MIN(endpoint->last,endpoint->tx_packetSize); + int sent = endpoint->last; + //endpoint->sent += endpoint->last; + endpoint->sent += sent; + endpoint->last -= sent; + } + else { + endpoint->last = 0; + } + //printk(KERN_INFO"cmplt: act: %d last: %d sent: %d\n", + // endpoint->tx_urb->actual_length, + // endpoint->last, endpoint->sent); + + if ((tx_urb->device->status == USBD_OK) && (endpoint->tx_urb->actual_length - endpoint->sent) <= 0) { + if (endpoint->endpoint_address) { + usbd_urb_sent_irq (endpoint->tx_urb, SEND_FINISHED_OK); + } + endpoint->tx_urb = NULL; + endpoint->last = endpoint->sent = 0; + //printk(KERN_INFO"cmplt: finished\n"); + } + } + // if we do not have a tx_urb see if we can get one + if (endpoint->endpoint_address && !endpoint->tx_urb && + (endpoint->tx_urb = first_urb_detached_irq (&endpoint->tx))) + { + endpoint->last = endpoint->sent = 0; + } + } + else { + printk (KERN_ERR "usbd_tx_complete_irq: no endpoint!\n"); + } +} diff -uNr linux.org/drivers/usb/device/usbd-module.h linux/drivers/usb/device/usbd-module.h --- linux.org/drivers/usb/device/usbd-module.h Thu Jan 1 08:00:00 1970 +++ linux/drivers/usb/device/usbd-module.h Thu May 15 15:54:48 2003 @@ -0,0 +1,60 @@ +/* + * linux/drivers/usbd/usb-module.h + * + * Copyright (c) 2000, 2001, 2002 Lineo + * Copyright (c) 2001 Hewlett Packard + * + * By: + * Stuart Lynne , + * Tom Rushworth , + * Bruce Balden + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + * + */ + +/* + * + * USBD_MODULE_INFO(info) + * + * + * The info parameter should take the form: + * + * "usbdcore 0.1-beta" + * + * name usbdcore + * major 0 + * minor 1 + * status beta + * + * Status should be one of: + * + * alpha work in progress, preview only + * beta initial testing, not for deployment + * + * early suitable for production use where new features are needed + * limited suitable for new installations + * production most stable + * + * deprectated no longer maintained + * abandoned no longer available + * + * Additional information added from usbd-export.h and usbd-build.h will + * record the build number and date exported. + */ + +#define USBD_MODULE_INFO(info) \ +const char __usbd_module_info[] = info " " USBD_BUILD " " USBD_EXPORT_DATE; + diff -uNr linux.org/drivers/usb/device/usbd-monitor.c linux/drivers/usb/device/usbd-monitor.c --- linux.org/drivers/usb/device/usbd-monitor.c Thu Jan 1 08:00:00 1970 +++ linux/drivers/usb/device/usbd-monitor.c Thu May 15 15:54:48 2003 @@ -0,0 +1,786 @@ +/* + * linux/drivers/usbd/usbd-monitor.c - USB Device Cable Monitor + * + * Copyright (c) 2000, 2001, 2002 Lineo + * Copyright (c) 2001 Hewlett Packard + * + * By: + * Stuart Lynne , + * Tom Rushworth , + * Bruce Balden + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + * + */ + + +#include +#include + +#include "usbd-export.h" +#include "usbd-build.h" +#include "usbd-module.h" + +MODULE_AUTHOR ("sl@lineo.com, tbr@lineo.com"); +MODULE_DESCRIPTION ("USB Device Monitor"); +USBD_MODULE_INFO ("usbd_monitor 0.3"); + +EXPORT_NO_SYMBOLS; + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + + +/* + * Architecture specific includes + */ + +#if defined(CONFIG_ARCH_SA1100) || defined(CONFIG_SABINAL_DISCOVERY) + +#include +#include + +#include "usbd.h" +#include "usbd-func.h" +#include "usbd-bus.h" +#include "hotplug.h" +/* pick up any required GPIO pin definitions */ +#if defined(CONFIG_ARCH_SA1100) +#include "bi/sa1100.h" +#endif +#endif + +#if defined(CONFIG_PM) +#include +#endif + + +/* Module Parameters ************************************************************************* */ + +typedef enum monitor_status { + MONITOR_UNKNOWN, + MONITOR_LOADING, // loading due to cable connection interrupt + MONITOR_LOADED, // loaded + MONITOR_UNLOADING, // unloading due to cable disconnection interrupt + MONITOR_UNLOADED, // unloaded +#ifdef CONFIG_PM + MONITOR_SUSPENDING, // suspending due to power management event + MONITOR_SUSPENDED, // suspended due to power management event + MONITOR_RESTORING, // restoring +#endif +} monitor_status_t; + +/* struct monitor_bi_data - + * + * private data structure for this bus interface driver + */ +struct monitor_data { + monitor_status_t status; + struct tq_struct monitor_bh; + struct tq_struct hotplug_bh; + + int have_irq; +#ifdef CONFIG_PM + struct pm_dev *pm_info; +#endif + +}; + +struct monitor_data monitor; +void monitor_int_hndlr (int irq, void *dev_id, struct pt_regs *regs); + +/** + * monitor_request_irq + */ +int monitor_request_irq (void) +{ +#if defined(CONFIG_SA1100_USBCABLE_GPIO) + int rc; + + printk (KERN_DEBUG "monitor_request_irq: %d %d\n", CONFIG_SA1100_USBCABLE_GPIO, SA1100_GPIO_TO_IRQ (CONFIG_SA1100_USBCABLE_GPIO)); + + if ((rc = request_irq (SA1100_GPIO_TO_IRQ (CONFIG_SA1100_USBCABLE_GPIO), monitor_int_hndlr, SA_SHIRQ, "USBD Monitor", &monitor))) { + printk (KERN_DEBUG "monitor_request_irq: failed: %d\n", rc); + return -EINVAL; + } + GPDR &= ~GPIO_GPIO (CONFIG_SA1100_USBCABLE_GPIO); + set_GPIO_IRQ_edge (GPIO_GPIO (CONFIG_SA1100_USBCABLE_GPIO), GPIO_BOTH_EDGES); +#endif + return 0; +} + +/** + * monitor_free_irq + */ +void monitor_free_irq (void) +{ +#if defined(CONFIG_SA1100_USBCABLE_GPIO) + free_irq (SA1100_GPIO_TO_IRQ (CONFIG_SA1100_USBCABLE_GPIO), NULL); +#endif +} + + +/** + * monitor_connected - connected to cable + * + * Return non-zero if via USB cable to USB Hub or Host + */ +int monitor_connected (void) +{ + int rc = 1; + + /* + * Architecture specific - determine connect status + */ + + /* + * SA-1100 + * + * Only pay attention to the status if we also can control the pullup. + */ +#if defined(CONFIG_SA1100_USBCABLE_GPIO) && defined(CONFIG_SA1100_CONNECT_GPIO) + +#ifdef CONFIG_SA1100_CONNECT_ACTIVE_HIGH + rc = (GPLR & GPIO_GPIO (23)) != 0; + printk (KERN_DEBUG "udc_connected: ACTIVE_HIGH: %d", rc); +#else + rc = (GPLR & GPIO_GPIO (23)) == 0; + printk (KERN_DEBUG "udc_connected: ACTIVE_LOW: %d", rc); +#endif + +#endif /* CONFIG_ARCH_SA1100 && CONFIG_SA1100_USBCABLE_GPIO */ + + printk (KERN_DEBUG "monitor_connected: %d\n", rc); + return rc; +} + +static char *hotplug_actions[] = { + "load", + "suspend", + "restore-loaded", + "restore-unloaded", + "unload" +}; +/* indices for the hotplug_actions array, these must match the array */ +#define MHA_LOAD 0 +#define MHA_SUSPEND 1 +#define MHA_RESTORE_LOADED 2 +#define MHA_RESTORE_UNLOADED 3 +#define MHA_UNLOAD 4 + +#if defined(CONFIG_USBD_PROCFS) + +#define HOTPLUG_SYNC_TIMEOUT (10*HZ) + +static DECLARE_MUTEX_LOCKED (hotplug_done); +static struct timer_list hotplug_timeout; + +static void hotplug_sync_over (unsigned long data) +{ + printk (KERN_ERR "usbdmonitor: warning - hotplug script timed out\n"); + up (&hotplug_done); +} +#endif + +static int monitor_exiting = 0; + +int monitor_hotplug (int action_ndx) +{ + /* This should probably be serialized - if PM runs in a separate + context, it would seem possible for someone to "rmmmod usbdmonitor" + (e.g. via "at") at the same time time PM decides to suspend. + Unfortunately, there is no airtight way to accomplish that inside + this module - once PM has called the registered fn, the "race" + is on :(. */ + int rc; + if (action_ndx < 0 || action_ndx > MHA_UNLOAD) { + return (-EINVAL); + } + if (monitor_exiting) { + if (MHA_UNLOAD != action_ndx) { + return (-EINVAL); + } + if (MONITOR_UNLOADED == monitor.status || MONITOR_UNLOADING == monitor.status) { + /* No need to do it again... */ + return (0); + } + } + printk (KERN_DEBUG "monitor_hotplug: agent: usbd interface: monitor action: %s\n", hotplug_actions[action_ndx]); +#if defined(CONFIG_USBD_PROCFS) + /* Sync - fire up the script and wait for it to echo something to + /proc/usb-monitor (or else PM SUSPEND may not work) */ + init_MUTEX_LOCKED (&hotplug_done); + /* fire up the script */ + rc = hotplug ("usbd", "monitor", hotplug_actions[action_ndx]); + if (0 == rc) { + /* wait for the nudge from a write to /proc/usb-monitor */ + init_timer (&hotplug_timeout); + hotplug_timeout.data = 0; + hotplug_timeout.function = hotplug_sync_over; + hotplug_timeout.expires = jiffies + HOTPLUG_SYNC_TIMEOUT; + add_timer (&hotplug_timeout); + down_interruptible (&hotplug_done); + del_timer (&hotplug_timeout); + } +#else + /* Async - fire up the script and return */ + rc = hotplug ("usbd", "monitor", hotplug_actions[action_ndx]); +#endif + return (rc); +} + +#ifdef CONFIG_PM +/** + * monitor_suspend + * + * Check status, unload if required, set monitor status. + */ +int monitor_suspend (void) +{ + unsigned long flags; + local_irq_save (flags); + switch (monitor.status) { + case MONITOR_UNKNOWN: + case MONITOR_UNLOADED: + local_irq_restore (flags); + return 0; + + case MONITOR_LOADING: + case MONITOR_UNLOADING: + case MONITOR_SUSPENDING: + case MONITOR_RESTORING: + case MONITOR_SUSPENDED: + // XXX we should wait for this + local_irq_restore (flags); + return -EINVAL; + + case MONITOR_LOADED: + monitor.status = MONITOR_SUSPENDING; + local_irq_restore (flags); + monitor_hotplug (MHA_SUSPEND); + monitor.status = MONITOR_SUSPENDED; + return 0; + default: + return -EINVAL; + } +} +#endif + +/** + * hotplug_bh - + * @ignored: + * + * Check connected status and load/unload as appropriate. + */ +void hotplug_bh (void *ignored) +{ + printk (KERN_DEBUG "hotplug_bh:\n"); + + if (monitor_connected ()) { + printk (KERN_DEBUG "monitor_restore: RESTORE_LOADED\n"); + monitor_hotplug (MHA_RESTORE_LOADED); + monitor.status = MONITOR_LOADED; + } else { + printk (KERN_DEBUG "monitor_restore: RESTORE_UNLOADED\n"); + monitor_hotplug (MHA_RESTORE_UNLOADED); + monitor.status = MONITOR_UNLOADED; + } + + MOD_DEC_USE_COUNT; +} + +/** + * hotplug_schedule_bh - + */ +void hotplug_schedule_bh (void) +{ + printk (KERN_DEBUG "hotplug_schedule_bh: schedule bh\n"); + + if (monitor.hotplug_bh.sync) { + return; + } + + MOD_INC_USE_COUNT; + if (!schedule_task (&monitor.hotplug_bh)) { + printk (KERN_DEBUG "monitor_schedule_bh: failed\n"); + MOD_DEC_USE_COUNT; + } +} + + + +#ifdef CONFIG_PM +/** + * monitor_restore + * + * Check status, load if required, set monitor status. + */ +int monitor_restore (void) +{ + unsigned long flags; + local_irq_save (flags); + switch (monitor.status) { + case MONITOR_SUSPENDED: + monitor.status = MONITOR_RESTORING; + local_irq_restore (flags); + + hotplug_schedule_bh (); + + return 0; + + case MONITOR_UNKNOWN: + case MONITOR_UNLOADED: + case MONITOR_LOADING: + case MONITOR_UNLOADING: + case MONITOR_SUSPENDING: + case MONITOR_RESTORING: + // XXX we should wait for this + local_irq_restore (flags); + return -EINVAL; + + case MONITOR_LOADED: + local_irq_restore (flags); + return 0; + default: + return -EINVAL; + } +} + + +/** + * monitor_pm_event + * + * Handle power management event + */ +static int monitor_pm_event (struct pm_dev *dev, pm_request_t rqst, void *unused) +{ + int rc; + /* See comment regarding race condition at start of monitor_hotplug() */ + MOD_INC_USE_COUNT; + rc = 0; + switch (rqst) { + case PM_SUSPEND: + // Force unloading + rc = monitor_suspend (); + printk (KERN_ERR "%s: suspend finished (rc=%d)\n", __FUNCTION__, rc); + break; + case PM_RESUME: + // load if required. + monitor_restore (); + } + MOD_DEC_USE_COUNT; + return rc; +} +#endif /* CONFIG_PM */ + +/** + * monitor_load + * + * Check status, load if required, set monitor status. + */ +int monitor_load (void) +{ + unsigned long flags; + printk (KERN_DEBUG "monitor_load: \n"); + local_irq_save (flags); + switch (monitor.status) { + case MONITOR_UNKNOWN: + case MONITOR_UNLOADED: + monitor.status = MONITOR_LOADING; + local_irq_restore (flags); + monitor_hotplug (MHA_LOAD); + monitor.status = MONITOR_LOADED; + return 0; + + case MONITOR_LOADING: + case MONITOR_UNLOADING: +#ifdef CONFIG_PM + case MONITOR_SUSPENDING: + case MONITOR_SUSPENDED: + case MONITOR_RESTORING: +#endif + // XXX we should wait for this + local_irq_restore (flags); + return -EINVAL; + + case MONITOR_LOADED: + local_irq_restore (flags); + return 0; + default: + return -EINVAL; + } +} + + +/** + * monitor_unload + * + * Check status, unload if required, set monitor status. + */ +int monitor_unload (void) +{ + unsigned long flags; + printk (KERN_DEBUG "monitor_unload: \n"); + local_irq_save (flags); + switch (monitor.status) { + case MONITOR_UNKNOWN: + case MONITOR_UNLOADED: + local_irq_restore (flags); + return 0; + + case MONITOR_LOADING: + case MONITOR_UNLOADING: +#ifdef CONFIG_PM + case MONITOR_SUSPENDING: + case MONITOR_SUSPENDED: + case MONITOR_RESTORING: +#endif + // XXX we should wait for this + local_irq_restore (flags); + return -EINVAL; + + case MONITOR_LOADED: + monitor.status = MONITOR_UNLOADING; + local_irq_restore (flags); + monitor_hotplug (MHA_UNLOAD); + monitor.status = MONITOR_UNLOADED; + return 0; + default: + return -EINVAL; + } +} + + + +/** +* monitor_event - called from monitor interrupt handler + */ +void monitor_event (void) +{ + if (monitor_connected ()) { + monitor_load (); + } else { + monitor_unload (); + /* If there is no way to know when the cable is reconnected, + (ie no cable connect IRQ), we need to reload right away. */ +#if !defined(CONFIG_SA1100_CONNECT_GPIO) + monitor_load (); +#endif + } +} + +/** + * monitor_bh - + * @ignored: + * + * Check connected status and load/unload as appropriate. + */ +void monitor_bh (void *ignored) +{ + printk (KERN_DEBUG "monitor_bh:\n"); + + monitor_event (); + + MOD_DEC_USE_COUNT; +} + + +/** + * monitor_schedule_bh - + */ +void monitor_schedule_bh (void) +{ + //printk(KERN_DEBUG "monitor_schedule_bh: schedule bh to %s\n", msg); + + if (monitor.monitor_bh.sync) { + return; + } + + MOD_INC_USE_COUNT; + if (!schedule_task (&monitor.monitor_bh)) { + printk (KERN_DEBUG "monitor_schedule_bh: failed\n"); + MOD_DEC_USE_COUNT; + } +} + +/** + * monitor_int_hndlr - + * @irq: + * @dev_id: + * @regs + */ +void monitor_int_hndlr (int irq, void *dev_id, struct pt_regs *regs) +{ + printk (KERN_DEBUG "\n"); + printk (KERN_DEBUG "monitor_int_hndlr:\n"); + monitor_schedule_bh (); +} + +#ifdef CONFIG_USBD_PROCFS +/* Proc Filesystem *************************************************************************** */ + +/* * + * usbd_monitor_proc_read - implement proc file system read. + * @file + * @buf + * @count + * @pos + * + * Standard proc file system read function. + */ +static ssize_t usbd_monitor_proc_read (struct file *file, char *buf, size_t count, loff_t * pos) +{ + unsigned long page; + int len = 0; + int index; + + MOD_INC_USE_COUNT; + // get a page, max 4095 bytes of data... + if (!(page = get_free_page (GFP_KERNEL))) { + MOD_DEC_USE_COUNT; + return -ENOMEM; + } + + len = 0; + index = (*pos)++; + + if (index == 0) { + len += sprintf ((char *) page + len, "usb-monitor status\n"); + } + + if (index == 1) { + switch (monitor.status) { + case MONITOR_UNKNOWN: + len += sprintf ((char *) page + len, "unknown\n"); + break; + case MONITOR_UNLOADED: + len += sprintf ((char *) page + len, "unloaded\n"); + break; + case MONITOR_LOADING: + len += sprintf ((char *) page + len, "loading\n"); + break; + case MONITOR_UNLOADING: + len += sprintf ((char *) page + len, "unloading\n"); + break; +#ifdef CONFIG_PM + case MONITOR_SUSPENDING: + len += sprintf ((char *) page + len, "suspending\n"); + break; + case MONITOR_SUSPENDED: + len += sprintf ((char *) page + len, "suspended\n"); + break; + case MONITOR_RESTORING: + len += sprintf ((char *) page + len, "restoring\n"); + break; +#endif + case MONITOR_LOADED: + len += sprintf ((char *) page + len, "loaded\n"); + break; + default: + len += sprintf ((char *) page + len, "?\n"); + } + } + + if (len > count) { + len = -EINVAL; + } else if (len > 0 && copy_to_user (buf, (char *) page, len)) { + len = -EFAULT; + } + free_page (page); + MOD_DEC_USE_COUNT; + return len; +} + +/* * + * usbd_monitor_proc_write - implement proc file system write. + * @file + * @buf + * @count + * @pos + * + * Proc file system write function, used to signal monitor actions complete. + * (Hotplug script (or whatever) writes to the file to signal the completion + * of the script.) An ugly hack. + */ +static ssize_t usbd_monitor_proc_write (struct file *file, const char *buf, size_t count, loff_t * pos) +{ + size_t n = count; + char c; + MOD_INC_USE_COUNT; + //printk(KERN_DEBUG "%s: count=%u\n",__FUNCTION__,count); + while (n > 0) { + // Not too efficient, but it shouldn't matter + if (copy_from_user (&c, buf + (count - n), 1)) { + count = -EFAULT; + break; + } + n -= 1; + //printk(KERN_DEBUG "%s: %u/%u %02x\n",__FUNCTION__,count-n,count,c); + } + up (&hotplug_done); + MOD_DEC_USE_COUNT; + return (count); +} + +static struct file_operations usbd_monitor_proc_operations_functions = { + read:usbd_monitor_proc_read, + write:usbd_monitor_proc_write, +}; + +#endif + + +/* Module Init ******************************************************************************* */ + + +/** + * monitor_modinit - commission bus interface driver + * + */ +static int __init monitor_modinit (void) +{ + + printk (KERN_INFO "usbdm: %s\n", __usbd_module_info); + + // Initialize data structures _before_ installing interrupt handlers + monitor.status = MONITOR_UNKNOWN; + + monitor.monitor_bh.routine = monitor_bh; + monitor.monitor_bh.data = NULL; + // XXX monitor.monitor_bh.sync = 0; + + monitor.hotplug_bh.routine = hotplug_bh; + monitor.hotplug_bh.data = NULL; + // XXX monitor.hotplug_bh.sync = 0; + + + /* + * Architecture specific - request IRQ + */ + + if (!monitor_request_irq ()) { + monitor.have_irq++; + } else { + printk (KERN_DEBUG "usbdm: request irq failed\n"); + } + +#ifdef CONFIG_PM + /* + * Architecture specific - register with power management + */ + + monitor.pm_info = NULL; + + if (!(monitor.pm_info = pm_register (PM_USB_DEV, PM_SYS_UNKNOWN, monitor_pm_event))) { + printk (KERN_ERR "%s: couldn't register for power management\n", __FUNCTION__); + if (monitor.have_irq) { + monitor_free_irq (); + } + return 1; + } + monitor.pm_info->state = 0; +#endif + +#ifdef CONFIG_USBD_PROCFS + { + struct proc_dir_entry *p; + + // create proc filesystem entries + if ((p = create_proc_entry ("usb-monitor", 0, 0)) == NULL) { + if (monitor.have_irq) { + monitor_free_irq (); + monitor.have_irq = 0; + } +#ifdef CONFIG_PM + if (monitor.pm_info) { + pm_unregister_all(monitor_pm_event); + monitor.pm_info = NULL; + } +#endif + return -ENOMEM; + } + p->proc_fops = &usbd_monitor_proc_operations_functions; + } +#endif + + // Find out if the cable is connected + // and load USBD modules if we are. + + monitor_event (); + + printk (KERN_INFO "monitor_modinit: finished\n"); + + return 0; +} + + +/** + * monitor_modexit - decommission bus interface driver + * + */ +static void __exit monitor_modexit (void) +{ + + printk (KERN_INFO "\n"); + printk (KERN_INFO "monitor_modexit:\n"); + + /* Stop any hotplug actions except unload */ + monitor_exiting = 1; + + /* + * Architecture specific - free appropriate IRQ + */ + + monitor_free_irq (); + +#ifdef CONFIG_PM + if (monitor.pm_info) { + pm_unregister_all(monitor_pm_event); + monitor.pm_info = NULL; + } +#endif + + /* + * Force unloading + */ + monitor_hotplug (MHA_UNLOAD); + +#ifdef CONFIG_USBD_PROCFS + // remove proc filesystem entry *AFTER* the last hotplug, + // in case it is being used for sync hotplug. + remove_proc_entry ("usb-monitor", NULL); +#endif + + return; +} + + +module_init (monitor_modinit); +module_exit (monitor_modexit); diff -uNr linux.org/drivers/usb/device/usbd-serialnumber.c linux/drivers/usb/device/usbd-serialnumber.c --- linux.org/drivers/usb/device/usbd-serialnumber.c Thu Jan 1 08:00:00 1970 +++ linux/drivers/usb/device/usbd-serialnumber.c Thu May 15 15:54:48 2003 @@ -0,0 +1,216 @@ +/* + * linux/drivers/usbd/usbd-serialnumber.c - USB Device Cable Monitor + * + * Copyright (c) 2000, 2001, 2002 Lineo + * Copyright (c) 2001 Hewlett Packard + * + * By: + * Stuart Lynne , + * Tom Rushworth , + * Bruce Balden + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + * + */ + + +#include +#include + +#include "usbd-export.h" +#include "usbd-build.h" +#include "usbd-module.h" + +MODULE_AUTHOR ("sl@lineo.com, tbr@lineo.com"); +MODULE_DESCRIPTION ("USB Device Monitor"); +USBD_MODULE_INFO ("usbd_monitor 0.2-alpha"); + +EXPORT_NO_SYMBOLS; + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + + +/* + * Architecture specific includes + */ + +#ifdef CONFIG_ARCH_SA1100 + +#include +#include + +#include "usbd.h" +#include "usbd-func.h" +#include "usbd-bus.h" +#include "hotplug.h" +#include "bi/sa1100.h" +#endif + +#if defined(CONFIG_PM) +#include +#endif + +#ifdef CONFIG_SA1100_BITSY +#include +#endif + +/* Module Parameters ************************************************************************* */ + +#ifdef CONFIG_USBD_PROCFS + +/* Proc Filesystem *************************************************************************** */ + +/* * + * usbd_device_proc_read - implement proc file system read. + * @file + * @buf + * @count + * @pos + * + * Standard proc file system read function. + * + * We let upper layers iterate for us, *pos will indicate which device to return + * statistics for. + */ +static ssize_t proc_read_serial (struct file *file, char *buf, size_t count, loff_t * pos) +{ + unsigned long page; + int len = 0; + int index; + + // get a page, max 4095 bytes of data... + if (!(page = get_free_page (GFP_KERNEL))) { + return -ENOMEM; + } + + len = 0; + index = (*pos)++; + + if (index == 0) { + len += sprintf ((char *) page + len, "Serial: "); + +#ifdef CONFIG_SA1100_BITSY + if (machine_is_bitsy ()) { + char serial_number[22]; + int i; + int j; + memset (&serial_number, 0, sizeof (serial_number)); + for (i = 0, j = 0; i < 20; i++, j++) { + char buf[4]; + h3600_eeprom_read (5 + i, buf, 2); + serial_number[j] = buf[1]; + } + len += sprintf ((char *) page + len, "%s", serial_number); + } +#endif + +#ifdef CONFIG_SA1100_CALYPSO + if (machine_is_calypso ()) { + __u32 eerom_serial; + int i; + if ((i = + CalypsoIicGet (IIC_ADDRESS_SERIAL0, (unsigned char *) &eerom_serial, + sizeof (eerom_serial)))) { + bus->serial_number = eerom_serial; + } + len += sprintf ((char *) page + len, "%x", eerom_serial); + } +#endif + + len += sprintf ((char *) page + len, "\n"); + } + + + if (len > count) { + len = -EINVAL; + } else if (len > 0 && copy_to_user (buf, (char *) page, len)) { + len = -EFAULT; + } + free_page (page); + return len; +} + +static struct file_operations proc_read_serial_ops = { + read:proc_read_serial, +}; + + +#endif + + +/* Module Init ******************************************************************************* */ + + +/** + * monitor_modinit - commission bus interface driver + * + */ +static int __init monitor_modinit (void) +{ + struct proc_dir_entry *p; + +#ifndef CONFIG_USBD_PROCFS + return -EINVAL; +#else + printk (KERN_INFO "usbdm: %s\n", __usbd_module_info); + + // create proc filesystem entry + if ((p = create_proc_entry ("usb-serial-number", 0, 0)) == NULL) + return -ENOMEM; + + p->proc_fops = &proc_read_serial_ops; + + printk (KERN_INFO "monitor_modinit: finished\n"); + + return 0; +#endif +} + + +/** + * monitor_modexit - decommission bus interface driver + * + */ +static void __exit monitor_modexit (void) +{ + + printk (KERN_INFO "\n"); + + remove_proc_entry ("usb-serial-number", NULL); + + printk (KERN_INFO "monitor_modexit:\n"); + + return; +} + + +module_init (monitor_modinit); +module_exit (monitor_modexit); diff -uNr linux.org/drivers/usb/device/usbd.c linux/drivers/usb/device/usbd.c --- linux.org/drivers/usb/device/usbd.c Thu Jan 1 08:00:00 1970 +++ linux/drivers/usb/device/usbd.c Thu May 15 15:54:48 2003 @@ -0,0 +1,1324 @@ +/* + * linux/drivers/usbd/usbd.c.c - USB Device Core Layer + * + * Copyright (c) 2000, 2001, 2002 Lineo + * Copyright (c) 2001 Hewlett Packard + * + * By: + * Stuart Lynne , + * Tom Rushworth , + * Bruce Balden + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + * + */ + + +/* + * Notes... + * + * 1. The order of loading must be: + * + * usb-device + * usb-function(s) + * usb-bus(s) + * + * 2. Loading usb-function modules allows them to register with the usb-device + * layer. This makes their usb configuration descriptors available. Currently + * function modules cannot be loaded after a bus module has been loaded. + * + * 3. Loading usb-bus modules causes them to register with the usb-device + * layer and then enable their upstream port. This in turn will cause the + * upstream host to enumerate this device. Note that bus modules can create + * multiple devices, one per physical interface. + * + * 4. The usb-device layer provides a default configuration for endpoint 0 for + * each device. + * + * 5. Additional configurations are added to support the configuration + * function driver when entering the configured state. + * + * 6. The usb-device maintains a list of configurations from the loaded + * function drivers. It will provide this to the USB HOST as part of the + * enumeration process and will add someof them as the active configuration as + * per the USB HOST instructions. + * + * 6. Two types of bus interface modules can be implemented: simple and compound. + * + * 7. Simple bus interface modules can only support a single function module. The + * first function module is used. + * + * 8. Compound bus interface modules can support multiple functions. When implemented + * these will use all available function modules. + * + */ + +#include +#include + +#include "usbd-export.h" +#include "usbd-build.h" +#include "usbd-module.h" + +MODULE_AUTHOR ("sl@lineo.com, tbr@lineo.com"); +MODULE_DESCRIPTION ("USB Device Core Support"); + +USBD_MODULE_INFO ("usbdcore 0.1"); + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "usbd.h" +#include "usbd-debug.h" +#include "usbd-func.h" +#include "usbd-bus.h" +#include "usbd-inline.h" + +#include "hotplug.h" + +#define MAX_INTERFACES 2 + +/* Module Parameters ************************************************************************* */ + +MODULE_PARM (maxstrings, "i"); +MODULE_PARM (dbg, "s"); + +MODULE_PARM_DESC (maxstrings, "Maximum number of strings to allow (0..255)"); +MODULE_PARM_DESC (dbg, "debug parameters"); + +int maxstrings = 20; +static char *dbg = NULL; + +/* Debug switches (module parameter "dbg=...") *********************************************** */ + +int dbgflg_usbdcore_init; +int dbgflg_usbdcore_ep0; +int dbgflg_usbdcore_rx; +int dbgflg_usbdcore_tx; +int dbgflg_usbdcore_usbe; +int dbgflg_usbdcore_urbmem; + +static debug_option dbg_table[] = { + {&dbgflg_usbdcore_init, NULL, "init", "initialization and termination"}, + {&dbgflg_usbdcore_ep0, NULL, "ep0", "End Point 0 (setup) packet handling"}, + {&dbgflg_usbdcore_rx, NULL, "rx", "USB RX (host->device) handling"}, + {&dbgflg_usbdcore_tx, NULL, "tx", "USB TX (device->host) handling"}, + {&dbgflg_usbdcore_usbe, NULL, "usbe", "USB events"}, + {&dbgflg_usbdcore_urbmem, NULL, "mem", "URB memory allocation"}, +#if 0 + {NULL, NULL, "hot", "hotplug actions"}, + {NULL, NULL, "mon", "USB conection monitor"}, +#endif + {NULL, NULL, NULL, NULL} +}; + +#define dbg_init(lvl,fmt,args...) dbgPRINT(dbgflg_usbdcore_init,lvl,fmt,##args) +#define dbg_ep0(lvl,fmt,args...) dbgPRINT(dbgflg_usbdcore_ep0,lvl,fmt,##args) +#define dbg_rx(lvl,fmt,args...) dbgPRINT(dbgflg_usbdcore_rx,lvl,fmt,##args) +#define dbg_tx(lvl,fmt,args...) dbgPRINT(dbgflg_usbdcore_tx,lvl,fmt,##args) +#define dbg_usbe(lvl,fmt,args...) dbgPRINT(dbgflg_usbdcore_usbe,lvl,fmt,##args) +#define dbg_urbmem(lvl,fmt,args...) dbgPRINT(dbgflg_usbdcore_urbmem,lvl,fmt,##args) + +/* Global variables ************************************************************************** */ + +struct usb_string_descriptor **usb_strings; + +int usb_devices; + +extern struct usb_function_driver ep0_driver; + +int registered_functions; +int registered_devices; + +char *usbd_device_events[] = { + "DEVICE_UNKNOWN", + "DEVICE_INIT", + "DEVICE_CREATE", + "DEVICE_HUB_CONFIGURED", + "DEVICE_RESET", + "DEVICE_ADDRESS_ASSIGNED", + "DEVICE_CONFIGURED", + "DEVICE_SET_INTERFACE", + "DEVICE_SET_FEATURE", + "DEVICE_CLEAR_FEATURE", + "DEVICE_DE_CONFIGURED", + "DEVICE_BUS_INACTIVE", + "DEVICE_BUS_ACTIVITY", + "DEVICE_POWER_INTERRUPTION", + "DEVICE_HUB_RESET", + "DEVICE_DESTROY", + "DEVICE_FUNCTION_PRIVATE", +}; + +char *usbd_device_states[] = { + "STATE_INIT", + "STATE_CREATED", + "STATE_ATTACHED", + "STATE_POWERED", + "STATE_DEFAULT", + "STATE_ADDRESSED", + "STATE_CONFIGURED", + "STATE_UNKNOWN", +}; + +char *usbd_device_requests[] = { + "GET STATUS", // 0 + "CLEAR FEATURE", // 1 + "RESERVED", // 2 + "SET FEATURE", // 3 + "RESERVED", // 4 + "SET ADDRESS", // 5 + "GET DESCRIPTOR", // 6 + "SET DESCRIPTOR", // 7 + "GET CONFIGURATION", // 8 + "SET CONFIGURATION", // 9 + "GET INTERFACE", // 10 + "SET INTERFACE", // 11 + "SYNC FRAME", // 12 +}; + +char *usbd_device_descriptors[] = { + "UNKNOWN", // 0 + "DEVICE", // 1 + "CONFIG", // 2 + "STRING", // 3 + "INTERFACE", // 4 + "ENDPOINT", // 5 + "DEVICE QUALIFIER", // 6 + "OTHER SPEED", // 7 + "INTERFACE POWER", // 8 +}; + +char *usbd_device_status[] = { + "USBD_OPENING", + "USBD_OK", + "USBD_SUSPENDED", + "USBD_CLOSING", +}; + +/* List support functions ******************************************************************** */ + +LIST_HEAD (function_drivers); // list of all registered function modules +LIST_HEAD (devices); // list of all registered devices + + +static inline struct usb_function_driver *list_entry_func (const struct list_head *le) +{ + return list_entry (le, struct usb_function_driver, drivers); +} + +static inline struct usb_device_instance *list_entry_device (const struct list_head *le) +{ + return list_entry (le, struct usb_device_instance, devices); +} + + +/* Support Functions ************************************************************************* */ + +/** + * usbd_hotplug - call a hotplug script + * @action: hotplug action + */ +int usbd_hotplug (char *interface, char *action) +{ +#ifdef CONFIG_HOTPLUG + dbg_init (1, "agent: usbd interface: %s action: %s", interface, action); + return hotplug ("usbd", interface, action); +#else + return (0); +#endif +} + + +/* Descriptor support functions ************************************************************** */ + + +/** + * usbd_get_string - find and return a string descriptor + * @index: string index to return + * + * Find an indexed string and return a pointer to a it. + */ +struct usb_string_descriptor *usbd_get_string (__u8 index) +{ + if (index >= maxstrings) { + return NULL; + } + return usb_strings[index]; +} + + +/** + * usbd_cancel_urb - cancel an urb being sent + * @urb: pointer to an urb structure + * + * Used by a USB Function driver to cancel an urb that is being + * sent. + */ +int usbd_cancel_urb (struct usb_device_instance *device, struct urb *urb) +{ + dbg_tx (3, "%p", urb); + if (!device->bus->driver->ops->cancel_urb) { + // XXX should we do usbd_dealloc_urb(urb); + return 0; + } + //urb->device->urbs_queued++; + return device->bus->driver->ops->cancel_urb (urb); +} + + +/* Access to device descriptor functions ***************************************************** */ + + +/* * + * usbd_device_function_instance - find a function instance for this device + * @device: + * @configuration: index to configuration, 0 - N-1 + * + * Get specifed device configuration. Index should be bConfigurationValue-1. + */ +struct usb_function_instance *usbd_device_function_instance (struct usb_device_instance *device, unsigned int port) +{ + //dbg_init(2,"device: %p port: %d functions: %d", device, port, device->functions); + if (port >= device->functions) { + dbg_init (0, "configuration out of range: %d %d", port, device->functions); + return NULL; + } + return device->function_instance_array + port; +} + + +/* * + * usbd_device_configuration_instance - find a configuration instance for this device + * @device: + * @configuration: index to configuration, 0 - N-1 + * + * Get specifed device configuration. Index should be bConfigurationValue-1. + */ +static struct usb_configuration_instance *usbd_device_configuration_instance (struct usb_device_instance *device, + unsigned int port, unsigned int configuration) +{ + struct usb_function_instance *function_instance; + struct usb_function_driver *function_driver; + + // XXX + configuration = configuration ? configuration - 1 : 0; + + if (!(function_instance = usbd_device_function_instance (device, port))) { + dbg_init (0, "function_instance NULL"); + return NULL; + } + if (!(function_driver = function_instance->function_driver)) { + dbg_init (0, "function_driver NULL"); + return NULL; + } + + if (configuration >= function_driver->configurations) { + dbg_init (0, "configuration out of range: %d %d %d", port, configuration, function_driver->configurations); + return NULL; + } + return function_driver->configuration_instance_array + configuration; +} + + +/* * + * usbd_device_interface_instance + * @device: + * @configuration: index to configuration, 0 - N-1 + * @interface: index to interface + * + * Return the specified interface descriptor for the specified device. + */ +struct usb_interface_instance *usbd_device_interface_instance (struct usb_device_instance *device, int port, int configuration, int interface) +{ + struct usb_configuration_instance *configuration_instance; + + if ((configuration_instance = usbd_device_configuration_instance (device, port, configuration)) == NULL) { + return NULL; + } + if (interface >= configuration_instance->interfaces) { + return NULL; + } + return configuration_instance->interface_instance_array + interface; +} + +/* * + * usbd_device_alternate_descriptor_list + * @device: + * @configuration: index to configuration, 0 - N-1 + * @interface: index to interface + * @alternate: alternate setting + * + * Return the specified alternate descriptor for the specified device. + */ +struct usb_alternate_instance *usbd_device_alternate_instance (struct usb_device_instance *device, int port, int configuration, int interface, int alternate) +{ + struct usb_interface_instance *interface_instance; + + if ((interface_instance = usbd_device_interface_instance (device, port, configuration, interface)) == NULL) { + return NULL; + } + + if (alternate >= interface_instance->alternates) { + return NULL; + } + + return interface_instance->alternates_instance_array + alternate; +} + + +/* * + * usbd_device_device_descriptor + * @device: which device + * @configuration: index to configuration, 0 - N-1 + * @port: which port + * + * Return the specified configuration descriptor for the specified device. + */ +struct usb_device_descriptor *usbd_device_device_descriptor (struct usb_device_instance *device, int port) +{ + struct usb_function_instance *function_instance; + if (!(function_instance = usbd_device_function_instance (device, port))) { + return NULL; + } + if (!function_instance->function_driver || !function_instance->function_driver->device_descriptor) { + return NULL; + } + return (function_instance->function_driver->device_descriptor); +} + + +/** + * usbd_device_configuration_descriptor + * @device: which device + * @port: which port + * @configuration: index to configuration, 0 - N-1 + * + * Return the specified configuration descriptor for the specified device. + */ +struct usb_configuration_descriptor *usbd_device_configuration_descriptor (struct + usb_device_instance + *device, int port, int configuration) +{ + struct usb_configuration_instance *configuration_instance; + if (!(configuration_instance = usbd_device_configuration_instance (device, port, configuration))) { + return NULL; + } + return (configuration_instance->configuration_descriptor); +} + + +/** + * usbd_device_interface_descriptor + * @device: which device + * @port: which port + * @configuration: index to configuration, 0 - N-1 + * @interface: index to interface + * @alternate: alternate setting + * + * Return the specified interface descriptor for the specified device. + */ +struct usb_interface_descriptor *usbd_device_interface_descriptor (struct usb_device_instance + *device, int port, int configuration, int interface, int alternate) +{ + struct usb_interface_instance *interface_instance; + if (!(interface_instance = usbd_device_interface_instance (device, port, configuration, interface))) { + return NULL; + } + if ((alternate < 0) || (alternate >= interface_instance->alternates)) { + return NULL; + } + return (interface_instance->alternates_instance_array[alternate].interface_descriptor); +} + + +/** + * usbd_device_class_descriptor_index + * @device: which device + * @port: which port + * @configuration: index to configuration, 0 - N-1 + * @interface: index to interface + * @index: which index + * + * Return the specified class descriptor for the specified device. + */ +struct usb_class_descriptor *usbd_device_class_descriptor_index (struct usb_device_instance *device, int port, int configuration, int interface, int alternate, int index) +{ + struct usb_alternate_instance *alternate_instance; + + if (!(alternate_instance = usbd_device_alternate_instance (device, port, configuration, interface, alternate))) { + return NULL; + } + if (index >= alternate_instance->classes) { + return NULL; + } + return *(alternate_instance->classes_descriptor_array + index); +} + + +/** + * usbd_device_endpoint_descriptor_index + * @device: which device + * @port: which port + * @configuration: index to configuration, 0 - N-1 + * @interface: index to interface + * @alternate: index setting + * @index: which index + * + * Return the specified endpoint descriptor for the specified device. + */ +struct usb_endpoint_descriptor *usbd_device_endpoint_descriptor_index (struct usb_device_instance + *device, int port, int configuration, int interface, int alternate, int index) +{ + struct usb_alternate_instance *alternate_instance; + + if (!(alternate_instance = usbd_device_alternate_instance (device, port, configuration, interface, alternate))) { + return NULL; + } + if (index >= alternate_instance->endpoints) { + return NULL; + } + return *(alternate_instance->endpoints_descriptor_array + index); +} + + +/** + * usbd_device_endpoint_transfersize + * @device: which device + * @port: which port + * @configuration: index to configuration, 0 - N-1 + * @interface: index to interface + * @index: which index + * + * Return the specified endpoint transfer size; + */ +int usbd_device_endpoint_transfersize (struct usb_device_instance *device, int port, int configuration, int interface, int alternate, int index) +{ + struct usb_alternate_instance *alternate_instance; + + if (!(alternate_instance = usbd_device_alternate_instance (device, port, configuration, interface, alternate))) { + return 0; + } + if (index >= alternate_instance->endpoints) { + return 0; + } + return *(alternate_instance->endpoint_transfersize_array + index); +} + + +/** + * usbd_device_endpoint_descriptor + * @device: which device + * @port: which port + * @configuration: index to configuration, 0 - N-1 + * @interface: index to interface + * @alternate: alternate setting + * @endpoint: which endpoint + * + * Return the specified endpoint descriptor for the specified device. + */ +struct usb_endpoint_descriptor *usbd_device_endpoint_descriptor (struct usb_device_instance *device, int port, int configuration, int interface, int alternate, int endpoint) +{ + struct usb_endpoint_descriptor *endpoint_descriptor; + int i; + + for (i = 0; !(endpoint_descriptor = usbd_device_endpoint_descriptor_index (device, port, configuration, interface, alternate, i)); i++) { + if (endpoint_descriptor->bEndpointAddress == endpoint) { + return endpoint_descriptor; + } + } + return NULL; +} + + +/** + * usbd_alloc_urb_data - allocate urb data buffer + * @urb: pointer to urb + * @len: size of buffer to allocate + * + * Allocate a data buffer for the specified urb structure. + */ +int usbd_alloc_urb_data (struct urb *urb, int len) +{ + len += 2; + urb->buffer_length = len; + urb->actual_length = 0; + if (len == 0) { + dbg_urbmem (0, "len == zero"); + return 0; + } + + if (urb->endpoint && urb->endpoint->endpoint_address && urb->function_instance && + urb->function_instance->function_driver->ops->alloc_urb_data) + { + dbg_urbmem (0, "urb->function->ops used"); + return urb->function_instance->function_driver->ops->alloc_urb_data (urb, len); + } + return !(urb->buffer = ckmalloc (len, GFP_ATOMIC)); +} + + +/** + * usbd_alloc_urb - allocate an URB appropriate for specified endpoint + * @device: device instance + * @function_instance: device instance + * @endpoint: endpoint + * @length: size of transfer buffer to allocate + * + * Allocate an urb structure. The usb device urb structure is used to + * contain all data associated with a transfer, including a setup packet for + * control transfers. + * + */ +struct urb *usbd_alloc_urb (struct usb_device_instance *device, struct usb_function_instance *function_instance, + __u8 endpoint_address, int length) +{ + int i; + + for (i = 0; i < device->bus->driver->max_endpoints; i++) { + struct usb_endpoint_instance *endpoint = device->bus->endpoint_array + i; + + dbg_urbmem (2, "i=%d epa=%d want %d", i, (endpoint->endpoint_address & 0x7f), endpoint_address); + if ((endpoint->endpoint_address & 0x7f) == endpoint_address) { + + struct urb *urb; + + dbg_urbmem (2, "[%d]: endpoint: %p %02x length: %d", i, endpoint, endpoint->endpoint_address, length); + + if (!(urb = ckmalloc (sizeof (struct urb), GFP_ATOMIC))) { + dbg_urbmem (0, "ckmalloc(%u,GFP_ATOMIC) failed", sizeof (struct urb)); + return NULL; + } + + urb->endpoint = endpoint; + urb->device = device; + urb->function_instance = function_instance; + urb_link_init (&urb->link); + + if (length) { + if (usbd_alloc_urb_data (urb, length)) { + dbg_urbmem (0, "usbd_alloc_urb_data(%u,GFP_ATOMIC) failed", length); + kfree (urb); + return NULL; + } + } else { + urb->buffer_length = urb->actual_length = 0; + } + dbg_urbmem(1, "[%d] urb: %p len: %d", endpoint_address, urb, length); + return urb; + } + } + dbg_urbmem (0, "can't find endpoint #%02x!", endpoint_address); + for (i = 0; i < device->bus->driver->max_endpoints; i++) { + struct usb_endpoint_instance *endpoint = device->bus->endpoint_array + i; + dbg_urbmem (0, "i=%d epa=%d want %d", i, (endpoint->endpoint_address & 0x7f), endpoint_address); + if ((endpoint->endpoint_address & 0x7f) == endpoint_address) { + dbg_urbmem (0, "found it, but too late"); + break; + } + } + return NULL; +} + +/** + * usbd_dealloc_urb - deallocate an URB and associated buffer + * @urb: pointer to an urb structure + * + * Deallocate an urb structure and associated data. + */ +void usbd_dealloc_urb (struct urb *urb) +{ + dbg_urbmem(1, "[%d] urb: %p len: %d", urb->endpoint->endpoint_address, urb, urb->buffer_length); + if (urb) { + if (urb->buffer) { + if (urb->function_instance && urb->function_instance->function_driver->ops->dealloc_urb_data) { + urb->function_instance->function_driver->ops->dealloc_urb_data (urb); + } + else { + kfree (urb->buffer); + } + } + kfree (urb); + } +} + +/** + * usbd_device_event - called to respond to various usb events + * @device: pointer to struct device + * @event: event to respond to + * + * Used by a Bus driver to indicate an event. + */ +void usbd_device_event_irq (struct usb_device_instance *device, usb_device_event_t event, int data) +{ + struct urb *urb; + + usb_device_state_t state; + + if (!device || !device->bus) { + dbg_usbe (1, "(%p,%d) NULL device or device->bus", device, event); + return; + } + + state = device->device_state; + + dbg_usbe (3, "-------------------------------------------------------------------------------------"); + + + dbg_usbe (1,"%s", USBD_DEVICE_EVENTS(event)); + + switch (event) { + case DEVICE_UNKNOWN: + break; + case DEVICE_INIT: + device->device_state = STATE_INIT; + break; + + case DEVICE_CREATE: + device->device_state = STATE_ATTACHED; + break; + + case DEVICE_HUB_CONFIGURED: + device->device_state = STATE_POWERED; + break; + + case DEVICE_RESET: + device->device_state = STATE_DEFAULT; + device->address = 0; + break; + + case DEVICE_ADDRESS_ASSIGNED: + device->device_state = STATE_ADDRESSED; + break; + + case DEVICE_CONFIGURED: + device->device_state = STATE_CONFIGURED; + break; + + case DEVICE_DE_CONFIGURED: + device->device_state = STATE_ADDRESSED; + break; + + case DEVICE_BUS_INACTIVE: + if (device->status != USBD_CLOSING) { + device->status = USBD_SUSPENDED; + } + break; + case DEVICE_BUS_ACTIVITY: + if (device->status != USBD_CLOSING) { + device->status = USBD_OK; + } + break; + + case DEVICE_SET_INTERFACE: + break; + case DEVICE_SET_FEATURE: + break; + case DEVICE_CLEAR_FEATURE: + break; + + case DEVICE_POWER_INTERRUPTION: + device->device_state = STATE_POWERED; + break; + case DEVICE_HUB_RESET: + device->device_state = STATE_ATTACHED; + break; + case DEVICE_DESTROY: + device->device_state = STATE_UNKNOWN; + break; + case DEVICE_FUNCTION_PRIVATE: + break; + } + dbg_usbe (3, "%s event: %d oldstate: %d newstate: %d status: %d address: %d", device->name, event, state, device->device_state, device->status, device->address);; + + // tell the bus interface driver + if (device->bus && device->bus->driver->ops->device_event) { + dbg_usbe (3, "calling bus->event"); + device->bus->driver->ops->device_event (device, event, data); + } +#if 0 + if (device->function_instance_array && (device->function_instance_array + port)->function_driver->ops->event) { + dbg_usbe (3, "calling function->event"); + (device->function_instance_array + port)->function_driver->ops->event (device, event); + } +#endif + + if (device->ep0 && device->ep0->function_driver->ops->event) { + dbg_usbe (3, "calling ep0->event"); + device->ep0->function_driver->ops->event (device, event, data); + } +#if 0 + // tell the bus interface driver + if (device->bus && device->bus->driver->ops->device_event) { + dbg_usbe (3, "calling bus->event"); + device->bus->driver->ops->device_event (device, event); + } +#endif + + // dbg_usbe(0, "FINISHED - NO FUNCTION BH"); + // return; + + // XXX + // queue an urb to endpoint zero + + dbg_usbe(1,"queueing event urb"); + if ((urb = usbd_alloc_urb (device, device->function_instance_array, 0, 0))) { + urb->event = event; + urb->data = data; + urb_append_irq (&(urb->endpoint->events), urb); + usbd_schedule_function_bh (device); + } + dbg_usbe (5, "FINISHED"); + return; +} + +void usbd_device_event (struct usb_device_instance *device, usb_device_event_t event, int data) +{ + unsigned long flags; + local_irq_save (flags); + usbd_device_event_irq (device, event, data); + local_irq_restore (flags); +} + +/** + * usbd_endpoint_halted + * @device: point to struct usb_device_instance + * @endpoint: endpoint to check + * + * Return non-zero if endpoint is halted. + */ +int usbd_endpoint_halted (struct usb_device_instance *device, int endpoint) +{ + if (!device->bus->driver->ops->endpoint_halted) { + return -EINVAL; + } + return device->bus->driver->ops->endpoint_halted (device, endpoint); +} + + +/** + * usbd_device_feature - set usb device feature + * @device: point to struct usb_device_instance + * @endpoint: which endpoint + * @feature: feature value + * + * Return non-zero if endpoint is halted. + */ +int usbd_device_feature (struct usb_device_instance *device, int endpoint, int feature) +{ + if (!device->bus->driver->ops->device_feature) { + return -EINVAL; + } + return device->bus->driver->ops->device_feature (device, endpoint, feature); +} + +#ifdef CONFIG_USBD_PROCFS +/* Proc Filesystem *************************************************************************** */ +/* * + * dohex + * + */ +static void dohexdigit (char *cp, unsigned char val) +{ + if (val < 0xa) { + *cp = val + '0'; + } else if ((val >= 0x0a) && (val <= 0x0f)) { + *cp = val - 0x0a + 'a'; + } +} + +/* * + * dohex + * + */ +static void dohexval (char *cp, unsigned char val) +{ + dohexdigit (cp++, val >> 4); + dohexdigit (cp++, val & 0xf); +} + +/* * + * dump_descriptor + */ +static int dump_descriptor (char *buf, char *sp, int num) +{ + int len = 0; + while (sp && num--) { + dohexval (buf, *sp++); + buf += 2; + *buf++ = ' '; + len += 3; + } + len++; + *buf = '\n'; + return len; +} + + +/* * + * usbd_device_proc_read - implement proc file system read. + * @file + * @buf + * @count + * @pos + * + * Standard proc file system read function. + * + * We let upper layers iterate for us, *pos will indicate which device to return + * statistics for. + */ +static ssize_t usbd_device_proc_read_functions (struct file *file, char *buf, size_t count, loff_t * pos) +{ + unsigned long page; + int len = 0; + int index; + + struct list_head *lhd; + struct usb_function_driver *function_driver; + struct usb_device_instance *device; + + // get a page, max 4095 bytes of data... + if (!(page = get_free_page (GFP_KERNEL))) { + return -ENOMEM; + } + + len = 0; + index = (*pos)++; + + if (index == 0) { + len += sprintf ((char *) page + len, "usb-device list\n"); + } + //read_lock(&usb_device_rwlock); + if (index == 1) { + list_for_each (lhd, &function_drivers) { + function_driver = list_entry_func (lhd); + if (function_driver->configuration_instance_array) { +#if 0 + { + int configuration; + // max 4095 bytes of data... + len += sprintf ((char *) page + len, "Function: %s", function_driver->name); + len += sprintf ((char *) page + len, "\tDescriptors: %d", function_driver->configurations); + len += sprintf ((char *) page + len, "\n"); + + for (configuration = 0; configuration < function_driver->configurations; configuration++) { + int interface; + struct usb_configuration_description *configuration = &function_driver->configuration_description[configuration]; + len += sprintf ((char *) page + len, "\tConfig: %d %s\n", configuration, configuration->iConfiguration); + + for (interface = 0; interface < configuration->interfaces; interface++) { + + int alternate; + struct usb_interface_instance *interface = &configuration->interface_instance_array[interface]; + + int endpoint; + struct usb_interface_description *interface = &configuration->interface_list[interface]; + len += sprintf ((char *) page + len, "\t\tInterface: %d\n", interface); + + + for (endpoint = 0; endpoint < interface->endpoints; endpoint++) { + // XXX + struct usb_endpoint_description + *endpoint = &interface->endpoint_list[endpoint]; + len += sprintf ((char *) page + len, "\t\t\tEndpoint: %d %d\n", endpoint, endpoint->bEndpointAddress); + } + + } + } + + break; + } +#endif + len += sprintf ((char *) page + len, "\nDevice descriptor "); + len += dump_descriptor ((char *) page + len, (char *) function_driver->device_descriptor, sizeof (struct usb_device_descriptor)); + { + int configuration; + struct usb_configuration_instance + *configuration_instance_array = function_driver->configuration_instance_array; + + for (configuration = 0; configuration < function_driver->configurations; configuration++) { + + int interface; + struct usb_configuration_descriptor + *configuration_descriptor = configuration_instance_array[configuration].configuration_descriptor; + + + len += sprintf ((char *) page + len, "\nConfiguration descriptor [%d ] ", configuration); + len += dump_descriptor ((char *) page + len, (char *) + configuration_descriptor, sizeof (struct usb_configuration_descriptor)); + + for (interface = 0; interface < configuration_instance_array[configuration].interfaces; interface++) { + + int alternate; + //int class; + struct usb_interface_instance + *interface_instance = configuration_instance_array[configuration].interface_instance_array + interface; + + dbg_init (1, "interface: %d:%d alternates: %d", configuration, interface, interface_instance->alternates); + + for (alternate = 0; alternate < interface_instance->alternates; alternate++) { + + int endpoint; + int class; + struct usb_alternate_instance + *alternate_instance = interface_instance->alternates_instance_array + alternate; + + struct usb_interface_descriptor + *interface_descriptor = alternate_instance->interface_descriptor; + + dbg_init (1, "alternate: %d:%d:%d classes: %d", configuration, interface, alternate, alternate_instance->classes); + + len += sprintf ((char *) page + len, "\nInterface descriptor [%d:%d:%d ] ", configuration, interface + 1, alternate); + + len += dump_descriptor ((char *) page + len, (char *) + interface_descriptor, sizeof (struct usb_interface_descriptor)); + + for (class = 0; class < alternate_instance->classes; class++) { + struct usb_class_descriptor + *class_descriptor = alternate_instance->classes_descriptor_array[class]; + len += sprintf ((char *) page + len, "Class descriptor [%d:%d:%d ] ", configuration, interface + 1, class); + + len += dump_descriptor ((char *) + page + len, (char *) + class_descriptor, *(char *) + class_descriptor); + } + + + dbg_init (1, "alternate: %d:%d:%d endpoints: %d", configuration, interface, alternate, alternate_instance->endpoints); + + for (endpoint = 0; endpoint < alternate_instance->endpoints; endpoint++) { + + struct + usb_endpoint_descriptor + *endpoint_descriptor = alternate_instance->endpoints_descriptor_array[endpoint]; + + dbg_init (1, "endpoint: %d:%d:%d:%d", configuration, interface, alternate, endpoint); + + len += + sprintf ((char *) page + len, "Endpoint descriptor [%d:%d:%d:%d] ", configuration, interface + 1, alternate, endpoint); + + len += dump_descriptor ((char *) + page + len, (char *) + endpoint_descriptor, sizeof (struct usb_endpoint_descriptor)); + } + } + } + } + break; + } + } + + } + } else if (index == 2) { + int i; + int k; + struct usb_string_descriptor *string_descriptor; + + len += sprintf ((char *) page + len, "\n\n"); + + if ((string_descriptor = usbd_get_string (0)) != NULL) { + len += sprintf ((char *) page + len, "String [%2d] ", 0); + + for (k = 0; k < (string_descriptor->bLength / 2) - 1; k++) { + len += sprintf ((char *) page + len, "%02x %02x ", string_descriptor->wData[k] >> 8, string_descriptor->wData[k] & 0xff); + len++; + } + len += sprintf ((char *) page + len, "\n"); + } + + for (i = 1; i < maxstrings; i++) { + + if ((string_descriptor = usbd_get_string (i)) != NULL) { + + len += sprintf((char *)page+len, "String [%2d:%2d] ", + i, string_descriptor->bLength); + + // bLength = sizeof(struct usb_string_descriptor) + 2*strlen(str)-2; + + for (k = 0; k < (string_descriptor->bLength / 2) - 1; k++) { + *(char *) (page + len) = (char) string_descriptor->wData[k]; + len++; + } + len += sprintf ((char *) page + len, "\n"); + } + } + } + + else if (index == 3) { + list_for_each (lhd, &devices) { + device = list_entry_device (lhd); + + len += sprintf ((char *) page + len, "\n\nBus: %s", device->bus->driver->name); + if (device->ep0) { + len += sprintf ((char *) page + len, " ep0: %s", device->ep0->function_driver->name); + } + if (device->function_instance_array) { + len += sprintf ((char *) page + len, " func: %s", device->function_instance_array->function_driver->name); + } + len += sprintf ((char *) page + len, " state: %s", usbd_device_states[device->device_state]); + len += sprintf ((char *) page + len, " addr: %d", device->address); + len += sprintf ((char *) page + len, " conf: %d", device->configuration); + len += sprintf ((char *) page + len, " int: %d", device->interface); + len += sprintf ((char *) page + len, " alt: %d", device->alternate); + + len += sprintf ((char *) page + len, "\n"); + break; + } + len += sprintf ((char *) page + len, "\n"); + } + //read_unlock(&usb_device_rwlock); + + if (len > count) { + len = -EINVAL; + } else if (len > 0 && copy_to_user (buf, (char *) page, len)) { + len = -EFAULT; + } + free_page (page); + return len; +} + +static struct file_operations usbd_device_proc_operations_functions = { + read:usbd_device_proc_read_functions, +}; + + +/* * + * usbd_device_proc_read - implement proc file system read. + * @file: xx + * @buf: xx + * @count: xx + * @pos: xx + * + * Standard proc file system read function. + * + * We let upper layers iterate for us, *pos will indicate which device to return + * statistics for. + */ +static ssize_t usbd_device_proc_read_devices (struct file *file, char *buf, size_t count, loff_t * pos) +{ + unsigned long page; + int len = 0; + int index; + + struct list_head *lhd; + struct usb_device_instance *device; + + // get a page, max 4095 bytes of data... + if (!(page = get_free_page (GFP_KERNEL))) { + return -ENOMEM; + } + + len = 0; + index = (*pos)++; + + if (index == 0) { + len += sprintf ((char *) page + len, "usb-device list\n"); + } + //read_lock(&usb_device_rwlock); + + if (index > 0) { + + list_for_each (lhd, &devices) { + int configuration; + + device = list_entry_device (lhd); + if (--index == 0) { + // max 4095 bytes of data... + len += sprintf ((char *) page + len, "Device: %s", device->bus->driver->name); + len += sprintf ((char *) page + len, "\n"); + len += sprintf ((char *) page + len, "%20s[ ] ", "Device:"); + len += dump_descriptor ((char *) page + len, (char *) device->device_descriptor, sizeof (struct usb_device_descriptor)); + break; + } + // iterate across configurations for this device + for (configuration = 0; index > 0; configuration++) { + int interface; + + struct usb_configuration_descriptor *configuration_descriptor; + + if (!(configuration_descriptor = usbd_device_configuration_descriptor (device, 0, configuration))) { + break; + } + + if (--index == 0) { + // max 4095 bytes of data... + len += sprintf ((char *) page + len, "%20s[%2d ] ", "Configuration:", configuration); + len += dump_descriptor ((char *) page + len, (char *) configuration_descriptor, sizeof (struct usb_configuration_descriptor)); + break; + } + // iterate across interfaces for this configurations + for (interface = 0; index > 0; interface++) { + int alternate; + struct usb_interface_instance *interface_instance; + + if (!(interface_instance = usbd_device_interface_instance (device, 0, configuration, interface))) { + break; + } + // itererate across alternates for this interafce + for (alternate = 0; index > 0; alternate++) { + + int endpoint; + struct usb_interface_descriptor + *interface_descriptor; + + if (!(interface_descriptor = usbd_device_interface_descriptor (device, 0, configuration, interface, alternate))) { + break; + } + + if (--index == 0) { + // max 4095 bytes of data... + len += sprintf ((char *) page + len, "%20s[%2d:%2d ] ", "Interface", configuration, interface); + len += dump_descriptor ((char *) page + len, (char *) + interface_descriptor, sizeof (struct usb_interface_descriptor)); + break; + } + // iterate across endpoints for this interface + for (endpoint = 0; index > 0; endpoint++) { + struct usb_endpoint_descriptor + *endpoint_descriptor; + + if (!(endpoint_descriptor = usbd_device_endpoint_descriptor_index (device, 0, configuration, interface, alternate, endpoint))) { + break; + } + if (--index == 0) { + len += sprintf ((char *) page + len, "%20s[%2d:%2d:%2d] ", "Endpoint", configuration, interface, endpoint); + + len += dump_descriptor ((char *) page + len, (char *) + endpoint_descriptor, sizeof (struct usb_endpoint_descriptor)); + break; + } + } + } + } + } + } + } + + if (index > 0) { + int i; + for (i = 1; i < maxstrings; i++) { + struct usb_string_descriptor *string_descriptor; + + if ((string_descriptor = usbd_get_string (i)) != NULL) { + + if (--index == 0) { + int k; + + len += sprintf ((char *) page + len, "%20s[%2d] ", "String", i); + + // bLength = sizeof(struct usb_string_descriptor) + 2*strlen(str)-2; + + for (k = 0; k < (string_descriptor->bLength / 2) - 1; k++) { + *(char *) (page + len) = (char) string_descriptor->wData[k]; + len++; + } + len += sprintf ((char *) page + len, "\n"); + } + } + } + } + //read_unlock(&usb_device_rwlock); + + if (len > count) { + len = -EINVAL; + } else if (len > 0 && copy_to_user (buf, (char *) page, len)) { + len = -EFAULT; + } + free_page (page); + return len; +} + +static struct file_operations usbd_device_proc_operations_devices = { + read:usbd_device_proc_read_devices, +}; +#endif + + + +/* Module init ******************************************************************************* */ + + +unsigned intset (unsigned int a, unsigned b) +{ + return (a ? a : b); +} + +char *strset (char *s, char *d) +{ + return ((s && strlen (s) ? s : d)); +} + +static int __init usbd_device_init (void) +{ +#if 0 + debug_option *op; +#endif + printk (KERN_INFO "usbdcore: %s (dbg=\"%s\")\n", __usbd_module_info, dbg ? dbg : ""); + +#if 0 + if (NULL != (op = find_debug_option (dbg_table, "hot"))) { + op->sub_table = usbd_hotplug_get_dbg_table (); + } + if (NULL != (op = find_debug_option (dbg_table, "mon"))) { + op->sub_table = usbd_monitor_get_dbg_table (); + } +#endif + if (0 != scan_debug_options ("usb-device", dbg_table, dbg)) { + return (-EINVAL); + } +#ifdef CONFIG_USBD_PROCFS + { + struct proc_dir_entry *p; + + // create proc filesystem entries + if ((p = create_proc_entry ("usb-functions", 0, 0)) == NULL) + return -ENOMEM; + p->proc_fops = &usbd_device_proc_operations_functions; + + if ((p = create_proc_entry ("usb-devices", 0, 0)) == NULL) + return -ENOMEM; + p->proc_fops = &usbd_device_proc_operations_devices; + } +#endif + return 0; +} + +static void __exit usbd_device_exit (void) +{ + printk (KERN_INFO "usbdcore: %s exiting\n", __usbd_module_info); + +#ifdef CONFIG_USBD_PROCFS + // remove proc filesystem entry + remove_proc_entry ("usb-functions", NULL); + remove_proc_entry ("usb-devices", NULL); +#endif +} + +module_init (usbd_device_init); +module_exit (usbd_device_exit); diff -uNr linux.org/drivers/usb/device/usbd.h linux/drivers/usb/device/usbd.h --- linux.org/drivers/usb/device/usbd.h Thu Jan 1 08:00:00 1970 +++ linux/drivers/usb/device/usbd.h Thu May 15 15:54:48 2003 @@ -0,0 +1,930 @@ +/* + * linux/drivers/usbd/usbd.h + * + * Copyright (c) 2000, 2001, 2002 Lineo + * Copyright (c) 2001 Hewlett Packard + * + * By: + * Stuart Lynne , + * Tom Rushworth , + * Bruce Balden + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + * + */ + + +/* + * USB Device Support structures + * + * Descriptors: + * + * Driver description: + * + * struct usb_device_description + * + * Driver instance configuration: + * + * struct usb_device_instance + * struct usb_endpoint_instance; + * + * struct usb_function_instance + * struct usb_bus_instance + * + * Data transfer: + * + * struct urb; + * + */ + +/* + * + * Notes... + * + * 1. The order of loading must be: + * + * usb-device default driver for endpoint 0 + * usb-function(s) one or more function drivers + * usb-bus-interface(s) one or more bus interface drivers + * + * 2. Loading usb-function modules allows them to register with the + * usb-device layer. This makes their usb configuration descriptors + * available. + * + * 3. Loading usb-bus-interface modules causes them to register with + * the usb-device layer. + * + * 4. The last action from loading a usb-bus-interface driver is to enable + * their upstream port. This in turn will cause the upstream host to + * enumerate this device. + * + * 5. The usb-device layer provides a default configuration for endpoint 0 for + * each device. + * + * 6. Additional configurations are added to support the configuration + * function driver when entering the configured state. + * + * 7. The usb-device maintains a list of configurations from the loaded + * function drivers. It will provide this to the USB HOST as part of the + * enumeration process and will add someof them as the active configuration + * as per the USB HOST instructions. + * + * + */ + +#ifndef MODULE +#undef __init +#define __init +#undef __exit +#define __exit +#undef GET_USE_COUNT +#define GET_USE_COUNT(module) 0 +#undef THIS_MODULE +#define THIS_MODULE 0 +#endif + + +#ifndef MIN +#define MIN(a,b) ((a) < (b) ? (a) : (b)) +#endif +#ifndef MAX +#define MAX(a,b) ((a) > (b) ? (a) : (b)) +#endif + +/* Overview + * + * USB Descriptors are used to build a configuration database for each USB + * Function driver. + * + * USB Function Drivers register a configuration that may be used by the + * default endpoint 0 driver to provide a USB HOST with options. + * + * USB Bus Interface drivers find and register specific physical bus + * interfaces. + * + * The USB Device layer creates and maintains a device structure for each + * physical interface that specifies the bus interface driver, shows possible + * configurations, specifies the default endpoint 0 driver, active + * configured function driver and configured endpoints. + * + * + */ + +struct urb; + +struct usb_endpoint_instance; +struct usb_configuration_description; +struct usb_function_driver; +struct usb_function_instance; + +struct usb_device_instance; + +struct usb_bus_driver; +struct usb_bus_instance; + + +/* USB definitions + */ +#if 0 +#define USB_ENDPOINT_CONTROL 0x00 +#define USB_ENDPOINT_ISOC 0x01 +#define USB_ENDPOINT_BULK 0x02 +#define USB_ENDPOINT_INT 0x03 + +#define USB_CONFIG_REMOTEWAKE 0x20 +#define USB_CONFIG_SELFPOWERED 0x40 +#define USB_CONFIG_BUSPOWERED 0x80 + + +#define USB_REQUEST_GET_STATUS 0x01 +#define USB_REQUEST_XXXXXXXXXXXXE 0x02 +#define USB_REQUEST_CLEAR_FEATURE 0x03 +#define USB_REQUEST_SET_FEATURE 0x04 +#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_DESCRIPTOR_DEVICE 0x01 +#define USB_DESCRIPTOR_CONFIG 0x02 +#define USB_DESCRIPTOR_STRING 0x03 +#define USB_DESCRIPTOR_INTERFACE 0x04 +#define USB_DESCRIPTOR_ENDPOINT 0x05 +#endif + + +/* + * Device and/or Interface Class codes + */ +#define USB_CLASS_PER_INTERFACE 0 /* for DeviceClass */ +#define USB_CLASS_AUDIO 1 +#define USB_CLASS_COMM 2 +#define USB_CLASS_HID 3 +#define USB_CLASS_PHYSICAL 5 +#define USB_CLASS_PRINTER 7 +#define USB_CLASS_MASS_STORAGE 8 +#define USB_CLASS_HUB 9 +#define USB_CLASS_DATA 10 +#define USB_CLASS_APP_SPEC 0xfe +#define USB_CLASS_VENDOR_SPEC 0xff + +/* + * USB types + */ +#define USB_TYPE_STANDARD (0x00 << 5) +#define USB_TYPE_CLASS (0x01 << 5) +#define USB_TYPE_VENDOR (0x02 << 5) +#define USB_TYPE_RESERVED (0x03 << 5) + +/* + * USB recipients + */ +#define USB_RECIP_DEVICE 0x00 +#define USB_RECIP_INTERFACE 0x01 +#define USB_RECIP_ENDPOINT 0x02 +#define USB_RECIP_OTHER 0x03 + +/* + * USB directions + */ +#define USB_DIR_OUT 0 +#define USB_DIR_IN 0x80 + +/* + * Descriptor types + */ +#define USB_DT_DEVICE 0x01 +#define USB_DT_CONFIG 0x02 +#define USB_DT_STRING 0x03 +#define USB_DT_INTERFACE 0x04 +#define USB_DT_ENDPOINT 0x05 + +#define USB_DT_HID (USB_TYPE_CLASS | 0x01) +#define USB_DT_REPORT (USB_TYPE_CLASS | 0x02) +#define USB_DT_PHYSICAL (USB_TYPE_CLASS | 0x03) +#define USB_DT_HUB (USB_TYPE_CLASS | 0x09) + +/* + * Descriptor sizes per descriptor type + */ +#define USB_DT_DEVICE_SIZE 18 +#define USB_DT_CONFIG_SIZE 9 +#define USB_DT_INTERFACE_SIZE 9 +#define USB_DT_ENDPOINT_SIZE 7 +#define USB_DT_ENDPOINT_AUDIO_SIZE 9 /* Audio extension */ +#define USB_DT_HUB_NONVAR_SIZE 7 +#define USB_DT_HID_SIZE 9 + +/* + * Endpoints + */ +#define USB_ENDPOINT_NUMBER_MASK 0x0f /* in bEndpointAddress */ +#define USB_ENDPOINT_DIR_MASK 0x80 + +#define USB_ENDPOINT_XFERTYPE_MASK 0x03 /* in bmAttributes */ +#define USB_ENDPOINT_XFER_CONTROL 0 +#define USB_ENDPOINT_XFER_ISOC 1 +#define USB_ENDPOINT_XFER_BULK 2 +#define USB_ENDPOINT_XFER_INT 3 + +/* + * USB Packet IDs (PIDs) + */ +#define USB_PID_UNDEF_0 0xf0 +#define USB_PID_OUT 0xe1 +#define USB_PID_ACK 0xd2 +#define USB_PID_DATA0 0xc3 +#define USB_PID_PING 0xb4 /* USB 2.0 */ +#define USB_PID_SOF 0xa5 +#define USB_PID_NYET 0x96 /* USB 2.0 */ +#define USB_PID_DATA2 0x87 /* USB 2.0 */ +#define USB_PID_SPLIT 0x78 /* USB 2.0 */ +#define USB_PID_IN 0x69 +#define USB_PID_NAK 0x5a +#define USB_PID_DATA1 0x4b +#define USB_PID_PREAMBLE 0x3c /* Token mode */ +#define USB_PID_ERR 0x3c /* USB 2.0: handshake mode */ +#define USB_PID_SETUP 0x2d +#define USB_PID_STALL 0x1e +#define USB_PID_MDATA 0x0f /* USB 2.0 */ + +/* + * Standard requests + */ +#define USB_REQ_GET_STATUS 0x00 +#define USB_REQ_CLEAR_FEATURE 0x01 +#define USB_REQ_SET_FEATURE 0x03 +#define USB_REQ_SET_ADDRESS 0x05 +#define USB_REQ_GET_DESCRIPTOR 0x06 +#define USB_REQ_SET_DESCRIPTOR 0x07 +#define USB_REQ_GET_CONFIGURATION 0x08 +#define USB_REQ_SET_CONFIGURATION 0x09 +#define USB_REQ_GET_INTERFACE 0x0A +#define USB_REQ_SET_INTERFACE 0x0B +#define USB_REQ_SYNCH_FRAME 0x0C + +#define USBD_DEVICE_REQUESTS(x) (((unsigned int)x <= USB_REQ_SYNCH_FRAME) ? usbd_device_requests[x] : "UNKNOWN") + +/* + * HID requests + */ +#define USB_REQ_GET_REPORT 0x01 +#define USB_REQ_GET_IDLE 0x02 +#define USB_REQ_GET_PROTOCOL 0x03 +#define USB_REQ_SET_REPORT 0x09 +#define USB_REQ_SET_IDLE 0x0A +#define USB_REQ_SET_PROTOCOL 0x0B + + +/* + * USB Spec Release number + */ + +#define USB_BCD_VERSION 0x0200 + + +/* + * Device Requests (c.f Table 9-2) + */ + +#define USB_REQ_DIRECTION_MASK 0x80 +#define USB_REQ_TYPE_MASK 0x60 +#define USB_REQ_RECIPIENT_MASK 0x1f + +#define USB_REQ_DEVICE2HOST 0x80 +#define USB_REQ_HOST2DEVICE 0x00 + +#define USB_REQ_TYPE_STANDARD 0x00 +#define USB_REQ_TYPE_CLASS 0x20 +#define USB_REQ_TYPE_VENDOR 0x40 + +#define USB_REQ_RECIPIENT_DEVICE 0x00 +#define USB_REQ_RECIPIENT_INTERFACE 0x01 +#define USB_REQ_RECIPIENT_ENDPOINT 0x02 +#define USB_REQ_RECIPIENT_OTHER 0x03 + +/* + * get status bits + */ + +#define USB_STATUS_SELFPOWERED 0x01 +#define USB_STATUS_REMOTEWAKEUP 0x02 + +#define USB_STATUS_HALT 0x01 + +/* + * descriptor types + */ + +#define USB_DESCRIPTOR_TYPE_DEVICE 0x01 +#define USB_DESCRIPTOR_TYPE_CONFIGURATION 0x02 +#define USB_DESCRIPTOR_TYPE_STRING 0x03 +#define USB_DESCRIPTOR_TYPE_INTERFACE 0x04 +#define USB_DESCRIPTOR_TYPE_ENDPOINT 0x05 +#define USB_DESCRIPTOR_TYPE_DEVICE_QUALIFIER 0x06 +#define USB_DESCRIPTOR_TYPE_OTHER_SPEED_CONFIGURATION 0x07 +#define USB_DESCRIPTOR_TYPE_INTERFACE_POWER 0x08 + +#define USBD_DEVICE_DESCRIPTORS(x) (((unsigned int)x <= USB_DESCRIPTOR_TYPE_INTERFACE_POWER) ? \ + usbd_device_descriptors[x] : "UNKNOWN") + +/* + * standard feature selectors + */ +#define USB_ENDPOINT_HALT 0x00 +#define USB_DEVICE_REMOTE_WAKEUP 0x01 +#define USB_TEST_MODE 0x02 + + +/* USB Requests + * + */ + +struct usb_device_request { + __u8 bmRequestType; + __u8 bRequest; + __u16 wValue; + __u16 wIndex; + __u16 wLength; +} __attribute__ ((packed)); + + +/* USB Status + * + */ +typedef enum urb_send_status { + SEND_IN_PROGRESS, + SEND_FINISHED_OK, + SEND_FINISHED_ERROR, + RECV_READY, + RECV_OK, + RECV_ERROR +} urb_send_status_t; + +/* + * Structure member address manipulation macros. + * These are used by client code (code using the urb_link routines), since + * the urb_link structure is embedded in the client data structures. + * + * Note: a macro offsetof equivalent to member_offset is defined in stddef.h + * but this is kept here for the sake of portability. + * + * p2surround returns a pointer to the surrounding structure given + * type of the surrounding structure, the name memb of the structure + * member pointed at by ptr. For example, if you have: + * + * struct foo { + * int x; + * float y; + * char z; + * } thingy; + * + * char *cp = &thingy.z; + * + * then + * + * &thingy == p2surround(struct foo, z, cp) + * + * Clear? + */ + +#define _cv_(ptr) ((char*)(void*)(ptr)) +#define member_offset(type,memb) (_cv_(&(((type*)0)->memb))-(char*)0) +#define p2surround(type,memb,ptr) ((type*)(void*)(_cv_(ptr)-member_offset(type,memb))) + +typedef struct urb_link { + struct urb_link *next; + struct urb_link *prev; +} urb_link; + +struct urb; + +/* + * Device Events + * + * These are defined in the USB Spec (c.f USB Spec 2.0 Figure 9-1). + * + * There are additional events defined to handle some extra actions we need + * to have handled. + * + */ +typedef enum usb_device_event { + + DEVICE_UNKNOWN, // bi - unknown event + DEVICE_INIT, // bi - initialize + DEVICE_CREATE, // bi - + DEVICE_HUB_CONFIGURED, // bi - bus has been plugged int + DEVICE_RESET, // bi - hub has powered our port + + DEVICE_ADDRESS_ASSIGNED, // ep0 - set address setup received + DEVICE_CONFIGURED, // ep0 - set configure setup received + DEVICE_SET_INTERFACE, // ep0 - set interface setup received + + DEVICE_SET_FEATURE, // ep0 - set feature setup received + DEVICE_CLEAR_FEATURE, // ep0 - clear feature setup received + + DEVICE_DE_CONFIGURED, // ep0 - set configure setup received for ?? + + DEVICE_BUS_INACTIVE, // bi - bus in inactive (no SOF packets) + DEVICE_BUS_ACTIVITY, // bi - bus is active again + + DEVICE_POWER_INTERRUPTION, // bi - hub has depowered our port + DEVICE_HUB_RESET, // bi - bus has been unplugged + DEVICE_DESTROY, // bi - device instance should be destroyed + + DEVICE_FUNCTION_PRIVATE, // function - private + +} usb_device_event_t; + +#define USBD_DEVICE_EVENTS(x) (((unsigned int)x <= DEVICE_FUNCTION_PRIVATE) ? \ + usbd_device_events[x] : "UNKNOWN") + + +/* Endpoint configuration + * + * Per endpoint configuration data. Used to track which function driver owns + * an endpoint. + * + */ +struct usb_endpoint_instance { + int endpoint_address; // logical endpoint address + + // control + int status; // halted + + struct urb_link events; // events + int state; // available for use by bus interface driver + + // receive side + struct urb_link rcv; // received urbs + struct urb_link rdy; // empty urbs ready to receive + struct urb *rcv_urb; // active urb + int rcv_attributes; // copy of bmAttributes from endpoint descriptor + int rcv_packetSize; // maximum packet size from endpoint descriptor + int rcv_transferSize; // maximum transfer size from function driver + int rcv_interrupts; + + // transmit side + struct urb_link tx; // urbs ready to transmit + struct urb_link done; // transmitted urbs + struct urb *tx_urb; // active urb + int tx_attributes; // copy of bmAttributes from endpoint descriptor + int tx_packetSize; // maximum packet size from endpoint descriptor + int tx_transferSize; // maximum transfer size from function driver + int tx_interrupts; + + int sent; // data already sent + int last; // data sent in last packet XXX do we need this + int errors; // number of tx errors seen + + int ep0_interrupts; +}; + + +/* USB Data structure - for passing data around. + * + * This is used for both sending and receiving data. + * + * The callback function is used to let the function driver know when + * transmitted data has been sent. + * + * The callback function is set by the alloc_recv function when an urb is + * allocated for receiving data for an endpoint and used to call the + * function driver to inform it that data has arrived. + */ + +struct urb { + + struct usb_endpoint_instance *endpoint; + + struct usb_device_instance *device; + struct usb_function_instance *function_instance; + + __u8 pid; // PID of received packet (SETUP or OUT) + struct usb_device_request device_request; // contents of received SETUP packet + + unsigned char *buffer; // data received (OUT) or being sent (IN) + unsigned int buffer_length; + unsigned int actual_length; + + + struct list_head urbs; // linked list of urbs(s) + + void *privdata; + + struct urb_link link; // embedded struct for circular doubly linked list of urbs + + urb_send_status_t status; + time_t jiffies; + usb_device_event_t event; + int data; +}; + + +/* What to do with submitted urb + * + */ +typedef enum urb_submit { + SUBMIT_SEND, // send buffer as DATA (control) or IN (bulk/interrupt) + SUBMIT_OK, // send zero length packet to indicate succes (control) + SUBMIT_STALL // indicate a request error +} urb_submit_t; + + +/* + * Device State (c.f USB Spec 2.0 Figure 9-1) + * + * What state the usb device is in. + * + * Note the state does not change if the device is suspended, we simply set a + * flag to show that it is suspended. + * + */ +typedef enum usb_device_state { + STATE_INIT, // just initialized + STATE_CREATED, // just created + STATE_ATTACHED, // we are attached + STATE_POWERED, // we have seen power indication (electrical bus signal) + STATE_DEFAULT, // we been reset + STATE_ADDRESSED, // we have been addressed (in default configuration) + STATE_CONFIGURED, // we have seen a set configuration device command + STATE_UNKNOWN, // destroyed +} usb_device_state_t; + +#define USBD_DEVICE_STATE(x) (((unsigned int)x <= STATE_UNKNOWN) ? usbd_device_states[x] : "UNKNOWN") + +/* + * Device status + * + * Overall state + */ +typedef enum usb_device_status { + USBD_OPENING, // we are currently opening + USBD_OK, // ok to use + USBD_SUSPENDED, // we are currently suspended + USBD_CLOSING, // we are currently closing +} usb_device_status_t; + +#define USBD_DEVICE_STATUS(x) (((unsigned int)x <= USBD_CLOSING) ? usbd_device_status[x] : "UNKNOWN") + +#if 0 +typedef enum usb_module_status { + MODULE_OPENED, + MODULE_ACTIVE, + MODULE_SUSPENDED, + MODULE_CLOSING, + MODULE_CLOSED, +} usb_module_status_t; +#endif + + +/* BUS Configuration + * + * These are used by the ep0 function driver to indicate type of + * configuratin change that should take place + */ +typedef enum usb_bus_change { + BUS_RESET, // error occurred, reset to STATE_UNKNOWN + BUS_ADDRESS, // set address device command received + BUS_CONFIGURE, // set configuration device command received + BUS_INTERFACE, // set interface device command received + BUS_CLOSE // please shutdown +} usb_bus_change_t; + +/* Function Configuration + * + * These are used by the ep0 function driver to indicate type of + * configuratin change that should take place + */ +typedef enum usb_function_change { + FUNCTION_START, // open function + FUNCTION_STOP // close function +} usb_function_change_t; + + +/* USB Device Instance + * + * For each physical bus interface we create a logical device structure. This + * tracks all of the required state to track the USB HOST's view of the device. + * + * Keep track of the device configuration for a real physical bus interface, + * this includes the bus interface, multiple function drivers, the current + * configuration and the current state. + * + * This will show: + * the specific bus interface driver + * the default endpoint 0 driver + * the configured function driver + * device state + * device status + * endpoint list + */ + +struct usb_device_instance { + + // generic + char *name; + struct usb_device_descriptor *device_descriptor; // per device descriptor + + // bus interface + struct usb_bus_instance *bus; // which bus interface driver + + // function driver(s) + struct usb_function_instance *ep0; // ep0 configuration + + // function configuration descriptors + unsigned int functions; //configurations; + struct usb_function_instance *function_instance_array; + + // device state + usb_device_state_t device_state; // current USB Device state + usb_device_state_t device_previous_state; // current USB Device state + + __u8 address; // current address (zero is default) + __u8 configuration; // current show configuration (zero is default) + __u8 interface; // current interface (zero is default) + __u8 alternate; // alternate flag + + usb_device_status_t status; // device status + + int urbs_queued; // number of submitted urbs + + // housekeeping + struct list_head devices; // linked list usb_device_instance(s) + + unsigned char dev_addr[ETH_ALEN]; + + struct tq_struct device_bh; // runs as bottom half, equivalent to interrupt time + struct tq_struct function_bh; // runs as process + + // Shouldn't need to make this atomic, all we need is a change indicator + unsigned long usbd_rxtx_timestamp; + unsigned long usbd_last_rxtx_timestamp; + +}; + + + +/* Function configuration structure + * + * This is allocated for each configured instance of a function driver. + * + * It stores pointers to the usb_function_driver for the appropriate function, + * and pointers to the USB HOST requested usb_configuration_description and + * usb_interface_description. + * + * The privdata pointer may be used by the function driver to store private + * per instance state information. + * + */ +struct usb_function_instance { + + //struct usb_device_instance *device; + + struct usb_function_driver *function_driver; + void *privdata; // private data for the function +}; + + +/* Bus Interface configuration structure + * + * This is allocated for each configured instance of a bus interface driver. + * + * It contains a pointer to the appropriate bus interface driver. + * + * The privdata pointer may be used by the bus interface driver to store private + * per instance state information. + */ +struct usb_bus_instance { + + struct usb_bus_driver *driver; + struct usb_device_instance *device; + struct usb_endpoint_instance *endpoint_array; // array of available configured endpoints + + unsigned int serial_number; + char *serial_number_str; + void *privdata; // private data for the bus interface + +}; + + +/** + * usbd_hotplug - call a hotplug script + * @interface: hotplug action + * @action: hotplug action + */ +int usbd_hotplug (char *, char *); + + +/* USB-Device + * + * The USB Device layer has three components. + * + * - maintain a list of function drivers + * - create and manage each device instance based on bus interface registrations + * - implement the default endpoint 0 control driver + * + * Function drivers register speculatively. Their use depends in the end on + * have one of their configurations being selected by the USB HOST. + * + * Bus Interface drivers register specifically. For each physical device + * they find they register, thereby creating a usb_device_instance. + * + * For each bus interface configuration registered the USB Device layer will + * create device configuration instance. The default endpoint 0 driver will + * be automatically installed to handle SETUP packets. + * + * Bus Interface drivers should not enable their upstream port until the bus + * interface registration has completed. + * + */ + +extern char *usbd_device_events[]; +extern char *usbd_device_states[]; +extern char *usbd_device_status[]; +extern char *usbd_device_requests[]; +extern char *usbd_device_descriptors[]; + + +/* function driver registration + * + * Called by function drivers to register themselves when loaded + * or de-register when unloading. + */ +//int usbd_strings_init(void); +int usbd_register_function (struct usb_function_driver *); +void usbd_deregister_function (struct usb_function_driver *); + +/* bus driver registration + * + * Called by bus interface drivers to register themselves when loaded + * or de-register when unloading. + */ +struct usb_bus_instance *usbd_register_bus (struct usb_bus_driver *); +void usbd_deregister_bus (struct usb_bus_instance *); + +/* device + * + * Called by bus interface drivers to register a virtual device for a + * physical interface that has been detected. Typically when actually + * plugged into a usb bus. The device can be de-register, typically + * when unplugged from the usb bus. + */ +struct usb_device_instance *usbd_register_device (char *, struct usb_bus_instance *, int); +void usbd_deregister_device (struct usb_device_instance *); + +/* + * usbd_configure_device is used by function drivers (usually the control endpoint) + * to change the device configuration. + * + * usbd_device_event is used by bus interface drivers to tell the higher layers that + * certain events have taken place. + */ +//int usbd_configure_device(usb_bus_change_t, struct usb_device_instance *); +void usbd_device_event_irq (struct usb_device_instance *conf, usb_device_event_t, int); +void usbd_device_event (struct usb_device_instance *conf, usb_device_event_t, int); + + +int usbd_endpoint_halted (struct usb_device_instance *, int); +int usbd_device_feature (struct usb_device_instance *, int, int); + +/* urb allocation + * + * Used to allocate and de-allocate urb structures. + */ +int usbd_alloc_urb_data (struct urb *, int); +struct urb *usbd_alloc_urb (struct usb_device_instance *, struct usb_function_instance *, __u8, int length); +//static __inline__ void usbd_dealloc_urb(struct urb *); + +/* + * Detach and return the first urb in a list with a distinguished + * head "hd", or NULL if the list is empty. + */ +//static __inline__ struct urb *first_urb_detached_irq(urb_link *); + +/* + * Detach and return the first urb in a list with a distinguished + * head "hd", or NULL if the list is empty. + */ +// XXX XXX need to implement +//static __inline__ struct urb *usbd_next_rcv_urb(struct usb_endpoint_instance *); + + + +/* + * usb_send_urb is used to submit a data urb for sending. + */ +//static __inline__ int usbd_send_urb(struct urb *); + +/* + * usb_cancel_urb is used to cancel a previously submitted data urb for sending. + */ +int usbd_cancel_urb (struct usb_device_instance *, struct urb *); + +/* + * usb_receive_setup is used by the bus interface driver to pass a SETUP + * packet into the the system. + */ +//static __inline__ int usbd_recv_setup(struct urb *); + +/** + * usbd_rcv_complete_irq - complete a receive + * @endpoint: + * @len: + * @status: + * + * Called from rcv interrupt to complete. + */ +//static __inline__ void usbd_rcv_complete_irq(struct usb_endpoint_instance *, int, int ); + +/** + * usbd_tx_complete_irq - complete a transmit + * @endpoint: + * + * Called from tx interrupt to complete. + */ +//static __inline__ void usbd_tx_complete_irq(struct usb_endpoint_instance *, int ); + +/* + * usb_receive_urb is used by the bus interface driver to pass a + * DATA packet into the the system. + */ +//static __inline__ void usbd_recv_urb_irq(struct urb *); + +/* + * usb_urb_sent is used by the bus interface driver to signal that + * an urb has been sent + */ +//static void usbd_urb_sent_irq(struct urb *, int); + + +/* descriptors + * + * Various ways of finding descriptors based on the current device and any + * possible configuration / interface / endpoint for it. + */ +struct usb_configuration_descriptor *usbd_device_configuration_descriptor (struct usb_device_instance *, int, int); + +struct usb_function_instance *usbd_device_function_instance (struct usb_device_instance *, unsigned int); + +struct usb_interface_instance *usbd_device_interface_instance (struct usb_device_instance *, int, int, int); + +struct usb_alternate_instance *usbd_device_alternate_instance (struct usb_device_instance *, int, int, int, int); + +struct usb_interface_descriptor *usbd_device_interface_descriptor (struct usb_device_instance *, int, int, int, int); + +struct usb_endpoint_descriptor *usbd_device_endpoint_descriptor_index (struct usb_device_instance *, int, int, int, int, int); + +struct usb_class_descriptor *usbd_device_class_descriptor_index (struct usb_device_instance *, int, int, int, int, int); + +struct usb_endpoint_descriptor *usbd_device_endpoint_descriptor (struct usb_device_instance *, int, int, int, int, int); + +int usbd_device_endpoint_transfersize (struct usb_device_instance *, int, int, int, int, int); + +struct usb_string_descriptor *usbd_get_string (__u8); + +struct usb_device_descriptor *usbd_device_device_descriptor (struct usb_device_instance *, int); + + +void usbd_flush_tx (struct usb_endpoint_instance *); +void usbd_fill_rcv (struct usb_device_instance *, struct usb_endpoint_instance *, int); + +void usbd_flush_rcv (struct usb_endpoint_instance *); +void usbd_flush_ep (struct usb_endpoint_instance *); + + + +static __inline__ void *ckmalloc (int n, int f) +{ + void *p; + if ((p = kmalloc (n, f)) == NULL) { + return NULL; + } + memset (p, 0, n); + return p; +} + +static __inline__ char *strdup (char *str) +{ + int n; + char *s; + if (str && (n = strlen (str) + 1) && (s = kmalloc (n, GFP_ATOMIC))) { + return strcpy (s, str); + } + return NULL; +} diff -uNr linux.org/drivers/video/fbmem.c linux/drivers/video/fbmem.c --- linux.org/drivers/video/fbmem.c Mon May 12 14:25:28 2003 +++ linux/drivers/video/fbmem.c Fri May 16 17:47:11 2003 @@ -652,7 +652,8 @@ if (boot_cpu_data.x86 > 3) pgprot_val(vma->vm_page_prot) |= _PAGE_PCD; #elif defined(__arm__) || defined(__mips__) - vma->vm_page_prot = pgprot_noncached(vma->vm_page_prot); + //vma->vm_page_prot = pgprot_noncached(vma->vm_page_prot); + vma->vm_page_prot = pgprot_noncached_buffered(vma->vm_page_prot); #elif defined(__sh__) pgprot_val(vma->vm_page_prot) &= ~_PAGE_CACHABLE; #elif defined(__ia64__) diff -uNr linux.org/drivers/video/pxafb.c linux/drivers/video/pxafb.c --- linux.org/drivers/video/pxafb.c Mon May 12 14:25:28 2003 +++ linux/drivers/video/pxafb.c Fri May 16 17:47:11 2003 @@ -1228,7 +1228,7 @@ * frame buffer */ fbi->map_size = PAGE_ALIGN(fbi->fb.fix.smem_len + PAGE_SIZE); - fbi->map_cpu = consistent_alloc(GFP_KERNEL, fbi->map_size, + fbi->map_cpu = consistent_alloc(GFP_FRAMEBUF, fbi->map_size, &fbi->map_dma); if (fbi->map_cpu) { diff -uNr linux.org/include/asm-arm/arch-pxa/pxa-regs.h linux/include/asm-arm/arch-pxa/pxa-regs.h --- linux.org/include/asm-arm/arch-pxa/pxa-regs.h Mon May 12 14:25:14 2003 +++ linux/include/asm-arm/arch-pxa/pxa-regs.h Mon May 19 10:09:05 2003 @@ -322,6 +322,26 @@ #define STDLL __REG(0x40700000) /* Divisor Latch Low Register (DLAB = 1) (read/write) */ #define STDLH __REG(0x40700004) /* Divisor Latch High Register (DLAB = 1) (read/write) */ +/* Hardware UART (HWUART) */ +#define HWUART HWRBR +#define HWRBR __REG(0x41600000) /* Receive Buffer Register (read only ) */ +#define HWTHR __REG(0x41600000) /* Transmit Holding Register (write only) */ +#define HWIER __REG(0x41600004) /* Interrupt Enable Register (read/write) */ +#define HWIIR __REG(0x41600008) /* Interrupt ID Register (read only) */ +#define HWFCR __REG(0x41600008) /* FIFO Control Register (write only) */ +#define HWLCR __REG(0x4160000C) /* Line Control Register (read/write) */ +#define HWMCR __REG(0x41600010) /* Modem Control Register (read/write */ +#define HWLSR __REG(0x41600014) /* Line Status Register (read only) */ +#define HWMSR __REG(0x41600018) /* Modem Status register */ +#define HWSPR __REG(0x4160001C) /* Scratch Pad Register (read/write) */ +#define HWISR __REG(0x41600020) /* Infrared Selection Register (read/write) */ +#define HWFOR __REG(0x41600024) /* FIFO Occupancy register (read-only) */ +#define HWABR __REG(0x41600028) /* Autobaud Control register (read/write) */ +#define HWACR __REG(0x4160002C) /* Autobaund Count register */ +#define HWDLL __REG(0x41600000) /* Divisor Latch Low Register (DLAB = 1) (read/write) */ +#define HWDLH __REG(0x41600004) /* Divisor Latch High Register (DLAB = 1) (read/write) */ + + #define IER_DMAE (1 << 7) /* DMA Requests Enable */ #define IER_UUE (1 << 6) /* UART Unit Enable */ #define IER_NRZE (1 << 5) /* NRZ coding Enable */ @@ -614,6 +634,14 @@ #define UDCCS_INT_TSP (1 << 7) /* Transmit short packet */ #define UFNRH __REG(0x40600060) /* UDC Frame Number Register High */ + +#define UFNHR_FNMSB (7 << 0) /* Most significant 3-bits of 11-bit frame number */ +#define UFNHR_IPE4 (1 << 3) /* Iso Packet Error Endpont 4 (read/write 1 to clear) */ +#define UFNHR_IPE9 (1 << 4) /* Iso Packet Error Endpont 9 (read/write 1 to clear) */ +#define UFNHR_IPE14 (1 << 5) /* Iso Packet Error Endpont 14 (read/write 1 to clear) */ +#define UFNHR_SIM (1 << 6) /* SOF interrupt mask (read/write) */ +#define UFNHR_SIR (1 << 7) /* SOF Interrupt Request (read/write 1 to clear) */ + #define UFNRL __REG(0x40600064) /* UDC Frame Number Register Low */ #define UBCR2 __REG(0x40600068) /* UDC Byte Count Reg 2 */ #define UBCR4 __REG(0x4060006c) /* UDC Byte Count Reg 4 */ @@ -876,9 +904,13 @@ #define GPIO40_FFDTR 40 /* FFUART data terminal Ready */ #define GPIO41_FFRTS 41 /* FFUART request to send */ #define GPIO42_BTRXD 42 /* BTUART receive data */ +#define GPIO42_HWRXD 42 #define GPIO43_BTTXD 43 /* BTUART transmit data */ +#define GPIO43_HWTXD 43 #define GPIO44_BTCTS 44 /* BTUART clear to send */ +#define GPIO44_HWCTS 44 #define GPIO45_BTRTS 45 /* BTUART request to send */ +#define GPIO45_HWRTS 45 #define GPIO46_ICPRXD 46 /* ICP receive data */ #define GPIO46_STRXD 46 /* STD_UART receive data */ #define GPIO47_ICPTXD 47 /* ICP transmit data */ @@ -983,9 +1015,13 @@ #define GPIO40_FFDTR_MD (40 | GPIO_ALT_FN_2_OUT) #define GPIO41_FFRTS_MD (41 | GPIO_ALT_FN_2_OUT) #define GPIO42_BTRXD_MD (42 | GPIO_ALT_FN_1_IN) +#define GPIO42_HWRXD_MD (42 | GPIO_ALT_FN_3_IN) #define GPIO43_BTTXD_MD (43 | GPIO_ALT_FN_2_OUT) +#define GPIO43_HWTXD_MD (43 | GPIO_ALT_FN_3_OUT) #define GPIO44_BTCTS_MD (44 | GPIO_ALT_FN_1_IN) +#define GPIO44_HWCTS_MD (44 | GPIO_ALT_FN_3_IN) #define GPIO45_BTRTS_MD (45 | GPIO_ALT_FN_2_OUT) +#define GPIO45_HWRTS_MD (45 | GPIO_ALT_FN_3_OUT) #define GPIO46_ICPRXD_MD (46 | GPIO_ALT_FN_1_IN) #define GPIO46_STRXD_MD (46 | GPIO_ALT_FN_2_IN) #define GPIO47_ICPTXD_MD (47 | GPIO_ALT_FN_2_OUT) @@ -1125,6 +1161,7 @@ #define CKEN7_BTUART (1 << 7) /* BTUART Unit Clock Enable */ #define CKEN6_FFUART (1 << 6) /* FFUART Unit Clock Enable */ #define CKEN5_STUART (1 << 5) /* STUART Unit Clock Enable */ +#define CKEN4_HWUART (1 << 4) /* HWUART Unit Clock Enable */ #define CKEN3_SSP (1 << 3) /* SSP Unit Clock Enable */ #define CKEN2_AC97 (1 << 2) /* AC97 Unit Clock Enable */ #define CKEN1_PWM1 (1 << 1) /* PWM1 Clock Enable */ diff -uNr linux.org/include/asm-arm/proc-armv/pgtable.h linux/include/asm-arm/proc-armv/pgtable.h --- linux.org/include/asm-arm/proc-armv/pgtable.h Mon May 12 14:25:14 2003 +++ linux/include/asm-arm/proc-armv/pgtable.h Fri May 16 17:48:13 2003 @@ -183,6 +183,7 @@ * Mark the prot value as uncacheable and unbufferable. */ #define pgprot_noncached(prot) __pgprot(pgprot_val(prot) & ~(L_PTE_CACHEABLE | L_PTE_BUFFERABLE)) +#define pgprot_noncached_buffered(prot) __pgprot(pgprot_val(prot) & ~(L_PTE_CACHEABLE) | L_PTE_BUFFERABLE) #endif /* __ASSEMBLY__ */ diff -uNr linux.org/include/linux/ac97_codec.h linux/include/linux/ac97_codec.h --- linux.org/include/linux/ac97_codec.h Mon May 12 14:25:14 2003 +++ linux/include/linux/ac97_codec.h Mon May 12 14:38:23 2003 @@ -223,6 +223,9 @@ int type; struct ac97_ops *codec_ops; +#ifdef CONFIG_PM + struct pm_dev *pmdev; +#endif /* controller specific lower leverl ac97 accessing routines */ u16 (*codec_read) (struct ac97_codec *codec, u8 reg); diff -uNr linux.org/include/linux/mm.h linux/include/linux/mm.h --- linux.org/include/linux/mm.h Mon May 12 14:25:14 2003 +++ linux/include/linux/mm.h Fri May 16 17:48:13 2003 @@ -599,6 +599,7 @@ #define __GFP_IO 0x40 /* Can start low memory physical IO? */ #define __GFP_HIGHIO 0x80 /* Can start high mem physical IO? */ #define __GFP_FS 0x100 /* Can call down to low-level FS? */ +#define __GFP_FRAMEBUF 0x200 /* non-cacheable, but bufferable? */ #define GFP_NOHIGHIO (__GFP_HIGH | __GFP_WAIT | __GFP_IO) #define GFP_NOIO (__GFP_HIGH | __GFP_WAIT) @@ -607,6 +608,7 @@ #define GFP_USER ( __GFP_WAIT | __GFP_IO | __GFP_HIGHIO | __GFP_FS) #define GFP_HIGHUSER ( __GFP_WAIT | __GFP_IO | __GFP_HIGHIO | __GFP_FS | __GFP_HIGHMEM) #define GFP_KERNEL (__GFP_HIGH | __GFP_WAIT | __GFP_IO | __GFP_HIGHIO | __GFP_FS) +#define GFP_FRAMEBUF (__GFP_HIGH | __GFP_WAIT | __GFP_IO | __GFP_HIGHIO | __GFP_FS | __GFP_FRAMEBUF) #define GFP_NFS (__GFP_HIGH | __GFP_WAIT | __GFP_IO | __GFP_HIGHIO | __GFP_FS) #define GFP_KSWAPD ( __GFP_WAIT | __GFP_IO | __GFP_HIGHIO | __GFP_FS) diff -uNr linux.org/kernel/pm.c linux/kernel/pm.c --- linux.org/kernel/pm.c Mon May 12 14:25:13 2003 +++ linux/kernel/pm.c Mon May 12 14:38:39 2003 @@ -234,7 +234,12 @@ struct list_head *entry; down(&pm_devs_lock); - entry = pm_devs.next; + + if (rqst == PM_SUSPEND) + entry = pm_devs.prev; + else + entry = pm_devs.next; + while (entry != &pm_devs) { struct pm_dev *dev = list_entry(entry, struct pm_dev, entry); if (dev->callback) { @@ -249,7 +254,10 @@ return status; } } - entry = entry->next; + if (rqst == PM_SUSPEND) + entry = entry->prev; + else + entry = entry->next; } up(&pm_devs_lock); return 0;