All of lore.kernel.org
 help / color / mirror / Atom feed
* [U-Boot] [PATCH 0/2] usb:gadget: Linux USB Gadget framework
@ 2011-07-05 12:53 Lukasz Majewski
  2011-07-05 12:53 ` [U-Boot] [PATCH 1/2] " Lukasz Majewski
                   ` (4 more replies)
  0 siblings, 5 replies; 9+ messages in thread
From: Lukasz Majewski @ 2011-07-05 12:53 UTC (permalink / raw)
  To: u-boot

Included commits provide Linux USB gadget support for U-boot. 
The USB Gadget infrastructure is running on top of the Samsung's
UDC OTG controller. The code has been tested on the GONI (S5PC110)
reference target.

Files:
drivers/usb/gadget/file_storage.c
drivers/usb/gadget/storage_common.c

Aren't passing the checkpatch.pl script check, however they were 
taken from Linux kernel. On purpose this code hasn't been corrected, to
facilitate further code porting from Linux to U-boot (or in opposite 
direction)

v1:
- Linux USB Gadget support for Samsung targets (tested on GONI)
- Simple USB Mass Storage (UMS) Gadget implementation to proof the
  concept of running USB Gadgets in U-boot.
- The new ums command


It is important to emphase, that now USB Gadget framework can handle one 
gadget. 


Lukasz Majewski (2):
  usb:gadget: Linux USB Gadget framework
  usb:gadget: USB Mass Storage Gadget support.

 arch/arm/include/asm/arch-s5pc1xx/hs_otg.h   |   32 +
 arch/arm/include/asm/arch-s5pc1xx/regs-otg.h |  309 +++
 board/samsung/goni/goni.c                    |  139 +
 common/Makefile                              |    1 +
 common/cmd_usb_mass_storage.c                |   67 +
 drivers/usb/gadget/Makefile                  |   11 +-
 drivers/usb/gadget/file_storage.c            | 3533 ++++++++++++++++++++++++++
 drivers/usb/gadget/s3c_udc_otg.c             |  878 +++++++
 drivers/usb/gadget/s3c_udc_otg_xfer_dma.c    | 1406 ++++++++++
 drivers/usb/gadget/storage_common.c          |  762 ++++++
 include/configs/s5p_goni.h                   |   13 +
 include/usb/lin_gadget_compat.h              |   69 +
 include/usb/s3c_udc.h                        |  160 ++
 include/usb_mass_storage.h                   |   36 +
 14 files changed, 7413 insertions(+), 3 deletions(-)
 create mode 100644 arch/arm/include/asm/arch-s5pc1xx/hs_otg.h
 create mode 100644 arch/arm/include/asm/arch-s5pc1xx/regs-otg.h
 create mode 100644 common/cmd_usb_mass_storage.c
 create mode 100644 drivers/usb/gadget/file_storage.c
 create mode 100644 drivers/usb/gadget/s3c_udc_otg.c
 create mode 100644 drivers/usb/gadget/s3c_udc_otg_xfer_dma.c
 create mode 100644 drivers/usb/gadget/storage_common.c
 create mode 100644 include/usb/lin_gadget_compat.h
 create mode 100644 include/usb/s3c_udc.h
 create mode 100644 include/usb_mass_storage.h

-- 
1.7.2.3

^ permalink raw reply	[flat|nested] 9+ messages in thread

* [U-Boot] [PATCH 1/2] usb:gadget: Linux USB Gadget framework
  2011-07-05 12:53 [U-Boot] [PATCH 0/2] usb:gadget: Linux USB Gadget framework Lukasz Majewski
