diff options
author | Linus Torvalds <torvalds@linux-foundation.org> | 2011-01-07 13:16:28 -0800 |
---|---|---|
committer | Linus Torvalds <torvalds@linux-foundation.org> | 2011-01-07 13:16:28 -0800 |
commit | 3e5b08cbbf78bedd316904ab0cf3b27119433ee5 (patch) | |
tree | 0365745c1b7441c1868551c024410c829c3accc6 /drivers/usb/gadget | |
parent | da40d036fd716f0efb2917076220814b1e927ae1 (diff) | |
parent | 2af10844eb6ed104f9505bf3a7ba3ceb02264f31 (diff) | |
download | op-kernel-dev-3e5b08cbbf78bedd316904ab0cf3b27119433ee5.zip op-kernel-dev-3e5b08cbbf78bedd316904ab0cf3b27119433ee5.tar.gz |
Merge branch 'usb-next' of git://git.kernel.org/pub/scm/linux/kernel/git/gregkh/usb-2.6
* 'usb-next' of git://git.kernel.org/pub/scm/linux/kernel/git/gregkh/usb-2.6: (144 commits)
USB: add support for Dream Cheeky DL100B Webmail Notifier (1d34:0004)
USB: serial: ftdi_sio: add support for TIOCSERGETLSR
USB: ehci-mxc: Setup portsc register prior to accessing OTG viewport
USB: atmel_usba_udc: fix freeing irq in usba_udc_remove()
usb: ehci-omap: fix tll channel enable mask
usb: ohci-omap3: fix trivial typo
USB: gadget: ci13xxx: don't assume that PAGE_SIZE is 4096
USB: gadget: ci13xxx: fix complete() callback for no_interrupt rq's
USB: gadget: update ci13xxx to work with g_ether
USB: gadgets: ci13xxx: fix probing of compiled-in gadget drivers
Revert "USB: musb: pm: don't rely fully on clock support"
Revert "USB: musb: blackfin: pm: make it work"
USB: uas: Use GFP_NOIO instead of GFP_KERNEL in I/O submission path
USB: uas: Ensure we only bind to a UAS interface
USB: uas: Rename sense pipe and sense urb to status pipe and status urb
USB: uas: Use kzalloc instead of kmalloc
USB: uas: Fix up the Sense IU
usb: musb: core: kill unneeded #include's
DA8xx: assign name to MUSB IRQ resource
usb: gadget: g_ncm added
...
Manually fix up trivial conflicts in USB Kconfig changes in:
arch/arm/mach-omap2/Kconfig
arch/sh/Kconfig
drivers/usb/Kconfig
drivers/usb/host/ehci-hcd.c
and annoying chip clock data conflicts in:
arch/arm/mach-omap2/clock3xxx_data.c
arch/arm/mach-omap2/clock44xx_data.c
Diffstat (limited to 'drivers/usb/gadget')
28 files changed, 8604 insertions, 878 deletions
diff --git a/drivers/usb/gadget/Kconfig b/drivers/usb/gadget/Kconfig index 607d0db..1dc9739 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 5780db4..55f5e8a 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 9034e034..f8dd726 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/atmel_usba_udc.c b/drivers/usb/gadget/atmel_usba_udc.c index 717ff65..e7c65a4 100644 --- a/drivers/usb/gadget/atmel_usba_udc.c +++ b/drivers/usb/gadget/atmel_usba_udc.c @@ -2057,8 +2057,10 @@ static int __exit usba_udc_remove(struct platform_device *pdev) usba_ep_cleanup_debugfs(&usba_ep[i]); usba_cleanup_debugfs(udc); - if (gpio_is_valid(udc->vbus_pin)) + if (gpio_is_valid(udc->vbus_pin)) { + free_irq(gpio_to_irq(udc->vbus_pin), udc); gpio_free(udc->vbus_pin); + } free_irq(udc->irq, udc); kfree(usba_ep); diff --git a/drivers/usb/gadget/ci13xxx_msm.c b/drivers/usb/gadget/ci13xxx_msm.c new file mode 100644 index 0000000..139ac94 --- /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 0000000..883ab5e --- /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 98b36fc..31656a2 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; } @@ -1449,7 +1460,7 @@ static int _hardware_enqueue(struct ci13xxx_ep *mEp, struct ci13xxx_req *mReq) mReq->ptr->page[0] = mReq->req.dma; for (i = 1; i < 5; i++) mReq->ptr->page[i] = - (mReq->req.dma + i * PAGE_SIZE) & ~TD_RESERVED_MASK; + (mReq->req.dma + i * CI13XXX_PAGE_SIZE) & ~TD_RESERVED_MASK; /* * QH configuration @@ -1540,7 +1551,7 @@ __acquires(mEp->lock) list_del_init(&mReq->queue); mReq->req.status = -ESHUTDOWN; - if (!mReq->req.no_interrupt && mReq->req.complete != NULL) { + if (mReq->req.complete != NULL) { spin_unlock(mEp->lock); mReq->req.complete(&mEp->ep, &mReq->req); spin_lock(mEp->lock); @@ -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; @@ -1789,18 +1794,20 @@ __acquires(mEp->lock) dbg_done(_usb_addr(mEp), mReq->ptr->token, retval); - if (!mReq->req.no_interrupt && mReq->req.complete != NULL) { + if (!list_empty(&mEp->qh[mEp->dir].queue)) { + struct ci13xxx_req* mReqEnq; + + mReqEnq = list_entry(mEp->qh[mEp->dir].queue.next, + struct ci13xxx_req, queue); + _hardware_enqueue(mEp, mReqEnq); + } + + if (mReq->req.complete != NULL) { spin_unlock(mEp->lock); mReq->req.complete(&mEp->ep, &mReq->req); spin_lock(mEp->lock); } - if (!list_empty(&mEp->qh[mEp->dir].queue)) { - mReq = list_entry(mEp->qh[mEp->dir].queue.next, - struct ci13xxx_req, queue); - _hardware_enqueue(mEp, mReq); - } - done: return retval; } @@ -2061,7 +2068,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 +2076,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 +2090,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; } @@ -2157,8 +2159,8 @@ static int ep_queue(struct usb_ep *ep, struct usb_request *req, goto done; } - if (req->length > (4 * PAGE_SIZE)) { - req->length = (4 * PAGE_SIZE); + if (req->length > (4 * CI13XXX_PAGE_SIZE)) { + req->length = (4 * CI13XXX_PAGE_SIZE); retval = -EMSGSIZE; warn("request length truncated"); } @@ -2170,8 +2172,10 @@ static int ep_queue(struct usb_ep *ep, struct usb_request *req, mReq->req.actual = 0; list_add_tail(&mReq->queue, &mEp->qh[mEp->dir].queue); - retval = _hardware_enqueue(mEp, mReq); - if (retval == -EALREADY || retval == -EBUSY) { + if (list_is_singular(&mEp->qh[mEp->dir].queue)) + retval = _hardware_enqueue(mEp, mReq); + + if (retval == -EALREADY) { dbg_event(_usb_addr(mEp), "QUEUE", retval); retval = 0; } @@ -2209,7 +2213,7 @@ static int ep_dequeue(struct usb_ep *ep, struct usb_request *req) list_del_init(&mReq->queue); req->status = -ECONNRESET; - if (!mReq->req.no_interrupt && mReq->req.complete != NULL) { + if (mReq->req.complete != NULL) { spin_unlock(mEp->lock); mReq->req.complete(&mEp->ep, &mReq->req); spin_lock(mEp->lock); @@ -2332,12 +2336,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 @@ -2358,7 +2397,6 @@ int usb_gadget_probe_driver(struct usb_gadget_driver *driver, if (driver == NULL || bind == NULL || - driver->unbind == NULL || driver->setup == NULL || driver->disconnect == NULL || driver->suspend == NULL || @@ -2372,13 +2410,13 @@ int usb_gadget_probe_driver(struct usb_gadget_driver *driver, /* alloc resources */ udc->qh_pool = dma_pool_create("ci13xxx_qh", &udc->gadget.dev, sizeof(struct ci13xxx_qh), - 64, PAGE_SIZE); + 64, CI13XXX_PAGE_SIZE); if (udc->qh_pool == NULL) return -ENOMEM; udc->td_pool = dma_pool_create("ci13xxx_td", &udc->gadget.dev, sizeof(struct ci13xxx_td), - 64, PAGE_SIZE); + 64, CI13XXX_PAGE_SIZE); if (udc->td_pool == NULL) { dma_pool_destroy(udc->qh_pool); udc->qh_pool = NULL; @@ -2390,7 +2428,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 +2447,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 +2468,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 +2475,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 +2525,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 +2597,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 +2663,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 +2680,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 +2770,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 +2782,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 4026e9c..f61fed0 100644 --- a/drivers/usb/gadget/ci13xxx_udc.h +++ b/drivers/usb/gadget/ci13xxx_udc.h @@ -19,6 +19,7 @@ /****************************************************************************** * DEFINE *****************************************************************************/ +#define CI13XXX_PAGE_SIZE 4096ul /* page size for TD's */ #define ENDPT_MAX (16) #define CTRL_PAYLOAD_MAX (64) #define RX (0) /* similar to USB_DIR_OUT but can be used as an index */ @@ -97,9 +98,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 +124,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 +176,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 8572dad..f6ff845 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) if (bcdDevice) cdev->desc.bcdDevice = cpu_to_le16(bcdDevice); - /* 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 1d2a2ab..13b9f47 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 484c5ba..1499f9e 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 838286b..b5dbb23 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, ¤t->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 0000000..130eee6 --- /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 d4fdf65..a6eacb5 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 af75e36..ebf6970 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 e511fec..5c2720d 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 ed02664..1210534 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 b48ad59..7136c24 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 b8ec954..7779724 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 0769179..0182242 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 0000000..65f1f7c --- /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 0000000..d5468a7 --- /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 0000000..d4dea97 --- /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 0000000..99c179a --- /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 0000000..0c8dd81 --- /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 7a86d2c..59ffe1e 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 fbe86ca..e3454fe 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 3c8c0c9..b56e1e7 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 |