summaryrefslogtreecommitdiffstats
path: root/drivers/usb/gadget
diff options
context:
space:
mode:
Diffstat (limited to 'drivers/usb/gadget')
-rw-r--r--drivers/usb/gadget/Kconfig77
-rw-r--r--drivers/usb/gadget/Makefile8
-rw-r--r--drivers/usb/gadget/amd5536udc.c1
-rw-r--r--drivers/usb/gadget/ci13xxx_msm.c134
-rw-r--r--drivers/usb/gadget/ci13xxx_pci.c176
-rw-r--r--drivers/usb/gadget/ci13xxx_udc.c384
-rw-r--r--drivers/usb/gadget/ci13xxx_udc.h19
-rw-r--r--drivers/usb/gadget/composite.c10
-rw-r--r--drivers/usb/gadget/dummy_hcd.c251
-rw-r--r--drivers/usb/gadget/f_fs.c437
-rw-r--r--drivers/usb/gadget/f_mass_storage.c524
-rw-r--r--drivers/usb/gadget/f_ncm.c1407
-rw-r--r--drivers/usb/gadget/file_storage.c29
-rw-r--r--drivers/usb/gadget/g_ffs.c41
-rw-r--r--drivers/usb/gadget/gadget_chips.h25
-rw-r--r--drivers/usb/gadget/imx_udc.c8
-rw-r--r--drivers/usb/gadget/imx_udc.h3
-rw-r--r--drivers/usb/gadget/langwell_udc.c23
-rw-r--r--drivers/usb/gadget/mass_storage.c2
-rw-r--r--drivers/usb/gadget/mv_udc.h294
-rw-r--r--drivers/usb/gadget/mv_udc_core.c2149
-rw-r--r--drivers/usb/gadget/mv_udc_phy.c214
-rw-r--r--drivers/usb/gadget/ncm.c248
-rw-r--r--drivers/usb/gadget/pch_udc.c2947
-rw-r--r--drivers/usb/gadget/u_audio.c10
-rw-r--r--drivers/usb/gadget/u_ether.c14
-rw-r--r--drivers/usb/gadget/u_ether.h5
27 files changed, 8580 insertions, 860 deletions
diff --git a/drivers/usb/gadget/Kconfig b/drivers/usb/gadget/Kconfig
index 607d0db4a98..1dc9739277b 100644
--- a/drivers/usb/gadget/Kconfig
+++ b/drivers/usb/gadget/Kconfig
@@ -338,6 +338,19 @@ config USB_S3C2410_DEBUG
boolean "S3C2410 udc debug messages"
depends on USB_GADGET_S3C2410
+config USB_GADGET_PXA_U2O
+ boolean "PXA9xx Processor USB2.0 controller"
+ select USB_GADGET_DUALSPEED
+ help
+ PXA9xx Processor series include a high speed USB2.0 device
+ controller, which support high speed and full speed USB peripheral.
+
+config USB_PXA_U2O
+ tristate
+ depends on USB_GADGET_PXA_U2O
+ default USB_GADGET
+ select USB_GADGET_SELECTED
+
#
# Controllers available in both integrated and discrete versions
#
@@ -414,8 +427,8 @@ config USB_FSL_QE
default USB_GADGET
select USB_GADGET_SELECTED
-config USB_GADGET_CI13XXX
- boolean "MIPS USB CI13xxx"
+config USB_GADGET_CI13XXX_PCI
+ boolean "MIPS USB CI13xxx PCI UDC"
depends on PCI
select USB_GADGET_DUALSPEED
help
@@ -426,9 +439,9 @@ config USB_GADGET_CI13XXX
dynamically linked module called "ci13xxx_udc" and force all
gadget drivers to also be dynamically linked.
-config USB_CI13XXX
+config USB_CI13XXX_PCI
tristate
- depends on USB_GADGET_CI13XXX
+ depends on USB_GADGET_CI13XXX_PCI
default USB_GADGET
select USB_GADGET_SELECTED
@@ -495,6 +508,49 @@ config USB_LANGWELL
default USB_GADGET
select USB_GADGET_SELECTED
+config USB_GADGET_EG20T
+ boolean "Intel EG20T(Topcliff) USB Device controller"
+ depends on PCI
+ select USB_GADGET_DUALSPEED
+ help
+ This is a USB device driver for EG20T PCH.
+ EG20T PCH is the platform controller hub that is used in Intel's
+ general embedded platform. EG20T PCH has USB device interface.
+ Using this interface, it is able to access system devices connected
+ to USB device.
+ This driver enables USB device function.
+ USB device is a USB peripheral controller which
+ supports both full and high speed USB 2.0 data transfers.
+ This driver supports both control transfer and bulk transfer modes.
+ This driver dose not support interrupt transfer or isochronous
+ transfer modes.
+
+config USB_EG20T
+ tristate
+ depends on USB_GADGET_EG20T
+ default USB_GADGET
+ select USB_GADGET_SELECTED
+
+config USB_GADGET_CI13XXX_MSM
+ boolean "MIPS USB CI13xxx for MSM"
+ depends on ARCH_MSM
+ select USB_GADGET_DUALSPEED
+ select USB_MSM_OTG_72K
+ help
+ MSM SoC has chipidea USB controller. This driver uses
+ ci13xxx_udc core.
+ This driver depends on OTG driver for PHY initialization,
+ clock management, powering up VBUS, and power management.
+
+ Say "y" to link the driver statically, or "m" to build a
+ dynamically linked module called "ci13xxx_msm" and force all
+ gadget drivers to also be dynamically linked.
+
+config USB_CI13XXX_MSM
+ tristate
+ depends on USB_GADGET_CI13XXX_MSM
+ default USB_GADGET
+ select USB_GADGET_SELECTED
#
# LAST -- dummy/emulated controller
@@ -685,6 +741,19 @@ config USB_ETH_EEM
If you say "y" here, the Ethernet gadget driver will use the EEM
protocol rather than ECM. If unsure, say "n".
+config USB_G_NCM
+ tristate "Network Control Model (NCM) support"
+ depends on NET
+ select CRC32
+ help
+ This driver implements USB CDC NCM subclass standard. NCM is
+ an advanced protocol for Ethernet encapsulation, allows grouping
+ of several ethernet frames into one USB transfer and diffferent
+ alignment possibilities.
+
+ Say "y" to link the driver statically, or "m" to build a
+ dynamically linked module called "g_ncm".
+
config USB_GADGETFS
tristate "Gadget Filesystem (EXPERIMENTAL)"
depends on EXPERIMENTAL
diff --git a/drivers/usb/gadget/Makefile b/drivers/usb/gadget/Makefile
index 5780db42417..55f5e8ae592 100644
--- a/drivers/usb/gadget/Makefile
+++ b/drivers/usb/gadget/Makefile
@@ -21,9 +21,13 @@ fsl_usb2_udc-$(CONFIG_ARCH_MXC) += fsl_mxc_udc.o
obj-$(CONFIG_USB_M66592) += m66592-udc.o
obj-$(CONFIG_USB_R8A66597) += r8a66597-udc.o
obj-$(CONFIG_USB_FSL_QE) += fsl_qe_udc.o
-obj-$(CONFIG_USB_CI13XXX) += ci13xxx_udc.o
+obj-$(CONFIG_USB_CI13XXX_PCI) += ci13xxx_pci.o
obj-$(CONFIG_USB_S3C_HSOTG) += s3c-hsotg.o
obj-$(CONFIG_USB_LANGWELL) += langwell_udc.o
+obj-$(CONFIG_USB_EG20T) += pch_udc.o
+obj-$(CONFIG_USB_PXA_U2O) += mv_udc.o
+mv_udc-y := mv_udc_core.o mv_udc_phy.o
+obj-$(CONFIG_USB_CI13XXX_MSM) += ci13xxx_msm.o
#
# USB gadget drivers
@@ -43,6 +47,7 @@ g_hid-y := hid.o
g_dbgp-y := dbgp.o
g_nokia-y := nokia.o
g_webcam-y := webcam.o
+g_ncm-y := ncm.o
obj-$(CONFIG_USB_ZERO) += g_zero.o
obj-$(CONFIG_USB_AUDIO) += g_audio.o
@@ -60,3 +65,4 @@ obj-$(CONFIG_USB_G_DBGP) += g_dbgp.o
obj-$(CONFIG_USB_G_MULTI) += g_multi.o
obj-$(CONFIG_USB_G_NOKIA) += g_nokia.o
obj-$(CONFIG_USB_G_WEBCAM) += g_webcam.o
+obj-$(CONFIG_USB_G_NCM) += g_ncm.o
diff --git a/drivers/usb/gadget/amd5536udc.c b/drivers/usb/gadget/amd5536udc.c
index 9034e034472..f8dd7269d79 100644
--- a/drivers/usb/gadget/amd5536udc.c
+++ b/drivers/usb/gadget/amd5536udc.c
@@ -3359,7 +3359,6 @@ static int udc_probe(struct udc *dev)
dev_set_name(&dev->gadget.dev, "gadget");
dev->gadget.dev.release = gadget_release;
dev->gadget.name = name;
- dev->gadget.name = name;
dev->gadget.is_dualspeed = 1;
/* init registers, interrupts, ... */
diff --git a/drivers/usb/gadget/ci13xxx_msm.c b/drivers/usb/gadget/ci13xxx_msm.c
new file mode 100644
index 00000000000..139ac941959
--- /dev/null
+++ b/drivers/usb/gadget/ci13xxx_msm.c
@@ -0,0 +1,134 @@
+/* Copyright (c) 2010, Code Aurora Forum. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 and
+ * only version 2 as published by the Free Software Foundation.
+ *
+ * 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., 51 Franklin Street, Fifth Floor, Boston, MA
+ * 02110-1301, USA.
+ *
+ */
+
+#include <linux/module.h>
+#include <linux/platform_device.h>
+#include <linux/pm_runtime.h>
+#include <linux/usb/msm_hsusb_hw.h>
+#include <linux/usb/ulpi.h>
+
+#include "ci13xxx_udc.c"
+
+#define MSM_USB_BASE (udc->regs)
+
+static irqreturn_t msm_udc_irq(int irq, void *data)
+{
+ return udc_irq();
+}
+
+static void ci13xxx_msm_notify_event(struct ci13xxx *udc, unsigned event)
+{
+ struct device *dev = udc->gadget.dev.parent;
+ int val;
+
+ switch (event) {
+ case CI13XXX_CONTROLLER_RESET_EVENT:
+ dev_dbg(dev, "CI13XXX_CONTROLLER_RESET_EVENT received\n");
+ writel(0, USB_AHBBURST);
+ writel(0, USB_AHBMODE);
+ break;
+ case CI13XXX_CONTROLLER_STOPPED_EVENT:
+ dev_dbg(dev, "CI13XXX_CONTROLLER_STOPPED_EVENT received\n");
+ /*
+ * Put the transceiver in non-driving mode. Otherwise host
+ * may not detect soft-disconnection.
+ */
+ val = otg_io_read(udc->transceiver, ULPI_FUNC_CTRL);
+ val &= ~ULPI_FUNC_CTRL_OPMODE_MASK;
+ val |= ULPI_FUNC_CTRL_OPMODE_NONDRIVING;
+ otg_io_write(udc->transceiver, val, ULPI_FUNC_CTRL);
+ break;
+ default:
+ dev_dbg(dev, "unknown ci13xxx_udc event\n");
+ break;
+ }
+}
+
+static struct ci13xxx_udc_driver ci13xxx_msm_udc_driver = {
+ .name = "ci13xxx_msm",
+ .flags = CI13XXX_REGS_SHARED |
+ CI13XXX_REQUIRE_TRANSCEIVER |
+ CI13XXX_PULLUP_ON_VBUS |
+ CI13XXX_DISABLE_STREAMING,
+
+ .notify_event = ci13xxx_msm_notify_event,
+};
+
+static int ci13xxx_msm_probe(struct platform_device *pdev)
+{
+ struct resource *res;
+ void __iomem *regs;
+ int irq;
+ int ret;
+
+ dev_dbg(&pdev->dev, "ci13xxx_msm_probe\n");
+
+ res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
+ if (!res) {
+ dev_err(&pdev->dev, "failed to get platform resource mem\n");
+ return -ENXIO;
+ }
+
+ regs = ioremap(res->start, resource_size(res));
+ if (!regs) {
+ dev_err(&pdev->dev, "ioremap failed\n");
+ return -ENOMEM;
+ }
+
+ ret = udc_probe(&ci13xxx_msm_udc_driver, &pdev->dev, regs);
+ if (ret < 0) {
+ dev_err(&pdev->dev, "udc_probe failed\n");
+ goto iounmap;
+ }
+
+ irq = platform_get_irq(pdev, 0);
+ if (irq < 0) {
+ dev_err(&pdev->dev, "IRQ not found\n");
+ ret = -ENXIO;
+ goto udc_remove;
+ }
+
+ ret = request_irq(irq, msm_udc_irq, IRQF_SHARED, pdev->name, pdev);
+ if (ret < 0) {
+ dev_err(&pdev->dev, "request_irq failed\n");
+ goto udc_remove;
+ }
+
+ pm_runtime_no_callbacks(&pdev->dev);
+ pm_runtime_enable(&pdev->dev);
+
+ return 0;
+
+udc_remove:
+ udc_remove();
+iounmap:
+ iounmap(regs);
+
+ return ret;
+}
+
+static struct platform_driver ci13xxx_msm_driver = {
+ .probe = ci13xxx_msm_probe,
+ .driver = { .name = "msm_hsusb", },
+};
+
+static int __init ci13xxx_msm_init(void)
+{
+ return platform_driver_register(&ci13xxx_msm_driver);
+}
+module_init(ci13xxx_msm_init);
diff --git a/drivers/usb/gadget/ci13xxx_pci.c b/drivers/usb/gadget/ci13xxx_pci.c
new file mode 100644
index 00000000000..883ab5e832d
--- /dev/null
+++ b/drivers/usb/gadget/ci13xxx_pci.c
@@ -0,0 +1,176 @@
+/*
+ * ci13xxx_pci.c - MIPS USB IP core family device controller
+ *
+ * Copyright (C) 2008 Chipidea - MIPS Technologies, Inc. All rights reserved.
+ *
+ * Author: David Lopo
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ */
+
+#include <linux/module.h>
+#include <linux/pci.h>
+
+#include "ci13xxx_udc.c"
+
+/* driver name */
+#define UDC_DRIVER_NAME "ci13xxx_pci"
+
+/******************************************************************************
+ * PCI block
+ *****************************************************************************/
+/**
+ * ci13xxx_pci_irq: interrut handler
+ * @irq: irq number
+ * @pdev: USB Device Controller interrupt source
+ *
+ * This function returns IRQ_HANDLED if the IRQ has been handled
+ * This is an ISR don't trace, use attribute interface instead
+ */
+static irqreturn_t ci13xxx_pci_irq(int irq, void *pdev)
+{
+ if (irq == 0) {
+ dev_err(&((struct pci_dev *)pdev)->dev, "Invalid IRQ0 usage!");
+ return IRQ_HANDLED;
+ }
+ return udc_irq();
+}
+
+static struct ci13xxx_udc_driver ci13xxx_pci_udc_driver = {
+ .name = UDC_DRIVER_NAME,
+};
+
+/**
+ * ci13xxx_pci_probe: PCI probe
+ * @pdev: USB device controller being probed
+ * @id: PCI hotplug ID connecting controller to UDC framework
+ *
+ * This function returns an error code
+ * Allocates basic PCI resources for this USB device controller, and then
+ * invokes the udc_probe() method to start the UDC associated with it
+ */
+static int __devinit ci13xxx_pci_probe(struct pci_dev *pdev,
+ const struct pci_device_id *id)
+{
+ void __iomem *regs = NULL;
+ int retval = 0;
+
+ if (id == NULL)
+ return -EINVAL;
+
+ retval = pci_enable_device(pdev);
+ if (retval)
+ goto done;
+
+ if (!pdev->irq) {
+ dev_err(&pdev->dev, "No IRQ, check BIOS/PCI setup!");
+ retval = -ENODEV;
+ goto disable_device;
+ }
+
+ retval = pci_request_regions(pdev, UDC_DRIVER_NAME);
+ if (retval)
+ goto disable_device;
+
+ /* BAR 0 holds all the registers */
+ regs = pci_iomap(pdev, 0, 0);
+ if (!regs) {
+ dev_err(&pdev->dev, "Error mapping memory!");
+ retval = -EFAULT;
+ goto release_regions;
+ }
+ pci_set_drvdata(pdev, (__force void *)regs);
+
+ pci_set_master(pdev);
+ pci_try_set_mwi(pdev);
+
+ retval = udc_probe(&ci13xxx_pci_udc_driver, &pdev->dev, regs);
+ if (retval)
+ goto iounmap;
+
+ /* our device does not have MSI capability */
+
+ retval = request_irq(pdev->irq, ci13xxx_pci_irq, IRQF_SHARED,
+ UDC_DRIVER_NAME, pdev);
+ if (retval)
+ goto gadget_remove;
+
+ return 0;
+
+ gadget_remove:
+ udc_remove();
+ iounmap:
+ pci_iounmap(pdev, regs);
+ release_regions:
+ pci_release_regions(pdev);
+ disable_device:
+ pci_disable_device(pdev);
+ done:
+ return retval;
+}
+
+/**
+ * ci13xxx_pci_remove: PCI remove
+ * @pdev: USB Device Controller being removed
+ *
+ * Reverses the effect of ci13xxx_pci_probe(),
+ * first invoking the udc_remove() and then releases
+ * all PCI resources allocated for this USB device controller
+ */
+static void __devexit ci13xxx_pci_remove(struct pci_dev *pdev)
+{
+ free_irq(pdev->irq, pdev);
+ udc_remove();
+ pci_iounmap(pdev, (__force void __iomem *)pci_get_drvdata(pdev));
+ pci_release_regions(pdev);
+ pci_disable_device(pdev);
+}
+
+/**
+ * PCI device table
+ * PCI device structure
+ *
+ * Check "pci.h" for details
+ */
+static DEFINE_PCI_DEVICE_TABLE(ci13xxx_pci_id_table) = {
+ { PCI_DEVICE(0x153F, 0x1004) },
+ { PCI_DEVICE(0x153F, 0x1006) },
+ { 0, 0, 0, 0, 0, 0, 0 /* end: all zeroes */ }
+};
+MODULE_DEVICE_TABLE(pci, ci13xxx_pci_id_table);
+
+static struct pci_driver ci13xxx_pci_driver = {
+ .name = UDC_DRIVER_NAME,
+ .id_table = ci13xxx_pci_id_table,
+ .probe = ci13xxx_pci_probe,
+ .remove = __devexit_p(ci13xxx_pci_remove),
+};
+
+/**
+ * ci13xxx_pci_init: module init
+ *
+ * Driver load
+ */
+static int __init ci13xxx_pci_init(void)
+{
+ return pci_register_driver(&ci13xxx_pci_driver);
+}
+module_init(ci13xxx_pci_init);
+
+/**
+ * ci13xxx_pci_exit: module exit
+ *
+ * Driver unload
+ */
+static void __exit ci13xxx_pci_exit(void)
+{
+ pci_unregister_driver(&ci13xxx_pci_driver);
+}
+module_exit(ci13xxx_pci_exit);
+
+MODULE_AUTHOR("MIPS - David Lopo <dlopo@chipidea.mips.com>");
+MODULE_DESCRIPTION("MIPS CI13XXX USB Peripheral Controller");
+MODULE_LICENSE("GPL");
+MODULE_VERSION("June 2008");
diff --git a/drivers/usb/gadget/ci13xxx_udc.c b/drivers/usb/gadget/ci13xxx_udc.c
index 98b36fc88c7..f200e472e47 100644
--- a/drivers/usb/gadget/ci13xxx_udc.c
+++ b/drivers/usb/gadget/ci13xxx_udc.c
@@ -22,7 +22,6 @@
* - ENDPT: endpoint operations (Gadget API)
* - GADGET: gadget operations (Gadget API)
* - BUS: bus glue code, bus abstraction layer
- * - PCI: PCI core interface and PCI resources (interrupts, memory...)
*
* Compile Options
* - CONFIG_USB_GADGET_DEBUG_FILES: enable debug facilities
@@ -60,11 +59,11 @@
#include <linux/io.h>
#include <linux/irq.h>
#include <linux/kernel.h>
-#include <linux/module.h>
-#include <linux/pci.h>
#include <linux/slab.h>
+#include <linux/pm_runtime.h>
#include <linux/usb/ch9.h>
#include <linux/usb/gadget.h>
+#include <linux/usb/otg.h>
#include "ci13xxx_udc.h"
@@ -75,9 +74,6 @@
/* ctrl register bank access */
static DEFINE_SPINLOCK(udc_lock);
-/* driver name */
-#define UDC_DRIVER_NAME "ci13xxx_udc"
-
/* control endpoint description */
static const struct usb_endpoint_descriptor
ctrl_endpt_desc = {
@@ -132,6 +128,9 @@ static struct {
size_t size; /* bank size */
} hw_bank;
+/* MSM specific */
+#define ABS_AHBBURST (0x0090UL)
+#define ABS_AHBMODE (0x0098UL)
/* UDC register map */
#define ABS_CAPLENGTH (0x100UL)
#define ABS_HCCPARAMS (0x108UL)
@@ -248,13 +247,7 @@ static u32 hw_ctest_and_write(u32 addr, u32 mask, u32 data)
return (reg & mask) >> ffs_nr(mask);
}
-/**
- * hw_device_reset: resets chip (execute without interruption)
- * @base: register base address
- *
- * This function returns an error code
- */
-static int hw_device_reset(void __iomem *base)
+static int hw_device_init(void __iomem *base)
{
u32 reg;
@@ -271,6 +264,28 @@ static int hw_device_reset(void __iomem *base)
hw_bank.size += CAP_LAST;
hw_bank.size /= sizeof(u32);
+ reg = hw_aread(ABS_DCCPARAMS, DCCPARAMS_DEN) >> ffs_nr(DCCPARAMS_DEN);
+ if (reg == 0 || reg > ENDPT_MAX)
+ return -ENODEV;
+
+ hw_ep_max = reg; /* cache hw ENDPT_MAX */
+
+ /* setup lock mode ? */
+
+ /* ENDPTSETUPSTAT is '0' by default */
+
+ /* HCSPARAMS.bf.ppc SHOULD BE zero for device */
+
+ return 0;
+}
+/**
+ * hw_device_reset: resets chip (execute without interruption)
+ * @base: register base address
+ *
+ * This function returns an error code
+ */
+static int hw_device_reset(struct ci13xxx *udc)
+{
/* should flush & stop before reset */
hw_cwrite(CAP_ENDPTFLUSH, ~0, ~0);
hw_cwrite(CAP_USBCMD, USBCMD_RS, 0);
@@ -279,6 +294,14 @@ static int hw_device_reset(void __iomem *base)
while (hw_cread(CAP_USBCMD, USBCMD_RST))
udelay(10); /* not RTOS friendly */
+
+ if (udc->udc_driver->notify_event)
+ udc->udc_driver->notify_event(udc,
+ CI13XXX_CONTROLLER_RESET_EVENT);
+
+ if (udc->udc_driver->flags && CI13XXX_DISABLE_STREAMING)
+ hw_cwrite(CAP_USBMODE, USBMODE_SDIS, USBMODE_SDIS);
+
/* USBMODE should be configured step by step */
hw_cwrite(CAP_USBMODE, USBMODE_CM, USBMODE_CM_IDLE);
hw_cwrite(CAP_USBMODE, USBMODE_CM, USBMODE_CM_DEVICE);
@@ -290,18 +313,6 @@ static int hw_device_reset(void __iomem *base)
return -ENODEV;
}
- reg = hw_aread(ABS_DCCPARAMS, DCCPARAMS_DEN) >> ffs_nr(DCCPARAMS_DEN);
- if (reg == 0 || reg > ENDPT_MAX)
- return -ENODEV;
-
- hw_ep_max = reg; /* cache hw ENDPT_MAX */
-
- /* setup lock mode ? */
-
- /* ENDPTSETUPSTAT is '0' by default */
-
- /* HCSPARAMS.bf.ppc SHOULD BE zero for device */
-
return 0;
}
@@ -1557,8 +1568,6 @@ __acquires(mEp->lock)
* Caller must hold lock
*/
static int _gadget_stop_activity(struct usb_gadget *gadget)
-__releases(udc->lock)
-__acquires(udc->lock)
{
struct usb_ep *ep;
struct ci13xxx *udc = container_of(gadget, struct ci13xxx, gadget);
@@ -1570,8 +1579,6 @@ __acquires(udc->lock)
if (gadget == NULL)
return -EINVAL;
- spin_unlock(udc->lock);
-
/* flush all endpoints */
gadget_for_each_ep(ep, gadget) {
usb_ep_fifo_flush(ep);
@@ -1591,8 +1598,6 @@ __acquires(udc->lock)
mEp->status = NULL;
}
- spin_lock(udc->lock);
-
return 0;
}
@@ -1621,6 +1626,7 @@ __acquires(udc->lock)
dbg_event(0xFF, "BUS RST", 0);
+ spin_unlock(udc->lock);
retval = _gadget_stop_activity(&udc->gadget);
if (retval)
goto done;
@@ -1629,10 +1635,9 @@ __acquires(udc->lock)
if (retval)
goto done;
- spin_unlock(udc->lock);
retval = usb_ep_enable(&mEp->ep, &ctrl_endpt_desc);
if (!retval) {
- mEp->status = usb_ep_alloc_request(&mEp->ep, GFP_KERNEL);
+ mEp->status = usb_ep_alloc_request(&mEp->ep, GFP_ATOMIC);
if (mEp->status == NULL) {
usb_ep_disable(&mEp->ep);
retval = -ENOMEM;
@@ -2061,7 +2066,6 @@ static struct usb_request *ep_alloc_request(struct usb_ep *ep, gfp_t gfp_flags)
{
struct ci13xxx_ep *mEp = container_of(ep, struct ci13xxx_ep, ep);
struct ci13xxx_req *mReq = NULL;
- unsigned long flags;
trace("%p, %i", ep, gfp_flags);
@@ -2070,8 +2074,6 @@ static struct usb_request *ep_alloc_request(struct usb_ep *ep, gfp_t gfp_flags)
return NULL;
}
- spin_lock_irqsave(mEp->lock, flags);
-
mReq = kzalloc(sizeof(struct ci13xxx_req), gfp_flags);
if (mReq != NULL) {
INIT_LIST_HEAD(&mReq->queue);
@@ -2086,8 +2088,6 @@ static struct usb_request *ep_alloc_request(struct usb_ep *ep, gfp_t gfp_flags)
dbg_event(_usb_addr(mEp), "ALLOC", mReq == NULL);
- spin_unlock_irqrestore(mEp->lock, flags);
-
return (mReq == NULL) ? NULL : &mReq->req;
}
@@ -2332,12 +2332,47 @@ static const struct usb_ep_ops usb_ep_ops = {
/******************************************************************************
* GADGET block
*****************************************************************************/
+static int ci13xxx_vbus_session(struct usb_gadget *_gadget, int is_active)
+{
+ struct ci13xxx *udc = container_of(_gadget, struct ci13xxx, gadget);
+ unsigned long flags;
+ int gadget_ready = 0;
+
+ if (!(udc->udc_driver->flags & CI13XXX_PULLUP_ON_VBUS))
+ return -EOPNOTSUPP;
+
+ spin_lock_irqsave(udc->lock, flags);
+ udc->vbus_active = is_active;
+ if (udc->driver)
+ gadget_ready = 1;
+ spin_unlock_irqrestore(udc->lock, flags);
+
+ if (gadget_ready) {
+ if (is_active) {
+ pm_runtime_get_sync(&_gadget->dev);
+ hw_device_reset(udc);
+ hw_device_state(udc->ci13xxx_ep[0].qh[RX].dma);
+ } else {
+ hw_device_state(0);
+ if (udc->udc_driver->notify_event)
+ udc->udc_driver->notify_event(udc,
+ CI13XXX_CONTROLLER_STOPPED_EVENT);
+ _gadget_stop_activity(&udc->gadget);
+ pm_runtime_put_sync(&_gadget->dev);
+ }
+ }
+
+ return 0;
+}
+
/**
* Device operations part of the API to the USB controller hardware,
* which don't involve endpoints (or i/o)
* Check "usb_gadget.h" for details
*/
-static const struct usb_gadget_ops usb_gadget_ops;
+static const struct usb_gadget_ops usb_gadget_ops = {
+ .vbus_session = ci13xxx_vbus_session,
+};
/**
* usb_gadget_probe_driver: register a gadget driver
@@ -2390,7 +2425,6 @@ int usb_gadget_probe_driver(struct usb_gadget_driver *driver,
info("hw_ep_max = %d", hw_ep_max);
udc->driver = driver;
- udc->gadget.ops = NULL;
udc->gadget.dev.driver = NULL;
retval = 0;
@@ -2410,9 +2444,11 @@ int usb_gadget_probe_driver(struct usb_gadget_driver *driver,
/* this allocation cannot be random */
for (k = RX; k <= TX; k++) {
INIT_LIST_HEAD(&mEp->qh[k].queue);
+ spin_unlock_irqrestore(udc->lock, flags);
mEp->qh[k].ptr = dma_pool_alloc(udc->qh_pool,
GFP_KERNEL,
&mEp->qh[k].dma);
+ spin_lock_irqsave(udc->lock, flags);
if (mEp->qh[k].ptr == NULL)
retval = -ENOMEM;
else
@@ -2429,7 +2465,6 @@ int usb_gadget_probe_driver(struct usb_gadget_driver *driver,
/* bind gadget */
driver->driver.bus = NULL;
- udc->gadget.ops = &usb_gadget_ops;
udc->gadget.dev.driver = &driver->driver;
spin_unlock_irqrestore(udc->lock, flags);
@@ -2437,12 +2472,24 @@ int usb_gadget_probe_driver(struct usb_gadget_driver *driver,
spin_lock_irqsave(udc->lock, flags);
if (retval) {
- udc->gadget.ops = NULL;
udc->gadget.dev.driver = NULL;
goto done;
}
+ pm_runtime_get_sync(&udc->gadget.dev);
+ if (udc->udc_driver->flags & CI13XXX_PULLUP_ON_VBUS) {
+ if (udc->vbus_active) {
+ if (udc->udc_driver->flags & CI13XXX_REGS_SHARED)
+ hw_device_reset(udc);
+ } else {
+ pm_runtime_put_sync(&udc->gadget.dev);
+ goto done;
+ }
+ }
+
retval = hw_device_state(udc->ci13xxx_ep[0].qh[RX].dma);
+ if (retval)
+ pm_runtime_put_sync(&udc->gadget.dev);
done:
spin_unlock_irqrestore(udc->lock, flags);
@@ -2475,19 +2522,22 @@ int usb_gadget_unregister_driver(struct usb_gadget_driver *driver)
spin_lock_irqsave(udc->lock, flags);
- hw_device_state(0);
-
- /* unbind gadget */
- if (udc->gadget.ops != NULL) {
+ if (!(udc->udc_driver->flags & CI13XXX_PULLUP_ON_VBUS) ||
+ udc->vbus_active) {
+ hw_device_state(0);
+ if (udc->udc_driver->notify_event)
+ udc->udc_driver->notify_event(udc,
+ CI13XXX_CONTROLLER_STOPPED_EVENT);
_gadget_stop_activity(&udc->gadget);
+ pm_runtime_put(&udc->gadget.dev);
+ }
- spin_unlock_irqrestore(udc->lock, flags);
- driver->unbind(&udc->gadget); /* MAY SLEEP */
- spin_lock_irqsave(udc->lock, flags);
+ /* unbind gadget */
+ spin_unlock_irqrestore(udc->lock, flags);
+ driver->unbind(&udc->gadget); /* MAY SLEEP */
+ spin_lock_irqsave(udc->lock, flags);
- udc->gadget.ops = NULL;
- udc->gadget.dev.driver = NULL;
- }
+ udc->gadget.dev.driver = NULL;
/* free resources */
for (i = 0; i < hw_ep_max; i++) {
@@ -2544,6 +2594,14 @@ static irqreturn_t udc_irq(void)
}
spin_lock(udc->lock);
+
+ if (udc->udc_driver->flags & CI13XXX_REGS_SHARED) {
+ if (hw_cread(CAP_USBMODE, USBMODE_CM) !=
+ USBMODE_CM_DEVICE) {
+ spin_unlock(udc->lock);
+ return IRQ_NONE;
+ }
+ }
intr = hw_test_and_clear_intr_active();
if (intr) {
isr_statistics.hndl.buf[isr_statistics.hndl.idx++] = intr;
@@ -2602,14 +2660,16 @@ static void udc_release(struct device *dev)
* No interrupts active, the IRQ has not been requested yet
* Kernel assumes 32-bit DMA operations by default, no need to dma_set_mask
*/
-static int udc_probe(struct device *dev, void __iomem *regs, const char *name)
+static int udc_probe(struct ci13xxx_udc_driver *driver, struct device *dev,
+ void __iomem *regs)
{
struct ci13xxx *udc;
int retval = 0;
trace("%p, %p, %p", dev, regs, name);
- if (dev == NULL || regs == NULL || name == NULL)
+ if (dev == NULL || regs == NULL || driver == NULL ||
+ driver->name == NULL)
return -EINVAL;
udc = kzalloc(sizeof(struct ci13xxx), GFP_KERNEL);
@@ -2617,42 +2677,77 @@ static int udc_probe(struct device *dev, void __iomem *regs, const char *name)
return -ENOMEM;
udc->lock = &udc_lock;
+ udc->regs = regs;
+ udc->udc_driver = driver;
- retval = hw_device_reset(regs);
- if (retval)
- goto done;
-
- udc->gadget.ops = NULL;
+ udc->gadget.ops = &usb_gadget_ops;
udc->gadget.speed = USB_SPEED_UNKNOWN;
udc->gadget.is_dualspeed = 1;
udc->gadget.is_otg = 0;
- udc->gadget.name = name;
+ udc->gadget.name = driver->name;
INIT_LIST_HEAD(&udc->gadget.ep_list);
udc->gadget.ep0 = NULL;
dev_set_name(&udc->gadget.dev, "gadget");
udc->gadget.dev.dma_mask = dev->dma_mask;
+ udc->gadget.dev.coherent_dma_mask = dev->coherent_dma_mask;
udc->gadget.dev.parent = dev;
udc->gadget.dev.release = udc_release;
+ retval = hw_device_init(regs);
+ if (retval < 0)
+ goto free_udc;
+
+ udc->transceiver = otg_get_transceiver();
+
+ if (udc->udc_driver->flags & CI13XXX_REQUIRE_TRANSCEIVER) {
+ if (udc->transceiver == NULL) {
+ retval = -ENODEV;
+ goto free_udc;
+ }
+ }
+
+ if (!(udc->udc_driver->flags & CI13XXX_REGS_SHARED)) {
+ retval = hw_device_reset(udc);
+ if (retval)
+ goto put_transceiver;
+ }
+
retval = device_register(&udc->gadget.dev);
- if (retval)
- goto done;
+ if (retval) {
+ put_device(&udc->gadget.dev);
+ goto put_transceiver;
+ }
#ifdef CONFIG_USB_GADGET_DEBUG_FILES
retval = dbg_create_files(&udc->gadget.dev);
#endif
- if (retval) {
- device_unregister(&udc->gadget.dev);
- goto done;
+ if (retval)
+ goto unreg_device;
+
+ if (udc->transceiver) {
+ retval = otg_set_peripheral(udc->transceiver, &udc->gadget);
+ if (retval)
+ goto remove_dbg;
}
+ pm_runtime_no_callbacks(&udc->gadget.dev);
+ pm_runtime_enable(&udc->gadget.dev);
_udc = udc;
return retval;
- done:
err("error = %i", retval);
+remove_dbg:
+#ifdef CONFIG_USB_GADGET_DEBUG_FILES
+ dbg_remove_files(&udc->gadget.dev);
+#endif
+unreg_device:
+ device_unregister(&udc->gadget.dev);
+put_transceiver:
+ if (udc->transceiver)
+ otg_put_transceiver(udc->transceiver);
+free_udc:
kfree(udc);
_udc = NULL;
return retval;
@@ -2672,6 +2767,10 @@ static void udc_remove(void)
return;
}
+ if (udc->transceiver) {
+ otg_set_peripheral(udc->transceiver, &udc->gadget);
+ otg_put_transceiver(udc->transceiver);
+ }
#ifdef CONFIG_USB_GADGET_DEBUG_FILES
dbg_remove_files(&udc->gadget.dev);
#endif
@@ -2680,156 +2779,3 @@ static void udc_remove(void)
kfree(udc);
_udc = NULL;
}
-
-/******************************************************************************
- * PCI block
- *****************************************************************************/
-/**
- * ci13xxx_pci_irq: interrut handler
- * @irq: irq number
- * @pdev: USB Device Controller interrupt source
- *
- * This function returns IRQ_HANDLED if the IRQ has been handled
- * This is an ISR don't trace, use attribute interface instead
- */
-static irqreturn_t ci13xxx_pci_irq(int irq, void *pdev)
-{
- if (irq == 0) {
- dev_err(&((struct pci_dev *)pdev)->dev, "Invalid IRQ0 usage!");
- return IRQ_HANDLED;
- }
- return udc_irq();
-}
-
-/**
- * ci13xxx_pci_probe: PCI probe
- * @pdev: USB device controller being probed
- * @id: PCI hotplug ID connecting controller to UDC framework
- *
- * This function returns an error code
- * Allocates basic PCI resources for this USB device controller, and then
- * invokes the udc_probe() method to start the UDC associated with it
- */
-static int __devinit ci13xxx_pci_probe(struct pci_dev *pdev,
- const struct pci_device_id *id)
-{
- void __iomem *regs = NULL;
- int retval = 0;
-
- if (id == NULL)
- return -EINVAL;
-
- retval = pci_enable_device(pdev);
- if (retval)
- goto done;
-
- if (!pdev->irq) {
- dev_err(&pdev->dev, "No IRQ, check BIOS/PCI setup!");
- retval = -ENODEV;
- goto disable_device;
- }
-
- retval = pci_request_regions(pdev, UDC_DRIVER_NAME);
- if (retval)
- goto disable_device;
-
- /* BAR 0 holds all the registers */
- regs = pci_iomap(pdev, 0, 0);
- if (!regs) {
- dev_err(&pdev->dev, "Error mapping memory!");
- retval = -EFAULT;
- goto release_regions;
- }
- pci_set_drvdata(pdev, (__force void *)regs);
-
- pci_set_master(pdev);
- pci_try_set_mwi(pdev);
-
- retval = udc_probe(&pdev->dev, regs, UDC_DRIVER_NAME);
- if (retval)
- goto iounmap;
-
- /* our device does not have MSI capability */
-
- retval = request_irq(pdev->irq, ci13xxx_pci_irq, IRQF_SHARED,
- UDC_DRIVER_NAME, pdev);
- if (retval)
- goto gadget_remove;
-
- return 0;
-
- gadget_remove:
- udc_remove();
- iounmap:
- pci_iounmap(pdev, regs);
- release_regions:
- pci_release_regions(pdev);
- disable_device:
- pci_disable_device(pdev);
- done:
- return retval;
-}
-
-/**
- * ci13xxx_pci_remove: PCI remove
- * @pdev: USB Device Controller being removed
- *
- * Reverses the effect of ci13xxx_pci_probe(),
- * first invoking the udc_remove() and then releases
- * all PCI resources allocated for this USB device controller
- */
-static void __devexit ci13xxx_pci_remove(struct pci_dev *pdev)
-{
- free_irq(pdev->irq, pdev);
- udc_remove();
- pci_iounmap(pdev, (__force void __iomem *)pci_get_drvdata(pdev));
- pci_release_regions(pdev);
- pci_disable_device(pdev);
-}
-
-/**
- * PCI device table
- * PCI device structure
- *
- * Check "pci.h" for details
- */
-static DEFINE_PCI_DEVICE_TABLE(ci13xxx_pci_id_table) = {
- { PCI_DEVICE(0x153F, 0x1004) },
- { PCI_DEVICE(0x153F, 0x1006) },
- { 0, 0, 0, 0, 0, 0, 0 /* end: all zeroes */ }
-};
-MODULE_DEVICE_TABLE(pci, ci13xxx_pci_id_table);
-
-static struct pci_driver ci13xxx_pci_driver = {
- .name = UDC_DRIVER_NAME,
- .id_table = ci13xxx_pci_id_table,
- .probe = ci13xxx_pci_probe,
- .remove = __devexit_p(ci13xxx_pci_remove),
-};
-
-/**
- * ci13xxx_pci_init: module init
- *
- * Driver load
- */
-static int __init ci13xxx_pci_init(void)
-{
- return pci_register_driver(&ci13xxx_pci_driver);
-}
-module_init(ci13xxx_pci_init);
-
-/**
- * ci13xxx_pci_exit: module exit
- *
- * Driver unload
- */
-static void __exit ci13xxx_pci_exit(void)
-{
- pci_unregister_driver(&ci13xxx_pci_driver);
-}
-module_exit(ci13xxx_pci_exit);
-
-MODULE_AUTHOR("MIPS - David Lopo <dlopo@chipidea.mips.com>");
-MODULE_DESCRIPTION("MIPS CI13XXX USB Peripheral Controller");
-MODULE_LICENSE("GPL");
-MODULE_VERSION("June 2008");
diff --git a/drivers/usb/gadget/ci13xxx_udc.h b/drivers/usb/gadget/ci13xxx_udc.h
index 4026e9cede3..4fd19313e23 100644
--- a/drivers/usb/gadget/ci13xxx_udc.h
+++ b/drivers/usb/gadget/ci13xxx_udc.h
@@ -97,9 +97,24 @@ struct ci13xxx_ep {
struct dma_pool *td_pool;
};
+struct ci13xxx;
+struct ci13xxx_udc_driver {
+ const char *name;
+ unsigned long flags;
+#define CI13XXX_REGS_SHARED BIT(0)
+#define CI13XXX_REQUIRE_TRANSCEIVER BIT(1)
+#define CI13XXX_PULLUP_ON_VBUS BIT(2)
+#define CI13XXX_DISABLE_STREAMING BIT(3)
+
+#define CI13XXX_CONTROLLER_RESET_EVENT 0
+#define CI13XXX_CONTROLLER_STOPPED_EVENT 1
+ void (*notify_event) (struct ci13xxx *udc, unsigned event);
+};
+
/* CI13XXX UDC descriptor & global resources */
struct ci13xxx {
spinlock_t *lock; /* ctrl register bank access */
+ void __iomem *regs; /* registers address space */
struct dma_pool *qh_pool; /* DMA pool for queue heads */
struct dma_pool *td_pool; /* DMA pool for transfer descs */
@@ -108,6 +123,9 @@ struct ci13xxx {
struct ci13xxx_ep ci13xxx_ep[ENDPT_MAX]; /* extended endpts */
struct usb_gadget_driver *driver; /* 3rd party gadget driver */
+ struct ci13xxx_udc_driver *udc_driver; /* device controller driver */
+ int vbus_active; /* is VBUS active */
+ struct otg_transceiver *transceiver; /* Transceiver struct */
};
/******************************************************************************
@@ -157,6 +175,7 @@ struct ci13xxx {
#define USBMODE_CM_DEVICE (0x02UL << 0)
#define USBMODE_CM_HOST (0x03UL << 0)
#define USBMODE_SLOM BIT(3)
+#define USBMODE_SDIS BIT(4)
/* ENDPTCTRL */
#define ENDPTCTRL_RXS BIT(0)
diff --git a/drivers/usb/gadget/composite.c b/drivers/usb/gadget/composite.c
index 7b5cc16e4a0..21dc0da36ab 100644
--- a/drivers/usb/gadget/composite.c
+++ b/drivers/usb/gadget/composite.c
@@ -1126,7 +1126,7 @@ static int composite_bind(struct usb_gadget *gadget)
cdev->desc = *composite->dev;
cdev->desc.bMaxPacketSize0 = gadget->ep0->maxpacket;
- /* stirng overrides */
+ /* string overrides */
if (iManufacturer || !cdev->desc.iManufacturer) {
if (!iManufacturer && !composite->iManufacturer &&
!*composite_manufacturer)
@@ -1188,6 +1188,8 @@ composite_suspend(struct usb_gadget *gadget)
composite->suspend(cdev);
cdev->suspended = 1;
+
+ usb_gadget_vbus_draw(gadget, 2);
}
static void
@@ -1195,6 +1197,7 @@ composite_resume(struct usb_gadget *gadget)
{
struct usb_composite_dev *cdev = get_gadget_data(gadget);
struct usb_function *f;
+ u8 maxpower;
/* REVISIT: should we have config level
* suspend/resume callbacks?
@@ -1207,6 +1210,11 @@ composite_resume(struct usb_gadget *gadget)
if (f->resume)
f->resume(f);
}
+
+ maxpower = cdev->config->bMaxPower;
+
+ usb_gadget_vbus_draw(gadget, maxpower ?
+ (2 * maxpower) : CONFIG_USB_GADGET_VBUS_DRAW);
}
cdev->suspended = 0;
diff --git a/drivers/usb/gadget/dummy_hcd.c b/drivers/usb/gadget/dummy_hcd.c
index 1d2a2abbfa8..13b9f47feec 100644
--- a/drivers/usb/gadget/dummy_hcd.c
+++ b/drivers/usb/gadget/dummy_hcd.c
@@ -1197,6 +1197,139 @@ static struct dummy_ep *find_endpoint (struct dummy *dum, u8 address)
#define Ep_Request (USB_TYPE_STANDARD | USB_RECIP_ENDPOINT)
#define Ep_InRequest (Ep_Request | USB_DIR_IN)
+
+/**
+ * handle_control_request() - handles all control transfers
+ * @dum: pointer to dummy (the_controller)
+ * @urb: the urb request to handle
+ * @setup: pointer to the setup data for a USB device control
+ * request
+ * @status: pointer to request handling status
+ *
+ * Return 0 - if the request was handled
+ * 1 - if the request wasn't handles
+ * error code on error
+ */
+static int handle_control_request(struct dummy *dum, struct urb *urb,
+ struct usb_ctrlrequest *setup,
+ int *status)
+{
+ struct dummy_ep *ep2;
+ int ret_val = 1;
+ unsigned w_index;
+ unsigned w_value;
+
+ w_index = le16_to_cpu(setup->wIndex);
+ w_value = le16_to_cpu(setup->wValue);
+ switch (setup->bRequest) {
+ case USB_REQ_SET_ADDRESS:
+ if (setup->bRequestType != Dev_Request)
+ break;
+ dum->address = w_value;
+ *status = 0;
+ dev_dbg(udc_dev(dum), "set_address = %d\n",
+ w_value);
+ ret_val = 0;
+ break;
+ case USB_REQ_SET_FEATURE:
+ if (setup->bRequestType == Dev_Request) {
+ ret_val = 0;
+ switch (w_value) {
+ case USB_DEVICE_REMOTE_WAKEUP:
+ break;
+ case USB_DEVICE_B_HNP_ENABLE:
+ dum->gadget.b_hnp_enable = 1;
+ break;
+ case USB_DEVICE_A_HNP_SUPPORT:
+ dum->gadget.a_hnp_support = 1;
+ break;
+ case USB_DEVICE_A_ALT_HNP_SUPPORT:
+ dum->gadget.a_alt_hnp_support = 1;
+ break;
+ default:
+ ret_val = -EOPNOTSUPP;
+ }
+ if (ret_val == 0) {
+ dum->devstatus |= (1 << w_value);
+ *status = 0;
+ }
+ } else if (setup->bRequestType == Ep_Request) {
+ /* endpoint halt */
+ ep2 = find_endpoint(dum, w_index);
+ if (!ep2 || ep2->ep.name == ep0name) {
+ ret_val = -EOPNOTSUPP;
+ break;
+ }
+ ep2->halted = 1;
+ ret_val = 0;
+ *status = 0;
+ }
+ break;
+ case USB_REQ_CLEAR_FEATURE:
+ if (setup->bRequestType == Dev_Request) {
+ ret_val = 0;
+ switch (w_value) {
+ case USB_DEVICE_REMOTE_WAKEUP:
+ w_value = USB_DEVICE_REMOTE_WAKEUP;
+ break;
+ default:
+ ret_val = -EOPNOTSUPP;
+ break;
+ }
+ if (ret_val == 0) {
+ dum->devstatus &= ~(1 << w_value);
+ *status = 0;
+ }
+ } else if (setup->bRequestType == Ep_Request) {
+ /* endpoint halt */
+ ep2 = find_endpoint(dum, w_index);
+ if (!ep2) {
+ ret_val = -EOPNOTSUPP;
+ break;
+ }
+ if (!ep2->wedged)
+ ep2->halted = 0;
+ ret_val = 0;
+ *status = 0;
+ }
+ break;
+ case USB_REQ_GET_STATUS:
+ if (setup->bRequestType == Dev_InRequest
+ || setup->bRequestType == Intf_InRequest
+ || setup->bRequestType == Ep_InRequest) {
+ char *buf;
+ /*
+ * device: remote wakeup, selfpowered
+ * interface: nothing
+ * endpoint: halt
+ */
+ buf = (char *)urb->transfer_buffer;
+ if (urb->transfer_buffer_length > 0) {
+ if (setup->bRequestType == Ep_InRequest) {
+ ep2 = find_endpoint(dum, w_index);
+ if (!ep2) {
+ ret_val = -EOPNOTSUPP;
+ break;
+ }
+ buf[0] = ep2->halted;
+ } else if (setup->bRequestType ==
+ Dev_InRequest) {
+ buf[0] = (u8)dum->devstatus;
+ } else
+ buf[0] = 0;
+ }
+ if (urb->transfer_buffer_length > 1)
+ buf[1] = 0;
+ urb->actual_length = min_t(u32, 2,
+ urb->transfer_buffer_length);
+ ret_val = 0;
+ *status = 0;
+ }
+ break;
+ }
+ return ret_val;
+}
+
/* drive both sides of the transfers; looks like irq handlers to
* both drivers except the callbacks aren't in_irq().
*/
@@ -1299,14 +1432,8 @@ restart:
if (ep == &dum->ep [0] && ep->setup_stage) {
struct usb_ctrlrequest setup;
int value = 1;
- struct dummy_ep *ep2;
- unsigned w_index;
- unsigned w_value;
setup = *(struct usb_ctrlrequest*) urb->setup_packet;
- w_index = le16_to_cpu(setup.wIndex);
- w_value = le16_to_cpu(setup.wValue);
-
/* paranoia, in case of stale queued data */
list_for_each_entry (req, &ep->queue, queue) {
list_del_init (&req->queue);
@@ -1328,117 +1455,9 @@ restart:
ep->last_io = jiffies;
ep->setup_stage = 0;
ep->halted = 0;
- switch (setup.bRequest) {
- case USB_REQ_SET_ADDRESS:
- if (setup.bRequestType != Dev_Request)
- break;
- dum->address = w_value;
- status = 0;
- dev_dbg (udc_dev(dum), "set_address = %d\n",
- w_value);
- value = 0;
- break;
- case USB_REQ_SET_FEATURE:
- if (setup.bRequestType == Dev_Request) {
- value = 0;
- switch (w_value) {
- case USB_DEVICE_REMOTE_WAKEUP:
- break;
- case USB_DEVICE_B_HNP_ENABLE:
- dum->gadget.b_hnp_enable = 1;
- break;
- case USB_DEVICE_A_HNP_SUPPORT:
- dum->gadget.a_hnp_support = 1;
- break;
- case USB_DEVICE_A_ALT_HNP_SUPPORT:
- dum->gadget.a_alt_hnp_support
- = 1;
- break;
- default:
- value = -EOPNOTSUPP;
- }
- if (value == 0) {
- dum->devstatus |=
- (1 << w_value);
- status = 0;
- }
- } else if (setup.bRequestType == Ep_Request) {
- // endpoint halt
- ep2 = find_endpoint (dum, w_index);
- if (!ep2 || ep2->ep.name == ep0name) {
- value = -EOPNOTSUPP;
- break;
- }
- ep2->halted = 1;
- value = 0;
- status = 0;
- }
- break;
- case USB_REQ_CLEAR_FEATURE:
- if (setup.bRequestType == Dev_Request) {
- switch (w_value) {
- case USB_DEVICE_REMOTE_WAKEUP:
- dum->devstatus &= ~(1 <<
- USB_DEVICE_REMOTE_WAKEUP);
- value = 0;
- status = 0;
- break;
- default:
- value = -EOPNOTSUPP;
- break;
- }
- } else if (setup.bRequestType == Ep_Request) {
- // endpoint halt
- ep2 = find_endpoint (dum, w_index);
- if (!ep2) {
- value = -EOPNOTSUPP;
- break;
- }
- if (!ep2->wedged)
- ep2->halted = 0;
- value = 0;
- status = 0;
- }
- break;
- case USB_REQ_GET_STATUS:
- if (setup.bRequestType == Dev_InRequest
- || setup.bRequestType
- == Intf_InRequest
- || setup.bRequestType
- == Ep_InRequest
- ) {
- char *buf;
-
- // device: remote wakeup, selfpowered
- // interface: nothing
- // endpoint: halt
- buf = (char *)urb->transfer_buffer;
- if (urb->transfer_buffer_length > 0) {
- if (setup.bRequestType ==
- Ep_InRequest) {
- ep2 = find_endpoint (dum, w_index);
- if (!ep2) {
- value = -EOPNOTSUPP;
- break;
- }
- buf [0] = ep2->halted;
- } else if (setup.bRequestType ==
- Dev_InRequest) {
- buf [0] = (u8)
- dum->devstatus;
- } else
- buf [0] = 0;
- }
- if (urb->transfer_buffer_length > 1)
- buf [1] = 0;
- urb->actual_length = min_t(u32, 2,
- urb->transfer_buffer_length);
- value = 0;
- status = 0;
- }
- break;
- }
+ value = handle_control_request(dum, urb, &setup,
+ &status);
/* gadget driver handles all other requests. block
* until setup() returns; no reentrancy issues etc.
diff --git a/drivers/usb/gadget/f_fs.c b/drivers/usb/gadget/f_fs.c
index 484c5ba5450..1499f9e4afa 100644
--- a/drivers/usb/gadget/f_fs.c
+++ b/drivers/usb/gadget/f_fs.c
@@ -1,10 +1,10 @@
/*
- * f_fs.c -- user mode filesystem api for usb composite funtcion controllers
+ * f_fs.c -- user mode file system API for USB composite function controllers
*
* Copyright (C) 2010 Samsung Electronics
* Author: Michal Nazarewicz <m.nazarewicz@samsung.com>
*
- * Based on inode.c (GadgetFS):
+ * Based on inode.c (GadgetFS) which was:
* Copyright (C) 2003-2004 David Brownell
* Copyright (C) 2003 Agilent Technologies
*
@@ -38,62 +38,56 @@
#define FUNCTIONFS_MAGIC 0xa647361 /* Chosen by a honest dice roll ;) */
-/* Debuging *****************************************************************/
-
-#define ffs_printk(level, fmt, args...) printk(level "f_fs: " fmt "\n", ## args)
-
-#define FERR(...) ffs_printk(KERN_ERR, __VA_ARGS__)
-#define FINFO(...) ffs_printk(KERN_INFO, __VA_ARGS__)
-
-#ifdef DEBUG
-# define FDBG(...) ffs_printk(KERN_DEBUG, __VA_ARGS__)
-#else
-# define FDBG(...) do { } while (0)
-#endif /* DEBUG */
-
-#ifdef VERBOSE_DEBUG
-# define FVDBG FDBG
-#else
-# define FVDBG(...) do { } while (0)
-#endif /* VERBOSE_DEBUG */
-
-#define ENTER() FVDBG("%s()", __func__)
+/* Debugging ****************************************************************/
#ifdef VERBOSE_DEBUG
+# define pr_vdebug pr_debug
# define ffs_dump_mem(prefix, ptr, len) \
- print_hex_dump_bytes("f_fs" prefix ": ", DUMP_PREFIX_NONE, ptr, len)
+ print_hex_dump_bytes(pr_fmt(prefix ": "), DUMP_PREFIX_NONE, ptr, len)
#else
+# define pr_vdebug(...) do { } while (0)
# define ffs_dump_mem(prefix, ptr, len) do { } while (0)
-#endif
+#endif /* VERBOSE_DEBUG */
+
+#define ENTER() pr_vdebug("%s()\n", __func__)
/* The data structure and setup file ****************************************/
enum ffs_state {
- /* Waiting for descriptors and strings. */
- /* In this state no open(2), read(2) or write(2) on epfiles
+ /*
+ * Waiting for descriptors and strings.
+ *
+ * In this state no open(2), read(2) or write(2) on epfiles
* may succeed (which should not be the problem as there
- * should be no such files opened in the firts place). */
+ * should be no such files opened in the first place).
+ */
FFS_READ_DESCRIPTORS,
FFS_READ_STRINGS,
- /* We've got descriptors and strings. We are or have called
+ /*
+ * We've got descriptors and strings. We are or have called
* functionfs_ready_callback(). functionfs_bind() may have
- * been called but we don't know. */
- /* This is the only state in which operations on epfiles may
- * succeed. */
+ * been called but we don't know.
+ *
+ * This is the only state in which operations on epfiles may
+ * succeed.
+ */
FFS_ACTIVE,
- /* All endpoints have been closed. This state is also set if
+ /*
+ * All endpoints have been closed. This state is also set if
* we encounter an unrecoverable error. The only
* unrecoverable error is situation when after reading strings
- * from user space we fail to initialise EP files or
- * functionfs_ready_callback() returns with error (<0). */
- /* In this state no open(2), read(2) or write(2) (both on ep0
+ * from user space we fail to initialise epfiles or
+ * functionfs_ready_callback() returns with error (<0).
+ *
+ * In this state no open(2), read(2) or write(2) (both on ep0
* as well as epfile) may succeed (at this point epfiles are
* unlinked and all closed so this is not a problem; ep0 is
* also closed but ep0 file exists and so open(2) on ep0 must
- * fail). */
+ * fail).
+ */
FFS_CLOSING
};
@@ -101,14 +95,18 @@ enum ffs_state {
enum ffs_setup_state {
/* There is no setup request pending. */
FFS_NO_SETUP,
- /* User has read events and there was a setup request event
+ /*
+ * User has read events and there was a setup request event
* there. The next read/write on ep0 will handle the
- * request. */
+ * request.
+ */
FFS_SETUP_PENDING,
- /* There was event pending but before user space handled it
+ /*
+ * There was event pending but before user space handled it
* some other event was introduced which canceled existing
* setup. If this state is set read/write on ep0 return
- * -EIDRM. This state is only set when adding event. */
+ * -EIDRM. This state is only set when adding event.
+ */
FFS_SETUP_CANCELED
};
@@ -120,23 +118,29 @@ struct ffs_function;
struct ffs_data {
struct usb_gadget *gadget;
- /* Protect access read/write operations, only one read/write
+ /*
+ * Protect access read/write operations, only one read/write
* at a time. As a consequence protects ep0req and company.
* While setup request is being processed (queued) this is
- * held. */
+ * held.
+ */
struct mutex mutex;
- /* Protect access to enpoint related structures (basically
+ /*
+ * Protect access to endpoint related structures (basically
* usb_ep_queue(), usb_ep_dequeue(), etc. calls) except for
- * endpint zero. */
+ * endpoint zero.
+ */
spinlock_t eps_lock;
- /* XXX REVISIT do we need our own request? Since we are not
- * handling setup requests immidiatelly user space may be so
+ /*
+ * XXX REVISIT do we need our own request? Since we are not
+ * handling setup requests immediately user space may be so
* slow that another setup will be sent to the gadget but this
* time not to us but another function and then there could be
* a race. Is that the case? Or maybe we can use cdev->req
- * after all, maybe we just need some spinlock for that? */
+ * after all, maybe we just need some spinlock for that?
+ */
struct usb_request *ep0req; /* P: mutex */
struct completion ep0req_completion; /* P: mutex */
int ep0req_status; /* P: mutex */
@@ -150,7 +154,7 @@ struct ffs_data {
enum ffs_state state;
/*
- * Possible transations:
+ * Possible transitions:
* + FFS_NO_SETUP -> FFS_SETUP_PENDING -- P: ev.waitq.lock
* happens only in ep0 read which is P: mutex
* + FFS_SETUP_PENDING -> FFS_NO_SETUP -- P: ev.waitq.lock
@@ -183,18 +187,21 @@ struct ffs_data {
/* Active function */
struct ffs_function *func;
- /* Device name, write once when file system is mounted.
- * Intendet for user to read if she wants. */
+ /*
+ * Device name, write once when file system is mounted.
+ * Intended for user to read if she wants.
+ */
const char *dev_name;
- /* Private data for our user (ie. gadget). Managed by
- * user. */
+ /* Private data for our user (ie. gadget). Managed by user. */
void *private_data;
/* filled by __ffs_data_got_descs() */
- /* real descriptors are 16 bytes after raw_descs (so you need
+ /*
+ * Real descriptors are 16 bytes after raw_descs (so you need
* to skip 16 bytes (ie. ffs->raw_descs + 16) to get to the
* first full speed descriptor). raw_descs_length and
- * raw_fs_descs_length do not have those 16 bytes added. */
+ * raw_fs_descs_length do not have those 16 bytes added.
+ */
const void *raw_descs;
unsigned raw_descs_length;
unsigned raw_fs_descs_length;
@@ -211,18 +218,23 @@ struct ffs_data {
const void *raw_strings;
struct usb_gadget_strings **stringtabs;
- /* File system's super block, write once when file system is mounted. */
+ /*
+ * File system's super block, write once when file system is
+ * mounted.
+ */
struct super_block *sb;
- /* File permissions, written once when fs is mounted*/
+ /* File permissions, written once when fs is mounted */
struct ffs_file_perms {
umode_t mode;
uid_t uid;
gid_t gid;
} file_perms;
- /* The endpoint files, filled by ffs_epfiles_create(),
- * destroyed by ffs_epfiles_destroy(). */
+ /*
+ * The endpoint files, filled by ffs_epfiles_create(),
+ * destroyed by ffs_epfiles_destroy().
+ */
struct ffs_epfile *epfiles;
};
@@ -236,7 +248,7 @@ static struct ffs_data *__must_check ffs_data_new(void) __attribute__((malloc));
static void ffs_data_opened(struct ffs_data *ffs);
static void ffs_data_closed(struct ffs_data *ffs);
-/* Called with ffs->mutex held; take over ownerrship of data. */
+/* Called with ffs->mutex held; take over ownership of data. */
static int __must_check
__ffs_data_got_descs(struct ffs_data *ffs, char *data, size_t len);
static int __must_check
@@ -267,11 +279,9 @@ static struct ffs_function *ffs_func_from_usb(struct usb_function *f)
static void ffs_func_free(struct ffs_function *func);
-
static void ffs_func_eps_disable(struct ffs_function *func);
static int __must_check ffs_func_eps_enable(struct ffs_function *func);
-
static int ffs_func_bind(struct usb_configuration *,
struct usb_function *);
static void ffs_func_unbind(struct usb_configuration *,
@@ -288,7 +298,6 @@ static int ffs_func_revmap_ep(struct ffs_function *func, u8 num);
static int ffs_func_revmap_intf(struct ffs_function *func, u8 intf);
-
/* The endpoints structures *************************************************/
struct ffs_ep {
@@ -321,7 +330,6 @@ struct ffs_epfile {
unsigned char _pad;
};
-
static int __must_check ffs_epfiles_create(struct ffs_data *ffs);
static void ffs_epfiles_destroy(struct ffs_epfile *epfiles, unsigned count);
@@ -348,7 +356,6 @@ static void ffs_ep0_complete(struct usb_ep *ep, struct usb_request *req)
complete_all(&ffs->ep0req_completion);
}
-
static int __ffs_ep0_queue_wait(struct ffs_data *ffs, char *data, size_t len)
{
struct usb_request *req = ffs->ep0req;
@@ -380,17 +387,16 @@ static int __ffs_ep0_queue_wait(struct ffs_data *ffs, char *data, size_t len)
static int __ffs_ep0_stall(struct ffs_data *ffs)
{
if (ffs->ev.can_stall) {
- FVDBG("ep0 stall\n");
+ pr_vdebug("ep0 stall\n");
usb_ep_set_halt(ffs->gadget->ep0);
ffs->setup_state = FFS_NO_SETUP;
return -EL2HLT;
} else {
- FDBG("bogus ep0 stall!\n");
+ pr_debug("bogus ep0 stall!\n");
return -ESRCH;
}
}
-
static ssize_t ffs_ep0_write(struct file *file, const char __user *buf,
size_t len, loff_t *ptr)
{
@@ -409,7 +415,6 @@ static ssize_t ffs_ep0_write(struct file *file, const char __user *buf,
if (unlikely(ret < 0))
return ret;
-
/* Check state */
switch (ffs->state) {
case FFS_READ_DESCRIPTORS:
@@ -421,14 +426,14 @@ static ssize_t ffs_ep0_write(struct file *file, const char __user *buf,
}
data = ffs_prepare_buffer(buf, len);
- if (unlikely(IS_ERR(data))) {
+ if (IS_ERR(data)) {
ret = PTR_ERR(data);
break;
}
/* Handle data */
if (ffs->state == FFS_READ_DESCRIPTORS) {
- FINFO("read descriptors");
+ pr_info("read descriptors\n");
ret = __ffs_data_got_descs(ffs, data, len);
if (unlikely(ret < 0))
break;
@@ -436,7 +441,7 @@ static ssize_t ffs_ep0_write(struct file *file, const char __user *buf,
ffs->state = FFS_READ_STRINGS;
ret = len;
} else {
- FINFO("read strings");
+ pr_info("read strings\n");
ret = __ffs_data_got_strings(ffs, data, len);
if (unlikely(ret < 0))
break;
@@ -461,11 +466,12 @@ static ssize_t ffs_ep0_write(struct file *file, const char __user *buf,
}
break;
-
case FFS_ACTIVE:
data = NULL;
- /* We're called from user space, we can use _irq
- * rather then _irqsave */
+ /*
+ * We're called from user space, we can use _irq
+ * rather then _irqsave
+ */
spin_lock_irq(&ffs->ev.waitq.lock);
switch (FFS_SETUP_STATE(ffs)) {
case FFS_SETUP_CANCELED:
@@ -493,23 +499,25 @@ static ssize_t ffs_ep0_write(struct file *file, const char __user *buf,
spin_unlock_irq(&ffs->ev.waitq.lock);
data = ffs_prepare_buffer(buf, len);
- if (unlikely(IS_ERR(data))) {
+ if (IS_ERR(data)) {
ret = PTR_ERR(data);
break;
}
spin_lock_irq(&ffs->ev.waitq.lock);
- /* We are guaranteed to be still in FFS_ACTIVE state
+ /*
+ * We are guaranteed to be still in FFS_ACTIVE state
* but the state of setup could have changed from
* FFS_SETUP_PENDING to FFS_SETUP_CANCELED so we need
* to check for that. If that happened we copied data
- * from user space in vain but it's unlikely. */
- /* For sure we are not in FFS_NO_SETUP since this is
+ * from user space in vain but it's unlikely.
+ *
+ * For sure we are not in FFS_NO_SETUP since this is
* the only place FFS_SETUP_PENDING -> FFS_NO_SETUP
* transition can be performed and it's protected by
- * mutex. */
-
+ * mutex.
+ */
if (FFS_SETUP_STATE(ffs) == FFS_SETUP_CANCELED) {
ret = -EIDRM;
done_spin:
@@ -521,25 +529,22 @@ done_spin:
kfree(data);
break;
-
default:
ret = -EBADFD;
break;
}
-
mutex_unlock(&ffs->mutex);
return ret;
}
-
-
static ssize_t __ffs_ep0_read_events(struct ffs_data *ffs, char __user *buf,
size_t n)
{
- /* We are holding ffs->ev.waitq.lock and ffs->mutex and we need
- * to release them. */
-
+ /*
+ * We are holding ffs->ev.waitq.lock and ffs->mutex and we need
+ * to release them.
+ */
struct usb_functionfs_event events[n];
unsigned i = 0;
@@ -568,7 +573,6 @@ static ssize_t __ffs_ep0_read_events(struct ffs_data *ffs, char __user *buf,
? -EFAULT : sizeof events;
}
-
static ssize_t ffs_ep0_read(struct file *file, char __user *buf,
size_t len, loff_t *ptr)
{
@@ -588,16 +592,16 @@ static ssize_t ffs_ep0_read(struct file *file, char __user *buf,
if (unlikely(ret < 0))
return ret;
-
/* Check state */
if (ffs->state != FFS_ACTIVE) {
ret = -EBADFD;
goto done_mutex;
}
-
- /* We're called from user space, we can use _irq rather then
- * _irqsave */
+ /*
+ * We're called from user space, we can use _irq rather then
+ * _irqsave
+ */
spin_lock_irq(&ffs->ev.waitq.lock);
switch (FFS_SETUP_STATE(ffs)) {
@@ -617,7 +621,8 @@ static ssize_t ffs_ep0_read(struct file *file, char __user *buf,
break;
}
- if (unlikely(wait_event_interruptible_exclusive_locked_irq(ffs->ev.waitq, ffs->ev.count))) {
+ if (wait_event_interruptible_exclusive_locked_irq(ffs->ev.waitq,
+ ffs->ev.count)) {
ret = -EINTR;
break;
}
@@ -625,7 +630,6 @@ static ssize_t ffs_ep0_read(struct file *file, char __user *buf,
return __ffs_ep0_read_events(ffs, buf,
min(n, (size_t)ffs->ev.count));
-
case FFS_SETUP_PENDING:
if (ffs->ev.setup.bRequestType & USB_DIR_IN) {
spin_unlock_irq(&ffs->ev.waitq.lock);
@@ -671,8 +675,6 @@ done_mutex:
return ret;
}
-
-
static int ffs_ep0_open(struct inode *inode, struct file *file)
{
struct ffs_data *ffs = inode->i_private;
@@ -688,7 +690,6 @@ static int ffs_ep0_open(struct inode *inode, struct file *file)
return 0;
}
-
static int ffs_ep0_release(struct inode *inode, struct file *file)
{
struct ffs_data *ffs = file->private_data;
@@ -700,7 +701,6 @@ static int ffs_ep0_release(struct inode *inode, struct file *file)
return 0;
}
-
static long ffs_ep0_ioctl(struct file *file, unsigned code, unsigned long value)
{
struct ffs_data *ffs = file->private_data;
@@ -721,7 +721,6 @@ static long ffs_ep0_ioctl(struct file *file, unsigned code, unsigned long value)
return ret;
}
-
static const struct file_operations ffs_ep0_operations = {
.owner = THIS_MODULE,
.llseek = no_llseek,
@@ -736,7 +735,6 @@ static const struct file_operations ffs_ep0_operations = {
/* "Normal" endpoints operations ********************************************/
-
static void ffs_epfile_io_complete(struct usb_ep *_ep, struct usb_request *req)
{
ENTER();
@@ -747,7 +745,6 @@ static void ffs_epfile_io_complete(struct usb_ep *_ep, struct usb_request *req)
}
}
-
static ssize_t ffs_epfile_io(struct file *file,
char __user *buf, size_t len, int read)
{
@@ -777,8 +774,8 @@ first_try:
goto error;
}
- if (unlikely(wait_event_interruptible
- (epfile->wait, (ep = epfile->ep)))) {
+ if (wait_event_interruptible(epfile->wait,
+ (ep = epfile->ep))) {
ret = -EINTR;
goto error;
}
@@ -810,12 +807,16 @@ first_try:
if (unlikely(ret))
goto error;
- /* We're called from user space, we can use _irq rather then
- * _irqsave */
+ /*
+ * We're called from user space, we can use _irq rather then
+ * _irqsave
+ */
spin_lock_irq(&epfile->ffs->eps_lock);
- /* While we were acquiring mutex endpoint got disabled
- * or changed? */
+ /*
+ * While we were acquiring mutex endpoint got disabled
+ * or changed?
+ */
} while (unlikely(epfile->ep != ep));
/* Halt */
@@ -857,7 +858,6 @@ error:
return ret;
}
-
static ssize_t
ffs_epfile_write(struct file *file, const char __user *buf, size_t len,
loff_t *ptr)
@@ -903,7 +903,6 @@ ffs_epfile_release(struct inode *inode, struct file *file)
return 0;
}
-
static long ffs_epfile_ioctl(struct file *file, unsigned code,
unsigned long value)
{
@@ -942,7 +941,6 @@ static long ffs_epfile_ioctl(struct file *file, unsigned code,
return ret;
}
-
static const struct file_operations ffs_epfile_operations = {
.owner = THIS_MODULE,
.llseek = no_llseek,
@@ -955,15 +953,13 @@ static const struct file_operations ffs_epfile_operations = {
};
-
/* File system and super block operations ***********************************/
/*
- * Mounting the filesystem creates a controller file, used first for
+ * Mounting the file system creates a controller file, used first for
* function configuration then later for event monitoring.
*/
-
static struct inode *__must_check
ffs_sb_make_inode(struct super_block *sb, void *data,
const struct file_operations *fops,
@@ -996,9 +992,7 @@ ffs_sb_make_inode(struct super_block *sb, void *data,
return inode;
}
-
/* Create "regular" file */
-
static struct inode *ffs_sb_create_file(struct super_block *sb,
const char *name, void *data,
const struct file_operations *fops,
@@ -1027,9 +1021,7 @@ static struct inode *ffs_sb_create_file(struct super_block *sb,
return inode;
}
-
/* Super block */
-
static const struct super_operations ffs_sb_operations = {
.statfs = simple_statfs,
.drop_inode = generic_delete_inode,
@@ -1050,7 +1042,7 @@ static int ffs_sb_fill(struct super_block *sb, void *_data, int silent)
ENTER();
- /* Initialize data */
+ /* Initialise data */
ffs = ffs_data_new();
if (unlikely(!ffs))
goto enomem0;
@@ -1096,7 +1088,6 @@ enomem0:
return -ENOMEM;
}
-
static int ffs_fs_parse_opts(struct ffs_sb_fill_data *data, char *opts)
{
ENTER();
@@ -1116,7 +1107,7 @@ static int ffs_fs_parse_opts(struct ffs_sb_fill_data *data, char *opts)
/* Value limit */
eq = strchr(opts, '=');
if (unlikely(!eq)) {
- FERR("'=' missing in %s", opts);
+ pr_err("'=' missing in %s\n", opts);
return -EINVAL;
}
*eq = 0;
@@ -1124,7 +1115,7 @@ static int ffs_fs_parse_opts(struct ffs_sb_fill_data *data, char *opts)
/* Parse value */
value = simple_strtoul(eq + 1, &end, 0);
if (unlikely(*end != ',' && *end != 0)) {
- FERR("%s: invalid value: %s", opts, eq + 1);
+ pr_err("%s: invalid value: %s\n", opts, eq + 1);
return -EINVAL;
}
@@ -1159,7 +1150,7 @@ static int ffs_fs_parse_opts(struct ffs_sb_fill_data *data, char *opts)
default:
invalid:
- FERR("%s: invalid option", opts);
+ pr_err("%s: invalid option\n", opts);
return -EINVAL;
}
@@ -1172,7 +1163,6 @@ invalid:
return 0;
}
-
/* "mount -t functionfs dev_name /dev/function" ends up here */
static struct dentry *
@@ -1224,10 +1214,8 @@ static struct file_system_type ffs_fs_type = {
};
-
/* Driver's main init/cleanup functions *************************************/
-
static int functionfs_init(void)
{
int ret;
@@ -1236,9 +1224,9 @@ static int functionfs_init(void)
ret = register_filesystem(&ffs_fs_type);
if (likely(!ret))
- FINFO("file system registered");
+ pr_info("file system registered\n");
else
- FERR("failed registering file system (%d)", ret);
+ pr_err("failed registering file system (%d)\n", ret);
return ret;
}
@@ -1247,18 +1235,16 @@ static void functionfs_cleanup(void)
{
ENTER();
- FINFO("unloading");
+ pr_info("unloading\n");
unregister_filesystem(&ffs_fs_type);
}
-
/* ffs_data and ffs_function construction and destruction code **************/
static void ffs_data_clear(struct ffs_data *ffs);
static void ffs_data_reset(struct ffs_data *ffs);
-
static void ffs_data_get(struct ffs_data *ffs)
{
ENTER();
@@ -1279,7 +1265,7 @@ static void ffs_data_put(struct ffs_data *ffs)
ENTER();
if (unlikely(atomic_dec_and_test(&ffs->ref))) {
- FINFO("%s(): freeing", __func__);
+ pr_info("%s(): freeing\n", __func__);
ffs_data_clear(ffs);
BUG_ON(mutex_is_locked(&ffs->mutex) ||
spin_is_locked(&ffs->ev.waitq.lock) ||
@@ -1289,8 +1275,6 @@ static void ffs_data_put(struct ffs_data *ffs)
}
}
-
-
static void ffs_data_closed(struct ffs_data *ffs)
{
ENTER();
@@ -1303,7 +1287,6 @@ static void ffs_data_closed(struct ffs_data *ffs)
ffs_data_put(ffs);
}
-
static struct ffs_data *ffs_data_new(void)
{
struct ffs_data *ffs = kzalloc(sizeof *ffs, GFP_KERNEL);
@@ -1326,7 +1309,6 @@ static struct ffs_data *ffs_data_new(void)
return ffs;
}
-
static void ffs_data_clear(struct ffs_data *ffs)
{
ENTER();
@@ -1344,7 +1326,6 @@ static void ffs_data_clear(struct ffs_data *ffs)
kfree(ffs->stringtabs);
}
-
static void ffs_data_reset(struct ffs_data *ffs)
{
ENTER();
@@ -1407,7 +1388,6 @@ static int functionfs_bind(struct ffs_data *ffs, struct usb_composite_dev *cdev)
return 0;
}
-
static void functionfs_unbind(struct ffs_data *ffs)
{
ENTER();
@@ -1420,7 +1400,6 @@ static void functionfs_unbind(struct ffs_data *ffs)
}
}
-
static int ffs_epfiles_create(struct ffs_data *ffs)
{
struct ffs_epfile *epfile, *epfiles;
@@ -1451,7 +1430,6 @@ static int ffs_epfiles_create(struct ffs_data *ffs)
return 0;
}
-
static void ffs_epfiles_destroy(struct ffs_epfile *epfiles, unsigned count)
{
struct ffs_epfile *epfile = epfiles;
@@ -1471,7 +1449,6 @@ static void ffs_epfiles_destroy(struct ffs_epfile *epfiles, unsigned count)
kfree(epfiles);
}
-
static int functionfs_bind_config(struct usb_composite_dev *cdev,
struct usb_configuration *c,
struct ffs_data *ffs)
@@ -1491,7 +1468,6 @@ static int functionfs_bind_config(struct usb_composite_dev *cdev,
func->function.bind = ffs_func_bind;
func->function.unbind = ffs_func_unbind;
func->function.set_alt = ffs_func_set_alt;
- /*func->function.get_alt = ffs_func_get_alt;*/
func->function.disable = ffs_func_disable;
func->function.setup = ffs_func_setup;
func->function.suspend = ffs_func_suspend;
@@ -1516,14 +1492,15 @@ static void ffs_func_free(struct ffs_function *func)
ffs_data_put(func->ffs);
kfree(func->eps);
- /* eps and interfaces_nums are allocated in the same chunk so
+ /*
+ * eps and interfaces_nums are allocated in the same chunk so
* only one free is required. Descriptors are also allocated
- * in the same chunk. */
+ * in the same chunk.
+ */
kfree(func);
}
-
static void ffs_func_eps_disable(struct ffs_function *func)
{
struct ffs_ep *ep = func->eps;
@@ -1581,11 +1558,12 @@ static int ffs_func_eps_enable(struct ffs_function *func)
/* Parsing and building descriptors and strings *****************************/
-
-/* This validates if data pointed by data is a valid USB descriptor as
+/*
+ * This validates if data pointed by data is a valid USB descriptor as
* well as record how many interfaces, endpoints and strings are
- * required by given configuration. Returns address afther the
- * descriptor or NULL if data is invalid. */
+ * required by given configuration. Returns address after the
+ * descriptor or NULL if data is invalid.
+ */
enum ffs_entity_type {
FFS_DESCRIPTOR, FFS_INTERFACE, FFS_STRING, FFS_ENDPOINT
@@ -1607,14 +1585,14 @@ static int __must_check ffs_do_desc(char *data, unsigned len,
/* At least two bytes are required: length and type */
if (len < 2) {
- FVDBG("descriptor too short");
+ pr_vdebug("descriptor too short\n");
return -EINVAL;
}
/* If we have at least as many bytes as the descriptor takes? */
length = _ds->bLength;
if (len < length) {
- FVDBG("descriptor longer then available data");
+ pr_vdebug("descriptor longer then available data\n");
return -EINVAL;
}
@@ -1622,15 +1600,15 @@ static int __must_check ffs_do_desc(char *data, unsigned len,
#define __entity_check_STRING(val) (val)
#define __entity_check_ENDPOINT(val) ((val) & USB_ENDPOINT_NUMBER_MASK)
#define __entity(type, val) do { \
- FVDBG("entity " #type "(%02x)", (val)); \
+ pr_vdebug("entity " #type "(%02x)\n", (val)); \
if (unlikely(!__entity_check_ ##type(val))) { \
- FVDBG("invalid entity's value"); \
+ pr_vdebug("invalid entity's value\n"); \
return -EINVAL; \
} \
ret = entity(FFS_ ##type, &val, _ds, priv); \
if (unlikely(ret < 0)) { \
- FDBG("entity " #type "(%02x); ret = %d", \
- (val), ret); \
+ pr_debug("entity " #type "(%02x); ret = %d\n", \
+ (val), ret); \
return ret; \
} \
} while (0)
@@ -1642,12 +1620,13 @@ static int __must_check ffs_do_desc(char *data, unsigned len,
case USB_DT_STRING:
case USB_DT_DEVICE_QUALIFIER:
/* function can't have any of those */
- FVDBG("descriptor reserved for gadget: %d", _ds->bDescriptorType);
+ pr_vdebug("descriptor reserved for gadget: %d\n",
+ _ds->bDescriptorType);
return -EINVAL;
case USB_DT_INTERFACE: {
struct usb_interface_descriptor *ds = (void *)_ds;
- FVDBG("interface descriptor");
+ pr_vdebug("interface descriptor\n");
if (length != sizeof *ds)
goto inv_length;
@@ -1659,7 +1638,7 @@ static int __must_check ffs_do_desc(char *data, unsigned len,
case USB_DT_ENDPOINT: {
struct usb_endpoint_descriptor *ds = (void *)_ds;
- FVDBG("endpoint descriptor");
+ pr_vdebug("endpoint descriptor\n");
if (length != USB_DT_ENDPOINT_SIZE &&
length != USB_DT_ENDPOINT_AUDIO_SIZE)
goto inv_length;
@@ -1674,7 +1653,7 @@ static int __must_check ffs_do_desc(char *data, unsigned len,
case USB_DT_INTERFACE_ASSOCIATION: {
struct usb_interface_assoc_descriptor *ds = (void *)_ds;
- FVDBG("interface association descriptor");
+ pr_vdebug("interface association descriptor\n");
if (length != sizeof *ds)
goto inv_length;
if (ds->iFunction)
@@ -1688,17 +1667,17 @@ static int __must_check ffs_do_desc(char *data, unsigned len,
case USB_DT_SECURITY:
case USB_DT_CS_RADIO_CONTROL:
/* TODO */
- FVDBG("unimplemented descriptor: %d", _ds->bDescriptorType);
+ pr_vdebug("unimplemented descriptor: %d\n", _ds->bDescriptorType);
return -EINVAL;
default:
/* We should never be here */
- FVDBG("unknown descriptor: %d", _ds->bDescriptorType);
+ pr_vdebug("unknown descriptor: %d\n", _ds->bDescriptorType);
return -EINVAL;
- inv_length:
- FVDBG("invalid length: %d (descriptor %d)",
- _ds->bLength, _ds->bDescriptorType);
+inv_length:
+ pr_vdebug("invalid length: %d (descriptor %d)\n",
+ _ds->bLength, _ds->bDescriptorType);
return -EINVAL;
}
@@ -1711,7 +1690,6 @@ static int __must_check ffs_do_desc(char *data, unsigned len,
return length;
}
-
static int __must_check ffs_do_descs(unsigned count, char *data, unsigned len,
ffs_entity_callback entity, void *priv)
{
@@ -1726,10 +1704,11 @@ static int __must_check ffs_do_descs(unsigned count, char *data, unsigned len,
if (num == count)
data = NULL;
- /* Record "descriptor" entitny */
+ /* Record "descriptor" entity */
ret = entity(FFS_DESCRIPTOR, (u8 *)num, (void *)data, priv);
if (unlikely(ret < 0)) {
- FDBG("entity DESCRIPTOR(%02lx); ret = %d", num, ret);
+ pr_debug("entity DESCRIPTOR(%02lx); ret = %d\n",
+ num, ret);
return ret;
}
@@ -1738,7 +1717,7 @@ static int __must_check ffs_do_descs(unsigned count, char *data, unsigned len,
ret = ffs_do_desc(data, len, entity, priv);
if (unlikely(ret < 0)) {
- FDBG("%s returns %d", __func__, ret);
+ pr_debug("%s returns %d\n", __func__, ret);
return ret;
}
@@ -1748,7 +1727,6 @@ static int __must_check ffs_do_descs(unsigned count, char *data, unsigned len,
}
}
-
static int __ffs_data_do_entity(enum ffs_entity_type type,
u8 *valuep, struct usb_descriptor_header *desc,
void *priv)
@@ -1762,16 +1740,20 @@ static int __ffs_data_do_entity(enum ffs_entity_type type,
break;
case FFS_INTERFACE:
- /* Interfaces are indexed from zero so if we
+ /*
+ * Interfaces are indexed from zero so if we
* encountered interface "n" then there are at least
- * "n+1" interfaces. */
+ * "n+1" interfaces.
+ */
if (*valuep >= ffs->interfaces_count)
ffs->interfaces_count = *valuep + 1;
break;
case FFS_STRING:
- /* Strings are indexed from 1 (0 is magic ;) reserved
- * for languages list or some such) */
+ /*
+ * Strings are indexed from 1 (0 is magic ;) reserved
+ * for languages list or some such)
+ */
if (*valuep > ffs->strings_count)
ffs->strings_count = *valuep;
break;
@@ -1786,7 +1768,6 @@ static int __ffs_data_do_entity(enum ffs_entity_type type,
return 0;
}
-
static int __ffs_data_got_descs(struct ffs_data *ffs,
char *const _data, size_t len)
{
@@ -1849,8 +1830,6 @@ error:
return ret;
}
-
-
static int __ffs_data_got_strings(struct ffs_data *ffs,
char *const _data, size_t len)
{
@@ -1876,17 +1855,17 @@ static int __ffs_data_got_strings(struct ffs_data *ffs,
if (unlikely(str_count < needed_count))
goto error;
- /* If we don't need any strings just return and free all
- * memory */
+ /*
+ * If we don't need any strings just return and free all
+ * memory.
+ */
if (!needed_count) {
kfree(_data);
return 0;
}
- /* Allocate */
+ /* Allocate everything in one chunk so there's less maintenance. */
{
- /* Allocate everything in one chunk so there's less
- * maintanance. */
struct {
struct usb_gadget_strings *stringtabs[lang_count + 1];
struct usb_gadget_strings stringtab[lang_count];
@@ -1937,13 +1916,17 @@ static int __ffs_data_got_strings(struct ffs_data *ffs,
if (unlikely(length == len))
goto error_free;
- /* user may provide more strings then we need,
- * if that's the case we simply ingore the
- * rest */
+ /*
+ * User may provide more strings then we need,
+ * if that's the case we simply ignore the
+ * rest
+ */
if (likely(needed)) {
- /* s->id will be set while adding
+ /*
+ * s->id will be set while adding
* function to configuration so for
- * now just leave garbage here. */
+ * now just leave garbage here.
+ */
s->s = data;
--needed;
++s;
@@ -1977,8 +1960,6 @@ error:
}
-
-
/* Events handling and management *******************************************/
static void __ffs_event_add(struct ffs_data *ffs,
@@ -1987,29 +1968,32 @@ static void __ffs_event_add(struct ffs_data *ffs,
enum usb_functionfs_event_type rem_type1, rem_type2 = type;
int neg = 0;
- /* Abort any unhandled setup */
- /* We do not need to worry about some cmpxchg() changing value
+ /*
+ * Abort any unhandled setup
+ *
+ * We do not need to worry about some cmpxchg() changing value
* of ffs->setup_state without holding the lock because when
* state is FFS_SETUP_PENDING cmpxchg() in several places in
- * the source does nothing. */
+ * the source does nothing.
+ */
if (ffs->setup_state == FFS_SETUP_PENDING)
ffs->setup_state = FFS_SETUP_CANCELED;
switch (type) {
case FUNCTIONFS_RESUME:
rem_type2 = FUNCTIONFS_SUSPEND;
- /* FALL THGOUTH */
+ /* FALL THROUGH */
case FUNCTIONFS_SUSPEND:
case FUNCTIONFS_SETUP:
rem_type1 = type;
- /* discard all similar events */
+ /* Discard all similar events */
break;
case FUNCTIONFS_BIND:
case FUNCTIONFS_UNBIND:
case FUNCTIONFS_DISABLE:
case FUNCTIONFS_ENABLE:
- /* discard everything other then power management. */
+ /* Discard everything other then power management. */
rem_type1 = FUNCTIONFS_SUSPEND;
rem_type2 = FUNCTIONFS_RESUME;
neg = 1;
@@ -2026,11 +2010,11 @@ static void __ffs_event_add(struct ffs_data *ffs,
if ((*ev == rem_type1 || *ev == rem_type2) == neg)
*out++ = *ev;
else
- FVDBG("purging event %d", *ev);
+ pr_vdebug("purging event %d\n", *ev);
ffs->ev.count = out - ffs->ev.types;
}
- FVDBG("adding event %d", type);
+ pr_vdebug("adding event %d\n", type);
ffs->ev.types[ffs->ev.count++] = type;
wake_up_locked(&ffs->ev.waitq);
}
@@ -2055,8 +2039,10 @@ static int __ffs_func_bind_do_descs(enum ffs_entity_type type, u8 *valuep,
struct ffs_function *func = priv;
struct ffs_ep *ffs_ep;
- /* If hs_descriptors is not NULL then we are reading hs
- * descriptors now */
+ /*
+ * If hs_descriptors is not NULL then we are reading hs
+ * descriptors now
+ */
const int isHS = func->function.hs_descriptors != NULL;
unsigned idx;
@@ -2075,9 +2061,9 @@ static int __ffs_func_bind_do_descs(enum ffs_entity_type type, u8 *valuep,
ffs_ep = func->eps + idx;
if (unlikely(ffs_ep->descs[isHS])) {
- FVDBG("two %sspeed descriptors for EP %d",
- isHS ? "high" : "full",
- ds->bEndpointAddress & USB_ENDPOINT_NUMBER_MASK);
+ pr_vdebug("two %sspeed descriptors for EP %d\n",
+ isHS ? "high" : "full",
+ ds->bEndpointAddress & USB_ENDPOINT_NUMBER_MASK);
return -EINVAL;
}
ffs_ep->descs[isHS] = ds;
@@ -2091,11 +2077,11 @@ static int __ffs_func_bind_do_descs(enum ffs_entity_type type, u8 *valuep,
struct usb_request *req;
struct usb_ep *ep;
- FVDBG("autoconfig");
+ pr_vdebug("autoconfig\n");
ep = usb_ep_autoconfig(func->gadget, ds);
if (unlikely(!ep))
return -ENOTSUPP;
- ep->driver_data = func->eps + idx;;
+ ep->driver_data = func->eps + idx;
req = usb_ep_alloc_request(ep, GFP_KERNEL);
if (unlikely(!req))
@@ -2111,7 +2097,6 @@ static int __ffs_func_bind_do_descs(enum ffs_entity_type type, u8 *valuep,
return 0;
}
-
static int __ffs_func_bind_do_nums(enum ffs_entity_type type, u8 *valuep,
struct usb_descriptor_header *desc,
void *priv)
@@ -2143,8 +2128,10 @@ static int __ffs_func_bind_do_nums(enum ffs_entity_type type, u8 *valuep,
break;
case FFS_ENDPOINT:
- /* USB_DT_ENDPOINT are handled in
- * __ffs_func_bind_do_descs(). */
+ /*
+ * USB_DT_ENDPOINT are handled in
+ * __ffs_func_bind_do_descs().
+ */
if (desc->bDescriptorType == USB_DT_ENDPOINT)
return 0;
@@ -2160,7 +2147,7 @@ static int __ffs_func_bind_do_nums(enum ffs_entity_type type, u8 *valuep,
break;
}
- FVDBG("%02x -> %02x", *valuep, newValue);
+ pr_vdebug("%02x -> %02x\n", *valuep, newValue);
*valuep = newValue;
return 0;
}
@@ -2211,9 +2198,11 @@ static int ffs_func_bind(struct usb_configuration *c,
func->eps = data->eps;
func->interfaces_nums = data->inums;
- /* Go throught all the endpoint descriptors and allocate
+ /*
+ * Go through all the endpoint descriptors and allocate
* endpoints first, so that later we can rewrite the endpoint
- * numbers without worying that it may be described later on. */
+ * numbers without worrying that it may be described later on.
+ */
if (likely(full)) {
func->function.descriptors = data->fs_descs;
ret = ffs_do_descs(ffs->fs_descs_count,
@@ -2234,9 +2223,11 @@ static int ffs_func_bind(struct usb_configuration *c,
__ffs_func_bind_do_descs, func);
}
- /* Now handle interface numbers allocation and interface and
- * enpoint numbers rewritting. We can do that in one go
- * now. */
+ /*
+ * Now handle interface numbers allocation and interface and
+ * endpoint numbers rewriting. We can do that in one go
+ * now.
+ */
ret = ffs_do_descs(ffs->fs_descs_count +
(high ? ffs->hs_descs_count : 0),
data->raw_descs, sizeof data->raw_descs,
@@ -2274,7 +2265,6 @@ static void ffs_func_unbind(struct usb_configuration *c,
ffs_func_free(func);
}
-
static int ffs_func_set_alt(struct usb_function *f,
unsigned interface, unsigned alt)
{
@@ -2322,20 +2312,21 @@ static int ffs_func_setup(struct usb_function *f,
ENTER();
- FVDBG("creq->bRequestType = %02x", creq->bRequestType);
- FVDBG("creq->bRequest = %02x", creq->bRequest);
- FVDBG("creq->wValue = %04x", le16_to_cpu(creq->wValue));
- FVDBG("creq->wIndex = %04x", le16_to_cpu(creq->wIndex));
- FVDBG("creq->wLength = %04x", le16_to_cpu(creq->wLength));
+ pr_vdebug("creq->bRequestType = %02x\n", creq->bRequestType);
+ pr_vdebug("creq->bRequest = %02x\n", creq->bRequest);
+ pr_vdebug("creq->wValue = %04x\n", le16_to_cpu(creq->wValue));
+ pr_vdebug("creq->wIndex = %04x\n", le16_to_cpu(creq->wIndex));
+ pr_vdebug("creq->wLength = %04x\n", le16_to_cpu(creq->wLength));
- /* Most requests directed to interface go throught here
+ /*
+ * Most requests directed to interface go through here
* (notable exceptions are set/get interface) so we need to
* handle them. All other either handled by composite or
* passed to usb_configuration->setup() (if one is set). No
* matter, we will handle requests directed to endpoint here
* as well (as it's straightforward) but what to do with any
- * other request? */
-
+ * other request?
+ */
if (ffs->state != FFS_ACTIVE)
return -ENODEV;
@@ -2378,8 +2369,7 @@ static void ffs_func_resume(struct usb_function *f)
}
-
-/* Enpoint and interface numbers reverse mapping ****************************/
+/* Endpoint and interface numbers reverse mapping ***************************/
static int ffs_func_revmap_ep(struct ffs_function *func, u8 num)
{
@@ -2410,7 +2400,6 @@ static int ffs_mutex_lock(struct mutex *mutex, unsigned nonblock)
: mutex_lock_interruptible(mutex);
}
-
static char *ffs_prepare_buffer(const char * __user buf, size_t len)
{
char *data;
@@ -2427,7 +2416,7 @@ static char *ffs_prepare_buffer(const char * __user buf, size_t len)
return ERR_PTR(-EFAULT);
}
- FVDBG("Buffer from user space:");
+ pr_vdebug("Buffer from user space:\n");
ffs_dump_mem("", data, len);
return data;
diff --git a/drivers/usb/gadget/f_mass_storage.c b/drivers/usb/gadget/f_mass_storage.c
index 838286b1cd1..b5dbb2308f5 100644
--- a/drivers/usb/gadget/f_mass_storage.c
+++ b/drivers/usb/gadget/f_mass_storage.c
@@ -37,7 +37,6 @@
* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
-
/*
* The Mass Storage Function acts as a USB Mass Storage device,
* appearing to the host as a disk drive or as a CD-ROM drive. In
@@ -185,7 +184,6 @@
* <http://www.usb.org/developers/devclass_docs/usbmass-ufi10.pdf>.
*/
-
/*
* Driver Design
*
@@ -275,7 +273,6 @@
/* #define VERBOSE_DEBUG */
/* #define DUMP_MSGS */
-
#include <linux/blkdev.h>
#include <linux/completion.h>
#include <linux/dcache.h>
@@ -300,7 +297,6 @@
#include "gadget_chips.h"
-
/*------------------------------------------------------------------------*/
#define FSG_DRIVER_DESC "Mass Storage Function"
@@ -308,7 +304,6 @@
static const char fsg_string_interface[] = "Mass Storage";
-
#define FSG_NO_INTR_EP 1
#define FSG_NO_DEVICE_STRINGS 1
#define FSG_NO_OTG 1
@@ -324,25 +319,30 @@ struct fsg_common;
/* FSF callback functions */
struct fsg_operations {
- /* Callback function to call when thread exits. If no
+ /*
+ * Callback function to call when thread exits. If no
* callback is set or it returns value lower then zero MSF
* will force eject all LUNs it operates on (including those
* marked as non-removable or with prevent_medium_removal flag
- * set). */
+ * set).
+ */
int (*thread_exits)(struct fsg_common *common);
- /* Called prior to ejection. Negative return means error,
+ /*
+ * Called prior to ejection. Negative return means error,
* zero means to continue with ejection, positive means not to
- * eject. */
+ * eject.
+ */
int (*pre_eject)(struct fsg_common *common,
struct fsg_lun *lun, int num);
- /* Called after ejection. Negative return means error, zero
- * or positive is just a success. */
+ /*
+ * Called after ejection. Negative return means error, zero
+ * or positive is just a success.
+ */
int (*post_eject)(struct fsg_common *common,
struct fsg_lun *lun, int num);
};
-
/* Data shared by all the FSG instances. */
struct fsg_common {
struct usb_gadget *gadget;
@@ -398,14 +398,15 @@ struct fsg_common {
/* Gadget's private data. */
void *private_data;
- /* Vendor (8 chars), product (16 chars), release (4
- * hexadecimal digits) and NUL byte */
+ /*
+ * Vendor (8 chars), product (16 chars), release (4
+ * hexadecimal digits) and NUL byte
+ */
char inquiry_string[8 + 16 + 4 + 1];
struct kref ref;
};
-
struct fsg_config {
unsigned nluns;
struct fsg_lun_config {
@@ -431,7 +432,6 @@ struct fsg_config {
char can_stall;
};
-
struct fsg_dev {
struct usb_function function;
struct usb_gadget *gadget; /* Copy of cdev->gadget */
@@ -449,7 +449,6 @@ struct fsg_dev {
struct usb_ep *bulk_out;
};
-
static inline int __fsg_is_set(struct fsg_common *common,
const char *func, unsigned line)
{
@@ -462,13 +461,11 @@ static inline int __fsg_is_set(struct fsg_common *common,
#define fsg_is_set(common) likely(__fsg_is_set(common, __func__, __LINE__))
-
static inline struct fsg_dev *fsg_from_func(struct usb_function *f)
{
return container_of(f, struct fsg_dev, function);
}
-
typedef void (*fsg_routine_t)(struct fsg_dev *);
static int exception_in_progress(struct fsg_common *common)
@@ -478,7 +475,7 @@ static int exception_in_progress(struct fsg_common *common)
/* Make bulk-out requests be divisible by the maxpacket size */
static void set_bulk_out_req_length(struct fsg_common *common,
- struct fsg_buffhd *bh, unsigned int length)
+ struct fsg_buffhd *bh, unsigned int length)
{
unsigned int rem;
@@ -489,6 +486,7 @@ static void set_bulk_out_req_length(struct fsg_common *common,
bh->outreq->length = length;
}
+
/*-------------------------------------------------------------------------*/
static int fsg_set_halt(struct fsg_dev *fsg, struct usb_ep *ep)
@@ -519,14 +517,15 @@ static void wakeup_thread(struct fsg_common *common)
wake_up_process(common->thread_task);
}
-
static void raise_exception(struct fsg_common *common, enum fsg_state new_state)
{
unsigned long flags;
- /* Do nothing if a higher-priority exception is already in progress.
+ /*
+ * 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. */
+ * and notify the main thread by sending it a signal.
+ */
spin_lock_irqsave(&common->lock, flags);
if (common->state <= new_state) {
common->exception_req_tag = common->ep0_req_tag;
@@ -555,10 +554,10 @@ static int ep0_queue(struct fsg_common *common)
return rc;
}
+
/*-------------------------------------------------------------------------*/
-/* Bulk and interrupt endpoint completion handlers.
- * These always run in_irq. */
+/* Completion handlers. These always run in_irq. */
static void bulk_in_complete(struct usb_ep *ep, struct usb_request *req)
{
@@ -567,7 +566,7 @@ static void bulk_in_complete(struct usb_ep *ep, struct usb_request *req)
if (req->status || req->actual != req->length)
DBG(common, "%s --> %d, %u/%u\n", __func__,
- req->status, req->actual, req->length);
+ req->status, req->actual, req->length);
if (req->status == -ECONNRESET) /* Request was cancelled */
usb_ep_fifo_flush(ep);
@@ -588,8 +587,7 @@ static void bulk_out_complete(struct usb_ep *ep, struct usb_request *req)
dump_msg(common, "bulk-out", req->buf, req->actual);
if (req->status || req->actual != bh->bulk_out_intended_length)
DBG(common, "%s --> %d, %u/%u\n", __func__,
- req->status, req->actual,
- bh->bulk_out_intended_length);
+ req->status, req->actual, bh->bulk_out_intended_length);
if (req->status == -ECONNRESET) /* Request was cancelled */
usb_ep_fifo_flush(ep);
@@ -602,13 +600,8 @@ static void bulk_out_complete(struct usb_ep *ep, struct usb_request *req)
spin_unlock(&common->lock);
}
-
-/*-------------------------------------------------------------------------*/
-
-/* Ep0 class-specific handlers. These always run in_irq. */
-
static int fsg_setup(struct usb_function *f,
- const struct usb_ctrlrequest *ctrl)
+ const struct usb_ctrlrequest *ctrl)
{
struct fsg_dev *fsg = fsg_from_func(f);
struct usb_request *req = fsg->common->ep0req;
@@ -628,8 +621,10 @@ static int fsg_setup(struct usb_function *f,
if (w_index != fsg->interface_number || w_value != 0)
return -EDOM;
- /* Raise an exception to stop the current operation
- * and reinitialize our state. */
+ /*
+ * Raise an exception to stop the current operation
+ * and reinitialize our state.
+ */
DBG(fsg, "bulk reset request\n");
raise_exception(fsg->common, FSG_STATE_RESET);
return DELAYED_STATUS;
@@ -641,7 +636,7 @@ static int fsg_setup(struct usb_function *f,
if (w_index != fsg->interface_number || w_value != 0)
return -EDOM;
VDBG(fsg, "get max LUN\n");
- *(u8 *) req->buf = fsg->common->nluns - 1;
+ *(u8 *)req->buf = fsg->common->nluns - 1;
/* Respond with data/status */
req->length = min((u16)1, w_length);
@@ -649,8 +644,7 @@ static int fsg_setup(struct usb_function *f,
}
VDBG(fsg,
- "unknown class-specific control req "
- "%02x.%02x v%04x i%04x l%u\n",
+ "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 -EOPNOTSUPP;
@@ -661,11 +655,10 @@ static int fsg_setup(struct usb_function *f,
/* 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)
+ struct usb_request *req, int *pbusy,
+ enum fsg_buffer_state *state)
{
int rc;
@@ -683,25 +676,34 @@ static void start_transfer(struct fsg_dev *fsg, struct usb_ep *ep,
/* 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))
+ /*
+ * 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);
+ ep->name, rc);
}
}
-#define START_TRANSFER_OR(common, ep_name, req, pbusy, state) \
- if (fsg_is_set(common)) \
- start_transfer((common)->fsg, (common)->fsg->ep_name, \
- req, pbusy, state); \
- else
-
-#define START_TRANSFER(common, ep_name, req, pbusy, state) \
- START_TRANSFER_OR(common, ep_name, req, pbusy, state) (void)0
-
+static bool start_in_transfer(struct fsg_common *common, struct fsg_buffhd *bh)
+{
+ if (!fsg_is_set(common))
+ return false;
+ start_transfer(common->fsg, common->fsg->bulk_in,
+ bh->inreq, &bh->inreq_busy, &bh->state);
+ return true;
+}
+static bool start_out_transfer(struct fsg_common *common, struct fsg_buffhd *bh)
+{
+ if (!fsg_is_set(common))
+ return false;
+ start_transfer(common->fsg, common->fsg->bulk_out,
+ bh->outreq, &bh->outreq_busy, &bh->state);
+ return true;
+}
static int sleep_thread(struct fsg_common *common)
{
@@ -739,16 +741,20 @@ static int do_read(struct fsg_common *common)
unsigned int partial_page;
ssize_t nread;
- /* Get the starting Logical Block Address and check that it's
- * not too big */
+ /*
+ * Get the starting Logical Block Address and check that it's
+ * not too big.
+ */
if (common->cmnd[0] == READ_6)
lba = get_unaligned_be24(&common->cmnd[1]);
else {
lba = get_unaligned_be32(&common->cmnd[2]);
- /* We allow DPO (Disable Page Out = don't save data in the
+ /*
+ * 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. */
+ * cache), but we don't implement them.
+ */
if ((common->cmnd[1] & ~0x18) != 0) {
curlun->sense_data = SS_INVALID_FIELD_IN_CDB;
return -EINVAL;
@@ -766,22 +772,23 @@ static int do_read(struct fsg_common *common)
return -EIO; /* No default reply */
for (;;) {
-
- /* Figure out how much we need to read:
+ /*
+ * 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. */
+ * the end of file.
+ */
amount = min(amount_left, FSG_BUFLEN);
- amount = min((loff_t) amount,
- curlun->file_length - file_offset);
+ 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);
+ amount = min(amount, (unsigned int)PAGE_CACHE_SIZE -
+ partial_page);
/* Wait for the next buffer to become available */
bh = common->next_buffhd_to_fill;
@@ -791,8 +798,10 @@ static int do_read(struct fsg_common *common)
return rc;
}
- /* If we were asked to read past the end of file,
- * end with an empty buffer. */
+ /*
+ * 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;
@@ -806,21 +815,19 @@ static int do_read(struct fsg_common *common)
/* Perform the read */
file_offset_tmp = file_offset;
nread = vfs_read(curlun->filp,
- (char __user *) bh->buf,
- amount, &file_offset_tmp);
+ (char __user *)bh->buf,
+ amount, &file_offset_tmp);
VLDBG(curlun, "file read %u @ %llu -> %d\n", amount,
- (unsigned long long) file_offset,
- (int) nread);
+ (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);
+ 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);
+ (int)nread, amount);
nread -= (nread & 511); /* Round down to a block */
}
file_offset += nread;
@@ -842,10 +849,8 @@ static int do_read(struct fsg_common *common)
/* Send this buffer and go read some more */
bh->inreq->zero = 0;
- START_TRANSFER_OR(common, bulk_in, bh->inreq,
- &bh->inreq_busy, &bh->state)
- /* Don't know what to do if
- * common->fsg is NULL */
+ if (!start_in_transfer(common, bh))
+ /* Don't know what to do if common->fsg is NULL */
return -EIO;
common->next_buffhd_to_fill = bh->next;
}
@@ -877,17 +882,21 @@ static int do_write(struct fsg_common *common)
curlun->filp->f_flags &= ~O_SYNC; /* Default is not to wait */
spin_unlock(&curlun->filp->f_lock);
- /* Get the starting Logical Block Address and check that it's
- * not too big */
+ /*
+ * Get the starting Logical Block Address and check that it's
+ * not too big
+ */
if (common->cmnd[0] == WRITE_6)
lba = get_unaligned_be24(&common->cmnd[1]);
else {
lba = get_unaligned_be32(&common->cmnd[2]);
- /* We allow DPO (Disable Page Out = don't save data in the
+ /*
+ * 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. */
+ * performing synchronous output.
+ */
if (common->cmnd[1] & ~0x18) {
curlun->sense_data = SS_INVALID_FIELD_IN_CDB;
return -EINVAL;
@@ -915,7 +924,8 @@ static int do_write(struct fsg_common *common)
bh = common->next_buffhd_to_fill;
if (bh->state == BUF_STATE_EMPTY && get_some_more) {
- /* Figure out how much we want to get:
+ /*
+ * 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.
@@ -923,14 +933,15 @@ static int do_write(struct fsg_common *common)
* 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. */
+ * Finally, round down to a block boundary.
+ */
amount = min(amount_left_to_req, FSG_BUFLEN);
- amount = min((loff_t) amount, curlun->file_length -
- usb_offset);
+ 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);
+ (unsigned int)PAGE_CACHE_SIZE - partial_page);
if (amount == 0) {
get_some_more = 0;
@@ -940,11 +951,13 @@ static int do_write(struct fsg_common *common)
curlun->info_valid = 1;
continue;
}
- amount -= (amount & 511);
+ amount -= amount & 511;
if (amount == 0) {
- /* Why were we were asked to transfer a
- * partial block? */
+ /*
+ * Why were we were asked to transfer a
+ * partial block?
+ */
get_some_more = 0;
continue;
}
@@ -956,15 +969,15 @@ static int do_write(struct fsg_common *common)
if (amount_left_to_req == 0)
get_some_more = 0;
- /* amount is always divisible by 512, hence by
- * the bulk-out maxpacket size */
+ /*
+ * amount is always divisible by 512, hence by
+ * the bulk-out maxpacket size
+ */
bh->outreq->length = amount;
bh->bulk_out_intended_length = amount;
bh->outreq->short_not_ok = 1;
- START_TRANSFER_OR(common, bulk_out, bh->outreq,
- &bh->outreq_busy, &bh->state)
- /* Don't know what to do if
- * common->fsg is NULL */
+ if (!start_out_transfer(common, bh))
+ /* Dunno what to do if common->fsg is NULL */
return -EIO;
common->next_buffhd_to_fill = bh->next;
continue;
@@ -990,30 +1003,29 @@ static int do_write(struct fsg_common *common)
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);
+ "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;
nwritten = vfs_write(curlun->filp,
- (char __user *) bh->buf,
- amount, &file_offset_tmp);
+ (char __user *)bh->buf,
+ amount, &file_offset_tmp);
VLDBG(curlun, "file write %u @ %llu -> %d\n", amount,
- (unsigned long long) file_offset,
- (int) nwritten);
+ (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);
+ (int)nwritten);
nwritten = 0;
} else if (nwritten < amount) {
LDBG(curlun, "partial file write: %d/%u\n",
- (int) nwritten, amount);
+ (int)nwritten, amount);
nwritten -= (nwritten & 511);
/* Round down to a block */
}
@@ -1086,16 +1098,20 @@ static int do_verify(struct fsg_common *common)
unsigned int amount;
ssize_t nread;
- /* Get the starting Logical Block Address and check that it's
- * not too big */
+ /*
+ * Get the starting Logical Block Address and check that it's
+ * not too big.
+ */
lba = get_unaligned_be32(&common->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. */
+ /*
+ * We allow DPO (Disable Page Out = don't save data in the
+ * cache) but we don't implement it.
+ */
if (common->cmnd[1] & ~0x10) {
curlun->sense_data = SS_INVALID_FIELD_IN_CDB;
return -EINVAL;
@@ -1120,16 +1136,17 @@ static int do_verify(struct fsg_common *common)
/* Just try to read the requested blocks */
while (amount_left > 0) {
-
- /* Figure out how much we need to read:
+ /*
+ * 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. */
+ * past the end of file.
+ */
amount = min(amount_left, FSG_BUFLEN);
- amount = min((loff_t) amount,
- curlun->file_length - file_offset);
+ amount = min((loff_t)amount,
+ curlun->file_length - file_offset);
if (amount == 0) {
curlun->sense_data =
SS_LOGICAL_BLOCK_ADDRESS_OUT_OF_RANGE;
@@ -1150,13 +1167,12 @@ static int do_verify(struct fsg_common *common)
return -EINTR;
if (nread < 0) {
- LDBG(curlun, "error in file verify: %d\n",
- (int) nread);
+ 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 */
+ (int)nread, amount);
+ nread -= nread & 511; /* Round down to a sector */
}
if (nread == 0) {
curlun->sense_data = SS_UNRECOVERED_READ_ERROR;
@@ -1198,7 +1214,6 @@ static int do_inquiry(struct fsg_common *common, struct fsg_buffhd *bh)
return 36;
}
-
static int do_request_sense(struct fsg_common *common, struct fsg_buffhd *bh)
{
struct fsg_lun *curlun = common->curlun;
@@ -1252,13 +1267,12 @@ static int do_request_sense(struct fsg_common *common, struct fsg_buffhd *bh)
return 18;
}
-
static int do_read_capacity(struct fsg_common *common, struct fsg_buffhd *bh)
{
struct fsg_lun *curlun = common->curlun;
u32 lba = get_unaligned_be32(&common->cmnd[2]);
int pmi = common->cmnd[8];
- u8 *buf = (u8 *) bh->buf;
+ u8 *buf = (u8 *)bh->buf;
/* Check the PMI and LBA fields */
if (pmi > 1 || (pmi == 0 && lba != 0)) {
@@ -1272,13 +1286,12 @@ static int do_read_capacity(struct fsg_common *common, struct fsg_buffhd *bh)
return 8;
}
-
static int do_read_header(struct fsg_common *common, struct fsg_buffhd *bh)
{
struct fsg_lun *curlun = common->curlun;
int msf = common->cmnd[1] & 0x02;
u32 lba = get_unaligned_be32(&common->cmnd[2]);
- u8 *buf = (u8 *) bh->buf;
+ u8 *buf = (u8 *)bh->buf;
if (common->cmnd[1] & ~0x02) { /* Mask away MSF */
curlun->sense_data = SS_INVALID_FIELD_IN_CDB;
@@ -1295,13 +1308,12 @@ static int do_read_header(struct fsg_common *common, struct fsg_buffhd *bh)
return 8;
}
-
static int do_read_toc(struct fsg_common *common, struct fsg_buffhd *bh)
{
struct fsg_lun *curlun = common->curlun;
int msf = common->cmnd[1] & 0x02;
int start_track = common->cmnd[6];
- u8 *buf = (u8 *) bh->buf;
+ u8 *buf = (u8 *)bh->buf;
if ((common->cmnd[1] & ~0x02) != 0 || /* Mask away MSF */
start_track > 1) {
@@ -1323,7 +1335,6 @@ static int do_read_toc(struct fsg_common *common, struct fsg_buffhd *bh)
return 20;
}
-
static int do_mode_sense(struct fsg_common *common, struct fsg_buffhd *bh)
{
struct fsg_lun *curlun = common->curlun;
@@ -1348,10 +1359,12 @@ static int do_mode_sense(struct fsg_common *common, struct fsg_buffhd *bh)
changeable_values = (pc == 1);
all_pages = (page_code == 0x3f);
- /* Write the mode parameter header. Fixed values are: default
+ /*
+ * 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. */
+ * the mode data length later.
+ */
memset(buf, 0, 8);
if (mscmnd == MODE_SENSE) {
buf[2] = (curlun->ro ? 0x80 : 0x00); /* WP, DPOFUA */
@@ -1365,8 +1378,10 @@ static int do_mode_sense(struct fsg_common *common, struct fsg_buffhd *bh)
/* No block descriptors */
- /* The mode pages, in numerical order. The only page we support
- * is the Caching page. */
+ /*
+ * 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 */
@@ -1388,8 +1403,10 @@ static int do_mode_sense(struct fsg_common *common, struct fsg_buffhd *bh)
buf += 12;
}
- /* Check that a valid page was requested and the mode data length
- * isn't too long. */
+ /*
+ * 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;
@@ -1404,7 +1421,6 @@ static int do_mode_sense(struct fsg_common *common, struct fsg_buffhd *bh)
return len;
}
-
static int do_start_stop(struct fsg_common *common)
{
struct fsg_lun *curlun = common->curlun;
@@ -1424,8 +1440,10 @@ static int do_start_stop(struct fsg_common *common)
loej = common->cmnd[4] & 0x02;
start = common->cmnd[4] & 0x01;
- /* Our emulation doesn't support mounting; the medium is
- * available for use as soon as it is loaded. */
+ /*
+ * Our emulation doesn't support mounting; the medium is
+ * available for use as soon as it is loaded.
+ */
if (start) {
if (!fsg_lun_is_open(curlun)) {
curlun->sense_data = SS_MEDIUM_NOT_PRESENT;
@@ -1466,7 +1484,6 @@ static int do_start_stop(struct fsg_common *common)
: 0;
}
-
static int do_prevent_allow(struct fsg_common *common)
{
struct fsg_lun *curlun = common->curlun;
@@ -1491,7 +1508,6 @@ static int do_prevent_allow(struct fsg_common *common)
return 0;
}
-
static int do_read_format_capacities(struct fsg_common *common,
struct fsg_buffhd *bh)
{
@@ -1509,7 +1525,6 @@ static int do_read_format_capacities(struct fsg_common *common,
return 12;
}
-
static int do_mode_select(struct fsg_common *common, struct fsg_buffhd *bh)
{
struct fsg_lun *curlun = common->curlun;
@@ -1591,7 +1606,7 @@ static int pad_with_zeros(struct fsg_dev *fsg)
bh->inreq->length = nsend;
bh->inreq->zero = 0;
start_transfer(fsg, fsg->bulk_in, bh->inreq,
- &bh->inreq_busy, &bh->state);
+ &bh->inreq_busy, &bh->state);
bh = fsg->common->next_buffhd_to_fill = bh->next;
fsg->common->usb_amount_left -= nsend;
nkeep = 0;
@@ -1617,7 +1632,7 @@ static int throw_away_data(struct fsg_common *common)
/* A short packet or an error ends everything */
if (bh->outreq->actual != bh->outreq->length ||
- bh->outreq->status != 0) {
+ bh->outreq->status != 0) {
raise_exception(common,
FSG_STATE_ABORT_BULK_OUT);
return -EINTR;
@@ -1631,15 +1646,15 @@ static int throw_away_data(struct fsg_common *common)
&& common->usb_amount_left > 0) {
amount = min(common->usb_amount_left, FSG_BUFLEN);
- /* amount is always divisible by 512, hence by
- * the bulk-out maxpacket size */
+ /*
+ * amount is always divisible by 512, hence by
+ * the bulk-out maxpacket size.
+ */
bh->outreq->length = amount;
bh->bulk_out_intended_length = amount;
bh->outreq->short_not_ok = 1;
- START_TRANSFER_OR(common, bulk_out, bh->outreq,
- &bh->outreq_busy, &bh->state)
- /* Don't know what to do if
- * common->fsg is NULL */
+ if (!start_out_transfer(common, bh))
+ /* Dunno what to do if common->fsg is NULL */
return -EIO;
common->next_buffhd_to_fill = bh->next;
common->usb_amount_left -= amount;
@@ -1654,7 +1669,6 @@ static int throw_away_data(struct fsg_common *common)
return 0;
}
-
static int finish_reply(struct fsg_common *common)
{
struct fsg_buffhd *bh = common->next_buffhd_to_fill;
@@ -1664,10 +1678,12 @@ static int finish_reply(struct fsg_common *common)
case DATA_DIR_NONE:
break; /* Nothing to send */
- /* If we don't know whether the host wants to read or write,
+ /*
+ * 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. */
+ * if we can and wait for a reset.
+ */
case DATA_DIR_UNKNOWN:
if (!common->can_stall) {
/* Nothing */
@@ -1688,18 +1704,18 @@ static int finish_reply(struct fsg_common *common)
/* If there's no residue, simply send the last buffer */
} else if (common->residue == 0) {
bh->inreq->zero = 0;
- START_TRANSFER_OR(common, bulk_in, bh->inreq,
- &bh->inreq_busy, &bh->state)
+ if (!start_in_transfer(common, bh))
return -EIO;
common->next_buffhd_to_fill = bh->next;
- /* For Bulk-only, if we're allowed to stall then send the
+ /*
+ * 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. */
+ * stall, pad out the remaining data with 0's.
+ */
} else if (common->can_stall) {
bh->inreq->zero = 1;
- START_TRANSFER_OR(common, bulk_in, bh->inreq,
- &bh->inreq_busy, &bh->state)
+ if (!start_in_transfer(common, bh))
/* Don't know what to do if
* common->fsg is NULL */
rc = -EIO;
@@ -1714,8 +1730,10 @@ static int finish_reply(struct fsg_common *common)
}
break;
- /* We have processed all we want from the data the host has sent.
- * There may still be outstanding bulk-out requests. */
+ /*
+ * 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 (common->residue == 0) {
/* Nothing to receive */
@@ -1725,12 +1743,14 @@ static int finish_reply(struct fsg_common *common)
raise_exception(common, FSG_STATE_ABORT_BULK_OUT);
rc = -EINTR;
- /* We haven't processed all the incoming data. Even though
+ /*
+ * 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. */
+ * clear the halt -- leading to problems later on.
+ */
#if 0
} else if (common->can_stall) {
if (fsg_is_set(common))
@@ -1740,8 +1760,10 @@ static int finish_reply(struct fsg_common *common)
rc = -EINTR;
#endif
- /* We can't stall. Read in the excess data and throw it
- * all away. */
+ /*
+ * We can't stall. Read in the excess data and throw it
+ * all away.
+ */
} else {
rc = throw_away_data(common);
}
@@ -1750,7 +1772,6 @@ static int finish_reply(struct fsg_common *common)
return rc;
}
-
static int send_status(struct fsg_common *common)
{
struct fsg_lun *curlun = common->curlun;
@@ -1798,8 +1819,7 @@ static int send_status(struct fsg_common *common)
bh->inreq->length = USB_BULK_CS_WRAP_LEN;
bh->inreq->zero = 0;
- START_TRANSFER_OR(common, bulk_in, bh->inreq,
- &bh->inreq_busy, &bh->state)
+ if (!start_in_transfer(common, bh))
/* Don't know what to do if common->fsg is NULL */
return -EIO;
@@ -1810,11 +1830,13 @@ static int send_status(struct fsg_common *common)
/*-------------------------------------------------------------------------*/
-/* Check whether the command is properly formed and whether its data size
- * and direction agree with the values we already have. */
+/*
+ * 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_common *common, int cmnd_size,
- enum data_direction data_dir, unsigned int mask,
- int needs_medium, const char *name)
+ enum data_direction data_dir, unsigned int mask,
+ int needs_medium, const char *name)
{
int i;
int lun = common->cmnd[1] >> 5;
@@ -1825,19 +1847,23 @@ static int check_command(struct fsg_common *common, int cmnd_size,
hdlen[0] = 0;
if (common->data_dir != DATA_DIR_UNKNOWN)
sprintf(hdlen, ", H%c=%u", dirletter[(int) common->data_dir],
- common->data_size);
+ common->data_size);
VDBG(common, "SCSI command: %s; Dc=%d, D%c=%u; Hc=%d%s\n",
name, cmnd_size, dirletter[(int) data_dir],
common->data_size_from_cmnd, common->cmnd_size, hdlen);
- /* We can't reply at all until we know the correct data direction
- * and size. */
+ /*
+ * We can't reply at all until we know the correct data direction
+ * and size.
+ */
if (common->data_size_from_cmnd == 0)
data_dir = DATA_DIR_NONE;
if (common->data_size < common->data_size_from_cmnd) {
- /* Host data size < Device data size is a phase error.
+ /*
+ * Host data size < Device data size is a phase error.
* Carry out the command, but only transfer as much as
- * we are allowed. */
+ * we are allowed.
+ */
common->data_size_from_cmnd = common->data_size;
common->phase_error = 1;
}
@@ -1845,8 +1871,7 @@ static int check_command(struct fsg_common *common, int cmnd_size,
common->usb_amount_left = common->data_size;
/* Conflicting data directions is a phase error */
- if (common->data_dir != data_dir
- && common->data_size_from_cmnd > 0) {
+ if (common->data_dir != data_dir && common->data_size_from_cmnd > 0) {
common->phase_error = 1;
return -EINVAL;
}
@@ -1854,7 +1879,8 @@ static int check_command(struct fsg_common *common, int cmnd_size,
/* Verify the length of the command itself */
if (cmnd_size != common->cmnd_size) {
- /* Special case workaround: There are plenty of buggy SCSI
+ /*
+ * 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
@@ -1896,8 +1922,10 @@ static int check_command(struct fsg_common *common, int cmnd_size,
curlun = NULL;
common->bad_lun_okay = 0;
- /* INQUIRY and REQUEST SENSE commands are explicitly allowed
- * to use unsupported LUNs; all others may not. */
+ /*
+ * INQUIRY and REQUEST SENSE commands are explicitly allowed
+ * to use unsupported LUNs; all others may not.
+ */
if (common->cmnd[0] != INQUIRY &&
common->cmnd[0] != REQUEST_SENSE) {
DBG(common, "unsupported LUN %d\n", common->lun);
@@ -1905,11 +1933,13 @@ static int check_command(struct fsg_common *common, int cmnd_size,
}
}
- /* If a unit attention condition exists, only INQUIRY and
- * REQUEST SENSE commands are allowed; anything else must fail. */
+ /*
+ * 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 &&
- common->cmnd[0] != INQUIRY &&
- common->cmnd[0] != REQUEST_SENSE) {
+ common->cmnd[0] != INQUIRY &&
+ common->cmnd[0] != REQUEST_SENSE) {
curlun->sense_data = curlun->unit_attention_data;
curlun->unit_attention_data = SS_NO_SENSE;
return -EINVAL;
@@ -1935,7 +1965,6 @@ static int check_command(struct fsg_common *common, int cmnd_size,
return 0;
}
-
static int do_scsi_command(struct fsg_common *common)
{
struct fsg_buffhd *bh;
@@ -2123,8 +2152,10 @@ static int do_scsi_command(struct fsg_common *common)
"TEST UNIT READY");
break;
- /* Although optional, this command is used by MS-Windows. We
- * support a minimal version: BytChk must be 0. */
+ /*
+ * Although optional, this command is used by MS-Windows. We
+ * support a minimal version: BytChk must be 0.
+ */
case VERIFY:
common->data_size_from_cmnd = 0;
reply = check_command(common, 10, DATA_DIR_NONE,
@@ -2164,10 +2195,12 @@ static int do_scsi_command(struct fsg_common *common)
reply = do_write(common);
break;
- /* Some mandatory commands that we recognize but don't implement.
+ /*
+ * 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. */
+ * of Posix locks.
+ */
case FORMAT_UNIT:
case RELEASE:
case RESERVE:
@@ -2195,7 +2228,7 @@ unknown_cmnd:
if (reply == -EINVAL)
reply = 0; /* Error reply length */
if (reply >= 0 && common->data_dir == DATA_DIR_TO_HOST) {
- reply = min((u32) reply, common->data_size_from_cmnd);
+ reply = min((u32)reply, common->data_size_from_cmnd);
bh->inreq->length = reply;
bh->state = BUF_STATE_FULL;
common->residue -= reply;
@@ -2225,7 +2258,8 @@ static int received_cbw(struct fsg_dev *fsg, struct fsg_buffhd *bh)
req->actual,
le32_to_cpu(cbw->Signature));
- /* The Bulk-only spec says we MUST stall the IN endpoint
+ /*
+ * 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
@@ -2233,7 +2267,8 @@ static int received_cbw(struct fsg_dev *fsg, struct fsg_buffhd *bh)
*
* We aren't required to halt the OUT endpoint; instead
* we can simply accept and discard any data received
- * until the next reset. */
+ * until the next reset.
+ */
wedge_bulk_in_endpoint(fsg);
set_bit(IGNORE_BULK_OUT, &fsg->atomic_bitflags);
return -EINVAL;
@@ -2246,8 +2281,10 @@ static int received_cbw(struct fsg_dev *fsg, struct fsg_buffhd *bh)
"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. */
+ /*
+ * We can do anything we want here, so let's stall the
+ * bulk pipes if we are allowed to.
+ */
if (common->can_stall) {
fsg_set_halt(fsg, fsg->bulk_out);
halt_bulk_in_endpoint(fsg);
@@ -2270,7 +2307,6 @@ static int received_cbw(struct fsg_dev *fsg, struct fsg_buffhd *bh)
return 0;
}
-
static int get_next_command(struct fsg_common *common)
{
struct fsg_buffhd *bh;
@@ -2287,14 +2323,15 @@ static int get_next_command(struct fsg_common *common)
/* Queue a request to read a Bulk-only CBW */
set_bulk_out_req_length(common, bh, USB_BULK_CB_WRAP_LEN);
bh->outreq->short_not_ok = 1;
- START_TRANSFER_OR(common, bulk_out, bh->outreq,
- &bh->outreq_busy, &bh->state)
+ if (!start_out_transfer(common, bh))
/* Don't know what to do if common->fsg is NULL */
return -EIO;
- /* We will drain the buffer in software, which means we
+ /*
+ * 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. */
+ * next_buffhd_to_fill.
+ */
/* Wait for the CBW to arrive */
while (bh->state != BUF_STATE_FULL) {
@@ -2425,7 +2462,6 @@ reset:
/****************************** ALT CONFIGS ******************************/
-
static int fsg_set_alt(struct usb_function *f, unsigned intf, unsigned alt)
{
struct fsg_dev *fsg = fsg_from_func(f);
@@ -2453,8 +2489,10 @@ static void handle_exception(struct fsg_common *common)
struct fsg_lun *curlun;
unsigned int exception_req_tag;
- /* Clear the existing signals. Anything but SIGUSR1 is converted
- * into a high-priority EXIT exception. */
+ /*
+ * Clear the existing signals. Anything but SIGUSR1 is converted
+ * into a high-priority EXIT exception.
+ */
for (;;) {
int sig =
dequeue_signal_lock(current, &current->blocked, &info);
@@ -2498,8 +2536,10 @@ static void handle_exception(struct fsg_common *common)
usb_ep_fifo_flush(common->fsg->bulk_out);
}
- /* Reset the I/O buffer states and pointers, the SCSI
- * state, and the exception. Then invoke the handler. */
+ /*
+ * Reset the I/O buffer states and pointers, the SCSI
+ * state, and the exception. Then invoke the handler.
+ */
spin_lock_irq(&common->lock);
for (i = 0; i < FSG_NUM_BUFFERS; ++i) {
@@ -2537,9 +2577,11 @@ static void handle_exception(struct fsg_common *common)
break;
case FSG_STATE_RESET:
- /* In case we were forced against our will to halt a
+ /*
+ * In case we were forced against our will to halt a
* bulk endpoint, clear the halt now. (The SuperH UDC
- * requires this.) */
+ * requires this.)
+ */
if (!fsg_is_set(common))
break;
if (test_and_clear_bit(IGNORE_BULK_OUT,
@@ -2549,9 +2591,11 @@ static void handle_exception(struct fsg_common *common)
if (common->ep0_req_tag == exception_req_tag)
ep0_queue(common); /* Complete the status stage */
- /* Technically this should go here, but it would only be
+ /*
+ * Technically this should go here, but it would only be
* a waste of time. Ditto for the INTERFACE_CHANGE and
- * CONFIG_CHANGE cases. */
+ * CONFIG_CHANGE cases.
+ */
/* for (i = 0; i < common->nluns; ++i) */
/* common->luns[i].unit_attention_data = */
/* SS_RESET_OCCURRED; */
@@ -2586,8 +2630,10 @@ static int fsg_main_thread(void *common_)
{
struct fsg_common *common = common_;
- /* Allow the thread to be killed by a signal, but set the signal mask
- * to block everything but INT, TERM, KILL, and USR1. */
+ /*
+ * 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);
@@ -2596,9 +2642,11 @@ static int fsg_main_thread(void *common_)
/* Allow the thread to be frozen */
set_freezable();
- /* Arrange for userspace references to be interpreted as kernel
+ /*
+ * 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. */
+ * that expects a __user pointer and it will work okay.
+ */
set_fs(get_ds());
/* The main loop */
@@ -2658,7 +2706,7 @@ static int fsg_main_thread(void *common_)
up_write(&common->filesem);
}
- /* Let the unbind and cleanup routines know the thread has exited */
+ /* Let fsg_unbind() know the thread has exited */
complete_and_exit(&common->thread_notifier, 0);
}
@@ -2690,7 +2738,6 @@ static inline void fsg_common_put(struct fsg_common *common)
kref_put(&common->ref, fsg_common_release);
}
-
static struct fsg_common *fsg_common_init(struct fsg_common *common,
struct usb_composite_dev *cdev,
struct fsg_config *cfg)
@@ -2736,8 +2783,10 @@ static struct fsg_common *fsg_common_init(struct fsg_common *common,
fsg_intf_desc.iInterface = rc;
}
- /* Create the LUNs, open their backing files, and register the
- * LUN devices in sysfs. */
+ /*
+ * Create the LUNs, open their backing files, and register the
+ * LUN devices in sysfs.
+ */
curlun = kzalloc(nluns * sizeof *curlun, GFP_KERNEL);
if (unlikely(!curlun)) {
rc = -ENOMEM;
@@ -2765,6 +2814,7 @@ static struct fsg_common *fsg_common_init(struct fsg_common *common,
if (rc) {
INFO(common, "failed to register LUN%d: %d\n", i, rc);
common->nluns = i;
+ put_device(&curlun->dev);
goto error_release;
}
@@ -2790,7 +2840,6 @@ static struct fsg_common *fsg_common_init(struct fsg_common *common,
}
common->nluns = nluns;
-
/* Data buffers cyclic list */
bh = common->buffhds;
i = FSG_NUM_BUFFERS;
@@ -2807,7 +2856,6 @@ buffhds_first_it:
} while (--i);
bh->next = common->buffhds;
-
/* Prepare inquiryString */
if (cfg->release != 0xffff) {
i = cfg->release;
@@ -2821,41 +2869,35 @@ buffhds_first_it:
i = 0x0399;
}
}
-#define OR(x, y) ((x) ? (x) : (y))
snprintf(common->inquiry_string, sizeof common->inquiry_string,
- "%-8s%-16s%04x",
- OR(cfg->vendor_name, "Linux "),
+ "%-8s%-16s%04x", cfg->vendor_name ?: "Linux",
/* Assume product name dependent on the first LUN */
- OR(cfg->product_name, common->luns->cdrom
+ cfg->product_name ?: (common->luns->cdrom
? "File-Stor Gadget"
- : "File-CD Gadget "),
+ : "File-CD Gadget"),
i);
-
- /* Some peripheral controllers are known not to be able to
+ /*
+ * Some peripheral controllers are known not to be able to
* halt bulk endpoints correctly. If one of them is present,
* disable stalls.
*/
common->can_stall = cfg->can_stall &&
!(gadget_is_at91(common->gadget));
-
spin_lock_init(&common->lock);
kref_init(&common->ref);
-
/* Tell the thread to start working */
common->thread_task =
kthread_create(fsg_main_thread, common,
- OR(cfg->thread_name, "file-storage"));
+ cfg->thread_name ?: "file-storage");
if (IS_ERR(common->thread_task)) {
rc = PTR_ERR(common->thread_task);
goto error_release;
}
init_completion(&common->thread_notifier);
init_waitqueue_head(&common->fsg_wait);
-#undef OR
-
/* Information */
INFO(common, FSG_DRIVER_DESC ", version: " FSG_DRIVER_VERSION "\n");
@@ -2889,18 +2931,15 @@ buffhds_first_it:
return common;
-
error_luns:
common->nluns = i + 1;
error_release:
common->state = FSG_STATE_TERMINATED; /* The thread is dead */
- /* Call fsg_common_release() directly, ref might be not
- * initialised */
+ /* Call fsg_common_release() directly, ref might be not initialised. */
fsg_common_release(&common->ref);
return ERR_PTR(rc);
}
-
static void fsg_common_release(struct kref *ref)
{
struct fsg_common *common = container_of(ref, struct fsg_common, ref);
@@ -2909,9 +2948,6 @@ static void fsg_common_release(struct kref *ref)
if (common->state != FSG_STATE_TERMINATED) {
raise_exception(common, FSG_STATE_EXIT);
wait_for_completion(&common->thread_notifier);
-
- /* The cleanup routine waits for this completion also */
- complete(&common->thread_notifier);
}
if (likely(common->luns)) {
@@ -2945,7 +2981,6 @@ static void fsg_common_release(struct kref *ref)
/*-------------------------------------------------------------------------*/
-
static void fsg_unbind(struct usb_configuration *c, struct usb_function *f)
{
struct fsg_dev *fsg = fsg_from_func(f);
@@ -2965,7 +3000,6 @@ static void fsg_unbind(struct usb_configuration *c, struct usb_function *f)
kfree(fsg);
}
-
static int fsg_bind(struct usb_configuration *c, struct usb_function *f)
{
struct fsg_dev *fsg = fsg_from_func(f);
@@ -3048,11 +3082,13 @@ static int fsg_bind_config(struct usb_composite_dev *cdev,
fsg->function.disable = fsg_disable;
fsg->common = common;
- /* Our caller holds a reference to common structure so we
+ /*
+ * Our caller holds a reference to common structure so we
* don't have to be worry about it being freed until we return
* from this function. So instead of incrementing counter now
* and decrement in error recovery we increment it only when
- * call to usb_add_function() was successful. */
+ * call to usb_add_function() was successful.
+ */
rc = usb_add_function(c, &fsg->function);
if (unlikely(rc))
@@ -3063,8 +3099,7 @@ static int fsg_bind_config(struct usb_composite_dev *cdev,
}
static inline int __deprecated __maybe_unused
-fsg_add(struct usb_composite_dev *cdev,
- struct usb_configuration *c,
+fsg_add(struct usb_composite_dev *cdev, struct usb_configuration *c,
struct fsg_common *common)
{
return fsg_bind_config(cdev, c, common);
@@ -3073,7 +3108,6 @@ fsg_add(struct usb_composite_dev *cdev,
/************************* Module parameters *************************/
-
struct fsg_module_parameters {
char *file[FSG_MAX_LUNS];
int ro[FSG_MAX_LUNS];
@@ -3087,7 +3121,6 @@ struct fsg_module_parameters {
int stall; /* can_stall */
};
-
#define _FSG_MODULE_PARAM_ARRAY(prefix, params, name, type, desc) \
module_param_array_named(prefix ## name, params.name, type, \
&prefix ## params.name ## _count, \
@@ -3115,7 +3148,6 @@ struct fsg_module_parameters {
_FSG_MODULE_PARAM(prefix, params, stall, bool, \
"false to prevent bulk stalls")
-
static void
fsg_config_from_params(struct fsg_config *cfg,
const struct fsg_module_parameters *params)
diff --git a/drivers/usb/gadget/f_ncm.c b/drivers/usb/gadget/f_ncm.c
new file mode 100644
index 00000000000..130eee678c8
--- /dev/null
+++ b/drivers/usb/gadget/f_ncm.c
@@ -0,0 +1,1407 @@
+/*
+ * f_ncm.c -- USB CDC Network (NCM) link function driver
+ *
+ * Copyright (C) 2010 Nokia Corporation
+ * Contact: Yauheni Kaliuta <yauheni.kaliuta@nokia.com>
+ *
+ * The driver borrows from f_ecm.c which is:
+ *
+ * Copyright (C) 2003-2005,2008 David Brownell
+ * Copyright (C) 2008 Nokia Corporation
+ *
+ * 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 <linux/kernel.h>
+#include <linux/device.h>
+#include <linux/etherdevice.h>
+#include <linux/crc32.h>
+
+#include <linux/usb/cdc.h>
+
+#include "u_ether.h"
+
+/*
+ * This function is a "CDC Network Control Model" (CDC NCM) Ethernet link.
+ * NCM is intended to be used with high-speed network attachments.
+ *
+ * Note that NCM requires the use of "alternate settings" for its data
+ * interface. This means that the set_alt() method has real work to do,
+ * and also means that a get_alt() method is required.
+ */
+
+/* to trigger crc/non-crc ndp signature */
+
+#define NCM_NDP_HDR_CRC_MASK 0x01000000
+#define NCM_NDP_HDR_CRC 0x01000000
+#define NCM_NDP_HDR_NOCRC 0x00000000
+
+struct ncm_ep_descs {
+ struct usb_endpoint_descriptor *in;
+ struct usb_endpoint_descriptor *out;
+ struct usb_endpoint_descriptor *notify;
+};
+
+enum ncm_notify_state {
+ NCM_NOTIFY_NONE, /* don't notify */
+ NCM_NOTIFY_CONNECT, /* issue CONNECT next */
+ NCM_NOTIFY_SPEED, /* issue SPEED_CHANGE next */
+};
+
+struct f_ncm {
+ struct gether port;
+ u8 ctrl_id, data_id;
+
+ char ethaddr[14];
+
+ struct ncm_ep_descs fs;
+ struct ncm_ep_descs hs;
+
+ struct usb_ep *notify;
+ struct usb_endpoint_descriptor *notify_desc;
+ struct usb_request *notify_req;
+ u8 notify_state;
+ bool is_open;
+
+ struct ndp_parser_opts *parser_opts;
+ bool is_crc;
+
+ /*
+ * for notification, it is accessed from both
+ * callback and ethernet open/close
+ */
+ spinlock_t lock;
+};
+
+static inline struct f_ncm *func_to_ncm(struct usb_function *f)
+{
+ return container_of(f, struct f_ncm, port.func);
+}
+
+/* peak (theoretical) bulk transfer rate in bits-per-second */
+static inline unsigned ncm_bitrate(struct usb_gadget *g)
+{
+ if (gadget_is_dualspeed(g) && g->speed == USB_SPEED_HIGH)
+ return 13 * 512 * 8 * 1000 * 8;
+ else
+ return 19 * 64 * 1 * 1000 * 8;
+}
+
+/*-------------------------------------------------------------------------*/
+
+/*
+ * We cannot group frames so use just the minimal size which ok to put
+ * one max-size ethernet frame.
+ * If the host can group frames, allow it to do that, 16K is selected,
+ * because it's used by default by the current linux host driver
+ */
+#define NTB_DEFAULT_IN_SIZE USB_CDC_NCM_NTB_MIN_IN_SIZE
+#define NTB_OUT_SIZE 16384
+
+/*
+ * skbs of size less than that will not be alligned
+ * to NCM's dwNtbInMaxSize to save bus bandwidth
+ */
+
+#define MAX_TX_NONFIXED (512 * 3)
+
+#define FORMATS_SUPPORTED (USB_CDC_NCM_NTB16_SUPPORTED | \
+ USB_CDC_NCM_NTB32_SUPPORTED)
+
+static struct usb_cdc_ncm_ntb_parameters ntb_parameters = {
+ .wLength = sizeof ntb_parameters,
+ .bmNtbFormatsSupported = cpu_to_le16(FORMATS_SUPPORTED),
+ .dwNtbInMaxSize = cpu_to_le32(NTB_DEFAULT_IN_SIZE),
+ .wNdpInDivisor = cpu_to_le16(4),
+ .wNdpInPayloadRemainder = cpu_to_le16(0),
+ .wNdpInAlignment = cpu_to_le16(4),
+
+ .dwNtbOutMaxSize = cpu_to_le32(NTB_OUT_SIZE),
+ .wNdpOutDivisor = cpu_to_le16(4),
+ .wNdpOutPayloadRemainder = cpu_to_le16(0),
+ .wNdpOutAlignment = cpu_to_le16(4),
+};
+
+/*
+ * Use wMaxPacketSize big enough to fit CDC_NOTIFY_SPEED_CHANGE in one
+ * packet, to simplify cancellation; and a big transfer interval, to
+ * waste less bandwidth.
+ */
+
+#define LOG2_STATUS_INTERVAL_MSEC 5 /* 1 << 5 == 32 msec */
+#define NCM_STATUS_BYTECOUNT 16 /* 8 byte header + data */
+
+static struct usb_interface_assoc_descriptor ncm_iad_desc __initdata = {
+ .bLength = sizeof ncm_iad_desc,
+ .bDescriptorType = USB_DT_INTERFACE_ASSOCIATION,
+
+ /* .bFirstInterface = DYNAMIC, */
+ .bInterfaceCount = 2, /* control + data */
+ .bFunctionClass = USB_CLASS_COMM,
+ .bFunctionSubClass = USB_CDC_SUBCLASS_NCM,
+ .bFunctionProtocol = USB_CDC_PROTO_NONE,
+ /* .iFunction = DYNAMIC */
+};
+
+/* interface descriptor: */
+
+static struct usb_interface_descriptor ncm_control_intf __initdata = {
+ .bLength = sizeof ncm_control_intf,
+ .bDescriptorType = USB_DT_INTERFACE,
+
+ /* .bInterfaceNumber = DYNAMIC */
+ .bNumEndpoints = 1,
+ .bInterfaceClass = USB_CLASS_COMM,
+ .bInterfaceSubClass = USB_CDC_SUBCLASS_NCM,
+ .bInterfaceProtocol = USB_CDC_PROTO_NONE,
+ /* .iInterface = DYNAMIC */
+};
+
+static struct usb_cdc_header_desc ncm_header_desc __initdata = {
+ .bLength = sizeof ncm_header_desc,
+ .bDescriptorType = USB_DT_CS_INTERFACE,
+ .bDescriptorSubType = USB_CDC_HEADER_TYPE,
+
+ .bcdCDC = cpu_to_le16(0x0110),
+};
+
+static struct usb_cdc_union_desc ncm_union_desc __initdata = {
+ .bLength = sizeof(ncm_union_desc),
+ .bDescriptorType = USB_DT_CS_INTERFACE,
+ .bDescriptorSubType = USB_CDC_UNION_TYPE,
+ /* .bMasterInterface0 = DYNAMIC */
+ /* .bSlaveInterface0 = DYNAMIC */
+};
+
+static struct usb_cdc_ether_desc ecm_desc __initdata = {
+ .bLength = sizeof ecm_desc,
+ .bDescriptorType = USB_DT_CS_INTERFACE,
+ .bDescriptorSubType = USB_CDC_ETHERNET_TYPE,
+
+ /* this descriptor actually adds value, surprise! */
+ /* .iMACAddress = DYNAMIC */
+ .bmEthernetStatistics = cpu_to_le32(0), /* no statistics */
+ .wMaxSegmentSize = cpu_to_le16(ETH_FRAME_LEN),
+ .wNumberMCFilters = cpu_to_le16(0),
+ .bNumberPowerFilters = 0,
+};
+
+#define NCAPS (USB_CDC_NCM_NCAP_ETH_FILTER | USB_CDC_NCM_NCAP_CRC_MODE)
+
+static struct usb_cdc_ncm_desc ncm_desc __initdata = {
+ .bLength = sizeof ncm_desc,
+ .bDescriptorType = USB_DT_CS_INTERFACE,
+ .bDescriptorSubType = USB_CDC_NCM_TYPE,
+
+ .bcdNcmVersion = cpu_to_le16(0x0100),
+ /* can process SetEthernetPacketFilter */
+ .bmNetworkCapabilities = NCAPS,
+};
+
+/* the default data interface has no endpoints ... */
+
+static struct usb_interface_descriptor ncm_data_nop_intf __initdata = {
+ .bLength = sizeof ncm_data_nop_intf,
+ .bDescriptorType = USB_DT_INTERFACE,
+
+ .bInterfaceNumber = 1,
+ .bAlternateSetting = 0,
+ .bNumEndpoints = 0,
+ .bInterfaceClass = USB_CLASS_CDC_DATA,
+ .bInterfaceSubClass = 0,
+ .bInterfaceProtocol = USB_CDC_NCM_PROTO_NTB,
+ /* .iInterface = DYNAMIC */
+};
+
+/* ... but the "real" data interface has two bulk endpoints */
+
+static struct usb_interface_descriptor ncm_data_intf __initdata = {
+ .bLength = sizeof ncm_data_intf,
+ .bDescriptorType = USB_DT_INTERFACE,
+
+ .bInterfaceNumber = 1,
+ .bAlternateSetting = 1,
+ .bNumEndpoints = 2,
+ .bInterfaceClass = USB_CLASS_CDC_DATA,
+ .bInterfaceSubClass = 0,
+ .bInterfaceProtocol = USB_CDC_NCM_PROTO_NTB,
+ /* .iInterface = DYNAMIC */
+};
+
+/* full speed support: */
+
+static struct usb_endpoint_descriptor fs_ncm_notify_desc __initdata = {
+ .bLength = USB_DT_ENDPOINT_SIZE,
+ .bDescriptorType = USB_DT_ENDPOINT,
+
+ .bEndpointAddress = USB_DIR_IN,
+ .bmAttributes = USB_ENDPOINT_XFER_INT,
+ .wMaxPacketSize = cpu_to_le16(NCM_STATUS_BYTECOUNT),
+ .bInterval = 1 << LOG2_STATUS_INTERVAL_MSEC,
+};
+
+static struct usb_endpoint_descriptor fs_ncm_in_desc __initdata = {
+ .bLength = USB_DT_ENDPOINT_SIZE,
+ .bDescriptorType = USB_DT_ENDPOINT,
+
+ .bEndpointAddress = USB_DIR_IN,
+ .bmAttributes = USB_ENDPOINT_XFER_BULK,
+};
+
+static struct usb_endpoint_descriptor fs_ncm_out_desc __initdata = {
+ .bLength = USB_DT_ENDPOINT_SIZE,
+ .bDescriptorType = USB_DT_ENDPOINT,
+
+ .bEndpointAddress = USB_DIR_OUT,
+ .bmAttributes = USB_ENDPOINT_XFER_BULK,
+};
+
+static struct usb_descriptor_header *ncm_fs_function[] __initdata = {
+ (struct usb_descriptor_header *) &ncm_iad_desc,
+ /* CDC NCM control descriptors */
+ (struct usb_descriptor_header *) &ncm_control_intf,
+ (struct usb_descriptor_header *) &ncm_header_desc,
+ (struct usb_descriptor_header *) &ncm_union_desc,
+ (struct usb_descriptor_header *) &ecm_desc,
+ (struct usb_descriptor_header *) &ncm_desc,
+ (struct usb_descriptor_header *) &fs_ncm_notify_desc,
+ /* data interface, altsettings 0 and 1 */
+ (struct usb_descriptor_header *) &ncm_data_nop_intf,
+ (struct usb_descriptor_header *) &ncm_data_intf,
+ (struct usb_descriptor_header *) &fs_ncm_in_desc,
+ (struct usb_descriptor_header *) &fs_ncm_out_desc,
+ NULL,
+};
+
+/* high speed support: */
+
+static struct usb_endpoint_descriptor hs_ncm_notify_desc __initdata = {
+ .bLength = USB_DT_ENDPOINT_SIZE,
+ .bDescriptorType = USB_DT_ENDPOINT,
+
+ .bEndpointAddress = USB_DIR_IN,
+ .bmAttributes = USB_ENDPOINT_XFER_INT,
+ .wMaxPacketSize = cpu_to_le16(NCM_STATUS_BYTECOUNT),
+ .bInterval = LOG2_STATUS_INTERVAL_MSEC + 4,
+};
+static struct usb_endpoint_descriptor hs_ncm_in_desc __initdata = {
+ .bLength = USB_DT_ENDPOINT_SIZE,
+ .bDescriptorType = USB_DT_ENDPOINT,
+
+ .bEndpointAddress = USB_DIR_IN,
+ .bmAttributes = USB_ENDPOINT_XFER_BULK,
+ .wMaxPacketSize = cpu_to_le16(512),
+};
+
+static struct usb_endpoint_descriptor hs_ncm_out_desc __initdata = {
+ .bLength = USB_DT_ENDPOINT_SIZE,
+ .bDescriptorType = USB_DT_ENDPOINT,
+
+ .bEndpointAddress = USB_DIR_OUT,
+ .bmAttributes = USB_ENDPOINT_XFER_BULK,
+ .wMaxPacketSize = cpu_to_le16(512),
+};
+
+static struct usb_descriptor_header *ncm_hs_function[] __initdata = {
+ (struct usb_descriptor_header *) &ncm_iad_desc,
+ /* CDC NCM control descriptors */
+ (struct usb_descriptor_header *) &ncm_control_intf,
+ (struct usb_descriptor_header *) &ncm_header_desc,
+ (struct usb_descriptor_header *) &ncm_union_desc,
+ (struct usb_descriptor_header *) &ecm_desc,
+ (struct usb_descriptor_header *) &ncm_desc,
+ (struct usb_descriptor_header *) &hs_ncm_notify_desc,
+ /* data interface, altsettings 0 and 1 */
+ (struct usb_descriptor_header *) &ncm_data_nop_intf,
+ (struct usb_descriptor_header *) &ncm_data_intf,
+ (struct usb_descriptor_header *) &hs_ncm_in_desc,
+ (struct usb_descriptor_header *) &hs_ncm_out_desc,
+ NULL,
+};
+
+/* string descriptors: */
+
+#define STRING_CTRL_IDX 0
+#define STRING_MAC_IDX 1
+#define STRING_DATA_IDX 2
+#define STRING_IAD_IDX 3
+
+static struct usb_string ncm_string_defs[] = {
+ [STRING_CTRL_IDX].s = "CDC Network Control Model (NCM)",
+ [STRING_MAC_IDX].s = NULL /* DYNAMIC */,
+ [STRING_DATA_IDX].s = "CDC Network Data",
+ [STRING_IAD_IDX].s = "CDC NCM",
+ { } /* end of list */
+};
+
+static struct usb_gadget_strings ncm_string_table = {
+ .language = 0x0409, /* en-us */
+ .strings = ncm_string_defs,
+};
+
+static struct usb_gadget_strings *ncm_strings[] = {
+ &ncm_string_table,
+ NULL,
+};
+
+/*
+ * Here are options for NCM Datagram Pointer table (NDP) parser.
+ * There are 2 different formats: NDP16 and NDP32 in the spec (ch. 3),
+ * in NDP16 offsets and sizes fields are 1 16bit word wide,
+ * in NDP32 -- 2 16bit words wide. Also signatures are different.
+ * To make the parser code the same, put the differences in the structure,
+ * and switch pointers to the structures when the format is changed.
+ */
+
+struct ndp_parser_opts {
+ u32 nth_sign;
+ u32 ndp_sign;
+ unsigned nth_size;
+ unsigned ndp_size;
+ unsigned ndplen_align;
+ /* sizes in u16 units */
+ unsigned dgram_item_len; /* index or length */
+ unsigned block_length;
+ unsigned fp_index;
+ unsigned reserved1;
+ unsigned reserved2;
+ unsigned next_fp_index;
+};
+
+#define INIT_NDP16_OPTS { \
+ .nth_sign = USB_CDC_NCM_NTH16_SIGN, \
+ .ndp_sign = USB_CDC_NCM_NDP16_NOCRC_SIGN, \
+ .nth_size = sizeof(struct usb_cdc_ncm_nth16), \
+ .ndp_size = sizeof(struct usb_cdc_ncm_ndp16), \
+ .ndplen_align = 4, \
+ .dgram_item_len = 1, \
+ .block_length = 1, \
+ .fp_index = 1, \
+ .reserved1 = 0, \
+ .reserved2 = 0, \
+ .next_fp_index = 1, \
+ }
+
+
+#define INIT_NDP32_OPTS { \
+ .nth_sign = USB_CDC_NCM_NTH32_SIGN, \
+ .ndp_sign = USB_CDC_NCM_NDP32_NOCRC_SIGN, \
+ .nth_size = sizeof(struct usb_cdc_ncm_nth32), \
+ .ndp_size = sizeof(struct usb_cdc_ncm_ndp32), \
+ .ndplen_align = 8, \
+ .dgram_item_len = 2, \
+ .block_length = 2, \
+ .fp_index = 2, \
+ .reserved1 = 1, \
+ .reserved2 = 2, \
+ .next_fp_index = 2, \
+ }
+
+static struct ndp_parser_opts ndp16_opts = INIT_NDP16_OPTS;
+static struct ndp_parser_opts ndp32_opts = INIT_NDP32_OPTS;
+
+static inline void put_ncm(__le16 **p, unsigned size, unsigned val)
+{
+ switch (size) {
+ case 1:
+ put_unaligned_le16((u16)val, *p);
+ break;
+ case 2:
+ put_unaligned_le32((u32)val, *p);
+
+ break;
+ default:
+ BUG();
+ }
+
+ *p += size;
+}
+
+static inline unsigned get_ncm(__le16 **p, unsigned size)
+{
+ unsigned tmp;
+
+ switch (size) {
+ case 1:
+ tmp = get_unaligned_le16(*p);
+ break;
+ case 2:
+ tmp = get_unaligned_le32(*p);
+ break;
+ default:
+ BUG();
+ }
+
+ *p += size;
+ return tmp;
+}
+
+/*-------------------------------------------------------------------------*/
+
+static inline void ncm_reset_values(struct f_ncm *ncm)
+{
+ ncm->parser_opts = &ndp16_opts;
+ ncm->is_crc = false;
+ ncm->port.cdc_filter = DEFAULT_FILTER;
+
+ /* doesn't make sense for ncm, fixed size used */
+ ncm->port.header_len = 0;
+
+ ncm->port.fixed_out_len = le32_to_cpu(ntb_parameters.dwNtbOutMaxSize);
+ ncm->port.fixed_in_len = NTB_DEFAULT_IN_SIZE;
+}
+
+/*
+ * Context: ncm->lock held
+ */
+static void ncm_do_notify(struct f_ncm *ncm)
+{
+ struct usb_request *req = ncm->notify_req;
+ struct usb_cdc_notification *event;
+ struct usb_composite_dev *cdev = ncm->port.func.config->cdev;
+ __le32 *data;
+ int status;
+
+ /* notification already in flight? */
+ if (!req)
+ return;
+
+ event = req->buf;
+ switch (ncm->notify_state) {
+ case NCM_NOTIFY_NONE:
+ return;
+
+ case NCM_NOTIFY_CONNECT:
+ event->bNotificationType = USB_CDC_NOTIFY_NETWORK_CONNECTION;
+ if (ncm->is_open)
+ event->wValue = cpu_to_le16(1);
+ else
+ event->wValue = cpu_to_le16(0);
+ event->wLength = 0;
+ req->length = sizeof *event;
+
+ DBG(cdev, "notify connect %s\n",
+ ncm->is_open ? "true" : "false");
+ ncm->notify_state = NCM_NOTIFY_NONE;
+ break;
+
+ case NCM_NOTIFY_SPEED:
+ event->bNotificationType = USB_CDC_NOTIFY_SPEED_CHANGE;
+ event->wValue = cpu_to_le16(0);
+ event->wLength = cpu_to_le16(8);
+ req->length = NCM_STATUS_BYTECOUNT;
+
+ /* SPEED_CHANGE data is up/down speeds in bits/sec */
+ data = req->buf + sizeof *event;
+ data[0] = cpu_to_le32(ncm_bitrate(cdev->gadget));
+ data[1] = data[0];
+
+ DBG(cdev, "notify speed %d\n", ncm_bitrate(cdev->gadget));
+ ncm->notify_state = NCM_NOTIFY_CONNECT;
+ break;
+ }
+ event->bmRequestType = 0xA1;
+ event->wIndex = cpu_to_le16(ncm->ctrl_id);
+
+ ncm->notify_req = NULL;
+ /*
+ * In double buffering if there is a space in FIFO,
+ * completion callback can be called right after the call,
+ * so unlocking
+ */
+ spin_unlock(&ncm->lock);
+ status = usb_ep_queue(ncm->notify, req, GFP_ATOMIC);
+ spin_lock(&ncm->lock);
+ if (status < 0) {
+ ncm->notify_req = req;
+ DBG(cdev, "notify --> %d\n", status);
+ }
+}
+
+/*
+ * Context: ncm->lock held
+ */
+static void ncm_notify(struct f_ncm *ncm)
+{
+ /*
+ * NOTE on most versions of Linux, host side cdc-ethernet
+ * won't listen for notifications until its netdevice opens.
+ * The first notification then sits in the FIFO for a long
+ * time, and the second one is queued.
+ *
+ * If ncm_notify() is called before the second (CONNECT)
+ * notification is sent, then it will reset to send the SPEED
+ * notificaion again (and again, and again), but it's not a problem
+ */
+ ncm->notify_state = NCM_NOTIFY_SPEED;
+ ncm_do_notify(ncm);
+}
+
+static void ncm_notify_complete(struct usb_ep *ep, struct usb_request *req)
+{
+ struct f_ncm *ncm = req->context;
+ struct usb_composite_dev *cdev = ncm->port.func.config->cdev;
+ struct usb_cdc_notification *event = req->buf;
+
+ spin_lock(&ncm->lock);
+ switch (req->status) {
+ case 0:
+ VDBG(cdev, "Notification %02x sent\n",
+ event->bNotificationType);
+ break;
+ case -ECONNRESET:
+ case -ESHUTDOWN:
+ ncm->notify_state = NCM_NOTIFY_NONE;
+ break;
+ default:
+ DBG(cdev, "event %02x --> %d\n",
+ event->bNotificationType, req->status);
+ break;
+ }
+ ncm->notify_req = req;
+ ncm_do_notify(ncm);
+ spin_unlock(&ncm->lock);
+}
+
+static void ncm_ep0out_complete(struct usb_ep *ep, struct usb_request *req)
+{
+ /* now for SET_NTB_INPUT_SIZE only */
+ unsigned in_size;
+ struct usb_function *f = req->context;
+ struct f_ncm *ncm = func_to_ncm(f);
+ struct usb_composite_dev *cdev = ep->driver_data;
+
+ req->context = NULL;
+ if (req->status || req->actual != req->length) {
+ DBG(cdev, "Bad control-OUT transfer\n");
+ goto invalid;
+ }
+
+ in_size = get_unaligned_le32(req->buf);
+ if (in_size < USB_CDC_NCM_NTB_MIN_IN_SIZE ||
+ in_size > le32_to_cpu(ntb_parameters.dwNtbInMaxSize)) {
+ DBG(cdev, "Got wrong INPUT SIZE (%d) from host\n", in_size);
+ goto invalid;
+ }
+
+ ncm->port.fixed_in_len = in_size;
+ VDBG(cdev, "Set NTB INPUT SIZE %d\n", in_size);
+ return;
+
+invalid:
+ usb_ep_set_halt(ep);
+ return;
+}
+
+static int ncm_setup(struct usb_function *f, const struct usb_ctrlrequest *ctrl)
+{
+ struct f_ncm *ncm = func_to_ncm(f);
+ struct usb_composite_dev *cdev = f->config->cdev;
+ struct usb_request *req = cdev->req;
+ 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);
+
+ /*
+ * composite driver infrastructure handles everything except
+ * CDC class messages; interface activation uses set_alt().
+ */
+ switch ((ctrl->bRequestType << 8) | ctrl->bRequest) {
+ case ((USB_DIR_OUT | USB_TYPE_CLASS | USB_RECIP_INTERFACE) << 8)
+ | USB_CDC_SET_ETHERNET_PACKET_FILTER:
+ /*
+ * see 6.2.30: no data, wIndex = interface,
+ * wValue = packet filter bitmap
+ */
+ if (w_length != 0 || w_index != ncm->ctrl_id)
+ goto invalid;
+ DBG(cdev, "packet filter %02x\n", w_value);
+ /*
+ * REVISIT locking of cdc_filter. This assumes the UDC
+ * driver won't have a concurrent packet TX irq running on
+ * another CPU; or that if it does, this write is atomic...
+ */
+ ncm->port.cdc_filter = w_value;
+ value = 0;
+ break;
+ /*
+ * and optionally:
+ * case USB_CDC_SEND_ENCAPSULATED_COMMAND:
+ * case USB_CDC_GET_ENCAPSULATED_RESPONSE:
+ * case USB_CDC_SET_ETHERNET_MULTICAST_FILTERS:
+ * case USB_CDC_SET_ETHERNET_PM_PATTERN_FILTER:
+ * case USB_CDC_GET_ETHERNET_PM_PATTERN_FILTER:
+ * case USB_CDC_GET_ETHERNET_STATISTIC:
+ */
+
+ case ((USB_DIR_IN | USB_TYPE_CLASS | USB_RECIP_INTERFACE) << 8)
+ | USB_CDC_GET_NTB_PARAMETERS:
+
+ if (w_length == 0 || w_value != 0 || w_index != ncm->ctrl_id)
+ goto invalid;
+ value = w_length > sizeof ntb_parameters ?
+ sizeof ntb_parameters : w_length;
+ memcpy(req->buf, &ntb_parameters, value);
+ VDBG(cdev, "Host asked NTB parameters\n");
+ break;
+
+ case ((USB_DIR_IN | USB_TYPE_CLASS | USB_RECIP_INTERFACE) << 8)
+ | USB_CDC_GET_NTB_INPUT_SIZE:
+
+ if (w_length < 4 || w_value != 0 || w_index != ncm->ctrl_id)
+ goto invalid;
+ put_unaligned_le32(ncm->port.fixed_in_len, req->buf);
+ value = 4;
+ VDBG(cdev, "Host asked INPUT SIZE, sending %d\n",
+ ncm->port.fixed_in_len);
+ break;
+
+ case ((USB_DIR_OUT | USB_TYPE_CLASS | USB_RECIP_INTERFACE) << 8)
+ | USB_CDC_SET_NTB_INPUT_SIZE:
+ {
+ if (w_length != 4 || w_value != 0 || w_index != ncm->ctrl_id)
+ goto invalid;
+ req->complete = ncm_ep0out_complete;
+ req->length = w_length;
+ req->context = f;
+
+ value = req->length;
+ break;
+ }
+
+ case ((USB_DIR_IN | USB_TYPE_CLASS | USB_RECIP_INTERFACE) << 8)
+ | USB_CDC_GET_NTB_FORMAT:
+ {
+ uint16_t format;
+
+ if (w_length < 2 || w_value != 0 || w_index != ncm->ctrl_id)
+ goto invalid;
+ format = (ncm->parser_opts == &ndp16_opts) ? 0x0000 : 0x0001;
+ put_unaligned_le16(format, req->buf);
+ value = 2;
+ VDBG(cdev, "Host asked NTB FORMAT, sending %d\n", format);
+ break;
+ }
+
+ case ((USB_DIR_OUT | USB_TYPE_CLASS | USB_RECIP_INTERFACE) << 8)
+ | USB_CDC_SET_NTB_FORMAT:
+ {
+ if (w_length != 0 || w_index != ncm->ctrl_id)
+ goto invalid;
+ switch (w_value) {
+ case 0x0000:
+ ncm->parser_opts = &ndp16_opts;
+ DBG(cdev, "NCM16 selected\n");
+ break;
+ case 0x0001:
+ ncm->parser_opts = &ndp32_opts;
+ DBG(cdev, "NCM32 selected\n");
+ break;
+ default:
+ goto invalid;
+ }
+ value = 0;
+ break;
+ }
+ case ((USB_DIR_IN | USB_TYPE_CLASS | USB_RECIP_INTERFACE) << 8)
+ | USB_CDC_GET_CRC_MODE:
+ {
+ uint16_t is_crc;
+
+ if (w_length < 2 || w_value != 0 || w_index != ncm->ctrl_id)
+ goto invalid;
+ is_crc = ncm->is_crc ? 0x0001 : 0x0000;
+ put_unaligned_le16(is_crc, req->buf);
+ value = 2;
+ VDBG(cdev, "Host asked CRC MODE, sending %d\n", is_crc);
+ break;
+ }
+
+ case ((USB_DIR_OUT | USB_TYPE_CLASS | USB_RECIP_INTERFACE) << 8)
+ | USB_CDC_SET_CRC_MODE:
+ {
+ int ndp_hdr_crc = 0;
+
+ if (w_length != 0 || w_index != ncm->ctrl_id)
+ goto invalid;
+ switch (w_value) {
+ case 0x0000:
+ ncm->is_crc = false;
+ ndp_hdr_crc = NCM_NDP_HDR_NOCRC;
+ DBG(cdev, "non-CRC mode selected\n");
+ break;
+ case 0x0001:
+ ncm->is_crc = true;
+ ndp_hdr_crc = NCM_NDP_HDR_CRC;
+ DBG(cdev, "CRC mode selected\n");
+ break;
+ default:
+ goto invalid;
+ }
+ ncm->parser_opts->ndp_sign &= ~NCM_NDP_HDR_CRC_MASK;
+ ncm->parser_opts->ndp_sign |= ndp_hdr_crc;
+ value = 0;
+ break;
+ }
+
+ /* and disabled in ncm descriptor: */
+ /* case USB_CDC_GET_NET_ADDRESS: */
+ /* case USB_CDC_SET_NET_ADDRESS: */
+ /* case USB_CDC_GET_MAX_DATAGRAM_SIZE: */
+ /* case USB_CDC_SET_MAX_DATAGRAM_SIZE: */
+
+ default:
+invalid:
+ DBG(cdev, "invalid control req%02x.%02x v%04x i%04x l%d\n",
+ ctrl->bRequestType, ctrl->bRequest,
+ w_value, w_index, w_length);
+ }
+
+ /* respond with data transfer or status phase? */
+ if (value >= 0) {
+ DBG(cdev, "ncm req%02x.%02x v%04x i%04x l%d\n",
+ ctrl->bRequestType, ctrl->bRequest,
+ w_value, w_index, w_length);
+ req->zero = 0;
+ req->length = value;
+ value = usb_ep_queue(cdev->gadget->ep0, req, GFP_ATOMIC);
+ if (value < 0)
+ ERROR(cdev, "ncm req %02x.%02x response err %d\n",
+ ctrl->bRequestType, ctrl->bRequest,
+ value);
+ }
+
+ /* device either stalls (value < 0) or reports success */
+ return value;
+}
+
+
+static int ncm_set_alt(struct usb_function *f, unsigned intf, unsigned alt)
+{
+ struct f_ncm *ncm = func_to_ncm(f);
+ struct usb_composite_dev *cdev = f->config->cdev;
+
+ /* Control interface has only altsetting 0 */
+ if (intf == ncm->ctrl_id) {
+ if (alt != 0)
+ goto fail;
+
+ if (ncm->notify->driver_data) {
+ DBG(cdev, "reset ncm control %d\n", intf);
+ usb_ep_disable(ncm->notify);
+ } else {
+ DBG(cdev, "init ncm ctrl %d\n", intf);
+ ncm->notify_desc = ep_choose(cdev->gadget,
+ ncm->hs.notify,
+ ncm->fs.notify);
+ }
+ usb_ep_enable(ncm->notify, ncm->notify_desc);
+ ncm->notify->driver_data = ncm;
+
+ /* Data interface has two altsettings, 0 and 1 */
+ } else if (intf == ncm->data_id) {
+ if (alt > 1)
+ goto fail;
+
+ if (ncm->port.in_ep->driver_data) {
+ DBG(cdev, "reset ncm\n");
+ gether_disconnect(&ncm->port);
+ ncm_reset_values(ncm);
+ }
+
+ /*
+ * CDC Network only sends data in non-default altsettings.
+ * Changing altsettings resets filters, statistics, etc.
+ */
+ if (alt == 1) {
+ struct net_device *net;
+
+ if (!ncm->port.in) {
+ DBG(cdev, "init ncm\n");
+ ncm->port.in = ep_choose(cdev->gadget,
+ ncm->hs.in,
+ ncm->fs.in);
+ ncm->port.out = ep_choose(cdev->gadget,
+ ncm->hs.out,
+ ncm->fs.out);
+ }
+
+ /* TODO */
+ /* Enable zlps by default for NCM conformance;
+ * override for musb_hdrc (avoids txdma ovhead)
+ */
+ ncm->port.is_zlp_ok = !(
+ gadget_is_musbhdrc(cdev->gadget)
+ );
+ ncm->port.cdc_filter = DEFAULT_FILTER;
+ DBG(cdev, "activate ncm\n");
+ net = gether_connect(&ncm->port);
+ if (IS_ERR(net))
+ return PTR_ERR(net);
+ }
+
+ spin_lock(&ncm->lock);
+ ncm_notify(ncm);
+ spin_unlock(&ncm->lock);
+ } else
+ goto fail;
+
+ return 0;
+fail:
+ return -EINVAL;
+}
+
+/*
+ * Because the data interface supports multiple altsettings,
+ * this NCM function *MUST* implement a get_alt() method.
+ */
+static int ncm_get_alt(struct usb_function *f, unsigned intf)
+{
+ struct f_ncm *ncm = func_to_ncm(f);
+
+ if (intf == ncm->ctrl_id)
+ return 0;
+ return ncm->port.in_ep->driver_data ? 1 : 0;
+}
+
+static struct sk_buff *ncm_wrap_ntb(struct gether *port,
+ struct sk_buff *skb)
+{
+ struct f_ncm *ncm = func_to_ncm(&port->func);
+ struct sk_buff *skb2;
+ int ncb_len = 0;
+ __le16 *tmp;
+ int div = ntb_parameters.wNdpInDivisor;
+ int rem = ntb_parameters.wNdpInPayloadRemainder;
+ int pad;
+ int ndp_align = ntb_parameters.wNdpInAlignment;
+ int ndp_pad;
+ unsigned max_size = ncm->port.fixed_in_len;
+ struct ndp_parser_opts *opts = ncm->parser_opts;
+ unsigned crc_len = ncm->is_crc ? sizeof(uint32_t) : 0;
+
+ ncb_len += opts->nth_size;
+ ndp_pad = ALIGN(ncb_len, ndp_align) - ncb_len;
+ ncb_len += ndp_pad;
+ ncb_len += opts->ndp_size;
+ ncb_len += 2 * 2 * opts->dgram_item_len; /* Datagram entry */
+ ncb_len += 2 * 2 * opts->dgram_item_len; /* Zero datagram entry */
+ pad = ALIGN(ncb_len, div) + rem - ncb_len;
+ ncb_len += pad;
+
+ if (ncb_len + skb->len + crc_len > max_size) {
+ dev_kfree_skb_any(skb);
+ return NULL;
+ }
+
+ skb2 = skb_copy_expand(skb, ncb_len,
+ max_size - skb->len - ncb_len - crc_len,
+ GFP_ATOMIC);
+ dev_kfree_skb_any(skb);
+ if (!skb2)
+ return NULL;
+
+ skb = skb2;
+
+ tmp = (void *) skb_push(skb, ncb_len);
+ memset(tmp, 0, ncb_len);
+
+ put_unaligned_le32(opts->nth_sign, tmp); /* dwSignature */
+ tmp += 2;
+ /* wHeaderLength */
+ put_unaligned_le16(opts->nth_size, tmp++);
+ tmp++; /* skip wSequence */
+ put_ncm(&tmp, opts->block_length, skb->len); /* (d)wBlockLength */
+ /* (d)wFpIndex */
+ /* the first pointer is right after the NTH + align */
+ put_ncm(&tmp, opts->fp_index, opts->nth_size + ndp_pad);
+
+ tmp = (void *)tmp + ndp_pad;
+
+ /* NDP */
+ put_unaligned_le32(opts->ndp_sign, tmp); /* dwSignature */
+ tmp += 2;
+ /* wLength */
+ put_unaligned_le16(ncb_len - opts->nth_size - pad, tmp++);
+
+ tmp += opts->reserved1;
+ tmp += opts->next_fp_index; /* skip reserved (d)wNextFpIndex */
+ tmp += opts->reserved2;
+
+ if (ncm->is_crc) {
+ uint32_t crc;
+
+ crc = ~crc32_le(~0,
+ skb->data + ncb_len,
+ skb->len - ncb_len);
+ put_unaligned_le32(crc, skb->data + skb->len);
+ skb_put(skb, crc_len);
+ }
+
+ /* (d)wDatagramIndex[0] */
+ put_ncm(&tmp, opts->dgram_item_len, ncb_len);
+ /* (d)wDatagramLength[0] */
+ put_ncm(&tmp, opts->dgram_item_len, skb->len - ncb_len);
+ /* (d)wDatagramIndex[1] and (d)wDatagramLength[1] already zeroed */
+
+ if (skb->len > MAX_TX_NONFIXED)
+ memset(skb_put(skb, max_size - skb->len),
+ 0, max_size - skb->len);
+
+ return skb;
+}
+
+static int ncm_unwrap_ntb(struct gether *port,
+ struct sk_buff *skb,
+ struct sk_buff_head *list)
+{
+ struct f_ncm *ncm = func_to_ncm(&port->func);
+ __le16 *tmp = (void *) skb->data;
+ unsigned index, index2;
+ unsigned dg_len, dg_len2;
+ unsigned ndp_len;
+ struct sk_buff *skb2;
+ int ret = -EINVAL;
+ unsigned max_size = le32_to_cpu(ntb_parameters.dwNtbOutMaxSize);
+ struct ndp_parser_opts *opts = ncm->parser_opts;
+ unsigned crc_len = ncm->is_crc ? sizeof(uint32_t) : 0;
+ int dgram_counter;
+
+ /* dwSignature */
+ if (get_unaligned_le32(tmp) != opts->nth_sign) {
+ INFO(port->func.config->cdev, "Wrong NTH SIGN, skblen %d\n",
+ skb->len);
+ print_hex_dump(KERN_INFO, "HEAD:", DUMP_PREFIX_ADDRESS, 32, 1,
+ skb->data, 32, false);
+
+ goto err;
+ }
+ tmp += 2;
+ /* wHeaderLength */
+ if (get_unaligned_le16(tmp++) != opts->nth_size) {
+ INFO(port->func.config->cdev, "Wrong NTB headersize\n");
+ goto err;
+ }
+ tmp++; /* skip wSequence */
+
+ /* (d)wBlockLength */
+ if (get_ncm(&tmp, opts->block_length) > max_size) {
+ INFO(port->func.config->cdev, "OUT size exceeded\n");
+ goto err;
+ }
+
+ index = get_ncm(&tmp, opts->fp_index);
+ /* NCM 3.2 */
+ if (((index % 4) != 0) && (index < opts->nth_size)) {
+ INFO(port->func.config->cdev, "Bad index: %x\n",
+ index);
+ goto err;
+ }
+
+ /* walk through NDP */
+ tmp = ((void *)skb->data) + index;
+ if (get_unaligned_le32(tmp) != opts->ndp_sign) {
+ INFO(port->func.config->cdev, "Wrong NDP SIGN\n");
+ goto err;
+ }
+ tmp += 2;
+
+ ndp_len = get_unaligned_le16(tmp++);
+ /*
+ * NCM 3.3.1
+ * entry is 2 items
+ * item size is 16/32 bits, opts->dgram_item_len * 2 bytes
+ * minimal: struct usb_cdc_ncm_ndpX + normal entry + zero entry
+ */
+ if ((ndp_len < opts->ndp_size + 2 * 2 * (opts->dgram_item_len * 2))
+ || (ndp_len % opts->ndplen_align != 0)) {
+ INFO(port->func.config->cdev, "Bad NDP length: %x\n", ndp_len);
+ goto err;
+ }
+ tmp += opts->reserved1;
+ tmp += opts->next_fp_index; /* skip reserved (d)wNextFpIndex */
+ tmp += opts->reserved2;
+
+ ndp_len -= opts->ndp_size;
+ index2 = get_ncm(&tmp, opts->dgram_item_len);
+ dg_len2 = get_ncm(&tmp, opts->dgram_item_len);
+ dgram_counter = 0;
+
+ do {
+ index = index2;
+ dg_len = dg_len2;
+ if (dg_len < 14 + crc_len) { /* ethernet header + crc */
+ INFO(port->func.config->cdev, "Bad dgram length: %x\n",
+ dg_len);
+ goto err;
+ }
+ if (ncm->is_crc) {
+ uint32_t crc, crc2;
+
+ crc = get_unaligned_le32(skb->data +
+ index + dg_len - crc_len);
+ crc2 = ~crc32_le(~0,
+ skb->data + index,
+ dg_len - crc_len);
+ if (crc != crc2) {
+ INFO(port->func.config->cdev, "Bad CRC\n");
+ goto err;
+ }
+ }
+
+ index2 = get_ncm(&tmp, opts->dgram_item_len);
+ dg_len2 = get_ncm(&tmp, opts->dgram_item_len);
+
+ if (index2 == 0 || dg_len2 == 0) {
+ skb2 = skb;
+ } else {
+ skb2 = skb_clone(skb, GFP_ATOMIC);
+ if (skb2 == NULL)
+ goto err;
+ }
+
+ if (!skb_pull(skb2, index)) {
+ ret = -EOVERFLOW;
+ goto err;
+ }
+
+ skb_trim(skb2, dg_len - crc_len);
+ skb_queue_tail(list, skb2);
+
+ ndp_len -= 2 * (opts->dgram_item_len * 2);
+
+ dgram_counter++;
+
+ if (index2 == 0 || dg_len2 == 0)
+ break;
+ } while (ndp_len > 2 * (opts->dgram_item_len * 2)); /* zero entry */
+
+ VDBG(port->func.config->cdev,
+ "Parsed NTB with %d frames\n", dgram_counter);
+ return 0;
+err:
+ skb_queue_purge(list);
+ dev_kfree_skb_any(skb);
+ return ret;
+}
+
+static void ncm_disable(struct usb_function *f)
+{
+ struct f_ncm *ncm = func_to_ncm(f);
+ struct usb_composite_dev *cdev = f->config->cdev;
+
+ DBG(cdev, "ncm deactivated\n");
+
+ if (ncm->port.in_ep->driver_data)
+ gether_disconnect(&ncm->port);
+
+ if (ncm->notify->driver_data) {
+ usb_ep_disable(ncm->notify);
+ ncm->notify->driver_data = NULL;
+ ncm->notify_desc = NULL;
+ }
+}
+
+/*-------------------------------------------------------------------------*/
+
+/*
+ * Callbacks let us notify the host about connect/disconnect when the
+ * net device is opened or closed.
+ *
+ * For testing, note that link states on this side include both opened
+ * and closed variants of:
+ *
+ * - disconnected/unconfigured
+ * - configured but inactive (data alt 0)
+ * - configured and active (data alt 1)
+ *
+ * Each needs to be tested with unplug, rmmod, SET_CONFIGURATION, and
+ * SET_INTERFACE (altsetting). Remember also that "configured" doesn't
+ * imply the host is actually polling the notification endpoint, and
+ * likewise that "active" doesn't imply it's actually using the data
+ * endpoints for traffic.
+ */
+
+static void ncm_open(struct gether *geth)
+{
+ struct f_ncm *ncm = func_to_ncm(&geth->func);
+
+ DBG(ncm->port.func.config->cdev, "%s\n", __func__);
+
+ spin_lock(&ncm->lock);
+ ncm->is_open = true;
+ ncm_notify(ncm);
+ spin_unlock(&ncm->lock);
+}
+
+static void ncm_close(struct gether *geth)
+{
+ struct f_ncm *ncm = func_to_ncm(&geth->func);
+
+ DBG(ncm->port.func.config->cdev, "%s\n", __func__);
+
+ spin_lock(&ncm->lock);
+ ncm->is_open = false;
+ ncm_notify(ncm);
+ spin_unlock(&ncm->lock);
+}
+
+/*-------------------------------------------------------------------------*/
+
+/* ethernet function driver setup/binding */
+
+static int __init
+ncm_bind(struct usb_configuration *c, struct usb_function *f)
+{
+ struct usb_composite_dev *cdev = c->cdev;
+ struct f_ncm *ncm = func_to_ncm(f);
+ int status;
+ struct usb_ep *ep;
+
+ /* allocate instance-specific interface IDs */
+ status = usb_interface_id(c, f);
+ if (status < 0)
+ goto fail;
+ ncm->ctrl_id = status;
+ ncm_iad_desc.bFirstInterface = status;
+
+ ncm_control_intf.bInterfaceNumber = status;
+ ncm_union_desc.bMasterInterface0 = status;
+
+ status = usb_interface_id(c, f);
+ if (status < 0)
+ goto fail;
+ ncm->data_id = status;
+
+ ncm_data_nop_intf.bInterfaceNumber = status;
+ ncm_data_intf.bInterfaceNumber = status;
+ ncm_union_desc.bSlaveInterface0 = status;
+
+ status = -ENODEV;
+
+ /* allocate instance-specific endpoints */
+ ep = usb_ep_autoconfig(cdev->gadget, &fs_ncm_in_desc);
+ if (!ep)
+ goto fail;
+ ncm->port.in_ep = ep;
+ ep->driver_data = cdev; /* claim */
+
+ ep = usb_ep_autoconfig(cdev->gadget, &fs_ncm_out_desc);
+ if (!ep)
+ goto fail;
+ ncm->port.out_ep = ep;
+ ep->driver_data = cdev; /* claim */
+
+ ep = usb_ep_autoconfig(cdev->gadget, &fs_ncm_notify_desc);
+ if (!ep)
+ goto fail;
+ ncm->notify = ep;
+ ep->driver_data = cdev; /* claim */
+
+ status = -ENOMEM;
+
+ /* allocate notification request and buffer */
+ ncm->notify_req = usb_ep_alloc_request(ep, GFP_KERNEL);
+ if (!ncm->notify_req)
+ goto fail;
+ ncm->notify_req->buf = kmalloc(NCM_STATUS_BYTECOUNT, GFP_KERNEL);
+ if (!ncm->notify_req->buf)
+ goto fail;
+ ncm->notify_req->context = ncm;
+ ncm->notify_req->complete = ncm_notify_complete;
+
+ /* copy descriptors, and track endpoint copies */
+ f->descriptors = usb_copy_descriptors(ncm_fs_function);
+ if (!f->descriptors)
+ goto fail;
+
+ ncm->fs.in = usb_find_endpoint(ncm_fs_function,
+ f->descriptors, &fs_ncm_in_desc);
+ ncm->fs.out = usb_find_endpoint(ncm_fs_function,
+ f->descriptors, &fs_ncm_out_desc);
+ ncm->fs.notify = usb_find_endpoint(ncm_fs_function,
+ f->descriptors, &fs_ncm_notify_desc);
+
+ /*
+ * support all relevant hardware speeds... we expect that when
+ * hardware is dual speed, all bulk-capable endpoints work at
+ * both speeds
+ */
+ if (gadget_is_dualspeed(c->cdev->gadget)) {
+ hs_ncm_in_desc.bEndpointAddress =
+ fs_ncm_in_desc.bEndpointAddress;
+ hs_ncm_out_desc.bEndpointAddress =
+ fs_ncm_out_desc.bEndpointAddress;
+ hs_ncm_notify_desc.bEndpointAddress =
+ fs_ncm_notify_desc.bEndpointAddress;
+
+ /* copy descriptors, and track endpoint copies */
+ f->hs_descriptors = usb_copy_descriptors(ncm_hs_function);
+ if (!f->hs_descriptors)
+ goto fail;
+
+ ncm->hs.in = usb_find_endpoint(ncm_hs_function,
+ f->hs_descriptors, &hs_ncm_in_desc);
+ ncm->hs.out = usb_find_endpoint(ncm_hs_function,
+ f->hs_descriptors, &hs_ncm_out_desc);
+ ncm->hs.notify = usb_find_endpoint(ncm_hs_function,
+ f->hs_descriptors, &hs_ncm_notify_desc);
+ }
+
+ /*
+ * NOTE: all that is done without knowing or caring about
+ * the network link ... which is unavailable to this code
+ * until we're activated via set_alt().
+ */
+
+ ncm->port.open = ncm_open;
+ ncm->port.close = ncm_close;
+
+ DBG(cdev, "CDC Network: %s speed IN/%s OUT/%s NOTIFY/%s\n",
+ gadget_is_dualspeed(c->cdev->gadget) ? "dual" : "full",
+ ncm->port.in_ep->name, ncm->port.out_ep->name,
+ ncm->notify->name);
+ return 0;
+
+fail:
+ if (f->descriptors)
+ usb_free_descriptors(f->descriptors);
+
+ if (ncm->notify_req) {
+ kfree(ncm->notify_req->buf);
+ usb_ep_free_request(ncm->notify, ncm->notify_req);
+ }
+
+ /* we might as well release our claims on endpoints */
+ if (ncm->notify)
+ ncm->notify->driver_data = NULL;
+ if (ncm->port.out)
+ ncm->port.out_ep->driver_data = NULL;
+ if (ncm->port.in)
+ ncm->port.in_ep->driver_data = NULL;
+
+ ERROR(cdev, "%s: can't bind, err %d\n", f->name, status);
+
+ return status;
+}
+
+static void
+ncm_unbind(struct usb_configuration *c, struct usb_function *f)
+{
+ struct f_ncm *ncm = func_to_ncm(f);
+
+ DBG(c->cdev, "ncm unbind\n");
+
+ if (gadget_is_dualspeed(c->cdev->gadget))
+ usb_free_descriptors(f->hs_descriptors);
+ usb_free_descriptors(f->descriptors);
+
+ kfree(ncm->notify_req->buf);
+ usb_ep_free_request(ncm->notify, ncm->notify_req);
+
+ ncm_string_defs[1].s = NULL;
+ kfree(ncm);
+}
+
+/**
+ * ncm_bind_config - add CDC Network link to a configuration
+ * @c: the configuration to support the network link
+ * @ethaddr: a buffer in which the ethernet address of the host side
+ * side of the link was recorded
+ * Context: single threaded during gadget setup
+ *
+ * Returns zero on success, else negative errno.
+ *
+ * Caller must have called @gether_setup(). Caller is also responsible
+ * for calling @gether_cleanup() before module unload.
+ */
+int __init ncm_bind_config(struct usb_configuration *c, u8 ethaddr[ETH_ALEN])
+{
+ struct f_ncm *ncm;
+ int status;
+
+ if (!can_support_ecm(c->cdev->gadget) || !ethaddr)
+ return -EINVAL;
+
+ /* maybe allocate device-global string IDs */
+ if (ncm_string_defs[0].id == 0) {
+
+ /* control interface label */
+ status = usb_string_id(c->cdev);
+ if (status < 0)
+ return status;
+ ncm_string_defs[STRING_CTRL_IDX].id = status;
+ ncm_control_intf.iInterface = status;
+
+ /* data interface label */
+ status = usb_string_id(c->cdev);
+ if (status < 0)
+ return status;
+ ncm_string_defs[STRING_DATA_IDX].id = status;
+ ncm_data_nop_intf.iInterface = status;
+ ncm_data_intf.iInterface = status;
+
+ /* MAC address */
+ status = usb_string_id(c->cdev);
+ if (status < 0)
+ return status;
+ ncm_string_defs[STRING_MAC_IDX].id = status;
+ ecm_desc.iMACAddress = status;
+
+ /* IAD */
+ status = usb_string_id(c->cdev);
+ if (status < 0)
+ return status;
+ ncm_string_defs[STRING_IAD_IDX].id = status;
+ ncm_iad_desc.iFunction = status;
+ }
+
+ /* allocate and initialize one new instance */
+ ncm = kzalloc(sizeof *ncm, GFP_KERNEL);
+ if (!ncm)
+ return -ENOMEM;
+
+ /* export host's Ethernet address in CDC format */
+ snprintf(ncm->ethaddr, sizeof ncm->ethaddr,
+ "%02X%02X%02X%02X%02X%02X",
+ ethaddr[0], ethaddr[1], ethaddr[2],
+ ethaddr[3], ethaddr[4], ethaddr[5]);
+ ncm_string_defs[1].s = ncm->ethaddr;
+
+ spin_lock_init(&ncm->lock);
+ ncm_reset_values(ncm);
+ ncm->port.is_fixed = true;
+
+ ncm->port.func.name = "cdc_network";
+ ncm->port.func.strings = ncm_strings;
+ /* descriptors are per-instance copies */
+ ncm->port.func.bind = ncm_bind;
+ ncm->port.func.unbind = ncm_unbind;
+ ncm->port.func.set_alt = ncm_set_alt;
+ ncm->port.func.get_alt = ncm_get_alt;
+ ncm->port.func.setup = ncm_setup;
+ ncm->port.func.disable = ncm_disable;
+
+ ncm->port.wrap = ncm_wrap_ntb;
+ ncm->port.unwrap = ncm_unwrap_ntb;
+
+ status = usb_add_function(c, &ncm->port.func);
+ if (status) {
+ ncm_string_defs[1].s = NULL;
+ kfree(ncm);
+ }
+ return status;
+}
diff --git a/drivers/usb/gadget/file_storage.c b/drivers/usb/gadget/file_storage.c
index d4fdf65fb92..a6eacb59571 100644
--- a/drivers/usb/gadget/file_storage.c
+++ b/drivers/usb/gadget/file_storage.c
@@ -3392,25 +3392,28 @@ static int __init fsg_bind(struct usb_gadget *gadget)
dev_set_name(&curlun->dev,"%s-lun%d",
dev_name(&gadget->dev), i);
- if ((rc = device_register(&curlun->dev)) != 0) {
+ kref_get(&fsg->ref);
+ rc = device_register(&curlun->dev);
+ if (rc) {
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);
+ put_device(&curlun->dev);
goto out;
}
curlun->registered = 1;
- kref_get(&fsg->ref);
+
+ rc = device_create_file(&curlun->dev, &dev_attr_ro);
+ if (rc)
+ goto out;
+ rc = device_create_file(&curlun->dev, &dev_attr_nofua);
+ if (rc)
+ goto out;
+ rc = device_create_file(&curlun->dev, &dev_attr_file);
+ if (rc)
+ goto out;
if (mod_data.file[i] && *mod_data.file[i]) {
- if ((rc = fsg_lun_open(curlun,
- mod_data.file[i])) != 0)
+ rc = fsg_lun_open(curlun, mod_data.file[i]);
+ if (rc)
goto out;
} else if (!mod_data.removable) {
ERROR(fsg, "no file given for LUN%d\n", i);
diff --git a/drivers/usb/gadget/g_ffs.c b/drivers/usb/gadget/g_ffs.c
index af75e362084..ebf6970a10b 100644
--- a/drivers/usb/gadget/g_ffs.c
+++ b/drivers/usb/gadget/g_ffs.c
@@ -1,7 +1,29 @@
+/*
+ * g_ffs.c -- user mode file system API for USB composite function controllers
+ *
+ * Copyright (C) 2010 Samsung Electronics
+ * Author: Michal Nazarewicz <m.nazarewicz@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
+ */
+
+#define pr_fmt(fmt) "g_ffs: " fmt
+
#include <linux/module.h>
#include <linux/utsname.h>
-
/*
* kbuild is not very cooperative with respect to linking separately
* compiled library objects into one module. So for now we won't use
@@ -43,7 +65,6 @@ static int eth_bind_config(struct usb_configuration *c, u8 ethaddr[ETH_ALEN]);
#include "f_fs.c"
-
#define DRIVER_NAME "g_ffs"
#define DRIVER_DESC "USB Function Filesystem"
#define DRIVER_VERSION "24 Aug 2004"
@@ -73,8 +94,6 @@ MODULE_PARM_DESC(bDeviceSubClass, "USB Device subclass");
module_param_named(bDeviceProtocol, gfs_dev_desc.bDeviceProtocol, byte, 0644);
MODULE_PARM_DESC(bDeviceProtocol, "USB Device protocol");
-
-
static const struct usb_descriptor_header *gfs_otg_desc[] = {
(const struct usb_descriptor_header *)
&(const struct usb_otg_descriptor) {
@@ -91,8 +110,7 @@ static const struct usb_descriptor_header *gfs_otg_desc[] = {
NULL
};
-/* string IDs are assigned dynamically */
-
+/* String IDs are assigned dynamically */
static struct usb_string gfs_strings[] = {
#ifdef CONFIG_USB_FUNCTIONFS_RNDIS
{ .s = "FunctionFS + RNDIS" },
@@ -114,8 +132,6 @@ static struct usb_gadget_strings *gfs_dev_strings[] = {
NULL,
};
-
-
struct gfs_configuration {
struct usb_configuration c;
int (*eth)(struct usb_configuration *c, u8 *ethaddr);
@@ -138,7 +154,6 @@ struct gfs_configuration {
#endif
};
-
static int gfs_bind(struct usb_composite_dev *cdev);
static int gfs_unbind(struct usb_composite_dev *cdev);
static int gfs_do_config(struct usb_configuration *c);
@@ -151,11 +166,9 @@ static struct usb_composite_driver gfs_driver = {
.iProduct = DRIVER_DESC,
};
-
static struct ffs_data *gfs_ffs_data;
static unsigned long gfs_registered;
-
static int gfs_init(void)
{
ENTER();
@@ -175,7 +188,6 @@ static void gfs_exit(void)
}
module_exit(gfs_exit);
-
static int functionfs_ready_callback(struct ffs_data *ffs)
{
int ret;
@@ -200,14 +212,11 @@ static void functionfs_closed_callback(struct ffs_data *ffs)
usb_composite_unregister(&gfs_driver);
}
-
static int functionfs_check_dev_callback(const char *dev_name)
{
return 0;
}
-
-
static int gfs_bind(struct usb_composite_dev *cdev)
{
int ret, i;
@@ -274,7 +283,6 @@ static int gfs_unbind(struct usb_composite_dev *cdev)
return 0;
}
-
static int gfs_do_config(struct usb_configuration *c)
{
struct gfs_configuration *gc =
@@ -315,7 +323,6 @@ static int gfs_do_config(struct usb_configuration *c)
return 0;
}
-
#ifdef CONFIG_USB_FUNCTIONFS_ETH
static int eth_bind_config(struct usb_configuration *c, u8 ethaddr[ETH_ALEN])
diff --git a/drivers/usb/gadget/gadget_chips.h b/drivers/usb/gadget/gadget_chips.h
index e511fec9f26..5c2720d64ff 100644
--- a/drivers/usb/gadget/gadget_chips.h
+++ b/drivers/usb/gadget/gadget_chips.h
@@ -96,7 +96,7 @@
/* Mentor high speed "dual role" controller, in peripheral role */
#ifdef CONFIG_USB_GADGET_MUSB_HDRC
-#define gadget_is_musbhdrc(g) !strcmp("musb_hdrc", (g)->name)
+#define gadget_is_musbhdrc(g) !strcmp("musb-hdrc", (g)->name)
#else
#define gadget_is_musbhdrc(g) 0
#endif
@@ -120,10 +120,10 @@
#define gadget_is_fsl_qe(g) 0
#endif
-#ifdef CONFIG_USB_GADGET_CI13XXX
-#define gadget_is_ci13xxx(g) (!strcmp("ci13xxx_udc", (g)->name))
+#ifdef CONFIG_USB_GADGET_CI13XXX_PCI
+#define gadget_is_ci13xxx_pci(g) (!strcmp("ci13xxx_pci", (g)->name))
#else
-#define gadget_is_ci13xxx(g) 0
+#define gadget_is_ci13xxx_pci(g) 0
#endif
// CONFIG_USB_GADGET_SX2
@@ -142,6 +142,17 @@
#define gadget_is_s3c_hsotg(g) 0
#endif
+#ifdef CONFIG_USB_GADGET_EG20T
+#define gadget_is_pch(g) (!strcmp("pch_udc", (g)->name))
+#else
+#define gadget_is_pch(g) 0
+#endif
+
+#ifdef CONFIG_USB_GADGET_CI13XXX_MSM
+#define gadget_is_ci13xxx_msm(g) (!strcmp("ci13xxx_msm", (g)->name))
+#else
+#define gadget_is_ci13xxx_msm(g) 0
+#endif
/**
* usb_gadget_controller_number - support bcdDevice id convention
@@ -192,7 +203,7 @@ static inline int usb_gadget_controller_number(struct usb_gadget *gadget)
return 0x21;
else if (gadget_is_fsl_qe(gadget))
return 0x22;
- else if (gadget_is_ci13xxx(gadget))
+ else if (gadget_is_ci13xxx_pci(gadget))
return 0x23;
else if (gadget_is_langwell(gadget))
return 0x24;
@@ -200,6 +211,10 @@ static inline int usb_gadget_controller_number(struct usb_gadget *gadget)
return 0x25;
else if (gadget_is_s3c_hsotg(gadget))
return 0x26;
+ else if (gadget_is_pch(gadget))
+ return 0x27;
+ else if (gadget_is_ci13xxx_msm(gadget))
+ return 0x28;
return -ENOENT;
}
diff --git a/drivers/usb/gadget/imx_udc.c b/drivers/usb/gadget/imx_udc.c
index ed0266462c5..1210534822d 100644
--- a/drivers/usb/gadget/imx_udc.c
+++ b/drivers/usb/gadget/imx_udc.c
@@ -1191,13 +1191,17 @@ static irqreturn_t imx_udc_ctrl_irq(int irq, void *dev)
return IRQ_HANDLED;
}
+#ifndef MX1_INT_USBD0
+#define MX1_INT_USBD0 MX1_USBD_INT0
+#endif
+
static irqreturn_t imx_udc_bulk_irq(int irq, void *dev)
{
struct imx_udc_struct *imx_usb = dev;
- struct imx_ep_struct *imx_ep = &imx_usb->imx_ep[irq - USBD_INT0];
+ struct imx_ep_struct *imx_ep = &imx_usb->imx_ep[irq - MX1_INT_USBD0];
int intr = __raw_readl(imx_usb->base + USB_EP_INTR(EP_NO(imx_ep)));
- dump_ep_intr(__func__, irq - USBD_INT0, intr, imx_usb->dev);
+ dump_ep_intr(__func__, irq - MX1_INT_USBD0, intr, imx_usb->dev);
if (!imx_usb->driver) {
__raw_writel(intr, imx_usb->base + USB_EP_INTR(EP_NO(imx_ep)));
diff --git a/drivers/usb/gadget/imx_udc.h b/drivers/usb/gadget/imx_udc.h
index b48ad59603d..7136c242b4e 100644
--- a/drivers/usb/gadget/imx_udc.h
+++ b/drivers/usb/gadget/imx_udc.h
@@ -23,9 +23,6 @@
/* Helper macros */
#define EP_NO(ep) ((ep->bEndpointAddress) & ~USB_DIR_IN) /* IN:1, OUT:0 */
#define EP_DIR(ep) ((ep->bEndpointAddress) & USB_DIR_IN ? 1 : 0)
-#define irq_to_ep(irq) (((irq) >= USBD_INT0) || ((irq) <= USBD_INT6) \
- ? ((irq) - USBD_INT0) : (USBD_INT6)) /*should not happen*/
-#define ep_to_irq(ep) (EP_NO((ep)) + USBD_INT0)
#define IMX_USB_NB_EP 6
/* Driver structures */
diff --git a/drivers/usb/gadget/langwell_udc.c b/drivers/usb/gadget/langwell_udc.c
index b8ec954c069..777972454e3 100644
--- a/drivers/usb/gadget/langwell_udc.c
+++ b/drivers/usb/gadget/langwell_udc.c
@@ -2225,6 +2225,7 @@ static void handle_setup_packet(struct langwell_udc *dev,
u16 wValue = le16_to_cpu(setup->wValue);
u16 wIndex = le16_to_cpu(setup->wIndex);
u16 wLength = le16_to_cpu(setup->wLength);
+ u32 portsc1;
dev_vdbg(&dev->pdev->dev, "---> %s()\n", __func__);
@@ -2313,6 +2314,28 @@ static void handle_setup_packet(struct langwell_udc *dev,
dev->dev_status &= ~(1 << wValue);
}
break;
+ case USB_DEVICE_TEST_MODE:
+ dev_dbg(&dev->pdev->dev, "SETUP: TEST MODE\n");
+ if ((wIndex & 0xff) ||
+ (dev->gadget.speed != USB_SPEED_HIGH))
+ ep0_stall(dev);
+
+ switch (wIndex >> 8) {
+ case TEST_J:
+ case TEST_K:
+ case TEST_SE0_NAK:
+ case TEST_PACKET:
+ case TEST_FORCE_EN:
+ if (prime_status_phase(dev, EP_DIR_IN))
+ ep0_stall(dev);
+ portsc1 = readl(&dev->op_regs->portsc1);
+ portsc1 |= (wIndex & 0xf00) << 8;
+ writel(portsc1, &dev->op_regs->portsc1);
+ goto end;
+ default:
+ rc = -EOPNOTSUPP;
+ }
+ break;
default:
rc = -EOPNOTSUPP;
break;
diff --git a/drivers/usb/gadget/mass_storage.c b/drivers/usb/gadget/mass_storage.c
index 0769179dbdb..01822422c3e 100644
--- a/drivers/usb/gadget/mass_storage.c
+++ b/drivers/usb/gadget/mass_storage.c
@@ -102,7 +102,7 @@ static struct fsg_module_parameters mod_data = {
};
FSG_MODULE_PARAMETERS(/* no prefix */, mod_data);
-static unsigned long msg_registered = 0;
+static unsigned long msg_registered;
static void msg_cleanup(void);
static int msg_thread_exits(struct fsg_common *common)
diff --git a/drivers/usb/gadget/mv_udc.h b/drivers/usb/gadget/mv_udc.h
new file mode 100644
index 00000000000..65f1f7c3bd4
--- /dev/null
+++ b/drivers/usb/gadget/mv_udc.h
@@ -0,0 +1,294 @@
+
+#ifndef __MV_UDC_H
+#define __MV_UDC_H
+
+#define VUSBHS_MAX_PORTS 8
+
+#define DQH_ALIGNMENT 2048
+#define DTD_ALIGNMENT 64
+#define DMA_BOUNDARY 4096
+
+#define EP_DIR_IN 1
+#define EP_DIR_OUT 0
+
+#define DMA_ADDR_INVALID (~(dma_addr_t)0)
+
+#define EP0_MAX_PKT_SIZE 64
+/* ep0 transfer state */
+#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 CAPLENGTH_MASK (0xff)
+#define DCCPARAMS_DEN_MASK (0x1f)
+
+#define HCSPARAMS_PPC (0x10)
+
+/* Frame Index Register Bit Masks */
+#define USB_FRINDEX_MASKS 0x3fff
+
+/* Command Register Bit Masks */
+#define USBCMD_RUN_STOP (0x00000001)
+#define USBCMD_CTRL_RESET (0x00000002)
+#define USBCMD_SETUP_TRIPWIRE_SET (0x00002000)
+#define USBCMD_SETUP_TRIPWIRE_CLEAR (~USBCMD_SETUP_TRIPWIRE_SET)
+
+#define USBCMD_ATDTW_TRIPWIRE_SET (0x00004000)
+#define USBCMD_ATDTW_TRIPWIRE_CLEAR (~USBCMD_ATDTW_TRIPWIRE_SET)
+
+/* bit 15,3,2 are for frame list size */
+#define USBCMD_FRAME_SIZE_1024 (0x00000000) /* 000 */
+#define USBCMD_FRAME_SIZE_512 (0x00000004) /* 001 */
+#define USBCMD_FRAME_SIZE_256 (0x00000008) /* 010 */
+#define USBCMD_FRAME_SIZE_128 (0x0000000C) /* 011 */
+#define USBCMD_FRAME_SIZE_64 (0x00008000) /* 100 */
+#define USBCMD_FRAME_SIZE_32 (0x00008004) /* 101 */
+#define USBCMD_FRAME_SIZE_16 (0x00008008) /* 110 */
+#define USBCMD_FRAME_SIZE_8 (0x0000800C) /* 111 */
+
+#define EPCTRL_TX_ALL_MASK (0xFFFF0000)
+#define EPCTRL_RX_ALL_MASK (0x0000FFFF)
+
+#define EPCTRL_TX_DATA_TOGGLE_RST (0x00400000)
+#define EPCTRL_TX_EP_STALL (0x00010000)
+#define EPCTRL_RX_EP_STALL (0x00000001)
+#define EPCTRL_RX_DATA_TOGGLE_RST (0x00000040)
+#define EPCTRL_RX_ENABLE (0x00000080)
+#define EPCTRL_TX_ENABLE (0x00800000)
+#define EPCTRL_CONTROL (0x00000000)
+#define EPCTRL_ISOCHRONOUS (0x00040000)
+#define EPCTRL_BULK (0x00080000)
+#define EPCTRL_INT (0x000C0000)
+#define EPCTRL_TX_TYPE (0x000C0000)
+#define EPCTRL_RX_TYPE (0x0000000C)
+#define EPCTRL_DATA_TOGGLE_INHIBIT (0x00000020)
+#define EPCTRL_TX_EP_TYPE_SHIFT (18)
+#define EPCTRL_RX_EP_TYPE_SHIFT (2)
+
+#define EPCOMPLETE_MAX_ENDPOINTS (16)
+
+/* endpoint list address bit masks */
+#define USB_EP_LIST_ADDRESS_MASK 0xfffff800
+
+#define PORTSCX_W1C_BITS 0x2a
+#define PORTSCX_PORT_RESET 0x00000100
+#define PORTSCX_PORT_POWER 0x00001000
+#define PORTSCX_FORCE_FULL_SPEED_CONNECT 0x01000000
+#define PORTSCX_PAR_XCVR_SELECT 0xC0000000
+#define PORTSCX_PORT_FORCE_RESUME 0x00000040
+#define PORTSCX_PORT_SUSPEND 0x00000080
+#define PORTSCX_PORT_SPEED_FULL 0x00000000
+#define PORTSCX_PORT_SPEED_LOW 0x04000000
+#define PORTSCX_PORT_SPEED_HIGH 0x08000000
+#define PORTSCX_PORT_SPEED_MASK 0x0C000000
+
+/* USB MODE Register Bit Masks */
+#define USBMODE_CTRL_MODE_IDLE 0x00000000
+#define USBMODE_CTRL_MODE_DEVICE 0x00000002
+#define USBMODE_CTRL_MODE_HOST 0x00000003
+#define USBMODE_CTRL_MODE_RSV 0x00000001
+#define USBMODE_SETUP_LOCK_OFF 0x00000008
+#define USBMODE_STREAM_DISABLE 0x00000010
+
+/* USB STS Register Bit Masks */
+#define USBSTS_INT 0x00000001
+#define USBSTS_ERR 0x00000002
+#define USBSTS_PORT_CHANGE 0x00000004
+#define USBSTS_FRM_LST_ROLL 0x00000008
+#define USBSTS_SYS_ERR 0x00000010
+#define USBSTS_IAA 0x00000020
+#define USBSTS_RESET 0x00000040
+#define USBSTS_SOF 0x00000080
+#define USBSTS_SUSPEND 0x00000100
+#define USBSTS_HC_HALTED 0x00001000
+#define USBSTS_RCL 0x00002000
+#define USBSTS_PERIODIC_SCHEDULE 0x00004000
+#define USBSTS_ASYNC_SCHEDULE 0x00008000
+
+
+/* Interrupt Enable Register Bit Masks */
+#define USBINTR_INT_EN (0x00000001)
+#define USBINTR_ERR_INT_EN (0x00000002)
+#define USBINTR_PORT_CHANGE_DETECT_EN (0x00000004)
+
+#define USBINTR_ASYNC_ADV_AAE (0x00000020)
+#define USBINTR_ASYNC_ADV_AAE_ENABLE (0x00000020)
+#define USBINTR_ASYNC_ADV_AAE_DISABLE (0xFFFFFFDF)
+
+#define USBINTR_RESET_EN (0x00000040)
+#define USBINTR_SOF_UFRAME_EN (0x00000080)
+#define USBINTR_DEVICE_SUSPEND (0x00000100)
+
+#define USB_DEVICE_ADDRESS_MASK (0xfe000000)
+#define USB_DEVICE_ADDRESS_BIT_SHIFT (25)
+
+struct mv_cap_regs {
+ u32 caplength_hciversion;
+ u32 hcsparams; /* HC structural parameters */
+ u32 hccparams; /* HC Capability Parameters*/
+ u32 reserved[5];
+ u32 dciversion; /* DC version number and reserved 16 bits */
+ u32 dccparams; /* DC Capability Parameters */
+};
+
+struct mv_op_regs {
+ u32 usbcmd; /* Command register */
+ u32 usbsts; /* Status register */
+ u32 usbintr; /* Interrupt enable */
+ u32 frindex; /* Frame index */
+ u32 reserved1[1];
+ u32 deviceaddr; /* Device Address */
+ u32 eplistaddr; /* Endpoint List Address */
+ u32 ttctrl; /* HOST TT status and control */
+ u32 burstsize; /* Programmable Burst Size */
+ u32 txfilltuning; /* Host Transmit Pre-Buffer Packet Tuning */
+ u32 reserved[4];
+ u32 epnak; /* Endpoint NAK */
+ u32 epnaken; /* Endpoint NAK Enable */
+ u32 configflag; /* Configured Flag register */
+ u32 portsc[VUSBHS_MAX_PORTS]; /* Port Status/Control x, x = 1..8 */
+ u32 otgsc;
+ u32 usbmode; /* USB Host/Device mode */
+ u32 epsetupstat; /* Endpoint Setup Status */
+ u32 epprime; /* Endpoint Initialize */
+ u32 epflush; /* Endpoint De-initialize */
+ u32 epstatus; /* Endpoint Status */
+ u32 epcomplete; /* Endpoint Interrupt On Complete */
+ u32 epctrlx[16]; /* Endpoint Control, where x = 0.. 15 */
+ u32 mcr; /* Mux Control */
+ u32 isr; /* Interrupt Status */
+ u32 ier; /* Interrupt Enable */
+};
+
+struct mv_udc {
+ struct usb_gadget gadget;
+ struct usb_gadget_driver *driver;
+ spinlock_t lock;
+ struct completion *done;
+ struct platform_device *dev;
+ int irq;
+
+ struct mv_cap_regs __iomem *cap_regs;
+ struct mv_op_regs __iomem *op_regs;
+ unsigned int phy_regs;
+ unsigned int max_eps;
+ struct mv_dqh *ep_dqh;
+ size_t ep_dqh_size;
+ dma_addr_t ep_dqh_dma;
+
+ struct dma_pool *dtd_pool;
+ struct mv_ep *eps;
+
+ struct mv_dtd *dtd_head;
+ struct mv_dtd *dtd_tail;
+ unsigned int dtd_entries;
+
+ struct mv_req *status_req;
+ struct usb_ctrlrequest local_setup_buff;
+
+ unsigned int resume_state; /* USB state to resume */
+ unsigned int usb_state; /* USB current state */
+ unsigned int ep0_state; /* Endpoint zero state */
+ unsigned int ep0_dir;
+
+ unsigned int dev_addr;
+
+ int errors;
+ unsigned softconnect:1,
+ vbus_active:1,
+ remote_wakeup:1,
+ softconnected:1,
+ force_fs:1;
+ struct clk *clk;
+};
+
+/* endpoint data structure */
+struct mv_ep {
+ struct usb_ep ep;
+ struct mv_udc *udc;
+ struct list_head queue;
+ struct mv_dqh *dqh;
+ const struct usb_endpoint_descriptor *desc;
+ u32 direction;
+ char name[14];
+ unsigned stopped:1,
+ wedge:1,
+ ep_type:2,
+ ep_num:8;
+};
+
+/* request data structure */
+struct mv_req {
+ struct usb_request req;
+ struct mv_dtd *dtd, *head, *tail;
+ struct mv_ep *ep;
+ struct list_head queue;
+ unsigned dtd_count;
+ unsigned mapped:1;
+};
+
+#define EP_QUEUE_HEAD_MULT_POS 30
+#define EP_QUEUE_HEAD_ZLT_SEL 0x20000000
+#define EP_QUEUE_HEAD_MAX_PKT_LEN_POS 16
+#define EP_QUEUE_HEAD_MAX_PKT_LEN(ep_info) (((ep_info)>>16)&0x07ff)
+#define EP_QUEUE_HEAD_IOS 0x00008000
+#define EP_QUEUE_HEAD_NEXT_TERMINATE 0x00000001
+#define EP_QUEUE_HEAD_IOC 0x00008000
+#define EP_QUEUE_HEAD_MULTO 0x00000C00
+#define EP_QUEUE_HEAD_STATUS_HALT 0x00000040
+#define EP_QUEUE_HEAD_STATUS_ACTIVE 0x00000080
+#define EP_QUEUE_CURRENT_OFFSET_MASK 0x00000FFF
+#define EP_QUEUE_HEAD_NEXT_POINTER_MASK 0xFFFFFFE0
+#define EP_QUEUE_FRINDEX_MASK 0x000007FF
+#define EP_MAX_LENGTH_TRANSFER 0x4000
+
+struct mv_dqh {
+ /* Bits 16..26 Bit 15 is Interrupt On Setup */
+ u32 max_packet_length;
+ u32 curr_dtd_ptr; /* Current dTD Pointer */
+ u32 next_dtd_ptr; /* Next dTD Pointer */
+ /* Total bytes (16..30), IOC (15), INT (8), STS (0-7) */
+ u32 size_ioc_int_sts;
+ u32 buff_ptr0; /* Buffer pointer Page 0 (12-31) */
+ u32 buff_ptr1; /* Buffer pointer Page 1 (12-31) */
+ u32 buff_ptr2; /* Buffer pointer Page 2 (12-31) */
+ u32 buff_ptr3; /* Buffer pointer Page 3 (12-31) */
+ u32 buff_ptr4; /* Buffer pointer Page 4 (12-31) */
+ u32 reserved1;
+ /* 8 bytes of setup data that follows the Setup PID */
+ u8 setup_buffer[8];
+ u32 reserved2[4];
+};
+
+
+#define DTD_NEXT_TERMINATE (0x00000001)
+#define DTD_IOC (0x00008000)
+#define DTD_STATUS_ACTIVE (0x00000080)
+#define DTD_STATUS_HALTED (0x00000040)
+#define DTD_STATUS_DATA_BUFF_ERR (0x00000020)
+#define DTD_STATUS_TRANSACTION_ERR (0x00000008)
+#define DTD_RESERVED_FIELDS (0x00007F00)
+#define DTD_ERROR_MASK (0x68)
+#define DTD_ADDR_MASK (0xFFFFFFE0)
+#define DTD_PACKET_SIZE 0x7FFF0000
+#define DTD_LENGTH_BIT_POS (16)
+
+struct mv_dtd {
+ u32 dtd_next;
+ u32 size_ioc_sts;
+ u32 buff_ptr0; /* Buffer pointer Page 0 */
+ u32 buff_ptr1; /* Buffer pointer Page 1 */
+ u32 buff_ptr2; /* Buffer pointer Page 2 */
+ u32 buff_ptr3; /* Buffer pointer Page 3 */
+ u32 buff_ptr4; /* Buffer pointer Page 4 */
+ u32 scratch_ptr;
+ /* 32 bytes */
+ dma_addr_t td_dma; /* dma address for this td */
+ struct mv_dtd *next_dtd_virt;
+};
+
+extern int mv_udc_phy_init(unsigned int base);
+
+#endif
diff --git a/drivers/usb/gadget/mv_udc_core.c b/drivers/usb/gadget/mv_udc_core.c
new file mode 100644
index 00000000000..d5468a7f38e
--- /dev/null
+++ b/drivers/usb/gadget/mv_udc_core.c
@@ -0,0 +1,2149 @@
+#include <linux/module.h>
+#include <linux/pci.h>
+#include <linux/dma-mapping.h>
+#include <linux/dmapool.h>
+#include <linux/kernel.h>
+#include <linux/delay.h>
+#include <linux/ioport.h>
+#include <linux/sched.h>
+#include <linux/slab.h>
+#include <linux/errno.h>
+#include <linux/init.h>
+#include <linux/timer.h>
+#include <linux/list.h>
+#include <linux/interrupt.h>
+#include <linux/moduleparam.h>
+#include <linux/device.h>
+#include <linux/usb/ch9.h>
+#include <linux/usb/gadget.h>
+#include <linux/usb/otg.h>
+#include <linux/pm.h>
+#include <linux/io.h>
+#include <linux/irq.h>
+#include <linux/platform_device.h>
+#include <linux/clk.h>
+#include <asm/system.h>
+#include <asm/unaligned.h>
+
+#include "mv_udc.h"
+
+#define DRIVER_DESC "Marvell PXA USB Device Controller driver"
+#define DRIVER_VERSION "8 Nov 2010"
+
+#define ep_dir(ep) (((ep)->ep_num == 0) ? \
+ ((ep)->udc->ep0_dir) : ((ep)->direction))
+
+/* timeout value -- usec */
+#define RESET_TIMEOUT 10000
+#define FLUSH_TIMEOUT 10000
+#define EPSTATUS_TIMEOUT 10000
+#define PRIME_TIMEOUT 10000
+#define READSAFE_TIMEOUT 1000
+#define DTD_TIMEOUT 1000
+
+#define LOOPS_USEC_SHIFT 4
+#define LOOPS_USEC (1 << LOOPS_USEC_SHIFT)
+#define LOOPS(timeout) ((timeout) >> LOOPS_USEC_SHIFT)
+
+static const char driver_name[] = "mv_udc";
+static const char driver_desc[] = DRIVER_DESC;
+
+/* controller device global variable */
+static struct mv_udc *the_controller;
+int mv_usb_otgsc;
+
+static void nuke(struct mv_ep *ep, int status);
+
+/* for endpoint 0 operations */
+static const struct usb_endpoint_descriptor mv_ep0_desc = {
+ .bLength = USB_DT_ENDPOINT_SIZE,
+ .bDescriptorType = USB_DT_ENDPOINT,
+ .bEndpointAddress = 0,
+ .bmAttributes = USB_ENDPOINT_XFER_CONTROL,
+ .wMaxPacketSize = EP0_MAX_PKT_SIZE,
+};
+
+static void ep0_reset(struct mv_udc *udc)
+{
+ struct mv_ep *ep;
+ u32 epctrlx;
+ int i = 0;
+
+ /* ep0 in and out */
+ for (i = 0; i < 2; i++) {
+ ep = &udc->eps[i];
+ ep->udc = udc;
+
+ /* ep0 dQH */
+ ep->dqh = &udc->ep_dqh[i];
+
+ /* configure ep0 endpoint capabilities in dQH */
+ ep->dqh->max_packet_length =
+ (EP0_MAX_PKT_SIZE << EP_QUEUE_HEAD_MAX_PKT_LEN_POS)
+ | EP_QUEUE_HEAD_IOS;
+
+ epctrlx = readl(&udc->op_regs->epctrlx[0]);
+ if (i) { /* TX */
+ epctrlx |= EPCTRL_TX_ENABLE | EPCTRL_TX_DATA_TOGGLE_RST
+ | (USB_ENDPOINT_XFER_CONTROL
+ << EPCTRL_TX_EP_TYPE_SHIFT);
+
+ } else { /* RX */
+ epctrlx |= EPCTRL_RX_ENABLE | EPCTRL_RX_DATA_TOGGLE_RST
+ | (USB_ENDPOINT_XFER_CONTROL
+ << EPCTRL_RX_EP_TYPE_SHIFT);
+ }
+
+ writel(epctrlx, &udc->op_regs->epctrlx[0]);
+ }
+}
+
+/* protocol ep0 stall, will automatically be cleared on new transaction */
+static void ep0_stall(struct mv_udc *udc)
+{
+ u32 epctrlx;
+
+ /* set TX and RX to stall */
+ epctrlx = readl(&udc->op_regs->epctrlx[0]);
+ epctrlx |= EPCTRL_RX_EP_STALL | EPCTRL_TX_EP_STALL;
+ writel(epctrlx, &udc->op_regs->epctrlx[0]);
+
+ /* update ep0 state */
+ udc->ep0_state = WAIT_FOR_SETUP;
+ udc->ep0_dir = EP_DIR_OUT;
+}
+
+static int process_ep_req(struct mv_udc *udc, int index,
+ struct mv_req *curr_req)
+{
+ struct mv_dtd *curr_dtd;
+ struct mv_dqh *curr_dqh;
+ int td_complete, actual, remaining_length;
+ int i, direction;
+ int retval = 0;
+ u32 errors;
+
+ curr_dqh = &udc->ep_dqh[index];
+ direction = index % 2;
+
+ curr_dtd = curr_req->head;
+ td_complete = 0;
+ actual = curr_req->req.length;
+
+ for (i = 0; i < curr_req->dtd_count; i++) {
+ if (curr_dtd->size_ioc_sts & DTD_STATUS_ACTIVE) {
+ dev_dbg(&udc->dev->dev, "%s, dTD not completed\n",
+ udc->eps[index].name);
+ return 1;
+ }
+
+ errors = curr_dtd->size_ioc_sts & DTD_ERROR_MASK;
+ if (!errors) {
+ remaining_length +=
+ (curr_dtd->size_ioc_sts & DTD_PACKET_SIZE)
+ >> DTD_LENGTH_BIT_POS;
+ actual -= remaining_length;
+ } else {
+ dev_info(&udc->dev->dev,
+ "complete_tr error: ep=%d %s: error = 0x%x\n",
+ index >> 1, direction ? "SEND" : "RECV",
+ errors);
+ if (errors & DTD_STATUS_HALTED) {
+ /* Clear the errors and Halt condition */
+ curr_dqh->size_ioc_int_sts &= ~errors;
+ retval = -EPIPE;
+ } else if (errors & DTD_STATUS_DATA_BUFF_ERR) {
+ retval = -EPROTO;
+ } else if (errors & DTD_STATUS_TRANSACTION_ERR) {
+ retval = -EILSEQ;
+ }
+ }
+ if (i != curr_req->dtd_count - 1)
+ curr_dtd = (struct mv_dtd *)curr_dtd->next_dtd_virt;
+ }
+ if (retval)
+ return retval;
+
+ curr_req->req.actual = actual;
+
+ return 0;
+}
+
+/*
+ * done() - retire a request; caller blocked irqs
+ * @status : request status to be set, only works when
+ * request is still in progress.
+ */
+static void done(struct mv_ep *ep, struct mv_req *req, int status)
+{
+ struct mv_udc *udc = NULL;
+ unsigned char stopped = ep->stopped;
+ struct mv_dtd *curr_td, *next_td;
+ int j;
+
+ udc = (struct mv_udc *)ep->udc;
+ /* Removed the req from fsl_ep->queue */
+ list_del_init(&req->queue);
+
+ /* req.status should be set as -EINPROGRESS in ep_queue() */
+ if (req->req.status == -EINPROGRESS)
+ req->req.status = status;
+ else
+ status = req->req.status;
+
+ /* Free dtd for the request */
+ next_td = req->head;
+ for (j = 0; j < req->dtd_count; j++) {
+ curr_td = next_td;
+ if (j != req->dtd_count - 1)
+ next_td = curr_td->next_dtd_virt;
+ dma_pool_free(udc->dtd_pool, curr_td, curr_td->td_dma);
+ }
+
+ if (req->mapped) {
+ dma_unmap_single(ep->udc->gadget.dev.parent,
+ req->req.dma, req->req.length,
+ ((ep_dir(ep) == EP_DIR_IN) ?
+ DMA_TO_DEVICE : DMA_FROM_DEVICE));
+ req->req.dma = DMA_ADDR_INVALID;
+ req->mapped = 0;
+ } else
+ dma_sync_single_for_cpu(ep->udc->gadget.dev.parent,
+ req->req.dma, req->req.length,
+ ((ep_dir(ep) == EP_DIR_IN) ?
+ DMA_TO_DEVICE : DMA_FROM_DEVICE));
+
+ if (status && (status != -ESHUTDOWN))
+ dev_info(&udc->dev->dev, "complete %s req %p stat %d len %u/%u",
+ ep->ep.name, &req->req, status,
+ req->req.actual, req->req.length);
+
+ ep->stopped = 1;
+
+ spin_unlock(&ep->udc->lock);
+ /*
+ * complete() is from gadget layer,
+ * eg fsg->bulk_in_complete()
+ */
+ if (req->req.complete)
+ req->req.complete(&ep->ep, &req->req);
+
+ spin_lock(&ep->udc->lock);
+ ep->stopped = stopped;
+}
+
+static int queue_dtd(struct mv_ep *ep, struct mv_req *req)
+{
+ u32 tmp, epstatus, bit_pos, direction;
+ struct mv_udc *udc;
+ struct mv_dqh *dqh;
+ unsigned int loops;
+ int readsafe, retval = 0;
+
+ udc = ep->udc;
+ direction = ep_dir(ep);
+ dqh = &(udc->ep_dqh[ep->ep_num * 2 + direction]);
+ bit_pos = 1 << (((direction == EP_DIR_OUT) ? 0 : 16) + ep->ep_num);
+
+ /* check if the pipe is empty */
+ if (!(list_empty(&ep->queue))) {
+ struct mv_req *lastreq;
+ lastreq = list_entry(ep->queue.prev, struct mv_req, queue);
+ lastreq->tail->dtd_next =
+ req->head->td_dma & EP_QUEUE_HEAD_NEXT_POINTER_MASK;
+ if (readl(&udc->op_regs->epprime) & bit_pos) {
+ loops = LOOPS(PRIME_TIMEOUT);
+ while (readl(&udc->op_regs->epprime) & bit_pos) {
+ if (loops == 0) {
+ retval = -ETIME;
+ goto done;
+ }
+ udelay(LOOPS_USEC);
+ loops--;
+ }
+ if (readl(&udc->op_regs->epstatus) & bit_pos)
+ goto done;
+ }
+ readsafe = 0;
+ loops = LOOPS(READSAFE_TIMEOUT);
+ while (readsafe == 0) {
+ if (loops == 0) {
+ retval = -ETIME;
+ goto done;
+ }
+ /* start with setting the semaphores */
+ tmp = readl(&udc->op_regs->usbcmd);
+ tmp |= USBCMD_ATDTW_TRIPWIRE_SET;
+ writel(tmp, &udc->op_regs->usbcmd);
+
+ /* read the endpoint status */
+ epstatus = readl(&udc->op_regs->epstatus) & bit_pos;
+
+ /*
+ * Reread the ATDTW semaphore bit to check if it is
+ * cleared. When hardware see a hazard, it will clear
+ * the bit or else we remain set to 1 and we can
+ * proceed with priming of endpoint if not already
+ * primed.
+ */
+ if (readl(&udc->op_regs->usbcmd)
+ & USBCMD_ATDTW_TRIPWIRE_SET) {
+ readsafe = 1;
+ }
+ loops--;
+ udelay(LOOPS_USEC);
+ }
+
+ /* Clear the semaphore */
+ tmp = readl(&udc->op_regs->usbcmd);
+ tmp &= USBCMD_ATDTW_TRIPWIRE_CLEAR;
+ writel(tmp, &udc->op_regs->usbcmd);
+
+ /* If endpoint is not active, we activate it now. */
+ if (!epstatus) {
+ if (direction == EP_DIR_IN) {
+ struct mv_dtd *curr_dtd = dma_to_virt(
+ &udc->dev->dev, dqh->curr_dtd_ptr);
+
+ loops = LOOPS(DTD_TIMEOUT);
+ while (curr_dtd->size_ioc_sts
+ & DTD_STATUS_ACTIVE) {
+ if (loops == 0) {
+ retval = -ETIME;
+ goto done;
+ }
+ loops--;
+ udelay(LOOPS_USEC);
+ }
+ }
+ /* No other transfers on the queue */
+
+ /* Write dQH next pointer and terminate bit to 0 */
+ dqh->next_dtd_ptr = req->head->td_dma
+ & EP_QUEUE_HEAD_NEXT_POINTER_MASK;
+ dqh->size_ioc_int_sts = 0;
+
+ /*
+ * Ensure that updates to the QH will
+ * occure before priming.
+ */
+ wmb();
+
+ /* Prime the Endpoint */
+ writel(bit_pos, &udc->op_regs->epprime);
+ }
+ } else {
+ /* Write dQH next pointer and terminate bit to 0 */
+ dqh->next_dtd_ptr = req->head->td_dma
+ & EP_QUEUE_HEAD_NEXT_POINTER_MASK;;
+ dqh->size_ioc_int_sts = 0;
+
+ /* Ensure that updates to the QH will occure before priming. */
+ wmb();
+
+ /* Prime the Endpoint */
+ writel(bit_pos, &udc->op_regs->epprime);
+
+ if (direction == EP_DIR_IN) {
+ /* FIXME add status check after prime the IN ep */
+ int prime_again;
+ u32 curr_dtd_ptr = dqh->curr_dtd_ptr;
+
+ loops = LOOPS(DTD_TIMEOUT);
+ prime_again = 0;
+ while ((curr_dtd_ptr != req->head->td_dma)) {
+ curr_dtd_ptr = dqh->curr_dtd_ptr;
+ if (loops == 0) {
+ dev_err(&udc->dev->dev,
+ "failed to prime %s\n",
+ ep->name);
+ retval = -ETIME;
+ goto done;
+ }
+ loops--;
+ udelay(LOOPS_USEC);
+
+ if (loops == (LOOPS(DTD_TIMEOUT) >> 2)) {
+ if (prime_again)
+ goto done;
+ dev_info(&udc->dev->dev,
+ "prime again\n");
+ writel(bit_pos,
+ &udc->op_regs->epprime);
+ prime_again = 1;
+ }
+ }
+ }
+ }
+done:
+ return retval;;
+}
+
+static struct mv_dtd *build_dtd(struct mv_req *req, unsigned *length,
+ dma_addr_t *dma, int *is_last)
+{
+ u32 temp;
+ struct mv_dtd *dtd;
+ struct mv_udc *udc;
+
+ /* how big will this transfer be? */
+ *length = min(req->req.length - req->req.actual,
+ (unsigned)EP_MAX_LENGTH_TRANSFER);
+
+ udc = req->ep->udc;
+
+ /*
+ * Be careful that no _GFP_HIGHMEM is set,
+ * or we can not use dma_to_virt
+ */
+ dtd = dma_pool_alloc(udc->dtd_pool, GFP_KERNEL, dma);
+ if (dtd == NULL)
+ return dtd;
+
+ dtd->td_dma = *dma;
+ /* initialize buffer page pointers */
+ temp = (u32)(req->req.dma + req->req.actual);
+ dtd->buff_ptr0 = cpu_to_le32(temp);
+ temp &= ~0xFFF;
+ dtd->buff_ptr1 = cpu_to_le32(temp + 0x1000);
+ dtd->buff_ptr2 = cpu_to_le32(temp + 0x2000);
+ dtd->buff_ptr3 = cpu_to_le32(temp + 0x3000);
+ dtd->buff_ptr4 = cpu_to_le32(temp + 0x4000);
+
+ req->req.actual += *length;
+
+ /* zlp is needed if req->req.zero is set */
+ if (req->req.zero) {
+ if (*length == 0 || (*length % req->ep->ep.maxpacket) != 0)
+ *is_last = 1;
+ else
+ *is_last = 0;
+ } else if (req->req.length == req->req.actual)
+ *is_last = 1;
+ else
+ *is_last = 0;
+
+ /* Fill in the transfer size; set active bit */
+ temp = ((*length << DTD_LENGTH_BIT_POS) | DTD_STATUS_ACTIVE);
+
+ /* Enable interrupt for the last dtd of a request */
+ if (*is_last && !req->req.no_interrupt)
+ temp |= DTD_IOC;
+
+ dtd->size_ioc_sts = temp;
+
+ mb();
+
+ return dtd;
+}
+
+/* generate dTD linked list for a request */
+static int req_to_dtd(struct mv_req *req)
+{
+ unsigned count;
+ int is_last, is_first = 1;
+ struct mv_dtd *dtd, *last_dtd = NULL;
+ struct mv_udc *udc;
+ dma_addr_t dma;
+
+ udc = req->ep->udc;
+
+ do {
+ dtd = build_dtd(req, &count, &dma, &is_last);
+ if (dtd == NULL)
+ return -ENOMEM;
+
+ if (is_first) {
+ is_first = 0;
+ req->head = dtd;
+ } else {
+ last_dtd->dtd_next = dma;
+ last_dtd->next_dtd_virt = dtd;
+ }
+ last_dtd = dtd;
+ req->dtd_count++;
+ } while (!is_last);
+
+ /* set terminate bit to 1 for the last dTD */
+ dtd->dtd_next = DTD_NEXT_TERMINATE;
+
+ req->tail = dtd;
+
+ return 0;
+}
+
+static int mv_ep_enable(struct usb_ep *_ep,
+ const struct usb_endpoint_descriptor *desc)
+{
+ struct mv_udc *udc;
+ struct mv_ep *ep;
+ struct mv_dqh *dqh;
+ u16 max = 0;
+ u32 bit_pos, epctrlx, direction;
+ unsigned char zlt = 0, ios = 0, mult = 0;
+
+ ep = container_of(_ep, struct mv_ep, ep);
+ udc = ep->udc;
+
+ if (!_ep || !desc || ep->desc
+ || desc->bDescriptorType != USB_DT_ENDPOINT)
+ return -EINVAL;
+
+ if (!udc->driver || udc->gadget.speed == USB_SPEED_UNKNOWN)
+ return -ESHUTDOWN;
+
+ direction = ep_dir(ep);
+ max = le16_to_cpu(desc->wMaxPacketSize);
+
+ /*
+ * disable HW zero length termination select
+ * driver handles zero length packet through req->req.zero
+ */
+ zlt = 1;
+
+ /* Get the endpoint queue head address */
+ dqh = (struct mv_dqh *)ep->dqh;
+
+ bit_pos = 1 << ((direction == EP_DIR_OUT ? 0 : 16) + ep->ep_num);
+
+ /* Check if the Endpoint is Primed */
+ if ((readl(&udc->op_regs->epprime) & bit_pos)
+ || (readl(&udc->op_regs->epstatus) & bit_pos)) {
+ dev_info(&udc->dev->dev,
+ "ep=%d %s: Init ERROR: ENDPTPRIME=0x%x,"
+ " ENDPTSTATUS=0x%x, bit_pos=0x%x\n",
+ (unsigned)ep->ep_num, direction ? "SEND" : "RECV",
+ (unsigned)readl(&udc->op_regs->epprime),
+ (unsigned)readl(&udc->op_regs->epstatus),
+ (unsigned)bit_pos);
+ goto en_done;
+ }
+ /* Set the max packet length, interrupt on Setup and Mult fields */
+ switch (desc->bmAttributes & USB_ENDPOINT_XFERTYPE_MASK) {
+ case USB_ENDPOINT_XFER_BULK:
+ zlt = 1;
+ mult = 0;
+ break;
+ case USB_ENDPOINT_XFER_CONTROL:
+ ios = 1;
+ case USB_ENDPOINT_XFER_INT:
+ mult = 0;
+ break;
+ case USB_ENDPOINT_XFER_ISOC:
+ /* Calculate transactions needed for high bandwidth iso */
+ mult = (unsigned char)(1 + ((max >> 11) & 0x03));
+ max = max & 0x8ff; /* bit 0~10 */
+ /* 3 transactions at most */
+ if (mult > 3)
+ goto en_done;
+ break;
+ default:
+ goto en_done;
+ }
+ dqh->max_packet_length = (max << EP_QUEUE_HEAD_MAX_PKT_LEN_POS)
+ | (mult << EP_QUEUE_HEAD_MULT_POS)
+ | (zlt ? EP_QUEUE_HEAD_ZLT_SEL : 0)
+ | (ios ? EP_QUEUE_HEAD_IOS : 0);
+ dqh->next_dtd_ptr = 1;
+ dqh->size_ioc_int_sts = 0;
+
+ ep->ep.maxpacket = max;
+ ep->desc = desc;
+ ep->stopped = 0;
+
+ /* Enable the endpoint for Rx or Tx and set the endpoint type */
+ epctrlx = readl(&udc->op_regs->epctrlx[ep->ep_num]);
+ if (direction == EP_DIR_IN) {
+ epctrlx &= ~EPCTRL_TX_ALL_MASK;
+ epctrlx |= EPCTRL_TX_ENABLE | EPCTRL_TX_DATA_TOGGLE_RST
+ | ((desc->bmAttributes & USB_ENDPOINT_XFERTYPE_MASK)
+ << EPCTRL_TX_EP_TYPE_SHIFT);
+ } else {
+ epctrlx &= ~EPCTRL_RX_ALL_MASK;
+ epctrlx |= EPCTRL_RX_ENABLE | EPCTRL_RX_DATA_TOGGLE_RST
+ | ((desc->bmAttributes & USB_ENDPOINT_XFERTYPE_MASK)
+ << EPCTRL_RX_EP_TYPE_SHIFT);
+ }
+ writel(epctrlx, &udc->op_regs->epctrlx[ep->ep_num]);
+
+ /*
+ * Implement Guideline (GL# USB-7) The unused endpoint type must
+ * be programmed to bulk.
+ */
+ epctrlx = readl(&udc->op_regs->epctrlx[ep->ep_num]);
+ if ((epctrlx & EPCTRL_RX_ENABLE) == 0) {
+ epctrlx |= ((desc->bmAttributes & USB_ENDPOINT_XFERTYPE_MASK)
+ << EPCTRL_RX_EP_TYPE_SHIFT);
+ writel(epctrlx, &udc->op_regs->epctrlx[ep->ep_num]);
+ }
+
+ epctrlx = readl(&udc->op_regs->epctrlx[ep->ep_num]);
+ if ((epctrlx & EPCTRL_TX_ENABLE) == 0) {
+ epctrlx |= ((desc->bmAttributes & USB_ENDPOINT_XFERTYPE_MASK)
+ << EPCTRL_TX_EP_TYPE_SHIFT);
+ writel(epctrlx, &udc->op_regs->epctrlx[ep->ep_num]);
+ }
+
+ return 0;
+en_done:
+ return -EINVAL;
+}
+
+static int mv_ep_disable(struct usb_ep *_ep)
+{
+ struct mv_udc *udc;
+ struct mv_ep *ep;
+ struct mv_dqh *dqh;
+ u32 bit_pos, epctrlx, direction;
+
+ ep = container_of(_ep, struct mv_ep, ep);
+ if ((_ep == NULL) || !ep->desc)
+ return -EINVAL;
+
+ udc = ep->udc;
+
+ /* Get the endpoint queue head address */
+ dqh = ep->dqh;
+
+ direction = ep_dir(ep);
+ bit_pos = 1 << ((direction == EP_DIR_OUT ? 0 : 16) + ep->ep_num);
+
+ /* Reset the max packet length and the interrupt on Setup */
+ dqh->max_packet_length = 0;
+
+ /* Disable the endpoint for Rx or Tx and reset the endpoint type */
+ epctrlx = readl(&udc->op_regs->epctrlx[ep->ep_num]);
+ epctrlx &= ~((direction == EP_DIR_IN)
+ ? (EPCTRL_TX_ENABLE | EPCTRL_TX_TYPE)
+ : (EPCTRL_RX_ENABLE | EPCTRL_RX_TYPE));
+ writel(epctrlx, &udc->op_regs->epctrlx[ep->ep_num]);
+
+ /* nuke all pending requests (does flush) */
+ nuke(ep, -ESHUTDOWN);
+
+ ep->desc = NULL;
+ ep->stopped = 1;
+ return 0;
+}
+
+static struct usb_request *
+mv_alloc_request(struct usb_ep *_ep, gfp_t gfp_flags)
+{
+ struct mv_req *req = NULL;
+
+ req = kzalloc(sizeof *req, gfp_flags);
+ if (!req)
+ return NULL;
+
+ req->req.dma = DMA_ADDR_INVALID;
+ INIT_LIST_HEAD(&req->queue);
+
+ return &req->req;
+}
+
+static void mv_free_request(struct usb_ep *_ep, struct usb_request *_req)
+{
+ struct mv_req *req = NULL;
+
+ req = container_of(_req, struct mv_req, req);
+
+ if (_req)
+ kfree(req);
+}
+
+static void mv_ep_fifo_flush(struct usb_ep *_ep)
+{
+ struct mv_udc *udc;
+ u32 bit_pos, direction;
+ struct mv_ep *ep = container_of(_ep, struct mv_ep, ep);
+ unsigned int loops;
+
+ udc = ep->udc;
+ direction = ep_dir(ep);
+ bit_pos = 1 << ((direction == EP_DIR_OUT ? 0 : 16) + ep->ep_num);
+ /*
+ * Flushing will halt the pipe
+ * Write 1 to the Flush register
+ */
+ writel(bit_pos, &udc->op_regs->epflush);
+
+ /* Wait until flushing completed */
+ loops = LOOPS(FLUSH_TIMEOUT);
+ while (readl(&udc->op_regs->epflush) & bit_pos) {
+ /*
+ * ENDPTFLUSH bit should be cleared to indicate this
+ * operation is complete
+ */
+ if (loops == 0) {
+ dev_err(&udc->dev->dev,
+ "TIMEOUT for ENDPTFLUSH=0x%x, bit_pos=0x%x\n",
+ (unsigned)readl(&udc->op_regs->epflush),
+ (unsigned)bit_pos);
+ return;
+ }
+ loops--;
+ udelay(LOOPS_USEC);
+ }
+ loops = LOOPS(EPSTATUS_TIMEOUT);
+ while (readl(&udc->op_regs->epstatus) & bit_pos) {
+ unsigned int inter_loops;
+
+ if (loops == 0) {
+ dev_err(&udc->dev->dev,
+ "TIMEOUT for ENDPTSTATUS=0x%x, bit_pos=0x%x\n",
+ (unsigned)readl(&udc->op_regs->epstatus),
+ (unsigned)bit_pos);
+ return;
+ }
+ /* Write 1 to the Flush register */
+ writel(bit_pos, &udc->op_regs->epflush);
+
+ /* Wait until flushing completed */
+ inter_loops = LOOPS(FLUSH_TIMEOUT);
+ while (readl(&udc->op_regs->epflush) & bit_pos) {
+ /*
+ * ENDPTFLUSH bit should be cleared to indicate this
+ * operation is complete
+ */
+ if (inter_loops == 0) {
+ dev_err(&udc->dev->dev,
+ "TIMEOUT for ENDPTFLUSH=0x%x,"
+ "bit_pos=0x%x\n",
+ (unsigned)readl(&udc->op_regs->epflush),
+ (unsigned)bit_pos);
+ return;
+ }
+ inter_loops--;
+ udelay(LOOPS_USEC);
+ }
+ loops--;
+ }
+}
+
+/* queues (submits) an I/O request to an endpoint */
+static int
+mv_ep_queue(struct usb_ep *_ep, struct usb_request *_req, gfp_t gfp_flags)
+{
+ struct mv_ep *ep = container_of(_ep, struct mv_ep, ep);
+ struct mv_req *req = container_of(_req, struct mv_req, req);
+ struct mv_udc *udc = ep->udc;
+ unsigned long flags;
+
+ /* catch various bogus parameters */
+ if (!_req || !req->req.complete || !req->req.buf
+ || !list_empty(&req->queue)) {
+ dev_err(&udc->dev->dev, "%s, bad params", __func__);
+ return -EINVAL;
+ }
+ if (unlikely(!_ep || !ep->desc)) {
+ dev_err(&udc->dev->dev, "%s, bad ep", __func__);
+ return -EINVAL;
+ }
+ if (ep->desc->bmAttributes == USB_ENDPOINT_XFER_ISOC) {
+ if (req->req.length > ep->ep.maxpacket)
+ return -EMSGSIZE;
+ }
+
+ udc = ep->udc;
+ if (!udc->driver || udc->gadget.speed == USB_SPEED_UNKNOWN)
+ return -ESHUTDOWN;
+
+ req->ep = ep;
+
+ /* map virtual address to hardware */
+ if (req->req.dma == DMA_ADDR_INVALID) {
+ req->req.dma = dma_map_single(ep->udc->gadget.dev.parent,
+ req->req.buf,
+ req->req.length, ep_dir(ep)
+ ? DMA_TO_DEVICE
+ : DMA_FROM_DEVICE);
+ req->mapped = 1;
+ } else {
+ dma_sync_single_for_device(ep->udc->gadget.dev.parent,
+ req->req.dma, req->req.length,
+ ep_dir(ep)
+ ? DMA_TO_DEVICE
+ : DMA_FROM_DEVICE);
+ req->mapped = 0;
+ }
+
+ req->req.status = -EINPROGRESS;
+ req->req.actual = 0;
+ req->dtd_count = 0;
+
+ spin_lock_irqsave(&udc->lock, flags);
+
+ /* build dtds and push them to device queue */
+ if (!req_to_dtd(req)) {
+ int retval;
+ retval = queue_dtd(ep, req);
+ if (retval) {
+ spin_unlock_irqrestore(&udc->lock, flags);
+ return retval;
+ }
+ } else {
+ spin_unlock_irqrestore(&udc->lock, flags);
+ return -ENOMEM;
+ }
+
+ /* Update ep0 state */
+ if (ep->ep_num == 0)
+ udc->ep0_state = DATA_STATE_XMIT;
+
+ /* irq handler advances the queue */
+ if (req != NULL)
+ list_add_tail(&req->queue, &ep->queue);
+ spin_unlock_irqrestore(&udc->lock, flags);
+
+ return 0;
+}
+
+/* dequeues (cancels, unlinks) an I/O request from an endpoint */
+static int mv_ep_dequeue(struct usb_ep *_ep, struct usb_request *_req)
+{
+ struct mv_ep *ep = container_of(_ep, struct mv_ep, ep);
+ struct mv_req *req;
+ struct mv_udc *udc = ep->udc;
+ unsigned long flags;
+ int stopped, ret = 0;
+ u32 epctrlx;
+
+ if (!_ep || !_req)
+ return -EINVAL;
+
+ spin_lock_irqsave(&ep->udc->lock, flags);
+ stopped = ep->stopped;
+
+ /* Stop the ep before we deal with the queue */
+ ep->stopped = 1;
+ epctrlx = readl(&udc->op_regs->epctrlx[ep->ep_num]);
+ if (ep_dir(ep) == EP_DIR_IN)
+ epctrlx &= ~EPCTRL_TX_ENABLE;
+ else
+ epctrlx &= ~EPCTRL_RX_ENABLE;
+ writel(epctrlx, &udc->op_regs->epctrlx[ep->ep_num]);
+
+ /* 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) {
+ ret = -EINVAL;
+ goto out;
+ }
+
+ /* The request is in progress, or completed but not dequeued */
+ if (ep->queue.next == &req->queue) {
+ _req->status = -ECONNRESET;
+ mv_ep_fifo_flush(_ep); /* flush current transfer */
+
+ /* The request isn't the last request in this ep queue */
+ if (req->queue.next != &ep->queue) {
+ struct mv_dqh *qh;
+ struct mv_req *next_req;
+
+ qh = ep->dqh;
+ next_req = list_entry(req->queue.next, struct mv_req,
+ queue);
+
+ /* Point the QH to the first TD of next request */
+ writel((u32) next_req->head, &qh->curr_dtd_ptr);
+ } else {
+ struct mv_dqh *qh;
+
+ qh = ep->dqh;
+ qh->next_dtd_ptr = 1;
+ qh->size_ioc_int_sts = 0;
+ }
+
+ /* The request hasn't been processed, patch up the TD chain */
+ } else {
+ struct mv_req *prev_req;
+
+ prev_req = list_entry(req->queue.prev, struct mv_req, queue);
+ writel(readl(&req->tail->dtd_next),
+ &prev_req->tail->dtd_next);
+
+ }
+
+ done(ep, req, -ECONNRESET);
+
+ /* Enable EP */
+out:
+ epctrlx = readl(&udc->op_regs->epctrlx[ep->ep_num]);
+ if (ep_dir(ep) == EP_DIR_IN)
+ epctrlx |= EPCTRL_TX_ENABLE;
+ else
+ epctrlx |= EPCTRL_RX_ENABLE;
+ writel(epctrlx, &udc->op_regs->epctrlx[ep->ep_num]);
+ ep->stopped = stopped;
+
+ spin_unlock_irqrestore(&ep->udc->lock, flags);
+ return ret;
+}
+
+static void ep_set_stall(struct mv_udc *udc, u8 ep_num, u8 direction, int stall)
+{
+ u32 epctrlx;
+
+ epctrlx = readl(&udc->op_regs->epctrlx[ep_num]);
+
+ if (stall) {
+ if (direction == EP_DIR_IN)
+ epctrlx |= EPCTRL_TX_EP_STALL;
+ else
+ epctrlx |= EPCTRL_RX_EP_STALL;
+ } else {
+ if (direction == EP_DIR_IN) {
+ epctrlx &= ~EPCTRL_TX_EP_STALL;
+ epctrlx |= EPCTRL_TX_DATA_TOGGLE_RST;
+ } else {
+ epctrlx &= ~EPCTRL_RX_EP_STALL;
+ epctrlx |= EPCTRL_RX_DATA_TOGGLE_RST;
+ }
+ }
+ writel(epctrlx, &udc->op_regs->epctrlx[ep_num]);
+}
+
+static int ep_is_stall(struct mv_udc *udc, u8 ep_num, u8 direction)
+{
+ u32 epctrlx;
+
+ epctrlx = readl(&udc->op_regs->epctrlx[ep_num]);
+
+ if (direction == EP_DIR_OUT)
+ return (epctrlx & EPCTRL_RX_EP_STALL) ? 1 : 0;
+ else
+ return (epctrlx & EPCTRL_TX_EP_STALL) ? 1 : 0;
+}
+
+static int mv_ep_set_halt_wedge(struct usb_ep *_ep, int halt, int wedge)
+{
+ struct mv_ep *ep;
+ unsigned long flags = 0;
+ int status = 0;
+ struct mv_udc *udc;
+
+ ep = container_of(_ep, struct mv_ep, ep);
+ udc = ep->udc;
+ if (!_ep || !ep->desc) {
+ status = -EINVAL;
+ goto out;
+ }
+
+ if (ep->desc->bmAttributes == USB_ENDPOINT_XFER_ISOC) {
+ status = -EOPNOTSUPP;
+ goto out;
+ }
+
+ /*
+ * Attempt to halt IN ep will fail if any transfer requests
+ * are still queue
+ */
+ if (halt && (ep_dir(ep) == EP_DIR_IN) && !list_empty(&ep->queue)) {
+ status = -EAGAIN;
+ goto out;
+ }
+
+ spin_lock_irqsave(&ep->udc->lock, flags);
+ ep_set_stall(udc, ep->ep_num, ep_dir(ep), halt);
+ if (halt && wedge)
+ ep->wedge = 1;
+ else if (!halt)
+ ep->wedge = 0;
+ spin_unlock_irqrestore(&ep->udc->lock, flags);
+
+ if (ep->ep_num == 0) {
+ udc->ep0_state = WAIT_FOR_SETUP;
+ udc->ep0_dir = EP_DIR_OUT;
+ }
+out:
+ return status;
+}
+
+static int mv_ep_set_halt(struct usb_ep *_ep, int halt)
+{
+ return mv_ep_set_halt_wedge(_ep, halt, 0);
+}
+
+static int mv_ep_set_wedge(struct usb_ep *_ep)
+{
+ return mv_ep_set_halt_wedge(_ep, 1, 1);
+}
+
+static struct usb_ep_ops mv_ep_ops = {
+ .enable = mv_ep_enable,
+ .disable = mv_ep_disable,
+
+ .alloc_request = mv_alloc_request,
+ .free_request = mv_free_request,
+
+ .queue = mv_ep_queue,
+ .dequeue = mv_ep_dequeue,
+
+ .set_wedge = mv_ep_set_wedge,
+ .set_halt = mv_ep_set_halt,
+ .fifo_flush = mv_ep_fifo_flush, /* flush fifo */
+};
+
+static void udc_stop(struct mv_udc *udc)
+{
+ u32 tmp;
+
+ /* Disable interrupts */
+ tmp = readl(&udc->op_regs->usbintr);
+ tmp &= ~(USBINTR_INT_EN | USBINTR_ERR_INT_EN |
+ USBINTR_PORT_CHANGE_DETECT_EN | USBINTR_RESET_EN);
+ writel(tmp, &udc->op_regs->usbintr);
+
+ /* Reset the Run the bit in the command register to stop VUSB */
+ tmp = readl(&udc->op_regs->usbcmd);
+ tmp &= ~USBCMD_RUN_STOP;
+ writel(tmp, &udc->op_regs->usbcmd);
+}
+
+static void udc_start(struct mv_udc *udc)
+{
+ u32 usbintr;
+
+ usbintr = USBINTR_INT_EN | USBINTR_ERR_INT_EN
+ | USBINTR_PORT_CHANGE_DETECT_EN
+ | USBINTR_RESET_EN | USBINTR_DEVICE_SUSPEND;
+ /* Enable interrupts */
+ writel(usbintr, &udc->op_regs->usbintr);
+
+ /* Set the Run bit in the command register */
+ writel(USBCMD_RUN_STOP, &udc->op_regs->usbcmd);
+}
+
+static int udc_reset(struct mv_udc *udc)
+{
+ unsigned int loops;
+ u32 tmp, portsc;
+
+ /* Stop the controller */
+ tmp = readl(&udc->op_regs->usbcmd);
+ tmp &= ~USBCMD_RUN_STOP;
+ writel(tmp, &udc->op_regs->usbcmd);
+
+ /* Reset the controller to get default values */
+ writel(USBCMD_CTRL_RESET, &udc->op_regs->usbcmd);
+
+ /* wait for reset to complete */
+ loops = LOOPS(RESET_TIMEOUT);
+ while (readl(&udc->op_regs->usbcmd) & USBCMD_CTRL_RESET) {
+ if (loops == 0) {
+ dev_err(&udc->dev->dev,
+ "Wait for RESET completed TIMEOUT\n");
+ return -ETIMEDOUT;
+ }
+ loops--;
+ udelay(LOOPS_USEC);
+ }
+
+ /* set controller to device mode */
+ tmp = readl(&udc->op_regs->usbmode);
+ tmp |= USBMODE_CTRL_MODE_DEVICE;
+
+ /* turn setup lockout off, require setup tripwire in usbcmd */
+ tmp |= USBMODE_SETUP_LOCK_OFF | USBMODE_STREAM_DISABLE;
+
+ writel(tmp, &udc->op_regs->usbmode);
+
+ writel(0x0, &udc->op_regs->epsetupstat);
+
+ /* Configure the Endpoint List Address */
+ writel(udc->ep_dqh_dma & USB_EP_LIST_ADDRESS_MASK,
+ &udc->op_regs->eplistaddr);
+
+ portsc = readl(&udc->op_regs->portsc[0]);
+ if (readl(&udc->cap_regs->hcsparams) & HCSPARAMS_PPC)
+ portsc &= (~PORTSCX_W1C_BITS | ~PORTSCX_PORT_POWER);
+
+ if (udc->force_fs)
+ portsc |= PORTSCX_FORCE_FULL_SPEED_CONNECT;
+ else
+ portsc &= (~PORTSCX_FORCE_FULL_SPEED_CONNECT);
+
+ writel(portsc, &udc->op_regs->portsc[0]);
+
+ tmp = readl(&udc->op_regs->epctrlx[0]);
+ tmp &= ~(EPCTRL_TX_EP_STALL | EPCTRL_RX_EP_STALL);
+ writel(tmp, &udc->op_regs->epctrlx[0]);
+
+ return 0;
+}
+
+static int mv_udc_get_frame(struct usb_gadget *gadget)
+{
+ struct mv_udc *udc;
+ u16 retval;
+
+ if (!gadget)
+ return -ENODEV;
+
+ udc = container_of(gadget, struct mv_udc, gadget);
+
+ retval = readl(udc->op_regs->frindex) & USB_FRINDEX_MASKS;
+
+ return retval;
+}
+
+/* Tries to wake up the host connected to this gadget */
+static int mv_udc_wakeup(struct usb_gadget *gadget)
+{
+ struct mv_udc *udc = container_of(gadget, struct mv_udc, gadget);
+ u32 portsc;
+
+ /* Remote wakeup feature not enabled by host */
+ if (!udc->remote_wakeup)
+ return -ENOTSUPP;
+
+ portsc = readl(&udc->op_regs->portsc);
+ /* not suspended? */
+ if (!(portsc & PORTSCX_PORT_SUSPEND))
+ return 0;
+ /* trigger force resume */
+ portsc |= PORTSCX_PORT_FORCE_RESUME;
+ writel(portsc, &udc->op_regs->portsc[0]);
+ return 0;
+}
+
+static int mv_udc_pullup(struct usb_gadget *gadget, int is_on)
+{
+ struct mv_udc *udc;
+ unsigned long flags;
+
+ udc = container_of(gadget, struct mv_udc, gadget);
+ spin_lock_irqsave(&udc->lock, flags);
+
+ udc->softconnect = (is_on != 0);
+ if (udc->driver && udc->softconnect)
+ udc_start(udc);
+ else
+ udc_stop(udc);
+
+ spin_unlock_irqrestore(&udc->lock, flags);
+ return 0;
+}
+
+/* device controller usb_gadget_ops structure */
+static const struct usb_gadget_ops mv_ops = {
+
+ /* returns the current frame number */
+ .get_frame = mv_udc_get_frame,
+
+ /* tries to wake up the host connected to this gadget */
+ .wakeup = mv_udc_wakeup,
+
+ /* D+ pullup, software-controlled connect/disconnect to USB host */
+ .pullup = mv_udc_pullup,
+};
+
+static void mv_udc_testmode(struct mv_udc *udc, u16 index, bool enter)
+{
+ dev_info(&udc->dev->dev, "Test Mode is not support yet\n");
+}
+
+static int eps_init(struct mv_udc *udc)
+{
+ struct mv_ep *ep;
+ char name[14];
+ int i;
+
+ /* initialize ep0 */
+ ep = &udc->eps[0];
+ ep->udc = udc;
+ strncpy(ep->name, "ep0", sizeof(ep->name));
+ ep->ep.name = ep->name;
+ ep->ep.ops = &mv_ep_ops;
+ ep->wedge = 0;
+ ep->stopped = 0;
+ ep->ep.maxpacket = EP0_MAX_PKT_SIZE;
+ ep->ep_num = 0;
+ ep->desc = &mv_ep0_desc;
+ INIT_LIST_HEAD(&ep->queue);
+
+ ep->ep_type = USB_ENDPOINT_XFER_CONTROL;
+
+ /* initialize other endpoints */
+ for (i = 2; i < udc->max_eps * 2; i++) {
+ ep = &udc->eps[i];
+ if (i % 2) {
+ snprintf(name, sizeof(name), "ep%din", i / 2);
+ ep->direction = EP_DIR_IN;
+ } else {
+ snprintf(name, sizeof(name), "ep%dout", i / 2);
+ ep->direction = EP_DIR_OUT;
+ }
+ ep->udc = udc;
+ strncpy(ep->name, name, sizeof(ep->name));
+ ep->ep.name = ep->name;
+
+ ep->ep.ops = &mv_ep_ops;
+ ep->stopped = 0;
+ ep->ep.maxpacket = (unsigned short) ~0;
+ ep->ep_num = i / 2;
+
+ INIT_LIST_HEAD(&ep->queue);
+ list_add_tail(&ep->ep.ep_list, &udc->gadget.ep_list);
+
+ ep->dqh = &udc->ep_dqh[i];
+ }
+
+ return 0;
+}
+
+/* delete all endpoint requests, called with spinlock held */
+static void nuke(struct mv_ep *ep, int status)
+{
+ /* called with spinlock held */
+ ep->stopped = 1;
+
+ /* endpoint fifo flush */
+ mv_ep_fifo_flush(&ep->ep);
+
+ while (!list_empty(&ep->queue)) {
+ struct mv_req *req = NULL;
+ req = list_entry(ep->queue.next, struct mv_req, queue);
+ done(ep, req, status);
+ }
+}
+
+/* stop all USB activities */
+static void stop_activity(struct mv_udc *udc, struct usb_gadget_driver *driver)
+{
+ struct mv_ep *ep;
+
+ nuke(&udc->eps[0], -ESHUTDOWN);
+
+ list_for_each_entry(ep, &udc->gadget.ep_list, ep.ep_list) {
+ nuke(ep, -ESHUTDOWN);
+ }
+
+ /* report disconnect; the driver is already quiesced */
+ if (driver) {
+ spin_unlock(&udc->lock);
+ driver->disconnect(&udc->gadget);
+ spin_lock(&udc->lock);
+ }
+}
+
+int usb_gadget_probe_driver(struct usb_gadget_driver *driver,
+ int (*bind)(struct usb_gadget *))
+{
+ struct mv_udc *udc = the_controller;
+ int retval = 0;
+ unsigned long flags;
+
+ if (!udc)
+ return -ENODEV;
+
+ if (udc->driver)
+ return -EBUSY;
+
+ spin_lock_irqsave(&udc->lock, flags);
+
+ /* hook up the driver ... */
+ driver->driver.bus = NULL;
+ udc->driver = driver;
+ udc->gadget.dev.driver = &driver->driver;
+
+ udc->usb_state = USB_STATE_ATTACHED;
+ udc->ep0_state = WAIT_FOR_SETUP;
+ udc->ep0_dir = USB_DIR_OUT;
+
+ spin_unlock_irqrestore(&udc->lock, flags);
+
+ retval = bind(&udc->gadget);
+ if (retval) {
+ dev_err(&udc->dev->dev, "bind to driver %s --> %d\n",
+ driver->driver.name, retval);
+ udc->driver = NULL;
+ udc->gadget.dev.driver = NULL;
+ return retval;
+ }
+ udc_reset(udc);
+ ep0_reset(udc);
+ udc_start(udc);
+
+ return 0;
+}
+EXPORT_SYMBOL(usb_gadget_probe_driver);
+
+int usb_gadget_unregister_driver(struct usb_gadget_driver *driver)
+{
+ struct mv_udc *udc = the_controller;
+ unsigned long flags;
+
+ if (!udc)
+ return -ENODEV;
+
+ udc_stop(udc);
+
+ spin_lock_irqsave(&udc->lock, flags);
+
+ /* stop all usb activities */
+ udc->gadget.speed = USB_SPEED_UNKNOWN;
+ stop_activity(udc, driver);
+ spin_unlock_irqrestore(&udc->lock, flags);
+
+ /* unbind gadget driver */
+ driver->unbind(&udc->gadget);
+ udc->gadget.dev.driver = NULL;
+ udc->driver = NULL;
+
+ return 0;
+}
+EXPORT_SYMBOL(usb_gadget_unregister_driver);
+
+static int
+udc_prime_status(struct mv_udc *udc, u8 direction, u16 status, bool empty)
+{
+ int retval = 0;
+ struct mv_req *req;
+ struct mv_ep *ep;
+
+ ep = &udc->eps[0];
+ udc->ep0_dir = direction;
+
+ req = udc->status_req;
+
+ /* fill in the reqest structure */
+ if (empty == false) {
+ *((u16 *) req->req.buf) = cpu_to_le16(status);
+ req->req.length = 2;
+ } else
+ req->req.length = 0;
+
+ req->ep = ep;
+ req->req.status = -EINPROGRESS;
+ req->req.actual = 0;
+ req->req.complete = NULL;
+ req->dtd_count = 0;
+
+ /* prime the data phase */
+ if (!req_to_dtd(req))
+ retval = queue_dtd(ep, req);
+ else{ /* no mem */
+ retval = -ENOMEM;
+ goto out;
+ }
+
+ if (retval) {
+ dev_err(&udc->dev->dev, "response error on GET_STATUS request\n");
+ goto out;
+ }
+
+ list_add_tail(&req->queue, &ep->queue);
+
+ return 0;
+out:
+ return retval;
+}
+
+static void ch9setaddress(struct mv_udc *udc, struct usb_ctrlrequest *setup)
+{
+ udc->dev_addr = (u8)setup->wValue;
+
+ /* update usb state */
+ udc->usb_state = USB_STATE_ADDRESS;
+
+ if (udc_prime_status(udc, EP_DIR_IN, 0, true))
+ ep0_stall(udc);
+}
+
+static void ch9getstatus(struct mv_udc *udc, u8 ep_num,
+ struct usb_ctrlrequest *setup)
+{
+ u16 status;
+ int retval;
+
+ if ((setup->bRequestType & (USB_DIR_IN | USB_TYPE_MASK))
+ != (USB_DIR_IN | USB_TYPE_STANDARD))
+ return;
+
+ if ((setup->bRequestType & USB_RECIP_MASK) == USB_RECIP_DEVICE) {
+ status = 1 << USB_DEVICE_SELF_POWERED;
+ status |= udc->remote_wakeup << USB_DEVICE_REMOTE_WAKEUP;
+ } else if ((setup->bRequestType & USB_RECIP_MASK)
+ == USB_RECIP_INTERFACE) {
+ /* get interface status */
+ status = 0;
+ } else if ((setup->bRequestType & USB_RECIP_MASK)
+ == USB_RECIP_ENDPOINT) {
+ u8 ep_num, direction;
+
+ ep_num = setup->wIndex & USB_ENDPOINT_NUMBER_MASK;
+ direction = (setup->wIndex & USB_ENDPOINT_DIR_MASK)
+ ? EP_DIR_IN : EP_DIR_OUT;
+ status = ep_is_stall(udc, ep_num, direction)
+ << USB_ENDPOINT_HALT;
+ }
+
+ retval = udc_prime_status(udc, EP_DIR_IN, status, false);
+ if (retval)
+ ep0_stall(udc);
+}
+
+static void ch9clearfeature(struct mv_udc *udc, struct usb_ctrlrequest *setup)
+{
+ u8 ep_num;
+ u8 direction;
+ struct mv_ep *ep;
+
+ if ((setup->bRequestType & (USB_TYPE_MASK | USB_RECIP_MASK))
+ == ((USB_TYPE_STANDARD | USB_RECIP_DEVICE))) {
+ switch (setup->wValue) {
+ case USB_DEVICE_REMOTE_WAKEUP:
+ udc->remote_wakeup = 0;
+ break;
+ case USB_DEVICE_TEST_MODE:
+ mv_udc_testmode(udc, 0, false);
+ break;
+ default:
+ goto out;
+ }
+ } else if ((setup->bRequestType & (USB_TYPE_MASK | USB_RECIP_MASK))
+ == ((USB_TYPE_STANDARD | USB_RECIP_ENDPOINT))) {
+ switch (setup->wValue) {
+ case USB_ENDPOINT_HALT:
+ ep_num = setup->wIndex & USB_ENDPOINT_NUMBER_MASK;
+ direction = (setup->wIndex & USB_ENDPOINT_DIR_MASK)
+ ? EP_DIR_IN : EP_DIR_OUT;
+ if (setup->wValue != 0 || setup->wLength != 0
+ || ep_num > udc->max_eps)
+ goto out;
+ ep = &udc->eps[ep_num * 2 + direction];
+ if (ep->wedge == 1)
+ break;
+ spin_unlock(&udc->lock);
+ ep_set_stall(udc, ep_num, direction, 0);
+ spin_lock(&udc->lock);
+ break;
+ default:
+ goto out;
+ }
+ } else
+ goto out;
+
+ if (udc_prime_status(udc, EP_DIR_IN, 0, true))
+ ep0_stall(udc);
+ else
+ udc->ep0_state = DATA_STATE_XMIT;
+out:
+ return;
+}
+
+static void ch9setfeature(struct mv_udc *udc, struct usb_ctrlrequest *setup)
+{
+ u8 ep_num;
+ u8 direction;
+
+ if ((setup->bRequestType & (USB_TYPE_MASK | USB_RECIP_MASK))
+ == ((USB_TYPE_STANDARD | USB_RECIP_DEVICE))) {
+ switch (setup->wValue) {
+ case USB_DEVICE_REMOTE_WAKEUP:
+ udc->remote_wakeup = 1;
+ break;
+ case USB_DEVICE_TEST_MODE:
+ if (setup->wIndex & 0xFF
+ && udc->gadget.speed != USB_SPEED_HIGH)
+ goto out;
+ if (udc->usb_state == USB_STATE_CONFIGURED
+ || udc->usb_state == USB_STATE_ADDRESS
+ || udc->usb_state == USB_STATE_DEFAULT)
+ mv_udc_testmode(udc,
+ setup->wIndex & 0xFF00, true);
+ else
+ goto out;
+ break;
+ default:
+ goto out;
+ }
+ } else if ((setup->bRequestType & (USB_TYPE_MASK | USB_RECIP_MASK))
+ == ((USB_TYPE_STANDARD | USB_RECIP_ENDPOINT))) {
+ switch (setup->wValue) {
+ case USB_ENDPOINT_HALT:
+ ep_num = setup->wIndex & USB_ENDPOINT_NUMBER_MASK;
+ direction = (setup->wIndex & USB_ENDPOINT_DIR_MASK)
+ ? EP_DIR_IN : EP_DIR_OUT;
+ if (setup->wValue != 0 || setup->wLength != 0
+ || ep_num > udc->max_eps)
+ goto out;
+ spin_unlock(&udc->lock);
+ ep_set_stall(udc, ep_num, direction, 1);
+ spin_lock(&udc->lock);
+ break;
+ default:
+ goto out;
+ }
+ } else
+ goto out;
+
+ if (udc_prime_status(udc, EP_DIR_IN, 0, true))
+ ep0_stall(udc);
+out:
+ return;
+}
+
+static void handle_setup_packet(struct mv_udc *udc, u8 ep_num,
+ struct usb_ctrlrequest *setup)
+{
+ bool delegate = false;
+
+ nuke(&udc->eps[ep_num * 2 + EP_DIR_OUT], -ESHUTDOWN);
+
+ dev_dbg(&udc->dev->dev, "SETUP %02x.%02x v%04x i%04x l%04x\n",
+ setup->bRequestType, setup->bRequest,
+ setup->wValue, setup->wIndex, setup->wLength);
+ /* We process some stardard setup requests here */
+ if ((setup->bRequestType & USB_TYPE_MASK) == USB_TYPE_STANDARD) {
+ switch (setup->bRequest) {
+ case USB_REQ_GET_STATUS:
+ ch9getstatus(udc, ep_num, setup);
+ break;
+
+ case USB_REQ_SET_ADDRESS:
+ ch9setaddress(udc, setup);
+ break;
+
+ case USB_REQ_CLEAR_FEATURE:
+ ch9clearfeature(udc, setup);
+ break;
+
+ case USB_REQ_SET_FEATURE:
+ ch9setfeature(udc, setup);
+ break;
+
+ default:
+ delegate = true;
+ }
+ } else
+ delegate = true;
+
+ /* delegate USB standard requests to the gadget driver */
+ if (delegate == true) {
+ /* USB requests handled by gadget */
+ if (setup->wLength) {
+ /* DATA phase from gadget, STATUS phase from udc */
+ udc->ep0_dir = (setup->bRequestType & USB_DIR_IN)
+ ? EP_DIR_IN : EP_DIR_OUT;
+ spin_unlock(&udc->lock);
+ if (udc->driver->setup(&udc->gadget,
+ &udc->local_setup_buff) < 0)
+ ep0_stall(udc);
+ spin_lock(&udc->lock);
+ udc->ep0_state = (setup->bRequestType & USB_DIR_IN)
+ ? DATA_STATE_XMIT : DATA_STATE_RECV;
+ } else {
+ /* no DATA phase, IN STATUS phase from gadget */
+ udc->ep0_dir = EP_DIR_IN;
+ spin_unlock(&udc->lock);
+ if (udc->driver->setup(&udc->gadget,
+ &udc->local_setup_buff) < 0)
+ ep0_stall(udc);
+ spin_lock(&udc->lock);
+ udc->ep0_state = WAIT_FOR_OUT_STATUS;
+ }
+ }
+}
+
+/* complete DATA or STATUS phase of ep0 prime status phase if needed */
+static void ep0_req_complete(struct mv_udc *udc,
+ struct mv_ep *ep0, struct mv_req *req)
+{
+ u32 new_addr;
+
+ if (udc->usb_state == USB_STATE_ADDRESS) {
+ /* set the new address */
+ new_addr = (u32)udc->dev_addr;
+ writel(new_addr << USB_DEVICE_ADDRESS_BIT_SHIFT,
+ &udc->op_regs->deviceaddr);
+ }
+
+ done(ep0, req, 0);
+
+ switch (udc->ep0_state) {
+ case DATA_STATE_XMIT:
+ /* receive status phase */
+ if (udc_prime_status(udc, EP_DIR_OUT, 0, true))
+ ep0_stall(udc);
+ break;
+ case DATA_STATE_RECV:
+ /* send status phase */
+ if (udc_prime_status(udc, EP_DIR_IN, 0 , true))
+ ep0_stall(udc);
+ break;
+ case WAIT_FOR_OUT_STATUS:
+ udc->ep0_state = WAIT_FOR_SETUP;
+ break;
+ case WAIT_FOR_SETUP:
+ dev_err(&udc->dev->dev, "unexpect ep0 packets\n");
+ break;
+ default:
+ ep0_stall(udc);
+ break;
+ }
+}
+
+static void get_setup_data(struct mv_udc *udc, u8 ep_num, u8 *buffer_ptr)
+{
+ u32 temp;
+ struct mv_dqh *dqh;
+
+ dqh = &udc->ep_dqh[ep_num * 2 + EP_DIR_OUT];
+
+ /* Clear bit in ENDPTSETUPSTAT */
+ temp = readl(&udc->op_regs->epsetupstat);
+ writel(temp | (1 << ep_num), &udc->op_regs->epsetupstat);
+
+ /* while a hazard exists when setup package arrives */
+ do {
+ /* Set Setup Tripwire */
+ temp = readl(&udc->op_regs->usbcmd);
+ writel(temp | USBCMD_SETUP_TRIPWIRE_SET, &udc->op_regs->usbcmd);
+
+ /* Copy the setup packet to local buffer */
+ memcpy(buffer_ptr, (u8 *) dqh->setup_buffer, 8);
+ } while (!(readl(&udc->op_regs->usbcmd) & USBCMD_SETUP_TRIPWIRE_SET));
+
+ /* Clear Setup Tripwire */
+ temp = readl(&udc->op_regs->usbcmd);
+ writel(temp & ~USBCMD_SETUP_TRIPWIRE_SET, &udc->op_regs->usbcmd);
+}
+
+static void irq_process_tr_complete(struct mv_udc *udc)
+{
+ u32 tmp, bit_pos;
+ int i, ep_num = 0, direction = 0;
+ struct mv_ep *curr_ep;
+ struct mv_req *curr_req, *temp_req;
+ int status;
+
+ /*
+ * We use separate loops for ENDPTSETUPSTAT and ENDPTCOMPLETE
+ * because the setup packets are to be read ASAP
+ */
+
+ /* Process all Setup packet received interrupts */
+ tmp = readl(&udc->op_regs->epsetupstat);
+
+ if (tmp) {
+ for (i = 0; i < udc->max_eps; i++) {
+ if (tmp & (1 << i)) {
+ get_setup_data(udc, i,
+ (u8 *)(&udc->local_setup_buff));
+ handle_setup_packet(udc, i,
+ &udc->local_setup_buff);
+ }
+ }
+ }
+
+ /* Don't clear the endpoint setup status register here.
+ * It is cleared as a setup packet is read out of the buffer
+ */
+
+ /* Process non-setup transaction complete interrupts */
+ tmp = readl(&udc->op_regs->epcomplete);
+
+ if (!tmp)
+ return;
+
+ writel(tmp, &udc->op_regs->epcomplete);
+
+ for (i = 0; i < udc->max_eps * 2; i++) {
+ ep_num = i >> 1;
+ direction = i % 2;
+
+ bit_pos = 1 << (ep_num + 16 * direction);
+
+ if (!(bit_pos & tmp))
+ continue;
+
+ if (i == 1)
+ curr_ep = &udc->eps[0];
+ else
+ curr_ep = &udc->eps[i];
+ /* process the req queue until an uncomplete request */
+ list_for_each_entry_safe(curr_req, temp_req,
+ &curr_ep->queue, queue) {
+ status = process_ep_req(udc, i, curr_req);
+ if (status)
+ break;
+
+ /* write back status to req */
+ curr_req->req.status = status;
+
+ /* ep0 request completion */
+ if (ep_num == 0) {
+ ep0_req_complete(udc, curr_ep, curr_req);
+ break;
+ } else {
+ done(curr_ep, curr_req, status);
+ }
+ }
+ }
+}
+
+void irq_process_reset(struct mv_udc *udc)
+{
+ u32 tmp;
+ unsigned int loops;
+
+ udc->ep0_dir = EP_DIR_OUT;
+ udc->ep0_state = WAIT_FOR_SETUP;
+ udc->remote_wakeup = 0; /* default to 0 on reset */
+
+ /* The address bits are past bit 25-31. Set the address */
+ tmp = readl(&udc->op_regs->deviceaddr);
+ tmp &= ~(USB_DEVICE_ADDRESS_MASK);
+ writel(tmp, &udc->op_regs->deviceaddr);
+
+ /* Clear all the setup token semaphores */
+ tmp = readl(&udc->op_regs->epsetupstat);
+ writel(tmp, &udc->op_regs->epsetupstat);
+
+ /* Clear all the endpoint complete status bits */
+ tmp = readl(&udc->op_regs->epcomplete);
+ writel(tmp, &udc->op_regs->epcomplete);
+
+ /* wait until all endptprime bits cleared */
+ loops = LOOPS(PRIME_TIMEOUT);
+ while (readl(&udc->op_regs->epprime) & 0xFFFFFFFF) {
+ if (loops == 0) {
+ dev_err(&udc->dev->dev,
+ "Timeout for ENDPTPRIME = 0x%x\n",
+ readl(&udc->op_regs->epprime));
+ break;
+ }
+ loops--;
+ udelay(LOOPS_USEC);
+ }
+
+ /* Write 1s to the Flush register */
+ writel((u32)~0, &udc->op_regs->epflush);
+
+ if (readl(&udc->op_regs->portsc[0]) & PORTSCX_PORT_RESET) {
+ dev_info(&udc->dev->dev, "usb bus reset\n");
+ udc->usb_state = USB_STATE_DEFAULT;
+ /* reset all the queues, stop all USB activities */
+ stop_activity(udc, udc->driver);
+ } else {
+ dev_info(&udc->dev->dev, "USB reset portsc 0x%x\n",
+ readl(&udc->op_regs->portsc));
+
+ /*
+ * re-initialize
+ * controller reset
+ */
+ udc_reset(udc);
+
+ /* reset all the queues, stop all USB activities */
+ stop_activity(udc, udc->driver);
+
+ /* reset ep0 dQH and endptctrl */
+ ep0_reset(udc);
+
+ /* enable interrupt and set controller to run state */
+ udc_start(udc);
+
+ udc->usb_state = USB_STATE_ATTACHED;
+ }
+}
+
+static void handle_bus_resume(struct mv_udc *udc)
+{
+ udc->usb_state = udc->resume_state;
+ udc->resume_state = 0;
+
+ /* report resume to the driver */
+ if (udc->driver) {
+ if (udc->driver->resume) {
+ spin_unlock(&udc->lock);
+ udc->driver->resume(&udc->gadget);
+ spin_lock(&udc->lock);
+ }
+ }
+}
+
+static void irq_process_suspend(struct mv_udc *udc)
+{
+ udc->resume_state = udc->usb_state;
+ udc->usb_state = USB_STATE_SUSPENDED;
+
+ if (udc->driver->suspend) {
+ spin_unlock(&udc->lock);
+ udc->driver->suspend(&udc->gadget);
+ spin_lock(&udc->lock);
+ }
+}
+
+static void irq_process_port_change(struct mv_udc *udc)
+{
+ u32 portsc;
+
+ portsc = readl(&udc->op_regs->portsc[0]);
+ if (!(portsc & PORTSCX_PORT_RESET)) {
+ /* Get the speed */
+ u32 speed = portsc & PORTSCX_PORT_SPEED_MASK;
+ switch (speed) {
+ case PORTSCX_PORT_SPEED_HIGH:
+ udc->gadget.speed = USB_SPEED_HIGH;
+ break;
+ case PORTSCX_PORT_SPEED_FULL:
+ udc->gadget.speed = USB_SPEED_FULL;
+ break;
+ case PORTSCX_PORT_SPEED_LOW:
+ udc->gadget.speed = USB_SPEED_LOW;
+ break;
+ default:
+ udc->gadget.speed = USB_SPEED_UNKNOWN;
+ break;
+ }
+ }
+
+ if (portsc & PORTSCX_PORT_SUSPEND) {
+ udc->resume_state = udc->usb_state;
+ udc->usb_state = USB_STATE_SUSPENDED;
+ if (udc->driver->suspend) {
+ spin_unlock(&udc->lock);
+ udc->driver->suspend(&udc->gadget);
+ spin_lock(&udc->lock);
+ }
+ }
+
+ if (!(portsc & PORTSCX_PORT_SUSPEND)
+ && udc->usb_state == USB_STATE_SUSPENDED) {
+ handle_bus_resume(udc);
+ }
+
+ if (!udc->resume_state)
+ udc->usb_state = USB_STATE_DEFAULT;
+}
+
+static void irq_process_error(struct mv_udc *udc)
+{
+ /* Increment the error count */
+ udc->errors++;
+}
+
+static irqreturn_t mv_udc_irq(int irq, void *dev)
+{
+ struct mv_udc *udc = (struct mv_udc *)dev;
+ u32 status, intr;
+
+ spin_lock(&udc->lock);
+
+ status = readl(&udc->op_regs->usbsts);
+ intr = readl(&udc->op_regs->usbintr);
+ status &= intr;
+
+ if (status == 0) {
+ spin_unlock(&udc->lock);
+ return IRQ_NONE;
+ }
+
+ /* Clear all the interrupts occured */
+ writel(status, &udc->op_regs->usbsts);
+
+ if (status & USBSTS_ERR)
+ irq_process_error(udc);
+
+ if (status & USBSTS_RESET)
+ irq_process_reset(udc);
+
+ if (status & USBSTS_PORT_CHANGE)
+ irq_process_port_change(udc);
+
+ if (status & USBSTS_INT)
+ irq_process_tr_complete(udc);
+
+ if (status & USBSTS_SUSPEND)
+ irq_process_suspend(udc);
+
+ spin_unlock(&udc->lock);
+
+ return IRQ_HANDLED;
+}
+
+/* release device structure */
+static void gadget_release(struct device *_dev)
+{
+ struct mv_udc *udc = the_controller;
+
+ complete(udc->done);
+ kfree(udc);
+}
+
+static int mv_udc_remove(struct platform_device *dev)
+{
+ struct mv_udc *udc = the_controller;
+
+ DECLARE_COMPLETION(done);
+
+ udc->done = &done;
+
+ /* free memory allocated in probe */
+ if (udc->dtd_pool)
+ dma_pool_destroy(udc->dtd_pool);
+
+ if (udc->ep_dqh)
+ dma_free_coherent(&dev->dev, udc->ep_dqh_size,
+ udc->ep_dqh, udc->ep_dqh_dma);
+
+ kfree(udc->eps);
+
+ if (udc->irq)
+ free_irq(udc->irq, &dev->dev);
+
+ if (udc->cap_regs)
+ iounmap(udc->cap_regs);
+ udc->cap_regs = NULL;
+
+ if (udc->phy_regs)
+ iounmap((void *)udc->phy_regs);
+ udc->phy_regs = 0;
+
+ if (udc->status_req) {
+ kfree(udc->status_req->req.buf);
+ kfree(udc->status_req);
+ }
+
+ device_unregister(&udc->gadget.dev);
+
+ /* free dev, wait for the release() finished */
+ wait_for_completion(&done);
+
+ the_controller = NULL;
+
+ return 0;
+}
+
+int mv_udc_probe(struct platform_device *dev)
+{
+ struct mv_udc *udc;
+ int retval = 0;
+ struct resource *r;
+ size_t size;
+
+ udc = kzalloc(sizeof *udc, GFP_KERNEL);
+ if (udc == NULL) {
+ dev_err(&dev->dev, "failed to allocate memory for udc\n");
+ retval = -ENOMEM;
+ goto error;
+ }
+
+ spin_lock_init(&udc->lock);
+
+ udc->dev = dev;
+
+ udc->clk = clk_get(&dev->dev, "U2OCLK");
+ if (IS_ERR(udc->clk)) {
+ retval = PTR_ERR(udc->clk);
+ goto error;
+ }
+
+ r = platform_get_resource_byname(udc->dev, IORESOURCE_MEM, "u2o");
+ if (r == NULL) {
+ dev_err(&dev->dev, "no I/O memory resource defined\n");
+ retval = -ENODEV;
+ goto error;
+ }
+
+ udc->cap_regs = (struct mv_cap_regs __iomem *)
+ ioremap(r->start, resource_size(r));
+ if (udc->cap_regs == NULL) {
+ dev_err(&dev->dev, "failed to map I/O memory\n");
+ retval = -EBUSY;
+ goto error;
+ }
+
+ r = platform_get_resource_byname(udc->dev, IORESOURCE_MEM, "u2ophy");
+ if (r == NULL) {
+ dev_err(&dev->dev, "no phy I/O memory resource defined\n");
+ retval = -ENODEV;
+ goto error;
+ }
+
+ udc->phy_regs = (unsigned int)ioremap(r->start, resource_size(r));
+ if (udc->phy_regs == 0) {
+ dev_err(&dev->dev, "failed to map phy I/O memory\n");
+ retval = -EBUSY;
+ goto error;
+ }
+
+ /* we will acces controller register, so enable the clk */
+ clk_enable(udc->clk);
+ retval = mv_udc_phy_init(udc->phy_regs);
+ if (retval) {
+ dev_err(&dev->dev, "phy initialization error %d\n", retval);
+ goto error;
+ }
+
+ udc->op_regs = (struct mv_op_regs __iomem *)((u32)udc->cap_regs
+ + (readl(&udc->cap_regs->caplength_hciversion)
+ & CAPLENGTH_MASK));
+ udc->max_eps = readl(&udc->cap_regs->dccparams) & DCCPARAMS_DEN_MASK;
+
+ size = udc->max_eps * sizeof(struct mv_dqh) *2;
+ size = (size + DQH_ALIGNMENT - 1) & ~(DQH_ALIGNMENT - 1);
+ udc->ep_dqh = dma_alloc_coherent(&dev->dev, size,
+ &udc->ep_dqh_dma, GFP_KERNEL);
+
+ if (udc->ep_dqh == NULL) {
+ dev_err(&dev->dev, "allocate dQH memory failed\n");
+ retval = -ENOMEM;
+ goto error;
+ }
+ udc->ep_dqh_size = size;
+
+ /* create dTD dma_pool resource */
+ udc->dtd_pool = dma_pool_create("mv_dtd",
+ &dev->dev,
+ sizeof(struct mv_dtd),
+ DTD_ALIGNMENT,
+ DMA_BOUNDARY);
+
+ if (!udc->dtd_pool) {
+ retval = -ENOMEM;
+ goto error;
+ }
+
+ size = udc->max_eps * sizeof(struct mv_ep) *2;
+ udc->eps = kzalloc(size, GFP_KERNEL);
+ if (udc->eps == NULL) {
+ dev_err(&dev->dev, "allocate ep memory failed\n");
+ retval = -ENOMEM;
+ goto error;
+ }
+
+ /* initialize ep0 status request structure */
+ udc->status_req = kzalloc(sizeof(struct mv_req), GFP_KERNEL);
+ if (!udc->status_req) {
+ dev_err(&dev->dev, "allocate status_req memory failed\n");
+ retval = -ENOMEM;
+ goto error;
+ }
+ INIT_LIST_HEAD(&udc->status_req->queue);
+
+ /* allocate a small amount of memory to get valid address */
+ udc->status_req->req.buf = kzalloc(8, GFP_KERNEL);
+ udc->status_req->req.dma = virt_to_phys(udc->status_req->req.buf);
+
+ udc->resume_state = USB_STATE_NOTATTACHED;
+ udc->usb_state = USB_STATE_POWERED;
+ udc->ep0_dir = EP_DIR_OUT;
+ udc->remote_wakeup = 0;
+
+ r = platform_get_resource(udc->dev, IORESOURCE_IRQ, 0);
+ if (r == NULL) {
+ dev_err(&dev->dev, "no IRQ resource defined\n");
+ retval = -ENODEV;
+ goto error;
+ }
+ udc->irq = r->start;
+ if (request_irq(udc->irq, mv_udc_irq,
+ IRQF_DISABLED | IRQF_SHARED, driver_name, udc)) {
+ dev_err(&dev->dev, "Request irq %d for UDC failed\n",
+ udc->irq);
+ retval = -ENODEV;
+ goto error;
+ }
+
+ /* initialize gadget structure */
+ udc->gadget.ops = &mv_ops; /* usb_gadget_ops */
+ udc->gadget.ep0 = &udc->eps[0].ep; /* gadget ep0 */
+ INIT_LIST_HEAD(&udc->gadget.ep_list); /* ep_list */
+ udc->gadget.speed = USB_SPEED_UNKNOWN; /* speed */
+ udc->gadget.is_dualspeed = 1; /* support dual speed */
+
+ /* the "gadget" abstracts/virtualizes the controller */
+ dev_set_name(&udc->gadget.dev, "gadget");
+ udc->gadget.dev.parent = &dev->dev;
+ udc->gadget.dev.dma_mask = dev->dev.dma_mask;
+ udc->gadget.dev.release = gadget_release;
+ udc->gadget.name = driver_name; /* gadget name */
+
+ retval = device_register(&udc->gadget.dev);
+ if (retval)
+ goto error;
+
+ eps_init(udc);
+
+ the_controller = udc;
+
+ goto out;
+error:
+ if (udc)
+ mv_udc_remove(udc->dev);
+out:
+ return retval;
+}
+
+#ifdef CONFIG_PM
+static int mv_udc_suspend(struct platform_device *_dev, pm_message_t state)
+{
+ struct mv_udc *udc = the_controller;
+
+ udc_stop(udc);
+
+ return 0;
+}
+
+static int mv_udc_resume(struct platform_device *_dev)
+{
+ struct mv_udc *udc = the_controller;
+ int retval;
+
+ retval = mv_udc_phy_init(udc->phy_regs);
+ if (retval) {
+ dev_err(_dev, "phy initialization error %d\n", retval);
+ goto error;
+ }
+ udc_reset(udc);
+ ep0_reset(udc);
+ udc_start(udc);
+
+ return 0;
+}
+
+static const struct dev_pm_ops mv_udc_pm_ops = {
+ .suspend = mv_udc_suspend,
+ .resume = mv_udc_resume,
+};
+#endif
+
+static struct platform_driver udc_driver = {
+ .probe = mv_udc_probe,
+ .remove = __exit_p(mv_udc_remove),
+ .driver = {
+ .owner = THIS_MODULE,
+ .name = "pxa-u2o",
+#ifdef CONFIG_PM
+ .pm = mv_udc_pm_ops,
+#endif
+ },
+};
+
+
+MODULE_DESCRIPTION(DRIVER_DESC);
+MODULE_AUTHOR("Chao Xie <chao.xie@marvell.com>");
+MODULE_VERSION(DRIVER_VERSION);
+MODULE_LICENSE("GPL");
+
+
+static int __init init(void)
+{
+ return platform_driver_register(&udc_driver);
+}
+module_init(init);
+
+
+static void __exit cleanup(void)
+{
+ platform_driver_unregister(&udc_driver);
+}
+module_exit(cleanup);
+
diff --git a/drivers/usb/gadget/mv_udc_phy.c b/drivers/usb/gadget/mv_udc_phy.c
new file mode 100644
index 00000000000..d4dea97e38a
--- /dev/null
+++ b/drivers/usb/gadget/mv_udc_phy.c
@@ -0,0 +1,214 @@
+#include <linux/delay.h>
+#include <linux/timer.h>
+#include <linux/io.h>
+#include <linux/errno.h>
+
+#include <mach/cputype.h>
+
+#ifdef CONFIG_ARCH_MMP
+
+#define UTMI_REVISION 0x0
+#define UTMI_CTRL 0x4
+#define UTMI_PLL 0x8
+#define UTMI_TX 0xc
+#define UTMI_RX 0x10
+#define UTMI_IVREF 0x14
+#define UTMI_T0 0x18
+#define UTMI_T1 0x1c
+#define UTMI_T2 0x20
+#define UTMI_T3 0x24
+#define UTMI_T4 0x28
+#define UTMI_T5 0x2c
+#define UTMI_RESERVE 0x30
+#define UTMI_USB_INT 0x34
+#define UTMI_DBG_CTL 0x38
+#define UTMI_OTG_ADDON 0x3c
+
+/* For UTMICTRL Register */
+#define UTMI_CTRL_USB_CLK_EN (1 << 31)
+/* pxa168 */
+#define UTMI_CTRL_SUSPEND_SET1 (1 << 30)
+#define UTMI_CTRL_SUSPEND_SET2 (1 << 29)
+#define UTMI_CTRL_RXBUF_PDWN (1 << 24)
+#define UTMI_CTRL_TXBUF_PDWN (1 << 11)
+
+#define UTMI_CTRL_INPKT_DELAY_SHIFT 30
+#define UTMI_CTRL_INPKT_DELAY_SOF_SHIFT 28
+#define UTMI_CTRL_PU_REF_SHIFT 20
+#define UTMI_CTRL_ARC_PULLDN_SHIFT 12
+#define UTMI_CTRL_PLL_PWR_UP_SHIFT 1
+#define UTMI_CTRL_PWR_UP_SHIFT 0
+/* For UTMI_PLL Register */
+#define UTMI_PLL_CLK_BLK_EN_SHIFT 24
+#define UTMI_PLL_FBDIV_SHIFT 4
+#define UTMI_PLL_REFDIV_SHIFT 0
+#define UTMI_PLL_FBDIV_MASK 0x00000FF0
+#define UTMI_PLL_REFDIV_MASK 0x0000000F
+#define UTMI_PLL_ICP_MASK 0x00007000
+#define UTMI_PLL_KVCO_MASK 0x00031000
+#define UTMI_PLL_PLLCALI12_SHIFT 29
+#define UTMI_PLL_PLLCALI12_MASK (0x3 << 29)
+#define UTMI_PLL_PLLVDD18_SHIFT 27
+#define UTMI_PLL_PLLVDD18_MASK (0x3 << 27)
+#define UTMI_PLL_PLLVDD12_SHIFT 25
+#define UTMI_PLL_PLLVDD12_MASK (0x3 << 25)
+#define UTMI_PLL_KVCO_SHIFT 15
+#define UTMI_PLL_ICP_SHIFT 12
+/* For UTMI_TX Register */
+#define UTMI_TX_REG_EXT_FS_RCAL_SHIFT 27
+#define UTMI_TX_REG_EXT_FS_RCAL_MASK (0xf << 27)
+#define UTMI_TX_REG_EXT_FS_RCAL_EN_MASK 26
+#define UTMI_TX_REG_EXT_FS_RCAL_EN (0x1 << 26)
+#define UTMI_TX_LOW_VDD_EN_SHIFT 11
+#define UTMI_TX_IMPCAL_VTH_SHIFT 14
+#define UTMI_TX_IMPCAL_VTH_MASK (0x7 << 14)
+#define UTMI_TX_CK60_PHSEL_SHIFT 17
+#define UTMI_TX_CK60_PHSEL_MASK (0xf << 17)
+#define UTMI_TX_TXVDD12_SHIFT 22
+#define UTMI_TX_TXVDD12_MASK (0x3 << 22)
+#define UTMI_TX_AMP_SHIFT 0
+#define UTMI_TX_AMP_MASK (0x7 << 0)
+/* For UTMI_RX Register */
+#define UTMI_RX_SQ_THRESH_SHIFT 4
+#define UTMI_RX_SQ_THRESH_MASK (0xf << 4)
+#define UTMI_REG_SQ_LENGTH_SHIFT 15
+#define UTMI_REG_SQ_LENGTH_MASK (0x3 << 15)
+
+#define REG_RCAL_START 0x00001000
+#define VCOCAL_START 0x00200000
+#define KVCO_EXT 0x00400000
+#define PLL_READY 0x00800000
+#define CLK_BLK_EN 0x01000000
+#endif
+
+static unsigned int u2o_read(unsigned int base, unsigned int offset)
+{
+ return readl(base + offset);
+}
+
+static void u2o_set(unsigned int base, unsigned int offset, unsigned int value)
+{
+ unsigned int reg;
+
+ reg = readl(base + offset);
+ reg |= value;
+ writel(reg, base + offset);
+ readl(base + offset);
+}
+
+static void u2o_clear(unsigned int base, unsigned int offset,
+ unsigned int value)
+{
+ unsigned int reg;
+
+ reg = readl(base + offset);
+ reg &= ~value;
+ writel(reg, base + offset);
+ readl(base + offset);
+}
+
+static void u2o_write(unsigned int base, unsigned int offset,
+ unsigned int value)
+{
+ writel(value, base + offset);
+ readl(base + offset);
+}
+
+#ifdef CONFIG_ARCH_MMP
+int mv_udc_phy_init(unsigned int base)
+{
+ unsigned long timeout;
+
+ /* Initialize the USB PHY power */
+ if (cpu_is_pxa910()) {
+ u2o_set(base, UTMI_CTRL, (1 << UTMI_CTRL_INPKT_DELAY_SOF_SHIFT)
+ | (1 << UTMI_CTRL_PU_REF_SHIFT));
+ }
+
+ u2o_set(base, UTMI_CTRL, 1 << UTMI_CTRL_PLL_PWR_UP_SHIFT);
+ u2o_set(base, UTMI_CTRL, 1 << UTMI_CTRL_PWR_UP_SHIFT);
+
+ /* UTMI_PLL settings */
+ u2o_clear(base, UTMI_PLL, UTMI_PLL_PLLVDD18_MASK
+ | UTMI_PLL_PLLVDD12_MASK | UTMI_PLL_PLLCALI12_MASK
+ | UTMI_PLL_FBDIV_MASK | UTMI_PLL_REFDIV_MASK
+ | UTMI_PLL_ICP_MASK | UTMI_PLL_KVCO_MASK);
+
+ u2o_set(base, UTMI_PLL, (0xee << UTMI_PLL_FBDIV_SHIFT)
+ | (0xb << UTMI_PLL_REFDIV_SHIFT)
+ | (3 << UTMI_PLL_PLLVDD18_SHIFT)
+ | (3 << UTMI_PLL_PLLVDD12_SHIFT)
+ | (3 << UTMI_PLL_PLLCALI12_SHIFT)
+ | (1 << UTMI_PLL_ICP_SHIFT) | (3 << UTMI_PLL_KVCO_SHIFT));
+
+ /* UTMI_TX */
+ u2o_clear(base, UTMI_TX, UTMI_TX_REG_EXT_FS_RCAL_EN_MASK
+ | UTMI_TX_TXVDD12_MASK
+ | UTMI_TX_CK60_PHSEL_MASK | UTMI_TX_IMPCAL_VTH_MASK
+ | UTMI_TX_REG_EXT_FS_RCAL_MASK | UTMI_TX_AMP_MASK);
+ u2o_set(base, UTMI_TX, (3 << UTMI_TX_TXVDD12_SHIFT)
+ | (4 << UTMI_TX_CK60_PHSEL_SHIFT)
+ | (4 << UTMI_TX_IMPCAL_VTH_SHIFT)
+ | (8 << UTMI_TX_REG_EXT_FS_RCAL_SHIFT)
+ | (3 << UTMI_TX_AMP_SHIFT));
+
+ /* UTMI_RX */
+ u2o_clear(base, UTMI_RX, UTMI_RX_SQ_THRESH_MASK
+ | UTMI_REG_SQ_LENGTH_MASK);
+ if (cpu_is_pxa168())
+ u2o_set(base, UTMI_RX, (7 << UTMI_RX_SQ_THRESH_SHIFT)
+ | (2 << UTMI_REG_SQ_LENGTH_SHIFT));
+ else
+ u2o_set(base, UTMI_RX, (0x7 << UTMI_RX_SQ_THRESH_SHIFT)
+ | (2 << UTMI_REG_SQ_LENGTH_SHIFT));
+
+ /* UTMI_IVREF */
+ if (cpu_is_pxa168())
+ /*
+ * fixing Microsoft Altair board interface with NEC hub issue -
+ * Set UTMI_IVREF from 0x4a3 to 0x4bf
+ */
+ u2o_write(base, UTMI_IVREF, 0x4bf);
+
+ /* calibrate */
+ timeout = jiffies + 100;
+ while ((u2o_read(base, UTMI_PLL) & PLL_READY) == 0) {
+ if (time_after(jiffies, timeout))
+ return -ETIME;
+ cpu_relax();
+ }
+
+ /* toggle VCOCAL_START bit of UTMI_PLL */
+ udelay(200);
+ u2o_set(base, UTMI_PLL, VCOCAL_START);
+ udelay(40);
+ u2o_clear(base, UTMI_PLL, VCOCAL_START);
+
+ /* toggle REG_RCAL_START bit of UTMI_TX */
+ udelay(200);
+ u2o_set(base, UTMI_TX, REG_RCAL_START);
+ udelay(40);
+ u2o_clear(base, UTMI_TX, REG_RCAL_START);
+ udelay(200);
+
+ /* make sure phy is ready */
+ timeout = jiffies + 100;
+ while ((u2o_read(base, UTMI_PLL) & PLL_READY) == 0) {
+ if (time_after(jiffies, timeout))
+ return -ETIME;
+ cpu_relax();
+ }
+
+ if (cpu_is_pxa168()) {
+ u2o_set(base, UTMI_RESERVE, 1 << 5);
+ /* Turn on UTMI PHY OTG extension */
+ u2o_write(base, UTMI_OTG_ADDON, 1);
+ }
+ return 0;
+}
+#else
+int mv_udc_phy_init(unsigned int base)
+{
+ return 0;
+}
+#endif
diff --git a/drivers/usb/gadget/ncm.c b/drivers/usb/gadget/ncm.c
new file mode 100644
index 00000000000..99c179ad729
--- /dev/null
+++ b/drivers/usb/gadget/ncm.c
@@ -0,0 +1,248 @@
+/*
+ * ncm.c -- NCM gadget driver
+ *
+ * Copyright (C) 2010 Nokia Corporation
+ * Contact: Yauheni Kaliuta <yauheni.kaliuta@nokia.com>
+ *
+ * The driver borrows from ether.c which is:
+ *
+ * Copyright (C) 2003-2005,2008 David Brownell
+ * Copyright (C) 2003-2004 Robert Schwebel, Benedikt Spranger
+ * Copyright (C) 2008 Nokia Corporation
+ *
+ * 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 DEBUG */
+/* #define VERBOSE_DEBUG */
+
+#include <linux/kernel.h>
+#include <linux/utsname.h>
+
+
+#include "u_ether.h"
+
+#define DRIVER_DESC "NCM Gadget"
+
+/*-------------------------------------------------------------------------*/
+
+/*
+ * 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 "composite.c"
+#include "usbstring.c"
+#include "config.c"
+#include "epautoconf.c"
+
+#include "f_ncm.c"
+#include "u_ether.c"
+
+/*-------------------------------------------------------------------------*/
+
+/* DO NOT REUSE THESE IDs with a protocol-incompatible driver!! Ever!!
+ * Instead: allocate your own, using normal USB-IF procedures.
+ */
+
+/* Thanks to NetChip Technologies for donating this product ID.
+ * It's for devices with only CDC Ethernet configurations.
+ */
+#define CDC_VENDOR_NUM 0x0525 /* NetChip */
+#define CDC_PRODUCT_NUM 0xa4a1 /* Linux-USB Ethernet Gadget */
+
+/*-------------------------------------------------------------------------*/
+
+static struct usb_device_descriptor device_desc = {
+ .bLength = sizeof device_desc,
+ .bDescriptorType = USB_DT_DEVICE,
+
+ .bcdUSB = cpu_to_le16 (0x0200),
+
+ .bDeviceClass = USB_CLASS_COMM,
+ .bDeviceSubClass = 0,
+ .bDeviceProtocol = 0,
+ /* .bMaxPacketSize0 = f(hardware) */
+
+ /* Vendor and product id defaults change according to what configs
+ * we support. (As does bNumConfigurations.) These values can
+ * also be overridden by module parameters.
+ */
+ .idVendor = cpu_to_le16 (CDC_VENDOR_NUM),
+ .idProduct = cpu_to_le16 (CDC_PRODUCT_NUM),
+ /* .bcdDevice = f(hardware) */
+ /* .iManufacturer = DYNAMIC */
+ /* .iProduct = DYNAMIC */
+ /* NO SERIAL NUMBER */
+ .bNumConfigurations = 1,
+};
+
+static struct usb_otg_descriptor otg_descriptor = {
+ .bLength = sizeof otg_descriptor,
+ .bDescriptorType = USB_DT_OTG,
+
+ /* REVISIT SRP-only hardware is possible, although
+ * it would not be called "OTG" ...
+ */
+ .bmAttributes = USB_OTG_SRP | USB_OTG_HNP,
+};
+
+static const struct usb_descriptor_header *otg_desc[] = {
+ (struct usb_descriptor_header *) &otg_descriptor,
+ NULL,
+};
+
+
+/* string IDs are assigned dynamically */
+
+#define STRING_MANUFACTURER_IDX 0
+#define STRING_PRODUCT_IDX 1
+
+static char manufacturer[50];
+
+static struct usb_string strings_dev[] = {
+ [STRING_MANUFACTURER_IDX].s = manufacturer,
+ [STRING_PRODUCT_IDX].s = DRIVER_DESC,
+ { } /* end of list */
+};
+
+static struct usb_gadget_strings stringtab_dev = {
+ .language = 0x0409, /* en-us */
+ .strings = strings_dev,
+};
+
+static struct usb_gadget_strings *dev_strings[] = {
+ &stringtab_dev,
+ NULL,
+};
+
+static u8 hostaddr[ETH_ALEN];
+
+/*-------------------------------------------------------------------------*/
+
+static int __init ncm_do_config(struct usb_configuration *c)
+{
+ /* FIXME alloc iConfiguration string, set it in c->strings */
+
+ if (gadget_is_otg(c->cdev->gadget)) {
+ c->descriptors = otg_desc;
+ c->bmAttributes |= USB_CONFIG_ATT_WAKEUP;
+ }
+
+ return ncm_bind_config(c, hostaddr);
+}
+
+static struct usb_configuration ncm_config_driver = {
+ /* .label = f(hardware) */
+ .label = "CDC Ethernet (NCM)",
+ .bConfigurationValue = 1,
+ /* .iConfiguration = DYNAMIC */
+ .bmAttributes = USB_CONFIG_ATT_SELFPOWER,
+};
+
+/*-------------------------------------------------------------------------*/
+
+static int __init gncm_bind(struct usb_composite_dev *cdev)
+{
+ int gcnum;
+ struct usb_gadget *gadget = cdev->gadget;
+ int status;
+
+ /* set up network link layer */
+ status = gether_setup(cdev->gadget, hostaddr);
+ if (status < 0)
+ return status;
+
+ gcnum = usb_gadget_controller_number(gadget);
+ if (gcnum >= 0)
+ device_desc.bcdDevice = cpu_to_le16(0x0300 | gcnum);
+ else {
+ /* We assume that can_support_ecm() tells the truth;
+ * but if the controller isn't recognized at all then
+ * that assumption is a bit more likely to be wrong.
+ */
+ dev_warn(&gadget->dev,
+ "controller '%s' not recognized; trying %s\n",
+ gadget->name,
+ ncm_config_driver.label);
+ device_desc.bcdDevice =
+ cpu_to_le16(0x0300 | 0x0099);
+ }
+
+
+ /* Allocate string descriptor numbers ... note that string
+ * contents can be overridden by the composite_dev glue.
+ */
+
+ /* device descriptor strings: manufacturer, product */
+ snprintf(manufacturer, sizeof manufacturer, "%s %s with %s",
+ init_utsname()->sysname, init_utsname()->release,
+ gadget->name);
+ status = usb_string_id(cdev);
+ if (status < 0)
+ goto fail;
+ strings_dev[STRING_MANUFACTURER_IDX].id = status;
+ device_desc.iManufacturer = status;
+
+ status = usb_string_id(cdev);
+ if (status < 0)
+ goto fail;
+ strings_dev[STRING_PRODUCT_IDX].id = status;
+ device_desc.iProduct = status;
+
+ status = usb_add_config(cdev, &ncm_config_driver,
+ ncm_do_config);
+ if (status < 0)
+ goto fail;
+
+ dev_info(&gadget->dev, "%s\n", DRIVER_DESC);
+
+ return 0;
+
+fail:
+ gether_cleanup();
+ return status;
+}
+
+static int __exit gncm_unbind(struct usb_composite_dev *cdev)
+{
+ gether_cleanup();
+ return 0;
+}
+
+static struct usb_composite_driver ncm_driver = {
+ .name = "g_ncm",
+ .dev = &device_desc,
+ .strings = dev_strings,
+ .unbind = __exit_p(gncm_unbind),
+};
+
+MODULE_DESCRIPTION(DRIVER_DESC);
+MODULE_AUTHOR("Yauheni Kaliuta");
+MODULE_LICENSE("GPL");
+
+static int __init init(void)
+{
+ return usb_composite_probe(&ncm_driver, gncm_bind);
+}
+module_init(init);
+
+static void __exit cleanup(void)
+{
+ usb_composite_unregister(&ncm_driver);
+}
+module_exit(cleanup);
diff --git a/drivers/usb/gadget/pch_udc.c b/drivers/usb/gadget/pch_udc.c
new file mode 100644
index 00000000000..0c8dd81dddc
--- /dev/null
+++ b/drivers/usb/gadget/pch_udc.c
@@ -0,0 +1,2947 @@
+/*
+ * Copyright (C) 2010 OKI SEMICONDUCTOR CO., LTD.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; version 2 of the License.
+ *
+ * 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 pr_fmt(fmt) KBUILD_MODNAME ": " fmt
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/pci.h>
+#include <linux/delay.h>
+#include <linux/errno.h>
+#include <linux/list.h>
+#include <linux/interrupt.h>
+#include <linux/usb/ch9.h>
+#include <linux/usb/gadget.h>
+
+/* Address offset of Registers */
+#define UDC_EP_REG_SHIFT 0x20 /* Offset to next EP */
+
+#define UDC_EPCTL_ADDR 0x00 /* Endpoint control */
+#define UDC_EPSTS_ADDR 0x04 /* Endpoint status */
+#define UDC_BUFIN_FRAMENUM_ADDR 0x08 /* buffer size in / frame number out */
+#define UDC_BUFOUT_MAXPKT_ADDR 0x0C /* buffer size out / maxpkt in */
+#define UDC_SUBPTR_ADDR 0x10 /* setup buffer pointer */
+#define UDC_DESPTR_ADDR 0x14 /* Data descriptor pointer */
+#define UDC_CONFIRM_ADDR 0x18 /* Write/Read confirmation */
+
+#define UDC_DEVCFG_ADDR 0x400 /* Device configuration */
+#define UDC_DEVCTL_ADDR 0x404 /* Device control */
+#define UDC_DEVSTS_ADDR 0x408 /* Device status */
+#define UDC_DEVIRQSTS_ADDR 0x40C /* Device irq status */
+#define UDC_DEVIRQMSK_ADDR 0x410 /* Device irq mask */
+#define UDC_EPIRQSTS_ADDR 0x414 /* Endpoint irq status */
+#define UDC_EPIRQMSK_ADDR 0x418 /* Endpoint irq mask */
+#define UDC_DEVLPM_ADDR 0x41C /* LPM control / status */
+#define UDC_CSR_BUSY_ADDR 0x4f0 /* UDC_CSR_BUSY Status register */
+#define UDC_SRST_ADDR 0x4fc /* SOFT RESET register */
+#define UDC_CSR_ADDR 0x500 /* USB_DEVICE endpoint register */
+
+/* Endpoint control register */
+/* Bit position */
+#define UDC_EPCTL_MRXFLUSH (1 << 12)
+#define UDC_EPCTL_RRDY (1 << 9)
+#define UDC_EPCTL_CNAK (1 << 8)
+#define UDC_EPCTL_SNAK (1 << 7)
+#define UDC_EPCTL_NAK (1 << 6)
+#define UDC_EPCTL_P (1 << 3)
+#define UDC_EPCTL_F (1 << 1)
+#define UDC_EPCTL_S (1 << 0)
+#define UDC_EPCTL_ET_SHIFT 4
+/* Mask patern */
+#define UDC_EPCTL_ET_MASK 0x00000030
+/* Value for ET field */
+#define UDC_EPCTL_ET_CONTROL 0
+#define UDC_EPCTL_ET_ISO 1
+#define UDC_EPCTL_ET_BULK 2
+#define UDC_EPCTL_ET_INTERRUPT 3
+
+/* Endpoint status register */
+/* Bit position */
+#define UDC_EPSTS_XFERDONE (1 << 27)
+#define UDC_EPSTS_RSS (1 << 26)
+#define UDC_EPSTS_RCS (1 << 25)
+#define UDC_EPSTS_TXEMPTY (1 << 24)
+#define UDC_EPSTS_TDC (1 << 10)
+#define UDC_EPSTS_HE (1 << 9)
+#define UDC_EPSTS_MRXFIFO_EMP (1 << 8)
+#define UDC_EPSTS_BNA (1 << 7)
+#define UDC_EPSTS_IN (1 << 6)
+#define UDC_EPSTS_OUT_SHIFT 4
+/* Mask patern */
+#define UDC_EPSTS_OUT_MASK 0x00000030
+#define UDC_EPSTS_ALL_CLR_MASK 0x1F0006F0
+/* Value for OUT field */
+#define UDC_EPSTS_OUT_SETUP 2
+#define UDC_EPSTS_OUT_DATA 1
+
+/* Device configuration register */
+/* Bit position */
+#define UDC_DEVCFG_CSR_PRG (1 << 17)
+#define UDC_DEVCFG_SP (1 << 3)
+/* SPD Valee */
+#define UDC_DEVCFG_SPD_HS 0x0
+#define UDC_DEVCFG_SPD_FS 0x1
+#define UDC_DEVCFG_SPD_LS 0x2
+
+/* Device control register */
+/* Bit position */
+#define UDC_DEVCTL_THLEN_SHIFT 24
+#define UDC_DEVCTL_BRLEN_SHIFT 16
+#define UDC_DEVCTL_CSR_DONE (1 << 13)
+#define UDC_DEVCTL_SD (1 << 10)
+#define UDC_DEVCTL_MODE (1 << 9)
+#define UDC_DEVCTL_BREN (1 << 8)
+#define UDC_DEVCTL_THE (1 << 7)
+#define UDC_DEVCTL_DU (1 << 4)
+#define UDC_DEVCTL_TDE (1 << 3)
+#define UDC_DEVCTL_RDE (1 << 2)
+#define UDC_DEVCTL_RES (1 << 0)
+
+/* Device status register */
+/* Bit position */
+#define UDC_DEVSTS_TS_SHIFT 18
+#define UDC_DEVSTS_ENUM_SPEED_SHIFT 13
+#define UDC_DEVSTS_ALT_SHIFT 8
+#define UDC_DEVSTS_INTF_SHIFT 4
+#define UDC_DEVSTS_CFG_SHIFT 0
+/* Mask patern */
+#define UDC_DEVSTS_TS_MASK 0xfffc0000
+#define UDC_DEVSTS_ENUM_SPEED_MASK 0x00006000
+#define UDC_DEVSTS_ALT_MASK 0x00000f00
+#define UDC_DEVSTS_INTF_MASK 0x000000f0
+#define UDC_DEVSTS_CFG_MASK 0x0000000f
+/* value for maximum speed for SPEED field */
+#define UDC_DEVSTS_ENUM_SPEED_FULL 1
+#define UDC_DEVSTS_ENUM_SPEED_HIGH 0
+#define UDC_DEVSTS_ENUM_SPEED_LOW 2
+#define UDC_DEVSTS_ENUM_SPEED_FULLX 3
+
+/* Device irq register */
+/* Bit position */
+#define UDC_DEVINT_RWKP (1 << 7)
+#define UDC_DEVINT_ENUM (1 << 6)
+#define UDC_DEVINT_SOF (1 << 5)
+#define UDC_DEVINT_US (1 << 4)
+#define UDC_DEVINT_UR (1 << 3)
+#define UDC_DEVINT_ES (1 << 2)
+#define UDC_DEVINT_SI (1 << 1)
+#define UDC_DEVINT_SC (1 << 0)
+/* Mask patern */
+#define UDC_DEVINT_MSK 0x7f
+
+/* Endpoint irq register */
+/* Bit position */
+#define UDC_EPINT_IN_SHIFT 0
+#define UDC_EPINT_OUT_SHIFT 16
+#define UDC_EPINT_IN_EP0 (1 << 0)
+#define UDC_EPINT_OUT_EP0 (1 << 16)
+/* Mask patern */
+#define UDC_EPINT_MSK_DISABLE_ALL 0xffffffff
+
+/* UDC_CSR_BUSY Status register */
+/* Bit position */
+#define UDC_CSR_BUSY (1 << 0)
+
+/* SOFT RESET register */
+/* Bit position */
+#define UDC_PSRST (1 << 1)
+#define UDC_SRST (1 << 0)
+
+/* USB_DEVICE endpoint register */
+/* Bit position */
+#define UDC_CSR_NE_NUM_SHIFT 0
+#define UDC_CSR_NE_DIR_SHIFT 4
+#define UDC_CSR_NE_TYPE_SHIFT 5
+#define UDC_CSR_NE_CFG_SHIFT 7
+#define UDC_CSR_NE_INTF_SHIFT 11
+#define UDC_CSR_NE_ALT_SHIFT 15
+#define UDC_CSR_NE_MAX_PKT_SHIFT 19
+/* Mask patern */
+#define UDC_CSR_NE_NUM_MASK 0x0000000f
+#define UDC_CSR_NE_DIR_MASK 0x00000010
+#define UDC_CSR_NE_TYPE_MASK 0x00000060
+#define UDC_CSR_NE_CFG_MASK 0x00000780
+#define UDC_CSR_NE_INTF_MASK 0x00007800
+#define UDC_CSR_NE_ALT_MASK 0x00078000
+#define UDC_CSR_NE_MAX_PKT_MASK 0x3ff80000
+
+#define PCH_UDC_CSR(ep) (UDC_CSR_ADDR + ep*4)
+#define PCH_UDC_EPINT(in, num)\
+ (1 << (num + (in ? UDC_EPINT_IN_SHIFT : UDC_EPINT_OUT_SHIFT)))
+
+/* Index of endpoint */
+#define UDC_EP0IN_IDX 0
+#define UDC_EP0OUT_IDX 1
+#define UDC_EPIN_IDX(ep) (ep * 2)
+#define UDC_EPOUT_IDX(ep) (ep * 2 + 1)
+#define PCH_UDC_EP0 0
+#define PCH_UDC_EP1 1
+#define PCH_UDC_EP2 2
+#define PCH_UDC_EP3 3
+
+/* Number of endpoint */
+#define PCH_UDC_EP_NUM 32 /* Total number of EPs (16 IN,16 OUT) */
+#define PCH_UDC_USED_EP_NUM 4 /* EP number of EP's really used */
+/* Length Value */
+#define PCH_UDC_BRLEN 0x0F /* Burst length */
+#define PCH_UDC_THLEN 0x1F /* Threshold length */
+/* Value of EP Buffer Size */
+#define UDC_EP0IN_BUFF_SIZE 64
+#define UDC_EPIN_BUFF_SIZE 512
+#define UDC_EP0OUT_BUFF_SIZE 64
+#define UDC_EPOUT_BUFF_SIZE 512
+/* Value of EP maximum packet size */
+#define UDC_EP0IN_MAX_PKT_SIZE 64
+#define UDC_EP0OUT_MAX_PKT_SIZE 64
+#define UDC_BULK_MAX_PKT_SIZE 512
+
+/* DMA */
+#define DMA_DIR_RX 1 /* DMA for data receive */
+#define DMA_DIR_TX 2 /* DMA for data transmit */
+#define DMA_ADDR_INVALID (~(dma_addr_t)0)
+#define UDC_DMA_MAXPACKET 65536 /* maximum packet size for DMA */
+
+/**
+ * struct pch_udc_data_dma_desc - Structure to hold DMA descriptor information
+ * for data
+ * @status: Status quadlet
+ * @reserved: Reserved
+ * @dataptr: Buffer descriptor
+ * @next: Next descriptor
+ */
+struct pch_udc_data_dma_desc {
+ u32 status;
+ u32 reserved;
+ u32 dataptr;
+ u32 next;
+};
+
+/**
+ * struct pch_udc_stp_dma_desc - Structure to hold DMA descriptor information
+ * for control data
+ * @status: Status
+ * @reserved: Reserved
+ * @data12: First setup word
+ * @data34: Second setup word
+ */
+struct pch_udc_stp_dma_desc {
+ u32 status;
+ u32 reserved;
+ struct usb_ctrlrequest request;
+} __attribute((packed));
+
+/* DMA status definitions */
+/* Buffer status */
+#define PCH_UDC_BUFF_STS 0xC0000000
+#define PCH_UDC_BS_HST_RDY 0x00000000
+#define PCH_UDC_BS_DMA_BSY 0x40000000
+#define PCH_UDC_BS_DMA_DONE 0x80000000
+#define PCH_UDC_BS_HST_BSY 0xC0000000
+/* Rx/Tx Status */
+#define PCH_UDC_RXTX_STS 0x30000000
+#define PCH_UDC_RTS_SUCC 0x00000000
+#define PCH_UDC_RTS_DESERR 0x10000000
+#define PCH_UDC_RTS_BUFERR 0x30000000
+/* Last Descriptor Indication */
+#define PCH_UDC_DMA_LAST 0x08000000
+/* Number of Rx/Tx Bytes Mask */
+#define PCH_UDC_RXTX_BYTES 0x0000ffff
+
+/**
+ * struct pch_udc_cfg_data - Structure to hold current configuration
+ * and interface information
+ * @cur_cfg: current configuration in use
+ * @cur_intf: current interface in use
+ * @cur_alt: current alt interface in use
+ */
+struct pch_udc_cfg_data {
+ u16 cur_cfg;
+ u16 cur_intf;
+ u16 cur_alt;
+};
+
+/**
+ * struct pch_udc_ep - Structure holding a PCH USB device Endpoint information
+ * @ep: embedded ep request
+ * @td_stp_phys: for setup request
+ * @td_data_phys: for data request
+ * @td_stp: for setup request
+ * @td_data: for data request
+ * @dev: reference to device struct
+ * @offset_addr: offset address of ep register
+ * @desc: for this ep
+ * @queue: queue for requests
+ * @num: endpoint number
+ * @in: endpoint is IN
+ * @halted: endpoint halted?
+ * @epsts: Endpoint status
+ */
+struct pch_udc_ep {
+ struct usb_ep ep;
+ dma_addr_t td_stp_phys;
+ dma_addr_t td_data_phys;
+ struct pch_udc_stp_dma_desc *td_stp;
+ struct pch_udc_data_dma_desc *td_data;
+ struct pch_udc_dev *dev;
+ unsigned long offset_addr;
+ const struct usb_endpoint_descriptor *desc;
+ struct list_head queue;
+ unsigned num:5,
+ in:1,
+ halted:1;
+ unsigned long epsts;
+};
+
+/**
+ * struct pch_udc_dev - Structure holding complete information
+ * of the PCH USB device
+ * @gadget: gadget driver data
+ * @driver: reference to gadget driver bound
+ * @pdev: reference to the PCI device
+ * @ep: array of endpoints
+ * @lock: protects all state
+ * @active: enabled the PCI device
+ * @stall: stall requested
+ * @prot_stall: protcol stall requested
+ * @irq_registered: irq registered with system
+ * @mem_region: device memory mapped
+ * @registered: driver regsitered with system
+ * @suspended: driver in suspended state
+ * @connected: gadget driver associated
+ * @set_cfg_not_acked: pending acknowledgement 4 setup
+ * @waiting_zlp_ack: pending acknowledgement 4 ZLP
+ * @data_requests: DMA pool for data requests
+ * @stp_requests: DMA pool for setup requests
+ * @dma_addr: DMA pool for received
+ * @ep0out_buf: Buffer for DMA
+ * @setup_data: Received setup data
+ * @phys_addr: of device memory
+ * @base_addr: for mapped device memory
+ * @irq: IRQ line for the device
+ * @cfg_data: current cfg, intf, and alt in use
+ */
+struct pch_udc_dev {
+ struct usb_gadget gadget;
+ struct usb_gadget_driver *driver;
+ struct pci_dev *pdev;
+ struct pch_udc_ep ep[PCH_UDC_EP_NUM];
+ spinlock_t lock; /* protects all state */
+ unsigned active:1,
+ stall:1,
+ prot_stall:1,
+ irq_registered:1,
+ mem_region:1,
+ registered:1,
+ suspended:1,
+ connected:1,
+ set_cfg_not_acked:1,
+ waiting_zlp_ack:1;
+ struct pci_pool *data_requests;
+ struct pci_pool *stp_requests;
+ dma_addr_t dma_addr;
+ unsigned long ep0out_buf[64];
+ struct usb_ctrlrequest setup_data;
+ unsigned long phys_addr;
+ void __iomem *base_addr;
+ unsigned irq;
+ struct pch_udc_cfg_data cfg_data;
+};
+
+#define PCH_UDC_PCI_BAR 1
+#define PCI_DEVICE_ID_INTEL_EG20T_UDC 0x8808
+
+static const char ep0_string[] = "ep0in";
+static DEFINE_SPINLOCK(udc_stall_spinlock); /* stall spin lock */
+struct pch_udc_dev *pch_udc; /* pointer to device object */
+
+static int speed_fs;
+module_param_named(speed_fs, speed_fs, bool, S_IRUGO);
+MODULE_PARM_DESC(speed_fs, "true for Full speed operation");
+
+/**
+ * struct pch_udc_request - Structure holding a PCH USB device request packet
+ * @req: embedded ep request
+ * @td_data_phys: phys. address
+ * @td_data: first dma desc. of chain
+ * @td_data_last: last dma desc. of chain
+ * @queue: associated queue
+ * @dma_going: DMA in progress for request
+ * @dma_mapped: DMA memory mapped for request
+ * @dma_done: DMA completed for request
+ * @chain_len: chain length
+ */
+struct pch_udc_request {
+ struct usb_request req;
+ dma_addr_t td_data_phys;
+ struct pch_udc_data_dma_desc *td_data;
+ struct pch_udc_data_dma_desc *td_data_last;
+ struct list_head queue;
+ unsigned dma_going:1,
+ dma_mapped:1,
+ dma_done:1;
+ unsigned chain_len;
+};
+
+static inline u32 pch_udc_readl(struct pch_udc_dev *dev, unsigned long reg)
+{
+ return ioread32(dev->base_addr + reg);
+}
+
+static inline void pch_udc_writel(struct pch_udc_dev *dev,
+ unsigned long val, unsigned long reg)
+{
+ iowrite32(val, dev->base_addr + reg);
+}
+
+static inline void pch_udc_bit_set(struct pch_udc_dev *dev,
+ unsigned long reg,
+ unsigned long bitmask)
+{
+ pch_udc_writel(dev, pch_udc_readl(dev, reg) | bitmask, reg);
+}
+
+static inline void pch_udc_bit_clr(struct pch_udc_dev *dev,
+ unsigned long reg,
+ unsigned long bitmask)
+{
+ pch_udc_writel(dev, pch_udc_readl(dev, reg) & ~(bitmask), reg);
+}
+
+static inline u32 pch_udc_ep_readl(struct pch_udc_ep *ep, unsigned long reg)
+{
+ return ioread32(ep->dev->base_addr + ep->offset_addr + reg);
+}
+
+static inline void pch_udc_ep_writel(struct pch_udc_ep *ep,
+ unsigned long val, unsigned long reg)
+{
+ iowrite32(val, ep->dev->base_addr + ep->offset_addr + reg);
+}
+
+static inline void pch_udc_ep_bit_set(struct pch_udc_ep *ep,
+ unsigned long reg,
+ unsigned long bitmask)
+{
+ pch_udc_ep_writel(ep, pch_udc_ep_readl(ep, reg) | bitmask, reg);
+}
+
+static inline void pch_udc_ep_bit_clr(struct pch_udc_ep *ep,
+ unsigned long reg,
+ unsigned long bitmask)
+{
+ pch_udc_ep_writel(ep, pch_udc_ep_readl(ep, reg) & ~(bitmask), reg);
+}
+
+/**
+ * pch_udc_csr_busy() - Wait till idle.
+ * @dev: Reference to pch_udc_dev structure
+ */
+static void pch_udc_csr_busy(struct pch_udc_dev *dev)
+{
+ unsigned int count = 200;
+
+ /* Wait till idle */
+ while ((pch_udc_readl(dev, UDC_CSR_BUSY_ADDR) & UDC_CSR_BUSY)
+ && --count)
+ cpu_relax();
+ if (!count)
+ dev_err(&dev->pdev->dev, "%s: wait error\n", __func__);
+}
+
+/**
+ * pch_udc_write_csr() - Write the command and status registers.
+ * @dev: Reference to pch_udc_dev structure
+ * @val: value to be written to CSR register
+ * @addr: address of CSR register
+ */
+static void pch_udc_write_csr(struct pch_udc_dev *dev, unsigned long val,
+ unsigned int ep)
+{
+ unsigned long reg = PCH_UDC_CSR(ep);
+
+ pch_udc_csr_busy(dev); /* Wait till idle */
+ pch_udc_writel(dev, val, reg);
+ pch_udc_csr_busy(dev); /* Wait till idle */
+}
+
+/**
+ * pch_udc_read_csr() - Read the command and status registers.
+ * @dev: Reference to pch_udc_dev structure
+ * @addr: address of CSR register
+ *
+ * Return codes: content of CSR register
+ */
+static u32 pch_udc_read_csr(struct pch_udc_dev *dev, unsigned int ep)
+{
+ unsigned long reg = PCH_UDC_CSR(ep);
+
+ pch_udc_csr_busy(dev); /* Wait till idle */
+ pch_udc_readl(dev, reg); /* Dummy read */
+ pch_udc_csr_busy(dev); /* Wait till idle */
+ return pch_udc_readl(dev, reg);
+}
+
+/**
+ * pch_udc_rmt_wakeup() - Initiate for remote wakeup
+ * @dev: Reference to pch_udc_dev structure
+ */
+static inline void pch_udc_rmt_wakeup(struct pch_udc_dev *dev)
+{
+ pch_udc_bit_set(dev, UDC_DEVCTL_ADDR, UDC_DEVCTL_RES);
+ mdelay(1);
+ pch_udc_bit_clr(dev, UDC_DEVCTL_ADDR, UDC_DEVCTL_RES);
+}
+
+/**
+ * pch_udc_get_frame() - Get the current frame from device status register
+ * @dev: Reference to pch_udc_dev structure
+ * Retern current frame
+ */
+static inline int pch_udc_get_frame(struct pch_udc_dev *dev)
+{
+ u32 frame = pch_udc_readl(dev, UDC_DEVSTS_ADDR);
+ return (frame & UDC_DEVSTS_TS_MASK) >> UDC_DEVSTS_TS_SHIFT;
+}
+
+/**
+ * pch_udc_clear_selfpowered() - Clear the self power control
+ * @dev: Reference to pch_udc_regs structure
+ */
+static inline void pch_udc_clear_selfpowered(struct pch_udc_dev *dev)
+{
+ pch_udc_bit_clr(dev, UDC_DEVCFG_ADDR, UDC_DEVCFG_SP);
+}
+
+/**
+ * pch_udc_set_selfpowered() - Set the self power control
+ * @dev: Reference to pch_udc_regs structure
+ */
+static inline void pch_udc_set_selfpowered(struct pch_udc_dev *dev)
+{
+ pch_udc_bit_set(dev, UDC_DEVCFG_ADDR, UDC_DEVCFG_SP);
+}
+
+/**
+ * pch_udc_set_disconnect() - Set the disconnect status.
+ * @dev: Reference to pch_udc_regs structure
+ */
+static inline void pch_udc_set_disconnect(struct pch_udc_dev *dev)
+{
+ pch_udc_bit_set(dev, UDC_DEVCTL_ADDR, UDC_DEVCTL_SD);
+}
+
+/**
+ * pch_udc_clear_disconnect() - Clear the disconnect status.
+ * @dev: Reference to pch_udc_regs structure
+ */
+static void pch_udc_clear_disconnect(struct pch_udc_dev *dev)
+{
+ /* Clear the disconnect */
+ pch_udc_bit_set(dev, UDC_DEVCTL_ADDR, UDC_DEVCTL_RES);
+ pch_udc_bit_clr(dev, UDC_DEVCTL_ADDR, UDC_DEVCTL_SD);
+ mdelay(1);
+ /* Resume USB signalling */
+ pch_udc_bit_clr(dev, UDC_DEVCTL_ADDR, UDC_DEVCTL_RES);
+}
+
+/**
+ * pch_udc_vbus_session() - set or clearr the disconnect status.
+ * @dev: Reference to pch_udc_regs structure
+ * @is_active: Parameter specifying the action
+ * 0: indicating VBUS power is ending
+ * !0: indicating VBUS power is starting
+ */
+static inline void pch_udc_vbus_session(struct pch_udc_dev *dev,
+ int is_active)
+{
+ if (is_active)
+ pch_udc_clear_disconnect(dev);
+ else
+ pch_udc_set_disconnect(dev);
+}
+
+/**
+ * pch_udc_ep_set_stall() - Set the stall of endpoint
+ * @ep: Reference to structure of type pch_udc_ep_regs
+ */
+static void pch_udc_ep_set_stall(struct pch_udc_ep *ep)
+{
+ if (ep->in) {
+ pch_udc_ep_bit_set(ep, UDC_EPCTL_ADDR, UDC_EPCTL_F);
+ pch_udc_ep_bit_set(ep, UDC_EPCTL_ADDR, UDC_EPCTL_S);
+ } else {
+ pch_udc_ep_bit_set(ep, UDC_EPCTL_ADDR, UDC_EPCTL_S);
+ }
+}
+
+/**
+ * pch_udc_ep_clear_stall() - Clear the stall of endpoint
+ * @ep: Reference to structure of type pch_udc_ep_regs
+ */
+static inline void pch_udc_ep_clear_stall(struct pch_udc_ep *ep)
+{
+ /* Clear the stall */
+ pch_udc_ep_bit_clr(ep, UDC_EPCTL_ADDR, UDC_EPCTL_S);
+ /* Clear NAK by writing CNAK */
+ pch_udc_ep_bit_set(ep, UDC_EPCTL_ADDR, UDC_EPCTL_CNAK);
+}
+
+/**
+ * pch_udc_ep_set_trfr_type() - Set the transfer type of endpoint
+ * @ep: Reference to structure of type pch_udc_ep_regs
+ * @type: Type of endpoint
+ */
+static inline void pch_udc_ep_set_trfr_type(struct pch_udc_ep *ep,
+ u8 type)
+{
+ pch_udc_ep_writel(ep, ((type << UDC_EPCTL_ET_SHIFT) &
+ UDC_EPCTL_ET_MASK), UDC_EPCTL_ADDR);
+}
+
+/**
+ * pch_udc_ep_set_bufsz() - Set the maximum packet size for the endpoint
+ * @ep: Reference to structure of type pch_udc_ep_regs
+ * @buf_size: The buffer size
+ */
+static void pch_udc_ep_set_bufsz(struct pch_udc_ep *ep,
+ u32 buf_size, u32 ep_in)
+{
+ u32 data;
+ if (ep_in) {
+ data = pch_udc_ep_readl(ep, UDC_BUFIN_FRAMENUM_ADDR);
+ data = (data & 0xffff0000) | (buf_size & 0xffff);
+ pch_udc_ep_writel(ep, data, UDC_BUFIN_FRAMENUM_ADDR);
+ } else {
+ data = pch_udc_ep_readl(ep, UDC_BUFOUT_MAXPKT_ADDR);
+ data = (buf_size << 16) | (data & 0xffff);
+ pch_udc_ep_writel(ep, data, UDC_BUFOUT_MAXPKT_ADDR);
+ }
+}
+
+/**
+ * pch_udc_ep_set_maxpkt() - Set the Max packet size for the endpoint
+ * @ep: Reference to structure of type pch_udc_ep_regs
+ * @pkt_size: The packet size
+ */
+static void pch_udc_ep_set_maxpkt(struct pch_udc_ep *ep, u32 pkt_size)
+{
+ u32 data = pch_udc_ep_readl(ep, UDC_BUFOUT_MAXPKT_ADDR);
+ data = (data & 0xffff0000) | (pkt_size & 0xffff);
+ pch_udc_ep_writel(ep, data, UDC_BUFOUT_MAXPKT_ADDR);
+}
+
+/**
+ * pch_udc_ep_set_subptr() - Set the Setup buffer pointer for the endpoint
+ * @ep: Reference to structure of type pch_udc_ep_regs
+ * @addr: Address of the register
+ */
+static inline void pch_udc_ep_set_subptr(struct pch_udc_ep *ep, u32 addr)
+{
+ pch_udc_ep_writel(ep, addr, UDC_SUBPTR_ADDR);
+}
+
+/**
+ * pch_udc_ep_set_ddptr() - Set the Data descriptor pointer for the endpoint
+ * @ep: Reference to structure of type pch_udc_ep_regs
+ * @addr: Address of the register
+ */
+static inline void pch_udc_ep_set_ddptr(struct pch_udc_ep *ep, u32 addr)
+{
+ pch_udc_ep_writel(ep, addr, UDC_DESPTR_ADDR);
+}
+
+/**
+ * pch_udc_ep_set_pd() - Set the poll demand bit for the endpoint
+ * @ep: Reference to structure of type pch_udc_ep_regs
+ */
+static inline void pch_udc_ep_set_pd(struct pch_udc_ep *ep)
+{
+ pch_udc_ep_bit_set(ep, UDC_EPCTL_ADDR, UDC_EPCTL_P);
+}
+
+/**
+ * pch_udc_ep_set_rrdy() - Set the receive ready bit for the endpoint
+ * @ep: Reference to structure of type pch_udc_ep_regs
+ */
+static inline void pch_udc_ep_set_rrdy(struct pch_udc_ep *ep)
+{
+ pch_udc_ep_bit_set(ep, UDC_EPCTL_ADDR, UDC_EPCTL_RRDY);
+}
+
+/**
+ * pch_udc_ep_clear_rrdy() - Clear the receive ready bit for the endpoint
+ * @ep: Reference to structure of type pch_udc_ep_regs
+ */
+static inline void pch_udc_ep_clear_rrdy(struct pch_udc_ep *ep)
+{
+ pch_udc_ep_bit_clr(ep, UDC_EPCTL_ADDR, UDC_EPCTL_RRDY);
+}
+
+/**
+ * pch_udc_set_dma() - Set the 'TDE' or RDE bit of device control
+ * register depending on the direction specified
+ * @dev: Reference to structure of type pch_udc_regs
+ * @dir: whether Tx or Rx
+ * DMA_DIR_RX: Receive
+ * DMA_DIR_TX: Transmit
+ */
+static inline void pch_udc_set_dma(struct pch_udc_dev *dev, int dir)
+{
+ if (dir == DMA_DIR_RX)
+ pch_udc_bit_set(dev, UDC_DEVCTL_ADDR, UDC_DEVCTL_RDE);
+ else if (dir == DMA_DIR_TX)
+ pch_udc_bit_set(dev, UDC_DEVCTL_ADDR, UDC_DEVCTL_TDE);
+}
+
+/**
+ * pch_udc_clear_dma() - Clear the 'TDE' or RDE bit of device control
+ * register depending on the direction specified
+ * @dev: Reference to structure of type pch_udc_regs
+ * @dir: Whether Tx or Rx
+ * DMA_DIR_RX: Receive
+ * DMA_DIR_TX: Transmit
+ */
+static inline void pch_udc_clear_dma(struct pch_udc_dev *dev, int dir)
+{
+ if (dir == DMA_DIR_RX)
+ pch_udc_bit_clr(dev, UDC_DEVCTL_ADDR, UDC_DEVCTL_RDE);
+ else if (dir == DMA_DIR_TX)
+ pch_udc_bit_clr(dev, UDC_DEVCTL_ADDR, UDC_DEVCTL_TDE);
+}
+
+/**
+ * pch_udc_set_csr_done() - Set the device control register
+ * CSR done field (bit 13)
+ * @dev: reference to structure of type pch_udc_regs
+ */
+static inline void pch_udc_set_csr_done(struct pch_udc_dev *dev)
+{
+ pch_udc_bit_set(dev, UDC_DEVCTL_ADDR, UDC_DEVCTL_CSR_DONE);
+}
+
+/**
+ * pch_udc_disable_interrupts() - Disables the specified interrupts
+ * @dev: Reference to structure of type pch_udc_regs
+ * @mask: Mask to disable interrupts
+ */
+static inline void pch_udc_disable_interrupts(struct pch_udc_dev *dev,
+ u32 mask)
+{
+ pch_udc_bit_set(dev, UDC_DEVIRQMSK_ADDR, mask);
+}
+
+/**
+ * pch_udc_enable_interrupts() - Enable the specified interrupts
+ * @dev: Reference to structure of type pch_udc_regs
+ * @mask: Mask to enable interrupts
+ */
+static inline void pch_udc_enable_interrupts(struct pch_udc_dev *dev,
+ u32 mask)
+{
+ pch_udc_bit_clr(dev, UDC_DEVIRQMSK_ADDR, mask);
+}
+
+/**
+ * pch_udc_disable_ep_interrupts() - Disable endpoint interrupts
+ * @dev: Reference to structure of type pch_udc_regs
+ * @mask: Mask to disable interrupts
+ */
+static inline void pch_udc_disable_ep_interrupts(struct pch_udc_dev *dev,
+ u32 mask)
+{
+ pch_udc_bit_set(dev, UDC_EPIRQMSK_ADDR, mask);
+}
+
+/**
+ * pch_udc_enable_ep_interrupts() - Enable endpoint interrupts
+ * @dev: Reference to structure of type pch_udc_regs
+ * @mask: Mask to enable interrupts
+ */
+static inline void pch_udc_enable_ep_interrupts(struct pch_udc_dev *dev,
+ u32 mask)
+{
+ pch_udc_bit_clr(dev, UDC_EPIRQMSK_ADDR, mask);
+}
+
+/**
+ * pch_udc_read_device_interrupts() - Read the device interrupts
+ * @dev: Reference to structure of type pch_udc_regs
+ * Retern The device interrupts
+ */
+static inline u32 pch_udc_read_device_interrupts(struct pch_udc_dev *dev)
+{
+ return pch_udc_readl(dev, UDC_DEVIRQSTS_ADDR);
+}
+
+/**
+ * pch_udc_write_device_interrupts() - Write device interrupts
+ * @dev: Reference to structure of type pch_udc_regs
+ * @val: The value to be written to interrupt register
+ */
+static inline void pch_udc_write_device_interrupts(struct pch_udc_dev *dev,
+ u32 val)
+{
+ pch_udc_writel(dev, val, UDC_DEVIRQSTS_ADDR);
+}
+
+/**
+ * pch_udc_read_ep_interrupts() - Read the endpoint interrupts
+ * @dev: Reference to structure of type pch_udc_regs
+ * Retern The endpoint interrupt
+ */
+static inline u32 pch_udc_read_ep_interrupts(struct pch_udc_dev *dev)
+{
+ return pch_udc_readl(dev, UDC_EPIRQSTS_ADDR);
+}
+
+/**
+ * pch_udc_write_ep_interrupts() - Clear endpoint interupts
+ * @dev: Reference to structure of type pch_udc_regs
+ * @val: The value to be written to interrupt register
+ */
+static inline void pch_udc_write_ep_interrupts(struct pch_udc_dev *dev,
+ u32 val)
+{
+ pch_udc_writel(dev, val, UDC_EPIRQSTS_ADDR);
+}
+
+/**
+ * pch_udc_read_device_status() - Read the device status
+ * @dev: Reference to structure of type pch_udc_regs
+ * Retern The device status
+ */
+static inline u32 pch_udc_read_device_status(struct pch_udc_dev *dev)
+{
+ return pch_udc_readl(dev, UDC_DEVSTS_ADDR);
+}
+
+/**
+ * pch_udc_read_ep_control() - Read the endpoint control
+ * @ep: Reference to structure of type pch_udc_ep_regs
+ * Retern The endpoint control register value
+ */
+static inline u32 pch_udc_read_ep_control(struct pch_udc_ep *ep)
+{
+ return pch_udc_ep_readl(ep, UDC_EPCTL_ADDR);
+}
+
+/**
+ * pch_udc_clear_ep_control() - Clear the endpoint control register
+ * @ep: Reference to structure of type pch_udc_ep_regs
+ * Retern The endpoint control register value
+ */
+static inline void pch_udc_clear_ep_control(struct pch_udc_ep *ep)
+{
+ return pch_udc_ep_writel(ep, 0, UDC_EPCTL_ADDR);
+}
+
+/**
+ * pch_udc_read_ep_status() - Read the endpoint status
+ * @ep: Reference to structure of type pch_udc_ep_regs
+ * Retern The endpoint status
+ */
+static inline u32 pch_udc_read_ep_status(struct pch_udc_ep *ep)
+{
+ return pch_udc_ep_readl(ep, UDC_EPSTS_ADDR);
+}
+
+/**
+ * pch_udc_clear_ep_status() - Clear the endpoint status
+ * @ep: Reference to structure of type pch_udc_ep_regs
+ * @stat: Endpoint status
+ */
+static inline void pch_udc_clear_ep_status(struct pch_udc_ep *ep,
+ u32 stat)
+{
+ return pch_udc_ep_writel(ep, stat, UDC_EPSTS_ADDR);
+}
+
+/**
+ * pch_udc_ep_set_nak() - Set the bit 7 (SNAK field)
+ * of the endpoint control register
+ * @ep: Reference to structure of type pch_udc_ep_regs
+ */
+static inline void pch_udc_ep_set_nak(struct pch_udc_ep *ep)
+{
+ pch_udc_ep_bit_set(ep, UDC_EPCTL_ADDR, UDC_EPCTL_SNAK);
+}
+
+/**
+ * pch_udc_ep_clear_nak() - Set the bit 8 (CNAK field)
+ * of the endpoint control register
+ * @ep: reference to structure of type pch_udc_ep_regs
+ */
+static void pch_udc_ep_clear_nak(struct pch_udc_ep *ep)
+{
+ unsigned int loopcnt = 0;
+ struct pch_udc_dev *dev = ep->dev;
+
+ if (!(pch_udc_ep_readl(ep, UDC_EPCTL_ADDR) & UDC_EPCTL_NAK))
+ return;
+ if (!ep->in) {
+ loopcnt = 10000;
+ while (!(pch_udc_read_ep_status(ep) & UDC_EPSTS_MRXFIFO_EMP) &&
+ --loopcnt)
+ udelay(5);
+ if (!loopcnt)
+ dev_err(&dev->pdev->dev, "%s: RxFIFO not Empty\n",
+ __func__);
+ }
+ loopcnt = 10000;
+ while ((pch_udc_read_ep_control(ep) & UDC_EPCTL_NAK) && --loopcnt) {
+ pch_udc_ep_bit_set(ep, UDC_EPCTL_ADDR, UDC_EPCTL_CNAK);
+ udelay(5);
+ }
+ if (!loopcnt)
+ dev_err(&dev->pdev->dev, "%s: Clear NAK not set for ep%d%s\n",
+ __func__, ep->num, (ep->in ? "in" : "out"));
+}
+
+/**
+ * pch_udc_ep_fifo_flush() - Flush the endpoint fifo
+ * @ep: reference to structure of type pch_udc_ep_regs
+ * @dir: direction of endpoint
+ * 0: endpoint is OUT
+ * !0: endpoint is IN
+ */
+static void pch_udc_ep_fifo_flush(struct pch_udc_ep *ep, int dir)
+{
+ unsigned int loopcnt = 0;
+ struct pch_udc_dev *dev = ep->dev;
+
+ if (dir) { /* IN ep */
+ pch_udc_ep_bit_set(ep, UDC_EPCTL_ADDR, UDC_EPCTL_F);
+ return;
+ }
+
+ if (pch_udc_read_ep_status(ep) & UDC_EPSTS_MRXFIFO_EMP)
+ return;
+ pch_udc_ep_bit_set(ep, UDC_EPCTL_ADDR, UDC_EPCTL_MRXFLUSH);
+ /* Wait for RxFIFO Empty */
+ loopcnt = 10000;
+ while (!(pch_udc_read_ep_status(ep) & UDC_EPSTS_MRXFIFO_EMP) &&
+ --loopcnt)
+ udelay(5);
+ if (!loopcnt)
+ dev_err(&dev->pdev->dev, "RxFIFO not Empty\n");
+ pch_udc_ep_bit_clr(ep, UDC_EPCTL_ADDR, UDC_EPCTL_MRXFLUSH);
+}
+
+/**
+ * pch_udc_ep_enable() - This api enables endpoint
+ * @regs: Reference to structure pch_udc_ep_regs
+ * @desc: endpoint descriptor
+ */
+static void pch_udc_ep_enable(struct pch_udc_ep *ep,
+ struct pch_udc_cfg_data *cfg,
+ const struct usb_endpoint_descriptor *desc)
+{
+ u32 val = 0;
+ u32 buff_size = 0;
+
+ pch_udc_ep_set_trfr_type(ep, desc->bmAttributes);
+ if (ep->in)
+ buff_size = UDC_EPIN_BUFF_SIZE;
+ else
+ buff_size = UDC_EPOUT_BUFF_SIZE;
+ pch_udc_ep_set_bufsz(ep, buff_size, ep->in);
+ pch_udc_ep_set_maxpkt(ep, le16_to_cpu(desc->wMaxPacketSize));
+ pch_udc_ep_set_nak(ep);
+ pch_udc_ep_fifo_flush(ep, ep->in);
+ /* Configure the endpoint */
+ val = ep->num << UDC_CSR_NE_NUM_SHIFT | ep->in << UDC_CSR_NE_DIR_SHIFT |
+ ((desc->bmAttributes & USB_ENDPOINT_XFERTYPE_MASK) <<
+ UDC_CSR_NE_TYPE_SHIFT) |
+ (cfg->cur_cfg << UDC_CSR_NE_CFG_SHIFT) |
+ (cfg->cur_intf << UDC_CSR_NE_INTF_SHIFT) |
+ (cfg->cur_alt << UDC_CSR_NE_ALT_SHIFT) |
+ le16_to_cpu(desc->wMaxPacketSize) << UDC_CSR_NE_MAX_PKT_SHIFT;
+
+ if (ep->in)
+ pch_udc_write_csr(ep->dev, val, UDC_EPIN_IDX(ep->num));
+ else
+ pch_udc_write_csr(ep->dev, val, UDC_EPOUT_IDX(ep->num));
+}
+
+/**
+ * pch_udc_ep_disable() - This api disables endpoint
+ * @regs: Reference to structure pch_udc_ep_regs
+ */
+static void pch_udc_ep_disable(struct pch_udc_ep *ep)
+{
+ if (ep->in) {
+ /* flush the fifo */
+ pch_udc_ep_writel(ep, UDC_EPCTL_F, UDC_EPCTL_ADDR);
+ /* set NAK */
+ pch_udc_ep_writel(ep, UDC_EPCTL_SNAK, UDC_EPCTL_ADDR);
+ pch_udc_ep_bit_set(ep, UDC_EPSTS_ADDR, UDC_EPSTS_IN);
+ } else {
+ /* set NAK */
+ pch_udc_ep_writel(ep, UDC_EPCTL_SNAK, UDC_EPCTL_ADDR);
+ }
+ /* reset desc pointer */
+ pch_udc_ep_writel(ep, 0, UDC_DESPTR_ADDR);
+}
+
+/**
+ * pch_udc_wait_ep_stall() - Wait EP stall.
+ * @dev: Reference to pch_udc_dev structure
+ */
+static void pch_udc_wait_ep_stall(struct pch_udc_ep *ep)
+{
+ unsigned int count = 10000;
+
+ /* Wait till idle */
+ while ((pch_udc_read_ep_control(ep) & UDC_EPCTL_S) && --count)
+ udelay(5);
+ if (!count)
+ dev_err(&ep->dev->pdev->dev, "%s: wait error\n", __func__);
+}
+
+/**
+ * pch_udc_init() - This API initializes usb device controller
+ * @dev: Rreference to pch_udc_regs structure
+ */
+static void pch_udc_init(struct pch_udc_dev *dev)
+{
+ if (NULL == dev) {
+ pr_err("%s: Invalid address\n", __func__);
+ return;
+ }
+ /* Soft Reset and Reset PHY */
+ pch_udc_writel(dev, UDC_SRST, UDC_SRST_ADDR);
+ pch_udc_writel(dev, UDC_SRST | UDC_PSRST, UDC_SRST_ADDR);
+ mdelay(1);
+ pch_udc_writel(dev, UDC_SRST, UDC_SRST_ADDR);
+ pch_udc_writel(dev, 0x00, UDC_SRST_ADDR);
+ mdelay(1);
+ /* mask and clear all device interrupts */
+ pch_udc_bit_set(dev, UDC_DEVIRQMSK_ADDR, UDC_DEVINT_MSK);
+ pch_udc_bit_set(dev, UDC_DEVIRQSTS_ADDR, UDC_DEVINT_MSK);
+
+ /* mask and clear all ep interrupts */
+ pch_udc_bit_set(dev, UDC_EPIRQMSK_ADDR, UDC_EPINT_MSK_DISABLE_ALL);
+ pch_udc_bit_set(dev, UDC_EPIRQSTS_ADDR, UDC_EPINT_MSK_DISABLE_ALL);
+
+ /* enable dynamic CSR programmingi, self powered and device speed */
+ if (speed_fs)
+ pch_udc_bit_set(dev, UDC_DEVCFG_ADDR, UDC_DEVCFG_CSR_PRG |
+ UDC_DEVCFG_SP | UDC_DEVCFG_SPD_FS);
+ else /* defaul high speed */
+ pch_udc_bit_set(dev, UDC_DEVCFG_ADDR, UDC_DEVCFG_CSR_PRG |
+ UDC_DEVCFG_SP | UDC_DEVCFG_SPD_HS);
+ pch_udc_bit_set(dev, UDC_DEVCTL_ADDR,
+ (PCH_UDC_THLEN << UDC_DEVCTL_THLEN_SHIFT) |
+ (PCH_UDC_BRLEN << UDC_DEVCTL_BRLEN_SHIFT) |
+ UDC_DEVCTL_MODE | UDC_DEVCTL_BREN |
+ UDC_DEVCTL_THE);
+}
+
+/**
+ * pch_udc_exit() - This API exit usb device controller
+ * @dev: Reference to pch_udc_regs structure
+ */
+static void pch_udc_exit(struct pch_udc_dev *dev)
+{
+ /* mask all device interrupts */
+ pch_udc_bit_set(dev, UDC_DEVIRQMSK_ADDR, UDC_DEVINT_MSK);
+ /* mask all ep interrupts */
+ pch_udc_bit_set(dev, UDC_EPIRQMSK_ADDR, UDC_EPINT_MSK_DISABLE_ALL);
+ /* put device in disconnected state */
+ pch_udc_set_disconnect(dev);
+}
+
+/**
+ * pch_udc_pcd_get_frame() - This API is invoked to get the current frame number
+ * @gadget: Reference to the gadget driver
+ *
+ * Return codes:
+ * 0: Success
+ * -EINVAL: If the gadget passed is NULL
+ */
+static int pch_udc_pcd_get_frame(struct usb_gadget *gadget)
+{
+ struct pch_udc_dev *dev;
+
+ if (!gadget)
+ return -EINVAL;
+ dev = container_of(gadget, struct pch_udc_dev, gadget);
+ return pch_udc_get_frame(dev);
+}
+
+/**
+ * pch_udc_pcd_wakeup() - This API is invoked to initiate a remote wakeup
+ * @gadget: Reference to the gadget driver
+ *
+ * Return codes:
+ * 0: Success
+ * -EINVAL: If the gadget passed is NULL
+ */
+static int pch_udc_pcd_wakeup(struct usb_gadget *gadget)
+{
+ struct pch_udc_dev *dev;
+ unsigned long flags;
+
+ if (!gadget)
+ return -EINVAL;
+ dev = container_of(gadget, struct pch_udc_dev, gadget);
+ spin_lock_irqsave(&dev->lock, flags);
+ pch_udc_rmt_wakeup(dev);
+ spin_unlock_irqrestore(&dev->lock, flags);
+ return 0;
+}
+
+/**
+ * pch_udc_pcd_selfpowered() - This API is invoked to specify whether the device
+ * is self powered or not
+ * @gadget: Reference to the gadget driver
+ * @value: Specifies self powered or not
+ *
+ * Return codes:
+ * 0: Success
+ * -EINVAL: If the gadget passed is NULL
+ */
+static int pch_udc_pcd_selfpowered(struct usb_gadget *gadget, int value)
+{
+ struct pch_udc_dev *dev;
+
+ if (!gadget)
+ return -EINVAL;
+ dev = container_of(gadget, struct pch_udc_dev, gadget);
+ if (value)
+ pch_udc_set_selfpowered(dev);
+ else
+ pch_udc_clear_selfpowered(dev);
+ return 0;
+}
+
+/**
+ * pch_udc_pcd_pullup() - This API is invoked to make the device
+ * visible/invisible to the host
+ * @gadget: Reference to the gadget driver
+ * @is_on: Specifies whether the pull up is made active or inactive
+ *
+ * Return codes:
+ * 0: Success
+ * -EINVAL: If the gadget passed is NULL
+ */
+static int pch_udc_pcd_pullup(struct usb_gadget *gadget, int is_on)
+{
+ struct pch_udc_dev *dev;
+
+ if (!gadget)
+ return -EINVAL;
+ dev = container_of(gadget, struct pch_udc_dev, gadget);
+ pch_udc_vbus_session(dev, is_on);
+ return 0;
+}
+
+/**
+ * pch_udc_pcd_vbus_session() - This API is used by a driver for an external
+ * transceiver (or GPIO) that
+ * detects a VBUS power session starting/ending
+ * @gadget: Reference to the gadget driver
+ * @is_active: specifies whether the session is starting or ending
+ *
+ * Return codes:
+ * 0: Success
+ * -EINVAL: If the gadget passed is NULL
+ */
+static int pch_udc_pcd_vbus_session(struct usb_gadget *gadget, int is_active)
+{
+ struct pch_udc_dev *dev;
+
+ if (!gadget)
+ return -EINVAL;
+ dev = container_of(gadget, struct pch_udc_dev, gadget);
+ pch_udc_vbus_session(dev, is_active);
+ return 0;
+}
+
+/**
+ * pch_udc_pcd_vbus_draw() - This API is used by gadget drivers during
+ * SET_CONFIGURATION calls to
+ * specify how much power the device can consume
+ * @gadget: Reference to the gadget driver
+ * @mA: specifies the current limit in 2mA unit
+ *
+ * Return codes:
+ * -EINVAL: If the gadget passed is NULL
+ * -EOPNOTSUPP:
+ */
+static int pch_udc_pcd_vbus_draw(struct usb_gadget *gadget, unsigned int mA)
+{
+ return -EOPNOTSUPP;
+}
+
+static const struct usb_gadget_ops pch_udc_ops = {
+ .get_frame = pch_udc_pcd_get_frame,
+ .wakeup = pch_udc_pcd_wakeup,
+ .set_selfpowered = pch_udc_pcd_selfpowered,
+ .pullup = pch_udc_pcd_pullup,
+ .vbus_session = pch_udc_pcd_vbus_session,
+ .vbus_draw = pch_udc_pcd_vbus_draw,
+};
+
+/**
+ * complete_req() - This API is invoked from the driver when processing
+ * of a request is complete
+ * @ep: Reference to the endpoint structure
+ * @req: Reference to the request structure
+ * @status: Indicates the success/failure of completion
+ */
+static void complete_req(struct pch_udc_ep *ep, struct pch_udc_request *req,
+ int status)
+{
+ struct pch_udc_dev *dev;
+ unsigned halted = ep->halted;
+
+ list_del_init(&req->queue);
+
+ /* set new status if pending */
+ if (req->req.status == -EINPROGRESS)
+ req->req.status = status;
+ else
+ status = req->req.status;
+
+ dev = ep->dev;
+ if (req->dma_mapped) {
+ if (ep->in)
+ pci_unmap_single(dev->pdev, req->req.dma,
+ req->req.length, PCI_DMA_TODEVICE);
+ else
+ pci_unmap_single(dev->pdev, req->req.dma,
+ req->req.length, PCI_DMA_FROMDEVICE);
+ req->dma_mapped = 0;
+ req->req.dma = DMA_ADDR_INVALID;
+ }
+ ep->halted = 1;
+ spin_unlock(&dev->lock);
+ if (!ep->in)
+ pch_udc_ep_clear_rrdy(ep);
+ req->req.complete(&ep->ep, &req->req);
+ spin_lock(&dev->lock);
+ ep->halted = halted;
+}
+
+/**
+ * empty_req_queue() - This API empties the request queue of an endpoint
+ * @ep: Reference to the endpoint structure
+ */
+static void empty_req_queue(struct pch_udc_ep *ep)
+{
+ struct pch_udc_request *req;
+
+ ep->halted = 1;
+ while (!list_empty(&ep->queue)) {
+ req = list_entry(ep->queue.next, struct pch_udc_request, queue);
+ complete_req(ep, req, -ESHUTDOWN); /* Remove from list */
+ }
+}
+
+/**
+ * pch_udc_free_dma_chain() - This function frees the DMA chain created
+ * for the request
+ * @dev Reference to the driver structure
+ * @req Reference to the request to be freed
+ *
+ * Return codes:
+ * 0: Success
+ */
+static void pch_udc_free_dma_chain(struct pch_udc_dev *dev,
+ struct pch_udc_request *req)
+{
+ struct pch_udc_data_dma_desc *td = req->td_data;
+ unsigned i = req->chain_len;
+
+ for (; i > 1; --i) {
+ dma_addr_t addr = (dma_addr_t)td->next;
+ /* do not free first desc., will be done by free for request */
+ td = phys_to_virt(addr);
+ pci_pool_free(dev->data_requests, td, addr);
+ }
+}
+
+/**
+ * pch_udc_create_dma_chain() - This function creates or reinitializes
+ * a DMA chain
+ * @ep: Reference to the endpoint structure
+ * @req: Reference to the request
+ * @buf_len: The buffer length
+ * @gfp_flags: Flags to be used while mapping the data buffer
+ *
+ * Return codes:
+ * 0: success,
+ * -ENOMEM: pci_pool_alloc invocation fails
+ */
+static int pch_udc_create_dma_chain(struct pch_udc_ep *ep,
+ struct pch_udc_request *req,
+ unsigned long buf_len,
+ gfp_t gfp_flags)
+{
+ struct pch_udc_data_dma_desc *td = req->td_data, *last;
+ unsigned long bytes = req->req.length, i = 0;
+ dma_addr_t dma_addr;
+ unsigned len = 1;
+
+ if (req->chain_len > 1)
+ pch_udc_free_dma_chain(ep->dev, req);
+
+ for (; ; bytes -= buf_len, ++len) {
+ if (ep->in)
+ td->status = PCH_UDC_BS_HST_BSY | min(buf_len, bytes);
+ else
+ td->status = PCH_UDC_BS_HST_BSY;
+
+ if (bytes <= buf_len)
+ break;
+
+ last = td;
+ td = pci_pool_alloc(ep->dev->data_requests, gfp_flags,
+ &dma_addr);
+ if (!td)
+ goto nomem;
+
+ i += buf_len;
+ td->dataptr = req->req.dma + i;
+ last->next = dma_addr;
+ }
+
+ req->td_data_last = td;
+ td->status |= PCH_UDC_DMA_LAST;
+ td->next = req->td_data_phys;
+ req->chain_len = len;
+ return 0;
+
+nomem:
+ if (len > 1) {
+ req->chain_len = len;
+ pch_udc_free_dma_chain(ep->dev, req);
+ }
+ req->chain_len = 1;
+ return -ENOMEM;
+}
+
+/**
+ * prepare_dma() - This function creates and initializes the DMA chain
+ * for the request
+ * @ep: Reference to the endpoint structure
+ * @req: Reference to the request
+ * @gfp: Flag to be used while mapping the data buffer
+ *
+ * Return codes:
+ * 0: Success
+ * Other 0: linux error number on failure
+ */
+static int prepare_dma(struct pch_udc_ep *ep, struct pch_udc_request *req,
+ gfp_t gfp)
+{
+ int retval;
+
+ req->td_data->dataptr = req->req.dma;
+ req->td_data->status |= PCH_UDC_DMA_LAST;
+ /* Allocate and create a DMA chain */
+ retval = pch_udc_create_dma_chain(ep, req, ep->ep.maxpacket, gfp);
+ if (retval) {
+ pr_err("%s: could not create DMA chain: %d\n",
+ __func__, retval);
+ return retval;
+ }
+ if (!ep->in)
+ return 0;
+ if (req->req.length <= ep->ep.maxpacket)
+ req->td_data->status = PCH_UDC_DMA_LAST | PCH_UDC_BS_HST_BSY |
+ req->req.length;
+ /* if bytes < max packet then tx bytes must
+ * be written in packet per buffer mode
+ */
+ if ((req->req.length < ep->ep.maxpacket) || !ep->num)
+ req->td_data->status = (req->td_data->status &
+ ~PCH_UDC_RXTX_BYTES) | req->req.length;
+ req->td_data->status = (req->td_data->status &
+ ~PCH_UDC_BUFF_STS) | PCH_UDC_BS_HST_BSY;
+ return 0;
+}
+
+/**
+ * process_zlp() - This function process zero length packets
+ * from the gadget driver
+ * @ep: Reference to the endpoint structure
+ * @req: Reference to the request
+ */
+static void process_zlp(struct pch_udc_ep *ep, struct pch_udc_request *req)
+{
+ struct pch_udc_dev *dev = ep->dev;
+
+ /* IN zlp's are handled by hardware */
+ complete_req(ep, req, 0);
+
+ /* if set_config or set_intf is waiting for ack by zlp
+ * then set CSR_DONE
+ */
+ if (dev->set_cfg_not_acked) {
+ pch_udc_set_csr_done(dev);
+ dev->set_cfg_not_acked = 0;
+ }
+ /* setup command is ACK'ed now by zlp */
+ if (!dev->stall && dev->waiting_zlp_ack) {
+ pch_udc_ep_clear_nak(&(dev->ep[UDC_EP0IN_IDX]));
+ dev->waiting_zlp_ack = 0;
+ }
+}
+
+/**
+ * pch_udc_start_rxrequest() - This function starts the receive requirement.
+ * @ep: Reference to the endpoint structure
+ * @req: Reference to the request structure
+ */
+static void pch_udc_start_rxrequest(struct pch_udc_ep *ep,
+ struct pch_udc_request *req)
+{
+ struct pch_udc_data_dma_desc *td_data;
+
+ pch_udc_clear_dma(ep->dev, DMA_DIR_RX);
+ td_data = req->td_data;
+ ep->td_data = req->td_data;
+ /* Set the status bits for all descriptors */
+ while (1) {
+ td_data->status = (td_data->status & ~PCH_UDC_BUFF_STS) |
+ PCH_UDC_BS_HST_RDY;
+ if ((td_data->status & PCH_UDC_DMA_LAST) == PCH_UDC_DMA_LAST)
+ break;
+ td_data = phys_to_virt(td_data->next);
+ }
+ /* Write the descriptor pointer */
+ pch_udc_ep_set_ddptr(ep, req->td_data_phys);
+ req->dma_going = 1;
+ pch_udc_enable_ep_interrupts(ep->dev, UDC_EPINT_OUT_EP0 << ep->num);
+ pch_udc_set_dma(ep->dev, DMA_DIR_RX);
+ pch_udc_ep_clear_nak(ep);
+ pch_udc_ep_set_rrdy(ep);
+}
+
+/**
+ * pch_udc_pcd_ep_enable() - This API enables the endpoint. It is called
+ * from gadget driver
+ * @usbep: Reference to the USB endpoint structure
+ * @desc: Reference to the USB endpoint descriptor structure
+ *
+ * Return codes:
+ * 0: Success
+ * -EINVAL:
+ * -ESHUTDOWN:
+ */
+static int pch_udc_pcd_ep_enable(struct usb_ep *usbep,
+ const struct usb_endpoint_descriptor *desc)
+{
+ struct pch_udc_ep *ep;
+ struct pch_udc_dev *dev;
+ unsigned long iflags;
+
+ if (!usbep || (usbep->name == ep0_string) || !desc ||
+ (desc->bDescriptorType != USB_DT_ENDPOINT) || !desc->wMaxPacketSize)
+ return -EINVAL;
+
+ ep = container_of(usbep, struct pch_udc_ep, ep);
+ dev = ep->dev;
+ if (!dev->driver || (dev->gadget.speed == USB_SPEED_UNKNOWN))
+ return -ESHUTDOWN;
+ spin_lock_irqsave(&dev->lock, iflags);
+ ep->desc = desc;
+ ep->halted = 0;
+ pch_udc_ep_enable(ep, &ep->dev->cfg_data, desc);
+ ep->ep.maxpacket = le16_to_cpu(desc->wMaxPacketSize);
+ pch_udc_enable_ep_interrupts(ep->dev, PCH_UDC_EPINT(ep->in, ep->num));
+ spin_unlock_irqrestore(&dev->lock, iflags);
+ return 0;
+}
+
+/**
+ * pch_udc_pcd_ep_disable() - This API disables endpoint and is called
+ * from gadget driver
+ * @usbep Reference to the USB endpoint structure
+ *
+ * Return codes:
+ * 0: Success
+ * -EINVAL:
+ */
+static int pch_udc_pcd_ep_disable(struct usb_ep *usbep)
+{
+ struct pch_udc_ep *ep;
+ struct pch_udc_dev *dev;
+ unsigned long iflags;
+
+ if (!usbep)
+ return -EINVAL;
+
+ ep = container_of(usbep, struct pch_udc_ep, ep);
+ dev = ep->dev;
+ if ((usbep->name == ep0_string) || !ep->desc)
+ return -EINVAL;
+
+ spin_lock_irqsave(&ep->dev->lock, iflags);
+ empty_req_queue(ep);
+ ep->halted = 1;
+ pch_udc_ep_disable(ep);
+ pch_udc_disable_ep_interrupts(ep->dev, PCH_UDC_EPINT(ep->in, ep->num));
+ ep->desc = NULL;
+ INIT_LIST_HEAD(&ep->queue);
+ spin_unlock_irqrestore(&ep->dev->lock, iflags);
+ return 0;
+}
+
+/**
+ * pch_udc_alloc_request() - This function allocates request structure.
+ * It is called by gadget driver
+ * @usbep: Reference to the USB endpoint structure
+ * @gfp: Flag to be used while allocating memory
+ *
+ * Return codes:
+ * NULL: Failure
+ * Allocated address: Success
+ */
+static struct usb_request *pch_udc_alloc_request(struct usb_ep *usbep,
+ gfp_t gfp)
+{
+ struct pch_udc_request *req;
+ struct pch_udc_ep *ep;
+ struct pch_udc_data_dma_desc *dma_desc;
+ struct pch_udc_dev *dev;
+
+ if (!usbep)
+ return NULL;
+ ep = container_of(usbep, struct pch_udc_ep, ep);
+ dev = ep->dev;
+ req = kzalloc(sizeof *req, gfp);
+ if (!req)
+ return NULL;
+ req->req.dma = DMA_ADDR_INVALID;
+ INIT_LIST_HEAD(&req->queue);
+ if (!ep->dev->dma_addr)
+ return &req->req;
+ /* ep0 in requests are allocated from data pool here */
+ dma_desc = pci_pool_alloc(ep->dev->data_requests, gfp,
+ &req->td_data_phys);
+ if (NULL == dma_desc) {
+ kfree(req);
+ return NULL;
+ }
+ /* prevent from using desc. - set HOST BUSY */
+ dma_desc->status |= PCH_UDC_BS_HST_BSY;
+ dma_desc->dataptr = __constant_cpu_to_le32(DMA_ADDR_INVALID);
+ req->td_data = dma_desc;
+ req->td_data_last = dma_desc;
+ req->chain_len = 1;
+ return &req->req;
+}
+
+/**
+ * pch_udc_free_request() - This function frees request structure.
+ * It is called by gadget driver
+ * @usbep: Reference to the USB endpoint structure
+ * @usbreq: Reference to the USB request
+ */
+static void pch_udc_free_request(struct usb_ep *usbep,
+ struct usb_request *usbreq)
+{
+ struct pch_udc_ep *ep;
+ struct pch_udc_request *req;
+ struct pch_udc_dev *dev;
+
+ if (!usbep || !usbreq)
+ return;
+ ep = container_of(usbep, struct pch_udc_ep, ep);
+ req = container_of(usbreq, struct pch_udc_request, req);
+ dev = ep->dev;
+ if (!list_empty(&req->queue))
+ dev_err(&dev->pdev->dev, "%s: %s req=0x%p queue not empty\n",
+ __func__, usbep->name, req);
+ if (req->td_data != NULL) {
+ if (req->chain_len > 1)
+ pch_udc_free_dma_chain(ep->dev, req);
+ pci_pool_free(ep->dev->data_requests, req->td_data,
+ req->td_data_phys);
+ }
+ kfree(req);
+}
+
+/**
+ * pch_udc_pcd_queue() - This function queues a request packet. It is called
+ * by gadget driver
+ * @usbep: Reference to the USB endpoint structure
+ * @usbreq: Reference to the USB request
+ * @gfp: Flag to be used while mapping the data buffer
+ *
+ * Return codes:
+ * 0: Success
+ * linux error number: Failure
+ */
+static int pch_udc_pcd_queue(struct usb_ep *usbep, struct usb_request *usbreq,
+ gfp_t gfp)
+{
+ int retval = 0;
+ struct pch_udc_ep *ep;
+ struct pch_udc_dev *dev;
+ struct pch_udc_request *req;
+ unsigned long iflags;
+
+ if (!usbep || !usbreq || !usbreq->complete || !usbreq->buf)
+ return -EINVAL;
+ ep = container_of(usbep, struct pch_udc_ep, ep);
+ dev = ep->dev;
+ if (!ep->desc && ep->num)
+ return -EINVAL;
+ req = container_of(usbreq, struct pch_udc_request, req);
+ if (!list_empty(&req->queue))
+ return -EINVAL;
+ if (!dev->driver || (dev->gadget.speed == USB_SPEED_UNKNOWN))
+ return -ESHUTDOWN;
+ spin_lock_irqsave(&ep->dev->lock, iflags);
+ /* map the buffer for dma */
+ if (usbreq->length &&
+ ((usbreq->dma == DMA_ADDR_INVALID) || !usbreq->dma)) {
+ if (ep->in)
+ usbreq->dma = pci_map_single(dev->pdev, usbreq->buf,
+ usbreq->length, PCI_DMA_TODEVICE);
+ else
+ usbreq->dma = pci_map_single(dev->pdev, usbreq->buf,
+ usbreq->length, PCI_DMA_FROMDEVICE);
+ req->dma_mapped = 1;
+ }
+ if (usbreq->length > 0) {
+ retval = prepare_dma(ep, req, gfp);
+ if (retval)
+ goto probe_end;
+ }
+ usbreq->actual = 0;
+ usbreq->status = -EINPROGRESS;
+ req->dma_done = 0;
+ if (list_empty(&ep->queue) && !ep->halted) {
+ /* no pending transfer, so start this req */
+ if (!usbreq->length) {
+ process_zlp(ep, req);
+ retval = 0;
+ goto probe_end;
+ }
+ if (!ep->in) {
+ pch_udc_start_rxrequest(ep, req);
+ } else {
+ /*
+ * For IN trfr the descriptors will be programmed and
+ * P bit will be set when
+ * we get an IN token
+ */
+ pch_udc_wait_ep_stall(ep);
+ pch_udc_ep_clear_nak(ep);
+ pch_udc_enable_ep_interrupts(ep->dev, (1 << ep->num));
+ pch_udc_set_dma(dev, DMA_DIR_TX);
+ }
+ }
+ /* Now add this request to the ep's pending requests */
+ if (req != NULL)
+ list_add_tail(&req->queue, &ep->queue);
+
+probe_end:
+ spin_unlock_irqrestore(&dev->lock, iflags);
+ return retval;
+}
+
+/**
+ * pch_udc_pcd_dequeue() - This function de-queues a request packet.
+ * It is called by gadget driver
+ * @usbep: Reference to the USB endpoint structure
+ * @usbreq: Reference to the USB request
+ *
+ * Return codes:
+ * 0: Success
+ * linux error number: Failure
+ */
+static int pch_udc_pcd_dequeue(struct usb_ep *usbep,
+ struct usb_request *usbreq)
+{
+ struct pch_udc_ep *ep;
+ struct pch_udc_request *req;
+ struct pch_udc_dev *dev;
+ unsigned long flags;
+ int ret = -EINVAL;
+
+ ep = container_of(usbep, struct pch_udc_ep, ep);
+ dev = ep->dev;
+ if (!usbep || !usbreq || (!ep->desc && ep->num))
+ return ret;
+ req = container_of(usbreq, struct pch_udc_request, req);
+ spin_lock_irqsave(&ep->dev->lock, flags);
+ /* make sure it's still queued on this endpoint */
+ list_for_each_entry(req, &ep->queue, queue) {
+ if (&req->req == usbreq) {
+ pch_udc_ep_set_nak(ep);
+ if (!list_empty(&req->queue))
+ complete_req(ep, req, -ECONNRESET);
+ ret = 0;
+ break;
+ }
+ }
+ spin_unlock_irqrestore(&ep->dev->lock, flags);
+ return ret;
+}
+
+/**
+ * pch_udc_pcd_set_halt() - This function Sets or clear the endpoint halt
+ * feature
+ * @usbep: Reference to the USB endpoint structure
+ * @halt: Specifies whether to set or clear the feature
+ *
+ * Return codes:
+ * 0: Success
+ * linux error number: Failure
+ */
+static int pch_udc_pcd_set_halt(struct usb_ep *usbep, int halt)
+{
+ struct pch_udc_ep *ep;
+ struct pch_udc_dev *dev;
+ unsigned long iflags;
+ int ret;
+
+ if (!usbep)
+ return -EINVAL;
+ ep = container_of(usbep, struct pch_udc_ep, ep);
+ dev = ep->dev;
+ if (!ep->desc && !ep->num)
+ return -EINVAL;
+ if (!ep->dev->driver || (ep->dev->gadget.speed == USB_SPEED_UNKNOWN))
+ return -ESHUTDOWN;
+ spin_lock_irqsave(&udc_stall_spinlock, iflags);
+ if (list_empty(&ep->queue)) {
+ if (halt) {
+ if (ep->num == PCH_UDC_EP0)
+ ep->dev->stall = 1;
+ pch_udc_ep_set_stall(ep);
+ pch_udc_enable_ep_interrupts(ep->dev,
+ PCH_UDC_EPINT(ep->in,
+ ep->num));
+ } else {
+ pch_udc_ep_clear_stall(ep);
+ }
+ ret = 0;
+ } else {
+ ret = -EAGAIN;
+ }
+ spin_unlock_irqrestore(&udc_stall_spinlock, iflags);
+ return ret;
+}
+
+/**
+ * pch_udc_pcd_set_wedge() - This function Sets or clear the endpoint
+ * halt feature
+ * @usbep: Reference to the USB endpoint structure
+ * @halt: Specifies whether to set or clear the feature
+ *
+ * Return codes:
+ * 0: Success
+ * linux error number: Failure
+ */
+static int pch_udc_pcd_set_wedge(struct usb_ep *usbep)
+{
+ struct pch_udc_ep *ep;
+ struct pch_udc_dev *dev;
+ unsigned long iflags;
+ int ret;
+
+ if (!usbep)
+ return -EINVAL;
+ ep = container_of(usbep, struct pch_udc_ep, ep);
+ dev = ep->dev;
+ if (!ep->desc && !ep->num)
+ return -EINVAL;
+ if (!ep->dev->driver || (ep->dev->gadget.speed == USB_SPEED_UNKNOWN))
+ return -ESHUTDOWN;
+ spin_lock_irqsave(&udc_stall_spinlock, iflags);
+ if (!list_empty(&ep->queue)) {
+ ret = -EAGAIN;
+ } else {
+ if (ep->num == PCH_UDC_EP0)
+ ep->dev->stall = 1;
+ pch_udc_ep_set_stall(ep);
+ pch_udc_enable_ep_interrupts(ep->dev,
+ PCH_UDC_EPINT(ep->in, ep->num));
+ ep->dev->prot_stall = 1;
+ ret = 0;
+ }
+ spin_unlock_irqrestore(&udc_stall_spinlock, iflags);
+ return ret;
+}
+
+/**
+ * pch_udc_pcd_fifo_flush() - This function Flush the FIFO of specified endpoint
+ * @usbep: Reference to the USB endpoint structure
+ */
+static void pch_udc_pcd_fifo_flush(struct usb_ep *usbep)
+{
+ struct pch_udc_ep *ep;
+
+ if (!usbep)
+ return;
+
+ ep = container_of(usbep, struct pch_udc_ep, ep);
+ if (ep->desc || !ep->num)
+ pch_udc_ep_fifo_flush(ep, ep->in);
+}
+
+static const struct usb_ep_ops pch_udc_ep_ops = {
+ .enable = pch_udc_pcd_ep_enable,
+ .disable = pch_udc_pcd_ep_disable,
+ .alloc_request = pch_udc_alloc_request,
+ .free_request = pch_udc_free_request,
+ .queue = pch_udc_pcd_queue,
+ .dequeue = pch_udc_pcd_dequeue,
+ .set_halt = pch_udc_pcd_set_halt,
+ .set_wedge = pch_udc_pcd_set_wedge,
+ .fifo_status = NULL,
+ .fifo_flush = pch_udc_pcd_fifo_flush,
+};
+
+/**
+ * pch_udc_init_setup_buff() - This function initializes the SETUP buffer
+ * @td_stp: Reference to the SETP buffer structure
+ */
+static void pch_udc_init_setup_buff(struct pch_udc_stp_dma_desc *td_stp)
+{
+ static u32 pky_marker;
+
+ if (!td_stp)
+ return;
+ td_stp->reserved = ++pky_marker;
+ memset(&td_stp->request, 0xFF, sizeof td_stp->request);
+ td_stp->status = PCH_UDC_BS_HST_RDY;
+}
+
+/**
+ * pch_udc_start_next_txrequest() - This function starts
+ * the next transmission requirement
+ * @ep: Reference to the endpoint structure
+ */
+static void pch_udc_start_next_txrequest(struct pch_udc_ep *ep)
+{
+ struct pch_udc_request *req;
+ struct pch_udc_data_dma_desc *td_data;
+
+ if (pch_udc_read_ep_control(ep) & UDC_EPCTL_P)
+ return;
+
+ if (list_empty(&ep->queue))
+ return;
+
+ /* next request */
+ req = list_entry(ep->queue.next, struct pch_udc_request, queue);
+ if (req->dma_going)
+ return;
+ if (!req->td_data)
+ return;
+ pch_udc_wait_ep_stall(ep);
+ req->dma_going = 1;
+ pch_udc_ep_set_ddptr(ep, 0);
+ td_data = req->td_data;
+ while (1) {
+ td_data->status = (td_data->status & ~PCH_UDC_BUFF_STS) |
+ PCH_UDC_BS_HST_RDY;
+ if ((td_data->status & PCH_UDC_DMA_LAST) == PCH_UDC_DMA_LAST)
+ break;
+ td_data = phys_to_virt(td_data->next);
+ }
+ pch_udc_ep_set_ddptr(ep, req->td_data_phys);
+ pch_udc_set_dma(ep->dev, DMA_DIR_TX);
+ pch_udc_ep_set_pd(ep);
+ pch_udc_enable_ep_interrupts(ep->dev, PCH_UDC_EPINT(ep->in, ep->num));
+ pch_udc_ep_clear_nak(ep);
+}
+
+/**
+ * pch_udc_complete_transfer() - This function completes a transfer
+ * @ep: Reference to the endpoint structure
+ */
+static void pch_udc_complete_transfer(struct pch_udc_ep *ep)
+{
+ struct pch_udc_request *req;
+ struct pch_udc_dev *dev = ep->dev;
+
+ if (list_empty(&ep->queue))
+ return;
+ req = list_entry(ep->queue.next, struct pch_udc_request, queue);
+ if ((req->td_data_last->status & PCH_UDC_BUFF_STS) !=
+ PCH_UDC_BS_DMA_DONE)
+ return;
+ if ((req->td_data_last->status & PCH_UDC_RXTX_STS) !=
+ PCH_UDC_RTS_SUCC) {
+ dev_err(&dev->pdev->dev, "Invalid RXTX status (0x%08x) "
+ "epstatus=0x%08x\n",
+ (req->td_data_last->status & PCH_UDC_RXTX_STS),
+ (int)(ep->epsts));
+ return;
+ }
+
+ req->req.actual = req->req.length;
+ req->td_data_last->status = PCH_UDC_BS_HST_BSY | PCH_UDC_DMA_LAST;
+ req->td_data->status = PCH_UDC_BS_HST_BSY | PCH_UDC_DMA_LAST;
+ complete_req(ep, req, 0);
+ req->dma_going = 0;
+ if (!list_empty(&ep->queue)) {
+ pch_udc_wait_ep_stall(ep);
+ pch_udc_ep_clear_nak(ep);
+ pch_udc_enable_ep_interrupts(ep->dev,
+ PCH_UDC_EPINT(ep->in, ep->num));
+ } else {
+ pch_udc_disable_ep_interrupts(ep->dev,
+ PCH_UDC_EPINT(ep->in, ep->num));
+ }
+}
+
+/**
+ * pch_udc_complete_receiver() - This function completes a receiver
+ * @ep: Reference to the endpoint structure
+ */
+static void pch_udc_complete_receiver(struct pch_udc_ep *ep)
+{
+ struct pch_udc_request *req;
+ struct pch_udc_dev *dev = ep->dev;
+ unsigned int count;
+
+ if (list_empty(&ep->queue))
+ return;
+
+ /* next request */
+ req = list_entry(ep->queue.next, struct pch_udc_request, queue);
+ if ((req->td_data_last->status & PCH_UDC_BUFF_STS) !=
+ PCH_UDC_BS_DMA_DONE)
+ return;
+ pch_udc_clear_dma(ep->dev, DMA_DIR_RX);
+ if ((req->td_data_last->status & PCH_UDC_RXTX_STS) !=
+ PCH_UDC_RTS_SUCC) {
+ dev_err(&dev->pdev->dev, "Invalid RXTX status (0x%08x) "
+ "epstatus=0x%08x\n",
+ (req->td_data_last->status & PCH_UDC_RXTX_STS),
+ (int)(ep->epsts));
+ return;
+ }
+ count = req->td_data_last->status & PCH_UDC_RXTX_BYTES;
+
+ /* on 64k packets the RXBYTES field is zero */
+ if (!count && (req->req.length == UDC_DMA_MAXPACKET))
+ count = UDC_DMA_MAXPACKET;
+ req->td_data->status |= PCH_UDC_DMA_LAST;
+ req->td_data_last->status |= PCH_UDC_BS_HST_BSY;
+
+ req->dma_going = 0;
+ req->req.actual = count;
+ complete_req(ep, req, 0);
+ /* If there is a new/failed requests try that now */
+ if (!list_empty(&ep->queue)) {
+ req = list_entry(ep->queue.next, struct pch_udc_request, queue);
+ pch_udc_start_rxrequest(ep, req);
+ }
+}
+
+/**
+ * pch_udc_svc_data_in() - This function process endpoint interrupts
+ * for IN endpoints
+ * @dev: Reference to the device structure
+ * @ep_num: Endpoint that generated the interrupt
+ */
+static void pch_udc_svc_data_in(struct pch_udc_dev *dev, int ep_num)
+{
+ u32 epsts;
+ struct pch_udc_ep *ep;
+
+ ep = &dev->ep[2*ep_num];
+ epsts = ep->epsts;
+ ep->epsts = 0;
+
+ if (!(epsts & (UDC_EPSTS_IN | UDC_EPSTS_BNA | UDC_EPSTS_HE |
+ UDC_EPSTS_TDC | UDC_EPSTS_RCS | UDC_EPSTS_TXEMPTY |
+ UDC_EPSTS_RSS | UDC_EPSTS_XFERDONE)))
+ return;
+ if ((epsts & UDC_EPSTS_BNA))
+ return;
+ if (epsts & UDC_EPSTS_HE)
+ return;
+ if (epsts & UDC_EPSTS_RSS) {
+ pch_udc_ep_set_stall(ep);
+ pch_udc_enable_ep_interrupts(ep->dev,
+ PCH_UDC_EPINT(ep->in, ep->num));
+ }
+ if (epsts & UDC_EPSTS_RCS) {
+ if (!dev->prot_stall) {
+ pch_udc_ep_clear_stall(ep);
+ } else {
+ pch_udc_ep_set_stall(ep);
+ pch_udc_enable_ep_interrupts(ep->dev,
+ PCH_UDC_EPINT(ep->in, ep->num));
+ }
+ }
+ if (epsts & UDC_EPSTS_TDC)
+ pch_udc_complete_transfer(ep);
+ /* On IN interrupt, provide data if we have any */
+ if ((epsts & UDC_EPSTS_IN) && !(epsts & UDC_EPSTS_RSS) &&
+ !(epsts & UDC_EPSTS_TDC) && !(epsts & UDC_EPSTS_TXEMPTY))
+ pch_udc_start_next_txrequest(ep);
+}
+
+/**
+ * pch_udc_svc_data_out() - Handles interrupts from OUT endpoint
+ * @dev: Reference to the device structure
+ * @ep_num: Endpoint that generated the interrupt
+ */
+static void pch_udc_svc_data_out(struct pch_udc_dev *dev, int ep_num)
+{
+ u32 epsts;
+ struct pch_udc_ep *ep;
+ struct pch_udc_request *req = NULL;
+
+ ep = &dev->ep[2*ep_num + 1];
+ epsts = ep->epsts;
+ ep->epsts = 0;
+
+ if ((epsts & UDC_EPSTS_BNA) && (!list_empty(&ep->queue))) {
+ /* next request */
+ req = list_entry(ep->queue.next, struct pch_udc_request,
+ queue);
+ if ((req->td_data_last->status & PCH_UDC_BUFF_STS) !=
+ PCH_UDC_BS_DMA_DONE) {
+ if (!req->dma_going)
+ pch_udc_start_rxrequest(ep, req);
+ return;
+ }
+ }
+ if (epsts & UDC_EPSTS_HE)
+ return;
+ if (epsts & UDC_EPSTS_RSS)
+ pch_udc_ep_set_stall(ep);
+ pch_udc_enable_ep_interrupts(ep->dev,
+ PCH_UDC_EPINT(ep->in, ep->num));
+ if (epsts & UDC_EPSTS_RCS) {
+ if (!dev->prot_stall) {
+ pch_udc_ep_clear_stall(ep);
+ } else {
+ pch_udc_ep_set_stall(ep);
+ pch_udc_enable_ep_interrupts(ep->dev,
+ PCH_UDC_EPINT(ep->in, ep->num));
+ }
+ }
+ if (((epsts & UDC_EPSTS_OUT_MASK) >> UDC_EPSTS_OUT_SHIFT) ==
+ UDC_EPSTS_OUT_DATA) {
+ if (ep->dev->prot_stall == 1) {
+ pch_udc_ep_set_stall(ep);
+ pch_udc_enable_ep_interrupts(ep->dev,
+ PCH_UDC_EPINT(ep->in, ep->num));
+ } else {
+ pch_udc_complete_receiver(ep);
+ }
+ }
+ if (list_empty(&ep->queue))
+ pch_udc_set_dma(dev, DMA_DIR_RX);
+}
+
+/**
+ * pch_udc_svc_control_in() - Handle Control IN endpoint interrupts
+ * @dev: Reference to the device structure
+ */
+static void pch_udc_svc_control_in(struct pch_udc_dev *dev)
+{
+ u32 epsts;
+ struct pch_udc_ep *ep;
+
+ ep = &dev->ep[UDC_EP0IN_IDX];
+ epsts = ep->epsts;
+ ep->epsts = 0;
+
+ if (!(epsts & (UDC_EPSTS_IN | UDC_EPSTS_BNA | UDC_EPSTS_HE |
+ UDC_EPSTS_TDC | UDC_EPSTS_RCS | UDC_EPSTS_TXEMPTY |
+ UDC_EPSTS_XFERDONE)))
+ return;
+ if ((epsts & UDC_EPSTS_BNA))
+ return;
+ if (epsts & UDC_EPSTS_HE)
+ return;
+ if ((epsts & UDC_EPSTS_TDC) && (!dev->stall))
+ pch_udc_complete_transfer(ep);
+ /* On IN interrupt, provide data if we have any */
+ if ((epsts & UDC_EPSTS_IN) && !(epsts & UDC_EPSTS_TDC) &&
+ !(epsts & UDC_EPSTS_TXEMPTY))
+ pch_udc_start_next_txrequest(ep);
+}
+
+/**
+ * pch_udc_svc_control_out() - Routine that handle Control
+ * OUT endpoint interrupts
+ * @dev: Reference to the device structure
+ */
+static void pch_udc_svc_control_out(struct pch_udc_dev *dev)
+{
+ u32 stat;
+ int setup_supported;
+ struct pch_udc_ep *ep;
+
+ ep = &dev->ep[UDC_EP0OUT_IDX];
+ stat = ep->epsts;
+ ep->epsts = 0;
+
+ /* If setup data */
+ if (((stat & UDC_EPSTS_OUT_MASK) >> UDC_EPSTS_OUT_SHIFT) ==
+ UDC_EPSTS_OUT_SETUP) {
+ dev->stall = 0;
+ dev->ep[UDC_EP0IN_IDX].halted = 0;
+ dev->ep[UDC_EP0OUT_IDX].halted = 0;
+ /* In data not ready */
+ pch_udc_ep_set_nak(&(dev->ep[UDC_EP0IN_IDX]));
+ dev->setup_data = ep->td_stp->request;
+ pch_udc_init_setup_buff(ep->td_stp);
+ pch_udc_clear_dma(dev, DMA_DIR_TX);
+ pch_udc_ep_fifo_flush(&(dev->ep[UDC_EP0IN_IDX]),
+ dev->ep[UDC_EP0IN_IDX].in);
+ if ((dev->setup_data.bRequestType & USB_DIR_IN))
+ dev->gadget.ep0 = &dev->ep[UDC_EP0IN_IDX].ep;
+ else /* OUT */
+ dev->gadget.ep0 = &ep->ep;
+ spin_unlock(&dev->lock);
+ /* If Mass storage Reset */
+ if ((dev->setup_data.bRequestType == 0x21) &&
+ (dev->setup_data.bRequest == 0xFF))
+ dev->prot_stall = 0;
+ /* call gadget with setup data received */
+ setup_supported = dev->driver->setup(&dev->gadget,
+ &dev->setup_data);
+ spin_lock(&dev->lock);
+ /* ep0 in returns data on IN phase */
+ if (setup_supported >= 0 && setup_supported <
+ UDC_EP0IN_MAX_PKT_SIZE) {
+ pch_udc_ep_clear_nak(&(dev->ep[UDC_EP0IN_IDX]));
+ /* Gadget would have queued a request when
+ * we called the setup */
+ pch_udc_set_dma(dev, DMA_DIR_RX);
+ pch_udc_ep_clear_nak(ep);
+ } else if (setup_supported < 0) {
+ /* if unsupported request, then stall */
+ pch_udc_ep_set_stall(&(dev->ep[UDC_EP0IN_IDX]));
+ pch_udc_enable_ep_interrupts(ep->dev,
+ PCH_UDC_EPINT(ep->in, ep->num));
+ dev->stall = 0;
+ pch_udc_set_dma(dev, DMA_DIR_RX);
+ } else {
+ dev->waiting_zlp_ack = 1;
+ }
+ } else if ((((stat & UDC_EPSTS_OUT_MASK) >> UDC_EPSTS_OUT_SHIFT) ==
+ UDC_EPSTS_OUT_DATA) && !dev->stall) {
+ if (list_empty(&ep->queue)) {
+ dev_err(&dev->pdev->dev, "%s: No request\n", __func__);
+ ep->td_data->status = (ep->td_data->status &
+ ~PCH_UDC_BUFF_STS) |
+ PCH_UDC_BS_HST_RDY;
+ pch_udc_set_dma(dev, DMA_DIR_RX);
+ } else {
+ /* control write */
+ /* next function will pickuo an clear the status */
+ ep->epsts = stat;
+
+ pch_udc_svc_data_out(dev, 0);
+ /* re-program desc. pointer for possible ZLPs */
+ pch_udc_ep_set_ddptr(ep, ep->td_data_phys);
+ pch_udc_set_dma(dev, DMA_DIR_RX);
+ }
+ }
+ pch_udc_ep_set_rrdy(ep);
+}
+
+
+/**
+ * pch_udc_postsvc_epinters() - This function enables end point interrupts
+ * and clears NAK status
+ * @dev: Reference to the device structure
+ * @ep_num: End point number
+ */
+static void pch_udc_postsvc_epinters(struct pch_udc_dev *dev, int ep_num)
+{
+ struct pch_udc_ep *ep;
+ struct pch_udc_request *req;
+
+ ep = &dev->ep[2*ep_num];
+ if (!list_empty(&ep->queue)) {
+ req = list_entry(ep->queue.next, struct pch_udc_request, queue);
+ pch_udc_enable_ep_interrupts(ep->dev,
+ PCH_UDC_EPINT(ep->in, ep->num));
+ pch_udc_ep_clear_nak(ep);
+ }
+}
+
+/**
+ * pch_udc_read_all_epstatus() - This function read all endpoint status
+ * @dev: Reference to the device structure
+ * @ep_intr: Status of endpoint interrupt
+ */
+static void pch_udc_read_all_epstatus(struct pch_udc_dev *dev, u32 ep_intr)
+{
+ int i;
+ struct pch_udc_ep *ep;
+
+ for (i = 0; i < PCH_UDC_USED_EP_NUM; i++) {
+ /* IN */
+ if (ep_intr & (0x1 << i)) {
+ ep = &dev->ep[2*i];
+ ep->epsts = pch_udc_read_ep_status(ep);
+ pch_udc_clear_ep_status(ep, ep->epsts);
+ }
+ /* OUT */
+ if (ep_intr & (0x10000 << i)) {
+ ep = &dev->ep[2*i+1];
+ ep->epsts = pch_udc_read_ep_status(ep);
+ pch_udc_clear_ep_status(ep, ep->epsts);
+ }
+ }
+}
+
+/**
+ * pch_udc_activate_control_ep() - This function enables the control endpoints
+ * for traffic after a reset
+ * @dev: Reference to the device structure
+ */
+static void pch_udc_activate_control_ep(struct pch_udc_dev *dev)
+{
+ struct pch_udc_ep *ep;
+ u32 val;
+
+ /* Setup the IN endpoint */
+ ep = &dev->ep[UDC_EP0IN_IDX];
+ pch_udc_clear_ep_control(ep);
+ pch_udc_ep_fifo_flush(ep, ep->in);
+ pch_udc_ep_set_bufsz(ep, UDC_EP0IN_BUFF_SIZE, ep->in);
+ pch_udc_ep_set_maxpkt(ep, UDC_EP0IN_MAX_PKT_SIZE);
+ /* Initialize the IN EP Descriptor */
+ ep->td_data = NULL;
+ ep->td_stp = NULL;
+ ep->td_data_phys = 0;
+ ep->td_stp_phys = 0;
+
+ /* Setup the OUT endpoint */
+ ep = &dev->ep[UDC_EP0OUT_IDX];
+ pch_udc_clear_ep_control(ep);
+ pch_udc_ep_fifo_flush(ep, ep->in);
+ pch_udc_ep_set_bufsz(ep, UDC_EP0OUT_BUFF_SIZE, ep->in);
+ pch_udc_ep_set_maxpkt(ep, UDC_EP0OUT_MAX_PKT_SIZE);
+ val = UDC_EP0OUT_MAX_PKT_SIZE << UDC_CSR_NE_MAX_PKT_SHIFT;
+ pch_udc_write_csr(ep->dev, val, UDC_EP0OUT_IDX);
+
+ /* Initialize the SETUP buffer */
+ pch_udc_init_setup_buff(ep->td_stp);
+ /* Write the pointer address of dma descriptor */
+ pch_udc_ep_set_subptr(ep, ep->td_stp_phys);
+ /* Write the pointer address of Setup descriptor */
+ pch_udc_ep_set_ddptr(ep, ep->td_data_phys);
+
+ /* Initialize the dma descriptor */
+ ep->td_data->status = PCH_UDC_DMA_LAST;
+ ep->td_data->dataptr = dev->dma_addr;
+ ep->td_data->next = ep->td_data_phys;
+
+ pch_udc_ep_clear_nak(ep);
+}
+
+
+/**
+ * pch_udc_svc_ur_interrupt() - This function handles a USB reset interrupt
+ * @dev: Reference to driver structure
+ */
+static void pch_udc_svc_ur_interrupt(struct pch_udc_dev *dev)
+{
+ struct pch_udc_ep *ep;
+ int i;
+
+ pch_udc_clear_dma(dev, DMA_DIR_TX);
+ pch_udc_clear_dma(dev, DMA_DIR_RX);
+ /* Mask all endpoint interrupts */
+ pch_udc_disable_ep_interrupts(dev, UDC_EPINT_MSK_DISABLE_ALL);
+ /* clear all endpoint interrupts */
+ pch_udc_write_ep_interrupts(dev, UDC_EPINT_MSK_DISABLE_ALL);
+
+ for (i = 0; i < PCH_UDC_EP_NUM; i++) {
+ ep = &dev->ep[i];
+ pch_udc_clear_ep_status(ep, UDC_EPSTS_ALL_CLR_MASK);
+ pch_udc_clear_ep_control(ep);
+ pch_udc_ep_set_ddptr(ep, 0);
+ pch_udc_write_csr(ep->dev, 0x00, i);
+ }
+ dev->stall = 0;
+ dev->prot_stall = 0;
+ dev->waiting_zlp_ack = 0;
+ dev->set_cfg_not_acked = 0;
+
+ /* disable ep to empty req queue. Skip the control EP's */
+ for (i = 0; i < (PCH_UDC_USED_EP_NUM*2); i++) {
+ ep = &dev->ep[i];
+ pch_udc_ep_set_nak(ep);
+ pch_udc_ep_fifo_flush(ep, ep->in);
+ /* Complete request queue */
+ empty_req_queue(ep);
+ }
+ if (dev->driver && dev->driver->disconnect)
+ dev->driver->disconnect(&dev->gadget);
+}
+
+/**
+ * pch_udc_svc_enum_interrupt() - This function handles a USB speed enumeration
+ * done interrupt
+ * @dev: Reference to driver structure
+ */
+static void pch_udc_svc_enum_interrupt(struct pch_udc_dev *dev)
+{
+ u32 dev_stat, dev_speed;
+ u32 speed = USB_SPEED_FULL;
+
+ dev_stat = pch_udc_read_device_status(dev);
+ dev_speed = (dev_stat & UDC_DEVSTS_ENUM_SPEED_MASK) >>
+ UDC_DEVSTS_ENUM_SPEED_SHIFT;
+ switch (dev_speed) {
+ case UDC_DEVSTS_ENUM_SPEED_HIGH:
+ speed = USB_SPEED_HIGH;
+ break;
+ case UDC_DEVSTS_ENUM_SPEED_FULL:
+ speed = USB_SPEED_FULL;
+ break;
+ case UDC_DEVSTS_ENUM_SPEED_LOW:
+ speed = USB_SPEED_LOW;
+ break;
+ default:
+ BUG();
+ }
+ dev->gadget.speed = speed;
+ pch_udc_activate_control_ep(dev);
+ pch_udc_enable_ep_interrupts(dev, UDC_EPINT_IN_EP0 | UDC_EPINT_OUT_EP0);
+ pch_udc_set_dma(dev, DMA_DIR_TX);
+ pch_udc_set_dma(dev, DMA_DIR_RX);
+ pch_udc_ep_set_rrdy(&(dev->ep[UDC_EP0OUT_IDX]));
+}
+
+/**
+ * pch_udc_svc_intf_interrupt() - This function handles a set interface
+ * interrupt
+ * @dev: Reference to driver structure
+ */
+static void pch_udc_svc_intf_interrupt(struct pch_udc_dev *dev)
+{
+ u32 reg, dev_stat = 0;
+ int i, ret;
+
+ dev_stat = pch_udc_read_device_status(dev);
+ dev->cfg_data.cur_intf = (dev_stat & UDC_DEVSTS_INTF_MASK) >>
+ UDC_DEVSTS_INTF_SHIFT;
+ dev->cfg_data.cur_alt = (dev_stat & UDC_DEVSTS_ALT_MASK) >>
+ UDC_DEVSTS_ALT_SHIFT;
+ dev->set_cfg_not_acked = 1;
+ /* Construct the usb request for gadget driver and inform it */
+ memset(&dev->setup_data, 0 , sizeof dev->setup_data);
+ dev->setup_data.bRequest = USB_REQ_SET_INTERFACE;
+ dev->setup_data.bRequestType = USB_RECIP_INTERFACE;
+ dev->setup_data.wValue = cpu_to_le16(dev->cfg_data.cur_alt);
+ dev->setup_data.wIndex = cpu_to_le16(dev->cfg_data.cur_intf);
+ /* programm the Endpoint Cfg registers */
+ /* Only one end point cfg register */
+ reg = pch_udc_read_csr(dev, UDC_EP0OUT_IDX);
+ reg = (reg & ~UDC_CSR_NE_INTF_MASK) |
+ (dev->cfg_data.cur_intf << UDC_CSR_NE_INTF_SHIFT);
+ reg = (reg & ~UDC_CSR_NE_ALT_MASK) |
+ (dev->cfg_data.cur_alt << UDC_CSR_NE_ALT_SHIFT);
+ pch_udc_write_csr(dev, reg, UDC_EP0OUT_IDX);
+ for (i = 0; i < PCH_UDC_USED_EP_NUM * 2; i++) {
+ /* clear stall bits */
+ pch_udc_ep_clear_stall(&(dev->ep[i]));
+ dev->ep[i].halted = 0;
+ }
+ dev->stall = 0;
+ spin_unlock(&dev->lock);
+ ret = dev->driver->setup(&dev->gadget, &dev->setup_data);
+ spin_lock(&dev->lock);
+}
+
+/**
+ * pch_udc_svc_cfg_interrupt() - This function handles a set configuration
+ * interrupt
+ * @dev: Reference to driver structure
+ */
+static void pch_udc_svc_cfg_interrupt(struct pch_udc_dev *dev)
+{
+ int i, ret;
+ u32 reg, dev_stat = 0;
+
+ dev_stat = pch_udc_read_device_status(dev);
+ dev->set_cfg_not_acked = 1;
+ dev->cfg_data.cur_cfg = (dev_stat & UDC_DEVSTS_CFG_MASK) >>
+ UDC_DEVSTS_CFG_SHIFT;
+ /* make usb request for gadget driver */
+ memset(&dev->setup_data, 0 , sizeof dev->setup_data);
+ dev->setup_data.bRequest = USB_REQ_SET_CONFIGURATION;
+ dev->setup_data.wValue = cpu_to_le16(dev->cfg_data.cur_cfg);
+ /* program the NE registers */
+ /* Only one end point cfg register */
+ reg = pch_udc_read_csr(dev, UDC_EP0OUT_IDX);
+ reg = (reg & ~UDC_CSR_NE_CFG_MASK) |
+ (dev->cfg_data.cur_cfg << UDC_CSR_NE_CFG_SHIFT);
+ pch_udc_write_csr(dev, reg, UDC_EP0OUT_IDX);
+ for (i = 0; i < PCH_UDC_USED_EP_NUM * 2; i++) {
+ /* clear stall bits */
+ pch_udc_ep_clear_stall(&(dev->ep[i]));
+ dev->ep[i].halted = 0;
+ }
+ dev->stall = 0;
+
+ /* call gadget zero with setup data received */
+ spin_unlock(&dev->lock);
+ ret = dev->driver->setup(&dev->gadget, &dev->setup_data);
+ spin_lock(&dev->lock);
+}
+
+/**
+ * pch_udc_dev_isr() - This function services device interrupts
+ * by invoking appropriate routines.
+ * @dev: Reference to the device structure
+ * @dev_intr: The Device interrupt status.
+ */
+static void pch_udc_dev_isr(struct pch_udc_dev *dev, u32 dev_intr)
+{
+ /* USB Reset Interrupt */
+ if (dev_intr & UDC_DEVINT_UR)
+ pch_udc_svc_ur_interrupt(dev);
+ /* Enumeration Done Interrupt */
+ if (dev_intr & UDC_DEVINT_ENUM)
+ pch_udc_svc_enum_interrupt(dev);
+ /* Set Interface Interrupt */
+ if (dev_intr & UDC_DEVINT_SI)
+ pch_udc_svc_intf_interrupt(dev);
+ /* Set Config Interrupt */
+ if (dev_intr & UDC_DEVINT_SC)
+ pch_udc_svc_cfg_interrupt(dev);
+ /* USB Suspend interrupt */
+ if (dev_intr & UDC_DEVINT_US)
+ dev_dbg(&dev->pdev->dev, "USB_SUSPEND\n");
+ /* Clear the SOF interrupt, if enabled */
+ if (dev_intr & UDC_DEVINT_SOF)
+ dev_dbg(&dev->pdev->dev, "SOF\n");
+ /* ES interrupt, IDLE > 3ms on the USB */
+ if (dev_intr & UDC_DEVINT_ES)
+ dev_dbg(&dev->pdev->dev, "ES\n");
+ /* RWKP interrupt */
+ if (dev_intr & UDC_DEVINT_RWKP)
+ dev_dbg(&dev->pdev->dev, "RWKP\n");
+}
+
+/**
+ * pch_udc_isr() - This function handles interrupts from the PCH USB Device
+ * @irq: Interrupt request number
+ * @dev: Reference to the device structure
+ */
+static irqreturn_t pch_udc_isr(int irq, void *pdev)
+{
+ struct pch_udc_dev *dev = (struct pch_udc_dev *) pdev;
+ u32 dev_intr, ep_intr;
+ int i;
+
+ dev_intr = pch_udc_read_device_interrupts(dev);
+ ep_intr = pch_udc_read_ep_interrupts(dev);
+
+ if (dev_intr)
+ /* Clear device interrupts */
+ pch_udc_write_device_interrupts(dev, dev_intr);
+ if (ep_intr)
+ /* Clear ep interrupts */
+ pch_udc_write_ep_interrupts(dev, ep_intr);
+ if (!dev_intr && !ep_intr)
+ return IRQ_NONE;
+ spin_lock(&dev->lock);
+ if (dev_intr)
+ pch_udc_dev_isr(dev, dev_intr);
+ if (ep_intr) {
+ pch_udc_read_all_epstatus(dev, ep_intr);
+ /* Process Control In interrupts, if present */
+ if (ep_intr & UDC_EPINT_IN_EP0) {
+ pch_udc_svc_control_in(dev);
+ pch_udc_postsvc_epinters(dev, 0);
+ }
+ /* Process Control Out interrupts, if present */
+ if (ep_intr & UDC_EPINT_OUT_EP0)
+ pch_udc_svc_control_out(dev);
+ /* Process data in end point interrupts */
+ for (i = 1; i < PCH_UDC_USED_EP_NUM; i++) {
+ if (ep_intr & (1 << i)) {
+ pch_udc_svc_data_in(dev, i);
+ pch_udc_postsvc_epinters(dev, i);
+ }
+ }
+ /* Process data out end point interrupts */
+ for (i = UDC_EPINT_OUT_SHIFT + 1; i < (UDC_EPINT_OUT_SHIFT +
+ PCH_UDC_USED_EP_NUM); i++)
+ if (ep_intr & (1 << i))
+ pch_udc_svc_data_out(dev, i -
+ UDC_EPINT_OUT_SHIFT);
+ }
+ spin_unlock(&dev->lock);
+ return IRQ_HANDLED;
+}
+
+/**
+ * pch_udc_setup_ep0() - This function enables control endpoint for traffic
+ * @dev: Reference to the device structure
+ */
+static void pch_udc_setup_ep0(struct pch_udc_dev *dev)
+{
+ /* enable ep0 interrupts */
+ pch_udc_enable_ep_interrupts(dev, UDC_EPINT_IN_EP0 |
+ UDC_EPINT_OUT_EP0);
+ /* enable device interrupts */
+ pch_udc_enable_interrupts(dev, UDC_DEVINT_UR | UDC_DEVINT_US |
+ UDC_DEVINT_ES | UDC_DEVINT_ENUM |
+ UDC_DEVINT_SI | UDC_DEVINT_SC);
+}
+
+/**
+ * gadget_release() - Free the gadget driver private data
+ * @pdev reference to struct pci_dev
+ */
+static void gadget_release(struct device *pdev)
+{
+ struct pch_udc_dev *dev = dev_get_drvdata(pdev);
+
+ kfree(dev);
+}
+
+/**
+ * pch_udc_pcd_reinit() - This API initializes the endpoint structures
+ * @dev: Reference to the driver structure
+ */
+static void pch_udc_pcd_reinit(struct pch_udc_dev *dev)
+{
+ const char *const ep_string[] = {
+ ep0_string, "ep0out", "ep1in", "ep1out", "ep2in", "ep2out",
+ "ep3in", "ep3out", "ep4in", "ep4out", "ep5in", "ep5out",
+ "ep6in", "ep6out", "ep7in", "ep7out", "ep8in", "ep8out",
+ "ep9in", "ep9out", "ep10in", "ep10out", "ep11in", "ep11out",
+ "ep12in", "ep12out", "ep13in", "ep13out", "ep14in", "ep14out",
+ "ep15in", "ep15out",
+ };
+ int i;
+
+ dev->gadget.speed = USB_SPEED_UNKNOWN;
+ INIT_LIST_HEAD(&dev->gadget.ep_list);
+
+ /* Initialize the endpoints structures */
+ memset(dev->ep, 0, sizeof dev->ep);
+ for (i = 0; i < PCH_UDC_EP_NUM; i++) {
+ struct pch_udc_ep *ep = &dev->ep[i];
+ ep->dev = dev;
+ ep->halted = 1;
+ ep->num = i / 2;
+ ep->in = ~i & 1;
+ ep->ep.name = ep_string[i];
+ ep->ep.ops = &pch_udc_ep_ops;
+ if (ep->in)
+ ep->offset_addr = ep->num * UDC_EP_REG_SHIFT;
+ else
+ ep->offset_addr = (UDC_EPINT_OUT_SHIFT + ep->num) *
+ UDC_EP_REG_SHIFT;
+ /* need to set ep->ep.maxpacket and set Default Configuration?*/
+ ep->ep.maxpacket = UDC_BULK_MAX_PKT_SIZE;
+ list_add_tail(&ep->ep.ep_list, &dev->gadget.ep_list);
+ INIT_LIST_HEAD(&ep->queue);
+ }
+ dev->ep[UDC_EP0IN_IDX].ep.maxpacket = UDC_EP0IN_MAX_PKT_SIZE;
+ dev->ep[UDC_EP0OUT_IDX].ep.maxpacket = UDC_EP0OUT_MAX_PKT_SIZE;
+
+ dev->dma_addr = pci_map_single(dev->pdev, dev->ep0out_buf, 256,
+ PCI_DMA_FROMDEVICE);
+
+ /* remove ep0 in and out from the list. They have own pointer */
+ list_del_init(&dev->ep[UDC_EP0IN_IDX].ep.ep_list);
+ list_del_init(&dev->ep[UDC_EP0OUT_IDX].ep.ep_list);
+
+ dev->gadget.ep0 = &dev->ep[UDC_EP0IN_IDX].ep;
+ INIT_LIST_HEAD(&dev->gadget.ep0->ep_list);
+}
+
+/**
+ * pch_udc_pcd_init() - This API initializes the driver structure
+ * @dev: Reference to the driver structure
+ *
+ * Return codes:
+ * 0: Success
+ */
+static int pch_udc_pcd_init(struct pch_udc_dev *dev)
+{
+ pch_udc_init(dev);
+ pch_udc_pcd_reinit(dev);
+ return 0;
+}
+
+/**
+ * init_dma_pools() - create dma pools during initialization
+ * @pdev: reference to struct pci_dev
+ */
+static int init_dma_pools(struct pch_udc_dev *dev)
+{
+ struct pch_udc_stp_dma_desc *td_stp;
+ struct pch_udc_data_dma_desc *td_data;
+
+ /* DMA setup */
+ dev->data_requests = pci_pool_create("data_requests", dev->pdev,
+ sizeof(struct pch_udc_data_dma_desc), 0, 0);
+ if (!dev->data_requests) {
+ dev_err(&dev->pdev->dev, "%s: can't get request data pool\n",
+ __func__);
+ return -ENOMEM;
+ }
+
+ /* dma desc for setup data */
+ dev->stp_requests = pci_pool_create("setup requests", dev->pdev,
+ sizeof(struct pch_udc_stp_dma_desc), 0, 0);
+ if (!dev->stp_requests) {
+ dev_err(&dev->pdev->dev, "%s: can't get setup request pool\n",
+ __func__);
+ return -ENOMEM;
+ }
+ /* setup */
+ td_stp = pci_pool_alloc(dev->stp_requests, GFP_KERNEL,
+ &dev->ep[UDC_EP0OUT_IDX].td_stp_phys);
+ if (!td_stp) {
+ dev_err(&dev->pdev->dev,
+ "%s: can't allocate setup dma descriptor\n", __func__);
+ return -ENOMEM;
+ }
+ dev->ep[UDC_EP0OUT_IDX].td_stp = td_stp;
+
+ /* data: 0 packets !? */
+ td_data = pci_pool_alloc(dev->data_requests, GFP_KERNEL,
+ &dev->ep[UDC_EP0OUT_IDX].td_data_phys);
+ if (!td_data) {
+ dev_err(&dev->pdev->dev,
+ "%s: can't allocate data dma descriptor\n", __func__);
+ return -ENOMEM;
+ }
+ dev->ep[UDC_EP0OUT_IDX].td_data = td_data;
+ dev->ep[UDC_EP0IN_IDX].td_stp = NULL;
+ dev->ep[UDC_EP0IN_IDX].td_stp_phys = 0;
+ dev->ep[UDC_EP0IN_IDX].td_data = NULL;
+ dev->ep[UDC_EP0IN_IDX].td_data_phys = 0;
+ return 0;
+}
+
+int usb_gadget_probe_driver(struct usb_gadget_driver *driver,
+ int (*bind)(struct usb_gadget *))
+{
+ struct pch_udc_dev *dev = pch_udc;
+ int retval;
+
+ if (!driver || (driver->speed == USB_SPEED_UNKNOWN) || !bind ||
+ !driver->setup || !driver->unbind || !driver->disconnect) {
+ dev_err(&dev->pdev->dev,
+ "%s: invalid driver parameter\n", __func__);
+ return -EINVAL;
+ }
+
+ if (!dev)
+ return -ENODEV;
+
+ if (dev->driver) {
+ dev_err(&dev->pdev->dev, "%s: already bound\n", __func__);
+ return -EBUSY;
+ }
+ driver->driver.bus = NULL;
+ dev->driver = driver;
+ dev->gadget.dev.driver = &driver->driver;
+
+ /* Invoke the bind routine of the gadget driver */
+ retval = bind(&dev->gadget);
+
+ if (retval) {
+ dev_err(&dev->pdev->dev, "%s: binding to %s returning %d\n",
+ __func__, driver->driver.name, retval);
+ dev->driver = NULL;
+ dev->gadget.dev.driver = NULL;
+ return retval;
+ }
+ /* get ready for ep0 traffic */
+ pch_udc_setup_ep0(dev);
+
+ /* clear SD */
+ pch_udc_clear_disconnect(dev);
+
+ dev->connected = 1;
+ return 0;
+}
+EXPORT_SYMBOL(usb_gadget_probe_driver);
+
+int usb_gadget_unregister_driver(struct usb_gadget_driver *driver)
+{
+ struct pch_udc_dev *dev = pch_udc;
+
+ if (!dev)
+ return -ENODEV;
+
+ if (!driver || (driver != dev->driver)) {
+ dev_err(&dev->pdev->dev,
+ "%s: invalid driver parameter\n", __func__);
+ return -EINVAL;
+ }
+
+ pch_udc_disable_interrupts(dev, UDC_DEVINT_MSK);
+
+ /* Assues that there are no pending requets with this driver */
+ driver->unbind(&dev->gadget);
+ dev->gadget.dev.driver = NULL;
+ dev->driver = NULL;
+ dev->connected = 0;
+
+ /* set SD */
+ pch_udc_set_disconnect(dev);
+ return 0;
+}
+EXPORT_SYMBOL(usb_gadget_unregister_driver);
+
+static void pch_udc_shutdown(struct pci_dev *pdev)
+{
+ struct pch_udc_dev *dev = pci_get_drvdata(pdev);
+
+ pch_udc_disable_interrupts(dev, UDC_DEVINT_MSK);
+ pch_udc_disable_ep_interrupts(dev, UDC_EPINT_MSK_DISABLE_ALL);
+
+ /* disable the pullup so the host will think we're gone */
+ pch_udc_set_disconnect(dev);
+}
+
+static void pch_udc_remove(struct pci_dev *pdev)
+{
+ struct pch_udc_dev *dev = pci_get_drvdata(pdev);
+
+ /* gadget driver must not be registered */
+ if (dev->driver)
+ dev_err(&pdev->dev,
+ "%s: gadget driver still bound!!!\n", __func__);
+ /* dma pool cleanup */
+ if (dev->data_requests)
+ pci_pool_destroy(dev->data_requests);
+
+ if (dev->stp_requests) {
+ /* cleanup DMA desc's for ep0in */
+ if (dev->ep[UDC_EP0OUT_IDX].td_stp) {
+ pci_pool_free(dev->stp_requests,
+ dev->ep[UDC_EP0OUT_IDX].td_stp,
+ dev->ep[UDC_EP0OUT_IDX].td_stp_phys);
+ }
+ if (dev->ep[UDC_EP0OUT_IDX].td_data) {
+ pci_pool_free(dev->stp_requests,
+ dev->ep[UDC_EP0OUT_IDX].td_data,
+ dev->ep[UDC_EP0OUT_IDX].td_data_phys);
+ }
+ pci_pool_destroy(dev->stp_requests);
+ }
+
+ pch_udc_exit(dev);
+
+ if (dev->irq_registered)
+ free_irq(pdev->irq, dev);
+ if (dev->base_addr)
+ iounmap(dev->base_addr);
+ if (dev->mem_region)
+ release_mem_region(dev->phys_addr,
+ pci_resource_len(pdev, PCH_UDC_PCI_BAR));
+ if (dev->active)
+ pci_disable_device(pdev);
+ if (dev->registered)
+ device_unregister(&dev->gadget.dev);
+ kfree(dev);
+ pci_set_drvdata(pdev, NULL);
+}
+
+#ifdef CONFIG_PM
+static int pch_udc_suspend(struct pci_dev *pdev, pm_message_t state)
+{
+ struct pch_udc_dev *dev = pci_get_drvdata(pdev);
+
+ pch_udc_disable_interrupts(dev, UDC_DEVINT_MSK);
+ pch_udc_disable_ep_interrupts(dev, UDC_EPINT_MSK_DISABLE_ALL);
+
+ pci_disable_device(pdev);
+ pci_enable_wake(pdev, PCI_D3hot, 0);
+
+ if (pci_save_state(pdev)) {
+ dev_err(&pdev->dev,
+ "%s: could not save PCI config state\n", __func__);
+ return -ENOMEM;
+ }
+ pci_set_power_state(pdev, pci_choose_state(pdev, state));
+ return 0;
+}
+
+static int pch_udc_resume(struct pci_dev *pdev)
+{
+ int ret;
+
+ pci_set_power_state(pdev, PCI_D0);
+ ret = pci_restore_state(pdev);
+ if (ret) {
+ dev_err(&pdev->dev, "%s: pci_restore_state failed\n", __func__);
+ return ret;
+ }
+ ret = pci_enable_device(pdev);
+ if (ret) {
+ dev_err(&pdev->dev, "%s: pci_enable_device failed\n", __func__);
+ return ret;
+ }
+ pci_enable_wake(pdev, PCI_D3hot, 0);
+ return 0;
+}
+#else
+#define pch_udc_suspend NULL
+#define pch_udc_resume NULL
+#endif /* CONFIG_PM */
+
+static int pch_udc_probe(struct pci_dev *pdev,
+ const struct pci_device_id *id)
+{
+ unsigned long resource;
+ unsigned long len;
+ int retval;
+ struct pch_udc_dev *dev;
+
+ /* one udc only */
+ if (pch_udc) {
+ pr_err("%s: already probed\n", __func__);
+ return -EBUSY;
+ }
+ /* init */
+ dev = kzalloc(sizeof *dev, GFP_KERNEL);
+ if (!dev) {
+ pr_err("%s: no memory for device structure\n", __func__);
+ return -ENOMEM;
+ }
+ /* pci setup */
+ if (pci_enable_device(pdev) < 0) {
+ kfree(dev);
+ pr_err("%s: pci_enable_device failed\n", __func__);
+ return -ENODEV;
+ }
+ dev->active = 1;
+ pci_set_drvdata(pdev, dev);
+
+ /* PCI resource allocation */
+ resource = pci_resource_start(pdev, 1);
+ len = pci_resource_len(pdev, 1);
+
+ if (!request_mem_region(resource, len, KBUILD_MODNAME)) {
+ dev_err(&pdev->dev, "%s: pci device used already\n", __func__);
+ retval = -EBUSY;
+ goto finished;
+ }
+ dev->phys_addr = resource;
+ dev->mem_region = 1;
+
+ dev->base_addr = ioremap_nocache(resource, len);
+ if (!dev->base_addr) {
+ pr_err("%s: device memory cannot be mapped\n", __func__);
+ retval = -ENOMEM;
+ goto finished;
+ }
+ if (!pdev->irq) {
+ dev_err(&pdev->dev, "%s: irq not set\n", __func__);
+ retval = -ENODEV;
+ goto finished;
+ }
+ pch_udc = dev;
+ /* initialize the hardware */
+ if (pch_udc_pcd_init(dev))
+ goto finished;
+ if (request_irq(pdev->irq, pch_udc_isr, IRQF_SHARED, KBUILD_MODNAME,
+ dev)) {
+ dev_err(&pdev->dev, "%s: request_irq(%d) fail\n", __func__,
+ pdev->irq);
+ retval = -ENODEV;
+ goto finished;
+ }
+ dev->irq = pdev->irq;
+ dev->irq_registered = 1;
+
+ pci_set_master(pdev);
+ pci_try_set_mwi(pdev);
+
+ /* device struct setup */
+ spin_lock_init(&dev->lock);
+ dev->pdev = pdev;
+ dev->gadget.ops = &pch_udc_ops;
+
+ retval = init_dma_pools(dev);
+ if (retval)
+ goto finished;
+
+ dev_set_name(&dev->gadget.dev, "gadget");
+ dev->gadget.dev.parent = &pdev->dev;
+ dev->gadget.dev.dma_mask = pdev->dev.dma_mask;
+ dev->gadget.dev.release = gadget_release;
+ dev->gadget.name = KBUILD_MODNAME;
+ dev->gadget.is_dualspeed = 1;
+
+ retval = device_register(&dev->gadget.dev);
+ if (retval)
+ goto finished;
+ dev->registered = 1;
+
+ /* Put the device in disconnected state till a driver is bound */
+ pch_udc_set_disconnect(dev);
+ return 0;
+
+finished:
+ pch_udc_remove(pdev);
+ return retval;
+}
+
+static DEFINE_PCI_DEVICE_TABLE(pch_udc_pcidev_id) = {
+ {
+ PCI_DEVICE(PCI_VENDOR_ID_INTEL, PCI_DEVICE_ID_INTEL_EG20T_UDC),
+ .class = (PCI_CLASS_SERIAL_USB << 8) | 0xfe,
+ .class_mask = 0xffffffff,
+ },
+ { 0 },
+};
+
+MODULE_DEVICE_TABLE(pci, pch_udc_pcidev_id);
+
+
+static struct pci_driver pch_udc_driver = {
+ .name = KBUILD_MODNAME,
+ .id_table = pch_udc_pcidev_id,
+ .probe = pch_udc_probe,
+ .remove = pch_udc_remove,
+ .suspend = pch_udc_suspend,
+ .resume = pch_udc_resume,
+ .shutdown = pch_udc_shutdown,
+};
+
+static int __init pch_udc_pci_init(void)
+{
+ return pci_register_driver(&pch_udc_driver);
+}
+module_init(pch_udc_pci_init);
+
+static void __exit pch_udc_pci_exit(void)
+{
+ pci_unregister_driver(&pch_udc_driver);
+}
+module_exit(pch_udc_pci_exit);
+
+MODULE_DESCRIPTION("Intel EG20T USB Device Controller");
+MODULE_AUTHOR("OKI SEMICONDUCTOR, <toshiharu-linux@dsn.okisemi.com>");
+MODULE_LICENSE("GPL");
diff --git a/drivers/usb/gadget/u_audio.c b/drivers/usb/gadget/u_audio.c
index 7a86d2c9109..59ffe1ecf1c 100644
--- a/drivers/usb/gadget/u_audio.c
+++ b/drivers/usb/gadget/u_audio.c
@@ -255,6 +255,7 @@ static int gaudio_open_snd_dev(struct gaudio *card)
ERROR(card, "No such PCM capture device: %s\n", fn_cap);
snd->substream = NULL;
snd->card = NULL;
+ snd->filp = NULL;
} else {
pcm_file = snd->filp->private_data;
snd->substream = pcm_file->substream;
@@ -273,17 +274,17 @@ static int gaudio_close_snd_dev(struct gaudio *gau)
/* Close control device */
snd = &gau->control;
- if (!IS_ERR(snd->filp))
+ if (snd->filp)
filp_close(snd->filp, current->files);
/* Close PCM playback device and setup substream */
snd = &gau->playback;
- if (!IS_ERR(snd->filp))
+ if (snd->filp)
filp_close(snd->filp, current->files);
/* Close PCM capture device and setup substream */
snd = &gau->capture;
- if (!IS_ERR(snd->filp))
+ if (snd->filp)
filp_close(snd->filp, current->files);
return 0;
@@ -304,8 +305,7 @@ int __init gaudio_setup(struct gaudio *card)
ret = gaudio_open_snd_dev(card);
if (ret)
ERROR(card, "we need at least one control device\n");
-
- if (!the_card)
+ else if (!the_card)
the_card = card;
return ret;
diff --git a/drivers/usb/gadget/u_ether.c b/drivers/usb/gadget/u_ether.c
index fbe86ca9580..e3454fe46b4 100644
--- a/drivers/usb/gadget/u_ether.c
+++ b/drivers/usb/gadget/u_ether.c
@@ -240,6 +240,9 @@ rx_submit(struct eth_dev *dev, struct usb_request *req, gfp_t gfp_flags)
size += out->maxpacket - 1;
size -= size % out->maxpacket;
+ if (dev->port_usb->is_fixed)
+ size = max(size, dev->port_usb->fixed_out_len);
+
skb = alloc_skb(size + NET_IP_ALIGN, gfp_flags);
if (skb == NULL) {
DBG(dev, "no rx skb\n");
@@ -578,12 +581,19 @@ static netdev_tx_t eth_start_xmit(struct sk_buff *skb,
req->context = skb;
req->complete = tx_complete;
+ /* NCM requires no zlp if transfer is dwNtbInMaxSize */
+ if (dev->port_usb->is_fixed &&
+ length == dev->port_usb->fixed_in_len &&
+ (length % in->maxpacket) == 0)
+ req->zero = 0;
+ else
+ req->zero = 1;
+
/* use zlp framing on tx for strict CDC-Ether conformance,
* though any robust network rx path ignores extra padding.
* and some hardware doesn't like to write zlps.
*/
- req->zero = 1;
- if (!dev->zlp && (length % in->maxpacket) == 0)
+ if (req->zero && !dev->zlp && (length % in->maxpacket) == 0)
length++;
req->length = length;
diff --git a/drivers/usb/gadget/u_ether.h b/drivers/usb/gadget/u_ether.h
index 3c8c0c9f9d7..b56e1e7d423 100644
--- a/drivers/usb/gadget/u_ether.h
+++ b/drivers/usb/gadget/u_ether.h
@@ -62,6 +62,10 @@ struct gether {
/* hooks for added framing, as needed for RNDIS and EEM. */
u32 header_len;
+ /* NCM requires fixed size bundles */
+ bool is_fixed;
+ u32 fixed_out_len;
+ u32 fixed_in_len;
struct sk_buff *(*wrap)(struct gether *port,
struct sk_buff *skb);
int (*unwrap)(struct gether *port,
@@ -103,6 +107,7 @@ static inline bool can_support_ecm(struct usb_gadget *gadget)
/* each configuration may bind one instance of an ethernet link */
int geth_bind_config(struct usb_configuration *c, u8 ethaddr[ETH_ALEN]);
int ecm_bind_config(struct usb_configuration *c, u8 ethaddr[ETH_ALEN]);
+int ncm_bind_config(struct usb_configuration *c, u8 ethaddr[ETH_ALEN]);
int eem_bind_config(struct usb_configuration *c);
#ifdef USB_ETH_RNDIS