@ 2011-07-05 12:53 ` Lukasz Majewski
  2011-07-05 12:53 ` [U-Boot] [PATCH 2/2] usb:gadget: USB Mass Storage Gadget support Lukasz Majewski
                   ` (3 subsequent siblings)
  4 siblings, 0 replies; 9+ messages in thread
From: Lukasz Majewski @ 2011-07-05 12:53 UTC (permalink / raw)
  To: u-boot

Support for Linux USB Gadget framework. Due to this commit
Linux kernel's and U-boot's implementations are now alike.

This should facilitate using USB gadgets from Linux and U-boot
interchangeably.

Signed-off-by: Lukasz Majewski <l.majewski@samsung.com>
---
 arch/arm/include/asm/arch-s5pc1xx/hs_otg.h   |   32 +
 arch/arm/include/asm/arch-s5pc1xx/regs-otg.h |  309 ++++++
 board/samsung/goni/goni.c                    |   71 ++
 drivers/usb/gadget/Makefile                  |    6 +-
 drivers/usb/gadget/s3c_udc_otg.c             |  878 ++++++++++++++++
 drivers/usb/gadget/s3c_udc_otg_xfer_dma.c    | 1406 ++++++++++++++++++++++++++
 include/configs/s5p_goni.h                   |    3 +
 include/usb/lin_gadget_compat.h              |   69 ++
 include/usb/s3c_udc.h                        |  160 +++
 9 files changed, 2931 insertions(+), 3 deletions(-)
 create mode 100644 arch/arm/include/asm/arch-s5pc1xx/hs_otg.h
 create mode 100644 arch/arm/include/asm/arch-s5pc1xx/regs-otg.h
 create mode 100644 drivers/usb/gadget/s3c_udc_otg.c
 create mode 100644 drivers/usb/gadget/s3c_udc_otg_xfer_dma.c
 create mode 100644 include/usb/lin_gadget_compat.h
 create mode 100644 include/usb/s3c_udc.h

diff --git a/arch/arm/include/asm/arch-s5pc1xx/hs_otg.h b/arch/arm/include/asm/arch-s5pc1xx/hs_otg.h
new file mode 100644
index 0000000..d68db04
--- /dev/null
+++ b/arch/arm/include/asm/arch-s5pc1xx/hs_otg.h
@@ -0,0 +1,32 @@
+/*
+ * (C) Copyright 2009 SAMSUNG Electronics
+ * Minkyu Kang <mk7.kang@samsung.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., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+ *
+ */
+
+#ifndef __ASM_ARCH_HSOTG_H_
+#define __ASM_ARCH_HSOTG_H_
+
+#ifndef __ASSEMBLY__
+struct s3c_plat_otg_data {
+	int		(*phy_control)(int on);
+	unsigned int	regs_phy;
+	unsigned int	regs_otg;
+};
+
+#endif	/* __ASSEMBLY__ */
+#endif
diff --git a/arch/arm/include/asm/arch-s5pc1xx/regs-otg.h b/arch/arm/include/asm/arch-s5pc1xx/regs-otg.h
new file mode 100644
index 0000000..eed1393
--- /dev/null
+++ b/arch/arm/include/asm/arch-s5pc1xx/regs-otg.h
@@ -0,0 +1,309 @@
+/* linux/arch/arm/plat-s3c/include/plat/regs-otg.h
+ *
+ * Copyright (C) 2004 Herbert Poetzl <herbert@13thfloor.at>
+ *
+ * This include file 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.
+ */
+
+#ifndef __ASM_ARCH_REGS_USB_OTG_HS_H
+#define __ASM_ARCH_REGS_USB_OTG_HS_H
+
+/*
+ * USB2.0 HS OTG
+ */
+#define S5PC100_OTG_BASE	0xED200000
+#define S5PC100_PHY_BASE	0xED300000
+
+#define S5PC110_OTG_BASE	0xEC000000
+#define S5PC110_PHY_BASE	0xEC100000
+
+#define S5P_USB_PHY_CONTROL     0xE010E80C
+/* USB2.0 OTG Controller register */
+#define S3C_USBOTG_PHYREG(x)		(((u32)regs_phy) + (x))
+#define S3C_USBOTG_PHYPWR		S3C_USBOTG_PHYREG(0x0)
+#define S3C_USBOTG_PHYCLK		S3C_USBOTG_PHYREG(0x4)
+#define S3C_USBOTG_RSTCON		S3C_USBOTG_PHYREG(0x8)
+
+
+/* USB2.0 OTG Controller register */
+#define S3C_USBOTGREG(x)	(((u32)regs_otg) + (x))
+/*========================================================================== */
+/* Core Global Registers */
+/* OTG Control & Status */
+#define S3C_UDC_OTG_GOTGCTL	S3C_USBOTGREG(0x000)
+/* OTG Interrupt */
+#define S3C_UDC_OTG_GOTGINT	S3C_USBOTGREG(0x004)
+/* Core AHB Configuration */
+#define S3C_UDC_OTG_GAHBCFG	S3C_USBOTGREG(0x008)
+/* Core USB Configuration */
+#define S3C_UDC_OTG_GUSBCFG	S3C_USBOTGREG(0x00C)
+/* Core Reset */
+#define S3C_UDC_OTG_GRSTCTL	S3C_USBOTGREG(0x010)
+/* Core Interrupt */
+#define S3C_UDC_OTG_GINTSTS	S3C_USBOTGREG(0x014)
+/* Core Interrupt Mask */
+#define S3C_UDC_OTG_GINTMSK	S3C_USBOTGREG(0x018)
+/* Receive Status Debug Read/Status Read */
+#define S3C_UDC_OTG_GRXSTSR	S3C_USBOTGREG(0x01C)
+/* Receive Status Debug Pop/Status Pop */
+#define S3C_UDC_OTG_GRXSTSP	S3C_USBOTGREG(0x020)
+/* Receive FIFO Size */
+#define S3C_UDC_OTG_GRXFSIZ	S3C_USBOTGREG(0x024)
+/* Non-Periodic Transmit FIFO Size */
+#define S3C_UDC_OTG_GNPTXFSIZ	S3C_USBOTGREG(0x028)
+/* Non-Periodic Transmit FIFO/Queue Status */
+#define S3C_UDC_OTG_GNPTXSTS	S3C_USBOTGREG(0x02C)
+/* Host Periodic Transmit FIFO Size */
+#define S3C_UDC_OTG_HPTXFSIZ	S3C_USBOTGREG(0x100)
+/* Device IN EP Transmit FIFO Size Register */
+#define S3C_UDC_OTG_DIEPTXF(n)	S3C_USBOTGREG(0x104 + ((n)-1)*0x4)
+
+/*=========================================================================== */
+/* Host Mode Registers */
+/*------------------------------------------------ */
+/* Host Global Registers */
+/* Host Configuration */
+#define S3C_UDC_OTG_HCFG	S3C_USBOTGREG(0x400)
+/* Host Frame Interval */
+#define S3C_UDC_OTG_HFIR	S3C_USBOTGREG(0x404)
+/* Host Frame Number/Frame Time Remaining */
+#define S3C_UDC_OTG_HFNUM	S3C_USBOTGREG(0x408)
+/* Host Periodic Transmit FIFO/Queue Status */
+#define S3C_UDC_OTG_HPTXSTS	S3C_USBOTGREG(0x410)
+/* Host All Channels Interrupt */
+#define S3C_UDC_OTG_HAINT	S3C_USBOTGREG(0x414)
+/* Host All Channels Interrupt Mask */
+#define S3C_UDC_OTG_HAINTMSK	S3C_USBOTGREG(0x418)
+
+/*------------------------------------------------ */
+/* Host Port Control & Status Registers */
+/* Host Port Control & Status */
+#define S3C_UDC_OTG_HPRT	S3C_USBOTGREG(0x440)
+/*------------------------------------------------ */
+/* Host Channel-Specific Registers */
+/* Host Channel-0 Characteristics */
+#define S3C_UDC_OTG_HCCHAR0	S3C_USBOTGREG(0x500)
+/* Host Channel-0 Split Control */
+#define S3C_UDC_OTG_HCSPLT0	S3C_USBOTGREG(0x504)
+/* Host Channel-0 Interrupt */
+#define S3C_UDC_OTG_HCINT0	S3C_USBOTGREG(0x508)
+/* Host Channel-0 Interrupt Mask */
+#define S3C_UDC_OTG_HCINTMSK0	S3C_USBOTGREG(0x50C)
+/* Host Channel-0 Transfer Size */
+#define S3C_UDC_OTG_HCTSIZ0	S3C_USBOTGREG(0x510)
+/* Host Channel-0 DMA Address */
+#define S3C_UDC_OTG_HCDMA0	S3C_USBOTGREG(0x514)
+/*=========================================================================== */
+/* Device Mode Registers */
+/*------------------------------------------------ */
+/* Device Global Registers */
+/* Device Configuration */
+#define S3C_UDC_OTG_DCFG	S3C_USBOTGREG(0x800)
+#define S3C_UDC_OTG_DCTL	S3C_USBOTGREG(0x804)	/* Device Control */
+#define S3C_UDC_OTG_DSTS	S3C_USBOTGREG(0x808)	/* Device Status */
+/* Device IN Endpoint Common Interrupt Mask */
+#define S3C_UDC_OTG_DIEPMSK	S3C_USBOTGREG(0x810)
+/* Device OUT Endpoint Common Interrupt Mask */
+#define S3C_UDC_OTG_DOEPMSK	S3C_USBOTGREG(0x814)
+/* Device All Endpoints Interrupt */
+#define S3C_UDC_OTG_DAINT	S3C_USBOTGREG(0x818)
+/* Device All Endpoints Interrupt Mask */
+#define S3C_UDC_OTG_DAINTMSK	S3C_USBOTGREG(0x81C)
+/* Device IN Token Sequence Learning Queue Read 1 */
+#define S3C_UDC_OTG_DTKNQR1	S3C_USBOTGREG(0x820)
+/* Device IN Token Sequence Learning Queue Read 2 */
+#define S3C_UDC_OTG_DTKNQR2	S3C_USBOTGREG(0x824)
+/* Device VBUS Discharge Time */
+#define S3C_UDC_OTG_DVBUSDIS	S3C_USBOTGREG(0x828)
+/* Device VBUS Pulsing Time */
+#define S3C_UDC_OTG_DVBUSPULSE	S3C_USBOTGREG(0x82C)
+/* Device IN Token Sequence Learning Queue Read 3 */
+#define S3C_UDC_OTG_DTKNQR3	S3C_USBOTGREG(0x830)
+/* Device IN Token Sequence Learning Queue Read 4 */
+#define S3C_UDC_OTG_DTKNQR4	S3C_USBOTGREG(0x834)
+
+
+/*------------------------------------------------ */
+/* Device Logical IN Endpoint-Specific Registers */
+/* Device IN Endpoint n Control */
+#define S3C_UDC_OTG_DIEPCTL(n)	S3C_USBOTGREG(0x900 + n*0x20)
+/* Device IN Endpoint n Interrupt */
+#define S3C_UDC_OTG_DIEPINT(n)	S3C_USBOTGREG(0x908 + n*0x20)
+/* Device IN Endpoint n Transfer Size */
+#define S3C_UDC_OTG_DIEPTSIZ(n) S3C_USBOTGREG(0x910 + n*0x20)
+/* Device IN Endpoint n DMA Address */
+#define S3C_UDC_OTG_DIEPDMA(n)	S3C_USBOTGREG(0x914 + n*0x20)
+
+/*------------------------------------------------ */
+/* Device Logical OUT Endpoint-Specific Registers */
+/* Device OUT Endpoint n Control */
+#define S3C_UDC_OTG_DOEPCTL(n)		S3C_USBOTGREG(0xB00 + n*0x20)
+/* Device OUT Endpoint n Interrupt */
+#define S3C_UDC_OTG_DOEPINT(n)		S3C_USBOTGREG(0xB08 + n*0x20)
+/* Device OUT Endpoint n Transfer Size */
+#define S3C_UDC_OTG_DOEPTSIZ(n)	S3C_USBOTGREG(0xB10 + n*0x20)
+/* Device OUT Endpoint n DMA Address */
+#define S3C_UDC_OTG_DOEPDMA(n)		S3C_USBOTGREG(0xB14 + n*0x20)
+
+/*------------------------------------------------ */
+/* Endpoint FIFO address */
+#define S3C_UDC_OTG_EP0_FIFO		S3C_USBOTGREG(0x1000)
+#define S3C_UDC_OTG_EP1_FIFO		S3C_USBOTGREG(0x2000)
+#define S3C_UDC_OTG_EP2_FIFO		S3C_USBOTGREG(0x3000)
+#define S3C_UDC_OTG_EP3_FIFO		S3C_USBOTGREG(0x4000)
+#define S3C_UDC_OTG_EP4_FIFO		S3C_USBOTGREG(0x5000)
+#define S3C_UDC_OTG_EP5_FIFO		S3C_USBOTGREG(0x6000)
+#define S3C_UDC_OTG_EP6_FIFO		S3C_USBOTGREG(0x7000)
+#define S3C_UDC_OTG_EP7_FIFO		S3C_USBOTGREG(0x8000)
+#define S3C_UDC_OTG_EP8_FIFO		S3C_USBOTGREG(0x9000)
+#define S3C_UDC_OTG_EP9_FIFO		S3C_USBOTGREG(0xA000)
+#define S3C_UDC_OTG_EP10_FIFO		S3C_USBOTGREG(0xB000)
+#define S3C_UDC_OTG_EP11_FIFO		S3C_USBOTGREG(0xC000)
+#define S3C_UDC_OTG_EP12_FIFO		S3C_USBOTGREG(0xD000)
+#define S3C_UDC_OTG_EP13_FIFO		S3C_USBOTGREG(0xE000)
+#define S3C_UDC_OTG_EP14_FIFO		S3C_USBOTGREG(0xF000)
+#define S3C_UDC_OTG_EP15_FIFO		S3C_USBOTGREG(0x10000)
+
+/*===================================================================== */
+/*definitions related to CSR setting */
+
+/* S3C_UDC_OTG_GOTGCTL */
+#define B_SESSION_VALID			(0x1<<19)
+#define A_SESSION_VALID			(0x1<<18)
+
+/* S3C_UDC_OTG_GAHBCFG */
+#define PTXFE_HALF			(0<<8)
+#define PTXFE_ZERO			(1<<8)
+#define NPTXFE_HALF			(0<<7)
+#define NPTXFE_ZERO			(1<<7)
+#define MODE_SLAVE			(0<<5)
+#define MODE_DMA			(1<<5)
+#define BURST_SINGLE			(0<<1)
+#define BURST_INCR			(1<<1)
+#define BURST_INCR4			(3<<1)
+#define BURST_INCR8			(5<<1)
+#define BURST_INCR16			(7<<1)
+#define GBL_INT_UNMASK			(1<<0)
+#define GBL_INT_MASK			(0<<0)
+
+/* S3C_UDC_OTG_GRSTCTL */
+#define AHB_MASTER_IDLE			(1u<<31)
+#define CORE_SOFT_RESET			(0x1<<0)
+
+/* S3C_UDC_OTG_GINTSTS/S3C_UDC_OTG_GINTMSK core interrupt register */
+#define INT_RESUME			(1u<<31)
+#define INT_DISCONN			(0x1<<29)
+#define INT_CONN_ID_STS_CNG		(0x1<<28)
+#define INT_OUT_EP			(0x1<<19)
+#define INT_IN_EP			(0x1<<18)
+#define INT_ENUMDONE			(0x1<<13)
+#define INT_RESET			(0x1<<12)
+#define INT_SUSPEND			(0x1<<11)
+#define INT_EARLY_SUSPEND		(0x1<<10)
+#define INT_NP_TX_FIFO_EMPTY		(0x1<<5)
+#define INT_RX_FIFO_NOT_EMPTY		(0x1<<4)
+#define INT_SOF				(0x1<<3)
+#define INT_DEV_MODE			(0x0<<0)
+#define INT_HOST_MODE			(0x1<<1)
+#define	INT_GOUTNakEff			(0x01<<7)
+#define	INT_GINNakEff			(0x01<<6)
+
+#define FULL_SPEED_CONTROL_PKT_SIZE	8
+#define FULL_SPEED_BULK_PKT_SIZE	64
+
+#define HIGH_SPEED_CONTROL_PKT_SIZE	64
+#define HIGH_SPEED_BULK_PKT_SIZE	512
+
+#define RX_FIFO_SIZE			(1024*4)
+#define NPTX_FIFO_SIZE			(1024*4)
+#define PTX_FIFO_SIZE			(1536*1)
+
+#define DEPCTL_TXFNUM_0			(0x0<<22)
+#define DEPCTL_TXFNUM_1			(0x1<<22)
+#define DEPCTL_TXFNUM_2			(0x2<<22)
+#define DEPCTL_TXFNUM_3			(0x3<<22)
+#define DEPCTL_TXFNUM_4			(0x4<<22)
+
+/* Enumeration speed */
+#define USB_HIGH_30_60MHZ		(0x0<<1)
+#define USB_FULL_30_60MHZ		(0x1<<1)
+#define USB_LOW_6MHZ			(0x2<<1)
+#define USB_FULL_48MHZ			(0x3<<1)
+
+/* S3C_UDC_OTG_GRXSTSP STATUS */
+#define OUT_PKT_RECEIVED		(0x2<<17)
+#define OUT_TRANSFER_COMPLELTED		(0x3<<17)
+#define SETUP_TRANSACTION_COMPLETED	(0x4<<17)
+#define SETUP_PKT_RECEIVED		(0x6<<17)
+#define GLOBAL_OUT_NAK			(0x1<<17)
+
+/* S3C_UDC_OTG_DCTL device control register */
+#define NORMAL_OPERATION		(0x1<<0)
+#define SOFT_DISCONNECT			(0x1<<1)
+#define TEST_CONTROL_MASK		(0x7<<4)
+#define TEST_J_MODE			(0x1<<4)
+#define TEST_K_MODE			(0x2<<4)
+#define TEST_SE0_NAK_MODE		(0x3<<4)
+#define TEST_PACKET_MODE		(0x4<<4)
+#define TEST_FORCE_ENABLE_MODE		(0x5<<4)
+
+/* S3C_UDC_OTG_DAINT device all endpoint interrupt register */
+#define DAINT_OUT_BIT			(16)
+#define DAINT_MASK			(0xFFFF)
+
+/* S3C_UDC_OTG_DIEPCTL0/DOEPCTL0 device
+   control IN/OUT endpoint 0 control register */
+#define DEPCTL_EPENA			(0x1<<31)
+#define DEPCTL_EPDIS			(0x1<<30)
+#define DEPCTL_SETD1PID		(0x1<<29)
+#define DEPCTL_SETD0PID		(0x1<<28)
+#define DEPCTL_SNAK			(0x1<<27)
+#define DEPCTL_CNAK			(0x1<<26)
+#define DEPCTL_STALL			(0x1<<21)
+#define DEPCTL_TYPE_BIT		(18)
+#define DEPCTL_TYPE_MASK		(0x3<<18)
+#define DEPCTL_CTRL_TYPE		(0x0<<18)
+#define DEPCTL_ISO_TYPE		(0x1<<18)
+#define DEPCTL_BULK_TYPE		(0x2<<18)
+#define DEPCTL_INTR_TYPE		(0x3<<18)
+#define DEPCTL_USBACTEP		(0x1<<15)
+#define DEPCTL_NEXT_EP_BIT		(11)
+#define DEPCTL_MPS_BIT			(0)
+#define DEPCTL_MPS_MASK		(0x7FF)
+
+#define DEPCTL0_MPS_64			(0x0<<0)
+#define DEPCTL0_MPS_32			(0x1<<0)
+#define DEPCTL0_MPS_16			(0x2<<0)
+#define DEPCTL0_MPS_8			(0x3<<0)
+#define DEPCTL_MPS_BULK_512		(512<<0)
+#define DEPCTL_MPS_INT_MPS_16		(16<<0)
+
+#define DIEPCTL0_NEXT_EP_BIT		(11)
+
+
+/* S3C_UDC_OTG_DIEPMSK/DOEPMSK device IN/OUT endpoint
+   common interrupt mask register */
+/* S3C_UDC_OTG_DIEPINTn/DOEPINTn device IN/OUT endpoint interrupt register */
+#define BACK2BACK_SETUP_RECEIVED	(0x1<<6)
+#define INTKNEPMIS			(0x1<<5)
+#define INTKN_TXFEMP			(0x1<<4)
+#define NON_ISO_IN_EP_TIMEOUT		(0x1<<3)
+#define CTRL_OUT_EP_SETUP_PHASE_DONE	(0x1<<3)
+#define AHB_ERROR			(0x1<<2)
+#define EPDISBLD			(0x1<<1)
+#define TRANSFER_DONE			(0x1<<0)
+
+/*DIEPTSIZ0 / DOEPTSIZ0 */
+
+/* DEPTSIZ common bit */
+#define DEPTSIZ_PKT_CNT_BIT		(19)
+#define DEPTSIZ_XFER_SIZE_BIT		(0)
+
+#define	DEPTSIZ_SETUP_PKCNT_1		(1<<29)
+#define	DEPTSIZ_SETUP_PKCNT_2		(2<<29)
+#define	DEPTSIZ_SETUP_PKCNT_3		(3<<29)
+
+#endif
diff --git a/board/samsung/goni/goni.c b/board/samsung/goni/goni.c
index 8149ebf..b594bb2 100644
--- a/board/samsung/goni/goni.c
+++ b/board/samsung/goni/goni.c
@@ -27,6 +27,8 @@
 #include <i2c.h>
 #include <asm/arch/gpio.h>
 #include <asm/arch/mmc.h>
+#include <asm/arch/hs_otg.h>
+#include <asm/arch/regs-otg.h>
 
 DECLARE_GLOBAL_DATA_PTR;
 
@@ -127,3 +129,72 @@ void i2c_init_board(void)
 
 	i2c_gpio_init(i2c_gpio, I2C_NUM, I2C_PMIC);
 }
+
+#ifdef CONFIG_USB_GADGET
+
+static int max8998_probe(void)
+{
+	unsigned char addr = 0xCC >> 1;
+
+	i2c_set_bus_num(I2C_PMIC);
+
+	if (i2c_probe(addr)) {
+		puts("Can't found max8998\n");
+		return 1;
+	}
+
+	return 0;
+}
+
+#define MAX8998_REG_ONOFF1	0x11
+#define MAX8998_REG_ONOFF2	0x12
+#define MAX8998_LDO3		(1 << 2)
+#define MAX8998_LDO8		(1 << 5)
+
+enum { LDO_OFF, LDO_ON };
+
+inline void max8998_set_ldo(unsigned int reg, unsigned char ldo, int on)
+{
+	unsigned char addr;
+	unsigned char val[2];
+
+	addr = 0xCC >> 1;	/* max8998 */
+
+	i2c_read(addr, reg, 1, val, 1);
+	if (on)
+		val[0] |= ldo;
+	else
+		val[0] &= ~ldo;
+
+	i2c_write(addr, reg, 1, val, 1);
+	printf("MAX ONOFF1: val[0]:0x%x val[1]:0x%x\n", val[0], val[1]);
+}
+
+static int s5pc1xx_phy_control(int on)
+{
+	static int status;
+
+	if (max8998_probe())
+		return -1;
+
+	if (on && !status) {
+		max8998_set_ldo(MAX8998_REG_ONOFF1, MAX8998_LDO3, LDO_ON);
+		max8998_set_ldo(MAX8998_REG_ONOFF2, MAX8998_LDO8, LDO_ON);
+		status = 1;
+	} else if (!on && status) {
+		max8998_set_ldo(MAX8998_REG_ONOFF1, MAX8998_LDO3, LDO_OFF);
+		max8998_set_ldo(MAX8998_REG_ONOFF2, MAX8998_LDO8, LDO_OFF);
+		status = 0;
+	}
+	udelay(10000);
+
+	return 0;
+}
+
+
+struct s3c_plat_otg_data s5pc110_otg_data = {
+	.phy_control = s5pc1xx_phy_control,
+	.regs_phy = S5PC110_PHY_BASE,
+	.regs_otg = S5PC110_OTG_BASE,
+};
+#endif
diff --git a/drivers/usb/gadget/Makefile b/drivers/usb/gadget/Makefile
index 7d5b504..6717e4f 100644
--- a/drivers/usb/gadget/Makefile
+++ b/drivers/usb/gadget/Makefile
@@ -26,9 +26,9 @@ include $(TOPDIR)/config.mk
 LIB	:= $(obj)libusb_gadget.o
 
 # new USB gadget layer dependencies
-ifdef CONFIG_USB_ETHER
-COBJS-y += ether.o epautoconf.o config.o usbstring.o
-COBJS-$(CONFIG_USB_ETH_RNDIS) += rndis.o
+ifdef CONFIG_USB_GADGET
+COBJS-y += epautoconf.o config.o usbstring.o
+COBJS-$(CONFIG_USB_GADGET_S3C_UDC_OTG) += s3c_udc_otg.o
 else
 # Devices not related to the new gadget layer depend on CONFIG_USB_DEVICE
 ifdef CONFIG_USB_DEVICE
diff --git a/drivers/usb/gadget/s3c_udc_otg.c b/drivers/usb/gadget/s3c_udc_otg.c
new file mode 100644
index 0000000..66f7311
--- /dev/null
+++ b/drivers/usb/gadget/s3c_udc_otg.c
@@ -0,0 +1,878 @@
+/*
+ * drivers/usb/gadget/s3c_udc_otg.c
+ * Samsung S3C on-chip full/high speed USB OTG 2.0 device controllers
+ *
+ * Copyright (C) 2008 for Samsung Electronics
+ *
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+ *
+ */
+
+#include <common.h>
+#include <asm/errno.h>
+#include <linux/list.h>
+#include <malloc.h>
+
+#include <linux/usb/ch9.h>
+#include <linux/usb/gadget.h>
+
+#include <asm/byteorder.h>
+#include <asm/io.h>
+
+#include <asm/mach-types.h>
+#include <asm/arch/gpio.h>
+
+#include <asm/arch/regs-otg.h>
+#include <asm/arch/hs_otg.h>
+#include <asm/arch/power.h>
+
+#include <usb/lin_gadget_compat.h>
+
+/***********************************************************/
+
+#define OTG_DMA_MODE		1
+
+#undef DEBUG_S3C_UDC_SETUP
+#undef DEBUG_S3C_UDC_EP0
+#undef DEBUG_S3C_UDC_ISR
+#undef DEBUG_S3C_UDC_OUT_EP
+#undef DEBUG_S3C_UDC_IN_EP
+#undef DEBUG_S3C_UDC
+
+/* #define DEBUG_S3C_UDC_SETUP */
+/* #define DEBUG_S3C_UDC_EP0 */
+/* #define DEBUG_S3C_UDC_ISR */
+/* #define DEBUG_S3C_UDC_OUT_EP */
+/* #define DEBUG_S3C_UDC_IN_EP */
+/* #define DEBUG_S3C_UDC */
+
+#include <usb/s3c_udc.h>
+
+#define EP0_CON		0
+#define EP_MASK		0xF
+
+#if defined(DEBUG_S3C_UDC_SETUP) || defined(DEBUG_S3C_UDC_ISR)	  \
+	|| defined(DEBUG_S3C_UDC_OUT_EP)
+
+static char *state_names[] = {
+	"WAIT_FOR_SETUP",
+	"DATA_STATE_XMIT",
+	"DATA_STATE_NEED_ZLP",
+	"WAIT_FOR_OUT_STATUS",
+	"DATA_STATE_RECV",
+	"WAIT_FOR_COMPLETE",
+	"WAIT_FOR_OUT_COMPLETE",
+	"WAIT_FOR_IN_COMPLETE",
+	"WAIT_FOR_NULL_COMPLETE",
+};
+#endif
+
+#define	DRIVER_DESC	"S3C HS USB OTG Device Driver, (c) 2008-2009 Samsung Electronics"
+#define	DRIVER_VERSION		"15 March 2009"
+
+struct s3c_udc	*the_controller;
+
+static const char driver_name[] = "s3c-udc";
+static const char driver_desc[] = DRIVER_DESC;
+static const char ep0name[] = "ep0-control";
+
+/* Max packet size*/
+static unsigned int ep0_fifo_size = 64;
+static unsigned int ep_fifo_size =  512;
+static unsigned int ep_fifo_size2 = 1024;
+static int reset_available = 1;
+
+extern void otg_phy_init(void);
+extern void otg_phy_off(void);
+static struct usb_ctrlrequest *usb_ctrl;
+static dma_addr_t usb_ctrl_dma_addr;
+
+/*
+  Local declarations.
+*/
+static int s3c_ep_enable(struct usb_ep *ep,
+			 const struct usb_endpoint_descriptor *);
+static int s3c_ep_disable(struct usb_ep *ep);
+static struct usb_request *s3c_alloc_request(struct usb_ep *ep,
+					     gfp_t gfp_flags);
+static void s3c_free_request(struct usb_ep *ep, struct usb_request *);
+
+static int s3c_queue(struct usb_ep *ep, struct usb_request *, gfp_t gfp_flags);
+static int s3c_dequeue(struct usb_ep *ep, struct usb_request *);
+static int s3c_fifo_status(struct usb_ep *ep);
+static void s3c_fifo_flush(struct usb_ep *ep);
+static void s3c_ep0_read(struct s3c_udc *dev);
+static void s3c_ep0_kick(struct s3c_udc *dev, struct s3c_ep *ep);
+static void s3c_handle_ep0(struct s3c_udc *dev);
+static int s3c_ep0_write(struct s3c_udc *dev);
+static int write_fifo_ep0(struct s3c_ep *ep, struct s3c_request *req);
+static void done(struct s3c_ep *ep, struct s3c_request *req, int status);
+static void stop_activity(struct s3c_udc *dev,
+			  struct usb_gadget_driver *driver);
+static int udc_enable(struct s3c_udc *dev);
+static void udc_set_address(struct s3c_udc *dev, unsigned char address);
+static void reconfig_usbd(void);
+static void set_max_pktsize(struct s3c_udc *dev, enum usb_device_speed speed);
+static void nuke(struct s3c_ep *ep, int status);
+static int s3c_udc_set_halt(struct usb_ep *_ep, int value);
+static void s3c_udc_set_nak(struct s3c_ep *ep);
+
+static struct usb_ep_ops s3c_ep_ops = {
+	.enable = s3c_ep_enable,
+	.disable = s3c_ep_disable,
+
+	.alloc_request = s3c_alloc_request,
+	.free_request = s3c_free_request,
+
+	.queue = s3c_queue,
+	.dequeue = s3c_dequeue,
+
+	.set_halt = s3c_udc_set_halt,
+	.fifo_status = s3c_fifo_status,
+	.fifo_flush = s3c_fifo_flush,
+};
+
+#define create_proc_files() do {} while (0)
+#define remove_proc_files() do {} while (0)
+
+/***********************************************************/
+
+void __iomem		*regs_otg;
+void __iomem		*regs_phy;
+
+
+void otg_phy_init(void)
+{
+	the_controller->pdata->phy_control(1);
+
+	/*USB PHY0 Enable */
+	printf("USB PHY0 Enable\n");
+
+	/* Enable PHY */
+	writel(readl(S5P_USB_PHY_CONTROL)|(0x1<<0), S5P_USB_PHY_CONTROL);
+
+	writel((readl(S3C_USBOTG_PHYPWR)
+		&~(0x3<<3)&~(0x1<<0)), S3C_USBOTG_PHYPWR);
+
+	writel((readl(S3C_USBOTG_PHYCLK)
+		&~(0x5<<2))|(0x3<<0), S3C_USBOTG_PHYCLK); /* PLL 24Mhz */
+	writel((readl(S3C_USBOTG_RSTCON)
+		&~(0x3<<1))|(0x1<<0), S3C_USBOTG_RSTCON);
+	udelay(10);
+	writel(readl(S3C_USBOTG_RSTCON)
+	       &~(0x7<<0), S3C_USBOTG_RSTCON);
+	udelay(10);
+}
+
+void otg_phy_off(void)
+{
+	/* reset controller just in case */
+	writel(0x1, S3C_USBOTG_RSTCON);
+	udelay(20);
+	writel(0x0, S3C_USBOTG_RSTCON);
+	udelay(20);
+
+	writel(readl(S3C_USBOTG_PHYPWR)|(0x3<<3)|(0x1), S3C_USBOTG_PHYPWR);
+	writel(readl(S5P_USB_PHY_CONTROL)&~(1<<0), S5P_USB_PHY_CONTROL);
+
+	writel((readl(S3C_USBOTG_PHYCLK) & ~(0x5 << 2)), S3C_USBOTG_PHYCLK);
+
+	udelay(10000);
+
+	the_controller->pdata->phy_control(0);
+}
+
+/***********************************************************/
+
+#include "s3c_udc_otg_xfer_dma.c"
+
+/*
+ *	udc_disable - disable USB device controller
+ */
+static void udc_disable(struct s3c_udc *dev)
+{
+	DEBUG_SETUP("%s: %p\n", __func__, dev);
+
+	udc_set_address(dev, 0);
+
+	dev->ep0state = WAIT_FOR_SETUP;
+	dev->gadget.speed = USB_SPEED_UNKNOWN;
+	dev->usb_address = 0;
+
+	otg_phy_off();
+}
+
+/*
+ *	udc_reinit - initialize software state
+ */
+static void udc_reinit(struct s3c_udc *dev)
+{
+	unsigned int i;
+
+	DEBUG_SETUP("%s: %p\n", __func__, dev);
+
+	/* device/ep0 records init */
+	INIT_LIST_HEAD(&dev->gadget.ep_list);
+	INIT_LIST_HEAD(&dev->gadget.ep0->ep_list);
+	dev->ep0state = WAIT_FOR_SETUP;
+
+	/* basic endpoint records init */
+	for (i = 0; i < S3C_MAX_ENDPOINTS; i++) {
+		struct s3c_ep *ep = &dev->ep[i];
+
+		if (i != 0)
+			list_add_tail(&ep->ep.ep_list, &dev->gadget.ep_list);
+
+		ep->desc = 0;
+		ep->stopped = 0;
+		INIT_LIST_HEAD(&ep->queue);
+		ep->pio_irqs = 0;
+	}
+
+	/* the rest was statically initialized, and is read-only */
+}
+
+#define BYTES2MAXP(x)	(x / 8)
+#define MAXP2BYTES(x)	(x * 8)
+
+/* until it's enabled, this UDC should be completely invisible
+ * to any USB host.
+ */
+static int udc_enable(struct s3c_udc *dev)
+{
+	DEBUG_SETUP("%s: %p\n", __func__, dev);
+
+	otg_phy_init();
+	reconfig_usbd();
+
+	DEBUG_SETUP("S3C USB 2.0 OTG Controller Core Initialized : 0x%x\n",
+		    readl(S3C_UDC_OTG_GINTMSK));
+
+	dev->gadget.speed = USB_SPEED_UNKNOWN;
+
+	return 0;
+}
+
+/*
+  Register entry point for the peripheral controller driver.
+*/
+int usb_gadget_register_driver(struct usb_gadget_driver *driver)
+{
+	struct s3c_udc *dev = the_controller;
+	int retval = 0;
+	unsigned long flags;
+
+	DEBUG_SETUP("%s: %s\n", __func__, "no name");
+
+	if (!driver
+	    || (driver->speed != USB_SPEED_FULL
+		&& driver->speed != USB_SPEED_HIGH)
+	    || !driver->bind || !driver->disconnect || !driver->setup)
+		return -EINVAL;
+	if (!dev)
+		return -ENODEV;
+	if (dev->driver)
+		return -EBUSY;
+
+	spin_lock_irqsave(&dev->lock, flags);
+	/* first hook up the driver ... */
+	dev->driver = driver;
+	spin_unlock_irqrestore(&dev->lock, flags);
+
+	if (retval) { /* TODO */
+		printk("target device_add failed, error %d\n", retval);
+		return retval;
+	}
+
+	retval = driver->bind(&dev->gadget);
+	if (retval) {
+		DEBUG_SETUP("%s: bind to driver %s --> error %d\n",
+			    dev->gadget.name,
+			    driver->driver.name, retval);
+		dev->driver = 0;
+		return retval;
+	}
+
+	enable_irq(IRQ_OTG);
+
+	DEBUG_SETUP("Registered gadget driver %s\n", dev->gadget.name);
+	udc_enable(dev);
+
+	return 0;
+}
+EXPORT_SYMBOL(usb_gadget_register_driver);
+
+/*
+  Unregister entry point for the peripheral controller driver.
+*/
+int usb_gadget_unregister_driver(struct usb_gadget_driver *driver)
+{
+	struct s3c_udc *dev = the_controller;
+	unsigned long flags;
+
+	if (!dev)
+		return -ENODEV;
+	if (!driver || driver != dev->driver)
+		return -EINVAL;
+
+	spin_lock_irqsave(&dev->lock, flags);
+	dev->driver = 0;
+	stop_activity(dev, driver);
+	spin_unlock_irqrestore(&dev->lock, flags);
+
+	driver->unbind(&dev->gadget);
+
+	disable_irq(IRQ_OTG);
+
+	DEBUG_SETUP("Unregistered gadget driver '%s'\n", driver->driver.name);
+
+	udc_disable(dev);
+	return 0;
+}
+EXPORT_SYMBOL(usb_gadget_unregister_driver);
+
+/*
+ *	done - retire a request; caller blocked irqs
+ */
+static void done(struct s3c_ep *ep, struct s3c_request *req, int status)
+{
+	unsigned int stopped = ep->stopped;
+
+	DEBUG("%s: %s %p, req = %p, stopped = %d\n",
+	      __func__, ep->ep.name, ep, &req->req, stopped);
+
+	list_del_init(&req->queue);
+
+	if (likely(req->req.status == -EINPROGRESS))
+		req->req.status = status;
+	else
+		status = req->req.status;
+
+	if (status && status != -ESHUTDOWN) {
+		DEBUG("complete %s req %p stat %d len %u/%u\n",
+		      ep->ep.name, &req->req, status,
+		      req->req.actual, req->req.length);
+	}
+
+	/* don't modify queue heads during completion callback */
+	ep->stopped = 1;
+
+#ifdef DEBUG_S3C_UDC
+	printf("calling complete callback\n");
+	{
+		int i, len = req->req.length;
+
+		printf("pkt[%d] = ", req->req.length);
+		if (len > 64)
+			len = 64;
+		for (i = 0; i < len; i++) {
+			printf("%02x", ((u8 *)req->req.buf)[i]);
+			if ((i & 7) == 7)
+				printf(" ");
+		}
+		printf("\n");
+	}
+#endif
+	spin_unlock(&ep->dev->lock);
+	req->req.complete(&ep->ep, &req->req);
+	spin_lock(&ep->dev->lock);
+
+	DEBUG("callback completed\n");
+
+	ep->stopped = stopped;
+}
+
+/*
+ *	nuke - dequeue ALL requests
+ */
+static void nuke(struct s3c_ep *ep, int status)
+{
+	struct s3c_request *req;
+
+	DEBUG("%s: %s %p\n", __func__, ep->ep.name, ep);
+
+	/* called with irqs blocked */
+	while (!list_empty(&ep->queue)) {
+		req = list_entry(ep->queue.next, struct s3c_request, queue);
+		done(ep, req, status);
+	}
+}
+
+static void stop_activity(struct s3c_udc *dev,
+			  struct usb_gadget_driver *driver)
+{
+	int i;
+
+	/* don't disconnect drivers more than once */
+	if (dev->gadget.speed == USB_SPEED_UNKNOWN)
+		driver = 0;
+	dev->gadget.speed = USB_SPEED_UNKNOWN;
+
+	/* prevent new request submissions, kill any outstanding requests  */
+	for (i = 0; i < S3C_MAX_ENDPOINTS; i++) {
+		struct s3c_ep *ep = &dev->ep[i];
+		ep->stopped = 1;
+		nuke(ep, -ESHUTDOWN);
+	}
+
+	/* report disconnect; the driver is already quiesced */
+	if (driver) {
+		spin_unlock(&dev->lock);
+		driver->disconnect(&dev->gadget);
+		spin_lock(&dev->lock);
+	}
+
+	/* re-init driver-visible data structures */
+	udc_reinit(dev);
+}
+
+static void reconfig_usbd(void)
+{
+	/* 2. Soft-reset OTG Core and then unreset again. */
+	int i;
+	unsigned int uTemp = writel(CORE_SOFT_RESET, S3C_UDC_OTG_GRSTCTL);
+
+	DEBUG(2, "Reseting OTG controller\n");
+
+	writel(0<<15		/* PHY Low Power Clock sel*/
+		|1<<14		/* Non-Periodic TxFIFO Rewind Enable*/
+		|0x5<<10	/* Turnaround time*/
+		|0<<9 | 0<<8	/* [0:HNP disable,1:HNP enable][ 0:SRP disable*/
+				/* 1:SRP enable] H1= 1,1*/
+		|0<<7		/* Ulpi DDR sel*/
+		|0<<6		/* 0: high speed utmi+, 1: full speed serial*/
+		|0<<4		/* 0: utmi+, 1:ulpi*/
+		|1<<3		/* phy i/f  0:8bit, 1:16bit*/
+		|0x7<<0,	/* HS/FS Timeout**/
+		S3C_UDC_OTG_GUSBCFG);
+
+	/* 3. Put the OTG device core in the disconnected state.*/
+	uTemp = readl(S3C_UDC_OTG_DCTL);
+	uTemp |= SOFT_DISCONNECT;
+	writel(uTemp, S3C_UDC_OTG_DCTL);
+
+	udelay(20);
+
+	/* 4. Make the OTG device core exit from the disconnected state.*/
+	uTemp = readl(S3C_UDC_OTG_DCTL);
+	uTemp = uTemp & ~SOFT_DISCONNECT;
+	writel(uTemp, S3C_UDC_OTG_DCTL);
+
+	/* 5. Configure OTG Core to initial settings of device mode.*/
+	/* [][1: full speed(30Mhz) 0:high speed]*/
+	writel(1<<18 | 0x0<<0, S3C_UDC_OTG_DCFG);
+
+	mdelay(1);
+
+	/* 6. Unmask the core interrupts*/
+	writel(GINTMSK_INIT, S3C_UDC_OTG_GINTMSK);
+
+	/* 7. Set NAK bit of EP0, EP1, EP2*/
+	writel(DEPCTL_EPDIS|DEPCTL_SNAK|(0<<0), S3C_UDC_OTG_DOEPCTL(EP0_CON));
+	writel(DEPCTL_EPDIS|DEPCTL_SNAK|(0<<0), S3C_UDC_OTG_DIEPCTL(EP0_CON));
+
+	for (i = 1; i < S3C_MAX_ENDPOINTS; i++) {
+		writel(DEPCTL_EPDIS|DEPCTL_SNAK, S3C_UDC_OTG_DOEPCTL(i));
+		writel(DEPCTL_EPDIS|DEPCTL_SNAK, S3C_UDC_OTG_DIEPCTL(i));
+	}
+
+	/* 8. Unmask EPO interrupts*/
+	writel(((1 << EP0_CON) << DAINT_OUT_BIT)
+	       | (1 << EP0_CON), S3C_UDC_OTG_DAINTMSK);
+
+	/* 9. Unmask device OUT EP common interrupts*/
+	writel(DOEPMSK_INIT, S3C_UDC_OTG_DOEPMSK);
+
+	/* 10. Unmask device IN EP common interrupts*/
+	writel(DIEPMSK_INIT, S3C_UDC_OTG_DIEPMSK);
+
+	/* 11. Set Rx FIFO Size (in 32-bit words) */
+	writel(RX_FIFO_SIZE >> 2, S3C_UDC_OTG_GRXFSIZ);
+
+	/* 12. Set Non Periodic Tx FIFO Size*/
+	writel((NPTX_FIFO_SIZE >> 2) << 16 | ((RX_FIFO_SIZE >> 2)) << 0,
+	       S3C_UDC_OTG_GNPTXFSIZ);
+
+	for (i = 1; i < S3C_MAX_HW_ENDPOINTS; i++)
+		writel((PTX_FIFO_SIZE >> 2) << 16 |
+		       ((RX_FIFO_SIZE + NPTX_FIFO_SIZE +
+			 PTX_FIFO_SIZE*(i-1)) >> 2) << 0,
+		       S3C_UDC_OTG_DIEPTXF(i));
+
+	/* check if defined tx fifo sizes fits
+	   in SPRAM (S5PC110 fifo has 7936 entries */
+#if (((RX_FIFO_SIZE + NPTX_FIFO_SIZE + PTX_FIFO_SIZE*(S3C_MAX_HW_ENDPOINTS-1)) >> 2) >= 7936)
+#error Too large tx fifo size defined!
+#endif
+
+	/* Flush the RX FIFO */
+	writel(0x10, S3C_UDC_OTG_GRSTCTL);
+	while (readl(S3C_UDC_OTG_GRSTCTL) & 0x10)
+		DEBUG("%s: waiting for S3C_UDC_OTG_GRSTCTL\n", __func__);
+
+	/* Flush all the Tx FIFO's */
+	writel(0x10<<6, S3C_UDC_OTG_GRSTCTL);
+	writel((0x10<<6)|0x20, S3C_UDC_OTG_GRSTCTL);
+	while (readl(S3C_UDC_OTG_GRSTCTL) & 0x20)
+		DEBUG("%s: waiting for S3C_UDC_OTG_GRSTCTL\n", __func__);
+
+	/* 13. Clear NAK bit of EP0, EP1, EP2*/
+	/* For Slave mode*/
+	/* EP0: Control OUT */
+	writel(DEPCTL_EPDIS | DEPCTL_CNAK|(0<<0), S3C_UDC_OTG_DOEPCTL(EP0_CON));
+
+	/* 14. Initialize OTG Link Core.*/
+	writel(GAHBCFG_INIT, S3C_UDC_OTG_GAHBCFG);
+}
+
+static void set_max_pktsize(struct s3c_udc *dev, enum usb_device_speed speed)
+{
+	unsigned int ep_ctrl;
+	int i;
+
+	if (speed == USB_SPEED_HIGH) {
+		ep0_fifo_size = 64;
+		ep_fifo_size = 512;
+		ep_fifo_size2 = 1024;
+		dev->gadget.speed = USB_SPEED_HIGH;
+	} else {
+		ep0_fifo_size = 64;
+		ep_fifo_size = 64;
+		ep_fifo_size2 = 64;
+		dev->gadget.speed = USB_SPEED_FULL;
+	}
+
+	dev->ep[0].ep.maxpacket = ep0_fifo_size;
+	for (i = 1; i < S3C_MAX_ENDPOINTS; i++)
+		dev->ep[i].ep.maxpacket = ep_fifo_size;
+
+	/* EP0 - Control IN (64 bytes)*/
+	ep_ctrl = readl(S3C_UDC_OTG_DIEPCTL(EP0_CON));
+	writel(ep_ctrl|(0<<0), S3C_UDC_OTG_DIEPCTL(EP0_CON));
+
+	/* EP0 - Control OUT (64 bytes)*/
+	ep_ctrl = readl(S3C_UDC_OTG_DOEPCTL(EP0_CON));
+	writel(ep_ctrl|(0<<0), S3C_UDC_OTG_DOEPCTL(EP0_CON));
+}
+
+static int s3c_ep_enable(struct usb_ep *_ep,
+			 const struct usb_endpoint_descriptor *desc)
+{
+	struct s3c_ep *ep;
+	struct s3c_udc *dev;
+	unsigned long flags;
+
+	DEBUG("%s: %p\n", __func__, _ep);
+
+	ep = container_of(_ep, struct s3c_ep, ep);
+	if (!_ep || !desc || ep->desc || _ep->name == ep0name
+	    || desc->bDescriptorType != USB_DT_ENDPOINT
+	    || ep->bEndpointAddress != desc->bEndpointAddress
+	    || ep_maxpacket(ep) < le16_to_cpu(desc->wMaxPacketSize)) {
+
+		DEBUG("%s: bad ep or descriptor\n", __func__);
+		return -EINVAL;
+	}
+
+	/* xfer types must match, except that interrupt ~= bulk */
+	if (ep->bmAttributes != desc->bmAttributes
+	    && ep->bmAttributes != USB_ENDPOINT_XFER_BULK
+	    && desc->bmAttributes != USB_ENDPOINT_XFER_INT) {
+
+		DEBUG("%s: %s type mismatch\n", __func__, _ep->name);
+		return -EINVAL;
+	}
+
+	/* hardware _could_ do smaller, but driver doesn't */
+	if ((desc->bmAttributes == USB_ENDPOINT_XFER_BULK
+	     && le16_to_cpu(desc->wMaxPacketSize) != ep_maxpacket(ep))
+	    || !desc->wMaxPacketSize) {
+
+		DEBUG("%s: bad %s maxpacket\n", __func__, _ep->name);
+		return -ERANGE;
+	}
+
+	dev = ep->dev;
+	if (!dev->driver || dev->gadget.speed == USB_SPEED_UNKNOWN) {
+
+		DEBUG("%s: bogus device state\n", __func__);
+		return -ESHUTDOWN;
+	}
+
+	ep->stopped = 0;
+	ep->desc = desc;
+	ep->pio_irqs = 0;
+	ep->ep.maxpacket = le16_to_cpu(desc->wMaxPacketSize);
+
+	/* Reset halt state */
+	s3c_udc_set_nak(ep);
+	s3c_udc_set_halt(_ep, 0);
+
+	spin_lock_irqsave(&ep->dev->lock, flags);
+	s3c_udc_ep_activate(ep);
+	spin_unlock_irqrestore(&ep->dev->lock, flags);
+
+	DEBUG("%s: enabled %s, stopped = %d, maxpacket = %d\n",
+	      __func__, _ep->name, ep->stopped, ep->ep.maxpacket);
+	return 0;
+}
+
+/** Disable EP
+ */
+static int s3c_ep_disable(struct usb_ep *_ep)
+{
+	struct s3c_ep *ep;
+	unsigned long flags;
+
+	DEBUG("%s: %p\n", __func__, _ep);
+
+	ep = container_of(_ep, struct s3c_ep, ep);
+	if (!_ep || !ep->desc) {
+		DEBUG("%s: %s not enabled\n", __func__,
+		      _ep ? ep->ep.name : NULL);
+		return -EINVAL;
+	}
+
+	spin_lock_irqsave(&ep->dev->lock, flags);
+
+	/* Nuke all pending requests */
+	nuke(ep, -ESHUTDOWN);
+
+	ep->desc = 0;
+	ep->stopped = 1;
+
+	spin_unlock_irqrestore(&ep->dev->lock, flags);
+
+	DEBUG("%s: disabled %s\n", __func__, _ep->name);
+	return 0;
+}
+
+static struct usb_request *s3c_alloc_request(struct usb_ep *ep,
+					     gfp_t gfp_flags)
+{
+	struct s3c_request *req;
+
+	DEBUG("%s: %s %p\n", __func__, ep->name, ep);
+
+	req = kmalloc(sizeof *req, gfp_flags);
+	if (!req)
+		return 0;
+
+	memset(req, 0, sizeof *req);
+	INIT_LIST_HEAD(&req->queue);
+
+	return &req->req;
+}
+
+static void s3c_free_request(struct usb_ep *ep, struct usb_request *_req)
+{
+	struct s3c_request *req;
+
+	DEBUG("%s: %p\n", __func__, ep);
+
+	req = container_of(_req, struct s3c_request, req);
+	WARN_ON(!list_empty(&req->queue));
+	kfree(req);
+}
+
+/* dequeue JUST ONE request */
+static int s3c_dequeue(struct usb_ep *_ep, struct usb_request *_req)
+{
+	struct s3c_ep *ep;
+	struct s3c_request *req;
+	unsigned long flags;
+
+	DEBUG("%s: %p\n", __func__, _ep);
+
+	ep = container_of(_ep, struct s3c_ep, ep);
+	if (!_ep || ep->ep.name == ep0name)
+		return -EINVAL;
+
+	spin_lock_irqsave(&ep->dev->lock, flags);
+
+	/* make sure it's actually queued on this endpoint */
+	list_for_each_entry(req, &ep->queue, queue) {
+		if (&req->req == _req)
+			break;
+	}
+	if (&req->req != _req) {
+		spin_unlock_irqrestore(&ep->dev->lock, flags);
+		return -EINVAL;
+	}
+
+	done(ep, req, -ECONNRESET);
+
+	spin_unlock_irqrestore(&ep->dev->lock, flags);
+	return 0;
+}
+
+/** Return bytes in EP FIFO
+ */
+static int s3c_fifo_status(struct usb_ep *_ep)
+{
+	int count = 0;
+	struct s3c_ep *ep;
+
+	ep = container_of(_ep, struct s3c_ep, ep);
+	if (!_ep) {
+		DEBUG("%s: bad ep\n", __func__);
+		return -ENODEV;
+	}
+
+	DEBUG("%s: %d\n", __func__, ep_index(ep));
+
+	/* LPD can't report unclaimed bytes from IN fifos */
+	if (ep_is_in(ep))
+		return -EOPNOTSUPP;
+
+	return count;
+}
+
+/** Flush EP FIFO
+ */
+static void s3c_fifo_flush(struct usb_ep *_ep)
+{
+	struct s3c_ep *ep;
+
+	ep = container_of(_ep, struct s3c_ep, ep);
+	if (unlikely(!_ep || (!ep->desc && ep->ep.name != ep0name))) {
+		DEBUG("%s: bad ep\n", __func__);
+		return;
+	}
+
+	DEBUG("%s: %d\n", __func__, ep_index(ep));
+}
+
+static const struct usb_gadget_ops s3c_udc_ops = {
+	/* current versions must always be self-powered */
+};
+
+static struct s3c_udc memory = {
+	.usb_address = 0,
+	.gadget = {
+		.ops = &s3c_udc_ops,
+		.ep0 = &memory.ep[0].ep,
+		.name = driver_name,
+	},
+
+	/* control endpoint */
+	.ep[0] = {
+		.ep = {
+			.name = ep0name,
+			.ops = &s3c_ep_ops,
+			.maxpacket = EP0_FIFO_SIZE,
+		},
+		.dev = &memory,
+
+		.bEndpointAddress = 0,
+		.bmAttributes = 0,
+
+		.ep_type = ep_control,
+	},
+
+	/* first group of endpoints */
+	.ep[1] = {
+		.ep = {
+			.name = "ep1in-bulk",
+			.ops = &s3c_ep_ops,
+			.maxpacket = EP_FIFO_SIZE,
+		},
+		.dev = &memory,
+
+		.bEndpointAddress = USB_DIR_IN | 1,
+		.bmAttributes = USB_ENDPOINT_XFER_BULK,
+
+		.ep_type = ep_bulk_out,
+		.fifo_num = 1,
+	},
+
+	.ep[2] = {
+		.ep = {
+			.name = "ep2out-bulk",
+			.ops = &s3c_ep_ops,
+			.maxpacket = EP_FIFO_SIZE,
+		},
+		.dev = &memory,
+
+		.bEndpointAddress = USB_DIR_OUT | 2,
+		.bmAttributes = USB_ENDPOINT_XFER_BULK,
+
+		.ep_type = ep_bulk_in,
+		.fifo_num = 2,
+	},
+
+	.ep[3] = {
+		.ep = {
+			.name = "ep3in-int",
+			.ops = &s3c_ep_ops,
+			.maxpacket = EP_FIFO_SIZE,
+		},
+		.dev = &memory,
+
+		.bEndpointAddress = USB_DIR_IN | 3,
+		.bmAttributes = USB_ENDPOINT_XFER_INT,
+
+		.ep_type = ep_interrupt,
+		.fifo_num = 3,
+	},
+};
+
+/*
+ *	probe - binds to the platform device
+ */
+
+int s3c_udc_probe(struct s3c_plat_otg_data *pdata)
+{
+	struct s3c_udc *dev = &memory;
+	int retval = 0, i;
+
+	DEBUG("%s: %p\n", __func__, pdata);
+
+	dev->pdata = pdata;
+
+	regs_phy = (void *)pdata->regs_phy;
+	regs_otg = (void *)pdata->regs_otg;
+
+	dev->gadget.is_dualspeed = 1;	/* Hack only*/
+	dev->gadget.is_otg = 0;
+	dev->gadget.is_a_peripheral = 0;
+	dev->gadget.b_hnp_enable = 0;
+	dev->gadget.a_hnp_support = 0;
+	dev->gadget.a_alt_hnp_support = 0;
+
+	the_controller = dev;
+
+	for (i = 0; i < S3C_MAX_ENDPOINTS+1; i++) {
+		dev->dma_buf[i] = kmalloc(DMA_BUFFER_SIZE, GFP_KERNEL);
+		dev->dma_addr[i] = (dma_addr_t) dev->dma_buf[i];
+	}
+	usb_ctrl = dev->dma_buf[0];
+	usb_ctrl_dma_addr = dev->dma_addr[0];
+
+	udc_reinit(dev);
+
+	return retval;
+}
+
+int usb_gadget_handle_interrupts()
+{
+	u32 intr_status = readl(S3C_UDC_OTG_GINTSTS);
+	u32 gintmsk = readl(S3C_UDC_OTG_GINTMSK);
+
+	if (intr_status & gintmsk)
+		return s3c_udc_irq(1, (void *)the_controller);
+	return 0;
+}
diff --git a/drivers/usb/gadget/s3c_udc_otg_xfer_dma.c b/drivers/usb/gadget/s3c_udc_otg_xfer_dma.c
new file mode 100644
index 0000000..779f6f6
--- /dev/null
+++ b/drivers/usb/gadget/s3c_udc_otg_xfer_dma.c
@@ -0,0 +1,1406 @@
+/*
+ * drivers/usb/gadget/s3c_udc_otg_xfer_dma.c
+ * Samsung S3C on-chip full/high speed USB OTG 2.0 device controllers
+ *
+ * Copyright (C) 2009 for Samsung Electronics
+ *
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+ *
+ */
+
+#define GINTMSK_INIT	(INT_OUT_EP | INT_IN_EP | INT_RESUME | INT_ENUMDONE\
+			| INT_RESET | INT_SUSPEND)
+#define DOEPMSK_INIT	(CTRL_OUT_EP_SETUP_PHASE_DONE | AHB_ERROR|TRANSFER_DONE)
+#define DIEPMSK_INIT	(NON_ISO_IN_EP_TIMEOUT|AHB_ERROR|TRANSFER_DONE)
+#define GAHBCFG_INIT	(PTXFE_HALF | NPTXFE_HALF | MODE_DMA | BURST_INCR4\
+			| GBL_INT_UNMASK)
+
+static u8 clear_feature_num;
+int clear_feature_flag;
+
+/* Bulk-Only Mass Storage Reset (class-specific request) */
+#define GET_MAX_LUN_REQUEST	0xFE
+#define BOT_RESET_REQUEST	0xFF
+
+void s3c_udc_ep_set_stall(struct s3c_ep *ep);
+
+static inline void s3c_udc_ep0_zlp(struct s3c_udc *dev)
+{
+	u32 ep_ctrl;
+
+	writel(usb_ctrl_dma_addr, S3C_UDC_OTG_DIEPDMA(EP0_CON));
+	writel((1<<19 | 0<<0), S3C_UDC_OTG_DIEPTSIZ(EP0_CON));
+
+	ep_ctrl = readl(S3C_UDC_OTG_DIEPCTL(EP0_CON));
+	writel(ep_ctrl|DEPCTL_EPENA|DEPCTL_CNAK, S3C_UDC_OTG_DIEPCTL(EP0_CON));
+
+	DEBUG_EP0("%s:EP0 ZLP DIEPCTL0 = 0x%x\n",
+		__func__, readl(S3C_UDC_OTG_DIEPCTL(EP0_CON)));
+	dev->ep0state = WAIT_FOR_IN_COMPLETE;
+}
+
+static inline void s3c_udc_pre_setup(void)
+{
+	u32 ep_ctrl;
+
+	DEBUG_IN_EP("%s : Prepare Setup packets.\n", __func__);
+
+	writel((1 << 19) | sizeof(struct usb_ctrlrequest),
+	       S3C_UDC_OTG_DOEPTSIZ(EP0_CON));
+	writel(usb_ctrl_dma_addr, S3C_UDC_OTG_DOEPDMA(EP0_CON));
+
+	ep_ctrl = readl(S3C_UDC_OTG_DOEPCTL(EP0_CON));
+	writel(ep_ctrl|DEPCTL_EPENA, S3C_UDC_OTG_DOEPCTL(EP0_CON));
+
+	DEBUG_EP0("%s:EP0 ZLP DIEPCTL0 = 0x%x\n",
+		__func__, readl(S3C_UDC_OTG_DIEPCTL(EP0_CON)));
+	DEBUG_EP0("%s:EP0 ZLP DOEPCTL0 = 0x%x\n",
+		__func__, readl(S3C_UDC_OTG_DOEPCTL(EP0_CON)));
+
+}
+
+static inline void s3c_ep0_complete_out(void)
+{
+	u32 ep_ctrl;
+
+	DEBUG_EP0("%s:EP0 ZLP DIEPCTL0 = 0x%x\n",
+		__func__, readl(S3C_UDC_OTG_DIEPCTL(EP0_CON)));
+	DEBUG_EP0("%s:EP0 ZLP DOEPCTL0 = 0x%x\n",
+		__func__, readl(S3C_UDC_OTG_DOEPCTL(EP0_CON)));
+
+	DEBUG_IN_EP("%s : Prepare Complete Out packet.\n", __func__);
+
+	writel((1 << 19) | sizeof(struct usb_ctrlrequest),
+	       S3C_UDC_OTG_DOEPTSIZ(EP0_CON));
+	writel(usb_ctrl_dma_addr, S3C_UDC_OTG_DOEPDMA(EP0_CON));
+
+	ep_ctrl = readl(S3C_UDC_OTG_DOEPCTL(EP0_CON));
+	writel(ep_ctrl|DEPCTL_EPENA|DEPCTL_CNAK, S3C_UDC_OTG_DOEPCTL(EP0_CON));
+
+	DEBUG_EP0("%s:EP0 ZLP DIEPCTL0 = 0x%x\n",
+		__func__, readl(S3C_UDC_OTG_DIEPCTL(EP0_CON)));
+	DEBUG_EP0("%s:EP0 ZLP DOEPCTL0 = 0x%x\n",
+		__func__, readl(S3C_UDC_OTG_DOEPCTL(EP0_CON)));
+
+}
+
+
+static int setdma_rx(struct s3c_ep *ep, struct s3c_request *req)
+{
+	u32 *buf, ctrl;
+	u32 length, pktcnt;
+	u32 ep_num = ep_index(ep);
+
+	buf = req->req.buf + req->req.actual;
+
+	length = min(req->req.length - req->req.actual, (int)ep->ep.maxpacket);
+
+	ep->len = length;
+	ep->dma_buf = buf;
+
+	if (length == 0)
+		pktcnt = 1;
+	else
+		pktcnt = (length - 1)/(ep->ep.maxpacket) + 1;
+
+	pktcnt = 1;
+	ctrl =  readl(S3C_UDC_OTG_DOEPCTL(ep_num));
+
+	writel(the_controller->dma_addr[ep_index(ep)+1],
+	       S3C_UDC_OTG_DOEPDMA(ep_num));
+	writel((pktcnt<<19)|(length<<0), S3C_UDC_OTG_DOEPTSIZ(ep_num));
+	writel(DEPCTL_EPENA|DEPCTL_CNAK|ctrl, S3C_UDC_OTG_DOEPCTL(ep_num));
+
+	DEBUG_OUT_EP("%s: EP%d RX DMA start : DOEPDMA = 0x%x,"
+		     "DOEPTSIZ = 0x%x, DOEPCTL = 0x%x\n"
+		     "\tbuf = 0x%p, pktcnt = %d, xfersize = %d\n",
+		     __func__, ep_num,
+		     readl(S3C_UDC_OTG_DOEPDMA(ep_num)),
+		     readl(S3C_UDC_OTG_DOEPTSIZ(ep_num)),
+		     readl(S3C_UDC_OTG_DOEPCTL(ep_num)),
+		     buf, pktcnt, length);
+	return 0;
+
+}
+
+static int setdma_tx(struct s3c_ep *ep, struct s3c_request *req)
+{
+	u32 *buf, ctrl = 0;
+	u32 length, pktcnt;
+	u32 ep_num = ep_index(ep);
+	u32 *p = the_controller->dma_buf[ep_index(ep)+1];
+
+	buf = req->req.buf + req->req.actual;
+	length = req->req.length - req->req.actual;
+
+	if (ep_num == EP0_CON)
+		length = min(length, (u32)ep_maxpacket(ep));
+
+	ep->len = length;
+	ep->dma_buf = buf;
+	memcpy(p, ep->dma_buf, length);
+
+	if (length == 0)
+		pktcnt = 1;
+	else
+		pktcnt = (length - 1)/(ep->ep.maxpacket) + 1;
+
+	/* Flush the endpoint's Tx FIFO */
+	writel(ep->fifo_num<<6, S3C_UDC_OTG_GRSTCTL);
+	writel((ep->fifo_num<<6)|0x20, S3C_UDC_OTG_GRSTCTL);
+	while (readl(S3C_UDC_OTG_GRSTCTL) & 0x20)
+		;
+
+	writel(the_controller->dma_addr[ep_index(ep)+1],
+	       S3C_UDC_OTG_DIEPDMA(ep_num));
+	writel((pktcnt<<19)|(length<<0), S3C_UDC_OTG_DIEPTSIZ(ep_num));
+
+	ctrl = readl(S3C_UDC_OTG_DIEPCTL(ep_num));
+
+	/* Write the FIFO number to be used for this endpoint */
+	ctrl &= ~(0xF << 22);
+	ctrl |= (ep->fifo_num << 22);
+
+	/* Clear reserved (Next EP) bits */
+	ctrl = (ctrl&~(EP_MASK<<DEPCTL_NEXT_EP_BIT));
+
+	writel(DEPCTL_EPENA|DEPCTL_CNAK|ctrl, S3C_UDC_OTG_DIEPCTL(ep_num));
+
+	DEBUG_IN_EP("%s:EP%d TX DMA start : DIEPDMA0 = 0x%x,"
+		    "DIEPTSIZ0 = 0x%x, DIEPCTL0 = 0x%x\n"
+		    "\tbuf = 0x%p, pktcnt = %d, xfersize = %d\n",
+		    __func__, ep_num,
+		    readl(S3C_UDC_OTG_DIEPDMA(ep_num)),
+		    readl(S3C_UDC_OTG_DIEPTSIZ(ep_num)),
+		    readl(S3C_UDC_OTG_DIEPCTL(ep_num)),
+		    buf, pktcnt, length);
+
+	return length;
+}
+
+static void complete_rx(struct s3c_udc *dev, u8 ep_num)
+{
+	struct s3c_ep *ep = &dev->ep[ep_num];
+	struct s3c_request *req = NULL;
+	u32 ep_tsr = 0, xfer_size = 0, is_short = 0;
+	u32 *p = the_controller->dma_buf[ep_index(ep)+1];
+
+	if (list_empty(&ep->queue)) {
+		DEBUG_OUT_EP("%s: RX DMA done : NULL REQ on OUT EP-%d\n",
+					__func__, ep_num);
+		return;
+
+	}
+
+	req = list_entry(ep->queue.next, struct s3c_request, queue);
+	ep_tsr = readl(S3C_UDC_OTG_DOEPTSIZ(ep_num));
+
+	if (ep_num == EP0_CON)
+		xfer_size = (ep_tsr & 0x7f);
+	else
+		xfer_size = (ep_tsr & 0x7fff);
+
+	xfer_size = ep->len - xfer_size;
+	memcpy(ep->dma_buf, p, ep->len);
+
+	req->req.actual += min(xfer_size, req->req.length - req->req.actual);
+	is_short = (xfer_size < ep->ep.maxpacket);
+
+	DEBUG_OUT_EP("%s: RX DMA done : ep = %d, rx bytes = %d/%d, "
+		     "is_short = %d, DOEPTSIZ = 0x%x, remained bytes = %d\n",
+			__func__, ep_num, req->req.actual, req->req.length,
+			is_short, ep_tsr, xfer_size);
+
+	if (is_short || req->req.actual == req->req.length) {
+		if (ep_num == EP0_CON && dev->ep0state == DATA_STATE_RECV) {
+			DEBUG_OUT_EP("	=> Send ZLP\n");
+			s3c_udc_ep0_zlp(dev);
+			/* packet will be completed in complete_tx() */
+			dev->ep0state = WAIT_FOR_IN_COMPLETE;
+		} else {
+			done(ep, req, 0);
+
+			if (!list_empty(&ep->queue)) {
+				req = list_entry(ep->queue.next,
+					struct s3c_request, queue);
+				DEBUG_OUT_EP("%s: Next Rx request start...\n",
+					 __func__);
+				setdma_rx(ep, req);
+			}
+		}
+	} else
+		setdma_rx(ep, req);
+}
+
+static void complete_tx(struct s3c_udc *dev, u8 ep_num)
+{
+	struct s3c_ep *ep = &dev->ep[ep_num];
+	struct s3c_request *req;
+	u32 ep_tsr = 0, xfer_size = 0, is_short = 0;
+	u32 last;
+
+	if (dev->ep0state == WAIT_FOR_NULL_COMPLETE) {
+		dev->ep0state = WAIT_FOR_OUT_COMPLETE;
+		s3c_ep0_complete_out();
+		return;
+	}
+
+	if (list_empty(&ep->queue)) {
+		DEBUG_IN_EP("%s: TX DMA done : NULL REQ on IN EP-%d\n",
+					__func__, ep_num);
+		return;
+
+	}
+
+	req = list_entry(ep->queue.next, struct s3c_request, queue);
+
+	ep_tsr = readl(S3C_UDC_OTG_DIEPTSIZ(ep_num));
+
+	xfer_size = ep->len;
+	is_short = (xfer_size < ep->ep.maxpacket);
+	req->req.actual += min(xfer_size, req->req.length - req->req.actual);
+
+	DEBUG_IN_EP("%s: TX DMA done : ep = %d, tx bytes = %d/%d, "
+		     "is_short = %d, DIEPTSIZ = 0x%x, remained bytes = %d\n",
+			__func__, ep_num, req->req.actual, req->req.length,
+			is_short, ep_tsr, xfer_size);
+
+	if (ep_num == 0) {
+		if (dev->ep0state == DATA_STATE_XMIT) {
+			DEBUG_IN_EP("%s: ep_num = %d, ep0stat =="
+				    "DATA_STATE_XMIT\n",
+				    __func__, ep_num);
+			last = write_fifo_ep0(ep, req);
+			if (last)
+				dev->ep0state = WAIT_FOR_COMPLETE;
+		} else if (dev->ep0state == WAIT_FOR_IN_COMPLETE) {
+			DEBUG_IN_EP("%s: ep_num = %d, completing request\n",
+				    __func__, ep_num);
+			done(ep, req, 0);
+			dev->ep0state = WAIT_FOR_SETUP;
+		} else if (dev->ep0state == WAIT_FOR_COMPLETE) {
+			DEBUG_IN_EP("%s: ep_num = %d, completing request\n",
+				    __func__, ep_num);
+			done(ep, req, 0);
+			dev->ep0state = WAIT_FOR_OUT_COMPLETE;
+			s3c_ep0_complete_out();
+		} else {
+			DEBUG_IN_EP("%s: ep_num = %d, invalid ep state\n",
+				    __func__, ep_num);
+		}
+		return;
+	}
+
+	if (req->req.actual == req->req.length)
+		done(ep, req, 0);
+
+	if (!list_empty(&ep->queue)) {
+		req = list_entry(ep->queue.next, struct s3c_request, queue);
+		DEBUG_IN_EP("%s: Next Tx request start...\n", __func__);
+		setdma_tx(ep, req);
+	}
+}
+
+static inline void s3c_udc_check_tx_queue(struct s3c_udc *dev, u8 ep_num)
+{
+	struct s3c_ep *ep = &dev->ep[ep_num];
+	struct s3c_request *req;
+
+	DEBUG_IN_EP("%s: Check queue, ep_num = %d\n", __func__, ep_num);
+
+	if (!list_empty(&ep->queue)) {
+		req = list_entry(ep->queue.next, struct s3c_request, queue);
+		DEBUG_IN_EP("%s: Next Tx request(0x%p) start...\n",
+			    __func__, req);
+
+		if (ep_is_in(ep))
+			setdma_tx(ep, req);
+		else
+			setdma_rx(ep, req);
+	} else {
+		DEBUG_IN_EP("%s: NULL REQ on IN EP-%d\n", __func__, ep_num);
+
+		return;
+	}
+
+}
+
+static void process_ep_in_intr(struct s3c_udc *dev)
+{
+	u32 ep_intr, ep_intr_status;
+	u8 ep_num = 0;
+
+	ep_intr = readl(S3C_UDC_OTG_DAINT);
+	DEBUG_IN_EP("*** %s: EP In interrupt : DAINT = 0x%x\n",
+				__func__, ep_intr);
+
+	ep_intr &= DAINT_MASK;
+
+	while (ep_intr) {
+		if (ep_intr & 0x1) {
+			ep_intr_status = readl(S3C_UDC_OTG_DIEPINT(ep_num));
+			DEBUG_IN_EP("\tEP%d-IN : DIEPINT = 0x%x\n",
+						ep_num, ep_intr_status);
+
+			/* Interrupt Clear */
+			writel(ep_intr_status, S3C_UDC_OTG_DIEPINT(ep_num));
+
+			if (ep_intr_status & TRANSFER_DONE) {
+				complete_tx(dev, ep_num);
+
+				if (ep_num == 0) {
+					if (dev->ep0state ==
+					    WAIT_FOR_IN_COMPLETE)
+						dev->ep0state = WAIT_FOR_SETUP;
+
+					if (dev->ep0state == WAIT_FOR_SETUP)
+						s3c_udc_pre_setup();
+
+					/* continue transfer after
+					   set_clear_halt for DMA mode */
+					if (clear_feature_flag == 1) {
+						s3c_udc_check_tx_queue(dev,
+							clear_feature_num);
+						clear_feature_flag = 0;
+					}
+				}
+			}
+		}
+		ep_num++;
+		ep_intr >>= 1;
+	}
+}
+
+static void process_ep_out_intr(struct s3c_udc *dev)
+{
+	u32 ep_intr, ep_intr_status;
+	u8 ep_num = 0;
+
+	ep_intr = readl(S3C_UDC_OTG_DAINT);
+	DEBUG_OUT_EP("*** %s: EP OUT interrupt : DAINT = 0x%x\n",
+				__func__, ep_intr);
+
+	ep_intr = (ep_intr >> DAINT_OUT_BIT) & DAINT_MASK;
+
+	while (ep_intr) {
+		if (ep_intr & 0x1) {
+			ep_intr_status = readl(S3C_UDC_OTG_DOEPINT(ep_num));
+			DEBUG_OUT_EP("\tEP%d-OUT : DOEPINT = 0x%x\n",
+						ep_num, ep_intr_status);
+
+			/* Interrupt Clear */
+			writel(ep_intr_status, S3C_UDC_OTG_DOEPINT(ep_num));
+
+			if (ep_num == 0) {
+				if (ep_intr_status & TRANSFER_DONE) {
+					if (dev->ep0state !=
+					    WAIT_FOR_OUT_COMPLETE)
+						complete_rx(dev, ep_num);
+					else {
+						dev->ep0state = WAIT_FOR_SETUP;
+						s3c_udc_pre_setup();
+					}
+				}
+
+				if (ep_intr_status &
+				    CTRL_OUT_EP_SETUP_PHASE_DONE) {
+					DEBUG_OUT_EP("\tSETUP packet(transaction) arrived\n");
+					s3c_handle_ep0(dev);
+				}
+			} else {
+				if (ep_intr_status & TRANSFER_DONE)
+					complete_rx(dev, ep_num);
+			}
+		}
+		ep_num++;
+		ep_intr >>= 1;
+	}
+}
+
+/*
+ *	usb client interrupt handler.
+ */
+static irqreturn_t s3c_udc_irq(int irq, void *_dev)
+{
+	struct s3c_udc *dev = _dev;
+	u32 intr_status;
+	u32 usb_status, gintmsk;
+	unsigned long flags;
+
+	spin_lock_irqsave(&dev->lock, flags);
+
+	intr_status = readl(S3C_UDC_OTG_GINTSTS);
+	gintmsk = readl(S3C_UDC_OTG_GINTMSK);
+
+	DEBUG_ISR("\n*** %s : GINTSTS=0x%x(on state %s), GINTMSK : 0x%x,"
+		  "DAINT : 0x%x, DAINTMSK : 0x%x\n",
+		  __func__, intr_status, state_names[dev->ep0state], gintmsk,
+		  readl(S3C_UDC_OTG_DAINT), readl(S3C_UDC_OTG_DAINTMSK));
+
+	if (!intr_status) {
+		spin_unlock_irqrestore(&dev->lock, flags);
+		return IRQ_HANDLED;
+	}
+
+	if (intr_status & INT_ENUMDONE) {
+		DEBUG_ISR("\tSpeed Detection interrupt\n");
+
+		writel(INT_ENUMDONE, S3C_UDC_OTG_GINTSTS);
+		usb_status = (readl(S3C_UDC_OTG_DSTS) & 0x6);
+
+		if (usb_status & (USB_FULL_30_60MHZ | USB_FULL_48MHZ)) {
+			DEBUG_ISR("\t\tFull Speed Detection\n");
+			set_max_pktsize(dev, USB_SPEED_FULL);
+
+		} else {
+			DEBUG_ISR("\t\tHigh Speed Detection : 0x%x\n",
+				  usb_status);
+			set_max_pktsize(dev, USB_SPEED_HIGH);
+		}
+	}
+
+	if (intr_status & INT_EARLY_SUSPEND) {
+		DEBUG_ISR("\tEarly suspend interrupt\n");
+		writel(INT_EARLY_SUSPEND, S3C_UDC_OTG_GINTSTS);
+	}
+
+	if (intr_status & INT_SUSPEND) {
+		usb_status = readl(S3C_UDC_OTG_DSTS);
+		DEBUG_ISR("\tSuspend interrupt :(DSTS):0x%x\n", usb_status);
+		writel(INT_SUSPEND, S3C_UDC_OTG_GINTSTS);
+
+		if (dev->gadget.speed != USB_SPEED_UNKNOWN
+		    && dev->driver) {
+			if (dev->driver->suspend)
+				dev->driver->suspend(&dev->gadget);
+
+			/* HACK to let gadget detect disconnected state */
+			if (dev->driver->disconnect) {
+				spin_unlock_irqrestore(&dev->lock, flags);
+				dev->driver->disconnect(&dev->gadget);
+				spin_lock_irqsave(&dev->lock, flags);
+			}
+		}
+	}
+
+	if (intr_status & INT_RESUME) {
+		DEBUG_ISR("\tResume interrupt\n");
+		writel(INT_RESUME, S3C_UDC_OTG_GINTSTS);
+
+		if (dev->gadget.speed != USB_SPEED_UNKNOWN
+		    && dev->driver
+		    && dev->driver->resume) {
+
+			dev->driver->resume(&dev->gadget);
+		}
+	}
+
+	if (intr_status & INT_RESET) {
+		usb_status = readl(S3C_UDC_OTG_GOTGCTL);
+		DEBUG_ISR("\tReset interrupt - (GOTGCTL):0x%x\n", usb_status);
+		writel(INT_RESET, S3C_UDC_OTG_GINTSTS);
+
+		if ((usb_status & 0xc0000) == (0x3 << 18)) {
+			if (reset_available) {
+				DEBUG_ISR("\t\tOTG core got reset (%d)!!\n",
+					  reset_available);
+				reconfig_usbd();
+				dev->ep0state = WAIT_FOR_SETUP;
+				reset_available = 0;
+				s3c_udc_pre_setup();
+			} else
+				reset_available = 1;
+
+		} else {
+			reset_available = 1;
+			DEBUG_ISR("\t\tRESET handling skipped\n");
+		}
+	}
+
+	if (intr_status & INT_IN_EP)
+		process_ep_in_intr(dev);
+
+	if (intr_status & INT_OUT_EP)
+		process_ep_out_intr(dev);
+
+	spin_unlock_irqrestore(&dev->lock, flags);
+
+	return IRQ_HANDLED;
+}
+
+/** Queue one request
+ *  Kickstart transfer if needed
+ */
+static int s3c_queue(struct usb_ep *_ep, struct usb_request *_req,
+			 gfp_t gfp_flags)
+{
+	struct s3c_request *req;
+	struct s3c_ep *ep;
+	struct s3c_udc *dev;
+	unsigned long flags;
+	u32 ep_num, gintsts;
+
+	req = container_of(_req, struct s3c_request, req);
+	if (unlikely(!_req || !_req->complete || !_req->buf
+		     || !list_empty(&req->queue))) {
+
+		DEBUG("%s: bad params\n", __func__);
+		return -EINVAL;
+	}
+
+	ep = container_of(_ep, struct s3c_ep, ep);
+
+	if (unlikely(!_ep || (!ep->desc && ep->ep.name != ep0name))) {
+
+		DEBUG("%s: bad ep: %s, %d, %x\n", __func__,
+		      ep->ep.name, !ep->desc, _ep);
+		return -EINVAL;
+	}
+
+	ep_num = ep_index(ep);
+	dev = ep->dev;
+	if (unlikely(!dev->driver || dev->gadget.speed == USB_SPEED_UNKNOWN)) {
+
+		DEBUG("%s: bogus device state %p\n", __func__, dev->driver);
+		return -ESHUTDOWN;
+	}
+
+	spin_lock_irqsave(&dev->lock, flags);
+
+	_req->status = -EINPROGRESS;
+	_req->actual = 0;
+
+	/* kickstart this i/o queue? */
+	DEBUG("\n*** %s: %s-%s req = %p, len = %d, buf = %p"
+		"Q empty = %d, stopped = %d\n",
+		__func__, _ep->name, ep_is_in(ep) ? "in" : "out",
+		_req, _req->length, _req->buf,
+		list_empty(&ep->queue), ep->stopped);
+
+#ifdef DEBUG_S3C_UDC
+	{
+		int i, len = _req->length;
+
+		printf("pkt = ");
+		if (len > 64)
+			len = 64;
+		for (i = 0; i < len; i++) {
+			printf("%02x", ((u8 *)_req->buf)[i]);
+			if ((i & 7) == 7)
+				printf(" ");
+		}
+		printf("\n");
+	}
+#endif
+
+	if (list_empty(&ep->queue) && !ep->stopped) {
+
+		if (ep_num == 0) {
+			/* EP0 */
+			list_add_tail(&req->queue, &ep->queue);
+			s3c_ep0_kick(dev, ep);
+			req = 0;
+
+		} else if (ep_is_in(ep)) {
+			gintsts = readl(S3C_UDC_OTG_GINTSTS);
+			DEBUG_IN_EP("%s: ep_is_in, S3C_UDC_OTG_GINTSTS=0x%x\n",
+						__func__, gintsts);
+
+			setdma_tx(ep, req);
+		} else {
+			gintsts = readl(S3C_UDC_OTG_GINTSTS);
+			DEBUG_OUT_EP("%s:ep_is_out, S3C_UDC_OTG_GINTSTS=0x%x\n",
+				__func__, gintsts);
+
+			setdma_rx(ep, req);
+		}
+	}
+
+	/* pio or dma irq handler advances the queue. */
+	if (likely(req != 0))
+		list_add_tail(&req->queue, &ep->queue);
+
+	spin_unlock_irqrestore(&dev->lock, flags);
+
+	return 0;
+}
+
+/****************************************************************/
+/* End Point 0 related functions                                */
+/****************************************************************/
+
+/* return:  0 = still running, 1 = completed, negative = errno */
+static int write_fifo_ep0(struct s3c_ep *ep, struct s3c_request *req)
+{
+	u32 max;
+	unsigned count;
+	int is_last;
+
+	max = ep_maxpacket(ep);
+
+	DEBUG_EP0("%s: max = %d\n", __func__, max);
+
+	count = setdma_tx(ep, req);
+
+	/* last packet is usually short (or a zlp) */
+	if (likely(count != max))
+		is_last = 1;
+	else {
+		if (likely(req->req.length != req->req.actual + count)
+		    || req->req.zero)
+			is_last = 0;
+		else
+			is_last = 1;
+	}
+
+	DEBUG_EP0("%s: wrote %s %d bytes%s %d left %p\n", __func__,
+		  ep->ep.name, count,
+		  is_last ? "/L" : "",
+		  req->req.length - req->req.actual - count, req);
+
+	/* requests complete when all IN data is in the FIFO */
+	if (is_last) {
+		ep->dev->ep0state = WAIT_FOR_SETUP;
+		return 1;
+	}
+
+	return 0;
+}
+
+static inline int s3c_fifo_read(struct s3c_ep *ep, u32 *cp, int max)
+{
+	u32 bytes;
+
+	bytes = sizeof(struct usb_ctrlrequest);
+	DEBUG_EP0("%s: bytes=%d, ep_index=%d\n", __func__,
+		  bytes, ep_index(ep));
+
+	return bytes;
+}
+
+/**
+ * udc_set_address - set the USB address for this device
+ * @address:
+ *
+ * Called from control endpoint function
+ * after it decodes a set address setup packet.
+ */
+static void udc_set_address(struct s3c_udc *dev, unsigned char address)
+{
+	u32 ctrl = readl(S3C_UDC_OTG_DCFG);
+	writel(address << 4 | ctrl, S3C_UDC_OTG_DCFG);
+
+	s3c_udc_ep0_zlp(dev);
+
+	DEBUG_EP0("%s: USB OTG 2.0 Device address=%d, DCFG=0x%x\n",
+		__func__, address, readl(S3C_UDC_OTG_DCFG));
+
+	dev->usb_address = address;
+}
+
+static inline void s3c_udc_ep0_set_stall(struct s3c_ep *ep)
+{
+	struct s3c_udc *dev;
+	u32		ep_ctrl = 0;
+
+	dev = ep->dev;
+	ep_ctrl = readl(S3C_UDC_OTG_DIEPCTL(EP0_CON));
+
+	/* set the disable and stall bits */
+	if (ep_ctrl & DEPCTL_EPENA)
+		ep_ctrl |= DEPCTL_EPDIS;
+
+	ep_ctrl |= DEPCTL_STALL;
+
+	writel(ep_ctrl, S3C_UDC_OTG_DIEPCTL(EP0_CON));
+
+	DEBUG_EP0("%s: set ep%d stall, DIEPCTL0 = 0x%x\n",
+		__func__, ep_index(ep), readl(S3C_UDC_OTG_DIEPCTL(EP0_CON)));
+	/*
+	 * The application can only set this bit, and the core clears it,
+	 * when a SETUP token is received for this endpoint
+	 */
+	dev->ep0state = WAIT_FOR_SETUP;
+
+	s3c_udc_pre_setup();
+}
+
+static void s3c_ep0_read(struct s3c_udc *dev)
+{
+	struct s3c_request *req;
+	struct s3c_ep *ep = &dev->ep[0];
+	int ret;
+
+	if (!list_empty(&ep->queue)) {
+		req = list_entry(ep->queue.next, struct s3c_request, queue);
+
+	} else {
+		DEBUG("%s: ---> BUG\n", __func__);
+		BUG();
+		return;
+	}
+
+	DEBUG_EP0("%s: req = %p, req.length = 0x%x, req.actual = 0x%x\n",
+		__func__, req, req->req.length, req->req.actual);
+
+	if (req->req.length == 0) {
+		/* zlp for Set_configuration, Set_interface,
+		 * or Bulk-Only mass storge reset */
+
+		ep->len = 0;
+		s3c_udc_ep0_zlp(dev);
+
+		DEBUG_EP0("%s: req.length = 0, bRequest = %d\n",
+			  __func__, usb_ctrl->bRequest);
+		return;
+	}
+
+	ret = setdma_rx(ep, req);
+}
+
+/*
+ * DATA_STATE_XMIT
+ */
+static int s3c_ep0_write(struct s3c_udc *dev)
+{
+	struct s3c_request *req;
+	struct s3c_ep *ep = &dev->ep[0];
+	int ret, need_zlp = 0;
+
+	if (list_empty(&ep->queue))
+		req = 0;
+	else
+		req = list_entry(ep->queue.next, struct s3c_request, queue);
+
+	if (!req) {
+		DEBUG_EP0("%s: NULL REQ\n", __func__);
+		return 0;
+	}
+
+	DEBUG_EP0("%s: req = %p, req.length = 0x%x, req.actual = 0x%x\n",
+		__func__, req, req->req.length, req->req.actual);
+
+	if (req->req.length - req->req.actual == ep0_fifo_size) {
+		/* Next write will end with the packet size, */
+		/* so we need Zero-length-packet */
+		need_zlp = 1;
+	}
+
+	ret = write_fifo_ep0(ep, req);
+
+	if ((ret == 1) && !need_zlp) {
+		/* Last packet */
+		dev->ep0state = WAIT_FOR_COMPLETE;
+		DEBUG_EP0("%s: finished, waiting for status\n", __func__);
+
+	} else {
+		dev->ep0state = DATA_STATE_XMIT;
+		DEBUG_EP0("%s: not finished\n", __func__);
+	}
+
+	return 1;
+}
+
+u16	g_status;
+
+static int s3c_udc_get_status(struct s3c_udc *dev,
+		struct usb_ctrlrequest *crq)
+{
+	u8 ep_num = crq->wIndex & 0x7F;
+	u32 ep_ctrl;
+	u32 *p = the_controller->dma_buf[1];
+
+
+	DEBUG_SETUP("%s: *** USB_REQ_GET_STATUS\n", __func__);
+
+	switch (crq->bRequestType & USB_RECIP_MASK) {
+	case USB_RECIP_INTERFACE:
+		g_status = 0;
+		DEBUG_SETUP("\tGET_STATUS:USB_RECIP_INTERFACE, g_stauts = %d\n",
+			    g_status);
+		break;
+
+	case USB_RECIP_DEVICE:
+		g_status = 0x1; /* Self powered */
+		DEBUG_SETUP("\tGET_STATUS: USB_RECIP_DEVICE, g_stauts = %d\n",
+			    g_status);
+		break;
+
+	case USB_RECIP_ENDPOINT:
+		if (crq->wLength > 2) {
+			DEBUG_SETUP("\tGET_STATUS:Not support EP or wLength\n");
+			return 1;
+		}
+
+		g_status = dev->ep[ep_num].stopped;
+		DEBUG_SETUP("\tGET_STATUS: USB_RECIP_ENDPOINT, g_stauts = %d\n",
+			    g_status);
+
+		break;
+
+	default:
+		return 1;
+	}
+
+	memcpy(p, &g_status, sizeof(g_status));
+
+	writel(the_controller->dma_addr[1], S3C_UDC_OTG_DIEPDMA(EP0_CON));
+	writel((1<<19)|(2<<0), S3C_UDC_OTG_DIEPTSIZ(EP0_CON));
+
+	ep_ctrl = readl(S3C_UDC_OTG_DIEPCTL(EP0_CON));
+	writel(ep_ctrl|DEPCTL_EPENA|DEPCTL_CNAK, S3C_UDC_OTG_DIEPCTL(EP0_CON));
+	dev->ep0state = WAIT_FOR_NULL_COMPLETE;
+
+	return 0;
+}
+
+static void s3c_udc_set_nak(struct s3c_ep *ep)
+{
+	u8		ep_num;
+	u32		ep_ctrl = 0;
+
+	ep_num = ep_index(ep);
+	DEBUG("%s: ep_num = %d, ep_type = %d\n", __func__, ep_num, ep->ep_type);
+
+	if (ep_is_in(ep)) {
+		ep_ctrl = readl(S3C_UDC_OTG_DIEPCTL(ep_num));
+		ep_ctrl |= DEPCTL_SNAK;
+		writel(ep_ctrl, S3C_UDC_OTG_DIEPCTL(ep_num));
+		DEBUG("%s: set NAK, DIEPCTL%d = 0x%x\n",
+			__func__, ep_num, readl(S3C_UDC_OTG_DIEPCTL(ep_num)));
+	} else {
+		ep_ctrl = readl(S3C_UDC_OTG_DOEPCTL(ep_num));
+		ep_ctrl |= DEPCTL_SNAK;
+		writel(ep_ctrl, S3C_UDC_OTG_DOEPCTL(ep_num));
+		DEBUG("%s: set NAK, DOEPCTL%d = 0x%x\n",
+			__func__, ep_num, readl(S3C_UDC_OTG_DOEPCTL(ep_num)));
+	}
+
+	return;
+}
+
+
+void s3c_udc_ep_set_stall(struct s3c_ep *ep)
+{
+	u8		ep_num;
+	u32		ep_ctrl = 0;
+
+	ep_num = ep_index(ep);
+	DEBUG("%s: ep_num = %d, ep_type = %d\n", __func__, ep_num, ep->ep_type);
+
+	if (ep_is_in(ep)) {
+		ep_ctrl = readl(S3C_UDC_OTG_DIEPCTL(ep_num));
+
+		/* set the disable and stall bits */
+		if (ep_ctrl & DEPCTL_EPENA)
+			ep_ctrl |= DEPCTL_EPDIS;
+
+		ep_ctrl |= DEPCTL_STALL;
+
+		writel(ep_ctrl, S3C_UDC_OTG_DIEPCTL(ep_num));
+		DEBUG("%s: set stall, DIEPCTL%d = 0x%x\n",
+			__func__, ep_num, readl(S3C_UDC_OTG_DIEPCTL(ep_num)));
+
+	} else {
+		ep_ctrl = readl(S3C_UDC_OTG_DOEPCTL(ep_num));
+
+		/* set the stall bit */
+		ep_ctrl |= DEPCTL_STALL;
+
+		writel(ep_ctrl, S3C_UDC_OTG_DOEPCTL(ep_num));
+		DEBUG("%s: set stall, DOEPCTL%d = 0x%x\n",
+			__func__, ep_num, readl(S3C_UDC_OTG_DOEPCTL(ep_num)));
+	}
+
+	return;
+}
+
+void s3c_udc_ep_clear_stall(struct s3c_ep *ep)
+{
+	u8		ep_num;
+	u32		ep_ctrl = 0;
+
+	ep_num = ep_index(ep);
+	DEBUG("%s: ep_num = %d, ep_type = %d\n", __func__, ep_num, ep->ep_type);
+
+	if (ep_is_in(ep)) {
+		ep_ctrl = readl(S3C_UDC_OTG_DIEPCTL(ep_num));
+
+		/* clear stall bit */
+		ep_ctrl &= ~DEPCTL_STALL;
+
+		/*
+		 * USB Spec 9.4.5: For endpoints using data toggle, regardless
+		 * of whether an endpoint has the Halt feature set, a
+		 * ClearFeature(ENDPOINT_HALT) request always results in the
+		 * data toggle being reinitialized to DATA0.
+		 */
+		if (ep->bmAttributes == USB_ENDPOINT_XFER_INT
+		    || ep->bmAttributes == USB_ENDPOINT_XFER_BULK) {
+			ep_ctrl |= DEPCTL_SETD0PID; /* DATA0 */
+		}
+
+		writel(ep_ctrl, S3C_UDC_OTG_DIEPCTL(ep_num));
+		DEBUG("%s: cleared stall, DIEPCTL%d = 0x%x\n",
+			__func__, ep_num, readl(S3C_UDC_OTG_DIEPCTL(ep_num)));
+
+	} else {
+		ep_ctrl = readl(S3C_UDC_OTG_DOEPCTL(ep_num));
+
+		/* clear stall bit */
+		ep_ctrl &= ~DEPCTL_STALL;
+
+		if (ep->bmAttributes == USB_ENDPOINT_XFER_INT
+		    || ep->bmAttributes == USB_ENDPOINT_XFER_BULK) {
+			ep_ctrl |= DEPCTL_SETD0PID; /* DATA0 */
+		}
+
+		writel(ep_ctrl, S3C_UDC_OTG_DOEPCTL(ep_num));
+		DEBUG("%s: cleared stall, DOEPCTL%d = 0x%x\n",
+			__func__, ep_num, readl(S3C_UDC_OTG_DOEPCTL(ep_num)));
+	}
+
+	return;
+}
+
+static int s3c_udc_set_halt(struct usb_ep *_ep, int value)
+{
+	struct s3c_ep	*ep;
+	struct s3c_udc	*dev;
+	unsigned long	flags;
+	u8		ep_num;
+
+	ep = container_of(_ep, struct s3c_ep, ep);
+	ep_num = ep_index(ep);
+
+	if (unlikely (!_ep || !ep->desc || ep_num == EP0_CON ||
+			ep->desc->bmAttributes == USB_ENDPOINT_XFER_ISOC)) {
+		DEBUG("%s: %s bad ep or descriptor\n", __func__, ep->ep.name);
+		return -EINVAL;
+	}
+
+	/* Attempt to halt IN ep will fail if any transfer requests
+	 * are still queue */
+	if (value && ep_is_in(ep) && !list_empty(&ep->queue)) {
+		DEBUG("%s: %s queue not empty, req = %p\n",
+			__func__, ep->ep.name,
+			list_entry(ep->queue.next, struct s3c_request, queue));
+
+		return -EAGAIN;
+	}
+
+	dev = ep->dev;
+	DEBUG("%s: ep_num = %d, value = %d\n", __func__, ep_num, value);
+
+	spin_lock_irqsave(&dev->lock, flags);
+
+	if (value == 0) {
+		ep->stopped = 0;
+		s3c_udc_ep_clear_stall(ep);
+	} else {
+		if (ep_num == 0)
+			dev->ep0state = WAIT_FOR_SETUP;
+
+		ep->stopped = 1;
+		s3c_udc_ep_set_stall(ep);
+	}
+
+	spin_unlock_irqrestore(&dev->lock, flags);
+
+	return 0;
+}
+
+void s3c_udc_ep_activate(struct s3c_ep *ep)
+{
+	u8 ep_num;
+	u32 ep_ctrl = 0, daintmsk = 0;
+
+	ep_num = ep_index(ep);
+
+	/* Read DEPCTLn register */
+	if (ep_is_in(ep)) {
+		ep_ctrl = readl(S3C_UDC_OTG_DIEPCTL(ep_num));
+		daintmsk = 1 << ep_num;
+	} else {
+		ep_ctrl = readl(S3C_UDC_OTG_DOEPCTL(ep_num));
+		daintmsk = (1 << ep_num) << DAINT_OUT_BIT;
+	}
+
+	DEBUG("%s: EPCTRL%d = 0x%x, ep_is_in = %d\n",
+		__func__, ep_num, ep_ctrl, ep_is_in(ep));
+
+	/* If the EP is already active don't change the EP Control
+	 * register. */
+	if (!(ep_ctrl & DEPCTL_USBACTEP)) {
+		ep_ctrl = (ep_ctrl & ~DEPCTL_TYPE_MASK) |
+			(ep->bmAttributes << DEPCTL_TYPE_BIT);
+		ep_ctrl = (ep_ctrl & ~DEPCTL_MPS_MASK) |
+			(ep->ep.maxpacket << DEPCTL_MPS_BIT);
+		ep_ctrl |= (DEPCTL_SETD0PID | DEPCTL_USBACTEP | DEPCTL_SNAK);
+
+		if (ep_is_in(ep)) {
+			writel(ep_ctrl, S3C_UDC_OTG_DIEPCTL(ep_num));
+			DEBUG("%s: USB Ative EP%d, DIEPCTRL%d = 0x%x\n",
+			      __func__, ep_num, ep_num,
+			      readl(S3C_UDC_OTG_DIEPCTL(ep_num)));
+		} else {
+			writel(ep_ctrl, S3C_UDC_OTG_DOEPCTL(ep_num));
+			DEBUG("%s: USB Ative EP%d, DOEPCTRL%d = 0x%x\n",
+			      __func__, ep_num, ep_num,
+			      readl(S3C_UDC_OTG_DOEPCTL(ep_num)));
+		}
+	}
+
+	/* Unmask EP Interrtupt */
+	writel(readl(S3C_UDC_OTG_DAINTMSK)|daintmsk, S3C_UDC_OTG_DAINTMSK);
+	DEBUG("%s: DAINTMSK = 0x%x\n", __func__, readl(S3C_UDC_OTG_DAINTMSK));
+
+}
+
+static int s3c_udc_clear_feature(struct usb_ep *_ep)
+{
+	struct s3c_udc	*dev;
+	struct s3c_ep	*ep;
+	u8		ep_num;
+
+	ep = container_of(_ep, struct s3c_ep, ep);
+	ep_num = ep_index(ep);
+
+	dev = ep->dev;
+	DEBUG_SETUP("%s: ep_num = %d, is_in = %d, clear_feature_flag = %d\n",
+		__func__, ep_num, ep_is_in(ep), clear_feature_flag);
+
+	if (usb_ctrl->wLength != 0) {
+		DEBUG_SETUP("\tCLEAR_FEATURE: wLength is not zero.....\n");
+		return 1;
+	}
+
+	switch (usb_ctrl->bRequestType & USB_RECIP_MASK) {
+	case USB_RECIP_DEVICE:
+		switch (usb_ctrl->wValue) {
+		case USB_DEVICE_REMOTE_WAKEUP:
+			DEBUG_SETUP("\tCLEAR_FEATURE:USB_DEVICE_REMOTE_WAKEUP\n");
+			break;
+
+		case USB_DEVICE_TEST_MODE:
+			DEBUG_SETUP("\tCLEAR_FEATURE: USB_DEVICE_TEST_MODE\n");
+			/** @todo Add CLEAR_FEATURE for TEST modes. */
+			break;
+		}
+
+		s3c_udc_ep0_zlp(dev);
+		break;
+
+	case USB_RECIP_ENDPOINT:
+		DEBUG_SETUP("\tCLEAR_FEATURE:USB_RECIP_ENDPOINT, wValue = %d\n",
+				usb_ctrl->wValue);
+
+		if (usb_ctrl->wValue == USB_ENDPOINT_HALT) {
+			if (ep_num == 0) {
+				s3c_udc_ep0_set_stall(ep);
+				return 0;
+			}
+
+			s3c_udc_ep0_zlp(dev);
+
+			s3c_udc_ep_clear_stall(ep);
+			s3c_udc_ep_activate(ep);
+			ep->stopped = 0;
+
+			clear_feature_num = ep_num;
+			clear_feature_flag = 1;
+		}
+		break;
+	}
+
+	return 0;
+}
+
+static int s3c_udc_set_feature(struct usb_ep *_ep)
+{
+	struct s3c_udc	*dev;
+	struct s3c_ep	*ep;
+	u8		ep_num;
+
+	ep = container_of(_ep, struct s3c_ep, ep);
+	ep_num = ep_index(ep);
+	dev = ep->dev;
+
+	DEBUG_SETUP("%s: *** USB_REQ_SET_FEATURE , ep_num = %d\n",
+		    __func__, ep_num);
+
+	if (usb_ctrl->wLength != 0) {
+		DEBUG_SETUP("\tSET_FEATURE: wLength is not zero.....\n");
+		return 1;
+	}
+
+	switch (usb_ctrl->bRequestType & USB_RECIP_MASK) {
+	case USB_RECIP_DEVICE:
+		switch (usb_ctrl->wValue) {
+		case USB_DEVICE_REMOTE_WAKEUP:
+			DEBUG_SETUP("\tSET_FEATURE:USB_DEVICE_REMOTE_WAKEUP\n");
+			break;
+		case USB_DEVICE_B_HNP_ENABLE:
+			DEBUG_SETUP("\tSET_FEATURE: USB_DEVICE_B_HNP_ENABLE\n");
+			break;
+
+		case USB_DEVICE_A_HNP_SUPPORT:
+			/* RH port supports HNP */
+			DEBUG_SETUP("\tSET_FEATURE:USB_DEVICE_A_HNP_SUPPORT\n");
+			break;
+
+		case USB_DEVICE_A_ALT_HNP_SUPPORT:
+			/* other RH port does */
+			DEBUG_SETUP("\tSET_FEATURE:USB_DEVICE_A_ALT_HNP_SUPPORT\n");
+			break;
+		}
+
+		s3c_udc_ep0_zlp(dev);
+		return 0;
+
+	case USB_RECIP_INTERFACE:
+		DEBUG_SETUP("\tSET_FEATURE: USB_RECIP_INTERFACE\n");
+		break;
+
+	case USB_RECIP_ENDPOINT:
+		DEBUG_SETUP("\tSET_FEATURE: USB_RECIP_ENDPOINT\n");
+		if (usb_ctrl->wValue == USB_ENDPOINT_HALT) {
+			if (ep_num == 0) {
+				s3c_udc_ep0_set_stall(ep);
+				return 0;
+			}
+			ep->stopped = 1;
+			s3c_udc_ep_set_stall(ep);
+		}
+
+		s3c_udc_ep0_zlp(dev);
+		return 0;
+	}
+
+	return 1;
+}
+
+/*
+ * WAIT_FOR_SETUP (OUT_PKT_RDY)
+ */
+static void s3c_ep0_setup(struct s3c_udc *dev)
+{
+	struct s3c_ep *ep = &dev->ep[0];
+	int i, bytes, is_in;
+	u8 ep_num;
+
+	/* Nuke all previous transfers */
+	nuke(ep, -EPROTO);
+
+	/* read control req from fifo (8 bytes) */
+	bytes = s3c_fifo_read(ep, (u32 *)usb_ctrl, 8);
+
+	DEBUG_SETUP("%s: bRequestType = 0x%x(%s), bRequest = 0x%x"
+		    "\twLength = 0x%x, wValue = 0x%x, wIndex= 0x%x\n",
+		    __func__, usb_ctrl->bRequestType,
+		    (usb_ctrl->bRequestType & USB_DIR_IN) ? "IN" : "OUT",
+		    usb_ctrl->bRequest,
+		    usb_ctrl->wLength, usb_ctrl->wValue, usb_ctrl->wIndex);
+
+#ifdef DEBUG_S3C_UDC
+	{
+		int i, len = sizeof(*usb_ctrl);
+		char *p = usb_ctrl;
+
+		printf("pkt = ");
+		for (i = 0; i < len; i++) {
+			printf("%02x", ((u8 *)p)[i]);
+			if ((i & 7) == 7)
+				printf(" ");
+		}
+		printf("\n");
+	}
+#endif
+
+
+	if (usb_ctrl->bRequest == GET_MAX_LUN_REQUEST &&
+	    usb_ctrl->wLength != 1) {
+		DEBUG_SETUP("\t%s:GET_MAX_LUN_REQUEST:invalid wLength = %d, setup returned\n",
+			__func__, usb_ctrl->wLength);
+
+		s3c_udc_ep0_set_stall(ep);
+		dev->ep0state = WAIT_FOR_SETUP;
+
+		return;
+	} else if (usb_ctrl->bRequest == BOT_RESET_REQUEST &&
+		 usb_ctrl->wLength != 0) {
+		/* Bulk-Only *mass storge reset of class-specific request */
+		DEBUG_SETUP("\t%s:BOT Rest:invalid wLength = %d, setup returned\n",
+			__func__, usb_ctrl->wLength);
+
+		s3c_udc_ep0_set_stall(ep);
+		dev->ep0state = WAIT_FOR_SETUP;
+
+		return;
+	}
+
+	/* Set direction of EP0 */
+	if (likely(usb_ctrl->bRequestType & USB_DIR_IN)) {
+		ep->bEndpointAddress |= USB_DIR_IN;
+		is_in = 1;
+
+	} else {
+		ep->bEndpointAddress &= ~USB_DIR_IN;
+		is_in = 0;
+	}
+	/* cope with automagic for some standard requests. */
+	dev->req_std = (usb_ctrl->bRequestType & USB_TYPE_MASK)
+		== USB_TYPE_STANDARD;
+	dev->req_config = 0;
+	dev->req_pending = 1;
+
+	/* Handle some SETUP packets ourselves */
+	if (dev->req_std) {
+		switch (usb_ctrl->bRequest) {
+		case USB_REQ_SET_ADDRESS:
+		DEBUG_SETUP("%s: *** USB_REQ_SET_ADDRESS (%d)\n",
+				__func__, usb_ctrl->wValue);
+
+			if (usb_ctrl->bRequestType
+				!= (USB_TYPE_STANDARD | USB_RECIP_DEVICE))
+				break;
+
+			udc_set_address(dev, usb_ctrl->wValue);
+			return;
+
+		case USB_REQ_SET_CONFIGURATION:
+			DEBUG_SETUP("=====================================\n");
+			DEBUG_SETUP("%s: USB_REQ_SET_CONFIGURATION (%d)\n",
+					__func__, usb_ctrl->wValue);
+
+			if (usb_ctrl->bRequestType == USB_RECIP_DEVICE) {
+				reset_available = 1;
+				dev->req_config = 1;
+			}
+			break;
+
+		case USB_REQ_GET_DESCRIPTOR:
+			DEBUG_SETUP("%s: *** USB_REQ_GET_DESCRIPTOR\n",
+				    __func__);
+			break;
+
+		case USB_REQ_SET_INTERFACE:
+			DEBUG_SETUP("%s: *** USB_REQ_SET_INTERFACE (%d)\n",
+					__func__, usb_ctrl->wValue);
+
+			if (usb_ctrl->bRequestType == USB_RECIP_INTERFACE) {
+				reset_available = 1;
+				dev->req_config = 1;
+			}
+			break;
+
+		case USB_REQ_GET_CONFIGURATION:
+			DEBUG_SETUP("%s: *** USB_REQ_GET_CONFIGURATION\n",
+				    __func__);
+			break;
+
+		case USB_REQ_GET_STATUS:
+			if (!s3c_udc_get_status(dev, usb_ctrl))
+				return;
+
+			break;
+
+		case USB_REQ_CLEAR_FEATURE:
+			ep_num = usb_ctrl->wIndex & 0x7f;
+
+			if (!s3c_udc_clear_feature(&dev->ep[ep_num].ep))
+				return;
+
+			break;
+
+		case USB_REQ_SET_FEATURE:
+			ep_num = usb_ctrl->wIndex & 0x7f;
+
+			if (!s3c_udc_set_feature(&dev->ep[ep_num].ep))
+				return;
+
+			break;
+
+		default:
+			DEBUG_SETUP("%s: *** Default of usb_ctrl->bRequest=0x%x"
+				"happened.\n", __func__, usb_ctrl->bRequest);
+			break;
+		}
+	}
+
+	if (likely(dev->driver)) {
+		/* device-2-host (IN) or no data setup command,
+		 * process immediately */
+		DEBUG_SETUP("%s:usb_ctrlreq will be passed to fsg_setup()\n",
+			    __func__);
+
+		spin_unlock(&dev->lock);
+		i = dev->driver->setup(&dev->gadget, usb_ctrl);
+		spin_lock(&dev->lock);
+
+		if (i < 0) {
+			if (dev->req_config) {
+				DEBUG_SETUP("\tconfig change 0x%02x fail %d?\n",
+					(u32)usb_ctrl->bRequest, i);
+				return;
+			}
+
+			/* setup processing failed, force stall */
+			s3c_udc_ep0_set_stall(ep);
+			dev->ep0state = WAIT_FOR_SETUP;
+
+			DEBUG_SETUP("\tdev->driver->setup failed (%d),"
+				    " bRequest = %d\n",
+				i, usb_ctrl->bRequest);
+
+
+		} else if (dev->req_pending) {
+			dev->req_pending = 0;
+			DEBUG_SETUP("\tdev->req_pending...\n");
+		}
+
+		DEBUG_SETUP("\tep0state = %s\n", state_names[dev->ep0state]);
+
+	}
+}
+
+/*
+ * handle ep0 interrupt
+ */
+static void s3c_handle_ep0(struct s3c_udc *dev)
+{
+	if (dev->ep0state == WAIT_FOR_SETUP) {
+		DEBUG_OUT_EP("%s: WAIT_FOR_SETUP\n", __func__);
+		s3c_ep0_setup(dev);
+
+	} else {
+		DEBUG_OUT_EP("%s: strange state!!(state = %s)\n",
+			__func__, state_names[dev->ep0state]);
+	}
+}
+
+static void s3c_ep0_kick(struct s3c_udc *dev, struct s3c_ep *ep)
+{
+	DEBUG_EP0("%s: ep_is_in = %d\n", __func__, ep_is_in(ep));
+	if (ep_is_in(ep)) {
+		dev->ep0state = DATA_STATE_XMIT;
+		s3c_ep0_write(dev);
+
+	} else {
+		dev->ep0state = DATA_STATE_RECV;
+		s3c_ep0_read(dev);
+	}
+}
diff --git a/include/configs/s5p_goni.h b/include/configs/s5p_goni.h
index 50726f7..f7d300c 100644
--- a/include/configs/s5p_goni.h
+++ b/include/configs/s5p_goni.h
@@ -229,4 +229,7 @@
 #define CONFIG_SYS_I2C_SPEED	50000
 #define CONFIG_I2C_MULTI_BUS
 #define CONFIG_SYS_MAX_I2C_BUS	7
+#define CONFIG_USB_GADGET		1
+#define CONFIG_USB_GADGET_S3C_UDC_OTG	1
+#define CONFIG_USB_GADGET_DUALSPEED	1
 #endif	/* __CONFIG_H */
diff --git a/include/usb/lin_gadget_compat.h b/include/usb/lin_gadget_compat.h
new file mode 100644
index 0000000..abea804
--- /dev/null
+++ b/include/usb/lin_gadget_compat.h
@@ -0,0 +1,69 @@
+/*
+ * Copyright (c) 2011 Samsung Electronics
+ * Lukasz Majewski <l.majewski@samsung.com>
+ *
+ * This is a Linux kernel compatibility layer for USB Gadget
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License as
+ * published by the Free Software Foundation; either version 2 of
+ * the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston,
+ * MA 02111-1307 USA
+ */
+
+#ifndef __LIN_COMPAT_H__
+#define __LIN_COMPAT_H__
+
+/* common */
+typedef int	spinlock_t;
+typedef int	wait_queue_head_t;
+typedef int	irqreturn_t;
+#define spin_lock_init(...)
+#define spin_lock(...)
+#define spin_lock_irqsave(lock, flags) do {flags = 1; } while (0)
+#define spin_unlock(...)
+#define spin_unlock_irqrestore(lock, flags) do {flags = 0; } while (0)
+#define disable_irq(...)
+#define enable_irq(...)
+
+#define mutex_init(...)
+#define mutex_lock(...)
+#define mutex_unlock(...)
+
+#define WARN_ON(x) if (x) {printf("WARNING in %s line %d\n" \
+				  , __FILE__, __LINE__); }
+
+#define printk printf
+
+#define KERN_WARNING
+#define KERN_ERR
+#define KERN_NOTICE
+#define KERN_DEBUG
+
+#define GFP_KERNEL	0
+
+#define IRQ_HANDLED	1
+
+#define ENOTSUPP	524	/* Operation is not supported */
+
+#define EXPORT_SYMBOL(x)
+
+#define dma_cache_maint(addr, size, mode) cache_flush()
+void cache_flush(void);
+
+#define kmalloc(size, type) malloc(size)
+#define kfree(addr) free(addr)
+#define mdelay(n) ({unsigned long msec = (n); while (msec--) udelay(1000); })
+
+#define __iomem
+
+#endif /* __LIN_COMPAT_H__ */
diff --git a/include/usb/s3c_udc.h b/include/usb/s3c_udc.h
new file mode 100644
index 0000000..8d74bf2
--- /dev/null
+++ b/include/usb/s3c_udc.h
@@ -0,0 +1,160 @@
+/*
+ * drivers/usb/gadget/s3c_udc.h
+ * Samsung S3C on-chip full/high speed USB device controllers
+ * Copyright (C) 2005 for Samsung Electronics
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+ *
+ */
+
+#ifndef __S3C_USB_GADGET
+#define __S3C_USB_GADGET
+
+
+#include <linux/usb/ch9.h>
+#include <linux/usb/gadget.h>
+#include <linux/list.h>
+#include <usb/lin_gadget_compat.h>
+
+/*-------------------------------------------------------------------------*/
+/* DMA bounce buffer size, 16K is enough even for mass storage */
+#define DMA_BUFFER_SIZE	(4096*4)
+
+#define EP0_FIFO_SIZE		64
+#define EP_FIFO_SIZE		512
+#define EP_FIFO_SIZE2		1024
+/* ep0-control, ep1in-bulk, ep2out-bulk, ep3in-int */
+#define S3C_MAX_ENDPOINTS	4
+#define S3C_MAX_HW_ENDPOINTS	16
+
+#define WAIT_FOR_SETUP          0
+#define DATA_STATE_XMIT         1
+#define DATA_STATE_NEED_ZLP     2
+#define WAIT_FOR_OUT_STATUS     3
+#define DATA_STATE_RECV         4
+#define WAIT_FOR_COMPLETE	5
+#define WAIT_FOR_OUT_COMPLETE	6
+#define WAIT_FOR_IN_COMPLETE	7
+#define WAIT_FOR_NULL_COMPLETE	8
+
+#define TEST_J_SEL		0x1
+#define TEST_K_SEL		0x2
+#define TEST_SE0_NAK_SEL	0x3
+#define TEST_PACKET_SEL		0x4
+#define TEST_FORCE_ENABLE_SEL	0x5
+
+/* ************************************************************************* */
+/* IO
+ */
+
+typedef enum ep_type {
+	ep_control, ep_bulk_in, ep_bulk_out, ep_interrupt
+} ep_type_t;
+
+struct s3c_ep {
+	struct usb_ep ep;
+	struct s3c_udc *dev;
+
+	const struct usb_endpoint_descriptor *desc;
+	struct list_head queue;
+	unsigned long pio_irqs;
+	int len;
+	void *dma_buf;
+
+	u8 stopped;
+	u8 bEndpointAddress;
+	u8 bmAttributes;
+
+	ep_type_t ep_type;
+	int fifo_num;
+};
+
+struct s3c_request {
+	struct usb_request req;
+	struct list_head queue;
+};
+
+struct s3c_udc {
+	struct usb_gadget gadget;
+	struct usb_gadget_driver *driver;
+
+	struct s3c_plat_otg_data *pdata;
+	spinlock_t lock;
+	void *dma_buf[S3C_MAX_ENDPOINTS+1];
+	dma_addr_t dma_addr[S3C_MAX_ENDPOINTS+1];
+
+	int ep0state;
+	struct s3c_ep ep[S3C_MAX_ENDPOINTS];
+
+	unsigned char usb_address;
+
+	unsigned req_pending:1, req_std:1, req_config:1;
+};
+
+extern struct s3c_udc *the_controller;
+
+#define ep_is_in(EP) (((EP)->bEndpointAddress&USB_DIR_IN) == USB_DIR_IN)
+#define ep_index(EP) ((EP)->bEndpointAddress&0xF)
+#define ep_maxpacket(EP) ((EP)->ep.maxpacket)
+
+/*-------------------------------------------------------------------------*/
+/* #define DEBUG_UDC */
+#ifdef DEBUG_UDC
+#define DBG(stuff...)		printf("udc: " stuff)
+#else
+#define DBG(stuff...)		do {} while (0)
+#endif
+
+#ifdef DEBUG_S3C_UDC_SETUP
+#define DEBUG_SETUP(fmt, args...) printk(fmt, ##args)
+#else
+#define DEBUG_SETUP(fmt, args...) do {} while (0)
+#endif
+
+#ifdef DEBUG_S3C_UDC_EP0
+#define DEBUG_EP0(fmt, args...) printk(fmt, ##args)
+#else
+#define DEBUG_EP0(fmt, args...) do {} while (0)
+#endif
+
+#ifdef DEBUG_S3C_UDC
+#define DEBUG(fmt, args...) printk(fmt, ##args)
+#else
+#define DEBUG(fmt, args...) do {} while (0)
+#endif
+
+#ifdef DEBUG_S3C_UDC_ISR
+#define DEBUG_ISR(fmt, args...) printk(fmt, ##args)
+#else
+#define DEBUG_ISR(fmt, args...) do {} while (0)
+#endif
+
+#ifdef DEBUG_S3C_UDC_OUT_EP
+#define DEBUG_OUT_EP(fmt, args...) printk(fmt, ##args)
+#else
+#define DEBUG_OUT_EP(fmt, args...) do {} while (0)
+#endif
+
+#ifdef DEBUG_S3C_UDC_IN_EP
+#define DEBUG_IN_EP(fmt, args...) printk(fmt, ##args)
+#else
+#define DEBUG_IN_EP(fmt, args...) do {} while (0)
+#endif
+
+#define ERR(stuff...)		printf("ERR udc: " stuff)
+#define WARN(stuff...)		printf("WARNING udc: " stuff)
+#define INFO(stuff...)		printf("INFO udc: " stuff)
+
+#endif
-- 
1.7.2.3

^ permalink raw reply related	[flat|nested] 9+ messages in thread

* [U-Boot] [PATCH 2/2] usb:gadget: USB Mass Storage Gadget support.
  2011-07-05 12:53 [U-Boot] [PATCH 0/2] usb:gadget: Linux USB Gadget framework Lukasz Majewski
  2011-07-05 12:53 ` [U-Boot] [PATCH 1/2] " Lukasz Majewski
