diff options
Diffstat (limited to 'packages/linux')
18 files changed, 118536 insertions, 395 deletions
diff --git a/packages/linux/linux-mtx-1-2.4.27/42-usb-ohci-fixes.patch b/packages/linux/linux-mtx-1-2.4.27/42-usb-ohci-fixes.patch index 4c3058ce00..aa5a0d45f7 100644 --- a/packages/linux/linux-mtx-1-2.4.27/42-usb-ohci-fixes.patch +++ b/packages/linux/linux-mtx-1-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/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 |