diff options
Diffstat (limited to 'packages/linux/linux-mtx-2-2.4.27')
-rw-r--r-- | packages/linux/linux-mtx-2-2.4.27/00-mtx-2.diff | 324 | ||||
-rw-r--r-- | packages/linux/linux-mtx-2-2.4.27/42-usb-ohci-fixes.patch | 9 | ||||
-rw-r--r-- | packages/linux/linux-mtx-2-2.4.27/46-otg.patch | 56853 | ||||
-rw-r--r-- | packages/linux/linux-mtx-2-2.4.27/47-au1000_eth.patch | 163 | ||||
-rw-r--r-- | packages/linux/linux-mtx-2-2.4.27/48-pptp.patch | 5092 | ||||
-rw-r--r-- | packages/linux/linux-mtx-2-2.4.27/defconfig-mtx-2 | 207 |
6 files changed, 62253 insertions, 395 deletions
diff --git a/packages/linux/linux-mtx-2-2.4.27/00-mtx-2.diff b/packages/linux/linux-mtx-2-2.4.27/00-mtx-2.diff index 589446c2e9..6d1ca1c75e 100644 --- a/packages/linux/linux-mtx-2-2.4.27/00-mtx-2.diff +++ b/packages/linux/linux-mtx-2-2.4.27/00-mtx-2.diff @@ -2500,8 +2500,8 @@ diff -Nur linux-old/Documentation/Configure.help linux/Documentation/Configure.h +# +CONFIG_USBD_AU1X00_BUS=m +CONFIG_USBD_AU1X00_SCLOCK=400 -+CONFIG_AU1000_USB_DEVICE=y -+CONFIG_AU1X00_USB_DEVICE=y ++# CONFIG_AU1000_USB_DEVICE is not set ++# CONFIG_AU1X00_USB_DEVICE is not set +# CONFIG_USBD_BI_REGISTER_TRACE is not set + +# @@ -2605,326 +2605,6 @@ diff -Nur linux-old/Documentation/Configure.help linux/Documentation/Configure.h MODULE_AUTHOR("Dan Malek, Embedded Edge, LLC."); MODULE_DESCRIPTION("SMBus adapter Alchemy pb1550"); ---- linux/drivers/net/au1000_eth.c~00-mtx-2.diff 2006-06-10 14:01:44.602796000 +0200 -+++ linux/drivers/net/au1000_eth.c 2006-06-10 13:56:07.353719250 +0200 -@@ -6,7 +6,9 @@ - * Copyright 2002 TimeSys Corp. - * Author: MontaVista Software, Inc. - * ppopov@mvista.com or source@mvista.com -- * -+ * Bjoern Riemer 2004 -+ * riemer@fokus.fraunhofer.de or riemer@riemer-nt.de -+ * // fixed the link beat detection with ioctls (SIOCGMIIPHY) - * ######################################################################## - * - * This program is free software; you can distribute it and/or modify it -@@ -83,7 +85,7 @@ - static int au1000_set_config(struct net_device *dev, struct ifmap *map); - static void set_rx_mode(struct net_device *); - static struct net_device_stats *au1000_get_stats(struct net_device *); --static inline void update_tx_stats(struct net_device *, u32, u32); -+static inline void update_tx_stats(struct net_device *, u32); - static inline void update_rx_stats(struct net_device *, u32); - static void au1000_timer(unsigned long); - static int au1000_ioctl(struct net_device *, struct ifreq *, int); -@@ -656,6 +658,7 @@ - ks8995m_status, - }; - -+ - #ifdef CONFIG_MIPS_BOSPORUS - struct phy_ops stub_ops = { - stub_init, -@@ -674,6 +677,7 @@ - {"Broadcom BCM5201 10/100 BaseT PHY",0x0040,0x6212, &bcm_5201_ops,0}, - {"Broadcom BCM5221 10/100 BaseT PHY",0x0040,0x61e4, &bcm_5201_ops,0}, - {"Broadcom BCM5222 10/100 BaseT PHY",0x0040,0x6322, &bcm_5201_ops,1}, -+ {"Broadcom BCM5241 10/100 BaseT PHY",0x0143,0xBC31, &bcm_5201_ops,1}, - {"AMD 79C901 HomePNA PHY",0x0000,0x35c8, &am79c901_ops,0}, - {"AMD 79C874 10/100 BaseT PHY",0x0022,0x561b, &am79c874_ops,0}, - {"LSI 80227 10/100 BaseT PHY",0x0016,0xf840, &lsi_80227_ops,0}, -@@ -810,28 +814,39 @@ - int phy_found=0; - #endif - -+#if 0 -+ if (aup && aup->mii) -+ aup->mii->chip_info = NULL; -+#endif -+ -+ - /* search for total of 32 possible mii phy addresses */ - for (phy_addr = 0; phy_addr < 32; phy_addr++) { - u16 mii_status; - u16 phy_id0, phy_id1; - int i; - -- #ifdef CONFIG_BCM5222_DUAL_PHY -+#ifdef CONFIG_BCM5222_DUAL_PHY - /* Mask the already found phy, try next one */ - if (au_macs[0]->mii && au_macs[0]->mii->mii_control_reg) { - if (au_macs[0]->phy_addr == phy_addr) - continue; - } -- #endif -+#endif - - mii_status = mdio_read(dev, phy_addr, MII_STATUS); - if (mii_status == 0xffff || mii_status == 0x0000) - /* the mii is not accessable, try next one */ - continue; - -+ - phy_id0 = mdio_read(dev, phy_addr, MII_PHY_ID0); - phy_id1 = mdio_read(dev, phy_addr, MII_PHY_ID1); - -+ /*printk ("mii_probe: found PHY at address 0x%X : %04X/%04X\n", phy_addr, -+ phy_id0, phy_id1 -+ ); */ -+ - /* search our mii table for the current mii */ - for (i = 0; mii_chip_table[i].phy_id1; i++) { - if (phy_id0 == mii_chip_table[i].phy_id0 && -@@ -853,7 +868,7 @@ - // values and set indicators. We need to do - // this now since mdio_{read,write} need the - // control and data register addresses. -- #ifdef CONFIG_BCM5222_DUAL_PHY -+#ifdef CONFIG_BCM5222_DUAL_PHY - if ( mii_chip_table[i].dual_phy) { - - /* assume both phys are controlled -@@ -867,11 +882,13 @@ - aup->mii->mii_data_reg = (u32 *) - &au_macs[0]->mac->mii_data; - } -- #endif -+#endif - goto found; - } - } - } -+ return -1; -+ - found: - - #ifdef CONFIG_MIPS_BOSPORUS -@@ -1027,24 +1044,26 @@ - struct au1000_private *aup = (struct au1000_private *) dev->priv; - - if (au1000_debug > 4) -- printk(KERN_INFO "%s: reset mac, aup %x\n", -- dev->name, (unsigned)aup); -+ printk(KERN_INFO "%s: reset mac, aup %x, macid %d\n", -+ dev->name, (unsigned)aup, aup->mac_id); -+ -+ return; - - spin_lock_irqsave(&aup->lock, flags); - del_timer(&aup->timer); - hard_stop(dev); -- #ifdef CONFIG_BCM5222_DUAL_PHY -+#ifdef CONFIG_BCM5222_DUAL_PHY - if (aup->mac_id != 0) { -- #endif -+#endif - /* If BCM5222, we can't leave MAC0 in reset because then - * we can't access the dual phy for ETH1 */ - *aup->enable = MAC_EN_CLOCK_ENABLE; - au_sync_delay(2); - *aup->enable = 0; - au_sync_delay(2); -- #ifdef CONFIG_BCM5222_DUAL_PHY -+#ifdef CONFIG_BCM5222_DUAL_PHY - } -- #endif -+#endif - aup->tx_full = 0; - for (i = 0; i < NUM_RX_DMA; i++) { - /* reset control bits */ -@@ -1140,17 +1159,39 @@ - iflist[1].macen_addr = AU1550_MAC1_ENABLE; - iflist[0].irq = AU1550_MAC0_DMA_INT; - iflist[1].irq = AU1550_MAC1_DMA_INT; -+ /*printk ("***** Au1550 Ethernet *****\n");*/ - break; - #endif - default: - num_ifs = 0; - } -+ -+ { -+ unsigned long pf = au_readl(SYS_PINFUNC); -+ pf &= ~1; -+ -+ au_writel (pf, SYS_PINFUNC); -+ au_writel (0x10000, SYS_OUTPUTSET); -+ -+ } -+ -+ -+ - for(i = 0; i < num_ifs; i++) { -+ /*printk ("*** calling au1000_probe(0x%08lX, %d, %d)\n", iflist[i].base_addr, iflist[i].irq, i);*/ - dev = au1000_probe(iflist[i].base_addr, iflist[i].irq, i); -+ /*printk ("*** left au1000_probe\n");*/ -+ - iflist[i].dev = dev; -+ -+ /*printk ("*** 1\n");*/ -+ - if (dev) - found_one++; - } -+ -+ printk("Au1x Ethernet found %d ethernet devices\n", found_one); -+ - if (!found_one) - return -ENODEV; - return 0; -@@ -1194,6 +1235,7 @@ - /* Allocate the data buffers */ - aup->vaddr = (u32)dma_alloc(MAX_BUF_SIZE * - (NUM_TX_BUFFS+NUM_RX_BUFFS), &aup->dma_addr); -+ - if (!aup->vaddr) { - kfree(dev); - release_region(ioaddr, MAC_IOSIZE); -@@ -1227,6 +1269,7 @@ - setup_hw_rings(aup, MAC0_RX_DMA_ADDR, MAC0_TX_DMA_ADDR); - aup->mac_id = 0; - au_macs[0] = aup; -+ - } - else - if (ioaddr == iflist[1].base_addr) -@@ -1257,6 +1300,8 @@ - printk(KERN_ERR "%s: out of memory\n", dev->name); - goto err_out; - } -+ -+ aup->mii->chip_info = NULL; - aup->mii->mii_control_reg = 0; - aup->mii->mii_data_reg = 0; - -@@ -1284,6 +1329,7 @@ - aup->rx_dma_ring[i]->buff_stat = (unsigned)pDB->dma_addr; - aup->rx_db_inuse[i] = pDB; - } -+ - for (i = 0; i < NUM_TX_DMA; i++) { - pDB = GetFreeDB(aup); - if (!pDB) { -@@ -1383,12 +1429,17 @@ - aup->phy_ops->phy_status(dev, aup->phy_addr, &link, &speed); - control = MAC_DISABLE_RX_OWN | MAC_RX_ENABLE | MAC_TX_ENABLE; - #ifndef CONFIG_CPU_LITTLE_ENDIAN -+ /*riemer: fix for startup without cable */ -+ if (!link) -+ dev->flags &= ~IFF_RUNNING; -+ - control |= MAC_BIG_ENDIAN; - #endif - if (link && (dev->if_port == IF_PORT_100BASEFX)) { - control |= MAC_FULL_DUPLEX; - } - aup->mac->control = control; -+ aup->mac->vlan1_tag = 0x8100; /* activate vlan support */ - au_sync(); - - spin_unlock_irqrestore(&aup->lock, flags); -@@ -1541,14 +1592,11 @@ - - - static inline void --update_tx_stats(struct net_device *dev, u32 status, u32 pkt_len) -+update_tx_stats(struct net_device *dev, u32 status) - { - struct au1000_private *aup = (struct au1000_private *) dev->priv; - struct net_device_stats *ps = &aup->stats; - -- ps->tx_packets++; -- ps->tx_bytes += pkt_len; -- - if (status & TX_FRAME_ABORTED) { - if (dev->if_port == IF_PORT_100BASEFX) { - if (status & (TX_JAB_TIMEOUT | TX_UNDERRUN)) { -@@ -1581,7 +1629,7 @@ - ptxd = aup->tx_dma_ring[aup->tx_tail]; - - while (ptxd->buff_stat & TX_T_DONE) { -- update_tx_stats(dev, ptxd->status, ptxd->len & 0x3ff); -+ update_tx_stats(dev, ptxd->status); - ptxd->buff_stat &= ~TX_T_DONE; - ptxd->len = 0; - au_sync(); -@@ -1603,6 +1651,7 @@ - static int au1000_tx(struct sk_buff *skb, struct net_device *dev) - { - struct au1000_private *aup = (struct au1000_private *) dev->priv; -+ struct net_device_stats *ps = &aup->stats; - volatile tx_dma_t *ptxd; - u32 buff_stat; - db_dest_t *pDB; -@@ -1622,7 +1671,7 @@ - return 1; - } - else if (buff_stat & TX_T_DONE) { -- update_tx_stats(dev, ptxd->status, ptxd->len & 0x3ff); -+ update_tx_stats(dev, ptxd->status); - ptxd->len = 0; - } - -@@ -1642,6 +1691,9 @@ - else - ptxd->len = skb->len; - -+ ps->tx_packets++; -+ ps->tx_bytes += ptxd->len; -+ - ptxd->buff_stat = pDB->dma_addr | TX_DMA_ENABLE; - au_sync(); - dev_kfree_skb(skb); -@@ -1840,17 +1892,35 @@ - - static int au1000_ioctl(struct net_device *dev, struct ifreq *rq, int cmd) - { -- //u16 *data = (u16 *)&rq->ifr_data; -+/* -+// This structure is used in all SIOCxMIIxxx ioctl calls -+struct mii_ioctl_data { -+ 0 u16 phy_id; -+ 1 u16 reg_num; -+ 2 u16 val_in; -+ 3 u16 val_out; -+};*/ -+ u16 *data = (u16 *)&rq->ifr_data; -+ struct au1000_private *aup = (struct au1000_private *) dev->priv; -+ //struct mii_ioctl_data *data = (struct mii_ioctl_data *) & rq->ifr_data; - - /* fixme */ - switch(cmd) { - case SIOCDEVPRIVATE: /* Get the address of the PHY in use. */ -- //data[0] = PHY_ADDRESS; -+ case SIOCGMIIPHY: -+ if (!netif_running(dev)) -+ return -EINVAL; -+ data[0] = aup->phy_addr; - case SIOCDEVPRIVATE+1: /* Read the specified MII register. */ -- //data[3] = mdio_read(ioaddr, data[0], data[1]); -+ case SIOCGMIIREG: -+ data[3] = mdio_read(dev, data[0], data[1]); -+ //data->val_out = mdio_read(dev,data->phy_id,data->reg_num); - return 0; - case SIOCDEVPRIVATE+2: /* Write the specified MII register */ -- //mdio_write(ioaddr, data[0], data[1], data[2]); -+ case SIOCSMIIREG: -+ if (!capable(CAP_NET_ADMIN)) -+ return -EPERM; -+ mdio_write(dev, data[0], data[1],data[2]); - return 0; - default: - return -EOPNOTSUPP; --- linux-old/drivers/sound/au1550_psc.c 2004-09-19 00:07:37.000000000 +0200 +++ linux/drivers/sound/au1550_psc.c 2006-04-30 20:35:58.000000000 +0200 @@ -55,14 +55,15 @@ diff --git a/packages/linux/linux-mtx-2-2.4.27/42-usb-ohci-fixes.patch b/packages/linux/linux-mtx-2-2.4.27/42-usb-ohci-fixes.patch index 4c3058ce00..aa5a0d45f7 100644 --- a/packages/linux/linux-mtx-2-2.4.27/42-usb-ohci-fixes.patch +++ b/packages/linux/linux-mtx-2-2.4.27/42-usb-ohci-fixes.patch @@ -11,6 +11,15 @@ /*-------------------------------------------------------------------------*/ /* AMD-756 (D2 rev) reports corrupt register contents in some cases. +@@ -147,7 +151,7 @@ + ohci->complete_head = urb; + ohci->complete_tail = urb; + } else { +- ohci->complete_head->hcpriv = urb; ++ ohci->complete_tail->hcpriv = urb; + ohci->complete_tail = urb; + } + } @@ -2587,12 +2591,12 @@ hc_release_ohci (ohci); return -ENODEV; diff --git a/packages/linux/linux-mtx-2-2.4.27/46-otg.patch b/packages/linux/linux-mtx-2-2.4.27/46-otg.patch new file mode 100644 index 0000000000..2004894b3e --- /dev/null +++ b/packages/linux/linux-mtx-2-2.4.27/46-otg.patch @@ -0,0 +1,56853 @@ +--- linux/Makefile-otgorig 2006-09-20 16:02:57.347146612 +0200 ++++ linux/Makefile 2006-09-20 16:03:35.280797278 +0200 +@@ -181,6 +181,7 @@ + DRIVERS-$(CONFIG_HAMRADIO) += drivers/net/hamradio/hamradio.o + DRIVERS-$(CONFIG_TC) += drivers/tc/tc.a + DRIVERS-$(CONFIG_USB) += drivers/usb/usbdrv.o ++DRIVERS-$(CONFIG_OTG) += drivers/otg/otg_drv.o + DRIVERS-$(CONFIG_USB_GADGET) += drivers/usb/gadget/built-in.o + DRIVERS-y +=drivers/media/media.o + DRIVERS-$(CONFIG_INPUT) += drivers/input/inputdrv.o +--- linux/arch/mips/config-shared.in-otgorig 2006-09-20 16:09:20.671834564 +0200 ++++ linux/arch/mips/config-shared.in 2006-09-20 16:09:28.068156725 +0200 +@@ -1009,6 +1009,7 @@ + endmenu + + source drivers/usb/Config.in ++source drivers/otg/Config.in + + source net/bluetooth/Config.in + +--- linux/drivers/Makefile-otgorig 2006-09-20 16:09:47.100985766 +0200 ++++ linux/drivers/Makefile 2006-09-20 16:10:25.730668535 +0200 +@@ -6,7 +6,7 @@ + # + + +-mod-subdirs := dio hil mtd sbus video macintosh usb input telephony ide \ ++mod-subdirs := dio hil mtd sbus video macintosh usb otg input telephony ide \ + message/i2o message/fusion scsi md ieee1394 pnp isdn atm \ + fc4 net/hamradio i2c acpi bluetooth usb/gadget sensors + +@@ -28,6 +28,7 @@ + subdir-$(CONFIG_MAC) += macintosh + subdir-$(CONFIG_PPC32) += macintosh + subdir-$(CONFIG_USB) += usb ++subdir-$(CONFIG_OTG) += otg + subdir-$(CONFIG_USB_GADGET) += usb/gadget + subdir-$(CONFIG_INPUT) += input + subdir-$(CONFIG_PHONE) += telephony +diff -uNr linux/drivers/no-otg/Config.in linux/drivers/otg/Config.in +--- linux/drivers/no-otg/Config.in 1970-01-01 01:00:00.000000000 +0100 ++++ linux/drivers/otg/Config.in 2006-09-01 21:41:25.000000000 +0200 +@@ -0,0 +1,103 @@ ++# ++# USBOTG - Top level configuration of a USB Peripheral or USB On-The-Go Peripheral Device ++# ++# Copyright (c) 2004 Belcarra ++# ++# ++ ++mainmenu_option next_comment ++ ++ # ++ # Enable/Disable OTG Support ++ # ++ comment 'On-The-Go and USB Peripheral Support' ++ tristate 'Support for On-The-Go and USB Peripherals ' CONFIG_OTG ++ ++ if [ "$CONFIG_OTG" != "n" ]; then ++ # ++ # Select appropriate hardware platform, each config file ++ # will only offer options if the kernel is configured for ++ # that specific architecture or platform. They should define ++ # the following to enable the further configuration in this ++ # file: ++ # CONFIG_OTG_PLATFORM_OTG offer OTG configuration ++ # ++ # CONFIG_OTG_PLATFORM_USBD offer USBD configuration and ++ # function selection ++ # CONFIG_OTG_PLATFORM_HOST offer HOST configuration ++ # ++ mainmenu_option next_comment ++ comment 'Select Hardware' ++ ++ # platform oriented configurations ++# source drivers/otg/config/Config.in-mainstone ++# source drivers/otg/config/Config.in-pcs-b780 ++# source drivers/otg/config/Config.in-pcs-p1 ++# source drivers/otg/config/Config.in-mx2ads ++# source drivers/otg/config/Config.in-omap-h2 ++ source drivers/otg/config/Config.in-db1550 ++# source drivers/otg/config/Config.in-mordor ++ ++ # architecture specific configurations ++ source drivers/otg/config/Config.in-au1x00 ++# source drivers/otg/config/Config.in-dbmx1 ++# source drivers/otg/config/Config.in-lh7a400 ++# source drivers/otg/config/Config.in-lubbock ++# source drivers/otg/config/Config.in-smdk2500 ++# source drivers/otg/config/Config.in-strongarm ++# source drivers/otg/config/Config.in-superh ++ ++ # generic drivers ++ source drivers/otg/config/Config.in-isp1301 ++ source drivers/otg/config/Config.in-max3353e ++ endmenu ++ ++ if [ "$CONFIG_OTG_PLATFORM_OTG" = "y" -o "$CONFIG_OTG_PLATFORM_USBD" = "y" ]; then ++ ++ # ++ # Generic Options ++ # ++ mainmenu_option next_comment ++ comment 'General Support Options' ++ bool 'Enable High Speed Descriptors' CONFIG_OTG_HIGH_SPEED ++ # bool 'Enable Root HUB Function' CONFIG_OTG_ROOT_HUB ++ bool 'OTG Fast Tracing' CONFIG_OTG_TRACE ++ tristate 'USB FUNCTION FS Module' CONFIG_OTG_PROCFSM ++ bool 'Disable C99 initializers' CONFIG_OTG_NOC99 ++ endmenu ++ ++ # ++ # Select USB Peripheral Function Drivers ++ # ++ mainmenu_option next_comment ++ comment 'Targeted Peripherals (USB Peripheral Function Drivers)' ++ source drivers/otg/functions/acm/Config.in ++ source drivers/otg/functions/mouse/Config.in ++ source drivers/otg/functions/network/Config.in ++ source drivers/otg/functions/msc/Config.in ++# source drivers/otg/functions/test/Config.in ++ endmenu ++ ++ mainmenu_option next_comment ++ comment 'Traditional Device Options' ++ bool 'Built-in Minimal USB Device' CONFIG_OTG_FW_MN ++ dep_bool 'Enable Auto-Start' CONFIG_OTG_TR_AUTO $CONFIG_OTG_MN ++ endmenu ++ fi ++ #dep_tristate 'Build OTG minihost core' CONFIG_OTG_HOSTCORE $CONFIG_OTG ++ if [ "$CONFIG_OTG_PLATFORM_HOST" != "n" ]; then ++ # ++ # Host configuration ++ # ++ mainmenu_option next_comment ++ comment 'Host configuration (OTG minihost core and HCD)' ++# source drivers/otg/core/Config.in ++ source drivers/otg/ocd/Config.in ++# source drivers/otg/classes/usblan/Config.in ++ endmenu ++ fi ++ ++ fi ++ ++endmenu ++ +diff -uNr linux/drivers/no-otg/Config.in-orig linux/drivers/otg/Config.in-orig +--- linux/drivers/no-otg/Config.in-orig 1970-01-01 01:00:00.000000000 +0100 ++++ linux/drivers/otg/Config.in-orig 2006-09-01 21:41:25.000000000 +0200 +@@ -0,0 +1,73 @@ ++# ++# OTG - configuration of a USB On-The-Go Device ++# ++# Copyright (c) 2004 Belcarra ++# ++# The otg_export script will delete all comments marked "(Testing)" ++# ++ ++mainmenu_option next_comment ++ ++comment 'OTG devices' ++ ++tristate 'Support for USB On-The-Go Devices ' CONFIG_OTG ++ ++if [ "$CONFIG_OTG" = "y" -o "$CONFIG_OTG" = "m" ]; then ++ comment '' ++ bool ' Enable High Speed Descriptors' CONFIG_OTG_HIGH_SPEED ++ bool ' Enable Root HUB Function' CONFIG_OTG_ROOT_HUB ++ comment '' ++ ++ bool ' Disable PCD' CONFIG_OTG_DISABLE_PCD ++ bool ' Disable HCD' CONFIG_OTG_DISABLE_HCD ++ ++ comment '' ++ ++ bool ' OTG Proc FS' CONFIG_OTG_PROCFS ++ tristate ' OTG Proc FS Module' CONFIG_OTG_PROCFSM $CONFIG_OTG ++ ++ comment 'On-The-Go Functions' ++ ++ source drivers/otg/functions/network/Config.in ++ source drivers/otg/functions/acm/Config.in ++ source drivers/otg/functions/msc/Config.in ++ ++ source drivers/otg/functions/isotest/Config.in ++ source drivers/otg/functions/mouse/Config.in ++ ++ comment '' ++ comment 'On-The-Go Transceiver Controller Drivers (TCD)' ++ ++ source drivers/otg/tcd/isp1301/Config.in ++ ++ comment '' ++ comment 'On-The-Go Peripheral Controller Drivers (PCD)' ++ ++ source drivers/otg/pcd/au1x00/Config.in ++ source drivers/otg/pcd/mx1/Config.in ++ source drivers/otg/pcd/mx2/Config.in ++ source drivers/otg/pcd/pxa/Config.in ++ source drivers/otg/pcd/sa1100/Config.in ++ source drivers/otg/pcd/lh7a400/Config.in ++ source drivers/otg/pcd/omap/Config.in ++ source drivers/otg/pcd/smdk2500/Config.in ++ source drivers/otg/pcd/superh/Config.in ++ source drivers/otg/pcd/sx2/Config.in ++ source drivers/otg/pcd/wmmx/Config.in ++ ++ comment '' ++ comment 'On-The-Go Host Controller Drivers (HCD)' ++ ++ source drivers/otg/hcd/omap/Config.in ++ ++ comment '' ++ tristate ' OTG Fast Tracing' CONFIG_OTG_TRACE ++ comment '' ++ ++ comment 'Non Current Bus Drivers (Testing)' ++ source drivers/otg/functions/pst/Config.in ++ source drivers/otg/functions/datalog/Config.in ++ ++fi ++ ++endmenu +diff -uNr linux/drivers/no-otg/FIX-MAKEFILES linux/drivers/otg/FIX-MAKEFILES +--- linux/drivers/no-otg/FIX-MAKEFILES 1970-01-01 01:00:00.000000000 +0100 ++++ linux/drivers/otg/FIX-MAKEFILES 2006-09-01 21:41:25.000000000 +0200 +@@ -0,0 +1,23 @@ ++#!/bin/sh ++ ++ARG=$1 ++shift ++ ++[ -z "${ARG}" ] && echo Bad args && exit 1 ++ ++#find . -name Makefile-${ARG} | while read i ++#do ++# m=`expr $i : "\(.*\)-${ARG}"` ++# ln -sfv $i $m ++#done ++ ++find . -type d | while read d ++do ++ pushd $d > /dev/null ++ if [ -s Makefile-${ARG} ] ; then ++ echo -n `pwd` ": " ++ ln -sfv Makefile-${ARG} Makefile ++ fi ++ popd >/dev/null ++done ++ +diff -uNr linux/drivers/no-otg/Kconfig linux/drivers/otg/Kconfig +--- linux/drivers/no-otg/Kconfig 1970-01-01 01:00:00.000000000 +0100 ++++ linux/drivers/otg/Kconfig 2006-09-01 21:41:25.000000000 +0200 +@@ -0,0 +1,115 @@ ++menu "On-The-Go and USB Peripheral Support" ++ ++ config OTG ++ tristate "Support for On-The-Go and USB Peripheral Support" ++ ---help--- ++ Configure all or part of the Belcarra OTG Stack ++ ++ ++ menu "On-The-Go Support" ++ depends on OTG ++ ++ source "drivers/otg/config/Kconfig-scma11-evb" ++ #source "drivers/otg/config/Kconfig-omap-h2" ++ ++ endmenu ++ ++ menu "On-The-Go Support Configuration" ++ depends on OTG_PLATFORM_OTG ++ ++ choice ++ depends on OTG && OTG_PLATFORM_OTG ++ prompt "On-The-Go Device Configuration" ++ config OTG_CFG_TR ++ bool "Traditional USB Peripheral" ++ ---help--- ++ Compile as a Traditional USB Peripheral. ++ On-The-Go support is enabled. ++ config OTG_CFG_HO ++ bool "Host Only" ++ ---help--- ++ Compile the USB Host support without the USB Peripheral ++ support. This is generally only useful for testing the ++ USB Host support and Host Controller drivers. ++ config OTG_CFG_PO ++ bool "Peripheral Only" ++ ---help--- ++ Compile as a On-The-Go Peripheral-Only device. This ++ is similiar to a Traditional USB Peripheral but enables ++ On-The-Go features such as SRP. ++ config OTG_CFG_DR ++ bool "Dual Role" ++ ---help--- ++ Compile as an On-The-Go Dual-Role device. ++ ++ endchoice ++ endmenu ++ ++ menu "Targeted Peripheral List (USB Host Class Drivers)" ++ depends on OTG_PLATFORM_OTG ++ # souce "drivers/otg/xxxx" ++ # ++ #---help--- ++ #A list of USB peripherals that this device ++ #can support when it is acting as a host. ++ endmenu ++ ++ menu "General Support Options" ++ ++ depends on OTG_PLATFORM_OTG|| OTG_PLATFORM_USBD ++ ++ # This needs to be a specific defined variable that comes ++ # from Kconfig-platform file ++ # ++ #config usb ++ # tristate 'OTG host core support (separate from native Linux host support)' ++ ++ config OTG_HIGH_SPEED ++ bool 'Enable high speed descriptors' ++ depends on OTG!=n ++ ++ config OTG_TRACE ++ bool 'OTG Fast Tracing' ++ depends on OTG!=n ++ ---help--- ++ This option implements register trace to support ++ driver debugging. ++ ++ #config OTG_ROOT_HUB ++ # bool 'Enable Root HUB Function' ++ # depends on OTG!=n ++ ++ config OTG_PROCFS ++ bool 'OTG Proc FS' ++ depends on OTG!=n ++ ---help--- ++ This option enables /proc/ support in various modules ++ Note: Some information previously exposed via the /proc ++ interface is now exposed via a different mechanism ++ ++ config OTG_PROCFSM ++ tristate 'OTG Proc FS Module' ++ depends on OTG != n ++ ---help--- ++ Build in extra support to perform various operations ++ through the /proc filesystem. Note: this module is ++ held over from the Device stack and its functions are ++ gradually being transferred to the OTG Admin API. ++ ++ endmenu ++ ++ ++ menu "Targeted Peripherals List (USB Peripheral Function Drivers)" ++ depends on OTG_PLATFORM_OTG || OTG_PLATFORM_USBD ++ #---help--- ++ #A list of USB peripheral types that this device ++ #can emulate when it is acting as a peripheral. ++ source "drivers/otg/functions/acm/Kconfig" ++ source "drivers/otg/functions/mouse/Kconfig" ++ source "drivers/otg/functions/msc/Kconfig" ++ source "drivers/otg/functions/network/Kconfig" ++ endmenu ++ ++ ++endmenu ++ +diff -uNr linux/drivers/no-otg/Makefile linux/drivers/otg/Makefile +--- linux/drivers/no-otg/Makefile 1970-01-01 01:00:00.000000000 +0100 ++++ linux/drivers/otg/Makefile 2006-09-01 21:41:25.000000000 +0200 +@@ -0,0 +1,132 @@ ++# ++# Belcarra OTG - On-The-Go ++# ++# Copyright (c) 2004 Belcarra Technologies Corp ++ ++TOPDIR ?= ../../.. ++ ++# order here may be important (determines linking order, thus module init order) ++subdir-y := otgcore functions ocd ++subdir-m := otgcore functions ocd ++subdir-n := ++subdir- := ++# The target object and module list name. ++ ++O_TARGET := otg_drv.o ++ ++# Objects that export symbols. ++ ++#export-objs := usbd.o usbd-bops.o usbd-fops.o usbd-pcd.o ep0.o hub.o ++ ++# Multipart objects. (core layer) ++ ++ ++# Optional parts of multipart objects. ++ ++# Object file lists. ++ ++obj-y := ++obj-m := ++obj-n := ++obj- := ++ ++# Each configuration option enables a list of files. ++ ++ifeq ($(CONFIG_OTG),y) ++obj-y += otgcore/otgcore.o ++endif ++ ++ ++# Object files in subdirectories (There has to be a better way to do this) ++ ++#=== Function drivers ++f-obj-y := ++f-obj-m := ++f-obj-n := ++f-obj- := ++ ++f-obj-$(CONFIG_OTG_ACM) += functions/function_target.o ++f-obj-$(CONFIG_OTG_ISOTEST) += functions/function_target.o ++f-obj-$(CONFIG_OTG_MSC) += functions/function_target.o ++f-obj-$(CONFIG_OTG_MOUSE) += functions/function_target.o ++f-obj-$(CONFIG_OTG_NETWORK) += functions/function_target.o ++f-obj-$(CONFIG_OTG_PST) += functions/function_target.o ++ ++# Remove any duplicate entries in the list by sorting (since that drops dups) ++obj-y += $(sort $(f-obj-y)) ++#obj-m += $(sort $(f-obj-m)) ++obj-n += $(sort $(f-obj-n)) ++obj- += $(sort $(f-obj-)) ++ ++#=== Peripheral controller drivers ++p-obj-y := ++p-obj-m := ++p-obj-n := ++p-obj- := ++ ++p-obj-$(CONFIG_OTG_AU1X00) += ocd/ocd_target.o ++p-obj-$(CONFIG_OTG_AU1550_DB1550_TR) += ocd/ocd_target.o ++ ++# Remove any duplicate entries in the list by sorting (since that drops dups) ++obj-y += $(sort $(p-obj-y)) ++#obj-m += $(sort $(p-obj-m)) ++obj-n += $(sort $(p-obj-n)) ++obj- += $(sort $(p-obj-)) ++ ++#=== Host controller drivers ++h-obj-y := ++h-obj-m := ++h-obj-n := ++h-obj- := ++ ++ ++# Remove any duplicate entries in the list by sorting (since that drops dups) ++obj-y += $(sort $(h-obj-y)) ++#obj-m += $(sort $(h-obj-m)) ++obj-n += $(sort $(h-obj-n)) ++obj- += $(sort $(h-obj-)) ++ ++ ++#=== Class drivers ++class-obj-y := ++class-obj-m := ++class-obj-n := ++class-obj- := ++ ++#class-obj-$(CONFIG_OTG_CLASS_USBLAN) += classes/class_target.o ++ ++# Remove any duplicate entries in the list by sorting (since that drops dups) ++obj-y += $(sort $(class-obj-y)) ++#obj-m += $(sort $(f-obj-m)) ++#obj-n += $(sort $(f-obj-n)) ++#obj- += $(sort $(f-obj-)) ++ ++ ++ ++# 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 ++EXTRA_CFLAGS += -Wno-format -Wall ++ +diff -uNr linux/drivers/no-otg/Makefile-l26 linux/drivers/otg/Makefile-l26 +--- linux/drivers/no-otg/Makefile-l26 1970-01-01 01:00:00.000000000 +0100 ++++ linux/drivers/otg/Makefile-l26 2006-09-01 21:41:25.000000000 +0200 +@@ -0,0 +1,7 @@ ++# ++# Belcarra OTG - On-The-Go ++# ++# Copyright (c) 2004 Belcarra Technologies Corp ++ ++obj-y += ++obj-m += functions/ ocd/ otgcore/ core/ +diff -uNr linux/drivers/no-otg/OWNER linux/drivers/otg/OWNER +--- linux/drivers/no-otg/OWNER 1970-01-01 01:00:00.000000000 +0100 ++++ linux/drivers/otg/OWNER 2006-09-01 21:41:25.000000000 +0200 +@@ -0,0 +1,20 @@ ++OWNER: Belcarra Technologies Corp ++LICENSEE: Freescale ++ ++THIS SOURCE CODE KIT IS SUPPLIED UNDER LICENSE ++ ++Unless expressly modified elsewhere, the author of ++each source file included herein retains ownership ++of the identified file notwithstanding release of ++a specific version under a Public License such as ++the GNU General Public License. ++ ++The line OWNER(entity) in a comment line at or near ++the top of a source file expressly asserts ownership ++of the file by that entity or person. ++ ++In addition the presence of an OWNER.TXT or OWNER ++file in a directory asserts ownership of the contents ++of that directory and of the compilation thereof into ++the present and derivative kits. ++ +diff -uNr linux/drivers/no-otg/README linux/drivers/otg/README +--- linux/drivers/no-otg/README 1970-01-01 01:00:00.000000000 +0100 ++++ linux/drivers/otg/README 2006-09-01 21:41:25.000000000 +0200 +@@ -0,0 +1,11 @@ ++otg/README ++ ++This is the top level of the OTG toolkit source tree. ++ ++ classes Host Class drivers ++ functions Peripheral Function drivers ++ ocd OTG Controller drivers ++ otgcore OTG State Machine and USB Device stack ++ otghw Hardware related include files ++ otg Include files ++ +diff -uNr linux/drivers/no-otg/config/Config.in-au1x00 linux/drivers/otg/config/Config.in-au1x00 +--- linux/drivers/no-otg/config/Config.in-au1x00 1970-01-01 01:00:00.000000000 +0100 ++++ linux/drivers/otg/config/Config.in-au1x00 2006-09-01 21:41:25.000000000 +0200 +@@ -0,0 +1,29 @@ ++# ++# Copyright (c) 2004 Belcarra ++# ++ ++# Au1x00 on DB1100, PB1100 and PB1500 ++ ++if [ "$CONFIG_SOC_AU1X00" = "y" -o \ ++ "$CONFIG_MIPS_AU1X00" = "y" -o \ ++ "$CONFIG_CPU_AU1X00" = "y" -o \ ++ "$CONFIG_MIPS_AU1500" = "y" -o \ ++ "$CONFIG_MIPS_AU1100" = "y" -o \ ++ "$CONFIG_MIPS_AU1000" = "y" ] ++then ++ mainmenu_option next_comment ++ comment 'AMD AU1X00 Bus Interface' ++ ++ dep_tristate 'DB1100/PB1100/PB1500 Development Boards Support' CONFIG_OTG_AU1X00 $CONFIG_OTG ++ ++ if [ "$CONFIG_OTG_AU1X00" != "n" ]; then ++ int 'AU1X00 System Clock' CONFIG_OTG_AU1X00_SCLOCK 400 ++ define_bool CONFIG_AU1000_USB_DEVICE n ++ define_bool CONFIG_AU1X00_USB_DEVICE y ++ define_bool CONFIG_OTG_PLATFORM_USBD y ++ #else ++ # define_bool CONFIG_OTG_PLATFORM_USBD n ++ fi ++ endmenu ++fi ++ +diff -uNr linux/drivers/no-otg/config/Config.in-db1550 linux/drivers/otg/config/Config.in-db1550 +--- linux/drivers/no-otg/config/Config.in-db1550 1970-01-01 01:00:00.000000000 +0100 ++++ linux/drivers/otg/config/Config.in-db1550 2006-09-01 21:41:25.000000000 +0200 +@@ -0,0 +1,48 @@ ++# ++# Copyright (c) 2004 Belcarra ++# ++ ++if [ "$CONFIG_MIPS_DB1550" = "y" -o "$CONFIG_MIPS_MTX2" ] ++then ++ mainmenu_option next_comment ++ comment 'DB1550 Development Board' ++ ++ dep_tristate 'DB1550 Development Boards Support' CONFIG_OTG_DB1550 $CONFIG_OTG ++ ++ #define_tristate CONFIG_OTG_AU1550 ++ ++ if [ "$CONFIG_OTG_DB1550" != "n" ]; then ++ int 'AU1X00 System Clock' CONFIG_OTG_AU1X00_SCLOCK 400 ++ define_bool CONFIG_AU1000_USB_DEVICE n ++ define_bool CONFIG_AU1X00_USB_DEVICE n ++ define_bool CONFIG_OTG_PLATFORM_USBD y ++ define_tristate CONFIG_OTG_AU1550 $CONFIG_OTG_DB1550 ++ ++ choice 'Select DB1550 Standard B or Mini A-B Port' \ ++ "Mini-B-J14 CONFIG_OTG_DB1550_J14 \ ++ Mini-A-B-J15 CONFIG_OTG_DB1550_J15" Mini-B-J14 ++ ++ if [ "$CONFIG_OTG_DB1550_J14" = "y" ]; then ++ define_bool CONFIG_OTG_PLATFORM_OTG n ++ define_bool CONFIG_OTG_PLATFORM_USBD y ++ define_bool CONFIG_OTG_MAX3353E n ++ define_tristate CONFIG_OTG_AU1550_DB1550_TR $CONFIG_OTG_DB1550 ++ ++ else ++ if [ "$CONFIG_OTG_DB1550_J15" = "y" ]; then ++ define_bool CONFIG_OTG_PLATFORM_OTG y ++ define_bool CONFIG_OTG_PLATFORM_USBD n ++ define_bool CONFIG_OTG_MAX3353E y ++ define_tristate CONFIG_OTG_MAX3353E_DB1550 $CONFIG_OTG_DB1550 ++ define_tristate CONFIG_OTG_AU1550_DB1550_DR $CONFIG_OTG_DB1550 ++ ++ # J15 is external OTG Transceiver Client ++ define_bool CONFIG_OTG_DB1550_HXOE n ++ define_bool CONFIG_OTG_DB1550_HXS y ++ define_int CONFIG_OTG_DB1550_SEOS 4 ++ fi ++ fi ++ fi ++ endmenu ++fi ++ +diff -uNr linux/drivers/no-otg/config/Config.in-dbmx1 linux/drivers/otg/config/Config.in-dbmx1 +--- linux/drivers/no-otg/config/Config.in-dbmx1 1970-01-01 01:00:00.000000000 +0100 ++++ linux/drivers/otg/config/Config.in-dbmx1 2006-09-01 21:41:25.000000000 +0200 +@@ -0,0 +1,19 @@ ++# ++# Copyright (c) 2004 Belcarra ++# ++ ++# MX1ADS - Motorola MX1 ++ ++if [ "$CONFIG_ARCH_MX1ADS" = "y" ]; then ++ ++ mainmenu_option next_comment ++ comment 'Motorola MX1 Bus Interface' ++ dep_tristate 'DBMX1 Developement Board Support' CONFIG_OTG_DBMX1 $CONFIG_OTG ++ ++ if [ "$CONFIG_OTG_DBMX1" != "n" ]; then ++ define_bool CONFIG_OTG_PLATFORM_USBD y ++ else ++ define_bool CONFIG_OTG_PLATFORM_USBD n ++ fi ++ endmenu ++fi +diff -uNr linux/drivers/no-otg/config/Config.in-isp1301 linux/drivers/otg/config/Config.in-isp1301 +--- linux/drivers/no-otg/config/Config.in-isp1301 1970-01-01 01:00:00.000000000 +0100 ++++ linux/drivers/otg/config/Config.in-isp1301 2006-09-01 21:41:25.000000000 +0200 +@@ -0,0 +1,25 @@ ++ ++# ++# Copyright (c) 2004 Belcarra ++# ++# ISP 1301 TCD ++ ++#if [ "$CONFIG_OTG_ISP1301" = "y" ]; then ++# mainmenu_option next_comment ++# comment 'ISP 1301' ++# ++# #bool 'Proc FS debug' CONFIG_OTG_ISP1301_PROCFSX ++# #bool 'Enable High Speed Descriptors' CONFIG_OTG_HIGH_SPEEDX ++# define_bool CONFIG_OTG_TEST y ++# ++# endmenu ++#fi ++if [ "$CONFIG_OTG_ISP1301" = "y" ]; then ++ ++ mainmenu_option next_comment ++ comment 'ISP 1301' ++ bool 'Proc FS debug' CONFIG_OTG_ISP1301_PROCFS ++ ++ endmenu ++fi ++ +diff -uNr linux/drivers/no-otg/config/Config.in-max3353e linux/drivers/otg/config/Config.in-max3353e +--- linux/drivers/no-otg/config/Config.in-max3353e 1970-01-01 01:00:00.000000000 +0100 ++++ linux/drivers/otg/config/Config.in-max3353e 2006-09-01 21:41:25.000000000 +0200 +@@ -0,0 +1,25 @@ ++ ++# ++# Copyright (c) 2004 Belcarra ++# ++# MAX 3353E TCD ++ ++#if [ "$CONFIG_OTG_MAX3353E" = "y" ]; then ++# mainmenu_option next_comment ++# comment 'MAX 3353E' ++# ++# #bool 'Proc FS debug' CONFIG_OTG_MAX3353E_PROCFSX ++# #bool 'Enable High Speed Descriptors' CONFIG_OTG_HIGH_SPEEDX ++# define_bool CONFIG_OTG_TEST y ++# ++# endmenu ++#fi ++if [ "$CONFIG_OTG_MAX3353E" = "y" ]; then ++ ++ mainmenu_option next_comment ++ comment 'MAX 3353E' ++ bool 'Proc FS debug' CONFIG_OTG_MAX3353E_PROCFS ++ ++ endmenu ++fi ++ +diff -uNr linux/drivers/no-otg/config/Config.in-mordor linux/drivers/otg/config/Config.in-mordor +--- linux/drivers/no-otg/config/Config.in-mordor 1970-01-01 01:00:00.000000000 +0100 ++++ linux/drivers/otg/config/Config.in-mordor 2006-09-01 21:41:25.000000000 +0200 +@@ -0,0 +1,28 @@ ++# ++# Copyright (c) 2004 Belcarra ++# ++ ++# Au1x00 on DB1100, PB1100 and PB1500 ++ ++if [ "$CONFIG_AMX_MORDOR" = "y" ] ++then ++ mainmenu_option next_comment ++ comment 'AMX Mordor Board' ++ ++ dep_tristate 'AMX Mordor Board Support' CONFIG_OTG_AU1550 $CONFIG_OTG ++ define_tristate CONFIG_OTG_BVD $CONFIG_OTG_AU1550 ++ ++ if [ "$CONFIG_OTG_AU1550" != "n" ]; then ++ int 'AU1X00 System Clock' CONFIG_OTG_AU1X00_SCLOCK 400 ++ define_bool CONFIG_AU1000_USB_DEVICE n ++ define_bool CONFIG_AU1X00_USB_DEVICE n ++ define_bool CONFIG_OTG_PLATFORM_USBD y ++ ++ define_bool CONFIG_OTG_PLATFORM_OTG n ++ define_bool CONFIG_OTG_PLATFORM_USBD y ++ ++ else ++ define_bool CONFIG_OTG_PLATFORM_USBD n ++ fi ++ endmenu ++fi +diff -uNr linux/drivers/no-otg/config/Kconfig-omap-h2 linux/drivers/otg/config/Kconfig-omap-h2 +--- linux/drivers/no-otg/config/Kconfig-omap-h2 1970-01-01 01:00:00.000000000 +0100 ++++ linux/drivers/otg/config/Kconfig-omap-h2 2006-09-01 21:41:25.000000000 +0200 +@@ -0,0 +1,87 @@ ++# ++# Copyright (c) 2004 Belcarra ++# ++ ++# OMAP - TI OMAP 1610 ++# ++ ++# CONFIG_OTG_PLATFORM_OTG # Offer OTG Configuration ++ ++# CONFIG_OTG_OMAP # Make OMAP driver ++# CONFIG_OTG_ISP1301 # Make ISP1301 driver ++ ++# CONFIG_OTG_ISP1301_OMAP_H2 # Compile ISP1301 OMAP H2 driver ++ ++# CONFIG_OTG_OMAP_H2 # OMAP Helen 2 board ++# CONFIG_OTG_OMAP_H2_3_WIRE # Use 3 Wire OTG Configuration ++# CONFIG_OTG_OMAP_H2_4_WIRE # Use 4 Wire OTG Configuration ++# CONFIG_OTG_OMAP_H2_DR # Compile as OTG Dual-role Device ++# CONFIG_OTG_OMAP_H2_TR # Compile as Traditional USB Peripheral ++ ++config OTG_OMAP_H2 ++ tristate "OMAP 1610 H2 Development Board" ++ depends on OTG && ARCH_OMAP ++ ---help--- ++ This implements On-The-Go USB Support for the Helen 2 OMAP ++ Development Board. ++ ++choice ++ prompt "Select Transceiver wire configuration" ++ depends on OTG && ARCH_OMAP && OTG_OMAP_H2 ++ config OTG_OMAP_H2_3_WIRE ++ bool 'Enable Pin Group 1, 3 Wire Configuration' ++ depends on OMAP_H2 !=n ++ ---help--- ++ The H2 board ISP1301 can be configured in either ++ the 3 wire or 4 wire configuration. ++ This enables the ISP1301 on the H2 board on Pin Group 1 ++ using the 3-Wire OTG Transceiver Configuration. This ++ requires that the ISP1301 be configured for DAT_SE0. ++ ++ config OTG_OMAP_H2_4_WIRE ++ bool 'Enable Pin Group 1, 4 Wire Configuration' ++ depends on OMAP_H2 !=n ++ ---help--- ++ The H2 board ISP1301 can be configured in either ++ the 3 wire or 4 wire configuration. ++ This enables the ISP1301 on the H2 board on Pin Group 1 ++ using the 3-Wire OTG Transceiver Configuration. This ++ requires that the ISP1301 be configured for VP_VM. ++ ++endchoice ++ ++config OTG_PLATFORM_OTG ++ bool ++ default OTG_OMAP_H2 ++ ++config OTG_OMAP_H2_TR ++ tristate ++ depends on OTG_CFG_TR ++ default OTG_OMAP_H2 ++ ++config OTG_OMAP_H2_HO ++ tristate ++ depends on OTG_CFG_HO ++ default OTG_OMAP_H2 ++ ++config OTG_OMAP_H2_PO ++ tristate ++ depends on OTG_CFG_PO ++ default OTG_OMAP_H2 ++ ++config OTG_OMAP_H2_DR ++ tristate ++ depends on OTG_CFG_DR ++ default OTG_OMAP_H2 ++ ++ ++config OTG_ISP1301 ++ tristate ++ depends on OTG_OMAP_H2 ++ default OTG_OMAP_H2 ++ ++config OTG_ISP1301_OMAP_H2 ++ tristate ++ depends on OTG_OMAP_H2 ++ default OTG_OMAP_H2 ++ +diff -uNr linux/drivers/no-otg/config/README-CONFIG.txt linux/drivers/otg/config/README-CONFIG.txt +--- linux/drivers/no-otg/config/README-CONFIG.txt 1970-01-01 01:00:00.000000000 +0100 ++++ linux/drivers/otg/config/README-CONFIG.txt 2006-09-01 21:41:25.000000000 +0200 +@@ -0,0 +1,106 @@ ++OTG System Configuration Stuart Lynne ++Belcarra Thu Jan 13 15:45:19 PST 2005 ++ ++This directory contains the linux system configurations to create ++architecture or board level drivers for On-The-Go or USB Device support. ++ ++There are four generic configurations available ++ ++ - tr - Traditional USB Peripheral ++ - po - OTG Peripheral only ++ - dr - OTG Dual-Role device ++ ++Traditional USB Peripheral ++************************** ++ ++Older types of systems supported USB peripherals as a separate module. It is ++not generally possible to configure these with On-The-Go support. In most ++cases the only customization available is for detection of the Vbus (cable ++attached) and control over the D-plus pullup resistor (soft-connect.) ++ ++For most of these types of systems the generated driver will be of the form: ++ ++ xxx_tr ++ ++Where xxx is the system architecture: ++ ++ au1x00 ++ lh7a400 ++ mx1 ++ pxa ++ sa1100 ++ smdk2500 ++ superh ++ ++ ++OTG Modes ++********* ++ ++Generally new systems that support On-The-Go have (at least) the following ++components: ++ ++ - USB Peripheral ++ - USB Host ++ - OTG Transceiver ++ - Charge Pump (optional) ++ ++In general the combination of the above is customized at the board or ++platform level, not the architecture (chip) level. There may be up to four ++different drivers implementing various combinations of the required support. ++ ++Required support: ++ ++ - pcd - Peripheral Controller Driver ++ - tcd - Transceiver Controller Driver ++ - hcd - Host Controller Driver ++ - ocd - OTG Controller Driver ++ ++Typically the pcd and ocd drivers will be in a single module. This module ++will be named: ++ ++ xxxx_ss ++ ++Where xxxx is the platform, e.g.: ++ ++ mainstone ++ mx1ads ++ omap-h2 ++ ++And ss is the type of OTG support being compiled: ++ ++ - tr - traditonal usb ++ - ho - host only ++ - po - peripheral only ++ - dr - dual-role ++ ++ ++ ++OTG Peripheral Only (po) ++************************ ++ ++This mode allows for implementing a restricted mode of On-The-Go support. ++The host driver module is not configured or available. ++ ++ ++OTG Dual-Role Device (dr) ++************************* ++ ++This mode implements the full On-The-Go stack with both USB Device and Usb ++Host support. ++ ++ ++Configuration File ++****************** ++ ++Under linux, older systems (those that support only traditional devices) will ++have a generic Config.in / Kconfig file to generate the required driver. ++ ++For newer systems that support OTG type configurations there should be a ++Configuration file for each major platform supported. This will specifically ++enable defines for all of the required drivers and their options. ++ ++Platform files may have several sub-types with appropriate configuration ++selectors. ++ ++ ++ +diff -uNr linux/drivers/no-otg/dirs linux/drivers/otg/dirs +--- linux/drivers/no-otg/dirs 1970-01-01 01:00:00.000000000 +0100 ++++ linux/drivers/otg/dirs 2006-09-01 21:41:25.000000000 +0200 +@@ -0,0 +1,11 @@ ++!if 0 ++Copyright (c) 2004 Belcarra ++!endif ++ ++DIRS= \ ++ wince \ ++ otgcore \ ++ functions/mouse ++ ++OPTIONAL_DIRS= \ ++ +diff -uNr linux/drivers/no-otg/functions/Makefile linux/drivers/otg/functions/Makefile +--- linux/drivers/no-otg/functions/Makefile 1970-01-01 01:00:00.000000000 +0100 ++++ linux/drivers/otg/functions/Makefile 2006-09-01 21:41:25.000000000 +0200 +@@ -0,0 +1,53 @@ ++# ++# Belcarra OTG - On-The-Go ++# ++# Copyright (c) 2004 Belcarra Technologies Corp ++ ++TOPDIR ?= ../../../.. ++ ++subdir-y := ++subdir-m := ++subdir-n := ++subdir- := ++ ++# The target object and module list name. ++ ++O_TARGET := function_target.o ++ ++# Function Drivers ++subdir-$(CONFIG_OTG_ACM) += acm ++subdir-$(CONFIG_OTG_ISOTEST) += isotest ++subdir-$(CONFIG_OTG_MSC) += msc ++subdir-$(CONFIG_OTG_MOUSE) += mouse ++subdir-$(CONFIG_OTG_NETWORK) += network ++ ++# Object file lists. ++ ++obj-y := ++obj-m := ++obj-n := ++obj- := ++ ++# Function drivers ++ifeq ($(CONFIG_OTG_ACM),y) ++obj-y += acm/acm_fd_drv.o ++endif ++ifeq ($(CONFIG_OTG_MOUSE),y) ++obj-y += mouse/mouse_target.o ++endif ++ifeq ($(CONFIG_OTG_NETWORK),y) ++obj-y += network/network_target.o ++endif ++ifeq ($(CONFIG_OTG_MSC),y) ++obj-y += msc/msc_target.o ++endif ++ifeq ($(CONFIG_OTG_PST),y) ++obj-y += pst/pst_target.o ++endif ++ifeq ($(CONFIG_OTG_TEST),y) ++obj-y += test/test_target.o ++endif ++ ++include $(TOPDIR)/Rules.make ++EXTRA_CFLAGS += -Wno-format -Wall ++ +diff -uNr linux/drivers/no-otg/functions/Makefile-l26 linux/drivers/otg/functions/Makefile-l26 +--- linux/drivers/no-otg/functions/Makefile-l26 1970-01-01 01:00:00.000000000 +0100 ++++ linux/drivers/otg/functions/Makefile-l26 2006-09-01 21:41:25.000000000 +0200 +@@ -0,0 +1,12 @@ ++# ++# Belcarra OTG - On-The-Go ++# ++# Copyright (c) 2004 Belcarra Technologies Corp ++ ++EXTRA_CFLAGS += -Wno-format -Wall ++# Function Drivers ++obj-$(CONFIG_OTG_ACM) += acm/ ++obj-$(CONFIG_OTG_MSC) += msc/ ++obj-$(CONFIG_OTG_MOUSE) += mouse/ ++obj-$(CONFIG_OTG_NETWORK) += network/ ++ +diff -uNr linux/drivers/no-otg/functions/acm/Config.in linux/drivers/otg/functions/acm/Config.in +--- linux/drivers/no-otg/functions/acm/Config.in 1970-01-01 01:00:00.000000000 +0100 ++++ linux/drivers/otg/functions/acm/Config.in 2006-09-01 21:41:26.000000000 +0200 +@@ -0,0 +1,28 @@ ++# ++# CDC ACM Function Driver ++# ++# Copyright (C) 2003,2004 Belcarra ++# ++ ++mainmenu_option next_comment ++comment "USB Peripheral Function Driver - CDC ACM" ++ ++dep_tristate ' CDC ACM Function' CONFIG_OTG_ACM $CONFIG_OTG ++if [ "$CONFIG_OTG_ACM" != "n" ]; then ++ hex 'VendorID (hex value)' CONFIG_OTG_ACM_VENDORID "15ec" ++ hex 'ProductID (hex value)' CONFIG_OTG_ACM_PRODUCTID "f002" ++ hex 'bcdDevice (binary-coded decimal)' CONFIG_OTG_ACM_BCDDEVICE "0100" ++ ++ string 'iManufacturer (string)' CONFIG_OTG_ACM_MANUFACTURER "Belcarra" ++ string 'iProduct (string)' CONFIG_OTG_ACM_PRODUCT_NAME "Belcarra ACM Device" ++ ++ string 'iConfiguration (string)' CONFIG_OTG_ACM_DESC "Acm Cfg" ++ string 'Comm Interface iInterface (string)' CONFIG_OTG_ACM_COMM_INTF "Comm Intf" ++ string 'Data Interface iInterface (string)' CONFIG_OTG_ACM_DATA_INTF "Data Intf" ++ ++ comment '' ++ #bool 'Communications Device' CONFIG_OTG_ACM_COMM ++ #bool 'TTY Device' CONFIG_OTG_ACM_TTY ++fi ++ ++endmenu +diff -uNr linux/drivers/no-otg/functions/acm/Kconfig linux/drivers/otg/functions/acm/Kconfig +--- linux/drivers/no-otg/functions/acm/Kconfig 1970-01-01 01:00:00.000000000 +0100 ++++ linux/drivers/otg/functions/acm/Kconfig 2006-09-01 21:41:25.000000000 +0200 +@@ -0,0 +1,54 @@ ++menu "OTG ACM Function" ++ ++config OTG_ACM ++ tristate " CDC ACM Function" ++ depends on OTG ++ ++menu "OTG ACM function options" ++ depends on OTG && OTG_ACM ++ ++config OTG_ACM_VENDORID ++ hex "VendorID (hex value)" ++ depends on OTG_ACM && OTG ++ default "0x15ec" ++ ++config OTG_ACM_PRODUCTID ++ depends on OTG_ACM && OTG ++ hex "ProductID (hex value)" ++ default "0xe003" ++config OTG_ACM_BCDDEVICE ++ depends on OTG_ACM && OTG ++ hex "bcdDevice (binary-coded decimal)" ++ default "0x0100" ++ ++config OTG_ACM_MANUFACTURER ++ depends on OTG_ACM && OTG ++ string "iManufacturer (string)" ++ default "Belcarra" ++ ++config OTG_ACM_PRODUCT_NAME ++ depends on OTG_ACM && OTG ++ string "iProduct (string)" ++ default "Belcarra ACM Device" ++ ++config OTG_ACM_DESC ++ depends on OTG_ACM && OTG ++ string "iConfiguration (string)" ++ default "Acm Cfg" ++ ++config OTG_ACM_COMM_INTF ++ depends on OTG_ACM && OTG ++ string "Comm Interface iInterface (string)" ++ default "Comm Intf" ++ ++config OTG_ACM_DATA_INTF ++ depends on OTG_ACM && OTG ++ string "Data Interface iInterface (string)" ++ default "Data Intf" ++ ++config OTG_ACM_TRACE ++ depends on OTG_ACM && OTG ++ bool " ACM Tracing" ++ default n ++endmenu ++endmenu +diff -uNr linux/drivers/no-otg/functions/acm/Makefile linux/drivers/otg/functions/acm/Makefile +--- linux/drivers/no-otg/functions/acm/Makefile 1970-01-01 01:00:00.000000000 +0100 ++++ linux/drivers/otg/functions/acm/Makefile 2006-09-01 21:41:26.000000000 +0200 +@@ -0,0 +1,80 @@ ++# ++# Function driver for a CDC ACM USB Device ++# ++# Copyright (c) 2003 Belcarra ++ ++# Multipart objects. ++ ++O_TARGET := acm_fd_drv.o ++list-multi := acm_fd.o tty_fd.o ++ ++#modem_fd-objs := acm-fd.o modem-l24-os.o modem.o ++#obex_fd-objs := acm-fd.o obex-l24-os.o obex.o ++tty_fd-objs := acm-fd.o tty-l24-os.o tty-fd.o ++ ++# Objects that export symbols. ++#export-objs := acm-fd.o ++ ++# Object file lists. ++ ++obj-y := ++obj-m := ++obj-n := ++obj- := ++ ++# Each configuration option enables a list of files. ++ ++#obj-$(CONFIG_OTG_ACM) += modem_fd.o ++#obj-$(CONFIG_OTG_ACM) += obex_fd.o ++obj-$(CONFIG_OTG_ACM) += tty_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. ++ ++ACMD=$(OTG)/functions/acm ++ ++OTG_DIR=$(TOPDIR)/drivers/otg ++OTGCORE_DIR=$(OTG_DIR)/otgcore ++#USBDCORE_DIR=$(OTG_DIR)/usbdcore ++include $(TOPDIR)/Rules.make ++EXTRA_CFLAGS += -I$(OTG_DIR) -Wno-unused -Wno-format -I$(OTGCORE_DIR) ++EXTRA_CFLAGS_nostdinc += -I$(OTG_DIR) -Wno-unused -Wno-format -I$(OTGCORE_DIR) ++ ++# Link rules for multi-part drivers. ++ ++modem_fd.o: $(modem_fd-objs) ++ $(LD) -r -o $@ $(modem_fd-objs) ++ ++obex_fd.o: $(obex_fd-objs) ++ $(LD) -r -o $@ $(obex_fd-objs) ++ ++tty_fd.o: $(tty_fd-objs) ++ $(LD) -r -o $@ $(tty_fd-objs) ++ ++# dependencies: ++ ++#modem.o: $(USBDCORE_DIR)/usbd.h $(USBDCORE_DIR)/usbd-bus.h $(USBDCORE_DIR)/usbd-func.h ++#obex.o: $(USBDCORE_DIR)/usbd.h $(USBDCORE_DIR)/usbd-bus.h $(USBDCORE_DIR)/usbd-func.h ++#tty.o: $(USBDCORE_DIR)/usbd.h $(USBDCORE_DIR)/usbd-bus.h $(USBDCORE_DIR)/usbd-func.h ++ +diff -uNr linux/drivers/no-otg/functions/acm/Makefile-l26 linux/drivers/otg/functions/acm/Makefile-l26 +--- linux/drivers/no-otg/functions/acm/Makefile-l26 1970-01-01 01:00:00.000000000 +0100 ++++ linux/drivers/otg/functions/acm/Makefile-l26 2006-09-01 21:41:26.000000000 +0200 +@@ -0,0 +1,13 @@ ++# Function driver for a CDC ACM OTG Device ++# ++# Copyright (c) 2004 Belcarra ++ ++acm_fd-objs := acm-fd.o acm-l26-os.o ++ ++obj-$(CONFIG_OTG_ACM) += acm_fd.o ++ ++OTG=$(TOPDIR)/drivers/otg ++ACMD=$(OTG)/functions/acm ++USBDCORE_DIR=$(OTG)/usbdcore ++EXTRA_CFLAGS += -I$(ACMD) -I$(OTG) -Wno-unused -Wno-format -I$(USBDCORE_DIR) ++EXTRA_CFLAGS_nostdinc += -I$(ACMD) -I$(OTG) -Wno-unused -Wno-format -I$(USBDCORE_DIR) +diff -uNr linux/drivers/no-otg/functions/acm/OBEX-TODO.txt linux/drivers/otg/functions/acm/OBEX-TODO.txt +--- linux/drivers/no-otg/functions/acm/OBEX-TODO.txt 1970-01-01 01:00:00.000000000 +0100 ++++ linux/drivers/otg/functions/acm/OBEX-TODO.txt 2006-09-01 21:41:26.000000000 +0200 +@@ -0,0 +1,16 @@ ++OBEX TODO List Stuart Lynne ++Belcarra Tue Aug 24 21:36:09 PDT 2004 ++ ++ ++1. OBEX documentation ++ ++ ++2. define obex requirements ++ ++ - similiar to acm ++ - uses comm interface for ? ++ - impelements data/nodata inteface ++ ++ - socket family interface ++ - char device interface ++ +diff -uNr linux/drivers/no-otg/functions/acm/TODO.txt linux/drivers/otg/functions/acm/TODO.txt +--- linux/drivers/no-otg/functions/acm/TODO.txt 1970-01-01 01:00:00.000000000 +0100 ++++ linux/drivers/otg/functions/acm/TODO.txt 2006-09-01 21:41:26.000000000 +0200 +@@ -0,0 +1,143 @@ ++ACM TODO List Stuart Lynne ++Belcarra Wed Sep 01 19:48:38 PDT 2004 ++ ++ ++1. The ACM driver needs to be expanded to allow it to build three ways: ++ ++ ++ - tty equivalent of old acm, uses linux tty layer ++ - modem present two simple char devices for data and comm interfaces ++ - obex single simple char device ++ ++TTY ++ - present a single char device with TTY line discipline ++ - TIOCM calls represent "state" of emulated serial port on this ++ side of null modem ++ - notifications and line state only affect far side of null modem ++ ++MODEM ++ - present two simple char device interfaces ++ - data char device will implement TIOCM ioctls ++ - comm char device will use encapsulated data over endpoint zero ++ - TIOCM calls represent state passed to/from host via notifications ++ and line state requests ++ ++OBEX ++ - present a single simple char device interface ++ - no requirement for either TIOCM or TTY line discipline ++ - TIOCM calls not implemented ++ ++ ++ ++2. The modem device must implement a virtual NULL modem and support the ++following IOCTL's. ++ ++ ++ ++Virtual NULL Modem ++ ++Peripheral to Host via Serial State Notification (write ioctls) ++ ++Application TIOCM Null Modem ACM (DCE) Notificaiton Host (DTE) ++------------------------------------------------------------------------------------------------------------- ++Request to send TIOCM_RTS RTS -> CTS CTS Not Available Clear to Send (N/A) ++Data Terminal Ready TIOCM_DTR DTR -> DSR DSR bTxCarrier Data Set Ready ++ DTR -> DCD DCD bRXCarrier Carrier Detect ++Ring Indicator TIOCM_OUT1 OUT1 -> RI RI bRingSignal Ring Indicator ++Send Break TIOCM_OUT2 OUT2 -> Break Break bBreak Break Received ++------------------------------------------------------------------------------------------------------------- ++ ++ ++Host to Peripheral via Set Control Line State ++ ++Host (DTE) Line State ACM (DCE) Null Modem TIOCM Peripheral (DTE) ++------------------------------------------------------------------------------------------------------------- ++Data Terminal Ready D0 DTR DTR -> DSR TIOCM_DSR Data Set Ready ++ DTR -> DCD TIOCM_CAR Carrier Detect ++Request To Send D1 RTS RTS -> CTS TIOCM_CTS Clear to Send ++------------------------------------------------------------------------------------------------------------- ++ ++ ++3. The following DEVICE REQUESTS have to be implemented and where ++possible support added to hook the results to the upper layers ++and applications. ++ ++ SetCommFeature ++ GetCommFeature ++ ClearCommFeature ++ ++ SendEncapsulatedCommand ++ GetEncapsulatedResponse ++ ++ SetLineCoding ++ GetLineCoding ++ ++ SetControlLineState ++ ++ SendBreak ++ ++4. The following NOTIFICATIONS need to be implemented and where ++possible hooked to appropriate indications from upper layers ++and applications. ++ ++ ++ RESPONSE_AVAILABLE ++ NETWORK_CONNECTION ++ SERIAL_STATE ++ ++ ++ ++5. ACM documentation needs to be updated to reflect implementation(s). ++ ++ ++6. POSIX IOCTLS ++ - TIOCM* ++ - hook into flow control or set from flow control as required ++ - baudrate and other device settings ++ - CTS should disable receive urb ++ ++7. COMM Interface ++ - GET / SEND Encapsulated command ++ - response available notification ++ - char device interface ++ ++8. Optional TTY ++ - register as TTY device only if requested ++ - register as simple char device otherwise ++ ++9. select() ++ - trackdown tty layer problem with select() ++ - sometimes fails under stress testing ++ ++ ++Notes.. ++ ++1. implementation of the comm and simple char device can probably share ++the same code base. These should queue received urbs. This will mean ++a small change in the os between acm_recv_urb() and the os specific ++layer. ++ ++2. Now it is acm_os_recv_chars(), this must change to acm_os_recv_urbs(). ++The acm_os_recv_urbs() function will be responsible for deallocing the ++urb when delivered. ++ ++3. The various os specific functions will need to have a method to ++distinguish between the comm and data interfaces. ++ ++ ++ ++ ++10. WMC - Wireless Mobice Class ++ ++ Call Management - bmCapabilities allow call management over data interface ++ ACM - bmCapabilities - 0x06, only SEND_BREAK, SET/GET LINE_CODING ++ ++ NETWORK_CONNECTION, XXX_COMM_FEATURE not allowed ++ ++ Call management over COMM interface optional ++ ++ COMM_FEATURE - optional? ++ ++ ++ ++ +diff -uNr linux/drivers/no-otg/functions/acm/acm-fd.c linux/drivers/otg/functions/acm/acm-fd.c +--- linux/drivers/no-otg/functions/acm/acm-fd.c 1970-01-01 01:00:00.000000000 +0100 ++++ linux/drivers/otg/functions/acm/acm-fd.c 2006-09-01 21:41:26.000000000 +0200 +@@ -0,0 +1,1077 @@ ++/* ++ * otg/functions/acm/acm-fd.c ++ * ++ * Copyright (c) 2003, 2004 Belcarra ++ * ++ * By: ++ * Tom Rushworth <tbr@belcarra.com>, ++ * Stuart Lynne <sl@belcarra.com>, ++ * Bruce Balden <balden@belcarra.com> ++ */ ++/*! ++ * @file otg/functions/acm/acm-fd.c ++ * @brief ACM Function Driver private defines ++ * ++ * An ACM (Abstract Control Model) driver is composed of several pieces: ++ * ++ * 1) An OS and function specific piece that handles creating and operating ++ * a device for the given OS suitable for a specific function. ++ * ++ * 2) A set descriptors suitable for the function. ++ * ++ * 3) This acm-fd library which implements the interface to ++ * the usb peripheral and otgcore stacks. ++ * ++ * If the USB piece interfaces with the host usbcore layer you get ++ * an ACM class driver. If the USB piece interfaces with the otgcore ++ * layer you get an ACM function driver. ++ * ++ * ++ * @ingroup ACMFunction ++ */ ++ ++ ++#include <otg/otg-compat.h> ++#include <otg/otg-module.h> ++ ++#include <linux/init.h> ++#include <asm/uaccess.h> ++#include <linux/ctype.h> ++#include <linux/timer.h> ++#include <linux/interrupt.h> ++#include <asm/atomic.h> ++#include <linux/smp_lock.h> ++#include <linux/slab.h> ++ ++#include <otg/usbp-chap9.h> ++#include <otg/usbp-func.h> ++ ++#include <otg/otg-trace.h> ++#include <otg/otg-api.h> ++#include "acm.h" ++#include "acm-fd.h" ++#include "acm-os.h" ++ ++ ++static u32 max_queued_urbs; ++static u32 max_queued_bytes; ++ ++// Define the low order 16 bits of an urb's memory address as it's ID for tracing. ++#define urbID(urb) (0xffff & (u32) (void *) urb) ++ ++/* ******************************************************************************************* */ ++ ++STATIC int acm_send_int_notification(struct acm_private *acm, int , int ); ++STATIC int acmfd_urb_sent_bulk (struct usbd_urb *urb, int rc); ++STATIC int acmfd_urb_sent_int (struct usbd_urb *urb, int rc); ++STATIC int acmfd_recv_urb (struct usbd_urb *urb, int rc); ++STATIC void acm_schedule_recv(struct acm_private *acm, int interface); ++ ++/* ******************************************************************************************* */ ++ ++/*! acm_ready ++ * @param acm ++ */ ++STATIC int acm_ready(struct acm_private *acm) ++{ ++ TRACE_MSG5(acm->trace_tag,"CONFIGURED: %x OPENED: %x THROTTLED: %x CARRIER: %x READY: %d", ++ acm->flags & ACM_CONFIGURED, acm->flags & ACM_OPENED, ++ acm->flags & ACM_THROTTLED, acm->flags & ACM_CARRIER, ++ (acm->flags & ACM_CONFIGURED) && (acm->flags & ACM_CARRIER) ? 1 : 0); ++ ++ return (acm->flags & ACM_CONFIGURED) && ++ (acm->flags & ACM_OPENED) && ++ !(acm->flags & ACM_THROTTLED) && ++ ((acm->flags & ACM_LOCAL) ? 1 : (acm->flags & ACM_CARRIER) ) ++ ; ++ ++} ++ ++/*! acm_open ++ * @param acm ++ * @param interface ++ * @return int ++ */ ++STATIC int acm_open(struct acm_private *acm, int interface) ++{ ++ TRACE_MSG0(acm->trace_tag,"OPEN"); ++ acm->flags |= ACM_OPENED; ++ acm->flags &= ~ACM_THROTTLED; ++ acm->bmUARTState = CDC_UARTSTATE_BRXCARRIER_DCD | CDC_UARTSTATE_BTXCARRIER_DSR; ++ acm_schedule_recv(acm, interface); ++ TRACE_MSG1(acm->trace_tag,"bmUARTState: %04x", acm->bmUARTState); ++ acm_send_int_notification(acm, CDC_NOTIFICATION_SERIAL_STATE, acm->bmUARTState); ++ return 0; ++} ++ ++/*! acm_flush ++ * @param acm ++ * @param interface ++ * @return number of urbs in queue ++ */ ++STATIC void acm_flush(struct acm_private *acm, int interface) ++{ ++ struct usbd_function_instance *function = acm->function; ++ unsigned long flags; ++ TRACE_MSG0(acm->trace_tag,"FLUSH"); ++ RETURN_UNLESS(function); ++ local_irq_save(flags); ++ acm->bytes_received = 0; ++ acm->bytes_forwarded = 0; ++ acm->bmUARTState = CDC_UARTSTATE_BRXCARRIER_DCD | CDC_UARTSTATE_BTXCARRIER_DSR; ++ TRACE_MSG1(acm->trace_tag,"bmUARTState: %04x", acm->bmUARTState); ++ // TBR: 20040705 use ...le32() not le16, spotted by Zhao Liang ++ acm->line_coding.dwDTERate = cpu_to_le32(0x1c200); // 115200 ++ acm->line_coding.bDataBits = 0x08; ++ ++ usbd_flush_endpoint_index(function, BULK_IN); ++ usbd_flush_endpoint_index(function, BULK_OUT); ++ local_irq_restore(flags); ++} ++ ++/*! acm_close ++ * @param acm ++ * @param interface ++ * @return number of urbs in queue ++ */ ++STATIC int acm_close(struct acm_private *acm, int interface) ++{ ++ TRACE_MSG0(acm->trace_tag,"CLOSE"); ++ acm->flags &= ~ACM_OPENED; ++ acm->flags &= ~ACM_THROTTLED; ++ acm_flush(acm, interface); ++ acm->bmUARTState = 0; ++ TRACE_MSG1(acm->trace_tag,"bmUARTState: %04x", acm->bmUARTState); ++ acm_send_int_notification(acm, CDC_NOTIFICATION_SERIAL_STATE, acm->bmUARTState); ++ return 0; ++} ++ ++ ++/*! acmfd_start_recv_urbs ++ * @param acm ++ * @param interface ++ * @return number of urbs in queue ++ */ ++STATIC int acmfd_start_recv_urbs(struct acm_private *acm, int interface) ++{ ++ /* ++ * Queue as many receive urbs as the OS layer has room for. Return ++ * the number in the queue (may be more than we queue here). ++ */ ++ struct usbd_function_instance *function = acm->function; ++ unsigned long flags; ++ int num_in_queue = 0; ++ ++ switch (interface) { ++ case COMM_INTF: ++ return 0; ++ case DATA_INTF: ++ TRACE_MSG1(acm->trace_tag,"START RECV: acm: %x", (int)acm); ++ TRACE_MSG2(acm->trace_tag,"START RECV: connected: %x recv_urbs: %d", ++ acm->flags & ACM_CONFIGURED, acm->recv_urbs); ++ ++ local_irq_save(flags); ++ if (acm_ready(acm)) { ++ ++ TRACE_MSG2(acm->trace_tag,"START RECV: throttled: %d space_avail: %d", acm->flags & ACM_THROTTLED, ++ acm->function_services->recv_space_available(acm, DATA_INTF)); ++ ++ while (((acm->recv_urbs + 1) * 64) < acm->function_services->recv_space_available(acm, DATA_INTF)) { ++ struct usbd_urb *urb; ++ ++ BREAK_IF(!(urb = usbd_alloc_urb(function, BULK_OUT, usbd_endpoint_transferSize(function, ++ BULK_OUT, usbd_high_speed(function)), acmfd_recv_urb))); ++ acm->recv_urbs++; ++ urb->function_privdata = acm; ++ TRACE_MSG3(acm->trace_tag,"START RECV: %d urb#%p privdata#%p",acm->recv_urbs,urb,acm); ++ CONTINUE_UNLESS (usbd_start_out_urb(urb)); ++ acm->recv_urbs--; ++ TRACE_MSG1(acm->trace_tag,"START RECV: %d", acm->recv_urbs); ++ usbd_free_urb(urb); ++ break; ++ } ++ if (!acm->recv_urbs) ++ acm_schedule_recv(acm, interface); ++ } ++ /* There needs to be at least one recv urb queued in order ++ * to keep driving the push to the OS layer, so return how ++ * many there are in case the OS layer must try again later. ++ */ ++ num_in_queue = acm->recv_urbs; ++ local_irq_restore(flags); ++ break; ++ } ++ return(num_in_queue); ++} ++ ++ ++struct acm_work acm_work; ++ ++/*! acmfd_start_recv ++ * @param data ++ */ ++STATIC void acmfd_start_recv(void *data) ++{ ++ struct acm_work *work = data; ++ struct acm_private *acm; ++ unsigned long flags; ++ int interface; ++ ++ local_irq_save(flags); ++ acm = work->acm; ++ interface = work->interface; ++ work->acm = NULL; ++ work->interface = -1; ++ acm->recv_tqueue.data = NULL; ++ local_irq_restore(flags); ++ acmfd_start_recv_urbs(acm, interface); ++} ++ ++ ++/*! acm_schedule_recv ++ * @param acm ++ * @param interface ++ */ ++void acm_schedule_recv(struct acm_private *acm, int interface) ++{ ++ unsigned long flags; ++ struct acm_work *work = &acm_work; ++ TRACE_MSG1(acm->trace_tag, "sync: %d", acm->recv_tqueue.sync); ++ local_irq_save(flags); ++ ++ UNLESS (acm->recv_tqueue.sync && acm->recv_tqueue.data) { ++ work->acm = acm; ++ work->interface = interface; ++ acm->recv_tqueue.routine = &acmfd_start_recv; ++ acm->recv_tqueue.data = work; ++ #if defined(LINUX24) ++ queue_task(&acm->recv_tqueue, &tq_timer); ++ #else ++ SCHEDULE_WORK(acm->recv_tqueue); ++ #endif ++ } ++ ++ local_irq_restore(flags); ++} ++ ++/* Transmit INTERRUPT ************************************************************************** */ ++ ++/*! acm_send_int_notfication ++ * Generates a response urb on the notification (INTERRUPT) endpoint. ++ * CALLED from interrupt context. ++ * @return non-zero if error ++ */ ++STATIC int acm_send_int_notification(struct acm_private *acm, int bnotification, int data) ++{ ++ struct usbd_function_instance *function = acm->function; ++ struct usbd_urb *urb = NULL; ++ struct cdc_notification_descriptor *notification; ++ unsigned long flags; ++ int rc = 0; ++ ++ TRACE_MSG4(acm->trace_tag,"used: %d MOD_IN_USE: %d configured: %d data: %04x", ++ atomic_read(&acm->used), MOD_IN_USE, acm->flags & ACM_CONFIGURED, data); ++ ++ RETURN_ZERO_UNLESS((acm->flags & ACM_CONFIGURED)); ++ ++ local_irq_save(flags); ++ ++ do { ++ BREAK_IF(!(urb = usbd_alloc_urb(function, INT_IN, ++ sizeof(struct cdc_notification_descriptor), ++ acmfd_urb_sent_int))); ++ ++ urb->function_privdata = acm; ++ ++ memset(urb->buffer, 0, urb->buffer_length); ++ urb->actual_length = sizeof(struct cdc_notification_descriptor); ++ ++ /* fill in notification structure */ ++ notification = (struct cdc_notification_descriptor *) urb->buffer; ++ ++ notification->bmRequestType = USB_DIR_IN | USB_TYPE_CLASS | USB_RECIP_INTERFACE; ++ notification->bNotification = bnotification; ++ ++ switch (bnotification) { ++ case CDC_NOTIFICATION_NETWORK_CONNECTION: ++ notification->wValue = data; ++ break; ++ case CDC_NOTIFICATION_SERIAL_STATE: ++ notification->wLength = cpu_to_le16(2); ++ *((unsigned short *)notification->data) = cpu_to_le16(data); ++ break; ++ } ++ ++ BREAK_IF(!(rc = usbd_start_in_urb (urb))); ++ ++ urb->function_privdata = NULL; ++ usbd_free_urb (urb); ++ ++ TRACE_MSG1(acm->trace_tag,"urbID#%04x --> 0",urbID(urb)); ++ ++ } while(0); ++ ++ local_irq_restore(flags); ++ return(rc); ++} ++ ++/* Callback functions for TX urb completion (function chosen when urb is allocated) ***********/ ++ ++/*! acmfd_urb_sent_bulk - called to indicate bulk URB transmit finished ++ * @param urb pointer to struct usbd_urb ++ * @param rc result ++ * @return non-zero for error ++ */ ++STATIC int acmfd_urb_sent_bulk (struct usbd_urb *urb, int rc) ++{ ++ struct acm_private *acm = urb->function_privdata; ++ struct usbd_function_instance *function; ++ ++ TRACE_MSG2(acm->trace_tag,"entered urbID#%04x rc=%d",urbID(urb),rc); ++ TRACE_MSG3(acm->trace_tag,"used: %d MOD_IN_USE: %d configured: %d", ++ atomic_read(&acm->used), MOD_IN_USE, acm->flags & ACM_CONFIGURED); ++ ++ if (!urb || !(function = urb->function_instance)) { ++ TRACE_MSG1(acm->trace_tag,"urbID#%04x --> -EINVAL",urbID(urb)); ++ return(-EINVAL); ++ } ++ TRACE_MSG1(acm->trace_tag,"IN length=%d",urb->actual_length); ++ ++ atomic_sub(urb->actual_length, &acm->queued_bytes); ++ atomic_dec(&acm->queued_urbs); ++ urb->function_privdata = NULL; ++ usbd_free_urb (urb); ++ if (acm->flags & ACM_OPENED) ++ acm->function_services->schedule_wakeup_writers(acm); ++ TRACE_MSG1(acm->trace_tag,"urbID#%04x --> 0",urbID(urb)); ++ return 0; ++} ++ ++/*! acmfd_urb_sent_int - called to indicate int URB transmit finished ++ * @param urb pointer to struct usbd_urb ++ * @param rc result ++ * @return non-zero for error ++ */ ++STATIC int acmfd_urb_sent_int (struct usbd_urb *urb, int rc) ++{ ++ struct acm_private *acm = urb->function_privdata; ++ struct usbd_function_instance *function; ++ ++ TRACE_MSG2(acm->trace_tag,"entered urbID#%04x rc=%d",urbID(urb),rc); ++ TRACE_MSG3(acm->trace_tag,"used: %d MOD_IN_USE: %d configured: %d", ++ atomic_read(&acm->used), MOD_IN_USE, acm->flags & ACM_CONFIGURED); ++ if (!urb || !(function = urb->function_instance)) { ++ TRACE_MSG1(acm->trace_tag,"urbID#%04x --> -EINVAL",urbID(urb)); ++ return(-EINVAL); ++ } ++ TRACE_MSG1(acm->trace_tag,"INT length=%d",urb->actual_length); ++ ++ urb->function_privdata = NULL; ++ usbd_free_urb(urb); ++ TRACE_MSG1(acm->trace_tag,"urbID#%04x --> 0",urbID(urb)); ++ return 0; ++} ++ ++/*! acmfd_urb_sent_ep0 - called to indicate ep0 URB transmit finished ++ * @param urb pointer to struct usbd_urb ++ * @param rc result ++ * @return non-zero for error ++ */ ++STATIC int acmfd_urb_sent_ep0 (struct usbd_urb *urb, int rc) ++{ ++ struct acm_private *acm = urb->function_privdata; ++ struct usbd_function_instance *function; ++ ++ TRACE_MSG2(acm->trace_tag,"entered urbID#%04x rc=%d",urbID(urb),rc); ++ TRACE_MSG3(acm->trace_tag,"used: %d MOD_IN_USE: %d configured: %d", ++ atomic_read(&acm->used), MOD_IN_USE, acm->flags & ACM_CONFIGURED); ++ ++ RETURN_EINVAL_IF (!urb || !(function = urb->function_instance)); ++ ++ TRACE_MSG1(acm->trace_tag,"INT length=%d",urb->actual_length); ++ ++ urb->function_privdata = NULL; ++ usbd_free_urb(urb); ++ return 0; ++} ++ ++/* USB Device Functions ************************************************************************ */ ++ ++typedef enum mesg { ++ mesg_unknown, ++ mesg_configured, ++ mesg_reset, ++} mesg_t; ++mesg_t acm_last_mesg; ++ ++char * acm_messages[3] = { ++ "", ++ "ACM Configured", ++ "ACM Reset", ++}; ++ ++ ++/*! acmfd_check_mesg ++ * @param curr_mesg ++ */ ++void acmfd_check_mesg(mesg_t curr_mesg) ++{ ++ RETURN_UNLESS(acm_last_mesg != curr_mesg); ++ acm_last_mesg = curr_mesg; ++ otg_message(acm_messages[curr_mesg]); ++} ++ ++ ++/*! acmfd_event_handler - process a device event ++ * @param function ++ * @param event ++ * @param data ++ */ ++void acmfd_event_handler (struct usbd_function_instance *function, usbd_device_event_t event, int data) ++{ ++ struct acm_private *acm = function->privdata; ++ int i; ++ ++ TRACE_MSG1(acm->trace_tag,"entered ev: %d",event); ++ TRACE_MSG3(acm->trace_tag,"used: %d MOD_IN_USE: %d ready: %d", atomic_read(&acm->used), MOD_IN_USE, acm_ready(acm)); ++ ++ switch (event) { ++ ++ case DEVICE_CONFIGURED: ++ TRACE_MSG0(acm->trace_tag,"CONFIGURED"); ++ acm->flags |= ACM_CONFIGURED; ++ acmfd_check_mesg(mesg_configured); ++ acmfd_start_recv_urbs(acm, DATA_INTF); ++ break; ++ ++ case DEVICE_RESET: ++ case DEVICE_DE_CONFIGURED: ++ TRACE_MSG0(acm->trace_tag,"RESET"); ++ ++ /* if configured and open then schedule hangup ++ */ ++ if ((acm->flags & ACM_OPENED) && (acm->flags & ACM_CONFIGURED)) ++ acm->function_services->schedule_hangup(acm); ++ ++ acm->flags &= ~(ACM_CONFIGURED | ACM_CARRIER); ++ acmfd_check_mesg(mesg_reset); ++ BREAK_IF(!acm->flags & ACM_CONFIGURED); ++ TRACE_MSG0(acm->trace_tag,"RESET continue"); ++ // XXX flush ++ // Release any queued urbs ++ break; ++ ++ default: ++ break; ++ } ++ TRACE_MSG0(acm->trace_tag,"exited"); ++} ++ ++/*! acm_write_room ++ * @param acm ++ * @param interface ++ * @return non-zero if error ++ */ ++STATIC int acm_write_room(struct acm_private *acm, int interface) ++{ ++ switch (interface) { ++ case COMM_INTF: ++ return 0; ++ case DATA_INTF: ++ return (!acm->function || max_queued_urbs <= atomic_read(&acm->queued_urbs) || ++ max_queued_bytes <= atomic_read(&acm->queued_bytes) ) ? 0 : acm->writesize ; ++ } ++ return 0; ++} ++ ++/*! acm_chars_in_buffer ++ * @param acm ++ * @param interface ++ * @return non-zero if error ++ */ ++STATIC int acm_chars_in_buffer(struct acm_private *acm, int interface) ++{ ++ int rc; ++ switch (interface) { ++ case COMM_INTF: ++ return 0; ++ case DATA_INTF: ++ return atomic_read(&acm->queued_bytes); ++ } ++ return 0; ++} ++ ++static int throttle_count = 0; ++static int unthrottle_count = 0; ++ ++/*! acm_throttle ++ * @param acm ++ * @param interface ++ */ ++STATIC void acm_throttle(struct acm_private *acm, int interface) ++{ ++ switch (interface) { ++ case COMM_INTF: ++ break; ++ case DATA_INTF: ++ throttle_count += 1; ++ TRACE_MSG1(acm->trace_tag,"entered %d",throttle_count); ++ acm->flags |= ACM_THROTTLED; ++ TRACE_MSG1(acm->trace_tag,"exited %d",throttle_count); ++ break; ++ } ++} ++ ++/*! acm_unthrottle ++ * @param acm ++ * @param interface ++ */ ++STATIC void acm_unthrottle(struct acm_private *acm, int interface) ++{ ++ switch (interface) { ++ case COMM_INTF: ++ break; ++ case DATA_INTF: ++ unthrottle_count += 1; ++ TRACE_MSG1(acm->trace_tag,"entered %d",unthrottle_count); ++ acm->flags &= ~ACM_THROTTLED; ++ TRACE_MSG1(acm->trace_tag,"exited %d",unthrottle_count); ++ UNLESS(acm->recv_urbs) ++ acm_schedule_recv(acm, interface); ++ break; ++ } ++} ++ ++/*! acm_xmit_chars ++ * @param acm ++ * @param interface ++ * @param count ++ * @param from_user ++ * @param buf ++ * @return number of bytes sent. ++ */ ++STATIC int acm_xmit_chars(struct acm_private *acm, int interface, int count, int from_user, const unsigned char *buf) ++{ ++ struct usbd_function_instance *function = acm->function; ++ struct usbd_urb *urb; ++ ++ RETURN_ZERO_UNLESS((acm->flags & ACM_CONFIGURED) /*&& (acm->flags & ACM_CARRIER)*/); ++ ++ TRACE_MSG2(acm->trace_tag, "count: %d from_user: %d", count, from_user); ++ ++ switch (interface) { ++ case COMM_INTF: ++ return 0; ++ case DATA_INTF: ++ // sanity check and are we connect ++ RETURN_ZERO_UNLESS(atomic_read(&acm->used)); ++ TRACE_MSG0(acm->trace_tag,"used OK"); ++ RETURN_ZERO_UNLESS (count); ++ RETURN_ZERO_UNLESS (acm->flags & ACM_CONFIGURED); ++ TRACE_MSG0(acm->trace_tag,"connected OK"); ++ RETURN_ZERO_IF(max_queued_urbs <= atomic_read(&acm->queued_urbs)); ++ TRACE_MSG0(acm->trace_tag,"max_queued_urbs OK"); ++ ++ // allocate a write urb ++ count = MIN(count, acm->writesize); ++ ++ RETURN_ZERO_UNLESS ((urb = usbd_alloc_urb (function, BULK_IN, count, acmfd_urb_sent_bulk))); ++ ++ if (from_user) ++ copy_from_user ((void *)urb->buffer, (void *)buf, count); ++ else ++ memcpy ((void *)urb->buffer, (void *)buf, count); ++ ++ urb->function_privdata = acm; ++ urb->actual_length = count; ++ atomic_add(count, &acm->queued_bytes); ++ atomic_inc(&acm->queued_urbs); ++ usbd_start_in_urb(urb); ++ TRACE_MSG2(acm->trace_tag,"urbID#%04x --> count: %d",urbID(urb),count); ++ return count; ++ default: ++ break; ++ } ++ return 0; ++} ++ ++/*! acmfd_recv_urb ++ * @param urb ++ * @param rc ++ * @return non-zero if error ++ */ ++STATIC int acmfd_recv_urb (struct usbd_urb *urb, int rc) ++{ ++ /* Return 0 if urb has been accepted, ++ * return 1 and expect caller to deal with urb release otherwise. ++ */ ++ struct acm_private *acm = urb->function_privdata; ++ unsigned long flags; ++ ++ local_irq_save(flags); ++ acm->recv_urbs--; // this could probably be atomic operation.... ++ local_irq_restore(flags); ++ ++ if (RECV_CANCELLED == rc) { ++ TRACE_MSG1(acm->trace_tag,"cancelled URB=%p",urb); ++ return -EINVAL; ++ } ++ if (RECV_OK != rc) { ++ TRACE_MSG2(acm->trace_tag,"rejected URB=%p rc=%d",urb,rc); ++ usbd_free_urb(urb); ++ acm_schedule_recv(acm, DATA_INTF); ++ //acmfd_start_recv_urbs(acm, DATA_INTF); ++ return 0; ++ } ++ ++ /* loopback mode ++ */ ++ if (acm->flags & ACM_LOOPBACK) ++ acm_xmit_chars(acm, DATA_INTF, urb->actual_length, 0, urb->buffer); ++ ++ /* acmfd_start_recv_urbs() will never queue more urbs than there is currently ++ * room in the upper layer buffer for. So we are guaranteed that any data actually ++ * received can be given to the upper layers without worrying if we will ++ * actually have room. ++ */ ++ else ++ if ((rc = acm->function_services->recv_chars(acm, DATA_INTF, urb->buffer, urb->actual_length))) ++ return(rc); // XXX ++ ++ ++ acm->bytes_received += urb->actual_length; ++ TRACE_MSG1(acm->trace_tag,"bytes_received: %d",acm->bytes_received); ++ usbd_free_urb(urb); ++ acm_schedule_recv(acm, DATA_INTF); ++ return 0; ++ ++#if 0 ++ // XXX it may be reasonable to schedule here.... ++ UNLESS (acmfd_start_recv_urbs(acm, DATA_INTF)) { ++ /* If start recv returns zero it means that there are no-queued urbs, ++ * and we should queue a work item to restart. ++ */ ++ acm_schedule_recv(acm, DATA_INTF); ++ } ++#endif ++ return 0; ++} ++ ++/*! acmfd_line_coding_urb_received - callback for sent URB ++ * ++ * Handles notification that an urb has been sent (successfully or otherwise). ++ * ++ * @param urb ++ * @param urb_rc ++ * @return non-zero for failure. ++ */ ++STATIC int acmfd_line_coding_urb_received (struct usbd_urb *urb, int urb_rc) ++{ ++ struct acm_private *acm = urb->function_privdata; ++ TRACE_MSG2(acm->trace_tag,"urbID#%04x rc=%d",urbID(urb),urb_rc); ++ ++ RETURN_EINVAL_IF (RECV_OK != urb_rc); ++ RETURN_EINVAL_IF (urb->actual_length < sizeof(struct cdc_acm_line_coding)); ++ ++ RETURN_EINVAL_UNLESS (memcpy(&acm->line_coding, urb->buffer, sizeof(struct cdc_acm_line_coding))); ++ ++ // something changed, copy and notify ++ ++ memcpy(&acm->line_coding, urb->buffer, sizeof(struct cdc_acm_line_coding)); ++ ++ // XXX notify application if baudrate has changed ++ ++ return -EINVAL; // caller will de-allocate ++} ++ ++/*! acmfd_device_request - called to indicate urb has been received ++ * @param function ++ * @param request ++ * @return non-zero if error ++ */ ++int acmfd_device_request (struct usbd_function_instance *function, struct usbd_device_request *request) ++{ ++ struct acm_private *acm = (struct acm_private *) (function->privdata); ++ ++ TRACE_MSG3(acm->trace_tag,"used: %d MOD_IN_USE: %d configured: %d", ++ atomic_read(&acm->used), MOD_IN_USE, acm->flags & ACM_CONFIGURED); ++ ++ TRACE_SETUP(acm->trace_tag,request); ++ ++ // verify that this is a usb class request per cdc-acm specification or a vendor request. ++ if (!(request->bmRequestType & (USB_REQ_TYPE_CLASS | USB_REQ_TYPE_VENDOR))) { ++ TRACE_MSG0(acm->trace_tag,"--> 0"); ++ return(0); ++ } ++ ++ // determine the request direction and process accordingly ++ switch (request->bmRequestType & (USB_REQ_DIRECTION_MASK | USB_REQ_TYPE_MASK)) { ++ ++ case USB_REQ_HOST2DEVICE | USB_REQ_TYPE_CLASS: ++ switch (request->bRequest) { ++ case CDC_CLASS_REQUEST_SEND_ENCAPSULATED: break; ++ case CDC_CLASS_REQUEST_SET_COMM_FEATURE: break; ++ case CDC_CLASS_REQUEST_CLEAR_COMM_FEATURE: break; ++ case CDC_CLASS_REQUEST_SET_LINE_CODING: ++ { ++ struct usbd_urb *urb; ++ int len = le16_to_cpu(request->wLength); ++ TRACE_MSG1(acm->trace_tag,"SET_LINE_CODING wLength=%d",len); ++ if (len <= 0) { ++ TRACE_MSG0(acm->trace_tag,"(len<=0)--> 0"); ++ return(0); ++ } ++ ++ /* Set up an ep0 recv urb for the rest of it. */ ++ UNLESS ((urb = usbd_alloc_urb_ep0(function, len, acmfd_line_coding_urb_received))) { ++ TRACE_MSG0(acm->trace_tag,"no mem for ep0 recv urb"); ++ return(-ENOMEM); ++ } ++ urb->function_privdata = acm; ++ if (usbd_start_out_urb(urb)) { ++ TRACE_MSG0(acm->trace_tag,"usbd_start_out_urb() failed"); ++ usbd_free_urb(urb); // de-alloc if error ++ TRACE_MSG0(acm->trace_tag,"--> -EINVAL"); ++ return(-EINVAL); ++ } ++ } ++ break; ++ ++ case CDC_CLASS_REQUEST_SET_CONTROL_LINE_STATE: ++ { ++ unsigned int prev_bmLineState = acm->bmLineState; ++ acm->bmLineState = le16_to_cpu(request->wValue); ++ ++ // schedule writers or hangup IFF open ++ BREAK_IF(!acm->privdata); ++ TRACE_MSG3(acm->trace_tag,"set control state, bmLineState: %04x previous: %04x changed: %04x", ++ acm->bmLineState, prev_bmLineState, acm->bmLineState ^ prev_bmLineState); ++ ++ // make sure there really is a state change ++ if ((acm->bmLineState ^ prev_bmLineState) & CDC_LINESTATE_D0_DTR) { ++ ++ TRACE_MSG1(acm->trace_tag,"DTR state changed -> %x", ++ (acm->bmLineState & CDC_LINESTATE_D0_DTR)); ++ ++ if (acm->bmLineState & CDC_LINESTATE_D0_DTR) { ++ if (acm->flags & ACM_OPENED) ++ acm->function_services->schedule_wakeup_writers(acm); ++ acm->flags |= ACM_CARRIER; ++ acm_schedule_recv(acm, DATA_INTF); ++ } ++ else { ++ if (acm->flags & ACM_OPENED) ++ acm->function_services->schedule_hangup(acm); ++ ++ acm->flags &= ~ACM_CARRIER; ++ acm_flush(acm, DATA_INTF); ++ } ++ ++ /* wake up blocked opens */ ++ acm->function_services->wakeup_opens(acm); ++ ++ /* wake up blocked ioctls */ ++ acm->function_services->wakeup_state(acm); ++ ++ } ++ ++ ++ ++ /* send notification if we have DCD */ ++ TRACE_MSG2(acm->trace_tag,"bmUARTState: %04x privdata: %p sending (DCD|DSR) notification", ++ acm->bmUARTState, acm->privdata); ++ ++ acm_send_int_notification(acm, CDC_NOTIFICATION_SERIAL_STATE, acm->bmUARTState); ++ } ++ break; ++ ++ case CDC_CLASS_REQUEST_SEND_BREAK: break; ++ default: break; ++ } ++ TRACE_MSG0(acm->trace_tag,"--> 0"); ++ return 0; ++ ++ case USB_REQ_DEVICE2HOST | USB_REQ_TYPE_CLASS: ++ switch (request->bRequest) { ++ case CDC_CLASS_REQUEST_GET_ENCAPSULATED: break; ++ case CDC_CLASS_REQUEST_GET_COMM_FEATURE: break; ++ case CDC_CLASS_REQUEST_GET_LINE_CODING: ++ { ++ struct usbd_urb *urb; ++ RETURN_ENOMEM_IF (!(urb = usbd_alloc_urb_ep0(function, sizeof(struct cdc_acm_line_coding), ++ acmfd_urb_sent_ep0))); ++ urb->function_privdata = acm; ++ ++ memcpy(urb->buffer, &acm->line_coding, sizeof(struct cdc_acm_line_coding)); ++ ++ urb->actual_length = sizeof(struct cdc_acm_line_coding); ++ ++ TRACE_MSG1(acm->trace_tag,"sending line coding urb: %p",(u32)(void*)urb); ++ RETURN_ZERO_UNLESS(usbd_start_in_urb(urb)); ++ usbd_free_urb(urb); ++ TRACE_MSG0(acm->trace_tag,"(send failed)--> -EINVAL"); ++ } ++ return -EINVAL; ++ default: break; ++ } ++ TRACE_MSG0(acm->trace_tag,"--> 0"); ++ return 0; ++ ++ case USB_REQ_HOST2DEVICE | USB_REQ_TYPE_VENDOR: break; ++ case USB_REQ_DEVICE2HOST | USB_REQ_TYPE_VENDOR: break; ++ ++ default: break; ++ } ++ TRACE_MSG0(acm->trace_tag,"--> 0"); ++ return 0; ++} ++ ++ ++/*! acmfd_function_enable ++ * @param function ++ * @return non-zero if error ++ */ ++STATIC int acmfd_function_enable (struct usbd_function_instance *function) ++{ ++ struct acm_private *acm = (struct acm_private *) (function->privdata); ++ ++ TRACE_MSG0(acm->trace_tag,"entered"); ++ TRACE_MSG1(acm->trace_tag,"INC: %d", MOD_IN_USE); ++ acm->function_services->enable(function); ++ acm->writesize = usbd_endpoint_wMaxPacketSize(function, BULK_OUT, 0) * 24; ++ ++ TRACE_MSG0(acm->trace_tag,"-> 0"); ++ return 0; ++} ++ ++/*! acmfd_function_disable ++ * @param function ++ */ ++STATIC void acmfd_function_disable (struct usbd_function_instance *function) ++{ ++ struct acm_private *acm = (struct acm_private *) (function->privdata); ++ ++ TRACE_MSG0(acm->trace_tag,"entered"); ++ acm->writesize = 0; ++ acm->function_services->disable(function); ++ TRACE_MSG1(acm->trace_tag,"DEC: %d", MOD_IN_USE); ++ TRACE_MSG0(acm->trace_tag,"exited"); ++} ++ ++ ++/*! function_ops ++ */ ++struct usbd_function_operations function_ops = { ++ event_handler: acmfd_event_handler, ++ device_request: acmfd_device_request, ++ function_enable: acmfd_function_enable, ++ function_disable: acmfd_function_disable, ++}; ++ ++/*! acm_fd_init ++ * @param acm ++ * @param usbd_module_info ++ * @param vendor_id ++ * @param product_id ++ * @param wmax_urbs ++ * @param wmax_bytes ++ * @return non-zero if error ++ */ ++STATIC int acm_fd_init(struct acm_private *acm, char *usbd_module_info, u32 vendor_id, u32 product_id, ++ u32 wmax_urbs, u32 wmax_bytes) ++{ ++ ++ TRACE_MSG0(acm->trace_tag,"entered"); ++ ++ if (vendor_id) ++ acm->function_driver->idVendor = cpu_to_le16(vendor_id); ++ if (product_id) ++ acm->function_driver->idProduct = cpu_to_le16(product_id); ++ max_queued_urbs = wmax_urbs; ++ max_queued_bytes = wmax_bytes; ++ ++ THROW_IF (NULL == (acm->function = usbd_register_function (acm->function_driver, "acm-fd", acm)), error); ++ ++ CATCH(error) { ++ printk(KERN_ERR"%s: ERROR\n", __FUNCTION__); ++ ++ if (acm->function) { ++ usbd_deregister_function (acm->function); ++ acm->function = NULL; ++ } ++ TRACE_MSG0(acm->trace_tag,"--> -EINVAL"); ++ return -EINVAL; ++ } ++ TRACE_MSG0(acm->trace_tag,"--> 0"); ++ return 0; ++} ++ ++ ++ ++/*! acm_wait_task ++ * @param acm ++ * @param queue ++ */ ++void acm_wait_task(struct acm_private *acm, WORK_ITEM *queue) ++{ ++ TRACE_MSG1(acm->trace_tag,"entered data=%p",queue->data); ++ RETURN_IF(!queue->data); ++ queue->data = NULL; ++#if defined(LINUX24) ++ while (queue->sync) { ++ TRACE_MSG1(acm->trace_tag,"waiting for queue: %p",queue); ++ schedule_timeout(HZ); ++ } ++#else ++ while(PENDING_WORK_ITEM((*queue))){ ++ TRACE_MSG1(acm->trace_tag,"waiting for queue: %p",queue); ++ SCHEDULE_TIMEOUT(1); // 1 second delay ++ } ++#endif ++ TRACE_MSG0(acm->trace_tag,"exited"); ++} ++ ++ ++/*! acm_get_dtr ++ * Get DTR status. ++ */ ++int acm_get_dtr(struct acm_private *acm) ++{ ++ return (acm->bmLineState & CDC_LINESTATE_D0_DTR) ? 1 : 0; ++} ++ ++/*! acm_get_dsr ++ * Get DSR status ++ */ ++int acm_get_dsr(struct acm_private *acm) ++{ ++ return (acm->bmUARTState & CDC_UARTSTATE_BTXCARRIER_DSR) ? 1 : 0; ++} ++ ++/*! acm_get_dcd ++ * Get DCD status ++ */ ++int acm_get_dcd(struct acm_private *acm) ++{ ++ return (acm->bmUARTState & CDC_UARTSTATE_BRXCARRIER_DCD) ? 1 : 0; ++} ++ ++/*! acm_set_dsr ++ * Set DSR status ++ * @param acm ++ * @param value ++ */ ++void acm_set_dsr(struct acm_private *acm, int value) ++{ ++ acm->bmUARTState &= ~CDC_UARTSTATE_BTXCARRIER_DSR; ++ acm->bmUARTState |= value ? CDC_UARTSTATE_BTXCARRIER_DSR : 0; ++ acm_send_int_notification(acm, CDC_NOTIFICATION_SERIAL_STATE, acm->bmUARTState); ++} ++ ++/*! acm_set_dcd ++ * Set DCD status ++ * @param acm ++ * @param value ++ */ ++void acm_set_dcd(struct acm_private *acm, int value) ++{ ++ acm->bmUARTState &= ~CDC_UARTSTATE_BRXCARRIER_DCD; ++ acm->bmUARTState |= value ? CDC_UARTSTATE_BRXCARRIER_DCD : 0; ++ acm_send_int_notification(acm, CDC_NOTIFICATION_SERIAL_STATE, acm->bmUARTState); ++} ++ ++/*! acm_ring ++ * Indicate Ring signal to host. ++ */ ++void acm_ring(struct acm_private *acm) ++{ ++ acm->bmUARTState |= CDC_UARTSTATE_BRINGSIGNAL; ++ acm_send_int_notification(acm, CDC_NOTIFICATION_SERIAL_STATE, acm->bmUARTState); ++ acm->bmUARTState &= ~CDC_UARTSTATE_BRINGSIGNAL; ++} ++ ++/*! acm_send_break ++ * Indicate Break signal to host. ++ */ ++void acm_send_break(struct acm_private *acm) ++{ ++ acm->bmUARTState |= CDC_UARTSTATE_BBREAK; ++ acm_send_int_notification(acm, CDC_NOTIFICATION_SERIAL_STATE, acm->bmUARTState); ++ acm->bmUARTState &= ~CDC_UARTSTATE_BBREAK; ++} ++ ++/*! acm_overrun ++ * Indicate Overrun signal to host. ++ */ ++void acm_overrun(struct acm_private *acm) ++{ ++ acm->bmUARTState |= CDC_UARTSTATE_BOVERRUN; ++ acm_send_int_notification(acm, CDC_NOTIFICATION_SERIAL_STATE, acm->bmUARTState); ++ acm->bmUARTState &= ~CDC_UARTSTATE_BOVERRUN; ++} ++ ++/*! acm_set_local ++ * Set LOCAL status ++ * @param acm ++ * @param value ++ */ ++void acm_set_local(struct acm_private *acm, int value) ++{ ++ acm->flags &= ~ACM_LOCAL; ++ acm->flags |= value ? ACM_LOCAL : 0; ++} ++ ++/*! acm_set_loopback ++ * Set LOOPBACK status ++ * @param acm ++ * @param value ++ */ ++void acm_set_loopback(struct acm_private *acm, int value) ++{ ++ acm->flags &= ~ACM_LOOPBACK; ++ acm->flags |= value ? ACM_LOOPBACK : 0; ++} ++ ++ ++ ++/*! acm_fd_term ++ * @param acm ++ */ ++STATIC void acm_fd_exit(struct acm_private *acm) ++{ ++ //acm_wait_task(acm->recv_tqueue); ++ RETURN_UNLESS (acm->function); ++ usbd_deregister_function (acm->function); ++ acm->function = NULL; ++} ++ ++/*! ++ * Function table exported to the OS specific upper layer. ++ */ ++struct acm_usb_services acm_fd_usb_ops = { ++ .fd_init = acm_fd_init, ++ .fd_exit = acm_fd_exit, ++ .xmit_chars = acm_xmit_chars, ++ .send_int_notification = acm_send_int_notification, ++ .throttle = acm_throttle, ++ .unthrottle = acm_unthrottle, ++ .write_room = acm_write_room, ++ .chars_in_buffer = acm_chars_in_buffer, ++ .wait_task = acm_wait_task, ++ .schedule_recv = acm_schedule_recv, ++ .open = acm_open, ++ .close = acm_close, ++ .flush = acm_flush, ++ .ready = acm_ready, ++ .get_dtr = acm_get_dtr, ++ .get_dsr = acm_get_dsr, ++ .get_dcd = acm_get_dcd, ++ .set_dsr = acm_set_dsr, ++ .set_dcd = acm_set_dcd, ++ .ring = acm_ring, ++ .send_break = acm_send_break, ++ .overrun = acm_overrun, ++ .set_local = acm_set_local, ++ .set_loopback = acm_set_loopback, ++}; ++ +diff -uNr linux/drivers/no-otg/functions/acm/acm-fd.h linux/drivers/otg/functions/acm/acm-fd.h +--- linux/drivers/no-otg/functions/acm/acm-fd.h 1970-01-01 01:00:00.000000000 +0100 ++++ linux/drivers/otg/functions/acm/acm-fd.h 2006-09-01 21:41:26.000000000 +0200 +@@ -0,0 +1,242 @@ ++/* ++ * otg/functions/acm/acm-fd.h ++ * ++ * Copyright (c) 2003, 2004 Belcarra ++ * ++ * By: ++ * Tom Rushworth <tbr@belcarra.com>, ++ * Stuart Lynne <sl@belcarra.com>, ++ * Bruce Balden <balden@belcarra.com> ++ * ++ */ ++/*! ++ * @file otg/functions/acm/acm-fd.h ++ * @brief ACM Function Driver private defines ++ * ++ * The top and bottom halves of the driver comunication via these structures. ++ * ++ * @ingroup ACMFunction ++ */ ++ ++#ifndef ACM_FD_H ++#define ACM_FD_H 1 ++ ++typedef void (*cpy_fn)(u8 *dst, u8 *src, int len); ++ ++/*! acm_usb_services ++ * Services that the acm_fd library provides. ++ */ ++struct acm_usb_services { ++ ++ /*! fd_init - called to initialize the acm_fd library ++ * This call initialized the acm_fd library and registers the function driver ++ * with the USB Peripheral Stack. ++ */ ++ int (*fd_init)(struct acm_private *acm, char *inf, u32 vendor_id, u32 product_id, u32 wmax_urbs, u32 wmax_bytes); ++ ++ /*! fd_exit - called before exiting ++ * This call will cause the function driver to be de-registered. ++ */ ++ void (*fd_exit)(struct acm_private *acm); ++ ++ /*! open - device open ++ * This is called the device is opened (first open only if non-exclusive opens allowed). ++ */ ++ int (*open)(struct acm_private *acm, int interface); ++ ++ /*! close - Device close ++ * This is called when the device closed (last close only if non-exclusive opens allowed.) ++ */ ++ int (*close)(struct acm_private *acm, int interface); ++ ++ ++ /*! flush - flush data urbs. ++ * Cancel outstanding data urbs. ++ */ ++ void (*flush)(struct acm_private *acm, int interface); ++ ++ /*! schedule_recv - queue as many data receive urbs as possible ++ * This will schedule a bottom half hander that will will start as ++ * many receive data urbs as are allowed given the amount of room ++ * available in the upper layer. If no urbs are queued by the ++ * bottom half handler it will re-schedule itself. ++ */ ++ void (*schedule_recv)(struct acm_private *acm, int interface); ++ ++ /*! throttle - set throttle flag for specified interface ++ * Receive urbs will not be queued when throttled. ++ */ ++ void (*throttle)(struct acm_private *acm, int interface); ++ ++ /*! unthrottle - reset throttle flag for specified interface ++ * Receive urbs are allowed to be queued. If no urbs are queued a ++ * bottom half handler will be scheduled to queue them. ++ */ ++ void (*unthrottle)(struct acm_private *acm, int interface); ++ ++ ++ /*! xmit_chars - send data via specified interface ++ * This will start a transmit urb to send the specified data. The ++ * number of characters sent will be returned. ++ */ ++ int (*xmit_chars)(struct acm_private *acm, int interface, int count, int from_user, const unsigned char *buf); ++ ++ /*! write_room ++ * Return amount of data that could be queued for sending. ++ */ ++ int (*write_room)(struct acm_private *acm, int interface); ++ ++ /*! chars_in_buffer ++ * Return number of chars in xmit buffer. ++ */ ++ int (*chars_in_buffer)(struct acm_private *acm, int interface); ++ ++ ++ /*! send_int_notification - send notification via interrupt endpoint ++ * This can be used to queue network, serial state change notifications. ++ */ ++ int (*send_int_notification)(struct acm_private *acm, int bnotification, int data); ++ ++ /*! wait_task - wait for task to complete. ++ */ ++ void (*wait_task)(struct acm_private *acm, WORK_ITEM *queue); ++ ++ /*! ready - return true if connected and carrier ++ */ ++ int (*ready)(struct acm_private *acm); ++ ++ /*! get_dtr ++ * Get DTR status. ++ */ ++ int (*get_dtr)(struct acm_private *acm); ++ ++ /*! get_dsr ++ * Get DSR status ++ */ ++ int (*get_dsr)(struct acm_private *acm); ++ ++ /*! get_dcd ++ * Get DCD status ++ */ ++ int (*get_dcd)(struct acm_private *acm); ++ ++ /*! set_dsr ++ * Set DSR status ++ */ ++ void (*set_dsr)(struct acm_private *acm, int value); ++ ++ /*! set_dcd ++ * Set DCD status ++ */ ++ void (*set_dcd)(struct acm_private *acm, int value); ++ ++ /*! ring ++ * Indicate Ring signal to host. ++ */ ++ void (*ring)(struct acm_private *acm); ++ ++ /*! send_break ++ * Indicate Break signal to host. ++ */ ++ void (*send_break)(struct acm_private *acm); ++ ++ /*! overrun ++ * Indicate Overrun signal to host. ++ */ ++ void (*overrun)(struct acm_private *acm); ++ ++ ++ /*! set_local ++ * Set LOCAL status ++ */ ++ void (*set_local)(struct acm_private *acm, int value); ++ ++ /*! set_loopback - set loopback mode ++ * Sets LOOP flag, data received from the host will be immediately ++ * returned without passing to the upper layer. ++ */ ++ void (*set_loopback)(struct acm_private *acm, int value); ++}; ++ ++/*! acm_function_services ++ * Services that the top level driver provides to the lower library. ++ */ ++struct acm_function_services { ++ /*! enable ++ * Enable the function driver. ++ */ ++ void (*enable)(struct usbd_function_instance *function); ++ ++ /*! disable ++ * Disable the function driver. ++ */ ++ void (*disable)(struct usbd_function_instance *function); ++ ++ /*! wakeup_opens ++ * Wakeup processes waiting for DTR. ++ */ ++ void (*wakeup_opens)(struct acm_private *acm); ++ ++ /*! wakeup_opens ++ * Wakeup processes waiting for state change. ++ */ ++ void (*wakeup_state)(struct acm_private *acm); ++ ++ /*! wakeup writers ++ * Wakeup pending writes. ++ */ ++ void (*schedule_wakeup_writers)(struct acm_private *acm); ++ ++ /*! recv_space_available ++ * Check for amount of receive space that is available, controls ++ * amount of receive urbs that will be queued. ++ */ ++ int (*recv_space_available)(struct acm_private *acm, int interface); ++ ++ /*! recv_chars ++ * Process chars received on specified interface. ++ */ ++ int (*recv_chars)(struct acm_private *acm, int interface, u8 *cp, int n); ++ ++ ++ /*! schedule_hangup ++ * Schedule a work item that will perform a hangup. ++ */ ++ void (*schedule_hangup)(struct acm_private *acm); ++ ++ /*! comm_feature ++ * Tell function that comm feature has changed. ++ */ ++ void (*comm_feature)(struct acm_private *acm); ++ ++ /*! line_coding ++ * Tell function that line coding has changed. ++ */ ++ void (*line_coding)(struct acm_private *acm); ++ ++ /*! control_state ++ * Tell function that control state has changed. ++ */ ++ void (*control_state)(struct acm_private *acm); ++ ++ /*! send_break ++ * Tell function to send a break signal. ++ */ ++ void (*send_break)(struct acm_private *acm); ++ ++}; ++ ++extern struct acm_usb_services acm_fd_usb_ops; ++extern struct usbd_function_operations function_ops; ++ ++ ++/* ++ * otg-trace tag. ++ */ ++extern otg_tag_t acm_fd_trace_tag; ++ ++#define MAX_QUEUED_BYTES 256 ++#define MAX_QUEUED_URBS 10 // Max for write ++ ++ ++#endif +diff -uNr linux/drivers/no-otg/functions/acm/acm-os.h linux/drivers/otg/functions/acm/acm-os.h +--- linux/drivers/no-otg/functions/acm/acm-os.h 1970-01-01 01:00:00.000000000 +0100 ++++ linux/drivers/otg/functions/acm/acm-os.h 2006-09-01 21:41:26.000000000 +0200 +@@ -0,0 +1,74 @@ ++/* ++ * otg/functions/acm/acm-os.h ++ * ++ * Copyright (c) 2003, 2004 Belcarra ++ * ++ * By: ++ * Tom Rushworth <tbr@belcarra.com>, ++ * Stuart Lynne <sl@belcarra.com>, ++ * Bruce Balden <balden@belcarra.com> ++ * ++ * ++ */ ++/*! ++ * @file otg/functions/acm/acm-os.h ++ * @brief ACM Function Driver private defines ++ * ++ * An ACM (Abstract Control Model) driver is composed of two pieces: ++ * 1) An OS specific piece that handles creating and operating ++ * a serial device for the given OS. ++ * 2) A USB specific piece that interfaces either with the host ++ * usbcore layer, or with the otgcore layer. ++ * ++ * If the USB piece interfaces with the host usbcore layer you get ++ * an ACM class driver. If the USB piece interfaces with the otgcore ++ * layer you get an ACM function driver. ++ * ++ * This file describes the functions exported by the various acm-*-os.c ++ * files (implementing (1)) for use in acm-fd.c (2). ++ * ++ * @ingroup ACMFunction ++ */ ++ ++#ifndef ACM_OS_H ++#define ACM_OS_H 1 ++ ++/* ++ * acm_os_recv_space_available - return the number of bytes of data ++ * the OS specific piece can accept without ++ * dropping some. ++ */ ++extern int acm_os_recv_space_available(struct acm_private *acm); ++ ++/* ++ * acm_os_recv_chars - receive n bytes starting at cp. This will never be ++ * more than the last call to acm_os_recv_space_available(). ++ * This will be called from interrupt context. ++ */ ++extern int acm_os_recv_chars(struct acm_private *acm, u8 *cp, int n); ++ ++/* ++ * acm_os_wakeup_writers - wakeup any blocked writers. This is called ++ * from interrupt context, so may need to queue ++ * the actual wakeup in a "bottom half". ++ */ ++extern void acm_os_wakeup_writers(struct acm_private *acm); ++ ++/* ++ * acm_os_hangup - send a hangup to any readers or writers. This can be ++ * called from interrupt context, so may need to queue ++ * the actual hangup in a "bottom half". ++ */ ++extern void acm_os_hangup(struct acm_private *acm); ++ ++/* ++ * acm_os_wakeup_opens - wakeup any blocked opens. This is called ++ * from interrupt context, so may need to queue ++ * the actual wakeup in a "bottom half". ++ */ ++extern void acm_os_wakeup_opens(struct acm_private *acm); ++ ++extern void acm_os_enable(struct usbd_function_instance *function); ++extern void acm_os_disable(struct usbd_function_instance *function); ++ ++#endif +diff -uNr linux/drivers/no-otg/functions/acm/acm.h linux/drivers/otg/functions/acm/acm.h +--- linux/drivers/no-otg/functions/acm/acm.h 1970-01-01 01:00:00.000000000 +0100 ++++ linux/drivers/otg/functions/acm/acm.h 2006-09-01 21:41:26.000000000 +0200 +@@ -0,0 +1,113 @@ ++/* ++ * otg/functions/acm/acm.h ++ * ++ * Copyright (c) 2003, 2004 Belcarra ++ * ++ * By: ++ * Stuart Lynne <sl@belcarra.com>, ++ * Tom Rushworth <tbr@belcarra.com>, ++ * Bruce Balden <balden@belcarra.com> ++ * ++ */ ++ ++/*! ++ * @defgroup ACMFunction ACM ++ * @ingroup functiongroup ++ */ ++ ++/*! ++ * @file otg/functions/acm/acm.h ++ * @brief ACM Function Driver private defines ++ * ++ * This is an ACM Function Driver. The upper edge is exposed ++ * to the hosting OS as a Posix type character device. The lower ++ * edge implements the USB Device Stack API. ++ * ++ * This driver implements the CDC ACM driver model and uses the CDC ACM ++ * protocols. ++ * ++ * Note that it appears to be impossible to determine the end of a receive ++ * bulk transfer larger than wMaxPacketsize. The host is free to send ++ * wMaxPacketsize chars in a single transfer. This means that we cannot ++ * queue receive urbs larger than wMaxPacketsize (typically 64 bytes.) ++ * ++ * This does not however prevent queuing transmit urbs with larger amounts ++ * of data. It is interpreted at the receiving (host) end as a series of ++ * wMaxPacketsize transfers but because there is no interpretation to the ++ * amounts of data sent it does affect anything if we treat the data as a ++ * single larger transfer. ++ * ++ * @ingroup ACMFunction ++ */ ++ ++ ++// Endpoint indexes in acm_endpoint_requests[] and the endpoint map. ++#define BULK_OUT 0x00 ++#define BULK_IN 0x01 ++#define INT_IN 0x02 ++#define ENDPOINTS 0x03 ++ ++#define COMM_INTF 0x00 ++#define DATA_INTF 0x01 ++ ++ ++ ++// Most hosts don't care about BMAXPOWER, but the UUT tests want it to be 1 ++#define BMAXPOWER 1 ++#define BMATTRIBUTE 0 ++ ++#define STATIC ++//#define STATIC static ++// ++ ++/*! @name ACM Flags ++ * @{ ++ */ ++#define ACM_OPENED (1 << 0) /*!< upper layer has opened the device port */ ++#define ACM_CONFIGURED (1 << 1) /*!< device is configured */ ++#define ACM_THROTTLED (1 << 2) /*!< upper layer doesn't want to receive data */ ++#define ACM_CARRIER (1 << 3) /*!< host has set DTR, i.e. host has opened com device */ ++#define ACM_LOOPBACK (1 << 4) /*!< upper layer wants local loopback */ ++#define ACM_LOCAL (1 << 5) /*!< upper layer specified LOCAL mode */ ++ ++/*! @} */ ++ ++/*! struct acm_private ++ */ ++struct acm_private { ++ struct usbd_function_instance *function; ++ struct usbd_function_driver *function_driver; ++ struct acm_function_services *function_services; ++ otg_tag_t trace_tag; ++ ++ u32 flags; ++ ++ int recv_urbs; ++ atomic_t used; ++ atomic_t queued_bytes; ++ atomic_t queued_urbs; ++ unsigned int writesize; // packetsize * 4 ++ ++ /*TBR debug receive flow control */ ++ unsigned long bytes_received; ++ unsigned long bytes_forwarded; ++ /*TBR end debug */ ++ ++ struct cdc_acm_line_coding line_coding; // C.f. Table 50 - [GS]ET_LINE_CODING Device Request (to/from host) ++ cdc_acm_bmUARTState bmUARTState; // C.f. Table 51 - SET_CONTROL_LINE_STATE Device Request (from host) ++ cdc_acm_bmLineState bmLineState; // C.f. Table 69 - SERIAL_STATE Notification (to host) ++ ++ ++ ++ struct tq_struct recv_tqueue; ++ ++ void *privdata; ++ ++}; ++ ++ ++struct acm_work { ++ struct acm_private *acm; ++ int interface; ++}; ++ +diff -uNr linux/drivers/no-otg/functions/acm/modem-l24-os.c linux/drivers/otg/functions/acm/modem-l24-os.c +--- linux/drivers/no-otg/functions/acm/modem-l24-os.c 1970-01-01 01:00:00.000000000 +0100 ++++ linux/drivers/otg/functions/acm/modem-l24-os.c 2006-09-01 21:41:26.000000000 +0200 +@@ -0,0 +1,194 @@ ++/* ++ * otg/functions/acm/modem-l24-os.c ++ * ++ * Copyright (c) 2003, 2004 Belcarra ++ * ++ * By: ++ * Tom Rushworth <tbr@belcarra.com>, ++ * Stuart Lynne <sl@belcarra.com>, ++ * Bruce Balden <balden@belcarra.com> ++ * ++ */ ++/*! ++ * @file otg/functions/acm/modem-l24-os.c ++ * @brief ACM Function Driver private defines ++ * ++ * An ACM (Abstract Control Model) driver is composed of two pieces: ++ * 1) An OS specific piece that handles creating and operating ++ * a serial device for the given OS. ++ * 2) A USB specific piece that interfaces either with the host ++ * usbcore layer, or with the otgcore layer. ++ * ++ * If the USB piece interfaces with the host usbcore layer you get ++ * an ACM class driver. If the USB piece interfaces with the otgcore ++ * layer you get an ACM function driver. ++ * ++ * This file is the OS specific piece that interfaces with Linux ++ * (kernel versions 2.4.*). It creates two simple characters devices. ++ * One for the data transport and one for the communications interface. ++ * ++ * @ingroup ACMFunction ++ */ ++ ++ ++//#include <otg/osversion.h> ++#include <otg/otg-compat.h> ++#include <otg/otg-module.h> ++ ++MOD_AUTHOR ("tbr@belcarra.com, sl@belcarra.com"); ++ ++MOD_DESCRIPTION ("Belcarra CDC-ACM Function"); ++EMBED_LICENSE(); ++ ++ ++#include <linux/init.h> ++#include <asm/uaccess.h> ++#include <linux/ctype.h> ++#include <linux/timer.h> ++#include <linux/interrupt.h> ++#include <asm/atomic.h> ++#include <linux/tty.h> ++#include <linux/tty_driver.h> ++#include <linux/tty_flip.h> ++#include <linux/smp_lock.h> ++#include <linux/slab.h> ++ ++#include <otg/usbp-chap9.h> ++#include <otg/usbp-func.h> ++ ++#include <linux/capability.h> ++#include <otg/otg-trace.h> ++#include "acm.h" ++#include "acm-fd.h" ++#include "acm-os.h" ++ ++// May need an ifdef here to pick up the acm_cl_usb_ops in the future. ++static struct acm_usb_services *usb_ops = &acm_fd_usb_ops; ++ ++#define MDM mdm_fd_trace_tag ++otg_tag_t mdm_fd_trace_tag; ++ ++/* Module Parameters ************************************************************************* */ ++ ++static u32 vendor_id; ++static u32 product_id; ++static u32 max_queued_urbs = MAX_QUEUED_URBS; ++static u32 max_queued_bytes = MAX_QUEUED_BYTES; ++ ++MOD_PARM (vendor_id, "i"); ++MOD_PARM (product_id, "i"); ++MOD_PARM (max_queued_urbs, "i"); ++MOD_PARM (max_queued_bytes, "i"); ++ ++MOD_PARM_DESC (vendor_id, "Device Vendor ID"); ++MOD_PARM_DESC (product_id, "Device Product ID"); ++MOD_PARM_DESC (max_queued_urbs, "Maximum TX Queued Urbs"); ++MOD_PARM_DESC (max_queued_bytes, "Maximum TX Queued Bytes"); ++ ++ ++/* ACM ***************************************************************************************** */ ++ ++#define ACM_TTY_MAJOR 166 ++#define ACM_TTY_MINOR 0 ++#define ACM_TTY_MINORS 1 ++ ++static struct acm_private acm_private; ++ ++/* USB Simple Char Device ********************************************************************** */ ++ ++ ++/* USB Comm Char Device ************************************************************************ */ ++ ++ ++/* USB Module init/exit ************************************************************************ */ ++/* ++ * acm_modinit - module init ++ * ++ */ ++STATIC int acm_modinit (void) ++{ ++ int i; ++ printk (KERN_INFO "Copyright (c) 2003-2004 sl@belcarra.com, tbr@belcarra.com\n"); ++ MDM = otg_trace_obtain_tag(); ++ ++ // initialize private structure ++ //acm_private.tty_driver = &acm_tty_driver; ++ //acm_private.wqueue.routine = acm_wakeup_writers; ++ //acm_private.wqueue.data = &acm_private; ++ //acm_private.hqueue.routine = acm_hangup; ++ //acm_private.hqueue.data = &acm_private; ++ //acm_private.recv_tqueue.routine = &acm_start_recv; ++ //acm_private.recv_tqueue.data = &acm_private; ++ ++ //init_waitqueue_head(&acm_private.open_wait); ++ ++ ++ // register as usb function driver ++ ++ THROW_IF (usb_ops->initialize_usb_part(&acm_private,"acm_fd",vendor_id,product_id, ++ max_queued_urbs,max_queued_bytes ), error); ++ ++ CATCH(error) { ++ printk(KERN_ERR"%s: ERROR\n", __FUNCTION__); ++ ++ TRACE_MSG0(MDM,"--> -EINVAL"); ++ return -EINVAL; ++ } ++ TRACE_MSG0(MDM,"--> 0"); ++ return 0; ++} ++ ++void acm_wait_task(WORK_ITEM *queue) ++{ ++ TRACE_MSG1(MDM,"entered data=%p",queue->data); ++ RETURN_IF(!queue->data); ++ queue->data = NULL; ++#if defined(LINUX24) ++ while (queue->sync) { ++ TRACE_MSG1(MDM,"waiting for queue: %p",queue); ++ schedule_timeout(HZ); ++ } ++#else ++ while(PENDING_WORK_ITEM((*queue))){ ++ TRACE_MSG1(MDM,"waiting for queue: %p",queue); ++ SCHEDULE_TIMEOUT(1); // 1 second delay ++ } ++#endif ++ TRACE_MSG0(MDM,"exited"); ++} ++ ++/* acm_modexit - module cleanup ++ */ ++STATIC void acm_modexit (void) ++{ ++ unsigned long flags; ++ struct usbd_urb *urb; ++ TRACE_MSG0(MDM,"entered"); ++ ++ // Wake up any pending opens after setting the exiting flag. ++ local_irq_save(flags); ++ acm_private.exiting = 1; ++ //if (acm_private.open_wait_count > 0) { ++ // wake_up_interruptible(&acm_private.open_wait); ++ //} ++ local_irq_restore(flags); ++ ++ // verify no tasks are running ++ //acm_wait_task(&acm_private.wqueue); ++ //acm_wait_task(&acm_private.hqueue); ++ ++//#if defined(LINUX24) ++// run_task_queue(&tq_timer); ++//#else ++// blk_run_queues(); ++//#endif ++ ++ //acm_wait_task(&acm_private.recv_tqueue); ++ ++ usb_ops->terminate_usb_part(&acm_private); ++ otg_trace_invalidate_tag(MDM); ++} ++ ++ ++module_init (acm_modinit); ++module_exit (acm_modexit); +diff -uNr linux/drivers/no-otg/functions/acm/modem.c linux/drivers/otg/functions/acm/modem.c +--- linux/drivers/no-otg/functions/acm/modem.c 1970-01-01 01:00:00.000000000 +0100 ++++ linux/drivers/otg/functions/acm/modem.c 2006-09-01 21:41:26.000000000 +0200 +@@ -0,0 +1,263 @@ ++/* ++ * otg/functions/acm/modem.c ++ * ++ * Copyright (c) 2003, 2004 Belcarra ++ * ++ * By: ++ * Tom Rushworth <tbr@belcarra.com>, ++ * Stuart Lynne <sl@belcarra.com>, ++ * Bruce Balden <balden@belcarra.com> ++ */ ++/*! ++ * @file otg/functions/acm/modem.c ++ * @brief ACM-MODEM Descriptor Set ++ * ++ * This file is the USB specific piece that interfaces with the otgcore layer. ++ * If you're looking for the USB specific piece that interfaces with the ++ * host usbcore layer, that's in otg/functions/acm/acm-cl.c ++ * ++ * Note: this function driver requires the following endpoints: ++ * ++ * BULK-IN ++ * BULK-OUT ++ * INTERRUPT-IN ++ * ++ * This function driver cannot be used on devices (such as the StrongArm ++ * SA1100) that do not have an interrupt endpoint. ++ * ++ * @ingroup MODEMFunction ++ */ ++ ++ ++#include <otg/otg-compat.h> ++#include <otg/otg-module.h> ++ ++#include <linux/init.h> ++#include <asm/uaccess.h> ++#include <linux/ctype.h> ++#include <linux/timer.h> ++#include <linux/interrupt.h> ++#include <asm/atomic.h> ++#include <linux/smp_lock.h> ++#include <linux/slab.h> ++ ++#include <otg/usbp-chap9.h> ++#include <otg/usbp-func.h> ++ ++#include <otg/otg-trace.h> ++#include "acm.h" ++#include "acm-fd.h" ++#include "acm-os.h" ++ ++ ++ ++/* ++ * CDC ACM Configuration ++ * ++ * Endpoint, Class, Interface, Configuration and Device descriptors/descriptions ++ */ ++ ++/*! BULK OUT Data Endpoint Descriptor ++ */ ++static u8 mdn_alt_1[] = { 0x07, USB_DT_ENDPOINT, USB_DIR_OUT, BULK, 0, 0x00, 0x00, }; ++ ++/*! BULK In Data Endpoint Descriptor ++ */ ++static u8 mdn_alt_2[] = { 0x07, USB_DT_ENDPOINT, USB_DIR_IN, BULK, 0, 0x00, 0x00, }; ++ ++/*! Data Endpoint Descriptor List ++ */ ++static struct usbd_endpoint_descriptor *mdn_alt_endpoints[] = { ++ (struct usbd_endpoint_descriptor *) mdn_alt_1, ++ (struct usbd_endpoint_descriptor *) mdn_alt_2, }; ++ ++/*! Data Endpoint Index List ++ */ ++u8 mdn_alt_indexes[] = { BULK_OUT, BULK_IN, }; ++ ++/*! INTERRUPT In Comm Endpoint Descriptor ++ */ ++static u8 mdn_comm_1[] = { 0x07, USB_DT_ENDPOINT, USB_DIR_IN, INTERRUPT, 0, 0x00, 0x0a, }; ++ ++/*! Comm Endpoint Descriptor List ++ */ ++static struct usbd_endpoint_descriptor *mdn_comm_endpoints[] = { (struct usbd_endpoint_descriptor *) mdn_comm_1 }; ++ ++ ++/*! Comm Endpoint Index List ++ */ ++u8 mdn_comm_indexes[] = { INT_IN, }; ++ ++/*! @{ ++ * Class Descriptors ++ */ ++static u8 cdc_class_1[] = { 0x05, CS_INTERFACE, USB_ST_HEADER, 0x01, 0x01, /* CLASS_BDC_VERSION, CLASS_BDC_VERSION */ }; ++static u8 cdc_class_2[] = { 0x05, CS_INTERFACE, USB_ST_CMF, 0x03, 0x01, /* bMasterInterface: 0, bSlaveInterface: 1 */ }; ++static u8 cdc_class_3[] = { 0x05, CS_INTERFACE, USB_ST_UF, 0x00, 0x01, /* bMasterInterface: 0, bSlaveInterface: 1 */ }; ++/*! @} */ ++ ++/*! ACMF Class Descriptor ++ * ACMF - c.f. Table 28 ++ * currenty set to 0x2 - Support Set_Line_Coding etc, ++ * ++ * XXX Should we also set 0x4 - Supports Network_Notification? ++ */ ++static u8 cdc_class_4[] = { 0x04, CS_INTERFACE, USB_ST_ACMF, 0x02, }; ++ ++static struct usbd_generic_class_descriptor *cdc_comm_class_descriptors[] = ++ { (struct usbd_generic_class_descriptor *) cdc_class_1, ++ (struct usbd_generic_class_descriptor *) cdc_class_2, ++ (struct usbd_generic_class_descriptor *) cdc_class_3, ++ (struct usbd_generic_class_descriptor *) cdc_class_4, }; ++ ++ ++ ++/* Alternate Descriptors */ ++// First two bytes are identical in all: bLength, bDescriptorType (0x09 0x04) ++ ++/*! Comm Alternate Descriptor ++ */ ++static u8 cdc_comm_alternate_descriptor[sizeof(struct usbd_interface_descriptor)] = { ++ 0x09, USB_DT_INTERFACE, COMM_INTF, 0x00, 0x00, // bInterfaceNumber, bAlternateSetting, bNumEndpoints ++ COMMUNICATIONS_INTERFACE_CLASS, COMMUNICATIONS_ACM_SUBCLASS, 0x01, 0x00, }; ++ ++/*! Data Alternate Descriptor ++ */ ++static u8 cdc_data_alternate_descriptor[sizeof(struct usbd_interface_descriptor)] = { ++ 0x09, USB_DT_INTERFACE, DATA_INTF, 0x00, 0x00, // bInterfaceNumber, bAlternateSetting, bNumEndpoints ++ DATA_INTERFACE_CLASS, COMMUNICATIONS_NO_SUBCLASS, COMMUNICATIONS_NO_PROTOCOL, 0x00, }; ++ ++ ++ ++/*! Comm alternate descriptions ++ */ ++static struct usbd_alternate_description cdc_comm_alternate_descriptions[] = { ++ { iInterface: CONFIG_OTG_ACM_COMM_INTF, ++ interface_descriptor: (struct usbd_interface_descriptor *)&cdc_comm_alternate_descriptor, ++ classes:sizeof (cdc_comm_class_descriptors) / sizeof (struct usbd_generic_class_descriptor *), ++ class_list: cdc_comm_class_descriptors, ++ endpoint_list: mdn_comm_endpoints, ++ endpoints:sizeof (mdn_comm_endpoints) / sizeof(struct usbd_endpoint_descriptor *), ++ endpoint_indexes: mdn_comm_indexes, ++ }, }; ++ ++/*! Data alternate descriptions ++ */ ++static struct usbd_alternate_description cdc_data_alternate_descriptions[] = { ++ { iInterface: CONFIG_OTG_ACM_DATA_INTF, ++ interface_descriptor: (struct usbd_interface_descriptor *)&cdc_data_alternate_descriptor, ++ endpoint_list: mdn_alt_endpoints, ++ endpoints:sizeof (mdn_alt_endpoints) / sizeof(struct usbd_endpoint_descriptor *), ++ endpoint_indexes: mdn_alt_indexes, ++ }, }; ++ ++ ++/*! Interface Descriptions List */ ++static struct usbd_interface_description cdc_interfaces[] = { ++ { ++ alternates:sizeof (cdc_comm_alternate_descriptions) / sizeof (struct usbd_alternate_description), ++ alternate_list:cdc_comm_alternate_descriptions, ++ }, ++ ++ { ++ alternates:sizeof (cdc_data_alternate_descriptions) / sizeof (struct usbd_alternate_description), ++ alternate_list:cdc_data_alternate_descriptions, ++ }, ++}; ++ ++ ++/*! Configuration Descriptor ++ */ ++static u8 cdc_configuration_descriptor[sizeof(struct usbd_configuration_descriptor)] = { ++ 0x09, USB_DT_CONFIGURATION, 0x00, 0x00, sizeof (cdc_interfaces) / sizeof (struct usbd_interface_description), ++ 0x01, 0x00, BMATTRIBUTE, BMAXPOWER, }; ++ ++/*! Configuration Description ++ */ ++struct usbd_configuration_description mdn_description[] = { ++ { iConfiguration: CONFIG_OTG_ACM_DESC, ++ configuration_descriptor: (struct usbd_configuration_descriptor *)cdc_configuration_descriptor, ++ }, }; ++ ++/*! Device Descriptor ++ */ ++static struct usbd_device_descriptor mdn_device_descriptor = { ++ bLength: sizeof(struct usbd_device_descriptor), ++ bDescriptorType: USB_DT_DEVICE, ++ bcdUSB: __constant_cpu_to_le16(USB_BCD_VERSION), ++ bDeviceClass: COMMUNICATIONS_DEVICE_CLASS, ++ bDeviceSubClass: 0x02, ++ bDeviceProtocol: 0x00, ++ bMaxPacketSize0: 0x00, ++ idVendor: __constant_cpu_to_le16(CONFIG_OTG_ACM_VENDORID), ++ idProduct: __constant_cpu_to_le16(CONFIG_OTG_ACM_PRODUCTID), ++ bcdDevice: __constant_cpu_to_le16(CONFIG_OTG_ACM_BCDDEVICE), ++}; ++ ++#ifdef CONFIG_OTG_HIGH_SPEED ++/*! High Speed Device Qualifier Descriptor ++ */ ++static struct usbd_device_qualifier_descriptor mdn_device_qualifier_descriptor = { ++ bLength: sizeof(struct usbd_device_qualifier_descriptor), ++ bDescriptorType: USB_DT_DEVICE_QUALIFIER, ++ bcdUSB: __constant_cpu_to_le16(USB_BCD_VERSION), ++ bDeviceClass: COMMUNICATIONS_DEVICE_CLASS, ++ bDeviceSubClass: 0x02, ++ bDeviceProtocol: 0x00, ++ bMaxPacketSize0: 0x00, ++}; ++#endif /* CONFIG_OTG_HIGH_SPEED */ ++ ++ ++/*! Endpoint Request List ++ */ ++static struct usbd_endpoint_request mdn_endpoint_requests[ENDPOINTS+1] = { ++ { 1, 1, 0, USB_DIR_OUT | USB_ENDPOINT_BULK, 64, 512, }, ++ { 1, 1, 0, USB_DIR_IN | USB_ENDPOINT_BULK, 64 /* * 4 */, 512, }, ++ { 1, 0, 0, USB_DIR_IN | USB_ENDPOINT_INTERRUPT, 16, 64, }, ++ { 0, }, ++}; ++ ++/*! OTG Descriptor ++ */ ++static struct usbd_otg_descriptor mdn_otg_descriptor = { ++ bLength : sizeof(struct usbd_otg_descriptor), ++ bDescriptorType: USB_DT_OTG, ++ bmAttributes: 0, ++}; ++ ++/*! Device Description ++ */ ++struct usbd_device_description mdn_device_description = { ++ device_descriptor: &mdn_device_descriptor, ++#ifdef CONFIG_OTG_HIGH_SPEED ++ device_qualifier_descriptor: &mdn_device_qualifier_descriptor, ++#endif /* CONFIG_OTG_HIGH_SPEED */ ++ otg_descriptor: &mdn_otg_descriptor, ++ iManufacturer: CONFIG_OTG_ACM_MANUFACTURER, ++ iProduct: CONFIG_OTG_ACM_PRODUCT_NAME, ++#if !defined(CONFIG_OTG_NO_SERIAL_NUMBER) && defined(CONFIG_OTG_SERIAL_NUMBER_STR) ++ iSerialNumber: CONFIG_OTG_SERIAL_NUMBER_STR, ++#endif ++}; ++ ++ ++ ++ ++/*! function_driver ++ */ ++struct usbd_function_driver modem_function_driver = { ++ name: "ACM-MDM", ++ fops:&function_ops, ++ device_description:&mdn_device_description, ++ bNumConfigurations:sizeof (mdn_description) / sizeof (struct usbd_configuration_description), ++ configuration_description:mdn_description, ++ idVendor: __constant_cpu_to_le16(CONFIG_OTG_ACM_VENDORID), ++ idProduct: __constant_cpu_to_le16(CONFIG_OTG_ACM_PRODUCTID), ++ bcdDevice: __constant_cpu_to_le16(CONFIG_OTG_ACM_BCDDEVICE), ++ bNumInterfaces:sizeof (cdc_interfaces) / sizeof (struct usbd_interface_description), ++ interface_list:cdc_interfaces, ++ endpointsRequested: ENDPOINTS, ++ requestedEndpoints: mdn_endpoint_requests, ++}; ++ +diff -uNr linux/drivers/no-otg/functions/acm/modem.h linux/drivers/otg/functions/acm/modem.h +--- linux/drivers/no-otg/functions/acm/modem.h 1970-01-01 01:00:00.000000000 +0100 ++++ linux/drivers/otg/functions/acm/modem.h 2006-09-01 21:41:26.000000000 +0200 +@@ -0,0 +1,118 @@ ++/* ++ * otg/functions/acm/modem.h ++ * ++ * Copyright (c) 2003, 2004 Belcarra ++ * ++ * By: ++ * Stuart Lynne <sl@belcarra.com>, ++ * Tom Rushworth <tbr@belcarra.com>, ++ * Bruce Balden <balden@belcarra.com> ++ * ++ */ ++/*! ++ * @defgroup MODEMFunction ACM-Modem ++ * @ingroup functiongroup ++ */ ++/*! ++ * @file otg/functions/acm/modem.h ++ * @brief ACM Function Driver private defines ++ * ++ * ++ * This is an ACM Function Driver. The upper edge is exposed ++ * to the hosting OS as a Posix type character device. The lower ++ * edge implements the USB Device Stack API. ++ * ++ * These are emulated and set by the modem driver as appropriate ++ * to model a virutal serial port. The ++ * ++ * TIOCM_RNG RNG (Ring) not used ++ * TIOCM_LE DSR (Data Set Ready / Line Enable) ++ * TIOCM_DSR DSR (Data Set Ready) ++ * TIOCM_CAR DCD (Data Carrier Detect) ++ * TIOCM_CTS CTS (Clear to Send) ++ * TIOCM_CD TIOCM_CAR ++ * TIOCM_RI TIOCM_RNG ++ * ++ * These are set by the application: ++ * ++ * TIOCM_DTR DTR (Data Terminal Ready) ++ * TIOCM_RTS RTS (Request to Send) ++ * ++ * TIOCM_LOOP Set into loopback mode ++ * TIOCM_OUT1 Not used. ++ * TIOCM_OUT2 Not used. ++ * ++ * ++ * The following File and termio c_cflags are used: ++ * ++ * O_NONBLOCK don't block in modem_open() ++ * O_EXCL don't allow more than one open ++ * ++ * CLOCAL ignore DTR status ++ * CBAUD send break if set to B0 ++ * ++ * Virtual NULL Modem ++ * ++ * Peripheral to Host via Serial State Notification (write ioctls) ++ * ++ * Application TIOCM Null Modem ACM (DCE) Notificaiton Host (DTE) ++ * ------------------------------------------------------------------------------------------------------------- ++ * Request to send TIOCM_RTS RTS -> CTS CTS Not Available Clear to Send (N/A) ++ * Data Terminal Ready TIOCM_DTR DTR -> DSR DSR bTxCarrier Data Set Ready ++ * DTR -> DCD DCD bRXCarrier Carrier Detect ++ * Ring Indicator TIOCM_OUT1 OUT1 -> RI RI bRingSignal Ring Indicator ++ * Overrun TIOCM_OUT2 OUT2 -> Overrun Overrun bOverrun Overrun ++ * Send Break B0 B0 -> Break Break bBreak Break Received ++ * ------------------------------------------------------------------------------------------------------------- ++ * ++ * ++ * Host to Peripheral via Set Control Line State ++ * ++ * Host (DTE) Line State ACM (DCE) Null Modem TIOCM Peripheral (DTE) ++ * ------------------------------------------------------------------------------------------------------------- ++ * Data Terminal Ready D0 DTR DTR -> DSR TIOCM_DSR Data Set Ready ++ * DTR -> DCD TIOCM_CAR Carrier Detect ++ * Request To Send D1 RTS RTS -> CTS TIOCM_CTS Clear to Send ++ * ------------------------------------------------------------------------------------------------------------- ++ * ++ * ++ * @ingroup MODEMFunction ++ */ ++ ++ ++extern struct usbd_function_driver modem_function_driver; ++ ++//#define MODEM_OPENED (1 << 0) /*!< OPENED flag */ ++#define MODEMFD_CLOCAL (1 << 1) /*!< CLOCAL flag */ ++//#define MODEMFD_LOOPBACK (1 << 2) /*!< LOOPBACK flag */ ++#define MODEMFD_EXCLUSIVE (1 << 3) /*!< EXCLUSIVE flag */ ++#define MODEMFD_THROTTLED (1 << 4) /*!< THROTTLED flag */ ++ ++/*! struct modem_private ++ */ ++struct modem_private { ++ ++ struct modem_driver *modem_driver; /*!< modem structure */ ++ int modem_driver_registered; /*!< non-zero if modem_driver registered */ ++ int usb_driver_registered; /*!< non-zero if usb function registered */ ++ ++ struct modem_struct *modem; /*!< non-null if modem open */ ++ struct tq_struct wqueue; /*!< task queue for writer wakeup */ ++ struct tq_struct hqueue; /*!< task queue for hangup */ ++ ++ u32 flags; /*!< flags */ ++ ++ u32 tiocm; /*!< tiocm settings */ ++ ++ struct serial_struct serial_struct; /*!< serial structure used for TIOCSSERIAL and TIOCGSERIAL */ ++ ++ wait_queue_head_t tiocm_wait; ++ u32 tiocgicount; ++ ++ u32 c_cflag; ++ ++ wait_queue_head_t open_wait; // wait queue for blocking open ++ int exiting; // True if module exiting ++}; ++ ++ +diff -uNr linux/drivers/no-otg/functions/acm/obex-l24-os.c linux/drivers/otg/functions/acm/obex-l24-os.c +--- linux/drivers/no-otg/functions/acm/obex-l24-os.c 1970-01-01 01:00:00.000000000 +0100 ++++ linux/drivers/otg/functions/acm/obex-l24-os.c 2006-09-01 21:41:26.000000000 +0200 +@@ -0,0 +1,194 @@ ++/* ++ * otg/functions/acm/obex-l24-os.c ++ * ++ * Copyright (c) 2003, 2004 Belcarra ++ * ++ * By: ++ * Tom Rushworth <tbr@belcarra.com>, ++ * Stuart Lynne <sl@belcarra.com>, ++ * Bruce Balden <balden@belcarra.com> ++ * ++ */ ++/*! ++ * @file otg/functions/acm/obex-l24-os.c ++ * @brief ACM Function Driver private defines ++ * ++ * An ACM (Abstract Control Model) driver is composed of two pieces: ++ * 1) An OS specific piece that handles creating and operating ++ * a serial device for the given OS. ++ * 2) A USB specific piece that interfaces either with the host ++ * usbcore layer, or with the otgcore layer. ++ * ++ * If the USB piece interfaces with the host usbcore layer you get ++ * an ACM class driver. If the USB piece interfaces with the otgcore ++ * layer you get an ACM function driver. ++ * ++ * This file is the OS specific piece that interfaces with Linux ++ * (kernel versions 2.4.*). It creates two simple characters devices. ++ * One for the data transport and one for the communications interface. ++ * ++ * @ingroup ACMFunction ++ */ ++ ++ ++//#include <otg/osversion.h> ++#include <otg/otg-compat.h> ++#include <otg/otg-module.h> ++ ++MOD_AUTHOR ("tbr@belcarra.com, sl@belcarra.com"); ++ ++MOD_DESCRIPTION ("Belcarra CDC-ACM Function"); ++EMBED_LICENSE(); ++ ++ ++#include <linux/init.h> ++#include <asm/uaccess.h> ++#include <linux/ctype.h> ++#include <linux/timer.h> ++#include <linux/interrupt.h> ++#include <asm/atomic.h> ++#include <linux/tty.h> ++#include <linux/tty_driver.h> ++#include <linux/tty_flip.h> ++#include <linux/smp_lock.h> ++#include <linux/slab.h> ++ ++#include <otg/usbp-chap9.h> ++#include <otg/usbp-func.h> ++ ++#include <linux/capability.h> ++#include <otg/otg-trace.h> ++#include "acm.h" ++#include "acm-fd.h" ++#include "acm-os.h" ++ ++// May need an ifdef here to pick up the acm_cl_usb_ops in the future. ++static struct acm_usb_services *usb_ops = &acm_fd_usb_ops; ++ ++#define OBX obx_fd_trace_tag ++otg_tag_t obx_fd_trace_tag; ++ ++/* Module Parameters ************************************************************************* */ ++ ++static u32 vendor_id; ++static u32 product_id; ++static u32 max_queued_urbs = MAX_QUEUED_URBS; ++static u32 max_queued_bytes = MAX_QUEUED_BYTES; ++ ++MOD_PARM (vendor_id, "i"); ++MOD_PARM (product_id, "i"); ++MOD_PARM (max_queued_urbs, "i"); ++MOD_PARM (max_queued_bytes, "i"); ++ ++MOD_PARM_DESC (vendor_id, "Device Vendor ID"); ++MOD_PARM_DESC (product_id, "Device Product ID"); ++MOD_PARM_DESC (max_queued_urbs, "Maximum TX Queued Urbs"); ++MOD_PARM_DESC (max_queued_bytes, "Maximum TX Queued Bytes"); ++ ++ ++/* ACM ***************************************************************************************** */ ++ ++#define ACM_TTY_MAJOR 166 ++#define ACM_TTY_MINOR 0 ++#define ACM_TTY_MINORS 1 ++ ++static struct acm_private acm_private; ++ ++/* USB Simple Char Device ********************************************************************** */ ++ ++ ++/* USB Comm Char Device ************************************************************************ */ ++ ++ ++/* USB Module init/exit ************************************************************************ */ ++/* ++ * acm_modinit - module init ++ * ++ */ ++STATIC int acm_modinit (void) ++{ ++ int i; ++ printk (KERN_INFO "Copyright (c) 2003-2004 sl@belcarra.com, tbr@belcarra.com\n"); ++ OBX = otg_trace_obtain_tag(); ++ ++ // initialize private structure ++ //acm_private.tty_driver = &acm_tty_driver; ++ //acm_private.wqueue.routine = acm_wakeup_writers; ++ //acm_private.wqueue.data = &acm_private; ++ //acm_private.hqueue.routine = acm_hangup; ++ //acm_private.hqueue.data = &acm_private; ++ //acm_private.recv_tqueue.routine = &acm_start_recv; ++ //acm_private.recv_tqueue.data = &acm_private; ++ ++ //init_waitqueue_head(&acm_private.open_wait); ++ ++ ++ // register as usb function driver ++ ++ THROW_IF (usb_ops->initialize_usb_part(&acm_private,"acm_fd",vendor_id,product_id, ++ max_queued_urbs,max_queued_bytes ), error); ++ ++ CATCH(error) { ++ printk(KERN_ERR"%s: ERROR\n", __FUNCTION__); ++ ++ TRACE_MSG0(OBX,"--> -EINVAL"); ++ return -EINVAL; ++ } ++ TRACE_MSG0(OBX,"--> 0"); ++ return 0; ++} ++ ++void acm_wait_task(WORK_ITEM *queue) ++{ ++ TRACE_MSG1(OBX,"entered data=%p",queue->data); ++ RETURN_IF(!queue->data); ++ queue->data = NULL; ++#if defined(LINUX24) ++ while (queue->sync) { ++ TRACE_MSG1(OBX,"waiting for queue: %p",queue); ++ schedule_timeout(HZ); ++ } ++#else ++ while(PENDING_WORK_ITEM((*queue))){ ++ TRACE_MSG1(OBX,"waiting for queue: %p",queue); ++ SCHEDULE_TIMEOUT(1); // 1 second delay ++ } ++#endif ++ TRACE_MSG0(OBX,"exited"); ++} ++ ++/* acm_modexit - module cleanup ++ */ ++STATIC void acm_modexit (void) ++{ ++ unsigned long flags; ++ struct usbd_urb *urb; ++ TRACE_MSG0(OBX,"entered"); ++ ++ // Wake up any pending opens after setting the exiting flag. ++ local_irq_save(flags); ++ acm_private.exiting = 1; ++ //if (acm_private.open_wait_count > 0) { ++ // wake_up_interruptible(&acm_private.open_wait); ++ //} ++ local_irq_restore(flags); ++ ++ // verify no tasks are running ++ //acm_wait_task(&acm_private.wqueue); ++ //acm_wait_task(&acm_private.hqueue); ++ ++//#if defined(LINUX24) ++// run_task_queue(&tq_timer); ++//#else ++// blk_run_queues(); ++//#endif ++ ++ //acm_wait_task(&acm_private.recv_tqueue); ++ ++ usb_ops->terminate_usb_part(&acm_private); ++ otg_trace_invalidate_tag(OBX); ++} ++ ++ ++module_init (acm_modinit); ++module_exit (acm_modexit); +diff -uNr linux/drivers/no-otg/functions/acm/obex.c linux/drivers/otg/functions/acm/obex.c +--- linux/drivers/no-otg/functions/acm/obex.c 1970-01-01 01:00:00.000000000 +0100 ++++ linux/drivers/otg/functions/acm/obex.c 2006-09-01 21:41:26.000000000 +0200 +@@ -0,0 +1,263 @@ ++/* ++ * otg/functions/acm/obex.c ++ * ++ * Copyright (c) 2003, 2004 Belcarra ++ * ++ * By: ++ * Tom Rushworth <tbr@belcarra.com>, ++ * Stuart Lynne <sl@belcarra.com>, ++ * Bruce Balden <balden@belcarra.com> ++ */ ++/*! ++ * @file otg/functions/acm/obex.c ++ * @brief ACM-OBEX Descriptor Set ++ * ++ * This file is the USB specific piece that interfaces with the otgcore layer. ++ * If you're looking for the USB specific piece that interfaces with the ++ * host usbcore layer, that's in otg/functions/acm/acm-cl.c ++ * ++ * Note: this function driver requires the following endpoints: ++ * ++ * BULK-IN ++ * BULK-OUT ++ * INTERRUPT-IN ++ * ++ * This function driver cannot be used on devices (such as the StrongArm ++ * SA1100) that do not have an interrupt endpoint. ++ * ++ * @ingroup OBEXFunction ++ */ ++ ++ ++#include <otg/otg-compat.h> ++#include <otg/otg-module.h> ++ ++#include <linux/init.h> ++#include <asm/uaccess.h> ++#include <linux/ctype.h> ++#include <linux/timer.h> ++#include <linux/interrupt.h> ++#include <asm/atomic.h> ++#include <linux/smp_lock.h> ++#include <linux/slab.h> ++ ++#include <otg/usbp-chap9.h> ++#include <otg/usbp-func.h> ++ ++#include <otg/otg-trace.h> ++#include "acm.h" ++#include "acm-fd.h" ++#include "acm-os.h" ++ ++ ++ ++/* ++ * CDC ACM Configuration ++ * ++ * Endpoint, Class, Interface, Configuration and Device descriptors/descriptions ++ */ ++ ++/*! BULK OUT Data Endpoint Descriptor ++ */ ++static u8 obx_alt_1[] = { 0x07, USB_DT_ENDPOINT, USB_DIR_OUT, BULK, 0, 0x00, 0x00, }; ++ ++/*! BULK In Data Endpoint Descriptor ++ */ ++static u8 obx_alt_2[] = { 0x07, USB_DT_ENDPOINT, USB_DIR_IN, BULK, 0, 0x00, 0x00, }; ++ ++/*! Data Endpoint Descriptor List ++ */ ++static struct usbd_endpoint_descriptor *obx_alt_endpoints[] = { ++ (struct usbd_endpoint_descriptor *) obx_alt_1, ++ (struct usbd_endpoint_descriptor *) obx_alt_2, }; ++ ++/*! Data Endpoint Index List ++ */ ++u8 obx_alt_indexes[] = { BULK_OUT, BULK_IN, }; ++ ++/*! INTERRUPT In Comm Endpoint Descriptor ++ */ ++static u8 obx_comm_1[] = { 0x07, USB_DT_ENDPOINT, USB_DIR_IN, INTERRUPT, 0, 0x00, 0x0a, }; ++ ++/*! Comm Endpoint Descriptor List ++ */ ++static struct usbd_endpoint_descriptor *obx_comm_endpoints[] = { (struct usbd_endpoint_descriptor *) obx_comm_1 }; ++ ++ ++/*! Comm Endpoint Index List ++ */ ++u8 obx_comm_indexes[] = { INT_IN, }; ++ ++/*! @{ ++ * Class Descriptors ++ */ ++static u8 cdc_class_1[] = { 0x05, CS_INTERFACE, USB_ST_HEADER, 0x01, 0x01, /* CLASS_BDC_VERSION, CLASS_BDC_VERSION */ }; ++static u8 cdc_class_2[] = { 0x05, CS_INTERFACE, USB_ST_CMF, 0x03, 0x01, /* bMasterInterface: 0, bSlaveInterface: 1 */ }; ++static u8 cdc_class_3[] = { 0x05, CS_INTERFACE, USB_ST_UF, 0x00, 0x01, /* bMasterInterface: 0, bSlaveInterface: 1 */ }; ++/*! @} */ ++ ++/*! ACMF Class Descriptor ++ * ACMF - c.f. Table 28 ++ * currenty set to 0x2 - Support Set_Line_Coding etc, ++ * ++ * XXX Should we also set 0x4 - Supports Network_Notification? ++ */ ++static u8 cdc_class_4[] = { 0x04, CS_INTERFACE, USB_ST_ACMF, 0x02, }; ++ ++static struct usbd_generic_class_descriptor *cdc_comm_class_descriptors[] = ++ { (struct usbd_generic_class_descriptor *) cdc_class_1, ++ (struct usbd_generic_class_descriptor *) cdc_class_2, ++ (struct usbd_generic_class_descriptor *) cdc_class_3, ++ (struct usbd_generic_class_descriptor *) cdc_class_4, }; ++ ++ ++ ++/* Alternate Descriptors */ ++// First two bytes are identical in all: bLength, bDescriptorType (0x09 0x04) ++ ++/*! Comm Alternate Descriptor ++ */ ++static u8 cdc_comm_alternate_descriptor[sizeof(struct usbd_interface_descriptor)] = { ++ 0x09, USB_DT_INTERFACE, COMM_INTF, 0x00, 0x00, // bInterfaceNumber, bAlternateSetting, bNumEndpoints ++ COMMUNICATIONS_INTERFACE_CLASS, COMMUNICATIONS_ACM_SUBCLASS, 0x01, 0x00, }; ++ ++/*! Data Alternate Descriptor ++ */ ++static u8 cdc_data_alternate_descriptor[sizeof(struct usbd_interface_descriptor)] = { ++ 0x09, USB_DT_INTERFACE, DATA_INTF, 0x00, 0x00, // bInterfaceNumber, bAlternateSetting, bNumEndpoints ++ DATA_INTERFACE_CLASS, COMMUNICATIONS_NO_SUBCLASS, COMMUNICATIONS_NO_PROTOCOL, 0x00, }; ++ ++ ++ ++/*! Comm alternate descriptions ++ */ ++static struct usbd_alternate_description cdc_comm_alternate_descriptions[] = { ++ { iInterface: CONFIG_OTG_ACM_COMM_INTF, ++ interface_descriptor: (struct usbd_interface_descriptor *)&cdc_comm_alternate_descriptor, ++ classes:sizeof (cdc_comm_class_descriptors) / sizeof (struct usbd_generic_class_descriptor *), ++ class_list: cdc_comm_class_descriptors, ++ endpoint_list: obx_comm_endpoints, ++ endpoints:sizeof (obx_comm_endpoints) / sizeof(struct usbd_endpoint_descriptor *), ++ endpoint_indexes: obx_comm_indexes, ++ }, }; ++ ++/*! Data alternate descriptions ++ */ ++static struct usbd_alternate_description cdc_data_alternate_descriptions[] = { ++ { iInterface: CONFIG_OTG_ACM_DATA_INTF, ++ interface_descriptor: (struct usbd_interface_descriptor *)&cdc_data_alternate_descriptor, ++ endpoint_list: obx_alt_endpoints, ++ endpoints:sizeof (obx_alt_endpoints) / sizeof(struct usbd_endpoint_descriptor *), ++ endpoint_indexes: obx_alt_indexes, ++ }, }; ++ ++ ++/*! Interface Descriptions List */ ++static struct usbd_interface_description cdc_interfaces[] = { ++ { ++ alternates:sizeof (cdc_comm_alternate_descriptions) / sizeof (struct usbd_alternate_description), ++ alternate_list:cdc_comm_alternate_descriptions, ++ }, ++ ++ { ++ alternates:sizeof (cdc_data_alternate_descriptions) / sizeof (struct usbd_alternate_description), ++ alternate_list:cdc_data_alternate_descriptions, ++ }, ++ ++}; ++ ++ ++/*! Configuration Descriptor ++ */ ++static u8 cdc_configuration_descriptor[sizeof(struct usbd_configuration_descriptor)] = { ++ 0x09, USB_DT_CONFIGURATION, 0x00, 0x00, sizeof (cdc_interfaces) / sizeof (struct usbd_interface_description), ++ 0x01, 0x00, BMATTRIBUTE, BMAXPOWER, }; ++ ++/*! Configuration Description ++ */ ++struct usbd_configuration_description obx_description[] = { ++ { iConfiguration: CONFIG_OTG_ACM_DESC, ++ configuration_descriptor: (struct usbd_configuration_descriptor *)cdc_configuration_descriptor, ++ }, }; ++ ++/*! Device Descriptor ++ */ ++static struct usbd_device_descriptor obx_device_descriptor = { ++ bLength: sizeof(struct usbd_device_descriptor), ++ bDescriptorType: USB_DT_DEVICE, ++ bcdUSB: __constant_cpu_to_le16(USB_BCD_VERSION), ++ bDeviceClass: COMMUNICATIONS_DEVICE_CLASS, ++ bDeviceSubClass: 0x02, ++ bDeviceProtocol: 0x00, ++ bMaxPacketSize0: 0x00, ++ idVendor: __constant_cpu_to_le16(CONFIG_OTG_ACM_VENDORID), ++ idProduct: __constant_cpu_to_le16(CONFIG_OTG_ACM_PRODUCTID), ++ bcdDevice: __constant_cpu_to_le16(CONFIG_OTG_ACM_BCDDEVICE), ++}; ++ ++#ifdef CONFIG_OTG_HIGH_SPEED ++/*! High Speed Device Qualifier Descriptor ++ */ ++static struct usbd_device_qualifier_descriptor obx_device_qualifier_descriptor = { ++ bLength: sizeof(struct usbd_device_qualifier_descriptor), ++ bDescriptorType: USB_DT_DEVICE_QUALIFIER, ++ bcdUSB: __constant_cpu_to_le16(USB_BCD_VERSION), ++ bDeviceClass: COMMUNICATIONS_DEVICE_CLASS, ++ bDeviceSubClass: 0x02, ++ bDeviceProtocol: 0x00, ++ bMaxPacketSize0: 0x00, ++}; ++#endif /* CONFIG_OTG_HIGH_SPEED */ ++ ++ ++/*! Endpoint Request List ++ */ ++static struct usbd_endpoint_request obx_endpoint_requests[ENDPOINTS+1] = { ++ { 1, 1, 0, USB_DIR_OUT | USB_ENDPOINT_BULK, 64, 512, }, ++ { 1, 1, 0, USB_DIR_IN | USB_ENDPOINT_BULK, 64 /* * 4 */, 512, }, ++ { 1, 0, 0, USB_DIR_IN | USB_ENDPOINT_INTERRUPT, 16, 64, }, ++ { 0, }, ++}; ++ ++/*! OTG Descriptor ++ */ ++static struct usbd_otg_descriptor obx_otg_descriptor = { ++ bLength : sizeof(struct usbd_otg_descriptor), ++ bDescriptorType: USB_DT_OTG, ++ bmAttributes: 0, ++}; ++ ++/*! Device Description ++ */ ++struct usbd_device_description obx_device_description = { ++ device_descriptor: &obx_device_descriptor, ++#ifdef CONFIG_OTG_HIGH_SPEED ++ device_qualifier_descriptor: &obx_device_qualifier_descriptor, ++#endif /* CONFIG_OTG_HIGH_SPEED */ ++ otg_descriptor: &obx_otg_descriptor, ++ iManufacturer: CONFIG_OTG_ACM_MANUFACTURER, ++ iProduct: CONFIG_OTG_ACM_PRODUCT_NAME, ++#if !defined(CONFIG_OTG_NO_SERIAL_NUMBER) && defined(CONFIG_OTG_SERIAL_NUMBER_STR) ++ iSerialNumber: CONFIG_OTG_SERIAL_NUMBER_STR, ++#endif ++}; ++ ++ ++ ++/*! function_driver ++ */ ++struct usbd_function_driver obex_function_driver = { ++ name: "ACM-OBX", ++ fops:&function_ops, ++ device_description:&obx_device_description, ++ bNumConfigurations:sizeof (obx_description) / sizeof (struct usbd_configuration_description), ++ configuration_description:obx_description, ++ idVendor: __constant_cpu_to_le16(CONFIG_OTG_ACM_VENDORID), ++ idProduct: __constant_cpu_to_le16(CONFIG_OTG_ACM_PRODUCTID), ++ bcdDevice: __constant_cpu_to_le16(CONFIG_OTG_ACM_BCDDEVICE), ++ bNumInterfaces:sizeof (cdc_interfaces) / sizeof (struct usbd_interface_description), ++ interface_list:cdc_interfaces, ++ endpointsRequested: ENDPOINTS, ++ requestedEndpoints: obx_endpoint_requests, ++}; ++ +diff -uNr linux/drivers/no-otg/functions/acm/obex.h linux/drivers/otg/functions/acm/obex.h +--- linux/drivers/no-otg/functions/acm/obex.h 1970-01-01 01:00:00.000000000 +0100 ++++ linux/drivers/otg/functions/acm/obex.h 2006-09-01 21:41:26.000000000 +0200 +@@ -0,0 +1,66 @@ ++/* ++ * otg/functions/acm/obex.h ++ * ++ * Copyright (c) 2003, 2004 Belcarra ++ * ++ * By: ++ * Stuart Lynne <sl@belcarra.com>, ++ * Tom Rushworth <tbr@belcarra.com>, ++ * Bruce Balden <balden@belcarra.com> ++ * ++ */ ++/*! ++ * @defgroup OBEXFunction ACM-OBEX ++ * @ingroup functiongroup ++ */ ++/*! ++ * @file otg/functions/acm/obex.h ++ * @brief ACM Function Driver private defines ++ * ++ * ++ * This is an ACM Function Driver. The upper edge is exposed ++ * to the hosting OS as a Posix type character device. The lower ++ * edge implements the USB Device Stack API. ++ * ++ * ++ * ++ * @ingroup OBEXFunction ++ */ ++ ++ ++extern struct usbd_function_driver tty_function_driver; ++ ++//#define TTY_OPENED (1 << 0) /*!< OPENED flag */ ++#define TTYFD_CLOCAL (1 << 1) /*!< CLOCAL flag */ ++//#define TTYFD_LOOPBACK (1 << 2) /*!< LOOPBACK flag */ ++#define TTYFD_EXCLUSIVE (1 << 3) /*!< EXCLUSIVE flag */ ++#define TTYFD_THROTTLED (1 << 4) /*!< THROTTLED flag */ ++ ++/*! struct obex_private ++ */ ++struct obex_private { ++ ++ struct tty_driver *tty_driver; /*!< tty structure */ ++ int tty_driver_registered; /*!< non-zero if tty_driver registered */ ++ int usb_driver_registered; /*!< non-zero if usb function registered */ ++ ++ struct tty_struct *tty; /*!< non-null if tty open */ ++ struct tq_struct wqueue; /*!< task queue for writer wakeup */ ++ struct tq_struct hqueue; /*!< task queue for hangup */ ++ ++ u32 flags; /*!< flags */ ++ ++ u32 tiocm; /*!< tiocm settings */ ++ ++ struct serial_struct serial_struct; /*!< serial structure used for TIOCSSERIAL and TIOCGSERIAL */ ++ ++ wait_queue_head_t tiocm_wait; ++ u32 tiocgicount; ++ ++ u32 c_cflag; ++ ++ wait_queue_head_t open_wait; // wait queue for blocking open ++ int exiting; // True if module exiting ++}; ++ ++ +diff -uNr linux/drivers/no-otg/functions/acm/tty-fd.c linux/drivers/otg/functions/acm/tty-fd.c +--- linux/drivers/no-otg/functions/acm/tty-fd.c 1970-01-01 01:00:00.000000000 +0100 ++++ linux/drivers/otg/functions/acm/tty-fd.c 2006-09-01 21:41:26.000000000 +0200 +@@ -0,0 +1,263 @@ ++/* ++ * otg/functions/acm/tty-fd.c ++ * ++ * Copyright (c) 2003, 2004 Belcarra ++ * ++ * By: ++ * Tom Rushworth <tbr@belcarra.com>, ++ * Stuart Lynne <sl@belcarra.com>, ++ * Bruce Balden <balden@belcarra.com> ++ */ ++/*! ++ * @file otg/functions/acm/tty-fd.c ++ * @brief ACM-TTY Descriptor Set ++ * ++ * This file is the USB specific piece that interfaces with the otgcore layer. ++ * If you're looking for the USB specific piece that interfaces with the ++ * host usbcore layer, that's in otg/functions/acm/acm-cl.c ++ * ++ * Note: this function driver requires the following endpoints: ++ * ++ * BULK-IN ++ * BULK-OUT ++ * INTERRUPT-IN ++ * ++ * This function driver cannot be used on devices (such as the StrongArm ++ * SA1100) that do not have an interrupt endpoint. ++ * ++ * @ingroup TTYFunction ++ */ ++ ++ ++#include <otg/otg-compat.h> ++#include <otg/otg-module.h> ++ ++#include <linux/init.h> ++#include <asm/uaccess.h> ++#include <linux/ctype.h> ++#include <linux/timer.h> ++#include <linux/interrupt.h> ++#include <asm/atomic.h> ++#include <linux/smp_lock.h> ++#include <linux/slab.h> ++ ++#include <otg/usbp-chap9.h> ++#include <otg/usbp-func.h> ++ ++#include <otg/otg-trace.h> ++#include "acm.h" ++#include "acm-fd.h" ++#include "acm-os.h" ++ ++ ++ ++/* ++ * CDC ACM Configuration ++ * ++ * Endpoint, Class, Interface, Configuration and Device descriptors/descriptions ++ */ ++ ++/*! BULK OUT Data Endpoint Descriptor ++ */ ++static u8 tty_alt_1[] = { 0x07, USB_DT_ENDPOINT, USB_DIR_OUT, BULK, 0, 0x00, 0x00, }; ++ ++/*! BULK In Data Endpoint Descriptor ++ */ ++static u8 tty_alt_2[] = { 0x07, USB_DT_ENDPOINT, USB_DIR_IN, BULK, 0, 0x00, 0x00, }; ++ ++/*! Data Endpoint Descriptor List ++ */ ++static struct usbd_endpoint_descriptor *tty_alt_endpoints[] = { ++ (struct usbd_endpoint_descriptor *) tty_alt_1, ++ (struct usbd_endpoint_descriptor *) tty_alt_2, }; ++ ++/*! Data Endpoint Index List ++ */ ++u8 tty_alt_indexes[] = { BULK_OUT, BULK_IN, }; ++ ++/*! INTERRUPT In Comm Endpoint Descriptor ++ */ ++static u8 tty_comm_1[] = { 0x07, USB_DT_ENDPOINT, USB_DIR_IN, INTERRUPT, 0, 0x00, 0x0a, }; ++ ++/*! Comm Endpoint Descriptor List ++ */ ++static struct usbd_endpoint_descriptor *tty_comm_endpoints[] = { (struct usbd_endpoint_descriptor *) tty_comm_1 }; ++ ++ ++/*! Comm Endpoint Index List ++ */ ++u8 tty_comm_indexes[] = { INT_IN, }; ++ ++/*! @{ ++ * Class Descriptors ++ */ ++static u8 cdc_class_1[] = { 0x05, CS_INTERFACE, USB_ST_HEADER, 0x01, 0x01, /* CLASS_BDC_VERSION, CLASS_BDC_VERSION */ }; ++static u8 cdc_class_2[] = { 0x05, CS_INTERFACE, USB_ST_CMF, 0x03, 0x01, /* bMasterInterface: 0, bSlaveInterface: 1 */ }; ++static u8 cdc_class_3[] = { 0x05, CS_INTERFACE, USB_ST_UF, 0x00, 0x01, /* bMasterInterface: 0, bSlaveInterface: 1 */ }; ++/*! @} */ ++ ++/*! ACMF Class Descriptor ++ * ACMF - c.f. Table 28 ++ * currenty set to 0x2 - Support Set_Line_Coding etc, ++ * ++ * XXX Should we also set 0x4 - Supports Network_Notification? ++ */ ++static u8 cdc_class_4[] = { 0x04, CS_INTERFACE, USB_ST_ACMF, 0x02, }; ++ ++static struct usbd_generic_class_descriptor *cdc_comm_class_descriptors[] = ++ { (struct usbd_generic_class_descriptor *) cdc_class_1, ++ (struct usbd_generic_class_descriptor *) cdc_class_2, ++ (struct usbd_generic_class_descriptor *) cdc_class_3, ++ (struct usbd_generic_class_descriptor *) cdc_class_4, }; ++ ++ ++ ++/* Alternate Descriptors */ ++// First two bytes are identical in all: bLength, bDescriptorType (0x09 0x04) ++ ++/*! Comm Alternate Descriptor ++ */ ++static u8 cdc_comm_alternate_descriptor[sizeof(struct usbd_interface_descriptor)] = { ++ 0x09, USB_DT_INTERFACE, COMM_INTF, 0x00, 0x00, // bInterfaceNumber, bAlternateSetting, bNumEndpoints ++ COMMUNICATIONS_INTERFACE_CLASS, COMMUNICATIONS_ACM_SUBCLASS, 0x01, 0x00, }; ++ ++/*! Data Alternate Descriptor ++ */ ++static u8 cdc_data_alternate_descriptor[sizeof(struct usbd_interface_descriptor)] = { ++ 0x09, USB_DT_INTERFACE, DATA_INTF, 0x00, 0x00, // bInterfaceNumber, bAlternateSetting, bNumEndpoints ++ DATA_INTERFACE_CLASS, COMMUNICATIONS_NO_SUBCLASS, COMMUNICATIONS_NO_PROTOCOL, 0x00, }; ++ ++ ++ ++/*! Comm alternate descriptions ++ */ ++static struct usbd_alternate_description cdc_comm_alternate_descriptions[] = { ++ { iInterface: CONFIG_OTG_ACM_COMM_INTF, ++ interface_descriptor: (struct usbd_interface_descriptor *)&cdc_comm_alternate_descriptor, ++ classes:sizeof (cdc_comm_class_descriptors) / sizeof (struct usbd_generic_class_descriptor *), ++ class_list: cdc_comm_class_descriptors, ++ endpoint_list: tty_comm_endpoints, ++ endpoints:sizeof (tty_comm_endpoints) / sizeof(struct usbd_endpoint_descriptor *), ++ endpoint_indexes: tty_comm_indexes, ++ }, }; ++ ++/*! Data alternate descriptions ++ */ ++static struct usbd_alternate_description cdc_data_alternate_descriptions[] = { ++ { iInterface: CONFIG_OTG_ACM_DATA_INTF, ++ interface_descriptor: (struct usbd_interface_descriptor *)&cdc_data_alternate_descriptor, ++ endpoint_list: tty_alt_endpoints, ++ endpoints:sizeof (tty_alt_endpoints) / sizeof(struct usbd_endpoint_descriptor *), ++ endpoint_indexes: tty_alt_indexes, ++ }, }; ++ ++ ++/*! Interface Descriptions List */ ++static struct usbd_interface_description cdc_interfaces[] = { ++ { ++ alternates:sizeof (cdc_comm_alternate_descriptions) / sizeof (struct usbd_alternate_description), ++ alternate_list:cdc_comm_alternate_descriptions, ++ }, ++ ++ { ++ alternates:sizeof (cdc_data_alternate_descriptions) / sizeof (struct usbd_alternate_description), ++ alternate_list:cdc_data_alternate_descriptions, ++ }, ++ ++}; ++ ++ ++/*! Configuration Descriptor ++ */ ++static u8 cdc_configuration_descriptor[sizeof(struct usbd_configuration_descriptor)] = { ++ 0x09, USB_DT_CONFIGURATION, 0x00, 0x00, sizeof (cdc_interfaces) / sizeof (struct usbd_interface_description), ++ 0x01, 0x00, BMATTRIBUTE, BMAXPOWER, }; ++ ++/*! Configuration Description ++ */ ++struct usbd_configuration_description tty_description[] = { ++ { iConfiguration: CONFIG_OTG_ACM_DESC, ++ configuration_descriptor: (struct usbd_configuration_descriptor *)cdc_configuration_descriptor, ++ }, }; ++ ++/*! Device Descriptor ++ */ ++static struct usbd_device_descriptor tty_device_descriptor = { ++ bLength: sizeof(struct usbd_device_descriptor), ++ bDescriptorType: USB_DT_DEVICE, ++ bcdUSB: __constant_cpu_to_le16(USB_BCD_VERSION), ++ bDeviceClass: COMMUNICATIONS_DEVICE_CLASS, ++ bDeviceSubClass: 0x02, ++ bDeviceProtocol: 0x00, ++ bMaxPacketSize0: 0x00, ++ idVendor: __constant_cpu_to_le16(CONFIG_OTG_ACM_VENDORID), ++ idProduct: __constant_cpu_to_le16(CONFIG_OTG_ACM_PRODUCTID), ++ bcdDevice: __constant_cpu_to_le16(CONFIG_OTG_ACM_BCDDEVICE), ++}; ++ ++#ifdef CONFIG_OTG_HIGH_SPEED ++/*! High Speed Device Qualifier Descriptor ++ */ ++static struct usbd_device_qualifier_descriptor tty_device_qualifier_descriptor = { ++ bLength: sizeof(struct usbd_device_qualifier_descriptor), ++ bDescriptorType: USB_DT_DEVICE_QUALIFIER, ++ bcdUSB: __constant_cpu_to_le16(USB_BCD_VERSION), ++ bDeviceClass: COMMUNICATIONS_DEVICE_CLASS, ++ bDeviceSubClass: 0x02, ++ bDeviceProtocol: 0x00, ++ bMaxPacketSize0: 0x00, ++}; ++#endif /* CONFIG_OTG_HIGH_SPEED */ ++ ++ ++/*! Endpoint Request List ++ */ ++static struct usbd_endpoint_request tty_endpoint_requests[ENDPOINTS+1] = { ++ { 1, 1, 0, USB_DIR_OUT | USB_ENDPOINT_BULK, 64, 512, }, ++ { 1, 1, 0, USB_DIR_IN | USB_ENDPOINT_BULK, 64 /* * 4 */, 512, }, ++ { 1, 0, 0, USB_DIR_IN | USB_ENDPOINT_INTERRUPT, 16, 64, }, ++ { 0, }, ++}; ++ ++/*! OTG Descriptor ++ */ ++static struct usbd_otg_descriptor tty_otg_descriptor = { ++ bLength : sizeof(struct usbd_otg_descriptor), ++ bDescriptorType: USB_DT_OTG, ++ bmAttributes: 0, ++}; ++ ++/*! Device Description ++ */ ++struct usbd_device_description tty_device_description = { ++ device_descriptor: &tty_device_descriptor, ++#ifdef CONFIG_OTG_HIGH_SPEED ++ device_qualifier_descriptor: &tty_device_qualifier_descriptor, ++#endif /* CONFIG_OTG_HIGH_SPEED */ ++ otg_descriptor: &tty_otg_descriptor, ++ iManufacturer: CONFIG_OTG_ACM_MANUFACTURER, ++ iProduct: CONFIG_OTG_ACM_PRODUCT_NAME, ++#if !defined(CONFIG_OTG_NO_SERIAL_NUMBER) && defined(CONFIG_OTG_SERIAL_NUMBER_STR) ++ iSerialNumber: CONFIG_OTG_SERIAL_NUMBER_STR, ++#endif ++}; ++ ++/*! function_driver ++ */ ++struct usbd_function_driver tty_function_driver = { ++ name: "ACM-TTY", ++ fops:&function_ops, ++ device_description:&tty_device_description, ++ bNumConfigurations:sizeof (tty_description) / sizeof (struct usbd_configuration_description), ++ configuration_description:tty_description, ++ idVendor: __constant_cpu_to_le16(CONFIG_OTG_ACM_VENDORID), ++ idProduct: __constant_cpu_to_le16(CONFIG_OTG_ACM_PRODUCTID), ++ bcdDevice: __constant_cpu_to_le16(CONFIG_OTG_ACM_BCDDEVICE), ++ bNumInterfaces:sizeof (cdc_interfaces) / sizeof (struct usbd_interface_description), ++ interface_list:cdc_interfaces, ++ endpointsRequested: ENDPOINTS, ++ requestedEndpoints: tty_endpoint_requests, ++}; ++ ++ ++ +diff -uNr linux/drivers/no-otg/functions/acm/tty-if.c linux/drivers/otg/functions/acm/tty-if.c +--- linux/drivers/no-otg/functions/acm/tty-if.c 1970-01-01 01:00:00.000000000 +0100 ++++ linux/drivers/otg/functions/acm/tty-if.c 2006-09-01 21:41:26.000000000 +0200 +@@ -0,0 +1,263 @@ ++/* ++ * otg/functions/acm/tty-if.c ++ * ++ * Copyright (c) 2003, 2004 Belcarra ++ * ++ * By: ++ * Tom Rushworth <tbr@belcarra.com>, ++ * Stuart Lynne <sl@belcarra.com>, ++ * Bruce Balden <balden@belcarra.com> ++ */ ++/*! ++ * @file otg/functions/acm/tty-if.c ++ * @brief ACM-TTY Descriptor Set ++ * ++ * This file is the USB specific piece that interfaces with the otgcore layer. ++ * If you're looking for the USB specific piece that interfaces with the ++ * host usbcore layer, that's in otg/functions/acm/acm-cl.c ++ * ++ * Note: this function driver requires the following endpoints: ++ * ++ * BULK-IN ++ * BULK-OUT ++ * INTERRUPT-IN ++ * ++ * This function driver cannot be used on devices (such as the StrongArm ++ * SA1100) that do not have an interrupt endpoint. ++ * ++ * @ingroup TTYFunction ++ */ ++ ++ ++#include <otg/otg-compat.h> ++#include <otg/otg-module.h> ++ ++#include <linux/init.h> ++#include <asm/uaccess.h> ++#include <linux/ctype.h> ++#include <linux/timer.h> ++#include <linux/interrupt.h> ++#include <asm/atomic.h> ++#include <linux/smp_lock.h> ++#include <linux/slab.h> ++ ++#include <otg/usbp-chap9.h> ++#include <otg/usbp-func.h> ++ ++#include <otg/otg-trace.h> ++#include "acm.h" ++#include "acm-fd.h" ++#include "acm-os.h" ++ ++ ++ ++/* ++ * CDC ACM Configuration ++ * ++ * Endpoint, Class, Interface, Configuration and Device descriptors/descriptions ++ */ ++ ++/*! BULK OUT Data Endpoint Descriptor ++ */ ++static u8 tty_alt_1[] = { 0x07, USB_DT_ENDPOINT, USB_DIR_OUT, BULK, 0, 0x00, 0x00, }; ++ ++/*! BULK In Data Endpoint Descriptor ++ */ ++static u8 tty_alt_2[] = { 0x07, USB_DT_ENDPOINT, USB_DIR_IN, BULK, 0, 0x00, 0x00, }; ++ ++/*! Data Endpoint Descriptor List ++ */ ++static struct usbd_endpoint_descriptor *tty_alt_endpoints[] = { ++ (struct usbd_endpoint_descriptor *) tty_alt_1, ++ (struct usbd_endpoint_descriptor *) tty_alt_2, }; ++ ++/*! Data Endpoint Index List ++ */ ++u8 tty_alt_indexes[] = { BULK_OUT, BULK_IN, }; ++ ++/*! INTERRUPT In Comm Endpoint Descriptor ++ */ ++static u8 tty_comm_1[] = { 0x07, USB_DT_ENDPOINT, USB_DIR_IN, INTERRUPT, 0, 0x00, 0x0a, }; ++ ++/*! Comm Endpoint Descriptor List ++ */ ++static struct usbd_endpoint_descriptor *tty_comm_endpoints[] = { (struct usbd_endpoint_descriptor *) tty_comm_1 }; ++ ++ ++/*! Comm Endpoint Index List ++ */ ++u8 tty_comm_indexes[] = { INT_IN, }; ++ ++/*! @{ ++ * Class Descriptors ++ */ ++static u8 cdc_class_1[] = { 0x05, CS_INTERFACE, USB_ST_HEADER, 0x01, 0x01, /* CLASS_BDC_VERSION, CLASS_BDC_VERSION */ }; ++static u8 cdc_class_2[] = { 0x05, CS_INTERFACE, USB_ST_CMF, 0x03, 0x01, /* bMasterInterface: 0, bSlaveInterface: 1 */ }; ++static u8 cdc_class_3[] = { 0x05, CS_INTERFACE, USB_ST_UF, 0x00, 0x01, /* bMasterInterface: 0, bSlaveInterface: 1 */ }; ++/*! @} */ ++ ++/*! ACMF Class Descriptor ++ * ACMF - c.f. Table 28 ++ * currenty set to 0x2 - Support Set_Line_Coding etc, ++ * ++ * XXX Should we also set 0x4 - Supports Network_Notification? ++ */ ++static u8 cdc_class_4[] = { 0x04, CS_INTERFACE, USB_ST_ACMF, 0x02, }; ++ ++static struct usbd_generic_class_descriptor *cdc_comm_class_descriptors[] = ++ { (struct usbd_generic_class_descriptor *) cdc_class_1, ++ (struct usbd_generic_class_descriptor *) cdc_class_2, ++ (struct usbd_generic_class_descriptor *) cdc_class_3, ++ (struct usbd_generic_class_descriptor *) cdc_class_4, }; ++ ++ ++ ++/* Alternate Descriptors */ ++// First two bytes are identical in all: bLength, bDescriptorType (0x09 0x04) ++ ++/*! Comm Alternate Descriptor ++ */ ++static u8 cdc_comm_alternate_descriptor[sizeof(struct usbd_interface_descriptor)] = { ++ 0x09, USB_DT_INTERFACE, COMM_INTF, 0x00, 0x00, // bInterfaceNumber, bAlternateSetting, bNumEndpoints ++ COMMUNICATIONS_INTERFACE_CLASS, COMMUNICATIONS_ACM_SUBCLASS, 0x01, 0x00, }; ++ ++/*! Data Alternate Descriptor ++ */ ++static u8 cdc_data_alternate_descriptor[sizeof(struct usbd_interface_descriptor)] = { ++ 0x09, USB_DT_INTERFACE, DATA_INTF, 0x00, 0x00, // bInterfaceNumber, bAlternateSetting, bNumEndpoints ++ DATA_INTERFACE_CLASS, COMMUNICATIONS_NO_SUBCLASS, COMMUNICATIONS_NO_PROTOCOL, 0x00, }; ++ ++ ++ ++/*! Comm alternate descriptions ++ */ ++static struct usbd_alternate_description cdc_comm_alternate_descriptions[] = { ++ { iInterface: CONFIG_OTG_ACM_COMM_INTF, ++ interface_descriptor: (struct usbd_interface_descriptor *)&cdc_comm_alternate_descriptor, ++ classes:sizeof (cdc_comm_class_descriptors) / sizeof (struct usbd_generic_class_descriptor *), ++ class_list: cdc_comm_class_descriptors, ++ endpoint_list: tty_comm_endpoints, ++ endpoints:sizeof (tty_comm_endpoints) / sizeof(struct usbd_endpoint_descriptor *), ++ endpoint_indexes: tty_comm_indexes, ++ }, }; ++ ++/*! Data alternate descriptions ++ */ ++static struct usbd_alternate_description cdc_data_alternate_descriptions[] = { ++ { iInterface: CONFIG_OTG_ACM_DATA_INTF, ++ interface_descriptor: (struct usbd_interface_descriptor *)&cdc_data_alternate_descriptor, ++ endpoint_list: tty_alt_endpoints, ++ endpoints:sizeof (tty_alt_endpoints) / sizeof(struct usbd_endpoint_descriptor *), ++ endpoint_indexes: tty_alt_indexes, ++ }, }; ++ ++ ++/*! Interface Descriptions List */ ++static struct usbd_interface_description cdc_interfaces[] = { ++ { ++ alternates:sizeof (cdc_comm_alternate_descriptions) / sizeof (struct usbd_alternate_description), ++ alternate_list:cdc_comm_alternate_descriptions, ++ }, ++ ++ { ++ alternates:sizeof (cdc_data_alternate_descriptions) / sizeof (struct usbd_alternate_description), ++ alternate_list:cdc_data_alternate_descriptions, ++ }, ++ ++}; ++ ++ ++/*! Configuration Descriptor ++ */ ++static u8 cdc_configuration_descriptor[sizeof(struct usbd_configuration_descriptor)] = { ++ 0x09, USB_DT_CONFIGURATION, 0x00, 0x00, sizeof (cdc_interfaces) / sizeof (struct usbd_interface_description), ++ 0x01, 0x00, BMATTRIBUTE, BMAXPOWER, }; ++ ++/*! Configuration Description ++ */ ++struct usbd_configuration_description tty_description[] = { ++ { iConfiguration: CONFIG_OTG_ACM_DESC, ++ configuration_descriptor: (struct usbd_configuration_descriptor *)cdc_configuration_descriptor, ++ }, }; ++ ++/*! Device Descriptor ++ */ ++static struct usbd_device_descriptor tty_device_descriptor = { ++ bLength: sizeof(struct usbd_device_descriptor), ++ bDescriptorType: USB_DT_DEVICE, ++ bcdUSB: __constant_cpu_to_le16(USB_BCD_VERSION), ++ bDeviceClass: COMMUNICATIONS_DEVICE_CLASS, ++ bDeviceSubClass: 0x02, ++ bDeviceProtocol: 0x00, ++ bMaxPacketSize0: 0x00, ++ idVendor: __constant_cpu_to_le16(CONFIG_OTG_ACM_VENDORID), ++ idProduct: __constant_cpu_to_le16(CONFIG_OTG_ACM_PRODUCTID), ++ bcdDevice: __constant_cpu_to_le16(CONFIG_OTG_ACM_BCDDEVICE), ++}; ++ ++#ifdef CONFIG_OTG_HIGH_SPEED ++/*! High Speed Device Qualifier Descriptor ++ */ ++static struct usbd_device_qualifier_descriptor tty_device_qualifier_descriptor = { ++ bLength: sizeof(struct usbd_device_qualifier_descriptor), ++ bDescriptorType: USB_DT_DEVICE_QUALIFIER, ++ bcdUSB: __constant_cpu_to_le16(USB_BCD_VERSION), ++ bDeviceClass: COMMUNICATIONS_DEVICE_CLASS, ++ bDeviceSubClass: 0x02, ++ bDeviceProtocol: 0x00, ++ bMaxPacketSize0: 0x00, ++}; ++#endif /* CONFIG_OTG_HIGH_SPEED */ ++ ++ ++/*! Endpoint Request List ++ */ ++static struct usbd_endpoint_request tty_endpoint_requests[ENDPOINTS+1] = { ++ { 1, 1, 0, USB_DIR_OUT | USB_ENDPOINT_BULK, 64, 512, }, ++ { 1, 1, 0, USB_DIR_IN | USB_ENDPOINT_BULK, 64 /* * 4 */, 512, }, ++ { 1, 0, 0, USB_DIR_IN | USB_ENDPOINT_INTERRUPT, 16, 64, }, ++ { 0, }, ++}; ++ ++/*! OTG Descriptor ++ */ ++static struct usbd_otg_descriptor tty_otg_descriptor = { ++ bLength : sizeof(struct usbd_otg_descriptor), ++ bDescriptorType: USB_DT_OTG, ++ bmAttributes: 0, ++}; ++ ++/*! Device Description ++ */ ++struct usbd_device_description tty_device_description = { ++ device_descriptor: &tty_device_descriptor, ++#ifdef CONFIG_OTG_HIGH_SPEED ++ device_qualifier_descriptor: &tty_device_qualifier_descriptor, ++#endif /* CONFIG_OTG_HIGH_SPEED */ ++ otg_descriptor: &tty_otg_descriptor, ++ iManufacturer: CONFIG_OTG_ACM_MANUFACTURER, ++ iProduct: CONFIG_OTG_ACM_PRODUCT_NAME, ++#if !defined(CONFIG_OTG_NO_SERIAL_NUMBER) && defined(CONFIG_OTG_SERIAL_NUMBER_STR) ++ iSerialNumber: CONFIG_OTG_SERIAL_NUMBER_STR, ++#endif ++}; ++ ++/*! function_driver ++ */ ++struct usbd_function_driver tty_function_driver = { ++ name: "ACM-TTY", ++ fops:&function_ops, ++ device_description:&tty_device_description, ++ bNumConfigurations:sizeof (tty_description) / sizeof (struct usbd_configuration_description), ++ configuration_description:tty_description, ++ idVendor: __constant_cpu_to_le16(CONFIG_OTG_ACM_VENDORID), ++ idProduct: __constant_cpu_to_le16(CONFIG_OTG_ACM_PRODUCTID), ++ bcdDevice: __constant_cpu_to_le16(CONFIG_OTG_ACM_BCDDEVICE), ++ bNumInterfaces:sizeof (cdc_interfaces) / sizeof (struct usbd_interface_description), ++ interface_list:cdc_interfaces, ++ endpointsRequested: ENDPOINTS, ++ requestedEndpoints: tty_endpoint_requests, ++}; ++ ++ ++ +diff -uNr linux/drivers/no-otg/functions/acm/tty-l24-os.c linux/drivers/otg/functions/acm/tty-l24-os.c +--- linux/drivers/no-otg/functions/acm/tty-l24-os.c 1970-01-01 01:00:00.000000000 +0100 ++++ linux/drivers/otg/functions/acm/tty-l24-os.c 2006-09-01 21:41:26.000000000 +0200 +@@ -0,0 +1,1291 @@ ++/* ++ * otg/functions/acm/tty-l24-os.c ++ * ++ * Copyright (c) 2003, 2004 Belcarra ++ * ++ * By: ++ * Tom Rushworth <tbr@belcarra.com>, ++ * Stuart Lynne <sl@belcarra.com>, ++ * Bruce Balden <balden@belcarra.com> ++ * ++ */ ++ ++/*! ++ * @file otg/functions/acm/tty-l24-os.c ++ * @brief ACM Function Driver private defines ++ * ++ * An ACM (Abstract Control Model) driver is composed of two pieces: ++ * ++ * 1) An OS specific piece that handles creating and operating ++ * a serial device for the given OS. ++ * ++ * 2) A USB specific piece that interfaces either with the host ++ * usbcore layer, or with the otgcore layer. ++ * ++ * If the USB piece interfaces with the host usbcore layer you get an ACM ++ * class driver. If the USB piece interfaces with the otgcore layer you get ++ * an ACM function driver. ++ * ++ * This file is the OS specific piece that interfaces with Linux (kernel ++ * versions 2.4.*). It talks to the bottom edge of the Linux tty layer. ++ * ++ * See the file acm/tty.c for USB descriptor definitions used for this ++ * function. ++ * ++ * @ingroup TTYFunction ++ */ ++ ++ ++//#include <otg/osversion.h> ++#include <otg/otg-compat.h> ++#include <otg/otg-module.h> ++ ++MOD_AUTHOR ("tbr@belcarra.com, sl@belcarra.com"); ++ ++MOD_DESCRIPTION ("Belcarra TTY Function"); ++EMBED_LICENSE(); ++ ++#include <linux/init.h> ++#include <asm/uaccess.h> ++#include <linux/ctype.h> ++#include <linux/timer.h> ++#include <linux/interrupt.h> ++#include <asm/atomic.h> ++#include <linux/tty.h> ++#include <linux/tty_driver.h> ++#include <linux/tty_flip.h> ++#include <linux/smp_lock.h> ++#include <linux/slab.h> ++#include <linux/serial.h> ++ ++#include <otg/usbp-chap9.h> ++#include <otg/usbp-func.h> ++ ++#include <linux/capability.h> ++#include "tty.h" ++#include <otg/otg-trace.h> ++#include "acm.h" ++#include "acm-fd.h" ++#include "acm-os.h" ++ ++static struct acm_usb_services *usb_ops = &acm_fd_usb_ops; ++ ++#define TTY tty_fd_trace_tag ++otg_tag_t tty_fd_trace_tag; ++ ++/* Module Parameters ************************************************************************* */ ++ ++static u32 vendor_id; ++static u32 product_id; ++static u32 max_queued_urbs = MAX_QUEUED_URBS; ++static u32 max_queued_bytes = MAX_QUEUED_BYTES; ++ ++MOD_PARM (vendor_id, "i"); ++MOD_PARM (product_id, "i"); ++MOD_PARM (max_queued_urbs, "i"); ++MOD_PARM (max_queued_bytes, "i"); ++ ++MOD_PARM_DESC (vendor_id, "Device Vendor ID"); ++MOD_PARM_DESC (product_id, "Device Product ID"); ++MOD_PARM_DESC (max_queued_urbs, "Maximum TX Queued Urbs"); ++MOD_PARM_DESC (max_queued_bytes, "Maximum TX Queued Bytes"); ++ ++ ++/* ACM ***************************************************************************************** */ ++ ++#define ACM_TTY_MAJOR 166 ++#define ACM_TTY_MINOR 0 ++#define ACM_TTY_MINORS 1 ++ ++extern struct acm_private acmfd_private; ++extern struct tty_private ttyfd_private; ++ ++ ++/* ******************************************************************************************* */ ++ ++#define TTY_OVERFLOW_SIZE 512 ++ ++u8 tty_overflow_buffer[TTY_OVERFLOW_SIZE]; ++int tty_overflow_used; ++ ++/* ******************************************************************************************* */ ++ ++/*! ttyfd_schedule ++ * ++ * Schedule a bottom half handler work item. ++ * ++ * @param queue ++ */ ++STATIC void ttyfd_schedule(WORK_ITEM *queue) ++{ ++ RETURN_IF(NO_WORK_DATA((*queue)) || PENDING_WORK_ITEM((*queue))); ++#if defined(LINUX24) ++ queue_task(queue, &tq_immediate); ++ mark_bh(IMMEDIATE_BH); ++ TRACE_MSG1(TTY,"task %p scheduled and marked",queue); ++#else ++ SCHEDULE_WORK((*queue)); ++ TRACE_MSG1(TTY,"task %p scheduled",queue); ++#endif ++} ++ ++ ++/*! ttyfd_block_until_ready ++ * ++ * Called from tty_open to implement blocking open. Wait for DTR indication ++ * from acm-fd. ++ * ++ * Called by TTY layer to open device. ++ * ++ * @param tty ++ * @param filp ++ * @param acm ++ * @returns non-zero for error ++ */ ++STATIC int ttyfd_block_until_ready(struct tty_struct *tty, struct file *filp, struct acm_private *acm) ++{ ++ struct tty_private *tty_private = (struct tty_private *) acm->privdata; ++ unsigned long flags; ++ int rc = 0; ++ ++ local_irq_save(flags); ++ //TRACE_MSG1(TTY,"open_wait_count: %d --> at entry", tty_private->open_wait_count); ++ //tty_private->open_wait_count += 1; ++ for (;;) { ++ /* check if the file has been closed */ ++ if (tty_hung_up_p(filp)) { ++ TRACE_MSG0(TTY,"tty_hung_up_p()"); ++ rc = -ERESTARTSYS; ++ break; ++ } ++ /* check for pending signals */ ++ if (signal_pending(current)) { ++ TRACE_MSG0(TTY,"signal_pending()"); ++ rc = -ERESTARTSYS; ++ break; ++ } ++ /* check if the module is unloading */ ++ if (tty_private->exiting) { ++ TRACE_MSG0(TTY,"module exiting()"); ++ rc = -ENODEV; ++ break; ++ } ++ /* check for what we want, do we have DTR? ++ */ ++ if (acm->bmLineState & CDC_LINESTATE_D0_DTR) { ++ // OK, there's somebody on the other end, let's go... ++ TRACE_MSG0(TTY,"found DTR"); ++ rc = 0; ++ break; ++ } ++ //TRACE_MSG1(TTY,"open_wait_count: %d sleeping...", tty_private->open_wait_count); ++ ++ /* restore irqs */ ++ local_irq_restore(flags); ++ ++ /* sleep */ ++ interruptible_sleep_on(&tty_private->open_wait); ++ ++ /* lock irqs again */ ++ local_irq_save(flags); ++ ++ //TRACE_MSG1(TTY,"open_wait_count: %d got WAKEUP", tty_private->open_wait_count); ++ } ++ //tty_private->open_wait_count -= 1; ++ //TRACE_MSG1(TTY,"open_wait_count: %d <-- at exit", tty_private->open_wait_count); ++ local_irq_restore(flags); ++ return(rc); ++} ++ ++/*! ttyfd_call_hangup ++ * ++ * Bottom half handler to safely send hangup signal to process group. ++ * ++ * @param private - point to acm_private dat structure ++ */ ++STATIC void ttyfd_call_hangup(void *private) ++{ ++ struct acm_private *acm = (struct acm_private *) private; ++ struct tty_private *tty_private = (struct tty_private *) acm->privdata; ++ ++ struct tty_struct *tty = (NULL == acm) ? NULL : tty_private->tty; ++ ++ TRACE_MSG5(acm->trace_tag,"used: %d MOD_IN_USE: %d configured: %d c_cflag: %04x clocal: %d", ++ atomic_read(&acm->used), MOD_IN_USE, acm->flags & ACM_CONFIGURED, ++ tty_private->c_cflag, tty_private->c_cflag & CLOCAL); ++ ++ ++ if (tty && !(tty_private->c_cflag & CLOCAL)) ++ tty_hangup(tty); ++ ++ wake_up_interruptible(&tty_private->open_wait); ++ ++ TRACE_MSG0(TTY,"exited"); ++} ++ ++/*! ttyfd_schedule_hangup ++ * ++ * Schedule hangup bottom half handler. ++ * ++ * @param acm - point to acm_private dat structure ++ */ ++void ttyfd_schedule_hangup(struct acm_private *acm) ++{ ++ struct tty_private *tty_private = (struct tty_private *) acm->privdata; ++ ++ TRACE_MSG0(TTY, "entered"); ++ ttyfd_schedule(&tty_private->hqueue); ++} ++ ++ ++/*! tty_open ++ * ++ * Called by TTY layer to open device. ++ * ++ * @param tty ++ * @param filp ++ * @returns non-zero for error ++ */ ++STATIC int tty_open(struct tty_struct *tty, struct file *filp) ++{ ++ struct acm_private *acm = &acmfd_private; ++ struct tty_private *tty_private = (struct tty_private *) acm->privdata; ++ int used; ++ int nonblocking; ++ int rc = 0; ++ unsigned long flags; ++ ++ TRACE_MSG3(acm->trace_tag,"used: %d MOD_IN_USE: %d configured: %d", ++ atomic_read(&acm->used), MOD_IN_USE, acm->flags & ACM_CONFIGURED); ++ ++ TRACE_MSG2(TTY,"f_flags: %08x NONBLOCK: %x", ++ filp->f_flags, filp->f_flags & O_NONBLOCK); ++ ++ nonblocking = filp->f_flags & O_NONBLOCK; ++ ++ /* Lock and increment used counter, save current value. ++ * Check for exclusive open at the same time. ++ */ ++ local_irq_save(flags); ++ ++ /* The value of acm->used controls MOD_{INC/DEC}_USE_COUNT, so ++ * it has to be incremented unconditionally, and no early return ++ * made until after USE_COUNT has been adjusted to match. ++ */ ++ used = atomic_post_inc(&acm->used); ++#ifdef MCEL ++ filp->f_flags |= O_EXCL; // QQQ Can we persuade MCEL to add this to their app? ++ if (nonblocking && !(acm->connected)) { ++ // QQQ Is MCEL actually using this "feature"? (See below for printk) ++ rc = -EINVAL; ++ } ++ else ++#endif ++#if 0 ++ if (filp->f_flags & O_EXCL) { ++ /* This is intended to be an exclusive open, so ++ * make sure no one has the device open already, and ++ * set the exclusive flag so no one can open it later. ++ */ ++ if (used > 0) { ++ // Someone already has it. ++ rc = -EBUSY; ++ } ++ else { ++ tty_private->flags |= TTYFD_EXCLUSIVE; ++ set_bit(TTY_EXCLUSIVE, &tty->flags); ++ } ++ } ++#if defined(LINUX24) ++ else if ((tty_private->flags & TTYFD_EXCLUSIVE) && !suser()) ++#else ++ if ((tty_private->flags & TTYFD_EXCLUSIVE) && !capable(CAP_SYS_TTY_CONFIG)) ++#endif ++ { ++ // Only the superuser can do a normal open of an O_EXCL tty ++ rc = -EBUSY; ++ } ++#endif ++ local_irq_restore(flags); ++ ++ // OK, now it's safe to make an early return if we are failing. ++ if (rc) { ++#ifdef MCEL ++ // This can dissappear when the "feature" above does. ++ if (-EINVAL == rc) ++ //printk(KERN_INFO "\nusb cable not connected!\n"); ++#endif ++ return(rc); ++ } ++ local_irq_save(flags); ++ ++ ++ /* To truly emulate the old dual-device approach of having a non-blocking ++ * device (e.g cu0) and a blocking device (e.g. tty0) we would need to ++ * track blocking and non-blocking opens separately. We don't. This ++ * may lead to funny behavior in the multiple open case. ++ */ ++ UNLESS (used) { ++ MOD_INC_USE_COUNT; ++ TRACE_MSG1(TTY,"INC: %d", MOD_IN_USE); ++ // First open. ++ TRACE_MSG2(TTY, "FIRST OPEN nonblocking: %x exclusive: %x", nonblocking, tty_private->flags & TTYFD_EXCLUSIVE); ++ tty->driver_data = acm; ++ tty_private->tty = tty; ++ tty->low_latency = 1; ++ usb_ops->open(acm, DATA_INTF); ++ usb_ops->set_local(acm, 1); ++ } ++ local_irq_restore(flags); ++ ++ ++ /* All done if configured ++ */ ++ RETURN_ZERO_UNLESS(usb_ops->ready(acm)); ++ ++ /* All done if non blocking open ++ */ ++ RETURN_ZERO_IF(nonblocking); ++ ++ /* Block open - wait until ready ++ */ ++ TRACE_MSG0(TTY, "BLOCKING - wait until ready"); ++ rc = ttyfd_block_until_ready(tty,filp,acm); ++ ++ /* The tty layer calls tty_close() even if this open fails, ++ * so any cleanup (rc != 0) will be done there. ++ */ ++ TRACE_MSG3(acm->trace_tag,"used: %d MOD_IN_USE: %d configured: %d", ++ atomic_read(&acm->used), MOD_IN_USE, acm->flags & ACM_CONFIGURED); ++ return(rc); ++} ++ ++/*! ttyfd_wakeup_opens ++ * ++ * Called by acm_fd to wakeup processes blocked in open waiting for DTR. ++ * ++ * @param acm - pointer to acm private data structure ++ */ ++void ttyfd_wakeup_opens(struct acm_private *acm) ++{ ++ struct tty_private *tty_private = (struct tty_private *) acm->privdata; ++ TRACE_MSG0(TTY, "entered"); ++ //if (tty_private->open_wait_count > 0) ++ wake_up_interruptible(&tty_private->open_wait); ++} ++ ++/*! tty_close ++ * ++ * Called by TTY layer to close device. ++ * ++ * @param tty ++ * @param filp ++ * @returns non-zero for error ++ */ ++STATIC void tty_close(struct tty_struct *tty, struct file *filp) ++{ ++ struct acm_private *acm = tty->driver_data; ++ struct tty_private *tty_private = (struct tty_private *) acm->privdata; ++ struct usbd_function_instance *function; ++ int used; ++ ++ TRACE_MSG0(TTY, "entered"); ++ ++ //UNLESS (acm) { ++ // printk(KERN_INFO"%s: ACM null\n", __FUNCTION__); ++ // return; ++ //} ++ ++ usb_ops->close(acm, DATA_INTF); ++ tty_private = (struct tty_private *) acm->privdata; ++ function = acm->function; ++ TRACE_MSG3(acm->trace_tag,"used: %d MOD_IN_USE: %d configured: %d", ++ atomic_read(&acm->used), MOD_IN_USE, acm->flags & ACM_CONFIGURED); ++ ++ // lock and decrement used counter, save result ++ used = atomic_pre_dec(&acm->used); ++ ++ // finished unless this is the last close ++ ++ RETURN_IF (used > 0); ++ ++ TRACE_MSG0(TTY,"FINAL CLOSE"); ++ ++ tty_private->tty = NULL; ++ ++ /* This should never happen if this is the last close, ++ * but it can't hurt to check. ++ */ ++ //if (tty_private->open_wait_count) ++ wake_up_interruptible(&tty_private->open_wait); ++ ++ if (tty_private->flags & TTYFD_EXCLUSIVE) { ++ tty_private->flags &= ~TTYFD_EXCLUSIVE; ++ if (tty) ++ clear_bit(TTYFD_EXCLUSIVE, &tty->flags); ++ } ++ ++ _MOD_DEC_USE_COUNT; ++ TRACE_MSG1(TTY,"DEC: %d", MOD_IN_USE); ++ TRACE_MSG1(TTY,"LAST CLOSE r-f=%d",(acm->bytes_received-acm->bytes_forwarded)); ++ ++ MOD_DEC_USE_COUNT; ++ ++ TRACE_MSG0(TTY,"FINISHED"); ++} ++ ++/* Transmit Function - called by tty layer ****************************************************** */ ++ ++int ttyfd_recv_space_available(struct acm_private *acm, int interface); ++int ttyfd_recv_chars(struct acm_private *acm, int interface, u8 *cp, int n); ++ ++#define LOOP_BUF 16 ++/*! ttyfd_loop_xmit_chars ++ * ++ * Implement data loop back, send xmit data back as received data. ++ * ++ * @param acm - pointer to acm private data structure ++ * @param interface - data interface to send on ++ * @param count - number of bytes to send ++ * @param from_user - true if from user memory space ++ * @param buf - pointer to data to send ++ * @return count - number of bytes actually sent. ++ */ ++STATIC int ttyfd_loop_xmit_chars(struct acm_private *acm, int interface, int count, int from_user, const unsigned char *buf) ++{ ++ int received = 0; ++ while (count > 0) { ++ u8 copybuf[LOOP_BUF]; ++ int i; ++ int space = ttyfd_recv_space_available(acm, DATA_INTF); ++ int recv = MIN (space, LOOP_BUF); ++ recv = MIN(recv, count); ++ ++ //TRACE_MSG7(TTY, "buf: %8x buf+received: %8x received: %d from_user: %d count: %d space: %d recv: %d\n", ++ // buf, buf + received, received, from_user, count, space, recv); ++ ++ BREAK_UNLESS(recv); ++ ++ if (from_user) ++ copy_from_user ((void *)copybuf, (void*)(buf + received), recv); ++ else ++ memcpy ((void *)copybuf, (void*)(buf + received), recv); ++ ++ count -= recv; ++ received += recv; ++ TRACE_MSG3(TTY, "count: %d recv: %d received: %d", count, recv, received); ++ ttyfd_recv_chars(acm, DATA_INTF, copybuf, recv); ++ } ++ return received; ++} ++ ++/*! ttyfd_overflow_send ++ * ++ * Check if there is data in overflow buffer and send if neccessary. ++ * ++ * @param acm - pointer to acm private data structure ++ * @param interface - data interface to send on ++ * @param force - force the send ++ */ ++void ttyfd_overflow_send(struct acm_private *acm, int interface, int force) ++{ ++ int chars_in_buffer; ++ int count; ++ ++ unsigned long flags; ++ local_irq_save(flags); ++ ++ chars_in_buffer = usb_ops->chars_in_buffer(acm, DATA_INTF); ++ ++ TRACE_MSG3(TTY, "overflow_used: %d chars_in_buffer: %d force: %d", tty_overflow_used, chars_in_buffer, force); ++ ++ if (force || !chars_in_buffer || tty_overflow_used > 200) { ++ ++ count = usb_ops->xmit_chars(acm, DATA_INTF, tty_overflow_used, 0, tty_overflow_buffer); ++ ++ TRACE_MSG2(TTY, "overflow_used: %d count: %d", tty_overflow_used, count); ++ ++ tty_overflow_used = 0; ++ ++ } ++ local_irq_restore(flags); ++} ++ ++ ++/*! ttyfd_overflow_xmit_chars ++ * ++ * Implement overflow buffer. The TTY layer implements echo with single ++ * character writes which can quickly exhaust the urbs allowed for sending. ++ * This results in data being lost. ++ * ++ * This function is called to accumulate small amounts of data when there is ++ * already an bulk in urb in the queue. ++ * ++ * The urb sent function calls wakeup writers which will call ++ * ttyfd_overflow_send which will send the data. ++ * ++ * @param acm - pointer to acm private data structure ++ * @param interface - data interface to send on ++ * @param count - number of bytes to send ++ * @param from_user - true if from user memory space ++ * @param buf - pointer to data to send ++ * @return count - number of bytes actually sent. ++ */ ++STATIC int ttyfd_overflow_xmit_chars(struct acm_private *acm, int interface, int count, int from_user, const unsigned char *buf) ++{ ++ unsigned long flags; ++ int overflow_room; ++ int write_room = usb_ops->write_room(acm, DATA_INTF); ++ ++ TRACE_MSG2(TTY,"-> count: %d from_usr: %d", count, from_user); ++ local_irq_save(flags); ++ ++ if ((overflow_room = TTY_OVERFLOW_SIZE - tty_overflow_used) > 0) { ++ ++ count = MIN(overflow_room, count); ++ if (from_user) ++ copy_from_user ((void *)tty_overflow_buffer + tty_overflow_used, (void*)buf, count); ++ else ++ memcpy ((void *)tty_overflow_buffer + tty_overflow_used, (void*)buf, count); ++ ++ tty_overflow_used += count; ++ } ++ else ++ count = 0; ++ ++ local_irq_restore(flags); ++ ++ /* start sending from overflow if neccessary ++ */ ++ ttyfd_overflow_send(acm, interface, 0); ++ ++ return count; ++} ++ ++/*! tty_chars_in_buffer ++ * ++ * Called by TTY layer to determine amount of pending write data. ++ * ++ * @param tty - pointer to tty data structure ++ * @return amount of pending write data ++ */ ++STATIC int tty_chars_in_buffer(struct tty_struct *tty) ++{ ++ struct acm_private *acm = tty->driver_data; ++ struct tty_private *tty_private = (struct tty_private *) acm->privdata; ++ int rc; ++ ++ //TRACE_MSG0(TTY, "entered"); ++ ++ rc = usb_ops->chars_in_buffer(acm, DATA_INTF); ++ TRACE_MSG1(TTY, "chars in buffer: %d", rc); ++ return rc; ++ //return(usb_ops->chars_in_buffer(acm, DATA_INTF)); ++} ++ ++/*! tty_write ++ * ++ * Called by TTY layer to write data. ++ * @param tty - pointer to tty data structure ++ * @param from_user - true if from user memory space ++ * @param buf - pointer to data to send ++ * @param count - number of bytes want to send. ++ * @return count - number of bytes actually sent. ++ */ ++STATIC int tty_write(struct tty_struct *tty, int from_user, const unsigned char *buf, int count) ++{ ++ struct acm_private *acm = tty->driver_data; ++ struct tty_private *tty_private = (struct tty_private *) acm->privdata; ++ ++ TRACE_MSG3(TTY,"-> count: %d loopback: %d from_usr: %d", count, tty_private->tiocm & TIOCM_LOOP, from_user); ++ ++ /* loopback mode ++ */ ++ if (tty_private->tiocm & TIOCM_LOOP) ++ return ttyfd_loop_xmit_chars(acm, DATA_INTF, count, from_user, buf); ++ ++ /* overflow mode ++ */ ++ if (tty_overflow_used || (tty_chars_in_buffer(tty_private->tty) && (count < 64))) ++ return ttyfd_overflow_xmit_chars(acm, DATA_INTF, count, from_user, buf); ++ ++ /* straight through mode ++ */ ++ return usb_ops->xmit_chars(acm, DATA_INTF, count, from_user, buf); ++ ++} ++ ++/*! tty_write_room ++ * ++ * Called by TTY layer get amount of write room available. ++ * ++ * @param tty - pointer to tty data structure ++ * @return amount of write room available ++ */ ++STATIC int tty_write_room(struct tty_struct *tty) ++{ ++ struct acm_private *acm = tty->driver_data; ++ struct tty_private *tty_private = (struct tty_private *) acm->privdata; ++ int rc; ++ ++ TRACE_MSG0(TTY, "entered"); ++ /* loopback mode ++ */ ++ if (tty_private->tiocm & TIOCM_LOOP) ++ return ttyfd_recv_space_available(acm, DATA_INTF); ++ ++ rc = usb_ops->write_room(acm, DATA_INTF) + (TTY_OVERFLOW_SIZE - tty_overflow_used); ++ TRACE_MSG1(TTY, "write room: %d", rc); ++ ++ return rc; ++ ++ ++ //return(usb_ops->write_room(acm, DATA_INTF)); ++} ++ ++static int throttle_count = 0; ++static int unthrottle_count = 0; ++ ++/*! tty_throttle ++ * ++ * Called by TTY layer to throttle (do not allow received data.) ++ * ++ * @return amount of write room available ++ */ ++STATIC void tty_throttle(struct tty_struct *tty) ++{ ++ struct acm_private *acm = tty->driver_data; ++ struct tty_private *tty_private = (struct tty_private *) acm->privdata; ++ ++ TRACE_MSG0(TTY, "entered"); ++ //tty_private->flags |= TTYFD_THROTTLED; ++ usb_ops->throttle(acm, DATA_INTF); ++} ++ ++/*! tty_unthrottle ++ * ++ * Called by TTY layer to unthrottle (allow received data.) ++ * ++ * @return amount of write room available ++ */ ++STATIC void tty_unthrottle(struct tty_struct *tty) ++{ ++ struct acm_private *acm = tty->driver_data; ++ struct tty_private *tty_private = (struct tty_private *) acm->privdata; ++ ++ TRACE_MSG0(TTY, "entered"); ++ //tty_private->flags &= ~TTYFD_THROTTLED; ++ usb_ops->unthrottle(acm, DATA_INTF); ++ ++ /* This function is called while the TTY_DONT_FLIP flag is still ++ * set, so there is no point trying to push the flip buffer. Just ++ * try to queue some recv urbs, and keep trying until we do manage ++ * to get some queued. ++ */ ++ tty_schedule_flip(tty); ++} ++ ++/* ********************************************************************************************** */ ++ ++/*! ttyfd_tiocm ++ * @param acm ++ * @param tiocm ++ * @return - computed tiocm value ++ */ ++unsigned int ttyfd_tiocm(struct acm_private *acm, unsigned int tiocm) ++{ ++ tiocm &= ~ ( TIOCM_DTR | TIOCM_DSR | TIOCM_CAR); ++ ++ return tiocm | ++ usb_ops->get_dtr(acm) ? TIOCM_DTR : 0 | ++ usb_ops->get_dsr(acm) ? TIOCM_DSR : 0 | ++ usb_ops->get_dcd(acm) ? TIOCM_CAR : 0 ; ++} ++ ++/*! tty_ioctl ++ * ++ * Used by TTY layer to execute IOCTL command. ++ * ++ * Called by TTY layer to open device. ++ * ++ * Unhandled commands must return -ENOIOCTLCMD for correct operation. ++ * ++ * @param tty ++ * @param file ++ * @param cmd ++ * @param arg ++ * @returns non-zero for error ++ */ ++STATIC int acm_tty_ioctl(struct tty_struct *tty, struct file *file, unsigned int cmd, unsigned long arg) ++{ ++ struct acm_private *acm = tty->driver_data; ++ struct tty_private *tty_private = (struct tty_private *) acm->privdata; ++ unsigned int mask; ++ unsigned int newctrl; ++ unsigned int tiocm; ++ unsigned int saved_tiocm; ++ unsigned int changed_tiocm; ++ int rc; ++ ++ //TRACE_MSG3(TTY,"entered: %8x dir: %x size: %x", cmd, _IOC_DIR(cmd), _IOC_SIZE(cmd)); ++ ++ switch(cmd) { ++ ++ case TIOCMGET: ++ TRACE_MSG1(TTY, "TIOCMGET: tiocm: %08x", tiocm); ++ tty_private->tiocm = ttyfd_tiocm(acm, tiocm); ++ RETURN_EINVAL_IF (copy_to_user((void *)arg, &tty_private->tiocm, sizeof(int))); ++ return 0; ++ ++ case TIOCMBIS: ++ case TIOCMBIC: ++ case TIOCMSET: ++ TRACE_MSG0(TTY, "TIOCMXXX: copying tiocm arguement"); ++ ++ TRACE_MSG1(TTY, "access: %d", access_ok(VERIFY_READ, (void *)arg, sizeof(int))); ++ RETURN_EINVAL_UNLESS(access_ok(VERIFY_READ, (void *)arg, sizeof(int))); ++ ++ ++ //RETURN_EINVAL_IF (copy_from_user(&tiocm, (void *)arg, sizeof(int))); ++ rc = copy_from_user((void *)&tiocm, (void *)arg, sizeof(int)); ++ TRACE_MSG2(TTY, "copy_from_user: %d tiocm: %08x", rc, tiocm); ++ ++ TRACE_MSG2(TTY, "tiocm: %08x mask: %08x", tiocm, ++ TIOCM_RTS | TIOCM_DTR | TIOCM_OUT1 | TIOCM_OUT2 | TIOCM_LOOP); ++ ++ tiocm &= TIOCM_RTS | TIOCM_DTR | TIOCM_OUT1 | TIOCM_OUT2 | TIOCM_LOOP; ++ saved_tiocm = tty_private->tiocm; ++ ++ switch (cmd) { ++ case TIOCMBIS: ++ TRACE_MSG1(TTY, "TIOCMBIS: tiocm: %08x", tiocm); ++ tty_private->tiocm |= tiocm; /* turn on flags set in tiocm */ ++ break; ++ case TIOCMBIC: ++ TRACE_MSG1(TTY, "TIOCMBIC: tiocm: %08x", tiocm); ++ tty_private->tiocm &= ~tiocm; /* turn off flags set in tiocm */ ++ break; ++ case TIOCMSET: ++ TRACE_MSG1(TTY, "TIOCMSET: tiocm: %08x", tiocm); ++ tty_private->tiocm = tiocm; /* set all flags as in tiocm */ ++ break; ++ } ++ /* make changes ++ */ ++ changed_tiocm = saved_tiocm ^ tty_private->tiocm; ++ TRACE_MSG4(TTY, "TIOCMSET: tiocm: %08x saved: %08x set: %08x changed: %d", ++ tiocm, saved_tiocm, tty_private->tiocm, changed_tiocm); ++ ++ if (changed_tiocm) { ++ /* DTR -> (DSR/DCD) */ ++ tiocm = tty_private->tiocm; ++ if (changed_tiocm & TIOCM_DTR) { ++ usb_ops->set_dsr(acm, tiocm & TIOCM_DTR); ++ usb_ops->set_dcd(acm, tiocm & TIOCM_DTR); ++ } ++ /* OUT1 -> Ring */ ++ if (changed_tiocm & TIOCM_OUT1) ++ usb_ops->ring(acm); ++ /* OUT2 -> Overrun */ ++ if (changed_tiocm & TIOCM_OUT2) ++ usb_ops->overrun(acm); ++ /* LOOPBACK */ ++ if (changed_tiocm & TIOCM_OUT2) ++ usb_ops->set_loopback(acm, tiocm & TIOCM_LOOP); ++ } ++ ++ return 0; ++ ++ case TIOCGSERIAL: ++ TRACE_MSG0(TTY, "TIOCGSERIAL"); ++ RETURN_EINVAL_IF (copy_to_user((void *)arg, &tty_private->serial_struct, sizeof(struct serial_struct))); ++ return 0; ++ ++ case TIOCSSERIAL: ++ TRACE_MSG0(TTY, "TIOCSSERIAL"); ++ RETURN_EFAULT_IF (copy_from_user(&tty_private->serial_struct, (void *)arg, sizeof(struct serial_struct))); ++ return 0; ++ ++ case TIOCSERCONFIG: ++ case TIOCSERGETLSR: /* Get line status register */ ++ case TIOCSERGSTRUCT: ++ TRACE_MSG0(TTY, "TIOCSER*"); ++ return -EINVAL; ++ ++ /* ++ * Wait for any of the 4 modem inputs (DCD,RI,DSR,CTS) to change ++ * - mask passed in arg for lines of interest ++ * (use |'ed TIOCM_RNG/DSR/CD/CTS for masking) ++ * Caller should use TIOCGICOUNT to see which one it was ++ */ ++ case TIOCMIWAIT: ++ TRACE_MSG0(TTY, "TIOCGMIWAIT"); ++ while (1) { ++ ++ saved_tiocm = tty_private->tiocm; ++ ++ interruptible_sleep_on(&tty_private->tiocm_wait); ++ ++ /* see if a signal did it */ ++ if (signal_pending(current)) ++ return -ERESTARTSYS; ++ ++ tty_private->tiocm = ttyfd_tiocm(acm, tiocm); ++ changed_tiocm = saved_tiocm ^ tty_private->tiocm; ++ RETURN_ZERO_IF ( (changed_tiocm | TIOCM_CAR) | (changed_tiocm | TIOCM_DSR) ); ++ /* loop */ ++ } ++ /* NOTREACHED */ ++ ++ /* ++ * Get counter of input serial line interrupts (DCD,RI,DSR,CTS) ++ * Return: write counters to the user passed counter struct ++ * NB: both 1->0 and 0->1 transitions are counted except for ++ * RI where only 0->1 is counted. ++ */ ++ case TIOCGICOUNT: ++ TRACE_MSG0(TTY, "TIOCGICOUNT"); ++ if (copy_to_user((void *)arg, &tty_private->tiocgicount, sizeof(int))) ++ return -EFAULT; ++ return 0; ++ ++ case TCSETS: ++ TRACE_MSG1(TTY, "TCSETS: tiocm: %08x", tiocm); ++ return -ENOIOCTLCMD; ++ case TCFLSH: ++ TRACE_MSG1(TTY, "TCFLSH: tiocm: %08x", tiocm); ++ return -ENOIOCTLCMD; ++ ++ case TCGETS: ++ TRACE_MSG1(TTY, "TCGETS: tiocm: %08x", tiocm); ++ return -ENOIOCTLCMD; ++ ++ default: ++ TRACE_MSG1(TTY, "unknown cmd: %08x", cmd); ++ return -ENOIOCTLCMD; ++ } ++ return -ENOIOCTLCMD; ++} ++ ++/*! ttyfd_wakeup_state ++ * ++ * Called by acm_fd to wakeup processes blocked waiting for state change ++ * ++ * @param acm - pointer to acm private data structure ++ */ ++void ttyfd_wakeup_state(struct acm_private *acm) ++{ ++ struct tty_private *tty_private = (struct tty_private *) acm->privdata; ++ TRACE_MSG0(TTY, "entered"); ++ wake_up_interruptible(&tty_private->tiocm_wait); ++} ++ ++ ++/*! tty_set_termios ++ * ++ * Used by TTY layer to set termios structure according to current status. ++ * ++ * @param tty - pointer to acm private data structure ++ * @param termios_old - termios structure ++ */ ++STATIC void tty_set_termios(struct tty_struct *tty, struct termios *termios_old) ++{ ++ struct acm_private *acm = tty->driver_data; ++ struct tty_private *tty_private = (struct tty_private *) acm->privdata; ++ ++ unsigned int c_cflag = tty->termios->c_cflag; ++ ++ /* see if CLOCAL has changed */ ++ if ((termios_old->c_cflag ^ tty->termios->c_cflag ) & CLOCAL) ++ usb_ops->set_local(acm, tty->termios->c_cflag & CLOCAL); ++ ++ /* save cflags ++ */ ++ tty_private->c_cflag = c_cflag; ++ ++ /* send break? ++ */ ++ if ((termios_old->c_cflag & CBAUD) && !(c_cflag & CBAUD)) ++ usb_ops->send_break(acm); ++} ++ ++/* ********************************************************************************************* */ ++/*! ttyfd_wakeup_writers ++ * ++ * Bottom half handler to wakeup pending writers. ++ * ++ * @param data - pointer to acm private data structure ++ */ ++STATIC void ttyfd_wakeup_writers(void *data) ++{ ++ struct acm_private *acm = (struct acm_private *) data; ++ struct tty_private *tty_private = (struct tty_private *) acm->privdata; ++ struct tty_struct *tty = tty_private->tty; ++ unsigned long flags; ++ ++ TRACE_MSG2(TTY,"used: %d MOD_IN_USE: %d", atomic_read(&acm->used), MOD_IN_USE); ++ ++ RETURN_UNLESS(usb_ops->ready(acm)); ++ RETURN_UNLESS(atomic_read(&acm->used)); ++ RETURN_UNLESS(tty); ++ ++ /* start sending from overflow buffer if necessary ++ */ ++ ttyfd_overflow_send(acm, DATA_INTF, 0); ++ ++ if ((tty->flags & (1 << TTY_DO_WRITE_WAKEUP)) && tty->ldisc.write_wakeup) ++ (tty->ldisc.write_wakeup)(tty); ++ ++ wake_up_interruptible(&tty->write_wait); ++} ++ ++/*! ttyfd_schedule_wakeup_writers ++ * ++ * Called by acm-fd to schedule a wakeup writes bottom half handler. ++ * ++ * @param acm - pointer to acm private data structure ++ */ ++void ttyfd_schedule_wakeup_writers(struct acm_private *acm) ++{ ++ struct tty_private *tty_private = (struct tty_private *) acm->privdata; ++ ++ TRACE_MSG2(TTY,"used: %d MOD_IN_USE: %d", atomic_read(&acm->used), MOD_IN_USE); ++ ttyfd_schedule(&tty_private->wqueue); ++} ++ ++/* ********************************************************************************************* */ ++ ++/*! ttyfd_recv_space_available ++ * ++ * Used by acm-fd to determine receive space available. This will determine ++ * how many receive urbs can be queued as there are never more receive urbs ++ * pending than there is currently room for data to be received. ++ * ++ * @param acm - pointer to acm private data structure ++ * @param interface - data interface to send on ++ * @return count - number of bytes available ++ */ ++int ttyfd_recv_space_available(struct acm_private *acm, int interface) ++{ ++ struct tty_private *tty_private; ++ struct tty_struct *tty; ++ int rc; ++ ++ TRACE_MSG0(TTY, "entered"); ++ RETURN_ZERO_UNLESS(acm); ++ ++ tty_private = (struct tty_private *) acm->privdata; ++ ++ RETURN_ZERO_UNLESS(tty_private); ++ ++ RETURN_ZERO_UNLESS(tty_private->tty); ++ ++ switch (interface) { ++ case COMM_INTF: ++ return 0; ++ case DATA_INTF: ++ tty = tty_private->tty; ++ rc = TTY_FLIPBUF_SIZE - tty->flip.count; ++ TRACE_MSG1(TTY, "recv space available: %d", rc); ++ return rc; ++ return(TTY_FLIPBUF_SIZE - tty->flip.count); ++ } ++ return 0; ++} ++ ++/*! ttyfd_recv_chars ++ * ++ * Called by acm-fd when data has been received. This will ++ * receive n bytes starting at cp. This will never be ++ * more than the last call to ttyfd_recv_space_available(). ++ * This will be called from interrupt context. ++ * ++ * @param acm - pointer to acm private data structure ++ * @param interface - data interface to send on ++ * @param n - number of bytes to send ++ * @param cp - pointer to data to send ++ * @return non-zero if error ++ */ ++int ttyfd_recv_chars(struct acm_private *acm, int interface, u8 *cp, int n) ++{ ++ struct tty_private *tty_private = (struct tty_private *) acm->privdata; ++ struct tty_struct *tty = tty_private->tty; ++ unsigned long flags; ++ ++ /* acm_start_recv_urbs() will never queue more urbs than there is currently ++ * room in the upper layer buffer for. So we are guaranteed that any data actually ++ * received can be given to the upper layers without worrying if we will ++ * actually have room. ++ * ++ * XXX I think that if the tty layer does get behind and we reach a point where ++ * there are no outstanding receive urbs, then we will also have been throttled ++ * so we will have an oppourtunity to queue more receive urbs when we get unthrottled. ++ * ++ * XXX If the above assumption is not true that a timer will be needed to periodically ++ * check if we can restart. ++ * ++ * YYY What can happen is that the tty_flip_buffer_push() call can fail for ++ * a variety of reasons (locked out while a read call is in progress, ldisc ++ * buffer is full, and probably others). We need to ensure that we keep ++ * trying to push the flip buffer until it does go, and there is room for ++ * more receive urbs. acm_start_recv_urbs() will queue a task to push and ++ * try again if it can't queue any now. ++ * ++ * ZZZ Yet another failure mode is to call tty_flip_buffer_push() too many ++ * times while throttled. This will result in the ldisc layer silently ++ * dropping data inside the n_tty_receive_buf() routine. (Acutal numbers ++ * for the 2.4.20 kernel this was discovered on are 128 bytes left in the ++ * ldisc buffer when throttle is called, and upto 7 64 byte urbs outstanding. ++ * The urbs will fit into the flip buffer, but NOT the ldisc buffer.) This ++ * means we must not call tty_flip_buffer_push() when throttled. We will ++ * count on the tty_unthrottle() call to kick off the final push. ++ */ ++ ++ { ++ int i; ++ local_irq_save(flags); ++ TRACE_MSG2(TTY, "recv: %d buffer: %x", n, cp); ++ ++ for (i = 0; i < n; i += 8) { ++ TRACE_MSG8(TTY, "%02x %02x %02x %02x %02x %02x %02x %02x", ++ cp[i + 0], cp[i + 1], cp[i + 2], cp[i + 3], ++ cp[i + 4], cp[i + 5], cp[i + 6], cp[i + 7] ++ ); ++ } ++ ++ local_irq_restore(flags); ++ } ++ switch (interface) { ++ case COMM_INTF: ++ return 0; ++ case DATA_INTF: ++ ++ RETURN_EINVAL_UNLESS(tty); ++ ++#define WORD_COPY 1 ++#if defined(WORD_COPY) ++ local_irq_save(flags); ++ memcpy(tty->flip.char_buf_ptr,cp,n); ++ memset(tty->flip.flag_buf_ptr,TTY_NORMAL,n); ++ tty->flip.count += n; ++ tty->flip.char_buf_ptr += n; ++ tty->flip.flag_buf_ptr += n; ++ acm->bytes_forwarded += n; ++ local_irq_restore(flags); ++#else ++ int i; ++ for (i = 0; i < n; i++) { ++ /* tty_flip_char() has no lock out from any calls ++ * to tty_flip_buffer_push() that may have been queued ++ * by acm_unthrottle() or acm_start_recv_urbs(), so... ++ */ ++ local_irq_save(flags); ++ tty_insert_flip_char(tty, *cp++, TTY_NORMAL); ++ local_irq_restore(flags); ++ } ++ acm->bytes_forwarded += n; ++#endif ++ ++ UNLESS (test_bit(TTY_THROTTLED, &tty->flags)) ++ tty_flip_buffer_push(tty); ++ ++ //UNLESS (tty_private->flags & TTYFD_THROTTLED) ++ // tty_flip_buffer_push(tty); ++ ++ return 0; ++ } ++ return 0; ++} ++ ++/* ********************************************************************************************* */ ++ ++/*! ttyfd_enable ++ * ++ * Called by acm-fd when the function driver is enabled. ++ */ ++void ttyfd_enable(struct usbd_function_instance *function) ++{ ++ //function->privdata = &acm_private; ++ //acm_private.function = function; ++} ++ ++/*! ttyfd_enable ++ * ++ * Called by acm-fd when the function driver is disabled. ++ */ ++void ttyfd_disable(struct usbd_function_instance *function) ++{ ++ //function->privdata = NULL; ++ //acm_private.function = NULL; ++} ++ ++/* ************************************************************************** */ ++ ++#if defined(LINUX24) ++static int tty_refcount; ++#else ++//Maintained inside tty_driver in LINUX 2.6 ++#endif ++static struct tty_struct *tty_table[ACM_TTY_MINORS]; ++static struct termios *tty_termios[ACM_TTY_MINORS]; ++static struct termios *tty_termios_locked[ACM_TTY_MINORS]; ++ ++/*! tty_driver ++ */ ++static struct tty_driver tty_driver = { ++ .magic = TTY_DRIVER_MAGIC, ++ .type = TTY_DRIVER_TYPE_SERIAL, ++ .subtype = SERIAL_TYPE_NORMAL, ++ .flags = TTY_DRIVER_REAL_RAW | TTY_DRIVER_NO_DEVFS, ++ .driver_name = "acm-CDC", ++ .name = "usb/acm/%d", ++ .major = ACM_TTY_MAJOR, ++ .num = ACM_TTY_MINORS, ++ .minor_start = 0, ++ ++ .open = tty_open, ++ .close = tty_close, ++ .write = tty_write, ++ .write_room = tty_write_room, ++ .ioctl = acm_tty_ioctl, ++ .throttle = tty_throttle, ++ .unthrottle = tty_unthrottle, ++ .chars_in_buffer = tty_chars_in_buffer, ++ .set_termios = tty_set_termios, ++ ++#if defined(LINUX24) ++ .refcount = &tty_refcount, ++#else ++ .refcount = 0, ++#endif ++ .table = tty_table, ++ .termios = tty_termios, ++ .termios_locked = tty_termios_locked, ++}; ++ ++/* USB Module init/exit ************************************************************************ */ ++ ++struct tty_private ttyfd_private = { ++ .tty_driver = &tty_driver, ++ //.wqueue.routine = ttyfd_wakeup_writers, ++ //.wqueue.data = &acmfd_private, ++ .wqueue = { ++ .routine = ttyfd_wakeup_writers, ++ .data = &acmfd_private, ++ }, ++ //.hqueue.routine = ttyfd_call_hangup, ++ //.hqueue.data = &acmfd_private, ++ .hqueue = { ++ .routine = ttyfd_call_hangup, ++ .data = &acmfd_private, ++ }, ++}; ++ ++ ++/*! tty_function_services ++ * ++ * This structure contains the list of services ++ */ ++struct acm_function_services tty_function_services = { ++ .schedule_wakeup_writers = ttyfd_schedule_wakeup_writers, ++ .recv_space_available = ttyfd_recv_space_available, ++ .recv_chars = ttyfd_recv_chars, ++ .enable = ttyfd_enable, ++ .disable = ttyfd_disable, ++ .schedule_hangup = ttyfd_schedule_hangup, ++ .wakeup_opens = ttyfd_wakeup_opens, ++ .wakeup_state = ttyfd_wakeup_state, ++}; ++ ++struct acm_private acmfd_private = { ++ .function_driver = &tty_function_driver, ++ .function_services = &tty_function_services, ++ .privdata = &ttyfd_private, ++ // TBR: 20040705 use ...le32() not le16, spotted by Zhao Liang ++ //.line_coding.dwDTERate = __constant_cpu_to_le32(0x1c200), // 115200 ++ //.line_coding.bDataBits = 0x08, ++ .line_coding = { ++ .dwDTERate = __constant_cpu_to_le32(0x1c200), // 115200 ++ .bDataBits = 0x08, ++ }, ++}; ++ ++ ++/*! ttyfd_modinit - module init ++ * ++ * This is called immediately after the module is loaded or during ++ * the kernel driver initialization if linked into the kernel. ++ * ++ */ ++STATIC int ttyfd_modinit (void) ++{ ++ int i; ++ ++ /* initialize private structures */ ++ acmfd_private.trace_tag = TTY = otg_trace_obtain_tag(); ++ init_waitqueue_head(&ttyfd_private.open_wait); ++ init_waitqueue_head(&ttyfd_private.tiocm_wait); ++ ++ /* update init_termios and register as tty driver */ ++ tty_driver.init_termios = tty_std_termios; ++ tty_driver.init_termios.c_cflag = B115200 | CS8 | CREAD | HUPCL | CLOCAL; ++ tty_driver.init_termios.c_lflag &= ~(ECHO | ICANON); ++ THROW_IF(tty_register_driver(&tty_driver), error); ++ tty_register_devfs(&tty_driver, 0, ACM_TTY_MINOR); ++ ttyfd_private.tty_driver_registered++; ++ ++ /* register as usb function driver via acm-fd */ ++ THROW_IF (usb_ops->fd_init(&acmfd_private, "acm_fd", vendor_id, product_id, max_queued_urbs,max_queued_bytes ), error); ++ ++ CATCH(error) { ++ printk(KERN_ERR"%s: ERROR\n", __FUNCTION__); ++ if (ttyfd_private.tty_driver_registered) { ++ tty_unregister_driver(&tty_driver); ++ ttyfd_private.tty_driver_registered = 0; ++ } ++ return -EINVAL; ++ } ++ return 0; ++} ++ ++ ++/*! ttyfd_modexit - module cleanup ++ * ++ * This is called prior to the module being unloaded. ++ */ ++STATIC void ttyfd_modexit (void) ++{ ++ struct acm_private *acm = &acmfd_private; ++ struct tty_private *tty_private = (struct tty_private *) acm->privdata; ++ unsigned long flags; ++ struct usbd_urb *urb; ++ ++ /* Wake up any pending opens after setting the exiting flag. */ ++ local_irq_save(flags); ++ ttyfd_private.exiting = 1; ++ //if (ttyfd_private.open_wait_count > 0) ++ wake_up_interruptible(&ttyfd_private.open_wait); ++ local_irq_restore(flags); ++ ++ /* verify no tasks are running */ ++ usb_ops->wait_task(acm, &acm->recv_tqueue); ++ usb_ops->wait_task(acm, &ttyfd_private.wqueue); ++ usb_ops->wait_task(acm, &ttyfd_private.hqueue); ++ ++#if defined(LINUX24) ++ run_task_queue(&tq_timer); ++#else ++ blk_run_queues(); ++#endif ++ /* de-register as tty and usb drivers */ ++ if (ttyfd_private.tty_driver_registered) ++ tty_unregister_driver(&tty_driver); ++ ++ /* de-register as function driver via acm-fd */ ++ usb_ops->fd_exit(&acmfd_private); ++ otg_trace_invalidate_tag(TTY); ++} ++ ++module_init (ttyfd_modinit); ++module_exit (ttyfd_modexit); ++ +diff -uNr linux/drivers/no-otg/functions/acm/tty-os.h linux/drivers/otg/functions/acm/tty-os.h +--- linux/drivers/no-otg/functions/acm/tty-os.h 1970-01-01 01:00:00.000000000 +0100 ++++ linux/drivers/otg/functions/acm/tty-os.h 2006-09-01 21:41:26.000000000 +0200 +@@ -0,0 +1,74 @@ ++/* ++ * otg/functions/acm/tty-os.h ++ * ++ * Copyright (c) 2003, 2004 Belcarra ++ * ++ * By: ++ * Tom Rushworth <tbr@belcarra.com>, ++ * Stuart Lynne <sl@belcarra.com>, ++ * Bruce Balden <balden@belcarra.com> ++ * ++ * ++ */ ++/*! ++ * @file otg/functions/acm/tty-os.h ++ * @brief ACM Function Driver private defines ++ * ++ * An ACM (Abstract Control Model) driver is composed of two pieces: ++ * 1) An OS specific piece that handles creating and operating ++ * a serial device for the given OS. ++ * 2) A USB specific piece that interfaces either with the host ++ * usbcore layer, or with the otgcore layer. ++ * ++ * If the USB piece interfaces with the host usbcore layer you get ++ * an ACM class driver. If the USB piece interfaces with the otgcore ++ * layer you get an ACM function driver. ++ * ++ * This file describes the functions exported by the various acm-*-os.c ++ * files (implementing (1)) for use in acm-fd.c (2). ++ * ++ * @ingroup TTYFunction ++ */ ++ ++#ifndef ACM_OS_H ++#define ACM_OS_H 1 ++ ++/* ++ * acm_os_recv_space_available - return the number of bytes of data ++ * the OS specific piece can accept without ++ * dropping some. ++ */ ++extern int acm_os_recv_space_available(struct acm_private *acm); ++ ++/* ++ * acm_os_recv_chars - receive n bytes starting at cp. This will never be ++ * more than the last call to acm_os_recv_space_available(). ++ * This will be called from interrupt context. ++ */ ++extern int acm_os_recv_chars(struct acm_private *acm, u8 *cp, int n); ++ ++/* ++ * acm_os_wakeup_writers - wakeup any blocked writers. This is called ++ * from interrupt context, so may need to queue ++ * the actual wakeup in a "bottom half". ++ */ ++extern void acm_os_wakeup_writers(struct acm_private *acm); ++ ++/* ++ * acm_os_hangup - send a hangup to any readers or writers. This can be ++ * called from interrupt context, so may need to queue ++ * the actual hangup in a "bottom half". ++ */ ++extern void acm_os_hangup(struct acm_private *acm); ++ ++/* ++ * acm_os_wakeup_opens - wakeup any blocked opens. This is called ++ * from interrupt context, so may need to queue ++ * the actual wakeup in a "bottom half". ++ */ ++extern void acm_os_wakeup_opens(struct acm_private *acm); ++ ++extern void acm_os_enable(struct usbd_function_instance *function); ++extern void acm_os_disable(struct usbd_function_instance *function); ++ ++#endif +diff -uNr linux/drivers/no-otg/functions/acm/tty.h linux/drivers/otg/functions/acm/tty.h +--- linux/drivers/no-otg/functions/acm/tty.h 1970-01-01 01:00:00.000000000 +0100 ++++ linux/drivers/otg/functions/acm/tty.h 2006-09-01 21:41:26.000000000 +0200 +@@ -0,0 +1,117 @@ ++/* ++ * otg/functions/acm/tty.h ++ * ++ * Copyright (c) 2003, 2004 Belcarra ++ * ++ * By: ++ * Stuart Lynne <sl@belcarra.com>, ++ * Tom Rushworth <tbr@belcarra.com>, ++ * Bruce Balden <balden@belcarra.com> ++ * ++ */ ++/*! ++ * @defgroup TTYFunction ACM-TTY ++ * @ingroup functiongroup ++ */ ++/*! ++ * @file otg/functions/acm/tty.h ++ * @brief ACM Function Driver private defines ++ * ++ * ++ * This is an ACM Function Driver. The upper edge is exposed ++ * to the hosting OS as a Posix type character device. The lower ++ * edge implements the USB Device Stack API. ++ * ++ * These are emulated and set by the tty driver as appropriate ++ * to model a virutal serial port. The ++ * ++ * TIOCM_RNG RNG (Ring) not used ++ * TIOCM_LE DSR (Data Set Ready / Line Enable) ++ * TIOCM_DSR DSR (Data Set Ready) ++ * TIOCM_CAR DCD (Data Carrier Detect) ++ * TIOCM_CTS CTS (Clear to Send) ++ * TIOCM_CD TIOCM_CAR ++ * TIOCM_RI TIOCM_RNG ++ * ++ * These are set by the application: ++ * ++ * TIOCM_DTR DTR (Data Terminal Ready) ++ * TIOCM_RTS RTS (Request to Send) ++ * ++ * TIOCM_LOOP Set into loopback mode ++ * TIOCM_OUT1 Not used. ++ * TIOCM_OUT2 Not used. ++ * ++ * ++ * The following File and termio c_cflags are used: ++ * ++ * O_NONBLOCK don't block in tty_open() ++ * O_EXCL don't allow more than one open ++ * ++ * CLOCAL ignore DTR status ++ * CBAUD send break if set to B0 ++ * ++ * Virtual NULL Modem ++ * ++ * Peripheral to Host via Serial State Notification (write ioctls) ++ * ++ * Application TIOCM Null Modem ACM (DCE) Notificaiton Host (DTE) ++ * ------------------------------------------------------------------------------------------------------------- ++ * Request to send TIOCM_RTS RTS -> CTS CTS Not Available Clear to Send (N/A) ++ * Data Terminal Ready TIOCM_DTR DTR -> DSR DSR bTxCarrier Data Set Ready ++ * DTR -> DCD DCD bRXCarrier Carrier Detect ++ * Ring Indicator TIOCM_OUT1 OUT1 -> RI RI bRingSignal Ring Indicator ++ * Overrun TIOCM_OUT2 OUT2 -> Overrun Overrun bOverrun Overrun ++ * Send Break B0 B0 -> Break Break bBreak Break Received ++ * ------------------------------------------------------------------------------------------------------------- ++ * ++ * ++ * Host to Peripheral via Set Control Line State ++ * ++ * Host (DTE) Line State ACM (DCE) Null Modem TIOCM Peripheral (DTE) ++ * ------------------------------------------------------------------------------------------------------------- ++ * Data Terminal Ready D0 DTR DTR -> DSR TIOCM_DSR Data Set Ready ++ * DTR -> DCD TIOCM_CAR Carrier Detect ++ * Request To Send D1 RTS RTS -> CTS TIOCM_CTS Clear to Send ++ * ------------------------------------------------------------------------------------------------------------- ++ * ++ * ++ * @ingroup TTYFunction ++ */ ++ ++extern struct usbd_function_driver tty_function_driver; ++ ++//#define TTY_OPENED (1 << 0) /*! OPENED flag */ ++#define TTYFD_CLOCAL (1 << 1) /*! CLOCAL flag */ ++//#define TTYFD_LOOPBACK (1 << 2) /*! LOOPBACK flag */ ++#define TTYFD_EXCLUSIVE (1 << 3) /*! EXCLUSIVE flag */ ++#define TTYFD_THROTTLED (1 << 4) /*! THROTTLED flag */ ++ ++/*! @struct tty_private ++ */ ++struct tty_private { ++ ++ struct tty_driver *tty_driver; /*!< tty structure */ ++ int tty_driver_registered; /*!< non-zero if tty_driver registered */ ++ int usb_driver_registered; /*!< non-zero if usb function registered */ ++ ++ struct tty_struct *tty; /*!< non-null if tty open */ ++ struct tq_struct wqueue; /*!< task queue for writer wakeup */ ++ struct tq_struct hqueue; /*!< task queue for hangup */ ++ ++ u32 flags; /*!< flags */ ++ ++ u32 tiocm; /*!< tiocm settings */ ++ ++ struct serial_struct serial_struct; /*!< serial structure used for TIOCSSERIAL and TIOCGSERIAL */ ++ ++ wait_queue_head_t tiocm_wait; ++ u32 tiocgicount; ++ ++ u32 c_cflag; ++ ++ wait_queue_head_t open_wait; /*! wait queue for blocking open*/ ++ int exiting; /*! True if module exiting */ ++}; ++ ++ +diff -uNr linux/drivers/no-otg/functions/isotest/Config.in linux/drivers/otg/functions/isotest/Config.in +--- linux/drivers/no-otg/functions/isotest/Config.in 1970-01-01 01:00:00.000000000 +0100 ++++ linux/drivers/otg/functions/isotest/Config.in 2006-09-01 21:41:26.000000000 +0200 +@@ -0,0 +1,28 @@ ++# ++# Loop Function ++# ++# Copyright (C) 2003-2004 Belcarra ++# Enhanced Jan 2004 to provide greater runtime selection ++# of xmit buffer patterns from the device to the host ++# ++ ++mainmenu_option next_comment ++comment "ISO Test Function" ++ ++dep_tristate ' Loop Function Driver' CONFIG_OTG_ISOTEST $CONFIG_OTG ++ ++if [ "$CONFIG_OTG_ISOTEST" = "y" -o "$CONFIG_OTG_ISOTEST" = "m" ]; then ++ ++ hex ' idVendor (hex value)' CONFIG_OTG_ISOTEST_VENDORID "15ec" ++ hex ' idProduct (hex value)' CONFIG_OTG_ISOTEST_PRODUCTID "f004" ++ hex ' bcdDevice (binary-coded decimal)' CONFIG_OTG_ISOTEST_BCDDEVICE "0100" ++ string ' iManufacturer (string)' CONFIG_OTG_ISOTEST_MANUFACTURER "" ++ string ' iProduct (string)' CONFIG_OTG_ISOTEST_PRODUCT_NAME "" ++ ++ string ' iConfiguration (string)' CONFIG_OTG_ISOTEST_DESC "ISO Test Cfg" ++ string ' Data Interface iInterface (string)' CONFIG_OTG_ISOTEST_DATA_INTF "ISO Data Intf" ++ #bool ' Runtime pattern buffer selection ' CONFIG_OTG_ISOTEST_XMIT_PATTERN "y" ++ ++fi ++ ++endmenu +diff -uNr linux/drivers/no-otg/functions/isotest/Makefile linux/drivers/otg/functions/isotest/Makefile +--- linux/drivers/no-otg/functions/isotest/Makefile 1970-01-01 01:00:00.000000000 +0100 ++++ linux/drivers/otg/functions/isotest/Makefile 2006-09-01 21:41:26.000000000 +0200 +@@ -0,0 +1,73 @@ ++# ++# Function driver ISO Test Device ++# ++# Copyright (c) 2003 Belcarra ++ ++# Multipart objects. ++ ++O_TARGET := isotest_fd_drv.o ++list-multi := isotest_fd.o isotest.o ++ ++isotest_fd-objs := iso.o test.o fermat.o ++isotest-objs := host.o test.o ++ ++# Objects that export symbols. ++export-objs := iso.o ++ ++# Object file lists. ++ ++obj-y := ++obj-m := ++obj-n := ++obj- := ++ ++# Each configuration option enables a list of files. ++ ++obj-$(CONFIG_OTG_ISOTEST) += isotest_fd.o isotest.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. ++ ++OTG=$(TOPDIR)/drivers/otg ++ISOD=$(OTG)/functions/isotest ++USBDCORE_DIR=$(OTG)/usbdcore ++include $(TOPDIR)/Rules.make ++EXTRA_CFLAGS += -I$(ISOD) -I$(OTG) -Wno-unused -Wno-format ++EXTRA_CFLAGS_nostdinc += -I$(ISOD) -I$(OTG) -Wno-unused -Wno-format ++ ++# Link rules for multi-part drivers. ++ ++isotest_fd.o: $(isotest_fd-objs) ++ $(LD) -r -o $@ $(isotest_fd-objs) ++ ++isotest.o: $(isotest-objs) ++ $(LD) -r -o $@ $(isotest-objs) ++ ++# dependencies: ++ ++isotest.o: $(USBDCORE_DIR)/usbd.h $(USBDCORE_DIR)/usbd-bus.h $(USBDCORE_DIR)/usbd-func.h test.h ++host.o: host.c test.h ++ ++test.o:test.h ++ +diff -uNr linux/drivers/no-otg/functions/isotest/fermat.c linux/drivers/otg/functions/isotest/fermat.c +--- linux/drivers/no-otg/functions/isotest/fermat.c 1970-01-01 01:00:00.000000000 +0100 ++++ linux/drivers/otg/functions/isotest/fermat.c 2006-09-01 21:41:26.000000000 +0200 +@@ -0,0 +1,132 @@ ++/* ++ * otg/network_fd/fermat.c - Network Function Driver ++ * ++ * Copyright (c) 2003, 2004 Belcarra ++ * ++ * By: ++ * Bruce Balden <balden@belcarra.com> ++ * ++ * ++ * 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 <linux/config.h> ++#include <linux/module.h> ++#include <linux/kernel.h> ++#include <linux/version.h> ++ ++#ifdef CONFIG_OTG_ISOTEST_MODULE ++ ++#include "fermat.h" ++ ++#ifndef ISO_FERMAT_DEFINED ++typedef unsigned char BYTE; ++typedef struct fermat { ++ int length; ++ BYTE power[256]; ++} FERMAT; ++#endif ++ ++ ++static int fermat_setup(FERMAT *p, int seed){ ++ int i = 0; ++ unsigned long x,y; ++ y = 1; ++ do{ ++ x = y; ++ p->power[i] = ( x == 256 ? 0 : x); ++ y = ( seed * x ) % 257; ++ i += 1; ++ }while( y != 1); ++ p->length = i; ++ return i; ++} ++ ++static void fermat_xform(FERMAT *p, BYTE *data, int length){ ++ BYTE *pw = p->power; ++ int i, j; ++ BYTE * q ; ++ for(i = 0, j=0, q = data; i < length; i++, j++, q++){ ++ if(j>=p->length){ ++ j = 0; ++ } ++ *q ^= pw[j]; ++ } ++} ++ ++static FERMAT default_fermat; ++static const int primitive_root = 5; ++void fermat_init(){ ++ (void) fermat_setup(&default_fermat, primitive_root); ++} ++ ++// Here are the public official versions. ++// Change the primitive_root above to another primitive root ++// if you need better scatter. Possible values are 3 and 7 ++ ++ ++void fermat_encode(BYTE *data, int length){ ++ fermat_xform(&default_fermat, data, length); ++} ++ ++void fermat_decode(BYTE *data, int length){ ++ fermat_xform(&default_fermat, data, length); ++} ++ ++ ++// Note: the seed must be a "primitive root" of 257. This means that ++// the return value of the setup routine must be 256 (otherwise the ++// seed is not a primitive root. The routine will still work fine ++// but will be less pseudo-random. ++ ++#undef TEST ++#if TEST ++#include <stdio.h> ++#include <memory.h> ++ ++// Use FERMAT in two ways: to encode, and to generate test data. ++ ++main(){ ++ //Note 3, 5, and 7 are primitive roots of 257 ++ // 11 is not a primitive root ++ FERMAT three, five, seven; ++ ++ FERMAT three2; ++ printf("Cycle lengths: 3,5,7 %d %d %d \n", ++ fermat_setup(&three, 3), ++ fermat_setup(&five, 5), ++ fermat_setup(&seven, 7)); ++ three2=three; // Copy data from three ++ fermat_xform(&three,three2.power,three2.length); ++ fermat_xform(&five,three2.power,three2.length); ++ fermat_xform(&seven,three2.power,three2.length); ++ fermat_xform(&seven,three2.power,three2.length); ++ fermat_xform(&five,three2.power,three2.length); ++ fermat_xform(&three,three2.power,three2.length); ++ ++ //At this stage, three2 and three should be identical ++ if(memcpy(&three,&three2,sizeof(FERMAT))){ ++ printf("Decoded intact\n"); ++ } ++ ++ fermat_init(); ++ fermat_encode(three2.power,256); ++ ++} ++#endif ++ ++#endif /* CONFIG_OTG_ISOTEST */ ++ +diff -uNr linux/drivers/no-otg/functions/isotest/fermat.h linux/drivers/otg/functions/isotest/fermat.h +--- linux/drivers/no-otg/functions/isotest/fermat.h 1970-01-01 01:00:00.000000000 +0100 ++++ linux/drivers/otg/functions/isotest/fermat.h 2006-09-01 21:41:26.000000000 +0200 +@@ -0,0 +1,38 @@ ++/* ++ * otg/network_fd/fermat.h - Network Function Driver ++ * ++ * Copyright (c) 2003, 2004 Belcarra ++ * ++ * By: ++ * Bruce Balden <balden@belcarra.com> ++ * ++ * 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 ISO_FERMAT_DEFINED ++#define ISO_FERMAT_DEFINED 1 ++typedef unsigned char BYTE; ++typedef struct fermat { ++ int length; ++ BYTE power[256]; ++} FERMAT; ++ ++void fermat_init(void); ++void fermat_encode(BYTE *data, int length); ++void fermat_decode(BYTE *data, int length); ++#endif ++ +diff -uNr linux/drivers/no-otg/functions/isotest/host.c linux/drivers/otg/functions/isotest/host.c +--- linux/drivers/no-otg/functions/isotest/host.c 1970-01-01 01:00:00.000000000 +0100 ++++ linux/drivers/otg/functions/isotest/host.c 2006-09-01 21:41:27.000000000 +0200 +@@ -0,0 +1,840 @@ ++/* ++ * otg/isotest_fd/host.c ++ * ++ * Copyright (c) 2003, 2004 Belcarra ++ * ++ * USB ISO Test ++ * ++ * ++ * Copyright (c) 2003, 2004 sl@belcarra.com ++ * ++ */ ++ ++//#include <linux/config.h> ++#include <linux/module.h> ++#include <linux/version.h> ++ ++#include <linux/kernel.h> ++//#include <linux/sched.h> ++#include <linux/signal.h> ++#include <linux/errno.h> ++//#include <linux/poll.h> ++#include <linux/init.h> ++#include <linux/slab.h> ++#include <linux/fcntl.h> ++#include <linux/module.h> ++#include <linux/spinlock.h> ++#include <linux/list.h> ++#include <linux/smp_lock.h> ++#include <linux/usb.h> ++#include <linux/interrupt.h> ++#include <linux/pci.h> ++#include <linux/delay.h> ++#include <linux/proc_fs.h> ++ ++#include <linux/vmalloc.h> ++ ++#include <asm/atomic.h> ++#include <asm/io.h> ++ ++ ++#if defined(CONFIG_ARCH_SA1100) || defined (CONFIG_ARCH_PXA) ++#include <asm/dma.h> ++#include <asm/mach/dma.h> ++#include <asm/irq.h> ++#include <asm/system.h> ++#include <asm/hardware.h> ++#include <asm/types.h> ++#endif ++ ++#if defined(CONFIG_MIPS_AU1000) || defined(CONFIG_MIPS_PB1500) || defined(CONFIG_MIPS_PB1100) ++#include <asm/au1000.h> ++#include <asm/au1000_dma.h> ++#include <asm/mipsregs.h> ++#endif ++ ++#if defined(CONFIG_ARCH_SAMSUNG) ++#include <asm/arch/timers.h> ++#include <asm/arch/hardware.h> ++#endif ++ ++#include "test.h" ++ ++ ++/* Use our own dbg macro */ ++#undef dbg ++#define dbg(format, arg...) do { if (debug) printk(KERN_DEBUG __FILE__ ": " format "\n" , ## arg); } while (0) ++ ++#define MIN(a,b) (((a) < (b))?(a):(b)) ++#define MAX(a,b) (((a) > (b))?(a):(b)) ++ ++#define THROW(x) goto x ++#define CATCH(x) while(0) x: ++#define THROW_IF(e, x) if (e) { goto x; } ++#define BREAK_IF(x) if (x) { break; } ++#define CONTINUE_IF(x) if (x) { continue; } ++#define RETURN_IF(y) if (y) { return; } ++#define RETURN_ZERO_IF(y) if (y) { return 0; } ++#define RETURN_NULL_IF(y) if (y) { return NULL; } ++ ++ ++/* Version Information */ ++#define DRIVER_VERSION "v0.9" ++#define DRIVER_AUTHOR "sl@belcarra.com" ++#define DRIVER_DESC "USB ISO Test" ++ ++/* Define these values to match your device */ ++ ++#ifdef CONFIG_OTG_ISOTEST_VENDORID ++ #undef USB_ISOTEST_VENDOR_ID ++ #define USB_ISOTEST_VENDOR_ID CONFIG_OTG_ISOTEST_VENDORID ++#else ++ #define USB_ISOTEST_VENDOR_ID 0xfff0 ++#endif ++ ++#ifdef CONFIG_OTG_ISOTEST_PRODUCTID ++ #undef USB_ISOTEST_PRODUCT_ID ++ #define USB_ISOTEST_PRODUCT_ID CONFIG_OTG_ISOTEST_PRODUCTID ++#else ++ #define USB_ISOTEST_PRODUCT_ID 0xfff1 ++#endif ++ ++/* Module paramaters */ ++//MODULE_PARM(debug, "i"); ++//MODULE_PARM_DESC(debug, "Debug enabled or not"); ++ ++static int send; ++MODULE_PARM(send, "i"); ++MODULE_PARM_DESC(send, "send test"); ++ ++static int recv; ++MODULE_PARM(recv, "i"); ++MODULE_PARM_DESC(recv, "recv test"); ++ ++static u32 vendor_id; // no default ++static u32 product_id; // no default ++ ++ ++MODULE_PARM_DESC(vendor_id, "User specified USB idVendor"); ++MODULE_PARM_DESC(product_id, "User specified USB idProduct"); ++MODULE_PARM(vendor_id, "i"); ++MODULE_PARM(product_id, "i"); ++ ++ ++/* table of devices that work with this driver */ ++static struct usb_device_id isotest_table [] = { ++ { USB_DEVICE(USB_ISOTEST_VENDOR_ID, USB_ISOTEST_PRODUCT_ID) }, ++ { }, /* extra entry */ ++ { }, /* Terminating entry */ ++}; ++ ++MODULE_DEVICE_TABLE (usb, isotest_table); ++ ++/* ********************************************************************************************* */ ++ ++ ++/* ********************************************************************************************* */ ++/* ++struct iso_test_data { ++ ++ // sender info ++ u32 sender_id; ++ time_t send_time; ++ u32 send_crc; ++ ++ // loop info ++ u32 recv_id; ++ time_t recv_time; ++ u32 recv_crc; ++ ++ // payload ++ u32 size; ++ u8 data[0]; ++}; ++*/ ++ ++#define IN_URBS 10 ++#define OUT_URBS 10 ++ ++ ++ ++ ++/* Structure to hold all of our device specific stuff */ ++struct usb_isotest { ++ struct usb_device * udev; /* save off the usb device pointer */ ++ struct usb_interface * interface; /* the interface for this device */ ++ ++ u8 closing; ++ u8 num_interrupt_in; /* number of interrupt in endpoints we have */ ++ u8 num_iso_in; /* number of iso in endpoints we have */ ++ u8 num_iso_out; /* number of iso out endpoints we have */ ++ ++ int iso_in_size; /* the size of the receive buffer */ ++ struct urb * iso_in_urbs[IN_URBS]; /* the urb used to send data */ ++ u8 iso_in_endpointAddr; /* the address of the iso in endpoint */ ++ int in_urbs; ++ ++ int iso_out_size; /* the size of the send buffer */ ++ struct urb * iso_out_urbs[OUT_URBS]; /* the urb used to send data */ ++ u8 iso_out_endpointAddr; /* the address of the iso out endpoint */ ++ int out_urbs; ++ ++ struct semaphore sem; /* locks this structure */ ++ struct tq_struct iso_bh; ++ wait_queue_head_t iso_wq; ++ ++ struct isotest_stats stats; ++ ++ int first; ++}; ++ ++ ++/* local function prototypes */ ++ ++static void * isotest_probe (struct usb_device *, unsigned int , const struct usb_device_id *); ++static void isotest_disconnect (struct usb_device *, void *); ++ ++ ++void isotest_schedule_bh(struct usb_isotest *isotest); ++ ++ ++/* ISO OUT - Transmit ************************************************************************** */ ++ ++ ++#if 1 ++static void isotest_iso_out_free_urb(struct urb *urb) ++{ ++ struct usb_isotest *isotest; ++ int i; ++ unsigned long flags; ++ ++ //printk(KERN_INFO"%s: urb: %p\n", __FUNCTION__, urb); ++ ++ RETURN_IF(!urb); ++ ++ if (urb->transfer_buffer) { ++ kfree(urb->transfer_buffer); ++ } ++ ++ isotest = (struct usb_isotest *)urb->context; ++ usb_free_urb(urb); ++ ++ RETURN_IF(!isotest); ++ ++ local_irq_save (flags); ++ for (i = 0; i < OUT_URBS; i++) { ++ CONTINUE_IF(isotest->iso_out_urbs[i] != urb); ++ //printk(KERN_INFO"%s: zeroing %d urb: %p\n", __FUNCTION__, i, urb); ++ isotest->iso_out_urbs[i] = NULL; ++ break; ++ } ++ local_irq_restore (flags); ++} ++#endif ++ ++static int iso_transfer_count; ++ ++int iso_out_submit(struct usb_isotest *isotest, struct urb *urb) ++{ ++ int i; ++ int j; ++ int rc = 0; ++ ++ //printk(KERN_INFO"%s: transfer: %d size: %d frames: %x\n", __FUNCTION__, ++ // iso_transfer_count, urb->transfer_buffer_length, urb->number_of_packets); ++ ++ RETURN_ZERO_IF(isotest->closing); ++ ++ urb->dev = isotest->udev; ++ ++ iso_transfer_count++; ++ ++ for (j = urb->transfer_buffer_length, i = 0; i < urb->number_of_packets; i++) { ++ ++ int send = MIN(isotest->iso_out_size, j); ++ ++ u8 *cp = urb->transfer_buffer + (isotest->iso_out_size * i); ++ ++ j -= send; ++ ++ urb->iso_frame_desc[i].offset = i * isotest->iso_out_size; ++ urb->iso_frame_desc[i].length = send; ++ ++ // iso_transfer_count ++ *cp++ = cpu_to_le16(iso_transfer_count) & 0xff; ++ *cp++ = (cpu_to_le16(iso_transfer_count) >> 8) & 0xff; ++ *cp++ = (cpu_to_le16(iso_transfer_count) >> 16) & 0xff; ++ *cp++ = (cpu_to_le16(iso_transfer_count) >> 24) & 0xff; ++ ++ // iso transfer length ++ *cp++ = cpu_to_le16(urb->transfer_buffer_length) & 0xff; ++ *cp++ = (cpu_to_le16(urb->transfer_buffer_length) >> 8) & 0xff; ++ ++ // iso frame size ++ *cp++ = cpu_to_le16(isotest->iso_out_size) & 0xff; ++ *cp++ = (cpu_to_le16(isotest->iso_out_size) >> 8) & 0xff; ++ ++ // total frames ++ *cp++ = cpu_to_le16(urb->number_of_packets) & 0xff; ++ *cp++ = (cpu_to_le16(urb->number_of_packets) >> 8) & 0xff; ++ ++ // this packet number ++ *cp++ = cpu_to_le16(i+1) & 0xff; ++ *cp++ = (cpu_to_le16(i+1) >> 8) & 0xff; ++ ++ } ++ ++ //printk(KERN_INFO"%s: submitting\n", __FUNCTION__); ++ ++ RETURN_ZERO_IF(!(rc = usb_submit_urb(urb))); ++ printk(KERN_INFO"%s: FAILED rc: %x\n", __FUNCTION__, rc); ++ ++ return rc; ++} ++ ++void isotest_iso_out_complete (struct urb *urb) ++{ ++ struct usb_isotest *isotest = (struct usb_isotest *)urb->context; ++ int rc; ++ ++ //printk(KERN_INFO"%s: urb: %p\n", __FUNCTION__, urb); ++ ++ RETURN_IF(!urb); ++ ++ if (urb->status /* && (urb->status != -ENOENT) && (urb->status != -ECONNRESET)*/) { ++ //printk(KERN_INFO"%s: - nonzero write iso status received: %d\n", __FUNCTION__, urb->status); ++ } ++ ++ iso_out_submit(isotest, urb); ++} ++ ++ ++#define ISO_SEND_TOTAL 1000 ++#define ISO_SEND_TRANSFERS 1200 ++ ++static int out_count; ++ ++struct urb *iso_out_start(struct usb_isotest *isotest) ++{ ++ struct urb * urb = NULL; ++ int rc = 0; ++ int i; ++ int j; ++ ++ ++ int size = ((ISO_SEND_TOTAL % isotest->iso_out_size) < 20) ? ISO_SEND_TOTAL + 20 : ISO_SEND_TOTAL; ++ int frames = (size / isotest->iso_out_size) + 1; ++ ++ //RETURN_NULL_IF(out_count-- <= 0); ++ ++ //printk(KERN_INFO"%s: %d %02x\n", __FUNCTION__, out_count, isotest->iso_out_endpointAddr); ++ ++ ++ //printk(KERN_INFO"%s: frames: %x packet: %d size: %d\n", __FUNCTION__, frames, isotest->iso_out_size, size); ++ ++ // allocate urb and buffer, fill buffer with some data ++ THROW_IF(!(urb = usb_alloc_urb(frames + 1)), error); ++ ++ THROW_IF (!(urb->transfer_buffer = kmalloc(size, GFP_ATOMIC)), error); ++ ++ for (i = 0; i < size; i++) { ++ unsigned char *cp = urb->transfer_buffer + i; ++ *cp = i % 256; ++ } ++ ++ //printk(KERN_INFO"%s: CCC\n", __FUNCTION__); ++ ++ urb->hcpriv = NULL; ++ urb->context = isotest; ++ urb->transfer_flags = USB_ISO_ASAP; ++ urb->complete = isotest_iso_out_complete; ++ urb->pipe = usb_sndisocpipe(isotest->udev, isotest->iso_out_endpointAddr); ++ ++ urb->transfer_buffer_length = size; ++ ++ urb->number_of_packets = frames; ++ ++ THROW_IF((rc = iso_out_submit(isotest, urb)), error); ++ ++ //printk(KERN_INFO"%s: OK frames: %d\n", __FUNCTION__, frames); ++ ++ CATCH(error) { ++ printk(KERN_INFO"%s: FAILED rc: %d\n", __FUNCTION__, rc); ++ //isotest_iso_out_free_urb(urb); ++ return NULL; ++ } ++ return urb; ++} ++ ++/* ISO IN - Receive **************************************************************************** */ ++ ++static int in_count = 5; ++static int in_submitted; ++static int in_resubmitted; ++static long in_completed; ++static long in_total_received; ++ ++#if 0 ++static void isotest_iso_in_free_urb(struct urb *urb) ++{ ++ struct usb_isotest *isotest; ++ int i; ++ unsigned long flags; ++ ++ //printk(KERN_INFO"%s: urb: %p\n", __FUNCTION__, urb); ++ ++ RETURN_IF(!urb); ++ ++ if (urb->transfer_buffer) { ++ kfree(urb->transfer_buffer); ++ } ++ ++ isotest = (struct usb_isotest *)urb->context; ++ usb_free_urb(urb); ++ ++ RETURN_IF(!isotest); ++ ++ local_irq_save (flags); ++ for (i = 0; i < IN_URBS; i++) { ++ CONTINUE_IF(isotest->iso_in_urbs[i] != urb); ++ //printk(KERN_INFO"%s: clearing %d urb: %p\n", __FUNCTION__, i, urb); ++ isotest->iso_in_urbs[i] = NULL; ++ break; ++ } ++ local_irq_restore (flags); ++} ++#endif ++ ++struct urb *iso_in_start(struct usb_isotest *isotest); ++ ++int iso_in_submit(struct usb_isotest *isotest, struct urb *urb) ++{ ++ int i; ++ int j; ++ int rc = 0; ++ ++ //printk(KERN_INFO"%s: %p\n", __FUNCTION__, urb->complete); ++ ++ RETURN_ZERO_IF(isotest->closing); ++ ++ urb->dev = isotest->udev; ++ urb->actual_length = 0; ++ ++ for (j = urb->transfer_buffer_length, i = 0; i < urb->number_of_packets; i++) { ++ ++ int send = MIN(isotest->iso_in_size, j); ++ j -= send; ++ urb->iso_frame_desc[i].offset = i * isotest->iso_in_size; ++ urb->iso_frame_desc[i].length = send; ++ } ++ ++ RETURN_ZERO_IF(!(rc = usb_submit_urb(urb))); ++ printk(KERN_INFO"%s: FAILED rc: %x\n", __FUNCTION__, rc); ++ ++ //isotest_iso_in_free_urb(urb); ++ return rc; ++} ++ ++ ++/** ++ * isotest_iso_in_complete ++ */ ++void isotest_iso_in_complete (struct urb *urb) ++{ ++ struct usb_isotest *isotest = (struct usb_isotest *)urb->context; ++ int i; ++ int rc; ++ int status; ++ ++ //printk(KERN_INFO"%s: urb: %p\n", __FUNCTION__, urb); ++ ++ RETURN_IF(!urb); ++ ++ if (isotest->closing) { ++ //printk(KERN_INFO"%s: urb: %p pre urbs: %d\n", __FUNCTION__, urb, isotest->in_urbs); ++ if (urb->transfer_buffer) { ++ kfree(urb->transfer_buffer); ++ } ++ usb_free_urb(urb); ++ isotest->in_urbs--; ++ //printk(KERN_INFO"%s: urb: %p pre urbs: %d\n", __FUNCTION__, urb, isotest->in_urbs); ++ return; ++ } ++ status = urb->status; ++ ++ if (status /* && (status != -ENOENT) && (status != -ECONNRESET)*/) { ++ //printk(KERN_INFO"%s: - urb: %p nonzero write iso status received: %x\n", __FUNCTION__, urb, status); ++ } ++ ++ else if (urb->actual_length) { ++ //printk(KERN_INFO"%s: urb: %p lenght: %d\n", __FUNCTION__, urb, urb->actual_length); ++ in_completed++; ++ in_total_received += urb->actual_length; ++ ++ //printk(KERN_INFO"%s: ", __FUNCTION__); ++ for (i = 0; i < urb->number_of_packets; i++) { ++ ++ iso_trace_recv_data(&isotest->stats, ++ urb->transfer_buffer + urb->iso_frame_desc[i].offset, ++ urb->iso_frame_desc[i].actual_length, 0); ++ ++ // printk("%d:%d:%x ", i, ++ // urb->iso_frame_desc[i].actual_length, ++ // urb->iso_frame_desc[i].status); ++ ++ ++ } ++ //printk("\n"); ++ } ++ ++ THROW_IF((rc = iso_in_submit(isotest, urb)), error); ++ ++ in_resubmitted++; ++ ++ CATCH(error) { ++ printk(KERN_INFO"%s: FAILED rc: %x\n", __FUNCTION__, rc); ++ } ++} ++ ++ ++struct urb *iso_in_start(struct usb_isotest *isotest) ++{ ++ struct urb * urb = NULL; ++ int frames = (ISO_SEND_TOTAL + isotest->iso_in_size) / isotest->iso_in_size; ++ int iso_transfer_size; ++ int rc = 0; ++ ++ ++ //RETURN_NULL_IF(in_count-- <= 0); ++ ++ //printk(KERN_INFO"%s: %d %02x\n", __FUNCTION__, in_count, isotest->iso_in_endpointAddr); ++ ++ //iso_transfer_size = ((ISO_SEND_TOTAL % isotest->iso_in_size) > 20) ? ++ // ISO_SEND_TOTAL : ++ // ISO_SEND_TOTAL + (20 - (ISO_SEND_TOTAL % isotest->iso_in_size)); ++ ++ iso_transfer_size = frames * isotest->iso_in_size; ++ ++ THROW_IF(!(urb = usb_alloc_urb(frames + 1)), error); ++ THROW_IF (!(urb->transfer_buffer = kmalloc(iso_transfer_size, GFP_ATOMIC)), error); ++ memset(urb->transfer_buffer, 0, isotest->iso_in_size); ++ ++ urb->hcpriv = NULL; ++ urb->context = isotest; ++ urb->transfer_flags = USB_ISO_ASAP; ++ urb->complete = isotest_iso_in_complete; ++ urb->pipe = usb_rcvisocpipe(isotest->udev, isotest->iso_in_endpointAddr); ++ ++ urb->transfer_buffer_length = iso_transfer_size; ++ urb->number_of_packets = frames; ++ ++ THROW_IF((rc = iso_in_submit(isotest, urb)), error); ++ in_submitted++; ++ isotest->in_urbs++; ++ //printk(KERN_INFO"%s: new urbs: %d\n", __FUNCTION__, isotest->in_urbs); ++ return urb; ++ ++ CATCH(error) { ++ printk(KERN_INFO"%s: FAILED rc: %x\n", __FUNCTION__, rc); ++ //isotest_iso_in_free_urb(urb); ++ return NULL; ++ } ++} ++ ++ ++ ++ ++/* ********************************************************************************************* */ ++ ++void isotest_schedule_bh(struct usb_isotest *isotest) ++{ ++ unsigned long flags; ++ ++ //RETURN_IF(!isotest->iso_bh.data); ++ ++ // schedule more data ++ local_irq_save (flags); ++ if (isotest->iso_bh.data && !isotest->iso_bh.sync) { ++ MOD_INC_USE_COUNT; ++ queue_task(&isotest->iso_bh, &tq_immediate); ++ mark_bh(IMMEDIATE_BH); ++ } ++ local_irq_restore (flags); ++} ++ ++ ++static void bottomhalf(void *data) ++{ ++ int i; ++ unsigned long flags; ++ struct usb_isotest *isotest = (struct usb_isotest *) data; ++ ++ if (isotest->first) { ++ //sleep_on_timeout(&isotest->iso_wq, 200); ++ udelay(100); ++ isotest->first = 0; ++ } ++ ++ THROW_IF(!isotest, error); ++ ++ if (isotest->closing) { ++ struct urb *urb; ++ //printk(KERN_INFO"%s: closing\n", __FUNCTION__); ++ ++ // unlink outstanding urbs, this has side-effect of calling completion routing ++ local_irq_save (flags); ++ for (i = 0; i < IN_URBS; i++) { ++ CONTINUE_IF(!(urb = isotest->iso_in_urbs[i])); ++ //printk(KERN_INFO"%s: unlinking: %d IN urb: %p\n", __FUNCTION__, i, urb); ++ isotest->iso_in_urbs[i] = NULL; ++ urb->transfer_flags |= USB_ASYNC_UNLINK; ++ usb_unlink_urb(urb); ++ } ++ local_irq_restore (flags); ++ ++ local_irq_save (flags); ++ for (i = 0; i < OUT_URBS; i++) { ++ CONTINUE_IF(!(urb = isotest->iso_out_urbs[i])); ++ //printk(KERN_INFO"%s: unlinking: %d OUT urb: %p\n", __FUNCTION__, i, urb); ++ isotest->iso_out_urbs[i] = NULL; ++ urb->transfer_flags |= USB_ASYNC_UNLINK; ++ usb_unlink_urb(urb); ++ } ++ local_irq_restore (flags); ++ ++ // tell disconnect that we are finished ++ isotest->iso_bh.data = NULL; ++ ++ } ++ else { ++ ++ //printk(KERN_INFO"%s: normal\n", __FUNCTION__); ++ ++ if (send && out_count) { ++ local_irq_save (flags); ++ for (i = 0; i < OUT_URBS; i++) { ++ CONTINUE_IF(isotest->iso_out_urbs[i]); ++ isotest->iso_out_urbs[i] = iso_out_start(isotest); ++ //printk(KERN_INFO"%s: starting: %d OUT urb: %p\n", __FUNCTION__, i, isotest->iso_out_urbs[i]); ++ } ++ local_irq_restore (flags); ++ } ++ ++ if (recv && in_count) { ++ local_irq_save (flags); ++ for (i = 0; i < IN_URBS; i++) { ++ CONTINUE_IF(isotest->iso_in_urbs[i]); ++ isotest->iso_in_urbs[i] = iso_in_start(isotest); ++ //printk(KERN_INFO"%s: starting: %d IN urb: %p\n", __FUNCTION__, i, isotest->iso_in_urbs[i]); ++ } ++ local_irq_restore (flags); ++ } ++ } ++ ++ CATCH(error) { ++ printk(KERN_ERR"%s: isotest NULL\n", __FUNCTION__); ++ } ++ MOD_DEC_USE_COUNT; ++} ++ ++/* ********************************************************************************************* */ ++ ++/* usb specific object needed to register this driver with the usb subsystem */ ++static struct usb_driver isotest_driver = { ++ name: "isotest", ++ probe: isotest_probe, ++ disconnect: isotest_disconnect, ++ id_table: isotest_table, ++}; ++ ++/** ++ * isotest_probe ++ * ++ * Called by the usb core when a new device is connected that it thinks ++ * this driver might be interested in. ++ */ ++static void * isotest_probe(struct usb_device *udev, unsigned int ifnum, const struct usb_device_id *id) ++{ ++ struct usb_isotest *isotest = NULL; ++ struct usb_interface *interface; ++ struct usb_device_descriptor *device = &udev->descriptor; ++ struct usb_interface_descriptor *interface_descriptor; ++ int i; ++ ++ printk(KERN_INFO"%s: %04x %04x\n", __FUNCTION__, device->idVendor, device->idProduct); ++ ++ // See if the device offered us matches what we can accept ++ //if ((device->idVendor != vendor_id) || (device->idProduct != product_id)) { ++ // printk(KERN_INFO"%s: FAILED\n", __FUNCTION__); ++ // return NULL; ++ //} ++ ++ // allocate memory for our device state and intialize it ++ if (!(isotest = kmalloc (sizeof(struct usb_isotest), GFP_KERNEL))) { ++ printk(KERN_INFO"%s: Out of memory\n", __FUNCTION__); ++ return NULL; ++ } ++ ++ memset (isotest, 0x00, sizeof (*isotest)); ++ init_MUTEX (&isotest->sem); ++ init_waitqueue_head(&isotest->iso_wq); ++ ++ isotest->udev = udev; ++ isotest->first = 1; ++ isotest->closing = 0; ++ isotest->in_urbs = 0; ++ isotest->interface = interface = &udev->actconfig->interface[ifnum]; ++ isotest->iso_bh.routine = bottomhalf; ++ isotest->iso_bh.data = (void *)isotest; ++ interface_descriptor = &interface->altsetting[0]; ++ ++ // set up the endpoint information and check out the endpoints ++ ++ for (i = 0; i < interface_descriptor->bNumEndpoints; ++i) { ++ ++ struct usb_endpoint_descriptor *endpoint = &interface_descriptor->endpoint[i]; ++ ++ //printk(KERN_INFO"%s: looking at %02x\n", __FUNCTION__, endpoint->bEndpointAddress); ++ ++ if ((endpoint->bEndpointAddress & 0x80) && ((endpoint->bmAttributes & 3) == 0x01)) { ++ ++ //printk(KERN_INFO"%s: found ISO IN %02x\n", __FUNCTION__, endpoint->bEndpointAddress); ++ isotest->iso_in_size = endpoint->wMaxPacketSize; ++ isotest->iso_in_endpointAddr = endpoint->bEndpointAddress; ++ } ++ ++ if (((endpoint->bEndpointAddress & 0x80) == 0x00) && ((endpoint->bmAttributes & 3) == 0x01)) { ++ ++ //printk(KERN_INFO"%s: found ISO OUT %02x\n", __FUNCTION__, endpoint->bEndpointAddress); ++ ++ isotest->iso_out_size = endpoint->wMaxPacketSize; ++ isotest->iso_out_endpointAddr = endpoint->bEndpointAddress; ++ } ++ } ++ ++ out_count = ISO_SEND_TRANSFERS; ++ ++ // let the user know what node this device is now attached to ++ //printk(KERN_INFO"%s: USB TEST device now attached to ISOTEST\n", __FUNCTION__); ++ ++ isotest_schedule_bh(isotest); ++ ++ return isotest; ++} ++ ++/** ++ * isotest_disconnect ++ * ++ * Called by the usb core when the device is removed from the system. ++ */ ++static void isotest_disconnect(struct usb_device *udev, void *ptr) ++{ ++ struct usb_isotest *isotest; ++ ++ printk(KERN_INFO"%s: in_submitted: %d in_resubmitted: %d in_completed: %ld in_total: %ld\n", ++ __FUNCTION__, in_submitted, in_resubmitted, in_completed, in_total_received); ++ ++ RETURN_IF(!(isotest = (struct usb_isotest *)ptr)); ++ ++ // set flag to say we are closing ++ isotest->closing = 1; ++ isotest_schedule_bh(isotest); ++ ++ while (isotest->iso_bh.data) { ++ isotest_schedule_bh(isotest); ++ printk(KERN_INFO"%s: waiting for bh\n", __FUNCTION__); ++ sleep_on_timeout(&isotest->iso_wq, 20); ++ } ++ ++ while (isotest->in_urbs) { ++ printk(KERN_INFO"%s: waiting for urbs\n", __FUNCTION__); ++ sleep_on_timeout(&isotest->iso_wq, 20); ++ } ++ ++ kfree(isotest); ++ ++ printk(KERN_INFO"%s: USB ISOTEST now disconnected\n", __FUNCTION__); ++} ++ ++ ++/* ********************************************************************************************* */ ++ ++void iso_start_in(int count) ++{ ++ ++} ++ ++void iso_start_out(int count) ++{ ++ ++} ++ ++ ++/** ++ * isotest_init ++ */ ++static int isotest_init(void) ++{ ++ int result; ++ ++ printk(KERN_INFO"%s:\n", __FUNCTION__); ++ ++ if (vendor_id && product_id) { ++ int i; ++ for (i = 0; i < (sizeof(isotest_table) / sizeof(struct usb_device_id) - 1); i++) { ++ ++ if ((isotest_table[i].idVendor == vendor_id) && isotest_table[i].idProduct == product_id ) { ++ printk(KERN_INFO"%s: vendor_id: %04x product_id: %04x already in table\n", ++ __FUNCTION__, vendor_id, product_id); ++ break; ++ } ++ printk(KERN_INFO"%s: vendor_id: %04x product_id: %04x\n", ++ __FUNCTION__, isotest_table[i].idVendor, isotest_table[i].idProduct); ++ } ++ if (!isotest_table[i].idVendor && !isotest_table[i].idProduct) { ++ printk(KERN_INFO"%s: inserting vendor_id: %04x product_id: %04x into table\n", ++ __FUNCTION__, vendor_id, product_id); ++ ++ isotest_table[i].match_flags = USB_DEVICE_ID_MATCH_DEVICE; ++ isotest_table[i].idVendor = vendor_id; ++ isotest_table[i].idProduct = product_id; ++ isotest_table[i].bDeviceClass = 0; ++ isotest_table[i].bDeviceSubClass = 0; ++ } ++ } ++ ++ iso_trace_init("isotest_host"); ++ ++ /* register this driver with the USB subsystem */ ++ result = usb_register(&isotest_driver); ++ if (result < 0) { ++ printk(KERN_INFO"%s: usb_register failed for the "__FILE__" driver. Error number %d\n", KERN_INFO, result); ++ return -1; ++ } ++ ++ printk(KERN_INFO "%s: " DRIVER_DESC " " DRIVER_VERSION "\n", __FUNCTION__); ++ return 0; ++} ++ ++ ++/** ++ * isotest_exit ++ */ ++static void isotest_exit(void) ++{ ++ /* deregister this driver with the USB subsystem */ ++ usb_deregister(&isotest_driver); ++ ++ iso_trace_exit("isotest_host"); ++} ++ ++ ++module_init (isotest_init); ++module_exit (isotest_exit); ++ ++MODULE_AUTHOR(DRIVER_AUTHOR); ++MODULE_DESCRIPTION(DRIVER_DESC); ++#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,4,17) ++MODULE_LICENSE("PRIVATE"); ++#endif ++ +diff -uNr linux/drivers/no-otg/functions/isotest/iso.c linux/drivers/otg/functions/isotest/iso.c +--- linux/drivers/no-otg/functions/isotest/iso.c 1970-01-01 01:00:00.000000000 +0100 ++++ linux/drivers/otg/functions/isotest/iso.c 2006-09-01 21:41:27.000000000 +0200 +@@ -0,0 +1,620 @@ ++/* ++ * otg/isotest_fd/iso.c ++ * ++ * Copyright (c) 2003, 2004 Belcarra ++ * ++ * By: ++ * Stuart Lynne <sl@belcarra.com>, ++ * Tom Rushworth <tbr@belcarra.com>, ++ * Bruce Balden <balden@belcarra.com> ++ * ++ * ++ */ ++ ++#include <linux/config.h> ++#include <linux/module.h> ++#include <linux/kernel.h> ++#include <linux/version.h> ++ ++MODULE_AUTHOR ("sl@belcarra.com, tbr@belcarra.com"); ++MODULE_DESCRIPTION ("USB Device Serial Function"); ++ ++#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,4,17) ++MODULE_LICENSE("GPL"); ++#endif ++ ++#include <linux/init.h> ++#include <linux/list.h> ++#include <asm/uaccess.h> ++#include <linux/slab.h> ++#include <linux/interrupt.h> ++ ++#include <linux/smp_lock.h> ++#include <linux/ctype.h> ++#include <linux/timer.h> ++#include <linux/string.h> ++ ++#include "usbp-chap9.h" ++#include <usbp-mem.h> ++#include <usbp-func.h> ++#include <usbp-admin.h> ++ ++USBD_MODULE_INFO ("isotest_fd 2.0-beta"); ++ ++#include "test.h" ++#include "fermat.h" ++ ++struct usb_isotest { ++ int interface; ++ struct usbd_function_instance *function; ++ rwlock_t rwlock; ++ ++ int open; ++ int closing; ++ struct tq_struct iso_bh; ++ wait_queue_head_t iso_wq; ++}; ++ ++#define ISO_OUT 0x00 ++#define ISO_IN 0x01 ++ ++#define ENDPOINTS 0x02 ++ ++ ++u8 isotest_requested_endpoints[ENDPOINTS+1] = { ++ USB_DIR_OUT | USB_ENDPOINT_ISOCHRONOUS, ++ USB_DIR_IN | USB_ENDPOINT_ISOCHRONOUS, ++ 0, ++}; ++ ++#define ISO_OUT_PKTSIZE 90 ++#define ISO_IN_PKTSIZE 90 ++#define isotest_requested_transferSizes xfer_sizes ++ ++u16 xfer_sizes[ENDPOINTS+1] = { ++ ISO_OUT_PKTSIZE, ++ ISO_IN_PKTSIZE, ++ 0, ++}; ++ ++ ++ ++/* Module Parameters ************************************************************************* */ ++// override vendor ID ++static u32 vendor_id; ++MODULE_PARM (vendor_id, "i"); ++MODULE_PARM_DESC (vendor_id, "vendor id"); ++ ++// override product ID ++static u32 product_id; ++MODULE_PARM (product_id, "i"); ++MODULE_PARM_DESC (product_id, "product id"); ++ ++MODULE_PARM (xfer_sizes, "3-3h"); ++MODULE_PARM_DESC (xfer_sizes, "Requested transfer sizes for each endpoint; default 90 for iso in and out"); ++ ++// packet sizes ++static u32 in = ISO_IN_PKTSIZE; ++MODULE_PARM (in, "i"); ++MODULE_PARM_DESC (in, "in size"); ++ ++static u32 out = ISO_OUT_PKTSIZE; ++MODULE_PARM (out, "i"); ++MODULE_PARM_DESC (out, "out size"); ++ ++static int fermat=0; ++MODULE_PARM (fermat, "i"); ++MODULE_PARM_DESC (fermat, "Apply randomization to buffer"); ++ ++static int custom=0; ++MODULE_PARM (custom, "i"); ++MODULE_PARM_DESC (custom, "Supply custom pattern via xmit_pattern parameter"); ++ ++static int print_all=0; ++MODULE_PARM (print_all, "i"); ++MODULE_PARM_DESC (print_all, "Print all buffers, not just the first"); ++ ++#define ZERO4 0,0,0,0 ++#define ZERO16 ZERO4,ZERO4,ZERO4,ZERO4 ++#define ZERO64 ZERO16,ZERO16,ZERO16,ZERO16 ++static u8 xmit_pattern[256]={1,0xE,1,2,3,4,5,6,7,8,9,0xa,0xb,0xc,0xd,0xe, ZERO16,ZERO16,ZERO16,ZERO64,ZERO64,ZERO64}; ++MODULE_PARM(xmit_pattern,"1-256b"); ++MODULE_PARM_DESC(xmit_pattern, "pattern to be transmitted, count, size, payload; size is " ++ "the size of the payload following. count is the number of times to repeat the pattern (0=infinite)"); ++static struct { ++ int count; ++ int size; ++ u8 *payload_start, *payload_end; ++ u8 *current; ++ int total_size; ++ int sent; ++} xps; // Transmit pattern state ++ ++static void xmit_pattern_state_init(void); ++static u8 xmit_pattern_next(void); ++static void fill_xmit_buffer(u32 size, u8*buffer); ++ ++static void xmit_pattern_state_init(){ ++ xps.count = xmit_pattern[0]; ++ xps.size = xmit_pattern[1]; ++ xps.payload_start = xmit_pattern+2; ++ xps.payload_end = xps.payload_start + xps.size; ++ xps.current = xps.payload_start; ++ xps.sent = 0; ++ xps.total_size = xps.count * xps.size; ++} ++ ++static u8 xmit_pattern_next(){ ++ ++ // current always points at the next character to send ++ u8 next_value = *xps.current++; ++ ++ xps.sent += 1; ++ ++ if (xps.current >= xps.payload_end) ++ xps.current = xps.payload_start; //Rewind buffer ++ ++ // if Total pattern has been sent; rewind ++ if (xps.sent >= xps.total_size){ ++ xmit_pattern_state_init(); ++ } ++ ++ return next_value; ++} ++ ++static void fill_xmit_buffer(u32 size, u8 *buffer) ++{ ++ u8 * limit = buffer + size; ++ xmit_pattern_state_init(); ++ while(buffer < limit){ ++ *buffer++ = xmit_pattern_next(); ++ } ++} ++ ++static void fill_xmit_buffer_default(u32 size, u8 * buffer){ ++ int j; ++ for(j = 0; j < size; j++){ ++ buffer[j] = j & 0xff; ++ } ++} ++ ++static void print_buffer(u32 size, u8 * buffer){ ++ // Print up to 16 bytes of the buffer, first time called only ++ static int first = 1; ++ int n = ( size >=16 ? 16 : size); ++ int j; ++ if((first) || (print_all)){ ++ char *prefix = first? "\n" : ""; ++ printk(KERN_INFO "%sxmit buffer:", prefix); ++ for( j=0 ; j < n; j++){ ++ printk("%02x",buffer[j]); ++ } ++ printk("\n"); ++ first = 0; ++ } ++} ++ ++ ++ ++/* ************************************************************************** */ ++ ++static struct isotest_stats isotest_stats; ++static struct usb_isotest isotest; ++ ++ ++/* Classes descriptors ++ */ ++ ++static u8 isotest_ep_1[7] = { 0x07, USB_DT_ENDPOINT, 0, ISOCHRONOUS, ISO_OUT_PKTSIZE&0xff, (ISO_OUT_PKTSIZE>>8)&0xff, 0x00}; ++static u8 isotest_ep_2[7] = { 0x07, USB_DT_ENDPOINT, 0, ISOCHRONOUS, ISO_IN_PKTSIZE&0xff, (ISO_IN_PKTSIZE>>8)&0xff, 0x00}; ++ ++static struct usbd_endpoint_descriptor *isotest_data_endpoints[] = { ++ (struct usbd_endpoint_descriptor *) &isotest_ep_1, ++ (struct usbd_endpoint_descriptor *) &isotest_ep_2 }; ++ ++u8 isotest_data_indexes [] = { ISO_OUT, ISO_IN, }; ++ ++ ++/* Alternate descriptors ++ */ ++static u8 isotest_data_alternate_descriptor[sizeof(struct usbd_interface_descriptor)] = { ++ 0x09, USB_DT_INTERFACE, 0x00, 0x00, 0x01, // bInterfaceNumber, bAlternateSetting, bNumEndpoints ++ 0x00, 0x00, 0x00, 0x00, ++}; ++ ++ ++/* Alternate descriptions ++ */ ++static struct usbd_alternate_description isotest_data_alternate_descriptions[] = { ++ { iInterface:CONFIG_OTG_ISOTEST_DATA_INTF, ++ interface_descriptor: (struct usbd_interface_descriptor *)&isotest_data_alternate_descriptor, ++ endpoints:sizeof (isotest_data_endpoints) / sizeof(u8 *), ++ endpoint_list: isotest_data_endpoints, ++ endpoint_indexes: isotest_data_indexes, ++ }, ++}; ++ ++ ++/* Interface descriptions ++ */ ++static struct usbd_interface_description isotest_interfaces[] = { ++ { alternates:sizeof (isotest_data_alternate_descriptions) / sizeof (struct usbd_alternate_description), ++alternate_list:isotest_data_alternate_descriptions, ++ }, ++}; ++ ++ ++/* Configuration descriptions ++ */ ++static u8 isotest_configuration_descriptor[sizeof(struct usbd_configuration_descriptor)] = { ++ 0x09, USB_DT_CONFIGURATION, 0x00, 0x00, // wLength ++ sizeof (isotest_interfaces) / sizeof (struct usbd_interface_description), ++ 0x01, 0x00, // bConfigurationValue, iConfiguration ++ 0, 0, ++}; ++ ++ ++struct usbd_configuration_description isotest_description[] = { ++ { iConfiguration:CONFIG_OTG_ISOTEST_DESC, ++ configuration_descriptor: (struct usbd_configuration_descriptor *)isotest_configuration_descriptor, ++ }, ++}; ++ ++/* Device Description ++ */ ++static struct usbd_device_descriptor isotest_device_descriptor = { ++ bLength: sizeof(struct usbd_device_descriptor), ++ bDescriptorType: USB_DT_DEVICE, ++ bcdUSB: __constant_cpu_to_le16(USB_BCD_VERSION), ++ bDeviceClass: COMMUNICATIONS_DEVICE_CLASS, ++ bDeviceSubClass: 0x00, ++ bDeviceProtocol: 0x00, ++ bMaxPacketSize0: 0x00, ++ idVendor: __constant_cpu_to_le16(CONFIG_OTG_ISOTEST_VENDORID), ++ idProduct: __constant_cpu_to_le16(CONFIG_OTG_ISOTEST_PRODUCTID), ++ bcdDevice: __constant_cpu_to_le16(CONFIG_OTG_ISOTEST_BCDDEVICE), ++}; ++ ++#ifdef CONFIG_OTG_HIGH_SPEED ++static struct usbd_device_qualifier_descriptor isotest_device_qualifier_descriptor = { ++ bLength: sizeof(struct usbd_device_qualifier_descriptor), ++ bDescriptorType: USB_DT_DEVICE_QUALIFIER, ++ bcdUSB: __constant_cpu_to_le16(USB_BCD_VERSION), ++ bDeviceClass: COMMUNICATIONS_DEVICE_CLASS, ++ bDeviceSubClass: 0x00, ++ bDeviceProtocol: 0x00, ++ bMaxPacketSize0: 0x00, ++}; ++#endif /* CONFIG_OTG_HIGH_SPEED */ ++ ++static struct usbd_endpoint_request iso_endpoint_requests[ENDPOINTS+1] = { ++ { 1, 0, 0, USB_DIR_OUT | USB_ENDPOINT_ISOCHRONOUS, ISO_OUT_PKTSIZE, ISO_OUT_PKTSIZE * 4, }, ++ { 1, 0, 0, USB_DIR_IN | USB_ENDPOINT_ISOCHRONOUS, ISO_IN_PKTSIZE, ISO_OUT_PKTSIZE * 4, }, ++ { 1, }, ++}; ++ ++static struct usbd_otg_descriptor iso_otg_descriptor = { ++bLength : sizeof(struct usbd_otg_descriptor), ++bDescriptorType: USB_DT_OTG, ++bmAttributes: 0, ++}; ++ ++struct usbd_device_description isotest_device_description = { ++ device_descriptor: &isotest_device_descriptor, ++ #ifdef CONFIG_OTG_HIGH_SPEED ++ device_qualifier_descriptor: &isotest_device_qualifier_descriptor, ++ #endif /* CONFIG_OTG_HIGH_SPEED */ ++ otg_descriptor: &iso_otg_descriptor, ++ iManufacturer: CONFIG_OTG_ISOTEST_MANUFACTURER, ++ iProduct: CONFIG_OTG_ISOTEST_PRODUCT_NAME, ++ #if !defined(CONFIG_OTG_NO_SERIAL_NUMBER) && defined(CONFIG_OTG_SERIAL_NUMBER_STR) ++ iSerialNumber: CONFIG_OTG_SERIAL_NUMBER_STR, ++ #endif ++}; ++ ++void iso_start_in(int count); ++void iso_start_out(int count); ++ ++void schedule_bh(void) ++{ ++ unsigned long flags; ++ ++ //printk(KERN_INFO"%s: GET_USE_COUNT: %d\n", __FUNCTION__, GET_USE_COUNT(THIS_MODULE)); ++ ++ local_irq_save (flags); ++ if (!isotest.iso_bh.sync) { ++ MOD_INC_USE_COUNT; ++ queue_task(&isotest.iso_bh, &tq_immediate); ++ mark_bh(IMMEDIATE_BH); ++ } ++ local_irq_restore (flags); ++ ++ //printk(KERN_INFO"%s: GET_USE_COUNT: %d\n", __FUNCTION__, GET_USE_COUNT(THIS_MODULE)); ++} ++ ++ ++int isotest_urb_sent (struct usbd_urb *urb, int rc); ++int isotest_recv_urb (struct usbd_urb *urb, int rc); ++ ++/* Transmit Function *************************************************************************** */ ++ ++static int out_count; ++static int in_count; ++static int send_size = 1000; ++static int iso_transfer_in_count; ++ ++static int isotest_xmit_data (void) ++{ ++ int i; ++ int j; ++ int frames; ++ int size = ((send_size % in) < 20) ? send_size + 20 : send_size; ++ struct usbd_urb *urb; ++ struct usbd_function_instance *function = isotest.function; ++ ++ //printk(KERN_INFO"%s: open: %d in_count: %d\n", __FUNCTION__, isotest.open, in_count); ++ ++ RETURN_ZERO_IF(!isotest.open); ++ ++ RETURN_EINVAL_IF(!(urb = usbd_alloc_urb (isotest.function, ISO_IN, size, isotest_urb_sent))); ++ ++ frames = (size / in) + 1; ++ ++ iso_transfer_in_count++; ++ ++ for (i = 0; i < frames; i++) { ++ ++ u8 *cp = urb->buffer + (i * in); ++ if(custom){ ++ (void) fill_xmit_buffer(in, cp); ++ } else { ++ (void) fill_xmit_buffer_default(in,cp); ++ } ++ if(fermat){ ++ fermat_encode(cp, in); ++ } ++ print_buffer(in, cp); ++ ++ // iso_transfer_count ++ *cp++ = cpu_to_le16(iso_transfer_in_count) & 0xff; ++ *cp++ = (cpu_to_le16(iso_transfer_in_count) >> 8) & 0xff; ++ *cp++ = (cpu_to_le16(iso_transfer_in_count) >> 16) & 0xff; ++ *cp++ = (cpu_to_le16(iso_transfer_in_count) >> 24) & 0xff; ++ ++ // iso transfer length ++ *cp++ = cpu_to_le16(size) & 0xff; ++ *cp++ = (cpu_to_le16(size) >> 8) & 0xff; ++ ++ // iso frame size ++ *cp++ = cpu_to_le16(in) & 0xff; ++ *cp++ = (cpu_to_le16(in) >> 8) & 0xff; ++ ++ // total frames ++ *cp++ = cpu_to_le16(frames) & 0xff; ++ *cp++ = (cpu_to_le16(frames) >> 8) & 0xff; ++ ++ // this packet number ++ *cp++ = cpu_to_le16(i+1) & 0xff; ++ *cp++ = (cpu_to_le16(i+1) >> 8) & 0xff; ++ ++ } ++ urb->actual_length = size; ++ ++ // push it down into the usb-device layer ++ //printk(KERN_INFO"%s: sending: %p length: %d\n", __FUNCTION__, urb, urb->actual_length); ++ return usbd_start_in_urb (urb); ++} ++ ++ ++/* isotest_urb_sent - called to indicate URB transmit finished ++ * @urb: pointer to struct usbd_urb ++ * @rc: result ++ */ ++int isotest_urb_sent (struct usbd_urb *urb, int rc) ++{ ++ //printk(KERN_INFO"%s: sent: %p status: %d\n", __FUNCTION__, urb, urb->status); ++ ++ usbd_free_urb (urb); ++ ++ //printk(KERN_INFO"%s: calling schedule_bh\n", __FUNCTION__); ++ schedule_bh(); ++ return 0; ++} ++ ++ ++/* USB Device Functions ************************************************************************ */ ++ ++/* isotest_event_handler - process a device event ++ * ++ */ ++void isotest_event_handler (struct usbd_function_instance *function, usbd_device_event_t event, int data) ++{ ++ int i; ++ switch (event) { ++ ++ case DEVICE_RESET: ++ case DEVICE_DESTROY: ++ if (isotest.open) ++ usbd_disable_irq(NULL); ++ ++ isotest.open = 0; ++ break; ++ ++ case DEVICE_CONFIGURED: ++ isotest.open = 1; ++ for (i = 0; i < 2; i++) { ++ struct usbd_urb *urb; ++ BREAK_IF(!(urb = usbd_alloc_urb (function, ISO_OUT, ++ usbd_endpoint_transferSize( ++ function, ISO_OUT,usbd_high_speed(function)), ++ isotest_recv_urb ++ ))); ++ if (usbd_start_out_urb(urb)) ++ usbd_free_urb(urb); ++ } ++ iso_start_in(100); ++ break; ++ ++ case DEVICE_BUS_INACTIVE: ++ break; ++ ++ case DEVICE_BUS_ACTIVITY: ++ break; ++ ++ default: ++ break; ++ } ++} ++ ++ ++/* isotest_recv_urb - called to indicate URB has been received ++ * @urb - pointer to struct usbd_urb ++ * ++ * Return non-zero if we failed and urb is still valid (not disposed) ++ */ ++int isotest_recv_urb (struct usbd_urb *urb, int rc) ++{ ++ //struct usbd_device_instance *device = urb->device; ++#if 0 ++ printk(KERN_INFO"%s: urb: %p length: %d framenum: %04x %d\n", __FUNCTION__, ++ urb, urb->actual_length, urb->framenum, urb->framenum); ++#endif ++ iso_trace_recv_data(&isotest_stats, urb->buffer, urb->actual_length, 0); ++ ++ // start_recv urb ++ return (usbd_start_out_urb (urb)); ++} ++ ++ ++/* ********************************************************************************************* */ ++ ++void iso_start_in(int count) ++{ ++ in_count = count; ++ //printk(KERN_INFO"%s: calling schedule_bh\n", __FUNCTION__); ++ schedule_bh(); ++} ++ ++void iso_start_out(int count) ++{ ++ out_count = count; ++ //printk(KERN_INFO"%s: calling schedule_bh\n", __FUNCTION__); ++ schedule_bh(); ++} ++ ++static void ++bottomhalf(void *data) ++{ ++ //printk(KERN_INFO"%s: closing: %d\n", __FUNCTION__, isotest.closing); ++ if (isotest.closing) ++ isotest.iso_bh.data = NULL; ++ ++ else if (isotest.open) ++ isotest_xmit_data (); ++ ++ MOD_DEC_USE_COUNT; ++ //printk(KERN_INFO"%s: GET_USE_COUNT: %d\n", __FUNCTION__, GET_USE_COUNT(THIS_MODULE)); ++} ++ ++ ++/* ********************************************************************************************* */ ++ ++static int isotest_function_enable (struct usbd_function_instance *function) ++{ ++ //printk(KERN_INFO"%s:\n", __FUNCTION__); ++ MOD_INC_USE_COUNT; ++ ++ //printk(KERN_INFO"%s: calling schedule_bh\n", __FUNCTION__); ++ isotest.closing = 0; ++ schedule_bh(); ++ return 0; ++} ++ ++static void isotest_function_disable (struct usbd_function_instance *function) ++{ ++ isotest.closing = 1; ++ while (isotest.iso_bh.data) { ++ //printk(KERN_INFO"%s: calling schedule_bh\n", __FUNCTION__); ++ schedule_bh(); ++ //printk(KERN_INFO"%s: waiting\n", __FUNCTION__); ++ sleep_on_timeout(&isotest.iso_wq, 20); ++ } ++ MOD_DEC_USE_COUNT; ++} ++ ++ ++struct usbd_function_operations function_ops = { ++ event_handler:isotest_event_handler, ++ function_enable: isotest_function_enable, ++ function_disable: isotest_function_disable, ++}; ++ ++struct usbd_function_driver function_driver = { ++ name:"ISO Test Function", ++ fops:&function_ops, ++ device_description:&isotest_device_description, ++ bNumConfigurations:sizeof (isotest_description) / sizeof (struct usbd_configuration_description), ++ configuration_description:isotest_description, ++ idVendor: __constant_cpu_to_le16(CONFIG_OTG_ISOTEST_VENDORID), ++ idProduct: __constant_cpu_to_le16(CONFIG_OTG_ISOTEST_PRODUCTID), ++ bcdDevice: __constant_cpu_to_le16(CONFIG_OTG_ISOTEST_BCDDEVICE), ++ bNumInterfaces:sizeof (isotest_interfaces) / sizeof (struct usbd_interface_description), ++ interface_list:isotest_interfaces, ++ endpointsRequested: ENDPOINTS, ++ requestedEndpoints: iso_endpoint_requests, ++}; ++ ++ ++/* ++ * isotest_modinit - module init ++ * ++ */ ++static int isotest_modinit (void) ++{ ++ printk (KERN_INFO "vendor_id: %04x product_id: %04x in: %02x out: %02x\n", ++ vendor_id, product_id, in, out); ++ ++ if (vendor_id) ++ function_driver.idVendor = cpu_to_le16(vendor_id); ++ ++ if (product_id) ++ function_driver.idProduct = cpu_to_le16(product_id); ++ ++ ++ isotest_ep_1[4] = out&0xff; ++ isotest_ep_1[5] = (out>>8)&0xff; ++ isotest_ep_2[4] = in&0xff; ++ isotest_ep_2[5] = (in>>8)&0xff; ++ ++ // XXX should this be the endpoint request structure? ++ iso_endpoint_requests[0].fs_requestedTransferSize = out; ++ iso_endpoint_requests[1].fs_requestedTransferSize = in; ++ ++ iso_trace_init("isotest_fd"); ++ if(fermat){ ++ fermat_init(); ++ } ++ ++ isotest.iso_bh.routine = bottomhalf; ++ isotest.iso_bh.data = (void *)&isotest; ++ init_waitqueue_head(&isotest.iso_wq); ++ ++ // register us with the usb device support layer ++ RETURN_EINVAL_IF (usbd_register_function (&function_driver, "isotest", NULL)); ++ ++ // return ++ return 0; ++} ++ ++ ++/* isotest_modexit - module cleanup ++ */ ++static void isotest_modexit (void) ++{ ++ usbd_deregister_function (&function_driver); ++ iso_trace_exit("isotest_fd"); ++} ++ ++module_init (isotest_modinit); ++module_exit (isotest_modexit); ++ +diff -uNr linux/drivers/no-otg/functions/isotest/iso_fermat linux/drivers/otg/functions/isotest/iso_fermat +--- linux/drivers/no-otg/functions/isotest/iso_fermat 1970-01-01 01:00:00.000000000 +0100 ++++ linux/drivers/otg/functions/isotest/iso_fermat 2006-09-01 21:41:26.000000000 +0200 +@@ -0,0 +1,34 @@ ++#!/bin/sh ++XFER=$1 ++PATTERN=$2 ++if [ -z "$PATTERN" ] ++then ++ PATTERN="0x1,0x8,1,2,3,4,5,6,7,8" ++fi ++echo "Using PATTERN='$PATTERN'" ++set -x ++ ++insmod /tmp/usbdcore.o ++insmod /tmp/usbdprocfs.o ++ ++insmod /tmp/isotest_fd.o vendor_id=0xfff0 product_id=0xfff1 in=$XFER out=$XFER xmit_pattern="$PATTERN" print_all=0 fermat=1 custom=0 ++ ++insmod /tmp/au1x00_bi.o ++echo "enable" > /proc/usbd-switch ++ ++ ++set +x ++echo -n "INSERT CABLE" ++sleep 15 ++echo -n "; REMOVE CABLE AND PRESS RETURN" ++read junk ++set -x ++cp /proc/isotest_fd client ++cp /proc/isotest_host host ++echo "disable" > /proc/usbd-switch ++ ++rmmod au1x00_bi ++rmmod isotest_fd ++rmmod usbdprocfs ++rmmod usbdcore ++ +diff -uNr linux/drivers/no-otg/functions/isotest/iso_one linux/drivers/otg/functions/isotest/iso_one +--- linux/drivers/no-otg/functions/isotest/iso_one 1970-01-01 01:00:00.000000000 +0100 ++++ linux/drivers/otg/functions/isotest/iso_one 2006-09-01 21:41:26.000000000 +0200 +@@ -0,0 +1,34 @@ ++#!/bin/sh ++XFER=$1 ++PATTERN=$2 ++if [ -z "$PATTERN" ] ++then ++ PATTERN="0x1,0x1,0xFF" ++fi ++echo "Using PATTERN='$PATTERN'" ++set -x ++ ++insmod /tmp/usbdcore.o ++insmod /tmp/usbdprocfs.o ++ ++insmod /tmp/isotest_fd.o vendor_id=0xfff0 product_id=0xfff1 in=$XFER out=$XFER xmit_pattern="$PATTERN" print_all=1 fermat=0 custom=1 ++ ++insmod /tmp/au1x00_bi.o ++echo "enable" > /proc/usbd-switch ++ ++ ++set +x ++echo -n "INSERT CABLE" ++sleep 15 ++echo -n "; REMOVE CABLE AND PRESS RETURN" ++read junk ++set -x ++cp /proc/isotest_fd client ++cp /proc/isotest_host host ++echo "disable" > /proc/usbd-switch ++ ++rmmod au1x00_bi ++rmmod isotest_fd ++rmmod usbdprocfs ++rmmod usbdcore ++ +diff -uNr linux/drivers/no-otg/functions/isotest/iso_zero linux/drivers/otg/functions/isotest/iso_zero +--- linux/drivers/no-otg/functions/isotest/iso_zero 1970-01-01 01:00:00.000000000 +0100 ++++ linux/drivers/otg/functions/isotest/iso_zero 2006-09-01 21:41:26.000000000 +0200 +@@ -0,0 +1,34 @@ ++#!/bin/sh ++XFER=$1 ++PATTERN=$2 ++if [ -z "$PATTERN" ] ++then ++ PATTERN="0x1,0x1,0x00" ++fi ++echo "Using PATTERN='$PATTERN'" ++set -x ++ ++insmod /tmp/usbdcore.o ++insmod /tmp/usbdprocfs.o ++ ++insmod /tmp/isotest_fd.o vendor_id=0xfff0 product_id=0xfff1 in=$XFER out=$XFER xmit_pattern="$PATTERN" print_all=1 fermat=0 custom=1 ++ ++insmod /tmp/au1x00_bi.o ++echo "enable" > /proc/usbd-switch ++ ++ ++set +x ++echo -n "INSERT CABLE" ++sleep 15 ++echo -n "; REMOVE CABLE AND PRESS RETURN" ++read junk ++set -x ++cp /proc/isotest_fd client ++cp /proc/isotest_host host ++echo "disable" > /proc/usbd-switch ++ ++rmmod au1x00_bi ++rmmod isotest_fd ++rmmod usbdprocfs ++rmmod usbdcore ++ +diff -uNr linux/drivers/no-otg/functions/isotest/iso_zero_silent linux/drivers/otg/functions/isotest/iso_zero_silent +--- linux/drivers/no-otg/functions/isotest/iso_zero_silent 1970-01-01 01:00:00.000000000 +0100 ++++ linux/drivers/otg/functions/isotest/iso_zero_silent 2006-09-01 21:41:26.000000000 +0200 +@@ -0,0 +1,34 @@ ++#!/bin/sh ++XFER=$1 ++PATTERN=$2 ++if [ -z "$PATTERN" ] ++then ++ PATTERN="0x1,0x1,0x00" ++fi ++echo "Using PATTERN='$PATTERN'" ++set -x ++ ++insmod /tmp/usbdcore.o ++insmod /tmp/usbdprocfs.o ++ ++insmod /tmp/isotest_fd.o vendor_id=0xfff0 product_id=0xfff1 in=$XFER out=$XFER xmit_pattern="$PATTERN" print_all=0 fermat=0 custom=1 ++ ++insmod /tmp/au1x00_bi.o ++echo "enable" > /proc/usbd-switch ++ ++ ++set +x ++echo -n "INSERT CABLE" ++sleep 15 ++echo -n "; REMOVE CABLE AND PRESS RETURN" ++read junk ++set -x ++cp /proc/isotest_fd client ++cp /proc/isotest_host host ++echo "disable" > /proc/usbd-switch ++ ++rmmod au1x00_bi ++rmmod isotest_fd ++rmmod usbdprocfs ++rmmod usbdcore ++ +diff -uNr linux/drivers/no-otg/functions/isotest/test.c linux/drivers/otg/functions/isotest/test.c +--- linux/drivers/no-otg/functions/isotest/test.c 1970-01-01 01:00:00.000000000 +0100 ++++ linux/drivers/otg/functions/isotest/test.c 2006-09-01 21:41:27.000000000 +0200 +@@ -0,0 +1,456 @@ ++/* ++ * otg/isotest_fd/test.c ++ * ++ * Copyright (c) 2003, 2004 Belcarra ++ * ++ * By: ++ * Stuart Lynne <sl@belcarra.com>, ++ * Tom Rushworth <tbr@belcarra.com>, ++ * ++ */ ++ ++#include <linux/config.h> ++#include <linux/module.h> ++#include <linux/version.h> ++ ++#include <linux/kernel.h> ++//#include <linux/sched.h> ++#include <linux/signal.h> ++#include <linux/errno.h> ++//#include <linux/poll.h> ++#include <linux/init.h> ++ ++#include <asm/system.h> ++#include <asm/atomic.h> ++//#include <linux/interrupt.h> ++//#include <linux/pci.h> ++#include <linux/init.h> ++#include <linux/delay.h> ++ ++#include <linux/proc_fs.h> ++#include <linux/vmalloc.h> ++ ++#include <asm/io.h> ++#include <asm/string.h> ++ ++#include <linux/proc_fs.h> ++ ++#include <linux/netdevice.h> ++#include <linux/cache.h> ++ ++ ++#if defined(CONFIG_ARCH_SA1100) || defined (CONFIG_ARCH_PXA) ++#include <asm/dma.h> ++#include <asm/mach/dma.h> ++#include <asm/irq.h> ++#include <asm/system.h> ++#include <asm/hardware.h> ++#include <asm/types.h> ++#endif ++ ++#if defined(CONFIG_MIPS_AU1000) || defined(CONFIG_MIPS_PB1500) || defined(CONFIG_MIPS_PB1100) ++#include <asm/au1000.h> ++#include <asm/au1000_dma.h> ++#include <asm/mipsregs.h> ++#endif ++ ++#if defined(CONFIG_ARCH_SAMSUNG) ++#include <asm/arch/timers.h> ++#include <asm/arch/hardware.h> ++#endif ++ ++#include <asm/uaccess.h> ++#include <asm/io.h> ++#include <asm/pgtable.h> ++#include <asm/pgalloc.h> ++#include "test.h" ++ ++ ++int iso_trace_first; ++int iso_trace_next; ++iso_trace_t *iso_traces; ++ ++ ++/* ******************************************************************************************* */ ++ ++void dump_stats(struct isotest_stats *stats) ++{ ++ if (stats->errors || ((stats->ok % 100) == 99)) { ++ TRACE_ISO(stats->iso_transfer_number, stats->total_frames, ++ stats->received, stats->errors, stats->missed, stats->skipped, stats->ok); ++ } ++ if (stats->errors) { ++ memset(stats, 0, sizeof(struct isotest_stats)); ++ } ++ else { ++ stats->ok++; ++ stats->iso_transfer_number++; ++ stats->last_packet = 0; ++ } ++} ++ ++u16 getu16(u8 **cp) ++{ ++ u16 val = 0; ++ ++ val = *(*cp)++; ++ val |= *(*cp)++ << 8; ++ ++ return val; ++} ++ ++u16 getu32(u8 **cp) ++{ ++ u32 val = 0; ++ ++ val = *(*cp)++; ++ val |= *(*cp)++ << 8; ++ val |= *(*cp)++ << 16; ++ val |= *(*cp)++ << 24; ++ ++ return val; ++} ++ ++static int iso_in_received; ++ ++void iso_trace_recv_data (struct isotest_stats *stats, u8 *cp, int length, int framenum) ++{ ++ //struct usb_device_instance *device = urb->device; ++ ++ u16 iso_transfer_number; ++ u16 total_size; ++ u16 packet_size; ++ u16 total_frames; ++ u16 this_packet; ++ u8 frames; ++ ++ ++ iso_transfer_number = getu32(&cp); ++ total_size = getu16(&cp); ++ packet_size = getu16(&cp); ++ total_frames = getu16(&cp); ++ this_packet = getu16(&cp); ++ ++ ++ // did we miss the end of the last ISO transfer? ++ if (stats->iso_transfer_number && (stats->iso_transfer_number != iso_transfer_number)) { ++ stats->errors++; ++ stats->missed++; ++ //printk(KERN_INFO"%s: ERROR bad iso number: %x %x %x %x %x expecting: %x\n", __FUNCTION__, iso_transfer_number, ++ // total_size, packet_size, total_frames, this_packet, stats->iso_transfer_number); ++ dump_stats(stats); ++ return; ++ } ++ ++ if (!stats->iso_transfer_number) { ++ stats->framenum = framenum; ++ stats->iso_transfer_number = iso_transfer_number; ++ stats->total_frames = total_frames; ++ } ++ ++ // is this the packet we are expecting? ++ if (stats->last_packet && ((stats->last_packet + 1) != this_packet)) { ++ stats->errors++; ++ stats->missed++; ++ //printk(KERN_INFO"%s: ERROR bad packet: %x %x %x %x %x expecting: %x\n", __FUNCTION__, iso_transfer_number, ++ // total_size, packet_size, total_frames, this_packet, stats->last_packet + 1); ++ } ++ ++ // did we miss a frame? ++ frames = (stats->framenum < framenum) ? (framenum - stats->framenum) : (stats->framenum - framenum); ++ ++ if (frames > 1) { ++ stats->errors++; ++ stats->skipped++; ++ //printk(KERN_INFO"%s: SKIPPED %x %x %x %x %x\n", __FUNCTION__, iso_transfer_number, ++ // total_size, packet_size, total_frames, this_packet); ++ } ++ ++ // update stats ++ stats->received++; ++ stats->last_packet = this_packet; ++ ++ // last packet? ++ if (stats->total_frames == this_packet) ++ dump_stats(stats); ++} ++ ++ ++ ++/* Proc Filesystem *************************************************************************** */ ++ ++/* * ++ * iso_trace_proc_read - implement proc file system read. ++ * @file ++ * @buf ++ * @count ++ * @pos ++ * ++ * Standard proc file system read function. ++ */ ++static ssize_t iso_trace_proc_read (struct file *file, char *buf, size_t count, loff_t * pos) ++{ ++ unsigned long page; ++ int len = 0; ++ int index; ++ int oindex; ++ int previous; ++ ++ MOD_INC_USE_COUNT; ++ //printk(KERN_INFO"%s: GET_USE_COUNT: %d\n", __FUNCTION__, GET_USE_COUNT(THIS_MODULE)); ++ // get a page, max 4095 bytes of data... ++ if (!(page = get_free_page (GFP_KERNEL))) { ++ MOD_DEC_USE_COUNT; ++ //printk(KERN_INFO"%s: GET_USE_COUNT: %d\n", __FUNCTION__, GET_USE_COUNT(THIS_MODULE)); ++ return -ENOMEM; ++ } ++ ++ len = 0; ++ oindex = index = (*pos)++; ++ ++ if (index == 0) { ++ //len += sprintf ((char *) page + len, " uS Exp Rcv Err Miss Skip OK\n"); ++ //len += sprintf ((char *) page + len, " uS Exp Rcv Err Miss Skip Xfers\n"); ++ len += sprintf ((char *) page + len, " uS Tr Exp Rcv Err Miss Skip Xfers\n"); ++ ++// uS Exp Rcv Err Miss Skip Xfers ++// uS Tr Exp Rcv Err Miss Skip Xfers ++// 0 | 100 9 900 0 0 0 99 ++ ++ } ++ ++ index += iso_trace_first; ++ if (index >= TRACE_MAX) { ++ index -= TRACE_MAX; ++ } ++ previous = (index) ? (index - 1) : (TRACE_MAX - 1); ++ ++ //printk(KERN_INFO"first: %d next: %d index: %d %d prev: %d\n", iso_trace_first, iso_trace_next, oindex, index, previous); ++ ++ if ( ++ ((iso_trace_first < iso_trace_next) && (index >= iso_trace_first) && (index < iso_trace_next)) || ++ ((iso_trace_first > iso_trace_next) && ((index < iso_trace_next) || (index >= iso_trace_first))) ++ ) ++ { ++ ++#if defined(CONFIG_ARCH_SA1100) || defined (CONFIG_ARCH_PXA) || defined(CONFIG_MIPS_AU1000) || defined(CONFIG_MIPS_PB1500) || defined(CONFIG_MIPS_PB1100) ++ u32 ticks = 0; ++#elif defined(CONFIG_ARCH_SAMSUNG) ++ u32 ticks = 0; ++#else ++ u64 jifs = 0; ++#endif ++ iso_trace_t *p = iso_traces + index; ++ //unsigned char *cp; ++ //int skip = 0; ++ ++ if (oindex > 0) { ++ iso_trace_t *o = iso_traces + previous; ++ ++#if defined(CONFIG_ARCH_SA1100) || defined (CONFIG_ARCH_PXA) ++ /* ++ * oscr is 3.6864 Mhz free running counter, ++ * ++ * 1/3.6864 = .2712 ++ * 60/221 = .2714 ++ * ++ */ ++ if (o->ocsr) { ++ ticks = (p->ocsr > o->ocsr) ? (p->ocsr - o->ocsr) : (o->ocsr - p->ocsr) ; ++ ticks = (ticks * 60) / 221; ++ } ++ ++#elif defined(CONFIG_MIPS_AU1000) || defined(CONFIG_MIPS_PB1500) || defined(CONFIG_MIPS_PB1100) ++ /* ++ * cp0_count is incrementing timer at system clock ++ */ ++ if (o->cp0_count) { ++ //ticks = (p->cp0_count > o->cp0_count) ? ++ // (p->cp0_count - o->cp0_count) : (o->cp0_count - p->cp0_count) ; ++ ticks = (p->cp0_count - o->cp0_count) ; ++ ticks = ticks / CONFIG_OTG_AU1X00_SCLOCK; ++ } ++ ++#elif defined(CONFIG_ARCH_SAMSUNG) ++ /* ++ * tcnt1 is a count-down timer running at the system bus clock ++ * The divisor must be set as a configuration value, typically 66 or 133. ++ */ ++ if (o->tcnt1) { ++ ticks = (p->tcnt1 < o->tcnt1) ? (o->tcnt1 - p->tcnt1) : (p->tcnt1 - o->tcnt1) ; ++ ticks /= CONFIG_OTG_SMDK2500_BCLOCK; ++ } ++#else ++ if (o->jiffies) { ++ jifs = p->jiffies - iso_traces[previous].jiffies; ++ } ++#endif ++ ++ //if (o->interrupts != p->interrupts) { ++ // skip++; ++ //} ++ } ++ ++ ++#if defined(CONFIG_ARCH_SA1100) || defined (CONFIG_ARCH_PXA) || defined(CONFIG_MIPS_AU1000) || defined(CONFIG_MIPS_PB1500) || defined(CONFIG_MIPS_PB1100) ++ len += sprintf ((char *) page + len, "%9d ", ticks); ++#elif defined(CONFIG_ARCH_SAMSUNG) ++ //len += sprintf ((char *) page + len, "%8u ", p->jiffies); ++ //len += sprintf ((char *) page + len, "%8u ", p->tcnt0); ++ len += sprintf ((char *) page + len, "%8u ", p->tcnt1); ++ if (ticks > 1024*1024) { ++ len += sprintf ((char *) page + len, "%8dM ", ticks>>20); ++ } ++ else { ++ len += sprintf ((char *) page + len, "%8d ", ticks); ++ } ++#else ++ if (jifs > 1024) { ++ len += sprintf ((char *) page + len, "%4dK", (int)jifs>>20); ++ } ++ else { ++ len += sprintf ((char *) page + len, "%4d ", (int)jifs); ++ } ++#endif ++ ++ len += sprintf ((char *) page + len, "| %8d", p->iso_transfer_number); ++ len += sprintf ((char *) page + len, "%6d", p->expected); ++ len += sprintf ((char *) page + len, "%6d", p->received); ++ len += sprintf ((char *) page + len, "%6d", p->errors); ++ len += sprintf ((char *) page + len, "%6d", p->missed); ++ len += sprintf ((char *) page + len, "%6d", p->skipped); ++ len += sprintf ((char *) page + len, "%6d", p->ok); ++ len += sprintf ((char *) page + len, "\n"); ++ } ++ ++#if 1 ++ if ((len > count) ) { ++ printk(KERN_INFO"%s: ((len > count) count=%d len=%d\n", __FUNCTION__, count, len); ++ len = -EINVAL; ++ } ++ ++#else ++ if ((len > count) || (len == 0)) { ++ printk(KERN_INFO"%s: ((len > count) || (len == 0)) count=%d len=%d\n", __FUNCTION__, count, len); ++ len = -EINVAL; ++ } ++#endif ++ else if (len > 0 && copy_to_user (buf, (char *) page, len)) { ++ printk(KERN_INFO"%s: (len > 0 && copy_to_user (buf, (char *) page, len) \n", __FUNCTION__); ++ len = -EFAULT; ++ } ++ free_page (page); ++ MOD_DEC_USE_COUNT; ++ //printk(KERN_INFO"%s: GET_USE_COUNT: %d\n", __FUNCTION__, GET_USE_COUNT(THIS_MODULE)); ++ return len; ++} ++ ++void iso_start_in(int count); ++void iso_start_out(int count); ++ ++/* * ++ * iso_trace_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 iso_trace_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_INFO"%s: GET_USE_COUNT: %d\n", __FUNCTION__, GET_USE_COUNT(THIS_MODULE)); ++ //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, "in", 2)) { ++ iso_start_in (10); ++ } ++ else if (!strncmp (command, "out", 3)) { ++ iso_start_out (10); ++ } ++ // XXX need to be able to set serial number here ++ MOD_DEC_USE_COUNT; ++ //printk(KERN_INFO"%s: GET_USE_COUNT: %d\n", __FUNCTION__, GET_USE_COUNT(THIS_MODULE)); ++ return (count); ++} ++ ++static struct file_operations iso_trace_proc_operations_functions = { ++read:iso_trace_proc_read, ++write:iso_trace_proc_write, ++}; ++ ++#if defined(CONFIG_ARCH_SAMSUNG) ++#endif ++ ++/** ++ * iso_trace_init ++ * ++ * Return non-zero if not successful. ++ */ ++int iso_trace_init (char *name) ++{ ++ printk(KERN_INFO"%s:\n", __FUNCTION__); ++ if (!(iso_traces = vmalloc(sizeof(iso_trace_t) * TRACE_MAX))) { ++ printk(KERN_ERR"%s: malloc failed %p %d\n", __FUNCTION__, iso_traces, sizeof(iso_trace_t) * TRACE_MAX); ++ return -EINVAL; ++ } ++ memset(iso_traces, 0, sizeof(iso_trace_t) * TRACE_MAX); ++ ++ { ++ struct proc_dir_entry *p; ++ ++ // create proc filesystem entries ++ if ((p = create_proc_entry (name, 0, 0)) == NULL) { ++ printk(KERN_INFO"BITRACE PROC FS failed\n"); ++ } ++ else { ++ p->proc_fops = &iso_trace_proc_operations_functions; ++ } ++ } ++#if defined(CONFIG_ARCH_SAMSUNG) ++ *(volatile u32 *)TMOD |= 0x3 << 3; ++#endif ++ printk(KERN_INFO"%s: OK\n", __FUNCTION__); ++ return 0; ++} ++ ++/** ++ * udc_release_io - release UDC io region ++ */ ++void iso_trace_exit (char *name) ++{ ++ { ++ unsigned long flags; ++ local_irq_save (flags); ++ remove_proc_entry (name, NULL); ++ if (iso_traces) { ++ iso_trace_t *p = iso_traces; ++ iso_traces = 0; ++ vfree(p); ++ } ++ local_irq_restore (flags); ++ } ++} ++ ++ ++/* End of FILE */ ++ +diff -uNr linux/drivers/no-otg/functions/isotest/test.h linux/drivers/otg/functions/isotest/test.h +--- linux/drivers/no-otg/functions/isotest/test.h 1970-01-01 01:00:00.000000000 +0100 ++++ linux/drivers/otg/functions/isotest/test.h 2006-09-01 21:41:27.000000000 +0200 +@@ -0,0 +1,129 @@ ++/* ++ * otg/isotest_fd/test.h ++ * ++ * Copyright (c) 2003, 2004 Belcarra ++ * ++ * By: ++ * Stuart Lynne <sl@belcarra.com>, ++ * Bruce Balden <balden@belcarra.com>, ++ * Tom Rushworth <tbr@belcarra.com>, ++ * ++ */ ++ ++#if defined(CONFIG_ARCH_SAMSUNG) ++#ifndef CONFIG_OTG_SMDK2500_BCLOCK ++#define CONFIG_OTG_SMDK2500_BCLOCK 66 ++#endif ++#endif ++ ++#define NUSBD_FRAMENUM 0xB0200038 ++ ++ ++struct isotest_stats { ++ ++ u16 count; ++ ++ u16 iso_transfer_number; ++ u16 total_size; ++ u16 packet_size; ++ u16 total_frames; ++ u16 last_packet; ++ ++ ++ u16 framenum; ++ u16 received; ++ u16 missed; ++ u16 skipped; ++ u16 errors; ++ ++ u32 ok; ++ ++}; ++ ++ ++ ++ ++ ++typedef struct iso_trace_types { ++#if defined(CONFIG_ARCH_SA1100) || defined (CONFIG_ARCH_PXA) ++ u32 ocsr; ++#elif defined(CONFIG_MIPS_AU1000) || defined(CONFIG_MIPS_PB1500) || defined(CONFIG_MIPS_PB1100) ++ u32 cp0_count; ++#elif defined(CONFIG_ARCH_SAMSUNG) ++ u32 tcnt1; ++#else ++ u64 jiffies; ++#endif ++ u16 iso_transfer_number; ++ ++ u16 expected; ++ u16 received; ++ u16 errors; ++ u16 missed; ++ u16 skipped; ++ ++ u32 ok; ++ ++} iso_trace_t; ++ ++ ++#define TRACE_MAX 3000 ++ ++extern int iso_trace_first; ++extern int iso_trace_next; ++ ++extern iso_trace_t *iso_traces; ++ ++static __inline__ iso_trace_t *ISO_TRACE_NEXT(void) ++{ ++ iso_trace_t *p; ++ ++ p = iso_traces + iso_trace_next; ++ ++#if defined(CONFIG_ARCH_SA1100) || defined (CONFIG_ARCH_PXA) ++ p->ocsr = OSCR; ++#elif defined(CONFIG_MIPS_AU1000) || defined(CONFIG_MIPS_PB1500) || defined(CONFIG_MIPS_PB1100) ++ p->cp0_count = read_c0_count(); ++#elif defined(CONFIG_ARCH_SAMSUNG) ++ p->tcnt1 = *(volatile u32 *)TCNT1; ++#else ++ p->jiffies = jiffies; ++#endif ++//#if defined(CONFIG_MIPS_AU1000) || defined(CONFIG_MIPS_PB1500) || defined(CONFIG_MIPS_PB1100) ++// p->iso_transfer_number = au_readl(NUSBD_FRAMENUM); ++//#endif ++ ++ iso_trace_next++; ++ iso_trace_next = (iso_trace_next == TRACE_MAX) ? 0 : iso_trace_next; ++ ++ if (iso_trace_next == iso_trace_first) { ++ iso_trace_first++; ++ iso_trace_first = (iso_trace_first == TRACE_MAX) ? 0 : iso_trace_first; ++ } ++ ++ return p; ++} ++ ++static __inline__ void TRACE_ISO(u16 iso_transfer_number, u16 expected, u16 received, u16 errors, u16 missed, u16 skipped, u32 ok) ++{ ++ iso_trace_t *p = NULL; ++ ++ //printk(KERN_INFO"%s: %d %d %d %d %d first: %d next: %d traces: %p\n", __FUNCTION__, ++ // expected, received, errors, missed, skipped, iso_trace_first, iso_trace_next, traces); ++ ++ if (!iso_traces) return; ++ ++ p = ISO_TRACE_NEXT(); ++ p->iso_transfer_number = iso_transfer_number; ++ p->expected = expected; ++ p->received = received; ++ p->errors = errors; ++ p->missed = missed; ++ p->skipped = skipped; ++ p->ok = ok; ++} ++ ++void iso_trace_recv_data (struct isotest_stats *stats, u8 *cp, int length, int framenum); ++int iso_trace_init (char *str); ++void iso_trace_exit (char *str); ++ +diff -uNr linux/drivers/no-otg/functions/mouse/Config.in linux/drivers/otg/functions/mouse/Config.in +--- linux/drivers/no-otg/functions/mouse/Config.in 1970-01-01 01:00:00.000000000 +0100 ++++ linux/drivers/otg/functions/mouse/Config.in 2006-09-01 21:41:27.000000000 +0200 +@@ -0,0 +1,28 @@ ++# ++# Mouse Function Driver ++# ++# Copyright (C) 2003 Belcarra ++# ++ ++ ++mainmenu_option next_comment ++ ++comment "USB Peripheral Function Driver - Random Mouse" ++dep_tristate ' Mouse Function' CONFIG_OTG_MOUSE $CONFIG_OTG ++ ++if [ "$CONFIG_OTG_MOUSE" != "n" ]; then ++ hex 'VendorID (hex value)' CONFIG_OTG_MOUSE_VENDORID "15ec" ++ hex 'ProductID (hex value)' CONFIG_OTG_MOUSE_PRODUCTID "f003" ++ hex 'bcdDevice (binary-coded decimal)' CONFIG_OTG_MOUSE_BCDDEVICE "0100" ++ ++ string 'iManufacturer (string)' CONFIG_OTG_MOUSE_MANUFACTURER "Belcarra" ++ #string 'iProduct (string)' CONFIG_OTG_MOUSE_PRODUCT_NAME "Belcarra Mouse" ++ ++ string 'iConfiguration (string)' CONFIG_OTG_MOUSE_DESC "Mouse Cfg" ++ string 'Comm Interface iInterface (string)' CONFIG_OTG_MOUSE_COMM_INTF "Comm Intf" ++ bool 'Mouse BH Test' CONFIG_OTG_MOUSE_BH ++ int 'Number of packets (zero is continous ' CONFIG_OTG_MOUSE_PACKETS "10" ++ int 'Polling Interval ' CONFIG_OTG_MOUSE_INTERVAL "1" ++ ++fi ++endmenu +diff -uNr linux/drivers/no-otg/functions/mouse/Kconfig linux/drivers/otg/functions/mouse/Kconfig +--- linux/drivers/no-otg/functions/mouse/Kconfig 1970-01-01 01:00:00.000000000 +0100 ++++ linux/drivers/otg/functions/mouse/Kconfig 2006-09-01 21:41:27.000000000 +0200 +@@ -0,0 +1,74 @@ ++menu "OTG Random Mouse function" ++ depends on OTG ++ ++config OTG_MOUSE ++ tristate " Random Mouse Function" ++ depends on OTG ++ ---help--- ++ This implements a simple Mouse driver that sends HID packets with ++ small random movements. This is a good function for initial testing ++ of Peripheral Controller Drivers as it provides for a complicated ++ enumeration but a simple uni-directional (send Interrupt only) data ++ transport. ++ ++menu "OTG Random Mouse function options" ++ depends on OTG && OTG_MOUSE ++ ++config OTG_MOUSE_VENDORID ++ hex "VendorID (hex value)" ++ depends on OTG && OTG_MOUSE ++ default "0x15ec" ++ ++config OTG_MOUSE_PRODUCTID ++ hex "ProductID (hex value)" ++ depends on OTG && OTG_MOUSE ++ default "0xf003" ++ ++config OTG_MOUSE_BCDDEVICE ++ hex "bcdDevice (binary-coded decimal)" ++ depends on OTG && OTG_MOUSE ++ default "0x0100" ++ ++config OTG_MOUSE_MANUFACTURER ++ string "iManufacturer (string)" ++ depends on OTG && OTG_MOUSE ++ default "Belcarra" ++ ++config OTG_MOUSE_PRODUCT_NAME ++ string "iProduct (string)" ++ depends on OTG && OTG_MOUSE ++ default "Random Mouse Class - Interrupt" ++ ++config OTG_MOUSE_COMM_INTF ++ string "MOUSE Bulk Only iInterface (string)" ++ depends on OTG && OTG_MOUSE ++ default "MOUSE Data Intf" ++ ++config OTG_MOUSE_DESC ++ string "Data Interface iConfiguration (string)" ++ depends on OTG && OTG_MOUSE ++ default "MOUSE Configuration" ++ ++config OTG_MOUSE_BH ++ bool " MOUSE BH Test" ++ depends on OTG && OTG_MOUSE ++ default n ++ ---help--- ++ Implement the Mouse send packet in a bottom half handler, ++ this will delay responses to test the PCD works correctly ++ when the host polls before a send data urb is queued. ++ ++ ++config OTG_MOUSE_PACKETS ++ int "Number of packets (zero is continous)" ++ depends on OTG && OTG_MOUSE ++ default 10 ++ ---help--- ++ Number of Mouse packets to send, will run ++ forever if set to zero. ++ ++ ++endmenu ++ ++ ++endmenu +diff -uNr linux/drivers/no-otg/functions/mouse/Makefile linux/drivers/otg/functions/mouse/Makefile +--- linux/drivers/no-otg/functions/mouse/Makefile 1970-01-01 01:00:00.000000000 +0100 ++++ linux/drivers/otg/functions/mouse/Makefile 2006-09-01 21:41:27.000000000 +0200 +@@ -0,0 +1,81 @@ ++# ++# Function driver for a Mouse USB Device ++# ++# Copyright (c) 2003 Belcarra ++ ++# Multipart objects. ++ ++O_TARGET := mouse_target.o ++#list-multi := mouse_fd.o cmouse_fd.o ++list-multi := mouse_fd.o ++ ++mouse_fd-objs := mouse-fd.o mouse-l24.o ++#cmouse_fd-objs := mouse-cf.o mouse-if.o cmouse-l24.o ++#cmouse_fd-objs := mouse-if.o cmouse-l24.o ++ ++# Objects that export symbols. ++#export-objs := mouse.o ++ ++# Object file lists. ++ ++obj-y := ++obj-m := ++obj-n := ++obj- := ++ ++# Each configuration option enables a list of files. ++ ++obj-$(CONFIG_OTG_MOUSE) += mouse_fd.o ++#obj-$(CONFIG_OTG_MOUSE) += cmouse_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. ++ ++#MOUSED=$(OTG)/functions/mouse ++ ++OTG_WARNINGS=-Wno-unused -Wno-format ++OTG=$(TOPDIR)/drivers/otg ++OTG_INCLUDES=-I$(OTG) ++OTG_EXTRA=$(OTG_INCLUDES) $(OTG_WARNINGS) ++OTGCORE_DIR=$(OTG)/otgcore ++##USBDCORE_DIR=$(OTG)/usbdcore ++include $(TOPDIR)/Rules.make ++#EXTRA_CFLAGS += -I$(OTG) -Wno-unused -Wno-format -I$(USBDCORE_DIR) -I$(OTGCORE_DIR) ++#EXTRA_CFLAGS_nostdinc += -I$(OTG) -Wno-unused -Wno-format -I$(USBDCORE_DIR) -I$(OTGCORE_DIR) ++EXTRA_CFLAGS += $(OTG_EXTRA) ++EXTRA_CFLAGS_nostdinc += $(OTG_EXTRA) ++ ++# Link rules for multi-part drivers. ++ ++mouse_fd.o: $(mouse_fd-objs) ++ $(LD) -r -o $@ $(mouse_fd-objs) ++ ++#cmouse_fd.o: $(cmouse_fd-objs) ++# $(LD) -r -o $@ $(cmouse_fd-objs) ++ ++# dependencies: ++ ++#mouse.o: $(USBDCORE_DIR)/usbd.h $(USBDCORE_DIR)/usbd-bus.h $(USBDCORE_DIR)/usbd-func.h ++ ++ +diff -uNr linux/drivers/no-otg/functions/mouse/Makefile-l26 linux/drivers/otg/functions/mouse/Makefile-l26 +--- linux/drivers/no-otg/functions/mouse/Makefile-l26 1970-01-01 01:00:00.000000000 +0100 ++++ linux/drivers/otg/functions/mouse/Makefile-l26 2006-09-01 21:41:27.000000000 +0200 +@@ -0,0 +1,14 @@ ++# Function driver for a Random Mouse ++# ++# Copyright (c) 2004 Belcarra ++ ++mouse_fd-objs := mouse-fd.o mouse-l24.o ++ ++obj-$(CONFIG_OTG_MOUSE) += mouse_fd.o ++ ++OTG=$(TOPDIR)/drivers/otg ++ACMD=$(OTG)/functions/mouse ++OTGCORE_DIR=$(OTG)/otgcore ++USBDCORE_DIR=$(OTG)/usbdcore ++EXTRA_CFLAGS += -I$(ACMD) -I$(OTG) -Wno-unused -Wno-format -I$(USBDCORE_DIR) -I$(OTGCORE_DIR) ++EXTRA_CFLAGS_nostdinc += -I$(ACMD) -I$(OTG) -Wno-unused -Wno-format -I$(USBDCORE_DIR) -I$(OTGCORE_DIR) +diff -uNr linux/drivers/no-otg/functions/mouse/cmouse-l24.c linux/drivers/otg/functions/mouse/cmouse-l24.c +--- linux/drivers/no-otg/functions/mouse/cmouse-l24.c 1970-01-01 01:00:00.000000000 +0100 ++++ linux/drivers/otg/functions/mouse/cmouse-l24.c 2006-09-01 21:41:27.000000000 +0200 +@@ -0,0 +1,199 @@ ++/* ++ * otg/functions/mouse/cmouse-l24.c ++ * ++ * Copyright (c) 2003, 2004 Belcarra ++ * ++ * By: ++ * Stuart Lynne <sl@belcarra.com>, ++ * Bruce Balden <balden@belcarra.com> ++ * ++ */ ++/*! ++ * @file otg/functions/mouse/cmouse-l24.c ++ * @brief Mouse Function Driver Linux 2.4 initialization ++ * ++ * ++ * This file contains the Linux 2.4 module init and exit functions ++ * to load and unload the Random Mouse Function. ++ * ++ * Notes ++ * ++ * This driver does not export any upper edge functionality to ++ * for the user or applications to use. It simply sends a configurable ++ * number of data packets to the USB Host when configured. Each data ++ * packet contains the equivalent of a small mouse report which will ++ * cause the mouse cursor to move on the host. ++ * ++ * To use simply load with something like: ++ * ++ * insmod cmouse_fd.o vendor_id=0xffff product_id=0xffff ++ * ++ * And attach to a Windows box. Windows should recognize as a mouse and ++ * immediately start receiving a stream of data. ++ * ++ * To terminate simply unplug. ++ * ++ * ++ * 1. The mouse driver is product name is hard coded to a string that will ++ * generate a string descriptor that is 32 bytes long. This will test ++ * most UDC's ep0 ZLP handling as it is a multiple of the most common ++ * UDC endpoint zero size. (An option can be added later to allow for ++ * 64 byte ep0 packetsize.) ++ * ++ * 2. The CONFIG_OTG_MOUSE_BH option can be enabled to delay the HID report ++ * to being generated by a bottom half handler. This will verify that ++ * the bus interface driver properly handles the case of a delayed ++ * CONTROL READ. I.e. when the usbd_device_request() function returns ++ * zero for successful completion but there is no tx_urb containing the ++ * requested data. The bus interface driver must setup the conditions for ++ * ACK'ing the SETUP packet but then NAK the IN data for endpoint zero ++ * until the tx_urb is started later. ++ * ++ * 3. The CONFIG_OTG_MOUSE_PACKETS option is used to set the number of ++ * mouse data reports to send. Set to zero for a continous stream ++ * of data. ++ * ++ * @ingroup MouseFunction ++ */ ++ ++#include <otg/otg-compat.h> ++#include <otg/otg-module.h> ++ ++#include <otg/usbp-chap9.h> ++#include <otg/usbp-func.h> ++#include <otg/otg-trace.h> ++ ++#include "cmouse.h" ++ ++ ++/* Module Parameters ******************************************************** */ ++/* ! ++ * @name XXXXX MODULE Parameters ++ */ ++/* ! @{ */ ++ ++MOD_AUTHOR ("sl@belcarra.com"); ++EMBED_LICENSE(); ++MOD_DESCRIPTION ("Belcarra Random Walk MOUSE Function"); ++ ++static u32 vendor_id; /*!< override built-in vendor ID */ ++static u32 product_id; /*!< override built-in product ID */ ++ ++MOD_PARM (vendor_id, "i"); ++MOD_PARM (product_id, "i"); ++ ++MOD_PARM_DESC (vendor_id, "Device Vendor ID"); ++MOD_PARM_DESC (product_id, "Device Product ID"); ++ ++otg_tag_t MOUSE; ++/* ! *} */ ++ ++/* USB Module init/exit ***************************************************** */ ++ ++struct mouse_private mouse_private; ++ ++char *mouse_interface_list[2] = { ++ "mouse-random-interface", ++ NULL, ++}; ++ ++/*! ++ * mouse_modinit() - module init ++ * ++ * This is called by the Linux kernel; either when the module is loaded ++ * if compiled as a module, or during the system intialization if the ++ * driver is linked into the kernel. ++ * ++ * This function will parse module parameters if required and then register ++ * the mouse driver with the USB Device software. ++ * ++ * If the CONFIG_OTG_MOUSE_BH option is enabled it will also setup the mouse ++ * bottom half handler. ++ * ++ */ ++static int mouse_modinit (void) ++{ ++ struct usbd_function_instance *mfi; ++ printk (KERN_INFO "%s: vendor_id: %04x product_id: %04x\n", __FUNCTION__, vendor_id, product_id); ++ ++ #if !defined(OTG_C99) ++ mouse_global_init(); ++ mouse_ops_init(); ++ #endif /* defined(OTG_C99) */ ++ ++ MOUSE = otg_trace_obtain_tag(); ++ TRACE_MSG2(MOUSE, "vendor_id: %04x product_id: %04x",vendor_id, product_id); ++ ++ if (vendor_id) ++ mouse_composite_driver.idVendor = cpu_to_le16(vendor_id); ++ if (product_id) ++ mouse_composite_driver.idProduct = cpu_to_le16(product_id); ++ ++ mouse_hid.wItemLength = cpu_to_le16(0x34); // XXX mips compiler bug..... ++ ++ // register as usb function driver ++ TRACE_MSG2(MOUSE, "%s %d", mouse_interface_driver.name, mouse_interface_driver.endpointsRequested); ++ THROW_UNLESS ((mouse_private.interface = ++ usbd_register_interface (&mouse_interface_driver, "mouse-random-interface", NULL)), error); ++ ++ TRACE_MSG2(MOUSE, "%s %d", mouse_interface_driver.name, mouse_interface_driver.endpointsRequested); ++ ++ THROW_UNLESS ((mouse_private.composite = ++ usbd_register_composite (&mouse_composite_driver, "mouse-random-cf", ++ mouse_interface_list, NULL)), error); ++ ++ #ifdef CONFIG_OTG_MOUSE_BH ++ mouse_private.notification_bh.routine = mouse_hid_bh; ++ mouse_private.notification_bh.data = NULL; ++ #endif ++ CATCH(error) { ++ if (mouse_private.interface) { ++ usbd_deregister_function (mouse_private.interface); ++ mouse_private.interface = NULL; ++ } ++ if (mouse_private.composite) { ++ usbd_deregister_function (mouse_private.composite); ++ mouse_private.composite = NULL; ++ } ++ otg_trace_invalidate_tag(MOUSE); ++ return -EINVAL; ++ } ++ return 0; ++} ++ ++module_init (mouse_modinit); ++ ++#if OTG_EPILOGUE ++/*! ++ * mouse_modexit() - module init ++ * ++ * This is called by the Linux kernel; when the module is being unloaded ++ * if compiled as a module. This function is never called if the ++ * driver is linked into the kernel. ++ * ++ * @param void ++ * @return void ++ */ ++static void mouse_modexit (void) ++{ ++ #ifdef CONFIG_OTG_MOUSE_BH ++ while (PENDING_WORK_ITEM(mouse_private.notification_bh)) { ++ printk(KERN_ERR"%s: waiting for bh\n", __FUNCTION__); ++ schedule_timeout(10 * HZ); ++ } ++ #endif ++ if (mouse_private.interface) { ++ usbd_deregister_function (mouse_private.interface); ++ mouse_private.interface = NULL; ++ } ++ if (mouse_private.composite) { ++ usbd_deregister_function (mouse_private.composite); ++ mouse_private.composite = NULL; ++ } ++ ++ otg_trace_invalidate_tag(MOUSE); ++} ++ ++module_exit (mouse_modexit); ++#endif ++ +diff -uNr linux/drivers/no-otg/functions/mouse/cmouse.h linux/drivers/otg/functions/mouse/cmouse.h +--- linux/drivers/no-otg/functions/mouse/cmouse.h 1970-01-01 01:00:00.000000000 +0100 ++++ linux/drivers/otg/functions/mouse/cmouse.h 2006-09-01 21:41:27.000000000 +0200 +@@ -0,0 +1,64 @@ ++/* ++ * otg/functions/mouse/mouse.h ++ * ++ * Copyright (c) 2003, 2004 Belcarra ++ * ++ * By: ++ * Stuart Lynne <sl@belcarra.com>, ++ * Tom Rushworth <tbr@belcarra.com>, ++ * Bruce Balden <balden@belcarra.com> ++ * ++ */ ++ ++/*! ++ * @defgroup MouseFunction Random Mouse ++ * @ingroup functiongroup ++ */ ++/*! ++ * @file otg/functions/mouse/mouse.h ++ * @brief Mouse Function Driver private defines ++ * ++ * ++ * This is a test USB HID Function Driver designed to test and ++ * verify that INTERRUPT IN endpoints work properly. ++ * ++ * ++ * @ingroup MouseFunction ++ */ ++ ++ ++/*! ++ * The mouse_private structure is used to collect all of the mouse driver ++ * global variables into one place. ++ */ ++struct mouse_private { ++ struct usbd_function_instance *interface; /*!< function instance for this module */ ++ struct usbd_function_instance *composite; /*!< function instance for this module */ ++ ++#ifdef CONFIG_OTG_MOUSE_BH ++ struct WORK_STRUCT notification_bh; ++#endif /* CONFIG_OTG_MOUSE_BH */ ++ int usb_driver_registered; /*!< non-zero if usb function registered */ ++ unsigned char connected; /*!< non-zero if connected to host (configured) */ ++ unsigned int writesize; /*!< packetsize * 4 */ ++ struct usbd_urb *tx_urb; /*!< saved copy of current tx urb */ ++ int wLength; ++ int x; ++ int y; ++ int last_x; ++ int last_y; ++ int n; ++}; ++ ++extern struct mouse_private mouse_private; ++extern struct usbd_function_driver mouse_interface_driver; ++extern struct usbd_function_driver mouse_composite_driver; ++extern struct hid_descriptor mouse_hid; ++ ++#ifndef OTG_C99 ++extern void mouse_global_init(void); ++#endif /* OTG_C99 */ ++ ++#define MOUSE mouse_trace_tag ++extern otg_tag_t MOUSE; ++ +diff -uNr linux/drivers/no-otg/functions/mouse/getmouse.c linux/drivers/otg/functions/mouse/getmouse.c +--- linux/drivers/no-otg/functions/mouse/getmouse.c 1970-01-01 01:00:00.000000000 +0100 ++++ linux/drivers/otg/functions/mouse/getmouse.c 2006-09-01 21:41:27.000000000 +0200 +@@ -0,0 +1,107 @@ ++/* ++ * ++ * ++ * 3.2. Non-Canonical Input Processing ++ * ++ * In non-canonical input processing mode, input is not assembled into lines and ++ * input processing (erase, kill, delete, etc.) does not occur. Two parameters ++ * control the behavior of this mode: c_cc[VTIME] sets the character timer, and ++ * c_cc[VMIN] sets the minimum number of characters to receive before satisfying ++ * the read. ++ * ++ * If MIN > 0 and TIME = 0, MIN sets the number of characters to receive before ++ * the read is satisfied. As TIME is zero, the timer is not used. ++ * ++ * If MIN = 0 and TIME > 0, TIME serves as a timeout value. The read will be ++ * satisfied if a single character is read, or TIME is exceeded (t = TIME *0.1 ++ * s). If TIME is exceeded, no character will be returned. ++ * ++ * If MIN > 0 and TIME > 0, TIME serves as an inter-character timer. The read ++ * will be satisfied if MIN characters are received, or the time between two ++ * characters exceeds TIME. The timer is restarted every time a character is ++ * received and only becomes active after the first character has been received. ++ * ++ * If MIN = 0 and TIME = 0, read will be satisfied immediately. The number of ++ * characters currently available, or the number of characters requested will be ++ * returned. According to Antonino (see contributions), you could issue a fcntl ++ * (fd, F_SETFL, FNDELAY); before reading to get the same result. ++ * ++ * By modifying newtio.c_cc[VTIME] and newtio.c_cc[VMIN] all modes described ++ * above can be tested. ++ * ++ */ ++ ++#include <sys/types.h> ++#include <sys/stat.h> ++#include <fcntl.h> ++#include <termios.h> ++#include <stdio.h> ++ ++#define BAUDRATE B1200 ++#define MODEMDEVICE "/dev/ttyS0" ++#define _POSIX_SOURCE 1 /* POSIX compliant source */ ++#define FALSE 0 ++#define TRUE 1 ++ ++volatile int STOP=FALSE; ++ ++main() ++{ ++ int fd,c, res; ++ struct termios oldtio,newtio; ++ unsigned char buf[255]; ++ ++ int bytes; ++ unsigned char mouse[3]; ++ ++ fd = open(MODEMDEVICE, O_RDWR | O_NOCTTY ); ++ if (fd <0) {perror(MODEMDEVICE); exit(-1); } ++ ++ tcgetattr(fd,&oldtio); /* save current port settings */ ++ ++ bzero(&newtio, sizeof(newtio)); ++ newtio.c_cflag = BAUDRATE | CRTSCTS | CS8 | CLOCAL | CREAD; ++ newtio.c_iflag = IGNPAR; ++ newtio.c_oflag = 0; ++ ++ /* set input mode (non-canonical, no echo,...) */ ++ newtio.c_lflag = 0; ++ ++ newtio.c_cc[VTIME] = 0; /* inter-character timer unused */ ++ newtio.c_cc[VMIN] = 1; /* blocking read until 5 chars received */ ++ ++ tcflush(fd, TCIFLUSH); ++ tcsetattr(fd,TCSANOW,&newtio); ++ ++ ++ bytes = 0; ++ while (STOP==FALSE) { /* loop for input */ ++ unsigned char c; ++ ++ res = read(fd,buf,1); /* returns after 5 chars have been input */ ++ buf[res]=0; /* so we can printf... */ ++ //fprintf(stderr, ":%02x:%d\n", buf[0], res); ++ //if (buf[0]=='z') STOP=TRUE; ++ ++ c = buf[0]; ++ ++ if ( c & 0x40 ) { ++ bytes = 1; ++ mouse[0] = c; ++ } ++ else if (bytes == 1) { ++ bytes = 2; ++ mouse[1] = c; ++ } ++ else if (bytes == 2) { ++ bytes = 0; ++ mouse[2] = c; ++ fprintf(stderr, "%02x %02x %02x\n", mouse[0], mouse[1], mouse[2]); ++ //printf("%c%c%c", mouse[0], mouse[1], mouse[2]); ++ } ++ } ++ tcsetattr(fd,TCSANOW,&oldtio); ++} ++ ++ ++/* End of FILE */ +diff -uNr linux/drivers/no-otg/functions/mouse/mouse-ce42.c linux/drivers/otg/functions/mouse/mouse-ce42.c +--- linux/drivers/no-otg/functions/mouse/mouse-ce42.c 1970-01-01 01:00:00.000000000 +0100 ++++ linux/drivers/otg/functions/mouse/mouse-ce42.c 2006-09-01 21:41:27.000000000 +0200 +@@ -0,0 +1,89 @@ ++/* ++ * otg/functions/mouse/mouse-ce42.c ++ * ++ * Copyright (c) 2003, 2004 Belcarra ++ * ++ * By: ++ * Chris Lynne <sl@belcarra.com>, ++ * Stuart Lynne <sl@belcarra.com>, ++ * ++ * This is a test USB HID Function Driver designed to test and ++ * verify that INTERRUPT IN endpoints work properly. ++ * ++ * This emulates a simple USB mouse and generates a constant stream ++ * of small random mouse movements. ++ * ++ * To use simply load with something like: ++ * ++ * insmod mouse_fd.o vendor_id=0xffff product_id=0xffff ++ * ++ * And attach to a Windows box. Windows should recognize as a mouse and ++ * immediately start receiving a stream of data. ++ * ++ * To terminate simply unplug. ++ * ++ * The mouse driver has several other characteristics to allow testing of ++ * other features of the bus interface driver: ++ * ++ * - ep0 ZLP handling ++ * ++ * - ep0 delayed CONTROL READ ++ * ++ * ++ * Notes ++ * ++ * 1. The mouse driver is product name is hard coded to a string that will ++ * generate a string descriptor that is 32 bytes long. This will test ++ * most UDC's ep0 ZLP handling as it is a multiple of the most common ++ * UDC endpoint zero size. (An option can be added later to allow for ++ * 64 byte ep0 packetsize.) ++ * ++ * 2. The CONFIG_OTG_MOUSE_BH option can be enabled to delay the HID report ++ * to being generated by a bottom half handler. This will verify that ++ * the bus interface driver properly handles the case of a delayed ++ * CONTROL READ. I.e. when the usbd_device_requesdevice_requesdevice_request ++ * zero for successful completion but there is no tx_urb containing the ++ * requested data. The bus interface driver must setup the conditions for ++ * ACK'ing the SETUP packet but then NAK the IN data for endpoint zero ++ * until the tx_urb is started later. ++ */ ++ ++ ++ ++#include "otg/usbp-chap9.h" ++#include <otg/usbp-mem.h> ++#include <otg/usbp-func.h> ++ ++/* ++ * mouse_modinit - module init ++ * ++ */ ++static int mouse_modinit (void) ++{ ++ ++ // register as usb function driver ++ THROW_IF (NULL == (mouse_private.function = usbd_register_function (&mouse_function_driver, "mouse", NULL)), error); ++#ifdef CONFIG_OTG_MOUSE_BH ++ mouse_private.notification_bh.routine = mouse_hid_bh; ++ mouse_private.notification_bh.data = NULL; ++#endif ++ CATCH(error) { ++ if (mouse_private.function) { ++ usbd_deregister_function (mouse_private.function); ++ mouse_private.function = NULL; ++ } ++ return -EINVAL; ++ } ++ return 0; ++} ++ ++/* mouse_modexit - module cleanup ++ */ ++static void mouse_modexit (void) ++{ ++ if (mouse_private.function) { ++ usbd_deregister_function (mouse_private.function); ++ mouse_private.function = NULL; ++ } ++} ++ +diff -uNr linux/drivers/no-otg/functions/mouse/mouse-fd.c linux/drivers/otg/functions/mouse/mouse-fd.c +--- linux/drivers/no-otg/functions/mouse/mouse-fd.c 1970-01-01 01:00:00.000000000 +0100 ++++ linux/drivers/otg/functions/mouse/mouse-fd.c 2006-09-01 21:41:27.000000000 +0200 +@@ -0,0 +1,826 @@ ++/* ++ * otg/functions/mouse/mouse-fd.c ++ * ++ * Copyright (c) 2003, 2004 Belcarra ++ * ++ * By: ++ * Stuart Lynne <sl@belcarra.com>, ++ * Bruce Balden <balden@belcarra.com> ++ * ++ */ ++/*! ++ * @file otg/functions/mouse/mouse-fd.c ++ * @brief Mouse Function Driver protocol implementation. ++ * ++ * This file implements the Mouse HID protocols and will generate ++ * random mouse data on the INTERRUPT endpoint. ++ * ++ * The primary purpose of this driver is to demonstrate how to ++ * implement a simple function driver and to provide a simple ++ * uni-directional driver for testing USB Device peripheral drivers. ++ * ++ * The mouse driver has several other characteristics to allow testing of ++ * other features of USB Device peripheral drivers: ++ * ++ * - ep0 ZLP handling ++ * ++ * - ep0 delayed CONTROL READ ++ * ++ * To verify that ep0 ZLP processing is being handling correctly this ++ * driver has a hard coded Product name that is returned as a 32 byte ++ * string. This forces most any implementations that have an endpoint ++ * zero packetsize of 8, 16 or 32 to send a Zero Length Packet to ++ * terminate the string transfer when the Product name is requested ++ * by the host. ++ * ++ * The delayed CONTROL READ test verifies that that USB Device peripheral ++ * drivers will correctly NAK until data is provide for device requests. ++ * This is done by (optionally) delaying the HID report via a bottom half ++ * handler. The device request returns normally and the peripheral driver ++ * must properly recognize that while the device request had a non-zero ++ * wLength field, there is currently no queued urb. ++ * ++ * When the bottom half handler is scheduled the HID report urb will be ++ * queued on endpoint zero and then returned to the host. ++ * ++ * ++ * @ingroup MouseFunction ++ */ ++ ++#include <linux/config.h> ++#include <otg/otg-compat.h> ++ ++#include <otg/usbp-chap9.h> ++#include <otg/usbp-func.h> ++#include <otg/otg-trace.h> ++#include <otg/otg-api.h> ++ ++#include "mouse.h" ++ ++struct mouse_private mouse_private; ++ ++/* ++ * ep0 testing.... ensure that this is exactly 16 bytes ++ */ ++#undef CONFIG_OTG_MOUSE_PRODUCT_NAME ++#define CONFIG_OTG_MOUSE_PRODUCT_NAME "Belcarra Mouse" ++ ++/*! ++ * @name Mouse Descriptors ++ * ++ * MouseHIDReport ++ * MOUSE Configuration ++ * ++ * Endpoint, Class, Interface, Configuration and Device descriptors/descriptions ++ */ ++ ++/*! ++ * Mouse Descriptors ++ * @{ ++ */ ++ ++#define BULK_INT 0x00 /*!< Interrupt endpoint number */ ++#define ENDPOINTS 0x01 /*!< Number of endpoints required */ ++ ++/*! List of required endpoint numbers ++ */ ++static u8 mouse_indexes[] = { BULK_INT, }; ++ ++/*! List of requested endpoints ++ */ ++static struct usbd_endpoint_request mouse_endpoint_requests[ENDPOINTS+1] = { ++ { 1, 0, 0, USB_DIR_IN | USB_ENDPOINT_INTERRUPT, 16, 64, }, ++ { 0, }, ++}; ++ ++/*! This is the HID report returned for the HID Device Request. ++ * There is no pre-defined descriptor type for this. ++ */ ++static char MouseHIDReport[52] = { ++ 0x05, 0x01, /*!< USAGE_PAGE (Generic Desktop) */ ++ 0x09, 0x02, /*!< USAGE (Mouse) */ ++ 0xa1, 0x01, /*!< COLLECTION (Application) */ ++ 0x09, 0x01, /*!< USAGE (Pointer) */ ++ 0xa1, 0x00, /*!< COLLECTION (Physical) */ ++ 0x05, 0x09, /*!< USAGE_PAGE (Button) */ ++ 0x19, 0x01, /*!< USAGE_MINIMUM (Button 1) */ ++ 0x29, 0x03, /*!< USAGE_MAXIMUM (Button 3) */ ++ 0x15, 0x00, /*!< LOGICAL_MINIMUM (0) */ ++ 0x25, 0x01, /*!< LOGICAL_MAXIMUM (1) */ ++ 0x95, 0x03, /*!< REPORT_COUNT (3) */ ++ 0x75, 0x01, /*!< REPORT_SIZE (1) */ ++ 0x81, 0x02, /*!< INPUT (Data,Var,Abs) */ ++ 0x95, 0x01, /*!< REPORT_COUNT (1) */ ++ 0x75, 0x05, /*!< REPORT_SIZE (5) */ ++ 0x81, 0x03, /*!< INPUT (Cnst,Var,Abs) */ ++ 0x05, 0x01, /*!< USAGE_PAGE (Generic Desktop) */ ++ 0x09, 0x30, /*!< USAGE (X) */ ++ 0x09, 0x31, /*!< USAGE (Y) */ ++ 0x09, 0x38, /*!< USAGE (WHEEL) */ ++ 0x15, 0x81, /*!< LOGICAL_MINIMUM (-127) */ ++ 0x25, 0x7f, /*!< LOGICAL_MAXIMUM (127) */ ++ 0x75, 0x08, /*!< REPORT_SIZE (8) */ ++ 0x95, 0x03, /*!< REPORT_COUNT (3) */ ++ 0x81, 0x06, /*!< INPUT (Data,Var,Rel) */ ++ 0xc0, /*!< END_COLLECTION */ ++ 0xc0 /*!< END_COLLECTION */ ++}; ++ ++#if !defined(OTG_C99) ++/*! This is the interrupt endpoint descriptor. ++ */ ++static struct usbd_endpoint_descriptor mouse_data; ++ ++/*! The list of endpoint descriptors ++ */ ++static struct usbd_endpoint_descriptor *mouse_default[] = { &mouse_data, }; ++ ++ ++/*! This is the HID desccriptor. ++ */ ++struct hid_descriptor mouse_hid; ++ ++/*! List of class descriptors ++ */ ++static struct usbd_generic_class_descriptor *mouse_hid_descriptors[] = { ++ (struct usbd_generic_class_descriptor *)&mouse_hid, }; ++ ++ ++/*! Data Interface Alternate description ++ */ ++static struct usbd_interface_descriptor mouse_data_alternate_descriptor; ++ ++/*! Interface Descriptions ++ */ ++static struct usbd_alternate_description mouse_data_alternate_descriptions[1]; ++ ++/*! List of Interface description(s) ++ */ ++static struct usbd_interface_description mouse_interfaces[1]; ++ ++/*! Configuration description(s) ++ */ ++static struct usbd_configuration_descriptor mouse_configuration_descriptor; ++ ++/*! Mouse Configuration Description ++ */ ++struct usbd_configuration_description mouse_configuration_description[1]; ++ ++/*! Device Description ++ */ ++static struct usbd_device_descriptor mouse_device_descriptor; ++ ++#ifdef CONFIG_OTG_HIGH_SPEED ++/*! High Speed Device Description ++ */ ++static struct usbd_device_qualifier_descriptor mouse_device_qualifier_descriptor; ++#endif /* CONFIG_OTG_HIGH_SPEED */ ++ ++/*! OTG Descriptor ++ */ ++static struct usbd_otg_descriptor mouse_otg_descriptor; ++ ++/*! Device Description ++ */ ++struct usbd_device_description mouse_device_description; ++ ++void mouse_global_init(void) ++{ ++ ++ /*! This is the interrupt endpoint descriptor. ++ */ ++ ZERO(mouse_data); ++ mouse_data.bLength = 0x07; ++ mouse_data.bDescriptorType = USB_DT_ENDPOINT; ++ mouse_data.bEndpointAddress = USB_DIR_IN; ++ mouse_data.bmAttributes = INTERRUPT; ++ mouse_data.wMaxPacketSize = __constant_cpu_to_le16(0x10); ++#if defined (CONFIG_OTG_MOUSE_INTERVAL) ++ mouse_data.bInterval = CONFIG_OTG_MOUSE_INTERVAL; ++#else ++ mouse_data.bInterval = 0x01; ++#endif /* defined (CONFIG_OTG_MOUSE_INTERVAL) */ ++ ++ ++ /*! This is the HID desccriptor. ++ */ ++ ZERO(mouse_hid); ++ mouse_hid.bLength = 0x09; ++ mouse_hid.bDescriptorType = 0x21; ++ mouse_hid.bcdHID = __constant_cpu_to_le16(0x110); ++ mouse_hid.bCountryCode = 0x00; ++ mouse_hid.bNumDescriptors = 0x01; ++ mouse_hid.bReportType = 0x22; ++ mouse_hid.wItemLength = __constant_cpu_to_le16(0x34); ++ ++ ++ /*! Data Interface Alternate description ++ */ ++ ZERO(mouse_data_alternate_descriptor); ++ mouse_data_alternate_descriptor.bLength = 0x09; ++ mouse_data_alternate_descriptor.bDescriptorType = USB_DT_INTERFACE; ++ mouse_data_alternate_descriptor.bInterfaceNumber = 0x00; ++ mouse_data_alternate_descriptor.bAlternateSetting = 0x00; ++ mouse_data_alternate_descriptor.bNumEndpoints = sizeof (mouse_default) / sizeof(struct usbd_endpoint_descriptor); ++ mouse_data_alternate_descriptor.bInterfaceClass = 0x03; ++ mouse_data_alternate_descriptor.bInterfaceSubClass = 0x01; ++ mouse_data_alternate_descriptor.bInterfaceProtocol = 0x02; ++ mouse_data_alternate_descriptor.iInterface = 0x00; ++ ++ /*! Interface Descriptions ++ */ ++ ZERO(mouse_data_alternate_descriptions); ++ mouse_data_alternate_descriptions[0].iInterface = "Random Mouse Interface - Interrupt"; ++ mouse_data_alternate_descriptions[0].interface_descriptor = &mouse_data_alternate_descriptor; ++ mouse_data_alternate_descriptions[0].classes = ++ sizeof (mouse_hid_descriptors) / sizeof (struct usbd_generic_class_descriptor *); ++ mouse_data_alternate_descriptions[0].class_list = mouse_hid_descriptors; ++ mouse_data_alternate_descriptions[0].endpoints = sizeof (mouse_default) / sizeof(struct usbd_endpoint_descriptor *); ++ mouse_data_alternate_descriptions[0].endpoint_list = mouse_default; ++ mouse_data_alternate_descriptions[0].endpoint_indexes = mouse_indexes; ++ ++ /*! List of Interface description(s) ++ */ ++ ZERO(mouse_interfaces); ++ mouse_interfaces[0].alternates = sizeof (mouse_data_alternate_descriptions) / sizeof (struct usbd_alternate_description); ++ mouse_interfaces[0].alternate_list = mouse_data_alternate_descriptions; ++ ++ ++ /*! Configuration description(s) ++ */ ++ ZERO(mouse_configuration_descriptor); ++ mouse_configuration_descriptor.bLength = 0x09; ++ mouse_configuration_descriptor.bDescriptorType = USB_DT_CONFIGURATION; ++ mouse_configuration_descriptor.wTotalLength = 0x00; ++ mouse_configuration_descriptor.bNumInterfaces = sizeof (mouse_interfaces) / sizeof (struct usbd_interface_description); ++ mouse_configuration_descriptor.bConfigurationValue = 0x01; ++ mouse_configuration_descriptor.iConfiguration = 0x00; ++ mouse_configuration_descriptor.bmAttributes = 0; ++ mouse_configuration_descriptor.bMaxPower = 0; ++ ++ /*! Mouse Configuration Description ++ */ ++ ZERO(mouse_configuration_description); ++ mouse_configuration_description[0].configuration_descriptor = &mouse_configuration_descriptor; ++ mouse_configuration_description[0].iConfiguration = "USB Random Mouse Configuration"; ++ mouse_configuration_description[0].bNumInterfaces = ++ sizeof (mouse_interfaces) / sizeof (struct usbd_interface_description); ++ mouse_configuration_description[0].interface_list = mouse_interfaces; ++ ++ /*! Device Description ++ */ ++ ZERO(mouse_device_descriptor); ++ mouse_device_descriptor.bLength = sizeof(struct usbd_device_descriptor); ++ mouse_device_descriptor.bDescriptorType = USB_DT_DEVICE; ++ mouse_device_descriptor.bcdUSB = __constant_cpu_to_le16(USB_BCD_VERSION); ++ mouse_device_descriptor.bDeviceClass = 0x00; ++ mouse_device_descriptor.bDeviceSubClass = 0x00; ++ mouse_device_descriptor.bDeviceProtocol = 0x00; ++ mouse_device_descriptor.bMaxPacketSize0 = 0x00; ++ mouse_device_descriptor.idVendor = __constant_cpu_to_le16(CONFIG_OTG_MOUSE_VENDORID); ++ mouse_device_descriptor.idProduct = __constant_cpu_to_le16(CONFIG_OTG_MOUSE_PRODUCTID); ++ mouse_device_descriptor.bcdDevice = __constant_cpu_to_le16(CONFIG_OTG_MOUSE_BCDDEVICE); ++ ++#ifdef CONFIG_OTG_HIGH_SPEED ++ /*! High Speed Device Description ++ */ ++ ZERO(mouse_device_qualifier_descriptor); ++ mouse_device_qualifier_descriptor.bLength = sizeof(struct usbd_device_qualifier_descriptor); ++ mouse_device_qualifier_descriptor.bDescriptorType = USB_DT_DEVICE_QUALIFIER; ++ mouse_device_qualifier_descriptor.bcdUSB = __constant_cpu_to_le16(USB_BCD_VERSION); ++ mouse_device_qualifier_descriptor.bDeviceClass = 0x00; ++ mouse_device_qualifier_descriptor.bDeviceSubClass = 0x00; ++ mouse_device_qualifier_descriptor.bDeviceProtocol = 0x00; ++ mouse_device_qualifier_descriptor.bMaxPacketSize0 = 0x00; ++#endif /* CONFIG_OTG_HIGH_SPEED */ ++ ++ /*! OTG Descriptor ++ */ ++ ZERO(mouse_otg_descriptor); ++ mouse_otg_descriptor.bLength = sizeof(struct usbd_otg_descriptor); ++ mouse_otg_descriptor.bDescriptorType = USB_DT_OTG; ++ mouse_otg_descriptor.bmAttributes = 0; ++ ++ /*! Device Description ++ */ ++ ZERO(mouse_device_description); ++ mouse_device_description.device_descriptor = &mouse_device_descriptor; ++#ifdef CONFIG_OTG_HIGH_SPEED ++ mouse_device_description.device_qualifier_descriptor = &mouse_device_qualifier_descriptor; ++#endif /* CONFIG_OTG_HIGH_SPEED */ ++ mouse_device_description.otg_descriptor = &mouse_otg_descriptor; ++ mouse_device_description.iManufacturer = CONFIG_OTG_MOUSE_MANUFACTURER; ++ mouse_device_description.iProduct = CONFIG_OTG_MOUSE_PRODUCT_NAME; ++#if !defined(CONFIG_OTG_NO_SERIAL_NUMBER) && defined(CONFIG_OTG_SERIAL_NUMBER_STR) ++ mouse_device_description.iSerialNumber = CONFIG_OTG_SERIAL_NUMBER_STR; ++#endif ++} ++ ++#else /* defined(OTG_C99) */ ++ ++/*! This is the interrupt endpoint descriptor. ++ */ ++static struct usbd_endpoint_descriptor mouse_data = { ++ .bLength = 0x07, ++ .bDescriptorType = USB_DT_ENDPOINT, ++ .bEndpointAddress = USB_DIR_IN, ++ .bmAttributes = INTERRUPT, ++ .wMaxPacketSize = __constant_cpu_to_le16(0x10), ++#if defined (CONFIG_OTG_MOUSE_INTERVAL) ++ .bInterval = CONFIG_OTG_MOUSE_INTERVAL, ++#else ++ .bInterval = 0x01, ++#endif /* defined (CONFIG_OTG_MOUSE_INTERVAL) */ ++}; ++ ++/*! The list of endpoint descriptors ++ */ ++static struct usbd_endpoint_descriptor *mouse_default[] = { &mouse_data, }; ++ ++/*! This is the HID desccriptor. ++ */ ++struct hid_descriptor mouse_hid = { ++ .bLength = 0x09, ++ .bDescriptorType = 0x21, ++ .bcdHID = __constant_cpu_to_le16(0x110), ++ .bCountryCode = 0x00, ++ .bNumDescriptors = 0x01, ++ .bReportType = 0x22, ++ .wItemLength = __constant_cpu_to_le16(0x34), ++}; ++ ++/*! List of class descriptors ++ */ ++static struct usbd_generic_class_descriptor *mouse_hid_descriptors[] = { ++ (struct usbd_generic_class_descriptor *)&mouse_hid, }; ++ ++ ++/*! Data Interface Alternate description ++ */ ++static struct usbd_interface_descriptor mouse_data_alternate_descriptor = { ++ .bLength = 0x09, ++ .bDescriptorType = USB_DT_INTERFACE, ++ .bInterfaceNumber = 0x00, ++ .bAlternateSetting = 0x00, ++ .bNumEndpoints = sizeof (mouse_default) / sizeof(struct usbd_endpoint_descriptor), ++ .bInterfaceClass = 0x03, ++ .bInterfaceSubClass = 0x01, ++ .bInterfaceProtocol = 0x02, ++ .iInterface = 0x00, ++}; ++ ++/*! Interface Descriptions ++ */ ++static struct usbd_alternate_description mouse_data_alternate_descriptions[] = { ++ { .iInterface = "Random Mouse Interface - Interrupt", ++ .interface_descriptor = &mouse_data_alternate_descriptor, ++ .classes = sizeof (mouse_hid_descriptors) / sizeof (struct usbd_generic_class_descriptor *), ++ .class_list = mouse_hid_descriptors, ++ .endpoints = sizeof (mouse_default) / sizeof(struct usbd_endpoint_descriptor *), ++ .endpoint_list = mouse_default, ++ .endpoint_indexes = mouse_indexes, ++ }, ++}; ++ ++/*! List of Interface description(s) ++ */ ++static struct usbd_interface_description mouse_interfaces[] = { ++ { .alternates = sizeof (mouse_data_alternate_descriptions) / sizeof (struct usbd_alternate_description), ++ .alternate_list = mouse_data_alternate_descriptions,}, ++}; ++ ++ ++/*! Configuration description(s) ++ */ ++static struct usbd_configuration_descriptor mouse_configuration_descriptor = { ++ .bLength = 0x09, ++ .bDescriptorType = USB_DT_CONFIGURATION, ++ .wTotalLength = 0x00, ++ .bNumInterfaces = sizeof (mouse_interfaces) / sizeof (struct usbd_interface_description), ++ .bConfigurationValue = 0x01, ++ .iConfiguration = 0x00, ++ .bmAttributes = 0, ++ .bMaxPower = 0, ++}; ++ ++/*! Mouse Configuration Description ++ */ ++struct usbd_configuration_description mouse_configuration_description[] = { ++ { .configuration_descriptor = &mouse_configuration_descriptor, ++ .iConfiguration = "USB Random Mouse Configuration", ++ }, ++}; ++ ++/*! Device Description ++ */ ++static struct usbd_device_descriptor mouse_device_descriptor = { ++ .bLength = sizeof(struct usbd_device_descriptor), ++ .bDescriptorType = USB_DT_DEVICE, ++ .bcdUSB = __constant_cpu_to_le16(USB_BCD_VERSION), ++ .bDeviceClass = 0x00, ++ .bDeviceSubClass = 0x00, ++ .bDeviceProtocol = 0x00, ++ .bMaxPacketSize0 = 0x00, ++ .idVendor = __constant_cpu_to_le16(CONFIG_OTG_MOUSE_VENDORID), ++ .idProduct = __constant_cpu_to_le16(CONFIG_OTG_MOUSE_PRODUCTID), ++ .bcdDevice = __constant_cpu_to_le16(CONFIG_OTG_MOUSE_BCDDEVICE), ++}; ++ ++#ifdef CONFIG_OTG_HIGH_SPEED ++/*! High Speed Device Description ++ */ ++static struct usbd_device_qualifier_descriptor mouse_device_qualifier_descriptor = { ++ .bLength = sizeof(struct usbd_device_qualifier_descriptor), ++ .bDescriptorType = USB_DT_DEVICE_QUALIFIER, ++ .bcdUSB = __constant_cpu_to_le16(USB_BCD_VERSION), ++ .bDeviceClass = 0x00, ++ .bDeviceSubClass = 0x00, ++ .bDeviceProtocol = 0x00, ++ .bMaxPacketSize0 = 0x00, ++}; ++#endif /* CONFIG_OTG_HIGH_SPEED */ ++ ++/*! OTG Descriptor ++ */ ++static struct usbd_otg_descriptor mouse_otg_descriptor = { ++ .bLength = sizeof(struct usbd_otg_descriptor), ++ .bDescriptorType = USB_DT_OTG, ++ .bmAttributes = 0, ++}; ++ ++/*! Device Description ++ */ ++struct usbd_device_description mouse_device_description = { ++ .device_descriptor = &mouse_device_descriptor, ++#ifdef CONFIG_OTG_HIGH_SPEED ++ .device_qualifier_descriptor = &mouse_device_qualifier_descriptor, ++#endif /* CONFIG_OTG_HIGH_SPEED */ ++ .otg_descriptor = &mouse_otg_descriptor, ++ .iManufacturer = CONFIG_OTG_MOUSE_MANUFACTURER, ++ .iProduct = CONFIG_OTG_MOUSE_PRODUCT_NAME, ++#if !defined(CONFIG_OTG_NO_SERIAL_NUMBER) && defined(CONFIG_OTG_SERIAL_NUMBER_STR) ++ .iSerialNumber = CONFIG_OTG_SERIAL_NUMBER_STR, ++#endif ++}; ++#endif /* defined(OTG_C99) */ ++ ++ ++/*! @} */ ++ ++static int mouse_count; ++ ++/* MOUSE ***************************************************************************************** */ ++/* Transmit Function *************************************************************************** */ ++ ++int get_xy[8] = { ++ 0, 0, 1, 1, ++ 0, 0, -1, -1, ++}; ++ ++/*! ++ * mouse_send() - send a mouse data urb with random data ++ * @param function ++ * @return void ++ */ ++void mouse_send(struct usbd_function_instance *function) ++{ ++ struct mouse_private *mouse = &mouse_private; ++ int new_x = 0; ++ int new_y = 0; ++ u8 random; ++ ++ memset(mouse->tx_urb->buffer, 0, 4); ++ if (!mouse->n) { ++ ++ get_random_bytes(&random, 1); ++ ++ mouse->last_x = MAX(-4, MIN(4, mouse->last_x + get_xy[random & 0x7])); ++ mouse->last_y = MAX(-4, MIN(4, mouse->last_y + get_xy[(random >> 3) & 0x7])); ++ mouse->n = (random>>6) & 0x3; ++ ++ new_x = mouse->x + mouse->last_x; ++ new_y = mouse->y + mouse->last_y; ++ ++ mouse->tx_urb->buffer[1] = mouse->last_x; ++ mouse->x = new_x; ++ mouse->tx_urb->buffer[2] = mouse->last_y; ++ mouse->y = new_y; ++ mouse->tx_urb->actual_length = 4; ++ ++ } ++ else if ((mouse->n)&1) { ++ mouse->n--; ++ } ++ else { ++ mouse->n--; ++ mouse->tx_urb->buffer[1] = mouse->last_x; ++ mouse->x = new_x; ++ mouse->tx_urb->buffer[2] = mouse->last_y; ++ mouse->y = new_y; ++ } ++ usbd_start_in_urb(mouse->tx_urb); ++} ++ ++/*! ++ * mouse_urb_sent() - called to indicate URB transmit finished ++ * This function is the callback function for sent urbs. ++ * It simply queues up another urb until the packets to be sent ++ * configuration parameter is reached (or forever if zero.) ++ * @param urb The urb to be sent. ++ * @param rc result ++ * @return int Return non-zero for failure. ++ */ ++int mouse_urb_sent (struct usbd_urb *urb, int rc) ++{ ++ struct mouse_private *mouse = &mouse_private; ++ struct usbd_function_instance *function = mouse->function; ++ ++ RETURN_ZERO_IF(usbd_get_device_status(function) == USBD_CLOSING); ++ RETURN_ZERO_IF(usbd_get_device_status(function) != USBD_OK); ++ RETURN_ZERO_IF(usbd_get_device_state(function) != STATE_CONFIGURED); ++ TRACE_MSG1(MOUSE, "mouse_count: %d", mouse_count); ++ #ifdef CONFIG_OTG_MOUSE_PACKETS ++ RETURN_ZERO_IF (CONFIG_OTG_MOUSE_PACKETS && (mouse_count++ > CONFIG_OTG_MOUSE_PACKETS)); ++ #endif /* CONFIG_OTG_MOUSE_PACKETS */ ++ mouse_send(function); // re-send ++ return 0; ++} ++ ++/* USB Device Functions ************************************************************************ */ ++ ++typedef enum mesg { ++ mesg_unknown, ++ mesg_configured, ++ mesg_reset, ++} mesg_t; ++mesg_t mouse_last_mesg; ++ ++char * mouse_messages[3] = { ++ "", ++ "Mouse Configured", ++ "Mouse Reset", ++}; ++ ++void mouse_check_mesg(mesg_t curr_mesg) ++{ ++ RETURN_UNLESS(mouse_last_mesg != curr_mesg); ++ mouse_last_mesg = curr_mesg; ++ otg_message(mouse_messages[curr_mesg]); ++} ++ ++/*! ++ * mouse_event_handler() - process a device event ++ * This function is called to process USB Device Events. ++ * ++ * The DEVICE_CONFIGURED event causes the mouse_send() to be called ++ * to start the data flow. ++ * ++ * The DEVICE_RESET or DEVICE_DE_CONFIGURED events cause the outstanding ++ * transmit urb to be cancelled. ++ * ++ * @param function the function instance ++ * @param event the event ++ * @param data ++ * @return void ++ * ++ */ ++void mouse_event_handler (struct usbd_function_instance *function, usbd_device_event_t event, int data) ++{ ++ struct mouse_private *mouse = &mouse_private; ++ ++ switch (event) { ++ case DEVICE_CONFIGURED: ++ TRACE_MSG0(MOUSE, "Mouse Configured"); ++ mouse_check_mesg(mesg_configured); ++ mouse_count = 0; ++ mouse->connected = 1; ++ if (!(mouse->tx_urb = usbd_alloc_urb (function, BULK_INT, 4, mouse_urb_sent))) ++ printk(KERN_INFO"%s: alloc failed\n", __FUNCTION__); ++ mouse_send(function); // start sending ++ break; ++ ++ case DEVICE_RESET: ++ case DEVICE_DE_CONFIGURED: ++ TRACE_MSG0(MOUSE, "Mouse De-Configured"); ++ mouse_check_mesg(mesg_reset); ++ BREAK_IF(!mouse->connected); ++ mouse->connected = 0; ++ if (mouse->tx_urb) { ++ usbd_free_urb (mouse->tx_urb); ++ mouse->tx_urb = NULL; ++ } ++ break; ++ default: ++ break; ++ } ++} ++ ++/*! copy_report() ++ * This function copies the Mouse HID report into the provided URB. ++ * @param urb Destination ++ * @param data Source ++ * @param size Amount of data to copy. ++ * @param max_buf Size of buffer ++ * @return Non-zero if error. ++ * ++ */ ++static int copy_report (struct usbd_urb *urb, void *data, int size, int max_buf) ++{ ++ int available; ++ int length; ++ ++ RETURN_EINVAL_IF (!urb); ++ RETURN_EINVAL_IF (!data); ++ RETURN_EINVAL_IF (!(length = size)); ++ RETURN_EINVAL_IF ((available = max_buf - urb->actual_length) <= 0); ++ ++ length = (length < available) ? length : available; ++ memcpy (urb->buffer + urb->actual_length, data, length); ++ urb->actual_length += length; ++ return 0; ++} ++ ++/*! ++ * mouse_send_hid() - send an EP0 urb containing HID report ++ * This is called to send the Mouse HID report. ++ */ ++static int mouse_send_hid (void *data) ++{ ++ struct mouse_private *mouse = &mouse_private; ++ struct usbd_function_instance *function = mouse->function; ++ struct usbd_urb *urb = usbd_alloc_urb_ep0(function, mouse->wLength, NULL); ++ int wMaxPacketSize = usbd_endpoint_zero_wMaxPacketSize(urb->function_instance, 0); ++ ++ TRACE_MSG1(MOUSE, "Send Hid wLength: %d", mouse->wLength); ++ RETURN_EINVAL_IF (copy_report(urb, MouseHIDReport, sizeof(MouseHIDReport), mouse->wLength)); ++ RETURN_EINVAL_UNLESS (wMaxPacketSize); ++ ++ if (!(urb->actual_length % wMaxPacketSize) && (urb->actual_length < mouse->wLength)) ++ urb->flags |= USBD_URB_SENDZLP; ++ ++ RETURN_ZERO_IF(!usbd_start_in_urb(urb)); ++ usbd_free_urb(urb); ++ return -EINVAL; ++} ++ ++#ifdef CONFIG_OTG_MOUSE_BH ++/*! ++ * mouse_hid_bh() - Bottom half handler to send a HID report ++ * @param data ++ */ ++static void mouse_hid_bh (void *data) ++{ ++ struct usbd_function_instance *function = mouse_private.function; ++ mouse_send_hid(function); ++} ++ ++/*! mouse_schedule_bh - schedule a call for mouse_hid_bh ++ * @param void ++ */ ++static int mouse_schedule_bh (void) ++{ ++ TRACE_MSG0(MOUSE, "Scheduling"); ++ return (!schedule_task (&mouse_private.notification_bh)) ? EINVAL : 0; ++} ++#endif /* CONFIG_OTG_MOUSE_BH */ ++ ++/*! ++ * mouse_device_request - called to indicate urb has been received ++ * @param function ++ * @param request ++ */ ++int mouse_device_request (struct usbd_function_instance *function, struct usbd_device_request *request) ++{ ++ /* verify that this is a usb class request per cdc-mouse specification or a vendor request. ++ * determine the request direction and process accordingly ++ */ ++ ++ switch (request->bmRequestType & (USB_REQ_DIRECTION_MASK | USB_REQ_TYPE_MASK)) { ++ ++ case USB_REQ_HOST2DEVICE: ++ case USB_REQ_HOST2DEVICE | USB_REQ_TYPE_CLASS: ++ case USB_REQ_HOST2DEVICE | USB_REQ_TYPE_VENDOR: ++ return 0; ++ ++ case USB_REQ_DEVICE2HOST : ++ case USB_REQ_DEVICE2HOST | USB_REQ_TYPE_CLASS: ++ case USB_REQ_DEVICE2HOST | USB_REQ_TYPE_VENDOR: ++ ++ switch (request->bRequest) { ++ case USB_REQ_GET_DESCRIPTOR: ++ switch (le16_to_cpu(request->wValue)>>8) { ++ case HID_REPORT: ++ mouse_private.wLength = request->wLength; ++ #ifdef CONFIG_OTG_MOUSE_BH ++ return mouse_schedule_bh(); ++ #else ++ return mouse_send_hid(function); ++ #endif ++ } ++ default: break; ++ } ++ break; ++ ++ default: ++ break; ++ } ++ return -EINVAL; ++} ++ ++ ++/*! mouse_function_enable - called by USB Device Core to enable the driver ++ * @param function The function instance for this driver to use. ++ * @return non-zero if error. ++ */ ++static int mouse_function_enable (struct usbd_function_instance *function) ++{ ++ struct mouse_private *mouse = &mouse_private; ++ mouse->function = function; ++ mouse->n = 0; ++ mouse->x = 0; ++ mouse->y = 0; ++ mouse->last_x = 0; ++ mouse->last_y = 0; ++ mouse->writesize = usbd_endpoint_wMaxPacketSize(function, BULK_INT, 0); ++ return 0; ++} ++ ++/*! mouse_function_disable - called by the USB Device Core to disable the driver ++ * @param function The function instance for this driver ++ */ ++static void mouse_function_disable (struct usbd_function_instance *function) ++{ ++ struct mouse_private *mouse = &mouse_private; ++ mouse->function = NULL; ++ mouse->writesize = 0; ++ mouse->function = NULL; ++} ++ ++/* ********************************************************************************************* */ ++#if !defined(OTG_C99) ++/*! function_ops - operations table for the USB Device Core ++ */ ++static struct usbd_function_operations mouse_function_ops; ++ ++/*! mouse_function_driver - USB Device Core function driver definition ++ */ ++struct usbd_function_driver mouse_function_driver; ++ ++void mouse_ops_init(void) ++{ ++ /*! function_ops - operations table for the USB Device Core ++ */ ++ ZERO(mouse_function_ops); ++ mouse_function_ops.event_handler = mouse_event_handler; /*! called for each USB Device Event */ ++ mouse_function_ops.device_request = mouse_device_request; /*! called for each received device request */ ++ mouse_function_ops.function_enable = mouse_function_enable; /*! called to enable the function driver */ ++ mouse_function_ops.function_disable = mouse_function_disable; /*! called to disable the function driver */ ++ ++ /*! function_driver - USB Device Core function driver definition ++ */ ++ ZERO(mouse_function_driver); ++ mouse_function_driver.name = "mouse-random"; /*! driver name */ ++ mouse_function_driver.fops = &mouse_function_ops; /*! operations table */ ++ mouse_function_driver.device_description = &mouse_device_description; /*! mouse device description */ ++ mouse_function_driver.bNumConfigurations = ++ sizeof (mouse_configuration_description) / sizeof (struct usbd_configuration_description); ++ mouse_function_driver.configuration_description = ++ mouse_configuration_description; /*! mouse configuration description */ ++ mouse_function_driver.idVendor = __constant_cpu_to_le16(CONFIG_OTG_MOUSE_VENDORID); ++ mouse_function_driver.idProduct = __constant_cpu_to_le16(CONFIG_OTG_MOUSE_PRODUCTID); ++ mouse_function_driver.bcdDevice = __constant_cpu_to_le16(CONFIG_OTG_MOUSE_BCDDEVICE); ++ ++ ++ mouse_function_driver.bNumInterfaces = sizeof (mouse_interfaces) / sizeof (struct usbd_interface_description); ++ mouse_function_driver.interface_list = mouse_interfaces; ++ mouse_function_driver.endpointsRequested = ENDPOINTS; ++ mouse_function_driver.requestedEndpoints = mouse_endpoint_requests; ++} ++#else /* defined(OTG_C99) */ ++/*! function_ops - operations table for the USB Device Core ++ */ ++static struct usbd_function_operations mouse_function_ops = { ++ .event_handler = mouse_event_handler, /*!< called for each USB Device Event */ ++ .device_request = mouse_device_request, /*!< called for each received device request */ ++ .function_enable = mouse_function_enable, /*!< called to enable the function driver */ ++ .function_disable = mouse_function_disable, /*!< called to disable the function driver */ ++}; ++ ++/*! mouse_function_driver - USB Device Core function driver definition ++ */ ++struct usbd_function_driver mouse_function_driver = { ++ .name = "mouse-random", /*!< driver name */ ++ .fops = &mouse_function_ops, /*!< operations table */ ++ .device_description = &mouse_device_description, /*!< mouse device description */ ++ .bNumConfigurations = sizeof (mouse_configuration_description) / sizeof (struct usbd_configuration_description), ++ .configuration_description = mouse_configuration_description, /*!< mouse configuration description */ ++ .idVendor = __constant_cpu_to_le16(CONFIG_OTG_MOUSE_VENDORID), ++ .idProduct = __constant_cpu_to_le16(CONFIG_OTG_MOUSE_PRODUCTID), ++ .bcdDevice = __constant_cpu_to_le16(CONFIG_OTG_MOUSE_BCDDEVICE), ++ .bNumInterfaces = sizeof (mouse_interfaces) / sizeof (struct usbd_interface_description), ++ .interface_list = mouse_interfaces, ++ .endpointsRequested = ENDPOINTS, ++ .requestedEndpoints = mouse_endpoint_requests, ++}; ++#endif /* defined(OTG_C99) */ ++ +diff -uNr linux/drivers/no-otg/functions/mouse/mouse-if.c linux/drivers/otg/functions/mouse/mouse-if.c +--- linux/drivers/no-otg/functions/mouse/mouse-if.c 1970-01-01 01:00:00.000000000 +0100 ++++ linux/drivers/otg/functions/mouse/mouse-if.c 2006-09-01 21:41:27.000000000 +0200 +@@ -0,0 +1,499 @@ ++/* ++ * otg/functions/mouse/mouse-if.c ++ * ++ * Copyright (c) 2003, 2004 Belcarra ++ * ++ * By: ++ * Stuart Lynne <sl@belcarra.com>, ++ * Bruce Balden <balden@belcarra.com> ++ * ++ */ ++/*! ++ * @file otg/functions/mouse/mouse-if.c ++ * @brief Mouse Interface Function Driver protocol implementation. ++ * ++ * This file implements the Mouse HID protocols and will generate ++ * random mouse data on the INTERRUPT endpoint. ++ * ++ * The primary purpose of this driver is to demonstrate how to ++ * implement a simple function driver and to provide a simple ++ * uni-directional driver for testing USB Device peripheral drivers. ++ * ++ * The mouse driver has several other characteristics to allow testing of ++ * other features of USB Device peripheral drivers: ++ * ++ * - ep0 ZLP handling ++ * ++ * - ep0 delayed CONTROL READ ++ * ++ * To verify that ep0 ZLP processing is being handling correctly this ++ * driver has a hard coded Product name that is returned as a 32 byte ++ * string. This forces most any implementations that have an endpoint ++ * zero packetsize of 8, 16 or 32 to send a Zero Length Packet to ++ * terminate the string transfer when the Product name is requested ++ * by the host. ++ * ++ * The delayed CONTROL READ test verifies that that USB Device peripheral ++ * drivers will correctly NAK until data is provide for device requests. ++ * This is done by (optionally) delaying the HID report via a bottom half ++ * handler. The device request returns normally and the peripheral driver ++ * must properly recognize that while the device request had a non-zero ++ * wLength field, there is currently no queued urb. ++ * ++ * When the bottom half handler is scheduled the HID report urb will be ++ * queued on endpoint zero and then returned to the host. ++ * ++ * ++ * @ingroup MouseFunction ++ */ ++ ++#include <linux/config.h> ++#include <otg/otg-compat.h> ++ ++#include <otg/usbp-chap9.h> ++#include <otg/usbp-func.h> ++#include <otg/otg-trace.h> ++#include <otg/otg-api.h> ++ ++#include "mouse.h" ++ ++ ++/* ++ * ep0 testing.... ensure that this is exactly 16 bytes ++ */ ++#undef CONFIG_OTG_MOUSE_PRODUCT_NAME ++#define CONFIG_OTG_MOUSE_PRODUCT_NAME "Belcarra Mouse" ++ ++/*! ++ * @name Mouse Descriptors ++ * ++ * MouseHIDReport ++ * MOUSE Configuration ++ * ++ * Endpoint, Class, Interface, Configuration and Device descriptors/descriptions ++ */ ++ ++/*! ++ * Mouse Descriptors ++ * @{ ++ */ ++ ++#define BULK_INT 0x00 /*!< Interrupt endpoint number */ ++#define ENDPOINTS 0x01 /*!< Number of endpoints required */ ++ ++/*! List of required endpoint numbers ++ */ ++static u8 mouse_indexes[] = { BULK_INT, }; ++ ++/*! List of requested endpoints ++ */ ++static struct usbd_endpoint_request mouse_endpoint_requests[ENDPOINTS+1] = { ++ { 1, 0, 0, USB_DIR_IN | USB_ENDPOINT_INTERRUPT, 16, 64, }, ++ { 0, }, ++}; ++ ++#if !defined(OTG_C99) ++/*! This is the interrupt endpoint descriptor. ++ */ ++static struct usbd_endpoint_descriptor mouse_data; ++ ++/*! The list of endpoint descriptors ++ */ ++static struct usbd_endpoint_descriptor *mouse_default[] = { &mouse_data, }; ++ ++ ++/*! List of class descriptors ++ */ ++static struct usbd_generic_class_descriptor *mouse_hid_descriptors[] = { ++ (struct usbd_generic_class_descriptor *)&mouse_hid, }; ++ ++ ++/*! Data Interface Alternate description ++ */ ++static struct usbd_interface_descriptor mouse_data_alternate_descriptor; ++ ++/*! Interface Descriptions ++ */ ++static struct usbd_alternate_description mouse_data_alternate_descriptions[1]; ++ ++/*! List of Interface description(s) ++ */ ++static struct usbd_interface_description mouse_interfaces[1]; ++ ++ ++void mouse_if_global_init(void) ++{ ++ ++ /*! This is the interrupt endpoint descriptor. ++ */ ++ ZERO(mouse_data); ++ mouse_data.bLength = 0x07; ++ mouse_data.bDescriptorType = USB_DT_ENDPOINT; ++ mouse_data.bEndpointAddress = USB_DIR_IN; ++ mouse_data.bmAttributes = INTERRUPT; ++ mouse_data.wMaxPacketSize = __constant_cpu_to_le16(0x10); ++#if defined (CONFIG_OTG_MOUSE_INTERVAL) ++ mouse_data.bInterval = CONFIG_OTG_MOUSE_INTERVAL; ++#else ++ mouse_data.bInterval = 0x01; ++#endif /* defined (CONFIG_OTG_MOUSE_INTERVAL) */ ++ ++ ++ /*! Data Interface Alternate description ++ */ ++ ZERO(mouse_data_alternate_descriptor); ++ mouse_data_alternate_descriptor.bLength = 0x09; ++ mouse_data_alternate_descriptor.bDescriptorType = USB_DT_INTERFACE; ++ mouse_data_alternate_descriptor.bInterfaceNumber = 0x00; ++ mouse_data_alternate_descriptor.bAlternateSetting = 0x00; ++ mouse_data_alternate_descriptor.bNumEndpoints = sizeof (mouse_default) / sizeof(struct usbd_endpoint_descriptor); ++ mouse_data_alternate_descriptor.bInterfaceClass = 0x03; ++ mouse_data_alternate_descriptor.bInterfaceSubClass = 0x01; ++ mouse_data_alternate_descriptor.bInterfaceProtocol = 0x02; ++ mouse_data_alternate_descriptor.iInterface = 0x00; ++ ++ /*! Interface Descriptions ++ */ ++ ZERO(mouse_data_alternate_descriptions); ++ mouse_data_alternate_descriptions[0].iInterface = "Random Mouse Interface - Interrupt"; ++ mouse_data_alternate_descriptions[0].interface_descriptor = &mouse_data_alternate_descriptor; ++ mouse_data_alternate_descriptions[0].classes = ++ sizeof (mouse_hid_descriptors) / sizeof (struct usbd_generic_class_descriptor *); ++ mouse_data_alternate_descriptions[0].class_list = mouse_hid_descriptors; ++ mouse_data_alternate_descriptions[0].endpoints = sizeof (mouse_default) / sizeof(struct usbd_endpoint_descriptor *); ++ mouse_data_alternate_descriptions[0].endpoint_list = mouse_default; ++ mouse_data_alternate_descriptions[0].endpoint_indexes = mouse_indexes; ++ ++ /*! List of Interface description(s) ++ */ ++ ZERO(mouse_interfaces); ++ mouse_interfaces[0].alternates = sizeof (mouse_data_alternate_descriptions) / sizeof (struct usbd_alternate_description); ++ mouse_interfaces[0].alternate_list = mouse_data_alternate_descriptions; ++ ++ ++#else /* defined(OTG_C99) */ ++ ++/*! This is the interrupt endpoint descriptor. ++ */ ++static struct usbd_endpoint_descriptor mouse_data = { ++ .bLength = 0x07, ++ .bDescriptorType = USB_DT_ENDPOINT, ++ .bEndpointAddress = USB_DIR_IN, ++ .bmAttributes = INTERRUPT, ++ .wMaxPacketSize = __constant_cpu_to_le16(0x10), ++#if defined (CONFIG_OTG_MOUSE_INTERVAL) ++ .bInterval = CONFIG_OTG_MOUSE_INTERVAL, ++#else ++ .bInterval = 0x01, ++#endif /* defined (CONFIG_OTG_MOUSE_INTERVAL) */ ++}; ++ ++/*! The list of endpoint descriptors ++ */ ++static struct usbd_endpoint_descriptor *mouse_default[] = { &mouse_data, }; ++ ++ ++/*! List of class descriptors ++ */ ++static struct usbd_generic_class_descriptor *mouse_hid_descriptors[] = { ++ (struct usbd_generic_class_descriptor *)&mouse_hid, }; ++ ++ ++/*! Data Interface Alternate description ++ */ ++static struct usbd_interface_descriptor mouse_data_alternate_descriptor = { ++ .bLength = 0x09, ++ .bDescriptorType = USB_DT_INTERFACE, ++ .bInterfaceNumber = 0x00, ++ .bAlternateSetting = 0x00, ++ .bNumEndpoints = sizeof (mouse_default) / sizeof(struct usbd_endpoint_descriptor), ++ .bInterfaceClass = 0x03, ++ .bInterfaceSubClass = 0x01, ++ .bInterfaceProtocol = 0x02, ++ .iInterface = 0x00, ++}; ++ ++/*! Interface Descriptions ++ */ ++static struct usbd_alternate_description mouse_data_alternate_descriptions[] = { ++ { .iInterface = "Random Mouse Interface - Interrupt", ++ .interface_descriptor = &mouse_data_alternate_descriptor, ++ .classes = sizeof (mouse_hid_descriptors) / sizeof (struct usbd_generic_class_descriptor *), ++ .class_list = mouse_hid_descriptors, ++ .endpoints = sizeof (mouse_default) / sizeof(struct usbd_endpoint_descriptor *), ++ .endpoint_list = mouse_default, ++ .endpoint_indexes = mouse_indexes, ++ }, ++}; ++ ++/*! List of Interface description(s) ++ */ ++static struct usbd_interface_description mouse_interfaces[] = { ++ { .alternates = sizeof (mouse_data_alternate_descriptions) / sizeof (struct usbd_alternate_description), ++ .alternate_list = mouse_data_alternate_descriptions,}, ++}; ++ ++#endif /* defined(OTG_C99) */ ++ ++/*! @} */ ++ ++static int mouse_count; ++ ++/* MOUSE ***************************************************************************************** */ ++/* Transmit Function *************************************************************************** */ ++ ++int get_xy[8] = { ++ 0, 0, 1, 1, ++ 0, 0, -1, -1, ++}; ++ ++/*! ++ * mouse_send() - send a mouse data urb with random data ++ * @param function ++ * @return void ++ */ ++void mouse_send(struct usbd_function_instance *function) ++{ ++ struct mouse_private *mouse = &mouse_private; ++ int new_x = 0; ++ int new_y = 0; ++ u8 random; ++ ++ memset(mouse->tx_urb->buffer, 0, 4); ++ if (!mouse->n) { ++ ++ get_random_bytes(&random, 1); ++ ++ mouse->last_x = MAX(-4, MIN(4, mouse->last_x + get_xy[random & 0x7])); ++ mouse->last_y = MAX(-4, MIN(4, mouse->last_y + get_xy[(random >> 3) & 0x7])); ++ mouse->n = (random>>6) & 0x3; ++ ++ new_x = mouse->x + mouse->last_x; ++ new_y = mouse->y + mouse->last_y; ++ ++ mouse->tx_urb->buffer[1] = mouse->last_x; ++ mouse->x = new_x; ++ mouse->tx_urb->buffer[2] = mouse->last_y; ++ mouse->y = new_y; ++ mouse->tx_urb->actual_length = 4; ++ ++ } ++ else if ((mouse->n)&1) { ++ mouse->n--; ++ } ++ else { ++ mouse->n--; ++ mouse->tx_urb->buffer[1] = mouse->last_x; ++ mouse->x = new_x; ++ mouse->tx_urb->buffer[2] = mouse->last_y; ++ mouse->y = new_y; ++ } ++ usbd_start_in_urb(mouse->tx_urb); ++} ++ ++/*! ++ * mouse_urb_sent() - called to indicate URB transmit finished ++ * This function is the callback function for sent urbs. ++ * It simply queues up another urb until the packets to be sent ++ * configuration parameter is reached (or forever if zero.) ++ * @param urb The urb to be sent. ++ * @param rc result ++ * @return int Return non-zero for failure. ++ */ ++int mouse_urb_sent (struct usbd_urb *urb, int rc) ++{ ++ struct mouse_private *mouse = &mouse_private; ++ struct usbd_function_instance *function = mouse->function; ++ ++ RETURN_ZERO_IF(usbd_get_device_status(function) == USBD_CLOSING); ++ RETURN_ZERO_IF(usbd_get_device_status(function) != USBD_OK); ++ RETURN_ZERO_IF(usbd_get_device_state(function) != STATE_CONFIGURED); ++ TRACE_MSG1(MOUSE, "mouse_count: %d", mouse_count); ++ #ifdef CONFIG_OTG_MOUSE_PACKETS ++ RETURN_ZERO_IF (CONFIG_OTG_MOUSE_PACKETS && (mouse_count++ > CONFIG_OTG_MOUSE_PACKETS)); ++ #endif /* CONFIG_OTG_MOUSE_PACKETS */ ++ mouse_send(function); // re-send ++ return 0; ++} ++ ++/* USB Device Functions ************************************************************************ */ ++ ++typedef enum mesg { ++ mesg_unknown, ++ mesg_configured, ++ mesg_reset, ++} mesg_t; ++static mesg_t mouse_last_mesg; ++ ++static char * mouse_messages[3] = { ++ "", ++ "Mouse-if Configured", ++ "Mouse-if Reset", ++}; ++ ++static void mouse_check_mesg(mesg_t curr_mesg) ++{ ++ RETURN_UNLESS(mouse_last_mesg != curr_mesg); ++ mouse_last_mesg = curr_mesg; ++ otg_message(mouse_messages[curr_mesg]); ++} ++ ++/*! ++ * mouse_event_handler() - process a device event ++ * This function is called to process USB Device Events. ++ * ++ * The DEVICE_CONFIGURED event causes the mouse_send() to be called ++ * to start the data flow. ++ * ++ * The DEVICE_RESET or DEVICE_DE_CONFIGURED events cause the outstanding ++ * transmit urb to be cancelled. ++ * ++ * @param function the function instance ++ * @param event the event ++ * @param data ++ * @return void ++ * ++ */ ++static void mouse_event_handler (struct usbd_function_instance *function, usbd_device_event_t event, int data) ++{ ++ struct mouse_private *mouse = &mouse_private; ++ ++ switch (event) { ++ case DEVICE_CONFIGURED: ++ TRACE_MSG0(MOUSE, "Mouse-if Configured"); ++ mouse_check_mesg(mesg_configured); ++ mouse_count = 0; ++ mouse->connected = 1; ++ if (!(mouse->tx_urb = usbd_alloc_urb (function, BULK_INT, 4, mouse_urb_sent))) ++ printk(KERN_INFO"%s: alloc failed\n", __FUNCTION__); ++ mouse_send(function); // start sending ++ break; ++ ++ case DEVICE_RESET: ++ case DEVICE_DE_CONFIGURED: ++ TRACE_MSG0(MOUSE, "Mouse-if De-Configured"); ++ mouse_check_mesg(mesg_reset); ++ BREAK_IF(!mouse->connected); ++ mouse->connected = 0; ++ if (mouse->tx_urb) { ++ usbd_free_urb (mouse->tx_urb); ++ mouse->tx_urb = NULL; ++ } ++ break; ++ default: ++ break; ++ } ++} ++ ++#ifdef CONFIG_OTG_MOUSE_BH ++/*! ++ * mouse_hid_bh() - Bottom half handler to send a HID report ++ * @param data ++ */ ++static void mouse_hid_bh (void *data) ++{ ++ struct usbd_function_instance *function = mouse_private.function; ++ mouse_send_hid(function); ++} ++ ++/*! mouse_schedule_bh - schedule a call for mouse_hid_bh ++ * @param void ++ */ ++static int mouse_schedule_bh (void) ++{ ++ TRACE_MSG0(MOUSE, "Scheduling"); ++ return (!schedule_task (&mouse_private.notification_bh)) ? EINVAL : 0; ++} ++#endif /* CONFIG_OTG_MOUSE_BH */ ++ ++/*! ++ * mouse_device_request - called to indicate urb has been received ++ * @param function ++ * @param request ++ */ ++static int mouse_device_request (struct usbd_function_instance *function, struct usbd_device_request *request) ++{ ++ return -EINVAL; ++} ++ ++ ++/*! mouse_function_enable - called by USB Device Core to enable the driver ++ * @param function The function instance for this driver to use. ++ * @return non-zero if error. ++ */ ++static int mouse_function_enable (struct usbd_function_instance *function) ++{ ++ struct mouse_private *mouse = &mouse_private; ++ mouse->function = function; ++ mouse->n = 0; ++ mouse->x = 0; ++ mouse->y = 0; ++ mouse->last_x = 0; ++ mouse->last_y = 0; ++ mouse->writesize = usbd_endpoint_wMaxPacketSize(function, BULK_INT, 0); ++ return 0; ++} ++ ++/*! mouse_function_disable - called by the USB Device Core to disable the driver ++ * @param function The function instance for this driver ++ */ ++static void mouse_function_disable (struct usbd_function_instance *function) ++{ ++ struct mouse_private *mouse = &mouse_private; ++ mouse->function = NULL; ++ mouse->writesize = 0; ++ mouse->function = NULL; ++} ++ ++/* ********************************************************************************************* */ ++#if !defined(OTG_C99) ++/*! function_ops - operations table for the USB Device Core ++ */ ++static struct usbd_function_operations mouse_function_ops; ++ ++/*! mouse_interfacedriver - USB Device Core function driver definition ++ */ ++struct usbd_function_driver mouse_interfacedriver; ++ ++void mouse_if_ops_init(void) ++{ ++ /*! function_ops - operations table for the USB Device Core ++ */ ++ ZERO(mouse_function_ops); ++ mouse_function_ops.event_handler = mouse_event_handler; /*! called for each USB Device Event */ ++ mouse_function_ops.device_request = mouse_device_request; /*! called for each received device request */ ++ mouse_function_ops.function_enable = mouse_function_enable; /*! called to enable the function driver */ ++ mouse_function_ops.function_disable = mouse_function_disable; /*! called to disable the function driver */ ++ ++ /*! function_driver - USB Device Core function driver definition ++ */ ++ ZERO(mouse_interfacedriver); ++ mouse_interface_driver.name = "mouse-random-if-driver-noc99"; /*! driver name */ ++ mouse_interface_driver.fops = &mouse_function_ops; /*! operations table */ ++ ++ mouse_function_driver.bNumInterfaces = sizeof (mouse_interfaces) / sizeof (struct usbd_interface_description); ++ mouse_function_driver.interface_list = mouse_interfaces; ++ mouse_interface_driver.endpointsRequested = ENDPOINTS; ++ mouse_interface_driver.requestedEndpoints = mouse_endpoint_requests; ++ ++} ++#else /* defined(OTG_C99) */ ++/*! function_ops - operations table for the USB Device Core ++ */ ++static struct usbd_function_operations mouse_function_ops = { ++ .event_handler = mouse_event_handler, /*!< called for each USB Device Event */ ++ .device_request = mouse_device_request, /*!< called for each received device request */ ++ .function_enable = mouse_function_enable, /*!< called to enable the function driver */ ++ .function_disable = mouse_function_disable, /*!< called to disable the function driver */ ++}; ++ ++/*! mouse_interface_driver - USB Device Core function driver definition ++ */ ++struct usbd_function_driver mouse_interface_driver = { ++ .name = "mouse-random-if-driver", /*!< driver name */ ++ .fops = &mouse_function_ops, /*!< operations table */ ++ .endpointsRequested = ENDPOINTS, ++ .requestedEndpoints = mouse_endpoint_requests, ++}; ++#endif /* defined(OTG_C99) */ ++ +diff -uNr linux/drivers/no-otg/functions/mouse/mouse-l24.c linux/drivers/otg/functions/mouse/mouse-l24.c +--- linux/drivers/no-otg/functions/mouse/mouse-l24.c 1970-01-01 01:00:00.000000000 +0100 ++++ linux/drivers/otg/functions/mouse/mouse-l24.c 2006-09-01 21:41:27.000000000 +0200 +@@ -0,0 +1,175 @@ ++/* ++ * otg/functions/mouse/mouse-l24.c ++ * ++ * Copyright (c) 2003, 2004 Belcarra ++ * ++ * By: ++ * Stuart Lynne <sl@belcarra.com>, ++ * Bruce Balden <balden@belcarra.com> ++ * ++ */ ++/*! ++ * @file otg/functions/mouse/mouse-l24.c ++ * @brief Mouse Function Driver Linux 2.4 initialization ++ * ++ * ++ * This file contains the Linux 2.4 module init and exit functions ++ * to load and unload the Random Mouse Function. ++ * ++ * Notes ++ * ++ * This driver does not export any upper edge functionality to ++ * for the user or applications to use. It simply sends a configurable ++ * number of data packets to the USB Host when configured. Each data ++ * packet contains the equivalent of a small mouse report which will ++ * cause the mouse cursor to move on the host. ++ * ++ * To use simply load with something like: ++ * ++ * insmod mouse_fd.o vendor_id=0xffff product_id=0xffff ++ * ++ * And attach to a Windows box. Windows should recognize as a mouse and ++ * immediately start receiving a stream of data. ++ * ++ * To terminate simply unplug. ++ * ++ * ++ * 1. The mouse driver is product name is hard coded to a string that will ++ * generate a string descriptor that is 32 bytes long. This will test ++ * most UDC's ep0 ZLP handling as it is a multiple of the most common ++ * UDC endpoint zero size. (An option can be added later to allow for ++ * 64 byte ep0 packetsize.) ++ * ++ * 2. The CONFIG_OTG_MOUSE_BH option can be enabled to delay the HID report ++ * to being generated by a bottom half handler. This will verify that ++ * the bus interface driver properly handles the case of a delayed ++ * CONTROL READ. I.e. when the usbd_device_request() function returns ++ * zero for successful completion but there is no tx_urb containing the ++ * requested data. The bus interface driver must setup the conditions for ++ * ACK'ing the SETUP packet but then NAK the IN data for endpoint zero ++ * until the tx_urb is started later. ++ * ++ * 3. The CONFIG_OTG_MOUSE_PACKETS option is used to set the number of ++ * mouse data reports to send. Set to zero for a continous stream ++ * of data. ++ * ++ * @ingroup MouseFunction ++ */ ++ ++#include <otg/otg-compat.h> ++#include <otg/otg-module.h> ++ ++#include <otg/usbp-chap9.h> ++#include <otg/usbp-func.h> ++#include <otg/otg-trace.h> ++ ++#include "mouse.h" ++ ++ ++/* Module Parameters ******************************************************** */ ++/* ! ++ * @name XXXXX MODULE Parameters ++ */ ++/* ! @{ */ ++ ++MOD_AUTHOR ("sl@belcarra.com, tbr@belcarra.com"); ++EMBED_LICENSE(); ++MOD_DESCRIPTION ("Belcarra Random Walk MOUSE Function"); ++ ++static u32 vendor_id; /*!< override built-in vendor ID */ ++static u32 product_id; /*!< override built-in product ID */ ++ ++MOD_PARM (vendor_id, "i"); ++MOD_PARM (product_id, "i"); ++ ++MOD_PARM_DESC (vendor_id, "Device Vendor ID"); ++MOD_PARM_DESC (product_id, "Device Product ID"); ++ ++otg_tag_t MOUSE; ++/* ! *} */ ++ ++/* USB Module init/exit ***************************************************** */ ++ ++/*! ++ * mouse_modinit() - module init ++ * ++ * This is called by the Linux kernel; either when the module is loaded ++ * if compiled as a module, or during the system intialization if the ++ * driver is linked into the kernel. ++ * ++ * This function will parse module parameters if required and then register ++ * the mouse driver with the USB Device software. ++ * ++ * If the CONFIG_OTG_MOUSE_BH option is enabled it will also setup the mouse ++ * bottom half handler. ++ * ++ */ ++static int mouse_modinit (void) ++{ ++ struct usbd_function_instance *mfi; ++ printk (KERN_INFO "%s: vendor_id: %04x product_id: %04x\n", __FUNCTION__, vendor_id, product_id); ++ ++ #if !defined(OTG_C99) ++ mouse_global_init(); ++ mouse_ops_init(); ++ #endif /* defined(OTG_C99) */ ++ ++ MOUSE = otg_trace_obtain_tag(); ++ TRACE_MSG2(MOUSE, "vendor_id: %04x product_id: %04x",vendor_id, product_id); ++ ++ if (vendor_id) ++ mouse_function_driver.idVendor = cpu_to_le16(vendor_id); ++ if (product_id) ++ mouse_function_driver.idProduct = cpu_to_le16(product_id); ++ ++ mouse_hid.wItemLength = cpu_to_le16(0x34); // XXX mips compiler bug..... ++ ++ // register as usb function driver ++ THROW_UNLESS ((mouse_private.function = usbd_register_function (&mouse_function_driver, "mouse", NULL)), error); ++ #ifdef CONFIG_OTG_MOUSE_BH ++ mouse_private.notification_bh.routine = mouse_hid_bh; ++ mouse_private.notification_bh.data = NULL; ++ #endif ++ CATCH(error) { ++ if (mouse_private.function) { ++ usbd_deregister_function (mouse_private.function); ++ mouse_private.function = NULL; ++ } ++ otg_trace_invalidate_tag(MOUSE); ++ return -EINVAL; ++ } ++ return 0; ++} ++ ++module_init (mouse_modinit); ++ ++#if OTG_EPILOGUE ++/*! ++ * mouse_modexit() - module init ++ * ++ * This is called by the Linux kernel; when the module is being unloaded ++ * if compiled as a module. This function is never called if the ++ * driver is linked into the kernel. ++ * ++ * @param void ++ * @return void ++ */ ++static void mouse_modexit (void) ++{ ++ #ifdef CONFIG_OTG_MOUSE_BH ++ while (PENDING_WORK_ITEM(mouse_private.notification_bh)) { ++ printk(KERN_ERR"%s: waiting for bh\n", __FUNCTION__); ++ schedule_timeout(10 * HZ); ++ } ++ #endif ++ if (mouse_private.function) { ++ usbd_deregister_function (mouse_private.function); ++ mouse_private.function = NULL; ++ } ++ ++ otg_trace_invalidate_tag(MOUSE); ++} ++ ++module_exit (mouse_modexit); ++#endif ++ +diff -uNr linux/drivers/no-otg/functions/mouse/mouse.h linux/drivers/otg/functions/mouse/mouse.h +--- linux/drivers/no-otg/functions/mouse/mouse.h 1970-01-01 01:00:00.000000000 +0100 ++++ linux/drivers/otg/functions/mouse/mouse.h 2006-09-01 21:41:27.000000000 +0200 +@@ -0,0 +1,63 @@ ++/* ++ * otg/functions/mouse/mouse.h ++ * ++ * Copyright (c) 2003, 2004 Belcarra ++ * ++ * By: ++ * Stuart Lynne <sl@belcarra.com>, ++ * Tom Rushworth <tbr@belcarra.com>, ++ * Bruce Balden <balden@belcarra.com> ++ * ++ */ ++ ++/*! ++ * @defgroup MouseFunction Random Mouse ++ * @ingroup functiongroup ++ */ ++/*! ++ * @file otg/functions/mouse/mouse.h ++ * @brief Mouse Function Driver private defines ++ * ++ * ++ * This is a test USB HID Function Driver designed to test and ++ * verify that INTERRUPT IN endpoints work properly. ++ * ++ * ++ * @ingroup MouseFunction ++ */ ++ ++ ++/*! ++ * The mouse_private structure is used to collect all of the mouse driver ++ * global variables into one place. ++ */ ++struct mouse_private { ++ struct usbd_function_instance *function; /*!< function instance for this module */ ++#ifdef CONFIG_OTG_MOUSE_BH ++ struct WORK_STRUCT notification_bh; ++#endif /* CONFIG_OTG_MOUSE_BH */ ++ int usb_driver_registered; /*!< non-zero if usb function registered */ ++ unsigned char connected; /*!< non-zero if connected to host (configured) */ ++ unsigned int writesize; /*!< packetsize * 4 */ ++ struct usbd_urb *tx_urb; /*!< saved copy of current tx urb */ ++ int wLength; ++ int x; ++ int y; ++ int last_x; ++ int last_y; ++ int n; ++}; ++ ++extern struct mouse_private mouse_private; ++extern struct usbd_function_driver mouse_function_driver; ++extern struct usbd_function_driver mouse_interface_driver; ++extern struct usbd_function_driver mouse_composite_driver; ++extern struct hid_descriptor mouse_hid; ++ ++#ifndef OTG_C99 ++extern void mouse_global_init(void); ++#endif /* OTG_C99 */ ++ ++#define MOUSE mouse_trace_tag ++extern otg_tag_t MOUSE; ++ +diff -uNr linux/drivers/no-otg/functions/msc/Config.in linux/drivers/otg/functions/msc/Config.in +--- linux/drivers/no-otg/functions/msc/Config.in 1970-01-01 01:00:00.000000000 +0100 ++++ linux/drivers/otg/functions/msc/Config.in 2006-09-01 21:41:27.000000000 +0200 +@@ -0,0 +1,25 @@ ++# ++# Mass Storage Class Function Drivers ++# ++# Copyright (C) 2003 Belcarra ++# ++ ++ ++mainmenu_option next_comment ++ ++comment "Mass Storage Function " ++dep_tristate ' Mass Storage Function' CONFIG_OTG_MSC $CONFIG_OTG ++ ++if [ "$CONFIG_OTG_MSC" = "y" -o "$CONFIG_OTG_MSC" = "m" ]; then ++ hex 'VendorID (hex value)' CONFIG_OTG_MSC_VENDORID "15ec" ++ hex 'ProductID (hex value)' CONFIG_OTG_MSC_PRODUCTID "f006" ++ hex 'bcdDevice (binary-coded decimal)' CONFIG_OTG_MSC_BCDDEVICE "0100" ++ ++ string 'iManufacturer (string)' CONFIG_OTG_MSC_MANUFACTURER "Belcarra" ++ string 'iProduct (string)' CONFIG_OTG_MSC_PRODUCT_NAME "Mass Storage Class - Bulk Only" ++ string 'MSC Bulk Only iInterface (string)' CONFIG_OTG_MSC_INTF "MSC BO Data Intf" ++ string 'Data Interface iConfiguration (string)' CONFIG_OTG_MSC_DESC "MSC BO Configuration" ++ ++fi ++ ++endmenu +diff -uNr linux/drivers/no-otg/functions/msc/Kconfig linux/drivers/otg/functions/msc/Kconfig +--- linux/drivers/no-otg/functions/msc/Kconfig 1970-01-01 01:00:00.000000000 +0100 ++++ linux/drivers/otg/functions/msc/Kconfig 2006-09-01 21:41:27.000000000 +0200 +@@ -0,0 +1,52 @@ ++menu "OTG Mass Storage function" ++ depends on OTG ++ ++config OTG_MSC ++ tristate " Mass Storage Function" ++ depends on OTG ++ ++menu "OTG Mass Storage function options" ++ depends on OTG && OTG_MSC ++ ++config OTG_MSC_VENDORID ++ hex "VendorID (hex value)" ++ depends on OTG && OTG_MSC ++ default "Ox15ec" ++ ++config OTG_MSC_PRODUCTID ++ hex "ProductID (hex value)" ++ depends on OTG && OTG_MSC ++ default "Oxf006" ++ ++config OTG_MSC_BCDDEVICE ++ hex "bcdDevice (binary-coded decimal)" ++ depends on OTG && OTG_MSC ++ default "Ox0100" ++ ++config OTG_MSC_MANUFACTURER ++ string "iManufacturer (string)" ++ depends on OTG && OTG_MSC ++ default "Belcarra" ++ ++config OTG_MSC_PRODUCT_NAME ++ string "iProduct (string)" ++ depends on OTG && OTG_MSC ++ default "Mass Storage Class - Bulk Only" ++ ++config OTG_MSC_INTF ++ string "MSC Bulk Only iInterface (string)" ++ depends on OTG && OTG_MSC ++ default "MSC BO Data Intf" ++ ++config OTG_MSC_DESC ++ string "Data Interface iConfiguration (string)" ++ depends on OTG && OTG_MSC ++ default "MSC BO Configuration" ++ ++config OTG_MSC_REGISTER_TRACE ++ bool " MSC Tracing" ++ depends on OTG && OTG_MSC ++ default n ++endmenu ++ ++endmenu +diff -uNr linux/drivers/no-otg/functions/msc/Makefile linux/drivers/otg/functions/msc/Makefile +--- linux/drivers/no-otg/functions/msc/Makefile 1970-01-01 01:00:00.000000000 +0100 ++++ linux/drivers/otg/functions/msc/Makefile 2006-09-01 21:41:27.000000000 +0200 +@@ -0,0 +1,68 @@ ++# ++# Function driver for a Mass Storage USB Device ++# ++# Copyright (c) 2003 Belcarra ++ ++# Multipart objects. ++ ++O_TARGET := msc_target.o ++list-multi := msc_fd.o ++ ++#msc_fd-objs := msc.o crc.o ++msc_fd-objs := msc-fd.o crc.o msc-linux.o msc-bo.o msc-io-l24.o ++ ++# Objects that export symbols. ++#export-objs := msc.o ++export-objs := msc-fd.o msc-linux.o msc-bo.o ++ ++# Object file lists. ++ ++obj-y := ++obj-m := ++obj-n := ++obj- := ++ ++# Each configuration option enables a list of files. ++ ++obj-$(CONFIG_OTG_MSC) += msc_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. ++ ++MSCD=$(OTG)/functions/msc ++ ++OTG=$(TOPDIR)/drivers/otg ++include $(TOPDIR)/Rules.make ++EXTRA_CFLAGS += -I$(OTG) -Wno-unused -Wno-format ++EXTRA_CFLAGS_nostdinc += -I$(OTG) -Wno-unused -Wno-format ++ ++# Link rules for multi-part drivers. ++ ++msc_fd.o: $(msc_fd-objs) ++ $(LD) -r -o $@ $(msc_fd-objs) ++ ++# dependencies: ++ ++mass.o: $(USBDCORE_DIR)/usbd.h $(USBDCORE_DIR)/usbd-bus.h $(USBDCORE_DIR)/usbd-func.h ++ +diff -uNr linux/drivers/no-otg/functions/msc/TODO.txt linux/drivers/otg/functions/msc/TODO.txt +--- linux/drivers/no-otg/functions/msc/TODO.txt 1970-01-01 01:00:00.000000000 +0100 ++++ linux/drivers/otg/functions/msc/TODO.txt 2006-09-01 21:41:27.000000000 +0200 +@@ -0,0 +1,118 @@ ++MSC TODO List Stuart Lynne ++Belcarra Mon Oct 25 00:51:12 PDT 2004 ++ ++ ++1. MSC documentation ++ ++ - requirements and specifications ++ - comparison to mass storage specification, bulk-only and rbc ++ - implementation specifics ++ - rbc/spc commands ++ - sense keys ++ - control and usage ++ ++2. PSC requirements ++ ++ - notification to PSM layer for addroot and deleteroot ++ ++ ++3. expunge non Belcarra code and header files ++ ++ ++4. Insertion emulation ++ ++ i. If not connected, ++ - do bdget(), ++ - do blkdev_get() ++ - set flags appropriately. ++ ++ ii. If connected, ++ - ensure that there are no active requests (sleep if neccessary) ++ - do bdget() ++ - do blkdev_get() ++ - ensure that overlapped requests from the host while ++ doing the open will not cause problems ++ ++5. Removal emulation ++ ++ i. If not connected ++ - reset flags appropriately ++ ++ ii. If connected, ++ - wait for any active requests to complete (sleep if necessary), ++ - reset flags ++ - ensure that overlapped requests from host while ++ resetting the flags will not cause problems ++ - wait for acknowledgement from host ++ ++ ++Note that all of the above must take place in the thread of execution of ++the ioctl call (i.e. a normal user thread or process.) ++ ++The ioctl should not complete until all of the above is complete OR ++cannot complete and you return an error. ++ ++ ++6. ioctls ++ ++Finish ioctl implementation. ++ ++ start start using block device (major, minor specified) ++ stop stop using block device ++ status return current connection status ++ connect wait for connection ++ disconnect wait for dis-connection ++ connected return zero if connected, error otherwise ++ disconnected return zero if dis-connected, error otherwise ++ ++ ++7. tests ++ ++Get the tests in the scripts directory working. ++ ++These should verify the following: ++ ++ 1. connect/disconnect while started/stopped ++ 2. start/stop while connected/disconnected ++ ++ ++Or as two matrixes: ++ ++ Connected Disconnected ++ Start ++ Stop ++ ++ ++ Started Stopped ++ Connect ++ DisConnect ++ ++ ++All valid combinations of starting, stopping, connecting and disconnecting ++must be verified. ++ ++ ++ ++8. Windows ++ ++Get Windows test applicatoin working. ++ ++9. Compatibility ++ ++Test against: ++ ++ WinXP ++ Win2k ++ Linux 2.4 ++ (Linux 2.6) ++ (WinME) ++ ++ ++ ++10. code review ++ ++ - review other drivers to verify we have implemented all required ++ rbc/spc-2 commands and sense codes ++ ++11. prevent removal ++ +diff -uNr linux/drivers/no-otg/functions/msc/crc.c linux/drivers/otg/functions/msc/crc.c +--- linux/drivers/no-otg/functions/msc/crc.c 1970-01-01 01:00:00.000000000 +0100 ++++ linux/drivers/otg/functions/msc/crc.c 2006-09-01 21:41:27.000000000 +0200 +@@ -0,0 +1,74 @@ ++/* ++ * otg/msc_fd/crc.c - crc table ++ * ++ * Copyright (c) 2003, 2004 Belcarra ++ * ++ * By: ++ * Chris Lynne <cl@belcarra.com> ++ * Stuart Lynne <sl@belcarra.com> ++ * Bruce Balden <balden@belcarra.com> ++ * ++ */ ++ ++ ++#include <linux/config.h> ++#include <linux/module.h> ++#include <linux/kernel.h> ++#include <linux/version.h> ++ ++ ++#include <linux/init.h> ++#include <asm/uaccess.h> ++#include <linux/ctype.h> ++#include <linux/timer.h> ++#include <linux/interrupt.h> ++#include <asm/atomic.h> ++#include <linux/mm.h> ++#include <linux/slab.h> ++ ++#include <otg/otg-compat.h> ++ ++//#include <usbd-chap9.h> ++//#include <usbd-mem.h> ++//#include <usbd.h> ++//#include <usbd-func.h> ++ ++#include "crc.h" ++ ++unsigned int *msc_crc32_table; ++ ++/** ++ * Generate the crc32 table ++ * ++ * return non-zero if malloc fails ++ */ ++int make_crc_table(void) ++{ ++ unsigned int n; ++ if (msc_crc32_table) return 0; ++ if (!(msc_crc32_table = (unsigned int *)ckmalloc(256*4, GFP_KERNEL))) return -EINVAL; ++ for (n = 0; n < 256; n++) { ++ int k; ++ unsigned int c = n; ++ for (k = 0; k < 8; k++) { ++ c = (c & 1) ? (CRC32_POLY ^ (c >> 1)) : (c >> 1); ++ } ++ msc_crc32_table[n] = c; ++ } ++ return 0; ++} ++ ++void free_crc_table(void) ++{ ++ if (msc_crc32_table) { ++ lkfree(msc_crc32_table); ++ msc_crc32_table = NULL; ++ } ++} ++ ++unsigned int crc32_compute(unsigned char *src, int len, unsigned int val) ++{ ++ for (; len-- > 0; val = COMPUTE_FCS (val, *src++)); ++ return val; ++} ++ +diff -uNr linux/drivers/no-otg/functions/msc/crc.h linux/drivers/otg/functions/msc/crc.h +--- linux/drivers/no-otg/functions/msc/crc.h 1970-01-01 01:00:00.000000000 +0100 ++++ linux/drivers/otg/functions/msc/crc.h 2006-09-01 21:41:27.000000000 +0200 +@@ -0,0 +1,29 @@ ++/* ++ * otg/msc_fd/crc.c - crc table ++ * ++ * Copyright (c) 2003, 2004 Belcarra ++ * ++ * By: ++ * Chris Lynne <cl@belcarra.com> ++ * Stuart Lynne <sl@belcarra.com> ++ * Bruce Balden <balden@belcarra.com> ++ * Ted Powell <ted@belcarra.com> ++ * ++ * ++ */ ++ ++ ++extern unsigned int *msc_crc32_table; ++ ++#define CRC32_INIT 0xffffffff // Initial FCS value ++#define CRC32_GOOD 0xdebb20e3 // Good final FCS value ++ ++#define CRC32_POLY 0xedb88320 // Polynomial for table generation ++ ++#define COMPUTE_FCS(val, c) (((val) >> 8) ^ msc_crc32_table[((val) ^ (c)) & 0xff]) ++ ++int make_crc_table(void); ++void free_crc_table(void); ++unsigned int crc32_compute(unsigned char *src, int len, unsigned int val); ++ ++ +diff -uNr linux/drivers/no-otg/functions/msc/msc-bo.c linux/drivers/otg/functions/msc/msc-bo.c +--- linux/drivers/no-otg/functions/msc/msc-bo.c 1970-01-01 01:00:00.000000000 +0100 ++++ linux/drivers/otg/functions/msc/msc-bo.c 2006-09-01 21:41:27.000000000 +0200 +@@ -0,0 +1,176 @@ ++/* ++ * otg/function/msc/msc-bo.c ++ * ++ * Copyright (c) 2003, 2004 Belcarra ++ * ++ * By: ++ * Stuart Lynne <sl@belcarra.com>, ++ * Tom Rushworth <tbr@belcarra.com>, ++ * Bruce Balden <balden@belcarra.com> ++ * ++ */ ++/*! ++ * @file otg/functions/msc/msc-bo.c ++ * @brief Mass Storage Driver private defines ++ * ++ * This is a Mass Storage Class Function that uses the Bulk Only protocol. ++ * ++ * ++ * @ingroup MSCFunction ++ */ ++ ++#include <otg/otg-compat.h> ++#include <otg/usbp-chap9.h> ++#include <otg/usbp-func.h> ++#include <otg/otg-trace.h> ++ ++#include <linux/config.h> ++#include <linux/module.h> ++#include <linux/kernel.h> ++#include <linux/version.h> ++ ++#include <linux/init.h> ++#include <asm/uaccess.h> ++#include <linux/ctype.h> ++#include <linux/timer.h> ++#include <linux/interrupt.h> ++#include <asm/atomic.h> ++#include <linux/random.h> ++#include <linux/slab.h> ++ ++#include <linux/blkdev.h> ++ ++ ++#include "msc-scsi.h" ++#include "msc.h" ++#include "msc-fd.h" ++#include "crc.h" ++ ++//#include "rbc.h" ++ ++/*! ++ * Mass Storage Class - Bulk Only ++ * ++ * Endpoint, Class, Interface, Configuration and Device descriptors/descriptions ++ */ ++ ++static u8 msc_data_1[] = { 0x07, USB_DT_ENDPOINT, USB_DIR_OUT, BULK, 0, 0x00, 0x00, }; ++static u8 msc_data_2[] = { 0x07, USB_DT_ENDPOINT, USB_DIR_IN, BULK, 0, 0x00, 0x00, }; ++static struct usbd_endpoint_descriptor *msc_default[] = { ++ (struct usbd_endpoint_descriptor *) msc_data_1, ++ (struct usbd_endpoint_descriptor *) msc_data_2, }; ++u8 msc_indexes[] = { BULK_OUT, BULK_IN, }; ++ ++ ++/*! Endpoint, Interface, Configuration and Device descriptions and descriptors ++ */ ++static u8 msc_data_alternate_descriptor[sizeof(struct usbd_interface_descriptor)] = { ++ 0x09, USB_DT_INTERFACE, ++ 0x00, 0x00, // bInterfaceNumber, bAlternateSetting ++ sizeof (msc_default) / sizeof(struct usbd_endpoint_descriptor), // bNumEndpoints ++ MASS_STORAGE_CLASS, ++ MASS_STORAGE_SUBCLASS_SCSI, ++ MASS_STORAGE_PROTO_BULK_ONLY, ++ 0x00, ++}; ++ ++static struct usbd_alternate_description msc_data_alternate_descriptions[] = { ++ { iInterface: CONFIG_OTG_MSC_INTF, ++ interface_descriptor: (struct usbd_interface_descriptor *)&msc_data_alternate_descriptor, ++ endpoints:sizeof (msc_default) / sizeof(struct usbd_endpoint_descriptor *), ++ endpoint_list: msc_default, ++ endpoint_indexes: msc_indexes, ++ }, ++}; ++ ++ ++struct usbd_interface_description msc_interfaces[] = { ++ { alternates:sizeof (msc_data_alternate_descriptions) / sizeof (struct usbd_alternate_description), ++ alternate_list:msc_data_alternate_descriptions,}, ++}; ++ ++u8 msc_configuration_descriptor[sizeof(struct usbd_configuration_descriptor)] = { ++ 0x09, USB_DT_CONFIGURATION, 0x00, 0x00, // wLength ++ sizeof (msc_interfaces) / sizeof (struct usbd_interface_description), ++ 0x01, 0x00, // bConfigurationValue, iConfiguration ++ 0, 0, ++}; ++ ++struct usbd_configuration_description msc_description[] = { ++ { iConfiguration: CONFIG_OTG_MSC_DESC, ++ configuration_descriptor: (struct usbd_configuration_descriptor *)msc_configuration_descriptor, ++ }, ++}; ++ ++ ++static struct usbd_device_descriptor msc_device_descriptor = { ++ bLength: sizeof(struct usbd_device_descriptor), ++ bDescriptorType: USB_DT_DEVICE, ++ bcdUSB: __constant_cpu_to_le16(USB_BCD_VERSION), ++ bDeviceClass: 0x00, ++ bDeviceSubClass: 0x02, ++ bDeviceProtocol: 0x00, ++ bMaxPacketSize0: 0x00, ++ idVendor: __constant_cpu_to_le16(CONFIG_OTG_MSC_VENDORID), ++ idProduct: __constant_cpu_to_le16(CONFIG_OTG_MSC_PRODUCTID), ++ bcdDevice: __constant_cpu_to_le16(CONFIG_OTG_MSC_BCDDEVICE), ++}; ++ ++#ifdef CONFIG_OTG_HIGH_SPEED ++static struct usbd_device_qualifier_descriptor msc_device_qualifier_descriptor = { ++ bLength: sizeof(struct usbd_device_qualifier_descriptor), ++ bDescriptorType: USB_DT_DEVICE_QUALIFIER, ++ bcdUSB: __constant_cpu_to_le16(USB_BCD_VERSION), ++ bDeviceClass: 0x00, ++ bDeviceSubClass: 0x02, ++ bDeviceProtocol: 0x00, ++ bMaxPacketSize0: 0x00, ++}; ++#endif /* CONFIG_OTG_HIGH_SPEED */ ++ ++ ++ ++ ++static struct usbd_endpoint_request msc_endpoint_requests[ENDPOINTS+1] = { ++ { 1, 0, 0, USB_DIR_OUT | USB_ENDPOINT_BULK, PAGE_SIZE, PAGE_SIZE, }, ++ { 1, 0, 0, USB_DIR_IN | USB_ENDPOINT_BULK, PAGE_SIZE, PAGE_SIZE, }, ++ { 0, }, ++}; ++ ++struct usbd_otg_descriptor msc_otg_descriptor = { ++ bLength : sizeof(struct usbd_otg_descriptor), ++ bDescriptorType: USB_DT_OTG, ++ bmAttributes: 0, ++}; ++ ++struct usbd_device_description msc_device_description = { ++ device_descriptor: &msc_device_descriptor, ++#ifdef CONFIG_OTG_HIGH_SPEED ++ device_qualifier_descriptor: &msc_device_qualifier_descriptor, ++#endif /* CONFIG_OTG_HIGH_SPEED */ ++ otg_descriptor: &msc_otg_descriptor, ++ iManufacturer: CONFIG_OTG_MSC_MANUFACTURER, ++ iProduct: CONFIG_OTG_MSC_PRODUCT_NAME, ++#if !defined(CONFIG_OTG_NO_SERIAL_NUMBER) && defined(CONFIG_OTG_SERIAL_NUMBER_STR) ++ iSerialNumber: CONFIG_OTG_SERIAL_NUMBER_STR, ++#endif ++}; ++ ++ ++extern struct usbd_function_operations msc_function_ops; ++ ++struct usbd_function_driver msc_function_driver = { ++ name: "msc-bulkonly", ++ fops:&msc_function_ops, ++ device_description:&msc_device_description, ++ bNumConfigurations:sizeof (msc_description) / sizeof (struct usbd_configuration_description), ++ configuration_description:msc_description, ++ idVendor: __constant_cpu_to_le16(CONFIG_OTG_MSC_VENDORID), ++ idProduct: __constant_cpu_to_le16(CONFIG_OTG_MSC_PRODUCTID), ++ bcdDevice: __constant_cpu_to_le16(CONFIG_OTG_MSC_BCDDEVICE), ++ bNumInterfaces:sizeof (msc_interfaces) / sizeof (struct usbd_interface_description), ++ interface_list:msc_interfaces, ++ endpointsRequested: ENDPOINTS, ++ requestedEndpoints: msc_endpoint_requests, ++}; ++ +diff -uNr linux/drivers/no-otg/functions/msc/msc-fd.c linux/drivers/otg/functions/msc/msc-fd.c +--- linux/drivers/no-otg/functions/msc/msc-fd.c 1970-01-01 01:00:00.000000000 +0100 ++++ linux/drivers/otg/functions/msc/msc-fd.c 2006-09-01 21:41:27.000000000 +0200 +@@ -0,0 +1,1233 @@ ++/* ++ * otg/function/msc/msc.-fd.cc ++ * ++ * Copyright (c) 2003, 2004 Belcarra ++ * ++ * By: ++ * Stuart Lynne <sl@belcarra.com>, ++ * Tom Rushworth <tbr@belcarra.com>, ++ * Bruce Balden <balden@belcarra.com> ++ * ++ */ ++ ++/*! ++ * @file otg/functions/msc/msc-fd.c ++ * @brief Mass Storage Driver private defines ++ * ++ * This is a Mass Storage Class Function that uses the Bulk Only protocol. ++ * ++ * ++ * Notes: ++ * ++ * 1. Currently we only support the Bulk Only model. Microsoft states that ++ * further support for the mass storage driver will only be done for devices ++ * that conform to the Bulk Only model. ++ * ++ * 2. Multiple LUN's are not supported, but in theory they could be. ++ * ++ * 3. Error handling should be done with STALL but using ZLP seems to also ++ * work. ZLP is usually easier to implement (except possibly on the SA1100.) ++ * We may need to make STALL an option if we find devices (perhaps SA1100) ++ * that cannot reliaby send a ZLP on BULK-IN endpoint. ++ * ++ * ++ * 4. WinXP will match the following: ++ * ++ * USB: Class_08&SubClass_02&Prot_50 ++ * USB: Class_08&SubClass_05&Prot_50 ++ * USB: Class_08&SubClass_06&Prot_50 ++ * ++ * SubClass 02 is MMC or SFF8020I ++ * SubClass 05 is SFF or SFF8070I ++ * SubClass 06 is SCSI ++ * ++ * From the Windows USB Storage FAQ: ++ * ++ * RBC not supported ++ * ++ * SubClass = 6 (SCSI) ++ * CDBs SHOULD NOT be padded to 12 bytes ++ * ModeSense/ModeSelect SHOULD NOT be translated from 1ah/15h to 5ah/55h ++ * should be used for FLASH ++ * ++ * SubClass !=6 ++ * CDBs SHOULD be padded to 12 bytes ++ * ModeSense/ModeSelect SHOULD be translated from 1ah/15h to 5ah/55h ++ * ++ * We are using the former, SubClass = 6, and implement the required SCSI operations. ++ * ++ * ++ * TODO ++ * ++ * ++ * ++ * TODO FIXME Bus Interface Notes ++ * ++ * 1. The bus interface driver must correctly implement NAK'ing if not rcv_urb is ++ * present (see au1x00.c or wmmx.c for examples.) ++ * ++ * 2. The bus interface driver must implement USBD_URB_SENDZLP flag (see au1x00.c ++ * for example.) ++ * ++ * @ingroup MSCFunction ++ */ ++ ++ ++#include <otg/otg-compat.h> ++#include <otg/usbp-chap9.h> ++#include <otg/usbp-func.h> ++#include <otg/otg-trace.h> ++ ++#include <linux/config.h> ++#include <linux/module.h> ++#include <linux/kernel.h> ++#include <linux/version.h> ++ ++#include <linux/init.h> ++#include <asm/uaccess.h> ++#include <linux/ctype.h> ++#include <linux/timer.h> ++#include <linux/interrupt.h> ++#include <asm/atomic.h> ++#include <linux/random.h> ++#include <linux/slab.h> ++ ++#include <linux/blkdev.h> ++ ++ ++#include "msc-scsi.h" ++#include "msc.h" ++#include "msc-fd.h" ++#include "crc.h" ++ ++ ++/* Module Parameters ************************************************************************* */ ++ ++ ++#define DEVICE_EJECTED 0x0001 // MEDIA_EJECTED ++#define DEVICE_INSERTED 0x0002 // MEDIA_INSERT ++ ++#define DEVICE_OPEN 0x0004 // WR_PROTECT_OFF ++#define DEVICE_WRITE_PROTECTED 0x0008 // WR_PROTECT_ON ++ ++#define DEVICE_CHANGE_ON 0x0010 // MEDIA_CHANGE_ON ++ ++#define DEVICE_PREVENT_REMOVAL 0x0020 ++ ++ ++#define DEF_NUMBER_OF_HEADS 0x10 ++#define DEF_SECTORS_PER_TRACK 0x20 ++ ++ ++ ++/* MSC ******************************************************************************************** */ ++ ++extern struct msc_private msc_private; ++ ++int msc_urb_sent (struct usbd_urb *tx_urb, int rc); ++static int msc_recv_urb (struct usbd_urb *urb, int rc); ++ ++otg_tag_t msc_fd_trace_tag; ++ ++/* Sense Key *********************************************************************************** */ ++ ++/*! set_sense_data - set sensedata in msc private struture ++ * @param msc ++ * @param sensedata ++ * @param info ++ * ++ * This saves sense data. Sense data indicates what type of error ++ * has occurred and will be returned to the host when a request sense ++ * command is sent. ++ */ ++void set_sense_data(struct msc_private *msc, u32 sensedata, u32 info) ++{ ++ TRACE_SENSE(sensedata, info); ++ msc->sensedata = sensedata; ++ msc->info = info; ++} ++ ++/* Check blockdev ****************************************************************************** */ ++ ++/*! msc_check_blockdev_name - check current status of the block device ++ * ++ * Check if the block device is operational, either generate a failed CSW ++ * or a ZLP if not ready. ++ * ++ * Returns non-zero if the block device is not available for I/O operations ++ * and a failed CSW has already been sent. ++ */ ++int msc_check_blockdev_name(struct msc_private *msc, int zlp, char *name) ++{ ++ if (msc->block_dev_state & DEVICE_EJECTED) { ++ TRACE_MSG0(MSC,"CHECK BLOCKDEV DEVICE_EJECTED"); ++ ((SENDZLP == zlp) ? msc_dispatch_query_urb_zlp : msc_start_sending_csw_failed) ++ (msc, SCSI_SENSEKEY_MEDIA_NOT_PRESENT, msc->lba, USB_MSC_FAILED); ++ return -EINVAL; ++ } ++ if (msc->block_dev_state & DEVICE_CHANGE_ON) { ++ msc->block_dev_state &= ~DEVICE_CHANGE_ON; ++ TRACE_MSG0(MSC,"CHECK BLOCKDEV DEVICE_CHANGE_ON"); ++ ((SENDZLP == zlp) ? msc_dispatch_query_urb_zlp : msc_start_sending_csw_failed) ++ (msc, SCSI_SENSEKEY_NOT_READY_TO_READY_CHANGE, msc->lba, USB_MSC_FAILED); ++ return -EINVAL; ++ } ++ //TRACE_MSG0(MSC,"CHECK BLOCKDEV DEVICE_INSERTED"); ++ return 0; ++} ++ ++/* Generic start recv urb and send csw ********************************************************* */ ++ ++/*! msc_start_recv - queue a receive urb ++ * ++ * Ensure that size is a multiple of the endpoint packetsize. ++ * ++ * Returns non-zero if there is an error in the USB layer. ++ */ ++int msc_start_recv_urb(struct usbd_function_instance *function, struct msc_private *msc, int size) ++{ ++ struct usbd_urb *rcv_urb = NULL; ++ int wMaxPacketSize = usbd_endpoint_wMaxPacketSize(function, BULK_OUT, usbd_high_speed(function)); ++ if ((size % wMaxPacketSize)) ++ size = ((size + wMaxPacketSize) / wMaxPacketSize) * wMaxPacketSize; ++ ++ RETURN_EINVAL_UNLESS((rcv_urb = usbd_alloc_urb (function, BULK_OUT, size, msc_recv_urb))); ++ rcv_urb->function_privdata = msc; ++ msc->rcv_urb_finished = NULL; ++ RETURN_ZERO_UNLESS(usbd_start_out_urb(rcv_urb)); ++ TRACE_MSG0(MSC,"START RECV URB ERROR"); ++ usbd_free_urb(rcv_urb); ++ return -EINVAL; ++} ++ ++/*! msc_start_sending - start sending a new data or csw urb ++ * ++ * Generate a CSW (Command Status Wrapper) to send to the the host. ++ * ++ * Returns non-zero if there is an error in the USB layer. ++ */ ++int msc_start_sending_csw(struct usbd_function_instance *function, struct msc_private *msc, u8 status) ++{ ++ COMMAND_STATUS_WRAPPER *csw; ++ int length = sizeof(COMMAND_STATUS_WRAPPER); ++ struct usbd_urb *tx_urb; ++ ++ //TRACE_MSG1(MSC,"START SENDING CSW %08x", status); ++ ++ msc->command_state = MSC_STATUS; ++ ++ RETURN_EINVAL_UNLESS((tx_urb = usbd_alloc_urb (function, BULK_IN, length, msc_urb_sent))); ++ ++ tx_urb->actual_length = length; ++ tx_urb->function_privdata = msc; ++ ++ // fill in CSW and queue the urb ++ csw = (COMMAND_STATUS_WRAPPER *) tx_urb->buffer; ++ csw->dCSWSignature = CSW_SIGNATURE; ++ csw->dCSWTag = msc->command.dCBWTag; ++ csw->dCSWDataResidue = msc->command.dCBWDataTransferLength - msc->data_transferred_in_bytes; ++ csw->bCSWStatus = status; ++ ++ TRACE_MSG2(MSC,"START SENDING CSW status: %02x data residue: %d", status, csw->dCSWDataResidue); ++ ++ RETURN_ZERO_UNLESS(usbd_start_in_urb (tx_urb)); ++ TRACE_MSG0(MSC,"START SENDING CSW FAILED"); ++ usbd_free_urb (tx_urb); ++ return -EINVAL; ++} ++ ++/*! msc_start_sending_csw_failed - starting sending a CSW showing failure ++ * ++ * Sets sensedata and generates a CSW with status set to USB_MSC_FAILED. ++ * ++ * Returns non-zero if there is an error in the USB layer. ++ */ ++int msc_start_sending_csw_failed(struct msc_private *msc, u32 sensedata, u32 info, int status) ++{ ++ TRACE_MSG2(MSC, "sensedata: %x status: %x", sensedata, status); ++ set_sense_data(msc, sensedata, info); ++ return msc_start_sending_csw(msc->function, msc, status); ++} ++ ++ ++/* ********************************************************************************************* */ ++ ++/*! msc_alloc_urb - allocate an urb for returning a query ++ * ++ * Returns NULL if there is an error in the USB layer. ++ */ ++struct usbd_urb * msc_alloc_urb(struct msc_private *msc, int length) ++{ ++ struct usbd_function_instance *function; ++ struct usbd_urb *urb; ++ ++ THROW_IF(!(function = msc->function), error); ++ THROW_IF(!(urb = usbd_alloc_urb (function, BULK_IN, length, msc_urb_sent)), error); ++ return urb; ++ CATCH(error) { ++ msc->command_state = MSC_READY; ++ return NULL; ++ } ++} ++ ++/*! msc_dispatch_query_urb - dispatch an urb containing query data ++ * ++ * Returns non-zero if there is an error in the USB layer. ++ */ ++int msc_dispatch_query_urb(struct usbd_urb *urb, struct msc_private *msc, int length, int status) ++{ ++ int rc; ++ unsigned long flags; ++ ++ TRACE_MSG1(MSC,"DISPATCH URB len: %d", length); ++ ++ // save information in msc and urb ++ // ++ urb->function_privdata = msc; ++ urb->actual_length = msc->TransferLength_in_bytes = length; ++ ++ // dispatch urb ++ local_irq_save(flags); ++ if ((rc = usbd_start_in_urb (urb))) { ++ ++ TRACE_MSG0(MSC,"DISPATCH URB FAILED"); ++ usbd_free_urb (urb); ++ ++ // stall? ++ msc->command_state = MSC_READY; ++ local_irq_restore(flags); ++ return -EINVAL; ++ } ++ msc->command_state = MSC_QUERY; ++ msc->status = status; ++ local_irq_restore(flags); ++ return 0; ++} ++ ++/*! msc_dispatch_query_urb_zlp - send a ZLP ++ * ++ * Returns non-zero if there is an error in the USB layer. ++ */ ++int msc_dispatch_query_urb_zlp(struct msc_private *msc, u32 sensedata, u32 info, int status) ++{ ++ struct usbd_urb *urb; ++ TRACE_MSG2(MSC, "sensedata: %x status: %x", sensedata, status); ++ RETURN_EINVAL_IF(!(urb = msc_alloc_urb(msc, 1))); ++ set_sense_data(msc, sensedata, info); ++ urb->flags |= USBD_URB_SENDZLP; ++ return msc_dispatch_query_urb(urb, msc, 0, status); ++} ++ ++/* READ 10 COMMAND - read and send data to the host ******************************************** */ ++extern int msc_scsi_read_10(struct msc_private *msc, char *name, int op); ++extern int msc_in_read_10_urb_sent(struct usbd_urb *tx_urb, struct msc_private *msc); ++ ++/* WRITE 10 - receive data from host and write to block device ********************************* */ ++extern int msc_scsi_write_10(struct msc_private *msc, char *name, int op); ++extern void msc_recv_out_blocks(struct usbd_urb *rcv_urb, struct msc_private *msc); ++ ++/* SCSI Commands ******************************************************************************* */ ++ ++/*! msc_scsi_inquiry - process an inquiry ++ * ++ * Used by: ++ * win2k ++ * ++ * Returns non-zero if there is an error in the USB layer. ++ */ ++int msc_scsi_inquiry(struct msc_private *msc, char *name, int op) ++{ ++ SCSI_INQUIRY_COMMAND *command = (SCSI_INQUIRY_COMMAND *)&msc->command.CBWCB; ++ SCSI_INQUIRY_DATA *data; ++ struct usbd_urb *urb; ++ int length = sizeof(SCSI_INQUIRY_DATA); ++ ++ /* ++ * C.f. SPC2 7.3 INQUIRY command ++ * C.f. Table 46 - Standard INQUIRY data format ++ * ++ * C.f. Table 47 - Peripheral Qualifier ++ * ++ * 000b The specified peripheral device type is currently connected to this ++ * logical unit..... ++ * 001b The device server is capable of of supporting the peripheral device ++ * type on this logical unit. However, the physical device is not currently ++ * connected to this logical unit..... ++ * 010b Reserved ++ * 011b The device server is not capable of supporting a physical device on ++ * this logical unit.... ++ * ++ */ ++ ++ TRACE_MSG4(MSC,"INQUIRY EnableVPD: %02x LogicalUnitNumber: %02x PageCode: %02x AllocLen: %02x", ++ command->EnableVPD, command->LogicalUnitNumber, command->PageCode, command->AllocationLength); ++ ++ // XXX THROW_IF(msc->command_state != MSC_READY, error); ++ ++ RETURN_EINVAL_IF(!(urb = msc_alloc_urb(msc, length))); ++ ++ data = (SCSI_INQUIRY_DATA *)urb->buffer; ++ data->PeripheralQaulifier = msc->block_dev_state & (DEVICE_EJECTED | DEVICE_CHANGE_ON) ? 0x1 : 0; ++ data->PeripheralDeviceType = 0x00; ++ data->RMB = 0x1; ++ data->ResponseDataFormat = 0x1; ++ data->AdditionalLength = 0x1f; ++ ++ strncpy(data->VendorInformation, CONFIG_OTG_MSC_MANUFACTURER, strlen(CONFIG_OTG_MSC_MANUFACTURER)); ++ strncpy(data->ProductIdentification, CONFIG_OTG_MSC_PRODUCT_NAME, strlen(CONFIG_OTG_MSC_PRODUCT_NAME)); ++ ++ return msc_dispatch_query_urb(urb, msc, length, USB_MSC_PASSED); ++} ++ ++/*! msc_scsi_read_format_capacity - process a query ++ * ++ * Used by: ++ * win2k ++ * ++ * Returns non-zero if there is an error in the USB layer. ++ */ ++int msc_scsi_read_format_capacity(struct msc_private *msc, char *name, int op) ++{ ++ SCSI_READ_FORMAT_CAPACITY_DATA *data; ++ struct usbd_urb *urb; ++ int length = sizeof(SCSI_READ_FORMAT_CAPACITY_DATA); ++ u32 block_num = msc->capacity; ++ u32 block_len; ++ ++ RETURN_EINVAL_IF(!(urb = msc_alloc_urb(msc, length))); ++ ++ data = (SCSI_READ_FORMAT_CAPACITY_DATA *) urb->buffer; ++ ++ data->CapacityListHeader.CapacityListLength = sizeof(data->CurrentMaximumCapacityDescriptor); ++ ++ data->CurrentMaximumCapacityDescriptor.NumberofBlocks = block_num; ++ data->CurrentMaximumCapacityDescriptor.DescriptorCode = 0x03; ++ memcpy(data->CurrentMaximumCapacityDescriptor.BlockLength + 1, &block_len, sizeof(block_len)); ++ ++ return msc_dispatch_query_urb(urb, msc, length, USB_MSC_PASSED); ++} ++ ++/*! msc_read_capacity - process a read_capacity command ++ * ++ * Used by: ++ * win2k ++ * ++ * Returns non-zero if there is an error in the USB layer. ++ */ ++int msc_scsi_read_capacity(struct msc_private *msc, char *name, int op) ++{ ++ SCSI_READ_CAPACITY_COMMAND *command = (SCSI_READ_CAPACITY_COMMAND *)&msc->command.CBWCB; ++ SCSI_READ_CAPACITY_DATA *data; ++ struct usbd_urb *urb; ++ int length = 8; ++ u32 lba; ++ ++ /* ++ * C.f. RBC 5.3 ++ */ ++ lba = be32_to_cpu(command->LogicalBlockAddress); ++ ++ TRACE_MSG1(MSC,"READ CAPACITY LBA: %d", lba); ++ ++ if ((command->PMI > 1) || (!command->PMI && lba)) { ++ TRACE_MSG1(MSC,"READ CAPACITY PMI: %d", command->PMI); ++ return msc_start_sending_csw_failed (msc, SCSI_SENSEKEY_INVALID_FIELD_IN_CDB, lba, USB_MSC_FAILED); ++ } ++ ++ // alloc urb ++ RETURN_EINVAL_IF(!(urb = msc_alloc_urb(msc, length))); ++ ++ data = (SCSI_READ_CAPACITY_DATA *) urb->buffer; ++ ++ data->LastLogicalBlockAddress = cpu_to_be32(msc->capacity); ++ data->BlockLengthInBytes = cpu_to_be32(msc->block_size); ++ ++ TRACE_MSG2(MSC,"RECV READ CAPACITY lba: %x block_size: %x", ++ be32_to_cpu(data->LastLogicalBlockAddress), be32_to_cpu(data->BlockLengthInBytes)); ++ ++ return msc_dispatch_query_urb(urb, msc, length, USB_MSC_PASSED); ++} ++ ++ ++/*! msc_scsi_request_sense - process a request_sense command ++ * ++ * Returns non-zero if there is an error in the USB layer. ++ */ ++int msc_scsi_request_sense(struct msc_private *msc, char *name, int op) ++{ ++ SCSI_REQUEST_SENSE_COMMAND* command = (SCSI_REQUEST_SENSE_COMMAND *)&msc->command.CBWCB; ++ SCSI_REQUEST_SENSE_DATA *data; ++ ++ /* ++ * C.f. SPC2 7.20 REQUEST SENSE command ++ */ ++ ++ struct usbd_urb *urb; ++ int length = sizeof(SCSI_REQUEST_SENSE_DATA); ++ ++ // alloc urb ++ RETURN_EINVAL_IF(!(urb = msc_alloc_urb(msc, length))); ++ ++ data = (SCSI_REQUEST_SENSE_DATA *) urb->buffer; ++ memset(command, 0x0, length); ++ data->ErrorCode = SCSI_ERROR_CURRENT; ++ data->SenseKey = msc->sensedata >> 16; ++ data->AdditionalSenseLength = 0xa; /* XXX is this needed */ ++ data->AdditionalSenseCode = msc->sensedata >> 8; ++ data->AdditionalSenseCodeQualifier = msc->sensedata; ++ data->Valid = 1; ++ ++ set_sense_data(msc, SCSI_SENSEKEY_NO_SENSE, 0); ++ ++ return msc_dispatch_query_urb(urb, msc, length, USB_MSC_PASSED); ++} ++ ++/*! msc_scsi_mode_sense - process a request_sense command ++ * ++ * Used by: ++ * win2k ++ * ++ * Returns non-zero if there is an error in the USB layer. ++ */ ++int msc_scsi_mode_sense(struct msc_private *msc, char *name, int op) ++{ ++ SCSI_MODE_SENSE_COMMAND *command = (SCSI_MODE_SENSE_COMMAND *)&msc->command.CBWCB; ++ SCSI_MODE_SENSE_DATA *data; ++ int length = sizeof(SCSI_MODE_SENSE_DATA); ++ ++ struct usbd_urb *urb; ++ u8 *cp; ++ ++ TRACE_MSG4(MSC,"MODE SENSE dbd: %02x PageControl: %02x PageCode: %02x Alloc: %02x", ++ command->DBD, command->PageControl, command->PageCode, 0); ++ length = 8; ++ ++ // alloc urb ++ RETURN_EINVAL_IF(!(urb = msc_alloc_urb(msc, length))); ++ ++ cp = (u8 *) urb->buffer; ++ memset(cp, 0x0, length); ++ ++ cp[0] = 0; ++ cp[1] = 0; ++ cp[2] = 0; // 0x80 is writeprotect ++ cp[3] = 0x08; ++ cp[4] = 0; ++ cp[5] = 0; ++ cp[6] = 0; ++ cp[7] = 0; ++ ++ return msc_dispatch_query_urb(urb, msc, length, USB_MSC_PASSED); ++} ++ ++/*! msc_scsi_mode_sense - process a request_sense command ++ * ++ * Used by: ++ * win2k ++ * ++ * XXX this doesn't work, need to re-implement and add these pages. ++ * ++ * Returns non-zero if there is an error in the USB layer. ++ */ ++int new_msc_scsi_mode_sense(struct msc_private *msc, char *name, int op) ++{ ++ ++ /* ++ * C.f. SPC2 7.8.1 MODE SENSE(6) command ++ */ ++ ++ static READ_WRITE_ERROR_RECOVERY_PAGE page_01 = { ++ PageCode:0x01, ++ PageLength:0x0A, ++ ReadRetryCount:0x03, ++ WriteRetryCount:0x80, ++ }; ++ static FLEXIBLE_DISK_PAGE page_05 = { ++ PageCode:0x05, ++ PageLength:0x1E, ++ TransferRate:__constant_cpu_to_be16(0xFA00), ++ NumberofHeads:0xA0, ++ SectorsperTrack:0x00, ++ DataBytesperSector:__constant_cpu_to_be16(0x0002), ++ NumberofCylinders:__constant_cpu_to_be16(0x0000), ++ MotorOnDelay:0x05, ++ MotorOffDelay:0x1E, ++ MediumRotationRate:__constant_cpu_to_be16(0x6801), ++ }; ++ static REMOVABLE_BLOCK_ACCESS_CAPABILITIES_PAGE page_1b = { ++ PageCode:0x1B, ++ PageLength:0x0A, ++ TLUN:0x01, ++ }; ++ static TIMER_AND_PROTECT_PAGE page_1c = { ++ PageCode:0x1c, ++ PageLength:0x06, ++ InactivityTimeMultiplier:0x0A, ++ }; ++ ++ SCSI_MODE_SENSE_COMMAND *command = (SCSI_MODE_SENSE_COMMAND *)&msc->command.CBWCB; ++ SCSI_MODE_SENSE_DATA *data; ++ struct usbd_urb *urb; ++ int length = sizeof(SCSI_MODE_SENSE_DATA); ++ ++ ++ TRACE_MSG4(MSC,"MODE SENSE dbd: %02x PageControl: %02x PageCode: %02x Alloc: %02x", ++ command->DBD, command->PageControl, command->PageCode, 0); ++ ++ ++ if (msc->block_dev_state & DEVICE_EJECTED) { ++ u16 sector = htons((unsigned short)msc->block_size); ++ u16 cylinder = 0; ++ page_05.NumberofHeads = 0; ++ page_05.SectorsperTrack = 0; ++ memcpy(&page_05.DataBytesperSector, §or, sizeof(sector)); ++ memcpy(&page_05.NumberofCylinders, &cylinder, sizeof(cylinder)); ++ } ++ else { ++ u16 sector = htons((unsigned short)msc->block_size); ++ u32 size = DEF_NUMBER_OF_HEADS * DEF_SECTORS_PER_TRACK * sector; ++ u16 cylinder = htons(msc->capacity / size); ++ page_05.NumberofHeads = DEF_NUMBER_OF_HEADS; ++ page_05.SectorsperTrack = DEF_SECTORS_PER_TRACK; ++ memcpy(&page_05.DataBytesperSector, §or, sizeof(sector)); ++ memcpy(&page_05.NumberofCylinders, &cylinder, sizeof(cylinder)); ++ } ++ ++ if (SCSI_MODEPAGE_CONTROL_CURRENT != command->PageControl) { ++ TRACE_MSG2(MSC,"MODE SENSE - requeested other than CONTROL_CURRENT: %d %d", ++ command->PageControl, command->PageCode); ++ return msc_start_sending_csw_failed (msc, SCSI_SENSEKEY_INVALID_FIELD_IN_CDB, msc->lba, USB_MSC_FAILED); ++ } ++ ++ RETURN_EINVAL_IF(!(urb = msc_alloc_urb(msc, length))); ++ data = (SCSI_MODE_SENSE_DATA *) urb->buffer; ++ ++ data->ModeParameterHeader.WriteProtect = msc->block_dev_state & DEVICE_WRITE_PROTECTED ? 1 : 0; ++ ++ switch (command->PageCode) { ++ case SCSI_MODEPAGE_ERROR_RECOVERY: ++ TRACE_MSG0(MSC, "MODEPAGE ERROR_RECOVERY"); ++ length = sizeof(MODE_PARAMETER_HEADER) + sizeof(page_01); ++ data->ModeParameterHeader.ModeDataLength = length - 1; ++ memcpy(&data->ModePages, &page_01, sizeof(page_01)); ++ break; ++ case SCSI_MODEPAGE_FLEXIBLE_DISK_PAGE: ++ TRACE_MSG0(MSC, "MODEPAGE FLEXIBLE_DISK_PAGE"); ++ length = sizeof(MODE_PARAMETER_HEADER) + sizeof(page_05); ++ data->ModeParameterHeader.ModeDataLength = length - 1; ++ memcpy(&data->ModePages, &page_05, sizeof(page_05)); ++ break; ++ case SCSI_MODEPAGE_REMOVABLE_BLOCK_ACCESS: ++ TRACE_MSG0(MSC, "MODEPAGE REMOVABLE_BLOCK_ACCESS"); ++ length = sizeof(MODE_PARAMETER_HEADER) + sizeof(page_1b); ++ data->ModeParameterHeader.ModeDataLength = length - 1; ++ memcpy(&data->ModePages, &page_1b, sizeof(page_1b)); ++ break; ++ case SCSI_MODEPAGE_INFORMATION_EXCEPTIONS: ++ TRACE_MSG0(MSC, "MODEPAGE INFORMATION_EXCEPTIONS"); ++ length = sizeof(MODE_PARAMETER_HEADER) + sizeof(page_1c); ++ data->ModeParameterHeader.ModeDataLength = length - 1; ++ memcpy(&data->ModePages, &page_1c, sizeof(page_1c)); ++ break; ++ case SCSI_MODEPAGE_ALL_SUPPORTED: ++ TRACE_MSG0(MSC, "MODEPAGE ALL_SUPPORTED"); ++ length = sizeof(MODE_PARAMETER_HEADER) + sizeof(MODE_ALL_PAGES); ++ data->ModeParameterHeader.ModeDataLength = length - 1; ++ memcpy(&data->ModePages.ModeAllPages.ReadWriteErrorRecoveryPage, &page_01, sizeof(page_01)); ++ memcpy(&data->ModePages.ModeAllPages.FlexibleDiskPage, &page_05, sizeof(page_05)); ++ memcpy(&data->ModePages.ModeAllPages.RemovableBlockAccessCapabilitiesPage, &page_1b, sizeof(page_1b)); ++ memcpy(&data->ModePages.ModeAllPages.TimerAndProtectPage, &page_1c, sizeof(page_1c)); ++ break; ++ case SCSI_MODEPAGE_CACHING: ++ // see file_storage.c for an example if we want to support this ++ TRACE_MSG0(MSC, "MODEPAGE CACHING (not supported)"); ++ break; ++ } ++ ++ TRACE_MSG2(MSC,"LENGTH: %d %d", msc->command.dCBWDataTransferLength, length); ++ ++ /* ++ * XXX verify that length is <= 256 bytes, return CHECK_CONDITION if it is ++ */ ++ length = MIN(msc->command.dCBWDataTransferLength, length); ++ ++ return msc_dispatch_query_urb(urb, msc, length, USB_MSC_PASSED); ++} ++ ++ ++/*! msc_scsi_test_unit_ready - process a request_sense command ++ * ++ * Used by: ++ * win2k ++ * ++ * Returns non-zero if there is an error in the USB layer. ++ */ ++int msc_scsi_test_unit_ready(struct msc_private *msc, char *name, int op) ++{ ++ return msc_start_sending_csw(msc->function, msc, USB_MSC_PASSED); ++} ++ ++ ++/*! msc_scsi_prevent_allow - process a request_sense command ++ * ++ * Used by: ++ * win2k ++ * ++ * Returns non-zero if there is an error in the USB layer. ++ */ ++int msc_scsi_prevent_allow(struct msc_private *msc, char *name, int op) ++{ ++ SCSI_PREVENT_ALLOW_MEDIA_REMOVAL_COMMAND* command = (SCSI_PREVENT_ALLOW_MEDIA_REMOVAL_COMMAND*)&msc->command.CBWCB; ++ ++ /* ++ * C.f. SPC2 7.12 Table 78 PREVENT ALLOW MEDIA REMOVAL Prevent Field ++ * ++ * 00b Medium removal shall be allowed from both the data transport ++ * element and the attached medium changer (if any). ++ * 01b Medium removal shall be prohibited from the data transport ++ * element but allowed from the attached medium changer (if any). ++ * 10b Medium removal shall be allowed for the data transport element ++ * but prohibited for the attached medium changer. ++ * 11b Medium remval shall be prohibited from both the data transport ++ * element and the attached medium changer ++ * ++ * Prevention shall terminate after 00b or 10b, after a SYNC CACHE or hard reset. ++ */ ++ ++ // XXX TODO ++ // this is from storageproto.c, shouldn't we implement something? ++ if (command->Prevent) ++ return msc_start_sending_csw_failed (msc, SCSI_SENSEKEY_INVALID_FIELD_IN_CDB, msc->lba, USB_MSC_FAILED); ++ ++ return msc_start_sending_csw(msc->function, msc, USB_MSC_PASSED); ++} ++ ++ ++/*! msc_scsi_start_stop - process a request_sense command ++ * ++ * C.f. RBC 5.4 and 5.4.2 ++ * ++ * Used by: ++ * win2k ++ * ++ * Returns non-zero if there is an error in the USB layer. ++ */ ++int msc_scsi_start_stop(struct msc_private *msc, char *name, int op) ++{ ++ SCSI_START_STOP_COMMAND* command = (SCSI_START_STOP_COMMAND*)&msc->command.CBWCB; ++ ++ TRACE_MSG4(MSC,"START STOP: Immed: %d Power: %x LoEj: %d Start: %d", ++ command->IMMED, command->PowerConditions, command->LoEj, command->Start); ++ /* ++ * C.f. 5.4 ++ * ++ * IMMED - if set return status immediately after command validation, otherwise ++ * return status as soon operation is completed. ++ * ++ * C.f. 5.4.1 Table 8 POWER CONDITIONS ++ * ++ * 0 - M - no change in power condition ++ * 1 - M - place device in active condition ++ * 2 - M - place device in idle condition ++ * 3 - M - place device in Standby condition ++ * 4 - - reserved ++ * 5 - M - place device in Sleep condition ++ * 6 - - reserved ++ * 7 - 0 - Device Control ++ * ++ * C.f. 5.4.2 Table 9 START STOP control bit definitions ++ * ++ * Power Load/Eject Start ++ * 1-7 x x LoEj and Start Ignored ++ * 0 0 0 Stop the medium ++ * 0 0 1 Make the medium ready ++ * 0 1 0 Unload the medium ++ * 0 1 1 Load the medium ++ * ++ */ ++ // XXX TODO ++ // this is from storageproto.c, shouldn't we implement something? ++ ++ if (command->Start && command->LoEj) ++ return msc_start_sending_csw_failed (msc, SCSI_SENSEKEY_INVALID_FIELD_IN_CDB, msc->lba, USB_MSC_FAILED); ++ ++ return msc_start_sending_csw(msc->function, msc, USB_MSC_PASSED); ++} ++ ++/*! msc_scsi_verify - process a verify command ++ * ++ * Used by: ++ * win2k ++ * ++ * Returns non-zero if there is an error in the USB layer. ++ */ ++int msc_scsi_verify(struct msc_private *msc, char *name, int op) ++{ ++ SCSI_VERIFY_COMMAND *command = (SCSI_VERIFY_COMMAND *)&msc->command.CBWCB; ++ ++ /* ++ * C.f. RBC 5.7 VERIFY command ++ */ ++ // XXX This actually should use the read_10 function and when ++ // finished reading simply send the following ++ return msc_start_sending_csw(msc->function, msc, USB_MSC_PASSED); ++} ++ ++ ++/*! msc_scsi_mode_select - process a select command ++ * ++ * Returns non-zero if there is an error in the USB layer. ++ */ ++int msc_scsi_mode_select(struct msc_private *msc, char *name, int op) ++{ ++ //SCSI_MODE_SELECT_COMMAND *command = (SCSI_MODE_SELECT_COMMAND *)&msc->command.CBWCB; ++ ++ /* ++ * C.f. SPC2 7.6 MODE SELECT(6) command ++ */ ++ ++ // if less than correct amount of data return USB_MSC_PHASE_ERROR - see MV ++ // ++ return msc_start_sending_csw(msc->function, msc, USB_MSC_PASSED); ++} ++ ++/*! msc_private_pcs - process a private command ++ * ++ * Returns non-zero if there is an error in the USB layer. ++ */ ++int msc_cmd_private_pcs(struct msc_private *msc, char *name, int op) ++{ ++ return msc_start_sending_csw_failed (msc, SCSI_SENSEKEY_INVALID_COMMAND, msc->lba, USB_MSC_FAILED); ++} ++ ++/*! msc_cmd_unknown - process an unknown command ++ * ++ * Returns non-zero if there is an error in the USB layer. ++ */ ++int msc_cmd_unknown(struct msc_private *msc, char *name, int op) ++{ ++ return msc_start_sending_csw_failed (msc, SCSI_SENSEKEY_INVALID_COMMAND, msc->lba, USB_MSC_FAILED); ++} ++ ++struct rbc_dispatch { ++ u8 op; ++ char *name; ++ int (*rbc_command) (struct msc_private *, char *, int op); ++ int device_check; ++}; ++ ++/*! Command cross reference ++ * ++ * This is the list of commands observed from each host OS. It is necessarily ++ * incomplete in that we not have reached some condition necessary to have ++ * other commands used. ++ * Win2k WinXP ++ * SCSI_TEST_UNIT_READY yes yes ++ * SCSI_READ_10 yes yes ++ * SCSI_WRITE_10 yes yes ++ * SCSI_READ_CAPACITY yes yes ++ * SCSI_VERIFY yes ++ * SCSI_INQUIRY yes yes ++ * SCSI_MODE_SENSE yes ++ * SCSI_READ_FORMAT_CAPACITY yes yes ++ * SCSI_REQUEST_SENSE ++ * SCSI_PREVENT_ALLOW_MEDIA_REMOVAL ++ * SCSI_START_STOP ++ * SCSI_MODE_SELECT ++ * SCSI_FORMAT_UNIT ++ * ++ */ ++ ++struct rbc_dispatch rbc_dispatch_table[] = { ++ { SCSI_TEST_UNIT_READY, "SCSI_TEST_UNIT_READY", msc_scsi_test_unit_ready, NOZLP }, ++ { SCSI_READ_10, "SCSI_READ_10", msc_scsi_read_10, SENDZLP }, ++ { SCSI_WRITE_10, "SCSI_WRITE_10", msc_scsi_write_10, NOZLP }, ++ { SCSI_READ_CAPACITY, "SCSI_READ_CAPACITY", msc_scsi_read_capacity, SENDZLP }, ++ { SCSI_VERIFY, "SCSI_VERIFY", msc_scsi_verify, NOCHK }, ++ { SCSI_INQUIRY, "SCSI_INQUIRY", msc_scsi_inquiry, NOCHK }, ++ { SCSI_MODE_SENSE, "SCSI_MODE_SENSE", msc_scsi_mode_sense, NOCHK }, ++ { SCSI_READ_FORMAT_CAPACITY, "SCSI_READ_FORMAT_CAPACITY", msc_scsi_read_format_capacity, SENDZLP }, ++ { SCSI_REQUEST_SENSE, "SCSI_REQUEST_SENSE", msc_scsi_request_sense, NOCHK }, ++ { SCSI_PREVENT_ALLOW_MEDIA_REMOVAL, "SCSI_PREVENT_ALLOW_MEDIA_REMOVAL", msc_scsi_prevent_allow, NOZLP }, ++ { SCSI_START_STOP, "SCSI_START_STOP", msc_scsi_start_stop, NOZLP }, ++ { SCSI_MODE_SELECT, "SCSI_MODE_SELECT", msc_scsi_mode_select, NOCHK }, ++ { SCSI_FORMAT_UNIT, "SCSI_FORMAT_UNIT", msc_cmd_unknown, NOCHK }, ++ ++ { SCSI_READ_6, "SCSI_READ_6", msc_cmd_unknown, NOCHK }, ++ { SCSI_WRITE_6, "SCSI_WRITE_6", msc_cmd_unknown, NOCHK }, ++ { SCSI_RESERVE, "SCSI_RESERVE", msc_cmd_unknown, NOCHK }, ++ { SCSI_RELEASE, "SCSI_RELEASE", msc_cmd_unknown, NOCHK }, ++ { SCSI_SEND_DIAGNOSTIC, "SCSI_SEND_DIAGNOSTIC", msc_cmd_unknown, NOCHK }, ++ { SCSI_SYNCHRONIZE_CACHE, "SCSI_SYNCHRONIZE_CACHE", msc_cmd_unknown, NOCHK }, ++ { SCSI_MODE_SENSE_10, "SCSI_MODE_SENSE_10", msc_cmd_unknown, NOCHK }, ++ { SCSI_REZERO_UNIT, "SCSI_REZERO_UNIT", msc_cmd_unknown, NOCHK }, ++ { SCSI_SEEK_10, "SCSI_SEEK_10", msc_cmd_unknown, NOCHK }, ++ { SCSI_WRITE_AND_VERIFY, "SCSI_WRITE_AND_VERIFY", msc_cmd_unknown, NOCHK }, ++ { SCSI_WRITE_12, "SCSI_WRITE_12", msc_cmd_unknown, NOCHK }, ++ { SCSI_READ_12, "SCSI_READ_12", msc_cmd_unknown, NOCHK }, ++ ++ { SCSI_PRIVATE_PCS, "SCSI_PRIVATE_PCS", msc_cmd_private_pcs, NOCHK }, ++ ++ { 0xff, "SCSI_UNKNOWN", msc_cmd_unknown, NOCHK }, ++}; ++ ++ ++/*! msc_recv_command - process a new CBW ++ * ++ * Return non-zero if urb was not disposed of. ++ */ ++void msc_recv_command(struct usbd_urb *urb, struct msc_private *msc) ++{ ++ COMMAND_BLOCK_WRAPPER *command = (COMMAND_BLOCK_WRAPPER *)urb->buffer; ++ u8 op = command->CBWCB[0]; ++ struct rbc_dispatch *dispatch; ++ ++ /* ++ * c.f. section 6.2 - Valid and Meaningful CBW ++ * c.f. section 6.2.1 - Valid CBW ++ * ++ * The CBW was received after the device had sent a CSW or after a ++ * reset XXX check that we only set MSC_READY after reset or sending ++ * CSW. ++ * ++ * The CBW is 31 (1Fh) bytes in length and the bCBWSignature is ++ * equal to 43425355h. ++ */ ++ THROW_IF(31 != urb->actual_length, error); ++ THROW_IF(CBW_SIGNATURE != le32_to_cpu(command->dCBWSignature), error); ++ ++ /* ++ * c.f. section 6.2.2 - Meaningful CBW ++ * ++ * no reserved bits are set ++ * the bCBWLUN contains a valid LUN supported by the device ++ * both bCBWCBlength and the content of the CBWCB are in accordance with bInterfaceSubClass ++ */ ++ ++ // XXX checklun etc ++ ++ /* ++ * Success ++ */ ++ memcpy(&msc->command, command, sizeof(COMMAND_BLOCK_WRAPPER)); ++ msc->data_transferred_in_bytes = msc->TransferLength_in_blocks = msc->TransferLength_in_bytes = 0; ++ ++ TRACE_TAG(command->dCBWTag, urb->framenum); ++ usbd_free_urb(urb); ++ urb = NULL; ++ ++ /* ++ * Search using the opcode to find the dispatch function to use and ++ * call it. ++ */ ++ for (dispatch = rbc_dispatch_table; dispatch->op != 0xff; dispatch++) { ++ CONTINUE_UNLESS ((dispatch->op == op)); ++ ++ TRACE_CBW(dispatch->name, dispatch->op, dispatch->device_check); ++ TRACE_RECV(&(command->CBWCB[1])); ++ ++ /* Depending on the command we may need to check if the device is available ++ * and either fail or send a ZLP if it is not ++ */ ++ if (dispatch->device_check) ++ RETURN_IF (msc_check_blockdev_name(msc, dispatch->device_check, dispatch->name)); ++ ++ /* Call the specific function that implements the specified command ++ */ ++ if (dispatch->rbc_command(msc, dispatch->name, op)) ++ TRACE_MSG0(MSC,"COMMAND ERROR"); ++ return; ++ } ++ ++ /* FALL THROUGH if no match is found */ ++ ++ CATCH(error) { ++ TRACE_MSG0(MSC,"RECV CBW ERROR"); ++ if (urb) ++ usbd_free_urb(urb); ++ ++ /* XXX which of these do we stall? ++ */ ++ usbd_halt_endpoint(urb->function_instance, BULK_IN); ++ usbd_halt_endpoint(urb->function_instance, BULK_OUT); ++ } ++ msc_cmd_unknown(msc, "CMD_UNKNOWN", op); ++} ++ ++ ++/* Sent Function - process a sent urb ********************************************************** */ ++ ++/*! msc_urb_sent - called to indicate URB transmit finished ++ * @param tx_urb: pointer to struct usbd_urb ++ * @param rc: result ++ * ++ * This is called when an urb is sent. Depending on current state ++ * it may: ++ * ++ * - continue sending data ++ * - send a CSW ++ * - start a recv for a CBW ++ * ++ * This is called from BOTTOM HALF context. ++ * ++ * @return non-zero if urb was not disposed of. ++ */ ++int msc_urb_sent (struct usbd_urb *tx_urb, int rc) ++{ ++ struct usbd_function_instance *function; ++ struct msc_private *msc = &msc_private; ++ ++ RETURN_EINVAL_IF(!(function = tx_urb->function_instance)); ++ RETURN_EINVAL_IF(usbd_get_device_status(function) == USBD_CLOSING); ++ RETURN_EINVAL_IF(usbd_get_device_state(function) != STATE_CONFIGURED); ++ ++ switch (msc->command_state) { ++ case MSC_DATA_IN_READ: ++ case MSC_DATA_IN_READ_FINISHED: ++ TRACE_MSG0(MSC,"URB SENT READ"); ++ return msc_in_read_10_urb_sent(tx_urb, msc); ++ ++ case MSC_QUERY: ++ // finished, send CSW ++ TRACE_MSG0(MSC,"URB SENT QUERY"); ++ msc_start_sending_csw(msc->function, msc, USB_MSC_PASSED); ++ break; ++ ++ case MSC_STATUS: ++ default: ++ // sent a CSW need to receive the next CBW ++ TRACE_MSG0(MSC,"URB SENT STATUS"); ++ msc->command_state = MSC_READY; ++ msc_start_recv_urb(msc->function, msc, sizeof(COMMAND_BLOCK_WRAPPER)); ++ break; ++ } ++ usbd_free_urb (tx_urb); ++ return 0; ++} ++ ++ ++/* Receive Function - receiving an urb ********************************************************* */ ++ ++/*! msc_recv_urb - process a received urb ++ * ++ * Return non-zero if urb was not disposed of. ++ */ ++static int msc_recv_urb (struct usbd_urb *rcv_urb, int rc) ++{ ++ struct msc_private *msc = &msc_private; ++ ++ RETURN_EINVAL_IF(!msc->connected); ++ ++ //TRACE_MSG2(MSC, "RECV URB length: %d state: %d", rcv_urb->actual_length, msc->command_state); ++ ++ switch(msc->command_state) { ++ ++ // ready to start a new transaction ++ case MSC_READY: ++ msc_recv_command(rcv_urb, msc); ++ return 0; ++ ++ // we think we are receiving data ++ case MSC_DATA_OUT_WRITE: ++ case MSC_DATA_OUT_WRITE_FINISHED: ++ msc_recv_out_blocks(rcv_urb, msc); ++ return 0; ++ ++ // we think we are sending data ++ case MSC_DATA_IN_READ: ++ case MSC_DATA_IN_READ_FINISHED: ++ msc_start_sending_csw_failed (msc, SCSI_SENSEKEY_INVALID_COMMAND, msc->lba, USB_MSC_FAILED); ++ break; ++ ++ // we think we are sending status ++ case MSC_STATUS: ++ msc_start_sending_csw_failed (msc, SCSI_SENSEKEY_INVALID_COMMAND, msc->lba, USB_MSC_FAILED); ++ break; ++ ++ // we don't think ++ case MSC_UNKNOWN: ++ default: ++ TRACE_MSG0(MSC,"RECV URB ERROR"); ++ usbd_halt_endpoint(rcv_urb->function_instance, BULK_OUT); ++ } ++ // let caller dispose of urb ++ return -EINVAL; ++} ++ ++/* USB Device Functions ************************************************************************ */ ++ ++ ++static void msc_device_request (struct usbd_function_instance *function, struct usbd_device_request *request) ++{ ++ TRACE_MSG0(MSC,"--"); ++} ++ ++static void msc_set_configuration (struct usbd_function_instance *function, int wValue) ++{ ++ TRACE_MSG1(MSC,"wValue: %02x", wValue); ++ ++} ++ ++static void msc_set_interface (struct usbd_function_instance *function, int wIndex, int wValue) ++{ ++ TRACE_MSG2(MSC,"wIndex: %02x wValue: %02x", wIndex, wValue); ++ ++} ++ ++static void msc_endpoint_cleared (struct usbd_function_instance *function, int bEndpointAddress) ++{ ++ TRACE_MSG1(MSC,"bEndpointAddress: %02x", bEndpointAddress); ++ ++} ++ ++/* USB Device Functions ************************************************************************ */ ++/*! msc_event_handler - process a device event ++ * ++ * This function is called when an USBD event occurs. ++ * ++ * This is called from INTERRUPT context. ++ */ ++void msc_event_handler (struct usbd_function_instance *function, usbd_device_event_t event, int data) ++{ ++ unsigned long flags; ++ struct msc_private *msc = &msc_private; ++ int connected; ++ switch (event) { ++ case DEVICE_CONFIGURED: ++ TRACE_MSG0(MSC,"EVENT CONFIGURED"); ++ msc->connected = 1; ++ msc->command_state = MSC_READY; ++ msc_start_recv_urb(function, msc, sizeof(COMMAND_BLOCK_WRAPPER)); ++ #if 0 ++ local_irq_save(flags); ++ if (msc->io_state & MSC_IOCTL_WAITING) { ++ msc->io_state &= ~MSC_IOCTL_WAITING; ++ TRACE_MSG0(MSC, "WAKEUP"); ++ } ++ local_irq_restore(flags); ++ #endif ++ wake_up_interruptible(&msc->ioctl_wq); ++ break; ++ ++ case DEVICE_BUS_INACTIVE: ++ case DEVICE_RESET: ++ case DEVICE_DE_CONFIGURED: ++ TRACE_MSG2(MSC,"EVENT RESET: connected %d msc->io_state", msc->connected, msc->io_state); ++ connected = msc->connected; ++ msc->connected = 0; ++ #if 0 ++ local_irq_save(flags); ++ if (msc->io_state & MSC_IOCTL_WAITING) { ++ msc->io_state &= ~MSC_IOCTL_WAITING; ++ TRACE_MSG0(MSC, "WAKEUP"); ++ wake_up_interruptible(&msc->ioctl_wq); ++ } ++ local_irq_restore(flags); ++ #endif ++ wake_up_interruptible(&msc->ioctl_wq); ++ BREAK_UNLESS(connected); ++ ++ // XXX we should have a semaphore to protect this ++ BREAK_UNLESS (msc->rcv_urb_finished); ++ usbd_free_urb (msc->rcv_urb_finished); ++ msc->rcv_urb_finished = NULL; ++ break; ++ ++ default: ++ TRACE_MSG0(MSC,"EVENT IGNORED"); ++ break; ++ } ++} ++ ++/*! msc_device_request - called to indicate urb has been received ++ * ++ * This function is called when a SETUP packet has been received that ++ * should be handled by the function driver. It will not be called to ++ * process the standard chapter nine defined requests. ++ * ++ * Return non-zero for failure. ++ */ ++int msc_device_handler (struct usbd_function_instance *function, struct usbd_device_request *request) ++{ ++ struct msc_private *msc = &msc_private; ++ struct usbd_urb *urb; ++ ++ TRACE_MSG0(MSC,"RECV SETUP"); ++ ++ // verify that this is a usb class request per cdc-acm specification or a vendor request. ++ RETURN_ZERO_IF (!(request->bmRequestType & (USB_REQ_TYPE_CLASS | USB_REQ_TYPE_VENDOR))); ++ ++ // determine the request direction and process accordingly ++ switch (request->bmRequestType & (USB_REQ_DIRECTION_MASK | USB_REQ_TYPE_MASK)) { ++ ++ case USB_REQ_HOST2DEVICE | USB_REQ_TYPE_CLASS: ++ switch (request->bRequest) { ++ case MSC_BULKONLY_RESET: ++ // XXX TODO FIXME ++ return 0; ++ } ++ ++ case USB_REQ_DEVICE2HOST | USB_REQ_TYPE_CLASS: ++ switch (request->bRequest) { ++ case MSC_BULKONLY_GETMAXLUN: ++ RETURN_EINVAL_IF(!(urb = usbd_alloc_urb_ep0(function, 1, NULL))); ++ urb->buffer[0] = 0; ++ urb->actual_length = 1; ++ RETURN_ZERO_IF(!usbd_start_in_urb(urb)); ++ usbd_free_urb(urb); ++ return -EINVAL; ++ } ++ default: ++ break; ++ } ++ return -EINVAL; ++} ++ ++/*! msc_function_enable - this is called by the USBD core layer ++ * ++ * This is called to initialize the function when a bus interface driver ++ * is loaded. ++ */ ++static int msc_function_enable (struct usbd_function_instance *function) ++{ ++ struct msc_private *msc = &msc_private; ++ ++ MOD_INC_USE_COUNT; ++ ++ // XXX TODO need to verify that serial number is minimum of 12 ++ ++ msc->function = function; ++ msc->command_state = MSC_READY; ++ ++ return 0; ++} ++ ++/*! msc_function_disable - this is called by the USBD core layer ++ * ++ * This is called to close the function when a bus interface driver ++ * is unloaded. ++ */ ++static void msc_function_disable (struct usbd_function_instance *function) ++{ ++ struct msc_private *msc = &msc_private; ++ ++ TRACE_MSG0(MSC,"FUNCTION EXIT"); ++ ++ msc->function = NULL; ++ ++ MOD_DEC_USE_COUNT; ++} ++ ++/* ********************************************************************************************* */ ++struct usbd_function_operations msc_function_ops = { ++ event_handler: msc_event_handler, ++ device_request: msc_device_request, ++ function_enable: msc_function_enable, ++ function_disable: msc_function_disable, ++ endpoint_cleared: msc_device_request, ++ endpoint_cleared: msc_set_configuration, ++ endpoint_cleared: msc_set_interface, ++ endpoint_cleared: msc_endpoint_cleared, ++}; ++ ++/* ********************************************************************************************* */ +diff -uNr linux/drivers/no-otg/functions/msc/msc-fd.h linux/drivers/otg/functions/msc/msc-fd.h +--- linux/drivers/no-otg/functions/msc/msc-fd.h 1970-01-01 01:00:00.000000000 +0100 ++++ linux/drivers/otg/functions/msc/msc-fd.h 2006-09-01 21:41:27.000000000 +0200 +@@ -0,0 +1,64 @@ ++/* ++ * otg/msc_fd/msc.h - Mass Storage Class ++ * ++ * Copyright (c) 2003, 2004 Belcarra ++ * ++ * By: ++ * Stuart Lynne <sl@belcarra.com> ++ * Bruce Balden <balden@belcarra.com> ++ * ++ */ ++ ++#ifndef MSC_FD_H ++#define MSC_FD_H 1 ++ ++extern int msc_dispatch_query_urb(struct usbd_urb *urb, struct msc_private *msc, int length, int status); ++extern int msc_start_sending_csw(struct usbd_function_instance *function, struct msc_private *msc, u8 status); ++extern int msc_dispatch_query_urb_zlp(struct msc_private *msc, u32 sensedata, u32 info, int status); ++extern int msc_start_sending_csw_failed(struct msc_private *msc, u32 sensedata, u32 info, int status); ++extern int msc_start_recv_urb(struct usbd_function_instance *function, struct msc_private *msc, int size); ++ ++#define NOCHK 0 ++#define NOZLP 1 ++#define SENDZLP 2 ++ ++#if 1 ++static __inline__ void TRACE_SENSE(unsigned int sense, unsigned int info) ++{ ++ TRACE_MSG2(MSC, "--> SENSE: %06x INFO: %08x", sense, info); ++} ++static __inline__ void TRACE_RLBA(unsigned int lba, unsigned int crc) ++{ ++ TRACE_MSG2(MSC, "<-- rlba [%8x %08x]", lba, crc); ++} ++static __inline__ void TRACE_SLBA(unsigned int lba, unsigned int crc) ++{ ++ TRACE_MSG2(MSC, "--> slba [%8x %08x]", lba, crc); ++} ++static __inline__ void TRACE_TLBA(unsigned int lba, unsigned int crc) ++{ ++ TRACE_MSG2(MSC, "--> tlba [%8x %08x]", lba, crc); ++} ++static __inline__ void TRACE_TAG(unsigned int tag, unsigned int frame) ++{ ++ TRACE_MSG2(MSC, "--> TAG: %8x FRAME: %03x", tag, frame); ++} ++static __inline__ void TRACE_CBW(char *msg, int val, int check) ++{ ++ TRACE_MSG3(MSC, " --> %s %02x check: %d", msg, val, check); ++} ++static __inline__ void TRACE_RECV(unsigned char *cp) ++{ ++ TRACE_MSG8(MSC, "<-- recv [%02x %02x %02x %02x %02x %02x %02x %02x]", ++ cp[0], cp[1], cp[2], cp[3], cp[4], cp[5], cp[6], cp[7]); ++} ++static __inline__ void TRACE_SENT(unsigned char *cp) ++{ ++ TRACE_MSG8(MSC, "--> sent [%02x %02x %02x %02x %02x %02x %02x %02x]", ++ cp[0], cp[1], cp[2], cp[3], cp[4], cp[5], cp[6], cp[7]); ++} ++ ++#endif ++ ++ ++#endif /* MSC_H */ +diff -uNr linux/drivers/no-otg/functions/msc/msc-io-l24.c linux/drivers/otg/functions/msc/msc-io-l24.c +--- linux/drivers/no-otg/functions/msc/msc-io-l24.c 1970-01-01 01:00:00.000000000 +0100 ++++ linux/drivers/otg/functions/msc/msc-io-l24.c 2006-09-01 21:41:28.000000000 +0200 +@@ -0,0 +1,272 @@ ++/* ++ * otg/function/msc/msc-io-l24.c - MSC IO ++ * ++ * Copyright (c) 2004 Belcarra ++ * ++ * By: ++ * Stuart Lynne <sl@lbelcarra.com>, ++ * Tom Rushworth <tbr@belcarra.com>, ++ * Bruce Balden <balden@belcarra.com> ++ * ++ */ ++/*! ++ * @file otg/functions/msc/msc-io-l24.c ++ * @brief ++ * ++ * NOTES ++ * ++ * TODO ++ * ++ * 1. implement prevent removal command. ++ * ++ * @ingroup MSCFunction ++ */ ++ ++#include <otg/otg-compat.h> ++#include <otg/otg-module.h> ++ ++#include <otg/usbp-chap9.h> ++#include <otg/usbp-func.h> ++#include <otg/otg-trace.h> ++#include <otg/otg-api.h> ++ ++#include <linux/poll.h> ++#include <linux/sched.h> ++#include <linux/devfs_fs_kernel.h> ++ ++#include "msc-scsi.h" ++#include "msc.h" ++#include "msc-fd.h" ++#include "crc.h" ++#include "msc-io.h" ++ ++extern void msc_open_blockdev (struct msc_private *msc); ++extern void msc_close_blockdev (struct msc_private *msc); ++extern int msc_connection_blockdev (struct msc_private *msc); ++extern struct msc_private msc_private; ++ ++#define DEVICE_EJECTED 0x0001 //MEDIA_EJECTED ++#define DEVICE_INSERTED 0x0002 //MEDIA_INSERT ++#define DEVICE_MOUNTED 0x0001 //DEVICE_MOUNTED ++#define DEVICE_UNMOUNTED 0x0002 //DEVICE_UNMOUNTED ++ ++extern u32 major; ++extern u32 minor; ++#if 0 ++void msc_io_wait(struct msc_private *msc, u32 flag) ++{ ++ unsigned long flags; ++ msc->io_state |= MSC_IOCTL_WAITING | flag; ++ TRACE_MSG1(MSC, "SLEEPING io_state: %x", msc->io_state); ++ interruptible_sleep_on(&msc->ioctl_wq); ++} ++ ++void msc_io_wakup(struct msc_private *msc) ++{ ++ unsigned long flags; ++ local_irq_save(flags); ++ if (msc->io_state & MSC_IOCTL_WAITING) { ++ msc->io_state &= ~MSC_IOCTL_WAITING; ++ TRACE_MSG0(MSC, "WAKEUP"); ++ wake_up_interruptible(&msc->ioctl_wq); ++ } ++ local_irq_restore(flags); ++} ++#endif ++/*! msc_io_ioctl_internal ++ */ ++int msc_io_ioctl_internal(unsigned int cmd, unsigned long arg) ++{ ++ int i; ++ int len; ++ int flag; ++ ++ static char func_buf[32]; ++ struct otgmsc_mount mount; ++ struct msc_private *msc = &msc_private; ++ unsigned long flags; ++ ++ TRACE_MSG2(MSC, "cmd: %d connect: %d", cmd, msc->connected); ++ memset(&mount, 0, sizeof(mount)); ++ switch (cmd) { ++ ++ /* Mount - make the specified major/minof device available for use ++ * by the USB Host, this does not require an active connection. ++ */ ++ case OTGMSC_START: ++ TRACE_MSG0(MSC, "Mounting the device"); ++ RETURN_EINVAL_IF(copy_from_user(&mount, (void *)arg, _IOC_SIZE(cmd))); ++ ++ TRACE_MSG3(MSC, "major=%d minor=%d state=%d", mount.major, mount.minor, msc->block_dev_state); ++ major = mount.major; ++ minor = mount.minor; ++ ++ //msc->io_state = MSC_INACTIVE; ++ msc_open_blockdev (msc); ++ RETURN_EAGAIN_UNLESS(msc->command_state == MSC_READY); ++ ++ mount.status = (msc->block_dev_state == DEVICE_INSERTED) ? DEVICE_MOUNTED : DEVICE_UNMOUNTED; ++ ++ TRACE_MSG1(MSC, "Device is mounted status: %d", mount.status); ++ ++ // XXX Need to copy result back to user space ++ RETURN_EINVAL_IF (copy_to_user((void *)arg, &mount, sizeof(mount))); ++ TRACE_MSG0(MSC, "Device mounted"); ++ return 0; ++ ++ /* Umount - make the currently mounted device unavailable to the USB Host, ++ * if there is pending block i/o block until it has finished. ++ * Note that if the driver is unloaded the waiting ioctl process ++ * must be woken up and informed with error code. ++ */ ++ case OTGMSC_STOP: ++ ++ TRACE_MSG0(MSC, "Unmounting the device"); ++ ++ RETURN_EINVAL_IF (copy_from_user (&mount, (void *)arg, _IOC_SIZE(cmd))); ++ ++ if (msc->command_state != MSC_READY) { ++ TRACE_MSG0(MSC, "SLEEPING"); ++ interruptible_sleep_on(&msc->ioctl_wq); ++ TRACE_MSG0(MSC, "AWAKE"); ++ } ++ ++ RETURN_EAGAIN_UNLESS(msc->command_state == MSC_INACTIVE); ++ ++ // XXX Need to copy result back to user space ++ msc->major = mount.major; ++ msc->minor = mount.minor; ++ msc_close_blockdev(msc); ++ mount.status = DEVICE_UNMOUNTED; ++ RETURN_EINVAL_IF (copy_to_user((void *)arg, &mount, sizeof(mount))); ++ TRACE_MSG0(MSC, "Device unmounted"); ++ return 0; ++ ++ ++ /* Status - return the current mount status. ++ */ ++ case OTGMSC_STATUS: ++ TRACE_MSG0(MSC, "Mount status"); ++ RETURN_EINVAL_IF (copy_from_user (&mount, (void *)arg, _IOC_SIZE(cmd))); ++ if (msc->block_dev_state == DEVICE_EJECTED) ++ mount.status = DEVICE_UNMOUNTED; ++ else ++ mount.status = DEVICE_MOUNTED; ++ ++ // XXX Need to copy result back to user space ++ RETURN_EINVAL_IF (copy_to_user((void *)arg, &mount, sizeof(mount))); ++ return 0; ++ ++ /* Wait_Connect - if not already connected wait until connected, ++ * Note that if the driver is unloaded the waiting ioctl process ++ * must be woken up and informed with error code. ++ */ ++ case OTGMSC_WAIT_CONNECT: ++ TRACE_MSG1(MSC, "Wait for connect: connected: %d", msc->connected); ++ local_irq_save(flags); ++ ++ if (msc->connected == 0) { ++ TRACE_MSG0(MSC, "SLEEPING"); ++ interruptible_sleep_on (&msc->ioctl_wq); ++ TRACE_MSG0(MSC, "AWAKE"); ++ } ++ RETURN_EAGAIN_UNLESS(msc->connected); ++ ++ RETURN_EINVAL_IF (copy_from_user (&mount, (void *)arg, _IOC_SIZE(cmd))); ++ RETURN_EINVAL_IF (copy_to_user((void *)arg, &mount, sizeof(mount))); ++ TRACE_MSG0(MSC, "Device connected"); ++ return 0; ++ ++ /* Wait_DisConnect - if not already disconnected wait until disconnected, ++ * Note that if the driver is unloaded the waiting ioctl process ++ * must be woken up and informed with error code. ++ */ ++ case OTGMSC_WAIT_DISCONNECT: ++ TRACE_MSG1(MSC, "Wait for disconnect: connected: %d", msc->connected); ++ ++ if (msc->connected == 1) { ++ TRACE_MSG0(MSC, "SLEEPING"); ++ interruptible_sleep_on (&msc->ioctl_wq); ++ TRACE_MSG0(MSC, "AWAKE"); ++ } ++ RETURN_EAGAIN_IF(msc->connected); ++ RETURN_EINVAL_IF (copy_from_user (&mount, (void *)arg, _IOC_SIZE(cmd))); ++ RETURN_EINVAL_IF (copy_to_user((void *)arg, &mount, sizeof(mount))); ++ TRACE_MSG0(MSC, "Device disconnected"); ++ return 0; ++ ++ default: ++ TRACE_MSG1(MSC, "Unknown command: %x", cmd); ++ TRACE_MSG1(MSC, "OTGMSC_START: %x", OTGMSC_START); ++ TRACE_MSG1(MSC, "OTGMSC_WRITEPROTECT: %x", OTGMSC_WRITEPROTECT); ++ TRACE_MSG1(MSC, "OTGMSC_STOP: %x", OTGMSC_STOP); ++ TRACE_MSG1(MSC, "OTGMSC_STATUS: %x", OTGMSC_STATUS); ++ TRACE_MSG1(MSC, "OTGMSC_WAIT_CONNECT: %x", OTGMSC_WAIT_CONNECT); ++ TRACE_MSG1(MSC, "OTGMSC_WAIT_DISCONNECT: %x", OTGMSC_WAIT_DISCONNECT); ++ return -EINVAL; ++ } ++ return 0; ++} ++ ++ ++ ++/*! msc_io_ioctl ++ */ ++int msc_io_ioctl(struct inode *inode, struct file *filp, unsigned int cmd, unsigned long arg) ++{ ++ int i; ++ int len; ++ int flag; ++ ++ //printk(KERN_INFO"%s: cmd: %08x arg: %08x\n", __FUNCTION__, cmd, arg); ++ TRACE_MSG6(MSC, "cmd: %08x arg: %08x type: %02d nr: %02d dir: %02d size: %02d", ++ cmd, arg, _IOC_TYPE(cmd), _IOC_NR(cmd), _IOC_DIR(cmd), _IOC_SIZE(cmd)); ++ ++ RETURN_EINVAL_UNLESS (_IOC_TYPE(cmd) == OTGMSC_MAGIC); ++ RETURN_EINVAL_UNLESS (_IOC_NR(cmd) <= OTGMSC_MAXNR); ++ ++ RETURN_EFAULT_IF((_IOC_DIR(cmd) == _IOC_READ) && !access_ok(VERIFY_WRITE, (void *)arg, _IOC_SIZE(cmd))); ++ RETURN_EFAULT_IF((_IOC_DIR(cmd) == _IOC_WRITE) && !access_ok(VERIFY_READ, (void *)arg, _IOC_SIZE(cmd))); ++ ++ return msc_io_ioctl_internal(cmd, arg); ++} ++ ++ ++ ++int msc_io_proc_ioctl(struct inode *inode, struct file *filp, unsigned int cmd, unsigned long arg) ++{ ++ return msc_io_ioctl(inode, filp, cmd, arg); ++} ++ ++ ++static struct file_operations msc_io_proc_switch_functions = { ++ ioctl:msc_io_proc_ioctl, ++}; ++ ++ ++/* msc_io_init_l24 - initialize ++ */ ++int msc_io_init_l24(void) ++{ ++ struct proc_dir_entry *message = NULL; ++ ++ THROW_IF (!(message = create_proc_entry ("msc_io", 0666, 0)), error); ++ message->proc_fops = &msc_io_proc_switch_functions; ++ CATCH(error) { ++ printk(KERN_ERR"%s: creating /proc/msc_io failed\n", __FUNCTION__); ++ if (message) ++ remove_proc_entry("msc_io", NULL); ++ return -EINVAL; ++ } ++ return 0; ++} ++ ++/* msc_io_exit_l24 - exit ++ */ ++void msc_io_exit_l24(void) ++{ ++ remove_proc_entry("msc_io", NULL); ++} ++ ++ ++ +diff -uNr linux/drivers/no-otg/functions/msc/msc-io.h linux/drivers/otg/functions/msc/msc-io.h +--- linux/drivers/no-otg/functions/msc/msc-io.h 1970-01-01 01:00:00.000000000 +0100 ++++ linux/drivers/otg/functions/msc/msc-io.h 2006-09-01 21:41:28.000000000 +0200 +@@ -0,0 +1,57 @@ ++/* ++ * otg/functions/msc/msc-io.h - Mass Storage Class ++ * ++ * Copyright (c) 2003, 2004 Belcarra ++ * ++ * By: ++ * Stuart Lynne <sl@belcarra.com> ++ * Bruce Balden <balden@belcarra.com> ++ * ++ */ ++/*! ++ * @file otg/functions/msc/msc-io.h ++ * @brief Mass Storage Driver private defines ++ * ++ * OTGMSC_START - make specified major/minor device available to USB Host ++ * OTGMSC_WRITEPROTECT - set or reset write protect flag ++ * ++ * OTGMSC_STOP - remove access to current device, block until pending I/O finished ++ * OTGMSC_STATUS - remove current USB connection status (connected or disconnected) ++ * OTGMSC_WAIT_CONNECT - wait until device is connected (may return immediately if already connected) ++ * OTGMSC_WAIT_DISCONNECT - wait until device is disconnected (may return immediately if already disconnected) ++ * ++ * @ingroup MSCFunction ++ */ ++ ++//#ifndef MSC_H ++//#define MSC_H 1 ++ ++#define MSC_IO "/proc/msc_io" ++ ++struct otgmsc_mount { ++ int major; ++ int minor; ++ int lun; ++ int writeprotect; ++ int result; ++ int status; ++}; ++ ++#define OTGMSC_MAGIC 'M' ++#define OTGMSC_MAXNR 10 ++ ++#define OTGMSC_START _IOWR(OTGMSC_MAGIC, 1, struct otgmsc_mount) ++#define OTGMSC_WRITEPROTECT _IOWR(OTGMSC_MAGIC, 2, struct otgmsc_mount) ++ ++#define OTGMSC_STOP _IOR(OTGMSC_MAGIC, 1, struct otgmsc_mount) ++#define OTGMSC_STATUS _IOR(OTGMSC_MAGIC, 2, struct otgmsc_mount) ++#define OTGMSC_WAIT_CONNECT _IOR(OTGMSC_MAGIC, 3, struct otgmsc_mount) ++#define OTGMSC_WAIT_DISCONNECT _IOR(OTGMSC_MAGIC, 4, struct otgmsc_mount) ++ ++ ++#define MSC_CONNECTED 0x01 ++#define MSC_DISCONNECTED 0x02 ++#define MSC_WRITEPROTECTED 0x04 ++ ++//#endif /* MSC_H */ ++ +diff -uNr linux/drivers/no-otg/functions/msc/msc-linux.c linux/drivers/otg/functions/msc/msc-linux.c +--- linux/drivers/no-otg/functions/msc/msc-linux.c 1970-01-01 01:00:00.000000000 +0100 ++++ linux/drivers/otg/functions/msc/msc-linux.c 2006-09-01 21:41:28.000000000 +0200 +@@ -0,0 +1,935 @@ ++/* ++ * otg/function/msc/msc-linux.c ++ * ++ * Copyright (c) 2003, 2004 Belcarra ++ * ++ * By: ++ * Stuart Lynne <sl@belcarra.com>, ++ * Tom Rushworth <tbr@belcarra.com>, ++ * Bruce Balden <balden@belcarra.com> ++ * ++ * ++ */ ++ ++/*! ++ * @file otg/functions/msc/msc-linux.c ++ * @brief Mass Storage Driver private defines ++ * ++ * ++ * This is a Mass Storage Class Function that uses the Bulk Only protocol. ++ * ++ * To use simply load with something like: ++ * ++ * insmod msc_fd.o vendor_id=0xffff product_id=0xffff ++ * ++ * Notes: ++ * ++ * 1. Currently block I/O is done a page at a time. I have not determined if ++ * it is possible to dispatch a multiple page generic request. It should at ++ * least be possible to queue page requests. ++ * ++ * 2. Currently for READ operations we have a maximum of one outstanding ++ * read block I/O and send urb. These are allowed to overlap so that we can ++ * continue to read data while sending data to the host. ++ * ++ * 3. Currently for WRITE operations we have a maximum of one outstanding ++ * recv urb and one outstanding write block I/O. These are allowed to ++ * overlap so that we can continue to receive data from the host while ++ * waiting for writing to complete. ++ * ++ * 4. It would be possible to allow multiple writes to be pending, to the ++ * limit of the page cache, if desired. ++ * ++ * 5. It should be possible to allow multiple outstanding reads to be ++ * pending, to the limit of the page cache, but this potentially could ++ * require dealing with out of order completions of the reads. Essentially a ++ * list of completed buffer heads would be required to hold any completed ++ * buffer heads that cannot be sent prior to another, earlier request being ++ * completed. ++ * ++ * 6. Currently ioctl calls are available to start and stop device i/o. ++ * ++ * 7. The driver can optionally generate trace messages showing each sectors ++ * CRC as read or written with LBA. These can be compared by user programs to ++ * ensure that the correct data was read and/or written. ++ * ++ * ++ * TODO ++ * ++ * 1. error handling for block io, e.g. what if using with removable block ++ * device (like CF card) and it is removed. ++ * ++ * 2. Currently we memcpy() data from between the urb buffer and buffer ++ * head page. It should be possible to simply use the page buffer for the ++ * urb. ++ * ++ * 3. Should we offer using fileio as an option? This would allow direct access ++ * to a block device image stored in a normal file or direct access to (for example) ++ * ram disks. It would require implementing a separate file I/O kernel thread to ++ * do the actual I/O. ++ * ++ * 4. It may be interesting to support use of SCSI block device and pass the ++ * scsi commands directly to that. This would allow vendor commands for real ++ * devices to be passed through and executed with results being properly ++ * returned to the host. [This is the intended design for the mass storage ++ * specification.] ++ * ++ * ++ * TODO FIXME Bus Interface Notes ++ * ++ * ++ * @ingroup MSCFunction ++ */ ++ ++ ++#include <otg/otg-compat.h> ++#include <otg/usbp-chap9.h> ++#include <otg/usbp-func.h> ++#include <otg/otg-trace.h> ++ ++#include <linux/config.h> ++#include <linux/module.h> ++#include <linux/kernel.h> ++#include <linux/version.h> ++ ++#include <linux/init.h> ++#include <asm/uaccess.h> ++#include <linux/ctype.h> ++#include <linux/timer.h> ++#include <linux/interrupt.h> ++#include <asm/atomic.h> ++#include <linux/random.h> ++#include <linux/slab.h> ++ ++#include <linux/blkdev.h> ++ ++ ++#include "msc-scsi.h" ++#include "msc.h" ++#include "msc-fd.h" ++#ifdef CONFIG_OTG_MSC_BLOCK_TRACE ++#include "crc.h" ++#endif /* CONFIG_OTG_MSC_BLOCK_TRACE */ ++#include "msc-io.h" ++ ++ ++//#include "rbc.h" ++ ++ ++/* Module Parameters ************************************************************************* */ ++ ++u32 vendor_id; ++u32 product_id; ++ ++u32 major; ++u32 minor; ++ ++MODULE_PARM (vendor_id, "i"); ++MODULE_PARM (product_id, "i"); ++MODULE_PARM (major, "i"); ++MODULE_PARM (minor, "i"); ++ ++MODULE_PARM_DESC (vendor_id, "Device Vendor ID"); ++MODULE_PARM_DESC (product_id, "Device Product ID"); ++MODULE_PARM_DESC (major, "Device Major"); ++MODULE_PARM_DESC (minor, "Device Minor"); ++ ++ ++#define DEVICE_EJECTED 0x0001 // MEDIA_EJECTED ++#define DEVICE_INSERTED 0x0002 // MEDIA_INSERT ++ ++#define DEVICE_OPEN 0x0004 // WR_PROTECT_OFF ++#define DEVICE_WRITE_PROTECTED 0x0008 // WR_PROTECT_ON ++ ++#define DEVICE_CHANGE_ON 0x0010 // MEDIA_CHANGE_ON ++ ++#define DEVICE_PREVENT_REMOVAL 0x0020 ++ ++ ++#define DEF_NUMBER_OF_HEADS 0x10 ++#define DEF_SECTORS_PER_TRACK 0x20 ++ ++ ++DECLARE_MUTEX(msc_sem); ++ ++ ++/* MSC ******************************************************************************************** */ ++ ++struct msc_private msc_private; ++ ++int msc_urb_sent (struct usbd_urb *tx_urb, int rc); ++ ++ ++/* Block Device ******************************************************************************** */ ++ ++/*! msc_open_blockdev - open the block device specified in msc->major, msc->minor ++ * ++ * Sets appropriate fields to show current status of block device. ++ * ++ * XXX TODO - this needs to be tested against RO and absent devices. ++ * ++ */ ++void msc_open_blockdev(struct msc_private *msc) ++{ ++ int rc; ++ ++ down(&msc_sem); ++ msc->block_dev_state = DEVICE_EJECTED; ++ ++ TRACE_MSG2(MSC, "OPEN BLOCKDEV: Major: %x Minor: %x", major, minor); ++ ++ /* ++ * Check device information and verify access to the block device. ++ */ ++ THROW_IF (!major, ejected); ++ ++ msc->dev = MKDEV(major, minor); ++ ++ THROW_IF (!(msc->bdev = bdget(kdev_t_to_nr(msc->dev))), ejected); ++ ++ if ((rc = blkdev_get(msc->bdev, FMODE_READ | FMODE_WRITE, 0, BDEV_RAW))) { ++ ++ TRACE_MSG0(MSC,"OPEN BLOCKDEV: cannot open RW"); ++ THROW_IF ((rc = blkdev_get(msc->bdev, FMODE_READ, 0, BDEV_RAW)), ejected); ++ msc->block_dev_state |= DEVICE_WRITE_PROTECTED; ++ } ++ ++ msc->io_state = MSC_INACTIVE; ++ msc->block_dev_state &= ~DEVICE_EJECTED; ++ msc->block_dev_state |= DEVICE_INSERTED | DEVICE_CHANGE_ON; ++ ++ TRACE_MSG1(MSC,"OPEN BLOCKDEV: opened block_dev_state: %x", msc->block_dev_state); ++ ++ /* ++ * Note that capacity must return the LBA of the last addressable block ++ * c.f. RBC 4.4, RBC 5.3 and notes below RBC Table 6 ++ * ++ * The blk_size array contains the number of addressable blocks or N, ++ * capacity is therefore N-1. ++ */ ++ ++ msc->capacity = (blk_size[MAJOR(msc_private.dev)][MINOR(msc_private.dev)] << 1) - 1; ++ msc->block_size = get_hardsect_size(msc->dev); ++ msc->max_blocks = PAGE_SIZE / msc->block_size; ++ ++ TRACE_MSG2(MSC,"blk_size: %x %d", ++ blk_size[MAJOR(msc_private.dev)][MINOR(msc_private.dev)] << 1, ++ blk_size[MAJOR(msc_private.dev)][MINOR(msc_private.dev)] << 1); ++ ++ TRACE_MSG2(MSC,"capacity: %x %d", msc->capacity, msc->capacity); ++ TRACE_MSG2(MSC,"block_size: %x %d", msc->block_size, msc->block_size); ++ TRACE_MSG2(MSC,"max_blocks: %x %d", msc->max_blocks, msc->max_blocks); ++ ++ /* setup generic buffer_head ++ * XXX do we need two pages? it should be possible to have a single page ++ * for both read and write, in fact do we need a read_bh and write_bh? ++ * XXX ensure the page (or pages) get deallocated ++ */ ++ msc->write_pending = msc->read_pending = 0; ++ msc->write_bh.b_rdev = msc->read_bh.b_rdev = msc->dev; ++ msc->write_bh.b_private = msc->read_bh.b_private = msc; ++ msc->read_bh.b_page = alloc_page(GFP_NOIO); // XXX ensure that this gets de-allocated ++ msc->write_bh.b_page = alloc_page(GFP_NOIO); // XXX ensure that this gets de-allocated ++ msc->read_bh.b_data = page_address(msc->read_bh.b_page); ++ msc->write_bh.b_data = page_address(msc->write_bh.b_page); ++ ++ CATCH(ejected) { ++ TRACE_MSG1(MSC,"OPEN BLOCKDEV: EJECTED block_dev_state: %x", msc->block_dev_state); ++ printk(KERN_INFO"%s: Cannot get device %d %d\n", __FUNCTION__, major, minor); ++ } ++ up(&msc_sem); ++} ++ ++/*! msc_close_blockdev - close the device for host ++ */ ++ ++void msc_close_blockdev(struct msc_private *msc) ++{ ++ ++ RETURN_IF(msc->block_dev_state == DEVICE_EJECTED); ++ ++ down(&msc_sem); ++ msc->block_dev_state = DEVICE_EJECTED; ++ if (msc->bdev) ++ blkdev_put(msc->bdev, BDEV_RAW); ++ ++ // XXX this should be a wait for read_bh/write_bh to go to NULL ++ //while (msc->read_bh.b_data || msc->write_bh.b_data) { ++ while (msc->read_pending || msc->write_pending) { ++ printk(KERN_INFO"%s: sleeping on read or write bh\n", __FUNCTION__); ++ sleep_on_timeout(&msc->msc_wq, 20); ++ } ++ __free_page((void *)&msc->read_bh.b_page); ++ msc->read_bh.b_page = NULL; ++ __free_page((void *)&msc->write_bh.b_page); ++ msc->write_bh.b_page = NULL; ++ up(&msc_sem); ++} ++ ++ ++/* READ 10 COMMAND - read and send data to the host ******************************************** */ ++ ++int msc_start_reading_block_data(struct usbd_function_instance *function, struct msc_private *msc); ++void msc_block_read_finished(struct buffer_head *bh, int flag); ++ ++ ++/*! msc_scsi_read_10 - process a read(10) command ++ * ++ * We have received a READ(10) CBW, if transfer length is non-zero ++ * initiate a generic block i/o otherwise send a CSW. ++ * ++ * Returns non-zero if there is an error in the USB layer. ++ */ ++int msc_scsi_read_10(struct msc_private *msc, char *name, int op) ++{ ++ SCSI_READ_10_COMMAND *command = (SCSI_READ_10_COMMAND *)&msc->command.CBWCB; ++ ++ /* ++ * save the CBW information and setup for transmitting data read from the block device ++ */ ++ msc->lba = be32_to_cpu(command->LogicalBlockAddress); ++ msc->TransferLength_in_blocks = be16_to_cpu(command->TransferLength); ++ msc->TransferLength_in_bytes = msc->TransferLength_in_blocks * msc->block_size; ++ msc->command_state = MSC_DATA_IN_READ; ++ msc->io_state = MSC_INACTIVE; ++ ++ /* ++ * Start reading blocks to send or simply send the CSW if the host ++ * didn't actually ask for a non-zero length. ++ */ ++ return (msc->TransferLength_in_blocks) ? msc_start_reading_block_data(msc->function, msc) : ++ msc_start_sending_csw(msc->function, msc, USB_MSC_PASSED); ++} ++ ++ ++/*! msc_start_reading_block_data - start reading data ++ * ++ * Generate a generic block io request to read some data to transmit. ++ * ++ * This function is initially called by msc_scsi_read_10() but can also be called ++ * by msc_urb_sent() if the amount of data requested was too large. ++ * ++ * The function msc_block_read_finished() will be called to actually send the data ++ * to the host when the I/O request is complete. ++ * ++ */ ++int msc_start_reading_block_data(struct usbd_function_instance *function, struct msc_private *msc) ++{ ++ int TransferLength_in_blocks = MIN(msc->max_blocks, msc->TransferLength_in_blocks); ++ unsigned long flags; ++ ++ TRACE_MSG3(MSC,"START READING BLOCK DATA lba: %x blocks: %d %d ", ++ msc->lba, msc->TransferLength_in_blocks, TransferLength_in_blocks); ++ ++ /* ensure that device state is ok ++ */ ++ if (msc->block_dev_state != DEVICE_INSERTED) { ++ TRACE_MSG0(MSC,"START READ MEDIA NOT PRESENT"); ++ return msc_start_sending_csw_failed (msc, SCSI_SENSEKEY_MEDIA_NOT_PRESENT, msc->lba, USB_MSC_FAILED); ++ } ++ ++ // XXX an ioctl has requested that pending io be aborted ++ local_irq_save(flags); ++ if (msc->io_state & MSC_ABORT_IO) { ++ msc->io_state &= ~MSC_ABORT_IO; ++ TRACE_MSG0(MSC,"BLOCK READ ABORTED"); ++ local_irq_restore(flags); ++ return msc_start_sending_csw_failed (msc, SCSI_SENSEKEY_UNRECOVERED_READ_ERROR, msc->lba, USB_MSC_FAILED); ++ } ++ local_irq_restore(flags); ++ ++ /* sanity check lba against capacity ++ */ ++ if (msc->lba >= msc->capacity) { ++ TRACE_MSG2(MSC, "START READ LBA out of range: lba: %d capacity: %d", msc->lba, msc->capacity); ++ return msc_start_sending_csw_failed (msc, SCSI_SENSEKEY_BLOCK_ADDRESS_OUT_OF_RANGE, msc->lba, USB_MSC_FAILED); ++ } ++ ++ /* setup buffer head - msc_block_read_finished() will be called when block i/o is finished ++ */ ++ msc->read_bh.b_end_io = msc_block_read_finished; ++ msc->read_bh.b_size = TransferLength_in_blocks * msc->block_size; ++ msc->read_bh.b_rsector = msc->lba; ++ msc->read_bh.b_state = (1UL << BH_Mapped) | (1UL << BH_Lock); ++ msc->io_state |= MSC_BLOCKIO_PENDING; ++ msc->read_pending=1; ++ ++ memset(msc->read_bh.b_data, 0x0, msc->read_bh.b_size); ++ ++ generic_make_request(READ, &msc->read_bh); ++ generic_unplug_device(blk_get_queue(msc->read_bh.b_rdev)); ++ return 0; ++} ++ ++ ++/*! msc_block_read_finished - called by generic request ++ * ++ * Called when block i/o read is complete, send the data to the host if possible. ++ * ++ * The function msc_urb_sent() will be called when the data is sent to ++ * either send additional data or the CSW as appropriate. ++ * ++ * If more data is required then call msc_start_reading_block_data() to ++ * issue another block i/o to get more data. ++ * ++ * These means that there can be two outstanding actions when this ++ * function completes: ++ * ++ * 1. a transmit urb may be pending, sending the most recently ++ * read data to the host ++ * 2. another block i/o may be pending to read additional data ++ * ++ * This leads to a race condition, if the block i/o finished before the urb ++ * transmit, then we must simply exit. The msc_in_read_10_urb_sent() ++ * function will ensure that this is restarted. ++ */ ++void msc_block_read_finished(struct buffer_head *bh, int uptodate) ++{ ++ //struct msc_private *msc = bh->b_private; ++ //int TransferLength_in_blocks = bh->b_size / msc->block_size; ++ ++ struct msc_private *msc; ++ int TransferLength_in_blocks; ++ ++ struct usbd_function_instance *function; ++ struct usbd_urb *tx_urb; ++ unsigned long flags; ++ int rc; ++#ifdef CONFIG_OTG_MSC_BLOCK_TRACE ++ u32 crc; ++#endif /* CONFIG_OTG_MSC_BLOCK_TRACE */ ++ int i; ++ ++ msc = bh->b_private; ++ TransferLength_in_blocks = bh->b_size / msc->block_size; ++ ++ TRACE_MSG1(MSC,"BLOCK READ FINISHED size: %x", bh->b_size); ++ ++ #if 0 ++ local_irq_save(flags); ++ if (msc->io_state & MSC_IOCTL_WAITING) { ++ wake_up_interruptible(&msc->ioctl_wq); ++ msc->io_state &= ~MSC_IOCTL_WAITING; ++ } ++ local_irq_restore(flags); ++ #endif ++ ++ wake_up_interruptible(&msc->ioctl_wq); ++ ++ /* ++ * Race condition here, if we have not finished sending the ++ * previous tx_urb then we want to simply exit and let ++ * msc_in_read_10_urb_sent() call us again. ++ * ++ * Ensure that we do not reset BLOCKIO flags if SEND PENDING and ++ * that we do reset BLOCKIO if not SEND PENDING ++ */ ++ { ++ unsigned long flags; ++ local_irq_save(flags); ++ if (msc->io_state & MSC_SEND_PENDING) { ++ TRACE_MSG0(MSC,"BLOCK READ SEND PENDING"); ++ msc->io_state |= MSC_BLOCKIO_FINISHED; ++ msc->uptodate = uptodate; ++ local_irq_restore(flags); ++ return; ++ } ++ msc->io_state &= ~(MSC_BLOCKIO_PENDING | MSC_BLOCKIO_FINISHED); ++ ++ local_irq_restore(flags); ++ } ++ ++ /* verify that the I/O completed ++ */ ++ if (1 != uptodate) { ++ TRACE_MSG0(MSC,"BLOCK READ FAILED"); ++ msc_start_sending_csw_failed (msc, SCSI_SENSEKEY_UNRECOVERED_READ_ERROR, msc->lba, USB_MSC_FAILED); ++ return; ++ } ++ ++#ifdef CONFIG_OTG_MSC_BLOCK_TRACE ++ /* debug output trace - dump rlba and computed crc ++ */ ++ for (i = 0; i < bh->b_size / msc->block_size; i++) { ++ crc = crc32_compute(bh->b_data + (i * msc->block_size), msc->block_size, CRC32_INIT); ++ TRACE_RLBA(bh->b_rsector + i, crc); ++ } ++#endif /* CONFIG_OTG_MSC_BLOCK_TRACE */ ++ /* allocate a tx_urb and copy into it ++ */ ++ THROW_IF(!(function = msc->function), error); ++ THROW_IF(!(tx_urb = usbd_alloc_urb (function, BULK_IN, bh->b_size, msc_urb_sent)), error); ++ TRACE_MSG1(MSC,"BLOCK READ FINISHED urb: %p", (int)tx_urb); ++ ++ tx_urb->function_privdata = msc; ++ tx_urb->actual_length = tx_urb->buffer_length = bh->b_size; ++ memcpy(tx_urb->buffer, bh->b_data, bh->b_size); ++ ++ msc->read_pending=0; ++ ++ { ++ unsigned long flags; ++ local_irq_save(flags); ++ ++ msc->io_state |= MSC_SEND_PENDING; ++ msc->TransferLength_in_bytes -= bh->b_size; ++ msc->TransferLength_in_blocks -= TransferLength_in_blocks; ++ msc->lba += TransferLength_in_blocks; ++ ++ if (!msc->TransferLength_in_blocks) { ++ TRACE_MSG0(MSC,"BLOCK READ FINISHED - IO FINISHED"); ++ // set flag so that CSW can be sent ++ msc->io_state |= MSC_DATA_IN_READ_FINISHED; ++ } ++ local_irq_restore(flags); ++ } ++ ++ /* dispatch urb - msc_urb_sent() will be called when urb is finished ++ */ ++ if ((rc = usbd_start_in_urb (tx_urb))) { ++ TRACE_MSG0(MSC,"BLOCK READ FINISHED FAILED"); ++ usbd_free_urb (tx_urb); ++ THROW(error); ++ } ++ ++ /* if more data is required then call msc_start_reading_block_data() to ++ * issue another block i/o to get more data. ++ */ ++ if (!(msc->io_state & MSC_DATA_IN_READ_FINISHED)) { ++ TRACE_MSG0(MSC,"BLOCK READ SEND RESTARTING"); ++ msc_start_reading_block_data(msc->function, msc); ++ } ++ ++ CATCH(error) { ++ TRACE_MSG0(MSC,"BLOCK READ FINISHED ERROR"); ++ // stall? ++ } ++} ++ ++/*! msc_in_read_10_urb_sent - called by msc_urb_sent when state is MSC_DATA_IN_READ ++ * ++ * This will process a read_10 urb that has been sent. Specifically if any previous ++ * read_10 block I/O has finished we recall the msc_block_read_finished() function ++ * to transmit another read_10 urb. ++ * ++ * If there is no other pending read_10 to do we create and send a CSW. ++ * ++ * If there is more I/O to do or there is already an outstanding I/O we simply ++ * return after freeing the URB. ++ * ++ * Return non-zero if urb was not disposed of. ++ */ ++int msc_in_read_10_urb_sent(struct usbd_urb *tx_urb, struct msc_private *msc) ++{ ++ unsigned long flags; ++ ++ TRACE_MSG0(MSC,"URB SENT DATA IN"); ++ ++ /* ++ * Potential race condition here, we may need to restart blockio. ++ */ ++ local_irq_save(flags); ++ ++ msc->io_state &= ~MSC_SEND_PENDING; ++ msc->data_transferred_in_bytes += tx_urb->actual_length; ++ ++ TRACE_MSG1(MSC,"URB SENT DATA IN data transferred: %d", msc->data_transferred_in_bytes); ++ ++ if (!tx_urb->actual_length) ++ msc->TransferLength_in_blocks = 0; ++ ++ /* XXX We should be checking urb status ++ */ ++ ++ usbd_free_urb (tx_urb); ++ ++ /* ++ * Check to see if we need to send CSW. ++ */ ++ if (MSC_DATA_IN_READ_FINISHED & msc->io_state) { ++ TRACE_MSG0(MSC,"URB SENT DATA IN - FINISHED SENDING CSW"); ++ local_irq_restore(flags); ++ return msc_start_sending_csw(msc->function, msc, USB_MSC_PASSED); ++ } ++ /* ++ * Check to see if there is a block read needs to be finished. ++ */ ++ if (MSC_BLOCKIO_FINISHED & msc->io_state) { ++ TRACE_MSG0(MSC,"URB SENT DATA IN - RESTART"); ++ local_irq_restore(flags); ++ // XXX uptodate? ++ msc_block_read_finished(&msc->read_bh, msc->uptodate); ++ return 0; ++ } ++ local_irq_restore(flags); ++ return 0; ++} ++ ++ ++/* WRITE 10 - receive data from host and write to block device ********************************* */ ++ ++int msc_start_receiving_data(struct usbd_function_instance *function, struct msc_private *msc); ++void msc_data_written(struct buffer_head *bh, int flag); ++void msc_data_test(struct buffer_head *bh, int flag); ++ ++/*! msc_scsi_write_10 - process a write command ++ * ++ * Call either msc_start_receiving_data() or msc_start_sending_csw() as appropriate. ++ * Normally msc_start_receiving_data() is called and it will use msc_start_recv_urb() ++ * to setup a receive urb of the appropriate size. ++ * ++ * Returns non-zero if there is an error in the USB layer. ++ */ ++int msc_scsi_write_10(struct msc_private *msc, char *name, int op) ++{ ++ SCSI_WRITE_10_COMMAND *command = (SCSI_WRITE_10_COMMAND *)&msc->command.CBWCB; ++ ++ /* save the CBW and setup for receiving data to be written to the block device ++ */ ++ msc->lba = be32_to_cpu(command->LogicalBlockAddress); ++ msc->TransferLength_in_blocks = be16_to_cpu(command->TransferLength); ++ msc->TransferLength_in_bytes = msc->TransferLength_in_blocks * msc->block_size; ++ ++ msc->command_state = MSC_DATA_OUT_WRITE; ++ msc->io_state = MSC_INACTIVE; ++ ++ TRACE_MSG1(MSC,"RECV WRITE lba: %x", msc->lba); ++ TRACE_MSG1(MSC,"RECV WRITE blocks: %d", msc->TransferLength_in_blocks); ++ ++ return (msc->TransferLength_in_blocks) ? ++ msc_start_receiving_data(msc->function, msc) : ++ msc_start_sending_csw(msc->function, msc, USB_MSC_PASSED); ++} ++ ++ ++/*! msc_start_receiving_data - called to initiate an urb to receive WRITE(10) data ++ * ++ * Initiate a receive urb to receive upto PAGE_SIZE WRITE(10) data. The ++ * msc_recv_urb() function will call msc_recv_out_blocks() to actually ++ * write the data. ++ * ++ * This is called from msc_scsi_write_10() to initiate the WRITE(10) and ++ * called from msc_data_written() to start the next page sized receive ++ * urb. ++ */ ++int msc_start_receiving_data(struct usbd_function_instance *function, struct msc_private *msc) ++{ ++ /* ++ * Calculating the length is most of the work we do :-) ++ */ ++ int TransferLength_in_blocks = MIN(msc->max_blocks, msc->TransferLength_in_blocks); ++ ++ TRACE_MSG1(MSC,"START RECEIVING DATA lba: %x", msc->lba); ++ TRACE_MSG1(MSC,"START RECEIVING DATA blocks: %d", msc->TransferLength_in_blocks); ++ TRACE_MSG1(MSC,"START RECEIVING DATA blocks: %d", TransferLength_in_blocks); ++ TRACE_MSG1(MSC,"START RECEIVING DATA bytes: %d", TransferLength_in_blocks * msc->block_size); ++ ++ THROW_IF(msc->command_state != MSC_DATA_OUT_WRITE, error); ++ THROW_IF(!msc->TransferLength_in_blocks, error); ++ ++ msc->io_state |= MSC_RECV_PENDING; ++ ++ return msc_start_recv_urb(function, msc, TransferLength_in_blocks * msc->block_size); ++ ++ CATCH(error) { ++ TRACE_MSG0(MSC,"START RECEIVING DATA ERROR"); ++ return -EINVAL; ++ } ++} ++ ++ ++/*! msc_recv_out_blocks - process received WRITE(10) data by writing to block device ++ * ++ * We get here indirectly from msc_recv_urb() when state is MSC_DATA_OUT_WRITE. ++ * ++ * Dealloc the urb and call Initiate the generic request to write the ++ * received data. The b_end_io function msc_data_written() will send the ++ * CSW. ++ * ++ */ ++void msc_recv_out_blocks(struct usbd_urb *rcv_urb, struct msc_private *msc) ++{ ++ int TransferLength_in_bytes = rcv_urb->actual_length; ++ int TransferLength_in_blocks = TransferLength_in_bytes / msc->block_size; ++#ifdef CONFIG_OTG_MSC_BLOCK_TRACE ++ u32 crc; ++#endif /* CONFIG_OTG_MSC_BLOCK_TRACE */ ++ int i; ++ ++ TRACE_MSG1(MSC,"RECV OUT bytes: %d", TransferLength_in_bytes); ++ TRACE_MSG1(MSC,"RECV OUT iostate: %x", msc->io_state); ++ TRACE_MSG1(MSC,"RECV OUT data transferred: %d", msc->data_transferred_in_bytes); ++ ++ /* ++ * Race condition here, we may get to here before the previous block ++ * write completed. If so just exit and the msc_data_written() ++ * function will recall us. ++ */ ++ { ++ unsigned long flags; ++ local_irq_save(flags); ++ if (msc->io_state & MSC_BLOCKIO_PENDING) { ++ TRACE_MSG0(MSC,"RECV OUT BLOCKIO PENDING"); ++ msc->io_state |= MSC_RECV_FINISHED; ++ msc->rcv_urb_finished = rcv_urb; ++ local_irq_restore(flags); ++ return; ++ } ++ msc->io_state &= ~(MSC_RECV_PENDING |MSC_RECV_FINISHED); ++ local_irq_restore(flags); ++ } ++ ++ msc->data_transferred_in_bytes += rcv_urb->actual_length; ++ ++#ifdef CONFIG_OTG_MSC_BLOCK_TRACE ++ for (i = 0; i < TransferLength_in_blocks; i++) { ++ crc = crc32_compute(rcv_urb->buffer + (i * msc->block_size), msc->block_size, CRC32_INIT); ++ TRACE_SLBA(msc->lba + i, crc); ++ } ++#endif /* CONFIG_OTG_MSC_BLOCK_TRACE */ ++ ++ msc->write_pending=1; ++ ++ memcpy(msc->write_bh.b_data, rcv_urb->buffer, rcv_urb->actual_length); ++ ++ usbd_free_urb(rcv_urb); ++ ++ /* ensure media state is ok ++ */ ++ if (msc->block_dev_state != DEVICE_INSERTED) { ++ TRACE_MSG0(MSC,"START READ MEDIA NOT PRESENT"); ++ msc_start_sending_csw_failed (msc, SCSI_SENSEKEY_MEDIA_NOT_PRESENT, msc->lba, USB_MSC_FAILED); ++ return; ++ } ++ ++ // XXX an ioctl has requested that pending io be aborted ++ if (msc->io_state & MSC_ABORT_IO) { ++ unsigned long flags; ++ local_irq_save(flags); ++ msc->io_state &= ~MSC_ABORT_IO; ++ TRACE_MSG0(MSC,"BLOCK READ ABORTED"); ++ local_irq_restore(flags); ++ msc_start_sending_csw_failed (msc, SCSI_SENSEKEY_UNRECOVERED_READ_ERROR, msc->lba, USB_MSC_FAILED); ++ return; ++ } ++ ++ /* sanity check lba against capacity ++ */ ++ if (msc->lba >= msc->capacity) { ++ TRACE_MSG2(MSC, "START READ LBA out of range: lba: %d capacity: %d", msc->lba, msc->capacity); ++ msc_start_sending_csw_failed (msc, SCSI_SENSEKEY_BLOCK_ADDRESS_OUT_OF_RANGE, msc->lba, USB_MSC_FAILED); ++ return; ++ } ++ ++ /* additional sanity check for lba - ensure non-zero ++ */ ++ if (!msc->TransferLength_in_blocks) { ++ TRACE_MSG0(MSC,"START READ LBA TransferLength_in_blocks zero:"); ++ msc_start_sending_csw_failed (msc, SCSI_SENSEKEY_BLOCK_ADDRESS_OUT_OF_RANGE, msc->lba, USB_MSC_FAILED); ++ return; ++ } ++ ++ /* XXX additional sanity check required here - verify that the transfer ++ * size agrees with what we where expecting, specifically I think ++ * we need to verify that we have received a multiple of the block ++ * size and either a full bulk transfer (so more will come) or ++ * the actual exact amount that the host said it would send. ++ * An error should generate a CSW with a USB_MSC_PHASE_ERROR ++ */ ++ ++ ++ TRACE_MSG1(MSC,"RECV OUT lba: %x", msc->lba); ++ TRACE_MSG1(MSC,"RECV OUT blocks left: %d", msc->TransferLength_in_blocks); ++ TRACE_MSG1(MSC,"RECV OUT blocks current: %d", TransferLength_in_blocks); ++ ++ /* Initiate writing the data - msc_data_written() will be called ++ * when finished. ++ */ ++ msc->write_bh.b_end_io = msc_data_written; ++ ++ /* set address in buffer head ++ */ ++ msc->write_bh.b_size = TransferLength_in_bytes; ++ msc->write_bh.b_rsector = msc->lba; ++ ++ /* decrement counters and increment address ++ */ ++ msc->TransferLength_in_bytes -= TransferLength_in_bytes; ++ msc->TransferLength_in_blocks -= TransferLength_in_blocks; ++ msc->lba += TransferLength_in_blocks; ++ msc->write_bh.b_state = (1UL << BH_Mapped) | (1UL << BH_Lock); ++ msc->io_state |= MSC_BLOCKIO_PENDING; ++ ++ ++ /* Check current status of TransferLength - if non-zero then we need ++ * to queue another urb to receive more data. ++ */ ++ if (!msc->TransferLength_in_blocks) ++ msc->command_state = MSC_DATA_OUT_WRITE_FINISHED; ++ else ++ msc_start_receiving_data(msc->function, msc); ++ ++ /* initiate the block i/o request ++ */ ++ generic_make_request(WRITE, &msc->write_bh); ++ generic_unplug_device(blk_get_queue(msc->write_bh.b_rdev)); ++} ++ ++ ++/*! msc_data_written - called by generic request ++ * ++ * Called when current block i/o read is finished, restart block i/o or send the CSW. ++ * ++ */ ++void msc_data_written(struct buffer_head *bh, int uptodate) ++{ ++ struct msc_private *msc = bh->b_private; ++ unsigned long flags; ++ u8 io_state; ++ ++ TRACE_MSG4(MSC,"DATA WRITTEN uptodate: %x b_state: %x state: %x io: %x", ++ uptodate, bh->b_state, msc->command_state, msc->io_state); ++ ++ TRACE_MSG1(MSC,"DATA WRITTEN lba: %x", msc->lba); ++ ++ local_irq_save(flags); ++ io_state = msc->io_state &= ~MSC_BLOCKIO_PENDING; ++ msc->write_pending=0; ++ ++ local_irq_restore(flags); ++ ++ // XXX an ioctl has waited for io to complete, need to wake it up ++ #if 0 ++ local_irq_save(flags); ++ if (msc->io_state & MSC_IOCTL_WAITING) { ++ wake_up_interruptible(&msc->ioctl_wq); ++ msc->io_state &= ~MSC_IOCTL_WAITING; ++ } ++ local_irq_restore(flags); ++ #endif ++ ++ wake_up_interruptible(&msc->ioctl_wq); ++ ++ /* ++ * verify that the I/O completed ++ */ ++ if (1 != uptodate) { ++ TRACE_MSG0(MSC,"BLOCK READ FAILED"); ++ msc_start_sending_csw_failed (msc, SCSI_SENSEKEY_UNRECOVERED_READ_ERROR, msc->lba, USB_MSC_FAILED); ++ // XXX CHECKME ++ if (msc->rcv_urb_finished) { ++ usbd_free_urb(msc->rcv_urb_finished); ++ msc->rcv_urb_finished = NULL; ++ } ++ return; ++ } ++ ++ /* ++ * If there was a rcv_urb that was not processed then we need ++ * to process it now. This is done by simply restarting msc_recv_out() ++ */ ++ if ((io_state & MSC_RECV_FINISHED) && msc->rcv_urb_finished) { ++ struct usbd_urb *rcv_urb = msc->rcv_urb_finished; ++ msc->rcv_urb_finished = NULL; ++ TRACE_MSG0(MSC,"DATA WRITTEN RECV FINISHED"); ++ msc_recv_out_blocks(rcv_urb, msc); ++ } ++ /* ++ * DATA_IN mode and no more data to write ++ */ ++ if (MSC_DATA_OUT_WRITE_FINISHED == msc->command_state) { ++ // finished, send CSW ++ TRACE_MSG0(MSC,"DATA WRITTEN send CSW"); ++ msc_start_sending_csw(msc->function, msc, USB_MSC_PASSED); ++ } ++} ++ ++extern struct usbd_function_operations msc_function_ops; ++extern struct usbd_function_driver msc_function_driver; ++ ++ ++/* USB Module init/exit ************************************************************************ */ ++ ++int msc_io_init_l24(void); ++void msc_io_exit_l24(void); ++ ++/*! msc_modinit - module init ++ * ++ */ ++static int msc_modinit (void) ++{ ++ int rc; ++ struct msc_private *msc = &msc_private; ++ ++ printk(KERN_INFO "Copyright (c) 2004 Belcarra Technologies; www.belcarra.com; sl@belcarra.com\n"); ++ printk (KERN_INFO "%s vendor_id: %04x product_id: %04x major: %d minor: %d\n", __FUNCTION__, ++ vendor_id, product_id, major, minor); ++ ++ ++ MSC = otg_trace_obtain_tag(); ++ ++ if (vendor_id) ++ msc_function_driver.idVendor = cpu_to_le16(vendor_id); ++ if (product_id) ++ msc_function_driver.idProduct = cpu_to_le16(product_id); ++ ++ init_waitqueue_head(&msc->msc_wq); ++ init_waitqueue_head(&msc->ioctl_wq); ++ ++ msc->block_dev_state = DEVICE_EJECTED; ++ ++ ++ msc->command_state = MSC_READY; ++ msc->io_state = MSC_INACTIVE; ++ ++#ifdef CONFIG_OTG_MSC_BLOCK_TRACE ++ make_crc_table(); ++#endif /* CONFIG_OTG_MSC_BLOCK_TRACE */ ++ ++ TRACE_MSG3(MSC,"PAGE_SHIFT: %x PAGE_SIZE: %x %d", PAGE_SHIFT, PAGE_SIZE, PAGE_SIZE); ++ ++ THROW_IF(msc_io_init_l24(), error); ++ ++ /* register as usb function driver ++ */ ++ THROW_UNLESS ((msc->function = usbd_register_function (&msc_function_driver, "msc", NULL)), error); ++ msc->usb_driver_registered++; ++ ++ CATCH(error) { ++ if (msc->usb_driver_registered) { ++ usbd_deregister_function (msc->function); ++ msc->usb_driver_registered = 0; ++ } ++ otg_trace_invalidate_tag(MSC); ++ return -EINVAL; ++ } ++ return 0; ++} ++ ++/*! msc_modexit - module cleanup ++ */ ++static void msc_modexit (void) ++{ ++ struct msc_private *msc = &msc_private; ++ ++ /* destroy control io interface ++ */ ++ msc_io_exit_l24(); ++ ++ /* flush io and free page buffers ++ */ ++ msc_close_blockdev(msc); ++ ++ if (msc->usb_driver_registered) ++ usbd_deregister_function (msc->function); ++ ++#ifdef CONFIG_OTG_MSC_BLOCK_TRACE ++ free_crc_table(); ++#endif /* CONFIG_OTG_MSC_BLOCK_TRACE */ ++ otg_trace_invalidate_tag(MSC); ++} ++ ++ ++module_init (msc_modinit); ++module_exit (msc_modexit); ++ +diff -uNr linux/drivers/no-otg/functions/msc/msc-original.c linux/drivers/otg/functions/msc/msc-original.c +--- linux/drivers/no-otg/functions/msc/msc-original.c 1970-01-01 01:00:00.000000000 +0100 ++++ linux/drivers/otg/functions/msc/msc-original.c 2006-09-01 21:41:28.000000000 +0200 +@@ -0,0 +1,2209 @@ ++/* ++ * otg/msc_fd/msc.c ++ * ++ * Copyright (c) 2003, 2004 Belcarra ++ * ++ * By: ++ * Stuart Lynne <sl@belcarra.com>, ++ * Tom Rushworth <tbr@belcarra.com>, ++ * Bruce Balden <balden@belcarra.com> ++ * ++ * ++ * This is a Mass Storage Class Function that uses the Bulk Only protocol. ++ * ++ * To use simply load with something like: ++ * ++ * insmod msc_fd.o vendor_id=0xffff product_id=0xffff ++ * ++ * Notes: ++ * ++ * 1. Currently block I/O is done a page at a time. I have not determined if ++ * it is possible to dispatch a multiple page generic request. It should at ++ * least be possible to queue page requests. ++ * ++ * 2. Currently for READ operations we have a maximum of one outstanding ++ * read block I/O and send urb. These are allowed to overlap so that we can ++ * continue to read data while sending data to the host. ++ * ++ * 3. Currently for WRITE operations we have a maximum of one outstanding ++ * recv urb and one outstanding write block I/O. These are allowed to ++ * overlap so that we can continue to receive data from the host while ++ * waiting for writing to complete. ++ * ++ * 4. It would be possible to allow multiple writes to be pending, to the ++ * limit of the page cache, if desired. ++ * ++ * 5. It should be possible to allow multiple outstanding reads to be ++ * pending, to the limit of the page cache, but this potentially could ++ * require dealing with out of order completions of the reads. Essentially a ++ * list of completed buffer heads would be required to hold any completed ++ * buffer heads that cannot be sent prior to another, earlier request being ++ * completed. ++ * ++ * 6. Currently we only support the Bulk Only model. Microsoft states that ++ * further support for the mass storage driver will only be done for devices ++ * that conform to the Bulk Only model. ++ * ++ * 7. Multiple LUN's are not supported, but in theory they could be. ++ * ++ * 8. It should be possible to use the removalble disk model to allow for a ++ * hotplug script to umount locally and signal that the block device is now ++ * available. An insertion event would notify the host that it can now use ++ * the device. Similarily applications could notify us to send a removal ++ * event to the host so that the device can be mounted locally. ++ * ++ * 9. Error handling should be done with STALL but using ZLP seems to also ++ * work. ZLP is usually easier to implement (except possibly on the SA1100.) ++ * We may need to make STALL an option if we find devices (perhaps SA1100) ++ * that cannot reliaby send a ZLP on BULK-IN endpoint. ++ * ++ * ++ * 10. WinXP will match the following: ++ * ++ * USB\Class_08&SubClass_02&Prot_50 ++ * USB\Class_08&SubClass_05&Prot_50 ++ * USB\Class_08&SubClass_06&Prot_50 ++ * ++ * SubClass 02 is MMC or SFF8020I ++ * SubClass 05 is SFF or SFF8070I ++ * SubClass 06 is SCSI ++ * ++ * From the Windows USB Storage FAQ: ++ * ++ * RBC not supported ++ * ++ * SubClass = 6 (SCSI) ++ * CDBs SHOULD NOT be padded to 12 bytes ++ * ModeSense/ModeSelect SHOULD NOT be translated from 1ah/15h to 5ah/55h ++ * should be used for FLASH ++ * ++ * SubClass !=6 ++ * CDBs SHOULD be padded to 12 bytes ++ * ModeSense/ModeSelect SHOULD be translated from 1ah/15h to 5ah/55h ++ * ++ * We are using the former, SubClass = 6, and implement the required SCSI operations. ++ * ++ * ++ * TODO ++ * ++ * 1. error handling for block io, e.g. what if using with removable block ++ * device (like CF card) and it is removed. ++ * ++ * 2. Currently we memcpy() data from between the urb buffer and buffer ++ * head page. It should be possible to simply use the page buffer for the ++ * urb. ++ * ++ * 3. Should we offer using fileio as an option? This would allow direct access ++ * to a block device image stored in a normal file or direct access to (for example) ++ * ram disks. It would require implementing a separate file I/O kernel thread to ++ * do the actual I/O. ++ * ++ * ++ * TODO FIXME Bus Interface Notes ++ * ++ * 1. The bus interface driver must correctly implement NAK'ing if not rcv_urb is ++ * present (see au1x00.c or wmmx.c for examples.) ++ * ++ * 2. The bus interface driver must implement USBD_URB_SENDZLP flag (see au1x00.c ++ * for example.) ++ * ++ */ ++ ++#if 1 ++#include <otg/otg-compat.h> ++#include <otg/usbp-chap9.h> ++#include <otg/usbp-func.h> ++#include <otg/otg-trace.h> ++#include <otg/otg-mesg.h> ++ ++#include <linux/config.h> ++#include <linux/module.h> ++#include <linux/kernel.h> ++#include <linux/version.h> ++ ++#include <linux/init.h> ++#include <asm/uaccess.h> ++#include <linux/ctype.h> ++#include <linux/timer.h> ++#include <linux/interrupt.h> ++#include <asm/atomic.h> ++#include <linux/random.h> ++#include <linux/slab.h> ++ ++#include <linux/blkdev.h> ++ ++ ++#include "msc-scsi.h" ++#include "msc.h" ++#include "crc.h" ++ ++static __inline__ void TRACE_SENSE(unsigned int sense, unsigned int info) ++{ ++ TRACE_MSG2(MSC, "--> SENSE: %06x INFO: %08x", sense, info); ++} ++static __inline__ void TRACE_RLBA(unsigned int lba, unsigned int crc) ++{ ++ TRACE_MSG2(MSC, "<-- rlba [%8x %08x]", lba, crc); ++} ++static __inline__ void TRACE_SLBA(unsigned int lba, unsigned int crc) ++{ ++ TRACE_MSG2(MSC, "--> slba [%8x %08x]", lba, crc); ++} ++static __inline__ void TRACE_TLBA(unsigned int lba, unsigned int crc) ++{ ++ TRACE_MSG2(MSC, "--> tlba [%8x %08x]", lba, crc); ++} ++static __inline__ void TRACE_TAG(unsigned int tag, unsigned int frame) ++{ ++ TRACE_MSG2(MSC, "--> TAG: %8x FRAME: %03x", tag, frame); ++} ++static __inline__ void TRACE_CBW(char *msg, int val) ++{ ++ TRACE_MSG2(MSC, " --> %s %02x", msg, val); ++} ++static __inline__ void TRACE_RECV(unsigned char *cp) ++{ ++ TRACE_MSG8(MSC, "<-- recv [%02x %02x %02x %02x %02x %02x %02x %02x]", ++ cp[0], cp[1], cp[2], cp[3], cp[4], cp[5], cp[6], cp[7]); ++} ++static __inline__ void TRACE_SENT(unsigned char *cp) ++{ ++ TRACE_MSG8(MSC, "--> sent [%02x %02x %02x %02x %02x %02x %02x %02x]", ++ cp[0], cp[1], cp[2], cp[3], cp[4], cp[5], cp[6], cp[7]); ++} ++ ++ ++#else ++ ++#include <linux/config.h> ++#include <linux/module.h> ++#include <linux/kernel.h> ++#include <linux/version.h> ++ ++MODULE_AUTHOR ("sl@belcarra.com, tbr@belcarra.com"); ++MODULE_LICENSE("PROPRIETARY"); ++MODULE_DESCRIPTION ("Belcarra Mass Storage Class Bulk Only Function"); ++ ++#include <linux/init.h> ++#include <asm/uaccess.h> ++#include <linux/ctype.h> ++#include <linux/timer.h> ++#include <linux/interrupt.h> ++#include <asm/atomic.h> ++#include <linux/random.h> ++#include <linux/slab.h> ++ ++#include <linux/blkdev.h> ++ ++#include "usbp-chap9.h" ++#include <usbp-mem.h> ++#include <otg-compat.h> ++#include <usbp-func.h> ++ ++USBD_MODULE_INFO ("msc_fd 2.0-beta"); ++ ++#include "msc-scsi.h" ++#include "msc.h" ++ ++#include "trace.h" ++#include "crc.h" ++#endif ++ ++//#include "rbc.h" ++ ++ ++/* Module Parameters ************************************************************************* */ ++ ++static u32 vendor_id; ++static u32 product_id; ++ ++static u32 major; ++static u32 minor; ++ ++MODULE_PARM (vendor_id, "i"); ++MODULE_PARM (product_id, "i"); ++MODULE_PARM (major, "i"); ++MODULE_PARM (minor, "i"); ++ ++MODULE_PARM_DESC (vendor_id, "Device Vendor ID"); ++MODULE_PARM_DESC (product_id, "Device Product ID"); ++MODULE_PARM_DESC (major, "Device Major"); ++MODULE_PARM_DESC (minor, "Device Minor"); ++ ++/* ++ * MSC Configuration ++ * ++ * Endpoint, Class, Interface, Configuration and Device descriptors/descriptions ++ */ ++ ++#define BULK_OUT 0x00 ++#define BULK_IN 0x01 ++#define ENDPOINTS 0x02 ++ ++/* ++ * Mass Storage Class - Bulk Only ++ * ++ * Endpoint, Class, Interface, Configuration and Device descriptors/descriptions ++ */ ++ ++static u8 msc_data_1[] = { 0x07, USB_DT_ENDPOINT, USB_DIR_OUT, BULK, 0, 0x00, 0x00, }; ++static u8 msc_data_2[] = { 0x07, USB_DT_ENDPOINT, USB_DIR_IN, BULK, 0, 0x00, 0x00, }; ++static struct usbd_endpoint_descriptor *msc_default[] = { ++ (struct usbd_endpoint_descriptor *) msc_data_1, ++ (struct usbd_endpoint_descriptor *) msc_data_2, }; ++u8 msc_indexes[] = { BULK_OUT, BULK_IN, }; ++ ++ ++/* Endpoint, Interface, Configuration and Device descriptions and descriptors ++ */ ++static u8 msc_data_alternate_descriptor[sizeof(struct usbd_interface_descriptor)] = { ++ 0x09, USB_DT_INTERFACE, ++ 0x00, 0x00, // bInterfaceNumber, bAlternateSetting ++ sizeof (msc_default) / sizeof(struct usbd_endpoint_descriptor), // bNumEndpoints ++ MASS_STORAGE_CLASS, ++ MASS_STORAGE_SUBCLASS_SCSI, ++ MASS_STORAGE_PROTO_BULK_ONLY, ++ 0x00, ++}; ++ ++static struct usbd_alternate_description msc_data_alternate_descriptions[] = { ++ { iInterface: CONFIG_OTG_MSC_INTF, ++ interface_descriptor: (struct usbd_interface_descriptor *)&msc_data_alternate_descriptor, ++ endpoints:sizeof (msc_default) / sizeof(struct usbd_endpoint_descriptor *), ++ endpoint_list: msc_default, ++ endpoint_indexes: msc_indexes, ++ }, ++}; ++ ++ ++struct usbd_interface_description msc_interfaces[] = { ++ { alternates:sizeof (msc_data_alternate_descriptions) / sizeof (struct usbd_alternate_description), ++ alternate_list:msc_data_alternate_descriptions,}, ++}; ++ ++u8 msc_configuration_descriptor[sizeof(struct usbd_configuration_descriptor)] = { ++ 0x09, USB_DT_CONFIGURATION, 0x00, 0x00, // wLength ++ sizeof (msc_interfaces) / sizeof (struct usbd_interface_description), ++ 0x01, 0x00, // bConfigurationValue, iConfiguration ++ 0, 0, ++}; ++ ++struct usbd_configuration_description msc_description[] = { ++ { iConfiguration: CONFIG_OTG_MSC_DESC, ++ configuration_descriptor: (struct usbd_configuration_descriptor *)msc_configuration_descriptor, ++ }, ++}; ++ ++ ++static struct usbd_device_descriptor msc_device_descriptor = { ++ bLength: sizeof(struct usbd_device_descriptor), ++ bDescriptorType: USB_DT_DEVICE, ++ bcdUSB: __constant_cpu_to_le16(USB_BCD_VERSION), ++ bDeviceClass: 0x00, ++ bDeviceSubClass: 0x02, ++ bDeviceProtocol: 0x00, ++ bMaxPacketSize0: 0x00, ++ idVendor: __constant_cpu_to_le16(CONFIG_OTG_MSC_VENDORID), ++ idProduct: __constant_cpu_to_le16(CONFIG_OTG_MSC_PRODUCTID), ++ bcdDevice: __constant_cpu_to_le16(CONFIG_OTG_MSC_BCDDEVICE), ++}; ++ ++#ifdef CONFIG_OTG_HIGH_SPEED ++static struct usbd_device_qualifier_descriptor msc_device_qualifier_descriptor = { ++ bLength: sizeof(struct usbd_device_qualifier_descriptor), ++ bDescriptorType: USB_DT_DEVICE_QUALIFIER, ++ bcdUSB: __constant_cpu_to_le16(USB_BCD_VERSION), ++ bDeviceClass: 0x00, ++ bDeviceSubClass: 0x02, ++ bDeviceProtocol: 0x00, ++ bMaxPacketSize0: 0x00, ++}; ++#endif /* CONFIG_OTG_HIGH_SPEED */ ++ ++ ++ ++ ++static struct usbd_endpoint_request msc_endpoint_requests[ENDPOINTS+1] = { ++ { 1, 0, 0, USB_DIR_OUT | USB_ENDPOINT_BULK, PAGE_SIZE, PAGE_SIZE, }, ++ { 1, 0, 0, USB_DIR_IN | USB_ENDPOINT_BULK, PAGE_SIZE, PAGE_SIZE, }, ++ { 0, }, ++}; ++ ++struct usbd_otg_descriptor msc_otg_descriptor = { ++ bLength : sizeof(struct usbd_otg_descriptor), ++ bDescriptorType: USB_DT_OTG, ++ bmAttributes: 0, ++}; ++ ++struct usbd_device_description msc_device_description = { ++ device_descriptor: &msc_device_descriptor, ++#ifdef CONFIG_OTG_HIGH_SPEED ++ device_qualifier_descriptor: &msc_device_qualifier_descriptor, ++#endif /* CONFIG_OTG_HIGH_SPEED */ ++ otg_descriptor: &msc_otg_descriptor, ++ iManufacturer: CONFIG_OTG_MSC_MANUFACTURER, ++ iProduct: CONFIG_OTG_MSC_PRODUCT_NAME, ++#if !defined(CONFIG_OTG_NO_SERIAL_NUMBER) && defined(CONFIG_OTG_SERIAL_NUMBER_STR) ++ iSerialNumber: CONFIG_OTG_SERIAL_NUMBER_STR, ++#endif ++}; ++ ++ ++#define DEVICE_EJECTED 0x0001 // MEDIA_EJECTED ++#define DEVICE_INSERTED 0x0002 // MEDIA_INSERT ++ ++#define DEVICE_OPEN 0x0004 // WR_PROTECT_OFF ++#define DEVICE_WRITE_PROTECTED 0x0008 // WR_PROTECT_ON ++ ++#define DEVICE_CHANGE_ON 0x0010 // MEDIA_CHANGE_ON ++ ++#define DEVICE_PREVENT_REMOVAL 0x0020 ++ ++ ++#define DEF_NUMBER_OF_HEADS 0x10 ++#define DEF_SECTORS_PER_TRACK 0x20 ++ ++ ++ ++/* MSC ******************************************************************************************** */ ++ ++static struct msc_private msc_private; ++int msc_interrupts; ++ ++int msc_urb_sent (struct usbd_urb *tx_urb, int rc); ++static int msc_recv_urb (struct usbd_urb *urb, int rc); ++ ++ ++/* Sense Key *********************************************************************************** */ ++ ++void set_sense_data(struct msc_private *msc, u32 sensedata, u32 info) ++{ ++ TRACE_SENSE(sensedata, info); ++ msc->sensedata = sensedata; ++ msc->info = info; ++} ++ ++ ++static int msc_reset(void) ++{ ++ struct msc_private *msc = &msc_private; ++ msc->device_state = MSC_DEVICE_DN; ++ msc->command_state = MSC_READY; ++ msc->io_state = MSC_INACTIVE; ++ msc->block_dev_state = 0; ++ msc->sensedata = 0; ++ msc->info = 0; ++ return 0; ++} ++ ++ ++/* Generic start recv urb and send csw ********************************************************* */ ++ ++/* msc_start_recv - queue a receive urb ++ * ++ * Returns non-zero if there is an error in the USB layer. ++ */ ++int msc_start_recv_urb(struct usbd_function_instance *function, struct msc_private *msc, int size) ++{ ++ struct usbd_urb *rcv_urb = NULL; ++ int wMaxPacketSize; ++ ++ TRACE_MSG1(MSC, "START RECV URB size: %d", size); ++ ++ // ensure that we are a multiple of the endpoint packetsize ++ wMaxPacketSize = usbd_endpoint_wMaxPacketSize(function, BULK_OUT, usbd_high_speed(function)); ++ if ((size % wMaxPacketSize)) { ++ size += wMaxPacketSize; ++ size = (size / wMaxPacketSize) * wMaxPacketSize; ++ } ++ ++ // allocate urb and queue it ++ THROW_IF(!(rcv_urb = usbd_alloc_urb (function, BULK_OUT, size, msc_recv_urb)), error); ++ TRACE_MSG1(MSC, "START RECV urb: %p", (int)rcv_urb); ++ ++ rcv_urb->function_privdata = msc; ++ msc->rcv_urb_finished = NULL; ++ THROW_IF(usbd_start_out_urb(rcv_urb), error); ++ return 0; ++ ++ CATCH(error) { ++ TRACE_MSG0(MSC,"START RECV URB ERROR"); ++ if (rcv_urb) ++ usbd_free_urb(rcv_urb); ++ return -EINVAL; ++ } ++} ++ ++/* msc_start_sending - start sending a new data or csw urb ++ * ++ * Generate a CSW (Command Status Wrapper) to send to the the host. ++ * ++ * Returns non-zero if there is an error in the USB layer. ++ */ ++int msc_start_sending_csw(struct usbd_function_instance *function, struct msc_private *msc, u8 status) ++{ ++ COMMAND_STATUS_WRAPPER *csw; ++ int length = sizeof(COMMAND_STATUS_WRAPPER); ++ struct usbd_urb *tx_urb; ++ ++ TRACE_MSG1(MSC,"START SENDING CSW %08x", status); ++ ++ msc->command_state = MSC_STATUS; ++ ++ THROW_IF(!(tx_urb = usbd_alloc_urb (function, BULK_IN, length, msc_urb_sent)), error); ++ TRACE_MSG1(MSC,"START sending csw urb: %p", (int)tx_urb); ++ tx_urb->actual_length = length; ++ tx_urb->function_privdata = msc; ++ ++ // fill in CSW and queue the urb ++ csw = (COMMAND_STATUS_WRAPPER *) tx_urb->buffer; ++ csw->dCSWSignature = CSW_SIGNATURE; ++ csw->dCSWTag = msc->command.dCBWTag; ++ csw->dCSWDataResidue = msc->command.dCBWDataTransferLength - msc->data_transferred_in_bytes; ++ csw->bCSWStatus = status; ++ ++ TRACE_MSG1(MSC,"START SENDING CSW data residue: %d", csw->dCSWDataResidue); ++ ++ THROW_IF (usbd_start_in_urb (tx_urb), error); ++ ++ CATCH(error) { ++ TRACE_MSG0(MSC,"START SENDING CSW FAILED"); ++ if (tx_urb) usbd_free_urb (tx_urb); ++ return -EINVAL; ++ } ++ return 0; ++} ++ ++/* msc_start_sending_csw_failed - starting sending a CSW showing failure ++ * ++ * Sets sensedata and generates a CSW with status set to USB_MSC_FAILED. ++ * ++ * Returns non-zero if there is an error in the USB layer. ++ */ ++int msc_start_sending_csw_failed(struct msc_private *msc, u32 sensedata, u32 info, int status) ++{ ++ set_sense_data(msc, sensedata, info); ++ return msc_start_sending_csw(msc->function, msc, status); ++} ++ ++ ++/* ********************************************************************************************* */ ++ ++/* msc_alloc_urb - allocate an urb for returning a query ++ * ++ * Returns NULL if there is an error in the USB layer. ++ */ ++struct usbd_urb * msc_alloc_urb(struct msc_private *msc, int length) ++{ ++ struct usbd_function_instance *function; ++ struct usbd_urb *urb; ++ ++ THROW_IF(!(function = msc->function), error); ++ THROW_IF(!(urb = usbd_alloc_urb (function, BULK_IN, length, msc_urb_sent)), error); ++ return urb; ++ ++ CATCH(error) { ++ msc->command_state = MSC_READY; ++ return NULL; ++ } ++} ++ ++ ++/* msc_dispatch_query_urb - dispatch an urb containing query data ++ * ++ * Returns non-zero if there is an error in the USB layer. ++ */ ++int msc_dispatch_query_urb(struct usbd_urb *urb, struct msc_private *msc, int length, int status) ++{ ++ int rc; ++ ++ TRACE_MSG1(MSC,"DISPATCH URB len: %d", length); ++ ++ // save information in msc and urb ++ // ++ urb->function_privdata = msc; ++ urb->actual_length = msc->TransferLength_in_bytes = length; ++ ++ // dispatch urb ++ { ++ unsigned long flags; ++ local_irq_save(flags); ++ if ((rc = usbd_start_in_urb (urb))) { ++ ++ TRACE_MSG0(MSC,"DISPATCH URB FAILED"); ++ usbd_free_urb (urb); ++ ++ // stall? ++ msc->command_state = MSC_READY; ++ local_irq_restore(flags); ++ return -EINVAL; ++ } ++ msc->command_state = MSC_QUERY; ++ msc->status = status; ++ local_irq_restore(flags); ++ } ++ return 0; ++} ++ ++ ++/* msc_dispatch_query_urb_zlp - send a ZLP ++ * ++ * Returns non-zero if there is an error in the USB layer. ++ */ ++int msc_dispatch_query_urb_zlp(struct msc_private *msc, u32 sensedata, u32 info, int status) ++{ ++ struct usbd_urb *urb; ++ RETURN_EINVAL_IF(!(urb = msc_alloc_urb(msc, 1))); ++ set_sense_data(msc, sensedata, info); ++ urb->flags |= USBD_URB_SENDZLP; ++ return msc_dispatch_query_urb(urb, msc, 0, status); ++} ++ ++ ++ ++/* Block Device ******************************************************************************** */ ++ ++/* msc_open_blockdev - open the block device specified in msc->major, msc->minor ++ * ++ * Sets appropriate fields to show current status of block device. ++ * ++ * XXX TODO - this needs to be tested against RO and absent devices. ++ * ++ */ ++void msc_open_blockdev(struct msc_private *msc) ++{ ++ int rc; ++ msc->block_dev_state = DEVICE_EJECTED; ++ ++ TRACE_MSG2(MSC, "OPEN BLOCKDEV: Major: %x Minor: %x", major, minor); ++ //printk(KERN_INFO"%s: major: %d minor: %d\n", __FUNCTION__, major, minor); ++ ++ /* ++ * Check device information and verify access to the block device. ++ */ ++ THROW_IF (!major, ejected); ++ ++ msc->dev = MKDEV(major, minor); ++ ++ THROW_IF (!(msc->bdev = bdget(kdev_t_to_nr(msc->dev))), ejected); ++ ++ if ((rc = blkdev_get(msc->bdev, FMODE_READ | FMODE_WRITE, 0, BDEV_RAW))) { ++ ++ TRACE_MSG0(MSC,"OPEN BLOCKDEV: cannot open RW"); ++ //printk(KERN_INFO"%s: cannot open RW\n", __FUNCTION__); ++ THROW_IF ((rc = blkdev_get(msc->bdev, FMODE_READ, 0, BDEV_RAW)), ejected); ++ msc->block_dev_state |= DEVICE_WRITE_PROTECTED; ++ } ++ ++ msc->block_dev_state &= ~DEVICE_EJECTED; ++ msc->block_dev_state |= DEVICE_INSERTED; ++ ++ TRACE_MSG1(MSC,"OPEN BLOCKDEV: opened block_dev_state: %x", msc->block_dev_state); ++ ++ /* ++ * Note that capacity must return the LBA of the last addressable block ++ * c.f. RBC 4.4, RBC 5.3 and notes below RBC Table 6 ++ * ++ * The blk_size array contains the number of addressable blocks or N, ++ * capacity is therefore N-1. ++ */ ++ ++ msc->capacity = (blk_size[MAJOR(msc_private.dev)][MINOR(msc_private.dev)] << 1) - 1; ++ msc->block_size = get_hardsect_size(msc->dev); ++ msc->max_blocks = PAGE_SIZE / msc->block_size; ++ ++ TRACE_MSG1(MSC,"blk_size: %x", blk_size[MAJOR(msc_private.dev)][MINOR(msc_private.dev)] << 1); ++ TRACE_MSG1(MSC,"blk_size: %d", blk_size[MAJOR(msc_private.dev)][MINOR(msc_private.dev)] << 1); ++ ++ TRACE_MSG1(MSC,"capacity: %x", msc->capacity); ++ TRACE_MSG1(MSC,"capacity: %d", msc->capacity); ++ ++ TRACE_MSG1(MSC,"block_size: %x", msc->block_size); ++ TRACE_MSG1(MSC,"block_size: %d", msc->block_size); ++ ++ TRACE_MSG1(MSC,"max_blocks: %d", msc->max_blocks); ++ ++ printk(KERN_INFO"%s: finis\n", __FUNCTION__); ++ ++ CATCH(ejected) { ++ TRACE_MSG1(MSC,"OPEN BLOCKDEV: EJECTED block_dev_state: %x", msc->block_dev_state); ++ printk(KERN_INFO"%s: cannot open R)\n", __FUNCTION__); ++ printk(KERN_INFO"%s: Cannot get device %d %d\n", __FUNCTION__, major, minor); ++ } ++} ++ ++/* msc_check_blockdev - check current status of the block device ++ * ++ * Check if the block device is operational, generate a failed CSW if not. ++ * ++ * Returns non-zero if the block device is not available for I/O operations ++ * and a failed CSW has already been sent. ++ */ ++int msc_check_blockdev(struct msc_private *msc, int zlp) ++{ ++ if (msc->block_dev_state & DEVICE_EJECTED) { ++ TRACE_MSG0(MSC,"CHECK BLOCKDEV DEVICE_EJECTED"); ++ ++ (zlp ? msc_dispatch_query_urb_zlp : msc_start_sending_csw_failed) ++ (msc, SCSI_SENSEKEY_MEDIUM_NOT_PRESENT, msc->lba, USB_MSC_FAILED); ++ ++ return -EINVAL; ++ } ++ if (msc->block_dev_state & DEVICE_CHANGE_ON) { ++ TRACE_MSG0(MSC,"CHECK BLOCKDEV DEVICE_CHANGE_ON"); ++ (zlp ? msc_dispatch_query_urb_zlp : msc_start_sending_csw_failed) ++ (msc, SCSI_SENSEKEY_NOT_READY_TO_CHANGE, msc->lba, USB_MSC_FAILED); ++ return -EINVAL; ++ } ++ return 0; ++} ++ ++ ++/* READ 10 COMMAND - read and send data to the host ******************************************** */ ++ ++int msc_start_reading_block_data(struct usbd_function_instance *function, struct msc_private *msc); ++void msc_block_read_finished(struct buffer_head *bh, int flag); ++ ++ ++/* msc_scsi_read_10 - process a read(10) command ++ * ++ * We have received a READ(10) CBW, if transfer length is non-zero ++ * initiate a generic block i/o otherwise send a CSW. ++ * ++ * Returns non-zero if there is an error in the USB layer. ++ */ ++int msc_scsi_read_10(struct msc_private *msc, char *name, int op) ++{ ++ SCSI_READ_10_COMMAND *command = (SCSI_READ_10_COMMAND *)&msc->command.CBWCB; ++ ++ RETURN_ZERO_IF (msc_check_blockdev(msc, 1)); ++ ++ /* ++ * save the CBW information and setup for transmitting data read from the block device ++ */ ++ msc->lba = be32_to_cpu(command->LogicalBlockAddress); ++ msc->TransferLength_in_blocks = be16_to_cpu(command->TransferLength); ++ msc->TransferLength_in_bytes = msc->TransferLength_in_blocks * msc->block_size; ++ msc->command_state = MSC_DATA_IN_READ; ++ msc->io_state = MSC_INACTIVE; ++ ++ /* ++ * Start reading blocks to send or simply send the CSW if the host ++ * didn't actually ask for a non-zero length. ++ */ ++ return (msc->TransferLength_in_blocks) ? msc_start_reading_block_data(msc->function, msc) : ++ msc_start_sending_csw(msc->function, msc, USB_MSC_PASSED); ++} ++ ++ ++/* msc_start_reading_block_data - start reading data ++ * ++ * Generate a generic block io request to read some data to transmit. ++ * ++ * This function is initially called by msc_scsi_read_10() but can also be called ++ * by msc_urb_sent() if the amount of data requested was too large. ++ * ++ * The function msc_block_read_finished() will be called to actually send the data ++ * to the host when the I/O request is complete. ++ * ++ */ ++int msc_start_reading_block_data(struct usbd_function_instance *function, struct msc_private *msc) ++{ ++ int TransferLength_in_blocks = MIN(msc->max_blocks, msc->TransferLength_in_blocks); ++ ++ TRACE_MSG1(MSC,"START READING BLOCK DATA lba: %x", msc->lba); ++ TRACE_MSG1(MSC,"START READING BLOCK DATA blocks: %d", msc->TransferLength_in_blocks); ++ TRACE_MSG1(MSC,"START READING BLOCK DATA blocks: %d", TransferLength_in_blocks); ++ ++ if (msc->block_dev_state != DEVICE_INSERTED) { ++ TRACE_MSG0(MSC,"START READ MEDIUM NOT PRESENT"); ++ return msc_start_sending_csw_failed (msc, SCSI_SENSEKEY_MEDIUM_NOT_PRESENT, msc->lba, USB_MSC_FAILED); ++ } ++ ++ if (msc->lba >= msc->capacity) { ++ TRACE_MSG2(MSC, "START READ LBA out of range: lba: %d capacity: %d", msc->lba, msc->capacity); ++ return msc_start_sending_csw_failed (msc, SCSI_SENSEKEY_LOGICAL_BLOCK_OUT_OF_RANGE, msc->lba, USB_MSC_FAILED); ++ } ++ ++ // setup buffer head ++ msc->read_bh.b_size = TransferLength_in_blocks * msc->block_size; ++ msc->read_bh.b_end_io = msc_block_read_finished; ++ msc->read_bh.b_rsector = msc->lba; ++ msc->read_bh.b_state = (1UL << BH_Mapped) | (1UL << BH_Lock); ++ msc->io_state |= MSC_BLOCKIO_PENDING; ++ msc->read_bh.b_data = page_address(msc->read_bh.b_page); ++ memset(msc->read_bh.b_data, 0x0, msc->read_bh.b_size); ++ ++ generic_make_request(READ, &msc->read_bh); ++ generic_unplug_device(blk_get_queue(msc->read_bh.b_rdev)); ++ ++ return 0; ++} ++ ++ ++/* msc_block_read_finished - called by generic request ++ * ++ * Called when block i/o read is complete, send the data to the host if possible. ++ * ++ * The function msc_urb_sent() will be called when the data is sent to ++ * either send additional data or the CSW as appropriate. ++ * ++ */ ++void msc_block_read_finished(struct buffer_head *bh, int uptodate) ++{ ++ struct msc_private *msc = bh->b_private; ++ int TransferLength_in_blocks = bh->b_size / msc->block_size; ++ struct usbd_function_instance *function; ++ struct usbd_urb *tx_urb; ++ int rc; ++ u32 crc; ++ int i; ++ ++ msc_interrupts++; ++ TRACE_MSG1(MSC,"BLOCK READ FINISHED size: %x", bh->b_size); ++ ++ /* ++ * Race condition here, if we have not finished sending the ++ * previous tx_urb then we want to simply exit and let ++ * msc_in_read_10_urb_sent() call us again. ++ * ++ * Ensure that we do not reset BLOCKIO flags if SEND PENDING and ++ * that we do reset BLOCKIO if not SEND PENDING ++ */ ++ { ++ unsigned long flags; ++ local_irq_save(flags); ++ if (msc->io_state & MSC_SEND_PENDING) { ++ TRACE_MSG0(MSC,"BLOCK READ SEND PENDING"); ++ msc->io_state |= MSC_BLOCKIO_FINISHED; ++ msc->uptodate = uptodate; ++ local_irq_restore(flags); ++ return; ++ } ++ msc->io_state &= ~(MSC_BLOCKIO_PENDING | MSC_BLOCKIO_FINISHED); ++ local_irq_restore(flags); ++ } ++ ++ // verify that the I/O completed ++ // ++ if (1 != uptodate) { ++ TRACE_MSG0(MSC,"BLOCK READ FAILED"); ++ msc_start_sending_csw_failed (msc, SCSI_SENSEKEY_UNRECOVERED_READ_ERROR, msc->lba, USB_MSC_FAILED); ++ return; ++ } ++ ++ // debug output trace ++ // ++ for (i = 0; i < bh->b_size / msc->block_size; i++) { ++ crc = crc32_compute(bh->b_data + (i * msc->block_size), msc->block_size, CRC32_INIT); ++ TRACE_RLBA(bh->b_rsector + i, crc); ++ } ++ ++ // allocate a tx_urb and copy into it ++ // ++ THROW_IF(!(function = msc->function), error); ++ THROW_IF(!(tx_urb = usbd_alloc_urb (function, BULK_IN, bh->b_size, msc_urb_sent)), error); ++ TRACE_MSG1(MSC,"BLOCK READ FINISHED urb: %p", (int)tx_urb); ++ ++ tx_urb->function_privdata = msc; ++ tx_urb->actual_length = tx_urb->buffer_length = bh->b_size; ++ memcpy(tx_urb->buffer, bh->b_data, bh->b_size); ++ ++ msc->read_bh.b_data = NULL; ++ { ++ unsigned long flags; ++ local_irq_save(flags); ++ ++ msc->io_state |= MSC_SEND_PENDING; ++ msc->TransferLength_in_bytes -= bh->b_size; ++ msc->TransferLength_in_blocks -= TransferLength_in_blocks; ++ msc->lba += TransferLength_in_blocks; ++ ++ if (!msc->TransferLength_in_blocks) { ++ TRACE_MSG0(MSC,"BLOCK READ FINISHED - IO FINISHED"); ++ // set flag so that CSW can be sent ++ msc->io_state |= MSC_DATA_IN_READ_FINISHED; ++ } ++ local_irq_restore(flags); ++ } ++ ++ // dispatch urb ++ if ((rc = usbd_start_in_urb (tx_urb))) { ++ TRACE_MSG0(MSC,"BLOCK READ FINISHED FAILED"); ++ usbd_free_urb (tx_urb); ++ THROW(error); ++ } ++ if (!(msc->io_state & MSC_DATA_IN_READ_FINISHED)) { ++ TRACE_MSG0(MSC,"BLOCK READ SEND RESTARTING"); ++ msc_start_reading_block_data(msc->function, msc); ++ return; ++ } ++ ++ CATCH(error) { ++ TRACE_MSG0(MSC,"BLOCK READ FINISHED ERROR"); ++ // stall? ++ } ++} ++ ++/* msc_in_read_10_urb_sent - called by msc_urb_sent when state is MSC_DATA_IN_READ ++ * ++ * This will process a read_10 urb that has been sent. Specifically if any previous ++ * read_10 block I/O has finished we recall the msc_block_read_finished() function ++ * to transmit another read_10 urb. ++ * ++ * If there is no other pending read_10 to do we create and send a CSW. ++ * ++ * If there is more I/O to do or there is already an outstanding I/O we simply ++ * return after freeing the URB. ++ * ++ * Return non-zero if urb was not disposed of. ++ */ ++int msc_in_read_10_urb_sent(struct usbd_urb *tx_urb, struct msc_private *msc) ++{ ++ unsigned long flags; ++ ++ TRACE_MSG0(MSC,"URB SENT DATA IN"); ++ ++ /* ++ * Potential race condition here, we may need to restart blockio. ++ */ ++ ++ local_irq_save(flags); ++ ++ msc->io_state &= ~MSC_SEND_PENDING; ++ msc->data_transferred_in_bytes += tx_urb->actual_length; ++ ++ TRACE_MSG1(MSC,"URB SENT DATA IN data transferred: %d", msc->data_transferred_in_bytes); ++ ++ if (!tx_urb->actual_length) ++ msc->TransferLength_in_blocks = 0; ++ ++ usbd_free_urb (tx_urb); ++ ++ /* ++ * Check to see if we need to send CSW. ++ */ ++ if (MSC_DATA_IN_READ_FINISHED & msc->io_state) { ++ TRACE_MSG0(MSC,"URB SENT DATA IN - FINISHED SENDING CSW"); ++ local_irq_restore(flags); ++ return msc_start_sending_csw(msc->function, msc, USB_MSC_PASSED); ++ } ++ /* ++ * Check to see if there is a block read needs to be finished. ++ */ ++ if (MSC_BLOCKIO_FINISHED & msc->io_state) { ++ TRACE_MSG0(MSC,"URB SENT DATA IN - RESTART"); ++ local_irq_restore(flags); ++ // XXX uptodate? ++ msc_block_read_finished(&msc->read_bh, msc->uptodate); ++ return 0; ++ } ++ local_irq_restore(flags); ++ return 0; ++} ++ ++ ++/* WRITE 10 - receive data from host and write to block device ********************************* */ ++ ++int msc_start_receiving_data(struct usbd_function_instance *function, struct msc_private *msc); ++void msc_data_written(struct buffer_head *bh, int flag); ++void msc_data_test(struct buffer_head *bh, int flag); ++ ++/* msc_scsi_write_10 - process a write command ++ * ++ * Call either msc_start_receiving_data() or msc_start_sending_csw() as appropriate. ++ * Normally msc_start_receiving_data() is called and it will use msc_start_recv_urb() ++ * to setup a receive urb of the appropriate size. ++ * ++ * Returns non-zero if there is an error in the USB layer. ++ */ ++int msc_scsi_write_10(struct msc_private *msc, char *name, int op) ++{ ++ SCSI_WRITE_10_COMMAND *command = (SCSI_WRITE_10_COMMAND *)&msc->command.CBWCB; ++ ++ RETURN_ZERO_IF (msc_check_blockdev(msc, 0)); ++ ++ // save the CBW and setup for receiving data to be written to the block device ++ // ++ msc->lba = be32_to_cpu(command->LogicalBlockAddress); ++ msc->TransferLength_in_blocks = be16_to_cpu(command->TransferLength); ++ msc->TransferLength_in_bytes = msc->TransferLength_in_blocks * msc->block_size; ++ ++ msc->command_state = MSC_DATA_OUT_WRITE; ++ msc->io_state = MSC_INACTIVE; ++ ++ TRACE_MSG1(MSC,"RECV WRITE lba: %x", msc->lba); ++ TRACE_MSG1(MSC,"RECV WRITE blocks: %d", msc->TransferLength_in_blocks); ++ ++ return (msc->TransferLength_in_blocks) ? ++ msc_start_receiving_data(msc->function, msc) : ++ msc_start_sending_csw(msc->function, msc, USB_MSC_PASSED); ++} ++ ++ ++/* msc_start_receiving_data - called to initiate an urb to receive WRITE(10) data ++ * ++ * Initiate a receive urb to receive upto PAGE_SIZE WRITE(10) data. The ++ * msc_recv_urb() function will call msc_recv_out_blocks() to actually ++ * write the data. ++ * ++ * This is called from msc_scsi_write_10() to initiate the WRITE(10) and ++ * called from msc_data_written() to start the next page sized receive ++ * urb. ++ * ++ */ ++int msc_start_receiving_data(struct usbd_function_instance *function, struct msc_private *msc) ++{ ++ /* ++ * Calculating the length is most of the work we do :-) ++ */ ++ int TransferLength_in_blocks = MIN(msc->max_blocks, msc->TransferLength_in_blocks); ++ ++ TRACE_MSG1(MSC,"START RECEIVING DATA lba: %x", msc->lba); ++ TRACE_MSG1(MSC,"START RECEIVING DATA blocks: %d", msc->TransferLength_in_blocks); ++ TRACE_MSG1(MSC,"START RECEIVING DATA blocks: %d", TransferLength_in_blocks); ++ TRACE_MSG1(MSC,"START RECEIVING DATA bytes: %d", TransferLength_in_blocks * msc->block_size); ++ ++ THROW_IF(msc->command_state != MSC_DATA_OUT_WRITE, error); ++ THROW_IF(!msc->TransferLength_in_blocks, error); ++ ++ msc->io_state |= MSC_RECV_PENDING; ++ ++ return msc_start_recv_urb(function, msc, TransferLength_in_blocks * msc->block_size); ++ ++ CATCH(error) { ++ TRACE_MSG0(MSC,"START RECEIVING DATA ERROR"); ++ return -EINVAL; ++ } ++} ++ ++ ++/* msc_recv_out_blocks - process received WRITE(10) data by writing to block device ++ * ++ * We get here indirectly from msc_recv_urb() when state is MSC_DATA_OUT_WRITE. ++ * ++ * Dealloc the urb and call Initiate the generic request to write the ++ * received data. The b_end_io function msc_data_written() will send the ++ * CSW. ++ * ++ */ ++void msc_recv_out_blocks(struct usbd_urb *rcv_urb, struct msc_private *msc) ++{ ++ int TransferLength_in_bytes = rcv_urb->actual_length; ++ int TransferLength_in_blocks = TransferLength_in_bytes / msc->block_size; ++ u32 crc; ++ int i; ++ ++ TRACE_MSG1(MSC,"RECV OUT bytes: %d", TransferLength_in_bytes); ++ TRACE_MSG1(MSC,"RECV OUT iostate: %x", msc->io_state); ++ TRACE_MSG1(MSC,"RECV OUT data transferred: %d", msc->data_transferred_in_bytes); ++ ++ /* ++ * Race condition here, we may get to here before the previous block ++ * write completed. If so just exit and the msc_data_written() ++ * function will recall us. ++ */ ++ { ++ unsigned long flags; ++ local_irq_save(flags); ++ if (msc->io_state & MSC_BLOCKIO_PENDING) { ++ TRACE_MSG0(MSC,"RECV OUT BLOCKIO PENDING"); ++ msc->io_state |= MSC_RECV_FINISHED; ++ msc->rcv_urb_finished = rcv_urb; ++ local_irq_restore(flags); ++ return; ++ } ++ msc->io_state &= ~(MSC_RECV_PENDING |MSC_RECV_FINISHED); ++ local_irq_restore(flags); ++ } ++ ++ msc->data_transferred_in_bytes += rcv_urb->actual_length; ++ ++ for (i = 0; i < TransferLength_in_blocks; i++) { ++ crc = crc32_compute(rcv_urb->buffer + (i * msc->block_size), msc->block_size, CRC32_INIT); ++ TRACE_SLBA(msc->lba + i, crc); ++ } ++ ++ msc->write_bh.b_data = page_address(msc->write_bh.b_page); ++ memcpy(msc->write_bh.b_data, rcv_urb->buffer, rcv_urb->actual_length); ++ ++ usbd_free_urb(rcv_urb); ++ ++ if (msc->block_dev_state != DEVICE_INSERTED) { ++ TRACE_MSG0(MSC,"START READ MEDIUM NOT PRESENT"); ++ msc_start_sending_csw_failed (msc, SCSI_SENSEKEY_MEDIUM_NOT_PRESENT, msc->lba, USB_MSC_FAILED); ++ return; ++ } ++ ++ if (msc->lba >= msc->capacity) { ++ TRACE_MSG2(MSC, "START READ LBA out of range: lba: %d capacity: %d", msc->lba, msc->capacity); ++ msc_start_sending_csw_failed (msc, SCSI_SENSEKEY_LOGICAL_BLOCK_OUT_OF_RANGE, msc->lba, USB_MSC_FAILED); ++ return; ++ } ++ ++ if (!msc->TransferLength_in_blocks) { ++ TRACE_MSG0(MSC,"START READ LBA TransferLength_in_blocks zero:"); ++ msc_start_sending_csw_failed (msc, SCSI_SENSEKEY_LOGICAL_BLOCK_OUT_OF_RANGE, msc->lba, USB_MSC_FAILED); ++ return; ++ } ++ ++ // Initiate writing the data ++ ++ TRACE_MSG1(MSC,"RECV OUT lba: %x", msc->lba); ++ TRACE_MSG1(MSC,"RECV OUT blocks left: %d", msc->TransferLength_in_blocks); ++ TRACE_MSG1(MSC,"RECV OUT blocks current: %d", TransferLength_in_blocks); ++ ++ ++ // set address in buffer head ++ msc->write_bh.b_size = TransferLength_in_bytes; ++ msc->write_bh.b_rsector = msc->lba; ++ ++ // decrement counters and increment address ++ msc->TransferLength_in_bytes -= TransferLength_in_bytes; ++ msc->TransferLength_in_blocks -= TransferLength_in_blocks; ++ msc->lba += TransferLength_in_blocks; ++ msc->write_bh.b_state = (1UL << BH_Mapped) | (1UL << BH_Lock); ++ msc->write_bh.b_end_io = msc_data_written; ++ msc->io_state |= MSC_BLOCKIO_PENDING; ++ ++ ++ // start new urb here ++ if (!msc->TransferLength_in_blocks) ++ msc->command_state = MSC_DATA_OUT_WRITE_FINISHED; ++ else ++ msc_start_receiving_data(msc->function, msc); ++ ++ generic_make_request(WRITE, &msc->write_bh); ++ generic_unplug_device(blk_get_queue(msc->write_bh.b_rdev)); ++} ++ ++ ++/* msc_data_written - called by generic request ++ * ++ * Called when current block i/o read is finished, restart block i/o or send the CSW. ++ * ++ */ ++void msc_data_written(struct buffer_head *bh, int uptodate) ++{ ++ struct msc_private *msc = bh->b_private; ++ unsigned long flags; ++ u8 io_state; ++ ++ msc_interrupts++; ++ ++ TRACE_MSG4(MSC,"DATA WRITTEN uptodate: %x b_state: %x state: %x io: %x", ++ uptodate, bh->b_state, msc->command_state, msc->io_state); ++ ++ TRACE_MSG1(MSC,"DATA WRITTEN lba: %x", msc->lba); ++ ++ local_irq_save(flags); ++ io_state = msc->io_state &= ~MSC_BLOCKIO_PENDING; ++ msc->write_bh.b_data = NULL; ++ local_irq_restore(flags); ++ ++ /* ++ * verify that the I/O completed ++ */ ++ if (1 != uptodate) { ++ TRACE_MSG0(MSC,"BLOCK READ FAILED"); ++ msc_start_sending_csw_failed (msc, SCSI_SENSEKEY_UNRECOVERED_READ_ERROR, msc->lba, USB_MSC_FAILED); ++ // XXX CHECKME ++ if (msc->rcv_urb_finished) { ++ usbd_free_urb(msc->rcv_urb_finished); ++ msc->rcv_urb_finished = NULL; ++ } ++ return; ++ } ++ /* ++ * If there was a rcv_urb that was not processed then we need ++ * to process it now. This is done by simply restarting msc_recv_out() ++ */ ++ if ((io_state & MSC_RECV_FINISHED) && msc->rcv_urb_finished) { ++ struct usbd_urb *rcv_urb = msc->rcv_urb_finished; ++ msc->rcv_urb_finished = NULL; ++ TRACE_MSG0(MSC,"DATA WRITTEN RECV FINISHED"); ++ msc_recv_out_blocks(rcv_urb, msc); ++ } ++ /* ++ * DATA_IN mode and no more data to write ++ */ ++ if (MSC_DATA_OUT_WRITE_FINISHED == msc->command_state) { ++ // finished, send CSW ++ TRACE_MSG0(MSC,"DATA WRITTEN send CSW"); ++ msc_start_sending_csw(msc->function, msc, USB_MSC_PASSED); ++ } ++} ++ ++ ++/* SCSI Commands ******************************************************************************* */ ++ ++/* msc_scsi_inquiry - process an inquiry ++ * ++ * Used by: ++ * win2k ++ * ++ * Returns non-zero if there is an error in the USB layer. ++ */ ++int msc_scsi_inquiry(struct msc_private *msc, char *name, int op) ++{ ++ SCSI_INQUIRY_COMMAND *command = (SCSI_INQUIRY_COMMAND *)&msc->command.CBWCB; ++ SCSI_INQUIRY_DATA *data; ++ struct usbd_urb *urb; ++ int length = sizeof(SCSI_INQUIRY_DATA); ++ ++ /* ++ * C.f. SPC2 7.3 INQUIRY command ++ * C.f. Table 46 - Standard INQUIRY data format ++ * ++ * C.f. Table 47 - Peripheral Qualifier ++ * ++ * 000b The specified peripheral device type is currently connected to this ++ * logical unit..... ++ * 001b The device server is capable of of supporting the peripheral device ++ * type on this logical unit. However, the physical device is not currently ++ * connected to this logical unit..... ++ * 010b Reserved ++ * 011b The device server is not capable of supporting a physical device on ++ * this logical unit.... ++ * ++ */ ++ ++ TRACE_MSG4(MSC,"INQUIRY EnableVPD: %02x LogicalUnitNumber: %02x PageCode: %02x AllocLen: %02x", ++ command->EnableVPD, command->LogicalUnitNumber, command->PageCode, command->AllocationLength); ++ ++ // XXX THROW_IF(msc->command_state != MSC_READY, error); ++ ++ RETURN_EINVAL_IF(!(urb = msc_alloc_urb(msc, length))); ++ ++ data = (SCSI_INQUIRY_DATA *)urb->buffer; ++ data->PeripheralQaulifier = msc->block_dev_state & (DEVICE_EJECTED | DEVICE_CHANGE_ON) ? 0x1 : 0; ++ data->PeripheralDeviceType = 0x00; ++ data->RMB = 0x1; ++ data->ResponseDataFormat = 0x1; ++ data->AdditionalLength = 0x1f; ++ ++ strncpy(data->VendorInformation, CONFIG_OTG_MSC_MANUFACTURER, strlen(CONFIG_OTG_MSC_MANUFACTURER)); ++ strncpy(data->ProductIdentification, CONFIG_OTG_MSC_PRODUCT_NAME, strlen(CONFIG_OTG_MSC_PRODUCT_NAME)); ++ ++ return msc_dispatch_query_urb(urb, msc, length, USB_MSC_PASSED); ++} ++ ++ ++/* old_msc_scsi_read_format_capacity - process a query ++ * ++ * Used by: ++ * win2k ++ * ++ * Returns non-zero if there is an error in the USB layer. ++ */ ++int old_msc_scsi_read_format_capacity(struct msc_private *msc, char *name, int op) ++{ ++ // send ZLP then CSW ++ return msc_dispatch_query_urb_zlp(msc, 0, 0, USB_MSC_PASSED); ++} ++ ++/* msc_scsi_read_format_capacity - process a query ++ * ++ * Used by: ++ * win2k ++ * ++ * Returns non-zero if there is an error in the USB layer. ++ */ ++int msc_scsi_read_format_capacity(struct msc_private *msc, char *name, int op) ++{ ++ SCSI_READ_FORMAT_CAPACITY_DATA *data; ++ struct usbd_urb *urb; ++ int length = sizeof(SCSI_READ_FORMAT_CAPACITY_DATA); ++ u32 block_num = msc->capacity; ++ u32 block_len; ++ ++ ++ ++ /* ++ * If we don't have a valid block device then simply ++ * send a ZLP. ++ * ++ * XXX VERIFY this is correct thing to do for this situation. ++ */ ++ //if (msc->block_dev_state & (DEVICE_EJECTED | DEVICE_CHANGE_ON)) ++ // return msc_dispatch_query_urb_zlp(msc); ++ RETURN_ZERO_IF (msc_check_blockdev(msc, 1)); ++ ++ RETURN_EINVAL_IF(!(urb = msc_alloc_urb(msc, length))); ++ ++ data = (SCSI_READ_FORMAT_CAPACITY_DATA *) urb->buffer; ++ ++ data->CapacityListHeader.CapacityListLength = sizeof(data->CurrentMaximumCapacityDescriptor); ++ ++ data->CurrentMaximumCapacityDescriptor.NumberofBlocks = block_num; ++ data->CurrentMaximumCapacityDescriptor.DescriptorCode = 0x03; ++ memcpy(data->CurrentMaximumCapacityDescriptor.BlockLength + 1, &block_len, sizeof(block_len)); ++ ++ return msc_dispatch_query_urb(urb, msc, length, USB_MSC_PASSED); ++} ++ ++/* msc_read_capacity - process a read_capacity command ++ * ++ * Used by: ++ * win2k ++ * ++ * Returns non-zero if there is an error in the USB layer. ++ */ ++int msc_scsi_read_capacity(struct msc_private *msc, char *name, int op) ++{ ++ SCSI_READ_CAPACITY_COMMAND *command = (SCSI_READ_CAPACITY_COMMAND *)&msc->command.CBWCB; ++ SCSI_READ_CAPACITY_DATA *data; ++ struct usbd_urb *urb; ++ int length = 8; ++ u32 lba; ++ ++ /* ++ * C.f. RBC 5.3 ++ */ ++ ++ /* ++ * If we don't have a valid block device then simply ++ * send a ZLP. ++ * ++ * XXX VERIFY this is correct thing to do for this situation. ++ */ ++ //if (msc->block_dev_state & (DEVICE_EJECTED | DEVICE_CHANGE_ON)) ++ // return msc_dispatch_query_urb_zlp(msc); ++ ++ RETURN_ZERO_IF (msc_check_blockdev(msc, 1)); ++ ++ //memcpy(&lba, &command->LogicalBlockAddress, 4); ++ //lba = be32_to_cpu(lba); ++ ++ lba = be32_to_cpu(command->LogicalBlockAddress); ++ ++ TRACE_MSG1(MSC,"READ CAPACITY LBA: %d", lba); ++ ++ if ((command->PMI > 1) || (!command->PMI && lba)) { ++ TRACE_MSG1(MSC,"READ CAPACITY PMI: %d", command->PMI); ++ return msc_start_sending_csw_failed (msc, SCSI_SENSEKEY_INVALID_FIELD_IN_CDB, lba, USB_MSC_FAILED); ++ } ++ ++ // alloc urb ++ RETURN_EINVAL_IF(!(urb = msc_alloc_urb(msc, length))); ++ ++ data = (SCSI_READ_CAPACITY_DATA *) urb->buffer; ++ ++ data->LastLogicalBlockAddress = cpu_to_be32(msc->capacity); ++ data->BlockLengthInBytes = cpu_to_be32(msc->block_size); ++ ++ TRACE_MSG1(MSC,"RECV READ CAPACITY lba: %x", be32_to_cpu(data->LastLogicalBlockAddress)); ++ TRACE_MSG1(MSC,"RECV READ CAPACITY block_size: %x", be32_to_cpu(data->BlockLengthInBytes)); ++ ++ return msc_dispatch_query_urb(urb, msc, length, USB_MSC_PASSED); ++} ++ ++ ++/* msc_scsi_request_sense - process a request_sense command ++ * ++ * Returns non-zero if there is an error in the USB layer. ++ */ ++int msc_scsi_request_sense(struct msc_private *msc, char *name, int op) ++{ ++ SCSI_REQUEST_SENSE_COMMAND* command = (SCSI_REQUEST_SENSE_COMMAND *)&msc->command.CBWCB; ++ SCSI_REQUEST_SENSE_DATA *data; ++ ++ /* ++ * C.f. SPC2 7.20 REQUEST SENSE command ++ */ ++ ++ struct usbd_urb *urb; ++ int length = sizeof(SCSI_REQUEST_SENSE_DATA); ++ ++ // alloc urb ++ RETURN_EINVAL_IF(!(urb = msc_alloc_urb(msc, length))); ++ ++ data = (SCSI_REQUEST_SENSE_DATA *) urb->buffer; ++ memset(command, 0x0, length); ++ data->ErrorCode = SCSI_ERROR_CURRENT; ++ data->SenseKey = msc->sensedata >> 16; ++ data->AdditionalSenseCode = msc->sensedata >> 8; ++ data->AdditionalSenseCodeQualifier = msc->sensedata; ++ data->Valid = 1; ++ ++ set_sense_data(msc, SCSI_SENSEKEY_NO_SENSE, 0); ++ ++ return msc_dispatch_query_urb(urb, msc, length, USB_MSC_PASSED); ++} ++ ++/* msc_scsi_mode_sense - process a request_sense command ++ * ++ * Used by: ++ * win2k ++ * ++ * Returns non-zero if there is an error in the USB layer. ++ */ ++int old_msc_scsi_mode_sense(struct msc_private *msc, char *name, int op) ++{ ++ SCSI_MODE_SENSE_COMMAND *command = (SCSI_MODE_SENSE_COMMAND *)&msc->command.CBWCB; ++ SCSI_MODE_SENSE_DATA *data; ++ int length = sizeof(SCSI_MODE_SENSE_DATA); ++ ++ struct usbd_urb *urb; ++ u8 *cp; ++ ++ ++ TRACE_MSG4(MSC,"MODE SENSE dbd: %02x PageControl: %02x PageCode: %02x Alloc: %02x", ++ command->DBD, ++ command->PageControl, ++ command->PageCode, ++ 0); ++ ++ length = 8; ++ ++ // alloc urb ++ RETURN_EINVAL_IF(!(urb = msc_alloc_urb(msc, length))); ++ ++ cp = (u8 *) urb->buffer; ++ memset(cp, 0x0, length); ++ ++ cp[0] = 0; ++ cp[1] = 0; ++ cp[2] = 0; // 0x80 is writeprotect ++ cp[3] = 0x08; ++ cp[4] = 0; ++ cp[5] = 0; ++ cp[6] = 0; ++ cp[7] = 0; ++ ++ return msc_dispatch_query_urb(urb, msc, length, USB_MSC_PASSED); ++} ++ ++/* msc_scsi_mode_sense - process a request_sense command ++ * ++ * Used by: ++ * win2k ++ * ++ * Returns non-zero if there is an error in the USB layer. ++ */ ++int msc_scsi_mode_sense(struct msc_private *msc, char *name, int op) ++{ ++ ++ /* ++ * C.f. SPC2 7.8.1 MODE SENSE(6) command ++ */ ++ ++ static READ_WRITE_ERROR_RECOVERY_PAGE page_01 = { ++ PageCode:0x01, ++ PageLength:0x0A, ++ ReadRetryCount:0x03, ++ WriteRetryCount:0x80, ++ }; ++ static FLEXIBLE_DISK_PAGE page_05 = { ++ PageCode:0x05, ++ PageLength:0x1E, ++ TransferRate:__constant_cpu_to_be16(0xFA00), ++ NumberofHeads:0xA0, ++ SectorsperTrack:0x00, ++ DataBytesperSector:__constant_cpu_to_be16(0x0002), ++ NumberofCylinders:__constant_cpu_to_be16(0x0000), ++ MotorOnDelay:0x05, ++ MotorOffDelay:0x1E, ++ MediumRotationRate:__constant_cpu_to_be16(0x6801), ++ }; ++ static REMOVABLE_BLOCK_ACCESS_CAPABILITIES_PAGE page_1b = { ++ PageCode:0x1B, ++ PageLength:0x0A, ++ TLUN:0x01, ++ }; ++ static TIMER_AND_PROTECT_PAGE page_1c = { ++ PageCode:0x1c, ++ PageLength:0x06, ++ InactivityTimeMultiplier:0x0A, ++ }; ++ ++ SCSI_MODE_SENSE_COMMAND *command = (SCSI_MODE_SENSE_COMMAND *)&msc->command.CBWCB; ++ SCSI_MODE_SENSE_DATA *data; ++ struct usbd_urb *urb; ++ int length = sizeof(SCSI_MODE_SENSE_DATA); ++ ++ TRACE_MSG4(MSC,"MODE SENSE dbd: %02x PageControl: %02x PageCode: %02x Alloc: %02x", ++ command->DBD, ++ command->PageControl, ++ command->PageCode, ++ 0); ++ ++ ++ if (msc->block_dev_state & DEVICE_EJECTED) { ++ u16 sector = htons((unsigned short)msc->block_size); ++ u16 cylinder = 0; ++ page_05.NumberofHeads = 0; ++ page_05.SectorsperTrack = 0; ++ memcpy(&page_05.DataBytesperSector, §or, sizeof(sector)); ++ memcpy(&page_05.NumberofCylinders, &cylinder, sizeof(cylinder)); ++ } ++ else { ++ u16 sector = htons((unsigned short)msc->block_size); ++ u32 size = DEF_NUMBER_OF_HEADS * DEF_SECTORS_PER_TRACK * sector; ++ u16 cylinder = htons(msc->capacity / size); ++ page_05.NumberofHeads = DEF_NUMBER_OF_HEADS; ++ page_05.SectorsperTrack = DEF_SECTORS_PER_TRACK; ++ memcpy(&page_05.DataBytesperSector, §or, sizeof(sector)); ++ memcpy(&page_05.NumberofCylinders, &cylinder, sizeof(cylinder)); ++ ++ } ++ ++ ++ if (SCSI_MODEPAGE_CONTROL_CURRENT != command->PageControl) { ++ TRACE_MSG2(MSC,"MODE SENSE - requeested other than CONTROL_CURRENT: %d %d", ++ command->PageControl, command->PageCode); ++ return msc_start_sending_csw_failed (msc, SCSI_SENSEKEY_INVALID_FIELD_IN_CDB, msc->lba, USB_MSC_FAILED); ++ } ++ ++ RETURN_EINVAL_IF(!(urb = msc_alloc_urb(msc, length))); ++ data = (SCSI_MODE_SENSE_DATA *) urb->buffer; ++ ++ data->ModeParameterHeader.WriteProtect = msc->block_dev_state & DEVICE_WRITE_PROTECTED ? 1 : 0; ++ ++ switch (command->PageCode) { ++ case SCSI_MODEPAGE_ERROR_RECOVERY: ++ length = sizeof(MODE_PARAMETER_HEADER) + sizeof(page_01); ++ data->ModeParameterHeader.ModeDataLength = length - 1; ++ memcpy(&data->ModePages, &page_01, sizeof(page_01)); ++ break; ++ case SCSI_MODEPAGE_FLEXIBLE_DISK_PAGE: ++ length = sizeof(MODE_PARAMETER_HEADER) + sizeof(page_05); ++ data->ModeParameterHeader.ModeDataLength = length - 1; ++ memcpy(&data->ModePages, &page_05, sizeof(page_05)); ++ break; ++ case SCSI_MODEPAGE_REMOVABLE_BLOCK_ACCESS: ++ length = sizeof(MODE_PARAMETER_HEADER) + sizeof(page_1b); ++ data->ModeParameterHeader.ModeDataLength = length - 1; ++ memcpy(&data->ModePages, &page_1b, sizeof(page_1b)); ++ break; ++ case SCSI_MODEPAGE_INFORMATION_EXCEPTIONS: ++ length = sizeof(MODE_PARAMETER_HEADER) + sizeof(page_1c); ++ data->ModeParameterHeader.ModeDataLength = length - 1; ++ memcpy(&data->ModePages, &page_1c, sizeof(page_1c)); ++ break; ++ case SCSI_MODEPAGE_ALL_SUPPORTED: ++ length = sizeof(MODE_PARAMETER_HEADER) + sizeof(MODE_ALL_PAGES); ++ data->ModeParameterHeader.ModeDataLength = length - 1; ++ memcpy(&data->ModePages.ModeAllPages.ReadWriteErrorRecoveryPage, &page_01, sizeof(page_01)); ++ memcpy(&data->ModePages.ModeAllPages.FlexibleDiskPage, &page_05, sizeof(page_05)); ++ memcpy(&data->ModePages.ModeAllPages.RemovableBlockAccessCapabilitiesPage, &page_1b, sizeof(page_1b)); ++ memcpy(&data->ModePages.ModeAllPages.TimerAndProtectPage, &page_1c, sizeof(page_1c)); ++ break; ++ case SCSI_MODEPAGE_CACHING: ++ // see file_storage.c for an example if we want to support this ++ break; ++ } ++ ++ TRACE_MSG2(MSC,"LENGTH: %d %d", msc->command.dCBWDataTransferLength, length); ++ ++ /* ++ * XXX verify that length is <= 256 bytes, return CHECK_CONDITION if it is ++ */ ++ length = MIN(msc->command.dCBWDataTransferLength, length); ++ ++ return msc_dispatch_query_urb(urb, msc, length, USB_MSC_PASSED); ++} ++ ++ ++/* msc_scsi_test_unit_ready - process a request_sense command ++ * ++ * Used by: ++ * win2k ++ * ++ * Returns non-zero if there is an error in the USB layer. ++ */ ++int msc_scsi_test_unit_ready(struct msc_private *msc, char *name, int op) ++{ ++ /* ++ * C.f. SPC2 7.25 TEST UNIT READY command ++ * ++ * Return GOOD, illegal request for bad LUN or NOT READY ++ * ++ */ ++ RETURN_EINVAL_IF (msc_check_blockdev(msc, 0)); ++ return msc_start_sending_csw(msc->function, msc, USB_MSC_PASSED); ++} ++ ++ ++/* msc_scsi_prevent_allow - process a request_sense command ++ * ++ * Used by: ++ * win2k ++ * ++ * Returns non-zero if there is an error in the USB layer. ++ */ ++int msc_scsi_prevent_allow(struct msc_private *msc, char *name, int op) ++{ ++ SCSI_PREVENT_ALLOW_MEDIUM_REMOVAL_COMMAND* command = (SCSI_PREVENT_ALLOW_MEDIUM_REMOVAL_COMMAND*)&msc->command.CBWCB; ++ ++ /* ++ * C.f. SPC2 7.12 Table 78 PREVENT ALLOW MEDIUM REMOVAL Prevent Field ++ * ++ * 00b Medium removal shall be allowed from both the data transport ++ * element and the attached medium changer (if any). ++ * 01b Medium removal shall be prohibited from the data transport ++ * element but allowed from the attached medium changer (if any). ++ * 10b Medium removal shall be allowed for the data transport element ++ * but prohibited for the attached medium changer. ++ * 11b Medium remval shall be prohibited from both the data transport ++ * element and the attached medium changer ++ * ++ * Prevention shall terminate after 00b or 10b, after a SYNC CACHE or hard reset. ++ */ ++ ++ TRACE_MSG1(MSC,"PREVENT: %d", command->Prevent); ++ ++ RETURN_EINVAL_IF (msc_check_blockdev(msc, 0)); ++ ++ ++ // XXX TODO ++ // this is from storageproto.c, shouldn't we implement something? ++ if (command->Prevent) ++ return msc_start_sending_csw_failed (msc, SCSI_SENSEKEY_INVALID_FIELD, msc->lba, USB_MSC_FAILED); ++ ++ return msc_start_sending_csw(msc->function, msc, USB_MSC_PASSED); ++} ++ ++ ++/* msc_scsi_start_stop - process a request_sense command ++ * ++ * C.f. RBC 5.4 and 5.4.2 ++ * ++ * Used by: ++ * win2k ++ * ++ * Returns non-zero if there is an error in the USB layer. ++ */ ++int msc_scsi_start_stop(struct msc_private *msc, char *name, int op) ++{ ++ SCSI_START_STOP_COMMAND* command = (SCSI_START_STOP_COMMAND*)&msc->command.CBWCB; ++ ++ TRACE_MSG4(MSC,"START STOP: Immed: %d Power: %x LoEj: %d Start: %d", ++ command->IMMED, command->PowerConditions, command->LoEj, command->Start); ++ ++ /* ++ * C.f. 5.4 ++ * ++ * IMMED - if set return status immediately after command validation, otherwise ++ * return status as soon operation is completed. ++ * ++ * C.f. 5.4.1 Table 8 POWER CONDITIONS ++ * ++ * 0 - M - no change in power condition ++ * 1 - M - place device in active condition ++ * 2 - M - place device in idle condition ++ * 3 - M - place device in Standby condition ++ * 4 - - reserved ++ * 5 - M - place device in Sleep condition ++ * 6 - - reserved ++ * 7 - 0 - Device Control ++ * ++ * C.f. 5.4.2 Table 9 START STOP control bit definitions ++ * ++ * Power Load/Eject Start ++ * 1-7 x x LoEj and Start Ignored ++ * 0 0 0 Stop the medium ++ * 0 0 1 Make the medium ready ++ * 0 1 0 Unload the medium ++ * 0 1 1 Load the medium ++ * ++ */ ++ ++ ++ RETURN_EINVAL_IF (msc_check_blockdev(msc, 0)); ++ ++ // XXX TODO ++ // this is from storageproto.c, shouldn't we implement something? ++ ++ if (command->Start && command->LoEj) ++ return msc_start_sending_csw_failed (msc, SCSI_SENSEKEY_INVALID_FIELD, msc->lba, USB_MSC_FAILED); ++ ++ return msc_start_sending_csw(msc->function, msc, USB_MSC_PASSED); ++} ++ ++ ++/* msc_scsi_verify - process a verify command ++ * ++ * Used by: ++ * win2k ++ * ++ * Returns non-zero if there is an error in the USB layer. ++ */ ++int msc_scsi_verify(struct msc_private *msc, char *name, int op) ++{ ++ SCSI_VERIFY_COMMAND *command = (SCSI_VERIFY_COMMAND *)&msc->command.CBWCB; ++ /* ++ * C.f. RBC 5.7 VERIFY command ++ */ ++ // XXX This actually should use the read_10 function and when ++ // finished reading simply send the following ++ return msc_start_sending_csw(msc->function, msc, USB_MSC_PASSED); ++} ++ ++ ++/* msc_scsi_mode_select - process a select command ++ * ++ * Returns non-zero if there is an error in the USB layer. ++ */ ++int msc_scsi_mode_select(struct msc_private *msc, char *name, int op) ++{ ++ //SCSI_MODE_SELECT_COMMAND *command = (SCSI_MODE_SELECT_COMMAND *)&msc->command.CBWCB; ++ ++ /* ++ * C.f. SPC2 7.6 MODE SELECT(6) command ++ */ ++ ++ // if less than correct amount of data return USB_MSC_PHASE_ERROR - see MV ++ // ++ return msc_start_sending_csw(msc->function, msc, USB_MSC_PASSED); ++} ++ ++ ++/* msc_cmd_unknown - process an unknown command ++ * ++ * ++ * Returns non-zero if there is an error in the USB layer. ++ */ ++int msc_cmd_unknown(struct msc_private *msc, char *name, int op) ++{ ++ /* ++ struct usbd_urb *urb; ++ RETURN_EINVAL_IF(!(urb = msc_alloc_urb(msc, 1))); // XXX +1 ? ++ urb->flags |= USBD_URB_SENDZLP; ++ return msc_dispatch_query_urb(urb, msc, 0); ++ */ ++ ++ printk(KERN_ERR"%s: NOT IMPLEMENTED %s opcode: %02x\n", __FUNCTION__, name, op); ++ ++ // should we send ZLP for unknow commands? ++ // return msc_dispatch_query_urb_zlp(msc); ++ ++ // or failed ++ return msc_start_sending_csw_failed (msc, SCSI_SENSEKEY_INVALID_COMMAND, msc->lba, USB_MSC_FAILED); ++} ++ ++ ++struct rbc_dispatch { ++ u8 op; ++ char *name; ++ int (*rbc_command) (struct msc_private *, char *, int op); ++}; ++ ++/* Command cross reference ++ * ++ * This is the list of commands observed from each host OS. It is necessarily ++ * incomplete in that we not have reached some condition necessary to have ++ * other commands used. ++ * Win2k WinXP ++ * SCSI_TEST_UNIT_READY yes yes ++ * SCSI_READ_10 yes yes ++ * SCSI_WRITE_10 yes yes ++ * SCSI_READ_CAPACITY yes yes ++ * SCSI_VERIFY yes ++ * SCSI_INQUIRY yes yes ++ * SCSI_MODE_SENSE yes ++ * SCSI_READ_FORMAT_CAPACITY yes yes ++ * SCSI_REQUEST_SENSE ++ * SCSI_PREVENT_ALLOW_MEDIUM_REMOVAL ++ * SCSI_START_STOP ++ * SCSI_MODE_SELECT ++ * SCSI_FORMAT_UNIT ++ * ++ */ ++ ++struct rbc_dispatch rbc_dispatch_table[] = { ++ { SCSI_TEST_UNIT_READY, "SCSI_TEST_UNIT_READY", msc_scsi_test_unit_ready }, // ++ { SCSI_READ_10, "SCSI_READ_10", msc_scsi_read_10 }, // ++ { SCSI_WRITE_10, "SCSI_WRITE_10", msc_scsi_write_10 }, // ++ { SCSI_READ_CAPACITY, "SCSI_READ_CAPACITY", msc_scsi_read_capacity }, // ++ { SCSI_VERIFY, "SCSI_VERIFY", msc_scsi_verify }, // ++ { SCSI_INQUIRY, "SCSI_INQUIRY", msc_scsi_inquiry }, // ++ { SCSI_MODE_SENSE, "SCSI_MODE_SENSE", msc_scsi_mode_sense }, // ++ { SCSI_READ_FORMAT_CAPACITY, "SCSI_READ_FORMAT_CAPACITY", msc_scsi_read_format_capacity }, // ++ { SCSI_REQUEST_SENSE, "SCSI_REQUEST_SENSE", msc_scsi_request_sense }, // ++ { SCSI_PREVENT_ALLOW_MEDIUM_REMOVAL, "SCSI_PREVENT_ALLOW_MEDIUM_REMOVAL", msc_scsi_prevent_allow }, // ++ { SCSI_START_STOP, "SCSI_START_STOP", msc_scsi_start_stop }, // ++ { SCSI_MODE_SELECT, "SCSI_MODE_SELECT", msc_scsi_mode_select }, ++ { SCSI_FORMAT_UNIT, "SCSI_FORMAT_UNIT", msc_cmd_unknown }, ++ ++ { SCSI_READ_6, "SCSI_READ_6", msc_cmd_unknown }, ++ { SCSI_WRITE_6, "SCSI_WRITE_6", msc_cmd_unknown }, ++ { SCSI_RESERVE, "SCSI_RESERVE", msc_cmd_unknown }, ++ { SCSI_RELEASE, "SCSI_RELEASE", msc_cmd_unknown }, ++ { SCSI_SEND_DIAGNOSTIC, "SCSI_SEND_DIAGNOSTIC", msc_cmd_unknown }, ++ { SCSI_SYNCHRONIZE_CACHE, "SCSI_SYNCHRONIZE_CACHE", msc_cmd_unknown }, ++ { SCSI_MODE_SENSE_10, "SCSI_MODE_SENSE_10", msc_cmd_unknown }, ++ { SCSI_REZERO_UNIT, "SCSI_REZERO_UNIT", msc_cmd_unknown }, ++ { SCSI_SEEK_10, "SCSI_SEEK_10", msc_cmd_unknown }, ++ { SCSI_WRITE_AND_VERIFY, "SCSI_WRITE_AND_VERIFY", msc_cmd_unknown }, ++ { SCSI_WRITE_12, "SCSI_WRITE_12", msc_cmd_unknown }, ++ { SCSI_READ_12, "SCSI_READ_12", msc_cmd_unknown }, ++ ++ { 0xff, "SCSI_UNKNOWN", msc_cmd_unknown }, ++}; ++ ++ ++/* msc_recv_command - process a new CBW ++ * ++ * Return non-zero if urb was not disposed of. ++ */ ++int msc_recv_command(struct usbd_urb *urb, struct msc_private *msc) ++{ ++ COMMAND_BLOCK_WRAPPER *command = (COMMAND_BLOCK_WRAPPER *)urb->buffer; ++ u8 op = command->CBWCB[0]; ++ struct rbc_dispatch *dispatch; ++ ++ /* ++ * c.f. section 6.2 - Valid and Meaningful CBW ++ * c.f. section 6.2.1 - Valid CBW ++ * ++ * The CBW was received after the device had sent a CSW or after a ++ * reset XXX check that we only set MSC_READY after reset or sending ++ * CSW. ++ * ++ * The CBW is 31 (1Fh) bytes in length and the bCBWSignature is ++ * equal to 43425355h. ++ */ ++ THROW_IF(31 != urb->actual_length, error); ++ THROW_IF(CBW_SIGNATURE != le32_to_cpu(command->dCBWSignature), error); ++ ++ /* ++ * c.f. section 6.2.2 - Meaningful CBW ++ * ++ * no reserved bits are set ++ * the bCBWLUN contains a valid LUN supported by the device ++ * both bCBWCBlength and the content of the CBWCB are in accordance with bInterfaceSubClass ++ */ ++ ++ // XXX checklun etc ++ ++ /* ++ * Success ++ */ ++ memcpy(&msc->command, command, sizeof(COMMAND_BLOCK_WRAPPER)); ++ msc->data_transferred_in_bytes = msc->TransferLength_in_blocks = msc->TransferLength_in_bytes = 0; ++ ++ TRACE_TAG(command->dCBWTag, urb->framenum); ++ usbd_free_urb(urb); ++ ++ /* ++ * Search using the opcode to find the dispatch function to use and ++ * call it. ++ */ ++ for (dispatch = rbc_dispatch_table; dispatch->op != 0xff; dispatch++) { ++ if ((dispatch->op == op)) { ++ TRACE_CBW(dispatch->name, dispatch->op); ++ TRACE_RECV(&(command->CBWCB[1])); ++ if (dispatch->rbc_command(msc, dispatch->name, op)) ++ TRACE_MSG0(MSC,"COMMAND ERROR"); ++ return 0; ++ } ++ } ++ /* FALL THROUGH if no match is found */ ++ ++ THROW_IF(msc_cmd_unknown(msc, "CMD_UNKNOWN", op), error); ++ return 0; ++ ++ CATCH(error) { ++ TRACE_MSG0(MSC,"RECV CBW ERROR"); ++ } ++ ++ /* ++ * default behaviour is to stall or send ZLP on BULK-IN endpoint ++ */ ++ TRACE_CBW("UNKNOWN COMMAND", op); ++ ++ /* ++ * We cannot return error as we have deallocated urb and there is no ++ * other sensible course of action that the usbdcore layer can take ++ * for us. ++ */ ++ return 0; ++} ++ ++ ++/* Sent Function - process a sent urb ********************************************************** */ ++ ++/* msc_urb_sent - called to indicate URB transmit finished ++ * @urb: pointer to struct usbd_urb ++ * @rc: result ++ * ++ * This is called when an urb is sent. Depending on current state ++ * it may: ++ * ++ * - continue sending data ++ * - send a CSW ++ * - start a recv for a CBW ++ * ++ * This is called from BOTTOM HALF context. ++ * ++ * Return non-zero if urb was not disposed of. ++ */ ++int msc_urb_sent (struct usbd_urb *tx_urb, int rc) ++{ ++ struct usbd_function_instance *function; ++ struct msc_private *msc = &msc_private; ++ ++ msc_interrupts++; ++ ++ TRACE_MSG1(MSC,"URB SENT tx_urb: %p", (int)tx_urb); ++ TRACE_MSG1(MSC,"URB SENT function: %p", (int)tx_urb->function_instance); ++ ++ RETURN_EINVAL_IF(!(function = tx_urb->function_instance)); ++ ++ TRACE_MSG1(MSC,"URB SENT status: %p", usbd_get_device_status(function)); ++ RETURN_EINVAL_IF(usbd_get_device_status(function) == USBD_CLOSING); ++ ++ TRACE_MSG1(MSC,"URB SENT state: %p", usbd_get_device_state(function)); ++ RETURN_EINVAL_IF(usbd_get_device_state(function) != STATE_CONFIGURED); ++ ++ switch (msc->command_state) { ++ case MSC_DATA_IN_READ: ++ case MSC_DATA_IN_READ_FINISHED: ++ TRACE_MSG0(MSC,"URB SENT READ"); ++ return msc_in_read_10_urb_sent(tx_urb, msc); ++ case MSC_QUERY: ++ // finished, send CSW ++ TRACE_MSG0(MSC,"URB SENT QUERY"); ++ msc_start_sending_csw(msc->function, msc, USB_MSC_PASSED); ++ break; ++ case MSC_STATUS: ++ default: ++ // sent a CSW need to receive the next CBW ++ TRACE_MSG0(MSC,"URB SENT STATUS"); ++ msc->command_state = MSC_READY; ++ msc_start_recv_urb(msc->function, msc, sizeof(COMMAND_BLOCK_WRAPPER)); ++ break; ++ } ++ usbd_free_urb (tx_urb); ++ return 0; ++} ++ ++ ++/* Receive Function - receiving an urb ********************************************************* */ ++ ++/* msc_recv_urb - process a received urb ++ * ++ * Return non-zero if urb was not disposed of. ++ */ ++static int msc_recv_urb (struct usbd_urb *rcv_urb, int rc) ++{ ++ struct msc_private *msc = &msc_private; ++ ++ msc_interrupts++; ++ ++ RETURN_EINVAL_IF(!msc->connected); ++ ++ TRACE_MSG2(MSC, "RECV URB length: %d state: %d", rcv_urb->actual_length, msc->command_state); ++ ++ switch(msc->command_state) { ++ ++ // ready to start a new transaction ++ case MSC_READY: ++ return msc_recv_command(rcv_urb, msc); ++ ++ // we think we are receiving data ++ case MSC_DATA_OUT_WRITE: ++ case MSC_DATA_OUT_WRITE_FINISHED: ++ msc_recv_out_blocks(rcv_urb, msc); ++ return 0; ++ ++ // we think we are sending data ++ case MSC_DATA_IN_READ: ++ case MSC_DATA_IN_READ_FINISHED: ++ msc_start_sending_csw_failed (msc, SCSI_SENSEKEY_INVALID_COMMAND, msc->lba, USB_MSC_FAILED); ++ break; ++ ++ // we think we are sending status ++ case MSC_STATUS: ++ msc_start_sending_csw_failed (msc, SCSI_SENSEKEY_INVALID_COMMAND, msc->lba, USB_MSC_FAILED); ++ break; ++ ++ // we don't think ++ case MSC_UNKNOWN: ++ default: ++ TRACE_MSG0(MSC,"RECV URB ERROR"); ++ } ++ // let caller dispose of urb ++ return -EINVAL; ++} ++ ++/* USB Device Functions ************************************************************************ */ ++ ++/* msc_event_handler - process a device event ++ * ++ * This function is called when an USBD event occurs. ++ * ++ * This is called from INTERRUPT context. ++ */ ++void msc_event_handler (struct usbd_function_instance *function, usbd_device_event_t event, int data) ++{ ++ struct msc_private *msc = &msc_private; ++ ++ msc_interrupts++; ++ ++ TRACE_MSG1(MSC,"EVENT IRQ %d", event); ++ ++ switch (event) { ++ case DEVICE_CONFIGURED: ++ TRACE_MSG0(MSC,"EVENT CONFIGURED"); ++ msc->connected = 1; ++ msc->command_state = MSC_READY; ++ msc_start_recv_urb(function, msc, sizeof(COMMAND_BLOCK_WRAPPER)); ++ break; ++ ++ case DEVICE_BUS_INACTIVE: ++ case DEVICE_RESET: ++ case DEVICE_DE_CONFIGURED: ++ TRACE_MSG0(MSC,"EVENT RESET"); ++ BREAK_IF(!msc->connected); ++ msc->connected = 0; ++ if (msc->rcv_urb_finished) { ++ usbd_free_urb (msc->rcv_urb_finished); ++ msc->rcv_urb_finished = NULL; ++ } ++ break; ++ ++ default: ++ TRACE_MSG0(MSC,"EVENT IGNORED"); ++ break; ++ } ++} ++ ++ ++/* msc_device_request - called to indicate urb has been received ++ * ++ * This function is called when a SETUP packet has been received that ++ * should be handled by the function driver. It will not be called to ++ * process the standard chapter nine defined requests. ++ * ++ * Return non-zero for failure. ++ */ ++int msc_device_request (struct usbd_function_instance *function, struct usbd_device_request *request) ++{ ++ struct msc_private *msc = &msc_private; ++ struct usbd_urb *urb; ++ ++ TRACE_MSG0(MSC,"RECV SETUP"); ++ msc_interrupts++; ++ ++ // verify that this is a usb class request per cdc-acm specification or a vendor request. ++ RETURN_ZERO_IF (!(request->bmRequestType & (USB_REQ_TYPE_CLASS | USB_REQ_TYPE_VENDOR))); ++ ++ // determine the request direction and process accordingly ++ switch (request->bmRequestType & (USB_REQ_DIRECTION_MASK | USB_REQ_TYPE_MASK)) { ++ ++ case USB_REQ_HOST2DEVICE | USB_REQ_TYPE_CLASS: ++ switch (request->bRequest) { ++ case MSC_BULKONLY_RESET: ++ // XXX TODO FIXME ++ return 0; ++ } ++ ++ case USB_REQ_DEVICE2HOST | USB_REQ_TYPE_CLASS: ++ switch (request->bRequest) { ++ case MSC_BULKONLY_GETMAXLUN: ++ RETURN_EINVAL_IF(!(urb = usbd_alloc_urb_ep0(function, 1, NULL))); ++ urb->buffer[0] = 0; ++ urb->actual_length = 1; ++ RETURN_ZERO_IF(!usbd_start_in_urb(urb)); ++ usbd_free_urb(urb); ++ return -EINVAL; ++ } ++ default: ++ break; ++ } ++ return -EINVAL; ++} ++ ++/* msc_function_enable - this is called by the USBD core layer ++ * ++ * This is called to initialize the function when a bus interface driver ++ * is loaded. ++ */ ++static int msc_function_enable (struct usbd_function_instance *function) ++{ ++ struct msc_private *msc = &msc_private; ++ ++ MOD_INC_USE_COUNT; ++ ++ // XXX TODO need to verify that serial number is minimum of 12 ++ ++ msc_interrupts++; ++ ++ msc->function = function; ++ msc->command_state = MSC_READY; ++ ++ //msc_open_blockdev(msc); ++ return 0; ++} ++ ++/* msc_function_disable - this is called by the USBD core layer ++ * ++ * This is called to close the function when a bus interface driver ++ * is unloaded. ++ */ ++static void msc_function_disable (struct usbd_function_instance *function) ++{ ++ struct msc_private *msc = &msc_private; ++ ++ msc_interrupts++; ++ ++ TRACE_MSG0(MSC,"FUNCTION EXIT"); ++ ++ msc->function = NULL; ++ ++ MOD_DEC_USE_COUNT; ++} ++ ++static struct usbd_function_operations function_ops = { ++ event_handler: msc_event_handler, ++ device_request: msc_device_request, ++ function_enable: msc_function_enable, ++ function_disable: msc_function_disable, ++}; ++ ++static struct usbd_function_driver function_driver = { ++ name: "msc-bulkonly", ++ fops:&function_ops, ++ device_description:&msc_device_description, ++ bNumConfigurations:sizeof (msc_description) / sizeof (struct usbd_configuration_description), ++ configuration_description:msc_description, ++ idVendor: __constant_cpu_to_le16(CONFIG_OTG_MSC_VENDORID), ++ idProduct: __constant_cpu_to_le16(CONFIG_OTG_MSC_PRODUCTID), ++ bcdDevice: __constant_cpu_to_le16(CONFIG_OTG_MSC_BCDDEVICE), ++ bNumInterfaces:sizeof (msc_interfaces) / sizeof (struct usbd_interface_description), ++ interface_list:msc_interfaces, ++ endpointsRequested: ENDPOINTS, ++ requestedEndpoints: msc_endpoint_requests, ++}; ++ ++ ++/* USB Module init/exit ************************************************************************ */ ++ ++/* ++ * msc_modinit - module init ++ * ++ */ ++static int msc_modinit (void) ++{ ++ int rc; ++ struct msc_private *msc = &msc_private; ++ ++ printk(KERN_INFO "Copyright (c) 2004 Belcarra Technologies; www.belcarra.com; sl@belcarra.com\n"); ++ printk (KERN_INFO "%s vendor_id: %04x product_id: %04x major: %d minor: %d\n", __FUNCTION__, ++ vendor_id, product_id, major, minor); ++ ++ if (vendor_id) ++ function_driver.idVendor = cpu_to_le16(vendor_id); ++ if (product_id) ++ function_driver.idProduct = cpu_to_le16(product_id); ++ ++ init_waitqueue_head(&msc->msc_wq); ++#if 0 ++ /* ++ * Check device information and verify access to the block device. ++ */ ++ RETURN_EINVAL_IF(!major && !minor); ++ msc->dev = MKDEV(major, minor); ++ if(!(msc->bdev = bdget(kdev_t_to_nr(msc->dev)))) { ++ printk(KERN_INFO"%s: Cannot find device %d %d\n", __FUNCTION__, major, minor); ++ return -EINVAL; ++ } ++ if ((rc = blkdev_get(msc->bdev, FMODE_READ | FMODE_WRITE, 0, BDEV_RAW))) { ++ printk(KERN_INFO"%s: Cannot get device %d %d\n", __FUNCTION__, major, minor); ++ return -EINVAL; ++ } ++ ++ /* ++ * Note that capacity must return the LBA of the last addressable block ++ * c.f. RBC 4.4, RBC 5.3 and notes below RBC Table 6 ++ * ++ * The blk_size array contains the number of addressable blocks or N, ++ * capacity is therefore N-1. ++ */ ++ ++ msc->capacity = (blk_size[MAJOR(msc_private.dev)][MINOR(msc_private.dev)] << 1) - 1; ++ msc->block_size = get_hardsect_size(msc->dev); ++ msc->max_blocks = PAGE_SIZE / msc->block_size; ++#endif ++ ++ msc->block_dev_state = DEVICE_EJECTED; ++ ++ msc_open_blockdev(msc); ++ ++ /* ++ * setup generic buffer_head ++ */ ++ ++ msc->read_bh.b_rdev = msc->dev; ++ msc->read_bh.b_private = msc; ++ msc->read_bh.b_page = alloc_page(GFP_NOIO); ++ ++ msc->write_bh.b_rdev = msc->dev; ++ msc->write_bh.b_private = msc; ++ msc->write_bh.b_page = alloc_page(GFP_NOIO); ++ ++ msc->command_state = MSC_READY; ++ msc->io_state = MSC_INACTIVE; ++ ++ msc_trace_init("msctrace", &msc_private); ++ make_crc_table(); ++ ++ TRACE_MSG1(MSC,"PAGE_SHIFT: %x", PAGE_SHIFT); ++ TRACE_MSG1(MSC,"PAGE_SIZE: %x", PAGE_SIZE); ++ TRACE_MSG1(MSC,"PAGE_SIZE: %d", PAGE_SIZE); ++ ++ // register as usb function driver ++ THROW_IF (usbd_register_function (&function_driver, NULL), error); ++ msc->usb_driver_registered++; ++ ++ CATCH(error) { ++ if (msc->usb_driver_registered) { ++ usbd_deregister_function (&function_driver); ++ msc->usb_driver_registered = 0; ++ } ++ return -EINVAL; ++ } ++ return 0; ++} ++ ++/* msc_modexit - module cleanup ++ */ ++static void msc_modexit (void) ++{ ++ struct msc_private *msc = &msc_private; ++ ++ if (msc->bdev) { ++ blkdev_put(msc->bdev, BDEV_RAW); ++ } ++ // XXX this should be a wait for read_bh/write_bh to go to NULL ++ while (msc->read_bh.b_data) { ++ printk(KERN_INFO"%s: sleeping on read bh\n", __FUNCTION__); ++ sleep_on_timeout(&msc->msc_wq, 20); ++ } ++ ++ while (msc->write_bh.b_data) { ++ printk(KERN_INFO"%s: sleeping on read bh\n", __FUNCTION__); ++ sleep_on_timeout(&msc->msc_wq, 20); ++ } ++ ++ printk(KERN_INFO"%s: freeing read bh\n", __FUNCTION__); ++ __free_page((void *)&msc->read_bh.b_page); ++ msc->read_bh.b_page = NULL; ++ ++ printk(KERN_INFO"%s: freeing write bh\n", __FUNCTION__); ++ __free_page((void *)&msc->write_bh.b_page); ++ msc->write_bh.b_page = NULL; ++ ++ if (msc->usb_driver_registered) { ++ usbd_deregister_function (&function_driver); ++ } ++ ++ msc_trace_exit("msctrace"); ++ free_crc_table(); ++} ++ ++ ++module_init (msc_modinit); ++module_exit (msc_modexit); +diff -uNr linux/drivers/no-otg/functions/msc/msc-scsi.h linux/drivers/otg/functions/msc/msc-scsi.h +--- linux/drivers/no-otg/functions/msc/msc-scsi.h 1970-01-01 01:00:00.000000000 +0100 ++++ linux/drivers/otg/functions/msc/msc-scsi.h 2006-09-01 21:41:28.000000000 +0200 +@@ -0,0 +1,795 @@ ++/* ++ * otg/msc_fd/msc_scsi.h - mass storage protocol library header ++ * ++ * Copyright(c) 2004, Belcarra ++ * ++ * Adapated from work: ++ * ++ * Copyright (c) 2003 Lineo Solutions, Inc. ++ * ++ * Written by Shunnosuke kabata ++ * ++ * 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. ++ * ++ */ ++ ++ ++/* ++ * ++ * Documents ++ * Universal Serial Bus Mass Storage Class - Specification Overview ++ * Universal Serial Bus Mass Storage Class - Bulk-Only Transport ++ * T10/1240-D - Information technology - Reduced Block Commands ++ * ++ * Notes ++ * ++ * 1. Reduced Block Command set C.f. Table 2 RBC ++ * ++ * Command Support ++ * Name OpCode Fixed Removable Reference ++ * Format Unit 04h O O RBC ++ * Inquiry 12h M M SPC-2 ++ * Mode Select (6) 15h M M SPC-2 ++ * Mode Sense (6) 1Ah M M SPC-2 ++ * Perisstent Reserve In 5Eh O O SPC-2 ++ * Persistent Reserve Out 5Fh O O SPC-2 ++ * Prevent/Allow Medium Removal 1Eh N/A M SPC-2 ++ * Read (10) 28h M M RBC ++ * Read Capacity 25h M M RBC ++ * Reelase (6) 17h O O SPC-2 ++ * Request Sense 03h O O SPC-2 ++ * Reserve (6) 16h O O SPC-2 ++ * Start Stop Unit 1Bh M M RBC ++ * Synchronize Cache 35h O O RBC ++ * Test Unit Ready 00h M M SPC-2 ++ * Verify (10) 2Fh M M RBC ++ * Write (10) 2Ah M M RBC ++ * Write Buffer 3Bh M O SPC-2 ++ * ++ * 2. Other commands seen? ++ * Sh MV FS ++ * SCSI_REZERO_UNIT 0x01 no ++ * SCSI_READ_6 0x08 yes ++ * SCSI_WRITE_6 0x0a yes ++ * SCSI_SEND_DIAGNOSTIC 0x1d no yes ++ * SCSI_READ_FORMAT_CAPACITY 0x23 yes yes ++ * SCSI_WRITE_AND_VERIFY 0x2e no ++ * SCSI_SEEK_10 0x2b no ++ * SCSI_MODE_SELECT_10 0x55 no yes yes ++ * SCSI_READ_12 0xa8 no yes ++ * SCSI_WRITE_12 0xaa no yes ++ * ++ * ++ * 3. Status - C.f. Chapter 5 - RBC ++ * ++ * Check Condition 02h ++ * ++ * ++ * 4. Sense Keys - C.f. Chapter 5 - RBC ++ * ++ * Not Ready 02h ++ * Media Error 03h ++ * Illegal Request 05h ++ * Unit Attention 06h ++ * ++ * 5. ASC/ASCQ - C.f. Chapter 5 - RBC ++ * ++ * Logical Unit Not Ready 04h ++ * Logical Unit Not Ready, Format in Progress 04h,04h ++ * Invalid Command Operation Code 20h ++ * Invalide Field in CDB 24h ++ * Format Command Failed 31h,01h ++ * Status Notification / Power Management Class Event 38h,02h ++ * Status Notification / Media Class Event 38h,04h ++ * Status Notification / Device Busy Class Event 38h,06h ++ * Low Power Condition Active 5Eh,00 ++ * Power Condition Change to Active 5Eh,41h ++ * Power Condition Change to Idle 5Eh,42h ++ * Power Condition Change to Standby 5Eh,43h ++ * Power Condition Change to Sleep 5Eh,45h ++ * Power Condition Change to Device Control 5Eh,47h ++ * ++ * 6. ASCQ - C.f. Chapter 5 - RBC ++ * ++ * ++ * ++ * 7. Sense Keys C.f. Chapter 5 - RBC ++ * ++ * Command Status Sense Key ASC/ASCQ ++ * ++ * 5.1 Format Unit ++ * Format progress Check Condition Not Ready, Logical Unit Note Ready, Format in Progress ++ * Sueccsful completion Check Condition Unit Attention, Status Notification / Media Class Event ++ * Failure Check Condition Media error, Format Command Failed ++ * ++ * 5.2 Read (10) ++ * ++ * 5.3 Read Capacity ++ * No Media Check Condition Not Ready, Logical Unit Not Ready ++ * ++ * 5.4 Start Stop Unit ++ * Power Consumption Check Condition Illegal Request,Low Power Condition Active ++ * ++ * 5.5 Synchronize Cache Check Condition Illegal Request,Invalid Command Operation Code ++ * ++ * 5.6 Write (10 ++ * ++ * 5.7 Verify ++ * ++ * 5.8 Mode ++ * ++ * 6.1 Inquiry ++ * ++ * 6.2 Mode Select (6) ++ * Cannot Save Check Condition Illegal Request,Invalid Field in CDB ++ * ++ * 6.3 Mode Sense (6) ++ * ++ * 6.4 Prevent Allow Medium Removal ++ * ++ * 6.5 Request Sense ++ * ++ * 6.6 Test Unit Ready ++ * ++ * 6.7 Write Buffer ++ * ++ * 7.1 Unit Attention ++ * Power Contition change Check Condition Unit Attention, Power Condition Change Notification ++ * ++ * 7.4.1 Event Status Sense ++ * ++ * Power Management Event Check Condition Unit Attention, Event Status Notification / Power Managment ++ * Media Class Event Check Condition Unit Attention, Event Status Notification / Media Class ++ * Device Busy Event Check Condition Unit Attention, Event Status Notification / Device Busy Class ++ * ++ * 7.4.6 Removable Medium Device Initial Response ++ * Ready Check Condition Unit Attention, Event Status Notification / Media Class Event ++ * Power Check Condition Unit Attention, Event Status Notification / Power Management Class Event ++ */ ++ ++#ifndef _MSCSCSIPROTO_H_ ++#define _MSCSCSIPROTO_H_ ++ ++ ++/* ++ * Class Specific Requests - C.f. MSC BO Chapter 3 ++ */ ++ ++#define MSC_BULKONLY_RESET 0xff ++#define MSC_BULKONLY_GETMAXLUN 0xfe ++ ++ ++/* ++ * Class Code ++ */ ++ ++#define MASS_STORAGE_CLASS 0x08 ++ ++/* ++ * MSC - Specification Overview ++ * ++ * SubClass Codes - C.f MSC Table 2.1 ++ */ ++ ++#define MASS_STORAGE_SUBCLASS_RBC 0x01 ++#define MASS_STORAGE_SUBCLASS_SFF8020I 0x02 ++#define MASS_STORAGE_SUBCLASS_QIC157 0x03 ++#define MASS_STORAGE_SUBCLASS_UFI 0x04 ++#define MASS_STORAGE_SUBCLASS_SFF8070I 0x05 ++#define MASS_STORAGE_SUBCLASS_SCSI 0x06 ++ ++/* ++ * Protocol - C.f MSC Table 3.1 ++ */ ++ ++#define MASS_STORAGE_PROTO_CBI_WITH_COMP 0x00 ++#define MASS_STORAGE_PROTO_CBI_NO_COMP 0x01 ++#define MASS_STORAGE_PROTO_BULK_ONLY 0x50 ++ ++/* ++ * SCSI Command ++*/ ++ ++#define SCSI_TEST_UNIT_READY 0x00 ++#define SCSI_REQUEST_SENSE 0x03 ++#define SCSI_FORMAT_UNIT 0x04 ++#define SCSI_INQUIRY 0x12 ++#define SCSI_MODE_SELECT 0x15 // aka MODE_SELECT_6 ++#define SCSI_MODE_SENSE 0x1a // aka MODE_SENSE_6 ++#define SCSI_START_STOP 0x1b ++#define SCSI_PREVENT_ALLOW_MEDIA_REMOVAL 0x1e ++#define SCSI_READ_FORMAT_CAPACITY 0x23 ++#define SCSI_READ_CAPACITY 0x25 ++#define SCSI_READ_10 0x28 ++#define SCSI_WRITE_10 0x2a ++#define SCSI_VERIFY 0x2f ++ ++#define SCSI_READ_6 0x08 ++#define SCSI_WRITE_6 0x0a ++#define SCSI_RESERVE 0x16 ++#define SCSI_RELEASE 0x17 ++#define SCSI_SEND_DIAGNOSTIC 0x1d ++#define SCSI_SYNCHRONIZE_CACHE 0x35 ++#define SCSI_MODE_SENSE_10 0x5a ++ ++#define SCSI_REZERO_UNIT 0x01 ++#define SCSI_REASSIGN_BLOCKS 0x07 ++#define SCSI_COPY 0x18 ++#define SCSI_RECEIVE_DIAGNOSTIC_RESULTS 0x1c ++#define SCSI_WRITE_AND_VERIFY 0x2e ++#define SCSI_PREFETCH 0x34 ++#define SCSI_READ_DEFECT_DATA 0x37 ++#define SCSI_COMPARE 0x39 ++#define SCSI_COPY_AND_VERIFY 0x3a ++#define SCSI_WRITE_BUFFER 0x3b ++#define SCSI_READ_BUFFER 0x3c ++#define SCSI_READ_LONG 0x3e ++#define SCSI_WRITE_LONG 0x3f ++#define SCSI_CHANGE_DEFINITION 0x40 ++#define SCSI_WRITE_SAME 0x41 ++#define SCSI_LOG_SELECT 0x4c ++#define SCSI_LOG_SENSE 0x4d ++#define SCSI_XD_WRITE 0x50 ++#define SCSI_XP_WRITE 0x51 ++#define SCSI_XD_READ 0x52 ++#define SCSI_MODE_SELECT_10 0x55 ++#define SCSI_RESERVE_10 0x56 ++#define SCSI_RELEASE_10 0x57 ++#define SCSI_MODE_SELECT_10 0x55 ++#define SCSI_XD_WRITE_EXTENDED 0x80 ++#define SCSI_REBUILD 0x81 ++#define SCSI_REGENERATE 0x82 ++ ++#define SCSI_SEEK_10 0x2b ++#define SCSI_WRITE_AND_VERIFY 0x2e ++#define SCSI_WRITE_12 0xaa ++#define SCSI_READ_12 0xa8 ++ ++/* ++ * Private ++ */ ++#define SCSI_PRIVATE_PCS 0xff ++ ++/* ++ * SCSI Command Parameter ++ */ ++ ++#define CBW_SIGNATURE 0x43425355 /* USBC */ ++#define CSW_SIGNATURE 0x53425355 /* USBS */ ++ ++#define PRODUCT_REVISION_LEVEL "1.00" ++ ++/* ++ * Command Block Status Values - C.f MSC BO Table 5.3 ++ */ ++ ++#define USB_MSC_PASSED 0x00 // good ++#define USB_MSC_FAILED 0x01 // bad ++#define USB_MSC_PHASE_ERROR 0x02 // we want to be reset ++ ++ ++ ++ ++/* ++ * SCSI Sense ++ * SenseKey ++ * AdditionalSenseCode ++ * SenseCodeQualifier ++ */ ++ ++#define SCSI_ERROR_CURRENT 0x70 ++#define SCSI_ERROR_DEFERRED 0x07 ++ ++/* ++ * SCSI Sense Keys ++ */ ++ ++#define SK_NO_SENSE 0x00 ++#define SK_RECOVERED_ERROR 0x01 ++#define SK_NOT_READY 0x02 ++#define SK_MEDIA_ERROR 0x03 ++#define SK_HARDWARE_ERROR 0x04 ++#define SK_ILLEGAL_REQUEST 0x05 ++#define SK_UNIT_ATTENTION 0x06 ++#define SK_DATA_PROTECT 0x07 ++#define SK_BLANK_CHECK 0x08 ++#define SK_COPY_ABORTED 0x0a ++#define SK_ABORTED_COMMAND 0x0b ++#define SK_VOLUME_OVERFLOW 0x0d ++#define SK_MISCOMPARE 0x0e ++ ++/* ++ * 5. ASC/ASCQ - C.f. Chapter 5 - RBC ++ */ ++ ++#define SK(SenseKey,ASC,ASCQ) ((SenseKey<<16) | (ASC << 8) | (ASCQ)) ++ ++#define SCSI_SENSEKEY_NO_SENSE SK(SK_NO_SENSE, 0x00,0x00) // 0x000000 ++ ++#define SCSI_FAILURE_PREDICTION_THRESHOLD_EXCEEDED SK(SK_RECOVERED_ERROR, 0x5d,0x00) // 0x015d00 ++ ++#define SCSI_SENSEKEY_LOGICAL_UNIT_NOT_READY SK(SK_NOT_READY, 0x04,0x00) // 0x020400 ++#define SCSI_SENSEKEY_FORMAT_IN_PROGRESS SK(SK_NOT_READY, 0x04,0x04) // 0x020404 ++#define SCSI_SENSEKEY_MEDIA_NOT_PRESENT SK(SK_NOT_READY, 0x3a,0x00) // 0x023a00 ++ ++#define SCSI_SENSEKEY_WRITE_ERROR SK(SK_MEDIA_ERROR, 0x0c,0x02) // 0x030c02 ++#define SCSI_SENSEKEY_UNRECOVERED_READ_ERROR SK(SK_MEDIA_ERROR, 0x11,0x00) // 0x031100 ++#define SCSI_FORMAT_COMMAND_FAILED SK(SK_MEDIA_ERROR, 0x31,0x01) // 0x033101 ++ ++#define SCSI_SENSEKEY_COMMUNICATION_FAILURE SK(SK_HARDWARE_ERROR, 0x08,0x00) // 0x040800 ++ ++#define SCSI_SENSEKEY_INVALID_COMMAND SK(SK_ILLEGAL_REQUEST, 0x20,0x00) // 0x052000 ++#define SCSI_SENSEKEY_BLOCK_ADDRESS_OUT_OF_RANGE SK(SK_ILLEGAL_REQUEST, 0x21,0x00) // 0x052100 ++#define SCSI_SENSEKEY_INVALID_FIELD_IN_CDB SK(SK_ILLEGAL_REQUEST, 0x24,0x00) // 0x052400 ++#define SCSI_SENSEKEY_LOGICAL_UNIT_NOT_SUPPORTED SK(SK_ILLEGAL_REQUEST, 0x25,0x00) // 0x052500 ++#define SCSI_SENSEKEY_SAVING_PARAMETERS_NOT_SUPPORTED SK(SK_ILLEGAL_REQUEST, 0x39,0x00) // 0x053900 ++#define SCSI_MEDIA_REMOVAL_PREVENTED SK(SK_ILLEGAL_REQUEST, 0x53,0x02) // 0x055302 ++ ++#define SCSI_SENSEKEY_NOT_READY_TO_READY_CHANGE SK(SK_UNIT_ATTENTION, 0x28,0x00) // 0x062800 ++#define SCSI_SENSEKEY_RESET_OCCURRED SK(SK_UNIT_ATTENTION, 0x29,0x00) // 0x062900 ++ ++#define SCSI_SENSEKEY_STATUS_NOTIFICATION_POWER_CLASS SK(SK_UNIT_ATTENTION, 0x38,0x02) // 0x063802 ++#define SCSI_SENSEKEY_STATUS_NOTIFICATION_MEDIA_CLASS SK(SK_UNIT_ATTENTION, 0x38,0x04) // 0x063804 ++#define SCSI_SENSEKEY_STATUS_NOTIFICATION_DEVICE_BUSY SK(SK_UNIT_ATTENTION, 0x38,0x06) // 0x063806 ++ ++#define SCSI_SENSEKEY_LOW_POWER_CONDITION_ACTIVE SK(SK_UNIT_ATTENTION, 0x5e,0x00) // 0x065e00 ++#define SCSI_SENSEKEY_POWER_CONDITION_CHANGE_TO_ACTIVE SK(SK_UNIT_ATTENTION, 0x5e,0x41) // 0x065e41 ++#define SCSI_SENSEKEY_POWER_CONDITION_CHANGE_TO_IDLE SK(SK_UNIT_ATTENTION, 0x5e,0x42) // 0x065e42 ++#define SCSI_SENSEKEY_POWER_CONDITION_CHANGE_TO_STANDBY SK(SK_UNIT_ATTENTION, 0x5e,0x43) // 0x065e43 ++#define SCSI_SENSEKEY_POWER_CONDITION_CHANGE_TO_SLEEP SK(SK_UNIT_ATTENTION, 0x5e,0x45) // 0x065e45 ++#define SCSI_SENSEKEY_POWER_CONDITION_CHANGE_TO_DEVICE SK(SK_UNIT_ATTENTION, 0x5e,0x47) // 0x065e47 ++ ++ ++#define SCSI_SENSEKEY_WRITE_PROTECTED SK(SK_DATA_PROTECT, 0x27,0x00) // 0x072700 ++ ++ ++/* ++ * Mode Page Code and Page Control ++ */ ++#define SCSI_MODEPAGE_CONTROL_CURRENT 0x0 ++#define SCSI_MODEPAGE_CONTROL_CHANGEABLE 0x1 ++#define SCSI_MODEPAGE_CONTROL_DEFAULT 0x2 ++#define SCSI_MODEPAGE_CONTROL_SAVED 0x3 ++ ++#define SCSI_MODEPAGE_UNIT_ATTENTION 0x00 ++#define SCSI_MODEPAGE_ERROR_RECOVERY 0x01 ++#define SCSI_MODEPAGE_DISCONNNECT_RECONNECT 0x02 ++#define SCSI_MODEPAGE_FORMAT 0x03 ++#define SCSI_MODEPAGE_RIGID_DRIVE_GEOMETRY 0x04 ++#define SCSI_MODEPAGE_FLEXIBLE_DISK_PAGE 0x05 // check ++#define SCSI_MODEPAGE_VERIFY_ERROR_RECOVERY 0x07 ++#define SCSI_MODEPAGE_CACHING 0x08 ++#define SCSI_MODEPAGE_CONTROL_MODE 0x0a ++#define SCSI_MODEPAGE_NOTCH_AND_PARTITION 0x0c ++#define SCSI_MODEPAGE_POWER_CONDITION 0x0d ++#define SCSI_MODEPAGE_XOR 0x10 ++#define SCSI_MODEPAGE_CONTROL_MODE_ALIAS 0x1a ++#define SCSI_MODEPAGE_REMOVABLE_BLOCK_ACCESS 0x1b// check ++#define SCSI_MODEPAGE_INFORMATION_EXCEPTIONS 0x1c ++#define SCSI_MODEPAGE_ALL_SUPPORTED 0x3f ++ ++ ++ ++/* ++ * Command Block Wrapper / Command Status Wrapper ++ */ ++ ++/* ++ * Command Block Wrapper ++ */ ++typedef struct{ ++ unsigned long dCBWSignature; ++ unsigned long dCBWTag; ++ unsigned long dCBWDataTransferLength; ++ u8 bmCBWFlags; ++ u8 bCBWLUN:4, ++ Reserved:4; ++ u8 bCBWCBLength:5, ++ Reserved2:3; ++ u8 CBWCB[16]; ++} __attribute__((packed)) COMMAND_BLOCK_WRAPPER; ++ ++/* ++ * Command Status Wrapper ++ */ ++typedef struct{ ++ unsigned long dCSWSignature; ++ unsigned long dCSWTag; ++ unsigned long dCSWDataResidue; ++ u8 bCSWStatus; ++} __attribute__((packed)) COMMAND_STATUS_WRAPPER; ++ ++/* ++ * SCSI Command ++ */ ++ ++/* ++ * INQUIRY ++ */ ++ ++typedef struct{ ++ u8 OperationCode; ++ u8 EnableVPD:1, ++ Reserved1:4, ++ LogicalUnitNumber:3; ++ u8 PageCode; ++ u8 Reserved2; ++ u8 AllocationLength; ++ u8 Reserved3; ++ u8 Reserved4; ++ u8 Reserved5; ++ u8 Reserved6; ++ u8 Reserved7; ++ u8 Reserved8; ++ u8 Reserved9; ++} __attribute__((packed)) SCSI_INQUIRY_COMMAND; ++ ++typedef struct{ ++ u8 PeripheralDeviceType:5, ++ PeripheralQaulifier:3; ++ u8 Reserved2:7, ++ RMB:1; ++ u8 ANSIVersion:3, ++ ECMAVersion:3, ++ ISOVersion:2; ++ u8 ResponseDataFormat:4, ++ Reserved3:4; ++ u8 AdditionalLength; ++ u8 Reserved4; ++ u8 Reserved5; ++ u8 Reserved6; ++ u8 VendorInformation[8]; ++ u8 ProductIdentification[16]; ++ u8 ProductRevisionLevel[4]; ++} __attribute__((packed)) SCSI_INQUIRY_DATA; ++ ++/* ++ * READ FORMAT CAPACITY ++ */ ++ ++typedef struct{ ++ u8 OperationCode; ++ u8 Reserved1:5, ++ LogicalUnitNumber:3; ++ u8 Reserved2; ++ u8 Reserved3; ++ u8 Reserved4; ++ u8 Reserved5; ++ u8 Reserved6; ++ u16 AllocationLength; ++ u8 Reserved7; ++ u8 Reserved8; ++ u8 Reserved9; ++} __attribute__((packed)) SCSI_READ_FORMAT_CAPACITY_COMMAND; ++ ++typedef struct{ ++ struct{ ++ u8 Reserved1; ++ u8 Reserved2; ++ u8 Reserved3; ++ u8 CapacityListLength; ++ } __attribute__((packed)) CapacityListHeader; ++ ++ struct{ ++ u32 NumberofBlocks; ++ u8 DescriptorCode:2, ++ Reserved1:6; ++ u8 BlockLength[3]; ++ } __attribute__((packed)) CurrentMaximumCapacityDescriptor; ++ ++} __attribute__((packed)) SCSI_READ_FORMAT_CAPACITY_DATA; ++ ++/* ++ * READ FORMAT CAPACITY ++ */ ++ ++typedef struct{ ++ u8 OperationCode; ++ u8 RelAdr:1, ++ Reserved1:4, ++ LogicalUnitNumber:3; ++ u32 LogicalBlockAddress; ++ u8 Reserved2; ++ u8 Reserved3; ++ u8 PMI:1, ++ Reserved4:7; ++ u8 Reserved5; ++ u8 Reserved6; ++ u8 Reserved7; ++} __attribute__((packed)) SCSI_READ_CAPACITY_COMMAND; ++ ++typedef struct{ ++ u32 LastLogicalBlockAddress; ++ u32 BlockLengthInBytes; ++} __attribute__((packed)) SCSI_READ_CAPACITY_DATA; ++ ++/* ++ * REQUEST SENSE ++ */ ++ ++typedef struct{ ++ u8 OperationCode; ++ u8 Reserved1:5, ++ LogicalUnitNumber:3; ++ u8 Reserved2; ++ u8 Reserved3; ++ u8 AllocationLength; ++ u8 Reserved4; ++ u8 Reserved5; ++ u8 Reserved6; ++ u8 Reserved7; ++ u8 Reserved8; ++ u8 Reserved9; ++ u8 Reserved10; ++} __attribute__((packed)) SCSI_REQUEST_SENSE_COMMAND; ++ ++typedef struct{ ++ u8 ErrorCode:7, ++ Valid:1; ++ u8 Reserved1; ++ u8 SenseKey:4, ++ Reserved2:4; ++ u32 Information; ++ u8 AdditionalSenseLength; ++ u8 Reserved3[4]; ++ u8 AdditionalSenseCode; ++ u8 AdditionalSenseCodeQualifier; ++ u8 Reserved4; ++ u8 Reserved5[3]; ++} __attribute__((packed)) SCSI_REQUEST_SENSE_DATA; ++ ++/* ++ * READ(10) ++ */ ++ ++typedef struct{ ++ u8 OperationCode; ++ u8 RelAdr:1, ++ Reserved1:2, ++ FUA:1, ++ DPO:1, ++ LogicalUnitNumber:3; ++ u32 LogicalBlockAddress; ++ u8 Reserved2; ++ u16 TransferLength; ++ u8 Reserved3; ++ u8 Reserved4; ++ u8 Reserved5; ++} __attribute__((packed)) SCSI_READ_10_COMMAND; ++ ++/* ++ * MODE SENSE ++ */ ++typedef struct{ ++ u8 OperationCode; ++ u8 Reserved1:3, ++ DBD:1, ++ Reserved2:1, ++ LogicalUnitNumber:3; ++ u8 PageCode:6, ++ PageControl:2; // PC ++ u8 Reserved3; ++ u8 Reserved4; ++ u8 Reserved5; ++ u8 Reserved6; ++ u16 ParameterListLength; ++ u8 Reserved7; ++ u8 Reserved8; ++ u8 Reserved9; ++} __attribute__((packed)) SCSI_MODE_SENSE_COMMAND; ++ ++typedef struct{ ++ u8 ModeDataLength; ++ u8 MediumTypeCode; ++ u8 Reserved1:4, ++ DPOFUA:1, ++ Reserved2:2, ++ WriteProtect:1; ++ u8 Reserved3; ++} __attribute__((packed)) MODE_PARAMETER_HEADER; ++ ++typedef struct{ ++ u8 PageCode:6, ++ Reserved1:1, ++ PS:1; ++ u8 PageLength; ++ u8 DCR:1, ++ Reserved2:1, ++ PER:1, ++ Reserved3:1, ++ RC:1, ++ Reserved4:1, ++ Reserved5:1, ++ AWRE:1; ++ u8 ReadRetryCount; ++ u8 Reserved6[4]; ++ u8 WriteRetryCount; ++ u8 Reserved7[3]; ++} __attribute__((packed)) READ_WRITE_ERROR_RECOVERY_PAGE; ++ ++typedef struct{ ++ u8 PageCode:6, ++ Reserved1:1, ++ PS:1; ++ u8 PageLength; ++ u16 TransferRate; ++ u8 NumberofHeads; ++ u8 SectorsperTrack; ++ u16 DataBytesperSector; ++ u16 NumberofCylinders; ++ u8 Reserved2[9]; ++ u8 MotorOnDelay; ++ u8 MotorOffDelay; ++ u8 Reserved3[7]; ++ u16 MediumRotationRate; ++ u8 Reserved4; ++ u8 Reserved5; ++} __attribute__((packed)) FLEXIBLE_DISK_PAGE; ++ ++typedef struct{ ++ u8 PageCode:6, ++ Reserved1:1, ++ PS:1; ++ u8 PageLength; ++ u8 Reserved2:6, ++ SRFP:1, ++ SFLP:1; ++ u8 TLUN:3, ++ Reserved3:3, ++ SML:1, ++ NCD:1; ++ u8 Reserved4[8]; ++} __attribute__((packed)) REMOVABLE_BLOCK_ACCESS_CAPABILITIES_PAGE; ++ ++typedef struct{ ++ u8 PageCode:6, ++ Reserved1:1, ++ PS:1; ++ u8 PageLength; ++ u8 Reserved2; ++ u8 InactivityTimeMultiplier:4, ++ Reserved3:4; ++ u8 SWPP:1, ++ DISP:1, ++ Reserved4:6; ++ u8 Reserved5; ++ u8 Reserved6; ++ u8 Reserved7; ++} __attribute__((packed)) TIMER_AND_PROTECT_PAGE; ++ ++typedef struct{ ++ READ_WRITE_ERROR_RECOVERY_PAGE ReadWriteErrorRecoveryPage; ++ FLEXIBLE_DISK_PAGE FlexibleDiskPage; ++ REMOVABLE_BLOCK_ACCESS_CAPABILITIES_PAGE RemovableBlockAccessCapabilitiesPage; ++ TIMER_AND_PROTECT_PAGE TimerAndProtectPage; ++} __attribute__((packed)) MODE_ALL_PAGES; ++ ++typedef struct{ ++ MODE_PARAMETER_HEADER ModeParameterHeader; ++ union{ ++ READ_WRITE_ERROR_RECOVERY_PAGE ReadWriteErrorRecoveryPage; ++ FLEXIBLE_DISK_PAGE FlexibleDiskPage; ++ REMOVABLE_BLOCK_ACCESS_CAPABILITIES_PAGE RemovableBlockAccessCapabilitiesPage; ++ TIMER_AND_PROTECT_PAGE TimerAndProtectPage; ++ MODE_ALL_PAGES ModeAllPages; ++ } __attribute__((packed)) ModePages; ++} __attribute__((packed)) SCSI_MODE_SENSE_DATA; ++ ++/* ++ * TEST UNIT READY ++ */ ++ ++typedef struct{ ++ u8 OperationCode; ++ u8 Reserved1:5, ++ LogicalUnitNumber:3; ++ u8 Reserved2; ++ u8 Reserved3; ++ u8 Reserved4; ++ u8 Reserved5; ++ u8 Reserved6; ++ u8 Reserved7; ++ u8 Reserved8; ++ u8 Reserved9; ++ u8 Reserved10; ++ u8 Reserved11; ++} __attribute__((packed)) SCSI_TEST_UNIT_READY_COMMAND; ++ ++/* ++ * PREVENT-ALLOW MEDIA REMOVAL ++ */ ++ ++typedef struct{ ++ u8 OperationCode; ++ u8 Reserved1:5, ++ LogicalUnitNumber:3; ++ u8 Reserved2; ++ u8 Reserved3; ++ u8 Prevent:2, ++ Reserved4:6; ++ u8 Reserved5; ++ u8 Reserved6; ++ u8 Reserved7; ++ u8 Reserved8; ++ u8 Reserved9; ++ u8 Reserved10; ++ u8 Reserved11; ++} __attribute__((packed)) SCSI_PREVENT_ALLOW_MEDIA_REMOVAL_COMMAND; ++ ++/* ++ * START-STOP UNIT ++ */ ++ ++typedef struct{ ++ u8 OperationCode; ++ u8 IMMED:1, ++ Reserved1:4, ++ LogicalUnitNumber:3; ++ u8 Reserved2; ++ u8 Reserved3; ++ u8 Start:1, ++ LoEj:1, ++ Reserved4:2, ++ PowerConditions:4; ++ u8 Reserved5; ++ u8 Reserved6; ++ u8 Reserved7; ++ u8 Reserved8; ++ u8 Reserved9; ++ u8 Reserved10; ++ u8 Reserved11; ++} __attribute__((packed)) SCSI_START_STOP_COMMAND; ++ ++/* ++ * WRITE(10) ++ */ ++ ++typedef struct{ ++ u8 OperationCode; ++ u8 RelAdr:1, ++ Reserved1:2, ++ FUA:1, ++ DPO:1, ++ LogicalUnitNumber:3; ++ u32 LogicalBlockAddress; ++ u8 Reserved2; ++ u16 TransferLength; ++ u8 Reserved3; ++ u8 Reserved4; ++ u8 Reserved5; ++} __attribute__((packed)) SCSI_WRITE_10_COMMAND; ++ ++/* ++ * VERIFY ++ */ ++ ++typedef struct{ ++ u8 OperationCode; ++ u8 RelAdr:1, ++ ByteChk:1, ++ Reserved1:1, ++ Reserved2:1, ++ DPO:1, ++ LogicalUnitNumber:3; ++ u32 LogicalBlockAddress; ++ u8 Reserved3; ++ u16 VerificationLength; ++ u8 Reserved4; ++ u8 Reserved5; ++ u8 Reserved6; ++} __attribute__((packed)) SCSI_VERIFY_COMMAND; ++ ++#endif /* _MSCSCSIPROTO_H_ */ ++ +diff -uNr linux/drivers/no-otg/functions/msc/msc.h linux/drivers/otg/functions/msc/msc.h +--- linux/drivers/no-otg/functions/msc/msc.h 1970-01-01 01:00:00.000000000 +0100 ++++ linux/drivers/otg/functions/msc/msc.h 2006-09-01 21:41:28.000000000 +0200 +@@ -0,0 +1,141 @@ ++/* ++ * otg/function/msc/msc.h - Mass Storage Class ++ * ++ * Copyright (c) 2003, 2004 Belcarra ++ * ++ * By: ++ * Stuart Lynne <sl@belcarra.com> ++ * Bruce Balden <balden@belcarra.com> ++ * ++ */ ++/*! ++ * @defgroup MSCFunction Mass Storage ++ * @ingroup functiongroup ++ */ ++/*! ++ * @file otg/functions/msc/msc.h ++ * @brief Mass Storage Driver private defines ++ * ++ * ++ * @ingroup MSCFunction ++ */ ++ ++#ifndef MSC_H ++#define MSC_H 1 ++ ++extern otg_tag_t msc_fd_trace_tag; ++#define MSC msc_fd_trace_tag ++ ++/* ++ * Command/Data/Status Flow ++ * C.f. 5 - Figure 1 ++ */ ++ ++typedef enum msc_state { ++ MSC_READY, ++ MSC_DATA_OUT_WRITE, ++ MSC_DATA_OUT_WRITE_FINISHED, ++ MSC_DATA_IN_READ, ++ MSC_DATA_IN_READ_FINISHED, ++ MSC_STATUS, ++ MSC_QUERY, ++ MSC_PHASE_ERROR, ++ MSC_UNKNOWN ++} msc_state_t; ++ ++ ++/* ++ * Device Transfer state ++ * C.F. Table 6.1 ++ */ ++ ++typedef enum msc_device_state { ++ MSC_DEVICE_DN, // The device intends to transfer no data ++ MSC_DEVICE_DI, // The device intends to send data to the host ++ MSC_DEVICE_DO, // The device intents to received data from the host ++} msc_device_state_t; ++ ++#define MSC_INACTIVE 0x0000 ++#define MSC_BLOCKIO_PENDING 0x0001 ++#define MSC_BLOCKIO_FINISHED 0x0002 ++#define MSC_RECV_PENDING 0x0010 ++#define MSC_RECV_FINISHED 0x0020 ++#define MSC_SEND_PENDING 0x0040 ++#define MSC_SEND_FINISHED 0x0080 ++#define MSC_IOCTL_WAITING 0x0100 // there is an ioctl call waiting for I/O completion ++#define MSC_ABORT_IO 0x0200 // please abort current i/o ++ ++#if 0 ++struct SPC_inquiry_cdb { ++ u8 OperationCode; /* 12H */ ++ u8 EnableVPD:1; ++ u8 CmdSupportData:1; ++ u8 Reserved0:6; ++ u8 PageCode; ++ u8 Reserved1; ++ u8 AllocationLen; ++ u8 Control; ++} __attribute__((packed)); ++#endif ++ ++struct msc_private { ++ struct usbd_function_instance *function; ++ ++ int usb_driver_registered; // non-zero if usb function registered ++ unsigned char connected; // non-zero if connected to host (configured) ++ ++ struct usbd_urb *rcv_urb_finished; ++ ++ struct buffer_head read_bh; ++ struct buffer_head write_bh; ++ ++ u16 read_pending; ++ u16 write_pending; ++ ++ msc_device_state_t device_state; ++ msc_state_t command_state; // current command state ++ u16 io_state; // current IO state ++ ++ COMMAND_BLOCK_WRAPPER command; ++ ++ u32 lba; // next lba to read/write from ++ u32 transfer_blocks; ++ u32 TransferLength_in_blocks; // amount of transfer remaining ++ u32 TransferLength_in_bytes; // amount of transfer remaining ++ u32 data_transferred_in_bytes; // amount of data actually transferred ++ ++ int major; ++ int minor; ++ kdev_t dev; ++ struct block_device *bdev; ++ u32 block_size; ++ u32 capacity; ++ u32 max_blocks; ++ ++ int uptodate; ++ ++ wait_queue_head_t msc_wq; ++ wait_queue_head_t ioctl_wq; ++ ++ u32 status; ++ u32 block_dev_state; ++ u32 sensedata; ++ u32 info; ++}; ++ ++ ++/* ++ * MSC Configuration ++ * ++ * Endpoint, Class, Interface, Configuration and Device descriptors/descriptions ++ */ ++ ++#define BULK_OUT 0x00 ++#define BULK_IN 0x01 ++#define ENDPOINTS 0x02 ++ ++extern struct usbd_function_operations function_ops; ++extern struct usbd_function_driver function_driver; ++ ++ ++#endif +diff -uNr linux/drivers/no-otg/functions/msc/scripts/test1 linux/drivers/otg/functions/msc/scripts/test1 +--- linux/drivers/no-otg/functions/msc/scripts/test1 1970-01-01 01:00:00.000000000 +0100 ++++ linux/drivers/otg/functions/msc/scripts/test1 2006-09-01 21:41:28.000000000 +0200 +@@ -0,0 +1,37 @@ ++#!/bin/sh ++# ++# Basic Start/Stop test ++# ++# ++# 1. Wait for connection ++# 2. loop making block device available for 10 second intervals ++# 3. exit if disconnected ++# ++ ++MAJOR=7 ++MINOR=0 ++ ++TIMEOUT=5 ++ ++set -x ++ ++# wait for connection ++msc_check connect ++ ++while sleep 1 ++do ++ ++ # make block device available ++ msc_check start $MAJOR $MINOR ++ ++ sleep $TIMEOUT ++ ++ # make block device un-available ++ msc_check stop ++ ++ sleep $TIMEOUT ++ ++ msc_check connected || break ++ ++done ++ +diff -uNr linux/drivers/no-otg/functions/msc/scripts/test2 linux/drivers/otg/functions/msc/scripts/test2 +--- linux/drivers/no-otg/functions/msc/scripts/test2 1970-01-01 01:00:00.000000000 +0100 ++++ linux/drivers/otg/functions/msc/scripts/test2 2006-09-01 21:41:28.000000000 +0200 +@@ -0,0 +1,35 @@ ++#!/bin/sh ++# ++# Basic Start/Stop test on connect/disconnect. ++# ++# Loop ++# 1. wait for connection ++# 2. making block device available for 10 seconds ++# 3. wait for disconnect ++# 4. continue ++# ++ ++MAJOR=7 ++MINOR=0 ++ ++TIMEOUT=5 ++ ++set -x ++ ++while sleep 1 ++do ++ # wait for connection ++ msc_check connect ++ ++ # make block device available ++ msc_check start $MAJOR $MINOR ++ ++ sleep $TIMEOUT ++ ++ # make block device un-available ++ msc_check stop ++ ++ msc_check disconnect ++ ++done ++ +diff -uNr linux/drivers/no-otg/functions/msc/scripts/test3 linux/drivers/otg/functions/msc/scripts/test3 +--- linux/drivers/no-otg/functions/msc/scripts/test3 1970-01-01 01:00:00.000000000 +0100 ++++ linux/drivers/otg/functions/msc/scripts/test3 2006-09-01 21:41:28.000000000 +0200 +@@ -0,0 +1,46 @@ ++#!/bin/sh ++# ++# Force connect/disconnect with start/stop. ++# ++# 1. loop making block device available for 10 second intervals ++# 2. exit if disconnected ++# 3. start ++# 4. stop ++# 5. force disconnect ++# ++ ++MAJOR=7 ++MINOR=0 ++ ++TIMEOUT=5 ++ ++set -x ++ ++while sleep 1 ++do ++ ++ # bus request ++ otgd bus_req ++ ++ # wait for connection ++ msc_check connect ++ ++ # make block device available ++ msc_check start $MAJOR $MINOR ++ ++ sleep $TIMEOUT ++ ++ # make block device un-available ++ msc_check stop ++ ++ sleep $TIMEOUT ++ ++ # bus request/ ++ otgd bus_req/ ++ ++ msc_check disconnect ++ ++ sleep $TIMEOUT ++ ++done ++ +diff -uNr linux/drivers/no-otg/functions/msc/scripts/test4 linux/drivers/otg/functions/msc/scripts/test4 +--- linux/drivers/no-otg/functions/msc/scripts/test4 1970-01-01 01:00:00.000000000 +0100 ++++ linux/drivers/otg/functions/msc/scripts/test4 2006-09-01 21:41:28.000000000 +0200 +@@ -0,0 +1,47 @@ ++#!/bin/sh ++ ++# Force connect/disconnect without start/stop. ++# ++# ++# 1. loop making block device available for 10 second intervals ++# 2. exit if disconnected ++# 3. force disconnect ++# 4. force connect ++# ++ ++MAJOR=7 ++MINOR=0 ++ ++TIMEOUT=5 ++ ++set -x ++ ++# make block device available ++msc_check start $MAJOR $MINOR ++ ++ ++while sleep 1 ++do ++ ++ # bus request ++ otgd bus_req ++ ++ # wait for connection ++ msc_check connect ++ ++ sleep $TIMEOUT ++ ++ sleep $TIMEOUT ++ ++ # bus request/ ++ otgd bus_req/ ++ ++ msc_check disconnect ++ ++ sleep $TIMEOUT ++ ++done ++ ++# make block device un-available ++msc_check stop ++ +diff -uNr linux/drivers/no-otg/functions/msc/scripts/test5 linux/drivers/otg/functions/msc/scripts/test5 +--- linux/drivers/no-otg/functions/msc/scripts/test5 1970-01-01 01:00:00.000000000 +0100 ++++ linux/drivers/otg/functions/msc/scripts/test5 2006-09-01 21:41:28.000000000 +0200 +@@ -0,0 +1,47 @@ ++#!/bin/sh ++# ++# Force connect/disconnect with start/stop. ++# ++# 1. loop making block device available for 10 second intervals ++# 2. exit if disconnected ++# 3. start ++# 4. force disconnect ++# 5. stop ++# 6. force connect ++# ++ ++MAJOR=7 ++MINOR=0 ++ ++TIMEOUT=5 ++ ++set -x ++ ++while sleep 1 ++do ++ ++ # bus request ++ otgd bus_req ++ ++ # wait for connection ++ msc_check connect ++ ++ # make block device available ++ msc_check start $MAJOR $MINOR ++ ++ sleep $TIMEOUT ++ ++ # bus request/ ++ otgd bus_req/ ++ ++ # make block device un-available ++ msc_check stop ++ ++ sleep $TIMEOUT ++ ++ msc_check disconnect ++ ++ sleep $TIMEOUT ++ ++done ++ +diff -uNr linux/drivers/no-otg/functions/msc/trace.c linux/drivers/otg/functions/msc/trace.c +--- linux/drivers/no-otg/functions/msc/trace.c 1970-01-01 01:00:00.000000000 +0100 ++++ linux/drivers/otg/functions/msc/trace.c 2006-09-01 21:41:28.000000000 +0200 +@@ -0,0 +1,340 @@ ++/* ++ * otg/msc_fd/trace.c ++ * ++ * Copyright (c) 2004 Belcarra ++ * ++ * Adapted from earlier work: ++ * Copyright (c) 2002, 2003 Belcarra ++ * Copyright (c) 2002 Lineo ++ * ++ * By: ++ * Stuart Lynne <sl@belcarra.com>, ++ * Tom Rushworth <tbr@belcarra.com>, ++ * ++ */ ++ ++#include <linux/config.h> ++#include <linux/module.h> ++#include <linux/version.h> ++ ++#include <linux/kernel.h> ++#include <linux/slab.h> ++#include <linux/interrupt.h> ++#include <linux/pci.h> ++#include <linux/init.h> ++#include <linux/delay.h> ++ ++#include <linux/proc_fs.h> ++#include <linux/vmalloc.h> ++ ++#include <asm/atomic.h> ++#include <asm/io.h> ++ ++#include <linux/proc_fs.h> ++ ++#include <linux/netdevice.h> ++#include <linux/pci.h> ++#include <linux/cache.h> ++ ++ ++ ++#include <asm/uaccess.h> ++#include <asm/io.h> ++#include <asm/pgtable.h> ++#include <asm/pgalloc.h> ++ ++#include <usbp-chap9.h> ++#include <usbp-mem.h> ++#include <usbp-func.h> ++#include "msc-scsi.h" ++#include "msc.h" ++#include "trace.h" ++ ++ ++static struct msc_private *msc_private; ++int msc_trace_first; ++int msc_trace_next; ++msc_trace_t *msc_traces; ++ ++extern int msc_interrupts; ++ ++ ++#if defined(CONFIG_OTG_MSC_REGISTER_TRACE) && defined(CONFIG_PROC_FS) ++ ++msc_trace_t *MSC_TRACE_NEXT(msc_trace_types_t msc_trace_type) ++{ ++ msc_trace_t *p; ++ ++ p = msc_traces + msc_trace_next; ++ ++ if (msc_private) { ++ p->ticks = usbd_ticks(msc_private->function); ++ p->sofs = usbd_framenum(msc_private->function); ++ } ++ p->interrupts = msc_interrupts; ++ p->msc_trace_type = msc_trace_type; ++ ++ msc_trace_next++; ++ msc_trace_next = (msc_trace_next == TRACE_MAX) ? 0 : msc_trace_next; ++ ++ if (msc_trace_next == msc_trace_first) { ++ msc_trace_first++; ++ msc_trace_first = (msc_trace_first == TRACE_MAX) ? 0 : msc_trace_first; ++ } ++ ++ return p; ++} ++ ++/* Proc Filesystem *************************************************************************** */ ++ ++/* * ++ * msc_trace_proc_read - implement proc file system read. ++ * @file ++ * @buf ++ * @count ++ * @pos ++ * ++ * Standard proc file system read function. ++ */ ++static ssize_t msc_trace_proc_read (struct file *file, char *buf, size_t count, loff_t * pos) ++{ ++ unsigned long page; ++ int len = 0; ++ int index; ++ int oindex; ++ int previous; ++ ++ 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; ++ oindex = index = (*pos)++; ++ ++ if (index == 0) ++ len += sprintf ((char *) page + len, " Index Ints Ticks\n"); ++ ++ ++ index += msc_trace_first; ++ if (index >= TRACE_MAX) ++ index -= TRACE_MAX; ++ ++ previous = (index) ? (index - 1) : (TRACE_MAX - 1); ++ ++ ++ if ( ++ ((msc_trace_first < msc_trace_next) && (index >= msc_trace_first) && (index < msc_trace_next)) || ++ ((msc_trace_first > msc_trace_next) && ((index < msc_trace_next) || (index >= msc_trace_first))) ++ ) ++ { ++ ++ u64 ticks = 0; ++ ++ msc_trace_t *p = msc_traces + index; ++ unsigned char *cp; ++ unsigned int *ip; ++ int skip = 0; ++ ++ if (oindex > 0) { ++ msc_trace_t *o = msc_traces + previous; ++ ++ if (o->ticks) ++ ticks = (p->ticks > o->ticks) ? (p->ticks - o->ticks) : (o->ticks - p->ticks) ; ++ ++ if (o->interrupts != p->interrupts) ++ skip++; ++ ++ } ++ ++ //printk(KERN_INFO"index: %d interrupts: %d\n", index, p->interrupts); ++ len += sprintf ((char *) page + len, "%s%6d %8d ", skip?"\n":"", index, p->interrupts); ++ ++ if (ticks > 1024*1024) ++ len += sprintf ((char *) page + len, "%8dM ", ticks>>20); ++ else ++ len += sprintf ((char *) page + len, "%8d ", ticks); ++ ++ len += sprintf ((char *) page + len, "%6d ", (int)p->sofs); ++ ++ switch (p->msc_trace_type) { ++ case msc_trace_msg: ++ len += sprintf ((char *) page + len, " -- "); ++ len += sprintf ((char *) page + len, p->trace.msg.msg); ++ break; ++ ++ case msc_trace_w: ++ len += sprintf ((char *) page + len, " --> "); ++ len += sprintf ((char *) page + len, "[%8x] W %s", p->trace.msg32.val, p->trace.msg32.msg); ++ break; ++ ++ case msc_trace_r: ++ len += sprintf ((char *) page + len, "<-- "); ++ len += sprintf ((char *) page + len, "[%8x] R %s", p->trace.msg32.val, p->trace.msg32.msg); ++ break; ++ ++ case msc_trace_msg32: ++ len += sprintf ((char *) page + len, " -- "); ++ len += sprintf ((char *) page + len, p->trace.msg32.msg, p->trace.msg32.val); ++ break; ++ ++ case msc_trace_msg16: ++ len += sprintf ((char *) page + len, " -- "); ++ len += sprintf ((char *) page + len, p->trace.msg16.msg, p->trace.msg16.val0, p->trace.msg16.val1); ++ break; ++ ++ case msc_trace_msg8: ++ len += sprintf ((char *) page + len, " -- "); ++ len += sprintf ((char *) page + len, p->trace.msg8.msg, ++ p->trace.msg8.val0, p->trace.msg8.val1, p->trace.msg8.val2, p->trace.msg8.val3); ++ break; ++ ++ case msc_trace_setup: ++ cp = (unsigned char *)&p->trace.setup; ++ len += sprintf ((char *) page + len, ++ " -- request [%02x %02x %02x %02x %02x %02x %02x %02x]", ++ cp[0], cp[1], cp[2], cp[3], cp[4], cp[5], cp[6], cp[7]); ++ break; ++ ++ case msc_trace_recv: ++ case msc_trace_sent: ++ cp = (unsigned char *)&p->trace.sent; ++ len += sprintf ((char *) page + len, ++ "%s %s [%02x %02x %02x %02x %02x %02x %02x %02x]", ++ ( p->msc_trace_type == msc_trace_recv)?"<-- ":" -->", ++ ( p->msc_trace_type == msc_trace_recv)?"recv":"sent", ++ cp[0], cp[1], cp[2], cp[3], cp[4], cp[5], cp[6], cp[7]); ++ break; ++ case msc_trace_rlba: ++ ip = (unsigned int *)&p->trace.ints; ++ len += sprintf ((char *) page + len, ++ "%s %s [%8x %08x]", ++ "<-- ", "rlba", ip[0], ip[1]); ++ break; ++ case msc_trace_slba: ++ case msc_trace_tlba: ++ ip = (unsigned int *)&p->trace.ints; ++ len += sprintf ((char *) page + len, ++ "%s %s [%8x %08x]", " -->", ++ ( p->msc_trace_type == msc_trace_tlba)?"tlba":"slba", ++ ip[0], ip[1]); ++ break; ++ ++ case msc_trace_tag: ++ ip = (unsigned int *)&p->trace.ints; ++ len += sprintf ((char *) page + len, ++ "%s TAG: %8x FRAME: %03x", " -->", ++ ip[0], ip[1]); ++ break; ++ ++ case msc_trace_sense: ++ ip = (unsigned int *)&p->trace.ints; ++ len += sprintf ((char *) page + len, ++ "%s SENSE: %06x INFO: %08x", " -->", ++ ip[0], ip[1]); ++ break; ++ ++ case msc_trace_cbw: ++ len += sprintf ((char *) page + len, " --> "); ++ len += sprintf ((char *) page + len, "%s %02x", p->trace.msg32.msg, p->trace.msg32.val); ++ break; ++ } ++ len += sprintf ((char *) page + len, "\n"); ++ } ++ ++ if ((len > count) || (len == 0)) ++ len = -EINVAL; ++ else if (len > 0 && copy_to_user (buf, (char *) page, len)) ++ len = -EFAULT; ++ ++ free_page (page); ++ MOD_DEC_USE_COUNT; ++ return len; ++} ++ ++/* * ++ * msc_trace_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 msc_trace_proc_write (struct file *file, const char *buf, size_t count, loff_t * pos) ++{ ++ return count; ++} ++ ++static struct file_operations msc_trace_proc_operations_functions = { ++ read:msc_trace_proc_read, ++ write:msc_trace_proc_write, ++}; ++ ++ ++/** ++ * msc_trace_init ++ * ++ * Return non-zero if not successful. ++ */ ++int msc_trace_init (char *name, struct msc_private *msc) ++{ ++ printk(KERN_INFO"%s:\n", __FUNCTION__); ++ if (!(msc_traces = vmalloc(sizeof(msc_trace_t) * TRACE_MAX))) { ++ printk(KERN_ERR"%s: malloc failed %p %d\n", __FUNCTION__, msc_traces, sizeof(msc_trace_t) * TRACE_MAX); ++ return -EINVAL; ++ } ++ memset(msc_traces, 0, sizeof(msc_trace_t) * TRACE_MAX); ++ ++ { ++ struct proc_dir_entry *p; ++ ++ // create proc filesystem entries ++ if ((p = create_proc_entry (name, 0, 0)) == NULL) { ++ printk(KERN_INFO"BITRACE PROC FS failed\n"); ++ } ++ else { ++ p->proc_fops = &msc_trace_proc_operations_functions; ++ } ++ } ++ printk(KERN_INFO"%s: OK\n", __FUNCTION__); ++ msc_private = msc; ++ return 0; ++} ++ ++/** ++ * udc_release_io - release UDC io region ++ */ ++void msc_trace_exit (char *name) ++{ ++ msc_private = NULL; ++ { ++ unsigned long flags; ++ local_irq_save (flags); ++ remove_proc_entry (name, NULL); ++ if (msc_traces) { ++ msc_trace_t *p = msc_traces; ++ msc_traces = NULL; ++ vfree(p); ++ } ++ local_irq_restore (flags); ++ } ++} ++ ++ ++#else ++int msc_trace_init (void) ++{ ++ return 0; ++} ++ ++void msc_trace_exit (char *) ++{ ++ return; ++} ++#endif ++ +diff -uNr linux/drivers/no-otg/functions/msc/trace.h linux/drivers/otg/functions/msc/trace.h +--- linux/drivers/no-otg/functions/msc/trace.h 1970-01-01 01:00:00.000000000 +0100 ++++ linux/drivers/otg/functions/msc/trace.h 2006-09-01 21:41:28.000000000 +0200 +@@ -0,0 +1,303 @@ ++/* ++ * otg/msc_fd/trace.h ++ * ++ * Copyright (c) 2004 Belcarra ++ * ++ * Adapted from earlier work: ++ * Copyright (c) 2002, 2003 Belcarra ++ * Copyright (c) 2002 Lineo ++ * ++ * By: ++ * Stuart Lynne <sl@belcarra.com>, ++ * Tom Rushworth <tbr@belcarra.com>, ++ * ++ * ++ */ ++ ++ ++typedef enum msc_trace_int_types { ++ msc_trace_int_udc, msc_trace_int_ep0, msc_trace_int_in, msc_trace_int_out, msc_trace_int_int ++} msc_trace_int_types_t; ++ ++typedef struct msc_trace_int { ++ u32 last; ++ u32 total; ++ u32 samples; ++} msc_trace_int_t; ++ ++ ++typedef enum msc_trace_types { ++ msc_trace_setup, msc_trace_msg, msc_trace_msg32, msc_trace_msg16, ++ msc_trace_msg8, msc_trace_recv, msc_trace_sent, msc_trace_w, msc_trace_r, ++ msc_trace_rlba, msc_trace_slba, msc_trace_tlba, msc_trace_tag, ++ msc_trace_sense, msc_trace_cbw ++} msc_trace_types_t; ++ ++ ++typedef struct msc_trace_regs32 { ++ u32 reg; ++ char * msg; ++} msc_trace_regs32_t; ++ ++ ++typedef struct msc_trace_msg { ++ char *msg; ++} msc_trace_msg_t; ++ ++typedef struct msc_trace_msg32 { ++ u32 val; ++ char *msg; ++} msc_trace_msg32_t; ++ ++typedef struct msc_trace_msg16 { ++ u16 val0; ++ u16 val1; ++ char *msg; ++} msc_trace_msg16_t; ++ ++typedef struct msc_trace_msg8 { ++ u8 val0; ++ u8 val1; ++ u8 val2; ++ u8 val3; ++ char *msg; ++} msc_trace_msg8_t; ++ ++ ++typedef struct trace { ++ msc_trace_types_t msc_trace_type; ++ char *function; ++ u32 interrupts; ++ u64 ticks; ++ u64 sofs; ++ ++ union { ++ msc_trace_msg_t msg; ++ msc_trace_msg8_t msg8; ++ msc_trace_msg16_t msg16; ++ msc_trace_msg32_t msg32; ++ ++ struct usbd_device_request setup; ++ unsigned char recv[8]; ++ unsigned char sent[8]; ++ ++ unsigned int ints[2]; ++ ++ } trace; ++ ++} msc_trace_t; ++ ++ ++#define TRACE_MAX 30000 ++ ++extern int msc_trace_first; ++extern int msc_trace_next; ++ ++extern msc_trace_int_t *msc_trace_ints; ++extern msc_trace_t *msc_traces; ++ ++#ifdef CONFIG_OTG_MSC_REGISTER_TRACE ++ ++msc_trace_t *MSC_TRACE_NEXT(msc_trace_types_t msc_trace_type); ++ ++static __inline__ void TRACE_SETUP(struct usbd_device_request *setup) ++{ ++ if (msc_traces) { ++ msc_trace_t *p = MSC_TRACE_NEXT(msc_trace_setup); ++ p->msc_trace_type = msc_trace_setup; ++ memcpy(&p->trace.setup, setup, sizeof(struct usbd_device_request)); ++ } ++} ++ ++static __inline__ void TRACE_MSG(char *msg) ++{ ++ if (msc_traces) { ++ msc_trace_t *p = MSC_TRACE_NEXT(msc_trace_msg); ++ p->trace.msg.msg = msg; ++ } ++} ++ ++static __inline__ void TRACE_W(char *msg, u32 val) ++{ ++ if (msc_traces) { ++ msc_trace_t *p = MSC_TRACE_NEXT(msc_trace_w); ++ p->trace.msg32.val = val; ++ p->trace.msg32.msg = msg; ++ } ++} ++ ++static __inline__ void TRACE_R(char *msg, u32 val) ++{ ++ if (msc_traces) { ++ msc_trace_t *p = MSC_TRACE_NEXT(msc_trace_r); ++ p->trace.msg32.val = val; ++ p->trace.msg32.msg = msg; ++ } ++} ++ ++static __inline__ void TRACE_MSG32(char *msg, u32 val) ++{ ++ if (msc_traces) { ++ msc_trace_t *p = MSC_TRACE_NEXT(msc_trace_msg32); ++ p->trace.msg32.val = val; ++ p->trace.msg32.msg = msg; ++ } ++} ++ ++static __inline__ void TRACE_MSG16(char *msg, u16 val0, u16 val1) ++{ ++ if (msc_traces) { ++ msc_trace_t *p = MSC_TRACE_NEXT(msc_trace_msg16); ++ p->trace.msg16.val0 = val0; ++ p->trace.msg16.val1 = val1; ++ p->trace.msg16.msg = msg; ++ } ++} ++ ++static __inline__ void TRACE_MSG8(char *msg, u8 val0, u8 val1, u8 val2, u8 val3) ++{ ++ if (msc_traces) { ++ msc_trace_t *p = MSC_TRACE_NEXT(msc_trace_msg8); ++ p->trace.msg8.val0 = val0; ++ p->trace.msg8.val1 = val1; ++ p->trace.msg8.val2 = val2; ++ p->trace.msg8.val3 = val3; ++ p->trace.msg8.msg = msg; ++ } ++} ++ ++static __inline__ void TRACE_RECV(unsigned char *cp) ++{ ++ if (msc_traces) { ++ msc_trace_t *p = MSC_TRACE_NEXT(msc_trace_recv); ++ memcpy(&p->trace.recv, cp, 8); ++ } ++} ++ ++static __inline__ void TRACE_RECVN(unsigned char *cp, int bytes) ++{ ++ if (msc_traces) { ++ msc_trace_t *p = MSC_TRACE_NEXT(msc_trace_recv); ++ memset(&p->trace.recv, 0, 8); ++ memcpy(&p->trace.recv, cp, bytes); ++ } ++} ++ ++static __inline__ void TRACE_SENT(unsigned char *cp) ++{ ++ if (msc_traces) { ++ msc_trace_t *p = MSC_TRACE_NEXT(msc_trace_sent); ++ memcpy(&p->trace.sent, cp, 8); ++ } ++} ++ ++ ++static __inline__ void TRACE_RLBA(unsigned int lba, unsigned int crc) ++{ ++ if (msc_traces) { ++ msc_trace_t *p = MSC_TRACE_NEXT(msc_trace_rlba); ++ p->trace.ints[0] = lba; ++ p->trace.ints[1] = crc; ++ } ++} ++ ++static __inline__ void TRACE_SLBA(unsigned int lba, unsigned int crc) ++{ ++ if (msc_traces) { ++ msc_trace_t *p = MSC_TRACE_NEXT(msc_trace_slba); ++ p->trace.ints[0] = lba; ++ p->trace.ints[1] = crc; ++ } ++} ++ ++static __inline__ void TRACE_TLBA(unsigned int lba, unsigned int crc) ++{ ++ if (msc_traces) { ++ msc_trace_t *p = MSC_TRACE_NEXT(msc_trace_tlba); ++ p->trace.ints[0] = lba; ++ p->trace.ints[1] = crc; ++ } ++} ++ ++static __inline__ void TRACE_TAG(unsigned int tag, unsigned int frame) ++{ ++ if (msc_traces) { ++ msc_trace_t *p = MSC_TRACE_NEXT(msc_trace_tag); ++ p->trace.ints[0] = tag; ++ p->trace.ints[1] = frame; ++ } ++} ++ ++static __inline__ void TRACE_SENSE(unsigned int sense, unsigned int info) ++{ ++ if (msc_traces) { ++ msc_trace_t *p = MSC_TRACE_NEXT(msc_trace_sense); ++ p->trace.ints[0] = sense; ++ p->trace.ints[1] = info; ++ } ++} ++ ++static __inline__ void TRACE_CBW(char *msg, int val) ++{ ++ if (msc_traces) { ++ msc_trace_t *p = MSC_TRACE_NEXT(msc_trace_cbw); ++ p->trace.msg32.val = val; ++ p->trace.msg32.msg = msg; ++ } ++} ++ ++#else ++ ++static __inline__ void TRACE_SETUP(struct usbd_device_request *setup) ++{ ++} ++ ++static __inline__ void TRACE_IRQS(u32 cr, u32 sr) ++{ ++} ++ ++static __inline__ void TRACE_RECV(unsigned char *cp) ++{ ++} ++ ++static __inline__ void TRACE_SENT(unsigned char *cp) ++{ ++} ++ ++static __inline__ void TRACE_W(char *msg, u32 val) ++{ ++} ++ ++static __inline__ void TRACE_R(char *msg, u32 val) ++{ ++} ++ ++static __inline__ void TRACE_MSG(char *msg) ++{ ++} ++ ++static __inline__ void TRACE_MSG32(char *msg, u32 val) ++{ ++} ++ ++static __inline__ void TRACE_MSG16(char *msg, u16 val0, u16 val1) ++{ ++} ++ ++static __inline__ void TRACE_TLBA(char *msg, u16 val0, u16 val1) ++{ ++} ++ ++static __inline__ void TRACE_TAG(char *msg, u16 val0, u16 val1) ++{ ++} ++ ++static __inline__ void TRACE_SENSE(char *msg, u8 val0, u8 val1, u8 val2, u8 vale) ++{ ++} ++ ++#endif ++ ++int msc_trace_init (char *str, struct msc_private *); ++void msc_trace_exit (char *str); ++ +diff -uNr linux/drivers/no-otg/functions/network/Config.in linux/drivers/otg/functions/network/Config.in +--- linux/drivers/no-otg/functions/network/Config.in 1970-01-01 01:00:00.000000000 +0100 ++++ linux/drivers/otg/functions/network/Config.in 2006-09-01 21:41:28.000000000 +0200 +@@ -0,0 +1,70 @@ ++# ++# Network Function ++# ++# Copyright (C) 2002-2004 Belcarra ++# ++ ++mainmenu_option next_comment ++comment "USB Peripheral Function Driver - Network" ++ ++dep_tristate ' Network Function Driver' CONFIG_OTG_NETWORK $CONFIG_OTG ++ ++if [ "$CONFIG_OTG_NETWORK" != "n" ]; then ++ ++ hex 'VendorID (hex value)' CONFIG_OTG_NETWORK_VENDORID "15ec" ++ hex 'ProductID (hex value)' CONFIG_OTG_NETWORK_PRODUCTID "e003" ++ hex 'bcdDevice (binary-coded decimal)' CONFIG_OTG_NETWORK_BCDDEVICE "0100" ++ ++ string 'iManufacturer (string)' CONFIG_OTG_NETWORK_MANUFACTURER "Belcarra" ++ string 'iProduct (string)' CONFIG_OTG_NETWORK_PRODUCT_NAME "Belcarra BLAN Device" ++ ++ choice "Mode" "\ ++ MDLM-BLAN-Networking-for-personal-devices CONFIG_OTG_NETWORK_BLAN \ ++ MDLM-SAFE-Networking-for-Bridge-Routers CONFIG_OTG_NETWORK_SAFE \ ++ CDC-Networking-for-Bridge-Routers CONFIG_OTG_NETWORK_CDC \ ++ Failsafe-BASIC-Networking-testing-only CONFIG_OTG_NETWORK_BASIC \ ++ Failsafe-BASIC2-Networking-testing-only CONFIG_OTG_NETWORK_BASIC2 \ ++ Experimental-Ethernet-Mode-Networking-testing-only CONFIG_OTG_NETWORK_EEM \ ++ " MDLM-BLAN-Networking-for-personal-devices ++ ++ if [ "$CONFIG_OTG_NETWORK_BLAN" = "y" ]; then ++ source drivers/otg/functions/network/Config.in-blan ++ fi ++ ++ if [ "$CONFIG_OTG_NETWORK_SAFE" = "y" ]; then ++ source drivers/otg/functions/network/Config.in-safe ++ fi ++ ++ if [ "$CONFIG_OTG_NETWORK_CDC" = "y" ]; then ++ source drivers/otg/functions/network/Config.in-cdc ++ fi ++ ++ if [ "$CONFIG_OTG_NETWORK_BASIC" = "y" ]; then ++ source drivers/otg/functions/network/Config.in-basic ++ fi ++ ++ if [ "$CONFIG_OTG_NETWORK_BASIC2" = "y" ]; then ++ source drivers/otg/functions/network/Config.in-basic2 ++ fi ++ ++ if [ "$CONFIG_OTG_NETWORK_EEM" = "y" ]; then ++ source drivers/otg/functions/network/Config.in-eem ++ fi ++ ++ comment '' ++ comment 'Testing Options' ++ bool 'Start Single Urb Test' CONFIG_OTG_NETWORK_START_SINGLE ++ bool 'EP0 Test' CONFIG_OTG_NETWORK_EP0TEST ++ dep_bool "Hotplug-config" CONFIG_OTG_NETWORK_HOTPLUG $CONFIG_HOTPLUG ++ ++else ++ define_bool CONFIG_OTG_NETWORK_BLAN n ++ define_bool CONFIG_OTG_NETWORK_SAFE n ++ define_bool CONFIG_OTG_NETWORK_CDC n ++ define_bool CONFIG_OTG_NETWORK_BASIC n ++ define_bool CONFIG_OTG_NETWORK_BASIC2 n ++ define_bool CONFIG_OTG_NETWORK_EEM n ++fi ++ ++endmenu ++ +diff -uNr linux/drivers/no-otg/functions/network/Config.in-basic linux/drivers/otg/functions/network/Config.in-basic +--- linux/drivers/no-otg/functions/network/Config.in-basic 1970-01-01 01:00:00.000000000 +0100 ++++ linux/drivers/otg/functions/network/Config.in-basic 2006-09-01 21:41:28.000000000 +0200 +@@ -0,0 +1,17 @@ ++# ++# Network Function ++# ++# Copyright (C) 2002-2003 Belcarra ++# ++ ++mainmenu_option next_comment ++comment "BASIC Networking Configuration" ++ ++ #bool " Failsafe BASIC Networking mode" CONFIG_OTG_NETWORK_BASIC ++ if [ "$CONFIG_OTG_NETWORK_BASIC" = "y" ]; then ++ comment 'BASIC Configuration' ++ string 'Data Interface iConfiguration (string)' CONFIG_OTG_NETWORK_BASIC_DESC "BASIC Net Cfg" ++ string 'Data Interface iInterface (string)' CONFIG_OTG_NETWORK_BASIC_INTF "Data Intf" ++ fi ++ ++endmenu +diff -uNr linux/drivers/no-otg/functions/network/Config.in-basic2 linux/drivers/otg/functions/network/Config.in-basic2 +--- linux/drivers/no-otg/functions/network/Config.in-basic2 1970-01-01 01:00:00.000000000 +0100 ++++ linux/drivers/otg/functions/network/Config.in-basic2 2006-09-01 21:41:28.000000000 +0200 +@@ -0,0 +1,13 @@ ++# ++# Network Function ++# ++# Copyright (C) 2002-2003 Belcarra ++# ++ ++mainmenu_option next_comment ++ comment "BASIC2 Networking Configuration" ++ string 'Data Interface iConfiguration (string)' CONFIG_OTG_NETWORK_BASIC2_DESC "BASIC Net Cfg" ++ string 'Comm Interface iInterface (string)' CONFIG_OTG_NETWORK_BASIC2_COMM_INTF "Comm Intf" ++ string 'Data Interface iInterface (string)' CONFIG_OTG_NETWORK_BASIC2_DATA_INTF "Data Intf" ++ ++endmenu +diff -uNr linux/drivers/no-otg/functions/network/Config.in-blan linux/drivers/otg/functions/network/Config.in-blan +--- linux/drivers/no-otg/functions/network/Config.in-blan 1970-01-01 01:00:00.000000000 +0100 ++++ linux/drivers/otg/functions/network/Config.in-blan 2006-09-01 21:41:28.000000000 +0200 +@@ -0,0 +1,38 @@ ++# ++# Network Function ++# ++# Copyright (C) 2002-2003 Belcarra ++# ++ ++mainmenu_option next_comment ++ comment "MBLM-BLAN Networking Configuration" ++ ++ string 'iConfiguration (string)' CONFIG_OTG_NETWORK_BLAN_DESC "BLAN Net Cfg" ++ string 'iInterface (string)' CONFIG_OTG_NETWORK_BLAN_INTF "Comm/Data Intf" ++ ++ ++ comment 'Special Framing Options' ++ bool "CRC" CONFIG_OTG_NETWORK_BLAN_CRC ++ if [ "$CONFIG_OTG_NETWORK_BLAN_CRC" = "y" ]; then ++ ++ bool "Pad Before CRC (to wMaxPacketSize-1)" CONFIG_OTG_NETWORK_BLAN_PADBEFORE ++ bool "Pad After CRC" CONFIG_OTG_NETWORK_BLAN_PADAFTER ++ if [ "$CONFIG_OTG_NETWORK_BLAN" = "y" -a "$CONFIG_OTG_NETWORK_BLAN_PADAFTER" = "y" ]; then ++ int 'Pad multiple' CONFIG_OTG_NETWORK_BLAN_PADBYTES "8" ++ fi ++ bool "Fermat Randomizer" CONFIG_OTG_NETWORK_BLAN_FERMAT ++ fi ++ ++ comment 'USBLAN Options' ++ bool "Do not Set Time" CONFIG_OTG_NETWORK_BLAN_DO_NOT_SETTIME ++ bool "Request Hostname" CONFIG_OTG_NETWORK_BLAN_HOSTNAME ++ bool "Infrastructure Device" CONFIG_OTG_NETWORK_BLAN_NONBRIDGED ++ bool "Data Notification OK" CONFIG_OTG_NETWORK_BLAN_DATA_NOTIFY_OK ++ int 'Pad multiple' CONFIG_OTG_NETWORK_BLAN_PADBYTES "8" ++ int 'Polling Interval ' CONFIG_OTG_NETWORK_BLAN_INTERVAL "4" ++ bool "Set iProduct from IPADDR" CONFIG_OTG_NETWORK_BLAN_IPADDR ++ bool "Auto-config" CONFIG_OTG_NETWORK_BLAN_AUTO_CONFIG ++ comment '' ++ ++ ++endmenu +diff -uNr linux/drivers/no-otg/functions/network/Config.in-cdc linux/drivers/otg/functions/network/Config.in-cdc +--- linux/drivers/no-otg/functions/network/Config.in-cdc 1970-01-01 01:00:00.000000000 +0100 ++++ linux/drivers/otg/functions/network/Config.in-cdc 2006-09-01 21:41:28.000000000 +0200 +@@ -0,0 +1,16 @@ ++# ++# Network Function ++# ++# Copyright (C) 2002-2003 Belcarra ++# ++ ++mainmenu_option next_comment ++ comment "CDC Networking Configuration" ++ ++ string 'iConfiguration (string)' CONFIG_OTG_NETWORK_CDC_DESC "SAFE Net Cfg" ++ string 'Comm Interface iInterface (string)' CONFIG_OTG_NETWORK_CDC_COMM_INTF "Comm Intf" ++ string 'Data (diabled) iInterface (string)' CONFIG_OTG_NETWORK_CDC_NODATA_INTF "Data (Disabled) Intf" ++ string 'Data Interface iInterface (string)' CONFIG_OTG_NETWORK_CDC_DATA_INTF "Data Intf" ++ ++ ++endmenu +diff -uNr linux/drivers/no-otg/functions/network/Config.in-eem linux/drivers/otg/functions/network/Config.in-eem +--- linux/drivers/no-otg/functions/network/Config.in-eem 1970-01-01 01:00:00.000000000 +0100 ++++ linux/drivers/otg/functions/network/Config.in-eem 2006-09-01 21:41:28.000000000 +0200 +@@ -0,0 +1,17 @@ ++# ++# Network Function ++# ++# Copyright (C) 2002-2003 Belcarra ++# ++ ++mainmenu_option next_comment ++comment "EEM Networking Configuration" ++ ++ #bool " Experimental Ethernet Mode" CONFIG_OTG_NETWORK_EEM ++ if [ "$CONFIG_OTG_NETWORK_EEM" = "y" ]; then ++ comment 'EEM Configuration' ++ string 'Data Interface iConfiguration (string)' CONFIG_OTG_NETWORK_EEM_DESC "EEM Net Cfg" ++ string 'Data Interface iInterface (string)' CONFIG_OTG_NETWORK_EEM_INTF "Data Intf" ++ fi ++ ++endmenu +diff -uNr linux/drivers/no-otg/functions/network/Config.in-safe linux/drivers/otg/functions/network/Config.in-safe +--- linux/drivers/no-otg/functions/network/Config.in-safe 1970-01-01 01:00:00.000000000 +0100 ++++ linux/drivers/otg/functions/network/Config.in-safe 2006-09-01 21:41:28.000000000 +0200 +@@ -0,0 +1,21 @@ ++# ++# Network Function ++# ++# Copyright (C) 2002-2003 Belcarra ++# ++ ++mainmenu_option next_comment ++ comment "MDLM-SAFE Networking Configuration" ++ ++ string 'Data Interface iConfiguration (string)' CONFIG_OTG_NETWORK_SAFE_DESC "SAFE Net Cfg" ++ string 'Data Interface iInterface (string)' CONFIG_OTG_NETWORK_SAFE_INTF "Data Intf" ++ bool " Do not Set Time" CONFIG_OTG_NETWORK_SAFE_DO_NOT_SETTIME ++ bool " CRC" CONFIG_OTG_NETWORK_SAFE_CRC ++ if [ "$CONFIG_OTG_NETWORK_SAFE_CRC" = "y" ]; then ++ bool "Pad Before CRC (to wMaxPacketSize-1)" CONFIG_OTG_NETWORK_SAFE_PADBEFORE ++ fi ++ bool "Infrastructure Device" CONFIG_OTG_NETWORK_SAFE_NONBRIDGED ++ comment '' ++ ++ ++endmenu +diff -uNr linux/drivers/no-otg/functions/network/Kconfig linux/drivers/otg/functions/network/Kconfig +--- linux/drivers/no-otg/functions/network/Kconfig 1970-01-01 01:00:00.000000000 +0100 ++++ linux/drivers/otg/functions/network/Kconfig 2006-09-01 21:41:28.000000000 +0200 +@@ -0,0 +1,131 @@ ++menu "OTG Network Function" ++ depends on OTG ++ ++config OTG_NETWORK ++ tristate " Network Function Driver" ++ depends on OTG ++ ++menu "OTG Network Function options" ++ depends on OTG_NETWORK ++ ++config OTG_NETWORK_VENDORID ++ hex "VendorID (hex value)" ++ depends on OTG && OTG_NETWORK ++ default "0x15ec" ++ ++config OTG_NETWORK_PRODUCTID ++ hex "ProductID (hex value)" ++ depends on OTG && OTG_NETWORK ++ default "0xe003" ++ ++config OTG_NETWORK_BCDDEVICE ++ hex "bcdDevice (binary-coded decimal)" ++ depends on OTG && OTG_NETWORK ++ default "0x0100" ++ ++config OTG_NETWORK_MANUFACTURER ++ string "iManufacturer (string)" ++ depends on OTG && OTG_NETWORK ++ default "Belcarra" ++ ++config OTG_NETWORK_PRODUCT_NAME ++ string "iProduct (string)" ++ depends on OTG && OTG_NETWORK ++ default "Belcarra BLAN Device" ++ ++config OTG_NETWORK_BLAN_DESC ++ string " iConfiguration (string)" ++ depends on OTG && OTG_NETWORK ++ default "BLAN Net Cfg" ++ ++config OTG_NETWORK_MANUFACTURER ++ string "iManufacturer (string)" ++ depends on OTG && OTG_NETWORK ++ default "Belcarra" ++ ++config OTG_NETWORK_PRODUCT_NAME ++ string "iProduct (string)" ++ depends on OTG && OTG_NETWORK ++ default "Belcarra BLAN Device" ++ ++config OTG_NETWORK_BLAN_DESC ++ string " iConfiguration (string)" ++ depends on OTG && OTG_NETWORK ++ default "BLAN Net Cfg" ++ ++config OTG_NETWORK_BLAN_INTF ++ string " iInterface (string)" ++ depends on OTG && OTG_NETWORK ++ default "Comm/Data Intf" ++ ++config OTG_NETWORK_BLAN_PADBYTES ++ int " Pad multiple" ++ default "8" ++ ++config OTG_NETWORK_SAFE_DESC ++ string " Data Interface iConfiguration (string)" ++ depends on OTG && OTG_NETWORK ++ default "SAFE Net Cfg" ++ ++config OTG_NETWORK_SAFE_INTF ++ string " Data Interface iInterface (string)" ++ depends on OTG && OTG_NETWORK ++ default "Data Intf" ++ ++config OTG_NETWORK_CDC_DESC ++ string " iConfiguration (string)" ++ depends on OTG && OTG_NETWORK ++ default "SAFE Net Cfg" ++ ++config OTG_NETWORK_CDC_COMM_INTF ++ string " Comm Interface iInterface (string)" ++ depends on OTG && OTG_NETWORK ++ default "Comm Intf" ++ ++config OTG_NETWORK_CDC_NODATA_INTF ++ string " Data (diabled) iInterface (string)" ++ depends on OTG && OTG_NETWORK ++ default "Data (Disabled) Intf" ++ ++config OTG_NETWORK_CDC_DATA_INTF ++ string " Data Interface iInterface (string)" ++ depends on OTG && OTG_NETWORK ++ default "Data Intf" ++ ++config OTG_NETWORK_BASIC_DESC ++ string " Data Interface iConfiguration (string)" ++ depends on OTG && OTG_NETWORK ++ default "BASIC Net Cfg" ++ ++config OTG_NETWORK_BASIC_INTF ++ string " Data Interface iInterface (string)" ++ depends on OTG && OTG_NETWORK ++ default "Data Intf" ++ ++config OTG_NETWORK_BASIC2_DESC ++ string " Data Interface iConfiguration (string)" ++ depends on OTG && OTG_NETWORK ++ default "BASIC Net Cfg" ++ ++config OTG_NETWORK_BASIC2_COMM_INTF ++ string " Comm Interface iInterface (string)" ++ depends on OTG && OTG_NETWORK ++ default "Comm Intf" ++ ++config OTG_NETWORK_BASIC2_DATA_INTF ++ string " Data Interface iInterface (string)" ++ depends on OTG && OTG_NETWORK ++ default "Data Intf" ++ ++config OTG_NETWORK_START_SINGLE ++ bool " Start Single Urb Test" ++ depends on OTG && OTG_NETWORK ++ default n ++ ++config OTG_NETWORK_EP0TEST ++ bool " EP0 Test" ++ depends on OTG && OTG_NETWORK ++ default n ++ ++endmenu ++endmenu +diff -uNr linux/drivers/no-otg/functions/network/Makefile linux/drivers/otg/functions/network/Makefile +--- linux/drivers/no-otg/functions/network/Makefile 1970-01-01 01:00:00.000000000 +0100 ++++ linux/drivers/otg/functions/network/Makefile 2006-09-01 21:41:28.000000000 +0200 +@@ -0,0 +1,70 @@ ++# ++# Network Function Driver ++# ++# Copyright (C) 2002-2003 Belcarra ++ ++ ++O_TARGET := network_target.o ++list-multi := network_fd.o ++ ++network_fd-objs := net-l24-os.o net-fd.o basic.o basic2.o blan.o cdc.o safe.o fermat.o ++ ++# Objects that export symbols. ++export-objs := net-l24-os.o net-fd.o ++ ++ ++# Object file lists. ++ ++obj-y := ++obj-m := ++obj-n := ++obj- := ++ ++# Each configuration option enables a list of files. ++ ++obj-$(CONFIG_OTG_NETWORK) += network_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. ++ ++ ++OTG_DIR=$(TOPDIR)/drivers/otg ++NETWORK_DIR=$(OTG_DIR)/functions/network ++include $(TOPDIR)/Rules.make ++EXTRA_CFLAGS += -Wno-unused -Wstrict-prototypes -Wno-format ++EXTRA_CFLAGS += -I$(NETWORK_DIR) -I$(OTG_DIR) ++EXTRA_CFLAGS_nostdinc += -Wstrict-prototypes -Wno-unused -Wno-format ++EXTRA_CFLAGS_nostdinc += -I$(NETWORK_DIR) -I$(OTG_DIR) ++ ++# Link rules for multi-part drivers. ++ ++network_fd.o: $(network_fd-objs) ++ $(LD) -r -o $@ $(network_fd-objs) ++ ++# dependencies: ++ ++net-fd.o: net-fd.h net-os.h network.h ++net-l24-os.o: net-fd.h net-os.h network.h ++ ++ +diff -uNr linux/drivers/no-otg/functions/network/Makefile-l26 linux/drivers/otg/functions/network/Makefile-l26 +--- linux/drivers/no-otg/functions/network/Makefile-l26 1970-01-01 01:00:00.000000000 +0100 ++++ linux/drivers/otg/functions/network/Makefile-l26 2006-09-01 21:41:28.000000000 +0200 +@@ -0,0 +1,22 @@ ++# ++# Network Function Driver ++# ++# Copyright (C) 2002-2004 Belcarra Technologies Corp ++ ++ ++OTG=$(TOPDIR)/drivers/otg ++NETWORKD=$(OTG)/functions/network ++OTGCORE_DIR=$(OTG)/otgcore ++USBDCORE_DIR=$(OTG)/usbdcore ++EXTRA_CFLAGS += -I$(NETWORKD) -I$(OTG) -Wno-unused -Wno-format -I$(USBDCORE_DIR) -I$(OTGCORE_DIR) ++EXTRA_CFLAGS_nostdinc += -I$(NETWORKD) -I$(OTG) -Wno-unused -Wno-format -I$(USBDCORE_DIR) -I$(OTGCORE_DIR) ++ ++network_fd-objs := network.o basic.o basic2.o blan.o cdc.o safe.o fermat.o ++obj-$(CONFIG_OTG_NETWORK) += network_fd.o ++ ++ ++# Link rules for multi-part drivers. ++ ++network_fd.o: $(network_fd-objs) ++ $(LD) -r -o $@ $(network_fd-objs) ++ +diff -uNr linux/drivers/no-otg/functions/network/TODO.txt linux/drivers/otg/functions/network/TODO.txt +--- linux/drivers/no-otg/functions/network/TODO.txt 1970-01-01 01:00:00.000000000 +0100 ++++ linux/drivers/otg/functions/network/TODO.txt 2006-09-01 21:41:28.000000000 +0200 +@@ -0,0 +1,32 @@ ++NETWORK TODO List Stuart Lynne ++Belcarra Tue Aug 24 21:36:09 PDT 2004 ++ ++ ++1. NETWORK documentation ++ ++ ++2. BLAN ++ - data notification ++ - timeout to resend data notification after XX mS ++ ++3. CDC ++ - filters ++ ++ ++4. RNDIS ++ - should we add back in? ++ ++ ++5. NOTIFICATIONS ++ ++ NETWORK_CONNECTION ++ CONNECTION_SPEED_CHANGE ++ ++ ++ ++I suspect that we should make the data notification a runtime option, ++the host driver would "enable" this after a certain number of devices ++are plugged in (say 5 or more, some configurable number.) This would ++be done by sending a vendor device request to the peripheral to tell ++the network driver that it needs to use this mechanism. ++ +diff -uNr linux/drivers/no-otg/functions/network/basic.c linux/drivers/otg/functions/network/basic.c +--- linux/drivers/no-otg/functions/network/basic.c 1970-01-01 01:00:00.000000000 +0100 ++++ linux/drivers/otg/functions/network/basic.c 2006-09-01 21:41:28.000000000 +0200 +@@ -0,0 +1,247 @@ ++/* ++ * otg/functions/network/basic.c - Network Function Driver ++ * ++ * Copyright (c) 2002, 2003, 2004 Belcarra ++ * ++ * By: ++ * Chris Lynne <cl@belcarra.com> ++ * Stuart Lynne <sl@belcarra.com> ++ * Bruce Balden <balden@belcarra.com> ++ * ++ * 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. ++ * ++ */ ++/*! ++ * @file otg/functions/network/basic.c ++ * @brief This file implements the required descriptors to implement ++ * a basic network device with a single interface. ++ * ++ * The BASIC network driver implements a very simple descriptor set. ++ * A single interface with two BULK data endpoints and a optional ++ * INTERRUPT endpoint. ++ * ++ * @ingroup NetworkFunction ++ */ ++ ++ ++#include <otg/otg-compat.h> ++#include <otg/otg-module.h> ++ ++#include <linux/slab.h> ++#include <linux/interrupt.h> ++#include <linux/utsname.h> ++#include <linux/netdevice.h> ++ ++#include <otg/usbp-chap9.h> ++#include <otg/usbp-func.h> ++ ++#include "network.h" ++ ++#define NTT network_fd_trace_tag ++ ++#ifdef CONFIG_OTG_NETWORK_BASIC ++/* USB BASIC Configuration ******************************************************************** */ ++ ++/* BASIC Communication Interface Class descriptors ++ */ ++/*! BULK OUT data endpoint descriptor ++ */ ++static struct usbd_endpoint_descriptor basic_data_1 = { ++ bLength:sizeof(struct usbd_endpoint_descriptor), ++ bDescriptorType:USB_DT_ENDPOINT, ++ bEndpointAddress:USB_DIR_OUT, ++ bmAttributes: BULK, ++ wMaxPacketSize: 0, ++ bInterval: 0, ++}; ++/*! BULK IN data endpoint descriptor ++ */ ++static struct usbd_endpoint_descriptor basic_data_2 =// { 0x07, USB_DT_ENDPOINT, USB_DIR_IN, BULK, 0, 0x00, 0x00, }; ++{ ++ bLength:sizeof(struct usbd_endpoint_descriptor), ++ bDescriptorType:USB_DT_ENDPOINT, ++ bEndpointAddress:USB_DIR_IN, ++ bmAttributes: BULK, ++ wMaxPacketSize: 0, ++ bInterval: 0x0, ++}; ++ ++/*! INTERRUPT IN data endpoint descriptor ++ */ ++static struct usbd_endpoint_descriptor basic_comm_1 = //{ 0x07, USB_DT_ENDPOINT, USB_DIR_IN, INTERRUPT,0, 0x00, 0x0a, }; ++{ ++ bLength:sizeof(struct usbd_endpoint_descriptor), ++ bDescriptorType:USB_DT_ENDPOINT, ++ bEndpointAddress:USB_DIR_IN, ++ bmAttributes: INTERRUPT, ++ wMaxPacketSize: 0, ++ bInterval: 0xa, ++}; ++ ++/*! Endpoint descriptor list ++ */ ++static struct usbd_endpoint_descriptor *basic_default[] = { &basic_data_1, ++ &basic_data_2, ++ &basic_comm_1, }; ++u8 basic_indexes[] = { BULK_OUT, BULK_IN, INT_IN, }; ++ ++/*! Data Interface Descriptor ++ */ ++static struct usbd_interface_descriptor basic_data_alternate_descriptor = { ++ bLength:sizeof(struct usbd_interface_descriptor), ++ bDescriptorType:USB_DT_INTERFACE, ++ bInterfaceNumber: 0x00, ++ bAlternateSetting: 0x00, // bInterfaceNumber, bAlternateSetting ++ sizeof (basic_default) / sizeof(struct usbd_endpoint_descriptor *), // bNumEndpoints ++ bInterfaceClass:LINEO_CLASS, ++ bInterfaceSubClass:LINEO_SUBCLASS_BASIC_NET, ++ bInterfaceProtocol:LINEO_BASIC_NET_CRC, ++ iInterface:0x00, ++}; ++ ++/*! Data Alternate Interface Description List ++ */ ++static struct usbd_alternate_description basic_data_alternate_descriptions[] = { ++ { iInterface: CONFIG_OTG_NETWORK_BASIC_INTF, ++ interface_descriptor: (struct usbd_interface_descriptor *)&basic_data_alternate_descriptor, ++ endpoints:sizeof (basic_default) / sizeof(struct usbd_endpoint_descriptor *), ++ endpoint_list: basic_default, ++ endpoint_indexes: basic_indexes, ++ }, ++}; ++ ++ ++/* BASIC Data Interface Alternate descriptions and descriptors ++ */ ++ ++/* BASIC Interface descriptions and descriptors ++ */ ++/*! Interface Description List ++ */ ++struct usbd_interface_description basic_interfaces[] = { ++ { alternates:sizeof (basic_data_alternate_descriptions) / sizeof (struct usbd_alternate_description), ++ alternate_list:basic_data_alternate_descriptions,}, ++}; ++ ++/* BASIC Configuration descriptions and descriptors ++ */ ++/*! Configuration Descriptor ++ */ ++u8 basic_configuration_descriptor[sizeof(struct usbd_configuration_descriptor)] = { ++ 0x09, USB_DT_CONFIGURATION, 0x00, 0x00, // wLength ++ sizeof (basic_interfaces) / sizeof (struct usbd_interface_description), ++ 0x01, 0x00, // bConfigurationValue, iConfiguration ++ 0, 0, ++}; ++ ++/*! Configuration Description List ++ */ ++struct usbd_configuration_description basic_description[] = { ++ { iConfiguration: CONFIG_OTG_NETWORK_BASIC_DESC, ++ configuration_descriptor: (struct usbd_configuration_descriptor *)basic_configuration_descriptor, ++ }, ++ ++}; ++ ++/* BASIC Device Description ++ */ ++/*! Device Descriptor ++ */ ++static struct usbd_device_descriptor basic_device_descriptor = { ++ bLength: sizeof(struct usbd_device_descriptor), ++ bDescriptorType: USB_DT_DEVICE, ++ bcdUSB: __constant_cpu_to_le16(USB_BCD_VERSION), ++ bDeviceClass: COMMUNICATIONS_DEVICE_CLASS, ++ bDeviceSubClass: 0x02, ++ bDeviceProtocol: 0x00, ++ bMaxPacketSize0: 0x00, ++ idVendor: __constant_cpu_to_le16(CONFIG_OTG_NETWORK_VENDORID), ++ idProduct: __constant_cpu_to_le16(CONFIG_OTG_NETWORK_PRODUCTID), ++ bcdDevice: __constant_cpu_to_le16(CONFIG_OTG_NETWORK_BCDDEVICE), ++}; ++ ++#ifdef CONFIG_OTG_HIGH_SPEED ++/*! High Speed Device Qualifier Descriptor ++ */ ++static struct usbd_device_qualifier_descriptor basic_device_qualifier_descriptor = { ++ bLength: sizeof(struct usbd_device_qualifier_descriptor), ++ bDescriptorType: USB_DT_DEVICE_QUALIFIER, ++ bcdUSB: __constant_cpu_to_le16(USB_BCD_VERSION), ++ bDeviceClass: COMMUNICATIONS_DEVICE_CLASS, ++ bDeviceSubClass: 0x02, ++ bDeviceProtocol: 0x00, ++ bMaxPacketSize0: 0x00, ++}; ++#endif /* CONFIG_OTG_HIGH_SPEED */ ++ ++/*! Endpoint Request List ++ */ ++static struct usbd_endpoint_request basic_endpoint_requests[ENDPOINTS+1] = { ++ { 1, 0, 0, USB_DIR_OUT | USB_ENDPOINT_BULK, MAXFRAMESIZE + 48, MAXFRAMESIZE + 512, }, ++ { 1, 0, 0, USB_DIR_IN | USB_ENDPOINT_BULK, MAXFRAMESIZE + 48, MAXFRAMESIZE + 512, }, ++ { 1, 0, 0, USB_DIR_IN | USB_ENDPOINT_INTERRUPT | USB_ENDPOINT_OPT, 16, 64, }, ++ { 0, }, ++}; ++ ++/*! OTG Descriptor ++ */ ++static struct usbd_otg_descriptor basic_otg_descriptor = { ++ bLength : sizeof(struct usbd_otg_descriptor), ++ bDescriptorType: USB_DT_OTG, ++ bmAttributes: 0, ++}; ++ ++/*! Device Description ++ */ ++struct usbd_device_description basic_device_description = { ++ device_descriptor: &basic_device_descriptor, ++#ifdef CONFIG_OTG_HIGH_SPEED ++ device_qualifier_descriptor: &basic_device_qualifier_descriptor, ++#endif /* CONFIG_OTG_HIGH_SPEED */ ++ otg_descriptor: &basic_otg_descriptor, ++ iManufacturer: CONFIG_OTG_NETWORK_MANUFACTURER, ++ iProduct: CONFIG_OTG_NETWORK_PRODUCT_NAME, ++#if !defined(CONFIG_OTG_NO_SERIAL_NUMBER) && defined(CONFIG_OTG_SERIAL_NUMBER_STR) ++ iSerialNumber:CONFIG_OTG_SERIAL_NUMBER_STR, ++#endif ++}; ++ ++ ++/*! basic_init ++ * @param function The function instance ++ */ ++void basic_init (struct usbd_function_instance *function) ++{ ++ basic_data_alternate_descriptions[0].endpoints = Usb_network_private.have_interrupt ? 3 : 2; ++} ++ ++/*! function driver description ++ */ ++struct usbd_function_driver basic_function_driver = { ++ name: "network-BASIC", ++ fops: &net_fd_function_ops, ++ device_description: &basic_device_description, ++ bNumConfigurations: sizeof (basic_description) / sizeof (struct usbd_configuration_description), ++ configuration_description: basic_description, ++ idVendor: __constant_cpu_to_le16(CONFIG_OTG_NETWORK_VENDORID), ++ idProduct: __constant_cpu_to_le16(CONFIG_OTG_NETWORK_PRODUCTID), ++ bcdDevice: __constant_cpu_to_le16(CONFIG_OTG_NETWORK_BCDDEVICE), ++ bNumInterfaces:sizeof (basic_interfaces) / sizeof (struct usbd_interface_description), ++ interface_list:basic_interfaces, ++ endpointsRequested: ENDPOINTS, ++ requestedEndpoints: basic_endpoint_requests, ++}; ++#endif /* CONFIG_OTG_NETWORK_BASIC */ ++ +diff -uNr linux/drivers/no-otg/functions/network/basic2.c linux/drivers/otg/functions/network/basic2.c +--- linux/drivers/no-otg/functions/network/basic2.c 1970-01-01 01:00:00.000000000 +0100 ++++ linux/drivers/otg/functions/network/basic2.c 2006-09-01 21:41:28.000000000 +0200 +@@ -0,0 +1,320 @@ ++/* ++ * otg/functions/network/basic2.c - Network Function Driver ++ * ++ * Copyright (c) 2002, 2003, 2004 Belcarra ++ * ++ * By: ++ * Chris Lynne <cl@belcarra.com> ++ * Stuart Lynne <sl@belcarra.com> ++ * Bruce Balden <balden@belcarra.com> ++ * ++ * 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. ++ * ++ */ ++/*! ++ * @file otg/functions/network/basic2.c ++ * @brief This file implements the required descriptors to implement ++ * a basic network device with two interfaces. ++ * ++ * The BASIC2 network driver implements a very simple descriptor set. ++ * A data interface with two BULK data endpoints and comm interface with ++ * an INTERRUPT endpoint. ++ * ++ * @ingroup NetworkFunction ++ */ ++ ++ ++#include <otg/otg-compat.h> ++#include <otg/otg-module.h> ++ ++#include <linux/slab.h> ++#include <linux/interrupt.h> ++#include <linux/utsname.h> ++#include <linux/netdevice.h> ++ ++#include <otg/usbp-chap9.h> ++#include <otg/otg-compat.h> ++#include <otg/usbp-func.h> ++ ++#include "network.h" ++ ++#define NTT network_fd_trace_tag ++ ++#ifdef CONFIG_OTG_NETWORK_BASIC2 ++/* USB BASIC Configuration ******************************************************************** */ ++ ++/* ++ * This provides a slight amplification of the basic configuration, it moves the ++ * interrupt endpoint (if available) to a separate interface, so that it is similiar ++ * to the cdc configuration ++ */ ++ ++/* BASIC Communication Interface Class descriptors ++ */ ++//static u8 basic2_data_1[] = { 0x07, USB_DT_ENDPOINT, USB_DIR_OUT, BULK, 0, 0x00, 0x00, }; ++//static u8 basic2_data_2[] = { 0x07, USB_DT_ENDPOINT, USB_DIR_IN, BULK, 0, 0x00, 0x00, }; ++//static u8 basic2_comm_1[] = { 0x07, USB_DT_ENDPOINT, USB_DIR_IN, INTERRUPT,0, 0x00, 0x0a, }; ++ ++/*! BULK OUT data endpoint descriptor ++ */ ++static struct usbd_endpoint_descriptor basic2_data_1 = { ++ bLength:sizeof(struct usbd_endpoint_descriptor), ++ bDescriptorType:USB_DT_ENDPOINT, ++ bEndpointAddress:USB_DIR_OUT, ++ bmAttributes: BULK, ++ wMaxPacketSize: 0, ++ bInterval: 0, ++}; ++/*! BULK IN data endpoint descriptor ++ */ ++static struct usbd_endpoint_descriptor basic2_data_2 = { ++ bLength:sizeof(struct usbd_endpoint_descriptor), ++ bDescriptorType:USB_DT_ENDPOINT, ++ bEndpointAddress:USB_DIR_IN, ++ bmAttributes: BULK, ++ wMaxPacketSize: 0, ++ bInterval: 0x0, ++}; ++ ++/*! INTERRUPT IN data endpoint descriptor ++ */ ++static struct usbd_endpoint_descriptor basic2_comm_1 = { ++ bLength:sizeof(struct usbd_endpoint_descriptor), ++ bDescriptorType:USB_DT_ENDPOINT, ++ bEndpointAddress:USB_DIR_IN, ++ bmAttributes: INTERRUPT, ++ wMaxPacketSize: 0, ++ bInterval: 0xa, ++}; ++ ++/*! Endpoint descriptor list ++ */ ++static struct usbd_endpoint_descriptor *basic2_default[] = { ++ &basic2_data_1, ++ &basic2_data_2, ++ &basic2_comm_1, }; ++u8 basic2_indexes[] = { BULK_OUT, BULK_IN, INT_IN, }; ++ ++static struct usbd_endpoint_descriptor *basic2_comm_endpoints[] = { ++ (struct usbd_endpoint_descriptor *) &basic2_comm_1, }; ++ ++/*! Endpoint descriptor list ++ */ ++static struct usbd_endpoint_descriptor *basic2_data_endpoints[] = { ++ (struct usbd_endpoint_descriptor *) &basic2_data_1, ++ (struct usbd_endpoint_descriptor *) &basic2_data_2, }; ++ ++u8 basic2_comm_indexes[] = { INT_IN, }; ++u8 basic2_data_indexes[] = { BULK_OUT, BULK_IN, }; ++ ++ ++/* BASIC2 Data Interface Alternate endpoints ++ */ ++ ++ ++#ifndef COMMUNICATIONS_NETWORK_SUBCLASS ++#define COMMUNICATIONS_NETWORK_SUBCLASS 0 ++#endif ++ ++#ifndef VENDOR_PROTOCOL ++#define VENDOR_PROTOCOL 0xFF ++#endif ++/*! Comm Interface Descriptor ++ */ ++static struct usbd_interface_descriptor basic2_comm_alternate_descriptor = { ++ bLength:sizeof(struct usbd_interface_descriptor), ++ bDescriptorType:USB_DT_INTERFACE, ++ bInterfaceNumber: 0x00, ++ bAlternateSetting: 0x00, // bInterfaceNumber, bAlternateSetting ++ sizeof (basic2_default) / sizeof(struct usbd_endpoint_descriptor *), // bNumEndpoints ++ bInterfaceClass:COMMUNICATIONS_INTERFACE_CLASS, ++ bInterfaceSubClass:COMMUNICATIONS_NETWORK_SUBCLASS, ++ bInterfaceProtocol:VENDOR_PROTOCOL, ++ iInterface:0x00, ++}; ++ ++/*! Data Interface Descriptor ++ */ ++static struct usbd_interface_descriptor basic2_data_alternate_descriptor = { ++ bLength:sizeof(struct usbd_interface_descriptor), ++ bDescriptorType:USB_DT_INTERFACE, ++ bInterfaceNumber: 0x01, ++ bAlternateSetting: 0x00, // bInterfaceNumber, bAlternateSetting ++ sizeof (basic2_default) / sizeof(struct usbd_endpoint_descriptor *), // bNumEndpoints ++ bInterfaceClass:DATA_INTERFACE_CLASS, ++ bInterfaceSubClass:COMMUNICATIONS_NO_SUBCLASS, ++ bInterfaceProtocol:COMMUNICATIONS_NO_PROTOCOL, ++ iInterface:0x00, ++}; ++ ++/*! Comm Alternate Interface Description List ++ */ ++static struct usbd_alternate_description basic2_comm_alternate_descriptions[] = { ++ { iInterface: CONFIG_OTG_NETWORK_BASIC2_COMM_INTF, ++ interface_descriptor: (struct usbd_interface_descriptor *)&basic2_comm_alternate_descriptor, ++ endpoints:sizeof (basic2_comm_endpoints) / sizeof(struct usbd_endpoint_descriptor *), ++ endpoint_list:basic2_comm_endpoints, ++ endpoint_indexes:basic2_comm_indexes, ++ }, ++}; ++ ++ ++/*! Data Alternate Interface Description List ++ */ ++static struct usbd_alternate_description basic2_data_alternate_descriptions[] = { ++ { iInterface: CONFIG_OTG_NETWORK_BASIC2_DATA_INTF, ++ interface_descriptor: (struct usbd_interface_descriptor *)&basic2_data_alternate_descriptor, ++ endpoints:sizeof (basic2_data_endpoints) / sizeof(struct usbd_endpoint_descriptor *), ++ endpoint_list: basic2_data_endpoints, ++ endpoint_indexes: basic2_data_indexes, ++ }, ++}; ++ ++ ++/* BASIC Data Interface Alternate descriptions and descriptors ++ */ ++ ++/* BASIC Interface descriptions and descriptors ++ */ ++/*! Interface Description List ++ */ ++static struct usbd_interface_description basic2_interfaces[] = { ++ { alternates:sizeof (basic2_comm_alternate_descriptions) / sizeof (struct usbd_alternate_description), ++ alternate_list:basic2_comm_alternate_descriptions,}, ++ ++ { alternates:sizeof (basic2_data_alternate_descriptions) / sizeof (struct usbd_alternate_description), ++ alternate_list:basic2_data_alternate_descriptions,}, ++}; ++ ++ ++/* BASIC Configuration descriptions and descriptors ++ */ ++/*! Configuration Descriptor ++ */ ++u8 basic2_configuration_descriptor[sizeof(struct usbd_configuration_descriptor)] = { ++ 0x09, USB_DT_CONFIGURATION, 0x00, 0x00, // wLength ++ sizeof (basic2_interfaces) / sizeof (struct usbd_interface_description), ++ 0x01, 0x00, // bConfigurationValue, iConfiguration ++ 0, 0, ++}; ++ ++/*! Configuration Description List ++ */ ++struct usbd_configuration_description basic2_description[] = { ++ { iConfiguration: CONFIG_OTG_NETWORK_BASIC2_DESC, ++ configuration_descriptor: (struct usbd_configuration_descriptor *)basic2_configuration_descriptor, ++ }, ++ ++}; ++ ++/* BASIC Device Description ++ */ ++/*! Device Descriptor ++ */ ++static struct usbd_device_descriptor basic2_device_descriptor = { ++ bLength: sizeof(struct usbd_device_descriptor), ++ bDescriptorType: USB_DT_DEVICE, ++ bcdUSB: __constant_cpu_to_le16(USB_BCD_VERSION), ++ bDeviceClass: COMMUNICATIONS_DEVICE_CLASS, ++ bDeviceSubClass: 0x02, ++ bDeviceProtocol: 0x00, ++ bMaxPacketSize0: 0x00, ++ idVendor: __constant_cpu_to_le16(CONFIG_OTG_NETWORK_VENDORID), ++ idProduct: __constant_cpu_to_le16(CONFIG_OTG_NETWORK_PRODUCTID), ++ bcdDevice: __constant_cpu_to_le16(CONFIG_OTG_NETWORK_BCDDEVICE), ++}; ++ ++#ifdef CONFIG_OTG_HIGH_SPEED ++/*! High Speed Device Qualifier Descriptor ++ */ ++static struct usbd_device_qualifier_descriptor basic2_device_qualifier_descriptor = { ++ bLength: sizeof(struct usbd_device_qualifier_descriptor), ++ bDescriptorType: USB_DT_DEVICE_QUALIFIER, ++ bcdUSB: __constant_cpu_to_le16(USB_BCD_VERSION), ++ bDeviceClass: COMMUNICATIONS_DEVICE_CLASS, ++ bDeviceSubClass: 0x02, ++ bDeviceProtocol: 0x00, ++ bMaxPacketSize0: 0x00, ++}; ++#endif /* CONFIG_OTG_HIGH_SPEED */ ++ ++/*! Endpoint Request List ++ */ ++static struct usbd_endpoint_request basic2_endpoint_requests[ENDPOINTS+1] = { ++ { 1, 0, 0, USB_DIR_OUT | USB_ENDPOINT_BULK, MAXFRAMESIZE + 48, MAXFRAMESIZE + 512, }, ++ { 1, 0, 0, USB_DIR_IN | USB_ENDPOINT_BULK, MAXFRAMESIZE + 48, MAXFRAMESIZE + 512, }, ++ { 1, 0, 0, USB_DIR_IN | USB_ENDPOINT_INTERRUPT | USB_ENDPOINT_OPT, 16, 64, }, ++ { 0, }, ++}; ++ ++/*! OTG Descriptor ++ */ ++static struct usbd_otg_descriptor basic2_otg_descriptor = { ++ bLength : sizeof(struct usbd_otg_descriptor), ++ bDescriptorType: USB_DT_OTG, ++ bmAttributes: 0, ++}; ++/*! Device Description ++ */ ++struct usbd_device_description basic2_device_description = { ++ device_descriptor: &basic2_device_descriptor, ++#ifdef CONFIG_OTG_HIGH_SPEED ++ device_qualifier_descriptor: &basic2_device_qualifier_descriptor, ++#endif /* CONFIG_OTG_HIGH_SPEED */ ++ otg_descriptor: &basic2_otg_descriptor, ++ iManufacturer: CONFIG_OTG_NETWORK_MANUFACTURER, ++ iProduct: CONFIG_OTG_NETWORK_PRODUCT_NAME, ++#if !defined(CONFIG_OTG_NO_SERIAL_NUMBER) && defined(CONFIG_OTG_SERIAL_NUMBER_STR) ++ iSerialNumber:CONFIG_OTG_SERIAL_NUMBER_STR, ++#endif ++}; ++ ++ ++/*! basic2_init ++ * @param function The function instance ++ */ ++void basic2_init (struct usbd_function_instance *function) ++{ ++ TRACE_MSG0(NTT,"entered"); ++ ++ basic2_comm_alternate_descriptions[0].endpoints = Usb_network_private.have_interrupt ? 1 : 0; ++ ++ TRACE_MSG2(NTT,"alternate: %p endpoints: %d", ++ basic2_data_alternate_descriptions, ++ basic2_data_alternate_descriptions->endpoints ++ ); ++ ++ TRACE_MSG0(NTT,"exited"); ++} ++ ++ ++/*! function driver description ++ */ ++struct usbd_function_driver basic2_function_driver = { ++ name: "network-BASIC2", ++ fops: &net_fd_function_ops, ++ device_description: &basic2_device_description, ++ bNumConfigurations: sizeof (basic2_description) / sizeof (struct usbd_configuration_description), ++ configuration_description: basic2_description, ++ idVendor: __constant_cpu_to_le16(CONFIG_OTG_NETWORK_VENDORID), ++ idProduct: __constant_cpu_to_le16(CONFIG_OTG_NETWORK_PRODUCTID), ++ bcdDevice: __constant_cpu_to_le16(CONFIG_OTG_NETWORK_BCDDEVICE), ++ bNumInterfaces:sizeof (basic2_interfaces) / sizeof (struct usbd_interface_description), ++ interface_list:basic2_interfaces, ++ endpointsRequested: ENDPOINTS, ++ requestedEndpoints: basic2_endpoint_requests, ++}; ++#endif /* CONFIG_OTG_NETWORK_BASIC2 */ ++ +diff -uNr linux/drivers/no-otg/functions/network/blan.c linux/drivers/otg/functions/network/blan.c +--- linux/drivers/no-otg/functions/network/blan.c 1970-01-01 01:00:00.000000000 +0100 ++++ linux/drivers/otg/functions/network/blan.c 2006-09-01 21:41:28.000000000 +0200 +@@ -0,0 +1,403 @@ ++/* ++ * otg/functions/network/blan.c - Network Function Driver ++ * ++ * Copyright (c) 2002, 2003, 2004 Belcarra ++ * ++ * By: ++ * Chris Lynne <cl@belcarra.com> ++ * Stuart Lynne <sl@belcarra.com> ++ * Bruce Balden <balden@belcarra.com> ++ * ++ * 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. ++ * ++ */ ++/*! ++ * @file otg/functions/network/blan.c ++ * @brief This file implements the required descriptors to implement ++ * a BLAN network device with a single interface. ++ * ++ * The BLAN network driver implements the BLAN protocol descriptors. ++ * ++ * The BLAN protocol is designed to support smart devices that want ++ * to create a virtual network between them host and other similiar ++ * devices. ++ * ++ * @ingroup NetworkFunction ++ */ ++ ++ ++#include <otg/otg-compat.h> ++#include <otg/otg-module.h> ++#include <linux/config.h> ++#include <linux/module.h> ++#include <linux/kernel.h> ++#include <linux/version.h> ++ ++#include <linux/slab.h> ++#include <linux/interrupt.h> ++#include <linux/utsname.h> ++#include <linux/netdevice.h> ++ ++#include <otg/usbp-chap9.h> ++#include <otg/usbp-func.h> ++ ++#include "network.h" ++ ++#define NTT network_fd_trace_tag ++ ++#ifdef CONFIG_OTG_NETWORK_BLAN ++/* USB BLAN Configuration ******************************************************************** */ ++ ++/* ++ * BLAN Ethernet Configuration ++ */ ++ ++/* Communication Interface Class descriptors ++ */ ++ ++/*! BULK OUT data endpoint descriptor ++ */ ++static struct usbd_endpoint_descriptor blan_data_1 = { ++ .bLength = sizeof(struct usbd_endpoint_descriptor), ++ .bDescriptorType = USB_DT_ENDPOINT, ++ .bEndpointAddress = USB_DIR_OUT, ++ .bmAttributes = BULK, ++ .wMaxPacketSize = 0, ++ .bInterval = 0, ++}; ++/*! BULK IN data endpoint descriptor ++ */ ++static struct usbd_endpoint_descriptor blan_data_2 =// { 0x07, USB_DT_ENDPOINT, USB_DIR_IN, BULK, 0, 0x00, 0x00, }; ++{ ++ .bLength = sizeof(struct usbd_endpoint_descriptor), ++ .bDescriptorType = USB_DT_ENDPOINT, ++ .bEndpointAddress = USB_DIR_IN, ++ .bmAttributes = BULK, ++ .wMaxPacketSize = 0, ++ .bInterval = 0x0, ++}; ++ ++/*! INTERRUPT IN data endpoint descriptor ++ */ ++static struct usbd_endpoint_descriptor blan_comm_1 = //{ 0x07, USB_DT_ENDPOINT, USB_DIR_IN, INTERRUPT,0, 0x00, 0x0a, }; ++{ ++ .bLength = sizeof(struct usbd_endpoint_descriptor), ++ .bDescriptorType = USB_DT_ENDPOINT, ++ .bEndpointAddress = USB_DIR_IN, ++ .bmAttributes = INTERRUPT, ++ .wMaxPacketSize = 0, ++#if defined (CONFIG_OTG_NETWORK_BLAN_INTERVAL) ++ .bInterval = CONFIG_OTG_NETWORK_BLAN_INTERVAL, ++#else ++ .bInterval = 0x08, ++#endif /* defined (CONFIG_OTG_NETWORK_BLAN_INTERVAL) */ ++}; ++ ++//static u8 blan_class_1[] = { 0x05, CS_INTERFACE, USB_ST_HEADER, 0x10, 0x01, /* CLASS_BDC_VERSION, CLASS_BDC_VERSION */ }; ++static struct usbd_class_header_function_descriptor blan_class_1 = { ++ .bFunctionLength = 0x05, /* Length */ ++ .bDescriptorType = CS_INTERFACE, ++ .bDescriptorSubtype = USB_ST_HEADER, ++ .bcdCDC = __constant_cpu_to_le16(0x0110) /*Version */ ++}; ++//static u8 blan_class_2[] = { 0x15, CS_INTERFACE, USB_ST_MDLM, 0x00, 0x01, /* bcdVersion, bcdVersion */ ++// 0x74, 0xf0, 0x3d, 0xbd, 0x1e, 0xc1, 0x44, 0x70, /* bGUID */ ++static struct usbd_class_mdlm_descriptor blan_class_2 = { ++ .bFunctionLength = 0x15, ++ .bDescriptorType = CS_INTERFACE, ++ .bDescriptorSubtype = USB_ST_MDLM, ++ .bcdVersion = __constant_cpu_to_le16(0x0100), ++ .bGUID = { ++ 0x74, 0xf0, 0x3d, 0xbd, 0x1e, 0xc1, 0x44, 0x70, /* bGUID */ ++ 0xa3, 0x67, 0x71, 0x34, 0xc9, 0xf5, 0x54, 0x37, /* bGUID */ }, ++}; ++ ++ ++ ++//static u8 blan_class_3[] = { 0x07, CS_INTERFACE, USB_ST_MDLMD, 0x01, 0x00, 0x00, 0x00, }; ++static struct usbd_class_blan_descriptor blan_class_3 = { ++ .bFunctionLength = 0x07, ++ .bDescriptorType = CS_INTERFACE, ++ .bDescriptorSubtype = USB_ST_MDLMD, ++ .bGuidDescriptorType = 0x01, ++ .bmNetworkCapabilities = 0x00, ++ .bmDataCapabilities = 0x00, ++ .bPad = 0x00, ++}; ++ ++//static u8 blan_class_4[] = { 0x0d, CS_INTERFACE, USB_ST_ENF, ++// 0x00, 0x00, 0x00, 0x00, 0x00, 0xea, 0x05, /* 1514 maximum frame size */ ++// 0x00, 0x00, 0x00 , }; ++static struct usbd_class_ethernet_networking_descriptor blan_class_4 = { ++ .bFunctionLength = 0x0d, ++ .bDescriptorType = CS_INTERFACE, ++ .bDescriptorSubtype = USB_ST_ENF, ++ .iMACAddress = 0x00, ++ .bmEthernetStatistics = 0x00, ++ .wMaxSegmentSize = 0x05ea, /* 1514 maximum frame size */ ++ .wNumberMCFilters = 0x00, ++ .bNumberPowerFilters = 0x00 , ++}; ++ ++//static u8 blan_class_5[] = { 0x07, CS_INTERFACE, USB_ST_NCT, 0x00, 0x00, 0x00, 0x00, }; ++static struct usbd_class_network_channel_descriptor blan_class_5 = { ++ .bFunctionLength = 0x07, ++ .bDescriptorType = CS_INTERFACE, ++ .bDescriptorSubtype = USB_ST_NCT, ++ .bEntityId = 0, ++ .iName = 0, ++ .bChannelIndex = 0, ++ .bPhysicalInterface = 0, ++}; ++ ++ ++/*! Endpoint descriptor list ++ */ ++static struct usbd_endpoint_descriptor *blan_alt_endpoints[] = { ++ (struct usbd_endpoint_descriptor *) &blan_data_1, ++ (struct usbd_endpoint_descriptor *) &blan_data_2, ++ (struct usbd_endpoint_descriptor *) &blan_comm_1, }; ++ ++u8 blan_alt_indexes[] = { BULK_OUT, BULK_IN, INT_IN, }; ++ ++static struct usbd_generic_class_descriptor *blan_comm_class_descriptors[] = { ++ (struct usbd_generic_class_descriptor *) &blan_class_1, ++ (struct usbd_generic_class_descriptor *) &blan_class_2, ++ (struct usbd_generic_class_descriptor *) &blan_class_3, ++ (struct usbd_generic_class_descriptor *) &blan_class_4, ++ (struct usbd_generic_class_descriptor *) &blan_class_5, }; ++ ++ ++/*! Data Interface Descriptor ++ */ ++static struct usbd_interface_descriptor blan_alternate_descriptor = { ++ .bLength = 0x09, ++ .bDescriptorType = USB_DT_INTERFACE, ++ .bInterfaceNumber = 0x00, ++ .bAlternateSetting = 0x00, // bInterfaceNumber, bAlternateSetting ++ .bNumEndpoints = sizeof (blan_alt_endpoints) / sizeof(struct usbd_endpoint_descriptor *), // bNumEndpoints ++ .bInterfaceClass = COMMUNICATIONS_INTERFACE_CLASS, ++ .bInterfaceSubClass = COMMUNICATIONS_MDLM_SUBCLASS, ++ .bInterfaceProtocol = COMMUNICATIONS_NO_PROTOCOL, ++ .iInterface = 0x00, ++}; ++ ++/*! Data Alternate Interface Description List ++ */ ++static struct usbd_alternate_description blan_alternate_descriptions[] = { ++ { ++ .iInterface = CONFIG_OTG_NETWORK_BLAN_INTF, ++ .interface_descriptor = (struct usbd_interface_descriptor *)&blan_alternate_descriptor, ++ .classes = sizeof (blan_comm_class_descriptors) / sizeof (struct usbd_generic_class_descriptor *), ++ .class_list = blan_comm_class_descriptors, ++ .endpoints = sizeof (blan_alt_endpoints) / sizeof(struct usbd_endpoint_descriptor *), ++ .endpoint_list = blan_alt_endpoints, ++ .endpoint_indexes = blan_alt_indexes, ++ }, ++}; ++/* Interface descriptions and descriptors ++ */ ++/*! Interface Description List ++ */ ++static struct usbd_interface_description blan_interfaces[] = { ++ { ++ .alternates = sizeof (blan_alternate_descriptions) / sizeof (struct usbd_alternate_description), ++ .alternate_list = blan_alternate_descriptions, ++ }, ++}; ++ ++ ++/* Configuration descriptions and descriptors ++ */ ++ ++/*! Configuration Descriptor ++ */ ++static struct usbd_configuration_descriptor blan_configuration_descriptor = { ++ .bLength = 0x09, ++ .bDescriptorType = USB_DT_CONFIGURATION, ++ .wTotalLength = 0x00, // wLength ++ .bNumInterfaces = sizeof (blan_interfaces) / sizeof (struct usbd_interface_description), ++ .bConfigurationValue = 0x01, ++ .iConfiguration = 0x00, // bConfigurationValue, iConfiguration ++ .bmAttributes = 0, ++ .bMaxPower = 0, ++}; ++ ++/*! Configuration Description List ++ */ ++struct usbd_configuration_description blan_description[] = { ++ { ++ .iConfiguration = CONFIG_OTG_NETWORK_BLAN_DESC, ++ .configuration_descriptor = &blan_configuration_descriptor, ++ }, ++}; ++ ++/* Device Description ++ */ ++ ++/*! Device Descriptor ++ */ ++static struct usbd_device_descriptor blan_device_descriptor = { ++ .bLength = sizeof(struct usbd_device_descriptor), ++ .bDescriptorType = USB_DT_DEVICE, ++ .bcdUSB = __constant_cpu_to_le16(USB_BCD_VERSION), ++ .bDeviceClass = COMMUNICATIONS_DEVICE_CLASS, ++ .bDeviceSubClass = 0x02, ++ .bDeviceProtocol = 0x00, ++ .bMaxPacketSize0 = 0x00, ++ .idVendor = __constant_cpu_to_le16(CONFIG_OTG_NETWORK_VENDORID), ++ .idProduct = __constant_cpu_to_le16(CONFIG_OTG_NETWORK_PRODUCTID), ++ .bcdDevice = __constant_cpu_to_le16(CONFIG_OTG_NETWORK_BCDDEVICE), ++}; ++ ++#ifdef CONFIG_OTG_HIGH_SPEED ++/*! High Speed Device Qualifier Descriptor ++ */ ++static struct usbd_device_qualifier_descriptor blan_device_qualifier_descriptor = { ++ .bLength = sizeof(struct usbd_device_qualifier_descriptor), ++ .bDescriptorType = USB_DT_DEVICE_QUALIFIER, ++ .bcdUSB = __constant_cpu_to_le16(USB_BCD_VERSION), ++ .bDeviceClass = COMMUNICATIONS_DEVICE_CLASS, ++ .bDeviceSubClass = 0x02, ++ .bDeviceProtocol = 0x00, ++ .bMaxPacketSize0 = 0x00, ++}; ++#endif /* CONFIG_OTG_HIGH_SPEED */ ++ ++/*! Endpoint Request List ++ */ ++static struct usbd_endpoint_request blan_endpoint_requests[ENDPOINTS+1] = { ++ { 1, 0, 0, USB_DIR_OUT | USB_ENDPOINT_BULK, MAXFRAMESIZE + 48, MAXFRAMESIZE + 512, }, ++ { 1, 0, 0, USB_DIR_IN | USB_ENDPOINT_BULK, MAXFRAMESIZE + 48, MAXFRAMESIZE + 512, }, ++ { 1, 0, 0, USB_DIR_IN | USB_ENDPOINT_INTERRUPT | USB_ENDPOINT_OPT, 16, 64, }, ++ { 0, }, ++}; ++ ++/*! OTG Descriptor ++ */ ++static struct usbd_otg_descriptor blan_otg_descriptor = { ++ .bLength = sizeof(struct usbd_otg_descriptor), ++ .bDescriptorType = USB_DT_OTG, ++ .bmAttributes = 0, ++}; ++ ++/*! Device Description ++ */ ++struct usbd_device_description blan_device_description = { ++ .device_descriptor = &blan_device_descriptor, ++#ifdef CONFIG_OTG_HIGH_SPEED ++ .device_qualifier_descriptor = &blan_device_qualifier_descriptor, ++#endif /* CONFIG_OTG_HIGH_SPEED */ ++ .otg_descriptor = &blan_otg_descriptor, ++ .iManufacturer = CONFIG_OTG_NETWORK_MANUFACTURER, ++ .iProduct = CONFIG_OTG_NETWORK_PRODUCT_NAME, ++#if !defined(CONFIG_OTG_NO_SERIAL_NUMBER) && defined(CONFIG_OTG_SERIAL_NUMBER_STR) ++ .iSerialNumber = CONFIG_OTG_SERIAL_NUMBER_STR, ++#endif ++}; ++ ++ ++/*! blan_init ++ * @param function The function instance ++ */ ++void blan_init (struct usbd_function_instance *function) ++{ ++ struct usbd_class_ethernet_networking_descriptor *ethernet; ++ struct usbd_class_network_channel_descriptor *channel; ++ ++ int len = 0; ++ char buf[255]; ++ ++ buf[0] = 0; ++ ++ blan_alternate_descriptions[0].endpoints = Usb_network_private.have_interrupt ? 3 : 2; ++ ++ // Update the iMACAddress field in the ethernet descriptor ++ { ++#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,4,17) ++ char address_str[14]; ++ snprintf(address_str, 13, "%02x%02x%02x%02x%02x%02x", ++ local_dev_addr[0], local_dev_addr[1], local_dev_addr[2], ++ local_dev_addr[3], local_dev_addr[4], local_dev_addr[5]); ++#else ++ char address_str[20]; ++ sprintf(address_str, "%02x%02x%02x%02x%02x%02x", ++ local_dev_addr[0], local_dev_addr[1], local_dev_addr[2], ++ local_dev_addr[3], local_dev_addr[4], local_dev_addr[5]); ++#endif ++ TRACE_MSG0(NTT,"alloc mac string"); ++ if ((ethernet = &blan_class_4)) ++ ethernet->iMACAddress = usbd_alloc_string(address_str); ++ TRACE_MSG0(NTT,"alloc mac string done"); ++ } ++ TRACE_MSG0(NTT,"alloc channel string"); ++ if ((channel = &blan_class_5)) ++ channel->iName = usbd_alloc_string(system_utsname.nodename); ++ TRACE_MSG0(NTT,"alloc channel string done"); ++ ++#ifdef CONFIG_OTG_NETWORK_BLAN_PADBYTES ++ blan_class_3.bPad = CONFIG_OTG_NETWORK_BLAN_PADBYTES; ++ len += sprintf(buf + len, "PADBYTES: %02x ", blan_class_3.bPad); ++#endif ++#ifdef CONFIG_OTG_NETWORK_BLAN_PADBEFORE ++ blan_class_3.bmDataCapabilities |= BMDATA_PADBEFORE; ++ len += sprintf(buf + len, "PADBEFORE: %02x ", blan_class_3.bmDataCapabilities); ++#endif ++#ifdef CONFIG_OTG_NETWORK_BLAN_PADAFTER ++ blan_class_3.bmDataCapabilities |= BMDATA_PADAFTER; ++ len += sprintf(buf + len, "PADAFTER: %02x ", blan_class_3.bmDataCapabilities); ++#endif ++#ifdef CONFIG_OTG_NETWORK_BLAN_CRC ++ blan_class_3.bmDataCapabilities |= BMDATA_CRC; ++ len += sprintf(buf + len, "CRC: %02x ", blan_class_3.bmDataCapabilities); ++#endif ++#ifdef CONFIG_OTG_NETWORK_BLAN_FERMAT ++ blan_class_3.bmDataCapabilities |= BMDATA_FERMAT; ++ len += sprintf(buf + len, "FERMAT: %02x ", blan_class_3.bmDataCapabilities); ++#endif ++#ifdef CONFIG_OTG_NETWORK_BLAN_HOSTNAME ++ blan_class_3.bmDataCapabilities |= BMDATA_HOSTNAME; ++ len += sprintf(buf + len, "HOSTNAME: %02x ", blan_class_3.bmDataCapabilities); ++#endif ++#ifdef CONFIG_OTG_NETWORK_BLAN_NONBRIDGED ++ blan_class_3.bmNetworkCapabilities |= BMNETWORK_NONBRIDGED; ++ len += sprintf(buf + len, "NONBRIDGE: %02x ", blan_class_3.bmNetworkCapabilities); ++#endif ++#ifdef CONFIG_OTG_NETWORK_BLAN_DATA_NOTIFY_OK ++ blan_class_3.bmNetworkCapabilities |= BMNETWORK_DATA_NOTIFY_OK; ++ len += sprintf(buf + len, "DATA NOTIFY: %02x ", blan_class_3.bmNetworkCapabilities); ++#endif ++ if (strlen(buf)) ++ TRACE_MSG1(NTT,"%s", buf); ++} ++ ++/*! function driver description ++ */ ++struct usbd_function_driver blan_function_driver = { ++ .name = "network-BLAN", ++ .fops = &net_fd_function_ops, ++ .device_description = &blan_device_description, ++ .bNumConfigurations = sizeof (blan_description) / sizeof (struct usbd_configuration_description), ++ .configuration_description = blan_description, ++ .idVendor = __constant_cpu_to_le16(CONFIG_OTG_NETWORK_VENDORID), ++ .idProduct = __constant_cpu_to_le16(CONFIG_OTG_NETWORK_PRODUCTID), ++ .bcdDevice = __constant_cpu_to_le16(CONFIG_OTG_NETWORK_BCDDEVICE), ++ .bNumInterfaces = sizeof (blan_interfaces) / sizeof (struct usbd_interface_description), ++ .interface_list = blan_interfaces, ++ .endpointsRequested = ENDPOINTS, ++ .requestedEndpoints = blan_endpoint_requests, ++}; ++#endif /* CONFIG_OTG_NETWORK_BLAN */ ++ +diff -uNr linux/drivers/no-otg/functions/network/cdc.c linux/drivers/otg/functions/network/cdc.c +--- linux/drivers/no-otg/functions/network/cdc.c 1970-01-01 01:00:00.000000000 +0100 ++++ linux/drivers/otg/functions/network/cdc.c 2006-09-01 21:41:28.000000000 +0200 +@@ -0,0 +1,376 @@ ++/* ++ * otg/functions/network/cdc.c - Network Function Driver ++ * ++ * Copyright (c) 2002, 2003, 2004 Belcarra ++ * ++ * By: ++ * Chris Lynne <cl@belcarra.com> ++ * Stuart Lynne <sl@belcarra.com> ++ * Bruce Balden <balden@belcarra.com> ++ * ++ * 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 CDC network driver implements the standard CDC Ethernet descriptors. ++ * ++ * The CDC protocol is suitable for infrastructure devices that ++ * are implementing a bridged or routed connection betwen an ++ * external network and the USB Host. ++ * ++ */ ++/*! ++ * @file otg/functions/network/cdc.c ++ * @brief This file implements the required descriptors to implement ++ * a CDC network device with two interfaces. ++ * ++ * @ingroup NetworkFunction ++ */ ++ ++#include <otg/otg-compat.h> ++#include <otg/otg-module.h> ++ ++#include <linux/interrupt.h> ++#include <linux/utsname.h> ++#include <linux/netdevice.h> ++ ++#include <otg/usbp-chap9.h> ++#include <otg/usbp-func.h> ++ ++#include "network.h" ++ ++#define NTT network_fd_trace_tag ++ ++ ++#ifdef CONFIG_OTG_NETWORK_CDC ++/* USB CDC Configuration ********************************************************************* */ ++ ++/* CDC Communication Interface Class descriptors ++ */ ++/*! BULK OUT data endpoint descriptor ++ */ ++static struct usbd_endpoint_descriptor cdc_data_1 = { ++ bLength: 0x07, ++ bDescriptorType: USB_DT_ENDPOINT, ++ bEndpointAddress: USB_DIR_OUT, ++ bmAttributes: BULK, ++ wMaxPacketSize: __constant_cpu_to_le16(0x00), ++ bInterval: 0x00, ++}; ++ ++/*! BULK IN data endpoint descriptor ++ */ ++static struct usbd_endpoint_descriptor cdc_data_2 = { ++ bLength: 0x07, ++ bDescriptorType: USB_DT_ENDPOINT, ++ bEndpointAddress: USB_DIR_IN, ++ bmAttributes: BULK, ++ wMaxPacketSize: __constant_cpu_to_le16(0x00), ++ bInterval: 0x00, ++}; ++ ++/*! INTERRUPT IN data endpoint descriptor ++ */ ++static struct usbd_endpoint_descriptor cdc_comm_1 = { ++ bLength: 0x07, ++ bDescriptorType: USB_DT_ENDPOINT, ++ bEndpointAddress: USB_DIR_IN, ++ bmAttributes: INTERRUPT, ++ wMaxPacketSize: __constant_cpu_to_le16(0x00), ++ bInterval: 0x0a, ++}; ++ ++ ++ ++ ++//static u8 cdc_class_1[] = { 0x05, CS_INTERFACE, USB_ST_HEADER, 0x10, 0x01, /* CLASS_BDC_VERSION, CLASS_BDC_VERSION */ }; ++ ++static struct usbd_class_header_function_descriptor cdc_class_1 = { ++ bFunctionLength: 0x05, ++ bDescriptorType: CS_INTERFACE, ++ bDescriptorSubtype: USB_ST_HEADER, ++ bcdCDC: __constant_cpu_to_le16(0x0110), ++}; ++ ++ ++//static u8 cdc_class_2[] = { ++// 0x0d, CS_INTERFACE, USB_ST_ENF, 0x00, 0x00, 0x00, 0x00, 0x00, 0xea, 0x05, /* 1514 maximum frame size */ ++// 0x00, 0x00, 0x00 , }; ++static struct usbd_class_ethernet_networking_descriptor cdc_class_2 = { ++ bFunctionLength: 0x0D, ++ bDescriptorType: CS_INTERFACE, ++ bDescriptorSubtype: USB_ST_HEADER, ++ iMACAddress: 0x00, ++ bmEthernetStatistics: 0x00, ++ wMaxSegmentSize: __constant_cpu_to_le16(0x05ea), ++ wNumberMCFilters: 0, ++ bNumberPowerFilters: 0, ++}; ++ ++//static u8 cdc_class_3[] = { 0x05, CS_INTERFACE, USB_ST_UF, 0x00, 0x01, /* bMasterInterface, bSlaveInterface */}; ++ ++static struct usbd_class_union_function_descriptor cdc_class_3 = ++{ ++ bFunctionLength: 0x05, ++ bDescriptorType: CS_INTERFACE, ++ bDescriptorSubtype: USB_ST_UF, ++ bMasterInterface: 0x00, ++ bSlaveInterface0: {1}, ++ ++}; ++ ++static usbd_class_descriptor_t *cdc_comm_class_descriptors[] = { ++ (struct usbd_generic_class_descriptor *) &cdc_class_1, ++ (struct usbd_generic_class_descriptor *) &cdc_class_2, ++ (struct usbd_generic_class_descriptor *) &cdc_class_3, ++}; ++ ++/*! Endpoint descriptor list ++ */ ++static usbd_endpoint_descriptor_t *cdc_data_endpoints[] = { ++ (struct usbd_endpoint_descriptor *) &cdc_data_1, ++ (struct usbd_endpoint_descriptor *) &cdc_data_2, ++}; ++static usbd_endpoint_descriptor_t *cdc_comm_endpoints[] = { ++ (usbd_endpoint_descriptor_t *) &cdc_comm_1, }; ++ ++u8 cdc_comm_indexes[] = { INT_IN, }; ++u8 cdc_data_indexes[] = { BULK_OUT, BULK_IN, }; ++ ++ ++/*! Comm Interface Descriptor ++ */ ++static struct usbd_interface_descriptor cdc_comm_alternate_descriptor = { ++ bLength: sizeof(struct usbd_interface_descriptor), ++ bDescriptorType: USB_DT_INTERFACE, ++ bInterfaceNumber: 0x00, ++ bAlternateSetting: 0x00, ++ bNumEndpoints: sizeof(cdc_comm_endpoints)/sizeof(usbd_endpoint_descriptor_t *), ++ bInterfaceClass: COMMUNICATIONS_INTERFACE_CLASS, ++ bInterfaceSubClass: COMMUNICATIONS_ENCM_SUBCLASS, ++ bInterfaceProtocol: COMMUNICATIONS_NO_PROTOCOL, ++ iInterface: 0x00 ++}; ++ ++/*! No Data Interface Descriptor ++ */ ++static struct usbd_interface_descriptor cdc_nodata_alternate_descriptor = { ++ bLength: sizeof(struct usbd_interface_descriptor), ++ bDescriptorType: USB_DT_INTERFACE, ++ bInterfaceNumber: 0x01, ++ bAlternateSetting: 0x00, ++ bNumEndpoints: 0, ++ bInterfaceClass: DATA_INTERFACE_CLASS, ++ bInterfaceSubClass: COMMUNICATIONS_NO_SUBCLASS, ++ bInterfaceProtocol: COMMUNICATIONS_NO_PROTOCOL, ++ iInterface: 0x00 ++}; ++ ++/*! Data Interface Descriptor ++ */ ++static struct usbd_interface_descriptor cdc_data_alternate_descriptor = { ++ bLength: sizeof(struct usbd_interface_descriptor), ++ bDescriptorType: USB_DT_INTERFACE, ++ bInterfaceNumber: 0x01, ++ bAlternateSetting: 0x01, ++ bNumEndpoints: sizeof(cdc_comm_endpoints)/sizeof(usbd_endpoint_descriptor_t *), ++ bInterfaceClass: DATA_INTERFACE_CLASS, ++ bInterfaceSubClass: COMMUNICATIONS_NO_SUBCLASS, ++ bInterfaceProtocol: COMMUNICATIONS_NO_PROTOCOL, ++ iInterface: 0x00 ++}; ++ ++/*! Comm Alternate Interface Description List ++ */ ++static struct usbd_alternate_description cdc_comm_alternate_descriptions[] = { ++ { ++ iInterface: CONFIG_OTG_NETWORK_CDC_COMM_INTF, ++ interface_descriptor: &cdc_comm_alternate_descriptor, ++ classes:sizeof (cdc_comm_class_descriptors) / sizeof (struct usbd_generic_class_descriptor *), ++ class_list: cdc_comm_class_descriptors, ++ ++ endpoints:sizeof (cdc_comm_endpoints) / sizeof(struct usbd_endpoint_descriptor *), ++ endpoint_list: cdc_comm_endpoints, ++ endpoint_indexes: cdc_comm_indexes, ++ }, ++}; ++ ++ ++/*! Data Alternate Interface Description List ++ */ ++static struct usbd_alternate_description cdc_data_alternate_descriptions[] = { ++ { ++ iInterface: CONFIG_OTG_NETWORK_CDC_NODATA_INTF, ++ interface_descriptor: (struct usbd_interface_descriptor *)&cdc_nodata_alternate_descriptor, ++ }, ++ { ++ iInterface: CONFIG_OTG_NETWORK_CDC_DATA_INTF, ++ interface_descriptor: &cdc_data_alternate_descriptor, ++ endpoints:sizeof (cdc_data_endpoints) / sizeof(struct usbd_endpoint_descriptor *), ++ endpoint_list: cdc_data_endpoints, ++ endpoint_indexes: cdc_data_indexes, ++ }, ++}; ++ ++/* Interface descriptions and descriptors ++ */ ++/*! Interface Description List ++ */ ++static struct usbd_interface_description cdc_interfaces[] = { ++ { ++ alternates:sizeof (cdc_comm_alternate_descriptions) / sizeof (struct usbd_alternate_description), ++ alternate_list:cdc_comm_alternate_descriptions, ++ }, ++ { ++ alternates:sizeof (cdc_data_alternate_descriptions) / sizeof (struct usbd_alternate_description), ++ alternate_list:cdc_data_alternate_descriptions, ++ }, ++}; ++ ++ ++/* Configuration descriptions and descriptors ++ */ ++ ++/*! Configuration Descriptor ++ */ ++static struct usbd_configuration_descriptor cdc_configuration_descriptor = { ++ bLength: sizeof(struct usbd_configuration_descriptor), ++ bDescriptorType: USB_DT_CONFIGURATION, ++ wTotalLength:0, ++ bNumInterfaces:sizeof(cdc_interfaces)/sizeof(struct usbd_interface_description *), ++ bConfigurationValue: 0x01, ++ iConfiguration: 0x00, ++ bmAttributes:0, ++ bMaxPower:0, ++}; ++ ++/*! Configuration Description List ++ */ ++static struct usbd_configuration_description cdc_description[] = { ++ { iConfiguration: CONFIG_OTG_NETWORK_CDC_DESC, ++ configuration_descriptor: &cdc_configuration_descriptor, ++ }, ++}; ++ ++ ++ ++/* Device Description ++ */ ++ ++/*! Device Descriptor ++ */ ++static struct usbd_device_descriptor cdc_device_descriptor = { ++ bLength: sizeof(struct usbd_device_descriptor), ++ bDescriptorType: USB_DT_DEVICE, ++ bcdUSB: __constant_cpu_to_le16(USB_BCD_VERSION), ++ bDeviceClass: COMMUNICATIONS_DEVICE_CLASS, ++ bDeviceSubClass: 0x02, ++ bDeviceProtocol: 0x00, ++ bMaxPacketSize0: 0x00, ++ idVendor: __constant_cpu_to_le16(CONFIG_OTG_NETWORK_VENDORID), ++ idProduct: __constant_cpu_to_le16(CONFIG_OTG_NETWORK_PRODUCTID), ++ bcdDevice: __constant_cpu_to_le16(CONFIG_OTG_NETWORK_BCDDEVICE), ++}; ++ ++#ifdef CONFIG_OTG_HIGH_SPEED ++/*! High Speed Device Qualifier Descriptor ++ */ ++static struct usbd_device_qualifier_descriptor cdc_device_qualifier_descriptor = { ++ bLength: sizeof(struct usbd_device_qualifier_descriptor), ++ bDescriptorType: USB_DT_DEVICE_QUALIFIER, ++ bcdUSB: __constant_cpu_to_le16(USB_BCD_VERSION), ++ bDeviceClass: COMMUNICATIONS_DEVICE_CLASS, ++ bDeviceSubClass: 0x02, ++ bDeviceProtocol: 0x00, ++ bMaxPacketSize0: 0x00, ++}; ++#endif /* CONFIG_OTG_HIGH_SPEED */ ++ ++/*! Endpoint Request List ++ */ ++static struct usbd_endpoint_request cdc_endpoint_requests[ENDPOINTS+1] = { ++ { 1, 1, 1, USB_DIR_OUT | USB_ENDPOINT_BULK, MAXFRAMESIZE + 48, MAXFRAMESIZE + 512, }, ++ { 1, 1, 1, USB_DIR_IN | USB_ENDPOINT_BULK, MAXFRAMESIZE + 48, MAXFRAMESIZE + 512, }, ++ { 1, 0, 0, USB_DIR_IN | USB_ENDPOINT_INTERRUPT | USB_ENDPOINT_OPT, 16, 64, }, ++ { 0, }, ++}; ++ ++/*! OTG Descriptor ++ */ ++static struct usbd_otg_descriptor cdc_otg_descriptor = { ++ bLength : sizeof(struct usbd_otg_descriptor), ++ bDescriptorType: USB_DT_OTG, ++ bmAttributes: 0, ++}; ++ ++/*! Device Description ++ */ ++struct usbd_device_description cdc_device_description = { ++ device_descriptor: &cdc_device_descriptor, ++#ifdef CONFIG_OTG_HIGH_SPEED ++ device_qualifier_descriptor: &cdc_device_qualifier_descriptor, ++#endif /* CONFIG_OTG_HIGH_SPEED */ ++ otg_descriptor: &cdc_otg_descriptor, ++ iManufacturer: CONFIG_OTG_NETWORK_MANUFACTURER, ++ iProduct: CONFIG_OTG_NETWORK_PRODUCT_NAME, ++#if !defined(CONFIG_OTG_NO_SERIAL_NUMBER) && defined(CONFIG_OTG_SERIAL_NUMBER_STR) ++ iSerialNumber:CONFIG_OTG_SERIAL_NUMBER_STR, ++#endif ++}; ++ ++ ++ ++/*! cdc_init ++ * @param function The function instance ++ */ ++void cdc_init (struct usbd_function_instance *function) ++{ ++ struct usbd_class_ethernet_networking_descriptor *ethernet; ++ ++ cdc_comm_alternate_descriptions[0].endpoints = Usb_network_private.have_interrupt ? 1 : 0; ++ ++ // Update the iMACAddress field in the ethernet descriptor ++ { ++ char address_str[14]; ++ snprintf(address_str, 13, "%02x%02x%02x%02x%02x%02x", ++ remote_dev_addr[0], remote_dev_addr[1], remote_dev_addr[2], ++ remote_dev_addr[3], remote_dev_addr[4], remote_dev_addr[5]); ++ ++ if ((ethernet = &cdc_class_2)) { ++ if (ethernet->iMACAddress) { ++ usbd_free_string_descriptor(ethernet->iMACAddress); ++ } ++ ethernet->iMACAddress = usbd_alloc_string(address_str); ++ } ++ } ++} ++ ++ ++/*! function driver description ++ */ ++struct usbd_function_driver cdc_function_driver = { ++ name: "network-CDC", ++ fops: &net_fd_function_ops, ++ device_description: &cdc_device_description, ++ bNumConfigurations: sizeof (cdc_description) / sizeof (struct usbd_configuration_description), ++ configuration_description: cdc_description, ++ idVendor: __constant_cpu_to_le16(CONFIG_OTG_NETWORK_VENDORID), ++ idProduct: __constant_cpu_to_le16(CONFIG_OTG_NETWORK_PRODUCTID), ++ bcdDevice: __constant_cpu_to_le16(CONFIG_OTG_NETWORK_BCDDEVICE), ++ bNumInterfaces:sizeof (cdc_interfaces) / sizeof (struct usbd_interface_description), ++ interface_list:cdc_interfaces, ++ endpointsRequested: ENDPOINTS, ++ requestedEndpoints: cdc_endpoint_requests, ++}; ++#endif /* CONFIG_OTG_NETWORK_CDC */ ++ +diff -uNr linux/drivers/no-otg/functions/network/eem.c linux/drivers/otg/functions/network/eem.c +--- linux/drivers/no-otg/functions/network/eem.c 1970-01-01 01:00:00.000000000 +0100 ++++ linux/drivers/otg/functions/network/eem.c 2006-09-01 21:41:28.000000000 +0200 +@@ -0,0 +1,247 @@ ++/* ++ * otg/functions/network/eem.c - Network Function Driver ++ * ++ * Copyright (c) 2002, 2003, 2004 Belcarra ++ * ++ * By: ++ * Chris Lynne <cl@belcarra.com> ++ * Stuart Lynne <sl@belcarra.com> ++ * Bruce Balden <balden@belcarra.com> ++ * ++ * 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. ++ * ++ */ ++/*! ++ * @file otg/functions/network/eem.c ++ * @brief This file implements the required descriptors to implement ++ * a eem network device with a single interface. ++ * ++ * The BASIC network driver implements a very simple descriptor set. ++ * A single interface with two BULK data endpoints and a optional ++ * INTERRUPT endpoint. ++ * ++ * @ingroup NetworkFunction ++ */ ++ ++ ++#include <otg/otg-compat.h> ++#include <otg/otg-module.h> ++ ++#include <linux/slab.h> ++#include <linux/interrupt.h> ++#include <linux/utsname.h> ++#include <linux/netdevice.h> ++ ++#include <otg/usbp-chap9.h> ++#include <otg/usbp-func.h> ++ ++#include "network.h" ++ ++#define NTT network_fd_trace_tag ++ ++#ifdef CONFIG_OTG_NETWORK_BASIC ++/* USB BASIC Configuration ******************************************************************** */ ++ ++/* BASIC Communication Interface Class descriptors ++ */ ++/*! BULK OUT data endpoint descriptor ++ */ ++static struct usbd_endpoint_descriptor eem_data_1 = { ++ bLength:sizeof(struct usbd_endpoint_descriptor), ++ bDescriptorType:USB_DT_ENDPOINT, ++ bEndpointAddress:USB_DIR_OUT, ++ bmAttributes: BULK, ++ wMaxPacketSize: 0, ++ bInterval: 0, ++}; ++/*! BULK IN data endpoint descriptor ++ */ ++static struct usbd_endpoint_descriptor eem_data_2 =// { 0x07, USB_DT_ENDPOINT, USB_DIR_IN, BULK, 0, 0x00, 0x00, }; ++{ ++ bLength:sizeof(struct usbd_endpoint_descriptor), ++ bDescriptorType:USB_DT_ENDPOINT, ++ bEndpointAddress:USB_DIR_IN, ++ bmAttributes: BULK, ++ wMaxPacketSize: 0, ++ bInterval: 0x0, ++}; ++ ++/*! INTERRUPT IN data endpoint descriptor ++ */ ++static struct usbd_endpoint_descriptor eem_comm_1 = //{ 0x07, USB_DT_ENDPOINT, USB_DIR_IN, INTERRUPT,0, 0x00, 0x0a, }; ++{ ++ bLength:sizeof(struct usbd_endpoint_descriptor), ++ bDescriptorType:USB_DT_ENDPOINT, ++ bEndpointAddress:USB_DIR_IN, ++ bmAttributes: INTERRUPT, ++ wMaxPacketSize: 0, ++ bInterval: 0xa, ++}; ++ ++/*! Endpoint descriptor list ++ */ ++static struct usbd_endpoint_descriptor *eem_default[] = { &eem_data_1, ++ &eem_data_2, ++ &eem_comm_1, }; ++u8 eem_indexes[] = { BULK_OUT, BULK_IN, INT_IN, }; ++ ++/*! Data Interface Descriptor ++ */ ++static struct usbd_interface_descriptor eem_data_alternate_descriptor = { ++ bLength:sizeof(struct usbd_interface_descriptor), ++ bDescriptorType:USB_DT_INTERFACE, ++ bInterfaceNumber: 0x00, ++ bAlternateSetting: 0x00, // bInterfaceNumber, bAlternateSetting ++ sizeof (eem_default) / sizeof(struct usbd_endpoint_descriptor *), // bNumEndpoints ++ bInterfaceClass:LINEO_CLASS, ++ bInterfaceSubClass:LINEO_SUBCLASS_BASIC_NET, ++ bInterfaceProtocol:LINEO_BASIC_NET_CRC, ++ iInterface:0x00, ++}; ++ ++/*! Data Alternate Interface Description List ++ */ ++static struct usbd_alternate_description eem_data_alternate_descriptions[] = { ++ { iInterface: CONFIG_OTG_NETWORK_BASIC_INTF, ++ interface_descriptor: (struct usbd_interface_descriptor *)&eem_data_alternate_descriptor, ++ endpoints:sizeof (eem_default) / sizeof(struct usbd_endpoint_descriptor *), ++ endpoint_list: eem_default, ++ endpoint_indexes: eem_indexes, ++ }, ++}; ++ ++ ++/* BASIC Data Interface Alternate descriptions and descriptors ++ */ ++ ++/* BASIC Interface descriptions and descriptors ++ */ ++/*! Interface Description List ++ */ ++struct usbd_interface_description eem_interfaces[] = { ++ { alternates:sizeof (eem_data_alternate_descriptions) / sizeof (struct usbd_alternate_description), ++ alternate_list:eem_data_alternate_descriptions,}, ++}; ++ ++/* BASIC Configuration descriptions and descriptors ++ */ ++/*! Configuration Descriptor ++ */ ++u8 eem_configuration_descriptor[sizeof(struct usbd_configuration_descriptor)] = { ++ 0x09, USB_DT_CONFIGURATION, 0x00, 0x00, // wLength ++ sizeof (eem_interfaces) / sizeof (struct usbd_interface_description), ++ 0x01, 0x00, // bConfigurationValue, iConfiguration ++ 0, 0, ++}; ++ ++/*! Configuration Description List ++ */ ++struct usbd_configuration_description eem_description[] = { ++ { iConfiguration: CONFIG_OTG_NETWORK_BASIC_DESC, ++ configuration_descriptor: (struct usbd_configuration_descriptor *)eem_configuration_descriptor, ++ }, ++ ++}; ++ ++/* BASIC Device Description ++ */ ++/*! Device Descriptor ++ */ ++static struct usbd_device_descriptor eem_device_descriptor = { ++ bLength: sizeof(struct usbd_device_descriptor), ++ bDescriptorType: USB_DT_DEVICE, ++ bcdUSB: __constant_cpu_to_le16(USB_BCD_VERSION), ++ bDeviceClass: COMMUNICATIONS_DEVICE_CLASS, ++ bDeviceSubClass: 0x02, ++ bDeviceProtocol: 0x00, ++ bMaxPacketSize0: 0x00, ++ idVendor: __constant_cpu_to_le16(CONFIG_OTG_NETWORK_VENDORID), ++ idProduct: __constant_cpu_to_le16(CONFIG_OTG_NETWORK_PRODUCTID), ++ bcdDevice: __constant_cpu_to_le16(CONFIG_OTG_NETWORK_BCDDEVICE), ++}; ++ ++#ifdef CONFIG_OTG_HIGH_SPEED ++/*! High Speed Device Qualifier Descriptor ++ */ ++static struct usbd_device_qualifier_descriptor eem_device_qualifier_descriptor = { ++ bLength: sizeof(struct usbd_device_qualifier_descriptor), ++ bDescriptorType: USB_DT_DEVICE_QUALIFIER, ++ bcdUSB: __constant_cpu_to_le16(USB_BCD_VERSION), ++ bDeviceClass: COMMUNICATIONS_DEVICE_CLASS, ++ bDeviceSubClass: 0x02, ++ bDeviceProtocol: 0x00, ++ bMaxPacketSize0: 0x00, ++}; ++#endif /* CONFIG_OTG_HIGH_SPEED */ ++ ++/*! Endpoint Request List ++ */ ++static struct usbd_endpoint_request eem_endpoint_requests[ENDPOINTS+1] = { ++ { 1, 0, 0, USB_DIR_OUT | USB_ENDPOINT_BULK, MAXFRAMESIZE + 48, MAXFRAMESIZE + 512, }, ++ { 1, 0, 0, USB_DIR_IN | USB_ENDPOINT_BULK, MAXFRAMESIZE + 48, MAXFRAMESIZE + 512, }, ++ { 1, 0, 0, USB_DIR_IN | USB_ENDPOINT_INTERRUPT | USB_ENDPOINT_OPT, 16, 64, }, ++ { 0, }, ++}; ++ ++/*! OTG Descriptor ++ */ ++static struct usbd_otg_descriptor eem_otg_descriptor = { ++ bLength : sizeof(struct usbd_otg_descriptor), ++ bDescriptorType: USB_DT_OTG, ++ bmAttributes: 0, ++}; ++ ++/*! Device Description ++ */ ++struct usbd_device_description eem_device_description = { ++ device_descriptor: &eem_device_descriptor, ++#ifdef CONFIG_OTG_HIGH_SPEED ++ device_qualifier_descriptor: &eem_device_qualifier_descriptor, ++#endif /* CONFIG_OTG_HIGH_SPEED */ ++ otg_descriptor: &eem_otg_descriptor, ++ iManufacturer: CONFIG_OTG_NETWORK_MANUFACTURER, ++ iProduct: CONFIG_OTG_NETWORK_PRODUCT_NAME, ++#if !defined(CONFIG_OTG_NO_SERIAL_NUMBER) && defined(CONFIG_OTG_SERIAL_NUMBER_STR) ++ iSerialNumber:CONFIG_OTG_SERIAL_NUMBER_STR, ++#endif ++}; ++ ++ ++/*! eem_init ++ * @param function The function instance ++ */ ++void eem_init (struct usbd_function_instance *function) ++{ ++ eem_data_alternate_descriptions[0].endpoints = Usb_network_private.have_interrupt ? 3 : 2; ++} ++ ++/*! function driver description ++ */ ++struct usbd_function_driver eem_function_driver = { ++ name: "network-BASIC", ++ fops: &net_fd_function_ops, ++ device_description: &eem_device_description, ++ bNumConfigurations: sizeof (eem_description) / sizeof (struct usbd_configuration_description), ++ configuration_description: eem_description, ++ idVendor: __constant_cpu_to_le16(CONFIG_OTG_NETWORK_VENDORID), ++ idProduct: __constant_cpu_to_le16(CONFIG_OTG_NETWORK_PRODUCTID), ++ bcdDevice: __constant_cpu_to_le16(CONFIG_OTG_NETWORK_BCDDEVICE), ++ bNumInterfaces:sizeof (eem_interfaces) / sizeof (struct usbd_interface_description), ++ interface_list:eem_interfaces, ++ endpointsRequested: ENDPOINTS, ++ requestedEndpoints: eem_endpoint_requests, ++}; ++#endif /* CONFIG_OTG_NETWORK_BASIC */ ++ +diff -uNr linux/drivers/no-otg/functions/network/fermat.c linux/drivers/otg/functions/network/fermat.c +--- linux/drivers/no-otg/functions/network/fermat.c 1970-01-01 01:00:00.000000000 +0100 ++++ linux/drivers/otg/functions/network/fermat.c 2006-09-01 21:41:30.000000000 +0200 +@@ -0,0 +1,140 @@ ++/* ++ * otg/functions/network/fermat.c - Network Function Driver ++ * ++ * Copyright (c) 2003, 2004 Belcarra ++ * ++ * By: ++ * Bruce Balden <balden@belcarra.com> ++ * ++ * ++ * 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. ++ * ++ */ ++/*! ++ * @file otg/functions/network/fermat.c ++ * @brief This implements a special data munging function that ++ * randomizes data. This is a very specific fix for a device ++ * that had trouble with runs of zeros. ++ * ++ * @ingroup NetworkFunction ++ */ ++ ++#include <linux/config.h> ++#include <linux/module.h> ++#include <linux/kernel.h> ++#include <linux/version.h> ++ ++#ifdef CONFIG_OTG_NETWORK_BLAN_FERMAT ++ ++#include "fermat.h" ++ ++#ifndef FERMAT_DEFINED ++typedef unsigned char BYTE; ++typedef struct fermat { ++ int length; ++ BYTE power[256]; ++} FERMAT; ++#endif ++ ++ ++static int fermat_setup(FERMAT *p, int seed){ ++ int i = 0; ++ unsigned long x,y; ++ y = 1; ++ do{ ++ x = y; ++ p->power[i] = ( x == 256 ? 0 : x); ++ y = ( seed * x ) % 257; ++ i += 1; ++ }while( y != 1); ++ p->length = i; ++ return i; ++} ++ ++static void fermat_xform(FERMAT *p, BYTE *data, int length){ ++ BYTE *pw = p->power; ++ int i, j; ++ BYTE * q ; ++ for(i = 0, j=0, q = data; i < length; i++, j++, q++){ ++ if(j>=p->length){ ++ j = 0; ++ } ++ *q ^= pw[j]; ++ } ++} ++ ++static FERMAT default_fermat; ++static const int primitive_root = 5; ++void fermat_init(){ ++ (void) fermat_setup(&default_fermat, primitive_root); ++} ++ ++// Here are the public official versions. ++// Change the primitive_root above to another primitive root ++// if you need better scatter. Possible values are 3 and 7 ++ ++ ++void fermat_encode(BYTE *data, int length){ ++ fermat_xform(&default_fermat, data, length); ++} ++ ++void fermat_decode(BYTE *data, int length){ ++ fermat_xform(&default_fermat, data, length); ++} ++ ++ ++// Note: the seed must be a "primitive root" of 257. This means that ++// the return value of the setup routine must be 256 (otherwise the ++// seed is not a primitive root. The routine will still work fine ++// but will be less pseudo-random. ++ ++#undef TEST ++#if TEST ++#include <stdio.h> ++#include <memory.h> ++ ++// Use FERMAT in two ways: to encode, and to generate test data. ++ ++main(){ ++ //Note 3, 5, and 7 are primitive roots of 257 ++ // 11 is not a primitive root ++ FERMAT three, five, seven; ++ ++ FERMAT three2; ++ printf("Cycle lengths: 3,5,7 %d %d %d \n", ++ fermat_setup(&three, 3), ++ fermat_setup(&five, 5), ++ fermat_setup(&seven, 7)); ++ three2=three; // Copy data from three ++ fermat_xform(&three,three2.power,three2.length); ++ fermat_xform(&five,three2.power,three2.length); ++ fermat_xform(&seven,three2.power,three2.length); ++ fermat_xform(&seven,three2.power,three2.length); ++ fermat_xform(&five,three2.power,three2.length); ++ fermat_xform(&three,three2.power,three2.length); ++ ++ //At this stage, three2 and three should be identical ++ if(memcpy(&three,&three2,sizeof(FERMAT))){ ++ printf("Decoded intact\n"); ++ } ++ ++ fermat_init(); ++ fermat_encode(three2.power,256); ++ ++} ++#endif ++ ++#endif /* CONFIG_OTG_NETWORK_BLAN_FERMAT */ ++ +diff -uNr linux/drivers/no-otg/functions/network/fermat.h linux/drivers/otg/functions/network/fermat.h +--- linux/drivers/no-otg/functions/network/fermat.h 1970-01-01 01:00:00.000000000 +0100 ++++ linux/drivers/otg/functions/network/fermat.h 2006-09-01 21:41:30.000000000 +0200 +@@ -0,0 +1,46 @@ ++/* ++ * otg/functions/network/fermat.h - Network Function Driver ++ * ++ * Copyright (c) 2003, 2004 Belcarra ++ * ++ * By: ++ * Bruce Balden <balden@belcarra.com> ++ * ++ * 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. ++ * ++ * ++ */ ++/*! ++ * @file otg/functions/network/fermat.h ++ * @brief Fermat related data structures. ++ * ++ * ++ * @ingroup NetworkFunction ++ */ ++ ++#ifndef FERMAT_DEFINED ++#define FERMAT_DEFINED 1 ++typedef unsigned char BYTE; ++typedef struct fermat { ++ int length; ++ BYTE power[256]; ++} FERMAT; ++ ++void fermat_init(void); ++void fermat_encode(BYTE *data, int length); ++void fermat_decode(BYTE *data, int length); ++#endif ++ ++ +diff -uNr linux/drivers/no-otg/functions/network/net-fd.c linux/drivers/otg/functions/network/net-fd.c +--- linux/drivers/no-otg/functions/network/net-fd.c 1970-01-01 01:00:00.000000000 +0100 ++++ linux/drivers/otg/functions/network/net-fd.c 2006-09-01 21:41:30.000000000 +0200 +@@ -0,0 +1,1463 @@ ++/* ++ * otg/functions/network/net-fd.c - Network Function Driver ++ * ++ * Copyright (c) 2002, 2003, 2004 Belcarra ++ * ++ * By: ++ * Tom Rushworth <tbr@belcarra.com> ++ * Chris Lynne <cl@belcarra.com> ++ * Stuart Lynne <sl@belcarra.com> ++ * Bruce Balden <balden@belcarra.com> ++ * ++ * 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. ++ * ++ */ ++/*! ++ * @file otg/functions/network/net-fd.c ++ * @brief The lower edge (USB Device Function) implementation of ++ * the Network Function Driver. This performs the core protocol ++ * handling and data encpasulation. ++ * ++ * This implements the lower edge (USB Device) layer of the Network Function ++ * Driver. Specifically the data encapsulation, envent and protocol handlers. ++ * ++ * ++ * ++ * This network function driver intended to interoperate with ++ * Belcarra's USBLAN Class drivers. ++ * ++ * These are available for Windows, Linux and Mac OSX. For more ++ * information and to download a copy for testing: ++ * ++ * http://www.belcarra.com/usblan/ ++ * ++ * When configured for CDC it can also work with any CDC Class driver. ++ * ++ * ++ * @ingroup NetworkFunction ++ */ ++ ++ ++#include <otg/otg-compat.h> ++#include <otg/otg-module.h> ++//#include <linux/config.h> ++//#include <linux/module.h> ++#include <linux/list.h> ++#include <linux/netdevice.h> ++#include <linux/skbuff.h> ++#include <linux/etherdevice.h> ++#include <net/arp.h> ++#include <linux/rtnetlink.h> ++#include <linux/smp_lock.h> ++#include <linux/ctype.h> ++#include <linux/time.h> ++#include <linux/timer.h> ++#include <linux/string.h> ++#include <linux/atmdev.h> ++#include <linux/pkt_sched.h> ++#include <linux/random.h> ++#include <linux/utsname.h> ++ ++#include <linux/ip.h> ++#include <linux/if_ether.h> ++#include <linux/in.h> ++#include <linux/inetdevice.h> ++ ++#include <linux/kmod.h> ++ ++#include <asm/uaccess.h> ++#include <asm/system.h> ++ ++#include <otg/usbp-chap9.h> ++//#include <otg/usbd-mem.h> ++#include <otg/usbp-func.h> ++ ++#include <otg/otg-trace.h> ++#include <otg/otg-api.h> ++ ++#include "network.h" ++#include "net-fd.h" ++#include "net-os.h" ++#ifdef CONFIG_OTG_NETWORK_BLAN_FERMAT ++#include "fermat.h" ++#endif ++ ++otg_tag_t network_fd_trace_tag; ++#define NTT network_fd_trace_tag ++ ++#if defined(CONFIG_OTG_NETWORK_CDC) ++void cdc_init(struct usbd_function_instance *); ++extern struct usbd_function_driver cdc_function_driver; ++#endif ++ ++#ifdef CONFIG_OTG_NETWORK_EEM ++void eem_init(struct usbd_function_instance *); ++extern struct usbd_function_driver eem_function_driver; ++#endif ++ ++#ifdef CONFIG_OTG_NETWORK_BASIC ++void basic_init(struct usbd_function_instance *); ++extern struct usbd_function_driver basic_function_driver; ++#endif ++ ++#ifdef CONFIG_OTG_NETWORK_BASIC2 ++void basic2_init(struct usbd_function_instance *); ++extern struct usbd_function_driver basic2_function_driver; ++#endif ++ ++#ifdef CONFIG_OTG_NETWORK_SAFE ++void safe_init(struct usbd_function_instance *); ++extern struct usbd_function_driver safe_function_driver; ++#endif ++ ++#ifdef CONFIG_OTG_NETWORK_BLAN ++void blan_init(struct usbd_function_instance *); ++extern struct usbd_function_driver blan_function_driver; ++#endif ++ ++static char ip_addr_str[20]; ++u32 ip_addr; ++u32 router_ip; ++u32 network_mask; ++u32 dns_server_ip; ++ ++//_________________________________________________________________________________________________ ++ ++/* ++ * If the following are defined we implement the crc32_copy routine using ++ * Duff's device. This will unroll the copy loop by either 4 or 8. Do not ++ * use these without profiling to test if it actually helps on any specific ++ * device. ++ */ ++#undef CONFIG_OTG_NETWORK_CRC_DUFF4 ++#undef CONFIG_OTG_NETWORK_CRC_DUFF8 ++ ++static u32 *network_crc32_table; ++ ++#define CRC32_INIT 0xffffffff // Initial FCS value ++#define CRC32_GOOD 0xdebb20e3 // Good final FCS value ++ ++#define CRC32_POLY 0xedb88320 // Polynomial for table generation ++ ++#define COMPUTE_FCS(val, c) (((val) >> 8) ^ network_crc32_table[((val) ^ (c)) & 0xff]) ++ ++//_________________________________________________________________________________________________ ++// crc32_copy ++ ++/*! make_crc_table ++ * Generate the crc32 table ++ * ++ * @return non-zero if malloc fails ++ */ ++STATIC int make_crc_table(void) ++{ ++ u32 n; ++ RETURN_ZERO_IF(network_crc32_table); ++ RETURN_ENOMEM_IF(!(network_crc32_table = (u32 *)ckmalloc(256*4, GFP_KERNEL))); ++ for (n = 0; n < 256; n++) { ++ int k; ++ u32 c = n; ++ for (k = 0; k < 8; k++) { ++ c = (c & 1) ? (CRC32_POLY ^ (c >> 1)) : (c >> 1); ++ } ++ network_crc32_table[n] = c; ++ } ++ return 0; ++} ++ ++#if !defined(CONFIG_OTG_NETWORK_CRC_DUFF4) && !defined(CONFIG_OTG_NETWORK_CRC_DUFF8) ++/*! crc32_copy ++ * Copies a specified number of bytes, computing the 32-bit CRC FCS as it does so. ++ * ++ * @param dst Pointer to the destination memory area. ++ * @param src Pointer to the source memory area. ++ * @param len Number of bytes to copy. ++ * @param val Starting value for the CRC FCS. ++ * ++ * @return Final value of the CRC FCS. ++ * ++ * @sa crc32_pad ++ */ ++static u32 __inline__ crc32_copy (u8 *dst, u8 *src, int len, u32 val) ++{ ++ for (; len-- > 0; val = COMPUTE_FCS (val, *dst++ = *src++)); ++ return val; ++} ++ ++#else /* DUFFn */ ++ ++/*! crc32_copy ++ * Copies a specified number of bytes, computing the 32-bit CRC FCS as it does so. ++ * ++ * @param dst Pointer to the destination memory area. ++ * @param src Pointer to the source memory area. ++ * @param len Number of bytes to copy. ++ * @param val Starting value for the CRC FCS. ++ * ++ * @return Final value of the CRC FCS. ++ * ++ * @sa crc32_pad ++ */ ++static u32 crc32_copy (u8 *dst, u8 *src, int len, u32 val) ++{ ++#if defined(CONFIG_OTG_NETWORK_CRC_DUFF8) ++ int n = (len + 7) / 8; ++ switch (len % 8) ++#elif defined(CONFIG_OTG_NETWORK_CRC_DUFF4) ++ int n = (len + 3) / 4; ++ switch (len % 4) ++#endif ++ { ++ case 0: do { ++ val = COMPUTE_FCS (val, *dst++ = *src++); ++#if defined(CONFIG_OTG_NETWORK_CRC_DUFF8) ++ case 7: ++ val = COMPUTE_FCS (val, *dst++ = *src++); ++ case 6: ++ val = COMPUTE_FCS (val, *dst++ = *src++); ++ case 5: ++ val = COMPUTE_FCS (val, *dst++ = *src++); ++ case 4: ++ val = COMPUTE_FCS (val, *dst++ = *src++); ++#endif ++ case 3: ++ val = COMPUTE_FCS (val, *dst++ = *src++); ++ case 2: ++ val = COMPUTE_FCS (val, *dst++ = *src++); ++ case 1: ++ val = COMPUTE_FCS (val, *dst++ = *src++); ++ } while (--n > 0); ++ } ++ return val; ++} ++#endif /* DUFFn */ ++ ++ ++//_________________________________________________________________________________________________ ++// crc32_pad ++ ++/*! crc32_pad - pad and calculate crc32 ++ * ++ * @return CRC FCS ++ */ ++static u32 __inline__ crc32_pad (u8 *dst, int len, u32 val) ++{ ++ for (; len-- > 0; val = COMPUTE_FCS (val, *dst++ = '\0')); ++ return val; ++} ++ ++//_________________________________________________________________________________________________ ++// net_fd_send_int ++// ++ ++/*! net_fd_urb_sent_int - callback for completed INT URB ++ * ++ * Handles notification that an urb has been sent (successfully or otherwise). ++ * ++ * @return non-zero for failure. ++ */ ++STATIC int net_fd_urb_sent_int (struct usbd_urb *urb, int urb_rc) ++{ ++ unsigned long flags; ++ int rc = -EINVAL; ++ struct usb_network_private *npd = urb->function_privdata; ++ ++ TRACE_MSG3(NTT,"urb: %p npd: %p urb_rc: %d", urb, npd, urb_rc); ++ ++ local_irq_save(flags); ++ npd->int_urb = NULL; ++ usbd_free_urb (urb); ++ local_irq_restore(flags); ++ return 0; ++} ++ ++/*! net_fd_send_int_blan - send an interrupt notification response ++ * ++ * Generates a response urb on the notification (INTERRUPT) endpoint. ++ * ++ * This is called from either a scheduled task or from the process context ++ * that calls network_open() or network_close(). ++ * This must be called with interrupts locked out as net_fd_event_handler can ++ * change the NETWORK_ATTACHED status ++ * ++ */ ++STATIC void net_fd_send_int_blan(struct usb_network_private *npd, int connected, int data) ++{ ++ struct usbd_urb *urb; ++ struct cdc_notification_descriptor *cdc; ++ int rc; ++ struct usbd_function_instance *function = (npd?npd->function:NULL); ++ ++ TRACE_MSG2(NTT,"npd=%p function=%p",npd,function); ++ ++ do { ++ BREAK_IF(!function); ++ ++ ++ BREAK_IF(npd->network_type != network_blan); ++ BREAK_IF(!npd->have_interrupt); ++ ++ BREAK_IF(!(npd->flags & NETWORK_ATTACHED)); ++ ++ TRACE_MSG3(NTT,"connected: %d network: %d %d", connected, ++ npd->network_type, network_blan); ++ ++ BREAK_IF(usbd_get_device_status(function) != USBD_OK); ++ ++ //if (npd->int_urb) { ++ // printk(KERN_INFO"%s: int_urb: %p\n", __FUNCTION__, npd->int_urb); ++ // usbd_cancel_urb_irq(npd->int_urb); ++ // npd->int_urb = NULL; ++ //} ++ ++ BREAK_IF(!(urb = usbd_alloc_urb (function, INT_IN, ++ sizeof(struct cdc_notification_descriptor), net_fd_urb_sent_int))); ++ ++ urb->actual_length = sizeof(struct cdc_notification_descriptor); ++ memset(urb->buffer, 0, sizeof(struct cdc_notification_descriptor)); ++ urb->function_privdata = npd; ++ ++ cdc = (struct cdc_notification_descriptor *)urb->buffer; ++ ++ cdc->bmRequestType = 0xa1; ++ ++ if (data) { ++ cdc->bNotification = 0xf0; ++ cdc->wValue = 1; ++ } ++ else { ++ cdc->bNotification = 0x00; ++ cdc->wValue = connected ? 0x01 : 0x00; ++ } ++ cdc->wIndex = 0x00; // XXX interface - check that this is correct ++ ++ ++ npd->int_urb = urb; ++ TRACE_MSG1(NTT,"int_urb: %p", urb); ++ BREAK_IF (!(rc = usbd_start_in_urb (urb))); ++ ++ TRACE_MSG1(NTT,"usbd_start_in_urb failed err: %x", rc); ++ printk(KERN_ERR"%s: usbd_start_in_urb failed err: %x\n", __FUNCTION__, rc); ++ urb->function_privdata = NULL; ++ npd->int_urb = NULL; ++ usbd_free_urb (urb); ++ ++ } while(0); ++} ++ ++//_________________________________________________________________________________________________ ++// net_fd_start_xmit ++ ++/*! net_fd_urb_sent_bulk - callback for completed BULK xmit URB ++ * ++ * Handles notification that an urb has been sent (successfully or otherwise). ++ * ++ * @param urb Pointer to the urb that has been sent. ++ * @param urb_rc Result code from the send operation. ++ * ++ * @return non-zero for failure. ++ */ ++STATIC int net_fd_urb_sent_bulk (struct usbd_urb *urb, int urb_rc) ++{ ++ unsigned long flags; ++ void *buff_ctx; ++ int rc = -EINVAL; ++ ++ TRACE_MSG2(NTT,"urb: %p urb_rc: %d", urb, urb_rc); ++ ++ local_irq_save(flags); ++ do { ++ ++ BREAK_IF(!urb); ++ buff_ctx = urb->function_privdata; ++ TRACE_MSG2(NTT,"urb: %p buff_ctx: %p", urb, buff_ctx); ++ urb->function_privdata = NULL; ++ ++ BREAK_IF(net_os_xmit_done(urb->function_instance,buff_ctx,urb_rc)); ++ ++ usbd_free_urb (urb); ++ rc = 0; ++ ++ } while (0); ++ local_irq_restore(flags); ++ return rc; ++} ++ ++/*! net_fd_start_xmit - start sending a buffer ++ * ++ * Called with net_os_mutex_enter()d. ++ * ++ * @return: 0 if all OK ++ * -EINVAL, -EUNATCH, -ENOMEM ++ * rc from usbd_start_in_urb() if that fails (is != 0, may be one of err values above) ++ * Note: -ECOMM is interpreted by calling routine as signal to leave IF stopped. ++ */ ++STATIC int net_fd_start_xmit (struct usb_network_private *npd, u8 *buff, int len, void *buff_ctx) ++{ ++ struct usbd_function_instance *function = (npd?npd->function:NULL); ++ struct usbd_urb *urb = NULL; ++ int rc; ++ int in_pkt_sz; ++ u8 *cp; ++ u32 crc; ++ TRACE_MSG2(NTT,"npd=%p function=%p",npd,function); ++ ++#if 0 ++ printk(KERN_INFO"%s: %s len: %d encap: %d\n", __FUNCTION__, net_device->name, len, npd->encapsulation); ++ printk(KERN_INFO"start_xmit: len: %x data: %p\n", len, buff); ++ { ++ u8 *cp = buff; ++ int i; ++ for (i = 0; i < len; i++) { ++ if ((i%32) == 0) { ++ printk("\ntx[%2x] ", i); ++ } ++ printk("%02x ", *cp++); ++ } ++ printk("\n"); ++ } ++#endif ++ ++ if (!(npd->flags & NETWORK_ATTACHED)) { ++ return -EUNATCH; ++ } ++ if (usbd_get_device_status(function) != USBD_OK) { ++ return -EINVAL; ++ } ++ ++#if defined(CONFIG_OTG_NETWORK_CDC) ++ // verify interface is enabled - non-zero altsetting means data is enabled ++ if (!usbd_interface_AltSetting(function, DATA_INTF)) { ++ return -EINVAL; ++ } ++#endif ++ in_pkt_sz = usbd_endpoint_wMaxPacketSize(function, BULK_IN, usbd_high_speed(function)); ++ ++ if (npd->encapsulation != simple_crc) { ++ TRACE_MSG0(NTT,"unknown encapsulation"); ++ printk(KERN_ERR"%s: unknown encapsulation\n", __FUNCTION__); ++ // Since we are now only really using one, just fix it. ++ npd->encapsulation = simple_crc; ++ } ++ //TRACE_MSG0(NTT,"SIMPLE_CRC"); ++ // allocate urb 5 bytes larger than required ++ if (!(urb = usbd_alloc_urb (function, BULK_IN, len + 5 + 4 + in_pkt_sz, net_fd_urb_sent_bulk ))) { ++ u8 epa = usbd_endpoint_bEndpointAddress(function, BULK_IN, usbd_high_speed(function)); ++ TRACE_MSG2(NTT,"urb alloc failed len: %d endpoint: %02x", len, epa); ++ printk(KERN_ERR"%s: urb alloc failed len: %d endpoint: %02x\n", __FUNCTION__, len, epa); ++ return -ENOMEM; ++ } ++ ++ switch (npd->network_type) { ++ case network_eem: ++ cp = urb->buffer + 2; ++ urb->actual_length = len + 2; ++ break; ++ default: ++ cp = urb->buffer; ++ urb->actual_length = len; ++ break; ++ } ++ // copy and crc len bytes ++ crc = crc32_copy(cp, buff, len, CRC32_INIT); ++ ++ switch (npd->network_type) { ++ case network_eem: ++ break; ++ default: ++ if ((urb->actual_length % in_pkt_sz) == (in_pkt_sz - 4)) { ++ ++ #undef CONFIG_OTG_NETWORK_PADBYTE ++ #ifdef CONFIG_OTG_NETWORK_PADBYTE ++ // add a pad byte if required to ensure a short packet, usbdnet driver ++ // will correctly handle pad byte before or after CRC, but the MCCI driver ++ // wants it before the CRC. ++ crc = crc32_pad(urb->buffer + urb->actual_length, 1, crc); ++ urb->actual_length++; ++ #else /* CONFIG_OTG_NETWORK_PADBYTE */ ++ urb->flags |= USBD_URB_SENDZLP; ++ TRACE_MSG2(NTT,"setting ZLP: urb: %p flags: %x", urb, urb->flags); ++ #endif /* CONFIG_OTG_NETWORK_PADBYTE */ ++ } ++ break; ++ } ++ // munge and append crc ++ crc = ~crc; ++ urb->buffer[urb->actual_length++] = crc & 0xff; ++ urb->buffer[urb->actual_length++] = (crc >> 8) & 0xff; ++ urb->buffer[urb->actual_length++] = (crc >> 16) & 0xff; ++ urb->buffer[urb->actual_length++] = (crc >> 24) & 0xff; ++ // End of CRC processing ++ // ++ switch (npd->network_type) { ++ case network_eem: ++ break; ++ default: ++ break; ++ } ++ ++ TRACE_MSG3(NTT,"urb=%p buff_ctx=%p priv=%p",urb,buff_ctx,urb->function_privdata); ++ urb->function_privdata = (void *) buff_ctx; ++#if 0 ++ printk(KERN_INFO"start_xmit: len: %d : %d data: %p\n", skb->len, urb->actual_length, urb->buffer); ++ { ++ u8 *cp = urb->buffer; ++ int i; ++ for (i = 0; i < urb->actual_length; i++) { ++ if ((i%32) == 0) { ++ printk("\ntx[%2x] ", i); ++ } ++ printk("%02x ", *cp++); ++ } ++ printk("\n"); ++ } ++#endif ++#if defined(CONFIG_OTG_NETWORK_BLAN_FERMAT) ++ if (npd->fermat) { ++ fermat_encode(urb->buffer, urb->actual_length); ++ } ++#endif ++ TRACE_MSG1(NTT,"sending urb: %p", urb); ++ if ((rc = usbd_start_in_urb (urb))) { ++ ++ TRACE_MSG1(NTT,"FAILED: %d", rc); ++ printk(KERN_ERR"%s: FAILED: %d\n", __FUNCTION__, rc); ++ urb->function_privdata = NULL; ++ usbd_free_urb (urb); ++ ++ return(rc); ++ } ++ #if 0 ++ { ++ static int xmit_count = 0; ++ if (xmit_count++ == 100) { ++ TRACE_MSG1(NTT, "halt test: %02x", BULK_IN); ++ usbd_halt_endpoint(function, BULK_IN); ++ } ++ } ++ #endif ++ ++ TRACE_MSG0(NTT,"OK"); ++ return 0; ++} ++ ++ ++//_________________________________________________________________________________________________ ++ ++/*! net_fd_recv_urb - callback to process a received URB ++ * ++ * @return non-zero for failure. ++ */ ++STATIC int net_fd_recv_urb(struct usbd_urb *urb, int rc) ++{ ++ struct usbd_function_instance *function = urb->function_instance; ++ struct usb_network_private *npd = urb->function_privdata; ++ void *buff_ctx = NULL; ++ u8 *buff; ++ int crc_bad = 0; ++ int trim = 0; ++ int len; ++ int out_pkt_sz; ++ u32 crc; ++ ++#if 0 ++ printk(KERN_INFO"%s: urb: %p len: %d maxtransfer: %d encap: %d\n", __FUNCTION__, ++ urb, urb->actual_length, npd->maxtransfer, npd->encapsulation); ++ ++ { ++ u8 *cp = urb->buffer; ++ int i; ++ for (i = 0; i < urb->actual_length; i++) { ++ if ((i%32) == 0) { ++ printk("\n[%2x] ", i); ++ } ++ printk("%02x ", *cp++); ++ } ++ printk("\n"); ++ } ++#endif ++ if (!urb || !urb->function_instance || !npd || !npd->function || ++ urb->function_instance != npd->function) { ++ TRACE_MSG4(NTT,"urb=%p npd=%p u->f=%p n->f=%p", ++ urb,(urb?npd:NULL),(urb?urb->function_instance:NULL), ++ ((urb&&npd)?npd->function:NULL)); ++ } ++ ++ THROW_IF(urb->status != RECV_OK, error); ++ ++ out_pkt_sz = usbd_endpoint_wMaxPacketSize(function, BULK_OUT, usbd_high_speed(function)); ++ // There is only one working encapsulation. ++ if (npd->encapsulation != simple_crc) { ++ npd->encapsulation = simple_crc; ++ } ++ ++ len = urb->actual_length; ++ trim = 0; ++ buff_ctx = net_os_alloc_buff(npd, &buff, len); ++ THROW_IF((NULL == buff_ctx), error); ++ ++#if defined(CONFIG_OTG_NETWORK_BLAN_PADAFTER) ++ { ++ /* This version simply checks for a correct CRC along the ++ * entire packet. Some UDC's have trouble with some packet ++ * sizes, this allows us to add pad bytes after the CRC. ++ */ ++ ++ u8 *src = urb->buffer; ++ int copied; ++ ++ // XXX this should work, but the MIPS optimizer seems to get it wrong.... ++ //copied = (len < out_pkt_sz) ? 0 : ((len / out_pkt_sz) - 1) * out_pkt_sz; ++ ++ if (len < out_pkt_sz*2) ++ copied = 0; ++ else { ++ int pkts = ((len - out_pkt_sz) / out_pkt_sz); ++ copied = (pkts - 1) * out_pkt_sz; ++ } ++ ++ len -= copied; ++ crc = CRC32_INIT; ++ for (; copied-- > 0 ; crc = COMPUTE_FCS (crc, *buff++ = *src++)); ++ ++ for (; (len-- > 0) && (CRC32_GOOD != crc); crc = COMPUTE_FCS (crc, *buff++ = *src++)); ++ ++ trim = len + 4; ++ ++ if (CRC32_GOOD != crc) { ++ TRACE_MSG1(NTT,"AAA frame: %03x", urb->framenum); ++ THROW_IF(npd->crc, crc_error); ++ } ++ else ++ npd->crc = 1; ++ } ++#else ++ /* ++ * The CRC can be sent in two ways when the size of the transfer ++ * ends up being a multiple of the packetsize: ++ * ++ * | ++ * <data> <CRC><CRC><CRC><CRC>|<???> case 1 ++ * <data> <NUL><CRC><CRC><CRC>|<CRC> case 2 ++ * <data> <NUL><CRC><CRC><CRC><CRC>| case 3 ++ * <data> <NUL><CRC><CRC><CRC>|<CRC> | case 4 ++ * | ++ * ++ * This complicates CRC checking, there are four scenarios: ++ * ++ * 1. length is 1 more than multiple of packetsize with a trailing byte ++ * 2. length is 1 more than multiple of packetsize ++ * 3. length is multiple of packetsize ++ * 4. none of the above ++ * ++ * Finally, even though we always compute CRC, we do not actually throw ++ * things away until and unless we have previously seen a good CRC. ++ * This allows backwards compatibility with hosts that do not support ++ * adding a CRC to the frame. ++ * ++ */ ++ ++ // test if 1 more than packetsize multiple ++ if (1 == (len % out_pkt_sz)) { ++ ++ // copy and CRC up to the packetsize boundary ++ crc = crc32_copy(buff, urb->buffer, len - 1, CRC32_INIT); ++ buff += len - 1; ++ ++ // if the CRC is good then this is case 1 ++ if (CRC32_GOOD != crc) { ++ ++ crc = crc32_copy(buff, urb->buffer + len - 1, 1, crc); ++ buff += 1; ++ ++ if (CRC32_GOOD != crc) { ++ //crc_errors[len%64]++; ++ TRACE_MSG2(NTT,"A CRC error %08x %03x", crc, urb->framenum); ++ THROW_IF(npd->crc, crc_error); ++ } ++ else ++ npd->crc = 1; ++ } ++ else ++ npd->crc = 1; ++ } ++ else { ++ crc = crc32_copy(buff, urb->buffer, len, CRC32_INIT); ++ buff += len; ++ ++ if (CRC32_GOOD != crc) { ++ //crc_errors[len%64]++; ++ TRACE_MSG2(NTT,"B CRC error %08x %03x", crc, urb->framenum); ++ THROW_IF(npd->crc, crc_error); ++ } ++ else ++ npd->crc = 1; ++ } ++ // trim IFF we are paying attention to crc ++ if (npd->crc) ++ trim = 4; ++#endif ++ // catch a simple error, just increment missed error and general error ++ CATCH(error) { ++ TRACE_MSG4(NTT,"CATCH(error) urb: %p status: %d len: %d function: %p", ++ urb, urb->status, urb->actual_length, function); ++ // catch a CRC error ++ CATCH(crc_error) { ++ crc_bad = 1; ++#if 0 ++ printk(KERN_INFO"%s: urb: %p status: %d len: %d maxtransfer: %d encap: %d\n", __FUNCTION__, ++ urb, urb->status, urb->actual_length, npd->maxtransfer, ++ npd->encapsulation); ++ ++ { ++ u8 *cp = urb->buffer; ++ int i; ++ for (i = 0; i < urb->actual_length; i++) { ++ if ((i%32) == 0) { ++ printk("\n[%2x] ", i); ++ } ++ printk("%02x ", *cp++); ++ } ++ printk("\n"); ++ } ++#endif ++ } ++ } ++ net_os_recv_buff(npd,buff_ctx,crc_bad,trim); ++ TRACE_MSG1(NTT,"restart: %p", urb); ++ return (usbd_start_out_urb (urb)); ++} ++/*! net_fd_recv_urb_eem - callback to process a received URB ++ * ++ * @return non-zero for failure. ++ */ ++STATIC int net_fd_recv_urb_eem(struct usbd_urb *urb, int rc) ++{ ++ return 0; ++} ++ ++//_________________________________________________________________________________________________ ++// net_fd_device_request ++// ++/*! net_fd_urb_received_ep0 - callback for sent URB ++ * ++ * Handles notification that an urb has been sent (successfully or otherwise). ++ * ++ * @return non-zero for failure. ++ */ ++STATIC int net_fd_urb_received_ep0 (struct usbd_urb *urb, int urb_rc) ++{ ++ TRACE_MSG2(NTT,"urb: %p status: %d", urb, urb->status); ++ ++ printk(KERN_INFO"%s:\n", __FUNCTION__); ++ RETURN_EINVAL_IF (RECV_OK != urb->status); ++ ++ // TRACE_MSG1(NTT,"%s", urb->buffer); // QQSV is this really a NUL-terminated string??? ++ ++ printk(KERN_INFO"%s: ok\n", __FUNCTION__); ++ return -EINVAL; // caller will de-allocate ++} ++ ++/*! net_fd_device_request - process a received SETUP URB ++ * ++ * Processes a received setup packet and CONTROL WRITE data. ++ * Results for a CONTROL READ are placed in urb->buffer. ++ * ++ * @return non-zero for failure. ++ */ ++STATIC int net_fd_device_request (struct usbd_function_instance *function, struct usbd_device_request *request) ++{ ++ struct usb_network_private *npd = (struct usb_network_private *) (function->privdata); ++ struct usbd_urb *urb; ++ int index; ++ ++ // Verify that this is a USB Class request per CDC specification or a vendor request. ++ RETURN_ZERO_IF (!(request->bmRequestType & (USB_REQ_TYPE_CLASS | USB_REQ_TYPE_VENDOR))); ++ ++ // Determine the request direction and process accordingly ++ switch (request->bmRequestType & (USB_REQ_DIRECTION_MASK | USB_REQ_TYPE_MASK)) { ++ ++ case USB_REQ_HOST2DEVICE | USB_REQ_TYPE_VENDOR: ++ ++ switch (request->bRequest) { ++ case MCCI_ENABLE_CRC: ++ if (make_crc_table()) ++ return -EINVAL; ++ npd->encapsulation = simple_crc; ++ return 0; ++ ++ case BELCARRA_PING: ++ TRACE_MSG1(NTT,"H2D VENDOR IP: %08x", ip_addr); ++ if ((npd->network_type == network_blan)) ++ net_os_send_notification_later(npd); ++ break; ++ ++#if !defined(CONFIG_OTG_NETWORK_BLAN_DO_NOT_SETTIME) || !defined(CONFIG_OTG_NETWORK_SAFE_DO_NOT_SETTIME) ++ case BELCARRA_SETTIME: ++ { ++#if defined(LINUX24) ++ struct timeval tv; ++#else ++ struct timespec tv; ++#endif ++ memset(&tv, 0, sizeof(tv)); ++ ++ // wIndex and wLength contain RFC868 time - seconds since midnight 1 jan 1900 ++ ++ tv.tv_sec = ntohl( request->wValue << 16 | request->wIndex); ++ // tv.tv_usec = 0; ++ ++ // convert to Unix time - seconds since midnight 1 jan 1970 ++ ++ tv.tv_sec -= RFC868_OFFSET_TO_EPOCH; ++ ++ TRACE_MSG1(NTT,"H2D VENDOR TIME: %08x", tv.tv_sec); ++ ++ // set the time ++ do_settimeofday(&tv); ++ } break; ++#endif ++ case BELCARRA_SETIP: ++ ip_addr = ntohl( request->wValue << 16 | request->wIndex); ++ // XXX need to get in correct order here ++ npd->local_dev_addr[2] = (ip_addr >> 24) & 0xff; ++ npd->local_dev_addr[3] = (ip_addr >> 16) & 0xff; ++ npd->local_dev_addr[4] = (ip_addr >> 8) & 0xff; ++ npd->local_dev_addr[5] = (ip_addr >> 0) & 0xff; ++#ifdef CONFIG_OTG_NETWORK_BLAN_IPADDR ++ snprintf(ip_addr_str, sizeof(ip_addr_str), "%d.%d.%d.%d", ++ (ip_addr >> 24) & 0xff, (ip_addr >> 16) & 0xff, ++ (ip_addr >> 8) & 0xff, (ip_addr) & 0xff); ++ index = usbd_realloc_string( ++ npd->function_driver->device_description->device_descriptor->iProduct, ip_addr_str); ++#endif /* CONFIG_OTG_NETWORK_IPADDR */ ++ break; ++ ++ case BELCARRA_SETMSK: ++ network_mask = ntohl( request->wValue << 16 | request->wIndex); ++ break; ++ ++ case BELCARRA_SETROUTER: ++ router_ip = ntohl( request->wValue << 16 | request->wIndex); ++ break; ++ ++ case BELCARRA_SETDNS: ++ dns_server_ip = ntohl( request->wValue << 16 | request->wIndex); ++ break; ++#ifdef CONFIG_OTG_NETWORK_BLAN_FERMAT ++ case BELCARRA_SETFERMAT: ++ npd->fermat = 1; ++ break; ++#endif ++#ifdef CONFIG_OTG_NETWORK_BLAN_HOSTNAME ++ case BELCARRA_HOSTNAME: ++ TRACE_MSG0(NTT,"HOSTNAME"); ++ RETURN_EINVAL_IF(!(urb = usbd_alloc_urb_ep0(function, le16_to_cpu(request->wLength), ++ net_fd_urb_received_ep0) )); ++ RETURN_ZERO_IF(!usbd_start_out_urb(urb)); // return if no error ++ usbd_free_urb(urb); // de-alloc if error ++ return -EINVAL; ++#endif ++#ifdef CONFIG_OTG_NETWORK_BLAN_DATA_NOTIFY_OK ++ case BELCARRA_DATA_NOTIFY: ++ TRACE_MSG0(NTT,"DATA NOTIFY"); ++ npd->data_notify = 1; ++ return -EINVAL; ++#endif ++ } ++ return 0; ++#if 0 ++ case USB_REQ_DEVICE2HOST | USB_REQ_TYPE_VENDOR: ++ urb->actual_length = 0; ++ switch (request->bRequest) { ++ case BELCARRA_GETMAC: ++ { ++ // copy and free the original buffer ++ memcpy(urb->buffer, npd->local_dev_addr, ETH_ALEN); ++ urb->actual_length = ETH_ALEN; ++ return 0; ++ } ++ } ++#endif ++ return 0; ++ default: ++ break; ++ } ++ return -EINVAL; ++} ++//_________________________________________________________________________________________________ ++ ++#ifdef CONFIG_OTG_NETWORK_START_SINGLE ++#define NETWORK_START_URBS 1 ++#else ++#define NETWORK_START_URBS 2 ++#endif ++ ++typedef enum mesg { ++ mesg_unknown, ++ mesg_configured, ++ mesg_reset, ++} mesg_t; ++mesg_t net_last_mesg; ++ ++char * net_messages[3] = { ++ "", ++ "Network Configured", ++ "Network Reset", ++}; ++ ++/*! net_check_mesg ++ */ ++void net_check_mesg(mesg_t curr_mesg) ++{ ++ RETURN_UNLESS(net_last_mesg != curr_mesg); ++ net_last_mesg = curr_mesg; ++ otg_message(net_messages[curr_mesg]); ++} ++ ++ ++ ++/*! net_fd_start_recv - start recv urb(s) ++ */ ++STATIC void net_fd_start_recv(struct usbd_function_instance *function) ++{ ++ struct usb_network_private *npd = (struct usb_network_private *) (function->privdata); ++ int i; ++ for (i = 0; i < NETWORK_START_URBS; i++) { ++ struct usbd_urb *urb; ++ BREAK_IF(!(urb = usbd_alloc_urb(function, BULK_OUT, ++ usbd_endpoint_transferSize(function, BULK_OUT, usbd_high_speed(function)), ++ net_fd_recv_urb))); ++ TRACE_MSG5(NTT,"i: %d urb=%p priv=%p npd=%p function=%p", ++ i, urb, urb->function_privdata, npd, function); ++ ++ urb->function_privdata = npd; ++ if (usbd_start_out_urb(urb)) { ++ urb->function_privdata = NULL; ++ usbd_free_urb(urb); ++ } ++ } ++} ++ ++/*! net_fd_start_recv_eem - start recv urb(s) ++ */ ++STATIC void net_fd_start_recv_eem(struct usbd_function_instance *function) ++{ ++} ++ ++/*! net_fd_event_handler - Processes a USB event. ++ */ ++STATIC void net_fd_event_handler (struct usbd_function_instance *function, usbd_device_event_t event, int data) ++{ ++ struct usb_network_private *npd = (struct usb_network_private *) (function->privdata); ++ ++ switch (event) { ++ ++ case DEVICE_RESET: ++ case DEVICE_DESTROY: ++ case DEVICE_BUS_INACTIVE: ++ case DEVICE_DE_CONFIGURED: ++ TRACE_MSG1(NTT,"RESET/DESTROY/BUS_INACTIVE/DE_CONFIGURED %08x",ip_addr); ++ net_check_mesg(mesg_reset); ++ { ++ // Return if argument is null. ++ ++ // XXX flush ++ ++ npd->flags &= ~NETWORK_ATTACHED; ++ npd->int_urb = NULL; ++ ++ // Disable our net-device. ++ // Apparently it doesn't matter if we should do this more than once. ++ ++ net_os_carrier_off(npd); ++ ++ // If we aren't already tearing things down, do it now. ++ if (!(npd->flags & NETWORK_DESTROYING)) { ++ npd->flags |= NETWORK_DESTROYING; ++ //npd->device = NULL; ++ } ++ } ++ npd->crc = 0; ++ break; ++ ++ case DEVICE_CONFIGURED: ++ case DEVICE_BUS_ACTIVITY: ++ TRACE_MSG1(NTT,"CONFIGURED/BUS_ACTIVITY %08x",ip_addr); ++ net_check_mesg(mesg_configured); ++ npd->flags |= NETWORK_ATTACHED; ++ if ((npd->network_type == network_blan) && (npd->flags & NETWORK_OPEN)) ++ net_os_send_notification_later(npd); ++ net_os_carrier_on(npd); ++ (npd->network_type == network_eem) ? net_fd_start_recv_eem : net_fd_start_recv(function); ++ break; ++ ++ case DEVICE_SET_INTERFACE: ++ // XXX if CDC then we can check device->alternates[1] and see if we should ++ // enable/disable data flow. ++ // XXX verify ep0.c SET_CONFIGURATION and SET_INTERFACE implmentation are ++ // complete before using this ++ break; ++ ++ default: ++ return; ++ } ++ // Let the OS layer know, if it's interested. ++ net_os_config(npd); ++ net_os_hotplug(npd); ++} ++ ++ ++/*! net_fd_endpoint_cleared - ++ */ ++STATIC void net_fd_endpoint_cleared (struct usbd_function_instance *function, int bEndpointAddress, int wIndex) ++{ ++ TRACE_MSG2(NTT, "bEndpointAddress: %02x wIndex: %02x", bEndpointAddress, wIndex); ++} ++ ++ ++//_________________________________________________________________________________________________ ++ ++/*! net_fd_function_enable - enable the function driver ++ * ++ * Called for usbd_function_enable() from usbd_register_device() ++ */ ++ ++STATIC int net_fd_function_enable (struct usbd_function_instance *function) ++{ ++ struct usb_network_private *npd; ++#if 0 ++ /* This is the first time we've seen the function instance ++ (it's allocated by the usbdcore), so we need to let the OS ++ layer see it and initialize the privdata pointer. */ ++ net_os_enable(function); ++ npd = (struct usb_network_private *) (function->privdata); ++ TRACE_MSG0(NTT,"semaphore DOWN"); ++ net_os_mutex_enter(npd); ++#else ++ npd = (struct usb_network_private *) (function->privdata); ++ TRACE_MSG0(NTT,"semaphore DOWN"); ++ net_os_mutex_enter(npd); ++ net_os_enable(function); ++#endif ++ _MOD_INC_USE_COUNT; // QQQ Should this be _before_ the mutex_enter()? ++ TRACE_MSG1(NTT, "INC: %d", MOD_IN_USE); ++ ++ // set the network device address from the local device address ++ // Now done in net_os_enable() above. ++ // memcpy(npd->net_dev->dev_addr, npd->local_dev_addr, ETH_ALEN); ++ ++ npd->have_interrupt = usbd_endpoint_bEndpointAddress(function, INT_IN, usbd_high_speed(function)) ? 1 : 0; ++ ++ npd->flags |= NETWORK_ENABLED; ++ ++#if defined(CONFIG_OTG_NETWORK_CDC) ++ cdc_init(function); ++#endif /* CONFIG_OTG_NETWORK_CDC */ ++ ++#ifdef CONFIG_OTG_NETWORK_EEM ++ eem_init(function); ++#endif ++ ++#ifdef CONFIG_OTG_NETWORK_BASIC ++ basic_init(function); ++#endif ++ ++#ifdef CONFIG_OTG_NETWORK_BASIC2 ++ basic2_init(function); ++#endif ++ ++#ifdef CONFIG_OTG_NETWORK_SAFE ++ safe_init(function); ++#endif ++#ifdef CONFIG_OTG_NETWORK_BLAN ++ blan_init(function); ++#endif ++ net_os_mutex_exit(npd); ++ TRACE_MSG0(NTT,"semaphore UP"); ++ return 0; ++} ++ ++/*! net_fd_function_disable - disable the function driver ++ * ++ */ ++STATIC void net_fd_function_disable (struct usbd_function_instance *function) ++{ ++ struct usb_network_private *npd = (struct usb_network_private *) (function->privdata); ++ TRACE_MSG0(NTT,"semaphore DOWN"); ++ net_os_mutex_enter(npd); ++ npd->flags &= ~NETWORK_ENABLED; ++ _MOD_DEC_USE_COUNT; // QQQ Should this be _after_ the up()? ++ TRACE_MSG1(NTT, "DEC: %d", MOD_IN_USE); ++ net_os_mutex_exit(npd); ++ TRACE_MSG0(NTT,"semaphore UP"); ++} ++ ++/*! net_fd_function_ops - operations table for network function driver ++ */ ++struct usbd_function_operations net_fd_function_ops = { ++ device_request: net_fd_device_request, ++ event_handler: net_fd_event_handler, ++ function_enable: net_fd_function_enable, ++ function_disable: net_fd_function_disable, ++ endpoint_cleared: net_fd_endpoint_cleared, ++}; ++ ++//______________________________________module_init and module_exit________________________________ ++ ++/*! hexdigit - ++ * ++ * Converts characters in [0-9A-F] to 0..15, characters in [a-f] to 42..47, and all others to 0. ++ */ ++static u8 hexdigit (char c) ++{ ++ return isxdigit (c) ? (isdigit (c) ? (c - '0') : (c - 'A' + 10)) : 0; ++} ++ ++/*! set_address - ++ */ ++static void set_address(char *mac_address_str, u8 *dev_addr) ++{ ++ int i; ++ if (mac_address_str && strlen(mac_address_str)) { ++ for (i = 0; i < ETH_ALEN; i++) { ++ dev_addr[i] = ++ hexdigit (mac_address_str[i * 2]) << 4 | ++ hexdigit (mac_address_str[i * 2 + 1]); ++ } ++ } ++ else { ++ get_random_bytes(dev_addr, ETH_ALEN); ++ dev_addr[0] = (dev_addr[0] & 0xfe) | 0x02; ++ } ++} ++ ++/*! macstrtest - ++ */ ++static int macstrtest(char *mac_address_str) ++{ ++ int l = 0; ++ ++ if (mac_address_str) { ++ l = strlen(mac_address_str); ++ } ++ return ((l != 0) && (l != 12)); ++} ++ ++/*! select_network_type ++ * ++ */ ++STATIC network_type_t select_network_type(struct usb_network_params *p) ++{ ++ /* ++ * Figure out what network mode to operate in. ++ */ ++ network_type_t network_type = network_unknown; ++ /* ++ * Step 1. Look to see if network_type is specified by something like module params. ++ */ ++#if defined(CONFIG_OTG_NETWORK_CDC) ++ p->cdc_capable = 1; ++ if (p->cdc) { ++ TRACE_MSG0(NTT,"cdc"); ++ network_type = network_cdc; ++ } ++#endif ++#ifdef CONFIG_OTG_NETWORK_EEM ++ p->eem_capable = 1; ++ if (p->eem) { ++ THROW_IF (network_type != network_unknown, select_error); ++ TRACE_MSG0(NTT,"eem"); ++ network_type = network_eem; ++ } ++#endif ++#ifdef CONFIG_OTG_NETWORK_BASIC ++ p->basic_capable = 1; ++ if (p->basic) { ++ THROW_IF (network_type != network_unknown, select_error); ++ TRACE_MSG0(NTT,"basic"); ++ network_type = network_basic; ++ } ++#endif ++#ifdef CONFIG_OTG_NETWORK_BASIC2 ++ p->basic2_capable = 1; ++ if (p->basic2) { ++ THROW_IF (network_type != network_unknown, select_error); ++ TRACE_MSG0(NTT,"basic2"); ++ network_type = network_basic2; ++ } ++#endif ++#ifdef CONFIG_OTG_NETWORK_SAFE ++ p->safe_capable = 1; ++ if (p->safe) { ++ THROW_IF (network_type != network_unknown, select_error); ++ TRACE_MSG0(NTT,"safe"); ++ network_type = network_safe; ++ } ++#endif ++#ifdef CONFIG_OTG_NETWORK_BLAN ++ p->blan_capable = 1; ++ if (p->blan) { ++ THROW_IF (network_type != network_unknown, select_error); ++ TRACE_MSG0(NTT,"blan"); ++ network_type = network_blan; ++ } ++#endif ++ ++ /* ++ * Step 2. If nothing was specified by parameter, default to ++ * to the first available (i.e. compiled) of: ++ * CDC, BASIC, BASIC2, SAFE, BLAN. ++ */ ++#if defined(CONFIG_OTG_NETWORK_CDC) ++ if (network_type == network_unknown) { ++ TRACE_MSG0(NTT,"unknown => cdc"); ++ network_type = network_cdc; ++ } ++#endif ++#if defined(CONFIG_OTG_NETWORK_EEM) ++ if (network_type == network_unknown) { ++ TRACE_MSG0(NTT,"unknown => eem"); ++ network_type = network_eem; ++ } ++#endif ++#if defined(CONFIG_OTG_NETWORK_BASIC) ++ if (network_type == network_unknown) { ++ TRACE_MSG0(NTT,"unknown => basic"); ++ network_type = network_basic; ++ } ++#endif ++#if defined(CONFIG_OTG_NETWORK_BASIC2) ++ if (network_type == network_unknown) { ++ TRACE_MSG0(NTT,"unknown => basic2"); ++ network_type = network_basic2; ++ } ++#endif ++#if defined(CONFIG_OTG_NETWORK_SAFE) ++ if (network_type == network_unknown) { ++ TRACE_MSG0(NTT,"unknown => safe"); ++ network_type = network_safe; ++ } ++#endif ++#if defined(CONFIG_OTG_NETWORK_BLAN) ++ if (network_type == network_unknown) { ++ TRACE_MSG0(NTT,"unknown => blan"); ++ network_type = network_blan; ++ } ++#endif ++ CATCH(select_error) { ++ network_type == network_unknown; ++ } ++ return(network_type); ++} ++ ++/*! select_descriptors ++ * ++ */ ++STATIC struct usbd_function_driver * select_descriptors(network_type_t network_type) ++{ ++ // select the function driver descriptors based on network_type ++ ++ switch (network_type) { ++ ++#if defined(CONFIG_OTG_NETWORK_CDC) ++ case network_cdc: ++ TRACE_MSG0(NTT,"fd=cdc"); ++ return(&cdc_function_driver); ++#endif ++#if defined(CONFIG_OTG_NETWORK_EEM) ++ case network_eem: ++ TRACE_MSG0(NTT,"fd=eem"); ++ return(&eem_function_driver); ++#endif ++#if defined(CONFIG_OTG_NETWORK_BASIC) ++ case network_basic: ++ TRACE_MSG0(NTT,"fd=basic"); ++ return(&basic_function_driver); ++#endif ++#if defined(CONFIG_OTG_NETWORK_BASIC2) ++ case network_basic2: ++ TRACE_MSG0(NTT,"fd=basic2"); ++ return(&basic2_function_driver); ++#endif ++#if defined(CONFIG_OTG_NETWORK_SAFE) ++ case network_safe: ++ TRACE_MSG1(NTT,"fd=safe bNumConfigurations: %d", ++ safe_function_driver.bNumConfigurations); ++ return(&safe_function_driver); ++#endif ++#if defined(CONFIG_OTG_NETWORK_BLAN) ++ case network_blan: ++ TRACE_MSG1(NTT,"fd=blan bNumConfigurations: %d", ++ blan_function_driver.bNumConfigurations); ++ return(&blan_function_driver); ++#endif ++ default: ++ break; ++ } ++ return(NULL); ++} ++ ++/*! net_fd_init - function driver usb part intialization ++ * ++ * @return non-zero for failure. ++ */ ++STATIC int net_fd_init(struct usb_network_private *npd, char *info_str) ++{ ++ struct usb_network_params *p = npd->params; ++ ++ printk(KERN_INFO "Copyright (c) 2002-2004 Belcarra Technologies; www.belcarra.com; sl@belcarra.com\n"); ++ NTT = otg_trace_obtain_tag(); ++ ++ /* ++ * Figure out what network mode to operate in. ++ */ ++ if (network_unknown == (npd->network_type = select_network_type(p)) || ++ NULL == (npd->function_driver = select_descriptors(npd->network_type))) { ++ printk(KERN_INFO"configuration selection error"); ++ otg_trace_invalidate_tag(NTT); ++ return -EINVAL; ++ } ++ ++#ifdef CONFIG_OTG_NETWORK_ALLOW_SETID ++ TRACE_MSG2(NTT,"checking idVendor: %04x idProduct: %04x", p->vendor_id, p->product_id); ++ ++ if (p->vendor_id) ++ npd->function_driver->idVendor = cpu_to_le16(p->vendor_id); ++ ++ if (p->product_id) ++ npd->function_driver->idProduct = cpu_to_le16(p->product_id); ++#endif ++ printk(KERN_INFO "%s: %s vendor_id: %04x product_id: %04x\n", ++ __FUNCTION__, info_str, ++ le16_to_cpu(npd->function_driver->idVendor), ++ le16_to_cpu(npd->function_driver->idProduct)); ++ TRACE_MSG3(NTT,"%s vendor_id: %04x product_id: %04x", info_str, ++ le16_to_cpu(npd->function_driver->idVendor), ++ le16_to_cpu(npd->function_driver->idProduct)); ++ ++#ifdef CONFIG_OTG_NETWORK_BLAN_FERMAT ++ TRACE_MSG0(NTT,"fermat"); ++ fermat_init(); ++#endif ++ ++#ifdef CONFIG_OTG_NETWORK_EP0TEST ++ /* ++ * ep0test - test that bus interface can do ZLP on endpoint zero ++ * ++ * This will artificially force iProduct string descriptor to be ++ * exactly the same as the endpoint zero packetsize. When the host ++ * requests this string it will request it not knowing the strength ++ * and will use a max length of 0xff. The bus interface driver must ++ * send a ZLP to terminate the transaction. ++ * ++ * The iProduct descriptor is used because both the Linux and ++ * Windows usb implmentations fetch this in a default enumeration. ++ * ++ */ ++ //UNLESS (ep0test) ++ // ep0test = usbd_endpoint_zero_wMaxPacketsize(function); ++ ++ if (ep0test) { ++ switch (ep0test) { ++ case 8: npd->function_driver->device_description->iProduct = "012"; break; ++ case 16: npd->function_driver->device_description->iProduct = "0123456"; break; ++ case 32: npd->function_driver->device_description->iProduct = "0123456789abcde"; break; ++ case 64: npd->function_driver->device_description->iProduct = "0123456789abcdef0123456789abcde"; break; ++ default: printk(KERN_ERR"%s: ep0test: bad value: %d, must be one of 8, 16, 32 or 64\n", ++ __FUNCTION__, ep0test); return -EINVAL; ++ break; ++ } ++ TRACE_MSG1(NTT,"ep0test: iProduct set to: %s", ++ npd->function_driver->device_description->iProduct); ++ printk(KERN_INFO"%s: ep0test: iProduct set to: %s\n", __FUNCTION__, ++ npd->function_driver->device_description->iProduct); ++ } ++ else { ++ TRACE_MSG0(NTT,"ep0test: not set"); ++ printk(KERN_INFO"%s: ep0test: not set\n", __FUNCTION__); ++ } ++#endif /* CONFIG_OTG_NETWORK_EP0TEST */ ++ ++ ++#ifdef CONFIG_OTG_NETWORK_LOCAL_MACADDR ++ if (NULL == p->local_mac_address_str) { ++ /* There is a configured override, and it has NOT been ++ overridden in turn by a module load time parameter, so use it. */ ++ p->local_mac_address_str = CONFIG_OTG_NETWORK_LOCAL_MACADDR; ++ } ++#endif ++#ifdef CONFIG_OTG_NETWORK_REMOTE_MACADDR ++ if (NULL == p->remote_mac_address_str) { ++ /* There is a configured override, and it has NOT been ++ overridden in turn by a module load time parameter, so use it. */ ++ p->remote_mac_address_str = CONFIG_OTG_NETWORK_REMOTE_MACADDR; ++ } ++#endif ++ if ((macstrtest(p->local_mac_address_str) || macstrtest(p->remote_mac_address_str))) { ++ TRACE_MSG2(NTT,"bad size %s %s", p->local_mac_address_str, p->remote_mac_address_str); ++ otg_trace_invalidate_tag(NTT); ++ return -EINVAL; ++ } ++ ++ set_address(p->local_mac_address_str, npd->local_dev_addr); ++ set_address(p->remote_mac_address_str, npd->remote_dev_addr); ++ npd->encapsulation = simple_crc; ++ ++ if (make_crc_table() || ++ NULL == (npd->function = usbd_register_function(npd->function_driver, "net-fd", NULL))) { ++ if (network_crc32_table) { ++ lkfree(network_crc32_table); ++ network_crc32_table = NULL; ++ } ++ otg_trace_invalidate_tag(NTT); ++ return -EINVAL; ++ } ++ npd->function->privdata = (void *) npd; ++ ++ return 0; ++ ++} ++ ++//_____________________________________________________________________________ ++ ++/*! net_fd_term - driver exit ++ * ++ * Cleans up the module. Deregisters the function driver and destroys the network object. ++ */ ++STATIC void net_fd_term(struct usb_network_private *npd) ++{ ++ if (NULL != npd->function) { ++ usbd_deregister_function(npd->function); ++ npd->function = NULL; ++ } ++ if (network_crc32_table) { ++ lkfree(network_crc32_table); ++ network_crc32_table = NULL; ++ } ++ otg_trace_invalidate_tag(NTT); ++} ++ ++//_________________________________________________________________________________________________ ++ ++ ++/*! net_fd_usb_ops operations ++ */ ++struct net_usb_services net_fd_usb_ops = { ++ .initialize_usb_part = net_fd_init, ++ .terminate_usb_part = net_fd_term, ++ .send_int_notification = net_fd_send_int_blan, ++ .start_xmit = net_fd_start_xmit, ++}; +diff -uNr linux/drivers/no-otg/functions/network/net-fd.h linux/drivers/otg/functions/network/net-fd.h +--- linux/drivers/no-otg/functions/network/net-fd.h 1970-01-01 01:00:00.000000000 +0100 ++++ linux/drivers/otg/functions/network/net-fd.h 2006-09-01 21:41:30.000000000 +0200 +@@ -0,0 +1,48 @@ ++/* ++ * otg/functions/network/net-fd.h - Network Function Driver ++ * ++ * Copyright (c) 2002, 2003, 2004 Belcarra ++ * ++ * By: ++ * Tom Rushworth <tbr@belcarra.com> ++ * Chris Lynne <cl@belcarra.com> ++ * Stuart Lynne <sl@belcarra.com> ++ * Bruce Balden <balden@belcarra.com> ++ * ++ * 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. ++ * ++ */ ++/*! ++ * @file otg/functions/network/net-fd.h ++ * @brief These are the functions exported by the USB specific parts ++ * (i.e. net-fd.c and net-cl.c) for use in the OS specific ++ * layers (e.g. net-l24-os.c). ++ * ++ * ++ * @ingroup NetworkFunction ++ */ ++#ifndef NET_FD_H ++#define NET_FD_H 1 ++ ++struct net_usb_services { ++ int (*initialize_usb_part)(struct usb_network_private *npd, char *info_str); ++ void (*terminate_usb_part)(struct usb_network_private *npd); ++ void (*send_int_notification)(struct usb_network_private *npd, int connected, int data); ++ int (*start_xmit)(struct usb_network_private *npd, __u8 *buff, int len, void *buff_ctx); ++}; ++ ++extern struct net_usb_services net_fd_usb_ops; ++ ++#endif +diff -uNr linux/drivers/no-otg/functions/network/net-l24-os.c linux/drivers/otg/functions/network/net-l24-os.c +--- linux/drivers/no-otg/functions/network/net-l24-os.c 1970-01-01 01:00:00.000000000 +0100 ++++ linux/drivers/otg/functions/network/net-l24-os.c 2006-09-01 21:41:30.000000000 +0200 +@@ -0,0 +1,1395 @@ ++/* ++ * otg/functions/network/net-l24-os.c - Network Function Driver ++ * ++ * Copyright (c) 2002, 2003, 2004 Belcarra Technologies ++ * ++ * By: ++ * Tom Rushworth <tbr@belcarra.com> ++ * Stuart Lynne <sl@belcarra.com> ++ * Bruce Balden <balden@belcarra.com> ++ * ++ * 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. ++ * ++ */ ++/*! ++ * @file otg/functions/network/net-l24-os.c ++ * @brief The Linux 2.4 OS specific upper edge (network interface) ++ * implementation for the Network Function Driver. ++ * ++ * This file implements a standard Linux network driver interface and ++ * the standard Linux 2.4 module init and exit functions. ++ * ++ * If compiled into the kernel, this driver can be used with NFSROOT to ++ * provide the ROOT filesystem. Please note that the kernel NFSROOT support ++ * (circa 2.4.20) can have problems if there are multiple interfaces. So ++ * it is best to ensure that there are no other network interfaces compiled ++ * in. ++ * ++ * ++ * @ingroup NetworkFunction ++ */ ++ ++ ++/* OS-specific #includes */ ++#include <linux/config.h> ++#include <linux/kernel.h> ++#include <linux/version.h> ++#include <linux/init.h> ++#include <linux/list.h> ++#include <linux/smp_lock.h> ++#include <linux/ctype.h> ++#include <linux/time.h> ++#include <linux/timer.h> ++#include <linux/string.h> ++#include <linux/random.h> ++#include <linux/utsname.h> ++#include <linux/kmod.h> ++#include <asm/uaccess.h> ++#include <asm/system.h> ++ ++/* Networking #includes from OS */ ++#include <linux/netdevice.h> ++#include <linux/skbuff.h> ++#include <linux/etherdevice.h> ++#include <net/arp.h> ++#include <linux/rtnetlink.h> ++#include <linux/atmdev.h> ++#include <linux/pkt_sched.h> ++#include <linux/ip.h> ++#include <linux/if_ether.h> ++#include <linux/in.h> ++#include <linux/inetdevice.h> ++ ++ ++ ++/* Belcarra public interfaces */ ++#include <otg/otg-compat.h> ++#include <otg/otg-module.h> ++#include <otg/usbp-chap9.h> ++//#include <otg/usbd-mem.h> ++#include <otg/usbp-func.h> ++ ++/* Belcarra private interfaces SUBJECT TO CHANGE WITHOUT NOTICE */ ++#include "network.h" ++#include "net-os.h" ++#include "net-fd.h" ++ ++MOD_AUTHOR ("sl@belcarra.com, tbr@belcarra.com, balden@belcarra.com"); ++ ++EMBED_LICENSE(); ++ ++MOD_DESCRIPTION ("USB Network Function"); ++ ++ ++ ++EMBED_USBD_INFO ("network_fd 2.0-beta"); ++ ++ ++#define NTT network_fd_trace_tag ++ ++wait_queue_head_t usb_netif_wq; ++#ifdef CONFIG_OTG_NET_NFS_SUPPORT ++int usb_is_configured; ++#endif ++ ++/* Module Parameters ************************************************************************* */ ++ ++STATIC struct usb_network_params params; ++ ++#ifdef CONFIG_OTG_NETWORK_ALLOW_SETID ++// override vendor ID ++static int vendor_id; ++MOD_PARM (vendor_id, "i"); ++MOD_PARM_DESC (vendor_id, "vendor id"); ++ ++// override product ID ++static int product_id; ++MOD_PARM (product_id, "i"); ++MOD_PARM_DESC (product_id, "product id"); ++#endif ++ ++// override local mac address ++static char *local_mac_address_str; ++MOD_PARM (local_mac_address_str, "s"); ++MOD_PARM_DESC (local_mac_address_str, "Local MAC"); ++ ++// override remote mac address ++static char *remote_mac_address_str; ++MOD_PARM (remote_mac_address_str, "s"); ++MOD_PARM_DESC (remote_mac_address_str, "Remote MAC"); ++ ++#ifdef CONFIG_OTG_NETWORK_EP0TEST ++static int ep0test; ++MOD_PARM (ep0test, "i"); ++MOD_PARM_DESC (ep0test, "Test EP0 String Handling - set to ep0 packetsize [8,16,32,64]"); ++#endif ++ ++static int zeroconf; ++MOD_PARM (zeroconf, "i"); ++MOD_PARM_DESC (zeroconf, "Use usbz%d for network name"); ++ ++#if defined(CONFIG_OTG_NETWORK_CDC) ++static int cdc; ++MOD_PARM (cdc, "i"); ++MOD_PARM_DESC (cdc, "Enable CDC mode"); ++#endif ++ ++#ifdef CONFIG_OTG_NETWORK_BASIC ++static int basic; ++MOD_PARM (basic, "i"); ++MOD_PARM_DESC (basic, "Enable BASIC mode"); ++#endif ++ ++#ifdef CONFIG_OTG_NETWORK_BASIC2 ++static int basic2; ++MOD_PARM (basic2, "i"); ++MOD_PARM_DESC (basic2, "Enable BASIC2 mode"); ++#endif ++ ++#ifdef CONFIG_OTG_NETWORK_SAFE ++static int safe; ++MOD_PARM (safe, "i"); ++MOD_PARM_DESC (safe, "Enable SAFE mode"); ++#endif ++ ++#ifdef CONFIG_OTG_NETWORK_BLAN ++static int blan; ++MOD_PARM (blan, "i"); ++MOD_PARM_DESC (blan, "Enable BLAN mode"); ++#endif ++ ++/* End of Module Parameters ****************************************************************** */ ++ ++ ++u8 local_dev_addr[ETH_ALEN]; ++ ++u8 remote_dev_addr[ETH_ALEN]; ++static u8 zeros[ETH_ALEN]; ++ ++extern u32 ip_addr; ++extern u32 router_ip; ++extern u32 network_mask; ++extern u32 dns_server_ip; ++ ++/* Prevent overlapping of bus administrative functions: ++ * ++ * network_function_enable ++ * network_function_disable ++ * network_hard_start_xmit ++ */ ++DECLARE_MUTEX(usbd_network_sem); ++ ++ ++struct net_device Network_net_device; ++struct usb_network_private Usb_network_private; ++ ++void notification_schedule_bh (void); ++int network_urb_sent_bulk (struct usbd_urb *urb, int urb_rc); ++int network_urb_sent_int (struct usbd_urb *urb, int urb_rc); ++ ++ ++//_________________________________________________________________________________________________ ++ ++/* ++ * Synchronization ++ * ++ * ++ * Notification bottom half ++ * ++ * This is a scheduled task that will send an interrupt notification. Because it ++ * is called from the task scheduler context it needs to verify that the device is ++ * still usable. ++ * ++ * static int network_send_int_blan(struct usbd_function_instance *, int ) ++ * static void notification_bh (void *) ++ * void notification_schedule_bh (void) ++ * ++ * ++ * Netdevice functions ++ * ++ * These are called by the Linux network layer. They must be protected by irq locks ++ * if necessary to prevent interruption by IRQ level events. ++ * ++ * int network_init (struct net_device *net_device) ++ * void network_uninit (struct net_device *net_device) ++ * int network_open (struct net_device *net_device) ++ * int network_stop (struct net_device *net_device) ++ * struct net_device_stats *network_get_stats (struct net_device *net_device) ++ * int network_set_mac_addr (struct net_device *net_device, void *p) ++ * void network_tx_timeout (struct net_device *net_device) ++ * int network_set_config (struct net_device *net_device, struct ifmap *map) ++ * int network_stop (struct net_device *net_device) ++ * int network_hard_start_xmit (struct sk_buff *skb, struct net_device *net_device) ++ * int network_do_ioctl (struct net_device *net_device, struct ifreq *rp, int cmd) ++ * ++ * ++ * Data bottom half functions ++ * ++ * These are called from the bus bottom half handler. ++ * ++ * static int network_recv (struct usb_network_private *, struct net_device *, struct sk_buff *) ++ * int network_recv_urb (struct usbd_urb *) ++ * int network_urb_sent (struct usbd_urb *, int ) ++ * ++ * ++ * Hotplug bottom half: ++ * ++ * This is a scheduled task that will send do a hotplug call. Because it is ++ * called from the task scheduler context it needs to verify that the ++ * device is still usable. ++ * ++ * static int hotplug_attach (u32 ip, u32 mask, u32 router, int attach) ++ * static void hotplug_bh (void *data) ++ * void net_os_hotplug (void) ++ * ++ * ++ * Irq level functions: ++ * ++ * These are called at interrupt time do process or respond to USB setup ++ * commands. ++ * ++ * int network_device_request (struct usbd_device_request *) ++ * void network_event_handler (struct usbd_function_instance *function, usbd_device_event_t event, int data) ++ * ++ * ++ * Enable and disable functions: ++ * ++ * void network_function_enable (struct usbd_function_instance *, struct usbd_function_instance *) ++ * void network_function_disable (struct usbd_function_instance *function) ++ * ++ * ++ * Driver initialization and exit: ++ * ++ * static int network_create (struct usb_network_private *) ++ * static void network_destroy (struct usb_network_private *) ++ * ++ * int network_modinit (void) ++ * void network_modexit (void) ++ */ ++ ++ ++//_______________________________USB part Functions_________________________________________ ++ ++/* ++ * net_os_mutex_enter - enter mutex region ++ */ ++void net_os_mutex_enter(struct usb_network_private *npd) ++{ ++ down(&usbd_network_sem); ++} ++ ++/* ++ * net_os_mutex_exit - exit mutex region ++ */ ++void net_os_mutex_exit(struct usb_network_private *npd) ++{ ++ up(&usbd_network_sem); ++} ++ ++/* notification_bh - Bottom half handler to send a notification status ++ * ++ * Send a notification with open/close status ++ * ++ * It should not be possible for this to be called more than once at a time ++ * as it is only called via schedule_task() which protects against a second ++ * invocation. ++ */ ++STATIC void notification_bh (void *data) ++{ ++ struct usb_network_private *npd = (struct usb_network_private *) data; ++ unsigned long flags; ++ local_irq_save(flags); ++ (*(npd->fd_ops->send_int_notification))(npd, npd->flags & NETWORK_OPEN, 0); ++ local_irq_restore(flags); ++ //_MOD_DEC_USE_COUNT; ++ TRACE_MSG1(NTT, "DEC: %d", MOD_IN_USE); ++} ++ ++/* notification_schedule_bh - schedule a call for notification_bh ++ */ ++void net_os_send_notification_later(struct usb_network_private *npd) ++{ ++#ifdef LINUX24 ++ //LINUX 2.4 is the only supported OS to need the USE_COUNT ++ //_MOD_INC_USE_COUNT; ++ TRACE_MSG1(NTT, "INC: %d", MOD_IN_USE); ++ if (!SCHEDULE_WORK(npd->notification_bh)) { ++ //_MOD_DEC_USE_COUNT; ++ TRACE_MSG1(NTT, "DEC: %d", MOD_IN_USE); ++ } ++#else ++ SCHEDULE_WORK(npd->notification_bh); ++ //XXX No provision for failure of schedule .... ++#endif ++} ++ ++/*! net_os_xmit_done - called from USB part when a transmit completes, good or bad. ++ * tx_rc is SEND_FINISHED_ERROR, SEND_CANCELLED or... ++ * buff_ctx is the pointer passed to fd_ops->start_xmit(). ++ * @return non-zero only if network does not exist, ow 0. ++ */ ++int net_os_xmit_done(struct usbd_function_instance *function, void *buff_ctx, int tx_rc) ++{ ++ struct sk_buff *skb = (struct sk_buff *) buff_ctx; ++ struct usb_network_private *npd; ++ struct net_device *net_device; ++ int rc = 0; ++ ++ npd = (function ? (struct usb_network_private *) (function->privdata) : NULL); ++ if (skb) { ++ if (NULL != npd && 0 == (rc = !(npd->flags & NETWORK_CREATED))) { ++ switch (tx_rc) { ++ case SEND_FINISHED_ERROR: ++ npd->stats.tx_errors++; ++ npd->stats.tx_dropped++; ++ break; ++ case SEND_CANCELLED: ++ npd->stats.tx_errors++; ++ npd->stats.tx_carrier_errors++; ++ break; ++ default: ++ break; ++ } ++ ++ npd->avg_queue_entries += npd->queued_entries; ++ npd->queued_entries--; ++ npd->samples++; ++ npd->jiffies += jiffies - *(time_t *) (&skb->cb); ++ npd->queued_bytes -= skb->len; ++ if (NULL != (net_device = npd->net_dev) && ++ netif_queue_stopped(net_device)) { ++ netif_wake_queue(net_device); ++ npd->restarts++; ++ } ++ } ++ dev_kfree_skb_any(skb); ++ } ++ return rc; ++} ++ ++/*! net_os_alloc_buf ++ */ ++void *net_os_alloc_buff(struct usb_network_private *npd, u8 **cp, int n) ++{ ++ struct sk_buff *skb; ++ // allocate skb of appropriate length, reserve 2 to align ip ++ if (!(skb = dev_alloc_skb(n+2))) { ++ *cp = NULL; ++ return(NULL); ++ } ++ skb_reserve(skb, 2); ++ *cp = skb_put(skb, n); ++ return((void*) skb); ++} ++ ++/*! network_recv - function to process an received data URB ++ * ++ * Passes received data to the network layer. Passes skb to network layer. ++ * ++ * @return non-zero for failure. ++ */ ++STATIC __inline__ int network_recv (struct usb_network_private *npd, ++ struct net_device *net_device, struct sk_buff *skb) ++{ ++ int rc; ++#if 0 ++ printk(KERN_INFO"%s: len: %x head: %p data: %p tail: %p\n", __FUNCTION__, ++ skb->len, skb->head, skb->data, skb->tail); ++ { ++ u8 *cp = skb->data; ++ int i; ++ for (i = 0; i < skb->len; i++) { ++ if ((i%32) == 0) { ++ printk("\nrx[%2x] ", i); ++ } ++ printk("%02x ", *cp++); ++ } ++ printk("\n"); ++ } ++#endif ++ ++ // refuse if no device present ++ if (!netif_device_present (net_device)) { ++ TRACE_MSG0(NTT,"device not present"); ++ printk(KERN_INFO"%s: device not present\n", __FUNCTION__); ++ return -EINVAL; ++ } ++ ++ // refuse if no carrier ++ if (!netif_carrier_ok (net_device)) { ++ TRACE_MSG0(NTT,"no carrier"); ++ printk(KERN_INFO"%s: no carrier\n", __FUNCTION__); ++ return -EINVAL; ++ } ++ ++ // refuse if the net device is down ++ if (!(net_device->flags & IFF_UP)) { ++ TRACE_MSG1(NTT,"not up net_dev->flags: %x", net_device->flags); ++ //printk(KERN_INFO"%s: not up net_dev->flags: %x\n", __FUNCTION__, net_device->flags); ++ //npd->stats.rx_dropped++; ++ return -EINVAL; ++ } ++ ++ skb->dev = net_device; ++ skb->pkt_type = PACKET_HOST; ++ skb->protocol = eth_type_trans (skb, net_device); ++ skb->ip_summed = CHECKSUM_UNNECESSARY; ++ ++ //TRACE_MSG4(NTT,"len: %x head: %p data: %p tail: %p", ++ // skb->len, skb->head, skb->data, skb->tail); ++ ++ ++ // pass it up to kernel networking layer ++ if ((rc = netif_rx (skb))) { ++ TRACE_MSG1(NTT,"netif_rx rc: %d", rc); ++ } ++ // QQSV - should be else case for stats? ++ npd->stats.rx_bytes += skb->len; ++ npd->stats.rx_packets++; ++ ++ return 0; ++} ++ ++/*! net_os_recv_buff - forward a received URB, or clean up after a bad one. ++ * buff_ctx == NULL --> just accumulate stats (count 1 bad buff) ++ * crc_bad != 0 --> count a crc error and free buff_ctx/skb ++ * trim --> amount to trim from valid skb ++ */ ++void net_os_recv_buff(struct usb_network_private *npd, void *buff_ctx, int crc_bad, int trim) ++{ ++ struct sk_buff *skb = (struct sk_buff *) buff_ctx; ++ ++ if (NULL == skb) { ++ /* Lower layer never got around to allocating an skb, but ++ needs to count a packet it can't forward. */ ++ npd->stats.rx_frame_errors++; ++ npd->stats.rx_errors++; ++ } else { ++ // There is an skb, either forward it or free it. ++ if (crc_bad) { ++ npd->stats.rx_crc_errors++; ++ npd->stats.rx_errors++; ++ } else if ((npd->flags & NETWORK_ATTACHED) && NULL != npd->net_dev) { ++ // All good, forward it. ++ if (trim) { ++ skb_trim(skb, skb->len - trim); ++ } ++ // all done if network_recv() == 0, ow fall through to error handling ++ RETURN_IF(!network_recv(npd, npd->net_dev, skb)); ++ // Network layer wouldn't take it. ++ // rx_dropped gets counted below, any other counter we need? ++ } ++ // Something wrong, free the skb ++ dev_kfree_skb_any (skb); ++ } ++ // The received buffer didn't get forwarded, so... ++ npd->stats.rx_dropped++; ++} ++ ++/*! net_os_enable ++ * ++ */ ++void net_os_enable(struct usbd_function_instance *function) ++{ ++ //struct usb_network_private *npd = &Usb_network_private; ++ struct usb_network_private *npd = (struct usb_network_private *) function->privdata; ++#if 0 ++ /* This is the first time we've gotten access to the function instance, ++ and it's an allocated structure. Fill in the links to our private data. */ ++ function->privdata = (void *) npd; ++ npd->function = function; ++#endif ++ TRACE_MSG3(NTT,"npd=%p function=%p, f->p=%p",npd,function,function->privdata); ++ ++ // set the network device address from the local device address ++ memcpy(npd->net_dev->dev_addr, npd->local_dev_addr, ETH_ALEN); ++} ++ ++/*! net_os_disable ++ * ++ */ ++extern void net_os_disable(struct usbd_function_instance *function) ++{ ++#if 0 ++ struct usb_network_private *npd = (struct usb_network_private *) (function->privdata); ++ function->privdata = NULL; ++ npd->function = NULL; ++#endif ++} ++ ++/* ++ * ++ */ ++void net_os_carrier_on(struct usb_network_private *npd) ++{ ++ netif_carrier_on(npd->net_dev); ++ netif_wake_queue(npd->net_dev); ++#ifdef CONFIG_OTG_NET_NFS_SUPPORT ++ if (!usb_is_configured) { ++ wake_up(&usb_netif_wq); ++ usb_is_configured = 1; ++ } ++#endif ++ ++} ++ ++/* ++ * ++ */ ++void net_os_carrier_off(struct usb_network_private *npd) ++{ ++ netif_stop_queue(npd->net_dev); ++ netif_carrier_off(npd->net_dev); ++} ++ ++ ++//_______________________________Network Layer Functions____________________________________ ++ ++/* ++ * In general because the functions are called from an independant layer it is necessary ++ * to verify that the device is still ok and to lock interrupts out to prevent in-advertant ++ * closures while in progress. ++ */ ++ ++/* network_init - ++ * ++ * Initializes the specified network device. ++ * ++ * Returns non-zero for failure. ++ */ ++STATIC int network_init (struct net_device *net_device) ++{ ++ TRACE_MSG0(NTT,"no-op"); ++ return 0; ++} ++ ++ ++/* network_uninit - ++ * ++ * Uninitializes the specified network device. ++ */ ++STATIC void network_uninit (struct net_device *net_device) ++{ ++ TRACE_MSG0(NTT,"no-op"); ++ return; ++} ++ ++ ++/* network_open - ++ * ++ * Opens the specified network device. ++ * ++ * Returns non-zero for failure. ++ */ ++STATIC int network_open (struct net_device *net_device) ++{ ++ struct usb_network_private *npd = (struct usb_network_private *)(net_device->priv); ++ unsigned long flags; ++ ++ _MOD_INC_USE_COUNT; ++ TRACE_MSG1(NTT, "INC: %d", MOD_IN_USE); ++ npd->flags |= NETWORK_OPEN; ++ netif_wake_queue (net_device); ++ ++ local_irq_save(flags); ++ (*(npd->fd_ops->send_int_notification))(npd, 1, 0); ++ local_irq_restore(flags); ++ ++#ifdef CONFIG_OTG_NET_NFS_SUPPORT ++ if (!usb_is_configured) { ++ if (!in_interrupt()) { ++ TRACE_MSG0(NTT,"Please replug USB cable and then ifconfig host interface.\n"); ++ printk(KERN_ERR"Please replug USB cable and then ifconfig host interface.\n"); ++ interruptible_sleep_on(&usb_netif_wq); ++ } ++ else { ++ TRACE_MSG_0(NTT,"Warning! In interrupt\n"); ++ printk(KERN_ERR"Warning! In interrupt\n"); ++ } ++ } ++#endif ++ return 0; ++} ++ ++ ++/* network_stop - ++ * ++ * Stops the specified network device. ++ * ++ * Returns non-zero for failure. ++ */ ++STATIC int network_stop (struct net_device *net_device) ++{ ++ struct usb_network_private *npd = (struct usb_network_private *)(net_device->priv); ++ unsigned long flags; ++ ++ TRACE_MSG0(NTT,"-"); ++ ++ //netif_stop_queue (net_device); ++ ++ npd->flags &= ~NETWORK_OPEN; ++ ++ local_irq_save(flags); ++ (*(npd->fd_ops->send_int_notification))(npd, 0, 0); ++ local_irq_restore(flags); ++ ++ _MOD_DEC_USE_COUNT; ++ TRACE_MSG1(NTT, "DEC: %d", MOD_IN_USE); ++ return 0; ++} ++ ++ ++/* network_get_stats - ++ * ++ * Gets statistics from the specified network device. ++ * ++ * Returns pointer to net_device_stats structure with the required information. ++ */ ++STATIC struct net_device_stats *network_get_stats (struct net_device *net_device) ++{ ++ struct usb_network_private *npd = (struct usb_network_private *)(net_device->priv); ++ return &npd->stats; ++} ++ ++ ++/* network_set_mac_addr ++ * ++ * Sets the MAC address of the specified network device. Fails if the device is in use. ++ * ++ * Returns non-zero for failure. ++ */ ++STATIC int network_set_mac_addr (struct net_device *net_device, void *p) ++{ ++ struct usb_network_private *npd = (struct usb_network_private *)(net_device->priv); ++ struct sockaddr *addr = p; ++ unsigned long flags; ++ ++ TRACE_MSG0(NTT,"--"); ++ ++ RETURN_EBUSY_IF(netif_running (net_device)); ++ local_irq_save(flags); ++ memcpy(net_device->dev_addr, addr->sa_data, net_device->addr_len); ++ memcpy(npd->local_dev_addr, npd->net_dev->dev_addr, ETH_ALEN); ++ local_irq_restore(flags); ++ return 0; ++} ++ ++ ++/* network_tx_timeout - ++ * ++ * Tells the specified network device that its current transmit attempt has timed out. ++ */ ++STATIC void network_tx_timeout (struct net_device *net_device) ++{ ++ struct usb_network_private *npd = (struct usb_network_private *)(net_device->priv); ++#if 0 ++ //printk(KERN_ERR"%s:\n", __FUNCTION__); ++ npd->stats.tx_errors++; ++ npd->stats.tx_dropped++; ++ usbd_cancel_urb_irq (npd->bus, NULL); // QQSV ++#endif ++#if 0 ++ // XXX attempt to wakeup the host... ++ if ((npd->network_type == network_blan) && (npd->flags & NETWORK_OPEN)) { ++ notification_schedule_bh(); ++ } ++#endif ++} ++ ++ ++/** network_set_config - ++ * ++ * Sets the specified network device's configuration. Fails if the device is in use. ++ * ++ * map An ifmap structure containing configuration values. ++ * Those values which are non-zero/non-null update the corresponding fields ++ * in net_device. ++ * ++ * Returns non-zero for failure. ++ */ ++STATIC int network_set_config (struct net_device *net_device, struct ifmap *map) ++{ ++ RETURN_EBUSY_IF(netif_running (net_device)); ++ if (map->base_addr) ++ net_device->base_addr = map->base_addr; ++ if (map->mem_start) ++ net_device->mem_start = map->mem_start; ++ if (map->irq) ++ net_device->irq = map->irq; ++ return 0; ++} ++ ++ ++/* network_change_mtu - ++ * ++ * Sets the specified network device's MTU. Fails if the new value is larger and ++ * the device is in use. ++ * ++ * Returns non-zero for failure. ++ */ ++STATIC int network_change_mtu (struct net_device *net_device, int mtu) ++{ ++ struct usb_network_private *npd = (struct usb_network_private *)(net_device->priv); ++ ++ TRACE_MSG0(NTT,"-"); ++ ++ RETURN_EBUSY_IF(netif_running (net_device)); ++ RETURN_EBUSY_IF(mtu > npd->mtu); ++ ++ npd->mtu = mtu; ++ return 0; ++} ++ ++//_________________________________________________________________________________________________ ++// network_hard_start_xmit ++ ++/* ++ * net_os_start_xmit - start sending an skb, with usbd_network_sem already held. ++ * ++ * Return non-zero (1) if busy. QQSV - this code always returns 0. ++ */ ++STATIC __inline__ int net_os_start_xmit (struct sk_buff *skb, struct net_device *net_device) ++{ ++ struct usb_network_private *npd = (struct usb_network_private *)(net_device->priv); ++ int rc; ++ ++ if (!netif_carrier_ok(net_device)) { ++ dev_kfree_skb_any (skb); ++ npd->stats.tx_dropped++; ++ //netif_stop_queue(net_device); // XXX ++ // XXX this is what we should do, blows up on some 2.4.20 kernels ++ // return(NET_XMIT_DROP); ++ return(0); ++ } ++ ++ // stop queue, it will be restarted only when we are ready for another skb ++ netif_stop_queue(net_device); ++ npd->stopped++; ++ ++ // Set the timestamp for tx timeout ++ net_device->trans_start = jiffies; ++ ++ // XXX request IN, should start a timer to resend this. ++ if (npd->data_notify) ++ (*(npd->fd_ops->send_int_notification))(npd, 0, 1); ++ ++ rc = (*(npd->fd_ops->start_xmit))(npd,skb->data,skb->len,(void*)skb); ++ switch (rc) { ++ case 0: ++ TRACE_MSG1(NTT,"OK: %d", rc); ++ // update some stats ++ npd->queued_entries++; ++ npd->queued_bytes += skb->len; ++ npd->stats.tx_packets++; ++ npd->stats.tx_bytes += skb->len; ++ ++ // Should we restart network queue ++ if ((npd->queued_entries < npd->max_queue_entries) && ++ (npd->queued_bytes < npd->max_queue_bytes)) ++ netif_wake_queue (net_device); ++ ++ break; ++ ++ case -EINVAL: ++ case -EUNATCH: ++ TRACE_MSG1(NTT,"not attached, send failed: %d", rc); ++ printk(KERN_ERR"%s: not attached, send failed: %d\n", __FUNCTION__, rc); ++ npd->stats.tx_errors++; ++ npd->stats.tx_carrier_errors++; ++ netif_wake_queue(net_device); ++ break; ++ ++ case -ENOMEM: ++ TRACE_MSG1(NTT,"no mem, send failed: %d", rc); ++ printk(KERN_ERR"%s: no mem, send failed: %d\n", __FUNCTION__, rc); ++ npd->stats.tx_errors++; ++ npd->stats.tx_fifo_errors++; ++ netif_wake_queue(net_device); ++ break; ++ ++ case -ECOMM: ++ TRACE_MSG2(NTT,"comm failure, send failed: %d %p", rc, net_device); ++ printk(KERN_ERR"%s: comm failure, send failed: %d %p\n", __FUNCTION__, rc, net_device); ++ // Leave the IF queue stopped. ++ npd->stats.tx_dropped++; ++ break; ++ ++ } ++ if (0 != rc) { ++ dev_kfree_skb_any (skb); ++ // XXX this is what we should do, blows up on some 2.4.20 kernels ++ // return(NET_XMIT_DROP); ++ return(0); ++ } ++ ++ return 0; ++} ++ ++/* ++ * network_hard_start_xmit - start sending an skb. Called by the OS network layer. ++ * ++ * Return non-zero (1) if busy. QQSV - this code always returns 0. ++ */ ++STATIC int network_hard_start_xmit (struct sk_buff *skb, struct net_device *net_device) ++{ ++ int rc; ++ down(&usbd_network_sem); ++ rc = net_os_start_xmit(skb,net_device); ++ up(&usbd_network_sem); ++ return rc; ++} ++ ++ ++/* network_do_ioctl - perform an ioctl call ++ * ++ * Carries out IOCTL commands for the specified network device. ++ * ++ * rp Points to an ifreq structure containing the IOCTL parameter(s). ++ * cmd The IOCTL command. ++ * ++ * Returns non-zero for failure. ++ */ ++STATIC int network_do_ioctl (struct net_device *net_device, struct ifreq *rp, int cmd) ++{ ++ return -ENOIOCTLCMD; ++} ++ ++//_________________________________________________________________________________________________ ++ ++struct net_device Network_net_device = { ++ get_stats: network_get_stats, ++ tx_timeout: network_tx_timeout, ++ do_ioctl: network_do_ioctl, ++ set_config: network_set_config, ++ set_mac_address: network_set_mac_addr, ++ hard_start_xmit: network_hard_start_xmit, ++ change_mtu: network_change_mtu, ++ init: network_init, ++ uninit: network_uninit, ++ open: network_open, ++ stop: network_stop, ++ priv: &Usb_network_private, ++}; ++ ++//_________________________________________________________________________________________________ ++ ++#ifdef CONFIG_OTG_NETWORK_BLAN_AUTO_CONFIG ++/* sock_ioctl - perform an ioctl call to inet device ++ */ ++int sock_ioctl(u32 cmd, struct ifreq *ifreq) ++{ ++ int rc = 0; ++ mm_segment_t save_get_fs = get_fs(); ++ TRACE_MSG1(NTT, "cmd: %x", cmd); ++ set_fs(get_ds()); ++ rc = devinet_ioctl(cmd, ifreq); ++ set_fs(save_get_fs); ++ return rc; ++} ++ ++/* sock_addr - setup a socket address for specified interface ++ */ ++static int sock_addr(char * ifname, u32 cmd, u32 s_addr) ++{ ++ struct ifreq ifreq; ++ struct sockaddr_in *sin = (void *) &(ifreq.ifr_ifru.ifru_addr); ++ ++ TRACE_MSG2(NTT, "ifname: %s addr: %x", ifname, ntohl(s_addr)); ++ ++ memset(&ifreq, 0, sizeof(ifreq)); ++ strcpy(ifreq.ifr_ifrn.ifrn_name, ifname); ++ ++ sin->sin_family = AF_INET; ++ sin->sin_addr.s_addr = s_addr; ++ ++ return sock_ioctl(cmd, &ifreq); ++} ++ ++ ++/* sock_flags - set flags for specified interface ++ */ ++static int sock_flags(char * ifname, u16 oflags, u16 sflags, u16 rflags) ++{ ++ int rc = 0; ++ struct ifreq ifreq; ++ ++ TRACE_MSG4(NTT, "ifname: %s oflags: %x s_flags: %x r_flags: %x", ifname, oflags, sflags, rflags); ++ ++ memset(&ifreq, 0, sizeof(ifreq)); ++ strcpy(ifreq.ifr_ifrn.ifrn_name, ifname); ++ ++ oflags |= sflags; ++ oflags &= ~rflags; ++ ifreq.ifr_flags = oflags; ++ ++ TRACE_MSG1(NTT, "-> ifr_flags: %x ", ifreq.ifr_flags); ++ ++ THROW_IF ((rc = sock_ioctl(SIOCSIFFLAGS, &ifreq)), error); ++ ++ TRACE_MSG1(NTT, "<- ifr_flags: %x ", ifreq.ifr_flags); ++ ++ CATCH(error) { ++ TRACE_MSG1(NTT, "ifconfig: cannot get/set interface flags (%d)", rc); ++ return rc; ++ } ++ return rc; ++} ++ ++/* network_attach - configure interface ++ * ++ * This will use socket calls to configure the interface to the supplied ++ * ip address and make it active. ++ */ ++STATIC int network_attach(struct usb_network_private *npd, u32 ip, u32 mask, u32 router, int attach) ++{ ++ int err = 0; ++ ++ if (attach) { ++ TRACE_MSG5(NTT, "ATTACH npd: %x ip: %08x mask: %08x router: %08x attach: %d", npd, ip, mask, router, attach); ++ u16 oflags = (npd && npd->net_dev) ? npd->net_dev->flags : 0; ++ ++ if (npd->net_dev) ++ memcpy(npd->net_dev->dev_addr, npd->local_dev_addr, ETH_ALEN); ++ ++ /* setup ip address, netwask, and broadcast address */ ++ if (ip) { ++ THROW_IF ((err = sock_addr("usbl0", SIOCSIFADDR, htonl(ip))), error); ++ if (mask) { ++ THROW_IF ((err = sock_addr("usbl0", SIOCSIFNETMASK, htonl(mask))), error); ++ THROW_IF ((err = sock_addr("usbl0", SIOCSIFBRDADDR, htonl(ip | ~mask))), error); ++ } ++ /* bring the interface up */ ++ THROW_IF ((err = sock_flags("usbl0", oflags, IFF_UP, 0)), error); ++ } ++ } ++ else { ++ TRACE_MSG5(NTT, "DETACH npd: %x ip: %08x mask: %08x router: %08x attach: %d", npd, ip, mask, router, attach); ++ u16 oflags = (npd && npd->net_dev) ? npd->net_dev->flags : 0; ++ /* bring the interface down */ ++ THROW_IF ((err = sock_flags("usbl0", oflags, 0, IFF_UP)), error); ++ } ++ ++ CATCH(error) { ++ TRACE_MSG1(NTT, "ifconfig: cannot configure interface (%d)", err); ++ return err; ++ } ++ return 0; ++} ++ ++/* network_config - configure network ++ * ++ * Check connected status and load/unload as appropriate. ++ * ++ */ ++STATIC void config_bh (void *data) ++{ ++ struct usb_network_private *npd = (struct usb_network_private *) data; ++ struct usbd_function_instance *function = npd->function; ++ ++ TRACE_MSG5(NTT, "function: %x enabled: %x status: %x state: %x npd->config_status: %d", ++ function, ++ npd->flags & NETWORK_ENABLED, ++ usbd_get_device_status(function), ++ usbd_get_device_state(function), ++ npd->config_status); ++ ++ if ((npd->flags & NETWORK_ENABLED ) && (function && (USBD_OK == usbd_get_device_status(function)) ++ && (STATE_CONFIGURED == usbd_get_device_state(function)) ++ && ip_addr ++ )) ++ { ++ TRACE_MSG2(NTT,"BUS state: %d status: %d", usbd_get_device_state(function), usbd_get_device_status(function)); ++ if (config_attached != npd->config_status) { ++ TRACE_MSG0(NTT,"ATTACH"); ++ npd->config_status = config_attached; ++ network_attach (npd, ip_addr, network_mask, router_ip, 1); ++ } ++ return; ++ } ++ ++ if (config_detached != npd->config_status) { ++ TRACE_MSG0(NTT,"DETACH"); ++ npd->config_status = config_detached; ++ network_attach (npd, ip_addr, network_mask, router_ip, 0); ++ } ++ //_MOD_DEC_USE_COUNT; ++ TRACE_MSG1(NTT, "DEC: %d", MOD_IN_USE); ++} ++ ++/* ++ * net_os_config - schedule a call to config bottom half ++ */ ++void net_os_config(struct usb_network_private *npd) ++{ ++ //_MOD_INC_USE_COUNT; ++ TRACE_MSG1(NTT, "INC: %d", MOD_IN_USE); ++ if (!SCHEDULE_WORK(npd->config_bh)) { ++ //_MOD_DEC_USE_COUNT; ++ TRACE_MSG1(NTT, "DEC: %d", MOD_IN_USE); ++ } ++} ++#else /* CONFIG_OTG_NETWORK_BLAN_AUTO_CONFIG */ ++void net_os_config(struct usb_network_private *npd) ++{ ++ TRACE_MSG0(NTT, "NOT CONFIGURED"); ++ // Do nothing, config not configured. ++} ++#endif /* CONFIG_OTG_NETWORK_BLAN_AUTO_CONFIG */ ++//______________________________________ Hotplug Functions ________________________________________ ++ ++#ifdef CONFIG_OTG_NETWORK_HOTPLUG ++ ++#define AGENT "network_fd" ++ ++/* hotplug_attach - call hotplug ++ */ ++STATIC int hotplug_attach(struct usb_network_private *npd, u32 ip, u32 mask, u32 router, int attach) ++{ ++ static int count = 0; ++ char *argv[3]; ++ char *envp[10]; ++ char ifname[20+12 + IFNAMSIZ]; ++ int i; ++ char count_str[20]; ++ ++ RETURN_EINVAL_IF(!hotplug_path[0]); ++ ++ argv[0] = hotplug_path; ++ argv[1] = AGENT; ++ argv[2] = 0; ++ ++ sprintf (ifname, "INTERFACE=%s", npd->net_dev->name); ++ sprintf (count_str, "COUNT=%d", count++); ++ ++ i = 0; ++ envp[i++] = "HOME=/"; ++ envp[i++] = "PATH=/sbin:/bin:/usr/sbin:/usr/bin"; ++ envp[i++] = ifname; ++ ++ ++ if (attach) { ++ unsigned char *cp; ++ char ip_str[20+32]; ++ char mask_str[20+32]; ++ char router_str[20+32]; ++ u32 nh; ++ ++ nh = htonl(ip); ++ cp = (unsigned char*) &nh; ++ sprintf (ip_str, "IP=%d.%d.%d.%d", cp[0], cp[1], cp[2], cp[3]); ++ ++ nh = htonl(mask); ++ cp = (unsigned char*) &nh; ++ sprintf (mask_str, "MASK=%d.%d.%d.%d", cp[0], cp[1], cp[2], cp[3]); ++ ++ nh = htonl(router); ++ cp = (unsigned char*) &nh; ++ sprintf (router_str, "ROUTER=%d.%d.%d.%d", cp[0], cp[1], cp[2], cp[3]); ++ ++ //TRACE_MSG3(NTT, "attach %s %s %s", ifname, ip_str, count_str); ++ ++ envp[i++] = "ACTION=attach"; ++ envp[i++] = ip_str; ++ envp[i++] = mask_str; ++ envp[i++] = router_str; ++ ++ TRACE_MSG3(NTT, "attach ip: %08x mask: %08x router: %08x", ip, mask, router); ++ } ++ else { ++ //TRACE_MSG2(NTT,"detach %s %s", ifname, count_str); ++ envp[i++] = "ACTION=detach"; ++ TRACE_MSG3(NTT, "detach ip: %08x mask: %08x router: %08x", ip, mask, router); ++ } ++ ++ envp[i++] = count_str; ++ envp[i++] = 0; ++ ++ return call_usermodehelper (argv[0], argv, envp); ++} ++ ++ ++/* hotplug_bh - bottom half handler to call hotplug script to signal ATTACH or DETACH ++ * ++ * Check connected status and load/unload as appropriate. ++ * ++ * It should not be possible for this to be called more than once at a time ++ * as it is only called via schedule_task() which protects against a second ++ * invocation. ++ */ ++STATIC void hotplug_bh (void *data) ++{ ++ //struct usb_network_private *network_private = &Usb_network_private; ++ struct usb_network_private *npd = (struct usb_network_private *) data; ++ struct usbd_function_instance *function = NULL; ++ ++ UNLESS (npd) { ++ //_MOD_DEC_USE_COUNT; ++ TRACE_MSG1(NTT, "NPD NULL DEC: %d", MOD_IN_USE); ++ return; ++ } ++ ++ function = npd->function; ++ ++ TRACE_MSG2(NTT, "function: %x npd->hotplug_status: %d", function, npd->hotplug_status); ++ ++ ++ if ((npd->flags & NETWORK_ENABLED ) && (function && (USBD_OK == usbd_get_device_status(function)) ++ && (STATE_CONFIGURED == usbd_get_device_state(function)))) ++ { ++ TRACE_MSG2(NTT,"BUS state: %d status: %d", usbd_get_device_state(function), usbd_get_device_status(function)); ++ if (hotplug_attached != npd->hotplug_status) { ++ TRACE_MSG0(NTT,"ATTACH"); ++ npd->hotplug_status = hotplug_attached; ++ hotplug_attach (npd, ip_addr, network_mask, router_ip, 1); ++ } ++ return; ++ } ++ ++ if (hotplug_detached != npd->hotplug_status) { ++ TRACE_MSG0(NTT,"DETACH"); ++ npd->hotplug_status = hotplug_detached; ++ hotplug_attach (npd, ip_addr, network_mask, router_ip, 0); ++ } ++ //_MOD_DEC_USE_COUNT; ++ TRACE_MSG1(NTT, "DEC: %d", MOD_IN_USE); ++} ++ ++/* ++ * net_os_hotplug - schedule a call to hotplug bottom half ++ */ ++void net_os_hotplug(struct usb_network_private *npd) ++{ ++ //_MOD_INC_USE_COUNT; ++ TRACE_MSG1(NTT, "INC: %d", MOD_IN_USE); ++ if (!SCHEDULE_WORK(npd->hotplug_bh)) { ++ //_MOD_DEC_USE_COUNT; ++ TRACE_MSG1(NTT, "DEC: %d", MOD_IN_USE); ++ } ++} ++#else ++void net_os_hotplug(struct usb_network_private *npd) ++{ ++ TRACE_MSG0(NTT, "NOT CONFIGURED"); ++ // Do nothing, hotplug not configured. ++} ++#endif /* CONFIG_OTG_NETWORK_HOTPLUG */ ++ ++//_________________________________________________________________________________________________ ++ ++/* network_create - create and initialize network private structure ++ * ++ * Returns non-zero for failure. ++ */ ++STATIC int network_create(struct usb_network_private *npd) ++{ ++ TRACE_MSG0(NTT,"entered"); ++ ++ // Set some fields to generic defaults and register the network device with the kernel networking code ++ ++ memset(&npd->stats, 0, sizeof npd->stats); ++ ++ ether_setup(npd->net_dev); ++ RETURN_EINVAL_IF (register_netdev(npd->net_dev)); ++ npd->flags |= NETWORK_REGISTERED; ++ ++ netif_stop_queue(npd->net_dev); ++ netif_carrier_off(npd->net_dev); ++ npd->net_dev->flags &= ~IFF_UP; ++ ++ npd->flags |= NETWORK_CREATED; ++ ++ npd->maxtransfer = MAXFRAMESIZE + 4 + 64; ++ ++ TRACE_MSG0(NTT,"finis"); ++ return 0; ++} ++ ++/* network_destroy - destroy network private struture ++ * ++ * Destroys the network interface referenced by the global variable @c Network_net_device. ++ */ ++STATIC void network_destroy(struct usb_network_private *npd) ++{ ++ if (npd->flags & NETWORK_REGISTERED) { ++ netif_stop_queue(npd->net_dev); ++ netif_carrier_off(npd->net_dev); ++ unregister_netdev(npd->net_dev); ++ } ++ npd->flags = 0; ++} ++ ++ ++//______________________________________module_init and module_exit________________________________ ++ ++#ifdef CONFIG_OTG_NETWORK_BLAN_AUTO_CONFIG ++ ++/*! network_proc_read_ip - implement proc file system read. ++ * Standard proc file system read function. ++ */ ++static ssize_t network_proc_read_ip (struct file *file, char *buf, size_t count, loff_t * pos) ++{ ++ unsigned long page; ++ int len = 0; ++ char *bp; ++ int index = (*pos)++; ++ ++ RETURN_ZERO_IF(index); ++ ++ // get a page, max 4095 bytes of data... ++ RETURN_ENOMEM_UNLESS ((page = GET_KERNEL_PAGE())); ++ ++ bp = (char *) page; ++ ++ len = sprintf(bp, "%d.%d.%d.%d\n", ++ (router_ip >> 24) & 0xff, ++ (router_ip >> 16) & 0xff, ++ (router_ip >> 8) & 0xff, ++ (router_ip) & 0xff ++ ); ++ ++ if (copy_to_user(buf, (char *) page, len)) ++ len = -EFAULT; ++ ++ free_page (page); ++ return len; ++} ++ ++static struct file_operations otg_message_proc_switch_functions = { ++ read: network_proc_read_ip, ++}; ++ ++#endif ++ ++ ++/* network_modinit - driver intialization ++ * ++ * Returns non-zero for failure. ++ */ ++STATIC int network_modinit (void) ++{ ++ // Pickup and link together the various global structs. ++ struct usb_network_private *npd = &Usb_network_private; ++ #ifdef CONFIG_PROC_FS ++ struct proc_dir_entry *p; ++ #endif /* CONFIG_PROC_FS */ ++ npd->fd_ops = &net_fd_usb_ops; ++ npd->params = ¶ms; ++ npd->net_dev = &Network_net_device; ++ npd->net_dev->priv = (void *) npd; ++#ifdef CONFIG_OTG_NETWORK_ALLOW_SETID ++ params.vendor_id = vendor_id; ++ params.product_id = product_id; ++#endif ++ params.local_mac_address_str = local_mac_address_str; ++ params.remote_mac_address_str = remote_mac_address_str; ++#ifdef CONFIG_OTG_NETWORK_EP0TEST ++ params.ep0test = ep0test; ++#endif ++ params.zeroconf = zeroconf; ++#if defined(CONFIG_OTG_NETWORK_CDC) ++ params.cdc = cdc; ++#endif ++#ifdef CONFIG_OTG_NETWORK_BASIC ++ params.basic = basic; ++#endif ++#ifdef CONFIG_OTG_NETWORK_BASIC2 ++ params.basic2 = basic2; ++#endif ++#ifdef CONFIG_OTG_NETWORK_SAFE ++ params.safe = safe; ++#endif ++#ifdef CONFIG_OTG_NETWORK_BLAN ++ params.blan = blan; ++#endif ++ // That should be it for reference to globals except for network_modexit(). ++ ++ init_waitqueue_head(&usb_netif_wq); ++ PREPARE_WORK_ITEM(npd->notification_bh, notification_bh,npd); ++#ifdef CONFIG_OTG_NETWORK_BLAN_AUTO_CONFIG ++ PREPARE_WORK_ITEM(npd->config_bh, config_bh, npd); ++ #ifdef CONFIG_PROC_FS ++ if ((p = create_proc_entry ("network_ip", 0666, 0))) ++ p->proc_fops = &otg_message_proc_switch_functions; ++ #endif /* CONFIG_PROC_FS */ ++#endif /* CONFIG_OTG_NETWORK_BLAN_AUTO_CONFIG */ ++#ifdef CONFIG_OTG_NETWORK_HOTPLUG ++ PREPARE_WORK_ITEM(npd->hotplug_bh, hotplug_bh, npd); ++#endif /* CONFIG_OTG_NETWORK_HOTPLUG */ ++ ++ if (0 != (*(npd->fd_ops->initialize_usb_part))(npd,"linux network_fd")) { ++ // If the usb part fails, there are no trace functions. ++ return -EINVAL; ++ } ++ ++ memcpy(npd->net_dev->dev_addr, npd->local_dev_addr, ETH_ALEN); ++ /* QQSV - this is very strange, since npd->network type can have ++ a number of different values. setting zeroconf is not going to ++ be quite as obvious as it might at first seem. */ ++ strncpy(npd->net_dev->name, ((npd->network_type == zeroconf) ? "usbz0" : ++ (network_blan ? "usbl0" : "usbb0")), 6); ++ ++ if (network_create(npd)) { ++ (*(npd->fd_ops->terminate_usb_part))(npd); ++ return -EINVAL; ++ } ++ ++ return 0; ++} ++ ++//_____________________________________________________________________________ ++ ++/* network_modexit - driver exit ++ * ++ * Cleans up the module. Deregisters the function driver and destroys the network object. ++ */ ++STATIC void network_modexit (void) ++{ ++ struct usb_network_private *npd = &Usb_network_private; ++ ++ #ifdef CONFIG_PROC_FS ++ remove_proc_entry("network_ip", NULL); ++ #endif /* CONFIG_PROC_FS */ ++ ++ while (PENDING_WORK_ITEM(npd->notification_bh)) { ++ // TRACE_MSG pointless here, module will be gon before we can look at it. ++ printk(KERN_ERR"%s: waiting for notificationhotplug bh\n", __FUNCTION__); ++ schedule_timeout(10 * HZ); ++ } ++#ifdef CONFIG_OTG_NETWORK_BLAN_AUTO_CONFIG ++ while (PENDING_WORK_ITEM(npd->config_bh)) { ++ printk(KERN_ERR"%s: waiting for config bh\n", __FUNCTION__); ++ schedule_timeout(10 * HZ); ++ } ++#endif /* CONFIG_OTG_NETWORK_BLAN_AUTO_CONFIG */ ++ ++#ifdef CONFIG_OTG_NETWORK_HOTPLUG ++ while (PENDING_WORK_ITEM(npd->hotplug_bh)) { ++ printk(KERN_ERR"%s: waiting for hotplug bh\n", __FUNCTION__); ++ schedule_timeout(10 * HZ); ++ } ++#endif ++ /* Which order to tear down the two parts? No matter which goes first, ++ the other could still try to send data through the now-torn-down part, ++ so go with usb last, because it has the otg_trace cleanup. */ ++ network_destroy(npd); ++ if (npd->fd_ops) ++ (*(npd->fd_ops->terminate_usb_part))(npd); ++} ++ ++//_________________________________________________________________________________________________ ++ ++module_init (network_modinit); ++module_exit (network_modexit); ++ +diff -uNr linux/drivers/no-otg/functions/network/net-os.h linux/drivers/otg/functions/network/net-os.h +--- linux/drivers/no-otg/functions/network/net-os.h 1970-01-01 01:00:00.000000000 +0100 ++++ linux/drivers/otg/functions/network/net-os.h 2006-09-01 21:41:30.000000000 +0200 +@@ -0,0 +1,107 @@ ++/* ++ * otg/functions/network/net-os.h ++ * ++ * Copyright (c) 2003, 2004 Belcarra ++ * ++ * By: ++ * Tom Rushworth <tbr@belcarra.com>, ++ * Stuart Lynne <sl@belcarra.com>, ++ * Bruce Balden <balden@belcarra.com> ++ */ ++ ++/*! ++ * @file otg/functions/network/net-os.h ++ * @brief Structures and function definitions required for implementation ++ * of the OS specific upper edge (network interface) for the Network ++ * Function Driver. ++ * ++ * A USB network driver is composed of two pieces: ++ * 1) An OS specific piece that handles creating and operating ++ * a network device for the given OS. ++ * 2) A USB specific piece that interfaces either with the host ++ * usbcore layer, or with the device usbdcore layer. ++ * ++ * If the USB piece interfaces with the host usbcore layer you get ++ * a network class driver. If the USB piece interfaces with the usbdcore ++ * layer you get a network function driver. ++ * ++ * This file describes the functions exported by the various net-*-os.c ++ * files (implementing (1)) for use in net-fd.c (2). ++ * ++ * ++ * @ingroup NetworkFunction ++ */ ++ ++#ifndef NET_OS_H ++#define NET_OS_H 1 ++ ++/* ++ * net_os_mutex_enter - enter mutex region ++ */ ++extern void net_os_mutex_enter(struct usb_network_private *npd); ++ ++/* ++ * net_os_mutex_exit - exit mutex region ++ */ ++extern void net_os_mutex_exit(struct usb_network_private *npd); ++ ++/* ++ * net_os_send_notification_later - schedule a callback to the USB part to send ++ * an INT notification. ++ */ ++extern void net_os_send_notification_later(struct usb_network_private *npd); ++ ++/* ++ * net_os_xmit_done - called from USB part when a transmit completes, good or bad. ++ * tx_rc is SEND_FINISHED_ERROR, SEND_CANCELLED or... ++ * buff_ctx is the pointer passed to fd_ops->start_xmit(). ++ * Return non-zero only if network does not exist, ow 0. ++ */ ++extern int net_os_xmit_done(struct usbd_function_instance *function, void *buff_ctx, int tx_rc); ++ ++/* ++ * ++ */ ++extern void *net_os_alloc_buff(struct usb_network_private *npd, u8 **cp, int n); ++ ++/* ++ * net_os_recv_buff - forward a received URB, or clean up after a bad one. ++ * buff_ctx == NULL --> just accumulate stats (count 1 bad buff) ++ * crc_bad != 0 --> count a crc error and free buff_ctx/skb ++ * trim --> amount to trim from valid buffer (i.e. shorten ++ * from amount requested in net_os_alloc_buff(..... n). ++ * This will be called from interrupt context. ++ */ ++extern void net_os_recv_buff(struct usb_network_private *npd, void *buff_ctx, int crc_bad, int trim); ++ ++/* ++ * net_os_config - schedule a network hotplug event if the OS supports hotplug. ++ */ ++extern void net_os_config(struct usb_network_private *npd); ++ ++/* ++ * net_os_hotplug - schedule a network hotplug event if the OS supports hotplug. ++ */ ++extern void net_os_hotplug(struct usb_network_private *npd); ++ ++/* ++ * ++ */ ++extern void net_os_enable(struct usbd_function_instance *function); ++ ++/* ++ * ++ */ ++extern void net_os_disable(struct usbd_function_instance *function); ++ ++/* ++ * ++ */ ++extern void net_os_carrier_on(struct usb_network_private *npd); ++ ++/* ++ * ++ */ ++extern void net_os_carrier_off(struct usb_network_private *npd); ++ ++#endif +diff -uNr linux/drivers/no-otg/functions/network/network.h linux/drivers/otg/functions/network/network.h +--- linux/drivers/no-otg/functions/network/network.h 1970-01-01 01:00:00.000000000 +0100 ++++ linux/drivers/otg/functions/network/network.h 2006-09-01 21:41:30.000000000 +0200 +@@ -0,0 +1,348 @@ ++/* ++ * otg/functions/network/network.h - Network Function Driver ++ * ++ * Copyright (c) 2002, 2003, 2004 Belcarra ++ * ++ * By: ++ * Tom Rushworth <tbr@belcarra.com> ++ * Chris Lynne <cl@belcarra.com> ++ * Stuart Lynne <sl@belcarra.com> ++ * Bruce Balden <balden@belcarra.com> ++ * ++ * 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. ++ */ ++/*! ++ * @defgroup NetworkFunction Network ++ * @ingroup functiongroup ++ */ ++/*! ++ * @file otg/functions/network/network.h ++ * @brief Network Function Driver private defines ++ * ++ * This defines the internal and private structures required ++ * by the Network Function Driver. ++ * ++ * @ingroup NetworkFunction ++ */ ++ ++/*! This is a test ++ */ ++#ifndef NETWORK_FD_H ++#define NETWORK_FD_H 1 ++ ++#include <otg/otg-trace.h> ++ ++extern otg_tag_t network_fd_trace_tag; ++ ++// Some platforms need to get rid of "static" when using GDB or looking at ksyms ++//#define STATIC ++#define STATIC static ++ ++typedef enum network_encapsulation { ++ simple_net, simple_crc, ++} network_encapsulation_t; ++ ++typedef enum network_config_status { ++ config_unkown, ++ config_attached, ++ config_detached ++} network_config_status_t; ++ ++typedef enum network_hotplug_status { ++ hotplug_unkown, ++ hotplug_attached, ++ hotplug_detached ++} network_hotplug_status_t; ++ ++typedef enum network_type { ++ network_unknown, ++ network_blan, ++ network_safe, ++ network_cdc, ++ network_basic, ++ network_basic2, ++ network_eem, ++} network_type_t; ++ ++struct usb_network_params { ++ // enabling switchs ++ u32 cdc; ++ u32 basic; ++ u32 basic2; ++ u32 safe; ++ u32 blan; ++ // capability flags ++ u32 cdc_capable; ++ u32 basic_capable; ++ u32 basic2_capable; ++ u32 safe_capable; ++ u32 blan_capable; ++ // overrides ++ u32 vendor_id; ++ u32 product_id; ++ char *local_mac_address_str; ++ char *remote_mac_address_str; ++ // other switches ++ u32 ep0test; ++ u32 zeroconf; ++}; ++ ++#define CONFIG_OTG_NETWORK_ALLOW_SETID 1 ++ ++#define NETWORK_CREATED 0x01 ++#define NETWORK_REGISTERED 0x02 ++#define NETWORK_DESTROYING 0x04 ++#define NETWORK_ENABLED 0x08 ++#define NETWORK_ATTACHED 0x10 ++#define NETWORK_OPEN 0x20 ++ ++ ++#define MCCI_ENABLE_CRC 0x03 ++#define BELCARRA_GETMAC 0x04 ++ ++#define BELCARRA_SETTIME 0x04 ++#define BELCARRA_SETIP 0x05 ++#define BELCARRA_SETMSK 0x06 ++#define BELCARRA_SETROUTER 0x07 ++#define BELCARRA_SETDNS 0x08 ++#define BELCARRA_PING 0x09 ++#define BELCARRA_SETFERMAT 0x0a ++#define BELCARRA_HOSTNAME 0x0b ++ ++ ++// RFC868 - seconds from midnight on 1 Jan 1900 GMT to Midnight 1 Jan 1970 GMT ++#define RFC868_OFFSET_TO_EPOCH 0x83AA7E80 ++ ++struct usb_network_private { ++ ++ struct net_device_stats stats; /* network device statistics */ ++ ++ int flags; ++ struct net_device *net_dev; ++ struct usbd_function_instance *function; ++ struct usbd_function_driver *function_driver; ++ struct usb_network_params *params; ++ struct net_usb_services *fd_ops; ++ unsigned int maxtransfer; ++ rwlock_t rwlock; ++ ++ network_config_status_t config_status; ++ network_hotplug_status_t hotplug_status; ++ network_type_t network_type; ++ ++ int state; ++ ++ int mtu; ++ int crc; ++#if defined(CONFIG_OTG_NETWORK_BLAN_FERMAT) ++ int fermat; ++#endif ++ ++ 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; ++ ++ int have_interrupt; ++ ++ int data_notify; ++ ++ struct usbd_urb *int_urb; ++ ++ network_encapsulation_t encapsulation; ++ ++ struct WORK_ITEM notification_bh; ++#ifdef CONFIG_OTG_NETWORK_BLAN_AUTO_CONFIG ++ struct WORK_ITEM config_bh; ++#endif /* CONFIG_OTG_NETWORK_BLAN_AUTO_CONFIG */ ++#ifdef CONFIG_HOTPLUG ++ struct WORK_ITEM hotplug_bh; ++#endif /* CONFIG_HOTPLUG */ ++ __u8 local_dev_addr[ETH_ALEN]; ++ __u8 remote_dev_addr[ETH_ALEN]; ++}; ++ ++// XXX this needs to be co-ordinated with rndis.c maximum's ++#define MAXFRAMESIZE 2000 ++ ++#if !defined(CONFIG_OTG_MANUFACTURER) ++ #define CONFIG_OTG_MANUFACTURER "Belcarra" ++#endif ++ ++ ++#if !defined(CONFIG_OTG_SERIAL_NUMBER_STR) ++ #define CONFIG_OTG_SERIAL_NUMBER_STR "" ++#endif ++ ++/* ++ * 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_BASIC_NET 0x01 ++#define LINEO_SUBCLASS_BASIC_SERIAL 0x02 ++ ++/* ++ * Lineo Protocols ++ */ ++#define LINEO_BASIC_NET_CRC 0x01 ++#define LINEO_BASIC_NET_CRC_PADDED 0x02 ++ ++#define LINEO_BASIC_SERIAL_CRC 0x01 ++#define LINEO_BASIC_SERIAL_CRC_PADDED 0x02 ++ ++ ++/* ++ * endpoint and interface indexes ++ */ ++#define BULK_OUT 0x00 ++#define BULK_IN 0x01 ++#define INT_IN 0x02 ++#define ENDPOINTS 0x03 ++ ++#define COMM_INTF 0x00 ++#define DATA_INTF 0x01 ++ ++ ++/* bmDataCapabilities */ ++#define BMDATA_CRC 0x01 ++#define BMDATA_PADBEFORE 0x02 ++#define BMDATA_PADAFTER 0x04 ++#define BMDATA_FERMAT 0x08 ++#define BMDATA_HOSTNAME 0x10 ++ ++/* bmNetworkCapabilities */ ++#define BMNETWORK_SET_PACKET_OK 0x01 ++#define BMNETWORK_NONBRIDGED 0x02 ++#define BMNETWORK_DATA_NOTIFY_OK 0x04 ++ ++ ++/* ++ * BLAN Data Plane ++ */ ++//#define CONFIG_OTG_NETWORK_PADBYTES 8 ++//#define CONFIG_OTG_NETWORK_PADAFTER 1 ++//#undef CONFIG_OTG_NETWORK_PADBEFORE ++//#define CONFIG_OTG_NETWORK_CRC 1 ++ ++ ++extern __u8 network_requested_endpoints[ENDPOINTS+1]; ++extern __u16 network_requested_transferSizes[ENDPOINTS+1]; ++extern struct usb_network_private Usb_network_private; ++extern __u8 local_dev_addr[ETH_ALEN]; ++extern __u8 remote_dev_addr[ETH_ALEN]; ++ ++extern struct usbd_function_operations net_fd_function_ops; ++ ++struct usbd_class_safe_networking_mdlm_descriptor { ++ __u8 bFunctionLength; // 0x06 ++ __u8 bDescriptorType; // 0x24 ++ __u8 bDescriptorSubtype; // 0x13 ++ __u8 bGuidDescriptorType; // 0x00 ++ __u8 bmNetworkCapabilities; ++ __u8 bmDataCapabilities; ++} __attribute__ ((packed)); ++ ++struct usbd_class_blan_networking_mdlm_descriptor { ++ __u8 bFunctionLength; // 0x07 ++ __u8 bDescriptorType; // 0x24 ++ __u8 bDescriptorSubtype; // 0x13 ++ __u8 bGuidDescriptorType; // 0x01 ++ __u8 bmNetworkCapabilities; ++ __u8 bmDataCapabilities; ++ __u8 bPad; ++} __attribute__ ((packed)); ++ ++ ++ ++ ++#define NETWORK_CREATED 0x01 ++#define NETWORK_REGISTERED 0x02 ++#define NETWORK_DESTROYING 0x04 ++#define NETWORK_ENABLED 0x08 ++#define NETWORK_ATTACHED 0x10 ++#define NETWORK_OPEN 0x20 ++ ++ ++#define MCCI_ENABLE_CRC 0x03 ++#define BELCARRA_GETMAC 0x04 ++ ++#define BELCARRA_SETTIME 0x04 ++#define BELCARRA_SETIP 0x05 ++#define BELCARRA_SETMSK 0x06 ++#define BELCARRA_SETROUTER 0x07 ++#define BELCARRA_SETDNS 0x08 ++#define BELCARRA_PING 0x09 ++#define BELCARRA_SETFERMAT 0x0a ++#define BELCARRA_HOSTNAME 0x0b ++#define BELCARRA_DATA_NOTIFY 0x0c ++ ++#define RFC868_OFFSET_TO_EPOCH 0x83AA7E80 // RFC868 - seconds from midnight on 1 Jan 1900 GMT to Midnight 1 Jan 1970 GMT ++ ++ ++extern __u32 *network_crc32_table; ++ ++#define CRC32_INIT 0xffffffff // Initial FCS value ++#define CRC32_GOOD 0xdebb20e3 // Good final FCS value ++ ++#define CRC32_POLY 0xedb88320 // Polynomial for table generation ++ ++#define COMPUTE_FCS(val, c) (((val) >> 8) ^ network_crc32_table[((val) ^ (c)) & 0xff]) ++ ++ ++#if 0 ++#if !defined(CONFIG_OTG_NETWORK_CRC_DUFF4) && !defined(CONFIG_OTG_NETWORK_CRC_DUFF8) ++/** ++ * Copies a specified number of bytes, computing the 32-bit CRC FCS as it does so. ++ * ++ * dst Pointer to the destination memory area. ++ * src Pointer to the source memory area. ++ * len Number of bytes to copy. ++ * val Starting value for the CRC FCS. ++ * ++ * Returns Final value of the CRC FCS. ++ * ++ * @sa crc32_pad ++ */ ++static __u32 __inline__ crc32_copy (__u8 *dst, __u8 *src, int len, __u32 val) ++{ ++ for (; len-- > 0; val = COMPUTE_FCS (val, *dst++ = *src++)); ++ return val; ++} ++ ++#endif /* DUFFn */ ++#endif ++ ++ ++ ++ ++#endif /* NETWORK_FD_H */ +diff -uNr linux/drivers/no-otg/functions/network/safe.c linux/drivers/otg/functions/network/safe.c +--- linux/drivers/no-otg/functions/network/safe.c 1970-01-01 01:00:00.000000000 +0100 ++++ linux/drivers/otg/functions/network/safe.c 2006-09-01 21:41:30.000000000 +0200 +@@ -0,0 +1,362 @@ ++/* ++ * otg/functions/network/safe.c - Network Function Driver ++ * ++ * Copyright (c) 2002, 2003, 2004 Belcarra ++ * ++ * By: ++ * Thomas Rushworth <tbr @belcarra.com> ++ * Stuart Lynne <sl@belcarra.com> ++ * Bruce Balden <balden@belcarra.com> ++ * ++ * 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 SAFE network driver implements the SAFE protocol descriptors. ++ * ++ * The SAFE protocol is suitable for infrastructure devices that ++ * are implementing a bridged or routed connection betwen an ++ * external network and the USB Host. ++ * ++ * ++ */ ++/*! ++ * @file otg/functions/network/safe.c ++ * @brief This file implements the required descriptors to implement ++ * a SAFE network device with one interface. ++ * ++ * @ingroup NetworkFunction ++ */ ++ ++#include <otg/otg-compat.h> ++#include <otg/otg-module.h> ++ ++ ++#include <linux/slab.h> ++#include <linux/interrupt.h> ++#include <linux/utsname.h> ++#include <linux/netdevice.h> ++ ++#include <otg/usbp-chap9.h> ++#include <otg/usbp-func.h> ++ ++#include "network.h" ++ ++#define NTT network_fd_trace_tag ++ ++#ifdef CONFIG_OTG_NETWORK_SAFE ++/* USB SAFE Configuration ********************************************************** */ ++ ++/* ++ * SAFE Ethernet Configuration ++ */ ++ ++/* Communication Interface Class descriptors ++ */ ++ ++/*! BULK OUT data endpoint descriptor ++ */ ++static struct usbd_endpoint_descriptor safe_data_1 = { ++ bLength: 0x07, ++ bDescriptorType: USB_DT_ENDPOINT, ++ bEndpointAddress: USB_DIR_OUT, ++ bmAttributes: BULK, ++ wMaxPacketSize: __constant_cpu_to_le16(0), ++ bInterval: 0, ++}; ++/*! BULK IN data endpoint descriptor ++ */ ++static struct usbd_endpoint_descriptor safe_data_2 = ++{ ++ bLength:sizeof(struct usbd_endpoint_descriptor), ++ bDescriptorType:USB_DT_ENDPOINT, ++ bEndpointAddress:USB_DIR_IN, ++ bmAttributes: BULK, ++ wMaxPacketSize: __constant_cpu_to_le16(0), ++ bInterval: 0x0, ++}; ++ ++/*! INTERRUPT IN data endpoint descriptor ++ */ ++static struct usbd_endpoint_descriptor safe_comm_1 = ++{ ++ bLength:sizeof(struct usbd_endpoint_descriptor), ++ bDescriptorType:USB_DT_ENDPOINT, ++ bEndpointAddress:USB_DIR_IN, ++ bmAttributes: INTERRUPT, ++ wMaxPacketSize: __constant_cpu_to_le16(0), ++ bInterval: 0xa, ++}; ++ ++#if 0 ++static struct usbd_endpoint_descriptor *basic_default[] = { &basic_data_1, ++ &basic_data_2, ++ &basic_comm_1, }; ++u8 basic_indexes[] = { BULK_OUT, BULK_IN, INT_IN, }; ++#endif ++ ++#if 0 ++static u8 safe_class_1[] = { 0x05, CS_INTERFACE, USB_ST_HEADER, 0x10, 0x01, /* CLASS_BDC_VERSION, CLASS_BDC_VERSION */ }; ++static u8 safe_class_2[] = { 0x15, CS_INTERFACE, USB_ST_MDLM, 0x00, 0x01, /* bcdVersion, bcdVersion */ ++ ++ 0x5d, 0x34, 0xcf, 0x66, 0x11, 0x18, 0x11, 0xd6, /* bGUID */ ++ 0xa2, 0x1a, 0x00, 0x01, 0x02, 0xca, 0x9a, 0x7f, /* bGUID */ }; ++ ++static u8 safe_class_3[] = { 0x06, CS_INTERFACE, USB_ST_MDLMD, 0x00, 0x00, 0x00, /* bDetailData */ }; ++ ++static u8 safe_class_4[] = { 0x0d, CS_INTERFACE, USB_ST_ENF, ++ 0x00, 0x00, 0x00, 0x00, 0x00, 0xea, 0x05, /* 1514 maximum frame size */ ++ 0x00, 0x00, 0x00 , }; ++#else ++static struct usbd_class_header_function_descriptor safe_class_1 = { ++ bFunctionLength:0x05, ++ bDescriptorType: CS_INTERFACE, ++ bDescriptorSubtype: USB_ST_HEADER, ++ bcdCDC: __constant_cpu_to_le16(0x0110), //0x10, 0x01, ++}; ++ ++static struct usbd_class_mdlm_descriptor safe_class_2 = { ++ bFunctionLength: 0x15, ++ bDescriptorType: CS_INTERFACE, ++ bDescriptorSubtype: USB_ST_MDLM, ++ bcdVersion: __constant_cpu_to_le16( 0x0100), //0x00, 0x01, ++ bGUID: { ++ 0x5d, 0x34, 0xcf, 0x66, 0x11, 0x18, 0x11, 0xd6, /* bGUID */ ++ 0xa2, 0x1a, 0x00, 0x01, 0x02, 0xca, 0x9a, 0x7f, /* bGUID */ }, ++}; ++ ++static struct usbd_class_safe_descriptor safe_class_3 = { ++ bFunctionLength: 0x06, ++ bDescriptorType: CS_INTERFACE, ++ bDescriptorSubtype: USB_ST_MDLMD, ++ bGuidDescriptorType: 0x00, ++ bmNetworkCapabilities: 0x00, ++ bmDataCapabilities: 0x00, /* bDetailData */ ++}; ++ ++static struct usbd_class_ethernet_networking_descriptor safe_class_4 = { ++ bFunctionLength: 0x0d, ++ bDescriptorType: CS_INTERFACE, ++ bDescriptorSubtype: USB_ST_ENF, ++ iMACAddress: 0x0, ++ bmEthernetStatistics: __constant_cpu_to_le32(0x00), ++ wMaxSegmentSize: __constant_cpu_to_le16(0x5ea), ++ wNumberMCFilters: __constant_cpu_to_le16(0x00), ++ bNumberPowerFilters: 0, ++}; ++ ++#endif ++ ++/*! Endpoint descriptor list ++ */ ++static struct usbd_endpoint_descriptor *safe_alt_endpoints[] = { ++ &safe_data_1, &safe_data_2, &safe_comm_1, }; ++ ++static struct usbd_generic_class_descriptor *safe_comm_class_descriptors[] = { ++ (struct usbd_generic_class_descriptor *) &safe_class_1, ++ (struct usbd_generic_class_descriptor *) &safe_class_2, ++ (struct usbd_generic_class_descriptor *) &safe_class_3, ++ (struct usbd_generic_class_descriptor *) &safe_class_4, }; ++ ++u8 safe_alt_indexes[] = { BULK_OUT, BULK_IN, INT_IN, }; ++ ++/*! Data Interface Descriptor ++ */ ++static struct usbd_interface_descriptor safe_alternate_descriptor = { ++ bLength: 0x09, ++ bDescriptorType: USB_DT_INTERFACE, ++ bInterfaceNumber: 0x00, ++ bAlternateSetting: 0x00, ++ bNumEndpoints: sizeof(safe_alt_endpoints)/sizeof(struct usbd_endpoint_descriptor *), ++ bInterfaceClass: COMMUNICATIONS_INTERFACE_CLASS, ++ bInterfaceSubClass: COMMUNICATIONS_MDLM_SUBCLASS, ++ bInterfaceProtocol: COMMUNICATIONS_NO_PROTOCOL, ++ iInterface: 0x00, ++}; ++ ++/*! Data Alternate Interface Description List ++ */ ++static struct usbd_alternate_description safe_alternate_descriptions[] = { ++ { ++ iInterface: CONFIG_OTG_NETWORK_SAFE_INTF, ++ interface_descriptor: &safe_alternate_descriptor, ++ classes: sizeof (safe_comm_class_descriptors) / sizeof (struct usbd_generic_class_descriptor *), ++ class_list: safe_comm_class_descriptors, ++ endpoints:sizeof (safe_alt_endpoints) / sizeof(struct usbd_endpoint_descriptor *), ++ endpoint_list: safe_alt_endpoints, ++ endpoint_indexes: safe_alt_indexes, ++ }, ++}; ++ ++/* Interface descriptions and descriptors ++ */ ++/*! Interface Description List ++ */ ++static struct usbd_interface_description safe_interfaces[] = { ++ { ++ alternates:sizeof (safe_alternate_descriptions) / sizeof (struct usbd_alternate_description), ++ alternate_list:safe_alternate_descriptions, }, ++}; ++ ++ ++/* Configuration descriptions and descriptors ++ */ ++ ++/*! Configuration Descriptor ++ */ ++static struct usbd_configuration_descriptor safe_configuration_descriptor = { ++ bLength: 0x09, ++ bDescriptorType: USB_DT_CONFIGURATION, ++ wTotalLength: __constant_cpu_to_le16(0x0000), //0x00, 0x00, // wLength ++ bNumInterfaces:sizeof (safe_interfaces) / sizeof (struct usbd_interface_description), ++ bConfigurationValue:0x01, ++ iConfiguration: 0x00, // bConfigurationValue, iConfiguration ++ bmAttributes: 0, ++ bMaxPower:0, ++}; ++ ++/*! Configuration Description List ++ */ ++struct usbd_configuration_description safe_description[] = { ++ { ++ iConfiguration: CONFIG_OTG_NETWORK_SAFE_DESC, ++ configuration_descriptor: &safe_configuration_descriptor, ++ }, ++}; ++ ++/* Device Description ++ */ ++/*! Device Descriptor ++ */ ++static struct usbd_device_descriptor safe_device_descriptor = { ++ bLength: sizeof(struct usbd_device_descriptor), ++ bDescriptorType: USB_DT_DEVICE, ++ bcdUSB: __constant_cpu_to_le16(USB_BCD_VERSION), ++ bDeviceClass: COMMUNICATIONS_DEVICE_CLASS, ++ bDeviceSubClass: 0x02, ++ bDeviceProtocol: 0x00, ++ bMaxPacketSize0: 0x00, ++ idVendor: __constant_cpu_to_le16(CONFIG_OTG_NETWORK_VENDORID), ++ idProduct: __constant_cpu_to_le16(CONFIG_OTG_NETWORK_PRODUCTID), ++ bcdDevice: __constant_cpu_to_le16(CONFIG_OTG_NETWORK_BCDDEVICE), ++}; ++ ++#ifdef CONFIG_OTG_HIGH_SPEED ++/*! High Speed Device Qualifier Descriptor ++ */ ++static struct usbd_device_qualifier_descriptor safe_device_qualifier_descriptor = { ++ bLength: sizeof(struct usbd_device_qualifier_descriptor), ++ bDescriptorType: USB_DT_DEVICE_QUALIFIER, ++ bcdUSB: __constant_cpu_to_le16(USB_BCD_VERSION), ++ bDeviceClass: COMMUNICATIONS_DEVICE_CLASS, ++ bDeviceSubClass: 0x02, ++ bDeviceProtocol: 0x00, ++ bMaxPacketSize0: 0x00, ++}; ++#endif /* CONFIG_OTG_HIGH_SPEED */ ++ ++/*! Endpoint Request List ++ */ ++static struct usbd_endpoint_request safe_endpoint_requests[ENDPOINTS+1] = { ++ { 1, 0, 0, USB_DIR_OUT | USB_ENDPOINT_BULK, MAXFRAMESIZE + 48, MAXFRAMESIZE + 512, }, ++ { 1, 0, 0, USB_DIR_IN | USB_ENDPOINT_BULK, MAXFRAMESIZE + 48, MAXFRAMESIZE + 512, }, ++ { 1, 0, 0, USB_DIR_IN | USB_ENDPOINT_INTERRUPT | USB_ENDPOINT_OPT, 16, 64, }, ++ { 0, }, ++}; ++ ++/*! OTG Descriptor ++ */ ++static struct usbd_otg_descriptor safe_otg_descriptor = { ++ bLength : sizeof(struct usbd_otg_descriptor), ++ bDescriptorType: USB_DT_OTG, ++ bmAttributes: 0, ++}; ++ ++/*! Device Description ++ */ ++struct usbd_device_description safe_device_description = { ++ device_descriptor: &safe_device_descriptor, ++#ifdef CONFIG_OTG_HIGH_SPEED ++ device_qualifier_descriptor: &safe_device_qualifier_descriptor, ++#endif /* CONFIG_OTG_HIGH_SPEED */ ++ otg_descriptor: &safe_otg_descriptor, ++ iManufacturer: CONFIG_OTG_NETWORK_MANUFACTURER, ++ iProduct: CONFIG_OTG_NETWORK_PRODUCT_NAME, ++#if !defined(CONFIG_OTG_NO_SERIAL_NUMBER) && defined(CONFIG_OTG_SERIAL_NUMBER_STR) ++ iSerialNumber:CONFIG_OTG_SERIAL_NUMBER_STR, ++#endif ++}; ++ ++/*! safe_init ++ * @param function The function instance ++ */ ++void safe_init (struct usbd_function_instance *function) ++{ ++ struct usbd_class_ethernet_networking_descriptor *ethernet; ++ int len = 0; ++ char buf[255]; ++ ++ buf[0] = 0; ++ ++ safe_alternate_descriptions[0].endpoints = Usb_network_private.have_interrupt ? 3 : 2; ++ ++ // Update the iMACAddress field in the ethernet descriptor ++ { ++ char address_str[14]; ++ snprintf(address_str, 13, "%02x%02x%02x%02x%02x%02x", ++ remote_dev_addr[0], remote_dev_addr[1], remote_dev_addr[2], ++ remote_dev_addr[3], remote_dev_addr[4], remote_dev_addr[5]); ++ if ((ethernet = &safe_class_4)) { ++ if (ethernet->iMACAddress) { ++ usbd_free_string_descriptor(ethernet->iMACAddress); ++ } ++ ethernet->iMACAddress = usbd_alloc_string(address_str); ++ } ++ } ++ ++#ifdef CONFIG_OTG_NETWORK_SAFE_PADBEFORE ++ safe_class_3.bmNetworkCapabilities |= BMDATA_PADBEFORE; ++ len += sprintf(buf + len, "PADBEFORE: %02x ", safe_class_3.bmNetworkCapabilities); ++#endif ++#ifdef CONFIG_OTG_NETWORK_SAFE_CRC ++ safe_class_3.bmNetworkCapabilities |= BMDATA_CRC; ++ len += sprintf(buf + len, "CRC: %02x ", safe_class_3.bmNetworkCapabilities); ++#endif ++#ifdef CONFIG_OTG_NETWORK_SAFE_NONBRIDGED ++ safe_class_3.bmNetworkCapabilities |= BMNETWORK_NONBRIDGED; ++ len += sprintf(buf + len, "NONBRIDGE: %02x ",safe_class_3.bmDataCapabilities); ++#endif ++ if (strlen(buf)) ++ TRACE_MSG1(NTT,"%s", buf); ++ ++} ++ ++/*! function driver description ++ */ ++struct usbd_function_driver safe_function_driver = { ++ name: "network-SAFE", ++ fops: &net_fd_function_ops, ++ device_description: &safe_device_description, ++ bNumConfigurations: sizeof (safe_description) / sizeof (struct usbd_configuration_description), ++ configuration_description: safe_description, ++ idVendor: __constant_cpu_to_le16(CONFIG_OTG_NETWORK_VENDORID), ++ idProduct: __constant_cpu_to_le16(CONFIG_OTG_NETWORK_PRODUCTID), ++ bcdDevice: __constant_cpu_to_le16(CONFIG_OTG_NETWORK_BCDDEVICE), ++ bNumInterfaces:sizeof (safe_interfaces) / sizeof (struct usbd_interface_description), ++ interface_list:safe_interfaces, ++ endpointsRequested: ENDPOINTS, ++ requestedEndpoints: safe_endpoint_requests, ++}; ++#endif /* CONFIG_OTG_NETWORK_SAFE */ ++ +diff -uNr linux/drivers/no-otg/ocd/Config.in linux/drivers/otg/ocd/Config.in +--- linux/drivers/no-otg/ocd/Config.in 1970-01-01 01:00:00.000000000 +0100 ++++ linux/drivers/otg/ocd/Config.in 2006-09-01 21:41:31.000000000 +0200 +@@ -0,0 +1,15 @@ ++ ++# ++# Copyright (c) 2004 Belcarra ++# ++# ++ ++ mainmenu_option next_comment ++ comment 'OTG Host Controller Driver (HCD)' ++ dep_tristate ' USB Host Controller Driver' CONFIG_OTG_HCD $CONFIG_OTG_HOSTCORE ++ hex 'VendorID (hex value)' CONFIG_OTG_ROOTHUB_VENDORID "15ec" ++ hex 'ProductID (hex value)' CONFIG_OTG_ROOTHUB_PRODUCTID "f010" ++ hex 'ProductID (hex value)' CONFIG_OTG_ROOTHUB_BCDDEVICE "0100" ++ ++ ++ endmenu +diff -uNr linux/drivers/no-otg/ocd/Makefile linux/drivers/otg/ocd/Makefile +--- linux/drivers/no-otg/ocd/Makefile 1970-01-01 01:00:00.000000000 +0100 ++++ linux/drivers/otg/ocd/Makefile 2006-09-01 21:41:30.000000000 +0200 +@@ -0,0 +1,44 @@ ++# ++# Belcarra OTG - On-The-Go ++# ++# Copyright (c) 2004 Belcarra Technologies Corp ++ ++TOPDIR ?= ../../../.. ++ ++subdir-y := ++subdir-m := ++subdir-n := ++subdir- := ++ ++# The target object and module list name. ++ ++O_TARGET := ocd_target.o ++ ++subdir-$(CONFIG_OTG_AU1550) += au1x00 ++subdir-$(CONFIG_OTG_AU1X00) += au1x00 ++subdir-$(CONFIG_OTG_ISP1301_OMAP_H2) += isp1301 ++subdir-$(CONFIG_OTG_ISP1301_MAINSTONE) += isp1301 ++ ++subdir-$(CONFIG_OTG_MAX3353E_DB1550) += max3353e ++ ++# Object file lists. ++ ++obj-y := ++obj-m := ++obj-n := ++obj- := ++ ++# Bus Interface Drivers ++ocd-obj-$(CONFIG_OTG_AU1550) += au1x00/au1x00_pcd_drv.o ++ocd-obj-$(CONFIG_OTG_AU1X00) += au1x00/au1x00_pcd_drv.o ++ ++# Platform ++ocd-obj-$(CONFIG_OTG_ISP1301_OMAP_H2) += isp1301/isp1301_target.o ++ocd-obj-$(CONFIG_OTG_ISP1301_MAINSTONE) += isp1301/isp1301_target.o ++ ++ocd-obj-$(CONFIG_OTG_MAX3353E_DB1550) += max3353e/max3353e_target.o ++ ++obj-y += $(ocd-obj-y) ++ ++include $(TOPDIR)/Rules.make ++EXTRA_CFLAGS += -Wno-format -Wall +diff -uNr linux/drivers/no-otg/ocd/Makefile-l26 linux/drivers/otg/ocd/Makefile-l26 +--- linux/drivers/no-otg/ocd/Makefile-l26 1970-01-01 01:00:00.000000000 +0100 ++++ linux/drivers/otg/ocd/Makefile-l26 2006-09-01 21:41:30.000000000 +0200 +@@ -0,0 +1,29 @@ ++# ++# Belcarra OTG - On-The-Go ++# ++# Copyright (c) 2004 Belcarra Technologies Corp ++ ++EXTRA_CFLAGS += -Wno-format -Wall ++ ++# Bus Interface Drivers ++#subdir-$(CONFIG_OTG_AU1X00) += au1x00 ++#subdir-$(CONFIG_OTG_LH7A400) += lh7a400 ++#subdir-$(CONFIG_OTG_MX1) += mx1 ++#subdir-$(CONFIG_OTG_MX2) += mx2 ++#subdir-$(CONFIG_OTG_NC2280) += nc2280 ++#subdir-$(CONFIG_OTG_PXA) += pxa ++#subdir-$(CONFIG_OTG_SA1100) += sa1100 ++#subdir-$(CONFIG_OTG_SMDK2500) += smdk2500 ++#subdir-$(CONFIG_OTG_SUPERH) += superh ++#subdir-$(CONFIG_OTG_SX2) += sx2 ++#subdir-$(CONFIG_OTG_WMMX) += wmmx ++ ++obj-m += scma11-evb/ ++obj-m += omap/ ++obj-m += isp1301/ ++ ++obj-y += scma11-evb/ ++obj-y += omap/ ++obj-y += isp1301/ ++ ++ +diff -uNr linux/drivers/no-otg/ocd/README linux/drivers/otg/ocd/README +--- linux/drivers/no-otg/ocd/README 1970-01-01 01:00:00.000000000 +0100 ++++ linux/drivers/otg/ocd/README 2006-09-01 21:41:30.000000000 +0200 +@@ -0,0 +1,54 @@ ++otg/pcd ++ ++ ++Linux Build support: ++ ++ Makefile ++ Makefile-l24 ++ Makefile-l26 ++ ++ README ++ ++PCD support files: ++ ++ otg-pcd/pcd.c ++ otg-pcd/pcd-init-l24.c ++ otg-pcd/pcd-ocd-init-l24.c ++ otg-pcd/tr-init-l24.c ++ ++TCD support files: ++ ++ otg-tcd/tcd.c ++ otg-tcd/tcd-init-l24.c ++ ++HCD support files; ++ ++ otg-hcd/hcd-init-l24.c ++ otg-hcd/hcd-l26.c ++ otg-hcd/hcd-rh.c ++ otg-hcd/hcd.c ++ ++Architecture Specific Drivers: ++ ++ lh7a400 ++ mx1 ++ nc2280 ++ pxa ++ sa1100 ++ smdk2500 ++ superh ++ sx2 ++ ++Device Specific Drivers: ++ ++ atlas ++ isp1301 ++ max3353e ++ ++Platform Specific Drivers ++ ++ au1x00 ++ bvd ++ mx2 ++ omap ++ +diff -uNr linux/drivers/no-otg/ocd/au1x00/Makefile linux/drivers/otg/ocd/au1x00/Makefile +--- linux/drivers/no-otg/ocd/au1x00/Makefile 1970-01-01 01:00:00.000000000 +0100 ++++ linux/drivers/otg/ocd/au1x00/Makefile 2006-09-01 21:41:31.000000000 +0200 +@@ -0,0 +1,111 @@ ++# ++# Makefile for the kernel USBD (device not host) drivers. ++# ++# Copyright (c) 2002 Belcarra ++ ++# 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. ++ ++O_TARGET := au1x00_pcd_drv.o ++ ++# Objects that export symbols. ++ ++export-objs := ++ ++# Multipart objects. ++ ++#au1x00_otg-objs := au1x00.o usbd-bi.o trace.o ++db1550_tr-objs := tr-init-l24.o au1550.o pcd.o ++db1550_dr-objs := tr-init-l24.o au1550.o pcd.o ++#au1550_tr-objs := tr-init-l24.o au1550.o pcd.o ++au1x00_tr-objs := tr-init-l24.o au1x00.o pcd.o ++ ++# Optional parts of multipart objects. ++ ++# Object file lists. ++ ++#obj-y := usbd-bi.o ++obj-y := ++obj-m := ++obj-n := ++obj- := ++ ++# Each configuration option enables a list of files. ++ ++obj-$(CONFIG_OTG_AU1550_DB1500_TR) += db1550_tr.o ++obj-$(CONFIG_OTG_AU1550_DB1550_TR) += db1550_tr.o ++obj-$(CONFIG_OTG_AU1550_DB1500_DR) += db1550_dr.o ++ ++#obj-$(CONFIG_OTG_AU1550) += au1550_tr.o ++obj-$(CONFIG_OTG_AU1X00) += au1x00_tr.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 ++ ++OTG=$(TOPDIR)/drivers/otg ++OCD_DIR=$(OTG)/ocd ++INCLUDE_DIRS = -I$(OTG) ++EXTRA_CFLAGS += -Wno-missing-prototypes -Wno-unused -Wno-format ${INCLUDE_DIRS} ++EXTRA_CFLAGS_nostdinc += -Wno-missing-prototypes -Wno-unused -Wno-format ${INCLUDE_DIRS} ++ ++I2C=$(OTG)/ocd/otg-i2c ++PCD=$(OTG)/ocd/otg-pcd ++vpath %.c $(USBDCORE_DIR) $(OCD_DIR) $(PCD) $(I2C) ++ ++ ++# Link rules for multi-part drivers. ++ ++db1550_tr.o: $(db1550_tr-objs) ++ $(LD) -r -o $@ $(db1550_tr-objs) ++ ++db1550_dr.o: $(db1550_tr-objs) ++ $(LD) -r -o $@ $(au1550_tr-objs) ++ ++#au1550_tr.o: $(au1550_tr-objs) ++# $(LD) -r -o $@ $(au1550_tr-objs) ++ ++au1x00_tr.o: $(au1x00_tr-objs) ++ $(LD) -r -o $@ $(au1x00_tr-objs) ++ ++# dependencies: ++ ++# local ++ ++ +diff -uNr linux/drivers/no-otg/ocd/au1x00/NOTES.txt linux/drivers/otg/ocd/au1x00/NOTES.txt +--- linux/drivers/no-otg/ocd/au1x00/NOTES.txt 1970-01-01 01:00:00.000000000 +0100 ++++ linux/drivers/otg/ocd/au1x00/NOTES.txt 2006-09-01 21:41:31.000000000 +0200 +@@ -0,0 +1,62 @@ ++ ++Notes ++ ++1. EP0 - packetsize of 8 does not work, this means that PIO cannot be used for IN (transmit). ++DMA must be used. Only 16, 32 and 64 are usable. ++ ++Update: even though we couldn't program the endpoint size to 8, we just set it to 8 in the ++device descriptor and things work ok. This works and we don't need or use DMA for either ++direction for EP0. ++ ++2. For each BULK transfer the first N packets must be sent with the full packetsize and the ++last must be sent as a short packet. The packetsize must be set before enabling the DMA. ++ ++3. For IN endpoints an interrupt will be generated for EVERY IN packet including ones that ++will be NAK'd. To reduce overhead the interrupt should only be enabled when there is data ++being sent and we need to know when it has been sent. ++ ++Update: for new silicon this is not true, the UDC will interrupt less often and will NAK ++until the interrupt is cleared. ++ ++4. The AU1x00 auto-acks many setup commands and is very sensitive to latency of the irq ++handler processing the interrupt and clearing the ep0 fifo. Like the pxa we need to spool the ++requests and process later when we receive a request for data. There is a special test in ++the bulk data receive handler to check if there are spooled requests to process. ++ ++Update: this has been mostly mitigated by reducing all upper layer processing in recv_setup, ++event_irq etc so that they complete in less than a milli-second. This simplifies things and ++we can now pass UUT tests. ++ ++5. There is a time sensitive problem in au_in_epn(). Without a udelay(8) sending large ++packets will eventually stall interrupts. ++ ++Update: this is not required for new silicon. ++ ++6. When receiving data (Bulk OUT) the UDC will not start NAKing until and unless the receive ++FIFO is full. This means that there is no reliable way to determine the end of one packet and ++the beginning of the next if there is any undue latency in handling the interrupt for the ++first packet. ++ ++The Belcarra Windows and Macintosh network class drivers have an option that can be used ++to pad all outgoing packets to a multiple of 8 bytes. This helps eliminate this problem. ++ ++Update: this is not a problem with new silicon, it NAK's until the interrupt is cleared. ++ ++8. The original AU1x00 UDC generated an interrupt for EVERY IN packet. If we do not have ++any data we need to disable the interrupt for the endpoint to eliminate the overhead ++for processing them. The interrupt must be re-enabled when DMA is finished and we ++actually want to know that the endpoint has finished. ++ ++Leaving the interrupt enabled also interfers with the DMA process. It is not apparant ++why. ++ ++Update: This has been fixed on the new silicon but it doesn't hurt to continue to do this. ++ ++9. The DMA functions replace the equivalent routines in au1100_dma.h except that they pass ++the actual struct dma_chan pointer instead of the channel number. The bounds checking and ++table lookup to derive this information amounts to a substantial increase in code size and ++latency which can simply be avoided by using the structure address directly and/or doing the ++equivalent i/o directly. ++ ++10. The Au1x00 does not support REMOTE WAKEUP ++ +diff -uNr linux/drivers/no-otg/ocd/au1x00/au1550.c linux/drivers/otg/ocd/au1x00/au1550.c +--- linux/drivers/no-otg/ocd/au1x00/au1550.c 1970-01-01 01:00:00.000000000 +0100 ++++ linux/drivers/otg/ocd/au1x00/au1550.c 2006-09-01 21:41:31.000000000 +0200 +@@ -0,0 +1,1451 @@ ++/* ++ * otg/ocd/au1x00/au1x00.c -- USB Device Controller driver. ++ * ++ * Copyright (c) 2004 Belcarra ++ * ++ * By: ++ * Stuart Lynne <sl@belcarra.com>, ++ * Tom Rushworth <tbr@belcarra.com>, ++ * ++ */ ++/*! ++ * @file otg/ocd/au1x00/au1550.c ++ * @brief AU1X00 Header ++ * ++ * This file contains the private defines and structures for the AU1X00 ++ * Drivers. ++ * ++ * @ingroup AU1X00 ++ */ ++ ++ ++#include <otg/pcd-include.h> ++#include "au1x00.h" ++ ++#include <asm/io.h> ++#include <asm/au1000.h> ++#include <asm/au1000_dma.h> ++ ++#if defined(CONFIG_SOC_AU1550) ++#include <asm/au1xxx_dbdma.h> ++#else ++#error "Wrong AU1X00_MDA" ++#endif ++ ++#include <asm/mipsregs.h> ++ ++#ifdef CONFIG_MIPS_FREEHAND ++#include <linux/i2c.h> ++#include <linux/sensors.h> /* for reading serial number */ ++#endif ++ ++ ++#undef CHECK_LATENCY ++#undef RECORD_LATENCY ++#undef MAX_INTR_LOOP_STATS //10 ++#if defined(MAX_INTR_LOOP_STATS) ++static u32 interrupt_loop_stats[MAX_INTR_LOOP_STATS+1]; ++#endif ++#ifdef RECORD_LATENCY ++#define CP0_COUNTS 50 ++u32 cp0_counts[CP0_COUNTS]; ++u32 cp0_record; ++#endif ++u32 cp0_count; ++unsigned int udc_saw_bus_activity; ++int au_halt_dma_expired; ++u32 au1x00_inten; ++ ++u8 au1x00_config_bulk[25] = { ++ 0x04, (USB_ENDPOINT_CONTROL << 4) | (EP0_PACKETSIZE & 0x380) >> 7, // 1 ++ (EP0_PACKETSIZE & 0x7F) << 1, 0x00, 0x00, ++ 0x24, (USB_ENDPOINT_BULK << 4) | USB_DIR_IN | (MAX_EPN_PACKET_SIZE & 0x380) >> 7, // 6 ++ (MAX_EPN_PACKET_SIZE & 0x7F) << 1, 0x00, 0x02, ++ 0x34, (USB_ENDPOINT_BULK << 4) | USB_DIR_IN | (MAX_EPN_PACKET_SIZE & 0x380) >> 7, // 11 ++ (MAX_EPN_PACKET_SIZE & 0x7F) << 1, 0x00, 0x03, ++ 0x44, (USB_ENDPOINT_BULK << 4) | USB_DIR_OUT | (MAX_EPN_PACKET_SIZE & 0x380) >> 7, // 16 ++ (MAX_EPN_PACKET_SIZE & 0x7F) << 1, 0x00, 0x04, ++ 0x54, (USB_ENDPOINT_BULK << 4) | USB_DIR_OUT | (MAX_EPN_PACKET_SIZE & 0x380) >> 7, // 21 ++ (MAX_EPN_PACKET_SIZE & 0x7F) << 1, 0x00, 0x05, ++}; ++ ++/* ********************************************************************************************* */ ++/* Note - The endpoints names are confusing. To simplify mapping of the interrupt request lines ++ * we use a numbering of the physical endpoints of 0-5, which also matches the fifo numbering ++ * (see the config block). ++ * ++ * Physical FIFO Name Direction Logical ++ * 0 0 EP0 OUT 0 ++ * 1 1 EP0 IN 0 ++ * 2 2 EP1 IN 81 ++ * 3 3 EP2 IN 82 ++ * 4 4 EP3 OUT 3 ++ * 5 5 EP4 OUT 4 ++ * ++ * The ep_regs array maps a physical endpoint number to the registers required to access the udc ++ * for that endpoint. Note that ep0 is always accessed via 0. The epl2p array maps the logical ++ * addresses back to the physical number. The epp2l array maps the physical number to the ++ * logical endpoint address ++ */ ++struct ep_regs ep_regs[6] = { ++ ++ { rd: USBD_EP0RD, rds: USBD_EP0RDSTAT, /*rx_id: DMA_ID_USBDEV_EP0_RX,*/ cs: USBD_EP0CS, rx_str: "EP0 OUT RD", ++ wr: USBD_EP0WR, wrs: USBD_EP0WRSTAT, tx_id: DSCR_CMD0_USBDEV_TX0, tx_str: "EP0 IN WR",}, ++ ++ { rds: 0, wrs: 0, indma: -1, outdma: -1, }, // ep0 ++ { wr: NUSBD_EP1WR, wrs: NUSBD_EP1WRSTAT, tx_id: DSCR_CMD0_USBDEV_TX1, cs: NUSBD_EP1CS, tx_str: "EP1 IN WR",}, ++ { wr: NUSBD_EP2WR, wrs: NUSBD_EP2WRSTAT, tx_id: DSCR_CMD0_USBDEV_TX2, cs: NUSBD_EP2CS, tx_str: "EP2 IN WR",}, ++ ++ { rd: NUSBD_EP3RD, rds: NUSBD_EP3RDSTAT, rx_id: DSCR_CMD0_USBDEV_RX3, cs: NUSBD_EP3CS, rx_str: "EP3 OUT RD",}, ++ { rd: NUSBD_EP4RD, rds: NUSBD_EP4RDSTAT, rx_id: DSCR_CMD0_USBDEV_RX4, cs: NUSBD_EP4CS, rx_str: "EP4 OUT RD",}, ++}; ++u8 epl2p[6] = { 0x00, 0x02, 0x03, 0x04, 0x05, 0x00, }; // map logical to physical ++u8 epp2l[6] = { 0x00, 0x00, 0x81, 0x82, 0x03, 0x04, }; // map physical to logical ++ ++static __inline__ void au_fifo_read(struct ep_regs *ep, unsigned char * cp, int bytes) ++{ ++ u32 rd = ep->rd; ++ for (; bytes--; *cp++ = au_readl(rd)); ++} ++ ++static __inline__ void au_fifo_write(int ep, unsigned char * cp, int bytes) ++{ ++ u32 wr = ep_regs[ep].wr; ++ for (; bytes--; au_writel(*cp++, wr)); ++ ep_regs[ep].last = bytes; ++} ++ ++void __inline__ au_inten(u32 inten, char *msg) ++{ ++ TRACE_MSG2(PCD, "%s %08x", msg, inten); ++ au1x00_inten = inten; ++ au_writel(au1x00_inten, USBD_INTEN); ++} ++ ++void __inline__ au_epn_interrupt_enable(int epn) ++{ ++ au_inten(au1x00_inten | (1 << epn), "epn enable"); ++} ++ ++void __inline__ au_epn_interrupt_disable(int epn) ++{ ++ au_inten(au1x00_inten & ~(1 << epn), "epn disable"); ++ au_writel((1 << epn) , USBD_INTSTAT); ++} ++ ++static void __inline__ send_zlp(unsigned char epn) ++{ ++ au_writel(USBDEV_FSTAT_FLUSH | USBDEV_FSTAT_UF | USBDEV_FSTAT_OF, ep_regs[epn].wrs); ++ au_writel(0 << 1, ep_regs[epn].cs); ++ au_writel(0, ep_regs[epn].wr); ++} ++/* ********************************************************************************************* */ ++#define AU_DMA_HALT_POLL 0x1000 ++ ++#define DDMA_DESCCMD_ARB (1<<15) ++#define DDMA_DESCCMD_DW (16) ++#define DDMA_DESCCMD_DW_N(n) ((n&3)<<DDMA_DESCCMD_DW) ++ #define DDMA_DESCCMD_DW_BYTE DDMA_DESCCMD_DW_N(0) ++ #define DDMA_DESCCMD_DW_HWORD DDMA_DESCCMD_DW_N(1) ++ #define DDMA_DESCCMD_DW_WORD DDMA_DESCCMD_DW_N(2) ++#define DDMA_DESCCMD_SW (18) ++#define DDMA_DESCCMD_SW_N(n) ((n&3)<<DDMA_DESCCMD_SW) ++ #define DDMA_DESCCMD_SW_BYTE DDMA_DESCCMD_SW_N(0) ++ #define DDMA_DESCCMD_SW_HWORD DDMA_DESCCMD_SW_N(1) ++ #define DDMA_DESCCMD_SW_WORD DDMA_DESCCMD_SW_N(2) ++ ++#define DDMA_DESCCMD_DID (20) ++#define DDMA_DESCCMD_DID_N(n) ((n&0x1F)<<DDMA_DESCCMD_DID) ++#define DDMA_DESCCMD_SID (25) ++#define DDMA_DESCCMD_SID_N(n) ((n&0x1F)<<DDMA_DESCCMD_SID) ++ ++/* ++ * Bit definitions for source stride/block 1 dimensional ++ */ ++#define DDMA_DESCSRC_STRIDE_SS (0) ++#define DDMA_DESCSRC_STRIDE_SS_N(n) ((n&0x3fff)<<DDMA_DESCSRC_STRIDE_SS) ++#define DDMA_DESCSRC_STRIDE_SB (14) ++#define DDMA_DESCSRC_STRIDE_SB_N(n) ((n&0x3Fff)<<DDMA_DESCSRC_STRIDE_SB) ++#define DDMA_DESCSRC_STRIDE_SAM (28) ++#define DDMA_DESCSRC_STRIDE_SAM_N(n) ((n&3)<<DDMA_DESCSRC_STRIDE_SAM) ++ #define DDMA_DESCSRC_STRIDE_SAM_INC DDMA_DESCSRC_STRIDE_SAM_N(0) ++ #define DDMA_DESCSRC_STRIDE_SAM_DEC DDMA_DESCSRC_STRIDE_SAM_N(1) ++ #define DDMA_DESCSRC_STRIDE_SAM_STATIC DDMA_DESCSRC_STRIDE_SAM_N(2) ++ #define DDMA_DESCSRC_STRIDE_SAM_BURST DDMA_DESCSRC_STRIDE_SAM_N(3) ++#define DDMA_DESCSRC_STRIDE_STS (30) ++#define DDMA_DESCSRC_STRIDE_STS_N(n) ((n&3)<<DDMA_DESCSRC_STRIDE_STS) ++ #define DDMA_DESCSRC_STRIDE_STS_1 DDMA_DESCSRC_STRIDE_STS_N(0) ++ #define DDMA_DESCSRC_STRIDE_STS_2 DDMA_DESCSRC_STRIDE_STS_N(1) ++ #define DDMA_DESCSRC_STRIDE_STS_4 DDMA_DESCSRC_STRIDE_STS_N(2) ++ #define DDMA_DESCSRC_STRIDE_STS_8 DDMA_DESCSRC_STRIDE_STS_N(3) ++ ++ ++/* ++ * Bit definitions for dest stride/block 1 dimensional ++ */ ++#define DDMA_DESCDST_STRIDE_DS (0) ++#define DDMA_DESCDST_STRIDE_DS_N(n) ((n&0x3fff)<<DDMA_DESCDST_STRIDE_DS) ++#define DDMA_DESCDST_STRIDE_DB (14) ++#define DDMA_DESCDST_STRIDE_DB_N(n) ((n&0x3Fff)<<DDMA_DESCDST_STRIDE_DB) ++#define DDMA_DESCDST_STRIDE_DAM (28) ++#define DDMA_DESCDST_STRIDE_DAM_N(n) ((n&3)<<DDMA_DESCDST_STRIDE_DAM) ++ #define DDMA_DESCDST_STRIDE_DAM_INC DDMA_DESCDST_STRIDE_DAM_N(0) ++ #define DDMA_DESCDST_STRIDE_DAM_DEC DDMA_DESCDST_STRIDE_DAM_N(1) #define DDMA_DESCDST_STRIDE_DAM_STATIC DDMA_DESCDST_STRIDE_DAM_N(2) ++ #define DDMA_DESCDST_STRIDE_DAM_BURST DDMA_DESCDST_STRIDE_DAM_N(3) ++#define DDMA_DESCDST_STRIDE_DTS (30) ++#define DDMA_DESCDST_STRIDE_DTS_N(n) ((n&3)<<DDMA_DESCDST_STRIDE_DTS) ++ #define DDMA_DESCDST_STRIDE_DTS_1 DDMA_DESCDST_STRIDE_DTS_N(0) ++ #define DDMA_DESCDST_STRIDE_DTS_2 DDMA_DESCDST_STRIDE_DTS_N(1) ++ #define DDMA_DESCDST_STRIDE_DTS_4 DDMA_DESCDST_STRIDE_DTS_N(2) ++ #define DDMA_DESCDST_STRIDE_DTS_8 DDMA_DESCDST_STRIDE_DTS_N(3) ++ ++#ifndef CONFIG_MIPS_MTX2 ++typedef struct dbdma_device_table { ++ u32 dev_flags; ++ u32 dev_tsize; ++ u32 dev_devwidth; ++ u32 dev_physaddr; /* If FIFO */ ++ u32 dev_intlevel; ++ u32 dev_intpolarity; ++} dbdev_tab_t; ++ ++typedef struct dbdma_chan_config { ++ u32 chan_flags; ++ u32 chan_index; ++ dbdev_tab_t *chan_src; ++ dbdev_tab_t *chan_dest; ++ au1x_dma_chan_t *chan_ptr; ++ au1x_ddma_desc_t *chan_desc_base; ++ au1x_ddma_desc_t *get_ptr, *put_ptr, *cur_ptr; ++ void *chan_callparam; ++ void (*chan_callback)(int, void *, struct pt_regs *); ++} chan_tab_t; ++#endif ++static void __inline__ au_halt_dma(u32 chan) ++{ ++ chan_tab_t *ctp = *((chan_tab_t **)chan); ++ volatile au1x_dma_chan_t *cp = ctp->chan_ptr;; ++ au1x_ddma_desc_t *dp; ++ ++ au1xxx_dbdma_stop(chan); ++ au_halt_dma_expired++; ++ ++ dp = ctp->cur_ptr; // ensure that descriptor is stopped ++ dp->dscr_cmd0 &= ~DSCR_CMD0_V; ++ ++} ++ ++static void __inline__ au_start_dma_in(int indma, u32 tx_id, __u8 *bp, int len) ++{ ++ chan_tab_t *ctp = *((chan_tab_t **)indma); ++ au1x_ddma_desc_t *dp = ctp->cur_ptr; ++ volatile au1x_dma_chan_t *cp = ctp->chan_ptr;; ++ ++ TRACE_MSG8(PCD, "%08x %08x cmd: %08x %04x src: %08x %08x dst: %08x %08x MEM2USB", ++ indma, dp, dp->dscr_cmd0, dp->dscr_cmd1, ++ dp->dscr_source0, dp->dscr_source1, dp->dscr_dest0, dp->dscr_dest1); ++ ++ dp->dscr_source0 = virt_to_phys(bp); ++ dp->dscr_source1 = DDMA_DESCSRC_STRIDE_STS_4 | DDMA_DESCSRC_STRIDE_SAM_INC; ++ ++ dp->dscr_cmd1 = len; ++ dp->dscr_cmd0 = ++ DSCR_CMD0_SID(DSCR_CMD0_ALWAYS) | ++ DSCR_CMD0_DID(indma) ++ ; ++ TRACE_MSG1(PCD, "cmd0: %08x SID/DID", dp->dscr_cmd0); ++ ++ dp->dscr_cmd0 |= ++ DDMA_DESCCMD_SW_BYTE | ++ DDMA_DESCCMD_DW_BYTE ++ ; ++ TRACE_MSG1(PCD, "cmd0: %08x SW/DW", dp->dscr_cmd0); ++ ++ dp->dscr_cmd0 = DSCR_CMD0_V | ++ DSCR_CMD0_SID(DSCR_CMD0_ALWAYS) | ++ DSCR_CMD0_DID(tx_id) | ++ DDMA_DESCCMD_SW_BYTE | ++ DDMA_DESCCMD_DW_BYTE | ++ DSCR_CMD0_ARB | ++ DSCR_CMD0_DN | ++ DSCR_CMD0_CV ++ ; ++ ++ dp->dscr_dest1 = DDMA_DESCDST_STRIDE_DTS_4 | DDMA_DESCSRC_STRIDE_SAM_STATIC; ++ ++ ctp->cur_ptr = dp; ++ cp->ddma_desptr = virt_to_phys(ctp->cur_ptr); ++ cp->ddma_cfg |= DDMA_CFG_EN; /* Enable channel */ ++ au_sync(); ++ cp->ddma_dbell = 0xffffffff; /* Make it go */ ++ au_sync(); ++ ++ TRACE_MSG8(PCD, "%08x %08x cmd: %08x %04x src: %08x %08x dst: %08x %08x MEM2USB start", ++ indma, dp, dp->dscr_cmd0, dp->dscr_cmd1, ++ dp->dscr_source0, dp->dscr_source1, dp->dscr_dest0, dp->dscr_dest1); ++} ++ ++static void __inline__ au_start_dma_out(unsigned int outdma, u32 rx_id, __u8 *bp, int wMaxPacketSize) ++{ ++ chan_tab_t *ctp = *((chan_tab_t **)outdma); ++ au1x_ddma_desc_t *dp = ctp->cur_ptr; ++ volatile au1x_dma_chan_t *cp = ctp->chan_ptr;; ++ ++ ++ dma_cache_inv((unsigned long) bp, wMaxPacketSize); ++ ++ TRACE_MSG8(PCD, "%08x %08x cmd: %08x %04x src: %08x %08x dst: %08x %08x USB2MEM", ++ outdma, dp, dp->dscr_cmd0, dp->dscr_cmd1, ++ dp->dscr_source0, dp->dscr_source1, dp->dscr_dest0, dp->dscr_dest1); ++ ++ // if (!au1xxx_dbdma_put_dest(outdma, bp, wMaxPacketSize)) ++ ++ dp->dscr_dest0 = virt_to_phys(bp); ++ dp->dscr_dest1 = DDMA_DESCDST_STRIDE_DTS_4 | DDMA_DESCDST_STRIDE_DAM_INC; ++ ++ dp->dscr_cmd1 = wMaxPacketSize; ++ dp->dscr_cmd0 = DSCR_CMD0_V | ++ DSCR_CMD0_SID(rx_id) | ++ DSCR_CMD0_DID(DSCR_CMD0_ALWAYS) | ++ DDMA_DESCCMD_SW_BYTE | ++ DDMA_DESCCMD_DW_BYTE | ++ DSCR_CMD0_ARB | ++ DSCR_CMD0_SN | ++ DSCR_CMD0_CV ++ ; ++ ++ dp->dscr_source1 = DDMA_DESCSRC_STRIDE_STS_4 | DDMA_DESCSRC_STRIDE_SAM_STATIC; ++ ++ ctp->cur_ptr = dp; ++ cp->ddma_desptr = virt_to_phys(ctp->cur_ptr); ++ cp->ddma_cfg |= DDMA_CFG_EN; /* Enable channel */ ++ au_sync(); ++ cp->ddma_dbell = 0xffffffff; /* Make it go */ ++ au_sync(); ++ ++ TRACE_MSG8(PCD, "%08x %08x cmd: %08x %04x src: %08x %08x dst: %08x %08x USB2MEM start", ++ outdma, dp, dp->dscr_cmd0, dp->dscr_cmd1, ++ dp->dscr_source0, dp->dscr_source1, dp->dscr_dest0, dp->dscr_dest1); ++} ++ ++/* ********************************************************************************************* */ ++static struct usbd_urb *au_rcv_complete_ep0_irq(struct usbd_endpoint_instance *endpoint, int len, int urb_bad) ++{ ++ struct usbd_urb *rcv_urb = endpoint->rcv_urb; ++ if (rcv_urb && !urb_bad) { ++ int i; ++ u8 *cp = rcv_urb->buffer + rcv_urb->actual_length; ++ for (i = 0 ; i < len; i+= 8) ++ TRACE_MSG8(PCD, "ARCV %02x %02x %02x %02x %02x %02x %02x %02x", ++ cp[i + 0], cp[i + 1], cp[i + 2], cp[i + 3], ++ cp[i + 4], cp[i + 5], cp[i + 6], cp[i + 7] ++ ); ++ ++ } ++ return pcd_rcv_complete_irq(endpoint, len, urb_bad); ++} ++ ++/* au_start_in_ep0 - start transmit ++ */ ++static void au_start_in_ep0 (struct usbd_endpoint_instance *endpoint, struct ep_regs *ep) ++{ ++ struct usbd_urb *urb = endpoint->tx_urb; ++ int last = ep->last = endpoint->last = MIN (urb->actual_length - endpoint->sent, endpoint->wMaxPacketSize); ++ TRACE_MSG2(PCD, "START IN EP0 SENT: %d SENDING: %d", endpoint->sent, last); ++ RETURN_IF ((urb->actual_length - endpoint->sent) <= 0); ++ au_writel(last << 1, ep->cs); // XXX ++ au_fifo_write(0, urb->buffer + endpoint->sent, last); ++ endpoint->last = last; ++} ++ ++/* au_in_ep0 - called to service an endpoint zero IN interrupt, data sent ++ */ ++static void au_in_ep0(struct usbd_endpoint_instance *endpoint, struct ep_regs *ep) ++{ ++ u32 cs; ++ struct usbd_urb *tx_urb; ++ int last; ++ TRACE_MSG1(PCD, "EP0 IN: tx_urb: %p", (int)endpoint->tx_urb); ++ if ((cs = au_readl(ep->cs)) & USBDEV_CS_STALL) { // clear stall if present ++ TRACE_MSG1(PCD, "CLEAR STALL %d", 0); ++ cs &= ~USBDEV_CS_STALL; ++ au_writel(cs, ep->cs); ++ return; ++ } ++ if (!(tx_urb = pcd_tx_complete_irq(endpoint, 0))) { // wait for setup if no more data ++ endpoint->state = WAIT_FOR_SETUP; ++ return; ++ } ++ TRACE_MSG4(PCD, "EP0 IN actual: %d last: %d sent: %d flags: %x", endpoint->tx_urb->actual_length, ++ endpoint->last, endpoint->sent, endpoint->tx_urb->flags); ++ if (pcd_tx_sendzlp(endpoint)) { // check if tx_urb we have is finished ++ TRACE_MSG0(PCD, "EP0 IN BULK - sending ZLP"); ++ tx_urb->flags &= ~USBD_URB_SENDZLP; ++ send_zlp(0); ++ pcd_tx_complete_irq(endpoint, 0); ++ return; ++ } ++ if (tx_urb->actual_length > endpoint->sent) { ++ if ((tx_urb->actual_length - endpoint->sent) < endpoint->wMaxPacketSize) ++ TRACE_MSG1(PCD, "EP0 IN starting short packet %d", tx_urb->actual_length - endpoint->sent); ++ else ++ TRACE_MSG1(PCD, "EP0 IN LEFT TO SEND %d", tx_urb->actual_length - endpoint->sent); ++ au_start_in_ep0(endpoint, ep); ++ } ++} ++ ++/* au_out_ep0 - called to service an endpoint zero OUT interrupt, data received ++ */ ++static void au_out_ep0(struct usbd_endpoint_instance *endpoint, struct ep_regs *ep) ++{ ++ struct usbd_device_request request; ++ int i; ++ u32 cs; ++ u32 bytes; ++ TRACE_MSG0(PCD, "EP0 OUT"); ++ cs = au_readl(ep->cs); // check if host aborted transfer and flush the write fifo ++ bytes = au_readl(ep->rds) & USBDEV_FSTAT_FCNT_MASK; ++ if (endpoint->state == DATA_STATE_RECV) { ++ struct usbd_urb *rcv_urb = pcd_rcv_next_irq(endpoint); ++ TRACE_MSG1(PCD, "EP0 OUT: RECV: rcv_urb: %x", (int) rcv_urb); ++ if (rcv_urb) { ++ au_fifo_read(ep, rcv_urb->buffer + rcv_urb->actual_length, bytes); ++ if (au_rcv_complete_ep0_irq(endpoint, bytes, 0)) ++ return; ++ au_writel(USBDEV_FSTAT_FLUSH | USBDEV_FSTAT_UF | USBDEV_FSTAT_OF, ep->rds); ++ endpoint->state = WAIT_FOR_SETUP; ++ send_zlp(0); ++ return; ++ } ++ endpoint->state = WAIT_FOR_SETUP; ++ } ++ pcd_tx_cancelled_irq(endpoint); ++ pcd_rcv_cancelled_irq(endpoint); ++ au_fifo_read(ep, (u8 *)&request, bytes); ++ au_writel(USBDEV_FSTAT_FLUSH | USBDEV_FSTAT_UF | USBDEV_FSTAT_OF, ep->wrs); ++ if (bytes != 8) { ++ TRACE_MSG1(PCD, "ERROR SETUP SET not eight bytes: %d", bytes); ++ au_writel(USBDEV_FSTAT_FLUSH | USBDEV_FSTAT_UF | USBDEV_FSTAT_OF, ep->wrs); ++ return; ++ } ++ TRACE_MSG4(PCD, "SETUP bmRequestType: %02x bRequest %02x state: %d status: %d", request.bmRequestType, ++ request.bRequest, pcd_instance->bus->device_state, pcd_instance->bus->status); ++ switch (request.bRequest) { // we need to simply ignore any of these ++ case USB_REQ_SET_ADDRESS: // Fake a bus reset IFF not state default and then process normally ++ BREAK_IF (pcd_instance->bus->device_state == STATE_DEFAULT); ++ udc_saw_bus_activity = 0; ++ usbd_bus_event_handler_irq (pcd_instance->bus, DEVICE_RESET, 0); ++ //usbd_bus_event_handler_irq (pcd_instance->bus, DEVICE_ADDRESS_ASSIGNED, 0); ++ break; ++ case USB_REQ_GET_DESCRIPTOR: // Fake a bus reset IFF suspended and then process normally ++ BREAK_IF (STATE_SUSPENDED != pcd_instance->bus->device_state); ++ udc_saw_bus_activity = 0; ++ usbd_bus_event_handler_irq (pcd_instance->bus, DEVICE_RESET, 0); ++ //usbd_bus_event_handler_irq (pcd_instance->bus, DEVICE_ADDRESS_ASSIGNED, 0); ++ break; ++ } ++ if (pcd_recv_setup_irq(pcd_instance, &request)) { ++ TRACE_MSG1(PCD, "ep0 STALL %d", cs); ++ au_writel(USBDEV_CS_STALL, USBD_EP0CS); ++ return; ++ } ++ if (((request.bmRequestType & USB_REQ_DIRECTION_MASK) == USB_REQ_HOST2DEVICE) && le16_to_cpu (request.wLength)) { ++ TRACE_MSG1(PCD, "ep0 Class H2D request %04x", le16_to_cpu(request.wLength)); ++ endpoint->state = DATA_STATE_RECV; ++ return; ++ } ++ if ((request.bmRequestType & USB_REQ_DIRECTION_MASK) == USB_REQ_HOST2DEVICE) { ++ TRACE_MSG1(PCD, "ep0 Class H2D request %04x", le16_to_cpu(request.wLength)); ++ if ((request.bmRequestType & ~USB_REQ_DIRECTION_MASK)) { ++ TRACE_MSG1(PCD, "ep0 Class or Vendor, send ZLP %d", cs); ++ send_zlp(0); ++ return; ++ } ++ } ++ TRACE_MSG1(PCD, "ep0 Class D2H request %04x", le16_to_cpu(request.wLength)); ++} ++/* ********************************************************************************************* */ ++/* au_start_in_bulk - start transmit ++ * The au1x00 will start to send when the first byte is loaded into the FIFO, either by ++ * DMA or PIO. The packetsize must be set first. ++ */ ++static void au_start_in_bulk (unsigned int epn, struct usbd_endpoint_instance *endpoint, struct ep_regs *ep) ++{ ++ struct usbd_urb *urb = endpoint->tx_urb; ++ u8 *bp = urb->buffer + endpoint->sent; ++ int last; ++ int i; ++ u32 *lp; ++ TRACE_MSG1(PCD, "START IN BULK %d", epn); ++ RETURN_IF (!urb || (( (urb->actual_length - endpoint->sent) == 0) && !(urb->flags & USBD_URB_SENDZLP))); ++ last = ep->last = endpoint->last = MIN (urb->actual_length - endpoint->sent, endpoint->wMaxPacketSize); ++ TRACE_MSG2(PCD, "START IN BULK sent: %d last:%d", endpoint->sent, last); ++ au_writel(USBDEV_FSTAT_FLUSH | USBDEV_FSTAT_UF | USBDEV_FSTAT_OF, ep->wrs); // XXX ++ if (!last) { ++ if (endpoint->tx_urb->flags & USBD_URB_SENDZLP) { ++ TRACE_MSG0(PCD, "START IN BULK - zending ZLP"); ++ send_zlp(epn); ++ endpoint->tx_urb->flags &= ~USBD_URB_SENDZLP; ++ } ++ return; ++ } ++ else if (8 >= last) { ++ au_writel(last << 1, ep->cs); ++ au_fifo_write(epn, bp, last); ++ return; ++ } ++ ++ dma_cache_wback_inv((unsigned long) bp, last); ++ //au_writel(last << 1, ep->cs); ++ au_start_dma_in(ep->indma, ep->tx_id, bp, last); ++ au_writel(last << 1, ep->cs); ++} ++ ++static void au_in_bulk(unsigned int epn, struct ep_regs *ep, struct usbd_endpoint_instance *endpoint) ++{ ++ struct usbd_urb *tx_urb; ++ int rc = 0; ++ u32 cs = au_readl(ep->cs); ++ u32 wrs = au_readl(ep->wrs); ++ u32 residue = au1xxx_get_dma_residue(ep->indma); ++ TRACE_MSG3(PCD, "BULK IN EPN - cs: %x wrs: %x residue:%d", cs, wrs, residue); ++ if (wrs) { // check for underflow or overflow ++ rc = 1; ++ if (wrs & USBDEV_FSTAT_UF) { ++ TRACE_MSG2(PCD, "BULK IN EPN - UF epn %d wrs: %x", epn, wrs); ++ rc = 1; // set rc to indicate an error ++ } ++ if (wrs & USBDEV_FSTAT_OF) ++ TRACE_MSG2(PCD, "BULK IN EPN - OF epn %d wrs: %x", epn, wrs); ++ au_writel(USBDEV_FSTAT_FLUSH | USBDEV_FSTAT_UF | USBDEV_FSTAT_OF, ep->wrs); // flush the fifo ++ } ++ if (cs & USBDEV_CS_NAK) { ++ RETURN_IF (ep->last && ((wrs&0x1f) == ep->last)); ++ rc = 1; ++ } ++ if (cs & USBDEV_CS_STALL) { // clear stall if present ++ TRACE_MSG1(PCD, "BULK IN EPN - CLEAR STALL %d", epn); ++ cs &= ~USBDEV_CS_STALL; ++ au_writel(cs, ep->cs); ++ return; ++ } ++ TRACE_MSG4(PCD, "BULK IN EPN epn: %d rc: %d last: %d sent: %d", epn, rc, endpoint->last, endpoint->sent); ++ ++ if ((tx_urb = pcd_tx_complete_irq(endpoint, rc))) { ++ if ((tx_urb->actual_length > endpoint->sent) || (endpoint->tx_urb->flags & USBD_URB_SENDZLP)) { ++ au_start_in_bulk(epl2p[endpoint->bEndpointAddress&0xf], endpoint, ep); ++ TRACE_MSG1(PCD, "BULK IN EPN - LEFT TO SEND %d", tx_urb->actual_length - endpoint->sent); ++ return; ++ } ++ } ++ au_epn_interrupt_disable(epn); // disable interrupts ++} ++ ++/* au_start_in_iso - start transmit ++ * The au1x00 will start to send when the first byte is loaded into the FIFO, either by DMA or ++ * PIO. The packetsize must be set first. ++ */ ++static void au_start_in_iso (unsigned int epn, struct usbd_endpoint_instance *endpoint, struct ep_regs *ep) ++{ ++ struct usbd_urb *urb = endpoint->tx_urb; ++ unsigned char *bp = urb->buffer + endpoint->sent; ++ int last; ++ TRACE_MSG2(PCD, "START IN ISO actual: %d sent: %d", urb->actual_length, endpoint->sent); ++ RETURN_IF ((urb->actual_length - endpoint->sent) == 0); ++ last = ep->last = endpoint->last = MIN (urb->actual_length - endpoint->sent, endpoint->wMaxPacketSize); ++ TRACE_MSG2(PCD, "START IN ISO last: %d packetSize: %d", last, endpoint->wMaxPacketSize); ++ au_writel(USBDEV_FSTAT_FLUSH | USBDEV_FSTAT_UF | USBDEV_FSTAT_OF, ep->wrs); // XXX ++ au_epn_interrupt_enable (epn); ++ dma_cache_wback_inv((unsigned long) bp, last); ++ au_writel(last << 1, ep->cs); ++ au_start_dma_in(ep->indma, ep->tx_id, bp, last); ++} ++ ++static void au_in_iso(unsigned int epn, struct ep_regs *ep, struct usbd_endpoint_instance *endpoint) ++{ ++ struct usbd_urb *tx_urb = pcd_tx_complete_irq(endpoint, 0); ++ u32 cs = au_readl(ep->cs); ++ u32 wrs = au_readl(ep->wrs); ++ TRACE_MSG2(PCD, "ISO IN EPN - cs: %x wrs: %x", cs, wrs); ++ TRACE_MSG4(PCD, "ISO IN EPN epn: %d last: %d sent: %d", epn, endpoint->last, endpoint->sent, 0); ++ ep->last = 0; ++ if ((tx_urb = endpoint->tx_urb) && (tx_urb->actual_length > endpoint->sent)) { ++ au_start_in_iso(epl2p[endpoint->bEndpointAddress&0xf], endpoint, ep); ++ /* XXX magic delay - without this large packets will eventually stall the transmit ++ * XXX and all traffic in both directions will stop. ++ */ ++ TRACE_MSG1(PCD, "ISO IN EPN - LEFT TO SEND %d", tx_urb->actual_length - endpoint->sent); ++ return; ++ } ++ ++ TRACE_MSG0(PCD, "ISO IN EPN - nothing to send"); ++ au_epn_interrupt_disable(epn); // disable interrupts ++} ++/* ********************************************************************************************* */ ++ ++static struct usbd_urb *au_rcv_complete_epn_irq(struct usbd_endpoint_instance *endpoint, int len, int urb_bad) ++{ ++ return pcd_rcv_complete_irq(endpoint, len, urb_bad); ++} ++ ++ ++static void au_start_out_bulk(unsigned int epn, struct usbd_endpoint_instance *endpoint, struct ep_regs *ep) ++{ ++ TRACE_MSG2(PCD, "START OUT BULK %d actual: %d pcktSize: %d", epn, endpoint->wMaxPacketSize); ++ if (!endpoint->rcv_urb) { ++ TRACE_MSG0(PCD, "START OUT BULK DISABLE"); ++ au_epn_interrupt_disable(epn); ++ return; ++ } ++ if (endpoint->rcv_error) { ++ TRACE_MSG0(PCD, "START OUT BULK reseting rcv_error"); ++ endpoint->rcv_error = 0; ++ } ++ au_start_dma_out(ep->outdma, ep->rx_id, ++ endpoint->rcv_urb->buffer + endpoint->rcv_urb->actual_length, endpoint->wMaxPacketSize); ++} ++ ++static void au_out_bulk(unsigned int epn, struct ep_regs *ep, struct usbd_endpoint_instance *endpoint) ++{ ++ int bytes = 0; ++ int residue = 0; ++ u32 *lp = NULL; ++ ++ struct usbd_urb *urb; ++ struct usbd_urb *completed_urb = NULL; ++ u32 cs; ++ u32 rds; ++ u32 nrds; ++ int i; ++ ++ cs = au_readl(ep->cs); ++ rds = au_readl(ep->rds); ++ ++ // XXX it appears that this is zero if 64 bytes where received!!! ++ ++ // get dest ring buffer ++ au1xxx_dbdma_get_dest(ep->outdma, (void **)&lp, &bytes); ++ ++ au_halt_dma(ep->outdma); ++ residue = endpoint->wMaxPacketSize - au1xxx_get_dma_residue(ep->outdma); ++ ++ TRACE_MSG5(PCD, "BULK OUT CS: %04x RD: %04x bytes: %d residue: %d DMA %s", ++ cs, rds, bytes, residue, lp ? "FINISHED" : "ACTIVE"); ++ ++ /* check for NAK ++ */ ++ if (cs & USBDEV_CS_NAK) { ++ TRACE_MSG0(PCD, "NAK bytes: 0"); ++ bytes = 0; ++ } ++ ++ /* with dbdma residue will either be zero or non-zero and less than packetsize, if ++ * zero then a complete transfer of packetsize was performed. ++ */ ++ else if (!residue && !rds) { ++ TRACE_MSG0(PCD, "BULK OUT CS: forcing bytes: 64"); ++ bytes = endpoint->wMaxPacketSize; ++ } ++ else { ++ TRACE_MSG0(PCD, "BULK OUT CS: using residue bytes: 64"); ++ bytes = residue; ++ } ++ ++ ++ if (!(urb = pcd_rcv_next_irq(endpoint))) { ++ TRACE_MSG0(PCD, "BULK OUT EPN - no rcv_urb"); ++ au_epn_interrupt_disable(epn); ++ return; ++ } ++ ++ TRACE_MSG4(PCD, "BULK OUT CS: %04x RD: %04x actual: %d DMA bytes: %d", cs, rds, urb->actual_length, bytes); ++ ++ if (bytes) ++ dma_cache_inv((u32) urb->buffer + urb->actual_length, bytes); ++ ++ /* The original AU1X00 UDC design will continue to receive data as long as there is room ++ * in the FIFO. We cannot tell when we are at the end of a packet and/or have the start ++ * of a new one. ++ * ++ * There are only two scenarios that are guaranteed (almost) to be correct: ++ * ++ * 64 bytes of data from DMA, empty fifo, continue Bulk OUT < 60 bytes of data and ++ * < 4 bytes in fifo, end Bulk OUT. ++ * ++ * There may be a third scenario that is ok: ++ * ++ * 0 bytes dma, 0 bytes in fifo, NAK ++ * ++ * Everything else is an error. In all cases we assume that it is safer to drop data ++ * than to accept it in error. This allows CRC or size protected encapsulations to ++ * notice bulk transfers received with errors. ++ * ++ * In general none of the policies or strategies are able to cope with all errors ++ * without missing errors and dropping good data. The intent is to minimize the amount ++ * of potentially bad data getting to the function driver while minimizing the amount of ++ * good data that is dropped. ++ * ++ * The new silicon mitigates this problem for non control endpoints because it will NAK ++ * additional data until the interrupt service flag is reset. ++ * ++ * Start with generic error tests, OF, UF or NAK indicate an error we cannot recover ++ * from, start flushing until end of current bulk transfer (wait for a short packet) ++ * ++ * XXX The following needs to be tested and streamlined for au1550, much of the extra ++ * testing done for older silicon cab probably be eliminated. For example the extra read ++ * of nrds to check for overrun in the FIFO should not be required. ++ * ++ */ ++ if (rds & (USBDEV_FSTAT_OF | USBDEV_FSTAT_UF)) { ++ TRACE_MSG2(PCD, "BULK OUT FLUSHING %d length: %d", bytes, urb->actual_length); ++ THROW(start_flushing); ++ } ++ rds = rds & USBDEV_FSTAT_FCNT_MASK; ++ nrds = au_readl(ep->rds); ++ ++ /* full size packet received, check that we are not flushing and that the FIFO ++ * does not have any data. If there is data in the FIFO we may not be able to ++ * restart DMA in time, so start flushing ++ */ ++ if (endpoint->wMaxPacketSize == bytes) { ++ TRACE_MSG2(PCD, "BULK OUT 64 BYTES %d length: %d", bytes, urb->actual_length); ++ if (endpoint->rcv_error) { ++ TRACE_MSG4(PCD, "FULL PACKET bytes: %d rds: %d nrds: %d cp0: %d CONTINUE FLUSHING", ++ bytes, rds, nrds, cp0_count); ++ THROW(start_flushing); ++ } ++ if ((nrds > 6) && (nrds < 8) ) { ++ TRACE_MSG4(PCD, "FIFO not empty bytes: %d rds: %d nrds: %d cp0: %d START FLUSHING nrds > 6 < 8", ++ bytes, rds, nrds, cp0_count); ++ THROW(start_flushing); ++ } ++ if (!urb->actual_length) ++ TRACE_MSG4(PCD, "PACKET ok bytes: %d rds: %d nrds: %d cp0: %d ACCEPTING 64 bytes", ++ bytes, rds, nrds, cp0_count); ++ au_writel(USBDEV_FSTAT_FLUSH | USBDEV_FSTAT_UF | USBDEV_FSTAT_OF, ep->rds); ++ au_rcv_complete_epn_irq(endpoint, endpoint->wMaxPacketSize, 0); ++ if (cs & USBDEV_CS_NAK) ++ if (nrds && (nrds < 8) ) { ++ TRACE_MSG4(PCD, "NAK bytes: %d rds: %d nrds: %d cp0: %d START FLUSHING CS_NAK", ++ bytes, rds, nrds, cp0_count); ++ THROW(start_flushing); ++ } ++ } ++ /* a nak'd packet may be ok to ignore IFF the FIFO is empty(?) or completely full. ++ */ ++ else if ((cs & USBDEV_CS_NAK) && (!nrds || (nrds == 8)) ) { ++ TRACE_MSG2(PCD, "BULK OUT NAK BYTES %d length: %d", bytes, urb->actual_length); ++ if (endpoint->rcv_error) { ++ TRACE_MSG4(PCD, "NAK bytes: %d rds: %d nrds: %d cp0: %d CONTINUE FLUSHING", ++ bytes, rds, nrds, cp0_count); ++ THROW(start_flushing); ++ } ++ au_writel(USBDEV_FSTAT_FLUSH | USBDEV_FSTAT_UF | USBDEV_FSTAT_OF, ep->rds); ++ } ++ /* short packet by DMA, additional data for this packet in FIFO, more than 3 ++ * bytes is probably an error. ++ */ ++ else { ++ TRACE_MSG2(PCD, "BULK OUT < 64 BYTES %d length: %d", bytes, urb->actual_length); ++ if ((cs & USBDEV_CS_NAK) && nrds && (nrds < 8) ) { ++ TRACE_MSG4(PCD, "BULK OUT NAK bytes: %d rds: %d nrds: %d cp0: %d START FLUSHING", ++ bytes, rds, nrds, cp0_count); ++ THROW(start_flushing); ++ } ++ if (nrds > 4) { ++ TRACE_MSG4(PCD, "BULK OUT SHORT PACKET by DMA full FIFO bytes: %d rds: %d nrds: %d cp0: %d START FLUSH", ++ bytes, rds, nrds, cp0_count); ++ THROW(start_flushing); ++ } ++ TRACE_MSG4(PCD, "BULK OUT SHORT bytes: %d rds: %d nrds: %d cp0: %d reading fifo", bytes, rds, nrds, cp0_count); ++ au_fifo_read(ep, urb->buffer + urb->actual_length + bytes, nrds); ++ bytes += nrds; ++ TRACE_MSG1(PCD, "BULK OUT < 64 BYTES %d", bytes); ++ au_writel(USBDEV_FSTAT_FLUSH | USBDEV_FSTAT_UF | USBDEV_FSTAT_OF, ep->rds); ++ if (!endpoint->rcv_error) { ++ TRACE_MSG0(PCD, "BULK OUT COMPLETED URB"); ++ au_rcv_complete_epn_irq(endpoint, bytes, 0); ++ } ++ else { ++ TRACE_MSG0(PCD, "BULK OUT - FLUSHING URB - reseting rcv_error"); ++ endpoint->rcv_error = 0; ++ } ++ } ++ CATCH(start_flushing) { ++ TRACE_MSG0(PCD, "BULK OUT - START FLUSHING URB"); ++ endpoint->rcv_error = 1; ++ endpoint->rcv_urb->actual_length = 0; ++ au_writel(USBDEV_FSTAT_FLUSH | USBDEV_FSTAT_UF | USBDEV_FSTAT_OF, ep->rds); ++ bytes = 0; ++ } ++ TRACE_MSG0(PCD, "BULK OUT - RESTARTING"); ++ au_start_out_bulk(epn, endpoint, ep); ++} ++ ++static void au_start_out_iso(unsigned int epn, struct usbd_endpoint_instance *endpoint, struct ep_regs *ep) ++{ ++ RETURN_IF(!endpoint->rcv_urb); ++ au_start_dma_out(ep->outdma, ep->rx_id, ++ endpoint->rcv_urb->buffer + endpoint->rcv_urb->actual_length, endpoint->wMaxPacketSize); ++} ++ ++static void au_out_iso(unsigned int epn, struct ep_regs *ep, struct usbd_endpoint_instance *endpoint) ++{ ++ int bytes = 0; ++ struct usbd_urb *urb; ++ u32 cs; ++ u32 rds; ++ au_halt_dma(ep->outdma); ++ cs = au_readl(ep->cs); ++ rds = au_readl(ep->rds); ++ if (!endpoint) { ++ au_writel(USBDEV_FSTAT_FLUSH | USBDEV_FSTAT_UF | USBDEV_FSTAT_OF, ep->rds); ++ return; ++ } ++ if (!(urb = endpoint->rcv_urb)) { ++ TRACE_MSG2(PCD, "ISO OUT EPN - rcv_urb was NULL bytes: %d rds: %d", bytes, rds); ++ au_rcv_complete_epn_irq(endpoint, bytes, 1); ++ TRACE_MSG0(PCD, "ISO OUT setting rcv_error"); ++ } ++ bytes = endpoint->wMaxPacketSize - au1xxx_get_dma_residue(ep->outdma); ++ rds = rds & USBDEV_FSTAT_FCNT_MASK; ++ au_fifo_read(ep, urb->buffer + urb->actual_length + bytes, rds); ++ bytes += rds; ++ au_writel(USBDEV_FSTAT_FLUSH | USBDEV_FSTAT_UF | USBDEV_FSTAT_OF, ep->rds); ++ au_rcv_complete_epn_irq(endpoint, bytes, 0); ++ au_start_out_iso(epn, endpoint, ep); ++} ++/* ********************************************************************************************* */ ++/* au_tx_dma_done - TX DMA interrupt handler ++ */ ++static void au_tx_dma_done(int irq, void *dev_id, struct pt_regs *regs) ++{ ++ int epn = (int) dev_id; ++ struct usbd_endpoint_instance *endpoint = pcd_instance->bus->endpoint_array + epn; ++ struct ep_regs *ep = &ep_regs[epn]; ++ int residue; ++ ocd_ops.interrupts++; ++ RETURN_IF (!epn); ++ au_halt_dma(ep->indma); ++ ep->last = 0; ++} ++ ++/* au_int_req - usb interrupt handler ++ */ ++static void au_int_req (int irq, void *dev_id, struct pt_regs *regs) ++{ ++ u32 intstat; ++ struct ep_regs *ep; ++#if defined(MAX_INTR_LOOP_STATS) ++ u32 loopcount = 0; ++#endif ++#ifdef RECORD_LATENCY ++ cp0_count = (read_c0_count(CP0_COUNT) - cp0_record) >> 9; ++ if (cp0_count < CP0_COUNTS) ++ cp0_counts[cp0_count]++; ++#endif ++#ifdef CHECK_LATENCY ++ u32 cp0_count = read_c0_count(CP0_COUNT); ++#endif ++ ocd_ops.interrupts++; ++ TRACE_MSG2(PCD, "INT - device: %d status: %d", pcd_instance->bus->device_state, pcd_instance->bus->status); ++#if 0 ++ if (ocd_ops.interrupts > 2000) { ++ TRACE_MSG0(PCD, "UDC_INT call udc_disable_interrupts"); ++ au_inten(0, "DISABLING"); ++ return; ++ } ++#endif ++ while (( intstat = au_readl(USBD_INTSTAT) & au1x00_inten)) { // read and reset interrupt status register ++ ++ int epn; ++#if 1 ++ for (epn = 2; epn < 6; epn++) { ++ CONTINUE_IF (!(intstat & (1 << epn))); ++#else ++ // NOT TESTED ++ u32 local_intstat = intstat; ++ TRACE_MSG2(PCD, "INTSTAT: %04x %04x", intstat, au_readl(USBD_INTEN)); ++ while (local_intstat & 0x3f) { ++ int epn = 31 - au_clz(local_intstat); ++ local_intstat &= ~(1<<epn); ++#endif ++ if (udc_saw_bus_activity) { ++ udc_saw_bus_activity = 0; ++ usbd_bus_event_handler_irq (pcd_instance->bus, DEVICE_BUS_ACTIVITY, 0); ++ au_inten(0x0033/*|USBDEV_INT_SOF*/, "int SOF"); ++ } ++ ep = &ep_regs[epn]; ++ switch(ep->eptype) { ++ case USB_DIR_IN | USB_ENDPOINT_BULK: ++ case USB_DIR_IN | USB_ENDPOINT_INTERRUPT: ++ //TRACE_MSG2(PCD, "BULK IN %d %02x", epn, ep->eptype); ++ au_in_bulk(epn, ep, pcd_instance->bus->endpoint_array + epn); ++ break; ++ case USB_DIR_IN | USB_ENDPOINT_ISOCHRONOUS: ++ //TRACE_MSG2(PCD, "ISO IN %d %02x", epn, ep->eptype); ++ au_in_iso(epn, ep, pcd_instance->bus->endpoint_array + epn); ++ break; ++ case USB_DIR_OUT | USB_ENDPOINT_BULK: ++ case USB_DIR_OUT | USB_ENDPOINT_INTERRUPT: ++ //TRACE_MSG2(PCD, "BULK OUT %d %02x", epn, ep->eptype); ++ au_out_bulk(epn, ep, pcd_instance->bus->endpoint_array + epn); ++ break; ++ case USB_DIR_OUT | USB_ENDPOINT_ISOCHRONOUS: ++ //TRACE_MSG2(PCD, "ISO OUT %d %04d", epn, au_readl(NUSBD_FRAMENUM)); ++ au_out_iso(epn, ep, pcd_instance->bus->endpoint_array + epn); ++ break; ++ } ++ } ++ au_writel(intstat, USBD_INTSTAT); // Only clear the interrupt(s) AFTER servicing OUT ++ /* even though we disable the bulk-in interrupt (endpoint 2) prior to enabling ++ * DMA we always see one additional interrupt that is a NAK on that endpoint. ++ */ ++ CONTINUE_IF(!(intstat & au1x00_inten)); ++ /* handle control endpoint and suspend interrupt ++ */ ++ if (intstat & ( ((1 << 0) | (1 << 1) | (1 << 3) | (1 << 5) | USBDEV_INT_SOF))) { ++ if (intstat & (1 << 0)) ++ au_out_ep0(pcd_instance->bus->endpoint_array + 0, &ep_regs[0]); ++ if (intstat & (1 << 1)) ++ au_in_ep0(pcd_instance->bus->endpoint_array + 0, &ep_regs[0]); ++ if (intstat & USBDEV_INT_SOF) ++ if (USBD_SUSPENDED == pcd_instance->bus->status) { ++ TRACE_MSG0(PCD, "SUS - ACTIVITY"); ++ udc_saw_bus_activity++; ++ } ++ } ++#if defined(MAX_INTR_LOOP_STATS) ++ loopcount += 1; // Gather stats on how many times this loop is performed. ++#endif ++ } ++#if defined(MAX_INTR_LOOP_STATS) ++ interrupt_loop_stats[MIN(loopcount, MAX_INTR_LOOP_STATS)]++; ++#endif ++#if defined(CHECK_LATENCY) ++ TRACE_MSG1(PCD, "USB IRQ - %d", read_c0_count(CP0_COUNT) - cp0_count); ++#endif ++#if defined(RECORD_LATENCY) ++ cp0_record = read_c0_count(CP0_COUNT); ++#endif ++} ++ ++/* au_int_sus -suspend interrupt handler ++ */ ++static void au_int_sus (int irq, void *dev_id, struct pt_regs *regs) ++{ ++ ocd_ops.interrupts++; ++ TRACE_MSG2(PCD, "SUS - INACTIVE device: %d status: %d", pcd_instance->bus->device_state, pcd_instance->bus->status); ++ switch(pcd_instance->bus->status) { ++ case USBD_OPENING: ++ case USBD_OK: ++ au_inten(0x0033, "suspend"); ++ usbd_bus_event_handler_irq (pcd_instance->bus, DEVICE_BUS_INACTIVE, 0); ++ break; ++ default: ++ break; ++ } ++} ++/* ********************************************************************************************* */ ++/* ++ * au_connect - enable pullup resistor ++ * Turn on the USB connection by enabling the pullup resistor. ++ * ++ * au_disconnect - disable pullup resistor ++ * Turn off the USB connection by disabling the pullup resistor. ++ */ ++ ++#if defined(CONFIG_MIPS_FREEHAND_NATIVE) ++#warning "Old code for Freehand" ++void au_connect (struct pcd_instance *pcd) ++{ ++ au1000gpio_set(GPIO01); ++} ++ ++void au_disconnect (struct pcd_instance *pcd) ++{ ++ au1000gpio_clear(GPIO01); ++} ++#elif defined(CONFIG_AMX_MORDOR) ++void au_connect (struct pcd_instance *pcd) ++{ ++ au_writel(0x00000008, SYS_OUTPUTSET); ++} ++ ++void au_disconnect (struct pcd_instance *pcd) ++{ ++ au_writel(0x00000008, SYS_OUTPUTCLR); ++} ++#elif defined(CONFIG_MIPS_DB1550) || defined(CONFIG_MIPS_MTX2) ++void au_connect (struct pcd_instance *pcd) ++{ ++ au_writel(au_readl(GPIO2_DIR) | 0x100, GPIO2_DIR); ++ au_writel(0x01000100, GPIO2_OUTPUT); ++ au_sync(); ++} ++void au_disconnect (struct pcd_instance *pcd) ++{ ++ au_writel(au_readl(GPIO2_DIR) | 0x100, GPIO2_DIR); ++ au_writel(0x01000000, GPIO2_OUTPUT); ++ au_sync(); ++} ++#else ++#warning "Please define au_connect / au_disconnect" ++void au_connect (struct pcd_instance *pcd) ++{ ++} ++void au_disconnect (struct pcd_instance *pcd) ++{ ++} ++#endif ++ ++#undef read_c0_COUNT ++#ifndef read_c0_count ++#define read_c0_count() read_32bit_cp0_register(CP0_COUNT) ++#endif ++/* ********************************************************************************************* */ ++/* au_ticks - get current ticks ++* */ ++u64 au_ticks (void) ++{ ++ return read_c0_count(); ++} ++ ++/* au_elapsed - return micro-seconds between two tick values ++ */ ++u64 au_elapsed(u64 *t1, u64 *t2) ++{ ++ u64 ticks = (*t1 > *t2) ? (*t1 - *t2) : (*t2 - *t1); ++ ticks = (u32)((u32) ticks / (u32) CONFIG_OTG_AU1X00_SCLOCK); ++ return ticks; ++} ++ ++/* au_framenum - get current framenum ++ */ ++u16 au_framenum (void) ++{ ++ return au_readl(NUSBD_FRAMENUM); ++} ++ ++ ++/* ********************************************************************************************* */ ++/* au_start_endpoint_in - start transmit ++ */ ++void au_start_endpoint_in(struct pcd_instance *pcd, struct usbd_endpoint_instance *endpoint) ++{ ++ int epn = epl2p[endpoint->bEndpointAddress&0xf]; ++ struct ep_regs *ep = &ep_regs[epn]; ++ switch(endpoint->bmAttributes & USB_ENDPOINT_MASK) { ++ case USB_ENDPOINT_CONTROL: ++ au_in_ep0(endpoint, ep); ++ break; ++ case USB_ENDPOINT_BULK: ++ case USB_ENDPOINT_INTERRUPT: ++ au_start_in_bulk(epn, endpoint, ep); ++ break; ++ case USB_ENDPOINT_ISOCHRONOUS: ++ au_start_in_iso(epn, endpoint, ep); ++ break; ++ } ++ au_epn_interrupt_enable(epn); ++} ++ ++/* au_start_endpoint_out - start receive ++ */ ++void au_start_endpoint_out(struct pcd_instance *pcd, struct usbd_endpoint_instance *endpoint) ++{ ++ int epn = epl2p[endpoint->bEndpointAddress&0xf]; ++ struct ep_regs *ep = &ep_regs[epn]; ++ switch(endpoint->bmAttributes & USB_ENDPOINT_MASK) { ++ case USB_ENDPOINT_CONTROL: ++ break; ++ case USB_ENDPOINT_BULK: ++ case USB_ENDPOINT_INTERRUPT: ++ au_start_out_bulk(epn, endpoint, ep); ++ au_epn_interrupt_enable(epn); ++ break; ++ case USB_ENDPOINT_ISOCHRONOUS: ++ au_start_out_iso(epn, endpoint, ep); ++ break; ++ } ++} ++ ++/* ++ * au_pcd_endpoint_halted() - is endpoint halted ++ */ ++static int ++au_pcd_endpoint_halted (struct pcd_instance *pcd, struct usbd_endpoint_instance *endpoint) ++{ ++int cs; ++ int epn = epl2p[endpoint->bEndpointAddress&0xf]; ++ int dir = (endpoint->bEndpointAddress & 0x80) ? 1 : 0; ++ struct ep_regs *ep = &ep_regs[epn]; ++ ++ TRACE_MSG2(PCD, "epn: %02x dir: %x", epn, dir); ++ return ((cs = au_readl(ep->cs)) & USBDEV_CS_STALL) ? TRUE : FALSE; ++} ++ ++/* au_pcd_halt_endpoint - halt endpoint ++ */ ++int au_pcd_halt_endpoint(struct pcd_instance *pcd, struct usbd_endpoint_instance *endpoint, int flag) ++{ ++ int epn = epl2p[endpoint->bEndpointAddress&0xf]; ++ int dir = (endpoint->bEndpointAddress & 0x80) ? 1 : 0; ++ struct ep_regs *ep = &ep_regs[epn]; ++ ++ TRACE_MSG3(PCD, "epn: %02x dir: %x flag: %d", epn, dir, flag); ++ ++ au_writel(au_readl(ep->cs) & USBDEV_CS_STALL, ep->cs); ++ return 0; ++} ++ ++ ++ ++void au_cancel_in_irq(struct pcd_instance *pcd, struct usbd_urb *urb) ++{ ++ int epn = epl2p[urb->endpoint->bEndpointAddress&0xf]; ++ struct ep_regs *ep = &ep_regs[epn]; ++ au_in_bulk(epn, ep, urb->endpoint); ++} ++ ++void au_cancel_out_irq(struct pcd_instance *pcd, struct usbd_urb *urb) ++{ ++ int epn = epl2p[urb->endpoint->bEndpointAddress&0xf]; ++ struct ep_regs *ep = &ep_regs[epn]; ++ au_out_bulk(epn, ep, urb->endpoint); ++} ++ ++/* au_serial_init - set a serial number if available ++ */ ++int au_serial_init (struct pcd_instance *pcd) ++{ ++#if defined(CONFIG_MIPS_FREEHAND) ++ int length; ++ long data[16]; /* yeah a hack, but we KNOW it's 16 */ ++ struct i2c_client *client; ++ char chData[16]; ++ int i; ++ ++ if (!(client = getFreeHandEepromClient())) { ++ printk(KERN_INFO"eeprom not ready when au_serial_init called\n"); ++ return -EINVAL; ++ } ++ eeprom_contents(client, SENSORS_PROC_REAL_READ, EEPROM_SYSCTL1, &length, (long *)data); ++ ++ /* serial number is first 9 longs. But each long is just an ASCII char ++ * convert this to a string and then extract a 4 byte value from it ++ */ ++ for (i = 0; i < 9; chData[i] = (char)data[i], i++); /* trunc, is ok */ ++ chData[9] = 0; /* terminate string */ ++ printk(KERN_INFO"%s: %s\n", __FUNCTION__, chData); ++ pcd_instance->bus->serial_number_str = lstrdup(chData); ++ return 0; ++#else ++ return -EINVAL; ++#endif ++} ++ ++ ++/* au_start - start session ++ */ ++void au_start (struct pcd_instance *pcd) ++{ ++ au_inten(0x0033/*|USBDEV_INT_SOF*/, "START"); // Only enable receive interrupts. ++} ++ ++/* au_stop - stop session ++ */ ++void au_stop (struct pcd_instance *pcd) ++{ ++ //au_inten(0, "STOP"); ++} ++ ++int au_assign_endpoint( u8 physicalEndpoint, int used[6], struct usbd_endpoint_map *endpoint_map, u8 bmAttributes, ++ u16 wMaxPacketSize, u16 transferSize) ++{ ++ struct ep_regs *ep = &ep_regs[physicalEndpoint]; ++ RETURN_EINVAL_IF(used[physicalEndpoint]); ++ endpoint_map->bEndpointAddress[0] = epp2l[physicalEndpoint]; ++ endpoint_map->physicalEndpoint[0] = physicalEndpoint; ++ endpoint_map->wMaxPacketSize[0] = wMaxPacketSize; ++ endpoint_map->transferSize[0] = transferSize; ++ endpoint_map->bmAttributes[0] = bmAttributes; ++ used[physicalEndpoint]++; ++ ep->eptype = bmAttributes & 0x83; ++ return 0; ++} ++ ++int au_request_endpoints(struct pcd_instance *pcd, struct usbd_endpoint_map *endpoint_map_array, int endpointsRequested, ++ struct usbd_endpoint_request *requestedEndpoints) ++{ ++ struct usbd_device_description *device_description; ++ int i, j; ++ int used[UDC_MAX_ENDPOINTS]; ++ memset(used, 0, sizeof(used)); ++ for (j = 0, i = 0; i < endpointsRequested; i++, j++) { ++ struct usbd_endpoint_map *endpoint_map = endpoint_map_array + i; ++ u8 bmAttributes = requestedEndpoints[i].bmAttributes; ++ u16 transferSize = requestedEndpoints[i].fs_requestedTransferSize; ++ RETURN_EINVAL_UNLESS(j < UDC_MAX_ENDPOINTS); ++ switch(bmAttributes) { ++ case USB_DIR_OUT | USB_ENDPOINT_BULK: ++ case USB_DIR_OUT | USB_ENDPOINT_INTERRUPT: ++ case USB_DIR_OUT | USB_ENDPOINT_INTERRUPT | USB_ENDPOINT_OPT: ++ CONTINUE_IF(!au_assign_endpoint(4, used, endpoint_map, bmAttributes, 0x40, transferSize)); ++ CONTINUE_IF(!au_assign_endpoint(5, used, endpoint_map, bmAttributes, 0x40, transferSize)); ++ break; ++ case USB_DIR_OUT | USB_ENDPOINT_ISOCHRONOUS: ++ CONTINUE_IF(!au_assign_endpoint(4, used, endpoint_map, bmAttributes, transferSize, transferSize)); ++ CONTINUE_IF(!au_assign_endpoint(5, used, endpoint_map, bmAttributes, transferSize, transferSize)); ++ break; ++ case USB_DIR_IN | USB_ENDPOINT_BULK: ++ case USB_DIR_IN | USB_ENDPOINT_INTERRUPT: ++ case USB_DIR_IN | USB_ENDPOINT_INTERRUPT | USB_ENDPOINT_OPT: ++ CONTINUE_IF(!au_assign_endpoint(2, used, endpoint_map, bmAttributes, 0x40, transferSize)); ++ CONTINUE_IF(!au_assign_endpoint(3, used, endpoint_map, bmAttributes, 0x40, transferSize)); ++ break; ++ case USB_DIR_IN | USB_ENDPOINT_ISOCHRONOUS: ++ CONTINUE_IF(!au_assign_endpoint(2, used, endpoint_map, bmAttributes, transferSize, transferSize)); ++ CONTINUE_IF(!au_assign_endpoint(3, used, endpoint_map, bmAttributes, transferSize, transferSize)); ++ break; ++ } ++ CONTINUE_IF(bmAttributes & USB_ENDPOINT_OPT); ++ return -EINVAL; ++ } ++ return 0; ++} ++ ++struct usbd_endpoint_map *au_endpoint_map_array; ++int au_endpointsRequested; ++ ++int au_set_endpoints2(struct pcd_instance *pcd, int endpointsRequested, struct usbd_endpoint_map *endpoint_map_array); ++int au_set_endpoints(struct pcd_instance *pcd, int endpointsRequested, struct usbd_endpoint_map *endpoint_map_array) ++{ ++ au_endpointsRequested = endpointsRequested; ++ au_endpoint_map_array = endpoint_map_array; ++ return au_set_endpoints2(pcd, au_endpointsRequested, au_endpoint_map_array); ++} ++ ++int au_set_endpoints2(struct pcd_instance *pcd, int endpointsRequested, struct usbd_endpoint_map *endpoint_map_array) ++{ ++ int i, j; ++ u8 config[25]; ++ u8 *cp; ++ memcpy(config, au1x00_config_bulk, sizeof(config)); ++ for (i = 0; i < endpointsRequested; i++) { ++ struct usbd_endpoint_map *endpoint_map = endpoint_map_array + i; ++ int epreq = endpoint_map->bmAttributes[0]; ++ int eptype = epreq & USB_ENDPOINT_MASK; ++ int epdir = epreq & USB_ENDPOINT_DIR_MASK ? 0x8 : 0; ++ int epsize = endpoint_map->wMaxPacketSize[0]; ++ int epaddr = epp2l[endpoint_map->physicalEndpoint[0]]; ++ CONTINUE_IF(!endpoint_map->physicalEndpoint[0] || (endpoint_map->physicalEndpoint[0] > 5)); ++ cp = config + ((endpoint_map->physicalEndpoint[0] - 1) * 5); ++ cp[0] = (epaddr & 0xf) << 4 | 0x4; ++ cp[1] = (eptype << 4) | epdir | (epsize & 0x380) >> 7; ++ cp[2] = (epsize & 0x7F) << 1; ++ } ++ TRACE_MSG0(PCD, "Disable interrupts"); ++ au_inten(0, "SET ENDPOINTS"); // disable interrupts ++ au_writel(0x0002, USBD_ENABLE); // reset controller ++ udelay(100); ++ au_writel(0x0003, USBD_ENABLE); // enable controller ++ udelay(100); ++ for (cp = config, i = 0; i < 25; i++, au_writel(*cp++, USBD_CONFIG)); // feed the config into the UDC ++ return 0; ++} ++ ++/* au_loc_conn - used to enable or disable peripheral connecting to bus ++ * ++ */ ++void au_loc_conn(struct otg_instance *otg, u8 flag) ++{ ++ struct pcd_instance *pcd = (struct pcd_instance *)otg->pcd; ++ TRACE_MSG0(PCD, "--"); ++ TRACE_MSG1(PCD, "PCD: %x", (int)pcd); ++ switch (flag) { ++ case SET: ++ TRACE_MSG0(PCD, "OUTPUT: OCD_LOC_CONN_SET - Enable DP PULLUP"); ++ au_set_endpoints2(pcd, au_endpointsRequested, au_endpoint_map_array); ++ au_inten(0x0033/*|USBDEV_INT_SOF*/, "START"); // Only enable receive interrupts. ++ au_connect(pcd); ++ break; ++ ++ case RESET: ++ TRACE_MSG0(PCD, "OUTPUT: OCD_LOC_CONN_RESET - Disable DP PULLUP"); ++ au_disconnect(pcd); ++ au_inten(0, "RESET ENDPOINTS"); // disable interrupts ++ au_writel(0x0000, USBD_ENABLE); // reset controller ++ break; ++ } ++} ++ ++ ++/* ********************************************************************************************* */ ++ ++ ++static u32 request_dma(int ep, int src, int dest, char *str, void dma_done(int , void *, struct pt_regs *)) ++{ ++ u32 dma; ++ ++ chan_tab_t *ctp; ++ volatile au1x_dma_chan_t *cp; ++ au1x_ddma_desc_t *dp; ++ ++ ++ if ((dma = au1xxx_dbdma_chan_alloc(src, dest, dma_done, (void *)ep)) < 0) { ++ printk(KERN_INFO"request_io[%d] dma: %x src: %d dest: %d %s FAILED\n", ep, dma, src, dest, str); ++ return -1; ++ } ++ ++ if(au1xxx_dbdma_ring_alloc(dma, 1) == 0) { ++ printk(KERN_INFO"request_io[%d] Ring Buffer Allocation for dma: %x src: %d dest: %d %s FAILED\n", ++ ep, dma, src, dest, str); ++ return -1; ++ } ++ ++ ctp = *((chan_tab_t **)dma); ++ cp = ctp->chan_ptr;; ++ dp = ctp->put_ptr; ++ dp->dscr_cmd0 &= ~DSCR_CMD0_V; ++ ++ TRACE_MSG8(PCD, "%08x %08x cmd: %08x %04x src: %08x %04x dst: %08x %08x DBDMA", ++ dma, dp, dp->dscr_cmd0, dp->dscr_cmd1, ++ dp->dscr_source0, dp->dscr_source1, dp->dscr_dest0, dp->dscr_dest1); ++ ++#if 0 ++ // XXX copy static components from au_start_out ++ // transmit - mem to usb ++ if (src == DBDMA_MEM_CHAN) { ++ TRACE_MSG8(PCD, "%08x %08x cmd: %08x %04x src: %08x %04x dst: %08x %08x MEM2USB", ++ dma, dp, dp->dscr_cmd0, dp->dscr_cmd1, ++ dp->dscr_source0, dp->dscr_source1, dp->dscr_dest0, dp->dscr_dest1); ++ } ++#endif ++#if 0 ++ // XXX copy static components from au_start_in ++ // receive - usb to mem ++ if (dest == DBDMA_MEM_CHAN) { ++ TRACE_MSG8(PCD, "%08x %08x cmd: %08x %04x src: %08x %08x dst: %08x %08x USB2MEM", ++ dma, dp, dp->dscr_cmd0, dp->dscr_cmd1, ++ dp->dscr_source0, dp->dscr_source1, dp->dscr_dest0, dp->dscr_dest1); ++ } ++#endif ++ TRACE_MSG5(PCD, "request_io[%d] dma: %x src: %d dest: %d %s SUCCEDED", ep, dma, src, dest, str); ++ return dma; ++} ++ ++/* au_mod_init ++ */ ++int au_mod_init (void) ++{ ++ int dev = request_irq (AU1000_USB_DEV_REQ_INT, au_int_req, SA_INTERRUPT, UDC_NAME "UDC Req", NULL); ++ int sus = request_irq (AU1000_USB_DEV_SUS_INT, au_int_sus, SA_INTERRUPT, UDC_NAME "UDC Sus", NULL); ++ u32 cp0_prid = read_c0_prid(); ++ int i; ++ ++ ++ if (dev || sus) { // check if either request irq failed ++ if (!dev) free_irq (AU1000_USB_DEV_REQ_INT, NULL); // free irqs that might have ++ if (!sus) free_irq (AU1000_USB_DEV_SUS_INT, NULL); // been successfully allocated ++ return -EINVAL; ++ } ++ for (i = 0; i < 6; i++) { ++ ep_regs[i].indma = ep_regs[i].tx_id ? ++ request_dma(i, DBDMA_MEM_CHAN, ep_regs[i].tx_id, ep_regs[i].tx_str, au_tx_dma_done) : -1; ++ ep_regs[i].outdma = ep_regs[i].rx_id ? ++ request_dma(i, ep_regs[i].rx_id, DBDMA_MEM_CHAN, ep_regs[i].rx_str, NULL) : -1; ++ } ++ switch (cp0_prid & CP0_PRID_SOC_MASK) { ++ case CP0_PRID_AU1000: ++ case CP0_PRID_AU1100: ++ case CP0_PRID_AU1500: ++ return -EINVAL; ++ case CP0_PRID_AU1550: ++ TRACE_MSG1(PCD, "AU1550 cp0_prid: %08x\n", cp0_prid); ++ printk(KERN_INFO"%s: AU1550 cp0_prid: %08x\n", __FUNCTION__, cp0_prid); ++ break; ++ default: ++ printk(KERN_INFO"%s: UNKNOWN CPU cp0_prid: %08x UNKNOWN UDC\n", __FUNCTION__, cp0_prid); ++ break; ++ } ++ return 0; ++} ++ ++/* au_mod_exit ++ */ ++void au_mod_exit (void) ++{ ++ int j; ++ au_writel(0x0000, USBD_ENABLE); ++ for (j = 0; j < 6; j++) { ++ struct ep_regs *ep = &ep_regs[j]; ++ if (ep->indma != -1) { ++ au1xxx_dbdma_chan_free(ep->indma); ++ ep->indma = -1; ++ } ++ if (ep->outdma != -1) { ++ au1xxx_dbdma_chan_free(ep->outdma); ++ ep->outdma = -1; ++ } ++ } ++ free_irq (AU1000_USB_DEV_REQ_INT, NULL); ++ free_irq (AU1000_USB_DEV_SUS_INT, NULL); ++#if defined(MAX_INTR_LOOP_STATS) ++ { ++ u32 lc; ++ for (lc = 0; lc <= MAX_INTR_LOOP_STATS; lc++) ++ if (interrupt_loop_stats[lc]) ++ printk(KERN_ERR "%s: interrupt loopcount[%02u] %9u\n", __FUNCTION__, lc,interrupt_loop_stats[lc]); ++ printk(KERN_INFO"%s: halt_dma_expired: %d\n", __FUNCTION__, au_halt_dma_expired); ++ } ++#endif ++#ifdef RECORD_LATENCY ++ { ++ int i; ++ for (i = 0; i < CP0_COUNTS; i++) ++ if (cp0_counts[i]) ++ printk(KERN_INFO"%s: cp0_counts[%d] %d\n", __FUNCTION__, i, cp0_counts[i]); ++ } ++#endif ++} ++ ++/* ********************************************************************************************* */ ++struct usbd_pcd_ops usbd_pcd_ops = { ++ max_endpoints: UDC_MAX_ENDPOINTS, ++ ep0_packetsize: EP0_PACKETSIZE, ++ name: UDC_NAME, ++ start: au_start, ++ stop: au_stop, ++ start_endpoint_in: au_start_endpoint_in, ++ start_endpoint_out: au_start_endpoint_out, ++ request_endpoints: au_request_endpoints, ++ set_endpoints: au_set_endpoints, ++ cancel_in_irq: au_cancel_in_irq, ++ cancel_out_irq: au_cancel_out_irq, ++ serial_init: au_serial_init, ++ halt_endpoint: au_pcd_halt_endpoint, ++ endpoint_halted: au_pcd_endpoint_halted, ++}; ++ ++struct pcd_ops pcd_ops = { ++ pcd_en_func: pcd_en_func, ++ mod_init: au_mod_init, ++ mod_exit: au_mod_exit, ++ framenum: au_framenum, ++}; ++ ++struct ocd_ops ocd_ops = { ++ #if defined(CONFIG_OTG_TR_AUTO) ++ capabilities: OCD_CAPABILITIES_TR | OCD_CAPABILITIES_AUTO, ++ #else ++ capabilities: OCD_CAPABILITIES_TR, ++ #endif ++ ticks: au_ticks, ++ elapsed: au_elapsed, ++}; ++ ++struct tcd_ops tcd_ops = { ++ dp_pullup_func: au_loc_conn, ++}; ++ +diff -uNr linux/drivers/no-otg/ocd/au1x00/au1x00.c linux/drivers/otg/ocd/au1x00/au1x00.c +--- linux/drivers/no-otg/ocd/au1x00/au1x00.c 1970-01-01 01:00:00.000000000 +0100 ++++ linux/drivers/otg/ocd/au1x00/au1x00.c 2006-09-01 21:41:31.000000000 +0200 +@@ -0,0 +1,1293 @@ ++/* ++ * otg/ocd/au1x00/au1x00.c -- USB Device Controller driver. ++ * ++ * Copyright (c) 2004 Belcarra ++ * ++ * By: ++ * Stuart Lynne <sl@belcarra.com>, ++ * Tom Rushworth <tbr@belcarra.com>, ++ * ++ */ ++/*! ++ * @file otg/ocd/au1x00/au1x00.c ++ * @brief AU1X00 Header ++ * ++ * This file contains the private defines and structures for the AU1X00 ++ * Drivers. ++ * ++ * @ingroup AU1X00 ++ */ ++ ++ ++#include <otg/pcd-include.h> ++#include "au1x00.h" ++ ++#include <asm/io.h> ++#include <asm/au1000.h> ++#include <asm/au1000_dma.h> ++ ++#if defined(CONFIG_SOC_AU1550) ++#define DBDMA ++#endif ++#ifdef DBDMA ++#warning "Using AU1XXX_DBMDA" ++#include <asm/au1xxx_dbdma.h> ++#else /* DBDMA */ ++#warning "Using AU1X00_MDA" ++#endif /* DBDMA */ ++ ++#include <asm/mipsregs.h> ++ ++ ++#ifdef CONFIG_MIPS_FREEHAND ++#include <linux/i2c.h> ++#include <linux/sensors.h> /* for reading serial number */ ++#endif ++ ++ ++#undef CHECK_LATENCY ++#undef RECORD_LATENCY ++#undef MAX_INTR_LOOP_STATS //10 ++#if defined(MAX_INTR_LOOP_STATS) ++static u32 interrupt_loop_stats[MAX_INTR_LOOP_STATS+1]; ++#endif ++#ifdef RECORD_LATENCY ++#define CP0_COUNTS 50 ++u32 cp0_counts[CP0_COUNTS]; ++u32 cp0_record; ++#endif ++u32 cp0_count; ++unsigned int udc_saw_bus_activity; ++int au_halt_dma_expired; ++u32 au1x00_new_silicon; ++u32 au1x00_inten; ++ ++u8 au1x00_config_bulk[25] = { ++ 0x04, (USB_ENDPOINT_CONTROL << 4) | (EP0_PACKETSIZE & 0x380) >> 7, // 1 ++ (EP0_PACKETSIZE & 0x7F) << 1, 0x00, 0x00, ++ 0x24, (USB_ENDPOINT_BULK << 4) | USB_DIR_IN | (MAX_EPN_PACKET_SIZE & 0x380) >> 7, // 6 ++ (MAX_EPN_PACKET_SIZE & 0x7F) << 1, 0x00, 0x02, ++ 0x34, (USB_ENDPOINT_BULK << 4) | USB_DIR_IN | (MAX_EPN_PACKET_SIZE & 0x380) >> 7, // 11 ++ (MAX_EPN_PACKET_SIZE & 0x7F) << 1, 0x00, 0x03, ++ 0x44, (USB_ENDPOINT_BULK << 4) | USB_DIR_OUT | (MAX_EPN_PACKET_SIZE & 0x380) >> 7, // 16 ++ (MAX_EPN_PACKET_SIZE & 0x7F) << 1, 0x00, 0x04, ++ 0x54, (USB_ENDPOINT_BULK << 4) | USB_DIR_OUT | (MAX_EPN_PACKET_SIZE & 0x380) >> 7, // 21 ++ (MAX_EPN_PACKET_SIZE & 0x7F) << 1, 0x00, 0x05, ++}; ++ ++/* ********************************************************************************************* */ ++/* Note - The endpoints names are confusing. To simplify mapping of the interrupt request lines ++ * we use a numbering of the physical endpoints of 0-5, which also matches the fifo numbering ++ * (see the config block). ++ * ++ * Physical FIFO Name Direction Logical ++ * 0 0 EP0 OUT 0 ++ * 1 1 EP0 IN 0 ++ * 2 2 EP1 IN 81 ++ * 3 3 EP2 IN 82 ++ * 4 4 EP3 OUT 3 ++ * 5 5 EP4 OUT 4 ++ * ++ * The ep_regs array maps a physical endpoint number to the registers required to access the udc ++ * for that endpoint. Note that ep0 is always accessed via 0. The epl2p array maps the logical ++ * addresses back to the physical number. The epp2l array maps the physical number to the ++ * logical endpoint address ++ */ ++#ifdef DBDMA ++struct ep_regs ep_regs[6] = { ++ ++ { rd: USBD_EP0RD, rds: USBD_EP0RDSTAT, /*rx_id: DMA_ID_USBDEV_EP0_RX,*/ cs: USBD_EP0CS, rx_str: "EP0 OUT RD", ++ wr: USBD_EP0WR, wrs: USBD_EP0WRSTAT, tx_id: DSCR_CMD0_USBDEV_TX0, tx_str: "EP0 IN WR",}, ++ ++ { rds: 0, wrs: 0, indma: -1, outdma: -1, }, ++ { wr: NUSBD_EP1WR, wrs: NUSBD_EP1WRSTAT, tx_id: DSCR_CMD0_USBDEV_TX1, cs: NUSBD_EP1CS, tx_str: "EP1 IN WR",}, ++ { wr: NUSBD_EP2WR, wrs: NUSBD_EP2WRSTAT, tx_id: DSCR_CMD0_USBDEV_TX2, cs: NUSBD_EP2CS, tx_str: "EP2 IN WR",}, ++ ++ { rd: NUSBD_EP3RD, rds: NUSBD_EP3RDSTAT, rx_id: DSCR_CMD0_USBDEV_RX3, cs: NUSBD_EP3CS, rx_str: "EP3 OUT RD",}, ++ { rd: NUSBD_EP4RD, rds: NUSBD_EP4RDSTAT, rx_id: DSCR_CMD0_USBDEV_RX4, cs: NUSBD_EP4CS, rx_str: "EP4 OUT RD",}, ++}; ++#else /*DBMA */ ++struct ep_regs ep_regs[6] = { ++ { rd: USBD_EP0RD, rds: USBD_EP0RDSTAT, /*rx_id: DMA_ID_USBDEV_EP0_RX,*/ cs: USBD_EP0CS, rx_str: "EP0 OUT RD", ++ wr: USBD_EP0WR, wrs: USBD_EP0WRSTAT, tx_id: DMA_ID_USBDEV_EP0_TX, tx_str: "EP0 IN WR",}, ++ { rds: 0, wrs: 0, indma: -1, outdma: -1, }, ++ { wr: NUSBD_EP1WR, wrs: NUSBD_EP1WRSTAT, tx_id: NDMA_ID_USBDEV_EP1_TX, cs: NUSBD_EP1CS, tx_str: "EP1 IN WR",}, ++ { wr: NUSBD_EP2WR, wrs: NUSBD_EP2WRSTAT, tx_id: NDMA_ID_USBDEV_EP2_TX, cs: NUSBD_EP2CS, tx_str: "EP2 IN WR",}, ++ ++ { rd: NUSBD_EP3RD, rds: NUSBD_EP3RDSTAT, rx_id: NDMA_ID_USBDEV_EP3_RX, cs: NUSBD_EP3CS, rx_str: "EP3 OUT RD",}, ++ { rd: NUSBD_EP4RD, rds: NUSBD_EP4RDSTAT, rx_id: NDMA_ID_USBDEV_EP4_RX, cs: NUSBD_EP4CS, rx_str: "EP4 OUT RD",}, ++}; ++#endif /*DBMA */ ++u8 epl2p[6] = { 0x00, 0x02, 0x03, 0x04, 0x05, 0x00, }; // map logical to physical ++u8 epp2l[6] = { 0x00, 0x00, 0x81, 0x82, 0x03, 0x04, }; // map physical to logical ++ ++static __inline__ void au_fifo_read(struct ep_regs *ep, unsigned char * cp, int bytes) ++{ ++ u32 rd = ep->rd; ++ for (; bytes--; *cp++ = au_readl(rd)); ++} ++ ++static __inline__ void au_fifo_write(int ep, unsigned char * cp, int bytes) ++{ ++ u32 wr = ep_regs[ep].wr; ++ for (; bytes--; au_writel(*cp++, wr)); ++ ep_regs[ep].last = bytes; ++} ++ ++void __inline__ au_inten(u32 inten) ++{ ++ au1x00_inten = inten; ++ au_writel(au1x00_inten, USBD_INTEN); ++} ++ ++void __inline__ au_epn_interrupt_enable(int epn) ++{ ++ au_inten(au1x00_inten | (1 << epn)); ++} ++ ++void __inline__ au_epn_interrupt_disable(int epn) ++{ ++ au_inten(au1x00_inten & ~(1 << epn)); ++ au_writel((1 << epn) , USBD_INTSTAT); ++} ++ ++static void __inline__ send_zlp(unsigned char epn) ++{ ++ au_writel(USBDEV_FSTAT_FLUSH | USBDEV_FSTAT_UF | USBDEV_FSTAT_OF, ep_regs[epn].wrs); ++ au_writel(0 << 1, ep_regs[epn].cs); ++ au_writel(0, ep_regs[epn].wr); ++} ++/* ********************************************************************************************* */ ++#define AU_DMA_HALT_POLL 0x1000 ++#ifdef DBDMA ++static void __inline__ au_halt_dma(int chan) ++{ ++ au1xxx_dbdma_stop(chan); ++ au_halt_dma_expired++; ++} ++ ++static int __inline__ au_get_dma_residue(int chan) ++{ ++ return au1xxx_get_dma_residue(chan); ++} ++ ++static void __inline__ au_start_dma_in(int indma, __u8 *bp, int len) ++{ ++ //printk("\n----------------------STARTING DMA IN dma:%X buffer:%p len:%d---------------\n", indma, bp, len); ++ // au1xxx_dbdma_stop(indma); ++ au1xxx_dbdma_put_source(indma, bp, len); ++ au1xxx_dbdma_start(indma); ++} ++ ++static void __inline__ au_start_dma_out(unsigned int outdma, __u8 *bp, int packetSize) ++{ ++ //printk("\n----------------------STARTING DMA OUT dma:%X buffer:%p len:%d---------------\n", outdma, bp, packetSize); ++ au1xxx_dbdma_put_dest(outdma, bp, packetSize); ++ au1xxx_dbdma_start(outdma); ++} ++ ++static u32 __inline__ au_get_dma_chan(u32 dma) ++{ ++ return dma; ++} ++#else /* DBDMA */ ++static void __inline__ au_halt_dma(struct dma_chan *chan) ++{ ++ int i; ++ au_writel(DMA_GO, chan->io + DMA_MODE_CLEAR); ++ for (i = 0; i < AU_DMA_HALT_POLL; i++) ++ RETURN_IF (au_readl(chan->io + DMA_MODE_READ) & DMA_HALT); ++ au_halt_dma_expired++; ++} ++ ++static int __inline__ au_get_dma_residue(struct dma_chan *chan) ++{ ++ int curBufCntReg = (au_readl(chan->io + DMA_MODE_READ) & DMA_AB) ? DMA_BUFFER1_COUNT : DMA_BUFFER0_COUNT; ++ return au_readl(chan->io + curBufCntReg) & DMA_COUNT_MASK; ++} ++ ++static void __inline__ au_start_dma(struct dma_chan *chan, u8 *bp, int len) ++{ ++ if (au_readl(chan->io + DMA_MODE_READ) & DMA_AB) { ++ au_writel(DMA_D1, chan->io + DMA_MODE_CLEAR); ++ au_writel(len & DMA_COUNT_MASK, chan->io + DMA_BUFFER1_COUNT); ++ au_writel(0, chan->io + DMA_BUFFER0_COUNT); ++ au_writel(virt_to_phys(bp), chan->io + DMA_BUFFER1_START); ++ au_writel(DMA_BE1, chan->io + DMA_MODE_SET); ++ } ++ else { ++ au_writel(DMA_D0, chan->io + DMA_MODE_CLEAR); ++ au_writel(len & DMA_COUNT_MASK, chan->io + DMA_BUFFER0_COUNT); ++ au_writel(0, chan->io + DMA_BUFFER1_COUNT); ++ au_writel(virt_to_phys(bp), chan->io + DMA_BUFFER0_START); ++ au_writel(DMA_BE0, chan->io + DMA_MODE_SET); ++ } ++ au_writel(DMA_GO, chan->io + DMA_MODE_SET); ++} ++ ++static void __inline__ au_start_dma_in(int indma, u8 *bp, int len) ++{ ++ au_start_dma(get_dma_chan(indma), bp, len); ++} ++ ++static void __inline__ au_start_dma_out(int outdma, u8 *bp, int wMaxPacketSize) ++{ ++ dma_cache_inv((unsigned long) bp, wMaxPacketSize); ++ au_start_dma(get_dma_chan(outdma), bp, wMaxPacketSize); ++} ++ ++static __inline__ struct dma_chan * au_get_dma_chan(u32 dma) ++{ ++ return get_dma_chan(dma); ++} ++#endif /* DBDMA */ ++static struct usbd_urb *au_rcv_complete_irq(struct usbd_endpoint_instance *endpoint, int len, int urb_bad) ++{ ++ return pcd_rcv_complete_irq(endpoint, len, urb_bad); ++} ++/* ********************************************************************************************* */ ++/* au_start_in_ep0 - start transmit ++ */ ++static void au_start_in_ep0 (struct usbd_endpoint_instance *endpoint, struct ep_regs *ep) ++{ ++ struct usbd_urb *urb = endpoint->tx_urb; ++ int last = ep->last = endpoint->last = MIN (urb->actual_length - endpoint->sent, endpoint->wMaxPacketSize); ++ TRACE_MSG2(PCD, "START IN EP0 SENT: %d SENDING: %d", endpoint->sent, last); ++ RETURN_IF ((urb->actual_length - endpoint->sent) <= 0); ++ au_writel(last << 1, ep->cs); // XXX ++ au_fifo_write(0, urb->buffer + endpoint->sent, last); ++ endpoint->last = last; ++} ++ ++/* au_in_ep0 - called to service an endpoint zero IN interrupt, data sent ++ */ ++static void au_in_ep0(struct usbd_endpoint_instance *endpoint, struct ep_regs *ep) ++{ ++ u32 cs; ++ struct usbd_urb *tx_urb; ++ int last; ++ TRACE_MSG1(PCD, "EP0 IN: tx_urb: %p", (int)endpoint->tx_urb); ++ if ((cs = au_readl(ep->cs)) & USBDEV_CS_STALL) { // clear stall if present ++ TRACE_MSG1(PCD, "CLEAR STALL %d", 0); ++ cs &= ~USBDEV_CS_STALL; ++ au_writel(cs, ep->cs); ++ return; ++ } ++ if (!(tx_urb = pcd_tx_complete_irq(endpoint, 0))) { // wait for setup if no more data ++ endpoint->state = WAIT_FOR_SETUP; ++ return; ++ } ++ TRACE_MSG4(PCD, "EP0 IN actual: %d last: %d sent: %d flags: %x", endpoint->tx_urb->actual_length, ++ endpoint->last, endpoint->sent, endpoint->tx_urb->flags); ++ if (pcd_tx_sendzlp(endpoint)) { // check if tx_urb we have is finished ++ TRACE_MSG0(PCD, "EP0 IN BULK - sending ZLP"); ++ tx_urb->flags &= ~USBD_URB_SENDZLP; ++ send_zlp(0); ++ pcd_tx_complete_irq(endpoint, 0); ++ return; ++ } ++ if (tx_urb->actual_length > endpoint->sent) { ++ if ((tx_urb->actual_length - endpoint->sent) < endpoint->wMaxPacketSize) ++ TRACE_MSG1(PCD, "EP0 IN starting short packet %d", tx_urb->actual_length - endpoint->sent); ++ else ++ TRACE_MSG1(PCD, "EP0 IN LEFT TO SEND %d", tx_urb->actual_length - endpoint->sent); ++ au_start_in_ep0(endpoint, ep); ++ } ++} ++ ++/* au_out_ep0 - called to service an endpoint zero OUT interrupt, data received ++ */ ++static void au_out_ep0(struct usbd_endpoint_instance *endpoint, struct ep_regs *ep) ++{ ++ struct usbd_device_request request; ++ int i; ++ u32 cs; ++ u32 bytes; ++ TRACE_MSG0(PCD, "EP0 OUT"); ++ cs = au_readl(ep->cs); // check if host aborted transfer and flush the write fifo ++ bytes = au_readl(ep->rds) & USBDEV_FSTAT_FCNT_MASK; ++ if (endpoint->state == DATA_STATE_RECV) { ++ struct usbd_urb *rcv_urb = pcd_rcv_next_irq(endpoint); ++ TRACE_MSG1(PCD, "EP0 OUT: RECV: rcv_urb: %x", (int) rcv_urb); ++ if (rcv_urb) { ++ au_fifo_read(ep, rcv_urb->buffer + rcv_urb->actual_length, bytes); ++ if (au_rcv_complete_irq(endpoint, bytes, 0)) ++ return; ++ au_writel(USBDEV_FSTAT_FLUSH | USBDEV_FSTAT_UF | USBDEV_FSTAT_OF, ep->rds); ++ endpoint->state = WAIT_FOR_SETUP; ++ send_zlp(0); ++ return; ++ } ++ endpoint->state = WAIT_FOR_SETUP; ++ } ++ pcd_tx_cancelled_irq(endpoint); ++ pcd_rcv_cancelled_irq(endpoint); ++ au_fifo_read(ep, (u8 *)&request, bytes); ++ au_writel(USBDEV_FSTAT_FLUSH | USBDEV_FSTAT_UF | USBDEV_FSTAT_OF, ep->wrs); ++ if (bytes != 8) { ++ TRACE_MSG1(PCD, "ERROR SETUP SET not eight bytes: %d", bytes); ++ au_writel(USBDEV_FSTAT_FLUSH | USBDEV_FSTAT_UF | USBDEV_FSTAT_OF, ep->wrs); ++ return; ++ } ++ TRACE_MSG4(PCD, "SETUP bmRequestType: %02x bRequest %02x state: %d status: %d", request.bmRequestType, ++ request.bRequest, pcd_instance->bus->device_state, pcd_instance->bus->status); ++ switch (request.bRequest) { // we need to simply ignore any of these ++ case USB_REQ_SET_ADDRESS: // Fake a bus reset IFF not state default and then process normally ++ BREAK_IF (pcd_instance->bus->device_state == STATE_DEFAULT); ++ udc_saw_bus_activity = 0; ++ usbd_bus_event_handler_irq (pcd_instance->bus, DEVICE_RESET, 0); ++ usbd_bus_event_handler_irq (pcd_instance->bus, DEVICE_ADDRESS_ASSIGNED, 0); ++ break; ++ case USB_REQ_GET_DESCRIPTOR: // Fake a bus reset IFF suspended and then process normally ++ BREAK_IF (STATE_SUSPENDED != pcd_instance->bus->device_state); ++ udc_saw_bus_activity = 0; ++ usbd_bus_event_handler_irq (pcd_instance->bus, DEVICE_RESET, 0); ++ usbd_bus_event_handler_irq (pcd_instance->bus, DEVICE_ADDRESS_ASSIGNED, 0); ++ break; ++ } ++ if (pcd_recv_setup_irq(pcd_instance, &request)) { ++ TRACE_MSG1(PCD, "ep0 STALL %d", cs); ++ au_writel(USBDEV_CS_STALL, USBD_EP0CS); ++ return; ++ } ++ if (((request.bmRequestType & USB_REQ_DIRECTION_MASK) == USB_REQ_HOST2DEVICE) && le16_to_cpu (request.wLength)) { ++ TRACE_MSG1(PCD, "ep0 Class H2D request %04x", le16_to_cpu(request.wLength)); ++ endpoint->state = DATA_STATE_RECV; ++ return; ++ } ++ if ((request.bmRequestType & USB_REQ_DIRECTION_MASK) == USB_REQ_HOST2DEVICE) { ++ TRACE_MSG1(PCD, "ep0 Class H2D request %04x", le16_to_cpu(request.wLength)); ++ if ((request.bmRequestType & ~USB_REQ_DIRECTION_MASK)) { ++ TRACE_MSG1(PCD, "ep0 Class or Vendor, send ZLP %d", cs); ++ send_zlp(0); ++ return; ++ } ++ } ++ TRACE_MSG1(PCD, "ep0 Class D2H request %04x", le16_to_cpu(request.wLength)); ++} ++/* ********************************************************************************************* */ ++/* au_start_in_bulk - start transmit ++ * The au1x00 will start to send when the first byte is loaded into the FIFO, either by ++ * DMA or PIO. The packetsize must be set first. ++ */ ++static void au_start_in_bulk (unsigned int epn, struct usbd_endpoint_instance *endpoint, struct ep_regs *ep) ++{ ++ struct usbd_urb *urb = endpoint->tx_urb; ++ unsigned char *bp = urb->buffer + endpoint->sent; ++ int last; ++ TRACE_MSG1(PCD, "START IN BULK %d", epn); ++ RETURN_IF (!urb || (( (urb->actual_length - endpoint->sent) == 0) && !(urb->flags & USBD_URB_SENDZLP))); ++ last = ep->last = endpoint->last = MIN (urb->actual_length - endpoint->sent, endpoint->wMaxPacketSize); ++ TRACE_MSG2(PCD, "START IN BULK sent: %d last:%d", endpoint->sent, last); ++ au_writel(USBDEV_FSTAT_FLUSH | USBDEV_FSTAT_UF | USBDEV_FSTAT_OF, ep->wrs); // XXX ++ if (!last) { ++ if (endpoint->tx_urb->flags & USBD_URB_SENDZLP) { ++ TRACE_MSG0(PCD, "START IN BULK - zending ZLP"); ++ send_zlp(epn); ++ endpoint->tx_urb->flags &= ~USBD_URB_SENDZLP; ++ } ++ return; ++ } ++ else if (au1x00_new_silicon && (8 >= last)) { ++ au_writel(last << 1, ep->cs); ++ au_fifo_write(epn, bp, last); ++ return; ++ } ++ if (!au1x00_new_silicon) ++ au_epn_interrupt_disable(epn); ++ dma_cache_wback_inv((unsigned long) bp, last); ++ au_writel(last << 1, ep->cs); ++ au_start_dma_in(ep->indma, bp, last); ++} ++ ++static void au_in_bulk(unsigned int epn, struct ep_regs *ep, struct usbd_endpoint_instance *endpoint) ++{ ++ struct usbd_urb *tx_urb; ++ int rc = 0; ++ u32 cs = au_readl(ep->cs); ++ u32 wrs = au_readl(ep->wrs); ++ TRACE_MSG2(PCD, "BULK IN EPN - cs: %x wrs: %x", cs, wrs); ++ if (!au1x00_new_silicon) ++ if (epn && (ep->last > 8)) { ++ TRACE_MSG2(PCD, "BULK IN EPN - DMA ACTIVE epn %d last %d", epn, ep->last); ++ au_epn_interrupt_disable(epn); ++ return; ++ } ++ if (wrs) { // check for underflow or overflow ++ rc = 1; ++ if (wrs & USBDEV_FSTAT_UF) { ++ TRACE_MSG2(PCD, "BULK IN EPN - UF epn %d wrs: %x", epn, wrs); ++ rc = 1; // set rc to indicate an error ++ } ++ if (wrs & USBDEV_FSTAT_OF) ++ TRACE_MSG2(PCD, "BULK IN EPN - OF epn %d wrs: %x", epn, wrs); ++ au_writel(USBDEV_FSTAT_FLUSH | USBDEV_FSTAT_UF | USBDEV_FSTAT_OF, ep->wrs); // flush the fifo ++ } ++ if (cs & USBDEV_CS_NAK) { ++ RETURN_IF (ep->last && ((wrs&0x1f) == ep->last)); ++ rc = 1; ++ } ++ if (cs & USBDEV_CS_STALL) { // clear stall if present ++ TRACE_MSG1(PCD, "BULK IN EPN - CLEAR STALL %d", epn); ++ cs &= ~USBDEV_CS_STALL; ++ au_writel(cs, ep->cs); ++ return; ++ } ++ TRACE_MSG4(PCD, "BULK IN EPN epn: %d rc: %d last: %d sent: %d", epn, rc, endpoint->last, endpoint->sent); ++ if ((tx_urb = pcd_tx_complete_irq(endpoint, rc))) { ++ if ((tx_urb->actual_length > endpoint->sent) || (endpoint->tx_urb->flags & USBD_URB_SENDZLP)) { ++ au_start_in_bulk(epl2p[endpoint->bEndpointAddress&0xf], endpoint, ep); ++ /* XXX magic delay - without this large packets will eventually stall the transmit ++ * XXX and all traffic in both directions will stop. ++ */ ++ if (!au1x00_new_silicon) ++ udelay(8); ++ TRACE_MSG1(PCD, "BULK IN EPN - LEFT TO SEND %d", tx_urb->actual_length - endpoint->sent); ++ return; ++ } ++ } ++ au_epn_interrupt_disable(epn); // disable interrupts ++} ++ ++/* au_start_in_iso - start transmit ++ * The au1x00 will start to send when the first byte is loaded into the FIFO, either by DMA or ++ * PIO. The packetsize must be set first. ++ */ ++static void au_start_in_iso (unsigned int epn, struct usbd_endpoint_instance *endpoint, struct ep_regs *ep) ++{ ++ struct usbd_urb *urb = endpoint->tx_urb; ++ unsigned char *bp = urb->buffer + endpoint->sent; ++ int last; ++ TRACE_MSG2(PCD, "START IN ISO actual: %d sent: %d", urb->actual_length, endpoint->sent); ++ RETURN_IF ((urb->actual_length - endpoint->sent) == 0); ++ last = ep->last = endpoint->last = MIN (urb->actual_length - endpoint->sent, endpoint->wMaxPacketSize); ++ TRACE_MSG2(PCD, "START IN ISO last: %d packetSize: %d", last, endpoint->wMaxPacketSize); ++ au_writel(USBDEV_FSTAT_FLUSH | USBDEV_FSTAT_UF | USBDEV_FSTAT_OF, ep->wrs); // XXX ++ au1x00_new_silicon ? au_epn_interrupt_enable : au_epn_interrupt_disable (epn); ++ dma_cache_wback_inv((unsigned long) bp, last); ++ au_writel(last << 1, ep->cs); ++ au_start_dma_in(ep->indma, bp, last); ++} ++ ++static void au_in_iso(unsigned int epn, struct ep_regs *ep, struct usbd_endpoint_instance *endpoint) ++{ ++ struct usbd_urb *tx_urb = pcd_tx_complete_irq(endpoint, 0); ++ u32 cs = au_readl(ep->cs); ++ u32 wrs = au_readl(ep->wrs); ++ TRACE_MSG2(PCD, "ISO IN EPN - cs: %x wrs: %x", cs, wrs); ++ if (!au1x00_new_silicon) ++ if (epn && (ep->last > 8)) { ++ au_epn_interrupt_disable(epn); ++ return; ++ } ++ TRACE_MSG4(PCD, "ISO IN EPN epn: %d last: %d sent: %d", epn, endpoint->last, endpoint->sent, 0); ++ ep->last = 0; ++ if ((tx_urb = endpoint->tx_urb) && (tx_urb->actual_length > endpoint->sent)) { ++ au_start_in_iso(epl2p[endpoint->bEndpointAddress&0xf], endpoint, ep); ++ /* XXX magic delay - without this large packets will eventually stall the transmit ++ * XXX and all traffic in both directions will stop. ++ */ ++ if (!au1x00_new_silicon) ++ udelay(8); ++ TRACE_MSG1(PCD, "ISO IN EPN - LEFT TO SEND %d", tx_urb->actual_length - endpoint->sent); ++ return; ++ } ++ else ++ TRACE_MSG0(PCD, "ISO IN EPN - nothing to send"); ++ au_epn_interrupt_disable(epn); // disable interrupts ++} ++/* ********************************************************************************************* */ ++ ++static void au_start_out_bulk(unsigned int epn, struct usbd_endpoint_instance *endpoint, struct ep_regs *ep) ++{ ++ TRACE_MSG2(PCD, "START OUT BULK %d %d", epn, endpoint->wMaxPacketSize); ++ if (!endpoint->rcv_urb) { ++ TRACE_MSG0(PCD, "START OUT BULK DISABLE"); ++ au_epn_interrupt_disable(epn); ++ return; ++ } ++ if (endpoint->rcv_error) { ++ TRACE_MSG0(PCD, "START OUT BULK reseting rcv_error"); ++ endpoint->rcv_error = 0; ++ } ++ au_start_dma_out(ep->outdma, endpoint->rcv_urb->buffer + endpoint->rcv_urb->actual_length, endpoint->wMaxPacketSize); ++} ++ ++static void au_out_bulk(unsigned int epn, struct ep_regs *ep, struct usbd_endpoint_instance *endpoint) ++{ ++ int bytes = 0; ++ struct usbd_urb *urb; ++ struct usbd_urb *completed_urb = NULL; ++ u32 cs; ++ u32 rds; ++ u32 nrds; ++ au_halt_dma(au_get_dma_chan(ep->outdma)); ++ cs = au_readl(ep->cs); ++ rds = au_readl(ep->rds); ++ TRACE_MSG2(PCD, "BULK OUT CS: %04x RD: %04x", cs, rds); ++ bytes = endpoint->wMaxPacketSize - au_get_dma_residue(au_get_dma_chan(ep->outdma)); ++ if (!(urb = pcd_rcv_next_irq(endpoint))) { ++ TRACE_MSG0(PCD, "BULK OUT EPN - no rcv_urb"); ++ au_epn_interrupt_disable(epn); ++ return; ++ } ++ /* The original AU1X00 UDC design will continue to receive data as long as there is room ++ * in the FIFO. We cannot tell when we are at the end of a packet and/or have the start ++ * of a new one. ++ * ++ * There are only two scenarios that are guaranteed (almost) to be correct: ++ * ++ * 64 bytes of data from DMA, empty fifo, continue Bulk OUT < 60 bytes of data and ++ * < 4 bytes in fifo, end Bulk OUT. ++ * ++ * There may be a third scenario that is ok: ++ * ++ * 0 bytes dma, 0 bytes in fifo, NAK ++ * ++ * Everything else is an error. In all cases we assume that it is safer to drop data ++ * than to accept it in error. This allows CRC or size protected encapsulations to ++ * notice bulk transfers received with errors. ++ * ++ * In general none of the policies or strategies are able to cope with all errors ++ * without missing errors and dropping good data. The intent is to minimize the amount ++ * of potentially bad data getting to the function driver while minimizing the amount of ++ * good data that is dropped. ++ * ++ * The new silicon mitigates this problem for non control endpoints because it will NAK ++ * additional data until the interrupt service flag is reset. ++ * ++ * Start with generic error tests, OF, UF or NAK indicate an error we cannot recover ++ * from, start flushing until end of current bulk transfer (wait for a short packet) ++ * ++ */ ++ if (rds & (USBDEV_FSTAT_OF | USBDEV_FSTAT_UF)) { ++ TRACE_MSG2(PCD, "BULK OUT FLUSHING %d length: %d", bytes, urb->actual_length); ++ THROW(start_flushing); ++ } ++ rds = rds & USBDEV_FSTAT_FCNT_MASK; ++ nrds = au_readl(ep->rds); ++ if (64 == bytes) { ++ TRACE_MSG2(PCD, "BULK OUT 64 BYTES %d length: %d", bytes, urb->actual_length); ++ /* full size packet received, check that we are not flushing and that the FIFO ++ * does not have any data. If there is data in the FIFO we may not be able to ++ * restart DMA in time, so start flushing ++ */ ++ if (endpoint->rcv_error) { ++ TRACE_MSG4(PCD, "FULL PACKET bytes: %d rds: %d nrds: %d cp0: %d CONTINUE FLUSHING", ++ bytes, rds, nrds, cp0_count); ++ THROW(start_flushing); ++ } ++ if ((nrds > 6) && (nrds < 8) ) { ++ TRACE_MSG4(PCD, "FIFO not empty bytes: %d rds: %d nrds: %d cp0: %d START FLUSHING nrds > 6 < 8", ++ bytes, rds, nrds, cp0_count); ++ THROW(start_flushing); ++ } ++ if (!urb->actual_length) ++ TRACE_MSG4(PCD, "PACKET ok bytes: %d rds: %d nrds: %d cp0: %d ACCEPTING 64 bytes", ++ bytes, rds, nrds, cp0_count); ++ au_writel(USBDEV_FSTAT_FLUSH | USBDEV_FSTAT_UF | USBDEV_FSTAT_OF, ep->rds); ++ au_rcv_complete_irq(endpoint, 64, 0); ++ if (cs & USBDEV_CS_NAK) ++ if (nrds && (nrds < 8) ) { ++ TRACE_MSG4(PCD, "NAK bytes: %d rds: %d nrds: %d cp0: %d START FLUSHING CS_NAK", ++ bytes, rds, nrds, cp0_count); ++ THROW(start_flushing); ++ } ++ } ++ else if ((cs & USBDEV_CS_NAK) && (!nrds || (nrds == 8)) ) { ++ TRACE_MSG2(PCD, "BULK OUT NAK BYTES %d length: %d", bytes, urb->actual_length); ++ /* a nak'd packet may be ok to ignore IFF the FIFO is empty(?) or completely full. ++ */ ++ if (endpoint->rcv_error) { ++ TRACE_MSG4(PCD, "NAK bytes: %d rds: %d nrds: %d cp0: %d CONTINUE FLUSHING", ++ bytes, rds, nrds, cp0_count); ++ THROW(start_flushing); ++ } ++ au_writel(USBDEV_FSTAT_FLUSH | USBDEV_FSTAT_UF | USBDEV_FSTAT_OF, ep->rds); ++ } ++ else { ++ TRACE_MSG2(PCD, "BULK OUT < 64 BYTES %d length: %d", bytes, urb->actual_length); ++ /* short packet by DMA, additional data for this packet in FIFO, more than 3 ++ * bytes is probably an error. ++ */ ++ if ((cs & USBDEV_CS_NAK) && nrds && (nrds < 8) ) { ++ TRACE_MSG4(PCD, "BULK OUT NAK bytes: %d rds: %d nrds: %d cp0: %d START FLUSHING", ++ bytes, rds, nrds, cp0_count); ++ THROW(start_flushing); ++ } ++ if (nrds > 4) { ++ TRACE_MSG4(PCD, "BULK OUT SHORT PACKET by DMA full FIFO bytes: %d rds: %d nrds: %d cp0: %d START FLUSHING", ++ bytes, rds, nrds, cp0_count); ++ THROW(start_flushing); ++ } ++ TRACE_MSG4(PCD, "BULK OUT SHORT bytes: %d rds: %d nrds: %d cp0: %d reading fifo", bytes, rds, nrds, cp0_count); ++ au_fifo_read(ep, urb->buffer + urb->actual_length + bytes, nrds); ++ bytes += nrds; ++ TRACE_MSG1(PCD, "BULK OUT < 64 BYTES %d", bytes); ++ au_writel(USBDEV_FSTAT_FLUSH | USBDEV_FSTAT_UF | USBDEV_FSTAT_OF, ep->rds); ++ if (!endpoint->rcv_error) { ++ TRACE_MSG0(PCD, "BULK OUT COMPLETED URB"); ++ au_rcv_complete_irq(endpoint, bytes, 0); ++ } ++ else { ++ TRACE_MSG0(PCD, "BULK OUT - FLUSHING URB - reseting rcv_error"); ++ endpoint->rcv_error = 0; ++ } ++ } ++ CATCH(start_flushing) { ++ TRACE_MSG0(PCD, "BULK OUT - START FLUSHING URB"); ++ endpoint->rcv_error = 1; ++ endpoint->rcv_urb->actual_length = 0; ++ au_writel(USBDEV_FSTAT_FLUSH | USBDEV_FSTAT_UF | USBDEV_FSTAT_OF, ep->rds); ++ bytes = 0; ++ } ++ TRACE_MSG0(PCD, "BULK OUT - RESTARTING"); ++ au_start_out_bulk(epn, endpoint, ep); ++} ++ ++static void au_start_out_iso(unsigned int epn, struct usbd_endpoint_instance *endpoint, struct ep_regs *ep) ++{ ++ int outdma = ep->outdma; ++ RETURN_IF(!endpoint->rcv_urb); ++ au_start_dma_out(outdma, endpoint->rcv_urb->buffer + endpoint->rcv_urb->actual_length, endpoint->wMaxPacketSize); ++} ++ ++static void au_out_iso(unsigned int epn, struct ep_regs *ep, struct usbd_endpoint_instance *endpoint) ++{ ++ int bytes = 0; ++ struct usbd_urb *urb; ++ u32 cs; ++ u32 rds; ++ au_halt_dma(au_get_dma_chan(ep->outdma)); ++ cs = au_readl(ep->cs); ++ rds = au_readl(ep->rds); ++ if (!endpoint) { ++ au_writel(USBDEV_FSTAT_FLUSH | USBDEV_FSTAT_UF | USBDEV_FSTAT_OF, ep->rds); ++ return; ++ } ++ if (!(urb = endpoint->rcv_urb)) { ++ TRACE_MSG2(PCD, "ISO OUT EPN - rcv_urb was NULL bytes: %d rds: %d", bytes, rds); ++ au_rcv_complete_irq(endpoint, bytes, 1); ++ TRACE_MSG0(PCD, "ISO OUT setting rcv_error"); ++ } ++ bytes = endpoint->wMaxPacketSize - au_get_dma_residue(au_get_dma_chan(ep->outdma)); ++ rds = rds & USBDEV_FSTAT_FCNT_MASK; ++ au_fifo_read(ep, urb->buffer + urb->actual_length + bytes, rds); ++ bytes += rds; ++ au_writel(USBDEV_FSTAT_FLUSH | USBDEV_FSTAT_UF | USBDEV_FSTAT_OF, ep->rds); ++ au_rcv_complete_irq(endpoint, bytes, 0); ++ au_start_out_iso(epn, endpoint, ep); ++} ++/* ********************************************************************************************* */ ++/* au_tx_dma_done - TX DMA interrupt handler ++ */ ++static void au_tx_dma_done(int irq, void *dev_id, struct pt_regs *regs) ++{ ++ int epn = (int) dev_id; ++ struct usbd_endpoint_instance *endpoint = pcd_instance->bus->endpoint_array + epn; ++ struct ep_regs *ep = &ep_regs[epn]; ++ int residue; ++#if 0 ++ int indma = irq - 6; ++ struct dma_chan *chan = get_dma_chan(indma); ++#endif ++#ifdef DBDMA ++#else /* DBDMA */ ++ struct dma_chan *chan = get_dma_chan(ep->indma); ++ u32 mode = au_readl(chan->io + DMA_MODE_READ); ++ TRACE_MSG1(PCD, "TX DMA done: mode: %x", mode); ++ if (mode & DMA_D0) ++ au_writel(DMA_D0, chan->io + DMA_MODE_CLEAR); ++ if (mode & DMA_D1) ++ au_writel(DMA_D1, chan->io + DMA_MODE_CLEAR); ++#endif /* DBDMA */ ++ ocd_ops.interrupts++; ++ RETURN_IF (!epn); ++ au_halt_dma(au_get_dma_chan(ep->indma)); ++ residue = au_get_dma_residue(au_get_dma_chan(ep->indma)); ++ TRACE_MSG2(PCD, "TX DMA IRQ - epn %d residue %d", epn, residue); ++ if (!au1x00_new_silicon) { ++ /* XXX magic delay - without this large packets will eventually stall the transmit ++ * XXX and all traffic in both directions will stop. ++ */ ++ udelay(8); ++ pcd_tx_complete_irq (endpoint, residue?1:0); ++ au_epn_interrupt_enable(epl2p[endpoint->bEndpointAddress&0xf]); ++ } ++ ep->last = 0; ++} ++ ++/* au_int_req - usb interrupt handler ++ */ ++static void au_int_req (int irq, void *dev_id, struct pt_regs *regs) ++{ ++ u32 intstat; ++ struct ep_regs *ep; ++#if defined(MAX_INTR_LOOP_STATS) ++ u32 loopcount = 0; ++#endif ++#ifdef RECORD_LATENCY ++ cp0_count = (read_c0_count(CP0_COUNT) - cp0_record) >> 9; ++ if (cp0_count < CP0_COUNTS) ++ cp0_counts[cp0_count]++; ++#endif ++#ifdef CHECK_LATENCY ++ u32 cp0_count = read_c0_count(CP0_COUNT); ++#endif ++ ocd_ops.interrupts++; ++#if 0 ++ if (ocd_ops.interrupts > 2000) { ++ TRACE_MSG0(PCD, "UDC_INT call udc_disable_interrupts"); ++ au_inten(0); ++ return; ++ } ++#endif ++ while (( intstat = au_readl(USBD_INTSTAT) & au1x00_inten)) { // read and reset interrupt status register ++ ++ int epn; ++#if 1 ++ for (epn = 2; epn < 6; epn++) { ++ CONTINUE_IF (!(intstat & (1 << epn))); ++#else ++ // NOT TESTED ++ u32 local_intstat = intstat; ++ TRACE_MSG2(PCD, "INTSTAT: %04x %04x", intstat, au_readl(USBD_INTEN)); ++ while (local_intstat & 0x3f) { ++ int epn = 31 - au_clz(local_intstat); ++ local_intstat &= ~(1<<epn); ++#endif ++ if (udc_saw_bus_activity) { ++ udc_saw_bus_activity = 0; ++ usbd_bus_event_handler_irq (pcd_instance->bus, DEVICE_BUS_ACTIVITY, 0); ++ au_inten(0x0033|USBDEV_INT_SOF); ++ } ++ ep = &ep_regs[epn]; ++ switch(ep->eptype) { ++ case USB_DIR_IN | USB_ENDPOINT_BULK: ++ case USB_DIR_IN | USB_ENDPOINT_INTERRUPT: ++ //TRACE_MSG2(PCD, "BULK IN %d %02x", epn, ep->eptype); ++ au_in_bulk(epn, ep, pcd_instance->bus->endpoint_array + epn); ++ break; ++ case USB_DIR_IN | USB_ENDPOINT_ISOCHRONOUS: ++ //TRACE_MSG2(PCD, "ISO IN %d %02x", epn, ep->eptype); ++ au_in_iso(epn, ep, pcd_instance->bus->endpoint_array + epn); ++ break; ++ case USB_DIR_OUT | USB_ENDPOINT_BULK: ++ case USB_DIR_OUT | USB_ENDPOINT_INTERRUPT: ++ //TRACE_MSG2(PCD, "BULK OUT %d %02x", epn, ep->eptype); ++ au_out_bulk(epn, ep, pcd_instance->bus->endpoint_array + epn); ++ break; ++ case USB_DIR_OUT | USB_ENDPOINT_ISOCHRONOUS: ++ //TRACE_MSG2(PCD, "ISO OUT %d %04d", epn, au_readl(NUSBD_FRAMENUM)); ++ au_out_iso(epn, ep, pcd_instance->bus->endpoint_array + epn); ++ break; ++ } ++ } ++ au_writel(intstat, USBD_INTSTAT); // Only clear the interrupt(s) AFTER servicing OUT ++ /* even though we disable the bulk-in interrupt (endpoint 2) prior to enabling ++ * DMA we always see one additional interrupt that is a NAK on that endpoint. ++ */ ++ CONTINUE_IF(!(intstat & au1x00_inten)); ++ /* handle control endpoint and suspend interrupt ++ */ ++ if (intstat & ( ((1 << 0) | (1 << 1) | (1 << 3) | (1 << 5) | USBDEV_INT_SOF))) { ++ if (intstat & (1 << 0)) ++ au_out_ep0(pcd_instance->bus->endpoint_array + 0, &ep_regs[0]); ++ if (intstat & (1 << 1)) ++ au_in_ep0(pcd_instance->bus->endpoint_array + 0, &ep_regs[0]); ++ if (intstat & USBDEV_INT_SOF) ++ if (USBD_SUSPENDED == pcd_instance->bus->status) { ++ TRACE_MSG0(PCD, "SUS - ACTIVITY"); ++ udc_saw_bus_activity++; ++ } ++ } ++#if defined(MAX_INTR_LOOP_STATS) ++ loopcount += 1; // Gather stats on how many times this loop is performed. ++#endif ++ } ++#if defined(MAX_INTR_LOOP_STATS) ++ interrupt_loop_stats[MIN(loopcount, MAX_INTR_LOOP_STATS)]++; ++#endif ++#if defined(CHECK_LATENCY) ++ TRACE_MSG1(PCD, "USB IRQ - %d", read_c0_count(CP0_COUNT) - cp0_count); ++#endif ++#if defined(RECORD_LATENCY) ++ cp0_record = read_c0_count(CP0_COUNT); ++#endif ++} ++ ++/* au_int_sus -suspend interrupt handler ++ */ ++static void au_int_sus (int irq, void *dev_id, struct pt_regs *regs) ++{ ++ ocd_ops.interrupts++; ++ TRACE_MSG2(PCD, "SUS - INACTIVE device: %d status: %d", pcd_instance->bus->device_state, pcd_instance->bus->status); ++ switch(pcd_instance->bus->status) { ++ case USBD_OPENING: ++ case USBD_OK: ++ au_inten(0x0033); ++ usbd_bus_event_handler_irq (pcd_instance->bus, DEVICE_BUS_INACTIVE, 0); ++ break; ++ default: ++ break; ++ } ++} ++/* ********************************************************************************************* */ ++/* ++ * au_connect - enable pullup resistor ++ * Turn on the USB connection by enabling the pullup resistor. ++ * ++ * au_disconnect - disable pullup resistor ++ * Turn off the USB connection by disabling the pullup resistor. ++ */ ++ ++#if defined(CONFIG_MIPS_PICOENGINE_MVCI) ++void au_connect (struct pcd_instance *pcd) ++{ ++ extern void pico_mvci_set_usb_pullup(int); ++ pico_mvci_set_usb_pullup(1); ++} ++ ++void au_disconnect (struct pcd_instance *pcd) ++{ ++ extern void pico_mvci_set_usb_pullup(int); ++ pico_mvci_set_usb_pullup(0); ++} ++ ++#elif defined(CONFIG_MIPS_FREEHAND_NATIVE) ++void au_connect (struct pcd_instance *pcd) ++{ ++ au1000gpio_set(GPIO01); ++} ++ ++void au_disconnect (struct pcd_instance *pcd) ++{ ++ au1000gpio_clear(GPIO01); ++} ++#elif defined(CONFIG_AMX_MORDOR) ++void au_connect (struct pcd_instance *pcd) ++{ ++ au_writel(0x00000008, SYS_OUTPUTSET); ++} ++ ++void au_disconnect (struct pcd_instance *pcd) ++{ ++ au_writel(0x00000008, SYS_OUTPUTCLR); ++} ++#else ++void au_connect (struct pcd_instance *pcd) ++{ ++} ++void au_disconnect (struct pcd_instance *pcd) ++{ ++} ++#endif ++ ++#if LINUX_VERSION_CODE < KERNEL_VERSION(2,4,19) ++#undef read_c0_prid ++#define read_c0_prid() read_32bit_cp0_register(CP0_PRID) ++#endif ++#undef read_c0_COUNT ++#ifndef read_c0_count ++#define read_c0_count() read_32bit_cp0_register(CP0_COUNT) ++#endif ++/* au_loc_conn - used to enable or disable peripheral connecting to bus ++ * ++ */ ++void au_loc_conn(struct otg_instance *otg, u8 flag) ++{ ++ struct pcd_instance *pcd = (struct pcd_instance *)otg->pcd; ++ TRACE_MSG0(PCD, "--"); ++ TRACE_MSG1(PCD, "PCD: %x", (int)pcd); ++ switch (flag) { ++ case SET: ++ TRACE_MSG0(PCD, "OUTPUT: OCD_LOC_CONN_SET"); ++ TRACE_MSG0(PCD, "OUTPUT: OCD_LOC_CONN_RESET - Enable DP PULLUP"); ++ au_connect(pcd); ++ break; ++ ++ case RESET: ++ TRACE_MSG0(PCD, "OUTPUT: OCD_LOC_CONN_RESET"); ++ TRACE_MSG0(PCD, "OUTPUT: OCD_LOC_CONN_RESET - Disable DP PULLUP"); ++ au_disconnect(pcd); ++ break; ++ ++ case PULSE: ++ TRACE_MSG0(PCD, "OUTPUT: OCD_LOC_CONN_PULSE"); ++ break; ++ } ++} ++ ++/* ********************************************************************************************* */ ++/* au_ticks - get current ticks ++* */ ++u64 au_ticks (void) ++{ ++ return read_c0_count(); ++} ++ ++/* au_elapsed - return micro-seconds between two tick values ++ */ ++u64 au_elapsed(u64 *t1, u64 *t2) ++{ ++ u64 ticks = (*t1 > *t2) ? (*t1 - *t2) : (*t2 - *t1); ++ ticks = (u32)((u32) ticks / (u32) CONFIG_OTG_AU1X00_SCLOCK); ++ return ticks; ++} ++ ++/* au_framenum - get current framenum ++ */ ++u16 au_framenum (void) ++{ ++ return au_readl(NUSBD_FRAMENUM); ++} ++ ++ ++/* ********************************************************************************************* */ ++/* au_start_endpoint_in - start transmit ++ */ ++void au_start_endpoint_in(struct pcd_instance *pcd, struct usbd_endpoint_instance *endpoint) ++{ ++ int epn = epl2p[endpoint->bEndpointAddress&0xf]; ++ struct ep_regs *ep = &ep_regs[epn]; ++ switch(endpoint->bmAttributes & USB_ENDPOINT_MASK) { ++ case USB_ENDPOINT_CONTROL: ++ au_in_ep0(endpoint, ep); ++ break; ++ case USB_ENDPOINT_BULK: ++ case USB_ENDPOINT_INTERRUPT: ++ au_start_in_bulk(epn, endpoint, ep); ++ break; ++ case USB_ENDPOINT_ISOCHRONOUS: ++ au_start_in_iso(epn, endpoint, ep); ++ break; ++ } ++ if (au1x00_new_silicon) ++ au_epn_interrupt_enable(epn); ++} ++ ++/* au_start_endpoint_out - start receive ++ */ ++void au_start_endpoint_out(struct pcd_instance *pcd, struct usbd_endpoint_instance *endpoint) ++{ ++ int epn = epl2p[endpoint->bEndpointAddress&0xf]; ++ struct ep_regs *ep = &ep_regs[epn]; ++ switch(endpoint->bmAttributes & USB_ENDPOINT_MASK) { ++ case USB_ENDPOINT_CONTROL: ++ break; ++ case USB_ENDPOINT_BULK: ++ case USB_ENDPOINT_INTERRUPT: ++ au_start_out_bulk(epn, endpoint, ep); ++ au_epn_interrupt_enable(epn); ++ break; ++ case USB_ENDPOINT_ISOCHRONOUS: ++ au_start_out_iso(epn, endpoint, ep); ++ break; ++ } ++} ++ ++void au_cancel_in_irq(struct pcd_instance *pcd, struct usbd_urb *urb) ++{ ++ int epn = epl2p[urb->endpoint->bEndpointAddress&0xf]; ++ struct ep_regs *ep = &ep_regs[epn]; ++ au_in_bulk(epn, ep, urb->endpoint); ++} ++ ++void au_cancel_out_irq(struct pcd_instance *pcd, struct usbd_urb *urb) ++{ ++ int epn = epl2p[urb->endpoint->bEndpointAddress&0xf]; ++ struct ep_regs *ep = &ep_regs[epn]; ++ au_out_bulk(epn, ep, urb->endpoint); ++} ++ ++/* au_serial_init - set a serial number if available ++ */ ++int au_serial_init (struct pcd_instance *pcd) ++{ ++#if defined(CONFIG_MIPS_FREEHAND) ++ int length; ++ long data[16]; /* yeah a hack, but we KNOW it's 16 */ ++ struct i2c_client *client; ++ char chData[16]; ++ int i; ++ ++ if (!(client = getFreeHandEepromClient())) { ++ printk(KERN_INFO"eeprom not ready when au_serial_init called\n"); ++ return -EINVAL; ++ } ++ eeprom_contents(client, SENSORS_PROC_REAL_READ, EEPROM_SYSCTL1, &length, (long *)data); ++ ++ /* serial number is first 9 longs. But each long is just an ASCII char ++ * convert this to a string and then extract a 4 byte value from it ++ */ ++ for (i = 0; i < 9; chData[i] = (char)data[i], i++); /* trunc, is ok */ ++ chData[9] = 0; /* terminate string */ ++ printk(KERN_INFO"%s: %s\n", __FUNCTION__, chData); ++ pcd_instance->bus->serial_number_str = lstrdup(chData); ++ return 0; ++#else ++ return -EINVAL; ++#endif ++} ++ ++ ++#ifdef DBDMA ++static int request_dma(int ep, int src, int dest, char *str, void dma_done(int , void *, struct pt_regs *)) ++{ ++ unsigned int dma; ++ ++ if ((dma = au1xxx_dbdma_chan_alloc(src, dest, dma_done, (void *)ep)) < 0) { ++ printk(KERN_INFO"request_io[%d] dma: %X src: %x dest: %x %s FAILED\n", ep, dma, src, dest, str); ++ return -1; ++ } ++ ++ au1xxx_dbdma_set_devwidth(dma, 16); ++ ++ if(au1xxx_dbdma_ring_alloc(dma, 4) == 0) { ++ printk(KERN_INFO"Ring Buffer Allocation for dma: %X src: %x dest: %x %s FAILED\n", ep, dma, src, dest, str); ++ return -1; ++ } ++ printk(KERN_INFO"request_io[%d] dma: %X src: %x dest: %x %s SUCCEDED\n", ep, dma, src, dest, str); ++ return dma; ++} ++#else /* DBDMA */ ++static int request_dma(int ep, int id, char *str, void dma_done(int , void *, struct pt_regs *)) ++{ ++ int dma = request_au1000_dma(id, str, dma_done, SA_INTERRUPT, (void *)ep); ++ if (dma >= 0) return dma; ++ printk(KERN_INFO"request_io[%d] dma: %d id: %x %s FAILED\n", ep, dma, id, str); ++ return -1; ++} ++#endif /* DBDMA */ ++ ++/* au_start - start session ++ */ ++void au_start (struct pcd_instance *pcd) ++{ ++ au_inten(0x0033|USBDEV_INT_SOF); // Only enable receive interrupts. ++} ++ ++/* au_stop - stop session ++ */ ++void au_stop (struct pcd_instance *pcd) ++{ ++ au_inten(0); ++} ++ ++int au_assign_endpoint( u8 physicalEndpoint, int used[6], struct usbd_endpoint_map *endpoint_map, u8 bmAttributes, ++ u16 wMaxPacketSize, u16 transferSize) ++{ ++ struct ep_regs *ep = &ep_regs[physicalEndpoint]; ++ RETURN_EINVAL_IF(used[physicalEndpoint]); ++ endpoint_map->bEndpointAddress[0] = epp2l[physicalEndpoint]; ++ endpoint_map->physicalEndpoint[0] = physicalEndpoint; ++ endpoint_map->wMaxPacketSize[0] = wMaxPacketSize; ++ endpoint_map->transferSize[0] = transferSize; ++ endpoint_map->bmAttributes[0] = bmAttributes; ++ used[physicalEndpoint]++; ++ ep->eptype = bmAttributes & 0x83; ++ return 0; ++} ++ ++int au_request_endpoints(struct pcd_instance *pcd, struct usbd_endpoint_map *endpoint_map_array, int endpointsRequested, ++ struct usbd_endpoint_request *requestedEndpoints) ++{ ++ struct usbd_device_description *device_description; ++ int i, j; ++ int used[UDC_MAX_ENDPOINTS]; ++ memset(used, 0, sizeof(used)); ++ for (j = 0, i = 0; i < endpointsRequested; i++, j++) { ++ struct usbd_endpoint_map *endpoint_map = endpoint_map_array + i; ++ u8 bmAttributes = requestedEndpoints[i].bmAttributes; ++ u16 transferSize = requestedEndpoints[i].fs_requestedTransferSize; ++ RETURN_EINVAL_UNLESS(j < UDC_MAX_ENDPOINTS); ++ switch(bmAttributes) { ++ case USB_DIR_OUT | USB_ENDPOINT_BULK: ++ case USB_DIR_OUT | USB_ENDPOINT_INTERRUPT: ++ case USB_DIR_OUT | USB_ENDPOINT_INTERRUPT | USB_ENDPOINT_OPT: ++ CONTINUE_IF(!au_assign_endpoint(4, used, endpoint_map, bmAttributes, 0x40, transferSize)); ++ CONTINUE_IF(!au_assign_endpoint(5, used, endpoint_map, bmAttributes, 0x40, transferSize)); ++ break; ++ case USB_DIR_OUT | USB_ENDPOINT_ISOCHRONOUS: ++ CONTINUE_IF(!au_assign_endpoint(4, used, endpoint_map, bmAttributes, transferSize, transferSize)); ++ CONTINUE_IF(!au_assign_endpoint(5, used, endpoint_map, bmAttributes, transferSize, transferSize)); ++ break; ++ case USB_DIR_IN | USB_ENDPOINT_BULK: ++ case USB_DIR_IN | USB_ENDPOINT_INTERRUPT: ++ case USB_DIR_IN | USB_ENDPOINT_INTERRUPT | USB_ENDPOINT_OPT: ++ CONTINUE_IF(!au_assign_endpoint(2, used, endpoint_map, bmAttributes, 0x40, transferSize)); ++ CONTINUE_IF(!au_assign_endpoint(3, used, endpoint_map, bmAttributes, 0x40, transferSize)); ++ break; ++ case USB_DIR_IN | USB_ENDPOINT_ISOCHRONOUS: ++ CONTINUE_IF(!au_assign_endpoint(2, used, endpoint_map, bmAttributes, transferSize, transferSize)); ++ CONTINUE_IF(!au_assign_endpoint(3, used, endpoint_map, bmAttributes, transferSize, transferSize)); ++ break; ++ } ++ CONTINUE_IF(bmAttributes & USB_ENDPOINT_OPT); ++ return -EINVAL; ++ } ++ return 0; ++} ++ ++int au_set_endpoints(struct pcd_instance *pcd, int endpointsRequested, struct usbd_endpoint_map *endpoint_map_array) ++{ ++ int i, j; ++ u8 config[25]; ++ u8 *cp; ++ memcpy(config, au1x00_config_bulk, sizeof(config)); ++ for (i = 0; i < endpointsRequested; i++) { ++ struct usbd_endpoint_map *endpoint_map = endpoint_map_array + i; ++ int epreq = endpoint_map->bmAttributes[0]; ++ int eptype = epreq & USB_ENDPOINT_MASK; ++ int epdir = epreq & USB_ENDPOINT_DIR_MASK ? 0x8 : 0; ++ int epsize = endpoint_map->wMaxPacketSize[0]; ++ int epaddr = epp2l[endpoint_map->physicalEndpoint[0]]; ++ CONTINUE_IF(!endpoint_map->physicalEndpoint[0] || (endpoint_map->physicalEndpoint[0] > 5)); ++ cp = config + ((endpoint_map->physicalEndpoint[0] - 1) * 5); ++ cp[0] = (epaddr & 0xf) << 4 | 0x4; ++ cp[1] = (eptype << 4) | epdir | (epsize & 0x380) >> 7; ++ cp[2] = (epsize & 0x7F) << 1; ++ } ++ au_inten(0); // disable interrupts ++ au_writel(0x0002, USBD_ENABLE); // reset controller ++ udelay(100); ++ au_writel(0x0003, USBD_ENABLE); // enable controller ++ udelay(100); ++ for (cp = config, i = 0; i < 25; i++, au_writel(*cp++, USBD_CONFIG)); // feed the config into the UDC ++ return 0; ++} ++/* ********************************************************************************************* */ ++/* au_mod_init ++ */ ++int au_mod_init (void) ++{ ++ int dev = request_irq (AU1000_USB_DEV_REQ_INT, au_int_req, SA_INTERRUPT, UDC_NAME "UDC Req", NULL); ++ int sus = request_irq (AU1000_USB_DEV_SUS_INT, au_int_sus, SA_INTERRUPT, UDC_NAME "UDC Sus", NULL); ++ u32 cp0_prid = read_c0_prid(); ++ int i; ++ ++ ++ if (dev || sus) { // check if either request irq failed ++ if (!dev) free_irq (AU1000_USB_DEV_REQ_INT, NULL); // free irqs that might have ++ if (!sus) free_irq (AU1000_USB_DEV_SUS_INT, NULL); // been successfully allocated ++ return -EINVAL; ++ } ++#ifdef DBDMA ++ for (i = 0; i < 6; i++) { ++ ep_regs[i].indma = ep_regs[i].tx_id ? ++ request_dma(i, DBDMA_MEM_CHAN, ep_regs[i].tx_id, ep_regs[i].tx_str, au_tx_dma_done) : -1; ++ ep_regs[i].outdma = ep_regs[i].rx_id ? ++ request_dma(i, ep_regs[i].rx_id, DBDMA_MEM_CHAN, ep_regs[i].rx_str, NULL) : -1; ++ } ++#else /*DBMA */ ++ for (i = 0; i < 6; i++) { ++ ep_regs[i].indma = ep_regs[i].tx_id ? request_dma(i, ep_regs[i].tx_id, ep_regs[i].tx_str, au_tx_dma_done) : -1; ++ ep_regs[i].outdma = ep_regs[i].rx_id ? request_dma(i, ep_regs[i].rx_id, ep_regs[i].rx_str, NULL) : -1; ++ } ++#endif /*DBMA */ ++ switch (cp0_prid & CP0_PRID_SOC_MASK) { ++ case CP0_PRID_AU1000: ++ case CP0_PRID_AU1100: ++ au1x00_new_silicon = (cp0_prid & CP0_PRID_REV_MASK) >= 4; ++ printk(KERN_INFO"%s: AU1100 cp0_prid: new: %d\n", __FUNCTION__, au1x00_new_silicon); ++ break; ++ case CP0_PRID_AU1500: ++ au1x00_new_silicon = (cp0_prid & CP0_PRID_REV_MASK) >= 2; ++ printk(KERN_INFO"%s: AU1500 cp0_prid: new: %d\n", __FUNCTION__, au1x00_new_silicon); ++ break; ++ case CP0_PRID_AU1550: ++ au1x00_new_silicon = (cp0_prid & CP0_PRID_REV_MASK) >= 0; ++ printk(KERN_INFO"%s: AU1550 cp0_prid: new: %d\n", __FUNCTION__, au1x00_new_silicon); ++ break; ++ default: ++ printk(KERN_INFO"%s: UNKNOWN CPU cp0_prid: %08x UNKNOWN UDC\n", __FUNCTION__, cp0_prid); ++ break; ++ } ++ return 0; ++} ++ ++/* au_mod_exit ++ */ ++void au_mod_exit (void) ++{ ++ int j; ++ au_writel(0x0000, USBD_ENABLE); ++ for (j = 0; j < 6; j++) { ++ struct ep_regs *ep = &ep_regs[j]; ++#ifdef DBDMA ++ if (ep->indma != -1) { ++ au1xxx_dbdma_chan_free(ep->indma); ++ ep->indma = -1; ++ } ++ if (ep->outdma != -1) { ++ au1xxx_dbdma_chan_free(ep->outdma); ++ ep->outdma = -1; ++ } ++#else /*DBMA */ ++ if (ep->indma != -1) { ++ free_au1000_dma(ep->indma); ++ ep->indma = -1; ++ } ++ if (ep->outdma != -1) { ++ free_au1000_dma(ep->outdma); ++ ep->outdma = -1; ++ } ++#endif /*DBMA */ ++ } ++ free_irq (AU1000_USB_DEV_REQ_INT, NULL); ++ free_irq (AU1000_USB_DEV_SUS_INT, NULL); ++#if defined(MAX_INTR_LOOP_STATS) ++ { ++ u32 lc; ++ for (lc = 0; lc <= MAX_INTR_LOOP_STATS; lc++) ++ if (interrupt_loop_stats[lc]) ++ printk(KERN_ERR "%s: interrupt loopcount[%02u] %9u\n", __FUNCTION__, lc,interrupt_loop_stats[lc]); ++ printk(KERN_INFO"%s: halt_dma_expired: %d\n", __FUNCTION__, au_halt_dma_expired); ++ } ++#endif ++#ifdef RECORD_LATENCY ++ { ++ int i; ++ for (i = 0; i < CP0_COUNTS; i++) ++ if (cp0_counts[i]) ++ printk(KERN_INFO"%s: cp0_counts[%d] %d\n", __FUNCTION__, i, cp0_counts[i]); ++ } ++#endif ++} ++ ++/* ********************************************************************************************* */ ++struct usbd_pcd_ops usbd_pcd_ops = { ++ max_endpoints: UDC_MAX_ENDPOINTS, ++ ep0_packetsize: EP0_PACKETSIZE, ++ name: UDC_NAME, ++ start: au_start, ++ stop: au_stop, ++ start_endpoint_in: au_start_endpoint_in, ++ start_endpoint_out: au_start_endpoint_out, ++ request_endpoints: au_request_endpoints, ++ set_endpoints: au_set_endpoints, ++ cancel_in_irq: au_cancel_in_irq, ++ cancel_out_irq: au_cancel_out_irq, ++ serial_init: au_serial_init, ++}; ++ ++struct pcd_ops pcd_ops = { ++ pcd_en_func: pcd_en_func, ++ mod_init: au_mod_init, ++ mod_exit: au_mod_exit, ++}; ++ ++struct ocd_ops ocd_ops = { ++ #if defined(CONFIG_OTG_TR_AUTO) ++ capabilities: OCD_CAPABILITIES_TR | OCD_CAPABILITIES_AUTO, ++ #else ++ capabilities: OCD_CAPABILITIES_TR, ++ #endif ++ ticks: au_ticks, ++ elapsed: au_elapsed, ++}; ++ ++struct tcd_ops tcd_ops = { ++ dp_pullup_func: au_loc_conn, ++}; ++ +diff -uNr linux/drivers/no-otg/ocd/au1x00/au1x00.h linux/drivers/otg/ocd/au1x00/au1x00.h +--- linux/drivers/no-otg/ocd/au1x00/au1x00.h 1970-01-01 01:00:00.000000000 +0100 ++++ linux/drivers/otg/ocd/au1x00/au1x00.h 2006-09-01 21:41:31.000000000 +0200 +@@ -0,0 +1,112 @@ ++/* ++ * otg/ocd/au1x00/au1100.h ++ * ++ * Copyright (c) 2004 Belcarra ++ * ++ * ++ * By: ++ * Stuart Lynne <sl@belcarra.com>, ++ * Tom Rushworth <tbr@belcarra.com>, ++ * Bruce Balden <balden@belcarra.com> ++ */ ++ /*! ++ * @defgroup AU1X00 AMD AU1X00 ++ * @ingroup pcdgroup ++ */ ++ ++/*! ++ * @file otg/ocd/au1x00/au1x00.h ++ * @brief AU1X00 Header ++ * ++ * This file contains the private defines and structures for the AU1X00 ++ * Drivers. ++ * ++ * @ingroup AU1X00 ++ */ ++ ++ ++ ++/* ++ * ++ * au1100 ++ * ++ * The au1100 does not seem to work properly with an 8 byte ++ * packetsize. So 16, 32 or 64 are the only valid values. ++ * ++ * Unfortunately this means that a DMA channel is required for ++ * EP0 transmit. ++ */ ++ ++#define EP0_PACKETSIZE 0x8 ++ ++#define UDC_MAX_ENDPOINTS 6 ++ ++#define UDC_NAME "AU1100" ++ ++#define NUSBD_EP0RD 0xB0200000 ++#define NUSBD_EP0WR 0xB0200004 ++#define NUSBD_EP1WR 0xB0200008 ++#define NUSBD_EP2WR 0xB020000C ++#define NUSBD_EP3RD 0xB0200010 ++#define NUSBD_EP4RD 0xB0200014 ++ ++#define NUSBD_EP0CS 0xB0200024 ++#define NUSBD_EP1CS 0xB0200028 ++#define NUSBD_EP2CS 0xB020002C ++#define NUSBD_EP3CS 0xB0200030 ++#define NUSBD_EP4CS 0xB0200034 ++ ++#define NUSBD_EP0RDSTAT 0xB0200040 ++#define NUSBD_EP0WRSTAT 0xB0200044 ++#define NUSBD_EP1WRSTAT 0xB0200048 ++#define NUSBD_EP2WRSTAT 0xB020004C ++#define NUSBD_EP3RDSTAT 0xB0200050 ++#define NUSBD_EP4RDSTAT 0xB0200054 ++ ++#define NUSBD_FRAMENUM 0xB0200038 ++ ++enum { ++ NDMA_ID_UART0_TX = 0, ++ NDMA_ID_UART0_RX, ++ NDMA_ID_GP04, ++ NDMA_ID_GP05, ++ NDMA_ID_AC97C_TX, ++ NDMA_ID_AC97C_RX, ++ NDMA_ID_UART3_TX, ++ NDMA_ID_UART3_RX, ++ NDMA_ID_USBDEV_EP0_RX, ++ NDMA_ID_USBDEV_EP0_TX, ++ NDMA_ID_USBDEV_EP1_TX, ++ NDMA_ID_USBDEV_EP2_TX, ++ NDMA_ID_USBDEV_EP3_RX, ++ NDMA_ID_USBDEV_EP4_RX, ++ NDMA_ID_I2S_TX, ++ NDMA_ID_I2S_RX, ++ NDMA_NUM_DEV ++}; ++ ++typedef struct ep_regs { ++ int rd; ++ int wr; ++ int cs; ++ int rds; ++ int wrs; ++ int rx_id; ++ int tx_id; ++ char * rx_str; ++ char * tx_str; ++ u32 indma; ++ u32 outdma; ++ int last; ++ int eptype; ++} ep_regs_t; ++ ++#define MAX_EPN_PACKET_SIZE 64 ++#define CP0_PRID_SOC_MASK 0xff000000 ++#define CP0_PRID_AU1000 0x00000000 ++#define CP0_PRID_AU1500 0x01000000 ++#define CP0_PRID_AU1100 0x02000000 ++#define CP0_PRID_AU1550 0x03000000 /* AMX */ ++ ++#define CP0_PRID_REV_MASK 0x000000ff ++ +diff -uNr linux/drivers/no-otg/ocd/fsotg/fs-hcd.c linux/drivers/otg/ocd/fsotg/fs-hcd.c +--- linux/drivers/no-otg/ocd/fsotg/fs-hcd.c 1970-01-01 01:00:00.000000000 +0100 ++++ linux/drivers/otg/ocd/fsotg/fs-hcd.c 2006-09-01 21:41:31.000000000 +0200 +@@ -0,0 +1,1527 @@ ++/* ++ * otg/hcd/fsotg/fs-hcd.c - Freescale USBOTG aware Host Controller Driver (HCD) ++ * ++ * Copyright (c) 2004 Belcarra Technologies ++ * ++ * By: ++ * Stuart Lynne <sl@belcara.com>, ++ * Tom Rushworth <tbr@belcara.com>, ++ * Bruce Balden <balden@belcara.com> ++ */ ++/*! ++ * @file otg/ocd/fsotg/fs-hcd.c ++ * @brief Freescale USB Host Controller Driver ++ * ++ * This is the hardware specific component of the HCD. It knows nothing ++ * about URBs, and deals with "transfers". This is not because it shouldn't ++ * know about them, but because URBs are generic, and so it seems reasonable ++ * to keep as much generic code in the hardware independent portion as ++ * possible. As a result, the parameter list to hcd_hw_start_transfer() ++ * is really fat. ++ * ++ * This is not compiled as a self contained module, but is linked with ++ * the generic component hc_xfer.o. ++ * ++ * There is some processor specific code here to support the alternate processors ++ * that implement this type of USB support. ++ * ++ * There is no board or platform level code here. ++ * ++ * @ingroup FSOTG ++ * @{ ++ */ ++ ++#include <linux/config.h> ++#include <linux/module.h> ++#include <linux/kernel.h> ++#include <linux/pci.h> // For DMA directions ++//#include <linux/slab.h> ++#include <linux/errno.h> ++ ++#include <linux/usb.h> ++#include <linux/delay.h> ++#include <asm/arch/mx2.h> ++ ++#include <otg/otg-compat.h> ++ ++#include "../core/hcd.h" ++ ++#include <otg/usbp-chap9.h> ++#include <otg/otg-trace.h> ++#include <otg/otg-api.h> ++#include <otg/otg-utils.h> ++ ++#include <otg/otg-hcd.h> ++#include <otg/hcd-l26.h> ++#include <otg/hcd-rh.h> ++#include <otg/hcd-hw.h> ++ ++#include <otghw/fsotg-hardware.h> ++ ++#ifndef CONFIG_OTG_ROOTHUB_VENDORID ++#define CONFIG_OTG_ROOTHUB_VENDORID 0x15ec ++#endif ++#ifndef CONFIG_OTG_ROOTHUB_PRODUCTID ++#define CONFIG_OTG_ROOTHUB_PRODUCTID 0xf010 ++#endif ++#ifndef CONFIG_OTG_ROOTHUB_BCDDEVICE ++#define CONFIG_OTG_ROOTHUB_BCDDEVICE 0x0100 ++#endif ++ ++ ++#define STATIC static ++extern void hcd_trace_mem(otg_tag_t tag, char *fn, char *msg, int len, volatile void *data); ++ ++// Start of hardware specific components.... ++ ++/* ********************************************************************************************* */ ++ ++// Globals for debug ++static u32 monitoring_etd_mask = 0; // 0 == no monitoring ++static u32 monitoring_etd_num; ++#define MAX_ETD_FRAMES 3 ++static u32 current_etd_frame; ++static u32 done_flags[1+MAX_ETD_FRAMES]; ++static u32 done_status[1+MAX_ETD_FRAMES]; ++static endpoint_transfer_descriptor mon_etd[1+MAX_ETD_FRAMES]; ++ ++ ++ ++/*! ++ * @struct cc_name ++ * @brief Map CC values to names ++ */ ++static char *cc_name[] = { ++ "no error", "CRC error", "bitstuff error", "data toggle error", ++ "stall", "device not responding", "PID failure" "reserved", ++ "data overrun", "data underrun", "ACK", "NAK", "buffer overrun", ++ "buffer underrun", "schedule overrun" "not accessed" ++}; ++ ++ ++/*! ++ * comp_code_to_status() - map condition cod3 to errno value ++ * @param cc condition code ++ * @return errno value ++ */ ++STATIC int comp_code_to_status(int cc) ++{ ++ // ++ if (cc) ++ TRACE_MSG2(HCD,"NON-ZERO CC=%d %s", cc,cc_name[cc]); ++ ++ switch (cc) { ++ case ETD_CC_NOERROR: return 0; ++ case ETD_CC_CRCERR: return -EILSEQ; ++ case ETD_CC_BITSTUFFERR: return -EPROTO; ++ case ETD_CC_DATATOGERR: return -EPROTO; // I guess, nothing else looks better. ++ case ETD_CC_STALL: return -EPIPE; ++ case ETD_CC_DEVNOTRESPONDING: return -ETIMEDOUT; ++ case ETD_CC_PIDFAILURE: return -EPROTO; ++ case ETD_CC_DATAOVERRUN: return -EOVERFLOW; ++ case ETD_CC_DATAUNDERRUN: return -EREMOTEIO; ++ case ETD_CC_ACK: return -EPROTO; // For lack of anything better ++ case ETD_CC_NAK: return -EPROTO; // For lack of anything better ++ case ETD_CC_BUFFEROVERRUN: return -ECOMM; ++ case ETD_CC_BUFFERUNDERRUN: return -ENOSR; ++ case ETD_CC_SCHEDOVERRUN: return -ENOSPC; ++ case ETD_CC_NOTACCESSED: return -EPROTO; // For lack of anything better ++ default: return -EINVAL; ++ } ++} ++ ++/*! ++ * rel_etd() - release etd ++ * {get,rel}_etd maintain a free list of ETDs by saving the index of the next free ETD in the ++ * etd->xbufsrtad field. The list is terminated by 0x0000ffff. List manipulation needs to be ++ * protected by irq_lock, since ETDs are released in the interrupt handler, but allocated at ++ * regular priority. ++ * @param fs_hcpriv ++ * @param etdn ++ */ ++STATIC void rel_etd(fs_hcpriv *fs_hcpriv, int etdn) ++{ ++ fs_wl(etd_word(etdn, 0), 0); ++ fs_wl(etd_word(etdn, 2), 0); ++ fs_wl(etd_word(etdn, 3), 0); ++ fs_wl(etd_word(etdn, 1), fs_hcpriv->free_etds); ++ fs_hcpriv->free_etds = etdn; ++} ++ ++/*! ++ * get_etd() - get etd ++ * {get,rel}_etd maintain a free list of ETDs by saving the index of the next free ETD in the ++ * etd->xbufsrtad field. The list is terminated by 0x0000ffff. List manipulation needs to be ++ * protected by irq_lock, since ETDs are released in the interrupt handler, but allocated at ++ * regular priority. ++ * @param fs_hcpriv ++ * @return int ++ */ ++STATIC int get_etd(fs_hcpriv *fs_hcpriv) ++{ ++ // Return the index of an available ETD, or -1 if none are available. ++ unsigned long flags; ++ int etdn = -1; ++ local_irq_save(flags); ++ // Is list is empty? ++ if (0x0000ffff != (etdn = fs_hcpriv->free_etds)) ++ fs_hcpriv->free_etds = fs_rl(etd_word(etdn, 1)); ++ local_irq_restore(flags); ++ return etdn; ++} ++ ++/* ********************************************************************************************* */ ++/* ++ * For the generic transfer aware framework. ++ */ ++ ++// For debugging... ++static int num_urbs_started = 0; ++#define MAX_URBS_STARTED 0 ++static char *dirpid_name[4] = {"SETUP","OUT","IN","WRONG"}; ++//static char *direct_name[4] = {"TD00","OUT","IN","TD11"}; ++static char *etd_urb_state_name[] = {"COMPLETED","SETUP_STATUS","SETUP_DATA","SETUP_PKT","BULK","INTERRUPT","ISOC","WRONG"}; ++static char *format_name[4] = { "CONTROL", "ISO", "BULK", "INT", }; ++ ++ ++/*! ++ * start_etd() - start an etd ++ * @param etdn ++ * @param len ++ * @param data ++ * @param out ++ */ ++STATIC void start_etd(int etdn, int len, void *data, int out) ++{ ++ if (len) { ++ consistent_sync(data,len, (out ? PCI_DMA_TODEVICE : PCI_DMA_FROMDEVICE)); ++ fs_wl(OTG_DMA_ETD_MSA(etdn), virt_to_bus(data)); ++ } ++ if (len || out) ++ fs_wl(OTG_DMA_ETD_EN, ETD_MASK(etdn)); ++ ++ fs_orl(OTG_HOST_IINT, ETD_MASK(etdn)); // Don't wait until SOF, interrupt ASAP. ++ fs_orl(OTG_HOST_ETD_DONE, ETD_MASK(etdn)); // Interrupt on ETD done (forget DMA interrupt). ++ fs_wl(OTG_HOST_ETD_EN, ETD_MASK(etdn)); ++} ++ ++/*! ++ * hcd_hw_start_transfer() - start a transfer ++ * Return 0..MAX_ACTIVE_XFERS-1 if the transfer is successfully started, ++ * -1 if there is something wrong with the requested transfer, ++ * -2 if there are no resources available at this time, but resubmission is OK. ++ * ++ * @param bus_hcpriv ++ * @param len length of data region ++ * @param data virtual address of data region ++ * @param toggle toggle value to start the xfer with ++ * @param maxps max packet size of endpoint ++ * @param slow 1 ==> slow speed, 0 ==> full speed QQSV verify: (((urb->pipe) >> 26) & 1) ++ * @param endpoint endpoint number ++ * @param address USB device address ++ * @param pid USB_PID_{OUT,IN,SETUP} ++ * @param format PIPE_{CONTROL,BULK,INTERRUPT,ISOCHRONOUS} ++ * @param other value depending on format ++ * ++ * PIPE_CONTROL: other == address of setup packet. ++ * PIPE_BULK: other == ZLP at end (T/F), ++ * PIPE_INTERRUPT: other == poll interval ++ */ ++int hcd_hw_start_transfer(struct bus_hcpriv *bus_hcpriv, int len, void *data, int toggle, int maxps, ++ int slow, int endpoint, int address, int pid, int format, u32 other) ++{ ++ fs_hcpriv *fs_hcpriv = (struct fs_hcpriv *) (bus_hcpriv->hw_hci); ++ fs_data_buff *x = NULL; ++ fs_data_buff *y = NULL; ++ int fmt = 0; ++ int out; ++ int etdn; ++ //int rc = 0; ++ int dir = 0; ++ endpoint_transfer_descriptor *sdp = NULL; ++ transfer_descriptor td; ++ int sdp_out = FALSE; ++ ++ //TRACE_MSG8(HCD, "len: %d toggle: %d endpoint: %02x pid: %d format: %d address: %d %s %s", ++ // len, toggle, endpoint, pid, format, address, dirpid_name[pid], format_name[format]); ++ ++ // data address needs to be on a 32-bit boundary. ++ if (0x3 & (u32)data) { ++ printk(KERN_ERR "%s: invalid data alignment for urb, urb rejected.\n",__FUNCTION__); ++ TRACE_MSG1(HCD, "invalid data alignment for urb (%08x), urb rejected.",(u32)data); ++ return -1; ++ } ++ num_urbs_started += 1; ++ if ((MAX_URBS_STARTED > 0) && (num_urbs_started > MAX_URBS_STARTED)) { ++ // Can the urb, don't touch the HW ++ printk(KERN_ERR "%s: ignoring urb %d\n",__FUNCTION__,num_urbs_started); ++ return -1; ++ } ++ ++ /* Check pid, setup pid and out flags ++ */ ++ switch (pid) { ++ case USB_PID_SETUP: ++ pid = ETD_DIRPID_SETUP; ++ out = TRUE; ++ break; ++ case USB_PID_OUT: ++ pid = ETD_DIRPID_OUT; ++ out = TRUE; ++ break; ++ case USB_PID_IN: ++ pid = ETD_DIRPID_IN; ++ out = FALSE; ++ break; ++ default: ++ TRACE_MSG1(HCD,"invalid PID %x",pid); ++ return -1; ++ } ++ switch (format) { ++ case PIPE_CONTROL: ++ case PIPE_BULK: ++ case PIPE_INTERRUPT: ++ td.w3.val = (ETD_SET_BUFSIZE(sizeof(fs_data_buff)-1) | ETD_SET_TOTBYECNT(len)); ++ break; ++ default: ++ return -1; ++ } ++ ++ /* ++ * 1. Allocate resources: ++ * a. X,Y buffers ++ * b. ETD (& matching DMA) ++ * 2. Set the registers ++ */ ++ UNLESS ((x = get_data_buff(fs_hcpriv)) && (y = get_data_buff(fs_hcpriv)) && (0 <= (etdn = get_etd(fs_hcpriv)))) { ++ // Resource allocation failure, retry later is OK. ++ TRACE_MSG0(HCD,"resource allocation failure -> resubmit OK"); ++ y = rel_data_buff(fs_hcpriv,y); ++ x = rel_data_buff(fs_hcpriv,x); ++ return -2; ++ } ++ ++ // OK, we've got all we need. ++ td.w1.bufsrtad.y = data_buff_boff(y); ++ td.w1.bufsrtad.x = data_buff_boff(x); ++ ++ switch (format) { ++ case PIPE_CONTROL: ++ fmt = ETD_FORMAT_CONTROL; ++ dir = ETD_DIRECT_TD00; ++ ++ /* build setup data/status phase ETD for use after completion of 8 byte setup packet ++ */ ++ fs_hcpriv->sdp_data[etdn] = data; // address of data phase buffer. ++ data = (void *) other; // address of setup packet. ++ sdp = &fs_hcpriv->sdp_etd[etdn]; ++ memset((void*)sdp, 0, sizeof(endpoint_transfer_descriptor)); ++ ++ /* toggle is constant 0 for setup packet, and 1 for data and status phases, ++ * so toggle was overloaded to handle data phase direction. If len == 0, there ++ * is no data phase, so direction is IN. ++ */ ++ sdp_out = (toggle && len > 0); ++ sdp->td.w1.val = td.w1.val; ++ sdp->td.w2.cb.flags = ETD_SET_DATATOGL((0x2|1)) | // toggle is always 1, for data or status phases. ++ ETD_SET_DELAYINT(0) | // when done, interrupt ASAP (wait for 0 frames) ++ ETD_SET_BUFROUND(1) | // Allow short recv xfers ++ ETD_SET_DIRPID((sdp_out?ETD_DIRPID_OUT:ETD_DIRPID_IN)); ++ sdp->td.w2.cb.reserved = 0; ++ sdp->td.w2.cb.rtrydelay = 1; // Number of frames to wait before retry on failure. ++ sdp->td.w3.val = ETD_SET_BUFSIZE(sizeof(fs_data_buff)-1) | ETD_SET_TOTBYECNT(len); ++ ++ /* build SETUP ETD for initial 8 byte setup packet ++ */ ++ len = 8; ++ td.w2.cb.flags = ETD_SET_DATATOGL((0x2|0)) | // starting toggle is here, not TOGCRY, and is always 0. ++ ETD_SET_DELAYINT(0) | // when done, interrupt ASAP (wait for 0 frames) ++ ETD_SET_BUFROUND(1) | // Allow short recv xfers ++ ETD_SET_DIRPID(pid); ++ td.w2.cb.reserved = 0; ++ td.w2.cb.rtrydelay = 1; // Number of frames to wait before retry on failure. ++ td.w3.val = (ETD_SET_BUFSIZE(sizeof(fs_data_buff)-1) | ETD_SET_TOTBYECNT(len)); ++ ++ fs_hcpriv->etd_urb_state[etdn] = ETD_URB_SETUP_START; ++ ++ //TRACE_MSG3(HCD,"SETUP pkt, len=%d : %08x : %08x",len,*((u32*)(void*)data),*(1+(u32*)(void*)data)); ++ ++ /* Complete the setup data phase ETD. ++ */ ++ sdp->epd = ETD_SET_MAXPKTSIZ(maxps) | ++ ETD_SET_TOGCRY(0) | ++ ETD_SET_FORMAT(ETD_FORMAT_BULK) | ++ ETD_SET_SPEED((slow?ETD_SPEED_LOW:ETD_SPEED_FULL)) | ++ ETD_SET_DIRECT(ETD_DIRECT_TD00) | ++ ETD_SET_ENDPNT(endpoint) | ++ ETD_SET_ADDRESS(address); ++ ++ break; ++ ++ case PIPE_BULK: ++ fmt = ETD_FORMAT_BULK; ++ dir = ETD_DIRECT_TD00; ++ ++ //TRACE_MSG1(HCD,"BULK, starting toggle %x",toggle); ++ td.w2.cb.flags = ETD_SET_DATATOGL((0x2|toggle)) | // starting toggle is here, not TOGCRY ++ ETD_SET_DELAYINT(0) | // when done, interrupt ASAP (wait for 0 frames) ++ ETD_SET_BUFROUND(1) | // Allow short recv xfers ++ ETD_SET_DIRPID(pid); ++ td.w2.cb.reserved = 0; ++ td.w2.cb.rtrydelay = 0; // Number of frames to wait before retry on failure. ++ ++ /* Set the state to indicate if a trailing ZLP is required. ++ */ ++ fs_hcpriv->etd_urb_state[etdn] = ((other && out && 0 == ++ (len % maxps)) ? ETD_URB_BULKWZLP_START : ETD_URB_BULK_START); ++ break; ++ ++ case PIPE_INTERRUPT: ++ fmt = ETD_FORMAT_INTERRUPT; ++ dir = ETD_DIRECT_TD00; ++ ++ //TRACE_MSG1(HCD,"INTERRUPT, starting toggle %x",toggle); ++ td.w2.intr.flags = ETD_SET_DATATOGL((0x2|toggle)) | // starting toggle is here, not TOGCRY ++ ETD_SET_DELAYINT(0) | // when done, interrupt ASAP (wait for 0 frames) ++ ETD_SET_BUFROUND(1) | // Allow short recv xfers ++ ETD_SET_DIRPID(pid); ++ ++ td.w2.intr.relpolpos = (fs_rl(OTG_HOST_FRM_NUM) + 1) & 0xff; // Start next frame ++ ++ td.w2.intr.polinterv = other & 0xff; ++ fs_hcpriv->etd_urb_state[etdn] = ETD_URB_INTERRUPT_START; ++ break; ++ ++ case PIPE_ISOCHRONOUS: ++ fmt = ETD_FORMAT_ISOC; ++ dir = out ? ETD_DIRECT_OUT : ETD_DIRECT_IN; ++ td.w2.isoc.startfrm = (fs_rl(OTG_HOST_FRM_NUM) + 1) & 0xffff; // next frame ++ if (len > 1023) { ++ // Two frames needed, send 1023 in the first, remainder in the second. ++ td.w2.isoc.flags = ETD_SET_DELAYINT(0) | ETD_SET_AUTOISO(0) | ETD_SET_FRAMECNT(1); ++ td.w3.isoc.pkt0 = ETD_SET_PKTLEN(1023); ++ td.w3.isoc.pkt1 = ETD_SET_PKTLEN(len-1023); ++ } ++ else { ++ // Only one frame needed, send it all at once. ++ td.w2.isoc.flags = ETD_SET_DELAYINT(0) | ETD_SET_AUTOISO(0) | ETD_SET_FRAMECNT(0); ++ td.w3.isoc.pkt0 = ETD_SET_PKTLEN(len); ++ td.w3.isoc.pkt1 = 0; ++ } ++ fs_hcpriv->etd_urb_state[etdn] = ETD_URB_ISOC_START; ++ break; ++ } ++ ++ fs_wl(etd_word(etdn, 0), ETD_SET_MAXPKTSIZ(maxps) | ETD_SET_TOGCRY(0) | ETD_SET_FORMAT(fmt) | ++ ETD_SET_SPEED((slow ? ETD_SPEED_LOW : ETD_SPEED_FULL)) | ++ ETD_SET_DIRECT(dir) | ETD_SET_ENDPNT(endpoint) | ETD_SET_ADDRESS(address)); ++ ++ fs_wl(etd_word(etdn, 1), td.w1.val); ++ fs_wl(etd_word(etdn, 2), td.w2.val); ++ fs_wl(etd_word(etdn, 3), td.w3.val); ++ ++ //TRACE_MSG5(HCD, "ETD[%02d] %08x %08x %08x %08x", etdn, ++ // fs_rl(etd_word(etdn, 0)), fs_rl(etd_word(etdn, 1)), ++ // fs_rl(etd_word(etdn, 2)), fs_rl(etd_word(etdn, 3))); ++ ++ start_etd(etdn, len, data, out); ++ return etdn; ++} ++ ++/*! ++ * finish_urb_irq() - called to complete transfer ++ * @param fs_hcpriv ++ * @param etdn ++ * @param killed ++ */ ++STATIC void finish_urb_irq(fs_hcpriv *fs_hcpriv, int etdn, int killed) ++{ ++ int cc = 0; ++ int next_toggle = 0; ++ int format = 0; ++ u32 remaining = 0; ++ fs_data_buff *x,*y; ++ endpoint_transfer_descriptor res; ++ ++ TRACE_MSG5(HCD, "etdn=%d state=%d %s nus=%u %s", etdn, fs_hcpriv->etd_urb_state[etdn], ++ etd_urb_state_name[fs_hcpriv->etd_urb_state[etdn]], num_urbs_started, (killed?"KILLED":"")); ++ ++ if (monitoring_etd_mask == ETD_MASK(etdn)) ++ monitoring_etd_mask = 0; ++ ++ ++ /* Disable/Abort DMA. R1: sec 23.13.16 pg 23-103 ++ * (supposed to auto-clear on DMA completion, but this shouldn't hurt). ++ */ ++ fs_wl(OTG_DMA_ETD_CH_CLR, ETD_MASK(etdn)); ++ ++ if (fs_rl(OTG_HOST_ETD_EN) & ETD_MASK(etdn)) ++ fs_andl(OTG_HOST_ETD_EN, ~ETD_MASK(etdn)); // This register IS write 0 to disable. Really. ++ ++ if ( fs_rl(OTG_HOST_ETD_DONE) & ETD_MASK(etdn)) ++ fs_andl(OTG_HOST_ETD_DONE, ~ETD_MASK(etdn)); // This register IS write 0 to disable. Really. ++ ++ // Hardware should have disabled ETD, but it doesn't hurt to check. ++ if (fs_rl(OTG_HOST_ETD_EN) & ETD_MASK(etdn)) ++ fs_wl(OTG_HOST_ETD_EN, ETD_MASK(etdn)); // This register IS write 1 to clear. Really. ++ ++ if (fs_rl(OTG_HOST_XINT_STAT) & ETD_MASK(etdn)) ++ fs_wl(OTG_HOST_XINT_STAT, ETD_MASK(etdn)); // This register IS write 1 to clear. Really. ++ ++ if (fs_rl(OTG_HOST_YINT_STAT) & ETD_MASK(etdn)) ++ fs_wl(OTG_HOST_YINT_STAT, ETD_MASK(etdn)); // This register should be write 1 to clear. ++ ++ ++ if (fs_rl(OTG_HOST_XYINT_STEN) & ETD_MASK(etdn)) ++ fs_andl(OTG_HOST_XYINT_STEN, ~ETD_MASK(etdn)); // This register should be write 0 to disable. ++ ++ if (fs_rl(OTG_HOST_XFILL_STAT) & ETD_MASK(etdn)) ++ fs_wl(OTG_HOST_XFILL_STAT, ETD_MASK(etdn)); // This register IS write 1 to clear. Really. ++ ++ if (fs_rl(OTG_HOST_YFILL_STAT) & ETD_MASK(etdn)) ++ fs_wl(OTG_HOST_YFILL_STAT, ETD_MASK(etdn)); // This register should be write 1 to clear. ++ ++ ++ // Extract results ++ res.epd = fs_rl(etd_word(etdn, 0)); ++ res.td.w1.val = fs_rl(etd_word(etdn, 1)); ++ res.td.w2.val = fs_rl(etd_word(etdn, 2)); ++ res.td.w3.val = fs_rl(etd_word(etdn, 3)); ++ ++ fs_wl(OTG_HOST_EP_DSTAT, ETD_MASK(etdn)); ++ ++ //TRACE_MSG1(HCD,"OTG_HOST_ETD_DONE: %08x", fs_rl(OTG_HOST_ETD_DONE)); ++ ++ if ((ETD_URB_BULK_START == fs_hcpriv->etd_urb_state[etdn] || ++ (ETD_URB_BULKWZLP_START == fs_hcpriv->etd_urb_state[etdn])) && ++ (ETD_GET_DIRPID(res.td.w2.cb.flags) != ETD_DIRPID_IN) ++ ) ++ TRACE_MSG4(HCD,"RES: epd: %08x w1: %08x w2: %08x w3: %08x", res.epd,res.td.w1.val,res.td.w2.val,res.td.w3.val); ++ ++ if (ETD_GET_HALTED(res.epd)) ++ TRACE_MSG0(HCD,"endpoint HALTED"); ++ ++ format = ETD_GET_FORMAT(res.epd); ++ ++ switch (fs_hcpriv->etd_urb_state[etdn]) { ++ case ETD_URB_SETUP_START: ++ /* Just finished the setup packet. ++ * Go to data or status phase of setup packet while we still have the ETD and data buffers allocated. ++ */ ++ if (!killed && ETD_CC_NOERROR == (cc = ETD_GET_COMPCODE(res.td.w2.cb.flags))) { ++ ++ endpoint_transfer_descriptor *sdp = &fs_hcpriv->sdp_etd[etdn]; ++ int len = ETD_GET_TOTBYECNT(sdp->td.w3.val); ++ void *data = fs_hcpriv->sdp_data[etdn]; ++ fs_hcpriv->etd_urb_state[etdn] = ((len > 0) ? ETD_URB_SETUP_DATA : ETD_URB_SETUP_STATUS); ++ ++ // Copy into the active ETD and start it. ++ fs_wl(etd_word(etdn, 0), sdp->epd); ++ fs_wl(etd_word(etdn, 1), sdp->td.w1.val); ++ fs_wl(etd_word(etdn, 2), sdp->td.w2.val); ++ fs_wl(etd_word(etdn, 3), sdp->td.w3.val); ++ // watch out for ETD_DIRPID_SETUP ++ start_etd(etdn, len,data,(ETD_GET_DIRPID(sdp->td.w2.cb.flags)!=ETD_DIRPID_IN)); ++ TRACE_MSG1(HCD,"SETUP %s Phase started",((len > 0) ? "DATA" : "STATUS")); ++ return; ++ } ++ /* Something is wrong, finish the URB as is. ++ */ ++ remaining = ETD_GET_TOTBYECNT(fs_rl(etd_word(etdn, 3))); ++ format = PIPE_CONTROL; ++ break; ++ ++ case ETD_URB_SETUP_DATA: ++ /* Go to status phase of setup packet while we still have the ETD and data buffers allocated. ++ * Save the data phase length for after status. ++ */ ++ if (!killed && ETD_CC_NOERROR == (cc = ETD_GET_COMPCODE(res.td.w2.cb.flags))) { ++ endpoint_transfer_descriptor *sdp = &fs_hcpriv->sdp_etd[etdn]; ++ fs_hcpriv->sdp_data[etdn] = (void *) ETD_GET_TOTBYECNT(res.td.w3.val); ++ fs_hcpriv->etd_urb_state[etdn] = ETD_URB_SETUP_STATUS; ++ ++ /* Rebuild data phase sdp for status phase with opposite direction, and 0 length. ++ */ ++ ++ sdp->td.w2.cb.flags = ETD_FLIP_DIRPID(sdp->td.w2.cb.flags); ++ sdp->td.w3.val = (ETD_SET_BUFSIZE(sizeof(fs_data_buff)-1) | ETD_SET_TOTBYECNT(0)); ++ ++ /* Copy into the active ETD and start it. ++ */ ++ fs_wl(etd_word(etdn, 0), sdp->epd); ++ fs_wl(etd_word(etdn, 1), sdp->td.w1.val); ++ fs_wl(etd_word(etdn, 2), sdp->td.w2.val); ++ fs_wl(etd_word(etdn, 3), sdp->td.w3.val); ++ start_etd(etdn, 0,NULL,(ETD_GET_DIRPID(sdp->td.w2.cb.flags)!=ETD_DIRPID_IN)); ++ TRACE_MSG0(HCD,"SETUP STATUS Phase started"); ++ return; ++ } ++ // Something is wrong, finish the URB as is. ++ remaining = ETD_GET_TOTBYECNT(res.td.w3.val); ++ format = PIPE_CONTROL; ++ break; ++ ++ case ETD_URB_SETUP_STATUS: ++ cc = ETD_GET_COMPCODE(res.td.w2.cb.flags); ++ // Fetch saved length. ++ remaining = (u32) fs_hcpriv->sdp_data[etdn]; ++ format = PIPE_CONTROL; ++ break; ++ ++ case ETD_URB_BULKWZLP_START: ++ // Need a trailing ZLP. ++ next_toggle = ETD_GET_TOGCRY(res.epd); ++ if (!killed && ETD_CC_NOERROR == ETD_GET_COMPCODE(res.td.w2.cb.flags)) { ++ // Since TOTBYECNT should be 0, re-enabling the same ETD ought to give a ZLP. ++ fs_hcpriv->etd_urb_state[etdn] = ETD_URB_BULKWZLP; ++ // Save the data phase length for after ZLP. ++ // fs_hcpriv->sdp_data[etdn] = (void *) ETD_GET_TOTBYECNT(res.td.w3.val); ++ // FIXME - verify toggle OK ++ ++ start_etd(etdn, 0,NULL,TRUE/*always OUT*/); ++ ++ TRACE_MSG0(HCD,"ZLP started"); ++ return; ++ } ++ // Something is wrong, finish the URB as is. ++ remaining = ETD_GET_TOTBYECNT(res.td.w3.val); ++ format = PIPE_BULK; ++ break; ++ ++ case ETD_URB_BULKWZLP: ++ // Trailing ZLP now finished. ++ cc = ETD_GET_COMPCODE(res.td.w2.cb.flags); ++ next_toggle = ETD_GET_TOGCRY(res.epd); ++ // Fetch saved length (it was zero, or we wouldn't be here). ++ remaining = 0; ++ format = PIPE_BULK; ++ break; ++ ++ case ETD_URB_BULK_START: ++ cc = ETD_GET_COMPCODE(res.td.w2.cb.flags); ++ next_toggle = ETD_GET_TOGCRY(res.epd); ++ remaining = ETD_GET_TOTBYECNT(res.td.w3.val); ++ format = PIPE_BULK; ++ break; ++ ++ case ETD_URB_INTERRUPT_START: ++ cc = ETD_GET_COMPCODE(res.td.w2.intr.flags); ++ remaining = ETD_GET_TOTBYECNT(res.td.w3.val); ++ format = PIPE_INTERRUPT; ++ break; ++ ++ case ETD_URB_ISOC_START: ++ // This probably needs more work, best guess only here. ++ remaining = ETD_GET_PKTLEN(res.td.w3.isoc.pkt0); ++ cc = ETD_GET_COMPCODE(res.td.w3.isoc.pkt0); ++ if (ETD_GET_FRAMECNT(res.td.w2.isoc.flags)) { ++ // There were two packets in this transfer ++ remaining += ETD_GET_PKTLEN(res.td.w3.isoc.pkt1); ++ } ++ format = PIPE_ISOCHRONOUS; ++ } ++ ++ fs_hcpriv->etd_urb_state[etdn] = ETD_URB_COMPLETED; ++ ++ /* Convert completion code to HW independent value. ++ */ ++ if (killed) { ++ cc = -ENOENT; ++ remaining = 0; ++ } ++ else ++ cc = comp_code_to_status(cc); ++ ++ /* Return ETD and X,Y buffers to free list. ++ */ ++ y = data_buff_addr(res.td.w1.bufsrtad.y); ++ x = data_buff_addr(res.td.w1.bufsrtad.x); ++ y = rel_data_buff(fs_hcpriv,y); ++ x = rel_data_buff(fs_hcpriv,x); ++ rel_etd(fs_hcpriv, etdn); ++ ++ /* Forward results to the layers above. ++ */ ++ //TRACE_MSG4(HCD,"completing urb on ETD %d cc=%d remaining=%d next_toggle=%d", etdn, cc, remaining,next_toggle); ++ hcd_transfer_complete(fs_hcpriv->bus_hcpriv, etdn, format, cc, remaining, next_toggle); ++} ++ ++/*! ++ * hcd_hw_unlink_urb() - unlink urb ++ * Called in irq_lock ++ * @param bus_hcpriv ++ * @param u ++ */ ++int hcd_hw_unlink_urb(struct bus_hcpriv *bus_hcpriv, int u) ++{ ++ unsigned long flags; ++ struct fs_hcpriv *fs_hcpriv; ++ RETURN_EINVAL_UNLESS ((fs_hcpriv = (struct fs_hcpriv *) bus_hcpriv->hw_hci)); ++ local_irq_save(flags); ++ finish_urb_irq(fs_hcpriv, u, TRUE); ++ local_irq_restore(flags); ++ return 0; ++} ++ ++/*! ++ * hcd_hw_frame_number() - return current frame number ++ * @param bus_hcpriv ++ * @return u32 ??? ++ */ ++u32 hcd_hw_frame_number(struct bus_hcpriv *bus_hcpriv) ++{ ++ return(fs_rl(OTG_HOST_FRM_NUM)); ++} ++ ++/* ********************************************************************************************* */ ++ ++/*! ++ * MX21_PORT_CHANGED() - send otg event information to state machine ++ * @param name ++ * @param cs ++ * @param changed ++ * @param status ++ * @param set ++ * @param reset ++ */ ++void MX21_PORT_CHANGED(char *name, u32 cs, u32 changed, u32 status, u64 set, u64 reset) ++{ ++ RETURN_UNLESS(cs & changed); ++ TRACE_MSG2(HCD, "%s%s", name, (cs & (1 << status)) ? "" : "/"); ++ if (cs & (1 << status) && set) ++ otg_event(hcd_instance->otg, set, HCD, name); ++ if (!(cs & (1 << status)) && reset) ++ otg_event(hcd_instance->otg, reset, HCD, name); ++} ++ ++/*! ++ * update_shadow_rh() - Update shadow _status_ info (change info already updated). ++ * @param bus_hcpriv ++ * @param port_num - 1-N based port number ++ * @param cs ++ * ++ */ ++STATIC void update_shadow_rh(struct bus_hcpriv *bus_hcpriv, int port_num, u32 cs) ++{ ++ u32 msk_and = 0; // bits to turn off ++ u32 msk_or = 0; // bits to turn on ++ ++ TRACE_MSG2(HCD,"port: %d cs: %08x", port_num, cs); ++ ++ /* PORT_STATUS_PRTRSTSC (20) - PORT_RESET (4) changed ++ * PORT_RESET (4) status change, end of reset, PORT_ENABLE may (should) be on. ++ */ ++ MX21_PORT_CHANGED("PORT_RESET", cs, PORT_STATUS_PRTRSTSC, PORT_RESET, ++ BUS_RESET, BUS_RESET_); ++ ++ if (cs & (1 << C_PORT_RESET)) { ++ TRACE_MSG1(HCD,"port: %d PORT_RESET complete (s.b. enabled)", port_num); ++ if (cs & (1 << PORT_ENABLE)) { ++ TRACE_MSG1(HCD,"port: %d enabled", port_num); ++ //otg_write_info_message("port update C_PORT_RESET"); ++ msk_or |= (1 << PORT_ENABLE); ++ } ++ else { ++ TRACE_MSG1(HCD,"port: %d NOT enabled!!!", port_num); ++ //otg_write_info_message("update C_PORT_RESET/"); ++ } ++ ++ /* Clear the PORT_RESET bit too, since it's over. ++ */ ++ msk_and |= (1 << PORT_RESET); ++ bus_hcpriv->rh_hcpriv->hub_port_change_status |= (1 << port_num); ++ } ++ ++ /* PORT_OVER_CURRENT (3) bit has changed (either direction). ++ * PORT_STATUS_OVRCURIC (19) - PORT_OVER_CURRENT (3) changed ++ */ ++ MX21_PORT_CHANGED("PORT_OVER_CURRENT", cs, PORT_STATUS_OVRCURIC, PORT_OVER_CURRENT, (u64) 0, (u64) 0); ++ if (cs & (1 << C_PORT_OVER_CURRENT)) { ++ /* OVER_CURRENT has come on. ++ */ ++ if (cs & (1 << PORT_OVER_CURRENT)) { ++ TRACE_MSG1(HCD,"port: %d over current ON", port_num); ++ msk_or |= (1 << PORT_OVER_CURRENT); ++ } ++ /* OVER_CURRENT has gone away. ++ */ ++ else { ++ TRACE_MSG1(HCD,"port: %d over current OFF", port_num); ++ msk_and |= (1 << PORT_OVER_CURRENT); ++ } ++ bus_hcpriv->rh_hcpriv->hub_port_change_status |= (1 << port_num); ++ } ++ ++ /* PORT_SUSPEND (2) Resume sequence has completed.... ++ * PORT_STATUS_PRTSTATSC (18) - PORT_SUSPEND (2) changed ++ */ ++ MX21_PORT_CHANGED("PORT_SUSPEND", cs, PORT_STATUS_PRTSTATSC, PORT_SUSPEND, ++ BUS_SUSPENDED, BUS_SUSPENDED_); ++ ++ if (cs & (1 << C_PORT_SUSPEND)) { ++ // FIXME - figure this out when we get to suspend/resume.... ++ ++ } ++ ++ ++ /* PORT_ENABLE (1) has been __cleared__ by some HW event ++ * PORT_STATUS_PRTENBLSC (17) - PORT_ENABLE (1) changed ++ */ ++ MX21_PORT_CHANGED("PORT_ENABLE", cs, PORT_STATUS_PRTENBLSC, PORT_ENABLE, (u64) 0, (u64) 0); ++ if (cs & (1 << C_PORT_ENABLE)) { ++ //otg_write_info_message("update C_PORT_ENABLE"); ++ TRACE_MSG1(HCD,"port: %d disabled (by HW)", port_num); ++ if (!(cs & (1 << PORT_ENABLE))) ++ msk_and |= ( 1 << PORT_ENABLE); ++ bus_hcpriv->rh_hcpriv->hub_port_change_status |= (1 << port_num); ++ } ++ ++ /* PORT_CONNECTION (0) bit has changed (either direction). ++ * PORT_STATUS_CONNECTSC (16) - PORT_CONNECTION (0) changed ++ */ ++ MX21_PORT_CHANGED("PORT_CONNECTION", cs, PORT_STATUS_CONNECTSC, PORT_CONNECTION, ++ HUB_PORT_CONNECT, HUB_PORT_CONNECT_ ); ++ ++ if (cs & (1 << C_PORT_CONNECTION)) { ++ /* CONNECT has come on. ++ */ ++ if (cs & (1 << PORT_CONNECTION)) { ++ TRACE_MSG1(HCD,"port: %d new connection", port_num); ++ msk_or |= (1 << PORT_CONNECTION); ++ if (cs & (1 << PORT_LOW_SPEED)) { ++ TRACE_MSG1(HCD,"port: %d low speed device", port_num); ++ msk_or |= (1 << PORT_LOW_SPEED); ++ } ++ //otg_write_info_message("update PORT_CONNECTION"); ++ //printk(KERN_INFO"%s: AAA\n", __FUNCTION__); ++ //otg_event(hcd_instance->otg, HUB_PORT_CONNECT, HCD, "HUB_PORT_CONNECT (mx21 hw)"); ++ //printk(KERN_INFO"%s: BBB\n", __FUNCTION__); ++ } ++ /* CONNECT has gone away. ++ */ ++ else { ++ TRACE_MSG1(HCD,"port: %d disconnection", port_num); ++ //otg_write_info_message("update PORT_CONNECTION/"); ++ msk_and |= (1 << PORT_CONNECTION) | (1 << PORT_LOW_SPEED); ++ //otg_event(hcd_instance->otg, HUB_PORT_CONNECT_, HCD, "HUB_PORT_CONNECT/ (mx21 hw)"); ++ } ++ bus_hcpriv->rh_hcpriv->hub_port_change_status |= (1 << port_num); ++ //printk(KERN_INFO"%s: CCC\n", __FUNCTION__); ++ } ++ ++ TRACE_MSG5(HCD, "port: %d shadow port_change_status: ((%08x & ~%04x) | %04x) hpcs: %04x", ++ port_num, bus_hcpriv->rh_hcpriv->port_change_status[port_num - 1], ++ msk_and, msk_or, bus_hcpriv->rh_hcpriv->hub_port_change_status); ++} ++ ++/* ********************************************************************************************* */ ++static int num_host_interrupts = 0; ++#define MAX_HOST_INTERRUPTS 0 ++static int num_psc_interrupts = 0; ++#define MAX_PSC_INTERRUPTS 0 ++ ++extern struct bus_hcpriv fs_bus_hcpriv; ++ ++/*! ++ * hcd_hw_int_hndlr() - interrupt handler for hcd controller ++ * @param irq ++ * @param dev_id ++ * @param regs ++ */ ++irqreturn_t hcd_hw_int_hndlr(int irq, void *dev_id, struct pt_regs *regs) ++{ ++ struct bus_hcpriv *bus_hcpriv = (struct bus_hcpriv *) &fs_bus_hcpriv; ++ struct fs_hcpriv *fs_hcpriv = (struct fs_hcpriv *) bus_hcpriv->hw_hci; ++ u32 host_sint; // C.f. 23.11.11 Host Interrupt Register ++ ++ if (OTG_USBDMA == irq) { ++ // HOST DMA error interrupt ++ u32 err_stat = fs_rl(OTG_DMA_ETD_ERR); ++ TRACE_MSG1(HCD,"host DMA interrupt, error status %08x",err_stat); ++ if (0 != err_stat) { ++ // Disable the matching DMA channels and clear the interrupt ++ fs_wl(OTG_DMA_ETD_CH_CLR, err_stat); ++ fs_wl(OTG_DMA_ETD_ERR, err_stat); ++ } ++ // local_irq_restore(flags); ++ return IRQ_HANDLED; ++ } ++ ++ /* FIXME - should check fs_hcpriv->mm->otg.OTG_Module_Interrupt_Status & (0x1 << 3) | 0x1; ++ * for Host (async + regular) Interrupt - enable clock on async. ++ */ ++ ++ num_host_interrupts += 1; ++ if (MAX_HOST_INTERRUPTS > 0 && num_host_interrupts > MAX_HOST_INTERRUPTS) { ++ // Host (async + regular) Interrupt Disable ++ ++ fs_wl(OTG_HOST_SINT_STEN, 0); ++ fs_andl(OTG_CORE_CINT_STEN, ~(/*(0x1 << 3) |*/ 0x1)); ++ } ++ ++ while ((host_sint = fs_rl(OTG_HOST_SINT_STAT))) { ++ ++ while (HOST_DONEINT & host_sint) { ++ u32 etds_done; ++ ++ TRACE_MSG1(HCD,"HOST_DONEINT: %08x", fs_rl(OTG_HOST_EP_DSTAT)); ++ fs_wl(OTG_HOST_SINT_STAT, HOST_DONEINT); ++ host_sint = fs_rl(OTG_HOST_SINT_STAT); ++ while ((etds_done = fs_rl(OTG_HOST_EP_DSTAT))) ++ finish_urb_irq(fs_hcpriv, fls(etds_done) - 1, FALSE); ++ } ++ ++ host_sint = fs_rl(OTG_HOST_SINT_STAT); ++ ++ /* Start Of Frame. ++ * Note: this interrupt seems to happen even if disabled when there is a frame number overflow. ++ */ ++ if (host_sint & HOST_SOFINT) { ++ if (0 == (fs_hcpriv->sof_count & (4096 - 1))) { ++ TRACE_MSG1(HCD,"SOF %08lx",fs_hcpriv->sof_count); ++ } ++ fs_hcpriv->sof_count += 1; ++ if (monitoring_etd_mask != 0) { ++ // This ETD needs to be watched.... ++ current_etd_frame += 1; ++ ++ done_flags[current_etd_frame] = fs_rl(OTG_HOST_ETD_EN); ++ done_status[current_etd_frame] = fs_rl(OTG_HOST_EP_DSTAT); ++ ++ mon_etd[current_etd_frame].epd = fs_rl(etd_word(monitoring_etd_num, 0)); ++ mon_etd[current_etd_frame].td.w1.val = fs_rl(etd_word(monitoring_etd_num, 1)); ++ mon_etd[current_etd_frame].td.w2.val = fs_rl(etd_word(monitoring_etd_num, 2)); ++ mon_etd[current_etd_frame].td.w3.val = fs_rl(etd_word(monitoring_etd_num, 3)); ++ ++ if (current_etd_frame >= MAX_ETD_FRAMES) { ++ // This has taken too long! ++ int i,u; ++ u = ((mon_etd[0].td.w3.val == mon_etd[1].td.w3.val) && ++ (mon_etd[1].td.w3.val == mon_etd[2].td.w3.val) && ++ (mon_etd[2].td.w3.val == mon_etd[3].td.w3.val)); ++ ++ TRACE_MSG5(HCD,"SOF: OUT urb TIMEOUT at %d/%d frames, mask: %08x num=%u u=%d", ++ current_etd_frame,MAX_ETD_FRAMES,monitoring_etd_mask,monitoring_etd_num,u); ++ ++ for (i = 0; i <= MAX_ETD_FRAMES; i++) ++ TRACE_MSG7(HCD,"SOF: %d epd: %08x w1: %08x w2: %08x w3: %08x ef: %08x st: %08x", ++ i, mon_etd[i]. epd, mon_etd[i]. td.w1.val, mon_etd[i].td.w2.val, ++ mon_etd[i].td.w3.val, done_flags[i], done_status[i]); ++ // Disable monitoring by killing the urb. FUTURE, look into restarting it. ++ finish_urb_irq(fs_hcpriv, monitoring_etd_num,TRUE); ++ } ++ } ++ } ++ ++ /* Frame Number Overflow. ++ */ ++ if (host_sint & HOST_FMOFINT) { ++ //TRACE_MSG0(HCD,"Frame Number Overflow"); ++ //printk(KERN_ERR "%s: Frame Number Overflow\n",__FUNCTION__); ++ } ++ ++ /* Port Status Change. ++ */ ++ if (host_sint & HOST_PSCINT) { ++ int port_num; ++ num_psc_interrupts += 1; ++ ++ /* Clear the interrupt by clearing the port(s) in question. ++ * Reflect any changes in the shadow values. ++ */ ++ for (port_num = 1; port_num <= bus_hcpriv->num_ports; port_num++) { ++ ++ u32 cs = fs_rl(fs_host_port_stat(port_num)); ++ u32 cm = cs & 0xFFFF0000; ++ ++ CONTINUE_UNLESS (cm); ++ fs_wl(fs_host_port_stat(port_num), cm); ++ bus_hcpriv->rh_hcpriv->port_change_status[port_num - 1] |= cm; ++ update_shadow_rh(bus_hcpriv, port_num, cs); ++ } ++ ++ /* Tell the RH to scan for changes in shadow data. (schedule bottom half handler) ++ */ ++ hcd_rh_int_hndlr(irq,bus_hcpriv,NULL); ++ } ++ ++ /* Overrun ++ */ ++ if (host_sint & (HOST_SORINT | HOST_HERRINT)) { ++ if (host_sint & HOST_SORINT) { ++ TRACE_MSG0(HCD,"Scheduling Overrun"); ++ printk(KERN_ERR "%s: Scheduling Overrun\n",__FUNCTION__); ++ } ++ if (host_sint & HOST_HERRINT) { ++ TRACE_MSG0(HCD,"Host Scheduling Error"); ++ printk(KERN_ERR "%s: Host Scheduling Error\n",__FUNCTION__); ++ } ++ } ++ /* Resume Detected. ++ */ ++ if (host_sint & HOST_RESDETINT) { ++ TRACE_MSG0(HCD,"Resume Detected"); ++ } ++ /* Clear by writing back the ones we've checked for since the last read. ++ */ ++ fs_wl(OTG_HOST_SINT_STAT, host_sint & (HOST_RESDETINT | HOST_FMOFINT | HOST_SORINT | ++ HOST_HERRINT | HOST_PSCINT | HOST_SOFINT)); ++ } ++ return IRQ_HANDLED; ++} ++ ++/* ********************************************************************************************* */ ++ ++ ++/*! ++ * hcd_hw_rh_hub_attributes() - get root hub attributes ++ * These provide access to the real root hub. ++ * @param bus_hcpriv ++ */ ++u16 hcd_hw_rh_hub_attributes(struct bus_hcpriv *bus_hcpriv) ++{ ++ // R1: sec 23.11.27 ++ return 0xFFFF & (fs_rl(OTG_HOST_ROOTHUB_DESCA) >> 8); ++} ++ ++/*! ++ * hcd_hw_rh_power_delay() - get power delay attributes ++ * These provide access to the real root hub. ++ * @param bus_hcpriv ++ */ ++u8 hcd_hw_rh_power_delay(struct bus_hcpriv *bus_hcpriv) ++{ ++ // R1: sec 23.11.27 ++ return fs_rl(OTG_HOST_ROOTHUB_DESCA) >> 24; ++} ++ ++/*! ++ * hcd_hw_rh_contr_current() - get contr current attributes ++ * These provide access to the real root hub. ++ * @param bus_hcpriv ++ */ ++u8 hcd_hw_rh_hub_contr_current(struct bus_hcpriv *bus_hcpriv) ++{ ++ return 0x0; ++} ++ ++/*! ++ * hcd_hw_rh_DeviceRemovalbe() - get DeviceRemovalbe attributes ++ * These provide access to the real root hub. ++ * @param bus_hcpriv ++ */ ++u8 hcd_hw_rh_DeviceRemovable(struct bus_hcpriv *bus_hcpriv) ++{ ++ // USB2.0 pg 418 ==> bit 0 reserved, 1..7 for ports ++ // R1: sec 23.11.28 ++ return 0xFF & fs_rl(OTG_HOST_ROOTHUB_DESCB); ++} ++ ++/*! ++ * hcd_hw_rh_PortPwrCtrlMask() - get DeviceRemovalbe attributes ++ * These provide access to the real root hub. ++ * @param bus_hcpriv ++ */ ++u8 hcd_hw_rh_PortPwrCtrlMask(struct bus_hcpriv *bus_hcpriv) ++{ ++ // USB2.0 pg 418 ==> all 1s??? QQSV ++ // R1: sec 23.11.28 ++ return 0xFF & fs_rl(OTG_HOST_ROOTHUB_DESCB); ++} ++ ++ ++/*! ++ * hcd_hw_rh_get_hub_change_status() - get change status ++ * These provide access to the real root hub. ++ * @param bus_hcpriv ++ */ ++u32 hcd_hw_rh_get_hub_change_status(struct bus_hcpriv *bus_hcpriv) ++{ ++ // R1: sec 23.11.29 pg 23-62, USB2.0 11.24.2.6 pg 425 ++ return fs_rl(OTG_HOST_ROOTHUB_STATUS & (ROOTHUB_STATUS_LOCPWRSC | ROOTHUB_STATUS_DEVCONWUE | ++ ROOTHUB_STATUS_OVRCURI | ROOTHUB_STATUS_LOCPWRS)); ++} ++ ++/*! ++ * hcd_hw_rh_hub_feature() - get hub feature ++ * These provide access to the real root hub. ++ * @param bus_hcpriv ++ * @param feat_selector ++ * @param set_flag ++ */ ++void hcd_hw_rh_hub_feature(struct bus_hcpriv *bus_hcpriv, int feat_selector, int set_flag) ++{ ++ if (set_flag) { ++ // SET feature USB2.0 11.24.2.12 pg 434 and USB2.0 11.24.2 tbl 11-17 pg 421 ++ // R1: sec 23.11.29 pg 23-62 ++ switch (feat_selector) { ++ // case C_HUB_OVER_CURRENT: MX21 doesn't support SW setting the over current feature ++ // case C_HUB_LOCAL_POWER: MX21 doesn't support local power ++ default: ++ // Error, but ignore. ++ TRACE_MSG1(HCD,"SET invalid feature %d",feat_selector); ++ break; ++ } ++ } ++ else { ++ // CLEAR feature USB2.0 11.24.2.1 pg 422 and USB2.0 11.24.2 tbl 11-17 pg 421 ++ // R1: sec 23.11.29 pg 23-62 ++ switch (feat_selector) { ++ case C_HUB_OVER_CURRENT: ++ fs_wl(OTG_HOST_ROOTHUB_STATUS, ROOTHUB_STATUS_OVRCURCHG); ++ break; ++ ++ // case C_HUB_LOCAL_POWER: MX21 doesn't support local power ++ default: ++ // Error, but ignore. ++ TRACE_MSG1(HCD,"CLEAR invalid feature %d",feat_selector); ++ break; ++ } ++ } ++} ++ ++/*! ++ * hcd_hw_rh_get_port_change_status() - get port change status ++ * R1: High 16 bits == change, low 16 == status ++ * Change & Status bits in register match USB2.0 spec on (read). ++ * @param bus_hcpriv ++ * @param wIndex is 1-origin ++ */ ++u32 hcd_hw_rh_get_port_change_status(struct bus_hcpriv *bus_hcpriv, int wIndex) ++{ ++ u32 port_status = fs_rl(fs_host_port_stat(wIndex)); ++ ++ TRACE_MSG8(HCD,"port %d cs: %08lx %s %s %s %s %s %s", ++ wIndex, port_status, ++ port_status & PORT_STATUS_CONNECTSC ? "CHANGE" : "", ++ port_status & PORT_STATUS_PRTPWRST ? "POWER" : "", ++ port_status & PORT_STATUS_PRTRSTST ? "RESET" : "", ++ port_status & PORT_STATUS_PRTSUSPST ? "SUSPEND" : "", ++ port_status & PORT_STATUS_PRTENABST ? "ENABLE" : "", ++ port_status & PORT_STATUS_CURCONST ? "CONNECT" : "" ++ ); ++ ++ return fs_rl(fs_host_port_stat(wIndex)); ++} ++ ++extern char *port_feature_name[]; ++ ++/*! ++ * hcd_hw_rh_port_feature() - get rh port feature ++ * @param bus_hcpriv ++ * @param wValue is feature selector ++ * @param wIndex is port number (1-N) ++ * @param set_flag ++ */ ++void hcd_hw_rh_port_feature(struct bus_hcpriv *bus_hcpriv, u16 wValue, u16 wIndex, int set_flag) ++{ ++ char buf[64]; ++ sprintf(buf, "port feature %s %s", port_feature_name[wValue], set_flag ? "SET" : "RESET"); ++ //otg_write_info_message(buf); ++ ++ TRACE_MSG4(HCD, "mx21 %s (%d) %s port_stat: %8x", port_feature_name[wValue], wValue, set_flag ? "SET" : "RESET", ++ fs_rl(fs_host_port_stat(wIndex))); ++ if (set_flag) { ++ // SET feature ++ switch (wValue) { ++ case PORT_SUSPEND: ++ otg_event(hcd_instance->otg, BUS_SUSPENDED, HCD, "HUB_PORT_SUSPEND (mx21 hw)"); ++ fs_wl(fs_host_port_stat(wIndex), 1 << wValue); ++ break; ++ case PORT_RESET: ++ case PORT_POWER: ++ fs_wl(fs_host_port_stat(wIndex), 1 << wValue); ++ break; ++ default: ++ // Error, but ignore. ++ TRACE_MSG2(HCD,"SET port %d invalid feature %d", wIndex, wValue); ++ break; ++ } ++ } ++ else { ++ // CLEAR feature (valid features from USB2.0 11.24.2.2 pg 423). ++ switch (wValue) { ++ case PORT_SUSPEND: // Cause a Host initiated resume, or no-op if already active. ++ wValue++; // yes, really. this clears PORT_SUSPEND ++ fs_wl(fs_host_port_stat(wIndex), 1 << wValue); ++ otg_event(hcd_instance->otg, BUS_SUSPENDED_, HCD, "HUB_PORT_SUSPEND/ (mx21 hw)"); ++ break; ++ ++ case PORT_POWER: // Put port in powered-off state. ++ wValue++; // yes, really. this clears PORT_POWER ++ fs_wl(fs_host_port_stat(wIndex), 1 << wValue); ++ break; ++ ++ case PORT_ENABLE: // Disable port. ++ //wValue--; ++ break; ++ default: ++ break; ++ } ++ switch (wValue) { ++ case PORT_POWER: // Put port in powered-off state. ++ case PORT_SUSPEND: // Cause a Host initiated resume, or no-op if already active. ++ case PORT_ENABLE: // Disable port. ++ case PORT_INDICATOR: // ind_selector gives which indicator to clear ++ case C_PORT_SUSPEND: // clear the PORT_SUSPEND change bit ++ case C_PORT_CONNECTION: // clear the PORT_CONNECTION change bit ++ case C_PORT_RESET: // clear the PORT_RESET change bit ++ case C_PORT_ENABLE: // clear the PORT_ENABLE change bit ++ case C_PORT_OVER_CURRENT: // clear the PORT_OVERCURRENT change bit ++ fs_wl(fs_host_port_stat(wIndex), 1 << wValue); ++ break; ++ default: ++ // Error, but ignore. ++ TRACE_MSG2(HCD,"CLEAR port %d invalid feature %d", wIndex, wValue); ++ break; ++ } ++ } ++ ++ TRACE_MSG3(HCD, "mx21 %s %s port_stat: %8x", port_feature_name[wValue], set_flag ? "SET" : "RESET", ++ fs_rl(fs_host_port_stat(wIndex))); ++} ++ ++/* ********************************************************************************************* */ ++/* Hardware Initialization - this is used by the OTG state machine to ++ * enable and disable the host controller hardware ++ */ ++void rh_hcd_en_func(struct otg_instance *otg, u8 flag); ++ ++extern void fs_host_clock_on(void); ++extern void fs_host_clock_off(void); ++ ++/*! ++ * fs_hcd_en_func() - otg hcd enable output function ++ * @param otg ++ * @param flag ++ */ ++void fs_hcd_en_func(struct otg_instance *otg, u8 flag) ++{ ++ struct bus_hcpriv *bus_hcpriv = (struct bus_hcpriv *)(((struct hcd_instance *)(otg->hcd))->privdata); ++ fs_hcpriv *fs_hcpriv = bus_hcpriv->hw_hci; ++ int i; ++ u32 hwmode = fs_rl(OTG_CORE_HWMODE); ++ unsigned long flags; ++ ++ /* puts OTG capable port into a state where host is enabled */ ++ ++ fs_andl(OTG_CORE_HWMODE, 0xffffff0f); // clear ++ ++ switch (flag) { ++ case SET: ++ local_irq_save(flags); ++ printk(KERN_INFO"%s: SET\n", __FUNCTION__); ++ if (hwmode & MODULE_CRECFG_HOST) { ++ printk(KERN_INFO"%s: warning FUNC STILL SET\n", __FUNCTION__); ++ } ++ //printk(KERN_INFO"%s: SET\n", __FUNCTION__); ++ fs_orl(OTG_CORE_HWMODE, MODULE_CRECFG_HOST); // set to software hnp ++ local_irq_restore(flags); ++ break; ++ case RESET: ++ local_irq_save(flags); ++ printk(KERN_INFO"%s: RESET\n", __FUNCTION__); ++ fs_andl(OTG_CORE_HWMODE, ~MODULE_CRECFG_HOST); // set to software hnp ++ local_irq_restore(flags); ++ break; ++ } ++ ++ TRACE_MSG1(HCD, "HWMODE: %08x", fs_rl(OTG_CORE_HWMODE)); ++} ++ ++ ++void rh_loc_sof_func(struct otg_instance *otg, u8 flag); ++void rh_loc_suspend_func(struct otg_instance *otg, u8 flag); ++ ++/*! ++ * fs_loc_suspend() - otg loc suspend output function ++ * @param otg ++ * @param flag ++ */ ++void fs_loc_suspend_func(struct otg_instance *otg, u8 flag) ++{ ++ unsigned long flags; ++ switch (flag) { ++ case SET: ++ ++ fs_orl(OTG_HOST_CONTROL, HOST_CONTROL_HCUSBSTE_SUSPEND); ++ break; ++ ++ case RESET: ++ break; ++ } ++ ++ rh_loc_suspend_func(otg, flag); ++ ++ switch (flag) { ++ case SET: ++ break; ++ ++ case RESET: ++ local_irq_save(flags); ++ fs_andl(OTG_HOST_CONTROL, ~HOST_CONTROL_HCUSBSTE_SUSPEND); ++ fs_orl(OTG_HOST_CONTROL, HOST_CONTROL_HCUSBSTE_OPERATIONAL); ++ local_irq_restore(flags); ++ break; ++ } ++} ++ ++/*! ++ * fs_loc_sof_func() - otg loc sof output function ++ * @param otg ++ * @param flag ++ */ ++void fs_loc_sof_func(struct otg_instance *otg, u8 flag) ++{ ++ struct bus_hcpriv *bus_hcpriv = (struct bus_hcpriv *)(((struct hcd_instance *)(otg->hcd))->privdata); ++ fs_hcpriv *fs_hcpriv = bus_hcpriv->hw_hci; ++ int i; ++ u32 mask; ++ unsigned long flags; ++ ++ switch (flag) { ++ case SET: ++ //printk(KERN_INFO"%s: SET\n", __FUNCTION__); ++ //TRACE_MSG0(HCD, "-----------------------------------------"); ++ ++ local_irq_save(flags); ++ ++ /* reset the host core ++ */ ++ fs_wl_clr(HCD, OTG_CORE_RST_CTRL, MODULE_RSTRH | MODULE_RSTHSIE | MODULE_RSTHC); ++ while (fs_rl(OTG_CORE_RST_CTRL)); ++ ++ fs_host_clock_on(); ++ ++ for (i = 0; i < NUM_DATA_BUFFS; i++) ++ (void) rel_data_buff(fs_hcpriv, ((fs_data_buff *)OTG_DATA_BASE)+i); ++ ++ for (i = 0; i < NUM_ETDS; i++) ++ rel_etd(fs_hcpriv,i); ++ ++ fs_wl(OTG_HOST_CONTROL, HOST_CONTROL_HCRESET | HOST_CONTROL_RMTWUEN | ++ HOST_CONTROL_HCUSBSTE_RESET | HOST_CONTROL_CTLBLKSR_11); ++ ++ ++ //TRACE_MSG0(HCD, "HW HCD_ENABLE_SET"); ++ fs_andl(OTG_CORE_HNP_CSTAT, ~(MODULE_MASTER | MODULE_SLAVE | MODULE_CMPEN | ++ MODULE_BGEN | MODULE_SWAUTORST | MODULE_ABBUSREQ)); ++ fs_rl(OTG_CORE_HNP_CSTAT); ++ ++ fs_orl(OTG_CORE_HNP_CSTAT, MODULE_MASTER | MODULE_CMPEN | MODULE_BGEN | MODULE_ABBUSREQ); ++ fs_orl(OTG_CORE_HNP_CSTAT, MODULE_ARMTHNPE | MODULE_BHNPEN); // XXX ++ ++ fs_wl(OTG_HOST_CONTROL, HOST_CONTROL_HCUSBSTE_OPERATIONAL); ++ ++ //hcd_hw_enable_interrupts(bus_hcpriv); ++ ++ mask = (HOST_PSCINT_EN | HOST_FMOFINT_EN | HOST_HERRINT_EN | ++ HOST_RESDETINT_EN | /* HOST_SOFINT_EN | */ HOST_DONEINT_EN | HOST_SORINT_EN); ++ ++ // R1: sec 23.11.15 pg 23-54 ++ fs_rl(OTG_CORE_HNP_CSTAT); ++ fs_rl(OTG_HOST_CONTROL); ++ fs_rl(OTG_HOST_ROOTHUB_STATUS); ++ fs_rl(OTG_HOST_PORT_STATUS_1); ++ fs_rl(OTG_HOST_PORT_STATUS_2); ++ fs_rl(OTG_HOST_PORT_STATUS_3); ++ ++ fs_wl(OTG_HOST_SINT_STEN, mask); ++ fs_rl(OTG_HOST_SINT_STEN); ++ local_irq_restore(flags); ++ } ++ ++ rh_loc_sof_func(otg, flag); ++ ++ switch (flag) { ++ case RESET: ++ local_irq_save(flags); ++ ++ //printk(KERN_INFO"%s: RESET\n", __FUNCTION__); ++ //TRACE_MSG0(HCD, "HW HCD_ENABLE_RESET"); ++ ++ //hcd_hw_disable_interrupts(bus_hcpriv); ++ // R1: sec 23.11.15 pg 23-54 ++ fs_wl(OTG_HOST_SINT_STAT, 0); ++ ++ fs_andl(OTG_HOST_CONTROL, ~HOST_CONTROL_HCUSBSTE_OPERATIONAL); ++ ++ // Shut down hardware if not already shut down.... ++ //hcd_hw_disable_interrupts(bus_hcpriv); ++ ++ fs_host_clock_off(); ++ local_irq_restore(flags); ++ break; ++ } ++} ++/* ********************************************************************************************* */ ++/* Module init ++ * ++ * Define the global data structures and call the initialization functions required ++ * to start the generic hcd layer and register with the otg core. ++ * ++ */ ++ ++void rh_suspend_func(struct otg_instance *otg, u8 on); ++void hcd_init_func (struct otg_instance *otg, u8 flag); ++int hcd_probe(struct usb_interface *intf, const struct usb_device_id *id); ++void hcd_disconnect(struct usb_interface *intf); ++ ++int fs_mod_init(void); ++#ifdef MODULE ++void fs_mod_exit(void); ++#endif ++ ++#if !defined(OTG_C99) ++ ++static struct usb_driver mx2_usb_driver; ++fs_hcpriv priv_mx21; ++struct bus_hcpriv fs_bus_hcpriv; ++struct hcd_ops hcd_ops; ++ ++ ++/*! ++ * fs_hcd_global_init() - initialize global vars for non C99 systems ++ */ ++void fs_hcd_global_init(void) ++{ ++ ZERO(mx2_usb_driver); ++ mx2_usb_driver.owner = THIS_MODULE; ++ mx2_usb_driver.name = "MX21_HCD"; ++ mx2_usb_driver.probe = hcd_probe; ++ mx2_usb_driver.disconnect = hcd_disconnect; ++ ++ ZERO(priv_mx21); ++ priv_mx21.free_buffs = 0xffff; ++ priv_mx21.free_etds = 0x0000ffff; ++ priv_mx21.bus_hcpriv = &fs_bus_hcpriv; ++ ++ ZERO(fs_bus_hcpriv); ++ fs_bus_hcpriv.hw_hci = &priv_mx21; ++ fs_bus_hcpriv.usb_driver = &mx2_usb_driver; ++ fs_bus_hcpriv.bus_device.driver = &mx2_usb_driver.driver; ++ fs_bus_hcpriv.bus_device.parent = NULL; ++ fs_bus_hcpriv.bus_device.bus = &usb_bus_type; // struct bus_type * ++#if 0 ++ strncpy(fs_bus_hcpriv.bus_device.name, "MX21 HOST", sizeof(fs_bus_hcpriv.bus_device.name)); ++#endif ++ fs_bus_hcpriv.num_ports = 1; ++ fs_bus_hcpriv.otg_port = 1; ++ fs_bus_hcpriv.otg_capable_mask = (1 << 1); ++ fs_bus_hcpriv.rh_serial = "000001"; ++ fs_bus_hcpriv.rh_product = "Virtual Root Hub"; ++ fs_bus_hcpriv.rh_manufacturer = "Belcarra Technologies"; ++ fs_bus_hcpriv.rh_vendorid = CONFIG_OTG_ROOTHUB_VENDORID; ++ fs_bus_hcpriv.rh_productid = CONFIG_OTG_ROOTHUB_PRODUCTID; ++ fs_bus_hcpriv.rh_bcddevice = CONFIG_OTG_ROOTHUB_BCDDEVICE; ++ fs_bus_hcpriv.rh_bmAttributes = 0x0; ++ fs_bus_hcpriv.rh_bMaxPower = 0; ++ ++ fs_bus_hcpriv.max_active_transfers = NUM_ETDS; ++ fs_bus_hcpriv.max_active_urbs = NUM_ETDS; ++ ++ ZERO(hcd_ops); ++ hcd_ops.name = "MX21 HCD"; ++ hcd_ops.max_ports = 1; ++ hcd_ops.capabilities = 0; ++ // module ++ hcd_ops.mod_init = fs_mod_init; // called for module init ++#ifdef MODULE ++ hcd_ops.mod_exit = fs_mod_exit; // called for module exit ++#endif ++ // otg state machine ++ hcd_ops.hcd_init_func = hcd_init_func; // initialize when otg enabled ++ hcd_ops.hcd_en_func = fs_hcd_en_func; // setup hardware as host ++ hcd_ops.loc_suspend_func = rh_loc_suspend_func; // enable port on hub ++ hcd_ops.loc_sof_func = fs_loc_sof_func; // enable port on hub ++}; ++ ++#else /* !defined(OTG_C99) */ ++ ++static struct usb_driver mx2_usb_driver = { ++ .owner = THIS_MODULE, ++ .name = "MX21_HCD", ++ .probe = hcd_probe, ++ .disconnect = hcd_disconnect, ++}; ++ ++extern struct bus_hcpriv fs_bus_hcpriv; ++fs_hcpriv priv_mx21 = { ++ .free_buffs = 0xffff, ++ .free_etds = 0x0000ffff, ++ .bus_hcpriv = &fs_bus_hcpriv, ++}; ++ ++struct bus_hcpriv fs_bus_hcpriv = { ++ ++ .hw_hci = &priv_mx21, ++ .usb_driver = &mx2_usb_driver, ++ .bus_device.driver = &mx2_usb_driver.driver, ++ .bus_device.parent = NULL, ++ .bus_device.bus = &usb_bus_type, // struct bus_type * ++ .bus_device.name = "MX21 HOST", ++ ++ .num_ports = 1, ++ .otg_port = 1, ++ .otg_capable_mask = (1 << 1), ++ .rh_serial = "000001", ++ .rh_product = "Virtual Root Hub", ++ .rh_manufacturer = "Belcarra Technologies", ++ .rh_vendorid = CONFIG_OTG_ROOTHUB_VENDORID, ++ .rh_productid = CONFIG_OTG_ROOTHUB_PRODUCTID, ++ .rh_bcddevice = CONFIG_OTG_ROOTHUB_BCDDEVICE, ++ .rh_bmAttributes = 0x0, ++ .rh_bMaxPower = 0, ++ ++ .max_active_transfers = NUM_ETDS, ++ .max_active_urbs = NUM_ETDS, ++}; ++ ++struct hcd_ops hcd_ops = { ++ ++ .name = "MX21 HCD", ++ .max_ports = 1, ++ .capabilities = 0, ++ // module ++ .mod_init = fs_mod_init, // called for module init ++#ifdef MODULE ++ .mod_exit = fs_mod_exit, // called for module exit ++#endif ++ .hcd_init_func = hcd_init_func, // initialize when otg enabled ++ .hcd_en_func = fs_hcd_en_func, // setup hardware as host ++ .loc_suspend_func = rh_loc_suspend_func, // enable port on hub ++ .loc_sof_func = fs_loc_sof_func, // enable port on hub ++}; ++#endif /* !defined(OTG_C99) */ ++ ++ ++extern int hcd_init(struct bus_hcpriv *bus_hcpriv, struct usb_driver *usb_driver); ++extern void hcd_exit(struct bus_hcpriv *bus_hcpriv, struct usb_driver *usb_driver); ++ ++/*! ++ * fs_mod_init() - initialize mx2 hcd ++ * ++ * This calls hcd_init() with the mx2 data structures. ++ */ ++int fs_mod_init(void) ++{ ++ RETURN_ENOMEM_IF(hcd_init(&fs_bus_hcpriv, &mx2_usb_driver)); ++ ++ hcd_instance->privdata = &fs_bus_hcpriv; ++ return 0; ++} ++ ++#ifdef MODULE ++/*! ++ * fs_mod_exit() - de-initialize mx2 hcd ++ * ++ * This calls hcd_exit() with the mx2 data structures. ++ */ ++void fs_mod_exit(void) ++{ ++ hcd_exit(&fs_bus_hcpriv, &mx2_usb_driver); ++} ++#endif ++ ++/* @} */ ++ +diff -uNr linux/drivers/no-otg/ocd/fsotg/fs-ocd.c linux/drivers/otg/ocd/fsotg/fs-ocd.c +--- linux/drivers/no-otg/ocd/fsotg/fs-ocd.c 1970-01-01 01:00:00.000000000 +0100 ++++ linux/drivers/otg/ocd/fsotg/fs-ocd.c 2006-09-01 21:41:31.000000000 +0200 +@@ -0,0 +1,263 @@ ++/* ++ * otg/ocd/fsotg/fs-ocd.c -- USB Device Controller driver ++ * ++ * Copyright (c) 2004 Belcarra ++ * ++ * By: ++ * Stuart Lynne <sl@lbelcarra.com>, ++ * Tom Rushworth <tbr@belcarra.com>, ++ * Bruce Balden <balden@belcarra.com> ++ * ++ */ ++/*! ++ * @file otg/ocd/fsotg/fs-ocd.c ++ * @brief Freescale USB OTG Controller Driver ++ * This is the OTG Controller Driver. ++ * ++ * This implements overall configuration and control of the Freescale OTG hardware ++ * and implements the main interrupt handler. ++ * ++ * There is some processor specific code here to support the alternate processors ++ * that implement this type of USB support. ++ * ++ * There is no board or platform level code here. ++ * ++ * @ingroup FSOTG ++ * ++ * @{ ++ */ ++ ++#include <otg/pcd-include.h> ++#include <linux/pci.h> ++#include <asm/arch/mx2.h> ++#undef GPT_BASE_ADDR ++#include <asm/arch/gpt.h> ++#include <asm/arch/clk.h> ++ ++#include <otghw/fsotg-hardware.h> ++ ++ ++/* ********************************************************************************************* */ ++#define TIMEOUT_VALUE 1000 ++/*! ++ * fs_func_clock_on() - enable function controller clock ++ */ ++void fs_func_clock_on(void) ++{ ++ u32 timeout = TIMEOUT_VALUE; ++ //TRACE_MSG0(OCD, "FUNC CLOCK ON:"); ++ fs_orl(OTG_CORE_CLK_CTRL, MODULE_FUNC_CLK); ++ while(!( fs_rl(OTG_CORE_CLK_CTRL) & MODULE_FUNC_CLK)) { timeout--; if (!timeout) break; } ++ ++ //fs_orl(OTG_CORE_CINT_STEN, MODULE_ASFCINT_EN |MODULE_FCINT_EN); ++} ++ ++/*! ++ * fs_host_clock_on() - enable host controller clock ++ */ ++void fs_host_clock_on(void) ++{ ++ u32 timeout = TIMEOUT_VALUE; ++ //TRACE_MSG0(OCD, "HOST CLOCK ON"); ++ fs_orl(OTG_CORE_CLK_CTRL, MODULE_HOST_CLK); ++ while(!( fs_rl(OTG_CORE_CLK_CTRL) & MODULE_HOST_CLK)) { timeout--; if (!timeout) break; } ++} ++ ++/*! ++ * fs_func_clock_off() - disable function controller clock ++ */ ++void fs_func_clock_off(void) ++{ ++ u32 timeout = TIMEOUT_VALUE; ++ //TRACE_MSG0(OCD, "FUNC CLOCK OFF"); ++ fs_andl(OTG_CORE_CLK_CTRL, 0x0); ++ while((fs_rl(OTG_CORE_CLK_CTRL) & (MODULE_FUNC_CLK | MODULE_MAIN_CLK))) { timeout--; if (!timeout) break; } ++} ++ ++/*! ++ * fs_host_clock_off() - disable host controller clock ++ */ ++void fs_host_clock_off(void) ++{ ++ u32 timeout = TIMEOUT_VALUE; ++ //TRACE_MSG0(OCD, "HOST CLOCK OFF"); ++ fs_andl(OTG_CORE_CLK_CTRL, 0x0); ++ while((fs_rl(OTG_CORE_CLK_CTRL) & (MODULE_HOST_CLK | MODULE_MAIN_CLK))) { timeout--; if (!timeout) break; } ++} ++ ++/*! ++ * fs_main_clock_on() - enable main clock ++ */ ++void fs_main_clock_on(void) ++{ ++ u32 timeout = TIMEOUT_VALUE; ++ //TRACE_MSG0(OCD, "MAINFUNC CLOCK ON"); ++ fs_orl(OTG_CORE_CLK_CTRL, MODULE_MAIN_CLK); ++ while(!( fs_rl(OTG_CORE_CLK_CTRL) & MODULE_MAIN_CLK)) { timeout--; if (!timeout) break; } ++} ++ ++/*! ++ * fs_main_clock_off() - disable main clock ++ */ ++void fs_main_clock_off(void) ++{ ++ u32 timeout = TIMEOUT_VALUE; ++ //TRACE_MSG0(OCD, "MAIN CLOCK OFF"); ++ fs_wl_set(OCD, OTG_CORE_CLK_CTRL, 0); ++ while((fs_rl(OTG_CORE_CLK_CTRL) & MODULE_MAIN_CLK)) { timeout--; if (!timeout) break; } ++} ++ ++/* ********************************************************************************************* */ ++ ++/*! ++ * fs_disable_interrupts() - disable interrupts ++ */ ++void fs_disable_interrupts (void) ++{ ++ fs_wl(OTG_CORE_CINT_STEN, 0 ); ++ fs_wl(OTG_CORE_HINT_STEN, 0 ); ++ fs_wl(OTG_HOST_SINT_STEN, 0 ); ++ fs_wl(OTG_HOST_XYINT_STEN, 0 ); ++ fs_wl(OTG_HOST_ETD_EN, 0); ++ fs_wl(OTG_HOST_ETD_DONE, 0 ); ++ fs_wl(OTG_FUNC_SINT_STEN, 0 ); ++ fs_wl(OTG_FUNC_XYINT_STEN, 0 ); ++ fs_wl(OTG_FUNC_EP_EN, 0 ); ++ fs_wl(OTG_FUNC_EP_DEN, 0 ); ++} ++ ++/* ********************************************************************************************* */ ++ ++/* ********************************************************************************************* */ ++/*! ++ * fs_init() - initial tcd setup ++ * Allocate interrupts and setup hardware. ++ */ ++void fs_init (void) ++{ ++ int timeout; ++ unsigned long flags; ++ ++ TRACE_MSG0(OCD, "MX2_MOD_OCD_INIT"); ++ local_irq_save (flags); ++ ++ fs_wl(OTG_SYS_CTRL, 0x0); ++ ++ /* 2. Ensure hardware is reset and cleared ++ */ ++ fs_clear_words((volatile u32 *)MX2_IO_ADDRESS(OTG_DMA_BASE), (32*16/4)); ++ //fs_clear((void *)MX2_IO_ADDRESS(OTG_FUNC_BASE), 0x200); ++ fs_main_clock_off(); ++ ++ fs_wl_clr(OCD, OTG_CORE_RST_CTRL, MODULE_RSTI2C | 0x3f); ++ while (fs_rl(OTG_CORE_RST_CTRL)); ++ ++ /* 3. OTG Hardware Mode and clocks ++ * set to diff, diff and Configure the OTG to behave as function ++ */ ++ TRACE_MSG0(OCD, "3. OTG Software Mode and clock"); ++ fs_andl(OTG_CORE_HWMODE, 0xffffff0f); // clear ++ fs_orl(OTG_CORE_HWMODE, MODULE_CRECFG_SHNP); // set to software hnp ++ fs_rl(OTG_CORE_HWMODE); ++ ++ fs_andl(OTG_CORE_HNP_CSTAT, ~0x00000800); ++ fs_rl(OTG_CORE_HNP_CSTAT); ++ ++ fs_wl_set(OCD, OTG_CORE_HNP_T3PCR, 0x00000000); ++ fs_rl(OTG_CORE_HNP_T3PCR); ++ ++ TRACE_MSG0(OCD, "6. Enable "); ++ TRACE_MSG0(OCD, "enable core interrupts"); ++ ++ fs_wl(OTG_CORE_CINT_STEN, 0); ++ ++ fs_wl(OTG_CORE_CINT_STEN, MODULE_ASHNPINT_EN | ++ MODULE_ASHCINT_EN | MODULE_HNPINT_EN | MODULE_HCINT); ++ ++ ++ TRACE_MSG0(OCD, "enable host interrupts"); ++ fs_wl(OTG_CORE_HINT_STEN, HNP_I2COTGINT_EN | HNP_AWAITBTO_EN | ++ HNP_AIDLEBDTO_EN | HNP_SRPSUCFAIL_EN | HNP_SRPINT_EN | HNP_VBUSERROR_EN | ++ HNP_ABSEVAILD_EN | HNP_ABUSVALID_EN | HNP_MASSLVCHG_EN | HNP_IDCHANGE_EN); ++ ++ TRACE_MSG0(OCD, "disable various host interrupts"); ++ fs_wl(OTG_HOST_XYINT_STEN, 0); ++ fs_wl(OTG_HOST_ETD_EN, 0); ++ fs_wl(OTG_HOST_ETD_DONE, 0); ++ fs_wl(OTG_HOST_SINT_STEN, 0); ++ ++ TRACE_MSG0(OCD, "disable various function interrupts"); ++ fs_wl(OTG_FUNC_XYINT_STEN, 0); ++ fs_wl(OTG_FUNC_EP_EN, 0); ++ fs_wl(OTG_FUNC_EP_DEN, 0); ++ ++ fs_wl(OTG_DMA_DINT_STEN, 0x3); ++ //fs_wb(I2C_MASTER_INT_REG_ADD, 0xf0); ++ //fs_wb(I2C_MASTER_INT_REG_ADD, 0x00); ++ ++ // XXX note that newer designs than the mx21 will also need to check ++ // and/or set OTG_CORE_INTERRUPT_STEN ++ ++ ++ TRACE_MSG0(OCD, "--"); ++ TRACE_MSG0(OCD, "8. Ready "); ++ TRACE_MSG0(OCD, "--"); ++ ++ local_irq_restore (flags); ++} ++ ++extern void fs_stop_ep(int epn, int dir); ++ ++/*! ++ * fs_exit() - de-initialize ++ */ ++void fs_exit(void) ++{ ++ int i; ++ unsigned long flags; ++ TRACE_MSG0(OCD, "MX2_MOD_OCD_EXIT"); ++ ++ fs_disable_interrupts(); ++ //_reg_CRM_PCCR1 &= ~(1<<27); // disable GPT3 ++ ++ local_irq_save (flags); ++ for (i = 0; i < 16; i++) { ++ fs_stop_ep(i, USB_DIR_IN); ++ fs_stop_ep(i, USB_DIR_OUT); ++ } ++ fs_clear_words((volatile u32 *)MX2_IO_ADDRESS(OTG_EP_BASE), (32*16/4)); ++ //fs_clear((void *)MX2_IO_ADDRESS(OTG_EP_BASE), 0x200); ++ //fs_clear((volatile u32 *)MX2_IO_ADDRESS(OTG_DMA_BASE), 32*16); ++ //fs_clear((void *)MX2_IO_ADDRESS(OTG_FUNC_BASE), 0x200); ++ fs_wl( OTG_CORE_HWMODE, 0xa3); ++ fs_main_clock_off(); ++ local_irq_restore (flags); ++} ++ ++ ++/*! ++ * fs_ocd_init() - used to initialize/enable or disable the tcd driver ++ * @param otg ++ * @param flag ++ */ ++void fs_ocd_init(struct otg_instance *otg, u8 flag) ++{ ++ TRACE_MSG0(OCD, "--"); ++ switch (flag) { ++ case SET: ++ TRACE_MSG0(OCD, "MX2_OCD_EN SET"); ++ fs_init(); ++ otg_event(otg, TCD_OK | ID_FLOAT, OCD, "OCD_OK"); ++ break; ++ case RESET: ++ fs_exit(); ++ TRACE_MSG0(OCD, "MX2_OCD_EN RESET"); ++ otg_event(otg, TCD_OK | ID_FLOAT, OCD, "OCD_OK"); ++ break; ++ } ++} ++ ++/* ********************************************************************************************* */ ++ ++/* @} */ ++ +diff -uNr linux/drivers/no-otg/ocd/fsotg/fs-pcd.c linux/drivers/otg/ocd/fsotg/fs-pcd.c +--- linux/drivers/no-otg/ocd/fsotg/fs-pcd.c 1970-01-01 01:00:00.000000000 +0100 ++++ linux/drivers/otg/ocd/fsotg/fs-pcd.c 2006-09-01 21:41:31.000000000 +0200 +@@ -0,0 +1,793 @@ ++/* ++ * otg/ocd/fsotg/fs-pcd.c -- Freescale USBOTG Peripheral Controller driver ++ * ++ * Copyright (c) 2004 Belcarra ++ * ++ * By: ++ * Stuart Lynne <sl@lbelcarra.com>, ++ * Tom Rushworth <tbr@belcarra.com>, ++ * Bruce Balden <balden@belcarra.com> ++ * ++ */ ++/*! ++ * @file otg/ocd/fsotg/fs-pcd.c ++ * @brief Freescale USB Peripheral Controller Driver ++ * This implements the Freescale USBOTG Peripheral Controller Driver. ++ * ++ * There is some processor specific code here to support the alternate processors ++ * that implement this type of USB support. ++ * ++ * There is no board or platform level code here. ++ * ++ * @ingroup FSOTG ++ * ++ * @{ ++ */ ++ ++#include <otg/pcd-include.h> ++#include <linux/pci.h> ++#include <asm/arch/mx2.h> ++ ++#include <otghw/fsotg-hardware.h> ++ ++void fs_func_clock_on(void); /* defined in mx2-ocd.c */ ++void fs_func_clock_off(void); /* defined in mx2-ocd.c */ ++ ++u8 fs_new_address, fs_usb_address; /* used to save and set USB address */ ++ ++/* ********************************************************************************************* */ ++/*! ++ * fs_ep_config() - configure and enable an endpoint to send or receive data ++ * First setup the endpoint descriptor words, and then enable the endpoint and done interrupt ++ * @param epn - endpoint number ++ * @param dir - direction IN or OUT ++ * @param ep_num - ++ * @param wMaxPacketSize ++ * @param format ++ * @param ttlbtecnt ++ * @param stall ++ */ ++static void inline ++fs_ep_config(int epn, int dir, u32 ep_num, int wMaxPacketSize, int format, int ttlbtecnt, int stall) ++{ ++ TRACE_MSG6(PCD, "epn: %02x dir: %02x sz: %d fmt: %x cnt: %d stall: %d", ++ epn, dir, wMaxPacketSize, format, ttlbtecnt, stall); ++ ++ fs_wl(ep_word(epn, dir, 0), ((stall ? 1 : 0) << 31) | (wMaxPacketSize << 16) | ((format & 3) << 14)); ++ fs_wl(ep_word(epn, dir, 1), ttlbtecnt ? (data_y_buf(epn, dir) << 16) | data_x_buf(epn, dir): 0); ++ fs_wl(ep_word(epn, dir, 3), ((wMaxPacketSize-1) << 21) | ttlbtecnt); ++ fs_orl(OTG_FUNC_IINT, ep_num); // issue done status interrupts immediately ++ fs_orl(OTG_FUNC_EP_DEN, ep_num); // enable endpoint done interrupt ++ fs_orl(OTG_FUNC_EP_EN, ep_num); // enable the interrupt ++} ++ ++/*! ++ * fs_ep_config_dma() - configure and enable an endpoint to send or receive data ++ * Check the RDY bit and clear if necessary, setup dma, do common setup and then set RDY. ++ * @param epn - endpoint number ++ * @param dir - direction IN or OUT ++ * @param wMaxPacketSize ++ * @param format ++ * @param ttlbtecnt ++ * @param bp ++ */ ++static void inline ++fs_ep_config_dma(int epn, int dir, int wMaxPacketSize, int format, int ttlbtecnt, u8 *bp) ++{ ++ u32 ep_num = ep_num_dir(epn, dir); ++ u32 dma_num = dma_num_dir(epn, dir); ++ TRACE_MSG2(PCD, "EP CONFIG DMA: epn: %02x num: %02x", epn, ep_num); ++ if (fs_rl(OTG_FUNC_EP_RDY) & ep_num) fs_wl_set(PCD, OTG_FUNC_EP_RDY, ep_num); ++ fs_wl(OTG_DMA_EPN_MSA(dma_num), (int)bp); ++ fs_wl_set(PCD, OTG_DMA_EP_EN, ep_num); ++ fs_ep_config(epn, dir, ep_num, wMaxPacketSize, format, ttlbtecnt, 0); ++ fs_wl_set(PCD, OTG_FUNC_EP_RDY, ep_num); // toggle ready bit ++} ++ ++/*! ++ * fs_ep_config_nodma() - configure and enable an endpoint to send or receive data ++ * Non-DMA setup, optional stall, ready etc. ++ * @param epn - endpoint number ++ * @param dir ++ * @param stall - stall endpoint ++ * @param wMaxPacketSize ++ * @param format ++ * @param ttlbtecnt ++ * @param rdy1 ++ * @param rdy2 ++ */ ++static void ++fs_ep_config_nodma(int epn, int dir, int stall, int wMaxPacketSize, int format, int ttlbtecnt, int rdy1, int rdy2) ++{ ++ u32 ep_num = ep_num_dir(epn, dir); ++ TRACE_MSG5(PCD, "EP CONFIG NODMA: epn: %02x num: %02x stall: %d r1: %d r2: %d", epn, ep_num, stall, rdy1, rdy2); ++ #if 1 ++ // XXX This should work, but enumeratation fails after disable/enable ++ if (rdy1) { ++ TRACE_MSG3(PCD, "EP CONFIG NODMA: epn: %02x num: %02x checking RDY: %02x", ++ epn, ep_num, rdy1 && fs_rl(OTG_FUNC_EP_RDY) & ep_num); ++ RETURN_IF (rdy1 && (fs_rl(OTG_FUNC_EP_RDY) & ep_num)); ++ } ++ #endif ++ UNLESS(epn) ++ fs_orl(OTG_FUNC_XYINT_STEN, ep_num); // enable x or y buffer interrupt ++ ++ fs_ep_config(epn, dir, ep_num, wMaxPacketSize, format, ttlbtecnt, stall); ++ RETURN_UNLESS(rdy2); ++ UNLESS (fs_rl(OTG_FUNC_EP_RDY) & ep_num) fs_wl_set(PCD, OTG_FUNC_EP_RDY, ep_num); // toggle ready bit ++} ++ ++/*! ++ * fs_sendzlp() - start sending a buffer on a specified endpoint ++ * Toggle XFILL_STAT or YFILL_STAT bit, then call fs_ep_config_nodma(). ++ * @param epn - endpoint number ++ * @param endpoint - endpoint instance ++ * @param wMaxPacketSize ++ */ ++static void inline ++fs_sendzlp (int epn, struct usbd_endpoint_instance *endpoint, int wMaxPacketSize) ++{ ++ u32 ep_num_in = ep_num_in(epn); ++ TRACE_MSG0(PCD, "ZLP"); ++ if (!(fs_rl(OTG_FUNC_XFILL_STAT) & ep_num_in)) { ++ //TRACE_MSG0(PCD, "EMPTY Y BUFFER: ZLP"); ++ fs_wl_set(PCD, OTG_FUNC_XFILL_STAT, ep_num_in); ++ } ++ else if (!(fs_rl(OTG_FUNC_YFILL_STAT) & ep_num_in)) { ++ //TRACE_MSG0(PCD, "EMPTY Y BUFFER: ZLP"); ++ fs_wl_set(PCD, OTG_FUNC_YFILL_STAT, ep_num_in); ++ } ++ fs_ep_config_nodma(epn, USB_DIR_IN, 0, endpoint->wMaxPacketSize, endpoint->bmAttributes & 0x3, 0, 1, 1); ++} ++ ++/*! ++ * fs_stop_ep() - stop endpoint ++ * @param epn - endpoint number ++ * @param dir - direction IN or OUT ++ */ ++static void fs_stop_ep(int epn, int dir) ++{ ++ int ep_num = ep_num_dir(epn, dir); ++ ++ TRACE_MSG3(PCD, "STOP EP: %08x dir: %02x [%08x]", ep_num, dir, fs_rl(ep_word(epn, dir, 0))); ++ ++ //fs_wl(ep_word(epn, dir, 0), ((stall ? 1 : 0) << 31) | (wMaxPacketSize << 16) | ((format & 3) << 14)); ++ ++ if (fs_rl(OTG_FUNC_EP_RDY) & ep_num) ++ fs_wl_clr(PCD, OTG_FUNC_EP_RDY, ep_num); ++ fs_andl(OTG_FUNC_XYINT_STEN, ~ep_num); ++ fs_andl(OTG_FUNC_IINT, ~ep_num); ++ if (fs_rl(OTG_FUNC_XINT_STAT) & ep_num) { ++ fs_wl_clr(PCD, OTG_FUNC_XINT_STAT, ep_num); ++ fs_wl_clr(PCD, OTG_FUNC_XFILL_STAT, ep_num); ++ } ++ else if (fs_rl(OTG_FUNC_YINT_STAT) & ep_num) { ++ fs_wl_clr(PCD, OTG_FUNC_YINT_STAT, ep_num); ++ fs_wl_clr(PCD, OTG_FUNC_YFILL_STAT, ep_num); ++ } ++} ++ ++/* ********************************************************************************************* */ ++/*! ++ * fs_pcd_setup_ep() - setup endpoint ++ * @param pcd - pcd instance ++ * @param epn - endpoint number ++ * @param endpoint - endpoint instance ++ */ ++static void inline ++fs_pcd_setup_ep (struct pcd_instance *pcd, unsigned int epn, struct usbd_endpoint_instance *endpoint) ++{ ++ TRACE_MSG2(PCD, "START EPN: config %d %02x", epn, endpoint->bEndpointAddress); ++ if (epn && endpoint->bEndpointAddress) ++ fs_ep_config_nodma(endpoint->bEndpointAddress & 0x7f, endpoint->bEndpointAddress & USB_DIR_IN, 0, ++ endpoint->wMaxPacketSize, endpoint->bmAttributes&0x3, 0, 0, 0); ++} ++ ++/*! ++ * fs_pcd_start_ep0_setup() - enable ep0 for receiving SETUP request ++ * Unless already ready, configure ep0 to receive. ++ * @param endpoint - endpoint instance ++ */ ++static void inline ++fs_pcd_start_ep0_setup (struct usbd_endpoint_instance *endpoint) ++{ ++ TRACE_MSG0(PCD, "START EP0: config"); ++ fs_ep_config_nodma(0, USB_DIR_OUT, 0, endpoint->wMaxPacketSize, endpoint->bmAttributes&0x3, ++ endpoint->wMaxPacketSize, 0, 1); // XXX rdy1 must be zero ++} ++/* ********************************************************************************************* */ ++/*! ++ * fs_pcd_start_endpoint_out() - start receive ++ * @param pcd - pcd instance ++ * @param endpoint - endpoint number ++ */ ++static void ++fs_pcd_start_endpoint_out(struct pcd_instance *pcd, struct usbd_endpoint_instance *endpoint) ++{ ++ struct usbd_urb *rcv_urb = pcd_rcv_next_irq(endpoint); ++ int epn = endpoint->bEndpointAddress & 0x7f; ++ UNLESS (rcv_urb) { ++ UNLESS (!epn) fs_pcd_start_ep0_setup(endpoint); // if EP0 then restart for receive setup ++ return; ++ } ++ rcv_urb->pcimap = (void *)pci_map_single (NULL, (void *) rcv_urb->buffer, rcv_urb->buffer_length, PCI_DMA_FROMDEVICE); ++ fs_ep_config_dma(epn, USB_DIR_OUT, endpoint->wMaxPacketSize, endpoint->bmAttributes & 0x3, ++ rcv_urb->buffer_length, rcv_urb->pcimap); ++ TRACE_MSG3(PCD, "START OUT: %02x rcv_urb: %x length: %d", endpoint->bEndpointAddress, rcv_urb, rcv_urb->actual_length); ++} ++ ++/*! ++ * fs_pcd_stop_out() - process interrupt for received buffer ++ * @param pcd - pcd instance ++ * @param epn - endpoint number ++ * @param endpoint - endpoint instance ++ */ ++static void ++fs_pcd_stop_out (struct pcd_instance *pcd, int epn, struct usbd_endpoint_instance *endpoint) ++{ ++ struct usbd_urb *rcv_urb = pcd_rcv_next_irq(endpoint); ++ u32 ep3 = fs_rl(ep_word(epn, USB_DIR_OUT, 3)); ++ u32 ep_num_out = ep_num_out(epn); ++ fs_stop_ep(epn, USB_DIR_OUT); ++ RETURN_UNLESS (rcv_urb); ++ pcd_rcv_finished_irq (endpoint,rcv_urb->buffer_length - (ep3 & 0xffff), 0); // XXX fix mask ++ if (!epn) ++ fs_sendzlp(epn, endpoint, endpoint->wMaxPacketSize); // ZLP ++ else ++ fs_pcd_start_endpoint_out(pcd, endpoint); // restart ++} ++/* fs_pcd_cancel_out_irq - cancel OUT urb ++ */ ++static void ++fs_pcd_cancel_out_irq (struct pcd_instance *pcd,struct usbd_urb *urb) ++{ ++ struct usbd_endpoint_instance *endpoint = urb->endpoint; ++ int epn = endpoint->bEndpointAddress & 0x7f; ++ TRACE_MSG3(PCD, "urb: %x endpoint: %x epn: %d", urb, endpoint, epn); ++ fs_pcd_stop_out (pcd, epn, endpoint); ++} ++/* ********************************************************************************************* */ ++/*! ++ * fs_pcd_start_endpoint_in() - start transmit ++ * Get next tx_urb (completing old one if there is one) and start sending it. ++ * @param pcd - pcd instance ++ * @param endpoint - endpoint instance ++ */ ++static void ++fs_pcd_start_endpoint_in(struct pcd_instance *pcd, struct usbd_endpoint_instance *endpoint) ++{ ++ struct usbd_urb *tx_urb = pcd_tx_complete_irq(endpoint, 0); ++ int epn = endpoint->bEndpointAddress & 0x7f; ++ ++ UNLESS (tx_urb) { ++ TRACE_MSG1(PCD, "START IN: %02x finished", endpoint->bEndpointAddress); ++ UNLESS (epn) { // if not EP0 we are done ++ if (fs_new_address != fs_usb_address) { ++ fs_usb_address = fs_new_address; ++ //TRACE_MSG1(PCD, "START IN: SETTING ADDRESS %x", fs_usb_address); ++ fs_wl(OTG_FUNC_DEV_ADDR, fs_usb_address); ++ } ++ fs_pcd_start_ep0_setup(endpoint); // if EP0 then restart for receive setup ++ } ++ return; ++ } ++ ++ if (pcd_tx_sendzlp(endpoint)) { // check if we need to send ZLP ++ TRACE_MSG1(PCD, "START IN: ZLP tx_urb: %x", tx_urb); ++ fs_sendzlp(epn, endpoint, endpoint->wMaxPacketSize); // ZLP ++ return; ++ } ++ endpoint->last = tx_urb->actual_length; ++ consistent_sync (tx_urb->buffer, tx_urb->actual_length, PCI_DMA_BIDIRECTIONAL); ++ fs_ep_config_dma(epn, USB_DIR_IN, endpoint->wMaxPacketSize, endpoint->bmAttributes & 0x3, ++ tx_urb->actual_length, tx_urb->buffer); ++ ++ TRACE_MSG4(PCD, "START IN: %02x tx_urb: %x length: %d %s", ++ endpoint->bEndpointAddress, tx_urb, tx_urb->actual_length, ++ (tx_urb->flags & USBD_URB_SENDZLP) ? "ZLP": ""); ++} ++ ++/*! ++ * fs_pcd_stop_in() - process interrupt for transmitted buffer ++ * @param pcd - pcd instance ++ * @param epn -endpoint number ++ * @param endpoint - endpoint instance ++ */ ++static void inline ++fs_pcd_stop_in (struct pcd_instance *pcd, int epn, struct usbd_endpoint_instance *endpoint) ++{ ++ fs_stop_ep(epn, USB_DIR_IN); ++ fs_pcd_start_endpoint_in(pcd, endpoint); ++} ++/*! ++ * fs_pcd_cancel_in_irq() - cancel IN urb ++ */ ++static void inline ++fs_pcd_cancel_in_irq (struct pcd_instance *pcd,struct usbd_urb *urb) ++{ ++ struct usbd_endpoint_instance *endpoint = urb->endpoint; ++ int epn = endpoint->bEndpointAddress & 0x7f; ++ TRACE_MSG3(PCD, "urb: %x endpoint: %x epn: %d", urb, endpoint, epn); ++ fs_pcd_stop_in (pcd, epn, endpoint); ++} ++ ++/* ********************************************************************************************* */ ++/*! ++ * fs_pcd_stop_ep0_setup() - setup endpoint zero ++ * @param pcd - pcd instance ++ * @param endpoint - endpoint instance ++ */ ++static void ++fs_pcd_stop_ep0_setup (struct pcd_instance *pcd, struct usbd_endpoint_instance *endpoint) ++{ ++ static struct usbd_device_request request; ++ u8 *cp = (u8 *) &request; ++ u32 ep0 = fs_rl(ep_word(0, USB_DIR_OUT, 0)); ++ u32 ep3 = fs_rl(ep_word(0, USB_DIR_OUT, 3)); ++ ++ fs_andl(OTG_FUNC_XYINT_STEN, ~ep_num_both(0)); ++ fs_andl(OTG_FUNC_IINT, ~ep_num_both(0)); ++ UNLESS (ep0 & EP0_SETUP) { // check if SETUP flag set ++ fs_pcd_stop_out(pcd, 0, endpoint); ++ return; ++ } ++ TRACE_MSG1(PCD, "EP0 - SETUP: size: %x", ep3 & 0xffff); ++ pcd_tx_cancelled_irq(endpoint); ++ pcd_rcv_cancelled_irq(endpoint); ++ fs_orl(OTG_FUNC_EP_TOGGLE, 0x2); // XXX is this required? ++ ++ /* get request from x or y buffer ++ */ ++ if (fs_rl(OTG_FUNC_XINT_STAT) & ep_num_out(0)) { ++ TRACE_MSG0(PCD, "READ X SETUP"); ++ fs_memcpy((u8 *)&request, (u8 *)data_x_address(0, USB_DIR_OUT), 8); ++ fs_wl_clr(PCD, OTG_FUNC_XINT_STAT, ep_num_out(0)); ++ fs_wl_clr(PCD, OTG_FUNC_XFILL_STAT, ep_num_out(0)); ++ fs_rl(OTG_FUNC_XFILL_STAT); ++ } ++ else if (fs_rl(OTG_FUNC_YINT_STAT) & ep_num_out(0)) { ++ TRACE_MSG0(PCD, "READ Y SETUP"); ++ fs_memcpy((u8 *)&request, (u8 *)data_y_address(0, USB_DIR_OUT), 8); ++ fs_wl_clr(PCD, OTG_FUNC_YINT_STAT, ep_num_out(0)); ++ fs_wl_clr(PCD, OTG_FUNC_YFILL_STAT, ep_num_out(0)); ++ fs_rl(OTG_FUNC_YFILL_STAT); ++ } ++ else ++ TRACE_MSG0(PCD, "READ NO SETUP"); // XXX what should we do here? ++ if (pcd_recv_setup_irq(pcd, &request)) { // process setup packet ++ TRACE_MSG0(PCD, "pcd_ep0: STALLING"); ++ fs_rl(OTG_FUNC_XINT_STAT); ++ fs_ep_config_nodma(0, USB_DIR_IN, 1, endpoint->wMaxPacketSize, endpoint->bmAttributes & 0x3, 0, 1, 1); ++ return; ++ } ++ RETURN_IF (request.wLength); // fs_pcd_start_endpoint_in() will start xfer ++ if ((request.bmRequestType & USB_REQ_DIRECTION_MASK) == USB_REQ_HOST2DEVICE) { ++ //RETURN_IF (request.wLength); // fs_pcd_start_endpoint_in() will start xfer ++ fs_sendzlp(0, endpoint, endpoint->wMaxPacketSize); // wLength zero, so send ZLP ++ return; ++ } ++ fs_pcd_start_ep0_setup(endpoint); // restart endpoint ++} ++/* ********************************************************************************************* */ ++static int pcd_resetdet_count; ++/*! ++ * fs_pcd_int_hndlr() - interrupt handler ++ * @param irq ++ * @param dev_id ++ * @param regs ++ * @return interrupt handled status ++ */ ++static irqreturn_t ++fs_pcd_int_hndlr (int irq, void *dev_id, struct pt_regs *regs) ++{ ++ struct pcd_instance *pcd = pcd_instance; ++ u32 cmd_stat, sint_stat, ep_dstat, ep_stats, mask; ++ ++ /* Reset and process any System interrupts ++ */ ++ if ((sint_stat = fs_rl(OTG_FUNC_SINT_STAT))) ++ fs_wl_clr(PCD, OTG_FUNC_SINT_STAT, sint_stat); ++ ++ cmd_stat = fs_rl(OTG_FUNC_CMD_STAT); ++ ++ TRACE_MSG2(PCD, "FUNC INT sint: %08x cmd: %08x", sint_stat, cmd_stat); ++ ++ if ((cmd_stat & COMMAND_RESETDET) || (sint_stat & SYSTEM_RESETINT)) { ++ TRACE_MSG1(PCD, "RESETDET: %08x", sint_stat); ++ usbd_bus_event_handler_irq (pcd->bus, DEVICE_RESET, 0); ++ fs_wl(OTG_FUNC_DEV_ADDR, 0); ++ fs_new_address = fs_usb_address = 0; ++ fs_wl(OTG_FUNC_XYINT_STEN, 0); ++ fs_wl(OTG_FUNC_IINT, 0); ++ //fs_wl(OTG_FUNC_EP_DEN, 0); // XXX this fails, ++ fs_wl(OTG_FUNC_EP_TOGGLE, 0); ++ fs_wl_clr(PCD, OTG_FUNC_EP_RDY, fs_rl(OTG_FUNC_EP_RDY)); ++ fs_wl_clr(PCD, OTG_FUNC_EP_DSTAT, fs_rl(OTG_FUNC_EP_DSTAT)); ++ fs_wl_clr(PCD, OTG_FUNC_XINT_STAT, fs_rl(OTG_FUNC_XINT_STAT)); ++ fs_wl_clr(PCD, OTG_FUNC_YINT_STAT, fs_rl(OTG_FUNC_YINT_STAT)); ++ if (pcd_resetdet_count++ > 10) { ++ ++ //printk(KERN_INFO"%s: SINT: %08x %08x CINT: %08x\n", __FUNCTION__, ++ // sint_stat, fs_rl(OTG_FUNC_SINT_STEN), fs_rl(OTG_CORE_CINT_STEN)); ++ fs_func_clock_on(); ++ fs_wl(OTG_FUNC_SINT_STEN, 0); ++ fs_wl(OTG_CORE_CINT_STEN, 0); ++ fs_wl(OTG_FUNC_XYINT_STEN, 0); ++ fs_wl(OTG_FUNC_EP_EN, 0); ++ fs_wl(OTG_FUNC_EP_DEN, 0); ++ fs_func_clock_off(); ++ //printk(KERN_INFO"%s: SINT: %08x %08x CINT: %08x RESET\n", __FUNCTION__, ++ // sint_stat, fs_rl(OTG_FUNC_SINT_STEN), fs_rl(OTG_CORE_CINT_STEN)); ++ } ++ } ++ if ((cmd_stat & COMMAND_RSMINPROG) || (sint_stat & SYSTEM_RSMFININT)) { ++ TRACE_MSG1(PCD, "RSMINPRO: %08x", sint_stat); ++ fs_wl_set(PCD, OTG_FUNC_SINT_STAT,SYSTEM_RSMFININT); ++ fs_func_clock_on(); ++ usbd_bus_event_handler_irq (pcd->bus, DEVICE_BUS_ACTIVITY, 0); ++ } ++ if ((cmd_stat & COMMAND_SUPDET) || (sint_stat & SYSTEM_SUSPDETINT)) { ++ int timeout = 0; ++ TRACE_MSG1(PCD, "SUSPENDDET: %08x", sint_stat); ++ fs_wl_set(PCD, OTG_FUNC_SINT_STAT,SYSTEM_SUSPDETINT); ++ fs_func_clock_off(); ++ usbd_bus_event_handler_irq (pcd->bus, DEVICE_BUS_INACTIVE, 0); ++ } ++ ep_dstat = fs_rl(OTG_FUNC_EP_DSTAT); ++ ep_stats = ep_dstat | fs_rl(OTG_FUNC_XINT_STAT) | fs_rl(OTG_FUNC_YINT_STAT); ++ if (ep_stats) { ++ int i; ++ ++ pcd_resetdet_count = 0; ++ ++ /* clear the done status flags ++ */ ++ TRACE_MSG1(PCD, "EP STATS: %08x", ep_stats); ++ fs_wl_clr(PCD, OTG_FUNC_EP_DSTAT, ep_dstat); ++ ++ /* check ep0 first - special handling for control endpoint, need to check dstat, y/x stats ++ */ ++ if (ep_stats & EP_OUT) fs_pcd_stop_ep0_setup(pcd, pcd->bus->endpoint_array); ++ if (ep_stats & EP_IN) fs_pcd_stop_in(pcd, 0, pcd->bus->endpoint_array); ++ ++ /* now check all of the data endpoints, only if in dstat ++ */ ++ for (i = 1, ep_dstat >>= 2; ep_dstat && (i < 16); i++, ep_dstat >>= 2) { ++ if (ep_dstat & EP_OUT) fs_pcd_stop_out(pcd, i, pcd->bus->endpoint_array + (2 * i)); ++ if (ep_dstat & EP_IN) fs_pcd_stop_in(pcd, i, pcd->bus->endpoint_array + (2 * i) + 1); ++ } ++ } ++ return IRQ_HANDLED; ++} ++ ++/* ********************************************************************************************* */ ++/*! ++ * fs_pcd_set_address() - set the USB address for this device ++ * @param pcd ++ * @param address ++ */ ++static void inline ++fs_pcd_set_address (struct pcd_instance *pcd, u8 address) ++{ ++ fs_usb_address = 0; ++ fs_new_address = address; /* this will be used in the interrupt handler */ ++} ++ ++/* ********************************************************************************************* */ ++/*! ++ * fs_pcd_en_func() - enable ++ * This is called to enable / disable the PCD and USBD stack. ++ * @param otg - otg instance ++ * @param flag - SET or RESET ++ */ ++static void ++fs_pcd_en_func (struct otg_instance *otg, u8 flag) ++{ ++ unsigned long flags; ++ struct pcd_instance *pcd = otg->pcd; ++ struct usbd_bus_instance *bus = pcd->bus; ++ u32 hwmode = fs_rl(OTG_CORE_HWMODE); ++ ++ ++ switch (flag) { ++ case SET: ++ TRACE_MSG0(PCD, "PCD_EN: SET"); ++ local_irq_save (flags); ++ fs_andl(OTG_CORE_HWMODE, 0xffffff0f); // clear ++ ++ //printk(KERN_INFO"%s: SET\n", __FUNCTION__); ++ if (hwmode & MODULE_CRECFG_HOST) { ++ printk(KERN_INFO"%s: warning HOST STILL SET\n", __FUNCTION__); ++ } ++ fs_orl(OTG_CORE_HWMODE, MODULE_CRECFG_FUNC); // set to software hnp ++ ++ /* reset the function core ++ */ ++ //fs_wl_clr(PCD, OTG_CORE_RST_CTRL, MODULE_RSTFSIE | MODULE_RSTFC); ++ //while (fs_rl(OTG_CORE_RST_CTRL)); ++ ++ fs_func_clock_on(); ++ ++ fs_pcd_start_ep0_setup(pcd->bus->endpoint_array); ++ ++ /* C.f. L3 12.1 - enable Slave, Band Gap enable, and Comparator enable. ++ * C.f. SCM-11 version of the USBOTG specification, ABBusReq enables the charge pump ++ */ ++ fs_andl(OTG_CORE_HNP_CSTAT, ~(MODULE_MASTER | MODULE_SLAVE | ++ MODULE_CMPEN | MODULE_BGEN | MODULE_SWAUTORST | MODULE_ABBUSREQ)); ++ ++ fs_orl(OTG_CORE_HNP_CSTAT, MODULE_SLAVE | MODULE_CMPEN | MODULE_BGEN | MODULE_SWAUTORST | MODULE_ABBUSREQ); ++ fs_rl(OTG_CORE_HNP_CSTAT); ++ ++ fs_orl(OTG_FUNC_SINT_STEN, ( /*SYSTEM_DONEREGINTDS_EN | *//*SYSTEM_SOFDETINT_EN |*/ SYSTEM_DONEREGINT_EN | ++ SYSTEM_SUSPDETINT_EN | SYSTEM_RSMFININT_EN | SYSTEM_RESETINT_EN)); ++ ++ fs_orl(OTG_CORE_CINT_STEN, MODULE_FCINT_EN | MODULE_ASFCINT_EN); ++ //printk(KERN_INFO"%s: SINT: %08x CINT: %08x SET\n", __FUNCTION__, ++ // fs_rl(OTG_FUNC_SINT_STEN), fs_rl(OTG_CORE_CINT_STEN)); ++ local_irq_restore (flags); ++ ++ break; ++ ++ case RESET: ++ TRACE_MSG0(PCD, "PCD_EN: RESET"); ++ //printk(KERN_INFO"%s: RESET\n", __FUNCTION__); ++ ++ //fs_andl(OTG_FUNC_SINT_STEN, ~( /*SYSTEM_DONEREGINTDS_EN | *//*SYSTEM_SOFDETINT_EN |*/ SYSTEM_DONEREGINT_EN | ++ // SYSTEM_SUSPDETINT_EN | SYSTEM_RSMFININT_EN | SYSTEM_RESETINT_EN)); ++ ++ local_irq_save (flags); ++ fs_func_clock_on(); ++ fs_andl(OTG_FUNC_SINT_STEN, 0); ++ fs_andl(OTG_CORE_CINT_STEN, ~(MODULE_FCINT_EN | MODULE_ASFCINT_EN)); ++ ++ fs_func_clock_off(); ++ //printk(KERN_INFO"%s: SINT: %08x CINT: %08x RESET\n", __FUNCTION__, ++ // fs_rl(OTG_FUNC_SINT_STEN), fs_rl(OTG_CORE_CINT_STEN)); ++ ++ /* reset the function core ++ */ ++ fs_wl_clr(PCD, OTG_CORE_RST_CTRL, MODULE_RSTFSIE | MODULE_RSTFC); ++ while (fs_rl(OTG_CORE_RST_CTRL)); ++ fs_andl(OTG_CORE_HWMODE, ~MODULE_CRECFG_FUNC); // set to software hnp ++ local_irq_restore (flags); ++ ++ usbd_bus_event_handler_irq (bus, DEVICE_RESET, 0); ++ usbd_bus_event_handler_irq (bus, DEVICE_DESTROY, 0); ++ ++ break; ++ } ++ TRACE_MSG1(PCD, "HWMODE: %08x", fs_rl(OTG_CORE_HWMODE)); ++} ++ ++/*! ++ * fs_remote_wakeup() - perform remote wakeup. ++ * Initiate a remote wakeup to the host. ++ * @param otg - otg instance ++ * @param flag - SET or RESET ++ */ ++static void ++fs_remote_wakeup(struct otg_instance *otg, u8 flag) ++{ ++ unsigned long flags; ++ TRACE_MSG0(PCD, "MX2_REMOTE_WAKEUP: "); ++ local_irq_save (flags); ++ fs_wl_set(PCD, OTG_FUNC_CMD_STAT, COMMAND_RSMINPROG); ++ fs_wl(OTG_FUNC_CMD_STAT, COMMAND_RSMINPROG); ++ local_irq_restore (flags); ++} ++ ++/* ********************************************************************************************* */ ++/*! ++ * fs_pcd_start() - start the UDC ++ * @param pcd ++ */ ++static void ++fs_pcd_start (struct pcd_instance *pcd) ++{ ++ //TRACE_MSG0(PCD, "UDC START"); ++ fs_rl(OTG_FUNC_SINT_STEN); ++ //fs_pcd_start_ep0_setup(pcd_instance->bus->endpoint_array); ++} ++ ++/*! ++ * fs_pcd_stop() - stop the UDC ++ * @param pcd ++ */ ++static void ++fs_pcd_stop (struct pcd_instance *pcd) ++{ ++ TRACE_MSG0(PCD, "UDC STOP"); ++} ++ ++/*! ++ * pcd_assign_endpoint() ++ */ ++static int ++pcd_assign_endpoint(u8 physicalEndpoint, int used[UDC_MAX_ENDPOINTS], ++ struct usbd_endpoint_map *endpoint_map, u8 bmAttributes, u16 wMaxPacketSize, u16 transferSize) ++{ ++ RETURN_EINVAL_IF( used[physicalEndpoint] ); ++ endpoint_map->bEndpointAddress[0] = physicalEndpoint | (USB_DIR_IN & bmAttributes); ++ endpoint_map->physicalEndpoint[0] = (physicalEndpoint * 2) + ((USB_DIR_IN & bmAttributes) ? 1 : 0); ++ endpoint_map->wMaxPacketSize[0] = wMaxPacketSize; ++ endpoint_map->transferSize[0] = transferSize; ++ endpoint_map->bmAttributes[0] = bmAttributes; ++ used[physicalEndpoint]++; ++ TRACE_MSG4(PCD, "ASSIGN: index: %02x phys: %02x addr: %02x", physicalEndpoint, ++ endpoint_map->physicalEndpoint[0], endpoint_map->bEndpointAddress[0], 0); ++ return 0; ++} ++ ++/*! ++ * fs_pcd_request_endpoints() ++ * @param pcd ++ * @param endpoint_map_array ++ * @param endpointsRequested ++ * @param requestedEndpoints ++ */ ++static int ++fs_pcd_request_endpoints(struct pcd_instance *pcd, struct usbd_endpoint_map *endpoint_map_array, int endpointsRequested, ++ struct usbd_endpoint_request *requestedEndpoints) ++{ ++ struct usbd_device_description *device_description; ++ int i, in, out; ++ int in_used[UDC_MAX_ENDPOINTS]; ++ int out_used[UDC_MAX_ENDPOINTS]; ++ memset(in_used, 0, sizeof(in_used)); ++ memset(out_used, 0, sizeof(out_used)); ++ TRACE_MSG1(PCD, "REQUEST ENDPOINTS: %d", endpointsRequested); ++ for (in = out = 1, i = 0; i < endpointsRequested; i++) { ++ struct usbd_endpoint_map *endpoint_map = endpoint_map_array + i; ++ u8 bmAttributes = requestedEndpoints[i].bmAttributes; ++ u16 transferSize = requestedEndpoints[i].fs_requestedTransferSize; ++ switch(bmAttributes) { ++ case USB_DIR_IN | USB_ENDPOINT_INTERRUPT: ++ case USB_DIR_IN | USB_ENDPOINT_INTERRUPT | USB_ENDPOINT_OPT: ++ case USB_DIR_IN | USB_ENDPOINT_BULK: ++ CONTINUE_IF(!pcd_assign_endpoint(in++, in_used, endpoint_map, bmAttributes, 0x40, transferSize)); ++ THROW(error); ++ ++ case USB_DIR_OUT | USB_ENDPOINT_BULK: ++ case USB_DIR_OUT | USB_ENDPOINT_INTERRUPT: ++ case USB_DIR_OUT | USB_ENDPOINT_INTERRUPT | USB_ENDPOINT_OPT: ++ CONTINUE_IF(!pcd_assign_endpoint(out++, out_used, endpoint_map, bmAttributes, 0x40, transferSize)); ++ //CONTINUE_IF(!pcd_assign_endpoint(in++, out_used, endpoint_map, bmAttributes, 0x40, transferSize)); ++ THROW(error); ++ ++ case USB_ENDPOINT_CONTROL: ++ continue; ++ case USB_DIR_IN | USB_ENDPOINT_ISOCHRONOUS: ++ case USB_DIR_OUT | USB_ENDPOINT_ISOCHRONOUS: ++ THROW(error); ++ } ++ THROW(error); ++ CATCH(error) { ++ printk(KERN_ERR"%s: FAILED num: %d bmAttributes: %02x transferSize: %02x\n", ++ __FUNCTION__, i, bmAttributes, transferSize); ++ return -EINVAL; ++ } ++ } ++ return 0; ++} ++ ++/*! ++ * fs_pcd_set_endpoints() - setup the physical endpoints for the endpoint map ++ * @param pcd ++ * @param endpointsRequested ++ * @param endpoint_map_array ++ */ ++static int ++fs_pcd_set_endpoints (struct pcd_instance *pcd, int endpointsRequested, struct usbd_endpoint_map *endpoint_map_array) ++{ ++ fs_clear_words((volatile u32 *)MX2_IO_ADDRESS(OTG_EP_BASE), (32*16/4)); ++ //fs_clear((void *)MX2_IO_ADDRESS(OTG_EP_BASE), 0x200); ++ TRACE_MSG0(PCD, "Enable system control interrupts"); ++ fs_wl(OTG_SYS_CTRL, SYS_CTRL_I2C_WU_INT_EN | SYS_CTRL_OTG_WU_INT_EN | SYS_CTRL_HOST_WU_INT_EN | SYS_CTRL_FNT_WU_INT_EN); ++ return 0; ++} ++ ++/*! ++ * fs_pcd_framenum() - get current framenum ++ */ ++static u16 ++fs_pcd_framenum (void) ++{ ++ return fs_rl(OTG_FUNC_FRM_NUM); ++} ++ ++/* ++ * fs_endpoint_halted() - is endpoint halted ++ */ ++static int ++fs_endpoint_halted (struct pcd_instance *pcd, struct usbd_endpoint_instance *endpoint) ++{ ++ int epn = endpoint->bEndpointAddress & 0x7f; ++ int dir = (endpoint->bEndpointAddress & 0x80) ? 1 : 0; ++ ++ TRACE_MSG2(PCD, "epn: %02x dir: %x", epn, dir); ++ return fs_rl(ep_word(epn, dir, 0)) & (1 << 31) ? TRUE : FALSE; ++} ++ ++/* fs_halt_endpoint - halt endpoint ++ */ ++int fs_halt_endpoint(struct pcd_instance *pcd, struct usbd_endpoint_instance *endpoint, int flag) ++{ ++ int epn = endpoint->bEndpointAddress & 0x7f; ++ int dir = (endpoint->bEndpointAddress & 0x80) ? 1 : 0; ++ TRACE_MSG3(PCD, "epn: %02x dir: %x flag: %d", epn, dir, flag); ++ fs_ep_config_nodma(epn, USB_DIR_IN, flag, endpoint->wMaxPacketSize, endpoint->bmAttributes & 0x3, 0, 0, 1); ++ return 0; ++} ++ ++/* ********************************************************************************************* */ ++ ++#if !defined(OTG_C99) ++struct usbd_pcd_ops usbd_pcd_ops; ++struct pcd_ops pcd_ops; ++ ++/*! ++ * fs_pcd_global_init() - initialize globals ++ */ ++static void ++fs_pcd_global_init(void) ++{ ++ ZERO(usbd_pcd_ops); ++ usbd_pcd_ops.bmAttributes = USB_OTG_HNP_SUPPORTED | USB_OTG_SRP_SUPPORTED; ++ usbd_pcd_ops.max_endpoints = UDC_MAX_ENDPOINTS; ++ usbd_pcd_ops.ep0_packetsize = EP0_PACKETSIZE; ++ usbd_pcd_ops.capabilities = REMOTE_WAKEUP_SUPPORTED; ++ usbd_pcd_ops.name = UDC_NAME; ++ usbd_pcd_ops.start = fs_pcd_start; ++ usbd_pcd_ops.stop = fs_pcd_stop; ++ usbd_pcd_ops.start_endpoint_in = fs_pcd_start_endpoint_in; ++ usbd_pcd_ops.start_endpoint_out = fs_pcd_start_endpoint_out; ++ usbd_pcd_ops.request_endpoints = fs_pcd_request_endpoints; ++ usbd_pcd_ops.set_endpoints = fs_pcd_set_endpoints; ++ usbd_pcd_ops.set_address = fs_pcd_set_address; ++ usbd_pcd_ops.setup_ep = fs_pcd_setup_ep; ++ usbd_pcd_ops.halt_endpoint = fs_halt_endpoint; ++ usbd_pcd_ops.endpoint_halted = fs_endpoint_halted; ++ ++ usbd_pcd_ops.cancel_in_irq = fs_pcd_cancel_in_irq; ++ usbd_pcd_ops.cancel_out_irq = fs_pcd_cancel_out_irq; ++ ++ ZERO(pcd_ops); ++ pcd_ops.pcd_en_func = fs_pcd_en_func; ++ pcd_ops.pcd_init_func = pcd_init_func; ++ pcd_ops.remote_wakeup_func = fs_remote_wakeup; ++ pcd_ops.framenum = fs_pcd_framenum; ++} ++#else /* !defined(OTG_C99) */ ++static struct ++usbd_pcd_ops usbd_pcd_ops = { ++ .bmAttributes = USB_OTG_HNP_SUPPORTED | USB_OTG_SRP_SUPPORTED, ++ .max_endpoints = UDC_MAX_ENDPOINTS, ++ .ep0_packetsize = EP0_PACKETSIZE, ++ .capabilities = REMOTE_WAKEUP_SUPPORTED, ++ .name = UDC_NAME, ++ .start = fs_pcd_start, ++ .stop = fs_pcd_stop, ++ .start_endpoint_in = fs_pcd_start_endpoint_in, ++ .start_endpoint_out = fs_pcd_start_endpoint_out, ++ .request_endpoints = fs_pcd_request_endpoints, ++ .set_endpoints = fs_pcd_set_endpoints, ++ .set_address = fs_pcd_set_address, ++ .setup_ep = fs_pcd_setup_ep, ++ .halt_endpoint = fs_halt_endpoint, ++ .endpoint_halted = fs_endpoint_halted, ++ ++ .cancel_in_irq = fs_pcd_cancel_in_irq, ++ .cancel_out_irq = fs_pcd_cancel_out_irq, ++}; ++ ++struct pcd_ops pcd_ops = { ++ .pcd_en_func = fs_pcd_en_func, ++ .pcd_init_func = pcd_init_func, ++ .remote_wakeup_func = fs_remote_wakeup, ++ .framenum = fs_pcd_framenum, ++}; ++#endif /* !defined(OTG_C99) */ ++ ++/* @} */ ++ +diff -uNr linux/drivers/no-otg/ocd/gen/gen.c linux/drivers/otg/ocd/gen/gen.c +--- linux/drivers/no-otg/ocd/gen/gen.c 1970-01-01 01:00:00.000000000 +0100 ++++ linux/drivers/otg/ocd/gen/gen.c 2006-09-01 21:41:31.000000000 +0200 +@@ -0,0 +1,268 @@ ++/* ++ * otg/ocd/gen/gen.c -- USB Device Controller driver ++ * ++ * Copyright (c) 2004 Belcarra ++ * ++ * By: ++ * Stuart Lynne <sl@lbelcarra.com>, ++ * Tom Rushworth <tbr@belcarra.com>, ++ * Bruce Balden <balden@belcarra.com> ++ * ++ */ ++/*! ++ * @file otg/ocd/gen/gen.c ++ * @brief Sample driver ++ * ++ * ++ * @ingroup USBP ++ */ ++ ++#include <udc.h> ++#include <asm/hardware.h> ++#include "gen.h" ++ ++ ++static unsigned char usb_address; ++ ++/* ********************************************************************************************* */ ++/* udc_write_buffer - write a buffer to the udc fifo ++ */ ++static void udc_write_buffer (unsigned char ep, unsigned char *b, unsigned char size) ++{ ++} ++ ++/* udc_read_buffer - fill a buffer from the udc fifo ++ */ ++static void udc_read_buffer (unsigned char ep, unsigned char *b, unsigned char size) ++{ ++} ++ ++/* udc_out - process an OUT interrupt ++ */ ++static void udc_out (struct usbd_endpoint_instance *endpoint) ++{ ++} ++ ++/* udc_in - process an IN interrupt ++ */ ++static void udc_in (struct usbd_endpoint_instance *endpoint) ++{ ++} ++ ++/* ********************************************************************************************* */ ++/* udc_in_ep0 - start transmit of ep0 Control Read data ++ */ ++static void udc_in_ep0 (struct usbd_endpoint_instance *endpoint) ++{ ++ struct usbd_urb *tx_urb = pcd_tx_complete_irq (endpoint, 0); ++ if (!tx_urb || pcd_tx_sendzlp (endpoint)) { ++ udc_write_buffer (0, NULL, 0); ++ return; ++ } ++ TRACE_MSG16("EP0 IN: actual: %d sent: %d", endpoint->tx_urb->actual_length, endpoint->sent); ++ endpoint->last = MIN (tx_urb->actual_length - endpoint->sent, endpoint->wMaxPacketSize); ++ udc_write_buffer (0, tx_urb->buffer + endpoint->sent, endpoint->last); ++} ++ ++/* udc_out_ep0 - start receive of ep0 Control Write data ++ */ ++static void udc_out_ep0 (struct usbd_endpoint_instance *endpoint) ++{ ++ struct usbd_urb *rcv_urb = pcd_rcv_next_irq (endpoint); ++ int size = 0; ++ RETURN_IF(!rcv_urb); ++ TRACE_MSG8("OUT EP0: actual: %d buffer_length: %d size: %d", rcv_urb->actual_length, rcv_urb->buffer_length, size, 0); ++ udc_read_buffer (0, rcv_urb->buffer + rcv_urb->actual_length, size); ++ if (!pcd_rcv_complete_irq (endpoint, size, 0)) ++ endpoint->state = WAIT_FOR_SETUP; ++} ++ ++/* udc_ep0_setup - process a Control Setup command ++ */ ++static void udc_ep0_setup (struct usbd_endpoint_instance *endpoint) ++{ ++ static struct usbd_device_request request; ++ u8 *cp = (u8 *) &request; ++ int i; ++ TRACE_MSG32("EP0 SETUP: tx_urb: %p", (int)endpoint->tx_urb); ++ pcd_tx_cancelled_irq (endpoint); ++ pcd_rcv_cancelled_irq (endpoint); ++ if (pcd_recv_setup_irq (&request)) { // process setup packet ++ TRACE_MSG("udc_ep0: setup failed"); ++ //udc_stall_ep (0); ++ endpoint->state = WAIT_FOR_SETUP; ++ return; ++ } ++ if ((request.bmRequestType & USB_REQ_DIRECTION_MASK) == USB_REQ_HOST2DEVICE) { ++ TRACE_MSG32("udc_ep0: send zlp %d", le16_to_cpu (request.wLength)); // XXX ++ //ctrl_outb (EP0s_RDFN, USBTRG); ++ RETURN_IF(pcd_rcv_next_irq (endpoint)); ++ //and_b (~EP0o_TS, USBIER0); ++ return; ++ } ++} ++/* ********************************************************************************************* */ ++/* udc_int_hndlr - interrupt handler ++ */ ++static void udc_int_hndlr (int irq, void *dev_id, struct pt_regs *regs) ++{ ++ trace_ops.interrupts++; ++#if 0 ++ if (trace_ops.interrupts > 1000) { udc_disable_interrupts (); udc_disable (); TRACE_MSG("UDC DISABLE"); return; } ++#endif ++} ++/* ********************************************************************************************* */ ++/* udc_start_endpoint_in - start transmit ++ */ ++void udc_start_endpoint_in (struct usbd_endpoint_instance *endpoint) ++{ ++} ++ ++/* udc_start_endpoint_out - start receive ++ */ ++void udc_start_endpoint_out (struct usbd_endpoint_instance *endpoint) ++{ ++} ++/* ********************************************************************************************* */ ++/* udc_attached - is the USB cable attached ++ * Return non-zero if cable is attached. ++ */ ++int udc_attached (void) ++{ ++ return 0; ++} ++ ++static int udc_connect_status; ++ ++/* udc_connected - is the USB pullup enabled ++ * Return non-zero if pullup is enabled. ++ */ ++int udc_connected (void) ++{ ++ return udc_connect_status; ++} ++ ++/* udc_connect - enable pullup resistor ++ */ ++void udc_connect (void) ++{ ++ udc_connect_status = 1; ++} ++ ++/* udc_disconnect - disable pullup resistor ++ */ ++void udc_disconnect (void) ++{ ++ udc_connect_status = 0; ++} ++ ++/* udc_start - start session ++ */ ++void udc_start (void) ++{ ++ // udc_enable_interrupts (); ++} ++ ++/* udc_stop - stop session ++ */ ++void udc_stop (void) ++{ ++ // udc_disable_interrupts (); ++} ++ ++/* udc_enable - enable the UDC ++ */ ++void udc_enable (void) ++{ ++} ++ ++/* udc_disable - disable the UDC ++ */ ++void udc_disable (void) ++{ ++} ++ ++/* udc_init - initialize ++ * Return non-zero if we cannot see device. ++ */ ++int udc_init (void) ++{ ++ return 0; ++} ++ ++/* udc_exit - disable the UDC ++ */ ++void udc_exit (void) ++{ ++} ++ ++/* ********************************************************************************************* */ ++/* udc_assign_endpoint ++ */ ++int udc_assign_endpoint (u8 physicalEndpoint, int used[6], struct usbd_endpoint_map *endpoint_map, u8 bmAttributes, ++ u16 wMaxPacketSize, u16 transferSize) ++{ ++ RETURN_EINVAL_IF(used[physicalEndpoint]); ++ endpoint_map->bEndpointAddress[0] = physicalEndpoint | (bmAttributes & USB_IN_DIR); ++ endpoint_map->physicalEndpoint[0] = physicalEndpoint; ++ endpoint_map->wMaxPacketSize[0] = wMaxPacketSize; ++ endpoint_map->transferSize[0] = transferSize; ++ endpoint_map->bmAttributes[0] = bmAttributes; ++ used[physicalEndpoint]++; ++ return 0; ++} ++ ++/* udc_request_endpoints - process endpoint request list ++ */ ++int udc_request_endpoints (struct usbd_endpoint_map *endpoint_map_array, int endpointsRequested, ++ struct usbd_endpoint_request *requestedEndpoints) ++{ ++ struct usbd_device_description *device_description; ++ int i, j; ++ int used[UDC_MAX_ENDPOINTS]; ++ memset (used, 0, sizeof (used)); ++ for (j = 0, i = 0; i < endpointsRequested; i++) { ++ struct usbd_endpoint_map *endpoint_map = endpoint_map_array + i; ++ u8 bmAttributes = requestedEndpoints[i].bmAttributes; ++ u16 transferSize = requestedEndpoints[i].fs_requestedTransferSize; ++ switch (bmAttributes) { ++ case USB_DIR_IN | USB_ENDPOINT_INTERRUPT: ++ case USB_DIR_IN | USB_ENDPOINT_INTERRUPT | USB_ENDPOINT_OPT: ++ case USB_DIR_IN | USB_ENDPOINT_BULK: ++ case USB_DIR_OUT | USB_ENDPOINT_BULK: ++ BREAK_IF(!udc_assign_endpoint (j++, used, endpoint_map, bmAttributes, 0x40, transferSize)); ++ return -EINVAL; ++ case USB_ENDPOINT_CONTROL: ++ continue; ++ case USB_DIR_OUT | USB_ENDPOINT_INTERRUPT: ++ case USB_DIR_OUT | USB_ENDPOINT_INTERRUPT | USB_ENDPOINT_OPT: ++ case USB_DIR_IN | USB_ENDPOINT_ISOCHRONOUS: ++ case USB_DIR_OUT | USB_ENDPOINT_ISOCHRONOUS: ++ return -EINVAL; ++ } ++ return -EINVAL; ++ } ++ RETURN_EINVAL_UNLESS (j < UDC_MAX_ENDPOINTS); ++ } ++ return 0; ++} ++/* ********************************************************************************************* */ ++struct udc_ops udc_ops = { ++ max_endpoints: UDC_MAX_ENDPOINTS, ++ ep0_packetsize: EP0_PACKETSIZE, ++ name: UDC_NAME, ++ init: udc_init, ++ exit: udc_exit, ++ enable: udc_enable, ++ disable: udc_disable, ++ start: udc_start, ++ stop: udc_stop, ++ start_endpoint_in: udc_start_endpoint_in, ++ start_endpoint_out: udc_start_endpoint_out, ++ request_endpoints: udc_request_endpoints, ++ attached: udc_attached, ++ connected: udc_connected, ++ connect: udc_connect, ++ disconnect: udc_disconnect, ++}; ++ +diff -uNr linux/drivers/no-otg/ocd/gen/gen.h linux/drivers/otg/ocd/gen/gen.h +--- linux/drivers/no-otg/ocd/gen/gen.h 1970-01-01 01:00:00.000000000 +0100 ++++ linux/drivers/otg/ocd/gen/gen.h 2006-09-01 21:41:31.000000000 +0200 +@@ -0,0 +1,29 @@ ++/* ++ * otg/ocd/gen/gen.h ++ * ++ * Copyright (c) 2004 Belcarra ++ * ++ * By: ++ * Stuart Lynne <sl@belcarra.com>, ++ * Tom Rushworth <tbr@belcarra.com>, ++ * Bruce Balden <balden@belcarra.com> ++ * ++ */ ++ ++/*! ++ * @file otg/ocd/gen/gen.h ++ * @brief Private structures and defines for Intel PXA-270 ++ * ++ * Notes ++ * ++ * 1. This is a minmal outline for a simple UDC driver. ++ * ++ * ++ * @ingroup BVD ++ */ ++ ++ ++#define UDC_NAME "GEN" ++#define UDC_MAX_ENDPOINTS 3 ++#define EP0_PACKETSIZE 8 ++ +diff -uNr linux/drivers/no-otg/ocd/isp1301/Config.in linux/drivers/otg/ocd/isp1301/Config.in +--- linux/drivers/no-otg/ocd/isp1301/Config.in 1970-01-01 01:00:00.000000000 +0100 ++++ linux/drivers/otg/ocd/isp1301/Config.in 2006-09-01 21:41:31.000000000 +0200 +@@ -0,0 +1,17 @@ ++ ++# ++# Copyright (c) 2004 Belcarra ++# ++# ISP 1301 TCD ++ ++if [ "$CONFIG_OTG_ISP1301" = "y" ]; then ++ mainmenu_option next_comment ++ ++ comment 'ISP 1301 (i2c)' ++ ++ dep_bool 'Proc FS debug' CONFIG_OTG_ISP1301_PROCFS $CONFIG_OTG_ISP1301 ++ ++ endmenu ++else ++ define_bool CONFIG_OTG_ISP1301_PROCFS n ++fi +diff -uNr linux/drivers/no-otg/ocd/isp1301/Kconfig linux/drivers/otg/ocd/isp1301/Kconfig +--- linux/drivers/no-otg/ocd/isp1301/Kconfig 1970-01-01 01:00:00.000000000 +0100 ++++ linux/drivers/otg/ocd/isp1301/Kconfig 2006-09-01 21:41:31.000000000 +0200 +@@ -0,0 +1,15 @@ ++ ++# ++# Copyright (c) 2004 Belcarra ++# ++ ++# OMAP 1610 H2 development board ++# ++config OTG_ISP1301_OMAP_H2 ++ tristate "OMAP 1610 H2 Development board - ISP 1301 (i2c)" ++ depends on I2C && OTG && ARCH_OMAP && OMAP_H2 ++ ++config OTG_ISP1301_MX2ADS ++ tristate "MX2ADS Development board - ISP 1301 (i2c)" ++ depends on I2C && OTG && ARCH_MX2ADS ++ +diff -uNr linux/drivers/no-otg/ocd/isp1301/Makefile linux/drivers/otg/ocd/isp1301/Makefile +--- linux/drivers/no-otg/ocd/isp1301/Makefile 1970-01-01 01:00:00.000000000 +0100 ++++ linux/drivers/otg/ocd/isp1301/Makefile 2006-09-01 21:41:31.000000000 +0200 +@@ -0,0 +1,96 @@ ++# ++# Makefile for the kernel USBD (device not host) drivers. ++# ++# Copyright (c) 2004 Belcarra ++ ++# 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. ++ ++O_TARGET := isp1301_target.o ++list-multi := isp1301_tcd.o ++ ++# Objects that export symbols. ++ ++# Multipart objects. ++ ++omap_h2_tcd-objs := tcd-init-l24.o tcd-omap-h2.o isp1301.o i2c-l26.o tcd.o isp1301-procfs.o ++mainstone_tcd-objs := tcd-init-l24.o tcd-mainstone.o tcd.o i2c-l26.o isp1301.o isp1301-procfs.o thread-l24.o ++ ++# Optional parts of multipart objects. ++ ++# Object file lists. ++ ++obj-y := ++obj-m := ++obj-n := ++obj- := ++ ++# Each configuration option enables a list of files. ++ ++obj-$(CONFIG_OTG_ISP1301_OMAP_H2) += omap_h2_tcd.o ++obj-$(CONFIG_OTG_ISP1301_MAINSTONE) += mainstone_tcd.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. ++ ++DOT_DIR=$(TCD_DIR)/isp1301 ++ ++include $(TOPDIR)/Rules.make ++OTG=$(TOPDIR)/drivers/otg ++OCD_DIR=$(OTG)/ocd ++INCLUDE_DIRS = -I$(OTG) ++EXTRA_CFLAGS += -Wno-missing-prototypes -Wno-unused -Wno-format ${INCLUDE_DIRS} ++EXTRA_CFLAGS_nostdinc += -Wno-missing-prototypes -Wno-unused -Wno-format ${INCLUDE_DIRS} ++ ++PCD=$(OTG)/ocd/otg-pcd ++vpath %.c $(USBDCORE_DIR) $(OCD_DIR) $(PCD) ++ ++ ++# Link rules for multi-part drivers. ++ ++omap_h2_tcd.o: $(omap_h2_tcd-objs) ++ $(LD) -r -o $@ $(iomap_h2_tcd-objs) ++ ++mainstone_tcd.o: $(mainstone_tcd-objs) ++ $(LD) -r -o $@ $(mainstone_tcd-objs) ++ ++# dependencies: ++ ++# local ++ ++ +diff -uNr linux/drivers/no-otg/ocd/isp1301/Makefile-l26 linux/drivers/otg/ocd/isp1301/Makefile-l26 +--- linux/drivers/no-otg/ocd/isp1301/Makefile-l26 1970-01-01 01:00:00.000000000 +0100 ++++ linux/drivers/otg/ocd/isp1301/Makefile-l26 2006-09-01 21:41:31.000000000 +0200 +@@ -0,0 +1,26 @@ ++# ++# Makefile for the kernel USBD (device not host) drivers. ++# ++# Copyright (c) 2004 Belcarra Technologies Corp ++ ++DOT_DIR=$(TCD_DIR)/isp1301 ++ ++OTG=$(TOPDIR)/drivers/otg ++HCD_DIR=$(OTG)/hcd ++TCD_DIR=$(OTG)/tcd ++USBDCORE_DIR=$(OTG)/usbdcore ++OTGCORE_DIR=$(OTG)/otgcore ++INCLUDE_DIRS = -I$(OTG) ++EXTRA_CFLAGS += -Wno-missing-prototypes -Wno-unused -Wno-format ${INCLUDE_DIRS} ++EXTRA_CFLAGS_nostdinc += -Wno-missing-prototypes -Wno-unused -Wno-format ${INCLUDE_DIRS} ++ ++vpath %.c $(USBDCORE_DIR) $(OCD_DIR) ++ ++ ++# Link rules for multi-part drivers. ++ ++omap_h2_tcd-objs := ../tcd-init-l24.o tcd-omap-h2.o isp1301.o i2c-l26.o ../tcd.o ++obj-$(CONFIG_OTG_ISP1301_OMAP_H2) += omap_h2_tcd.o ++ ++ ++ +diff -uNr linux/drivers/no-otg/ocd/isp1301/isp1301-procfs.c linux/drivers/otg/ocd/isp1301/isp1301-procfs.c +--- linux/drivers/no-otg/ocd/isp1301/isp1301-procfs.c 1970-01-01 01:00:00.000000000 +0100 ++++ linux/drivers/otg/ocd/isp1301/isp1301-procfs.c 2006-09-01 21:41:31.000000000 +0200 +@@ -0,0 +1,385 @@ ++/* ++ * otg/ocd/isp1301/isp1301-procfs.c - USB Device Core Layer ++ * ++ * Copyright (c) 2004 Belcarra ++ * ++ * By: ++ * Stuart Lynne <sl@belcarra.com>, ++ * Tom Rushworth <tbr@belcarra.com>, ++ * Bruce Balden <balden@belcarra.com> ++ * ++ * ++ */ ++/*! ++ * @file otg/ocd/isp1301/isp1301-procfs.c ++ * @brief Implement /proc/isp1301 to dump ISP1301 registers. ++ * ++ * ++ * @ingroup ISP1301TCD ++ */ ++ ++ ++#include <otg/pcd-include.h> ++#include "otghw/isp1301-hardware.h" ++#include <otghw/isp1301.h> ++ ++#ifdef CONFIG_ARCH_MX2ADS ++#include <asm/arch/mx2.h> ++#define MX2_OTG_XCVR_DEVAD 0x18 ++#define MX2_SEQ_OP_REG 0x19 ++#define MX2_SEQ_RD_STARTAD 0x1a ++#define MX2_I2C_OP_CTRL_REG 0x1b ++#define MX2_SCLK_TO_SCL_HPER 0x1e ++#define MX2_I2C_INTERRUPT_AND_CTRL 0x1f ++ ++#define OTG_BASE_ADDR 0x10024000 ++//#define OTG_I2C_BASE (OTG_BASE_ADDR+0x100) ++ ++#endif /* CONFIG_ARCH_MX2ADS */ ++ ++#ifdef CONFIG_OTG_ISP1301_PROCFS ++/* Proc Filesystem *************************************************************************** */ ++ ++extern struct isp1301_private isp1301_private; ++ ++#define MAX_HISTORY 6 ++struct reg_list { ++ u8 reg; ++ u8 size; ++ char *name; ++ u32 values[MAX_HISTORY]; ++}; ++ ++#define REG(r, s) {r, s, #r, } ++ ++struct reg_list isp1301_prod_list[] = { ++ REG(ISP1301_VENDOR_ID, 2), ++ REG(ISP1301_PRODUCT_ID, 2), ++ REG(ISP1301_VERSION_ID, 2), ++ { 0, 1, NULL,}, ++}; ++struct reg_list isp1301_reg_list[] = { ++ REG(ISP1301_OTG_CONTROL_SET, 1), ++ REG(ISP1301_INTERRUPT_SOURCE, 1), ++ REG(ISP1301_INTERRUPT_LATCH_SET, 1), ++ REG(ISP1301_INTERRUPT_ENABLE_LOW_SET, 1), ++ REG(ISP1301_INTERRUPT_ENABLE_HIGH_SET, 1), ++ REG(ISP1301_MODE_CONTROL_1_SET, 1), ++ { 0, 1, NULL,}, ++}; ++struct reg_list isp1301_spec_list[] = { ++ REG(ISP1301_MODE_CONTROL_2_SET, 1), ++ REG(ISP1301_OTG_STATUS, 1), ++ { 0, 1, NULL,}, ++}; ++struct reg_list max3301e_spec_list[] = { ++ REG(MAX3301E_SPECIAL_FUNCTION_1_SET, 1), ++ REG(MAX3301E_SPECIAL_FUNCTION_2_SET, 1), ++ { 0, 1, NULL,}, ++}; ++#ifdef CONFIG_ARCH_MX2ADS ++struct reg_list mx21_spec_list[] = { ++ REG(MX2_OTG_XCVR_DEVAD, 1), ++ REG(MX2_SEQ_OP_REG, 1), ++ REG(MX2_SEQ_RD_STARTAD, 1), ++ REG(MX2_I2C_OP_CTRL_REG, 1), ++ REG(MX2_SCLK_TO_SCL_HPER, 1), ++ REG(MX2_I2C_INTERRUPT_AND_CTRL, 1), ++ { 0, 1, NULL,}, ++}; ++#endif /* CONFIG_ARCH_MX2ADS */ ++ ++void isp1301_update(struct reg_list *list) ++{ ++ for (; list && list->name; list++) { ++ TRACE_MSG1(TCD, "list: %s", list->name); ++ memmove(list->values + 1, list->values, sizeof(list->values) - sizeof(u32)); ++ switch(list->size) { ++ case 1: ++ list->values[0] = i2c_readb(list->reg); ++ break; ++ case 2: ++ list->values[0] = i2c_readw(list->reg); ++ break; ++ case 4: ++ list->values[0] = i2c_readl(list->reg); ++ break; ++ } ++ } ++} ++ ++#ifdef CONFIG_ARCH_MX2ADS ++static u8 __inline__ mx2_rb(u32 port) ++{ ++ return *(volatile u8 *) (MX2_IO_ADDRESS(port + OTG_I2C_BASE)); ++} ++ ++ ++void mx21_update(struct reg_list *list) ++{ ++ for (; list && list->name; list++) { ++ memmove(list->values + 1, list->values, sizeof(list->values) - sizeof(u32)); ++ list->values[0] = mx2_rb(list->reg); ++ } ++} ++ ++#endif /* CONFIG_ARCH_MX2ADS */ ++ ++void isp1301_update_all(void) ++{ ++ isp1301_update(isp1301_reg_list); ++ switch (isp1301_private.transceiver_map->transceiver_type) { ++ case isp1301: ++ isp1301_update(isp1301_spec_list); ++ break; ++ case max3301e: ++ isp1301_update(max3301e_spec_list); ++ break; ++ default: ++ break; ++ } ++#ifdef CONFIG_ARCH_MX2ADS ++ mx21_update(mx21_spec_list); ++#endif /* CONFIG_ARCH_MX2ADS */ ++ ++} ++ ++ ++ ++/* ++ * 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); ++} ++ ++int isp1301_dump(char *buf, char *name, char *fmt, struct reg_list *reg) ++{ ++ int len = 0, i; ++ len += sprintf (buf + len, "%-20s %-34s [%03x]: ", name, reg->name, reg->reg); ++ len += sprintf (buf + len, fmt, reg->values[0]); ++ for (i = 1; i < MAX_HISTORY; i++) ++ if (reg->values[i - 1] == reg->values[i]) ++ len += sprintf (buf + len, " "); ++ else ++ len += sprintf (buf + len, fmt, reg->values[i]); ++ len += sprintf (buf + len, "\n"); ++ return len; ++} ++ ++int isp1301_dump_list(char * buf, char *name, struct reg_list *list) ++{ ++ int len = 0; ++ for (; list && list->name; list++) ++ switch(list->size) { ++ case 1: len += isp1301_dump(buf + len, name, " %02x", list); break; ++ case 2: len += isp1301_dump(buf + len, name, " %04x", list); break; ++ case 4: len += isp1301_dump(buf + len, name, " %08x", list); break; ++ } ++ return len; ++} ++ ++char *isp1301_otg_control[8] = { ++ "DP_PULLUP", ++ "DM_PULLUP", ++ "DP_PULLDOWN", ++ "DM_PULLDOWN", ++ "ID_PULLDOWN", ++ "VBUS_DRV", ++ "VBUS_DISCHRG", ++ "VBUS_CHRG", ++}; ++char *isp1301_interrupt_source[8] = { ++ "VBUS_VLD", ++ "SESS_VLD", ++ "DP_HI", ++ "ID_GND", ++ "DM_HI", ++ "ID_FLOAT", ++ "BDIS_ACON", ++ "CR_INT", ++}; ++ ++char *isp1301_mode_control_1[8] = { ++ "SPEED_REG", ++ "SUSPEND_REG", ++ "DAT_SE0", ++ "TRANSP_EN", ++ "BDIS_ACON_EN", ++ "OE_INT_EN", ++ "UART_EN", ++ NULL, ++}; ++ ++char *isp1301_otg_status[8] = { ++ NULL, NULL, NULL, NULL, NULL, NULL, ++ "B_SESS_END", ++ "B_SESS_VLD", ++}; ++ ++int isp1301_detailed(char *buf, char *name, u8 reg, char **detail) ++{ ++ u8 val; ++ int i; ++ int len = 0; ++ ++ val = i2c_readb(reg); ++ for (i = 7; i >= 0; i--) { ++ ++ if (3 == (i % 4)) ++ len += sprintf (buf + len, "\n%-20s [%02d] ", name, reg); ++ ++ if (detail[i]) ++ len += sprintf (buf + len, "%14s%s ", detail[i], (val & (1 << i)) ? " " : "/"); ++ else ++ len += sprintf (buf + len, "%14s%s ", "", " "); ++ } ++ len += sprintf (buf + len, "\n", name); ++ ++ return len; ++} ++ ++int isp1301_dump_all(char *buf) ++{ ++ int len = 0; ++ len += isp1301_dump_list(buf + len, "ISP1301 Standard", isp1301_reg_list); ++ switch (isp1301_private.transceiver_map->transceiver_type) { ++ case isp1301: ++ len += isp1301_dump_list(buf + len, "ISP1301 Extra", isp1301_spec_list); ++ break; ++ case max3301e: ++ len += isp1301_dump_list(buf + len, "MAX3301E Extra", max3301e_spec_list); ++ break; ++ default: ++ break; ++ } ++#ifdef CONFIG_ARCH_MX2ADS ++ len += isp1301_dump_list(buf + len, "MX21 ADS Extra", mx21_spec_list); ++#endif /* CONFIG_ARCH_MX2ADS */ ++ return len; ++} ++ ++int isp1301_dump_detail(char *buf) ++{ ++ int len = 0; ++ ++ len += isp1301_detailed(buf + len, "MODE CONTROL 1", ISP1301_MODE_CONTROL_1, isp1301_mode_control_1); ++ len += isp1301_detailed(buf + len, "INTERRUPT ENABLE", ISP1301_INTERRUPT_ENABLE_HIGH, isp1301_interrupt_source); ++ ++ len += isp1301_detailed(buf + len, "OTG CONTROL", ISP1301_OTG_CONTROL_SET, isp1301_otg_control); ++ len += isp1301_detailed(buf + len, "INTERRUPT SOURCE", ISP1301_INTERRUPT_SOURCE, isp1301_interrupt_source); ++ len += isp1301_detailed(buf + len, "OTG STATUS", ISP1301_OTG_STATUS, isp1301_otg_status); ++ return len; ++} ++ ++ ++/*! ++ * isp1301_device_proc_read - implement proc file system read. ++ * ++ * 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 isp1301_device_proc_read_functions (struct file *file, char *buf, size_t count, loff_t * pos) ++{ ++ unsigned long page; ++ int len = 0; ++ int index; ++ int i; ++ u32 r; ++ ++ int config_size; ++ ++ // get a page, max 4095 bytes of data... ++ RETURN_EINVAL_UNLESS ((page = get_free_page (GFP_KERNEL))); ++ ++ len = 0; ++ index = (*pos)++; ++ ++ //printk(KERN_INFO"%s: index: %d\n", __FUNCTION__, index); ++ switch(index) { ++ case 0: ++ len += sprintf ((char *) page + len, "ISP1301 Transceiver Registers\n"); ++ ++ TRACE_MSG0(TCD, "UPDATING"); ++ isp1301_update_all(); ++ TRACE_MSG0(TCD, "UPDATE FINISHED"); ++ len += sprintf ((char *) page + len , "Vendor: %04x Product: %04x Revision: %04x %s\n", ++ isp1301_private.vendor, isp1301_private.product, ++ isp1301_private.revision, isp1301_private.transceiver_map->name ++ ); ++ ++ len += isp1301_dump_all((char *) page + len); ++ len += sprintf ((char *) page + len, "\n"); ++ ++ break; ++ ++ case 1: ++ len += sprintf ((char *) page + len, "\n--\n"); ++ len += isp1301_dump_detail((char *) page + len); ++ len += sprintf ((char *) page + len, "\n"); ++ break; ++ ++ default: ++ break; ++ ++ } ++ ++ //printk(KERN_INFO"%s: len: %d\n", __FUNCTION__, len); ++ ++ if (len > count) ++ len = -EINVAL; ++ ++ else if ((len > 0) && copy_to_user (buf, (char *) page, len)) ++ len = -EFAULT; ++ ++ //printk(KERN_INFO"%s: len: %d\n", __FUNCTION__, len); ++ free_page (page); ++ return len; ++} ++ ++static struct file_operations isp1301_device_proc_operations_functions = { ++ read:isp1301_device_proc_read_functions, ++}; ++ ++/* Module init ******************************************************************************* */ ++ ++static int isp1301_procfs_init (void) ++{ ++ struct proc_dir_entry *p; ++ ++ // create proc filesystem entries ++ if ((p = create_proc_entry ("isp1301", 0, 0)) == NULL) ++ return -ENOMEM; ++ ++ p->proc_fops = &isp1301_device_proc_operations_functions; ++ ++ isp1301_update_all(); ++ ++ return 0; ++} ++static void isp1301_procfs_exit (void) ++{ ++ remove_proc_entry ("isp1301", NULL); ++} ++#else ++static int isp1301_procfs_init (void) { return 0; ++} ++static void isp1301_procfs_exit (void) { } ++#endif +diff -uNr linux/drivers/no-otg/ocd/isp1301/isp1301.c linux/drivers/otg/ocd/isp1301/isp1301.c +--- linux/drivers/no-otg/ocd/isp1301/isp1301.c 1970-01-01 01:00:00.000000000 +0100 ++++ linux/drivers/otg/ocd/isp1301/isp1301.c 2006-09-01 21:41:31.000000000 +0200 +@@ -0,0 +1,923 @@ ++/* ++ * otg/ocd/isp1301/isp1301.c -- USB Transceiver Controller driver ++ * ++ * Copyright (c) 2004 Belcarra ++ * ++ * By: ++ * Stuart Lynne <sl@lbelcarra.com>, ++ * Tom Rushworth <tbr@belcarra.com>, ++ * Bruce Balden <balden@belcarra.com> ++ * ++ */ ++/*! ++ * @file otg/ocd/isp1301/isp1301.c ++ * @brief ISP1301 OTG Transceiver Driver. ++ * ++ * This is the generic ISP1301 TCD core support. ++ * ++ * Notes: ++ * ++ * 1. The ISP1301 can control the speed and suspend directly, would it be ++ * appropriate to allow state machine to control this. ++ * ++ * 2. The ISP1301 has auto connect feature, can this be used without change ++ * to state machine. ++ * ++ * 3. The ISP1301 can control the ADR/PSW pin to enable / disable external ++ * charge pump. ++ * ++ * @ingroup ISP1301TCD ++ */ ++ ++#include <otg/otg-compat.h> ++ ++#include <asm/irq.h> ++#include <asm/system.h> ++#include <asm/io.h> ++#include <linux/i2c.h> ++ ++#include <otg/usbp-chap9.h> ++#include <otg/usbp-bus.h> ++#include <otg/otg-trace.h> ++#include <otg/otg-api.h> ++#include <otg/otg-hcd.h> ++#include <otg/otg-tcd.h> ++#include <otg/otg-ocd.h> ++#include <otg/otg-pcd.h> ++ ++#include <otghw/isp1301-hardware.h> ++#include <otghw/isp1301.h> ++ ++#ifdef CONFIG_OMAP_H2 ++#include <asm/arch/gpio.h> ++#include <asm/arch/mux.h> ++#endif /* CONFIG_OMAP_H2 */ ++ ++struct otg_transceiver_map isp1301_transceiver_map[] = { ++ { isp1301, 0x4cc, 0x1301, 0x00, "Phillips ISP1301", }, ++ { max3301e, 0x6a0b, 0x0133, 0x00, "Maxim MAX3301E", }, ++ { 0, 0, 0, 0, "Unknown Transceiver", }, ++}; ++ ++struct isp1301_private isp1301_private; ++ ++ ++ ++/* ********************************************************************************************* */ ++#define TRACE_I2CB(t,r) TRACE_MSG3(t, "%-40s[%02x] %02x", #r, r, i2c_readb(r)) ++#define TRACE_I2CW(t,r) TRACE_MSG3(t, "%-40s[%02x] %04x", #r, r, i2c_readw(r)) ++#define TRACE_GPIO(t,b,r) TRACE_MSG2(t, "%-40s %04x", #r, readw(b + r)) ++#define TRACE_REGL(t,r) TRACE_MSG2(t, "%-40s %08x", #r, readl(r)) ++ ++ ++ ++/*! isp1301_int_src - update interrupt source test mask ++ * ++ * This sets the current mask and updates the interrupt registers to match. ++ */ ++void isp1301_int_src(u8 int_src) ++{ ++ TRACE_MSG1(TCD, "setting int_src %02x", int_src); ++ isp1301_private.int_src = int_src; ++ i2c_writeb(ISP1301_INTERRUPT_ENABLE_LOW_CLR, ~int_src); ++ i2c_writeb(ISP1301_INTERRUPT_ENABLE_HIGH_CLR, ~int_src); ++ i2c_writeb(ISP1301_INTERRUPT_ENABLE_LOW_SET, int_src); ++ i2c_writeb(ISP1301_INTERRUPT_ENABLE_HIGH_SET, int_src); ++ i2c_writeb(ISP1301_INTERRUPT_LATCH_CLR, int_src); ++} ++ ++/*! isp1301_int_src_set - add to the interrupt source mask ++ * ++ */ ++void isp1301_int_src_set(u8 int_src) ++{ ++ isp1301_int_src(int_src |= isp1301_private.int_src); ++} ++ ++/*! isp1301_int_src_clr - remove from the interrup source mask ++ */ ++void isp1301_int_src_clr(u8 int_src) ++{ ++ isp1301_int_src(int_src = isp1301_private.int_src & ~int_src); ++} ++ ++/* ********************************************************************************************* */ ++ ++typedef struct isp1301_test { ++ u32 input; ++ char *name; ++ u8 mask; ++} isp1301_test_t; ++ ++#define OV(i, m) {i, #i, m} ++ ++struct isp1301_test isp1301_int_src_tests[9] = { ++ ++ OV(VBUS_VLD, ISP1301_VBUS_VLD), ++ OV(A_SESS_VLD, ISP1301_SESS_VLD), ++ OV(DP_HIGH, ISP1301_DP_HI), ++ OV(ID_GND, ISP1301_ID_GND), ++ OV(DM_HIGH, ISP1301_DM_HI), ++ OV(ID_FLOAT, ISP1301_ID_FLOAT), ++ OV(BDIS_ACON, ISP1301_BDIS_ACON), ++ OV(CR_INT_DET, ISP1301_CR_INT), ++ {0, NULL, 0}, ++}; ++ ++struct isp1301_test isp1301_otg_status_tests[3] = { ++ ++ OV(B_SESS_END, ISP1301_B_SESS_END), ++ OV(B_SESS_VLD, ISP1301_B_SESS_VLD), ++ {0, NULL, 0}, ++}; ++ ++/*! isp1301_event() - set input bit based on current settings and interrupt mask ++ */ ++u64 isp1301_event(struct otg_instance *otg, struct isp1301_test *tests, int val, int testmask, char *msg) ++{ ++ int i; ++ u64 inputs = 0; ++ TRACE_MSG3(TCD, "val: %02x mask: %02x %s", val, testmask, msg); ++ for (i = 0; tests[i].mask; i++) { ++ u64 input = tests[i].input; ++ u8 mask = tests[i].mask; ++ CONTINUE_UNLESS(mask & testmask); ++ inputs |= (val & mask) ? input : _NOT(input); ++ TRACE_MSG6(TCD, "%s%s %02x %08x inputs: %08x %08x", ++ tests[i].name, (val & mask) ? "" : "/", tests[i].mask, tests[i].input, ++ (u32) (inputs >> 32), (u32)(inputs & 0xffffffff) ++ ); ++ } ++ return inputs; ++} ++ ++ ++ ++int isp1301_bh_first; ++int isp1301_debounce; ++u64 isp1301_ticks; ++ ++void isp1301_info(char *msg, u8 value) ++{ ++ u64 new_ticks = otg_tmr_ticks(); ++ u64 ticks = otg_tmr_elapsed(&new_ticks, &isp1301_ticks); ++ ++ if (ticks < 10000) ++ TRACE_MSG3(PCD, "ISP1301 %d uS %s: %02x", (u32)(ticks & 0xffffffff), msg, value); ++ else if (ticks < 10000000) ++ TRACE_MSG3(PCD, "ISP1301 %d mS %s: %02x", (u32)((ticks >> 10) & 0xffffffff), msg, value); ++ else ++ TRACE_MSG3(PCD, "ISP1301 %d S %s: %02x", (u32)((ticks >> 20) & 0xffffffff), msg, value); ++ ++ isp1301_ticks = new_ticks; ++} ++ ++ ++void isp1301_trace(u8 int_src, u8 int_changed, char *msg) ++{ ++ ++ u64 new_ticks = otg_tmr_ticks(); ++ u64 ticks = otg_tmr_elapsed(&new_ticks, &isp1301_ticks); ++ ++ if (ticks < 10000) ++ TRACE_MSG4(PCD, "ISP1301 %d uS %s: src: %02x chng: %02x", ++ (u32)(ticks & 0xffffffff), msg, int_src, int_changed); ++ else if (ticks < 10000000) ++ TRACE_MSG4(PCD, "ISP1301 %d mS %s src: %02x chng: %02x", ++ (u32)((ticks >> 10) & 0xffffffff), msg, int_src, int_changed); ++ else ++ TRACE_MSG4(PCD, "ISP1301 %d S %s src: %02x chng: %02x ", ++ (u32)((ticks >> 20) & 0xffffffff), msg, int_src, int_changed); ++ ++ isp1301_ticks = new_ticks; ++} ++ ++/*! isp1301_bh ++ */ ++void isp1301_bh(void *arg) ++{ ++ u64 inputs = 0; ++ u8 int_lat, int_src1 = 0, int_src2 = 0, otg_status = 0, special_function_22 = 0; ++ static u8 int_src_saved, otg_status_saved, special_function_22_saved; ++ ++ //TRACE_MSG0(TCD, "--"); ++ /* read the interrupt latch and and clear latch and update otg ++ */ ++ if ((int_lat = i2c_readb(ISP1301_INTERRUPT_LATCH_SET))) { ++ //TRACE_MSG1(TCD, "CLEARING INT LAT: %02x", int_lat); ++ isp1301_info("INT LAT", int_lat); ++ i2c_writeb(ISP1301_INTERRUPT_LATCH_CLR, int_lat); ++ } ++ ++ isp1301_info("INT_LAT", int_lat); ++ ++ /* If int_lat shows a change or we are forcing an update, read interrupt source. ++ * Note that we mask DP_HI and DM_HI when LOC_CONN is set, we don't want them ++ * when USB Bus is in use. ++ * ++ * Mask with bits we are currently interested in. ++ * ++ * Ensure that we have two matching reads to ensure that value is stable. ++ * Also ensure that interrupt source is non-zero. ++ */ ++ ++ int_src1 = i2c_readb(ISP1301_INTERRUPT_SOURCE); ++ isp1301_trace(int_src1, int_src_saved ^ int_src1, "INT_SRC_1"); ++ ++ do { ++ int_src2 = i2c_readb(ISP1301_INTERRUPT_SOURCE); // re-read interrupt source ++ BREAK_IF(int_src1 && (int_src1 & (ISP1301_ID_GND | ISP1301_ID_FLOAT)) && ++ (int_src1 == int_src2)); // finished if non-zero and same ++ isp1301_trace(int_src2, int_src_saved ^ int_src2, "INT_SRC_2"); ++ int_src1 = int_src2; // save most recent read value ++ } while (1); ++ ++ ++ #if 0 ++ while ( (int_src1 != (int_src2 = i2c_readb(ISP1301_INTERRUPT_SOURCE) & isp1301_private.int_src))) { ++ isp1301_trace(int_src2, int_src_saved ^ int_src2, "INT_SRC_2"); ++ int_src1 = int_src2; ++ } ++ #endif ++ ++ if (isp1301_bh_first || int_src_saved ^ (int_src1 & isp1301_private.int_src)) { ++ int_src_saved = int_src1; ++ inputs |= isp1301_event(tcd_instance->otg, isp1301_int_src_tests, int_src1, ++ isp1301_private.int_src, "ISP1301 INT SRC"); ++ if (int_src1 & ISP1301_VBUS_VLD) { ++ TRACE_MSG0(TCD, "VBUS_VLD, setting B_SESS_VLD"); ++ inputs |= B_SESS_VLD; ++ } ++ else if (!(int_src1 & ISP1301_SESS_VLD)) { ++ TRACE_MSG0(TCD, "A_SESS_VLD/, setting B_SESS_VLD/"); ++ inputs |= B_SESS_VLD_; ++ } ++ } ++ ++ /* further work if B-Device ++ */ ++ if ( !(int_src1 & ISP1301_ID_GND) || !(int_src_saved & ISP1301_ID_GND) ) { ++ /* read the otg_status register and update otg state machine ++ * ++ * XXX this needs to be conditional on Transceiver type. The ++ * MAX3301E for example supports sess_end in a separate register. ++ */ ++ switch (isp1301_private.transceiver_map->transceiver_type) { ++ case isp1301: ++ otg_status = i2c_readb(ISP1301_OTG_STATUS); ++ isp1301_trace(otg_status, int_src_saved ^ int_src1, "OTG_STATUS"); ++ TRACE_MSG3(TCD, "otg_status_saved: %02x otg_status: %02x chng: %02x", ++ otg_status_saved, otg_status, otg_status_saved ^ otg_status); ++ ++ if (isp1301_bh_first || (otg_status_saved ^ otg_status)) { ++ inputs |= isp1301_event(tcd_instance->otg, isp1301_otg_status_tests, otg_status, 0xff, ++ "ISP1301 OTG STATUS"); ++ otg_status_saved = otg_status; ++ } ++ ++ #if 0 ++ isp1301_info("OTG STATUS", otg_status); ++ if (isp1301_bh_first) ++ otg_status_saved = ~otg_status; ++ ++ otg_status_saved = isp1301_update_otg_status( tcd_instance->otg, otg_status, ++ otg_status_saved ^ otg_status); ++ #endif ++ break; ++ ++ // XXX ++ case max3301e: ++ special_function_22 = i2c_readb(MAX3301E_SPECIAL_FUNCTION_2_SET); ++ break; ++ case unknown_transceiver: ++ break; ++ } ++ } ++ if (inputs) otg_event(tcd_instance->otg, inputs, TCD, "ISP1301"); ++ isp1301_bh_first = 0; ++} ++ ++ ++/*! isp1301_bh_wakeup - wakeup the isp1301 bottom half ++ */ ++void isp1301_bh_wakeup(int first) ++{ ++ //TRACE_MSG0(TCD, "--"); ++ isp1301_bh_first |= first; ++ SCHEDULE_WORK(isp1301_private.bh); ++} ++ ++ ++/* ********************************************************************************************** */ ++/*! isp1301_vbus - Do we have Vbus (cable attached?) ++ * Return non-zero if Vbus is detected. ++ * ++ */ ++int isp1301_vbus (struct otg_instance *otg) ++{ ++ return 0; ++} ++ ++/*! isp1301_id - Do we have Vbus (cable attached?) ++ * Return non-zero if Vbus is detected. ++ * ++ */ ++int isp1301_idvbus (struct otg_instance *otg) ++{ ++ return 0; ++} ++ ++ ++/* ********************************************************************************************* */ ++/*! isp1301_tcd_en() - used to enable ++ * ++ */ ++void isp1301_tcd_en(struct otg_instance *otg, u8 flag) ++{ ++ switch (flag) { ++ case SET: ++ TRACE_MSG0(TCD, "SET"); ++ break; ++ case PULSE: ++ TRACE_MSG0(TCD, "PULSE"); ++ break; ++ case RESET: ++ TRACE_MSG0(TCD, "RESET"); ++ break; ++ } ++ isp1301_bh_wakeup(TRUE); ++} ++ ++ ++/*! isp1301_chrg_vbus - used to enable or disable B-device Vbus charging ++ */ ++void isp1301_chrg_vbus(struct otg_instance *otg, u8 flag) ++{ ++ //TRACE_MSG0(TCD, "--"); ++ switch (flag) { ++ case SET: ++ TRACE_MSG0(TCD, "CHRG_VBUS_SET"); ++ i2c_writeb(ISP1301_OTG_CONTROL_SET, ISP1301_VBUS_CHRG); ++ break; ++ case RESET: ++ TRACE_MSG0(TCD, "CHRG_VBUS_RESET"); ++ i2c_writeb(ISP1301_OTG_CONTROL_CLR, ISP1301_VBUS_RESET); ++ break; ++ case PULSE: ++ break; ++ } ++} ++ ++/*! isp1301_drv_vbus - used to enable or disable A-device driving Vbus ++ */ ++void isp1301_drv_vbus(struct otg_instance *otg, u8 flag) ++{ ++ //TRACE_MSG0(TCD, "--"); ++ switch (flag) { ++ case SET: ++ TRACE_MSG0(TCD, "DRV_VBUS_SET"); ++ i2c_writeb(ISP1301_OTG_CONTROL_SET, ISP1301_VBUS_DRV); ++ break; ++ case RESET: ++ TRACE_MSG0(TCD, "DRV_VBUS_RESET"); ++ i2c_writeb(ISP1301_OTG_CONTROL_CLR, ISP1301_VBUS_RESET); ++ break; ++ } ++} ++ ++/*! isp1301_dischrg_vbus - used to enable Vbus discharge ++ */ ++void isp1301_dischrg_vbus(struct otg_instance *otg, u8 flag) ++{ ++ struct tcd_instance *tcd = otg->tcd; ++ //TRACE_MSG0(TCD, "--"); ++ switch (flag) { ++ case SET: ++ TRACE_MSG0(TCD, "OUTPUT: TCD_DISCHRG_VBUS_SET"); ++ i2c_writeb(ISP1301_OTG_CONTROL_SET, ISP1301_VBUS_DISCHRG); ++ break; ++ case RESET: ++ TRACE_MSG0(TCD, "OUTPUT: TCD_DISCHRG_VBUS_RESET"); ++ i2c_writeb(ISP1301_OTG_CONTROL_CLR, ISP1301_VBUS_RESET); ++ break; ++ } ++} ++ ++/*! isp1301_mx21_vbus_drain - used to enable Vbus discharge ++ */ ++void isp1301_mx21_vbus_drain_func(struct otg_instance *otg, u8 flag) ++{ ++ struct tcd_instance *tcd = otg->tcd; ++ //TRACE_MSG0(TCD, "--"); ++ switch (flag) { ++ case SET: ++ TRACE_MSG0(TCD, "OUTPUT: TCD_DISCHRG_VBUS_SET"); ++ i2c_writeb(ISP1301_OTG_CONTROL_SET, ISP1301_VBUS_DISCHRG); ++ break; ++ case RESET: ++ TRACE_MSG0(TCD, "OUTPUT: TCD_DISCHRG_VBUS_RESET"); ++ i2c_writeb(ISP1301_OTG_CONTROL_CLR, ISP1301_VBUS_RESET); ++ break; ++ } ++} ++ ++/*! isp1301_dp_pullup_func - used to enable or disable peripheral connecting to bus ++ * ++ * C.f. 5.1.6, 5.1.7, 5.2.4 and 5.2.5 ++ * ++ * host peripheral ++ * d+ pull-up clr set ++ * d+ pull-down set clr ++ * ++ * d- pull-up clr clr ++ * d- pull-down set set ++ * ++ */ ++void isp1301_dp_pullup_func(struct otg_instance *otg, u8 flag) ++{ ++ struct tcd_instance *tcd = (struct tcd_instance *)otg->tcd; ++ //TRACE_MSG0(TCD, "--"); ++ switch (flag) { ++ case SET: ++ isp1301_private.flags |= ISP1301_LOC_CONN; ++ TRACE_MSG0(TCD, "ISP1301_LOC_CONN SET - Set DP PULLUP"); ++ i2c_writeb(ISP1301_OTG_CONTROL_SET, ISP1301_DP_PULLUP); ++ break; ++ ++ case RESET: ++ isp1301_private.flags &= ~ISP1301_LOC_CONN; ++ TRACE_MSG0(TCD, "ISP1301_LOC_CONN RESET - Clr DP PULLUP"); ++ i2c_writeb(ISP1301_OTG_CONTROL_CLR, ISP1301_DP_PULLUP); ++ break; ++ } ++} ++ ++/*! isp1301_dm_pullup_func - used to enable or disable peripheral connecting to bus ++ * ++ */ ++void isp1301_dm_pullup_func(struct otg_instance *otg, u8 flag) ++{ ++ struct tcd_instance *tcd = (struct tcd_instance *)otg->tcd; ++ //TRACE_MSG0(TCD, "--"); ++ switch (flag) { ++ case SET: ++ isp1301_private.flags |= ISP1301_LOC_CONN; ++ TRACE_MSG0(TCD, "ISP1301_LOC_CONN SET - Set DM PULLUP"); ++ i2c_writeb(ISP1301_OTG_CONTROL_SET, ISP1301_DM_PULLUP); ++ break; ++ ++ case RESET: ++ isp1301_private.flags &= ~ISP1301_LOC_CONN; ++ TRACE_MSG0(TCD, "ISP1301_LOC_CONN RESET - Clr DM PULLUP"); ++ i2c_writeb(ISP1301_OTG_CONTROL_CLR, ISP1301_DM_PULLUP); ++ break; ++ } ++} ++ ++/*! isp1301_dp_pulldown_func - used to enable or disable peripheral connecting to bus ++ * ++ * C.f. 5.1.6, 5.1.7, 5.2.4 and 5.2.5 ++ * ++ * host peripheral ++ * d+ pull-up clr set ++ * d+ pull-down set clr ++ * ++ * d- pull-up clr clr ++ * d- pull-down set set ++ * ++ */ ++void isp1301_dp_pulldown_func(struct otg_instance *otg, u8 flag) ++{ ++ struct tcd_instance *tcd = (struct tcd_instance *)otg->tcd; ++ //TRACE_MSG0(TCD, "--"); ++ switch (flag) { ++ case SET: ++ isp1301_private.flags |= ISP1301_LOC_CONN; ++ TRACE_MSG0(TCD, "ISP1301_LOC_CONN SET - Set DP PULLUP"); ++ i2c_writeb(ISP1301_OTG_CONTROL_SET, ISP1301_DP_PULLDOWN); ++ break; ++ ++ case RESET: ++ isp1301_private.flags &= ~ISP1301_LOC_CONN; ++ TRACE_MSG0(TCD, "ISP1301_LOC_CONN RESET - Clr DP PULLUP"); ++ i2c_writeb(ISP1301_OTG_CONTROL_CLR, ISP1301_DP_PULLDOWN); ++ break; ++ } ++} ++ ++/*! isp1301_dm_pulldown_func - used to enable or disable peripheral connecting to bus ++ * ++ */ ++void isp1301_dm_pulldown_func(struct otg_instance *otg, u8 flag) ++{ ++ struct tcd_instance *tcd = (struct tcd_instance *)otg->tcd; ++ //TRACE_MSG0(TCD, "--"); ++ switch (flag) { ++ case SET: ++ isp1301_private.flags |= ISP1301_LOC_CONN; ++ TRACE_MSG0(TCD, "ISP1301_LOC_CONN SET - Set DM PULLUP"); ++ i2c_writeb(ISP1301_OTG_CONTROL_SET, ISP1301_DM_PULLDOWN); ++ break; ++ ++ case RESET: ++ isp1301_private.flags &= ~ISP1301_LOC_CONN; ++ TRACE_MSG0(TCD, "ISP1301_LOC_CONN RESET - Clr DM PULLUP"); ++ i2c_writeb(ISP1301_OTG_CONTROL_CLR, ISP1301_DM_PULLDOWN); ++ break; ++ } ++} ++ ++/*! isp1301_peripheral_host_func - used to enable or disable peripheral connecting to bus ++ * ++ * A-Device D+ pulldown D- pulldown ++ * idle set set ++ * host set set ++ * peripheral reset set ++ * ++ * B-Device ++ * idle set set ++ * host set set ++ * peripheral reset set ++ */ ++void isp1301_peripheral_host_func(struct otg_instance *otg, u8 flag) ++{ ++ struct tcd_instance *tcd = (struct tcd_instance *)otg->tcd; ++ //TRACE_MSG0(TCD, "--"); ++ switch (flag) { ++ case SET: // peripheral ++ TRACE_MSG0(TCD, "SET - CLR DP PULLDOWN"); ++ i2c_writeb(ISP1301_OTG_CONTROL_CLR, ISP1301_DP_PULLDOWN); ++ break; ++ ++ case RESET: // host ++ TRACE_MSG0(TCD, "RESET - SET DM PULLDOWN"); ++ i2c_writeb(ISP1301_OTG_CONTROL_SET, ISP1301_DP_PULLDOWN); ++ break; ++ } ++} ++ ++/*! isp1301_dm_det_func - used to enable or disable D- detect ++ * ++ */ ++void isp1301_dm_det_func(struct otg_instance *otg, u8 flag) ++{ ++ struct tcd_instance *tcd = (struct tcd_instance *)otg->tcd; ++ //TRACE_MSG0(TCD, "--"); ++ switch (flag) { ++ case SET: ++ TRACE_MSG0(TCD, "setting DM_HI detect"); ++ isp1301_int_src_set(ISP1301_DM_HI); ++ isp1301_bh_wakeup(TRUE); ++ isp1301_debounce = 0; ++ break; ++ ++ case RESET: ++ TRACE_MSG0(TCD, "reseting DM_HI detect"); ++ isp1301_int_src_clr(ISP1301_DM_HI); ++ isp1301_bh_wakeup(TRUE); ++ isp1301_debounce = 1; ++ break; ++ } ++} ++ ++/*! isp1301_dp_det_func - used to enable or disable D+ detect ++ * ++ */ ++void isp1301_dp_det_func(struct otg_instance *otg, u8 flag) ++{ ++ struct tcd_instance *tcd = (struct tcd_instance *)otg->tcd; ++ //TRACE_MSG0(TCD, "--"); ++ switch (flag) { ++ case SET: ++ TRACE_MSG0(TCD, "setting DP_HI detect"); ++ isp1301_int_src_set(ISP1301_DP_HI); ++ isp1301_bh_wakeup(TRUE); ++ break; ++ ++ case RESET: ++ TRACE_MSG0(TCD, "reseting DP_HI detect"); ++ isp1301_int_src_clr(ISP1301_DP_HI); ++ isp1301_bh_wakeup(TRUE); ++ break; ++ } ++} ++ ++/*! isp1301_cr_det_func - used to enable or disable D+ detect ++ * ++ */ ++void isp1301_cr_det_func(struct otg_instance *otg, u8 flag) ++{ ++ struct tcd_instance *tcd = (struct tcd_instance *)otg->tcd; ++ //TRACE_MSG0(TCD, "--"); ++ switch (flag) { ++ case SET: ++ TRACE_MSG0(TCD, "setting CR_INT detect"); ++ isp1301_int_src_set(ISP1301_CR_INT); ++ isp1301_bh_wakeup(TRUE); ++ break; ++ ++ case RESET: ++ TRACE_MSG0(TCD, "reseting CR_INT detect"); ++ isp1301_int_src_clr(ISP1301_CR_INT); ++ isp1301_bh_wakeup(TRUE); ++ break; ++ } ++} ++ ++/*! isp1301_bdis_acon_func - used to enable or disable auto a-connect ++ * ++ */ ++void isp1301_bdis_acon_func(struct otg_instance *otg, u8 flag) ++{ ++ struct tcd_instance *tcd = (struct tcd_instance *)otg->tcd; ++ //TRACE_MSG0(TCD, "--"); ++ switch (flag) { ++ case SET: ++ TRACE_MSG0(TCD, "setting BDIS ACON"); ++ isp1301_int_src_set(ISP1301_BDIS_ACON); ++ i2c_writeb(ISP1301_MODE_CONTROL_1_SET, ISP1301_BDIS_ACON_EN); ++ break; ++ ++ case RESET: ++ TRACE_MSG0(TCD, "reseting BDIS ACON"); ++ i2c_writeb(ISP1301_MODE_CONTROL_1_CLR, ISP1301_BDIS_ACON_EN); ++ isp1301_int_src_clr(ISP1301_BDIS_ACON); ++ break; ++ } ++} ++ ++/*! isp1301_id_pulldown_func - used to enable or disable ID pulldown ++ * ++ */ ++void isp1301_id_pulldown_func(struct otg_instance *otg, u8 flag) ++{ ++ struct tcd_instance *tcd = (struct tcd_instance *)otg->tcd; ++ //TRACE_MSG0(TCD, "--"); ++ switch (flag) { ++ case SET: ++ TRACE_MSG0(TCD, "setting ID PULLDOWN"); ++ i2c_writeb(ISP1301_OTG_CONTROL_SET, ISP1301_ID_PULLDOWN); ++ break; ++ ++ case RESET: ++ TRACE_MSG0(TCD, "reseting ID PULLDOWN"); ++ i2c_writeb(ISP1301_OTG_CONTROL_CLR, ISP1301_ID_PULLDOWN); ++ break; ++ } ++} ++ ++/*! isp1301_audio_func - used to enable or disable Carkit Interrupt ++ * ++ */ ++void isp1301_audio_func(struct otg_instance *otg, u8 flag) ++{ ++ struct tcd_instance *tcd = (struct tcd_instance *)otg->tcd; ++ //TRACE_MSG0(TCD, "--"); ++ switch (flag) { ++ case SET: ++ TRACE_MSG0(TCD, "SET AUDIO_EN"); ++ //isp1301_int_src_set(ISP1301_CR_INT); ++ i2c_writeb(ISP1301_MODE_CONTROL_2_SET, ISP1301_AUDIO_EN); ++ break; ++ ++ case RESET: ++ TRACE_MSG0(TCD, "RESET AUDIO_EN"); ++ i2c_writeb(ISP1301_MODE_CONTROL_2_CLR, ISP1301_AUDIO_EN); ++ //isp1301_int_src_clr(ISP1301_CR_INT); ++ break; ++ } ++} ++ ++/*! isp1301_uart_func - used to enable or disable transparent uart mode ++ * ++ */ ++void isp1301_uart_func(struct otg_instance *otg, u8 flag) ++{ ++ struct tcd_instance *tcd = (struct tcd_instance *)otg->tcd; ++ //TRACE_MSG0(TCD, "--"); ++ switch (flag) { ++ case SET: ++ TRACE_MSG0(TCD, "setting UART_EN"); ++ i2c_writeb(ISP1301_MODE_CONTROL_1_SET, ISP1301_UART_EN); ++ /* XXX enable uart */ ++ break; ++ ++ case RESET: ++ TRACE_MSG0(TCD, "reseting UART_EN"); ++ i2c_writeb(ISP1301_MODE_CONTROL_1_CLR, ISP1301_UART_EN); ++ /* XXX disable uart */ ++ break; ++ } ++} ++ ++/*! isp1301_mono_func - used to enable or disable mono audio connection ++ * ++ */ ++void isp1301_mono_func(struct otg_instance *otg, u8 flag) ++{ ++ struct tcd_instance *tcd = (struct tcd_instance *)otg->tcd; ++ //TRACE_MSG0(TCD, "--"); ++ switch (flag) { ++ case SET: ++ TRACE_MSG0(TCD, "setting MONO"); ++ /* XXX enable mono output */ ++ break; ++ ++ case RESET: ++ TRACE_MSG0(TCD, "reseting MONO"); ++ /* XXX disable mono output */ ++ break; ++ } ++} ++ ++ ++/* ********************************************************************************************* */ ++extern int isp1301_procfs_init (void); ++extern void isp1301_procfs_exit (void); ++ ++extern void mx2_isp1301_bh(void *arg); ++ ++/*! isp1301_mod_init ++ */ ++int isp1301_mod_init(WORK_PROC bh_proc) ++//int isp1301_mod_init(void) ++{ ++ /* for interrupt bottom half handler ++ */ ++ //PREPARE_WORK_ITEM(isp1301_private.bh, &isp1301_bh, NULL); ++ ++ UNLESS(bh_proc) ++ bh_proc = &isp1301_bh; ++ ++ PREPARE_WORK_ITEM(isp1301_private.bh, bh_proc, NULL); ++ ++ //else ++ // PREPARE_WORK_ITEM(isp1301_private.bh, &isp1301_bh, NULL); ++ ++ /* setup isp1301, enable interrupts, clear latch ++ * XXX when and how do we do DM_HI and DP_HI, when ID is present? ++ */ ++ ++ isp1301_int_src(ISP1301_ID_FLOAT | ISP1301_DM_HI | ISP1301_ID_GND | ISP1301_DP_HI | ISP1301_SESS_VLD | ISP1301_VBUS_VLD); ++ ++ //isp1301_int_src(ISP1301_INT_SRC); ++ //isp1301_int_src(ISP1301_NO_SE1_INT); ++ //isp1301_int_src_clr(ISP1301_CR_INT); ++ ++ //i2c_writeb(ISP1301_OTG_CONTROL_SET, ISP1301_DM_PULLDOWN | ISP1301_DP_PULLDOWN); ++ ++#ifdef CONFIG_OTG_ISP1301_PROCFS ++ isp1301_procfs_init (); ++#endif /* CONFIG_OTG_ISP1301_PROCFS */ ++ ++ return 0; ++} ++ ++/*! isp1301_mod_exit ++ */ ++void isp1301_mod_exit(void) ++{ ++ i2c_writeb(ISP1301_INTERRUPT_ENABLE_LOW_CLR, 0xff); ++ i2c_writeb(ISP1301_INTERRUPT_ENABLE_HIGH_CLR, 0xff); ++ //i2c_close(); ++#ifdef CONFIG_OTG_ISP1301_PROCFS ++ isp1301_procfs_exit (); ++#endif /* CONFIG_OTG_ISP1301_PROCFS */ ++ ++ while (PENDING_WORK_ITEM(isp1301_private.bh)) { ++ printk(KERN_ERR"%s: waiting for bh\n", __FUNCTION__); ++ schedule_timeout(10 * HZ); ++ } ++ ++} ++ ++/* ********************************************************************************************* */ ++/*! isp1301_configure - configure the ISP1301 for this host ++ * @param tx_mode - the type of connection between the host USB and the ISP1301 ++ * @param spd_ctrl - suspend control method ++ */ ++void isp1301_configure(tx_mode_t tx_mode, spd_ctrl_t spd_ctrl) ++{ ++ u16 vendor = 0, product = 0, revision = 0; ++ u8 mode1, mode2, control; ++ ++ struct otg_transceiver_map *map = isp1301_transceiver_map; ++ ++ ++#if defined(CONFIG_OTG_ISP1301_MX2ADS) || defined(CONFIG_OTG_ISP1301_MX2ADS_MODULE) ++ u32 vp; ++ TRACE_MSG0(TCD, "1. Read Transceiver ID's with long read"); ++ vp = i2c_readl(ISP1301_VENDOR_ID); ++ isp1301_private.vendor = vp & 0xffff; ++ isp1301_private.product = vp >> 16; ++ isp1301_private.revision = i2c_readw(ISP1301_VERSION_ID); ++ mode1 = i2c_readb(ISP1301_MODE_CONTROL_1_SET); ++ mode2 = i2c_readb(ISP1301_MODE_CONTROL_2_SET); ++ ++ TRACE_MSG5(TCD, "2. OTG Transceiver: vendor: %04x product: %04x revision: %04x mode1: %02x mode2: %02x", ++ vendor, product, revision, mode1, mode2); ++ ++#else /* defined(CONFIG_OTG_ISP1301_MX2ADS) */ ++ ++ isp1301_private.vendor = i2c_readw(ISP1301_VENDOR_ID); ++ isp1301_private.product = i2c_readw(ISP1301_PRODUCT_ID); ++ isp1301_private.revision = i2c_readw(ISP1301_VERSION_ID); ++ mode1 = i2c_readb(ISP1301_MODE_CONTROL_1_SET); ++ mode2 = i2c_readb(ISP1301_MODE_CONTROL_2_SET); ++ ++ TRACE_MSG5(TCD, "2. OTG Transceiver: vendor: %04x product: %04x revision: %04x mode1: %02x mode2: %02x", ++ isp1301_private.vendor, isp1301_private.product, isp1301_private.revision, mode1, mode2); ++ ++#endif /* defined(CONFIG_OTG_ISP1301_MX2ADS) */ ++ ++ for ( ; map && map->transceiver_type != unknown_transceiver; map++) { ++ CONTINUE_UNLESS ((isp1301_private.vendor == map->vendor) && (isp1301_private.product == map->product)); ++ TRACE_MSG1(TCD, "Found Transceiver: %s", map->name); ++ isp1301_private.transceiver_map = map; ++ break; ++ } ++ UNLESS (isp1301_private.transceiver_map) ++ isp1301_private.transceiver_map = isp1301_transceiver_map; ++ ++ TRACE_MSG0(TCD, "3. Disable All Transceiver Control Register 1 "); ++ i2c_writeb(ISP1301_OTG_CONTROL_CLR, 0xff); // clear ++ ++ TRACE_MSG0(TCD, "4. Enable D-/D+ Pulldowns in Transceiver Control Register 1 "); ++ ++ i2c_writeb(ISP1301_OTG_CONTROL_SET, ISP1301_DM_PULLDOWN | ISP1301_DP_PULLDOWN); ++ i2c_writeb(ISP1301_OTG_CONTROL_CLR, (u8) ISP1301_DM_PULLUP | ISP1301_DP_PULLUP); ++ ++ ++ TRACE_MSG0(TCD, "5. Clear latch and enable interrupts"); ++ i2c_writeb(ISP1301_INTERRUPT_LATCH_CLR, 0xff); ++ //i2c_writeb(ISP1301_INTERRUPT_ENABLE_LOW_CLR, 0xeb); ++ //i2c_writeb(ISP1301_INTERRUPT_ENABLE_HIGH_CLR, 0xeb); ++ isp1301_int_src_set(0xeb); ++ ++ /* The PSW_OE enables the ADR/PSW pin for output driving either high ++ * or low depending on the address. ++ * ++ * ADR ADR_REG Address PSW_OE=0 PSW_OE=1 ++ * ++ * low 0 0x2c LOW HIGH ++ * ++ * high 1 0x2d HIGH LOW ++ * ++ * ++ * The i2c address by tying the ADR/PSW pin either high or low. ++ * Setting the PSW_OE drives the ADR/PSW into the opposite of the ++ * default wiring. ++ * ++ * On the MX21ADS the ADR/PSW pin is wired high which enables the ++ * charge pump. So enabling the PSW_OE is required to disable Vbus ++ * generation on the Charge Pump. ++ * ++ * The MAX3355E will only enable Vbus when this signal is high AND ++ * the ID signal is low. So it may be safe to leave enabled when ++ * operating as a B-device (ID floating.) ++ */ ++ ++ //i2c_writeb(ISP1301_MODE_CONTROL_2_SET, ISP1301_PSW_OE); // PSW_OE - OFFVBUS low ++ ++ ++ TRACE_MSG1(TCD, "6. tx_mode: %02x", tx_mode); ++ switch (tx_mode) { ++ case vp_vm_bidirectional: ++ break; ++ case dat_se0_unidirectional: ++ i2c_writeb(ISP1301_MODE_CONTROL_2_CLR, ISP1301_BI_DI); ++ case dat_se0_bidirectional: ++ i2c_writeb(ISP1301_MODE_CONTROL_1_SET, ISP1301_DAT_SE0); ++ break; ++ } ++ TRACE_MSG0(TCD, "6. tx_mode done"); ++ ++ TRACE_MSG1(TCD, "7. spd_ctrl: %02x", spd_ctrl); ++ switch (spd_ctrl) { ++ case spd_susp_pins: ++ i2c_writeb(ISP1301_MODE_CONTROL_2_CLR, ISP1301_SPD_SUSP_CTRL); ++ break; ++ case spd_susp_reg: ++ i2c_writeb(ISP1301_MODE_CONTROL_2_SET, ISP1301_SPD_SUSP_CTRL); ++ break; ++ } ++ ++ //i2c_writeb(ISP1301_MODE_CONTROL_2_SET, ISP1301_PSW_OE); // PSW_OE - OFFVBUS low ++ ++ TRACE_MSG0(TCD, "7. spd_ctrl done"); ++ ++ TRACE_MSG1(TCD, "8. transceiver_type: %02x", isp1301_private.transceiver_map); ++ ++ TRACE_MSG1(TCD, "8. transceiver_type: %02x", isp1301_private.transceiver_map->transceiver_type); ++ switch (isp1301_private.transceiver_map->transceiver_type) { ++ case isp1301: ++ break; ++ case max3301e: ++ i2c_writeb(MAX3301E_SPECIAL_FUNCTION_1_SET, MAX3301E_SESS_END); ++ break; ++ case unknown_transceiver: ++ break; ++ } ++ TRACE_MSG0(TCD, "8. transceiver_type done"); ++} ++ +diff -uNr linux/drivers/no-otg/ocd/isp1301/tcd-mainstone.c linux/drivers/otg/ocd/isp1301/tcd-mainstone.c +--- linux/drivers/no-otg/ocd/isp1301/tcd-mainstone.c 1970-01-01 01:00:00.000000000 +0100 ++++ linux/drivers/otg/ocd/isp1301/tcd-mainstone.c 2006-09-01 21:41:31.000000000 +0200 +@@ -0,0 +1,177 @@ ++/* ++ * otg/ocd/mx2/tcd-mainstone.c -- Mainstone ISP1301 Transceiver Controller driver ++ * ++ * Copyright (c) 2004 Belcarra ++ * ++ * By: ++ * Stuart Lynne <sl@lbelcarra.com>, ++ * Tom Rushworth <tbr@belcarra.com>, ++ * Bruce Balden <balden@belcarra.com> ++ * ++ */ ++/*! ++ * @file otg/ocd/isp1301/tcd-mainstone.c ++ * @brief Mainstone ISP1301 driver. ++ * ++ * This implements the ISP1301 Transceiver Controller Driver for the Mainstone ++ * using the USBOTG I2C controller for access. ++ * ++ * Notes ++ * ++ * 1. The interrupt source is handled by the OCD driver so we just publish ++ * an interrupt handler for it to call. ++ * ++ * ++ * @ingroup ISP1301TCD ++ */ ++ ++#include <otg/pcd-include.h> ++#include <linux/pci.h> ++#include <asm/arch/irq.h> ++#include <asm/arch/irqs.h> ++#include <otghw/isp1301-hardware.h> ++#include "mx2.h" ++#include "otghw/bvd-hardware.h" ++#include <otghw/isp1301.h> ++ ++/* ********************************************************************************************* */ ++/* ********************************************************************************************* */ ++ ++ ++/* ********************************************************************************************* */ ++ ++ ++/*! isp1301_int_hndlr ++ */ ++static irqreturn_t isp1301_int_hndlr (int irq, void *dev_id, struct pt_regs *regs) ++{ ++ TRACE_MSG0(TCD, "--"); ++ ++ SCHEDULE_WORK(isp1301_private.bh); ++ ++ return IRQ_HANDLED; ++} ++ ++ ++/* ********************************************************************************************* */ ++ ++/*! mainstone_tcd_init - used to initialize/enable or disable the tcd driver ++ */ ++void mainstone_tcd_init(struct otg_instance *otg, u8 flag) ++{ ++ TRACE_MSG0(TCD, "--"); ++ switch (flag) { ++ case SET: ++ TRACE_MSG0(TCD, "MAINSTONE_TCD_EN SET"); ++ otg_event(otg, TCD_OK, TCD, "TCD_OK"); ++ isp1301_thread_wakeup(1, 1); ++ break; ++ case RESET: ++ TRACE_MSG0(TCD, "MAINSTONE_TCD_EN RESET"); ++ isp1301_thread_wakeup(0, 0); ++ otg_event(otg, TCD_OK, TCD, "TCD_OK"); ++ break; ++ } ++} ++ ++ ++struct tcd_ops tcd_ops; ++struct tcd_instance *mainstone_tcd_instance; ++//wait_queue_head_t mx2_i2c_wait; // wait structure for i2c i/o interrupt ++ ++#define ADAPTOR_NAME "PXA-I2C-Adapter" ++ ++/*! mainstone_mod_init ++ */ ++int mainstone_mod_init(void) ++{ ++ struct otg_instance *otg; ++ int i2c = i2c_configure(ADAPTOR_NAME, ISP1301_I2C_ADDR_HIGH); ++ int irq = request_irq (GPIOX_TO_IRQ(35), isp1301_int_hndlr, SA_INTERRUPT, "ISP1301", NULL); ++ ++ TRACE_MSG0(TCD, "--"); ++ ++ THROW_IF(i2c, error); ++ ++ TRACE_MSG0(TCD, "1. Enable Mainstone external OTG Transceiver!"); ++ ++ TRACE_MSG1(TCD, "MST_MSCWR2: %08x", MST_MSCWR2); ++ MST_MSCWR2 |= MSCWR2_USB_OTG_SEL | MSCWR2_USB_OTG_RST; ++ TRACE_MSG1(TCD, "MST_MSCWR2: %08x", MST_MSCWR2); ++ ++ ++ TRACE_MSG0(TCD, "2. configure i2c driver!"); ++ isp1301_configure(dat_se0_bidirectional, spd_susp_reg); ++ ++ TRACE_MSG0(TCD, "3. configure isp1301!"); ++ isp1301_mod_init(); ++ ++ TRACE_MSG0(TCD, "4. set ops"); ++ THROW_UNLESS(ocd_instance && (otg = ocd_instance->otg), error); ++ THROW_UNLESS(mainstone_tcd_instance = otg_set_tcd_ops(&tcd_ops), error); ++ ++ /* Success! ++ */ ++ TRACE_MSG0(TCD, "5. Success!"); ++ ++ otg_init(otg); ++ //otg_start(otg, ocd_ops.capabilities & OCD_CAPABILITIES_AUTO); ++ ++ // XXX TESTING ++ //isp1301_dp_pullup_func(otg, 1); ++ ++ /* XXX start thread to monitor for changes - not needed once gpio interrupt implmented ++ */ ++ isp1301_thread_init(isp1301_bh); ++ ++ /* error handling ++ */ ++ CATCH(error) { ++ //if (irq) free_irq(GPIO_2_80_TO_IRQ(35), NULL); ++ if (irq) free_irq(GPIOX_TO_IRQ(35), NULL); ++ if (i2c) i2c_close(); ++ } ++ return 0; ++} ++ ++/*! mainstone_mod_exit ++ */ ++void mainstone_mod_exit(void) ++{ ++ //free_irq(GPIO_2_80_TO_IRQ(35), NULL); ++ free_irq(GPIOX_TO_IRQ(35), NULL); ++ ++ /* XXX stop thread - not needed once gpio interrupt implmented ++ */ ++ isp1301_thread_exit(NULL); ++ ++ isp1301_mod_exit(); ++ TRACE_MSG1(TCD, "MST_MSCWR2: %08x", MST_MSCWR2); ++ MST_MSCWR2 &= ~(MSCWR2_USB_OTG_SEL | MSCWR2_USB_OTG_RST); ++ TRACE_MSG1(TCD, "MST_MSCWR2: %08x", MST_MSCWR2); ++ //isp1301_thread_exit(); ++ i2c_close(); ++ ++} ++ ++/* ********************************************************************************************* */ ++ ++struct tcd_ops tcd_ops = { ++ ++ vbus: isp1301_vbus, ++ ++ mod_init: mainstone_mod_init, ++ mod_exit: mainstone_mod_exit, ++ ++ tcd_init_func: mainstone_tcd_init, ++ ++ dischrg_vbus_func: isp1301_dischrg_vbus, ++ chrg_vbus_func: isp1301_chrg_vbus, ++ drv_vbus_func: isp1301_drv_vbus, ++ dp_pullup_func: isp1301_dp_pullup_func, ++ dm_pullup_func: isp1301_dm_pullup_func, ++ dm_det_func: isp1301_dm_det_func, ++ dp_det_func: isp1301_dp_det_func, ++ ++}; ++ +diff -uNr linux/drivers/no-otg/ocd/isp1301/tcd-omap-h2.c linux/drivers/otg/ocd/isp1301/tcd-omap-h2.c +--- linux/drivers/no-otg/ocd/isp1301/tcd-omap-h2.c 1970-01-01 01:00:00.000000000 +0100 ++++ linux/drivers/otg/ocd/isp1301/tcd-omap-h2.c 2006-09-01 21:41:31.000000000 +0200 +@@ -0,0 +1,180 @@ ++/* ++ * otg/ocd/isp1301/tcd-omap-h2.c -- USB Transceiver Controller driver ++ * ++ * Copyright (c) 2004 Belcarra ++ * ++ * By: ++ * Stuart Lynne <sl@lbelcarra.com>, ++ * Tom Rushworth <tbr@belcarra.com>, ++ * Bruce Balden <balden@belcarra.com> ++ * ++ */ ++/*! ++ * @file otg/ocd/isp1301/tcd-omap-h2.c ++ * @brief OMAP H2 ISP1301 driver. ++ * ++ * ++ * @ingroup ISP1301TCD ++ */ ++ ++#include <otg/pcd-include.h> ++#include <linux/pci.h> ++#include <asm/arch/irq.h> ++#include <asm/arch/irqs.h> ++#include <otghw/isp1301-hardware.h> ++#include "otghw/bvd-hardware.h" ++#include <otghw/isp1301.h> ++ ++#include <asm/irq.h> ++#include <asm/system.h> ++#include <asm/io.h> ++#include <linux/i2c.h> ++ ++#include <asm/arch/gpio.h> ++#include <asm/arch/mux.h> ++//struct isp1301_private isp1301_private; ++ ++/* ********************************************************************************************* */ ++ ++ ++static irqreturn_t isp1301_int_hndlr (int irq, void *dev_id, struct pt_regs *regs) ++{ ++ TRACE_MSG0(TCD, "--"); ++ ++ SCHEDULE_WORK(isp1301_private.bh); ++ ++ return IRQ_HANDLED; ++} ++ ++ ++/* ********************************************************************************************* */ ++ ++struct tcd_ops tcd_ops; ++ ++#define ADAPTOR_NAME "OMAP I2C" ++ ++/*! omap_mod_init ++ */ ++int omap_mod_init(void) ++{ ++ int i2c = i2c_configure(ADAPTOR_NAME, ISP1301_I2C_ADDR_HIGH); ++ ++ int gpio = omap_request_gpio (ISP1301_GPIO); ++ int irq = 0; ++ ++ TRACE_MSG0(TCD, "--"); ++ ++ /* did we get gpio and i2c? ++ */ ++ THROW_IF(gpio || i2c, error); ++ ++ /* for interrupt bottom half handler ++ */ ++ // moved isp1301.c - PREPARE_WORK_ITEM(isp1301_private.bh, &isp1301_bh, NULL); ++ ++ /* ++ * See H2 C.f. Figure 17 USBOTG Interface Connection ++ * GPIO2 M14 ++ * setup gpio 2 and request interrupt for it ++ */ ++ omap_set_gpio_direction(ISP1301_GPIO, 1); ++ //omap_cfg_reg(M14_1510_GPIO2); ++ omap_set_gpio_edge_ctrl(ISP1301_GPIO, OMAP_GPIO_FALLING_EDGE); ++ irq = request_irq (OMAP_GPIO_IRQ(ISP1301_GPIO), isp1301_int_hndlr, SA_INTERRUPT, "ISP1301", NULL); ++ THROW_IF(irq, error); ++ ++ #ifdef CONFIG_OTG_OMAP_H2_3_WIRE ++ ++ /* OTG on Pin Group 1 Using 3-Wire OTG Transceiver C.f. Figure 15-59 ++ * See also H2 C.f. Figure 17 USBOTG Interface Connection ++ * DAT_SE0 mode ++ * Set DAT_SE0, SUSPEND_REG and BI_DI ++ */ ++ //i2c_writeb(ISP1301_MODE_CONTROL_1_SET, ISP1301_DAT_SE0 | ISP1301_SUSPEND_REG); ++ //i2c_writeb(ISP1301_MODE_CONTROL_1_CLR, ~(ISP1301_DAT_SE0 | ISP1301_SUSPEND_REG)); ++ //i2c_writeb(ISP1301_MODE_CONTROL_2_SET, ISP1301_BI_DI); ++ //i2c_writeb(ISP1301_MODE_CONTROL_2_CLR, ~ISP1301_BI_DI); ++ ++ isp1301_configure(dat_se0_bidirectional, spd_susp_pins); ++ ++ #elif CONFIG_OTG_OMAP_H2_4_WIRE ++ ++ /* OTG on Pin Group 1 Using 4-Wire OTG Transceiver C.f. Figure 15-60 ++ * See also H2 C.f. Figure 17 USBOTG Interface Connection ++ * VP_VM mode ++ * Reset DAT_SE0 ++ * Set SUSPEND_REG and BI_DI ++ */ ++ //i2c_writeb(ISP1301_MODE_CONTROL_1_SET, ISP1301_SUSPEND_REG); ++ //i2c_writeb(ISP1301_MODE_CONTROL_1_CLR, ~ISP1301_SUSPEND_REG); ++ //i2c_writeb(ISP1301_MODE_CONTROL_2_SET, ISP1301_BI_DI); ++ //i2c_writeb(ISP1301_MODE_CONTROL_2_CLR, ~ISP1301_BI_DI); ++ ++ isp1301_configure(vp_vm_bidirectional); ++ ++ #else ++ #error "ISP1301 Connection style not selected!" ++ #endif /* CONFIG_OTG_OMAP_H2_4_WIRE */ ++ ++ isp1301_mod_init(); ++ ++ i2c_writeb(ISP1301_OTG_CONTROL_SET, ISP1301_DM_PULLDOWN | ISP1301_DP_PULLDOWN); ++ ++ /* Success! ++ */ ++ ++ TRACE_MSG0(TCD, "5. Success!"); ++ ++ otg_init(otg); ++ ++ /* error handling ++ */ ++ CATCH(error) { ++ if (irq) free_irq(OMAP_GPIO_IRQ(ISP1301_GPIO), NULL); ++ if (gpio) omap_free_gpio(ISP1301_GPIO); ++ if (i2c) i2c_close(); ++ } ++ return 0; ++} ++ ++/*! omap_mod_exit ++ */ ++void omap_mod_exit(void) ++{ ++ #if 0 ++ // moved to isp1301.c ++ while (PENDING_WORK_ITEM(isp1301_private.bh)) { ++ printk(KERN_ERR"%s: waiting for bh\n", __FUNCTION__); ++ schedule_timeout(10 * HZ); ++ } ++ #endif ++ ++ isp1301_mod_exit(); ++ ++ i2c_writeb(ISP1301_INTERRUPT_ENABLE_LOW_CLR, 0xff); ++ i2c_writeb(ISP1301_INTERRUPT_ENABLE_HIGH_CLR, 0xff); ++ i2c_close(); ++ free_irq(OMAP_GPIO_IRQ(ISP1301_GPIO), NULL); ++ omap_free_gpio(ISP1301_GPIO); ++} ++ ++/* ********************************************************************************************* */ ++ ++struct tcd_ops tcd_ops = { ++ ++ //initial_state: tr_init, ++ vbus: isp1301_vbus, ++ ++ mod_init: omap_mod_init, ++ mod_exit: omap_mod_exit, ++ ++ dischrg_vbus_func: isp1301_dischrg_vbus, ++ chrg_vbus_func: isp1301_chrg_vbus, ++ drv_vbus_func: isp1301_drv_vbus, ++ dp_pullup_func: isp1301_dp_pullup_func, ++ dm_pullup_func: isp1301_dm_pullup_func, ++ dm_det_func: isp1301_dm_det_func, ++ dp_det_func: isp1301_dp_det_func, ++ ++}; ++ +diff -uNr linux/drivers/no-otg/ocd/isp1301/thread-l24.c linux/drivers/otg/ocd/isp1301/thread-l24.c +--- linux/drivers/no-otg/ocd/isp1301/thread-l24.c 1970-01-01 01:00:00.000000000 +0100 ++++ linux/drivers/otg/ocd/isp1301/thread-l24.c 2006-09-01 21:41:31.000000000 +0200 +@@ -0,0 +1,144 @@ ++/* ++ * otg/ocd/isp1301/thread-l24.c -- MX21ADS ISP1301 Transceiver Controller driver ++ * ++ * Copyright (c) 2004 Belcarra ++ * ++ * By: ++ * Stuart Lynne <sl@lbelcarra.com>, ++ * Tom Rushworth <tbr@belcarra.com>, ++ * Bruce Balden <balden@belcarra.com> ++ * ++ */ ++/*! ++ * @file otg/ocd/isp1301/thread-l24.c ++ * @brief Implement a kernel thread for checking ISP1301. ++ * ++ * ++ * @ingroup ISP1301TCD ++ */ ++ ++#include <otg/pcd-include.h> ++#include <otg/otg-module.h> ++#include <linux/pci.h> ++//#include <asm/arch/mx2.h> ++#include <otghw/isp1301-hardware.h> ++//#include "mx2.h" ++//#include "otghw/mx2-hardware.h" ++ ++ ++/* isp1301_bh ++ */ ++extern void isp1301_bh(void *arg); ++ ++/* ********************************************************************************************* */ ++/* ********************************************************************************************* */ ++ ++/* I2C Thread ++ * The mx21 USBOTG module does not seem to be able to keep uptodate with the OTG Transceiver ++ * registers. This kernel thread will iterated endlessly reading the appropriate registers. ++ * ++ * If this is successful we can assume that the USBOTG module will correctly monitor the appropriate ++ * registers and allow us to follow them via the normal USBOTG interrupts and registers. ++ * ++ * Otherwise we can monitor here and issue the otg events directly. ++ */ ++//#define ISP1301_THREAD_TIMEOUT_EN (10) ++#define ISP1301_THREAD_TIMEOUT_EN (1*HZ) ++#define ISP1301_THREAD_TIMEOUT_DIS (1*HZ) ++ ++int isp1301_terminate; ++int mx2_pid; ++wait_queue_head_t isp1301_wait; ++static DECLARE_COMPLETION(mx2_exited); ++int isp1301_en; ++ ++void (*isp1301_bh_proc)(void *); ++ ++/*! isp1301_thread - implement a kernel task to monitor isp1301 for changes ++ */ ++int isp1301_thread(void *data) ++{ ++ u8 int_src_saved = 0, otg_status_saved = 0, int_src, otg_status; ++ TRACE_MSG0(TCD, "--"); ++ daemonize(); ++ reparent_to_init(); ++ sprintf(current->comm, "isp1301_thread"); ++ ++ /* loop until told to terminate ++ */ ++ while (!isp1301_terminate) { ++ ++ #if 0 ++ // XXX TESTING ++ static int count = 0; ++ count++; ++ if (!(count % 5)) ++ isp1301_dp_pullup_func(tcd_instance->otg, !(count % 10)? 1 : 2); ++ #endif ++ ++ /* sleep for a while if not enabled ++ */ ++ if (!isp1301_en) { ++ TRACE_MSG1(TCD, "WAITING: %d", ISP1301_THREAD_TIMEOUT_DIS); ++ interruptible_sleep_on_timeout(&isp1301_wait, ISP1301_THREAD_TIMEOUT_DIS); ++ continue; ++ } ++ ++ /* update 1301 ++ */ ++ if (isp1301_bh_proc) ++ isp1301_bh_proc(NULL); ++ ++ //TRACE_MSG1(TCD, "SLEEPING: %d", ISP1301_THREAD_TIMEOUT_EN); ++ interruptible_sleep_on_timeout(&isp1301_wait, ISP1301_THREAD_TIMEOUT_EN); ++ //TRACE_MSG0(TCD, "RUNNING"); ++ } ++ TRACE_MSG0(TCD, "Exiting"); ++ complete_and_exit(&mx2_exited, 0); ++} ++ ++extern int isp1301_bh_first; ++/*! isp1301_thread_wakeup - wakeup the isp1301 task ++ */ ++void isp1301_thread_wakeup(int enabled, int first) ++{ ++ TRACE_MSG0(TCD, "--"); ++ isp1301_bh_first |= first; ++ isp1301_en = enabled; ++ wake_up(&isp1301_wait); ++} ++ ++/* ********************************************************************************************* */ ++ ++/*! isp1301_thread_init - initial tcd setup ++ * Allocate interrupts and setup hardware. ++ */ ++int isp1301_thread_init (void (bh_proc)(void *)) ++{ ++ isp1301_terminate = 0; ++ TRACE_MSG0(TCD, "--"); ++ isp1301_bh_proc = bh_proc; ++ init_waitqueue_head(&isp1301_wait); ++ THROW_IF((mx2_pid = kernel_thread(&isp1301_thread, NULL, CLONE_FS | CLONE_FILES | CLONE_SIGHAND)) < 0, error); ++ isp1301_thread_wakeup(0, 0); ++ ++ CATCH(error) { ++ return -EINVAL; ++ } ++ return 0; ++} ++ ++/*! isp1301_thread_exit - de-initialize ++ */ ++void isp1301_thread_exit (wait_queue_head_t *wait_queue) ++{ ++ TRACE_MSG0(TCD, "MX2_MOD_TCD_EXIT - terminating"); ++ isp1301_bh_proc = NULL; ++ isp1301_terminate = 1; ++ if (wait_queue) ++ wake_up_interruptible(wait_queue); // wakeup waiting bottom half handler ++ isp1301_thread_wakeup(0, 0); ++ wait_for_completion(&mx2_exited); ++ TRACE_MSG0(TCD, "MX2_MOD_TCD_EXIT - terminated"); ++} ++ +diff -uNr linux/drivers/no-otg/ocd/max3353e/Config.in linux/drivers/otg/ocd/max3353e/Config.in +--- linux/drivers/no-otg/ocd/max3353e/Config.in 1970-01-01 01:00:00.000000000 +0100 ++++ linux/drivers/otg/ocd/max3353e/Config.in 2006-09-01 21:41:32.000000000 +0200 +@@ -0,0 +1,19 @@ ++ ++# ++# @(#) balden@belcarra.com|otg/ocd/max3353e/Config.in|20060831021117|26677 ++# Copyright (c) 2004 Belcarra ++# Copyright (c) 2005-2006 Belcarra Technologies 2005 Corp ++# ++# MAX 3353E TCD ++ ++if [ "$CONFIG_OTG_MAX3353E" = "y" ]; then ++ mainmenu_option next_comment ++ ++ comment 'MAX 3353E (i2c)' ++ ++ dep_bool 'Proc FS debug' CONFIG_OTG_MAX3353E_PROCFS $CONFIG_OTG_MAX3353E ++ ++ endmenu ++else ++ define_bool CONFIG_OTG_MAX3353E_PROCFS n ++fi +diff -uNr linux/drivers/no-otg/ocd/max3353e/Makefile linux/drivers/otg/ocd/max3353e/Makefile +--- linux/drivers/no-otg/ocd/max3353e/Makefile 1970-01-01 01:00:00.000000000 +0100 ++++ linux/drivers/otg/ocd/max3353e/Makefile 2006-09-01 21:41:32.000000000 +0200 +@@ -0,0 +1,100 @@ ++# ++# Makefile for the kernel USBD (device not host) drivers. ++# @(#) balden@belcarra.com|otg/ocd/max3353e/Makefile-l24|20060831021117|12633 ++# ++# Copyright (c) 2004 Belcarra ++# Copyright (c) 2005-2006 Belcarra Technologies 2005 Corp ++ ++# 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. ++ ++O_TARGET := max3353e_target.o ++list-multi := max3353e_tcd.o ++ ++# Objects that export symbols. ++ ++export-objs := i2c-max3353e.o max3353e.o ++ ++# Multipart objects. ++ ++#db1550_tcd-objs := tcd-init-l24.o tcd-omap-h2.o max3353e.o i2c-l26.o tcd.o max3353e-procfs.o ++#db1550_tcd-objs := tcd-init-l24.o tcd-db1550.o tcd-mx2ads.o max3353e.o i2c-l26.o tcd.o max3353e-procfs.o ++db1550_tcd-objs := tcd-init-l24.o max3353e.o tcd-db1550.o i2c-max3353e.o ++# Optional parts of multipart objects. ++ ++# Object file lists. ++ ++obj-y := ++obj-m := ++obj-n := ++obj- := ++ ++# Each configuration option enables a list of files. ++ ++obj-$(CONFIG_OTG_MAX3353E_DB1550) += db1550_tcd.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. ++ ++DOT_DIR=$(TCD_DIR)/max3353e ++ ++include $(TOPDIR)/Rules.make ++OTG=$(TOPDIR)/drivers/otg ++OCD_DIR=$(OTG)ocd/ ++OTGLIB=$(OTG)/ocd/otglib ++INCLUDE_DIRS = -I$(OTG) ++EXTRA_CFLAGS += -Wno-missing-prototypes -Wno-unused -Wno-format ${INCLUDE_DIRS} ++EXTRA_CFLAGS_nostdinc += -Wno-missing-prototypes -Wno-unused -Wno-format ${INCLUDE_DIRS} ++ ++I2C=$(OTG)/ocd/otg-i2c ++PCD=$(OTG)/ocd/otg-pcd ++TCD=$(OTG)/ocd/otg-tcd ++vpath %.c $(USBDCORE_DIR) $(OCD_DIR) $(PCD) $(TCD) $(OTGLIB) ++ ++ ++# Link rules for multi-part drivers. ++ ++db1550_tcd.o: $(db1550_tcd-objs) ++ $(LD) -r -o $@ $(db1550_tcd-objs) ++ ++# dependencies: ++ ++# local ++ ++ +diff -uNr linux/drivers/no-otg/ocd/max3353e/Makefile-l26 linux/drivers/otg/ocd/max3353e/Makefile-l26 +--- linux/drivers/no-otg/ocd/max3353e/Makefile-l26 1970-01-01 01:00:00.000000000 +0100 ++++ linux/drivers/otg/ocd/max3353e/Makefile-l26 2006-09-01 21:41:32.000000000 +0200 +@@ -0,0 +1,28 @@ ++# ++# Makefile for the kernel USBD (device not host) drivers. ++# @(#) balden@belcarra.com|otg/ocd/max3353e/Makefile-l26|20060831021117|56045 ++# ++# Copyright (c) 2004 Belcarra Technologies Corp ++# Copyright (c) 2005-2006 Belcarra Technologies 2005 Corp ++ ++DOT_DIR=$(TCD_DIR)/max3353e ++ ++OTG=$(TOPDIR)/drivers/otg ++HCD_DIR=$(OTG)/hcd ++TCD_DIR=$(OTG)/tcd ++USBDCORE_DIR=$(OTG)/usbdcore ++OTGCORE_DIR=$(OTG)/otgcore ++INCLUDE_DIRS = -I$(OTG) ++EXTRA_CFLAGS += -Wno-missing-prototypes -Wno-unused -Wno-format ${INCLUDE_DIRS} ++EXTRA_CFLAGS_nostdinc += -Wno-missing-prototypes -Wno-unused -Wno-format ${INCLUDE_DIRS} ++ ++vpath %.c $(USBDCORE_DIR) $(OCD_DIR) ++ ++ ++# Link rules for multi-part drivers. ++ ++db1550_tcd-objs := ../tcd-init-l24.o max3353e.o i2c-l26.o ../tcd.o ++obj-$(CONFIG_OTG_MAX3353E_DB1550) += db1550_tcd.o ++ ++ ++ +diff -uNr linux/drivers/no-otg/ocd/max3353e/README linux/drivers/otg/ocd/max3353e/README +--- linux/drivers/no-otg/ocd/max3353e/README 1970-01-01 01:00:00.000000000 +0100 ++++ linux/drivers/otg/ocd/max3353e/README 2006-09-01 21:41:32.000000000 +0200 +@@ -0,0 +1,9 @@ ++Some notes for using max3353 tranciever in DB1550 development board ++1- Patch kernel with AMD DB1550 i2c driver. ++2- Patch arch/mips/au1000/db1x00/irqmap.c ++#if defined(CONFIG_MIPS_DB1550) || defined(CONFIG_MIPS_MTX2) ++ { AU1000_GPIO_3, INTC_INT_LOW_LEVEL, 0 }, // PCMCIA Card 0 IRQ# ++ { AU1000_GPIO_5, INTC_INT_LOW_LEVEL, 0 }, // PCMCIA Card 1 IRQ# ++ { AU1500_GPIO_207, INTC_INT_FALL_EDGE, 0 }, // OTG tranciever ++#else ++ +diff -uNr linux/drivers/no-otg/ocd/max3353e/i2c-max3353e.c linux/drivers/otg/ocd/max3353e/i2c-max3353e.c +--- linux/drivers/no-otg/ocd/max3353e/i2c-max3353e.c 1970-01-01 01:00:00.000000000 +0100 ++++ linux/drivers/otg/ocd/max3353e/i2c-max3353e.c 2006-09-01 21:41:32.000000000 +0200 +@@ -0,0 +1,178 @@ ++/* @(#) balden@belcarra.com|otg/ocd/max3353e/i2c-max3353e.c|20060831021117|42179 ++ * Copyright (c) 2005-2006 Belcarra Technologies 2005 Corp ++ */ ++#include <otg/otg-compat.h> ++ ++#include <asm/irq.h> ++#include <asm/system.h> ++#include <asm/io.h> ++#include <linux/i2c.h> ++ ++#include <otg/pcd-include.h> ++#include <linux/pci.h> ++#include <otghw/max3353e-hardware.h> ++//#include "isp1301.h" ++ ++/* ++ * N.B. i2c functions must not be called from interrupt handlers ++ */ ++ ++static struct file *i2c_file; ++static struct i2c_client *db1550_i2c_client; ++static int initstate_i2c; ++static int initstate_region; ++#define MAX_I2C 16 ++ ++#ifdef CONFIG_OMAP_H2 ++#define ADAPTOR_NAME "OMAP I2C" ++#endif /* CONFIG_OMAP_H2 */ ++#ifdef CONFIG_ARCH_MAINSTONE ++#define ADAPTOR_NAME "PXA-I2C-Adapter" ++#endif /* CONFIG_ARCH_MAINSTONE */ ++#ifdef CONFIG_OTG_DB1550_J15 ++#define ADAPTOR_NAME "pb1550 adapter" ++#endif /* CONFIG_OTG_DB1550_J15 */ ++ ++void i2c_writeb(u8 subaddr, u8 buf) ++{ ++ char tmpbuf[2]; ++ int ret; ++ ++ tmpbuf[0] = subaddr; /*register number */ ++ tmpbuf[1] = buf; /*register data */ ++ ++ ret = i2c_master_send(db1550_i2c_client, &tmpbuf[0], 2); ++ while (ret != 2){ ++ ret = i2c_master_send(db1550_i2c_client, &tmpbuf[0], 2); ++ } ++ TRACE_MSG1(TCD, "I2C write %d\n", ret); ++ ++} ++ ++void i2c_close() ++{ ++ if (initstate_i2c) ++ filp_close(i2c_file, NULL); ++ initstate_i2c = 0; ++ ++} ++ ++int i2c_configure (char *name, int addr) ++{ ++ char filename[20]; ++ int tmp; ++ ++ RETURN_ZERO_IF(initstate_i2c); ++ ++ /*find the I2C driver we need ++ */ ++ for (tmp = 0; tmp < MAX_I2C; tmp++) { ++ ++ sprintf(filename, "/dev/i2c-%d", tmp); ++ ++ printk(KERN_INFO"%s: %s\n", __FUNCTION__, filename); ++ ++ UNLESS (IS_ERR(i2c_file = filp_open(filename, O_RDWR, 0))) { ++ ++ //printk(KERN_INFO"%s: %s found\n", __FUNCTION__, filename); ++ ++ /*found some driver */ ++ db1550_i2c_client = (struct i2c_client *)i2c_file->private_data; ++ ++ printk(KERN_INFO"%s: found %s\n", __FUNCTION__, db1550_i2c_client->adapter->name); ++ if (strlen(db1550_i2c_client->adapter->name) >= 8) { ++ if (!strncmp(db1550_i2c_client->adapter->name, name, strlen(name))) ++ break; /*we found our driver! */ ++ } ++ db1550_i2c_client = NULL; ++ filp_close(i2c_file, NULL); ++ } ++ printk(KERN_INFO"%s: %s %d\n", __FUNCTION__, filename, i2c_file); ++ } ++ if (tmp == MAX_I2C) { // Nothing found ++ printk(KERN_ERR"%s: cannot find I2C driver", __FUNCTION__); ++ return -ENODEV; ++ } ++ db1550_i2c_client->addr = addr; ++ initstate_i2c = 1; ++ return 0; ++} ++ ++/*! i2c_readb ++ * Read byte from i2c device ++ * @param subaddr ++ */ ++u8 i2c_readb(u8 subaddr) ++{ ++int ret; ++ ++ u8 buf = 0; ++ ret = i2c_master_send(db1550_i2c_client, &subaddr, 1); ++ while (ret != 1){ ++// printk(KERN_INFO"send return %d\n", ret); ++ ret = i2c_master_send(db1550_i2c_client, &subaddr, 1); ++ } ++ // printk(KERN_INFO"send return %d\n", ret); ++ ret = i2c_master_recv(db1550_i2c_client, &buf, 1); ++ while (ret != 1){ ++ // printk(KERN_INFO"rec return %d with %02x\n", ret, buf); ++ ret = i2c_master_send(db1550_i2c_client, &subaddr, 1); ++ while (ret != 1){ ++ // printk(KERN_INFO"send return %d\n", ret); ++ ret = i2c_master_send(db1550_i2c_client, &subaddr, 1); ++ } ++ ret = i2c_master_recv(db1550_i2c_client, &buf, 1); ++ } ++ // printk(KERN_INFO"rec return %d with %02x\n", ret, buf); ++ TRACE_MSG2(TCD, "addr: %02x buf: %02x", subaddr, buf); ++ return buf; ++} ++ ++ ++/*! i2c_readl ++ * Read long from i2c device ++ * @param subaddr ++ */ ++u32 i2c_readl(u8 subaddr) ++{ ++ int ret; ++ // u32 buf = 0; ++ u8 buf[5]; ++ ret = i2c_master_send(db1550_i2c_client, &subaddr, 1); ++ while (ret != 1){ ++ printk(KERN_INFO"send return %d\n", ret); ++ ret = i2c_master_send(db1550_i2c_client, &subaddr, 1); ++ } ++ printk(KERN_INFO"send return %d\n", ret); ++ ret = i2c_master_recv(db1550_i2c_client, buf, 4); ++ while (ret != 4){ ++ printk(KERN_INFO"rec return %d with %02x\n", ret, buf); ++ ret = i2c_master_send(db1550_i2c_client, &subaddr, 1); ++ while (ret != 1){ ++ printk(KERN_INFO"send return %d\n", ret); ++ ret = i2c_master_send(db1550_i2c_client, &subaddr, 1); ++ } ++ ret = i2c_master_recv(db1550_i2c_client, buf, 4); ++ } ++ printk(KERN_INFO"rec return %d with %02x %02x %02x %02x\n", ret, buf[0], buf[1], buf[2], buf[3]); ++ TRACE_MSG2(TCD, "addr: %02x buf: %08x", subaddr, buf); ++ return buf; ++} ++ ++int i2c_readw (int addr) ++{ ++ int data; ++ ++ data = 0; ++ ++ return data; ++} ++ ++ ++EXPORT_SYMBOL(i2c_writeb); ++EXPORT_SYMBOL(i2c_readw); ++EXPORT_SYMBOL(i2c_readb); ++EXPORT_SYMBOL(i2c_readl); ++EXPORT_SYMBOL(i2c_configure); ++EXPORT_SYMBOL(i2c_close); ++ +diff -uNr linux/drivers/no-otg/ocd/max3353e/max3353-procfs.c linux/drivers/otg/ocd/max3353e/max3353-procfs.c +--- linux/drivers/no-otg/ocd/max3353e/max3353-procfs.c 1970-01-01 01:00:00.000000000 +0100 ++++ linux/drivers/otg/ocd/max3353e/max3353-procfs.c 2006-09-01 21:41:32.000000000 +0200 +@@ -0,0 +1,303 @@ ++/* ++ * otg/ocd/max3353e/max3353-procfs.c - USB Device Core Layer ++ * @(#) balden@belcarra.com|otg/ocd/max3353e/max3353-procfs.c|20060831021117|57336 ++ * ++ * Copyright (c) 2004-2005 Belcarra ++ * Copyright (c) 2005-2006 Belcarra Technologies 2005 Corp ++ * ++ * By: ++ * Stuart Lynne <sl@belcarra.com>, ++ * Shahrad Payandeh <sp@belcarra.com>, ++ * Bruce Balden <balden@belcarra.com> ++ * ++ * ++ */ ++/*! ++ * @file otg/ocd/max3353e/max3353-procfs.c ++ * @brief Implement /proc/max3353 to dump MAX3353 registers. ++ * ++ * ++ * @ingroup MAX3353TCD ++ */ ++ ++ ++#include <otg/pcd-include.h> ++#include "otghw/max3353-hardware.h" ++#include "max3353.h" ++ ++#ifdef CONFIG_ARCH_MX2ADS ++#include <asm/arch/mx2.h> ++#define MX2_OTG_XCVR_DEVAD 0x18 ++#define MX2_SEQ_OP_REG 0x19 ++#define MX2_SEQ_RD_STARTAD 0x1a ++#define MX2_I2C_OP_CTRL_REG 0x1b ++#define MX2_SCLK_TO_SCL_HPER 0x1e ++#define MX2_I2C_INTERRUPT_AND_CTRL 0x1f ++ ++#define OTG_BASE_ADDR 0x10024000 ++//#define OTG_I2C_BASE (OTG_BASE_ADDR+0x100) ++ ++#endif /* CONFIG_ARCH_MX2ADS */ ++ ++#ifdef CONFIG_OTG_MAX3353_PROCFS ++/* Proc Filesystem *************************************************************************** */ ++ ++extern struct max3353_private max3353_private; ++ ++#define MAX_HISTORY 6 ++struct reg_list { ++ u8 reg; ++ u8 size; ++ char *name; ++ u32 values[MAX_HISTORY]; ++}; ++ ++#define REG(r, s) {r, s, #r, } ++ ++struct reg_list max3353_prod_list[] = { ++ REG(MAX3353_VENDOR_ID, 2), ++ REG(MAX3353_PRODUCT_ID, 2), ++ REG(MAX3353_VERSION_ID, 2), ++ { 0, 1, NULL,}, ++}; ++struct reg_list max3353_reg_list[] = { ++ REG(MAX3353_OTG_CONTROL_SET, 1), ++ REG(MAX3353_INTERRUPT_SOURCE, 1), ++ REG(MAX3353_INTERRUPT_LATCH_SET, 1), ++ REG(MAX3353_INTERRUPT_ENABLE_LOW_SET, 1), ++ REG(MAX3353_INTERRUPT_ENABLE_HIGH_SET, 1), ++ REG(MAX3353_MODE_CONTROL_1_SET, 1), ++ { 0, 1, NULL,}, ++}; ++struct reg_list max3353_spec_list[] = { ++ REG(MAX3353_MODE_CONTROL_2_SET, 1), ++ REG(MAX3353_OTG_STATUS, 1), ++ { 0, 1, NULL,}, ++}; ++struct reg_list max3301e_spec_list[] = { ++ REG(MAX3301E_SPECIAL_FUNCTION_1_SET, 1), ++ REG(MAX3301E_SPECIAL_FUNCTION_2_SET, 1), ++ { 0, 1, NULL,}, ++}; ++#ifdef CONFIG_ARCH_MX2ADS ++struct reg_list mx21_spec_list[] = { ++ REG(MX2_OTG_XCVR_DEVAD, 1), ++ REG(MX2_SEQ_OP_REG, 1), ++ REG(MX2_SEQ_RD_STARTAD, 1), ++ REG(MX2_I2C_OP_CTRL_REG, 1), ++ REG(MX2_SCLK_TO_SCL_HPER, 1), ++ REG(MX2_I2C_INTERRUPT_AND_CTRL, 1), ++ { 0, 1, NULL,}, ++}; ++#endif /* CONFIG_ARCH_MX2ADS */ ++ ++void max3353_update(struct reg_list *list) ++{ ++ for (; list && list->name; list++) { ++ memmove(list->values + 1, list->values, sizeof(list->values) - sizeof(u32)); ++ switch(list->size) { ++ case 1: ++ list->values[0] = i2c_readb(list->reg); ++ break; ++ case 2: ++ list->values[0] = i2c_readw(list->reg); ++ break; ++ case 4: ++ list->values[0] = i2c_readl(list->reg); ++ break; ++ } ++ } ++} ++ ++#ifdef CONFIG_ARCH_MX2ADS ++static u8 __inline__ mx2_rb(u32 port) ++{ ++ return *(volatile u8 *) (MX2_IO_ADDRESS(port + OTG_I2C_BASE)); ++} ++ ++ ++void mx21_update(struct reg_list *list) ++{ ++ for (; list && list->name; list++) { ++ memmove(list->values + 1, list->values, sizeof(list->values) - sizeof(u32)); ++ list->values[0] = mx2_rb(list->reg); ++ } ++} ++ ++#endif /* CONFIG_ARCH_MX2ADS */ ++ ++void max3353_update_all(void) ++{ ++ max3353_update(max3353_reg_list); ++ switch (max3353_private.transceiver_map->transceiver_type) { ++ case max3353: ++ max3353_update(max3353_spec_list); ++ break; ++ case max3301e: ++ max3353_update(max3301e_spec_list); ++ break; ++ default: ++ break; ++ } ++#ifdef CONFIG_ARCH_MX2ADS ++ mx21_update(mx21_spec_list); ++#endif /* CONFIG_ARCH_MX2ADS */ ++} ++ ++ ++ ++/* ++ * 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); ++} ++ ++int max3353_dump(char *buf, char *name, char *fmt, struct reg_list *reg) ++{ ++ int len = 0, i; ++ len += sprintf (buf + len, "%-20s %-34s [%03x]: ", name, reg->name, reg->reg); ++ len += sprintf (buf + len, fmt, reg->values[0]); ++ for (i = 1; i < MAX_HISTORY; i++) ++ if (reg->values[i - 1] == reg->values[i]) ++ len += sprintf (buf + len, " "); ++ else ++ len += sprintf (buf + len, fmt, reg->values[i]); ++ len += sprintf (buf + len, "\n"); ++ return len; ++} ++ ++int max3353_dump_list(char * buf, char *name, struct reg_list *list) ++{ ++ int len = 0; ++ for (; list && list->name; list++) ++ switch(list->size) { ++ case 1: len += max3353_dump(buf + len, name, " %02x", list); break; ++ case 2: len += max3353_dump(buf + len, name, " %04x", list); break; ++ case 4: len += max3353_dump(buf + len, name, " %08x", list); break; ++ } ++ return len; ++} ++ ++int max3353_dump_all(char *buf) ++{ ++ int len = 0; ++ len += max3353_dump_list(buf + len, "MAX3353 Standard", max3353_reg_list); ++ switch (max3353_private.transceiver_map->transceiver_type) { ++ case max3353: ++ len += max3353_dump_list(buf + len, "MAX3353 Extra", max3353_spec_list); ++ break; ++ case max3301e: ++ len += max3353_dump_list(buf + len, "MAX3301E Extra", max3301e_spec_list); ++ break; ++ default: ++ break; ++ } ++#ifdef CONFIG_ARCH_MX2ADS ++ len += max3353_dump_list(buf + len, "MX21 ADS Extra", mx21_spec_list); ++#endif /* CONFIG_ARCH_MX2ADS */ ++ return len; ++} ++ ++ ++ ++/*! ++ * max3353_device_proc_read - implement proc file system read. ++ * ++ * 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 max3353_device_proc_read_functions (struct file *file, char *buf, size_t count, loff_t * pos) ++{ ++ unsigned long page; ++ int len = 0; ++ int index; ++ int i; ++ u32 r; ++ ++ int config_size; ++ ++ // get a page, max 4095 bytes of data... ++ RETURN_EINVAL_UNLESS ((page = get_free_page (GFP_KERNEL))); ++ ++ len = 0; ++ index = (*pos)++; ++ ++ switch(index) { ++ case 0: ++ len += sprintf ((char *) page + len, "MAX3353 Transceiver Registers\n"); ++ ++ max3353_update_all(); ++ len += sprintf ((char *) page + len , "Vendor: %04x Product: %04x Revision: %04x %s\n", ++ max3353_private.vendor, max3353_private.product, ++ max3353_private.revision, max3353_private.transceiver_map->name ++ ); ++ ++ len += max3353_dump_all((char *) page + len); ++ len += sprintf ((char *) page + len, "\n"); ++ ++ break; ++ ++ case 1: ++ break; ++ ++ } ++ ++ if (len > count) { ++ len = -EINVAL; ++ } ++ else if ((len > 0) && copy_to_user (buf, (char *) page, len)) { ++ len = -EFAULT; ++ } ++ else { ++ } ++ free_page (page); ++ return len; ++} ++ ++static struct file_operations max3353_device_proc_operations_functions = { ++ read:max3353_device_proc_read_functions, ++}; ++ ++/* Module init ******************************************************************************* */ ++ ++static int max3353_procfs_init (void) ++{ ++ struct proc_dir_entry *p; ++ ++ // create proc filesystem entries ++ if ((p = create_proc_entry ("max3353", 0, 0)) == NULL) ++ return -ENOMEM; ++ ++ p->proc_fops = &max3353_device_proc_operations_functions; ++ ++ max3353_update_all(); ++ ++ return 0; ++} ++static void max3353_procfs_exit (void) ++{ ++ remove_proc_entry ("max3353", NULL); ++} ++#else ++static int max3353_procfs_init (void) { return 0; ++} ++static void max3353_procfs_exit (void) { } ++#endif +diff -uNr linux/drivers/no-otg/ocd/max3353e/max3353.c linux/drivers/otg/ocd/max3353e/max3353.c +--- linux/drivers/no-otg/ocd/max3353e/max3353.c 1970-01-01 01:00:00.000000000 +0100 ++++ linux/drivers/otg/ocd/max3353e/max3353.c 2006-09-01 21:41:32.000000000 +0200 +@@ -0,0 +1,706 @@ ++/* ++ * otg/ocd/max3353e/max3353.c -- USB Transceiver Controller driver ++ * @(#) balden@belcarra.com|otg/ocd/max3353e/max3353.c|20060831021117|27226 ++ * ++ * Copyright (c) 2003-2005 Belcarra ++ * Copyright (c) 2005-2006 Belcarra Technologies 2005 Corp ++ * ++ * By: ++ * Stuart Lynne <sl@lbelcarra.com>, ++ * Shahrad Payandeh <sp@belcarra.com>, ++ * Bruce Balden <balden@belcarra.com> ++ * ++ */ ++/*! ++ * @file otg/ocd/max3353e/max3353.c ++ * @brief MAX3353 OTG Transceiver Driver. ++ * ++ * This is the generic MAX3353 TCD core support. ++ * ++ * Notes: ++ * ++ * 1. The MAX3353 can control the speed and suspend directly, would it be ++ * appropriate to allow state machine to control this. ++ * ++ * 2. The MAX3353 has auto connect feature, can this be used without change ++ * to state machine. ++ * ++ * 3. The MAX3353 can control the ADR/PSW pin to enable / disable external ++ * charge pump. ++ * ++ * @ingroup MAX3353TCD ++ */ ++ ++#include <otg/otg-compat.h> ++ ++#include <asm/irq.h> ++#include <asm/system.h> ++#include <asm/io.h> ++#include <linux/i2c.h> ++ ++#include <otg/usbp-chap9.h> ++#include <otg/usbp-bus.h> ++#include <otg/otg-trace.h> ++#include <otg/otg-api.h> ++#include <otg/otg-hcd.h> ++#include <otg/otg-tcd.h> ++#include <otg/otg-ocd.h> ++#include <otg/otg-pcd.h> ++ ++#include <otghw/max3353-hardware.h> ++#include "max3353.h" ++ ++ ++struct otg_transceiver_map max3353_transceiver_map[] = { ++ { max3353, 0x4cc, 0x1301, 0x00, "Phillips MAX3353", }, ++ { max3301e, 0x6a0b, 0x0133, 0x00, "Maxim MAX3301E", }, ++ { 0, 0, 0, 0, "Unknown Transceiver", }, ++}; ++ ++struct max3353_private max3353_private; ++ ++ ++ ++/* ********************************************************************************************* */ ++#define TRACE_I2CB(t,r) TRACE_MSG3(t, "%-40s[%02x] %02x", #r, r, i2c_readb(r)) ++#define TRACE_I2CW(t,r) TRACE_MSG3(t, "%-40s[%02x] %04x", #r, r, i2c_readw(r)) ++#define TRACE_GPIO(t,b,r) TRACE_MSG2(t, "%-40s %04x", #r, readw(b + r)) ++#define TRACE_REGL(t,r) TRACE_MSG2(t, "%-40s %08x", #r, readl(r)) ++ ++ ++ ++/*! max3353_int_src - update interrupt source test mask ++ * ++ * This sets the current mask and updates the interrupt registers to match. ++ */ ++void max3353_int_src(u8 int_src) ++{ ++ TRACE_MSG1(TCD, "setting int_src %02x", int_src); ++ max3353_private.int_src = int_src; ++ i2c_writeb(MAX3353_INTERRUPT_ENABLE_LOW_CLR, ~int_src); ++ i2c_writeb(MAX3353_INTERRUPT_ENABLE_HIGH_CLR, ~int_src); ++ i2c_writeb(MAX3353_INTERRUPT_ENABLE_LOW_SET, int_src); ++ i2c_writeb(MAX3353_INTERRUPT_ENABLE_HIGH_SET, int_src); ++ i2c_writeb(MAX3353_INTERRUPT_LATCH_CLR, int_src); ++} ++ ++/*! max3353_int_src_set - add to the interrupt source mask ++ * ++ */ ++void max3353_int_src_set(u8 int_src) ++{ ++ max3353_int_src(int_src |= max3353_private.int_src); ++} ++ ++/*! max3353_int_src_clr - remove from the interrup source mask ++ */ ++void max3353_int_src_clr(u8 int_src) ++{ ++ max3353_int_src(int_src = max3353_private.int_src & ~int_src); ++} ++ ++/* ********************************************************************************************* */ ++ ++ ++/*! max3353_event_set_irq ++ */ ++void max3353_event_set_irq(struct otg_instance *otg, int changed, int flag, u32 input, otg_tag_t tag, char *mset, char *mreset) ++{ ++ TRACE_MSG5(TCD, "changed: %x flag: %x input: %08x %s %s", changed, flag, input, ++ flag ? mset: mreset, ++ changed ? "": "(ignored)"); ++ ++ otg_event_set_irq(otg, changed, flag, input, tag, flag ? mset : mreset); ++} ++ ++/*! max3353_update - called from mx2_i2c_xcvr_bh to find changes in the max3353 Interrupt Source Register ++ */ ++u8 max3353_update_int_src(struct otg_instance *otg, u8 int_src, u8 changed) ++{ ++ UNLESS (changed) return int_src; ++ TRACE_MSG2(TCD, "CHANGED INTERRUPT_SOURCE: %02x (%02x) ", int_src, changed); ++ ++ /* Core events ++ */ ++ max3353_event_set_irq(otg, MAX3353_ID_GND & changed, MAX3353_ID_GND & int_src, ++ ID_GND, TCD, "ID_GND", "ID_GND/"); ++ max3353_event_set_irq(otg, MAX3353_ID_FLOAT & changed, MAX3353_ID_FLOAT & int_src, ++ ID_FLOAT, TCD, "ID_FLOAT", "ID_FLOAT/"); ++ max3353_event_set_irq(otg, MAX3353_VBUS_VLD & changed, MAX3353_VBUS_VLD & int_src, ++ VBUS_VLD, TCD, "VBUS_VLD", "VBUS_VLD/"); ++ max3353_event_set_irq(otg, MAX3353_SESS_VLD & changed, MAX3353_SESS_VLD & int_src, ++ A_SESS_VLD, TCD, "A_SESS_VLD", "A_SESS_VLD/"); ++ ++ max3353_event_set_irq(otg, MAX3353_DM_HI & changed, MAX3353_DM_HI & int_src, ++ DM_HIGH, TCD, "DM_HIGH", "DM_HIGH/"); ++ ++ max3353_event_set_irq(otg, MAX3353_DP_HI & changed, MAX3353_DP_HI & int_src, ++ DP_HIGH, TCD, "DP_HIGH", "DP_HIGH/"); ++ ++ //max3353_event_set_irq(otg, MAX3353_SE1 & changed, ((MAX3353_SE1 & int_src) == 0), SE0_DET, TCD, "SE0", "SE0/"); ++ ++ /* carkit ++ */ ++ max3353_event_set_irq(otg, MAX3353_CR_INT & changed, !(MAX3353_CR_INT & int_src), ++ CR_INT_DET, TCD, "CR_INT", "CR_INT/"); ++ ++ //TRACE_MSG3(TCD, "MAX3353_SE1: %02x & int_src: %02x = %02x", MAX3353_SE1, int_src, MAX3353_SE1 & int_src); ++ //max3353_event_set_irq(otg, MAX3353_SE1 & changed, ((MAX3353_SE1 & int_src) == MAX3353_SE1), ++ // SE1_DET, TCD, "SE1", "SE1/"); ++ ++ ++ /* A-Device ++ */ ++ max3353_event_set_irq(otg, MAX3353_BDIS_ACON & changed, !(MAX3353_BDIS_ACON & int_src), ++ BDIS_ACON, TCD, "BDIS_ACON", "BDIS_ACON/" ); ++ ++ return int_src; ++} ++ ++/*! max3353_update - called from mx2_i2c_xcvr_bh to find changes in the max3353 OTG Status Register ++ */ ++u8 max3353_update_otg_status(struct otg_instance *otg, u8 otg_status, u8 changed) ++{ ++ UNLESS (changed) return otg_status; ++ TRACE_MSG2(TCD, "CHANGED OTG_STATUS_REG: %02x (%02x) ", otg_status, changed); ++ ++ max3353_event_set_irq(otg, MAX3353_B_SESS_VLD & changed, MAX3353_B_SESS_VLD & otg_status, ++ B_SESS_VLD, TCD, "B_SESS_VLD", "B_SESS_VLD/"); ++ max3353_event_set_irq(otg, MAX3353_B_SESS_END & changed, MAX3353_B_SESS_END & otg_status, ++ B_SESS_END, TCD, "B_SESS_END", "B_SESS_END/"); ++ ++ //otg_b_sess_vld(otg, first || (MAX3353_B_SESS_VLD & changed), MAX3353_B_SESS_VLD & otg_status, MAX3353_NAME ); ++ return otg_status; ++} ++ ++int max3353_bh_first; ++ ++ ++/*! max3353_bh ++ */ ++void max3353_bh(void *arg) ++{ ++ u8 int_lat, int_src = 0, otg_status = 0, special_function_22 = 0; ++ static u8 int_src_saved, otg_status_saved, special_function_22_saved; ++ ++ //TRACE_MSG0(TCD, "--"); ++ /* read the interrupt latch and and clear latch and update otg ++ */ ++ if ((int_lat = i2c_readb(MAX3353_INTERRUPT_LATCH_SET))) { ++ //TRACE_MSG1(TCD, "CLEARING INT LAT: %02x", int_lat); ++ i2c_writeb(MAX3353_INTERRUPT_LATCH_CLR, int_lat); ++ } ++ ++ /* If int_lat shows a change or we are forcing an update, read interrupt source. ++ * Note that we mask DP_HI and DM_HI when LOC_CONN is set, we don't want them ++ * when USB Bus is in use. ++ * ++ * Mask with bits we are currently interested in. ++ */ ++ int_src = i2c_readb(MAX3353_INTERRUPT_SOURCE) & max3353_private.int_src; ++ ++ UNLESS ( max3353_private.flags & MAX3353_LOC_CONN) { ++ } ++ ++ ++ /* further work if B-Device and VBUS_VLD ++ */ ++ if ( (!(int_src & MAX3353_ID_GND) && (int_src & MAX3353_VBUS_VLD)) || ++ (!(int_src_saved & MAX3353_ID_GND) && (int_src_saved & MAX3353_VBUS_VLD)) ) ++ { ++ /* read the otg_status register and update otg state machine ++ * ++ * XXX this needs to be conditional on Transceiver type. The ++ * MAX3301E for example supports sess_end in a separate register. ++ */ ++ switch (max3353_private.transceiver_map->transceiver_type) { ++ case max3353: ++ otg_status = i2c_readb(MAX3353_OTG_STATUS); ++ if (max3353_bh_first) ++ otg_status_saved = ~otg_status; ++ otg_status_saved = max3353_update_otg_status(tcd_instance->otg, otg_status, ++ otg_status_saved ^ otg_status); ++ break; ++ case max3301e: ++ // XXX ++ special_function_22 = i2c_readb(MAX3301E_SPECIAL_FUNCTION_2_SET); ++ break; ++ case unknown_transceiver: ++ break; ++ } ++ } ++ if (max3353_bh_first) ++ int_src_saved = ~int_src; ++ ++ int_src_saved = max3353_update_int_src(tcd_instance->otg, int_src, int_src_saved ^ int_src); ++ ++ max3353_bh_first = 0; ++} ++ ++/* ********************************************************************************************** */ ++/*! max3353_vbus - Do we have Vbus (cable attached?) ++ * Return non-zero if Vbus is detected. ++ * ++ */ ++int max3353_vbus (struct otg_instance *otg) ++{ ++ return 0; ++} ++ ++/*! max3353_id - Do we have Vbus (cable attached?) ++ * Return non-zero if Vbus is detected. ++ * ++ */ ++int max3353_idvbus (struct otg_instance *otg) ++{ ++ return 0; ++} ++ ++ ++/* ********************************************************************************************* */ ++ ++/*! max3353_chrg_vbus - used to enable or disable B-device Vbus charging ++ */ ++void max3353_chrg_vbus(struct otg_instance *otg, u8 flag) ++{ ++ TRACE_MSG0(TCD, "--"); ++ switch (flag) { ++ case SET: ++ TRACE_MSG0(TCD, "CHRG_VBUS_SET"); ++ i2c_writeb(MAX3353_OTG_CONTROL_SET, (u8) MAX3353_VBUS_CHRG); ++ break; ++ case RESET: ++ TRACE_MSG0(TCD, "CHRG_VBUS_RESET"); ++ i2c_writeb(MAX3353_OTG_CONTROL_CLR, (u8) MAX3353_VBUS_CHRG); ++ break; ++ case PULSE: ++ break; ++ } ++} ++ ++/*! max3353_drv_vbus - used to enable or disable A-device driving Vbus ++ */ ++void max3353_drv_vbus(struct otg_instance *otg, u8 flag) ++{ ++ TRACE_MSG0(TCD, "--"); ++ switch (flag) { ++ case SET: ++ TRACE_MSG0(TCD, "DRV_VBUS_SET"); ++ i2c_writeb(MAX3353_OTG_CONTROL_SET, (u8) MAX3353_VBUS_DRV); ++ break; ++ case RESET: ++ TRACE_MSG0(TCD, "DRV_VBUS_RESET"); ++ i2c_writeb(MAX3353_OTG_CONTROL_CLR, (u8) MAX3353_VBUS_DRV); ++ break; ++ } ++} ++ ++/*! max3353_dischrg_vbus - used to enable Vbus discharge ++ */ ++void max3353_dischrg_vbus(struct otg_instance *otg, u8 flag) ++{ ++ struct tcd_instance *tcd = otg->tcd; ++ TRACE_MSG0(TCD, "--"); ++ switch (flag) { ++ case SET: ++ TRACE_MSG0(TCD, "OUTPUT: TCD_DISCHRG_VBUS_SET"); ++ i2c_writeb(MAX3353_OTG_CONTROL_SET, MAX3353_VBUS_DISCHRG); ++ break; ++ case RESET: ++ TRACE_MSG0(TCD, "OUTPUT: TCD_DISCHRG_VBUS_RESET"); ++ i2c_writeb(MAX3353_OTG_CONTROL_CLR, MAX3353_VBUS_DISCHRG); ++ break; ++ } ++} ++ ++/*! max3353_dp_pullup_func - used to enable or disable peripheral connecting to bus ++ * ++ * C.f. 5.1.6, 5.1.7, 5.2.4 and 5.2.5 ++ * ++ * host peripheral ++ * d+ pull-up clr set ++ * d+ pull-down set clr ++ * ++ * d- pull-up clr clr ++ * d- pull-down set set ++ * ++ */ ++void max3353_dp_pullup_func(struct otg_instance *otg, u8 flag) ++{ ++ struct tcd_instance *tcd = (struct tcd_instance *)otg->tcd; ++ TRACE_MSG0(TCD, "--"); ++ switch (flag) { ++ case SET: ++ printk(KERN_INFO"%s: --\n", __FUNCTION__); ++ max3353_private.flags |= MAX3353_LOC_CONN; ++ TRACE_MSG0(TCD, "MAX3353_LOC_CONN SET - Clr DP PULLDOWN"); ++ //i2c_writeb(MAX3353_OTG_CONTROL_CLR, (u8) MAX3353_DP_PULLDOWN); ++ //i2c_writeb(MAX3353_OTG_CONTROL_CLR, (u8) (MAX3353_DP_PULLDOWN | MAX3353_DM_PULLDOWN) ); ++ TRACE_MSG0(TCD, "MAX3353_LOC_CONN SET - Set DP PULLUP"); ++ i2c_writeb(MAX3353_OTG_CONTROL_SET, (u8) MAX3353_DP_PULLUP); ++ break; ++ ++ case RESET: ++ max3353_private.flags &= ~MAX3353_LOC_CONN; ++ TRACE_MSG0(TCD, "MAX3353_LOC_CONN RESET - Clr DP PULLUP"); ++ i2c_writeb(MAX3353_OTG_CONTROL_CLR, (u8)MAX3353_DP_PULLUP); ++ TRACE_MSG0(TCD, "MAX3353_LOC_CONN SET - Set DP PULLDOWN"); ++ //i2c_writeb(MAX3353_OTG_CONTROL_SET, (u8)MAX3353_DP_PULLDOWN); ++ //i2c_writeb(MAX3353_OTG_CONTROL_SET, (u8) (MAX3353_DP_PULLDOWN | MAX3353_DM_PULLDOWN) ); ++ break; ++ ++ case PULSE: ++ TRACE_MSG0(TCD, "MAX3353_LOC_CONN PULSE"); ++ break; ++ } ++} ++ ++/*! max3353_dm_pullup_func - used to enable or disable peripheral connecting to bus ++ * ++ */ ++void max3353_dm_pullup_func(struct otg_instance *otg, u8 flag) ++{ ++ struct tcd_instance *tcd = (struct tcd_instance *)otg->tcd; ++ TRACE_MSG0(TCD, "--"); ++ switch (flag) { ++ case SET: ++ max3353_private.flags |= MAX3353_LOC_CONN; ++ TRACE_MSG0(TCD, "MAX3353_LOC_CONN SET - Clr DM PULLDOWN"); ++ //i2c_writeb(MAX3353_OTG_CONTROL_CLR, (u8) MAX3353_DM_PULLDOWN); ++ TRACE_MSG0(TCD, "MAX3353_LOC_CONN SET - Set DM PULLUP"); ++ i2c_writeb(MAX3353_OTG_CONTROL_SET, (u8) MAX3353_DM_PULLUP); ++ break; ++ ++ case RESET: ++ max3353_private.flags &= ~MAX3353_LOC_CONN; ++ TRACE_MSG0(TCD, "MAX3353_LOC_CONN RESET - Clr DM PULLUP"); ++ i2c_writeb(MAX3353_OTG_CONTROL_CLR, (u8)MAX3353_DM_PULLUP); ++ TRACE_MSG0(TCD, "MAX3353_LOC_CONN SET - Set DM PULLDOWN"); ++ //i2c_writeb(MAX3353_OTG_CONTROL_SET, (u8)MAX3353_DM_PULLDOWN); ++ break; ++ ++ case PULSE: ++ TRACE_MSG0(TCD, "MAX3353_LOC_CONN PULSE"); ++ break; ++ } ++} ++ ++/*! max3353_dm_det_func - used to enable or disable D- detect ++ * ++ */ ++void max3353_dm_det_func(struct otg_instance *otg, u8 flag) ++{ ++ struct tcd_instance *tcd = (struct tcd_instance *)otg->tcd; ++ TRACE_MSG0(TCD, "--"); ++ switch (flag) { ++ case SET: ++ TRACE_MSG0(TCD, "setting DM_HI detect"); ++ max3353_int_src_set(MAX3353_DM_HI); ++ max3353_bh_first = 1; ++ break; ++ ++ case RESET: ++ TRACE_MSG0(TCD, "reseting DM_HI detect"); ++ max3353_int_src_clr(MAX3353_DM_HI); ++ break; ++ } ++} ++ ++/*! max3353_dp_det_func - used to enable or disable D+ detect ++ * ++ */ ++void max3353_dp_det_func(struct otg_instance *otg, u8 flag) ++{ ++ struct tcd_instance *tcd = (struct tcd_instance *)otg->tcd; ++ TRACE_MSG0(TCD, "--"); ++ switch (flag) { ++ case SET: ++ TRACE_MSG0(TCD, "setting DP_HI detect"); ++ max3353_int_src_set(MAX3353_DP_HI); ++ max3353_bh_first = 1; ++ break; ++ ++ case RESET: ++ TRACE_MSG0(TCD, "reseting DP_HI detect"); ++ max3353_int_src_clr(MAX3353_DP_HI); ++ break; ++ } ++} ++ ++/*! max3353_bdis_acon_func - used to enable or disable auto a-connect ++ * ++ */ ++void max3353_bdis_acon_func(struct otg_instance *otg, u8 flag) ++{ ++ struct tcd_instance *tcd = (struct tcd_instance *)otg->tcd; ++ TRACE_MSG0(TCD, "--"); ++ switch (flag) { ++ case SET: ++ TRACE_MSG0(TCD, "setting BDIS ACON"); ++ i2c_writeb(MAX3353_OTG_CONTROL_SET, MAX3353_ID_PULLDOWN); ++ break; ++ ++ case RESET: ++ TRACE_MSG0(TCD, "reseting BDIS ACON"); ++ i2c_writeb(MAX3353_OTG_CONTROL_CLR, MAX3353_ID_PULLDOWN); ++ break; ++ } ++} ++ ++/*! max3353_id_pulldown_func - used to enable or disable ID pulldown ++ * ++ */ ++void max3353_id_pulldown_func(struct otg_instance *otg, u8 flag) ++{ ++ struct tcd_instance *tcd = (struct tcd_instance *)otg->tcd; ++ TRACE_MSG0(TCD, "--"); ++ switch (flag) { ++ case SET: ++ TRACE_MSG0(TCD, "setting ID PULLDOWN"); ++ i2c_writeb(MAX3353_MODE_CONTROL_1_SET, MAX3353_BDIS_ACON_EN); ++ break; ++ ++ case RESET: ++ TRACE_MSG0(TCD, "reseting ID PULLDOWN"); ++ i2c_writeb(MAX3353_MODE_CONTROL_1_CLR, MAX3353_BDIS_ACON_EN); ++ break; ++ } ++} ++ ++/*! max3353_audio_func - used to enable or disable Carkit Interrupt ++ * ++ */ ++void max3353_audio_func(struct otg_instance *otg, u8 flag) ++{ ++ struct tcd_instance *tcd = (struct tcd_instance *)otg->tcd; ++ TRACE_MSG0(TCD, "--"); ++ switch (flag) { ++ case SET: ++ TRACE_MSG0(TCD, "setting CARKIT INT"); ++ i2c_writeb(MAX3353_MODE_CONTROL_2_SET, MAX3353_AUDIO_EN); ++ break; ++ ++ case RESET: ++ TRACE_MSG0(TCD, "reseting CARKIT INT"); ++ i2c_writeb(MAX3353_MODE_CONTROL_2_CLR, MAX3353_AUDIO_EN); ++ break; ++ } ++} ++ ++/*! max3353_uart_func - used to enable or disable transparent uart mode ++ * ++ */ ++void max3353_uart_func(struct otg_instance *otg, u8 flag) ++{ ++ struct tcd_instance *tcd = (struct tcd_instance *)otg->tcd; ++ TRACE_MSG0(TCD, "--"); ++ switch (flag) { ++ case SET: ++ TRACE_MSG0(TCD, "setting UART"); ++ i2c_writeb(MAX3353_MODE_CONTROL_1_SET, MAX3353_UART_EN); ++ /* XXX enable uart */ ++ break; ++ ++ case RESET: ++ MAX3353SG0(TCD, "reseting UART"); ++ i2c_writeb(MAX3353_MODE_CONTROL_1_CLR, MAX3353_UART_EN); ++ /* XXX disable uart */ ++ break; ++ } ++} ++ ++/*! max3353_mono_func - used to enable or disable mono audio connection ++ * ++ */ ++void max3353_mono_func(struct otg_instance *otg, u8 flag) ++{ ++ struct tcd_instance *tcd = (struct tcd_instance *)otg->tcd; ++ TRACE_MSG0(TCD, "--"); ++ switch (flag) { ++ case SET: ++ TRACE_MSG0(TCD, "setting MONO"); ++ /* XXX enable mono output */ ++ break; ++ ++ case RESET: ++ TRACE_MSG0(TCD, "reseting MONO"); ++ /* XXX disable mono output */ ++ break; ++ } ++} ++ ++ ++/* ********************************************************************************************* */ ++extern int max3353_procfs_init (void); ++extern void max3353_procfs_exit (void); ++ ++/*! max3353_mod_init ++ */ ++int max3353_mod_init(void) ++{ ++ /* for interrupt bottom half handler ++ */ ++ PREPARE_WORK_ITEM(max3353_private.bh, &max3353_bh, NULL); ++ ++ /* setup max3353, enable interrupts, clear latch ++ * XXX when and how do we do DM_HI and DP_HI, when ID is present? ++ */ ++ max3353_int_src(MAX3353_INT_SRC); ++ //max3353_int_src(MAX3353_NO_SE1_INT); ++ ++ i2c_writeb(MAX3353_OTG_CONTROL_SET, MAX3353_DM_PULLDOWN | MAX3353_DP_PULLDOWN); ++ ++#ifdef CONFIG_OTG_MAX3353_PROCFS ++ max3353_procfs_init (); ++#endif /* CONFIG_OTG_MAX3353_PROCFS */ ++ ++ return 0; ++} ++ ++/*! max3353_mod_exit ++ */ ++void max3353_mod_exit(void) ++{ ++ i2c_writeb(MAX3353_INTERRUPT_ENABLE_LOW_CLR, 0xff); ++ i2c_writeb(MAX3353_INTERRUPT_ENABLE_HIGH_CLR, 0xff); ++ //i2c_close(); ++#ifdef CONFIG_OTG_MAX3353_PROCFS ++ max3353_procfs_exit (); ++#endif /* CONFIG_OTG_MAX3353_PROCFS */ ++ ++ while (PENDING_WORK_ITEM(max3353_private.bh)) { ++ printk(KERN_ERR"%s: waiting for bh\n", __FUNCTION__); ++ schedule_timeout(10 * HZ); ++ } ++ ++} ++ ++/* ********************************************************************************************* */ ++/*! max3353_configure - configure the MAX3353 for this host ++ * @param tx_mode - the type of connection between the host USB and the MAX3353 ++ * @param spd_ctrl - suspend control method ++ */ ++void max3353_configure(tx_mode_t tx_mode, spd_ctrl_t spd_ctrl) ++{ ++ u16 vendor = 0, product = 0, revision = 0; ++ u8 mode1, mode2, control; ++ ++ struct otg_transceiver_map *map = max3353_transceiver_map; ++ ++ ++#if defined(CONFIG_OTG_MAX3353_MX2ADS) || defined(CONFIG_OTG_MAX3353_MX2ADS_MODULE) ++ u32 vp; ++ MAX3353SG0(TCD, "1. Read Transceiver ID's with long read"); ++ vp = i2c_readl(MAX3353_VENDOR_ID); ++ max3353_private.vendor = vp & 0xffff; ++ max3353_private.product = vp >> 16; ++ max3353_private.revision = i2c_readw(MAX3353_VERSION_ID); ++ mode1 = i2c_readb(MAX3353_MODE_CONTROL_1_SET); ++ mode2 = i2c_readb(MAX3353_MODE_CONTROL_2_SET); ++ ++ TRACE_MSG5(TCD, "2. OTG Transceiver: vendor: %04x product: %04x revision: %04x mode1: %02x mode2: %02x", ++ vendor, product, revision, mode1, mode2); ++ ++#else /* defined(CONFIG_OTG_MAX3353_MX2ADS) */ ++ ++ max3353_private.vendor = i2c_readw(MAX3353_VENDOR_ID); ++ max3353_private.product = i2c_readw(MAX3353_PRODUCT_ID); ++ max3353_private.revision = i2c_readw(MAX3353_VERSION_ID); ++ mode1 = i2c_readb(MAX3353_MODE_CONTROL_1_SET); ++ mode2 = i2c_readb(MAX3353_MODE_CONTROL_2_SET); ++ ++ TRACE_MSG5(TCD, "2. OTG Transceiver: vendor: %04x product: %04x revision: %04x mode1: %02x mode2: %02x", ++ max3353_private.vendor, max3353_private.product, max3353_private.revision, mode1, mode2); ++ ++#endif /* defined(CONFIG_OTG_MAX3353_MX2ADS) */ ++ ++ for ( ; map && map->transceiver_type != unknown_transceiver; map++) { ++ CONTINUE_UNLESS ((max3353_private.vendor == map->vendor) && (max3353_private.product == map->product)); ++ TRACE_MSG1(TCD, "Found Transceiver: %s", map->name); ++ max3353_private.transceiver_map = map; ++ break; ++ } ++ UNLESS (max3353_private.transceiver_map) ++ max3353_private.transceiver_map = max3353_transceiver_map; ++ ++ TRACE_MSG0(TCD, "3. Disable All Transceiver Control Register 1 "); ++ i2c_writeb(MAX3353_OTG_CONTROL_CLR, 0xff); // clear ++ ++ TRACE_MSG0(TCD, "4. Enable D-/D+ Pulldowns in Transceiver Control Register 1 "); ++ ++ i2c_writeb(MAX3353_OTG_CONTROL_SET, MAX3353_DM_PULLDOWN | MAX3353_DP_PULLDOWN); ++ i2c_writeb(MAX3353_OTG_CONTROL_CLR, (u8) MAX3353_DM_PULLUP | MAX3353_DP_PULLUP); ++ ++ ++ TRACE_MSG0(TCD, "5. Clear latch and enable interrupts"); ++ i2c_writeb(MAX3353_INTERRUPT_LATCH_CLR, 0xff); ++ i2c_writeb(MAX3353_INTERRUPT_ENABLE_LOW_CLR, 0xeb); ++ i2c_writeb(MAX3353_INTERRUPT_ENABLE_HIGH_CLR, 0xeb); ++ ++ /* The PSW_OE enables the ADR/PSW pin for output driving either high ++ * or low depending on the address. ++ * ++ * ADR ADR_REG Address PSW_OE=0 PSW_OE=1 ++ * ++ * low 0 0x2c LOW HIGH ++ * ++ * high 1 0x2d HIGH LOW ++ * ++ * ++ * The i2c address by tying the ADR/PSW pin either high or low. ++ * Setting the PSW_OE drives the ADR/PSW into the opposite of the ++ * default wiring. ++ * ++ * On the MX21ADS the ADR/PSW pin is wired high which enables the ++ * charge pump. So enabling the PSW_OE is required to disable Vbus ++ * generation on the Charge Pump. ++ * ++ * The MAX3355E will only enable Vbus when this signal is high AND ++ * the ID signal is low. So it may be safe to leave enabled when ++ * operating as a B-device (ID floating.) ++ */ ++ ++ //i2c_writeb(MAX3353_MODE_CONTROL_2_SET, MAX3353_PSW_OE); // PSW_OE - OFFVBUS low ++ ++ ++ TRACE_MSG1(TCD, "6. tx_mode: %02x", tx_mode); ++ switch (tx_mode) { ++ case vp_vm_bidirectional: ++ break; ++ case dat_se0_unidirectional: ++ i2c_writeb(MAX3353_MODE_CONTROL_2_CLR, MAX3353_BI_DI); ++ MAX3353t_se0_bidirectional: ++ i2c_writeb(MAX3353_MODE_CONTROL_1_SET, MAX3353_DAT_SE0); ++ break; ++ } ++ TRACE_MSG0(TCD, "6. tx_mode done"); ++ ++ TRACE_MSG1(TCD, "7. spd_ctrl: %02x", spd_ctrl); ++ switch (spd_ctrl) { ++ case spd_susp_pins: ++ i2c_writeb(MAX3353_MODE_CONTROL_2_CLR, MAX3353_SPD_SUSP_CTRL); ++ break; ++ case spd_susp_reg: ++ i2c_writeb(MAX3353_MODE_CONTROL_2_SET, MAX3353_SPD_SUSP_CTRL); ++ break; ++ } ++ ++ //i2c_writeb(MAX3353_MODE_CONTROL_2_SET, MAX3353_PSW_OE); // PSW_OE - OFFVBUS low ++ ++ TRACE_MSG0(TCD, "7. spd_ctrl done"); ++ ++ TRACE_MSG1(TCD, "8. transceiver_type: %02x", max3353_private.transceiver_map); ++ ++ TRACE_MSG1(TCD, "8. transceiver_type: %02x", max3353_private.transceiver_map->transceiver_type); ++ switch (max3353_private.transceiver_map->transceiver_type) { ++ case max3353: ++ break; ++ case max3301e: ++ i2c_writeb(MAX3301E_SPECIAL_FUNCTION_1_SET, MAX3301E_SESS_END); ++ break; ++ case unknown_transceiver: ++ break; ++ } ++ TRACE_MSG0(TCD, "8. transceiver_type done"); ++} ++ +diff -uNr linux/drivers/no-otg/ocd/max3353e/max3353.h linux/drivers/otg/ocd/max3353e/max3353.h +--- linux/drivers/no-otg/ocd/max3353e/max3353.h 1970-01-01 01:00:00.000000000 +0100 ++++ linux/drivers/otg/ocd/max3353e/max3353.h 2006-09-01 21:41:32.000000000 +0200 +@@ -0,0 +1,132 @@ ++/* ++ * otg/ocd/max3353/max3353.h -- USB Transceiver Controller driver ++ * @(#) balden@belcarra.com|otg/ocd/max3353e/max3353.h|20060831021117|56821 ++ * ++ * Copyright (c) 2004-2005 Belcarra ++ * Copyright (c) 2005-2006 Belcarra Technologies 2005 Corp ++ * ++ * By: ++ * Stuart Lynne <sl@lbelcarra.com>, ++ * Bruce Balden <balden@belcarra.com> ++ * ++ */ ++ ++ ++/*! ++ * @file otg/ocd/max3353e/max3353.h ++ * @brief Private structures and defines for MAX3353 Transciever Controller Driver. ++ * ++ * This file contains the private defines and structures for the MAX3353 Transceiver ++ * Driver. ++ * ++ * @ingroup MAX3353TCD ++ */ ++ ++ ++#ifdef CONFIG_XXXXX ++#define MAX3353_GPIO (2) ++#define TCD_MAX3353_I2C_ADDR MAX3353_I2C_ADDR_HIGH; ++#endif ++ ++#define MAX3353_NAME "max3353" ++ ++#define MAX3353_LOC_CONN 0x01 ++ ++#define MAX3353_INT_SRC 0xeb ++ ++//#define MAX3353_ALL_INT_SRC 0xff ++//#define MAX3353_NO_SE1_INT 0xeb ++//#define MAX3353_NO_SE1_INT 0xfb /* do not pay attention to DP_HI */ ++ ++typedef enum otg_transceiver { ++ unknown_transceiver, ++ max3353, ++ max3301e ++} otg_transceiver_t; ++ ++struct otg_transceiver_map { ++ otg_transceiver_t transceiver_type; ++ u16 vendor; ++ u16 product; ++ u16 revision; ++ char *name; ++}; ++ ++struct max3353_private { ++ WORK_STRUCT bh; ++ u16 vendor; ++ u16 product; ++ u16 revision; ++ u32 flags; ++ u8 int_src; ++ struct otg_transceiver_map *transceiver_map; ++}; ++ ++/*! tx_mode ++ * This defines the various ways that the MAX3353 can be ++ * wired into the host USB. ++ */ ++typedef enum tx_mode { ++ dat_se0_bidirectional, /*!< 3-Wire */ ++ vp_vm_bidirectional, /*!< 4-Wire */ ++ dat_se0_unidirectional /*!< 6-Wire */ ++} tx_mode_t; ++ ++/*! spd_ctrl ++ * This defines how the speed and suspend pins are controlled ++ * for this host. ++ */ ++typedef enum spd_ctrl { ++ spd_susp_pins, /*!< controlled by SPEED and SUSPEND pins */ ++ spd_susp_reg, /*!< controled by SPEED_REG and SUSPEND_REG in Mode Control 1 register */ ++} spd_ctrl_t; ++ ++ ++extern struct tcd_ops tcd_ops; ++extern struct max3353_private max3353_private; ++extern struct tcd_instance *tcd_instance; ++ ++/* max3353.c */ ++extern int max3353_mod_init(void); ++extern void max3353_mod_exit(void); ++extern void max3353_chrg_vbus(struct otg_instance *, u8 ); ++extern void max3353_drv_vbus(struct otg_instance *, u8 ); ++extern void max3353_dischrg_vbus(struct otg_instance *, u8 ); ++extern void max3353_dp_pullup_func(struct otg_instance *, u8 ); ++extern void max3353_dm_pullup_func(struct otg_instance *, u8 ); ++extern void max3353_dm_det_func(struct otg_instance *, u8 ); ++extern void max3353_dp_det_func(struct otg_instance *, u8 ); ++extern void max3353_bdis_acon_func(struct otg_instance *otg, u8 flag); ++extern void max3353_id_pulldown_func(struct otg_instance *otg, u8 flag); ++extern void max3353_audio_func(struct otg_instance *otg, u8 flag); ++extern void max3353_uart_func(struct otg_instance *otg, u8 flag); ++extern void max3353_mono_func(struct otg_instance *otg, u8 flag); ++ ++extern void max3353_otg_timer(struct otg_instance *, u8 ); ++extern void max3353_bh(void *); ++extern int max3353_id (struct otg_instance *); ++extern int max3353_vbus (struct otg_instance *); ++extern void max3353_configure(tx_mode_t, spd_ctrl_t ); ++ ++/* thread */ ++extern int max3353_thread_init (void (bh_proc)(void *)); ++extern void max3353_thread_exit (wait_queue_head_t *); ++extern void max3353_thread_wakeup(int enabled, int first); ++ ++ ++ ++/* i2c-xxx.c */ ++int i2c_configure(char *name, int addr); ++extern void i2c_close(void); ++extern u8 i2c_readb(u8 ); ++extern u16 i2c_readw(u8 ); ++extern u32 i2c_readl(u8 ); ++extern int i2c_writeb(u8 , u8 ); ++ ++ ++/* ********************************************************************************************* */ ++#define TRACE_I2CB(t,r) TRACE_MSG3(t, "%-40s[%02x] %02x", #r, r, i2c_readb(r)) ++#define TRACE_I2CW(t,r) TRACE_MSG3(t, "%-40s[%02x] %04x", #r, r, i2c_readw(r)) ++#define TRACE_GPIO(t,b,r) TRACE_MSG2(t, "%-40s %04x", #r, readw(b + r)) ++#define TRACE_REGL(t,r) TRACE_MSG2(t, "%-40s %08x", #r, readl(r)) ++ +diff -uNr linux/drivers/no-otg/ocd/max3353e/max3353e-procfs.c linux/drivers/otg/ocd/max3353e/max3353e-procfs.c +--- linux/drivers/no-otg/ocd/max3353e/max3353e-procfs.c 1970-01-01 01:00:00.000000000 +0100 ++++ linux/drivers/otg/ocd/max3353e/max3353e-procfs.c 2006-09-01 21:41:32.000000000 +0200 +@@ -0,0 +1,303 @@ ++/* ++ * otg/ocd/max3353/max3353-procfs.c - USB Device Core Layer ++ * @(#) balden@belcarra.com|otg/ocd/max3353e/max3353e-procfs.c|20060831021117|57336 ++ * ++ * Copyright (c) 2004-2005 Belcarra ++ * Copyright (c) 2005-2006 Belcarra Technologies 2005 Corp ++ * ++ * By: ++ * Stuart Lynne <sl@belcarra.com>, ++ * Shahrad Payandeh <sp@belcarra.com>, ++ * Bruce Balden <balden@belcarra.com> ++ * ++ * ++ */ ++/*! ++ * @file otg/ocd/max3353e/max3353e-procfs.c ++ * @brief Implement /proc/max3353 to dump MAX3353 registers. ++ * ++ * ++ * @ingroup MAX3353TCD ++ */ ++ ++ ++#include <otg/pcd-include.h> ++#include "otghw/max3353-hardware.h" ++#include "max3353.h" ++ ++#ifdef CONFIG_ARCH_MX2ADS ++#include <asm/arch/mx2.h> ++#define MX2_OTG_XCVR_DEVAD 0x18 ++#define MX2_SEQ_OP_REG 0x19 ++#define MX2_SEQ_RD_STARTAD 0x1a ++#define MX2_I2C_OP_CTRL_REG 0x1b ++#define MX2_SCLK_TO_SCL_HPER 0x1e ++#define MX2_I2C_INTERRUPT_AND_CTRL 0x1f ++ ++#define OTG_BASE_ADDR 0x10024000 ++//#define OTG_I2C_BASE (OTG_BASE_ADDR+0x100) ++ ++#endif /* CONFIG_ARCH_MX2ADS */ ++ ++#ifdef CONFIG_OTG_MAX3353_PROCFS ++/* Proc Filesystem *************************************************************************** */ ++ ++extern struct max3353_private max3353_private; ++ ++#define MAX_HISTORY 6 ++struct reg_list { ++ u8 reg; ++ u8 size; ++ char *name; ++ u32 values[MAX_HISTORY]; ++}; ++ ++#define REG(r, s) {r, s, #r, } ++ ++struct reg_list max3353_prod_list[] = { ++ REG(MAX3353_VENDOR_ID, 2), ++ REG(MAX3353_PRODUCT_ID, 2), ++ REG(MAX3353_VERSION_ID, 2), ++ { 0, 1, NULL,}, ++}; ++struct reg_list max3353_reg_list[] = { ++ REG(MAX3353_OTG_CONTROL_SET, 1), ++ REG(MAX3353_INTERRUPT_SOURCE, 1), ++ REG(MAX3353_INTERRUPT_LATCH_SET, 1), ++ REG(MAX3353_INTERRUPT_ENABLE_LOW_SET, 1), ++ REG(MAX3353_INTERRUPT_ENABLE_HIGH_SET, 1), ++ REG(MAX3353_MODE_CONTROL_1_SET, 1), ++ { 0, 1, NULL,}, ++}; ++struct reg_list max3353_spec_list[] = { ++ REG(MAX3353_MODE_CONTROL_2_SET, 1), ++ REG(MAX3353_OTG_STATUS, 1), ++ { 0, 1, NULL,}, ++}; ++struct reg_list max3301e_spec_list[] = { ++ REG(MAX3301E_SPECIAL_FUNCTION_1_SET, 1), ++ REG(MAX3301E_SPECIAL_FUNCTION_2_SET, 1), ++ { 0, 1, NULL,}, ++}; ++#ifdef CONFIG_ARCH_MX2ADS ++struct reg_list mx21_spec_list[] = { ++ REG(MX2_OTG_XCVR_DEVAD, 1), ++ REG(MX2_SEQ_OP_REG, 1), ++ REG(MX2_SEQ_RD_STARTAD, 1), ++ REG(MX2_I2C_OP_CTRL_REG, 1), ++ REG(MX2_SCLK_TO_SCL_HPER, 1), ++ REG(MX2_I2C_INTERRUPT_AND_CTRL, 1), ++ { 0, 1, NULL,}, ++}; ++#endif /* CONFIG_ARCH_MX2ADS */ ++ ++void max3353_update(struct reg_list *list) ++{ ++ for (; list && list->name; list++) { ++ memmove(list->values + 1, list->values, sizeof(list->values) - sizeof(u32)); ++ switch(list->size) { ++ case 1: ++ list->values[0] = i2c_readb(list->reg); ++ break; ++ case 2: ++ list->values[0] = i2c_readw(list->reg); ++ break; ++ case 4: ++ list->values[0] = i2c_readl(list->reg); ++ break; ++ } ++ } ++} ++ ++#ifdef CONFIG_ARCH_MX2ADS ++static u8 __inline__ mx2_rb(u32 port) ++{ ++ return *(volatile u8 *) (MX2_IO_ADDRESS(port + OTG_I2C_BASE)); ++} ++ ++ ++void mx21_update(struct reg_list *list) ++{ ++ for (; list && list->name; list++) { ++ memmove(list->values + 1, list->values, sizeof(list->values) - sizeof(u32)); ++ list->values[0] = mx2_rb(list->reg); ++ } ++} ++ ++#endif /* CONFIG_ARCH_MX2ADS */ ++ ++void max3353_update_all(void) ++{ ++ max3353_update(max3353_reg_list); ++ switch (max3353_private.transceiver_map->transceiver_type) { ++ case max3353: ++ max3353_update(max3353_spec_list); ++ break; ++ case max3301e: ++ max3353_update(max3301e_spec_list); ++ break; ++ default: ++ break; ++ } ++#ifdef CONFIG_ARCH_MX2ADS ++ mx21_update(mx21_spec_list); ++#endif /* CONFIG_ARCH_MX2ADS */ ++} ++ ++ ++ ++/* ++ * 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); ++} ++ ++int max3353_dump(char *buf, char *name, char *fmt, struct reg_list *reg) ++{ ++ int len = 0, i; ++ len += sprintf (buf + len, "%-20s %-34s [%03x]: ", name, reg->name, reg->reg); ++ len += sprintf (buf + len, fmt, reg->values[0]); ++ for (i = 1; i < MAX_HISTORY; i++) ++ if (reg->values[i - 1] == reg->values[i]) ++ len += sprintf (buf + len, " "); ++ else ++ len += sprintf (buf + len, fmt, reg->values[i]); ++ len += sprintf (buf + len, "\n"); ++ return len; ++} ++ ++int max3353_dump_list(char * buf, char *name, struct reg_list *list) ++{ ++ int len = 0; ++ for (; list && list->name; list++) ++ switch(list->size) { ++ case 1: len += max3353_dump(buf + len, name, " %02x", list); break; ++ case 2: len += max3353_dump(buf + len, name, " %04x", list); break; ++ case 4: len += max3353_dump(buf + len, name, " %08x", list); break; ++ } ++ return len; ++} ++ ++int max3353_dump_all(char *buf) ++{ ++ int len = 0; ++ len += max3353_dump_list(buf + len, "MAX3353 Standard", max3353_reg_list); ++ switch (max3353_private.transceiver_map->transceiver_type) { ++ case max3353: ++ len += max3353_dump_list(buf + len, "MAX3353 Extra", max3353_spec_list); ++ break; ++ case max3301e: ++ len += max3353_dump_list(buf + len, "MAX3301E Extra", max3301e_spec_list); ++ break; ++ default: ++ break; ++ } ++#ifdef CONFIG_ARCH_MX2ADS ++ len += max3353_dump_list(buf + len, "MX21 ADS Extra", mx21_spec_list); ++#endif /* CONFIG_ARCH_MX2ADS */ ++ return len; ++} ++ ++ ++ ++/*! ++ * max3353_device_proc_read - implement proc file system read. ++ * ++ * 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 max3353_device_proc_read_functions (struct file *file, char *buf, size_t count, loff_t * pos) ++{ ++ unsigned long page; ++ int len = 0; ++ int index; ++ int i; ++ u32 r; ++ ++ int config_size; ++ ++ // get a page, max 4095 bytes of data... ++ RETURN_EINVAL_UNLESS ((page = get_free_page (GFP_KERNEL))); ++ ++ len = 0; ++ index = (*pos)++; ++ ++ switch(index) { ++ case 0: ++ len += sprintf ((char *) page + len, "MAX3353 Transceiver Registers\n"); ++ ++ max3353_update_all(); ++ len += sprintf ((char *) page + len , "Vendor: %04x Product: %04x Revision: %04x %s\n", ++ max3353_private.vendor, max3353_private.product, ++ max3353_private.revision, max3353_private.transceiver_map->name ++ ); ++ ++ len += max3353_dump_all((char *) page + len); ++ len += sprintf ((char *) page + len, "\n"); ++ ++ break; ++ ++ case 1: ++ break; ++ ++ } ++ ++ if (len > count) { ++ len = -EINVAL; ++ } ++ else if ((len > 0) && copy_to_user (buf, (char *) page, len)) { ++ len = -EFAULT; ++ } ++ else { ++ } ++ free_page (page); ++ return len; ++} ++ ++static struct file_operations max3353_device_proc_operations_functions = { ++ read:max3353_device_proc_read_functions, ++}; ++ ++/* Module init ******************************************************************************* */ ++ ++static int max3353_procfs_init (void) ++{ ++ struct proc_dir_entry *p; ++ ++ // create proc filesystem entries ++ if ((p = create_proc_entry ("max3353", 0, 0)) == NULL) ++ return -ENOMEM; ++ ++ p->proc_fops = &max3353_device_proc_operations_functions; ++ ++ max3353_update_all(); ++ ++ return 0; ++} ++static void max3353_procfs_exit (void) ++{ ++ remove_proc_entry ("max3353", NULL); ++} ++#else ++static int max3353_procfs_init (void) { return 0; ++} ++static void max3353_procfs_exit (void) { } ++#endif +diff -uNr linux/drivers/no-otg/ocd/max3353e/max3353e.c linux/drivers/otg/ocd/max3353e/max3353e.c +--- linux/drivers/no-otg/ocd/max3353e/max3353e.c 1970-01-01 01:00:00.000000000 +0100 ++++ linux/drivers/otg/ocd/max3353e/max3353e.c 2006-09-01 21:41:32.000000000 +0200 +@@ -0,0 +1,657 @@ ++/* ++ * otg/ocd/max3353/max3353.c -- USB Transceiver Controller driver ++ * @(#) balden@belcarra.com|otg/ocd/max3353e/max3353e.c|20060831021117|08740 ++ * ++ * Copyright (c) 2004-2005 Belcarra ++ * Copyright (c) 2005-2006 Belcarra Technologies 2005 Corp ++ * ++ * By: ++ * Stuart Lynne <sl@lbelcarra.com>, ++ * Shahrad Payandeh <sp@belcarra.com>, ++ * Bruce Balden <balden@belcarra.com> ++ * ++ */ ++/*! ++ * @file otg/ocd/max3353e/max3353e.c ++ * @brief MAX3353 OTG Transceiver Driver. ++ * ++ * This is the generic MAX3353 TCD core support. ++ * ++ * Notes: ++ * ++ * 1. The MAX3353 can control the speed and suspend directly, would it be ++ * appropriate to allow state machine to control this. ++ * ++ * 2. The MAX3353 has auto connect feature, can this be used without change ++ * to state machine. ++ * ++ * 3. The MAX3353 can control the ADR/PSW pin to enable / disable external ++ * charge pump. ++ * ++ * @ingroup MAX3353TCD ++ */ ++ ++#include <otg/otg-compat.h> ++ ++#include <asm/irq.h> ++#include <asm/system.h> ++#include <asm/io.h> ++#include <asm/au1000.h> ++#include <linux/i2c.h> ++ ++#include <otg/usbp-chap9.h> ++#include <otg/usbp-func.h> ++#include <otg/usbp-bus.h> ++#include <otg/otg-trace.h> ++#include <otg/otg-api.h> ++#include <otg/otg-hcd.h> ++#include <otg/otg-tcd.h> ++#include <otg/otg-ocd.h> ++#include <otg/otg-pcd.h> ++ ++#include <otghw/max3353e-hardware.h> ++#include "max3353e.h" ++ ++ ++struct otg_transceiver_map max3353_transceiver_map[] = { ++ { max3353, 0x4cc, 0x1301, 0x00, "Phillips MAX3353", }, ++ { max3301e, 0x6a0b, 0x0133, 0x00, "Maxim MAX3301E", }, ++ { 0, 0, 0, 0, "Unknown Transceiver", }, ++}; ++ ++struct max3353_private max3353_private; ++ ++ ++ ++/* ********************************************************************************************* */ ++#define TRACE_I2CB(t,r) TRACE_MSG3(t, "%-40s[%02x] %02x", #r, r, i2c_readb(r)) ++#define TRACE_I2CW(t,r) TRACE_MSG3(t, "%-40s[%02x] %04x", #r, r, i2c_readw(r)) ++#define TRACE_GPIO(t,b,r) TRACE_MSG2(t, "%-40s %04x", #r, readw(b + r)) ++#define TRACE_REGL(t,r) TRACE_MSG2(t, "%-40s %08x", #r, readl(r)) ++ ++ ++ ++/*! max3353_int_src - update interrupt source test mask ++ * ++ * This sets the current mask and updates the interrupt registers to match. ++ */ ++void max3353_int_src(u8 int_src) ++{ ++ TRACE_MSG1(TCD, "setting int_src %02x", int_src); ++ max3353_private.int_src = int_src; ++ i2c_writeb(MAX3353E_INTERRUPT_MASK, int_src); ++ i2c_writeb(MAX3353E_INTERRUPT_EDGE, 3); //detect on positive edge ++ i2c_writeb(MAX3353E_INTERRUPT_LATCH, 0); ++} ++ ++/*! max3353_int_src_set - add to the interrupt source mask ++ * ++ */ ++void max3353_int_src_set(u8 int_src) ++{ ++ max3353_int_src(int_src |= max3353_private.int_src); ++} ++ ++/*! max3353_int_src_clr - remove from the interrup source mask ++ */ ++void max3353_int_src_clr(u8 int_src) ++{ ++ max3353_int_src(int_src = max3353_private.int_src & ~int_src); ++} ++ ++/* ********************************************************************************************* */ ++/* ********************************************************************************************* */ ++ ++ ++ ++typedef struct max3353_test { ++ u32 input; ++ char *name; ++ u8 mask; ++} max3353_test_t; ++ ++#define OV(i, m) {i, #i, m} ++#define MAX3353E_B_SESS_END 1<<4 ++#define MAX3353E_B_SESS_VLD 1<<6 ++#define MAX3353E_A_SESS_VLD 1<<5 ++ ++ ++struct max3353_test max3353_int_src_tests[5] = { ++ OV(VBUS_VLD, MAX3353E_VBUS_VALID), ++ OV(ID_GND, MAX3353E_ID_GND), ++ OV(ID_FLOAT, MAX3353E_ID_FLOAT), ++ OV(BDIS_ACON, MAX3353E_A_HNP), ++ {0, NULL, 0}, ++}; ++ ++struct max3353_test max3353_otg_status_tests[4] = { ++ OV(B_SESS_END, MAX3353E_B_SESS_END), ++ OV(B_SESS_VLD, MAX3353E_B_SESS_VLD), ++ OV(A_SESS_VLD, MAX3353E_A_SESS_VLD), ++ {0, NULL, 0}, ++}; ++ ++u64 max3353_event(struct otg_instance *otg, struct max3353_test *tests, int val, char *msg) ++{ ++ int i; ++ u64 inputs = 0; ++ TRACE_MSG2(TCD, "%02x %s", val, msg); ++ for (i = 0; tests[i].mask; i++) { ++ u64 input = tests[i].input; ++ u8 mask = tests[i].mask; ++ inputs |= (val & mask) ? input : _NOT(input); ++ TRACE_MSG5(TCD, "%d - %s%s %02x %08x", i, tests[i].name, (val & mask) ? "" : "/", tests[i].mask, tests[i].input); ++ } ++ return inputs; ++} ++ ++/*! max3353_event_set_irq ++ */ ++void max3353_event_set_irq(struct otg_instance *otg, int changed, int flag, u32 input, otg_tag_t tag, char *mset, char *mreset) ++{ ++ TRACE_MSG5(TCD, "changed: %x flag: %x input: %08x %s %s", changed, flag, input, ++ flag ? mset: mreset, ++ changed ? "": "(ignored)"); ++ ++ otg_event_set_irq(otg, changed, flag, input, tag, flag ? mset : mreset); ++} ++ ++/*! max3353_update - called from mx2_i2c_xcvr_bh to find changes in the max3353 Interrupt Source Register ++ */ ++u8 max3353_update_int_src(struct otg_instance *otg, u8 int_src, u8 changed) ++{ ++ UNLESS (changed) return int_src; ++ TRACE_MSG2(TCD, "CHANGED INTERRUPT_SOURCE: %02x (%02x) ", int_src, changed); ++ ++ /* Core events ++ */ ++ max3353_event_set_irq(otg, MAX3353E_ID_GND & changed, MAX3353E_ID_GND & int_src, ++ ID_GND, TCD, "ID_GND", "ID_GND/"); ++ max3353_event_set_irq(otg, MAX3353E_ID_FLOAT & changed, MAX3353E_ID_FLOAT & int_src, ++ ID_FLOAT, TCD, "ID_FLOAT", "ID_FLOAT/"); ++ max3353_event_set_irq(otg, MAX3353E_VBUS_VALID & changed, MAX3353E_VBUS_VALID & int_src, ++ VBUS_VLD, TCD, "VBUS_VLD", "VBUS_VLD/"); ++ max3353_event_set_irq(otg, MAX3353E_SESSION_END & changed, MAX3353E_SESSION_END & int_src, ++ A_SESS_VLD, TCD, "A_SESS_VLD", "A_SESS_VLD/"); ++ ++ ++ return int_src; ++} ++ ++/*! max3353_update - called from mx2_i2c_xcvr_bh to find changes in the max3353 OTG Status Register ++ */ ++u8 max3353_update_otg_status(struct otg_instance *otg, u8 otg_status, u8 changed) ++{ ++ UNLESS (changed) return otg_status; ++ TRACE_MSG2(TCD, "CHANGED OTG_STATUS_REG: %02x (%02x) ", otg_status, changed); ++ ++ max3353_event_set_irq(otg, MAX3353E_SESSION_VALID_EN & changed, MAX3353E_SESSION_VALID_EN & otg_status, ++ B_SESS_VLD, TCD, "B_SESS_VLD", "B_SESS_VLD/"); ++ max3353_event_set_irq(otg, MAX3353E_SESSION_END & changed, MAX3353E_SESSION_END & otg_status, ++ B_SESS_END, TCD, "B_SESS_END", "B_SESS_END/"); ++ ++ return otg_status; ++} ++ ++ ++int max3353_bh_first; ++ ++/*! max3353_bh ++ */ ++void max3353_bh(void *arg) ++{ ++ ++ u64 inputs = 0; ++ u8 int_lat,status_reg,otg_reg; ++ u8 reg1,reg2; ++ ++ au_writel(0x40000000, IC1_MASKCLR); //disable interrupt ++ au_writel(0x40000000, IC1_FALLINGCLR); ++ au_writel(0x40000000, IC1_RISINGCLR); ++ TRACE_MSG0(TCD, "--MAX3353 ISR start"); ++// printk(KERN_INFO"ISR Start .......\n"); ++ ++ /* read the interrupt latch and and clear latch and update otg ++ */ ++ if ((int_lat = i2c_readb(MAX3353E_INTERRUPT_LATCH))) { ++ TRACE_MSG1(TCD, "INT LAT: %02x", int_lat); ++ i2c_writeb(MAX3353E_INTERRUPT_LATCH, 0); ++ } ++ ++ status_reg = i2c_readb(MAX3353E_STATUS_REGISTER); ++ inputs |= max3353_event(tcd_instance->otg, max3353_int_src_tests, status_reg, "MAX3353 STAT SRC"); ++ TRACE_MSG1(TCD, "inputs after applying status register %02x\n", inputs); ++ ++ ++ otg_reg = 0; ++ ++ if (status_reg & MAX3353E_SESSION_VALID_EN) { //1.4,,4.5 ++ TRACE_MSG0(TCD, "Setting B_SESS_VLD"); ++ otg_reg |= MAX3353E_B_SESS_VLD; ++ } ++ if (status_reg & MAX3353E_SESSION_END) { ++ TRACE_MSG0(TCD, "Setting B_SESS_END"); ++ otg_reg |= MAX3353E_B_SESS_END; ++ } ++ if (!(status_reg & MAX3353E_SESSION_END)) { //0.8<<1.4 ++ if (!(status_reg & MAX3353E_SESSION_VALID_EN)){ ++ TRACE_MSG0(TCD, "Setting A_SESS_VLD"); ++ otg_reg |= MAX3353E_A_SESS_VLD; ++ } ++ } ++ ++ ++ ++ inputs |= max3353_event(tcd_instance->otg, max3353_otg_status_tests, otg_reg, "MAX3353 OTG SRC"); ++ TRACE_MSG1(TCD, "inputs after applying otg register %02x\n", inputs); ++ ++ ++ ++ TRACE_MSG0(TCD, "--MAX3353 ISR end"); ++ if (inputs) otg_event(tcd_instance->otg, inputs, TCD, "MAX3353"); ++ au_writel(0x40000000, IC1_MASKSET); //enable interrupt ++} ++ ++ ++ ++/* ********************************************************************************************** */ ++/*! max3353_vbus - Do we have Vbus (cable attached?) ++ * Return non-zero if Vbus is detected. ++ * ++ */ ++int max3353_vbus (struct otg_instance *otg) ++{ ++ return 0; ++} ++ ++/*! max3353_id - Do we have Vbus (cable attached?) ++ * Return non-zero if Vbus is detected. ++ * ++ */ ++int max3353_idvbus (struct otg_instance *otg) ++{ ++ return 0; ++} ++ ++ ++/* ********************************************************************************************* */ ++ ++/*! max3353_chrg_vbus - used to enable or disable B-device Vbus charging ++ */ ++void max3353_chrg_vbus(struct otg_instance *otg, u8 flag) ++{ ++ u8 reg2; ++ ++ TRACE_MSG0(TCD, "--"); ++ reg2 = i2c_readb(MAX3353E_CONTROL_REGISTER_2); ++ TRACE_MSG1(TCD, "Control 2 - Begin = %02x", reg2); ++ switch (flag) { ++ case SET: ++ TRACE_MSG0(TCD, "CHRG_VBUS_SET"); ++ reg2 |= MAX3353E_VBUS_CHG2; ++ i2c_writeb(MAX3353E_CONTROL_REGISTER_2, (u8) reg2); ++ break; ++ case RESET: ++ TRACE_MSG0(TCD, "CHRG_VBUS_RESET"); ++ reg2 &= (~MAX3353E_VBUS_CHG2); ++ i2c_writeb(MAX3353E_CONTROL_REGISTER_2, (u8) reg2); ++ break; ++ case PULSE: ++ break; ++ } ++ TRACE_MSG1(TCD, "Control 2 - End = %02x", reg2); ++} ++ ++/*! max3353_drv_vbus - used to enable or disable A-device driving Vbus ++ */ ++void max3353_drv_vbus(struct otg_instance *otg, u8 flag) ++{ ++u8 reg2; ++ ++ TRACE_MSG0(TCD, "--"); ++ reg2 = i2c_readb(MAX3353E_CONTROL_REGISTER_2); ++ TRACE_MSG1(TCD, "Control 2 - Begin = %02x", reg2); ++ switch (flag) { ++ case SET: ++ TRACE_MSG0(TCD, "DRV_VBUS_SET"); ++ reg2 |= MAX3353E_VBUS_DRV; ++ i2c_writeb(MAX3353E_CONTROL_REGISTER_2, (u8) reg2); ++ break; ++ case RESET: ++ TRACE_MSG0(TCD, "DRV_VBUS_RESET"); ++ reg2 &= (~MAX3353E_VBUS_DRV); ++ i2c_writeb(MAX3353E_CONTROL_REGISTER_2, (u8) reg2); ++ break; ++ case PULSE: ++ break; ++ } ++ TRACE_MSG1(TCD, "Control 2 - End = %02x", reg2); ++} ++ ++/*! max3353_dischrg_vbus - used to enable Vbus discharge ++ */ ++void max3353_dischrg_vbus(struct otg_instance *otg, u8 flag) ++{ ++ u8 reg2; ++ ++ TRACE_MSG0(TCD, "--"); ++ reg2 = i2c_readb(MAX3353E_CONTROL_REGISTER_2); ++ TRACE_MSG1(TCD, "Control 2 - Begin = %02x", reg2); ++ switch (flag) { ++ case SET: ++ TRACE_MSG0(TCD, "DISCHG_VBUS_SET"); ++ reg2 |= MAX3353E_VBUS_DISCHG; ++ i2c_writeb(MAX3353E_CONTROL_REGISTER_2, (u8) reg2); ++ break; ++ case RESET: ++ TRACE_MSG0(TCD, "DISCHG_VBUS_RESET"); ++ reg2 &= (~MAX3353E_VBUS_DISCHG); ++ i2c_writeb(MAX3353E_CONTROL_REGISTER_2, (u8) reg2); ++ break; ++ case PULSE: ++ break; ++ } ++ TRACE_MSG1(TCD, "Control 2 - End = %02x", reg2); ++} ++ ++/*! max3353_dp_pullup_func - used to enable or disable peripheral connecting to bus ++ * ++ * C.f. 5.1.6, 5.1.7, 5.2.4 and 5.2.5 ++ * ++ * host peripheral ++ * d+ pull-up clr set ++ * d+ pull-down set clr ++ * ++ * d- pull-up clr clr ++ * d- pull-down set set ++ * ++ */ ++void max3353_dp_pullup_func(struct otg_instance *otg, u8 flag) ++{ ++ struct tcd_instance *tcd = (struct tcd_instance *)otg->tcd; ++ TRACE_MSG0(TCD, "-- "); ++ switch (flag) { ++ case SET: ++ printk(KERN_INFO"%s: --\n", __FUNCTION__); ++ max3353_private.flags = i2c_readb(MAX3353E_CONTROL_REGISTER_1); ++ TRACE_MSG0(TCD, "Clr DP PULLDOWN"); ++ max3353_private.flags &= (~MAX3353E_DP_PULLDOWN); ++ TRACE_MSG0(TCD, "Set DP PULLUP"); ++ max3353_private.flags |= (MAX3353E_DP_PULLUP); ++ i2c_writeb(MAX3353E_CONTROL_REGISTER_1, (u8) max3353_private.flags); ++ break; ++ ++ case RESET: ++ max3353_private.flags = i2c_readb(MAX3353E_CONTROL_REGISTER_1); ++ TRACE_MSG0(TCD, "Clr DP PULLUP"); ++ max3353_private.flags &= (~MAX3353E_DP_PULLUP); ++ TRACE_MSG0(TCD, "Set DP PULLDOWN"); ++ max3353_private.flags |= (MAX3353E_DP_PULLDOWN); ++ i2c_writeb(MAX3353E_CONTROL_REGISTER_1, (u8) max3353_private.flags); ++ break; ++ ++ case PULSE: ++ TRACE_MSG0(TCD, "MAX3353_LOC_CONN PULSE"); ++ break; ++ } ++ int temp = 0; ++ temp = i2c_readb(MAX3353E_CONTROL_REGISTER_1); ++ TRACE_MSG1(TCD, "Control Register 1: %02x", temp); ++ ++} ++ ++/*! max3353_dm_pullup_func - used to enable or disable peripheral connecting to bus ++ * ++ */ ++void max3353_dm_pullup_func(struct otg_instance *otg, u8 flag) ++{ ++ struct tcd_instance *tcd = (struct tcd_instance *)otg->tcd; ++ TRACE_MSG0(TCD, "--"); ++ switch (flag) { ++ case SET: ++ max3353_private.flags = i2c_readb(MAX3353E_CONTROL_REGISTER_1); ++ TRACE_MSG0(TCD, "Clr DM PULLDOWN"); ++ max3353_private.flags &= (~MAX3353E_DM_PULLDOWN); ++ i2c_writeb(MAX3353E_CONTROL_REGISTER_1, (u8) max3353_private.flags); ++ TRACE_MSG0(TCD, "Set DM PULLUP"); ++ max3353_private.flags |= (MAX3353E_DM_PULLUP); ++ i2c_writeb(MAX3353E_CONTROL_REGISTER_1, (u8) max3353_private.flags); ++ break; ++ ++ case RESET: ++ max3353_private.flags = i2c_readb(MAX3353E_CONTROL_REGISTER_1); ++ TRACE_MSG0(TCD, "Clr DM PULLUP"); ++ max3353_private.flags &= (~MAX3353E_DM_PULLUP); ++ i2c_writeb(MAX3353E_CONTROL_REGISTER_1, (u8) max3353_private.flags); ++ TRACE_MSG0(TCD, "Set DM PULLDOWN"); ++ max3353_private.flags |= (MAX3353E_DM_PULLDOWN); ++ i2c_writeb(MAX3353E_CONTROL_REGISTER_1, (u8) max3353_private.flags); ++ break; ++ ++ case PULSE: ++ TRACE_MSG0(TCD, "MAX3353_LOC_CONN PULSE"); ++ break; ++ } ++ int temp = 0; ++ temp = i2c_readb(MAX3353E_CONTROL_REGISTER_1); ++ TRACE_MSG1(TCD, "Control Register 1 %02x", temp); ++} ++ ++/*! max3353_dm_det_func - used to enable or disable D- detect ++ * ++ */ ++void max3353_dm_det_func(struct otg_instance *otg, u8 flag) ++{ ++ return; ++} ++ ++/*! max3353_dp_det_func - used to enable or disable D+ detect ++ * ++ */ ++void max3353_dp_det_func(struct otg_instance *otg, u8 flag) ++{ ++ return; ++} ++ ++/*! max3353_bdis_acon_func - used to enable or disable auto a-connect ++ * ++ */ ++void max3353_bdis_acon_func(struct otg_instance *otg, u8 flag) ++{ ++ return; ++} ++ ++/*! max3353_id_pulldown_func - used to enable or disable ID pulldown ++ * ++ */ ++void max3353_id_pulldown_func(struct otg_instance *otg, u8 flag) ++{ ++ return; ++} ++ ++/*! max3353_audio_func - used to enable or disable Carkit Interrupt ++ * ++ */ ++void max3353_audio_func(struct otg_instance *otg, u8 flag) ++{ ++ return; ++} ++ ++/*! max3353_uart_func - used to enable or disable transparent uart mode ++ * ++ */ ++void max3353_uart_func(struct otg_instance *otg, u8 flag) ++{ ++ return; ++} ++ ++/*! max3353_mono_func - used to enable or disable mono audio connection ++ * ++ */ ++void max3353_mono_func(struct otg_instance *otg, u8 flag) ++{ ++ return; ++} ++ ++ ++/* ********************************************************************************************* */ ++extern int max3353_procfs_init (void); ++extern void max3353_procfs_exit (void); ++ ++/*! max3353_mod_init ++ */ ++int max3353_mod_init(void) ++{ ++ int ret; ++ ++ TRACE_MSG0(TCD, "Initilize"); ++ /* for interrupt bottom half handler ++ */ ++ PREPARE_WORK_ITEM(max3353_private.bh, &max3353_bh, NULL); ++ ++ /* setup max3353, enable interrupts, clear latch ++ * XXX when and how do we do DM_HI and DP_HI, when ID is present? ++ */ ++ max3353_int_src_set(0x1f); ++ //max3353_int_src(MAX3353_NO_SE1_INT); ++ /*Turn on the max3353 and discharge the bus*/ ++ i2c_writeb(MAX3353E_CONTROL_REGISTER_2, 0x0); ++ i2c_writeb(MAX3353E_CONTROL_REGISTER_1, 0x0); ++ int status = i2c_readb(MAX3353E_STATUS_REGISTER); ++ if ((status & 0x1) == 0){ ++ i2c_writeb(MAX3353E_CONTROL_REGISTER_2, 0x10); ++ } ++ i2c_writeb(MAX3353E_CONTROL_REGISTER_2, 0x0); ++ i2c_writeb(MAX3353E_CONTROL_REGISTER_1, MAX3353E_DM_PULLDOWN | MAX3353E_DP_PULLDOWN); ++ ++#ifdef CONFIG_OTG_MAX3353_PROCFS ++ TRACE_MSG0(TCD, "MAX3353 Procfs"); ++ max3353_procfs_init (); ++#endif /* CONFIG_OTG_MAX3353_PROCFS */ ++ ++ au_sync(); ++ ++ // au_writel(0x1, IC1_TESTBIT); //disable test bit for low leve interrupt ++ //All this done when define type of interrupt in irqmap.c ++ /*au_writel(0x40000000, IC1_CFG0CLR); //low level activation ++ au_writel(0x40000000, IC1_CFG1SET); ++ au_writel(0x40000000, IC1_CFG2CLR); ++ au_writel(0x40000000, IC1_MASKSET);//enable interrupt ++ */ ++ //au_writel(0x40000000, IC1_SRCCLR);//set test bit as a source ++ au_writel(0x40000000, IC1_SRCSET);//set source to original ++ //au_writel(0x0, IC1_TESTBIT); ++ //au_writel(0x1, IC1_TESTBIT); ++ ++#if 0 ++ ret = au_readl(IC0_CFG0RD); ++ printk (KERN_INFO"IC0 Reg 0 : %08x\n", ret); ++ ret = au_readl(IC0_CFG1RD); ++ printk (KERN_INFO"IC0 Reg 1 : %08x\n", ret); ++ ret = au_readl(IC0_CFG2RD); ++ printk (KERN_INFO"IC0 Reg 2 : %08x\n", ret); ++ ret = au_readl(IC0_SRCRD); ++ printk (KERN_INFO"IC0 Source : %08x\n", ret); ++ ret = au_readl(IC0_ASSIGNRD); ++ printk (KERN_INFO"IC0 Assign : %08x\n", ret); ++ ret = au_readl(IC0_WAKERD); ++ printk (KERN_INFO"IC0 Wakeup : %08x\n", ret); ++ ret = au_readl(IC0_MASKRD); ++ printk (KERN_INFO"IC0 Mask : %08x\n", ret); ++ ++ ret = au_readl(IC1_CFG0RD); ++ printk (KERN_INFO"IC1 Reg 0 : %08x\n", ret); ++ ret = au_readl(IC1_CFG1RD); ++ printk (KERN_INFO"IC1 Reg 1 : %08x\n", ret); ++ ret = au_readl(IC1_CFG2RD); ++ printk (KERN_INFO"IC1 Reg 2 : %08x\n", ret); ++ ret = au_readl(IC1_SRCRD); ++ printk (KERN_INFO"IC1 Source : %08x\n", ret); ++ ret = au_readl(IC1_ASSIGNRD); ++ printk (KERN_INFO"IC1 Assign : %08x\n", ret); ++ ret = au_readl(IC1_WAKERD); ++ printk (KERN_INFO"IC1 Wakeup : %08x\n", ret); ++ ret = au_readl(IC1_MASKRD); ++ printk (KERN_INFO"IC1 Mask : %08x\n", ret); ++ ++#endif ++ ++ return 0; ++} ++ ++/*! max3353_mod_exit ++ */ ++void max3353_mod_exit(void) ++{ ++ i2c_writeb(MAX3353E_INTERRUPT_MASK, 0x0); ++ //i2c_close(); ++#ifdef CONFIG_OTG_MAX3353_PROCFS ++ max3353_procfs_exit (); ++#endif /* CONFIG_OTG_MAX3353_PROCFS */ ++ ++ while (PENDING_WORK_ITEM(max3353_private.bh)) { ++ printk(KERN_ERR"%s: waiting for bh\n", __FUNCTION__); ++ schedule_timeout(10 * HZ); ++ } ++ ++} ++ ++/* ********************************************************************************************* */ ++/*! max3353_configure - configure the MAX3353 for this host ++ * @param tx_mode - the type of connection between the host USB and the MAX3353 ++ * @param spd_ctrl - suspend control method ++ */ ++void max3353_configure(tx_mode_t tx_mode, spd_ctrl_t spd_ctrl) ++{ ++ u16 vendor = 0, product = 0, revision = 0; ++ u8 mode1, mode2, control; ++ ++ struct otg_transceiver_map *map = max3353_transceiver_map; ++ TRACE_MSG0(TCD, "Configure"); ++ ++ //#if defined(CONFIG_OTG_MAX3353_MX2ADS) || defined(CONFIG_OTG_MAX3353_MX2ADS_MODULE) ++ u32 vp; ++ u8 vb=0; ++ TRACE_MSG0(TCD, "1. Read Transceiver ID's with long read"); ++ vb = i2c_readb(MAX3353E_MANUFACTURER_REGISTER_0); ++ max3353_private.vendor = vb; ++ vb = i2c_readb(MAX3353E_MANUFACTURER_REGISTER_1); ++ max3353_private.vendor += vb<<8; ++ vb = i2c_readb(MAX3353E_MANUFACTURER_REGISTER_2); ++ max3353_private.vendor += vb<<16; ++ vb = i2c_readb(MAX3353E_MANUFACTURER_REGISTER_3); ++ max3353_private.vendor += vb<<24; ++ ++ vb = i2c_readb(MAX3353E_PRODUCT_ID_REGISTER_0); ++ max3353_private.product = vb; ++ vb = i2c_readb(MAX3353E_PRODUCT_ID_REGISTER_1); ++ max3353_private.product += vb<<8; ++ vb = i2c_readb(MAX3353E_PRODUCT_ID_REGISTER_2); ++ max3353_private.product += vb<<16; ++ vb = i2c_readb(MAX3353E_PRODUCT_ID_REGISTER_3); ++ max3353_private.product += vb<<24; ++ ++ mode1 = i2c_readb(MAX3353E_CONTROL_REGISTER_1); ++ mode2 = i2c_readb(MAX3353E_CONTROL_REGISTER_2); ++ ++ TRACE_MSG4(TCD, "2. OTG Transceiver: vendor: %08x product: %08x mode1: %02x mode2: %02x", ++ max3353_private.vendor, max3353_private.product, mode1, mode2); ++ ++ TRACE_MSG0(TCD, "3. Disable All Transceiver Control Register 1 "); ++ i2c_writeb(MAX3353E_CONTROL_REGISTER_1, 0x0); // clear ++ i2c_writeb(MAX3353E_CONTROL_REGISTER_2, 0x0); // clear ++ ++ TRACE_MSG0(TCD, "4. Enable D-/D+ Pulldowns in Transceiver Control Register 1 "); ++ ++ i2c_writeb(MAX3353E_CONTROL_REGISTER_1, MAX3353E_DM_PULLDOWN | MAX3353E_DP_PULLDOWN); ++ max3353_private.flags = MAX3353E_DM_PULLDOWN | MAX3353E_DP_PULLDOWN; ++ ++ TRACE_MSG0(TCD, "5. Clear latch and enable interrupts"); ++ i2c_writeb(MAX3353E_INTERRUPT_LATCH, 0x0); ++ max3353_int_src_set(0x1f); ++ ++ ++ ++} ++ ++ +diff -uNr linux/drivers/no-otg/ocd/max3353e/max3353e.h linux/drivers/otg/ocd/max3353e/max3353e.h +--- linux/drivers/no-otg/ocd/max3353e/max3353e.h 1970-01-01 01:00:00.000000000 +0100 ++++ linux/drivers/otg/ocd/max3353e/max3353e.h 2006-09-01 21:41:32.000000000 +0200 +@@ -0,0 +1,134 @@ ++/* ++ * otg/ocd/max3353/max3353.h -- USB Transceiver Controller driver ++ * @(#) balden@belcarra.com|otg/ocd/max3353e/max3353e.h|20060831021118|62926 ++ * ++ * Copyright (c) 2004-2005 Belcarra ++ * Copyright (c) 2005-2006 Belcarra Technologies 2005 Corp ++ * ++ * By: ++ * Stuart Lynne <sl@lbelcarra.com>, ++ * Bruce Balden <balden@belcarra.com> ++ * ++ */ ++/*! ++ * @defgroup MAX3353TCD TCD - Maxim MAX3353 ++ * @ingroup platformgroup ++ */ ++/*! ++ * @file otg/ocd/max3353e/max3353e.h ++ * @brief Private structures and defines for MAX3353 Transciever Controller Driver. ++ * ++ * This file contains the private defines and structures for the MAX3353 Transceiver ++ * Driver. ++ * ++ * @ingroup MAX3353TCD ++ */ ++ ++ ++#ifdef CONFIG_XXXXX ++#define MAX3353_GPIO (2) ++#define TCD_MAX3353_I2C_ADDR MAX3353_I2C_ADDR_HIGH; ++#endif ++ ++#define MAX3353_NAME "max3353" ++ ++#define MAX3353_LOC_CONN 0x01 ++ ++#define MAX3353_INT_SRC 0xeb ++ ++//#define MAX3353_ALL_INT_SRC 0xff ++//#define MAX3353_NO_SE1_INT 0xeb ++//#define MAX3353_NO_SE1_INT 0xfb /* do not pay attention to DP_HI */ ++ ++typedef enum otg_transceiver { ++ unknown_transceiver, ++ max3353, ++ max3301e ++} otg_transceiver_t; ++ ++struct otg_transceiver_map { ++ otg_transceiver_t transceiver_type; ++ u16 vendor; ++ u16 product; ++ u16 revision; ++ char *name; ++}; ++ ++struct max3353_private { ++ WORK_STRUCT bh; ++ u16 vendor; ++ u16 product; ++ u16 revision; ++ u32 flags; ++ u8 int_src; ++ struct otg_transceiver_map *transceiver_map; ++}; ++ ++/*! tx_mode ++ * This defines the various ways that the MAX3353 can be ++ * wired into the host USB. ++ */ ++typedef enum tx_mode { ++ dat_se0_bidirectional, /*!< 3-Wire */ ++ vp_vm_bidirectional, /*!< 4-Wire */ ++ dat_se0_unidirectional /*!< 6-Wire */ ++} tx_mode_t; ++ ++/*! spd_ctrl ++ * This defines how the speed and suspend pins are controlled ++ * for this host. ++ */ ++typedef enum spd_ctrl { ++ spd_susp_pins, /*!< controlled by SPEED and SUSPEND pins */ ++ spd_susp_reg, /*!< controled by SPEED_REG and SUSPEND_REG in Mode Control 1 register */ ++} spd_ctrl_t; ++ ++ ++extern struct tcd_ops tcd_ops; ++extern struct max3353_private max3353_private; ++extern struct tcd_instance *tcd_instance; ++ ++/* max3353.c */ ++extern int max3353_mod_init(void); ++extern void max3353_mod_exit(void); ++extern void max3353_chrg_vbus(struct otg_instance *, u8 ); ++extern void max3353_drv_vbus(struct otg_instance *, u8 ); ++extern void max3353_dischrg_vbus(struct otg_instance *, u8 ); ++extern void max3353_dp_pullup_func(struct otg_instance *, u8 ); ++extern void max3353_dm_pullup_func(struct otg_instance *, u8 ); ++extern void max3353_dm_det_func(struct otg_instance *, u8 ); ++extern void max3353_dp_det_func(struct otg_instance *, u8 ); ++extern void max3353_bdis_acon_func(struct otg_instance *otg, u8 flag); ++extern void max3353_id_pulldown_func(struct otg_instance *otg, u8 flag); ++extern void max3353_audio_func(struct otg_instance *otg, u8 flag); ++extern void max3353_uart_func(struct otg_instance *otg, u8 flag); ++extern void max3353_mono_func(struct otg_instance *otg, u8 flag); ++ ++extern void max3353_otg_timer(struct otg_instance *, u8 ); ++extern void max3353_bh(void *); ++extern int max3353_id (struct otg_instance *); ++extern int max3353_vbus (struct otg_instance *); ++extern void max3353_configure(tx_mode_t, spd_ctrl_t ); ++ ++/* thread */ ++extern int max3353_thread_init (void (bh_proc)(void *)); ++extern void max3353_thread_exit (wait_queue_head_t *); ++extern void max3353_thread_wakeup(int enabled, int first); ++ ++ ++ ++/* i2c-xxx.c */ ++int i2c_configure(char *name, int addr); ++extern void i2c_close(void); ++extern u8 i2c_readb(u8 ); ++extern u16 i2c_readw(u8 ); ++extern u32 i2c_readl(u8 ); ++extern int i2c_writeb(u8 , u8 ); ++ ++ ++/* ********************************************************************************************* */ ++#define TRACE_I2CB(t,r) TRACE_MSG3(t, "%-40s[%02x] %02x", #r, r, i2c_readb(r)) ++#define TRACE_I2CW(t,r) TRACE_MSG3(t, "%-40s[%02x] %04x", #r, r, i2c_readw(r)) ++#define TRACE_GPIO(t,b,r) TRACE_MSG2(t, "%-40s %04x", #r, readw(b + r)) ++#define TRACE_REGL(t,r) TRACE_MSG2(t, "%-40s %08x", #r, readl(r)) ++ +diff -uNr linux/drivers/no-otg/ocd/max3353e/tcd-db1550.c linux/drivers/otg/ocd/max3353e/tcd-db1550.c +--- linux/drivers/no-otg/ocd/max3353e/tcd-db1550.c 1970-01-01 01:00:00.000000000 +0100 ++++ linux/drivers/otg/ocd/max3353e/tcd-db1550.c 2006-09-01 21:41:32.000000000 +0200 +@@ -0,0 +1,227 @@ ++/* ++ * otg/ocd/max3353e/tcd-db1550.c -- USB Transceiver Controller driver ++ * @(#) balden@belcarra.com|otg/ocd/max3353e/tcd-db1550.c|20060831021118|37213 ++ * ++ * Copyright (c) 2004-2005 Belcarra ++ * Copyright (c) 2005-2006 Belcarra Technologies 2005 Corp ++ * ++ * By: ++ * Stuart Lynne <sl@lbelcarra.com>, ++ * Shahrad Payandeh <sp@belcarra.com>, ++ * Bruce Balden <balden@belcarra.com> ++ * ++ */ ++/*! ++ * @file otg/ocd/max3353e/tcd-db1550.c ++ * @brief DB1550 MAX3353 driver. ++ * ++ * ++ * @ingroup MAX3353TCD ++ */ ++ ++#include <otg/pcd-include.h> ++#include <linux/pci.h> ++#include <otghw/max3353e-hardware.h> ++#include "otghw/bvd-hardware.h" ++#include "max3353e.h" ++#include <asm/au1000.h> ++#include <linux/i2c.h> ++#include <linux/i2c-dev.h> ++ ++struct ocd_instance *ocd_instance; ++otg_tag_t OCD; ++ ++#if 0 ++#include <otg/usbp-chap9.h> ++#include <otg/usbp-func.h> ++#include <otg/usbp-bus.h> ++#include <otg/otg-trace.h> ++#include <otg/hcd.h> ++#include <otg/tcd.h> ++#include <otg/ocd.h> ++#include <otg/pcd.h> ++ ++#include <otghw/isp1301-hardware.h> ++#include "isp1301.h" ++ ++#endif ++/*SP ++#include <asm/arch/gpio.h> ++#include <asm/arch/mux.h> ++*/ ++//struct isp1301_private isp1301_private; ++ ++#ifdef CONFIG_OMAP_H2 ++#define ADAPTOR_NAME "OMAP I2C" ++#endif /* CONFIG_OMAP_H2 */ ++#ifdef CONFIG_ARCH_MAINSTONE ++#define ADAPTOR_NAME "PXA-I2C-Adapter" ++#endif /* CONFIG_ARCH_MAINSTONE */ ++#ifdef CONFIG_OTG_DB1550_J15 ++#define ADAPTOR_NAME "pb1550 adapter" ++#endif /* CONFIG_OTG_DB1550_J15 */ ++ ++/* ********************************************************************************************* */ ++ ++ ++static irqreturn_t max3353_int_hndlr (int irq, void *dev_id, struct pt_regs *regs) ++{ ++ TRACE_MSG0(TCD, "--"); ++ ++ SCHEDULE_WORK(max3353_private.bh); ++ ++ return IRQ_HANDLED; ++} ++ ++ ++/* ********************************************************************************************* */ ++ ++struct ocd_ops ocd_ops; ++struct tcd_instance *db1550_tcd_instance; ++ ++//#define ADAPTOR_NAME "pb1550 adapter" ++ ++/*! tcd_mod_init ++ */ ++int tcd_mod_init(void) ++{ ++ ++ struct otg_instance *otg; ++ ++ int i2c = i2c_configure(ADAPTOR_NAME, MAX3353E_I2C_ADDR_HIGH); ++ int irq = request_irq (AU1500_GPIO_207, max3353_int_hndlr, SA_INTERRUPT, "MAX3353", NULL); ++ THROW_IF(irq, error); ++ ++ TRACE_MSG0(TCD, "--"); ++ ++ THROW_IF(i2c, error); ++ ++ TRACE_MSG0(TCD, "1. Enable db1550 external OTG Transceiver!"); ++ ++ ++ TRACE_MSG0(TCD, "2. configure i2c driver!"); ++ max3353_configure(dat_se0_bidirectional, spd_susp_reg); ++ ++ TRACE_MSG0(TCD, "3. configure max3353!"); ++ max3353_mod_init(); ++ ++ TRACE_MSG0(TCD, "4. set ops"); ++ THROW_UNLESS(ocd_instance = otg_set_ocd_ops(&ocd_ops), error); ++ THROW_UNLESS(ocd_instance && (otg = ocd_instance->otg), error); ++ THROW_UNLESS(db1550_tcd_instance = otg_set_tcd_ops(&tcd_ops), error); ++ ++#if 0 //test for making pulse on DP ++ for (;;){ ++ max3353_private.flags = i2c_readb(MAX3353E_CONTROL_REGISTER_1); ++ TRACE_MSG0(TCD, "Clr DP PULLDOWN"); ++ max3353_private.flags &= (~MAX3353E_DP_PULLDOWN); ++ TRACE_MSG0(TCD, "Set DP PULLUP"); ++ max3353_private.flags |= (MAX3353E_DP_PULLUP); ++ i2c_writeb(MAX3353E_CONTROL_REGISTER_1, (u8) max3353_private.flags); ++ } ++#endif ++ ++ ++ /* Success! ++ */ ++ ++ TRACE_MSG0(TCD, "5. Success!"); ++ ++ otg_init(otg); ++ ++ //In device mode only this part makes a virtual interrupt, while the cable is ++ //and modules are loaded. ++ au_writel (0x40000000, IC1_SRCCLR); ++ au_writel (0x0, IC1_TESTBIT); ++ au_writel (0x1, IC1_TESTBIT); ++ au_writel (0x0, IC1_TESTBIT); ++ au_writel (0x40000000, IC1_SRCSET); ++ ++ ++ /* error handling ++ */ ++ CATCH(error) { ++ printk(KERN_INFO"Error in tcd initilization\n"); ++ if (irq) free_irq(AU1500_GPIO_207, NULL); ++ if (i2c) i2c_close(); ++ return -EINVAL; ++ } ++ return 0; ++} ++ ++/*! tcd_mod_exit ++ */ ++void tcd_mod_exit(void) ++{ ++ struct otg_instance *otg = tcd_instance->otg; ++ TRACE_MSG0(TCD, "Exit."); ++ ++ otg_event(otg, enable_otg_, OCD, "enable_otg_"); ++ //if (otg) ++ // otg_exit(otg); ++ ++ db1550_tcd_instance = otg_set_tcd_ops(NULL); ++ free_irq(AU1500_GPIO_207, NULL); ++ max3353_mod_exit(); ++ ++ i2c_writeb(MAX3353E_INTERRUPT_MASK, 0xff); ++ i2c_close(); ++ ++ if (otg) ++ otg_exit(otg); ++} ++/* ********************************************************************************************* */ ++struct tcd_ops tcd_ops = { ++ ++// vbus: max3353_vbus, ++ ++ mod_init: tcd_mod_init, ++ mod_exit: tcd_mod_exit, ++ ++ dischrg_vbus_func: max3353_dischrg_vbus, ++ chrg_vbus_func: max3353_chrg_vbus, ++ drv_vbus_func: max3353_drv_vbus, ++ dp_pullup_func: max3353_dp_pullup_func, ++ dm_pullup_func: max3353_dm_pullup_func, ++ ++}; ++ ++#if 0 ++ ++struct tcd_ops tcd_ops; ++ ++ ++void db1550_tcd_global_init(void) ++{ ++ ZERO(tcd_ops); ++ ++ tcd_ops.id = max3353_id; ++ tcd_ops.vbus = max3353_vbus; ++ tcd_ops.chrg_vbus_func = max3353_chrg_vbus; ++ tcd_ops.dischrg_vbus_func = max3353_dischrg_vbus; ++ tcd_ops.dp_pullup_func = max3353_dp_pullup_func; ++ tcd_ops.dm_pullup_func = max3353_dm_pullup_func; ++ tcd_ops.dp_pulldown_func = max3353_dp_pulldown_func; ++ tcd_ops.dm_pulldown_func = max3353_dm_pulldown_func; ++ tcd_ops.overcurrent_func = NULL; ++ tcd_ops.dm_det_func = max3353_dm_det_func; ++ tcd_ops.dp_det_func = max3353_dp_det_func; ++ tcd_ops.cr_det_func = max3353_cr_det_func; ++ ++ tcd_ops.peripheral_host_func = max3353_peripheral_host_func; ++ ++ tcd_ops.id_pulldown_func = max3353_id_pulldown_func; ++ tcd_ops.audio_func = max3353_audio_func; ++ tcd_ops.uart_func = max3353_uart_func; ++ tcd_ops.mono_func = max3353_mono_func; ++ ++ tcd_ops.mod_init = tcd_mod_init; ++ tcd_ops.mod_exit = tcd_mod_exit; ++} ++ ++ ++#endif ++ ++ ++ ++ +diff -uNr linux/drivers/no-otg/ocd/max3353e/tcd-mx2ads.c linux/drivers/otg/ocd/max3353e/tcd-mx2ads.c +--- linux/drivers/no-otg/ocd/max3353e/tcd-mx2ads.c 1970-01-01 01:00:00.000000000 +0100 ++++ linux/drivers/otg/ocd/max3353e/tcd-mx2ads.c 2006-09-01 21:41:32.000000000 +0200 +@@ -0,0 +1,541 @@ ++/* ++ * otg/ocd/max3353e/tcd-mx2ads.c -- MX21ADS ISP1301 Transceiver Controller driver ++ * @(#) balden@belcarra.com|otg/ocd/max3353e/tcd-mx2ads.c|20060831021118|46922 ++ * ++ * Copyright (c) 2004-2005 Belcarra ++ * Copyright (c) 2005-2006 Belcarra Technologies 2005 Corp ++ * ++ * By: ++ * Stuart Lynne <sl@lbelcarra.com>, ++ * Shahrad Payandeh <sp@belcarra.com>, ++ * Bruce Balden <balden@belcarra.com> ++ * ++ */ ++/*! ++ * @file otg/ocd/max3353e/tcd-mx2ads.c ++ ++ * @brief MX2ADS ISP1301 driver. ++ * ++ * This implements the ISP1301 Transceiver Controller Driver for the mx21 ++ * using the USBOTG I2C controller for access. ++ * ++ * Notes ++ * ++ * 1. There are two interrupt sources, the I2C IO completion type generated when either of NOACK ++ * or RWREADY is set and the XCVR activity type generated when there is a OTG transceiver ++ * generated interrupt. ++ * ++ * It is important to only enable a single source at a time. By default we leave the XCVR ++ * interrupt enabled for normal operation and only enable the I2C IO type when we are actively ++ * waiting for an I2C IO operation to complete. ++ * ++ * 2. The USBOTG module can also interact with the OTG Transceiver, attempting to do register ++ * reads for various conditions (such as XCVR interrupts.) These interfere and cause problems so ++ * we keep the the I2C Controller interface in software mode at all times. ++ * ++ * 3. Because we must wait several hundred micro-seconds for I/O operations to complete, the i2c ++ * implementation is only usable from non-interrupt contexts. This means that all work that ++ * needs to be done needs to be pushed to a bottom half handler. A generic i2cWrite_irq() is ++ * provided where simple writes are all that are required. ++ * ++ * 4. The handling of the ISC_INTERRUPT_AND_CTRL register is complicated because the top nibble ++ * is r/w controlling the interrupt source enables while the bottom nibble is write 1 to clear ++ * to reset two of the three interrupt source bits (NOACK and RWREADY). ++ * ++ * ++ * TODO ++ * ++ * 1. The MX2ADS seems to have a hardware problem, when operating as a B-Device, Vbus does not ++ * drop to zero when disconnected (unplugged.) A 22kohm resister across C133 is suggested. ++ * Alternately I am just enabling VBUS_DISCHRG in LOC_CONN when we enable DP_PULLUP. Hopefully ++ * this will not be required in production code. ++ * ++ * @ingroup ISP1301TCD ++ */ ++ ++#include <otg/pcd-include.h> ++#include <linux/pci.h> ++#include <asm/arch/mx2.h> ++#include <otghw/isp1301-hardware.h> ++#include "mx2.h" ++#include <otghw/mx2-hardware.h> ++#include "isp1301.h" ++//#include <otg/otg-ops.h> ++ ++/* ********************************************************************************************* */ ++/* ********************************************************************************************* */ ++ ++/*! mx2_urb ++ */ ++u8 mx2_urbv(u32 port) ++{ ++ u8 val = mx2_rb(port); ++ TRACE_MSG2(TCD, "PORT: %08x %02x", port, val); ++ return val; ++} ++ ++/* ********************************************************************************************* */ ++/* ********************************************************************************************* */ ++/* I2C Bottom Half I/O ++ * ++ * These are the I2C I/O routines that can be used from any non-interrupt ++ * context, typically from a bottom half handler. ++ * ++ * There is an interrupt safe, delayed i2cWrite function, it queues the ++ * writes and schedules work via a bottom half handler that can safely call ++ * the i2cWrite function. ++ */ ++#define MX2_I2C_TIMEOUT 1 ++#define I2C_READ_MASK 0x80 ++#define I2C_MAX_WRITE 100 ++ ++DECLARE_MUTEX(i2c_sem); // semaphore to prevent overlapped i2c i/o ++wait_queue_head_t mx2_i2c_wait; // wait structure for i2c i/o interrupt ++struct WORK_STRUCT mx2_i2c_io_work_struct; // work structure for queued i2c writes ++ ++int i2c_Write_Queued; ++u8 i2c_Write_Data[I2C_MAX_WRITE]; ++u32 i2c_Write_Addr[I2C_MAX_WRITE]; ++char * i2c_Write_Msg[I2C_MAX_WRITE]; ++ ++/*! i2c_read - read from otg transceiver ++ * This programs the I2C controller to do a read, waits for the completion ++ * interrupt, cleans up and returns the value for the specified register. ++ */ ++void i2c_read(u32 port, int ops) ++{ ++ u8 i2c_interrupt_and_ctrl; ++ u8 ctrl_reg; ++ int count; ++ RETURN_IF(i2c_Write_Queued < 0); ++ down(&i2c_sem); // pass through semaphore ++#if 1 ++ if ((ctrl_reg = mx2_rb(MX2_I2C_OP_CTRL_REG)) & 0x4) { ++ TRACE_MSG1(TCD, "bad ctrl_reg: %02x", ctrl_reg); ++ } ++ for (count = 0; (count < 100) && (ctrl_reg = mx2_rb(MX2_I2C_OP_CTRL_REG)) & 0x80; count++) { ++ TRACE_MSG1(TCD, "busy ctrl_reg: %02x", ctrl_reg); ++ } ++#endif ++ mx2_orb(MX2_I2C_INTERRUPT_AND_CTRL, 0x6); // reset and disable interrupts ++ mx2_wb(MX2_SEQ_OP_REG, ops); // set number of sequential operations to 1 ++ mx2_wb(MX2_SEQ_RD_STARTAD, port); // sequential read start address ++ mx2_wb(MX2_OTG_XCVR_DEVAD, I2C_READ_MASK | MX2_I2C_DEV_ADDR); // receiver address ++ mx2_orb(MX2_I2C_INTERRUPT_AND_CTRL, I2C_NOACK_EN | I2C_RWREADY_EN); // enable i2x I/O interrupt ++ interruptible_sleep_on_timeout(&mx2_i2c_wait, MX2_I2C_TIMEOUT); // wait for signal from i2c io interrupt handler ++ up(&i2c_sem); ++} ++ ++/*! i2c_readb - read a byte from otg transceiver ++ * This programs the I2C controller to do a single byte read, waits for the ++ * completion interrupt, cleans up and returns the value for the specified ++ * register. ++ */ ++u8 i2c_readb(u8 port) ++{ ++ u8 data; ++ u32 addr = OTG_I2C_BASE + port; ++ i2c_read(addr, 1); ++ data = mx2_rb(addr); // read the register ++ //TRACE_MSG2(TCD, "port: %08x data: %02x", port, data); ++ return data; ++} ++ ++/*! i2c_readw - read two bytes from otg transceiver ++ * This programs the I2C controller to do a two byte read, waits for the ++ * completion interrupt, cleans up and returns the value for the specified ++ * registers. ++ */ ++u16 i2c_readw(u8 port) ++{ ++ u16 data; ++ u32 addr = OTG_I2C_BASE + port; ++ i2c_read(addr, 2); ++ data = mx2_rb(addr) | (mx2_rb(addr + 1) << 8); // read the register ++ //TRACE_MSG2(TCD, "port: %08x data: %04x", port, data); ++ return data; ++} ++ ++/*! i2c_readl - read two bytes from otg transceiver ++ * This programs the I2C controller to do a two byte read, waits for the ++ * completion interrupt, cleans up and returns the value for the specified ++ * registers. ++ */ ++u32 i2c_readl(u8 port) ++{ ++ u32 data; ++ u32 addr = OTG_I2C_BASE + port; ++ i2c_read(addr, 2); ++ data = mx2_rb(addr) | (mx2_rb(addr + 1) << 8) | (mx2_rb(addr + 2) << 16) | (mx2_rb(addr + 3) << 24); ++ //TRACE_MSG2(TCD, "port: %08x data: %04x", port, data); ++ return data; ++} ++ ++/*! i2cWrite - write a byte to otg transceiver ++ * This writes the data to the shadow register, programs the I2C controller ++ * to do a single byte write, waits for the completion interrupt, cleans up ++ * and exits. ++ */ ++void i2cWrite(u32 port, u8 data, char *msg) ++{ ++ u8 i2c_interrupt_and_ctrl; ++ u8 test; ++ u8 ctrl_reg; ++ int count; ++ //TRACE_FMSG3(TCD, msg, "i2cWrite: port: %s (%08x) data: %02x", mx2_ureg_name(port), port, data); ++ TRACE_FMSG2(TCD, msg, "i2cWrite: (%08x) data: %02x", port, data); ++ ++ //test = i2c_readb(port & 0xfffffffe); ++ //TRACE_MSG2(TCD, "i2cWrite: %02x before %02x", port, test); ++ ++ down(&i2c_sem); // pass through semaphore ++ ++#if 1 ++ if ((ctrl_reg = mx2_rb(MX2_I2C_OP_CTRL_REG)) & 0x4) { ++ TRACE_MSG1(TCD, "bad ctrl_reg: %02x", ctrl_reg); ++ } ++ for (count = 0; (count < 10) && (ctrl_reg = mx2_rb(MX2_I2C_OP_CTRL_REG)) & 0x80; count++) { ++ TRACE_MSG1(TCD, "busy ctrl_reg: %02x", ctrl_reg); ++ } ++#endif ++ mx2_orb(MX2_I2C_INTERRUPT_AND_CTRL, 0x6); // reset and disable interrupts ++ mx2_wb(port, data); // write the register ++ mx2_wb(MX2_SEQ_OP_REG, 1); // set number of sequential operations to 1 ++ mx2_wb(MX2_SEQ_RD_STARTAD, port); // sequential read start address ++ mx2_wb(MX2_OTG_XCVR_DEVAD, MX2_I2C_DEV_ADDR); // receiver address ++ mx2_orb(MX2_I2C_INTERRUPT_AND_CTRL, I2C_NOACK_EN | I2C_RWREADY_EN); // enable i2x I/O interrupt ++ interruptible_sleep_on_timeout(&mx2_i2c_wait, MX2_I2C_TIMEOUT); // wait for signal from i2c io interrupt handler ++ up(&i2c_sem); ++ ++ //test = i2c_readb(port & 0xfffffffe); ++ //TRACE_MSG2(TCD, "i2cWrite: %02x after %02x", port, test); ++} ++ ++/*! mx2_i2c_io_bh - bottom half handler to use i2cWrite on queued data ++ */ ++void mx2_i2c_io_bh(void *arg) ++{ ++ while (i2c_Write_Queued > 0) { ++ u8 write = i2c_Write_Data[0]; ++ u32 port = i2c_Write_Addr[0]; ++ char *msg = i2c_Write_Msg[0]; ++ u8 data; ++ unsigned long flags; // atomic update of counter and saved values ++ local_irq_save (flags); ++ i2c_Write_Queued--; ++ memcpy(i2c_Write_Data, i2c_Write_Data + 1, sizeof(i2c_Write_Data) - 1); ++ memcpy(i2c_Write_Addr, i2c_Write_Addr + 1, sizeof(i2c_Write_Addr) - 4); ++ memcpy(i2c_Write_Msg, i2c_Write_Msg + 1, sizeof(i2c_Write_Msg) - 4); ++ local_irq_restore (flags); ++ i2cWrite(port, write, msg); // perform write ++ } ++} ++ ++/*! i2cWrite_irq - queue data for i2cWrite bottom half handler ++ */ ++void i2cWrite_irq(u32 port, u8 data, char *msg) ++{ ++ RETURN_IF(i2c_Write_Queued < 0); ++ if ((i2c_Write_Queued == I2C_MAX_WRITE)) { ++ TRACE_MSG3(TCD, "TOO MANY QUEUED CANNOT WRITE port: %08x data: %02x active: %d", port, data, i2c_Write_Queued); ++ return; ++ } ++ i2c_Write_Addr[i2c_Write_Queued] = port; ++ i2c_Write_Data[i2c_Write_Queued] = data; ++ i2c_Write_Msg[i2c_Write_Queued] = msg; ++ i2c_Write_Queued++; ++ TRACE_FMSG3(TCD, msg, "i2cWrite_irq: port: %08x data: %02x active: %d", port, data, i2c_Write_Queued); ++ SCHEDULE_WORK(mx2_i2c_io_work_struct); ++} ++ ++/*! i2c_writeb ++ */ ++int i2c_writeb(u8 subaddr, u8 buf) ++{ ++ i2cWrite_irq(OTG_I2C_BASE + subaddr, buf, "i2c_writeb"); ++ return 0; ++} ++ ++/* ********************************************************************************************* */ ++/* ********************************************************************************************* */ ++/*! mx2ads_bh ++ */ ++void mx2ads_bh(void *arg) ++{ ++ isp1301_bh(NULL); ++ //mx2_bh(NULL); ++ ++ /* re-enable the OTG XCVRINT ++ */ ++ mx2_orb(MX2_I2C_INTERRUPT_AND_CTRL, I2C_OTGXCVRINT_EN); ++} ++ ++ ++ ++/* ********************************************************************************************* */ ++/* ********************************************************************************************* */ ++/* I2C I/O and XCVR Interrupt Handler ++ */ ++ ++/*! mx2_i2c_int_hndlr ++ * This is called from the main mx21ads interrupt handler to handle the ++ * i2c i/o interrupt. There are two main interrupt sources: ++ * ++ */ ++irqreturn_t mx2_i2c_int_hndlr (int irq, void *dev_id, struct pt_regs *regs) ++{ ++ //TRACE_MSG0(TCD, "MX2ADS I2C INTERRUPT: WAKEUP"); ++ wake_up_interruptible(&mx2_i2c_wait); // wakeup waiting bottom half handler ++ return IRQ_HANDLED; ++} ++ ++/*! mx2_tcd_int_hndlr ++ * This is called from the main mx21ads interrupt handler to handle the ++ * i2c i/o interrupt. There are two main interrupt sources: ++ */ ++irqreturn_t mx2_tcd_int_hndlr (int irq, void *dev_id, struct pt_regs *regs) ++{ ++ //TRACE_MSG0(TCD, "MX2ADS TCD INTERRUPT: SCHEDULE WORK"); ++ isp1301_thread_wakeup(1, 0); // wakeup kernel thread ++ return IRQ_HANDLED; ++} ++ ++/* ********************************************************************************************* */ ++/* ********************************************************************************************* */ ++ ++/*! mx2_tcd_init - used to initialize/enable or disable the tcd driver ++ */ ++void mx2_tcd_init(struct otg_instance *otg, u8 flag) ++{ ++ TRACE_MSG0(TCD, "--"); ++ switch (flag) { ++ case SET: ++ TRACE_MSG0(TCD, "MX2_TCD_EN SET"); ++ otg_event(otg, TCD_OK | ID_FLOAT, TCD, "TCD_OK"); ++ //mx2_i2c_xcvr_schedule(1); ++ isp1301_thread_wakeup(1, 1); ++ break; ++ case RESET: ++ TRACE_MSG0(TCD, "MX2_TCD_EN RESET"); ++ isp1301_thread_wakeup(0, 0); ++ otg_event(otg, TCD_OK, TCD, "TCD_OK"); ++ break; ++ } ++} ++ ++/*! mx2_dp_pullup_func - used to enable or disable peripheral connecting to bus ++ */ ++void mx2_dp_pullup_func(struct otg_instance *otg, u8 flag) ++{ ++ struct tcd_instance *tcd = (struct tcd_instance *)otg->tcd; ++ TRACE_MSG0(TCD, "--"); ++ switch (flag) { ++ case SET: ++ TRACE_MSG0(TCD, "MX2_LOC_CONN SET"); ++ ++ TRACE_MSG0(TCD, "MX2_LOC_CONN SET - enable function interrupts"); ++ mx2_wl(OTG_FUNC_SINT_STEN, /*SYSTEM_DONEREGINTDS_EN | *//*SYSTEM_SOFDETINT_EN |*/ SYSTEM_DONEREGINT_EN | ++ SYSTEM_SUSPDETINT_EN | SYSTEM_RSMFININT_EN | SYSTEM_RESETINT_EN); ++ ++ TRACE_MSG0(TCD, "MX2_LOC_CONN SET - Set VBUS_DISCHRG"); ++ i2c_writeb(ISP1301_OTG_CONTROL_SET, (u8) ISP1301_VBUS_DISCHRG); ++ break; ++ } ++ ++ /* call generic dp pullup function ++ */ ++ isp1301_dp_pullup_func(otg, flag); ++ ++ switch (flag) { ++ case RESET: ++ TRACE_MSG0(TCD, "MX2_LOC_CONN SET - Clr VBUS_DISCHRG"); ++ i2c_writeb(ISP1301_OTG_CONTROL_CLR, (u8) ISP1301_VBUS_DISCHRG); ++ break; ++ } ++ ++} ++ ++/*! mx2_drv_vbus - used to enable or disable A-device driving Vbus ++ */ ++void mx2_drv_vbus(struct otg_instance *otg, u8 flag) ++{ ++ TRACE_MSG0(TCD, "--"); ++ switch (flag) { ++ case SET: ++ TRACE_MSG0(TCD, "DRV_VBUS_SET"); ++ i2c_writeb(ISP1301_OTG_CONTROL_CLR, (u8) ISP1301_VBUS_DRV); ++ break; ++ } ++ ++ /* call generic drv vbus function ++ */ ++ isp1301_drv_vbus(otg, flag); ++ ++ switch (flag) { ++ case RESET: ++ TRACE_MSG0(TCD, "DRV_VBUS_RESET"); ++ i2c_writeb(ISP1301_OTG_CONTROL_SET, (u8) ISP1301_VBUS_DRV); ++ break; ++ } ++} ++ ++ ++ ++/*! mx2_id - return true if id is present ++ */ ++int mx2_id(struct otg_instance *otg) ++{ ++ /* this will force the bottom half handler to read the interrupt ++ * source register and generate an otg event ++ */ ++ TRACE_MSG0(TCD, "ID: 0"); ++ //mx2_i2c_xcvr_schedule(1); ++ isp1301_thread_wakeup(1, 1); ++ return 0; ++} ++ ++/*! mx2_vbus - return true if Vbus is present ++ */ ++int mx2_vbus(struct otg_instance *otg) ++{ ++ /* this will force the bottom half handler to read the interrupt ++ * source register and generate an otg event ++ */ ++ TRACE_MSG0(TCD, "VBUS: 0"); ++ //mx2_i2c_xcvr_schedule(1); ++ isp1301_thread_wakeup(1, 1); ++ return 0; ++} ++ ++/* ********************************************************************************************* */ ++struct tcd_ops *otg_tcd_ops; ++struct tcd_instance *mx2ads_tcd_instance; ++ ++/*! mx2_tcd_mod_init - initial tcd setup ++ * Allocate interrupts and setup hardware. ++ * This is called from mx2-ocd.c ++ */ ++int mx2_tcd_mod_init (void) ++{ ++ struct otg_instance *otg; ++ ++ printk(KERN_INFO"%s\n", __FUNCTION__); ++ ++ TRACE_MSG0(TCD, "0. Basic Setup"); ++ ++ ++ init_waitqueue_head(&mx2_i2c_wait); ++ PREPARE_WORK_ITEM(mx2_i2c_io_work_struct, &mx2_i2c_io_bh, NULL); ++ //PREPARE_WORK_ITEM(mx2_i2c_xcvr_work_struct, &mx2_i2c_xcvr_bh, NULL); ++ ++ isp1301_thread_init(mx2ads_bh); ++ ++ ++ TRACE_MSG1(TCD, "i2c_int_hndlr: %x", otg_tcd_ops); ++ TRACE_MSG2(TCD, "i2c_int_hndlr: %x %x", &tcd_ops, tcd_ops.i2c_int_hndlr); ++ TRACE_MSG2(TCD, "tcd_int_hndlr: %x %x", &tcd_ops, tcd_ops.tcd_int_hndlr); ++ ++ TRACE_MSG0(TCD, "4. Enable Interrupts"); ++ ++ mx2_wb(MX2_I2C_OP_CTRL_REG, 0x04); // Software mode, output enabled ++ ++ mx2_urbv(MX2_OTG_XCVR_DEVAD); ++ mx2_urbv(MX2_SEQ_OP_REG); ++ mx2_urbv(MX2_SEQ_RD_STARTAD); ++ mx2_urbv(MX2_I2C_OP_CTRL_REG); ++ mx2_urbv(MX2_SCLK_TO_SCL_HPER); ++ mx2_urbv(MX2_I2C_INTERRUPT_AND_CTRL); ++ ++ mx2_wb(MX2_I2C_OP_CTRL_REG, 0x01); // Software mode, output enabled ++ ++ ++ ++ mx2_wb(MX2_I2C_INTERRUPT_AND_CTRL, I2C_OTGXCVRINT_EN | I2C_NOACK_EN | I2C_RWREADY_EN); ++ ++ isp1301_configure(vp_vm_bidirectional, spd_susp_pins); ++ isp1301_mod_init(); ++ ++ THROW_UNLESS(tcd_instance && (otg = tcd_instance->otg), error); ++ THROW_UNLESS(mx2ads_tcd_instance = otg_set_tcd_ops(&tcd_ops), error); ++ ++ /* Success! ++ */ ++ ++ TRACE_MSG0(TCD, "5. Success!"); ++ ++ otg_init(otg); ++ //otg_start(otg, ocd_ops.capabilities & OCD_CAPABILITIES_AUTO); ++ ++ /* ++ if ((ocd_ops.capabilities & OCD_CAPABILITIES_TR) && (ocd_ops.capabilities & OCD_CAPABILITIES_AUTO)) { ++ otg_event_irq(otg, enable_otg, TCD, "enable_otg"); ++ otg_event_irq(otg, bus_req, TCD, "bus_req"); ++ } ++ */ ++ ++ CATCH(error) { ++ printk(KERN_INFO"%s: failed\n", __FUNCTION__); ++ return -EINVAL; ++ } ++ ++ TRACE_MSG0(TCD, "MX2_MOD_TCD_INIT FINISHED"); ++ return 0; ++} ++ ++/*! mx2_tcd_mod_exit - de-initialize ++ * This is called from mx2-ocd.c ++ */ ++void mx2_tcd_mod_exit (void) ++{ ++ struct otg_instance *otg = tcd_instance->otg; ++ TRACE_MSG0(TCD, "MX2_MOD_TCD_EXIT"); ++ ++ otg_event(otg, enable_otg_, OCD, "enable_otg_"); ++ //if (otg) ++ // otg_exit(otg); ++ ++ mx2ads_tcd_instance = otg_set_tcd_ops(NULL); ++ ++ i2c_Write_Queued = -1; ++ wake_up_interruptible(&mx2_i2c_wait); // wakeup waiting bottom half handler ++ ++ while (PENDING_WORK_ITEM(mx2_i2c_io_work_struct) /*|| PENDING_WORK_ITEM(mx2_i2c_xcvr_work_struct)*/ ) { ++ printk(KERN_ERR"%s: waiting for bh\n", __FUNCTION__); ++ schedule_timeout(10 * HZ); ++ } ++ isp1301_thread_exit(&mx2_i2c_wait); ++ isp1301_mod_exit(); ++ ++ if (otg) ++ otg_exit(otg); ++} ++ ++/* ********************************************************************************************* */ ++struct tcd_ops tcd_ops = { ++ id: mx2_id, ++ vbus: mx2_vbus, ++ ++ tcd_init_func: mx2_tcd_init, ++ tcd_en_func: NULL, ++ chrg_vbus_func: isp1301_chrg_vbus, ++ drv_vbus_func: isp1301_drv_vbus, ++ dischrg_vbus_func: isp1301_dischrg_vbus, ++ dp_pullup_func: mx2_dp_pullup_func, ++ dm_pullup_func: isp1301_dm_pullup_func, ++ ++ overcurrent_func: NULL, ++ dm_det_func: isp1301_dm_det_func, ++ dp_det_func: isp1301_dp_det_func, ++ charge_pump_func: NULL, ++ bdis_acon_func: isp1301_bdis_acon_func, ++ id_pulldown_func: isp1301_id_pulldown_func, ++ audio_func: isp1301_audio_func, ++ uart_func: isp1301_uart_func, ++ mono_func: isp1301_mono_func, ++ ++ i2c_int_hndlr: mx2_i2c_int_hndlr, ++ tcd_int_hndlr: mx2_tcd_int_hndlr, ++ mod_init: mx2_tcd_mod_init, ++ mod_exit: mx2_tcd_mod_exit, ++}; ++ +diff -uNr linux/drivers/no-otg/ocd/otg-hcd/hcd-init-l24.c linux/drivers/otg/ocd/otg-hcd/hcd-init-l24.c +--- linux/drivers/no-otg/ocd/otg-hcd/hcd-init-l24.c 1970-01-01 01:00:00.000000000 +0100 ++++ linux/drivers/otg/ocd/otg-hcd/hcd-init-l24.c 2006-09-01 21:41:32.000000000 +0200 +@@ -0,0 +1,89 @@ ++/* ++ * otg/ocd/otg-hcd/hcd-l24-init.c - OTG Host Controller Driver Module Initialization ++ * ++ * Copyright (c) 2004 Belcarra ++ * ++ * By: ++ * Stuart Lynne <sl@belcara.com>, ++ * Tom Rushworth <tbr@belcara.com>, ++ * Bruce Balden <balden@belcara.com> ++ * ++ */ ++/*! ++ * @file otg/ocd/otg-hcd/hcd-init-l24.c ++ * @brief ++ * ++ * This file initializes the low level hardware drivers. ++ * ++ * This is the linux 2.4 version. ++ * ++ * @ingroup OTGHCD ++ */ ++ ++#include <linux/config.h> ++#include <linux/module.h> ++ ++#include <otg/otg-compat.h> ++ ++#include <otg/otg-compat.h> ++#include <otg/usbp-chap9.h> ++#include <otg/otg-trace.h> ++#include <otg/otg-api.h> ++#include <otg/otg-utils.h> ++#include <otg/otg-hcd.h> ++ ++#ifdef MODULE ++#if LINUX_VERSION_CODE >= KERNEL_VERSION (2,4,17) ++MODULE_LICENSE ("GPL"); ++#endif ++MODULE_PARM (serial_number_str, "s"); ++MODULE_PARM_DESC (serial_number_str, "Serial Number"); ++MODULE_AUTHOR ("sl@belcarra.com, tbr@belcarra.com"); ++MODULE_DESCRIPTION ("USB On-The-Go HCD"); ++#endif ++char *serial_number_str; ++ ++otg_tag_t HCD; ++extern struct hcd_ops hcd_ops; ++struct hcd_instance *hcd_instance; ++ ++/* ************************************************************************************* */ ++ ++/* hcd_modexit - This is *only* used for drivers compiled and used as a module. ++ */ ++static void hcd_modexit (void) ++{ ++ printk(KERN_INFO"%s\n", __FUNCTION__); ++ if (hcd_ops.mod_exit) hcd_ops.mod_exit(); ++ hcd_instance = otg_set_hcd_ops(NULL); ++ otg_trace_invalidate_tag(HCD); ++} ++ ++/* otg_modinit - linux module initialization ++ * ++ * This needs to initialize the hcd, pcd and tcd drivers. This includes tcd and possibly hcd ++ * for some architectures. ++ * ++ */ ++static int hcd_modinit (void) ++{ ++ printk(KERN_INFO"%s\n", __FUNCTION__); ++ HCD = otg_trace_obtain_tag(); ++ ++ THROW_UNLESS(hcd_instance = otg_set_hcd_ops(&hcd_ops), error); ++ THROW_IF((hcd_ops.mod_init) ? hcd_ops.mod_init() : 0, error); ++ ++ CATCH(error) { ++ hcd_modexit(); ++ return -EINVAL; ++ } ++ return 0; ++} ++ ++module_init (hcd_modinit); ++#ifdef MODULE ++module_exit (hcd_modexit); ++#endif ++ ++ ++ +diff -uNr linux/drivers/no-otg/ocd/otg-hcd/hcd-l26.c linux/drivers/otg/ocd/otg-hcd/hcd-l26.c +--- linux/drivers/no-otg/ocd/otg-hcd/hcd-l26.c 1970-01-01 01:00:00.000000000 +0100 ++++ linux/drivers/otg/ocd/otg-hcd/hcd-l26.c 2006-09-01 21:41:32.000000000 +0200 +@@ -0,0 +1,806 @@ ++/* ++ * otg/ocd/otg-hcd/hcd-l26.c - Generic transfer level USBOTG aware Host Controller Driver (HCD) ++ * ++ * Copyright (c) 2004 Belcarra Technologies ++ * ++ * By: ++ * Tom Rushworth <tbr@belcara.com>, ++ * Stuart Lynne <sl@belcara.com>, ++ * Bruce Balden <balden@belcara.com> ++ * ++ */ ++/*! ++ * @file otg/ocd/otg-hcd/hcd-l26.c ++ * @brief Linux Generic Host Controller Driver ++ * ++ * @ingroup OTGHCD ++ */ ++ ++#include <linux/config.h> ++#include <linux/module.h> ++#include <linux/kernel.h> ++#include <linux/slab.h> ++#include <linux/init.h> ++#include <linux/usb.h> ++#include <asm/io.h> ++ ++ ++#include <otg/otg-compat.h> ++ ++#include "../core/hcd.h" ++ ++#include <otg/usbp-chap9.h> ++#include <otg/otg-trace.h> ++#include <otg/otg-api.h> ++#include <otg/otg-hcd.h> ++ ++#include <otg/hcd-l26.h> ++#include <otg/hcd-rh.h> ++#include <otg/hcd-hw.h> ++ ++int hcd_probe(struct usb_interface *intf, const struct usb_device_id *id) ++{ ++ return 0; ++} ++ ++void hcd_disconnect(struct usb_interface *intf) ++{ ++} ++ ++#define STATIC static ++ ++STATIC __inline__ struct urb *grab_active_urb(struct bus_hcpriv *bus_hcpriv, int u) ++{ ++ unsigned long flags; ++ struct urb *urb; ++ // Paranoia. ++ RETURN_NULL_UNLESS(bus_hcpriv && bus_hcpriv->active_urbs && (u < bus_hcpriv->max_active_urbs)); ++ local_irq_save(flags); ++ urb = bus_hcpriv->active_urbs[u]; ++ bus_hcpriv->active_urbs[u] = NULL; ++ local_irq_restore(flags); ++ return urb; ++} ++ ++#define OUT(o,e)((o && e) ? 1 : 0) ++ ++STATIC void hcd_queue_urb(struct dev_hcpriv *dev_hcpriv, struct urb *urb, int endpoint, int out) ++{ ++ unsigned long flags; ++ // Paranoia. ++ RETURN_UNLESS(dev_hcpriv && urb && (endpoint < 16)); ++ local_irq_save(flags); ++ dev_hcpriv->num_urbs_both[OUT(out, endpoint)][endpoint] += 1; ++ list_add_tail(&urb->urb_list, &dev_hcpriv->queued_urbs_both[OUT(out, endpoint)][endpoint]); ++ TRACE_MSG4(HCD,"urb: %08x ep: %d n: %u %s", (u32)(void*)urb, ++ endpoint, dev_hcpriv->num_urbs_both[OUT(out, endpoint)][endpoint], "IN"); ++ local_irq_restore(flags); ++} ++ ++STATIC struct urb *hcd_next_urb(struct dev_hcpriv *dev_hcpriv, int endpoint, int out) ++{ ++ // MUST BE CALLED in_irq()!! ++ struct urb *urb; ++ struct list_head *epq; ++ // Paranoia. ++ RETURN_NULL_UNLESS(dev_hcpriv && (endpoint < 16)); ++ epq = &dev_hcpriv->queued_urbs_both[OUT(out, endpoint)][endpoint]; ++ RETURN_NULL_IF(list_empty(epq)); ++ urb = list_entry(epq->next, struct urb, urb_list); ++ list_del_init(&urb->urb_list); ++ dev_hcpriv->num_urbs_both[OUT(out, endpoint)][endpoint] -= 1; ++ return urb; ++} ++ ++STATIC void hcd_finish_urb(struct urb *urb, int reason) ++{ ++ /* Call the upper layer completion routine. Decrement the ref count ++ * (release our interest in this urb). ++ */ ++ urb->status = reason; ++ if (urb->complete) ++ urb->complete(urb, NULL); ++ usb_put_urb(urb); ++} ++ ++STATIC int hcd_submit_to_hw(struct bus_hcpriv *bus_hcpriv, struct urb *urb, int out) ++{ ++ // Pass the urb down to the hardware. ++ int rc; ++ void *data; ++ int dev_addr; ++ int endpoint; ++ int format; ++ int len; ++ int pid; ++ int toggle; ++ u32 other; ++ unsigned long flags; ++ ++ struct usb_device *dev; ++ struct usb_bus *bus; ++ struct dev_hcpriv *dev_hcpriv; ++ u8 *cp; ++ ++ RETURN_EINVAL_UNLESS((dev = urb->dev)); ++ RETURN_EINVAL_UNLESS((bus = dev->bus)); ++ RETURN_EINVAL_UNLESS((dev_hcpriv = (struct dev_hcpriv *) (dev->hcpriv))); ++ ++ dev_addr = usb_pipedevice(urb->pipe); ++ endpoint = usb_pipeendpoint(urb->pipe); ++ ++ TRACE_MSG4(HCD,"urb: %08lx ep: %d %s len: %d", (u32)(void*)urb, endpoint, (out?"OUT":"IN"), urb->actual_length); ++ ++ if (out) { ++#if 0 ++ int i; ++ u8 *cp = urb->transfer_buffer; ++ ++ TRACE_MSG1(HCD, "NEXT TX: length: %d", urb->actual_length); ++ ++ for (i = 0; i < urb->actual_length; i+= 8) ++ ++ TRACE_MSG8(HCD, "SENT %02x %02x %02x %02x %02x %02x %02x %02x", ++ cp[i + 0], cp[i + 1], cp[i + 2], cp[i + 3], ++ cp[i + 4], cp[i + 5], cp[i + 6], cp[i + 7] ++ ); ++#endif ++ } ++ ++ if (usb_endpoint_halted(urb->dev, endpoint, out)) { ++ TRACE_MSG4(HCD,"urb for halted endpoint urb: %08lx dev: %d ep: %d dir: %c", ++ urb, dev_addr, endpoint, (out?'O':'I')); ++ urb->actual_length = 0; ++ hcd_finish_urb(urb,-EPIPE); ++ return -EINVAL; ++ } ++ ++ data = urb->transfer_buffer; ++ len = urb->transfer_buffer_length; ++ format = usb_pipetype(urb->pipe); ++ other = 0; ++ ++ switch (format) { ++ case PIPE_CONTROL: ++ pid = USB_PID_SETUP; ++ // overload toggle with data phase direction (setup is always toggle 0) ++ // toggle == TRUE --> OUT (HOST2DEVICE --> OUT) ++ ++ toggle = ((((struct usbd_device_request *) urb->setup_packet)->bmRequestType & USB_REQ_DIRECTION_MASK) == ++ USB_REQ_HOST2DEVICE); ++ ++ other = (u32) (void *) urb->setup_packet; ++ ++ // XXX send set feature otg enable IFF set configuration and ++ // to first device and previously otg descriptor was seen. ++ break; ++ ++ case PIPE_BULK: ++ pid = (out ? USB_PID_OUT : USB_PID_IN); ++ toggle = usb_gettoggle(urb->dev,endpoint,out); ++ other = (urb->transfer_flags & URB_ZERO_PACKET); ++ break; ++ ++ case PIPE_INTERRUPT: ++ pid = (out ? USB_PID_OUT : USB_PID_IN); ++ toggle = usb_gettoggle(urb->dev,endpoint,out); ++ other = urb->interval; ++ break; ++ ++ case PIPE_ISOCHRONOUS: ++ pid = (out ? USB_PID_OUT : USB_PID_IN); ++ toggle = 0; ++ other = urb->interval; ++ break; ++ ++ default: ++ pid = 0; ++ toggle = 0; ++ break; ++ } ++ ++ /* We need an irq lock here in order to make sure the urb gets saved in ++ bus_hcpriv->active_urbs[] before any possible completion interrupt. */ ++ local_irq_save(flags); ++ rc = hcd_hw_start_transfer(bus_hcpriv, len, data, toggle, usb_maxpacket(urb->dev, urb->pipe, out), ++ (((urb->pipe) >> 26) & 1), endpoint,dev_addr, pid, format,other); ++ ++ if (rc >= 0 && rc < bus_hcpriv->max_active_urbs) { ++ ++ if (!bus_hcpriv->active_urbs[rc]) ++ bus_hcpriv->active_urbs[rc] = urb; ++ ++ /* Oops, total screw up, we shouldn't ever see this. ++ * (The lower layer just told us it started a second urb in ++ * an already busy slot.) ++ */ ++ else ++ TRACE_MSG3(HCD,"ERROR - act: %08x urb: %08x slot: %d", ++ (u32)(void*)(bus_hcpriv->active_urbs[rc]), (u32)(void*)urb, rc); ++ } ++ local_irq_restore(flags); ++ if (rc < 0) { ++ // Resubmit is OK... ++ if (rc == -2) { ++ TRACE_MSG1(HCD,"no ETDs for urb: %08lx",urb); ++ return 1; ++ } ++ ++ // Complete the urb as invalid. ++ urb->actual_length = 0; ++ hcd_finish_urb(urb,-EINVAL); ++ // And signal to try again. ++ return -1; ++ } ++ return 0; ++} ++ ++STATIC void hcd_queue_start(struct bus_hcpriv *bus_hcpriv, struct dev_hcpriv *dev_hcpriv, int endpoint, int out) ++{ ++ unsigned long flags; ++ struct urb *urb = NULL; ++ int rc; ++ ++ TRACE_MSG2(HCD, "endpoint: %2x %s", endpoint, out ? "OUT" : "IN"); ++ ++ // Paranoia. ++ RETURN_UNLESS(dev_hcpriv && endpoint < 16); ++ do { ++ local_irq_save(flags); ++ if (dev_hcpriv->epq_state_both[OUT(out, endpoint)][endpoint] == EPQ_RUNNING) { ++ // There's already an urb for this device/endpoint in the HW, don't start another. ++ urb = NULL; ++ } ++ else if (!(urb = hcd_next_urb(dev_hcpriv, endpoint, out))) { ++ dev_hcpriv->epq_state_both[OUT(out, endpoint)][endpoint] = EPQ_EMPTY; ++ } ++ else { ++ // There is an urb to go, set the state to show the HW is (will be) busy. ++ dev_hcpriv->epq_state_both[OUT(out, endpoint)][endpoint] = EPQ_RUNNING; ++ } ++ local_irq_restore(flags); ++ if (!urb) { ++ // This endpoint is already active in the HW, or doesn't need to be. ++ rc = 0; ++ } ++ else { ++ // Try to pass the urb down to the HW. ++ rc = hcd_submit_to_hw(bus_hcpriv, urb, out); ++ } ++ } while (rc < 0); ++ if (rc > 0) { ++ // We've run out of HW resources, try again later. ++ local_irq_save(flags); ++ list_add(&urb->urb_list, &dev_hcpriv->queued_urbs_both[OUT(out, endpoint)][endpoint]); ++ dev_hcpriv->num_urbs_both[OUT(out, endpoint)][endpoint] += 1; ++ dev_hcpriv->epq_state_both[OUT(out, endpoint)][endpoint] = EPQ_WAITING; ++ TRACE_MSG3(HCD,"urb: %08x ep: %d n: %u WAITING IN", ++ (u32)(void*)urb, endpoint, dev_hcpriv->num_urbs_both[OUT(out, endpoint)][endpoint]); ++ local_irq_restore(flags); ++ /* There should be no need to queue a task to try later, as the HW ++ * should have lots of pending urbs, and urb completion will lead ++ * to a search for new work. There is a small chance that _all_ the ++ * pending urbs will complete between the call to hcd_submit_to_hw(), ++ * but it's unlikely. ++ */ ++ } ++} ++ ++ ++/* ********************************************************************************************** */ ++/* Functions provided to the HW dependent layer (below this one). ++ */ ++void hcd_transfer_complete(struct bus_hcpriv *bus_hcpriv, int transfer_id, int format, int cc, u32 remaining, int next_toggle) ++{ ++ struct urb *urb = grab_active_urb(bus_hcpriv, transfer_id); ++ struct usb_device *dev; ++ struct usb_bus *bus; ++ struct dev_hcpriv *dev_hcpriv; ++ int out; ++ ++ int endpoint; ++ u8 *cp; ++ ++ UNLESS (urb) { ++ TRACE_MSG1(HCD,"active_urb[%d] NULL",transfer_id); ++ return; ++ } ++ ++ RETURN_UNLESS((dev = urb->dev)); ++ RETURN_UNLESS((bus = dev->bus)); ++ RETURN_UNLESS((dev_hcpriv = (struct dev_hcpriv *) (dev->hcpriv))); ++ ++ out = usb_pipeout(urb->pipe); ++ ++ TRACE_MSG5(HCD,"id: %d status: %d tlen: %d rem: %d buffer: %p", ++ transfer_id, cc, urb->transfer_buffer_length, remaining, urb->transfer_buffer); ++ //if (out) ++ if (urb->transfer_buffer) { ++#if 0 ++ int i; ++ u8 *cp = urb->transfer_buffer; ++ ++ TRACE_MSG1(HCD, "NEXT TX: length: %d", urb->actual_length); ++ ++ for (i = 0; i < urb->actual_length; i+= 8) ++ ++ TRACE_MSG8(HCD, "RECV %02x %02x %02x %02x %02x %02x %02x %02x", ++ cp[i + 0], cp[i + 1], cp[i + 2], cp[i + 3], ++ cp[i + 4], cp[i + 5], cp[i + 6], cp[i + 7] ++ ); ++#endif ++ } ++ ++ if (-ENOENT == cc) { ++ // Cancelled. ++ urb->actual_length = 0; ++ } ++ else { ++ switch (format) { ++ case PIPE_CONTROL: ++ urb->actual_length = urb->transfer_buffer_length - remaining; ++ ++ if ((cp = urb->setup_packet)) ++ TRACE_MSG8(HCD, "SETUP %02x %02x %02x %02x %02x %02x %02x %02x", ++ cp[0], cp[1], cp[2], cp[3], cp[4], cp[5], cp[6], cp[7]); ++ ++ if ((dev == bus_hcpriv->first_dev) && !cc && urb->setup_packet) { ++ struct usb_ctrlrequest *request = (struct usb_ctrlrequest*) urb->setup_packet; ++ switch (request->bRequest) { ++ case USB_REQ_SET_ADDRESS: ++ otg_event(hcd_instance->otg, ADDRESSED, HCD, "HCD COMPLETE ADDRESSED"); ++ break; ++ case USB_REQ_SET_CONFIGURATION: ++ otg_event(hcd_instance->otg, CONFIGURED, HCD, "HCD COMPLETE CONFIGURED"); ++ break; ++ // XXX check for GET_CONFIGURATION and otg descriptor here ++ } ++ } ++ break; ++ ++ case PIPE_BULK: ++ case PIPE_INTERRUPT: ++ if (0 == cc) { ++ // QQSV needed for BULK, what about interrupt? ++ TRACE_MSG4(HCD,"dev: %d ep: %d dir: %d TOGGLE: %d",urb->dev->devnum, ++ usb_pipeendpoint(urb->pipe),usb_pipeout(urb->pipe),next_toggle); ++ usb_settoggle(urb->dev,usb_pipeendpoint(urb->pipe),usb_pipeout(urb->pipe),next_toggle); ++ } ++ urb->actual_length = urb->transfer_buffer_length - remaining; ++ //hcd_trace_mem(HCD,__FUNCTION__,"BULK/INTERRUPT urb",urb->actual_length,urb->transfer_buffer); ++ break; ++ ++ case PIPE_ISOCHRONOUS: ++ urb->actual_length = urb->transfer_buffer_length - remaining; // QQSV ++ break; ++ ++ default: // Paranoia. ++ urb->actual_length = 0; ++ break; ++ } ++ } ++ endpoint = usb_pipeendpoint(urb->pipe); ++ hcd_finish_urb(urb, cc); ++ ++ // Start the next urb in the queue for this device/endpoint. ++ dev_hcpriv->epq_state_both[OUT(out, endpoint)][endpoint] = EPQ_WAITING; ++ ++ TRACE_MSG2(HCD,"queue_start ep: %d %s >>>", endpoint, out ? "OUT" : "IN"); ++ hcd_queue_start(bus_hcpriv, dev_hcpriv, endpoint, out); ++ ++ //TRACE_MSG1(HCD,"queue_start ep: %d <<<",endpoint); ++ // FIXME - in the future, need to kick off a BH to search other EPs/devs for waiting urbs ++ // if (dev_hcpriv->epq_state[endpoint] == EPQ_EMPTY) { ++ // /* This queue is empty, so its HW resources are now available, ++ // kick off BH to search for other EP's and other devs that are waiting. */ ++ // _________ ++ // } ++} ++ ++/* ********************************************************************************************** */ ++/* Functions provided to the USB core layer (above this one). ++ */ ++ ++/* Allocate space for some USB function plugged into this HC or any hub below it. ++ * Return 0 on success, error code (e.g. -ENOMEM) on failure. ++ */ ++STATIC int hcd_allocate(struct usb_device *dev) ++{ ++ struct usb_bus *bus; ++ struct bus_hcpriv *bus_hcpriv; ++ struct dev_hcpriv *dev_hcpriv; ++ int endpoint; ++ ++ TRACE_MSG1(HCD,"entered devnum: %d", dev->devnum); ++ ++ RETURN_ENOMEM_UNLESS((bus = dev->bus)); ++ RETURN_ENOMEM_UNLESS((bus_hcpriv = (struct bus_hcpriv *) bus->hcpriv)); ++ ++ RETURN_ENOMEM_UNLESS(dev_hcpriv = (struct dev_hcpriv *) kmalloc(sizeof(struct dev_hcpriv), GFP_KERNEL)); ++ ++ memset(dev_hcpriv, 0, sizeof(struct dev_hcpriv)); ++ for (endpoint = 0; endpoint < 16; endpoint++) { ++ INIT_LIST_HEAD(&dev_hcpriv->queued_urbs_both[0][endpoint]); ++ INIT_LIST_HEAD(&dev_hcpriv->queued_urbs_both[1][endpoint]); ++ } ++ ++ dev->hcpriv = dev_hcpriv; ++ dev_hcpriv->dev = dev; ++ ++ UNLESS (bus_hcpriv->roothub_dev) ++ bus_hcpriv->roothub_dev = dev; ++ ++ else UNLESS (bus_hcpriv->first_dev) ++ bus_hcpriv->first_dev = dev; ++ ++ return 0; ++} ++ ++ ++STATIC int hcd_deallocate(struct usb_device *dev) ++{ ++ struct usb_bus *bus = dev->bus; ++ struct bus_hcpriv *bus_hcpriv = (struct bus_hcpriv *) bus->hcpriv; ++ struct dev_hcpriv *dev_hcpriv; ++ ++ if (dev == bus_hcpriv->first_dev) ++ bus_hcpriv->first_dev = NULL; ++ ++ if (dev == bus_hcpriv->roothub_dev) ++ bus_hcpriv->roothub_dev = NULL; ++ ++ if ((dev_hcpriv = (struct dev_hcpriv *) (dev->hcpriv))) { ++ dev_hcpriv->dev = NULL; ++ kfree(dev_hcpriv); ++ dev->hcpriv = NULL; ++ } ++ return 0; ++} ++ ++ ++STATIC void *hcd_buffer_alloc(struct usb_bus *usb_bus, size_t size, int mem_flags, dma_addr_t *dma) ++{ ++ return consistent_alloc(0, size, dma); ++} ++ ++STATIC void hcd_buffer_free(struct usb_bus *usb_bus, size_t size, void *addr, dma_addr_t dma) ++{ ++ RETURN_UNLESS(addr); ++ consistent_free(addr,size,dma); ++} ++ ++STATIC int frame_number(struct usb_device *usb_dev) ++{ ++ u32 n = hcd_hw_frame_number((struct bus_hcpriv *)(usb_dev->bus->hcpriv)); ++ TRACE_MSG1(HCD,"-->: %08lx",n); ++ return n; ++} ++ ++STATIC int submit_urb(struct urb *urb, int mem_flags) ++{ ++ struct bus_hcpriv *bus_hcpriv; ++ struct dev_hcpriv *dev_hcpriv; ++ int dev_addr; ++ int endpoint; ++ int out; ++ ++ RETURN_EINVAL_UNLESS (urb && urb->dev && urb->dev->bus); ++ RETURN_EINVAL_UNLESS ((bus_hcpriv = (struct bus_hcpriv *) (urb->dev->bus->hcpriv))); ++ RETURN_EINVAL_UNLESS ((dev_hcpriv = (struct dev_hcpriv *) (urb->dev->hcpriv))); ++ RETURN_EINVAL_IF (bus_hcpriv->terminating); ++ ++ dev_addr = usb_pipedevice(urb->pipe); ++ endpoint = usb_pipeendpoint(urb->pipe); ++ out = usb_pipeout(urb->pipe); ++ ++ RETURN_EPIPE_IF (usb_endpoint_halted(urb->dev, endpoint, out)); ++ ++ /* Divert to root hub if appropriate, (note: NO ref count change). ++ */ ++ if (urb->dev == bus_hcpriv->roothub_dev) { ++ TRACE_MSG4(HCD,"root hub --> urb: %08lx dev: %d ep: %d dir: %s", urb, dev_addr, endpoint, (out ? "OUT" : "IN")); ++ return hcd_rh_submit_urb(bus_hcpriv, urb, mem_flags); ++ } ++ ++ /* Increase ref count so we own a piece of the URB. Queue the urb, ++ * then make sure the queue is running. ++ */ ++ TRACE_MSG4(HCD,"device --> urb: %08lx dev: %d ep: %d dir: %S", urb, dev_addr, endpoint, (out ? "OUT" : "IN")); ++ hcd_queue_urb(dev_hcpriv, usb_get_urb(urb), endpoint, out); ++ hcd_queue_start(bus_hcpriv, dev_hcpriv, endpoint, out); ++ return 0; ++} ++ ++STATIC int unlink_urb(struct urb *urb) ++{ ++ int u; ++ unsigned long flags; ++ struct bus_hcpriv *bus_hcpriv = NULL; ++ struct dev_hcpriv *dev_hcpriv = NULL; ++ int dev_addr; ++ int endpoint; ++ struct list_head *epq; ++ int out; ++ ++ RETURN_EINVAL_UNLESS (urb && urb->dev && urb->dev->bus); ++ RETURN_EINVAL_UNLESS ((dev_hcpriv = (struct dev_hcpriv *) (urb->dev->hcpriv))); ++ RETURN_EINVAL_UNLESS ((bus_hcpriv = (struct bus_hcpriv *) (urb->dev->bus->hcpriv))); ++ ++ out = usb_pipeout(urb->pipe); ++ ++ dev_addr = usb_pipedevice(urb->pipe); ++ ++ /* Does it belong to the root hub? ++ */ ++ if (urb->dev == bus_hcpriv->roothub_dev) { ++ TRACE_MSG1(HCD,"root hub --> urb: %08lx",urb); ++ return hcd_rh_unlink_urb(bus_hcpriv,urb); ++ } ++ ++ /* Look to see if it is in a device/endpoint queue. ++ */ ++ endpoint = usb_pipeendpoint(urb->pipe); ++ ++ epq = &dev_hcpriv->queued_urbs_both[OUT(out, endpoint)][endpoint]; ++ ++ /* Searching a list while in_irq - yuck. For the time being, ++ * this just assumes that the list is short. In the future, ++ * it may be possible to keep a small amount of state in the ++ * urb itself to indicate if it is in this list, so that all ++ * we need to do in_irq is check the state and delete from the ++ * list if the state indicates we should. The state info will ++ * only needed while this HC "owns" the urb. ++ */ ++ local_irq_save(flags); ++ ++ if (EPQ_EMPTY != dev_hcpriv->epq_state_both[OUT(out, endpoint)][endpoint]) { ++ // There are queued urbs, search to see if this is one of them. ++ struct urb *qe; ++ list_for_each_entry(qe, epq, urb_list) { ++ if (qe == urb) { ++ // Found it. It hasn't been sent to the HW yet, so it can be finished here. ++ list_del_init(&urb->urb_list); ++ dev_hcpriv->num_urbs_both[OUT(out, endpoint)][endpoint] -= 1; ++ if (list_empty(epq)) ++ dev_hcpriv->epq_state_both[OUT(out, endpoint)][endpoint] = EPQ_EMPTY; ++ ++ TRACE_MSG3(HCD,"urb: %08x ep: %d n: %u UNLINKED IN", ++ (u32)(void*)urb, endpoint, ++ dev_hcpriv->num_urbs_both[OUT(out, endpoint)][endpoint]); ++ hcd_finish_urb(urb,-ENOENT); ++ local_irq_restore(flags); ++ return 0; ++ } ++ } ++ } ++ ++ local_irq_restore(flags); ++ ++ /* Look to see if this is one of the active urbs. ++ */ ++ if (bus_hcpriv->active_urbs) { ++ ++ for (u = 0; u < bus_hcpriv->max_active_urbs; u++) { ++ local_irq_save(flags); ++ /* This is the one we want, unlink from the HW. ++ * HW layer does the completion through hcd_transfer_complete() ++ * in this file, which releases bus_hcpriv->active_urbs[u]. ++ */ ++ if (urb == bus_hcpriv->active_urbs[u]) { ++ int out; ++ out = usb_pipeout(urb->pipe); ++ u = hcd_hw_unlink_urb(bus_hcpriv, u); ++ ++ TRACE_MSG4(HCD,"urb: %08x ep: %d n: %u UNLINKED from HW %s", (u32)(void*)urb, endpoint, ++ dev_hcpriv->num_urbs_both[OUT(out, endpoint)][endpoint], out ? "OUT" : "IN"); ++ ++ local_irq_restore(flags); ++ // FUTURE - think about starting a search for new work for the released HW slot. ++ return u; ++ } ++ local_irq_restore(flags); ++ } ++ } ++ // The urb isn't ours, or completed while we were looking for it. ++ TRACE_MSG1(HCD,"Already completed, or someone else's urb: %08lx", urb); ++ return -EINVAL; ++} ++ ++/* ********************************************************************************************** */ ++ ++// void disable(struct usb_device *dev, int bEndpointAddress); ++ ++static struct usb_operations operations = { ++ .allocate = hcd_allocate, ++ .deallocate = hcd_deallocate, ++ .get_frame_number = frame_number, ++ .submit_urb = submit_urb, ++ .unlink_urb = unlink_urb, ++ .buffer_alloc = hcd_buffer_alloc, /* allocate dma-consistent buffer for URB_DMA_NOMAPPING */ ++ .buffer_free = hcd_buffer_free, ++ .disable = NULL ++}; ++ ++ ++/* ********************************************************************************************** */ ++ ++/* Called as a "bottom-half" to do usbcore registration once all the HW is usable. ++ */ ++STATIC void hcd_bh_init(void *arg) ++{ ++ struct hcd_instance *hcd = (struct hcd_instance *) arg; ++ otg_event(hcd->otg, HCD_OK, HCD, "HCD_INIT SET HCD_OK"); ++ TRACE_MSG0(HCD,"finished"); ++} ++ ++STATIC void hcd_bh_exit(void *arg) ++{ ++ struct hcd_instance *hcd = (struct hcd_instance *) arg; ++ otg_event(hcd->otg, HCD_OK, HCD, "HCD_INIT RESET (EXIT) HCD_OK"); ++} ++ ++ ++/*! hcd_init_func - per host controller common initialization ++ * ++ * This is called to initialize / de-initialize the HCD, all except the last ++ * stage of registering the root hub, because that needs to wait until rh_hcd_en_func() ++ * ++ * We start work items to do this. ++ * ++ */ ++void hcd_init_func (struct otg_instance *otg, u8 flag) ++{ ++ struct hcd_instance *hcd = otg->hcd; ++ ++ switch (flag) { ++ case SET: ++ // Schedule BH for hcd_bh_init... ++ TRACE_MSG0(HCD, "HCD_INIT: SET"); ++ PREPARE_WORK_ITEM(hcd->bh, hcd_bh_init, hcd); ++ SCHEDULE_WORK(hcd->bh); ++ TRACE_MSG0(HCD, "hcd_bh_init() schedule finished"); ++ break; ++ ++ case RESET: ++ TRACE_MSG0(HCD, "HCD_INIT: RESET"); ++ PREPARE_WORK_ITEM(hcd->bh, hcd_bh_exit, hcd); ++ SCHEDULE_WORK(hcd->bh); ++ break; ++ } ++} ++ ++ ++/* ********************************************************************************************** */ ++ ++STATIC void release_hw_rh(struct bus_hcpriv *bus_hcpriv) ++{ ++ RETURN_UNLESS (bus_hcpriv); ++ ++ hcd_rh_exit(bus_hcpriv); ++ ++ // Complete (cancel) any active urbs. ++ if (bus_hcpriv->active_urbs) { ++ int u; ++ for (u = 0; u < bus_hcpriv->max_active_urbs; u++) { ++ struct urb *urb; ++ if ((urb = grab_active_urb(bus_hcpriv,u))) { ++ urb->actual_length = 0; ++ hcd_finish_urb(urb,-ENOENT); // USB_ST_URB_KILLED ++ } ++ } ++ } ++ ++ // Release any resources from the layer above. ++ if (bus_hcpriv->usb_bus) { ++ usb_deregister_bus(bus_hcpriv->usb_bus); ++ bus_hcpriv->usb_bus = NULL; ++ } ++ ++ // Lastly, release this layer's info. ++ if (bus_hcpriv->active_urbs) { ++ kfree(bus_hcpriv->active_urbs); ++ bus_hcpriv->active_urbs = NULL; ++ } ++} ++ ++/* ********************************************************************************************** */ ++/* ++ * Note that initialization and cleanup happen in two stages: ++ * 1. Allocation of the basic struct bus_hcpriv, the first level of LDM/USB driver registration, and OTG operations ++ * 2. Everything else - completion of data allocation, USBcore registration, HC HW init, root hub enumeration - when ++ * called back by OTG (after any magic required to access the HCD hardware.) ++ * Only stage 1 is done on module entry. ++ */ ++ ++int __init hcd_init(struct bus_hcpriv *bus_hcpriv, struct usb_driver *usb_driver) ++{ ++ struct usb_bus *usb_bus = NULL; ++ int usb_registered = 0; ++ int device_registered = 0; ++ int bus_registered = 0; ++ int rc = 0; ++ struct urb **active_urbs = NULL; ++ ++ /* fixup bus_hcpriv structure ++ */ ++ bus_hcpriv->usb_driver = usb_driver; ++ bus_hcpriv->bus_device.driver = &usb_driver->driver; ++ bus_hcpriv->max_active_urbs = bus_hcpriv->max_active_transfers; ++ usb_driver->probe = hcd_probe; ++ ++ UNLESS (hcd_ops.hcd_init_func) hcd_ops.hcd_init_func = hcd_init_func; ++ ++ /* allocate urb array ++ */ ++ THROW_UNLESS ((active_urbs = (struct urb **) kmalloc(bus_hcpriv->max_active_urbs*sizeof(struct urb *),GFP_KERNEL)), ++ error); ++ ++ bus_hcpriv->active_urbs = active_urbs; ++ memset(bus_hcpriv->active_urbs,0,bus_hcpriv->max_active_urbs*sizeof(struct urb *)); ++ TRACE_MSG1(HCD,"active_urbs ok, max active URBs: %d",bus_hcpriv->max_active_urbs); ++ ++ /* register with usb core ++ */ ++ THROW_UNLESS(usb_registered = usb_register(usb_driver) ? 0 : 1, error); ++ TRACE_MSG0(HCD,"usb_register ok"); ++ ++ /* We need a bus before we can initialize the root hub. ++ */ ++ THROW_UNLESS ((usb_bus = usb_alloc_bus(&operations)), error); ++ bus_hcpriv->usb_bus = usb_bus; ++ TRACE_MSG0(HCD,"usb bus allocated"); ++ ++ /* register bus ++ */ ++ snprintf(bus_hcpriv->bus_device.bus_id, BUS_ID_SIZE, "bus_hcpriv%d", usb_bus->busnum); ++ snprintf(bus_hcpriv->bus_device.name, DEVICE_NAME_SIZE,"%s%d", bus_hcpriv->usb_driver->name, usb_bus->busnum); ++ usb_bus->hcpriv = (void *) bus_hcpriv; ++ usb_bus->controller = &bus_hcpriv->bus_device; ++ THROW_UNLESS ((bus_registered = usb_register_bus(usb_bus)) ? 0 : 1, error); ++ TRACE_MSG0(HCD,"usb bus registered"); ++ ++ /* All set for the host controller, initialize the virtual root hub. ++ */ ++ THROW_IF (hcd_rh_init(bus_hcpriv), error); ++ TRACE_MSG0(HCD,"root hub initialized"); ++ ++ /* register device ++ */ ++ //THROW_IF ((device_registered = device_register(&bus_hcpriv->bus_device) ? 0 : 1), error); ++ if (device_register(&bus_hcpriv->bus_device)) { ++ printk(KERN_ERR"%s: device_register not ok\n", __FUNCTION__); ++ } ++ ++ /* Everybody's ready, enable the interrupts. ++ */ ++ ++ /* Kick off the root hub. ++ */ ++ THROW_IF ((rc = usb_register_root_hub(bus_hcpriv->rh_hcpriv->dev, &(bus_hcpriv->bus_device))), error); ++ ++ return 0; ++ ++ CATCH(error) { ++ ++ if (device_registered) device_unregister(&bus_hcpriv->bus_device); ++ if (usb_bus) { ++ if (bus_registered && usb_bus) usb_deregister_bus(usb_bus); ++ kfree(usb_bus); ++ } ++ if (usb_registered) usb_deregister(usb_driver); ++ if (active_urbs) kfree(active_urbs); ++ return -EINVAL; ++ } ++} ++ ++#ifdef MODULE ++void hcd_exit(struct bus_hcpriv *bus_hcpriv, struct usb_driver *usb_driver) ++{ ++ release_hw_rh(bus_hcpriv); ++ ++ usb_deregister_bus(bus_hcpriv->usb_bus); ++ device_unregister(&bus_hcpriv->bus_device); ++ usb_deregister(usb_driver); ++} ++#endif ++ +diff -uNr linux/drivers/no-otg/ocd/otg-hcd/hcd-rh.c linux/drivers/otg/ocd/otg-hcd/hcd-rh.c +--- linux/drivers/no-otg/ocd/otg-hcd/hcd-rh.c 1970-01-01 01:00:00.000000000 +0100 ++++ linux/drivers/otg/ocd/otg-hcd/hcd-rh.c 2006-09-01 21:41:32.000000000 +0200 +@@ -0,0 +1,1068 @@ ++/* ++ * otg/ocd/otg-hcd/hcd-rh.c - Generic transfer level USBOTG aware ++ * virtual root hub _FUNCTION_ driver. ++ * ++ * Copyright (c) 2004 Belcarra Technologies ++ * ++ * By: ++ * Tom Rushworth <tbr@belcara.com>, ++ * Stuart Lynne <sl@belcara.com>, ++ * Bruce Balden <balden@belcara.com> ++ */ ++/*! ++ * @file otg/ocd/otg-hcd/hcd-rh.c ++ * @brief Root Hub for Generic Host Controller Driver ++ * ++ * ++ * ++ * @ingroup OTGHCD ++ */ ++ ++#include <linux/config.h> ++#include <linux/module.h> ++#include <linux/kernel.h> ++#include <linux/slab.h> ++#include <linux/errno.h> ++#include <linux/usb.h> ++ ++#include <otg/otg-compat.h> ++#include "../core/hcd.h" ++ ++#include <otg/usbp-chap9.h> ++#include <otg/otg-trace.h> ++#include <otg/otg-api.h> ++#include <otg/otg-utils.h> ++#include <otg/otg-hcd.h> ++ ++#include <otg/hcd-l26.h> ++#include <otg/hcd-rh.h> ++#include <otg/hcd-hw.h> ++ ++#define STATIC static ++ ++/* NOTE: This code assumes the root hub has < 8 ports. If it has ++ more some of the descriptors and other structures will have to ++ be re-sized. */ ++ ++STATIC u8 prepare_str(char *ascii, u8 *str, int str_size) ++{ ++ int c; ++ u8 *up = str; ++ u8 *ep = (up + str_size) - 1; ++ while ((c = *ascii++) && up < ep) { ++ *up++ = (c & 0x7F); ++ *up++ = 0; ++ } ++ return (u8)(up - str); ++} ++ ++STATIC int hcd_rh_string(struct bus_hcpriv *bus_hcpriv, int strno, u8 *buff, int buff_len) ++{ ++ // Provide default strings, but let the HW component override them. ++ char *str; ++ switch (strno) { ++ case 0: // Language ID ++ *buff++ = 4; ++ *buff++ = USB_DT_STRING; ++ *buff++ = 0; ++ *buff++ = 0; ++ return 4; ++ case XHC_RH_STRING_CONFIGURATION: ++ str = "Root hub"; ++ break; ++ case XHC_RH_STRING_INTERFACE: ++ str = "Root hub - Status"; ++ break; ++ case XHC_RH_STRING_SERIAL: ++ str = bus_hcpriv->rh_serial ? bus_hcpriv->rh_serial : "000001"; ++ break; ++ case XHC_RH_STRING_PRODUCT: ++ str = bus_hcpriv->rh_product ? bus_hcpriv->rh_product : "Virtual Root Hub"; ++ break; ++ case XHC_RH_STRING_MANUFACTURER: ++ str = bus_hcpriv->rh_manufacturer ? bus_hcpriv->rh_manufacturer : "Belcarra Technologies"; ++ break; ++ default: ++ str = "????"; ++ } ++ ++ buff[0] = 2 + prepare_str(str,buff+2,buff_len-2); ++ buff[1] = USB_DT_STRING; ++ return buff[0]; ++} ++ ++ ++/* ********************************************************************************************** */ ++ ++ ++static char *hub_feature_name[] = { ++ "C_HUB_LOCAL_POWER", ++ "C_HUB_OVER_POWER", ++}; ++ ++STATIC void hcd_rh_hub_feature(struct bus_hcpriv *bus_hcpriv, u16 wValue, int set_flag) ++{ ++ unsigned long flags; ++ TRACE_MSG3(HCD,"%s feature %d %s",(set_flag?"SET":"CLR"), wValue, hub_feature_name[wValue]); ++ local_irq_save(flags); ++ if (set_flag) { ++ // SET feature USB2.0 11.24.2.12 pg 434 and USB2.0 11.24.2 tbl 11-17 pg 421 ++ // R1: sec 23.11.29 pg 23-62 ++ switch (wValue) { ++ // case C_HUB_OVER_CURRENT: ____ FIXME ---- ++ // case C_HUB_LOCAL_POWER: ____ FIXME ---- ++ default: ++ // Error, but ignore. ++ TRACE_MSG1(HCD,"SET invalid FEATURE %d", wValue); ++ break; ++ } ++ } ++ else { ++ // CLEAR feature USB2.0 11.24.2.1 pg 422 and USB2.0 11.24.2 tbl 11-17 pg 421 ++ // R1: sec 23.11.29 pg 23-62 ++ switch (wValue) { ++ case C_HUB_OVER_CURRENT: ++ bus_hcpriv->rh_hcpriv->hub_change_status &= ~(1 << wValue); ++ TRACE_MSG1(HCD,"CLR C_HUB_OVER_CURRENT hub_change_status: %08x", bus_hcpriv->rh_hcpriv->hub_change_status); ++ break; ++ // case C_HUB_LOCAL_POWER: ____ FIXME ---- ++ default: ++ // Error, but ignore. ++ TRACE_MSG1(HCD,"CLR invalid FEATURE %d", wValue); ++ break; ++ } ++ } ++ local_irq_restore(flags); ++ hcd_hw_rh_hub_feature(bus_hcpriv, wValue, set_flag); ++} ++ ++/* ********************************************************************************************** */ ++ ++char *port_feature_name[] = { ++ "PORT_CONNECTION", "PORT_ENABLE", "PORT_SUSPEND", "FEATURE 0x03", ++ "PORT_RESET", "FEATURE 0x05", "FEATURE 0x06", "FEATURE 0x07", ++ "PORT_POWER", "PORT_LOW_SPEED", "FEATURE 0x0a", "FEATURE 0x0b", ++ "FEATURE 0x0c", "FEATURE 0x0d", "FEATURE 0x0e", "FEATURE 0x0f", ++ "C_PORT_CONNECTION", "C_PORT_ENABLE", "C_PORT_SUSPEND", "C_PORT_OVER_CURRENT", ++ "C_PORT_RESET", "PORT_TEST", ++}; ++ ++void check_port_status_(int feature, u32 change_status, u32 hw_change_status) ++{ ++ int mask = (1 << feature); ++ if ((change_status & mask) != (hw_change_status & mask)) ++ TRACE_MSG4(HCD, "%-20s %2d %6s %6s MISMATCH", port_feature_name[feature], feature, ++ change_status & mask ? "SET" : "RESET", hw_change_status & mask ? "SET" : "RESET" ); ++} ++ ++void check_port_status(struct bus_hcpriv *bus_hcpriv, u16 wIndex, char *msg) ++{ ++ struct rh_hcpriv *rh_hcpriv = bus_hcpriv->rh_hcpriv; ++ u32 change_status = ((wIndex -1) < bus_hcpriv->num_ports) ? bus_hcpriv->rh_hcpriv->port_change_status[wIndex-1] : 0; ++ u32 hw_change_status = hcd_hw_rh_get_port_change_status(bus_hcpriv, wIndex); ++ ++ TRACE_MSG4(HCD,"PORT_STATUS[%2d] %08x %08x %s", wIndex, ++ change_status, hcd_hw_rh_get_port_change_status(bus_hcpriv, wIndex), msg); ++ ++ UNLESS(rh_hcpriv->suspended) { ++ // XXX ++ } ++ ++ check_port_status_(PORT_CONNECTION, change_status, hw_change_status); ++ check_port_status_(PORT_ENABLE, change_status, hw_change_status); ++ check_port_status_(PORT_RESET, change_status, hw_change_status); ++ check_port_status_(PORT_POWER, change_status, hw_change_status); ++ check_port_status_(C_PORT_CONNECTION, change_status, hw_change_status); ++ check_port_status_(C_PORT_ENABLE, change_status, hw_change_status); ++ check_port_status_(C_PORT_RESET, change_status, hw_change_status); ++} ++/* ********************************************************************************************** */ ++/* ++ * wIndex is port number (1-N) ++ * wValue is feature selector ++ */ ++STATIC void hcd_rh_port_feature(struct bus_hcpriv *bus_hcpriv, u16 wValue, u16 wIndex, int set_flag) ++{ ++ TRACE_MSG4(HCD,"%s port %d FEATURE %02x %s", (set_flag?"SET":"CLR"), wIndex, ++ wValue, port_feature_name[wValue]); ++ /* Not a valid port number ++ */ ++ if (wIndex > bus_hcpriv->num_ports) { ++ TRACE_MSG1(HCD, "invalid port: %d", wIndex); ++ return; ++ } ++ /* Feature selector is wValue ++ * wIndex is 1-origin ++ */ ++ TRACE_MSG2(HCD, "port_change_status[%d]: -> %08x", wIndex, bus_hcpriv->rh_hcpriv->port_change_status[wIndex -1]); ++ if (set_flag) { ++ // SET feature ++ switch (wValue) { ++ case PORT_SUSPEND: ++ case PORT_RESET: ++ case PORT_POWER: ++ bus_hcpriv->rh_hcpriv->port_change_status[wIndex - 1] |= (1 << wValue); ++ break; ++ } ++ } ++ else { ++ // CLEAR feature (valid features from USB2.0 11.24.2.2 pg 423). ++ switch (wValue) { ++ case PORT_ENABLE: // Disable port. ++ case PORT_SUSPEND: // Cause a Host initiated resume, or no-op if already active. ++ case PORT_POWER: // Put port in powered-off state. ++ case C_PORT_CONNECTION: // clear the PORT_CONNECTION change bit ++ case C_PORT_RESET: // clear the PORT_RESET change bit ++ case C_PORT_ENABLE: // clear the PORT_ENABLE change bit ++ case C_PORT_SUSPEND: // clear the PORT_SUSPEND change bit ++ case C_PORT_OVER_CURRENT: // clear the PORT_OVERCURRENT change bit ++ bus_hcpriv->rh_hcpriv->port_change_status[wIndex - 1] &= ~(1 << wValue); ++ break; ++ } ++ } ++ /* update hardware? ++ */ ++ ++ RETURN_UNLESS (wIndex == bus_hcpriv->otg_port); ++ RETURN_IF (bus_hcpriv->rh_hcpriv->otg_device_mask & (1 << wIndex)); ++ ++ hcd_hw_rh_port_feature(bus_hcpriv, wValue, wIndex, set_flag); ++ ++ TRACE_MSG4(HCD, "OTG Port wValue: %x wIndex: %d %d set: %d", wValue, wIndex, bus_hcpriv->otg_port, set_flag); ++ if (set_flag) { ++ // SET feature ++ switch (wValue) { ++ case PORT_RESET: ++ TRACE_MSG0(HCD, "OTG Port - HUB_PORT_ENABLED - PORT_RESET"); ++ //otg_event(bus_hcpriv->hcd->otg, HUB_PORT_ENABLED, HCD, "Hub Port Enable (PORT_RESET)"); ++ otg_event(hcd_instance->otg, BUS_RESET, HCD, "PORT_RESET (SET Feature)"); ++ break; ++ case PORT_SUSPEND: ++ case PORT_POWER: ++ break; ++ } ++ check_port_status(bus_hcpriv, wIndex, "PORT SET"); ++ } ++ else { ++ // CLEAR feature (valid features from USB2.0 11.24.2.2 pg 423). ++ switch (wValue) { ++ case C_PORT_CONNECTION: // clear the PORT_CONNECTION change bit ++ TRACE_MSG0(HCD, "OTG Port - HUB_PORT_ENABLED/ - C_PORT_CONNECTION"); ++ break; ++ case C_PORT_ENABLE: // clear the PORT_ENABLE change bit ++ TRACE_MSG0(HCD, "OTG Port - HUB_PORT_ENABLED/ - C_PORT_ENABLE"); ++ break; ++ case PORT_ENABLE: // Disable port. ++ case PORT_SUSPEND: // Cause a Host initiated resume, or no-op if already active. ++ case PORT_POWER: // Put port in powered-off state. ++ case C_PORT_RESET: // clear the PORT_RESET change bit ++ case C_PORT_SUSPEND: // clear the PORT_SUSPEND change bit ++ case C_PORT_OVER_CURRENT: // clear the PORT_OVERCURRENT change bit ++ break; ++ } ++ check_port_status(bus_hcpriv, wIndex, "PORT CLR"); ++ } ++} ++ ++/* ********************************************************************************************** */ ++/* ++ * Functions for periodic response to the INT URB. ++ * Note that the USB2.0 spec says (sec 11.12.2 pg 337) that a hub controller ++ * will respond to an INT poll as long as the change bits remain set. This ++ * means we have to record them and continue responding periodically (hence ++ * the timer) until the class driver clears them. ++ * ++ */ ++ ++void hcd_rh_urb_complete(struct bus_hcpriv *bus_hcpriv, struct urb *urb) ++{ ++ // Almost not needed (vrh could do this directly), except for tracing. ++ TRACE_MSG1(HCD,"urb %p",urb); ++ ++ /* Call the upper layer completion routine. ++ */ ++ if (urb->complete) ++ urb->complete(urb,NULL); ++ ++ // XXX should we do this here ++ // usb_put_urb(int_urb); ++} ++/* ********************************************************************************************** */ ++/* ++ * Some sort of change may have happened on the root hub or one of the ports, ++ * scan for changes in the shadow data, and notify usbcore if there is an ++ * INT URB available to do so with. ++ */ ++STATIC void hcd_rh_portstatus_bh(void *data) ++{ ++ struct bus_hcpriv *bus_hcpriv = (struct bus_hcpriv *) data; ++ struct rh_hcpriv *rh_hcpriv = bus_hcpriv->rh_hcpriv; ++ u8 change_data; ++ struct urb *int_urb; ++ ++ u32 hub_port_change_status; ++ unsigned long flags; ++ ++ TRACE_MSG1(HCD, "suspended: %d", rh_hcpriv->suspended); ++ RETURN_IF(rh_hcpriv->suspended); ++ ++ //printk(KERN_INFO"%s:\n", __FUNCTION__); ++ ++ local_irq_save(flags); ++ hub_port_change_status = bus_hcpriv->rh_hcpriv->hub_port_change_status; ++ bus_hcpriv->rh_hcpriv->hub_port_change_status = 0; ++ local_irq_restore(flags); ++ ++ ++ change_data = (u8) hub_port_change_status & 0xff; ++ //change_data ~= ~bus_hcpriv->rh_hcpriv->otg_device_mask; ++ ++ RETURN_UNLESS (change_data && (int_urb = rh_hcpriv->int_urb)); ++ ++ TRACE_MSG1(HCD,"non-zero change_data: %02x", change_data); ++ ++ change_data |= 1; // set hub change bit XXX check ++ ++ // Completing the urb will "use it up", so remove the link to it. ++ ++ del_timer(&rh_hcpriv->poll_timer); ++ rh_hcpriv->int_urb = NULL; ++ ++ *((u8 *)(int_urb->transfer_buffer)) = change_data; ++ int_urb->actual_length = 1; ++ ++ /* Completing an INT urb will cause it to be immediately re-submitted, so this will call submit_urb() ++ */ ++ hcd_rh_urb_complete(bus_hcpriv, int_urb); ++ usb_put_urb(int_urb); ++} ++/* ********************************************************************************************** */ ++STATIC void poll_int_urb(unsigned long data) ++{ ++ struct bus_hcpriv *bus_hcpriv = (struct bus_hcpriv *) (void *) data; ++ unsigned long flags; ++ ++ RETURN_UNLESS (bus_hcpriv && bus_hcpriv->rh_hcpriv); ++ ++ local_irq_save(flags); ++ PREPARE_WORK_ITEM(bus_hcpriv->rh_hcpriv->psc_bh, hcd_rh_portstatus_bh, bus_hcpriv); ++ SCHEDULE_WORK(bus_hcpriv->rh_hcpriv->psc_bh); ++ local_irq_restore(flags); ++} ++/* ********************************************************************************************** */ ++STATIC u32 rh_get_port_change_status(struct bus_hcpriv *bus_hcpriv, u16 wIndex) ++{ ++ if ((wIndex != bus_hcpriv->otg_port) || !(bus_hcpriv->rh_hcpriv->otg_device_mask)) ++ return hcd_hw_rh_get_port_change_status(bus_hcpriv, wIndex); ++ ++ TRACE_MSG0(HCD, "USING SHADOW INFORMATION"); ++ return bus_hcpriv->rh_hcpriv->port_change_status[wIndex-1]; ++} ++ ++/* ++ * hcd_rh_submit_urb() ++ * Returns: values as for the generic submit_urb() function, see comp_code_to_status(int cc). ++ */ ++int hcd_rh_submit_urb(struct bus_hcpriv *bus_hcpriv, struct urb *urb, int mem_flags) ++{ ++ struct rh_hcpriv *rh_hcpriv; ++ unsigned int pipe = urb->pipe; ++ struct usbd_device_request *req; ++ u8 bRequest; ++ u8 bmRequestType; ++ u16 wValue; ++ u16 wIndex; ++ u16 wLength; ++ u8 direction; ++ u8 recipient; ++ u8 type; ++ int rc = 0; ++ int rc_stall = -EPIPE; ++ void *reply = NULL; ++ int rlen = 0; ++ u16 status[2]; ++ ++ static rh_urb; ++ ++ TRACE_MSG1(HCD,"pipe: %08x", pipe); ++ ++ ++ //printk(KERN_INFO"%s: rh_urb: %d\n", __FUNCTION__); ++ TRACE_MSG1(HCD, "rh_urb: %d", rh_urb++); ++ ++ RETURN_ETIMEDOUT_UNLESS(bus_hcpriv && bus_hcpriv->rh_hcpriv); ++ ++ rh_hcpriv = bus_hcpriv->rh_hcpriv; ++ ++ if (usb_pipeint(pipe)) { ++ // This must be our interrupt endpoint poll. ++ TRACE_MSG1(HCD,"interrupt polling urb: %08lx",urb); ++ urb->status = 0; ++ ++ // Increment the ref count, we're keeping this for a while. ++ usb_get_urb(urb); ++ if (rh_hcpriv->int_urb) { ++ // Hmm, a new one while we still have the old...Shouldn't happen, and doesn't work if it does :(. ++ //printk(KERN_INFO "%s: releasing old: %08x\n",__FUNCTION__,(u32)(void*)rh_hcpriv->int_urb); ++ del_timer(&rh_hcpriv->poll_timer); ++ usb_put_urb(rh_hcpriv->int_urb); ++ } ++ ++ rh_hcpriv->int_urb = urb; ++ /* Schedule a scan for changes in case anything happened while there was no urb. ++ * This has to be slightly delayed, in case this urb is a response to a completed ++ * scan - usbcore submits a new int urb before clearing the hub/port change status, ++ * relying on the int urb polling delay to give it time to do the clearing. ++ */ ++ //printk(KERN_INFO "%s: scheduling PSC poll\n",__FUNCTION__); ++ //init_poll_timer(bus_hcpriv); ++ init_timer(&rh_hcpriv->poll_timer); ++ rh_hcpriv->poll_timer.function = poll_int_urb; ++ rh_hcpriv->poll_timer.data = (unsigned long) (void *) bus_hcpriv; ++ rh_hcpriv->poll_timer.expires = jiffies + ++ ((((rh_hcpriv->int_urb->interval < 10) ? 10 : rh_hcpriv->int_urb->interval) * HZ) / 1000); ++ add_timer(&rh_hcpriv->poll_timer); ++ return rc; ++ } ++ ++ TRACE_MSG1(HCD,"req: %08x",(u32)(void*)urb->setup_packet); ++ RETURN_EINVAL_UNLESS((req = (struct usbd_device_request *) urb->setup_packet)) ++ ++ bRequest = req->bRequest; ++ bmRequestType = req->bmRequestType; ++ wValue = le16_to_cpu(req->wValue); ++ wIndex = le16_to_cpu(req->wIndex); ++ wLength = le16_to_cpu(req->wLength); ++ direction = bmRequestType & USB_REQ_DIRECTION_MASK; ++ recipient = bmRequestType & USB_REQ_RECIPIENT_MASK; ++ type = bmRequestType & USB_REQ_TYPE_MASK; ++ ++ TRACE_MSG8(HCD,"urb: %08lx req: %02x reqType: %02x val=%u ndx=%u len=%u dir: %02x recip: %02x", ++ urb,bRequest,bmRequestType, wValue,wIndex,wLength,direction,recipient); ++ ++ if (USB_REQ_HOST2DEVICE == direction) { ++ /* these do not require a reply */ ++ int set_flag = (USB_REQ_SET_FEATURE == bRequest); ++ TRACE_MSG0(HCD,"H->D"); ++ //printk(KERN_INFO "%s: H->D\n",__FUNCTION__); ++ urb->actual_length = 0; ++ switch (recipient) { ++ case USB_RECIP_HUB: ++ switch (bRequest) { ++ case USB_REQ_CLEAR_FEATURE: ++ case USB_REQ_SET_FEATURE: ++ if (USB_REQ_TYPE_CLASS == type) { ++ TRACE_MSG2(HCD,"%s hub feature: %04x",(set_flag?"set":"clear"),wValue); ++ //printk(KERN_INFO "%s hub feature: %04x\n",(set_flag?"set":"clear"),wValue); ++ hcd_rh_hub_feature(bus_hcpriv, wValue, set_flag); ++ } ++ else { ++ TRACE_MSG2(HCD,"HUB C/S feature invalid type r: %02x t: %02x",bRequest,bmRequestType); ++ rc = rc_stall; ++ } ++ break; ++ case USB_REQ_SET_ADDRESS: ++ if (USB_REQ_TYPE_STANDARD == type) { ++ TRACE_MSG1(HCD,"set address %u",wValue); ++ //printk(KERN_INFO "set address %u\n",wValue); ++ bus_hcpriv->root_hub_addr = wValue; ++ } ++ else { ++ TRACE_MSG2(HCD,"HUB SETADDRESS invalid type r: %02x t: %02x",bRequest,bmRequestType); ++ rc = rc_stall; ++ } ++ break; ++ case USB_REQ_SET_CONFIGURATION: ++ if (USB_REQ_TYPE_STANDARD == type) { ++ TRACE_MSG1(HCD,"set configuration: %02x",wValue); ++ //printk(KERN_INFO "set configuration: %02x\n",wValue); ++ rh_hcpriv->curr_cfg = (u8) (0xFF & wValue); ++ } ++ else { ++ TRACE_MSG2(HCD,"HUB SETCONFIG invalid type r: %02x t: %02x",bRequest,bmRequestType); ++ rc = rc_stall; ++ } ++ break; ++ case USB_REQ_SET_INTERFACE: ++ // wIndex is interface number ++ if (USB_REQ_TYPE_STANDARD == type) { ++ TRACE_MSG2(HCD,"set interface: %02x to : %02x",wIndex,wValue); ++ //printk(KERN_INFO "set interface: %02x to : %02x\n",wIndex,wValue); ++ rh_hcpriv->curr_itf = (u8) (0xFF & wValue); ++ } ++ else { ++ TRACE_MSG2(HCD,"HUB SETINTERFACE invalid type r: %02x t: %02x",bRequest,bmRequestType); ++ rc = rc_stall; ++ } ++ break; ++ default: ++ TRACE_MSG2(HCD,"HUB invalid request r: %02x t: %02x",bRequest,bmRequestType); ++ rc = rc_stall; ++ } ++ break; ++ case USB_RECIP_PORT: ++ switch (bRequest) { ++ case USB_REQ_CLEAR_FEATURE: ++ case USB_REQ_SET_FEATURE: ++ // At this point wIndex == port_num is 1-origin. ++ if (USB_REQ_TYPE_CLASS == type && wIndex <= bus_hcpriv->num_ports && wIndex > 0) { ++ TRACE_MSG3(HCD,"%s port %u FEATURE: %04x",(set_flag?"set":"clear"),wIndex,wValue); ++ hcd_rh_port_feature(bus_hcpriv, wValue, wIndex, set_flag); ++ } ++ else { ++ TRACE_MSG2(HCD,"PORT C/S FEATURE invalid type r: %02x t: %02x",bRequest,bmRequestType); ++ rc = rc_stall; ++ } ++ break; ++ default: ++ TRACE_MSG2(HCD,"PORT invalid request r: %02x t: %02x",bRequest,bmRequestType); ++ rc = rc_stall; ++ } ++ break; ++ case USB_REQ_RECIPIENT_ENDPOINT: ++ // Clear stalled endpoint ++ if (USB_REQ_CLEAR_FEATURE != bRequest || 1 != wValue || ++ USB_REQ_TYPE_STANDARD != type) { // QQSV verify type correct ++ TRACE_MSG3(HCD,"ENDPOINT invalid request,val,type r: %02x t: %02x val=%u", ++ bRequest,bmRequestType,wValue); ++ rc = rc_stall; ++ } ++ // Nothing to really do to "clear" the stalled EP. ++ TRACE_MSG2(HCD,"clear endpoint ndx: %02x val: %02x",wIndex,wValue); ++ //printk(KERN_INFO "clear endpoint ndx: %02x val: %02x\n",wIndex,wValue); ++ break; ++ case USB_REQ_RECIPIENT_INTERFACE: ++ default: ++ TRACE_MSG2(HCD,"H->D invalid recipient r: %02x t: %02x",bRequest,bmRequestType); ++ rc = rc_stall; ++ } ++ } ++ else { ++ /* these do require a reply */ ++ TRACE_MSG0(HCD,"H<-D"); ++ //printk(KERN_INFO "%s: H<-D\n",__FUNCTION__); ++ switch (recipient) { ++ case USB_RECIP_HUB: ++ switch (bRequest) { ++ case USB_REQ_GET_DESCRIPTOR: ++ if (USB_REQ_TYPE_CLASS == type) { ++ // Return the hub class descriptor ++ TRACE_MSG0(HCD,"get hub class descriptor"); ++ //printk(KERN_INFO "get hub class descriptor\n"); ++ reply = (void *) rh_hcpriv->hd; ++ rlen = rh_hcpriv->hd->bDescLength; ++ } ++ else if (USB_REQ_TYPE_STANDARD != type) { ++ TRACE_MSG2(HCD,"HUB GETDESCRIPTOR invalid type r: %02x t: %02x",bRequest,bmRequestType); ++ rc = rc_stall; ++ } ++ else { ++ // Return one of the other descriptors ++ switch (wValue >> 8) { ++ case USB_DT_DEVICE: ++ TRACE_MSG0(HCD,"get device descriptor"); ++ //printk(KERN_INFO "get device descriptor\n"); ++ reply = (void *) rh_hcpriv->dd; ++ rlen = rh_hcpriv->dd->bLength; ++ break; ++ case USB_DT_CONFIGURATION: ++ TRACE_MSG0(HCD,"get configuration descriptor"); ++ reply = (void *) rh_hcpriv->cd; ++ rlen = USB_DT_CONFIG_SIZE+USB_DT_INTERFACE_SIZE+USB_DT_ENDPOINT_SIZE; ++ break; ++ case USB_DT_STRING: ++ TRACE_MSG1(HCD,"get string descriptor %u",wValue); ++ reply = urb->transfer_buffer; ++ rlen = hcd_rh_string(bus_hcpriv,(wValue & 0xFF),reply,wLength); ++ break; ++ default: ++ TRACE_MSG1(HCD,"HUB GETDESCRIPTOR invalid wValue: %04x",wValue); ++ rc = rc_stall; ++ } ++ } ++ break; ++ case USB_REQ_GET_STATUS: ++ if (USB_REQ_TYPE_CLASS == type) { ++ u32 change_status = bus_hcpriv->rh_hcpriv->hub_change_status; ++ status[0] = cpu_to_le16(change_status & 0xFFFF); ++ status[1] = cpu_to_le16(change_status >> 16); ++ reply = (void *) &status[0]; ++ rlen = 4; ++ TRACE_MSG1(HCD,"GET HUB_CHANGE_STATUS: %08x", bus_hcpriv->rh_hcpriv->hub_change_status); ++ } ++ else { ++ TRACE_MSG2(HCD,"HUB GETSTATUS invalid type r: %02x t: %02x",bRequest,bmRequestType); ++ rc = rc_stall; ++ } ++ break; ++ case USB_REQ_GET_INTERFACE: ++ // FIXME Verify type and interface number (wIndex) ++ TRACE_MSG1(HCD,"get interface %u",wIndex); ++ reply = (void *) &rh_hcpriv->curr_itf; ++ rlen = 1; ++ break; ++ case USB_REQ_GET_CONFIGURATION: ++ // FIXME Verify type ++ TRACE_MSG0(HCD,"get configuration"); ++ // Return a single byte value ++ reply = (void *) &rh_hcpriv->curr_cfg; ++ rlen = 1; ++ break; ++ default: ++ TRACE_MSG2(HCD,"H<-D HUB invalid request r: %02x t: %02x",bRequest,bmRequestType); ++ rc = rc_stall; ++ } ++ break; ++ case USB_RECIP_PORT: ++ switch (bRequest) { ++ case USB_REQ_GET_STATUS: ++ if (USB_REQ_TYPE_CLASS == type) { ++ /* USB2.0 11.24.2.7 pg 426 says first hword of reply are status (Tbl 11-21), ++ second hword is change (Tbl 11-22). */ ++ u32 change_status = ((wIndex -1) < bus_hcpriv->num_ports) ? ++ bus_hcpriv->rh_hcpriv->port_change_status[wIndex-1] : 0; ++ u32 hw_change_status = ((wIndex -1) < bus_hcpriv->num_ports) ? ++ rh_get_port_change_status(bus_hcpriv, wIndex) : 0; ++ ++ check_port_status(bus_hcpriv, wIndex, "HOST POLL"); ++ ++ status[0] = cpu_to_le16(hw_change_status & 0xFFFF); ++ status[1] = cpu_to_le16(change_status >> 16); ++ reply = (void *) &status[0]; ++ rlen = 4; ++ ++ TRACE_MSG3(HCD,"GET PORT_CHANGE_STATUS[%2d] %04x %04x", wIndex, status[0], status[1]); ++ } ++ else { ++ TRACE_MSG2(HCD,"PORT GETSTATUS invalid type r: %02x t: %02x",bRequest,bmRequestType); ++ rc = rc_stall; ++ } ++ break; ++ default: ++ TRACE_MSG2(HCD,"H<-D PORT invalid request r: %02x t: %02x",bRequest,bmRequestType); ++ rc = rc_stall; ++ } ++ break; ++ case USB_REQ_RECIPIENT_INTERFACE: ++ case USB_REQ_RECIPIENT_ENDPOINT: ++ default: ++ TRACE_MSG2(HCD,"H<-D invalid recipient r: %02x t: %02x",bRequest,bmRequestType); ++ rc = rc_stall; ++ } ++ if (!rc && reply) { ++ // There is a reply to copy into place. ++ if (rlen > wLength) { ++ // Truncate the reply (used to get part of a descriptor). ++ rlen = wLength; ++ } ++ if (rlen > 0 && reply != urb->transfer_buffer) { ++ memcpy(urb->transfer_buffer,reply,rlen); ++ } ++ urb->actual_length = rlen; ++ } ++ } ++ // All done, "complete" it. ++ urb->status = rc; ++ TRACE_MSG3(HCD,"urb: %08lx completing status=%d len=%d",urb,urb->status,urb->actual_length); ++ hcd_rh_urb_complete(bus_hcpriv,urb); ++ TRACE_MSG2(HCD,"urb: %08lx completed->%d",urb,rc); ++ return rc; ++} ++ ++/* ++ * hcd_rh_unlink_urb() ++ */ ++int hcd_rh_unlink_urb(struct bus_hcpriv *bus_hcpriv, struct urb *urb) ++{ ++ struct rh_hcpriv *rh_hcpriv = bus_hcpriv->rh_hcpriv; ++ RETURN_EINVAL_UNLESS(urb && (urb == rh_hcpriv->int_urb)); ++ del_timer(&rh_hcpriv->poll_timer); ++ rh_hcpriv->int_urb = NULL; ++ urb->status = -ENOENT; // Cancelled ++ hcd_rh_urb_complete(bus_hcpriv,urb); ++ usb_put_urb(urb); ++ return 0; ++} ++/* ********************************************************************************************** */ ++/* ++ * For the hardware specific root hub component. ++ */ ++irqreturn_t hcd_rh_int_hndlr(int irq, void *dev_id, struct pt_regs *regs) ++{ ++ struct bus_hcpriv *bus_hcpriv = (struct bus_hcpriv *) dev_id; ++ ++ // Assume port status change. ++ //printk(KERN_INFO "%s: scheduling hcd_rh_portstatus_bh\n",__FUNCTION__); ++ ++ PREPARE_WORK_ITEM(bus_hcpriv->rh_hcpriv->psc_bh, hcd_rh_portstatus_bh, bus_hcpriv); ++ SCHEDULE_WORK(bus_hcpriv->rh_hcpriv->psc_bh); ++ return IRQ_HANDLED; ++} ++/* ********************************************************************************************** */ ++#if !defined(OTG_C99) ++struct usbd_device_descriptor rh_device_descriptor; ++struct usbd_configuration_descriptor rh_configuration_descriptor; ++struct usbd_interface_descriptor rh_interface_descriptor; ++struct usbd_endpoint_descriptor rh_endpoint_descriptor; ++struct hub_descriptor rh_hub_descriptor; ++ ++void rh_global_init(void) ++{ ++ ZERO(rh_device_descriptor); ++ rh_device_descriptor.bLength = USB_DT_DEVICE_SIZE; ++ rh_device_descriptor.bDescriptorType = USB_DT_DEVICE; ++ rh_device_descriptor.bcdUSB = __constant_cpu_to_le16(USB_BCD_VERSION); ++ rh_device_descriptor.bDeviceClass = USB_CLASS_HUB; ++ rh_device_descriptor.bDeviceSubClass = 0; ++ rh_device_descriptor.bDeviceProtocol = 0; ++ rh_device_descriptor.bMaxPacketSize0 = 8; ++ rh_device_descriptor.iManufacturer = XHC_RH_STRING_MANUFACTURER; ++ rh_device_descriptor.iProduct = XHC_RH_STRING_PRODUCT; ++ rh_device_descriptor.iSerialNumber = XHC_RH_STRING_SERIAL; ++ rh_device_descriptor.bNumConfigurations = 1; ++ ++ ZERO(rh_configuration_descriptor); ++ rh_configuration_descriptor.bLength = USB_DT_CONFIG_SIZE; ++ rh_configuration_descriptor.bDescriptorType = USB_DT_CONFIGURATION; ++ rh_configuration_descriptor.wTotalLength = __constant_cpu_to_le16(USB_DT_CONFIG_SIZE+USB_DT_INTERFACE_SIZE ++ + USB_DT_ENDPOINT_SIZE); ++ rh_configuration_descriptor.bNumInterfaces = 1; ++ rh_configuration_descriptor.bConfigurationValue = 1; ++ rh_configuration_descriptor.iConfiguration = XHC_RH_STRING_CONFIGURATION; ++ ++ ZERO(rh_interface_descriptor); ++ rh_interface_descriptor.bLength = USB_DT_INTERFACE_SIZE; ++ rh_interface_descriptor.bDescriptorType = USB_DT_INTERFACE; ++ rh_interface_descriptor.bInterfaceNumber = 0; ++ rh_interface_descriptor.bAlternateSetting = 0; ++ rh_interface_descriptor.bNumEndpoints = 1; ++ rh_interface_descriptor.bInterfaceClass = USB_CLASS_HUB; ++ rh_interface_descriptor.bInterfaceSubClass = 0; ++ rh_interface_descriptor.bInterfaceProtocol = 0; ++ rh_interface_descriptor.iInterface = XHC_RH_STRING_INTERFACE; ++ ++ ZERO(rh_endpoint_descriptor); ++ rh_endpoint_descriptor.bLength = USB_DT_ENDPOINT_SIZE; ++ rh_endpoint_descriptor.bDescriptorType = USB_DT_ENDPOINT; ++ rh_endpoint_descriptor.bEndpointAddress = USB_DIR_IN | 1; // Fixed EP 1 ++ rh_endpoint_descriptor.bmAttributes = INTERRUPT; ++ rh_endpoint_descriptor.wMaxPacketSize = __constant_cpu_to_le16(4); ++ rh_endpoint_descriptor.bInterval = 0xFF; ++ ++ ZERO(rh_hub_descriptor); ++ rh_hub_descriptor.bDescLength = USB_DT_HUB_NONVAR_SIZE+2; ++ rh_hub_descriptor.bDescriptorType = USB_DT_HUB; ++}; ++ ++#else /* !defined(OTG_C99) */ ++struct usbd_device_descriptor rh_device_descriptor = { ++ .bLength = USB_DT_DEVICE_SIZE, ++ .bDescriptorType = USB_DT_DEVICE, ++ .bcdUSB = __constant_cpu_to_le16(USB_BCD_VERSION), ++ .bDeviceClass = USB_CLASS_HUB, ++ .bDeviceSubClass = 0, ++ .bDeviceProtocol = 0, ++ .bMaxPacketSize0 = 8, ++ .iManufacturer = XHC_RH_STRING_MANUFACTURER, ++ .iProduct = XHC_RH_STRING_PRODUCT, ++ .iSerialNumber = XHC_RH_STRING_SERIAL, ++ .bNumConfigurations = 1, ++}; ++ ++struct usbd_configuration_descriptor rh_configuration_descriptor = { ++ .bLength = USB_DT_CONFIG_SIZE, ++ .bDescriptorType = USB_DT_CONFIGURATION, ++ .wTotalLength = __constant_cpu_to_le16(USB_DT_CONFIG_SIZE+USB_DT_INTERFACE_SIZE+USB_DT_ENDPOINT_SIZE), ++ .bNumInterfaces = 1, ++ .bConfigurationValue = 1, ++ .iConfiguration = XHC_RH_STRING_CONFIGURATION, ++}; ++ ++struct usbd_interface_descriptor rh_interface_descriptor = { ++ .bLength = USB_DT_INTERFACE_SIZE, ++ .bDescriptorType = USB_DT_INTERFACE, ++ .bInterfaceNumber = 0, ++ .bAlternateSetting = 0, ++ .bNumEndpoints = 1, ++ .bInterfaceClass = USB_CLASS_HUB, ++ .bInterfaceSubClass = 0, ++ .bInterfaceProtocol = 0, ++ .iInterface = XHC_RH_STRING_INTERFACE, ++}; ++ ++struct usbd_endpoint_descriptor rh_endpoint_descriptor = { ++ .bLength = USB_DT_ENDPOINT_SIZE, ++ .bDescriptorType = USB_DT_ENDPOINT, ++ .bEndpointAddress = USB_DIR_IN | 1, // Fixed EP 1 ++ .bmAttributes = INTERRUPT, ++ .wMaxPacketSize = __constant_cpu_to_le16(4), ++ .bInterval = 0xFF, ++}; ++ ++/* This assumes 1-8 ports. ++ * If ports > 8, then DeviceRemovable and PortPwrCtrlMask need to be sized accordingly. ++ */ ++struct hub_descriptor rh_hub_descriptor = { ++ .bDescLength = USB_DT_HUB_NONVAR_SIZE+2, ++ .bDescriptorType = USB_DT_HUB, ++}; ++#endif /* !defined(OTG_C99) */ ++ ++/* ********************************************************************************************** */ ++/* ++ * Copy static descriptors to buffer to allow modification for actual ++ * configuration and provide full configuration descriptor. ++ */ ++STATIC void build_descriptors(struct bus_hcpriv *bus_hcpriv) ++{ ++ struct rh_hcpriv *rh_hcpriv = bus_hcpriv->rh_hcpriv; ++ void *bp = rh_hcpriv->descriptors; ++ ++ ++ rh_hcpriv->dd = (struct usbd_device_descriptor *) bp; ++ memcpy(bp, &rh_device_descriptor, sizeof(rh_device_descriptor)); ++ rh_hcpriv->cd = (struct usbd_configuration_descriptor *) bp = bp + USB_DT_DEVICE_SIZE; ++ memcpy(bp, &rh_configuration_descriptor, sizeof(rh_configuration_descriptor)); ++ rh_hcpriv->id = (struct usbd_interface_descriptor *) bp = bp + USB_DT_CONFIG_SIZE; ++ memcpy(bp, &rh_interface_descriptor, sizeof(rh_interface_descriptor)); ++ rh_hcpriv->ed = (struct usbd_endpoint_descriptor *) bp = bp + USB_DT_INTERFACE_SIZE; ++ memcpy(bp, &rh_endpoint_descriptor, sizeof(rh_endpoint_descriptor)); ++ rh_hcpriv->hd = (struct hub_descriptor *) bp = bp + USB_DT_ENDPOINT_SIZE; ++ memcpy(bp, &rh_hub_descriptor, sizeof(rh_hub_descriptor)); ++ ++ rh_hcpriv->dd->idVendor = cpu_to_le16(bus_hcpriv->rh_vendorid); ++ rh_hcpriv->dd->idProduct = cpu_to_le16(bus_hcpriv->rh_productid); ++ rh_hcpriv->dd->bcdDevice = cpu_to_le16(bus_hcpriv->rh_bcddevice); ++ rh_hcpriv->cd->bmAttributes = bus_hcpriv->rh_bmAttributes; ++ rh_hcpriv->cd->bMaxPower = bus_hcpriv->rh_bMaxPower; ++ rh_hcpriv->hd->bNbrPorts = bus_hcpriv->num_ports, ++ rh_hcpriv->hd->wHubCharacteristics = cpu_to_le16(hcd_hw_rh_hub_attributes(bus_hcpriv)); ++ rh_hcpriv->hd->bPwrOn2PwrGood = hcd_hw_rh_power_delay(bus_hcpriv); ++ rh_hcpriv->hd->bHubContrCurrent = hcd_hw_rh_hub_contr_current(bus_hcpriv); ++ rh_hcpriv->hd->DeviceRemovable = hcd_hw_rh_DeviceRemovable(bus_hcpriv); ++ rh_hcpriv->hd->PortPwrCtrlMask = hcd_hw_rh_PortPwrCtrlMask(bus_hcpriv); ++} ++ ++/* ********************************************************************************************** */ ++ ++#define PORT_SYNC_MASK ( (1 << PORT_POWER) | (1 << PORT_SUSPEND) | (1 << PORT_RESET) ) ++ ++/* puts OTG capable port into a_host or b_host state - attempt to use port */ ++void rh_loc_sof_func(struct otg_instance *otg, u8 on) ++{ ++ struct bus_hcpriv *bus_hcpriv = (struct bus_hcpriv *)(((struct hcd_instance *)(otg->hcd))->privdata); ++ int i; ++ unsigned long flags; ++ ++ RETURN_UNLESS (bus_hcpriv && bus_hcpriv->rh_hcpriv && bus_hcpriv->rh_hcpriv->port_change_status); ++ ++ local_irq_save(flags); ++ switch (on) { ++ case SET: ++ //printk(KERN_INFO"%s: SET\n", __FUNCTION__); ++ TRACE_MSG0(HCD, "RH LOC_SOF SET"); ++ ++ // Stop treating otg capable port as a device ++ bus_hcpriv->rh_hcpriv->otg_device_mask &= ~(0x1 << (bus_hcpriv->otg_port)); ++ ++ /* Sync the state of the HW to the shadow values, because ++ * the root hub was registered with usbcore a while back, and ++ * at the very least it probably tried to turn on the power, ++ * and until now, the otg_device_mask was preventing that. ++ */ ++ for (i = 0; i < 16; i++) { ++ ++ if (PORT_SYNC_MASK & (0x1 << i) & bus_hcpriv->rh_hcpriv->port_change_status[bus_hcpriv->otg_port - 1]) { ++ hcd_hw_rh_port_feature(bus_hcpriv, i, bus_hcpriv->otg_port, TRUE); ++ check_port_status(bus_hcpriv, bus_hcpriv->otg_port, "OTG EN"); ++ } ++ } ++ ++ /* "Connect" on OTG port and simulate a port status change interrupt ++ */ ++ bus_hcpriv->rh_hcpriv->port_change_status[bus_hcpriv->otg_port - 1] |= 0x00010001; ++ TRACE_MSG2(HCD, "OUTPUT: RH LOC_SOF_SET port_change_status[%d]: %08x", ++ bus_hcpriv->otg_port, bus_hcpriv->rh_hcpriv->port_change_status[bus_hcpriv->otg_port - 1]); ++ check_port_status(bus_hcpriv, bus_hcpriv->otg_port, "SOF SET"); ++ break; ++ ++ case RESET: ++ //printk(KERN_INFO"%s: RESET\n", __FUNCTION__); ++ TRACE_MSG0(HCD, "RH LOC_SOF RESET"); ++ ++ check_port_status(bus_hcpriv, bus_hcpriv->otg_port, "SOF RESET - BEFORE"); ++ ++ // "Disconnect" on OTG port and simulate a port status change interrupt ++ bus_hcpriv->rh_hcpriv->port_change_status[bus_hcpriv->otg_port - 1] &= ~0x00000001; ++ bus_hcpriv->rh_hcpriv->port_change_status[bus_hcpriv->otg_port - 1] |= 0x00010000; ++ TRACE_MSG2(HCD, "OUTPUT: RH LOC_SOF_RESET port_change_status[%d]: %08x", ++ bus_hcpriv->otg_port, bus_hcpriv->rh_hcpriv->port_change_status[bus_hcpriv->otg_port - 1]); ++ ++ ++ // Turn off port power. ++ hcd_hw_rh_port_feature(bus_hcpriv, PORT_POWER, bus_hcpriv->otg_port, FALSE); ++ //hcd_hw_rh_port_feature(bus_hcpriv, PORT_ENABLE, bus_hcpriv->otg_port, FALSE); ++ ++ check_port_status(bus_hcpriv, bus_hcpriv->otg_port, "SOF RESET - AFTER"); ++ ++ // Start treating otg capable port as a device ++ bus_hcpriv->rh_hcpriv->otg_device_mask |= (0x1 << (bus_hcpriv->otg_port)); ++ break; ++ } ++ ++ bus_hcpriv->rh_hcpriv->hub_port_change_status |= (1 << bus_hcpriv->otg_port); ++ TRACE_MSG0(HCD, "Schedule hcd_rh_portstatus_bh"); ++ PREPARE_WORK_ITEM(bus_hcpriv->rh_hcpriv->psc_bh, hcd_rh_portstatus_bh, bus_hcpriv); ++ SCHEDULE_WORK(bus_hcpriv->rh_hcpriv->psc_bh); ++ local_irq_restore(flags); ++} ++ ++/* puts OTG capable port into a_host or b_host state - attempt to use port */ ++void rh_loc_suspend_func(struct otg_instance *otg, u8 on) ++{ ++ struct bus_hcpriv *bus_hcpriv = (struct bus_hcpriv *)(((struct hcd_instance *)(otg->hcd))->privdata); ++ struct rh_hcpriv *rh_hcpriv = bus_hcpriv->rh_hcpriv; ++ int i; ++ unsigned long flags; ++ ++ //printk(KERN_INFO"%s: \n", __FUNCTION__); ++ RETURN_UNLESS (bus_hcpriv && bus_hcpriv->rh_hcpriv && bus_hcpriv->rh_hcpriv->port_change_status); ++ ++ local_irq_save(flags); ++ switch (on) { ++ case SET: ++ TRACE_MSG0(HCD, "OUTPUT: RH LOC_SUSPEND SET"); ++ ++ rh_hcpriv->suspended = 1; ++ ++ // suspend ++ hcd_hw_rh_port_feature(bus_hcpriv, PORT_SUSPEND, bus_hcpriv->otg_port, TRUE); ++ ++ // "Connect" on OTG port and simulate a port status change interrupt ++ bus_hcpriv->rh_hcpriv->port_change_status[bus_hcpriv->otg_port - 1] |= 0x00040004; ++ TRACE_MSG2(HCD, "OUTPUT: RH LOC_SUSPNED_SET port_change_status[%d]: %08x", ++ bus_hcpriv->otg_port, bus_hcpriv->rh_hcpriv->port_change_status[bus_hcpriv->otg_port - 1]); ++ check_port_status(bus_hcpriv, bus_hcpriv->otg_port, "LOC_SUSPEND SET"); ++ break; ++ ++ case RESET: ++ ++ TRACE_MSG0(HCD, "OUTPUT: RH LOC_SUSPEND RESET"); ++ check_port_status(bus_hcpriv, bus_hcpriv->otg_port, "LOC_SUSPEND RESET - BEFORE"); ++ ++ // "Disconnect" on OTG port and simulate a port status change interrupt ++ bus_hcpriv->rh_hcpriv->port_change_status[bus_hcpriv->otg_port - 1] &= ~0x00000004; ++ bus_hcpriv->rh_hcpriv->port_change_status[bus_hcpriv->otg_port - 1] |= 0x00040000; ++ TRACE_MSG2(HCD, "OUTPUT: RH LOC_SUSPEND port_change_status[%d]: %08x", ++ bus_hcpriv->otg_port, bus_hcpriv->rh_hcpriv->port_change_status[bus_hcpriv->otg_port - 1]); ++ ++ ++ // suspend ++ hcd_hw_rh_port_feature(bus_hcpriv, PORT_SUSPEND, bus_hcpriv->otg_port, FALSE); ++ check_port_status(bus_hcpriv, bus_hcpriv->otg_port, "SOF RESET - AFTER"); ++ ++ rh_hcpriv->suspended = 0; ++ ++ break; ++ } ++ ++ bus_hcpriv->rh_hcpriv->hub_port_change_status |= (1 << bus_hcpriv->otg_port); ++ TRACE_MSG0(HCD, "Schedule hcd_rh_portstatus_bh"); ++ PREPARE_WORK_ITEM(bus_hcpriv->rh_hcpriv->psc_bh, hcd_rh_portstatus_bh, bus_hcpriv); ++ SCHEDULE_WORK(bus_hcpriv->rh_hcpriv->psc_bh); ++ local_irq_restore(flags); ++} ++ ++void rh_suspend_func(struct otg_instance *otg, u8 flag) ++{ ++ /* suspends the OTG capable port */ ++ //TRACE_MSG0(HCD, "--"); ++ switch (flag) { ++ case SET: ++ //printk(KERN_INFO"%s: SET\n", __FUNCTION__); ++ TRACE_MSG0(HCD, "OUTPUT: RH SUSPEND_SET"); ++ break; ++ case RESET: ++ //printk(KERN_INFO"%s: RESET\n", __FUNCTION__); ++ TRACE_MSG0(HCD, "OUTPUT: RH SUSPEND_RESET"); ++ break; ++ } ++} ++ ++/* ********************************************************************************************* */ ++ ++int hcd_rh_init(struct bus_hcpriv *bus_hcpriv) ++{ ++ struct rh_hcpriv *rh_hcpriv; ++ int wIndex; ++ int port_mask; ++ ++ #if !defined(OTG_C99) ++ rh_global_init(); ++ #endif /* !defined(OTG_C99) */ ++ ++ //printk(KERN_INFO"%s: \n", __FUNCTION__); ++ RETURN_ZERO_UNLESS (bus_hcpriv); ++ RETURN_ENOMEM_UNLESS ((rh_hcpriv = bus_hcpriv->rh_hcpriv = ++ (struct rh_hcpriv *) kmalloc(sizeof(struct rh_hcpriv),GFP_KERNEL))); ++ memset(rh_hcpriv, 0, sizeof(struct rh_hcpriv)); ++ ++ //printk(KERN_INFO "%s: rh_hcpriv: %08x\n",__FUNCTION__,(u32)(void*)rh_hcpriv); ++ //init_timer(&rh_hcpriv->poll_timer); ++ // Get the number of ports, and which port is OTG capable. ++ ++ //rh_hcpriv->num_ports = hcd_hw_rh_num_ports(bus_hcpriv); ++ ++ // Allocate the shadow port_change_status array. ++ UNLESS ((rh_hcpriv->port_change_status = (u32 *) kmalloc((bus_hcpriv->num_ports*sizeof(u32)),GFP_KERNEL))) { ++ //printk(KERN_ERR "%s: allocation of root hub shadow port data fails\n",__FUNCTION__); ++ kfree(rh_hcpriv); ++ bus_hcpriv->rh_hcpriv = rh_hcpriv = NULL; ++ return -ENOMEM; ++ } ++ memset(rh_hcpriv->port_change_status,0,(bus_hcpriv->num_ports*sizeof(u32))); ++ UNLESS ((rh_hcpriv->dev = usb_alloc_dev(NULL, bus_hcpriv->usb_bus, 0))) { ++ //printk(KERN_ERR "%s: allocation of root hub dev fails\n",__FUNCTION__); ++ kfree(rh_hcpriv->port_change_status); ++ rh_hcpriv->port_change_status = NULL; ++ kfree(rh_hcpriv); ++ bus_hcpriv->rh_hcpriv = rh_hcpriv = NULL; ++ return -ENOMEM; ++ } ++ rh_hcpriv->dev->speed = USB_SPEED_FULL; // QQSV --- this belongs in the HW dependent portion... ++ ++ // Build up USB descriptors ++ build_descriptors(bus_hcpriv); ++ ++ // Leave OTG capable port as disabled as possible until OTG core enables it. ++ rh_hcpriv->otg_device_mask = bus_hcpriv->otg_capable_mask; ++ ++ for (wIndex = 1; wIndex <= bus_hcpriv->num_ports; wIndex++) { ++ port_mask = (1 << (wIndex - 1)); ++ if (port_mask & bus_hcpriv->otg_capable_mask) ++ TRACE_MSG1(HCD,"OTG capable port %d", wIndex); ++ else ++ TRACE_MSG1(HCD,"Standard port %d", wIndex); ++ } ++ return 0; ++} ++ ++void hcd_rh_exit(struct bus_hcpriv *bus_hcpriv) ++{ ++ struct rh_hcpriv *rh_hcpriv; ++ RETURN_UNLESS(bus_hcpriv && (rh_hcpriv = bus_hcpriv->rh_hcpriv)); ++ ++ (void *) hcd_rh_unlink_urb(bus_hcpriv,rh_hcpriv->int_urb); ++ ++ if (rh_hcpriv->dev) { ++ usb_put_dev(rh_hcpriv->dev); ++ rh_hcpriv->dev = NULL; ++ } ++ if (rh_hcpriv->port_change_status) { ++ kfree(rh_hcpriv->port_change_status); ++ rh_hcpriv->port_change_status = NULL; ++ } ++ kfree(rh_hcpriv); ++ bus_hcpriv->rh_hcpriv = rh_hcpriv = NULL; ++} ++ +diff -uNr linux/drivers/no-otg/ocd/otg-hcd/hcd.c linux/drivers/otg/ocd/otg-hcd/hcd.c +--- linux/drivers/no-otg/ocd/otg-hcd/hcd.c 1970-01-01 01:00:00.000000000 +0100 ++++ linux/drivers/otg/ocd/otg-hcd/hcd.c 2006-09-01 21:41:32.000000000 +0200 +@@ -0,0 +1,139 @@ ++/* ++ * otg/ocd/otg-hcd/hcd.c - OTG Host Controller Driver ++ * ++ * Copyright (c) 2004 Belcarra ++ * ++ * By: ++ * Stuart Lynne <sl@belcara.com>, ++ * Tom Rushworth <tbr@belcara.com>, ++ * Bruce Balden <balden@belcara.com> ++ * ++ * Notes ++ * ++ * 1. The usbd-bi layer has be re-implemented to re-factor the UDC layer in a way to ++ * simplify implementation of UDC drivers. As much of the complexity of dealing with the middle ++ * layers and buffer (urb) handling is provided for in the common bi layer. ++ * ++ * 2. TODO The udc interface will be further modified to allow the UDC to export a block of ++ * function pointers for the common bi layer to use. This will allow the common layer to ++ * implement default operations where the UDC does not provide an function. For example many UDC ++ * drivers do not provide full support for cable detection and usb pullup control. If these ++ * routines are not provided the common layer will supply defaults. This eliminates a reasonably ++ * larger amount of effectively unused code from many of the udc drivers. ++ * ++ */ ++/*! ++ * @file otg/ocd/otg-hcd/hcd.c ++ * @brief Root Hub for Generic Host Controller Driver ++ * ++ * ++ * This file initializes the low level hardware drivers. ++ * ++ * This is the linux 2.4 version. ++ * ++ * @ingroup OTGHCD ++ */ ++ ++#include <otg/otg-compat.h> ++ ++ ++#include <otg/usbp-chap9.h> ++#include <otg/usbp-bus.h> ++#include <otg/otg-trace.h> ++#include <otg/otg-api.h> ++#include <otg/otg-tcd.h> ++#include <otg/otg-hcd.h> ++#include <otg/otg-pcd.h> ++ ++/* ************************************************************************************* */ ++ ++void hcd_en_func (struct otg_instance *otg, u8 flag) ++{ ++ //struct hcd_instance *hcd = otg->hcd; ++ TRACE_MSG0(HCD, "--"); ++ switch (flag) { ++ case SET: ++ TRACE_MSG0(HCD, "OUTPUT: HCD_EN_SET"); ++ break; ++ case RESET: ++ TRACE_MSG0(HCD, "OUTPUT: HCD_EN_RESET"); ++ break; ++ } ++} ++ ++void hcd_init_func (struct otg_instance *otg, u8 flag) ++{ ++ struct hcd_instance *hcd = otg->hcd; ++ ++ TRACE_MSG0(HCD, "--"); ++ switch (flag) { ++ case SET: ++ TRACE_MSG0(HCD, "OUTPUT: HCD_INIT_SET"); ++ otg_event(hcd->otg, HCD_OK, HCD, "HCD_INIT SET HCD_OK"); ++ break; ++ case RESET: ++ TRACE_MSG0(HCD, "OUTPUT: HCD_INIT_RESET"); ++ otg_event(hcd->otg, HCD_OK, HCD, "HCD_INIT SET HCD_OK"); ++ break; ++ } ++} ++ ++/* ************************************************************************************* */ ++ ++void hcd_loc_sof_func (struct otg_instance *otg, u8 flag) ++{ ++ //struct hcd_instance *hcd = otg->hcd; ++ TRACE_MSG0(HCD, "--"); ++ switch (flag) { ++ case SET: ++ TRACE_MSG0(HCD, "OUTPUT: HCD_LOC_SOF_SET"); ++ break; ++ case RESET: ++ TRACE_MSG0(HCD, "OUTPUT: HCD_LOC_SOF_RESET"); ++ break; ++ } ++} ++ ++void hcd_suspend_func (struct otg_instance *otg, u8 flag) ++{ ++ //struct hcd_instance *hcd = otg->hcd; ++ TRACE_MSG0(HCD, "--"); ++ switch (flag) { ++ case SET: ++ TRACE_MSG0(HCD, "OUTPUT: HCD_SUSPEND_EN_SET"); ++ break; ++ case RESET: ++ TRACE_MSG0(HCD, "OUTPUT: HCD_SUSPEND_EN_RESET"); ++ break; ++ } ++} ++ ++void hcd_remote_wakeup_en_func (struct otg_instance *otg, u8 flag) ++{ ++ //struct hcd_instance *hcd = otg->hcd; ++ TRACE_MSG0(HCD, "--"); ++ switch (flag) { ++ case SET: ++ TRACE_MSG0(HCD, "OUTPUT: HCD_REMOTE_WAKEUP_EN_SET"); ++ break; ++ case RESET: ++ TRACE_MSG0(HCD, "OUTPUT: HCD_REMOTE_WAKEUP_EN_RESET"); ++ break; ++ } ++} ++ ++void hcd_hnp_en_func (struct otg_instance *otg, u8 flag) ++{ ++ //struct hcd_instance *hcd = otg->hcd; ++ TRACE_MSG0(HCD, "--"); ++ switch (flag) { ++ case SET: ++ TRACE_MSG0(HCD, "OUTPUT: HCD_HNP_EN_SET"); ++ break; ++ case RESET: ++ TRACE_MSG0(HCD, "OUTPUT: HCD_HNP_EN_RESET"); ++ break; ++ } ++} ++ ++ +diff -uNr linux/drivers/no-otg/ocd/otg-i2c/i2c-l26.c linux/drivers/otg/ocd/otg-i2c/i2c-l26.c +--- linux/drivers/no-otg/ocd/otg-i2c/i2c-l26.c 1970-01-01 01:00:00.000000000 +0100 ++++ linux/drivers/otg/ocd/otg-i2c/i2c-l26.c 2006-09-01 21:41:32.000000000 +0200 +@@ -0,0 +1,160 @@ ++/* ++ * otg/ocd/otg-i2c/i2c-l26.c -- Linux 2.6 I2C access ++ * ++ * Copyright (c) 2004 Belcarra ++ * ++ * By: ++ * Stuart Lynne <sl@lbelcarra.com>, ++ * Tom Rushworth <tbr@belcarra.com>, ++ * Bruce Balden <balden@belcarra.com> ++ * ++ */ ++/*! ++ * @file otg/ocd/otg-i2c/i2c-l26.c ++ * @brief Linux I2C I/O via generic i2c device. ++ * ++ * @ingroup ISP1301TCD ++ */ ++ ++#include <otg/otg-compat.h> ++ ++#include <asm/irq.h> ++#include <asm/system.h> ++#include <asm/io.h> ++#include <linux/i2c.h> ++ ++#include <otg/pcd-include.h> ++#include <linux/pci.h> ++#include <otghw/isp1301-hardware.h> ++#include "isp1301.h" ++ ++/* ********************************************************************************************* */ ++ ++/* ++ * N.B. i2c functions must not be called from interrupt handlers ++ */ ++ ++static struct file *i2c_file; ++static struct i2c_client *omap_i2c_client; ++static int initstate_i2c; ++static int initstate_region; ++#define MAX_I2C 16 ++ ++#ifdef CONFIG_OMAP_H2 ++#define ADAPTOR_NAME "OMAP I2C" ++#endif /* CONFIG_OMAP_H2 */ ++#ifdef CONFIG_ARCH_MAINSTONE ++#define ADAPTOR_NAME "PXA-I2C-Adapter" ++#endif /* CONFIG_ARCH_MAINSTONE */ ++ ++/*! i2c_configure ++ * Attempt to find and open generic i2c device ++ * @return - non-zero for failure ++ */ ++int i2c_configure(char *name, int addr) ++{ ++ char filename[20]; ++ int tmp; ++ ++ RETURN_ZERO_IF(initstate_i2c); ++ ++ /*find the I2C driver we need ++ */ ++ for (tmp = 0; tmp < MAX_I2C; tmp++) { ++ ++ sprintf(filename, "/dev/i2c/%d", tmp); ++ ++ printk(KERN_INFO"%s: %s\n", __FUNCTION__, filename); ++ ++ UNLESS (IS_ERR(i2c_file = filp_open(filename, O_RDWR, 0))) { ++ ++ //printk(KERN_INFO"%s: %s found\n", __FUNCTION__, filename); ++ ++ /*found some driver */ ++ omap_i2c_client = (struct i2c_client *)i2c_file->private_data; ++ ++ printk(KERN_INFO"%s: found %s\n", __FUNCTION__, omap_i2c_client->adapter->name); ++ if (strlen(omap_i2c_client->adapter->name) >= 8) { ++ if (!strncmp(omap_i2c_client->adapter->name, name, strlen(name))) ++ break; /*we found our driver! */ ++ } ++ omap_i2c_client = NULL; ++ filp_close(i2c_file, NULL); ++ } ++ printk(KERN_INFO"%s: %s %d\n", __FUNCTION__, filename, i2c_file); ++ } ++ if (tmp == MAX_I2C) { // Nothing found ++ printk(KERN_ERR"%s: cannot find I2C driver", __FUNCTION__); ++ return -ENODEV; ++ } ++ omap_i2c_client->addr = addr; ++ initstate_i2c = 1; ++ return 0; ++} ++ ++/*! i2c_close ++ * Close i2c device fd. ++ */ ++void i2c_close(void) ++{ ++ if (initstate_i2c) ++ filp_close(i2c_file, NULL); ++ initstate_i2c = 0; ++} ++ ++/*! i2c_readb ++ * Read byte from i2c device ++ * @param subaddr ++ */ ++u8 i2c_readb(u8 subaddr) ++{ ++ u8 buf = 0; ++ i2c_master_send(omap_i2c_client, &subaddr, 1); ++ i2c_master_recv(omap_i2c_client, &buf, 1); ++ TRACE_MSG2(TCD, "addr: %02x buf: %02x", subaddr, buf); ++ return buf; ++} ++ ++/*! i2c_readw ++ * Read word from i2c device ++ * @param subaddr ++ */ ++u16 i2c_readw(u8 subaddr) ++{ ++ u16 buf = 0; ++ i2c_master_send(omap_i2c_client, &subaddr, 1); ++ i2c_master_recv(omap_i2c_client, (u8 *)&buf, 2); ++ TRACE_MSG2(TCD, "addr: %02x buf: %04x", subaddr, buf); ++ return buf; ++} ++ ++/*! i2c_readl ++ * Read long from i2c device ++ * @param subaddr ++ */ ++u32 i2c_readl(u8 subaddr) ++{ ++ u32 buf = 0; ++ i2c_master_send(omap_i2c_client, &subaddr, 1); ++ i2c_master_recv(omap_i2c_client, (u8 *)&buf, 4); ++ TRACE_MSG2(TCD, "addr: %02x buf: %08x", subaddr, buf); ++ return buf; ++} ++ ++ ++/*! i2c_writeb ++ * Writw byte to i2c device ++ * @param subaddr ++ * @param buf ++ */ ++int i2c_writeb(u8 subaddr, u8 buf) ++{ ++ char tmpbuf[2]; ++ ++ tmpbuf[0] = subaddr; /*register number */ ++ tmpbuf[1] = buf; /*register data */ ++ i2c_master_send(omap_i2c_client, &tmpbuf[0], 2); ++ ++ return 0; ++} ++ +diff -uNr linux/drivers/no-otg/ocd/otg-pcd/pcd-init-l24.c linux/drivers/otg/ocd/otg-pcd/pcd-init-l24.c +--- linux/drivers/no-otg/ocd/otg-pcd/pcd-init-l24.c 1970-01-01 01:00:00.000000000 +0100 ++++ linux/drivers/otg/ocd/otg-pcd/pcd-init-l24.c 2006-09-01 21:41:32.000000000 +0200 +@@ -0,0 +1,119 @@ ++/* ++ * otg/ocd/otg-pcd/pcd-init-l24.c - OTG Peripheral Controller Driver Module Initialization ++ * ++ * Copyright (c) 2004 Belcarra ++ * ++ * By: ++ * Stuart Lynne <sl@belcarra.com>, ++ * Tom Rushworth <tbr@belcarra.com>, ++ * Bruce Balden <balden@belcarra.com> ++ * ++ */ ++/*! ++ * @file otg/ocd/otg-pcd/pcd-init-l24.c ++ * @brief PCD only driver init. ++ * ++ * ++ * PCD Initialization ++ * ++ * This file initializes all of the low level hardware drivers for a PCD. ++ * ++ * The Peripheral Controller Driver (pcd) implements the lowest layer of the ++ * USBD stack to send and receive data from the USB actings as a USB ++ * peripheral. ++ * ++ * Notes ++ * ++ * 1. This is the linux 2.4 version. ++ * ++ * TODO ++ * ++ * 1. hook up serial_number_str to pcd.c for use with bus interface layer. ++ * ++ * ++ * @ingroup OTGPCD ++ */ ++ ++#include <otg/otg-compat.h> ++#include <otg/otg-module.h> ++ ++#include <otg/usbp-chap9.h> ++#include <otg/otg-module.h> ++#include <otg/usbp-bus.h> ++ ++#include <otg/otg-trace.h> ++#include <otg/otg-api.h> ++#include <otg/otg-tcd.h> ++#include <otg/otg-hcd.h> ++#include <otg/otg-pcd.h> ++ ++#ifdef MODULE ++#if LINUX_VERSION_CODE >= KERNEL_VERSION (2,4,17) ++MODULE_LICENSE ("GPL"); ++#endif ++MODULE_PARM (serial_number_str, "s"); ++MODULE_PARM_DESC (serial_number_str, "Serial Number"); ++MODULE_AUTHOR ("sl@belcarra.com, tbr@belcarra.com"); ++MODULE_DESCRIPTION ("USB On-The-Go PCD"); ++#endif ++char *serial_number_str; ++ ++otg_tag_t PCD; ++struct pcd_instance *pcd_instance; ++ ++/* ************************************************************************************* */ ++ ++/* pcd_modexit - module exit or init failure cleanup ++ * ++ * Specifically for each driver: ++ * ++ * call ops.mod_exit ++ * reset instance address and ops table address in state machine to NULL ++ * invalidate tag ++ */ ++static void pcd_modexit (void) ++{ ++ struct otg_instance *otg = ocd_instance->otg; ++ printk(KERN_INFO"%s\n", __FUNCTION__); ++ if (otg) ++ otg_exit(otg); ++ if (pcd_ops.mod_exit) pcd_ops.mod_exit(); ++ pcd_instance = otg_set_pcd_ops(NULL); ++ PCD = otg_trace_invalidate_tag(PCD); ++} ++ ++/* pcd_modinit - linux module initialization ++ * ++ * This needs to initialize the ocd, pcd and tcd drivers. ++ * ++ * Specifically for each driver: ++ * ++ * obtain tag ++ * pass ops table address to state machine and get instance address ++ * call ops.mod_init ++ * ++ * Note that we automatically provide a default tcd_init if ++ * none is set. ++ */ ++static int pcd_modinit (void) ++{ ++ printk(KERN_INFO"%s\n", __FUNCTION__); ++ ++ #if !defined(OTG_C99) ++ pcd_global_init(); ++ #endif /* !defined(OTG_C99) */ ++ ++ PCD = otg_trace_obtain_tag(); ++ ++ UNLESS(pcd_ops.pcd_init_func) pcd_ops.pcd_init_func = pcd_init_func; ++ THROW_UNLESS(pcd_instance = otg_set_pcd_ops(&pcd_ops), error); ++ THROW_IF((pcd_ops.mod_init) ? pcd_ops.mod_init() : 0, error); ++ ++ CATCH(error) { ++ pcd_modexit(); ++ return -EINVAL; ++ } ++ return 0; ++} ++MOD_EXIT(pcd_modexit); ++MOD_INIT(pcd_modinit); +diff -uNr linux/drivers/no-otg/ocd/otg-pcd/pcd-ocd-init-l24.c linux/drivers/otg/ocd/otg-pcd/pcd-ocd-init-l24.c +--- linux/drivers/no-otg/ocd/otg-pcd/pcd-ocd-init-l24.c 1970-01-01 01:00:00.000000000 +0100 ++++ linux/drivers/otg/ocd/otg-pcd/pcd-ocd-init-l24.c 2006-09-01 21:41:32.000000000 +0200 +@@ -0,0 +1,141 @@ ++/* ++ * otg/pcd/pcd-ocd-init-l24.c - OTG Peripheral and OTG Controller Drivers Module Initialization ++ * ++ * Copyright (c) 2004 Belcarra ++ * ++ * By: ++ * Stuart Lynne <sl@belcarra.com>, ++ * Tom Rushworth <tbr@belcarra.com>, ++ * Bruce Balden <balden@belcarra.com> ++ * ++ */ ++/*! ++ * @file otg/ocd/otg-pcd/pcd-ocd-init-l24.c ++ * @brief PCD only driver init. ++ * ++ * ++ * PCD/OCD Initialization ++ * ++ * This file initializes all of the low level hardware drivers for a combined ++ * PCD/OCD module. It does not initialize the TCD module. ++ * ++ * USB Periphirals using this must implement the following: ++ * ++ * ocd_ops operations from ocd driver ++ * pcd_ops operations from pcd driver ++ * ++ * The OTG Controller driver (ocd) implements any required OTG timers and if ++ * necessary mediates access to and initializes the OTG and/or USB hardware. ++ * ++ * The Peripheral Controller Driver (pcd) implements the lowest layer of the ++ * USBD stack to send and receive data from the USB actings as a USB ++ * peripheral. ++ * ++ * Notes ++ * ++ * 1. This is the linux 2.4 version. ++ * ++ * TODO ++ * ++ * 1. hook up serial_number_str to pcd.c for use with bus interface layer. ++ * ++ * @ingroup OTGPCD ++ */ ++ ++#include <otg/otg-compat.h> ++#include <otg/otg-module.h> ++ ++#include <otg/usbp-chap9.h> ++#include <otg/usbp-bus.h> ++ ++#include <otg/otg-trace.h> ++#include <otg/otg-api.h> ++#include <otg/otg-tcd.h> ++#include <otg/otg-hcd.h> ++#include <otg/otg-pcd.h> ++#include <otg/otg-ocd.h> ++ ++#ifdef MODULE ++#if LINUX_VERSION_CODE >= KERNEL_VERSION (2,4,17) ++MODULE_LICENSE ("GPL"); ++#endif ++MODULE_PARM (serial_number_str, "s"); ++MODULE_PARM_DESC (serial_number_str, "Serial Number"); ++MODULE_AUTHOR ("sl@belcarra.com, tbr@belcarra.com"); ++MODULE_DESCRIPTION ("USB On-The-Go PCD"); ++#endif ++char *serial_number_str; ++ ++otg_tag_t PCD; ++struct pcd_instance *pcd_instance; ++otg_tag_t OCD; ++struct ocd_instance *ocd_instance; ++ ++ ++/* ************************************************************************************* */ ++ ++/* pcd_ocd_modexit - module exit or init failure cleanup ++ * ++ * Specifically for each driver: ++ * ++ * call ops.mod_exit ++ * reset instance address and ops table address in state machine to NULL ++ * invalidate tag ++ */ ++static void pcd_ocd_modexit (void) ++{ ++ struct otg_instance *otg = ocd_instance->otg; ++ printk(KERN_INFO"%s\n", __FUNCTION__); ++ if (otg) ++ otg_exit(otg); ++ ++ if (pcd_ops.mod_exit) pcd_ops.mod_exit(); ++ pcd_instance = otg_set_pcd_ops(NULL); ++ PCD = otg_trace_invalidate_tag(PCD); ++ ++ if (ocd_ops.mod_exit) ocd_ops.mod_exit(); ++ ocd_instance = otg_set_ocd_ops(NULL); ++ OCD = otg_trace_invalidate_tag(OCD); ++} ++ ++/* pcd_ocd_modinit - linux module initialization ++ * ++ * This needs to initialize the ocd, pcd and tcd drivers. ++ * ++ * Specifically for each driver: ++ * ++ * obtain tag ++ * pass ops table address to state machine and get instance address ++ * call ops.mod_init ++ * ++ * Note that we automatically provide a default tcd_init if ++ * none is set. ++ */ ++static int pcd_ocd_modinit (void) ++{ ++ printk(KERN_INFO"%s\n", __FUNCTION__); ++ ++ #if !defined(OTG_C99) ++ pcd_global_init(); ++ #endif /* !defined(OTG_C99) */ ++ ++ UNLESS(pcd_ops.pcd_init_func) pcd_ops.pcd_init_func = pcd_init_func; ++ PCD = otg_trace_obtain_tag(); ++ THROW_UNLESS(pcd_instance = otg_set_pcd_ops(&pcd_ops), error); ++ THROW_IF((pcd_ops.mod_init) ? pcd_ops.mod_init() : 0, error); ++ ++ OCD = otg_trace_obtain_tag(); ++ THROW_UNLESS(ocd_instance = otg_set_ocd_ops(&ocd_ops), error); ++ THROW_IF((ocd_ops.mod_init) ? ocd_ops.mod_init() : 0, error); ++ ++ CATCH(error) { ++ pcd_ocd_modexit(); ++ return -EINVAL; ++ } ++ return 0; ++} ++ ++MOD_EXIT (pcd_ocd_modexit); ++MOD_INIT (pcd_ocd_modinit); ++ ++ +diff -uNr linux/drivers/no-otg/ocd/otg-pcd/pcd.c linux/drivers/otg/ocd/otg-pcd/pcd.c +--- linux/drivers/no-otg/ocd/otg-pcd/pcd.c 1970-01-01 01:00:00.000000000 +0100 ++++ linux/drivers/otg/ocd/otg-pcd/pcd.c 2006-09-01 21:41:32.000000000 +0200 +@@ -0,0 +1,845 @@ ++/* ++ * otg/ocd/otg-pcd/pcd.c - OTG Peripheral Controller Driver ++ * ++ * Copyright (c) 2004 Belcarra ++ * ++ * By: ++ * Stuart Lynne <sl@belcarra.com>, ++ * Tom Rushworth <tbr@belcarra.com>, ++ * Bruce Balden <balden@belcarra.com> ++ * ++ */ ++/*! ++ * @file otg/ocd/otg-pcd/pcd.c ++ * @brief PCD only driver init. ++ * Notes ++ * ++ * 1. The pcd file abstracts much of the UDC complexity as possible and ++ * provides a common implementation that is shared to various extents by the ++ * various pcd drivers. ++ * ++ * @ingroup OTGPCD ++ */ ++ ++#include <otg/pcd-include.h> ++#include <asm/au1000.h> ++ ++ ++extern char *serial_number_str; ++ ++/* ******************************************************************************************* */ ++/*! bus_request_endpoints ++ */ ++int bus_request_endpoints(struct usbd_bus_instance *bus, struct usbd_endpoint_map *endpoint_map_array, int endpointsRequested, ++ struct usbd_endpoint_request *requestedEndpoints) ++{ ++ struct pcd_instance *pcd = (struct pcd_instance *)bus->privdata; ++ int i; ++ TRACE_MSG0(PCD, "BUS_REQUEST ENDPOINTS:"); ++ if (usbd_pcd_ops.request_endpoints(pcd, endpoint_map_array, endpointsRequested, requestedEndpoints)) { ++ printk(KERN_INFO"%s: failed\n", __FUNCTION__); ++ return -EINVAL; ++ } ++ for (i = 0; i < endpointsRequested; i++) { ++ struct usbd_endpoint_map *endpoint_map = endpoint_map_array + i; ++ TRACE_MSG4(PCD, "address: %02x physical: %02x request: %02x size: %04x", ++ endpoint_map->bEndpointAddress[0], endpoint_map->physicalEndpoint[0], ++ endpoint_map->bmAttributes[0], endpoint_map->wMaxPacketSize[0]); ++ } ++ return 0; ++} ++ ++/*! bus_set_endpoints ++ */ ++int bus_set_endpoints(struct usbd_bus_instance *bus, int endpointsRequested, struct usbd_endpoint_map *endpoint_map_array) ++{ ++ struct pcd_instance *pcd = (struct pcd_instance *)bus->privdata; ++ TRACE_MSG0(PCD, "SET ENDPOINTS"); ++ return usbd_pcd_ops.set_endpoints ? usbd_pcd_ops.set_endpoints(pcd, endpointsRequested, endpoint_map_array) : 0; ++} ++ ++/*! bus_set_address ++ */ ++int bus_set_address(struct usbd_bus_instance *bus, int address) ++{ ++ struct pcd_instance *pcd = (struct pcd_instance *)bus->privdata; ++ if (usbd_pcd_ops.set_address) usbd_pcd_ops.set_address (pcd, address); ++ return 0; ++} ++ ++/* ******************************************************************************************* */ ++ ++/*! pcd_search_endpoint_index - find endpoint map index given endpoint address ++ */ ++int pcd_search_endpoint_index(struct usbd_bus_instance *bus, int bEndpointAddress) ++{ ++ int i; ++ for (i = 0; i < bus->endpoints; i++) ++ BREAK_IF (bus->endpoint_array[i].bEndpointAddress == bEndpointAddress); ++ return i; ++} ++ ++/*! pcd_search_endpoint - find endpoint given endpoint address ++ */ ++struct usbd_endpoint_instance * pcd_search_endpoint(struct usbd_bus_instance *bus, int bEndpointAddress) ++{ ++ int i = pcd_search_endpoint_index(bus, bEndpointAddress); ++ TRACE_MSG2(PCD, "BUS_SEARCH ENDPOINT: addr: %02x index: %d", bEndpointAddress, i); ++ RETURN_NULL_UNLESS(i < bus->endpoints); ++ return &(bus->endpoint_array[i]); ++} ++ ++/*! bus_endpoint_halted - check if endpoint halted ++ * Used by the USB Device Core to check endpoint halt status. ++ * ++ * Return actual halted status if available or'd with endpoint->feature_setting set value. ++ * Assume not halted if udc driver does not provide an endpoint halted function. ++ */ ++int bus_endpoint_halted (struct usbd_bus_instance *bus, int bEndpointAddress) ++{ ++ struct pcd_instance *pcd = (struct pcd_instance *)bus->privdata; ++ int endpoint_index = pcd_search_endpoint_index(bus, bEndpointAddress); ++ struct usbd_endpoint_instance *endpoint = pcd_search_endpoint(bus, bEndpointAddress); ++ int halted; ++ ++ TRACE_MSG1(PCD, "BUS_ENDPOINT HALTED: endpoint: %p", (int) endpoint); ++ RETURN_EINVAL_UNLESS(endpoint); ++ ++ //halted = usbd_pcd_ops.endpoint_halted ? usbd_pcd_ops.endpoint_halted(pcd, endpoint_index) : 0; ++ halted = usbd_pcd_ops.endpoint_halted ? usbd_pcd_ops.endpoint_halted(pcd, endpoint) : 0; ++ TRACE_MSG1(PCD, "BUS_ENDPOINT HALTED: %08x", bus->endpoint_array[endpoint_index].feature_setting); ++ ++ return halted || (endpoint->feature_setting & FEATURE(USB_ENDPOINT_HALT)? 1 : 0); ++} ++ ++/*! bus_halt_endpoint - handle set/clear feature requests ++ * Used by the USB Device Core to set/clear endpoint halt status. ++ * ++ * We assume that if the udc driver does not implement anything then ++ * we should just return zero for ok. ++ */ ++int bus_halt_endpoint (struct usbd_bus_instance *bus, int bEndpointAddress, int flag) ++{ ++ struct pcd_instance *pcd = (struct pcd_instance *)bus->privdata; ++ int endpoint_index = pcd_search_endpoint_index(bus, bEndpointAddress); ++ struct usbd_endpoint_instance *endpoint = pcd_search_endpoint(bus, bEndpointAddress); ++ ++ TRACE_MSG1(PCD, "BUS_ENDPOINT HALT: endpoint: %p", (int) endpoint); ++ RETURN_EINVAL_UNLESS(endpoint); ++ endpoint->feature_setting = flag; ++ return usbd_pcd_ops.halt_endpoint ? usbd_pcd_ops.halt_endpoint(pcd, endpoint, flag) : 0; ++} ++ ++/*! pcd_disable_endpoints - disable udc and all endpoints ++ */ ++static void pcd_disable_endpoints (struct usbd_bus_instance *bus) ++{ ++ struct pcd_instance *pcd = (struct pcd_instance *)bus->privdata; ++ int i; ++ RETURN_IF (!bus || !bus->endpoint_array); ++ for (i = 1; i < usbd_pcd_ops.max_endpoints; i++) { ++ struct usbd_endpoint_instance *endpoint = (bus->endpoint_array + i); ++ CONTINUE_IF (!endpoint); ++ usbd_flush_endpoint (endpoint); ++ } ++} ++ ++ ++/*! bus_event_handler - handle generic bus event ++ * Called by usb core layer to inform bus of an event. ++ */ ++int bus_event_handler (struct usbd_bus_instance *bus, usbd_device_event_t event, int data) ++{ ++ struct pcd_instance *pcd = (struct pcd_instance *)bus->privdata; ++ int epn; ++ //struct usbd_endpoint_map *endpoint_map_array = bus->function_instance->endpoint_map_array; ++ ++ //TRACE_MSG1(PCD, "BUS_EVENT %x", event); ++ switch (event) { ++ case DEVICE_UNKNOWN: ++ break; ++ ++ case DEVICE_INIT: ++ TRACE_MSG0(PCD, "EVENT INIT"); ++ break; ++ ++ case DEVICE_CREATE: ++ TRACE_MSG0(PCD, "EVENT CREATE"); ++ pcd_disable_endpoints (bus); ++ if (usbd_pcd_ops.start) usbd_pcd_ops.start (pcd); ++ break; ++ ++ case DEVICE_HUB_CONFIGURED: ++ TRACE_MSG0(PCD, "EVENT HUB_CONFIGURED"); ++ //bi_connect (bus, NULL); ++ break; ++ ++ case DEVICE_RESET: ++ TRACE_MSG0(PCD, "EVENT RESET"); ++ ++ if (usbd_pcd_ops.set_address) usbd_pcd_ops.set_address (pcd, 0); ++ ++ if (usbd_pcd_ops.reset_ep) usbd_pcd_ops.reset_ep (pcd, 0); ++ ++ pcd_disable_endpoints (bus); ++ otg_event_irq(pcd->otg, BUS_RESET | BUS_SUSPENDED_, PCD, "DEVICE_RESET"); ++ ++ break; ++ ++ case DEVICE_ADDRESS_ASSIGNED: ++ TRACE_MSG1(PCD, "EVENT ADDRESSED: %d", data); ++ ++ otg_event_irq(pcd->otg, ADDRESSED, PCD, "ADDRESSED"); ++ ++ break; ++ ++ case DEVICE_CONFIGURED: ++ TRACE_MSG0(PCD, "EVENT CONFIGURED"); ++ otg_event_irq(pcd->otg, CONFIGURED, PCD, "CONFIGURED"); ++ // iterate across the physical endpoint instance array to enable the endpoints ++ if (usbd_pcd_ops.setup_ep) ++ for (epn = 1; epn < bus->endpoints; epn++) ++ usbd_pcd_ops.setup_ep (pcd, epn, bus->endpoint_array + epn); ++ break; ++ ++ case DEVICE_DE_CONFIGURED: ++ TRACE_MSG0(PCD, "EVENT DE-CONFIGURED"); ++ break; ++ ++ case DEVICE_SET_INTERFACE: ++ TRACE_MSG0(PCD, "EVENT SET INTERFACE"); ++ break; ++ ++ case DEVICE_SET_FEATURE: ++ TRACE_MSG0(PCD, "EVENT SET FEATURE"); ++ break; ++ ++ case DEVICE_CLEAR_FEATURE: ++ TRACE_MSG0(PCD, "EVENT CLEAR FEATURE"); ++ break; ++ ++ case DEVICE_BUS_INACTIVE: ++ TRACE_MSG0(PCD, "EVENT INACTIVE"); ++ TRACE_MSG1(PCD, "EVENT INACTIVE: state: %x", bus->device_state); ++ otg_event_irq(pcd->otg, BUS_SUSPENDED, PCD, "DEVICE_BUS_INACTIVE"); ++ break; ++ ++ case DEVICE_BUS_ACTIVITY: ++ TRACE_MSG0(PCD, "EVENT ACTIVITY"); ++ otg_event_irq(pcd->otg, BUS_SUSPENDED_, PCD, "DEVICE_BUS_ACTIVITY"); ++ break; ++ ++ case DEVICE_POWER_INTERRUPTION: ++ TRACE_MSG0(PCD, "POWER INTERRUPTION"); ++ break; ++ ++ case DEVICE_HUB_RESET: ++ TRACE_MSG0(PCD, "HUB RESET"); ++ break; ++ ++ case DEVICE_DESTROY: ++ TRACE_MSG0(PCD, "DEVICE DESTROY"); ++ //bi_disconnect (bus, NULL); ++ pcd_disable_endpoints (bus); ++ if (usbd_pcd_ops.stop) usbd_pcd_ops.stop (pcd); ++ break; ++ ++ case DEVICE_CLOSE: ++ TRACE_MSG0(PCD, "DEVICE CLOSE"); ++ break; ++ default: ++ TRACE_MSG1(PCD, "DEVICE UNKNOWN: %d", event); ++ break; ++ } ++ TRACE_MSG2(PCD, "EVENT bNumInterfaces: %x alternates: %x", bus->bNumInterfaces, bus->alternates); ++ return 0; ++} ++ ++ ++/*! bus_start_endpoint_in ++ */ ++int bus_start_endpoint_in (struct usbd_bus_instance *bus, struct usbd_endpoint_instance *endpoint) ++{ ++ struct pcd_instance *pcd = (struct pcd_instance *)bus->privdata; ++ unsigned long flags; ++ ++ RETURN_ZERO_IF (!endpoint); ++ ocd_ops.interrupts++; ++ ++ local_irq_save (flags); ++ ++ // call pcd_start_endpoint_in IFF we didn't previously have a tx urb ++ if (!endpoint->tx_urb && pcd_tx_next_irq (endpoint)) { ++ usbd_pcd_ops.start_endpoint_in (pcd, endpoint); ++ } ++ local_irq_restore (flags); ++ return 0; ++} ++ ++/*! bus_start_endpoint_out ++ */ ++int bus_start_endpoint_out (struct usbd_bus_instance *bus, struct usbd_endpoint_instance *endpoint) ++{ ++ struct pcd_instance *pcd = (struct pcd_instance *)bus->privdata; ++ unsigned long flags; ++ ++ RETURN_ZERO_IF (!endpoint); ++ ocd_ops.interrupts++; ++ local_irq_save (flags); ++ // call pcd_start_endpoint_OUT IFF we didn't previously have a rcv urb ++ if (!endpoint->rcv_urb && pcd_rcv_next_irq (endpoint)) { ++ usbd_pcd_ops.start_endpoint_out (pcd, endpoint); ++ } ++ local_irq_restore (flags); ++ return 0; ++} ++ ++ ++/*! bus_cancel_urb_irq - cancel sending an urb ++ * Used by the USB Device Core to cancel an urb. ++ */ ++int bus_cancel_urb_irq (struct usbd_urb *urb) ++{ ++ struct usbd_bus_instance *bus = urb->bus; ++ struct pcd_instance *pcd = (struct pcd_instance *)bus->privdata; ++ RETURN_EINVAL_IF (!urb); ++ TRACE_MSG1(PCD, "BUS_CANCEL URB: %x", (int)urb); ++ switch (urb->endpoint->bEndpointAddress & USB_ENDPOINT_DIR_MASK) { ++ case USB_DIR_IN: ++ // is this the active urb? ++ if (urb->endpoint->tx_urb == urb) { ++ urb->endpoint->tx_urb = NULL; ++ TRACE_MSG1(PCD, "CANCEL IN URB: %02x", urb->endpoint->bEndpointAddress); ++ if (usbd_pcd_ops.cancel_in_irq) usbd_pcd_ops.cancel_in_irq (pcd, urb); ++ } ++ usbd_urb_finished_irq (urb, SEND_CANCELLED); ++ break; ++ ++ case USB_DIR_OUT: ++ // is this the active urb? ++ if (urb->endpoint->rcv_urb == urb) { ++ urb->endpoint->rcv_urb = NULL; ++ TRACE_MSG1(PCD, "CANCEL OUT URB: %02x", urb->endpoint->bEndpointAddress); ++ if (usbd_pcd_ops.cancel_out_irq) usbd_pcd_ops.cancel_out_irq (pcd, urb); ++ } ++ TRACE_MSG0(PCD, "CANCEL RECV URB"); ++ usbd_urb_finished_irq (urb, RECV_CANCELLED); ++ break; ++ } ++ urb->endpoint->last = urb->endpoint->sent = 0; ++ return 0; ++} ++ ++/*! pcd_rcv_finished_irq ++ */ ++struct usbd_urb * pcd_rcv_finished_irq (struct usbd_endpoint_instance *endpoint, int len, int urb_bad) ++{ ++ struct usbd_urb *rcv_urb; ++ ++ // if we had an urb then update actual_length, dispatch if neccessary ++ if (likely ( (int) (rcv_urb = endpoint->rcv_urb))) { ++ struct usbd_bus_instance *bus = rcv_urb->bus; ++ struct pcd_instance *pcd = (struct pcd_instance *)bus->privdata; ++ ++ //TRACE_MSG5(PCD, "BUS_RCV COMPLETE: finished buffer: %x actual: %d len: %d bad: %d: status: %d", ++ // rcv_urb->buffer, rcv_urb->actual_length, len, urb_bad, rcv_urb->status); ++ ++ if (!urb_bad && !endpoint->rcv_error && (rcv_urb->bus->status == USBD_OK)) { ++ ++#if 0 ++ int i; ++ u8 *cp = rcv_urb->buffer; ++ for (i = 0; i < rcv_urb->actual_length + len; i+= 8) { ++ ++ TRACE_MSG8(PCD, "RECV %02x %02x %02x %02x %02x %02x %02x %02x", ++ cp[i + 0], cp[i + 1], cp[i + 2], cp[i + 3], ++ cp[i + 4], cp[i + 5], cp[i + 6], cp[i + 7] ++ ); ++ } ++#endif ++ // increment the received data size ++ rcv_urb->actual_length += len; ++ ++ endpoint->rcv_urb = NULL; ++ rcv_urb->jiffies = jiffies; ++ rcv_urb->framenum = pcd_ops.framenum ? pcd_ops.framenum () : 0; ++ //TRACE_MSG1(PCD, "BUS_RCV COMPLETE: framenum: %x", (int) rcv_urb->framenum); ++ usbd_urb_finished_irq (rcv_urb, RECV_OK); ++ rcv_urb = NULL; ++ } ++ else { ++ rcv_urb->actual_length = 0; ++ //endpoint->rcv_error = 1; ++ } ++ } ++ ++ // if we don't have an urb see if we can get one ++ return pcd_rcv_next_irq (endpoint); ++} ++ ++/*! pcd_rcv_complete_irq ++ */ ++struct usbd_urb * pcd_rcv_complete_irq (struct usbd_endpoint_instance *endpoint, int len, int urb_bad) ++{ ++ struct usbd_urb *rcv_urb; ++ ++ // if we had an urb then update actual_length, dispatch if neccessary ++ if (likely ( (int) (rcv_urb = endpoint->rcv_urb))) { ++ struct usbd_bus_instance *bus = rcv_urb->bus; ++ struct pcd_instance *pcd = (struct pcd_instance *)bus->privdata; ++ ++ TRACE_MSG4(PCD, "BUS_RCV COMPLETE: actual: %d len: %d bad: %d: status: %d", ++ rcv_urb->actual_length, len, urb_bad, rcv_urb->status); ++ ++ //TRACE_MSG4(PCD, "BUS_RCV COMPLETE: request: %d buffer: %d packet: %d transfer: %d", ++ // rcv_urb->request_length, rcv_urb->buffer_length, ++ // endpoint->wMaxPacketSize, endpoint->rcv_transferSize); ++ ++ // check the urb is ok, are we adding data less than the packetsize ++ if (!urb_bad && !endpoint->rcv_error && (rcv_urb->bus->status == USBD_OK) && (len <= endpoint->wMaxPacketSize)) { ++ ++ // 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 ++ ++ // XXX this needs to be fixed, for example the MSC driver ++ // has varying maximum sizes ++ ++ ++ if ( ++ ( (len < endpoint->wMaxPacketSize) || ++ (rcv_urb->actual_length >= endpoint->rcv_transferSize) || ++ (rcv_urb->actual_length >= rcv_urb->request_length) || ++ (rcv_urb->actual_length + endpoint->wMaxPacketSize > rcv_urb->buffer_length))) ++ { ++#if 0 ++ int i; ++ u8 *cp = rcv_urb->buffer; ++ for (i = 0; i < rcv_urb->actual_length; i+= 8) ++ TRACE_MSG8(PCD, "RECV %02x %02x %02x %02x %02x %02x %02x %02x", ++ cp[i + 0], cp[i + 1], cp[i + 2], cp[i + 3], ++ cp[i + 4], cp[i + 5], cp[i + 6], cp[i + 7] ++ ); ++#endif ++ endpoint->rcv_urb = NULL; ++ rcv_urb->jiffies = jiffies; ++ rcv_urb->framenum = pcd_ops.framenum ? pcd_ops.framenum () : 0; ++ TRACE_MSG2(PCD, "BUS_RCV COMPLETE: finished length: %d framenum: %x", ++ rcv_urb->actual_length, (int) rcv_urb->framenum); ++ usbd_urb_finished_irq (rcv_urb, RECV_OK); ++ rcv_urb = NULL; ++ } ++ } ++ else { ++ rcv_urb->actual_length = 0; ++ //endpoint->rcv_error = 1; ++ } ++ } ++ ++ // if we don't have an urb see if we can get one ++ return pcd_rcv_next_irq (endpoint); ++} ++ ++/*! pcd_tx_complete_irq ++ */ ++struct usbd_urb * pcd_tx_complete_irq (struct usbd_endpoint_instance *endpoint, int restart) ++{ ++ struct usbd_urb *tx_urb; ++ ++ TRACE_MSG2(PCD, "tx_urb: %x restart: %d", endpoint->tx_urb, restart); ++ ++ // if we have a tx_urb advance or reset, finish if complete ++ if ( (tx_urb = endpoint->tx_urb)) { ++ ++ struct usbd_bus_instance *bus = tx_urb->bus; ++ struct pcd_instance *pcd = (struct pcd_instance *)bus->privdata; ++ ++ TRACE_MSG5(PCD, "BUS_TX CURRENT TX_URB: %p actual: %d sent: %d last: %d: flags: %d", ++ (int)endpoint->tx_urb, tx_urb->actual_length, endpoint->sent, ++ endpoint->last, tx_urb->flags); ++ ++ if (likely (!restart)) { ++ int sent = endpoint->last; ++ endpoint->sent += sent; ++ endpoint->last -= sent; ++ } ++ else { ++ TRACE_MSG0(PCD, "RESTARTING"); ++ endpoint->last = 0; ++ } ++ ++ //if ( ( (tx_urb->actual_length - endpoint->sent) <= 0) && ! (tx_urb->flags & USBD_URB_SENDZLP) ) { ++ ++ if ( (endpoint->sent >= tx_urb->actual_length) && ! (tx_urb->flags & USBD_URB_SENDZLP) ) { ++ tx_urb->jiffies = jiffies; ++ tx_urb->framenum = pcd_ops.framenum ? pcd_ops.framenum () : 0; ++ TRACE_MSG2(PCD, "BUS_TX COMPLETE: finished tx_urb: %p framenum: %x", ++ (int)tx_urb, (int)tx_urb->framenum); ++ usbd_urb_finished_irq (tx_urb, SEND_FINISHED_OK); ++ endpoint->tx_urb = NULL; ++ endpoint->last = endpoint->sent = 0; ++ } ++ } ++ return pcd_tx_next_irq (endpoint); ++} ++ ++/* ******************************************************************************************* */ ++ ++/*! pcd_disable - Stop using the USB Device Controller ++ * Stop using the USB Device Controller. ++ * Shutdown and free dma channels, de-register the interrupt handler. ++ */ ++void pcd_disable (struct usbd_bus_instance *bus) ++{ ++ struct pcd_instance *pcd = (struct pcd_instance *)bus->privdata; ++ TRACE_MSG0(PCD, "BUS_UDC DISABLE"); ++ if (usbd_pcd_ops.disable_ep) usbd_pcd_ops.disable_ep (pcd, 0); ++ pcd_disable_endpoints (bus); ++ ++ if (usbd_pcd_ops.disable) usbd_pcd_ops.disable (pcd); ++} ++ ++/* ************************************************************************************* */ ++/*! bus_framenum ++ */ ++int bus_framenum (void) ++{ ++ return (pcd_ops.framenum) ? pcd_ops.framenum () : 0; ++} ++ ++/*! bus_interrupts ++ */ ++u64 bus_interrupts (void) ++{ ++ return ocd_ops.interrupts; ++} ++ ++/*! bus_ticks ++ */ ++u64 bus_ticks (void) ++{ ++ return (ocd_ops.ticks) ? ocd_ops.ticks () : 0; ++} ++ ++/*! bus_elapsed ++ */ ++u64 bus_elapsed (u64 *t1, u64 *t2) ++{ ++ return (ocd_ops.elapsed) ? (ocd_ops.elapsed (t1, t2)) : 0; ++} ++ ++/*! pcd_recv_setup_emulate_irq - emulate a device request ++ */ ++int pcd_recv_setup_emulate_irq(struct usbd_bus_instance *bus, u8 bmRequestType, u8 bRequest, u16 wValue, u16 wIndex, u16 wLength) ++{ ++ struct pcd_instance *pcd = (struct pcd_instance *)bus->privdata; ++ struct usbd_device_request request; ++ ++ TRACE_MSG4(PCD, "bmRequestType: %02x bRequest: %02x wValue: %04x wIndex: %04x", bmRequestType, bRequest, wValue, wIndex); ++ request.bmRequestType = bmRequestType; ++ request.bRequest = bRequest; ++ request.wValue = cpu_to_le16(wValue); ++ request.wIndex = cpu_to_le16(wIndex); ++ request.wLength = cpu_to_le16(wLength); ++ return pcd_recv_setup_irq(pcd, &request); ++} ++ ++/*! pcd_check_device_feature - verify that feature is set or clear ++ * Check current feature setting and emulate SETUP Request to set or clear ++ * if required. ++ */ ++void pcd_check_device_feature(struct usbd_bus_instance *bus, int feature, int flag) ++{ ++ int status = bus->device_feature_settings & (1 << feature); ++ TRACE_MSG2(PCD, "BUS_CHECK FEATURE: feature: %x flag: %x", feature, flag); ++ if (!status && flag) ++ pcd_recv_setup_emulate_irq(bus, USB_REQ_HOST2DEVICE, USB_REQ_SET_FEATURE, feature, 0, 0); ++ else if (status && !flag) ++ pcd_recv_setup_emulate_irq(bus, USB_REQ_HOST2DEVICE, USB_REQ_CLEAR_FEATURE, feature, 0, 0); ++ TRACE_MSG1(PCD, "BUS_CHECK FEATURE: features; %08x", bus->device_feature_settings); ++} ++ ++/* ******************************************************************************************* */ ++#if !defined(OTG_C99) ++struct usbd_bus_operations bus_ops; ++ ++void pcd_global_init(void) ++{ ++ ZERO(bus_ops); ++ bus_ops.start_endpoint_in = bus_start_endpoint_in; ++ bus_ops.start_endpoint_out = bus_start_endpoint_out; ++ bus_ops.cancel_urb_irq = bus_cancel_urb_irq; ++ bus_ops.endpoint_halted = bus_endpoint_halted; ++ bus_ops.halt_endpoint = bus_halt_endpoint; ++ bus_ops.set_address = bus_set_address; ++ bus_ops.event_handler = bus_event_handler; ++ bus_ops.request_endpoints = bus_request_endpoints; ++ bus_ops.set_endpoints = bus_set_endpoints; ++ bus_ops.framenum = bus_framenum; ++ bus_ops.ticks = bus_ticks; ++ bus_ops.elapsed = bus_elapsed; ++ bus_ops.interrupts = bus_interrupts; ++ //bus_ops.device_feature = bus_device_feature; ++} ++ ++#else /* !defined(OTG_C99) */ ++struct usbd_bus_operations bus_ops = { ++ .start_endpoint_in = bus_start_endpoint_in, ++ .start_endpoint_out = bus_start_endpoint_out, ++ .cancel_urb_irq = bus_cancel_urb_irq, ++ .endpoint_halted = bus_endpoint_halted, ++ .halt_endpoint = bus_halt_endpoint, ++ .set_address = bus_set_address, ++ .event_handler = bus_event_handler, ++ .request_endpoints = bus_request_endpoints, ++ .set_endpoints = bus_set_endpoints, ++ .framenum = bus_framenum, ++ .ticks = bus_ticks, ++ .elapsed = bus_elapsed, ++ .interrupts = bus_interrupts, ++ //.device_feature = bus_device_feature, ++}; ++#endif /* !defined(OTG_C99) */ ++ ++ ++struct usbd_bus_driver bus_driver = { ++ bops: &bus_ops, ++}; ++ ++ ++ ++/* ******************************************************************************************* */ ++/* Prevent overlapp of bi administrative functions mainly: ++ * bus_register_bh ++ * bus_deregister_bh ++ */ ++//DECLARE_MUTEX (usbd_bi_sem); ++struct usbd_bus_instance *usbd_bus; ++ ++/*! pcd_startup_events ++ */ ++void pcd_startup_events(struct usbd_bus_instance *bus) ++{ ++ struct pcd_instance *pcd = (struct pcd_instance *)bus->privdata; ++ //TRACE_MSG0(PCD, "BI_STARTUP"); ++ if (usbd_pcd_ops.startup_events) { ++ TRACE_MSG0(PCD, "BI_STARTUP_EVENTS - udc"); ++ usbd_pcd_ops.startup_events(pcd); ++ } ++ else { ++ TRACE_MSG0(PCD, "BI_STARTUP_EVENTS - default"); ++ usbd_bus_event_handler_irq (bus, DEVICE_INIT, 0); ++ usbd_bus_event_handler_irq (bus, DEVICE_CREATE, 0); ++ usbd_bus_event_handler_irq (bus, DEVICE_HUB_CONFIGURED, 0); ++ usbd_bus_event_handler_irq (bus, DEVICE_RESET, 0); ++ } ++ //TRACE_MSG0(PCD, "BI_STARTUP: FINISHED"); ++} ++ ++/*! bus_register_bh ++ */ ++void bus_register_bh(void *arg) ++{ ++ struct pcd_instance *pcd = arg; ++ struct otg_instance *otg = pcd->otg; ++ struct usbd_bus_instance *bus; ++ struct usbd_endpoint_instance *endpoint; ++ unsigned long flags; ++ ++ TRACE_MSG1(PCD, "BUS_REGISTER_BH: pcd: %p", (int)arg); ++ RETURN_UNLESS(pcd); ++ ++ TRACE_MSG0(PCD, "BUS_REGISTER_BH"); ++ ++ bus_driver.name = usbd_pcd_ops.name; ++ bus_driver.max_endpoints = usbd_pcd_ops.max_endpoints; ++ bus_driver.maxpacketsize = usbd_pcd_ops.ep0_packetsize; ++ bus_driver.capabilities = usbd_pcd_ops.capabilities; ++ bus_driver.bMaxPower = usbd_pcd_ops.bMaxPower; ++ bus_driver.ports = usbd_pcd_ops.ports; ++ // XXX bus_driver.otg_bmAttributes = tcd_ops.bmAttributes; ++ bus_driver.otg_bmAttributes = usbd_pcd_ops.bmAttributes; ++ ++ TRACE_MSG1(PCD, "BUS_REGISTER_BH UDC Capabilities: %x", bus_driver.capabilities); ++ // XXX TRACE_MSG1(PCD, "BUS_REGISTER_BH UDC OTG Attributes: %x\n", tcd_ops.bmAttributes); ++ TRACE_MSG1(PCD, "BUS_REGISTER_BH UDC OTG Attributes: %x\n", usbd_pcd_ops.bmAttributes); ++ ++ // register this bus interface driver and create the device driver instance ++ if (! (bus = usbd_register_bus (&bus_driver, usbd_pcd_ops.ep0_packetsize))) { ++ TRACE_MSG0(PCD, "BUS_REGISTER_BH: register failed"); ++ printk (KERN_INFO "%s: failed\n", __FUNCTION__); ++ pcd_disable (NULL); ++ otg_event(pcd->otg, enable_otg_ | PCD_OK, PCD, "BUS_REGISTER_BH"); ++ return; ++ } ++ ++ bus->privdata = (void *) pcd; ++ pcd->bus = bus; ++ ++ //if (usbd_pcd_ops.serial_init ? usbd_pcd_ops.serial_init (pcd) : -EINVAL) { ++ // ++ //} ++ ++ if (serial_number_str && strlen(serial_number_str)) { ++ char *sp, *dp; ++ int i; ++ printk(KERN_INFO"%s:\n", __FUNCTION__); ++ printk(KERN_INFO"%s: serial_number_str: %s\n", __FUNCTION__, serial_number_str); ++ TRACE_MSG1(PCD, "prm serial_number_str: %s", serial_number_str); ++ for (sp = serial_number_str, dp = otg->serial_number, i = 0; ++ *sp && (i < (sizeof(otg->serial_number) - 1)); i++, sp++) ++ if (isxdigit(*sp)) *dp++ = toupper(*sp); ++ } ++ ++ ++ TRACE_MSG1(PCD, "otg serial_number_str: %s", pcd->otg->serial_number); ++ TRACE_MSG0(PCD, "BUS_REGISTER_BH: usbd_enable_function_irq"); ++ ++ if (usbd_enable_function_irq (bus, pcd->otg->function_name, pcd->otg->serial_number)) { ++ TRACE_MSG0(PCD, "BUS_REGISTER_BH: enable function failed"); ++ printk (KERN_INFO "%s: failed\n", __FUNCTION__); ++ pcd_disable (NULL); ++ otg_event(pcd->otg, enable_otg_ | PCD_OK, PCD, "BUS_REGISTER_BH"); ++ return; ++ } ++ ++ // setup endpoint zero ++ endpoint = bus->endpoint_array + 0; ++ endpoint->bEndpointAddress = 0; ++ ++ //endpoint->tx_attributes = 0; ++ // ++ ++ endpoint->wMaxPacketSize = usbd_pcd_ops.ep0_packetsize; ++ endpoint->rcv_transferSize = 4096; // XXX should this be higher ++ endpoint->wMaxPacketSize = usbd_pcd_ops.ep0_packetsize; ++ if (usbd_pcd_ops.setup_ep) usbd_pcd_ops.setup_ep (pcd, 0, endpoint); ++ ++ TRACE_MSG0(PCD, "BUS_REGISTER_BH: startup"); ++ ++ // hopefully device enumeration will finish this process ++ // XXX should this move to pcd_en? ++ pcd_startup_events (bus); ++ TRACE_MSG0(PCD, "BUS_REGISTER_BH: FINISHED - sending PCD_OK"); ++ //MOD_INC_USE_COUNT; ++ otg_event(pcd->otg, PCD_OK, PCD, "BUS_REGISTER_BH PCD_OK"); ++} ++ ++/*! bus_deregister_bh ++ */ ++void bus_deregister_bh(void *arg) ++{ ++ struct pcd_instance *pcd = arg; ++ struct usbd_bus_instance *bus; ++ struct bus_data *data; ++ unsigned long flags; ++ ++ //TRACE_MSG1(PCD, "BUS_DEREGISTER_BH: pcd: %x", pcd); ++ if (pcd && (bus = pcd->bus) && (usbd_bus_state_enabled == bus->bus_state)) { ++ ++ if (usbd_pcd_ops.disable) usbd_pcd_ops.disable (pcd); ++ ++ if (bus->device_state != STATE_ATTACHED) { ++ usbd_bus_event_handler_irq (bus, DEVICE_RESET, 0); ++ usbd_bus_event_handler_irq (bus, DEVICE_POWER_INTERRUPTION, 0); ++ usbd_bus_event_handler_irq (bus, DEVICE_HUB_RESET, 0); ++ } ++ usbd_bus_event_handler_irq (bus, DEVICE_DESTROY, 0); ++ pcd_disable_endpoints (bus); ++ pcd_disable (bus); ++ ++ usbd_disable_function (bus); ++ bus->bus_state = usbd_bus_state_disabled; ++ ++ //TRACE_MSG1(PCD, "%s: BI_SEM UP", (int)__FUNCTION__); ++ //otg_event(pcd->otg, exit_ok, "BUS_DISABLE EXIT_OK"); ++ ++ //if (bus->serial_number_str) ++ // lkfree (pcd->bus->serial_number_str); ++ ++ usbd_deregister_bus (bus); ++ pcd->bus = NULL; ++ ++ TRACE_MSG0(PCD, "BUS_DEREGISTER_BH: FINISHED - sending PCD_OK"); ++ //MOD_DEC_USE_COUNT; ++ } ++ otg_event(pcd->otg, PCD_OK, PCD, "BUS_DEREGISTER_BH PCD_OK"); ++} ++ ++/* ************************************************************************************* */ ++ ++ ++/*! pcd_en_func - enable ++ * ++ * This is called to enable / disable the PCD and USBD stack. ++ */ ++void pcd_en_func (struct otg_instance *otg, u8 flag) ++{ ++ struct pcd_instance *pcd = otg->pcd; ++ struct usbd_bus_instance *bus = pcd->bus; ++ ++ //TRACE_MSG0(PCD, "--"); ++ switch (flag) { ++ case SET: ++ TRACE_MSG0(PCD, "PCD_EN: SET"); ++#ifdef CONFIG_OTG_DB1550_J15 ++ int ret = au_readl(SYS_PINFUNC); ++ au_writel((ret | 0x8000),SYS_PINFUNC); ++ ret = au_readl(SYS_PINFUNC); ++ TRACE_MSG1(PCD, "Set the OTG for device mode (sys_pfunc = %04x)", ret); ++#endif ++ // check if we can see the UDC and register, then enable the function ++ if (usbd_pcd_ops.enable) usbd_pcd_ops.enable (pcd); ++ //BREAK_IF (pcd_enable_irq (bus)); ++ //BREAK_IF (usbd_enable_function_irq (bus, otg->function_name)); ++ ++ // XXX should this move to here ++ // pcd_startup_events (bus); ++ break; ++ ++ case RESET: ++ TRACE_MSG0(PCD, "PCD_EN: RESET"); ++ usbd_bus_event_handler_irq (bus, DEVICE_RESET, 0); ++ TRACE_MSG0(PCD, "PCD_EN: DESTROY"); ++ usbd_bus_event_handler_irq (bus, DEVICE_DESTROY, 0); ++ TRACE_MSG0(PCD, "PCD_EN: FINISHED"); ++ // XXX need to set a flag here ++ break; ++ } ++} ++ ++/*! pcd_init_func - per peripheral controller common initialization ++ * ++ * This is called to initialize / de-initialize the PCD and USBD stack. ++ * ++ * We start work items to do this. ++ * ++ */ ++void pcd_init_func (struct otg_instance *otg, u8 flag) ++{ ++ struct pcd_instance *pcd = otg->pcd; ++ struct usbd_bus_instance *bus = pcd->bus; ++ //struct bus_data *data = NULL; ++ ++ //printk(KERN_INFO"%s:\n", __FUNCTION__); ++ ++ //TRACE_MSG0(PCD, "--"); ++ switch (flag) { ++ case SET: ++ TRACE_MSG0(PCD, "PCD_INIT: SET"); ++ PREPARE_WORK_ITEM(pcd->bh, bus_register_bh, pcd); ++ SCHEDULE_WORK(pcd->bh); ++ TRACE_MSG0(PCD, "BUS_REGISTER_SCHEDULE: finished"); ++ break; ++ ++ case RESET: ++ TRACE_MSG0(PCD, "PCD_INIT: RESET"); ++ PREPARE_WORK_ITEM(pcd->bh, bus_deregister_bh, pcd); ++ SCHEDULE_WORK(pcd->bh); ++ } ++} ++ +diff -uNr linux/drivers/no-otg/ocd/otg-pcd/tr-init-l24.c linux/drivers/otg/ocd/otg-pcd/tr-init-l24.c +--- linux/drivers/no-otg/ocd/otg-pcd/tr-init-l24.c 1970-01-01 01:00:00.000000000 +0100 ++++ linux/drivers/otg/ocd/otg-pcd/tr-init-l24.c 2006-09-01 21:41:32.000000000 +0200 +@@ -0,0 +1,234 @@ ++/* ++ * otg/ocd/otg-pcd/tr-init-l24.c - Traditional Device Peripheral and OTG Controller Drivers Module Initialization ++ * ++ * Copyright (c) 2004 Belcarra ++ * ++ * By: ++ * Stuart Lynne <sl@belcarra.com>, ++ * Tom Rushworth <tbr@belcarra.com>, ++ * Bruce Balden <balden@belcarra.com> ++ * ++ */ ++/*! ++ * @file otg/ocd/otg-pcd/tr-init-l24.c ++ * @brief Traditional device driver init. ++ * ++ * TR OTG PCD/OCD/TCD Initialization ++ * ++ * This file initializes all of the low level hardware drivers for a traditional device. ++ * ++ * Traditional USB Devices must implement the following: ++ * ++ * ocd_ops operations from ocd driver ++ * pcd_ops operations from pcd driver ++ * tcd_ops operations from tcd driver ++ * ++ * The OTG Controller driver (ocd) implements any required OTG timers and if ++ * necessary mediates access to and initializes the OTG and/or USB hardware. ++ * ++ * The Peripheral Controller Driver (pcd) implements the lowest layer of the ++ * USBD stack to send and receive data from the USB actings as a USB ++ * peripheral. ++ * ++ * The Transceiver Controller Driver (tcd) implements control of the OTG or ++ * USB transceiver. At a minimum for a traditional device this should ++ * implement VBUS sensing (sometimes known as cable detect) and where ++ * possible control over the DP+ pullup resistor. ++ * ++ * ++ * Notes ++ * ++ * 1. This is the linux 2.4 version. ++ * ++ * 2. This will optionally do an auto start with TR_INIT if the OCD_CAPBILITIES_AUTO ++ * flag is set. ++ * ++ * TODO ++ * ++ * 1. hook up serial_number_str to pcd.c for use with bus interface layer. ++ * ++ * @ingroup OTGPCD ++ */ ++ ++#include <otg/otg-compat.h> ++#include <otg/otg-module.h> ++ ++#include <otg/usbp-chap9.h> ++#include <otg/usbp-bus.h> ++#include <otg/otg-trace.h> ++#include <otg/otg-api.h> ++#include <otg/otg-tcd.h> ++#include <otg/otg-hcd.h> ++#include <otg/otg-pcd.h> ++#include <otg/otg-ocd.h> ++ ++EMBED_LICENSE(); ++ ++MOD_PARM (serial_number_str, "s"); ++MOD_PARM_DESC (serial_number_str, "Serial Number"); ++MOD_AUTHOR ("sl@belcarra.com, tbr@belcarra.com"); ++MOD_DESCRIPTION ("Traditional USB Device"); ++ ++char *serial_number_str; ++ ++otg_tag_t OCD; ++struct ocd_instance *ocd_instance; ++ ++otg_tag_t PCD; ++struct pcd_instance *pcd_instance; ++ ++otg_tag_t TCD; ++struct tcd_instance *tcd_instance; ++ ++ ++/* ************************************************************************************* */ ++ ++/* tr_ocd_start_timer ++ * Fake - Set or reset timer to interrupt in number of uS (micro-seconds). ++ * This is only suitable for MN or TR firmware. ++ */ ++int tr_ocd_start_timer(struct otg_instance *otg, int usec) ++{ ++ otg_event(otg, TMOUT, OCD, "FAKE TMOUT"); ++ return 0; ++} ++ ++/* ************************************************************************** */ ++/* tr_tcd_en_func - used to enable or disable transciever ++ * ++ */ ++void tr_tcd_en_func(struct otg_instance *otg, u8 flag) ++{ ++ struct pcd_instance *pcd = (struct pcd_instance *) otg->pcd; ++ struct usbd_bus_instance *bus = pcd->bus; ++ ++ //printk(KERN_INFO"%s: start\n", __FUNCTION__); ++ TRACE_MSG0(TCD, "--"); ++ TRACE_MSG2(TCD, "pcd: %x bus: %x", pcd, pcd->bus); ++ ++ switch (flag) { ++ case PULSE: ++ case SET: ++ TRACE_MSG0(TCD, "TR_TCD_EN SET"); ++ /* enable clock interrupt */ ++ otg_event_set_irq(tcd_instance->otg, 1, 1, B_SESS_VLD, TCD, "B_SESS_VLD"); ++ break; ++ case RESET: ++ TRACE_MSG0(TCD, "TR_TCD_EN RESET"); ++ /* disable clock interrupt */ ++ break; ++ } ++// TRACE_MSG2(TCD, "ep0 endpoint: %x bEndpointAddress: %02x BBB", bus->ep0->endpoint_map_array->endpoint, ++ // bus->ep0->endpoint_map_array->endpoint->bEndpointAddress); ++} ++ ++ ++/* ************************************************************************** */ ++ ++/* tr_modexit - This is used as module exit, and as cleanup if modinit fails. ++ * ++ * Specifically for each driver: ++ * ++ * call ops.mod_exit ++ * reset instance address and ops table address in state machine to NULL ++ * invalidate tag ++ */ ++static void tr_modexit (void) ++{ ++ struct otg_instance *otg = ocd_instance->otg; ++ printk(KERN_INFO"%s\n", __FUNCTION__); ++ ++ TRACE_MSG1(TCD, "otg: %x", otg); ++ if (otg) { ++ TRACE_MSG0(TCD, "Calling otg_exit()"); ++ otg_exit(otg); ++ TRACE_MSG0(TCD, "back from otg_exit()"); ++ } ++ else ++ TRACE_MSG0(TCD, "did not call otg_exit()"); ++ ++#if 0 ++ if ((ocd_ops.capabilities & OCD_CAPABILITIES_TR) && (ocd_ops.capabilities & OCD_CAPABILITIES_AUTO)) { ++ printk(KERN_INFO"%s calling otg_admin\n",__FUNCTION__); ++ otg_admin("exit_all", "Traditional Device Auto TR_INIT"); ++ } ++#endif ++ ++ if (tcd_ops.mod_exit) tcd_ops.mod_exit(); ++ if (ocd_ops.mod_exit) ocd_ops.mod_exit(); ++ if (pcd_ops.mod_exit) pcd_ops.mod_exit(); ++ ++ tcd_instance = otg_set_tcd_ops(NULL); ++ ocd_instance = otg_set_ocd_ops(NULL); ++ pcd_instance = otg_set_pcd_ops(NULL); ++ ++ TCD = otg_trace_invalidate_tag(TCD); ++ OCD = otg_trace_invalidate_tag(OCD); ++ PCD = otg_trace_invalidate_tag(PCD); ++ printk(KERN_INFO"%s finished\n", __FUNCTION__); ++} ++ ++ ++/* tr_modinit - linux module initialization ++ * ++ * This needs to initialize the ocd, pcd and tcd drivers. ++ * ++ * Specifically for each driver: ++ * ++ * obtain tag ++ * pass ops table address to state machine and get instance address ++ * call ops.mod_init ++ * ++ */ ++static int tr_modinit (void) ++{ ++ struct otg_instance *otg; ++ printk(KERN_INFO"%s\n", __FUNCTION__); ++ ++ #if !defined(OTG_C99) ++ pcd_global_init(); ++ #endif /* !defined(OTG_C99) */ ++ ++ /* For each of ocd, pcd and tcd do: ++ * ++ * get tag ++ * update ops table with default functions where possible ++ * do otg_set_xxx_ops() as appropriate ++ * call xxx_ops.mod_init as appropriate ++ */ ++ ++ OCD = otg_trace_obtain_tag(); ++ PCD = otg_trace_obtain_tag(); ++ TCD = otg_trace_obtain_tag(); ++ ++ UNLESS(ocd_ops.start_timer) ocd_ops.start_timer = tr_ocd_start_timer; ++ UNLESS(pcd_ops.pcd_init_func) pcd_ops.pcd_init_func = pcd_init_func; ++ UNLESS(pcd_ops.pcd_en_func) pcd_ops.pcd_en_func = pcd_en_func; ++ UNLESS(tcd_ops.tcd_en_func) tcd_ops.tcd_en_func = tr_tcd_en_func; ++ ++ THROW_IF((ocd_ops.mod_init) ? ocd_ops.mod_init() : 0, error); ++ THROW_IF((pcd_ops.mod_init) ? pcd_ops.mod_init() : 0, error); ++ THROW_IF((tcd_ops.mod_init) ? tcd_ops.mod_init() : 0, error); ++ ++ THROW_UNLESS(ocd_instance = otg_set_ocd_ops(&ocd_ops), error); ++ THROW_UNLESS(pcd_instance = otg_set_pcd_ops(&pcd_ops), error); ++ THROW_UNLESS(tcd_instance = otg_set_tcd_ops(&tcd_ops), error); ++ ++ THROW_UNLESS(ocd_instance && (otg = ocd_instance->otg), error); ++ ++ CATCH(error) { ++ tr_modexit(); ++ return -EINVAL; ++ } ++ /* Success! ++ */ ++ otg_init(otg); ++#if 0 ++ otg_start(otg, ocd_ops.capabilities & OCD_CAPABILITIES_TR); ++#endif ++ return 0; ++} ++ ++module_init(tr_modinit); ++MOD_EXIT (tr_modexit); ++ +diff -uNr linux/drivers/no-otg/ocd/otg-tcd/tcd-init-l24.c linux/drivers/otg/ocd/otg-tcd/tcd-init-l24.c +--- linux/drivers/no-otg/ocd/otg-tcd/tcd-init-l24.c 1970-01-01 01:00:00.000000000 +0100 ++++ linux/drivers/otg/ocd/otg-tcd/tcd-init-l24.c 2006-09-01 21:41:33.000000000 +0200 +@@ -0,0 +1,95 @@ ++/* ++ * otg/ocd/otg-tcd/tcd-init-l24.c - OTG Transceiver Controller Driver Module Initialization ++ * ++ * Copyright (c) 2004 Belcarra ++ * ++ * By: ++ * Stuart Lynne <sl@belcara.com>, ++ * Tom Rushworth <tbr@belcara.com>, ++ * Bruce Balden <balden@belcara.com> ++ * ++ */ ++/*! ++ * @file otg/ocd/otg-tcd/tcd-init-l24.c ++ * @brief PCD only driver init. ++ * ++ * OTG TCD Initialization ++ * ++ * This file initializes the low level hardware drivers. ++ * ++ * This is the linux 2.4 version. ++ * ++ * @ingroup OTGTCD ++ */ ++ ++#include <otg/otg-compat.h> ++#include <otg/otg-module.h> ++ ++#include <otg/usbp-chap9.h> ++#include <otg/usbp-bus.h> ++#include <otg/otg-trace.h> ++#include <otg/otg-api.h> ++#include <otg/otg-tcd.h> ++#include <otg/otg-hcd.h> ++#include <otg/otg-pcd.h> ++ ++ ++#ifdef MODULE ++#if LINUX_VERSION_CODE >= KERNEL_VERSION (2,4,17) ++MODULE_LICENSE ("GPL"); ++#endif ++MODULE_AUTHOR ("sl@belcarra.com, tbr@belcarra.com"); ++MODULE_DESCRIPTION ("USB On-The-Go TCD"); ++#endif ++ ++otg_tag_t TCD; ++ ++extern struct tcd_ops tcd_ops; ++struct tcd_instance *tcd_instance; ++ ++//extern void db1550_tcd_global_init(void); ++ ++/* ************************************************************************************* */ ++ ++/* tcd_modexit - This is *only* used for drivers compiled and used as a module. ++ */ ++static void tcd_modexit (void) ++{ ++ struct otg_instance *otg = tcd_instance->otg; ++ //printk(KERN_INFO"%s\n", __FUNCTION__); ++ ++ if (otg) ++ otg_exit(otg); ++ ++ if (tcd_ops.mod_exit) tcd_ops.mod_exit(); ++ tcd_instance = otg_set_tcd_ops(NULL); ++ otg_trace_invalidate_tag(TCD); ++} ++ ++/* otg_modinit - linux module initialization ++ * ++ * This needs to initialize the hcd, pcd and tcd drivers. This includes tcd and possibly hcd ++ * for some architectures. ++ * ++ */ ++static int tcd_modinit (void) ++{ ++ printk(KERN_INFO"Initilize the tcd structure\n"); ++// db1550_tcd_global_init(); ++ TCD = otg_trace_obtain_tag(); ++ THROW_UNLESS(tcd_instance = otg_set_tcd_ops(&tcd_ops), error); ++ THROW_IF((tcd_ops.mod_init) ? tcd_ops.mod_init() : 0, error); ++ //printk(KERN_INFO"%s finish\n", __FUNCTION__); ++ ++ TRACE_MSG0(TCD, "--"); ++ ++ CATCH(error) { ++ tcd_modexit(); ++ return -EINVAL; ++ } ++ return 0; ++} ++ ++module_init (tcd_modinit); ++module_exit (tcd_modexit); ++ +diff -uNr linux/drivers/no-otg/ocd/otg-tcd/tcd.c linux/drivers/otg/ocd/otg-tcd/tcd.c +--- linux/drivers/no-otg/ocd/otg-tcd/tcd.c 1970-01-01 01:00:00.000000000 +0100 ++++ linux/drivers/otg/ocd/otg-tcd/tcd.c 2006-09-01 21:41:33.000000000 +0200 +@@ -0,0 +1,55 @@ ++/* ++ * otg/ocd/otg-tcd/tcd.c - OTG Transceiver Controller Driver ++ * ++ * Copyright (c) 2004 Belcarra ++ * ++ * By: ++ * Stuart Lynne <sl@belcara.com>, ++ * Tom Rushworth <tbr@belcara.com>, ++ * Bruce Balden <balden@belcara.com> ++ * ++ */ ++/*! ++ * @file otg/ocd/otg-tcd/tcd.c ++ * @brief TCD Support ++ * Notes ++ * ++ * @ingroup OTGTCD ++ */ ++ ++#include <otg/otg-compat.h> ++ ++#include <otg/usbp-chap9.h> ++#include <otg/usbp-bus.h> ++#include <otg/otg-trace.h> ++#include <otg/otg-api.h> ++#include <otg/otg-tcd.h> ++#include <otg/otg-hcd.h> ++#include <otg/otg-pcd.h> ++ ++extern struct tcd_ops tcd_ops; ++ ++/* ************************************************************************************* */ ++void tcd_cable_event_irq (struct otg_instance *otg) ++{ ++ TRACE_MSG1(TCD, "TCD_CABLE_EVENT: vbus: %d", tcd_vbus(otg)); ++ otg_event(otg, VBUS_VLD, TCD, "CABLE"); ++ //otg_b_sess_vld(otg, 1, tcd_vbus (otg), "Cable Event"); ++} ++ ++void tcd_cable_event (struct otg_instance *otg) ++{ ++ unsigned long flags; ++ local_irq_save (flags); ++ tcd_cable_event_irq (otg); ++ local_irq_restore (flags); ++} ++ ++/* ************************************************************************************* */ ++int tcd_vbus (struct otg_instance *otg) ++{ ++ TRACE_MSG0(TCD, "OCD VBUS"); ++ return tcd_ops.vbus ? tcd_ops.vbus (otg) : 1; ++} ++ ++ +diff -uNr linux/drivers/no-otg/otg/hcd-hw.h linux/drivers/otg/otg/hcd-hw.h +--- linux/drivers/no-otg/otg/hcd-hw.h 1970-01-01 01:00:00.000000000 +0100 ++++ linux/drivers/otg/otg/hcd-hw.h 2006-09-01 21:41:33.000000000 +0200 +@@ -0,0 +1,101 @@ ++/* ++ * otg/hcd/hc_xfer_hw.h - Generic transfer level USBOTG aware Host Controller Driver (HCD) ++ * ++ * Copyright (c) 2004 Belcarra Technologies ++ * ++ * By: ++ * Tom Rushworth <tbr@belcara.com>, ++ * Stuart Lynne <sl@belcara.com>, ++ * Bruce Balden <balden@belcara.com> ++ * ++ */ ++/*! ++ * @file otg/otg/hcd-hw.h ++ * @brief Implements Generic Host Controller Driver ++ * ++ * @ingroup OTGHCD ++ */ ++#ifndef HC_XFER_HW_H ++#define HC_XFER_HW_H 1 ++ ++/*===========================================================================* ++ * Functions provided by the hardware specific component (whatever HW it is). ++ * Each HW specific component provides these functions. ++ *===========================================================================*/ ++ ++/*===========================================================================* ++ * For the generic transfer aware framework. ++ *===========================================================================*/ ++ ++/* ++ * hcd_hw_max_active_transfers() ++ * Returns: the (constant) maximum number of concurrently active transfers the HW will handle. ++ */ ++extern int hcd_hw_max_active_transfers(struct bus_hcpriv *bus_hcpriv); ++ ++/* ++ * hcd_hw_start_transfer() ++ * Returns: tid >= 0 when the transfer starts - this is an index in [0..hcd_hw_max_active_transfers] ++ * -1 when the transfer is invalid, ++ * -2 when there are no resources for the transfer currently available. ++ */ ++extern int hcd_hw_start_transfer(struct bus_hcpriv *bus_hcpriv, ++ int len, // length of data region ++ void *data, // virtual address of data region ++ int toggle, // toggle value to start the xfer with ++ int maxps, // max packet size of endpoint ++ int slow, // 1 ==> slow speed, 0 ==> full speed QQSV verify: (((urb->pipe) >> 26) & 1) ++ int endpoint, // endpoint number ++ int address, // USB device address ++ int pid, // {PID_OUT, PID_IN, PID_SETUP} ++ int format, // PIPE_{CONTROL,BULK,INTERRUPT,ISOCHRONOUS} ++ u32 other); // Values depending on format ++ ++/* ++ * hcd_hw_unlink_urb() ++ * Called in irq_lock, calls back to xhc_transfer_complete(). ++ * Returns: 0 when transfer is unlinked, non-zero otherwise. ++ */ ++extern int hcd_hw_unlink_urb(struct bus_hcpriv *bus_hcpriv, int tid); // tid is value returned by hcd_hw_start_transfer() ++ ++extern u32 hcd_hw_frame_number(struct bus_hcpriv *bus_hcpriv); ++ ++extern void hcd_hw_enable_interrupts(struct bus_hcpriv *bus_hcpriv); ++extern void hcd_hw_disable_interrupts(struct bus_hcpriv *bus_hcpriv); ++ ++extern void hcd_hw_get_ops(struct bus_hcpriv *bus_hcpriv); ++ ++extern int hcd_hw_init(struct bus_hcpriv *bus_hcpriv); ++ ++extern void hcd_hw_exit(struct bus_hcpriv *bus_hcpriv); ++ ++/*===========================================================================* ++ * For the virtual root hub. ++ *===========================================================================*/ ++ ++extern int hcd_hw_rh_num_ports(struct bus_hcpriv *bus_hcpriv); ++extern u8 hcd_hw_rh_otg_capable_mask(struct bus_hcpriv *bus_hcpriv); ++ ++extern char *hcd_hw_rh_string(struct bus_hcpriv *bus_hcpriv, int strno); ++extern u16 hcd_hw_rh_idVendor(struct bus_hcpriv *bus_hcpriv); ++extern u16 hcd_hw_rh_idProduct(struct bus_hcpriv *bus_hcpriv); ++extern u16 hcd_hw_rh_bcdDevice(struct bus_hcpriv *bus_hcpriv); ++extern u8 hcd_hw_rh_cfg_bmAttributes(struct bus_hcpriv *bus_hcpriv); ++extern u8 hcd_hw_rh_cfg_MaxPower(struct bus_hcpriv *bus_hcpriv); ++extern u16 hcd_hw_rh_hub_attributes(struct bus_hcpriv *bus_hcpriv); ++extern u8 hcd_hw_rh_power_delay(struct bus_hcpriv *bus_hcpriv); ++extern u8 hcd_hw_rh_hub_contr_current(struct bus_hcpriv *bus_hcpriv); ++extern u8 hcd_hw_rh_DeviceRemovable(struct bus_hcpriv *bus_hcpriv); ++extern u8 hcd_hw_rh_PortPwrCtrlMask(struct bus_hcpriv *bus_hcpriv); ++extern u32 hcd_hw_rh_get_hub_change_status(struct bus_hcpriv *bus_hcpriv); ++extern void hcd_hw_rh_hub_feature(struct bus_hcpriv *bus_hcpriv, int feat_selector, int set_flag); ++extern void hcd_hw_hcd_en_func(struct otg_instance *oi, u8 on); ++/* ++ * Note: in the following functions, portnum is 0-origin. ++ * (I.e., valid range is [0..(hcd_hw_rh_num_ports-1)].) ++ */ ++extern u32 hcd_hw_rh_get_port_change_status(struct bus_hcpriv *bus_hcpriv, int portnum); ++extern void hcd_hw_rh_port_feature(struct bus_hcpriv *bus_hcpriv, u16 wValue, u16 wIndex, int set_flag); ++ ++ ++#endif +diff -uNr linux/drivers/no-otg/otg/hcd-l26.h linux/drivers/otg/otg/hcd-l26.h +--- linux/drivers/no-otg/otg/hcd-l26.h 1970-01-01 01:00:00.000000000 +0100 ++++ linux/drivers/otg/otg/hcd-l26.h 2006-09-01 21:41:33.000000000 +0200 +@@ -0,0 +1,119 @@ ++/* ++ * otg/hcd/hc_xfer.h - Generic transfer level USBOTG aware Host Controller Driver (HCD) ++ * ++ * Copyright (c) 2004 Belcarra Technologies ++ * ++ * By: ++ * Stuart Lynne <sl@belcara.com>, ++ * Tom Rushworth <tbr@belcara.com>, ++ * Bruce Balden <balden@belcara.com> ++ * ++ */ ++/*! ++ * @file otg/otg/hcd-l26.h ++ * @brief Implements Linux version of Generic Host Controller driver ++ * ++ * @ingroup OTGHCD ++ */ ++#ifndef HC_XFER_H ++#define HC_XFER_H 1 ++ ++/*===========================================================================* ++ * Data structures and functions provided by the generic transfer aware ++ * host controller framework. ++ * ++ * The generic framework must be linked with a hardware specific component, ++ * whose functions are specified in hc_xfer_hw.h, and a generic root hub ++ * component, whose functions are specified in hc_xfer_rh.h. ++ * ++ * The resulting module provides a table of operations to the usb core layer, ++ * passed to the core layer by a call to usb_alloc_bus(). ++ *===========================================================================*/ ++ ++#define XHC_RH_DESCRIPTORS_SIZE (USB_DT_DEVICE_SIZE+USB_DT_CONFIG_SIZE+USB_DT_INTERFACE_SIZE+USB_DT_ENDPOINT_SIZE+USB_DT_HUB_NONVAR_SIZE+2) ++ ++typedef struct rh_hcpriv { ++ struct urb *int_urb; ++ struct usbd_device_descriptor *dd; ++ struct usbd_configuration_descriptor *cd; ++ struct usbd_interface_descriptor *id; ++ struct usbd_endpoint_descriptor *ed; ++ struct hub_descriptor *hd; ++ struct timer_list poll_timer; ++ //int poll_jiffies; ++ struct usb_device *dev; ++ int dev_registered; ++ u32 hub_change_status; // For shadowing the HW ++ u32 *port_change_status; // An array of [num_ports] values for shadowing the HW ++ u32 hub_port_change_status; ++ struct WORK_STRUCT psc_bh; ++ u8 curr_cfg; ++ u8 curr_itf; ++ //u8 num_ports; ++ u8 otg_device_mask; // (1 << port_num) bit set iff port_num is acting as a device, not host, and so not usable ++ u8 descriptors[XHC_RH_DESCRIPTORS_SIZE]; ++ int suspended; ++} rh_hcpriv_t; ++ ++typedef struct bus_hcpriv { // XFER level Host Controller Info ++ struct usb_bus *usb_bus; ++ void *hw_hci; // HW specific information for this HC ++ int root_hub_addr; // Initially 0, set by the virtual root hub when addressed. ++ struct rh_hcpriv *rh_hcpriv; // Generic root hub info (see hc_xfer_rh.[hc]) ++ int terminating; // Boolean, TRUE if shutting down. ++ //struct hcd_instance *hcd; // OTG Controller Info ++ int max_active_urbs; ++ struct urb **active_urbs; ++ int device_registered; // bus_device status ++ struct device bus_device; // Linux Driver Model info. ++ //struct hcd_ops otg_ops; // operations for OTG component. ++ struct usb_driver *usb_driver; ++ ++ u8 num_ports; ++ u8 otg_port; ++ u8 otg_capable_mask; ++ u16 rh_vendorid; ++ u16 rh_productid; ++ u16 rh_bcddevice; ++ char *rh_serial; ++ char *rh_product; ++ char *rh_manufacturer; ++ u8 rh_bmAttributes; ++ u8 rh_bMaxPower; ++ ++ struct usb_device *roothub_dev; ++ struct usb_device *first_dev; ++ ++ int max_active_transfers; ++ ++} bus_hcpriv_t; ++ ++typedef struct dev_hcpriv { // XFER level HCI per attached device info ++ struct usb_device *dev; ++ struct list_head queued_urbs_both[2][16]; ++ u32 num_urbs_both[2][16]; ++ u8 epq_state_both[2][16]; ++} dev_hcpriv_t; ++ ++#define EPQ_EMPTY 0 ++#define EPQ_RUNNING 1 ++#define EPQ_WAITING 2 ++ ++/*===========================================================================* ++ * For the hardware specific component. ++ *===========================================================================*/ ++ ++extern void hcd_transfer_complete(struct bus_hcpriv *bus_hcpriv, int transfer_id, int format, ++ int cc, u32 remaining, int next_toggle); ++ ++/*===========================================================================* ++ * For the virtual root hub. ++ *===========================================================================*/ ++ ++extern void hcd_rh_urb_complete(struct bus_hcpriv *bus_hcpriv, struct urb *urb); ++ ++#include "../otg/otg-trace.h" ++extern otg_tag_t xfer_hci_trace_tag; ++ ++ ++#endif +diff -uNr linux/drivers/no-otg/otg/hcd-rh.h linux/drivers/otg/otg/hcd-rh.h +--- linux/drivers/no-otg/otg/hcd-rh.h 1970-01-01 01:00:00.000000000 +0100 ++++ linux/drivers/otg/otg/hcd-rh.h 2006-09-01 21:41:33.000000000 +0200 +@@ -0,0 +1,55 @@ ++/* ++ * otg/hcd/hc_xfer_rh.h - Generic transfer level USBOTG aware Host Controller Driver (HCD) ++ * ++ * Copyright (c) 2004 Belcarra Technologies ++ * ++ * By: ++ * Stuart Lynne <sl@belcara.com>, ++ * Tom Rushworth <tbr@belcara.com>, ++ * Bruce Balden <balden@belcara.com> ++ * ++ */ ++/*! ++ * @file otg/otg/hcd-rh.h ++ * @brief Implements Generic Root Hub function for Generic Host Controller Driver. ++ * ++ * @ingroup OTGHCD ++ */ ++#ifndef HC_XFER_RH_H ++#define HC_XFER_RH_H 1 ++ ++#define XHC_RH_STRING_CONFIGURATION 1 ++#define XHC_RH_STRING_INTERFACE 2 ++#define XHC_RH_STRING_SERIAL 3 ++#define XHC_RH_STRING_PRODUCT 4 ++#define XHC_RH_STRING_MANUFACTURER 5 ++ ++/*===========================================================================* ++ * Functions provided by the generic virtual root hub. ++ *===========================================================================*/ ++ ++/*===========================================================================* ++ * For the generic transfer aware framework. ++ *===========================================================================*/ ++ ++/* ++ * hcd_rh_submit_urb() ++ * Returns: values as for the generic submit_urb() function. ++ */ ++extern int hcd_rh_submit_urb(struct bus_hcpriv *bus_hcpriv, struct urb *urb, int mem_flags); ++ ++extern int hcd_rh_unlink_urb(struct bus_hcpriv *bus_hcpriv, struct urb *urb); ++ ++extern void hcd_rh_get_ops(struct bus_hcpriv *bus_hcpriv); ++ ++extern int hcd_rh_init(struct bus_hcpriv *bus_hcpriv); ++ ++extern void hcd_rh_exit(struct bus_hcpriv *bus_hcpriv); ++ ++/*===========================================================================* ++ * For the hardware specific root hub component. ++ *===========================================================================*/ ++ ++extern irqreturn_t hcd_rh_int_hndlr(int irq, void *dev_id, struct pt_regs *regs); ++ ++#endif +diff -uNr linux/drivers/no-otg/otg/otg-api.h linux/drivers/otg/otg/otg-api.h +--- linux/drivers/no-otg/otg/otg-api.h 1970-01-01 01:00:00.000000000 +0100 ++++ linux/drivers/otg/otg/otg-api.h 2006-09-01 21:41:33.000000000 +0200 +@@ -0,0 +1,233 @@ ++/* ++ * otg/otg-api.h ++ * ++ * Copyright (c) 2004 Belcarra ++ * ++ * By: ++ * Stuart Lynne <sl@belcarra.com>, ++ * Tom Rushworth <tbr@belcarra.com>, ++ * Bruce Balden <balden@belcarra.com> ++ * ++ */ ++/* ++ * Doxygen group definitions - N.B. These much each be in ++ * their own separate comment... ++ */ ++/*! ++ * @defgroup onthegogroup On-The-Go ++ */ ++/*! ++ * @defgroup functiongroup Function Drivers ++ */ ++/*! ++ * @defgroup tcdgroup Transceiver Controller Drivers ++ */ ++/*! ++ * @defgroup pcdgroup Peripheral Controller Drivers ++ */ ++/*! ++ * @defgroup libgroup Architecture Libraries ++ */ ++/*! ++ * @defgroup platformgroup Platform Drivers ++ */ ++ ++ ++/*! ++ * @defgroup OTGCore Core ++ * @ingroup onthegogroup ++ */ ++ ++/*! ++ * @file otg/otg/otg-api.h ++ * @brief Core Defines for USB OTG Core Layaer ++ * ++ * @ingroup OTGCore ++ */ ++ ++/*! ++ * @name OTGCORE OTG API Definitions ++ * This contains the OTG API structures and definitions. ++ * @{ ++ */ ++ ++#include <otg/otg-fw.h> ++ ++#ifdef CONFIG_OTG_FW_MN ++#include <otg/otg-fw-mn.h> ++#else /* CONFIG_OTG_FW_MN */ ++#include <otg/otg-fw-df.h> ++#endif /* CONFIG_OTG_FW_MN */ ++ ++ ++struct otg_instance; ++typedef void (*otg_output_proc_t) (struct otg_instance *, u8); ++ ++extern struct otg_firmware *otg_firmware_loaded; ++extern struct otg_firmware *otg_firmware_orig; ++extern struct otg_firmware *otg_firmware_loading; ++ ++ ++char * otg_get_state_name(int state); ++ ++/*! ++ * @struct otg_instance ++ * ++ * This tracks the current OTG configuration and state ++ */ ++struct otg_instance { ++ ++ u16 state; /*!< current state */ ++ int previous; /*!< previous state */ ++ ++ int active; /*!< used as semaphore */ ++ ++ u32 current_inputs; /*!< input settings */ ++ u64 outputs; /*!< output settings */ ++ ++ u64 tickcount; /*!< when we transitioned to current state */ ++ u64 bconn; /*!< when b_conn was set */ ++ ++ struct otg_state *current_outputs; /*!< output table entry for current state */ ++ struct otg_state *previous_outputs; /*!< output table entry for current state */ ++ ++ otg_output_proc_t otg_output_ops[MAX_OUTPUTS]; /*!< array of functions mapped to output numbers */ ++ ++ int (*start_timer) (struct otg_instance *, int); /*!< OCD function to start timer */ ++ u64 (*ticks) (void); /*!< OCD function to return ticks */ ++ u64 (*elapsed) ( u64 *, u64 *); /*!< OCD function to return elapsed ticks */ ++ ++ void * hcd; /*!< pointer to hcd instance */ ++ void * ocd; /*!< pointer to ocd instance */ ++ void * pcd; /*!< pointer to pcd instance */ ++ void * tcd; /*!< pointer to tcd instance */ ++ ++ char function_name[OTGADMIN_MAXSTR]; /*!< current function */ ++ char serial_number[OTGADMIN_MAXSTR]; /*!< current serial number */ ++}; ++ ++typedef int (*otg_event_t) (struct otg_instance *, int, char *); ++ ++struct otg_event_info { ++ char *name; ++ otg_event_t event; ++}; ++ ++ ++/* 1 - used by external functions to pass in administrative commands as simple strings ++ */ ++extern int otg_status(int, u32, u32, char *, int); ++extern void otg_message(char *); ++ ++/* 2 - used internally by any OTG stack component to single event from interrupt context ++ */ ++extern int otg_event_irq(struct otg_instance *, u64, otg_tag_t, char *); ++ ++/* 3 - used internally by any OTG stack component to single event from non-interrupt context, ++ */ ++extern int otg_event(struct otg_instance *, u64, otg_tag_t, char *); ++ ++/* 5 - used by PCD/TCD drivers to signal various OTG Transceiver events ++ */ ++int otg_event_set_irq(struct otg_instance *, int, int, u32, otg_tag_t, char *); ++ ++ ++extern void otg_init (struct otg_instance *otg); ++extern void otg_exit (struct otg_instance *otg); ++ ++/* message ++ */ ++#if defined(OTG_LINUX) ++extern int otg_message_init_l24(struct otg_instance *); ++extern void otg_message_exit_l24(void); ++#endif /* defined(OTG_LINUX) */ ++ ++#if defined(OTG_WINCE) ++extern int otg_message_init_w42(struct otg_instance *); ++extern void otg_message_exit_w42(void); ++#endif /* defined(OTG_WINCE) */ ++ ++extern int otg_message_init(struct otg_instance *); ++extern void otg_message_exit(void); ++ ++extern int otg_write_message_irq(char *buf, int size); ++extern int otg_write_message(char *buf, int size); ++extern int otg_read_message(char *buf, int size); ++extern int otg_data_queued(void); ++extern unsigned int otg_message_block(void); ++extern unsigned int otg_message_poll(struct file *, struct poll_table_struct *); ++ ++ ++/* trace ++ */ ++extern int otg_trace_init (void); ++extern void otg_trace_exit (void); ++#if defined(OTG_LINUX) ++extern int otg_trace_init_l24(void); ++extern void otg_trace_exit_l24(void); ++#endif /* defined(OTG_LINUX) */ ++ ++#if defined(OTG_WINCE) ++extern int otg_trace_init_w42(void); ++extern void otg_trace_exit_w42(void); ++#endif /* defined(OTG_WINCE) */ ++ ++extern int otgtrace_init (void); ++extern void otgtrace_exit (void); ++extern int otg_trace_proc_read (char *page, int count, int * pos); ++extern int otg_trace_proc_write (const char *buf, int count, int * pos); ++ ++/* usbp ++ */ ++extern int usbd_device_init (void); ++extern void usbd_device_exit (void); ++ ++ ++/* ++ * otgcore/otg-mesg.c ++ */ ++ ++extern void otg_message(char *buf); ++extern void otg_mesg_get_status_update(struct otg_status_update *status_update); ++extern void otg_mesg_get_firmware_info(struct otg_firmware_info *firmware_info); ++extern int otg_mesg_set_firmware_info(struct otg_firmware_info *firmware_info); ++extern struct otg_instance * mesg_otg_instance; ++ ++ ++ ++/* ++ * otgcore/otg.c ++ */ ++extern char * otg_get_state_names(int i); ++ ++/* ++ * ops ++ */ ++ ++extern struct hcd_ops *otg_hcd_ops; ++extern struct ocd_ops *otg_ocd_ops; ++extern struct pcd_ops *otg_pcd_ops; ++extern struct tcd_ops *otg_tcd_ops; ++extern struct usbd_ops *otg_usbd_ops; ++ ++#define CORE core_trace_tag ++extern otg_tag_t CORE; ++ ++extern struct hcd_instance * otg_set_hcd_ops(struct hcd_ops *); ++extern struct ocd_instance * otg_set_ocd_ops(struct ocd_ops *); ++extern struct pcd_instance * otg_set_pcd_ops(struct pcd_ops *); ++extern struct tcd_instance * otg_set_tcd_ops(struct tcd_ops *); ++extern int otg_set_usbd_ops(struct usbd_ops *); ++ ++extern void otg_get_trace_info(otg_trace_t *p); ++extern int otg_tmr_id_gnd(void); ++extern u64 otg_tmr_ticks(void); ++extern u64 otg_tmr_elapsed(u64 *t1, u64 *t2); ++extern u16 otg_tmr_framenum(void); ++extern u32 otg_tmr_interrupts(void); ++ ++extern void otg_write_info_message(char *msg); ++ ++/* @} */ ++ ++ +diff -uNr linux/drivers/no-otg/otg/otg-compat.h linux/drivers/otg/otg/otg-compat.h +--- linux/drivers/no-otg/otg/otg-compat.h 1970-01-01 01:00:00.000000000 +0100 ++++ linux/drivers/otg/otg/otg-compat.h 2006-09-01 21:41:33.000000000 +0200 +@@ -0,0 +1,73 @@ ++/* ++ * otg/otgcore/otg-compat.h ++ * ++ * Copyright (c) 2004 Belcarra ++ * ++ */ ++/*! ++ * @file otg/otg/otg-compat.h ++ * @brief Common include file to determine and include appropriate OS compatibility file. ++ * ++ * ++ * @ingroup OTGCore ++ */ ++#ifndef _OTG_COMPAT_H ++#define _OTG_COMPAT_H 1 ++ ++#if defined(_WIN32_WCE) ++#define OTG_WINCE _WIN32_WCE ++#endif /* defined(_WIN32_WCE) */ ++ ++ /* What operating system are we running under? */ ++ ++ /* Recursively include enough information to determine which release */ ++ ++ #if (__GNUC__ >=3) ++ #define GCC3 ++ #else ++ #define GCC2 ++ #endif ++ #include <linux/config.h> ++ #include <linux/kernel.h> ++ #include <linux/version.h> ++ ++#if defined(__GNUC__) ++ #define OTG_LINUX ++ #ifndef CONFIG_OTG_NOC99 ++ #define OTG_C99 ++ #else ++ #endif ++ ++ #if LINUX_VERSION_CODE > KERNEL_VERSION(2,5,2) ++ #define LINUX26 ++ #elif LINUX_VERSION_CODE > KERNEL_VERSION(2,4,5) ++ #define LINUX24 ++ #else /* LINUX_VERSION_CODE > KERNEL_VERSION(2,4,5) */ ++ #define LINUX24 ++ #define LINUX_OLD ++ #warning "Early unsupported release of Linux kernel" ++ #endif /* LINUX_VERSION_CODE > KERNEL_VERSION(2,4,5) */ ++ ++ /* We are running under a supported version of Linux */ ++ #include <otg/otg-linux.h> ++ ++#elif defined(OTG_WINCE) ++ ++ /* We are running under a supported version of WinCE */ ++ #include <otg/otg-wince.h> ++ #include <otg/otg-wince-ex.h> ++ ++#else /* defined(OTG_WINCE) */ ++ ++ #error "Operating system not recognized" ++ ++#endif /* defined(OTG_WINCE) */ ++ ++#include <otg/otg-utils.h> ++ ++#if !defined(OTG_C99) ++#else /* !defined(OTG_C99) */ ++#endif /* !defined(OTG_C99) */ ++ ++ ++#endif /* _OTG_COMPAT_H */ +diff -uNr linux/drivers/no-otg/otg/otg-fw-df.h linux/drivers/otg/otg/otg-fw-df.h +--- linux/drivers/no-otg/otg/otg-fw-df.h 1970-01-01 01:00:00.000000000 +0100 ++++ linux/drivers/otg/otg/otg-fw-df.h 2006-09-01 21:41:33.000000000 +0200 +@@ -0,0 +1,53 @@ ++ ++/* Generated by otg-states-h.awk ++ * ++ * Do not Edit thie file ++ */ ++ ++/*! ++* @file otg/otg/otg-fw-df.h ++* @brief OTG Firmware - Firmware for df ++* ++* This file defines the OTG State Machine tests. ++* ++* @ingroup OTGFW ++*/ ++ ++ ++/*! ++* @page OTGFW ++* @section OTGFW - otg-fw-df.h ++* This contains the input, output and timout definitions for the OTG state machine firmware ++*/ ++extern char otg_fw_name_df[]; ++extern int otg_test_max_df; ++extern struct otg_test otg_tests_df[]; ++extern struct otg_output otg_outputs_df[]; ++ ++ /* ++ * Copyright (c) 2004 Belcarra ++ * ++ */ ++ /*! ++ * This is the default Firmware. It is included in the ++ * compiled modules and supports the auto Traditional USB ++ * mode. No user inputs are implemented. ++ */ ++ ++#define invalid_state 0 /* State machine has been initialized. */ ++ ++#define otg_disabled 1 /* State machine has been initialized. */ ++ ++#define terminator_state 2 /* terminator */ ++ /* ++ * This is not an OTG State. It is used internally to mark the end of the ++ * list of states and inputs. ++ */ ++ ++#define OTG_STATES_DF 3 ++ ++extern struct otg_state otg_states_df[OTG_STATES_DF + 1]; ++ ++extern char *otg_get_state_name_df(int); ++ ++extern struct otg_firmware otg_firmware_df; +diff -uNr linux/drivers/no-otg/otg/otg-fw-mn.h linux/drivers/otg/otg/otg-fw-mn.h +--- linux/drivers/no-otg/otg/otg-fw-mn.h 1970-01-01 01:00:00.000000000 +0100 ++++ linux/drivers/otg/otg/otg-fw-mn.h 2006-09-01 21:41:33.000000000 +0200 +@@ -0,0 +1,201 @@ ++ ++/* Generated by otg-states-h.awk ++ * ++ * Do not Edit thie file ++ */ ++ ++/*! ++* @file otg/otg/otg-fw-mn.h ++* @brief OTG Firmware - Firmware for mn ++* ++* This file defines the OTG State Machine tests. ++* ++* @ingroup OTGFW ++*/ ++ ++ ++/*! ++* @page OTGFW ++* @section OTGFW - otg-fw-mn.h ++* This contains the input, output and timout definitions for the OTG state machine firmware ++*/ ++extern char otg_fw_name_mn[]; ++extern int otg_test_max_mn; ++extern struct otg_test otg_tests_mn[]; ++extern struct otg_output otg_outputs_mn[]; ++ ++ /* ++ * Copyright (c) 2004 Belcarra ++ */ ++ /*! ++ * This is the initialization set for pcd and tcd. ++ */ ++ ++#define invalid_state 0 /* Un-initialized state. */ ++ /* ++ * This the initial state of the software when first loaded. ++ * It is not possible to return to this state. ++ */ ++ ++#define otg_disabled 1 /* State Machine ready but disabled. */ ++ /* ++ * The USBOTG State Machine has been initialized but is inactive. ++ * This state may have arrived at from either the invalid_state or ++ * from the otg_disable state. ++ */ ++ ++#define otg_disable_tcd 2 /* State Machine waiting for driver de-initialization. */ ++ /* ++ * The State Machine stops the device drivers and waits for them ++ * to signal that they have finished de-initializing. ++ * ++ * This state disables the TCD driver and waits for TCD ok. ++ */ ++ ++#define otg_disable_pcd 3 /* State Machine waiting for driver de-initialization. */ ++ /* ++ * The State Machine stops the device drivers and waits for them ++ * to signal that they have finished de-initializing. ++ * ++ * This state disables the PCD driver and waits for PCD ok. ++ */ ++ ++#define otg_enable_pcd 4 /* State Machine waiting for driver initialization. */ ++ /* ++ * The State Machine starts the device drivers and waits for them ++ * to signal that they have finished initializing. ++ * ++ * This state enables the PCD driver and waits for PCD ok. ++ */ ++ ++#define otg_enable_tcd 5 /* State Machine waiting for driver initialization. */ ++ /* ++ * The State Machine starts the device drivers and waits for them ++ * to signal that they have finished initializing. ++ * ++ * This state enables the PCD driver and waits for PCD ok. ++ */ ++ /* ++ * Copyright (c) 2004 Belcarra ++ * ++ */ ++ /*! ++ * This is the minimal firmware. It can be included in the ++ * compiled modules and supports the auto Traditional USB ++ * mode. No user inputs are required for normal operation. ++ * ++ * The b_bus_drop input can be optionally used to disconnect and re-connect. ++ * ++ * The enable_otg input can be optionally used to disable and re-enable. ++ * Note that disable/enable will reset b_bus_drop. ++ * ++ */ ++ /*! ++ * @name Meta State - otg_init ++ * @{ ++ */ ++ ++#define otg_enabled 6 /* State machine has been initialized. */ ++ /*! ++ * The State Machine has successfully started the device drivers and ++ * is waiting for an input event. Typically it will move from here to ++ * an idle state specific to the current conditions (a_idle, b_idle, mn_idle etc.) ++ * based on user request b_bus_drop. ++ * ++ */ ++ /* @} */ ++ /*! ++ * @name Meta State - b_idle ++ * @{ ++ */ ++ ++#define mn_idle 7 /* In idle mode with bus_drop reset. */ ++ /*! ++ * The State Machine is in the idle state for a Traditional USB Device. ++ * It is waiting for Vbus to indicate that it has been plugged into a USB Host. ++ * ++ */ ++ ++#define mn_bus_drop 8 /* In idle mode with bus_drop set. */ ++ /*! ++ * The State Machine is in the idle state for a Traditional USB Device. ++ * The B-Device applications has requested that the bus connection be dropped. ++ * Wait for either enable_otg reset or b_bus_drop reset. ++ */ ++ /* @} */ ++ /*! ++ * @name Meta State - b_peripheral ++ * @{ ++ */ ++ ++#define mn_peripheral 9 /* Signal a connection and wait for packet traffic. */ ++ /*! ++ * The State Machine in the peripheral state for a Traditional USB Device. ++ * The D+ pullup is enabled and we are waiting for a BUS_RESET to ++ * indicate that the USB Host has recognized that a USB Device is attached. ++ */ ++ ++#define mn_bus_reset 10 /* The bus has been reset. */ ++ /*! ++ * The State Machine in the bus_reset state for a Traditional USB Device. ++ * It is waiting to be enumerated and configured by the USB Host. ++ */ ++ ++#define mn_addressed 11 /* The device has been addressed. */ ++ /*! ++ * The State Machine in the configured state for a Traditional USB Device. ++ * This means that there is an active session, there is packet traffic ++ * with this device. ++ */ ++ ++#define mn_configured 12 /* The device has been configured. */ ++ /*! ++ * The State Machine in the configured state for a Traditional USB Device. ++ * This means that there is an active session, there is packet traffic ++ * with this device. ++ */ ++ ++#define mn_discharge 13 /* Discharging Vbus */ ++ /*! ++ * The State Machine in the discharge state for a Traditional USB Device. ++ * The device has been unplugged. The Vbus discharge resistor will be enabled ++ * for the TLDISC_DSCHRG time period. ++ */ ++ /* @} */ ++ /*! ++ * @name Meta State - b_suspended ++ * @{ ++ */ ++ ++#define mn_suspended 14 /* The bus has been suspended */ ++ /*! ++ * The State Machine in the suspend state for a Traditional USB Device. ++ */ ++ ++#define mn_wakeup_enabled 15 /* Suspended with REMOTE WAKEUP enabled. */ ++ /*! ++ * The State Machine in the suspend state for a Traditional USB Device, ++ * prior to suspended the USB Host enabled Remote Wakeup by sending a ++ * set REMOTE WAKUP request. ++ */ ++ ++#define mn_wakeup 16 /* Perform REMOTE WAKEUP procedure. */ ++ /*! ++ * The State Machine in the wakeup state for a Traditional USB Device, ++ * The REMOTE WAKEUP procedure will be performed. ++ */ ++ /* @} */ ++ ++#define terminator_state 17 /* terminator */ ++ /*! ++ * This is not an OTG State. It is used internally to mark the end of the ++ * list of states and inputs. ++ */ ++ ++#define OTG_STATES_MN 18 ++ ++extern struct otg_state otg_states_mn[OTG_STATES_MN + 1]; ++ ++extern char *otg_get_state_name_mn(int); ++ ++extern struct otg_firmware otg_firmware_mn; +diff -uNr linux/drivers/no-otg/otg/otg-fw.h linux/drivers/otg/otg/otg-fw.h +--- linux/drivers/no-otg/otg/otg-fw.h 1970-01-01 01:00:00.000000000 +0100 ++++ linux/drivers/otg/otg/otg-fw.h 2006-09-01 21:41:33.000000000 +0200 +@@ -0,0 +1,1309 @@ ++ ++/* Generated by otg-h.awk ++ * ++ * Do not Edit, see otg-state.awk ++ */ ++ ++ ++/*! ++* @defgroup OTGFW Firmware ++* @ingroup onthegogroup ++*/ ++ ++/*! ++* @file otg/otg/otg-fw.h ++* @brief OTG Firmware - Input, Output and Timeout definitions ++* This file defines the OTG State Machine input, output and timeout constants. ++* ++* ++* @ingroup OTGFW ++*/ ++ ++/*! ++* @name OTGFW ++* @section OTGFW - otg-fw.h ++*/ ++ ++/*! ++* @name OTGVERSION OTG Version ++* Version information ++* @{ ++*/ ++ ++#define OTG_VERSION_FW 200502152159L ++ ++/*! @} */ ++ ++/*! ++* @name BASIC ++* These are the provided for application layer compatibility. ++* @{ ++*/ ++#ifdef OTG_APPLICATION ++typedef unsigned char u8; ++typedef unsigned short u16; ++typedef unsigned long u32; ++typedef unsigned long long u64; ++#include <stdio.h> ++#include <sys/ioctl.h> ++#endif /* OTG_APPLICATION/ */ ++ ++/*! @} */ ++ ++/*! ++* @name OTGSTRUCT OTG Structures ++* Basic OTG Structures ++*/ ++#define OTGADMIN_MAXSTR 48 ++ ++/*! ++ * @struct otg_test ++ * ++ * This defines the tests that allow the state machine to move ++ * from state to state based on input events. ++ * ++ * Goto target: if ( ++ * (!test1 || (test1 & inputs)) && ++ * (!test2 || (test2 & inputs)) && ++ * ++ */ ++typedef struct otg_test { ++ u16 test; /*!< Test number */ ++ u16 state; /*!< State Machine State */ ++ u32 target; /*!< Goto this target if */ ++ u64 test1; /*!< (!test1 || (test1 & inputs)) && */ ++ u64 test2; /*!< (!test2 || (test2 & inputs)) && */ ++ u64 test3; /*!< (!test3 || (test3 & inputs)) && */ ++ /* N.B. An empty test is ignored. */ ++} otg_test_t; /*!< */ ++ ++ ++/*! ++ * @struct otg_input_name ++ * This structure simply allows for a table of input names that can ++ * searched for by input mask ++ */ ++typedef struct otg_input_name { ++ u32 value; /*!< Bit value */ ++ char name[OTGADMIN_MAXSTR]; /*!< Name of state. */ ++} otg_input_name_t; ++ ++ ++/*! ++ * @struct otg_state ++ * This defines an OTG State. Each state has a name, meta state, ++ * timeout, reset and outputs ++ * ++ * On entry to the state the reset mask is or'd into the current ++ * input mask, the settings defined in the output mask are used to. ++ * call the required output functions and then if present the timer ++ * is started with the timeout value (in uSec). ++ * ++ */ ++typedef struct otg_state { ++ u16 state; /*!< State Machine State */ ++ u16 meta; /*!< Meta Machine State */ ++ char name[OTGADMIN_MAXSTR]; /*!< Name of state. */ ++ u32 tmout; /*!< Start timeout if non-zero */ ++ u64 reset; /*!< Or these inputs on entry to state */ ++ u64 outputs; /*!< Or these outputs on entry to state */ ++} otg_state_t; ++ ++ ++/*! ++ * @struct otg_ioctl_name ++ * This structure allows a table for a table of IOCTL event names that can be ++ * searched for by the ioctl command value. It also stores the actual ++ * input mask to set or reset when according to the ioctl arguement. ++ */ ++typedef struct otg_ioctl_name { ++ u32 cmd; /*!< Ioctl cmd */ ++ u32 set; /*!< Signal to set/reset */ ++ char *name; /*!< Name of ioctl. */ ++} otg_ioctl_name_t; ++ ++ ++/*! ++ * @struct otg_admin_command ++ * This allows for a table of IOCTL admin commands. ++ */ ++typedef struct otg_admin_command { ++ int n; /*!< Ioctl cmd */ ++ char string[OTGADMIN_MAXSTR]; /*!< Signal to set/reset */ ++} otg_admin_command_t; ++ ++ ++/*! ++ * @struct otg_status_update ++ * This is used by the OTG administrative programs to pass data to and received ++ * data from the OTG State Machine driver. ++ */ ++typedef struct otg_status_update { ++ u16 state; /*!< current state */ ++ u16 meta; /*!< current meta state */ ++ u32 inputs; /*!< current inputs */ ++ u64 outputs; /*!< current outputs */ ++ u32 capabilities; /*!< */ ++ char fw_name[OTGADMIN_MAXSTR]; /*!< name of firmware */ ++ char state_name[OTGADMIN_MAXSTR]; /*!< name of current state */ ++ char meta_name[OTGADMIN_MAXSTR]; /*!< name of current meta-state */ ++ char function_name[OTGADMIN_MAXSTR]; /*!< currently selected function */ ++} otg_status_update_t; ++ ++ ++/*! ++ * @struct otg_firmware_info ++ * This is used by the OTG Administrative program to pass firmware information ++ * to the OTG State Machine./ ++ */ ++typedef struct otg_firmware_info { ++ int number_of_states; /*!< number of states */ ++ int number_of_tests; /*!< number of tests */ ++ char fw_name[OTGADMIN_MAXSTR]; /*!< name of firmware */ ++} otg_firmware_info_t; ++ ++ ++/*! ++ * @struct otg_firmware ++ * This is used by the OTG Administrative program to pass firmware ++ * to the OTG State Machine./ ++ */ ++typedef struct otg_firmware { ++ int number_of_states; /*!< number of states */ ++ int number_of_tests; /*!< number of tests */ ++ char fw_name[OTGADMIN_MAXSTR]; /*!< name of firmware */ ++ struct otg_state *otg_states; /*!< output information */ ++ struct otg_test *otg_tests; /*!< test information */ ++} otg_firmware_t; ++ ++/*! @} */ ++ ++/*! ++* @name OTGTIME Time Macros ++* Basic OTG Time Macros ++*/ ++ ++#define US(n) (n) /*!< micro-seconds */ ++ ++#define MS(n) (1000 * US(n)) /*!< milli-seconds */ ++ ++#define SEC(n) (1000 * MS(n)) /*!< seconds */ ++ ++/*! @} */ ++ ++/*! ++* @name OTGOUTPUTM Output Macros ++* OTG Output Macros ++*/ ++ ++/* OTG State Outputs ++ * ++ * Each state that we transition to defines new settings for each of the defined ++ * outputs. Each output setting can have four settings: ++ * ++ * ++ * ++ * ++ * ++ */ ++#define NC ((u64)0x0) /*!< NC no change */ ++#define SET ((u64)0x1) /*!< SET the output should be set (i.e. enabled) */ ++#define RESET ((u64)0x2) /*!< RESET the output should be reset (i.e. disabled) */ ++#define PULSE ((u64)0x3) /*!< OTHER special setting, for example used for pulse vbus */ ++#define POWER ((u64)0x3) /*!< POWER */ ++ ++#define _MASK(n) (((u64) 1) << n) /*!< Set nth bit in 64 bit mask */ ++#define _NOT(n) (((u64) n) << 32) /*!< Set nth+32 bit in 64 bit mask */ ++#define _ncmask(n) (NC) /*!< No output change mask */ ++#define _setmask(n) (SET << (n*2)) /*!< Output set mask */ ++#define _resetmask(n) (RESET << (n*2)) /*!< Output reset mask */ ++#define _pulsemask(n) (PULSE << (n*2)) /*!< Output pulse mask */ ++#define _powermask(n) (POWER << (n*2)) /*!< Output poser mask */ ++ ++/*! @} */ ++ ++/*! ++* @name OTGNAMES OTG Names ++* Basic OTG Name Tables ++*/ ++ ++extern struct otg_input_name otg_input_names[]; ++extern struct otg_ioctl_name otg_ioctl_names[]; ++extern struct otg_timeouts otg_timeouts[]; ++extern struct otg_firmware *otg_firmware_loaded; ++extern char *otg_output_names[]; ++ ++ ++/*! @} */ ++ ++/* Generated by otg-inputs-h.awk ++ * ++ * Do not Edit this file. ++ */ ++ ++ ++ /*! ++ * N.B. The OTG State Machine Documentation uses the syntax A and A/ ++ * to indicate an event being true or not true. Because a trailing ++ * slash is not legal in C we use A and A_ to indicate true and not true. ++ */ ++ ++ /*! ++ * @name OTG Transceiver Inputs ++ * These inputs reflect changes seen in the OTG Transceiver. These ++ * what are available in most common OTG transceivers, for example ++ * the ISP1301 or MAX3353. ++ * ++ * The following are typical Vbus comparators. ++ * ++ * ISP1301 MAX3353E ++ * B-Session end threshold 0.2V-0.8V 0.5V ++ * Session Valid Comparator 0.8V-2.0V 1.4V ++ * B-Session Valid 2.0V-4.0V N/A ++ * A-Device Vbus Valid Comparator 4.4V 4.6V ++ * ++ * @{ ++ */ ++#define ID_GND _MASK( 0) /*!< otg_ok - ID is grounded */ ++#define ID_GND_ _NOT(ID_GND) ++#define ID_FLOAT _MASK( 1) /*!< otg_ok - ID is floating */ ++#define ID_FLOAT_ _NOT(ID_FLOAT) ++#define DP_HIGH _MASK( 2) /*!< otg_ok - DP is pulled high */ ++#define DP_HIGH_ _NOT(DP_HIGH) ++#define DM_HIGH _MASK( 3) /*!< otg_ok - DM pullup is pulled high */ ++#define DM_HIGH_ _NOT(DM_HIGH) ++#define B_SESS_END _MASK( 4) /*!< otg_ok - Vbus less than B-Session end thresshold */ ++#define B_SESS_END_ _NOT(B_SESS_END) ++#define A_SESS_VLD _MASK( 5) /*!< otg_ok - Vbus greater than Session valid threshold */ ++#define A_SESS_VLD_ _NOT(A_SESS_VLD) ++#define B_SESS_VLD _MASK( 6) /*!< otg_ok - Vbus greater than B-Session Valid threshold */ ++#define B_SESS_VLD_ _NOT(B_SESS_VLD) ++#define VBUS_VLD _MASK( 7) /*!< otg_ok - Vbus greater than A-Device Vbus Valid threshold */ ++#define VBUS_VLD_ _NOT(VBUS_VLD) ++#define SRP_DET _MASK( 8) /*!< a_idle - SRP Detected (Vbus pulsed) */ ++#define SRP_DET_ _NOT(SRP_DET) ++#define SE0_DET _MASK( 9) /*!< b_idle - SE0 Detected (Single Ended Zeros, DM and DP both low) */ ++#define SE0_DET_ _NOT(SE0_DET) ++#define SE1_DET _MASK(10) /*!< b_idle - SE1 Detected (Single Ended Ones, DM and DP both high) */ ++#define SE1_DET_ _NOT(SE1_DET) ++#define BDIS_ACON _MASK(10) /*!< a_hnp_wait - Auto DP pullup high after B-disconnect */ ++#define BDIS_ACON_ _NOT(BDIS_ACON) ++#define CR_INT_DET _MASK(10) /*!< ph_audio - 0.4V < DP < 0.6V */ ++#define CR_INT_DET_ _NOT(CR_INT_DET) ++ /* @} */ ++ /*! ++ * @name Peripheral and Host driver signals. ++ * ++ * ++ * @{ ++ */ ++#define HUB_PORT_CONNECT _MASK(12) /*!< otg_both - Port Connection */ ++#define HUB_PORT_CONNECT_ _NOT(HUB_PORT_CONNECT) ++#define BUS_RESET _MASK(13) /*!< otg_both - Bus has reset. */ ++#define BUS_RESET_ _NOT(BUS_RESET) ++#define ADDRESSED _MASK(14) /*!< otg_both - Device has been addressed (received SET ADDRESS request.) */ ++#define ADDRESSED_ _NOT(ADDRESSED) ++#define CONFIGURED _MASK(15) /*!< otg_both - Device has been configured (received SET CONFIG request.) */ ++#define CONFIGURED_ _NOT(CONFIGURED) ++#define NOT_SUPPORTED _MASK(16) /*!< otg_both - Peripheral not supported (no class driver.) */ ++#define NOT_SUPPORTED_ _NOT(NOT_SUPPORTED) ++#define BUS_SUSPENDED _MASK(17) /*!< otg_both - Bus has been suspended. */ ++#define BUS_SUSPENDED_ _NOT(BUS_SUSPENDED) ++ /* @} */ ++ ++ /*! ++ * @name Administrative Policy Inputs ++ * These are only valid in the state indicated and ++ * can be enabled or disabled. ++ * ++ * @{ ++ */ ++#define a_bcon_no_tmout_req _MASK(18) /*!< otg_host - Application on A-host wants Ta_wait_bcon timeout disabled (non-OTG mode). */ ++#define a_bcon_no_tmout_req_ _NOT(a_bcon_no_tmout_req) ++#define a_hpwr_req _MASK(19) /*!< otg_host - Application on A-host wants external charge pump enabled. */ ++#define a_hpwr_req_ _NOT(a_hpwr_req) ++#define bus_drop _MASK(20) /*!< otg_ok - Application on Device needs to power down bus. */ ++#define bus_drop_ _NOT(bus_drop) ++#define a_bus_drop _MASK(20) /*!< otg_ok - Application on A-Device needs to power down bus. */ ++#define a_bus_drop_ _NOT(a_bus_drop) ++#define b_bus_drop _MASK(20) /*!< otg_ok - Application on B-Device needs to disconnect from bus. */ ++#define b_bus_drop_ _NOT(b_bus_drop) ++#define bus_req _MASK(21) /*!< otg_ok - Application on Device wants to use the bus. */ ++#define bus_req_ _NOT(bus_req) ++#define a_bus_req _MASK(21) /*!< otg_ok - Application on A-Device wants to act as host */ ++#define a_bus_req_ _NOT(a_bus_req) ++#define b_bus_req _MASK(21) /*!< otg_ok - Application on B-Device wants to act as host */ ++#define b_bus_req_ _NOT(b_bus_req) ++#define b_sess_req _MASK(21) /*!< otg_ok - Application on B-Device to perform SRP (alias for b_srp_req.) */ ++#define b_sess_req_ _NOT(b_sess_req) ++#define suspend_req _MASK(22) /*!< otg_host - Application on Device requests bus be suspended (alias for a_bus_req/.) */ ++#define suspend_req_ _NOT(suspend_req) ++#define a_suspend_req _MASK(22) /*!< otg_host - Application on A-host requests bus be suspended (alias for a_bus_req/.) */ ++#define a_suspend_req_ _NOT(a_suspend_req) ++#define b_suspend_req _MASK(22) /*!< otg_host - Application on B-host requests bus be suspended (alias for b_bus_req/.) */ ++#define b_suspend_req_ _NOT(b_suspend_req) ++ /* @} */ ++ ++ /*! ++ * @name Administrative Action Inputs ++ * These are only valid in state indicated. The ++ * state machine tests must reset in states ++ * prior and after use. ++ * @{ ++ */ ++#define set_remote_wakeup_cmd _MASK(24) /*!< a_host - A-Device will send Remote Wakeup Enable Request */ ++#define set_remote_wakeup_cmd_ _NOT(set_remote_wakeup_cmd) ++#define remote_wakeup_cmd _MASK(24) /*!< tr_configured - B-Device will perform Remote Wakeup. */ ++#define remote_wakeup_cmd_ _NOT(remote_wakeup_cmd) ++#define reset_remote_wakeup_cmd _MASK(25) /*!< a_host - A-Device will send Remote Wakeup Disable Request */ ++#define reset_remote_wakeup_cmd_ _NOT(reset_remote_wakeup_cmd) ++#define clr_err_cmd _MASK(25) /*!< a_vbus_err - A-Device ill clears Vbus overcurrent error. */ ++#define clr_err_cmd_ _NOT(clr_err_cmd) ++#define b_hnp_cmd _MASK(25) /*!< b_configured - B-Device will attempt HNP */ ++#define b_hnp_cmd_ _NOT(b_hnp_cmd) ++#define ph_int_cmd _MASK(25) /*!< ph_audio - B-Device will request Carkit interrupt */ ++#define ph_int_cmd_ _NOT(ph_int_cmd) ++#define ph_audio_cmd _MASK(25) /*!< ph_uart - Application on B-Device connected to Carkit requests audio mode. */ ++#define ph_audio_cmd_ _NOT(ph_audio_cmd) ++#define cr_int_cmd _MASK(25) /*!< cr_aud - Application on A-Device wants to emulate Carkit */ ++#define cr_int_cmd_ _NOT(cr_int_cmd) ++#define led_on_cmd _MASK(26) /*!< ph_uart - B-Device will enable Carkit LED */ ++#define led_on_cmd_ _NOT(led_on_cmd) ++#define led_off_cmd _MASK(27) /*!< ph_uart - B-Device will disable Carkit LED */ ++#define led_off_cmd_ _NOT(led_off_cmd) ++ /* @} */ ++ ++ /*! ++ * @name Internal State ++ * Used to track status changes. ++ * @{ ++ */ ++#define HNP_ENABLED _MASK(27) /*!< b_configured - B-HNP Enable Request sent (a-host) or received (b-peripheral). */ ++#define HNP_ENABLED_ _NOT(HNP_ENABLED) ++#define HNP_CAPABLE _MASK(28) /*!< otg_both - B-host Peripheral may do HNP */ ++#define HNP_CAPABLE_ _NOT(HNP_CAPABLE) ++#define HNP_SUPPORTED _MASK(28) /*!< otg_both - B-host can do HNP (A-Host Received HNP Supported SET FEATURE) */ ++#define HNP_SUPPORTED_ _NOT(HNP_SUPPORTED) ++#define REMOTE_WAKEUP_ENABLED _MASK(29) /*!< otg_configured - Remote Wakeup Enable Request received. */ ++#define REMOTE_WAKEUP_ENABLED_ _NOT(REMOTE_WAKEUP_ENABLED) ++#define REMOTE_CAPABLE _MASK(29) /*!< otg_both - Peripheral can do remote wakeup */ ++#define REMOTE_CAPABLE_ _NOT(REMOTE_CAPABLE) ++ /* @} */ ++ ++ /*! ++ * @name Global Administration ++ * @{ ++ */ ++ /* @} */ ++ ++ /*! ++ * @name Driver Initialization Finished signals ++ * @{ ++ */ ++#define PCD_OK _MASK(30) /*!< otg_driver - PCD Driver Initialization Finished. */ ++#define PCD_OK_ _NOT(PCD_OK) ++#define TCD_OK _MASK(30) /*!< otg_driver - TCD Driver Initialization Finished. */ ++#define TCD_OK_ _NOT(TCD_OK) ++#define HCD_OK _MASK(30) /*!< otg_driver - HCD Driver Initialization Finished. */ ++#define HCD_OK_ _NOT(HCD_OK) ++#define OCD_OK _MASK(30) /*!< otg_driver - OCD Driver Initialization Finished. */ ++#define OCD_OK_ _NOT(OCD_OK) ++ /* @} */ ++ ++ /*! ++ * @name Timeout and enable ++ * @{ ++ */ ++#define TMOUT _MASK(30) /*!< otg_all - Generic Timeout */ ++#define TMOUT_ _NOT(TMOUT) ++#define enable_otg _MASK(31) /*!< otg_all - Move State Machine otg_disabled state. */ ++#define enable_otg_ _NOT(enable_otg) ++#define AUTO _MASK(31) /*!< otg_all - Auto Return (enable_otg only true when active) */ ++#define AUTO_ _NOT(AUTO) ++ /* @} */ ++ ++ /*! @name Timeouts C.f. OTG Table 5-2 A-Device Timing ++ * @{ ++ */ ++#define Ta_wait_vrise _MASK(30) /*!< a_wait_vrise - Wait for Vbus Rise */ ++#define Ta_wait_vrise_ _NOT(Ta_wait_vrise) ++#define TA_WAIT_VRISE MS(100) ++#define Ta_wait_vrise_200 _MASK(30) /*!< a_wait_vrise - Wait for Vbus Rise */ ++#define Ta_wait_vrise_200_ _NOT(Ta_wait_vrise_200) ++#define TA_WAIT_VRISE_200 MS(200) ++#define Ta_wait_vrise_400 _MASK(30) /*!< a_wait_vrise - Wait for Vbus Rise */ ++#define Ta_wait_vrise_400_ _NOT(Ta_wait_vrise_400) ++#define TA_WAIT_VRISE_400 MS(400) ++#define Ta_wait_vrise_500 _MASK(30) /*!< a_wait_vrise - Wait for Vbus Rise */ ++#define Ta_wait_vrise_500_ _NOT(Ta_wait_vrise_500) ++#define TA_WAIT_VRISE_500 MS(500) ++#define Ta_wait_vrise_800 _MASK(30) /*!< a_wait_vrise - Wait for Vbus Rise */ ++#define Ta_wait_vrise_800_ _NOT(Ta_wait_vrise_800) ++#define TA_WAIT_VRISE_800 MS(800) ++#define Ta_bcon_ldb _MASK(30) /*!< a_bcon_ldb - B-Connect Long Debounce */ ++#define Ta_bcon_ldb_ _NOT(Ta_bcon_ldb) ++#define TA_BCON_LDB MS(100) ++#define Ta_wait_bcon _MASK(30) /*!< a_wait_bcon - Wait for 1 second for B-Connect */ ++#define Ta_wait_bcon_ _NOT(Ta_wait_bcon) ++#define TA_WAIT_BCON SEC(1) ++#define Ta_wait_bcon_5 _MASK(30) /*!< a_wait_bcon - Wait for 5 second for B-Connect */ ++#define Ta_wait_bcon_5_ _NOT(Ta_wait_bcon_5) ++#define TA_WAIT_BCON_5 SEC(5) ++#define Ta_wait_bcon_10 _MASK(30) /*!< a_wait_bcon - Wait for 10 second for B-Connect */ ++#define Ta_wait_bcon_10_ _NOT(Ta_wait_bcon_10) ++#define TA_WAIT_BCON_10 SEC(10) ++#define Ta_aidl_bdis _MASK(30) /*!< a_hnp_wait - A-Idle to B-Disconnect */ ++#define Ta_aidl_bdis_ _NOT(Ta_aidl_bdis) ++#define TA_AIDL_BDIS MS(200) ++#define Ta_bdis_acon _MASK(30) /*!< a_suspend - B-disconnect to A-Connect */ ++#define Ta_bdis_acon_ _NOT(Ta_bdis_acon) ++#define TA_BDIS_ACON MS(3) ++#define Ta_bidl_adis_min _MASK(30) /*!< a_peripheral - B-Idle to A-Disconnect minimum (TODO) */ ++#define Ta_bidl_adis_min_ _NOT(Ta_bidl_adis_min) ++#define TA_BIDL_ADIS_MIN MS(3) ++#define Ta_bcon_sdb _MASK(30) /*!< a_bcon_sdb - B-Connect Short Debounce */ ++#define Ta_bcon_sdb_ _NOT(Ta_bcon_sdb) ++#define TA_BCON_SDB US(2) ++#define Ta_bcon_sdb_win _MASK(30) /*!< a_bcon_win - B-Connect Short Debounce Window */ ++#define Ta_bcon_sdb_win_ _NOT(Ta_bcon_sdb_win) ++#define TA_BCON_SDB_WIN MS(100) ++ /* @} */ ++ ++ /*! @name Timeouts C.f. OTG Table 5-3 B-Device Timing ++ * @{ ++ */ ++#define Tb_se0_srp _MASK(30) /*!< b_srp_se0 - SE0 Time Before SRP */ ++#define Tb_se0_srp_ _NOT(Tb_se0_srp) ++#define TB_SE0_SRP MS(2) ++#define Tb_data_pls _MASK(30) /*!< b_srp_init - Data-Line Pulse Time */ ++#define Tb_data_pls_ _NOT(Tb_data_pls) ++#define TB_DATA_PLS MS(7) ++#define Tb_data_pls_min _MASK(30) /*!< a_srp_min - Data-Line Pulse minimum time */ ++#define Tb_data_pls_min_ _NOT(Tb_data_pls_min) ++#define TB_DATA_PLS_MIN MS(5) ++#define Tb_data_pls_max _MASK(30) /*!< a_srp_wait - Data-Line Pulse maximum time */ ++#define Tb_data_pls_max_ _NOT(Tb_data_pls_max) ++#define TB_DATA_PLS_MAX MS(10) ++#define Tb_srp_init _MASK(30) /*!< b_srp_init - SRP Initiate Time (TODO multi-state? Not-needed?) */ ++#define Tb_srp_init_ _NOT(Tb_srp_init) ++#define TB_SRP_INIT MS(100) ++#define Tb_srp_fail_min _MASK(30) /*!< b_srp_wait - SRP Fail Time minimum (TODO) */ ++#define Tb_srp_fail_min_ _NOT(Tb_srp_fail_min) ++#define TB_SRP_FAIL_MIN SEC(5) ++#define Tb_aidl_bdis_min _MASK(30) /*!< b_peripheral - A-idle to B-Disconnect minimum (TODO) */ ++#define Tb_aidl_bdis_min_ _NOT(Tb_aidl_bdis_min) ++#define TB_AIDL_BDIS_MIN MS(5) ++#define Tb_aidl_bdis_max _MASK(30) /*!< b_peripheral - A-idle to B-Disconnect maximum (TODO) */ ++#define Tb_aidl_bdis_max_ _NOT(Tb_aidl_bdis_max) ++#define TB_AIDL_BDIS_MAX MS(150) ++#define Tldisc_dschrg _MASK(30) /*!< b_dischrg - Local Disconnect to Data Line Discharge (TODO) */ ++#define Tldisc_dschrg_ _NOT(Tldisc_dschrg) ++#define TLDISC_DSCHRG US(25) ++#define Tb_ase0_brst_min _MASK(30) /*!< b_wait_acon - A-SE0 to B-Reset minimum */ ++#define Tb_ase0_brst_min_ _NOT(Tb_ase0_brst_min) ++#define TB_ASE0_BRST_MIN US(3125) ++#define Tb_acon_dbnc _MASK(30) /*!< b_acon_dbnc - A-Connect Debounce */ ++#define Tb_acon_dbnc_ _NOT(Tb_acon_dbnc) ++#define TB_ACON_DBNC US(2) ++#define Tb_acon_bse0 _MASK(30) /*!< b_host_se0 - A-Connect to B-SE0 */ ++#define Tb_acon_bse0_ _NOT(Tb_acon_bse0) ++#define TB_ACON_BSE0 MS(1) ++#define Tid_ldb _MASK(30) /*!< otg_enable - ID changes debounce */ ++#define Tid_ldb_ _NOT(Tid_ldb) ++#define TID_LDB MS(100) ++ /* @} */ ++ ++ /*! @name Timeouts for Carkit ++ * @{ ++ */ ++#define Tph_bcon_ldb _MASK(30) /*!< ph_init - B-Connect Long Debounce */ ++#define Tph_bcon_ldb_ _NOT(Tph_bcon_ldb) ++#define TPH_BCON_LDB MS(100) ++#define Tph_init_pls _MASK(30) /*!< ph_int - Timeout for Carkit interrupt. */ ++#define Tph_init_pls_ _NOT(Tph_init_pls) ++#define TPH_INIT_PLS US(500) ++#define Tcr_uart_rsp _MASK(30) /*!< ph_int - Timeout for Carkit UART */ ++#define Tcr_uart_rsp_ _NOT(Tcr_uart_rsp) ++#define TCR_UART_RSP MS(30) ++#define Tcr_aud_det _MASK(30) /*!< ph_audio_wait - Timeout waiting for Carkit Audio ack */ ++#define Tcr_aud_det_ _NOT(Tcr_aud_det) ++#define TCR_AUD_DET US(1000) ++#define Tph_led_off _MASK(30) /*!< ph_uart - Timeout for LED off pulse */ ++#define Tph_led_off_ _NOT(Tph_led_off) ++#define TPH_LED_OFF US(10) ++#define Tph_led_on _MASK(30) /*!< ph_uart - Timeout for LED on pulse */ ++#define Tph_led_on_ _NOT(Tph_led_on) ++#define TPH_LED_ON MS(4) ++#define Tcr_led_off _MASK(30) /*!< ph_uart - Timeout for LED off pulse */ ++#define Tcr_led_off_ _NOT(Tcr_led_off) ++#define TCR_LED_OFF MS(4) ++#define Tcr_led_on _MASK(30) /*!< ph_uart - Timeout for LED on pulse */ ++#define Tcr_led_on_ _NOT(Tcr_led_on) ++#define TCR_LED_ON MS(8) ++#define Tcr_dm_disc _MASK(30) /*!< cr_aud - Timeout for DM disconnect */ ++#define Tcr_dm_disc_ _NOT(Tcr_dm_disc) ++#define TCR_DM_DISC MS(50) ++#define Tph_mono_ack _MASK(30) /*!< cr_wait - Timeout for CR INT */ ++#define Tph_mono_ack_ _NOT(Tph_mono_ack) ++#define TPH_MONO_ACK MS(1) ++#define Tph_aud_det _MASK(30) /*!< cr_ack - Timeout for CR ACK */ ++#define Tph_aud_det_ _NOT(Tph_aud_det) ++#define TPH_AUD_DET MS(100) ++ /* @} */ ++ ++ /*! @name Timeouts for timer test ++ * @{ ++ */ ++#define Tzero _MASK(30) /*!< otg_enabled - Startup */ ++#define Tzero_ _NOT(Tzero) ++#define TZERO MS(2) ++#define Tst_one_ms _MASK(30) /*!< tm_start - Timer test */ ++#define Tst_one_ms_ _NOT(Tst_one_ms) ++#define TST_ONE_MS MS(1) ++#define Tst_ten_ms _MASK(30) /*!< tm_start - Timer test */ ++#define Tst_ten_ms_ _NOT(Tst_ten_ms) ++#define TST_TEN_MS MS(10) ++#define Tst_one_second _MASK(30) /*!< tm_start - Timer test */ ++#define Tst_one_second_ _NOT(Tst_one_second) ++#define TST_ONE_SECOND SEC(1) ++#define Tst_two_second _MASK(30) /*!< tm_start - Timer test */ ++#define Tst_two_second_ _NOT(Tst_two_second) ++#define TST_TWO_SECOND SEC(2) ++#define Tst_four_second _MASK(30) /*!< tm_start - Timer test */ ++#define Tst_four_second_ _NOT(Tst_four_second) ++#define TST_FOUR_SECOND SEC(4) ++#define Tst_eight_second _MASK(30) /*!< tm_start - Timer test */ ++#define Tst_eight_second_ _NOT(Tst_eight_second) ++#define TST_EIGHT_SECOND SEC(8) ++#define Tst_ten_second _MASK(30) /*!< tm_start - Timer test */ ++#define Tst_ten_second_ _NOT(Tst_ten_second) ++#define TST_TEN_SECOND SEC(10) ++ /* @} */ ++ ++/* Generated by otg-outputs-h.awk ++ * ++ * Do not Edit this file. ++ */ ++ ++ ++ ++ /* State Machine Outputs ++ */ ++ ++ /*! @name Driver Initialization Outputs ++ * N.B. tcd_en is used for older devices, to check if Vbus ++ * already enabled. ++ * @{ ++ */ ++ ++#define TCD_INIT_OUT 0 /*!< Initiate Transceiver Controller Driver Initialization (or De-initialization.) */ ++#define tcd_init_out _setmask(TCD_INIT_OUT) ++#define tcd_init_out_ _resetmask(TCD_INIT_OUT) ++#define tcd_init_out_set _setmask(TCD_INIT_OUT) ++#define tcd_init_out_reset _resetmask(TCD_INIT_OUT) ++#define tcd_init_out_power _powermask(TCD_INIT_OUT) ++ ++#define PCD_INIT_OUT 1 /*!< Initiate Peripheral Controller Driver Initialization (or De-initialization.) */ ++#define pcd_init_out _setmask(PCD_INIT_OUT) ++#define pcd_init_out_ _resetmask(PCD_INIT_OUT) ++#define pcd_init_out_set _setmask(PCD_INIT_OUT) ++#define pcd_init_out_reset _resetmask(PCD_INIT_OUT) ++#define pcd_init_out_power _powermask(PCD_INIT_OUT) ++ ++#define HCD_INIT_OUT 2 /*!< Initiate Host Controller Driver Initialization (or De-initialization). */ ++#define hcd_init_out _setmask(HCD_INIT_OUT) ++#define hcd_init_out_ _resetmask(HCD_INIT_OUT) ++#define hcd_init_out_set _setmask(HCD_INIT_OUT) ++#define hcd_init_out_reset _resetmask(HCD_INIT_OUT) ++#define hcd_init_out_power _powermask(HCD_INIT_OUT) ++ ++#define OCD_INIT_OUT 3 /*!< Initiate OTG Controller Driver Initialization (or De-initialization). */ ++#define ocd_init_out _setmask(OCD_INIT_OUT) ++#define ocd_init_out_ _resetmask(OCD_INIT_OUT) ++#define ocd_init_out_set _setmask(OCD_INIT_OUT) ++#define ocd_init_out_reset _resetmask(OCD_INIT_OUT) ++#define ocd_init_out_power _powermask(OCD_INIT_OUT) ++ ++#define TCD_EN_OUT 4 /*!< Enable Transceiver Controller Driver */ ++#define tcd_en_out _setmask(TCD_EN_OUT) ++#define tcd_en_out_ _resetmask(TCD_EN_OUT) ++#define tcd_en_out_set _setmask(TCD_EN_OUT) ++#define tcd_en_out_reset _resetmask(TCD_EN_OUT) ++#define tcd_en_out_power _powermask(TCD_EN_OUT) ++ ++#define PCD_EN_OUT 5 /*!< Enable Peripheral Controller Driver */ ++#define pcd_en_out _setmask(PCD_EN_OUT) ++#define pcd_en_out_ _resetmask(PCD_EN_OUT) ++#define pcd_en_out_set _setmask(PCD_EN_OUT) ++#define pcd_en_out_reset _resetmask(PCD_EN_OUT) ++#define pcd_en_out_power _powermask(PCD_EN_OUT) ++ ++#define HCD_EN_OUT 6 /*!< Enable Host Controller Driver */ ++#define hcd_en_out _setmask(HCD_EN_OUT) ++#define hcd_en_out_ _resetmask(HCD_EN_OUT) ++#define hcd_en_out_set _setmask(HCD_EN_OUT) ++#define hcd_en_out_reset _resetmask(HCD_EN_OUT) ++#define hcd_en_out_power _powermask(HCD_EN_OUT) ++ /* @) */ ++ ++ /*! @name Transceiver Controller Driver Outputs ++ * @{ ++ */ ++ ++#define DRV_VBUS_OUT 7 /*!< A-Device will Drive Vbus to 5V through charge pump. */ ++#define drv_vbus_out _setmask(DRV_VBUS_OUT) ++#define drv_vbus_out_ _resetmask(DRV_VBUS_OUT) ++#define drv_vbus_out_set _setmask(DRV_VBUS_OUT) ++#define drv_vbus_out_reset _resetmask(DRV_VBUS_OUT) ++#define drv_vbus_out_power _powermask(DRV_VBUS_OUT) ++ ++#define CHRG_VBUS_OUT 8 /*!< B-Device will charge Vbus to 3.3V through resistor (SRP.) */ ++#define chrg_vbus_out _setmask(CHRG_VBUS_OUT) ++#define chrg_vbus_out_ _resetmask(CHRG_VBUS_OUT) ++#define chrg_vbus_out_set _setmask(CHRG_VBUS_OUT) ++#define chrg_vbus_out_reset _resetmask(CHRG_VBUS_OUT) ++#define chrg_vbus_out_power _powermask(CHRG_VBUS_OUT) ++ ++#define DISCHRG_VBUS_OUT 9 /*!< B-Device will discharge Vbus (enable dischage resistor.) */ ++#define dischrg_vbus_out _setmask(DISCHRG_VBUS_OUT) ++#define dischrg_vbus_out_ _resetmask(DISCHRG_VBUS_OUT) ++#define dischrg_vbus_out_set _setmask(DISCHRG_VBUS_OUT) ++#define dischrg_vbus_out_reset _resetmask(DISCHRG_VBUS_OUT) ++#define dischrg_vbus_out_power _powermask(DISCHRG_VBUS_OUT) ++ ++#define DM_PULLUP_OUT 10 /*!< DM pullup control - aka loc_carkit */ ++#define dm_pullup_out _setmask(DM_PULLUP_OUT) ++#define dm_pullup_out_ _resetmask(DM_PULLUP_OUT) ++#define dm_pullup_out_set _setmask(DM_PULLUP_OUT) ++#define dm_pullup_out_reset _resetmask(DM_PULLUP_OUT) ++#define dm_pullup_out_power _powermask(DM_PULLUP_OUT) ++ ++#define DM_PULLDOWN_OUT 11 /*!< DM pulldown control */ ++#define dm_pulldown_out _setmask(DM_PULLDOWN_OUT) ++#define dm_pulldown_out_ _resetmask(DM_PULLDOWN_OUT) ++#define dm_pulldown_out_set _setmask(DM_PULLDOWN_OUT) ++#define dm_pulldown_out_reset _resetmask(DM_PULLDOWN_OUT) ++#define dm_pulldown_out_power _powermask(DM_PULLDOWN_OUT) ++ ++#define DP_PULLUP_OUT 12 /*!< DP pullup control - aka loc_conn */ ++#define dp_pullup_out _setmask(DP_PULLUP_OUT) ++#define dp_pullup_out_ _resetmask(DP_PULLUP_OUT) ++#define dp_pullup_out_set _setmask(DP_PULLUP_OUT) ++#define dp_pullup_out_reset _resetmask(DP_PULLUP_OUT) ++#define dp_pullup_out_power _powermask(DP_PULLUP_OUT) ++ ++#define DP_PULLDOWN_OUT 13 /*!< DP pulldown control */ ++#define dp_pulldown_out _setmask(DP_PULLDOWN_OUT) ++#define dp_pulldown_out_ _resetmask(DP_PULLDOWN_OUT) ++#define dp_pulldown_out_set _setmask(DP_PULLDOWN_OUT) ++#define dp_pulldown_out_reset _resetmask(DP_PULLDOWN_OUT) ++#define dp_pulldown_out_power _powermask(DP_PULLDOWN_OUT) ++ ++#define CLR_OVERCURRENT_OUT 14 /*!< Clear overcurrent indication */ ++#define clr_overcurrent_out _setmask(CLR_OVERCURRENT_OUT) ++#define clr_overcurrent_out_ _resetmask(CLR_OVERCURRENT_OUT) ++#define clr_overcurrent_out_set _setmask(CLR_OVERCURRENT_OUT) ++#define clr_overcurrent_out_reset _resetmask(CLR_OVERCURRENT_OUT) ++#define clr_overcurrent_out_power _powermask(CLR_OVERCURRENT_OUT) ++ ++#define DM_DET_OUT 15 /*!< Enable B-Device D- High detect */ ++#define dm_det_out _setmask(DM_DET_OUT) ++#define dm_det_out_ _resetmask(DM_DET_OUT) ++#define dm_det_out_set _setmask(DM_DET_OUT) ++#define dm_det_out_reset _resetmask(DM_DET_OUT) ++#define dm_det_out_power _powermask(DM_DET_OUT) ++ ++#define DP_DET_OUT 16 /*!< Enable B-Device D+ High detect */ ++#define dp_det_out _setmask(DP_DET_OUT) ++#define dp_det_out_ _resetmask(DP_DET_OUT) ++#define dp_det_out_set _setmask(DP_DET_OUT) ++#define dp_det_out_reset _resetmask(DP_DET_OUT) ++#define dp_det_out_power _powermask(DP_DET_OUT) ++ ++#define CR_DET_OUT 17 /*!< Enable D+ CR detect */ ++#define cr_det_out _setmask(CR_DET_OUT) ++#define cr_det_out_ _resetmask(CR_DET_OUT) ++#define cr_det_out_set _setmask(CR_DET_OUT) ++#define cr_det_out_reset _resetmask(CR_DET_OUT) ++#define cr_det_out_power _powermask(CR_DET_OUT) ++ ++#define CHARGE_PUMP_OUT 18 /*!< Enable external charge pump. */ ++#define charge_pump_out _setmask(CHARGE_PUMP_OUT) ++#define charge_pump_out_ _resetmask(CHARGE_PUMP_OUT) ++#define charge_pump_out_set _setmask(CHARGE_PUMP_OUT) ++#define charge_pump_out_reset _resetmask(CHARGE_PUMP_OUT) ++#define charge_pump_out_power _powermask(CHARGE_PUMP_OUT) ++ ++#define BDIS_ACON_OUT 19 /*!< Enable auto A-connect after B-disconnect. */ ++#define bdis_acon_out _setmask(BDIS_ACON_OUT) ++#define bdis_acon_out_ _resetmask(BDIS_ACON_OUT) ++#define bdis_acon_out_set _setmask(BDIS_ACON_OUT) ++#define bdis_acon_out_reset _resetmask(BDIS_ACON_OUT) ++#define bdis_acon_out_power _powermask(BDIS_ACON_OUT) ++ ++#define MX21_VBUS_DRAIN 20 /*!< MX21 hack */ ++#define mx21_vbus_drain _setmask(MX21_VBUS_DRAIN) ++#define mx21_vbus_drain_ _resetmask(MX21_VBUS_DRAIN) ++#define mx21_vbus_drain_set _setmask(MX21_VBUS_DRAIN) ++#define mx21_vbus_drain_reset _resetmask(MX21_VBUS_DRAIN) ++#define mx21_vbus_drain_power _powermask(MX21_VBUS_DRAIN) ++ ++#define ID_PULLDOWN_OUT 21 /*!< Enable the ID to ground pulldown ( (CEA-936 - 5 wire carkit.) */ ++#define id_pulldown_out _setmask(ID_PULLDOWN_OUT) ++#define id_pulldown_out_ _resetmask(ID_PULLDOWN_OUT) ++#define id_pulldown_out_set _setmask(ID_PULLDOWN_OUT) ++#define id_pulldown_out_reset _resetmask(ID_PULLDOWN_OUT) ++#define id_pulldown_out_power _powermask(ID_PULLDOWN_OUT) ++ ++#define UART_OUT 22 /*!< Enable Transparent UART mode (CEA-936.) */ ++#define uart_out _setmask(UART_OUT) ++#define uart_out_ _resetmask(UART_OUT) ++#define uart_out_set _setmask(UART_OUT) ++#define uart_out_reset _resetmask(UART_OUT) ++#define uart_out_power _powermask(UART_OUT) ++ ++#define AUDIO_OUT 23 /*!< Enable Audio mode (CEA-936 CarKit interrupt detector.) */ ++#define audio_out _setmask(AUDIO_OUT) ++#define audio_out_ _resetmask(AUDIO_OUT) ++#define audio_out_set _setmask(AUDIO_OUT) ++#define audio_out_reset _resetmask(AUDIO_OUT) ++#define audio_out_power _powermask(AUDIO_OUT) ++ ++#define MONO_OUT 24 /*!< Enable Mono-Audio mode (CEA-936.) */ ++#define mono_out _setmask(MONO_OUT) ++#define mono_out_ _resetmask(MONO_OUT) ++#define mono_out_set _setmask(MONO_OUT) ++#define mono_out_reset _resetmask(MONO_OUT) ++#define mono_out_power _powermask(MONO_OUT) ++ /* @) */ ++ ++ /*! @name Peripheral Controller Driver Outputs ++ * @{ ++ */ ++ ++#define REMOTE_WAKEUP_OUT 25 /*!< Peripheral will perform remote wakeup. */ ++#define remote_wakeup_out _setmask(REMOTE_WAKEUP_OUT) ++#define remote_wakeup_out_ _resetmask(REMOTE_WAKEUP_OUT) ++#define remote_wakeup_out_set _setmask(REMOTE_WAKEUP_OUT) ++#define remote_wakeup_out_reset _resetmask(REMOTE_WAKEUP_OUT) ++#define remote_wakeup_out_power _powermask(REMOTE_WAKEUP_OUT) ++ /* @) */ ++ ++ /*! @name Host Controller Driver Outputs ++ * @{ ++ */ ++ ++#define LOC_SOF_OUT 26 /*!< Host will enable packet traffic. */ ++#define loc_sof_out _setmask(LOC_SOF_OUT) ++#define loc_sof_out_ _resetmask(LOC_SOF_OUT) ++#define loc_sof_out_set _setmask(LOC_SOF_OUT) ++#define loc_sof_out_reset _resetmask(LOC_SOF_OUT) ++#define loc_sof_out_power _powermask(LOC_SOF_OUT) ++ ++#define LOC_SUSPEND_OUT 27 /*!< Host will suspend bus. */ ++#define loc_suspend_out _setmask(LOC_SUSPEND_OUT) ++#define loc_suspend_out_ _resetmask(LOC_SUSPEND_OUT) ++#define loc_suspend_out_set _setmask(LOC_SUSPEND_OUT) ++#define loc_suspend_out_reset _resetmask(LOC_SUSPEND_OUT) ++#define loc_suspend_out_power _powermask(LOC_SUSPEND_OUT) ++ ++#define REMOTE_WAKEUP_EN_OUT 28 /*!< Host will send remote wakeup enable or disable request. */ ++#define remote_wakeup_en_out _setmask(REMOTE_WAKEUP_EN_OUT) ++#define remote_wakeup_en_out_ _resetmask(REMOTE_WAKEUP_EN_OUT) ++#define remote_wakeup_en_out_set _setmask(REMOTE_WAKEUP_EN_OUT) ++#define remote_wakeup_en_out_reset _resetmask(REMOTE_WAKEUP_EN_OUT) ++#define remote_wakeup_en_out_power _powermask(REMOTE_WAKEUP_EN_OUT) ++ ++#define HNP_EN_OUT 29 /*!< Host will send HNP enable request. */ ++#define hnp_en_out _setmask(HNP_EN_OUT) ++#define hnp_en_out_ _resetmask(HNP_EN_OUT) ++#define hnp_en_out_set _setmask(HNP_EN_OUT) ++#define hnp_en_out_reset _resetmask(HNP_EN_OUT) ++#define hnp_en_out_power _powermask(HNP_EN_OUT) ++ ++#define HPWR_OUT 30 /*!< Host will enable high power (external charge pump.) */ ++#define hpwr_out _setmask(HPWR_OUT) ++#define hpwr_out_ _resetmask(HPWR_OUT) ++#define hpwr_out_set _setmask(HPWR_OUT) ++#define hpwr_out_reset _resetmask(HPWR_OUT) ++#define hpwr_out_power _powermask(HPWR_OUT) ++ /* @) */ ++#define MAX_OUTPUTS 31 ++ ++/* Generated by otg-ioctls-h.awk ++ * ++ * Do not Edit this file, ++ */ ++ ++ ++#if defined(OTG_LINUX) ++ ++#define OTGADMIN_MAGIC 'O' ++#define OTGADMIN_VERSION _IOR(OTGADMIN_MAGIC, 1, u64) ++#define OTGADMIN_STATUS _IOR(OTGADMIN_MAGIC, 3, struct otg_status_update) ++#define OTGADMIN_SET_FUNCTION _IOW(OTGADMIN_MAGIC, 4, struct otg_admin_command) ++#define OTGADMIN_GET_FUNCTION _IOR(OTGADMIN_MAGIC, 4, struct otg_admin_command) ++#define OTGADMIN_SET_INFO _IOW(OTGADMIN_MAGIC, 5, struct otg_firmware_info) ++#define OTGADMIN_GET_INFO _IOR(OTGADMIN_MAGIC, 5, struct otg_firmware_info) ++#define OTGADMIN_SET_STATE _IOW(OTGADMIN_MAGIC, 6, struct otg_state) ++#define OTGADMIN_GET_STATE _IOR(OTGADMIN_MAGIC, 6, struct otg_state) ++#define OTGADMIN_SET_TEST _IOW(OTGADMIN_MAGIC, 7, struct otg_test) ++#define OTGADMIN_GET_TEST _IOR(OTGADMIN_MAGIC, 7, struct otg_test) ++#define OTGADMIN_SET_SERIAL _IOW(OTGADMIN_MAGIC, 8, struct otg_admin_command) ++#define OTGADMIN_GET_SERIAL _IOR(OTGADMIN_MAGIC, 8, struct otg_admin_command) ++ ++ ++ ++ /*! ++ * Signal Sent: a_bcon_no_tmout_req ++ * State Valid: otg_host ++ * Application on A-host wants Ta_wait_bcon timeout disabled (non-OTG mode). ++ */ ++#define OTGADMIN_A_BCON_NO_TMOUT_REQ _IOW(OTGADMIN_MAGIC, 10, int) ++ ++ /*! ++ * Signal Sent: a_hpwr_req ++ * State Valid: otg_host ++ * Application on A-host wants external charge pump enabled. ++ */ ++#define OTGADMIN_A_HPWR_REQ _IOW(OTGADMIN_MAGIC, 11, int) ++ ++ /*! ++ * Signal Sent: bus_drop ++ * State Valid: otg_ok ++ * Application on Device needs to power down bus. ++ */ ++#define OTGADMIN_BUS_DROP _IOW(OTGADMIN_MAGIC, 12, int) ++ ++ /*! ++ * Signal Sent: a_bus_drop ++ * State Valid: otg_ok ++ * Application on A-Device needs to power down bus. ++ */ ++#define OTGADMIN_A_BUS_DROP _IOW(OTGADMIN_MAGIC, 13, int) ++ ++ /*! ++ * Signal Sent: b_bus_drop ++ * State Valid: otg_ok ++ * Application on B-Device needs to disconnect from bus. ++ */ ++#define OTGADMIN_B_BUS_DROP _IOW(OTGADMIN_MAGIC, 14, int) ++ ++ /*! ++ * Signal Sent: bus_req ++ * State Valid: otg_ok ++ * Application on Device wants to use the bus. ++ */ ++#define OTGADMIN_BUS_REQ _IOW(OTGADMIN_MAGIC, 15, int) ++ ++ /*! ++ * Signal Sent: a_bus_req ++ * State Valid: otg_ok ++ * Application on A-Device wants to act as host ++ */ ++#define OTGADMIN_A_BUS_REQ _IOW(OTGADMIN_MAGIC, 16, int) ++ ++ /*! ++ * Signal Sent: b_bus_req ++ * State Valid: otg_ok ++ * Application on B-Device wants to act as host ++ */ ++#define OTGADMIN_B_BUS_REQ _IOW(OTGADMIN_MAGIC, 17, int) ++ ++ /*! ++ * Signal Sent: b_sess_req ++ * State Valid: otg_ok ++ * Application on B-Device to perform SRP (alias for b_srp_req.) ++ */ ++#define OTGADMIN_B_SESS_REQ _IOW(OTGADMIN_MAGIC, 18, int) ++ ++ /*! ++ * Signal Sent: suspend_req ++ * State Valid: otg_host ++ * Application on Device requests bus be suspended (alias for a_bus_req/.) ++ */ ++#define OTGADMIN_SUSPEND_REQ _IOW(OTGADMIN_MAGIC, 19, int) ++ ++ /*! ++ * Signal Sent: a_suspend_req ++ * State Valid: otg_host ++ * Application on A-host requests bus be suspended (alias for a_bus_req/.) ++ */ ++#define OTGADMIN_A_SUSPEND_REQ _IOW(OTGADMIN_MAGIC, 20, int) ++ ++ /*! ++ * Signal Sent: b_suspend_req ++ * State Valid: otg_host ++ * Application on B-host requests bus be suspended (alias for b_bus_req/.) ++ */ ++#define OTGADMIN_B_SUSPEND_REQ _IOW(OTGADMIN_MAGIC, 21, int) ++ ++ /*! ++ * Signal Sent: set_remote_wakeup_cmd ++ * State Valid: a_host ++ * A-Device will send Remote Wakeup Enable Request ++ */ ++#define OTGADMIN_SET_REMOTE_WAKEUP_CMD _IOW(OTGADMIN_MAGIC, 22, int) ++ ++ /*! ++ * Signal Sent: remote_wakeup_cmd ++ * State Valid: tr_configured ++ * B-Device will perform Remote Wakeup. ++ */ ++#define OTGADMIN_REMOTE_WAKEUP_CMD _IOW(OTGADMIN_MAGIC, 23, int) ++ ++ /*! ++ * Signal Sent: reset_remote_wakeup_cmd ++ * State Valid: a_host ++ * A-Device will send Remote Wakeup Disable Request ++ */ ++#define OTGADMIN_RESET_REMOTE_WAKEUP_CMD _IOW(OTGADMIN_MAGIC, 24, int) ++ ++ /*! ++ * Signal Sent: clr_err_cmd ++ * State Valid: a_vbus_err ++ * A-Device ill clears Vbus overcurrent error. ++ */ ++#define OTGADMIN_CLR_ERR_CMD _IOW(OTGADMIN_MAGIC, 25, int) ++ ++ /*! ++ * Signal Sent: b_hnp_cmd ++ * State Valid: b_configured ++ * B-Device will attempt HNP ++ */ ++#define OTGADMIN_B_HNP_CMD _IOW(OTGADMIN_MAGIC, 26, int) ++ ++ /*! ++ * Signal Sent: ph_int_cmd ++ * State Valid: ph_audio ++ * B-Device will request Carkit interrupt ++ */ ++#define OTGADMIN_PH_INT_CMD _IOW(OTGADMIN_MAGIC, 27, int) ++ ++ /*! ++ * Signal Sent: ph_audio_cmd ++ * State Valid: ph_uart ++ * Application on B-Device connected to Carkit requests audio mode. ++ */ ++#define OTGADMIN_PH_AUDIO_CMD _IOW(OTGADMIN_MAGIC, 28, int) ++ ++ /*! ++ * Signal Sent: cr_int_cmd ++ * State Valid: cr_aud ++ * Application on A-Device wants to emulate Carkit ++ */ ++#define OTGADMIN_CR_INT_CMD _IOW(OTGADMIN_MAGIC, 29, int) ++ ++ /*! ++ * Signal Sent: led_on_cmd ++ * State Valid: ph_uart ++ * B-Device will enable Carkit LED ++ */ ++#define OTGADMIN_LED_ON_CMD _IOW(OTGADMIN_MAGIC, 30, int) ++ ++ /*! ++ * Signal Sent: led_off_cmd ++ * State Valid: ph_uart ++ * B-Device will disable Carkit LED ++ */ ++#define OTGADMIN_LED_OFF_CMD _IOW(OTGADMIN_MAGIC, 31, int) ++ ++ /*! ++ * Signal Sent: enable_otg ++ * State Valid: otg_all ++ * Move State Machine otg_disabled state. ++ */ ++#define OTGADMIN_ENABLE_OTG _IOW(OTGADMIN_MAGIC, 32, int) ++ ++#define OTGADMIN_MAXNR 33 ++ ++ ++#endif /* defined(OTG_LINUX) */ ++ ++/* Generated by otg-iocontrol-h.awk ++ * ++ * Do not Edit this file, ++ */ ++ ++ ++#if defined(OTG_WINCE) ++ ++#define OTGADMIN_BASE$ 0x401 ++#define _USBLAN_CTL_CODE(_Function, _Method, _Access) \ ++ CTL_CODE(OTGADMIN_BASE, _Function, _Method, _Access) ++ ++#define OTGADMIN_VERSION OTGADMIN_CTL_CODE(0x401, METHOD_BUFFERRED, FILE_READ_ACCESS) ++#define OTGADMIN_STATUS OTGADMIN_CTL_CODE(0x403, METHOD_BUFFERRED, FILE_READ_ACCESS) ++#define OTGADMIN_SET_FUNCTION OTGADMIN_CTL_CODE(0x404, METHOD_BUFFERRED, FILE_WRITE_ACCESS) ++#define OTGADMIN_GET_FUNCTION OTGADMIN_CTL_CODE(0x404, METHOD_BUFFERRED, FILE_READ_ACCESS) ++#define OTGADMIN_SET_INFO OTGADMIN_CTL_CODE(0x405, METHOD_BUFFERRED, FILE_WRITE_ACCESS) ++#define OTGADMIN_GET_INFO OTGADMIN_CTL_CODE(0x405, METHOD_BUFFERRED, FILE_READ_ACCESS) ++#define OTGADMIN_SET_STATE OTGADMIN_CTL_CODE(0x406, METHOD_BUFFERRED, FILE_WRITE_ACCESS) ++#define OTGADMIN_GET_STATE OTGADMIN_CTL_CODE(0x406, METHOD_BUFFERRED, FILE_READ_ACCESS) ++#define OTGADMIN_SET_TEST OTGADMIN_CTL_CODE(0x407, METHOD_BUFFERRED, FILE_WRITE_ACCESS) ++#define OTGADMIN_GET_TEST OTGADMIN_CTL_CODE(0x407, METHOD_BUFFERRED, FILE_READ_ACCESS) ++ ++ ++ ++ /*! ++ * Signal Sent: a_bcon_no_tmout_req ++ * State Valid: otg_host ++ * Application on A-host wants Ta_wait_bcon timeout disabled (non-OTG mode). ++ */ ++#define OTGADMIN_A_BCON_NO_TMOUT_REQ OTGADMIN_CTL_CODE(, 0x40a, METHOD_BUFFERRED, FILE_READ_ACCESS) ++ ++ /*! ++ * Signal Sent: a_hpwr_req ++ * State Valid: otg_host ++ * Application on A-host wants external charge pump enabled. ++ */ ++#define OTGADMIN_A_HPWR_REQ OTGADMIN_CTL_CODE(, 0x40b, METHOD_BUFFERRED, FILE_READ_ACCESS) ++ ++ /*! ++ * Signal Sent: bus_drop ++ * State Valid: otg_ok ++ * Application on Device needs to power down bus. ++ */ ++#define OTGADMIN_BUS_DROP OTGADMIN_CTL_CODE(, 0x40c, METHOD_BUFFERRED, FILE_READ_ACCESS) ++ ++ /*! ++ * Signal Sent: a_bus_drop ++ * State Valid: otg_ok ++ * Application on A-Device needs to power down bus. ++ */ ++#define OTGADMIN_A_BUS_DROP OTGADMIN_CTL_CODE(, 0x40d, METHOD_BUFFERRED, FILE_READ_ACCESS) ++ ++ /*! ++ * Signal Sent: b_bus_drop ++ * State Valid: otg_ok ++ * Application on B-Device needs to disconnect from bus. ++ */ ++#define OTGADMIN_B_BUS_DROP OTGADMIN_CTL_CODE(, 0x40e, METHOD_BUFFERRED, FILE_READ_ACCESS) ++ ++ /*! ++ * Signal Sent: bus_req ++ * State Valid: otg_ok ++ * Application on Device wants to use the bus. ++ */ ++#define OTGADMIN_BUS_REQ OTGADMIN_CTL_CODE(, 0x40f, METHOD_BUFFERRED, FILE_READ_ACCESS) ++ ++ /*! ++ * Signal Sent: a_bus_req ++ * State Valid: otg_ok ++ * Application on A-Device wants to act as host ++ */ ++#define OTGADMIN_A_BUS_REQ OTGADMIN_CTL_CODE(, 0x410, METHOD_BUFFERRED, FILE_READ_ACCESS) ++ ++ /*! ++ * Signal Sent: b_bus_req ++ * State Valid: otg_ok ++ * Application on B-Device wants to act as host ++ */ ++#define OTGADMIN_B_BUS_REQ OTGADMIN_CTL_CODE(, 0x411, METHOD_BUFFERRED, FILE_READ_ACCESS) ++ ++ /*! ++ * Signal Sent: b_sess_req ++ * State Valid: otg_ok ++ * Application on B-Device to perform SRP (alias for b_srp_req.) ++ */ ++#define OTGADMIN_B_SESS_REQ OTGADMIN_CTL_CODE(, 0x412, METHOD_BUFFERRED, FILE_READ_ACCESS) ++ ++ /*! ++ * Signal Sent: suspend_req ++ * State Valid: otg_host ++ * Application on Device requests bus be suspended (alias for a_bus_req/.) ++ */ ++#define OTGADMIN_SUSPEND_REQ OTGADMIN_CTL_CODE(, 0x413, METHOD_BUFFERRED, FILE_READ_ACCESS) ++ ++ /*! ++ * Signal Sent: a_suspend_req ++ * State Valid: otg_host ++ * Application on A-host requests bus be suspended (alias for a_bus_req/.) ++ */ ++#define OTGADMIN_A_SUSPEND_REQ OTGADMIN_CTL_CODE(, 0x414, METHOD_BUFFERRED, FILE_READ_ACCESS) ++ ++ /*! ++ * Signal Sent: b_suspend_req ++ * State Valid: otg_host ++ * Application on B-host requests bus be suspended (alias for b_bus_req/.) ++ */ ++#define OTGADMIN_B_SUSPEND_REQ OTGADMIN_CTL_CODE(, 0x415, METHOD_BUFFERRED, FILE_READ_ACCESS) ++ ++ /*! ++ * Signal Sent: set_remote_wakeup_cmd ++ * State Valid: a_host ++ * A-Device will send Remote Wakeup Enable Request ++ */ ++#define OTGADMIN_SET_REMOTE_WAKEUP_CMD OTGADMIN_CTL_CODE(, 0x416, METHOD_BUFFERRED, FILE_READ_ACCESS) ++ ++ /*! ++ * Signal Sent: remote_wakeup_cmd ++ * State Valid: tr_configured ++ * B-Device will perform Remote Wakeup. ++ */ ++#define OTGADMIN_REMOTE_WAKEUP_CMD OTGADMIN_CTL_CODE(, 0x417, METHOD_BUFFERRED, FILE_READ_ACCESS) ++ ++ /*! ++ * Signal Sent: reset_remote_wakeup_cmd ++ * State Valid: a_host ++ * A-Device will send Remote Wakeup Disable Request ++ */ ++#define OTGADMIN_RESET_REMOTE_WAKEUP_CMD OTGADMIN_CTL_CODE(, 0x418, METHOD_BUFFERRED, FILE_READ_ACCESS) ++ ++ /*! ++ * Signal Sent: clr_err_cmd ++ * State Valid: a_vbus_err ++ * A-Device ill clears Vbus overcurrent error. ++ */ ++#define OTGADMIN_CLR_ERR_CMD OTGADMIN_CTL_CODE(, 0x419, METHOD_BUFFERRED, FILE_READ_ACCESS) ++ ++ /*! ++ * Signal Sent: b_hnp_cmd ++ * State Valid: b_configured ++ * B-Device will attempt HNP ++ */ ++#define OTGADMIN_B_HNP_CMD OTGADMIN_CTL_CODE(, 0x41a, METHOD_BUFFERRED, FILE_READ_ACCESS) ++ ++ /*! ++ * Signal Sent: ph_int_cmd ++ * State Valid: ph_audio ++ * B-Device will request Carkit interrupt ++ */ ++#define OTGADMIN_PH_INT_CMD OTGADMIN_CTL_CODE(, 0x41b, METHOD_BUFFERRED, FILE_READ_ACCESS) ++ ++ /*! ++ * Signal Sent: ph_audio_cmd ++ * State Valid: ph_uart ++ * Application on B-Device connected to Carkit requests audio mode. ++ */ ++#define OTGADMIN_PH_AUDIO_CMD OTGADMIN_CTL_CODE(, 0x41c, METHOD_BUFFERRED, FILE_READ_ACCESS) ++ ++ /*! ++ * Signal Sent: cr_int_cmd ++ * State Valid: cr_aud ++ * Application on A-Device wants to emulate Carkit ++ */ ++#define OTGADMIN_CR_INT_CMD OTGADMIN_CTL_CODE(, 0x41d, METHOD_BUFFERRED, FILE_READ_ACCESS) ++ ++ /*! ++ * Signal Sent: led_on_cmd ++ * State Valid: ph_uart ++ * B-Device will enable Carkit LED ++ */ ++#define OTGADMIN_LED_ON_CMD OTGADMIN_CTL_CODE(, 0x41e, METHOD_BUFFERRED, FILE_READ_ACCESS) ++ ++ /*! ++ * Signal Sent: led_off_cmd ++ * State Valid: ph_uart ++ * B-Device will disable Carkit LED ++ */ ++#define OTGADMIN_LED_OFF_CMD OTGADMIN_CTL_CODE(, 0x41f, METHOD_BUFFERRED, FILE_READ_ACCESS) ++ ++ /*! ++ * Signal Sent: enable_otg ++ * State Valid: otg_all ++ * Move State Machine otg_disabled state. ++ */ ++#define OTGADMIN_ENABLE_OTG OTGADMIN_CTL_CODE(, 0x420, METHOD_BUFFERRED, FILE_READ_ACCESS) ++ ++#define OTGADMIN_MAXNR 33 ++ ++ ++#endif /* defined(OTG_WINCE) */ ++ ++/* Generated by otg-metas-h.awk ++ * ++ * Do not Edit thie file ++ */ ++ ++ ++ /*! @name OTG Figure 6-2 Meta States ++ * ++ * These are the Meta States defined in the 2.0 OTG Specification ++ * Figure 6.2 - Dual-Role A-Device. ++ * @{ ++ */ ++ ++#define m_a_idle 0 /* */ ++ ++#define m_a_wait_vrise 1 /* */ ++ ++#define m_a_wait_bcon 2 /* */ ++ ++#define m_a_host 3 /* */ ++ ++#define m_a_suspend 4 /* */ ++ ++#define m_a_peripheral 5 /* */ ++ ++#define m_a_wait_vfall 6 /* */ ++ ++#define m_a_vbus_err 7 /* */ ++ /* @} */ ++ /*! @name OTG Figure 6-3 Meta States ++ * ++ * These are the Meta States defined in the 2.0 OTG Specification ++ * Figure 6.3 - Dual-Role B-Device. ++ * @{ ++ */ ++ ++#define m_b_idle 8 /* */ ++ ++#define m_b_srp_init 9 /* */ ++ ++#define m_b_peripheral 10 /* */ ++ ++#define m_b_suspend 11 /* */ ++ ++#define m_b_wait_acon 12 /* */ ++ ++#define m_b_host 13 /* */ ++ ++#define m_b_suspended 14 /* */ ++ /* @} */ ++ /*! @name Carkit Meta States Figure 7-7 ++ * ++ * @{ ++ */ ++ ++#define m_ph_disc 15 /* Equivalent to b_peripheral */ ++ ++#define m_ph_init 16 /* */ ++ ++#define m_ph_uart 17 /* */ ++ ++#define m_ph_aud 18 /* */ ++ ++#define m_ph_wait 19 /* */ ++ ++#define m_ph_exit 20 /* */ ++ ++#define m_cr_init 21 /* */ ++ ++#define m_cr_uart 22 /* */ ++ ++#define m_cr_aud 23 /* */ ++ ++#define m_cr_ack 24 /* */ ++ ++#define m_cr_wait 25 /* */ ++ ++#define m_cr_disc 26 /* */ ++ /* @} */ ++ /*! @name Additional states used locally. ++ * ++ * @{ ++ */ ++ ++#define m_otg_init 27 /* */ ++ ++#define m_usb_accessory 28 /* */ ++ ++#define m_usb_factory 29 /* */ ++ ++#define m_unknown 30 /* */ ++ /* @} */ ++ ++#define OTG_METAS_FW 31 ++ ++extern char *otg_meta_names[]; +diff -uNr linux/drivers/no-otg/otg/otg-hcd.h linux/drivers/otg/otg/otg-hcd.h +--- linux/drivers/no-otg/otg/otg-hcd.h 1970-01-01 01:00:00.000000000 +0100 ++++ linux/drivers/otg/otg/otg-hcd.h 2006-09-01 21:41:33.000000000 +0200 +@@ -0,0 +1,85 @@ ++/* ++ * otg/otg-hcd.h - OTG Host Controller Driver ++ * ++ * Copyright (c) 2004 Belcarra ++ * ++ * By: ++ * Stuart Lynne <sl@belcarra.com>, ++ * Tom Rushworth <tbr@belcarra.com>, ++ * Bruce Balden <balden@belcarra.com> ++ * ++ * ++ */ ++/*! ++ * @defgroup OTGHCD Host Controller Driver Support ++ * @ingroup onthegogroup ++ */ ++/*! ++ * @file otg/otg/otg-hcd.h ++ * @brief Defines common to On-The-Go Host Controller Support ++ * ++ * This file defines the hcd_ops and hcd_instance structures. ++ * ++ * The hcd_ops structure contains all of the output functions that will ++ * be called as required by the OTG event handler when changing states. ++ * ++ * The hcd_instance structure is used to maintain the global data ++ * required by the host controller drivers. ++ * ++ * @ingroup OTGHCD ++ */ ++ ++/*! ++ * @name HCD Host Controller Driver ++ * @{ ++ */ ++struct hcd_instance; ++ ++/*! ++ * @struct hcd_ops ++ * The pcd_ops structure contains pointers to all of the functions implemented for the ++ * linked in driver. Having them in this structure allows us to easily determine what ++ * functions are available without resorting to ugly compile time macros or ifdefs ++ * ++ * There is only one instance of this, defined in the device specific lower layer. ++ */ ++struct hcd_ops { ++ ++ /* mandatory */ ++ int max_ports; /*!< maximum number of ports available */ ++ u32 capabilities; /*!< UDC Capabilities - see usbd-bus.h for details */ ++ char *name; /*!< name of controller */ ++ ++ /* Driver Initialization - by degrees ++ */ ++ int (*mod_init) (void); /*!< HCD Module Initialization */ ++ void (*mod_exit) (void); /*!< HCD Module Exit */ ++ ++ ++ otg_output_proc_t hcd_init_func; /*!< OTG calls to initialize or de-initialize the HCD */ ++ otg_output_proc_t hcd_en_func; /*!< OTG calls to enable or disable the HCD */ ++ otg_output_proc_t loc_sof_func; /*!< OTG calls to into a_host or b_host state - attempt to use port */ ++ otg_output_proc_t loc_suspend_func; /*!< OTG calls to suspend bus */ ++ otg_output_proc_t remote_wakeup_en_func; /*!< OTG calls to issue SET FEATURE REMOTE WAKEUP */ ++ otg_output_proc_t hnp_en_func; /*!< OTG calls to issues SET FEATURE B_HNP_ENABLE */ ++}; ++ ++/*! ++ * @struct hcd_instance ++ */ ++struct hcd_instance { ++ struct otg_instance *otg; /*!< pointer to OTG Instance */ ++ void * privdata; /*!< pointer to private data for PCD */ ++ struct WORK_STRUCT bh; /*!< work structure for bottom half handler */ ++}; ++ ++#define HCD hcd_trace_tag ++extern otg_tag_t HCD; ++extern struct hcd_ops hcd_ops; ++extern struct hcd_instance *hcd_instance; ++ ++extern void hcd_init_func(struct otg_instance *, u8 ); ++extern void hcd_en_func(struct otg_instance *, u8 ); ++ ++/* @} */ ++ +diff -uNr linux/drivers/no-otg/otg/otg-linux.h linux/drivers/otg/otg/otg-linux.h +--- linux/drivers/no-otg/otg/otg-linux.h 1970-01-01 01:00:00.000000000 +0100 ++++ linux/drivers/otg/otg/otg-linux.h 2006-09-01 21:41:33.000000000 +0200 +@@ -0,0 +1,284 @@ ++/* ++ * otg/otg/otg-linux.h ++ * ++ * Copyright (c) 2004 Belcarra ++ * ++ */ ++ ++/*! ++ * @file otg/otg/otg-linux.h ++ * @brief Linux OS Compatibility defines ++ * ++ * @ingroup OTGCore ++ */ ++#ifndef _OTG_LINUX_H ++#define _OTG_LINUX_H 1 ++ ++ ++ ++#include <linux/config.h> ++#include <linux/kernel.h> ++#include <linux/version.h> ++#include <linux/init.h> ++#include <linux/ctype.h> ++#include <linux/slab.h> ++#include <asm/types.h> ++#include <asm/uaccess.h> ++#include <asm/semaphore.h> ++#include <asm/atomic.h> ++#include <linux/proc_fs.h> ++#include <linux/interrupt.h> ++#include <linux/random.h> ++#include <linux/sched.h> ++ ++/*! @name Compilation Related ++ */ ++ ++/*! @{ */ ++#undef PRAGMAPACK ++#define PACKED __attribute__((packed)) ++#define INLINE __inline__ ++ ++/*! @} */ ++ ++/*! @name Memory Allocation Primitives ++ * ++ * CKMALLOC() ++ * LSTRDUP() ++ * LKFREE() ++ * LIST_ENTRY() ++ * LIST_FOR_EACH() ++ */ ++ ++ /*! @{ */ ++ ++#if defined(LINUX26) ++ #include <linux/gfp.h> ++#define GET_KERNEL_PAGE() __get_free_page(GFP_KERNEL) ++ ++#else /* LINUX26 */ ++ ++ #include <linux/mm.h> ++ #define GET_KERNEL_PAGE() get_free_page(GFP_KERNEL) ++#endif /* LINUX26 */ ++ ++ ++ ++// Common to all supported versions of Linux ?? ++ ++#define CKMALLOC(n,f) _ckmalloc(__FUNCTION__, __LINE__, n, f) ++#define LSTRDUP(str) _lstrdup(__FUNCTION__, __LINE__, str) ++#define LKFREE(p) _lkfree(__FUNCTION__, __LINE__, p) ++ ++#define ckmalloc(n,f) _ckmalloc(__FUNCTION__, __LINE__, n, f) ++#define lstrdup(str) _lstrdup(__FUNCTION__, __LINE__, str) ++#define lkfree(p) _lkfree(__FUNCTION__, __LINE__, p) ++ ++#define OTG_MALLOC_TEST ++#undef OTG_MALLOC_DEBUG ++ ++#ifdef OTG_MALLOC_TEST ++ extern int otg_mallocs; ++#endif ++ ++ ++static INLINE void *_ckmalloc (const char *func, int line, int n, int f) ++{ ++ void *p; ++ if ((p = kmalloc (n, f)) == NULL) { ++ return NULL; ++ } ++ memset (p, 0, n); ++ #ifdef OTG_MALLOC_TEST ++ ++otg_mallocs; ++ #endif ++ #ifdef OTG_MALLOC_DEBUG ++ printk(KERN_INFO"%s: %p %s %d %d\n", __FUNCTION__, p, func, line, otg_mallocs); ++ #endif ++ return p; ++} ++ ++static INLINE char *_lstrdup (const char *func, int line, char *str) ++{ ++ int n; ++ char *s; ++ if (str && (n = strlen (str) + 1) && (s = kmalloc (n, GFP_ATOMIC))) { ++#ifdef OTG_MALLOC_TEST ++ ++otg_mallocs; ++#endif ++#ifdef OTG_MALLOC_DEBUG ++ printk(KERN_INFO"%s: %p %s %d %d\n", __FUNCTION__, s, func, line, otg_mallocs); ++#endif ++ return strcpy (s, str); ++ } ++ return NULL; ++} ++ ++static INLINE void _lkfree (const char *func, int line, void *p) ++{ ++ if (p) { ++#ifdef OTG_MALLOC_TEST ++ --otg_mallocs; ++#endif ++#ifdef OTG_MALLOC_DEBUG ++ printk(KERN_INFO"%s: %p %s %d %d\n", __FUNCTION__, p, func, line, otg_mallocs); ++#endif ++ kfree (p); ++#ifdef MALLOC_TEST ++ if (otg_mallocs < 0) { ++ printk(KERN_INFO"%s: %p %s %d %d otg_mallocs less zero!\n", __FUNCTION__, p, func, line, otg_mallocs); ++ } ++#endif ++#ifdef OTG_MALLOC_DEBUG ++ else { ++ printk(KERN_INFO"%s: %s %d NULL\n", __FUNCTION__, func, line); ++ } ++#endif ++ } ++} ++ ++#if 1 ++#include <linux/list.h> ++#define LIST_NODE struct list_head ++#define LIST_NODE_INIT(name) struct list_head name = {&name, &name} ++#define LIST_ENTRY(pointer, type, member) list_entry(pointer, type, member) ++#define LIST_FOR_EACH(cursor, head) list_for_each(cursor, head) ++#define LIST_ADD_TAIL(n,h) list_add_tail(n,h) ++#define LIST_DEL(h) list_del(&(h)) ++#else ++#include <otg/otg-list.h> ++#endif ++ ++/*! @} */ ++ ++ ++/*! @name Atomic Operations ++ * ++ * atomic_post_inc() ++ * atomic_pre_dec() ++ */ ++/*! @{ */ ++static __inline__ int atomic_post_inc(volatile atomic_t *v) ++{ ++ unsigned long flags; ++ int result; ++ local_irq_save(flags); ++ result = (v->counter)++; ++ local_irq_restore(flags); ++ return(result); ++} ++ ++static __inline__ int atomic_pre_dec(volatile atomic_t *v) ++{ ++ unsigned long flags; ++ int result; ++ local_irq_save(flags); ++ result = --(v->counter); ++ local_irq_restore(flags); ++ return(result); ++} ++/*! @} */ ++ ++ ++ ++/*!@name Scheduling Primitives ++ * ++ * WORK_STRUCT ++ * WORK_ITEM ++ * ++ * SCHEDULE_TIMEOUT()\n ++ * SET_WORK_ARG()\n ++ * SCHEDULE_WORK()\n ++ * SCHEDULE_IMMEDIATE_WORK()\n ++ * NO_WORK_DATA()\n ++ * MOD_DEC_USE_COUNT\n ++ * MOD_INC_USE_COUNT\n ++ */ ++ ++/*! @{ */ ++ ++static void inline SCHEDULE_TIMEOUT(int seconds){ ++ schedule_timeout( seconds * HZ ); ++} ++ ++ ++/* Separate Linux 2.4 and 2.6 versions of scheduling primitives */ ++#if defined(LINUX26) ++ #include <linux/workqueue.h> ++ ++ #define WORK_STRUCT work_struct ++ #define WORK_ITEM work_struct ++ typedef struct WORK_ITEM WORK_ITEM; ++ #if 0 ++ #define PREPARE_WORK_ITEM(__item,__routine,__data) INIT_WORK((__item),(__routine),(__data)) ++ #else ++ #include <linux/interrupt.h> ++ #define PREPARE_WORK_ITEM(__item,__routine,__data) __prepare_work(&(__item),(__routine),(__data)) ++ static inline void __prepare_work(struct work_struct *_work, ++ void (*_routine), ++ void * _data){ ++ INIT_LIST_HEAD(&_work->entry); ++ _work->pending = 0; ++ _work->func = _routine; ++ _work->data = _data; ++ init_timer(&_work->timer); ++ } ++ #endif ++ #undef PREPARE_WORK ++ typedef void (* WORK_PROC)(void *); ++ ++ #define SET_WORK_ARG(__item, __data) (__item).data = __data ++ ++ #define SCHEDULE_WORK(item) schedule_work(&item) ++ #define SCHEDULE_IMMEDIATE_WORK(item) SCHEDULE_WORK((item)) ++ #define PENDING_WORK_ITEM(item) (item.pending != 0) ++ #define NO_WORK_DATA(item) (!item.data) ++ #define _MOD_DEC_USE_COUNT //Not used in 2.6 ++ #define _MOD_INC_USE_COUNT //Not used in 2.6 ++ ++#else /* LINUX26 */ ++ ++ #define WORK_STRUCT tq_struct ++ #define WORK_ITEM tq_struct ++ typedef struct WORK_ITEM WORK_ITEM; ++ #define PREPARE_WORK_ITEM(item,work_routine,work_data) { item.routine = work_routine; item.data = work_data; } ++ #define SET_WORK_ARG(__item, __data) (__item).data = __data ++ #define NO_WORK_DATA(item) (!(item).data) ++ #define SCHEDULE_WORK(item) schedule_task(&item) ++ #define PENDING_WORK_ITEM(item) (item.sync != 0) ++ #define _MOD_DEC_USE_COUNT MOD_DEC_USE_COUNT ++ #define _MOD_INC_USE_COUNT MOD_INC_USE_COUNT ++ ++ typedef void (* WORK_PROC)(void *); ++ ++ #if !defined(IRQ_HANDLED) ++ // Irq's ++ typedef void irqreturn_t; ++ #define IRQ_NONE ++ #define IRQ_HANDLED ++ #define IRQ_RETVAL(x) ++ #endif ++#endif /* LINUX26 */ ++ ++/*! @} */ ++ ++/*!@name Semaphores ++ * ++ * up() ++ * down() ++ */ ++/*! @{ */ ++#define UP(s) up(s) ++#define DOWN(s) down(s) ++/*! @} */ ++ ++/*! @name Printk ++ * ++ * PRINTK() ++ */ ++/*! @{ */ ++#define PRINTK(s) printk(s) ++/*! @} */ ++ ++ ++#endif /* _OTG_LINUX_H */ +diff -uNr linux/drivers/no-otg/otg/otg-list.h linux/drivers/otg/otg/otg-list.h +--- linux/drivers/no-otg/otg/otg-list.h 1970-01-01 01:00:00.000000000 +0100 ++++ linux/drivers/otg/otg/otg-list.h 2006-09-01 21:41:33.000000000 +0200 +@@ -0,0 +1,69 @@ ++#ifndef _OTG_LIST_H ++#define _OTG_LIST_H 1 ++ ++/* ++ This file defines doubly linked list capabilities ++ needed. Many operating systems have built in capabilities ++ which match and may possibly be borrowed. ++ ++ ++ Here is a typical implementation ++*/ ++ ++struct otg_list_node { ++ struct otg_list_node *previous, *next; ++}; ++typedef struct otg_list_node otg_list_node_t, *otg_list_node_ptr; ++ ++//A macro to define an list node within a structure ++#define OTG_LIST_HEAD struct otg_list_node ++#define OTG_LIST_HEAD_INIT(name) struct otg_list_node name = {&name, &name}; ++//Assume that an OTG_LIST_HEAD pointer is embedded as structure ++// Recover numerical offset of member "member" within a pointer to a "type" ++#define __OTG_PTR_OFFSET(type, member) (u32) ( &(((type *) 0)->member) ) ++//Recover typed pointer from pointer to interior of type at the given offset ++#define __OTG_PTR_CONTAINER(ptr, type, member) (type *) ((char *) ptr - __OTG_PTR_OFFSET(type,member)) ++// member "member" of type "type". Recover a pointer to the beginning of the structure ++ ++#define OTG_LIST_ENTRY(pointer, type, member) __OTG_PTR_CONTAINER(pointer, type, member) ++ ++//Recover list header embedded in structure which is a member of a list ++#define OTG_LIST_MEMBER(pointer, member) &(ptr->member) ++//Add an entry at the logical end of the list ++ ++//#define INLINE inline ++#define OTG_LIST_ADD_TAIL(new_entry, list_head) __otg_list_add_tail(new_entry, list_head) ++static INLINE void __otg_list_add_tail(struct otg_list_node *new_entry, struct otg_list_node *list_head) ++{ ++ struct otg_list_node *old_previous = list_head->previous; ++ old_previous->next = new_entry; ++ new_entry->next = list_head; ++ new_entry->previous = old_previous; ++ list_head -> previous = new_entry; ++ ++} ++ ++//Delete an entry from the surrounding list ++#define OTG_LIST_DEL_ENTRY(del_entry) __otg_list_del_entry(&(del_entry)) ++ ++static void INLINE __otg_list_del_entry(struct otg_list_node *del_entry){ ++ //Remove from list ++ del_entry->previous->next = del_entry->next; ++ del_entry->next->previous = del_entry->previous; ++ //Invalidate the entry ++ del_entry->previous = NULL; ++ del_entry->next = NULL; ++} ++ ++#define OTG_LIST_FOR_EACH(cursor, list) for(cursor=(list)->next; cursor != (list); cursor=cursor->next) ++ ++#undef LIST_HEAD ++#define LIST_HEAD OTG_LIST_HEAD ++#undef LIST_HEAD_INIT ++#define LIST_HEAD_INIT(name) OTG_LIST_HEAD_INIT(name) ++#define LIST_ENTRY(pointer,type,member) OTG_LIST_ENTRY(pointer,type,member) ++#define LIST_ADD_TAIL(new_entry, list_head) OTG_LIST_ADD_TAIL(new_entry, list_head) ++#define LIST_DEL_ENTRY(del_entry) OTG_LIST_DEL_ENTRY(del_entry) ++#define LIST_FOR_EACH(cursor, list) OTG_LIST_FOR_EACH(cursor, list) ++ ++#endif /*_OTG_LIST_H */ +diff -uNr linux/drivers/no-otg/otg/otg-module.h linux/drivers/otg/otg/otg-module.h +--- linux/drivers/no-otg/otg/otg-module.h 1970-01-01 01:00:00.000000000 +0100 ++++ linux/drivers/otg/otg/otg-module.h 2006-09-01 21:41:33.000000000 +0200 +@@ -0,0 +1,71 @@ ++/* ++ * otg/otg/otg-module.h ++ * ++ * Copyright (c) 2004 Belcarra ++ * ++ */ ++/*! ++ * @file otg/otg/otg-module.h ++ * @brief Linux Module OS Compatibility defines ++ * ++ * ++ * @ingroup OTGCore ++ */ ++#ifndef _OTG_MODULE_H ++#define _OTG_MODULE_H 1 ++ ++#if defined(OTG_LINUX) ++ #if !defined(_LINUX_MODULE_H) ++ #include <linux/module.h> ++ #endif /* _LINUX_MODULE_H */ ++ ++ #if defined(MODULE) ++ ++ #define MOD_EXIT(exit_routine) module_exit(exit_routine) ++ #define MOD_INIT(init_routine) module_init(init_routine) ++ #define MOD_PROC(proc) (proc) ++ #define MOD_AUTHOR(string) MODULE_AUTHOR(string) ++ #define MOD_PARM(param, type) MODULE_PARM(param, type) ++ #define MOD_PARM_DESC(param,desc) MODULE_PARM_DESC(param, desc) ++ #define MOD_DESCRIPTION(description) MODULE_DESCRIPTION(description) ++ #define OTG_EXPORT_SYMBOL(symbol) EXPORT_SYMBOL(symbol) ++ #define OTG_EPILOGUE 1 /* EPILOGUE ROUTINE NEEDED */ ++ ++ #else /* defined(MODULE) */ ++ ++ #define MOD_EXIT(exit_routine) ++ #define MOD_INIT(init_routine) module_init(init_routine) ++ #define MOD_PROC(proc) NULL ++ #define MOD_AUTHOR(string) ++ #define MOD_PARM(param, type) ++ #define MOD_PARM_DESC(param,desc) ++ #define MOD_DESCRIPTION(description) ++ #define OTG_EXPORT_SYMBOL(symbol) //EXPORT_SYMBOL(symbol) ++ #undef EXPORT_SYMBOL ++ #define OTG_EPILOGUE 0 /* EPILOGUE ROUTINE NOT NEEDED */ ++ ++ #endif /* defined(MODULE) */ ++ ++ //#undef MODULE_AUTHOR ++ //#undef MODULE_DESCRIPTION ++ //#undef MODULE_PARM_DESC ++ //#undef MODULE_PARM ++ #if defined(LINUX24) || defined(LINUX26) ++ #include <linux/version.h> ++ #if defined(MODULE) && (LINUX_VERSION_CODE >= KERNEL_VERSION (2,4,17)) ++ //"GPL License applies under certain cirumstances; consult your vendor for details" ++ #define EMBED_LICENSE() MODULE_LICENSE ("GPL") ++ #else ++ #define EMBED_LICENSE() //Operation not supported for earlier Linux kernels ++ #endif ++ ++ #else /* defined(LINUX24) || defined(LINUX26) */ ++ #error "Need to define EMBED_LICENSE for the current operating system" ++ #endif /* defined(LINUX24) || defined(LINUX26) */ ++ #define EMBED_MODULE_INFO(section,moduleinfo) static char __##section##_module_info[] = moduleinfo "balden@belcarra.com" ++ #define EMBED_USBD_INFO(moduleinfo) EMBED_MODULE_INFO(usbd,moduleinfo) ++ #define GET_MODULE_INFO(section) __##section##_module_info ++#endif /* defined(OTG_LINUX) */ ++ ++ ++#endif +diff -uNr linux/drivers/no-otg/otg/otg-ocd.h linux/drivers/otg/otg/otg-ocd.h +--- linux/drivers/no-otg/otg/otg-ocd.h 1970-01-01 01:00:00.000000000 +0100 ++++ linux/drivers/otg/otg/otg-ocd.h 2006-09-01 21:41:33.000000000 +0200 +@@ -0,0 +1,94 @@ ++/* ++ * otg/otg-ocd.h - OTG Controller Driver ++ * ++ * Copyright (c) 2004 Belcarra ++ * ++ * By: ++ * Stuart Lynne <sl@belcarra.com>, ++ * Tom Rushworth <tbr@belcarra.com>, ++ * Bruce Balden <balden@belcarra.com> ++ * ++ * ++ */ ++/*! ++ * @defgroup OTGOCD OTG Controller Driver Support ++ * @ingroup onthegogroup ++ */ ++/*! ++ * @file otg/otg/otg-ocd.h ++ * @brief Defines common to On-The-Go OTG Controller Support ++ * ++ * This file defines the ocd_ops and ocd_instance structures. ++ * ++ * The ocd_ops structure contains all of the output functions that will ++ * be called as required by the OTG event handler when changing states. ++ * ++ * The ocd_instance structure is used to maintain the global data ++ * required by the OTG controller drivers. ++ * ++ * @ingroup OTGOCD ++ */ ++ ++/*! ++ * @name OCD OTG Controller Driver ++ * @{ ++ */ ++//struct ocd_instance; ++ ++struct ocd_instance { ++ struct otg_instance *otg; ++ void * privdata; ++}; ++ ++ ++typedef int (*otg_timer_callback_proc_t) (void *); ++ ++#define OCD_CAPABILITIES_DR 1 << 0 ++#define OCD_CAPABILITIES_PO 1 << 1 ++#define OCD_CAPABILITIES_TR 1 << 2 ++#define OCD_CAPABILITIES_HOST 1 << 3 ++ ++#define OCD_CAPABILITIES_AUTO 1 << 4 ++ ++ ++/*! ++ * @struct ocd_ops ++ * The ocd_ops structure contains pointers to all of the functions implemented for the ++ * linked in driver. Having them in this structure allows us to easily determine what ++ * functions are available without resorting to ugly compile time macros or ifdefs ++ * ++ * There is only one instance of this, defined in the device specific lower layer. ++ */ ++struct ocd_ops { ++ ++ u32 capabilities; /* OCD Capabilities */ ++ ++ /* Driver Initialization - by degrees ++ */ ++ int (*mod_init) (void); /*!< OCD Module Initialization */ ++ void (*mod_exit) (void); /*!< OCD Module Exit */ ++ ++ otg_output_proc_t ocd_init_func; /*!< OTG calls to initialize or de-initialize the OCD */ ++ ++ int (*start_timer) (struct otg_instance *, int);/*!< called by OTG to start timer */ ++ u64 (*ticks) (void); /*!< called by OTG to fetch current ticks, typically micro-seconds when available */ ++ u64 (*elapsed) ( u64 *, u64 *); /*!< called by OTG to get micro-seconds elapsed between two ticks */ ++ u32 interrupts; /*!< called by OTG to get number of interrupts */ ++}; ++ ++ ++#if 0 ++struct ocd_instance { ++ struct otg_instance *otg; ++ void * privdata; ++}; ++#endif ++ ++#ifndef OTG_APPLICATION ++ ++//#define OCD ocd_trace_tag ++extern otg_tag_t OCD; ++extern struct ocd_ops ocd_ops; ++extern struct ocd_instance *ocd_instance; ++#endif ++/* @} */ +diff -uNr linux/drivers/no-otg/otg/otg-p2k.h linux/drivers/otg/otg/otg-p2k.h +--- linux/drivers/no-otg/otg/otg-p2k.h 1970-01-01 01:00:00.000000000 +0100 ++++ linux/drivers/otg/otg/otg-p2k.h 2006-09-01 21:41:33.000000000 +0200 +@@ -0,0 +1,208 @@ ++/* ++ * otg/otg/otg-p2k.h ++ * ++ * Copyright (c) 2004 Belcarra ++ * ++ */ ++/*! ++ * @file otg/otg/otg-p2k.h ++ * @brief Linux OS Compatibility defines ++ * ++ * @ingroup OTGCore ++ */ ++ ++#ifndef _OTG_P2K_H 1 ++#define _OTG_P2K_H 1 ++ ++/*!@name Common Platform 2000 includes ++ */ ++/*! * @{ */ ++ ++/* UNKNOWN SO FAR */ ++/*! @} */ ++ ++/*!@name Compilation Related ++ */ ++ ++/*! @{ */ ++#define PRAGMAPACK ++#define PACKED ++#define INLINE __inline ++ ++/*! @} */ ++ ++/*!@name OTG int typedefs ++ */ ++/*! * @{ */ ++typedef unsigned short u16; ++typedef unsigned char u8; ++typedef unsigned long int u32; ++typedef __int64 u64; ++typedef unsigned char __u8; ++typedef unsigned short __u16; ++typedef unsigned long int __u32; ++typedef __int64 __u64; ++ ++/*! @} */ ++ ++/*!@name Memory Allocation Primitives ++ * ++ * CKMALLOC() ++ * LSTRDUP() ++ * LKFREE() ++ * LIST_ENTRY() ++ */ ++/*! @{ */ ++ ++#define GFP_KERNEL 0 ++#define GFP_ATOMIC 0 ++ ++#define CKMALLOC(n,f) _ckmalloc(__FILE__, __LINE__, n, f) ++#define LSTRDUP(str) _lstrdup(__FILE__, __LINE__, str) ++#define LKFREE(p) _lkfree(__FILE__, __LINE__, p) ++ ++#define OTG_MALLOC_TEST ++#undef OTG_MALLOC_DEBUG ++ ++#ifdef OTG_MALLOC_TEST ++ extern int otg_mallocs; ++#endif ++ ++static INLINE void *_ckmalloc (const char *func, int line, int n, int f) ++{ ++ ++ return 0; ++} ++static INLINE char *_lstrdup (const char *func, int line, char *str) ++{ ++ ++ return 0; ++} ++static INLINE void _lkfree (const char *func, int line, void *p) ++{ ++} ++ ++// XXX ++#define LIST_ENTRY(p, t, m) \ ++ ((t *)((char *)(p)-(unsigned long)(&((t *)0)->m))) ++ ++// XXX ++#define LIST_FOR_EACH(p, h) \ ++ for (p = (h)->next, (p->next); p != (h); \ ++ p = p->next, (p->next)) ++ ++#define LIST_ADD_TAIL(n,h) (0) ++#define LIST_ADD_TAIL(n,h) (0) ++#define LIST_DEL(h) (0) ++ ++ ++/*! @} */ ++ ++ ++/*! @name Atomic Operations ++ * ++ * atomic_post_inc() ++ * atomic_pre_dec() ++ */ ++/*! @{ */ ++ ++/* @} */ ++ ++ ++ ++/*! @name Scheduling Primitives ++ * ++ * WORK_STRUCT ++ * WORK_ITEM ++ * ++ * SCHEDULE_TIMEOUT() ++ * SET_WORK_ARG() ++ * SCHEDULE_WORK() ++ * SCHEDULE_IMMEDIATE_WORK() ++ * NO_WORK_DATA() ++ * MOD_DEC_USE_COUNT ++ * MOD_INC_USE_COUNT ++ */ ++/*! @{ */ ++#define HZ 60 ++#define SCHEDULE_TIMEOUT(seconds) ++ ++ #define SCHEDULE_WORK(item) (0) ++#define PENDING_WORK_ITEM(item) (0) ++#define PREPARE_WORK_ITEM(__item,__routine,__data) (0) ++ ++/*! @} */ ++ ++ ++/*! @name Semaphores ++ * ++ * up() ++ * down() ++ */ ++/*! @{ */ ++#define UP(s) ++#define DOWN(s) ++/*! @} */ ++ ++ ++/*!@name Printk ++ * ++ * PRINTK() ++ */ ++/*! @{ */ ++//#define PRINTK(s) printk(s) ++// DEBUGMSG(ZONE_INIT,(_T("OTGCORE - CORE - %s"), _T(msg))); ++ ++/*! @} */ ++ ++/*! @name Little Endian Macros ++ * / ++ *! @{ ++ */ ++ ++__inline u16 cpu_to_le16 ( u16 x ) ++{ ++ return (x<<8) | (x>>8); ++} ++ ++__inline u16 le16_to_cpu ( u16 val ) ++{ ++ return ((((val) >> 8) & 0xff) | (((val) & 0xff) << 8)); ++} ++ ++__inline u32 le32_to_cpu (u32 val) ++{ ++ return (((val) & 0xff000000) >> 24) | (((val) & 0x00ff0000) >> 8) | ++ (((val) & 0x0000ff00) << 8) | (((val) & 0x000000ff) << 24); ++} ++/*! @} */ ++ ++/*! @name little endian macros ++ */ ++/*! @{ */ ++#define local_irq_save(f) f = 0; __asm cli ++#define local_irq_restore(f) __asm sti ++ ++/*! @} */ ++ ++/*! @name Ioctl ++ */ ++/*! @{ */ ++#define _IOR(m,n,s) ((m)|(n)) ++#define _IOW(m,n,s) ((m)|(n)) ++#define _IOCSIZE(a) (sizeof(a)) ++#define copy_from_user(d,s,n) memcpy(d,s,n) ++#define copy_to_user(d,s,n) memcpy(d,s,n) ++ ++/*! @} */ ++ ++/*! @name ERRNO ++ */ ++/*! @{ */ ++#define EINVAL WSAEINVAL ++ ++/*! @} */ ++ ++ ++ ++#endif /* _OTG_P2K_H */ +diff -uNr linux/drivers/no-otg/otg/otg-pcd.h linux/drivers/otg/otg/otg-pcd.h +--- linux/drivers/no-otg/otg/otg-pcd.h 1970-01-01 01:00:00.000000000 +0100 ++++ linux/drivers/otg/otg/otg-pcd.h 2006-09-01 21:41:33.000000000 +0200 +@@ -0,0 +1,84 @@ ++/* ++ * otg/otg-pcd.h - OTG Peripheral Controller Driver ++ * ++ * Copyright (c) 2004 Belcarra ++ * ++ * By: ++ * Stuart Lynne <sl@belcarra.com>, ++ * Tom Rushworth <tbr@belcarra.com>, ++ * Bruce Balden <balden@belcarra.com> ++ * ++ */ ++ ++/*! ++ * @defgroup OTGPCD Peripheral Controller Driver Support ++ * @ingroup onthegogroup ++ */ ++/*! ++ * @file otg/otg/otg-pcd.h ++ * @brief Defines common to On-The-Go Peripheral Controller Support ++ * ++ * This file defines the pcd_ops and pcd_instance structures. ++ * ++ * The pcd_ops structure contains all of the output functions that will ++ * be called as required by the OTG event handler when changing states. ++ * ++ * The pcd_instance structure is used to maintain the global data ++ * required by the peripheral controller drivers. ++ * ++ * @ingroup OTGPCD ++ */ ++ ++/*! ++ * @name PCD Peripheral Controller Driver ++ * @{ ++ */ ++typedef u16 (*framenum_t)(void); ++ ++/*! ++ * @struct pcd_ops ++ * ++ * The pcd_ops structure contains pointers to all of the functions implemented for the ++ * linked in driver. Having them in this structure allows us to easily determine what ++ * functions are available without resorting to ugly compile time macros or ifdefs ++ * ++ * There is only one instance of this, defined in the device specific lower layer. ++ */ ++struct pcd_ops { ++ ++ int (*mod_init) (void); /*!< PCD Module Initialization */ ++ void (*mod_exit) (void); /*!< PCD Module Exit */ ++ ++ otg_output_proc_t pcd_init_func; /*!< OTG calls to initialize or de-initialize the PCD */ ++ otg_output_proc_t pcd_en_func; /*!< OTG calls to enable or disable the PCD */ ++ otg_output_proc_t remote_wakeup_func; /*!< OTG calls to have PCD perform remote wakeup */ ++ ++ framenum_t framenum; /*!< OTG calls to get current USB Frame number */ ++}; ++ ++#define PCD pcd_trace_tag ++extern otg_tag_t PCD; ++extern struct pcd_ops pcd_ops; ++extern struct pcd_instance *pcd_instance; ++ ++/*! ++ * @struct pcd_instance ++ */ ++struct pcd_instance { ++ struct otg_instance *otg; /*!< pointer to OTG Instance */ ++ struct usbd_bus_instance *bus; /*!< pointer to usb bus instance */ ++ void * privdata; /*!< pointer to private data for PCD */ ++ int pcd_exiting; /*!< non-zero if OTG is unloading */ ++ struct WORK_STRUCT bh; /*!< work structure for bottom half handler */ ++ ++}; ++ ++ ++#if !defined(OTG_C99) ++extern void pcd_global_init(void); ++#endif /* !defined(OTG_C99) */ ++extern void pcd_init_func(struct otg_instance *, u8 ); ++extern void pcd_en_func(struct otg_instance *, u8 ); ++ ++/* @} */ ++ +diff -uNr linux/drivers/no-otg/otg/otg-tcd.h linux/drivers/otg/otg/otg-tcd.h +--- linux/drivers/no-otg/otg/otg-tcd.h 1970-01-01 01:00:00.000000000 +0100 ++++ linux/drivers/otg/otg/otg-tcd.h 2006-09-01 21:41:33.000000000 +0200 +@@ -0,0 +1,114 @@ ++/* ++ * otg/otg-tcd.h ++ * ++ * Copyright (c) 2004 Belcarra ++ * ++ * By: ++ * Stuart Lynne <sl@belcarra.com>, ++ * Tom Rushworth <tbr@belcarra.com>, ++ * Bruce Balden <balden@belcarra.com> ++ * ++ */ ++ ++/*! ++ * @defgroup OTGTCD Transceiver Controller Driver Support ++ * @ingroup onthegogroup ++ */ ++/*! ++ * @file otg/otg/otg-tcd.h ++ * @brief Defines common to On-The-Go Transceiver Controller Support ++ * ++ * This file defines the tcd_ops and tcd_instance structures. ++ * ++ * The tcd_ops structure contains all of the output functions that will ++ * be called as required by the OTG event handler when changing states. ++ * ++ * The tcd_instance structure is used to maintain the global data ++ * required by the transceiver controller drivers. ++ * ++ * @ingroup OTGTCD ++ */ ++ ++/*! ++ * @name TCD Transceiver Controller Driver ++ * @{ ++ */ ++ ++ ++/*! ++ * @struct tcd_ops ++ * The pcd_ops structure contains pointers to all of the functions implemented for the ++ * linked in driver. Having them in this structure allows us to easily determine what ++ * functions are available without resorting to ugly compile time macros or ifdefs ++ * ++ * There is only one instance of this, defined in the device specific lower layer. ++ */ ++struct tcd_ops { ++ u64 initial_state; ++ ++ void * privdata; ++ ++ /* Driver Initialization - by degrees ++ */ ++ int (*mod_init) (void); /*!< TCD Module Initialization */ ++ void (*mod_exit) (void); /*!< TCD Module Exit */ ++ ++ ++ /* 3. called for usbd_vbus() if defined */ ++ int (*vbus) (struct otg_instance *); /* return non-zero if the Vbus valid */ ++ int (*id) (struct otg_instance *); /* return non-zero if the ID valid */ ++ ++ /* 4. called by otg event to control outputs ++ */ ++ ++ otg_output_proc_t tcd_init_func; /*!< OTG calls to initialize or de-initialize the HCD */ ++ otg_output_proc_t tcd_en_func; /*!< OTG calls to enable or disable the TCD */ ++ otg_output_proc_t chrg_vbus_func; /*!< OTG calls to enable or disable charging Vbus */ ++ otg_output_proc_t drv_vbus_func; /*!< OTG calls to enable or disable Vbus */ ++ otg_output_proc_t dischrg_vbus_func; /*!< OTG calls to enable or disable discharging Vbus */ ++ otg_output_proc_t dp_pullup_func; /*!< OTG calls to enable or disable D+ pullup (aka loc_conn) */ ++ otg_output_proc_t dm_pullup_func; /*!< OTG calls to enable or disable D- pullup (aka loc_carkit) */ ++ otg_output_proc_t dp_pulldown_func; /*!< OTG calls to enable or disable D+ pulldown (aka loc_conn) */ ++ otg_output_proc_t dm_pulldown_func; /*!< OTG calls to enable or disable D- pulldown (aka loc_carkit) */ ++ otg_output_proc_t peripheral_host_func; /*!< OTG calls to enable or disable D- pulldown (aka loc_carkit) */ ++ otg_output_proc_t overcurrent_func; /*!< OTG calls to clear overcurrent indication */ ++ otg_output_proc_t dm_det_func; /*!< OTG calls to enable or disable D+ pullup detection */ ++ otg_output_proc_t dp_det_func; /*!< OTG calls to enable or disable D- pullup detection */ ++ otg_output_proc_t cr_det_func; /*!< OTG calls to enable or disable D+ CRINT detection */ ++ otg_output_proc_t charge_pump_func; /*!< OTG calls to enable or disable external charge pump */ ++ otg_output_proc_t bdis_acon_func; /*!< OTG calls to enable or disable the BDIS ACON feature */ ++ otg_output_proc_t mx21_vbus_drain_func; /*!< OTG calls to enable or disable the mx21 vbus drain feature */ ++ otg_output_proc_t id_pulldown_func; /*!< OTG calls to enable or disable ID pulldown to ground */ ++ otg_output_proc_t audio_func; /*!< OTG calls to enable or disable audio function */ ++ otg_output_proc_t uart_func; /*!< OTG calls to enable or disable uart function */ ++ otg_output_proc_t mono_func; /*!< OTG calls to enable or disable mono function */ ++}; ++ ++/*! ++ * @struct hcd_instance ++ */ ++struct tcd_instance { ++ struct otg_instance *otg; /*!< pointer to OTG Instance */ ++ struct WORK_STRUCT bh; /*!< work structure for bottom half handler */ ++}; ++ ++ ++extern struct trace_ops trace_ops; ++extern struct tcd_ops tcd_ops; ++ ++ ++extern struct tcd_instance *tcd_instance; ++extern int tcd_vbus (struct otg_instance *otg); ++ ++/* pcd_cable_event[_irq] - these can be called in a traditional PCD implementation ++ * to set b_sess_vld appropriately when vbus is sensed (pcd must implement ocd_ops.vbus) ++ */ ++extern void tcd_cable_event_irq (struct otg_instance *); ++extern void tcd_cable_event (struct otg_instance *); ++ ++//extern void tcd_init(struct otg_instance *, u8 ); ++ ++#define TCD tcd_trace_tag ++extern otg_tag_t TCD; ++ ++/* @} */ +diff -uNr linux/drivers/no-otg/otg/otg-trace.h linux/drivers/otg/otg/otg-trace.h +--- linux/drivers/no-otg/otg/otg-trace.h 1970-01-01 01:00:00.000000000 +0100 ++++ linux/drivers/otg/otg/otg-trace.h 2006-09-01 21:41:33.000000000 +0200 +@@ -0,0 +1,404 @@ ++/* ++ * otg/otgcore/otg-trace.h ++ * ++ * Copyright (c) 2004 Belcarra ++ * ++ * By: ++ * Stuart Lynne <sl@belcarra.com>, ++ * Tom Rushworth <tbr@belcarra.com>, ++ * ++ */ ++/*! ++ * @file otg/otg/otg-trace.h ++ * @brief Core Defines for USB OTG Core Layaer ++ * ++ * Fast Trace Utility ++ * This set of definitions and code is meant to provide a _fast_ debugging facility ++ * (much faster than printk) so that time critical code can be debugged by looking ++ * at a trace of events provided by reading a file in the procfs without affecting ++ * the timing of events in the critical code. ++ * ++ * The mechanism used it to allocate a (large) ring buffer of relatively small structures ++ * that include location and high-res timestamp info, and up to 8 bytes of optional ++ * data. Values are stored and timestamps are taken as the critical code runs, but ++ * data formatting and display are done during the procfs read, when more time is ++ * available :). ++ * ++ * Note that there is usually some machine dependent code involved in getting the ++ * high-res timestamp, and there may be other bits used just to keep the overall ++ * time impact as low as possible. ++ * ++ * Varargs up to 9 arguments are now supported, but you have to supply the number ++ * of args, since examining the format string for the number at trace event time ++ * was deemed too expensive time-wise. ++ * ++ * ++ * @ingroup OTGCore ++ */ ++ ++#ifndef OTG_TRACE_H ++#define OTG_TRACE_H 1 ++ ++//#include <linux/interrupt.h> ++ ++ ++ ++typedef u8 otg_tag_t; // 1-origin tags (0 ==> invalid/unused trace entry. Max is 32. ++ ++#ifdef PRAGMAPACK ++#pragma pack(push,1) ++#endif /* PRAGMAPACK */ ++typedef enum otg_trace_types { ++ otg_trace_msg_invalid_n, ++ otg_trace_msg_va_start_n, ++ otg_trace_msg_va_list_n, ++ otg_trace_setup_n, ++ otg_trace_msg_n, ++ otg_trace_msg32_n, ++ otg_trace_msg16_n, ++ otg_trace_msg8_n ++} PACKED otg_trace_types_t; ++#ifdef PRAGMAPACK ++#pragma pack(pop) ++#endif /* PRAGMAPACK */ ++ ++typedef struct otg_trace_msg { ++ char *msg; ++} otg_trace_msg_t; ++ ++typedef struct otg_trace_msg32 { ++ char *msg; ++ u32 val; ++} otg_trace_msg32_t; ++ ++typedef struct otg_trace_msg16 { ++ char *msg; ++ u16 val0; ++ u16 val1; ++} otg_trace_msg16_t; ++ ++typedef struct otg_trace_msg8 { ++ char *msg; ++ u8 val0; ++ u8 val1; ++ u8 val2; ++ u8 val3; ++} otg_trace_msg8_t; ++ ++#define OTG_TRACE_MAX_IN_VA 7 ++typedef struct otg_trace_msg_va_list { ++ u32 val[OTG_TRACE_MAX_IN_VA]; ++} otg_trace_msg_va_list_t; ++ ++typedef struct otg_trace_msg_va_start { ++ const char *function; ++ u32 interrupts; ++ u64 ticks; ++ u16 framenum; ++ u16 dummy; ++ ++ union { ++ otg_trace_msg_t msg; ++ otg_trace_msg8_t msg8; ++ otg_trace_msg16_t msg16; ++ otg_trace_msg32_t msg32; ++ ++#if !defined(BELCARRA_DEBUG) ++ struct usbd_device_request setup; ++#else ++ unsigned char setup[8]; ++#endif /* !defined(BELCARRA_DEBUG) */ ++ ++ } trace; ++} PACKED otg_trace_msg_va_start_t; ++ ++typedef struct trace { ++ otg_trace_types_t otg_trace_type; ++ otg_tag_t tag; ++ u8 va_num_args; ++ u8 in_interrupt:1; ++ u8 id_gnd:1; ++ union { ++ otg_trace_msg_va_start_t s; ++ otg_trace_msg_va_list_t l; ++ } va; ++} PACKED otg_trace_t; ++ ++#define TRACE_MAX_IS_2N 1 ++#define TRACE_MAX 0x00008000 ++#define TRACE_MASK 0x00007FFF ++//#define TRACE_MAX 0x000080 ++//#define TRACE_MASK 0x00007F ++ ++ ++#if defined(CONFIG_OTG_TRACE) || defined(CONFIG_OTG_TRACE_MODULE) ++ ++#if !defined(OLD_TRACE_API) ++ ++// The new API hides otg_trace_get_next(), and adds otg_trace_setup() as part of the core. ++ ++#if !defined(BELCARRA_DEBUG) ++extern void otg_trace_setup(otg_tag_t tag, const char *fn, void *setup); ++#define TRACE_SETUP(tag,setup) otg_trace_setup(tag,__FUNCTION__,setup) ++#endif ++#define TRACE_MSG0(tag, msg) otg_trace_msg(tag, __FUNCTION__, 0, msg, 0) ++#define TRACE_FMSG0(tag, f, msg) otg_trace_msg(tag, f, 0, msg, 0) ++ ++#else ++ ++extern int otg_trace_first; ++extern int otg_trace_last_read; ++extern int otg_trace_next; ++ ++#if 0 ++ ++extern otg_trace_t *otg_traces; ++ ++ ++otg_trace_t *otg_trace_get_next(otg_tag_t tag, const char *fn, otg_trace_types_t otg_trace_type, int n); ++ ++#if !defined(BELCARRA_DEBUG) ++/* ++ * Trace a setup packet. ++ * The BELCARRA_DEBUG guard is so the rest of these functions can be used ++ * outside of a USB context, such as debugging TTY flow control. ++ */ ++static __inline__ void otg_trace_setup(otg_tag_t tag, const char *fn, struct usbd_device_request *setup) ++{ ++ if (otg_traces) { ++ otg_trace_t *p = otg_trace_get_next(tag,fn,otg_trace_setup_n,1); ++ memcpy(&p->va.s.trace.setup, setup, sizeof(struct usbd_device_request)); ++ } ++} ++#define TRACE_SETUP(tag,setup) otg_trace_setup(tag,__FUNCTION__,setup) ++#endif /* !defined(BELCARRA_DEBUG) */ ++ ++/* ++ * Trace a single non-parameterized message. ++ */ ++static __inline__ void otg_trace_msg_0(otg_tag_t tag, const char *fn, char *msg) ++{ ++ if (otg_traces) { ++ otg_trace_t *p = otg_trace_get_next(tag,fn,otg_trace_msg_n,1); ++ p->va.s.trace.msg.msg = msg; ++ } ++} ++#define TRACE_MSG0(tag, msg) otg_trace_msg_0(tag, __FUNCTION__, msg) ++#define TRACE_FMSG0(tag, f, msg) otg_trace_msg_0(tag, f, msg) ++ ++/* ++ * Trace a message with a single 32-bit value. ++ */ ++static __inline__ void otg_trace_msg_1xU32(otg_tag_t tag, const char *fn, char *fmt, u32 val) ++{ ++ if (otg_traces) { ++ otg_trace_t *p = otg_trace_get_next(tag,fn,otg_trace_msg32_n,1); ++ p->va.s.trace.msg32.val = val; ++ p->va.s.trace.msg32.msg = fmt; ++ } ++} ++#define OTRACE_MSG1(tag,fmt,val) otg_trace_msg_1xU32(tag,__FUNCTION__,fmt,(u32)val) ++ ++/* ++ * Trace a message with two 16-bit values. ++ */ ++static __inline__ void otg_trace_msg_2xU16(otg_tag_t tag, const char *fn, char *fmt, u16 val0, u16 val1) ++{ ++ if (otg_traces) { ++ otg_trace_t *p = otg_trace_get_next(tag,fn,otg_trace_msg16_n,1); ++ p->va.s.trace.msg16.val0 = val0; ++ p->va.s.trace.msg16.val1 = val1; ++ p->va.s.trace.msg16.msg = fmt; ++ } ++} ++#define OTRACE_MSG2(tag,fmt,val0,val1) otg_trace_msg_2xU16(tag,__FUNCTION__,fmt,(u16)val0,(u16)val1) ++ ++/* ++ * Trace a message with four 8-bit values. ++ */ ++static __inline__ void otg_trace_msg_4xU8(otg_tag_t tag, const char *fn, char *fmt, u8 val0, u8 val1, u8 val2, u8 val3) ++{ ++ if (otg_traces) { ++ otg_trace_t *p = otg_trace_get_next(tag,fn,otg_trace_msg8_n,1); ++ p->va.s.trace.msg8.val0 = val0; ++ p->va.s.trace.msg8.val1 = val1; ++ p->va.s.trace.msg8.val2 = val2; ++ p->va.s.trace.msg8.val3 = val3; ++ p->va.s.trace.msg8.msg = fmt; ++ } ++} ++#define OTRACE_MSG4(tag,fmt,val0,val1,val2,val3) otg_trace_msg_4xU8(tag,__FUNCTION__,fmt,(u8)val0,(u8)val1,(u8)val2,(u8)val3) ++#endif ++ ++#endif ++ ++extern void otg_trace_msg(otg_tag_t tag, const char *fn, u8 nargs, char *fmt, ...); ++ ++#define eprintf(format, args...) printk (KERN_INFO format , ## args) ++ ++#define TRACE_MSG(tag, nargs, fmt, args...) otg_trace_msg(tag, __FUNCTION__, nargs, fmt, ## args) ++ ++#define TRACE_MSG1(tag, fmt, a1) \ ++ otg_trace_msg(tag, __FUNCTION__, 1, fmt, a1) ++#define TRACE_MSG2(tag, fmt, a1, a2) \ ++ otg_trace_msg(tag, __FUNCTION__, 2, fmt, a1, a2) ++#define TRACE_MSG3(tag, fmt, a1, a2, a3) \ ++ otg_trace_msg(tag, __FUNCTION__, 3, fmt, a1, a2, a3) ++#define TRACE_MSG4(tag, fmt, a1, a2, a3, a4) \ ++ otg_trace_msg(tag, __FUNCTION__, 4, fmt, a1, a2, a3, a4) ++#define TRACE_MSG5(tag, fmt, a1, a2, a3, a4, a5) \ ++ otg_trace_msg(tag, __FUNCTION__, 5, fmt, a1, a2, a3, a4, a5) ++#define TRACE_MSG6(tag, fmt, a1, a2, a3, a4, a5, a6) \ ++ otg_trace_msg(tag, __FUNCTION__, 6, fmt, a1, a2, a3, a4, a5, a6) ++#define TRACE_MSG7(tag, fmt, a1, a2, a3, a4, a5, a6, a7) \ ++ otg_trace_msg(tag, __FUNCTION__, 7, fmt, a1, a2, a3, a4, a5, a6, a7) ++#define TRACE_MSG8(tag, fmt, a1, a2, a3, a4, a5, a6, a7, a8) \ ++ otg_trace_msg(tag, __FUNCTION__, 8, fmt, a1, a2, a3, a4, a5, a6, a7, a8) ++#if 0 ++#define TRACE_MSG9(tag, fmt, a1, a2, a3, a4, a5, a6, a7, a8, a9) \ ++ otg_trace_msg(tag, __FUNCTION__, 9, fmt, a1, a2, a3, a4, a5, a6, a7, a8, a9) ++#endif ++#define TRACE_FMSG1(tag, f, fmt, a1) \ ++ otg_trace_msg(tag, f, 1, fmt, a1) ++#define TRACE_FMSG2(tag, f, fmt, a1, a2) \ ++ otg_trace_msg(tag, f, 2, fmt, a1, a2) ++#define TRACE_FMSG3(tag, f, fmt, a1, a2, a3) \ ++ otg_trace_msg(tag, f, 3, fmt, a1, a2, a3) ++#define TRACE_FMSG4(tag, f, fmt, a1, a2, a3, a4) \ ++ otg_trace_msg(tag, f, 4, fmt, a1, a2, a3, a4) ++#define TRACE_FMSG5(tag, f, fmt, a1, a2, a3, a4, a5) \ ++ otg_trace_msg(tag, f, 5, fmt, a1, a2, a3, a4, a5) ++#define TRACE_FMSG6(tag, f, fmt, a1, a2, a3, a4, a5, a6) \ ++ otg_trace_msg(tag, f, 6, fmt, a1, a2, a3, a4, a5, a6) ++#define TRACE_FMSG7(tag, f, fmt, a1, a2, a3, a4, a5, a6, a7) \ ++ otg_trace_msg(tag, f, 7, fmt, a1, a2, a3, a4, a5, a6, a7) ++#define TRACE_FMSG8(tag, f, fmt, a1, a2, a3, a4, a5, a6, a7, a8) \ ++ otg_trace_msg(tag, f, 8, fmt, a1, a2, a3, a4, a5, a6, a7, a8) ++#if 0 ++#define TRACE_FMSG9(tag, f, fmt, a1, a2, a3, a4, a5, a6, a7, a8, a9) \ ++ otg_trace_msg(tag, f, 9, fmt, a1, a2, a3, a4, a5, a6, a7, a8, a9) ++#endif ++extern otg_tag_t otg_trace_obtain_tag(void); ++extern otg_tag_t otg_trace_invalidate_tag(otg_tag_t tag); ++ ++#elif defined(OTG_WINCE) ++ ++#define TRACE_MSG0(tag, msg) \ ++ DEBUGMSG(ZONE_INIT,(_T("OTGCORE - CORE - %s"), _T(msg))) ++ ++#define TRACE_MSG1(tag, fmt, a1) \ ++ DEBUGMSG(ZONE_INIT,(_T("OTGCORE - CORE - %s %d"), _T(fmt), a1)) ++ ++#define TRACE_MSG2(tag, fmt, a1, a2) \ ++ DEBUGMSG(ZONE_INIT,(_T("OTGCORE - CORE - %s %d %d"), _T(fmt), a1, a2)) ++ ++#define TRACE_MSG3(tag, fmt, a1, a2, a3) \ ++ DEBUGMSG(ZONE_INIT,(_T("OTGCORE - CORE - %s %d %d"), _T(fmt), a1, a2, a3)); ++ ++#define TRACE_MSG4(tag, fmt, a1, a2, a3, a4) \ ++ DEBUGMSG(ZONE_INIT,(_T("OTGCORE - CORE - %s %d %d %d %d"), _T(fmt), a1, a2, a3, a4)); ++ ++#define TRACE_MSG5(tag, fmt, a1, a2, a3, a4, a5) \ ++ DEBUGMSG(ZONE_INIT,(_T("OTGCORE - CORE - %s %d %d %d %d %d"), _T(fmt), a1, a2, a3, a4, a5)); ++ ++#define TRACE_MSG6(tag, fmt, a1, a2, a3, a4, a5, a6) \ ++ DEBUGMSG(ZONE_INIT,(_T("OTGCORE - CORE - %s %d %d %d %d %d %d %d"), _T(fmt), a1, a2, a3, a4, a5, a6)); ++ ++#define TRACE_MSG7(tag, fmt, a1, a2, a3, a4, a5, a6, a7) \ ++ DEBUGMSG(ZONE_INIT,(_T("OTGCORE - CORE - %s %d %d %d %d %d %d %d %d"), _T(fmt), a1, a2, a3, a4, a5, a6, a7)); ++ ++#define TRACE_MSG8(tag, fmt, a1, a2, a3, a4, a5, a6, a7, a8) \ ++ DEBUGMSG(ZONE_INIT,(_T("OTGCORE - CORE - %s %d %d %d %d %d %d %d %d %d"), _T(fmt), a1, a2, a3, a4, a5, a6, a7, a8)); ++ ++extern otg_tag_t otg_trace_obtain_tag(void); ++extern otg_tag_t otg_trace_invalidate_tag(otg_tag_t tag); ++ ++/* ++#define TRACE_OTG_CURRENT(o) \ ++ DEBUGMSG(ZONE_INIT,(_T("OTGCORE - CURRENT - %s (%s) RESET: %08x SET: %08x"),\ ++ otg_state_names[o->state], \ ++ otg_state_names[o->previous], \ ++ (u32)(~o->current_inputs), \ ++ (u32)(o->current_inputs))\ ++ ); ++ ++#define TRACE_OTG_CHANGE(o) \ ++ DEBUGMSG(ZONE_INIT,(_T("OTGCORE - CHANGE - %s (%s) RESET: %08x SET: %08x"),\ ++ otg_state_names[o->state], \ ++ otg_state_names[o->previous], \ ++ (u32)(~o->current_inputs), \ ++ (u32)(o->current_inputs))\ ++ ); ++ ++#define TRACE_OTG_NEW(o) \ ++ DEBUGMSG(ZONE_INIT,(_T("OTGCORE - NEW - %s (%s) RESET: %08x SET: %08x"),\ ++ otg_state_names[o->state], \ ++ otg_state_names[o->previous], \ ++ (u32)(~o->current_inputs), \ ++ (u32)(o->current_inputs))\ ++ ); ++ ++#define TRACE_OTG_INPUTS(u) \ ++ DEBUGMSG(ZONE_INIT,(_T("OTGCORE - INPUTS - RESET: %08x SET: %08x"),\ ++ (u32)(u >> 32), \ ++ (u32)(u & 0xffffffff))\ ++ ); ++*/ ++ ++extern otg_tag_t otg_trace_obtain_tag(void); ++extern otg_tag_t otg_trace_invalidate_tag(otg_tag_t tag); ++ ++#else /* defined(CONFIG_OTG_TRACE) || defined(CONFIG_OTG_TRACE_MODULE) */ ++ ++#define TRACE_SETUP(tag,setup) ++ ++//#define TRACE_MSG(tag,nargs,fmt,...) ++//#define TRACE_FMSG(tag, nargs,fmt,...) ++ ++#define TRACE_MSG0(tag, fmt) ++#define TRACE_MSG1(tag, fmt, a1) ++#define TRACE_MSG2(tag, fmt, a1, a2) ++#define TRACE_MSG3(tag, fmt, a1, a2, a3) ++#define TRACE_MSG4(tag, fmt, a1, a2, a3, a4) ++#define TRACE_MSG5(tag, fmt, a1, a2, a3, a4, a5) ++#define TRACE_MSG6(tag, fmt, a1, a2, a3, a4, a5, a6) ++#define TRACE_MSG7(tag, fmt, a1, a2, a3, a4, a5, a6, a7) ++#define TRACE_MSG8(tag, fmt, a1, a2, a3, a4, a5, a6, a7, a8) ++//#define TRACE_MSG9(tag, fmt, a1, a2, a3, a4, a5, a6, a7, a8, a9) ++ ++#define TRACE_FMSG0(tag, f, fmt) ++#define TRACE_FMSG1(tag, f, fmt, a1) ++#define TRACE_FMSG2(tag, f, fmt, a1, a2) ++#define TRACE_FMSG3(tag, f, fmt, a1, a2, a3) ++#define TRACE_FMSG4(tag, f, fmt, a1, a2, a3, a4) ++#define TRACE_FMSG5(tag, f, fmt, a1, a2, a3, a4, a5) ++#define TRACE_FMSG6(tag, f, fmt, a1, a2, a3, a4, a5, a6) ++#define TRACE_FMSG7(tag, f, fmt, a1, a2, a3, a4, a5, a6, a7) ++#define TRACE_FMSG8(tag, f, fmt, a1, a2, a3, a4, a5, a6, a7, a8) ++//#define TRACE_FMSG9(tag, f, fmt, a1, a2, a3, a4, a5, a6, a7, a8, a9) ++ ++//static inline otg_tag_t otg_trace_obtain_tag(void) ++//{ ++// return 0; ++//} ++ ++//static inline otg_tag_t otg_trace_invalidate_tag(otg_tag_t tag) ++//{ ++// return 0; ++//} ++ ++extern otg_tag_t otg_trace_obtain_tag(void); ++extern otg_tag_t otg_trace_invalidate_tag(otg_tag_t tag); ++ ++#endif /* defined(CONFIG_OTG_TRACE) || defined(CONFIG_OTG_TRACE_MODULE) */ ++ ++#define USBD usbd_trace_tag ++extern otg_tag_t USBD; ++ ++//#define ACM acm_trace_tag ++//extern otg_tag_t ACM; ++ ++//#define NETWORK network_trace_tag ++//extern otg_tag_t NETWORK; ++ ++#endif /* OTG_TRACE_H */ +diff -uNr linux/drivers/no-otg/otg/otg-utils.h linux/drivers/otg/otg/otg-utils.h +--- linux/drivers/no-otg/otg/otg-utils.h 1970-01-01 01:00:00.000000000 +0100 ++++ linux/drivers/otg/otg/otg-utils.h 2006-09-01 21:41:33.000000000 +0200 +@@ -0,0 +1,151 @@ ++/* ++ * otg/otg/otg-utils.h ++ * ++ * Basic utilities for OTG package ++ * Copyright (c) 2004 Belcarra ++ */ ++/*! ++ * @file otg/otg/otg-utils.h ++ * @brief Useful macros. ++ * ++ * ++ * @ingroup OTGCore ++ */ ++ ++ ++#if !defined(_OTG_UTILS_H) ++#define _OTG_UTILS_H 1 ++ ++/*! @name Min, Max and zero ++ * @{ ++ */ ++#ifndef MIN ++#define MIN(a,b) ((a) < (b) ? (a) : (b)) ++#endif ++#ifndef MAX ++#define MAX(a,b) ((a) > (b) ? (a) : (b)) ++#endif ++ ++#define ZERO(a) memset(&a, 0, sizeof(a)) ++ ++/* @} */ ++ ++/*! @name TRUE and FALSE ++ * ++ * @{ ++ */ ++#if !defined(TRUE) ++#define TRUE 1 ++#endif ++ ++#if !defined(FALSE) ++#define FALSE 0 ++#endif ++/* @} */ ++ ++ ++/*! @name CATCH and THROW ++ * ++ * These macros implement a conceptually clean way to use ++ * C's goto to implement a standard error handler. The typical forms are: ++ * ++ * THROW(error); // continue execution in CATCH statement ++ * THROW_IF(test, error); // continue execution in CATCH statement if test true ++ * THROW_UNLESS(test, error); // continue execution in CATCH statement if test false ++ * ++ * // program execution will continue after the CATCH statement; ++ * CATCH(error) { ++ * // handle error here ++ * } ++ * ++ * @{ ++ */ ++#define CATCH(x) while(0) x: ++#define THROW(x) goto x ++#define THROW_IF(e, x) if (e) goto x; ++#define THROW_UNLESS(e, x) UNLESS (e) goto x; ++/* @} */ ++ ++ ++/*! @name UNLESS ++ * ++ * This implements a simple equivalent to negated if (expression). ++ * ++ * Unless expression then statement else statement. ++ * ++ * @{ ++ */ ++#define unless(x) if(!(x)) ++#define UNLESS(x) if(!(x)) ++/* @} */ ++ ++/*! @name BREAK_ and CONTINUE_ ++ * ++ * These implement a conditional break and continue statement. ++ * ++ * @{ ++ */ ++#define BREAK_UNLESS(x) UNLESS (x) break; ++#define BREAK_IF(x) if (x) break; ++#define CONTINUE_IF(x) if (x) continue; ++#define CONTINUE_UNLESS(x) UNLESS (x) continue; ++ ++/* @} */ ++ ++ ++/*! @name RETURN_ ++ * ++ * These implement conditional return statement. ++ * @{ ++ */ ++#define RETURN_IF(x) if (x) return; ++#define RETURN_ZERO_IF(x) if (x) return 0; ++#define RETURN_NULL_IF(x) if (x) return NULL; ++#define RETURN_EBUSY_IF(x) if (x) return -EBUSY; ++#define RETURN_EFAULT_IF(x) if (x) return -EFAULT; ++#define RETURN_EINVAL_IF(x) if (x) return -EINVAL; ++#define RETURN_ENOMEM_IF(x) if (x) return -ENOMEM; ++#define RETURN_EAGAIN_IF(x) if (x) return -EAGAIN; ++#define RETURN_ETIMEDOUT_IF(x) if (x) return -ETIMEDOUT; ++#define RETURN_EPIPE_IF(x) if (x) return -EPIPE; ++#define RETURN_IRQ_HANDLED_IF(x) if (x) return IRQ_HANDLED; ++#define RETURN_TRUE_IF(x) if (x) return TRUE; ++#define RETURN_FALSE_IF(x) if (x) return FALSE; ++ ++#define RETURN_UNLESS(x) UNLESS (x) return; ++#define RETURN_NULL_UNLESS(x) UNLESS (x) return NULL; ++#define RETURN_ZERO_UNLESS(x) UNLESS (x) return 0; ++#define RETURN_EBUSY_UNLESS(x) UNLESS (x) return -EBUSY; ++#define RETURN_EFAULT_UNLESS(x) UNLESS (x) return -EFAULT; ++#define RETURN_EINVAL_UNLESS(x) UNLESS (x) return -EINVAL; ++#define RETURN_ENOMEM_UNLESS(x) UNLESS (x) return -ENOMEM; ++#define RETURN_EAGAIN_UNLESS(x) UNLESS (x) return -EAGAIN; ++#define RETURN_ETIMEDOUT_UNLESS(x) UNLESS (x) return -ETIMEDOUT; ++#define RETURN_EPIPE_UNLESS(x) UNLESS (x) return -EPIPE; ++#define RETURN_IRQ_HANDLED_UNLESS(x) UNLESS (x) return IRQ_HANDLED; ++#define RETURN_TRUE_UNLESS(x) UNLESS (x) return TRUE; ++#define RETURN_FALSE_UNLESS(x) UNLESS (x) return FALSE; ++ ++/* @} */ ++ ++ ++/*! @name likely and unlikely ++ * ++ * Under linux these can be used to provide a hint to GCC that ++ * the expression being evaluated is probably going to evaluate ++ * to true (likely) or false (unlikely). The intention is that ++ * GCC can then provide optimal code for that. ++ * @{ ++ */ ++#ifndef likely ++#define likely(x) x ++#endif ++ ++#ifndef unlikely ++#define unlikely(x) x ++#endif ++/* @} */ ++ ++ ++#endif /* _OTG_UTILS_H */ ++ +diff -uNr linux/drivers/no-otg/otg/pcd-include.h linux/drivers/otg/otg/pcd-include.h +--- linux/drivers/no-otg/otg/pcd-include.h 1970-01-01 01:00:00.000000000 +0100 ++++ linux/drivers/otg/otg/pcd-include.h 2006-09-01 21:41:33.000000000 +0200 +@@ -0,0 +1,36 @@ ++/* ++ * otg/udc.h ++ * ++ * Copyright (c) 2004 Belcarra ++ * ++ * By: ++ * Stuart Lynne <sl@belcarra.com>, ++ * Tom Rushworth <tbr@belcarra.com>, ++ * Bruce Balden <balden@belcarra.com> ++ * ++ */ ++/*! ++ * @file otg/otg/pcd-include.h ++ * @brief Linux OS Precompiled Headers ++ * ++ * @ingroup OTGCore ++ */ ++ ++#include <otg/otg-compat.h> ++#include <otg/otg-module.h> ++ ++#include <asm/irq.h> ++#include <asm/system.h> ++#include <asm/io.h> ++ ++#include <otg/usbp-chap9.h> ++//#include <otg/usbp-func.h> ++#include <otg/usbp-bus.h> ++#include <otg/otg-trace.h> ++#include <otg/otg-api.h> ++#include <otg/otg-tcd.h> ++#include <otg/otg-hcd.h> ++#include <otg/otg-pcd.h> ++#include <otg/otg-ocd.h> ++#include <otg/usbp-pcd.h> ++ +diff -uNr linux/drivers/no-otg/otg/usbp-bus.h linux/drivers/otg/otg/usbp-bus.h +--- linux/drivers/no-otg/otg/usbp-bus.h 1970-01-01 01:00:00.000000000 +0100 ++++ linux/drivers/otg/otg/usbp-bus.h 2006-09-01 21:41:33.000000000 +0200 +@@ -0,0 +1,354 @@ ++/* ++ * otg/otg/usbp-bus.h - USB Device Bus Interface Driver Interface ++ * ++ * Copyright (c) 2004 Belcarra ++ * ++ * By: ++ * Stuart Lynne <sl@belcarra.com>, ++ * Tom Rushworth <tbr@belcarra.com>, ++ * Bruce Balden <balden@belcarra.com> ++ * ++ */ ++ ++/*! ++ * @file otg/otg/usbp-bus.h ++ * @brief Bus interface Defines for USB Device Core Layaer ++ * ++ * This file contains the USB Peripheral Driver definitions. ++ * ++ * The ++ * ++ * This is the interface between the bottom of the USB Core and the top of ++ * the Bus Interace Drivers and is comprised of: ++ * ++ * o public functions exported by the USB Core layer ++ * ++ * o structures and functions passed by the Function Driver to the USB ++ * Core layer ++ * ++ * USB Peripheral Controller Drivers are structured such that the upper edge ++ * implements interfaces to the USB Peripheral Core layer to provide low ++ * level USB services and the lower edge interfaces to the actual USB ++ * Hardware (typically called the USB Device Controller or UDC.) ++ * ++ * The peripheral controller layer primarily deals with endpoints. ++ * There is one control endpoint and one or more data endpoints. ++ * ++ * The control endpoint is special, with received data processed with ++ * the built in EP0 function. Which in turn will pass requests to ++ * interface functions as appropriate. ++ * ++ * Data endpoints are controlled by interface driver and have a ++ * function callback specified by the interface driver to perform ++ * whatever needs to be done when data is sent or received. ++ * ++ * ++ * ++ * @ingroup USBP ++ */ ++ ++struct usbd_endpoint_map; ++struct usbd_endpoint_instance; ++struct usbd_bus_instance; ++struct usbd_urb; ++ ++/*! ++ * USB Bus Interface Driver structures ++ * ++ * Driver description: ++ * ++ * struct usbd_bus_operations ++ * struct usbd_bus_driver ++ * ++ */ ++ ++extern int usbd_maxstrings; ++ ++ ++/*! ++ * exported to otg ++ */ ++struct usbd_ops { ++ int (*admin) (char *); ++ char * (*function_name) (int); ++}; ++ ++ ++ ++/*! 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 endpoint_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 usbd_bus_operations { ++ //int (*xbus_serial_number) (struct usbd_bus_instance *, char *); ++ ++ int (*start_endpoint_in) (struct usbd_bus_instance *, struct usbd_endpoint_instance *); ++ int (*start_endpoint_out) (struct usbd_bus_instance *, struct usbd_endpoint_instance *); ++ int (*cancel_urb_irq) (struct usbd_urb *); ++ //int (*device_feature) (struct usbd_bus_instance *, int, int); ++ int (*endpoint_halted) (struct usbd_bus_instance *, int, int); ++ int (*halt_endpoint) (struct usbd_bus_instance *, int, int, int); ++ int (*set_address) (struct usbd_bus_instance *, int); ++ int (*event_handler) (struct usbd_bus_instance *, usbd_device_event_t, int); ++ int (*request_endpoints) (struct usbd_bus_instance *, struct usbd_endpoint_map *, ++ int, struct usbd_endpoint_request *); ++ int (*set_endpoints) (struct usbd_bus_instance *, int , struct usbd_endpoint_map *); ++ int (*framenum) (void); ++ u64 (*ticks) (void); ++ u64 (*elapsed) (u64 *, u64 *); ++ u64 (*interrupts) (void); ++}; ++ ++/*! Endpoint Map ++ * ++ * An array of these structures is created by the bus interface driver to ++ * show what endpoints have been configured for the function driver. ++ * ++ * This is the logical array of endpoints that are indexed into by the ++ * function layer using the logical endpoint numbers defined by the function ++ * drivers. It maps these numbers into a real physical endpoint. ++ * ++ * Fields that can be different for Full speed versus High speed are ++ * represented with an array both values are available. ++ * ++ * For interfaces with multiple alternate settings, each separate ++ * combinantion of interface_num/altsetting/endpoint must be specified. ++ * It is up to the lower layers to determine if / when overlapped ++ * endpoints can be re-used. ++ * ++ */ ++struct usbd_endpoint_map { ++ u8 configuration; ++ u8 interface_num; ++ u8 alternate; ++ u8 bEndpointAddress[2]; // logical endpoint address ++ u16 wMaxPacketSize[2]; // packetsSize for requested endpoint ++ u8 bmAttributes[2]; // requested endpoint type ++ u16 transferSize[2]; // transferSize for bulk transfers ++ u8 physicalEndpoint[2]; // physical endpoint number ++ struct usbd_endpoint_instance *endpoint; ++}; ++ ++ ++/*! Endpoint configuration ++ * ++ * Per endpoint configuration data. Used to track which actual physical ++ * endpoints. ++ * ++ * The bus layer maintains an array of these to represent all physical ++ * endpoints. ++ * ++ */ ++struct usbd_endpoint_instance { ++ int bEndpointAddress; // logical endpoint address ++ int physical_endpoint; // physical endpoint address - bus interface specific ++ int bmAttributes; // endpoint type ++ u16 wMaxPacketSize; // packet size for requested endpoint ++ u32 feature_setting; // save set feature information ++ ++ // control ++ int state; // available for use by bus interface driver ++ ++ // receive side ++ struct urb_link rdy; // empty urbs ready to receive ++ struct usbd_urb *rcv_urb; // active urb ++ u32 rcv_transferSize; // maximum transfer size from function driver ++ int rcv_error; // current bulk-in has an error ++ ++ // transmit side ++ struct urb_link tx; // urbs ready to transmit ++ struct usbd_urb *tx_urb; // active urb ++ u32 tx_transferSize; // maximum transfer size from function driver ++ ++ u32 sent; // data already sent ++ u32 last; // data sent in last packet XXX do we need this ++ ++ void *privdata; ++ ++}; ++ ++/*! @name endpoint zero states ++ * @{ ++ */ ++#define WAIT_FOR_SETUP 0 ++#define DATA_STATE_XMIT 1 ++#define DATA_STATE_NEED_ZLP 2 ++#define WAIT_FOR_OUT_STATUS 3 ++#define DATA_STATE_RECV 4 ++#define DATA_STATE_PENDING_XMIT 5 ++#define WAIT_FOR_IN_STATUS 6 ++/*! @} */ ++ ++/*! 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 usbd_bus_driver { ++ char *name; ++ u8 max_endpoints; // maximimum number of rx enpoints ++ u8 otg_bmAttributes; ++ u8 maxpacketsize; ++ u8 HighSpeedCapable; ++ struct usbd_device_description *device_description; ++ struct usbd_bus_operations *bops; ++ void *privdata; ++ u32 capabilities; ++ u8 bMaxPower; ++ u8 ports; ++}; ++ ++/*! ++ * Bus state ++ * ++ * enabled or disabled ++ */ ++typedef enum usbd_bus_state { ++ usbd_bus_state_unknown, ++ usbd_bus_state_disabled, ++ usbd_bus_state_enabled ++} usbd_bus_state_t; ++ ++ ++/*! ++ * @name UDC Capabilities ++ * @{ ++ */ ++#define HAVE_CABLE_IRQ 0x0001 ++#define REMOTE_WAKEUP_SUPPORTED 0x0002 ++#define ROOT_HUB 0x0004 ++/*! @} */ ++ ++/*! 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 usbd_bus_instance { ++ ++ struct usbd_bus_driver *driver; ++ ++ int endpoints; ++ struct usbd_endpoint_instance *endpoint_array; // array of available configured endpoints ++ ++ struct urb_link finished; // processed urbs ++ ++ //char *serial_number_str; ++ ++ usbd_device_status_t status; // device status ++ usbd_bus_state_t bus_state; ++ usbd_device_state_t device_state; // current USB Device state ++ usbd_device_state_t suspended_state; // previous USB Device state ++ ++ struct usbd_function_instance *ep0; // ep0 configuration ++ struct usbd_function_instance *function_instance; ++ ++ ++ u8 HighSpeedFlag; ++ ++ u8 ConfigurationValue; // current set configuration (zero is default) ++ u8 bNumInterfaces; // number of interfaces in the current configuration ++ u8 *alternates; // array[0..interfaces-1] of alternate settings ++ // for each interface ++ ++ u32 device_feature_settings;// save set feature information ++ u8 otg_bmAttributes; ++ ++ struct WORK_STRUCT device_bh; // runs as bottom half, equivalent to interrupt time ++ ++ void *privdata; // private data for the bus interface ++ char *arg; ++}; ++ ++ ++ ++ ++/*! bus driver registration ++ * ++ * Called by bus interface drivers to register themselves when loaded ++ * or de-register when unloading. ++ */ ++struct usbd_bus_instance *usbd_register_bus (struct usbd_bus_driver *, int); ++void usbd_deregister_bus (struct usbd_bus_instance *); ++ ++ ++/*! Enable/Disable Function ++ * ++ * Called by a bus interface driver to select and enable a specific function ++ * driver. ++ */ ++int usbd_enable_function_irq (struct usbd_bus_instance *, char *, char *); ++void usbd_disable_function (struct usbd_bus_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. ++ */ ++void usbd_bus_event_handler (struct usbd_bus_instance *, usbd_device_event_t, int); ++void usbd_bus_event_handler_irq (struct usbd_bus_instance *, usbd_device_event_t, int); ++ ++ ++/*! ++ * usbd_device_request - 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. ++ * ++ * This function must return 0 for success and -EINVAL if the request ++ * is to be stalled. ++ * ++ * Not that if the SETUP is Host to Device with a non-zero wLength then there ++ * *MUST* be a valid receive urb queued OR the request must be stalled. ++ */ ++int usbd_device_request (struct usbd_bus_instance*, struct usbd_device_request *); ++ ++/*! ++ * Device I/O ++ */ ++void usbd_urb_finished (struct usbd_urb *, int ); ++void usbd_urb_finished_irq (struct usbd_urb *, int ); ++struct usbd_urb *usbd_first_urb_detached_irq (urb_link * hd); ++ ++/*! flush endpoint ++ */ ++void usbd_flush_endpoint_irq (struct usbd_endpoint_instance *); ++void usbd_flush_endpoint (struct usbd_endpoint_instance *); ++int usbd_find_endpoint_index(struct usbd_bus_instance *bus, int bEndpointAddress); ++ ++ ++ +diff -uNr linux/drivers/no-otg/otg/usbp-chap9.h linux/drivers/otg/otg/usbp-chap9.h +--- linux/drivers/no-otg/otg/usbp-chap9.h 1970-01-01 01:00:00.000000000 +0100 ++++ linux/drivers/otg/otg/usbp-chap9.h 2006-09-01 21:41:33.000000000 +0200 +@@ -0,0 +1,1577 @@ ++/* ++ * otg/otg/usbp-chap9.h ++ * ++ * Copyright (c) 2004 Belcarra ++ * By: ++ * Stuart Lynne <sl@belcarra.com>, ++ * Tom Rushworth <tbr@belcarra.com>, ++ * Bruce Balden <balden@belcarra.com> ++ * ++ */ ++/*! ++ * @defgroup USBP USB Peripheral Support ++ * @ingroup onthegogroup ++ */ ++ ++ ++/*! ++ * @file otg/otg/usbp-chap9.h ++ * @brief Chapter 9 and other class descriptor structure definitions. ++ * ++ * USB Descriptors are used to build a configuration database for each USB ++ * Function driver. ++ * ++ * @ingroup USBP ++ */ ++ ++/*! ++ * @name 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 ++/*! @} */ ++ ++ ++/*! @name USB Device Request Types (bmRequestType) ++ *C.f. USB 2.0 Table 9-2 ++*/ ++ ++/*! @{ */ ++ ++#define USB_TYPE_STANDARD (0x00 << 5) ++#define USB_TYPE_CLASS (0x01 << 5) ++#define USB_TYPE_VENDOR (0x02 << 5) ++#define USB_TYPE_RESERVED (0x03 << 5) ++ ++#define USB_RECIP_DEVICE 0x00 ++#define USB_RECIP_INTERFACE 0x01 ++#define USB_RECIP_ENDPOINT 0x02 ++#define USB_RECIP_OTHER 0x03 ++ ++#define USB_RECIP_HUB 0x00 ++#define USB_RECIP_PORT 0x03 ++ ++#define USB_DIR_OUT 0 ++#define USB_DIR_IN 0x80 ++#define USB_ENDPOINT_OPT 0x40 ++/*! @} */ ++ ++/*! @name Descriptor types ++ * C.f. USB Table 9-5 ++ */ ++ ++/*! @{ */ ++ ++#define USB_DT_DEVICE 0x01 ++#define USB_DT_CONFIGURATION 0x02 ++#define USB_DT_STRING 0x03 ++#define USB_DT_INTERFACE 0x04 ++#define USB_DT_ENDPOINT 0x05 ++#define USB_DT_DEVICE_QUALIFIER 0x06 ++#define USB_DT_OTHER_SPEED_CONFIGURATION 0x07 ++#define USB_DT_INTERFACE_POWER 0x08 ++#define USB_DT_OTG 0x09 ++ ++ ++#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) ++/*! @} */ ++ ++/*! @name 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 ++/*! @} */ ++ ++/*! @name Endpoints ++ */ ++/*! @{ */ ++#define USB_ENDPOINT_NUMBER_MASK 0x0f /* in bEndpointAddress */ ++#define USB_ENDPOINT_DIR_MASK 0x80 ++ ++#define USB_ENDPOINT_MASK 0x03 /* in bmAttributes */ ++#define USB_ENDPOINT_CONTROL 0x00 ++#define USB_ENDPOINT_ISOCHRONOUS 0x01 ++#define USB_ENDPOINT_BULK 0x02 ++#define USB_ENDPOINT_INTERRUPT 0x03 ++/*! @} */ ++ ++/*! ++ * @name 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 */ ++/*! @} */ ++ ++/*! * @name 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 ++/*! @} */ ++ ++/*! ++ * @name Hub requests ++ */ ++ /*! @{ */ ++#define USB_REQ_CLEAR_TT_BUFFER 0x08 ++#define USB_REQ_RESET_TT 0x09 ++#define USB_REQ_GET_TT_STATE 0x0A ++#define USB_REQ_STOP_TT 0x0B ++/*! @} */ ++ ++/*! ++ * @name 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 ++/*! @} */ ++ ++ ++/*! ++ * @name USB Spec Release number ++ */ ++ /*! @{ */ ++ ++#define USB_BCD_VERSION 0x0200 ++/*! @} */ ++ ++/*! ++ * @name Audio ++ */ ++ /*! @{ */ ++ ++#define CS_AUDIO_UNDEFINED 0x20 ++#define CS_AUDIO_DEVICE 0x21 ++#define CS_AUDIO_CONFIGURATION 0x22 ++#define CS_AUDIO_STRING 0x23 ++#define CS_AUDIO_INTERFACE 0x24 ++#define CS_AUDIO_ENDPOINT 0x25 ++ ++#define AUDIO_HEADER 0x01 ++#define AUDIO_INPUT_TERMINAL 0x02 ++#define AUDIO_OUTPUT_TERMINAL 0x03 ++#define AUDIO_MIXER_UNIT 0x04 ++#define AUDIO_SELECTOR_UNIT 0x05 ++#define AUDIO_FEATURE_UNIT 0x06 ++#define AUDIO_PROCESSING_UNIT 0x07 ++#define AUDIO_EXTENSION_UNIT 0x08 ++/*! @} */ ++ ++ ++/*! ++ * @name 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 ++/*! @} */ ++ ++/*! ++ * @name Hub Recipients ++ */ ++ /*! @{ */ ++#define USB_REQ_RECIPIENT_HUB 0x00 ++#define USB_REQ_RECIPIENT_PORT 0x03 ++/*! @} */ ++ ++/*! ++ * @name get status bits ++ */ ++ /*! @{ */ ++ ++#define USB_STATUS_SELFPOWERED 0x01 ++#define USB_STATUS_REMOTEWAKEUP 0x02 ++ ++#define USB_STATUS_HALT 0x01 ++/*! @} */ ++ ++/*! ++ * @name Convert Feature Selector to a Status Flag Setting ++ */ ++ /*! @{ */ ++#define FEATURE(f) (1 << f) ++/*! @} */ ++ ++/*! ++ * @name standard feature selectors ++ */ ++ /*! @{ */ ++#define USB_ENDPOINT_HALT 0x00 ++#define USB_DEVICE_REMOTE_WAKEUP 0x01 ++#define USB_TEST_MODE 0x02 ++/*! @} */ ++ ++/*! ++ * @name Hub Feature Selectors C.f. Table 11-17 ++ */ ++ /*! @{ */ ++#if !defined(C_HUB_LOCAL_POWER) ++#define C_HUB_LOCAL_POWER 0x0 ++#endif ++#if !defined(C_HUB_OVER_CURRENT) ++#define C_HUB_OVER_CURRENT 0x1 ++#endif ++ ++#define PORT_CONNECTION 0 ++#define PORT_ENABLE 1 ++#define PORT_SUSPEND 2 ++#define PORT_OVER_CURRENT 3 ++#define PORT_RESET 4 ++ ++#define PORT_POWER 8 ++#define PORT_LOW_SPEED 9 ++#define PORT_HIGH_SPEED 10 ++ ++#define C_PORT_CONNECTION 16 ++#define C_PORT_ENABLE 17 ++#define C_PORT_SUSPEND 18 ++#define C_PORT_OVER_CURRENT 19 ++#define C_PORT_RESET 20 ++ ++#define PORT_TEST 21 ++#define PORT_INDICATOR 22 ++ ++/*! @} */ ++ ++#ifdef PRAGMAPACK ++#pragma pack(push,1) ++#endif ++ ++ ++struct usbd_device_request { ++ u8 bmRequestType; ++ u8 bRequest; ++ u16 wValue; ++ u16 wIndex; ++ u16 wLength; ++} PACKED; ++ ++ ++ ++struct usbd_otg_descriptor { ++ u8 bLength; ++ u8 bDescriptorType; ++ u8 bmAttributes; ++} PACKED; ++ ++/*! ++ * @name OTG ++ */ ++ /*! @{ */ ++#define USB_OTG_HNP_SUPPORTED 0x02 ++#define USB_OTG_SRP_SUPPORTED 0x01 ++/*! @} */ ++ ++/*! ++ * @name OTG Feature selectors ++ */ ++ /*! @{ */ ++#define USB_OTG_B_HNP_ENABLE 0x03 ++#define USB_OTG_A_HNP_SUPPORT 0x04 ++#define USB_OTG_A_ALT_HNP_ENABLE 0x05 ++/*! @} */ ++ ++/*! ++ * @name Class-Specific Request Codes ++ * C.f. CDC Table 46 ++ */ ++ /*! @{ */ ++ ++#define CDC_CLASS_REQUEST_SEND_ENCAPSULATED 0x00 ++#define CDC_CLASS_REQUEST_GET_ENCAPSULATED 0x01 ++ ++#define CDC_CLASS_REQUEST_SET_COMM_FEATURE 0x02 ++#define CDC_CLASS_REQUEST_GET_COMM_FEATURE 0x03 ++#define CDC_CLASS_REQUEST_CLEAR_COMM_FEATURE 0x04 ++ ++#define CDC_CLASS_REQUEST_SET_LINE_CODING 0x20 ++#define CDC_CLASS_REQUEST_GET_LINE_CODING 0x21 ++ ++#define CDC_CLASS_REQUEST_SET_CONTROL_LINE_STATE 0x22 ++#define CDC_CLASS_REQUEST_SEND_BREAK 0x23 ++/*! @} */ ++ ++/*! ++ * @name Notification codes ++ * c.f. CDC Table 68 ++ */ ++ /*! @{ */ ++ ++#define CDC_NOTIFICATION_NETWORK_CONNECTION 0x00 ++#define CDC_NOTIFICATION_RESPONSE_AVAILABLE 0x01 ++#define CDC_NOTIFICATION_AUX_JACK_HOOK_STATE 0x08 ++#define CDC_NOTIFICATION_RING_DETECT 0x09 ++#define CDC_NOTIFICATION_SERIAL_STATE 0x20 ++#define CDC_NOTIFICATION_CALL_STATE_CHANGE 0x28 ++#define CDC_NOTIFICATION_LINE_STATE_CHANGE 0x29 ++#define CDC_NOTIFICATION_CONNECTION_SPEED_CHANGE 0x2a ++/*! @} */ ++ ++struct hub_descriptor { ++ u8 bDescLength; ++ u8 bDescriptorType; ++ u8 bNbrPorts; ++ u16 wHubCharacteristics; ++ u8 bPwrOn2PwrGood; ++ u8 bHubContrCurrent; ++ u8 DeviceRemovable; ++ u8 PortPwrCtrlMask; ++} PACKED; ++ ++/*! ++ * @name Hub Descriptor C.f. 11.23.2.1 Table 11-13 ++ * N.B. This assumes 1-8 ports, DeviceRemovable and PortPwrCtrlMask ++ * must be sized for the number of if ports > 8. ++ */ ++ /*! @{ */ ++ ++ ++#define HUB_GANGED_POWER 0x00 ++#define HUB_INDIVIDUAL_POWER 0x01 ++#define HUB_COMPOUND_DEVICE 0x04 ++#define HUB_GLOBAL_OVERCURRENT 0x00 ++#define HUB_INDIVIDUAL_OVERCURRENT 0x08 ++#define HUB_NO_OVERCURRENT 0x10 ++#define HUB_TT_8 0x00 ++#define HUB_TT_16 0x20 ++#define HUB_TT_24 0x40 ++#define HUB_TT_32 0x60 ++#define HUB_INDICATORS_SUPPORTED 0x80 ++/*! @} */ ++ ++/*! ++ * @name HID - Class Descriptors ++ * C.f. 7.1.1 ++ */ ++ /*! @{ */ ++ ++#define HID 0x21 ++#define HID_REPORT 0x22 ++#define HID_PHYSICAL 0x23 ++/*! @} */ ++ ++/*! ++ * name HID Descriptor ++ * C.f. E.8 ++ */ ++ /*! @{ */ ++ ++ ++struct hid_descriptor { ++ u8 bLength; ++ u8 bDescriptorType; ++ u16 bcdHID; ++ u8 bCountryCode; ++ u8 bNumDescriptors; ++ u8 bReportType; ++ u16 wItemLength; ++} PACKED; ++ ++/*! @} */ ++ ++/*! ++ * name ACM - Line Coding structure ++ * C.f. CDC Table 50 ++ */ ++ /*! @{ */ ++ ++ ++struct cdc_acm_line_coding { ++ u32 dwDTERate; ++ u8 bCharFormat; ++ u8 bParityType; ++ u8 bDataBits; ++} PACKED; ++ ++ /*!@} */ ++ ++/*! ++ * @name ACM - Line State ++ * C.f. CDC Table 51 ++ */ ++ /*! @{ */ ++typedef u16 cdc_acm_bmLineState; ++#define CDC_LINESTATE_D1_RTS (1 << 1) ++#define CDC_LINESTATE_D0_DTR (1 << 0) ++/*! @} */ ++ ++/*! ++ * Serial State - C.f. 6.3.5 ++ */ ++struct acm_serial_state { ++ u16 uart_state_bitmap; ++}; ++ ++ ++/*! @name ACM - UART State ++ * C.f. Table 69 - UART State Bitmap Values ++ */ ++/*! @{ */ ++typedef u16 cdc_acm_bmUARTState; ++#define CDC_UARTSTATE_BOVERRUN (1 << 6) ++#define CDC_UARTSTATE_BPARITY (1 << 5) ++#define CDC_UARTSTATE_BFRAMING (1 << 4) ++#define CDC_UARTSTATE_BRINGSIGNAL (1 << 3) ++#define CDC_UARTSTATE_BBREAK (1 << 2) ++#define CDC_UARTSTATE_BTXCARRIER_DSR (1 << 1) ++#define CDC_UARTSTATE_BRXCARRIER_DCD (1 << 0) ++/*! @} */ ++ ++/* !USB Notification ++ * ++ */ ++ ++struct cdc_notification_descriptor { ++ u8 bmRequestType; ++ u8 bNotification; ++ u16 wValue; ++ u16 wIndex; ++ u16 wLength; ++ u8 data[2]; ++} PACKED; ++ ++ ++ ++ ++/*! ++ * @name 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 ++/*! @} */ ++ ++/*! @name c.f. CDC 4.1 Table 14 */ ++ ++/*! @{ */ ++ ++#define AUDIO_CLASS 0x01 ++#define COMMUNICATIONS_DEVICE_CLASS 0x02 ++/*! @} */ ++ ++/*! @name c.f. CDC 4.2 Table 15 ++ */ ++ /*! @{ */ ++#define COMMUNICATIONS_INTERFACE_CLASS 0x02 ++/*! @} */ ++ ++/*! @name 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 ++ ++#define AUDIO_CONTROL_SUBCLASS 0x01 ++#define AUDIO_STREAMING_SUBCLASS 0x02 ++/*! @} */ ++ ++/*! @name 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 ++/*! @} */ ++ ++/*! @name c.f. CDC 4.6 Table 18 ++ */ ++ /*! @{ */ ++#define DATA_INTERFACE_CLASS 0x0a ++/*! @} */ ++ ++/*! @name c.f. CDC 4.7 Table 19 ++ */ ++ /*! @{ */ ++#define COMMUNICATIONS_NO_PROTOCOL 0x00 ++/*! @} */ ++ ++ ++/*! @name c.f. CDC 5.2.3 Table 24 ++ * c.f. Audio Appendix A.4 ++ */ ++ /*! @{ */ ++#define CS_UNDEFINED 0x20 ++#define CS_DEVICE 0x21 ++#define CS_CONFIG 0x22 ++#define CS_STRING 0x23 ++#define CS_INTERFACE 0x24 ++#define CS_ENDPOINT 0x25 ++/*! @} */ ++ ++/*! ++ * @name Audio Interface Class - c.f Appendix A.1 ++ */ ++ /*! @{ */ ++#define AUDIO_INTERFACE_CLASS 0x01 ++/*! @} */ ++ ++/*! @name Audio Interface Subclass - c.f Appendix A.2 ++ */ ++ /*! @{ */ ++#define AUDIO_SUBCLASS_UNDEFINED 0x00 ++#define AUDIO_AUDIOCONTROL 0x01 ++#define AUDIO_AUDIOSTREAMING 0x02 ++#define AUDIO_MIDISTREAMING 0x03 ++/*! @} */ ++ ++/*! @name Audio Interface Proctol - c.f. Appendix A.3 ++ */ ++ /*! @{ */ ++#define AUDIO_PR_PROTOCOL_UNDEFINED 0x00 ++/*! @} */ ++ ++/*! @name Audio Class-Specific AC Interface Descriptor Subtypes - c.f. A.5 ++ */ ++ /*! @{ */ ++#define AUDIO_AC_DESCRIPTOR_UNDEFINED 0x00 ++#define AUDIO_AC_HEADER 0x01 ++#define AUDIO_AC_INPUT_TERMINAL 0x02 ++#define AUDIO_AC_OUTPUT_TERMINAL 0x03 ++#define AUDIO_AC_MIXER_UNIT 0x04 ++#define AUDIO_AC_SELECTOR_UNIT 0x05 ++#define AUDIO_AC_FEATURE_UNIT 0x06 ++#define AUDIO_AC_PROCESSING_UNIT 0x07 ++#define AUDIO_AC_EXTENSION_UNIT 0x08 ++/*! @} */ ++ ++/*! @name Audio Class-Specific AS Interface Descriptor Subtypes - c.f. A.6 ++ */ ++ /*! @{ */ ++#define AUDIO_AS_DESCRIPTOR_UNDEFINED 0x00 ++#define AUDIO_AS_GENERAL 0x01 ++#define AUDIO_AS_FORMAT_TYPE 0x02 ++#define AUDIO_AS_FORMAT_UNSPECFIC 0x03 ++/*! @} */ ++ ++/*! @name Audio Class Processing Unit Processing Types - c.f. A.7 ++ */ ++ /*! @{ */ ++#define AUDIO_PROCESS_UNDEFINED 0x00 ++#define AUDIO_UP_DOWN_MUIX_PROCESS 0x01 ++#define AUDIO_DOLBY_PROLOGIC_PROCESS 0x02 ++#define AUDIO_3D_STEREO_EXTENDER_PROCESS 0x03 ++#define AUDIO_REVERBERATION_PROCESS 0x04 ++#define AUDIO_CHORUS_PROCESS 0x05 ++#define AUDIO_DYN_RANGE_COMP_PROCESS 0x06 ++/*! @} */ ++ ++/*! @name Audio Class-Specific Endpoint Descriptor Subtypes - c.f. A.8 ++ */ ++ /*! @{ */ ++#define AUDIO_DESCRIPTOR_UNDEFINED 0x00 ++#define AUDIO_EP_GENERAL 0x01 ++/*! @} */ ++ ++/*! @name Audio Class-Specific Request Codes - c.f. A.9 ++ */ ++ /*! @{ */ ++#define AUDIO_REQUEST_CODE_UNDEFINED 0x00 ++#define AUDIO_SET_CUR 0x01 ++#define AUDIO_GET_CUR 0x81 ++#define AUDIO_SET_MIN 0x02 ++#define AUDIO_GET_MIN 0x82 ++#define AUDIO_SET_MAX 0x03 ++#define AUDIO_GET_MAX 0x83 ++#define AUDIO_SET_RES 0x04 ++#define AUDIO_GET_RES 0x84 ++#define AUDIO_SET_MEM 0x05 ++#define AUDIO_GET_MEM 0x85 ++#define AUDIO_GET_STAT 0xff ++/*! @} */ ++ ++/*! ++ * @name 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 ++/*! @} */ ++ ++/*! ++ * @name Class Descriptor Description Structures... ++ * c.f. CDC 5.1 ++ * c.f. WCMC 6.7.2 ++ * ++ * XXX add the other dozen class descriptor description structures.... ++ * ++ */ ++ /*! @{ */ ++struct usbd_header_functional_descriptor { ++ u8 bFunctionLength; ++ u8 bDescriptorType; ++ u8 bDescriptorSubtype; ++ u16 bcdCDC; ++}; ++ ++struct usbd_call_management_functional_descriptor { ++ u8 bFunctionLength; ++ u8 bDescriptorType; ++ u8 bDescriptorSubtype; ++ u8 bmCapabilities; ++ u8 bDataInterface; ++}; ++ ++struct usbd_abstract_control_functional_descriptor { ++ u8 bFunctionLength; ++ u8 bDescriptorType; ++ u8 bDescriptorSubtype; ++ u8 bmCapabilities; ++}; ++ ++struct usbd_union_functional_functor { ++ u8 bFunctionLength; ++ u8 bDescriptorType; ++ u8 bDescriptorSubtype; ++ u8 bMasterInterface; ++ u8 bSlaveInterface[1]; ++ //u8 bSlaveInterface[0]; // XXX FIXME ++}; ++ ++struct usbd_ethernet_networking_descriptor { ++ u8 bFunctionLength; ++ u8 bDescriptorType; ++ u8 bDescriptorSubtype; ++ char *iMACAddress; ++ u8 bmEthernetStatistics; ++ u16 wMaxSegmentSize; ++ u16 wNumberMCFilters; ++ u8 bNumberPowerFilters; ++}; ++ ++struct usbd_mobile_direct_line_model_descriptor { ++ u8 bFunctionLength; ++ u8 bDescriptorType; ++ u8 bDescriptorSubtype; ++ u16 bcdVersion; ++ u8 bGUID[16]; ++}; ++ ++struct usbd_mobile_direct_line_model_detail_descriptor { ++ u8 bFunctionLength; ++ u8 bDescriptorType; ++ u8 bDescriptorSubtype; ++ u8 bGuidDescriptorType; ++ u8 bDetailData[2]; ++ //u8 bDetailData[0]; // XXX FIXME ++}; ++#if 0 ++struct usbd_header_description { ++ u8 bDescriptorSubtype; ++ u16 bcdCDC; ++}; ++ ++struct usbd_call_management_description { ++ u8 bmCapabilities; ++ u8 bDataInterface; ++}; ++ ++struct usbd_abstract_control_description { ++ u8 bmCapabilities; ++}; ++ ++struct usbd_union_function_description { ++ u8 bMasterInterface; ++ u8 bSlaveInterface[1]; ++ //u8 bSlaveInterface[0]; // XXX FIXME ++}; ++ ++struct usbd_ethernet_networking_description { ++ char *iMACAddress; ++ u8 bmEthernetStatistics; ++ u16 wMaxSegmentSize; ++ u16 wNumberMCFilters; ++ u8 bNumberPowerFilters; ++}; ++ ++struct usbd_mobile_direct_line_model_description { ++ u16 bcdVersion; ++ u8 bGUID[16]; ++}; ++ ++struct usbd_mobile_direct_line_model_detail_description { ++ u8 bGuidDescriptorType; ++ u8 bDetailData[2]; ++ //u8 bDetailData[0]; // XXX FIXME ++}; ++ ++struct usbd_class_description { ++ u8 bDescriptorSubtype; ++ u8 elements; ++ union { ++ struct usbd_header_description header; ++ struct usbd_call_management_description call_management; ++ struct usbd_abstract_control_description abstract_control; ++ struct usbd_union_function_description union_function; ++ struct usbd_ethernet_networking_description ethernet_networking; ++ struct usbd_mobile_direct_line_model_description mobile_direct; ++ struct usbd_mobile_direct_line_model_detail_description mobile_direct_detail; ++ } description; ++}; ++#endif ++/*! @} */ ++ ++/*! ++ * @name Endpoint Modifiers ++ * static struct usbd_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 CONTROL 0x00 ++#define ISOCHRONOUS 0x01 ++#define BULK 0x02 ++#define INTERRUPT 0x03 ++/*! @} */ ++ ++ ++/*! @name configuration modifiers ++ */ ++ ++/* @{ */ ++#define BMATTRIBUTE_RESERVED 0x80 ++#define BMATTRIBUTE_SELF_POWERED 0x40 ++#define BMATTRIBUTE_REMOTE_WAKEUP 0x20 ++ ++#if 0 ++/* ++ * The UUT tester specifically tests for MaxPower to be non-zero (> 0). ++ */ ++#if !defined(CONFIG_OTG_MAXPOWER) || (CONFIG_OTG_MAXPOWER == 0) ++ #define BMATTRIBUTE BMATTRIBUTE_RESERVED | BMATTRIBUTE_SELF_POWERED ++ #define BMAXPOWER 1 ++#else ++ #define BMATTRIBUTE BMATTRIBUTE_RESERVED ++ #define BMAXPOWER CONFIG_USBD_MAXPOWER ++#endif ++#endif ++/*! @} */ ++ ++ ++ ++/*! ++ * @name Standard Usb Descriptor Structures ++ */ ++ /*! @{ */ ++ ++struct usbd_endpoint_descriptor { ++ u8 bLength; ++ u8 bDescriptorType; // 0x5 ++ u8 bEndpointAddress; ++ u8 bmAttributes; ++ u16 wMaxPacketSize; ++ u8 bInterval; ++} PACKED; ++ ++struct usbd_interface_descriptor { ++ u8 bLength; ++ u8 bDescriptorType; // 0x04 ++ u8 bInterfaceNumber; ++ u8 bAlternateSetting; ++ u8 bNumEndpoints; ++ u8 bInterfaceClass; ++ u8 bInterfaceSubClass; ++ u8 bInterfaceProtocol; ++ u8 iInterface; ++} PACKED; ++ ++struct usbd_configuration_descriptor { ++ u8 bLength; ++ u8 bDescriptorType; // 0x2 ++ u16 wTotalLength; ++ u8 bNumInterfaces; ++ u8 bConfigurationValue; ++ u8 iConfiguration; ++ u8 bmAttributes; ++ u8 bMaxPower; ++} PACKED; ++ ++struct usbd_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; ++} PACKED; ++ ++struct usbd_string_descriptor { ++ u8 bLength; ++ u8 bDescriptorType; // 0x03 ++ u16 wData[0]; ++} PACKED; ++ ++struct usbd_device_qualifier_descriptor { ++ u8 bLength; ++ u8 bDescriptorType; ++ u16 bcdUSB; ++ u8 bDeviceClass; ++ u8 bDeviceSubClass; ++ u8 bDeviceProtocol; ++ u8 bMaxPacketSize0; ++ u8 bNumConfigurations; ++ u8 bReserved; ++} PACKED; ++ ++struct usbd_generic_descriptor { ++ u8 bLength; ++ u8 bDescriptorType; ++} PACKED; ++ ++struct usbd_generic_class_descriptor { ++ u8 bLength; ++ u8 bDescriptorType; ++ u8 bDescriptorSubtype; ++} PACKED; ++/*! @} */ ++ ++/*! ++ * @name Communications Class Dscriptor Structures ++ * c.f. CDC 5.2 Table 25c ++ */ ++ ++/*! @{*/ ++ ++struct usbd_class_function_descriptor { ++ u8 bFunctionLength; ++ u8 bDescriptorType; ++ u8 bDescriptorSubtype; ++} PACKED; ++ ++struct usbd_class_function_descriptor_generic { ++ u8 bFunctionLength; ++ u8 bDescriptorType; ++ u8 bDescriptorSubtype; ++ u8 bmCapabilities; ++} PACKED; ++ ++struct usbd_class_header_function_descriptor { ++ u8 bFunctionLength; ++ u8 bDescriptorType; ++ u8 bDescriptorSubtype; // 0x00 ++ u16 bcdCDC; ++} PACKED; ++ ++struct usbd_class_call_management_descriptor { ++ u8 bFunctionLength; ++ u8 bDescriptorType; ++ u8 bDescriptorSubtype; // 0x01 ++ u8 bmCapabilities; ++ u8 bDataInterface; ++} PACKED; ++ ++struct usbd_class_abstract_control_descriptor { ++ u8 bFunctionLength; ++ u8 bDescriptorType; ++ u8 bDescriptorSubtype; // 0x02 ++ u8 bmCapabilities; ++} PACKED; ++ ++struct usbd_class_direct_line_descriptor { ++ u8 bFunctionLength; ++ u8 bDescriptorType; ++ u8 bDescriptorSubtype; // 0x03 ++} PACKED; ++ ++struct usbd_class_telephone_ringer_descriptor { ++ u8 bFunctionLength; ++ u8 bDescriptorType; ++ u8 bDescriptorSubtype; // 0x04 ++ u8 bRingerVolSeps; ++ u8 bNumRingerPatterns; ++} PACKED; ++ ++struct usbd_class_telephone_call_descriptor { ++ u8 bFunctionLength; ++ u8 bDescriptorType; ++ u8 bDescriptorSubtype; // 0x05 ++ u8 bmCapabilities; ++} PACKED; ++ ++struct usbd_class_union_function_descriptor { ++ u8 bFunctionLength; ++ u8 bDescriptorType; ++ u8 bDescriptorSubtype; // 0x06 ++ u8 bMasterInterface; ++ u8 bSlaveInterface0[0]; ++} PACKED; ++ ++struct usbd_class_country_selection_descriptor { ++ u8 bFunctionLength; ++ u8 bDescriptorType; ++ u8 bDescriptorSubtype; // 0x07 ++ u8 iCountryCodeRelDate; ++ u16 wCountryCode0[0]; ++} PACKED; ++ ++ ++struct usbd_class_telephone_operational_descriptor { ++ u8 bFunctionLength; ++ u8 bDescriptorType; ++ u8 bDescriptorSubtype; // 0x08 ++ u8 bmCapabilities; ++} PACKED; ++ ++ ++struct usbd_class_usbd_terminal_descriptor { ++ u8 bFunctionLength; ++ u8 bDescriptorType; ++ u8 bDescriptorSubtype; // 0x09 ++ u8 bEntityId; ++ u8 bInterfaceNo; ++ u8 bOutInterfaceNo; ++ u8 bmOptions; ++ u8 bChild0[0]; ++} PACKED; ++ ++struct usbd_class_network_channel_descriptor { ++ u8 bFunctionLength; ++ u8 bDescriptorType; ++ u8 bDescriptorSubtype; // 0x0a ++ u8 bEntityId; ++ u8 iName; ++ u8 bChannelIndex; ++ u8 bPhysicalInterface; ++} PACKED; ++ ++struct usbd_class_protocol_unit_function_descriptor { ++ u8 bFunctionLength; ++ u8 bDescriptorType; ++ u8 bDescriptorSubtype; // 0x0b ++ u8 bEntityId; ++ u8 bProtocol; ++ u8 bChild0[0]; ++} PACKED; ++ ++struct usbd_class_extension_unit_descriptor { ++ u8 bFunctionLength; ++ u8 bDescriptorType; ++ u8 bDescriptorSubtype; // 0x0c ++ u8 bEntityId; ++ u8 bExtensionCode; ++ u8 iName; ++ u8 bChild0[0]; ++} PACKED; ++ ++struct usbd_class_multi_channel_descriptor { ++ u8 bFunctionLength; ++ u8 bDescriptorType; ++ u8 bDescriptorSubtype; // 0x0d ++ u8 bmCapabilities; ++} PACKED; ++ ++struct usbd_class_capi_control_descriptor { ++ u8 bFunctionLength; ++ u8 bDescriptorType; ++ u8 bDescriptorSubtype; // 0x0e ++ u8 bmCapabilities; ++} PACKED; ++ ++struct usbd_class_ethernet_networking_descriptor { ++ u8 bFunctionLength; ++ u8 bDescriptorType; ++ u8 bDescriptorSubtype; // 0x0f ++ u8 iMACAddress; ++ u32 bmEthernetStatistics; ++ u16 wMaxSegmentSize; ++ u16 wNumberMCFilters; ++ u8 bNumberPowerFilters; ++} PACKED; ++ ++struct usbd_class_atm_networking_descriptor { ++ u8 bFunctionLength; ++ u8 bDescriptorType; ++ u8 bDescriptorSubtype; // 0x10 ++ u8 iEndSystermIdentifier; ++ u8 bmDataCapabilities; ++ u8 bmATMDeviceStatistics; ++ u16 wType2MaxSegmentSize; ++ u16 wType3MaxSegmentSize; ++ u16 wMaxVC; ++} PACKED; ++ ++ ++struct usbd_class_mdlm_descriptor { ++ u8 bFunctionLength; ++ u8 bDescriptorType; ++ u8 bDescriptorSubtype; // 0x12 ++ u16 bcdVersion; ++ u8 bGUID[16]; ++} PACKED; ++ ++/* ++struct usbd_class_mdlmd_descriptor { ++ u8 bFunctionLength; ++ u8 bDescriptorType; ++ u8 bDescriptorSubtype; // 0x13 ++ u8 bGuidDescriptorType; ++ u8 bDetailData[0]; ++} PACKED; ++*/ ++ ++struct usbd_class_blan_descriptor { ++ u8 bFunctionLength; // 0x7 ++ u8 bDescriptorType; ++ u8 bDescriptorSubtype; // 0x13 ++ u8 bGuidDescriptorType; ++ u8 bmNetworkCapabilities; ++ u8 bmDataCapabilities; ++ u8 bPad; ++} PACKED; ++ ++struct usbd_class_safe_descriptor { ++ u8 bFunctionLength; // 0x6 ++ u8 bDescriptorType; ++ u8 bDescriptorSubtype; // 0x13 ++ u8 bGuidDescriptorType; ++ u8 bmNetworkCapabilities; ++ u8 bmDataCapabilities; ++} PACKED; ++ ++/*@}*/ ++ ++ ++/*! ++ * @name Descriptor Union Structures ++ */ ++ /*! @{ */ ++ ++struct usbd_descriptor { ++ union { ++ struct usbd_generic_descriptor generic_descriptor; ++ struct usbd_generic_class_descriptor generic_class_descriptor; ++ struct usbd_endpoint_descriptor endpoint_descriptor; ++ struct usbd_interface_descriptor interface_descriptor; ++ struct usbd_configuration_descriptor configuration_descriptor; ++ struct usbd_device_descriptor device_descriptor; ++ struct usbd_string_descriptor string_descriptor; ++ } descriptor; ++ ++} PACKED; ++ ++struct usbd_class_descriptor { ++ union { ++ struct usbd_class_function_descriptor function; ++ struct usbd_class_function_descriptor_generic generic; ++ struct usbd_class_header_function_descriptor header_function; ++ struct usbd_class_call_management_descriptor call_management; ++ struct usbd_class_abstract_control_descriptor abstract_control; ++ struct usbd_class_direct_line_descriptor direct_line; ++ struct usbd_class_telephone_ringer_descriptor telephone_ringer; ++ struct usbd_class_telephone_operational_descriptor telephone_operational; ++ struct usbd_class_telephone_call_descriptor telephone_call; ++ struct usbd_class_union_function_descriptor union_function; ++ struct usbd_class_country_selection_descriptor country_selection; ++ struct usbd_class_usbd_terminal_descriptor usb_terminal; ++ struct usbd_class_network_channel_descriptor network_channel; ++ struct usbd_class_extension_unit_descriptor extension_unit; ++ struct usbd_class_multi_channel_descriptor multi_channel; ++ struct usbd_class_capi_control_descriptor capi_control; ++ struct usbd_class_ethernet_networking_descriptor ethernet_networking; ++ struct usbd_class_atm_networking_descriptor atm_networking; ++ struct usbd_class_mdlm_descriptor mobile_direct; ++ //struct usbd_class_mdlmd_descriptor mobile_direct_detail; ++ struct usbd_class_blan_descriptor mobile_direct_blan_detail; ++ struct usbd_class_safe_descriptor mobile_direct_safe_detail; ++ } descriptor; ++ ++} PACKED; ++/*! @} */ ++ ++/*! ++ * @name Audio Status Word Format - c.f. Table 3.1 ++ */ ++ /*! @{ */ ++ ++#define AUDIO_STATUS_INTERRUPT_PENDING 1<<7 ++#define AUDIO_STATUS_MEMORY_CONTENT_CHANGED 1<<6 ++#define AUDIO_STATUS_ORIGINATOR_AUDIO_CONTROL_INTERFACE 0 ++#define AUDIO_STATUS_ORIGINATOR_AUDIO_STREAMING_INTERFACE 1 ++#define AUDIO_STATUS_ORIGINATOR_AUDIO_STREAMING_ENDPOINT 2 ++#define AUDIO_STATUS_ORIGINATOR_AUDIOCONTROL_INTERFACE 0 ++ ++struct usbd_audio_status_word { ++ u8 bStatusType; ++ u8 bOriginator; ++} PACKED; ++ ++struct usbd_audio_ac_interface_header_descriptor { ++ u8 bFunctionLength; ++ u8 bDescriptorType; ++ u8 bDescriptorSubtype; ++ u16 bcdADC; ++ u16 wTotalLength; ++ u8 binCollection; ++ u8 bainterfaceNr[0]; ++} PACKED; ++ ++struct usbd_audio_input_terminal_descriptor { ++ u8 bFunctionLength; ++ u8 bDescriptorType; ++ u8 bDescriptorSubtype; ++ u8 bTerminalID; ++ u16 wTerminalType; ++ u8 bAssocTerminal; ++ u8 bNrChannels; ++ u16 wChannelConfig; ++ u8 iChannelNames; ++ u8 iTerminal; ++} PACKED; ++ ++struct usbd_audio_input_terminal_description { ++ struct usbd_audio_input_terminal_descriptor *audio_input_terminal_descriptor; ++ u16 wTerminalType; ++ u16 wChannelConfig; ++ char * iChannelNames; ++ char * iTerminal; ++}; ++ ++struct usbd_audio_output_terminal_descriptor { ++ u8 bFunctionLength; ++ u8 bDescriptorType; ++ u8 bDescriptorSubtype; ++ u8 bTerminalID; ++ u16 wTerminalType; ++ u8 bAssocTerminal; ++ u8 bSourceID; ++ u8 iTerminal; ++} PACKED; ++ ++struct usbd_audio_output_terminal_description { ++ struct usbd_audio_output_terminal_descriptor *audio_output_terminal_descriptor; ++ u16 wTerminalType; ++ char * iTerminal; ++}; ++ ++struct usbd_audio_mixer_unit_descriptor_a { ++ u8 bFunctionLength; ++ u8 bDescriptorType; ++ u8 bDescriptorSubtype; ++ u8 bUniteID; ++ u8 bNrinPins; ++} PACKED; ++ ++struct usbd_audio_mixer_unit_descriptor_b { ++ u8 baSourceID; ++ u8 bNrChannels; ++ u16 wChannelConfig; ++ u8 iChannelNames; ++ u8 bmControls; ++ u8 iMixer; ++} PACKED; ++ ++struct usbd_audio_mixer_unit_description { ++ struct usbd_audio_mixer_unit_descriptor *audio_mixer_unit_descriptor; ++ u16 wChannelConfig; ++ char * iMixer; ++}; ++ ++ ++struct usbd_audio_as_general_interface_descriptor { ++ u8 bLength; ++ u8 bDescriptorType; ++ u8 bDescriptorSubtype; ++ u8 bTerminalLink; ++ u8 bDelay; ++ u16 wFormatTag; ++} PACKED; ++ ++struct usbd_audio_as_general_interface_description { ++ struct usbd_audio_as_general_interface_descriptor *audio_as_general_interface_descriptor; ++ u16 wFormatTag; ++}; ++ ++struct usbd_audio_as_format_type_descriptor { ++ u8 bLength; ++ u8 bDescriptorType; ++ u8 bDescriptorSubtype; ++ u8 bFormatType; ++ u8 bNrChannels; ++ u8 bSubFrameSize; ++ u8 bBitResolution; ++ u8 bSamFreqType; ++ u8 iSamFreq[3]; ++} PACKED; ++ ++struct usbd_audio_as_format_type_description { ++ struct usbd_audio_as_format_type_descriptor *audio_as_format_type_descriptor; ++ u16 wFormatTag; ++}; ++ ++ ++ ++ ++struct usbd_audio_descriptor { ++ union { ++ struct usbd_audio_ac_interface_header_descriptor header; ++ struct usbd_audio_input_terminal_descriptor input; ++ struct usbd_audio_output_terminal_descriptor output; ++ } descriptor; ++} PACKED; ++ ++ ++ ++struct usbd_ac_endpoint_descriptor { ++ u8 bLength; ++ u8 bDescriptorType; ++ u8 bEndpointAddress; ++ u8 bmAttributes; ++ u16 wMaxPacketSize; ++ u8 bInterval; ++ u8 bRefresh; ++ u8 bSynchAddress; ++} PACKED; ++ ++ ++struct usbd_as_iso_endpoint_descriptor { ++ u8 bLength; ++ u8 bDescriptorType; ++ u8 bDescriptorSubtype; ++ u8 bmAttributes; ++ u8 bLockDelayUnits; ++ u16 wLockDelay; ++} PACKED; ++/*! @}*/ ++ ++ ++/*! ++ * @name MCPC ++ */ ++ /*! @{ */ ++ ++/*! struct usbd_interface_association ++ * Used in AB-1 or DL-1 interface bundles. ++ */ ++struct usbd_interface_association_descriptor { ++ u8 bLength; ++ u8 bDescriptorType; ++ u8 bFirstInterface; ++ u8 bInterfaceCount; ++ u8 bFunctionClass; ++ u8 bFunctionSubClass; ++ u8 bFunctionProcotol; ++ u8 iFunction; ++} PACKED; ++ ++struct usbd_mobile_abstract_control_model_specific_desciptor { ++ u8 bFunctionLength; ++ u8 bDescriptorType; ++ u8 bDescriptorSubType; ++ u8 bType; ++ u8 bMode_1; ++}; ++ ++struct usbd_mobile_direct_line_model_specific_desciptor { ++ u8 bFunctionLength; ++ u8 bDescriptorType; ++ u8 bDescriptorSubType; ++ u8 bType; ++ u8 bMode_N[2]; ++}; ++ ++ ++/*! @} */ ++ ++/*! ++ * 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 usbd_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_CLOSE, // bi - device instance should be destroyed ++ ++} usbd_device_event_t; ++ ++/*! USB Request Block structure ++ * ++ * 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. ++ * ++ * Note that for OUT urbs the buffer is always allocated to a multiple of ++ * the packetsize that is 1 larger than the requested size. This prevents ++ * overflow if the host unexpectedly sends a full sized packet when we are ++ * expecting a short one (the host is always right..) ++ * ++ * USBD_URB_SENDZLP - set this flag if a ZLP (zero length packet) should be ++ * sent after the data is sent. This is required if sending data that is a ++ * multiple of the packetsize but less than the maximum transfer size for ++ * the protocol in use. A ZLP is required to ensure that the host ++ * recognizes a short transfer. ++ * ++ */ ++ ++typedef struct urb_link { ++ struct urb_link *next; ++ struct urb_link *prev; ++} urb_link; ++ ++ ++/*! URB Status ++ * ++ * This defines the current status of a pending or finshed URB. ++ * ++ */ ++typedef enum usbd_urb_status { ++ SEND_IN_QUEUE, ++ SEND_IN_PROGRESS, ++ SEND_FINISHED_OK, ++ SEND_FINISHED_ERROR, ++ SEND_CANCELLED, ++ SEND_STALLED, ++ SEND_DISABLED, ++ RECV_IN_QUEUE, ++ RECV_IN_PROGRESS, ++ RECV_OK, ++ RECV_ERROR, ++ RECV_CANCELLED, ++ RECV_STALLED, ++ RECV_DISABLED ++} usbd_urb_status_t; ++ ++#define USBD_URB_SENDZLP 0x01 ++ ++struct usbd_urb { ++ ++ struct usbd_bus_instance *bus; ++ struct usbd_function_instance *function_instance; ++ struct usbd_endpoint_instance *endpoint; ++ ++ int (*callback) (struct usbd_urb *, int); ++ ++ u8 *buffer; // data received (OUT) or being sent (IN) ++ u32 request_length; // maximum data expected for OUT ++ u32 buffer_length; // allocated size of buffer ++ u32 actual_length; // actual data received (OUT or being sent (IN) ++ u32 flags; ++ ++ void *function_privdata; ++ void *bus_privdata; ++ void *pcimap; ++ ++ struct urb_link link; ++ usbd_urb_status_t status; // what is the current status of the urb ++ time_t jiffies; // timestamp for when urb was started or finished ++ u16 framenum; // SOF framenum when urb was finished ++}; ++ ++/*! Endpoint Request ++ * ++ * An array of these structures is initialized by each function driver to specify the endpoints ++ * it requires. ++ * ++ * The bus interface driver will attempt to fulfill these requests with the actual endpoints it ++ * has available. ++ * ++ * Note that in most cases the bEndpointAddress should be left as zero except for specialized ++ * function drivers that both require a specific value and know ahead of time that the specified ++ * value is legal for the bus interface driver being used. ++ */ ++struct usbd_endpoint_request { ++ u8 configuration; /* configuration endpoint will be in */ ++ u8 interface_num; /* interface endpoint will be in */ ++ u8 alternate; /* altsetting for this request */ ++ u8 bmAttributes; /* endpoint type AND direction */ ++ u16 fs_requestedTransferSize; /* max full speed transfer size for this endpoint */ ++ u16 hs_requestedTransferSize; /* max high speed transfer size for this endpoint */ ++ u8 bEndpointAddress; /* specific bEndpointAddress function driver requires */ ++ u8 physical; /* physical endpoint used */ ++}; ++ ++/*! ++ * Device status ++ * ++ * Overall state, we use this to show when we are suspended. ++ * This is required because when resumed we need to go back ++ * to previous state. ++ */ ++typedef enum usbd_device_status { ++ USBD_OPENING, // we are currently opening ++ USBD_RESETING, // we are currently opening ++ USBD_OK, // ok to use ++ USBD_SUSPENDED, // we are currently suspended ++ USBD_CLOSING, // we are currently closing ++ USBD_CLOSED, // we are currently closing ++ USBD_UNKNOWN, ++} usbd_device_status_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 usbd_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_SUSPENDED, // device has been suspended ++ STATE_UNKNOWN, // destroyed ++} usbd_device_state_t; ++ ++ ++ ++#ifdef PRAGMAPACK ++#pragma pack(pop) ++#endif ++ ++ +diff -uNr linux/drivers/no-otg/otg/usbp-func.h linux/drivers/otg/otg/usbp-func.h +--- linux/drivers/no-otg/otg/usbp-func.h 1970-01-01 01:00:00.000000000 +0100 ++++ linux/drivers/otg/otg/usbp-func.h 2006-09-01 21:41:33.000000000 +0200 +@@ -0,0 +1,428 @@ ++/* ++ * otg/otg/usbp-func.h - USB Device Function Driver Interface ++ * ++ * Copyright (c) 2004 Belcarra ++ * ++ * By: ++ * Stuart Lynne <sl@belcarra.com>, ++ * Tom Rushworth <tbr@belcarra.com>, ++ * Bruce Balden <balden@belcarra.com> ++ * ++ * ++ */ ++ ++/*! ++ * @file otg/otg/usbp-func.h ++ * @brief Function Driver related structures and definitions. ++ * ++ * This file contains the USB Function Driver and USB Interface Driver ++ * definitions. ++ * ++ * The USBOTG support allows for implementation of composite devices. ++ * ++ * From USB 2.0, C.f. 5.2.3: ++ * ++ * A Device that has multiple interfaces controlled independantly of ++ * each other is referred to as a composite device. A composite device ++ * has only a single device. ++ * ++ * In this implemenation the function portion of the device is split into ++ * two types of drivers: ++ * ++ * - USB Interface Driver ++ * - USB Function Driver ++ * ++ * ++ * USB Interface Driver ++ * ++ * An USB Interface Driver specifies the Interface related descriptors and ++ * implements the data handling processes for each of the data endpoints ++ * associated with the interface (or interfaces) required for the function. ++ * It typically will also implement the upper edge interface to the OS. ++ * ++ * The USB Interface Driver will also handle non-standard device requests ++ * and feature requests that are for the interface or an endpoint associated ++ * with one of the interfaces associated with the driver. ++ * ++ * Each interface implemented by an USB Interface Driver will have a ++ * interface instance that stores a pointer to the parent USB Function ++ * Driver and has an array of pointers to endpoint instances for the data ++ * endpoints associated with the interface. ++ * ++ * ++ * USB Function Driver ++ * ++ * A USB Function Driver specifies the device and configuration descriptors. ++ * A configuration descriptor is assembled from the interface descriptors ++ * from the USB Interface Driver (or drivers) that it is making available to ++ * the USB Host. ++ * ++ * The USB Function Driver handles non-standard device requests and feature ++ * requests for the device. ++ * ++ * Each function driver has a function instance that maintains an array ++ * of pointers to configuration instances and a pointer to the device ++ * descriptor. ++ * ++ * Each configuration instance maintains an array of pointers to the ++ * interface instances for that configuration and a pointer to the ++ * complete configuration descriptor. ++ * ++ * @ingroup USBP ++ */ ++ ++/*! ++ * This file contains the USB Device Core Common definitions. It also contains definitions for ++ * functions that are commonly used by both Function Drivers and Bus Interface Drivers. ++ * ++ * Specifically: ++ * ++ * o public functions exported by the USB Core layer ++ * ++ * o common structures and definitions ++ * ++ */ ++ ++ ++ ++struct usbd_bus_instance; ++struct usbd_endpoint_instance; ++struct usbd_function_instance; ++ ++/*! ++ * USB Function Driver types ++ */ ++typedef enum usbd_function_types { ++ function_simple, ++ function_interface, ++ function_composite ++} usbd_function_types_t; ++ ++ ++/*! ++ * USB Function Driver structures ++ * ++ * Descriptors: ++ * struct usbd_endpoint_description ++ * struct usbd_interface_description ++ * struct usbd_configuration_description ++ * ++ * Driver description: ++ * struct usbd_function_driver ++ * struct usbd_function_operations ++ * ++ */ ++ ++struct usbd_function_operations { ++ ++ int (*function_enable) (struct usbd_function_instance *); ++ void (*function_disable) (struct usbd_function_instance *); ++ ++ /* ++ * All of the following may be called from an interrupt context ++ */ ++ void (*event_handler) (struct usbd_function_instance *, usbd_device_event_t, int); ++ int (*device_request) (struct usbd_function_instance *, struct usbd_device_request *); ++ int (*set_configuration) (struct usbd_function_instance *, int configuration); ++ ++ int (*set_interface) (struct usbd_function_instance *, int interface, int altsetting); ++ ++ void * (*get_descriptor)(struct usbd_function_instance *, int descriptor_type, int descriptor_index); ++ int * (*set_descriptor)(struct usbd_function_instance *, int descriptor_type, int descriptor_index, void *); ++ ++ void (*endpoint_cleared)(struct usbd_function_instance*, int wIndex, int); ++ ++}; ++ ++ ++/*! ++ * function driver definitions ++ */ ++struct usbd_alternate_instance { ++ struct usbd_interface_descriptor *interface_descriptor; ++ int classes; ++ struct usbd_generic_class_descriptor **class_list; ++ int endpoints; ++ struct usbd_endpoint_descriptor **endpoint_list; ++ u8 *endpoint_indexes; ++}; ++ ++ ++/*! ++ * usb device description structures ++ */ ++ ++struct usbd_alternate_description { ++ struct usbd_interface_descriptor *interface_descriptor; ++ char *iInterface; ++ ++ // list of CDC class descriptions for this alternate interface ++ u8 classes; ++ struct usbd_generic_class_descriptor **class_list; ++ ++ // list of endpoint descriptions for this alternate interface ++ u8 endpoints; ++ struct usbd_endpoint_descriptor **endpoint_list; ++ ++ // list of indexes into endpoint request map for each endpoint descriptor ++ u8 *endpoint_indexes; ++}; ++ ++typedef struct usbd_generic_class_descriptor usbd_class_descriptor_t; ++typedef struct usbd_endpoint_descriptor usbd_endpoint_descriptor_t; ++ ++struct usbd_interface_description { ++ // list of alternate interface descriptions for this interface ++ u8 alternates; ++ struct usbd_alternate_description *alternate_list; ++}; ++ ++struct usbd_configuration_description { ++ struct usbd_configuration_descriptor *configuration_descriptor; ++ char *iConfiguration; ++ // list of interface descriptons for this configuration ++ //u8 bNumInterfaces; ++ //struct usbd_interface_description *interface_list; ++ int configuration_type; ++}; ++ ++struct usbd_device_description { ++ struct usbd_device_descriptor *device_descriptor; ++ #ifdef CONFIG_OTG_HIGH_SPEED ++ struct usbd_device_qualifier_descriptor *device_qualifier_descriptor; ++ #endif /* CONFIG_OTG_HIGH_SPEED */ ++ struct usbd_otg_descriptor *otg_descriptor; ++ u8 *iManufacturer; ++ u8 *iProduct; ++ u8 *iSerialNumber; ++ //u8 endpointsRequested; ++ //struct usbd_endpoint_request *requestedEndpoints; ++}; ++ ++ ++struct usbd_interfaces_instance { ++ u8 alternates; ++ struct usbd_alternate_instance *alternates_instance_array; ++}; ++ ++struct usbd_configuration_instance { ++ int bNumInterfaces; ++ struct usbd_configuration_descriptor *configuration_descriptor; ++ struct usbd_interfaces_instance *interfaces_instance_array; ++ struct usbd_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 usbd_function_driver { ++ const char *name; ++ struct usbd_function_operations *fops; // functions ++ ++ // device & configuration descriptions ++ struct usbd_device_description *device_description; ++ struct usbd_configuration_description *configuration_description; ++ int bNumConfigurations; ++ ++ u16 idVendor; ++ u16 idProduct; ++ u16 bcdDevice; ++ ++ u8 bNumInterfaces; ++ struct usbd_interface_description *interface_list; ++ ++ u8 endpointsRequested; ++ struct usbd_endpoint_request *requestedEndpoints; ++ ++ // constructed descriptors ++ struct usbd_device_descriptor *device_descriptor; ++#ifdef CONFIG_OTG_HIGH_SPEED ++ struct usbd_device_qualifier_descriptor *device_qualifier_descriptor; ++#endif /* CONFIG_OTG_HIGH_SPEED */ ++ struct usbd_otg_descriptor *otg_descriptor; ++ struct usbd_configuration_instance *configuration_instance_array; ++}; ++ ++/*! Function configuration structure ++ * ++ * This is allocated for each configured instance of a function driver. ++ * ++ * It stores pointers to the usbd_function_driver for the appropriate function, ++ * and pointers to the USB HOST requested usbd_configuration_description and ++ * usbd_interface_description. ++ * ++ * The privdata pointer may be used by the function driver to store private ++ * per instance state information. ++ * ++ * The endpoint map will contain a list of all endpoints for the configuration ++ * driver, and only related endpoints for an interface driver. ++ * ++ * The interface driver array will be NULL for an interface driver. ++ */ ++struct usbd_function_instance { ++ char * name; ++ LIST_NODE instances; // linked list (was drivers in usbd_function_driver) ++ struct usbd_bus_instance *bus; ++ struct usbd_function_driver *function_driver; ++ void *privdata; // private data for the function ++ ++ usbd_function_types_t function_type; ++ ++ int configuration; // configuration driver only ++ int altsetting; // interface driver only ++ ++ char **interfaces_list; ++ ++ int interfaces; ++ struct usbd_function_instance **interfaces_array; ++ ++ int endpoints; ++ struct usbd_endpoint_request *requestedEndpoints; ++ struct usbd_endpoint_map *endpoint_map_array; ++ ++}; ++ ++ ++struct usbd_function_instance; ++ ++/*! ++ * @name Function Driver Registration ++ * ++ * Called by function drivers to register themselves when loaded ++ * or de-register when unloading. ++ * @{ ++ */ ++struct usbd_function_instance * usbd_register_function (struct usbd_function_driver *, char *, void *); ++struct usbd_function_instance * usbd_register_interface (struct usbd_function_driver *, char *, void *); ++struct usbd_function_instance * usbd_register_composite (struct usbd_function_driver *function_driver, char*, char **, void *); ++ ++void usbd_deregister_function (struct usbd_function_instance *); ++struct usbd_function_instance *usbd_find_function (char *name); ++struct usbd_function_instance *usbd_find_interface(char *name); ++/*! @} */ ++ ++ ++/*! ++ * @name String Descriptor database ++ * ++ * @{ ++ */ ++ ++struct usbd_string_descriptor *usbd_get_string_descriptor (u8); ++u8 usbd_realloc_string (u8, char *); ++u8 usbd_alloc_string (char *); ++void usbd_free_string_descriptor(u8 ); ++ ++//extern struct usbd_string_descriptor **usb_strings; ++//void usbd_free_descriptor_strings (struct usbd_descriptor *); ++ ++/*! @} */ ++ ++ ++ ++/*! ++ * @name Device Information ++ * ++ * @} ++ */ ++int usbd_high_speed(struct usbd_function_instance *); ++int usbd_bmaxpower(struct usbd_function_instance *); ++int usbd_endpoint_wMaxPacketSize(struct usbd_function_instance *, int, int); ++int usbd_endpoint_zero_wMaxPacketSize(struct usbd_function_instance *, int); ++int usbd_endpoint_bEndpointAddress(struct usbd_function_instance *, int, int); ++int usbd_get_bMaxPower(struct usbd_function_instance *); ++/*! @} */ ++ ++ ++/*! ++ * @name Device Control ++ * ++ * @} ++ */ ++usbd_device_state_t usbd_get_device_state(struct usbd_function_instance *); ++usbd_device_status_t usbd_get_device_status(struct usbd_function_instance *); ++int usbd_framenum(struct usbd_function_instance *); ++u64 usbd_ticks(struct usbd_function_instance *); ++u64 usbd_elapsed(struct usbd_function_instance *, u64 *, u64 *); ++ ++/*! @} */ ++ ++/*! ++ * @name Endpoint I/O ++ * ++ * @} ++ */ ++ ++struct usbd_urb *usbd_alloc_urb (struct usbd_function_instance *, int, int length, int (*callback) (struct usbd_urb *, int)); ++struct usbd_urb *usbd_alloc_urb_ep0 (struct usbd_function_instance *, int length, int (*callback) (struct usbd_urb *, int)); ++void usbd_free_urb (struct usbd_urb *urb); ++int usbd_start_in_urb (struct usbd_urb *urb); ++int usbd_start_out_urb (struct usbd_urb *); ++int usbd_cancel_urb(struct usbd_urb *); ++int usbd_halt_endpoint (struct usbd_function_instance *function, int endpoint_index); ++int usbd_endpoint_halted (struct usbd_function_instance *function, int endpoint); ++ ++ ++/*! @} */ ++ ++ ++/*! ++ * @name endpoint information ++ * ++ * Used by function drivers to get specific endpoint information ++ * @{ ++ */ ++ ++int usbd_endpoint_transferSize(struct usbd_function_instance *, int, int); ++int usbd_endpoint_interface(struct usbd_function_instance *, int); ++int usbd_interface_AltSetting(struct usbd_function_instance *, int); ++int usbd_ConfigurationValue(struct usbd_function_instance *); ++void usbd_endpoint_update(struct usbd_function_instance *, int , struct usbd_endpoint_descriptor *, int); ++/*! @} */ ++ ++//int usbd_remote_wakeup_enabled(struct usbd_function_instance *); ++ ++/*! ++ * @name device information ++ * ++ * Used by function drivers to get device feature information ++ * @{ ++ */ ++int usbd_otg_bmattributes(struct usbd_function_instance *); ++/*! @} */ ++ ++/*! ++ * @name usbd_feature_enabled - return non-zero if specified feature has been enabled ++ * @{ ++ */ ++int usbd_feature_enabled(struct usbd_function_instance *function, int f); ++/*! @} */ ++ ++/* DEPRECATED */ ++/*! ++ * @name Access to function privdata (DEPRECATED) ++ * @{ ++ */ ++void *usbd_function_get_privdata(struct usbd_function_instance *function); ++void usbd_function_set_privdata(struct usbd_function_instance *function, void *privdata); ++void usbd_flush_endpoint_index (struct usbd_function_instance *, int ); ++int usbd_old_get_descriptor (struct usbd_function_instance *function, u8 *buffer, int max, int descriptor_type, int index); ++/*! @} */ ++ +diff -uNr linux/drivers/no-otg/otg/usbp-pcd.h linux/drivers/otg/otg/usbp-pcd.h +--- linux/drivers/no-otg/otg/usbp-pcd.h 1970-01-01 01:00:00.000000000 +0100 ++++ linux/drivers/otg/otg/usbp-pcd.h 2006-09-01 21:41:34.000000000 +0200 +@@ -0,0 +1,279 @@ ++/* ++ * otg/otg/usbp-pcd.h - OTG Peripheral Controller Driver ++ * ++ * Copyright (c) 2004 Belcarra ++ * ++ * By: ++ * Stuart Lynne <sl@belcarra.com>, ++ * Tom Rushworth <tbr@belcarra.com>, ++ * Bruce Balden <balden@belcarra.com> ++ * ++ */ ++ ++/*! ++ * @file otg/otg/usbp-pcd.h ++ * @brief Peripheral Controller Driver Related Structures and Definitions. ++ * ++ * @ingroup USBP ++ */ ++ ++/*! ++ * The pcd_ops structure contains pointers to all of the functions implemented for the ++ * linked in driver. Having them in this structure allows us to easily determine what ++ * functions are available without resorting to ugly compile time macros or ifdefs ++ * ++ * There is only one instance of this, defined in the device specific lower layer. ++ */ ++struct usbd_pcd_ops { ++ ++ /* mandatory */ ++ u8 bmAttributes; ++ int max_endpoints; ++ int ep0_packetsize; ++ u32 capabilities; /* UDC Capabilities - see usbd-bus.h for details */ ++ u8 bMaxPower; ++ char *name; ++ u8 ports; ++ ++ ++ /* 3. called by ot_init() if defined */ ++ int (* serial_init) (struct pcd_instance *); /* get device serial number if available */ ++ ++ /* 4. called from usbd_enable_function_irq() - REQUIRED */ ++ int (*request_endpoints) /* process endpoint_request list and return endpoint_map */ ++ (struct pcd_instance *, ++ struct usbd_endpoint_map *, /* showing allocated endpoints with attributes, addresses */ ++ int, struct usbd_endpoint_request*); /* and other associated information */ ++ ++ /* 5. called from usbd_enable_function_irq() - REQUIRED */ ++ int (*set_endpoints) /* setup required endpoints in hardware */ ++ (struct pcd_instance *, int , struct usbd_endpoint_map *); ++ ++ /* 6. called by xxx() if defined */ ++ void (*enable) (struct pcd_instance *); /* enable the UDC */ ++ ++ /* 7. called by xxx() if defined */ ++ void (*disable) (struct pcd_instance *); /* disable the UDC */ ++ ++ /* 8. called by xxx() if defined */ ++ void (*start) (struct pcd_instance *); /* called for DEVICE_CREATE to start the UDC */ ++ void (*stop) (struct pcd_instance *); /* called for DEVICE_DESTROY to stop the UDC */ ++ void (*disable_ep) ++ (struct pcd_instance *, ++ unsigned int ep); /* called for DEVICE_DESTROTY to disable endpoint zero */ ++ ++ /* 9. called by xxx() if defined */ ++ void (*set_address) ++ (struct pcd_instance *, ++ unsigned char address); /* called for DEVICE_RESET and DEVICE_ADDRESS_ASSIGNED to */ ++ ++ ++ /* 10. called by xxx() if defined */ ++ void (*setup_ep) ++ (struct pcd_instance *, ++ unsigned int ep, /* called for DEVICE_CONFIGURED to setup specified endpoint for use */ ++ struct usbd_endpoint_instance *endpoint); ++ ++ /* 11. called by xxx() if defined */ ++ void (*reset_ep) ++ (struct pcd_instance *, ++ unsigned int ep); /* called for DEVICE_RESET to reset endpoint zero */ ++ ++ ++ /* 12. called by usbd_send_urb() - REQUIRED */ ++ void (*start_endpoint_in) /* start an IN urb */ ++ (struct pcd_instance *, ++ struct usbd_endpoint_instance *); ++ ++ /* 13. called by usbd_start_recv() - REQUIRED */ ++ void (*start_endpoint_out) /* start an OUT urb */ ++ (struct pcd_instance *, ++ struct usbd_endpoint_instance *); ++ ++ /* 14. called by usbd_cancel_urb_irq() */ ++ void (*cancel_in_irq) ++ (struct pcd_instance *, ++ struct usbd_urb *urb); /* cancel active urb for IN endpoint */ ++ void (*cancel_out_irq) ++ (struct pcd_instance *, ++ struct usbd_urb *urb); /* cancel active urb for OUT endpoint */ ++ ++ ++ /* 15. called from ep0_recv_setup() for GET_STATUS request */ ++ int (*endpoint_halted) ++ (struct pcd_instance *, ++ struct usbd_endpoint_instance *); /* Return non-zero if requested endpoint is halted */ ++ int (*halt_endpoint) ++ (struct pcd_instance *, ++ struct usbd_endpoint_instance *, ++ int); /* Return non-zero if requested endpoint clear fails */ ++ ++ /* 16 */ ++ void (*startup_events) (struct pcd_instance *); /* perform UDC specific USB events */ ++ ++ /* 17. root hub operations ++ */ ++ int (*hub_status) (struct pcd_instance *); ++ int (*port_status) (struct pcd_instance *, int); ++ int (*hub_feature) (struct pcd_instance *, int, int); ++ int (*port_feature) (struct pcd_instance *, int, int, int); ++ int (*get_ports_status) (struct pcd_instance *); ++ int (*wait_for_change) (struct pcd_instance *); ++}; ++ ++extern struct usbd_pcd_ops usbd_pcd_ops; ++ ++ ++/*! pcd_rcv_next_irq - get the current or next urb to receive into ++ * Called by UDC driver to get current receive urb or next available receive urb. ++ */ ++static __inline__ struct usbd_urb * pcd_rcv_next_irq (struct usbd_endpoint_instance *endpoint) ++{ ++ if (!endpoint->rcv_urb) ++ if ( (endpoint->rcv_urb = usbd_first_urb_detached_irq (&endpoint->rdy))) ++ endpoint->rcv_urb->status = RECV_IN_PROGRESS; ++ ++ //TRACE_MSG1(PCD, "BUS_RCV_URB: %p", endpoint->rcv_urb); ++ return endpoint->rcv_urb; ++} ++ ++ ++/*! pcd_rcv_complete_irq - complete a receive operation ++ * Called by UDC driver to show completion of outstanding I/O, this will ++ * update the urb length and if necessary will finish the urb passing it to the ++ * bottom half which will in turn call the function driver callback. ++ */ ++struct usbd_urb * pcd_rcv_complete_irq (struct usbd_endpoint_instance *endpoint, int len, int urb_bad); ++struct usbd_urb * pcd_rcv_finished_irq (struct usbd_endpoint_instance *endpoint, int len, int urb_bad); ++ ++ ++/*! pcd_rcv_cancelled_irq - cancel current receive urb ++ * Called by UDC driver to cancel the current receive urb. ++ */ ++static __inline__ void pcd_rcv_cancelled_irq (struct usbd_endpoint_instance *endpoint) ++{ ++ struct usbd_urb *rcv_urb; ++ ++ TRACE_MSG1(PCD, "BUS_RCV CANCELLED: %p", (int) endpoint->rcv_urb); ++ RETURN_IF (! (rcv_urb = endpoint->rcv_urb)); ++ //TRACE_MSG1(PCD, "rcv_urb: %p", (int)endpoint->rcv_urb); ++ usbd_urb_finished_irq (rcv_urb, RECV_CANCELLED); ++ endpoint->sent = endpoint->last = 0; ++ endpoint->rcv_urb = NULL; ++} ++ ++ ++/*! pcd_tx_next_irq - get the current or next urb to transmit ++ * Called by UDC driver to get current transmit urb or next available transmit urb. ++ */ ++static __inline__ struct usbd_urb * pcd_tx_next_irq (struct usbd_endpoint_instance *endpoint) ++{ ++ if (!endpoint->tx_urb) ++ if ( (endpoint->tx_urb = usbd_first_urb_detached_irq (&endpoint->tx))) { ++#if 0 ++ int i; ++ u8 *cp = endpoint->tx_urb->buffer; ++ ++ TRACE_MSG2(PCD, "NEXT TX: length: %d flags: %x", ++ endpoint->tx_urb->actual_length, endpoint->tx_urb->flags); ++ ++ for (i = 0; i < endpoint->tx_urb->actual_length; i+= 8) ++ ++ TRACE_MSG8(PCD, "SENT %02x %02x %02x %02x %02x %02x %02x %02x", ++ cp[i + 0], cp[i + 1], cp[i + 2], cp[i + 3], ++ cp[i + 4], cp[i + 5], cp[i + 6], cp[i + 7] ++ ); ++#endif ++ endpoint->tx_urb->status = SEND_IN_PROGRESS; ++ } ++ //TRACE_MSG1(PCD, "BUS_TX NEXT TX_URB: %p", (int)endpoint->tx_urb); ++ return endpoint->tx_urb; ++} ++ ++ ++/*! pcd_tx_complete_irq - complete a transmit operation ++ * Called by UDC driver to show completion of an outstanding I/O, this will update the urb sent ++ * information and if necessary will finish the urb passing it to the bottom half which will in ++ * turn call the function driver callback. ++ */ ++struct usbd_urb * pcd_tx_complete_irq (struct usbd_endpoint_instance *endpoint, int restart); ++ ++ ++/*! pcd_tx_cancelled_irq - finish pending tx_urb with SEND_CANCELLED ++ * Called by UDC driver to cancel the current transmit urb. ++ */ ++static __inline__ void pcd_tx_cancelled_irq (struct usbd_endpoint_instance *endpoint) ++{ ++ struct usbd_urb *tx_urb; ++ ++ TRACE_MSG1(PCD, "BUS_TX CANCELLED: %p", (int) endpoint->tx_urb); ++ RETURN_IF (! (tx_urb = endpoint->tx_urb)); ++ usbd_urb_finished_irq (tx_urb, SEND_CANCELLED); ++ endpoint->sent = endpoint->last = 0; ++ endpoint->tx_urb = NULL; ++} ++ ++ ++/*! pcd_tx_sendzlp - test if we need to send a ZLP ++ * This has a side-effect of reseting the USBD_URB_SENDZLP flag. ++ */ ++static __inline__ int pcd_tx_sendzlp (struct usbd_endpoint_instance *endpoint) ++{ ++ struct usbd_urb *tx_urb = endpoint->tx_urb; ++ RETURN_FALSE_IF (!tx_urb || (tx_urb->actual_length < endpoint->sent) || ! (tx_urb->flags & USBD_URB_SENDZLP)); ++ tx_urb->flags &= ~USBD_URB_SENDZLP; ++ return TRUE; ++} ++ ++ ++/*! pcd_rcv_complete_irq - complete a receive ++ * Called from rcv interrupt to complete. ++ */ ++static __inline__ void pcd_rcv_fast_complete_irq (struct usbd_endpoint_instance *endpoint, struct usbd_urb *rcv_urb) ++{ ++ //TRACE_MSG1(PCD, "BUS_RCV FAST COMPLETE: %d", rcv_urb->actual_length); ++ usbd_urb_finished_irq (rcv_urb, RECV_OK); ++} ++ ++/*! pcd_recv_setup - process a device request ++ * Note that we verify if a receive urb has been queued for H2D with non-zero wLength ++ * and return -EINVAL to stall if the upper layers have not properly tested for and ++ * setup a receive urb in this case. ++ */ ++static __inline__ int pcd_recv_setup_irq (struct pcd_instance *pcd, struct usbd_device_request *request) ++{ ++ struct usbd_bus_instance *bus = pcd->bus; ++ struct usbd_endpoint_instance *endpoint = bus->endpoint_array + 0; ++ TRACE_SETUP (PCD, request); ++ RETURN_EINVAL_IF (usbd_device_request(bus, request)); // fail if already failed ++ ++ //TRACE_MSG2(PCD, "BUS_RECV SETUP: RCV URB: %x TX_URB: %x", (int)endpoint->rcv_urb, (int)endpoint->tx_urb); ++ RETURN_ZERO_IF ( (request->bmRequestType & USB_REQ_DIRECTION_MASK) == USB_REQ_DEVICE2HOST); ++ RETURN_ZERO_IF (!le16_to_cpu (request->wLength)); ++ RETURN_EINVAL_IF (!endpoint->rcv_urb); ++ return 0; ++} ++ ++/*! pcd_recv_setup_emulate_irq - emulate a device request ++ * Called by the UDC driver to inject a SETUP request. This is typically used ++ * by drivers for UDC's that do not pass all SETUP requests to us but instead give us ++ * a configuration change interrupt. ++ */ ++int pcd_recv_setup_emulate_irq(struct usbd_bus_instance *, u8, u8, u16, u16, u16); ++ ++/*! pcd_ep0_reset_irq - reset ep0 endpoint ++ */ ++static void __inline__ pcd_ep0_reset_endpoint_irq (struct usbd_endpoint_instance *endpoint) ++{ ++ TRACE_MSG0(PCD, "BUS_EP0 RESET"); ++ pcd_tx_cancelled_irq (endpoint); ++ pcd_rcv_cancelled_irq (endpoint); ++ endpoint->sent = endpoint->last = 0; ++} ++ ++/*! pcd_check_device_feature - verify that feature is set or clear ++ * Check current feature setting and emulate SETUP Request to set or clear ++ * if required. ++ */ ++void pcd_check_device_feature(struct usbd_bus_instance *, int , int ); ++ +diff -uNr linux/drivers/no-otg/otgcore/Makefile linux/drivers/otg/otgcore/Makefile +--- linux/drivers/no-otg/otgcore/Makefile 1970-01-01 01:00:00.000000000 +0100 ++++ linux/drivers/otg/otgcore/Makefile 2006-09-01 21:41:34.000000000 +0200 +@@ -0,0 +1,107 @@ ++# ++# Belcarra OTG - On-The-Go ++# ++# Copyright (c) 2004 Belcarra Technologies Corp ++ ++TOPDIR ?= ../../../.. ++ ++subdir-y := ++subdir-m := ++subdir-n := ++subdir- := ++ ++ ++# The target object and module list name. ++ ++O_TARGET := otgcore_target.o ++ ++# Objects that export symbols. ++ ++export-objs := core-init-l24.o otg.o otg-trace.o otg-mesg.o usbp-bops.o usbp-fops.o otg-mesg-l24.o ++ ++# Multipart objects. (core layer) ++ ++list-multi := otgtrace.o otgcore.o otgadmin.o ++ ++otgcore-objs := core-init-l24.o otg.o \ ++ otg-trace.o otg-trace-l24.o \ ++ otg-mesg.o otg-mesg-l24.o \ ++ otg-fw.o \ ++ usbp-fops.o usbp-bops.o ++ ++ ++usbprocfs-objs := usbp-procfs.o ++ ++ ++ifeq ("$(CONFIG_OTG_FW_MN)", "y") ++otgcore-objs += otg-fw-mn.o ++else ++otgcore-objs += otg-fw-df.o ++endif ++ ++ ++# Optional parts of multipart objects. ++ ++# Object file lists. ++ ++obj-y := ++obj-m := ++obj-n := ++obj- := ++ ++# Each configuration option enables a list of files. ++ ++obj-$(CONFIG_OTG) += otgcore.o ++obj-$(CONFIG_OTG_PROCFSM) += usbprocfs.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 ++OTG=$(TOPDIR)/drivers/otg ++#USBDCORE_DIR=$(OTG)/usbdcore ++OTGCORE_DIR=$(OTG)/otgcore ++EXTRA_CFLAGS += -Wno-missing-prototypes -Wno-unused -Wno-format -I$(OTG) ++EXTRA_CFLAGS_nostdinc += -Wno-missing-prototypes -Wno-unused -Wno-format -I$(OTG) ++ ++vpath %.c $(OTGCORE_DIR) ++ ++# Link rules for multi-part drivers. ++ ++otgadmin.o: $(otgadmin-objs) ++ $(LD) -r -o $@ $(otgadmin-objs) ++ ++otgcore.o: $(otgcore-objs) ++ $(LD) -r -o $@ $(otgcore-objs) ++ ++otgtrace.o: $(otgtrace-objs) ++ $(LD) -r -o $@ $(otgtrace-objs) ++ ++usbprocfs.o: $(usbprocfs-objs) ++ $(LD) -r -o $@ $(usbprocfs-objs) ++ +diff -uNr linux/drivers/no-otg/otgcore/Makefile-l26 linux/drivers/otg/otgcore/Makefile-l26 +--- linux/drivers/no-otg/otgcore/Makefile-l26 1970-01-01 01:00:00.000000000 +0100 ++++ linux/drivers/otg/otgcore/Makefile-l26 2006-09-01 21:41:34.000000000 +0200 +@@ -0,0 +1,24 @@ ++# ++# Belcarra OTG - On-The-Go ++# ++# Copyright (c) 2004 Belcarra Technologies Corp ++ ++OTG=$(TOPDIR)/drivers/otg ++EXTRA_CFLAGS += -Wno-missing-prototypes -Wno-unused -Wno-format -I$(OTG) ++EXTRA_CFLAGS_nostdinc += -Wno-missing-prototypes -Wno-unused -Wno-format -I$(OTG) ++ ++#otgadmin-objs := admin-init-l24.o otg-admin.o otg-decode.o ++#obj-$(CONFIG_OTG) += otgadmin.o ++ ++otgcore-objs := core-init-l24.o otg.o \ ++ otg-trace.o otg-trace-l24.o \ ++ otg-mesg.o otg-mesg-l24.o \ ++ otg-fw.o \ ++ usbp-bops.o \ ++ usbp-fops.o \ ++ ++obj-$(CONFIG_OTG) += otgcore.o ++ ++#otgtrace-objs := otg-trace.o ++#obj-$(CONFIG_OTG_TRACE) += otgtrace.o ++ +diff -uNr linux/drivers/no-otg/otgcore/TODO-USBP.txt linux/drivers/otg/otgcore/TODO-USBP.txt +--- linux/drivers/no-otg/otgcore/TODO-USBP.txt 1970-01-01 01:00:00.000000000 +0100 ++++ linux/drivers/otg/otgcore/TODO-USBP.txt 2006-09-01 21:41:34.000000000 +0200 +@@ -0,0 +1,51 @@ ++USBD Core Architecture ToDo Stuart Lynne ++Belcarra Fri Aug 13 14:37:13 PDT 2004 ++ ++ ++The following need to be addressed to support ongoing development and support ++for the USB Device core and functions. ++ ++ ++Specifically: ++ - finish acm, make CDC compliant, support all possible operations ++ - enhance network, add NDIS support, ++ - wmc - implement wmc ++ - obex ++ - mass storage ++ ++ ++These require: ++ ++ - enhancements to allow building composite functions ++ - common routines for handling CDC class requirements ++ ++ ++1. interface list ++ ++ - currently we have an endpoint list that allows each endpoint to be ++ mapped to a function ++ ++ - we need to do the same for interfaces, allow each interface to be ++ owned by a function, this will allow ep0 to multiplex per ++ interface standard requests to the proper function driver ++ ++ ++2. common functions ++ ++ - a common send_interrupt_urb function ++ ++ int usbd_send_interrupt(struct usbd_function_instance *, ++ int, void *, int, int (*callback) (struct usbd_urb *, int)); ++ ++ ++ - this creates an interrupt urb on the required endpoint, copies the ++ data in, and starts it with the appropriate callback etc. ++ ++ ++ - notification ++ ++ usbd_network_notfication ++ usbd_response_available ++ usbd_serial_state ++ ++ +diff -uNr linux/drivers/no-otg/otgcore/core-init-l24.c linux/drivers/otg/otgcore/core-init-l24.c +--- linux/drivers/no-otg/otgcore/core-init-l24.c 1970-01-01 01:00:00.000000000 +0100 ++++ linux/drivers/otg/otgcore/core-init-l24.c 2006-09-01 21:41:34.000000000 +0200 +@@ -0,0 +1,173 @@ ++/* ++ * otg/otg/core-init-l24.c - OTG Peripheral Controller Driver Module Initialization ++ * ++ * Copyright (c) 2004 Belcarra ++ * ++ * By: ++ * Stuart Lynne <sl@belcara.com>, ++ * Tom Rushworth <tbr@belcara.com>, ++ * Bruce Balden <balden@belcara.com> ++ * ++ * This is the linux 2.4 version. ++ * ++ */ ++/*! ++ * @file otg/otgcore/core-init-l24.c ++ * @brief OTG Core Linux initialization. ++ * ++ * This file is the starting point for defining the Linux OTG Core ++ * driver. It references and starts all of the other components that ++ * must be "linked" into the OTGCORE mdoule. ++ * ++ * @ingroup OTGCore ++ */ ++ ++ ++#include <otg/otg-compat.h> ++#include <otg/otg-module.h> ++ ++#include <otg/usbp-chap9.h> ++#include <otg/usbp-func.h> ++#include <otg/usbp-bus.h> ++#include <otg/otg-trace.h> ++#include <otg/otg-api.h> ++#include <otg/otg-tcd.h> ++#include <otg/otg-hcd.h> ++#include <otg/otg-pcd.h> ++#include <otg/otg-ocd.h> ++ ++EMBED_LICENSE(); // When supported by the OS, embed license information in the binary ++ ++ ++#ifdef OTG_MALLOC_TEST ++int otg_mallocs; ++#endif /* OTG_MALLOC_TEST */ ++ ++otg_tag_t CORE; ++ ++#ifdef CONFIG_OTG_FW_MN ++struct otg_firmware *otg_firmware_loaded = &otg_firmware_mn; ++struct otg_firmware *otg_firmware_orig = &otg_firmware_mn; ++#else /* CONFIG_OTG_FW_MN */ ++struct otg_firmware *otg_firmware_loaded = &otg_firmware_df; ++struct otg_firmware *otg_firmware_orig = &otg_firmware_df; ++#endif /* CONFIG_OTG_FW_MN */ ++ ++struct otg_firmware *otg_firmware_loading; ++ ++ ++struct otg_instance otg_instance_private; ++ ++struct hcd_instance hcd_instance_private = { otg: &otg_instance_private, }; ++struct ocd_instance ocd_instance_private = { otg: &otg_instance_private, }; ++struct pcd_instance pcd_instance_private = { otg: &otg_instance_private, }; ++struct tcd_instance tcd_instance_private = { otg: &otg_instance_private, }; ++ ++struct otg_instance otg_instance_private = { ++ hcd: &hcd_instance_private, ++ ocd: &ocd_instance_private, ++ pcd: &pcd_instance_private, ++ tcd: &tcd_instance_private, ++}; ++ ++struct otg_instance *otg_instance = &otg_instance_private; ++ ++char * otg_get_state_name(int state) ++{ ++ struct otg_state *otg_state; ++ if (!otg_firmware_loaded || (state >= otg_firmware_loaded->number_of_states)) ++ return "UNKNOWN_STATE"; ++ ++ otg_state = otg_firmware_loaded->otg_states + state; ++ return otg_state->name; ++} ++ ++ ++/* ************************************************************************************* */ ++ ++ ++/* otg_modinit - linux module initialization ++ */ ++static int otg_modinit (void) ++{ ++ int message_init = 0; ++ int message_init_l24 = 0; ++ int trace_init = 0; ++ int trace_init_l24 = 0; ++ ++ ++ printk(KERN_INFO"%s: \n", __FUNCTION__); ++ ++ trace_init = otg_trace_init(); ++ trace_init_l24 = otg_trace_init_l24(); ++ ++ CORE = otg_trace_obtain_tag(); ++ ++ TRACE_MSG0(CORE,"--"); ++ otg_instance->current_outputs = otg_firmware_loaded->otg_states; ++ otg_instance->previous_outputs = otg_firmware_loaded->otg_states; ++ ++ /* initialize otg-mesg and usbp ++ */ ++ THROW_IF((message_init = otg_message_init(otg_instance)), error); ++ THROW_IF((message_init_l24 = otg_message_init_l24(otg_instance)), error); ++ THROW_IF(usbd_device_init(), error); ++ ++ otg_set_ocd_ops(NULL); ++ otg_set_tcd_ops(NULL); ++ otg_set_hcd_ops(NULL); ++ otg_set_pcd_ops(NULL); ++ ++ CATCH(error) { ++ CORE = otg_trace_invalidate_tag(CORE); ++ if (trace_init_l24 )otg_trace_exit_l24(); ++ if (trace_init) otg_trace_exit(); ++ if (message_init_l24 )otg_message_exit_l24(); ++ if (message_init) otg_message_exit(); ++ return -EINVAL; ++ } ++ return 0; ++} ++module_init (otg_modinit); ++ ++ ++#if OTG_EPILOGUE /* Set nonzero in <otg-module.h> when -DMODULE is in force */ ++/* otg_modexit - This is *only* used for drivers compiled and used as a module. ++ */ ++static void otg_modexit (void) ++{ ++ printk(KERN_INFO"%s: \n", __FUNCTION__); ++ ++ usbd_device_exit(); ++ otg_message_exit_l24(); ++ ++ CORE = otg_trace_invalidate_tag(CORE); ++ otg_trace_exit(); ++ ++ if (otg_firmware_loading) { ++ lkfree(otg_firmware_loading->otg_states); ++ lkfree(otg_firmware_loading->otg_tests); ++ lkfree(otg_firmware_loading); ++ } ++ if (otg_firmware_loaded && (otg_firmware_loaded != otg_firmware_orig)) { ++ lkfree(otg_firmware_loaded->otg_states); ++ lkfree(otg_firmware_loaded->otg_tests); ++ lkfree(otg_firmware_loaded); ++ } ++} ++#endif ++ ++#if defined (CONFIG_OTG_USBD_PM) ++int usbd_load(char * arg) { return 0; } ++int usbd_unload(char *arg) { return 0; } ++OTG_EXPORT_SYMBOL(usbd_load); ++OTG_EXPORT_SYMBOL(usbd_unload); ++#endif ++ ++MOD_EXIT(otg_modexit); ++OTG_EXPORT_SYMBOL(otg_get_state_name); ++ ++#ifdef OTG_MALLOC_TEST ++OTG_EXPORT_SYMBOL(otg_mallocs); ++#endif /* OTG_MALLOC_TEST */ ++ +diff -uNr linux/drivers/no-otg/otgcore/hub.c linux/drivers/otg/otgcore/hub.c +--- linux/drivers/no-otg/otgcore/hub.c 1970-01-01 01:00:00.000000000 +0100 ++++ linux/drivers/otg/otgcore/hub.c 2006-09-01 21:41:34.000000000 +0200 +@@ -0,0 +1,607 @@ ++/* ++ * otg/otgcore/hub.c ++ * ++ * Copyright (c) 2004 Belcarra ++ * ++ * By: ++ * Stuart Lynne <sl@belcarra.com>, ++ * Tom Rushworth <tbr@belcarra.com>, ++ * Bruce Balden <balden@belcarra.com> ++ * ++ * This is a root hub function. ++ * ++ */ ++/*! ++ * @file otg/otgcore/hub.c ++ * @brief A simple hub function driver. ++ * ++ * ++ * @ingroup USBDCore ++ */ ++#include <otg/otg-compat.h> ++#include <linux/config.h> ++#include <linux/kernel.h> ++#include <linux/version.h> ++#include <linux/ctype.h> ++#include <linux/timer.h> ++#include <linux/slab.h> ++#include <linux/errno.h> ++ ++#include <otg/usbp-chap9.h> ++#include <otg/usbp-func.h> ++#include <otg/usbp-bus.h> ++ ++#ifdef CONFIG_OTG_ROOT_HUB ++/* ++ * Root Hub Configuration ++ * ++ * Endpoint, Class, Interface, Configuration and Device descriptors/descriptions ++ * Data Interface Alternate description(s) ++ * Interface description(s) ++ * Configuration description(s) ++ * Device Description ++ */ ++ ++#define BULK_INT 0x00 ++#define ENDPOINTS 0x01 ++ ++#if 0 ++ ++Configuration descriptor [1 ] 09 02 19 00 01 01 02 00 00 ++ 09 02 19 00 01 01 00 40 00 ++ ++Interface descriptor [1:1:0 ] 09 04 00 00 01 09 00 00 03 ++Endpoint descriptor [1:1:0:1] 07 05 81 03 10 00 ff ++ ++#endif ++ ++#if defined(OTG_WINCE) ++ ++static struct hub_descriptor rh_hub_descriptor; ++struct usbd_endpoint_descriptor rh_data; ++ ++static struct usbd_endpoint_descriptor *rh_default[] = { &rh_data, }; ++u8 rh_indexes[] = { BULK_INT, }; ++ ++static struct usbd_interface_descriptor rh_data_alternate_descriptor; ++static struct usbd_alternate_description rh_data_alternate_descriptions[1]; ++}; ++ ++static struct usbd_interface_description rh_interfaces[1]; ++ ++struct usbd_configuration_descriptor rh_configuration_descriptor; ++ ++struct usbd_configuration_description rh_description[1]; ++ ++static struct usbd_device_descriptor rh_device_descriptor; ++#ifdef CONFIG_OTG_HIGH_SPEED ++static struct usbd_device_qualifier_descriptor rh_device_qualifier_descriptor; ++#endif /* CONFIG_OTG_HIGH_SPEED */ ++ ++static struct usbd_endpoint_request rh_endpoint_requests[ENDPOINTS+1] = { ++ { 1, 0, 0, USB_DIR_IN | USB_ENDPOINT_INTERRUPT, 16, 64, }, ++ { 0, }, ++}; ++ ++static struct usbd_otg_descriptor rh_otg_descriptor; ++ ++struct usbd_device_description rh_device_description; ++ ++void hub_global_init(void) ++{ ++ ZERO(rh_hub_descriptor); ++ rh_hub_descriptor.bDescLength = 0x09; ++ rh_hub_descriptor.bDescriptorType = USB_DT_HUB; ++ rh_hub_descriptor.bNbrPorts = 0x00; ++ rh_hub_descriptor.wHubCharacteristics = HUB_GANGED_POWER | HUB_GLOBAL_OVERCURRENT; ++ rh_hub_descriptor.bPwrOn2PwrGood = 0x01; ++ rh_hub_descriptor.bHubContrCurrent = 0x01; ++ rh_hub_descriptor.DeviceRemovable = 0x00; ++ rh_hub_descriptor.PortPwrCtrlMask = 0xff; ++ ++ ZERO(rh_data); ++ rh_data.bLength: 0x07; ++ rh_data.bDescriptorType = USB_DT_ENDPOINT; ++ rh_data.bEndpointAddress = USB_DIR_IN; ++ rh_data.bmAttributes = INTERRUPT; ++ rh_data.wMaxPacketSize = __constant_cpu_to_le16(0x4); ++ rh_data.bInterval: 0xff; ++ ++ ZERO(rh_data_alternate_desriptor); ++ rh_data_alternate_descriptor.bLength = 0x09; ++ rh_data_alternate_descriptor.bDescriptorType = USB_DT_INTERFACE; ++ rh_data_alternate_descriptor.bNumEndpoints = 0x01; ++ rh_data_alternate_descriptor.bInterfaceClass = USB_CLASS_HUB; ++ ++ ZERO(rh_data_alternate_descriptions); ++ rh_data_alternate_descriptors[0].iInterface =Root hub - Status"; ++ rh_data_alternate_descriptors[0].interface_descriptor = &rh_data_alternate_descriptor; ++ rh_data_alternate_descriptors[0].endpoints = sizeof (rh_default) / sizeof(struct usbd_endpoint_descriptor *); ++ rh_data_alternate_descriptors[0].endpoint_list = rh_default; ++ rh_data_alternate_descriptors[0].endpoint_indexes = rh_indexes; ++ ++ ZERO(rh_interfaces); ++ ++ rh_interfaces[0].alternates = sizeof (rh_data_alternate_descriptions) / sizeof (struct usbd_alternate_description); ++ rh_interfaces[0].alternate_list = rh_data_alternate_descriptions; ++ ++ ++ ZERO(rh_configuration_descriptor); ++ rh_configuration_descriptor.bLength = 0x09; ++ rh_configuration_descriptor.bDescriptorType = USB_DT_CONFIGURATION; ++ rh_configuration_descriptor.bNumInterfaces = sizeof (rh_interfaces) / sizeof (struct usbd_interface_description); ++ rh_configuration_descriptor.bConfigurationValue = 0x01; ++ ++ ++ ZERO(rh_description); ++ rh_description[0].configuration_descriptor = &rh_configuration_descriptor; ++ rh_description[0].iConfiguration = Root Hub"; ++ rh_description[0].bNumInterfaces = sizeof (rh_interfaces) / sizeof (struct usbd_interface_description); ++ rh_description[0].interface_list = rh_interfaces; ++ ++ ZERO(rh_device_descriptor); ++ ++ rh_device_descriptor.bLength = sizeof(struct usbd_device_descriptor); ++ rh_device_descriptor.bDescriptorType = USB_DT_DEVICE; ++ rh_device_descriptor.bcdUSB = __constant_cpu_to_le16(USB_BCD_VERSION); ++ rh_device_descriptor.bDeviceClass = USB_CLASS_HUB; ++ ++ ++#ifdef CONFIG_OTG_HIGH_SPEED ++ ZERO(rh_device_qualifier_descriptor); ++ rh_device_qualifier_descriptor.bLength = sizeof(struct usbd_device_qualifier_descriptor); ++ rh_device_qualifier_descriptor.bDescriptorType = USB_DT_DEVICE_QUALIFIER; ++ rh_device_qualifier_descriptor.bcdUSB = __constant_cpu_to_le16(USB_BCD_VERSION); ++ rh_device_qualifier_descriptor.bDeviceClass = USB_CLASS_HUB; ++#endif /* CONFIG_OTG_HIGH_SPEED */ ++ ++ ZERO(rh_otg_descriptor); ++ rh_otg_descriptor.bLength = sizeof(struct usbd_otg_descriptor); ++ rh_otg_descriptor.bDescriptorType = USB_DT_OTG; ++ rh_otg_descriptor.bmAttributes = 0; ++ ++ ++ ZERO(rh_device_description); ++ rh_device_description.device_descriptor = &rh_device_descriptor; ++ #ifdef CONFIG_OTG_HIGH_SPEED ++ rh_device_description.device_qualifier_descriptor = &rh_device_qualifier_descriptor; ++ #endif /* CONFIG_OTG_HIGH_SPEED */ ++ rh_device_description.otg_descriptor = &rh_otg_descriptor; ++ rh_device_description.iManufacturer = ""; ++ rh_device_description.iProduct = "root hub"; ++ rh_device_description.endpointsRequested = ENDPOINTS; ++ rh_device_description.requestedEndpoints = rh_endpoint_requests; ++ ++} ++#else /* defined(OTG_WINCE) */ ++ ++static struct hub_descriptor rh_hub_descriptor = { ++ bDescLength: 0x09, ++ bDescriptorType: USB_DT_HUB, ++ bNbrPorts: 0x00, ++ wHubCharacteristics: HUB_GANGED_POWER | HUB_GLOBAL_OVERCURRENT, ++ bPwrOn2PwrGood: 0x01, ++ bHubContrCurrent: 0x01, ++ DeviceRemovable: 0x00, ++ PortPwrCtrlMask: 0xff, ++}; ++ ++struct usbd_endpoint_descriptor rh_data = { ++ bLength: 0x07, ++ bDescriptorType: USB_DT_ENDPOINT, ++ bEndpointAddress: USB_DIR_IN, ++ bmAttributes: INTERRUPT, ++ wMaxPacketSize: __constant_cpu_to_le16(0x4), ++ bInterval: 0xff, ++}; ++ ++static struct usbd_endpoint_descriptor *rh_default[] = { &rh_data, }; ++u8 rh_indexes[] = { BULK_INT, }; ++ ++static struct usbd_interface_descriptor rh_data_alternate_descriptor = { ++ bLength: 0x09, ++ bDescriptorType: USB_DT_INTERFACE, ++ bNumEndpoints: 0x01, ++ bInterfaceClass: USB_CLASS_HUB, ++}; ++ ++static struct usbd_alternate_description rh_data_alternate_descriptions[] = { ++ { iInterface:"Root hub - Status", ++ interface_descriptor: &rh_data_alternate_descriptor, ++ endpoints:sizeof (rh_default) / sizeof(struct usbd_endpoint_descriptor *), ++ endpoint_list: rh_default, ++ endpoint_indexes: rh_indexes, ++ }, ++}; ++ ++static struct usbd_interface_description rh_interfaces[] = { ++ { alternates:sizeof (rh_data_alternate_descriptions) / sizeof (struct usbd_alternate_description), ++ alternate_list:rh_data_alternate_descriptions,}, ++}; ++ ++ ++struct usbd_configuration_descriptor rh_configuration_descriptor = { ++ bLength: 0x09, ++ bDescriptorType: USB_DT_CONFIGURATION, ++ bNumInterfaces: sizeof (rh_interfaces) / sizeof (struct usbd_interface_description), ++ bConfigurationValue: 0x01, ++}; ++ ++struct usbd_configuration_description rh_description[] = { ++ { configuration_descriptor: &rh_configuration_descriptor, ++ iConfiguration:"Root Hub", ++ bNumInterfaces:sizeof (rh_interfaces) / sizeof (struct usbd_interface_description), ++ interface_list:rh_interfaces,}, ++}; ++ ++static struct usbd_device_descriptor rh_device_descriptor = { ++ bLength: sizeof(struct usbd_device_descriptor), ++ bDescriptorType: USB_DT_DEVICE, ++ bcdUSB: __constant_cpu_to_le16(USB_BCD_VERSION), ++ bDeviceClass: USB_CLASS_HUB, ++}; ++ ++#ifdef CONFIG_OTG_HIGH_SPEED ++static struct usbd_device_qualifier_descriptor rh_device_qualifier_descriptor = { ++ bLength: sizeof(struct usbd_device_qualifier_descriptor), ++ bDescriptorType: USB_DT_DEVICE_QUALIFIER, ++ bcdUSB: __constant_cpu_to_le16(USB_BCD_VERSION), ++ bDeviceClass: USB_CLASS_HUB, ++}; ++#endif /* CONFIG_OTG_HIGH_SPEED */ ++ ++static struct usbd_endpoint_request rh_endpoint_requests[ENDPOINTS+1] = { ++ { 1, 0, 0, USB_DIR_IN | USB_ENDPOINT_INTERRUPT, 16, 64, }, ++ { 0, }, ++}; ++ ++static struct usbd_otg_descriptor rh_otg_descriptor = { ++ bLength : sizeof(struct usbd_otg_descriptor), ++ bDescriptorType: USB_DT_OTG, ++ bmAttributes: 0, ++}; ++ ++struct usbd_device_description rh_device_description = { ++ device_descriptor: &rh_device_descriptor, ++ #ifdef CONFIG_OTG_HIGH_SPEED ++ device_qualifier_descriptor: &rh_device_qualifier_descriptor, ++ #endif /* CONFIG_OTG_HIGH_SPEED */ ++ otg_descriptor: &rh_otg_descriptor, ++ iManufacturer: "", ++ iProduct: "root hub", ++ endpointsRequested: ENDPOINTS, ++ requestedEndpoints: rh_endpoint_requests, ++}; ++#endif /* defined(OTG_WINCE) */ ++ ++/* root hub Private variables ******************************************************************** */ ++ ++struct rh_private { ++ struct usbd_function_instance *function; ++ int usb_driver_registered; // non-zero if usb function registered ++ unsigned char connected; // non-zero if connected to host (configured) ++ u8 ports; ++ u8 hub_features; ++ u32 *port_features; ++ u32 ports_status; ++ struct WORK_STRUCT bh; ++}; ++ ++struct rh_private rh_private; ++ ++/* Transmit Function *************************************************************************** */ ++ ++void rh_task(void *data) ++{ ++ ++ //struct rh_private *rh = (struct rh_private *)data; ++ //struct usbd_function_instance *function = rh->function; ++ //struct usbd_bus_instance *bus = function->bus; ++ ++ printk(KERN_INFO"%s: start\n", __FUNCTION__); ++#if 0 ++ wait_queue_head_t rh_wait_queue; ++ init_waitqueue_head(&rh_wait_queue); ++ ++ while (rh->bh.data && rh->connected) { ++ u32 ports_status = cpu_to_le32(bus->driver->bops->get_ports_status(bus)); ++ ++ printk(KERN_INFO"%s: status: %08x\n", __FUNCTION__, ports_status); ++ ++ if (rh->ports_status != ports_status) { ++ struct usbd_urb *urb = usbd_alloc_urb (function, BULK_INT, 4, NULL); ++ BREAK_UNLESS(urb); ++ memcpy(urb->buffer, &ports_status, 4); ++ urb->actual_length = 4; ++ if (usbd_send_urb(urb)) { ++ printk(KERN_INFO"%s: usbd_send_urb failed\n", __FUNCTION__); ++ usbd_dealloc_urb (urb); ++ } ++ } ++ rh->ports_status = ports_status; ++ interruptible_sleep_on_timeout(&rh_wait_queue, 10000); ++ } ++#endif ++ printk(KERN_INFO"%s: exiting\n", __FUNCTION__); ++} ++ ++/* USB Device Functions ************************************************************************ */ ++ ++/* rh_event_irq - process a device event ++ */ ++void rh_event_irq (struct usbd_function_instance *function, usbd_device_event_t event, int data) ++{ ++ struct rh_private *rh = &rh_private; ++ ++ switch (event) { ++ case DEVICE_CONFIGURED: ++ rh->connected = 1; ++ ++ SET_WORK_ARG(rh->bh, &rh_private); ++// rh->bh.data = &rh_private; ++ SCHEDULE_WORK(rh_private.bh); ++// schedule_task(&rh_private.bh); ++ break; ++ ++ case DEVICE_RESET: ++ case DEVICE_DE_CONFIGURED: ++ BREAK_UNLESS(rh->connected); ++ rh->connected = 0; ++ rh->bh.data = 0; ++ break; ++ default: ++ break; ++ } ++} ++ ++/* 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 int copy_report (struct usbd_urb *urb, void *data, int size, int max_buf) ++{ ++ int available; ++ int length = size; ++ ++ //printk(KERN_INFO"%s: data: %p size: %d max: %d\n", __FUNCTION__, data, size, max_buf); ++ RETURN_EINVAL_UNLESS (urb); ++ RETURN_EINVAL_UNLESS (data); ++ RETURN_EINVAL_UNLESS (size); ++ ++ RETURN_EINVAL_IF ((available = max_buf - urb->actual_length) <= 0); ++ ++ //printk(KERN_INFO"%s: length: %d available: %d\n", __FUNCTION__, length, available); ++ ++ length = (length < available) ? length : available; ++ memcpy (urb->buffer + urb->actual_length, data, length); ++ urb->actual_length += length; ++ return 0; ++} ++ ++/* rh_recv_setup_irq - called to indicate urb has been received ++ */ ++int rh_recv_setup_irq (struct usbd_function_instance *function, struct usbd_device_request *request) ++{ ++ struct usbd_bus_instance *bus = function->bus; ++ u8 bRequest = request->bRequest; ++ u8 bmRequestType = request->bmRequestType; ++ u16 wValue = le16_to_cpu(request->wValue); ++ u16 wIndex = le16_to_cpu(request->wIndex); ++ u16 wLength = le16_to_cpu(request->wLength); ++ ++ u8 direction = bmRequestType & USB_REQ_DIRECTION_MASK; ++ u8 recipient = bmRequestType & USB_REQ_RECIPIENT_MASK; ++ ++ //printk(KERN_INFO"%s:\n", __FUNCTION__); ++ /* verify that this is a class request ++ */ ++ RETURN_EINVAL_UNLESS((request->bmRequestType & USB_REQ_TYPE_MASK) == USB_REQ_TYPE_CLASS); ++ ++ //printk(KERN_INFO"%s: valid direction: %x recipient: %x bRequest: %02x\n", ++ // __FUNCTION__, direction, recipient, bRequest); ++ ++ if (USB_REQ_HOST2DEVICE == direction) { ++ ++ //printk(KERN_INFO"%s: H2D\n", __FUNCTION__); ++ ++ /* these do not require a reply ++ */ ++ int setclear_flag = USB_REQ_SET_FEATURE == bRequest; ++ switch (recipient) { ++ case USB_RECIP_HUB: ++ switch (request->bRequest) { ++ case USB_REQ_CLEAR_FEATURE: ++ case USB_REQ_SET_FEATURE: ++ bus->driver->bops->hub_feature(bus, wValue, setclear_flag); ++ } ++ return 0; ++ case USB_RECIP_PORT: ++ switch (request->bRequest) { ++ case USB_REQ_CLEAR_FEATURE: ++ case USB_REQ_SET_FEATURE: ++ bus->driver->bops->port_feature(bus, wValue, wIndex, setclear_flag); ++#if 0 ++ case USB_REQ_CLEAR_TT_BUFFER: ++ case USB_REQ_RESET_TT: ++ case USB_REQ_STOP_TT: ++#endif ++ } ++ return 0; ++ } ++ } ++ else { ++ //printk(KERN_INFO"%s: D2H\n", __FUNCTION__); ++ ++ /* these do require a reply ++ */ ++ struct usbd_urb *urb; ++ int rc = 0; ++ u32 status; ++ ++ RETURN_EINVAL_UNLESS(wLength); ++ RETURN_EINVAL_UNLESS((urb = usbd_alloc_urb_ep0(function, wLength, NULL))); ++ ++ switch (recipient) { ++ case USB_RECIP_HUB: ++ //printk(KERN_INFO"%s: HUB\n", __FUNCTION__); ++ switch (request->bRequest) { ++ case USB_REQ_GET_DESCRIPTOR: ++ BREAK_UNLESS (copy_report(urb, (void *)&rh_hub_descriptor, sizeof(rh_hub_descriptor), wLength)); ++ rc = -EINVAL; ++ break; ++ ++ case USB_REQ_GET_STATUS: ++ status = cpu_to_le16(bus->driver->bops->hub_status(bus)); ++ urb->actual_length = 4; ++ memcpy(urb->buffer, &status, 4); ++ break; ++ } ++ break; ++ case USB_RECIP_PORT: ++ //printk(KERN_INFO"%s: PORT\n", __FUNCTION__); ++ switch (request->bRequest) { ++ case USB_REQ_GET_STATUS: ++ status = cpu_to_le16(bus->driver->bops->port_status(bus, wIndex)); ++ urb->actual_length = 4; ++ memcpy(urb->buffer, &status, 4); ++ break; ++#if 0 ++ case USB_REQ_GET_TT_STATE: ++#endif ++ } ++ break; ++ default: ++ rc = -EINVAL; ++ } ++ UNLESS (rc) ++ RETURN_ZERO_IF(!usbd_send_urb(urb)); ++ /* error */ ++ usbd_dealloc_urb(urb); ++ } ++ return -EINVAL; ++} ++ ++ ++static int rh_function_enable (struct usbd_function_instance *function) ++{ ++ struct rh_private *rh = &rh_private; ++ struct usbd_bus_instance *bus = function->bus; ++ int ports; ++ ++ printk (KERN_INFO "%s:\n", __FUNCTION__); ++ ++ if (rh->port_features) ++ lkfree(rh->port_features); ++ ++ rh->ports = ports = bus->driver->ports; ++ rh->port_features = ckmalloc(4 * ports, GFP_KERNEL); ++ rh_private.bh.data = &rh_private; ++ ++ rh_hub_descriptor.bNbrPorts = ports; ++ ++ printk (KERN_INFO "%s: ports: %d\n", __FUNCTION__, ports); ++ ++ //schedule_task(&rh_private.bh); ++ ++ printk (KERN_INFO "%s: finished\n", __FUNCTION__); ++ ++ return 0; ++} ++ ++static void rh_function_disable (struct usbd_function_instance *function) ++{ ++ struct rh_private *rh = &rh_private; ++ printk (KERN_INFO "%s:\n", __FUNCTION__); ++ if (rh->port_features) ++ lkfree(rh->port_features); ++ printk (KERN_INFO "%s: finished\n", __FUNCTION__); ++} ++ ++#if defined(OTG_WINCE) ++ ++struct otg_instance otg_instance_private; ++ ++void hub_ops_init(void) ++{ ++ ZERO(otg_instance_private); ++ otg_instance_private.hcd = &hcd_instance_privatej ++} ++ ++#else /* defined(OTG_WINCE) */ ++ ++static struct usbd_function_operations function_ops = { ++ event_irq: rh_event_irq, ++ recv_setup_irq: rh_recv_setup_irq, ++ function_enable: rh_function_enable, ++ function_disable: rh_function_disable, ++}; ++ ++static struct usbd_function_driver function_driver = { ++ name:"root hub", ++ fops:&function_ops, ++ device_description:&rh_device_description, ++ bNumConfigurations:sizeof (rh_description) / sizeof (struct usbd_configuration_description), ++ configuration_description:rh_description, ++ idVendor: __constant_cpu_to_le16(0x15ec), ++ idProduct: __constant_cpu_to_le16(0xf010), ++ bcdDevice: __constant_cpu_to_le16(0x000), ++}; ++#endif /* defined(OTG_WINCE) */ ++ ++/* USB Module init/exit ************************************************************************ */ ++#if defined(OTG_WINCE) ++ ++ ++#else /* defined(OTG_WINCE) */ ++ ++ ++/* ++ * rh_modinit - module init ++ */ ++int rh_init (void) ++{ ++ printk (KERN_INFO "%s:\n", __FUNCTION__); ++ ++ THROW_IF (usbd_register_function (&function_driver), error); ++ ++ printk (KERN_INFO "%s: registered\n", __FUNCTION__); ++ ++ rh_private.usb_driver_registered++; ++ ++// rh_private.bh.sync = 0; ++// rh_private.bh.routine = rh_task; ++ PREPARE_WORK_ITEM(rh_private.bh,rh_task,NULL); ++ ++ CATCH(error) { ++ if (rh_private.usb_driver_registered) { ++ usbd_deregister_function (&function_driver); ++ rh_private.usb_driver_registered = 0; ++ } ++ return -EINVAL; ++ } ++ printk (KERN_INFO "%s: finished\n", __FUNCTION__); ++ return 0; ++} ++ ++/* rh_modexit - module cleanup ++ */ ++void rh_exit (void) ++{ ++ //while (rh_private.bh.sync) ++ while (PENDING_WORK_ITEM(rh_private.bh)) ++ schedule_timeout(HZ); ++ ++ //rh_private.bh.data = 0; ++ SET_WORK_ARG(rh_private.bh,0); ++ while (PENDING_WORK_ITEM(rh_private.bh)) ++ schedule_timeout(HZ); ++ ++ if (rh_private.usb_driver_registered) ++ usbd_deregister_function (&function_driver); ++} ++#endif /* defined(OTG_WINCE) */ ++#endif /* CONFIG_OTG_ROOT_HUB */ ++ +diff -uNr linux/drivers/no-otg/otgcore/otg-fw-df.c linux/drivers/otg/otgcore/otg-fw-df.c +--- linux/drivers/no-otg/otgcore/otg-fw-df.c 1970-01-01 01:00:00.000000000 +0100 ++++ linux/drivers/otg/otgcore/otg-fw-df.c 2006-09-01 21:41:34.000000000 +0200 +@@ -0,0 +1,121 @@ ++ ++/* Generated by otg-tests-c.awk ++ * ++ * Do not Edit, see otg-state.awk ++ */ ++ ++ ++/*! ++* @file otg/otgcore/otg-fw-df.c ++* @brief OTG Firmware - Firmware for df ++* ++* This file defines the OTG State Machine tests. ++* ++* ++* @ingroup OTGFW ++*/ ++ ++/*! ++* @page OTGFW ++* @section OTGFW - otg-fw-df.c ++* This contains the input, output and timout definitions for the OTG state machine firmware ++*/ ++ ++ ++#ifdef OTG_APPLICATION ++#define NULL 0 ++#include "otg-fw.h" ++#include "otg-fw-df.h" ++#else /* OTG_APPLICATION/ */ ++#include <otg/otg-compat.h> ++#include <otg/usbp-chap9.h> ++#include <otg/usbp-func.h> ++#include <otg/usbp-bus.h> ++#include <otg/otg-trace.h> ++//#include <otg/otg-api.h> ++#include <otg/otg-fw.h> ++#include <otg/otg-fw-df.h> ++#endif /* OTG_APPLICATION */ ++ ++char otg_fw_name_df[] = "otg_df"; ++ ++ ++struct otg_test otg_tests_df[] = { ++ /* ++ * Copyright (c) 2004 Belcarra ++ * ++ */ ++ /*! ++ * This is the default Firmware. It is included in the ++ * compiled modules and supports the auto Traditional USB ++ * mode. No user inputs are implemented. ++ */ ++ { /* */ ++ 0, /* .test */ ++ invalid_state, /* .state */ ++ otg_disabled, /* .target */ ++ enable_otg, /* .test1 */ ++ }, ++ /* ++ * This is not an OTG State. It is used internally to mark the end of the ++ * list of states and inputs. ++ */ ++ { /* */ ++ 1, /* .test */ ++ terminator_state, /* .state */ ++ invalid_state, /* .target */ ++ 0, /* .test1 */ ++ }, ++ {2, invalid_state,}, ++ ++}; ++ ++#define OTG_TESTS_DF 2 ++ ++int otg_test_max_df = 2; ++ ++ /* eof */ ++ ++/* Generated by otg-info-c.awk ++ * ++ * Do not Edit, see otg-state.awk ++ */ ++ ++struct otg_state otg_states_df[OTG_STATES_DF + 1] = { ++ { /* 0 */ ++ invalid_state, /* .state */ ++ m_otg_init, /* .meta */ ++ "invalid_state", /* .name */ ++ 0, /* .tmout */ ++ 0, /* .reset */ ++ }, ++ { /* 1 */ ++ otg_disabled, /* .state */ ++ m_otg_init, /* .meta */ ++ "otg_disabled", /* .name */ ++ 0, /* .tmout */ ++ 0, /* .reset */ ++ }, ++ { /* 2 */ ++ terminator_state, /* .state */ ++ m_otg_init, /* .meta */ ++ "terminator_state", /* .name */ ++ 0, /* .tmout */ ++ 0, /* .reset */ ++ /* .outputs */ ++ 0, ++ }, ++ ++ {0, 0, "", 0, 0,}, ++ ++}; ++ ++struct otg_firmware otg_firmware_df = { ++ OTG_STATES_DF, /* number of states */ ++ OTG_TESTS_DF, /* number of tests */ ++ "otg-df", /* name of firmware */ ++ otg_states_df, /* struct otg_state * */ ++ otg_tests_df, /* struct otg_test * */ ++}; ++ ++/* eof */ +diff -uNr linux/drivers/no-otg/otgcore/otg-fw-mn.c linux/drivers/otg/otgcore/otg-fw-mn.c +--- linux/drivers/no-otg/otgcore/otg-fw-mn.c 1970-01-01 01:00:00.000000000 +0100 ++++ linux/drivers/otg/otgcore/otg-fw-mn.c 2006-09-01 21:41:34.000000000 +0200 +@@ -0,0 +1,511 @@ ++ ++/* Generated by otg-tests-c.awk ++ * ++ * Do not Edit, see otg-state.awk ++ */ ++ ++ ++/*! ++* @file otg/otgcore/otg-fw-mn.c ++* @brief OTG Firmware - Firmware for mn ++* ++* This file defines the OTG State Machine tests. ++* ++* ++* @ingroup OTGFW ++*/ ++ ++/*! ++* @page OTGFW ++* @section OTGFW - otg-fw-mn.c ++* This contains the input, output and timout definitions for the OTG state machine firmware ++*/ ++ ++ ++#ifdef OTG_APPLICATION ++#define NULL 0 ++#include "otg-fw.h" ++#include "otg-fw-mn.h" ++#else /* OTG_APPLICATION/ */ ++#include <otg/otg-compat.h> ++#include <otg/usbp-chap9.h> ++#include <otg/usbp-func.h> ++#include <otg/usbp-bus.h> ++#include <otg/otg-trace.h> ++//#include <otg/otg-api.h> ++#include <otg/otg-fw.h> ++#include <otg/otg-fw-mn.h> ++#endif /* OTG_APPLICATION */ ++ ++char otg_fw_name_mn[] = "otg_mn"; ++ ++ ++struct otg_test otg_tests_mn[] = { ++ /* ++ * Copyright (c) 2004 Belcarra ++ */ ++ /*! ++ * This is the initialization set for pcd and tcd. ++ */ ++ /* ++ * This the initial state of the software when first loaded. ++ * It is not possible to return to this state. ++ */ ++ { /* Initialize by sending the otg_enable signal. */ ++ 0, /* .test */ ++ invalid_state, /* .state */ ++ otg_disabled, /* .target */ ++ enable_otg, /* .test1 */ ++ }, ++ /* ++ * The USBOTG State Machine has been initialized but is inactive. ++ * This state may have arrived at from either the invalid_state or ++ * from the otg_disable state. ++ */ ++ { /* Initialize by sending the otg_enable signal. */ ++ 1, /* .test */ ++ otg_disabled, /* .state */ ++ otg_enable_pcd, /* .target */ ++ enable_otg, /* .test1 */ ++ }, ++ /* ++ * The State Machine stops the device drivers and waits for them ++ * to signal that they have finished de-initializing. ++ * ++ * This state disables the TCD driver and waits for TCD ok. ++ */ ++ { /* Wait for ok from de-initializing drivers. */ ++ 2, /* .test */ ++ otg_disable_tcd, /* .state */ ++ otg_disable_pcd, /* .target */ ++ TCD_OK, /* .test1 */ ++ }, ++ /* ++ * The State Machine stops the device drivers and waits for them ++ * to signal that they have finished de-initializing. ++ * ++ * This state disables the PCD driver and waits for PCD ok. ++ */ ++ { /* Wait for ok from de-initializing drivers. */ ++ 3, /* .test */ ++ otg_disable_pcd, /* .state */ ++ otg_disabled, /* .target */ ++ PCD_OK, /* .test1 */ ++ }, ++ /* ++ * The State Machine starts the device drivers and waits for them ++ * to signal that they have finished initializing. ++ * ++ * This state enables the PCD driver and waits for PCD ok. ++ */ ++ { /* Wait for ok from initializing drivers. */ ++ 4, /* .test */ ++ otg_enable_pcd, /* .state */ ++ otg_enable_tcd, /* .target */ ++ PCD_OK, /* .test1 */ ++ }, ++ /* ++ * The State Machine starts the device drivers and waits for them ++ * to signal that they have finished initializing. ++ * ++ * This state enables the PCD driver and waits for PCD ok. ++ */ ++ { /* Wait for ok from initializing drivers. */ ++ 5, /* .test */ ++ otg_enable_tcd, /* .state */ ++ otg_enabled, /* .target */ ++ TCD_OK, /* .test1 */ ++ }, ++ /* ++ * Copyright (c) 2004 Belcarra ++ * ++ */ ++ /*! ++ * This is the minimal firmware. It can be included in the ++ * compiled modules and supports the auto Traditional USB ++ * mode. No user inputs are required for normal operation. ++ * ++ * The b_bus_drop input can be optionally used to disconnect and re-connect. ++ * ++ * The enable_otg input can be optionally used to disable and re-enable. ++ * Note that disable/enable will reset b_bus_drop. ++ * ++ */ ++ /*! ++ * The State Machine has successfully started the device drivers and ++ * is waiting for an input event. Typically it will move from here to ++ * an idle state specific to the current conditions (a_idle, b_idle, mn_idle etc.) ++ * based on user request b_bus_drop. ++ * ++ */ ++ { /* Check for disable. */ ++ 6, /* .test */ ++ otg_enabled, /* .state */ ++ otg_disable_tcd, /* .target */ ++ enable_otg_, /* .test1 */ ++ }, ++ { /* Move to idle. */ ++ 7, /* .test */ ++ otg_enabled, /* .state */ ++ mn_idle, /* .target */ ++ AUTO | AUTO_, /* .test1 */ ++ }, ++ /*! ++ * The State Machine is in the idle state for a Traditional USB Device. ++ * It is waiting for Vbus to indicate that it has been plugged into a USB Host. ++ * ++ */ ++ { /* Check for disable (must be done for check for bus_drop.) */ ++ 8, /* .test */ ++ mn_idle, /* .state */ ++ otg_disable_tcd, /* .target */ ++ enable_otg_, /* .test1 */ ++ }, ++ { /* Check for b_bus_drop */ ++ 9, /* .test */ ++ mn_idle, /* .state */ ++ mn_bus_drop, /* .target */ ++ b_bus_drop, /* .test1 */ ++ }, ++ { /* move to peripheral mode when SESSION valid. */ ++ 10, /* .test */ ++ mn_idle, /* .state */ ++ mn_peripheral, /* .target */ ++ b_bus_drop_, /* .test1 */ ++ B_SESS_VLD, /* .test2 */ ++ }, ++ /*! ++ * The State Machine is in the idle state for a Traditional USB Device. ++ * The B-Device applications has requested that the bus connection be dropped. ++ * Wait for either enable_otg reset or b_bus_drop reset. ++ */ ++ { /* Wait for b_bus_drop/ or enable_otg/ */ ++ 11, /* .test */ ++ mn_bus_drop, /* .state */ ++ mn_idle, /* .target */ ++ b_bus_drop_ | enable_otg_, /* .test1 */ ++ }, ++ /*! ++ * The State Machine in the peripheral state for a Traditional USB Device. ++ * The D+ pullup is enabled and we are waiting for a BUS_RESET to ++ * indicate that the USB Host has recognized that a USB Device is attached. ++ */ ++ { /* Move to idle if we loose any of these inputs. */ ++ 12, /* .test */ ++ mn_peripheral, /* .state */ ++ mn_idle, /* .target */ ++ enable_otg_ | B_SESS_VLD_ | b_bus_drop, /* .test1 */ ++ }, ++ { /* Move to next state if bus reset is seen. */ ++ 13, /* .test */ ++ mn_peripheral, /* .state */ ++ mn_bus_reset, /* .target */ ++ BUS_RESET, /* .test1 */ ++ }, ++ /*! ++ * The State Machine in the bus_reset state for a Traditional USB Device. ++ * It is waiting to be enumerated and configured by the USB Host. ++ */ ++ { /* Move to idle via discharge, if we loose any of these inputs. */ ++ 14, /* .test */ ++ mn_bus_reset, /* .state */ ++ mn_discharge, /* .target */ ++ enable_otg_ | B_SESS_VLD_ | b_bus_drop, /* .test1 */ ++ }, ++ { /* Progress if we are addressed. */ ++ 15, /* .test */ ++ mn_bus_reset, /* .state */ ++ mn_addressed, /* .target */ ++ ADDRESSED, /* .test1 */ ++ }, ++ /*! ++ * The State Machine in the configured state for a Traditional USB Device. ++ * This means that there is an active session, there is packet traffic ++ * with this device. ++ */ ++ { /* Move to idle via discharge, if we loose any of these inputs. */ ++ 16, /* .test */ ++ mn_addressed, /* .state */ ++ mn_discharge, /* .target */ ++ enable_otg_ | B_SESS_VLD_ | b_bus_drop, /* .test1 */ ++ }, ++ { /* Progress if we are configured. */ ++ 17, /* .test */ ++ mn_addressed, /* .state */ ++ mn_configured, /* .target */ ++ CONFIGURED, /* .test1 */ ++ }, ++ /*! ++ * The State Machine in the configured state for a Traditional USB Device. ++ * This means that there is an active session, there is packet traffic ++ * with this device. ++ */ ++ { /* */ ++ 18, /* .test */ ++ mn_configured, /* .state */ ++ mn_suspended, /* .target */ ++ BUS_SUSPENDED, /* .test1 */ ++ }, ++ { /* Move to idle via discharge, if we loose any of these inputs. */ ++ 19, /* .test */ ++ mn_configured, /* .state */ ++ mn_discharge, /* .target */ ++ enable_otg_ | B_SESS_VLD_ | b_bus_drop, /* .test1 */ ++ }, ++ /*! ++ * The State Machine in the discharge state for a Traditional USB Device. ++ * The device has been unplugged. The Vbus discharge resistor will be enabled ++ * for the TLDISC_DSCHRG time period. ++ */ ++ { /* Progress to idle on timeout. */ ++ 20, /* .test */ ++ mn_discharge, /* .state */ ++ mn_idle, /* .target */ ++ Tldisc_dschrg, /* .test1 */ ++ }, ++ /*! ++ * The State Machine in the suspend state for a Traditional USB Device. ++ */ ++ { /* Move to idle via discharge, if we loose any of these inputs. */ ++ 21, /* .test */ ++ mn_suspended, /* .state */ ++ mn_discharge, /* .target */ ++ enable_otg_ | B_SESS_VLD_ | b_bus_drop, /* .test1 */ ++ }, ++ { /* Check for a resumed bus. */ ++ 22, /* .test */ ++ mn_suspended, /* .state */ ++ mn_configured, /* .target */ ++ BUS_SUSPENDED_, /* .test1 */ ++ }, ++ { /* Is remote wakeup enabled? */ ++ 23, /* .test */ ++ mn_suspended, /* .state */ ++ mn_wakeup_enabled, /* .target */ ++ REMOTE_WAKEUP_ENABLED, /* .test1 */ ++ }, ++ /*! ++ * The State Machine in the suspend state for a Traditional USB Device, ++ * prior to suspended the USB Host enabled Remote Wakeup by sending a ++ * set REMOTE WAKUP request. ++ */ ++ { /* Move to idle via discharge, if we loose any of these inputs. */ ++ 24, /* .test */ ++ mn_wakeup_enabled, /* .state */ ++ mn_discharge, /* .target */ ++ enable_otg_ | b_bus_req_ | B_SESS_VLD_, /* .test1 */ ++ }, ++ { /* Check for a resumed bus. */ ++ 25, /* .test */ ++ mn_wakeup_enabled, /* .state */ ++ mn_suspended, /* .target */ ++ BUS_SUSPENDED_, /* .test1 */ ++ }, ++ { /* Remote wakeup requested? */ ++ 26, /* .test */ ++ mn_wakeup_enabled, /* .state */ ++ mn_wakeup, /* .target */ ++ remote_wakeup_cmd, /* .test1 */ ++ }, ++ /*! ++ * The State Machine in the wakeup state for a Traditional USB Device, ++ * The REMOTE WAKEUP procedure will be performed. ++ */ ++ { /* Automatic return. */ ++ 27, /* .test */ ++ mn_wakeup, /* .state */ ++ mn_wakeup_enabled, /* .target */ ++ AUTO | AUTO_, /* .test1 */ ++ }, ++ /*! ++ * This is not an OTG State. It is used internally to mark the end of the ++ * list of states and inputs. ++ */ ++ { /* */ ++ 28, /* .test */ ++ terminator_state, /* .state */ ++ invalid_state, /* .target */ ++ 0, /* .test1 */ ++ }, ++ {29, invalid_state,}, ++ ++}; ++ ++#define OTG_TESTS_MN 29 ++ ++int otg_test_max_mn = 29; ++ ++ /* eof */ ++ ++/* Generated by otg-info-c.awk ++ * ++ * Do not Edit, see otg-state.awk ++ */ ++ ++struct otg_state otg_states_mn[OTG_STATES_MN + 1] = { ++ { /* 0 */ ++ invalid_state, /* .state */ ++ m_otg_init, /* .meta */ ++ "invalid_state", /* .name */ ++ 0, /* .tmout */ ++ 0, /* .reset */ ++ }, ++ { /* 1 */ ++ otg_disabled, /* .state */ ++ m_otg_init, /* .meta */ ++ "otg_disabled", /* .name */ ++ 0, /* .tmout */ ++ enable_otg_, /* .reset */ ++ /* .outputs */ ++ chrg_vbus_out_ | drv_vbus_out_ | dp_pullup_out_ | loc_sof_out_, ++ }, ++ { /* 2 */ ++ otg_disable_tcd, /* .state */ ++ m_otg_init, /* .meta */ ++ "otg_disable_tcd", /* .name */ ++ 0, /* .tmout */ ++ TCD_OK_, /* .reset */ ++ /* .outputs */ ++ tcd_init_out_, ++ }, ++ { /* 3 */ ++ otg_disable_pcd, /* .state */ ++ m_otg_init, /* .meta */ ++ "otg_disable_pcd", /* .name */ ++ 0, /* .tmout */ ++ PCD_OK_, /* .reset */ ++ /* .outputs */ ++ pcd_init_out_, ++ }, ++ { /* 4 */ ++ otg_enable_pcd, /* .state */ ++ m_otg_init, /* .meta */ ++ "otg_enable_pcd", /* .name */ ++ 0, /* .tmout */ ++ PCD_OK_, /* .reset */ ++ /* .outputs */ ++ chrg_vbus_out_ | drv_vbus_out_ | dp_pullup_out_ | loc_sof_out_ | pcd_init_out, ++ }, ++ { /* 5 */ ++ otg_enable_tcd, /* .state */ ++ m_otg_init, /* .meta */ ++ "otg_enable_tcd", /* .name */ ++ 0, /* .tmout */ ++ TCD_OK_, /* .reset */ ++ /* .outputs */ ++ chrg_vbus_out_ | drv_vbus_out_ | dp_pullup_out_ | loc_sof_out_ | tcd_init_out, ++ }, ++ { /* 6 */ ++ otg_enabled, /* .state */ ++ m_otg_init, /* .meta */ ++ "otg_enabled", /* .name */ ++ 0, /* .tmout */ ++ b_bus_drop_, /* .reset */ ++ }, ++ { /* 7 */ ++ mn_idle, /* .state */ ++ m_b_idle, /* .meta */ ++ "mn_idle", /* .name */ ++ 0, /* .tmout */ ++ 0, /* .reset */ ++ /* .outputs */ ++ dp_pullup_out_ | pcd_en_out_ | tcd_en_out_power | dischrg_vbus_out_, ++ }, ++ { /* 8 */ ++ mn_bus_drop, /* .state */ ++ m_b_idle, /* .meta */ ++ "mn_bus_drop", /* .name */ ++ 0, /* .tmout */ ++ 0, /* .reset */ ++ }, ++ { /* 9 */ ++ mn_peripheral, /* .state */ ++ m_b_peripheral, /* .meta */ ++ "mn_peripheral", /* .name */ ++ 0, /* .tmout */ ++ BUS_RESET_, /* .reset */ ++ /* .outputs */ ++ dp_pullup_out | pcd_en_out | dischrg_vbus_out_, ++ }, ++ { /* 10 */ ++ mn_bus_reset, /* .state */ ++ m_b_peripheral, /* .meta */ ++ "mn_bus_reset", /* .name */ ++ 0, /* .tmout */ ++ BUS_RESET_ | ADDRESSED_, /* .reset */ ++ }, ++ { /* 11 */ ++ mn_addressed, /* .state */ ++ m_b_peripheral, /* .meta */ ++ "mn_addressed", /* .name */ ++ 0, /* .tmout */ ++ ADDRESSED_ | CONFIGURED_, /* .reset */ ++ /* .outputs */ ++ dp_pullup_out | pcd_en_out | dischrg_vbus_out_, ++ }, ++ { /* 12 */ ++ mn_configured, /* .state */ ++ m_b_peripheral, /* .meta */ ++ "mn_configured", /* .name */ ++ 0, /* .tmout */ ++ CONFIGURED_ | BUS_SUSPENDED_, /* .reset */ ++ /* .outputs */ ++ dp_pullup_out | pcd_en_out | dischrg_vbus_out_, ++ }, ++ { /* 13 */ ++ mn_discharge, /* .state */ ++ m_b_peripheral, /* .meta */ ++ "mn_discharge", /* .name */ ++ TLDISC_DSCHRG, /* .tmout */ ++ 0, /* .reset */ ++ /* .outputs */ ++ dischrg_vbus_out | pcd_en_out_, ++ }, ++ { /* 14 */ ++ mn_suspended, /* .state */ ++ m_b_suspended, /* .meta */ ++ "mn_suspended", /* .name */ ++ 0, /* .tmout */ ++ 0, /* .reset */ ++ }, ++ { /* 15 */ ++ mn_wakeup_enabled, /* .state */ ++ m_b_suspended, /* .meta */ ++ "mn_wakeup_enabled", /* .name */ ++ 0, /* .tmout */ ++ remote_wakeup_cmd_, /* .reset */ ++ /* .outputs */ ++ remote_wakeup_out_, ++ }, ++ { /* 16 */ ++ mn_wakeup, /* .state */ ++ m_b_suspended, /* .meta */ ++ "mn_wakeup", /* .name */ ++ 0, /* .tmout */ ++ 0, /* .reset */ ++ /* .outputs */ ++ remote_wakeup_out, ++ }, ++ { /* 17 */ ++ terminator_state, /* .state */ ++ m_otg_init, /* .meta */ ++ "terminator_state", /* .name */ ++ 0, /* .tmout */ ++ 0, /* .reset */ ++ /* .outputs */ ++ 0, ++ }, ++ ++ {0, 0, "", 0, 0,}, ++ ++}; ++ ++struct otg_firmware otg_firmware_mn = { ++ OTG_STATES_MN, /* number of states */ ++ OTG_TESTS_MN, /* number of tests */ ++ "otg-mn", /* name of firmware */ ++ otg_states_mn, /* struct otg_state * */ ++ otg_tests_mn, /* struct otg_test * */ ++}; ++ ++/* eof */ +diff -uNr linux/drivers/no-otg/otgcore/otg-fw.c linux/drivers/otg/otgcore/otg-fw.c +--- linux/drivers/no-otg/otgcore/otg-fw.c 1970-01-01 01:00:00.000000000 +0100 ++++ linux/drivers/otg/otgcore/otg-fw.c 2006-09-01 21:41:34.000000000 +0200 +@@ -0,0 +1,251 @@ ++ ++/* Generated by otg-fw-c.awk ++ * ++ * Do not Edit, see otg-state.awk ++ */ ++ ++/*! ++* @file otg/otgcore/otg-fw.c ++* @brief OTG Firmware - Input, Output and Timeout definitions ++* ++* This file defines the OTG State Machine input, output and timeout constants. ++* ++* ++* @ingroup OTGFW ++*/ ++ ++/*! ++* @page OTGFW ++* @section OTGFW - otg-fw.c ++* This contains the input, output and timout definitions for the OTG state machine firmware ++*/ ++ ++ ++#ifdef OTG_APPLICATION ++#include "otg-fw.h" ++#else /* OTG_APPLICATION/ */ ++#include <otg/otg-compat.h> ++#include <otg/usbp-chap9.h> ++#include <otg/usbp-func.h> ++#include <otg/usbp-bus.h> ++#include <otg/otg-trace.h> ++#include <otg/otg-api.h> ++#endif /* OTG_APPLICATION */ ++ ++#define OTG_VERSION_FWC 200502152159L ++ ++ ++#if OTG_VERSION_FWC != OTG_VERSION_FW ++#error OTG_VERSION_FWC != OTG_VERSION_FW ++#endif /* OTG_VERSION_FWC != OTG_VERSION_FW */ ++ ++/* Generated by otg-input-name-c.awk ++ * ++ * Do not Edit, see otg-state.awk ++ */ ++ ++ ++/* #include "otg-fw.h" */ ++ ++#define OTG_VERSION_INPUT_TABLE 200502152159L ++ ++ ++#if OTG_VERSION_INPUT_TABLE != OTG_VERSION_FW ++#error OTG_VERSION_INPUT_TABLE != OTG_VERSION_FW ++#endif /* OTG_VERSION_INPUT_TABLE != OTG_VERSION_FW */ ++ ++ ++struct otg_input_name otg_input_names[] = { ++ {ID_GND, "ID_GND",}, ++ {ID_FLOAT, "ID_FLOAT",}, ++ {DP_HIGH, "DP_HIGH",}, ++ {DM_HIGH, "DM_HIGH",}, ++ {B_SESS_END, "B_SESS_END",}, ++ {A_SESS_VLD, "A_SESS_VLD",}, ++ {B_SESS_VLD, "B_SESS_VLD",}, ++ {VBUS_VLD, "VBUS_VLD",}, ++ {SRP_DET, "SRP_DET",}, ++ {SE0_DET, "SE0_DET",}, ++ {SE1_DET, "SE1_DET",}, ++ {BDIS_ACON, "BDIS_ACON",}, ++ {CR_INT_DET, "CR_INT_DET",}, ++ {HUB_PORT_CONNECT, "HUB_PORT_CONNECT",}, ++ {BUS_RESET, "BUS_RESET",}, ++ {ADDRESSED, "ADDRESSED",}, ++ {CONFIGURED, "CONFIGURED",}, ++ {NOT_SUPPORTED, "NOT_SUPPORTED",}, ++ {BUS_SUSPENDED, "BUS_SUSPENDED",}, ++ {a_bcon_no_tmout_req, "a_bcon_no_tmout_req",}, ++ {a_hpwr_req, "a_hpwr_req",}, ++ {bus_drop, "bus_drop",}, ++ {a_bus_drop, "a_bus_drop",}, ++ {b_bus_drop, "b_bus_drop",}, ++ {bus_req, "bus_req",}, ++ {a_bus_req, "a_bus_req",}, ++ {b_bus_req, "b_bus_req",}, ++ {b_sess_req, "b_sess_req",}, ++ {suspend_req, "suspend_req",}, ++ {a_suspend_req, "a_suspend_req",}, ++ {b_suspend_req, "b_suspend_req",}, ++ {set_remote_wakeup_cmd, "set_remote_wakeup_cmd",}, ++ {remote_wakeup_cmd, "remote_wakeup_cmd",}, ++ {reset_remote_wakeup_cmd, "reset_remote_wakeup_cmd",}, ++ {clr_err_cmd, "clr_err_cmd",}, ++ {b_hnp_cmd, "b_hnp_cmd",}, ++ {ph_int_cmd, "ph_int_cmd",}, ++ {ph_audio_cmd, "ph_audio_cmd",}, ++ {cr_int_cmd, "cr_int_cmd",}, ++ {led_on_cmd, "led_on_cmd",}, ++ {led_off_cmd, "led_off_cmd",}, ++ {HNP_ENABLED, "HNP_ENABLED",}, ++ {HNP_CAPABLE, "HNP_CAPABLE",}, ++ {HNP_SUPPORTED, "HNP_SUPPORTED",}, ++ {REMOTE_WAKEUP_ENABLED, "REMOTE_WAKEUP_ENABLED",}, ++ {REMOTE_CAPABLE, "REMOTE_CAPABLE",}, ++ {PCD_OK, "PCD_OK",}, ++ {TCD_OK, "TCD_OK",}, ++ {HCD_OK, "HCD_OK",}, ++ {OCD_OK, "OCD_OK",}, ++ {TMOUT, "TMOUT",}, ++ {enable_otg, "enable_otg",}, ++ {AUTO, "AUTO",}, ++ {0, "0",}, ++ ++}; ++ ++ /* eof */ ++ ++/* Generated by otg-ioctls-names-c.awk ++ * ++ * Do not Edit, see otg-state.awk ++ */ ++#if defined(OTG_LINUX) || defined(OTG_WINCE) ++ ++ ++/* #include "otg-fw.h" */ ++ ++#define OTG_VERSION_IOCTL_TABLE 200502152159L ++ ++ ++#if OTG_VERSION_IOCTL_TABLE != OTG_VERSION_FW ++#error OTG_VERSION_IOCTL_TABLE != OTG_VERSION_FW ++#endif /* OTG_VERSION_IOCTL_TABLE != OTG_VERSION_FW */ ++ ++struct otg_ioctl_name otg_ioctl_names[] = { ++ {OTGADMIN_A_BCON_NO_TMOUT_REQ, a_bcon_no_tmout_req, "OTGADMIN_A_BCON_NO_TMOUT_REQ",}, ++ {OTGADMIN_A_HPWR_REQ, a_hpwr_req, "OTGADMIN_A_HPWR_REQ",}, ++ {OTGADMIN_BUS_DROP, bus_drop, "OTGADMIN_BUS_DROP",}, ++ {OTGADMIN_A_BUS_DROP, a_bus_drop, "OTGADMIN_A_BUS_DROP",}, ++ {OTGADMIN_B_BUS_DROP, b_bus_drop, "OTGADMIN_B_BUS_DROP",}, ++ {OTGADMIN_BUS_REQ, bus_req, "OTGADMIN_BUS_REQ",}, ++ {OTGADMIN_A_BUS_REQ, a_bus_req, "OTGADMIN_A_BUS_REQ",}, ++ {OTGADMIN_B_BUS_REQ, b_bus_req, "OTGADMIN_B_BUS_REQ",}, ++ {OTGADMIN_B_SESS_REQ, b_sess_req, "OTGADMIN_B_SESS_REQ",}, ++ {OTGADMIN_SUSPEND_REQ, suspend_req, "OTGADMIN_SUSPEND_REQ",}, ++ {OTGADMIN_A_SUSPEND_REQ, a_suspend_req, "OTGADMIN_A_SUSPEND_REQ",}, ++ {OTGADMIN_B_SUSPEND_REQ, b_suspend_req, "OTGADMIN_B_SUSPEND_REQ",}, ++ {OTGADMIN_SET_REMOTE_WAKEUP_CMD, set_remote_wakeup_cmd, "OTGADMIN_SET_REMOTE_WAKEUP_CMD",}, ++ {OTGADMIN_REMOTE_WAKEUP_CMD, remote_wakeup_cmd, "OTGADMIN_REMOTE_WAKEUP_CMD",}, ++ {OTGADMIN_RESET_REMOTE_WAKEUP_CMD, reset_remote_wakeup_cmd, "OTGADMIN_RESET_REMOTE_WAKEUP_CMD",}, ++ {OTGADMIN_CLR_ERR_CMD, clr_err_cmd, "OTGADMIN_CLR_ERR_CMD",}, ++ {OTGADMIN_B_HNP_CMD, b_hnp_cmd, "OTGADMIN_B_HNP_CMD",}, ++ {OTGADMIN_PH_INT_CMD, ph_int_cmd, "OTGADMIN_PH_INT_CMD",}, ++ {OTGADMIN_PH_AUDIO_CMD, ph_audio_cmd, "OTGADMIN_PH_AUDIO_CMD",}, ++ {OTGADMIN_CR_INT_CMD, cr_int_cmd, "OTGADMIN_CR_INT_CMD",}, ++ {OTGADMIN_LED_ON_CMD, led_on_cmd, "OTGADMIN_LED_ON_CMD",}, ++ {OTGADMIN_LED_OFF_CMD, led_off_cmd, "OTGADMIN_LED_OFF_CMD",}, ++ {OTGADMIN_ENABLE_OTG, enable_otg, "OTGADMIN_ENABLE_OTG",}, ++ {0, 0, "",}, ++ ++}; ++ ++ /* eof */ ++#endif /* defined(OTG_LINUX) || defined(OTG_WINCE) */ ++ ++/* Generated by otg-output-names-c.awk ++ * ++ * Do not Edit, see otg-state.awk ++ */ ++ ++char *otg_output_names[] = { ++ "tcd_init_out", /* 0 Initiate Transceiver Controller Driver Initialization (or De-initialization.) */ ++ "pcd_init_out", /* 1 Initiate Peripheral Controller Driver Initialization (or De-initialization.) */ ++ "hcd_init_out", /* 2 Initiate Host Controller Driver Initialization (or De-initialization). */ ++ "ocd_init_out", /* 3 Initiate OTG Controller Driver Initialization (or De-initialization). */ ++ "tcd_en_out", /* 4 Enable Transceiver Controller Driver */ ++ "pcd_en_out", /* 5 Enable Peripheral Controller Driver */ ++ "hcd_en_out", /* 6 Enable Host Controller Driver */ ++ "drv_vbus_out", /* 7 A-Device will Drive Vbus to 5V through charge pump. */ ++ "chrg_vbus_out", /* 8 B-Device will charge Vbus to 3.3V through resistor (SRP.) */ ++ "dischrg_vbus_out", /* 9 B-Device will discharge Vbus (enable dischage resistor.) */ ++ "dm_pullup_out", /* 10 DM pullup control - aka loc_carkit */ ++ "dm_pulldown_out", /* 11 DM pulldown control */ ++ "dp_pullup_out", /* 12 DP pullup control - aka loc_conn */ ++ "dp_pulldown_out", /* 13 DP pulldown control */ ++ "clr_overcurrent_out", /* 14 Clear overcurrent indication */ ++ "dm_det_out", /* 15 Enable B-Device D- High detect */ ++ "dp_det_out", /* 16 Enable B-Device D+ High detect */ ++ "cr_det_out", /* 17 Enable D+ CR detect */ ++ "charge_pump_out", /* 18 Enable external charge pump. */ ++ "bdis_acon_out", /* 19 Enable auto A-connect after B-disconnect. */ ++ "mx21_vbus_drain", /* 20 MX21 hack */ ++ "id_pulldown_out", /* 21 Enable the ID to ground pulldown ( (CEA-936 - 5 wire carkit.) */ ++ "uart_out", /* 22 Enable Transparent UART mode (CEA-936.) */ ++ "audio_out", /* 23 Enable Audio mode (CEA-936 CarKit interrupt detector.) */ ++ "mono_out", /* 24 Enable Mono-Audio mode (CEA-936.) */ ++ "remote_wakeup_out", /* 25 Peripheral will perform remote wakeup. */ ++ "loc_sof_out", /* 26 Host will enable packet traffic. */ ++ "loc_suspend_out", /* 27 Host will suspend bus. */ ++ "remote_wakeup_en_out", /* 28 Host will send remote wakeup enable or disable request. */ ++ "hnp_en_out", /* 29 Host will send HNP enable request. */ ++ "hpwr_out", /* 30 Host will enable high power (external charge pump.) */ ++ NULL, ++ ++}; ++ ++ /* eof */ ++ ++/* Generated by otg-meta-names-c.awk ++ * ++ * Do not Edit, see otg-state.awk ++ */ ++ ++ ++ ++char *otg_meta_names[] = { ++ "a_idle", /* 0 - */ ++ "a_wait_vrise", /* 1 - */ ++ "a_wait_bcon", /* 2 - */ ++ "a_host", /* 3 - */ ++ "a_suspend", /* 4 - */ ++ "a_peripheral", /* 5 - */ ++ "a_wait_vfall", /* 6 - */ ++ "a_vbus_err", /* 7 - */ ++ "b_idle", /* 8 - */ ++ "b_srp_init", /* 9 - */ ++ "b_peripheral", /* 10 - */ ++ "b_suspend", /* 11 - */ ++ "b_wait_acon", /* 12 - */ ++ "b_host", /* 13 - */ ++ "b_suspended", /* 14 - */ ++ "ph_disc", /* 15 - Equivalent to b_peripheral */ ++ "ph_init", /* 16 - */ ++ "ph_uart", /* 17 - */ ++ "ph_aud", /* 18 - */ ++ "ph_wait", /* 19 - */ ++ "ph_exit", /* 20 - */ ++ "cr_init", /* 21 - */ ++ "cr_uart", /* 22 - */ ++ "cr_aud", /* 23 - */ ++ "cr_ack", /* 24 - */ ++ "cr_wait", /* 25 - */ ++ "cr_disc", /* 26 - */ ++ "otg_init", /* 27 - */ ++ "usb_accessory", /* 28 - */ ++ "usb_factory", /* 29 - */ ++ "unknown", /* 30 - */ ++ ++ "", ++}; ++ ++/* eof */ +diff -uNr linux/drivers/no-otg/otgcore/otg-mesg-l24.c linux/drivers/otg/otgcore/otg-mesg-l24.c +--- linux/drivers/no-otg/otgcore/otg-mesg-l24.c 1970-01-01 01:00:00.000000000 +0100 ++++ linux/drivers/otg/otgcore/otg-mesg-l24.c 2006-09-01 21:41:34.000000000 +0200 +@@ -0,0 +1,399 @@ ++/* ++ * otg/otgcore/otg-mesg-l24.c - OTG state machine Administration ++ * ++ * Copyright (c) 2004 Belcarra ++ * ++ * By: ++ * Stuart Lynne <sl@lbelcarra.com>, ++ * Tom Rushworth <tbr@belcarra.com>, ++ * Bruce Balden <balden@belcarra.com> ++ * ++ */ ++/*! ++ * @file otg/otgcore/otg-mesg-l24.c ++ * @brief OTG Core Linux Message Interface. ++ * ++ * Implement linux char device via /proc/otg-mesg. ++ * ++ * @ingroup OTGCore ++ */ ++ ++#include <otg/otg-compat.h> ++#include <otg/otg-module.h> ++ ++#include <otg/usbp-chap9.h> ++#include <otg/usbp-func.h> ++#include <otg/usbp-bus.h> ++#include <otg/otg-trace.h> ++#include <otg/otg-api.h> ++#include <otg/otg-tcd.h> ++#include <otg/otg-hcd.h> ++#include <otg/otg-pcd.h> ++#include <otg/otg-ocd.h> ++ ++#include <linux/poll.h> ++#include <linux/sched.h> ++ ++ ++wait_queue_head_t otg_message_queue; ++ ++/*! ++ * otg_message() - queue message data ++ * @param buf message to send. ++ */ ++void otg_message (char *buf) ++{ ++ char time_buf[64]; ++ static u64 save_ticks; ++ u64 new_ticks; ++ u64 ticks; ++ ++ new_ticks = otg_tmr_ticks(); ++ ++ otg_write_message(buf, strlen(buf)); ++ ++ ticks = otg_tmr_elapsed(&new_ticks, &save_ticks); ++ save_ticks = new_ticks; ++ ++ if (ticks < 10000) ++ sprintf(time_buf, " %d uS\n", ticks); ++ else if (ticks < 10000000) ++ //sprintf(time_buf, " %d mS\n", ticks / 1000); ++ sprintf(time_buf, " %d mS\n", ticks >> 10); ++ else ++ //sprintf(time_buf, " %d S\n", ticks / 1000000); ++ sprintf(time_buf, " %d S\n", ticks >> 20); ++ ++ otg_write_message(time_buf, strlen(time_buf)); ++ ++ ++ //TRACE_MSG0(CORE, "wakeup "); ++ //printk(KERN_INFO"%s: wakeup\n", __FUNCTION__); ++ wake_up_interruptible(&otg_message_queue); ++} ++ ++/*! ++ * otg_message_block() - implement block ++ */ ++unsigned int otg_message_block(void) ++{ ++ int count = otg_data_queued(); ++ if (count) return count; ++ //TRACE_MSG0(CORE, "sleeping "); ++ //printk(KERN_INFO"%s: sleeping\n", __FUNCTION__); ++ interruptible_sleep_on(&otg_message_queue); ++ return otg_data_queued(); ++} ++ ++/*! ++ * otg_message_poll() - implement poll ++ * @param filp file pointer ++ * @param wait poll table structure ++ */ ++unsigned int otg_message_poll(struct file *filp, struct poll_table_struct *wait) ++{ ++ //TRACE_MSG0(CORE, "polling "); ++ poll_wait(filp, &otg_message_queue, wait); ++ return otg_data_queued() ? POLLIN | POLLRDNORM : 0; ++} ++ ++/*! ++ * otg_message_ioctl_internal() - ioctl call ++ * @param cmd ioctl command. ++ * @param arg ioctl arguement. ++ * @return non-zero for error. ++ */ ++int otg_message_ioctl_internal(unsigned int cmd, unsigned long arg) ++{ ++ int i; ++ int len; ++ int flag; ++ struct otg_admin_command admin_command; ++ struct otg_status_update status_update; ++ struct otg_firmware_info firmware_info; ++ struct otg_state otg_state; ++ struct otg_test otg_test; ++ struct otg_ioctl_name *otg_ioctl_name; ++ static char func_buf[32]; ++ char *sp, *dp; ++ char *name; ++ ++ switch (cmd) { ++ ++ case OTGADMIN_GET_FUNCTION: ++ //TRACE_MSG0(CORE, "OTGADMIN_GET_FUNCTION"); ++ RETURN_EINVAL_UNLESS(otg_firmware_loaded); ++ RETURN_EINVAL_IF(copy_from_user(&admin_command, (void *)arg, _IOC_SIZE(cmd))); ++ name = otg_usbd_ops->function_name(admin_command.n); ++ admin_command.string[0] = '\0'; ++ if (name) ++ strncat(admin_command.string, name, sizeof(admin_command.string)); ++ ++ RETURN_EINVAL_IF(copy_to_user((void *)arg, &admin_command, _IOC_SIZE(cmd))); ++ return 0; ++ ++ case OTGADMIN_SET_FUNCTION: ++ //TRACE_MSG0(CORE, "OTGADMIN_SET_FUNCTION"); ++ RETURN_EINVAL_UNLESS(otg_firmware_loaded); ++ memset(&admin_command, 0x41, sizeof(struct otg_admin_command)); ++ RETURN_EINVAL_IF(copy_from_user(&admin_command, (void *)arg, _IOC_SIZE(cmd))); ++ len = sizeof(mesg_otg_instance->function_name); ++ admin_command.string[len] = '\0'; ++ //TRACE_MSG1(CORE, "Setting function: \"%s\"", mesg_otg_instance->function_name); ++ strncpy(mesg_otg_instance->function_name, admin_command.string, len); ++ mesg_otg_instance->function_name[len-1] = '\0'; ++ return 0; ++ ++ case OTGADMIN_GET_SERIAL: ++ //TRACE_MSG0(CORE, "OTGADMIN_GET_SERIAL"); ++ RETURN_EINVAL_UNLESS(otg_firmware_loaded); ++ strncpy(admin_command.string, mesg_otg_instance->serial_number, sizeof(admin_command.string)); ++ admin_command.string[sizeof(admin_command.string) - 1] = '\0'; ++ RETURN_EINVAL_IF(copy_to_user((void *)arg, &admin_command, _IOC_SIZE(cmd))); ++ return 0; ++ ++ case OTGADMIN_SET_SERIAL: ++ //TRACE_MSG0(CORE, "OTGADMIN_SET_SERIAL"); ++ RETURN_EINVAL_UNLESS(otg_firmware_loaded); ++ RETURN_EINVAL_IF(copy_from_user(&admin_command, (void *)arg, _IOC_SIZE(cmd))); ++ admin_command.string[sizeof(admin_command.string) - 1] = '\0'; ++ printk(KERN_INFO"%s: string: %s\n", __FUNCTION__, admin_command.string); ++ for (sp = admin_command.string, dp = mesg_otg_instance->serial_number, i = 0; ++ *sp && (i < (sizeof(admin_command.string) - 1)); i++, sp++) ++ if (isxdigit(*sp)) *dp++ = toupper(*sp); ++ ++ *sp = '\0'; ++ //TRACE_MSG1(CORE, "serial_number: %s", mesg_otg_instance->serial_number); ++ printk(KERN_INFO"%s: serial: %s\n", __FUNCTION__, mesg_otg_instance->serial_number); ++ return 0; ++ ++ case OTGADMIN_STATUS: ++ //TRACE_MSG0(CORE, "OTGADMIN_STATUS"); ++ RETURN_EINVAL_UNLESS(otg_firmware_loaded); ++ memset(&status_update, 0, sizeof(struct otg_status_update)); ++ ++ otg_mesg_get_status_update(&status_update); ++ ++ RETURN_EINVAL_IF(copy_to_user((void *)arg, &status_update, _IOC_SIZE(cmd))); ++ return 0; ++ ++ case OTGADMIN_GET_INFO: ++ //TRACE_MSG0(CORE, "OTGADMIN_GET_INFO"); ++ RETURN_EINVAL_UNLESS(otg_firmware_loaded); ++ ++ otg_mesg_get_firmware_info(&firmware_info); ++ ++ RETURN_EINVAL_IF(copy_to_user((void *)arg, &firmware_info, _IOC_SIZE(cmd))); ++ //TRACE_MSG0(CORE, "OTGADMIN_GET_INFO: finished"); ++ return 0; ++ ++ case OTGADMIN_SET_INFO: ++ //TRACE_MSG0(CORE, "OTGADMIN_SET_INFO"); ++ memset(&firmware_info, 0x41, sizeof(firmware_info)); ++ RETURN_EINVAL_IF(copy_from_user(&firmware_info, (void *)arg, _IOC_SIZE(cmd))); ++ ++ ++ return otg_mesg_set_firmware_info(&firmware_info); ++ return 0; ++ ++ case OTGADMIN_GET_STATE: ++ case OTGADMIN_SET_STATE: ++ //TRACE_MSG0(CORE, "OTGADMIN_XXX_STATE"); ++ RETURN_EINVAL_IF(copy_from_user(&otg_state, (void *)arg, _IOC_SIZE(cmd))); ++ ++ switch (cmd) { ++ case OTGADMIN_GET_STATE: ++ RETURN_EINVAL_UNLESS(otg_firmware_loaded); ++ //TRACE_MSG0(CORE, "OTGADMIN_GET_STATE"); ++ RETURN_EINVAL_UNLESS (otg_state.state < otg_firmware_loaded->number_of_states); ++ memcpy(&otg_state, otg_firmware_loaded->otg_states + otg_state.state, sizeof(otg_state)); ++ RETURN_EINVAL_IF(copy_to_user((void *)arg, &otg_state, _IOC_SIZE(cmd))); ++ break; ++ case OTGADMIN_SET_STATE: ++ RETURN_EINVAL_UNLESS(otg_firmware_loading); ++ //TRACE_MSG0(CORE, "OTGADMIN_SET_STATE"); ++ RETURN_EINVAL_UNLESS (otg_state.state < otg_firmware_loading->number_of_states); ++ memcpy(otg_firmware_loading->otg_states + otg_state.state, &otg_state, sizeof(otg_state)); ++ break; ++ } ++ return 0; ++ ++ ++ case OTGADMIN_GET_TEST: ++ case OTGADMIN_SET_TEST: ++ RETURN_EINVAL_IF(copy_from_user(&otg_test, (void *)arg, _IOC_SIZE(cmd))); ++ ++ switch (cmd) { ++ case OTGADMIN_GET_TEST: ++ RETURN_EINVAL_UNLESS(otg_firmware_loaded); ++ //TRACE_MSG1(CORE, "OTGADMIN_GET_TEST: %d", otg_test.test); ++ RETURN_EINVAL_UNLESS (otg_test.test < otg_firmware_loaded->number_of_tests); ++ memcpy(&otg_test, otg_firmware_loaded->otg_tests + otg_test.test, sizeof(otg_test)); ++ RETURN_EINVAL_IF(copy_to_user((void *)arg, &otg_test, _IOC_SIZE(cmd))); ++ break; ++ case OTGADMIN_SET_TEST: ++ RETURN_EINVAL_UNLESS(otg_firmware_loading); ++ //TRACE_MSG1(CORE, "OTGADMIN_SET_TEST : %d", otg_test.test); ++ RETURN_EINVAL_UNLESS (otg_test.test < otg_firmware_loading->number_of_tests); ++ memcpy(otg_firmware_loading->otg_tests + otg_test.test, &otg_test, sizeof(otg_test)); ++ break; ++ } ++ return 0; ++ ++ default: ++ //TRACE_MSG0(CORE, "OTGADMIN_"); ++ RETURN_EINVAL_UNLESS(otg_firmware_loaded); ++ for (otg_ioctl_name = otg_ioctl_names; otg_ioctl_name && otg_ioctl_name->cmd; otg_ioctl_name++) { ++ //TRACE_MSG4(CORE, "lookup: %04x %04x %08x %08x", ++ // _IOC_NR(cmd), _IOC_NR(otg_ioctl_name->cmd), cmd, otg_ioctl_name->cmd); ++ BREAK_IF(_IOC_NR(otg_ioctl_name->cmd) == _IOC_NR(cmd)); ++ } ++ //TRACE_MSG3(CORE, "checking %d %08x %08x", _IOC_NR(cmd), otg_ioctl_name->cmd, cmd); ++ RETURN_EINVAL_UNLESS(otg_ioctl_name->cmd); ++ __get_user(flag, (int *)arg); ++ //TRACE_MSG3(CORE, "%s %08x flag: %d", otg_ioctl_name->name, otg_ioctl_name->set, flag); ++ otg_event (mesg_otg_instance, flag ? otg_ioctl_name->set : _NOT(otg_ioctl_name->set), CORE, otg_ioctl_name->name); ++ return 0; ++ } ++ return 0; ++} ++ ++ ++ ++/*! ++ * otg_message_ioctl() - ioctl ++ * @param inode inode structure. ++ * @param filp file. ++ * @param cmd ioctl command. ++ * @param arg ioctl argument. ++ * @return non-zero for error. ++ */ ++int otg_message_ioctl(struct inode *inode, struct file *filp, unsigned int cmd, unsigned long arg) ++{ ++ int i; ++ int len; ++ int flag; ++ struct otg_admin_command admin_command; ++ struct otg_status_update status_update; ++ struct otg_firmware_info firmware_info; ++ struct otg_state otg_state; ++ struct otg_test otg_test; ++ //struct otg_ioctl_map *map = ioctl_map; ++ struct otg_ioctl_name *otg_ioctl_name; ++ ++ static char func_buf[32]; ++ ++ //TRACE_MSG6(CORE, "cmd: %08x arg: %08x type: %02d nr: %02d dir: %02d size: %02d", ++ // cmd, arg, _IOC_TYPE(cmd), _IOC_NR(cmd), _IOC_DIR(cmd), _IOC_SIZE(cmd)); ++ ++ RETURN_EINVAL_UNLESS (_IOC_TYPE(cmd) == OTGADMIN_MAGIC); ++ RETURN_EINVAL_UNLESS (_IOC_NR(cmd) <= OTGADMIN_MAXNR); ++ ++ RETURN_EFAULT_IF((_IOC_DIR(cmd) == _IOC_READ) && !access_ok(VERIFY_WRITE, (void *)arg, _IOC_SIZE(cmd))); ++ RETURN_EFAULT_IF((_IOC_DIR(cmd) == _IOC_WRITE) && !access_ok(VERIFY_READ, (void *)arg, _IOC_SIZE(cmd))); ++ ++ return otg_message_ioctl_internal(cmd, arg); ++} ++ ++ ++/*! ++ * otg_message_proc_read() - implement proc file system read. ++ * Standard proc file system read function. ++ * @param file file. ++ * @param buf buffer to fill ++ * @param count size of buffer ++ * @param pos file position ++ * @return number of bytes or negative number for error ++ */ ++static ssize_t otg_message_proc_read (struct file *file, char *buf, size_t count, loff_t * pos) ++{ ++ unsigned long page; ++ int len = 0, avail; ++ char *bp,*ep; ++ struct list_head *lhd; ++ int state; ++ ++ // get a page, max 4095 bytes of data... ++ RETURN_ENOMEM_UNLESS ((page = GET_KERNEL_PAGE())); ++ ++ bp = (char *) page; ++ ++ len = bp - (char *) page; ++ ++ if (( avail = otg_message_block())) { ++ ++ len += otg_read_message(bp, 4095); ++ ++ if (len > count) ++ len = -EINVAL; ++ else if ((len > 0) && copy_to_user (buf, (char *) page, len)) ++ len = -EFAULT; ++ ++ } ++ ++ free_page (page); ++ return len; ++} ++ ++/*! otg_message_proc_ioctl() - ++ * @param inode inode structure. ++ * @param filp file. ++ * @param cmd ioctl command. ++ * @param arg ioctl argument. ++ * @return non-zero for error. ++ */ ++int otg_message_proc_ioctl(struct inode *inode, struct file *filp, unsigned int cmd, unsigned long arg) ++{ ++ return otg_message_ioctl(inode, filp, cmd, arg); ++} ++ ++/*! ++ * @param filp file. ++ * @param wait buffer to fill ++ * @return number of bytes or negative number for error ++ */ ++unsigned int otg_message_proc_poll(struct file *filp, struct poll_table_struct *wait) ++{ ++ return otg_message_poll(filp, wait); ++} ++ ++/*! ++ * @var otg_message_proc_switch_functions ++ */ ++static struct file_operations otg_message_proc_switch_functions = { ++ ioctl:otg_message_proc_ioctl, ++ poll:otg_message_proc_poll, ++ read:otg_message_proc_read, ++}; ++ ++ ++/*! ++ * otg_message_init_l24() - initialize ++ */ ++int otg_message_init_l24(struct otg_instance *otg) ++{ ++ struct proc_dir_entry *message = NULL; ++ init_waitqueue_head(&otg_message_queue); ++ ++ THROW_IF (!(message = create_proc_entry ("otg_message", 0666, 0)), error); ++ message->proc_fops = &otg_message_proc_switch_functions; ++ CATCH(error) { ++ printk(KERN_ERR"%s: creating /proc/otg_message failed\n", __FUNCTION__); ++ if (message) ++ remove_proc_entry("otg_message", NULL); ++ return -EINVAL; ++ } ++ return 0; ++} ++ ++/*! ++ * otg_message_exit_l24() - exit ++ */ ++void otg_message_exit_l24(void) ++{ ++ remove_proc_entry("otg_message", NULL); ++} ++ ++EXPORT_SYMBOL(otg_message); ++ ++ +diff -uNr linux/drivers/no-otg/otgcore/otg-mesg.c linux/drivers/otg/otgcore/otg-mesg.c +--- linux/drivers/no-otg/otgcore/otg-mesg.c 1970-01-01 01:00:00.000000000 +0100 ++++ linux/drivers/otg/otgcore/otg-mesg.c 2006-09-01 21:41:35.000000000 +0200 +@@ -0,0 +1,369 @@ ++/* ++ * otg/otgcore/otg-mesg.c - OTG state machine Administration ++ * ++ * Copyright (c) 2004 Belcarra ++ * ++ * By: ++ * Stuart Lynne <sl@lbelcarra.com>, ++ * Tom Rushworth <tbr@belcarra.com>, ++ * Bruce Balden <balden@belcarra.com> ++ * ++ */ ++/*! ++ * @file otg/otgcore/otg-mesg.c ++ * @brief OTG Core Message Facility. ++ * ++ * The OTG Core Message Facility has two functions: ++ * ++ * 1. implement a message queue for messages from the otg state machine to the otg ++ * management application ++ * ++ * 2. implement an ioctl control mechanism allowing the otg managment application ++ * to pass data and commands to the otg state machine ++ * ++ * @ingroup OTGCore ++ */ ++ ++#include <otg/otg-compat.h> ++#include <otg/otg-module.h> ++ ++#include <otg/usbp-chap9.h> ++#include <otg/usbp-func.h> ++#include <otg/usbp-bus.h> ++#include <otg/otg-trace.h> ++#include <otg/otg-api.h> ++#include <otg/otg-tcd.h> ++#include <otg/otg-hcd.h> ++#include <otg/otg-pcd.h> ++#include <otg/otg-ocd.h> ++ ++#if defined(OTG_LINUX) ++#include <linux/poll.h> ++#include <linux/sched.h> ++#endif /* defined(OTG_LINUX) */ ++ ++//#define OTG_MESSAGE_BUF_SIZE 512 ++#define OTG_MESSAGE_BUF_SIZE 2048 ++ ++struct otg_instance * mesg_otg_instance; ++ ++char *otg_message_buf; // buffer ++char *otg_message_head; // next available empty space ++char *otg_message_tail; // next available data if not equal to head ++ ++/*! ++ * otg_data_avail() - amount of data in buffer ++ * space data ++ * tp hp 20 - (10 - 5) = 15 (10 - 5) ++ * 0......5.data.10......20 ++ * ++ * hp tp (10 - 5) = 5 20 - (10 - 5) ++ * 0.data.5......10.data.20 ++ * ++ * @return Bytes available in message ring buffer. ++ */ ++int otg_data_avail(void) ++{ ++ int avail = (otg_message_head >= otg_message_tail) ? ++ (otg_message_head - otg_message_tail) : ++ OTG_MESSAGE_BUF_SIZE - (otg_message_tail - otg_message_head) ; ++ ++ //TRACE_MSG4(CORE, "buf: %p head: %p tail: %p data avail: %d", otg_message_buf, otg_message_head, otg_message_tail, avail); ++ return avail; ++} ++ ++/*! ++ * otg_space_avail() - amount of space in buffer ++ * ++ * @return Space available in message ring buffer. ++ */ ++int otg_space_avail(void) ++{ ++ int avail = (otg_message_head >= otg_message_tail) ? ++ OTG_MESSAGE_BUF_SIZE - (otg_message_head - otg_message_tail) : ++ (otg_message_tail - otg_message_head) ; ++ ++ //TRACE_MSG4(CORE, "buf: %p head: %p tail: %p space avail: %d", otg_message_buf, otg_message_head, otg_message_tail, avail); ++ return avail; ++} ++ ++/*! ++ * otg_get_byte() - return next byte ++ * ++ * @return Next byte in message ring buffer ++ */ ++char otg_get_byte(void) ++{ ++ char c = *otg_message_tail; ++ ++ //TRACE_MSG3(CORE, "buf: %p head: %p tail: %p", otg_message_buf, otg_message_head, otg_message_tail); ++ if ((otg_message_tail - otg_message_buf) == OTG_MESSAGE_BUF_SIZE) ++ otg_message_tail = otg_message_buf; ++ else ++ otg_message_tail++; ++ ++ return c; ++} ++ ++/*! ++ * otg_put_byte() - put byte into buffer ++ * ++ * Put byte into message ring buffer ++ * ++ * @param byte put into message ring. ++ */ ++void otg_put_byte(char byte) ++{ ++ *otg_message_head = byte; ++ if ((otg_message_head - otg_message_buf) == OTG_MESSAGE_BUF_SIZE) ++ otg_message_head = otg_message_buf; ++ else ++ otg_message_head++; ++ ++} ++ ++/*! ++ * otg_write_message_irq() - queue message data (interrupt version) ++ * ++ * Attempt to write message data into ring buffer, return amount ++ * of data actually written. ++ * ++ * @param buf - pointer to data ++ * @param size - amount of available data ++ * @return number of bytes that where not written. ++ */ ++int otg_write_message_irq (char *buf, int size) ++{ ++ struct pcd_instance *pcd = NULL; ++ struct usbd_bus_instance *bus = NULL; ++ ++ pcd = (struct pcd_instance *)mesg_otg_instance->pcd; ++ if (pcd) ++ bus = pcd->bus; ++ ++ if (size >= (OTG_MESSAGE_BUF_SIZE - 2)) ++ return size; ++ ++ while (otg_space_avail() < (size + 1)) ++ otg_get_byte(); ++ ++ ++ /* copy, note that we don't have to test for space as we have ++ * already ensured that there is enough room for all of the data ++ */ ++ while (size--) ++ otg_put_byte(*buf++); ++ ++ //otg_put_byte('\n'); ++ ++ return size; ++} ++ ++/*! ++ * otg_write_message() - queue message data ++ * ++ * Attempt to write message data into ring buffer, return amount ++ * of data actually written. ++ * ++ * @param buf - pointer to data ++ * @param size - amount of available data ++ * @return number of bytes that where not written. ++ */ ++int otg_write_message (char *buf, int size) ++{ ++ int rc; ++ unsigned long flags; ++ local_irq_save (flags); ++ rc = otg_write_message_irq(buf, size); ++ local_irq_restore (flags); ++ return size; ++} ++ ++/*! ++ * otg_read_message() - get queued message data ++ * ++ * @param buf - pointer to data ++ * @param size - amount of available data ++ * @return the number of bytes read. ++ */ ++int otg_read_message(char *buf, int size) ++{ ++ int count = 0; ++ unsigned long flags; ++ ++ local_irq_save (flags); ++ ++ /* Copy bytes to the buffer while there is data available and space ++ * to put it in the buffer ++ */ ++ for ( ; otg_data_avail() && size--; count++) ++ BREAK_IF((*buf++ = otg_get_byte()) == '\n'); ++ ++ local_irq_restore (flags); ++ return count; ++} ++ ++/*! ++ * otg_data_queued() - return amount of data currently queued ++ * ++ * @return Amount of data currently queued in mesage ring buffer. ++ */ ++int otg_data_queued(void) ++{ ++ int count; ++ unsigned long flags; ++ local_irq_save (flags); ++ count = otg_data_avail(); ++ local_irq_restore (flags); ++ //printk(KERN_INFO"%s: count: %d\n", __FUNCTION__, count); ++ return count; ++} ++ ++ ++/*! ++ * otg_message_init() - initialize ++ * @param otg OTG instance ++ */ ++int otg_message_init(struct otg_instance *otg) ++{ ++ struct proc_dir_entry *message = NULL; ++ otg_message_buf = otg_message_head = otg_message_tail = NULL; ++ THROW_UNLESS((otg_message_buf = CKMALLOC(OTG_MESSAGE_BUF_SIZE + 16, GFP_KERNEL)), error); ++ otg_message_head = otg_message_tail = otg_message_buf ; ++ ++ mesg_otg_instance = otg; ++ CATCH(error) { ++ #if defined(OTG_LINUX) ++ printk(KERN_ERR"%s: creating /proc/otg_message failed\n", __FUNCTION__); ++ #endif /* defined(OTG_LINUX) */ ++ #if defined(OTG_WINCE) ++ DEBUGMSG(ZONE_INIT, (_T("otg_message_init: creating //proc//otg_message failed\n")) ); ++ #endif /* defined(OTG_LINUX) */ ++ return -EINVAL; ++ } ++ return 0; ++} ++ ++/*! ++ * otg_message_exit() - exit ++ */ ++void otg_message_exit(void) ++{ ++ mesg_otg_instance = NULL; ++ if (otg_message_buf) ++ LKFREE(otg_message_buf); ++ otg_message_buf = otg_message_head = otg_message_tail = NULL; ++} ++ ++ ++/* ********************************************************************************************* */ ++/* ********************************************************************************************* */ ++ ++/*! ++ * otg_mesg_get_status_update() - get status update ++ * @param status_update ++ */ ++void otg_mesg_get_status_update(struct otg_status_update *status_update) ++{ ++ ++ TRACE_MSG0(CORE, "--"); ++ status_update->state = mesg_otg_instance->state; ++ status_update->meta = mesg_otg_instance->current_outputs->meta; ++ status_update->inputs = mesg_otg_instance->current_inputs; ++ status_update->outputs = mesg_otg_instance->outputs; ++ status_update->capabilities = otg_ocd_ops ? otg_ocd_ops->capabilities : 0; ++ ++ strncpy(status_update->fw_name, otg_firmware_loaded->fw_name, OTGADMIN_MAXSTR); ++ strncpy(status_update->state_name, otg_get_state_name(mesg_otg_instance->state), OTGADMIN_MAXSTR); ++ ++ if (mesg_otg_instance->function_name) ++ strncpy(status_update->function_name, mesg_otg_instance->function_name, OTGADMIN_MAXSTR); ++ ++ TRACE_MSG5(CORE, "state: %02x meta: %02x inputs: %08x outputs: %04x", ++ status_update->state, status_update->meta, status_update->inputs, ++ status_update->outputs, status_update->capabilities); ++} ++ ++ ++/*! ++ * otg_mesg_get_firmware_info() - get firmware info ++ * @param firmware_info ++ */ ++void otg_mesg_get_firmware_info(struct otg_firmware_info *firmware_info) ++{ ++ memset(firmware_info, 0, sizeof(firmware_info)); ++ // XXX irq lock ++ ++ firmware_info->number_of_states = otg_firmware_loaded->number_of_states; ++ firmware_info->number_of_tests = otg_firmware_loaded->number_of_tests; ++ memcpy(&firmware_info->fw_name, otg_firmware_loaded->fw_name, sizeof(firmware_info->fw_name)); ++ ++} ++ ++/*! ++ * otg_mesg_set_firmware_info() - set firmware info ++ * @param firmware_info ++ */ ++int otg_mesg_set_firmware_info(struct otg_firmware_info *firmware_info) ++{ ++ ++ if (otg_firmware_loading) { ++ ++ if ( (otg_firmware_loading->number_of_states == firmware_info->number_of_states) && ++ (otg_firmware_loading->number_of_tests == firmware_info->number_of_tests) ++ ) { ++ TRACE_MSG0(CORE, "OTGADMIN_SET_INFO: loaded"); ++ otg_firmware_loaded = otg_firmware_loading; ++ otg_firmware_loading = NULL; ++ return 0; ++ } ++ else { ++ TRACE_MSG0(CORE, "OTGADMIN_SET_INFO: abandoned"); ++ LKFREE(otg_firmware_loading->otg_states); ++ LKFREE(otg_firmware_loading->otg_tests); ++ LKFREE(otg_firmware_loading); ++ return -EINVAL; ++ } ++ } ++ ++ TRACE_MSG2(CORE, "OTGADMIN_SET_INFO: otg_firmware_loaded: %x otg_firmware_orig: %x", ++ otg_firmware_loaded, otg_firmware_orig); ++ if (otg_firmware_loaded && (otg_firmware_loaded != otg_firmware_orig)) { ++ LKFREE(otg_firmware_loaded->otg_states); ++ LKFREE(otg_firmware_loaded->otg_tests); ++ LKFREE(otg_firmware_loaded); ++ otg_firmware_loaded = otg_firmware_orig; ++ TRACE_MSG0(CORE, "OTGADMIN_SET_INFO: unloaded"); ++ } ++ if (firmware_info->number_of_states && firmware_info->number_of_tests) { ++ int size; ++ otg_firmware_loaded = NULL; ++ TRACE_MSG0(CORE, "OTGADMIN_SET_INFO: setup"); ++ THROW_UNLESS((otg_firmware_loading = CKMALLOC(sizeof(struct otg_firmware), GFP_KERNEL)), error); ++ size = sizeof(struct otg_state) * firmware_info->number_of_states; ++ THROW_UNLESS((otg_firmware_loading->otg_states = CKMALLOC(size, GFP_KERNEL)), error); ++ size = sizeof(struct otg_test) * firmware_info->number_of_tests; ++ THROW_UNLESS((otg_firmware_loading->otg_tests = CKMALLOC(size, GFP_KERNEL)), error); ++ ++ otg_firmware_loading->number_of_states = firmware_info->number_of_states; ++ otg_firmware_loading->number_of_tests = firmware_info->number_of_tests; ++ memcpy(otg_firmware_loading->fw_name, &firmware_info->fw_name, sizeof(firmware_info->fw_name)); ++ ++ CATCH(error) { ++ TRACE_MSG0(CORE, "OTGADMIN_SET_INFO: setup error"); ++ if (otg_firmware_loading) { ++ if (otg_firmware_loading->otg_states) LKFREE(otg_firmware_loading->otg_states); ++ if (otg_firmware_loading->otg_tests) LKFREE(otg_firmware_loading->otg_tests); ++ LKFREE(otg_firmware_loading); ++ return -EINVAL; ++ } ++ } ++ ++ } ++ return 0; ++} ++ ++ ++/* ********************************************************************************************* */ ++/* ********************************************************************************************* */ ++ +diff -uNr linux/drivers/no-otg/otgcore/otg-trace-l24.c linux/drivers/otg/otgcore/otg-trace-l24.c +--- linux/drivers/no-otg/otgcore/otg-trace-l24.c 1970-01-01 01:00:00.000000000 +0100 ++++ linux/drivers/otg/otgcore/otg-trace-l24.c 2006-09-01 21:41:35.000000000 +0200 +@@ -0,0 +1,174 @@ ++/* ++ * otg/otgcore/otg-trace-l24.c ++ * ++ * Copyright (c) 2004 Belcarra ++ * ++ * By: ++ * Stuart Lynne <sl@belcarra.com>, ++ * Tom Rushworth <tbr@belcarra.com>, ++ * ++ */ ++/*! ++ * @file otg/otgcore/otg-trace-l24.c ++ * @brief OTG Core Linux Debug Trace Facility ++ * ++ * This allows access to the OTG Core Trace via /proc/trace_otg. Each time ++ * this file is opened and read it will contain a snapshot of the most ++ * recent trace messages. ++ * ++ * Reading the trace resets it, the next read will open a new snapshot. ++ * ++ * N.B. There is no protection from multiple reads. ++ * ++ * N.B. This is for debugging and is not normally enabled for production software. ++ * ++ * @ingroup OTGCore ++ */ ++ ++#include <otg/otg-compat.h> ++#include <otg/otg-module.h> ++#include <linux/vmalloc.h> ++#include <stdarg.h> ++ ++#include <otg/usbp-chap9.h> ++#include <otg/usbp-func.h> ++#include <otg/usbp-bus.h> ++ ++#include <otg/otg-trace.h> ++#include <otg/otg-api.h> ++ ++#if defined(CONFIG_OTG_TRACE) || defined(CONFIG_OTG_TRACE_MODULE) ++ ++DECLARE_MUTEX(otg_trace_sem_l24); ++ ++ ++/* * ++ * otg_trace_proc_read_l24 - implement proc file system read. ++ * @file ++ * @buf ++ * @count ++ * @pos ++ * ++ * Standard proc file system read function. ++ */ ++static ssize_t otg_trace_proc_read_l24 (struct file *file, char *buf, size_t count, loff_t * pos) ++{ ++ unsigned long page; ++ int len = 0; ++ int index; ++ int oindex; ++ int rc; ++ ++ u64 ticks = 0; ++ ++ unsigned char *cp; ++ int skip = 0; ++ ++ otg_trace_t px; ++ otg_trace_t ox; ++ otg_trace_t vx; ++ otg_trace_t *o, *p; ++ ++ RETURN_ENOMEM_IF (!(page = GET_KERNEL_PAGE())); ++ ++ _MOD_INC_USE_COUNT; ++ ++ // Lock out tag changes while reading ++ DOWN(&otg_trace_sem_l24); ++ len = otg_trace_proc_read((char *)page, count, (int *)pos); ++ ++ if (len > count) ++ len = -EINVAL; ++ ++ else if (len > 0 && copy_to_user (buf, (char *) page, len)) ++ len = -EFAULT; ++ ++ free_page (page); ++ ++ UP(&otg_trace_sem_l24); ++ free_page (page); ++ _MOD_DEC_USE_COUNT; ++ return len; ++} ++ ++#define MAX_TRACE_CMD_LEN 64 ++/* * ++ * otg_trace_proc_write_l24 - implement proc file system write. ++ * @file ++ * @buf ++ * @count ++ * @pos ++ * ++ * Proc file system write function. ++ */ ++static ssize_t otg_trace_proc_write_l24 (struct file *file, const char *buf, size_t count, loff_t * pos) ++{ ++ char command[MAX_TRACE_CMD_LEN+1]; ++ size_t l = MIN(count, MAX_TRACE_CMD_LEN); ++ ++ RETURN_ZERO_UNLESS(count); ++ RETURN_EINVAL_IF (copy_from_user (command, buf, l)); ++ ++ ++ RETURN_EINVAL_IF(copy_from_user (command, buf, l)); ++ ++ return otg_trace_proc_write(command, l, (int *)pos); ++#if 0 ++ else { ++ n -= l; ++ while (n > 0) { ++ if (copy_from_user (&c, buf + (count - n), 1)) { ++ count = -EFAULT; ++ break; ++ } ++ n -= 1; ++ } ++ // Terminate command[] ++ if (l > 0 && command[l-1] == '\n') { ++ l -= 1; ++ } ++ command[l] = 0; ++ } ++#endif ++} ++ ++/* Module init ************************************************************** */ ++ ++ ++ ++static struct file_operations otg_trace_proc_operations_functions = { ++ read: otg_trace_proc_read_l24, ++ write: otg_trace_proc_write_l24, ++}; ++ ++/*! otg_trace_init_l24 ++ * ++ * Return non-zero if not successful. ++ */ ++int otg_trace_init_l24 (void) ++{ ++ struct proc_dir_entry *p; ++ ++ // create proc filesystem entries ++ if ((p = create_proc_entry ("trace_otg", 0, 0)) == NULL) ++ printk(KERN_INFO"%s PROC FS failed\n", "trace_otg"); ++ else ++ p->proc_fops = &otg_trace_proc_operations_functions; ++ ++ return 0; ++} ++ ++/*! otg_trace_exit_l24 - remove procfs entry, free trace data space. ++ */ ++void otg_trace_exit_l24 (void) ++{ ++ otg_trace_t *p; ++ unsigned long flags; ++ remove_proc_entry ("trace_otg", NULL); ++} ++ ++#else /* defined(CONFIG_OTG_TRACE) || defined(CONFIG_OTG_TRACE_MODULE) */ ++int otg_trace_init_l24 (void) {return 0;} ++void otg_trace_exit_l24 (void) {} ++#endif /* defined(CONFIG_OTG_TRACE) || defined(CONFIG_OTG_TRACE_MODULE) */ ++ +diff -uNr linux/drivers/no-otg/otgcore/otg-trace.c linux/drivers/otg/otgcore/otg-trace.c +--- linux/drivers/no-otg/otgcore/otg-trace.c 1970-01-01 01:00:00.000000000 +0100 ++++ linux/drivers/otg/otgcore/otg-trace.c 2006-09-01 21:41:35.000000000 +0200 +@@ -0,0 +1,615 @@ ++/* ++ * otg/otgcore/otg-trace.c ++ * ++ * Copyright (c) 2004 Belcarra ++ * ++ * By: ++ * Stuart Lynne <sl@belcarra.com>, ++ * Tom Rushworth <tbr@belcarra.com>, ++ * ++ */ ++/*! ++ * @file otg/otgcore/otg-trace.c ++ * @brief OTG Core Debug Trace Facility ++ * ++ * This implements the OTG Core Trace Facility. ++ * ++ * @ingroup OTGCore ++ */ ++ ++#include <otg/otg-compat.h> ++#include <otg/otg-module.h> ++ ++#if defined(OTG_LINUX) ++#include <linux/vmalloc.h> ++#include <stdarg.h> ++#endif /* defined(OTG_LINUX) */ ++ ++#include <otg/usbp-chap9.h> ++#include <otg/usbp-func.h> ++#include <otg/usbp-bus.h> ++ ++#include <otg/otg-trace.h> ++#include <otg/otg-api.h> ++ ++#define STATIC static ++ ++#if defined(CONFIG_OTG_TRACE) || defined(CONFIG_OTG_TRACE_MODULE) ++ ++DECLARE_MUTEX(otg_trace_sem); ++ ++static int otg_trace_exiting; ++ ++#define OTG_TRACE_NAME "trace_otg" ++ ++typedef struct otg_trace_snapshot { ++ int first; ++ int next; ++ int total; ++ otg_trace_t *traces; ++} otg_trace_snapshot; ++ ++otg_trace_snapshot otg_snapshot1; ++otg_trace_snapshot otg_snapshot2; ++ ++static otg_trace_snapshot *otg_trace_current_traces; ++static otg_trace_snapshot *otg_trace_other_traces; ++ ++STATIC otg_trace_snapshot *rel_snapshot(otg_trace_snapshot *tss) ++{ ++ RETURN_NULL_UNLESS(tss); ++ if (tss->traces) { ++ vfree(tss->traces); ++ tss->traces = NULL; ++ } ++ //vfree(tss); ++ return NULL; ++} ++ ++STATIC void init_snapshot(otg_trace_snapshot *tss) ++{ ++ tss->first = tss->next = tss->total = 0; ++ memset(tss->traces,0,sizeof(otg_trace_t) * TRACE_MAX); ++} ++ ++STATIC otg_trace_snapshot *get_snapshot(otg_trace_snapshot *tss) ++{ ++ //otg_trace_snapshot *tss; ++ unsigned long flags; ++ //UNLESS ((tss = vmalloc(sizeof(otg_trace_snapshot)))) { ++ // printk(KERN_ERR "Cannot allocate memory for OTG trace snapshot.\n"); ++ // return NULL; ++ //} ++ ++ UNLESS ((tss->traces = vmalloc(sizeof(otg_trace_t) * TRACE_MAX))) { ++ printk(KERN_ERR "Cannot allocate memory for OTG trace log.\n"); ++ return rel_snapshot(tss); ++ } ++ init_snapshot(tss); ++ return tss; ++} ++ ++/*! otg_trace_get_next ++ */ ++STATIC otg_trace_t *otg_trace_get_next(otg_trace_snapshot *tss, otg_tag_t tag, const char *fn, otg_trace_types_t otg_trace_type, int n) ++{ ++ otg_trace_t *p; ++ ++ // Get the next (n) trace slots. ++ ++ p = tss->traces + tss->next; ++ ++ while (n-- > 0) { ++ tss->next = (tss->next + 1) & TRACE_MASK; ++ if (tss->next == tss->first) { ++ // We have wraparound, bump tss->first ++ tss->first = (tss->first + 1) & TRACE_MASK; ++ } ++ tss->total++; ++ } ++ // End of next trace slot. ++ ++ otg_get_trace_info(p); ++ p->otg_trace_type = otg_trace_type; ++ p->tag = tag; ++ p->va.s.function = fn; ++ ++ return p; ++} ++ ++/* otg_trace_next_va ++ */ ++STATIC otg_trace_t *otg_trace_next_va(otg_trace_snapshot *tss, otg_trace_t *p) ++{ ++ /* Return the next trace slot in a va group. ++ No need to be atomic, just worry about wraparound. */ ++ int nxt = (p - tss->traces); ++ nxt = (nxt + 1) & TRACE_MASK; ++ p = tss->traces + nxt; ++ return(p); ++} ++ ++/* otg_trace_setup ++ */ ++void otg_trace_setup(otg_tag_t tag, const char *fn, void *setup) ++{ ++ otg_trace_t *p; ++ otg_trace_snapshot *tss; ++ unsigned long flags; ++ local_irq_save(flags); ++ UNLESS ((tss = otg_trace_current_traces)) { ++ local_irq_restore(flags); ++ return; ++ } ++ // Reserve the trace slots. ++ UNLESS ((p = otg_trace_get_next(tss,tag,fn,otg_trace_setup_n,1))) { ++ local_irq_restore(flags); ++ return; ++ } ++ p->va_num_args = 0; ++ memcpy(&p->va.s.trace.setup, setup, sizeof(p->va.s.trace.setup) /*sizeof(struct usbd_device_request)*/); ++ local_irq_restore(flags); ++} ++ ++/* otg_trace_msg ++ */ ++void otg_trace_msg(otg_tag_t tag, const char *fn, u8 nargs, char *fmt, ...) ++{ ++ int n; ++ otg_trace_t *p; ++ otg_trace_snapshot *tss; ++ unsigned long flags; ++ ++ // Figure out how many trace slots we're going to need. ++ n = 1; ++ if (nargs > 1) { ++ n = 2; ++ // Too many arguments, bail. ++ RETURN_IF (nargs > (OTG_TRACE_MAX_IN_VA+1)); ++ } ++ ++ local_irq_save(flags); ++ UNLESS ((tss = otg_trace_current_traces)) { ++ local_irq_restore(flags); ++ return; ++ } ++ // Reserve the trace slots. ++ UNLESS((p = otg_trace_get_next(tss,tag,fn,otg_trace_msg_va_start_n,n))) { ++ local_irq_restore(flags); ++ return; ++ } ++ p->va.s.trace.msg32.msg = fmt; ++ p->va_num_args = nargs; ++ if (nargs > 0) { ++ va_list ap; ++ va_start(ap,fmt); ++ p->va.s.trace.msg32.val = va_arg(ap,u32); ++ if (--nargs > 0) { ++ // We need the next trace slot. ++ int i; ++ while (nargs >= 8) { ++ p = otg_trace_next_va(tss,p); ++ p->otg_trace_type = otg_trace_msg_va_list_n; ++ for (i = 0; i < 8; i++) p->va.l.val[i] = va_arg(ap,u32); ++ nargs -= 8; ++ } ++ p = otg_trace_next_va(tss,p); ++ p->otg_trace_type = otg_trace_msg_va_list_n; ++ for (i = 0; i < nargs; i++) p->va.l.val[i] = va_arg(ap,u32); ++ } ++ va_end(ap); ++ } ++ local_irq_restore(flags); ++} ++ ++/* Proc Filesystem *************************************************************************** */ ++ ++/* int slot_in_data ++ */ ++STATIC int slot_in_data(otg_trace_snapshot *tss, int slot) ++{ ++ // Return 1 if slot is in the valid data region, 0 otherwise. ++ return (((tss->first < tss->next) && (slot >= tss->first) && (slot < tss->next)) || ++ ((tss->first > tss->next) && ((slot < tss->next) || (slot >= tss->first)))); ++ ++} ++ ++/*! otg_trace_read_slot ++ * ++ * Place a slot pointer in *ps (and possibly *pv), with the older entry (if any) in *po. ++ * Return: -1 at end of data ++ * 0 if not a valid enty ++ * 1 if entry valid but no older valid entry ++ * 2 if both entry and older are valid. ++ */ ++STATIC int otg_trace_read_slot(otg_trace_snapshot *tss, int index, otg_trace_t **ps, otg_trace_t **po) ++{ ++ int res; ++ int previous; ++ otg_trace_t *s,*o; ++ ++ *ps = *po = NULL; ++ index = (tss->first + index) & TRACE_MASK; ++ // Are we at the end of the data? ++ if (!slot_in_data(tss,index)) { ++ // End of data. ++ return(-1); ++ } ++ /* Nope, there's data to show. */ ++ s = tss->traces + index; ++ if (!s->tag || ++ s->otg_trace_type == otg_trace_msg_invalid_n || ++ s->otg_trace_type == otg_trace_msg_va_list_n) { ++ /* Ignore this slot, it's not valid, or is part ++ of a previously processed varargs. */ ++ return(0); ++ } ++ *ps = s; ++ res = 1; ++ // Is there a previous event (for "ticks" calculation)? ++ previous = (index - 1) & TRACE_MASK; ++ if (previous != tss->next && tss->total > 1) { ++ // There is a valid previous event. ++ res = 2; ++ o = tss->traces + previous; ++ /* If the previous event was a varargs event, we want ++ a copy of the first slot, not the last. */ ++ while (o->otg_trace_type == otg_trace_msg_va_list_n) { ++ previous = (previous - 1) & TRACE_MASK; ++ if (previous == tss->next) { ++ res = 1; ++ o = NULL; ++ break; ++ } ++ o = tss->traces + previous; ++ } ++ *po = o; ++ } ++ return(res); ++} ++ ++ ++/*! otg_trace_proc_read - implement proc file system read. ++ * ++ * Standard proc file system read function. ++ * @file ++ * @buf ++ * @count ++ * @pos ++ */ ++int otg_trace_proc_read(char *page, int count, int * pos) ++{ ++ int len = 0; ++ int index; ++ int oindex; ++ int rc; ++ ++ u64 ticks = 0; ++ ++ unsigned char *cp; ++ int skip = 0; ++ ++ otg_trace_t *o; ++ otg_trace_t *s; ++ otg_trace_snapshot *tss; ++ ++ DOWN(&otg_trace_sem); ++ ++ /* Grab the current snapshot, and replace it with the other. This ++ * needs to be atomic WRT otg_trace_msg() calls. ++ */ ++ UNLESS (*pos) { ++ unsigned long flags; ++ tss = otg_trace_other_traces; ++ init_snapshot(tss); // clear previous ++ local_irq_save(flags); ++ otg_trace_other_traces = otg_trace_current_traces; // swap snapshots ++ otg_trace_current_traces = tss; ++ local_irq_restore(flags); ++ } ++ ++ tss = otg_trace_other_traces; ++ ++ /* Get the index of the entry to read - this needs to be atomic WRT tag operations. ++ */ ++ s = o = NULL; ++ oindex = index = (*pos)++; ++ do { ++ rc = otg_trace_read_slot(tss,index,&s,&o); ++ switch (rc) { ++ case -1: // End of data. ++ UP(&otg_trace_sem); ++ return(0); ++ ++ case 0: // Invalid slot, skip it and try the next. ++ index = (*pos)++; ++ break; ++ ++ case 1: // s valid, o NULL ++ case 2: // s and o valid ++ break; ++ } ++ } while (rc == 0); ++ ++ len = 0; ++ ++ UNLESS (oindex) ++ len += sprintf((char *) page + len, "Tag Index Ints Framenum Ticks\n"); ++ ++ /* If there is a previous trace event, we want to calculate how many ++ * ticks have elapsed siince it happened. Unfortunately, determining ++ * if there _is_ a previous event isn't obvious, since we have to watch ++ * out for startup, varargs and wraparound. ++ */ ++ if (o) { ++ ++ ticks = otg_tmr_elapsed(&s->va.s.ticks, &o->va.s.ticks); ++ ++ if ((o->va.s.interrupts != s->va.s.interrupts) || (o->tag != s->tag) || (o->in_interrupt != s->in_interrupt)) ++ skip++; ++ } ++ ++ len += sprintf((char *) page + len, "%s%02x %c %8u %8d ", skip?"\n":"", ++ s->tag, s->id_gnd ? 'A' : 'B', index, s->va.s.interrupts); ++ ++ len += sprintf((char *) page + len, "%03x ", s->va.s.framenum); ++ ++ if (ticks < 10000) ++ len += sprintf((char *) page + len, "%8duS", ticks); ++ else if (ticks < 10000000) ++ len += sprintf((char *) page + len, "%8dmS", ticks >> 10); ++ else ++ len += sprintf((char *) page + len, "%8dS ", ticks >> 20); ++ ++ len += sprintf((char *) page + len, " %s ", s->in_interrupt ? "--" : "=="); ++ len += sprintf((char *) page + len, "%-24s: ",s->va.s.function); ++ ++ switch (s->otg_trace_type) { ++ case otg_trace_msg_invalid_n: ++ case otg_trace_msg_va_list_n: ++ len += sprintf((char *) page + len, " -- N/A"); ++ break; ++ ++ case otg_trace_msg_va_start_n: ++ if (s->va_num_args <= 1) ++ len += sprintf((char *) page + len, s->va.s.trace.msg32.msg, s->va.s.trace.msg32.val); ++ ++ else { ++ otg_trace_t *v = otg_trace_next_va(tss,s); ++ len += sprintf((char *) page + len, s->va.s.trace.msg32.msg, s->va.s.trace.msg32.val, ++ v->va.l.val[0], v->va.l.val[1], v->va.l.val[2], v->va.l.val[3], ++ v->va.l.val[4], v->va.l.val[5], v->va.l.val[6]); ++ } ++ break; ++ ++ case otg_trace_msg_n: ++ len += sprintf((char *) page + len, s->va.s.trace.msg.msg); ++ break; ++ ++ case otg_trace_msg32_n: ++ len += sprintf((char *) page + len, s->va.s.trace.msg32.msg, s->va.s.trace.msg32.val); ++ break; ++ ++ case otg_trace_msg16_n: ++ len += sprintf((char *) page + len, s->va.s.trace.msg16.msg, s->va.s.trace.msg16.val0, s->va.s.trace.msg16.val1); ++ break; ++ ++ case otg_trace_msg8_n: ++ len += sprintf((char *) page + len, s->va.s.trace.msg8.msg, ++ s->va.s.trace.msg8.val0, s->va.s.trace.msg8.val1, s->va.s.trace.msg8.val2, ++ s->va.s.trace.msg8.val3); ++ break; ++ ++ case otg_trace_setup_n: ++ cp = (unsigned char *)&s->va.s.trace.setup; ++ len += sprintf((char *) page + len, ++ " -- request [%02x %02x %02x %02x %02x %02x %02x %02x]", ++ cp[0], cp[1], cp[2], cp[3], cp[4], cp[5], cp[6], cp[7]); ++ break; ++ } ++ len += sprintf((char *) page + len, "\n"); ++ ++ UP(&otg_trace_sem); ++ return len; ++} ++ ++/*! otg_trace_proc_write - implement proc file system write. ++ * @file ++ * @buf ++ * @count ++ * @pos ++ * ++ * Proc file system write function. ++ */ ++int otg_trace_proc_write (const char *buf, int count, int * pos) ++{ ++#define MAX_TRACE_CMD_LEN 64 ++ char command[MAX_TRACE_CMD_LEN+1]; ++ size_t n = count; ++ size_t l; ++ char c; ++ ++ if (n > 0) { ++ l = MIN(n,MAX_TRACE_CMD_LEN); ++ if (copy_from_user (command, buf, l)) ++ count = -EFAULT; ++ else { ++ n -= l; ++ while (n > 0) { ++ if (copy_from_user (&c, buf + (count - n), 1)) { ++ count = -EFAULT; ++ break; ++ } ++ n -= 1; ++ } ++ // Terminate command[] ++ if (l > 0 && command[l-1] == '\n') { ++ l -= 1; ++ } ++ command[l] = 0; ++ } ++ } ++ ++ if (0 >= count) { ++ printk(KERN_INFO"%s: count <= 0 %d\n", __FUNCTION__, count); ++ return count; ++ } ++ ++ ++ return count; ++} ++ ++static u32 otg_tags_mask = 0x0; ++extern void otg_trace_exit (void); ++ ++/*! otg_trace_obtain_tag(void) ++ */ ++otg_tag_t otg_trace_obtain_tag(void) ++{ ++ /* Return the next unused tag value [1..32] if one is available, ++ * return 0 if none is available. ++ */ ++ otg_tag_t tag = 0; ++ DOWN(&otg_trace_sem); ++ if (otg_tags_mask == 0xffffffff || otg_trace_exiting) { ++ // They're all in use. ++ UP(&otg_trace_sem); ++ return tag; ++ } ++ if (otg_tags_mask != 0x00000000) { ++ // 2nd or later tag, search for an unused one ++ while (otg_tags_mask & (0x1 << tag)) { ++ tag += 1; ++ } ++ } ++ ++ // Successful 1st or later tag. ++ otg_tags_mask |= 0x1 << tag; ++ UP(&otg_trace_sem); ++ return tag + 1; ++} ++ ++/*! otg_trace_invalidate_tag(otg_tag_t tag) ++ */ ++otg_tag_t otg_trace_invalidate_tag(otg_tag_t tag) ++{ ++ otg_trace_t *p,*e; ++ otg_trace_snapshot *css,*oss; ++ unsigned long flags; ++ ++ // Nothing to do ++ RETURN_ZERO_IF ((0 >= tag) || (tag > 32)); ++ ++ // Grab a local copy of the snapshot pointers. ++ local_irq_save(flags); ++ css = otg_trace_current_traces; ++ oss = otg_trace_other_traces; ++ local_irq_restore(flags); ++ ++ ++ // Run through all the trace messages, and invalidate any with the given tag. ++ ++ DOWN(&otg_trace_sem); ++ if (css) ++ for (e = TRACE_MAX + (p = css->traces); p < e; p++) { ++ if (p->tag == tag) { ++ p->otg_trace_type = otg_trace_msg_invalid_n; ++ p->tag = 0; ++ } ++ } ++ if (oss) ++ for (e = TRACE_MAX + (p = oss->traces); p < e; p++) { ++ if (p->tag == tag) { ++ p->otg_trace_type = otg_trace_msg_invalid_n; ++ p->tag = 0; ++ } ++ } ++ ++ // Turn off the tag ++ otg_tags_mask &= ~(0x1 << (tag-1)); ++ if (otg_tags_mask == 0x00000000) { /* otg_trace_exit (); */ } ++ UP(&otg_trace_sem); ++ return 0; ++} ++ ++ ++ ++/* Module init ************************************************************** */ ++ ++#define OT otg_trace_init_test ++otg_tag_t OT; ++ ++/*! otg_trace_init ++ * ++ * Return non-zero if not successful. ++ */ ++int otg_trace_init (void) ++{ ++ otg_trace_exiting = 0; ++ otg_trace_current_traces = otg_trace_other_traces = NULL; ++ UNLESS ((otg_trace_current_traces = get_snapshot(&otg_snapshot1)) && ++ (otg_trace_other_traces = get_snapshot(&otg_snapshot2))) ++ { ++ printk(KERN_ERR"%s: malloc failed (2x) %d\n", __FUNCTION__, sizeof(otg_trace_t) * TRACE_MAX); ++ otg_trace_current_traces = rel_snapshot(otg_trace_current_traces); ++ otg_trace_other_traces = rel_snapshot(otg_trace_other_traces); ++ return -ENOMEM; ++ } ++ OT = otg_trace_obtain_tag(); ++ RETURN_ENOMEM_IF(!OT); ++ TRACE_MSG0(OT,"--"); ++ return 0; ++} ++ ++/*! otg_trace_exit - remove procfs entry, free trace data space. ++ */ ++void otg_trace_exit (void) ++{ ++ otg_trace_t *p; ++ otg_tag_t tag; ++ ++ unsigned long flags; ++ otg_trace_snapshot *c,*o; ++ ++ otg_trace_invalidate_tag(OT); ++ otg_trace_exiting = 1; ++ ++ local_irq_save (flags); ++ ++ /* Look for any outstanding tags. */ ++ for (tag = 32; otg_tags_mask && tag > 0; tag--) ++ if (otg_tags_mask & (0x1 << (tag-1))) ++ otg_trace_invalidate_tag(tag); ++ ++ c = otg_trace_current_traces; ++ o = otg_trace_other_traces; ++ otg_trace_current_traces = otg_trace_other_traces = NULL; ++ local_irq_restore (flags); ++ ++ c = rel_snapshot(c); ++ o = rel_snapshot(o); ++} ++ ++#else /* defined(CONFIG_OTG_TRACE) || defined(CONFIG_OTG_TRACE_MODULE) */ ++int otg_trace_init (void) {return 0;} ++void otg_trace_exit (void) {} ++otg_tag_t otg_trace_obtain_tag(void) { return 0; } ++void otg_trace_setup(otg_tag_t tag, const char *fn, void *setup) { } ++void otg_trace_msg(otg_tag_t tag, const char *fn, u8 nargs, char *fmt, ...) { } ++otg_tag_t otg_trace_invalidate_tag(otg_tag_t tag) { return 0; } ++#endif /* defined(CONFIG_OTG_TRACE) || defined(CONFIG_OTG_TRACE_MODULE) */ ++ ++OTG_EXPORT_SYMBOL(otg_trace_setup); ++OTG_EXPORT_SYMBOL(otg_trace_msg); ++OTG_EXPORT_SYMBOL(otg_trace_obtain_tag); ++OTG_EXPORT_SYMBOL(otg_trace_invalidate_tag); ++ ++#if 0 ++// These are only needed when debugging the trace code itself. ++OTG_EXPORT_SYMBOL(otg_trace_get_next); ++OTG_EXPORT_SYMBOL(otg_trace_next_va); ++OTG_EXPORT_SYMBOL(slot_in_data); ++OTG_EXPORT_SYMBOL(otg_trace_read_slot); ++OTG_EXPORT_SYMBOL(otg_trace_proc_read); ++#endif ++ +diff -uNr linux/drivers/no-otg/otgcore/otg.c linux/drivers/otg/otgcore/otg.c +--- linux/drivers/no-otg/otgcore/otg.c 1970-01-01 01:00:00.000000000 +0100 ++++ linux/drivers/otg/otgcore/otg.c 2006-09-01 21:41:35.000000000 +0200 +@@ -0,0 +1,936 @@ ++/* ++ * otg/otgcore/otg.c - OTG state machine ++ * ++ * Copyright (c) 2004 Belcarra ++ * ++ * By: ++ * Stuart Lynne <sl@lbelcarra.com>, ++ * Tom Rushworth <tbr@belcarra.com>, ++ * Bruce Balden <balden@belcarra.com> ++ * ++ */ ++/*! ++ * @file otg/otgcore/otg.c ++ * @brief OTG Core State Machine Event Processing ++ * ++ * The OTG State Machine receives "input" information passed to it ++ * from the other drivers (pcd, tcd, hcd and ocd) that describe what ++ * is happening. ++ * ++ * The State Machine uses the inputs to move from state to state. Each ++ * state defines four things: ++ * ++ * 1. Reset - what inputs to set or reset on entry to the state. ++ * ++ * 2. Outputs - what output functions to call to set or reset. ++ * ++ * 3. Timeout - an optional timeout value to set ++ * ++ * 4. Tests - a series of tests that allow the state machine to move to ++ * new states based on current or new inputs. ++ * ++ * @ingroup OTGCore ++ */ ++#ifndef OTG_REGRESS ++ ++#include <otg/otg-compat.h> ++#include <otg/otg-module.h> ++ ++#include <otg/usbp-chap9.h> ++#include <otg/usbp-func.h> ++#include <otg/usbp-bus.h> ++ ++#include <otg/otg-trace.h> ++#include <otg/otg-api.h> ++#include <otg/otg-tcd.h> ++#include <otg/otg-hcd.h> ++#include <otg/otg-pcd.h> ++#include <otg/otg-ocd.h> ++ ++ ++struct hcd_ops *otg_hcd_ops; ++struct ocd_ops *otg_ocd_ops; ++struct pcd_ops *otg_pcd_ops; ++struct tcd_ops *otg_tcd_ops; ++struct usbd_ops *otg_usbd_ops; ++ ++extern struct otg_instance otg_instance_private; ++extern struct hcd_instance hcd_instance_private; ++extern struct ocd_instance ocd_instance_private; ++extern struct pcd_instance pcd_instance_private; ++extern struct tcd_instance tcd_instance_private; ++ ++#if 0 ++/* otg_vbus - return vbus status ++ * ++ * XXX Should we default to set or reset if there is no vbus function ++ */ ++int otg_vbus(struct otg_instance *otg) ++{ ++ TRACE_MSG2(CORE, "otg_tcd_ops: %p vbus: %p", otg_tcd_ops, otg_tcd_ops ? otg_tcd_ops->vbus : NULL); ++ return (otg_tcd_ops && otg_tcd_ops->vbus) ? otg_tcd_ops->vbus(otg) : 1; ++} ++#endif ++ ++#if defined (OTG_WINCE) ++#define TRACE_SM_CURRENT(t, m, o) \ ++ ++#else ++#define TRACE_SM_CURRENT(t, m, o) \ ++ TRACE_MSG5(t, "SM_CURRENT: RESET: %08x SET: %08x %s (%s) %s",\ ++ (u32)(~o->current_inputs), \ ++ (u32)(o->current_inputs), \ ++ o->current_outputs->name, \ ++ o->previous_outputs->name, \ ++ m \ ++ ); ++#endif //OTG_WINCE ++#define TRACE_SM_CHANGE(t, m, o) \ ++ TRACE_MSG5(t, "SM_CHANGE: RESET: %08x SET: %08x %s (%s) %s",\ ++ (u32)(~o->current_inputs), \ ++ (u32)(o->current_inputs), \ ++ o->current_outputs->name, \ ++ o->previous_outputs->name, \ ++ m \ ++ ); ++ ++#define TRACE_SM_NEW(t, m, o) \ ++ TRACE_MSG5(t, "SM_NEW: RESET: %08x SET: %08x %s (%s) %s",\ ++ (u32)(~o->current_inputs), \ ++ (u32)(o->current_inputs), \ ++ o->current_outputs->name, \ ++ o->previous_outputs->name, \ ++ m \ ++ ); ++ ++ ++#define TRACE_SM_INPUTS(t, m, u, c) \ ++ TRACE_MSG4(t, "SM_INPUTS: RESET: %08x SET: %08x CUR: %08x %s",\ ++ (u32)(u >> 32), \ ++ (u32)(u & 0xffffffff), \ ++ c, \ ++ m \ ++ ) ++ ++#endif ++ ++ ++ ++DECLARE_MUTEX (otg_sem); ++ ++/*! ++ * Output change lookup table - this maps the current output and the desired ++ * output to what should be done. This is just so that we don't redo changes, ++ * if the output does not change we do not want to re-call the output function ++ */ ++u8 otg_output_lookup[4][4] = { ++ /* NC SET RESET PULSE, <--- current/ new */ ++ { NC, NC, NC, PULSE, }, /* NC */ ++ { SET, NC, SET, PULSE, }, /* SET */ ++ { RESET, RESET, NC, PULSE, }, /* RESET */ ++ { PULSE, PULSE, PULSE, PULSE, }, /* UNKNOWN */ ++}; ++ ++ ++/*! ++ * otg_new() - check OTG new inputs and select new state to be in ++ * ++ * This is used by the OTG event handler to see if the state should ++ * change based on the current input values. ++ * ++ * @param otg pointer to the otg instance. ++ * @return either the next OTG state or invalid_state if no change. ++ */ ++static int otg_new(struct otg_instance *otg) ++{ ++ int state = otg->state; ++ struct otg_test *otg_test = otg_firmware_loaded ? otg_firmware_loaded->otg_tests : NULL; ++ u64 input_mask = ((u64)otg->current_inputs) | ((u64)(~otg->current_inputs) << 32); // get a copy of current inputs ++ ++ UNLESS(otg_test) return invalid_state; ++ ++ TRACE_MSG3(CORE, "OTG_NEW: %s inputs: %08x %08x", otg_get_state_name(state), ++ (u32)(input_mask>>32&0xffffffff), (u32)(input_mask&0xffffffff)); ++ ++ /* iterate across the otg inputs table, for each entry that matches the current ++ * state, test the input_mask to see if a state transition is required. ++ */ ++ for (; otg_test->target != invalid_state; otg_test++) { ++ ++ u64 test1 = otg_test->test1; ++ u64 test2 = otg_test->test2; ++ u64 test3 = otg_test->test3; ++ ++ CONTINUE_UNLESS(otg_test->state == state); // skip states that don't match ++ ++ ++ TRACE_MSG7(CORE, "OTG_NEW: %s (%s, %d) test1: %08x %08x test2 %08x %08x", ++ otg_get_state_name(otg_test->state), ++ otg_get_state_name(otg_test->target), ++ otg_test->test, ++ (u32)(test1>>32&0xffffffff), (u32)(test1&0xffffffff), ++ (u32)(test2>>32&0xffffffff), (u32)(test2&0xffffffff) ++ ); ++ ++ /* the otg inputs table has multiple masks that define between multiple tests. Each ++ * test is a simple OR to check if specific inputs are present. ++ */ ++ CONTINUE_UNLESS ( ++ (!otg_test->test1 || ((test1 = otg_test->test1 & input_mask))) && ++ (!otg_test->test2 || ((test2 = otg_test->test2 & input_mask))) && ++ (!otg_test->test3 || ((test3 = otg_test->test3 & input_mask))) /*&& ++ (!otg_test->test4 || (otg_test->test4 & input_mask)) */ ); ++ ++ TRACE_MSG7(CORE, "OTG_NEW: GOTO %s test1: %08x %08x test2 %08x %08x test3 %08x %08x", ++ otg_get_state_name(otg_test->target), ++ (u32)(test1>>32&0xffffffff), (u32)(test1&0xffffffff), ++ (u32)(test2>>32&0xffffffff), (u32)(test2&0xffffffff), ++ (u32)(test3>>32&0xffff), (u32)(test3&0xffff) ++ ); ++ ++ return otg_test->target; ++ } ++ return invalid_state; ++} ++ ++char mbuf[96]; ++ ++/*! ++ * otg_write_state_message_irq() - ++ * ++ * Send a message to the otg management application. ++ * ++ * @param msg ++ * @param reset ++ * @param inputs ++ */ ++void otg_write_state_message_irq(char *msg, u64 reset, u32 inputs) ++{ ++ sprintf(mbuf, "State: %s reset: %08x set: %08x inputs: %08x ", msg, (u32)(reset>>32), (u32)(reset&0xffffffff), inputs); ++ mbuf[95] = '\0'; ++ otg_message(mbuf); ++} ++ ++/*! ++ * otg_write_output_message_irq() - ++ * ++ * Send a message to the otg management application. ++ * ++ * @param msg ++ * @param val ++ */ ++void otg_write_output_message_irq(char *msg, int val) ++{ ++ strcpy(mbuf, "Output: "); ++ strncat(mbuf, msg, 64 - strlen(mbuf)); ++ if (val == 2) strncat(mbuf, "/", 64 - strlen(mbuf)); ++ mbuf[95] = '\0'; ++ otg_message(mbuf); ++} ++ ++/*! ++ * otg_write_timer_message_irq() - ++ * ++ * Send a message to the otg management application. ++ * ++ * @param msg ++ * @param val ++ */ ++void otg_write_timer_message_irq(char *msg, int val) ++{ ++ if (val < 10000) ++ sprintf(mbuf, "Timer: %s %d uS %u", msg, val, otg_tmr_ticks()); ++ else if (val < 10000000) ++ //sprintf(time_buf, " %d mS\n", ticks / 1000); ++ sprintf(mbuf, "Timer: %s %d mS %u", msg, val >> 10, otg_tmr_ticks()); ++ else ++ //sprintf(time_buf, " %d S\n", ticks / 1000000); ++ sprintf(mbuf, "Timer: %s %d S %u", msg, val >> 20, otg_tmr_ticks()); ++ ++ mbuf[95] = '\0'; ++ otg_message(mbuf); ++} ++ ++/*! ++ * otg_write_info_message_irq() - ++ * ++ * Send a message to the otg management application. ++ * ++ * @param msg ++ */ ++void otg_write_info_message(char *msg) ++{ ++ unsigned long flags; ++ local_irq_save (flags); ++ sprintf(mbuf, "Info: %s", msg); ++ mbuf[95] = '\0'; ++ otg_message(mbuf); ++ local_irq_restore (flags); ++} ++ ++/*! ++ * otg_write_reset_message_irq() - ++ * ++ * Send a message to the otg management application. ++ * ++ * @param msg ++ * @param val ++ */ ++void otg_write_reset_message(char *msg, int val) ++{ ++ unsigned long flags; ++ local_irq_save (flags); ++ sprintf(mbuf, "State: %s reset: %x", msg, val); ++ mbuf[95] = '\0'; ++ otg_message(mbuf); ++ local_irq_restore (flags); ++} ++ ++OTG_EXPORT_SYMBOL(otg_write_info_message); ++ ++char otg_change_names[4] = { ++ '_', ' ', '/', '#', ++}; ++ ++/*! ++ * otg_do_event_irq() - process OTG events and determine OTG outputs to reflect new state ++ * ++ * This function is passed a mask with changed input values. This ++ * is passed to the otg_new() function to see if the state should ++ * change. If it changes then the output functions required for ++ * the new state are called. ++ * ++ * @param otg pointer to the otg instance. ++ * @param inputs input mask ++ * @param tag trace tag to use for tracing ++ * @param msg message for tracing ++ * @return non-zero if state changed. ++ */ ++static int otg_do_event_irq(struct otg_instance *otg, u64 inputs, otg_tag_t tag, char *msg) ++{ ++ u32 current_inputs = otg->current_inputs; ++ int current_state = otg->state; ++ u32 inputs_set = (u32)(inputs & 0xffffffff); // get changed inputs that SET something ++ u32 inputs_reset = (u32) (inputs >> 32); // get changed inputs that RESET something ++ int target; ++ ++ RETURN_ZERO_UNLESS(otg && inputs);; ++ TRACE_SM_INPUTS(tag, msg, inputs, current_inputs); ++ ++ /* Special Overrides - These are special tests that satisfy specific injunctions from the ++ * OTG Specification. ++ */ ++ if (inputs_set & inputs_reset) { // verify that there is no overlap between SET and RESET masks ++ TRACE_MSG1(CORE, "OTG_EVENT: ERROR attempting to set and reset the same input: %08x", inputs_set & inputs_reset); ++ return 0; ++ } ++ /* don't allow bus_req and bus_drop to be set at the same time ++ */ ++ if ((inputs_set & (bus_req | bus_drop)) == (bus_req | bus_drop) ) { ++ TRACE_MSG2(CORE, "OTG_EVENT: ERROR attempting to set both bus_req and bus_drop: %08x %08x", ++ inputs_set, (bus_req | bus_drop)); ++ return 0; ++ } ++ /* set bus_drop_ if bus_req, set bus_req_ if bus_drop ++ */ ++ if (inputs_set & bus_req) { ++ inputs |= bus_drop_; ++ TRACE_MSG1(CORE, "OTG_EVENT: forcing bus_drop/: %08x", inputs_set); ++ } ++ if (inputs_set & bus_drop) { ++ inputs |= bus_req_; ++ TRACE_MSG1(CORE, "OTG_EVENT: forcing bus_req/: %08x", inputs_set); ++ } ++ ++ otg->current_inputs &= ~inputs_reset; ++ otg->current_inputs |= inputs_set; ++ TRACE_MSG3(CORE, "OTG_EVENT: reset: %08x set: %08x inputs: %08x", inputs_reset, inputs_set, otg->current_inputs); ++ ++ TRACE_SM_CHANGE(tag, msg, otg); ++ ++ RETURN_ZERO_IF(otg->active); ++ ++ otg->active++; ++ ++ /* Search the state input change table to see if we need to change to a new state. ++ * This may take several iterations. ++ */ ++ while (invalid_state != (target = otg_new(otg))) { ++ ++ struct otg_state *otg_state; ++ int original_state = otg->state; ++ u64 current_outputs; ++ u64 output_results; ++ u64 new_outputs; ++ int i; ++ ++ /* if previous output started timer, then cancel timer before proceeding ++ */ ++ if ((otg_state = otg->current_outputs) && otg_state->tmout && otg->start_timer) { ++ TRACE_MSG0(CORE, "reseting timer"); ++ otg->start_timer(otg, 0); ++ } ++ ++ BREAK_UNLESS(otg_firmware_loaded); ++ ++ BREAK_UNLESS(target < otg_firmware_loaded->number_of_states); ++ ++ otg_state = otg_firmware_loaded->otg_states + target; ++ ++ BREAK_UNLESS(otg_state->name); ++ ++ otg->previous = otg->state; ++ otg->state = target; ++ otg->tickcount = otg_tmr_ticks(); ++ ++ ++ otg->previous_outputs= otg->current_outputs; ++ //otg->current_outputs = NULL; ++ ++ ++ /* A matching input table rule has been found, we are transitioning to a new ++ * state. We need to find the new state entry in the output table. ++ * XXX this could be a table lookup instead of linear search. ++ */ ++ ++ current_outputs = otg->outputs; ++ new_outputs = otg_state->outputs; ++ output_results = 0; ++ ++ TRACE_SM_NEW(tag, msg, otg); ++ ++ /* reset any inputs that the new state want's reset ++ */ ++ otg_do_event_irq(otg, otg_state->reset | TMOUT_, tag, otg_state->name); ++ ++ otg_write_state_message_irq(otg_state->name, otg_state->reset, otg->current_inputs); ++ ++#if 1 ++ switch (target) { /* C.f. 6.6.1.12 b_conn reset */ ++ case otg_disabled: ++ otg->current_inputs = 0; ++ otg->outputs = 0; ++ break; ++ default: ++ //otg_event_irq(otg, not(b_conn), "a_host/ & a_suspend/: set b_conn/"); ++ break; ++ } ++#endif ++ TRACE_MSG5(CORE, "OTG_EVENT: CALLING OUTPUT MAX:%d OUTPUTS CURRENT: %08x %08x NEW: %08x %08x", ++ MAX_OUTPUTS, ++ (u32)(current_outputs >> 32), ++ (u32)(current_outputs & 0xffffffff), ++ (u32)(new_outputs >> 32), ++ (u32)(new_outputs & 0xffffffff) ++ ); ++ ++ /* Iterate across the outputs bitmask, calling the appropriate functions ++ * to make required changes. The current outputs are saved and we DO NOT ++ * make calls to change output values until they change again. ++ */ ++ for (i = 0; i < MAX_OUTPUTS; i++) { ++ ++ u8 current_output = (u8)(current_outputs & 0x3); ++ u8 new_output = (u8)(new_outputs & 0x3); ++ u8 changed = otg_output_lookup[new_output][current_output]; ++ ++ output_results >>= 2; ++ ++ switch (changed) { ++ case NC: ++ output_results |= ((current_outputs & 0x3) << (MAX_OUTPUTS * 2)); ++ break; ++ case SET: ++ case RESET: ++ output_results |= (((u64)changed) << (MAX_OUTPUTS * 2)); ++ case PULSE: ++ ++ ++ otg_write_output_message_irq(otg_output_names[i], changed); ++ ++ //TRACE_MSG3(CORE, "OTG_EVENT: CHECKING OUTPUT %s %d %s", ++ // otg_state->name, i, otg_output_names[i]); ++ ++ BREAK_UNLESS(otg->otg_output_ops[i]); // Check if we have an output routine ++ ++ TRACE_MSG7(CORE, "OTG_EVENT: CALLING OUTPUT %s %d %s%c cur: %02x new: %02x chng: %02x", ++ otg_state->name, i, otg_output_names[i], ++ otg_change_names[changed&0x3], ++ current_output, new_output, ++ changed); ++ ++ otg->otg_output_ops[i](otg, changed); // Output changed, call function to do it ++ ++ //TRACE_MSG2(CORE, "OTG_EVENT: OUTPUT FINISED %s %s", otg_state->name, otg_output_names[i]); ++ break; ++ } ++ ++ current_outputs >>= 2; ++ new_outputs >>= 2; ++ } ++ ++ output_results >>= 2; ++ otg->outputs = output_results; ++ otg->current_outputs = otg_state; ++ ++ //TRACE_MSG2(CORE, "OTG_EVENT: STATE: otg->current_outputs: %08x %08x", ++ // (u32)(otg->outputs & 0xffffffff), (u32)(otg->outputs >> 32)); ++ ++ if (((otg->outputs & pcd_en_out_set) == pcd_en_out_set) && ++ ((otg->outputs & hcd_en_out_set) == hcd_en_out_set)) ++ { ++ TRACE_MSG0(CORE, "WARNING PCD_EN and HCD_EN both set"); ++ printk(KERN_INFO "%s: WARNING PCD_EN and HCD_EN both set\n", __FUNCTION__); ++ } ++ ++ /* start timer? ++ */ ++ CONTINUE_UNLESS(otg_state->tmout); ++ TRACE_MSG1(CORE, "setting timer: %d", otg_state->tmout); ++ if (otg->start_timer) { ++ otg_write_timer_message_irq(otg_state->name, otg_state->tmout); ++ otg->start_timer(otg, otg_state->tmout); ++ } ++ else { ++ //TRACE_MSG0(CORE, "NO TIMER"); ++ otg_do_event_irq(otg, TMOUT_, CORE, otg_state->name); ++ } ++ ++ } ++ //TRACE_MSG0(CORE, "finishing"); ++ otg->active = 0; ++ ++ if (current_state == otg->state) ++ TRACE_MSG0(CORE, "OTG_EVENT: NOT CHANGED"); ++ ++ otg->active = 0; ++ return 0; ++} ++ ++/*! ++ * otg_write_input_message_irq() - ++ ++ * @param inputs input mask ++ * @param tag trace tag to use for tracing ++ * @param msg message for tracing ++ */ ++void otg_write_input_message_irq(u64 inputs, otg_tag_t tag, char *msg) ++{ ++ u32 reset = (u32)(inputs >> 32); ++ u32 set = (u32) (inputs & 0xffffffff); ++ ++ char buf[64]; ++ //snprintf(buf, sizeof(buf), "Input: %08x %08x %s", *inputs >> 32, *inputs & 0xffffffff, msg ? msg : "NULL"); ++ TRACE_MSG3(tag, "Inputs: %08x %08x %s", set, reset, msg ? msg : "NULL"); ++ #ifdef OTG_WINCE ++ sprintf(buf, "Inputs: %08x %08x %s", reset, set, msg ? msg : "NULL"); ++ #else /* OTG_WINCE */ ++ snprintf(buf, sizeof(buf), "Inputs: %08x %08x %s", reset, set, msg ? msg : "NULL"); ++ #endif /* OTG_WINCE */ ++ buf[63] = '\0'; ++ otg_message(buf); ++} ++ ++ ++/*! ++ * otg_event_input_message_irq() - ++ * @param otg pointer to the otg instance. ++ * @param inputs input mask ++ * @param tag trace tag to use for tracing ++ * @param msg message for tracing ++ * @return non-zero if state changed. ++ */ ++int otg_event_irq(struct otg_instance *otg, u64 inputs, otg_tag_t tag, char *msg) ++{ ++ TRACE_SM_INPUTS(tag, msg, inputs, otg->current_inputs); ++ //otg_write_input_message_irq((u32)(inputs >> 32), (u32) (inputs & 0xffffffff), tag, msg); ++ otg_write_input_message_irq(inputs, tag, msg); ++ return otg_do_event_irq(otg, inputs, tag, msg); ++} ++ ++/*! ++ * otg_event() - ++ * @param otg pointer to the otg instance. ++ * @param inputs input mask ++ * @param tag trace tag to use for tracing ++ * @param msg message for tracing ++ * @return non-zero if state changed. ++ */ ++int otg_event(struct otg_instance *otg, u64 inputs, otg_tag_t tag, char *msg) ++{ ++ int rc; ++ unsigned long flags; ++ RETURN_ZERO_UNLESS(otg); ++ local_irq_save (flags); ++ rc = otg_event_irq(otg, inputs, tag, msg); ++ local_irq_restore (flags); ++ return rc; ++} ++ ++ ++/*! ++ * otg_event_set_irq() - ++ * @param otg pointer to the otg instance. ++ * @param changed ++ * @param flag ++ * @param input input mask ++ * @param tag trace tag to use for tracing ++ * @param msg message for tracing ++ * @return non-zero if state changed. ++ */ ++int otg_event_set_irq(struct otg_instance *otg, int changed, int flag, u32 input, otg_tag_t tag, char *msg) ++{ ++ RETURN_ZERO_UNLESS(changed); ++ TRACE_MSG4(tag, "%s: %08x changed: %d flag: %d", msg, input, changed, flag); ++ return otg_event(otg, flag ? input : _NOT(input), tag, msg); ++} ++ ++ ++/*! ++ * otg_init() - create and initialize otg instance ++ * ++ * Called to initialize the OTG state machine. This will cause the state ++ * to change from the invalid_state to otg_disabled. ++ * ++ * If the device has the OCD_CAPABILITIES_AUTO flag set then ++ * an initial enable_otg event is generated. This will cause ++ * the state to move from otg_disabled to otg_enabled. The ++ * requird drivers will be initialized by calling the appropriate ++ * output functions: ++ * ++ * - pcd_init_func ++ * - hcd_init_func ++ * - tcd_init_func ++ * ++ * @param otg pointer to the otg instance. ++ */ ++void otg_init (struct otg_instance *otg) ++{ ++ DOWN(&otg_sem); ++ otg->outputs = tcd_init_out_ | pcd_init_out_ | hcd_init_out_; ++ ++ /* This will move the state machine into the otg_disabled state ++ */ ++ otg_event(otg, enable_otg, CORE, "enable_otg"); // XXX ++ ++ if (otg_ocd_ops->capabilities & OCD_CAPABILITIES_AUTO) ++ otg_event(otg, AUTO, CORE, "AUTO"); // XXX ++ ++ UP(&otg_sem); ++} ++ ++ ++/*! ++ * otg_exit() ++ * ++ * This is called by the driver that started the state machine to ++ * cause it to exit. The state will move to otg_disabled. ++ * ++ * The appropriate output functions will be called to disable the ++ * peripheral drivers. ++ * ++ * @param otg pointer to the otg instance. ++ */ ++void otg_exit (struct otg_instance *otg) ++{ ++ //TRACE_MSG0(CORE, "DOWN OTG_SEM"); ++ DOWN(&otg_sem); ++ ++ //TRACE_MSG0(CORE, "OTG_EXIT"); ++ //otg_event(otg, exit_all | not(a_bus_req) | a_bus_drop | not(b_bus_req), "tcd_otg_exit"); ++ ++ otg_event(otg, enable_otg_, CORE, "enable_otg_"); ++ ++ while (otg->state != otg_disabled) { ++ // TRACE_MSG pointless here, module will be gon before we can look at it. ++ #ifdef OTG_WINCE ++ //DEBUGMSG(KERN_ERR"%s: waiting for state machine to disable\n", __FUNCTION__); ++ #else /* OTG_WINCE */ ++ printk(KERN_ERR"%s: waiting for state machine to disable\n", __FUNCTION__); ++ #endif /* OTG_WINCE */ ++ SCHEDULE_TIMEOUT(10 * HZ); ++ } ++ //TRACE_MSG0(CORE, "UP OTG_SEM"); ++ UP(&otg_sem); ++} ++ ++ ++OTG_EXPORT_SYMBOL(otg_event); ++OTG_EXPORT_SYMBOL(otg_event_irq); ++OTG_EXPORT_SYMBOL(otg_init); ++OTG_EXPORT_SYMBOL(otg_exit); ++OTG_EXPORT_SYMBOL(otg_event_set_irq); ++ ++/* ********************************************************************************************* */ ++ ++/*! ++ * otg_tcd_init_func() - ++ * ++ * This is a default tcd_init_func. It will be used ++ * if no other is provided. ++ * ++ * @param otg pointer to the otg instance. ++ * @param flag set or reset ++ */ ++void otg_tcd_init_func (struct otg_instance *otg, u8 flag) ++{ ++ otg_event(otg, TCD_OK, CORE, "TCD_OK"); ++} ++ ++/*! ++ * otg_pcd_init_func() - ++ * ++ * This is a default pcd_init_func. It will be used ++ * if no other is provided. ++ * ++ * @param otg pointer to the otg instance. ++ * @param flag set or reset ++ */ ++void otg_pcd_init_func (struct otg_instance *otg, u8 flag) ++{ ++ otg_event(otg, PCD_OK, CORE, "PCD_OK"); ++} ++ ++/*! ++ * otg_hcd_init_func() - ++ * ++ * This is a default hcd_init_func. It will be used ++ * if no other is provided. ++ * ++ * @param otg pointer to the otg instance. ++ * @param flag set or reset ++ */ ++void otg_hcd_init_func (struct otg_instance *otg, u8 flag) ++{ ++ otg_event(otg, HCD_OK, CORE, "HCD_OK"); ++} ++ ++/*! ++ * otg_ocd_init_func() - ++ * ++ * This is a default hcd_init_func. It will be used ++ * if no other is provided. ++ * ++ * @param otg pointer to the otg instance. ++ * @param flag set or reset ++ */ ++void otg_ocd_init_func (struct otg_instance *otg, u8 flag) ++{ ++ otg_event(otg, HCD_OK, CORE, "OCD_OK"); ++} ++ ++/*! ++ * otg_hcd_set_ops() - ++ * @param hcd_ops - host operations table to use ++ */ ++struct hcd_instance * otg_set_hcd_ops(struct hcd_ops *hcd_ops) ++{ ++ otg_hcd_ops = hcd_ops; ++ if (hcd_ops) { ++ otg_instance_private.otg_output_ops[HCD_INIT_OUT] = otg_hcd_ops->hcd_init_func; ++ otg_instance_private.otg_output_ops[HCD_EN_OUT] = otg_hcd_ops->hcd_en_func; ++ otg_instance_private.otg_output_ops[LOC_SOF_OUT] = otg_hcd_ops->loc_sof_func; ++ otg_instance_private.otg_output_ops[LOC_SUSPEND_OUT] = otg_hcd_ops->loc_suspend_func; ++ otg_instance_private.otg_output_ops[REMOTE_WAKEUP_EN_OUT] = otg_hcd_ops->remote_wakeup_en_func; ++ otg_instance_private.otg_output_ops[HNP_EN_OUT] = otg_hcd_ops->hnp_en_func; ++ } ++ else ++ otg_instance_private.otg_output_ops[HCD_INIT_OUT] = ++ otg_instance_private.otg_output_ops[HCD_EN_OUT] = ++ otg_instance_private.otg_output_ops[LOC_SOF_OUT] = ++ otg_instance_private.otg_output_ops[LOC_SUSPEND_OUT] = ++ otg_instance_private.otg_output_ops[REMOTE_WAKEUP_EN_OUT] = ++ otg_instance_private.otg_output_ops[HNP_EN_OUT] = NULL; ++ ++ ++ UNLESS (otg_instance_private.otg_output_ops[HCD_INIT_OUT]) ++ otg_instance_private.otg_output_ops[HCD_INIT_OUT] = otg_hcd_init_func; ++ ++ return &hcd_instance_private; ++} ++ ++/*! ++ * otg_ocd_set_ops() - ++ * ++ * @param ocd_ops - ocd operations table to use ++ */ ++struct ocd_instance * otg_set_ocd_ops(struct ocd_ops *ocd_ops) ++{ ++ otg_ocd_ops = ocd_ops; ++ if (ocd_ops) { ++ otg_instance_private.otg_output_ops[OCD_INIT_OUT] = otg_ocd_ops->ocd_init_func; ++ otg_instance_private.start_timer = ocd_ops->start_timer; ++ } ++ else { ++ otg_instance_private.otg_output_ops[OCD_INIT_OUT] = NULL; ++ otg_instance_private.start_timer = NULL; ++ } ++ ++ UNLESS (otg_instance_private.otg_output_ops[OCD_INIT_OUT]) ++ otg_instance_private.otg_output_ops[OCD_INIT_OUT] = otg_ocd_init_func; ++ ++ return &ocd_instance_private; ++} ++ ++/*! ++ * otg_pcd_set_ops() - ++ * @param pcd_ops - pcd operations table to use ++ */ ++struct pcd_instance * otg_set_pcd_ops(struct pcd_ops *pcd_ops) ++{ ++ otg_pcd_ops = pcd_ops; ++ if (pcd_ops) { ++ otg_instance_private.otg_output_ops[PCD_INIT_OUT] = otg_pcd_ops->pcd_init_func; ++ otg_instance_private.otg_output_ops[PCD_EN_OUT] = otg_pcd_ops->pcd_en_func; ++ otg_instance_private.otg_output_ops[REMOTE_WAKEUP_OUT] = otg_pcd_ops->remote_wakeup_func; ++ } ++ else ++ otg_instance_private.otg_output_ops[PCD_INIT_OUT] = ++ otg_instance_private.otg_output_ops[PCD_EN_OUT] = ++ otg_instance_private.otg_output_ops[REMOTE_WAKEUP_OUT] = NULL; ++ ++ UNLESS (otg_instance_private.otg_output_ops[PCD_INIT_OUT]) ++ otg_instance_private.otg_output_ops[PCD_INIT_OUT] = otg_pcd_init_func; ++ ++ return &pcd_instance_private; ++} ++ ++/*! ++ * otg_tcd_set_ops() - ++ * @param tcd_ops - tcd operations table to use ++ */ ++struct tcd_instance * otg_set_tcd_ops(struct tcd_ops *tcd_ops) ++{ ++ otg_tcd_ops = tcd_ops; ++ if (tcd_ops) { ++ otg_instance_private.otg_output_ops[TCD_INIT_OUT] = otg_tcd_ops->tcd_init_func; ++ otg_instance_private.otg_output_ops[TCD_EN_OUT] = otg_tcd_ops->tcd_en_func; ++ otg_instance_private.otg_output_ops[CHRG_VBUS_OUT] = otg_tcd_ops->chrg_vbus_func; ++ otg_instance_private.otg_output_ops[DRV_VBUS_OUT] = otg_tcd_ops->drv_vbus_func; ++ otg_instance_private.otg_output_ops[DISCHRG_VBUS_OUT] = otg_tcd_ops->dischrg_vbus_func; ++ otg_instance_private.otg_output_ops[DP_PULLUP_OUT] = otg_tcd_ops->dp_pullup_func; ++ otg_instance_private.otg_output_ops[DM_PULLUP_OUT] = otg_tcd_ops->dm_pullup_func; ++ otg_instance_private.otg_output_ops[DP_PULLDOWN_OUT] = otg_tcd_ops->dp_pulldown_func; ++ otg_instance_private.otg_output_ops[DM_PULLDOWN_OUT] = otg_tcd_ops->dm_pulldown_func; ++ //otg_instance_private.otg_output_ops[PERIPHERAL_HOST] = otg_tcd_ops->peripheral_host_func; ++ otg_instance_private.otg_output_ops[CLR_OVERCURRENT_OUT] = otg_tcd_ops->overcurrent_func; ++ otg_instance_private.otg_output_ops[DM_DET_OUT] = otg_tcd_ops->dm_det_func; ++ otg_instance_private.otg_output_ops[DP_DET_OUT] = otg_tcd_ops->dp_det_func; ++ otg_instance_private.otg_output_ops[CR_DET_OUT] = otg_tcd_ops->cr_det_func; ++ otg_instance_private.otg_output_ops[AUDIO_OUT] = otg_tcd_ops->audio_func; ++ otg_instance_private.otg_output_ops[CHARGE_PUMP_OUT] = otg_tcd_ops->charge_pump_func; ++ otg_instance_private.otg_output_ops[BDIS_ACON_OUT] = otg_tcd_ops->bdis_acon_func; ++ otg_instance_private.otg_output_ops[MX21_VBUS_DRAIN] = otg_tcd_ops->mx21_vbus_drain_func; ++ otg_instance_private.otg_output_ops[ID_PULLDOWN_OUT] = otg_tcd_ops->id_pulldown_func; ++ otg_instance_private.otg_output_ops[UART_OUT] = otg_tcd_ops->uart_func; ++ otg_instance_private.otg_output_ops[MONO_OUT] = otg_tcd_ops->mono_func; ++ } ++ else ++ otg_instance_private.otg_output_ops[TCD_INIT_OUT] = ++ otg_instance_private.otg_output_ops[TCD_EN_OUT] = ++ otg_instance_private.otg_output_ops[CHRG_VBUS_OUT] = ++ otg_instance_private.otg_output_ops[DRV_VBUS_OUT] = ++ otg_instance_private.otg_output_ops[DISCHRG_VBUS_OUT] = ++ otg_instance_private.otg_output_ops[DP_PULLUP_OUT] = ++ otg_instance_private.otg_output_ops[DM_PULLUP_OUT] = ++ otg_instance_private.otg_output_ops[DP_PULLDOWN_OUT] = ++ otg_instance_private.otg_output_ops[DM_PULLDOWN_OUT] = ++ //otg_instance_private.otg_output_ops[PERIPHERAL_HOST] = ++ otg_instance_private.otg_output_ops[CLR_OVERCURRENT_OUT] = ++ otg_instance_private.otg_output_ops[DM_DET_OUT] = ++ otg_instance_private.otg_output_ops[DP_DET_OUT] = ++ otg_instance_private.otg_output_ops[CR_DET_OUT] = ++ otg_instance_private.otg_output_ops[AUDIO_OUT] = ++ otg_instance_private.otg_output_ops[CHARGE_PUMP_OUT] = ++ otg_instance_private.otg_output_ops[BDIS_ACON_OUT] = ++ otg_instance_private.otg_output_ops[MX21_VBUS_DRAIN] = ++ otg_instance_private.otg_output_ops[ID_PULLDOWN_OUT] = ++ otg_instance_private.otg_output_ops[UART_OUT] = ++ otg_instance_private.otg_output_ops[MONO_OUT] = NULL; ++ ++ UNLESS (otg_instance_private.otg_output_ops[TCD_INIT_OUT]) ++ otg_instance_private.otg_output_ops[TCD_INIT_OUT] = otg_tcd_init_func; ++ ++ return &tcd_instance_private; ++} ++ ++/*! ++ * otg_set_usbd_ops() - ++ * @param usbd_ops ++ */ ++int otg_set_usbd_ops(struct usbd_ops *usbd_ops) ++{ ++ otg_usbd_ops = usbd_ops; ++ return 0; ++} ++ ++ ++/*! ++ * otg_get_trace_info() ++ */ ++void otg_get_trace_info(otg_trace_t *p) ++{ ++ p->id_gnd = otg_instance_private.current_inputs & ID_GND ? 1 : 0; ++ p->va.s.ticks = (otg_ocd_ops && otg_ocd_ops->ticks) ? otg_ocd_ops->ticks () : 0; ++ p->va.s.interrupts = (otg_ocd_ops && otg_ocd_ops->interrupts) ? otg_ocd_ops->interrupts : 0; ++ p->va.s.framenum = (otg_pcd_ops && otg_pcd_ops->framenum) ? otg_pcd_ops->framenum() : 0; ++ p->in_interrupt = in_interrupt(); ++} ++ ++/*! ++ * otg_tmr_ticks() - ++ * @return number of ticks. ++ */ ++u64 otg_tmr_ticks(void) ++{ ++ return (otg_ocd_ops && otg_ocd_ops->ticks) ? otg_ocd_ops->ticks () : 0; ++} ++ ++/*! ++ * otg_tmr_elapsed() - ++ * @param t1 ++ * @param t2 ++ * @return number of uSecs between t1 and t2 ticks. ++ */ ++u64 otg_tmr_elapsed(u64 *t1, u64 *t2) ++{ ++ return (otg_ocd_ops && otg_ocd_ops->elapsed) ? otg_ocd_ops->elapsed (t1, t2) : 0; ++} ++ ++/*! ++ * otg_tmr_framenum() - ++ * @return the current USB frame number. ++ */ ++u16 otg_tmr_framenum(void) ++{ ++ return (otg_pcd_ops && otg_pcd_ops->framenum) ? otg_pcd_ops->framenum() : 0; ++} ++ ++/*! ++ * otg_tmr_interrupts() - ++ * @return the current number of interrupts. ++ */ ++u32 otg_tmr_interrupts(void) ++{ ++ return (otg_ocd_ops && otg_ocd_ops->interrupts) ? otg_ocd_ops->interrupts : 0; ++} ++ ++ ++OTG_EXPORT_SYMBOL(otg_hcd_ops); ++OTG_EXPORT_SYMBOL(otg_ocd_ops); ++OTG_EXPORT_SYMBOL(otg_pcd_ops); ++OTG_EXPORT_SYMBOL(otg_tcd_ops); ++OTG_EXPORT_SYMBOL(otg_set_hcd_ops); ++OTG_EXPORT_SYMBOL(otg_set_ocd_ops); ++OTG_EXPORT_SYMBOL(otg_set_pcd_ops); ++OTG_EXPORT_SYMBOL(otg_set_tcd_ops); ++OTG_EXPORT_SYMBOL(otg_set_usbd_ops); ++OTG_EXPORT_SYMBOL(otg_tmr_ticks); ++OTG_EXPORT_SYMBOL(otg_tmr_elapsed); ++OTG_EXPORT_SYMBOL(otg_write_input_message_irq); ++ ++ ++#if defined(OTG_WINCE) ++#else /* defined(OTG_WINCE) */ ++#endif /* defined(OTG_WINCE) */ ++ +diff -uNr linux/drivers/no-otg/otgcore/sources linux/drivers/otg/otgcore/sources +--- linux/drivers/no-otg/otgcore/sources 1970-01-01 01:00:00.000000000 +0100 ++++ linux/drivers/otg/otgcore/sources 2006-09-01 21:41:34.000000000 +0200 +@@ -0,0 +1,41 @@ ++!if 0 ++Copyright (c) Microsoft Corporation. All rights reserved. ++!endif ++!if 0 ++Use of this source code is subject to the terms of the Microsoft end-user ++license agreement (EULA) under which you licensed this SOFTWARE PRODUCT. ++If you did not accept the terms of the EULA, you are not authorized to use ++this source code. For a copy of the EULA, please see the LICENSE.RTF on your ++install media. ++!endif ++ ++SYNCHRONIZE_DRAIN=1 ++ ++INCLUDES= \ ++ $(_PUBLICROOT)\common\ddk\inc; $(_PUBLICROOT)\common\oak\inc; $(_PUBLICROOT)\common\sdk\inc; \ ++ $(_WINCEROOT)\public\common\oak\DRIVERS\otg; ++ ++ ++TARGETNAME=otgcore ++TARGETTYPE=LIBRARY ++ ++ ++SOURCES= \ ++ core-init-w42.c \ ++ otg.c \ ++ otg-mesg.c \ ++ otg-mesg-w42.c \ ++ otg-ops.c \ ++ usbp-bops.c \ ++ usbp-fops.c \ ++ otg-fw.c \ ++ otg-fw-df.c \ ++ otg-fw-mn.c \ ++ usbp.c \ ++ otg-trace.c \ ++ otg-trace-w42.c \ ++ ep0.c \ ++ ++# usbp-procfs.c \ ++ ++ +diff -uNr linux/drivers/no-otg/otgcore/usbp-bops.c linux/drivers/otg/otgcore/usbp-bops.c +--- linux/drivers/no-otg/otgcore/usbp-bops.c 1970-01-01 01:00:00.000000000 +0100 ++++ linux/drivers/otg/otgcore/usbp-bops.c 2006-09-01 21:41:35.000000000 +0200 +@@ -0,0 +1,1857 @@ ++/* ++ * otg/otgcore/usbp-bops.c - USB Device Prototype ++ * ++ * Copyright (c) 2004-2005 Belcarra ++ * ++ * By: ++ * Stuart Lynne <sl@belcarra.com>, ++ * Bruce Balden <balden@belcarra.com> ++ * ++ */ ++/*! ++ * @file otg/otgcore/usbp-bops.c ++ * @brief Bus Interface related functions. ++ * ++ * This implements the functions used to implement Peripheral Controller Drivers (PCD.) ++ * ++ * @ingroup USBP ++ */ ++ ++#include <otg/otg-compat.h> ++#include <otg/otg-module.h> ++ ++#include <otg/usbp-chap9.h> ++#include <otg/otg-trace.h> ++#include <otg/otg-api.h> ++#include <otg/otg-trace.h> ++#include <otg/usbp-func.h> ++#include <otg/usbp-bus.h> ++#include <otg/otg-pcd.h> ++ ++/* ********************************************************************************************* */ ++/*! ++ * Note that all of the list functions overlapped operation using the usbd_bus_sem; ++ * ++ * usbd_device_bh ++ * ++ * usbd_register_bus ++ * usbd_deregister_bus ++ * usbd_enable_function_irq ++ * usbd_disable_function ++ */ ++DECLARE_MUTEX(usbd_bus_sem); ++void usbd_device_bh (void *data); ++ ++/* ********************************************************************************************* */ ++/*! ++ * Function driver enable/disable ++ * ++ * Called by usbd_enable_function/usbd_disable_function to call the selected ++ * function drivers function_enable or function_disable function. ++ */ ++int usbd_strings_init (void); ++void usbd_strings_exit(void); ++ ++extern struct usbd_function_instance *usbd_find_function (char *name); ++ ++struct usbd_bus_instance *usbd_bus_instance; ++OTG_EXPORT_SYMBOL(usbd_bus_instance); ++ ++/* ********************************************************************************************* */ ++/* ********************************************************************************************* */ ++/*! ++ * Bus Interface Management: ++ * usbd_register_bus() ++ * usbd_deregister_bus() ++ */ ++ ++/*! urb_link_init() - initialize urb linke ++ * ++ * 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. ++ * ++ * @param ul link to urb ++ */ ++static INLINE void urb_link_init (urb_link * ul) ++{ ++ ul->prev = ul->next = ul; ++} ++ ++static struct usbd_function_operations ep0_ops; ++struct usbd_function_driver ep0_driver; ++ ++ ++/*! ++ * usbd_register_bus() - called by a USB BUS INTERFACE driver to register a bus driver ++ * ++ * Used by a USB Bus interface driver to register itself with the usb device layer. ++ * ++ * @param driver ++ * @param ep0_wMaxPacketSize ++ * @return non-zero if error ++ */ ++struct usbd_bus_instance *usbd_register_bus (struct usbd_bus_driver *driver, int ep0_wMaxPacketSize) ++{ ++ int i; ++ struct usbd_bus_instance *bus = NULL; ++ ++ DOWN(&usbd_bus_sem); ++ ++ THROW_IF(usbd_bus_instance, error); ++ THROW_IF((bus = CKMALLOC (sizeof (struct usbd_bus_instance), GFP_ATOMIC)) == NULL, error); ++ ++ bus->driver = driver; ++ bus->endpoints = bus->driver->max_endpoints; ++ bus->otg_bmAttributes = bus->driver->otg_bmAttributes; ++ ++ THROW_IF(!(bus->endpoint_array = CKMALLOC(sizeof (struct usbd_endpoint_instance) * bus->endpoints, GFP_ATOMIC)), error); ++ ++ for (i = 0; i < bus->endpoints; i++) { ++ struct usbd_endpoint_instance *endpoint = bus->endpoint_array + i; ++ endpoint->physical_endpoint = i; ++ urb_link_init (&endpoint->rdy); ++ urb_link_init (&endpoint->tx); ++ } ++ ++ urb_link_init (&bus->finished); ++ ++ ZERO(ep0_ops); ++ ZERO(ep0_driver); ++ ep0_driver.name = "EP0"; ++ ep0_driver.fops = &ep0_ops; ++ ++ ++ THROW_IF((bus->ep0 = CKMALLOC (sizeof (struct usbd_function_instance), GFP_ATOMIC)) == NULL, error); ++ bus->ep0->function_driver = &ep0_driver; ++ THROW_IF(!(bus->ep0->endpoint_map_array = CKMALLOC(sizeof(struct usbd_endpoint_map) * 2, GFP_KERNEL)), error); ++ bus->ep0->endpoint_map_array[0].wMaxPacketSize[0] = ep0_wMaxPacketSize; ++ bus->ep0->endpoint_map_array[0].wMaxPacketSize[1] = ep0_wMaxPacketSize; ++ ++ bus->device_state = STATE_CREATED; ++ bus->status = USBD_OPENING; ++ ++ usbd_bus_instance = bus; ++ ++ CATCH(error) { ++ if (bus) { ++ if (bus->endpoint_array) ++ LKFREE(bus->endpoint_array); ++ LKFREE(bus); ++ } ++ bus->endpoints = 0; ++ #if defined(OTG_LINUX) ++ printk(KERN_INFO"%s: FAILED\n", __FUNCTION__); ++ #endif /* defined(OTG_LINUX) */ ++ #if defined(OTG_WINCE) ++ DEBUGMSG(ZONE_INIT, (_T("usbd_register_bus: FAILED\n"))); ++ #endif /* defined(OTG_LINUX) */ ++ bus = NULL; ++ } ++ ++ UP(&usbd_bus_sem); ++ return bus; ++} ++ ++/*! ++ * usbd_deregister_bus() - called by a USB BUS INTERFACE driver to deregister a bus driver ++ * ++ * Used by a USB Bus interface driver to de-register itself with the usb device ++ * layer. ++ * ++ * @param bus ++ */ ++void usbd_deregister_bus (struct usbd_bus_instance *bus) ++{ ++ DOWN(&usbd_bus_sem); ++ ++ usbd_bus_instance = NULL; ++ ++ if (bus->ep0) { ++ if (bus->ep0->endpoint_map_array) ++ LKFREE(bus->ep0->endpoint_map_array); ++ LKFREE(bus->ep0); ++ } ++ ++ LKFREE (bus->arg); ++ LKFREE (bus->endpoint_array); ++ LKFREE (bus); ++ bus->endpoints = 0; ++ UP(&usbd_bus_sem); ++} ++ ++OTG_EXPORT_SYMBOL(usbd_register_bus); ++OTG_EXPORT_SYMBOL(usbd_deregister_bus); ++ ++/* ********************************************************************************************* */ ++/* ********************************************************************************************* */ ++/*! ++ * Function Management: ++ * usbd_enable_function() ++ * usbd_disable_function() ++ * ++ */ ++ ++/*! ++ * alloc_function_alternates() - 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. ++ * @param bInterfaceNumber ++ * @param interface_description ++ * @param alternates ++ * @param alternate_description_array ++ * @return pointer to alternate instance array ++ */ ++static struct usbd_alternate_instance * ++alloc_function_alternates (int bInterfaceNumber, ++ struct usbd_interface_description *interface_description, ++ int alternates, struct usbd_alternate_description *alternate_description_array) ++{ ++ int alternate_num; ++ struct usbd_alternate_instance *alternate_instance_array = NULL; ++ ++ // allocate array of alternate instances ++ RETURN_NULL_IF(!(alternate_instance_array = (struct usbd_alternate_instance *) ++ CKMALLOC (sizeof (struct usbd_alternate_instance) * alternates, GFP_KERNEL))); ++ ++ // iterate across the alternate descriptions ++ for (alternate_num = 0; alternate_num < alternates; alternate_num++) { ++ ++ struct usbd_alternate_description *alternate_description = alternate_description_array + alternate_num; ++ struct usbd_alternate_instance *alternate_instance = alternate_instance_array + alternate_num; ++ struct usbd_interface_descriptor *interface_descriptor; ++ ++ interface_descriptor = alternate_description->interface_descriptor; ++ if (interface_descriptor->bInterfaceNumber != bInterfaceNumber) { ++ //TRACE_MSG2(USBD, "bInterfaceNumber mis-match: %d should be %d", ++ // interface_descriptor->bInterfaceNumber, bInterfaceNumber); ++ LKFREE(alternate_instance_array); ++ return NULL; ++ } ++ ++ interface_descriptor->bNumEndpoints = alternate_description->endpoints; ++ interface_descriptor->iInterface = usbd_alloc_string (alternate_description->iInterface ); ++ ++ alternate_instance->class_list = alternate_description->class_list; ++ alternate_instance->classes = alternate_description->classes; ++ alternate_instance->endpoint_list = alternate_description->endpoint_list; ++ alternate_instance->endpoint_indexes = alternate_description->endpoint_indexes; ++ ++ // 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 ++ * ++ * 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. ++ * ++ * @param bNumInterfaces ++ * @param interface_description_array ++ * @return interface instance ++ */ ++static struct usbd_interfaces_instance * ++alloc_function_interfaces (int bNumInterfaces, struct usbd_interface_description *interface_description_array) ++{ ++ int interface_num; ++ struct usbd_interfaces_instance *interfaces_instance_array = NULL; ++ ++ // allocate array of interface instances ++ RETURN_NULL_IF(!(interfaces_instance_array = (struct usbd_interfaces_instance *) ++ CKMALLOC (sizeof (struct usbd_interfaces_instance) * bNumInterfaces, GFP_KERNEL))); ++ ++ // iterate across the interface descriptions ++ for (interface_num = 0; interface_num < bNumInterfaces; interface_num++) { ++ ++ struct usbd_interface_description *interface_description = interface_description_array + interface_num; ++ struct usbd_interfaces_instance *interfaces_instance = interfaces_instance_array + interface_num; ++ ++ interfaces_instance->alternates_instance_array = ++ alloc_function_alternates (interface_num, interface_description, ++ interface_description->alternates, interface_description->alternate_list); ++ interfaces_instance->alternates = interface_description->alternates; ++ } ++ return interfaces_instance_array; ++} ++ ++ ++/*! ++ * alloc_function_configurations() - allocate configuration instance array ++ * ++ * 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. ++ * ++ * @param bNumConfigurations ++ * @param configuration_description_array ++ * @param otg_descriptor ++ * @param remote_wakeup_supported ++ * @param bMaxPower ++ * @return configuration instance ++ */ ++static struct usbd_configuration_instance * ++alloc_function_configurations (struct usbd_function_driver *function_driver, ++ int remote_wakeup_supported, ++ int bMaxPower) ++{ ++ ++ int bNumConfigurations = function_driver->bNumConfigurations; ++ struct usbd_configuration_description *configuration_description_array = function_driver->configuration_description; ++ struct usbd_otg_descriptor *otg_descriptor = function_driver->otg_descriptor; ++ int config_num; ++ struct usbd_configuration_instance *configuration_instance_array = NULL; ++ ++ //TRACE_MSG2(USBD, "remote: %x bMaxPower: %x", remote_wakeup_supported, bMaxPower); ++ ++ // allocate array ++ RETURN_NULL_IF(!(configuration_instance_array = (struct usbd_configuration_instance *) ++ CKMALLOC (sizeof (struct usbd_configuration_instance) * bNumConfigurations, GFP_KERNEL))); ++ // fill in array ++ for (config_num = 0; config_num < bNumConfigurations; config_num++) { ++ int interface_num; ++ int length; ++ ++ struct usbd_configuration_description *configuration_description = configuration_description_array + config_num; ++ struct usbd_configuration_descriptor *configuration_descriptor; ++ ++ configuration_descriptor = configuration_description->configuration_descriptor; ++ ++ // 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 = config_num + 1; ++ configuration_descriptor->wTotalLength = 0; ++ configuration_descriptor->bNumInterfaces = function_driver->bNumInterfaces; ++ configuration_descriptor->iConfiguration = usbd_alloc_string (configuration_description->iConfiguration); ++ configuration_descriptor->bMaxPower = bMaxPower; ++ configuration_descriptor->bmAttributes = BMATTRIBUTE_RESERVED | ++ bMaxPower ? 0 : BMATTRIBUTE_SELF_POWERED | ++ remote_wakeup_supported ? BMATTRIBUTE_REMOTE_WAKEUP : 0; ++ ++ // save the configuration descriptor in the configuration instance array ++ configuration_instance_array[config_num].configuration_descriptor = configuration_descriptor; ++ ++ RETURN_NULL_IF (!(configuration_instance_array[config_num].interfaces_instance_array = ++ alloc_function_interfaces (function_driver->bNumInterfaces, ++ function_driver->interface_list))); ++ ++ length = sizeof(struct usbd_configuration_descriptor); ++ ++ for (interface_num = 0; interface_num < configuration_descriptor->bNumInterfaces; interface_num++) { ++ ++ int alternate_num; ++ struct usbd_interfaces_instance *interfaces_instance = ++ configuration_instance_array[config_num].interfaces_instance_array + interface_num; ++ ++ TRACE_MSG2(USBD, "len: %02x:%02d configuration", length, length); ++ for (alternate_num = 0; alternate_num < interfaces_instance->alternates; alternate_num++) { ++ int class_num; ++ int endpoint; ++ struct usbd_alternate_instance *alternate_instance = ++ interfaces_instance->alternates_instance_array + alternate_num; ++ ++ length += sizeof (struct usbd_interface_descriptor); ++ ++ TRACE_MSG2(USBD, "len: %02x:%02d interface", length, length); ++ for (class_num = 0; class_num < alternate_instance->classes; class_num++) { ++ struct usbd_generic_class_descriptor * class_descriptor = ++ *(alternate_instance->class_list + class_num); ++ length += class_descriptor->bLength; ++ TRACE_MSG2(USBD, "len: %02x:%02d class_num", length, length); ++ } ++ ++ for (endpoint = 0; endpoint < alternate_instance->endpoints; endpoint++) { ++ struct usbd_endpoint_descriptor * endpoint_descriptor = ++ *(alternate_instance->endpoint_list + endpoint); ++ length += endpoint_descriptor->bLength; ++ TRACE_MSG2(USBD, "len: %02x:%02d endpoint", length, length); ++ } ++ } ++ } ++ length += otg_descriptor ? sizeof(struct usbd_otg_descriptor) : 0; ++ configuration_descriptor->wTotalLength = cpu_to_le16 ((u16)length); ++ // XXX only support a single configuration currently ++ configuration_instance_array[config_num].bNumInterfaces = function_driver->bNumInterfaces; ++ TRACE_MSG3(USBD, "configuration_instance_array[%d]: %p wTotalLength: %d", config_num, ++ &configuration_instance_array[config_num], length); ++ } ++ return configuration_instance_array; ++} ++ ++ ++/*! ++ * bus_function_enable() - enable a usbd function driver for use ++ * ++ * This will copy descriptors from the function driver and get them ready ++ * for use. ++ * @param bus ++ * @param function ++ * @param serial_number ++ * @return non-zero if error ++ */ ++static int ++bus_function_enable (struct usbd_bus_instance *bus, struct usbd_function_instance *function, char *serial_number) ++{ ++ struct usbd_function_driver *function_driver = function->function_driver; ++ struct usbd_device_description *device_description; ++ struct usbd_device_descriptor *device_descriptor; ++#ifdef CONFIG_OTG_HIGH_SPEED ++ struct usbd_device_qualifier_descriptor *device_qualifier_descriptor; ++#endif /* CONFIG_OTG_HIGH_SPEED */ ++ struct usbd_otg_descriptor *otg_descriptor; ++ int rc = 0; ++ ++ function->bus = bus; ++ ++ RETURN_EINVAL_IF(function_driver->fops->function_enable && function_driver->fops->function_enable (function)); ++ RETURN_ZERO_IF(!(device_description = function_driver->device_description)); ++ RETURN_ZERO_IF(!(device_descriptor = device_description->device_descriptor)); ++ ++ device_descriptor->bMaxPacketSize0 = bus->driver->maxpacketsize; ++ device_descriptor->iManufacturer = usbd_alloc_string (device_description->iManufacturer); ++ device_descriptor->iProduct = usbd_alloc_string (device_description->iProduct); ++ ++ if (strlen (serial_number)) ++ device_descriptor->iSerialNumber = usbd_alloc_string (serial_number); ++ else ++ device_descriptor->iSerialNumber = usbd_alloc_string (device_description->iSerialNumber); ++ ++ device_descriptor->bNumConfigurations = function_driver->bNumConfigurations; ++ device_descriptor->idVendor = function_driver->idVendor; ++ device_descriptor->idProduct = function_driver->idProduct; ++ device_descriptor->bcdDevice = function_driver->bcdDevice; ++ ++ if ((otg_descriptor = device_description->otg_descriptor)) { ++ otg_descriptor->bmAttributes = usbd_otg_bmattributes(function); ++ function_driver->otg_descriptor = otg_descriptor; ++ } ++ ++ /* call alloc_function_configurations() to allocate the configuration descriptor array ++ */ ++ RETURN_EINVAL_IF (!(function_driver->configuration_instance_array = ++ alloc_function_configurations (function_driver, ++ bus->driver->capabilities & REMOTE_WAKEUP_SUPPORTED, ++ bus->driver->bMaxPower ++ ))); ++ ++ function_driver->device_descriptor = device_descriptor; ++#ifdef CONFIG_OTG_HIGH_SPEED ++ TRACE_MSG0(USBD, "HIGH SPEED"); ++ if ((device_qualifier_descriptor = device_description->device_qualifier_descriptor)) { ++ device_qualifier_descriptor->bNumConfigurations = function_driver->bNumConfigurations; ++ function_driver->device_qualifier_descriptor = device_qualifier_descriptor; ++ } ++#endif /* CONFIG_OTG_HIGH_SPEED */ ++ //TRACE_MSG0(USBD, "finished"); ++ return rc; ++} ++ ++/*! ++ * usbd_function_disable() - disable a usbd function driver ++ * @param function ++ */ ++void usbd_function_disable (struct usbd_function_instance *function) ++{ ++ int configuration; ++ struct usbd_function_driver *function_driver = function->function_driver; ++ struct usbd_configuration_instance *configuration_instance_array = function_driver->configuration_instance_array; ++ ++ if (function->function_driver->fops->function_disable) ++ function->function_driver->fops->function_disable (function); ++ ++ RETURN_IF(!function_driver->configuration_instance_array); ++ ++ // iterate across the descriptors list and de-allocate all structures ++ if (function_driver->configuration_instance_array) { ++ for (configuration = 0; configuration < function_driver->bNumConfigurations; configuration++) { ++ int interface_num; ++ struct usbd_configuration_instance *configuration_instance = configuration_instance_array + configuration; ++ ++ for (interface_num = 0; interface_num < configuration_instance->bNumInterfaces; interface_num++) { ++ ++ //int alternate_num; ++ struct usbd_interfaces_instance *interfaces_instance = ++ configuration_instance->interfaces_instance_array + interface_num; ++ ++ LKFREE (interfaces_instance->alternates_instance_array); ++ } ++ LKFREE (configuration_instance->interfaces_instance_array); ++ } ++ } ++ LKFREE (configuration_instance_array); ++ function->bus = NULL; ++} ++ ++ ++/*! ++ * usbd_enable_function() - called to enable the desired function ++ * ++ * Used by a USB Bus interface driver to create a virtual device. ++ * ++ * @param bus ++ * @param arg ++ * @param serial_number ++ * @return non-zero if error ++ */ ++int usbd_enable_function(struct usbd_bus_instance *bus, char *arg, char *serial_number) ++{ ++ struct usbd_function_instance *function = NULL; ++ struct usbd_function_driver *function_driver = NULL; ++ LIST_NODE *lhd = NULL; ++ int len = 0; ++ int i; ++ int rc = -EINVAL; ++ int epn, endpointsRequested; ++ int endpoints; ++ ++ DOWN(&usbd_bus_sem); ++ ++ if (arg && bus->arg) ++ LKFREE(bus->arg); ++ ++ if (arg) { ++ bus->arg = LSTRDUP(arg); ++ len = strlen(arg); ++ } ++ ++ /* initialize the strings pool, and zero endpoint array urbs ++ */ ++ THROW_IF(usbd_strings_init (), error); ++ ++ for (i = 1; i < bus->endpoints; i++) { ++ struct usbd_endpoint_instance *endpoint = bus->endpoint_array + i; ++ endpoint->rcv_urb = endpoint->tx_urb = NULL; ++ } ++ ++ /* find requested function ++ */ ++ if ((function = usbd_find_function(arg))) { ++ ++ function_driver = function->function_driver; ++ ++ TRACE_MSG1(USBD, "request endpoints: %x", function_driver); ++ ++ switch(function->function_type) { ++ case function_simple: ++ THROW_IF(bus->driver->bops->request_endpoints( bus, function->endpoint_map_array, ++ function->function_driver->endpointsRequested, ++ function->function_driver->requestedEndpoints), error); ++ break; ++ case function_interface: ++ break; ++ case function_composite: ++ THROW_IF(bus->driver->bops->request_endpoints( bus, function->endpoint_map_array, ++ function->endpoints, ++ function->requestedEndpoints), error); ++ break; ++ } ++ ++ ++ bus_function_enable (bus, function, serial_number); ++ } ++ ++ THROW_IF (!(bus->function_instance = function), error); ++ ++ ++ /* if composite we need to compose the interface list and endpoint map ++ */ ++ if(function_composite == function->function_type) { ++ char *name; ++ int i; ++ } ++ ++ /* we need to de-compose the endpoint request array into the individual ++ * interface endpoint map array. ++ */ ++ for (endpoints = 0, i = 0; i < function->interfaces; i++) { ++ struct usbd_function_instance *interface_function = function->interfaces_array[i]; ++ interface_function->endpoint_map_array = function->endpoint_map_array; ++ endpoints += interface_function->endpoints; ++ } ++ ++ ++ switch(bus->function_instance->function_type) { ++ case function_simple: ++ case function_composite: ++ THROW_IF(bus->driver->bops->set_endpoints( bus, ++ function->function_driver->endpointsRequested, ++ function->endpoint_map_array), error); ++ break; ++ case function_interface: ++ break; ++ } ++ ++ // device bottom half ++ PREPARE_WORK_ITEM(bus->device_bh, usbd_device_bh, bus); ++ bus->status = USBD_OK; ++ bus->bus_state = usbd_bus_state_enabled; ++ bus->ep0->endpoint_map_array->endpoint = bus->endpoint_array; ++ THROW_IF (bus_function_enable (bus, bus->ep0, NULL), error); ++ ++ // iterate across the logical endpoint map to copy appropriate information ++ // into the physical endpoint instance array ++ ++ endpointsRequested = bus->function_instance->function_driver->endpointsRequested; ++ ++ for (epn = 0; epn < /*bus->function_instance->*/endpointsRequested; epn++) { ++ ++ struct usbd_endpoint_map *endpoint_map = bus->function_instance->endpoint_map_array + epn; ++ int physicalEndpoint = endpoint_map->physicalEndpoint[0]; ++ struct usbd_endpoint_instance *endpoint = bus->endpoint_array + physicalEndpoint; ++ ++ endpoint_map->endpoint = endpoint; ++ endpoint->bEndpointAddress = endpoint_map->bEndpointAddress[0]; ++ endpoint->bmAttributes = endpoint_map->bmAttributes[0]; ++ ++ switch(endpoint->bEndpointAddress & USB_ENDPOINT_DIR_MASK) { ++ case USB_DIR_IN: ++ endpoint->tx_transferSize = endpoint_map->transferSize[0]; ++ endpoint->wMaxPacketSize = endpoint_map->wMaxPacketSize[0]; ++ endpoint->last = 0; ++ endpoint->tx_urb = NULL; ++ break; ++ ++ case USB_DIR_OUT: ++ endpoint->rcv_transferSize = endpoint_map->transferSize[0]; ++ endpoint->wMaxPacketSize = endpoint_map->wMaxPacketSize[0]; ++ endpoint->rcv_urb = NULL; ++ break; ++ } ++ } ++ ++ /* if composite we need to de-compose the endpoint map into the individual ++ * interface endpoint maps. ++ */ ++ if(function_composite == function->function_type) { ++ } ++ ++ rc = 0; ++ ++ CATCH(error) { ++ usbd_strings_exit(); ++ } ++ UP(&usbd_bus_sem); ++ return rc; ++} ++ ++int usbd_enable_function_irq (struct usbd_bus_instance *bus, char *arg, char *serial_number) ++{ ++ return usbd_enable_function(bus, arg, serial_number); ++} ++ ++/*! ++ * usbd_disable_function() - called to disable the current function ++ * ++ * Used by a USB Bus interface driver to destroy a virtual device. ++ * ++ * @param bus ++ */ ++void usbd_disable_function (struct usbd_bus_instance *bus) ++{ ++ DOWN(&usbd_bus_sem); ++ ++ // prevent any more bottom half scheduling ++ bus->status = USBD_CLOSING; ++ UP(&usbd_bus_sem); ++ ++ /* XXX ++ * if composite ??? ++ */ ++ switch(bus->function_instance->function_type) { ++ case function_simple: ++ case function_interface: ++ break; ++ case function_composite: ++ break; ++ } ++ ++ // wait for pending device bottom half to finish ++//XXX FIXME ++ //while (bus->device_bh.data /*|| device->function_bh.data */) { ++ while (PENDING_WORK_ITEM(bus->device_bh)){ ++ ++ //printk(KERN_INFO"%s: waiting for usbd_device_bh %ld %p interrupt: %d\n", __FUNCTION__, ++ // bus->device_bh.sync, bus->device_bh.data, in_interrupt()); ++ ++ // This can probably be either, but for consistency's sake... ++#if 1 ++ //I.e. run this task immediately and then delay up to 2000 secdons ... ++#ifdef LINUX24 ++ queue_task(&bus->device_bh, &tq_immediate); ++ mark_bh (IMMEDIATE_BH); ++#else ++ SCHEDULE_WORK(bus->device_bh); ++#endif ++ // schedule_task(&device->device_bh); ++ ++ SCHEDULE_TIMEOUT (2000 * HZ); ++#else ++ //Generic version ... ++ SCHEDULE_IMMEDIATE_WORK(bus->device_bh); ++ SCHEDULE_TIMEOUT(2000); //Seconds ++#endif ++ } ++ DOWN(&usbd_bus_sem); ++ ++ // tell the function driver to close ++ usbd_function_disable (bus->ep0); ++ usbd_function_disable (bus->function_instance); ++ ++ // free alternates memory ++ if (bus->alternates) { ++ bus->bNumInterfaces = 0; ++ LKFREE(bus->alternates); ++ bus->alternates = NULL; ++ } ++ if (bus->function_instance) { ++#if 0 ++ if (bus->function_instance->endpoint_map_array) ++ LKFREE(bus->function_instance->endpoint_map_array); ++ LKFREE(bus->function_instance); ++#endif ++ bus->function_instance = NULL; ++ } ++ usbd_strings_exit(); ++ bus->status = USBD_CLOSED; ++ UP(&usbd_bus_sem); ++} ++ ++OTG_EXPORT_SYMBOL(usbd_enable_function); ++OTG_EXPORT_SYMBOL(usbd_enable_function_irq); ++OTG_EXPORT_SYMBOL(usbd_disable_function); ++ ++/* ********************************************************************************************* */ ++/* ********************************************************************************************* */ ++/*! ++ * Bus Event Handling: ++ * usbd_bus_event_handler() ++ * ++ */ ++static usbd_device_state_t event_states[DEVICE_CLOSE] = { ++ STATE_UNKNOWN, STATE_INIT, STATE_ATTACHED, STATE_POWERED, ++ STATE_DEFAULT, STATE_ADDRESSED, STATE_CONFIGURED, STATE_UNKNOWN, ++ STATE_UNKNOWN, STATE_UNKNOWN, STATE_ADDRESSED, STATE_SUSPENDED, ++ STATE_UNKNOWN, STATE_POWERED, STATE_ATTACHED, ++}; ++ ++static usbd_device_status_t event_status[DEVICE_CLOSE+1] = { ++ USBD_UNKNOWN, // DEVICE_UNKNOWN ++ USBD_OPENING, // DEVICE_INIT ++ USBD_OPENING, // DEVICE_CREATE ++ USBD_OPENING, // DEVICE_HUB_CONFIGURED ++ USBD_RESETING, // DEVICE_RESET ++ USBD_OK, // DEVICE_ADDRESS_ASSIGNED ++ USBD_OK, // DEVICE_CONFIGURED ++ USBD_OK, // DEVICE_SET_INTERFACE ++ USBD_OK, // DEVICE_SET_FEATURE ++ USBD_OK, // DEVICE_CLEAR_FEATURE ++ USBD_OK, // DEVICE_DE_CONFIGURED ++ USBD_SUSPENDED, // DEVICE_BUS_INACTIVE ++ USBD_OK, // DEVICE_BUS_ACTIVITY ++ USBD_RESETING, // DEVICE_POWER_INTERRUPTION ++ USBD_RESETING, // DEVICE_HUB_RESET ++ USBD_CLOSING, // DEVICE_DESTROY ++ USBD_CLOSED, // DEVICE_CLOSE ++}; ++ ++/*! ++ * usbd_bus_event_handler_irq() - called to respond to various usb events ++ * ++ * This is called from an interrupt context. ++ * ++ * Used by a Bus driver to indicate an event. ++ * @param bus ++ * @param event ++ * @param data ++ */ ++void usbd_bus_event_handler_irq (struct usbd_bus_instance *bus, usbd_device_event_t event, int data) ++{ ++ RETURN_UNLESS(bus); ++ ++ /* get the next bus device state by table lookup of event, we need to save the ++ * current state IFF we are suspending so that it can be restored on resume. ++ */ ++ switch (event) { ++ case DEVICE_BUS_INACTIVE: ++ bus->suspended_state = bus->device_state; ++ /* FALL THROUGH */ ++ default: ++ bus->device_state = event_states[event]; ++ //TRACE_MSG0(USBD, "default"); ++ break; ++ case DEVICE_UNKNOWN: ++ //TRACE_MSG0(USBD, "UNKNOWN"); ++ break; ++ case DEVICE_BUS_ACTIVITY: ++ bus->device_state = bus->suspended_state; ++ //TRACE_MSG0(USBD, "ACTIVITY"); ++ break; ++ ++ case DEVICE_SET_INTERFACE: ++ case DEVICE_SET_FEATURE: ++ case DEVICE_CLEAR_FEATURE: ++ //TRACE_MSG0(USBD, "SET"); ++ break; ++ } ++ ++ /* set the bus status by table lookup of event. ++ */ ++ switch (event) { ++ case DEVICE_BUS_ACTIVITY: ++ case DEVICE_BUS_INACTIVE: ++ BREAK_IF(USBD_CLOSING != bus->status); ++ /* FALL THROUGH */ ++ default: ++ bus->status = event_status[event]; ++ //TRACE_MSG0(USBD, "DEFAULT"); ++ break; ++ } ++ ++ /* we need to reset things on some transitions ++ */ ++ switch (bus->device_state) { ++ default: ++ // if we lost configuration then get rid of alternate settings ++ if (bus->alternates) { ++ LKFREE(bus->alternates); ++ bus->alternates = NULL; ++ } ++ bus->bNumInterfaces = 0; ++ bus->device_feature_settings = 0; ++ bus->ConfigurationValue = 0; ++ case STATE_CONFIGURED: ++ break; ++ case STATE_SUSPENDED: ++ // XXX Checkme - is suspend equivalent to end of session? ++ // C.f. OTG 6.5.1-6.5.3 imply that these must be reset at the end of a session or reset ++ //bus->device_feature_settings &= ++ // ~(FEATURE(USB_OTG_A_HNP_ENABLE) | FEATURE(USB_OTG_B_HNP_ENABLE) | FEATURE(USB_OTG_A_ALT_HNP_ENABLE)); ++ break; ++ } ++ /* Pass the event to the bus interface driver and then the function driver and ++ * any interface function drivers. ++ */ ++ bus->driver->bops->event_handler (bus, event, data); ++ bus->function_instance->function_driver->fops->event_handler(bus->function_instance, event, data); ++ #if 0 ++ // XXX pass to interface function drivers ++ #endif ++} ++ ++/*! ++ * usbd_bus_event_handler() - process bus event ++ * @param bus ++ * @param event ++ * @param data ++ */ ++void usbd_bus_event_handler (struct usbd_bus_instance *bus, usbd_device_event_t event, int data) ++{ ++ unsigned long flags; ++ local_irq_save (flags); ++ usbd_bus_event_handler_irq(bus, event, data); ++ local_irq_restore (flags); ++} ++ ++OTG_EXPORT_SYMBOL(usbd_bus_event_handler); ++OTG_EXPORT_SYMBOL(usbd_bus_event_handler_irq); ++ ++/* ********************************************************************************************* */ ++/* ********************************************************************************************* */ ++/*! ++ * Device Request Processing: ++ * usbd_device_request() ++ * ++ */ ++ ++/*! ++ * C.f. 9.4 Standard Requests and table 9.3 ++ * ++ * Encode valid requests into a bitmap for each recipient type for ++ * both directions. This is needed because there are some requests ++ * that appear to be a standard request but are not described in ++ * Chapter nine. For example the mouse driver hid request. ++ * ++ * So we only process the actual set of standard requests that are ++ * defined in chapter nine and even if it appears to be standard but ++ * is not in chapter nine then we pass it to the function driver. ++ * ++ * @{ ++ */ ++ ++#define STD(x) (1<<(x+1)) ++ ++/*! @{ */ ++/*! h2d_standard_requests and d2h_standard_requests ++ * ++ * These tables list all of the valid Chapter Nine requests. Any request ++ * not listed in these tables will NOT be processed by the EP0 function and ++ * will instead be passed to the appropriate function driver. ++ */ ++u32 h2d_standard_requests[4] = { ++ // 0 - Device ++ STD(USB_REQ_CLEAR_FEATURE) | ++ STD(USB_REQ_SET_FEATURE) | ++ STD(USB_REQ_SET_ADDRESS) | ++ STD(USB_REQ_SET_DESCRIPTOR) | ++ STD(USB_REQ_SET_CONFIGURATION) , ++ // 1 - Interface ++ STD(USB_REQ_CLEAR_FEATURE) | ++ STD(USB_REQ_SET_FEATURE) | ++ STD(USB_REQ_SET_INTERFACE) , ++ // 2 - Endpoint ++ STD(USB_REQ_CLEAR_FEATURE) | ++ STD(USB_REQ_SET_FEATURE) , ++ // 3 - Other ++ 0, ++}; ++ ++u32 d2h_standard_requests[4] = { ++ // 0 - Device ++ STD(USB_REQ_GET_STATUS) | ++ STD(USB_REQ_GET_DESCRIPTOR) | ++ STD(USB_REQ_GET_CONFIGURATION), ++ // 1 - Interface ++ STD(USB_REQ_GET_STATUS) | ++ STD(USB_REQ_GET_INTERFACE) , ++ // 2 - Endpoint ++ STD(USB_REQ_GET_STATUS) | ++ STD(USB_REQ_SYNCH_FRAME) , ++ // 3 - Other ++ 0, ++}; ++ ++/* @} */ ++ ++ ++/*! ++ * devreq_set_configuration() - process a received urb ++ * ++ * Used by a USB Bus interface driver to pass received device request to ++ * the appropriate USB Function driver. ++ * ++ * @param function ++ * @param request ++ * @return non-zero if errror ++ */ ++static int devreq_set_configuration(struct usbd_function_instance *function, int wValue) ++{ ++ struct usbd_bus_instance *bus = function->bus; ++ struct usbd_function_driver *function_driver = bus->function_instance->function_driver; ++ int bNumConfigurations = function_driver->bNumConfigurations; ++ int bNumInterfaces; ++ u8 ConfigurationValue; ++ ++ struct usbd_configuration_instance *configuration_instance; ++ struct usbd_configuration_descriptor *configuration_descriptor; ++ ++ // get rid of previous interface and alternates ++ if (bus->bNumInterfaces && bus->alternates) { ++ bus->bNumInterfaces = 0; ++ LKFREE(bus->alternates); ++ bus->alternates = NULL; ++ } ++ ++ // c.f. 9.4.7 - the top half of wValue is reserved ++ // ++ // c.f. 9.4.7 - zero is the default or addressed state, in our case this ++ // is the same is configuration zero, but will be fixed in usbd.c when used. ++ ++ ConfigurationValue = wValue & 0x7f; ++ ++ RETURN_EINVAL_IF(ConfigurationValue > bNumConfigurations); ++ ++ /* save ConfigurationValue for GET_CONFIGURATION request ++ */ ++ bus->ConfigurationValue = ConfigurationValue; ++ ++ /* A ConfigurationValue of zero is for the default configuration (i.e.) the ++ * first one. A non-zero value needs to be decremented to allow it to be used ++ * as an index. ++ */ ++ ConfigurationValue = !ConfigurationValue ? 0 : ConfigurationValue -1; ++ ++ configuration_instance = ++ &function_driver->configuration_instance_array[ConfigurationValue]; ++ ++ configuration_descriptor = configuration_instance->configuration_descriptor; ++ ++ RETURN_EINVAL_IF(!configuration_descriptor); ++ ++ bNumInterfaces = configuration_instance->bNumInterfaces; ++ ++ /* reset interface and alternate settings ++ */ ++ RETURN_EINVAL_IF (!(bus->alternates = (u8 *) CKMALLOC(bNumInterfaces, GFP_ATOMIC))); ++ bus->bNumInterfaces = bNumInterfaces; ++ ++ /* Call function driver set_configuration() operation and if available any ++ * interface drivers set_configuration() operations. ++ */ ++ if (function_driver->fops->set_configuration) { ++ function_driver->fops->function_enable && function_driver->fops->set_configuration (function, wValue); ++ } ++ /* ++ * XXX ++ */ ++ ++ usbd_bus_event_handler (bus, DEVICE_CONFIGURED, 0); ++ return 0; ++} ++ ++/*! ++ * devreq_set_interface_altsetting() - ++ * ++ * Used by a USB Bus interface driver to pass received device request to ++ * the appropriate USB Function driver. ++ * ++ * @param function ++ * @param request ++ * @return non-zero if errror ++ */ ++static int devreq_set_interface_altsetting(struct usbd_function_instance *function, int wIndex, int wValue) ++{ ++ struct usbd_bus_instance *bus = function->bus; ++ struct usbd_function_driver *function_driver = function->function_driver; ++ ++ RETURN_EINVAL_IF(wIndex > bus->bNumInterfaces); // wIndex is interface ++ ++ bus->alternates[wIndex] = (u8) wValue; ++ ++ /* Call function driver set_interface() operation and if available any ++ * interface drivers set_interface() operations. ++ */ ++ if (function_driver->fops->set_interface) ++ function_driver->fops->function_enable && function_driver->fops->set_interface (function, wIndex, wValue); ++ /* ++ * XXX ++ */ ++ ++ usbd_bus_event_handler (bus, DEVICE_SET_INTERFACE, 0); ++ ++ return 0; ++} ++ ++/*! ++ * devreq_get_device_feature_settings() - process a received urb ++ * ++ * Used by a USB Bus interface driver to pass received device request to ++ * the appropriate USB Function driver. ++ * ++ * @param function ++ * @param request ++ * @return non-zero if errror ++ */ ++int devreq_get_device_feature_settings(struct usbd_function_instance *function) ++{ ++ struct usbd_bus_instance *bus = function->bus; ++ return bus->device_feature_settings; ++} ++ ++ ++/*! devreq_device_feature - handle set/clear feature requests for device ++ * Used by the USB Device Core to set/clear endpoint halt status. ++ * ++ * We assume that if the udc driver does not implement anything then ++ * we should just return zero for ok. ++ */ ++int devreq_device_feature (struct usbd_bus_instance *bus, int features, int flag) ++{ ++ struct pcd_instance *pcd = (struct pcd_instance *)bus->privdata; ++ //u32 features = bus->device_feature_settings; ++ ++ TRACE_MSG0(USBD, "BUS_DEVICE FEATURE"); ++ ++ /* if A-Device has enabled hnp ++ */ ++ if (features & FEATURE(USB_OTG_B_HNP_ENABLE) ) ++ otg_event(pcd->otg, HNP_ENABLED, USBD, "DEVICE FEATURE - B_HNP_ENABLE"); ++ ++ /* if A-Device does not support HNP on this port ++ */ ++ else if ( features & FEATURE(USB_OTG_A_ALT_HNP_ENABLE) ) ++ ; //otg_event(pcd->otg, not(a_hnp_support)); ++ ++ /* if A-Device does support HNP ++ */ ++ else if (features & FEATURE(USB_OTG_A_HNP_SUPPORT)) ++ ; //otg_event(pcd->otg, a_hnp_support); ++ ++ return 0; ++ //return usbd_pcd_ops.device_feature ? usbd_pcd_ops.device_feature() : 0; ++} ++ ++/*! ++ * copy_config() - copy data into buffer ++ */ ++static int copy_config (u8 *cp, void *data, int actual_length, int max_buf) ++{ ++ int available = max_buf - actual_length; ++ int length = MIN(*(u8 *)data, available); ++ RETURN_ZERO_UNLESS (data); ++ RETURN_ZERO_UNLESS (length); ++ memcpy (cp, data, length); ++ return length; ++} ++ ++/*! ++ * copy_endpoint() - copy data into buffer ++ */ ++static int copy_endpoint (struct usbd_function_instance *function, u8 *cp, ++ struct usbd_endpoint_descriptor *endpoint, int endpoint_index, int actual_length, int max_buf, int hs) ++{ ++ int available = max_buf - actual_length; ++ int length = MIN(endpoint->bLength, available); ++ struct usbd_endpoint_descriptor endpoint_copy; ++ ++ RETURN_ZERO_IF (!length); ++ memcpy (&endpoint_copy, endpoint, endpoint->bLength); ++ usbd_endpoint_update(function, endpoint_index, &endpoint_copy, hs); ++ memcpy (cp, &endpoint_copy, length); ++ return length; ++} ++ ++ ++/*! ++ * usbd_old_get_descriptor() - copy a descriptor into buffer ++ * ++ * Return non-zero for error. ++ */ ++int usbd_old_get_descriptor (struct usbd_function_instance *function, u8 *buffer, int max, int descriptor_type, int index) ++{ ++ struct usbd_bus_instance *bus = function->bus; ++ struct usbd_function_driver *function_driver = bus->function_instance->function_driver; ++ int actual_length = 0; ++ ++ switch (descriptor_type) { ++ case USB_DT_DEVICE: ++ { ++ struct usbd_device_descriptor *device_descriptor = function_driver->device_descriptor; ++ ++ // copy descriptor for this device ++ actual_length += copy_config (buffer + actual_length, device_descriptor, actual_length, max); ++ ++ // correct the correct control endpoint 0 max packet size into the descriptor ++ device_descriptor = (struct usbd_device_descriptor *) buffer; ++ device_descriptor->bMaxPacketSize0 = bus->driver->maxpacketsize; ++ } ++ break; ++ ++ #ifdef CONFIG_OTG_HIGH_SPEED ++ case USB_DT_DEVICE_QUALIFIER: // c.f. 9.6.2 Device Qualifier ++ { ++ struct usbd_device_qualifier_descriptor *device_qualifier_descriptor = ++ function_driver->device_qualifier_descriptor; ++ ++ // copy descriptor for this device ++ actual_length += copy_config (buffer + actual_length, device_qualifier_descriptor, actual_length, max); ++ ++ } ++ break; ++ ++ case USB_DT_OTHER_SPEED_CONFIGURATION: ++ #endif /* CONFIG_OTG_HIGH_SPEED */ ++ ++ case USB_DT_CONFIGURATION: ++ { ++ #ifdef CONFIG_OTG_HIGH_SPEED ++ int hs = bus->HighSpeedFlag ? descriptor_type == USB_DT_CONFIGURATION: ++ descriptor_type == USB_DT_OTHER_SPEED_CONFIGURATION; ++ #else /* CONFIG_OTG_HIGH_SPEED */ ++ int hs = 0; ++ #endif /* CONFIG_OTG_HIGH_SPEED */ ++ int interface_num; ++ ++ struct usbd_configuration_instance *configuration_instance = ++ &function_driver->configuration_instance_array[index]; ++ ++ struct usbd_configuration_descriptor *configuration_descriptor = ++ configuration_instance->configuration_descriptor; ++ ++ struct usbd_otg_descriptor *otg_descriptor = function_driver->otg_descriptor; ++ ++ RETURN_EINVAL_IF (!configuration_descriptor); ++ RETURN_EINVAL_IF (index > function_driver->device_descriptor->bNumConfigurations); ++ ++ actual_length += copy_config (buffer + actual_length, configuration_descriptor, actual_length, max); ++ ++ // iterate across all bNumInterfaces for specified configuration ++ for (interface_num = 0; interface_num < configuration_descriptor->bNumInterfaces; interface_num++) { ++ ++ int alternate_num; ++ struct usbd_interfaces_instance *interfaces_instance = ++ configuration_instance->interfaces_instance_array + interface_num; ++ ++ // iterate across all interface alternates ++ for (alternate_num = 0; alternate_num < interfaces_instance->alternates; alternate_num++) { ++ ++ int class_num; ++ int endpoint; ++ ++ struct usbd_alternate_instance *alternate_instance = ++ interfaces_instance->alternates_instance_array + alternate_num; ++ ++ //struct usbd_interface_descriptor *usbd_interface_descriptor; ++ ++ // copy descriptor for this interface ++ actual_length += copy_config (buffer + actual_length, ++ alternate_instance->interface_descriptor, actual_length, max); ++ ++ // iterate across all classes for the alternate interface ++ for (class_num = 0; class_num < alternate_instance->classes; class_num++) ++ actual_length += copy_config (buffer + actual_length, ++ *(alternate_instance->class_list + class_num), ++ actual_length, max); ++ ++ // iterate across all endpoints for the alternate interface ++ //interface_descriptor = alternate_instance->interface_descriptor; ++ ++ for (endpoint = 0; endpoint < alternate_instance->endpoints ; endpoint++) { ++ ++ TRACE_MSG2(USBD, "endpoint: %d index: %d", ++ endpoint, alternate_instance->endpoint_indexes[endpoint]); ++ ++ actual_length += copy_endpoint ( ++ bus->function_instance, ++ buffer + actual_length, ++ *(( alternate_instance->endpoint_list) + endpoint), ++ alternate_instance->endpoint_indexes[endpoint], ++ actual_length, ++ max, ++ hs); ++ } ++ } ++ } ++ ++ /* copy the OTG descriptor if required ++ */ ++ actual_length += copy_config (buffer + actual_length, otg_descriptor, actual_length, max); ++ ++ /* set the configuration attributes and power fields in the configuration descriptor ++ */ ++ configuration_descriptor = (struct usbd_configuration_descriptor *)buffer; ++ ++ configuration_descriptor->bmAttributes = BMATTRIBUTE_RESERVED; ++ ++ if (bus->device_feature_settings & FEATURE(USB_DEVICE_REMOTE_WAKEUP)) ++ configuration_descriptor->bmAttributes |= BMATTRIBUTE_REMOTE_WAKEUP; ++ ++ if (bus->driver->bMaxPower == 0) { ++ configuration_descriptor->bmAttributes |= BMATTRIBUTE_SELF_POWERED; ++ configuration_descriptor->bMaxPower = 1; ++ } ++ else ++ configuration_descriptor->bMaxPower = bus->driver->bMaxPower; ++ ++ TRACE_MSG2(USBD, "bmAttributes: %02x bMaxPower: %02x", ++ configuration_descriptor->bmAttributes, configuration_descriptor->bMaxPower); ++ } ++ break; ++ ++ case USB_DT_STRING: ++ { ++ struct usbd_string_descriptor *string_descriptor = NULL; ++ RETURN_EINVAL_IF (!(string_descriptor = usbd_get_string_descriptor ((u8)index))); ++ actual_length += copy_config (buffer + actual_length, string_descriptor, actual_length, max); ++ } ++ break; ++ default: ++ return -EINVAL; ++ } ++ return actual_length; ++} ++ ++ ++ ++ ++/*! ++ * setclear() - set or reset a bit in a mask ++ * @param features ++ * @param flag ++ * @param value ++ */ ++static void setclear(u32 *features, u32 flag, u32 value) ++{ ++ if (value) *features |= flag; ++ else *features &= ~flag; ++} ++ ++/*! do_standard_device_request - process a device request ++ * ++ * Process a received device request. If not a Chapter nine request pass it ++ * to the other loaded function driver device_request() function. ++ * ++ * Return non-zero to indicate failure. ++ */ ++static int do_standard_device_request (struct usbd_function_instance *function, struct usbd_device_request *request) ++{ ++ struct usbd_bus_instance *bus = function->bus; ++ u8 bRequest = request->bRequest; ++ u8 bmRequestType = request->bmRequestType; ++ u16 wValue = le16_to_cpu(request->wValue); ++ u16 wIndex = le16_to_cpu(request->wIndex); ++ u16 wLength = le16_to_cpu(request->wLength); ++ u16 endpoint_index; ++ ++ usbd_device_state_t device_state = usbd_get_device_state(function); ++ ++ switch (device_state) { ++ case STATE_CREATED: ++ case STATE_ATTACHED: ++ case STATE_POWERED: ++ TRACE_MSG1(USBD, "bad device_state: %d", device_state); ++ return -EINVAL; ++ ++ case STATE_INIT: ++ case STATE_DEFAULT: ++ switch (bRequest) { ++ case USB_REQ_CLEAR_FEATURE: ++ case USB_REQ_GET_INTERFACE: ++ case USB_REQ_GET_STATUS: ++ case USB_REQ_SET_DESCRIPTOR: ++ case USB_REQ_SET_INTERFACE: ++ case USB_REQ_SYNCH_FRAME: ++ TRACE_MSG2(USBD, "bad request: %d for this device_state: %d", bRequest, device_state); ++ return -EINVAL; ++ ++ case USB_REQ_GET_CONFIGURATION: ++ case USB_REQ_SET_ADDRESS: ++ case USB_REQ_SET_CONFIGURATION: ++ case USB_REQ_GET_DESCRIPTOR: ++ case USB_REQ_SET_FEATURE: ++ break; ++ } ++ case STATE_ADDRESSED: ++ case STATE_CONFIGURED: ++ case STATE_SUSPENDED: ++ break; ++ case STATE_UNKNOWN: ++ TRACE_MSG1(USBD, "unknown device_state: %d", device_state); ++ return -EINVAL; ++ } ++ ++ /* Handle all requests that return data (direction bit set on bm RequestType). ++ * N.B. no Chapter 9 requests send additional data. ++ */ ++ if ((bmRequestType & USB_REQ_DIRECTION_MASK)) { ++ struct usbd_urb *urb; ++ int rc = 0; ++ switch (bRequest) { ++ case USB_REQ_CLEAR_FEATURE: ++ case USB_REQ_SET_ADDRESS: ++ case USB_REQ_SET_CONFIGURATION: ++ case USB_REQ_SET_DESCRIPTOR: ++ case USB_REQ_SET_FEATURE: ++ case USB_REQ_SET_INTERFACE: ++ case USB_REQ_SYNCH_FRAME: ++ TRACE_MSG0(USBD, "bad direction"); ++ return -EINVAL; ++ } ++ ++ RETURN_EINVAL_IF(!wLength); ++ ++ /* allocate urb, no callback, urb will be automatically de-allocated ++ */ ++ RETURN_EINVAL_IF(!(urb = usbd_alloc_urb_ep0 (function, wLength, NULL))); ++ ++ switch (bRequest) { ++ ++ case USB_REQ_GET_STATUS: ++ urb->actual_length = 2; ++ urb->buffer[0] = urb->buffer[1] = 0; ++ ++ rc = 0; ++ switch (bmRequestType & USB_REQ_RECIPIENT_MASK) { ++ case USB_REQ_RECIPIENT_DEVICE: ++ ++ UNLESS (usbd_get_bMaxPower(function)) urb->buffer[0] |= USB_STATUS_SELFPOWERED; ++ urb->buffer[0] |= ++ (devreq_get_device_feature_settings(function) & FEATURE(USB_DEVICE_REMOTE_WAKEUP)) ? ++ USB_STATUS_REMOTEWAKEUP : 0; ++ break; ++ case USB_REQ_RECIPIENT_ENDPOINT: ++ RETURN_EINVAL_UNLESS((endpoint_index = usbd_find_endpoint_index(bus, wIndex)) < bus->endpoints); ++ urb->buffer[0] = usbd_endpoint_halted (function, endpoint_index); ++ break; ++ case USB_REQ_RECIPIENT_INTERFACE: ++ break; ++ case USB_REQ_RECIPIENT_OTHER: ++ default: ++ TRACE_MSG0(USBD, "bad recipient"); ++ rc = -EINVAL; ++ } ++ break; ++ ++ case USB_REQ_GET_DESCRIPTOR: ++ rc = usbd_old_get_descriptor (function, urb->buffer, wLength, wValue >> 8, wValue & 0xff); ++ if (rc != -EINVAL) { ++ urb->actual_length = rc; ++ rc = 0; ++ } ++ else ++ TRACE_MSG0(USBD, "bad description"); ++ break; ++ ++ case USB_REQ_GET_CONFIGURATION: ++ TRACE_MSG1(USBD, "GET CONFIGURATION: %d", bus->ConfigurationValue); ++ urb->actual_length = 1; ++ urb->buffer[0] = bus->ConfigurationValue; ++ break; ++ ++ case USB_REQ_GET_INTERFACE: ++ RETURN_EINVAL_IF(wIndex > bus->bNumInterfaces); ++ urb->actual_length = 1; ++ urb->buffer[0] = bus->alternates[wIndex]; ++ break; ++ default: ++ TRACE_MSG0(USBD, "bad descriptor type"); ++ rc = 1; ++ } ++ ++ if (!(urb->actual_length % usbd_endpoint_zero_wMaxPacketSize(function, usbd_high_speed(function))) && ++ (urb->actual_length < wLength)) ++ urb->flags |= USBD_URB_SENDZLP; ++ ++ RETURN_ZERO_UNLESS(rc || usbd_start_in_urb(urb)); ++ /* only get here if error */ ++ usbd_free_urb(urb); ++ TRACE_MSG0(USBD, "get failed"); ++ return -EINVAL; ++ } ++ /* Handle the requests that do not return data. ++ */ ++ else { ++ int setclear_flag = USB_REQ_SET_FEATURE == bRequest; ++ switch (bRequest) { ++ case USB_REQ_CLEAR_FEATURE: ++ case USB_REQ_SET_FEATURE: ++ TRACE_MSG2(USBD, "FEATURE: recipient: %d wValue: %d", ++ bmRequestType & USB_REQ_RECIPIENT_MASK, wValue); ++ switch (bmRequestType & USB_REQ_RECIPIENT_MASK) { ++ case USB_REQ_RECIPIENT_DEVICE: ++ //switch (wValue) { ++ // otg_event(pcd->otg, HNP_ENABLED, PCD, "DEVICE FEATURE - B_HNP_ENABLE"); ++ // break; ++ //} ++ switch (wValue) { ++ case USB_OTG_A_ALT_HNP_ENABLE: // C.f. OTG 6.5.3 ++ case USB_OTG_A_HNP_SUPPORT: // C.f. OTG 6.5.2 ++ case USB_OTG_B_HNP_ENABLE: // C.f. OTG 6.5.1 ++ RETURN_EINVAL_UNLESS(setclear_flag); // cleared by reset ++ case USB_DEVICE_REMOTE_WAKEUP: ++ break; ++ default: ++ return -EINVAL; ++ } ++ setclear(&bus->device_feature_settings, FEATURE(wValue), setclear_flag); ++ return devreq_device_feature(bus, FEATURE(wValue), setclear_flag); ++ ++ case USB_REQ_RECIPIENT_ENDPOINT: ++ RETURN_EINVAL_UNLESS(USB_ENDPOINT_HALT == wValue); ++ ++ setclear(&(bus->endpoint_array[wIndex].feature_setting), ++ FEATURE(wValue), bRequest == USB_REQ_SET_FEATURE); ++ ++ /* wIndex contains bEndpointAddress, also find and verify the endpoint map index as well. ++ * Then clear the endpoint using the bus interface driver halt_endpoint() operation, ++ * if that is ok, then inform the function driver using the endpoint_cleared() operation. ++ * ++ */ ++ RETURN_EINVAL_UNLESS((endpoint_index = usbd_find_endpoint_index(bus, wIndex)) < bus->endpoints); ++ RETURN_EINVAL_IF(function->bus->driver->bops->halt_endpoint(bus, wIndex, ++ endpoint_index, setclear_flag)); ++ ++ if (function->function_driver->fops->endpoint_cleared && !setclear_flag) ++ function->function_driver->fops->endpoint_cleared(function, wIndex, endpoint_index ); ++ ++ return 0; ++ ++ case USB_REQ_RECIPIENT_INTERFACE: ++ case USB_REQ_RECIPIENT_OTHER: ++ default: ++ TRACE_MSG0(USBD, "bad recipient"); ++ return -EINVAL; ++ } ++ ++ case USB_REQ_SET_ADDRESS: ++ if (bus->driver->bops->set_address) bus->driver->bops->set_address (bus, wValue); ++ usbd_bus_event_handler (bus, DEVICE_ADDRESS_ASSIGNED, wValue); ++ return 0; ++ ++ case USB_REQ_SET_DESCRIPTOR: ++ TRACE_MSG0(USBD, "set descriptor not supported"); ++ return -EINVAL; ++ ++ case USB_REQ_SET_CONFIGURATION: ++ return devreq_set_configuration(function, wValue); ++ ++ case USB_REQ_SET_INTERFACE: ++ return devreq_set_interface_altsetting(function, wIndex, wValue); ++ ++ case USB_REQ_GET_CONFIGURATION: ++ case USB_REQ_GET_DESCRIPTOR: ++ case USB_REQ_GET_INTERFACE: ++ case USB_REQ_GET_STATUS: ++ case USB_REQ_SYNCH_FRAME: ++ TRACE_MSG0(USBD, "unkown"); ++ return -EINVAL; ++ } ++ } ++ TRACE_MSG0(USBD, "not possible"); ++ return -EINVAL; ++} ++ ++ ++/*! ++ * usbd_device_request() - process a device request ++ * ++ * Used by a USB Bus interface driver to pass received device request to ++ * the appropriate USB Function driver. ++ * ++ * @param function ++ * @param request ++ * @return non-zero if errror ++ */ ++int usbd_device_request(struct usbd_bus_instance *bus, struct usbd_device_request *request) ++{ ++ struct usbd_function_instance *function = bus->function_instance; ++ u8 bRequest = request->bRequest; ++ u8 bmRequestType = request->bmRequestType; ++ u16 wValue = le16_to_cpu(request->wValue); ++ u16 wIndex = le16_to_cpu(request->wIndex); ++ u16 wLength = le16_to_cpu(request->wLength); ++ ++ TRACE_MSG5(USBD, "bmRequestType:%02x bRequest:%02x wValue:%04x wIndex:%04x wLength:%04x", ++ bmRequestType, bRequest, wValue, wIndex, wLength); ++ ++ /* Handle only USB Standard Requests (c.f. USB Spec table 9-2, D6..5 must be 0), ++ * otherwise call the currently enabled function specific receive setup. ++ */ ++ if (!(bmRequestType & USB_REQ_TYPE_MASK) && ++ ((bmRequestType & USB_DIR_IN ? d2h_standard_requests : h2d_standard_requests) ++ [bmRequestType & 0x3] & STD(bRequest))) ++ return do_standard_device_request(function, request); ++ ++ /* Standard Requests not in Chapter nine, Class and Vendor device requests ++ * must be handled by the function driver or interface driver as appropriate. ++ */ ++ switch(bmRequestType & USB_REQ_RECIPIENT_MASK) { ++ case USB_REQ_RECIPIENT_INTERFACE: ++ case USB_REQ_RECIPIENT_ENDPOINT: ++ /* XXX ++ * if function->interfaces[] is valid then we shuffle request off to ++ * interface driver for interface and endpoint recipients, otherwise ++ * fall through. ++ */ ++ ++ /* ++ * XXX for now just fall through for all. ++ */ ++ ++ case USB_REQ_RECIPIENT_DEVICE: ++ case USB_REQ_RECIPIENT_OTHER: ++ default: ++ return function->function_driver->fops->device_request(function, request); ++ } ++} ++ ++OTG_EXPORT_SYMBOL(usbd_device_request); ++OTG_EXPORT_SYMBOL(usbd_old_get_descriptor); ++ ++/* ********************************************************************************************* */ ++/* ********************************************************************************************* */ ++/*! ++ * Device I/O: ++ * usbd_urb_finished() ++ * usbd_first_urb_detached() ++ * usbd_find_endpoint_address() ++ */ ++ ++/*! ++ * do_urb_callback() - tell function that an urb has been transmitted. ++ * ++ * 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. ++ * ++ * If there is no callback or if the URB call back returns non-zero then the ++ * urb must be de-allocated here. ++ * ++ * @param urb ++ * @param rc ++ */ ++static void do_urb_callback (struct usbd_urb *urb, int rc) ++{ ++ RETURN_UNLESS (urb); ++ if (!urb->callback || urb->callback(urb,rc)) ++ usbd_free_urb(urb); ++} ++ ++/*! ++ * urb_append_irq() - append a linked list of urbs to another linked list ++ * @param hd link to urb list ++ * @param urb to append to list ++ */ ++static INLINE void urb_append_irq (urb_link * hd, struct usbd_urb *urb) ++{ ++ urb_link *new; ++ urb_link *pul; ++ RETURN_UNLESS (hd && urb); ++ new = &urb->link; ++ pul = hd->prev; ++ new->prev->next = hd; ++ hd->prev = new->prev; ++ new->prev = pul; ++ pul->next = new; ++} ++ ++/*! ++ * urb_append() - irq safe version ++ * @param hd list of urbs ++ * @param urb urb to append ++ */ ++void urb_append(urb_link *hd, struct usbd_urb *urb) ++{ ++ unsigned long flags; ++ local_irq_save (flags); ++ urb_append_irq(hd, urb); ++ local_irq_restore (flags); ++} ++ ++ ++/*! ++ * usbd_urb_finished() - tell function that an urb has been finished. ++ * ++ * Used by a USB Bus driver to pass a sent urb back to the function ++ * driver via the endpoints finished queue. ++ * @param urb urb to process ++ * @param rc result code ++ * ++ */ ++void usbd_urb_finished(struct usbd_urb *urb, int rc) ++{ ++ urb->status = (usbd_urb_status_t)rc; ++ #if defined(OTG_LINUX) ++ urb->jiffies = jiffies; ++ #endif /* defined(OTG_LINUX) */ ++ if (USBD_OK != urb->bus->status) ++ do_urb_callback (urb, rc); ++ else { ++ urb_append_irq (&(urb->bus->finished), urb); ++ RETURN_IF (PENDING_WORK_ITEM(urb->bus->device_bh)); ++#ifdef LINUX24 ++ //FIXME XXX ++ queue_task (&urb->bus->device_bh, &tq_immediate); ++ mark_bh (IMMEDIATE_BH); ++#else ++ SCHEDULE_WORK(urb->bus->device_bh); ++#endif ++ } ++} ++void usbd_urb_finished_irq (struct usbd_urb *urb, int rc) ++{ ++ usbd_urb_finished(urb, rc); ++} ++ ++/*! ++ * @name STRUCT ++ * @brief ++ * 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) ++ * ++ * @{ ++ */ ++ ++#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))) ++ ++/*! ++ * @name list ++ * @brief List support functions ++ * ++ * @{ ++ */ ++ ++/*! ++ * urb_link() - return first urb_link in list ++ * ++ * 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. ++ * ++ * @return link to urb ++ */ ++static INLINE urb_link *first_urb_link_irq (urb_link * hd) ++{ ++ urb_link *nx; ++ return (!hd || !(nx = hd->next) || (nx == hd)) ? NULL : nx; ++} ++ ++/*! ++ * first_urb_irq() - return first urb in list ++ * Return the first urb in a list with a distinguished ++ * head "hd", or NULL if the list is empty. ++ * ++ * Called from interrupt. ++ * ++ * @param hd linked list of urbs ++ * @return pointer to urb ++ */ ++static INLINE struct usbd_urb *first_urb_irq (urb_link * hd) ++{ ++ urb_link *nx; ++ return (!(nx = first_urb_link_irq (hd))) ? NULL : p2surround (struct usbd_urb, link, nx); ++} ++ ++ ++ ++/*! ++ * usbd_first_urb_detached_irq() - detach an return first urb ++ * Detach and return the first urb in a list with a distinguished ++ * head "hd", or NULL if the list is empty. ++ * ++ * @param hd linked list of urbs ++ * @return pointer to urb or NULL ++ */ ++struct usbd_urb *usbd_first_urb_detached_irq (urb_link * hd) ++{ ++ struct usbd_urb *urb; ++ urb_link *ul; ++ RETURN_NULL_IF (!(urb = first_urb_irq (hd))); ++ ul = &urb->link; ++ ul->next->prev = ul->prev; ++ ul->prev->next = ul->next; ++ ul->prev = ul->next = ul; ++ return urb; ++} ++/* @} */ ++ ++/*! ++ * usbd_first_urb_detached() - detach and return urb ++ * ++ * Detach and return the first urb in a list with a distinguished ++ * head "hd", or NULL if the list is empty. ++ * ++ * @param hd list of urbs ++ * @return pointer to urb or NULL ++ */ ++struct usbd_urb *usbd_first_urb_detached (urb_link * hd) ++{ ++ struct usbd_urb *urb; ++ unsigned long flags; ++ local_irq_save (flags); ++ urb = usbd_first_urb_detached_irq (hd); ++ local_irq_restore (flags); ++ return urb; ++} ++ ++ ++/*! ++ * usbd_find_endpoint_index() ++ * @param bus ++ * @param bEndpointAddress ++ * Find the endpoint map index for the specified bEndpointAddress. ++ */ ++int usbd_find_endpoint_index(struct usbd_bus_instance *bus, int bEndpointAddress) ++{ ++ int i; ++ for (i = 0; i < bus->endpoints; i++) ++ BREAK_IF (bus->endpoint_array[i].bEndpointAddress == bEndpointAddress); ++ return i; ++} ++ ++OTG_EXPORT_SYMBOL(usbd_urb_finished); ++OTG_EXPORT_SYMBOL(usbd_urb_finished_irq); ++OTG_EXPORT_SYMBOL(usbd_first_urb_detached); ++OTG_EXPORT_SYMBOL(usbd_first_urb_detached_irq); ++OTG_EXPORT_SYMBOL(usbd_find_endpoint_index); ++ ++/* ********************************************************************************************** */ ++/* usbb bus bottom half ************************************************************************* */ ++ ++/*! ++ * usbd_device_bh() - Bottom half handler to process sent or received urbs. ++ * @param data ++ */ ++void usbd_device_bh (void *data) ++{ ++ struct usbd_bus_instance *bus = data; ++ struct usbd_endpoint_instance *endpoint; ++ struct usbd_urb *urb; ++ ++ RETURN_IF (!bus || !(endpoint = bus->endpoint_array)); ++ DOWN(&usbd_bus_sem); ++ ++ for (; (urb = usbd_first_urb_detached (&bus->finished)); do_urb_callback (urb, urb->status)); ++ ++ #if defined(OTG_LINUX) ++ if (USBD_CLOSING == bus->status) ++ bus->device_bh.data = NULL; ++ #endif /* defined(OTG_LINUX) */ ++ UP(&usbd_bus_sem); ++} ++ ++/* ********************************************************************************************** */ ++/* Module init ********************************************************************************** */ ++ ++extern char * usbd_function_name(int n); ++struct usbd_ops usbd_ops; ++otg_tag_t USBD; ++ ++/*! ++ * usbd_device_init() - initialize ++ */ ++int usbd_device_init (void) ++{ ++ USBD = otg_trace_obtain_tag(); ++ TRACE_MSG0(USBD,"--"); ++ usbd_ops.function_name = usbd_function_name; ++ otg_set_usbd_ops(&usbd_ops); ++ return 0; ++} ++ ++/*! ++ * usbd_device_exit() - de-initialize ++ */ ++void usbd_device_exit (void) ++{ ++ otg_set_usbd_ops(NULL); ++ otg_trace_invalidate_tag(USBD); ++} ++ ++ +diff -uNr linux/drivers/no-otg/otgcore/usbp-fops.c linux/drivers/otg/otgcore/usbp-fops.c +--- linux/drivers/no-otg/otgcore/usbp-fops.c 1970-01-01 01:00:00.000000000 +0100 ++++ linux/drivers/otg/otgcore/usbp-fops.c 2006-09-01 21:41:35.000000000 +0200 +@@ -0,0 +1,1123 @@ ++/* ++ * otg/otgcore/usbp-fops.c - USB Function support ++ * ++ * Copyright (c) 2004-2005 Belcarra ++ * ++ * By: ++ * Stuart Lynne <sl@belcarra.com>, ++ * Bruce Balden <balden@belcarra.com> ++ * ++ */ ++/*! ++ * @file otg/otgcore/usbp-fops.c ++ * @brief Function driver related functions. ++ * ++ * This implements the functions used to implement USB Function drivers. ++ * ++ * @ingroup USBP ++ */ ++ ++#include <otg/otg-compat.h> ++#include <otg/otg-module.h> ++ ++#include <otg/usbp-chap9.h> ++#include <otg/otg-trace.h> ++#include <otg/usbp-func.h> ++#include <otg/usbp-bus.h> ++ ++ ++struct usbd_string_descriptor **usb_strings; ++void urb_append(urb_link *hd, struct usbd_urb *urb); ++ ++LIST_NODE_INIT(usbd_function_instances); // list of all registered configuration function modules ++LIST_NODE_INIT(usbd_interface_instances); // list of all registered interface function modules ++ ++/* ********************************************************************************************* */ ++/* ********************************************************************************************* */ ++/*! ++ * Function Registration: ++ * usbd_register_function() ++ * usbd_register_interface() ++ * usbd_register_composite() ++ * usbd_deregister_function() ++ * usbd_find_function() ++ * usbd_find_interface() ++ * ++ */ ++ ++/*! ++ * usbd_register_function_internal() - register a usbd function driver ++ * ++ * USBD Function drivers call this to register with the USBD Core layer. ++ * Return NULL on failure. ++ * ++ * @param function_driver ++ * @param privdata ++ * @return function instance ++ * ++ */ ++static struct usbd_function_instance * ++usbd_register_function_internal(struct usbd_function_driver *function_driver, char *name, ++ usbd_function_types_t function_type, char **interface_list, void *privdata) ++{ ++ struct usbd_function_instance *function = NULL; ++ ++ TRACE_MSG3(USBD, "interface: %s driver: %s endpoints: %d", name, ++ function_driver->name, function_driver->endpointsRequested); ++ ++ if (!(function = (struct usbd_function_instance *) CKMALLOC (sizeof (struct usbd_function_instance), GFP_KERNEL))) { ++ #if defined(OTG_LINUX) ++ printk(KERN_ERR,"usbd_function_instance allocation failed.\n"); ++ #endif /* defined(OTG_LINUX) */ ++ #if defined(OTG_WINCE) ++ DEBUGMSG(ZONE_INIT, (_T("usbd_register_function: FAILED\n"))); ++ #endif /* defined(OTG_LINUX) */ ++ return(NULL); ++ } ++ function->name = LSTRDUP(name); ++ function->function_driver = function_driver; ++ function->privdata = privdata; ++ function->function_type = function_type; ++ function->interfaces_list = interface_list; ++ ++ //function->endpointsRequested = function->function_driver->endpointsRequested; ++ //function->requestedEndpoitns = function->function_driver->requestedEndpoints; ++ ++ switch(function_type) { ++ case function_simple: ++ case function_interface: ++ break; ++ case function_composite: ++ break; ++ } ++ ++ if (!(function->endpoint_map_array = (struct usbd_endpoint_map *) ++ CKMALLOC (sizeof(struct usbd_endpoint_map) * ++ function_driver->endpointsRequested, GFP_KERNEL))) { ++ #if defined(OTG_LINUX) ++ printk(KERN_ERR,"usbd_endpoint_map allocation failed.\n"); ++ #endif /* defined(OTG_LINUX) */ ++ #if defined(OTG_WINCE) ++ DEBUGMSG(ZONE_INIT, (_T("usbd_register_function: FAILED\n"))); ++ #endif /* defined(OTG_LINUX) */ ++ LKFREE(function); ++ return(NULL); ++ } ++ ++ switch(function_type) { ++ case function_simple: ++ case function_interface: ++ case function_composite: ++ break; ++ } ++ return function; ++} ++ ++/*! ++ * usbd_register_function() - register a usbd function driver ++ * ++ * USBD Function drivers call this to register with the USBD Core layer. ++ * Return NULL on failure. ++ * ++ * @param function_driver ++ * @param privdata ++ * @return function instance ++ * ++ */ ++struct usbd_function_instance * ++usbd_register_function (struct usbd_function_driver *function_driver, char * name, void *privdata) ++{ ++ struct usbd_function_instance *function = ++ usbd_register_function_internal(function_driver, name, function_simple, NULL, privdata); ++ TRACE_MSG1(USBD, "%s", name); ++ ++ LIST_ADD_TAIL (&function->instances, &usbd_function_instances); ++ return function; ++} ++ ++/*! ++ * usbd_register_interface() - register a usbd function driver ++ * ++ * USBD Function drivers call this to register with the USBD Core layer. ++ * Return NULL on failure. ++ * ++ * @param function_driver ++ * @param privdata ++ * @return function instance ++ * ++ */ ++struct usbd_function_instance * ++usbd_register_interface(struct usbd_function_driver *function_driver, char * name, void *privdata) ++{ ++ struct usbd_function_instance *function = ++ usbd_register_function_internal(function_driver, name, function_interface, NULL, privdata); ++ TRACE_MSG1(USBD, "%s", name); ++ LIST_ADD_TAIL (&function->instances, &usbd_interface_instances); ++ return function; ++} ++ ++/*! ++ * usbd_register_composite() - register a usbd function driver ++ * ++ * USBD Function drivers call this to register with the USBD Core layer. ++ * Return NULL on failure. ++ * ++ * @param function_driver ++ * @param privdata ++ * @return function instance ++ * ++ */ ++struct usbd_function_instance * ++usbd_register_composite (struct usbd_function_driver *function_driver, char * name, char **interfaces_list, void *privdata) ++{ ++ struct usbd_function_instance *function = NULL; ++ struct usbd_function_instance **interfaces_array = NULL; ++ struct usbd_endpoint_request *requestedEndpoints = NULL; ++ struct usbd_endpoint_map *endpoint_map_array = NULL; ++ ++ int i, j, k, l; ++ char *interface_name; ++ int endpoints = 0; ++ int cfg_length = 0; ++ ++ TRACE_MSG1(USBD, "%s", name); ++ THROW_UNLESS((function = usbd_register_function_internal(function_driver, ++ name, function_composite, interfaces_list, privdata)), error); ++ ++ /* list and count interfaces, then allocate interfaces_array. ++ */ ++ for (i = 0; (interface_name = function->interfaces_list[i]); i++) { ++ TRACE_MSG2(USBD, "INTERFACE[%02d] %20s", i, interface_name); ++ } ++ function->interfaces = i; ++ ++ TRACE_MSG1(USBD, "interfaces: %d", function->interfaces); ++ ++ THROW_UNLESS((interfaces_array = (struct usbd_function_instance **) ++ CKMALLOC (sizeof(struct usbd_function_instance) * function->interfaces, GFP_KERNEL)), error); ++ function->interfaces_array = interfaces_array; ++ ++#if 0 ++ /* Find all interfaces and compose the interface list, count the endpoints ++ * and allocte the endpoint request array. Also find and total descriptor sizes. ++ */ ++ for (i = 0; i < function->interfaces; i++) { ++ struct usbd_function_instance *interface_function; ++ struct usbd_interface_description *interface_description = interface_function->interface_list; ++ THROW_UNLESS(interface_function = usbd_find_interface(interfaces_list[i]), error); ++ ++ TRACE_MSG4(USBD, "interface_function: %x name: %s driver: %s %d", interface_function, ++ interface_function->name, ++ interface_function->function_driver->name, ++ interface_function->function_driver->endpointsRequested); ++ ++ interfaces_array[i] = interface_function; ++ endpoints += interface_function->function_driver->endpointsRequested; ++ TRACE_MSG4(USBD, "ENDPOINT [%02d] %20s %02d %02d", ++ i, interfaces_list[i], interface_function->function_driver->endpointsRequested, endpoints); ++ ++ /* iterate across all interface descriptions */ ++ for (j = 0; j < bNumInterfaces; j++) { ++ struct usbd_alternate_instance *alternate_instance = interface_description[j]; ++ ++ /* iterate across all alternates for this interface */ ++ for (k = 0; k < interface_description[j].alternates; k++) { ++ ++ ++ ++ } ++ } ++ ++ } ++#endif ++ function->endpoints = endpoints; ++ TRACE_MSG2(USBD, "endpoints: %d %d", endpoints, function->endpoints); ++ ++ ++ /* requeset the endpoints from the bus interface driver ++ */ ++ THROW_UNLESS((requestedEndpoints = (struct usbd_endpoint_request *) ++ CKMALLOC (sizeof(struct usbd_endpoint_request) * function->endpoints, GFP_KERNEL)), error); ++ ++ function->requestedEndpoints = requestedEndpoints; ++#if 0 ++ /* we need to compose the endpoint request array ++ */ ++ for (endpoints = 0, i = 0; i < function->interfaces; i++) { ++ struct usbd_function_instance *interface_function = interfaces_array[i]; ++ struct usbd_interface_description *interface_description = interface_function->interface_list; ++ ++ for (j = 0; j < interface_function->endpoints; j++) { ++ memcpy(&requestedEndpoints[endpoints], &interface_function->requestedEndpoints[j], ++ sizeof(struct usbd_endpoint_request)); ++ requestedEndpoints[endpoints].interface_num = i; ++ } ++ } ++#endif ++ ++ ++ /* Insert into function list ++ */ ++ LIST_ADD_TAIL (&function->instances, &usbd_function_instances); ++ return function; ++ ++ CATCH(error) { ++ if (requestedEndpoints) LKFREE(requestedEndpoints); ++ if (interfaces_array) LKFREE(interfaces_array); ++ if (function) usbd_deregister_function(function); ++ return NULL; ++ } ++} ++ ++/*! ++ * usbd_deregister_function() - de-register a usbd function driver instance ++ * ++ * USBD Function drivers call this to de-register with the USBD Core layer. ++ * @param function_instance ++ */ ++void usbd_deregister_function (struct usbd_function_instance *function_instance) ++{ ++ RETURN_UNLESS (function_instance); ++ LIST_DEL (function_instance->instances); ++ if (function_instance->name) LKFREE(function_instance->name); ++ if (function_instance->endpoint_map_array) LKFREE(function_instance->endpoint_map_array); ++ LKFREE(function_instance); ++} ++ ++/*! ++ * usbd_find_function_internal() - register a usbd function driver ++ * ++ * USBD Function drivers call this to register with the USBD Core layer. ++ * Return NULL on failure. ++ * ++ * @param name ++ * @return function instance ++ * ++ */ ++static struct usbd_function_instance * ++usbd_find_function_internal(LIST_NODE *usbd_function_instances, char *name) ++{ ++ int len = name ? strlen(name) : 0; ++ LIST_NODE *lhd = NULL; ++ TRACE_MSG1(USBD, "%s", name); ++ LIST_FOR_EACH (lhd, usbd_function_instances) { ++ ++ struct usbd_function_instance *function = LIST_ENTRY (lhd, struct usbd_function_instance, instances); ++ struct usbd_function_driver *function_driver; ++ function_driver = function->function_driver; ++ printk(KERN_ERR"%s: name: %s len: %d function: %s\n", __FUNCTION__, name ? name : "", len, function_driver->name); ++ CONTINUE_IF(len || strncmp(function_driver->name, name, len)); ++ ++ TRACE_MSG5(USBD, "%s %s %d %s %d", name, function->name, len, ++ function->function_driver->name, ++ function->function_driver->endpointsRequested); ++ CONTINUE_IF(len && strncmp(function->name, name, len)); ++ ++ TRACE_MSG4(USBD, "FOUND %s %x %s %d", name, function, ++ function->function_driver->name, ++ function->function_driver->endpointsRequested); ++ ++ return function; ++ ++ //struct usbd_function_driver *function_driver = function->function_driver; ++ //printk(KERN_ERR"%s: name: %s len: %d function: %s\n", __FUNCTION__,name?name : "", len, function_driver->name); ++ //CONTINUE_IF(len || strncmp(function_driver->name, name, len)); ++ } ++ return NULL; ++} ++ ++ ++/*! ++ * usbd_find_function() - register a usbd function driver ++ * ++ * USBD Function drivers call this to register with the USBD Core layer. ++ * Return NULL on failure. ++ * ++ * @param name ++ * @return function instance ++ * ++ */ ++struct usbd_function_instance * ++usbd_find_function (char *name) ++{ ++ return usbd_find_function_internal(&usbd_function_instances, name); ++} ++ ++/*! ++ * usbd_default_function() - register a usbd function driver ++ * ++ * USBD Function drivers call this to register with the USBD Core layer. ++ * Return NULL on failure. ++ * ++ * @param name ++ * @return function instance ++ * ++ */ ++struct usbd_function_instance * ++usbd_default_function (char *name) ++{ ++ return usbd_find_function_internal(&usbd_function_instances, ""); ++} ++ ++/*! ++ * usbd_find_interface() - register a usbd function driver ++ * ++ * USBD Function drivers call this to register with the USBD Core layer. ++ * Return NULL on failure. ++ * ++ * @param name ++ * @return function instance ++ * ++ */ ++struct usbd_function_instance *usbd_find_interface(char *name) ++{ ++ TRACE_MSG1(USBD, "%s", name); ++ return usbd_find_function_internal(&usbd_interface_instances, name); ++} ++ ++ ++/*! ++ * usbd_function_name() - return name of nth function driver ++ * @param n index to function name ++ */ ++char * usbd_function_name(int n) ++{ ++ struct usbd_function_instance *function = NULL; ++ LIST_NODE *lhd = NULL; ++ ++ LIST_FOR_EACH (lhd, &usbd_function_instances) { ++ struct usbd_function_driver *function_driver; ++ function = LIST_ENTRY (lhd, struct usbd_function_instance, instances); ++ function_driver = function->function_driver; ++ ++ TRACE_MSG2(USBD, "name: [%d] %s", n, function_driver->name); ++ ++ CONTINUE_IF(n--); ++ return (char *)function_driver->name; ++ }; ++ return NULL; ++} ++ ++OTG_EXPORT_SYMBOL(usbd_register_function); ++OTG_EXPORT_SYMBOL(usbd_register_interface); ++OTG_EXPORT_SYMBOL(usbd_register_composite); ++OTG_EXPORT_SYMBOL(usbd_deregister_function); ++ ++/* ********************************************************************************************* */ ++/* ********************************************************************************************* */ ++/*! ++ * String Descriptors: ++ * usbd_alloc_string_descriptor() ++ * usbd_free_string_descriptor() ++ * usbd_get_string_descriptor() ++ */ ++ ++#define LANGID_ENGLISH "\011" ++#define LANGID_US_ENGLISH "\004" ++#define LANGIDs LANGID_US_ENGLISH LANGID_ENGLISH ++ ++/*! ++ * usbd_alloc_string_zero() - allocate a string descriptor and return index number ++ * ++ * Find an empty slot in index string array, create a corresponding descriptor ++ * and return the slot number. ++ * ++ * @param str string to allocate ++ * @return the slot number ++ */ ++static u8 usbd_alloc_string_zero (char *str) ++{ ++ u8 bLength; ++ u16 *wData; ++ struct usbd_string_descriptor *string = NULL; ++ ++ RETURN_ZERO_IF(usb_strings[0] != NULL); ++ ++ bLength = sizeof (struct usbd_string_descriptor) + strlen (str); ++ ++ RETURN_ZERO_IF(!(string = (struct usbd_string_descriptor *) CKMALLOC (bLength, GFP_KERNEL))); ++ ++ string->bLength = bLength; // set descriptor length ++ string->bDescriptorType = USB_DT_STRING; // set descriptor type ++ ++ for (wData = string->wData; *str; str += 2, wData++) // copy ++ *wData = (u16) ((str[0] << 8 | str[1])); ++ ++ usb_strings[0] = string; // save in string index array ++ return 0; ++} ++ ++int usbd_maxstrings = 40; ++ ++/*! ++ * usbd_strings_init() - initialize usb strings pool ++ */ ++int usbd_strings_init (void) ++{ ++ //TRACE_MSG0(USBD, "entered"); ++ RETURN_ZERO_IF(usb_strings); ++ usbd_maxstrings = MIN(usbd_maxstrings, 254); ++ ++ RETURN_EINVAL_IF (!(usb_strings = CKMALLOC (sizeof (struct usbd_string_descriptor *) * usbd_maxstrings, GFP_KERNEL))); ++ if (usbd_alloc_string_zero (LANGIDs) != 0) { ++ LKFREE (usb_strings); ++ return -1; ++ } ++ return 0; ++} ++ ++/*! ++ * usbd_strings_exit() - de-initialize usb strings pool ++ */ ++void usbd_strings_exit(void) ++{ ++ int i; ++ //TRACE_MSG0(USBD, "entered"); ++ RETURN_IF (!usb_strings); ++ for (i = 0; i < usbd_maxstrings; i++) ++ usbd_free_string_descriptor((u8)i); ++ LKFREE (usb_strings); ++ usb_strings = NULL; ++} ++ ++ ++/*! ++ * usbd_get_string_descriptor() - find and return a string descriptor ++ * ++ * Find an indexed string and return a pointer to a it. ++ * @param index of string ++ * @return pointer to string descriptor or NULL ++ */ ++struct usbd_string_descriptor *usbd_get_string_descriptor (u8 index) ++{ ++ RETURN_NULL_IF (index >= usbd_maxstrings); ++ return usb_strings[index]; ++} ++ ++/*! ++ * usbd_realloc_string() - allocate a string descriptor and return index number ++ * ++ * Find an empty slot in index string array, create a corresponding descriptor ++ * and return the slot number. ++ * @param index ++ * @param str ++ * @return new index ++ */ ++u8 usbd_realloc_string (u8 index, char *str) ++{ ++ struct usbd_string_descriptor *string; ++ u8 bLength; ++ u16 *wData; ++ ++ RETURN_ZERO_IF(!str || !strlen (str)); ++ //TRACE_MSG2(USBD, "index: %d str: %s", index, str); ++ ++ // XXX should have semaphores... ++ ++ if ((index < usbd_maxstrings) && (string = usb_strings[index])) { ++ usb_strings[index] = NULL; ++ LKFREE (string); ++ } ++ ++ bLength = sizeof (struct usbd_string_descriptor) + 2 * strlen (str); ++ ++ RETURN_ZERO_IF(!(string = (struct usbd_string_descriptor *)CKMALLOC (bLength, GFP_KERNEL))); ++ ++ string->bLength = bLength; ++ string->bDescriptorType = USB_DT_STRING; ++ ++ for (wData = string->wData; *str;) ++ *wData++ = (u16) (*str++); ++ ++ // store in string index array ++ usb_strings[index] = string; ++ return index; ++} ++ ++/*! ++ * usbd_alloc_string() - allocate a string descriptor and return index number ++ * ++ * Find an empty slot in index string array, create a corresponding descriptor ++ * and return the slot number. ++ * @param str ++ * @return index ++ * ++ * XXX rename to usbd_alloc_string_descriptor ++ */ ++u8 usbd_alloc_string (char *str) ++{ ++ int i; ++ struct usbd_string_descriptor *string = NULL; ++ u8 bLength; ++ u16 *wData; ++ ++ RETURN_ZERO_IF(!str || !strlen (str)); ++ ++ // find an empty string descriptor slot ++ for (i = 1; i < usbd_maxstrings; i++) { ++ ++ CONTINUE_IF (usb_strings[i] != NULL); ++ ++ bLength = sizeof (struct usbd_string_descriptor) + 2 * strlen (str); ++ ++ RETURN_ZERO_IF(!(string = (struct usbd_string_descriptor *)CKMALLOC (bLength, GFP_KERNEL))); ++ ++ 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; ++ } ++ return 0; ++} ++ ++ ++/*! ++ * usbd_free_string_descriptor() - deallocate a string descriptor ++ * ++ * Find and remove an allocated string. ++ * @param index ++ * ++ */ ++void usbd_free_string_descriptor (u8 index) ++{ ++ struct usbd_string_descriptor *string; ++ ++ if ((index < usbd_maxstrings) && (string = usb_strings[index])) { ++ usb_strings[index] = NULL; ++ LKFREE (string); ++ } ++} ++ ++OTG_EXPORT_SYMBOL(usbd_realloc_string); ++OTG_EXPORT_SYMBOL(usbd_alloc_string); ++OTG_EXPORT_SYMBOL(usbd_free_string_descriptor); ++OTG_EXPORT_SYMBOL(usbd_get_string_descriptor); ++OTG_EXPORT_SYMBOL(usbd_maxstrings); ++ ++ ++/* ********************************************************************************************* */ ++/* ********************************************************************************************* */ ++/*! ++ * Device Information: ++ * usbd_high_speed() ++ * usbd_get_bMaxPower() ++ * usbd_endpoint_wMaxPacketsize() ++ * usbd_endpoint_wMaxPacketsize_ep0() ++ * usbd_endpoint_bEndpointAddress() ++ * ++ */ ++ ++ ++/*! ++ * usbd_high_speed() - return high speed status ++ * @return true if operating at high speed ++ */ ++int usbd_high_speed(struct usbd_function_instance *function) ++{ ++ // XXX TODO - per function modifications for composite devices ++ return function->bus->HighSpeedFlag ? 1 : 0; ++} ++ ++/*! ++ * usbd_get_bMaxPower() - process a received urb ++ * ++ * Used by a USB Bus interface driver to pass received device request to ++ * the appropriate USB Function driver. ++ * ++ * @param function ++ * @param request ++ * @return non-zero if errror ++ */ ++int usbd_get_bMaxPower(struct usbd_function_instance *function) ++{ ++ struct usbd_bus_instance *bus = function->bus; ++ return bus->driver->bMaxPower; ++} ++ ++ ++/*! ++ * usbd_endpoint_wMaxPacketSize() - get maximum packet size for endpoint ++ * @param function ++ * @param endpoint_index ++ * @param hs highspeed flag ++ * @return endpoint size ++ */ ++int usbd_endpoint_wMaxPacketSize(struct usbd_function_instance *function, int endpoint_index, int hs) ++{ ++ struct usbd_endpoint_map *endpoint_map; ++ RETURN_ZERO_IF(!(endpoint_map = function->endpoint_map_array)); ++ return le16_to_cpu(endpoint_map[endpoint_index].wMaxPacketSize[hs]); ++} ++ ++/*! ++ * usbd_endpoint_zero_wMaxPacketSize() - get maximum packet size for endpoint zero ++ * @param function ++ * @param hs highspeed flag ++ * @return endpoint size ++ */ ++int usbd_endpoint_zero_wMaxPacketSize(struct usbd_function_instance *function, int hs) ++{ ++ struct usbd_endpoint_map *endpoint_map; ++ RETURN_ZERO_IF(!(endpoint_map = function->bus->ep0->endpoint_map_array)); ++ return le16_to_cpu(endpoint_map[0].wMaxPacketSize[hs]); ++} ++ ++/*! ++ * usbd_endpoint_bEndpointAddress() - get endpoint addrsess ++ * @param function ++ * @param endpoint_index ++ * @param hs high speed flag ++ * @return endpoint address ++ */ ++int usbd_endpoint_bEndpointAddress(struct usbd_function_instance *function, int endpoint_index, int hs) ++{ ++ struct usbd_endpoint_map *endpoint_map; ++ RETURN_ZERO_IF(!(endpoint_map = function->endpoint_map_array)); ++ return endpoint_map[endpoint_index].bEndpointAddress[hs]; ++} ++ ++OTG_EXPORT_SYMBOL(usbd_endpoint_wMaxPacketSize); ++OTG_EXPORT_SYMBOL(usbd_endpoint_zero_wMaxPacketSize); ++OTG_EXPORT_SYMBOL(usbd_endpoint_bEndpointAddress); ++OTG_EXPORT_SYMBOL(usbd_high_speed); ++ ++/* ********************************************************************************************* */ ++/* ********************************************************************************************* */ ++/*! ++ * Device Control: ++ * ++ * XXX usbd_request_endpoints() ++ * XXX usbd_configure_endpoints() ++ * ++ * usbd_get_device_state() ++ * usbd_get_device_status() ++ * usbd_framenum() ++ * usbd_ticks() ++ * usbd_elapsed() ++ */ ++ ++/*! ++ * usbd_flush_endpoint_irq() - flush urbs from endpoint ++ * ++ * Iterate across the approrpiate tx or rcv list and cancel any outstanding urbs. ++ * ++ * @param endpoint flush urbs on this endpoint ++ */ ++void usbd_flush_endpoint_irq (struct usbd_endpoint_instance *endpoint) ++{ ++ struct usbd_urb *urb; ++ ++ if (endpoint->bEndpointAddress) ++ TRACE_MSG1(USBD, "bEndpointAddress: %02x", endpoint->bEndpointAddress); ++ ++ if ((urb = endpoint->tx_urb)) ++ usbd_cancel_urb(urb); ++ ++ for (; (urb = usbd_first_urb_detached_irq (&endpoint->tx)); usbd_cancel_urb(urb)); ++ ++ if ((urb = endpoint->rcv_urb)) ++ usbd_cancel_urb(urb); ++ ++ for (; (urb = usbd_first_urb_detached_irq (&endpoint->rdy)); usbd_cancel_urb(urb)); ++} ++ ++/*! ++ * usbd_flush_endpoint() - flush urbs from endpoint ++ * ++ * Iterate across the approrpiate tx or rcv list and cancel any outstanding urbs. ++ * ++ * @param endpoint flush urbs on this endpoint ++ */ ++void usbd_flush_endpoint (struct usbd_endpoint_instance *endpoint) ++{ ++ unsigned long flags; ++ local_irq_save (flags); ++ usbd_flush_endpoint_irq(endpoint); ++ local_irq_restore (flags); ++} ++ ++/*! ++ * usbd_flush_endpoint_address() - flush endpoint ++ * @param function ++ * @param endpoint_index ++ */ ++void usbd_flush_endpoint_index (struct usbd_function_instance *function, int endpoint_index) ++{ ++ struct usbd_endpoint_map *endpoint_map; ++ struct usbd_endpoint_instance *endpoint; ++ ++ RETURN_IF(!function); ++ RETURN_IF(!(endpoint_map = function->endpoint_map_array)); ++ RETURN_IF(!(endpoint = endpoint_map[endpoint_index].endpoint)); ++ usbd_flush_endpoint (endpoint); ++} ++ ++/*! ++ * usbd_get_device_state() - return device state ++ * @param function ++ * @return current device state ++ */ ++usbd_device_state_t usbd_get_device_state(struct usbd_function_instance *function) ++{ ++ //TRACE_MSG1(USBD, "%d", function->bus->device_state); ++ return (function && function->bus) ? function->bus->device_state : STATE_UNKNOWN; ++} ++ ++/*! ++ * usbd_get_device_status() - return device status ++ * @param function ++ * @return current device status ++ */ ++usbd_device_status_t usbd_get_device_status(struct usbd_function_instance *function) ++{ ++ //TRACE_MSG1(USBD, "%d", function->bus->status); ++ return (function && function->bus) ? function->bus->status : USBD_UNKNOWN; ++} ++ ++/*! ++ * usbd_framenum() - return framenum ++ * @param function ++ * @return current framenum ++ */ ++int usbd_framenum(struct usbd_function_instance * function) ++{ ++ RETURN_ZERO_UNLESS(function); ++ RETURN_ZERO_UNLESS(function->bus->driver->bops->framenum); ++ return function->bus->driver->bops->framenum (); ++} ++ ++/*! ++ * usbd_ticks() - return ticks ++ * @param function ++ * @return current ticks ++ */ ++u64 usbd_ticks(struct usbd_function_instance *function) ++{ ++ RETURN_ZERO_UNLESS(function); ++ RETURN_ZERO_UNLESS(function->bus->driver->bops->ticks); ++ return function->bus->driver->bops->ticks (); ++} ++ ++/*! ++ * usbd_elapsed() - return elapsed ++ * @param function ++ * @param t1 ++ * @param t2 ++ * @return elapsed uSecs between t1 and t2 ++ */ ++u64 usbd_elapsed(struct usbd_function_instance *function, u64 *t1, u64 *t2) ++{ ++ RETURN_ZERO_UNLESS(function); ++ RETURN_ZERO_UNLESS(function->bus->driver->bops->elapsed); ++ return function->bus->driver->bops->elapsed (t1, t2); ++} ++ ++ ++//OTG_EXPORT_SYMBOL(usbd_set_endpoint_halt); ++//OTG_EXPORT_SYMBOL(usbd_endpoint_halted); ++OTG_EXPORT_SYMBOL(usbd_get_device_state); ++OTG_EXPORT_SYMBOL(usbd_get_device_status); ++OTG_EXPORT_SYMBOL(usbd_framenum); ++OTG_EXPORT_SYMBOL(usbd_ticks); ++OTG_EXPORT_SYMBOL(usbd_elapsed); ++OTG_EXPORT_SYMBOL(usbd_flush_endpoint); ++OTG_EXPORT_SYMBOL(usbd_flush_endpoint_index); ++ ++/* ********************************************************************************************* */ ++/* ********************************************************************************************* */ ++/*! ++ * Endpoint I/O: ++ * usbd_alloc_urb() ++ * usbd_start_in_urb() ++ * usbd_start_out_urb() ++ * usbd_cancel_urb() ++ * ++ */ ++ ++/*! ++ * usbd_alloc_urb() - allocate an URB appropriate for specified endpoint ++ * ++ * 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. ++ * ++ * @param function ++ * @param endpoint_index ++ * @param length ++ * @param callback ++ * @return urb ++ */ ++struct usbd_urb *usbd_alloc_urb (struct usbd_function_instance *function, int endpoint_index, ++ int length, int (*callback) (struct usbd_urb *, int)) ++{ ++ struct usbd_urb *urb = NULL; ++ struct usbd_endpoint_map *endpoint_map; ++ struct usbd_endpoint_instance *endpoint; ++ unsigned long flags; ++ ++ RETURN_NULL_IF(!(endpoint_map = function->endpoint_map_array)); ++ RETURN_NULL_IF(!(endpoint = endpoint_map[endpoint_index].endpoint)); ++ ++ local_irq_save(flags); ++ THROW_IF (!(urb = (struct usbd_urb *)CKMALLOC (sizeof (struct usbd_urb), GFP_ATOMIC)), error); ++ urb->endpoint = endpoint; ++ urb->bus = function->bus; ++ urb->function_instance = function; ++ urb->link.prev = urb->link.next = &urb->link; ++ urb->callback = callback; ++ urb->buffer_length = urb->actual_length = 0; ++ ++ if (length) { ++ urb->request_length = length; ++ ++ /* For receive we always overallocate to ensure that receiving another ++ * full sized packet when we are only expecting a short packet will ++ * not overflow the buffer ++ */ ++ if (!endpoint->bEndpointAddress || endpoint->bEndpointAddress & USB_ENDPOINT_DIR_MASK) { ++ length = ((length / endpoint->wMaxPacketSize) + 1) * endpoint->wMaxPacketSize; ++ } ++ urb->buffer_length = length; ++ ++ #if 0 ++ if (urb->endpoint && urb->endpoint->bEndpointAddress && urb->function_instance && ++ urb->function_instance->function_driver->fops->alloc_urb_data) ++ { ++ THROW_IF(urb->function_instance->function_driver->fops->alloc_urb_data (urb, length), error); ++ } ++ else ++ #endif ++ THROW_IF(!(urb->buffer = (u8 *)CKMALLOC (length, GFP_ATOMIC)), error); ++ ++ } ++ ++ CATCH(error) { ++ #if defined(OTG_LINUX) ++ printk(KERN_ERR"%s: dealloc %p\n", __FUNCTION__, urb); ++ #endif /* defined(OTG_LINUX) */ ++ #if defined(OTG_WINCE) ++ DEBUGMSG(ZONE_INIT, (_T("usbd_alloc_urb: FAILED\n"))); ++ #endif /* defined(OTG_LINUX) */ ++ usbd_free_urb(urb); ++ urb = NULL; ++ } ++ local_irq_restore(flags); ++ return urb; ++} ++ ++/*! ++ * usbd_halt_endpoint() - ++ * ++ * @param function function that owns endpoint ++ * @param endpoint endpoint number ++ * @return non-zero if endpoint is halted. ++ */ ++int usbd_halt_endpoint (struct usbd_function_instance *function, int endpoint_index) ++{ ++ struct usbd_endpoint_map *endpoint_map; ++ struct usbd_endpoint_instance *endpoint; ++ ++ RETURN_ZERO_IF(!(endpoint_map = function->endpoint_map_array)); ++ RETURN_ZERO_IF(!(endpoint = endpoint_map[endpoint_index].endpoint)); ++ ++ return function->bus->driver->bops->halt_endpoint (function->bus, endpoint->bEndpointAddress, endpoint_index, 1); ++} ++ ++ ++/*! ++ * usbd_alloc_urb_ep0() - allocate an urb for endpoint zero ++ * @param function ++ * @param length ++ * @param callback ++ * @return urb ++ */ ++struct usbd_urb *usbd_alloc_urb_ep0 (struct usbd_function_instance *function, int length, ++ int (*callback) (struct usbd_urb *, int)) ++{ ++ return usbd_alloc_urb(function->bus->ep0, 0, length, callback); ++} ++ ++/*! ++ * usbd_free_urb() - deallocate an URB and associated buffer ++ * ++ * Deallocate an urb structure and associated data. ++ * @param urb ++ */ ++void usbd_free_urb (struct usbd_urb *urb) ++{ ++ RETURN_IF (!urb); ++ if (urb->buffer) { ++ #if 0 ++ if (urb->function_instance && urb->function_instance->function_driver->fops->dealloc_urb_data) ++ urb->function_instance->function_driver->fops->dealloc_urb_data ((void *) urb->buffer); ++ else ++ #endif ++ LKFREE ((void *) urb->buffer); ++ } ++ LKFREE (urb); ++} ++ ++ ++/*! ++ * usbd_start_in_urb() - submit a urb to send ++ * ++ * 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. ++ * ++ * @param urb to send ++ * @return non-zero if error ++ */ ++int usbd_start_in_urb (struct usbd_urb *urb) ++{ ++ RETURN_EINVAL_IF (urb->endpoint->bEndpointAddress && (USBD_OK != urb->bus->status)); ++ if (urb->endpoint->feature_setting & FEATURE(USB_ENDPOINT_HALT)) { ++ usbd_urb_finished(urb, SEND_STALLED); ++ return 0; ++ } ++ urb->status = SEND_IN_QUEUE; ++ #if defined(OTG_LINUX) ++ urb->jiffies = jiffies; ++ #endif /* defined(OTG_LINUX) */ ++ urb_append (&(urb->endpoint->tx), urb); ++ return urb->bus->driver->bops->start_endpoint_in(urb->bus, urb->endpoint); ++} ++ ++/*! ++ * usbd_start_out_urb() - recycle a received urb ++ * ++ * Used by a USB Function interface driver to recycle an urb. ++ * ++ * @param urb to process ++ * @return non-zero if error ++ */ ++int usbd_start_out_urb (struct usbd_urb *urb) ++{ ++ RETURN_EINVAL_IF (urb->endpoint->bEndpointAddress && (USBD_OK != urb->bus->status)); ++ if (urb->endpoint->feature_setting & FEATURE(USB_ENDPOINT_HALT)) { ++ usbd_urb_finished(urb, RECV_STALLED); ++ return 0; ++ } ++ urb->actual_length = 0; ++ urb->status = RECV_IN_QUEUE; ++ #if defined(OTG_LINUX) ++ urb->jiffies = jiffies; ++ #endif /* defined(OTG_LINUX) */ ++ urb_append (&(urb->endpoint->rdy), urb); ++ return urb->bus->driver->bops->start_endpoint_out(urb->bus, urb->endpoint); ++} ++ ++/*! ++ * usbd_cancel_urb() - cancel an urb being sent ++ * ++ * @param urb to process ++ * @return non-zero if error ++ */ ++int usbd_cancel_urb (struct usbd_urb *urb) ++{ ++ //TRACE_MSG0(USBD, "entered"); ++ return urb->bus->driver->bops->cancel_urb_irq (urb); ++} ++ ++ ++/*! ++ * usbd_endpoint_halted() - return halt status ++ * ++ * @param function function that owns endpoint ++ * @param endpoint endpoint number ++ * @return non-zero if endpoint is halted. ++ */ ++int usbd_endpoint_halted (struct usbd_function_instance *function, int endpoint_index) ++{ ++ struct usbd_endpoint_instance *endpoint; ++ struct usbd_endpoint_map *endpoint_map; ++ //TRACE_MSG0(USBD, "entered"); ++ // ++ RETURN_ZERO_UNLESS((endpoint_map = function->endpoint_map_array)); ++ RETURN_ZERO_UNLESS((endpoint = endpoint_map[endpoint_index].endpoint)); ++ return function->bus->driver->bops->endpoint_halted (function->bus, endpoint->bEndpointAddress, endpoint_index); ++} ++ ++ ++OTG_EXPORT_SYMBOL(usbd_alloc_urb); ++OTG_EXPORT_SYMBOL(usbd_alloc_urb_ep0); ++OTG_EXPORT_SYMBOL(usbd_free_urb); ++ ++OTG_EXPORT_SYMBOL(usbd_start_in_urb); ++OTG_EXPORT_SYMBOL(usbd_start_out_urb); ++OTG_EXPORT_SYMBOL(usbd_cancel_urb); ++OTG_EXPORT_SYMBOL(usbd_halt_endpoint); ++ ++/* ********************************************************************************************* */ ++ ++/*! ++ * usbd_function_get_privdata() - get private data pointer ++ * @param function ++ * @return void * pointer to private data ++ */ ++void *usbd_function_get_privdata(struct usbd_function_instance *function) ++{ ++ return(function->privdata); ++} ++ ++/*! ++ * usbd_function_set_privdata() - set private data structure in function ++ * @param function ++ * @param privdata ++ */ ++void usbd_function_set_privdata(struct usbd_function_instance *function, void *privdata) ++{ ++ function->privdata = privdata; ++} ++ ++ ++ ++ ++/*! ++ * usbd_endpoint_transferSize() - get transferSize for endpoint ++ * @param function ++ * @param endpoint_index ++ * @param hs highspeed flag ++ * @return transfer size ++ */ ++int usbd_endpoint_transferSize(struct usbd_function_instance *function, int endpoint_index, int hs) ++{ ++ struct usbd_endpoint_map *endpoint_map; ++ RETURN_ZERO_IF(!(endpoint_map = function->endpoint_map_array)); ++ return endpoint_map[endpoint_index].transferSize[hs]; ++} ++ ++/*! ++ * usbd_endpoint_update() - update endpoint address and size ++ * @param function ++ * @param endpoint_index ++ * @param endpoint descriptor ++ * @param hs high speed flag ++ */ ++void usbd_endpoint_update(struct usbd_function_instance *function, int endpoint_index, ++ struct usbd_endpoint_descriptor *endpoint, int hs) ++{ ++ endpoint->bEndpointAddress = usbd_endpoint_bEndpointAddress(function, endpoint_index, hs); ++ endpoint->wMaxPacketSize = usbd_endpoint_wMaxPacketSize(function, endpoint_index, hs); ++} ++ ++/*! ++ * usbd_otg_bmattributes() - return attributes ++ * @param function ++ * @return endpoint attributes ++ */ ++int usbd_otg_bmattributes(struct usbd_function_instance *function) ++{ ++ // XXX TODO - per function modifications for composite devices ++ return function->bus->otg_bmAttributes; ++} ++ ++OTG_EXPORT_SYMBOL(usbd_function_get_privdata); ++OTG_EXPORT_SYMBOL(usbd_function_set_privdata); ++OTG_EXPORT_SYMBOL(usbd_endpoint_transferSize); ++OTG_EXPORT_SYMBOL(usbd_otg_bmattributes); ++OTG_EXPORT_SYMBOL(usbd_endpoint_update); +diff -uNr linux/drivers/no-otg/otgcore/usbp-procfs.c linux/drivers/otg/otgcore/usbp-procfs.c +--- linux/drivers/no-otg/otgcore/usbp-procfs.c 1970-01-01 01:00:00.000000000 +0100 ++++ linux/drivers/otg/otgcore/usbp-procfs.c 2006-09-01 21:41:35.000000000 +0200 +@@ -0,0 +1,302 @@ ++/* ++ * otg/otgcore/usbp-procfs.c - USB Device Core Layer ++ * ++ * Copyright (c) 2004 Belcarra ++ * ++ * By: ++ * Stuart Lynne <sl@belcarra.com>, ++ * Tom Rushworth <tbr@belcarra.com>, ++ * Bruce Balden <balden@belcarra.com> ++ * ++ */ ++/*! ++ * @file otg/otgcore/usbp-procfs.c ++ * @brief Implements /proc/usbd-functions, which displays descriptors for the current selected function. ++ * ++ * ++ * @ingroup USBP ++ */ ++ ++#include <otg/otg-compat.h> ++#include <otg/otg-module.h> ++ ++#ifdef LINUX24 ++EXPORT_NO_SYMBOLS; ++#endif ++ ++ ++MOD_AUTHOR ("sl@belcarra.com, tbr@belcarra.com"); ++MOD_DESCRIPTION ("USB Device Core Support Procfs"); ++EMBED_LICENSE(); ++ ++#include <otg/usbp-chap9.h> ++#include <otg/usbp-func.h> ++#include <otg/usbp-bus.h> ++ ++EMBED_USBD_INFO ("usbdprocfs 2.0-beta"); ++ ++#define MAX_INTERFACES 2 ++ ++extern struct usbd_bus_instance *usbd_bus_instance; ++ ++/* 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; ++ ++ RETURN_ZERO_UNLESS(sp); ++ ++ num = *sp; ++ ++ //printk(KERN_INFO"%s: %p %d %d %d\n", __FUNCTION__, buf, *buf, buf[0], num); ++ ++ while (sp && num--) { ++ dohexval (buf, *sp++); ++ buf += 2; ++ *buf++ = ' '; ++ len += 3; ++ } ++ len++; ++ *buf = '\n'; ++ return len; ++} ++ ++/* dump_descriptors ++ */ ++static int dump_config_descriptor(char *buf, char *sp) ++{ ++ struct usbd_configuration_descriptor *config = (struct usbd_configuration_descriptor *) sp; ++ ++ int wTotalLength = le16_to_cpu(config->wTotalLength); ++ int bConfigurationValue = config->bConfigurationValue; ++ int interface_num; ++ int class_num; ++ int endpoint_num; ++ int total; ++ ++ interface_num = class_num = endpoint_num = 0; ++ ++ for (total = 0; wTotalLength > 0; ) { ++ //printk(KERN_INFO"%s: wTotalLength: %d total: %d bLength: %d type: %d\n", ++ // __FUNCTION__, wTotalLength, total, sp[0], sp[1]); ++ BREAK_UNLESS(sp[0]); ++ switch (sp[1]) { ++ case USB_DT_CONFIGURATION: ++ interface_num = class_num = endpoint_num = 0; ++ total += sprintf(buf + total, "\nConfiguration descriptor [%d ] ", ++ bConfigurationValue); ++ break; ++ case USB_DT_INTERFACE: ++ class_num = 0; ++ total += sprintf(buf + total, "\nInterface descriptor [%d:%d:%d ] ", ++ bConfigurationValue, ++interface_num, class_num); ++ break; ++ case USB_DT_ENDPOINT: ++ class_num = endpoint_num = 0; ++ total += sprintf(buf + total, "Endpoint descriptor [%d:%d:%d:%d] ", ++ bConfigurationValue, interface_num, class_num, ++endpoint_num); ++ break; ++ case USB_DT_OTG: ++ class_num = endpoint_num = 0; ++ total += sprintf(buf + total, "OTG descriptor [%d ] ", bConfigurationValue); ++ break; ++ default: ++ endpoint_num = 0; ++ total += sprintf(buf + total, "Class descriptor [%d:%d:%d ] ", ++ bConfigurationValue, interface_num, ++class_num); ++ break; ++ } ++ total += dump_descriptor(buf + total, sp); ++ wTotalLength -= sp[0]; ++ sp += sp[0]; ++ } ++ total += sprintf(buf + total, "\n"); ++ return total; ++} ++ ++/*! * ++ * usbd_device_proc_read - implement proc file system read. ++ * @param file ++ * @param buf ++ * @param count ++ * @param 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; ++ ++ u8 config_descriptor[512]; ++ int config_size; ++ ++ //struct list_head *lhd; ++ ++ // get a page, max 4095 bytes of data... ++// if (!(page = GET_FREE_PAGE(GFP_KERNEL))) { ++ ++ if (!(page = GET_KERNEL_PAGE())) { ++ return -ENOMEM; ++ } ++ ++ len = 0; ++ index = (*pos)++; ++ ++ if (index == 0) { ++ len += sprintf ((char *) page + len, "usb-device list\n"); ++ } ++ ++ //printk(KERN_INFO"%s: index: %d len: %d\n", __FUNCTION__, index, len); ++ ++ if (usbd_bus_instance && usbd_bus_instance->function_instance) { ++ int configuration = index; ++ struct usbd_function_instance *function_instance = usbd_bus_instance->function_instance; ++ struct usbd_function_driver *function_driver = function_instance->function_driver; ++ struct usbd_configuration_instance *configuration_instance_array = function_driver->configuration_instance_array; ++ if (configuration_instance_array) { ++ ++ if (index == 0) { ++ len += sprintf ((char *) page + len, "\nDevice descriptor "); ++ len += dump_descriptor ((char *) page + len, (char *) function_driver->device_descriptor); ++#ifdef CONFIG_OTG_HIGH_SPEED ++ len += sprintf ((char *) page + len, "\nDevice Qualifier descriptor "); ++ len += dump_descriptor ((char *) page + len, ++ (char *) function_driver->device_qualifier_descriptor); ++#endif ++ } ++ if (configuration < function_driver->bNumConfigurations) { ++ ++ if ((config_size = usbd_old_get_descriptor(usbd_bus_instance->ep0, config_descriptor, ++ sizeof(config_descriptor), ++ USB_DT_CONFIGURATION, 0)) > index) { ++ len += dump_config_descriptor((char *)page + len, config_descriptor ); ++ } ++#ifdef CONFIG_OTG_HIGH_SPEED ++ if ((config_size = usbd_old_get_descriptor(usbd_bus_instance->ep0, config_descriptor, ++ sizeof(config_descriptor), ++ USB_DT_OTHER_SPEED_CONFIGURATION, index)) > 0) { ++ len += dump_config_descriptor((char *)page + len, config_descriptor ); ++ } ++ ++#endif ++ } ++ else if (configuration == function_driver->bNumConfigurations) { ++ int i; ++ int k; ++ struct usbd_string_descriptor *string_descriptor; ++ ++ //len += sprintf ((char *) page + len, "\n\n"); ++ ++ if ((string_descriptor = usbd_get_string_descriptor (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 < usbd_maxstrings; i++) { ++ ++ if ((string_descriptor = usbd_get_string_descriptor (i)) != NULL) { ++ ++ len += sprintf((char *)page+len, "String [%2d:%2d] ", ++ i, string_descriptor->bLength); ++ ++ // bLength = sizeof(struct usbd_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"); ++ } ++ } ++ len += sprintf((char *)page + len, "\n--\n"); ++ } ++ } ++ } ++ ++ //printk(KERN_INFO"%s: len: %d count: %d\n", __FUNCTION__, len, count); ++ ++ if (len > count) { ++ //printk(KERN_INFO"%s: len > count\n", __FUNCTION__); ++ //printk(KERN_INFO"%s", page); ++ len = -EINVAL; ++ } ++ else if ((len > 0) && copy_to_user (buf, (char *) page, len)) { ++ //printk(KERN_INFO"%s: EFAULT\n", __FUNCTION__); ++ len = -EFAULT; ++ } ++ else { ++ //printk(KERN_INFO"%s: OK\n", __FUNCTION__); ++ } ++ free_page (page); ++ return len; ++} ++ ++/* Module init ******************************************************************************* */ ++#if defined(OTG_WINCE) ++ ++#else /* defined(OTG_WINCE) */ ++ ++static struct file_operations usbd_device_proc_operations_functions = { ++ read:usbd_device_proc_read_functions, ++}; ++ ++static int usbd_procfs_init (void) ++{ ++ 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; ++ return 0; ++} ++ ++static void usbd_procfs_exit (void) ++{ ++ // remove proc filesystem entry ++ remove_proc_entry ("usb-functions", NULL); ++} ++ ++module_init (usbd_procfs_init); ++module_exit (usbd_procfs_exit); ++ ++#endif /* defined(OTG_WINCE) */ ++ +diff -uNr linux/drivers/no-otg/otghw/atlas-hardware.h linux/drivers/otg/otghw/atlas-hardware.h +--- linux/drivers/no-otg/otghw/atlas-hardware.h 1970-01-01 01:00:00.000000000 +0100 ++++ linux/drivers/otg/otghw/atlas-hardware.h 2006-09-01 21:41:35.000000000 +0200 +@@ -0,0 +1,59 @@ ++/* ++ * otg/include/atlas-hardware.h -- Atlas Transceiver hardware specific defines ++ * ++ * Copyright (c) 2004 Belcarra ++ * ++ * By: ++ * Stuart Lynne <sl@lbelcarra.com>, ++ * Tom Rushworth <tbr@belcarra.com>, ++ * Bruce Balden <balden@belcarra.com> ++ * ++ */ ++/*! ++ * @defgroup ATLAS ++ * @ingroup tcdgroup ++ */ ++/*! ++ * @file otghw/atlas-hardware.h ++ * @brief Hardware defines for Freescale USBOTG Hardware ++ * ++ * @ingroup ATLAS ++ */ ++ ++/* References are to: ++ * ++ * Atlas DTS 0.7 - 04/02/27 ++ */ ++ ++/* ++ * SPI Register Summary ++ */ ++ ++#define ATLAS_FSENB (1 << 0) ++#define ATLAS_USB_SUSPEND (1 << 1) ++#define ATLAS_USB_PU (1 << 2) ++#define ATLAS_UDP_PD (1 << 3) ++#define ATLAS_UDM_PD (1 << 4) ++#define ATLAS_PULLOVR (1 << 5) ++#define ATLAS_VUSB_EN (1 << 6) ++#define ATLAS_USB_PS (1 << 7) ++#define ATLAS_VBUS_REG_EN (1 << 8) ++#define ATLAS_VBUS_PD_ENB (1 << 9) ++#define ATLAS_CURRLIM (1 << 10) ++#define ATLAS_SE0_CONN (1 << 11) ++#define ATLAS_DLP_SRP (1 << 12) ++#define ATLAS_USB_XCVR_EN (1 << 13) ++#define ATLAS_VBUS_REG_LVL (1 << 14) ++ ++#define ATLAS_DATA_MODE_MASK (3 << 15) ++#define ATLAS_DATA_MODE_USE (0 << 15) ++#define ATLAS_DATA_MODE_BSE (1 << 15) ++#define ATLAS_DATA_MODE_DM (2 << 15) ++#define ATLAS_DATA_MODE_NU (3 << 15) ++ ++#define ATLAS_RS232ENB (1 << 17) ++#define ATLAS_ID_INTERRUPT (1 << 20) ++#define ATLAS_AUDIO_EN (1 << 21) ++#define ATLAS_STEREO_EN (1 << 22) ++ ++ +diff -uNr linux/drivers/no-otg/otghw/bvd-hardware.h linux/drivers/otg/otghw/bvd-hardware.h +--- linux/drivers/no-otg/otghw/bvd-hardware.h 1970-01-01 01:00:00.000000000 +0100 ++++ linux/drivers/otg/otghw/bvd-hardware.h 2006-09-01 21:41:35.000000000 +0200 +@@ -0,0 +1,601 @@ ++/* ++ * otg/otghw/bvd-hardware.h ++ * ++ * Copyright (c) 2004 Belcarra ++ * ++ * ++ * TODO ++ * ++ * 1. see if using UDCCSR0_ACM/AREN will help ++ * ++ */ ++/*! ++ * @file otghw/bvd-hardware.h ++ * @brief Hardware defines for Intel PXA-270 Hardware ++ * ++ * @ingroup BVD ++ */ ++ ++ ++#ifndef UDCOTGICR ++#define UDCOTGICR __REG(0x40600018) /* UDC On-The-Go interrupt control */ ++#endif ++ ++#ifndef UDCOTGISR ++#define UDCOTGISR __REG(0x4060001c) ++ ++#define UDCOTGISR_IRSF (1 << 24) /* OTG SET_FEATURE command recvd */ ++#define UDCOTGISR_IRXR (1 << 17) /* Extra Transciever Interrupt Rising Edge Interrupt Enable */ ++#define UDCOTGISR_IRXF (1 << 16) /* Extra Transciever Interrupt Falling Edge Interrupt Enable */ ++#define UDCOTGISR_IRVV40R (1 << 9) /* OTG Vbus Valid 4.0V Rising Edge Interrupt Enable */ ++#define UDCOTGISR_IRVV40F (1 << 8) /* OTG Vbus Valid 4.0V Falling Edge Interrupt Enable */ ++#define UDCOTGISR_IRVV44R (1 << 7) /* OTG Vbus Valid 4.4V Rising Edge Interrupt Enable */ ++#define UDCOTGISR_IRVV44F (1 << 6) /* OTG Vbus Valid 4.4V Falling Edge Interrupt Enable */ ++#define UDCOTGISR_IRSVR (1 << 5) /* OTG Session Valid Rising Edge Interrupt Enable */ ++#define UDCOTGISR_IRSVF (1 << 4) /* OTG Session Valid Falling Edge Interrupt Enable */ ++#define UDCOTGISR_IRSDR (1 << 3) /* OTG A-Device SRP Detect Rising Edge Interrupt Enable */ ++#define UDCOTGISR_IRSDF (1 << 2) /* OTG A-Device SRP Detect Falling Edge Interrupt Enable */ ++#define UDCOTGISR_IRIDR (1 << 1) /* OTG ID Change Rising Edge Interrupt Enable */ ++#define UDCOTGISR_IRIDF (1 << 0) /* OTG ID Change Falling Edge */ ++ ++ ++#endif ++ ++ ++#ifndef UP2OCR ++#define UP2OCR __REG(0x40600020) ++ ++#define UP2OCR_SEOS_OFF (0x00 << 24) ++#define UP2OCR_SEOS_DSEO (0x02 << 24) ++#define UP2OCR_SEOS_HSEO (0x03 << 24) ++#define UP2OCR_SEOS_DEXT (0x04 << 24) ++#define UP2OCR_SEOS_HEXT (0x05 << 24) ++#define UP2OCR_SEOS_DINT (0x06 << 24) ++#define UP2OCR_SEOS_HINT (0x07 << 24) ++ ++#define UP2OCR_HXOE (0x1 << 17) ++#define UP2OCR_HXS (0x1 << 16) ++ ++#define UP2OCR_IDON (0x1 << 10) ++#define UP2OCR_EXSUS (0x1 << 9) ++#define UP2OCR_EXSP (0x1 << 8) ++#define UP2OCR_DMPUBE (0x1 << 7) ++#define UP2OCR_DPPUBE (0x1 << 6) ++#define UP2OCR_DMPUE (0x1 << 5) ++#define UP2OCR_DPPUE (0x1 << 4) ++#define UP2OCR_DMPDE (0x1 << 3) ++#define UP2OCR_DPPDE (0x1 << 2) ++#define UP2OCR_CPVPE (0x1 << 1) ++#define UP2OCR_CPVEN (0x1 << 0) ++ ++#endif ++ ++#ifndef UP3OCR ++#define UP3OCR __REG(0x40600024) ++#endif ++ ++/* ++ * UDCCSR0 Bit Definitions C.f. Sheet 1 or 3 ++ */ ++#define UDCCSR0_ACM (1 << 9) ++#define UDCCSR0_AREN (1 << 8) ++ ++ ++/* ++ * Mainstone Miscellanous Write Register (MSCWR2) C.f. 3.2.2.6 (DVK) ++ * ++ * USB_OTG_RST enable external usb otg transceiver ++ * USB_OTG_SEL enable usb otg (disables ffuart) ++ * nUSBC_SC enable usb client detection ++ */ ++#ifndef MSCWR2_USB_OTG_SEL ++#define MSCWR2_USB_OTG_SEL MST_MSCWR2_USB_OTG_SEL ++#endif ++#ifndef MSCWR2_USB_OTG_RST ++#define MSCWR2_USB_OTG_RST MST_MSCWR2_USB_OTG_RST ++#endif ++#ifndef MSCRD1_USB_CBL ++#define MSCRD1_USB_CBL MST_MSCRD_USB_CBL ++#endif ++#ifndef MSCWR2_nUSBC_SC ++#define MSCWR2_nUSBC_SC MST_MSCWR2_nUSBC_SC ++#endif ++ ++/* ++ * USB_P2_N GPIO configuration - C.f. 24.2 ++ * ++ * These should be in ++ */ ++#define GPIO34_USB_P2_2 (34 | GPIO_ALT_FN_1_OUT) ++#define GPIO35_USB_P2_1 (35 | GPIO_ALT_FN_2_IN) ++#define GPIO36_USB_P2_4 (36 | GPIO_ALT_FN_1_OUT) ++#define GPIO37_USB_P2_8 (37 | GPIO_ALT_FN_1_OUT) ++#define GPIO38_USB_P2_3 (38 | GPIO_ALT_FN_3_IN) ++#define GPIO39_USB_P2_6 (39 | GPIO_ALT_FN_1_OUT) ++#define GPIO40_USB_P2_5 (40 | GPIO_ALT_FN_3_IN) ++#define GPIO41_USB_P2_7 (41 | GPIO_ALT_FN_2_IN) ++ ++ ++#if 0 ++/* UDC endpiont0 states */ ++#define EP0_WAIT_FOR_SETUP 0 ++#define EP0_DATA_STATE_XMIT 1 ++#define EP0_DATA_STATE_NEED_ZLP 2 ++#define EP0_WAIT_FOR_OUT_STATUS 3 ++ ++/* UDC register definitions */ ++#define UDCCR __REG(0x40600000) /* UDC Control Register */ ++#define UDCICR0 __REG(0x40600004) /* UDC Interrupt Control Register0 */ ++#define UDCICR1 __REG(0x40600008) /* UDC Interrupt Control Register1 */ ++#define UDCISR0 __REG(0x4060000C) /* UDC Interrupt Status Register 0 */ ++#define UDCISR1 __REG(0x40600010) /* UDC Interrupt Status Register 1 */ ++#define UDCFNR __REG(0x40600014) /* UDC Frame Number Register */ ++ ++#define UDCCSR0 __REG(0x40600100) /* UDC Control/Status register - Endpoint 0 */ ++#define UDCCSRA __REG(0x40600104) /* UDC Control/Status register - Endpoint A */ ++#define UDCCSRB __REG(0x40600108) /* UDC Control/Status register - Endpoint B */ ++#define UDCCSRC __REG(0x4060010C) /* UDC Control/Status register - Endpoint C */ ++#define UDCCSRD __REG(0x40600110) /* UDC Control/Status register - Endpoint D */ ++#define UDCCSRE __REG(0x40600114) /* UDC Control/Status register - Endpoint E */ ++#define UDCCSRF __REG(0x40600118) /* UDC Control/Status register - Endpoint F */ ++#define UDCCSRG __REG(0x4060011C) /* UDC Control/Status register - Endpoint G */ ++#define UDCCSRH __REG(0x40600120) /* UDC Control/Status register - Endpoint H */ ++#define UDCCSRI __REG(0x40600124) /* UDC Control/Status register - Endpoint I */ ++#define UDCCSRJ __REG(0x40600128) /* UDC Control/Status register - Endpoint J */ ++#define UDCCSRK __REG(0x4060012C) /* UDC Control/Status register - Endpoint K */ ++#define UDCCSRL __REG(0x40600130) /* UDC Control/Status register - Endpoint L */ ++#define UDCCSRM __REG(0x40600134) /* UDC Control/Status register - Endpoint M */ ++#define UDCCSRN __REG(0x40600138) /* UDC Control/Status register - Endpoint N */ ++#define UDCCSRP __REG(0x4060013C) /* UDC Control/Status register - Endpoint P */ ++#define UDCCSRQ __REG(0x40600140) /* UDC Control/Status register - Endpoint Q */ ++#define UDCCSRR __REG(0x40600144) /* UDC Control/Status register - Endpoint R */ ++#define UDCCSRS __REG(0x40600148) /* UDC Control/Status register - Endpoint S */ ++#define UDCCSRT __REG(0x4060014C) /* UDC Control/Status register - Endpoint T */ ++#define UDCCSRU __REG(0x40600150) /* UDC Control/Status register - Endpoint U */ ++#define UDCCSRV __REG(0x40600154) /* UDC Control/Status register - Endpoint V */ ++#define UDCCSRW __REG(0x40600158) /* UDC Control/Status register - Endpoint W */ ++#define UDCCSRX __REG(0x4060015C) /* UDC Control/Status register - Endpoint X */ ++ ++#define UDCBCR0 __REG(0x40600200) /* Byte Count Register - EP0 */ ++#define UDCBCRA __REG(0x40600204) /* Byte Count Register - EPA */ ++#define UDCBCRB __REG(0x40600208) /* Byte Count Register - EPB */ ++#define UDCBCRC __REG(0x4060020C) /* Byte Count Register - EPC */ ++#define UDCBCRD __REG(0x40600210) /* Byte Count Register - EPD */ ++#define UDCBCRE __REG(0x40600214) /* Byte Count Register - EPE */ ++#define UDCBCRF __REG(0x40600218) /* Byte Count Register - EPF */ ++#define UDCBCRG __REG(0x4060021C) /* Byte Count Register - EPG */ ++#define UDCBCRH __REG(0x40600220) /* Byte Count Register - EPH */ ++#define UDCBCRI __REG(0x40600224) /* Byte Count Register - EPI */ ++#define UDCBCRJ __REG(0x40600228) /* Byte Count Register - EPJ */ ++#define UDCBCRK __REG(0x4060022C) /* Byte Count Register - EPK */ ++#define UDCBCRL __REG(0x40600230) /* Byte Count Register - EPL */ ++#define UDCBCRM __REG(0x40600234) /* Byte Count Register - EPM */ ++#define UDCBCRN __REG(0x40600238) /* Byte Count Register - EPN */ ++#define UDCBCRP __REG(0x4060023C) /* Byte Count Register - EPP */ ++#define UDCBCRQ __REG(0x40600240) /* Byte Count Register - EPQ */ ++#define UDCBCRR __REG(0x40600244) /* Byte Count Register - EPR */ ++#define UDCBCRS __REG(0x40600248) /* Byte Count Register - EPS */ ++#define UDCBCRT __REG(0x4060024C) /* Byte Count Register - EPT */ ++#define UDCBCRU __REG(0x40600250) /* Byte Count Register - EPU */ ++#define UDCBCRV __REG(0x40600254) /* Byte Count Register - EPV */ ++#define UDCBCRW __REG(0x40600258) /* Byte Count Register - EPW */ ++#define UDCBCRX __REG(0x4060025C) /* Byte Count Register - EPX */ ++ ++#define UDCDR0 __REG(0x40600300) /* Data Register - EP0 */ ++#define UDCDRA __REG(0x40600304) /* Data Register - EPA */ ++#define UDCDRB __REG(0x40600308) /* Data Register - EPB */ ++#define UDCDRC __REG(0x4060030C) /* Data Register - EPC */ ++#define UDCDRD __REG(0x40600310) /* Data Register - EPD */ ++#define UDCDRE __REG(0x40600314) /* Data Register - EPE */ ++#define UDCDRF __REG(0x40600318) /* Data Register - EPF */ ++#define UDCDRG __REG(0x4060031C) /* Data Register - EPG */ ++#define UDCDRH __REG(0x40600320) /* Data Register - EPH */ ++#define UDCDRI __REG(0x40600324) /* Data Register - EPI */ ++#define UDCDRJ __REG(0x40600328) /* Data Register - EPJ */ ++#define UDCDRK __REG(0x4060032C) /* Data Register - EPK */ ++#define UDCDRL __REG(0x40600330) /* Data Register - EPL */ ++#define UDCDRM __REG(0x40600334) /* Data Register - EPM */ ++#define UDCDRN __REG(0x40600338) /* Data Register - EPN */ ++#define UDCDRP __REG(0x4060033C) /* Data Register - EPP */ ++#define UDCDRQ __REG(0x40600340) /* Data Register - EPQ */ ++#define UDCDRR __REG(0x40600344) /* Data Register - EPR */ ++#define UDCDRS __REG(0x40600348) /* Data Register - EPS */ ++#define UDCDRT __REG(0x4060034C) /* Data Register - EPT */ ++#define UDCDRU __REG(0x40600350) /* Data Register - EPU */ ++#define UDCDRV __REG(0x40600354) /* Data Register - EPV */ ++#define UDCDRW __REG(0x40600358) /* Data Register - EPW */ ++#define UDCDRX __REG(0x4060035C) /* Data Register - EPX */ ++ ++#define UDCCRA __REG(0x40600404) /* Configuration register EPA */ ++#define UDCCRB __REG(0x40600408) /* Configuration register EPB */ ++#define UDCCRC __REG(0x4060040C) /* Configuration register EPC */ ++#define UDCCRD __REG(0x40600410) /* Configuration register EPD */ ++#define UDCCRE __REG(0x40600414) /* Configuration register EPE */ ++#define UDCCRF __REG(0x40600418) /* Configuration register EPF */ ++#define UDCCRG __REG(0x4060041C) /* Configuration register EPG */ ++#define UDCCRH __REG(0x40600420) /* Configuration register EPH */ ++#define UDCCRI __REG(0x40600424) /* Configuration register EPI */ ++#define UDCCRJ __REG(0x40600428) /* Configuration register EPJ */ ++#define UDCCRK __REG(0x4060042C) /* Configuration register EPK */ ++#define UDCCRL __REG(0x40600430) /* Configuration register EPL */ ++#define UDCCRM __REG(0x40600434) /* Configuration register EPM */ ++#define UDCCRN __REG(0x40600438) /* Configuration register EPN */ ++#define UDCCRP __REG(0x4060043C) /* Configuration register EPP */ ++#define UDCCRQ __REG(0x40600440) /* Configuration register EPQ */ ++#define UDCCRR __REG(0x40600444) /* Configuration register EPR */ ++#define UDCCRS __REG(0x40600448) /* Configuration register EPS */ ++#define UDCCRT __REG(0x4060044C) /* Configuration register EPT */ ++#define UDCCRU __REG(0x40600450) /* Configuration register EPU */ ++#define UDCCRV __REG(0x40600454) /* Configuration register EPV */ ++#define UDCCRW __REG(0x40600458) /* Configuration register EPW */ ++#define UDCCRX __REG(0x4060045C) /* Configuration register EPX */ ++ ++/* UDC Control Register (UDCCR) */ ++#define UDCCR_DWRE (1 <<16) /* Device Remote Wake-up Feature */ ++#define UDCCR_ACN (1 <<11) /* Active UDC Configuration Number */ ++#define UDCCR_AIN (1 << 8) /* Active UDC Interface Number */ ++#define UDCCR_AAISN (1 << 5) /* Active UDC Alternative Interface Setting Number */ ++#define UDCCR_SMAC (1 << 4) /* Switch Endpoint Memory to Active Configuration */ ++#define UDCCR_EMCE (1 << 3) /* Endpoint Meory Configuration Error */ ++#define UDCCR_UDR (1 << 2) /* UDC Resume */ ++#define UDCCR_UDA (1 << 1) /* UDC Active */ ++#define UDCCR_UDE (1 << 0) /* UDC Enable */ ++ ++#define UDCCR_ACN_MASK 0x3 ++#define UDCCR_AIN_MASK 0x7 ++#define UDCCR_AAISN_MASK 0x7 ++ ++#define UDCICR0_IE0 (1<<0) /* EP0 Interrupt Enable */ ++#define UDCICR0_IEA (1<<2) /* EPA Interrupt Enable */ ++#define UDCICR0_IEB (1<<4) /* EPB Interrupt Enable */ ++#define UDCICR0_IEC (1<<6) /* EPC Interrupt Enable */ ++#define UDCICR0_IED (1<<8) /* EPD Interrupt Enable */ ++#define UDCICR0_IEE (1<<10) /* EPE Interrupt Enable */ ++#define UDCICR0_IEF (1<<12) /* EPF Interrupt Enable */ ++#define UDCICR0_IEG (1<<14) /* EPG Interrupt Enable */ ++#define UDCICR0_IEH (1<<16) /* EPH Interrupt Enable */ ++#define UDCICR0_IEI (1<<18) /* EPI Interrupt Enable */ ++#define UDCICR0_IEJ (1<<20) /* EPJ Interrupt Enable */ ++#define UDCICR0_IEK (1<<22) /* EPK Interrupt Enable */ ++#define UDCICR0_IEL (1<<24) /* EPL Interrupt Enable */ ++#define UDCICR0_IEM (1<<26) /* EPM Interrupt Enable */ ++#define UDCICR0_IEN (1<<28) /* EPN Interrupt Enable */ ++#define UDCICR0_IEP (1<<30) /* EPP Interrupt Enable */ ++#define UDCICR1_IEQ (1<<0) /* EPQ Interrupt Enable */ ++#define UDCICR1_IER (1<<2) /* EPR Interrupt Enable */ ++#define UDCICR1_IES (1<<4) /* EPS Interrupt Enable */ ++#define UDCICR1_IET (1<<6) /* EPT Interrupt Enable */ ++#define UDCICR1_IEU (1<<8) /* EPU Interrupt Enable */ ++#define UDCICR1_IEV (1<<10) /* EPV Interrupt Enable */ ++#define UDCICR1_IEW (1<<12) /* EPW Interrupt Enable */ ++#define UDCICR1_IEX (1<<14) /* EPX Interrupt Enable */ ++#define UDCICR1_IERS (1<<27) /* Reset */ ++#define UDCICR1_IESU (1<<28) /* Suspend */ ++#define UDCICR1_IERU (1<<29) /* Resume */ ++#define UDCICR1_IESOF (1<<30) /* Start Of Frame */ ++#define UDCICR1_IECC (1<<31) /* Configuration Changed */ ++ ++#define UDCISR0_IE0 (1<<0) /* EP0 Interrupt Enable */ ++#define UDCISR0_IEA (1<<2) /* EPA Interrupt Enable */ ++#define UDCISR0_IEB (1<<4) /* EPB Interrupt Enable */ ++#define UDCISR0_IEC (1<<6) /* EPC Interrupt Enable */ ++#define UDCISR0_IED (1<<8) /* EPD Interrupt Enable */ ++#define UDCISR0_IEE (1<<10) /* EPE Interrupt Enable */ ++#define UDCISR0_IEF (1<<12) /* EPF Interrupt Enable */ ++#define UDCISR0_IEG (1<<14) /* EPG Interrupt Enable */ ++#define UDCISR0_IEH (1<<16) /* EPH Interrupt Enable */ ++#define UDCISR0_IEI (1<<18) /* EPI Interrupt Enable */ ++#define UDCISR0_IEJ (1<<20) /* EPJ Interrupt Enable */ ++#define UDCISR0_IEK (1<<22) /* EPK Interrupt Enable */ ++#define UDCISR0_IEL (1<<24) /* EPL Interrupt Enable */ ++#define UDCISR0_IEM (1<<26) /* EPM Interrupt Enable */ ++#define UDCISR0_IEN (1<<28) /* EPN Interrupt Enable */ ++#define UDCISR0_IEP (1<<30) /* EPP Interrupt Enable */ ++#define UDCISR1_IEQ (1<<0) /* EPQ Interrupt Enable */ ++#define UDCISR1_IER (1<<2) /* EPR Interrupt Enable */ ++#define UDCISR1_IES (1<<4) /* EPS Interrupt Enable */ ++#define UDCISR1_IET (1<<6) /* EPT Interrupt Enable */ ++#define UDCISR1_IEU (1<<8) /* EPU Interrupt Enable */ ++#define UDCISR1_IEV (1<<10) /* EPV Interrupt Enable */ ++#define UDCISR1_IEW (1<<12) /* EPW Interrupt Enable */ ++#define UDCISR1_IEX (1<<14) /* EPX Interrupt Enable */ ++#define UDCISR1_IERS (1<<27) /* Reset */ ++#define UDCISR1_IESU (1<<28) /* Suspend */ ++#define UDCISR1_IERU (1<<29) /* Resume */ ++#define UDCISR1_IESOF (1<<30) /* Start Of Frame */ ++#define UDCISR1_IECC (1<<31) /* Configuration Changed */ ++ ++#define UDC_INT_FIFOERROR (0x2) ++#define UDC_INT_PACKETCMP (0x1) ++ ++#define UDC_FNR_MASK (0x7ff) ++ ++/* UDC Endpoint 0 Control Status Register (UDCCS0) */ ++#define UDCCSR0_OPR (1 << 0) /* OUT packet ready */ ++#define UDCCSR0_IPR (1 << 1) /* IN packet ready */ ++#define UDCCSR0_FTF (1 << 2) /* Flush Tx FIFO */ ++#define UDCCSR0_DME (1 << 3) /* DMA Enable */ ++#define UDCCSR0_SST (1 << 4) /* Sent stall */ ++#define UDCCSR0_FST (1 << 5) /* Force stall */ ++#define UDCCSR0_RNE (1 << 6) /* Receive FIFO not empty */ ++#define UDCCSR0_SA (1 << 7) /* Setup Active */ ++ ++/* UDC Endpoint A-X */ ++#define UDCCSR_FS (1 << 0) /* FIFO service */ ++#define UDCCSR_PC (1 << 1) /* Packet complete */ ++#define UDCCSR_TRN (1 << 2) /* Tx/Rx NAK */ ++#define UDCCSR_DME (1 << 3) /* DMA Enable */ ++#define UDCCSR_SST (1 << 4) /* Sent STALL */ ++#define UDCCSR_FST (1 << 5) /* Force STALL */ ++#define UDCCSR_BNE (1 << 6) /* Buffer Not Empty */ ++#define UDCCSR_BNF (1 << 6) /* Buffer Not Full */ ++#define UDCCSR_SP (1 << 7) /* Short packet */ ++#define UDCCSR_FEF (1 << 8) /* Flush Endpoint FIFO */ ++#define UDCCSR_DPE (1 << 9) /* Data Packet Error */ ++ ++#define UDCCSR_WR_MASK (UDCCSR_DME|UDCCSR_FST) ++#define UDC_BCR_MASK (0x3ff) ++ ++#define UDCCONR_EE (1 << 0) /* Endpoint Enable */ ++#define UDCCONR_DE (1 << 1) /* Double-buffering Enable */ ++#define UDCCONR_MPS (1 << 2) /* Maximum Packet Size */ ++#define UDCCONR_ED (1 <<12) /* USB Endpoint Direction */ ++#define UDCCONR_ET (1 <<13) /* Endpoint Type */ ++#define UDCCONR_EN (1 <<15) /* Endpoint Number */ ++#define UDCCONR_AISN (1 <<19) /* Alternate Interface Number */ ++#define UDCCONR_IN (1 <<22) /* Interface Number */ ++#define UDCCONR_CN (1 <<25) /* Configuration Number */ ++ ++#define UDC_CON_CN_MASK (0x3) ++#define UDC_CON_IN_MASK (0x7) ++#define UDC_CON_AISN_MASK (0x7) ++#define UDC_CON_EN_MASK (0xf) ++#define UDC_CON_ET_MASK (0x3) ++#define UDC_CON_MPS_MASK (0x3ff) ++ ++/* DMA Request to Channel Map Register */ ++ ++/* Endpoint 0 state definitions */ ++#define WAIT_FOR_SETUP 0 ++#define DATA_STATE_XMIT 1 ++#define DATA_STATE_RECV 2 ++#define DATA_STATE_PENDING_XMIT 3 ++#define DATA_STATE_NEED_ZLP 4 ++#define WAIT_FOR_OUT_STATUS 5 ++ ++#endif ++ ++ ++#define UDCCR __REG(0x40600000) /* UDC Control Register */ ++#define UDCCR_OEN (1 << 31) /* On-the-Go Enable */ ++#define UDCCR_AALTHNP (1 << 30) /* A-device Alternate Host Negotiation ++ Protocol Port Support */ ++#define UDCCR_AHNP (1 << 29) /* A-device Host Negotiation Protocol ++ Support */ ++#define UDCCR_BHNP (1 << 28) /* B-device Host Negotiation Protocol ++ Enable */ ++#define UDCCR_DWRE (1 << 16) /* Device Remote Wake-up Enable */ ++#define UDCCR_ACN (0x03 << 11) /* Active UDC configuration Number */ ++#define UDCCR_ACN_S 11 ++#define UDCCR_AIN (0x07 << 8) /* Active UDC interface Number */ ++#define UDCCR_AIN_S 8 ++#define UDCCR_AAISN (0x07 << 5) /* Active UDC Alternate Interface ++ Setting Number */ ++#define UDCCR_AAISN_S 5 ++#define UDCCR_SMAC (1 << 4) /* Switch Endpoint Memory to Active ++ Configuration */ ++#define UDCCR_EMCE (1 << 3) /* Endpoint Memory Configuration ++ Error */ ++#define UDCCR_UDR (1 << 2) /* UDC Resume */ ++#define UDCCR_UDA (1 << 1) /* UDC Active */ ++#define UDCCR_UDE (1 << 0) /* UDC Enable */ ++ ++#define UDCICR0 __REG(0x40600004) /* UDC Interrupt Control Register0 */ ++#define UDCICR1 __REG(0x40600008) /* UDC Interrupt Control Register1 */ ++#define UDCICR_FIFOERR (1 << 1) /* FIFO Error interrupt for EP */ ++#define UDCICR_PKTCOMPL (1 << 0) /* Packet Complete interrupt for EP */ ++ ++#define UDC_INT_FIFOERROR (0x2) ++#define UDC_INT_PACKETCMP (0x1) ++ ++#define UDCICR_INT(n,intr) (((intr) & 0x03) << (((n) & 0x0F) * 2)) ++#define UDCICR1_IECC (1 << 31) /* IntEn - Configuration Change */ ++#define UDCICR1_IESOF (1 << 30) /* IntEn - Start of Frame */ ++#define UDCICR1_IERU (1 << 29) /* IntEn - Resume */ ++#define UDCICR1_IESU (1 << 28) /* IntEn - Suspend */ ++#define UDCICR1_IERS (1 << 27) /* IntEn - Reset */ ++ ++#define UDCISR0 __REG(0x4060000C) /* UDC Interrupt Status Register 0 */ ++#define UDCISR1 __REG(0x40600010) /* UDC Interrupt Status Register 1 */ ++#define UDCISR_INT(n,intr) (((intr) & 0x03) << (((n) & 0x0F) * 2)) ++#define UDCISR1_IECC (1 << 31) /* IntEn - Configuration Change */ ++#define UDCISR1_IESOF (1 << 30) /* IntEn - Start of Frame */ ++#define UDCISR1_IERU (1 << 29) /* IntEn - Resume */ ++#define UDCISR1_IESU (1 << 28) /* IntEn - Suspend */ ++#define UDCISR1_IERS (1 << 27) /* IntEn - Reset */ ++ ++ ++#define UDCFNR __REG(0x40600014) /* UDC Frame Number Register */ ++#define UDCOTGICR __REG(0x40600018) /* UDC On-The-Go interrupt control */ ++#define UDCOTGICR_IESF (1 << 24) /* OTG SET_FEATURE command recvd */ ++#define UDCOTGICR_IEXR (1 << 17) /* Extra Transciever Interrupt ++ Rising Edge Interrupt Enable */ ++#define UDCOTGICR_IEXF (1 << 16) /* Extra Transciever Interrupt ++ Falling Edge Interrupt Enable */ ++#define UDCOTGICR_IEVV40R (1 << 9) /* OTG Vbus Valid 4.0V Rising Edge ++ Interrupt Enable */ ++#define UDCOTGICR_IEVV40F (1 << 8) /* OTG Vbus Valid 4.0V Falling Edge ++ Interrupt Enable */ ++#define UDCOTGICR_IEVV44R (1 << 7) /* OTG Vbus Valid 4.4V Rising Edge ++ Interrupt Enable */ ++#define UDCOTGICR_IEVV44F (1 << 6) /* OTG Vbus Valid 4.4V Falling Edge ++ Interrupt Enable */ ++#define UDCOTGICR_IESVR (1 << 5) /* OTG Session Valid Rising Edge ++ Interrupt Enable */ ++#define UDCOTGICR_IESVF (1 << 4) /* OTG Session Valid Falling Edge ++ Interrupt Enable */ ++#define UDCOTGICR_IESDR (1 << 3) /* OTG A-Device SRP Detect Rising ++ Edge Interrupt Enable */ ++#define UDCOTGICR_IESDF (1 << 2) /* OTG A-Device SRP Detect Falling ++ Edge Interrupt Enable */ ++#define UDCOTGICR_IEIDR (1 << 1) /* OTG ID Change Rising Edge ++ Interrupt Enable */ ++#define UDCOTGICR_IEIDF (1 << 0) /* OTG ID Change Falling Edge ++ Interrupt Enable */ ++ ++#define UDCCSN(x) __REG2(0x40600100, (x) << 2) ++#define UDCCSR0 __REG(0x40600100) /* UDC Control/Status register - Endpoint 0 */ ++#define UDCCSR0_SA (1 << 7) /* Setup Active */ ++#define UDCCSR0_RNE (1 << 6) /* Receive FIFO Not Empty */ ++#define UDCCSR0_FST (1 << 5) /* Force Stall */ ++#define UDCCSR0_SST (1 << 4) /* Sent Stall */ ++#define UDCCSR0_DME (1 << 3) /* DMA Enable */ ++#define UDCCSR0_FTF (1 << 2) /* Flush Transmit FIFO */ ++#define UDCCSR0_IPR (1 << 1) /* IN Packet Ready */ ++#define UDCCSR0_OPC (1 << 0) /* OUT Packet Complete */ ++ ++#define UDCCSRA __REG(0x40600104) /* UDC Control/Status register - Endpoint A */ ++#define UDCCSRB __REG(0x40600108) /* UDC Control/Status register - Endpoint B */ ++#define UDCCSRC __REG(0x4060010C) /* UDC Control/Status register - Endpoint C */ ++#define UDCCSRD __REG(0x40600110) /* UDC Control/Status register - Endpoint D */ ++#define UDCCSRE __REG(0x40600114) /* UDC Control/Status register - Endpoint E */ ++#define UDCCSRF __REG(0x40600118) /* UDC Control/Status register - Endpoint F */ ++#define UDCCSRG __REG(0x4060011C) /* UDC Control/Status register - Endpoint G */ ++#define UDCCSRH __REG(0x40600120) /* UDC Control/Status register - Endpoint H */ ++#define UDCCSRI __REG(0x40600124) /* UDC Control/Status register - Endpoint I */ ++#define UDCCSRJ __REG(0x40600128) /* UDC Control/Status register - Endpoint J */ ++#define UDCCSRK __REG(0x4060012C) /* UDC Control/Status register - Endpoint K */ ++#define UDCCSRL __REG(0x40600130) /* UDC Control/Status register - Endpoint L */ ++#define UDCCSRM __REG(0x40600134) /* UDC Control/Status register - Endpoint M */ ++#define UDCCSRN __REG(0x40600138) /* UDC Control/Status register - Endpoint N */ ++#define UDCCSRP __REG(0x4060013C) /* UDC Control/Status register - Endpoint P */ ++#define UDCCSRQ __REG(0x40600140) /* UDC Control/Status register - Endpoint Q */ ++#define UDCCSRR __REG(0x40600144) /* UDC Control/Status register - Endpoint R */ ++#define UDCCSRS __REG(0x40600148) /* UDC Control/Status register - Endpoint S */ ++#define UDCCSRT __REG(0x4060014C) /* UDC Control/Status register - Endpoint T */ ++#define UDCCSRU __REG(0x40600150) /* UDC Control/Status register - Endpoint U */ ++#define UDCCSRV __REG(0x40600154) /* UDC Control/Status register - Endpoint V */ ++#define UDCCSRW __REG(0x40600158) /* UDC Control/Status register - Endpoint W */ ++#define UDCCSRX __REG(0x4060015C) /* UDC Control/Status register - Endpoint X */ ++ ++#define UDCCSR_DPE (1 << 9) /* Data Packet Error */ ++#define UDCCSR_FEF (1 << 8) /* Flush Endpoint FIFO */ ++#define UDCCSR_SP (1 << 7) /* Short Packet Control/Status */ ++#define UDCCSR_BNE (1 << 6) /* Buffer Not Empty (IN endpoints) */ ++#define UDCCSR_BNF (1 << 6) /* Buffer Not Full (OUT endpoints) */ ++#define UDCCSR_FST (1 << 5) /* Force STALL */ ++#define UDCCSR_SST (1 << 4) /* Sent STALL */ ++#define UDCCSR_DME (1 << 3) /* DMA Enable */ ++#define UDCCSR_TRN (1 << 2) /* Tx/Rx NAK */ ++#define UDCCSR_PC (1 << 1) /* Packet Complete */ ++#define UDCCSR_FS (1 << 0) /* FIFO needs service */ ++ ++#define UDCBCN(x) __REG2(0x40600200, (x)<<2) ++#define UDCBCR0 __REG(0x40600200) /* Byte Count Register - EP0 */ ++#define UDCBCRA __REG(0x40600204) /* Byte Count Register - EPA */ ++#define UDCBCRB __REG(0x40600208) /* Byte Count Register - EPB */ ++#define UDCBCRC __REG(0x4060020C) /* Byte Count Register - EPC */ ++#define UDCBCRD __REG(0x40600210) /* Byte Count Register - EPD */ ++#define UDCBCRE __REG(0x40600214) /* Byte Count Register - EPE */ ++#define UDCBCRF __REG(0x40600218) /* Byte Count Register - EPF */ ++#define UDCBCRG __REG(0x4060021C) /* Byte Count Register - EPG */ ++#define UDCBCRH __REG(0x40600220) /* Byte Count Register - EPH */ ++#define UDCBCRI __REG(0x40600224) /* Byte Count Register - EPI */ ++#define UDCBCRJ __REG(0x40600228) /* Byte Count Register - EPJ */ ++#define UDCBCRK __REG(0x4060022C) /* Byte Count Register - EPK */ ++#define UDCBCRL __REG(0x40600230) /* Byte Count Register - EPL */ ++#define UDCBCRM __REG(0x40600234) /* Byte Count Register - EPM */ ++#define UDCBCRN __REG(0x40600238) /* Byte Count Register - EPN */ ++#define UDCBCRP __REG(0x4060023C) /* Byte Count Register - EPP */ ++#define UDCBCRQ __REG(0x40600240) /* Byte Count Register - EPQ */ ++#define UDCBCRR __REG(0x40600244) /* Byte Count Register - EPR */ ++#define UDCBCRS __REG(0x40600248) /* Byte Count Register - EPS */ ++#define UDCBCRT __REG(0x4060024C) /* Byte Count Register - EPT */ ++#define UDCBCRU __REG(0x40600250) /* Byte Count Register - EPU */ ++#define UDCBCRV __REG(0x40600254) /* Byte Count Register - EPV */ ++#define UDCBCRW __REG(0x40600258) /* Byte Count Register - EPW */ ++#define UDCBCRX __REG(0x4060025C) /* Byte Count Register - EPX */ ++ ++#define UDCDN(x) __REG2(0x40600300, (x)<<2) ++#define PHYS_UDCDN(x) (0x40600300 + ((x)<<2)) ++#define PUDCDN(x) (volatile u32 *)(io_p2v(PHYS_UDCDN((x)))) ++#define UDCDR0 __REG(0x40600300) /* Data Register - EP0 */ ++#define UDCDRA __REG(0x40600304) /* Data Register - EPA */ ++#define UDCDRB __REG(0x40600308) /* Data Register - EPB */ ++#define UDCDRC __REG(0x4060030C) /* Data Register - EPC */ ++#define UDCDRD __REG(0x40600310) /* Data Register - EPD */ ++#define UDCDRE __REG(0x40600314) /* Data Register - EPE */ ++#define UDCDRF __REG(0x40600318) /* Data Register - EPF */ ++#define UDCDRG __REG(0x4060031C) /* Data Register - EPG */ ++#define UDCDRH __REG(0x40600320) /* Data Register - EPH */ ++#define UDCDRI __REG(0x40600324) /* Data Register - EPI */ ++#define UDCDRJ __REG(0x40600328) /* Data Register - EPJ */ ++#define UDCDRK __REG(0x4060032C) /* Data Register - EPK */ ++#define UDCDRL __REG(0x40600330) /* Data Register - EPL */ ++#define UDCDRM __REG(0x40600334) /* Data Register - EPM */ ++#define UDCDRN __REG(0x40600338) /* Data Register - EPN */ ++#define UDCDRP __REG(0x4060033C) /* Data Register - EPP */ ++#define UDCDRQ __REG(0x40600340) /* Data Register - EPQ */ ++#define UDCDRR __REG(0x40600344) /* Data Register - EPR */ ++#define UDCDRS __REG(0x40600348) /* Data Register - EPS */ ++#define UDCDRT __REG(0x4060034C) /* Data Register - EPT */ ++#define UDCDRU __REG(0x40600350) /* Data Register - EPU */ ++#define UDCDRV __REG(0x40600354) /* Data Register - EPV */ ++#define UDCDRW __REG(0x40600358) /* Data Register - EPW */ ++#define UDCDRX __REG(0x4060035C) /* Data Register - EPX */ ++ ++#define UDCCN(x) __REG2(0x40600400, (x)<<2) ++#define UDCCRA __REG(0x40600404) /* Configuration register EPA */ ++#define UDCCRB __REG(0x40600408) /* Configuration register EPB */ ++#define UDCCRC __REG(0x4060040C) /* Configuration register EPC */ ++#define UDCCRD __REG(0x40600410) /* Configuration register EPD */ ++#define UDCCRE __REG(0x40600414) /* Configuration register EPE */ ++#define UDCCRF __REG(0x40600418) /* Configuration register EPF */ ++#define UDCCRG __REG(0x4060041C) /* Configuration register EPG */ ++#define UDCCRH __REG(0x40600420) /* Configuration register EPH */ ++#define UDCCRI __REG(0x40600424) /* Configuration register EPI */ ++#define UDCCRJ __REG(0x40600428) /* Configuration register EPJ */ ++#define UDCCRK __REG(0x4060042C) /* Configuration register EPK */ ++#define UDCCRL __REG(0x40600430) /* Configuration register EPL */ ++#define UDCCRM __REG(0x40600434) /* Configuration register EPM */ ++#define UDCCRN __REG(0x40600438) /* Configuration register EPN */ ++#define UDCCRP __REG(0x4060043C) /* Configuration register EPP */ ++#define UDCCRQ __REG(0x40600440) /* Configuration register EPQ */ ++#define UDCCRR __REG(0x40600444) /* Configuration register EPR */ ++#define UDCCRS __REG(0x40600448) /* Configuration register EPS */ ++#define UDCCRT __REG(0x4060044C) /* Configuration register EPT */ ++#define UDCCRU __REG(0x40600450) /* Configuration register EPU */ ++#define UDCCRV __REG(0x40600454) /* Configuration register EPV */ ++#define UDCCRW __REG(0x40600458) /* Configuration register EPW */ ++#define UDCCRX __REG(0x4060045C) /* Configuration register EPX */ ++ ++#define UDCCONR_CN (0x03 << 25) /* Configuration Number */ ++#define UDCCONR_CN_S (25) ++#define UDCCONR_IN (0x07 << 22) /* Interface Number */ ++#define UDCCONR_IN_S (22) ++#define UDCCONR_AISN (0x07 << 19) /* Alternate Interface Number */ ++#define UDCCONR_AISN_S (19) ++#define UDCCONR_EN (0x0f << 15) /* Endpoint Number */ ++#define UDCCONR_EN_S (15) ++#define UDCCONR_ET (0x03 << 13) /* Endpoint Type: */ ++#define UDCCONR_ET_S (13) ++#define UDCCONR_ET_INT (0x03 << 13) /* Interrupt */ ++#define UDCCONR_ET_BULK (0x02 << 13) /* Bulk */ ++#define UDCCONR_ET_ISO (0x01 << 13) /* Isochronous */ ++#define UDCCONR_ET_NU (0x00 << 13) /* Not used */ ++#define UDCCONR_ED (1 << 12) /* Endpoint Direction */ ++#define UDCCONR_MPS (0x3ff << 2) /* Maximum Packet Size */ ++#define UDCCONR_MPS_S (2) ++#define UDCCONR_DE (1 << 1) /* Double Buffering Enable */ ++#define UDCCONR_EE (1 << 0) /* Endpoint Enable */ ++ ++ ++#define UDC_INT_FIFOERROR (0x2) ++#define UDC_INT_PACKETCMP (0x1) ++ ++#define UDC_FNR_MASK (0x7ff) ++ ++#define UDCCSR_WR_MASK (UDCCSR_DME|UDCCSR_FST) ++#define UDC_BCR_MASK (0x3ff) ++ ++ +diff -uNr linux/drivers/no-otg/otghw/cea-936.h linux/drivers/otg/otghw/cea-936.h +--- linux/drivers/no-otg/otghw/cea-936.h 1970-01-01 01:00:00.000000000 +0100 ++++ linux/drivers/otg/otghw/cea-936.h 2006-09-01 21:41:35.000000000 +0200 +@@ -0,0 +1,148 @@ ++/* ++ * otg/carkit.h ++ * ++ * Copyright (c) 2004 Belcarra ++ * ++ * ++ * Notes ++ * ++ * 1. Document references are to CEA-936a - Dec 12, 2003 ++ * ++ */ ++/*! ++ * @defgroup CEA ++ * @ingroup tcdgroup ++ */ ++/*! ++ * @file otghw/cea-936.h ++ * @brief Hardware defines for CEA-936 Hardware ++ * ++ * @ingroup CEA ++ */ ++ ++ ++/* ++ * Command Descriptions - C.f. 10.3 ++ */ ++#define CARKIT_GET_DEVICE 0x01 ++#define CARKIT_GET_CARKIT 0x02 ++#define CARKIT_AUDIO_EN 0x03 ++#define CARKIT_SET_CRNT 0x04 ++#define CARKIT_SET_UART 0x05 ++#define CARKIT_CARKIT_MASTER 0x06 ++ ++/* ++ * generic get command ++ */ ++struct carkit_get { ++ u8 strt; ++ u8 command_type; ++ u8 crc; ++ u8 terminator; ++}; ++ ++struct carkit_set { ++ u8 strt; ++ u8 command_type; ++ u8 command_value; ++ u8 crc; ++ u8 terminator; ++}; ++ ++struct carkit_get_carkit_ack { ++ u8 ack; ++ u8 terminator; ++}; ++ ++/* ++ * Get Device - C.f 10.3.1 ++ */ ++ ++struct carkit_get_device_carkit { ++ u8 ack; ++ u8 device_id; ++ u16 vendor_id; ++ u16 product_id; ++ u8 crc; ++ u8 terminator; ++}; ++ ++#define CARKIT_DEVICE_ID_GENERIC 0x01 ++#define CARKIT_DEVICE_ID_ENHANCED 0x02 ++#define CARKIT_DEVICE_ID_PROPRIETARY 0x03 ++ ++ ++/* ++ * Get CarKit - C.f. 10.3.2 ++ */ ++ ++struct carkit_get_carkit_carkit { ++ u8 ack; ++ u16 cr_ftrs; ++ u8 cr_sts; ++ u8 crc; ++ u8 terminator; ++}; ++ ++#define CARKIT_CR_FTRS_0_STERO (1 << 0) ++#define CARKIT_CR_FTRS_0_CRNT_ADJ (1 << 1) ++#define CARKIT_CR_FTRS_0_UART_MAX (7 << 2) ++#define CARKIT_CR_FTRS_0_UART_MSTR (1 << 5) ++ ++#define CARKIT_CR_FTRS_1_PH_REMOVE_FTR (1 << 0) ++#define CARKIT_CR_FTRS_1_CAR_OFF_FTR (1 << 1) ++#define CARKIT_CR_FTRS_1_CRDL_BTN_FTR (1 << 2) ++ ++#define CARKIT_CR_PH_REMOVE_STS (1 << 0) ++#define CARKIT_CR_CAR_OFF_STS (1 << 1) ++#define CARKIT_CR_CRDL_BTN_STS (1 << 2) ++#define CARKIT_CR_PH_RMV_CHG (1 << 4) ++#define CARKIT_CR_CAR_OFF_CHG (1 << 5) ++#define CARKIT_CR_CRDL_BTN_CHG (1 << 6) ++ ++ ++/* ++ * Set Carkit - C.f. 10.3.3 ++ */ ++#define CARKIT_CR_CTL_PH_RMV_IEN (1 << 0) ++#define CARKIT_CR_CAR_OFF_IEN (1 << 1) ++#define CARKIT_CR_CRDL_BTN_IEN (1 << 2) ++#define CARKIT_CR_CARKIT_MSTR (1 << 5) ++#define CARKIT_CR_PULSE_ID (1 << 6) ++#define CARKIT_CR_ID_INT_EN (1 << 7) ++ ++ ++/* ++ * Audio En - C.f. 10.3.4 ++ */ ++#define CARKIT_AUDIO_MODE_MONO (1 << 0) ++#define CARKIT_AUDIO_MODE_STEREO (1 << 1) ++ ++/* ++ * Set Crnt - C.f. 10.3.5 ++ */ ++#define CARKIT_CRNT_LMT (0xf) ++ ++ ++/* ++ * Set UART - C.f. 10.3.6 ++ */ ++#define CARKIT_UART_SPEED_9600 (1 << 0) ++#define CARKIT_UART_SPEED_19200 (1 << 1) ++#define CARKIT_UART_SPEED_38400 (1 << 3) ++#define CARKIT_UART_SPEED_57600 (1 << 4) ++#define CARKIT_UART_SPEED_115200 (1 << 4) ++ ++ ++/* ++ * Special chars - C.f. 10.4 ++ */ ++#define CARKIT_STRT 0x41 ++#define CARKIT_ESC 0x18 ++#define CARKIT_ACK 0x06 ++#define CARKIT_NYET 0x30 ++#define CARKIT_NAK 0x15 ++#define CARKIT_NSUP 0x1f ++#define CARKIT_CR 0x0d ++ ++ +diff -uNr linux/drivers/no-otg/otghw/fsotg-hardware.h linux/drivers/otg/otghw/fsotg-hardware.h +--- linux/drivers/no-otg/otghw/fsotg-hardware.h 1970-01-01 01:00:00.000000000 +0100 ++++ linux/drivers/otg/otghw/fsotg-hardware.h 2006-09-01 21:41:35.000000000 +0200 +@@ -0,0 +1,1054 @@ ++/* ++ * otghw/fsotg-hardware.h ++ * ++ * Copyright (c) 2004 Belcarra ++ * ++ * By: ++ * Stuart Lynne <sl@belcarra.com>, ++ * Tom Rushworth <tbr@belcarra.com>, ++ * Bruce Balden <balden@belcarra.com> ++ * ++ */ ++ ++/*! ++ * @defgroup FSOTG Freescale USBOTG Support ++ * @ingroup libgroup ++ */ ++/*! ++ * @file otghw/fsotg-hardware.h ++ * @brief Hardware defines for Freescale USBOTG Hardware ++ * ++ * This supports the Freescale implementation of the Trans Dimension ++ * USBOTG. ++ * ++ * This is used on: ++ * ++ * - i.MX21 ++ * - SCM-A11 ++ * - Argon+ ++ * - Zeus ++ * ++ * @ingroup FSOTG ++ */ ++ ++#if defined(CONFIG_ARCH_MX2ADS) ++#include <otghw/mx2ads.h> ++#include <otghw/mx2ads-hardware.h> ++#endif /* defined(CONFIG_ARCH_MX2ADS) */ ++ ++#if defined(CONFIG_ARCH_SCMA11EVB) ++#endif /* defined(CONFIG_ARCH_SCMA11EVB) */ ++ ++ ++ ++/*! ++ * @name OTG_SYS_CTRL ++ * C.f. 23.8 USB Control Register ++ */ ++ /*! @{ */ ++ ++//#define OTG_SYS_CTRL (OTG_SYS_BASE+0x00) ++ ++#define SYS_CTRL_I2C_WU_INT_STAT (1 << 27) ++#define SYS_CTRL_OTG_WU_INT_STAT (1 << 26) ++#define SYS_CTRL_HOST_WU_INT_STAT (1 << 25) ++#define SYS_CTRL_FNT_WU_INT_STAT (1 << 24) ++ ++#define SYS_CTRL_I2C_WU_INT_EN (1 << 19) ++#define SYS_CTRL_OTG_WU_INT_EN (1 << 18) ++#define SYS_CTRL_HOST_WU_INT_EN (1 << 17) ++#define SYS_CTRL_FNT_WU_INT_EN (1 << 16) ++ ++#define SYS_CTRL_OTG_BYP_VAL (0x3 << 11) ++#define SYS_CTRL_HOST1_BYP_VAL (0x3 << 9) ++ ++#define SYS_CTRL_OTG_PWR_MASK (1 << 6) ++#define SYS_CTRL_HOST1_PWR_MASK (1 << 6) ++#define SYS_CTRL_HOST2_PWR_MASK (1 << 4) ++#define SYS_CTRL_USB_BYP (1 << 2) ++#define SYS_CTRL_HOST1_TXEN_OE (1 << 1) ++/*! @} */ ++ ++/*! ++ * @name C.f. 23.9 USBOTG Module Registers ++ */ ++ /*! @{ */ ++ ++#define OTG_CORE_HWMODE (OTG_CORE_BASE+0x00) // 32bit core hardware mode reg ++ ++#define XCVR_D_D 0x00 ++#define XCVR_SE_D 0x01 ++#define XCVR_D_SE 0x02 ++#define XCVR_SE_SE 0x03 ++ ++#define MODULE_ANASDBEN (1 << 14) ++#define MODULE_OTGXCVR (0x3 << 6) ++#define MODULE_HOSTXCVR (0x3 << 4) ++#define MODULE_CRECFG (0x3) ++#define MODULE_CRECFG_HHNP (0x0) ++#define MODULE_CRECFG_HOST (0x1) ++#define MODULE_CRECFG_FUNC (0x2) ++#define MODULE_CRECFG_SHNP (0x3) ++ ++#define OTG_CORE_CINT_STAT (OTG_CORE_BASE+0x04) // 32bit core int status reg ++ ++#define MODULE_FCINTDSPEN (1 << 6) ++ ++#define MODULE_ASHNPINT (1 << 5) ++#define MODULE_ASFCINT (1 << 4) ++#define MODULE_ASHCINT (1 << 3) ++#define MODULE_HNPINT (1 << 2) ++#define MODULE_FCINT (1 << 1) ++#define MODULE_HCINT (1) ++ ++#define OTG_CORE_CINT_STEN (OTG_CORE_BASE+0x08) // 32bit core int enable reg ++ ++#define MODULE_ASHNPINT_EN (1 << 5) ++#define MODULE_ASFCINT_EN (1 << 4) ++#define MODULE_ASHCINT_EN (1 << 3) ++#define MODULE_HNPINT_EN (1 << 2) ++#define MODULE_FCINT_EN (1 << 1) ++#define MODULE_HCINT_EN (1) ++ ++#define OTG_CORE_CLK_CTRL (OTG_CORE_BASE+0x0C) // 32bit core clock control reg ++ ++#define MODULE_FUNC_CLK (1 << 2) ++#define MODULE_HOST_CLK (1 << 1) ++#define MODULE_MAIN_CLK (1) ++ ++#define OTG_CORE_RST_CTRL (OTG_CORE_BASE+0x10) // 32bit core reset control reg ++ ++#define MODULE_RSTI2C (1 << 15) ++#define MODULE_RSTCTRL (1 << 5) ++#define MODULE_RSTFC (1 << 4) ++#define MODULE_RSTFSIE (1 << 3) ++#define MODULE_RSTRH (1 << 2) ++#define MODULE_RSTHSIE (1 << 1) ++#define MODULE_RSTHC (1) ++ ++#define OTG_CORE_FRM_INTVL (OTG_CORE_BASE+0x14) // 32bit core frame interval reg ++ ++#define MODULE_RESET_FRAME (1 << 15) ++ ++#define OTG_CORE_FRM_REMAIN (OTG_CORE_BASE+0x18) // 32bit core frame remaining reg ++ ++#define OTG_CORE_HNP_CSTAT (OTG_CORE_BASE+0x1C) // 32bit core HNP current state reg ++ ++#define MODULE_HNPDAT (1 << 30) ++#define MODULE_VBUSBSE (1 << 29) ++#define MODULE_VBUSABSV (1 << 28) ++#define MODULE_VBUSGTAVV (1 << 27) ++ ++#define MODULE_ARMTHNPE (1 << 25) ++#define MODULE_BHNPEN (1 << 24) ++ ++#define MODULE_SLAVE (1 << 22) ++#define MODULE_MASTER (1 << 21) ++#define MODULE_BGEN (1 << 20) ++#define MODULE_CMPEN (1 << 19) ++#define MODULE_ISBDEV (1 << 18) ++#define MODULE_ISADEV (1 << 17) ++ ++#define MODULE_SWVBUSPUL (1 << 15) ++ ++#define MODULE_SWAUTORST (1 << 12) ++#define MODULE_SWPUDP (1 << 11) ++ ++#define MODULE_SWPDDM (1 << 9) ++#define MODULE_HNPSTATE (0x1f << 8) ++#define MODULE_CLRERROR (1 << 3) ++#define MODULE_ADROPBUS (1 << 2) ++#define MODULE_ABBUSREQ (1 << 1) ++ ++#define HNP_A_IDLE (0x10 << 8) ++#define HNP_A_MASTER (0x11 << 8) ++#define HNP_A_SLAVE (0x12 << 8) ++#define HNP_A_WAIT_VPULSE (0x13 << 8) ++ ++#define HNP_A_WAIT_DPULSE (0x14 << 8) ++#define HNP_A_WAIT_CONN_A (0x15 << 8) ++#define HNP_A_WAIT_CONN_B (0x16 << 8) ++#define HNP_A_WAIT_VRISE (0x17 << 8) ++#define HNP_A_SUSPEND (0x18 << 8) ++#define HNP_A_WAIT_VFALL (0x19 << 8) ++#define HNP_A_VBUS_ERR (0x1a << 8) ++#define HNP_CONN_DEBOUNCE (0x1b << 8) ++#define HNP_A_WAIT_ABREQ (0x1c << 8) ++#define HNP_B_IDLE (0x00 << 8) ++#define HNP_B_MASTER (0x01 << 8) ++#define HNP_B_SLAVE (0x02 << 8) ++#define HNP_B_SRP_INIT_V (0x03 << 8) ++#define HNP_B_SRP_INIT_D (0x04 << 8) ++#define HNP_B_PRE_WAIT_CONN_AB_SHORT_DB (0x05 << 8) ++#define HNP_B_WAIT_CONN_A (0x06 << 8) ++#define HNP_B_PRE_SLAVE (0x0a << 8) ++ ++ ++#define OTG_CORE_HNP_TIMER1 (OTG_CORE_BASE+0x20) // 32bit core HNP timer 1 reg ++#define OTG_CORE_HNP_TIMER2 (OTG_CORE_BASE+0x24) // 32bit core HNP timer 2 reg ++#define OTG_CORE_HNP_T3PCR (OTG_CORE_BASE+0x28) // 32bit core HNP timer 3 pulse ctrl ++ ++#define HNP_DATAPULSE (1 << 5) ++#define HNP_VBUSPULSE (1 << 4) ++ ++#define OTG_CORE_HINT_STAT (OTG_CORE_BASE+0x2C) // 32bit core HNP int status reg ++ ++#define HNP_I2COTGINT (1 << 15) ++#define HNP_AWAITBTO (1 << 8) // Hardware HNP Only ++#define HNP_AIDLEBDTO (1 << 7) // Hardware HNP Only ++#define HNP_SRPSUCFAIL (1 << 6) // Hardware HNP Only ++#define HNP_SRPINT (1 << 5) ++#define HNP_VBUSERROR (1 << 4) // Hardware HNP Only ++#define HNP_ABSEVAILD (1 << 3) ++#define HNP_ABUSVALID (1 << 2) ++#define HNP_MASSLVCHG (1 << 1) // Hardware HNP Only ++#define HNP_IDCHANGE (1) ++ ++#define OTG_CORE_HINT_STEN (OTG_CORE_BASE+0x30) // 32bit core HNP int enable reg ++ ++#define HNP_I2COTGINT_EN (1 << 15) ++#define HNP_AWAITBTO_EN (1 << 8) ++#define HNP_AIDLEBDTO_EN (1 << 7) ++#define HNP_SRPSUCFAIL_EN (1 << 6) ++#define HNP_SRPINT_EN (1 << 5) ++#define HNP_VBUSERROR_EN (1 << 4) ++#define HNP_ABSEVAILD_EN (1 << 3) ++#define HNP_ABUSVALID_EN (1 << 2) ++#define HNP_MASSLVCHG_EN (1 << 1) ++#define HNP_IDCHANGE_EN (1) ++ ++ ++#define OTG_CORE_CPUEPSEL_STAT (OTG_CORE_BASE+0x34) ++#define OTG_CORE_INTERRUPT_STEN (OTG_CORE_BASE+0x3c) ++/*! @} */ ++ ++ ++/*! ++ * @name C.f. 23.11.11 Host Registers ++ */ ++ /*! @{ */ ++ ++ ++#define OTG_HOST_CONTROL (OTG_HOST_BASE+0x00) // 32bit host controller config reg ++ ++#define HOST_CONTROL_HCRESET (1 << 31) ++#define HOST_CONTROL_RMTWUEN (1 << 4) ++ ++#define HOST_CONTROL_HCUSBSTE_RESET (0 << 2) ++#define HOST_CONTROL_HCUSBSTE_RESUME (1 << 2) ++#define HOST_CONTROL_HCUSBSTE_OPERATIONAL (2 << 2) ++#define HOST_CONTROL_HCUSBSTE_SUSPEND (3 << 2) ++ ++#define HOST_CONTROL_CTLBLKSR_11 (0) ++#define HOST_CONTROL_CTLBLKSR_21 (1) ++#define HOST_CONTROL_CTLBLKSR_41 (2) ++#define HOST_CONTROL_CTLBLKSR_81 (3) ++ ++ ++#define OTG_HOST_SINT_STAT (OTG_HOST_BASE+0x08) // 32bit host system int status reg ++ ++#define HOST_PSCINT (1 << 6) // Port Status Change ++#define HOST_FMOFINT (1 << 5) // Frame Number Overflow ++#define HOST_HERRINT (1 << 4) // Host Error ++#define HOST_RESDETINT (1 << 3) // Resume Detected ++#define HOST_SOFINT (1 << 2) // Start of Frame ++#define HOST_DONEINT (1 << 1) // Done Register ++#define HOST_SORINT (1) // Schedule Overrun ++ ++#define OTG_HOST_SINT_STEN (OTG_HOST_BASE+0x0C) // 32bit host system int enable reg ++ ++#define HOST_PSCINT_EN (1 << 6) // Port Status Change ++#define HOST_FMOFINT_EN (1 << 5) // Frame Number Overflow ++#define HOST_HERRINT_EN (1 << 4) // Host Error ++#define HOST_RESDETINT_EN (1 << 3) // Resume Detected ++#define HOST_SOFINT_EN (1 << 2) // Start of Frame ++#define HOST_DONEINT_EN (1 << 1) // Done Register ++#define HOST_SORINT_EN (1) // Schedule Overrun ++ ++#define OTG_HOST_XINT_STAT (OTG_HOST_BASE+0x18) // 32bit host X buf int status reg ++#define OTG_HOST_YINT_STAT (OTG_HOST_BASE+0x1C) // 32bit host Y buf int status reg ++ ++#define OTG_HOST_XYINT_STEN (OTG_HOST_BASE+0x20) // 32bit host XY buf int enable reg ++#define OTG_HOST_XFILL_STAT (OTG_HOST_BASE+0x28) // 32bit host X filled status reg ++#define OTG_HOST_YFILL_STAT (OTG_HOST_BASE+0x2C) // 32bit host Y filled status reg ++ ++#define OTG_HOST_ETD_EN (OTG_HOST_BASE+0x40) // 32bit host ETD enables reg ++#define OTG_HOST_DIR_ROUTE (OTG_HOST_BASE+0x48) // 32bit host direct routing reg ++#define OTG_HOST_IINT (OTG_HOST_BASE+0x4C) // 32bit host immediate interrupt reg ++ ++#define OTG_HOST_EP_DSTAT (OTG_HOST_BASE+0x50) // 32bit host endpoints done status ++#define OTG_HOST_ETD_DONE (OTG_HOST_BASE+0x54) // 32bit host ETD done reg ++ ++#define OTG_HOST_FRM_NUM (OTG_HOST_BASE+0x60) // 32bit host frame number reg ++#define OTG_HOST_LSP_THRESH (OTG_HOST_BASE+0x64) // 32bit host low speed threshold reg ++ ++#define OTG_HOST_ROOTHUB_DESCA (OTG_HOST_BASE+0x68) // 32bit host root hub descriptor A ++#define OTG_HOST_ROOTHUB_DESCB (OTG_HOST_BASE+0x6C) // 32bit host root hub descriptor B ++/* @} */ ++ ++/*! ++ * @name C.f. 23.11.29 ++ */ ++/*! @{ */ ++#define OTG_HOST_ROOTHUB_STATUS (OTG_HOST_BASE+0x70) // 32bit host root hub status reg ++ ++#define ROOTHUB_STATUS_CLRMTWUE (1 << 31) // Clear Remote Wakeup Enable ++#define ROOTHUB_STATUS_OVRCURCHG (1 << 17) // Over Current Indicator Change ++#define ROOTHUB_STATUS_LOCPWRSC (1 << 16) // Local Power Status Change ++#define ROOTHUB_STATUS_DEVCONWUE (1 << 15) // Device Connect Wakeup Enable ++#define ROOTHUB_STATUS_OVRCURI (1 << 1) // Over Current Indicator ++#define ROOTHUB_STATUS_LOCPWRS (1) // Local Power Status ++/*! @} */ ++ ++ ++/*! ++ * @name C.f. 23.11.30 ++ */ ++/*! @{ */ ++#define OTG_HOST_PORT_STATUS_1 (OTG_HOST_BASE+0x74) // 32bit host port 1 status bits ++#define OTG_HOST_PORT_STATUS_2 (OTG_HOST_BASE+0x78) // 32bit host port 2 status bits ++#define OTG_HOST_PORT_STATUS_3 (OTG_HOST_BASE+0x7c) // 32bit host port 3 status bits ++ ++#define PORT_STATUS_PRTRSTSC (1 << 20) // Port Reset Status Change ++#define PORT_STATUS_OVRCURIC (1 << 19) // Port Over Current Indicator Change ++#define PORT_STATUS_PRTSTATSC (1 << 18) // Port Suspend Status Change ++#define PORT_STATUS_PRTENBLSC (1 << 17) // Port Enable Status Change ++#define PORT_STATUS_CONNECTSC (1 << 16) // Connect Status Change ++ ++#define PORT_STATUS_LSDEVCON (1 << 9) // Low Speed Device Attached ++#define PORT_STATUS_PRTPWRST (1 << 8) // Port Power Status ++ ++#define PORT_STATUS_PRTRSTST (1 << 4) // Port Reset Status ++#define PORT_STATUS_PRTOVRCURI (1 << 3) // Port Over Current Indicator ++#define PORT_STATUS_PRTSUSPST (1 << 2) // Port Suspend Status ++#define PORT_STATUS_PRTENABST (1 << 1) // Port Enable Status ++#define PORT_STATUS_CURCONST (1) // Current Connect Status ++ ++ ++/* flags are the same for the interrupt, control and bulk transfer descriptors */ ++// R1: sec 23.11.10.2,3 pg 23-44,47 ++#define ETD_GET_COMPCODE(flags) ((flags >> 12) & 0xf) ++// R1: sec 23.11.10 Table 23-23 pg 23-40 ++#define ETD_CC_NOERROR 0x0 ++#define ETD_CC_CRCERR 0x1 ++#define ETD_CC_BITSTUFFERR 0x2 ++#define ETD_CC_DATATOGERR 0x3 ++#define ETD_CC_STALL 0x4 ++#define ETD_CC_DEVNOTRESPONDING 0x5 ++#define ETD_CC_PIDFAILURE 0x6 ++// Reserved 0x7 ++#define ETD_CC_DATAOVERRUN 0x8 ++#define ETD_CC_DATAUNDERRUN 0x9 ++#define ETD_CC_ACK 0xA ++#define ETD_CC_NAK 0xB ++#define ETD_CC_BUFFEROVERRUN 0xC ++#define ETD_CC_BUFFERUNDERRUN 0xD ++#define ETD_CC_SCHEDOVERRUN 0xE ++#define ETD_CC_NOTACCESSED 0xF ++ ++// R1: sec 23.11.10.2,3 pg 23-44,47 (contd) ++#define ETD_GET_ERRORCNT(flags) (((flags) >> 8) & 0xf) ++#define ETD_GET_DATATOGL(flags) (((flags) >> 6) & 0x3) ++#define ETD_SET_DATATOGL(v) (((v) & 0x3) << 6) ++#define ETD_GET_DELAYINT(flags) (((flags) >> 3) & 0x7) ++#define ETD_SET_DELAYINT(v) (((v) & 0x7) << 3) ++#define ETD_GET_BUFROUND(flags) (((flags) >> 2) & 0x1) ++#define ETD_SET_BUFROUND(v) (((v) & 0x1) << 2) ++#define ETD_GET_DIRPID(flags) ((flags) & 0x3) ++#define ETD_SET_DIRPID(v) ((v) & 0x3) ++#define ETD_FLIP_DIRPID(flags) ((flags) ^ 0x3) /* xor with 0x3 flips 2<->1 (IN<->OUT) */ ++#define ETD_DIRPID_SETUP 0x0 // OUT ++#define ETD_DIRPID_OUT 0x1 ++#define ETD_DIRPID_IN 0x2 ++#define ETD_DIRPID_RESERVED 0x3 ++ ++// For non-isoc td.w3.val - ++#define ETD_GET_BUFSIZE(w3) (((w3) >> 21) & 0xfff) ++#define ETD_SET_BUFSIZE(v) (((v) & 0xfff) << 21) ++#define ETD_GET_TOTBYECNT(w3) ((w3) & 0xfffff) ++#define ETD_SET_TOTBYECNT(v) ((v) & 0xfffff) ++ ++/* isoc flags shares the COMPCODE and DELAYINT bitfields with the other TDs, ++ but the remaining bits are different. */ ++#define ETD_GET_AUTOISO(flags) (((flags) >> 11) & 0x1) ++#define ETD_SET_AUTOISO(v) (((v) & 0x1) << 11) ++#define ETD_GET_FRAMECNT(flags) (((flags) >> 8) & 0x1) ++#define ETD_SET_FRAMECNT(v) (((v) & 0x1) << 8) ++// For isoc td.w3.val - ++/* pkt1 and pkt0 use the COMPCODE bitfield, and add a PKTLEN field. */ ++#define ETD_GET_PKTLEN(pkt) ((pkt) & 0x3ff) ++#define ETD_SET_PKTLEN(v) ((v) & 0x3ff) ++ ++ ++// ETD endpoint descriptor (etd->epd) bitfield access ++// R1: sec 23.11.10.1 pg 23-41 (word0) ++#define ETD_GET_SNDNAK(epd) (((epd) >> 30) & 0x1) ++#define ETD_SET_SNDNAK(v) (((v) & 0x1) << 30) ++#define ETD_GET_TOGCRY(epd) (((epd) >> 28) & 0x1) ++#define ETD_SET_TOGCRY(v) (((v) & 0x1) << 28) ++#define ETD_GET_HALTED(epd) (((epd) >> 27) & 0x1) ++#define ETD_SET_HALTED(v) (((v) & 0x1) << 27) ++#define ETD_GET_MAXPKTSIZ(epd) (((epd) >> 16) & 0x3ff) ++#define ETD_SET_MAXPKTSIZ(v) (((v) & 0x3ff) << 16) ++#define ETD_GET_FORMAT(epd) (((epd) >> 14) & 0x3) ++#define ETD_SET_FORMAT(v) (((v) & 0x3) << 14) ++#define ETD_FORMAT_CONTROL 0x0 ++#define ETD_FORMAT_ISOC 0x1 ++#define ETD_FORMAT_BULK 0x2 ++#define ETD_FORMAT_INTERRUPT 0x3 ++#define ETD_GET_SPEED(epd) (((epd) >> 13) & 0x1) ++#define ETD_SET_SPEED(v) (((v) & 0x1) << 13) ++#define ETD_SPEED_FULL 0x0 ++#define ETD_SPEED_LOW 0x1 ++#define ETD_GET_DIRECT(epd) (((epd) >> 11) & 0x3) ++#define ETD_SET_DIRECT(v) (((v) & 0x3) << 11) ++/* xor with 3 flips 2<->1 */ ++#define ETD_FLIP_DIRECT(epd) ((epd) ^ (0x3 << 11)) ++#define ETD_DIRECT_TD00 0x0 ++#define ETD_DIRECT_OUT 0x1 ++#define ETD_DIRECT_IN 0x2 ++#define ETD_DIRECT_TD11 0x3 ++#define ETD_GET_ENDPNT(epd) (((epd) >> 7) & 0xf) ++#define ETD_SET_ENDPNT(v) (((v) & 0xf) << 7) ++#define ETD_GET_ADDRESS(epd) ((epd) & 0x7f) ++#define ETD_SET_ADDRESS(v) ((v) & 0x7f) ++ ++// etd_urb_state[] values ++#define ETD_URB_COMPLETED 0 ++#define ETD_URB_SETUP_STATUS 1 ++#define ETD_URB_SETUP_DATA 2 ++#define ETD_URB_SETUP_START 3 ++#define ETD_URB_BULK_START 4 ++#define ETD_URB_BULKWZLP 5 ++#define ETD_URB_BULKWZLP_START 6 ++#define ETD_URB_INTERRUPT_START 7 ++#define ETD_URB_ISOC_START 8 ++/*! @} */ ++ ++ ++/*! ++ * @name C.f. 23.12.8 Function Registers ++ */ ++/*! @{ */ ++ ++#define OTG_FUNC_CMD_STAT (OTG_FUNC_BASE+0x00) // 32bit func command status reg ++ ++#define COMMAND_SOFTRESET (1 << 7) ++#define COMMAND_BADISOAP (1 << 3) ++#define COMMAND_SUPDET (1 << 2) ++#define COMMAND_RSMINPROG (1 << 1) ++#define COMMAND_RESETDET (1) ++ ++ ++#define OTG_FUNC_DEV_ADDR (OTG_FUNC_BASE+0x04) // 32bit func device address reg ++ ++#define OTG_FUNC_SINT_STAT (OTG_FUNC_BASE+0x08) // 32bit func system int status reg ++ ++#define SYSTEM_DONEREGINTDS (1 << 5) ++#define SYSTEM_SOFDETINT (1 << 4) ++#define SYSTEM_DONEREGINT (1 << 3) ++#define SYSTEM_SUSPDETINT (1 << 2) ++#define SYSTEM_RSMFININT (1 << 1) ++#define SYSTEM_RESETINT (1) ++ ++#define OTG_FUNC_SINT_STEN (OTG_FUNC_BASE+0x0C) // 32bit func system int enable reg ++ ++#define SYSTEM_DONEREGINTDS_EN (1 << 5) ++#define SYSTEM_SOFDETINT_EN (1 << 4) ++#define SYSTEM_DONEREGINT_EN (1 << 3) ++#define SYSTEM_SUSPDETINT_EN (1 << 2) ++#define SYSTEM_RSMFININT_EN (1 << 1) ++#define SYSTEM_RESETINT_EN (1) ++ ++ ++#define OTG_FUNC_XINT_STAT (OTG_FUNC_BASE+0x10) // 32bit func X buf int status reg ++#define OTG_FUNC_YINT_STAT (OTG_FUNC_BASE+0x14) // 32bit func Y buf int status reg ++#define OTG_FUNC_XYINT_STEN (OTG_FUNC_BASE+0x18) // 32bit func XY buf int enable reg ++#define OTG_FUNC_XFILL_STAT (OTG_FUNC_BASE+0x1C) // 32bit func X filled status reg ++ ++#define OTG_FUNC_YFILL_STAT (OTG_FUNC_BASE+0x20) // 32bit func Y filled status reg ++#define OTG_FUNC_EP_EN (OTG_FUNC_BASE+0x24) // 32bit func endpoints enable reg ++#define OTG_FUNC_EP_RDY (OTG_FUNC_BASE+0x28) // 32bit func endpoints ready reg ++#define OTG_FUNC_IINT (OTG_FUNC_BASE+0x2C) // 32bit func immediate interrupt reg ++ ++#define OTG_FUNC_EP_DSTAT (OTG_FUNC_BASE+0x30) // 32bit func endpoints done status ++#define OTG_FUNC_EP_DEN (OTG_FUNC_BASE+0x34) // 32bit func endpoints done enable ++#define OTG_FUNC_EP_TOGGLE (OTG_FUNC_BASE+0x38) // 32bit func endpoints toggle bits ++#define OTG_FUNC_FRM_NUM (OTG_FUNC_BASE+0x3C) // 32bit func frame number reg ++ ++ ++#define EP0_STALL (1 << 31) ++#define EP0_SETUP (1 << 30) ++#define EP0_OVERRUN (1 << 29) ++#define EP0_AUTOISO (1 << 27) ++ ++ ++/* generic endpoint register manipulations ++ */ ++#define EP_OUT 0x1 ++#define EP_IN 0x2 ++#define EP_BOTH 0x3 ++ ++#define ETD_MASK(n) (1 << n) ++#define ep_num_both(n) (EP_BOTH << n) ++#define ep_num_dir(n, dir) ((dir ? EP_IN : EP_OUT) << (n*2)) ++#define ep_num_out(n) ep_num_dir(n, USB_DIR_OUT) ++#define ep_num_in(n) ep_num_dir(n, USB_DIR_IN) ++ ++ ++/* ep descriptor access ++ */ ++static __inline__ u32 etd_word(int n, int word) ++{ ++ u32 offset = n * 16; ++ offset += word * 4; ++ return OTG_ETD_BASE + offset; ++} ++ ++/* ep descriptor access ++ */ ++static __inline__ u32 ep_word(int n, int dir, int word) ++{ ++ u32 offset = n * 2; ++ offset += dir ? 1 : 0; ++ offset *= 16; ++ offset += word * 4; ++ return OTG_EP_BASE + offset; ++} ++ ++/* endpoint data buffer access ++ * ++ * This is a simplistic allocator, will do until we want to support ISO or host mode. ++ * ++ * This works because we are assuming a maximum of 16 allocate endpoints, and no ++ * overlapped endpoints (both in and out are allocated). ++ */ ++ ++static volatile __inline__ u16 data_x_buf(int n, int dir) ++{ ++ return 0x40 * (n * 4 + 2 * (dir ? 1 : 0)); ++} ++static volatile __inline__ u16 data_y_buf(int n, int dir) ++{ ++ return 0x40 * (n * 4 + 2 * (dir ? 1 : 0) + 1); ++} ++ ++static volatile __inline__ u8 * data_x_address(int n, int dir) ++{ ++ return (volatile u8 *) IO_ADDRESS(OTG_DATA_BASE + data_x_buf(n, dir)); ++} ++static volatile __inline__ u8 * data_y_address(int n, int dir) ++{ ++ return (volatile u8 *) IO_ADDRESS(OTG_DATA_BASE + data_y_buf(n, dir)); ++} ++/*! @} */ ++ ++/*! ++ * @name C.f. 23.13.3 DMA Registers ++ */ ++/*! @{ */ ++#define OTG_DMA_REV_NUM (OTG_DMA_BASE+0x000) // 32bit dma revision number reg ++#define OTG_DMA_DINT_STAT (OTG_DMA_BASE+0x004) // 32bit dma int status reg ++#define OTG_DMA_DINT_STEN (OTG_DMA_BASE+0x008) // 32bit dma int enable reg ++#define OTG_DMA_ETD_ERR (OTG_DMA_BASE+0x00C) // 32bit dma ETD error status reg ++#define OTG_DMA_EP_ERR (OTG_DMA_BASE+0x010) // 32bit dma EP error status reg ++#define OTG_DMA_ETD_EN (OTG_DMA_BASE+0x020) // 32bit dma ETD DMA enable reg ++#define OTG_DMA_EP_EN (OTG_DMA_BASE+0x024) // 32bit dma EP DMA enable reg ++#define OTG_DMA_ETD_ENXREQ (OTG_DMA_BASE+0x028) // 32bit dma ETD DMA enable Xtrig req ++#define OTG_DMA_EP_ENXREQ (OTG_DMA_BASE+0x02C) // 32bit dma EP DMA enable Ytrig req ++#define OTG_DMA_ETD_ENXYREQ (OTG_DMA_BASE+0x030) // 32bit dma ETD DMA enble XYtrig req ++#define OTG_DMA_EP_ENXYREQ (OTG_DMA_BASE+0x034) // 32bit dma EP DMA enable XYtrig req ++#define OTG_DMA_ETD_BURST4 (OTG_DMA_BASE+0x038) // 32bit dma ETD DMA enble burst4 reg ++#define OTG_DMA_EP_BURST4 (OTG_DMA_BASE+0x03C) // 32bit dma EP DMA enable burst4 reg ++#define OTG_DMA_MISC_CTRL (OTG_DMA_BASE+0x040) // 32bit dma EP misc control reg ++#define OTG_DMA_ETD_CH_CLR (OTG_DMA_BASE+0x044) // 32bit dma ETD clear channel reg ++#define OTG_DMA_EP_CH_CLR (OTG_DMA_BASE+0x048) // 32bit dma EP clear channel reg ++ ++ ++#define OTG_DMA_ETD_CH_CLR (OTG_DMA_BASE+0x044) // 32bit dma ETD clear channel reg ++#define OTG_DMA_ETD_ERR (OTG_DMA_BASE+0x00C) // 32bit dma ETD error status reg ++#define OTG_DMA_ETD_EN (OTG_DMA_BASE+0x020) // 32bit dma ETD DMA enable reg ++#define OTG_DMA_ETD_ENXREQ (OTG_DMA_BASE+0x028) // 32bit dma ETD DMA enable Xtrig req ++#define OTG_DMA_ETD_ENXYREQ (OTG_DMA_BASE+0x030) // 32bit dma ETD DMA enble XYtrig req ++#define OTG_DMA_ETD_BURST4 (OTG_DMA_BASE+0x038) // 32bit dma ETD DMA enble burst4 reg ++ ++ ++ ++#define OTG_DMA_EP_CH_CLR (OTG_DMA_BASE+0x048) // 32bit dma EP clear channel reg ++#define OTG_DMA_EP_ERR (OTG_DMA_BASE+0x010) // 32bit dma EP error status reg ++#define OTG_DMA_EP_EN (OTG_DMA_BASE+0x024) // 32bit dma EP DMA enable reg ++#define OTG_DMA_EP_ENXREQ (OTG_DMA_BASE+0x02C) // 32bit dma EP DMA enable Ytrig req ++#define OTG_DMA_EP_ENXYREQ (OTG_DMA_BASE+0x034) // 32bit dma EP DMA enable XYtrig req ++#define OTG_DMA_EP_BURST4 (OTG_DMA_BASE+0x03C) // 32bit dma EP DMA enable burst4 reg ++ ++ ++#define dma_num_dir(n, dir) (n * 2 + (dir ? 1 : 0)) ++#define dma_num_out(n) dma_num_dir(n, USB_DIR_OUT) ++#define dma_num_in(n) dma_num_dir(n, USB_DIR_IN) ++ ++#define OTG_DMA_ETD_MSA(x) (OTG_DMA_BASE+0x100+x*4) ++#define OTG_DMA_EPN_MSA(x) (OTG_DMA_BASE+0x180+x*4) ++#define OTG_DMA_ETDN_BPTR(x) (OTG_DMA_BASE+0x280+x*4) ++#define OTG_DMA_EPN_BPTR(x) (OTG_DMA_BASE+0x284+x*4) ++ ++/*! @} */ ++ ++/* ********************************************************************************************** */ ++/*! ++ * Warning: the MX21 memory mapped region appears to require 32-bit access only. ++ * As of this writing, only the ETD region has been tested, but it does show the ++ * following behavior: ++ * ++ * Step 1: *(volatile u32 *)(void*)0xe40243f8 == 00000000 ++ * Step 2: *(volatile u16 *)(void*)0xe40243f8 = 0x0084; ++ * Step 3: *(volatile u32 *)(void*)0xe40243f8 == 00840084 ++ * Step 4: *(volatile u8 *)(void*)(0xe40243f8+3) = 0x01; ++ * Step 5: *(volatile u32 *)(void*)0xe40243f8 == 01010101 ++ * ++ * Any access to less than 32 bits is replicated as many times as required ++ * to make 32 bits, and the surrounding 32-bit region is changed. ++ * ++ * Just to add insult to injury, gcc 3.2.3 wants to store constants ++ * that are small enough using byte instructions. E.g. ++ * ++ * mm->host.System_Interrupt_Enables = (SIEN_PSCIEN | SIEN_FMOFIEN | SIEN_HERRIEN | SIEN_RESDETIEN | ++ * SIEN_DONEIEN | SIEN_SORIEN); ++ * ++ * becomes: ++ * ++ * ldrb r2, [r3, #140] ++ * mov r2, #123 ++ * strb r2, [r3, #140] ++ * ldrb r2, [r3, #141] ++ * strb r1, [r3, #141] ++ * ldrb r2, [r3, #142] ++ * strb r1, [r3, #142] ++ * ldrb r2, [r3, #143] ++ * strb r1, [r3, #143] ++ * ++ * which, given the 32-bit access restrictions above, ++ * is not going to work very well. Changing the original assignment to: ++ * ++ * *&(mm->host.System_Interrupt_Enables) = (SIEN_PSCIEN | SIEN_FMOFIEN | SIEN_HERRIEN | SIEN_RESDETIEN | ++ * SIEN_DONEIEN | SIEN_SORIEN); ++ * ++ * which should be identical, from a language definition point of view, ++ * produces: ++ * ++ * mov r2, #123 ++ * str r2, [r3, #140] ++ * ++ */ ++ ++ ++/*! ++ * @name TransferDescriptors ++ * ++ * Note that the MX21 needs to run in little-endian mode for OTG, but ++ * since the memory can only be accessed in 32-bit quantities, anything ++ * smaller will be built in regular memory, then transfered one word at ++ * a time to the memory-mapped registers, and that word access will flip ++ * the order of bytes. u16 quantities survive this flipping because they ++ * get "pre-flipped" in regular memory. ++ * ++ * ++ * An Endpoint Transfer Descriptor (ETD) consists of 4x32bit words. ++ * The first word is the Endpoint Descriptor, the last three are ++ * the transfer descriptor. Transfer descriptors come in 3 formats, ++ * CONTROL/BULK, INTERRUPT, and ISOCRONOUS. Becasue of the memory ++ * access restrictions to less than 32 bits described above, only ++ * the words are mapped directly, The smaller fields are assembled ++ * into 32-bit words before being placed into the live ETD. ++ * ++ */ ++ /*! @{ */ ++ ++/*! ++ * @struct transfer_descriptor_w1 ++ * All three types of TD have the same format for w1. ++ */ ++typedef struct transfer_descriptor_w1 { ++#if defined(LITTLE_ENDIAN) ++ u16 x; ++ u16 y; ++#else ++ u16 y; ++ u16 x; ++#endif ++} __attribute__ ((packed)) volatile transfer_descriptor_w1; ++ ++/*! ++ * @struct control_bulk_transfer_descriptor_w2 ++ * There are 3 different formats for w2. ++ * C.f. R1: sec 23.11.10.2 pg 23-43 ++ */ ++typedef struct control_bulk_transfer_descriptor_w2 { ++#if defined(LITTLE_ENDIAN) ++ u8 rtrydelay; ++ u8 reserved; ++ u16 flags; ++#else ++ u16 flags; ++ u8 reserved; ++ u8 rtrydelay; ++#endif ++} __attribute__ ((packed)) volatile control_bulk_transfer_descriptor_w2; ++ ++/*! ++ * @struct interrupt_transfer_descriptor_w2 ++ * C.f. R1: sec 23.11.10.3 pg 23-46 ++ */ ++typedef struct interrupt_transfer_descriptor_w2 { ++#if defined(LITTLE_ENDIAN) ++ u8 polinterv; ++ u8 relpolpos; ++ u16 flags; ++#else ++ u16 flags; ++ u8 relpolpos; ++ u8 polinterv; ++#endif ++} __attribute__ ((packed)) volatile interrupt_transfer_descriptor_w2; ++ ++/*! ++ * @struct isoc_transfer_descriptor_w2 ++ * C.f. R1: sec 23.11.10.4 pg 23-48 ++ */ ++typedef struct isoc_transfer_descriptor_w2 { ++#if defined(LITTLE_ENDIAN) ++ u16 startfrm; ++ u16 flags; ++#else ++ u16 flags; ++ u16 startfrm; ++#endif ++} __attribute__ ((packed)) volatile isoc_transfer_descriptor_w2; ++ ++ ++ ++/*! ++ * @struct isoc_transfer_descriptor_w3 ++ * There are 2 different formats for w3. ++ * All but isoc use a packet size and 20-bit bytecount, accessed through w3.val. ++ */ ++typedef struct isoc_transfer_descriptor_w3 { ++#if defined(LITTLE_ENDIAN) ++ u16 pkt0; ++ u16 pkt1; ++#else ++ u16 pkt1; ++ u16 pkt0; ++#endif ++} __attribute__ ((packed)) volatile isoc_transfer_descriptor_w3; ++ ++/*! ++ * @name transfer_descriptor ++ */ ++/*! @{ */ ++typedef struct transfer_descriptor { ++ union { ++ u32 val; ++ transfer_descriptor_w1 bufsrtad; ++ } volatile w1; ++ union { ++ u32 val; ++ control_bulk_transfer_descriptor_w2 cb; ++ interrupt_transfer_descriptor_w2 intr; ++ isoc_transfer_descriptor_w2 isoc; ++ } volatile w2; ++ union { ++ u32 val; ++ isoc_transfer_descriptor_w3 isoc; ++ } volatile w3; ++} __attribute__ ((packed)) volatile transfer_descriptor; ++ ++/*! @} */ ++ ++/*! ++ * @name endpoint_transfer_descriptor ++ * C.f. R1: sec 23.11.10 pg 23-41 ++ */ ++ /*! @{ */ ++typedef struct endpoint_transfer_descriptor { ++ u32 epd; ++ transfer_descriptor td; ++} __attribute__ ((packed)) volatile endpoint_transfer_descriptor; // ETD ++ ++/*! @} */ ++ ++ ++/*! ++ * @name DataBuffs ++ * ++ * There is no predetermined size for data buffers, so ++ * this structure was chosen as an arbitrary, but generally ++ * adequate size. ++ */ ++/*! @{ */ ++ ++/*! ++ * @struct fs_data_buff ++ */ ++typedef struct fs_data_buff { ++ u32 data[DATA_BUFF_SIZE/sizeof(u32)]; // Cannot safely write less than 32-bit quantities ++} volatile fs_data_buff; ++ ++ ++/*! ++ * @struct fs_hcpriv ++ */ ++typedef struct fs_hcpriv { ++ struct bus_hcpriv *bus_hcpriv; ++ u32 sof_count; ++ u32 free_etds; ++ endpoint_transfer_descriptor sdp_etd[NUM_ETDS]; // Setup data phase save area ++ void *sdp_data[NUM_ETDS]; ++ u8 etd_urb_state[NUM_ETDS]; ++ u16 free_buffs; ++ u16 buff_list[NUM_DATA_BUFFS]; ++} fs_hcpriv; ++ ++ ++/*! ++ * get_data_buff() - get data buff ++ */ ++static __inline__ fs_data_buff *get_data_buff(fs_hcpriv *fs_hcpriv) ++{ ++ // Get the next available free data_buff, return NULL if none available. ++ unsigned long flags; ++ fs_data_buff *db; ++ u16 ndx; ++ local_irq_save(flags); ++ if (0xffff != (ndx = fs_hcpriv->free_buffs)) { ++ fs_hcpriv->free_buffs = fs_hcpriv->buff_list[ndx]; ++ db = ndx + (fs_data_buff *)OTG_DATA_BASE; ++ } ++ else ++ db = NULL; ++ local_irq_restore(flags); ++ return(db); ++} ++ ++/*! ++ * rel_data_buff() - release data buff ++ */ ++static __inline__ fs_data_buff *rel_data_buff(fs_hcpriv *fs_hcpriv, fs_data_buff *db) ++{ ++ // Release db to the pool of available data_buffs. ++ unsigned long flags; ++ u16 ndx; ++ if (NULL != db) { ++ local_irq_save(flags); ++ ndx = db - (fs_data_buff *)OTG_DATA_BASE; ++ fs_hcpriv->buff_list[ndx] = fs_hcpriv->free_buffs; ++ fs_hcpriv->free_buffs = ndx; ++#if 0 ++ db->next = fs_hcpriv->free_buffs; ++ fs_hcpriv->free_buffs = db; ++#endif ++ local_irq_restore(flags); ++ } ++ return(NULL); ++} ++ ++/*! ++ * data_buff_boff() - get data buffer offset given address ++ */ ++static __inline__ u16 data_buff_boff(fs_data_buff *db) ++{ ++ return((u16)(((void *) db) - ((void *) OTG_DATA_BASE))); ++} ++ ++/*! ++ * data_buff_addr() - get data buffer address given offset ++ */ ++static __inline__ fs_data_buff *data_buff_addr(u16 boff) ++{ ++ // Return the address of the fs_data_buffer that is boff bytes from the start of data buffer memory. ++ return(boff + ((void *) OTG_DATA_BASE)); ++} ++/*! @} */ ++ ++/* ********************************************************************************************** */ ++ ++/*! ++ * @name FSUSBOTGIO ++ * @brief Freescale USBOTG I/O support. ++ */ ++ /*! @{ */ ++ ++/*! ++ * fs_rb() - read a byte ++ * @param port ++ * @return byte read ++ */ ++static u8 __inline__ fs_rb(u32 port) ++{ ++ return *(volatile u8 *) (IO_ADDRESS(port)); ++} ++ ++/*! ++ * fs_rl() - read a long ++ * @param port ++ * @return word read ++ */ ++static u32 __inline__ fs_rl(u32 port) ++{ ++ return *(volatile u32 *) (IO_ADDRESS(port)); ++} ++ ++/*! ++ * fs_wb() - write a byte ++ * @param port ++ * @param val ++ */ ++static void __inline__ fs_wb(u32 port, u8 val) ++{ ++ *(volatile u8 *)(IO_ADDRESS(port)) = val; ++} ++ ++/*! ++ * fs_orb() - or a byte ++ * @param port ++ * @param val ++ */ ++static void __inline__ fs_orb(u32 port, u8 val) ++{ ++ u8 set = fs_rb(port) | val; ++ *(volatile u8 *)(IO_ADDRESS(port)) = set; ++} ++ ++/*! ++ * fs_andb() - and a byte ++ * @param port ++ * @param val ++ */ ++static void __inline__ fs_andb(u32 port, u8 val) ++{ ++ u8 set = fs_rb(port) & val; ++ *(volatile u8 *)(IO_ADDRESS(port)) = set; ++} ++ ++/*! ++ * fs_wl() - write a long ++ * @param port ++ * @param val ++ */ ++static void __inline__ fs_wl(u32 port, u32 val) ++{ ++ u32 set; ++ *(volatile u32 *)(IO_ADDRESS(port)) = val; ++ set = fs_rl(port); ++} ++ ++/*! ++ * fs_orl() - or a long ++ * @param port ++ * @param val ++ */ ++static void __inline__ fs_orl(u32 port, u32 val) ++{ ++ u32 set = fs_rl(port); ++ *(volatile u32 *)(IO_ADDRESS(port)) = (set | val); ++} ++ ++/*! ++ * fs_andl() - and a long ++ * @param port ++ * @param val ++ */ ++static void __inline__ fs_andl(u32 port, u32 val) ++{ ++ u32 set = fs_rl(port); ++ *(volatile u32 *)(IO_ADDRESS(port)) = (set & val); ++} ++ ++/*! ++ * fs_host_port_stat() - get host start port address for ports 1-3 ++ * @param n ++ * @returns port address ++ */ ++static u32 inline fs_host_port_stat(int n) ++{ ++ switch (n) { ++ default: ++ case 1: ++ return OTG_HOST_PORT_STATUS_1; ++ case 2: ++ return OTG_HOST_PORT_STATUS_2; ++ case 3: ++ return OTG_HOST_PORT_STATUS_3; ++ } ++} ++ ++ ++/*! ++ * fs_wl_set() - set a word and verify ++ * @param tag ++ * @param port ++ * @param val ++ */ ++static void inline fs_wl_set(otg_tag_t tag, u32 port, u32 val) ++{ ++ u32 set; ++ *(volatile u32 *)(IO_ADDRESS(port)) = val; ++ set = fs_rl(port); ++ if ((set & val) != val) { ++ TRACE_MSG1(tag, "SET FAILED: %08x", set); ++ } ++} ++ ++/*! ++ * fs_wl_clr() - clr a word and verify ++ * @param tag ++ * @param port ++ * @param clr ++ */ ++static void inline fs_wl_clr(otg_tag_t tag, u32 port, u32 clr) ++{ ++ u32 set; ++ *(volatile u32 *)(IO_ADDRESS(port)) = clr; ++ set = fs_rl(port); ++ if (set & clr) { ++ TRACE_MSG1(tag, "CLEAR FAILED 1: %08x", set); ++ *(volatile u32 *)(IO_ADDRESS(port)) = clr; ++ set = fs_rl(port); ++ if (set & clr) ++ TRACE_MSG1(tag, "CLEAR FAILED 2: %08x", set); ++ } ++} ++ ++ ++/*! ++ * fs_memcpy32() - emulage memcpy using long copy ++ * @param dp destination pointer ++ * @param sp source pointer ++ * @param words number of 32bit words to copy ++ * ++ */ ++static void inline fs_memcpy32(u32 *dp, u32 *sp, volatile int words) ++{ ++ while (words--) *dp++ = *sp++; ++} ++/*! ++ * fs_memcpy() - emulage memcpy using byte copy ++ * @param dp destination pointer ++ * @param sp source pointer ++ * @param bytes number of 8bit bytes to copy ++ */ ++static void inline fs_memcpy(u8 *dp, u8 *sp, volatile int bytes) ++{ ++ while (bytes--) *dp++ = *sp++; ++} ++/*! ++ * fs_clear_words() - clear memory ++ * @param addr address to clear from ++ * @param words number of 32bit words to clear. ++ */ ++static void inline fs_clear_words(volatile u32 *addr, int words) ++{ ++ while (words--) *addr++ = 0; ++} ++ ++/*! @} */ ++ +diff -uNr linux/drivers/no-otg/otghw/isp1301-hardware.h linux/drivers/otg/otghw/isp1301-hardware.h +--- linux/drivers/no-otg/otghw/isp1301-hardware.h 1970-01-01 01:00:00.000000000 +0100 ++++ linux/drivers/otg/otghw/isp1301-hardware.h 2006-09-01 21:41:35.000000000 +0200 +@@ -0,0 +1,218 @@ ++/* ++ * otg/otghw/isp1301-hardware.h -- ISP1301 hardware specific defines ++ * ++ * Copyright (c) 2004 Belcarra ++ * ++ * By: ++ * Stuart Lynne <sl@lbelcarra.com>, ++ * Tom Rushworth <tbr@belcarra.com>, ++ * Bruce Balden <balden@belcarra.com> ++ * ++ */ ++/*! ++ * @file otg/otghw/isp1301-hardware.h ++ * @brief Public Structures And Defines For ISP1301 Hardware. ++ * ++ * The Phillips ISP1301 is an OTG Transceiver. There a additional ++ * compatible parts such as the Maxim MAX3301E ++ * ++ * @ingroup ISP1301TCD ++ */ ++ ++ ++/*! ++ * @name ADR/PSW - C.f. 8.9.1 ++ * ++ * The i2c address is either 0x2c or 0x2d depending on the level of the ++ * ADR/PSW pin after device reset. Note that this pin can be programmed as ++ * an output via the Mode Control 2 register, bit PSW_OE to enable an ++ * @{ ++ */ ++ ++#define ISP1301_I2C_ADDR_LOW 0x2c ++#define ISP1301_I2C_ADDR_HIGH 0x2d ++/*! @} */ ++ ++/*! ++ * @name ISP1301 Registers C.f. 11.1 ++ * @{ ++ */ ++ ++#define ISP1301_VENDOR_ID 0x00 ++#define ISP1301_PRODUCT_ID 0x02 ++#define ISP1301_VERSION_ID 0x14 ++ ++#define ISP1301_VENDOR_ID_LOW 0x00 ++#define ISP1301_PRODUCT_ID_LOW 0x02 ++#define ISP1301_VERSION_ID_LOW 0x14 ++#define ISP1301_VENDOR_ID_HIGH 0x01 ++#define ISP1301_PRODUCT_ID_HIGH 0x03 ++#define ISP1301_VERSION_ID_HIGH 0x15 ++ ++/* these are all single byte registers ++ */ ++#define ISP1301_MODE_CONTROL_1 0x04 ++#define ISP1301_MODE_CONTROL_1_SET 0x04 ++#define ISP1301_MODE_CONTROL_1_CLR 0x05 ++ ++#define ISP1301_MODE_CONTROL_2 0x12 ++#define ISP1301_MODE_CONTROL_2_SET 0x12 ++#define ISP1301_MODE_CONTROL_2_CLR 0x13 ++ ++#define ISP1301_OTG_CONTROL 0x06 ++#define ISP1301_OTG_CONTROL_SET 0x06 ++#define ISP1301_OTG_CONTROL_CLR 0x07 ++ ++#define ISP1301_OTG_STATUS 0x10 ++ ++#define ISP1301_INTERRUPT_SOURCE 0x08 ++ ++#define ISP1301_INTERRUPT_LATCH 0x0a ++#define ISP1301_INTERRUPT_LATCH_SET 0x0a ++#define ISP1301_INTERRUPT_LATCH_CLR 0x0b ++ ++#define ISP1301_INTERRUPT_ENABLE_LOW 0x0c ++#define ISP1301_INTERRUPT_ENABLE_LOW_SET 0x0c ++#define ISP1301_INTERRUPT_ENABLE_LOW_CLR 0x0d ++ ++#define ISP1301_INTERRUPT_ENABLE_HIGH 0x0e ++#define ISP1301_INTERRUPT_ENABLE_HIGH_SET 0x0e ++#define ISP1301_INTERRUPT_ENABLE_HIGH_CLR 0x0f ++/*! @} */ ++ ++ ++/*! ++ * @name Mode Control 1 register - C.f. Table 17 and Table 18 ++ * @{ ++ */ ++#define ISP1301_UART_EN (1 << 6) ++#define ISP1301_OE_INT_EN (1 << 5) ++#define ISP1301_BDIS_ACON_EN (1 << 4) ++#define ISP1301_TRANSP_EN (1 << 3) ++#define ISP1301_DAT_SE0 (1 << 2) ++#define ISP1301_SUSPEND_REG (1 << 1) ++#define ISP1301_SPEED_REG (1 << 0) ++/*! @} */ ++ ++ ++/*! ++ * @name Mode Control 2 register - C.f. Table 19 and Table 20 ++ * @{ ++ */ ++#define ISP1301_EN2V7 (1 << 7) ++#define ISP1301_PSW_OE (1 << 6) ++#define ISP1301_AUDIO_EN (1 << 5) ++#define ISP1301_TRANSP_BDIR (3 << 3) ++#define ISP1301_BI_DI (1 << 2) ++#define ISP1301_SPD_SUSP_CTRL (1 << 1) ++#define ISP1301_GLOBAL_PWR_ON (1 << 0) ++/*! @} */ ++ ++ ++/*! ++ * @name OTG Control register - C.f. Table 21 and Table 22 ++ * @{ ++ */ ++#define ISP1301_VBUS_CHRG (1 << 7) ++#define ISP1301_VBUS_DISCHRG (1 << 6) ++#define ISP1301_VBUS_DRV (1 << 5) ++#define ISP1301_ID_PULLDOWN (1 << 4) ++#define ISP1301_DM_PULLDOWN (1 << 3) ++#define ISP1301_DP_PULLDOWN (1 << 2) ++#define ISP1301_DM_PULLUP (1 << 1) ++#define ISP1301_DP_PULLUP (1 << 0) ++ ++#define ISP1301_VBUS_RESET (ISP1301_VBUS_CHRG | ISP1301_VBUS_DISCHRG | ISP1301_VBUS_DRV) ++ ++/*! @} */ ++ ++ ++/*! ++ * @name OTG Status register - C.f. Table 23 and Table 24 ++ * @{ ++ */ ++#define ISP1301_B_SESS_VLD (1 << 7) ++#define ISP1301_B_SESS_END (1 << 6) ++/*! @} */ ++ ++ ++/*! ++ * @name Interrupt Source Register ++ * Interrupt Source register - C.f. Table 25 and Table 26 ++ * Interrupt Latch register - C.f. Table 27 and Table 28 ++ * Interrupt Enable Low register - C.f. Table 29 and Table 30 ++ * Interrupt Enable High register - C.f. Table 31 and Table 32 ++ * @{ ++ */ ++#define ISP1301_CR_INT (1 << 7) ++#define ISP1301_BDIS_ACON (1 << 6) ++#define ISP1301_ID_FLOAT (1 << 5) ++#define ISP1301_DM_HI (1 << 4) ++#define ISP1301_ID_GND (1 << 3) ++#define ISP1301_DP_HI (1 << 2) ++#define ISP1301_SESS_VLD (1 << 1) ++#define ISP1301_VBUS_VLD (1 << 0) ++ ++//#define ISP1301_SE1 (ISP1301_DM_HI | ISP1301_DP_HI) ++ ++/*! @} */ ++ ++/*! ++ * @name Maxim MAX3301E ++ * ++ * This part is compatible with the ISP1301 with minor differences: ++ * ++ * Mode Control 1 register is called Control Register 1. It is almost ++ * identical except for: ++ * ++ * bit isp1301 max3301e ++ * 3 transp_en not used ++ * @{ ++ */ ++#define MAX3301E_CONTROL_REGISTER_1 0x04 ++#define MAX3301E_CONTROL_REGISTER_1_SET 0x04 ++#define MAX3301E_CONTROL_REGISTER_1_CLR 0x05 ++/*! @} */ ++ ++/*! ++ * @name Mode Control 2 ++ * Mode Control 2 is called Special Function Register 1. Mostly the same ++ * except for: ++ * ++ * bit isp1301 max3301e ++ * 7 en2v7 gp_en ++ * 6 psw_oe sess_end ++ * 5 audio_en int_source ++ * @{ ++ */ ++#define MAX3301E_SPECIAL_FUNCTION_1 0x12 ++#define MAX3301E_SPECIAL_FUNCTION_1_SET 0x12 ++#define MAX3301E_SPECIAL_FUNCTION_1_CLR 0x13 ++#define MAX3301E_GP_EN (1 << 7) ++#define MAX3301E_SESS_END (1 << 6) ++#define MAX3301E_INT_SOURCE (1 << 5) ++/*! @} */ ++ ++/*! ++ * @name OTG Status ++ * The OTG Status is not present, but B_SESS_END seems to be available ++ * in the Mode Control 2 register bit 6. ++ * ++ * An additional register is available for MAX3301E specific features. ++ * ++ * Special Function Register 2 ++ * @{ ++ */ ++ ++#define MAX3301E_SPECIAL_FUNCTION_2 0x16 ++#define MAX3301E_SPECIAL_FUNCTION_2_SET 0x16 ++#define MAX3301E_SPECIAL_FUNCTION_2_CLR 0x17 ++#define MAX3301E_SDWN (1 << 0) ++#define MAX3301E_IRQ_MODE (1 << 1) ++#define MAX3301E_XCVR_INPUT_DISC (1 << 2) ++#define MAX3301E_REQ_SET (1 << 3) ++/*! @} */ ++ ++ ++ ++ +diff -uNr linux/drivers/no-otg/otghw/isp1301.h linux/drivers/otg/otghw/isp1301.h +--- linux/drivers/no-otg/otghw/isp1301.h 1970-01-01 01:00:00.000000000 +0100 ++++ linux/drivers/otg/otghw/isp1301.h 2006-09-01 21:41:35.000000000 +0200 +@@ -0,0 +1,158 @@ ++/* ++ * otg/otghw/isp1301/isp1301.h -- USB Transceiver Controller driver ++ * ++ * Copyright (c) 2004 Belcarra ++ * ++ * By: ++ * Stuart Lynne <sl@lbelcarra.com>, ++ * Tom Rushworth <tbr@belcarra.com>, ++ * Bruce Balden <balden@belcarra.com> ++ * ++ */ ++/*! ++ * @defgroup ISP1301TCD Phillips ISP1301 ++ * @ingroup tcdgroup ++ */ ++/*! ++ * @file otg/otghw/isp1301.h ++ * @brief Private structures and defines for ISP1301 Transciever Controller Driver. ++ * ++ * This file contains the private defines and structures for the ISP1301 Transceiver ++ * Driver. ++ * ++ * @ingroup ISP1301TCD ++ */ ++ ++/*! ++ * @name ISP1301 ISP1301 configuration ++ * @{ ++ */ ++ ++#ifdef CONFIG_OMAP_H2 ++#define ISP1301_GPIO (2) ++#define TCD_ISP1301_I2C_ADDR ISP1301_I2C_ADDR_HIGH; ++#endif ++ ++#define ISP1301_NAME "isp1301" ++ ++#define ISP1301_LOC_CONN 0x01 ++ ++#define ISP1301_INT_SRC 0xeb ++ ++/*! ++ * @enum otg_transceiver ++ * Define ISP1301 compatible transceivers ++ */ ++typedef enum otg_transceiver { ++ unknown_transceiver, /*!< unknown */ ++ isp1301, /*!< Phillips ISP1301 */ ++ max3301e /*!< Maxim MAX3301e */ ++} otg_transceiver_t; ++ ++/*! ++ * @struct otg_transceiver_map ++ * How to determine transceiver model and manufacturer ++ */ ++struct otg_transceiver_map { ++ otg_transceiver_t transceiver_type; /*!< transceiver type */ ++ u16 vendor; /*!< vendor number */ ++ u16 product; /*!< product number */ ++ u16 revision; /*!< revision number */ ++ char *name; /*!< name */ ++}; ++ ++/*! ++ * @struct isp1301_private ++ * Information required for operation ++ */ ++struct isp1301_private { ++ WORK_STRUCT bh; /*!< work structure for bottom half handler */ ++ u16 vendor; /*!< vendor number found */ ++ u16 product; /*!< product number found */ ++ u16 revision; /*!< revision number found */ ++ u32 flags; /*!< flags */ ++ u8 int_src; /*!< current interrupt source */ ++ struct otg_transceiver_map *transceiver_map; /*! pointer to transceiver map for this transceiver */ ++}; ++ ++/*! ++ * @enum tx_mode ++ * This defines the various ways that the ISP1301 can be ++ * wired into the host USB. ++ */ ++typedef enum tx_mode { ++ dat_se0_bidirectional, /*!< 3-Wire */ ++ vp_vm_bidirectional, /*!< 4-Wire */ ++ dat_se0_unidirectional /*!< 6-Wire */ ++} tx_mode_t; ++ ++/*! ++ * @enum spd_ctrl ++ * This defines how the speed and suspend pins are controlled ++ * for this host. ++ */ ++typedef enum spd_ctrl { ++ spd_susp_pins, /*!< controlled by SPEED and SUSPEND pins */ ++ spd_susp_reg, /*!< controled by SPEED_REG and SUSPEND_REG in Mode Control 1 register */ ++} spd_ctrl_t; ++ ++ ++extern struct tcd_ops tcd_ops; ++extern struct isp1301_private isp1301_private; ++extern struct tcd_instance *tcd_instance; ++ ++/* isp1301.c */ ++extern int isp1301_mod_init(WORK_PROC ); ++extern int isp1301_mod_init__(void ); ++extern void isp1301_mod_exit(void); ++extern void isp1301_tcd_en(struct otg_instance *, u8 ); ++extern void isp1301_chrg_vbus(struct otg_instance *, u8 ); ++extern void isp1301_drv_vbus(struct otg_instance *, u8 ); ++extern void isp1301_dischrg_vbus(struct otg_instance *, u8 ); ++extern void isp1301_dp_pullup_func(struct otg_instance *, u8 ); ++extern void isp1301_dm_pullup_func(struct otg_instance *, u8 ); ++extern void isp1301_dp_pulldown_func(struct otg_instance *, u8 ); ++extern void isp1301_dm_pulldown_func(struct otg_instance *, u8 ); ++extern void isp1301_peripheral_host_func(struct otg_instance *, u8 ); ++extern void isp1301_dm_det_func(struct otg_instance *, u8 ); ++extern void isp1301_dp_det_func(struct otg_instance *, u8 ); ++extern void isp1301_cr_det_func(struct otg_instance *, u8 ); ++extern void isp1301_bdis_acon_func(struct otg_instance *, u8 ); ++extern void isp1301_mx21_vbus_drain_func(struct otg_instance *, u8 ); ++extern void isp1301_id_pulldown_func(struct otg_instance *, u8 ); ++extern void isp1301_audio_func(struct otg_instance *, u8 ); ++extern void isp1301_uart_func(struct otg_instance *, u8 ); ++extern void isp1301_mono_func(struct otg_instance *, u8 ); ++ ++extern void isp1301_otg_timer(struct otg_instance *, u8 ); ++extern void isp1301_bh(void *); ++extern int isp1301_id (struct otg_instance *); ++extern int isp1301_vbus (struct otg_instance *); ++extern void isp1301_configure(tx_mode_t, spd_ctrl_t ); ++ ++extern void isp1301_bh_wakeup(int); ++ ++/* thread */ ++extern int isp1301_thread_init (void (bh_proc)(void *)); ++extern void isp1301_thread_exit (wait_queue_head_t *); ++extern void isp1301_thread_wakeup(int enabled, int first); ++ ++ ++ ++/* i2c-xxx.c */ ++int i2c_configure(char *name, int addr); ++extern void i2c_close(void); ++extern u8 i2c_readb(u8 ); ++extern u16 i2c_readw(u8 ); ++extern u32 i2c_readl(u8 ); ++extern void i2c_writeb(u8 , u8 ); ++ ++ ++/* ********************************************************************************************* */ ++#define TRACE_I2CB(t,r) TRACE_MSG3(t, "%-40s[%02x] %02x", #r, r, i2c_readb(r)) ++#define TRACE_I2CW(t,r) TRACE_MSG3(t, "%-40s[%02x] %04x", #r, r, i2c_readw(r)) ++#define TRACE_GPIO(t,b,r) TRACE_MSG2(t, "%-40s %04x", #r, readw(b + r)) ++#define TRACE_REGL(t,r) TRACE_MSG2(t, "%-40s %08x", #r, readl(r)) ++ ++/* @} */ ++ +diff -uNr linux/drivers/no-otg/otghw/max3353e-hardware.h linux/drivers/otg/otghw/max3353e-hardware.h +--- linux/drivers/no-otg/otghw/max3353e-hardware.h 1970-01-01 01:00:00.000000000 +0100 ++++ linux/drivers/otg/otghw/max3353e-hardware.h 2006-09-01 21:41:35.000000000 +0200 +@@ -0,0 +1,141 @@ ++/* ++ * otg/otg/max3353e-hardware.h -- MAX3353E hardware specific defines ++ * ++ * Copyright (c) 2004 Belcarra ++ * ++ * By: ++ * Stuart Lynne <sl@lbelcarra.com>, ++ * Tom Rushworth <tbr@belcarra.com>, ++ * Bruce Balden <balden@belcarra.com> ++ * ++ */ ++/*! ++ * @file otg/otg/max3353e-hardware.h ++ * @brief Public structures and defines for MAX3353E Hardware. ++ * ++ * The Maxim MAX3353E is an OTG Charge Pump with controllable ++ * pullup resistors. ++ * ++ * @ingroup MAX3353ETCD ++ */ ++ ++ ++/*! ++ * @name ADR/PSW - C.f. Slave Address ++ * ++ * The high/low address is selected with the ADD pin. ++ * ++ * @{ ++ */ ++ ++#define MAX3353E_I2C_ADDR_LOW 0x16 ++#define MAX3353E_I2C_ADDR_HIGH 0x2C ++ ++/*! @} */ ++ ++ ++ ++/*! ++ * @name MAX3353E Registers C.f. Table 2 - Register Address Map ++ * @{ ++ */ ++ ++#define MAX3353E_MANUFACTURER_REGISTER_0 0x00 ++#define MAX3353E_MANUFACTURER_REGISTER_1 0x01 ++#define MAX3353E_MANUFACTURER_REGISTER_2 0x02 ++#define MAX3353E_MANUFACTURER_REGISTER_3 0x03 ++ ++#define MAX3353E_PRODUCT_ID_REGISTER_0 0x04 ++#define MAX3353E_PRODUCT_ID_REGISTER_1 0x05 ++#define MAX3353E_PRODUCT_ID_REGISTER_2 0x06 ++#define MAX3353E_PRODUCT_ID_REGISTER_3 0x07 ++ ++#define MAX3353E_CONTROL_REGISTER_1 0x10 ++#define MAX3353E_CONTROL_REGISTER_2 0x11 ++ ++#define MAX3353E_STATUS_REGISTER 0x13 ++ ++#define MAX3353E_INTERRUPT_MASK 0x14 ++#define MAX3353E_INTERRUPT_EDGE 0x15 ++#define MAX3353E_INTERRUPT_LATCH 0x16 ++ ++/*! @} */ ++ ++ ++/*! ++ * @name Control Register 1 ++ * @{ ++ */ ++#define MAX3353E_DM_PULLDOWN (1 << 7) ++#define MAX3353E_DP_PULLDOWN (1 << 6) ++#define MAX3353E_DM_PULLUP (1 << 5) ++#define MAX3353E_DP_PULLUP (1 << 4) ++ ++#define MAX3353E_BDISC_ACONN (1 << 2) ++#define MAX3353E_IRQ_MODE (1 << 1) ++ ++/*! @} */ ++ ++/*! ++ * @name Control Register 2 ++ * @{ ++ */ ++#define MAX3353E_VBUS_DISCHG (1 << 4) ++#define MAX3353E_VBUS_DRV (1 << 3) ++#define MAX3353E_VBUS_CHG2 (1 << 2) ++#define MAX3353E_VBUS_CHG1 (1 << 1) ++#define MAX3353E_DSWIN (1 << 0) ++ ++/*! @} */ ++ ++/*! ++ * @name Status Register ++ * @{ ++ */ ++#define MAX3353E_B_HNP (1 << 6) ++#define MAX3353E_A_HNP (1 << 5) ++#define MAX3353E_ID_FLOAT (1 << 4) ++#define MAX3353E_ID_GND (1 << 3) ++#define MAX3353E_SESSION_END (1 << 2) ++#define MAX3353E_SESSION_VALID_EN (1 << 1) ++#define MAX3353E_VBUS_VALID (1 << 0) ++ ++/*! @} */ ++ ++/*! ++ * @name Interrupt Mask ++ * @{ ++ */ ++#define MAX3353E_A_HNP_EN (1 << 5) ++#define MAX3353E_ID_FLOAT_EN (1 << 4) ++#define MAX3353E_ID_GND_EN (1 << 3) ++#define MAX3353E_SESSION_END_EN (1 << 2) ++#define MAX3353E_SESSION_VALID_EN (1 << 1) ++#define MAX3353E_VBUS_VALID_EN (1 << 0) ++ ++/*! @} */ ++ ++/*! ++ * @name Interrupt Edge ++ * @{ ++ */ ++#define MAX3353E_SESSION_VALID_ED (1 << 1) ++#define MAX3353E_VBUS_VALID_ED (1 << 0) ++ ++/*! @} */ ++ ++/*! ++ * @name Interrupt Latch ++ * @{ ++ */ ++#define MAX3353E_A_HNP_RQ (1 << 7) ++#define MAX3353E_ID_FLOAT_RQ (1 << 6) ++#define MAX3353E_ID_GND_RQ (1 << 5) ++#define MAX3353E_SESSION_END_RQ (1 << 4) ++#define MAX3353E_SESSION_VALID_RN (1 << 3) ++#define MAX3353E_VBUS_VALID_RN (1 << 2) ++#define MAX3353E_SESSION_VALID_RP (1 << 1) ++#define MAX3353E_VBUS_VALID_RP (1 << 0) ++ ++/*! @} */ ++ +diff -uNr linux/drivers/no-otg/otghw/mx2ads-hardware.h linux/drivers/otg/otghw/mx2ads-hardware.h +--- linux/drivers/no-otg/otghw/mx2ads-hardware.h 1970-01-01 01:00:00.000000000 +0100 ++++ linux/drivers/otg/otghw/mx2ads-hardware.h 2006-09-01 21:41:35.000000000 +0200 +@@ -0,0 +1,111 @@ ++/* ++ * otghw/mx2ads-hardware.h ++ * ++ * Copyright (c) 2004 Belcarra ++ * ++ * By: ++ * Stuart Lynne <sl@belcarra.com>, ++ * Tom Rushworth <tbr@belcarra.com>, ++ * Bruce Balden <balden@belcarra.com> ++ * ++ */ ++/*! ++ * @file otghw/mx2ads-hardware.h ++ * @brief MX2ADS Hardware constants ++ * ++ * @ingroup MX2ADS ++ */ ++ ++ ++/*! ++ * @name MX2ETDS ++ * R1 = i.MX21 Application Processor Reference Manual Rev 0.6 2003/11/01 TO 1.0 ++ * (aka "IMX21RM_TO1.pdf") ++ * R2 = "USBOTG_L3_Specification_v.C1.0.pdf" ++ * @{ ++ */ ++ ++#define NUM_ETDS 32 ++#define DATA_BUFF_SIZE 64 ++#define DATA_BUFFER_TOTAL 4096 ++#define NUM_DATA_BUFFS (4096/DATA_BUFF_SIZE) ++//#define LITTLE_ENDIAN 1 ++ ++/* @} */ ++ ++ ++/*! ++ * @name MX2Interrupts ++ * @{ ++ */ ++#define OTG_USBWKUP 53 ++#define OTG_USBDMA 54 ++#define OTG_USBHOST 55 ++#define OTG_USBFUNC 56 ++#define OTG_USBHNP 57 ++#define OTG_USBCTRL 58 ++/* @} */ ++ ++/*! ++ * @name AHBMemoryMap ++ * C.F. Table 23-5 AHB Memory Map ++ * @{ ++ */ ++#ifndef OTG_BASE_ADDR ++#define OTG_BASE_ADDR 0x10024000 ++ ++#define OTG_CORE_BASE (OTG_BASE_ADDR+0x000) // base location for core ++#define OTG_FUNC_BASE (OTG_BASE_ADDR+0x040) // base location for function ++#define OTG_HOST_BASE (OTG_BASE_ADDR+0x080) // base location for host ++#define OTG_I2C_BASE (OTG_BASE_ADDR+0x100) // base location for I2C ++ ++#define OTG_ETD_BASE (OTG_BASE_ADDR+0x200) // base location for etd memory ++#define OTG_EP_BASE (OTG_BASE_ADDR+0x400) // base location for ep memory ++#define OTG_SYS_BASE (OTG_BASE_ADDR+0x600) // base location for system ++ ++#define OTG_DMA_BASE (OTG_BASE_ADDR+0x800) // base location for dma ++ ++#define OTG_DATA_BASE (OTG_BASE_ADDR+0x1000) // base location for data memory ++ ++#define OTG_SYS_CTRL (OTG_SYS_BASE+0x000) // base location for system ++#endif ++/* @} */ ++ ++/*! ++ * @name I2CTransceiver ++ * C.f. 23.14.10 I2C OTG Transceiver Controller Registers ++ * These are the I2C controller and access registers. ++ * ++ * N.B. I2C_ERROR is not documented. ++ * @{ ++ */ ++ ++ ++#define I2C_BUSY (1 << 7) ++#define I2C_ERROR (1 << 2) ++#define I2C_HWSMODE (1 << 1) ++#define I2C_I2COE (1 << 0) ++ ++#define I2C_SCLK_TO_SCL_DIVISION (OTG_I2C_BASE+0x1E) ++ ++#define I2C_INTERRUPT_AND_CONTROL (OTG_I2C_BASE+0x1F) ++ ++#define I2C_STATUS_MASK (0x7) ++ ++#define I2C_NOACK_EN (1 << 6) ++#define I2C_RWREADY_EN (1 << 5) ++#define I2C_OTGXCVRINT_EN (1 << 4) ++ ++#define I2C_NOACK (1 << 2) ++#define I2C_RWREADY (1 << 1) ++#define I2C_OTGXCVRINT (1 << 0) ++ ++#define MX2_OTG_XCVR_DEVAD OTG_I2C_BASE+0x18 ++#define MX2_SEQ_OP_REG OTG_I2C_BASE+0x19 ++#define MX2_SEQ_RD_STARTAD OTG_I2C_BASE+0x1a ++#define MX2_I2C_OP_CTRL_REG OTG_I2C_BASE+0x1b ++#define MX2_SCLK_TO_SCL_HPER OTG_I2C_BASE+0x1e ++#define MX2_I2C_INTERRUPT_AND_CTRL OTG_I2C_BASE+0x1f ++ ++/* @} */ ++ +diff -uNr linux/drivers/no-otg/otghw/mx2ads.h linux/drivers/otg/otghw/mx2ads.h +--- linux/drivers/no-otg/otghw/mx2ads.h 1970-01-01 01:00:00.000000000 +0100 ++++ linux/drivers/otg/otghw/mx2ads.h 2006-09-01 21:41:35.000000000 +0200 +@@ -0,0 +1,46 @@ ++/* ++ * otghw/mx2ads.h ++ * ++ * Copyright (c) 2004 Belcarra ++ * ++ * By: ++ * Stuart Lynne <sl@belcarra.com>, ++ * Tom Rushworth <tbr@belcarra.com>, ++ * Bruce Balden <balden@belcarra.com> ++ */ ++ ++/*! ++ * @defgroup MX2ADS Freescale MX2ADS ++ * @ingroup platformgroup ++ */ ++/*! ++ * @file otghw/mx2ads.h ++ * @brief MX2ADS Platform constants ++ * ++ * Notes ++ * ++ * 1. Interrupts - we seem to get all interrupts on the ctrl interrupt. ++ * ++ * 2. Clearing - it appears that clearing the status bits sometimes fails, This ++ * has been observed in SINT_STAT and XFILL_STAT. ++ * ++ * 3. Double buffer - without DMA it does not appear to be possible to use ++ * both X and Y buffers for OUT transfers because we can not apparantely guarantee ++ * the order that the X and Y buffers will be used. If we get an interrupt with ++ * both X and Y active we cannot determine which order to use them. As long as ++ * we get each interrupt fast enough this is not a problem as we can then just ++ * use whichever is active. ++ * ++ * 4. Remote Wakeup - cannot set RSMINPROG bit ++ * ++ * @ingroup MX2ADS ++ */ ++ ++ ++#define UDC_NAME "MX21" ++#define UDC_MAX_ENDPOINTS 32 ++#define EP0_PACKETSIZE 64 ++ ++ ++extern otg_tag_t MX2; ++ +diff -uNr linux/drivers/no-otg/otghw/td243-hardware.h linux/drivers/otg/otghw/td243-hardware.h +--- linux/drivers/no-otg/otghw/td243-hardware.h 1970-01-01 01:00:00.000000000 +0100 ++++ linux/drivers/otg/otghw/td243-hardware.h 2006-09-01 21:41:35.000000000 +0200 +@@ -0,0 +1,709 @@ ++/* ++ * otg/pcd/mx2/td243-hardware.h ++ * ++ * Copyright (c) 2004 Belcarra ++ * ++ * By: ++ * Stuart Lynne <sl@belcarra.com>, ++ * Tom Rushworth <tbr@belcarra.com>, ++ * Bruce Balden <balden@belcarra.com> ++ * ++ */ ++/*! ++ * @defgroup TD243 Transdimension USBOTG ++ * @ingroup libgroup ++ */ ++/*! ++ * @file /otg/otghw/td243-hardware.h ++ * @brief Hardware defines for Transimension USBOTG Hardware ++ * ++ * @ingroup TD243 ++ */ ++ ++ ++/* ++ * C.f. 23.8 USB Control Register ++ */ ++ ++#define SYS_CTRL_I2C_WU_INT_STAT (1 << 27) ++#define SYS_CTRL_OTG_WU_INT_STAT (1 << 26) ++#define SYS_CTRL_HOST_WU_INT_STAT (1 << 25) ++#define SYS_CTRL_FNT_WU_INT_STAT (1 << 24) ++ ++#define SYS_CTRL_I2C_WU_INT_EN (1 << 19) ++#define SYS_CTRL_OTG_WU_INT_EN (1 << 18) ++#define SYS_CTRL_HOST_WU_INT_EN (1 << 17) ++#define SYS_CTRL_FNT_WU_INT_EN (1 << 16) ++ ++#define SYS_CTRL_OTG_BYP_VAL (0x3 << 11) ++#define SYS_CTRL_HOST1_BYP_VAL (0x3 << 9) ++ ++ ++ ++#define SYS_CTRL_OTG_PWR_MASK (1 << 6) ++#define SYS_CTRL_HOST1_PWR_MASK (1 << 6) ++#define SYS_CTRL_HOST2_PWR_MASK (1 << 4) ++#define SYS_CTRL_USB_BYP (1 << 2) ++#define SYS_CTRL_HOST1_TXEN_OE (1 << 1) ++ ++ ++/* ++ * C.f. 23.9 USBOTG Module Registers ++ */ ++ ++#define OTG_CORE_HWMODE (OTG_CORE_BASE+0x00) // 32bit core hardware mode reg ++ ++#define XCVR_D_D 0x00 ++#define XCVR_SE_D 0x01 ++#define XCVR_D_SE 0x02 ++#define XCVR_SE_SE 0x03 ++ ++#define MODULE_ANASDBEN (1 << 14) ++#define MODULE_OTGXCVR (0x3 << 6) ++#define MODULE_HOSTXCVR (0x3 << 4) ++#define MODULE_CRECFG (0x3) ++#define MODULE_CRECFG_HHNP (0x0) ++#define MODULE_CRECFG_HOST (0x1) ++#define MODULE_CRECFG_FUNC (0x2) ++#define MODULE_CRECFG_SHNP (0x3) ++ ++#define OTG_CORE_CINT_STAT (OTG_CORE_BASE+0x04) // 32bit core int status reg ++ ++#define MODULE_FCINTDSPEN (1 << 6) ++ ++#define MODULE_ASHNPINT (1 << 5) ++#define MODULE_ASFCINT (1 << 4) ++#define MODULE_ASHCINT (1 << 3) ++#define MODULE_HNPINT (1 << 2) ++#define MODULE_FCINT (1 << 1) ++#define MODULE_HCINT (1) ++ ++#define OTG_CORE_CINT_STEN (OTG_CORE_BASE+0x08) // 32bit core int enable reg ++ ++#define MODULE_ASHNPINT_EN (1 << 5) ++#define MODULE_ASFCINT_EN (1 << 4) ++#define MODULE_ASHCINT_EN (1 << 3) ++#define MODULE_HNPINT_EN (1 << 2) ++#define MODULE_FCINT_EN (1 << 1) ++#define MODULE_HCINT_EN (1) ++ ++#define OTG_CORE_CLK_CTRL (OTG_CORE_BASE+0x0C) // 32bit core clock control reg ++ ++#define MODULE_FUNC_CLK (1 << 2) ++#define MODULE_HOST_CLK (1 << 1) ++#define MODULE_MAIN_CLK (1) ++ ++#define OTG_CORE_RST_CTRL (OTG_CORE_BASE+0x10) // 32bit core reset control reg ++ ++#define MODULE_RSTI2C (1 << 15) ++#define MODULE_RSTCTRL (1 << 5) ++#define MODULE_RSTFC (1 << 4) ++#define MODULE_RSTFSIE (1 << 3) ++#define MODULE_RSTRH (1 << 2) ++#define MODULE_RSTHSIE (1 << 1) ++#define MODULE_RSTHC (1) ++ ++#define OTG_CORE_FRM_INTVL (OTG_CORE_BASE+0x14) // 32bit core frame interval reg ++ ++#define MODULE_RESET_FRAME (1 << 15) ++ ++#define OTG_CORE_FRM_REMAIN (OTG_CORE_BASE+0x18) // 32bit core frame remaining reg ++ ++#define OTG_CORE_HNP_CSTAT (OTG_CORE_BASE+0x1C) // 32bit core HNP current state reg ++ ++#define MODULE_HNPDAT (1 << 30) ++#define MODULE_VBUSBSE (1 << 29) ++#define MODULE_VBUSABSV (1 << 28) ++#define MODULE_VBUSGTAVV (1 << 27) ++ ++#define MODULE_ARMTHNPE (1 << 25) ++#define MODULE_BHNPEN (1 << 24) ++ ++#define MODULE_SLAVE (1 << 22) ++#define MODULE_MASTER (1 << 21) ++#define MODULE_BGEN (1 << 20) ++#define MODULE_CMPEN (1 << 19) ++#define MODULE_ISBDEV (1 << 18) ++#define MODULE_ISADEV (1 << 17) ++ ++#define MODULE_SWVBUSPUL (1 << 15) ++ ++#define MODULE_SWAUTORST (1 << 12) ++#define MODULE_SWPUDP (1 << 11) ++ ++#define MODULE_SWPDDM (1 << 9) ++#define MODULE_HNPSTATE (0x1f << 8) ++#define MODULE_CLRERROR (1 << 3) ++#define MODULE_ADROPBUS (1 << 2) ++#define MODULE_ABBUSREQ (1 << 1) ++ ++#define HNP_A_IDLE (0x10 << 8) ++#define HNP_A_MASTER (0x11 << 8) ++#define HNP_A_SLAVE (0x12 << 8) ++#define HNP_A_WAIT_VPULSE (0x13 << 8) ++ ++#define HNP_A_WAIT_DPULSE (0x14 << 8) ++#define HNP_A_WAIT_CONN_A (0x15 << 8) ++#define HNP_A_WAIT_CONN_B (0x16 << 8) ++#define HNP_A_WAIT_VRISE (0x17 << 8) ++#define HNP_A_SUSPEND (0x18 << 8) ++#define HNP_A_WAIT_VFALL (0x19 << 8) ++#define HNP_A_VBUS_ERR (0x1a << 8) ++#define HNP_CONN_DEBOUNCE (0x1b << 8) ++#define HNP_A_WAIT_ABREQ (0x1c << 8) ++#define HNP_B_IDLE (0x00 << 8) ++#define HNP_B_MASTER (0x01 << 8) ++#define HNP_B_SLAVE (0x02 << 8) ++#define HNP_B_SRP_INIT_V (0x03 << 8) ++#define HNP_B_SRP_INIT_D (0x04 << 8) ++#define HNP_B_PRE_WAIT_CONN_AB_SHORT_DB (0x05 << 8) ++#define HNP_B_WAIT_CONN_A (0x06 << 8) ++#define HNP_B_PRE_SLAVE (0x0a << 8) ++ ++ ++#define OTG_CORE_HNP_TIMER1 (OTG_CORE_BASE+0x20) // 32bit core HNP timer 1 reg ++#define OTG_CORE_HNP_TIMER2 (OTG_CORE_BASE+0x24) // 32bit core HNP timer 2 reg ++#define OTG_CORE_HNP_T3PCR (OTG_CORE_BASE+0x28) // 32bit core HNP timer 3 pulse ctrl ++ ++#define HNP_DATAPULSE (1 << 5) ++#define HNP_VBUSPULSE (1 << 4) ++ ++#define OTG_CORE_HINT_STAT (OTG_CORE_BASE+0x2C) // 32bit core HNP int status reg ++ ++#define HNP_I2COTGINT (1 << 15) ++#define HNP_AWAITBTO (1 << 8) // Hardware HNP Only ++#define HNP_AIDLEBDTO (1 << 7) // Hardware HNP Only ++#define HNP_SRPSUCFAIL (1 << 6) // Hardware HNP Only ++#define HNP_SRPINT (1 << 5) ++#define HNP_VBUSERROR (1 << 4) // Hardware HNP Only ++#define HNP_ABSEVAILD (1 << 3) ++#define HNP_ABUSVALID (1 << 2) ++#define HNP_MASSLVCHG (1 << 1) // Hardware HNP Only ++#define HNP_IDCHANGE (1) ++ ++#define OTG_CORE_HINT_STEN (OTG_CORE_BASE+0x30) // 32bit core HNP int enable reg ++ ++#define HNP_I2COTGINT_EN (1 << 15) ++#define HNP_AWAITBTO_EN (1 << 8) ++#define HNP_AIDLEBDTO_EN (1 << 7) ++#define HNP_SRPSUCFAIL_EN (1 << 6) ++#define HNP_SRPINT_EN (1 << 5) ++#define HNP_VBUSERROR_EN (1 << 4) ++#define HNP_ABSEVAILD_EN (1 << 3) ++#define HNP_ABUSVALID_EN (1 << 2) ++#define HNP_MASSLVCHG_EN (1 << 1) ++#define HNP_IDCHANGE_EN (1) ++ ++ ++#define OTG_CORE_CPUEPSEL_STAT (OTG_CORE_BASE+0x34) ++#define OTG_CORE_INTERRUPT_STEN (OTG_CORE_BASE+0x3c) ++ ++ ++/* ++ * C.f. 23.11.11 Host Registers ++ */ ++ ++ ++#define OTG_HOST_CONTROL (OTG_HOST_BASE+0x00) // 32bit host controller config reg ++ ++#define HOST_CONTROL_HCRESET (1 << 31) ++#define HOST_CONTROL_RMTWUEN (1 << 4) ++ ++#define HOST_CONTROL_HCUSBSTE_RESET (0 << 2) ++#define HOST_CONTROL_HCUSBSTE_RESUME (1 << 2) ++#define HOST_CONTROL_HCUSBSTE_OPERATIONAL (2 << 2) ++#define HOST_CONTROL_HCUSBSTE_SUSPEND (3 << 2) ++ ++#define HOST_CONTROL_CTLBLKSR_11 (0) ++#define HOST_CONTROL_CTLBLKSR_21 (1) ++#define HOST_CONTROL_CTLBLKSR_41 (2) ++#define HOST_CONTROL_CTLBLKSR_81 (3) ++ ++ ++#define OTG_HOST_SINT_STAT (OTG_HOST_BASE+0x08) // 32bit host system int status reg ++ ++#define HOST_PSCINT (1 << 6) // Port Status Change ++#define HOST_FMOFINT (1 << 5) // Frame Number Overflow ++#define HOST_HERRINT (1 << 4) // Host Error ++#define HOST_RESDETINT (1 << 3) // Resume Detected ++#define HOST_SOFINT (1 << 2) // Start of Frame ++#define HOST_DONEINT (1 << 1) // Done Register ++#define HOST_SORINT (1) // Schedule Overrun ++ ++#define OTG_HOST_SINT_STEN (OTG_HOST_BASE+0x0C) // 32bit host system int enable reg ++ ++#define HOST_PSCINT_EN (1 << 6) // Port Status Change ++#define HOST_FMOFINT_EN (1 << 5) // Frame Number Overflow ++#define HOST_HERRINT_EN (1 << 4) // Host Error ++#define HOST_RESDETINT_EN (1 << 3) // Resume Detected ++#define HOST_SOFINT_EN (1 << 2) // Start of Frame ++#define HOST_DONEINT_EN (1 << 1) // Done Register ++#define HOST_SORINT_EN (1) // Schedule Overrun ++ ++#define OTG_HOST_XINT_STAT (OTG_HOST_BASE+0x18) // 32bit host X buf int status reg ++#define OTG_HOST_YINT_STAT (OTG_HOST_BASE+0x1C) // 32bit host Y buf int status reg ++ ++#define OTG_HOST_XYINT_STEN (OTG_HOST_BASE+0x20) // 32bit host XY buf int enable reg ++#define OTG_HOST_XFILL_STAT (OTG_HOST_BASE+0x28) // 32bit host X filled status reg ++#define OTG_HOST_YFILL_STAT (OTG_HOST_BASE+0x2C) // 32bit host Y filled status reg ++ ++#define OTG_HOST_ETD_EN (OTG_HOST_BASE+0x40) // 32bit host ETD enables reg ++#define OTG_HOST_DIR_ROUTE (OTG_HOST_BASE+0x48) // 32bit host direct routing reg ++#define OTG_HOST_IINT (OTG_HOST_BASE+0x4C) // 32bit host immediate interrupt reg ++ ++#define OTG_HOST_EP_DSTAT (OTG_HOST_BASE+0x50) // 32bit host endpoints done status ++#define OTG_HOST_ETD_DONE (OTG_HOST_BASE+0x54) // 32bit host ETD done reg ++ ++#define OTG_HOST_FRM_NUM (OTG_HOST_BASE+0x60) // 32bit host frame number reg ++#define OTG_HOST_LSP_THRESH (OTG_HOST_BASE+0x64) // 32bit host low speed threshold reg ++ ++#define OTG_HOST_ROOTHUB_DESCA (OTG_HOST_BASE+0x68) // 32bit host root hub descriptor A ++#define OTG_HOST_ROOTHUB_DESCB (OTG_HOST_BASE+0x6C) // 32bit host root hub descriptor B ++ ++/* ++ * C.f. 23.11.29 ++ */ ++#define OTG_HOST_ROOTHUB_STATUS (OTG_HOST_BASE+0x70) // 32bit host root hub status reg ++ ++#define ROOTHUB_STATUS_CLRMTWUE (1 << 31) // Clear Remote Wakeup Enable ++#define ROOTHUB_STATUS_OVRCURCHG (1 << 17) // Over Current Indicator Change ++#define ROOTHUB_STATUS_LOCPWRSC (1 << 16) // Local Power Status Change ++#define ROOTHUB_STATUS_DEVCONWUE (1 << 15) // Device Connect Wakeup Enable ++#define ROOTHUB_STATUS_OVRCURI (1 << 1) // Over Current Indicator ++#define ROOTHUB_STATUS_LOCPWRS (1) // Local Power Status ++ ++ ++/* ++ * C.f. 23.11.30 ++ */ ++#define OTG_HOST_PORT_STATUS_1 (OTG_HOST_BASE+0x74) // 32bit host port 1 status bits ++#define OTG_HOST_PORT_STATUS_2 (OTG_HOST_BASE+0x78) // 32bit host port 2 status bits ++#define OTG_HOST_PORT_STATUS_3 (OTG_HOST_BASE+0x7c) // 32bit host port 3 status bits ++ ++#define PORT_STATUS_PRTRSTSC (1 << 20) // Port Reset Status Change ++#define PORT_STATUS_OVRCURIC (1 << 19) // Port Over Current Indicator Change ++#define PORT_STATUS_PRTSTATSC (1 << 18) // Port Suspend Status Change ++#define PORT_STATUS_PRTENBLSC (1 << 17) // Port Enable Status Change ++#define PORT_STATUS_CONNECTSC (1 << 16) // Connect Status Change ++ ++#define PORT_STATUS_LSDEVCON (1 << 9) // Low Speed Device Attached ++#define PORT_STATUS_PRTPWRST (1 << 8) // Port Power Status ++ ++#define PORT_STATUS_PRTRSTST (1 << 4) // Port Reset Status ++#define PORT_STATUS_PRTOVRCURI (1 << 3) // Port Over Current Indicator ++#define PORT_STATUS_PRTSUSPST (1 << 2) // Port Suspend Status ++#define PORT_STATUS_PRTENABST (1 << 1) // Port Enable Status ++#define PORT_STATUS_CURCONST (1) // Current Connect Status ++ ++ ++/* flags are the same for the interrupt, control and bulk transfer descriptors */ ++// R1: sec 23.11.10.2,3 pg 23-44,47 ++#define ETD_GET_COMPCODE(flags) ((flags >> 12) & 0xf) ++// R1: sec 23.11.10 Table 23-23 pg 23-40 ++#define ETD_CC_NOERROR 0x0 ++#define ETD_CC_CRCERR 0x1 ++#define ETD_CC_BITSTUFFERR 0x2 ++#define ETD_CC_DATATOGERR 0x3 ++#define ETD_CC_STALL 0x4 ++#define ETD_CC_DEVNOTRESPONDING 0x5 ++#define ETD_CC_PIDFAILURE 0x6 ++// Reserved 0x7 ++#define ETD_CC_DATAOVERRUN 0x8 ++#define ETD_CC_DATAUNDERRUN 0x9 ++#define ETD_CC_ACK 0xA ++#define ETD_CC_NAK 0xB ++#define ETD_CC_BUFFEROVERRUN 0xC ++#define ETD_CC_BUFFERUNDERRUN 0xD ++#define ETD_CC_SCHEDOVERRUN 0xE ++#define ETD_CC_NOTACCESSED 0xF ++ ++// R1: sec 23.11.10.2,3 pg 23-44,47 (contd) ++#define ETD_GET_ERRORCNT(flags) (((flags) >> 8) & 0xf) ++#define ETD_GET_DATATOGL(flags) (((flags) >> 6) & 0x3) ++#define ETD_SET_DATATOGL(v) (((v) & 0x3) << 6) ++#define ETD_GET_DELAYINT(flags) (((flags) >> 3) & 0x7) ++#define ETD_SET_DELAYINT(v) (((v) & 0x7) << 3) ++#define ETD_GET_BUFROUND(flags) (((flags) >> 2) & 0x1) ++#define ETD_SET_BUFROUND(v) (((v) & 0x1) << 2) ++#define ETD_GET_DIRPID(flags) ((flags) & 0x3) ++#define ETD_SET_DIRPID(v) ((v) & 0x3) ++#define ETD_FLIP_DIRPID(flags) ((flags) ^ 0x3) /* xor with 0x3 flips 2<->1 (IN<->OUT) */ ++#define ETD_DIRPID_SETUP 0x0 // OUT ++#define ETD_DIRPID_OUT 0x1 ++#define ETD_DIRPID_IN 0x2 ++#define ETD_DIRPID_RESERVED 0x3 ++ ++// For non-isoc td.w3.val - ++#define ETD_GET_BUFSIZE(w3) (((w3) >> 21) & 0xfff) ++#define ETD_SET_BUFSIZE(v) (((v) & 0xfff) << 21) ++#define ETD_GET_TOTBYECNT(w3) ((w3) & 0xfffff) ++#define ETD_SET_TOTBYECNT(v) ((v) & 0xfffff) ++ ++/* isoc flags shares the COMPCODE and DELAYINT bitfields with the other TDs, ++ but the remaining bits are different. */ ++#define ETD_GET_AUTOISO(flags) (((flags) >> 11) & 0x1) ++#define ETD_SET_AUTOISO(v) (((v) & 0x1) << 11) ++#define ETD_GET_FRAMECNT(flags) (((flags) >> 8) & 0x1) ++#define ETD_SET_FRAMECNT(v) (((v) & 0x1) << 8) ++// For isoc td.w3.val - ++/* pkt1 and pkt0 use the COMPCODE bitfield, and add a PKTLEN field. */ ++#define ETD_GET_PKTLEN(pkt) ((pkt) & 0x3ff) ++#define ETD_SET_PKTLEN(v) ((v) & 0x3ff) ++ ++ ++// ETD endpoint descriptor (etd->epd) bitfield access ++// R1: sec 23.11.10.1 pg 23-41 (word0) ++#define ETD_GET_SNDNAK(epd) (((epd) >> 30) & 0x1) ++#define ETD_SET_SNDNAK(v) (((v) & 0x1) << 30) ++#define ETD_GET_TOGCRY(epd) (((epd) >> 28) & 0x1) ++#define ETD_SET_TOGCRY(v) (((v) & 0x1) << 28) ++#define ETD_GET_HALTED(epd) (((epd) >> 27) & 0x1) ++#define ETD_SET_HALTED(v) (((v) & 0x1) << 27) ++#define ETD_GET_MAXPKTSIZ(epd) (((epd) >> 16) & 0x3ff) ++#define ETD_SET_MAXPKTSIZ(v) (((v) & 0x3ff) << 16) ++#define ETD_GET_FORMAT(epd) (((epd) >> 14) & 0x3) ++#define ETD_SET_FORMAT(v) (((v) & 0x3) << 14) ++#define ETD_FORMAT_CONTROL 0x0 ++#define ETD_FORMAT_ISOC 0x1 ++#define ETD_FORMAT_BULK 0x2 ++#define ETD_FORMAT_INTERRUPT 0x3 ++#define ETD_GET_SPEED(epd) (((epd) >> 13) & 0x1) ++#define ETD_SET_SPEED(v) (((v) & 0x1) << 13) ++#define ETD_SPEED_FULL 0x0 ++#define ETD_SPEED_LOW 0x1 ++#define ETD_GET_DIRECT(epd) (((epd) >> 11) & 0x3) ++#define ETD_SET_DIRECT(v) (((v) & 0x3) << 11) ++/* xor with 3 flips 2<->1 */ ++#define ETD_FLIP_DIRECT(epd) ((epd) ^ (0x3 << 11)) ++#define ETD_DIRECT_TD00 0x0 ++#define ETD_DIRECT_OUT 0x1 ++#define ETD_DIRECT_IN 0x2 ++#define ETD_DIRECT_TD11 0x3 ++#define ETD_GET_ENDPNT(epd) (((epd) >> 7) & 0xf) ++#define ETD_SET_ENDPNT(v) (((v) & 0xf) << 7) ++#define ETD_GET_ADDRESS(epd) ((epd) & 0x7f) ++#define ETD_SET_ADDRESS(v) ((v) & 0x7f) ++ ++// etd_urb_state[] values ++#define ETD_URB_COMPLETED 0 ++#define ETD_URB_SETUP_STATUS 1 ++#define ETD_URB_SETUP_DATA 2 ++#define ETD_URB_SETUP_START 3 ++#define ETD_URB_BULK_START 4 ++#define ETD_URB_BULKWZLP 5 ++#define ETD_URB_BULKWZLP_START 6 ++#define ETD_URB_INTERRUPT_START 7 ++#define ETD_URB_ISOC_START 8 ++ ++ ++/* ++ * C.f. 23.12.8 Function Registers ++ */ ++ ++#define OTG_FUNC_CMD_STAT (OTG_FUNC_BASE+0x00) // 32bit func command status reg ++ ++#define COMMAND_SOFTRESET (1 << 7) ++#define COMMAND_BADISOAP (1 << 3) ++#define COMMAND_SUPDET (1 << 2) ++#define COMMAND_RSMINPROG (1 << 1) ++#define COMMAND_RESETDET (1) ++ ++ ++#define OTG_FUNC_DEV_ADDR (OTG_FUNC_BASE+0x04) // 32bit func device address reg ++ ++#define OTG_FUNC_SINT_STAT (OTG_FUNC_BASE+0x08) // 32bit func system int status reg ++ ++#define SYSTEM_DONEREGINTDS (1 << 5) ++#define SYSTEM_SOFDETINT (1 << 4) ++#define SYSTEM_DONEREGINT (1 << 3) ++#define SYSTEM_SUSPDETINT (1 << 2) ++#define SYSTEM_RSMFININT (1 << 1) ++#define SYSTEM_RESETINT (1) ++ ++#define OTG_FUNC_SINT_STEN (OTG_FUNC_BASE+0x0C) // 32bit func system int enable reg ++ ++#define SYSTEM_DONEREGINTDS_EN (1 << 5) ++#define SYSTEM_SOFDETINT_EN (1 << 4) ++#define SYSTEM_DONEREGINT_EN (1 << 3) ++#define SYSTEM_SUSPDETINT_EN (1 << 2) ++#define SYSTEM_RSMFININT_EN (1 << 1) ++#define SYSTEM_RESETINT_EN (1) ++ ++ ++#define OTG_FUNC_XINT_STAT (OTG_FUNC_BASE+0x10) // 32bit func X buf int status reg ++#define OTG_FUNC_YINT_STAT (OTG_FUNC_BASE+0x14) // 32bit func Y buf int status reg ++#define OTG_FUNC_XYINT_STEN (OTG_FUNC_BASE+0x18) // 32bit func XY buf int enable reg ++#define OTG_FUNC_XFILL_STAT (OTG_FUNC_BASE+0x1C) // 32bit func X filled status reg ++ ++#define OTG_FUNC_YFILL_STAT (OTG_FUNC_BASE+0x20) // 32bit func Y filled status reg ++#define OTG_FUNC_EP_EN (OTG_FUNC_BASE+0x24) // 32bit func endpoints enable reg ++#define OTG_FUNC_EP_RDY (OTG_FUNC_BASE+0x28) // 32bit func endpoints ready reg ++#define OTG_FUNC_IINT (OTG_FUNC_BASE+0x2C) // 32bit func immediate interrupt reg ++ ++#define OTG_FUNC_EP_DSTAT (OTG_FUNC_BASE+0x30) // 32bit func endpoints done status ++#define OTG_FUNC_EP_DEN (OTG_FUNC_BASE+0x34) // 32bit func endpoints done enable ++#define OTG_FUNC_EP_TOGGLE (OTG_FUNC_BASE+0x38) // 32bit func endpoints toggle bits ++#define OTG_FUNC_FRM_NUM (OTG_FUNC_BASE+0x3C) // 32bit func frame number reg ++ ++ ++#define EP0_STALL (1 << 31) ++#define EP0_SETUP (1 << 30) ++#define EP0_OVERRUN (1 << 29) ++#define EP0_AUTOISO (1 << 27) ++ ++ ++/* generic endpoint register manipulations ++ */ ++#define EP_OUT 0x1 ++#define EP_IN 0x2 ++#define EP_BOTH 0x3 ++ ++#define ETD_MASK(n) (1 << n) ++#define ep_num_both(n) (EP_BOTH << n) ++#define ep_num_dir(n, dir) ((dir ? EP_IN : EP_OUT) << (n*2)) ++#define ep_num_out(n) ep_num_dir(n, USB_DIR_OUT) ++#define ep_num_in(n) ep_num_dir(n, USB_DIR_IN) ++ ++ ++/* ep descriptor access ++ */ ++static __inline__ u32 etd_word(int n, int word) ++{ ++ u32 offset = n * 16; ++ offset += word * 4; ++ return OTG_ETD_BASE + offset; ++} ++ ++/* ep descriptor access ++ */ ++static __inline__ u32 ep_word(int n, int dir, int word) ++{ ++ u32 offset = n * 2; ++ offset += dir ? 1 : 0; ++ offset *= 16; ++ offset += word * 4; ++ return OTG_EP_BASE + offset; ++} ++ ++/* endpoint data buffer access ++ * ++ * This is a simplistic allocator, will do until we want to support ISO or host mode. ++ * ++ * This works because we are assuming a maximum of 16 allocate endpoints, and no ++ * overlapped endpoints (both in and out are allocated). ++ */ ++ ++static volatile __inline__ u16 data_x_buf(int n, int dir) ++{ ++ return 0x40 * (n * 4 + 2 * (dir ? 1 : 0)); ++} ++static volatile __inline__ u16 data_y_buf(int n, int dir) ++{ ++ return 0x40 * (n * 4 + 2 * (dir ? 1 : 0) + 1); ++} ++ ++static volatile __inline__ u8 * data_x_address(int n, int dir) ++{ ++ return (volatile u8 *) IO_ADDRESS(OTG_DATA_BASE + data_x_buf(n, dir)); ++} ++static volatile __inline__ u8 * data_y_address(int n, int dir) ++{ ++ return (volatile u8 *) IO_ADDRESS(OTG_DATA_BASE + data_y_buf(n, dir)); ++} ++ ++/* ++ * C.f. 23.13.3 DMA Registers ++ */ ++#define OTG_DMA_REV_NUM (OTG_DMA_BASE+0x000) // 32bit dma revision number reg ++#define OTG_DMA_DINT_STAT (OTG_DMA_BASE+0x004) // 32bit dma int status reg ++#define OTG_DMA_DINT_STEN (OTG_DMA_BASE+0x008) // 32bit dma int enable reg ++#define OTG_DMA_ETD_ERR (OTG_DMA_BASE+0x00C) // 32bit dma ETD error status reg ++#define OTG_DMA_EP_ERR (OTG_DMA_BASE+0x010) // 32bit dma EP error status reg ++#define OTG_DMA_ETD_EN (OTG_DMA_BASE+0x020) // 32bit dma ETD DMA enable reg ++#define OTG_DMA_EP_EN (OTG_DMA_BASE+0x024) // 32bit dma EP DMA enable reg ++#define OTG_DMA_ETD_ENXREQ (OTG_DMA_BASE+0x028) // 32bit dma ETD DMA enable Xtrig req ++#define OTG_DMA_EP_ENXREQ (OTG_DMA_BASE+0x02C) // 32bit dma EP DMA enable Ytrig req ++#define OTG_DMA_ETD_ENXYREQ (OTG_DMA_BASE+0x030) // 32bit dma ETD DMA enble XYtrig req ++#define OTG_DMA_EP_ENXYREQ (OTG_DMA_BASE+0x034) // 32bit dma EP DMA enable XYtrig req ++#define OTG_DMA_ETD_BURST4 (OTG_DMA_BASE+0x038) // 32bit dma ETD DMA enble burst4 reg ++#define OTG_DMA_EP_BURST4 (OTG_DMA_BASE+0x03C) // 32bit dma EP DMA enable burst4 reg ++#define OTG_DMA_MISC_CTRL (OTG_DMA_BASE+0x040) // 32bit dma EP misc control reg ++#define OTG_DMA_ETD_CH_CLR (OTG_DMA_BASE+0x044) // 32bit dma ETD clear channel reg ++#define OTG_DMA_EP_CH_CLR (OTG_DMA_BASE+0x048) // 32bit dma EP clear channel reg ++ ++ ++#define OTG_DMA_ETD_CH_CLR (OTG_DMA_BASE+0x044) // 32bit dma ETD clear channel reg ++#define OTG_DMA_ETD_ERR (OTG_DMA_BASE+0x00C) // 32bit dma ETD error status reg ++#define OTG_DMA_ETD_EN (OTG_DMA_BASE+0x020) // 32bit dma ETD DMA enable reg ++#define OTG_DMA_ETD_ENXREQ (OTG_DMA_BASE+0x028) // 32bit dma ETD DMA enable Xtrig req ++#define OTG_DMA_ETD_ENXYREQ (OTG_DMA_BASE+0x030) // 32bit dma ETD DMA enble XYtrig req ++#define OTG_DMA_ETD_BURST4 (OTG_DMA_BASE+0x038) // 32bit dma ETD DMA enble burst4 reg ++ ++ ++ ++#define OTG_DMA_EP_CH_CLR (OTG_DMA_BASE+0x048) // 32bit dma EP clear channel reg ++#define OTG_DMA_EP_ERR (OTG_DMA_BASE+0x010) // 32bit dma EP error status reg ++#define OTG_DMA_EP_EN (OTG_DMA_BASE+0x024) // 32bit dma EP DMA enable reg ++#define OTG_DMA_EP_ENXREQ (OTG_DMA_BASE+0x02C) // 32bit dma EP DMA enable Ytrig req ++#define OTG_DMA_EP_ENXYREQ (OTG_DMA_BASE+0x034) // 32bit dma EP DMA enable XYtrig req ++#define OTG_DMA_EP_BURST4 (OTG_DMA_BASE+0x03C) // 32bit dma EP DMA enable burst4 reg ++ ++ ++#define dma_num_dir(n, dir) (n * 2 + (dir ? 1 : 0)) ++#define dma_num_out(n) dma_num_dir(n, USB_DIR_OUT) ++#define dma_num_in(n) dma_num_dir(n, USB_DIR_IN) ++ ++#define OTG_DMA_ETD_MSA(x) (OTG_DMA_BASE+0x100+x*4) ++#define OTG_DMA_EPN_MSA(x) (OTG_DMA_BASE+0x180+x*4) ++#define OTG_DMA_ETDN_BPTR(x) (OTG_DMA_BASE+0x280+x*4) ++#define OTG_DMA_EPN_BPTR(x) (OTG_DMA_BASE+0x284+x*4) ++ ++ ++/* ++ * C.f. 23.14.10 I2C OTG Transceiver Controller Registers ++ * These are the ISP1301 shadow registers. ++ * ++ * See isp1301-hardware.h for register bit defines. ++ */ ++#if 1 ++#define MX2_VENDOR_ID_LOW OTG_I2C_BASE+0x00 ++#define MX2_VENDOR_ID_HIGH OTG_I2C_BASE+0x01 ++#define MX2_PRODUCT_ID_LOW OTG_I2C_BASE+0x02 ++#define MX2_PRODUCT_ID_HIGH OTG_I2C_BASE+0x03 ++ ++#define MX2_MODE_CONTROL_1_SET OTG_I2C_BASE+0x04 ++#define MX2_MODE_CONTROL_1_CLR OTG_I2C_BASE+0x05 ++#define MX2_OTG_CONTROL_SET OTG_I2C_BASE+0x06 ++#define MX2_OTG_CONTROL_CLR OTG_I2C_BASE+0x07 ++ ++#define MX2_INTERRUPT_SOURCE OTG_I2C_BASE+0x08 ++ ++#define MX2_INT_LAT_REG_SET OTG_I2C_BASE+0x0a ++#define MX2_INT_LAT_REG_CLR OTG_I2C_BASE+0x0b ++ ++#define MX2_INT_FALSE_REG_SET OTG_I2C_BASE+0x0c ++#define MX2_INT_FALSE_REG_CLR OTG_I2C_BASE+0x0d ++#define MX2_INT_TRUE_REG_SET OTG_I2C_BASE+0x0e ++#define MX2_INT_TRUE_REG_CLR OTG_I2C_BASE+0x0f ++ ++#define MX2_OTG_STATUS_REG OTG_I2C_BASE+0x10 ++ ++#define MX2_MODE_CONTROL_2_SET OTG_I2C_BASE+0x12 ++#define MX2_MODE_CONTROL_2_CLR OTG_I2C_BASE+0x13 ++ ++#define MX2_BCD_DEV_LOW OTG_I2C_BASE+0x14 ++#define MX2_BCD_DEV_HIGH OTG_I2C_BASE+0x15 ++#endif ++ ++/* ++ * C.f. 23.14.10 I2C OTG Transceiver Controller Registers ++ * These are the I2C controller and access registers. ++ * ++ * N.B. I2C_ERROR is not documented. ++ */ ++ ++ ++#define I2C_BUSY (1 << 7) ++#define I2C_ERROR (1 << 2) ++#define I2C_HWSMODE (1 << 1) ++#define I2C_I2COE (1 << 0) ++ ++#define I2C_SCLK_TO_SCL_DIVISION (OTG_I2C_BASE+0x1E) ++ ++#define I2C_INTERRUPT_AND_CONTROL (OTG_I2C_BASE+0x1F) ++ ++#define I2C_STATUS_MASK (0x7) ++ ++#define I2C_NOACK_EN (1 << 6) ++#define I2C_RWREADY_EN (1 << 5) ++#define I2C_OTGXCVRINT_EN (1 << 4) ++ ++#define I2C_NOACK (1 << 2) ++#define I2C_RWREADY (1 << 1) ++#define I2C_OTGXCVRINT (1 << 0) ++ ++#define MX2_OTG_XCVR_DEVAD OTG_I2C_BASE+0x18 ++#define MX2_SEQ_OP_REG OTG_I2C_BASE+0x19 ++#define MX2_SEQ_RD_STARTAD OTG_I2C_BASE+0x1a ++#define MX2_I2C_OP_CTRL_REG OTG_I2C_BASE+0x1b ++#define MX2_SCLK_TO_SCL_HPER OTG_I2C_BASE+0x1e ++#define MX2_I2C_INTERRUPT_AND_CTRL OTG_I2C_BASE+0x1f ++ ++ ++/* ********************************************************************************************** */ ++ ++/*! mx2_rb ++ */ ++static u8 __inline__ mx2_rb(u32 port) ++{ ++ return *(volatile u8 *) (IO_ADDRESS(port)); ++} ++ ++/*! mx2_rl ++ */ ++static u32 __inline__ mx2_rl(u32 port) ++{ ++ return *(volatile u32 *) (IO_ADDRESS(port)); ++} ++ ++/*! mx2_wb ++ */ ++static void __inline__ mx2_wb(u32 port, u8 val) ++{ ++ *(volatile u8 *)(IO_ADDRESS(port)) = val; ++} ++ ++/*! mx2_orb ++ */ ++static void __inline__ mx2_orb(u32 port, u8 val) ++{ ++ u8 set = mx2_rb(port) | val; ++ *(volatile u8 *)(IO_ADDRESS(port)) = set; ++} ++ ++/*! mx2_andb ++ */ ++static void __inline__ mx2_andb(u32 port, u8 val) ++{ ++ u8 set = mx2_rb(port) & val; ++ *(volatile u8 *)(IO_ADDRESS(port)) = set; ++} ++ ++/*! mx2_wl ++ */ ++static void __inline__ mx2_wl(u32 port, u32 val) ++{ ++ u32 set; ++ *(volatile u32 *)(IO_ADDRESS(port)) = val; ++ set = mx2_rl(port); ++} ++ ++/*! mx2_orl ++ */ ++static void __inline__ mx2_orl(u32 port, u32 val) ++{ ++ u32 set = mx2_rl(port); ++ *(volatile u32 *)(IO_ADDRESS(port)) = (set | val); ++} ++ ++/*! mx2_andl ++ */ ++static void __inline__ mx2_andl(u32 port, u32 val) ++{ ++ u32 set = mx2_rl(port); ++ *(volatile u32 *)(IO_ADDRESS(port)) = (set & val); ++} ++ ++static u32 inline mx2_host_port_stat(int n) ++{ ++ switch (n) { ++ default: ++ case 1: ++ return OTG_HOST_PORT_STATUS_1; ++ case 2: ++ return OTG_HOST_PORT_STATUS_2; ++ case 3: ++ return OTG_HOST_PORT_STATUS_3; ++ } ++} ++ diff --git a/packages/linux/linux-mtx-2-2.4.27/47-au1000_eth.patch b/packages/linux/linux-mtx-2-2.4.27/47-au1000_eth.patch new file mode 100644 index 0000000000..b1eb6c990d --- /dev/null +++ b/packages/linux/linux-mtx-2-2.4.27/47-au1000_eth.patch @@ -0,0 +1,163 @@ +--- linux/drivers/net/au1000_eth.h 2003-06-05 02:00:35.000000000 +0200 ++++ patch/drivers/net/au1000_eth.h 2006-10-13 14:36:42.237757250 +0200 +@@ -233,3 +233,11 @@ + struct timer_list timer; + spinlock_t lock; /* Serialise access to device */ + }; ++ ++struct mii_ioctl_data { ++ u16 phy_id; ++ u16 reg_num; ++ u16 val_in; ++ u16 val_out; ++}; ++ +--- linux/drivers/net/au1000_eth.c 2004-11-24 12:12:42.000000000 +0100 ++++ patch/drivers/net/au1000_eth.c 2006-10-13 14:36:36.649408000 +0200 +@@ -83,8 +83,6 @@ + static int au1000_set_config(struct net_device *dev, struct ifmap *map); + static void set_rx_mode(struct net_device *); + static struct net_device_stats *au1000_get_stats(struct net_device *); +-static inline void update_tx_stats(struct net_device *, u32, u32); +-static inline void update_rx_stats(struct net_device *, u32); + static void au1000_timer(unsigned long); + static int au1000_ioctl(struct net_device *, struct ifreq *, int); + static int mdio_read(struct net_device *, int, int); +@@ -674,6 +672,7 @@ + {"Broadcom BCM5201 10/100 BaseT PHY",0x0040,0x6212, &bcm_5201_ops,0}, + {"Broadcom BCM5221 10/100 BaseT PHY",0x0040,0x61e4, &bcm_5201_ops,0}, + {"Broadcom BCM5222 10/100 BaseT PHY",0x0040,0x6322, &bcm_5201_ops,1}, ++ {"Broadcom BCM5241 10/100 BaseT PHY",0x0143,0xBC31, &bcm_5201_ops,0}, + {"AMD 79C901 HomePNA PHY",0x0000,0x35c8, &am79c901_ops,0}, + {"AMD 79C874 10/100 BaseT PHY",0x0022,0x561b, &am79c874_ops,0}, + {"LSI 80227 10/100 BaseT PHY",0x0016,0xf840, &lsi_80227_ops,0}, +@@ -1389,6 +1388,7 @@ + control |= MAC_FULL_DUPLEX; + } + aup->mac->control = control; ++ aup->mac->vlan1_tag = 0x8100; /* activate vlan support */ + au_sync(); + + spin_unlock_irqrestore(&aup->lock, flags); +@@ -1539,16 +1539,11 @@ + } + } + +- +-static inline void +-update_tx_stats(struct net_device *dev, u32 status, u32 pkt_len) ++static void update_tx_stats(struct net_device *dev, u32 status) + { + struct au1000_private *aup = (struct au1000_private *) dev->priv; + struct net_device_stats *ps = &aup->stats; + +- ps->tx_packets++; +- ps->tx_bytes += pkt_len; +- + if (status & TX_FRAME_ABORTED) { + if (dev->if_port == IF_PORT_100BASEFX) { + if (status & (TX_JAB_TIMEOUT | TX_UNDERRUN)) { +@@ -1581,7 +1576,7 @@ + ptxd = aup->tx_dma_ring[aup->tx_tail]; + + while (ptxd->buff_stat & TX_T_DONE) { +- update_tx_stats(dev, ptxd->status, ptxd->len & 0x3ff); ++ update_tx_stats(dev, ptxd->status); + ptxd->buff_stat &= ~TX_T_DONE; + ptxd->len = 0; + au_sync(); +@@ -1603,6 +1598,7 @@ + static int au1000_tx(struct sk_buff *skb, struct net_device *dev) + { + struct au1000_private *aup = (struct au1000_private *) dev->priv; ++ struct net_device_stats *ps = &aup->stats; + volatile tx_dma_t *ptxd; + u32 buff_stat; + db_dest_t *pDB; +@@ -1622,7 +1618,7 @@ + return 1; + } + else if (buff_stat & TX_T_DONE) { +- update_tx_stats(dev, ptxd->status, ptxd->len & 0x3ff); ++ update_tx_stats(dev, ptxd->status); + ptxd->len = 0; + } + +@@ -1642,6 +1638,9 @@ + else + ptxd->len = skb->len; + ++ ps->tx_packets++; ++ ps->tx_bytes += ptxd->len; ++ + ptxd->buff_stat = pDB->dma_addr | TX_DMA_ENABLE; + au_sync(); + dev_kfree_skb(skb); +@@ -1650,7 +1649,6 @@ + return 0; + } + +- + static inline void update_rx_stats(struct net_device *dev, u32 status) + { + struct au1000_private *aup = (struct au1000_private *) dev->priv; +@@ -1840,21 +1838,36 @@ + + static int au1000_ioctl(struct net_device *dev, struct ifreq *rq, int cmd) + { +- //u16 *data = (u16 *)&rq->ifr_data; ++ struct au1000_private *aup = (struct au1000_private *)dev->priv; ++ struct mii_ioctl_data *mii_data = (struct mii_ioctl_data *)&rq->ifr_ifru; ++ u16 val = mii_data->val_in; ++ const u32 enabled = MAC_EN_RESET0 | MAC_EN_RESET1 | ++ MAC_EN_RESET2 | MAC_EN_CLOCK_ENABLE; ++ ++ /* Check if net device is running */ ++ if (!netif_running(dev)) return -EINVAL; ++ ++ /* Check if ethernet mac is in reset mode */ ++ if (*aup->enable & enabled != enabled) return -EINVAL; + +- /* fixme */ + switch(cmd) { +- case SIOCDEVPRIVATE: /* Get the address of the PHY in use. */ +- //data[0] = PHY_ADDRESS; +- case SIOCDEVPRIVATE+1: /* Read the specified MII register. */ +- //data[3] = mdio_read(ioaddr, data[0], data[1]); +- return 0; +- case SIOCDEVPRIVATE+2: /* Write the specified MII register */ +- //mdio_write(ioaddr, data[0], data[1], data[2]); +- return 0; ++ case SIOCGMIIPHY: /* Get the address of the PHY in use. */ ++ mii_data->phy_id = aup->phy_addr; ++ break; ++ case SIOCGMIIREG: /* Read the specified MII register. */ ++ mii_data->val_out = mdio_read(dev, mii_data->phy_id, ++ mii_data->reg_num); ++ break; ++ case SIOCSMIIREG: /* Write the specified MII register */ ++ if (!capable(CAP_NET_ADMIN)) ++ return -EPERM; ++ mdio_write(dev,mii_data->phy_id, mii_data->reg_num, ++ val); + default: + return -EOPNOTSUPP; ++ + } ++ return 0; + } + + +--- linux/drivers/net/Config.in 2006-10-13 15:05:33.697966750 +0200 ++++ patch/drivers/net/Config.in 2006-10-13 14:36:47.030056750 +0200 +@@ -67,9 +67,7 @@ + fi + if [ "$CONFIG_SOC_AU1X00" = "y" ]; then + tristate ' MIPS Au1x00 Ethernet support' CONFIG_MIPS_AU1X00_ENET +- if [ "$CONFIG_MIPS_AU1X00_ENET" = "y" ]; then +- bool ' Broadcom 5222 Dual Phy Support' CONFIG_BCM5222_DUAL_PHY +- fi ++ dep_mbool ' Broadcom 5222 Dual Phy Support' CONFIG_BCM5222_DUAL_PHY $CONFIG_MIPS_AU1X00_ENET + fi + if [ "$CONFIG_SGI_IP27" = "y" ]; then + bool ' SGI IOC3 Ethernet' CONFIG_SGI_IOC3_ETH diff --git a/packages/linux/linux-mtx-2-2.4.27/48-pptp.patch b/packages/linux/linux-mtx-2-2.4.27/48-pptp.patch new file mode 100644 index 0000000000..5896f90370 --- /dev/null +++ b/packages/linux/linux-mtx-2-2.4.27/48-pptp.patch @@ -0,0 +1,5092 @@ +diff -uNr linux_org/Documentation/Configure.help linux/Documentation/Configure.help +--- linux_org/Documentation/Configure.help 2006-10-27 14:08:20.000000000 +0200 ++++ linux/Documentation/Configure.help 2006-10-27 14:11:52.000000000 +0200 +@@ -2848,6 +2848,31 @@ + If you want to compile it as a module, say M here and read + <file:Documentation/modules.txt>. If unsure, say `Y'. + ++PPTP conntrack and NAT support ++CONFIG_IP_NF_PPTP ++ This module adds support for PPTP (Point to Point Tunnelling Protocol, ++ RFC2637) conncection tracking and NAT. ++ ++ If you are running PPTP sessions over a stateful firewall or NAT box, ++ you may want to enable this feature. ++ ++ Please note that not all PPTP modes of operation are supported yet. ++ For more info, read top of the file net/ipv4/netfilter/ip_conntrack_pptp.c ++ ++ If you want to compile it as a module, say M here and read ++ Documentation/modules.txt. If unsure, say `N'. ++ ++GRE protocol conntrack and NAT support ++CONFIG_IP_NF_CT_PROTO_GRE ++ This module adds generic support for connection tracking and NAT of the ++ GRE protocol (RFC1701, RFC2784). Please note that this will only work ++ with GRE connections using the key field of the GRE header. ++ ++ You will need GRE support to enable PPTP support. ++ ++ If you want to compile it as a module, say `M' here and read ++ Documentation/modules.txt. If unsire, say `N'. ++ + User space queueing via NETLINK + CONFIG_IP_NF_QUEUE + Netfilter has the ability to queue packets to user space: the +diff -uNr linux_org/include/linux/netfilter_ipv4/ip_conntrack.h linux/include/linux/netfilter_ipv4/ip_conntrack.h +--- linux_org/include/linux/netfilter_ipv4/ip_conntrack.h 2004-11-24 12:13:57.000000000 +0100 ++++ linux/include/linux/netfilter_ipv4/ip_conntrack.h 2006-10-27 14:11:52.000000000 +0200 +@@ -50,19 +50,23 @@ + + #include <linux/netfilter_ipv4/ip_conntrack_tcp.h> + #include <linux/netfilter_ipv4/ip_conntrack_icmp.h> ++#include <linux/netfilter_ipv4/ip_conntrack_proto_gre.h> + + /* per conntrack: protocol private data */ + union ip_conntrack_proto { + /* insert conntrack proto private data here */ ++ struct ip_ct_gre gre; + struct ip_ct_tcp tcp; + struct ip_ct_icmp icmp; + }; + + union ip_conntrack_expect_proto { + /* insert expect proto private data here */ ++ struct ip_ct_gre_expect gre; + }; + + /* Add protocol helper include file here */ ++#include <linux/netfilter_ipv4/ip_conntrack_pptp.h> + #include <linux/netfilter_ipv4/ip_conntrack_amanda.h> + + #include <linux/netfilter_ipv4/ip_conntrack_ftp.h> +@@ -71,6 +75,7 @@ + /* per expectation: application helper private data */ + union ip_conntrack_expect_help { + /* insert conntrack helper private data (expect) here */ ++ struct ip_ct_pptp_expect exp_pptp_info; + struct ip_ct_amanda_expect exp_amanda_info; + struct ip_ct_ftp_expect exp_ftp_info; + struct ip_ct_irc_expect exp_irc_info; +@@ -85,16 +90,19 @@ + /* per conntrack: application helper private data */ + union ip_conntrack_help { + /* insert conntrack helper private data (master) here */ ++ struct ip_ct_pptp_master ct_pptp_info; + struct ip_ct_ftp_master ct_ftp_info; + struct ip_ct_irc_master ct_irc_info; + }; + + #ifdef CONFIG_IP_NF_NAT_NEEDED + #include <linux/netfilter_ipv4/ip_nat.h> ++#include <linux/netfilter_ipv4/ip_nat_pptp.h> + + /* per conntrack: nat application helper private data */ + union ip_conntrack_nat_help { + /* insert nat helper private data here */ ++ struct ip_nat_pptp nat_pptp_info; + }; + #endif + +diff -uNr linux_org/include/linux/netfilter_ipv4/ip_conntrack_pptp.h linux/include/linux/netfilter_ipv4/ip_conntrack_pptp.h +--- linux_org/include/linux/netfilter_ipv4/ip_conntrack_pptp.h 1970-01-01 01:00:00.000000000 +0100 ++++ linux/include/linux/netfilter_ipv4/ip_conntrack_pptp.h 2006-10-27 14:11:52.000000000 +0200 +@@ -0,0 +1,313 @@ ++/* PPTP constants and structs */ ++#ifndef _CONNTRACK_PPTP_H ++#define _CONNTRACK_PPTP_H ++ ++/* state of the control session */ ++enum pptp_ctrlsess_state { ++ PPTP_SESSION_NONE, /* no session present */ ++ PPTP_SESSION_ERROR, /* some session error */ ++ PPTP_SESSION_STOPREQ, /* stop_sess request seen */ ++ PPTP_SESSION_REQUESTED, /* start_sess request seen */ ++ PPTP_SESSION_CONFIRMED, /* session established */ ++}; ++ ++/* state of the call inside the control session */ ++enum pptp_ctrlcall_state { ++ PPTP_CALL_NONE, ++ PPTP_CALL_ERROR, ++ PPTP_CALL_OUT_REQ, ++ PPTP_CALL_OUT_CONF, ++ PPTP_CALL_IN_REQ, ++ PPTP_CALL_IN_REP, ++ PPTP_CALL_IN_CONF, ++ PPTP_CALL_CLEAR_REQ, ++}; ++ ++ ++/* conntrack private data */ ++struct ip_ct_pptp_master { ++ enum pptp_ctrlsess_state sstate; /* session state */ ++ ++ /* everything below is going to be per-expectation in newnat, ++ * since there could be more than one call within one session */ ++ enum pptp_ctrlcall_state cstate; /* call state */ ++ u_int16_t pac_call_id; /* call id of PAC, host byte order */ ++ u_int16_t pns_call_id; /* call id of PNS, host byte order */ ++}; ++ ++/* conntrack_expect private member */ ++struct ip_ct_pptp_expect { ++ enum pptp_ctrlcall_state cstate; /* call state */ ++ u_int16_t pac_call_id; /* call id of PAC */ ++ u_int16_t pns_call_id; /* call id of PNS */ ++}; ++ ++ ++#ifdef __KERNEL__ ++ ++#include <linux/netfilter_ipv4/lockhelp.h> ++DECLARE_LOCK_EXTERN(ip_pptp_lock); ++ ++#define IP_CONNTR_PPTP PPTP_CONTROL_PORT ++ ++union pptp_ctrl_union { ++ void *rawreq; ++ struct PptpStartSessionRequest *sreq; ++ struct PptpStartSessionReply *srep; ++ struct PptpStopSessionReqest *streq; ++ struct PptpStopSessionReply *strep; ++ struct PptpOutCallRequest *ocreq; ++ struct PptpOutCallReply *ocack; ++ struct PptpInCallRequest *icreq; ++ struct PptpInCallReply *icack; ++ struct PptpInCallConnected *iccon; ++ struct PptpClearCallRequest *clrreq; ++ struct PptpCallDisconnectNotify *disc; ++ struct PptpWanErrorNotify *wanerr; ++ struct PptpSetLinkInfo *setlink; ++}; ++ ++ ++ ++#define PPTP_CONTROL_PORT 1723 ++ ++#define PPTP_PACKET_CONTROL 1 ++#define PPTP_PACKET_MGMT 2 ++ ++#define PPTP_MAGIC_COOKIE 0x1a2b3c4d ++ ++struct pptp_pkt_hdr { ++ __u16 packetLength; ++ __u16 packetType; ++ __u32 magicCookie; ++}; ++ ++/* PptpControlMessageType values */ ++#define PPTP_START_SESSION_REQUEST 1 ++#define PPTP_START_SESSION_REPLY 2 ++#define PPTP_STOP_SESSION_REQUEST 3 ++#define PPTP_STOP_SESSION_REPLY 4 ++#define PPTP_ECHO_REQUEST 5 ++#define PPTP_ECHO_REPLY 6 ++#define PPTP_OUT_CALL_REQUEST 7 ++#define PPTP_OUT_CALL_REPLY 8 ++#define PPTP_IN_CALL_REQUEST 9 ++#define PPTP_IN_CALL_REPLY 10 ++#define PPTP_IN_CALL_CONNECT 11 ++#define PPTP_CALL_CLEAR_REQUEST 12 ++#define PPTP_CALL_DISCONNECT_NOTIFY 13 ++#define PPTP_WAN_ERROR_NOTIFY 14 ++#define PPTP_SET_LINK_INFO 15 ++ ++#define PPTP_MSG_MAX 15 ++ ++/* PptpGeneralError values */ ++#define PPTP_ERROR_CODE_NONE 0 ++#define PPTP_NOT_CONNECTED 1 ++#define PPTP_BAD_FORMAT 2 ++#define PPTP_BAD_VALUE 3 ++#define PPTP_NO_RESOURCE 4 ++#define PPTP_BAD_CALLID 5 ++#define PPTP_REMOVE_DEVICE_ERROR 6 ++ ++struct PptpControlHeader { ++ __u16 messageType; ++ __u16 reserved; ++}; ++ ++/* FramingCapability Bitmap Values */ ++#define PPTP_FRAME_CAP_ASYNC 0x1 ++#define PPTP_FRAME_CAP_SYNC 0x2 ++ ++/* BearerCapability Bitmap Values */ ++#define PPTP_BEARER_CAP_ANALOG 0x1 ++#define PPTP_BEARER_CAP_DIGITAL 0x2 ++ ++struct PptpStartSessionRequest { ++ __u16 protocolVersion; ++ __u8 reserved1; ++ __u8 reserved2; ++ __u32 framingCapability; ++ __u32 bearerCapability; ++ __u16 maxChannels; ++ __u16 firmwareRevision; ++ __u8 hostName[64]; ++ __u8 vendorString[64]; ++}; ++ ++/* PptpStartSessionResultCode Values */ ++#define PPTP_START_OK 1 ++#define PPTP_START_GENERAL_ERROR 2 ++#define PPTP_START_ALREADY_CONNECTED 3 ++#define PPTP_START_NOT_AUTHORIZED 4 ++#define PPTP_START_UNKNOWN_PROTOCOL 5 ++ ++struct PptpStartSessionReply { ++ __u16 protocolVersion; ++ __u8 resultCode; ++ __u8 generalErrorCode; ++ __u32 framingCapability; ++ __u32 bearerCapability; ++ __u16 maxChannels; ++ __u16 firmwareRevision; ++ __u8 hostName[64]; ++ __u8 vendorString[64]; ++}; ++ ++/* PptpStopReasons */ ++#define PPTP_STOP_NONE 1 ++#define PPTP_STOP_PROTOCOL 2 ++#define PPTP_STOP_LOCAL_SHUTDOWN 3 ++ ++struct PptpStopSessionRequest { ++ __u8 reason; ++}; ++ ++/* PptpStopSessionResultCode */ ++#define PPTP_STOP_OK 1 ++#define PPTP_STOP_GENERAL_ERROR 2 ++ ++struct PptpStopSessionReply { ++ __u8 resultCode; ++ __u8 generalErrorCode; ++}; ++ ++struct PptpEchoRequest { ++ __u32 identNumber; ++}; ++ ++/* PptpEchoReplyResultCode */ ++#define PPTP_ECHO_OK 1 ++#define PPTP_ECHO_GENERAL_ERROR 2 ++ ++struct PptpEchoReply { ++ __u32 identNumber; ++ __u8 resultCode; ++ __u8 generalErrorCode; ++ __u16 reserved; ++}; ++ ++/* PptpFramingType */ ++#define PPTP_ASYNC_FRAMING 1 ++#define PPTP_SYNC_FRAMING 2 ++#define PPTP_DONT_CARE_FRAMING 3 ++ ++/* PptpCallBearerType */ ++#define PPTP_ANALOG_TYPE 1 ++#define PPTP_DIGITAL_TYPE 2 ++#define PPTP_DONT_CARE_BEARER_TYPE 3 ++ ++struct PptpOutCallRequest { ++ __u16 callID; ++ __u16 callSerialNumber; ++ __u32 minBPS; ++ __u32 maxBPS; ++ __u32 bearerType; ++ __u32 framingType; ++ __u16 packetWindow; ++ __u16 packetProcDelay; ++ __u16 reserved1; ++ __u16 phoneNumberLength; ++ __u16 reserved2; ++ __u8 phoneNumber[64]; ++ __u8 subAddress[64]; ++}; ++ ++/* PptpCallResultCode */ ++#define PPTP_OUTCALL_CONNECT 1 ++#define PPTP_OUTCALL_GENERAL_ERROR 2 ++#define PPTP_OUTCALL_NO_CARRIER 3 ++#define PPTP_OUTCALL_BUSY 4 ++#define PPTP_OUTCALL_NO_DIAL_TONE 5 ++#define PPTP_OUTCALL_TIMEOUT 6 ++#define PPTP_OUTCALL_DONT_ACCEPT 7 ++ ++struct PptpOutCallReply { ++ __u16 callID; ++ __u16 peersCallID; ++ __u8 resultCode; ++ __u8 generalErrorCode; ++ __u16 causeCode; ++ __u32 connectSpeed; ++ __u16 packetWindow; ++ __u16 packetProcDelay; ++ __u32 physChannelID; ++}; ++ ++struct PptpInCallRequest { ++ __u16 callID; ++ __u16 callSerialNumber; ++ __u32 callBearerType; ++ __u32 physChannelID; ++ __u16 dialedNumberLength; ++ __u16 dialingNumberLength; ++ __u8 dialedNumber[64]; ++ __u8 dialingNumber[64]; ++ __u8 subAddress[64]; ++}; ++ ++/* PptpInCallResultCode */ ++#define PPTP_INCALL_ACCEPT 1 ++#define PPTP_INCALL_GENERAL_ERROR 2 ++#define PPTP_INCALL_DONT_ACCEPT 3 ++ ++struct PptpInCallReply { ++ __u16 callID; ++ __u16 peersCallID; ++ __u8 resultCode; ++ __u8 generalErrorCode; ++ __u16 packetWindow; ++ __u16 packetProcDelay; ++ __u16 reserved; ++}; ++ ++struct PptpInCallConnected { ++ __u16 peersCallID; ++ __u16 reserved; ++ __u32 connectSpeed; ++ __u16 packetWindow; ++ __u16 packetProcDelay; ++ __u32 callFramingType; ++}; ++ ++struct PptpClearCallRequest { ++ __u16 callID; ++ __u16 reserved; ++}; ++ ++struct PptpCallDisconnectNotify { ++ __u16 callID; ++ __u8 resultCode; ++ __u8 generalErrorCode; ++ __u16 causeCode; ++ __u16 reserved; ++ __u8 callStatistics[128]; ++}; ++ ++struct PptpWanErrorNotify { ++ __u16 peersCallID; ++ __u16 reserved; ++ __u32 crcErrors; ++ __u32 framingErrors; ++ __u32 hardwareOverRuns; ++ __u32 bufferOverRuns; ++ __u32 timeoutErrors; ++ __u32 alignmentErrors; ++}; ++ ++struct PptpSetLinkInfo { ++ __u16 peersCallID; ++ __u16 reserved; ++ __u32 sendAccm; ++ __u32 recvAccm; ++}; ++ ++ ++struct pptp_priv_data { ++ __u16 call_id; ++ __u16 mcall_id; ++ __u16 pcall_id; ++}; ++ ++#endif /* __KERNEL__ */ ++#endif /* _CONNTRACK_PPTP_H */ +diff -uNr linux_org/include/linux/netfilter_ipv4/ip_conntrack_proto_gre.h linux/include/linux/netfilter_ipv4/ip_conntrack_proto_gre.h +--- linux_org/include/linux/netfilter_ipv4/ip_conntrack_proto_gre.h 1970-01-01 01:00:00.000000000 +0100 ++++ linux/include/linux/netfilter_ipv4/ip_conntrack_proto_gre.h 2006-10-27 14:11:52.000000000 +0200 +@@ -0,0 +1,123 @@ ++#ifndef _CONNTRACK_PROTO_GRE_H ++#define _CONNTRACK_PROTO_GRE_H ++#include <asm/byteorder.h> ++ ++/* GRE PROTOCOL HEADER */ ++ ++/* GRE Version field */ ++#define GRE_VERSION_1701 0x0 ++#define GRE_VERSION_PPTP 0x1 ++ ++/* GRE Protocol field */ ++#define GRE_PROTOCOL_PPTP 0x880B ++ ++/* GRE Flags */ ++#define GRE_FLAG_C 0x80 ++#define GRE_FLAG_R 0x40 ++#define GRE_FLAG_K 0x20 ++#define GRE_FLAG_S 0x10 ++#define GRE_FLAG_A 0x80 ++ ++#define GRE_IS_C(f) ((f)&GRE_FLAG_C) ++#define GRE_IS_R(f) ((f)&GRE_FLAG_R) ++#define GRE_IS_K(f) ((f)&GRE_FLAG_K) ++#define GRE_IS_S(f) ((f)&GRE_FLAG_S) ++#define GRE_IS_A(f) ((f)&GRE_FLAG_A) ++ ++/* GRE is a mess: Four different standards */ ++struct gre_hdr { ++#if defined(__LITTLE_ENDIAN_BITFIELD) ++ __u16 rec:3, ++ srr:1, ++ seq:1, ++ key:1, ++ routing:1, ++ csum:1, ++ version:3, ++ reserved:4, ++ ack:1; ++#elif defined(__BIG_ENDIAN_BITFIELD) ++ __u16 csum:1, ++ routing:1, ++ key:1, ++ seq:1, ++ srr:1, ++ rec:3, ++ ack:1, ++ reserved:4, ++ version:3; ++#else ++#error "Adjust your <asm/byteorder.h> defines" ++#endif ++ __u16 protocol; ++}; ++ ++/* modified GRE header for PPTP */ ++struct gre_hdr_pptp { ++ __u8 flags; /* bitfield */ ++ __u8 version; /* should be GRE_VERSION_PPTP */ ++ __u16 protocol; /* should be GRE_PROTOCOL_PPTP */ ++ __u16 payload_len; /* size of ppp payload, not inc. gre header */ ++ __u16 call_id; /* peer's call_id for this session */ ++ __u32 seq; /* sequence number. Present if S==1 */ ++ __u32 ack; /* seq number of highest packet recieved by */ ++ /* sender in this session */ ++}; ++ ++ ++/* this is part of ip_conntrack */ ++struct ip_ct_gre { ++ unsigned int stream_timeout; ++ unsigned int timeout; ++}; ++ ++/* this is part of ip_conntrack_expect */ ++struct ip_ct_gre_expect { ++ struct ip_ct_gre_keymap *keymap_orig, *keymap_reply; ++}; ++ ++#ifdef __KERNEL__ ++struct ip_conntrack_expect; ++ ++/* structure for original <-> reply keymap */ ++struct ip_ct_gre_keymap { ++ struct list_head list; ++ ++ struct ip_conntrack_tuple tuple; ++}; ++ ++ ++/* add new tuple->key_reply pair to keymap */ ++int ip_ct_gre_keymap_add(struct ip_conntrack_expect *exp, ++ struct ip_conntrack_tuple *t, ++ int reply); ++ ++/* change an existing keymap entry */ ++void ip_ct_gre_keymap_change(struct ip_ct_gre_keymap *km, ++ struct ip_conntrack_tuple *t); ++ ++/* delete keymap entries */ ++void ip_ct_gre_keymap_destroy(struct ip_conntrack_expect *exp); ++ ++ ++/* get pointer to gre key, if present */ ++static inline u_int32_t *gre_key(struct gre_hdr *greh) ++{ ++ if (!greh->key) ++ return NULL; ++ if (greh->csum || greh->routing) ++ return (u_int32_t *) (greh+sizeof(*greh)+4); ++ return (u_int32_t *) (greh+sizeof(*greh)); ++} ++ ++/* get pointer ot gre csum, if present */ ++static inline u_int16_t *gre_csum(struct gre_hdr *greh) ++{ ++ if (!greh->csum) ++ return NULL; ++ return (u_int16_t *) (greh+sizeof(*greh)); ++} ++ ++#endif /* __KERNEL__ */ ++ ++#endif /* _CONNTRACK_PROTO_GRE_H */ +diff -uNr linux_org/include/linux/netfilter_ipv4/ip_conntrack_tuple.h linux/include/linux/netfilter_ipv4/ip_conntrack_tuple.h +--- linux_org/include/linux/netfilter_ipv4/ip_conntrack_tuple.h 2003-11-17 02:07:46.000000000 +0100 ++++ linux/include/linux/netfilter_ipv4/ip_conntrack_tuple.h 2006-10-27 14:11:52.000000000 +0200 +@@ -14,7 +14,7 @@ + union ip_conntrack_manip_proto + { + /* Add other protocols here. */ +- u_int16_t all; ++ u_int32_t all; + + struct { + u_int16_t port; +@@ -25,6 +25,9 @@ + struct { + u_int16_t id; + } icmp; ++ struct { ++ u_int32_t key; ++ } gre; + }; + + /* The manipulable part of the tuple. */ +@@ -44,7 +47,7 @@ + u_int32_t ip; + union { + /* Add other protocols here. */ +- u_int16_t all; ++ u_int64_t all; + + struct { + u_int16_t port; +@@ -55,6 +58,11 @@ + struct { + u_int8_t type, code; + } icmp; ++ struct { ++ u_int16_t protocol; ++ u_int8_t version; ++ u_int32_t key; ++ } gre; + } u; + + /* The protocol. */ +@@ -80,10 +88,16 @@ + #ifdef __KERNEL__ + + #define DUMP_TUPLE(tp) \ +-DEBUGP("tuple %p: %u %u.%u.%u.%u:%hu -> %u.%u.%u.%u:%hu\n", \ ++DEBUGP("tuple %p: %u %u.%u.%u.%u:%u -> %u.%u.%u.%u:%u\n", \ + (tp), (tp)->dst.protonum, \ +- NIPQUAD((tp)->src.ip), ntohs((tp)->src.u.all), \ +- NIPQUAD((tp)->dst.ip), ntohs((tp)->dst.u.all)) ++ NIPQUAD((tp)->src.ip), ntohl((tp)->src.u.all), \ ++ NIPQUAD((tp)->dst.ip), ntohl((tp)->dst.u.all)) ++ ++#define DUMP_TUPLE_RAW(x) \ ++ DEBUGP("tuple %p: %u %u.%u.%u.%u:0x%08x -> %u.%u.%u.%u:0x%08x\n",\ ++ (x), (x)->dst.protonum, \ ++ NIPQUAD((x)->src.ip), ntohl((x)->src.u.all), \ ++ NIPQUAD((x)->dst.ip), ntohl((x)->dst.u.all)) + + #define CTINFO2DIR(ctinfo) ((ctinfo) >= IP_CT_IS_REPLY ? IP_CT_DIR_REPLY : IP_CT_DIR_ORIGINAL) + +diff -uNr linux_org/include/linux/netfilter_ipv4/ip_conntrack_tuple.h.orig linux/include/linux/netfilter_ipv4/ip_conntrack_tuple.h.orig +--- linux_org/include/linux/netfilter_ipv4/ip_conntrack_tuple.h.orig 1970-01-01 01:00:00.000000000 +0100 ++++ linux/include/linux/netfilter_ipv4/ip_conntrack_tuple.h.orig 2003-11-17 02:07:46.000000000 +0100 +@@ -0,0 +1,139 @@ ++#ifndef _IP_CONNTRACK_TUPLE_H ++#define _IP_CONNTRACK_TUPLE_H ++ ++/* A `tuple' is a structure containing the information to uniquely ++ identify a connection. ie. if two packets have the same tuple, they ++ are in the same connection; if not, they are not. ++ ++ We divide the structure along "manipulatable" and ++ "non-manipulatable" lines, for the benefit of the NAT code. ++*/ ++ ++/* The protocol-specific manipulable parts of the tuple: always in ++ network order! */ ++union ip_conntrack_manip_proto ++{ ++ /* Add other protocols here. */ ++ u_int16_t all; ++ ++ struct { ++ u_int16_t port; ++ } tcp; ++ struct { ++ u_int16_t port; ++ } udp; ++ struct { ++ u_int16_t id; ++ } icmp; ++}; ++ ++/* The manipulable part of the tuple. */ ++struct ip_conntrack_manip ++{ ++ u_int32_t ip; ++ union ip_conntrack_manip_proto u; ++}; ++ ++/* This contains the information to distinguish a connection. */ ++struct ip_conntrack_tuple ++{ ++ struct ip_conntrack_manip src; ++ ++ /* These are the parts of the tuple which are fixed. */ ++ struct { ++ u_int32_t ip; ++ union { ++ /* Add other protocols here. */ ++ u_int16_t all; ++ ++ struct { ++ u_int16_t port; ++ } tcp; ++ struct { ++ u_int16_t port; ++ } udp; ++ struct { ++ u_int8_t type, code; ++ } icmp; ++ } u; ++ ++ /* The protocol. */ ++ u_int16_t protonum; ++ } dst; ++}; ++ ++/* This is optimized opposed to a memset of the whole structure. Everything we ++ * really care about is the source/destination unions */ ++#define IP_CT_TUPLE_U_BLANK(tuple) \ ++ do { \ ++ (tuple)->src.u.all = 0; \ ++ (tuple)->dst.u.all = 0; \ ++ } while (0) ++ ++enum ip_conntrack_dir ++{ ++ IP_CT_DIR_ORIGINAL, ++ IP_CT_DIR_REPLY, ++ IP_CT_DIR_MAX ++}; ++ ++#ifdef __KERNEL__ ++ ++#define DUMP_TUPLE(tp) \ ++DEBUGP("tuple %p: %u %u.%u.%u.%u:%hu -> %u.%u.%u.%u:%hu\n", \ ++ (tp), (tp)->dst.protonum, \ ++ NIPQUAD((tp)->src.ip), ntohs((tp)->src.u.all), \ ++ NIPQUAD((tp)->dst.ip), ntohs((tp)->dst.u.all)) ++ ++#define CTINFO2DIR(ctinfo) ((ctinfo) >= IP_CT_IS_REPLY ? IP_CT_DIR_REPLY : IP_CT_DIR_ORIGINAL) ++ ++/* If we're the first tuple, it's the original dir. */ ++#define DIRECTION(h) ((enum ip_conntrack_dir)(&(h)->ctrack->tuplehash[1] == (h))) ++ ++/* Connections have two entries in the hash table: one for each way */ ++struct ip_conntrack_tuple_hash ++{ ++ struct list_head list; ++ ++ struct ip_conntrack_tuple tuple; ++ ++ /* this == &ctrack->tuplehash[DIRECTION(this)]. */ ++ struct ip_conntrack *ctrack; ++}; ++ ++#endif /* __KERNEL__ */ ++ ++static inline int ip_ct_tuple_src_equal(const struct ip_conntrack_tuple *t1, ++ const struct ip_conntrack_tuple *t2) ++{ ++ return t1->src.ip == t2->src.ip ++ && t1->src.u.all == t2->src.u.all; ++} ++ ++static inline int ip_ct_tuple_dst_equal(const struct ip_conntrack_tuple *t1, ++ const struct ip_conntrack_tuple *t2) ++{ ++ return t1->dst.ip == t2->dst.ip ++ && t1->dst.u.all == t2->dst.u.all ++ && t1->dst.protonum == t2->dst.protonum; ++} ++ ++static inline int ip_ct_tuple_equal(const struct ip_conntrack_tuple *t1, ++ const struct ip_conntrack_tuple *t2) ++{ ++ return ip_ct_tuple_src_equal(t1, t2) && ip_ct_tuple_dst_equal(t1, t2); ++} ++ ++static inline int ip_ct_tuple_mask_cmp(const struct ip_conntrack_tuple *t, ++ const struct ip_conntrack_tuple *tuple, ++ const struct ip_conntrack_tuple *mask) ++{ ++ return !(((t->src.ip ^ tuple->src.ip) & mask->src.ip) ++ || ((t->dst.ip ^ tuple->dst.ip) & mask->dst.ip) ++ || ((t->src.u.all ^ tuple->src.u.all) & mask->src.u.all) ++ || ((t->dst.u.all ^ tuple->dst.u.all) & mask->dst.u.all) ++ || ((t->dst.protonum ^ tuple->dst.protonum) ++ & mask->dst.protonum)); ++} ++ ++#endif /* _IP_CONNTRACK_TUPLE_H */ +diff -uNr linux_org/include/linux/netfilter_ipv4/ip_nat_pptp.h linux/include/linux/netfilter_ipv4/ip_nat_pptp.h +--- linux_org/include/linux/netfilter_ipv4/ip_nat_pptp.h 1970-01-01 01:00:00.000000000 +0100 ++++ linux/include/linux/netfilter_ipv4/ip_nat_pptp.h 2006-10-27 14:11:52.000000000 +0200 +@@ -0,0 +1,11 @@ ++/* PPTP constants and structs */ ++#ifndef _NAT_PPTP_H ++#define _NAT_PPTP_H ++ ++/* conntrack private data */ ++struct ip_nat_pptp { ++ u_int16_t pns_call_id; /* NAT'ed PNS call id */ ++ u_int16_t pac_call_id; /* NAT'ed PAC call id */ ++}; ++ ++#endif /* _NAT_PPTP_H */ +diff -uNr linux_org/net/ipv4/netfilter/Config.in linux/net/ipv4/netfilter/Config.in +--- linux_org/net/ipv4/netfilter/Config.in 2003-08-13 19:19:30.000000000 +0200 ++++ linux/net/ipv4/netfilter/Config.in 2006-10-27 14:11:52.000000000 +0200 +@@ -7,6 +7,11 @@ + tristate 'Connection tracking (required for masq/NAT)' CONFIG_IP_NF_CONNTRACK + if [ "$CONFIG_IP_NF_CONNTRACK" != "n" ]; then + dep_tristate ' FTP protocol support' CONFIG_IP_NF_FTP $CONFIG_IP_NF_CONNTRACK ++ dep_tristate ' GRE protocol support' CONFIG_IP_NF_CT_PROTO_GRE $CONFIG_IP_NF_CONNTRACK ++ dep_tristate ' PPTP protocol support' CONFIG_IP_NF_PPTP $CONFIG_IP_NF_CONNTRACK ++ if [ "$CONFIG_IP_NF_PPTP" != "n" ]; then ++ bool ' PPTP verbose debug' CONFIG_IP_NF_PPTP_DEBUG ++ fi + dep_tristate ' Amanda protocol support' CONFIG_IP_NF_AMANDA $CONFIG_IP_NF_CONNTRACK + dep_tristate ' TFTP protocol support' CONFIG_IP_NF_TFTP $CONFIG_IP_NF_CONNTRACK + dep_tristate ' IRC protocol support' CONFIG_IP_NF_IRC $CONFIG_IP_NF_CONNTRACK +@@ -67,6 +72,20 @@ + fi + fi + bool ' NAT of local connections (READ HELP)' CONFIG_IP_NF_NAT_LOCAL ++ if [ "$CONFIG_IP_NF_PPTP" = "m" ]; then ++ define_tristate CONFIG_IP_NF_NAT_PPTP m ++ else ++ if [ "$CONFIG_IP_NF_PPTP" = "y" ]; then ++ define_tristate CONFIG_IP_NF_NAT_PPTP $CONFIG_IP_NF_NAT ++ fi ++ fi ++ if [ "$CONFIG_IP_NF_CT_PROTO_GRE" = "m" ]; then ++ define_tristate CONFIG_IP_NF_NAT_PROTO_GRE m ++ else ++ if [ "$CONFIG_IP_NF_CT_PROTO_GRE" = "y" ]; then ++ define_tristate CONFIG_IP_NF_NAT_PROTO_GRE $CONFIG_IP_NF_NAT ++ fi ++ fi + if [ "$CONFIG_EXPERIMENTAL" = "y" ]; then + dep_tristate ' Basic SNMP-ALG support (EXPERIMENTAL)' CONFIG_IP_NF_NAT_SNMP_BASIC $CONFIG_IP_NF_NAT + fi +diff -uNr linux_org/net/ipv4/netfilter/Makefile linux/net/ipv4/netfilter/Makefile +--- linux_org/net/ipv4/netfilter/Makefile 2003-08-13 19:19:30.000000000 +0200 ++++ linux/net/ipv4/netfilter/Makefile 2006-10-27 14:11:52.000000000 +0200 +@@ -30,8 +30,21 @@ + + # connection tracking + obj-$(CONFIG_IP_NF_CONNTRACK) += ip_conntrack.o ++ ++# connection tracking protocol helpers ++obj-$(CONFIG_IP_NF_CT_PROTO_GRE) += ip_conntrack_proto_gre.o ++ifdef CONFIG_IP_NF_CT_PROTO_GRE ++ export-objs += ip_conntrack_proto_gre.o ++endif ++ ++# NAT protocol helpers ++obj-$(CONFIG_IP_NF_NAT_PROTO_GRE) += ip_nat_proto_gre.o + + # connection tracking helpers ++obj-$(CONFIG_IP_NF_PPTP) += ip_conntrack_pptp.o ++ifdef CONFIG_IP_NF_NAT_PPTP ++ export-objs += ip_conntrack_pptp.o ++endif + obj-$(CONFIG_IP_NF_AMANDA) += ip_conntrack_amanda.o + ifdef CONFIG_IP_NF_AMANDA + export-objs += ip_conntrack_amanda.o +@@ -49,6 +62,7 @@ + endif + + # NAT helpers ++obj-$(CONFIG_IP_NF_NAT_PPTP) += ip_nat_pptp.o + obj-$(CONFIG_IP_NF_NAT_AMANDA) += ip_nat_amanda.o + obj-$(CONFIG_IP_NF_NAT_TFTP) += ip_nat_tftp.o + obj-$(CONFIG_IP_NF_NAT_FTP) += ip_nat_ftp.o +diff -uNr linux_org/net/ipv4/netfilter/ip_conntrack_core.c linux/net/ipv4/netfilter/ip_conntrack_core.c +--- linux_org/net/ipv4/netfilter/ip_conntrack_core.c 2004-11-24 12:14:04.000000000 +0100 ++++ linux/net/ipv4/netfilter/ip_conntrack_core.c 2006-10-27 14:11:52.000000000 +0200 +@@ -142,6 +142,8 @@ + tuple->dst.ip = iph->daddr; + tuple->dst.protonum = iph->protocol; + ++ tuple->src.u.all = tuple->dst.u.all = 0; ++ + ret = protocol->pkt_to_tuple((u_int32_t *)iph + iph->ihl, + len - 4*iph->ihl, + tuple); +@@ -157,6 +159,8 @@ + inverse->dst.ip = orig->src.ip; + inverse->dst.protonum = orig->dst.protonum; + ++ inverse->src.u.all = inverse->dst.u.all = 0; ++ + return protocol->invert_tuple(inverse, orig); + } + +@@ -945,8 +949,8 @@ + * so there is no need to use the tuple lock too */ + + DEBUGP("ip_conntrack_expect_related %p\n", related_to); +- DEBUGP("tuple: "); DUMP_TUPLE(&expect->tuple); +- DEBUGP("mask: "); DUMP_TUPLE(&expect->mask); ++ DEBUGP("tuple: "); DUMP_TUPLE_RAW(&expect->tuple); ++ DEBUGP("mask: "); DUMP_TUPLE_RAW(&expect->mask); + + old = LIST_FIND(&ip_conntrack_expect_list, resent_expect, + struct ip_conntrack_expect *, &expect->tuple, +@@ -1063,15 +1067,14 @@ + + MUST_BE_READ_LOCKED(&ip_conntrack_lock); + WRITE_LOCK(&ip_conntrack_expect_tuple_lock); +- + DEBUGP("change_expect:\n"); +- DEBUGP("exp tuple: "); DUMP_TUPLE(&expect->tuple); +- DEBUGP("exp mask: "); DUMP_TUPLE(&expect->mask); +- DEBUGP("newtuple: "); DUMP_TUPLE(newtuple); ++ DEBUGP("exp tuple: "); DUMP_TUPLE_RAW(&expect->tuple); ++ DEBUGP("exp mask: "); DUMP_TUPLE_RAW(&expect->mask); ++ DEBUGP("newtuple: "); DUMP_TUPLE_RAW(newtuple); + if (expect->ct_tuple.dst.protonum == 0) { + /* Never seen before */ + DEBUGP("change expect: never seen before\n"); +- if (!ip_ct_tuple_equal(&expect->tuple, newtuple) ++ if (!ip_ct_tuple_mask_cmp(&expect->tuple, newtuple, &expect->mask) + && LIST_FIND(&ip_conntrack_expect_list, expect_clash, + struct ip_conntrack_expect *, newtuple, &expect->mask)) { + /* Force NAT to find an unused tuple */ +diff -uNr linux_org/net/ipv4/netfilter/ip_conntrack_core.c.orig linux/net/ipv4/netfilter/ip_conntrack_core.c.orig +--- linux_org/net/ipv4/netfilter/ip_conntrack_core.c.orig 1970-01-01 01:00:00.000000000 +0100 ++++ linux/net/ipv4/netfilter/ip_conntrack_core.c.orig 2004-11-24 12:14:04.000000000 +0100 +@@ -0,0 +1,1446 @@ ++/* Connection state tracking for netfilter. This is separated from, ++ but required by, the NAT layer; it can also be used by an iptables ++ extension. */ ++ ++/* (c) 1999 Paul `Rusty' Russell. Licenced under the GNU General ++ * Public Licence. ++ * ++ * 23 Apr 2001: Harald Welte <laforge@gnumonks.org> ++ * - new API and handling of conntrack/nat helpers ++ * - now capable of multiple expectations for one master ++ * 16 Jul 2002: Harald Welte <laforge@gnumonks.org> ++ * - add usage/reference counts to ip_conntrack_expect ++ * - export ip_conntrack[_expect]_{find_get,put} functions ++ * */ ++ ++#include <linux/version.h> ++#include <linux/config.h> ++#include <linux/types.h> ++#include <linux/ip.h> ++#include <linux/netfilter.h> ++#include <linux/netfilter_ipv4.h> ++#include <linux/module.h> ++#include <linux/skbuff.h> ++#include <linux/proc_fs.h> ++#include <linux/vmalloc.h> ++#include <linux/brlock.h> ++#include <net/checksum.h> ++#include <linux/stddef.h> ++#include <linux/sysctl.h> ++#include <linux/slab.h> ++#include <linux/random.h> ++#include <linux/jhash.h> ++/* For ERR_PTR(). Yeah, I know... --RR */ ++#include <linux/fs.h> ++ ++/* This rwlock protects the main hash table, protocol/helper/expected ++ registrations, conntrack timers*/ ++#define ASSERT_READ_LOCK(x) MUST_BE_READ_LOCKED(&ip_conntrack_lock) ++#define ASSERT_WRITE_LOCK(x) MUST_BE_WRITE_LOCKED(&ip_conntrack_lock) ++ ++#include <linux/netfilter_ipv4/ip_conntrack.h> ++#include <linux/netfilter_ipv4/ip_conntrack_protocol.h> ++#include <linux/netfilter_ipv4/ip_conntrack_helper.h> ++#include <linux/netfilter_ipv4/ip_conntrack_core.h> ++#include <linux/netfilter_ipv4/listhelp.h> ++ ++#define IP_CONNTRACK_VERSION "2.1" ++ ++#if 0 ++#define DEBUGP printk ++#else ++#define DEBUGP(format, args...) ++#endif ++ ++DECLARE_RWLOCK(ip_conntrack_lock); ++DECLARE_RWLOCK(ip_conntrack_expect_tuple_lock); ++ ++void (*ip_conntrack_destroyed)(struct ip_conntrack *conntrack) = NULL; ++LIST_HEAD(ip_conntrack_expect_list); ++LIST_HEAD(protocol_list); ++static LIST_HEAD(helpers); ++unsigned int ip_conntrack_htable_size = 0; ++int ip_conntrack_max = 0; ++static atomic_t ip_conntrack_count = ATOMIC_INIT(0); ++struct list_head *ip_conntrack_hash; ++static kmem_cache_t *ip_conntrack_cachep; ++ ++extern struct ip_conntrack_protocol ip_conntrack_generic_protocol; ++ ++static inline int proto_cmpfn(const struct ip_conntrack_protocol *curr, ++ u_int8_t protocol) ++{ ++ return protocol == curr->proto; ++} ++ ++struct ip_conntrack_protocol *__ip_ct_find_proto(u_int8_t protocol) ++{ ++ struct ip_conntrack_protocol *p; ++ ++ MUST_BE_READ_LOCKED(&ip_conntrack_lock); ++ p = LIST_FIND(&protocol_list, proto_cmpfn, ++ struct ip_conntrack_protocol *, protocol); ++ if (!p) ++ p = &ip_conntrack_generic_protocol; ++ ++ return p; ++} ++ ++struct ip_conntrack_protocol *ip_ct_find_proto(u_int8_t protocol) ++{ ++ struct ip_conntrack_protocol *p; ++ ++ READ_LOCK(&ip_conntrack_lock); ++ p = __ip_ct_find_proto(protocol); ++ READ_UNLOCK(&ip_conntrack_lock); ++ return p; ++} ++ ++inline void ++ip_conntrack_put(struct ip_conntrack *ct) ++{ ++ IP_NF_ASSERT(ct); ++ IP_NF_ASSERT(ct->infos[0].master); ++ /* nf_conntrack_put wants to go via an info struct, so feed it ++ one at random. */ ++ nf_conntrack_put(&ct->infos[0]); ++} ++ ++static int ip_conntrack_hash_rnd_initted; ++static unsigned int ip_conntrack_hash_rnd; ++ ++static u_int32_t ++hash_conntrack(const struct ip_conntrack_tuple *tuple) ++{ ++#if 0 ++ dump_tuple(tuple); ++#endif ++ return (jhash_3words(tuple->src.ip, ++ (tuple->dst.ip ^ tuple->dst.protonum), ++ (tuple->src.u.all | (tuple->dst.u.all << 16)), ++ ip_conntrack_hash_rnd) % ip_conntrack_htable_size); ++} ++ ++inline int ++get_tuple(const struct iphdr *iph, size_t len, ++ struct ip_conntrack_tuple *tuple, ++ struct ip_conntrack_protocol *protocol) ++{ ++ int ret; ++ ++ /* Never happen */ ++ if (iph->frag_off & htons(IP_OFFSET)) { ++ printk("ip_conntrack_core: Frag of proto %u.\n", ++ iph->protocol); ++ return 0; ++ } ++ /* Guarantee 8 protocol bytes: if more wanted, use len param */ ++ else if (iph->ihl * 4 + 8 > len) ++ return 0; ++ ++ tuple->src.ip = iph->saddr; ++ tuple->dst.ip = iph->daddr; ++ tuple->dst.protonum = iph->protocol; ++ ++ ret = protocol->pkt_to_tuple((u_int32_t *)iph + iph->ihl, ++ len - 4*iph->ihl, ++ tuple); ++ return ret; ++} ++ ++static int ++invert_tuple(struct ip_conntrack_tuple *inverse, ++ const struct ip_conntrack_tuple *orig, ++ const struct ip_conntrack_protocol *protocol) ++{ ++ inverse->src.ip = orig->dst.ip; ++ inverse->dst.ip = orig->src.ip; ++ inverse->dst.protonum = orig->dst.protonum; ++ ++ return protocol->invert_tuple(inverse, orig); ++} ++ ++ ++/* ip_conntrack_expect helper functions */ ++ ++/* Compare tuple parts depending on mask. */ ++static inline int expect_cmp(const struct ip_conntrack_expect *i, ++ const struct ip_conntrack_tuple *tuple) ++{ ++ MUST_BE_READ_LOCKED(&ip_conntrack_expect_tuple_lock); ++ return ip_ct_tuple_mask_cmp(tuple, &i->tuple, &i->mask); ++} ++ ++static void ++destroy_expect(struct ip_conntrack_expect *exp) ++{ ++ DEBUGP("destroy_expect(%p) use=%d\n", exp, atomic_read(&exp->use)); ++ IP_NF_ASSERT(atomic_read(&exp->use) == 0); ++ IP_NF_ASSERT(!timer_pending(&exp->timeout)); ++ ++ kfree(exp); ++} ++ ++inline void ip_conntrack_expect_put(struct ip_conntrack_expect *exp) ++{ ++ IP_NF_ASSERT(exp); ++ ++ if (atomic_dec_and_test(&exp->use)) { ++ /* usage count dropped to zero */ ++ destroy_expect(exp); ++ } ++} ++ ++static inline struct ip_conntrack_expect * ++__ip_ct_expect_find(const struct ip_conntrack_tuple *tuple) ++{ ++ MUST_BE_READ_LOCKED(&ip_conntrack_lock); ++ MUST_BE_READ_LOCKED(&ip_conntrack_expect_tuple_lock); ++ return LIST_FIND(&ip_conntrack_expect_list, expect_cmp, ++ struct ip_conntrack_expect *, tuple); ++} ++ ++/* Find a expectation corresponding to a tuple. */ ++struct ip_conntrack_expect * ++ip_conntrack_expect_find_get(const struct ip_conntrack_tuple *tuple) ++{ ++ struct ip_conntrack_expect *exp; ++ ++ READ_LOCK(&ip_conntrack_lock); ++ READ_LOCK(&ip_conntrack_expect_tuple_lock); ++ exp = __ip_ct_expect_find(tuple); ++ if (exp) ++ atomic_inc(&exp->use); ++ READ_UNLOCK(&ip_conntrack_expect_tuple_lock); ++ READ_UNLOCK(&ip_conntrack_lock); ++ ++ return exp; ++} ++ ++/* remove one specific expectation from all lists and drop refcount, ++ * does _NOT_ delete the timer. */ ++static void __unexpect_related(struct ip_conntrack_expect *expect) ++{ ++ DEBUGP("unexpect_related(%p)\n", expect); ++ MUST_BE_WRITE_LOCKED(&ip_conntrack_lock); ++ ++ /* we're not allowed to unexpect a confirmed expectation! */ ++ IP_NF_ASSERT(!expect->sibling); ++ ++ /* delete from global and local lists */ ++ list_del(&expect->list); ++ list_del(&expect->expected_list); ++ ++ /* decrement expect-count of master conntrack */ ++ if (expect->expectant) ++ expect->expectant->expecting--; ++ ++ ip_conntrack_expect_put(expect); ++} ++ ++/* remove one specific expecatation from all lists, drop refcount ++ * and expire timer. ++ * This function can _NOT_ be called for confirmed expects! */ ++static void unexpect_related(struct ip_conntrack_expect *expect) ++{ ++ IP_NF_ASSERT(expect->expectant); ++ IP_NF_ASSERT(expect->expectant->helper); ++ /* if we are supposed to have a timer, but we can't delete ++ * it: race condition. __unexpect_related will ++ * be calledd by timeout function */ ++ if (expect->expectant->helper->timeout ++ && !del_timer(&expect->timeout)) ++ return; ++ ++ __unexpect_related(expect); ++} ++ ++/* delete all unconfirmed expectations for this conntrack */ ++static void remove_expectations(struct ip_conntrack *ct, int drop_refcount) ++{ ++ struct list_head *exp_entry, *next; ++ struct ip_conntrack_expect *exp; ++ ++ DEBUGP("remove_expectations(%p)\n", ct); ++ ++ list_for_each_safe(exp_entry, next, &ct->sibling_list) { ++ exp = list_entry(exp_entry, struct ip_conntrack_expect, ++ expected_list); ++ ++ /* we skip established expectations, as we want to delete ++ * the un-established ones only */ ++ if (exp->sibling) { ++ DEBUGP("remove_expectations: skipping established %p of %p\n", exp->sibling, ct); ++ if (drop_refcount) { ++ /* Indicate that this expectations parent is dead */ ++ ip_conntrack_put(exp->expectant); ++ exp->expectant = NULL; ++ } ++ continue; ++ } ++ ++ IP_NF_ASSERT(list_inlist(&ip_conntrack_expect_list, exp)); ++ IP_NF_ASSERT(exp->expectant == ct); ++ ++ /* delete expectation from global and private lists */ ++ unexpect_related(exp); ++ } ++} ++ ++static void ++clean_from_lists(struct ip_conntrack *ct) ++{ ++ unsigned int ho, hr; ++ ++ DEBUGP("clean_from_lists(%p)\n", ct); ++ MUST_BE_WRITE_LOCKED(&ip_conntrack_lock); ++ ++ ho = hash_conntrack(&ct->tuplehash[IP_CT_DIR_ORIGINAL].tuple); ++ hr = hash_conntrack(&ct->tuplehash[IP_CT_DIR_REPLY].tuple); ++ LIST_DELETE(&ip_conntrack_hash[ho], &ct->tuplehash[IP_CT_DIR_ORIGINAL]); ++ LIST_DELETE(&ip_conntrack_hash[hr], &ct->tuplehash[IP_CT_DIR_REPLY]); ++ ++ /* Destroy all un-established, pending expectations */ ++ remove_expectations(ct, 1); ++} ++ ++static void ++destroy_conntrack(struct nf_conntrack *nfct) ++{ ++ struct ip_conntrack *ct = (struct ip_conntrack *)nfct, *master = NULL; ++ struct ip_conntrack_protocol *proto; ++ ++ DEBUGP("destroy_conntrack(%p)\n", ct); ++ IP_NF_ASSERT(atomic_read(&nfct->use) == 0); ++ IP_NF_ASSERT(!timer_pending(&ct->timeout)); ++ ++ /* To make sure we don't get any weird locking issues here: ++ * destroy_conntrack() MUST NOT be called with a write lock ++ * to ip_conntrack_lock!!! -HW */ ++ proto = ip_ct_find_proto(ct->tuplehash[IP_CT_DIR_REPLY].tuple.dst.protonum); ++ if (proto && proto->destroy) ++ proto->destroy(ct); ++ ++ if (ip_conntrack_destroyed) ++ ip_conntrack_destroyed(ct); ++ ++ WRITE_LOCK(&ip_conntrack_lock); ++ /* Make sure don't leave any orphaned expectations lying around */ ++ if (ct->expecting) ++ remove_expectations(ct, 1); ++ ++ /* Delete our master expectation */ ++ if (ct->master) { ++ if (ct->master->expectant) { ++ /* can't call __unexpect_related here, ++ * since it would screw up expect_list */ ++ list_del(&ct->master->expected_list); ++ master = ct->master->expectant; ++ } ++ kfree(ct->master); ++ } ++ WRITE_UNLOCK(&ip_conntrack_lock); ++ ++ if (master) ++ ip_conntrack_put(master); ++ ++ DEBUGP("destroy_conntrack: returning ct=%p to slab\n", ct); ++ kmem_cache_free(ip_conntrack_cachep, ct); ++ atomic_dec(&ip_conntrack_count); ++} ++ ++static void death_by_timeout(unsigned long ul_conntrack) ++{ ++ struct ip_conntrack *ct = (void *)ul_conntrack; ++ ++ WRITE_LOCK(&ip_conntrack_lock); ++ clean_from_lists(ct); ++ WRITE_UNLOCK(&ip_conntrack_lock); ++ ip_conntrack_put(ct); ++} ++ ++static inline int ++conntrack_tuple_cmp(const struct ip_conntrack_tuple_hash *i, ++ const struct ip_conntrack_tuple *tuple, ++ const struct ip_conntrack *ignored_conntrack) ++{ ++ MUST_BE_READ_LOCKED(&ip_conntrack_lock); ++ return i->ctrack != ignored_conntrack ++ && ip_ct_tuple_equal(tuple, &i->tuple); ++} ++ ++static struct ip_conntrack_tuple_hash * ++__ip_conntrack_find(const struct ip_conntrack_tuple *tuple, ++ const struct ip_conntrack *ignored_conntrack) ++{ ++ struct ip_conntrack_tuple_hash *h; ++ unsigned int hash = hash_conntrack(tuple); ++ ++ MUST_BE_READ_LOCKED(&ip_conntrack_lock); ++ h = LIST_FIND(&ip_conntrack_hash[hash], ++ conntrack_tuple_cmp, ++ struct ip_conntrack_tuple_hash *, ++ tuple, ignored_conntrack); ++ return h; ++} ++ ++/* Find a connection corresponding to a tuple. */ ++struct ip_conntrack_tuple_hash * ++ip_conntrack_find_get(const struct ip_conntrack_tuple *tuple, ++ const struct ip_conntrack *ignored_conntrack) ++{ ++ struct ip_conntrack_tuple_hash *h; ++ ++ READ_LOCK(&ip_conntrack_lock); ++ h = __ip_conntrack_find(tuple, ignored_conntrack); ++ if (h) ++ atomic_inc(&h->ctrack->ct_general.use); ++ READ_UNLOCK(&ip_conntrack_lock); ++ ++ return h; ++} ++ ++static inline struct ip_conntrack * ++__ip_conntrack_get(struct nf_ct_info *nfct, enum ip_conntrack_info *ctinfo) ++{ ++ struct ip_conntrack *ct ++ = (struct ip_conntrack *)nfct->master; ++ ++ /* ctinfo is the index of the nfct inside the conntrack */ ++ *ctinfo = nfct - ct->infos; ++ IP_NF_ASSERT(*ctinfo >= 0 && *ctinfo < IP_CT_NUMBER); ++ return ct; ++} ++ ++/* Return conntrack and conntrack_info given skb->nfct->master */ ++struct ip_conntrack * ++ip_conntrack_get(struct sk_buff *skb, enum ip_conntrack_info *ctinfo) ++{ ++ if (skb->nfct) ++ return __ip_conntrack_get(skb->nfct, ctinfo); ++ return NULL; ++} ++ ++/* Confirm a connection given skb->nfct; places it in hash table */ ++int ++__ip_conntrack_confirm(struct nf_ct_info *nfct) ++{ ++ unsigned int hash, repl_hash; ++ struct ip_conntrack *ct; ++ enum ip_conntrack_info ctinfo; ++ ++ ct = __ip_conntrack_get(nfct, &ctinfo); ++ ++ /* ipt_REJECT uses ip_conntrack_attach to attach related ++ ICMP/TCP RST packets in other direction. Actual packet ++ which created connection will be IP_CT_NEW or for an ++ expected connection, IP_CT_RELATED. */ ++ if (CTINFO2DIR(ctinfo) != IP_CT_DIR_ORIGINAL) ++ return NF_ACCEPT; ++ ++ hash = hash_conntrack(&ct->tuplehash[IP_CT_DIR_ORIGINAL].tuple); ++ repl_hash = hash_conntrack(&ct->tuplehash[IP_CT_DIR_REPLY].tuple); ++ ++ /* We're not in hash table, and we refuse to set up related ++ connections for unconfirmed conns. But packet copies and ++ REJECT will give spurious warnings here. */ ++ /* IP_NF_ASSERT(atomic_read(&ct->ct_general.use) == 1); */ ++ ++ /* No external references means noone else could have ++ confirmed us. */ ++ IP_NF_ASSERT(!is_confirmed(ct)); ++ DEBUGP("Confirming conntrack %p\n", ct); ++ ++ WRITE_LOCK(&ip_conntrack_lock); ++ /* See if there's one in the list already, including reverse: ++ NAT could have grabbed it without realizing, since we're ++ not in the hash. If there is, we lost race. */ ++ if (!LIST_FIND(&ip_conntrack_hash[hash], ++ conntrack_tuple_cmp, ++ struct ip_conntrack_tuple_hash *, ++ &ct->tuplehash[IP_CT_DIR_ORIGINAL].tuple, NULL) ++ && !LIST_FIND(&ip_conntrack_hash[repl_hash], ++ conntrack_tuple_cmp, ++ struct ip_conntrack_tuple_hash *, ++ &ct->tuplehash[IP_CT_DIR_REPLY].tuple, NULL)) { ++ list_prepend(&ip_conntrack_hash[hash], ++ &ct->tuplehash[IP_CT_DIR_ORIGINAL]); ++ list_prepend(&ip_conntrack_hash[repl_hash], ++ &ct->tuplehash[IP_CT_DIR_REPLY]); ++ /* Timer relative to confirmation time, not original ++ setting time, otherwise we'd get timer wrap in ++ weird delay cases. */ ++ ct->timeout.expires += jiffies; ++ add_timer(&ct->timeout); ++ atomic_inc(&ct->ct_general.use); ++ set_bit(IPS_CONFIRMED_BIT, &ct->status); ++ WRITE_UNLOCK(&ip_conntrack_lock); ++ return NF_ACCEPT; ++ } ++ ++ WRITE_UNLOCK(&ip_conntrack_lock); ++ return NF_DROP; ++} ++ ++/* Returns true if a connection correspondings to the tuple (required ++ for NAT). */ ++int ++ip_conntrack_tuple_taken(const struct ip_conntrack_tuple *tuple, ++ const struct ip_conntrack *ignored_conntrack) ++{ ++ struct ip_conntrack_tuple_hash *h; ++ ++ READ_LOCK(&ip_conntrack_lock); ++ h = __ip_conntrack_find(tuple, ignored_conntrack); ++ READ_UNLOCK(&ip_conntrack_lock); ++ ++ return h != NULL; ++} ++ ++/* Returns conntrack if it dealt with ICMP, and filled in skb fields */ ++struct ip_conntrack * ++icmp_error_track(struct sk_buff *skb, ++ enum ip_conntrack_info *ctinfo, ++ unsigned int hooknum) ++{ ++ const struct iphdr *iph = skb->nh.iph; ++ struct icmphdr *hdr; ++ struct ip_conntrack_tuple innertuple, origtuple; ++ struct iphdr *inner; ++ size_t datalen; ++ struct ip_conntrack_protocol *innerproto; ++ struct ip_conntrack_tuple_hash *h; ++ ++ IP_NF_ASSERT(iph->protocol == IPPROTO_ICMP); ++ IP_NF_ASSERT(skb->nfct == NULL); ++ ++ hdr = (struct icmphdr *)((u_int32_t *)iph + iph->ihl); ++ inner = (struct iphdr *)(hdr + 1); ++ datalen = skb->len - iph->ihl*4 - sizeof(*hdr); ++ ++ if (skb->len < iph->ihl * 4 + sizeof(*hdr) + sizeof(*iph)) { ++ DEBUGP("icmp_error_track: too short\n"); ++ return NULL; ++ } ++ ++ if (hdr->type != ICMP_DEST_UNREACH ++ && hdr->type != ICMP_SOURCE_QUENCH ++ && hdr->type != ICMP_TIME_EXCEEDED ++ && hdr->type != ICMP_PARAMETERPROB ++ && hdr->type != ICMP_REDIRECT) ++ return NULL; ++ ++ /* Ignore ICMP's containing fragments (shouldn't happen) */ ++ if (inner->frag_off & htons(IP_OFFSET)) { ++ DEBUGP("icmp_error_track: fragment of proto %u\n", ++ inner->protocol); ++ return NULL; ++ } ++ ++ /* Ignore it if the checksum's bogus. */ ++ if (ip_compute_csum((unsigned char *)hdr, sizeof(*hdr) + datalen)) { ++ DEBUGP("icmp_error_track: bad csum\n"); ++ return NULL; ++ } ++ ++ innerproto = ip_ct_find_proto(inner->protocol); ++ /* Are they talking about one of our connections? */ ++ if (inner->ihl * 4 + 8 > datalen ++ || !get_tuple(inner, datalen, &origtuple, innerproto)) { ++ DEBUGP("icmp_error: ! get_tuple p=%u (%u*4+%u dlen=%u)\n", ++ inner->protocol, inner->ihl, 8, ++ datalen); ++ return NULL; ++ } ++ ++ /* Ordinarily, we'd expect the inverted tupleproto, but it's ++ been preserved inside the ICMP. */ ++ if (!invert_tuple(&innertuple, &origtuple, innerproto)) { ++ DEBUGP("icmp_error_track: Can't invert tuple\n"); ++ return NULL; ++ } ++ ++ *ctinfo = IP_CT_RELATED; ++ ++ h = ip_conntrack_find_get(&innertuple, NULL); ++ if (!h) { ++ /* Locally generated ICMPs will match inverted if they ++ haven't been SNAT'ed yet */ ++ /* FIXME: NAT code has to handle half-done double NAT --RR */ ++ if (hooknum == NF_IP_LOCAL_OUT) ++ h = ip_conntrack_find_get(&origtuple, NULL); ++ ++ if (!h) { ++ DEBUGP("icmp_error_track: no match\n"); ++ return NULL; ++ } ++ /* Reverse direction from that found */ ++ if (DIRECTION(h) != IP_CT_DIR_REPLY) ++ *ctinfo += IP_CT_IS_REPLY; ++ } else { ++ if (DIRECTION(h) == IP_CT_DIR_REPLY) ++ *ctinfo += IP_CT_IS_REPLY; ++ } ++ ++ /* Update skb to refer to this connection */ ++ skb->nfct = &h->ctrack->infos[*ctinfo]; ++ return h->ctrack; ++} ++ ++/* There's a small race here where we may free a just-assured ++ connection. Too bad: we're in trouble anyway. */ ++static inline int unreplied(const struct ip_conntrack_tuple_hash *i) ++{ ++ return !(test_bit(IPS_ASSURED_BIT, &i->ctrack->status)); ++} ++ ++static int early_drop(struct list_head *chain) ++{ ++ /* Traverse backwards: gives us oldest, which is roughly LRU */ ++ struct ip_conntrack_tuple_hash *h; ++ int dropped = 0; ++ ++ READ_LOCK(&ip_conntrack_lock); ++ h = LIST_FIND_B(chain, unreplied, struct ip_conntrack_tuple_hash *); ++ if (h) ++ atomic_inc(&h->ctrack->ct_general.use); ++ READ_UNLOCK(&ip_conntrack_lock); ++ ++ if (!h) ++ return dropped; ++ ++ if (del_timer(&h->ctrack->timeout)) { ++ death_by_timeout((unsigned long)h->ctrack); ++ dropped = 1; ++ } ++ ip_conntrack_put(h->ctrack); ++ return dropped; ++} ++ ++static inline int helper_cmp(const struct ip_conntrack_helper *i, ++ const struct ip_conntrack_tuple *rtuple) ++{ ++ return ip_ct_tuple_mask_cmp(rtuple, &i->tuple, &i->mask); ++} ++ ++struct ip_conntrack_helper *ip_ct_find_helper(const struct ip_conntrack_tuple *tuple) ++{ ++ return LIST_FIND(&helpers, helper_cmp, ++ struct ip_conntrack_helper *, ++ tuple); ++} ++ ++/* Allocate a new conntrack: we return -ENOMEM if classification ++ failed due to stress. Otherwise it really is unclassifiable. */ ++static struct ip_conntrack_tuple_hash * ++init_conntrack(const struct ip_conntrack_tuple *tuple, ++ struct ip_conntrack_protocol *protocol, ++ struct sk_buff *skb) ++{ ++ struct ip_conntrack *conntrack; ++ struct ip_conntrack_tuple repl_tuple; ++ size_t hash; ++ struct ip_conntrack_expect *expected; ++ int i; ++ static unsigned int drop_next = 0; ++ ++ if (!ip_conntrack_hash_rnd_initted) { ++ get_random_bytes(&ip_conntrack_hash_rnd, 4); ++ ip_conntrack_hash_rnd_initted = 1; ++ } ++ ++ hash = hash_conntrack(tuple); ++ ++ if (ip_conntrack_max && ++ atomic_read(&ip_conntrack_count) >= ip_conntrack_max) { ++ /* Try dropping from random chain, or else from the ++ chain about to put into (in case they're trying to ++ bomb one hash chain). */ ++ unsigned int next = (drop_next++)%ip_conntrack_htable_size; ++ ++ if (!early_drop(&ip_conntrack_hash[next]) ++ && !early_drop(&ip_conntrack_hash[hash])) { ++ if (net_ratelimit()) ++ printk(KERN_WARNING ++ "ip_conntrack: table full, dropping" ++ " packet.\n"); ++ return ERR_PTR(-ENOMEM); ++ } ++ } ++ ++ if (!invert_tuple(&repl_tuple, tuple, protocol)) { ++ DEBUGP("Can't invert tuple.\n"); ++ return NULL; ++ } ++ ++ conntrack = kmem_cache_alloc(ip_conntrack_cachep, GFP_ATOMIC); ++ if (!conntrack) { ++ DEBUGP("Can't allocate conntrack.\n"); ++ return ERR_PTR(-ENOMEM); ++ } ++ ++ memset(conntrack, 0, sizeof(*conntrack)); ++ atomic_set(&conntrack->ct_general.use, 1); ++ conntrack->ct_general.destroy = destroy_conntrack; ++ conntrack->tuplehash[IP_CT_DIR_ORIGINAL].tuple = *tuple; ++ conntrack->tuplehash[IP_CT_DIR_ORIGINAL].ctrack = conntrack; ++ conntrack->tuplehash[IP_CT_DIR_REPLY].tuple = repl_tuple; ++ conntrack->tuplehash[IP_CT_DIR_REPLY].ctrack = conntrack; ++ for (i=0; i < IP_CT_NUMBER; i++) ++ conntrack->infos[i].master = &conntrack->ct_general; ++ ++ if (!protocol->new(conntrack, skb->nh.iph, skb->len)) { ++ kmem_cache_free(ip_conntrack_cachep, conntrack); ++ return NULL; ++ } ++ /* Don't set timer yet: wait for confirmation */ ++ init_timer(&conntrack->timeout); ++ conntrack->timeout.data = (unsigned long)conntrack; ++ conntrack->timeout.function = death_by_timeout; ++ ++ INIT_LIST_HEAD(&conntrack->sibling_list); ++ ++ WRITE_LOCK(&ip_conntrack_lock); ++ /* Need finding and deleting of expected ONLY if we win race */ ++ READ_LOCK(&ip_conntrack_expect_tuple_lock); ++ expected = LIST_FIND(&ip_conntrack_expect_list, expect_cmp, ++ struct ip_conntrack_expect *, tuple); ++ READ_UNLOCK(&ip_conntrack_expect_tuple_lock); ++ ++ /* If master is not in hash table yet (ie. packet hasn't left ++ this machine yet), how can other end know about expected? ++ Hence these are not the droids you are looking for (if ++ master ct never got confirmed, we'd hold a reference to it ++ and weird things would happen to future packets). */ ++ if (expected && !is_confirmed(expected->expectant)) ++ expected = NULL; ++ ++ /* Look up the conntrack helper for master connections only */ ++ if (!expected) ++ conntrack->helper = ip_ct_find_helper(&repl_tuple); ++ ++ /* If the expectation is dying, then this is a looser. */ ++ if (expected ++ && expected->expectant->helper->timeout ++ && ! del_timer(&expected->timeout)) ++ expected = NULL; ++ ++ if (expected) { ++ DEBUGP("conntrack: expectation arrives ct=%p exp=%p\n", ++ conntrack, expected); ++ /* Welcome, Mr. Bond. We've been expecting you... */ ++ __set_bit(IPS_EXPECTED_BIT, &conntrack->status); ++ conntrack->master = expected; ++ expected->sibling = conntrack; ++ LIST_DELETE(&ip_conntrack_expect_list, expected); ++ expected->expectant->expecting--; ++ nf_conntrack_get(&master_ct(conntrack)->infos[0]); ++ } ++ atomic_inc(&ip_conntrack_count); ++ WRITE_UNLOCK(&ip_conntrack_lock); ++ ++ if (expected && expected->expectfn) ++ expected->expectfn(conntrack); ++ return &conntrack->tuplehash[IP_CT_DIR_ORIGINAL]; ++} ++ ++/* On success, returns conntrack ptr, sets skb->nfct and ctinfo */ ++static inline struct ip_conntrack * ++resolve_normal_ct(struct sk_buff *skb, ++ struct ip_conntrack_protocol *proto, ++ int *set_reply, ++ unsigned int hooknum, ++ enum ip_conntrack_info *ctinfo) ++{ ++ struct ip_conntrack_tuple tuple; ++ struct ip_conntrack_tuple_hash *h; ++ ++ IP_NF_ASSERT((skb->nh.iph->frag_off & htons(IP_OFFSET)) == 0); ++ ++ if (!get_tuple(skb->nh.iph, skb->len, &tuple, proto)) ++ return NULL; ++ ++ /* look for tuple match */ ++ h = ip_conntrack_find_get(&tuple, NULL); ++ if (!h) { ++ h = init_conntrack(&tuple, proto, skb); ++ if (!h) ++ return NULL; ++ if (IS_ERR(h)) ++ return (void *)h; ++ } ++ ++ /* It exists; we have (non-exclusive) reference. */ ++ if (DIRECTION(h) == IP_CT_DIR_REPLY) { ++ *ctinfo = IP_CT_ESTABLISHED + IP_CT_IS_REPLY; ++ /* Please set reply bit if this packet OK */ ++ *set_reply = 1; ++ } else { ++ /* Once we've had two way comms, always ESTABLISHED. */ ++ if (test_bit(IPS_SEEN_REPLY_BIT, &h->ctrack->status)) { ++ DEBUGP("ip_conntrack_in: normal packet for %p\n", ++ h->ctrack); ++ *ctinfo = IP_CT_ESTABLISHED; ++ } else if (test_bit(IPS_EXPECTED_BIT, &h->ctrack->status)) { ++ DEBUGP("ip_conntrack_in: related packet for %p\n", ++ h->ctrack); ++ *ctinfo = IP_CT_RELATED; ++ } else { ++ DEBUGP("ip_conntrack_in: new packet for %p\n", ++ h->ctrack); ++ *ctinfo = IP_CT_NEW; ++ } ++ *set_reply = 0; ++ } ++ skb->nfct = &h->ctrack->infos[*ctinfo]; ++ return h->ctrack; ++} ++ ++/* Netfilter hook itself. */ ++unsigned int ip_conntrack_in(unsigned int hooknum, ++ struct sk_buff **pskb, ++ const struct net_device *in, ++ const struct net_device *out, ++ int (*okfn)(struct sk_buff *)) ++{ ++ struct ip_conntrack *ct; ++ enum ip_conntrack_info ctinfo; ++ struct ip_conntrack_protocol *proto; ++ int set_reply; ++ int ret; ++ ++ /* FIXME: Do this right please. --RR */ ++ (*pskb)->nfcache |= NFC_UNKNOWN; ++ ++/* Doesn't cover locally-generated broadcast, so not worth it. */ ++#if 0 ++ /* Ignore broadcast: no `connection'. */ ++ if ((*pskb)->pkt_type == PACKET_BROADCAST) { ++ printk("Broadcast packet!\n"); ++ return NF_ACCEPT; ++ } else if (((*pskb)->nh.iph->daddr & htonl(0x000000FF)) ++ == htonl(0x000000FF)) { ++ printk("Should bcast: %u.%u.%u.%u->%u.%u.%u.%u (sk=%p, ptype=%u)\n", ++ NIPQUAD((*pskb)->nh.iph->saddr), ++ NIPQUAD((*pskb)->nh.iph->daddr), ++ (*pskb)->sk, (*pskb)->pkt_type); ++ } ++#endif ++ ++ /* Previously seen (loopback)? Ignore. Do this before ++ fragment check. */ ++ if ((*pskb)->nfct) ++ return NF_ACCEPT; ++ ++ /* Gather fragments. */ ++ if ((*pskb)->nh.iph->frag_off & htons(IP_MF|IP_OFFSET)) { ++ *pskb = ip_ct_gather_frags(*pskb); ++ if (!*pskb) ++ return NF_STOLEN; ++ } ++ ++ proto = ip_ct_find_proto((*pskb)->nh.iph->protocol); ++ ++ /* It may be an icmp error... */ ++ if ((*pskb)->nh.iph->protocol == IPPROTO_ICMP ++ && icmp_error_track(*pskb, &ctinfo, hooknum)) ++ return NF_ACCEPT; ++ ++ if (!(ct = resolve_normal_ct(*pskb, proto,&set_reply,hooknum,&ctinfo))) ++ /* Not valid part of a connection */ ++ return NF_ACCEPT; ++ ++ if (IS_ERR(ct)) ++ /* Too stressed to deal. */ ++ return NF_DROP; ++ ++ IP_NF_ASSERT((*pskb)->nfct); ++ ++ ret = proto->packet(ct, (*pskb)->nh.iph, (*pskb)->len, ctinfo); ++ if (ret == -1) { ++ /* Invalid */ ++ nf_conntrack_put((*pskb)->nfct); ++ (*pskb)->nfct = NULL; ++ return NF_ACCEPT; ++ } ++ ++ if (ret != NF_DROP && ct->helper) { ++ ret = ct->helper->help((*pskb)->nh.iph, (*pskb)->len, ++ ct, ctinfo); ++ if (ret == -1) { ++ /* Invalid */ ++ nf_conntrack_put((*pskb)->nfct); ++ (*pskb)->nfct = NULL; ++ return NF_ACCEPT; ++ } ++ } ++ if (set_reply) ++ set_bit(IPS_SEEN_REPLY_BIT, &ct->status); ++ ++ return ret; ++} ++ ++int invert_tuplepr(struct ip_conntrack_tuple *inverse, ++ const struct ip_conntrack_tuple *orig) ++{ ++ return invert_tuple(inverse, orig, ip_ct_find_proto(orig->dst.protonum)); ++} ++ ++static inline int resent_expect(const struct ip_conntrack_expect *i, ++ const struct ip_conntrack_tuple *tuple, ++ const struct ip_conntrack_tuple *mask) ++{ ++ DEBUGP("resent_expect\n"); ++ DEBUGP(" tuple: "); DUMP_TUPLE(&i->tuple); ++ DEBUGP("ct_tuple: "); DUMP_TUPLE(&i->ct_tuple); ++ DEBUGP("test tuple: "); DUMP_TUPLE(tuple); ++ return (((i->ct_tuple.dst.protonum == 0 && ip_ct_tuple_equal(&i->tuple, tuple)) ++ || (i->ct_tuple.dst.protonum && ip_ct_tuple_equal(&i->ct_tuple, tuple))) ++ && ip_ct_tuple_equal(&i->mask, mask)); ++} ++ ++/* Would two expected things clash? */ ++static inline int expect_clash(const struct ip_conntrack_expect *i, ++ const struct ip_conntrack_tuple *tuple, ++ const struct ip_conntrack_tuple *mask) ++{ ++ /* Part covered by intersection of masks must be unequal, ++ otherwise they clash */ ++ struct ip_conntrack_tuple intersect_mask ++ = { { i->mask.src.ip & mask->src.ip, ++ { i->mask.src.u.all & mask->src.u.all } }, ++ { i->mask.dst.ip & mask->dst.ip, ++ { i->mask.dst.u.all & mask->dst.u.all }, ++ i->mask.dst.protonum & mask->dst.protonum } }; ++ ++ return ip_ct_tuple_mask_cmp(&i->tuple, tuple, &intersect_mask); ++} ++ ++inline void ip_conntrack_unexpect_related(struct ip_conntrack_expect *expect) ++{ ++ WRITE_LOCK(&ip_conntrack_lock); ++ unexpect_related(expect); ++ WRITE_UNLOCK(&ip_conntrack_lock); ++} ++ ++static void expectation_timed_out(unsigned long ul_expect) ++{ ++ struct ip_conntrack_expect *expect = (void *) ul_expect; ++ ++ DEBUGP("expectation %p timed out\n", expect); ++ WRITE_LOCK(&ip_conntrack_lock); ++ __unexpect_related(expect); ++ WRITE_UNLOCK(&ip_conntrack_lock); ++} ++ ++/* Add a related connection. */ ++int ip_conntrack_expect_related(struct ip_conntrack *related_to, ++ struct ip_conntrack_expect *expect) ++{ ++ struct ip_conntrack_expect *old, *new; ++ int ret = 0; ++ ++ WRITE_LOCK(&ip_conntrack_lock); ++ /* Because of the write lock, no reader can walk the lists, ++ * so there is no need to use the tuple lock too */ ++ ++ DEBUGP("ip_conntrack_expect_related %p\n", related_to); ++ DEBUGP("tuple: "); DUMP_TUPLE(&expect->tuple); ++ DEBUGP("mask: "); DUMP_TUPLE(&expect->mask); ++ ++ old = LIST_FIND(&ip_conntrack_expect_list, resent_expect, ++ struct ip_conntrack_expect *, &expect->tuple, ++ &expect->mask); ++ if (old) { ++ /* Helper private data may contain offsets but no pointers ++ pointing into the payload - otherwise we should have to copy ++ the data filled out by the helper over the old one */ ++ DEBUGP("expect_related: resent packet\n"); ++ if (related_to->helper->timeout) { ++ if (!del_timer(&old->timeout)) { ++ /* expectation is dying. Fall through */ ++ old = NULL; ++ } else { ++ old->timeout.expires = jiffies + ++ related_to->helper->timeout * HZ; ++ add_timer(&old->timeout); ++ } ++ } ++ ++ if (old) { ++ WRITE_UNLOCK(&ip_conntrack_lock); ++ return -EEXIST; ++ } ++ } else if (related_to->helper->max_expected && ++ related_to->expecting >= related_to->helper->max_expected) { ++ /* old == NULL */ ++ if (!(related_to->helper->flags & ++ IP_CT_HELPER_F_REUSE_EXPECT)) { ++ WRITE_UNLOCK(&ip_conntrack_lock); ++ if (net_ratelimit()) ++ printk(KERN_WARNING ++ "ip_conntrack: max number of expected " ++ "connections %i of %s reached for " ++ "%u.%u.%u.%u->%u.%u.%u.%u\n", ++ related_to->helper->max_expected, ++ related_to->helper->name, ++ NIPQUAD(related_to->tuplehash[IP_CT_DIR_ORIGINAL].tuple.src.ip), ++ NIPQUAD(related_to->tuplehash[IP_CT_DIR_ORIGINAL].tuple.dst.ip)); ++ return -EPERM; ++ } ++ DEBUGP("ip_conntrack: max number of expected " ++ "connections %i of %s reached for " ++ "%u.%u.%u.%u->%u.%u.%u.%u, reusing\n", ++ related_to->helper->max_expected, ++ related_to->helper->name, ++ NIPQUAD(related_to->tuplehash[IP_CT_DIR_ORIGINAL].tuple.src.ip), ++ NIPQUAD(related_to->tuplehash[IP_CT_DIR_ORIGINAL].tuple.dst.ip)); ++ ++ /* choose the the oldest expectation to evict */ ++ list_for_each_entry(old, &related_to->sibling_list, ++ expected_list) ++ if (old->sibling == NULL) ++ break; ++ ++ /* We cannot fail since related_to->expecting is the number ++ * of unconfirmed expectations */ ++ IP_NF_ASSERT(old && old->sibling == NULL); ++ ++ /* newnat14 does not reuse the real allocated memory ++ * structures but rather unexpects the old and ++ * allocates a new. unexpect_related will decrement ++ * related_to->expecting. ++ */ ++ unexpect_related(old); ++ ret = -EPERM; ++ } else if (LIST_FIND(&ip_conntrack_expect_list, expect_clash, ++ struct ip_conntrack_expect *, &expect->tuple, ++ &expect->mask)) { ++ WRITE_UNLOCK(&ip_conntrack_lock); ++ DEBUGP("expect_related: busy!\n"); ++ return -EBUSY; ++ } ++ ++ new = (struct ip_conntrack_expect *) ++ kmalloc(sizeof(struct ip_conntrack_expect), GFP_ATOMIC); ++ if (!new) { ++ WRITE_UNLOCK(&ip_conntrack_lock); ++ DEBUGP("expect_relaed: OOM allocating expect\n"); ++ return -ENOMEM; ++ } ++ ++ DEBUGP("new expectation %p of conntrack %p\n", new, related_to); ++ memcpy(new, expect, sizeof(*expect)); ++ new->expectant = related_to; ++ new->sibling = NULL; ++ atomic_set(&new->use, 1); ++ ++ /* add to expected list for this connection */ ++ list_add_tail(&new->expected_list, &related_to->sibling_list); ++ /* add to global list of expectations */ ++ list_prepend(&ip_conntrack_expect_list, &new->list); ++ /* add and start timer if required */ ++ if (related_to->helper->timeout) { ++ init_timer(&new->timeout); ++ new->timeout.data = (unsigned long)new; ++ new->timeout.function = expectation_timed_out; ++ new->timeout.expires = jiffies + ++ related_to->helper->timeout * HZ; ++ add_timer(&new->timeout); ++ } ++ related_to->expecting++; ++ ++ WRITE_UNLOCK(&ip_conntrack_lock); ++ ++ return ret; ++} ++ ++/* Change tuple in an existing expectation */ ++int ip_conntrack_change_expect(struct ip_conntrack_expect *expect, ++ struct ip_conntrack_tuple *newtuple) ++{ ++ int ret; ++ ++ MUST_BE_READ_LOCKED(&ip_conntrack_lock); ++ WRITE_LOCK(&ip_conntrack_expect_tuple_lock); ++ ++ DEBUGP("change_expect:\n"); ++ DEBUGP("exp tuple: "); DUMP_TUPLE(&expect->tuple); ++ DEBUGP("exp mask: "); DUMP_TUPLE(&expect->mask); ++ DEBUGP("newtuple: "); DUMP_TUPLE(newtuple); ++ if (expect->ct_tuple.dst.protonum == 0) { ++ /* Never seen before */ ++ DEBUGP("change expect: never seen before\n"); ++ if (!ip_ct_tuple_equal(&expect->tuple, newtuple) ++ && LIST_FIND(&ip_conntrack_expect_list, expect_clash, ++ struct ip_conntrack_expect *, newtuple, &expect->mask)) { ++ /* Force NAT to find an unused tuple */ ++ ret = -1; ++ } else { ++ memcpy(&expect->ct_tuple, &expect->tuple, sizeof(expect->tuple)); ++ memcpy(&expect->tuple, newtuple, sizeof(expect->tuple)); ++ ret = 0; ++ } ++ } else { ++ /* Resent packet */ ++ DEBUGP("change expect: resent packet\n"); ++ if (ip_ct_tuple_equal(&expect->tuple, newtuple)) { ++ ret = 0; ++ } else { ++ /* Force NAT to choose again the same port */ ++ ret = -1; ++ } ++ } ++ WRITE_UNLOCK(&ip_conntrack_expect_tuple_lock); ++ ++ return ret; ++} ++ ++/* Alter reply tuple (maybe alter helper). If it's already taken, ++ return 0 and don't do alteration. */ ++int ip_conntrack_alter_reply(struct ip_conntrack *conntrack, ++ const struct ip_conntrack_tuple *newreply) ++{ ++ WRITE_LOCK(&ip_conntrack_lock); ++ if (__ip_conntrack_find(newreply, conntrack)) { ++ WRITE_UNLOCK(&ip_conntrack_lock); ++ return 0; ++ } ++ /* Should be unconfirmed, so not in hash table yet */ ++ IP_NF_ASSERT(!is_confirmed(conntrack)); ++ ++ DEBUGP("Altering reply tuple of %p to ", conntrack); ++ DUMP_TUPLE(newreply); ++ ++ conntrack->tuplehash[IP_CT_DIR_REPLY].tuple = *newreply; ++ if (!conntrack->master && list_empty(&conntrack->sibling_list)) ++ conntrack->helper = ip_ct_find_helper(newreply); ++ WRITE_UNLOCK(&ip_conntrack_lock); ++ ++ return 1; ++} ++ ++int ip_conntrack_helper_register(struct ip_conntrack_helper *me) ++{ ++ MOD_INC_USE_COUNT; ++ ++ WRITE_LOCK(&ip_conntrack_lock); ++ list_prepend(&helpers, me); ++ WRITE_UNLOCK(&ip_conntrack_lock); ++ ++ return 0; ++} ++ ++static inline int unhelp(struct ip_conntrack_tuple_hash *i, ++ const struct ip_conntrack_helper *me) ++{ ++ if (i->ctrack->helper == me) { ++ /* Get rid of any expected. */ ++ remove_expectations(i->ctrack, 0); ++ /* And *then* set helper to NULL */ ++ i->ctrack->helper = NULL; ++ } ++ return 0; ++} ++ ++void ip_conntrack_helper_unregister(struct ip_conntrack_helper *me) ++{ ++ unsigned int i; ++ ++ /* Need write lock here, to delete helper. */ ++ WRITE_LOCK(&ip_conntrack_lock); ++ LIST_DELETE(&helpers, me); ++ ++ /* Get rid of expecteds, set helpers to NULL. */ ++ for (i = 0; i < ip_conntrack_htable_size; i++) ++ LIST_FIND_W(&ip_conntrack_hash[i], unhelp, ++ struct ip_conntrack_tuple_hash *, me); ++ WRITE_UNLOCK(&ip_conntrack_lock); ++ ++ /* Someone could be still looking at the helper in a bh. */ ++ br_write_lock_bh(BR_NETPROTO_LOCK); ++ br_write_unlock_bh(BR_NETPROTO_LOCK); ++ ++ MOD_DEC_USE_COUNT; ++} ++ ++/* Refresh conntrack for this many jiffies. */ ++void ip_ct_refresh(struct ip_conntrack *ct, unsigned long extra_jiffies) ++{ ++ IP_NF_ASSERT(ct->timeout.data == (unsigned long)ct); ++ ++ WRITE_LOCK(&ip_conntrack_lock); ++ /* If not in hash table, timer will not be active yet */ ++ if (!is_confirmed(ct)) ++ ct->timeout.expires = extra_jiffies; ++ else { ++ /* Need del_timer for race avoidance (may already be dying). */ ++ if (del_timer(&ct->timeout)) { ++ ct->timeout.expires = jiffies + extra_jiffies; ++ add_timer(&ct->timeout); ++ } ++ } ++ WRITE_UNLOCK(&ip_conntrack_lock); ++} ++ ++/* Returns new sk_buff, or NULL */ ++struct sk_buff * ++ip_ct_gather_frags(struct sk_buff *skb) ++{ ++ struct sock *sk = skb->sk; ++#ifdef CONFIG_NETFILTER_DEBUG ++ unsigned int olddebug = skb->nf_debug; ++#endif ++ if (sk) { ++ sock_hold(sk); ++ skb_orphan(skb); ++ } ++ ++ local_bh_disable(); ++ skb = ip_defrag(skb); ++ local_bh_enable(); ++ ++ if (!skb) { ++ if (sk) sock_put(sk); ++ return skb; ++ } else if (skb_is_nonlinear(skb) && skb_linearize(skb, GFP_ATOMIC) != 0) { ++ kfree_skb(skb); ++ if (sk) sock_put(sk); ++ return NULL; ++ } ++ ++ if (sk) { ++ skb_set_owner_w(skb, sk); ++ sock_put(sk); ++ } ++ ++ ip_send_check(skb->nh.iph); ++ skb->nfcache |= NFC_ALTERED; ++#ifdef CONFIG_NETFILTER_DEBUG ++ /* Packet path as if nothing had happened. */ ++ skb->nf_debug = olddebug; ++#endif ++ return skb; ++} ++ ++/* Used by ipt_REJECT. */ ++static void ip_conntrack_attach(struct sk_buff *nskb, struct nf_ct_info *nfct) ++{ ++ struct ip_conntrack *ct; ++ enum ip_conntrack_info ctinfo; ++ ++ ct = __ip_conntrack_get(nfct, &ctinfo); ++ ++ /* This ICMP is in reverse direction to the packet which ++ caused it */ ++ if (CTINFO2DIR(ctinfo) == IP_CT_DIR_ORIGINAL) ++ ctinfo = IP_CT_RELATED + IP_CT_IS_REPLY; ++ else ++ ctinfo = IP_CT_RELATED; ++ ++ /* Attach new skbuff, and increment count */ ++ nskb->nfct = &ct->infos[ctinfo]; ++ atomic_inc(&ct->ct_general.use); ++} ++ ++static inline int ++do_kill(const struct ip_conntrack_tuple_hash *i, ++ int (*kill)(const struct ip_conntrack *i, void *data), ++ void *data) ++{ ++ return kill(i->ctrack, data); ++} ++ ++/* Bring out ya dead! */ ++static struct ip_conntrack_tuple_hash * ++get_next_corpse(int (*kill)(const struct ip_conntrack *i, void *data), ++ void *data, unsigned int *bucket) ++{ ++ struct ip_conntrack_tuple_hash *h = NULL; ++ ++ READ_LOCK(&ip_conntrack_lock); ++ for (; !h && *bucket < ip_conntrack_htable_size; (*bucket)++) { ++ h = LIST_FIND(&ip_conntrack_hash[*bucket], do_kill, ++ struct ip_conntrack_tuple_hash *, kill, data); ++ } ++ if (h) ++ atomic_inc(&h->ctrack->ct_general.use); ++ READ_UNLOCK(&ip_conntrack_lock); ++ ++ return h; ++} ++ ++void ++ip_ct_selective_cleanup(int (*kill)(const struct ip_conntrack *i, void *data), ++ void *data) ++{ ++ struct ip_conntrack_tuple_hash *h; ++ unsigned int bucket = 0; ++ ++ while ((h = get_next_corpse(kill, data, &bucket)) != NULL) { ++ /* Time to push up daises... */ ++ if (del_timer(&h->ctrack->timeout)) ++ death_by_timeout((unsigned long)h->ctrack); ++ /* ... else the timer will get him soon. */ ++ ++ ip_conntrack_put(h->ctrack); ++ } ++} ++ ++/* Fast function for those who don't want to parse /proc (and I don't ++ blame them). */ ++/* Reversing the socket's dst/src point of view gives us the reply ++ mapping. */ ++static int ++getorigdst(struct sock *sk, int optval, void *user, int *len) ++{ ++ struct ip_conntrack_tuple_hash *h; ++ struct ip_conntrack_tuple tuple; ++ ++ IP_CT_TUPLE_U_BLANK(&tuple); ++ tuple.src.ip = sk->rcv_saddr; ++ tuple.src.u.tcp.port = sk->sport; ++ tuple.dst.ip = sk->daddr; ++ tuple.dst.u.tcp.port = sk->dport; ++ tuple.dst.protonum = IPPROTO_TCP; ++ ++ /* We only do TCP at the moment: is there a better way? */ ++ if (strcmp(sk->prot->name, "TCP") != 0) { ++ DEBUGP("SO_ORIGINAL_DST: Not a TCP socket\n"); ++ return -ENOPROTOOPT; ++ } ++ ++ if ((unsigned int) *len < sizeof(struct sockaddr_in)) { ++ DEBUGP("SO_ORIGINAL_DST: len %u not %u\n", ++ *len, sizeof(struct sockaddr_in)); ++ return -EINVAL; ++ } ++ ++ h = ip_conntrack_find_get(&tuple, NULL); ++ if (h) { ++ struct sockaddr_in sin; ++ ++ sin.sin_family = AF_INET; ++ sin.sin_port = h->ctrack->tuplehash[IP_CT_DIR_ORIGINAL] ++ .tuple.dst.u.tcp.port; ++ sin.sin_addr.s_addr = h->ctrack->tuplehash[IP_CT_DIR_ORIGINAL] ++ .tuple.dst.ip; ++ ++ DEBUGP("SO_ORIGINAL_DST: %u.%u.%u.%u %u\n", ++ NIPQUAD(sin.sin_addr.s_addr), ntohs(sin.sin_port)); ++ ip_conntrack_put(h->ctrack); ++ if (copy_to_user(user, &sin, sizeof(sin)) != 0) ++ return -EFAULT; ++ else ++ return 0; ++ } ++ DEBUGP("SO_ORIGINAL_DST: Can't find %u.%u.%u.%u/%u-%u.%u.%u.%u/%u.\n", ++ NIPQUAD(tuple.src.ip), ntohs(tuple.src.u.tcp.port), ++ NIPQUAD(tuple.dst.ip), ntohs(tuple.dst.u.tcp.port)); ++ return -ENOENT; ++} ++ ++static struct nf_sockopt_ops so_getorigdst ++= { { NULL, NULL }, PF_INET, ++ 0, 0, NULL, /* Setsockopts */ ++ SO_ORIGINAL_DST, SO_ORIGINAL_DST+1, &getorigdst, ++ 0, NULL }; ++ ++static int kill_all(const struct ip_conntrack *i, void *data) ++{ ++ return 1; ++} ++ ++/* Mishearing the voices in his head, our hero wonders how he's ++ supposed to kill the mall. */ ++void ip_conntrack_cleanup(void) ++{ ++ ip_ct_attach = NULL; ++ /* This makes sure all current packets have passed through ++ netfilter framework. Roll on, two-stage module ++ delete... */ ++ br_write_lock_bh(BR_NETPROTO_LOCK); ++ br_write_unlock_bh(BR_NETPROTO_LOCK); ++ ++ i_see_dead_people: ++ ip_ct_selective_cleanup(kill_all, NULL); ++ if (atomic_read(&ip_conntrack_count) != 0) { ++ schedule(); ++ goto i_see_dead_people; ++ } ++ ++ kmem_cache_destroy(ip_conntrack_cachep); ++ vfree(ip_conntrack_hash); ++ nf_unregister_sockopt(&so_getorigdst); ++} ++ ++static int hashsize = 0; ++MODULE_PARM(hashsize, "i"); ++ ++int __init ip_conntrack_init(void) ++{ ++ unsigned int i; ++ int ret; ++ ++ /* Idea from tcp.c: use 1/16384 of memory. On i386: 32MB ++ * machine has 256 buckets. >= 1GB machines have 8192 buckets. */ ++ if (hashsize) { ++ ip_conntrack_htable_size = hashsize; ++ } else { ++ ip_conntrack_htable_size ++ = (((num_physpages << PAGE_SHIFT) / 16384) ++ / sizeof(struct list_head)); ++ if (num_physpages > (1024 * 1024 * 1024 / PAGE_SIZE)) ++ ip_conntrack_htable_size = 8192; ++ if (ip_conntrack_htable_size < 16) ++ ip_conntrack_htable_size = 16; ++ } ++ ip_conntrack_max = 8 * ip_conntrack_htable_size; ++ ++ printk("ip_conntrack version %s (%u buckets, %d max)" ++ " - %Zd bytes per conntrack\n", IP_CONNTRACK_VERSION, ++ ip_conntrack_htable_size, ip_conntrack_max, ++ sizeof(struct ip_conntrack)); ++ ++ ret = nf_register_sockopt(&so_getorigdst); ++ if (ret != 0) { ++ printk(KERN_ERR "Unable to register netfilter socket option\n"); ++ return ret; ++ } ++ ++ ip_conntrack_hash = vmalloc(sizeof(struct list_head) ++ * ip_conntrack_htable_size); ++ if (!ip_conntrack_hash) { ++ printk(KERN_ERR "Unable to create ip_conntrack_hash\n"); ++ goto err_unreg_sockopt; ++ } ++ ++ ip_conntrack_cachep = kmem_cache_create("ip_conntrack", ++ sizeof(struct ip_conntrack), 0, ++ SLAB_HWCACHE_ALIGN, NULL, NULL); ++ if (!ip_conntrack_cachep) { ++ printk(KERN_ERR "Unable to create ip_conntrack slab cache\n"); ++ goto err_free_hash; ++ } ++ /* Don't NEED lock here, but good form anyway. */ ++ WRITE_LOCK(&ip_conntrack_lock); ++ /* Sew in builtin protocols. */ ++ list_append(&protocol_list, &ip_conntrack_protocol_tcp); ++ list_append(&protocol_list, &ip_conntrack_protocol_udp); ++ list_append(&protocol_list, &ip_conntrack_protocol_icmp); ++ WRITE_UNLOCK(&ip_conntrack_lock); ++ ++ for (i = 0; i < ip_conntrack_htable_size; i++) ++ INIT_LIST_HEAD(&ip_conntrack_hash[i]); ++ ++ /* For use by ipt_REJECT */ ++ ip_ct_attach = ip_conntrack_attach; ++ return ret; ++ ++err_free_hash: ++ vfree(ip_conntrack_hash); ++err_unreg_sockopt: ++ nf_unregister_sockopt(&so_getorigdst); ++ ++ return -ENOMEM; ++} +diff -uNr linux_org/net/ipv4/netfilter/ip_conntrack_pptp.c linux/net/ipv4/netfilter/ip_conntrack_pptp.c +--- linux_org/net/ipv4/netfilter/ip_conntrack_pptp.c 1970-01-01 01:00:00.000000000 +0100 ++++ linux/net/ipv4/netfilter/ip_conntrack_pptp.c 2006-10-27 14:11:52.000000000 +0200 +@@ -0,0 +1,637 @@ ++/* ++ * ip_conntrack_pptp.c - Version 1.9 ++ * ++ * Connection tracking support for PPTP (Point to Point Tunneling Protocol). ++ * PPTP is a a protocol for creating virtual private networks. ++ * It is a specification defined by Microsoft and some vendors ++ * working with Microsoft. PPTP is built on top of a modified ++ * version of the Internet Generic Routing Encapsulation Protocol. ++ * GRE is defined in RFC 1701 and RFC 1702. Documentation of ++ * PPTP can be found in RFC 2637 ++ * ++ * (C) 2000-2003 by Harald Welte <laforge@gnumonks.org> ++ * ++ * Development of this code funded by Astaro AG (http://www.astaro.com/) ++ * ++ * Limitations: ++ * - We blindly assume that control connections are always ++ * established in PNS->PAC direction. This is a violation ++ * of RFFC2673 ++ * ++ * TODO: - finish support for multiple calls within one session ++ * (needs expect reservations in newnat) ++ * - testing of incoming PPTP calls ++ * ++ * Changes: ++ * 2002-02-05 - Version 1.3 ++ * - Call ip_conntrack_unexpect_related() from ++ * pptp_timeout_related() to destroy expectations in case ++ * CALL_DISCONNECT_NOTIFY or tcp fin packet was seen ++ * (Philip Craig <philipc@snapgear.com>) ++ * - Add Version information at module loadtime ++ * 2002-02-10 - Version 1.6 ++ * - move to C99 style initializers ++ * - remove second expectation if first arrives ++ * ++ */ ++ ++#include <linux/config.h> ++#include <linux/module.h> ++#include <linux/netfilter.h> ++#include <linux/ip.h> ++#include <net/checksum.h> ++#include <net/tcp.h> ++ ++#include <linux/netfilter_ipv4/lockhelp.h> ++#include <linux/netfilter_ipv4/ip_conntrack_helper.h> ++#include <linux/netfilter_ipv4/ip_conntrack_proto_gre.h> ++#include <linux/netfilter_ipv4/ip_conntrack_pptp.h> ++ ++#define IP_CT_PPTP_VERSION "1.9" ++ ++MODULE_LICENSE("GPL"); ++MODULE_AUTHOR("Harald Welte <laforge@gnumonks.org>"); ++MODULE_DESCRIPTION("Netfilter connection tracking helper module for PPTP"); ++ ++DECLARE_LOCK(ip_pptp_lock); ++ ++#if 0 ++#include "ip_conntrack_pptp_priv.h" ++#define DEBUGP(format, args...) printk(KERN_DEBUG __FILE__ ":" __FUNCTION__ \ ++ ": " format, ## args) ++#else ++#define DEBUGP(format, args...) ++#endif ++ ++#define SECS *HZ ++#define MINS * 60 SECS ++#define HOURS * 60 MINS ++#define DAYS * 24 HOURS ++ ++#define PPTP_GRE_TIMEOUT (10 MINS) ++#define PPTP_GRE_STREAM_TIMEOUT (5 DAYS) ++ ++static int pptp_expectfn(struct ip_conntrack *ct) ++{ ++ struct ip_conntrack *master; ++ struct ip_conntrack_expect *exp; ++ ++ DEBUGP("increasing timeouts\n"); ++ /* increase timeout of GRE data channel conntrack entry */ ++ ct->proto.gre.timeout = PPTP_GRE_TIMEOUT; ++ ct->proto.gre.stream_timeout = PPTP_GRE_STREAM_TIMEOUT; ++ ++ master = master_ct(ct); ++ if (!master) { ++ DEBUGP(" no master!!!\n"); ++ return 0; ++ } ++ ++ exp = ct->master; ++ if (!exp) { ++ DEBUGP("no expectation!!\n"); ++ return 0; ++ } ++ ++ DEBUGP("completing tuples with ct info\n"); ++ /* we can do this, since we're unconfirmed */ ++ if (ct->tuplehash[IP_CT_DIR_ORIGINAL].tuple.dst.u.gre.key == ++ htonl(master->help.ct_pptp_info.pac_call_id)) { ++ /* assume PNS->PAC */ ++ ct->tuplehash[IP_CT_DIR_ORIGINAL].tuple.src.u.gre.key = ++ htonl(master->help.ct_pptp_info.pns_call_id); ++ ct->tuplehash[IP_CT_DIR_REPLY].tuple.dst.u.gre.key = ++ htonl(master->help.ct_pptp_info.pns_call_id); ++ } else { ++ /* assume PAC->PNS */ ++ ct->tuplehash[IP_CT_DIR_ORIGINAL].tuple.src.u.gre.key = ++ htonl(master->help.ct_pptp_info.pac_call_id); ++ ct->tuplehash[IP_CT_DIR_REPLY].tuple.dst.u.gre.key = ++ htonl(master->help.ct_pptp_info.pac_call_id); ++ } ++ ++ /* delete other expectation */ ++ if (exp->expected_list.next != &exp->expected_list) { ++ struct ip_conntrack_expect *other_exp; ++ struct list_head *cur_item, *next; ++ ++ for (cur_item = master->sibling_list.next; ++ cur_item != &master->sibling_list; cur_item = next) { ++ next = cur_item->next; ++ other_exp = list_entry(cur_item, ++ struct ip_conntrack_expect, ++ expected_list); ++ /* remove only if occurred at same sequence number */ ++ if (other_exp != exp && other_exp->seq == exp->seq) { ++ DEBUGP("unexpecting other direction\n"); ++ ip_ct_gre_keymap_destroy(other_exp); ++ ip_conntrack_unexpect_related(other_exp); ++ } ++ } ++ } ++ ++ return 0; ++} ++ ++/* timeout GRE data connections */ ++static int pptp_timeout_related(struct ip_conntrack *ct) ++{ ++ struct list_head *cur_item, *next; ++ struct ip_conntrack_expect *exp; ++ ++ /* FIXME: do we have to lock something ? */ ++ for (cur_item = ct->sibling_list.next; ++ cur_item != &ct->sibling_list; cur_item = next) { ++ next = cur_item->next; ++ exp = list_entry(cur_item, struct ip_conntrack_expect, ++ expected_list); ++ ++ ip_ct_gre_keymap_destroy(exp); ++ if (!exp->sibling) { ++ ip_conntrack_unexpect_related(exp); ++ continue; ++ } ++ ++ DEBUGP("setting timeout of conntrack %p to 0\n", ++ exp->sibling); ++ exp->sibling->proto.gre.timeout = 0; ++ exp->sibling->proto.gre.stream_timeout = 0; ++ ip_ct_refresh(exp->sibling, 0); ++ } ++ ++ return 0; ++} ++ ++/* expect GRE connections (PNS->PAC and PAC->PNS direction) */ ++static inline int ++exp_gre(struct ip_conntrack *master, ++ u_int32_t seq, ++ u_int16_t callid, ++ u_int16_t peer_callid) ++{ ++ struct ip_conntrack_expect exp; ++ struct ip_conntrack_tuple inv_tuple; ++ ++ memset(&exp, 0, sizeof(exp)); ++ /* tuple in original direction, PNS->PAC */ ++ exp.tuple.src.ip = master->tuplehash[IP_CT_DIR_ORIGINAL].tuple.src.ip; ++ exp.tuple.src.u.gre.key = htonl(ntohs(peer_callid)); ++ exp.tuple.dst.ip = master->tuplehash[IP_CT_DIR_ORIGINAL].tuple.dst.ip; ++ exp.tuple.dst.u.gre.key = htonl(ntohs(callid)); ++ exp.tuple.dst.u.gre.protocol = __constant_htons(GRE_PROTOCOL_PPTP); ++ exp.tuple.dst.u.gre.version = GRE_VERSION_PPTP; ++ exp.tuple.dst.protonum = IPPROTO_GRE; ++ ++ exp.mask.src.ip = 0xffffffff; ++ exp.mask.src.u.all = 0; ++ exp.mask.dst.u.all = 0; ++ exp.mask.dst.u.gre.key = 0xffffffff; ++ exp.mask.dst.u.gre.version = 0xff; ++ exp.mask.dst.u.gre.protocol = 0xffff; ++ exp.mask.dst.ip = 0xffffffff; ++ exp.mask.dst.protonum = 0xffff; ++ ++ exp.seq = seq; ++ exp.expectfn = pptp_expectfn; ++ ++ exp.help.exp_pptp_info.pac_call_id = ntohs(callid); ++ exp.help.exp_pptp_info.pns_call_id = ntohs(peer_callid); ++ ++ DEBUGP("calling expect_related "); ++ DUMP_TUPLE_RAW(&exp.tuple); ++ ++ /* Add GRE keymap entries */ ++ if (ip_ct_gre_keymap_add(&exp, &exp.tuple, 0) != 0) ++ return 1; ++ ++ invert_tuplepr(&inv_tuple, &exp.tuple); ++ if (ip_ct_gre_keymap_add(&exp, &inv_tuple, 1) != 0) { ++ ip_ct_gre_keymap_destroy(&exp); ++ return 1; ++ } ++ ++ if (ip_conntrack_expect_related(master, &exp) != 0) { ++ ip_ct_gre_keymap_destroy(&exp); ++ DEBUGP("cannot expect_related()\n"); ++ return 1; ++ } ++ ++ /* tuple in reply direction, PAC->PNS */ ++ exp.tuple.src.ip = master->tuplehash[IP_CT_DIR_REPLY].tuple.src.ip; ++ exp.tuple.src.u.gre.key = htonl(ntohs(callid)); ++ exp.tuple.dst.ip = master->tuplehash[IP_CT_DIR_REPLY].tuple.dst.ip; ++ exp.tuple.dst.u.gre.key = htonl(ntohs(peer_callid)); ++ ++ DEBUGP("calling expect_related "); ++ DUMP_TUPLE_RAW(&exp.tuple); ++ ++ /* Add GRE keymap entries */ ++ ip_ct_gre_keymap_add(&exp, &exp.tuple, 0); ++ invert_tuplepr(&inv_tuple, &exp.tuple); ++ ip_ct_gre_keymap_add(&exp, &inv_tuple, 1); ++ /* FIXME: cannot handle error correctly, since we need to free ++ * the above keymap :( */ ++ ++ if (ip_conntrack_expect_related(master, &exp) != 0) { ++ /* free the second pair of keypmaps */ ++ ip_ct_gre_keymap_destroy(&exp); ++ DEBUGP("cannot expect_related():\n"); ++ return 1; ++ } ++ ++ return 0; ++} ++ ++static inline int ++pptp_inbound_pkt(struct tcphdr *tcph, ++ struct pptp_pkt_hdr *pptph, ++ size_t datalen, ++ struct ip_conntrack *ct, ++ enum ip_conntrack_info ctinfo) ++{ ++ struct PptpControlHeader *ctlh; ++ union pptp_ctrl_union pptpReq; ++ ++ struct ip_ct_pptp_master *info = &ct->help.ct_pptp_info; ++ u_int16_t msg, *cid, *pcid; ++ u_int32_t seq; ++ ++ ctlh = (struct PptpControlHeader *) ++ ((char *) pptph + sizeof(struct pptp_pkt_hdr)); ++ pptpReq.rawreq = (void *) ++ ((char *) ctlh + sizeof(struct PptpControlHeader)); ++ ++ msg = ntohs(ctlh->messageType); ++ DEBUGP("inbound control message %s\n", strMName[msg]); ++ ++ switch (msg) { ++ case PPTP_START_SESSION_REPLY: ++ /* server confirms new control session */ ++ if (info->sstate < PPTP_SESSION_REQUESTED) { ++ DEBUGP("%s without START_SESS_REQUEST\n", ++ strMName[msg]); ++ break; ++ } ++ if (pptpReq.srep->resultCode == PPTP_START_OK) ++ info->sstate = PPTP_SESSION_CONFIRMED; ++ else ++ info->sstate = PPTP_SESSION_ERROR; ++ break; ++ ++ case PPTP_STOP_SESSION_REPLY: ++ /* server confirms end of control session */ ++ if (info->sstate > PPTP_SESSION_STOPREQ) { ++ DEBUGP("%s without STOP_SESS_REQUEST\n", ++ strMName[msg]); ++ break; ++ } ++ if (pptpReq.strep->resultCode == PPTP_STOP_OK) ++ info->sstate = PPTP_SESSION_NONE; ++ else ++ info->sstate = PPTP_SESSION_ERROR; ++ break; ++ ++ case PPTP_OUT_CALL_REPLY: ++ /* server accepted call, we now expect GRE frames */ ++ if (info->sstate != PPTP_SESSION_CONFIRMED) { ++ DEBUGP("%s but no session\n", strMName[msg]); ++ break; ++ } ++ if (info->cstate != PPTP_CALL_OUT_REQ && ++ info->cstate != PPTP_CALL_OUT_CONF) { ++ DEBUGP("%s without OUTCALL_REQ\n", strMName[msg]); ++ break; ++ } ++ if (pptpReq.ocack->resultCode != PPTP_OUTCALL_CONNECT) { ++ info->cstate = PPTP_CALL_NONE; ++ break; ++ } ++ ++ cid = &pptpReq.ocack->callID; ++ pcid = &pptpReq.ocack->peersCallID; ++ ++ info->pac_call_id = ntohs(*cid); ++ ++ if (htons(info->pns_call_id) != *pcid) { ++ DEBUGP("%s for unknown callid %u\n", ++ strMName[msg], ntohs(*pcid)); ++ break; ++ } ++ ++ DEBUGP("%s, CID=%X, PCID=%X\n", strMName[msg], ++ ntohs(*cid), ntohs(*pcid)); ++ ++ info->cstate = PPTP_CALL_OUT_CONF; ++ ++ seq = ntohl(tcph->seq) + ((void *)pcid - (void *)pptph); ++ if (exp_gre(ct, seq, *cid, *pcid) != 0) ++ printk("ip_conntrack_pptp: error during exp_gre\n"); ++ break; ++ ++ case PPTP_IN_CALL_REQUEST: ++ /* server tells us about incoming call request */ ++ if (info->sstate != PPTP_SESSION_CONFIRMED) { ++ DEBUGP("%s but no session\n", strMName[msg]); ++ break; ++ } ++ pcid = &pptpReq.icack->peersCallID; ++ DEBUGP("%s, PCID=%X\n", strMName[msg], ntohs(*pcid)); ++ info->cstate = PPTP_CALL_IN_REQ; ++ info->pac_call_id= ntohs(*pcid); ++ break; ++ ++ case PPTP_IN_CALL_CONNECT: ++ /* server tells us about incoming call established */ ++ if (info->sstate != PPTP_SESSION_CONFIRMED) { ++ DEBUGP("%s but no session\n", strMName[msg]); ++ break; ++ } ++ if (info->sstate != PPTP_CALL_IN_REP ++ && info->sstate != PPTP_CALL_IN_CONF) { ++ DEBUGP("%s but never sent IN_CALL_REPLY\n", ++ strMName[msg]); ++ break; ++ } ++ ++ pcid = &pptpReq.iccon->peersCallID; ++ cid = &info->pac_call_id; ++ ++ if (info->pns_call_id != ntohs(*pcid)) { ++ DEBUGP("%s for unknown CallID %u\n", ++ strMName[msg], ntohs(*cid)); ++ break; ++ } ++ ++ DEBUGP("%s, PCID=%X\n", strMName[msg], ntohs(*pcid)); ++ info->cstate = PPTP_CALL_IN_CONF; ++ ++ /* we expect a GRE connection from PAC to PNS */ ++ seq = ntohl(tcph->seq) + ((void *)pcid - (void *)pptph); ++ if (exp_gre(ct, seq, *cid, *pcid) != 0) ++ printk("ip_conntrack_pptp: error during exp_gre\n"); ++ ++ break; ++ ++ case PPTP_CALL_DISCONNECT_NOTIFY: ++ /* server confirms disconnect */ ++ cid = &pptpReq.disc->callID; ++ DEBUGP("%s, CID=%X\n", strMName[msg], ntohs(*cid)); ++ info->cstate = PPTP_CALL_NONE; ++ ++ /* untrack this call id, unexpect GRE packets */ ++ pptp_timeout_related(ct); ++ break; ++ ++ case PPTP_WAN_ERROR_NOTIFY: ++ break; ++ ++ case PPTP_ECHO_REQUEST: ++ case PPTP_ECHO_REPLY: ++ /* I don't have to explain these ;) */ ++ break; ++ default: ++ DEBUGP("invalid %s (TY=%d)\n", (msg <= PPTP_MSG_MAX) ++ ? strMName[msg]:strMName[0], msg); ++ break; ++ } ++ ++ return NF_ACCEPT; ++ ++} ++ ++static inline int ++pptp_outbound_pkt(struct tcphdr *tcph, ++ struct pptp_pkt_hdr *pptph, ++ size_t datalen, ++ struct ip_conntrack *ct, ++ enum ip_conntrack_info ctinfo) ++{ ++ struct PptpControlHeader *ctlh; ++ union pptp_ctrl_union pptpReq; ++ struct ip_ct_pptp_master *info = &ct->help.ct_pptp_info; ++ u_int16_t msg, *cid, *pcid; ++ ++ ctlh = (struct PptpControlHeader *) ((void *) pptph + sizeof(*pptph)); ++ pptpReq.rawreq = (void *) ((void *) ctlh + sizeof(*ctlh)); ++ ++ msg = ntohs(ctlh->messageType); ++ DEBUGP("outbound control message %s\n", strMName[msg]); ++ ++ switch (msg) { ++ case PPTP_START_SESSION_REQUEST: ++ /* client requests for new control session */ ++ if (info->sstate != PPTP_SESSION_NONE) { ++ DEBUGP("%s but we already have one", ++ strMName[msg]); ++ } ++ info->sstate = PPTP_SESSION_REQUESTED; ++ break; ++ case PPTP_STOP_SESSION_REQUEST: ++ /* client requests end of control session */ ++ info->sstate = PPTP_SESSION_STOPREQ; ++ break; ++ ++ case PPTP_OUT_CALL_REQUEST: ++ /* client initiating connection to server */ ++ if (info->sstate != PPTP_SESSION_CONFIRMED) { ++ DEBUGP("%s but no session\n", ++ strMName[msg]); ++ break; ++ } ++ info->cstate = PPTP_CALL_OUT_REQ; ++ /* track PNS call id */ ++ cid = &pptpReq.ocreq->callID; ++ DEBUGP("%s, CID=%X\n", strMName[msg], ntohs(*cid)); ++ info->pns_call_id = ntohs(*cid); ++ break; ++ case PPTP_IN_CALL_REPLY: ++ /* client answers incoming call */ ++ if (info->cstate != PPTP_CALL_IN_REQ ++ && info->cstate != PPTP_CALL_IN_REP) { ++ DEBUGP("%s without incall_req\n", ++ strMName[msg]); ++ break; ++ } ++ if (pptpReq.icack->resultCode != PPTP_INCALL_ACCEPT) { ++ info->cstate = PPTP_CALL_NONE; ++ break; ++ } ++ pcid = &pptpReq.icack->peersCallID; ++ if (info->pac_call_id != ntohs(*pcid)) { ++ DEBUGP("%s for unknown call %u\n", ++ strMName[msg], ntohs(*pcid)); ++ break; ++ } ++ DEBUGP("%s, CID=%X\n", strMName[msg], ntohs(*pcid)); ++ /* part two of the three-way handshake */ ++ info->cstate = PPTP_CALL_IN_REP; ++ info->pns_call_id = ntohs(pptpReq.icack->callID); ++ break; ++ ++ case PPTP_CALL_CLEAR_REQUEST: ++ /* client requests hangup of call */ ++ if (info->sstate != PPTP_SESSION_CONFIRMED) { ++ DEBUGP("CLEAR_CALL but no session\n"); ++ break; ++ } ++ /* FUTURE: iterate over all calls and check if ++ * call ID is valid. We don't do this without newnat, ++ * because we only know about last call */ ++ info->cstate = PPTP_CALL_CLEAR_REQ; ++ break; ++ case PPTP_SET_LINK_INFO: ++ break; ++ case PPTP_ECHO_REQUEST: ++ case PPTP_ECHO_REPLY: ++ /* I don't have to explain these ;) */ ++ break; ++ default: ++ DEBUGP("invalid %s (TY=%d)\n", (msg <= PPTP_MSG_MAX)? ++ strMName[msg]:strMName[0], msg); ++ /* unknown: no need to create GRE masq table entry */ ++ break; ++ } ++ ++ return NF_ACCEPT; ++} ++ ++ ++/* track caller id inside control connection, call expect_related */ ++static int ++conntrack_pptp_help(const struct iphdr *iph, size_t len, ++ struct ip_conntrack *ct, enum ip_conntrack_info ctinfo) ++ ++{ ++ struct pptp_pkt_hdr *pptph; ++ ++ struct tcphdr *tcph = (void *) iph + iph->ihl * 4; ++ u_int32_t tcplen = len - iph->ihl * 4; ++ u_int32_t datalen = tcplen - tcph->doff * 4; ++ void *datalimit; ++ int dir = CTINFO2DIR(ctinfo); ++ struct ip_ct_pptp_master *info = &ct->help.ct_pptp_info; ++ ++ int oldsstate, oldcstate; ++ int ret; ++ ++ /* don't do any tracking before tcp handshake complete */ ++ if (ctinfo != IP_CT_ESTABLISHED ++ && ctinfo != IP_CT_ESTABLISHED+IP_CT_IS_REPLY) { ++ DEBUGP("ctinfo = %u, skipping\n", ctinfo); ++ return NF_ACCEPT; ++ } ++ ++ /* not a complete TCP header? */ ++ if (tcplen < sizeof(struct tcphdr) || tcplen < tcph->doff * 4) { ++ DEBUGP("tcplen = %u\n", tcplen); ++ return NF_ACCEPT; ++ } ++ ++ /* checksum invalid? */ ++ if (tcp_v4_check(tcph, tcplen, iph->saddr, iph->daddr, ++ csum_partial((char *) tcph, tcplen, 0))) { ++ printk(KERN_NOTICE __FILE__ ": bad csum\n"); ++ /* W2K PPTP server sends TCP packets with wrong checksum :(( */ ++ //return NF_ACCEPT; ++ } ++ ++ if (tcph->fin || tcph->rst) { ++ DEBUGP("RST/FIN received, timeouting GRE\n"); ++ /* can't do this after real newnat */ ++ info->cstate = PPTP_CALL_NONE; ++ ++ /* untrack this call id, unexpect GRE packets */ ++ pptp_timeout_related(ct); ++ } ++ ++ ++ pptph = (struct pptp_pkt_hdr *) ((void *) tcph + tcph->doff * 4); ++ datalimit = (void *) pptph + datalen; ++ ++ /* not a full pptp packet header? */ ++ if ((void *) pptph+sizeof(*pptph) >= datalimit) { ++ DEBUGP("no full PPTP header, can't track\n"); ++ return NF_ACCEPT; ++ } ++ ++ /* if it's not a control message we can't do anything with it */ ++ if (ntohs(pptph->packetType) != PPTP_PACKET_CONTROL || ++ ntohl(pptph->magicCookie) != PPTP_MAGIC_COOKIE) { ++ DEBUGP("not a control packet\n"); ++ return NF_ACCEPT; ++ } ++ ++ oldsstate = info->sstate; ++ oldcstate = info->cstate; ++ ++ LOCK_BH(&ip_pptp_lock); ++ ++ /* FIXME: We just blindly assume that the control connection is always ++ * established from PNS->PAC. However, RFC makes no guarantee */ ++ if (dir == IP_CT_DIR_ORIGINAL) ++ /* client -> server (PNS -> PAC) */ ++ ret = pptp_outbound_pkt(tcph, pptph, datalen, ct, ctinfo); ++ else ++ /* server -> client (PAC -> PNS) */ ++ ret = pptp_inbound_pkt(tcph, pptph, datalen, ct, ctinfo); ++ DEBUGP("sstate: %d->%d, cstate: %d->%d\n", ++ oldsstate, info->sstate, oldcstate, info->cstate); ++ UNLOCK_BH(&ip_pptp_lock); ++ ++ return ret; ++} ++ ++/* control protocol helper */ ++static struct ip_conntrack_helper pptp = { ++ .list = { NULL, NULL }, ++ .name = "pptp", ++ .flags = IP_CT_HELPER_F_REUSE_EXPECT, ++ .me = THIS_MODULE, ++ .max_expected = 2, ++ .timeout = 0, ++ .tuple = { .src = { .ip = 0, ++ .u = { .tcp = { .port = ++ __constant_htons(PPTP_CONTROL_PORT) } } ++ }, ++ .dst = { .ip = 0, ++ .u = { .all = 0 }, ++ .protonum = IPPROTO_TCP ++ } ++ }, ++ .mask = { .src = { .ip = 0, ++ .u = { .tcp = { .port = 0xffff } } ++ }, ++ .dst = { .ip = 0, ++ .u = { .all = 0 }, ++ .protonum = 0xffff ++ } ++ }, ++ .help = conntrack_pptp_help ++}; ++ ++/* ip_conntrack_pptp initialization */ ++static int __init init(void) ++{ ++ int retcode; ++ ++ DEBUGP(__FILE__ ": registering helper\n"); ++ if ((retcode = ip_conntrack_helper_register(&pptp))) { ++ printk(KERN_ERR "Unable to register conntrack application " ++ "helper for pptp: %d\n", retcode); ++ return -EIO; ++ } ++ ++ printk("ip_conntrack_pptp version %s loaded\n", IP_CT_PPTP_VERSION); ++ return 0; ++} ++ ++static void __exit fini(void) ++{ ++ ip_conntrack_helper_unregister(&pptp); ++ printk("ip_conntrack_pptp version %s unloaded\n", IP_CT_PPTP_VERSION); ++} ++ ++module_init(init); ++module_exit(fini); ++ ++EXPORT_SYMBOL(ip_pptp_lock); +diff -uNr linux_org/net/ipv4/netfilter/ip_conntrack_pptp_priv.h linux/net/ipv4/netfilter/ip_conntrack_pptp_priv.h +--- linux_org/net/ipv4/netfilter/ip_conntrack_pptp_priv.h 1970-01-01 01:00:00.000000000 +0100 ++++ linux/net/ipv4/netfilter/ip_conntrack_pptp_priv.h 2006-10-27 14:11:52.000000000 +0200 +@@ -0,0 +1,24 @@ ++#ifndef _IP_CT_PPTP_PRIV_H ++#define _IP_CT_PPTP_PRIV_H ++ ++/* PptpControlMessageType names */ ++static const char *strMName[] = { ++ "UNKNOWN_MESSAGE", ++ "START_SESSION_REQUEST", ++ "START_SESSION_REPLY", ++ "STOP_SESSION_REQUEST", ++ "STOP_SESSION_REPLY", ++ "ECHO_REQUEST", ++ "ECHO_REPLY", ++ "OUT_CALL_REQUEST", ++ "OUT_CALL_REPLY", ++ "IN_CALL_REQUEST", ++ "IN_CALL_REPLY", ++ "IN_CALL_CONNECT", ++ "CALL_CLEAR_REQUEST", ++ "CALL_DISCONNECT_NOTIFY", ++ "WAN_ERROR_NOTIFY", ++ "SET_LINK_INFO" ++}; ++ ++#endif +diff -uNr linux_org/net/ipv4/netfilter/ip_conntrack_proto_gre.c linux/net/ipv4/netfilter/ip_conntrack_proto_gre.c +--- linux_org/net/ipv4/netfilter/ip_conntrack_proto_gre.c 1970-01-01 01:00:00.000000000 +0100 ++++ linux/net/ipv4/netfilter/ip_conntrack_proto_gre.c 2006-10-27 14:11:52.000000000 +0200 +@@ -0,0 +1,343 @@ ++/* ++ * ip_conntrack_proto_gre.c - Version 1.2 ++ * ++ * Connection tracking protocol helper module for GRE. ++ * ++ * GRE is a generic encapsulation protocol, which is generally not very ++ * suited for NAT, as it has no protocol-specific part as port numbers. ++ * ++ * It has an optional key field, which may help us distinguishing two ++ * connections between the same two hosts. ++ * ++ * GRE is defined in RFC 1701 and RFC 1702, as well as RFC 2784 ++ * ++ * PPTP is built on top of a modified version of GRE, and has a mandatory ++ * field called "CallID", which serves us for the same purpose as the key ++ * field in plain GRE. ++ * ++ * Documentation about PPTP can be found in RFC 2637 ++ * ++ * (C) 2000-2003 by Harald Welte <laforge@gnumonks.org> ++ * ++ * Development of this code funded by Astaro AG (http://www.astaro.com/) ++ * ++ */ ++ ++#include <linux/config.h> ++#include <linux/module.h> ++#include <linux/types.h> ++#include <linux/timer.h> ++#include <linux/netfilter.h> ++#include <linux/ip.h> ++#include <linux/in.h> ++#include <linux/list.h> ++ ++#include <linux/netfilter_ipv4/lockhelp.h> ++ ++DECLARE_RWLOCK(ip_ct_gre_lock); ++#define ASSERT_READ_LOCK(x) MUST_BE_READ_LOCKED(&ip_ct_gre_lock) ++#define ASSERT_WRITE_LOCK(x) MUST_BE_WRITE_LOCKED(&ip_ct_gre_lock) ++ ++#include <linux/netfilter_ipv4/listhelp.h> ++#include <linux/netfilter_ipv4/ip_conntrack_protocol.h> ++#include <linux/netfilter_ipv4/ip_conntrack_helper.h> ++#include <linux/netfilter_ipv4/ip_conntrack_core.h> ++ ++#include <linux/netfilter_ipv4/ip_conntrack_proto_gre.h> ++#include <linux/netfilter_ipv4/ip_conntrack_pptp.h> ++ ++MODULE_LICENSE("GPL"); ++MODULE_AUTHOR("Harald Welte <laforge@gnumonks.org>"); ++MODULE_DESCRIPTION("netfilter connection tracking protocol helper for GRE"); ++ ++/* shamelessly stolen from ip_conntrack_proto_udp.c */ ++#define GRE_TIMEOUT (30*HZ) ++#define GRE_STREAM_TIMEOUT (180*HZ) ++ ++#if 0 ++#define DEBUGP(format, args...) printk(KERN_DEBUG __FILE__ ":" __FUNCTION__ \ ++ ": " format, ## args) ++#define DUMP_TUPLE_GRE(x) printk("%u.%u.%u.%u:0x%x -> %u.%u.%u.%u:0x%x:%u:0x%x\n", \ ++ NIPQUAD((x)->src.ip), ntohl((x)->src.u.gre.key), \ ++ NIPQUAD((x)->dst.ip), ntohl((x)->dst.u.gre.key), \ ++ (x)->dst.u.gre.version, \ ++ ntohs((x)->dst.u.gre.protocol)) ++#else ++#define DEBUGP(x, args...) ++#define DUMP_TUPLE_GRE(x) ++#endif ++ ++/* GRE KEYMAP HANDLING FUNCTIONS */ ++static LIST_HEAD(gre_keymap_list); ++ ++static inline int gre_key_cmpfn(const struct ip_ct_gre_keymap *km, ++ const struct ip_conntrack_tuple *t) ++{ ++ return ((km->tuple.src.ip == t->src.ip) && ++ (km->tuple.dst.ip == t->dst.ip) && ++ (km->tuple.dst.protonum == t->dst.protonum) && ++ (km->tuple.dst.u.all == t->dst.u.all)); ++} ++ ++/* look up the source key for a given tuple */ ++static u_int32_t gre_keymap_lookup(struct ip_conntrack_tuple *t) ++{ ++ struct ip_ct_gre_keymap *km; ++ u_int32_t key; ++ ++ READ_LOCK(&ip_ct_gre_lock); ++ km = LIST_FIND(&gre_keymap_list, gre_key_cmpfn, ++ struct ip_ct_gre_keymap *, t); ++ if (!km) { ++ READ_UNLOCK(&ip_ct_gre_lock); ++ return 0; ++ } ++ ++ key = km->tuple.src.u.gre.key; ++ READ_UNLOCK(&ip_ct_gre_lock); ++ ++ return key; ++} ++ ++/* add a single keymap entry, associate with specified expect */ ++int ip_ct_gre_keymap_add(struct ip_conntrack_expect *exp, ++ struct ip_conntrack_tuple *t, int reply) ++{ ++ struct ip_ct_gre_keymap *km; ++ ++ km = kmalloc(sizeof(*km), GFP_ATOMIC); ++ if (!km) ++ return -1; ++ ++ /* initializing list head should be sufficient */ ++ memset(km, 0, sizeof(*km)); ++ ++ memcpy(&km->tuple, t, sizeof(*t)); ++ ++ if (!reply) ++ exp->proto.gre.keymap_orig = km; ++ else ++ exp->proto.gre.keymap_reply = km; ++ ++ DEBUGP("adding new entry %p: ", km); ++ DUMP_TUPLE_GRE(&km->tuple); ++ ++ WRITE_LOCK(&ip_ct_gre_lock); ++ list_append(&gre_keymap_list, km); ++ WRITE_UNLOCK(&ip_ct_gre_lock); ++ ++ return 0; ++} ++ ++/* change the tuple of a keymap entry (used by nat helper) */ ++void ip_ct_gre_keymap_change(struct ip_ct_gre_keymap *km, ++ struct ip_conntrack_tuple *t) ++{ ++ DEBUGP("changing entry %p to: ", km); ++ DUMP_TUPLE_GRE(t); ++ ++ WRITE_LOCK(&ip_ct_gre_lock); ++ memcpy(&km->tuple, t, sizeof(km->tuple)); ++ WRITE_UNLOCK(&ip_ct_gre_lock); ++} ++ ++/* destroy the keymap entries associated with specified expect */ ++void ip_ct_gre_keymap_destroy(struct ip_conntrack_expect *exp) ++{ ++ DEBUGP("entering for exp %p\n", exp); ++ WRITE_LOCK(&ip_ct_gre_lock); ++ if (exp->proto.gre.keymap_orig) { ++ DEBUGP("removing %p from list\n", exp->proto.gre.keymap_orig); ++ list_del(&exp->proto.gre.keymap_orig->list); ++ kfree(exp->proto.gre.keymap_orig); ++ exp->proto.gre.keymap_orig = NULL; ++ } ++ if (exp->proto.gre.keymap_reply) { ++ DEBUGP("removing %p from list\n", exp->proto.gre.keymap_reply); ++ list_del(&exp->proto.gre.keymap_reply->list); ++ kfree(exp->proto.gre.keymap_reply); ++ exp->proto.gre.keymap_reply = NULL; ++ } ++ WRITE_UNLOCK(&ip_ct_gre_lock); ++} ++ ++ ++/* PUBLIC CONNTRACK PROTO HELPER FUNCTIONS */ ++ ++/* invert gre part of tuple */ ++static int gre_invert_tuple(struct ip_conntrack_tuple *tuple, ++ const struct ip_conntrack_tuple *orig) ++{ ++ tuple->dst.u.gre.protocol = orig->dst.u.gre.protocol; ++ tuple->dst.u.gre.version = orig->dst.u.gre.version; ++ ++ tuple->dst.u.gre.key = orig->src.u.gre.key; ++ tuple->src.u.gre.key = orig->dst.u.gre.key; ++ ++ return 1; ++} ++ ++/* gre hdr info to tuple */ ++static int gre_pkt_to_tuple(const void *datah, size_t datalen, ++ struct ip_conntrack_tuple *tuple) ++{ ++ struct gre_hdr *grehdr = (struct gre_hdr *) datah; ++ struct gre_hdr_pptp *pgrehdr = (struct gre_hdr_pptp *) datah; ++ u_int32_t srckey; ++ ++ /* core guarantees 8 protocol bytes, no need for size check */ ++ ++ tuple->dst.u.gre.version = grehdr->version; ++ tuple->dst.u.gre.protocol = grehdr->protocol; ++ ++ switch (grehdr->version) { ++ case GRE_VERSION_1701: ++ if (!grehdr->key) { ++ DEBUGP("Can't track GRE without key\n"); ++ return 0; ++ } ++ tuple->dst.u.gre.key = *(gre_key(grehdr)); ++ break; ++ ++ case GRE_VERSION_PPTP: ++ if (ntohs(grehdr->protocol) != GRE_PROTOCOL_PPTP) { ++ DEBUGP("GRE_VERSION_PPTP but unknown proto\n"); ++ return 0; ++ } ++ tuple->dst.u.gre.key = htonl(ntohs(pgrehdr->call_id)); ++ break; ++ ++ default: ++ printk(KERN_WARNING "unknown GRE version %hu\n", ++ tuple->dst.u.gre.version); ++ return 0; ++ } ++ ++ srckey = gre_keymap_lookup(tuple); ++ ++#if 0 ++ DEBUGP("found src key %x for tuple ", ntohl(srckey)); ++ DUMP_TUPLE_GRE(tuple); ++#endif ++ tuple->src.u.gre.key = srckey; ++ ++ return 1; ++} ++ ++/* print gre part of tuple */ ++static unsigned int gre_print_tuple(char *buffer, ++ const struct ip_conntrack_tuple *tuple) ++{ ++ return sprintf(buffer, "version=%d protocol=0x%04x srckey=0x%x dstkey=0x%x ", ++ tuple->dst.u.gre.version, ++ ntohs(tuple->dst.u.gre.protocol), ++ ntohl(tuple->src.u.gre.key), ++ ntohl(tuple->dst.u.gre.key)); ++} ++ ++/* print private data for conntrack */ ++static unsigned int gre_print_conntrack(char *buffer, ++ const struct ip_conntrack *ct) ++{ ++ return sprintf(buffer, "timeout=%u, stream_timeout=%u ", ++ (ct->proto.gre.timeout / HZ), ++ (ct->proto.gre.stream_timeout / HZ)); ++} ++ ++/* Returns verdict for packet, and may modify conntrack */ ++static int gre_packet(struct ip_conntrack *ct, ++ struct iphdr *iph, size_t len, ++ enum ip_conntrack_info conntrackinfo) ++{ ++ /* If we've seen traffic both ways, this is a GRE connection. ++ * Extend timeout. */ ++ if (ct->status & IPS_SEEN_REPLY) { ++ ip_ct_refresh(ct, ct->proto.gre.stream_timeout); ++ /* Also, more likely to be important, and not a probe. */ ++ set_bit(IPS_ASSURED_BIT, &ct->status); ++ } else ++ ip_ct_refresh(ct, ct->proto.gre.timeout); ++ ++ return NF_ACCEPT; ++} ++ ++/* Called when a new connection for this protocol found. */ ++static int gre_new(struct ip_conntrack *ct, ++ struct iphdr *iph, size_t len) ++{ ++ DEBUGP(": "); ++ DUMP_TUPLE_GRE(&ct->tuplehash[IP_CT_DIR_ORIGINAL].tuple); ++ ++ /* initialize to sane value. Ideally a conntrack helper ++ * (e.g. in case of pptp) is increasing them */ ++ ct->proto.gre.stream_timeout = GRE_STREAM_TIMEOUT; ++ ct->proto.gre.timeout = GRE_TIMEOUT; ++ ++ return 1; ++} ++ ++/* Called when a conntrack entry has already been removed from the hashes ++ * and is about to be deleted from memory */ ++static void gre_destroy(struct ip_conntrack *ct) ++{ ++ struct ip_conntrack_expect *master = ct->master; ++ ++ DEBUGP(" entering\n"); ++ ++ if (!master) { ++ DEBUGP("no master exp for ct %p\n", ct); ++ return; ++ } ++ ++ ip_ct_gre_keymap_destroy(master); ++} ++ ++/* protocol helper struct */ ++static struct ip_conntrack_protocol gre = { { NULL, NULL }, IPPROTO_GRE, ++ "gre", ++ gre_pkt_to_tuple, ++ gre_invert_tuple, ++ gre_print_tuple, ++ gre_print_conntrack, ++ gre_packet, ++ gre_new, ++ gre_destroy, ++ NULL, ++ THIS_MODULE }; ++ ++/* ip_conntrack_proto_gre initialization */ ++static int __init init(void) ++{ ++ int retcode; ++ ++ if ((retcode = ip_conntrack_protocol_register(&gre))) { ++ printk(KERN_ERR "Unable to register conntrack protocol " ++ "helper for gre: %d\n", retcode); ++ return -EIO; ++ } ++ ++ return 0; ++} ++ ++static void __exit fini(void) ++{ ++ struct list_head *pos, *n; ++ ++ /* delete all keymap entries */ ++ WRITE_LOCK(&ip_ct_gre_lock); ++ list_for_each_safe(pos, n, &gre_keymap_list) { ++ DEBUGP("deleting keymap %p at module unload time\n", pos); ++ list_del(pos); ++ kfree(pos); ++ } ++ WRITE_UNLOCK(&ip_ct_gre_lock); ++ ++ ip_conntrack_protocol_unregister(&gre); ++} ++ ++EXPORT_SYMBOL(ip_ct_gre_keymap_add); ++EXPORT_SYMBOL(ip_ct_gre_keymap_change); ++EXPORT_SYMBOL(ip_ct_gre_keymap_destroy); ++ ++module_init(init); ++module_exit(fini); +diff -uNr linux_org/net/ipv4/netfilter/ip_nat_core.c linux/net/ipv4/netfilter/ip_nat_core.c +--- linux_org/net/ipv4/netfilter/ip_nat_core.c 2004-11-24 12:14:04.000000000 +0100 ++++ linux/net/ipv4/netfilter/ip_nat_core.c 2006-10-27 14:11:52.000000000 +0200 +@@ -430,7 +430,7 @@ + *tuple = *orig_tuple; + while ((rptr = find_best_ips_proto_fast(tuple, mr, conntrack, hooknum)) + != NULL) { +- DEBUGP("Found best for "); DUMP_TUPLE(tuple); ++ DEBUGP("Found best for "); DUMP_TUPLE_RAW(tuple); + /* 3) The per-protocol part of the manip is made to + map into the range to make a unique tuple. */ + +@@ -572,9 +572,9 @@ + HOOK2MANIP(hooknum)==IP_NAT_MANIP_SRC ? "SRC" : "DST", + conntrack); + DEBUGP("Original: "); +- DUMP_TUPLE(&orig_tp); ++ DUMP_TUPLE_RAW(&orig_tp); + DEBUGP("New: "); +- DUMP_TUPLE(&new_tuple); ++ DUMP_TUPLE_RAW(&new_tuple); + #endif + + /* We now have two tuples (SRCIP/SRCPT/DSTIP/DSTPT): +diff -uNr linux_org/net/ipv4/netfilter/ip_nat_core.c.orig linux/net/ipv4/netfilter/ip_nat_core.c.orig +--- linux_org/net/ipv4/netfilter/ip_nat_core.c.orig 1970-01-01 01:00:00.000000000 +0100 ++++ linux/net/ipv4/netfilter/ip_nat_core.c.orig 2004-11-24 12:14:04.000000000 +0100 +@@ -0,0 +1,1014 @@ ++/* NAT for netfilter; shared with compatibility layer. */ ++ ++/* (c) 1999 Paul `Rusty' Russell. Licenced under the GNU General ++ Public Licence. */ ++#include <linux/version.h> ++#include <linux/module.h> ++#include <linux/types.h> ++#include <linux/timer.h> ++#include <linux/skbuff.h> ++#include <linux/netfilter_ipv4.h> ++#include <linux/brlock.h> ++#include <linux/vmalloc.h> ++#include <net/checksum.h> ++#include <net/icmp.h> ++#include <net/ip.h> ++#include <net/tcp.h> /* For tcp_prot in getorigdst */ ++ ++#define ASSERT_READ_LOCK(x) MUST_BE_READ_LOCKED(&ip_nat_lock) ++#define ASSERT_WRITE_LOCK(x) MUST_BE_WRITE_LOCKED(&ip_nat_lock) ++ ++#include <linux/netfilter_ipv4/ip_conntrack.h> ++#include <linux/netfilter_ipv4/ip_conntrack_core.h> ++#include <linux/netfilter_ipv4/ip_conntrack_protocol.h> ++#include <linux/netfilter_ipv4/ip_nat.h> ++#include <linux/netfilter_ipv4/ip_nat_protocol.h> ++#include <linux/netfilter_ipv4/ip_nat_core.h> ++#include <linux/netfilter_ipv4/ip_nat_helper.h> ++#include <linux/netfilter_ipv4/ip_conntrack_helper.h> ++#include <linux/netfilter_ipv4/listhelp.h> ++ ++#if 0 ++#define DEBUGP printk ++#else ++#define DEBUGP(format, args...) ++#endif ++ ++DECLARE_RWLOCK(ip_nat_lock); ++DECLARE_RWLOCK_EXTERN(ip_conntrack_lock); ++ ++/* Calculated at init based on memory size */ ++static unsigned int ip_nat_htable_size; ++ ++static struct list_head *bysource; ++static struct list_head *byipsproto; ++LIST_HEAD(protos); ++LIST_HEAD(helpers); ++ ++extern struct ip_nat_protocol unknown_nat_protocol; ++ ++/* We keep extra hashes for each conntrack, for fast searching. */ ++static inline size_t ++hash_by_ipsproto(u_int32_t src, u_int32_t dst, u_int16_t proto) ++{ ++ /* Modified src and dst, to ensure we don't create two ++ identical streams. */ ++ return (src + dst + proto) % ip_nat_htable_size; ++} ++ ++static inline size_t ++hash_by_src(const struct ip_conntrack_manip *manip, u_int16_t proto) ++{ ++ /* Original src, to ensure we map it consistently if poss. */ ++ return (manip->ip + manip->u.all + proto) % ip_nat_htable_size; ++} ++ ++/* Noone using conntrack by the time this called. */ ++static void ip_nat_cleanup_conntrack(struct ip_conntrack *conn) ++{ ++ struct ip_nat_info *info = &conn->nat.info; ++ unsigned int hs, hp; ++ ++ if (!info->initialized) ++ return; ++ ++ IP_NF_ASSERT(info->bysource.conntrack); ++ IP_NF_ASSERT(info->byipsproto.conntrack); ++ ++ hs = hash_by_src(&conn->tuplehash[IP_CT_DIR_ORIGINAL].tuple.src, ++ conn->tuplehash[IP_CT_DIR_ORIGINAL] ++ .tuple.dst.protonum); ++ ++ hp = hash_by_ipsproto(conn->tuplehash[IP_CT_DIR_REPLY].tuple.src.ip, ++ conn->tuplehash[IP_CT_DIR_REPLY].tuple.dst.ip, ++ conn->tuplehash[IP_CT_DIR_REPLY] ++ .tuple.dst.protonum); ++ ++ WRITE_LOCK(&ip_nat_lock); ++ LIST_DELETE(&bysource[hs], &info->bysource); ++ LIST_DELETE(&byipsproto[hp], &info->byipsproto); ++ WRITE_UNLOCK(&ip_nat_lock); ++} ++ ++/* We do checksum mangling, so if they were wrong before they're still ++ * wrong. Also works for incomplete packets (eg. ICMP dest ++ * unreachables.) */ ++u_int16_t ++ip_nat_cheat_check(u_int32_t oldvalinv, u_int32_t newval, u_int16_t oldcheck) ++{ ++ u_int32_t diffs[] = { oldvalinv, newval }; ++ return csum_fold(csum_partial((char *)diffs, sizeof(diffs), ++ oldcheck^0xFFFF)); ++} ++ ++static inline int cmp_proto(const struct ip_nat_protocol *i, int proto) ++{ ++ return i->protonum == proto; ++} ++ ++struct ip_nat_protocol * ++find_nat_proto(u_int16_t protonum) ++{ ++ struct ip_nat_protocol *i; ++ ++ MUST_BE_READ_LOCKED(&ip_nat_lock); ++ i = LIST_FIND(&protos, cmp_proto, struct ip_nat_protocol *, protonum); ++ if (!i) ++ i = &unknown_nat_protocol; ++ return i; ++} ++ ++/* Is this tuple already taken? (not by us) */ ++int ++ip_nat_used_tuple(const struct ip_conntrack_tuple *tuple, ++ const struct ip_conntrack *ignored_conntrack) ++{ ++ /* Conntrack tracking doesn't keep track of outgoing tuples; only ++ incoming ones. NAT means they don't have a fixed mapping, ++ so we invert the tuple and look for the incoming reply. ++ ++ We could keep a separate hash if this proves too slow. */ ++ struct ip_conntrack_tuple reply; ++ ++ invert_tuplepr(&reply, tuple); ++ return ip_conntrack_tuple_taken(&reply, ignored_conntrack); ++} ++ ++/* Does tuple + the source manip come within the range mr */ ++static int ++in_range(const struct ip_conntrack_tuple *tuple, ++ const struct ip_conntrack_manip *manip, ++ const struct ip_nat_multi_range *mr) ++{ ++ struct ip_nat_protocol *proto = find_nat_proto(tuple->dst.protonum); ++ unsigned int i; ++ struct ip_conntrack_tuple newtuple = { *manip, tuple->dst }; ++ ++ for (i = 0; i < mr->rangesize; i++) { ++ /* If we are allowed to map IPs, then we must be in the ++ range specified, otherwise we must be unchanged. */ ++ if (mr->range[i].flags & IP_NAT_RANGE_MAP_IPS) { ++ if (ntohl(newtuple.src.ip) < ntohl(mr->range[i].min_ip) ++ || (ntohl(newtuple.src.ip) ++ > ntohl(mr->range[i].max_ip))) ++ continue; ++ } else { ++ if (newtuple.src.ip != tuple->src.ip) ++ continue; ++ } ++ ++ if ((mr->range[i].flags & IP_NAT_RANGE_PROTO_SPECIFIED) ++ && proto->in_range(&newtuple, IP_NAT_MANIP_SRC, ++ &mr->range[i].min, &mr->range[i].max)) ++ return 1; ++ } ++ return 0; ++} ++ ++static inline int ++src_cmp(const struct ip_nat_hash *i, ++ const struct ip_conntrack_tuple *tuple, ++ const struct ip_nat_multi_range *mr) ++{ ++ return (i->conntrack->tuplehash[IP_CT_DIR_ORIGINAL].tuple.dst.protonum ++ == tuple->dst.protonum ++ && i->conntrack->tuplehash[IP_CT_DIR_ORIGINAL].tuple.src.ip ++ == tuple->src.ip ++ && i->conntrack->tuplehash[IP_CT_DIR_ORIGINAL].tuple.src.u.all ++ == tuple->src.u.all ++ && in_range(tuple, ++ &i->conntrack->tuplehash[IP_CT_DIR_ORIGINAL] ++ .tuple.src, ++ mr)); ++} ++ ++/* Only called for SRC manip */ ++static struct ip_conntrack_manip * ++find_appropriate_src(const struct ip_conntrack_tuple *tuple, ++ const struct ip_nat_multi_range *mr) ++{ ++ unsigned int h = hash_by_src(&tuple->src, tuple->dst.protonum); ++ struct ip_nat_hash *i; ++ ++ MUST_BE_READ_LOCKED(&ip_nat_lock); ++ i = LIST_FIND(&bysource[h], src_cmp, struct ip_nat_hash *, tuple, mr); ++ if (i) ++ return &i->conntrack->tuplehash[IP_CT_DIR_ORIGINAL].tuple.src; ++ else ++ return NULL; ++} ++ ++#ifdef CONFIG_IP_NF_NAT_LOCAL ++/* If it's really a local destination manip, it may need to do a ++ source manip too. */ ++static int ++do_extra_mangle(u_int32_t var_ip, u_int32_t *other_ipp) ++{ ++ struct rtable *rt; ++ ++ /* FIXME: IPTOS_TOS(iph->tos) --RR */ ++ if (ip_route_output(&rt, var_ip, 0, 0, 0) != 0) { ++ DEBUGP("do_extra_mangle: Can't get route to %u.%u.%u.%u\n", ++ NIPQUAD(var_ip)); ++ return 0; ++ } ++ ++ *other_ipp = rt->rt_src; ++ ip_rt_put(rt); ++ return 1; ++} ++#endif ++ ++/* Simple way to iterate through all. */ ++static inline int fake_cmp(const struct ip_nat_hash *i, ++ u_int32_t src, u_int32_t dst, u_int16_t protonum, ++ unsigned int *score, ++ const struct ip_conntrack *conntrack) ++{ ++ /* Compare backwards: we're dealing with OUTGOING tuples, and ++ inside the conntrack is the REPLY tuple. Don't count this ++ conntrack. */ ++ if (i->conntrack != conntrack ++ && i->conntrack->tuplehash[IP_CT_DIR_REPLY].tuple.src.ip == dst ++ && i->conntrack->tuplehash[IP_CT_DIR_REPLY].tuple.dst.ip == src ++ && (i->conntrack->tuplehash[IP_CT_DIR_REPLY].tuple.dst.protonum ++ == protonum)) ++ (*score)++; ++ return 0; ++} ++ ++static inline unsigned int ++count_maps(u_int32_t src, u_int32_t dst, u_int16_t protonum, ++ const struct ip_conntrack *conntrack) ++{ ++ unsigned int score = 0; ++ unsigned int h; ++ ++ MUST_BE_READ_LOCKED(&ip_nat_lock); ++ h = hash_by_ipsproto(src, dst, protonum); ++ LIST_FIND(&byipsproto[h], fake_cmp, struct ip_nat_hash *, ++ src, dst, protonum, &score, conntrack); ++ ++ return score; ++} ++ ++/* For [FUTURE] fragmentation handling, we want the least-used ++ src-ip/dst-ip/proto triple. Fairness doesn't come into it. Thus ++ if the range specifies 1.2.3.4 ports 10000-10005 and 1.2.3.5 ports ++ 1-65535, we don't do pro-rata allocation based on ports; we choose ++ the ip with the lowest src-ip/dst-ip/proto usage. ++ ++ If an allocation then fails (eg. all 6 ports used in the 1.2.3.4 ++ range), we eliminate that and try again. This is not the most ++ efficient approach, but if you're worried about that, don't hand us ++ ranges you don't really have. */ ++static struct ip_nat_range * ++find_best_ips_proto(struct ip_conntrack_tuple *tuple, ++ const struct ip_nat_multi_range *mr, ++ const struct ip_conntrack *conntrack, ++ unsigned int hooknum) ++{ ++ unsigned int i; ++ struct { ++ const struct ip_nat_range *range; ++ unsigned int score; ++ struct ip_conntrack_tuple tuple; ++ } best = { NULL, 0xFFFFFFFF }; ++ u_int32_t *var_ipp, *other_ipp, saved_ip, orig_dstip; ++ static unsigned int randomness = 0; ++ ++ if (HOOK2MANIP(hooknum) == IP_NAT_MANIP_SRC) { ++ var_ipp = &tuple->src.ip; ++ saved_ip = tuple->dst.ip; ++ other_ipp = &tuple->dst.ip; ++ } else { ++ var_ipp = &tuple->dst.ip; ++ saved_ip = tuple->src.ip; ++ other_ipp = &tuple->src.ip; ++ } ++ /* Don't do do_extra_mangle unless neccessary (overrides ++ explicit socket bindings, for example) */ ++ orig_dstip = tuple->dst.ip; ++ ++ IP_NF_ASSERT(mr->rangesize >= 1); ++ for (i = 0; i < mr->rangesize; i++) { ++ /* Host order */ ++ u_int32_t minip, maxip, j; ++ ++ /* Don't do ranges which are already eliminated. */ ++ if (mr->range[i].flags & IP_NAT_RANGE_FULL) { ++ continue; ++ } ++ ++ if (mr->range[i].flags & IP_NAT_RANGE_MAP_IPS) { ++ minip = ntohl(mr->range[i].min_ip); ++ maxip = ntohl(mr->range[i].max_ip); ++ } else ++ minip = maxip = ntohl(*var_ipp); ++ ++ randomness++; ++ for (j = 0; j < maxip - minip + 1; j++) { ++ unsigned int score; ++ ++ *var_ipp = htonl(minip + (randomness + j) ++ % (maxip - minip + 1)); ++ ++ /* Reset the other ip in case it was mangled by ++ * do_extra_mangle last time. */ ++ *other_ipp = saved_ip; ++ ++#ifdef CONFIG_IP_NF_NAT_LOCAL ++ if (hooknum == NF_IP_LOCAL_OUT ++ && *var_ipp != orig_dstip ++ && !do_extra_mangle(*var_ipp, other_ipp)) { ++ DEBUGP("Range %u %u.%u.%u.%u rt failed!\n", ++ i, NIPQUAD(*var_ipp)); ++ /* Can't route? This whole range part is ++ * probably screwed, but keep trying ++ * anyway. */ ++ continue; ++ } ++#endif ++ ++ /* Count how many others map onto this. */ ++ score = count_maps(tuple->src.ip, tuple->dst.ip, ++ tuple->dst.protonum, conntrack); ++ if (score < best.score) { ++ /* Optimization: doesn't get any better than ++ this. */ ++ if (score == 0) ++ return (struct ip_nat_range *) ++ &mr->range[i]; ++ ++ best.score = score; ++ best.tuple = *tuple; ++ best.range = &mr->range[i]; ++ } ++ } ++ } ++ *tuple = best.tuple; ++ ++ /* Discard const. */ ++ return (struct ip_nat_range *)best.range; ++} ++ ++/* Fast version doesn't iterate through hash chains, but only handles ++ common case of single IP address (null NAT, masquerade) */ ++static struct ip_nat_range * ++find_best_ips_proto_fast(struct ip_conntrack_tuple *tuple, ++ const struct ip_nat_multi_range *mr, ++ const struct ip_conntrack *conntrack, ++ unsigned int hooknum) ++{ ++ if (mr->rangesize != 1 ++ || (mr->range[0].flags & IP_NAT_RANGE_FULL) ++ || ((mr->range[0].flags & IP_NAT_RANGE_MAP_IPS) ++ && mr->range[0].min_ip != mr->range[0].max_ip)) ++ return find_best_ips_proto(tuple, mr, conntrack, hooknum); ++ ++ if (mr->range[0].flags & IP_NAT_RANGE_MAP_IPS) { ++ if (HOOK2MANIP(hooknum) == IP_NAT_MANIP_SRC) ++ tuple->src.ip = mr->range[0].min_ip; ++ else { ++ /* Only do extra mangle when required (breaks ++ socket binding) */ ++#ifdef CONFIG_IP_NF_NAT_LOCAL ++ if (tuple->dst.ip != mr->range[0].min_ip ++ && hooknum == NF_IP_LOCAL_OUT ++ && !do_extra_mangle(mr->range[0].min_ip, ++ &tuple->src.ip)) ++ return NULL; ++#endif ++ tuple->dst.ip = mr->range[0].min_ip; ++ } ++ } ++ ++ /* Discard const. */ ++ return (struct ip_nat_range *)&mr->range[0]; ++} ++ ++static int ++get_unique_tuple(struct ip_conntrack_tuple *tuple, ++ const struct ip_conntrack_tuple *orig_tuple, ++ const struct ip_nat_multi_range *mrr, ++ struct ip_conntrack *conntrack, ++ unsigned int hooknum) ++{ ++ struct ip_nat_protocol *proto ++ = find_nat_proto(orig_tuple->dst.protonum); ++ struct ip_nat_range *rptr; ++ unsigned int i; ++ int ret; ++ ++ /* We temporarily use flags for marking full parts, but we ++ always clean up afterwards */ ++ struct ip_nat_multi_range *mr = (void *)mrr; ++ ++ /* 1) If this srcip/proto/src-proto-part is currently mapped, ++ and that same mapping gives a unique tuple within the given ++ range, use that. ++ ++ This is only required for source (ie. NAT/masq) mappings. ++ So far, we don't do local source mappings, so multiple ++ manips not an issue. */ ++ if (hooknum == NF_IP_POST_ROUTING) { ++ struct ip_conntrack_manip *manip; ++ ++ manip = find_appropriate_src(orig_tuple, mr); ++ if (manip) { ++ /* Apply same source manipulation. */ ++ *tuple = ((struct ip_conntrack_tuple) ++ { *manip, orig_tuple->dst }); ++ DEBUGP("get_unique_tuple: Found current src map\n"); ++ return 1; ++ } ++ } ++ ++ /* 2) Select the least-used IP/proto combination in the given ++ range. ++ */ ++ *tuple = *orig_tuple; ++ while ((rptr = find_best_ips_proto_fast(tuple, mr, conntrack, hooknum)) ++ != NULL) { ++ DEBUGP("Found best for "); DUMP_TUPLE(tuple); ++ /* 3) The per-protocol part of the manip is made to ++ map into the range to make a unique tuple. */ ++ ++ /* Only bother mapping if it's not already in range ++ and unique */ ++ if ((!(rptr->flags & IP_NAT_RANGE_PROTO_SPECIFIED) ++ || proto->in_range(tuple, HOOK2MANIP(hooknum), ++ &rptr->min, &rptr->max)) ++ && !ip_nat_used_tuple(tuple, conntrack)) { ++ ret = 1; ++ goto clear_fulls; ++ } else { ++ if (proto->unique_tuple(tuple, rptr, ++ HOOK2MANIP(hooknum), ++ conntrack)) { ++ /* Must be unique. */ ++ IP_NF_ASSERT(!ip_nat_used_tuple(tuple, ++ conntrack)); ++ ret = 1; ++ goto clear_fulls; ++ } else if (HOOK2MANIP(hooknum) == IP_NAT_MANIP_DST) { ++ /* Try implicit source NAT; protocol ++ may be able to play with ports to ++ make it unique. */ ++ struct ip_nat_range r ++ = { IP_NAT_RANGE_MAP_IPS, ++ tuple->src.ip, tuple->src.ip, ++ { 0 }, { 0 } }; ++ DEBUGP("Trying implicit mapping\n"); ++ if (proto->unique_tuple(tuple, &r, ++ IP_NAT_MANIP_SRC, ++ conntrack)) { ++ /* Must be unique. */ ++ IP_NF_ASSERT(!ip_nat_used_tuple ++ (tuple, conntrack)); ++ ret = 1; ++ goto clear_fulls; ++ } ++ } ++ DEBUGP("Protocol can't get unique tuple %u.\n", ++ hooknum); ++ } ++ ++ /* Eliminate that from range, and try again. */ ++ rptr->flags |= IP_NAT_RANGE_FULL; ++ *tuple = *orig_tuple; ++ } ++ ++ ret = 0; ++ ++ clear_fulls: ++ /* Clear full flags. */ ++ IP_NF_ASSERT(mr->rangesize >= 1); ++ for (i = 0; i < mr->rangesize; i++) ++ mr->range[i].flags &= ~IP_NAT_RANGE_FULL; ++ ++ return ret; ++} ++ ++static inline int ++helper_cmp(const struct ip_nat_helper *helper, ++ const struct ip_conntrack_tuple *tuple) ++{ ++ return ip_ct_tuple_mask_cmp(tuple, &helper->tuple, &helper->mask); ++} ++ ++/* Where to manip the reply packets (will be reverse manip). */ ++static unsigned int opposite_hook[NF_IP_NUMHOOKS] ++= { [NF_IP_PRE_ROUTING] = NF_IP_POST_ROUTING, ++ [NF_IP_POST_ROUTING] = NF_IP_PRE_ROUTING, ++#ifdef CONFIG_IP_NF_NAT_LOCAL ++ [NF_IP_LOCAL_OUT] = NF_IP_LOCAL_IN, ++ [NF_IP_LOCAL_IN] = NF_IP_LOCAL_OUT, ++#endif ++}; ++ ++unsigned int ++ip_nat_setup_info(struct ip_conntrack *conntrack, ++ const struct ip_nat_multi_range *mr, ++ unsigned int hooknum) ++{ ++ struct ip_conntrack_tuple new_tuple, inv_tuple, reply; ++ struct ip_conntrack_tuple orig_tp; ++ struct ip_nat_info *info = &conntrack->nat.info; ++ int in_hashes = info->initialized; ++ ++ MUST_BE_WRITE_LOCKED(&ip_nat_lock); ++ IP_NF_ASSERT(hooknum == NF_IP_PRE_ROUTING ++ || hooknum == NF_IP_POST_ROUTING ++ || hooknum == NF_IP_LOCAL_IN ++ || hooknum == NF_IP_LOCAL_OUT); ++ IP_NF_ASSERT(info->num_manips < IP_NAT_MAX_MANIPS); ++ IP_NF_ASSERT(!(info->initialized & (1 << HOOK2MANIP(hooknum)))); ++ ++ /* What we've got will look like inverse of reply. Normally ++ this is what is in the conntrack, except for prior ++ manipulations (future optimization: if num_manips == 0, ++ orig_tp = ++ conntrack->tuplehash[IP_CT_DIR_ORIGINAL].tuple) */ ++ invert_tuplepr(&orig_tp, ++ &conntrack->tuplehash[IP_CT_DIR_REPLY].tuple); ++ ++#if 0 ++ { ++ unsigned int i; ++ ++ DEBUGP("Hook %u (%s), ", hooknum, ++ HOOK2MANIP(hooknum)==IP_NAT_MANIP_SRC ? "SRC" : "DST"); ++ DUMP_TUPLE(&orig_tp); ++ DEBUGP("Range %p: ", mr); ++ for (i = 0; i < mr->rangesize; i++) { ++ DEBUGP("%u:%s%s%s %u.%u.%u.%u - %u.%u.%u.%u %u - %u\n", ++ i, ++ (mr->range[i].flags & IP_NAT_RANGE_MAP_IPS) ++ ? " MAP_IPS" : "", ++ (mr->range[i].flags ++ & IP_NAT_RANGE_PROTO_SPECIFIED) ++ ? " PROTO_SPECIFIED" : "", ++ (mr->range[i].flags & IP_NAT_RANGE_FULL) ++ ? " FULL" : "", ++ NIPQUAD(mr->range[i].min_ip), ++ NIPQUAD(mr->range[i].max_ip), ++ mr->range[i].min.all, ++ mr->range[i].max.all); ++ } ++ } ++#endif ++ ++ do { ++ if (!get_unique_tuple(&new_tuple, &orig_tp, mr, conntrack, ++ hooknum)) { ++ DEBUGP("ip_nat_setup_info: Can't get unique for %p.\n", ++ conntrack); ++ return NF_DROP; ++ } ++ ++#if 0 ++ DEBUGP("Hook %u (%s) %p\n", hooknum, ++ HOOK2MANIP(hooknum)==IP_NAT_MANIP_SRC ? "SRC" : "DST", ++ conntrack); ++ DEBUGP("Original: "); ++ DUMP_TUPLE(&orig_tp); ++ DEBUGP("New: "); ++ DUMP_TUPLE(&new_tuple); ++#endif ++ ++ /* We now have two tuples (SRCIP/SRCPT/DSTIP/DSTPT): ++ the original (A/B/C/D') and the mangled one (E/F/G/H'). ++ ++ We're only allowed to work with the SRC per-proto ++ part, so we create inverses of both to start, then ++ derive the other fields we need. */ ++ ++ /* Reply connection: simply invert the new tuple ++ (G/H/E/F') */ ++ invert_tuplepr(&reply, &new_tuple); ++ ++ /* Alter conntrack table so it recognizes replies. ++ If fail this race (reply tuple now used), repeat. */ ++ } while (!ip_conntrack_alter_reply(conntrack, &reply)); ++ ++ /* FIXME: We can simply used existing conntrack reply tuple ++ here --RR */ ++ /* Create inverse of original: C/D/A/B' */ ++ invert_tuplepr(&inv_tuple, &orig_tp); ++ ++ /* Has source changed?. */ ++ if (!ip_ct_tuple_src_equal(&new_tuple, &orig_tp)) { ++ /* In this direction, a source manip. */ ++ info->manips[info->num_manips++] = ++ ((struct ip_nat_info_manip) ++ { IP_CT_DIR_ORIGINAL, hooknum, ++ IP_NAT_MANIP_SRC, new_tuple.src }); ++ ++ IP_NF_ASSERT(info->num_manips < IP_NAT_MAX_MANIPS); ++ ++ /* In the reverse direction, a destination manip. */ ++ info->manips[info->num_manips++] = ++ ((struct ip_nat_info_manip) ++ { IP_CT_DIR_REPLY, opposite_hook[hooknum], ++ IP_NAT_MANIP_DST, orig_tp.src }); ++ IP_NF_ASSERT(info->num_manips <= IP_NAT_MAX_MANIPS); ++ } ++ ++ /* Has destination changed? */ ++ if (!ip_ct_tuple_dst_equal(&new_tuple, &orig_tp)) { ++ /* In this direction, a destination manip */ ++ info->manips[info->num_manips++] = ++ ((struct ip_nat_info_manip) ++ { IP_CT_DIR_ORIGINAL, hooknum, ++ IP_NAT_MANIP_DST, reply.src }); ++ ++ IP_NF_ASSERT(info->num_manips < IP_NAT_MAX_MANIPS); ++ ++ /* In the reverse direction, a source manip. */ ++ info->manips[info->num_manips++] = ++ ((struct ip_nat_info_manip) ++ { IP_CT_DIR_REPLY, opposite_hook[hooknum], ++ IP_NAT_MANIP_SRC, inv_tuple.src }); ++ IP_NF_ASSERT(info->num_manips <= IP_NAT_MAX_MANIPS); ++ } ++ ++ /* If there's a helper, assign it; based on new tuple. */ ++ if (!conntrack->master) ++ info->helper = LIST_FIND(&helpers, helper_cmp, struct ip_nat_helper *, ++ &reply); ++ ++ /* It's done. */ ++ info->initialized |= (1 << HOOK2MANIP(hooknum)); ++ ++ if (in_hashes) { ++ IP_NF_ASSERT(info->bysource.conntrack); ++ replace_in_hashes(conntrack, info); ++ } else { ++ place_in_hashes(conntrack, info); ++ } ++ ++ return NF_ACCEPT; ++} ++ ++void replace_in_hashes(struct ip_conntrack *conntrack, ++ struct ip_nat_info *info) ++{ ++ /* Source has changed, so replace in hashes. */ ++ unsigned int srchash ++ = hash_by_src(&conntrack->tuplehash[IP_CT_DIR_ORIGINAL] ++ .tuple.src, ++ conntrack->tuplehash[IP_CT_DIR_ORIGINAL] ++ .tuple.dst.protonum); ++ /* We place packet as seen OUTGOUNG in byips_proto hash ++ (ie. reverse dst and src of reply packet. */ ++ unsigned int ipsprotohash ++ = hash_by_ipsproto(conntrack->tuplehash[IP_CT_DIR_REPLY] ++ .tuple.dst.ip, ++ conntrack->tuplehash[IP_CT_DIR_REPLY] ++ .tuple.src.ip, ++ conntrack->tuplehash[IP_CT_DIR_REPLY] ++ .tuple.dst.protonum); ++ ++ IP_NF_ASSERT(info->bysource.conntrack == conntrack); ++ MUST_BE_WRITE_LOCKED(&ip_nat_lock); ++ ++ list_del(&info->bysource.list); ++ list_del(&info->byipsproto.list); ++ ++ list_prepend(&bysource[srchash], &info->bysource); ++ list_prepend(&byipsproto[ipsprotohash], &info->byipsproto); ++} ++ ++void place_in_hashes(struct ip_conntrack *conntrack, ++ struct ip_nat_info *info) ++{ ++ unsigned int srchash ++ = hash_by_src(&conntrack->tuplehash[IP_CT_DIR_ORIGINAL] ++ .tuple.src, ++ conntrack->tuplehash[IP_CT_DIR_ORIGINAL] ++ .tuple.dst.protonum); ++ /* We place packet as seen OUTGOUNG in byips_proto hash ++ (ie. reverse dst and src of reply packet. */ ++ unsigned int ipsprotohash ++ = hash_by_ipsproto(conntrack->tuplehash[IP_CT_DIR_REPLY] ++ .tuple.dst.ip, ++ conntrack->tuplehash[IP_CT_DIR_REPLY] ++ .tuple.src.ip, ++ conntrack->tuplehash[IP_CT_DIR_REPLY] ++ .tuple.dst.protonum); ++ ++ IP_NF_ASSERT(!info->bysource.conntrack); ++ ++ MUST_BE_WRITE_LOCKED(&ip_nat_lock); ++ info->byipsproto.conntrack = conntrack; ++ info->bysource.conntrack = conntrack; ++ ++ list_prepend(&bysource[srchash], &info->bysource); ++ list_prepend(&byipsproto[ipsprotohash], &info->byipsproto); ++} ++ ++static void ++manip_pkt(u_int16_t proto, struct iphdr *iph, size_t len, ++ const struct ip_conntrack_manip *manip, ++ enum ip_nat_manip_type maniptype, ++ __u32 *nfcache) ++{ ++ *nfcache |= NFC_ALTERED; ++ find_nat_proto(proto)->manip_pkt(iph, len, manip, maniptype); ++ ++ if (maniptype == IP_NAT_MANIP_SRC) { ++ iph->check = ip_nat_cheat_check(~iph->saddr, manip->ip, ++ iph->check); ++ iph->saddr = manip->ip; ++ } else { ++ iph->check = ip_nat_cheat_check(~iph->daddr, manip->ip, ++ iph->check); ++ iph->daddr = manip->ip; ++ } ++#if 0 ++ if (ip_fast_csum((u8 *)iph, iph->ihl) != 0) ++ DEBUGP("IP: checksum on packet bad.\n"); ++ ++ if (proto == IPPROTO_TCP) { ++ void *th = (u_int32_t *)iph + iph->ihl; ++ if (tcp_v4_check(th, len - 4*iph->ihl, iph->saddr, iph->daddr, ++ csum_partial((char *)th, len-4*iph->ihl, 0))) ++ DEBUGP("TCP: checksum on packet bad\n"); ++ } ++#endif ++} ++ ++static inline int exp_for_packet(struct ip_conntrack_expect *exp, ++ struct sk_buff **pskb) ++{ ++ struct ip_conntrack_protocol *proto; ++ int ret = 1; ++ ++ MUST_BE_READ_LOCKED(&ip_conntrack_lock); ++ proto = __ip_ct_find_proto((*pskb)->nh.iph->protocol); ++ if (proto->exp_matches_pkt) ++ ret = proto->exp_matches_pkt(exp, pskb); ++ ++ return ret; ++} ++ ++/* Do packet manipulations according to binding. */ ++unsigned int ++do_bindings(struct ip_conntrack *ct, ++ enum ip_conntrack_info ctinfo, ++ struct ip_nat_info *info, ++ unsigned int hooknum, ++ struct sk_buff **pskb) ++{ ++ unsigned int i; ++ struct ip_nat_helper *helper; ++ enum ip_conntrack_dir dir = CTINFO2DIR(ctinfo); ++ int is_tcp = (*pskb)->nh.iph->protocol == IPPROTO_TCP; ++ ++ /* Need nat lock to protect against modification, but neither ++ conntrack (referenced) and helper (deleted with ++ synchronize_bh()) can vanish. */ ++ READ_LOCK(&ip_nat_lock); ++ for (i = 0; i < info->num_manips; i++) { ++ /* raw socket (tcpdump) may have clone of incoming ++ skb: don't disturb it --RR */ ++ if (skb_cloned(*pskb) && !(*pskb)->sk) { ++ struct sk_buff *nskb = skb_copy(*pskb, GFP_ATOMIC); ++ if (!nskb) { ++ READ_UNLOCK(&ip_nat_lock); ++ return NF_DROP; ++ } ++ kfree_skb(*pskb); ++ *pskb = nskb; ++ } ++ ++ if (info->manips[i].direction == dir ++ && info->manips[i].hooknum == hooknum) { ++ DEBUGP("Mangling %p: %s to %u.%u.%u.%u %u\n", ++ *pskb, ++ info->manips[i].maniptype == IP_NAT_MANIP_SRC ++ ? "SRC" : "DST", ++ NIPQUAD(info->manips[i].manip.ip), ++ htons(info->manips[i].manip.u.all)); ++ manip_pkt((*pskb)->nh.iph->protocol, ++ (*pskb)->nh.iph, ++ (*pskb)->len, ++ &info->manips[i].manip, ++ info->manips[i].maniptype, ++ &(*pskb)->nfcache); ++ } ++ } ++ helper = info->helper; ++ READ_UNLOCK(&ip_nat_lock); ++ ++ if (helper) { ++ struct ip_conntrack_expect *exp = NULL; ++ struct list_head *cur_item; ++ int ret = NF_ACCEPT; ++ int helper_called = 0; ++ ++ DEBUGP("do_bindings: helper existing for (%p)\n", ct); ++ ++ /* Always defragged for helpers */ ++ IP_NF_ASSERT(!((*pskb)->nh.iph->frag_off ++ & htons(IP_MF|IP_OFFSET))); ++ ++ /* Have to grab read lock before sibling_list traversal */ ++ READ_LOCK(&ip_conntrack_lock); ++ list_for_each_prev(cur_item, &ct->sibling_list) { ++ exp = list_entry(cur_item, struct ip_conntrack_expect, ++ expected_list); ++ ++ /* if this expectation is already established, skip */ ++ if (exp->sibling) ++ continue; ++ ++ if (exp_for_packet(exp, pskb)) { ++ /* FIXME: May be true multiple times in the ++ * case of UDP!! */ ++ DEBUGP("calling nat helper (exp=%p) for packet\n", exp); ++ ret = helper->help(ct, exp, info, ctinfo, ++ hooknum, pskb); ++ if (ret != NF_ACCEPT) { ++ READ_UNLOCK(&ip_conntrack_lock); ++ return ret; ++ } ++ helper_called = 1; ++ } ++ } ++ /* Helper might want to manip the packet even when there is no ++ * matching expectation for this packet */ ++ if (!helper_called && helper->flags & IP_NAT_HELPER_F_ALWAYS) { ++ DEBUGP("calling nat helper for packet without expectation\n"); ++ ret = helper->help(ct, NULL, info, ctinfo, ++ hooknum, pskb); ++ if (ret != NF_ACCEPT) { ++ READ_UNLOCK(&ip_conntrack_lock); ++ return ret; ++ } ++ } ++ READ_UNLOCK(&ip_conntrack_lock); ++ ++ /* Adjust sequence number only once per packet ++ * (helper is called at all hooks) */ ++ if (is_tcp && (hooknum == NF_IP_POST_ROUTING ++ || hooknum == NF_IP_LOCAL_IN)) { ++ DEBUGP("ip_nat_core: adjusting sequence number\n"); ++ /* future: put this in a l4-proto specific function, ++ * and call this function here. */ ++ ip_nat_seq_adjust(*pskb, ct, ctinfo); ++ } ++ ++ return ret; ++ ++ } else ++ return NF_ACCEPT; ++ ++ /* not reached */ ++} ++ ++unsigned int ++icmp_reply_translation(struct sk_buff *skb, ++ struct ip_conntrack *conntrack, ++ unsigned int hooknum, ++ int dir) ++{ ++ struct iphdr *iph = skb->nh.iph; ++ struct icmphdr *hdr = (struct icmphdr *)((u_int32_t *)iph + iph->ihl); ++ struct iphdr *inner = (struct iphdr *)(hdr + 1); ++ size_t datalen = skb->len - ((void *)inner - (void *)iph); ++ unsigned int i; ++ struct ip_nat_info *info = &conntrack->nat.info; ++ ++ IP_NF_ASSERT(skb->len >= iph->ihl*4 + sizeof(struct icmphdr)); ++ /* Must be RELATED */ ++ IP_NF_ASSERT(skb->nfct ++ - ((struct ip_conntrack *)skb->nfct->master)->infos ++ == IP_CT_RELATED ++ || skb->nfct ++ - ((struct ip_conntrack *)skb->nfct->master)->infos ++ == IP_CT_RELATED+IP_CT_IS_REPLY); ++ ++ /* Redirects on non-null nats must be dropped, else they'll ++ start talking to each other without our translation, and be ++ confused... --RR */ ++ if (hdr->type == ICMP_REDIRECT) { ++ /* Don't care about races here. */ ++ if (info->initialized ++ != ((1 << IP_NAT_MANIP_SRC) | (1 << IP_NAT_MANIP_DST)) ++ || info->num_manips != 0) ++ return NF_DROP; ++ } ++ ++ DEBUGP("icmp_reply_translation: translating error %p hook %u dir %s\n", ++ skb, hooknum, dir == IP_CT_DIR_ORIGINAL ? "ORIG" : "REPLY"); ++ /* Note: May not be from a NAT'd host, but probably safest to ++ do translation always as if it came from the host itself ++ (even though a "host unreachable" coming from the host ++ itself is a bit weird). ++ ++ More explanation: some people use NAT for anonymizing. ++ Also, CERT recommends dropping all packets from private IP ++ addresses (although ICMP errors from internal links with ++ such addresses are not too uncommon, as Alan Cox points ++ out) */ ++ ++ READ_LOCK(&ip_nat_lock); ++ for (i = 0; i < info->num_manips; i++) { ++ DEBUGP("icmp_reply: manip %u dir %s hook %u\n", ++ i, info->manips[i].direction == IP_CT_DIR_ORIGINAL ? ++ "ORIG" : "REPLY", info->manips[i].hooknum); ++ ++ if (info->manips[i].direction != dir) ++ continue; ++ ++ /* Mapping the inner packet is just like a normal ++ packet, except it was never src/dst reversed, so ++ where we would normally apply a dst manip, we apply ++ a src, and vice versa. */ ++ if (info->manips[i].hooknum == hooknum) { ++ DEBUGP("icmp_reply: inner %s -> %u.%u.%u.%u %u\n", ++ info->manips[i].maniptype == IP_NAT_MANIP_SRC ++ ? "DST" : "SRC", ++ NIPQUAD(info->manips[i].manip.ip), ++ ntohs(info->manips[i].manip.u.udp.port)); ++ manip_pkt(inner->protocol, inner, ++ skb->len - ((void *)inner - (void *)iph), ++ &info->manips[i].manip, ++ !info->manips[i].maniptype, ++ &skb->nfcache); ++ /* Outer packet needs to have IP header NATed like ++ it's a reply. */ ++ ++ /* Use mapping to map outer packet: 0 give no ++ per-proto mapping */ ++ DEBUGP("icmp_reply: outer %s -> %u.%u.%u.%u\n", ++ info->manips[i].maniptype == IP_NAT_MANIP_SRC ++ ? "SRC" : "DST", ++ NIPQUAD(info->manips[i].manip.ip)); ++ manip_pkt(0, iph, skb->len, ++ &info->manips[i].manip, ++ info->manips[i].maniptype, ++ &skb->nfcache); ++ } ++ } ++ READ_UNLOCK(&ip_nat_lock); ++ ++ /* Since we mangled inside ICMP packet, recalculate its ++ checksum from scratch. (Hence the handling of incorrect ++ checksums in conntrack, so we don't accidentally fix one.) */ ++ hdr->checksum = 0; ++ hdr->checksum = ip_compute_csum((unsigned char *)hdr, ++ sizeof(*hdr) + datalen); ++ ++ return NF_ACCEPT; ++} ++ ++int __init ip_nat_init(void) ++{ ++ size_t i; ++ ++ /* Leave them the same for the moment. */ ++ ip_nat_htable_size = ip_conntrack_htable_size; ++ ++ /* One vmalloc for both hash tables */ ++ bysource = vmalloc(sizeof(struct list_head) * ip_nat_htable_size*2); ++ if (!bysource) { ++ return -ENOMEM; ++ } ++ byipsproto = bysource + ip_nat_htable_size; ++ ++ /* Sew in builtin protocols. */ ++ WRITE_LOCK(&ip_nat_lock); ++ list_append(&protos, &ip_nat_protocol_tcp); ++ list_append(&protos, &ip_nat_protocol_udp); ++ list_append(&protos, &ip_nat_protocol_icmp); ++ WRITE_UNLOCK(&ip_nat_lock); ++ ++ for (i = 0; i < ip_nat_htable_size; i++) { ++ INIT_LIST_HEAD(&bysource[i]); ++ INIT_LIST_HEAD(&byipsproto[i]); ++ } ++ ++ /* FIXME: Man, this is a hack. <SIGH> */ ++ IP_NF_ASSERT(ip_conntrack_destroyed == NULL); ++ ip_conntrack_destroyed = &ip_nat_cleanup_conntrack; ++ ++ return 0; ++} ++ ++/* Clear NAT section of all conntracks, in case we're loaded again. */ ++static int clean_nat(const struct ip_conntrack *i, void *data) ++{ ++ memset((void *)&i->nat, 0, sizeof(i->nat)); ++ return 0; ++} ++ ++/* Not __exit: called from ip_nat_standalone.c:init_or_cleanup() --RR */ ++void ip_nat_cleanup(void) ++{ ++ ip_ct_selective_cleanup(&clean_nat, NULL); ++ ip_conntrack_destroyed = NULL; ++ vfree(bysource); ++} +diff -uNr linux_org/net/ipv4/netfilter/ip_nat_pptp.c linux/net/ipv4/netfilter/ip_nat_pptp.c +--- linux_org/net/ipv4/netfilter/ip_nat_pptp.c 1970-01-01 01:00:00.000000000 +0100 ++++ linux/net/ipv4/netfilter/ip_nat_pptp.c 2006-10-27 14:11:52.000000000 +0200 +@@ -0,0 +1,475 @@ ++/* ++ * ip_nat_pptp.c - Version 1.5 ++ * ++ * NAT support for PPTP (Point to Point Tunneling Protocol). ++ * PPTP is a a protocol for creating virtual private networks. ++ * It is a specification defined by Microsoft and some vendors ++ * working with Microsoft. PPTP is built on top of a modified ++ * version of the Internet Generic Routing Encapsulation Protocol. ++ * GRE is defined in RFC 1701 and RFC 1702. Documentation of ++ * PPTP can be found in RFC 2637 ++ * ++ * (C) 2000-2003 by Harald Welte <laforge@gnumonks.org> ++ * ++ * Development of this code funded by Astaro AG (http://www.astaro.com/) ++ * ++ * TODO: - Support for multiple calls within one session ++ * (needs netfilter newnat code) ++ * - NAT to a unique tuple, not to TCP source port ++ * (needs netfilter tuple reservation) ++ * ++ * Changes: ++ * 2002-02-10 - Version 1.3 ++ * - Use ip_nat_mangle_tcp_packet() because of cloned skb's ++ * in local connections (Philip Craig <philipc@snapgear.com>) ++ * - add checks for magicCookie and pptp version ++ * - make argument list of pptp_{out,in}bound_packet() shorter ++ * - move to C99 style initializers ++ * - print version number at module loadtime ++ * 2003-09-22 - Version 1.5 ++ * - use SNATed tcp sourceport as callid, since we get called before ++ * TCP header is mangled (Philip Craig <philipc@snapgear.com>) ++ * ++ */ ++ ++#include <linux/config.h> ++#include <linux/module.h> ++#include <linux/ip.h> ++#include <linux/tcp.h> ++#include <net/tcp.h> ++#include <linux/netfilter_ipv4/ip_nat.h> ++#include <linux/netfilter_ipv4/ip_nat_rule.h> ++#include <linux/netfilter_ipv4/ip_nat_helper.h> ++#include <linux/netfilter_ipv4/ip_nat_pptp.h> ++#include <linux/netfilter_ipv4/ip_conntrack_helper.h> ++#include <linux/netfilter_ipv4/ip_conntrack_proto_gre.h> ++#include <linux/netfilter_ipv4/ip_conntrack_pptp.h> ++ ++#define IP_NAT_PPTP_VERSION "1.5" ++ ++MODULE_LICENSE("GPL"); ++MODULE_AUTHOR("Harald Welte <laforge@gnumonks.org>"); ++MODULE_DESCRIPTION("Netfilter NAT helper module for PPTP"); ++ ++ ++#if 0 ++#include "ip_conntrack_pptp_priv.h" ++#define DEBUGP(format, args...) printk(KERN_DEBUG __FILE__ ":" __FUNCTION__ \ ++ ": " format, ## args) ++#else ++#define DEBUGP(format, args...) ++#endif ++ ++static unsigned int ++pptp_nat_expected(struct sk_buff **pskb, ++ unsigned int hooknum, ++ struct ip_conntrack *ct, ++ struct ip_nat_info *info) ++{ ++ struct ip_conntrack *master = master_ct(ct); ++ struct ip_nat_multi_range mr; ++ struct ip_ct_pptp_master *ct_pptp_info; ++ struct ip_nat_pptp *nat_pptp_info; ++ u_int32_t newip, newcid; ++ int ret; ++ ++ IP_NF_ASSERT(info); ++ IP_NF_ASSERT(master); ++ IP_NF_ASSERT(!(info->initialized & (1 << HOOK2MANIP(hooknum)))); ++ ++ DEBUGP("we have a connection!\n"); ++ ++ LOCK_BH(&ip_pptp_lock); ++ ct_pptp_info = &master->help.ct_pptp_info; ++ nat_pptp_info = &master->nat.help.nat_pptp_info; ++ ++ /* need to alter GRE tuple because conntrack expectfn() used 'wrong' ++ * (unmanipulated) values */ ++ if (HOOK2MANIP(hooknum) == IP_NAT_MANIP_DST) { ++ DEBUGP("completing tuples with NAT info \n"); ++ /* we can do this, since we're unconfirmed */ ++ if (ct->tuplehash[IP_CT_DIR_ORIGINAL].tuple.dst.u.gre.key == ++ htonl(ct_pptp_info->pac_call_id)) { ++ /* assume PNS->PAC */ ++ ct->tuplehash[IP_CT_DIR_ORIGINAL].tuple.src.u.gre.key = ++ htonl(nat_pptp_info->pns_call_id); ++ ct->tuplehash[IP_CT_DIR_REPLY].tuple.dst.u.gre.key = ++ htonl(nat_pptp_info->pns_call_id); ++ newip = master->tuplehash[IP_CT_DIR_REPLY].tuple.src.ip; ++ newcid = htonl(nat_pptp_info->pac_call_id); ++ } else { ++ /* assume PAC->PNS */ ++ ct->tuplehash[IP_CT_DIR_ORIGINAL].tuple.src.u.gre.key = ++ htonl(nat_pptp_info->pac_call_id); ++ ct->tuplehash[IP_CT_DIR_REPLY].tuple.dst.u.gre.key = ++ htonl(nat_pptp_info->pac_call_id); ++ newip = master->tuplehash[IP_CT_DIR_ORIGINAL].tuple.src.ip; ++ newcid = htonl(nat_pptp_info->pns_call_id); ++ } ++ } else { ++ if (ct->tuplehash[IP_CT_DIR_ORIGINAL].tuple.dst.u.gre.key == ++ htonl(ct_pptp_info->pac_call_id)) { ++ /* assume PNS->PAC */ ++ newip = master->tuplehash[IP_CT_DIR_REPLY].tuple.dst.ip; ++ newcid = htonl(ct_pptp_info->pns_call_id); ++ } ++ else { ++ /* assume PAC->PNS */ ++ newip = master->tuplehash[IP_CT_DIR_ORIGINAL].tuple.dst.ip; ++ newcid = htonl(ct_pptp_info->pac_call_id); ++ } ++ } ++ ++ mr.rangesize = 1; ++ mr.range[0].flags = IP_NAT_RANGE_MAP_IPS | IP_NAT_RANGE_PROTO_SPECIFIED; ++ mr.range[0].min_ip = mr.range[0].max_ip = newip; ++ mr.range[0].min = mr.range[0].max = ++ ((union ip_conntrack_manip_proto ) { newcid }); ++ DEBUGP("change ip to %u.%u.%u.%u\n", ++ NIPQUAD(newip)); ++ DEBUGP("change key to 0x%x\n", ntohl(newcid)); ++ ret = ip_nat_setup_info(ct, &mr, hooknum); ++ ++ UNLOCK_BH(&ip_pptp_lock); ++ ++ return ret; ++ ++} ++ ++/* outbound packets == from PNS to PAC */ ++static inline unsigned int ++pptp_outbound_pkt(struct sk_buff **pskb, ++ struct ip_conntrack *ct, ++ enum ip_conntrack_info ctinfo, ++ struct ip_conntrack_expect *exp) ++ ++{ ++ struct iphdr *iph = (*pskb)->nh.iph; ++ struct tcphdr *tcph = (void *) iph + iph->ihl*4; ++ struct pptp_pkt_hdr *pptph = (struct pptp_pkt_hdr *) ++ ((void *)tcph + tcph->doff*4); ++ ++ struct PptpControlHeader *ctlh; ++ union pptp_ctrl_union pptpReq; ++ struct ip_ct_pptp_master *ct_pptp_info = &ct->help.ct_pptp_info; ++ struct ip_nat_pptp *nat_pptp_info = &ct->nat.help.nat_pptp_info; ++ ++ u_int16_t msg, *cid = NULL, new_callid; ++ ++ /* FIXME: size checks !!! */ ++ ctlh = (struct PptpControlHeader *) ((void *) pptph + sizeof(*pptph)); ++ pptpReq.rawreq = (void *) ((void *) ctlh + sizeof(*ctlh)); ++ ++ new_callid = htons(ct_pptp_info->pns_call_id); ++ ++ switch (msg = ntohs(ctlh->messageType)) { ++ case PPTP_OUT_CALL_REQUEST: ++ cid = &pptpReq.ocreq->callID; ++ /* FIXME: ideally we would want to reserve a call ID ++ * here. current netfilter NAT core is not able to do ++ * this :( For now we use TCP source port. This breaks ++ * multiple calls within one control session */ ++ ++ /* save original call ID in nat_info */ ++ nat_pptp_info->pns_call_id = ct_pptp_info->pns_call_id; ++ ++ /* don't use tcph->source since we are at a DSTmanip ++ * hook (e.g. PREROUTING) and pkt is not mangled yet */ ++ new_callid = ct->tuplehash[IP_CT_DIR_REPLY].tuple.dst.u.tcp.port; ++ ++ /* save new call ID in ct info */ ++ ct_pptp_info->pns_call_id = ntohs(new_callid); ++ break; ++ case PPTP_IN_CALL_REPLY: ++ cid = &pptpReq.icreq->callID; ++ break; ++ case PPTP_CALL_CLEAR_REQUEST: ++ cid = &pptpReq.clrreq->callID; ++ break; ++ default: ++ DEBUGP("unknown outbound packet 0x%04x:%s\n", msg, ++ (msg <= PPTP_MSG_MAX)? strMName[msg]:strMName[0]); ++ /* fall through */ ++ ++ case PPTP_SET_LINK_INFO: ++ /* only need to NAT in case PAC is behind NAT box */ ++ case PPTP_START_SESSION_REQUEST: ++ case PPTP_START_SESSION_REPLY: ++ case PPTP_STOP_SESSION_REQUEST: ++ case PPTP_STOP_SESSION_REPLY: ++ case PPTP_ECHO_REQUEST: ++ case PPTP_ECHO_REPLY: ++ /* no need to alter packet */ ++ return NF_ACCEPT; ++ } ++ ++ IP_NF_ASSERT(cid); ++ ++ DEBUGP("altering call id from 0x%04x to 0x%04x\n", ++ ntohs(*cid), ntohs(new_callid)); ++ ++ /* mangle packet */ ++ ip_nat_mangle_tcp_packet(pskb, ct, ctinfo, (void *)cid - (void *)pptph, ++ sizeof(new_callid), (char *)&new_callid, ++ sizeof(new_callid)); ++ ++ return NF_ACCEPT; ++} ++ ++/* inbound packets == from PAC to PNS */ ++static inline unsigned int ++pptp_inbound_pkt(struct sk_buff **pskb, ++ struct ip_conntrack *ct, ++ enum ip_conntrack_info ctinfo, ++ struct ip_conntrack_expect *oldexp) ++{ ++ struct iphdr *iph = (*pskb)->nh.iph; ++ struct tcphdr *tcph = (void *) iph + iph->ihl*4; ++ struct pptp_pkt_hdr *pptph = (struct pptp_pkt_hdr *) ++ ((void *)tcph + tcph->doff*4); ++ ++ struct PptpControlHeader *ctlh; ++ union pptp_ctrl_union pptpReq; ++ struct ip_ct_pptp_master *ct_pptp_info = &ct->help.ct_pptp_info; ++ struct ip_nat_pptp *nat_pptp_info = &ct->nat.help.nat_pptp_info; ++ ++ u_int16_t msg, new_cid = 0, new_pcid, *pcid = NULL, *cid = NULL; ++ u_int32_t old_dst_ip; ++ ++ struct ip_conntrack_tuple t, inv_t; ++ struct ip_conntrack_tuple *orig_t, *reply_t; ++ ++ /* FIXME: size checks !!! */ ++ ctlh = (struct PptpControlHeader *) ((void *) pptph + sizeof(*pptph)); ++ pptpReq.rawreq = (void *) ((void *) ctlh + sizeof(*ctlh)); ++ ++ new_pcid = htons(nat_pptp_info->pns_call_id); ++ ++ switch (msg = ntohs(ctlh->messageType)) { ++ case PPTP_OUT_CALL_REPLY: ++ pcid = &pptpReq.ocack->peersCallID; ++ cid = &pptpReq.ocack->callID; ++ if (!oldexp) { ++ DEBUGP("outcall but no expectation\n"); ++ break; ++ } ++ old_dst_ip = oldexp->tuple.dst.ip; ++ t = oldexp->tuple; ++ invert_tuplepr(&inv_t, &t); ++ ++ /* save original PAC call ID in nat_info */ ++ nat_pptp_info->pac_call_id = ct_pptp_info->pac_call_id; ++ ++ /* alter expectation */ ++ orig_t = &ct->tuplehash[IP_CT_DIR_ORIGINAL].tuple; ++ reply_t = &ct->tuplehash[IP_CT_DIR_REPLY].tuple; ++ if (t.src.ip == orig_t->src.ip && t.dst.ip == orig_t->dst.ip) { ++ /* expectation for PNS->PAC direction */ ++ t.src.u.gre.key = htonl(nat_pptp_info->pns_call_id); ++ t.dst.u.gre.key = htonl(ct_pptp_info->pac_call_id); ++ inv_t.src.ip = reply_t->src.ip; ++ inv_t.dst.ip = reply_t->dst.ip; ++ inv_t.src.u.gre.key = htonl(nat_pptp_info->pac_call_id); ++ inv_t.dst.u.gre.key = htonl(ct_pptp_info->pns_call_id); ++ } else { ++ /* expectation for PAC->PNS direction */ ++ t.src.u.gre.key = htonl(nat_pptp_info->pac_call_id); ++ t.dst.u.gre.key = htonl(ct_pptp_info->pns_call_id); ++ inv_t.src.ip = orig_t->src.ip; ++ inv_t.dst.ip = orig_t->dst.ip; ++ inv_t.src.u.gre.key = htonl(nat_pptp_info->pns_call_id); ++ inv_t.dst.u.gre.key = htonl(ct_pptp_info->pac_call_id); ++ } ++ ++ if (!ip_conntrack_change_expect(oldexp, &t)) { ++ DEBUGP("successfully changed expect\n"); ++ } else { ++ DEBUGP("can't change expect\n"); ++ } ++ ip_ct_gre_keymap_change(oldexp->proto.gre.keymap_orig, &t); ++ ip_ct_gre_keymap_change(oldexp->proto.gre.keymap_reply, &inv_t); ++ break; ++ case PPTP_IN_CALL_CONNECT: ++ pcid = &pptpReq.iccon->peersCallID; ++ if (!oldexp) ++ break; ++ old_dst_ip = oldexp->tuple.dst.ip; ++ t = oldexp->tuple; ++ ++ /* alter expectation, no need for callID */ ++ if (t.dst.ip == ct->tuplehash[IP_CT_DIR_ORIGINAL].tuple.dst.ip) { ++ /* expectation for PNS->PAC direction */ ++ t.src.ip = ct->tuplehash[IP_CT_DIR_REPLY].tuple.dst.ip; ++ } else { ++ /* expectation for PAC->PNS direction */ ++ t.dst.ip = ct->tuplehash[IP_CT_DIR_REPLY].tuple.dst.ip; ++ } ++ ++ if (!ip_conntrack_change_expect(oldexp, &t)) { ++ DEBUGP("successfully changed expect\n"); ++ } else { ++ DEBUGP("can't change expect\n"); ++ } ++ break; ++ case PPTP_IN_CALL_REQUEST: ++ /* only need to nat in case PAC is behind NAT box */ ++ break; ++ case PPTP_WAN_ERROR_NOTIFY: ++ pcid = &pptpReq.wanerr->peersCallID; ++ break; ++ case PPTP_CALL_DISCONNECT_NOTIFY: ++ pcid = &pptpReq.disc->callID; ++ break; ++ ++ default: ++ DEBUGP("unknown inbound packet %s\n", ++ (msg <= PPTP_MSG_MAX)? strMName[msg]:strMName[0]); ++ /* fall through */ ++ ++ case PPTP_START_SESSION_REQUEST: ++ case PPTP_START_SESSION_REPLY: ++ case PPTP_STOP_SESSION_REQUEST: ++ case PPTP_STOP_SESSION_REPLY: ++ case PPTP_ECHO_REQUEST: ++ case PPTP_ECHO_REPLY: ++ /* no need to alter packet */ ++ return NF_ACCEPT; ++ } ++ ++ /* mangle packet */ ++ IP_NF_ASSERT(pcid); ++ DEBUGP("altering peer call id from 0x%04x to 0x%04x\n", ++ ntohs(*pcid), ntohs(new_pcid)); ++ ip_nat_mangle_tcp_packet(pskb, ct, ctinfo, (void *)pcid - (void *)pptph, ++ sizeof(new_pcid), (char *)&new_pcid, ++ sizeof(new_pcid)); ++ ++ if (new_cid) { ++ IP_NF_ASSERT(cid); ++ DEBUGP("altering call id from 0x%04x to 0x%04x\n", ++ ntohs(*cid), ntohs(new_cid)); ++ ip_nat_mangle_tcp_packet(pskb, ct, ctinfo, ++ (void *)cid - (void *)pptph, ++ sizeof(new_cid), (char *)&new_cid, ++ sizeof(new_cid)); ++ } ++ ++ /* great, at least we don't need to resize packets */ ++ return NF_ACCEPT; ++} ++ ++ ++static unsigned int tcp_help(struct ip_conntrack *ct, ++ struct ip_conntrack_expect *exp, ++ struct ip_nat_info *info, ++ enum ip_conntrack_info ctinfo, ++ unsigned int hooknum, struct sk_buff **pskb) ++{ ++ struct iphdr *iph = (*pskb)->nh.iph; ++ struct tcphdr *tcph = (void *) iph + iph->ihl*4; ++ unsigned int datalen = (*pskb)->len - iph->ihl*4 - tcph->doff*4; ++ struct pptp_pkt_hdr *pptph; ++ ++ int dir; ++ ++ DEBUGP("entering\n"); ++ ++ /* Only mangle things once: DST for original direction ++ and SRC for reply direction. */ ++ dir = CTINFO2DIR(ctinfo); ++ if (!((HOOK2MANIP(hooknum) == IP_NAT_MANIP_SRC ++ && dir == IP_CT_DIR_ORIGINAL) ++ || (HOOK2MANIP(hooknum) == IP_NAT_MANIP_DST ++ && dir == IP_CT_DIR_REPLY))) { ++ DEBUGP("Not touching dir %s at hook %s\n", ++ dir == IP_CT_DIR_ORIGINAL ? "ORIG" : "REPLY", ++ hooknum == NF_IP_POST_ROUTING ? "POSTROUTING" ++ : hooknum == NF_IP_PRE_ROUTING ? "PREROUTING" ++ : hooknum == NF_IP_LOCAL_OUT ? "OUTPUT" ++ : hooknum == NF_IP_LOCAL_IN ? "INPUT" : "???"); ++ return NF_ACCEPT; ++ } ++ ++ /* if packet is too small, just skip it */ ++ if (datalen < sizeof(struct pptp_pkt_hdr)+ ++ sizeof(struct PptpControlHeader)) { ++ DEBUGP("pptp packet too short\n"); ++ return NF_ACCEPT; ++ } ++ ++ pptph = (struct pptp_pkt_hdr *) ((void *)tcph + tcph->doff*4); ++ ++ /* if it's not a control message, we can't handle it */ ++ if (ntohs(pptph->packetType) != PPTP_PACKET_CONTROL || ++ ntohl(pptph->magicCookie) != PPTP_MAGIC_COOKIE) { ++ DEBUGP("not a pptp control packet\n"); ++ return NF_ACCEPT; ++ } ++ ++ LOCK_BH(&ip_pptp_lock); ++ ++ if (dir == IP_CT_DIR_ORIGINAL) { ++ /* reuqests sent by client to server (PNS->PAC) */ ++ pptp_outbound_pkt(pskb, ct, ctinfo, exp); ++ } else { ++ /* response from the server to the client (PAC->PNS) */ ++ pptp_inbound_pkt(pskb, ct, ctinfo, exp); ++ } ++ ++ UNLOCK_BH(&ip_pptp_lock); ++ ++ return NF_ACCEPT; ++} ++ ++/* nat helper struct for control connection */ ++static struct ip_nat_helper pptp_tcp_helper = { ++ .list = { NULL, NULL }, ++ .name = "pptp", ++ .flags = IP_NAT_HELPER_F_ALWAYS, ++ .me = THIS_MODULE, ++ .tuple = { .src = { .ip = 0, ++ .u = { .tcp = { .port = ++ __constant_htons(PPTP_CONTROL_PORT) } ++ } ++ }, ++ .dst = { .ip = 0, ++ .u = { .all = 0 }, ++ .protonum = IPPROTO_TCP ++ } ++ }, ++ ++ .mask = { .src = { .ip = 0, ++ .u = { .tcp = { .port = 0xFFFF } } ++ }, ++ .dst = { .ip = 0, ++ .u = { .all = 0 }, ++ .protonum = 0xFFFF ++ } ++ }, ++ .help = tcp_help, ++ .expect = pptp_nat_expected ++}; ++ ++ ++static int __init init(void) ++{ ++ DEBUGP("%s: registering NAT helper\n", __FILE__); ++ if (ip_nat_helper_register(&pptp_tcp_helper)) { ++ printk(KERN_ERR "Unable to register NAT application helper " ++ "for pptp\n"); ++ return -EIO; ++ } ++ ++ printk("ip_nat_pptp version %s loaded\n", IP_NAT_PPTP_VERSION); ++ return 0; ++} ++ ++static void __exit fini(void) ++{ ++ DEBUGP("cleanup_module\n" ); ++ ip_nat_helper_unregister(&pptp_tcp_helper); ++ printk("ip_nat_pptp version %s unloaded\n", IP_NAT_PPTP_VERSION); ++} ++ ++module_init(init); ++module_exit(fini); +diff -uNr linux_org/net/ipv4/netfilter/ip_nat_proto_gre.c linux/net/ipv4/netfilter/ip_nat_proto_gre.c +--- linux_org/net/ipv4/netfilter/ip_nat_proto_gre.c 1970-01-01 01:00:00.000000000 +0100 ++++ linux/net/ipv4/netfilter/ip_nat_proto_gre.c 2006-10-27 14:11:52.000000000 +0200 +@@ -0,0 +1,225 @@ ++/* ++ * ip_nat_proto_gre.c - Version 1.2 ++ * ++ * NAT protocol helper module for GRE. ++ * ++ * GRE is a generic encapsulation protocol, which is generally not very ++ * suited for NAT, as it has no protocol-specific part as port numbers. ++ * ++ * It has an optional key field, which may help us distinguishing two ++ * connections between the same two hosts. ++ * ++ * GRE is defined in RFC 1701 and RFC 1702, as well as RFC 2784 ++ * ++ * PPTP is built on top of a modified version of GRE, and has a mandatory ++ * field called "CallID", which serves us for the same purpose as the key ++ * field in plain GRE. ++ * ++ * Documentation about PPTP can be found in RFC 2637 ++ * ++ * (C) 2000-2003 by Harald Welte <laforge@gnumonks.org> ++ * ++ * Development of this code funded by Astaro AG (http://www.astaro.com/) ++ * ++ */ ++ ++#include <linux/config.h> ++#include <linux/module.h> ++#include <linux/ip.h> ++#include <linux/netfilter_ipv4/ip_nat.h> ++#include <linux/netfilter_ipv4/ip_nat_rule.h> ++#include <linux/netfilter_ipv4/ip_nat_protocol.h> ++#include <linux/netfilter_ipv4/ip_conntrack_proto_gre.h> ++ ++MODULE_LICENSE("GPL"); ++MODULE_AUTHOR("Harald Welte <laforge@gnumonks.org>"); ++MODULE_DESCRIPTION("Netfilter NAT protocol helper module for GRE"); ++ ++#if 0 ++#define DEBUGP(format, args...) printk(KERN_DEBUG __FILE__ ":" __FUNCTION__ \ ++ ": " format, ## args) ++#else ++#define DEBUGP(x, args...) ++#endif ++ ++/* is key in given range between min and max */ ++static int ++gre_in_range(const struct ip_conntrack_tuple *tuple, ++ enum ip_nat_manip_type maniptype, ++ const union ip_conntrack_manip_proto *min, ++ const union ip_conntrack_manip_proto *max) ++{ ++ u_int32_t key; ++ ++ if (maniptype == IP_NAT_MANIP_SRC) ++ key = tuple->src.u.gre.key; ++ else ++ key = tuple->dst.u.gre.key; ++ ++ return ntohl(key) >= ntohl(min->gre.key) ++ && ntohl(key) <= ntohl(max->gre.key); ++} ++ ++/* generate unique tuple ... */ ++static int ++gre_unique_tuple(struct ip_conntrack_tuple *tuple, ++ const struct ip_nat_range *range, ++ enum ip_nat_manip_type maniptype, ++ const struct ip_conntrack *conntrack) ++{ ++ u_int32_t min, i, range_size; ++ u_int32_t key = 0, *keyptr; ++ ++ if (maniptype == IP_NAT_MANIP_SRC) ++ keyptr = &tuple->src.u.gre.key; ++ else ++ keyptr = &tuple->dst.u.gre.key; ++ ++ if (!(range->flags & IP_NAT_RANGE_PROTO_SPECIFIED)) { ++ ++ switch (tuple->dst.u.gre.version) { ++ case 0: ++ DEBUGP("NATing GRE version 0 (ct=%p)\n", ++ conntrack); ++ min = 1; ++ range_size = 0xffffffff; ++ break; ++ case GRE_VERSION_PPTP: ++ DEBUGP("%p: NATing GRE PPTP\n", ++ conntrack); ++ min = 1; ++ range_size = 0xffff; ++ break; ++ default: ++ printk(KERN_WARNING "nat_gre: unknown GRE version\n"); ++ return 0; ++ break; ++ } ++ ++ } else { ++ min = ntohl(range->min.gre.key); ++ range_size = ntohl(range->max.gre.key) - min + 1; ++ } ++ ++ DEBUGP("min = %u, range_size = %u\n", min, range_size); ++ ++ for (i = 0; i < range_size; i++, key++) { ++ *keyptr = htonl(min + key % range_size); ++ if (!ip_nat_used_tuple(tuple, conntrack)) ++ return 1; ++ } ++ ++ DEBUGP("%p: no NAT mapping\n", conntrack); ++ ++ return 0; ++} ++ ++/* manipulate a GRE packet according to maniptype */ ++static void ++gre_manip_pkt(struct iphdr *iph, size_t len, ++ const struct ip_conntrack_manip *manip, ++ enum ip_nat_manip_type maniptype) ++{ ++ struct gre_hdr *greh = (struct gre_hdr *)((u_int32_t *)iph+iph->ihl); ++ struct gre_hdr_pptp *pgreh = (struct gre_hdr_pptp *) greh; ++ ++ /* we only have destination manip of a packet, since 'source key' ++ * is not present in the packet itself */ ++ if (maniptype == IP_NAT_MANIP_DST) { ++ /* key manipulation is always dest */ ++ switch (greh->version) { ++ case 0: ++ if (!greh->key) { ++ DEBUGP("can't nat GRE w/o key\n"); ++ break; ++ } ++ if (greh->csum) { ++ /* FIXME: Never tested this code... */ ++ *(gre_csum(greh)) = ++ ip_nat_cheat_check(~*(gre_key(greh)), ++ manip->u.gre.key, ++ *(gre_csum(greh))); ++ } ++ *(gre_key(greh)) = manip->u.gre.key; ++ break; ++ case GRE_VERSION_PPTP: ++ DEBUGP("call_id -> 0x%04x\n", ++ ntohl(manip->u.gre.key)); ++ pgreh->call_id = htons(ntohl(manip->u.gre.key)); ++ break; ++ default: ++ DEBUGP("can't nat unknown GRE version\n"); ++ break; ++ } ++ } ++} ++ ++/* print out a nat tuple */ ++static unsigned int ++gre_print(char *buffer, ++ const struct ip_conntrack_tuple *match, ++ const struct ip_conntrack_tuple *mask) ++{ ++ unsigned int len = 0; ++ ++ if (mask->dst.u.gre.version) ++ len += sprintf(buffer + len, "version=%d ", ++ ntohs(match->dst.u.gre.version)); ++ ++ if (mask->dst.u.gre.protocol) ++ len += sprintf(buffer + len, "protocol=0x%x ", ++ ntohs(match->dst.u.gre.protocol)); ++ ++ if (mask->src.u.gre.key) ++ len += sprintf(buffer + len, "srckey=0x%x ", ++ ntohl(match->src.u.gre.key)); ++ ++ if (mask->dst.u.gre.key) ++ len += sprintf(buffer + len, "dstkey=0x%x ", ++ ntohl(match->src.u.gre.key)); ++ ++ return len; ++} ++ ++/* print a range of keys */ ++static unsigned int ++gre_print_range(char *buffer, const struct ip_nat_range *range) ++{ ++ if (range->min.gre.key != 0 ++ || range->max.gre.key != 0xFFFF) { ++ if (range->min.gre.key == range->max.gre.key) ++ return sprintf(buffer, "key 0x%x ", ++ ntohl(range->min.gre.key)); ++ else ++ return sprintf(buffer, "keys 0x%u-0x%u ", ++ ntohl(range->min.gre.key), ++ ntohl(range->max.gre.key)); ++ } else ++ return 0; ++} ++ ++/* nat helper struct */ ++static struct ip_nat_protocol gre = ++ { { NULL, NULL }, "GRE", IPPROTO_GRE, ++ gre_manip_pkt, ++ gre_in_range, ++ gre_unique_tuple, ++ gre_print, ++ gre_print_range ++ }; ++ ++static int __init init(void) ++{ ++ if (ip_nat_protocol_register(&gre)) ++ return -EIO; ++ ++ return 0; ++} ++ ++static void __exit fini(void) ++{ ++ ip_nat_protocol_unregister(&gre); ++} ++ ++module_init(init); ++module_exit(fini); diff --git a/packages/linux/linux-mtx-2-2.4.27/defconfig-mtx-2 b/packages/linux/linux-mtx-2-2.4.27/defconfig-mtx-2 index fb8c033e09..55b05b57a2 100644 --- a/packages/linux/linux-mtx-2-2.4.27/defconfig-mtx-2 +++ b/packages/linux/linux-mtx-2-2.4.27/defconfig-mtx-2 @@ -311,13 +311,15 @@ CONFIG_SYN_COOKIES=y # # IP: Netfilter Configuration # -CONFIG_IP_NF_CONNTRACK=y +CONFIG_IP_NF_CONNTRACK=m CONFIG_IP_NF_FTP=m +CONFIG_IP_NF_CT_PROTO_GRE=m +CONFIG_IP_NF_PPTP=m CONFIG_IP_NF_AMANDA=m CONFIG_IP_NF_TFTP=m CONFIG_IP_NF_IRC=m CONFIG_IP_NF_QUEUE=m -CONFIG_IP_NF_IPTABLES=y +CONFIG_IP_NF_IPTABLES=m CONFIG_IP_NF_MATCH_LIMIT=m CONFIG_IP_NF_MATCH_MAC=m CONFIG_IP_NF_MATCH_PKTTYPE=m @@ -332,29 +334,31 @@ CONFIG_IP_NF_MATCH_LENGTH=m CONFIG_IP_NF_MATCH_TTL=m CONFIG_IP_NF_MATCH_TCPMSS=m CONFIG_IP_NF_MATCH_HELPER=m -CONFIG_IP_NF_MATCH_STATE=y -CONFIG_IP_NF_MATCH_CONNTRACK=y +CONFIG_IP_NF_MATCH_STATE=m +CONFIG_IP_NF_MATCH_CONNTRACK=m CONFIG_IP_NF_MATCH_UNCLEAN=m CONFIG_IP_NF_MATCH_OWNER=m -CONFIG_IP_NF_FILTER=y -CONFIG_IP_NF_TARGET_REJECT=y +CONFIG_IP_NF_FILTER=m +CONFIG_IP_NF_TARGET_REJECT=m CONFIG_IP_NF_TARGET_MIRROR=m -CONFIG_IP_NF_NAT=y +CONFIG_IP_NF_NAT=m CONFIG_IP_NF_NAT_NEEDED=y -CONFIG_IP_NF_TARGET_MASQUERADE=y -CONFIG_IP_NF_TARGET_REDIRECT=y +CONFIG_IP_NF_TARGET_MASQUERADE=m +CONFIG_IP_NF_TARGET_REDIRECT=m CONFIG_IP_NF_NAT_AMANDA=m # CONFIG_IP_NF_NAT_LOCAL is not set +CONFIG_IP_NF_NAT_PPTP=m +CONFIG_IP_NF_NAT_PROTO_GRE=m CONFIG_IP_NF_NAT_SNMP_BASIC=m CONFIG_IP_NF_NAT_IRC=m CONFIG_IP_NF_NAT_FTP=m CONFIG_IP_NF_NAT_TFTP=m -CONFIG_IP_NF_MANGLE=y +CONFIG_IP_NF_MANGLE=m CONFIG_IP_NF_TARGET_TOS=m CONFIG_IP_NF_TARGET_ECN=m CONFIG_IP_NF_TARGET_DSCP=m -CONFIG_IP_NF_TARGET_MARK=y -CONFIG_IP_NF_TARGET_LOG=y +CONFIG_IP_NF_TARGET_MARK=m +CONFIG_IP_NF_TARGET_LOG=m CONFIG_IP_NF_TARGET_ULOG=m CONFIG_IP_NF_TARGET_TCPMSS=m CONFIG_IP_NF_ARPTABLES=m @@ -1212,67 +1216,124 @@ CONFIG_USB_LCD=m # CONFIG_USB_GADGET is not set # -# USB clients (devices, not hosts) -# -CONFIG_USBD=m -# CONFIG_USBD_HIGH_SPEED is not set -# CONFIG_USBD_NO_SERIAL_NUMBER is not set -CONFIG_USBD_SERIAL_NUMBER_STR="" -CONFIG_USBD_MAXPOWER=0 -CONFIG_USBD_PROCFS=y -CONFIG_USBD_PROCFSM=m - -# -# Network Function -# -CONFIG_USBD_NETWORK=m -CONFIG_USBD_NETWORK_VENDORID=12b9 -CONFIG_USBD_NETWORK_PRODUCTID=f001 -CONFIG_USBD_NETWORK_BCDDEVICE=0100 -CONFIG_USBD_NETWORK_MANUFACTURER="Belcarra" -## CONFIG_USBD_NETWORK_PRODUCT_NAME="Belcarra BLAN Device" -## CONFIG_USBD_NETWORK_BLAN=y -## CONFIG_USBD_NETWORK_BLAN_DESC="BLAN Net Cfg" -## CONFIG_USBD_NETWORK_BLAN_INTF="Comm/Data Intf" -# CONFIG_USBD_NETWORK_BLAN_CRC is not set -# CONFIG_USBD_NETWORK_BLAN_DO_NOT_SETTIME is not set -# CONFIG_USBD_NETWORK_BLAN_HOSTNAME is not set -# CONFIG_USBD_NETWORK_BLAN_NOBRIDGE is not set -CONFIG_USBD_NETWORK_SAFE=y -CONFIG_USBD_NETWORK_SAFE_NOBRIDGE=y -# CONFIG_USBD_NETWORK_CDC is not set -# CONFIG_USBD_NETWORK_BASIC is not set -# CONFIG_USBD_NETWORK_BASIC2 is not set -# CONFIG_USBD_NETWORK_START_SINGLE is not set -# CONFIG_USBD_NETWORK_EP0TEST is not set - -# -# CDC ACM Function -# -CONFIG_USBD_ACM=m -CONFIG_USBD_ACM_VENDORID=12b9 -CONFIG_USBD_ACM_PRODUCTID=f002 -CONFIG_USBD_ACM_BCDDEVICE=0100 -CONFIG_USBD_ACM_MANUFACTURER="Belcarra" -CONFIG_USBD_ACM_PRODUCT_NAME="Belcarra ACM Device" -CONFIG_USBD_ACM_DESC="Acm Cfg" -CONFIG_USBD_ACM_COMM_INTF="Comm Intf" -CONFIG_USBD_ACM_DATA_INTF="Data Intf" -# CONFIG_USBD_ACM_TRACE is not set - -# -# Random Mouse Function -# -# CONFIG_USBD_MOUSE is not set - -# -# AMD AU1X000 Bus Interface -# -CONFIG_USBD_AU1X00_BUS=m -CONFIG_USBD_AU1X00_SCLOCK=400 -CONFIG_AU1000_USB_DEVICE=y -CONFIG_AU1X00_USB_DEVICE=y -# CONFIG_USBD_BI_REGISTER_TRACE is not set +# On-The-Go and USB Peripheral Support +# +CONFIG_OTG=m + +# +# Select Hardware +# + +# +# DB1550 Development Board +# +CONFIG_OTG_DB1550=m +CONFIG_OTG_AU1X00_SCLOCK=400 +# CONFIG_AU1000_USB_DEVICE is not set +# CONFIG_AU1X00_USB_DEVICE is not set +CONFIG_OTG_PLATFORM_USBD=y +CONFIG_OTG_AU1550=m +CONFIG_OTG_DB1550_J14=y +# CONFIG_OTG_DB1550_J15 is not set +# CONFIG_OTG_PLATFORM_OTG is not set +CONFIG_OTG_PLATFORM_USBD=y +# CONFIG_OTG_MAX3353E is not set +CONFIG_OTG_AU1550_DB1550_TR=m + +# +# AMD AU1X00 Bus Interface +# +# CONFIG_OTG_AU1X00 is not set + +# +# General Support Options +# +# CONFIG_OTG_HIGH_SPEED is not set +CONFIG_OTG_TRACE=y +CONFIG_OTG_PROCFSM=m +# CONFIG_OTG_NOC99 is not set + +# +# Targeted Peripherals (USB Peripheral Function Drivers) +# + +# +# USB Peripheral Function Driver - CDC ACM +# +CONFIG_OTG_ACM=m +CONFIG_OTG_ACM_VENDORID=15ec +CONFIG_OTG_ACM_PRODUCTID=f001 +CONFIG_OTG_ACM_BCDDEVICE=0100 +CONFIG_OTG_ACM_MANUFACTURER="Belcarra" +CONFIG_OTG_ACM_PRODUCT_NAME="Belcarra ACM Device" +CONFIG_OTG_ACM_DESC="Acm Cfg" +CONFIG_OTG_ACM_COMM_INTF="Comm Intf" +CONFIG_OTG_ACM_DATA_INTF="Data Intf" + +# +# USB Peripheral Function Driver - Random Mouse +# +# CONFIG_OTG_MOUSE is not set + +# +# USB Peripheral Function Driver - Network +# +CONFIG_OTG_NETWORK=m +CONFIG_OTG_NETWORK_VENDORID=15ec +CONFIG_OTG_NETWORK_PRODUCTID=f001 +CONFIG_OTG_NETWORK_BCDDEVICE=0100 +CONFIG_OTG_NETWORK_MANUFACTURER="Belcarra" +CONFIG_OTG_NETWORK_PRODUCT_NAME="Belcarra BLAN Device" +CONFIG_OTG_NETWORK_BLAN=y +# CONFIG_OTG_NETWORK_SAFE is not set +# CONFIG_OTG_NETWORK_CDC is not set +# CONFIG_OTG_NETWORK_BASIC is not set +# CONFIG_OTG_NETWORK_BASIC2 is not set +# CONFIG_OTG_NETWORK_EEM is not set + +# +# MBLM-BLAN Networking Configuration +# +CONFIG_OTG_NETWORK_BLAN_DESC="BLAN Net Cfg" +CONFIG_OTG_NETWORK_BLAN_INTF="Comm/Data Intf" +CONFIG_OTG_NETWORK_BLAN_CRC=y +# CONFIG_OTG_NETWORK_BLAN_PADBEFORE is not set +# CONFIG_OTG_NETWORK_BLAN_PADAFTER is not set +# CONFIG_OTG_NETWORK_BLAN_FERMAT is not set +# CONFIG_OTG_NETWORK_BLAN_DO_NOT_SETTIME is not set +# CONFIG_OTG_NETWORK_BLAN_HOSTNAME is not set +CONFIG_OTG_NETWORK_BLAN_NONBRIDGED=y +# CONFIG_OTG_NETWORK_BLAN_DATA_NOTIFY_OK is not set +CONFIG_OTG_NETWORK_BLAN_PADBYTES=8 +CONFIG_OTG_NETWORK_BLAN_INTERVAL=4 +# CONFIG_OTG_NETWORK_BLAN_IPADDR is not set +CONFIG_OTG_NETWORK_BLAN_AUTO_CONFIG=y +# CONFIG_OTG_NETWORK_START_SINGLE is not set +# CONFIG_OTG_NETWORK_EP0TEST is not set +# CONFIG_OTG_NETWORK_HOTPLUG is not set + +# +# Mass Storage Function +# +# CONFIG_OTG_MSC is not set + +# +# Traditional Device Options +# +CONFIG_OTG_FW_MN=y +CONFIG_OTG_TR_AUTO=y + +# +# Host configuration (OTG minihost core and HCD) +# + +# +# OTG Host Controller Driver (HCD) +# +# CONFIG_OTG_HCD is not set +CONFIG_OTG_ROOTHUB_VENDORID=15ec +CONFIG_OTG_ROOTHUB_PRODUCTID=f010 +CONFIG_OTG_ROOTHUB_BCDDEVICE=0100 # # Bluetooth support |