@ 2011-07-05 12:53 ` Lukasz Majewski
  2011-07-05 14:08 ` [U-Boot] [PATCH 0/2] usb:gadget: Linux USB Gadget framework Wolfgang Denk
                   ` (2 subsequent siblings)
  4 siblings, 0 replies; 9+ messages in thread
From: Lukasz Majewski @ 2011-07-05 12:53 UTC (permalink / raw)
  To: u-boot

This is one of gadgets available for Linux kernel's USB Gadget
framework.

Signed-off-by: Lukasz Majewski <l.majewski@samsung.com>
---
 board/samsung/goni/goni.c           |   68 +
 common/Makefile                     |    1 +
 common/cmd_usb_mass_storage.c       |   67 +
 drivers/usb/gadget/Makefile         |    5 +
 drivers/usb/gadget/file_storage.c   | 3533 +++++++++++++++++++++++++++++++++++
 drivers/usb/gadget/storage_common.c |  762 ++++++++
 include/configs/s5p_goni.h          |   10 +
 include/usb_mass_storage.h          |   36 +
 8 files changed, 4482 insertions(+), 0 deletions(-)
 create mode 100644 common/cmd_usb_mass_storage.c
 create mode 100644 drivers/usb/gadget/file_storage.c
 create mode 100644 drivers/usb/gadget/storage_common.c
 create mode 100644 include/usb_mass_storage.h

diff --git a/board/samsung/goni/goni.c b/board/samsung/goni/goni.c
index b594bb2..efeecb5 100644
--- a/board/samsung/goni/goni.c
+++ b/board/samsung/goni/goni.c
@@ -30,6 +30,8 @@
 #include <asm/arch/hs_otg.h>
 #include <asm/arch/regs-otg.h>
 
+#include <usb_mass_storage.h>
+
 DECLARE_GLOBAL_DATA_PTR;
 
 static struct s5pc110_gpio *s5pc110_gpio;
@@ -198,3 +200,69 @@ struct s3c_plat_otg_data s5pc110_otg_data = {
 	.regs_otg = S5PC110_OTG_BASE,
 };
 #endif
+
+#ifdef CONFIG_USB_GADGET_MASS_STORAGE
+static int ums_read_sector(struct ums_device *ums_dev,
+			   unsigned int n, void *buf)
+{
+	if (ums_dev->mmc->block_dev.block_read(ums_dev->dev_num,
+					      n + ums_dev->offset, 1, buf) != 1)
+		return -1;
+
+	return 0;
+}
+
+static int ums_write_sector(struct ums_device *ums_dev,
+			    unsigned int n, void *buf)
+{
+	if (ums_dev->mmc->block_dev.block_write(ums_dev->dev_num,
+					      n + ums_dev->offset, 1, buf) != 1)
+		return -1;
+
+	return 0;
+}
+
+static void ums_get_capacity(struct ums_device *ums_dev,
+			     long long int *capacity)
+{
+	long long int tmp_capacity;
+
+	tmp_capacity = (long long int) ((ums_dev->offset + ums_dev->part_size)
+					* SECTOR_SIZE);
+	*capacity = ums_dev->mmc->capacity - tmp_capacity;
+}
+
+static struct ums_board_info ums_board = {
+	.read_sector = ums_read_sector,
+	.write_sector = ums_write_sector,
+	.get_capacity = ums_get_capacity,
+	.name = "GONI UMS disk",
+	.ums_dev = {
+		.mmc = NULL,
+		.dev_num = 0,
+		.offset = 0,
+		.part_size = 0.
+	},
+};
+
+struct ums_board_info *board_ums_init(unsigned int dev_num, unsigned int offset,
+				      unsigned int part_size)
+{
+	struct mmc *mmc;
+
+	mmc = find_mmc_device(dev_num);
+	if (!mmc)
+		return NULL;
+
+	ums_board.ums_dev.mmc = mmc;
+	ums_board.ums_dev.dev_num = dev_num;
+	ums_board.ums_dev.offset = offset;
+	ums_board.ums_dev.part_size = part_size;
+
+	/* Init MMC */
+	mmc_init(mmc);
+
+	s3c_udc_probe(&s5pc110_otg_data);
+	return &ums_board;
+}
+#endif
diff --git a/common/Makefile b/common/Makefile
index 224b7cc..2fc1437 100644
--- a/common/Makefile
+++ b/common/Makefile
@@ -158,6 +158,7 @@ COBJS-y += cmd_usb.o
 COBJS-y += usb.o
 COBJS-$(CONFIG_USB_STORAGE) += usb_storage.o
 endif
+COBJS-$(CONFIG_CMD_USB_MASS_STORAGE) += cmd_usb_mass_storage.o
 COBJS-$(CONFIG_CMD_XIMG) += cmd_ximg.o
 COBJS-$(CONFIG_YAFFS2) += cmd_yaffs2.o
 
diff --git a/common/cmd_usb_mass_storage.c b/common/cmd_usb_mass_storage.c
new file mode 100644
index 0000000..d030a9d
--- /dev/null
+++ b/common/cmd_usb_mass_storage.c
@@ -0,0 +1,67 @@
+/*
+ * Copyright (C) 2011 Samsung Electrnoics
+ * Lukasz Majewski <l.majewski@samsung.com>
+ *
+ */
+
+#include <errno.h>
+#include <common.h>
+#include <command.h>
+#include <usb_mass_storage.h>
+
+
+#undef UMS_DBG
+#define UMS_DBG(fmt,args...)	printf (fmt ,##args)
+/* #define UMS_DBG(...) */
+
+int do_usb_mass_storage(cmd_tbl_t *cmdtp, int flag, int argc, char *argv[])
+{
+	char *ep;
+	unsigned int dev_num = 0, offset = 0, part_size = 0;
+
+	struct ums_board_info* ums_info;
+
+	if (argc < 2) {
+		printf("usage: ums <dev> - e.g. ums 0\n");
+		return 0;
+	}
+
+	dev_num = (int)simple_strtoul(argv[1], &ep, 16);
+
+	if (dev_num) {
+		puts("\nSet eMMC device to 0! - e.g. ums 0\n");
+		goto fail;
+	}
+
+	ums_info = board_ums_init(dev_num, offset, part_size);
+
+	if (!ums_info) {
+		printf("MMC: %d -> NOT available\n", dev_num);
+		goto fail;
+	}
+
+	fsg_init(ums_info);
+	while (1)
+	{
+		int irq_res;
+		/* Handle control-c and timeouts */
+		if (ctrlc()) {
+			printf("The remote end did not respond in time.\n");
+			goto fail;
+		}
+
+		irq_res = usb_gadget_handle_interrupts();
+
+		/* Check if USB cable has been detached */
+		if (fsg_main_thread(NULL) == EIO)
+			goto fail;
+	}
+fail:
+	return -1;
+}
+
+U_BOOT_CMD(ums, CONFIG_SYS_MAXARGS, 1, do_usb_mass_storage,
+	"Use the UMS [User Mass Storage]",
+        "ums - User Mass Storage Gadget"
+);
+
diff --git a/drivers/usb/gadget/Makefile b/drivers/usb/gadget/Makefile
index 6717e4f..9139130 100644
--- a/drivers/usb/gadget/Makefile
+++ b/drivers/usb/gadget/Makefile
@@ -28,7 +28,12 @@ LIB	:= $(obj)libusb_gadget.o
 # new USB gadget layer dependencies
 ifdef CONFIG_USB_GADGET
 COBJS-y += epautoconf.o config.o usbstring.o
+COBJS-$(CONFIG_USB_ETHER) += ether.o rndis.o
+COBJS-$(CONFIG_USB_GADGET_MASS_STORAGE) += file_storage.o
+COBJS-$(CONFIG_DFU_GADGET) += dfu.o
+COBJS-$(CONFIG_USB_GADGET_AT91) += at91_udc.o
 COBJS-$(CONFIG_USB_GADGET_S3C_UDC_OTG) += s3c_udc_otg.o
+COBJS-$(CONFIG_USBDOWNLOAD_GADGET) += usbd_gadget.o
 else
 # Devices not related to the new gadget layer depend on CONFIG_USB_DEVICE
 ifdef CONFIG_USB_DEVICE
diff --git a/drivers/usb/gadget/file_storage.c b/drivers/usb/gadget/file_storage.c
new file mode 100644
index 0000000..d97473d
--- /dev/null
+++ b/drivers/usb/gadget/file_storage.c
@@ -0,0 +1,3533 @@
+/* /\* */
+/*  * file_storage.c -- File-backed USB Storage Gadget, for USB development */
+/*  * */
+/*  * Copyright (C) 2003-2008 Alan Stern */
+/*  * All rights reserved. */
+/*  * */
+/*  * Redistribution and use in source and binary forms, with or without */
+/*  * modification, are permitted provided that the following conditions */
+/*  * are met: */
+/*  * 1. Redistributions of source code must retain the above copyright */
+/*  *    notice, this list of conditions, and the following disclaimer, */
+/*  *    without modification. */
+/*  * 2. Redistributions in binary form must reproduce the above copyright */
+/*  *    notice, this list of conditions and the following disclaimer in the */
+/*  *    documentation and/or other materials provided with the distribution. */
+/*  * 3. The names of the above-listed copyright holders may not be used */
+/*  *    to endorse or promote products derived from this software without */
+/*  *    specific prior written permission. */
+/*  * */
+/*  * ALTERNATIVELY, this software may be distributed under the terms of the */
+/*  * GNU General Public License ("GPL") as published by the Free Software */
+/*  * Foundation, either version 2 of that License or (at your option) any */
+/*  * later version. */
+/*  * */
+/*  * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS */
+/*  * IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, */
+/*  * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR */
+/*  * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR */
+/*  * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, */
+/*  * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, */
+/*  * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR */
+/*  * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF */
+/*  * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING */
+/*  * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS */
+/*  * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */
+/*  *\/ */
+
+
+/* /\* */
+/*  * The File-backed Storage Gadget acts as a USB Mass Storage device, */
+/*  * appearing to the host as a disk drive or as a CD-ROM drive.  In addition */
+/*  * to providing an example of a genuinely useful gadget driver for a USB */
+/*  * device, it also illustrates a technique of double-buffering for increased */
+/*  * throughput.  Last but not least, it gives an easy way to probe the */
+/*  * behavior of the Mass Storage drivers in a USB host. */
+/*  * */
+/*  * Backing storage is provided by a regular file or a block device, specified */
+/*  * by the "file" module parameter.  Access can be limited to read-only by */
+/*  * setting the optional "ro" module parameter.  (For CD-ROM emulation, */
+/*  * access is always read-only.)  The gadget will indicate that it has */
+/*  * removable media if the optional "removable" module parameter is set. */
+/*  * */
+/*  * The gadget supports the Control-Bulk (CB), Control-Bulk-Interrupt (CBI), */
+/*  * and Bulk-Only (also known as Bulk-Bulk-Bulk or BBB) transports, selected */
+/*  * by the optional "transport" module parameter.  It also supports the */
+/*  * following protocols: RBC (0x01), ATAPI or SFF-8020i (0x02), QIC-157 (0c03), */
+/*  * UFI (0x04), SFF-8070i (0x05), and transparent SCSI (0x06), selected by */
+/*  * the optional "protocol" module parameter.  In addition, the default */
+/*  * Vendor ID, Product ID, release number and serial number can be overridden. */
+/*  * */
+/*  * There is support for multiple logical units (LUNs), each of which has */
+/*  * its own backing file.  The number of LUNs can be set using the optional */
+/*  * "luns" module parameter (anywhere from 1 to 8), and the corresponding */
+/*  * files are specified using comma-separated lists for "file" and "ro". */
+/*  * The default number of LUNs is taken from the number of "file" elements; */
+/*  * it is 1 if "file" is not given.  If "removable" is not set then a backing */
+/*  * file must be specified for each LUN.  If it is set, then an unspecified */
+/*  * or empty backing filename means the LUN's medium is not loaded.  Ideally */
+/*  * each LUN would be settable independently as a disk drive or a CD-ROM */
+/*  * drive, but currently all LUNs have to be the same type.  The CD-ROM */
+/*  * emulation includes a single data track and no audio tracks; hence there */
+/*  * need be only one backing file per LUN.  Note also that the CD-ROM block */
+/*  * length is set to 512 rather than the more common value 2048. */
+/*  * */
+/*  * Requirements are modest; only a bulk-in and a bulk-out endpoint are */
+/*  * needed (an interrupt-out endpoint is also needed for CBI).  The memory */
+/*  * requirement amounts to two 16K buffers, size configurable by a parameter. */
+/*  * Support is included for both full-speed and high-speed operation. */
+/*  * */
+/*  * Note that the driver is slightly non-portable in that it assumes a */
+/*  * single memory/DMA buffer will be useable for bulk-in, bulk-out, and */
+/*  * interrupt-in endpoints.  With most device controllers this isn't an */
+/*  * issue, but there may be some with hardware restrictions that prevent */
+/*  * a buffer from being used by more than one endpoint. */
+/*  * */
+/*  * Module options: */
+/*  * */
+/*  *	file=filename[,filename...] */
+/*  *				Required if "removable" is not set, names of */
+/*  *					the files or block devices used for */
+/*  *					backing storage */
+/*  *	ro=b[,b...]		Default false, booleans for read-only access */
+/*  *	removable		Default false, boolean for removable media */
+/*  *	luns=N			Default N = number of filenames, number of */
+/*  *					LUNs to support */
+/*  *	nofua=b[,b...]		Default false, booleans for ignore FUA flag */
+/*  *					in SCSI WRITE(10,12) commands */
+/*  *	stall			Default determined according to the type of */
+/*  *					USB device controller (usually true), */
+/*  *					boolean to permit the driver to halt */
+/*  *					bulk endpoints */
+/*  *	cdrom			Default false, boolean for whether to emulate */
+/*  *					a CD-ROM drive */
+/*  *	transport=XXX		Default BBB, transport name (CB, CBI, or BBB) */
+/*  *	protocol=YYY		Default SCSI, protocol name (RBC, 8020 or */
+/*  *					ATAPI, QIC, UFI, 8070, or SCSI; */
+/*  *					also 1 - 6) */
+/*  *	vendor=0xVVVV		Default 0x0525 (NetChip), USB Vendor ID */
+/*  *	product=0xPPPP		Default 0xa4a5 (FSG), USB Product ID */
+/*  *	release=0xRRRR		Override the USB release number (bcdDevice) */
+/*  *	serial=HHHH...		Override serial number (string of hex chars) */
+/*  *	buflen=N		Default N=16384, buffer size used (will be */
+/*  *					rounded down to a multiple of */
+/*  *					PAGE_CACHE_SIZE) */
+/*  * */
+/*  * If CONFIG_USB_FILE_STORAGE_TEST is not set, only the "file", "ro", */
+/*  * "removable", "luns", "nofua", "stall", and "cdrom" options are available; */
+/*  * default values are used for everything else. */
+/*  * */
+/*  * The pathnames of the backing files and the ro settings are available in */
+/*  * the attribute files "file", "nofua", and "ro" in the lun<n> subdirectory of */
+/*  * the gadget's sysfs directory.  If the "removable" option is set, writing to */
+/*  * these files will simulate ejecting/loading the medium (writing an empty */
+/*  * line means eject) and adjusting a write-enable tab.  Changes to the ro */
+/*  * setting are not allowed when the medium is loaded or if CD-ROM emulation */
+/*  * is being used. */
+/*  * */
+/*  * This gadget driver is heavily based on "Gadget Zero" by David Brownell. */
+/*  * The driver's SCSI command interface was based on the "Information */
+/*  * technology - Small Computer System Interface - 2" document from */
+/*  * X3T9.2 Project 375D, Revision 10L, 7-SEP-93, available at */
+/*  * <http://www.t10.org/ftp/t10/drafts/s2/s2-r10l.pdf>.  The single exception */
+/*  * is opcode 0x23 (READ FORMAT CAPACITIES), which was based on the */
+/*  * "Universal Serial Bus Mass Storage Class UFI Command Specification" */
+/*  * document, Revision 1.0, December 14, 1998, available at */
+/*  * <http://www.usb.org/developers/devclass_docs/usbmass-ufi10.pdf>. */
+/*  *\/ */
+
+
+/* /\* */
+/*  *				Driver Design */
+/*  * */
+/*  * The FSG driver is fairly straightforward.  There is a main kernel */
+/*  * thread that handles most of the work.  Interrupt routines field */
+/*  * callbacks from the controller driver: bulk- and interrupt-request */
+/*  * completion notifications, endpoint-0 events, and disconnect events. */
+/*  * Completion events are passed to the main thread by wakeup calls.  Many */
+/*  * ep0 requests are handled at interrupt time, but SetInterface, */
+/*  * SetConfiguration, and device reset requests are forwarded to the */
+/*  * thread in the form of "exceptions" using SIGUSR1 signals (since they */
+/*  * should interrupt any ongoing file I/O operations). */
+/*  * */
+/*  * The thread's main routine implements the standard command/data/status */
+/*  * parts of a SCSI interaction.  It and its subroutines are full of tests */
+/*  * for pending signals/exceptions -- all this polling is necessary since */
+/*  * the kernel has no setjmp/longjmp equivalents.  (Maybe this is an */
+/*  * indication that the driver really wants to be running in userspace.) */
+/*  * An important point is that so long as the thread is alive it keeps an */
+/*  * open reference to the backing file.  This will prevent unmounting */
+/*  * the backing file's underlying filesystem and could cause problems */
+/*  * during system shutdown, for example.  To prevent such problems, the */
+/*  * thread catches INT, TERM, and KILL signals and converts them into */
+/*  * an EXIT exception. */
+/*  * */
+/*  * In normal operation the main thread is started during the gadget's */
+/*  * fsg_bind() callback and stopped during fsg_unbind().  But it can also */
+/*  * exit when it receives a signal, and there's no point leaving the */
+/*  * gadget running when the thread is dead.  So just before the thread */
+/*  * exits, it deregisters the gadget driver.  This makes things a little */
+/*  * tricky: The driver is deregistered at two places, and the exiting */
+/*  * thread can indirectly call fsg_unbind() which in turn can tell the */
+/*  * thread to exit.  The first problem is resolved through the use of the */
+/*  * REGISTERED atomic bitflag; the driver will only be deregistered once. */
+/*  * The second problem is resolved by having fsg_unbind() check */
+/*  * fsg->state; it won't try to stop the thread if the state is already */
+/*  * FSG_STATE_TERMINATED. */
+/*  * */
+/*  * To provide maximum throughput, the driver uses a circular pipeline of */
+/*  * buffer heads (struct fsg_buffhd).  In principle the pipeline can be */
+/*  * arbitrarily long; in practice the benefits don't justify having more */
+/*  * than 2 stages (i.e., double buffering).  But it helps to think of the */
+/*  * pipeline as being a long one.  Each buffer head contains a bulk-in and */
+/*  * a bulk-out request pointer (since the buffer can be used for both */
+/*  * output and input -- directions always are given from the host's */
+/*  * point of view) as well as a pointer to the buffer and various state */
+/*  * variables. */
+/*  * */
+/*  * Use of the pipeline follows a simple protocol.  There is a variable */
+/*  * (fsg->next_buffhd_to_fill) that points to the next buffer head to use. */
+/*  * At any time that buffer head may still be in use from an earlier */
+/*  * request, so each buffer head has a state variable indicating whether */
+/*  * it is EMPTY, FULL, or BUSY.  Typical use involves waiting for the */
+/*  * buffer head to be EMPTY, filling the buffer either by file I/O or by */
+/*  * USB I/O (during which the buffer head is BUSY), and marking the buffer */
+/*  * head FULL when the I/O is complete.  Then the buffer will be emptied */
+/*  * (again possibly by USB I/O, during which it is marked BUSY) and */
+/*  * finally marked EMPTY again (possibly by a completion routine). */
+/*  * */
+/*  * A module parameter tells the driver to avoid stalling the bulk */
+/*  * endpoints wherever the transport specification allows.  This is */
+/*  * necessary for some UDCs like the SuperH, which cannot reliably clear a */
+ * halt on a bulk endpoint.  However, under certain circumstances the
+ * Bulk-only specification requires a stall.  In such cases the driver
+ * will halt the endpoint and set a flag indicating that it should clear
+ * the halt in software during the next device reset.  Hopefully this
+ * will permit everything to work correctly.  Furthermore, although the
+ * specification allows the bulk-out endpoint to halt when the host sends
+ * too much data, implementing this would cause an unavoidable race.
+ * The driver will always use the "no-stall" approach for OUT transfers.
+ *
+ * One subtle point concerns sending status-stage responses for ep0
+ * requests.  Some of these requests, such as device reset, can involve
+ * interrupting an ongoing file I/O operation, which might take an
+ * arbitrarily long time.  During that delay the host might give up on
+ * the original ep0 request and issue a new one.  When that happens the
+ * driver should not notify the host about completion of the original
+ * request, as the host will no longer be waiting for it.  So the driver
+ * assigns to each ep0 request a unique tag, and it keeps track of the
+ * tag value of the request associated with a long-running exception
+ * (device-reset, interface-change, or configuration-change).  When the
+ * exception handler is finished, the status-stage response is submitted
+ * only if the current ep0 request tag is equal to the exception request
+ * tag.  Thus only the most recently received ep0 request will get a
+ * status-stage response.
+ *
+ * Warning: This driver source file is too long.  It ought to be split up
+ * into a header file plus about 3 separate .c files, to handle the details
+ * of the Gadget, USB Mass Storage, and SCSI protocols.
+ */
+
+
+/* #define VERBOSE_DEBUG */
+/* #define DUMP_MSGS */
+
+#include <config.h>
+#include <malloc.h>
+#include <common.h>
+
+#include <linux/err.h>
+#include <linux/usb/ch9.h>
+#include <linux/usb/gadget.h>
+#include <usb_mass_storage.h>
+
+#include <asm/unaligned.h>
+#include <linux/usb/gadget.h>
+#include "gadget_chips.h"
+
+/*
+ * Kbuild is not very cooperative with respect to linking separately
+ * compiled library objects into one module.  So for now we won't use
+ * separate compilation ... ensuring init/exit sections work to shrink
+ * the runtime footprint, and giving us at least some parts of what
+ * a "gcc --combine ... part1.c part2.c part3.c ... " build would.
+ */
+
+/* #include "usbstring.c" */
+/* #include "config.c" */
+/* #include "epautoconf.c" */
+
+/*-------------------------------------------------------------------------*/
+
+#define DRIVER_DESC		"File-backed Storage Gadget"
+#define DRIVER_NAME		"g_file_storage"
+/* DRIVER_VERSION must be at least 6 characters long, as it is used
+ * to generate a fallback serial number. */
+#define DRIVER_VERSION		"20 November 2008"
+
+static       char fsg_string_manufacturer[64];
+static const char fsg_string_product[] = DRIVER_DESC;
+static       char fsg_string_serial[13];
+static const char fsg_string_config[] = "Self-powered";
+static const char fsg_string_interface[] = "Mass Storage";
+
+
+#include "storage_common.c"
+
+
+/*
+ * This driver assumes self-powered hardware and has no way for users to
+ * trigger remote wakeup.  It uses autoconfiguration to select endpoints
+ * and endpoint addresses.
+ */
+
+
+#define spin_lock_irqsave(lock,flags) do {flags = 1;} while (0)
+#define spin_unlock_irqrestore(lock,flags) do {flags = 0;} while (0)
+#define spin_lock_irq(lock) do {} while (0)
+#define spin_unlock_irq(lock) do {} while (0)
+#define spin_lock(lock) do {} while (0)
+#define spin_unlock(lock) do {} while (0)
+#define spin_lock_init(x) do {} while (0)
+#define init_rwsem(x)
+#define init_completion(x)
+#define wake_up_process(thread_task) do {} while (0)
+#define send_sig_info(sig,mode,thread_task) do {} while (0)
+typedef int spinlock_t;
+#define GFP_ATOMIC ((gfp_t) 0)
+#define GFP_KERNEL ((gfp_t) 0)
+#define set_current_state(...)		do {} while (0)
+#define __set_current_state(...)	do {} while (0)
+#define PAGE_CACHE_SHIFT	12
+#define PAGE_CACHE_SIZE		(1 << PAGE_CACHE_SHIFT)
+#define __user
+#define __init
+#define __exit
+#define __ref
+#define barrier() do {} while (0)
+#ifdef CONFIG_SMP
+#define smp_mb()	mb ()
+#define smp_rmb()	rmb ()
+#define smp_wmb()	wmb ()
+#else
+#define smp_mb()	barrier ()
+#define smp_rmb()	barrier ()
+#define smp_wmb()	barrier ()
+#endif
+#define try_to_freeze(...)		0
+#define set_freezable(...)		do { } while (0)
+//#define kmalloc(x,y)			malloc(x)
+//#define kfree(x)			free(x)
+#define kzalloc(size,flags)	calloc(size, 1)
+#define ENOTSUPP	524	/* Operation is not supported */
+#define kthread_create(...)	__builtin_return_address(0)
+#define signal_pending(x) 0
+#define schedule(x)	do {} while (0)
+#define msleep_interruptible(x)	0
+#define siginfo_t int
+#define allow_signal(x) do {} while (0)
+#define complete_and_exit(...) do {} while (0)
+#define DEVICE_ATTR(x,...) int dummy_##x
+#define kref_put(...) do {} while (0)
+#define complete(...) do {} while (0)
+#define dev_set_drvdata(...) do {} while (0)
+#define dev_set_name(...) do {} while (0)
+#define device_register(...) 0
+#define device_unregister(...) 0
+#define device_create_file(...) 0
+#define kref_get(...) do {} while (0)
+#define kref_init(...) do {} while (0)
+#define module_init(...)
+#define module_exit(...)
+#define wait_for_completion(...) do {} while (0)
+#define dev_get_drvdata(...) NULL
+
+struct kref {int x;};
+struct completion {int x;};
+
+inline void set_bit(int nr, volatile void * addr)
+{
+	int	mask;
+	volatile unsigned int *a = addr;
+
+	a += nr >> 5;
+	mask = 1 << (nr & 0x1f);
+	*a |= mask;
+}
+
+inline void clear_bit(int nr, volatile void * addr)
+{
+	int	mask;
+	volatile unsigned int *a = addr;
+
+	a += nr >> 5;
+	mask = 1 << (nr & 0x1f);
+	*a &= ~mask;
+}
+
+/*-------------------------------------------------------------------------*/
+
+
+/* Encapsulate the module parameter settings */
+
+static struct {
+	char		*file[FSG_MAX_LUNS];
+	int		ro[FSG_MAX_LUNS];
+	int		nofua[FSG_MAX_LUNS];
+	unsigned int	num_filenames;
+	unsigned int	num_ros;
+	unsigned int	num_nofuas;
+	unsigned int	nluns;
+
+	int		removable;
+	int		can_stall;
+	int		cdrom;
+
+	char		*transport_parm;
+	char		*protocol_parm;
+	unsigned short	vendor;
+	unsigned short	product;
+	unsigned short	release;
+	char		*serial;
+	unsigned int	buflen;
+
+	int		transport_type;
+	char		*transport_name;
+	int		protocol_type;
+	char		*protocol_name;
+
+} mod_data = {					// Default values
+	.transport_parm		= "BBB",
+	.protocol_parm		= "SCSI",
+	.removable		= 1,
+	.can_stall		= 0,
+	.cdrom			= 0,
+	.vendor			= FSG_VENDOR_ID,
+	.product		= FSG_PRODUCT_ID,
+	.release		= 0xffff,	// Use controller chip type
+	.buflen			= 16384,
+	};
+
+
+/* In the non-TEST version, only the module parameters listed above
+ * are available. */
+
+/*
+ * These definitions will permit the compiler to avoid generating code for
+ * parts of the driver that aren't used in the non-TEST version.  Even gcc
+ * can recognize when a test of a constant expression yields a dead code
+ * path.
+ */
+
+#define transport_is_bbb()	1
+#define transport_is_cbi()	0
+#define protocol_is_scsi()	1
+
+
+/*-------------------------------------------------------------------------*/
+
+
+struct fsg_dev {
+	/* lock protects: state, all the req_busy's, and cbbuf_cmnd */
+	spinlock_t		lock;
+	struct usb_gadget	*gadget;
+
+	/* filesem protects: backing files in use */
+	struct rw_semaphore	filesem;
+
+	/* reference counting: wait until all LUNs are released */
+	struct kref		ref;
+
+	struct usb_ep		*ep0;		// Handy copy of gadget->ep0
+	struct usb_request	*ep0req;	// For control responses
+	unsigned int		ep0_req_tag;
+	const char		*ep0req_name;
+
+	struct usb_request	*intreq;	// For interrupt responses
+	int			intreq_busy;
+	struct fsg_buffhd	*intr_buffhd;
+
+	unsigned int		bulk_out_maxpacket;
+	enum fsg_state		state;		// For exception handling
+	unsigned int		exception_req_tag;
+
+	u8			config, new_config;
+
+	unsigned int		running : 1;
+	unsigned int		bulk_in_enabled : 1;
+	unsigned int		bulk_out_enabled : 1;
+	unsigned int		intr_in_enabled : 1;
+	unsigned int		phase_error : 1;
+	unsigned int		short_packet_received : 1;
+	unsigned int		bad_lun_okay : 1;
+
+	unsigned long		atomic_bitflags;
+#define REGISTERED		0
+#define IGNORE_BULK_OUT		1
+#define SUSPENDED		2
+
+	struct usb_ep		*bulk_in;
+	struct usb_ep		*bulk_out;
+	struct usb_ep		*intr_in;
+
+	struct fsg_buffhd	*next_buffhd_to_fill;
+	struct fsg_buffhd	*next_buffhd_to_drain;
+	struct fsg_buffhd	buffhds[FSG_NUM_BUFFERS];
+
+	int			thread_wakeup_needed;
+	struct completion	thread_notifier;
+	struct task_struct	*thread_task;
+
+	int			cmnd_size;
+	u8			cmnd[MAX_COMMAND_SIZE];
+	enum data_direction	data_dir;
+	u32			data_size;
+	u32			data_size_from_cmnd;
+	u32			tag;
+	unsigned int		lun;
+	u32			residue;
+	u32			usb_amount_left;
+
+	/* The CB protocol offers no way for a host to know when a command
+	 * has completed.  As a result the next command may arrive early,
+	 * and we will still have to handle it.  For that reason we need
+	 * a buffer to store new commands when using CB (or CBI, which
+	 * does not oblige a host to wait for command completion either). */
+	int			cbbuf_cmnd_size;
+	u8			cbbuf_cmnd[MAX_COMMAND_SIZE];
+
+	unsigned int		nluns;
+	struct fsg_lun		*luns;
+	struct fsg_lun		*curlun;
+};
+
+typedef void (*fsg_routine_t)(struct fsg_dev *);
+
+static int exception_in_progress(struct fsg_dev *fsg)
+{
+	return fsg->state > FSG_STATE_IDLE;
+}
+
+/* Make bulk-out requests be divisible by the maxpacket size */
+static void set_bulk_out_req_length(struct fsg_dev *fsg,
+		struct fsg_buffhd *bh, unsigned int length)
+{
+	unsigned int	rem;
+
+	bh->bulk_out_intended_length = length;
+	rem = length % fsg->bulk_out_maxpacket;
+	if (rem > 0)
+		length += fsg->bulk_out_maxpacket - rem;
+	bh->outreq->length = length;
+}
+
+static struct fsg_dev			*the_fsg;
+struct ums_board_info			*ums_info;
+
+
+/*-------------------------------------------------------------------------*/
+
+static int fsg_set_halt(struct fsg_dev *fsg, struct usb_ep *ep)
+{
+	const char	*name;
+
+	if (ep == fsg->bulk_in)
+		name = "bulk-in";
+	else if (ep == fsg->bulk_out)
+		name = "bulk-out";
+	else
+		name = ep->name;
+	DBG(fsg, "%s set halt\n", name);
+	return usb_ep_set_halt(ep);
+}
+
+
+/*-------------------------------------------------------------------------*/
+
+/*
+ * DESCRIPTORS ... most are static, but strings and (full) configuration
+ * descriptors are built on demand.  Also the (static) config and interface
+ * descriptors are adjusted during fsg_bind().
+ */
+
+/* There is only one configuration. */
+#define	CONFIG_VALUE		1
+
+static struct usb_device_descriptor
+device_desc = {
+	.bLength =		sizeof device_desc,
+	.bDescriptorType =	USB_DT_DEVICE,
+
+	.bcdUSB =		cpu_to_le16(0x0200),
+	.bDeviceClass =		USB_CLASS_PER_INTERFACE,
+
+	/* The next three values can be overridden by module parameters */
+	.idVendor =		cpu_to_le16(FSG_VENDOR_ID),
+	.idProduct =		cpu_to_le16(FSG_PRODUCT_ID),
+	.bcdDevice =		cpu_to_le16(0xffff),
+
+	.iManufacturer =	FSG_STRING_MANUFACTURER,
+	.iProduct =		FSG_STRING_PRODUCT,
+	.iSerialNumber =	FSG_STRING_SERIAL,
+	.bNumConfigurations =	1,
+};
+
+static struct usb_config_descriptor
+config_desc = {
+	.bLength =		sizeof config_desc,
+	.bDescriptorType =	USB_DT_CONFIG,
+
+	/* wTotalLength computed by usb_gadget_config_buf() */
+	.bNumInterfaces =	1,
+	.bConfigurationValue =	CONFIG_VALUE,
+	.iConfiguration =	FSG_STRING_CONFIG,
+	.bmAttributes =		USB_CONFIG_ATT_ONE | USB_CONFIG_ATT_SELFPOWER,
+	.bMaxPower =		50, //CONFIG_USB_GADGET_VBUS_DRAW / 2,
+};
+
+
+static struct usb_qualifier_descriptor
+dev_qualifier = {
+	.bLength =		sizeof dev_qualifier,
+	.bDescriptorType =	USB_DT_DEVICE_QUALIFIER,
+
+	.bcdUSB =		cpu_to_le16(0x0200),
+	.bDeviceClass =		USB_CLASS_PER_INTERFACE,
+
+	.bNumConfigurations =	1,
+};
+
+
+
+/*
+ * Config descriptors must agree with the code that sets configurations
+ * and with code managing interfaces and their altsettings.  They must
+ * also handle different speeds and other-speed requests.
+ */
+static int populate_config_buf(struct usb_gadget *gadget,
+		u8 *buf, u8 type, unsigned index)
+{
+	enum usb_device_speed			speed = gadget->speed;
+	int					len;
+	const struct usb_descriptor_header	**function;
+
+	if (index > 0)
+		return -EINVAL;
+
+	if (gadget_is_dualspeed(gadget) && type == USB_DT_OTHER_SPEED_CONFIG)
+		speed = (USB_SPEED_FULL + USB_SPEED_HIGH) - speed;
+	function = gadget_is_dualspeed(gadget) && speed == USB_SPEED_HIGH
+		? (const struct usb_descriptor_header **)fsg_hs_function
+		: (const struct usb_descriptor_header **)fsg_fs_function;
+
+	/* for now, don't advertise srp-only devices */
+	if (!gadget_is_otg(gadget))
+		function++;
+
+	len = usb_gadget_config_buf(&config_desc, buf, EP0_BUFSIZE, function);
+	((struct usb_config_descriptor *) buf)->bDescriptorType = type;
+	return len;
+}
+
+
+/*-------------------------------------------------------------------------*/
+
+/* These routines may be called in process context or in_irq */
+
+/* Caller must hold fsg->lock */
+static void wakeup_thread(struct fsg_dev *fsg)
+{
+	/* Tell the main thread that something has happened */
+	fsg->thread_wakeup_needed = 1;
+	if (fsg->thread_task)
+		wake_up_process(fsg->thread_task);
+}
+
+
+static void raise_exception(struct fsg_dev *fsg, enum fsg_state new_state)
+{
+	unsigned long		flags;
+
+	/* Do nothing if a higher-priority exception is already in progress.
+	 * If a lower-or-equal priority exception is in progress, preempt it
+	 * and notify the main thread by sending it a signal. */
+	spin_lock_irqsave(&fsg->lock, flags);
+	if (fsg->state <= new_state) {
+		fsg->exception_req_tag = fsg->ep0_req_tag;
+		fsg->state = new_state;
+		if (fsg->thread_task)
+			send_sig_info(SIGUSR1, SEND_SIG_FORCED,
+					fsg->thread_task);
+		fsg->thread_wakeup_needed = 1;
+	}
+	spin_unlock_irqrestore(&fsg->lock, flags);
+}
+
+
+/*-------------------------------------------------------------------------*/
+
+/* The disconnect callback and ep0 routines.  These always run in_irq,
+ * except that ep0_queue() is called in the main thread to acknowledge
+ * completion of various requests: set config, set interface, and
+ * Bulk-only device reset. */
+
+static void fsg_disconnect(struct usb_gadget *gadget)
+{
+	struct fsg_dev		*fsg = get_gadget_data(gadget);
+
+	DBG(fsg, "disconnect or port reset\n");
+	raise_exception(fsg, FSG_STATE_DISCONNECT);
+}
+
+
+static int ep0_queue(struct fsg_dev *fsg)
+{
+	int	rc;
+
+	rc = usb_ep_queue(fsg->ep0, fsg->ep0req, GFP_ATOMIC);
+	if (rc != 0 && rc != -ESHUTDOWN) {
+
+		/* We can't do much more than wait for a reset */
+		WARNING(fsg, "error in submission: %s --> %d\n",
+				fsg->ep0->name, rc);
+	}
+	return rc;
+}
+
+static void ep0_complete(struct usb_ep *ep, struct usb_request *req)
+{
+	struct fsg_dev		*fsg = ep->driver_data;
+
+	if (req->actual > 0)
+		dump_msg(fsg, fsg->ep0req_name, req->buf, req->actual);
+	if (req->status || req->actual != req->length)
+		DBG(fsg, "%s --> %d, %u/%u\n", __func__,
+				req->status, req->actual, req->length);
+	if (req->status == -ECONNRESET)		// Request was cancelled
+		usb_ep_fifo_flush(ep);
+
+	if (req->status == 0 && req->context)
+		((fsg_routine_t) (req->context))(fsg);
+}
+
+
+/*-------------------------------------------------------------------------*/
+
+/* Bulk and interrupt endpoint completion handlers.
+ * These always run in_irq. */
+
+static void bulk_in_complete(struct usb_ep *ep, struct usb_request *req)
+{
+	struct fsg_dev		*fsg = ep->driver_data;
+	struct fsg_buffhd	*bh = req->context;
+
+	if (req->status || req->actual != req->length)
+		DBG(fsg, "%s --> %d, %u/%u\n", __func__,
+				req->status, req->actual, req->length);
+	if (req->status == -ECONNRESET)		// Request was cancelled
+		usb_ep_fifo_flush(ep);
+
+	/* Hold the lock while we update the request and buffer states */
+	smp_wmb();
+	spin_lock(&fsg->lock);
+	bh->inreq_busy = 0;
+	bh->state = BUF_STATE_EMPTY;
+	wakeup_thread(fsg);
+	spin_unlock(&fsg->lock);
+}
+
+static void bulk_out_complete(struct usb_ep *ep, struct usb_request *req)
+{
+	struct fsg_dev		*fsg = ep->driver_data;
+	struct fsg_buffhd	*bh = req->context;
+
+	dump_msg(fsg, "bulk-out", req->buf, req->actual);
+	if (req->status || req->actual != bh->bulk_out_intended_length)
+		DBG(fsg, "%s --> %d, %u/%u\n", __func__,
+				req->status, req->actual,
+				bh->bulk_out_intended_length);
+	if (req->status == -ECONNRESET)		// Request was cancelled
+		usb_ep_fifo_flush(ep);
+
+	/* Hold the lock while we update the request and buffer states */
+	smp_wmb();
+	spin_lock(&fsg->lock);
+	bh->outreq_busy = 0;
+	bh->state = BUF_STATE_FULL;
+	wakeup_thread(fsg);
+	spin_unlock(&fsg->lock);
+}
+
+
+static void intr_in_complete(struct usb_ep *ep, struct usb_request *req)
+{}
+
+
+/*-------------------------------------------------------------------------*/
+
+/* Ep0 class-specific handlers.  These always run in_irq. */
+
+static void received_cbi_adsc(struct fsg_dev *fsg, struct fsg_buffhd *bh)
+{}
+
+
+static int class_setup_req(struct fsg_dev *fsg,
+		const struct usb_ctrlrequest *ctrl)
+{
+	struct usb_request	*req = fsg->ep0req;
+	int			value = -EOPNOTSUPP;
+	u16			w_index = le16_to_cpu(ctrl->wIndex);
+	u16                     w_value = le16_to_cpu(ctrl->wValue);
+	u16			w_length = le16_to_cpu(ctrl->wLength);
+
+	if (!fsg->config)
+		return value;
+
+	/* Handle Bulk-only class-specific requests */
+	if (transport_is_bbb()) {
+		switch (ctrl->bRequest) {
+
+		case USB_BULK_RESET_REQUEST:
+			if (ctrl->bRequestType != (USB_DIR_OUT |
+					USB_TYPE_CLASS | USB_RECIP_INTERFACE))
+				break;
+			if (w_index != 0 || w_value != 0) {
+				value = -EDOM;
+				break;
+			}
+
+			/* Raise an exception to stop the current operation
+			 * and reinitialize our state. */
+			DBG(fsg, "bulk reset request\n");
+			raise_exception(fsg, FSG_STATE_RESET);
+			value = DELAYED_STATUS;
+			break;
+
+		case USB_BULK_GET_MAX_LUN_REQUEST:
+			if (ctrl->bRequestType != (USB_DIR_IN |
+					USB_TYPE_CLASS | USB_RECIP_INTERFACE))
+				break;
+			if (w_index != 0 || w_value != 0) {
+				value = -EDOM;
+				break;
+			}
+			VDBG(fsg, "get max LUN\n");
+			*(u8 *) req->buf = fsg->nluns - 1;
+			value = 1;
+			break;
+		}
+	}
+
+	/* Handle CBI class-specific requests */
+	else {
+		switch (ctrl->bRequest) {
+
+		case USB_CBI_ADSC_REQUEST:
+			if (ctrl->bRequestType != (USB_DIR_OUT |
+					USB_TYPE_CLASS | USB_RECIP_INTERFACE))
+				break;
+			if (w_index != 0 || w_value != 0) {
+				value = -EDOM;
+				break;
+			}
+			if (w_length > MAX_COMMAND_SIZE) {
+				value = -EOVERFLOW;
+				break;
+			}
+			value = w_length;
+			fsg->ep0req->context = received_cbi_adsc;
+			break;
+		}
+	}
+
+	if (value == -EOPNOTSUPP)
+		VDBG(fsg,
+			"unknown class-specific control req "
+			"%02x.%02x v%04x i%04x l%u\n",
+			ctrl->bRequestType, ctrl->bRequest,
+			le16_to_cpu(ctrl->wValue), w_index, w_length);
+	return value;
+}
+
+
+/*-------------------------------------------------------------------------*/
+
+/* Ep0 standard request handlers.  These always run in_irq. */
+
+static int standard_setup_req(struct fsg_dev *fsg,
+		const struct usb_ctrlrequest *ctrl)
+{
+	struct usb_request	*req = fsg->ep0req;
+	int			value = -EOPNOTSUPP;
+	u16			w_index = le16_to_cpu(ctrl->wIndex);
+	u16			w_value = le16_to_cpu(ctrl->wValue);
+
+	/* Usually this just stores reply data in the pre-allocated ep0 buffer,
+	 * but config change events will also reconfigure hardware. */
+	switch (ctrl->bRequest) {
+
+	case USB_REQ_GET_DESCRIPTOR:
+		if (ctrl->bRequestType != (USB_DIR_IN | USB_TYPE_STANDARD |
+				USB_RECIP_DEVICE))
+			break;
+		switch (w_value >> 8) {
+
+		case USB_DT_DEVICE:
+			VDBG(fsg, "get device descriptor\n");
+			value = sizeof device_desc;
+			memcpy(req->buf, &device_desc, value);
+			break;
+		case USB_DT_DEVICE_QUALIFIER:
+			VDBG(fsg, "get device qualifier\n");
+			if (!gadget_is_dualspeed(fsg->gadget))
+				break;
+			value = sizeof dev_qualifier;
+			memcpy(req->buf, &dev_qualifier, value);
+			break;
+
+		case USB_DT_OTHER_SPEED_CONFIG:
+			VDBG(fsg, "get other-speed config descriptor\n");
+			if (!gadget_is_dualspeed(fsg->gadget))
+				break;
+			goto get_config;
+		case USB_DT_CONFIG:
+			VDBG(fsg, "get configuration descriptor\n");
+get_config:
+			value = populate_config_buf(fsg->gadget,
+					req->buf,
+					w_value >> 8,
+					w_value & 0xff);
+			break;
+
+		case USB_DT_STRING:
+			VDBG(fsg, "get string descriptor\n");
+
+			/* wIndex == language code */
+			value = usb_gadget_get_string(&fsg_stringtab,
+					w_value & 0xff, req->buf);
+			break;
+		}
+		break;
+
+	/* One config, two speeds */
+	case USB_REQ_SET_CONFIGURATION:
+		if (ctrl->bRequestType != (USB_DIR_OUT | USB_TYPE_STANDARD |
+				USB_RECIP_DEVICE))
+			break;
+		VDBG(fsg, "set configuration\n");
+		if (w_value == CONFIG_VALUE || w_value == 0) {
+			fsg->new_config = w_value;
+
+			/* Raise an exception to wipe out previous transaction
+			 * state (queued bufs, etc) and set the new config. */
+			raise_exception(fsg, FSG_STATE_CONFIG_CHANGE);
+			value = DELAYED_STATUS;
+		}
+		break;
+	case USB_REQ_GET_CONFIGURATION:
+		if (ctrl->bRequestType != (USB_DIR_IN | USB_TYPE_STANDARD |
+				USB_RECIP_DEVICE))
+			break;
+		VDBG(fsg, "get configuration\n");
+		*(u8 *) req->buf = fsg->config;
+		value = 1;
+		break;
+
+	case USB_REQ_SET_INTERFACE:
+		if (ctrl->bRequestType != (USB_DIR_OUT| USB_TYPE_STANDARD |
+				USB_RECIP_INTERFACE))
+			break;
+		if (fsg->config && w_index == 0) {
+
+			/* Raise an exception to wipe out previous transaction
+			 * state (queued bufs, etc) and install the new
+			 * interface altsetting. */
+			raise_exception(fsg, FSG_STATE_INTERFACE_CHANGE);
+			value = DELAYED_STATUS;
+		}
+		break;
+	case USB_REQ_GET_INTERFACE:
+		if (ctrl->bRequestType != (USB_DIR_IN | USB_TYPE_STANDARD |
+				USB_RECIP_INTERFACE))
+			break;
+		if (!fsg->config)
+			break;
+		if (w_index != 0) {
+			value = -EDOM;
+			break;
+		}
+		VDBG(fsg, "get interface\n");
+		*(u8 *) req->buf = 0;
+		value = 1;
+		break;
+
+	default:
+		VDBG(fsg,
+			"unknown control req %02x.%02x v%04x i%04x l%u\n",
+			ctrl->bRequestType, ctrl->bRequest,
+			w_value, w_index, le16_to_cpu(ctrl->wLength));
+	}
+
+	return value;
+}
+
+
+static int fsg_setup(struct usb_gadget *gadget,
+		const struct usb_ctrlrequest *ctrl)
+{
+	struct fsg_dev		*fsg = get_gadget_data(gadget);
+	int			rc;
+	int			w_length = le16_to_cpu(ctrl->wLength);
+
+	++fsg->ep0_req_tag;		// Record arrival of a new request
+	fsg->ep0req->context = NULL;
+	fsg->ep0req->length = 0;
+	dump_msg(fsg, "ep0-setup", (u8 *) ctrl, sizeof(*ctrl));
+
+	if ((ctrl->bRequestType & USB_TYPE_MASK) == USB_TYPE_CLASS)
+		rc = class_setup_req(fsg, ctrl);
+	else
+		rc = standard_setup_req(fsg, ctrl);
+
+	/* Respond with data/status or defer until later? */
+	if (rc >= 0 && rc != DELAYED_STATUS) {
+		rc = min(rc, w_length);
+		fsg->ep0req->length = rc;
+		fsg->ep0req->zero = rc < w_length;
+		fsg->ep0req_name = (ctrl->bRequestType & USB_DIR_IN ?
+				"ep0-in" : "ep0-out");
+		rc = ep0_queue(fsg);
+	}
+
+	/* Device either stalls (rc < 0) or reports success */
+	return rc;
+}
+
+
+/*-------------------------------------------------------------------------*/
+
+/* All the following routines run in process context */
+
+
+/* Use this for bulk or interrupt transfers, not ep0 */
+static void start_transfer(struct fsg_dev *fsg, struct usb_ep *ep,
+		struct usb_request *req, int *pbusy,
+		enum fsg_buffer_state *state)
+{
+	int	rc;
+
+	if (ep == fsg->bulk_in)
+		dump_msg(fsg, "bulk-in", req->buf, req->length);
+	else if (ep == fsg->intr_in)
+		dump_msg(fsg, "intr-in", req->buf, req->length);
+
+	spin_lock_irq(&fsg->lock);
+	*pbusy = 1;
+	*state = BUF_STATE_BUSY;
+	spin_unlock_irq(&fsg->lock);
+	rc = usb_ep_queue(ep, req, GFP_KERNEL);
+	if (rc != 0) {
+		*pbusy = 0;
+		*state = BUF_STATE_EMPTY;
+
+		/* We can't do much more than wait for a reset */
+
+		/* Note: currently the net2280 driver fails zero-length
+		 * submissions if DMA is enabled. */
+		if (rc != -ESHUTDOWN && !(rc == -EOPNOTSUPP &&
+						req->length == 0))
+			WARNING(fsg, "error in submission: %s --> %d\n",
+					ep->name, rc);
+	}
+}
+
+static void busy_indicator(void) {
+	static int state = 0;
+	switch (state) {
+	case 0:
+		puts("\r|");break;
+	case 1:
+		puts("\r/");break;
+	case 2:
+		puts("\r-");break;
+	case 3:
+		puts("\r\\");break;
+	case 4:
+		puts("\r|");break;
+	case 5:
+		puts("\r/");break;
+	case 6:
+		puts("\r-");break;
+	case 7:
+		puts("\r\\");break;
+	}
+	if (state++ == 8)
+		state = 0;
+}
+
+static int sleep_thread(struct fsg_dev *fsg, int line)
+{
+	int rc = 0;
+	int i = 0, k = 0;
+
+	/* Wait until a signal arrives or we are woken up */
+	for (;;) {
+
+		set_current_state(TASK_INTERRUPTIBLE);
+		if (signal_pending(current)) {
+			rc = -EINTR;
+			break;
+		}
+		if (fsg->thread_wakeup_needed)
+			break;
+		schedule();
+		if (++i == 50000) {
+			busy_indicator();
+			i = 0;
+			k++;
+		}
+
+		usb_gadget_handle_interrupts();
+	}
+	__set_current_state(TASK_RUNNING);
+	fsg->thread_wakeup_needed = 0;
+	return rc;
+}
+
+
+/*-------------------------------------------------------------------------*/
+
+static int do_read(struct fsg_dev *fsg)
+{
+	struct fsg_lun		*curlun = fsg->curlun;
+	u32			lba;
+	struct fsg_buffhd	*bh;
+	int			rc;
+	u32			amount_left;
+	loff_t			file_offset, file_offset_tmp;
+	unsigned int		amount, amount_tmp;
+	unsigned int		partial_page;
+	ssize_t			nread;
+
+	/* Get the starting Logical Block Address and check that it's
+	 * not too big */
+	if (fsg->cmnd[0] == SC_READ_6)
+		lba = get_unaligned_be24(&fsg->cmnd[1]);
+	else {
+		lba = get_unaligned_be32(&fsg->cmnd[2]);
+
+		/* We allow DPO (Disable Page Out = don't save data in the
+		 * cache) and FUA (Force Unit Access = don't read from the
+		 * cache), but we don't implement them. */
+		if ((fsg->cmnd[1] & ~0x18) != 0) {
+			curlun->sense_data = SS_INVALID_FIELD_IN_CDB;
+			return -EINVAL;
+		}
+	}
+	if (lba >= curlun->num_sectors) {
+		curlun->sense_data = SS_LOGICAL_BLOCK_ADDRESS_OUT_OF_RANGE;
+		return -EINVAL;
+	}
+	file_offset = ((loff_t) lba) << 9;
+
+	/* Carry out the file reads */
+	amount_left = fsg->data_size_from_cmnd;
+	if (unlikely(amount_left == 0))
+		return -EIO;		// No default reply
+
+	for (;;) {
+
+		/* Figure out how much we need to read:
+		 * Try to read the remaining amount.
+		 * But don't read more than the buffer size.
+		 * And don't try to read past the end of the file.
+		 * Finally, if we're not at a page boundary, don't read past
+		 *	the next page.
+		 * If this means reading 0 then we were asked to read past
+		 *	the end of file. */
+		amount = min((unsigned int) amount_left, mod_data.buflen);
+		amount = min((loff_t) amount,
+				curlun->file_length - file_offset);
+		partial_page = file_offset & (PAGE_CACHE_SIZE - 1);
+		if (partial_page > 0)
+			amount = min(amount, (unsigned int) PAGE_CACHE_SIZE -
+					partial_page);
+
+		/* Wait for the next buffer to become available */
+		bh = fsg->next_buffhd_to_fill;
+		while (bh->state != BUF_STATE_EMPTY) {
+			rc = sleep_thread(fsg, __LINE__);
+			if (rc)
+				return rc;
+		}
+
+		/* If we were asked to read past the end of file,
+		 * end with an empty buffer. */
+		if (amount == 0) {
+			curlun->sense_data =
+					SS_LOGICAL_BLOCK_ADDRESS_OUT_OF_RANGE;
+			curlun->sense_data_info = file_offset >> 9;
+			curlun->info_valid = 1;
+			bh->inreq->length = 0;
+			bh->state = BUF_STATE_FULL;
+			break;
+		}
+
+		/* Perform the read */
+		file_offset_tmp = file_offset;
+		amount_tmp = amount;
+		nread = 0;
+		while (amount_tmp > 0) {
+			ums_info->read_sector(&(ums_info->ums_dev),
+			                      (file_offset_tmp + nread) / SECTOR_SIZE,
+					      (char __user *)bh->buf + nread);
+			amount_tmp -= SECTOR_SIZE;
+			nread += SECTOR_SIZE;
+		}
+		file_offset_tmp += amount;
+
+		VLDBG(curlun, "file read %u @ %llu -> %d\n", amount,
+				(unsigned long long) file_offset,
+				(int) nread);
+		if (signal_pending(current))
+			return -EINTR;
+
+		if (nread < 0) {
+			LDBG(curlun, "error in file read: %d\n",
+					(int) nread);
+			nread = 0;
+		} else if (nread < amount) {
+			LDBG(curlun, "partial file read: %d/%u\n",
+					(int) nread, amount);
+			nread -= (nread & 511);	// Round down to a block
+		}
+		file_offset  += nread;
+		amount_left  -= nread;
+		fsg->residue -= nread;
+		bh->inreq->length = nread;
+		bh->state = BUF_STATE_FULL;
+
+		/* If an error occurred, report it and its position */
+		if (nread < amount) {
+			curlun->sense_data = SS_UNRECOVERED_READ_ERROR;
+			curlun->sense_data_info = file_offset >> 9;
+			curlun->info_valid = 1;
+			break;
+		}
+
+		if (amount_left == 0)
+			break;		// No more left to read
+
+		/* Send this buffer and go read some more */
+		bh->inreq->zero = 0;
+		start_transfer(fsg, fsg->bulk_in, bh->inreq,
+				&bh->inreq_busy, &bh->state);
+		fsg->next_buffhd_to_fill = bh->next;
+	}
+
+	return -EIO;		// No default reply
+}
+
+
+/*-------------------------------------------------------------------------*/
+
+static int do_write(struct fsg_dev *fsg)
+{
+	struct fsg_lun		*curlun = fsg->curlun;
+	u32			lba;
+	struct fsg_buffhd	*bh;
+	int			get_some_more;
+	u32			amount_left_to_req, amount_left_to_write;
+	loff_t			usb_offset, file_offset, file_offset_tmp;
+	unsigned int		amount, amount_tmp;
+	unsigned int		partial_page;
+	ssize_t			nwritten;
+	int			rc;
+
+	if (curlun->ro) {
+		curlun->sense_data = SS_WRITE_PROTECTED;
+		return -EINVAL;
+	}
+
+	/* Get the starting Logical Block Address and check that it's
+	 * not too big */
+	if (fsg->cmnd[0] == SC_WRITE_6)
+		lba = get_unaligned_be24(&fsg->cmnd[1]);
+	else {
+		lba = get_unaligned_be32(&fsg->cmnd[2]);
+
+		/* We allow DPO (Disable Page Out = don't save data in the
+		 * cache) and FUA (Force Unit Access = write directly to the
+		 * medium).  We don't implement DPO; we implement FUA by
+		 * performing synchronous output. */
+		if ((fsg->cmnd[1] & ~0x18) != 0) {
+			curlun->sense_data = SS_INVALID_FIELD_IN_CDB;
+			return -EINVAL;
+		}
+	}
+	if (lba >= curlun->num_sectors) {
+		curlun->sense_data = SS_LOGICAL_BLOCK_ADDRESS_OUT_OF_RANGE;
+		return -EINVAL;
+	}
+
+	/* Carry out the file writes */
+	get_some_more = 1;
+	file_offset = usb_offset = ((loff_t) lba) << 9;
+	amount_left_to_req = amount_left_to_write = fsg->data_size_from_cmnd;
+
+	while (amount_left_to_write > 0) {
+
+		/* Queue a request for more data from the host */
+		bh = fsg->next_buffhd_to_fill;
+		if (bh->state == BUF_STATE_EMPTY && get_some_more) {
+
+			/* Figure out how much we want to get:
+			 * Try to get the remaining amount.
+			 * But don't get more than the buffer size.
+			 * And don't try to go past the end of the file.
+			 * If we're not at a page boundary,
+			 *	don't go past the next page.
+			 * If this means getting 0, then we were asked
+			 *	to write past the end of file.
+			 * Finally, round down to a block boundary. */
+			amount = min(amount_left_to_req, mod_data.buflen);
+			amount = min((loff_t) amount, curlun->file_length -
+					usb_offset);
+			partial_page = usb_offset & (PAGE_CACHE_SIZE - 1);
+			if (partial_page > 0)
+				amount = min(amount,
+	(unsigned int) PAGE_CACHE_SIZE - partial_page);
+
+			if (amount == 0) {
+				get_some_more = 0;
+				curlun->sense_data =
+					SS_LOGICAL_BLOCK_ADDRESS_OUT_OF_RANGE;
+				curlun->sense_data_info = usb_offset >> 9;
+				curlun->info_valid = 1;
+				continue;
+			}
+			amount -= (amount & 511);
+			if (amount == 0) {
+
+				/* Why were we were asked to transfer a
+				 * partial block? */
+				get_some_more = 0;
+				continue;
+			}
+
+			/* Get the next buffer */
+			usb_offset += amount;
+			fsg->usb_amount_left -= amount;
+			amount_left_to_req -= amount;
+			if (amount_left_to_req == 0)
+				get_some_more = 0;
+
+			/* amount is always divisible by 512, hence by
+			 * the bulk-out maxpacket size */
+			bh->outreq->length = bh->bulk_out_intended_length =
+					amount;
+			bh->outreq->short_not_ok = 1;
+			start_transfer(fsg, fsg->bulk_out, bh->outreq,
+					&bh->outreq_busy, &bh->state);
+			fsg->next_buffhd_to_fill = bh->next;
+			continue;
+		}
+
+		/* Write the received data to the backing file */
+		bh = fsg->next_buffhd_to_drain;
+		if (bh->state == BUF_STATE_EMPTY && !get_some_more)
+			break;			// We stopped early
+		if (bh->state == BUF_STATE_FULL) {
+			smp_rmb();
+			fsg->next_buffhd_to_drain = bh->next;
+			bh->state = BUF_STATE_EMPTY;
+
+			/* Did something go wrong with the transfer? */
+			if (bh->outreq->status != 0) {
+				curlun->sense_data = SS_COMMUNICATION_FAILURE;
+				curlun->sense_data_info = file_offset >> 9;
+				curlun->info_valid = 1;
+				break;
+			}
+
+			amount = bh->outreq->actual;
+			if (curlun->file_length - file_offset < amount) {
+				LERROR(curlun,
+	"write %u @ %llu beyond end %llu\n",
+	amount, (unsigned long long) file_offset,
+	(unsigned long long) curlun->file_length);
+				amount = curlun->file_length - file_offset;
+			}
+
+			/* Perform the write */
+			file_offset_tmp = file_offset;
+			amount_tmp = amount;
+			nwritten = 0;
+			while (amount_tmp > 0) {
+				ums_info->write_sector(&(ums_info->ums_dev), (file_offset_tmp + nwritten) / SECTOR_SIZE,
+						       (char __user *)bh->buf + nwritten);
+				amount_tmp -= SECTOR_SIZE;
+				nwritten += SECTOR_SIZE;
+			}
+			file_offset_tmp += amount;
+
+			VLDBG(curlun, "file write %u @ %llu -> %d\n", amount,
+					(unsigned long long) file_offset,
+					(int) nwritten);
+			if (signal_pending(current))
+				return -EINTR;		// Interrupted!
+
+			if (nwritten < 0) {
+				LDBG(curlun, "error in file write: %d\n",
+						(int) nwritten);
+				nwritten = 0;
+			} else if (nwritten < amount) {
+				LDBG(curlun, "partial file write: %d/%u\n",
+						(int) nwritten, amount);
+				nwritten -= (nwritten & 511);
+						// Round down to a block
+			}
+			file_offset += nwritten;
+			amount_left_to_write -= nwritten;
+			fsg->residue -= nwritten;
+
+			/* If an error occurred, report it and its position */
+			if (nwritten < amount) {
+				curlun->sense_data = SS_WRITE_ERROR;
+				curlun->sense_data_info = file_offset >> 9;
+				curlun->info_valid = 1;
+				break;
+			}
+
+			/* Did the host decide to stop early? */
+			if (bh->outreq->actual != bh->outreq->length) {
+				fsg->short_packet_received = 1;
+				break;
+			}
+			continue;
+		}
+
+		/* Wait for something to happen */
+		rc = sleep_thread(fsg, __LINE__);
+		if (rc)
+			return rc;
+	}
+
+	return -EIO;		// No default reply
+}
+
+
+/*-------------------------------------------------------------------------*/
+
+static int do_synchronize_cache(struct fsg_dev *fsg)
+{
+	struct fsg_lun	*curlun = fsg->curlun;
+	int		rc;
+
+	/* We ignore the requested LBA and write out all file's
+	 * dirty data buffers. */
+	rc = fsg_lun_fsync_sub(curlun);
+	if (rc)
+		curlun->sense_data = SS_WRITE_ERROR;
+	return 0;
+}
+
+
+/*-------------------------------------------------------------------------*/
+
+static int do_verify(struct fsg_dev *fsg)
+{
+	struct fsg_lun		*curlun = fsg->curlun;
+	u32			lba;
+	u32			verification_length;
+	struct fsg_buffhd	*bh = fsg->next_buffhd_to_fill;
+	loff_t			file_offset, file_offset_tmp;
+	u32			amount_left;
+	unsigned int		amount, amount_tmp;
+	ssize_t			nread;
+
+	/* Get the starting Logical Block Address and check that it's
+	 * not too big */
+	lba = get_unaligned_be32(&fsg->cmnd[2]);
+	if (lba >= curlun->num_sectors) {
+		curlun->sense_data = SS_LOGICAL_BLOCK_ADDRESS_OUT_OF_RANGE;
+		return -EINVAL;
+	}
+
+	/* We allow DPO (Disable Page Out = don't save data in the
+	 * cache) but we don't implement it. */
+	if ((fsg->cmnd[1] & ~0x10) != 0) {
+		curlun->sense_data = SS_INVALID_FIELD_IN_CDB;
+		return -EINVAL;
+	}
+
+	verification_length = get_unaligned_be16(&fsg->cmnd[7]);
+	if (unlikely(verification_length == 0))
+		return -EIO;		// No default reply
+
+	/* Prepare to carry out the file verify */
+	amount_left = verification_length << 9;
+	file_offset = ((loff_t) lba) << 9;
+
+	/* Write out all the dirty buffers before invalidating them */
+	fsg_lun_fsync_sub(curlun);
+	if (signal_pending(current))
+		return -EINTR;
+
+	if (signal_pending(current))
+		return -EINTR;
+
+	/* Just try to read the requested blocks */
+	while (amount_left > 0) {
+
+		/* Figure out how much we need to read:
+		 * Try to read the remaining amount, but not more than
+		 * the buffer size.
+		 * And don't try to read past the end of the file.
+		 * If this means reading 0 then we were asked to read
+		 * past the end of file. */
+		amount = min((unsigned int) amount_left, mod_data.buflen);
+		amount = min((loff_t) amount,
+				curlun->file_length - file_offset);
+		if (amount == 0) {
+			curlun->sense_data =
+					SS_LOGICAL_BLOCK_ADDRESS_OUT_OF_RANGE;
+			curlun->sense_data_info = file_offset >> 9;
+			curlun->info_valid = 1;
+			break;
+		}
+
+		/* Perform the read */
+		file_offset_tmp = file_offset;
+		amount_tmp = amount;
+		nread = 0;
+		while (amount_tmp > 0) {
+			ums_info->read_sector(&(ums_info->ums_dev), (file_offset_tmp + nread) / SECTOR_SIZE,
+					      (char __user *)bh->buf + nread);
+			amount_tmp -= SECTOR_SIZE;
+			nread += SECTOR_SIZE;
+		}
+		file_offset_tmp += amount;
+
+		VLDBG(curlun, "file read %u @ %llu -> %d\n", amount,
+				(unsigned long long) file_offset,
+				(int) nread);
+		if (signal_pending(current))
+			return -EINTR;
+
+		if (nread < 0) {
+			LDBG(curlun, "error in file verify: %d\n",
+					(int) nread);
+			nread = 0;
+		} else if (nread < amount) {
+			LDBG(curlun, "partial file verify: %d/%u\n",
+					(int) nread, amount);
+			nread -= (nread & 511);	// Round down to a sector
+		}
+		if (nread == 0) {
+			curlun->sense_data = SS_UNRECOVERED_READ_ERROR;
+			curlun->sense_data_info = file_offset >> 9;
+			curlun->info_valid = 1;
+			break;
+		}
+		file_offset += nread;
+		amount_left -= nread;
+	}
+	return 0;
+}
+
+
+/*-------------------------------------------------------------------------*/
+
+static int do_inquiry(struct fsg_dev *fsg, struct fsg_buffhd *bh)
+{
+	u8	*buf = (u8 *) bh->buf;
+
+	static char vendor_id[] = "Linux   ";
+	static char product_cdrom_id[] = "File-CD Gadget  ";
+
+	if (!fsg->curlun) {		// Unsupported LUNs are okay
+		fsg->bad_lun_okay = 1;
+		memset(buf, 0, 36);
+		buf[0] = 0x7f;		// Unsupported, no device-type
+		buf[4] = 31;		// Additional length
+		return 36;
+	}
+
+	memset(buf, 0, 8);
+	buf[0] = (mod_data.cdrom ? TYPE_CDROM : TYPE_DISK);
+	if (mod_data.removable)
+		buf[1] = 0x80;
+	buf[2] = 2;		// ANSI SCSI level 2
+	buf[3] = 2;		// SCSI-2 INQUIRY data format
+	buf[4] = 31;		// Additional length
+				// No special options
+	sprintf(buf + 8, "%-8s%-16s%04x", vendor_id,
+			(mod_data.cdrom ? product_cdrom_id :
+				ums_info->name),
+		(char *)mod_data.release);
+	return 36;
+}
+
+
+static int do_request_sense(struct fsg_dev *fsg, struct fsg_buffhd *bh)
+{
+	struct fsg_lun	*curlun = fsg->curlun;
+	u8		*buf = (u8 *) bh->buf;
+	u32		sd, sdinfo;
+	int		valid;
+
+	/*
+	 * From the SCSI-2 spec., section 7.9 (Unit attention condition):
+	 *
+	 * If a REQUEST SENSE command is received from an initiator
+	 * with a pending unit attention condition (before the target
+	 * generates the contingent allegiance condition), then the
+	 * target shall either:
+	 *   a) report any pending sense data and preserve the unit
+	 *	attention condition on the logical unit, or,
+	 *   b) report the unit attention condition, may discard any
+	 *	pending sense data, and clear the unit attention
+	 *	condition on the logical unit for that initiator.
+	 *
+	 * FSG normally uses option a); enable this code to use option b).
+	 */
+#if 0
+	if (curlun && curlun->unit_attention_data != SS_NO_SENSE) {
+		curlun->sense_data = curlun->unit_attention_data;
+		curlun->unit_attention_data = SS_NO_SENSE;
+	}
+#endif
+
+	if (!curlun) {		// Unsupported LUNs are okay
+		fsg->bad_lun_okay = 1;
+		sd = SS_LOGICAL_UNIT_NOT_SUPPORTED;
+		sdinfo = 0;
+		valid = 0;
+	} else {
+		sd = curlun->sense_data;
+		sdinfo = curlun->sense_data_info;
+		valid = curlun->info_valid << 7;
+		curlun->sense_data = SS_NO_SENSE;
+		curlun->sense_data_info = 0;
+		curlun->info_valid = 0;
+	}
+
+	memset(buf, 0, 18);
+	buf[0] = valid | 0x70;			// Valid, current error
+	buf[2] = SK(sd);
+	put_unaligned_be32(sdinfo, &buf[3]);	/* Sense information */
+	buf[7] = 18 - 8;			// Additional sense length
+	buf[12] = ASC(sd);
+	buf[13] = ASCQ(sd);
+	return 18;
+}
+
+
+static int do_read_capacity(struct fsg_dev *fsg, struct fsg_buffhd *bh)
+{
+	struct fsg_lun	*curlun = fsg->curlun;
+	u32		lba = get_unaligned_be32(&fsg->cmnd[2]);
+	int		pmi = fsg->cmnd[8];
+	u8		*buf = (u8 *) bh->buf;
+
+	/* Check the PMI and LBA fields */
+	if (pmi > 1 || (pmi == 0 && lba != 0)) {
+		curlun->sense_data = SS_INVALID_FIELD_IN_CDB;
+		return -EINVAL;
+	}
+
+	put_unaligned_be32(curlun->num_sectors - 1, &buf[0]);
+						/* Max logical block */
+	put_unaligned_be32(512, &buf[4]);	/* Block length */
+	return 8;
+}
+
+
+static int do_read_header(struct fsg_dev *fsg, struct fsg_buffhd *bh)
+{
+	struct fsg_lun	*curlun = fsg->curlun;
+	int		msf = fsg->cmnd[1] & 0x02;
+	u32		lba = get_unaligned_be32(&fsg->cmnd[2]);
+	u8		*buf = (u8 *) bh->buf;
+
+	if ((fsg->cmnd[1] & ~0x02) != 0) {		/* Mask away MSF */
+		curlun->sense_data = SS_INVALID_FIELD_IN_CDB;
+		return -EINVAL;
+	}
+	if (lba >= curlun->num_sectors) {
+		curlun->sense_data = SS_LOGICAL_BLOCK_ADDRESS_OUT_OF_RANGE;
+		return -EINVAL;
+	}
+
+	memset(buf, 0, 8);
+	buf[0] = 0x01;		/* 2048 bytes of user data, rest is EC */
+	store_cdrom_address(&buf[4], msf, lba);
+	return 8;
+}
+
+
+static int do_read_toc(struct fsg_dev *fsg, struct fsg_buffhd *bh)
+{
+	struct fsg_lun	*curlun = fsg->curlun;
+	int		msf = fsg->cmnd[1] & 0x02;
+	int		start_track = fsg->cmnd[6];
+	u8		*buf = (u8 *) bh->buf;
+
+	if ((fsg->cmnd[1] & ~0x02) != 0 ||		/* Mask away MSF */
+			start_track > 1) {
+		curlun->sense_data = SS_INVALID_FIELD_IN_CDB;
+		return -EINVAL;
+	}
+
+	memset(buf, 0, 20);
+	buf[1] = (20-2);		/* TOC data length */
+	buf[2] = 1;			/* First track number */
+	buf[3] = 1;			/* Last track number */
+	buf[5] = 0x16;			/* Data track, copying allowed */
+	buf[6] = 0x01;			/* Only track is number 1 */
+	store_cdrom_address(&buf[8], msf, 0);
+
+	buf[13] = 0x16;			/* Lead-out track is data */
+	buf[14] = 0xAA;			/* Lead-out track number */
+	store_cdrom_address(&buf[16], msf, curlun->num_sectors);
+	return 20;
+}
+
+
+static int do_mode_sense(struct fsg_dev *fsg, struct fsg_buffhd *bh)
+{
+	struct fsg_lun	*curlun = fsg->curlun;
+	int		mscmnd = fsg->cmnd[0];
+	u8		*buf = (u8 *) bh->buf;
+	u8		*buf0 = buf;
+	int		pc, page_code;
+	int		changeable_values, all_pages;
+	int		valid_page = 0;
+	int		len, limit;
+
+	if ((fsg->cmnd[1] & ~0x08) != 0) {		// Mask away DBD
+		curlun->sense_data = SS_INVALID_FIELD_IN_CDB;
+		return -EINVAL;
+	}
+	pc = fsg->cmnd[2] >> 6;
+	page_code = fsg->cmnd[2] & 0x3f;
+	if (pc == 3) {
+		curlun->sense_data = SS_SAVING_PARAMETERS_NOT_SUPPORTED;
+		return -EINVAL;
+	}
+	changeable_values = (pc == 1);
+	all_pages = (page_code == 0x3f);
+
+	/* Write the mode parameter header.  Fixed values are: default
+	 * medium type, no cache control (DPOFUA), and no block descriptors.
+	 * The only variable value is the WriteProtect bit.  We will fill in
+	 * the mode data length later. */
+	memset(buf, 0, 8);
+	if (mscmnd == SC_MODE_SENSE_6) {
+		buf[2] = (curlun->ro ? 0x80 : 0x00);		// WP, DPOFUA
+		buf += 4;
+		limit = 255;
+	} else {			// SC_MODE_SENSE_10
+		buf[3] = (curlun->ro ? 0x80 : 0x00);		// WP, DPOFUA
+		buf += 8;
+		limit = 65535;		// Should really be mod_data.buflen
+	}
+
+	/* No block descriptors */
+
+	/* The mode pages, in numerical order.  The only page we support
+	 * is the Caching page. */
+	if (page_code == 0x08 || all_pages) {
+		valid_page = 1;
+		buf[0] = 0x08;		// Page code
+		buf[1] = 10;		// Page length
+		memset(buf+2, 0, 10);	// None of the fields are changeable
+
+		if (!changeable_values) {
+			buf[2] = 0x04;	// Write cache enable,
+					// Read cache not disabled
+					// No cache retention priorities
+			put_unaligned_be16(0xffff, &buf[4]);
+					/* Don't disable prefetch */
+					/* Minimum prefetch = 0 */
+			put_unaligned_be16(0xffff, &buf[8]);
+					/* Maximum prefetch */
+			put_unaligned_be16(0xffff, &buf[10]);
+					/* Maximum prefetch ceiling */
+		}
+		buf += 12;
+	}
+
+	/* Check that a valid page was requested and the mode data length
+	 * isn't too long. */
+	len = buf - buf0;
+	if (!valid_page || len > limit) {
+		curlun->sense_data = SS_INVALID_FIELD_IN_CDB;
+		return -EINVAL;
+	}
+
+	/*  Store the mode data length */
+	if (mscmnd == SC_MODE_SENSE_6)
+		buf0[0] = len - 1;
+	else
+		put_unaligned_be16(len - 2, buf0);
+	return len;
+}
+
+
+static int do_start_stop(struct fsg_dev *fsg)
+{
+	struct fsg_lun	*curlun = fsg->curlun;
+	int		loej, start;
+
+	if (!mod_data.removable) {
+		curlun->sense_data = SS_INVALID_COMMAND;
+		return -EINVAL;
+	}
+
+	// int immed = fsg->cmnd[1] & 0x01;
+	loej = fsg->cmnd[4] & 0x02;
+	start = fsg->cmnd[4] & 0x01;
+
+	return 0;
+}
+
+
+static int do_prevent_allow(struct fsg_dev *fsg)
+{
+	struct fsg_lun	*curlun = fsg->curlun;
+	int		prevent;
+
+	if (!mod_data.removable) {
+		curlun->sense_data = SS_INVALID_COMMAND;
+		return -EINVAL;
+	}
+
+	prevent = fsg->cmnd[4] & 0x01;
+	if ((fsg->cmnd[4] & ~0x01) != 0) {		// Mask away Prevent
+		curlun->sense_data = SS_INVALID_FIELD_IN_CDB;
+		return -EINVAL;
+	}
+
+	if (curlun->prevent_medium_removal && !prevent)
+		fsg_lun_fsync_sub(curlun);
+	curlun->prevent_medium_removal = prevent;
+	return 0;
+}
+
+
+static int do_read_format_capacities(struct fsg_dev *fsg,
+			struct fsg_buffhd *bh)
+{
+	struct fsg_lun	*curlun = fsg->curlun;
+	u8		*buf = (u8 *) bh->buf;
+
+	buf[0] = buf[1] = buf[2] = 0;
+	buf[3] = 8;		// Only the Current/Maximum Capacity Descriptor
+	buf += 4;
+
+	put_unaligned_be32(curlun->num_sectors, &buf[0]);
+						/* Number of blocks */
+	put_unaligned_be32(512, &buf[4]);	/* Block length */
+	buf[4] = 0x02;				/* Current capacity */
+	return 12;
+}
+
+
+static int do_mode_select(struct fsg_dev *fsg, struct fsg_buffhd *bh)
+{
+	struct fsg_lun	*curlun = fsg->curlun;
+
+	/* We don't support MODE SELECT */
+	curlun->sense_data = SS_INVALID_COMMAND;
+	return -EINVAL;
+}
+
+
+/*-------------------------------------------------------------------------*/
+
+static int halt_bulk_in_endpoint(struct fsg_dev *fsg)
+{
+	int	rc;
+
+	rc = fsg_set_halt(fsg, fsg->bulk_in);
+	if (rc == -EAGAIN)
+		VDBG(fsg, "delayed bulk-in endpoint halt\n");
+	while (rc != 0) {
+		if (rc != -EAGAIN) {
+			WARNING(fsg, "usb_ep_set_halt -> %d\n", rc);
+			rc = 0;
+			break;
+		}
+
+		/* Wait for a short time and then try again */
+		if (msleep_interruptible(100) != 0)
+			return -EINTR;
+		rc = usb_ep_set_halt(fsg->bulk_in);
+	}
+	return rc;
+}
+
+static int wedge_bulk_in_endpoint(struct fsg_dev *fsg)
+{
+	int	rc;
+
+	DBG(fsg, "bulk-in set wedge\n");
+	rc = 0; //usb_ep_set_wedge(fsg->bulk_in);
+	if (rc == -EAGAIN)
+		VDBG(fsg, "delayed bulk-in endpoint wedge\n");
+	while (rc != 0) {
+		if (rc != -EAGAIN) {
+			WARNING(fsg, "usb_ep_set_wedge -> %d\n", rc);
+			rc = 0;
+			break;
+		}
+
+		/* Wait for a short time and then try again */
+		if (msleep_interruptible(100) != 0)
+			return -EINTR;
+		//rc = usb_ep_set_wedge(fsg->bulk_in);
+	}
+	return rc;
+}
+
+static int pad_with_zeros(struct fsg_dev *fsg)
+{
+	struct fsg_buffhd	*bh = fsg->next_buffhd_to_fill;
+	u32			nkeep = bh->inreq->length;
+	u32			nsend;
+	int			rc;
+
+	bh->state = BUF_STATE_EMPTY;		// For the first iteration
+	fsg->usb_amount_left = nkeep + fsg->residue;
+	while (fsg->usb_amount_left > 0) {
+
+		/* Wait for the next buffer to be free */
+		while (bh->state != BUF_STATE_EMPTY) {
+			rc = sleep_thread(fsg, __LINE__);
+			if (rc)
+				return rc;
+		}
+
+		nsend = min(fsg->usb_amount_left, (u32) mod_data.buflen);
+		memset(bh->buf + nkeep, 0, nsend - nkeep);
+		bh->inreq->length = nsend;
+		bh->inreq->zero = 0;
+		start_transfer(fsg, fsg->bulk_in, bh->inreq,
+				&bh->inreq_busy, &bh->state);
+		bh = fsg->next_buffhd_to_fill = bh->next;
+		fsg->usb_amount_left -= nsend;
+		nkeep = 0;
+	}
+	return 0;
+}
+
+static int throw_away_data(struct fsg_dev *fsg)
+{
+	struct fsg_buffhd	*bh;
+	u32			amount;
+	int			rc;
+
+	while ((bh = fsg->next_buffhd_to_drain)->state != BUF_STATE_EMPTY ||
+			fsg->usb_amount_left > 0) {
+
+		/* Throw away the data in a filled buffer */
+		if (bh->state == BUF_STATE_FULL) {
+			smp_rmb();
+			bh->state = BUF_STATE_EMPTY;
+			fsg->next_buffhd_to_drain = bh->next;
+
+			/* A short packet or an error ends everything */
+			if (bh->outreq->actual != bh->outreq->length ||
+					bh->outreq->status != 0) {
+				raise_exception(fsg, FSG_STATE_ABORT_BULK_OUT);
+				return -EINTR;
+			}
+			continue;
+		}
+
+		/* Try to submit another request if we need one */
+		bh = fsg->next_buffhd_to_fill;
+		if (bh->state == BUF_STATE_EMPTY && fsg->usb_amount_left > 0) {
+			amount = min(fsg->usb_amount_left,
+					(u32) mod_data.buflen);
+
+			/* amount is always divisible by 512, hence by
+			 * the bulk-out maxpacket size */
+			bh->outreq->length = bh->bulk_out_intended_length =
+					amount;
+			bh->outreq->short_not_ok = 1;
+			start_transfer(fsg, fsg->bulk_out, bh->outreq,
+					&bh->outreq_busy, &bh->state);
+			fsg->next_buffhd_to_fill = bh->next;
+			fsg->usb_amount_left -= amount;
+			continue;
+		}
+
+		/* Otherwise wait for something to happen */
+		rc = sleep_thread(fsg, __LINE__);
+		if (rc)
+			return rc;
+	}
+	return 0;
+}
+
+
+static int finish_reply(struct fsg_dev *fsg)
+{
+	struct fsg_buffhd	*bh = fsg->next_buffhd_to_fill;
+	int			rc = 0;
+
+	switch (fsg->data_dir) {
+	case DATA_DIR_NONE:
+		break;			// Nothing to send
+
+	/* If we don't know whether the host wants to read or write,
+	 * this must be CB or CBI with an unknown command.  We mustn't
+	 * try to send or receive any data.  So stall both bulk pipes
+	 * if we can and wait for a reset. */
+	case DATA_DIR_UNKNOWN:
+		if (mod_data.can_stall) {
+			fsg_set_halt(fsg, fsg->bulk_out);
+			rc = halt_bulk_in_endpoint(fsg);
+		}
+		break;
+
+	/* All but the last buffer of data must have already been sent */
+	case DATA_DIR_TO_HOST:
+		if (fsg->data_size == 0){
+			;		// Nothing to send
+		}
+		/* If there's no residue, simply send the last buffer */
+		else if (fsg->residue == 0) {
+			bh->inreq->zero = 0;
+			start_transfer(fsg, fsg->bulk_in, bh->inreq,
+					&bh->inreq_busy, &bh->state);
+			fsg->next_buffhd_to_fill = bh->next;
+		}
+
+		/* There is a residue.  For CB and CBI, simply mark the end
+		 * of the data with a short packet.  However, if we are
+		 * allowed to stall, there was no data at all (residue ==
+		 * data_size), and the command failed (invalid LUN or
+		 * sense data is set), then halt the bulk-in endpoint
+		 * instead. */
+		else if (!transport_is_bbb()) {
+			if (mod_data.can_stall &&
+					fsg->residue == fsg->data_size &&
+	(!fsg->curlun || fsg->curlun->sense_data != SS_NO_SENSE)) {
+				bh->state = BUF_STATE_EMPTY;
+				rc = halt_bulk_in_endpoint(fsg);
+			} else {
+				bh->inreq->zero = 1;
+				start_transfer(fsg, fsg->bulk_in, bh->inreq,
+						&bh->inreq_busy, &bh->state);
+				fsg->next_buffhd_to_fill = bh->next;
+			}
+		}
+
+		/* For Bulk-only, if we're allowed to stall then send the
+		 * short packet and halt the bulk-in endpoint.  If we can't
+		 * stall, pad out the remaining data with 0's. */
+		else {
+			if (mod_data.can_stall) {
+				bh->inreq->zero = 1;
+				start_transfer(fsg, fsg->bulk_in, bh->inreq,
+						&bh->inreq_busy, &bh->state);
+				fsg->next_buffhd_to_fill = bh->next;
+				rc = halt_bulk_in_endpoint(fsg);
+			} else
+				rc = pad_with_zeros(fsg);
+		}
+		break;
+
+	/* We have processed all we want from the data the host has sent.
+	 * There may still be outstanding bulk-out requests. */
+	case DATA_DIR_FROM_HOST:
+		if (fsg->residue == 0)
+			;		// Nothing to receive
+
+		/* Did the host stop sending unexpectedly early? */
+		else if (fsg->short_packet_received) {
+			raise_exception(fsg, FSG_STATE_ABORT_BULK_OUT);
+			rc = -EINTR;
+		}
+
+		/* We haven't processed all the incoming data.  Even though
+		 * we may be allowed to stall, doing so would cause a race.
+		 * The controller may already have ACK'ed all the remaining
+		 * bulk-out packets, in which case the host wouldn't see a
+		 * STALL.  Not realizing the endpoint was halted, it wouldn't
+		 * clear the halt -- leading to problems later on. */
+#if 0
+		else if (mod_data.can_stall) {
+			fsg_set_halt(fsg, fsg->bulk_out);
+			raise_exception(fsg, FSG_STATE_ABORT_BULK_OUT);
+			rc = -EINTR;
+		}
+#endif
+
+		/* We can't stall.  Read in the excess data and throw it
+		 * all away. */
+		else
+			rc = throw_away_data(fsg);
+		break;
+	}
+	return rc;
+}
+
+
+static int send_status(struct fsg_dev *fsg)
+{
+	struct fsg_lun		*curlun = fsg->curlun;
+	struct fsg_buffhd	*bh;
+	int			rc;
+	u8			status = USB_STATUS_PASS;
+	u32			sd, sdinfo = 0;
+
+	/* Wait for the next buffer to become available */
+	bh = fsg->next_buffhd_to_fill;
+	while (bh->state != BUF_STATE_EMPTY) {
+		rc = sleep_thread(fsg, __LINE__);
+		if (rc)
+			return rc;
+	}
+
+	if (curlun) {
+		sd = curlun->sense_data;
+		sdinfo = curlun->sense_data_info;
+	} else if (fsg->bad_lun_okay)
+		sd = SS_NO_SENSE;
+	else
+		sd = SS_LOGICAL_UNIT_NOT_SUPPORTED;
+
+	if (fsg->phase_error) {
+		DBG(fsg, "sending phase-error status\n");
+		status = USB_STATUS_PHASE_ERROR;
+		sd = SS_INVALID_COMMAND;
+	} else if (sd != SS_NO_SENSE) {
+		DBG(fsg, "sending command-failure status\n");
+		status = USB_STATUS_FAIL;
+		VDBG(fsg, "  sense data: SK x%02x, ASC x%02x, ASCQ x%02x;"
+				"  info x%x\n",
+				SK(sd), ASC(sd), ASCQ(sd), sdinfo);
+	}
+
+	if (transport_is_bbb()) {
+		struct bulk_cs_wrap	*csw = bh->buf;
+
+		/* Store and send the Bulk-only CSW */
+		csw->Signature = cpu_to_le32(USB_BULK_CS_SIG);
+		csw->Tag = fsg->tag;
+		csw->Residue = cpu_to_le32(fsg->residue);
+		csw->Status = status;
+
+		bh->inreq->length = USB_BULK_CS_WRAP_LEN;
+		bh->inreq->zero = 0;
+		start_transfer(fsg, fsg->bulk_in, bh->inreq,
+				&bh->inreq_busy, &bh->state);
+
+	} else if (mod_data.transport_type == USB_PR_CB) {
+
+		/* Control-Bulk transport has no status phase! */
+		return 0;
+
+	} else {			// USB_PR_CBI
+		struct interrupt_data	*buf = bh->buf;
+
+		/* Store and send the Interrupt data.  UFI sends the ASC
+		 * and ASCQ bytes.  Everything else sends a Type (which
+		 * is always 0) and the status Value. */
+		if (mod_data.protocol_type == USB_SC_UFI) {
+			buf->bType = ASC(sd);
+			buf->bValue = ASCQ(sd);
+		} else {
+			buf->bType = 0;
+			buf->bValue = status;
+		}
+		fsg->intreq->length = CBI_INTERRUPT_DATA_LEN;
+
+		fsg->intr_buffhd = bh;		// Point to the right buffhd
+		fsg->intreq->buf = bh->inreq->buf;
+		fsg->intreq->context = bh;
+		start_transfer(fsg, fsg->intr_in, fsg->intreq,
+				&fsg->intreq_busy, &bh->state);
+	}
+
+	fsg->next_buffhd_to_fill = bh->next;
+	return 0;
+}
+
+
+/*-------------------------------------------------------------------------*/
+
+/* Check whether the command is properly formed and whether its data size
+ * and direction agree with the values we already have. */
+static int check_command(struct fsg_dev *fsg, int cmnd_size,
+		enum data_direction data_dir, unsigned int mask,
+		int needs_medium, const char *name)
+{
+	int			i;
+	int			lun = fsg->cmnd[1] >> 5;
+	static const char	dirletter[4] = {'u', 'o', 'i', 'n'};
+	char			hdlen[20];
+	struct fsg_lun		*curlun;
+
+	/* Adjust the expected cmnd_size for protocol encapsulation padding.
+	 * Transparent SCSI doesn't pad. */
+	if (protocol_is_scsi())
+		;
+
+	/* There's some disagreement as to whether RBC pads commands or not.
+	 * We'll play it safe and accept either form. */
+	else if (mod_data.protocol_type == USB_SC_RBC) {
+		if (fsg->cmnd_size == 12)
+			cmnd_size = 12;
+
+	/* All the other protocols pad to 12 bytes */
+	} else
+		cmnd_size = 12;
+
+	hdlen[0] = 0;
+	if (fsg->data_dir != DATA_DIR_UNKNOWN)
+		sprintf(hdlen, ", H%c=%u", dirletter[(int) fsg->data_dir],
+				fsg->data_size);
+	VDBG(fsg, "SCSI command: %s;  Dc=%d, D%c=%u;  Hc=%d%s\n",
+			name, cmnd_size, dirletter[(int) data_dir],
+			fsg->data_size_from_cmnd, fsg->cmnd_size, hdlen);
+
+	/* We can't reply at all until we know the correct data direction
+	 * and size. */
+	if (fsg->data_size_from_cmnd == 0)
+		data_dir = DATA_DIR_NONE;
+	if (fsg->data_dir == DATA_DIR_UNKNOWN) {	// CB or CBI
+		fsg->data_dir = data_dir;
+		fsg->data_size = fsg->data_size_from_cmnd;
+
+	} else {					// Bulk-only
+		if (fsg->data_size < fsg->data_size_from_cmnd) {
+
+			/* Host data size < Device data size is a phase error.
+			 * Carry out the command, but only transfer as much
+			 * as we are allowed. */
+			fsg->data_size_from_cmnd = fsg->data_size;
+			fsg->phase_error = 1;
+		}
+	}
+	fsg->residue = fsg->usb_amount_left = fsg->data_size;
+
+	/* Conflicting data directions is a phase error */
+	if (fsg->data_dir != data_dir && fsg->data_size_from_cmnd > 0) {
+		fsg->phase_error = 1;
+		return -EINVAL;
+	}
+
+	/* Verify the length of the command itself */
+	if (cmnd_size != fsg->cmnd_size) {
+
+		/* Special case workaround: There are plenty of buggy SCSI
+		 * implementations. Many have issues with cbw->Length
+		 * field passing a wrong command size. For those cases we
+		 * always try to work around the problem by using the length
+		 * sent by the host side provided it is at least as large
+		 * as the correct command length.
+		 * Examples of such cases would be MS-Windows, which issues
+		 * REQUEST SENSE with cbw->Length == 12 where it should
+		 * be 6, and xbox360 issuing INQUIRY, TEST UNIT READY and
+		 * REQUEST SENSE with cbw->Length == 10 where it should
+		 * be 6 as well.
+		 */
+		if (cmnd_size <= fsg->cmnd_size) {
+			DBG(fsg, "%s is buggy! Expected length %d "
+					"but we got %d\n", name,
+					cmnd_size, fsg->cmnd_size);
+			cmnd_size = fsg->cmnd_size;
+		} else {
+			fsg->phase_error = 1;
+			return -EINVAL;
+		}
+	}
+
+	/* Check that the LUN values are consistent */
+	if (transport_is_bbb()) {
+		if (fsg->lun != lun)
+			DBG(fsg, "using LUN %d from CBW, "
+					"not LUN %d from CDB\n",
+					fsg->lun, lun);
+	} else
+		fsg->lun = lun;		// Use LUN from the command
+
+	/* Check the LUN */
+	if (fsg->lun >= 0 && fsg->lun < fsg->nluns) {
+		fsg->curlun = curlun = &fsg->luns[fsg->lun];
+		if (fsg->cmnd[0] != SC_REQUEST_SENSE) {
+			curlun->sense_data = SS_NO_SENSE;
+			curlun->sense_data_info = 0;
+			curlun->info_valid = 0;
+		}
+	} else {
+		fsg->curlun = curlun = NULL;
+		fsg->bad_lun_okay = 0;
+
+		/* INQUIRY and REQUEST SENSE commands are explicitly allowed
+		 * to use unsupported LUNs; all others may not. */
+		if (fsg->cmnd[0] != SC_INQUIRY &&
+				fsg->cmnd[0] != SC_REQUEST_SENSE) {
+			DBG(fsg, "unsupported LUN %d\n", fsg->lun);
+			return -EINVAL;
+		}
+	}
+
+	/* If a unit attention condition exists, only INQUIRY and
+	 * REQUEST SENSE commands are allowed; anything else must fail. */
+	if (curlun && curlun->unit_attention_data != SS_NO_SENSE &&
+			fsg->cmnd[0] != SC_INQUIRY &&
+			fsg->cmnd[0] != SC_REQUEST_SENSE) {
+		curlun->sense_data = curlun->unit_attention_data;
+		curlun->unit_attention_data = SS_NO_SENSE;
+		return -EINVAL;
+	}
+
+	/* Check that only command bytes listed in the mask are non-zero */
+	fsg->cmnd[1] &= 0x1f;			// Mask away the LUN
+	for (i = 1; i < cmnd_size; ++i) {
+		if (fsg->cmnd[i] && !(mask & (1 << i))) {
+			if (curlun)
+				curlun->sense_data = SS_INVALID_FIELD_IN_CDB;
+			return -EINVAL;
+		}
+	}
+
+	return 0;
+}
+
+
+static int do_scsi_command(struct fsg_dev *fsg)
+{
+	struct fsg_buffhd	*bh;
+	int			rc;
+	int			reply = -EINVAL;
+	int			i;
+	static char		unknown[16];
+
+	dump_cdb(fsg);
+
+	/* Wait for the next buffer to become available for data or status */
+	bh = fsg->next_buffhd_to_drain = fsg->next_buffhd_to_fill;
+	while (bh->state != BUF_STATE_EMPTY) {
+		rc = sleep_thread(fsg, __LINE__);
+		if (rc)
+			return rc;
+	}
+	fsg->phase_error = 0;
+	fsg->short_packet_received = 0;
+
+	down_read(&fsg->filesem);	// We're using the backing file
+	switch (fsg->cmnd[0]) {
+
+	case SC_INQUIRY:
+		fsg->data_size_from_cmnd = fsg->cmnd[4];
+		if ((reply = check_command(fsg, 6, DATA_DIR_TO_HOST,
+				(1<<4), 0,
+				"INQUIRY")) == 0)
+			reply = do_inquiry(fsg, bh);
+		break;
+
+	case SC_MODE_SELECT_6:
+		fsg->data_size_from_cmnd = fsg->cmnd[4];
+		if ((reply = check_command(fsg, 6, DATA_DIR_FROM_HOST,
+				(1<<1) | (1<<4), 0,
+				"MODE SELECT(6)")) == 0)
+			reply = do_mode_select(fsg, bh);
+		break;
+
+	case SC_MODE_SELECT_10:
+		fsg->data_size_from_cmnd = get_unaligned_be16(&fsg->cmnd[7]);
+		if ((reply = check_command(fsg, 10, DATA_DIR_FROM_HOST,
+				(1<<1) | (3<<7), 0,
+				"MODE SELECT(10)")) == 0)
+			reply = do_mode_select(fsg, bh);
+		break;
+
+	case SC_MODE_SENSE_6:
+		fsg->data_size_from_cmnd = fsg->cmnd[4];
+		if ((reply = check_command(fsg, 6, DATA_DIR_TO_HOST,
+				(1<<1) | (1<<2) | (1<<4), 0,
+				"MODE SENSE(6)")) == 0)
+			reply = do_mode_sense(fsg, bh);
+		break;
+
+	case SC_MODE_SENSE_10:
+		fsg->data_size_from_cmnd = get_unaligned_be16(&fsg->cmnd[7]);
+		if ((reply = check_command(fsg, 10, DATA_DIR_TO_HOST,
+				(1<<1) | (1<<2) | (3<<7), 0,
+				"MODE SENSE(10)")) == 0)
+			reply = do_mode_sense(fsg, bh);
+		break;
+
+	case SC_PREVENT_ALLOW_MEDIUM_REMOVAL:
+		fsg->data_size_from_cmnd = 0;
+		if ((reply = check_command(fsg, 6, DATA_DIR_NONE,
+				(1<<4), 0,
+				"PREVENT-ALLOW MEDIUM REMOVAL")) == 0)
+			reply = do_prevent_allow(fsg);
+		break;
+
+	case SC_READ_6:
+		i = fsg->cmnd[4];
+		fsg->data_size_from_cmnd = (i == 0 ? 256 : i) << 9;
+		if ((reply = check_command(fsg, 6, DATA_DIR_TO_HOST,
+				(7<<1) | (1<<4), 1,
+				"READ(6)")) == 0)
+			reply = do_read(fsg);
+		break;
+
+	case SC_READ_10:
+		fsg->data_size_from_cmnd =
+				get_unaligned_be16(&fsg->cmnd[7]) << 9;
+		if ((reply = check_command(fsg, 10, DATA_DIR_TO_HOST,
+				(1<<1) | (0xf<<2) | (3<<7), 1,
+				"READ(10)")) == 0)
+			reply = do_read(fsg);
+		break;
+
+	case SC_READ_12:
+		fsg->data_size_from_cmnd =
+				get_unaligned_be32(&fsg->cmnd[6]) << 9;
+		if ((reply = check_command(fsg, 12, DATA_DIR_TO_HOST,
+				(1<<1) | (0xf<<2) | (0xf<<6), 1,
+				"READ(12)")) == 0)
+			reply = do_read(fsg);
+		break;
+
+	case SC_READ_CAPACITY:
+		fsg->data_size_from_cmnd = 8;
+		if ((reply = check_command(fsg, 10, DATA_DIR_TO_HOST,
+				(0xf<<2) | (1<<8), 1,
+				"READ CAPACITY")) == 0)
+			reply = do_read_capacity(fsg, bh);
+		break;
+
+	case SC_READ_HEADER:
+		if (!mod_data.cdrom)
+			goto unknown_cmnd;
+		fsg->data_size_from_cmnd = get_unaligned_be16(&fsg->cmnd[7]);
+		if ((reply = check_command(fsg, 10, DATA_DIR_TO_HOST,
+				(3<<7) | (0x1f<<1), 1,
+				"READ HEADER")) == 0)
+			reply = do_read_header(fsg, bh);
+		break;
+
+	case SC_READ_TOC:
+		if (!mod_data.cdrom)
+			goto unknown_cmnd;
+		fsg->data_size_from_cmnd = get_unaligned_be16(&fsg->cmnd[7]);
+		if ((reply = check_command(fsg, 10, DATA_DIR_TO_HOST,
+				(7<<6) | (1<<1), 1,
+				"READ TOC")) == 0)
+			reply = do_read_toc(fsg, bh);
+		break;
+
+	case SC_READ_FORMAT_CAPACITIES:
+		fsg->data_size_from_cmnd = get_unaligned_be16(&fsg->cmnd[7]);
+		if ((reply = check_command(fsg, 10, DATA_DIR_TO_HOST,
+				(3<<7), 1,
+				"READ FORMAT CAPACITIES")) == 0)
+			reply = do_read_format_capacities(fsg, bh);
+		break;
+
+	case SC_REQUEST_SENSE:
+		fsg->data_size_from_cmnd = fsg->cmnd[4];
+		if ((reply = check_command(fsg, 6, DATA_DIR_TO_HOST,
+				(1<<4), 0,
+				"REQUEST SENSE")) == 0)
+			reply = do_request_sense(fsg, bh);
+		break;
+
+	case SC_START_STOP_UNIT:
+		fsg->data_size_from_cmnd = 0;
+		if ((reply = check_command(fsg, 6, DATA_DIR_NONE,
+				(1<<1) | (1<<4), 0,
+				"START-STOP UNIT")) == 0)
+			reply = do_start_stop(fsg);
+		break;
+
+	case SC_SYNCHRONIZE_CACHE:
+		fsg->data_size_from_cmnd = 0;
+		if ((reply = check_command(fsg, 10, DATA_DIR_NONE,
+				(0xf<<2) | (3<<7), 1,
+				"SYNCHRONIZE CACHE")) == 0)
+			reply = do_synchronize_cache(fsg);
+		break;
+
+	case SC_TEST_UNIT_READY:
+		fsg->data_size_from_cmnd = 0;
+		reply = check_command(fsg, 6, DATA_DIR_NONE,
+				0, 1,
+				"TEST UNIT READY");
+		break;
+
+	/* Although optional, this command is used by MS-Windows.  We
+	 * support a minimal version: BytChk must be 0. */
+	case SC_VERIFY:
+		fsg->data_size_from_cmnd = 0;
+		if ((reply = check_command(fsg, 10, DATA_DIR_NONE,
+				(1<<1) | (0xf<<2) | (3<<7), 1,
+				"VERIFY")) == 0)
+			reply = do_verify(fsg);
+		break;
+
+	case SC_WRITE_6:
+		i = fsg->cmnd[4];
+		fsg->data_size_from_cmnd = (i == 0 ? 256 : i) << 9;
+		if ((reply = check_command(fsg, 6, DATA_DIR_FROM_HOST,
+				(7<<1) | (1<<4), 1,
+				"WRITE(6)")) == 0)
+			reply = do_write(fsg);
+		break;
+
+	case SC_WRITE_10:
+		fsg->data_size_from_cmnd =
+				get_unaligned_be16(&fsg->cmnd[7]) << 9;
+		if ((reply = check_command(fsg, 10, DATA_DIR_FROM_HOST,
+				(1<<1) | (0xf<<2) | (3<<7), 1,
+				"WRITE(10)")) == 0)
+			reply = do_write(fsg);
+		break;
+
+	case SC_WRITE_12:
+		fsg->data_size_from_cmnd =
+				get_unaligned_be32(&fsg->cmnd[6]) << 9;
+		if ((reply = check_command(fsg, 12, DATA_DIR_FROM_HOST,
+				(1<<1) | (0xf<<2) | (0xf<<6), 1,
+				"WRITE(12)")) == 0)
+			reply = do_write(fsg);
+		break;
+
+	/* Some mandatory commands that we recognize but don't implement.
+	 * They don't mean much in this setting.  It's left as an exercise
+	 * for anyone interested to implement RESERVE and RELEASE in terms
+	 * of Posix locks. */
+	case SC_FORMAT_UNIT:
+	case SC_RELEASE:
+	case SC_RESERVE:
+	case SC_SEND_DIAGNOSTIC:
+		// Fall through
+
+	default:
+ unknown_cmnd:
+		fsg->data_size_from_cmnd = 0;
+		sprintf(unknown, "Unknown x%02x", fsg->cmnd[0]);
+		if ((reply = check_command(fsg, fsg->cmnd_size,
+				DATA_DIR_UNKNOWN, 0xff, 0, unknown)) == 0) {
+			fsg->curlun->sense_data = SS_INVALID_COMMAND;
+			reply = -EINVAL;
+		}
+		break;
+	}
+	up_read(&fsg->filesem);
+
+	if (reply == -EINTR)
+		return -EINTR;
+
+	/* Set up the single reply buffer for finish_reply() */
+	if (reply == -EINVAL)
+		reply = 0;		// Error reply length
+
+	if (reply >= 0 && fsg->data_dir == DATA_DIR_TO_HOST) {
+		reply = min((u32) reply, fsg->data_size_from_cmnd);
+		bh->inreq->length = reply;
+		bh->state = BUF_STATE_FULL;
+		fsg->residue -= reply;
+	}				// Otherwise it's already set
+
+	return 0;
+}
+
+
+/*-------------------------------------------------------------------------*/
+
+static int received_cbw(struct fsg_dev *fsg, struct fsg_buffhd *bh)
+{
+	struct usb_request		*req = bh->outreq;
+	struct fsg_bulk_cb_wrap	*cbw = req->buf;
+
+	/* Was this a real packet?  Should it be ignored? */
+	if (req->status || test_bit(IGNORE_BULK_OUT, &fsg->atomic_bitflags))
+		return -EINVAL;
+
+	/* Is the CBW valid? */
+	if (req->actual != USB_BULK_CB_WRAP_LEN ||
+			cbw->Signature != cpu_to_le32(
+				USB_BULK_CB_SIG)) {
+		DBG(fsg, "invalid CBW: len %u sig 0x%x\n",
+				req->actual,
+				le32_to_cpu(cbw->Signature));
+
+		/* The Bulk-only spec says we MUST stall the IN endpoint
+		 * (6.6.1), so it's unavoidable.  It also says we must
+		 * retain this state until the next reset, but there's
+		 * no way to tell the controller driver it should ignore
+		 * Clear-Feature(HALT) requests.
+		 *
+		 * We aren't required to halt the OUT endpoint; instead
+		 * we can simply accept and discard any data received
+		 * until the next reset. */
+		wedge_bulk_in_endpoint(fsg);
+		set_bit(IGNORE_BULK_OUT, &fsg->atomic_bitflags);
+		return -EINVAL;
+	}
+
+	/* Is the CBW meaningful? */
+	if (cbw->Lun >= FSG_MAX_LUNS || cbw->Flags & ~USB_BULK_IN_FLAG ||
+			cbw->Length <= 0 || cbw->Length > MAX_COMMAND_SIZE) {
+		DBG(fsg, "non-meaningful CBW: lun = %u, flags = 0x%x, "
+				"cmdlen %u\n",
+				cbw->Lun, cbw->Flags, cbw->Length);
+
+		/* We can do anything we want here, so let's stall the
+		 * bulk pipes if we are allowed to. */
+		if (mod_data.can_stall) {
+			fsg_set_halt(fsg, fsg->bulk_out);
+			halt_bulk_in_endpoint(fsg);
+		}
+		return -EINVAL;
+	}
+
+	/* Save the command for later */
+	fsg->cmnd_size = cbw->Length;
+	memcpy(fsg->cmnd, cbw->CDB, fsg->cmnd_size);
+	if (cbw->Flags & USB_BULK_IN_FLAG)
+		fsg->data_dir = DATA_DIR_TO_HOST;
+	else
+		fsg->data_dir = DATA_DIR_FROM_HOST;
+	fsg->data_size = le32_to_cpu(cbw->DataTransferLength);
+	if (fsg->data_size == 0)
+		fsg->data_dir = DATA_DIR_NONE;
+	fsg->lun = cbw->Lun;
+	fsg->tag = cbw->Tag;
+	return 0;
+}
+
+
+static int get_next_command(struct fsg_dev *fsg)
+{
+	struct fsg_buffhd	*bh;
+	int			rc = 0;
+
+	if (transport_is_bbb()) {
+
+		/* Wait for the next buffer to become available */
+		bh = fsg->next_buffhd_to_fill;
+		while (bh->state != BUF_STATE_EMPTY) {
+			rc = sleep_thread(fsg, __LINE__);
+			if (rc)
+				return rc;
+		}
+
+		/* Queue a request to read a Bulk-only CBW */
+		set_bulk_out_req_length(fsg, bh, USB_BULK_CB_WRAP_LEN);
+		bh->outreq->short_not_ok = 1;
+		start_transfer(fsg, fsg->bulk_out, bh->outreq,
+				&bh->outreq_busy, &bh->state);
+
+		/* We will drain the buffer in software, which means we
+		 * can reuse it for the next filling.  No need to advance
+		 * next_buffhd_to_fill. */
+
+		/* Wait for the CBW to arrive */
+		while (bh->state != BUF_STATE_FULL) {
+			rc = sleep_thread(fsg, __LINE__);
+			if (rc)
+				return rc;
+		}
+		smp_rmb();
+		rc = received_cbw(fsg, bh);
+		bh->state = BUF_STATE_EMPTY;
+
+	} else {		// USB_PR_CB or USB_PR_CBI
+
+		/* Wait for the next command to arrive */
+		while (fsg->cbbuf_cmnd_size == 0) {
+			rc = sleep_thread(fsg, __LINE__);
+			if (rc)
+				return rc;
+		}
+
+		/* Is the previous status interrupt request still busy?
+		 * The host is allowed to skip reading the status,
+		 * so we must cancel it. */
+		if (fsg->intreq_busy)
+			usb_ep_dequeue(fsg->intr_in, fsg->intreq);
+
+		/* Copy the command and mark the buffer empty */
+		fsg->data_dir = DATA_DIR_UNKNOWN;
+		spin_lock_irq(&fsg->lock);
+		fsg->cmnd_size = fsg->cbbuf_cmnd_size;
+		memcpy(fsg->cmnd, fsg->cbbuf_cmnd, fsg->cmnd_size);
+		fsg->cbbuf_cmnd_size = 0;
+		spin_unlock_irq(&fsg->lock);
+	}
+	return rc;
+}
+
+
+/*-------------------------------------------------------------------------*/
+
+static int enable_endpoint(struct fsg_dev *fsg, struct usb_ep *ep,
+		const struct usb_endpoint_descriptor *d)
+{
+	int	rc;
+
+	ep->driver_data = fsg;
+	rc = usb_ep_enable(ep, d);
+	if (rc)
+		ERROR(fsg, "can't enable %s, result %d\n", ep->name, rc);
+	return rc;
+}
+
+static int alloc_request(struct fsg_dev *fsg, struct usb_ep *ep,
+		struct usb_request **preq)
+{
+	*preq = usb_ep_alloc_request(ep, GFP_ATOMIC);
+	if (*preq)
+		return 0;
+	ERROR(fsg, "can't allocate request for %s\n", ep->name);
+	return -ENOMEM;
+}
+
+/*
+ * Reset interface setting and re-init endpoint state (toggle etc).
+ * Call with altsetting < 0 to disable the interface.  The only other
+ * available altsetting is 0, which enables the interface.
+ */
+static int do_set_interface(struct fsg_dev *fsg, int altsetting)
+{
+	int	rc = 0;
+	int	i;
+	const struct usb_endpoint_descriptor	*d;
+
+	if (fsg->running)
+		DBG(fsg, "reset interface\n");
+
+reset:
+	/* Deallocate the requests */
+	for (i = 0; i < FSG_NUM_BUFFERS; ++i) {
+		struct fsg_buffhd *bh = &fsg->buffhds[i];
+
+		if (bh->inreq) {
+			usb_ep_free_request(fsg->bulk_in, bh->inreq);
+			bh->inreq = NULL;
+		}
+		if (bh->outreq) {
+			usb_ep_free_request(fsg->bulk_out, bh->outreq);
+			bh->outreq = NULL;
+		}
+	}
+	if (fsg->intreq) {
+		usb_ep_free_request(fsg->intr_in, fsg->intreq);
+		fsg->intreq = NULL;
+	}
+
+	/* Disable the endpoints */
+	if (fsg->bulk_in_enabled) {
+		usb_ep_disable(fsg->bulk_in);
+		fsg->bulk_in_enabled = 0;
+	}
+	if (fsg->bulk_out_enabled) {
+		usb_ep_disable(fsg->bulk_out);
+		fsg->bulk_out_enabled = 0;
+	}
+	if (fsg->intr_in_enabled) {
+		usb_ep_disable(fsg->intr_in);
+		fsg->intr_in_enabled = 0;
+	}
+
+	fsg->running = 0;
+	if (altsetting < 0 || rc != 0)
+		return rc;
+
+	DBG(fsg, "set interface %d\n", altsetting);
+
+	/* Enable the endpoints */
+	d = fsg_ep_desc(fsg->gadget,
+			&fsg_fs_bulk_in_desc, &fsg_hs_bulk_in_desc);
+	if ((rc = enable_endpoint(fsg, fsg->bulk_in, d)) != 0)
+		goto reset;
+	fsg->bulk_in_enabled = 1;
+
+	d = fsg_ep_desc(fsg->gadget,
+			&fsg_fs_bulk_out_desc, &fsg_hs_bulk_out_desc);
+	if ((rc = enable_endpoint(fsg, fsg->bulk_out, d)) != 0)
+		goto reset;
+	fsg->bulk_out_enabled = 1;
+	fsg->bulk_out_maxpacket = le16_to_cpu(d->wMaxPacketSize);
+	clear_bit(IGNORE_BULK_OUT, &fsg->atomic_bitflags);
+
+	if (transport_is_cbi()) {
+		d = fsg_ep_desc(fsg->gadget,
+				&fsg_fs_intr_in_desc, &fsg_hs_intr_in_desc);
+		if ((rc = enable_endpoint(fsg, fsg->intr_in, d)) != 0)
+			goto reset;
+		fsg->intr_in_enabled = 1;
+	}
+
+	/* Allocate the requests */
+	for (i = 0; i < FSG_NUM_BUFFERS; ++i) {
+		struct fsg_buffhd	*bh = &fsg->buffhds[i];
+
+		if ((rc = alloc_request(fsg, fsg->bulk_in, &bh->inreq)) != 0)
+			goto reset;
+		if ((rc = alloc_request(fsg, fsg->bulk_out, &bh->outreq)) != 0)
+			goto reset;
+		bh->inreq->buf = bh->outreq->buf = bh->buf;
+		bh->inreq->context = bh->outreq->context = bh;
+		bh->inreq->complete = bulk_in_complete;
+		bh->outreq->complete = bulk_out_complete;
+	}
+	if (transport_is_cbi()) {
+		if ((rc = alloc_request(fsg, fsg->intr_in, &fsg->intreq)) != 0)
+			goto reset;
+		fsg->intreq->complete = intr_in_complete;
+	}
+
+	fsg->running = 1;
+	for (i = 0; i < fsg->nluns; ++i)
+		fsg->luns[i].unit_attention_data = SS_RESET_OCCURRED;
+	return rc;
+}
+
+
+/*
+ * Change our operational configuration.  This code must agree with the code
+ * that returns config descriptors, and with interface altsetting code.
+ *
+ * It's also responsible for power management interactions.  Some
+ * configurations might not work with our current power sources.
+ * For now we just assume the gadget is always self-powered.
+ */
+static int do_set_config(struct fsg_dev *fsg, u8 new_config)
+{
+	int	rc = 0;
+
+	/* Disable the single interface */
+	if (fsg->config != 0) {
+		DBG(fsg, "reset config\n");
+		fsg->config = 0;
+		rc = do_set_interface(fsg, -1);
+	}
+
+	/* Enable the interface */
+	if (new_config != 0) {
+		fsg->config = new_config;
+		if ((rc = do_set_interface(fsg, 0)) != 0) {
+			fsg->config = 0;	// Reset on errors
+		} else {
+			char *speed;
+
+			switch (fsg->gadget->speed) {
+			case USB_SPEED_LOW:	speed = "low";	break;
+			case USB_SPEED_FULL:	speed = "full";	break;
+			case USB_SPEED_HIGH:	speed = "high";	break;
+			default:		speed = "?";	break;
+			}
+			INFO(fsg, "%s speed config #%d\n", speed, fsg->config);
+		}
+	}
+	return rc;
+}
+
+
+/*-------------------------------------------------------------------------*/
+
+static void handle_exception(struct fsg_dev *fsg)
+{
+	int			sig;
+	int			i;
+	int			num_active;
+	struct fsg_buffhd	*bh;
+	enum fsg_state		old_state;
+	u8			new_config;
+	struct fsg_lun		*curlun;
+	unsigned int		exception_req_tag;
+	int			rc;
+
+	/* Clear the existing signals.  Anything but SIGUSR1 is converted
+	 * into a high-priority EXIT exception. */
+	for (;;) {
+		sig = 0;
+		if (!sig)
+			break;
+		/*if (sig != SIGUSR1) {
+			if (fsg->state < FSG_STATE_EXIT)
+				DBG(fsg, "Main thread exiting on signal\n");
+			raise_exception(fsg, FSG_STATE_EXIT);
+		}*/
+	}
+
+	/* Cancel all the pending transfers */
+	if (fsg->intreq_busy)
+		usb_ep_dequeue(fsg->intr_in, fsg->intreq);
+	for (i = 0; i < FSG_NUM_BUFFERS; ++i) {
+		bh = &fsg->buffhds[i];
+		if (bh->inreq_busy)
+			usb_ep_dequeue(fsg->bulk_in, bh->inreq);
+		if (bh->outreq_busy)
+			usb_ep_dequeue(fsg->bulk_out, bh->outreq);
+	}
+
+	/* Wait until everything is idle */
+	for (;;) {
+		num_active = fsg->intreq_busy;
+		for (i = 0; i < FSG_NUM_BUFFERS; ++i) {
+			bh = &fsg->buffhds[i];
+			num_active += bh->inreq_busy + bh->outreq_busy;
+		}
+		if (num_active == 0)
+			break;
+		if (sleep_thread(fsg, __LINE__))
+			return;
+	}
+
+	/* Clear out the controller's fifos */
+	if (fsg->bulk_in_enabled)
+		usb_ep_fifo_flush(fsg->bulk_in);
+	if (fsg->bulk_out_enabled)
+		usb_ep_fifo_flush(fsg->bulk_out);
+	if (fsg->intr_in_enabled)
+		usb_ep_fifo_flush(fsg->intr_in);
+
+	/* Reset the I/O buffer states and pointers, the SCSI
+	 * state, and the exception.  Then invoke the handler. */
+	spin_lock_irq(&fsg->lock);
+
+	for (i = 0; i < FSG_NUM_BUFFERS; ++i) {
+		bh = &fsg->buffhds[i];
+		bh->state = BUF_STATE_EMPTY;
+	}
+	fsg->next_buffhd_to_fill = fsg->next_buffhd_to_drain =
+			&fsg->buffhds[0];
+
+	exception_req_tag = fsg->exception_req_tag;
+	new_config = fsg->new_config;
+	old_state = fsg->state;
+
+	if (old_state == FSG_STATE_ABORT_BULK_OUT)
+		fsg->state = FSG_STATE_STATUS_PHASE;
+	else {
+		for (i = 0; i < fsg->nluns; ++i) {
+			curlun = &fsg->luns[i];
+			curlun->prevent_medium_removal = 0;
+			curlun->sense_data = curlun->unit_attention_data =
+					SS_NO_SENSE;
+			curlun->sense_data_info = 0;
+			curlun->info_valid = 0;
+		}
+		fsg->state = FSG_STATE_IDLE;
+	}
+	spin_unlock_irq(&fsg->lock);
+
+	/* Carry out any extra actions required for the exception */
+	switch (old_state) {
+	default:
+		break;
+
+	case FSG_STATE_ABORT_BULK_OUT:
+		send_status(fsg);
+		spin_lock_irq(&fsg->lock);
+		if (fsg->state == FSG_STATE_STATUS_PHASE)
+			fsg->state = FSG_STATE_IDLE;
+		spin_unlock_irq(&fsg->lock);
+		break;
+
+	case FSG_STATE_RESET:
+		/* In case we were forced against our will to halt a
+		 * bulk endpoint, clear the halt now.  (The SuperH UDC
+		 * requires this.) */
+		if (test_and_clear_bit(IGNORE_BULK_OUT, &fsg->atomic_bitflags))
+			usb_ep_clear_halt(fsg->bulk_in);
+
+		if (transport_is_bbb()) {
+			if (fsg->ep0_req_tag == exception_req_tag)
+				ep0_queue(fsg);	// Complete the status stage
+
+		} else if (transport_is_cbi())
+			send_status(fsg);	// Status by interrupt pipe
+
+		/* Technically this should go here, but it would only be
+		 * a waste of time.  Ditto for the INTERFACE_CHANGE and
+		 * CONFIG_CHANGE cases. */
+		// for (i = 0; i < fsg->nluns; ++i)
+		//	fsg->luns[i].unit_attention_data = SS_RESET_OCCURRED;
+		break;
+
+	case FSG_STATE_INTERFACE_CHANGE:
+		rc = do_set_interface(fsg, 0);
+		if (fsg->ep0_req_tag != exception_req_tag)
+			break;
+		if (rc != 0)			// STALL on errors
+			fsg_set_halt(fsg, fsg->ep0);
+		else				// Complete the status stage
+			ep0_queue(fsg);
+		break;
+
+	case FSG_STATE_CONFIG_CHANGE:
+		rc = do_set_config(fsg, new_config);
+		if (fsg->ep0_req_tag != exception_req_tag)
+			break;
+		if (rc != 0)			// STALL on errors
+			fsg_set_halt(fsg, fsg->ep0);
+		else				// Complete the status stage
+			ep0_queue(fsg);
+		break;
+
+	case FSG_STATE_DISCONNECT:
+		for (i = 0; i < fsg->nluns; ++i)
+			fsg_lun_fsync_sub(fsg->luns + i);
+		do_set_config(fsg, 0);		// Unconfigured state
+		break;
+
+	case FSG_STATE_EXIT:
+	case FSG_STATE_TERMINATED:
+		do_set_config(fsg, 0);			// Free resources
+		spin_lock_irq(&fsg->lock);
+		fsg->state = FSG_STATE_TERMINATED;	// Stop the thread
+		spin_unlock_irq(&fsg->lock);
+		break;
+	}
+}
+
+
+/*-------------------------------------------------------------------------*/
+
+int fsg_main_thread(void * _fsg)
+{
+	int ret;
+	struct fsg_dev		*fsg = the_fsg;
+
+	/* Allow the thread to be killed by a signal, but set the signal mask
+	 * to block everything but INT, TERM, KILL, and USR1. */
+	allow_signal(SIGINT);
+	allow_signal(SIGTERM);
+	allow_signal(SIGKILL);
+	allow_signal(SIGUSR1);
+
+	/* Allow the thread to be frozen */
+	set_freezable();
+
+	/* Arrange for userspace references to be interpreted as kernel
+	 * pointers.  That way we can pass a kernel pointer to a routine
+	 * that expects a __user pointer and it will work okay. */
+	//set_fs(get_ds());
+
+	/* The main loop */
+	do {
+		if (exception_in_progress(fsg)) {
+			handle_exception(fsg);
+			continue;
+		}
+
+		if (!fsg->running) {
+			//sleep_thread(fsg, __LINE__);
+			continue;
+		}
+
+		ret = get_next_command(fsg);
+		if (ret) {
+			/* Check ig USB cable has been detached */
+			if (ret == EIO)
+				return ret;
+
+			continue;
+		}
+
+		spin_lock_irq(&fsg->lock);
+		if (!exception_in_progress(fsg))
+			fsg->state = FSG_STATE_DATA_PHASE;
+		spin_unlock_irq(&fsg->lock);
+
+		if (do_scsi_command(fsg) || finish_reply(fsg)) {
+			continue;
+		}
+
+		spin_lock_irq(&fsg->lock);
+		if (!exception_in_progress(fsg))
+			fsg->state = FSG_STATE_STATUS_PHASE;
+		spin_unlock_irq(&fsg->lock);
+
+		if (send_status(fsg)) {
+			continue;
+		}
+
+		spin_lock_irq(&fsg->lock);
+		if (!exception_in_progress(fsg))
+			fsg->state = FSG_STATE_IDLE;
+		spin_unlock_irq(&fsg->lock);
+	} while (0);
+
+	spin_lock_irq(&fsg->lock);
+	fsg->thread_task = NULL;
+	spin_unlock_irq(&fsg->lock);
+
+	/* If we are exiting because of a signal, unregister the
+	 * gadget driver. */
+	if (test_and_clear_bit(REGISTERED, &fsg->atomic_bitflags))
+		; //usb_gadget_unregister_driver(&fsg_driver);
+
+	/* Let the unbind and cleanup routines know the thread has exited */
+	complete_and_exit(&fsg->thread_notifier, 0);
+	return 0;
+}
+
+
+/*-------------------------------------------------------------------------*/
+
+#if 0 /* Remove warining ================ OBS */
+/* The write permissions and store_xxx pointers are set in fsg_bind() */
+static DEVICE_ATTR(ro, 0444, fsg_show_ro, NULL);
+static DEVICE_ATTR(nofua, 0644, fsg_show_nofua, NULL);
+static DEVICE_ATTR(file, 0444, fsg_show_file, NULL);
+
+
+/*-------------------------------------------------------------------------*/
+
+static void fsg_release(struct kref *ref)
+{
+	struct fsg_dev	*fsg = container_of(ref, struct fsg_dev, ref);
+
+	kfree(fsg->luns);
+	kfree(fsg);
+}
+
+static void lun_release(struct device *dev)
+{
+	struct rw_semaphore	*filesem = dev_get_drvdata(dev);
+	struct fsg_dev		*fsg =
+		container_of(filesem, struct fsg_dev, filesem);
+
+	kref_put(&fsg->ref, fsg_release);
+}
+#endif
+static void /* __init_or_exit */ fsg_unbind(struct usb_gadget *gadget)
+{
+	struct fsg_dev		*fsg = get_gadget_data(gadget);
+	int			i;
+	struct fsg_lun		*curlun;
+	struct usb_request	*req = fsg->ep0req;
+
+	DBG(fsg, "unbind\n");
+	clear_bit(REGISTERED, &fsg->atomic_bitflags);
+
+	/* Unregister the sysfs attribute files and the LUNs */
+	for (i = 0; i < fsg->nluns; ++i) {
+		curlun = &fsg->luns[i];
+		if (curlun->registered) {
+			//device_remove_file(&curlun->dev, &dev_attr_ro);
+			//device_remove_file(&curlun->dev, &dev_attr_file);
+			fsg_lun_close(curlun);
+			/* device_unregister(&curlun->dev); */
+			curlun->registered = 0;
+		}
+	}
+
+	/* If the thread isn't already dead, tell it to exit now */
+	if (fsg->state != FSG_STATE_TERMINATED) {
+		raise_exception(fsg, FSG_STATE_EXIT);
+		wait_for_completion(&fsg->thread_notifier);
+
+		/* The cleanup routine waits for this completion also */
+		complete(&fsg->thread_notifier);
+	}
+
+	/* Free the data buffers */
+	for (i = 0; i < FSG_NUM_BUFFERS; ++i)
+		kfree(fsg->buffhds[i].buf);
+
+	/* Free the request and buffer for endpoint 0 */
+	if (req) {
+		kfree(req->buf);
+		usb_ep_free_request(fsg->ep0, req);
+	}
+
+	set_gadget_data(gadget, NULL);
+}
+
+
+static int __init check_parameters(struct fsg_dev *fsg)
+{
+	int	prot;
+	int	gcnum;
+	int	i;
+
+	/* Store the default values */
+	mod_data.transport_type = USB_PR_BULK;
+	mod_data.transport_name = "Bulk-only";
+	mod_data.protocol_type = USB_SC_SCSI;
+	mod_data.protocol_name = "Transparent SCSI";
+
+	/* Some peripheral controllers are known not to be able to
+	 * halt bulk endpoints correctly.  If one of them is present,
+	 * disable stalls.
+	 */
+	if (gadget_is_at91(fsg->gadget))
+		mod_data.can_stall = 0;
+
+	if (mod_data.release == 0xffff) {	// Parameter wasn't set
+		gcnum = 1 ;// usb_gadget_controller_number(fsg->gadget);
+		if (gcnum >= 0)
+			mod_data.release = 0x0300 + gcnum;
+		else {
+			printf("controller '%s' not recognized\n",
+				fsg->gadget->name);
+			mod_data.release = 0x0399;
+		}
+	}
+
+	prot = simple_strtol(mod_data.protocol_parm, NULL, 0);
+
+
+	/* Serial string handling.
+	 * On a real device, the serial string would be loaded
+	 * from permanent storage. */
+	if (mod_data.serial) {
+		const char *ch;
+		unsigned len = 0;
+
+		/* Sanity check :
+		 * The CB[I] specification limits the serial string to
+		 * 12 uppercase hexadecimal characters.
+		 * BBB need at least 12 uppercase hexadecimal characters,
+		 * with a maximum of 126. */
+		for (ch = mod_data.serial; *ch; ++ch) {
+			++len;
+			if ((*ch < '0' || *ch > '9') &&
+			    (*ch < 'A' || *ch > 'F')) { /* not uppercase hex */
+				printf(
+					"Invalid serial string character: %c; "
+					"Failing back to default\n",
+					*ch);
+				goto fill_serial;
+			}
+		}
+		if (len > 126 ||
+		    (mod_data.transport_type == USB_PR_BULK && len < 12) ||
+		    (mod_data.transport_type != USB_PR_BULK && len > 12)) {
+			printf(
+				"Invalid serial string length; "
+				"Failing back to default\n");
+			goto fill_serial;
+		}
+		fsg_strings[FSG_STRING_SERIAL - 1].s = mod_data.serial;
+	} else {
+		printf(
+			"Userspace failed to provide serial number; "
+			"Failing back to default\n");
+fill_serial:
+		/* Serial number not specified or invalid, make our own.
+		 * We just encode it from the driver version string,
+		 * 12 characters to comply with both CB[I] and BBB spec.
+		 * Warning : Two devices running the same kernel will have
+		 * the same fallback serial number. */
+		for (i = 0; i < 12; i += 2) {
+			unsigned char	c = DRIVER_VERSION[i / 2];
+
+			if (!c)
+				break;
+			sprintf(&fsg_string_serial[i], "%02X", c);
+		}
+	}
+
+	return 0;
+}
+
+
+static int __ref fsg_bind(struct usb_gadget *gadget)
+{
+	struct fsg_dev		*fsg = the_fsg;
+	int			rc;
+	int			i;
+	struct fsg_lun		*curlun;
+	struct usb_ep		*ep;
+	struct usb_request	*req;
+
+	fsg->gadget = gadget;
+	set_gadget_data(gadget, fsg);
+	fsg->ep0 = gadget->ep0;
+	fsg->ep0->driver_data = fsg;
+
+	if ((rc = check_parameters(fsg)) != 0) {
+		goto out;
+	}
+
+	if (mod_data.removable) {	// Enable the store_xxx attributes
+		//dev_attr_file.attr.mode = 0644;
+		//dev_attr_file.store = fsg_store_file;
+		if (!mod_data.cdrom) {
+			//dev_attr_ro.attr.mode = 0644;
+			//dev_attr_ro.store = fsg_store_ro;
+		}
+	}
+
+	/* Only for removable media? */
+	//dev_attr_nofua.attr.mode = 0644;
+	//dev_attr_nofua.store = fsg_store_nofua;
+
+	/* Find out how many LUNs there should be */
+	i = mod_data.nluns;
+	if (i == 0)
+		i = max(mod_data.num_filenames, 1u);
+	if (i > FSG_MAX_LUNS) {
+		ERROR(fsg, "invalid number of LUNs: %d\n", i);
+		rc = -EINVAL;
+		goto out;
+	}
+
+	/* Create the LUNs, open their backing files, and register the
+	 * LUN devices in sysfs. */
+	fsg->luns = kzalloc(i * sizeof(struct fsg_lun), GFP_KERNEL);
+	if (!fsg->luns) {
+		rc = -ENOMEM;
+		goto out;
+	}
+	fsg->nluns = i;
+
+	for (i = 0; i < fsg->nluns; ++i) {
+		curlun = &fsg->luns[i];
+		curlun->cdrom = !!mod_data.cdrom;
+		curlun->ro = mod_data.cdrom || mod_data.ro[i];
+		curlun->initially_ro = curlun->ro;
+		curlun->removable = mod_data.removable;
+		curlun->nofua = mod_data.nofua[i];
+		dev_set_drvdata(&curlun->dev, &fsg->filesem);
+		dev_set_name(&curlun->dev,"%s-lun%d",
+			     dev_name(&gadget->dev), i);
+
+		if ((rc = device_register(&curlun->dev)) != 0) {
+			INFO(fsg, "failed to register LUN%d: %d\n", i, rc);
+			goto out;
+		}
+		if ((rc = device_create_file(&curlun->dev,
+					&dev_attr_ro)) != 0 ||
+				(rc = device_create_file(&curlun->dev,
+					&dev_attr_nofua)) != 0 ||
+				(rc = device_create_file(&curlun->dev,
+					&dev_attr_file)) != 0) {
+			/* device_unregister(&curlun->dev); */
+			goto out;
+		}
+		curlun->registered = 1;
+		kref_get(&fsg->ref);
+
+		if ((rc = fsg_lun_open(curlun,
+		                       mod_data.file[i])) != 0) {
+			goto out;
+		}
+	}
+
+	/* Find all the endpoints we will use */
+	usb_ep_autoconfig_reset(gadget);
+	ep = usb_ep_autoconfig(gadget, &fsg_fs_bulk_in_desc);
+	if (!ep)
+		goto autoconf_fail;
+	ep->driver_data = fsg;		// claim the endpoint
+	fsg->bulk_in = ep;
+
+	ep = usb_ep_autoconfig(gadget, &fsg_fs_bulk_out_desc);
+	if (!ep)
+		goto autoconf_fail;
+	ep->driver_data = fsg;		// claim the endpoint
+	fsg->bulk_out = ep;
+
+	if (transport_is_cbi()) {
+		ep = usb_ep_autoconfig(gadget, &fsg_fs_intr_in_desc);
+		if (!ep)
+			goto autoconf_fail;
+		ep->driver_data = fsg;		// claim the endpoint
+		fsg->intr_in = ep;
+	}
+
+	/* Fix up the descriptors */
+	device_desc.bMaxPacketSize0 = fsg->ep0->maxpacket;
+	device_desc.idVendor = cpu_to_le16(mod_data.vendor);
+	device_desc.idProduct = cpu_to_le16(mod_data.product);
+	device_desc.bcdDevice = cpu_to_le16(mod_data.release);
+
+	i = (transport_is_cbi() ? 3 : 2);	// Number of endpoints
+	fsg_intf_desc.bNumEndpoints = i;
+	fsg_intf_desc.bInterfaceSubClass = mod_data.protocol_type;
+	fsg_intf_desc.bInterfaceProtocol = mod_data.transport_type;
+	fsg_fs_function[i + FSG_FS_FUNCTION_PRE_EP_ENTRIES] = NULL;
+
+	if (gadget_is_dualspeed(gadget)) {
+		fsg_hs_function[i + FSG_HS_FUNCTION_PRE_EP_ENTRIES] = NULL;
+
+		/* Assume ep0 uses the same maxpacket value for both speeds */
+		dev_qualifier.bMaxPacketSize0 = fsg->ep0->maxpacket;
+
+		/* Assume endpoint addresses are the same for both speeds */
+		fsg_hs_bulk_in_desc.bEndpointAddress =
+			fsg_fs_bulk_in_desc.bEndpointAddress;
+		fsg_hs_bulk_out_desc.bEndpointAddress =
+			fsg_fs_bulk_out_desc.bEndpointAddress;
+		fsg_hs_intr_in_desc.bEndpointAddress =
+			fsg_fs_intr_in_desc.bEndpointAddress;
+	}
+
+	if (gadget_is_otg(gadget))
+		fsg_otg_desc.bmAttributes |= USB_OTG_HNP;
+
+	rc = -ENOMEM;
+
+	/* Allocate the request and buffer for endpoint 0 */
+	fsg->ep0req = req = usb_ep_alloc_request(fsg->ep0, GFP_KERNEL);
+	if (!req)
+		goto out;
+	req->buf = kmalloc(EP0_BUFSIZE, GFP_KERNEL);
+	if (!req->buf)
+		goto out;
+	req->complete = ep0_complete;
+
+	/* Allocate the data buffers */
+	for (i = 0; i < FSG_NUM_BUFFERS; ++i) {
+		struct fsg_buffhd	*bh = &fsg->buffhds[i];
+
+		/* Allocate for the bulk-in endpoint.  We assume that
+		 * the buffer will also work with the bulk-out (and
+		 * interrupt-in) endpoint. */
+		bh->buf = kmalloc(mod_data.buflen, GFP_KERNEL);
+		if (!bh->buf)
+			goto out;
+		bh->next = bh + 1;
+	}
+	fsg->buffhds[FSG_NUM_BUFFERS - 1].next = &fsg->buffhds[0];
+
+	/* This should reflect the actual gadget power source */
+	usb_gadget_set_selfpowered(gadget);
+
+	/*snprintf(fsg_string_manufacturer, sizeof fsg_string_manufacturer,
+			"%s %s with %s",
+			init_utsname()->sysname, init_utsname()->release,
+			gadget->name);*/
+
+	fsg->thread_task = kthread_create(fsg_main_thread, fsg,
+			"file-storage-gadget");
+	if (IS_ERR(fsg->thread_task)) {
+		rc = PTR_ERR(fsg->thread_task);
+	}
+
+	INFO(fsg, DRIVER_DESC ", version: " DRIVER_VERSION "\n");
+	INFO(fsg, "Number of LUNs=%d\n", fsg->nluns);
+
+	DBG(fsg, "transport=%s (x%02x)\n",
+			mod_data.transport_name, mod_data.transport_type);
+	DBG(fsg, "protocol=%s (x%02x)\n",
+			mod_data.protocol_name, mod_data.protocol_type);
+	DBG(fsg, "VendorID=x%04x, ProductID=x%04x, Release=x%04x\n",
+			mod_data.vendor, mod_data.product, mod_data.release);
+	DBG(fsg, "removable=%d, stall=%d, cdrom=%d, buflen=%u\n",
+			mod_data.removable, mod_data.can_stall,
+			mod_data.cdrom, mod_data.buflen);
+	set_bit(REGISTERED, &fsg->atomic_bitflags);
+
+	/* Tell the thread to start working */
+	wake_up_process(fsg->thread_task);
+	return 0;
+
+autoconf_fail:
+	ERROR(fsg, "unable to autoconfigure all endpoints\n");
+	rc = -ENOTSUPP;
+
+out:
+	fsg->state = FSG_STATE_TERMINATED;	// The thread is dead
+	fsg_unbind(gadget);
+	complete(&fsg->thread_notifier);
+	return rc;
+}
+
+
+/*-------------------------------------------------------------------------*/
+
+static void fsg_suspend(struct usb_gadget *gadget)
+{
+	struct fsg_dev		*fsg = get_gadget_data(gadget);
+
+	DBG(fsg, "suspend\n");
+	set_bit(SUSPENDED, &fsg->atomic_bitflags);
+}
+
+static void fsg_resume(struct usb_gadget *gadget)
+{
+	struct fsg_dev		*fsg = get_gadget_data(gadget);
+
+	DBG(fsg, "resume\n");
+	clear_bit(SUSPENDED, &fsg->atomic_bitflags);
+}
+
+
+/*-------------------------------------------------------------------------*/
+
+static struct usb_gadget_driver		fsg_driver = {
+#ifdef CONFIG_USB_GADGET_DUALSPEED
+	.speed		= USB_SPEED_HIGH,
+#else
+	.speed		= USB_SPEED_FULL,
+#endif
+	.bind		= fsg_bind,
+	.unbind		= fsg_unbind,
+	.disconnect	= fsg_disconnect,
+	.setup		= fsg_setup,
+	.suspend	= fsg_suspend,
+	.resume		= fsg_resume,
+};
+
+static int __init fsg_alloc(void)
+{
+	struct fsg_dev		*fsg;
+
+	fsg = kzalloc(sizeof *fsg, GFP_KERNEL);
+	if (!fsg)
+		return -ENOMEM;
+	spin_lock_init(&fsg->lock);
+	init_rwsem(&fsg->filesem);
+	kref_init(&fsg->ref);
+	init_completion(&fsg->thread_notifier);
+
+	the_fsg = fsg;
+	return 0;
+}
+
+
+int __init fsg_init(struct ums_board_info* _ums)
+{
+	int		rc;
+	struct fsg_dev	*fsg;
+
+	ums_info = _ums;
+	if ((rc = fsg_alloc()) != 0)
+		return rc;
+	fsg = the_fsg;
+	if ((rc = usb_gadget_register_driver(&fsg_driver)) != 0)
+		kref_put(&fsg->ref, fsg_release);
+	usb_gadget_connect(the_fsg->gadget);
+
+	return rc;
+}
+module_init(fsg_init);
+
+#if 0
+static void __exit fsg_cleanup(void)
+{
+	struct fsg_dev	*fsg = the_fsg;
+
+	/* Unregister the driver iff the thread hasn't already done so */
+	if (test_and_clear_bit(REGISTERED, &fsg->atomic_bitflags))
+		usb_gadget_unregister_driver(&fsg_driver);
+
+	/* Wait for the thread to finish up */
+	wait_for_completion(&fsg->thread_notifier);
+
+	kref_put(&fsg->ref, fsg_release);
+}
+#endif
+module_exit(fsg_cleanup);
diff --git a/drivers/usb/gadget/storage_common.c b/drivers/usb/gadget/storage_common.c
new file mode 100644
index 0000000..6b2e592
--- /dev/null
+++ b/drivers/usb/gadget/storage_common.c
@@ -0,0 +1,762 @@
+/*
+ * storage_common.c -- Common definitions for mass storage functionality
+ *
+ * Copyright (C) 2003-2008 Alan Stern
+ * Copyeight (C) 2009 Samsung Electronics
+ * Author: Michal Nazarewicz (m.nazarewicz at samsung.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., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+ */
+
+
+/*
+ * This file requires the following identifiers used in USB strings to
+ * be defined (each of type pointer to char):
+ *  - fsg_string_manufacturer -- name of the manufacturer
+ *  - fsg_string_product      -- name of the product
+ *  - fsg_string_serial       -- product's serial
+ *  - fsg_string_config       -- name of the configuration
+ *  - fsg_string_interface    -- name of the interface
+ * The first four are only needed when FSG_DESCRIPTORS_DEVICE_STRINGS
+ * macro is defined prior to including this file.
+ */
+
+/*
+ * When FSG_NO_INTR_EP is defined fsg_fs_intr_in_desc and
+ * fsg_hs_intr_in_desc objects as well as
+ * FSG_FS_FUNCTION_PRE_EP_ENTRIES and FSG_HS_FUNCTION_PRE_EP_ENTRIES
+ * macros are not defined.
+ *
+ * When FSG_NO_DEVICE_STRINGS is defined FSG_STRING_MANUFACTURER,
+ * FSG_STRING_PRODUCT, FSG_STRING_SERIAL and FSG_STRING_CONFIG are not
+ * defined (as well as corresponding entries in string tables are
+ * missing) and FSG_STRING_INTERFACE has value of zero.
+ *
+ * When FSG_NO_OTG is defined fsg_otg_desc won't be defined.
+ */
+
+/*
+ * When FSG_BUFFHD_STATIC_BUFFER is defined when this file is included
+ * the fsg_buffhd structure's buf field will be an array of FSG_BUFLEN
+ * characters rather then a pointer to void.
+ */
+
+
+//#include <asm/unaligned.h>
+
+
+/*
+ * Thanks to NetChip Technologies for donating this product ID.
+ *
+ * DO NOT REUSE THESE IDs with any other driver!!  Ever!!
+ * Instead:  allocate your own, using normal USB-IF procedures.
+ */
+#define FSG_VENDOR_ID	0x0525	/* NetChip */
+#define FSG_PRODUCT_ID	0xa4a5	/* Linux-USB File-backed Storage Gadget */
+
+
+/*-------------------------------------------------------------------------*/
+
+
+#ifndef DEBUG
+#undef VERBOSE_DEBUG
+#undef DUMP_MSGS
+#endif /* !DEBUG */
+
+#ifdef VERBOSE_DEBUG
+#define VLDBG	LDBG
+#else
+#define VLDBG(lun, fmt, args...) do { } while (0)
+#endif /* VERBOSE_DEBUG */
+
+/*
+#define LDBG(lun, fmt, args...)   dev_dbg (&(lun)->dev, fmt, ## args)
+#define LERROR(lun, fmt, args...) dev_err (&(lun)->dev, fmt, ## args)
+#define LWARN(lun, fmt, args...)  dev_warn(&(lun)->dev, fmt, ## args)
+#define LINFO(lun, fmt, args...)  dev_info(&(lun)->dev, fmt, ## args)
+*/
+
+#define LDBG(lun, fmt, args...) do { } while (0)
+#define LERROR(lun, fmt, args...) do { } while (0)
+#define LWARN(lun, fmt, args...) do { } while (0)
+#define LINFO(lun, fmt, args...) do { } while (0)
+/*
+ * Keep those macros in sync with those in
+ * include/linux/usb/composite.h or else GCC will complain.  If they
+ * are identical (the same names of arguments, white spaces in the
+ * same places) GCC will allow redefinition otherwise (even if some
+ * white space is removed or added) warning will be issued.
+ *
+ * Those macros are needed here because File Storage Gadget does not
+ * include the composite.h header.  For composite gadgets those macros
+ * are redundant since composite.h is included any way.
+ *
+ * One could check whether those macros are already defined (which
+ * would indicate composite.h had been included) or not (which would
+ * indicate we were in FSG) but this is not done because a warning is
+ * desired if definitions here differ from the ones in composite.h.
+ *
+ * We want the definitions to match and be the same in File Storage
+ * Gadget as well as Mass Storage Function (and so composite gadgets
+ * using MSF).  If someone changes them in composite.h it will produce
+ * a warning in this file when building MSF.
+ */
+
+
+/* #define DBG(d, fmt, args...)     printf(fmt , ## args) */
+/* #define VDBG(d, fmt, args...)    printf(fmt , ## args) */
+/* #define ERROR(d, fmt, args...)   printf(fmt , ## args) */
+/* #define WARNING(d, fmt, args...) printf(fmt , ## args) */
+/* #define INFO(d, fmt, args...)    printf(fmt , ## args) */
+
+
+#define DBG(d, fmt, args...)     do { } while (0)
+#define VDBG(d, fmt, args...)    do { } while (0)
+#define ERROR(d, fmt, args...)   do { } while (0)
+#define WARNING(d, fmt, args...) do { } while (0)
+#define INFO(d, fmt, args...)    do { } while (0)
+
+
+#ifdef DUMP_MSGS
+
+#  define dump_msg(fsg, /* const char * */ label,			\
+		   /* const u8 * */ buf, /* unsigned */ length) do {	\
+	if (length < 512) {						\
+		DBG(fsg, "%s, length %u:\n", label, length);		\
+		print_hex_dump(KERN_DEBUG, "", DUMP_PREFIX_OFFSET,	\
+			       16, 1, buf, length, 0);			\
+	}								\
+} while (0)
+
+#  define dump_cdb(fsg) do { } while (0)
+
+#else
+
+#  define dump_msg(fsg, /* const char * */ label, \
+		   /* const u8 * */ buf, /* unsigned */ length) do { } while (0)
+
+#  ifdef VERBOSE_DEBUG
+
+#    define dump_cdb(fsg)						\
+	print_hex_dump(KERN_DEBUG, "SCSI CDB: ", DUMP_PREFIX_NONE,	\
+		       16, 1, (fsg)->cmnd, (fsg)->cmnd_size, 0)		\
+
+#  else
+
+#    define dump_cdb(fsg) do { } while (0)
+
+#  endif /* VERBOSE_DEBUG */
+
+#endif /* DUMP_MSGS */
+
+
+
+
+
+/*-------------------------------------------------------------------------*/
+
+/* SCSI device types */
+#define TYPE_DISK	0x00
+#define TYPE_CDROM	0x05
+
+/* USB protocol value = the transport method */
+#define USB_PR_CBI	0x00		/* Control/Bulk/Interrupt */
+#define USB_PR_CB	0x01		/* Control/Bulk w/o interrupt */
+#define USB_PR_BULK	0x50		/* Bulk-only */
+
+/* USB subclass value = the protocol encapsulation */
+#define USB_SC_RBC	0x01		/* Reduced Block Commands (flash) */
+#define USB_SC_8020	0x02		/* SFF-8020i, MMC-2, ATAPI (CD-ROM) */
+#define USB_SC_QIC	0x03		/* QIC-157 (tape) */
+#define USB_SC_UFI	0x04		/* UFI (floppy) */
+#define USB_SC_8070	0x05		/* SFF-8070i (removable) */
+#define USB_SC_SCSI	0x06		/* Transparent SCSI */
+
+/* Bulk-only data structures */
+
+/* Command Block Wrapper */
+struct fsg_bulk_cb_wrap {
+	__le32	Signature;		/* Contains 'USBC' */
+	u32	Tag;			/* Unique per command id */
+	__le32	DataTransferLength;	/* Size of the data */
+	u8	Flags;			/* Direction in bit 7 */
+	u8	Lun;			/* LUN (normally 0) */
+	u8	Length;			/* Of the CDB, <= MAX_COMMAND_SIZE */
+	u8	CDB[16];		/* Command Data Block */
+};
+
+#define USB_BULK_CB_WRAP_LEN	31
+#define USB_BULK_CB_SIG		0x43425355	/* Spells out USBC */
+#define USB_BULK_IN_FLAG	0x80
+
+/* Command Status Wrapper */
+struct bulk_cs_wrap {
+	__le32	Signature;		/* Should = 'USBS' */
+	u32	Tag;			/* Same as original command */
+	__le32	Residue;		/* Amount not transferred */
+	u8	Status;			/* See below */
+};
+
+#define USB_BULK_CS_WRAP_LEN	13
+#define USB_BULK_CS_SIG		0x53425355	/* Spells out 'USBS' */
+#define USB_STATUS_PASS		0
+#define USB_STATUS_FAIL		1
+#define USB_STATUS_PHASE_ERROR	2
+
+/* Bulk-only class specific requests */
+#define USB_BULK_RESET_REQUEST		0xff
+#define USB_BULK_GET_MAX_LUN_REQUEST	0xfe
+
+
+/* CBI Interrupt data structure */
+struct interrupt_data {
+	u8	bType;
+	u8	bValue;
+};
+
+#define CBI_INTERRUPT_DATA_LEN		2
+
+/* CBI Accept Device-Specific Command request */
+#define USB_CBI_ADSC_REQUEST		0x00
+
+
+/* Length of a SCSI Command Data Block */
+#define MAX_COMMAND_SIZE	16
+
+/* SCSI commands that we recognize */
+#define SC_FORMAT_UNIT			0x04
+#define SC_INQUIRY			0x12
+#define SC_MODE_SELECT_6		0x15
+#define SC_MODE_SELECT_10		0x55
+#define SC_MODE_SENSE_6			0x1a
+#define SC_MODE_SENSE_10		0x5a
+#define SC_PREVENT_ALLOW_MEDIUM_REMOVAL	0x1e
+#define SC_READ_6			0x08
+#define SC_READ_10			0x28
+#define SC_READ_12			0xa8
+#define SC_READ_CAPACITY		0x25
+#define SC_READ_FORMAT_CAPACITIES	0x23
+#define SC_READ_HEADER			0x44
+#define SC_READ_TOC			0x43
+#define SC_RELEASE			0x17
+#define SC_REQUEST_SENSE		0x03
+#define SC_RESERVE			0x16
+#define SC_SEND_DIAGNOSTIC		0x1d
+#define SC_START_STOP_UNIT		0x1b
+#define SC_SYNCHRONIZE_CACHE		0x35
+#define SC_TEST_UNIT_READY		0x00
+#define SC_VERIFY			0x2f
+#define SC_WRITE_6			0x0a
+#define SC_WRITE_10			0x2a
+#define SC_WRITE_12			0xaa
+
+/* SCSI Sense Key/Additional Sense Code/ASC Qualifier values */
+#define SS_NO_SENSE				0
+#define SS_COMMUNICATION_FAILURE		0x040800
+#define SS_INVALID_COMMAND			0x052000
+#define SS_INVALID_FIELD_IN_CDB			0x052400
+#define SS_LOGICAL_BLOCK_ADDRESS_OUT_OF_RANGE	0x052100
+#define SS_LOGICAL_UNIT_NOT_SUPPORTED		0x052500
+#define SS_MEDIUM_NOT_PRESENT			0x023a00
+#define SS_MEDIUM_REMOVAL_PREVENTED		0x055302
+#define SS_NOT_READY_TO_READY_TRANSITION	0x062800
+#define SS_RESET_OCCURRED			0x062900
+#define SS_SAVING_PARAMETERS_NOT_SUPPORTED	0x053900
+#define SS_UNRECOVERED_READ_ERROR		0x031100
+#define SS_WRITE_ERROR				0x030c02
+#define SS_WRITE_PROTECTED			0x072700
+
+#define SK(x)		((u8) ((x) >> 16))	/* Sense Key byte, etc. */
+#define ASC(x)		((u8) ((x) >> 8))
+#define ASCQ(x)		((u8) (x))
+
+
+struct device_attribute { int i; };
+struct rw_semaphore { int i; };
+#define down_write(...)			do { } while (0)
+#define up_write(...)			do { } while (0)
+#define down_read(...)			do { } while (0)
+#define up_read(...)			do { } while (0)
+#define ETOOSMALL	525
+
+#include <usb_mass_storage.h>
+extern struct ums_board_info		*ums_info;
+
+/*-------------------------------------------------------------------------*/
+
+
+struct fsg_lun {
+	loff_t		file_length;
+	loff_t		num_sectors;
+
+	unsigned int	initially_ro:1;
+	unsigned int	ro:1;
+	unsigned int	removable:1;
+	unsigned int	cdrom:1;
+	unsigned int	prevent_medium_removal:1;
+	unsigned int	registered:1;
+	unsigned int	info_valid:1;
+	unsigned int	nofua:1;
+
+	u32		sense_data;
+	u32		sense_data_info;
+	u32		unit_attention_data;
+
+	struct device	dev;
+};
+
+#define fsg_lun_is_open(curlun)	((curlun)->filp != NULL)
+#if 0
+static struct fsg_lun *fsg_lun_from_dev(struct device *dev)
+{
+	return container_of(dev, struct fsg_lun, dev);
+}
+#endif
+
+/* Big enough to hold our biggest descriptor */
+#define EP0_BUFSIZE	256
+#define DELAYED_STATUS	(EP0_BUFSIZE + 999)	/* An impossibly large value */
+
+/* Number of buffers we will use.  2 is enough for double-buffering */
+#define FSG_NUM_BUFFERS	2
+
+/* Default size of buffer length. */
+#define FSG_BUFLEN	((u32)16384)
+
+/* Maximal number of LUNs supported in mass storage function */
+#define FSG_MAX_LUNS	8
+
+enum fsg_buffer_state {
+	BUF_STATE_EMPTY = 0,
+	BUF_STATE_FULL,
+	BUF_STATE_BUSY
+};
+
+struct fsg_buffhd {
+#ifdef FSG_BUFFHD_STATIC_BUFFER
+	char				buf[FSG_BUFLEN];
+#else
+	void				*buf;
+#endif
+	enum fsg_buffer_state		state;
+	struct fsg_buffhd		*next;
+
+	/*
+	 * The NetChip 2280 is faster, and handles some protocol faults
+	 * better, if we don't submit any short bulk-out read requests.
+	 * So we will record the intended request length here.
+	 */
+	unsigned int			bulk_out_intended_length;
+
+	struct usb_request		*inreq;
+	int				inreq_busy;
+	struct usb_request		*outreq;
+	int				outreq_busy;
+};
+
+enum fsg_state {
+	/* This one isn't used anywhere */
+	FSG_STATE_COMMAND_PHASE = -10,
+	FSG_STATE_DATA_PHASE,
+	FSG_STATE_STATUS_PHASE,
+
+	FSG_STATE_IDLE = 0,
+	FSG_STATE_ABORT_BULK_OUT,
+	FSG_STATE_RESET,
+	FSG_STATE_INTERFACE_CHANGE,
+	FSG_STATE_CONFIG_CHANGE,
+	FSG_STATE_DISCONNECT,
+	FSG_STATE_EXIT,
+	FSG_STATE_TERMINATED
+};
+
+enum data_direction {
+	DATA_DIR_UNKNOWN = 0,
+	DATA_DIR_FROM_HOST,
+	DATA_DIR_TO_HOST,
+	DATA_DIR_NONE
+};
+
+
+/*-------------------------------------------------------------------------*/
+
+
+static inline u32 get_unaligned_be24(u8 *buf)
+{
+	return 0xffffff & (u32) get_unaligned_be32(buf - 1);
+}
+
+
+/*-------------------------------------------------------------------------*/
+
+
+enum {
+#ifndef FSG_NO_DEVICE_STRINGS
+	FSG_STRING_MANUFACTURER	= 1,
+	FSG_STRING_PRODUCT,
+	FSG_STRING_SERIAL,
+	FSG_STRING_CONFIG,
+#endif
+	FSG_STRING_INTERFACE
+};
+
+
+#ifndef FSG_NO_OTG
+static struct usb_otg_descriptor
+fsg_otg_desc = {
+	.bLength =		sizeof fsg_otg_desc,
+	.bDescriptorType =	USB_DT_OTG,
+
+	.bmAttributes =		USB_OTG_SRP,
+};
+#endif
+
+/* There is only one interface. */
+
+static struct usb_interface_descriptor
+fsg_intf_desc = {
+	.bLength =		sizeof fsg_intf_desc,
+	.bDescriptorType =	USB_DT_INTERFACE,
+
+	.bNumEndpoints =	2,		/* Adjusted during fsg_bind() */
+	.bInterfaceClass =	USB_CLASS_MASS_STORAGE,
+	.bInterfaceSubClass =	USB_SC_SCSI,	/* Adjusted during fsg_bind() */
+	.bInterfaceProtocol =	USB_PR_BULK,	/* Adjusted during fsg_bind() */
+	.iInterface =		FSG_STRING_INTERFACE,
+};
+
+/*
+ * Three full-speed endpoint descriptors: bulk-in, bulk-out, and
+ * interrupt-in.
+ */
+
+static struct usb_endpoint_descriptor
+fsg_fs_bulk_in_desc = {
+	.bLength =		USB_DT_ENDPOINT_SIZE,
+	.bDescriptorType =	USB_DT_ENDPOINT,
+
+	.bEndpointAddress =	USB_DIR_IN,
+	.bmAttributes =		USB_ENDPOINT_XFER_BULK,
+	/* wMaxPacketSize set by autoconfiguration */
+};
+
+static struct usb_endpoint_descriptor
+fsg_fs_bulk_out_desc = {
+	.bLength =		USB_DT_ENDPOINT_SIZE,
+	.bDescriptorType =	USB_DT_ENDPOINT,
+
+	.bEndpointAddress =	USB_DIR_OUT,
+	.bmAttributes =		USB_ENDPOINT_XFER_BULK,
+	/* wMaxPacketSize set by autoconfiguration */
+};
+
+#ifndef FSG_NO_INTR_EP
+
+static struct usb_endpoint_descriptor
+fsg_fs_intr_in_desc = {
+	.bLength =		USB_DT_ENDPOINT_SIZE,
+	.bDescriptorType =	USB_DT_ENDPOINT,
+
+	.bEndpointAddress =	USB_DIR_IN,
+	.bmAttributes =		USB_ENDPOINT_XFER_INT,
+	.wMaxPacketSize =	cpu_to_le16(2),
+	.bInterval =		32,	/* frames -> 32 ms */
+};
+
+#ifndef FSG_NO_OTG
+#  define FSG_FS_FUNCTION_PRE_EP_ENTRIES	2
+#else
+#  define FSG_FS_FUNCTION_PRE_EP_ENTRIES	1
+#endif
+
+#endif
+
+static struct usb_descriptor_header *fsg_fs_function[] = {
+#ifndef FSG_NO_OTG
+	(struct usb_descriptor_header *) &fsg_otg_desc,
+#endif
+	(struct usb_descriptor_header *) &fsg_intf_desc,
+	(struct usb_descriptor_header *) &fsg_fs_bulk_in_desc,
+	(struct usb_descriptor_header *) &fsg_fs_bulk_out_desc,
+#ifndef FSG_NO_INTR_EP
+	(struct usb_descriptor_header *) &fsg_fs_intr_in_desc,
+#endif
+	NULL,
+};
+
+
+/*
+ * USB 2.0 devices need to expose both high speed and full speed
+ * descriptors, unless they only run at full speed.
+ *
+ * That means alternate endpoint descriptors (bigger packets)
+ * and a "device qualifier" ... plus more construction options
+ * for the configuration descriptor.
+ */
+static struct usb_endpoint_descriptor
+fsg_hs_bulk_in_desc = {
+	.bLength =		USB_DT_ENDPOINT_SIZE,
+	.bDescriptorType =	USB_DT_ENDPOINT,
+
+	/* bEndpointAddress copied from fs_bulk_in_desc during fsg_bind() */
+	.bmAttributes =		USB_ENDPOINT_XFER_BULK,
+	.wMaxPacketSize =	cpu_to_le16(512),
+};
+
+static struct usb_endpoint_descriptor
+fsg_hs_bulk_out_desc = {
+	.bLength =		USB_DT_ENDPOINT_SIZE,
+	.bDescriptorType =	USB_DT_ENDPOINT,
+
+	/* bEndpointAddress copied from fs_bulk_out_desc during fsg_bind() */
+	.bmAttributes =		USB_ENDPOINT_XFER_BULK,
+	.wMaxPacketSize =	cpu_to_le16(512),
+	.bInterval =		1,	/* NAK every 1 uframe */
+};
+
+#ifndef FSG_NO_INTR_EP
+
+static struct usb_endpoint_descriptor
+fsg_hs_intr_in_desc = {
+	.bLength =		USB_DT_ENDPOINT_SIZE,
+	.bDescriptorType =	USB_DT_ENDPOINT,
+
+	/* bEndpointAddress copied from fs_intr_in_desc during fsg_bind() */
+	.bmAttributes =		USB_ENDPOINT_XFER_INT,
+	.wMaxPacketSize =	cpu_to_le16(2),
+	.bInterval =		9,	/* 2**(9-1) = 256 uframes -> 32 ms */
+};
+
+#ifndef FSG_NO_OTG
+#  define FSG_HS_FUNCTION_PRE_EP_ENTRIES	2
+#else
+#  define FSG_HS_FUNCTION_PRE_EP_ENTRIES	1
+#endif
+
+#endif
+
+static struct usb_descriptor_header *fsg_hs_function[] = {
+#ifndef FSG_NO_OTG
+	(struct usb_descriptor_header *) &fsg_otg_desc,
+#endif
+	(struct usb_descriptor_header *) &fsg_intf_desc,
+	(struct usb_descriptor_header *) &fsg_hs_bulk_in_desc,
+	(struct usb_descriptor_header *) &fsg_hs_bulk_out_desc,
+#ifndef FSG_NO_INTR_EP
+	(struct usb_descriptor_header *) &fsg_hs_intr_in_desc,
+#endif
+	NULL,
+};
+
+/* Maxpacket and other transfer characteristics vary by speed. */
+static struct usb_endpoint_descriptor *
+fsg_ep_desc(struct usb_gadget *g, struct usb_endpoint_descriptor *fs,
+		struct usb_endpoint_descriptor *hs)
+{
+	if (gadget_is_dualspeed(g) && g->speed == USB_SPEED_HIGH)
+		return hs;
+	return fs;
+}
+
+
+/* Static strings, in UTF-8 (for simplicity we use only ASCII characters) */
+static struct usb_string		fsg_strings[] = {
+#ifndef FSG_NO_DEVICE_STRINGS
+	{FSG_STRING_MANUFACTURER,	fsg_string_manufacturer},
+	{FSG_STRING_PRODUCT,		fsg_string_product},
+	{FSG_STRING_SERIAL,		fsg_string_serial},
+	{FSG_STRING_CONFIG,		fsg_string_config},
+#endif
+	{FSG_STRING_INTERFACE,		fsg_string_interface},
+	{}
+};
+
+static struct usb_gadget_strings	fsg_stringtab = {
+	.language	= 0x0409,		/* en-us */
+	.strings	= fsg_strings,
+};
+
+
+ /*-------------------------------------------------------------------------*/
+
+/*
+ * If the next two routines are called while the gadget is registered,
+ * the caller must own fsg->filesem for writing.
+ */
+
+static int fsg_lun_open(struct fsg_lun *curlun, const char *filename)
+{
+	int				ro;
+	int				rc = -EINVAL;
+	loff_t				size;
+	loff_t				num_sectors;
+	loff_t				min_sectors;
+
+	/* R/W if we can, R/O if we must */
+	ro = curlun->initially_ro;
+
+	ums_info->get_capacity(&(ums_info->ums_dev), &size);
+	if (size < 0) {
+		LINFO(curlun, "unable to find file size: %s\n", filename);
+		rc = (int) size;
+		goto out;
+	}
+	num_sectors = size >> 9;	/* File size in 512-byte blocks */
+	min_sectors = 1;
+	if (num_sectors < min_sectors) {
+		LINFO(curlun, "file too small: %s\n", filename);
+		rc = -ETOOSMALL;
+		goto out;
+	}
+
+	curlun->ro = ro;
+	curlun->file_length = size;
+	curlun->num_sectors = num_sectors;
+	LDBG(curlun, "open backing file: %s\n", filename);
+	rc = 0;
+
+out:
+	return rc;
+}
+
+
+static void fsg_lun_close(struct fsg_lun *curlun)
+{
+}
+
+
+/*-------------------------------------------------------------------------*/
+
+/*
+ * Sync the file data, don't bother with the metadata.
+ * This code was copied from fs/buffer.c:sys_fdatasync().
+ */
+static int fsg_lun_fsync_sub(struct fsg_lun *curlun)
+{
+	return 0;
+}
+
+static void store_cdrom_address(u8 *dest, int msf, u32 addr)
+{
+	if (msf) {
+		/* Convert to Minutes-Seconds-Frames */
+		addr >>= 2;		/* Convert to 2048-byte frames */
+		addr += 2*75;		/* Lead-in occupies 2 seconds */
+		dest[3] = addr % 75;	/* Frames */
+		addr /= 75;
+		dest[2] = addr % 60;	/* Seconds */
+		addr /= 60;
+		dest[1] = addr;		/* Minutes */
+		dest[0] = 0;		/* Reserved */
+	} else {
+		/* Absolute sector */
+		put_unaligned_be32(addr, dest);
+	}
+}
+
+
+/*-------------------------------------------------------------------------*/
+
+#if 0 /* Remove warining ================ OBS */
+static ssize_t fsg_show_ro(struct device *dev, struct device_attribute *attr,
+			   char *buf)
+{
+	struct fsg_lun	*curlun = fsg_lun_from_dev(dev);
+
+	return sprintf(buf, "%d\n", 1
+				  ? curlun->ro
+				  : curlun->initially_ro);
+}
+
+static ssize_t fsg_show_nofua(struct device *dev, struct device_attribute *attr,
+			      char *buf)
+{
+	struct fsg_lun	*curlun = fsg_lun_from_dev(dev);
+
+	return sprintf(buf, "%u\n", curlun->nofua);
+}
+
+static ssize_t fsg_show_file(struct device *dev, struct device_attribute *attr,
+			     char *buf)
+{
+	*buf = 0;
+	return 0;
+}
+
+
+static ssize_t fsg_store_ro(struct device *dev, struct device_attribute *attr,
+			    const char *buf, size_t count)
+{
+	ssize_t		rc = count;
+	struct fsg_lun	*curlun = fsg_lun_from_dev(dev);
+	unsigned long	ro;
+
+	ro = simple_strtoul(buf, NULL, 2);
+
+	/*
+	 * Allow the write-enable status to change only while the
+	 * backing file is closed.
+	 */
+	curlun->ro = ro;
+	curlun->initially_ro = ro;
+	LDBG(curlun, "read-only status set to %d\n", curlun->ro);
+	return rc;
+}
+
+static ssize_t fsg_store_nofua(struct device *dev,
+			       struct device_attribute *attr,
+			       const char *buf, size_t count)
+{
+	struct fsg_lun	*curlun = fsg_lun_from_dev(dev);
+	unsigned long	nofua;
+
+	nofua = simple_strtoul(buf, NULL, 2);
+
+	/* Sync data when switching from async mode to sync */
+	if (!nofua && curlun->nofua)
+		fsg_lun_fsync_sub(curlun);
+
+	curlun->nofua = nofua;
+
+	return count;
+}
+
+static ssize_t fsg_store_file(struct device *dev, struct device_attribute *attr,
+			      const char *buf, size_t count)
+{
+	struct fsg_lun	*curlun = fsg_lun_from_dev(dev);
+	struct rw_semaphore	*filesem = NULL; //dev_get_drvdata(dev);
+	int		rc = 0;
+
+	/* Remove a trailing newline */
+	if (count > 0 && buf[count-1] == '\n')
+		((char *) buf)[count-1] = 0;		/* Ugh! */
+
+	/* Eject current medium */
+	down_write(filesem);
+	fsg_lun_close(curlun);
+	curlun->unit_attention_data = SS_MEDIUM_NOT_PRESENT;
+
+	/* Load new medium */
+	if (count > 0 && buf[0]) {
+		rc = fsg_lun_open(curlun, buf);
+		if (rc == 0)
+			curlun->unit_attention_data =
+					SS_NOT_READY_TO_READY_TRANSITION;
+	}
+	up_write(filesem);
+	return (rc < 0 ? rc : count);
+}
+#endif
diff --git a/include/configs/s5p_goni.h b/include/configs/s5p_goni.h
index f7d300c..be7a9ef 100644
--- a/include/configs/s5p_goni.h
+++ b/include/configs/s5p_goni.h
@@ -88,6 +88,8 @@
 #define CONFIG_CMD_MTDPARTS
 #define CONFIG_CMD_MMC
 
+#define CONFIG_CMD_USB_MASS_STORAGE
+
 #define CONFIG_BOOTDELAY		1
 #define CONFIG_ZERO_BOOTDELAY_CHECK
 
@@ -229,7 +231,15 @@
 #define CONFIG_SYS_I2C_SPEED	50000
 #define CONFIG_I2C_MULTI_BUS
 #define CONFIG_SYS_MAX_I2C_BUS	7
+
+#if defined (CONFIG_CMD_USB_MASS_STORAGE)
 #define CONFIG_USB_GADGET		1
 #define CONFIG_USB_GADGET_S3C_UDC_OTG	1
 #define CONFIG_USB_GADGET_DUALSPEED	1
+#endif
+
+#if defined (CONFIG_CMD_USB_MASS_STORAGE)
+#define CONFIG_USB_GADGET_MASS_STORAGE
+#endif
+
 #endif	/* __CONFIG_H */
diff --git a/include/usb_mass_storage.h b/include/usb_mass_storage.h
new file mode 100644
index 0000000..5b45908
--- /dev/null
+++ b/include/usb_mass_storage.h
@@ -0,0 +1,36 @@
+#ifndef __USB_MASS_STORAGE_H__
+#define __USB_MASS_STORAGE_H__
+
+#define SECTOR_SIZE		0x200
+
+#include <mmc.h>
+
+struct ums_device {
+	struct mmc *mmc;
+	int dev_num;
+	int offset;
+	int part_size;
+};
+
+struct ums_board_info {
+	int (*read_sector)(struct ums_device *ums_dev,
+			   unsigned int n, void *buf);
+	int (*write_sector)(struct ums_device *ums_dev,
+			    unsigned int n, void *buf);
+	void (*get_capacity)(struct ums_device *ums_dev,
+			     long long int *capacity);
+	const char* name;
+	struct ums_device ums_dev;
+};
+
+extern int fsg_init(struct ums_board_info *);
+extern struct ums_board_info *board_ums_init(unsigned int,
+					     unsigned int, unsigned int);
+extern int usb_gadget_handle_interrupts(void);
+extern int fsg_main_thread(void *);
+
+/* USB Attach/Detach */
+extern int micro_usb_attached(void);
+extern int micro_usb_detach(void);
+
+#endif
-- 
1.7.2.3

^ permalink raw reply related	[flat|nested] 9+ messages in thread

* [U-Boot] [PATCH 0/2] usb:gadget: Linux USB Gadget framework
  2011-07-05 12:53 [U-Boot] [PATCH 0/2] usb:gadget: Linux USB Gadget framework Lukasz Majewski
  2011-07-05 12:53 ` [U-Boot] [PATCH 1/2] " Lukasz Majewski
  2011-07-05 12:53 ` [U-Boot] [PATCH 2/2] usb:gadget: USB Mass Storage Gadget support Lukasz Majewski
@ 2011-07-05 14:08 ` Wolfgang Denk
  2011-07-05 14:16 ` 馬克泡
  2011-07-05 18:46 ` Remy Bohmer
  4 siblings, 0 replies; 9+ messages in thread
From: Wolfgang Denk @ 2011-07-05 14:08 UTC (permalink / raw)
  To: u-boot

Dear Lukasz Majewski,

In message <1309870396-26363-1-git-send-email-l.majewski@samsung.com> you wrote:
> Included commits provide Linux USB gadget support for U-boot. 
> The USB Gadget infrastructure is running on top of the Samsung's
> UDC OTG controller. The code has been tested on the GONI (S5PC110)
> reference target.

What about the other architectures / SoCs?

> Aren't passing the checkpatch.pl script check, however they were 
> taken from Linux kernel. On purpose this code hasn't been corrected, to
> facilitate further code porting from Linux to U-boot (or in opposite 
> direction)

Why don't you provide reference then in the patches?  See bullet # 4
at http://www.denx.de/wiki/view/U-Boot/Patches#Attributing_Code_Copyrights_Sign

Are these really unchanged files, or what exactly did you change?

And why do you even violate coding style inthe lines that you did
change?  for example, I'm pretty sure that this was changed by you:

WARNING: space prohibited between function name and open parenthesis '('
#222: FILE: common/cmd_usb_mass_storage.c:14:
+#define UMS_DBG(fmt,args...)   printf (fmt ,##args)

ERROR: space required after that ',' (ctx:VxV)
#222: FILE: common/cmd_usb_mass_storage.c:14:
+#define UMS_DBG(fmt,args...)   printf (fmt ,##args)
                    ^

ERROR: space required after that ',' (ctx:WxV)
#222: FILE: common/cmd_usb_mass_storage.c:14:
+#define UMS_DBG(fmt,args...)   printf (fmt ,##args)
                                            ^


Best regards,

Wolfgang Denk

-- 
DENX Software Engineering GmbH,     MD: Wolfgang Denk & Detlev Zundel
HRB 165235 Munich, Office: Kirchenstr.5, D-82194 Groebenzell, Germany
Phone: (+49)-8142-66989-10 Fax: (+49)-8142-66989-80 Email: wd at denx.de
All easy problems have already been solved.

^ permalink raw reply	[flat|nested] 9+ messages in thread

* [U-Boot] [PATCH 0/2] usb:gadget: Linux USB Gadget framework
  2011-07-05 12:53 [U-Boot] [PATCH 0/2] usb:gadget: Linux USB Gadget framework Lukasz Majewski
                   ` (2 preceding siblings ...)
  2011-07-05 14:08 ` [U-Boot] [PATCH 0/2] usb:gadget: Linux USB Gadget framework Wolfgang Denk
@ 2011-07-05 14:16 ` 馬克泡
       [not found]   ` <20110711153117.78a90152@lmajewski.digital.local>
  2011-07-05 18:46 ` Remy Bohmer
  4 siblings, 1 reply; 9+ messages in thread
From: 馬克泡 @ 2011-07-05 14:16 UTC (permalink / raw)
  To: u-boot

Hi Lukasz Majewski,

2011/7/5 Lukasz Majewski <l.majewski@samsung.com>:
> Included commits provide Linux USB gadget support for U-boot.
> The USB Gadget infrastructure is running on top of the Samsung's
> UDC OTG controller. The code has been tested on the GONI (S5PC110)
> reference target.

It's very happy to see there is a port of USB OTG from Linux framework.
However, please remind that according to the patch rules on u-boot's wiki page:
http://www.denx.de/wiki/view/U-Boot/Patches#Attributing_Code_Copyrights_Sign
Include git commit hash number and kernel version which you grabbed
the code from it.

I've found that you're using the #define marco to represent registers.
I'm not sure it you're using automatically generated asm-offsets.h
method to manage
these registers.
However, it's better to rewrite it into structure form whihc is easier
to read and maintain
both in u-boot and in Linux.

If there are implemented the same way in Linux kernel, I suggest you could also
improve it in Linux kernel and send cosmetic patches.

-- 
Best regards,
Macpaul Lin

^ permalink raw reply	[flat|nested] 9+ messages in thread

* [U-Boot] [PATCH 0/2] usb:gadget: Linux USB Gadget framework
  2011-07-05 12:53 [U-Boot] [PATCH 0/2] usb:gadget: Linux USB Gadget framework Lukasz Majewski
                   ` (3 preceding siblings ...)
  2011-07-05 14:16 ` 馬克泡
@ 2011-07-05 18:46 ` Remy Bohmer
  4 siblings, 0 replies; 9+ messages in thread
From: Remy Bohmer @ 2011-07-05 18:46 UTC (permalink / raw)
  To: u-boot

Hi,

2011/7/5 Lukasz Majewski <l.majewski@samsung.com>:
> Included commits provide Linux USB gadget support for U-boot.
> The USB Gadget infrastructure is running on top of the Samsung's
> UDC OTG controller. The code has been tested on the GONI (S5PC110)
> reference target.
>
> Files:
> drivers/usb/gadget/file_storage.c
> drivers/usb/gadget/storage_common.c
>
> Aren't passing the checkpatch.pl script check, however they were
> taken from Linux kernel. On purpose this code hasn't been corrected, to
> facilitate further code porting from Linux to U-boot (or in opposite
> direction)

It looks interesting, but I really have to dig through all this code.
It is huge, and I see things being touched in the USB-ethernet gadget
stack. I need to process this carefully.
Unfortunately, due to the summer holidays I expect not to be able to
look into it in detail for the next 3 weeks...

For now, I see if I can do a quick scan and give you some quick
remarks, but you may expect more when I looked into it in detail.

Kind regards,

Remy

^ permalink raw reply	[flat|nested] 9+ messages in thread

* [U-Boot] [PATCH 0/2] usb:gadget: Linux USB Gadget framework
       [not found]   ` <20110711153117.78a90152@lmajewski.digital.local>
@ 2011-07-11 14:12     ` 馬克泡
  0 siblings, 0 replies; 9+ messages in thread
From: 馬克泡 @ 2011-07-11 14:12 UTC (permalink / raw)
  To: u-boot

Hi  Lukasz,

2011/7/11 Lukasz Majewski <l.majewski@samsung.com>

> On Tue, 05 Jul 2011 22:16:22 +0800
> ??? <macpaul@gmail.com> wrote:
>

Please keep all the discussions are CC-ed to related persons (included the
u-boot's mailing list address),
unless you have private stuff to talk. Hence every people can learn the
experience from others.

[deleted some messages]


>
> struct s3c_otg {
>        u32 s3c_udc_otg_gotgctl;
>        u32 s3c_udc_otg_gotgint;
>        u32 s3c_udc_otg_gahbcfg;
> };
>
>
We have some coding style in consensus especially for u-boot (mostly are
from Linux).
Yes, please rewrite all of them into structure format.
If all of your codes are written in C, please use C structure only.
Both u-boot and Linux are project wrote in C.
If your define will be used both in assembly and in C, please use asm-offset
techniques to
manage them.


> I didn't used the asm-offsets.h approach. Those files has been edited
> manually.
>
> As fair as I've looked into the code - it looks that both above
> approaches are widely used in u-boot - e.g.
> include/asm/arch-pxa/pxa-regs.h vs. include/asm/arch-s5pc2xx/gpio.h
>
>
Both u-boot and Linux kernel are old open source projects.
Hence there are some (and lots of them) the old codes in the project which
are hard
to be maintained. If we have time (both u-boot and LInux kernel maintainers)
will do fix
the old coding style which leads problems.

For the structures, you have to be ware of writing them in nested formats,
like,
struct aaa {
     int bbb;
     struct ccc {
     };
};

Please do un-nest the structures! For example,
struct aaa {
    int bbb;
    struct ccc ccc1;
};
struct ccc {
};

These are more easier to read and to debug.

Using checkpatch.pl will help you to fix about 85% to 95% coding style
problems.
Also saving everyone's time to review all the patches.

If you are kind and have times, please do help clean up the old coding
style.
Please add the prefix "cosmetic:" to each of the clean up patches.


> > However, it's better to rewrite it into structure form whihc is easier
> > to read and maintain
> > both in u-boot and in Linux.
>
> Please correct me if I'm wrong, but I've assumed that you are thinking
> about the second presented approach (struct s3c_otg).
>
> Can you give me a more detailed explanation about yours preferred
> approach?


Let me quote a part of the discussion between Wolfgang and I before.

Me: "> Please take a look at the similar driver "ftmac100.c", > Some coding
patterns in "ftgmac100.c" are followed as "ftmac100.c".


Wolfgang: "Oh. Thanks for pointing out.  So this other driver needs to be fixed,
     too."

Those wrong coding style have been fixed after this discussion by other
people. :-)


>  --
> Best regards,
>
> Lukasz Majewski
>
> Samsung Poland R&D Center
> Platform Group
>

Thanks!

-- 
Best regards,
Macpaul Lin

^ permalink raw reply	[flat|nested] 9+ messages in thread

* [U-Boot] [PATCH 0/2] usb:gadget: Linux USB Gadget framework
  2011-07-05 13:58 Lukasz Majewski
@ 2011-07-05 14:03 ` Wolfgang Denk
  0 siblings, 0 replies; 9+ messages in thread
From: Wolfgang Denk @ 2011-07-05 14:03 UTC (permalink / raw)
  To: u-boot

Dear Lukasz Majewski,

In message <1309874283-4862-1-git-send-email-l.majewski@samsung.com> you wrote:
> Included commits provide Linux USB gadget support for U-boot. 
> The USB Gadget infrastructure is running on top of the Samsung's
> UDC OTG controller. The code has been tested on the GONI (S5PC110)
> reference target.

Why are you reposting these patches, apparently unchanged, just one
hour after the first posting?


Wolfgang Denk

-- 
DENX Software Engineering GmbH,     MD: Wolfgang Denk & Detlev Zundel
HRB 165235 Munich, Office: Kirchenstr.5, D-82194 Groebenzell, Germany
Phone: (+49)-8142-66989-10 Fax: (+49)-8142-66989-80 Email: wd at denx.de
A woman should have compassion.
	-- Kirk, "Catspaw", stardate 3018.2

^ permalink raw reply	[flat|nested] 9+ messages in thread

* [U-Boot]  [PATCH 0/2] usb:gadget: Linux USB Gadget framework
@ 2011-07-05 13:58 Lukasz Majewski
  2011-07-05 14:03 ` Wolfgang Denk
  0 siblings, 1 reply; 9+ messages in thread
From: Lukasz Majewski @ 2011-07-05 13:58 UTC (permalink / raw)
  To: u-boot

Included commits provide Linux USB gadget support for U-boot. 
The USB Gadget infrastructure is running on top of the Samsung's
UDC OTG controller. The code has been tested on the GONI (S5PC110)
reference target.

This patches requires two other patches prepared and posted by me, namely:

[PATCH] [U-Boot] [RFC] Access mode validation for eMMC cards > 2 GiB
[U-Boot] [PATCH] i2c:gpio:s5p: I2C GPIO Software implementation

Files:
drivers/usb/gadget/file_storage.c
drivers/usb/gadget/storage_common.c

Aren't passing the checkpatch.pl script check, however they were 
taken from Linux kernel. On purpose this code hasn't been corrected, to
facilitate further code porting from Linux to U-boot (or in opposite 
direction)

v1:
- Linux USB Gadget support for Samsung targets (tested on GONI)
- Simple USB Mass Storage (UMS) Gadget implementation to proof the
  concept of running USB Gadgets in U-boot.
- The new ums command


It is important to emphase, that now USB Gadget framework can handle one 
gadget. 

p.s. I'd like to apologize all the receipments of this patch - one version 
of this patch set was without [U-Boot] tag, so probably it has been
rejected by the mail server.

Lukasz Majewski (2):
  usb:gadget: Linux USB Gadget framework
  usb:gadget: USB Mass Storage Gadget support.

 arch/arm/include/asm/arch-s5pc1xx/hs_otg.h   |   32 +
 arch/arm/include/asm/arch-s5pc1xx/regs-otg.h |  309 +++
 board/samsung/goni/goni.c                    |  139 +
 common/Makefile                              |    1 +
 common/cmd_usb_mass_storage.c                |   67 +
 drivers/usb/gadget/Makefile                  |   11 +-
 drivers/usb/gadget/file_storage.c            | 3533 ++++++++++++++++++++++++++
 drivers/usb/gadget/s3c_udc_otg.c             |  878 +++++++
 drivers/usb/gadget/s3c_udc_otg_xfer_dma.c    | 1406 ++++++++++
 drivers/usb/gadget/storage_common.c          |  762 ++++++
 include/configs/s5p_goni.h                   |   13 +
 include/usb/lin_gadget_compat.h              |   69 +
 include/usb/s3c_udc.h                        |  160 ++
 include/usb_mass_storage.h                   |   36 +
 14 files changed, 7413 insertions(+), 3 deletions(-)
 create mode 100644 arch/arm/include/asm/arch-s5pc1xx/hs_otg.h
 create mode 100644 arch/arm/include/asm/arch-s5pc1xx/regs-otg.h
 create mode 100644 common/cmd_usb_mass_storage.c
 create mode 100644 drivers/usb/gadget/file_storage.c
 create mode 100644 drivers/usb/gadget/s3c_udc_otg.c
 create mode 100644 drivers/usb/gadget/s3c_udc_otg_xfer_dma.c
 create mode 100644 drivers/usb/gadget/storage_common.c
 create mode 100644 include/usb/lin_gadget_compat.h
 create mode 100644 include/usb/s3c_udc.h
 create mode 100644 include/usb_mass_storage.h

-- 
1.7.2.3

^ permalink raw reply	[flat|nested] 9+ messages in thread

end of thread, other threads:[~2011-07-11 14:12 UTC | newest]

Thread overview: 9+ messages (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2011-07-05 12:53 [U-Boot] [PATCH 0/2] usb:gadget: Linux USB Gadget framework Lukasz Majewski
2011-07-05 12:53 ` [U-Boot] [PATCH 1/2] " Lukasz Majewski
2011-07-05 12:53 ` [U-Boot] [PATCH 2/2] usb:gadget: USB Mass Storage Gadget support Lukasz Majewski
2011-07-05 14:08 ` [U-Boot] [PATCH 0/2] usb:gadget: Linux USB Gadget framework Wolfgang Denk
2011-07-05 14:16 ` 馬克泡
     [not found]   ` <20110711153117.78a90152@lmajewski.digital.local>
2011-07-11 14:12     ` 馬克泡
2011-07-05 18:46 ` Remy Bohmer
2011-07-05 13:58 Lukasz Majewski
2011-07-05 14:03 ` Wolfgang Denk

This is an external index of several public inboxes,
see mirroring instructions on how to clone and mirror
all data and code used by this external index.