summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorLinus Torvalds <torvalds@linux-foundation.org>2009-12-11 23:22:55 (GMT)
committerLinus Torvalds <torvalds@linux-foundation.org>2009-12-11 23:22:55 (GMT)
commit748e566b7e24541e05e3e70be311887a1262f2a1 (patch)
tree41cc3a9aa04918cc17efa575baf6dbf87f40ddba
parent5f1141eb352ea79d849920039503e40dd623fffa (diff)
parentacf509ae28301d78b022c534c26b1e4765c18f2b (diff)
downloadlinux-748e566b7e24541e05e3e70be311887a1262f2a1.tar.xz
Merge git://git.kernel.org/pub/scm/linux/kernel/git/gregkh/usb-2.6
* git://git.kernel.org/pub/scm/linux/kernel/git/gregkh/usb-2.6: (122 commits) USB: mos7840: add device IDs for B&B electronics devices USB: ftdi_sio: add USB device ID's for B&B Electronics line USB: musb: musb_host: fix sparse warning USB: musb: musb_gadget: fix sparse warning USB: musb: omap2430: fix sparse warning USB: core: message: fix sparse warning USB: core: hub: fix sparse warning USB: core: fix sparse warning for static function USB: Added USB_ETH_RNDIS to use instead of CONFIG_USB_ETH_RNDIS USB: Check bandwidth when switching alt settings. USB: Refactor code to find alternate interface settings. USB: xhci: Fix command completion after a drop endpoint. USB: xhci: Make reverting an alt setting "unfailable". USB: usbtmc: Use usb_clear_halt() instead of custom code. USB: xhci: Add correct email and files to MAINTAINERS entry. USB: ehci-omap.c: introduce missing kfree USB: xhci-mem.c: introduce missing kfree USB: add remove_id sysfs attr for usb drivers USB: g_multi kconfig: fix depends and help text USB: option: add pid for ZTE ...
-rw-r--r--Documentation/ABI/testing/sysfs-bus-usb13
-rw-r--r--Documentation/ABI/testing/sysfs-class-uwb_rc-wusbhc13
-rw-r--r--Documentation/kernel-parameters.txt2
-rw-r--r--Documentation/powerpc/dts-bindings/xilinx.txt11
-rw-r--r--Documentation/usb/power-management.txt69
-rw-r--r--MAINTAINERS4
-rw-r--r--arch/arm/plat-mxc/Makefile1
-rw-r--r--arch/arm/plat-mxc/ehci.c92
-rw-r--r--arch/arm/plat-mxc/include/mach/mxc_ehci.h37
-rw-r--r--drivers/bluetooth/btusb.c2
-rw-r--r--drivers/hid/usbhid/hid-core.c8
-rw-r--r--drivers/net/wimax/i2400m/usb.c7
-rw-r--r--drivers/usb/Kconfig2
-rw-r--r--drivers/usb/Makefile2
-rw-r--r--drivers/usb/class/cdc-acm.c58
-rw-r--r--drivers/usb/class/usbtmc.c54
-rw-r--r--drivers/usb/core/driver.c135
-rw-r--r--drivers/usb/core/file.c1
-rw-r--r--drivers/usb/core/generic.c2
-rw-r--r--drivers/usb/core/hcd.c82
-rw-r--r--drivers/usb/core/hcd.h19
-rw-r--r--drivers/usb/core/hub.c142
-rw-r--r--drivers/usb/core/message.c82
-rw-r--r--drivers/usb/core/sysfs.c61
-rw-r--r--drivers/usb/core/urb.c22
-rw-r--r--drivers/usb/core/usb.c67
-rw-r--r--drivers/usb/core/usb.h1
-rw-r--r--drivers/usb/gadget/Kconfig60
-rw-r--r--drivers/usb/gadget/Makefile4
-rw-r--r--drivers/usb/gadget/at91_udc.c6
-rw-r--r--drivers/usb/gadget/audio.c115
-rw-r--r--drivers/usb/gadget/composite.c59
-rw-r--r--drivers/usb/gadget/ether.c16
-rw-r--r--drivers/usb/gadget/f_acm.c28
-rw-r--r--drivers/usb/gadget/f_audio.c76
-rw-r--r--drivers/usb/gadget/f_mass_storage.c3091
-rw-r--r--drivers/usb/gadget/f_rndis.c35
-rw-r--r--drivers/usb/gadget/file_storage.c879
-rw-r--r--drivers/usb/gadget/mass_storage.c240
-rw-r--r--drivers/usb/gadget/multi.c358
-rw-r--r--drivers/usb/gadget/storage_common.c778
-rw-r--r--drivers/usb/gadget/u_ether.h2
-rw-r--r--drivers/usb/host/Kconfig22
-rw-r--r--drivers/usb/host/ehci-hcd.c19
-rw-r--r--drivers/usb/host/ehci-hub.c2
-rw-r--r--drivers/usb/host/ehci-mxc.c296
-rw-r--r--drivers/usb/host/ehci-omap.c756
-rw-r--r--drivers/usb/host/ehci-q.c32
-rw-r--r--drivers/usb/host/ehci-sched.c36
-rw-r--r--drivers/usb/host/ehci-xilinx-of.c300
-rw-r--r--drivers/usb/host/isp1362.h4
-rw-r--r--drivers/usb/host/ohci-at91.c10
-rw-r--r--drivers/usb/host/ohci-pnx4008.c8
-rw-r--r--drivers/usb/host/r8a66597-hcd.c4
-rw-r--r--drivers/usb/host/whci/debug.c20
-rw-r--r--drivers/usb/host/whci/hcd.c1
-rw-r--r--drivers/usb/host/whci/qset.c350
-rw-r--r--drivers/usb/host/whci/whcd.h9
-rw-r--r--drivers/usb/host/whci/whci-hc.h14
-rw-r--r--drivers/usb/host/xhci-hcd.c139
-rw-r--r--drivers/usb/host/xhci-mem.c255
-rw-r--r--drivers/usb/host/xhci-pci.c2
-rw-r--r--drivers/usb/host/xhci-ring.c343
-rw-r--r--drivers/usb/host/xhci.h37
-rw-r--r--drivers/usb/misc/usbtest.c7
-rw-r--r--drivers/usb/mon/mon_bin.c51
-rw-r--r--drivers/usb/mon/mon_text.c23
-rw-r--r--drivers/usb/musb/Kconfig5
-rw-r--r--drivers/usb/musb/blackfin.c16
-rw-r--r--drivers/usb/musb/blackfin.h37
-rw-r--r--drivers/usb/musb/musb_core.c10
-rw-r--r--drivers/usb/musb/musb_core.h8
-rw-r--r--drivers/usb/musb/musb_dma.h11
-rw-r--r--drivers/usb/musb/musb_gadget.c196
-rw-r--r--drivers/usb/musb/musb_gadget.h4
-rw-r--r--drivers/usb/musb/musb_gadget_ep0.c70
-rw-r--r--drivers/usb/musb/musb_host.c10
-rw-r--r--drivers/usb/musb/musb_regs.h4
-rw-r--r--drivers/usb/musb/musbhsdma.c12
-rw-r--r--drivers/usb/musb/omap2430.c2
-rw-r--r--drivers/usb/otg/Kconfig9
-rw-r--r--drivers/usb/otg/Makefile1
-rw-r--r--drivers/usb/otg/twl4030-usb.c4
-rw-r--r--drivers/usb/otg/ulpi.c136
-rw-r--r--drivers/usb/serial/ark3116.c975
-rw-r--r--drivers/usb/serial/ftdi_sio.c32
-rw-r--r--drivers/usb/serial/ftdi_sio.h14
-rw-r--r--drivers/usb/serial/mos7840.c24
-rw-r--r--drivers/usb/serial/option.c39
-rw-r--r--drivers/usb/serial/sierra.c91
-rw-r--r--drivers/usb/storage/scsiglue.c3
-rw-r--r--drivers/usb/storage/transport.c17
-rw-r--r--drivers/usb/storage/unusual_devs.h7
-rw-r--r--drivers/usb/storage/usb.c43
-rw-r--r--drivers/usb/storage/usb.h1
-rw-r--r--drivers/usb/usb-skeleton.c4
-rw-r--r--drivers/usb/wusbcore/devconnect.c7
-rw-r--r--drivers/usb/wusbcore/security.c6
-rw-r--r--drivers/usb/wusbcore/wusbhc.c32
-rw-r--r--drivers/usb/wusbcore/wusbhc.h1
-rw-r--r--include/linux/usb.h33
-rw-r--r--include/linux/usb/composite.h1
-rw-r--r--include/linux/usb/otg.h68
-rw-r--r--include/linux/usb/ulpi.h7
-rw-r--r--include/linux/usb_usual.h4
-rw-r--r--scripts/mod/file2alias.c95
106 files changed, 9634 insertions, 1881 deletions
diff --git a/Documentation/ABI/testing/sysfs-bus-usb b/Documentation/ABI/testing/sysfs-bus-usb
index 7772928..deb6b48 100644
--- a/Documentation/ABI/testing/sysfs-bus-usb
+++ b/Documentation/ABI/testing/sysfs-bus-usb
@@ -144,3 +144,16 @@ Description:
Write a 1 to force the device to disconnect
(equivalent to unplugging a wired USB device).
+
+What: /sys/bus/usb/drivers/.../remove_id
+Date: November 2009
+Contact: CHENG Renquan <rqcheng@smu.edu.sg>
+Description:
+ Writing a device ID to this file will remove an ID
+ that was dynamically added via the new_id sysfs entry.
+ The format for the device ID is:
+ idVendor idProduct. After successfully
+ removing an ID, the driver will no longer support the
+ device. This is useful to ensure auto probing won't
+ match the driver to the device. For example:
+ # echo "046d c315" > /sys/bus/usb/drivers/foo/remove_id
diff --git a/Documentation/ABI/testing/sysfs-class-uwb_rc-wusbhc b/Documentation/ABI/testing/sysfs-class-uwb_rc-wusbhc
index 4e8106f..25b1e75 100644
--- a/Documentation/ABI/testing/sysfs-class-uwb_rc-wusbhc
+++ b/Documentation/ABI/testing/sysfs-class-uwb_rc-wusbhc
@@ -23,3 +23,16 @@ Description:
Since this relates to security (specifically, the
lifetime of PTKs and GTKs) it should not be changed
from the default.
+
+What: /sys/class/uwb_rc/uwbN/wusbhc/wusb_phy_rate
+Date: August 2009
+KernelVersion: 2.6.32
+Contact: David Vrabel <david.vrabel@csr.com>
+Description:
+ The maximum PHY rate to use for all connected devices.
+ This is only of limited use for testing and
+ development as the hardware's automatic rate
+ adaptation is better then this simple control.
+
+ Refer to [ECMA-368] section 10.3.1.1 for the value to
+ use.
diff --git a/Documentation/kernel-parameters.txt b/Documentation/kernel-parameters.txt
index 777dc8a..3f886e2 100644
--- a/Documentation/kernel-parameters.txt
+++ b/Documentation/kernel-parameters.txt
@@ -2663,6 +2663,8 @@ and is between 256 and 4096 characters. It is defined in the file
to a common usb-storage quirk flag as follows:
a = SANE_SENSE (collect more than 18 bytes
of sense data);
+ b = BAD_SENSE (don't collect more than 18
+ bytes of sense data);
c = FIX_CAPACITY (decrease the reported
device capacity by one sector);
h = CAPACITY_HEURISTICS (decrease the
diff --git a/Documentation/powerpc/dts-bindings/xilinx.txt b/Documentation/powerpc/dts-bindings/xilinx.txt
index 80339fe..ea68046 100644
--- a/Documentation/powerpc/dts-bindings/xilinx.txt
+++ b/Documentation/powerpc/dts-bindings/xilinx.txt
@@ -292,4 +292,15 @@
- reg-offset : A value of 3 is required
- reg-shift : A value of 2 is required
+ vii) Xilinx USB Host controller
+
+ The Xilinx USB host controller is EHCI compatible but with a different
+ base address for the EHCI registers, and it is always a big-endian
+ USB Host controller. The hardware can be configured as high speed only,
+ or high speed/full speed hybrid.
+
+ Required properties:
+ - xlnx,support-usb-fs: A value 0 means the core is built as high speed
+ only. A value 1 means the core also supports
+ full speed devices.
diff --git a/Documentation/usb/power-management.txt b/Documentation/usb/power-management.txt
index ad64261..c7c1dc2 100644
--- a/Documentation/usb/power-management.txt
+++ b/Documentation/usb/power-management.txt
@@ -2,7 +2,7 @@
Alan Stern <stern@rowland.harvard.edu>
- October 5, 2007
+ November 10, 2009
@@ -123,9 +123,9 @@ relevant attribute files are: wakeup, level, and autosuspend.
power/level
- This file contains one of three words: "on", "auto",
- or "suspend". You can write those words to the file
- to change the device's setting.
+ This file contains one of two words: "on" or "auto".
+ You can write those words to the file to change the
+ device's setting.
"on" means that the device should be resumed and
autosuspend is not allowed. (Of course, system
@@ -134,10 +134,10 @@ relevant attribute files are: wakeup, level, and autosuspend.
"auto" is the normal state in which the kernel is
allowed to autosuspend and autoresume the device.
- "suspend" means that the device should remain
- suspended, and autoresume is not allowed. (But remote
- wakeup may still be allowed, since it is controlled
- separately by the power/wakeup attribute.)
+ (In kernels up to 2.6.32, you could also specify
+ "suspend", meaning that the device should remain
+ suspended and autoresume was not allowed. This
+ setting is no longer supported.)
power/autosuspend
@@ -313,13 +313,14 @@ three of the methods listed above. In addition, a driver indicates
that it supports autosuspend by setting the .supports_autosuspend flag
in its usb_driver structure. It is then responsible for informing the
USB core whenever one of its interfaces becomes busy or idle. The
-driver does so by calling these five functions:
+driver does so by calling these six functions:
int usb_autopm_get_interface(struct usb_interface *intf);
void usb_autopm_put_interface(struct usb_interface *intf);
- int usb_autopm_set_interface(struct usb_interface *intf);
int usb_autopm_get_interface_async(struct usb_interface *intf);
void usb_autopm_put_interface_async(struct usb_interface *intf);
+ void usb_autopm_get_interface_no_resume(struct usb_interface *intf);
+ void usb_autopm_put_interface_no_suspend(struct usb_interface *intf);
The functions work by maintaining a counter in the usb_interface
structure. When intf->pm_usage_count is > 0 then the interface is
@@ -331,11 +332,13 @@ considered to be idle, and the kernel may autosuspend the device.
associated with the device itself rather than any of its interfaces.
This field is used only by the USB core.)
-The driver owns intf->pm_usage_count; it can modify the value however
-and whenever it likes. A nice aspect of the non-async usb_autopm_*
-routines is that the changes they make are protected by the usb_device
-structure's PM mutex (udev->pm_mutex); however drivers may change
-pm_usage_count without holding the mutex. Drivers using the async
+Drivers must not modify intf->pm_usage_count directly; its value
+should be changed only be using the functions listed above. Drivers
+are responsible for insuring that the overall change to pm_usage_count
+during their lifetime balances out to 0 (it may be necessary for the
+disconnect method to call usb_autopm_put_interface() one or more times
+to fulfill this requirement). The first two routines use the PM mutex
+in struct usb_device for mutual exclusion; drivers using the async
routines are responsible for their own synchronization and mutual
exclusion.
@@ -347,11 +350,6 @@ exclusion.
attempts an autosuspend if the new value is <= 0 and the
device isn't suspended.
- usb_autopm_set_interface() leaves pm_usage_count alone.
- It attempts an autoresume if the value is > 0 and the device
- is suspended, and it attempts an autosuspend if the value is
- <= 0 and the device isn't suspended.
-
usb_autopm_get_interface_async() and
usb_autopm_put_interface_async() do almost the same things as
their non-async counterparts. The differences are: they do
@@ -360,13 +358,11 @@ exclusion.
such as an URB's completion handler, but when they return the
device will not generally not yet be in the desired state.
-There also are a couple of utility routines drivers can use:
-
- usb_autopm_enable() sets pm_usage_cnt to 0 and then calls
- usb_autopm_set_interface(), which will attempt an autosuspend.
-
- usb_autopm_disable() sets pm_usage_cnt to 1 and then calls
- usb_autopm_set_interface(), which will attempt an autoresume.
+ usb_autopm_get_interface_no_resume() and
+ usb_autopm_put_interface_no_suspend() merely increment or
+ decrement the pm_usage_count value; they do not attempt to
+ carry out an autoresume or an autosuspend. Hence they can be
+ called in an atomic context.
The conventional usage pattern is that a driver calls
usb_autopm_get_interface() in its open routine and
@@ -400,11 +396,11 @@ though, setting this flag won't cause the kernel to autoresume it.
Normally a driver would set this flag in its probe method, at which
time the device is guaranteed not to be autosuspended.)
-The usb_autopm_* routines have to run in a sleepable process context;
-they must not be called from an interrupt handler or while holding a
-spinlock. In fact, the entire autosuspend mechanism is not well geared
-toward interrupt-driven operation. However there is one thing a
-driver can do in an interrupt handler:
+The synchronous usb_autopm_* routines have to run in a sleepable
+process context; they must not be called from an interrupt handler or
+while holding a spinlock. In fact, the entire autosuspend mechanism
+is not well geared toward interrupt-driven operation. However there
+is one thing a driver can do in an interrupt handler:
usb_mark_last_busy(struct usb_device *udev);
@@ -423,15 +419,16 @@ an URB had completed too recently.
External suspend calls should never be allowed to fail in this way,
only autosuspend calls. The driver can tell them apart by checking
-udev->auto_pm; this flag will be set to 1 for internal PM events
-(autosuspend or autoresume) and 0 for external PM events.
+the PM_EVENT_AUTO bit in the message.event argument to the suspend
+method; this bit will be set for internal PM events (autosuspend) and
+clear for external PM events.
Many of the ingredients in the autosuspend framework are oriented
towards interfaces: The usb_interface structure contains the
pm_usage_cnt field, and the usb_autopm_* routines take an interface
pointer as their argument. But somewhat confusingly, a few of the
-pieces (usb_mark_last_busy() and udev->auto_pm) use the usb_device
-structure instead. Drivers need to keep this straight; they can call
+pieces (i.e., usb_mark_last_busy()) use the usb_device structure
+instead. Drivers need to keep this straight; they can call
interface_to_usbdev() to find the device structure for a given
interface.
diff --git a/MAINTAINERS b/MAINTAINERS
index 98d5ca1..520a3b3 100644
--- a/MAINTAINERS
+++ b/MAINTAINERS
@@ -5680,9 +5680,11 @@ S: Maintained
F: drivers/net/wireless/rndis_wlan.c
USB XHCI DRIVER
-M: Sarah Sharp <sarah.a.sharp@intel.com>
+M: Sarah Sharp <sarah.a.sharp@linux.intel.com>
L: linux-usb@vger.kernel.org
S: Supported
+F: drivers/usb/host/xhci*
+F: drivers/usb/host/pci-quirks*
USB ZC0301 DRIVER
M: Luca Risolia <luca.risolia@studio.unibo.it>
diff --git a/arch/arm/plat-mxc/Makefile b/arch/arm/plat-mxc/Makefile
index 4cbca9d..996cbac 100644
--- a/arch/arm/plat-mxc/Makefile
+++ b/arch/arm/plat-mxc/Makefile
@@ -9,6 +9,7 @@ obj-$(CONFIG_ARCH_MX1) += iomux-mx1-mx2.o dma-mx1-mx2.o
obj-$(CONFIG_ARCH_MX2) += iomux-mx1-mx2.o dma-mx1-mx2.o
obj-$(CONFIG_ARCH_MXC_IOMUX_V3) += iomux-v3.o
obj-$(CONFIG_MXC_PWM) += pwm.o
+obj-$(CONFIG_USB_EHCI_MXC) += ehci.o
obj-$(CONFIG_MXC_ULPI) += ulpi.o
obj-$(CONFIG_ARCH_MXC_AUDMUX_V1) += audmux-v1.o
obj-$(CONFIG_ARCH_MXC_AUDMUX_V2) += audmux-v2.o
diff --git a/arch/arm/plat-mxc/ehci.c b/arch/arm/plat-mxc/ehci.c
new file mode 100644
index 0000000..41599be
--- /dev/null
+++ b/arch/arm/plat-mxc/ehci.c
@@ -0,0 +1,92 @@
+/*
+ * Copyright (c) 2009 Daniel Mack <daniel@caiaq.de>
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License as published by the
+ * Free Software Foundation; either version 2 of the License, or (at your
+ * option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
+ * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
+ * for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software Foundation,
+ * Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ */
+
+#include <linux/platform_device.h>
+#include <linux/io.h>
+
+#include <mach/hardware.h>
+#include <mach/mxc_ehci.h>
+
+#define USBCTRL_OTGBASE_OFFSET 0x600
+
+#define MX31_OTG_SIC_SHIFT 29
+#define MX31_OTG_SIC_MASK (0xf << MX31_OTG_SIC_SHIFT)
+#define MX31_OTG_PM_BIT (1 << 24)
+
+#define MX31_H2_SIC_SHIFT 21
+#define MX31_H2_SIC_MASK (0xf << MX31_H2_SIC_SHIFT)
+#define MX31_H2_PM_BIT (1 << 16)
+#define MX31_H2_DT_BIT (1 << 5)
+
+#define MX31_H1_SIC_SHIFT 13
+#define MX31_H1_SIC_MASK (0xf << MX31_H1_SIC_SHIFT)
+#define MX31_H1_PM_BIT (1 << 8)
+#define MX31_H1_DT_BIT (1 << 4)
+
+int mxc_set_usbcontrol(int port, unsigned int flags)
+{
+ unsigned int v;
+
+ if (cpu_is_mx31()) {
+ v = readl(IO_ADDRESS(MX31_OTG_BASE_ADDR +
+ USBCTRL_OTGBASE_OFFSET));
+
+ switch (port) {
+ case 0: /* OTG port */
+ v &= ~(MX31_OTG_SIC_MASK | MX31_OTG_PM_BIT);
+ v |= (flags & MXC_EHCI_INTERFACE_MASK)
+ << MX31_OTG_SIC_SHIFT;
+ if (flags & MXC_EHCI_POWER_PINS_ENABLED)
+ v |= MX31_OTG_PM_BIT;
+
+ break;
+ case 1: /* H1 port */
+ v &= ~(MX31_H1_SIC_MASK | MX31_H1_PM_BIT);
+ v |= (flags & MXC_EHCI_INTERFACE_MASK)
+ << MX31_H1_SIC_SHIFT;
+ if (flags & MXC_EHCI_POWER_PINS_ENABLED)
+ v |= MX31_H1_PM_BIT;
+
+ if (!(flags & MXC_EHCI_TTL_ENABLED))
+ v |= MX31_H1_DT_BIT;
+
+ break;
+ case 2: /* H2 port */
+ v &= ~(MX31_H2_SIC_MASK | MX31_H2_PM_BIT);
+ v |= (flags & MXC_EHCI_INTERFACE_MASK)
+ << MX31_H2_SIC_SHIFT;
+ if (!(flags & MXC_EHCI_POWER_PINS_ENABLED))
+ v |= MX31_H2_PM_BIT;
+
+ if (!(flags & MXC_EHCI_TTL_ENABLED))
+ v |= MX31_H2_DT_BIT;
+
+ break;
+ }
+
+ writel(v, IO_ADDRESS(MX31_OTG_BASE_ADDR +
+ USBCTRL_OTGBASE_OFFSET));
+ return 0;
+ }
+
+ printk(KERN_WARNING
+ "%s() unable to setup USBCONTROL for this CPU\n", __func__);
+ return -EINVAL;
+}
+EXPORT_SYMBOL(mxc_set_usbcontrol);
+
diff --git a/arch/arm/plat-mxc/include/mach/mxc_ehci.h b/arch/arm/plat-mxc/include/mach/mxc_ehci.h
new file mode 100644
index 0000000..8f79623
--- /dev/null
+++ b/arch/arm/plat-mxc/include/mach/mxc_ehci.h
@@ -0,0 +1,37 @@
+#ifndef __INCLUDE_ASM_ARCH_MXC_EHCI_H
+#define __INCLUDE_ASM_ARCH_MXC_EHCI_H
+
+/* values for portsc field */
+#define MXC_EHCI_PHY_LOW_POWER_SUSPEND (1 << 23)
+#define MXC_EHCI_FORCE_FS (1 << 24)
+#define MXC_EHCI_UTMI_8BIT (0 << 28)
+#define MXC_EHCI_UTMI_16BIT (1 << 28)
+#define MXC_EHCI_SERIAL (1 << 29)
+#define MXC_EHCI_MODE_UTMI (0 << 30)
+#define MXC_EHCI_MODE_PHILIPS (1 << 30)
+#define MXC_EHCI_MODE_ULPI (2 << 30)
+#define MXC_EHCI_MODE_SERIAL (3 << 30)
+
+/* values for flags field */
+#define MXC_EHCI_INTERFACE_DIFF_UNI (0 << 0)
+#define MXC_EHCI_INTERFACE_DIFF_BI (1 << 0)
+#define MXC_EHCI_INTERFACE_SINGLE_UNI (2 << 0)
+#define MXC_EHCI_INTERFACE_SINGLE_BI (3 << 0)
+#define MXC_EHCI_INTERFACE_MASK (0xf)
+
+#define MXC_EHCI_POWER_PINS_ENABLED (1 << 5)
+#define MXC_EHCI_TTL_ENABLED (1 << 6)
+
+struct mxc_usbh_platform_data {
+ int (*init)(struct platform_device *pdev);
+ int (*exit)(struct platform_device *pdev);
+
+ unsigned int portsc;
+ unsigned int flags;
+ struct otg_transceiver *otg;
+};
+
+int mxc_set_usbcontrol(int port, unsigned int flags);
+
+#endif /* __INCLUDE_ASM_ARCH_MXC_EHCI_H */
+
diff --git a/drivers/bluetooth/btusb.c b/drivers/bluetooth/btusb.c
index 44bc8bb..4d29059 100644
--- a/drivers/bluetooth/btusb.c
+++ b/drivers/bluetooth/btusb.c
@@ -1066,7 +1066,7 @@ static int btusb_suspend(struct usb_interface *intf, pm_message_t message)
return 0;
spin_lock_irq(&data->txlock);
- if (!(interface_to_usbdev(intf)->auto_pm && data->tx_in_flight)) {
+ if (!((message.event & PM_EVENT_AUTO) && data->tx_in_flight)) {
set_bit(BTUSB_SUSPENDING, &data->flags);
spin_unlock_irq(&data->txlock);
} else {
diff --git a/drivers/hid/usbhid/hid-core.c b/drivers/hid/usbhid/hid-core.c
index 0258289..e2997a8 100644
--- a/drivers/hid/usbhid/hid-core.c
+++ b/drivers/hid/usbhid/hid-core.c
@@ -1253,10 +1253,9 @@ static int hid_suspend(struct usb_interface *intf, pm_message_t message)
{
struct hid_device *hid = usb_get_intfdata(intf);
struct usbhid_device *usbhid = hid->driver_data;
- struct usb_device *udev = interface_to_usbdev(intf);
int status;
- if (udev->auto_pm) {
+ if (message.event & PM_EVENT_AUTO) {
spin_lock_irq(&usbhid->lock); /* Sync with error handler */
if (!test_bit(HID_RESET_PENDING, &usbhid->iofl)
&& !test_bit(HID_CLEAR_HALT, &usbhid->iofl)
@@ -1281,7 +1280,7 @@ static int hid_suspend(struct usb_interface *intf, pm_message_t message)
return -EIO;
}
- if (!ignoreled && udev->auto_pm) {
+ if (!ignoreled && (message.event & PM_EVENT_AUTO)) {
spin_lock_irq(&usbhid->lock);
if (test_bit(HID_LED_ON, &usbhid->iofl)) {
spin_unlock_irq(&usbhid->lock);
@@ -1294,7 +1293,8 @@ static int hid_suspend(struct usb_interface *intf, pm_message_t message)
hid_cancel_delayed_stuff(usbhid);
hid_cease_io(usbhid);
- if (udev->auto_pm && test_bit(HID_KEYS_PRESSED, &usbhid->iofl)) {
+ if ((message.event & PM_EVENT_AUTO) &&
+ test_bit(HID_KEYS_PRESSED, &usbhid->iofl)) {
/* lost race against keypresses */
status = hid_start_in(hid);
if (status < 0)
diff --git a/drivers/net/wimax/i2400m/usb.c b/drivers/net/wimax/i2400m/usb.c
index 47e84ef..3b48681 100644
--- a/drivers/net/wimax/i2400m/usb.c
+++ b/drivers/net/wimax/i2400m/usb.c
@@ -579,7 +579,7 @@ void i2400mu_disconnect(struct usb_interface *iface)
*
* As well, the device might refuse going to sleep for whichever
* reason. In this case we just fail. For system suspend/hibernate,
- * we *can't* fail. We look at usb_dev->auto_pm to see if the
+ * we *can't* fail. We check PM_EVENT_AUTO to see if the
* suspend call comes from the USB stack or from the system and act
* in consequence.
*
@@ -591,14 +591,11 @@ int i2400mu_suspend(struct usb_interface *iface, pm_message_t pm_msg)
int result = 0;
struct device *dev = &iface->dev;
struct i2400mu *i2400mu = usb_get_intfdata(iface);
-#ifdef CONFIG_PM
- struct usb_device *usb_dev = i2400mu->usb_dev;
-#endif
unsigned is_autosuspend = 0;
struct i2400m *i2400m = &i2400mu->i2400m;
#ifdef CONFIG_PM
- if (usb_dev->auto_pm > 0)
+ if (pm_msg.event & PM_EVENT_AUTO)
is_autosuspend = 1;
#endif
diff --git a/drivers/usb/Kconfig b/drivers/usb/Kconfig
index 2407508..81aac7f 100644
--- a/drivers/usb/Kconfig
+++ b/drivers/usb/Kconfig
@@ -60,6 +60,8 @@ config USB_ARCH_HAS_EHCI
default y if ARCH_IXP4XX
default y if ARCH_W90X900
default y if ARCH_AT91SAM9G45
+ default y if ARCH_MXC
+ default y if ARCH_OMAP34XX
default PCI
# ARM SA1111 chips have a non-PCI based "OHCI-compatible" USB host interface.
diff --git a/drivers/usb/Makefile b/drivers/usb/Makefile
index be3c9b8..473aa1a 100644
--- a/drivers/usb/Makefile
+++ b/drivers/usb/Makefile
@@ -44,3 +44,5 @@ obj-y += early/
obj-$(CONFIG_USB_ATM) += atm/
obj-$(CONFIG_USB_SPEEDTOUCH) += atm/
+
+obj-$(CONFIG_USB_ULPI) += otg/
diff --git a/drivers/usb/class/cdc-acm.c b/drivers/usb/class/cdc-acm.c
index e4eca78..34d4eb9 100644
--- a/drivers/usb/class/cdc-acm.c
+++ b/drivers/usb/class/cdc-acm.c
@@ -1461,6 +1461,12 @@ err_out:
}
#endif /* CONFIG_PM */
+
+#define NOKIA_PCSUITE_ACM_INFO(x) \
+ USB_DEVICE_AND_INTERFACE_INFO(0x0421, x, \
+ USB_CLASS_COMM, USB_CDC_SUBCLASS_ACM, \
+ USB_CDC_ACM_PROTO_VENDOR)
+
/*
* USB driver structure.
*/
@@ -1519,6 +1525,57 @@ static struct usb_device_id acm_ids[] = {
.driver_info = NO_UNION_NORMAL, /* reports zero length descriptor */
},
+ /* Nokia S60 phones expose two ACM channels. The first is
+ * a modem and is picked up by the standard AT-command
+ * information below. The second is 'vendor-specific' but
+ * is treated as a serial device at the S60 end, so we want
+ * to expose it on Linux too. */
+ { NOKIA_PCSUITE_ACM_INFO(0x042D), }, /* Nokia 3250 */
+ { NOKIA_PCSUITE_ACM_INFO(0x04D8), }, /* Nokia 5500 Sport */
+ { NOKIA_PCSUITE_ACM_INFO(0x04C9), }, /* Nokia E50 */
+ { NOKIA_PCSUITE_ACM_INFO(0x0419), }, /* Nokia E60 */
+ { NOKIA_PCSUITE_ACM_INFO(0x044D), }, /* Nokia E61 */
+ { NOKIA_PCSUITE_ACM_INFO(0x0001), }, /* Nokia E61i */
+ { NOKIA_PCSUITE_ACM_INFO(0x0475), }, /* Nokia E62 */
+ { NOKIA_PCSUITE_ACM_INFO(0x0508), }, /* Nokia E65 */
+ { NOKIA_PCSUITE_ACM_INFO(0x0418), }, /* Nokia E70 */
+ { NOKIA_PCSUITE_ACM_INFO(0x0425), }, /* Nokia N71 */
+ { NOKIA_PCSUITE_ACM_INFO(0x0486), }, /* Nokia N73 */
+ { NOKIA_PCSUITE_ACM_INFO(0x04DF), }, /* Nokia N75 */
+ { NOKIA_PCSUITE_ACM_INFO(0x000e), }, /* Nokia N77 */
+ { NOKIA_PCSUITE_ACM_INFO(0x0445), }, /* Nokia N80 */
+ { NOKIA_PCSUITE_ACM_INFO(0x042F), }, /* Nokia N91 & N91 8GB */
+ { NOKIA_PCSUITE_ACM_INFO(0x048E), }, /* Nokia N92 */
+ { NOKIA_PCSUITE_ACM_INFO(0x0420), }, /* Nokia N93 */
+ { NOKIA_PCSUITE_ACM_INFO(0x04E6), }, /* Nokia N93i */
+ { NOKIA_PCSUITE_ACM_INFO(0x04B2), }, /* Nokia 5700 XpressMusic */
+ { NOKIA_PCSUITE_ACM_INFO(0x0134), }, /* Nokia 6110 Navigator (China) */
+ { NOKIA_PCSUITE_ACM_INFO(0x046E), }, /* Nokia 6110 Navigator */
+ { NOKIA_PCSUITE_ACM_INFO(0x002f), }, /* Nokia 6120 classic & */
+ { NOKIA_PCSUITE_ACM_INFO(0x0088), }, /* Nokia 6121 classic */
+ { NOKIA_PCSUITE_ACM_INFO(0x00fc), }, /* Nokia 6124 classic */
+ { NOKIA_PCSUITE_ACM_INFO(0x0042), }, /* Nokia E51 */
+ { NOKIA_PCSUITE_ACM_INFO(0x00b0), }, /* Nokia E66 */
+ { NOKIA_PCSUITE_ACM_INFO(0x00ab), }, /* Nokia E71 */
+ { NOKIA_PCSUITE_ACM_INFO(0x0481), }, /* Nokia N76 */
+ { NOKIA_PCSUITE_ACM_INFO(0x0007), }, /* Nokia N81 & N81 8GB */
+ { NOKIA_PCSUITE_ACM_INFO(0x0071), }, /* Nokia N82 */
+ { NOKIA_PCSUITE_ACM_INFO(0x04F0), }, /* Nokia N95 & N95-3 NAM */
+ { NOKIA_PCSUITE_ACM_INFO(0x0070), }, /* Nokia N95 8GB */
+ { NOKIA_PCSUITE_ACM_INFO(0x00e9), }, /* Nokia 5320 XpressMusic */
+ { NOKIA_PCSUITE_ACM_INFO(0x0099), }, /* Nokia 6210 Navigator, RM-367 */
+ { NOKIA_PCSUITE_ACM_INFO(0x0128), }, /* Nokia 6210 Navigator, RM-419 */
+ { NOKIA_PCSUITE_ACM_INFO(0x008f), }, /* Nokia 6220 Classic */
+ { NOKIA_PCSUITE_ACM_INFO(0x00a0), }, /* Nokia 6650 */
+ { NOKIA_PCSUITE_ACM_INFO(0x007b), }, /* Nokia N78 */
+ { NOKIA_PCSUITE_ACM_INFO(0x0094), }, /* Nokia N85 */
+ { NOKIA_PCSUITE_ACM_INFO(0x003a), }, /* Nokia N96 & N96-3 */
+ { NOKIA_PCSUITE_ACM_INFO(0x00e9), }, /* Nokia 5320 XpressMusic */
+ { NOKIA_PCSUITE_ACM_INFO(0x0108), }, /* Nokia 5320 XpressMusic 2G */
+ { NOKIA_PCSUITE_ACM_INFO(0x01f5), }, /* Nokia N97, RM-505 */
+
+ /* NOTE: non-Nokia COMM/ACM/0xff is likely MSFT RNDIS... NOT a modem! */
+
/* control interfaces with various AT-command sets */
{ USB_INTERFACE_INFO(USB_CLASS_COMM, USB_CDC_SUBCLASS_ACM,
USB_CDC_ACM_PROTO_AT_V25TER) },
@@ -1533,7 +1590,6 @@ static struct usb_device_id acm_ids[] = {
{ USB_INTERFACE_INFO(USB_CLASS_COMM, USB_CDC_SUBCLASS_ACM,
USB_CDC_ACM_PROTO_AT_CDMA) },
- /* NOTE: COMM/ACM/0xff is likely MSFT RNDIS ... NOT a modem!! */
{ }
};
diff --git a/drivers/usb/class/usbtmc.c b/drivers/usb/class/usbtmc.c
index b4bd241..7c5f4e3 100644
--- a/drivers/usb/class/usbtmc.c
+++ b/drivers/usb/class/usbtmc.c
@@ -347,13 +347,8 @@ usbtmc_abort_bulk_out_check_status:
goto exit;
usbtmc_abort_bulk_out_clear_halt:
- rv = usb_control_msg(data->usb_dev,
- usb_sndctrlpipe(data->usb_dev, 0),
- USB_REQ_CLEAR_FEATURE,
- USB_DIR_OUT | USB_TYPE_STANDARD |
- USB_RECIP_ENDPOINT,
- USB_ENDPOINT_HALT, data->bulk_out, buffer,
- 0, USBTMC_TIMEOUT);
+ rv = usb_clear_halt(data->usb_dev,
+ usb_sndbulkpipe(data->usb_dev, data->bulk_out));
if (rv < 0) {
dev_err(dev, "usb_control_msg returned %d\n", rv);
@@ -562,10 +557,16 @@ static ssize_t usbtmc_write(struct file *filp, const char __user *buf,
n_bytes = roundup(12 + this_part, 4);
memset(buffer + 12 + this_part, 0, n_bytes - (12 + this_part));
- retval = usb_bulk_msg(data->usb_dev,
- usb_sndbulkpipe(data->usb_dev,
- data->bulk_out),
- buffer, n_bytes, &actual, USBTMC_TIMEOUT);
+ do {
+ retval = usb_bulk_msg(data->usb_dev,
+ usb_sndbulkpipe(data->usb_dev,
+ data->bulk_out),
+ buffer, n_bytes,
+ &actual, USBTMC_TIMEOUT);
+ if (retval != 0)
+ break;
+ n_bytes -= actual;
+ } while (n_bytes);
data->bTag_last_write = data->bTag;
data->bTag++;
@@ -702,14 +703,8 @@ usbtmc_clear_check_status:
usbtmc_clear_bulk_out_halt:
- rv = usb_control_msg(data->usb_dev,
- usb_sndctrlpipe(data->usb_dev, 0),
- USB_REQ_CLEAR_FEATURE,
- USB_DIR_OUT | USB_TYPE_STANDARD |
- USB_RECIP_ENDPOINT,
- USB_ENDPOINT_HALT,
- data->bulk_out, buffer, 0,
- USBTMC_TIMEOUT);
+ rv = usb_clear_halt(data->usb_dev,
+ usb_sndbulkpipe(data->usb_dev, data->bulk_out));
if (rv < 0) {
dev_err(dev, "usb_control_msg returned %d\n", rv);
goto exit;
@@ -730,13 +725,8 @@ static int usbtmc_ioctl_clear_out_halt(struct usbtmc_device_data *data)
if (!buffer)
return -ENOMEM;
- rv = usb_control_msg(data->usb_dev,
- usb_sndctrlpipe(data->usb_dev, 0),
- USB_REQ_CLEAR_FEATURE,
- USB_DIR_OUT | USB_TYPE_STANDARD |
- USB_RECIP_ENDPOINT,
- USB_ENDPOINT_HALT, data->bulk_out,
- buffer, 0, USBTMC_TIMEOUT);
+ rv = usb_clear_halt(data->usb_dev,
+ usb_sndbulkpipe(data->usb_dev, data->bulk_out));
if (rv < 0) {
dev_err(&data->usb_dev->dev, "usb_control_msg returned %d\n",
@@ -759,12 +749,8 @@ static int usbtmc_ioctl_clear_in_halt(struct usbtmc_device_data *data)
if (!buffer)
return -ENOMEM;
- rv = usb_control_msg(data->usb_dev, usb_sndctrlpipe(data->usb_dev, 0),
- USB_REQ_CLEAR_FEATURE,
- USB_DIR_OUT | USB_TYPE_STANDARD |
- USB_RECIP_ENDPOINT,
- USB_ENDPOINT_HALT, data->bulk_in, buffer, 0,
- USBTMC_TIMEOUT);
+ rv = usb_clear_halt(data->usb_dev,
+ usb_rcvbulkpipe(data->usb_dev, data->bulk_in));
if (rv < 0) {
dev_err(&data->usb_dev->dev, "usb_control_msg returned %d\n",
@@ -1109,13 +1095,13 @@ static void usbtmc_disconnect(struct usb_interface *intf)
kref_put(&data->kref, usbtmc_delete);
}
-static int usbtmc_suspend (struct usb_interface *intf, pm_message_t message)
+static int usbtmc_suspend(struct usb_interface *intf, pm_message_t message)
{
/* this driver does not have pending URBs */
return 0;
}
-static int usbtmc_resume (struct usb_interface *intf)
+static int usbtmc_resume(struct usb_interface *intf)
{
return 0;
}
diff --git a/drivers/usb/core/driver.c b/drivers/usb/core/driver.c
index 4f86447..60a45f1 100644
--- a/drivers/usb/core/driver.c
+++ b/drivers/usb/core/driver.c
@@ -83,6 +83,47 @@ static ssize_t store_new_id(struct device_driver *driver,
}
static DRIVER_ATTR(new_id, S_IWUSR, NULL, store_new_id);
+/**
+ * store_remove_id - remove a USB device ID from this driver
+ * @driver: target device driver
+ * @buf: buffer for scanning device ID data
+ * @count: input size
+ *
+ * Removes a dynamic usb device ID from this driver.
+ */
+static ssize_t
+store_remove_id(struct device_driver *driver, const char *buf, size_t count)
+{
+ struct usb_dynid *dynid, *n;
+ struct usb_driver *usb_driver = to_usb_driver(driver);
+ u32 idVendor = 0;
+ u32 idProduct = 0;
+ int fields = 0;
+ int retval = 0;
+
+ fields = sscanf(buf, "%x %x", &idVendor, &idProduct);
+ if (fields < 2)
+ return -EINVAL;
+
+ spin_lock(&usb_driver->dynids.lock);
+ list_for_each_entry_safe(dynid, n, &usb_driver->dynids.list, node) {
+ struct usb_device_id *id = &dynid->id;
+ if ((id->idVendor == idVendor) &&
+ (id->idProduct == idProduct)) {
+ list_del(&dynid->node);
+ kfree(dynid);
+ retval = 0;
+ break;
+ }
+ }
+ spin_unlock(&usb_driver->dynids.lock);
+
+ if (retval)
+ return retval;
+ return count;
+}
+static DRIVER_ATTR(remove_id, S_IWUSR, NULL, store_remove_id);
+
static int usb_create_newid_file(struct usb_driver *usb_drv)
{
int error = 0;
@@ -107,6 +148,21 @@ static void usb_remove_newid_file(struct usb_driver *usb_drv)
&driver_attr_new_id);
}
+static int
+usb_create_removeid_file(struct usb_driver *drv)
+{
+ int error = 0;
+ if (drv->probe != NULL)
+ error = driver_create_file(&drv->drvwrap.driver,
+ &driver_attr_remove_id);
+ return error;
+}
+
+static void usb_remove_removeid_file(struct usb_driver *drv)
+{
+ driver_remove_file(&drv->drvwrap.driver, &driver_attr_remove_id);
+}
+
static void usb_free_dynids(struct usb_driver *usb_drv)
{
struct usb_dynid *dynid, *n;
@@ -128,6 +184,16 @@ static void usb_remove_newid_file(struct usb_driver *usb_drv)
{
}
+static int
+usb_create_removeid_file(struct usb_driver *drv)
+{
+ return 0;
+}
+
+static void usb_remove_removeid_file(struct usb_driver *drv)
+{
+}
+
static inline void usb_free_dynids(struct usb_driver *usb_drv)
{
}
@@ -774,19 +840,34 @@ int usb_register_driver(struct usb_driver *new_driver, struct module *owner,
INIT_LIST_HEAD(&new_driver->dynids.list);
retval = driver_register(&new_driver->drvwrap.driver);
+ if (retval)
+ goto out;
- if (!retval) {
- pr_info("%s: registered new interface driver %s\n",
+ usbfs_update_special();
+
+ retval = usb_create_newid_file(new_driver);
+ if (retval)
+ goto out_newid;
+
+ retval = usb_create_removeid_file(new_driver);
+ if (retval)
+ goto out_removeid;
+
+ pr_info("%s: registered new interface driver %s\n",
usbcore_name, new_driver->name);
- usbfs_update_special();
- usb_create_newid_file(new_driver);
- } else {
- printk(KERN_ERR "%s: error %d registering interface "
- " driver %s\n",
- usbcore_name, retval, new_driver->name);
- }
+out:
return retval;
+
+out_removeid:
+ usb_remove_newid_file(new_driver);
+out_newid:
+ driver_unregister(&new_driver->drvwrap.driver);
+
+ printk(KERN_ERR "%s: error %d registering interface "
+ " driver %s\n",
+ usbcore_name, retval, new_driver->name);
+ goto out;
}
EXPORT_SYMBOL_GPL(usb_register_driver);
@@ -806,6 +887,7 @@ void usb_deregister(struct usb_driver *driver)
pr_info("%s: deregistering interface driver %s\n",
usbcore_name, driver->name);
+ usb_remove_removeid_file(driver);
usb_remove_newid_file(driver);
usb_free_dynids(driver);
driver_unregister(&driver->drvwrap.driver);
@@ -948,8 +1030,6 @@ static int usb_resume_device(struct usb_device *udev, pm_message_t msg)
done:
dev_vdbg(&udev->dev, "%s: status %d\n", __func__, status);
- if (status == 0)
- udev->autoresume_disabled = 0;
return status;
}
@@ -1280,11 +1360,6 @@ static int usb_resume_both(struct usb_device *udev, pm_message_t msg)
/* Propagate the resume up the tree, if necessary */
if (udev->state == USB_STATE_SUSPENDED) {
- if ((msg.event & PM_EVENT_AUTO) &&
- udev->autoresume_disabled) {
- status = -EPERM;
- goto done;
- }
if (parent) {
status = usb_autoresume_device(parent);
if (status == 0) {
@@ -1341,7 +1416,6 @@ static int usb_autopm_do_device(struct usb_device *udev, int inc_usage_cnt)
int status = 0;
usb_pm_lock(udev);
- udev->auto_pm = 1;
udev->pm_usage_cnt += inc_usage_cnt;
WARN_ON(udev->pm_usage_cnt < 0);
if (inc_usage_cnt)
@@ -1473,7 +1547,6 @@ static int usb_autopm_do_interface(struct usb_interface *intf,
if (intf->condition == USB_INTERFACE_UNBOUND)
status = -ENODEV;
else {
- udev->auto_pm = 1;
atomic_add(inc_usage_cnt, &intf->pm_usage_cnt);
udev->last_busy = jiffies;
if (inc_usage_cnt >= 0 &&
@@ -1640,8 +1713,6 @@ int usb_autopm_get_interface_async(struct usb_interface *intf)
if (intf->condition == USB_INTERFACE_UNBOUND)
status = -ENODEV;
- else if (udev->autoresume_disabled)
- status = -EPERM;
else {
atomic_inc(&intf->pm_usage_cnt);
if (atomic_read(&intf->pm_usage_cnt) > 0 &&
@@ -1654,28 +1725,6 @@ int usb_autopm_get_interface_async(struct usb_interface *intf)
}
EXPORT_SYMBOL_GPL(usb_autopm_get_interface_async);
-/**
- * usb_autopm_set_interface - set a USB interface's autosuspend state
- * @intf: the usb_interface whose state should be set
- *
- * This routine sets the autosuspend state of @intf's device according
- * to @intf's usage counter, which the caller must have set previously.
- * If the counter is <= 0, the device is autosuspended (if it isn't
- * already suspended and if nothing else prevents the autosuspend). If
- * the counter is > 0, the device is autoresumed (if it isn't already
- * awake).
- */
-int usb_autopm_set_interface(struct usb_interface *intf)
-{
- int status;
-
- status = usb_autopm_do_interface(intf, 0);
- dev_vdbg(&intf->dev, "%s: status %d cnt %d\n",
- __func__, status, atomic_read(&intf->pm_usage_cnt));
- return status;
-}
-EXPORT_SYMBOL_GPL(usb_autopm_set_interface);
-
#else
void usb_autosuspend_work(struct work_struct *work)
@@ -1707,7 +1756,6 @@ int usb_external_suspend_device(struct usb_device *udev, pm_message_t msg)
do_unbind_rebind(udev, DO_UNBIND);
usb_pm_lock(udev);
- udev->auto_pm = 0;
status = usb_suspend_both(udev, msg);
usb_pm_unlock(udev);
return status;
@@ -1730,7 +1778,6 @@ int usb_external_resume_device(struct usb_device *udev, pm_message_t msg)
int status;
usb_pm_lock(udev);
- udev->auto_pm = 0;
status = usb_resume_both(udev, msg);
udev->last_busy = jiffies;
usb_pm_unlock(udev);
diff --git a/drivers/usb/core/file.c b/drivers/usb/core/file.c
index 222ee07..bfc6c2e 100644
--- a/drivers/usb/core/file.c
+++ b/drivers/usb/core/file.c
@@ -99,6 +99,7 @@ static int init_usb_class(void)
printk(KERN_ERR "class_create failed for usb devices\n");
kfree(usb_class);
usb_class = NULL;
+ goto exit;
}
usb_class->class->devnode = usb_devnode;
diff --git a/drivers/usb/core/generic.c b/drivers/usb/core/generic.c
index 05e6d31..bdf87a8 100644
--- a/drivers/usb/core/generic.c
+++ b/drivers/usb/core/generic.c
@@ -139,7 +139,7 @@ int usb_choose_configuration(struct usb_device *udev)
if (best) {
i = best->desc.bConfigurationValue;
- dev_info(&udev->dev,
+ dev_dbg(&udev->dev,
"configuration #%d chosen from %d choice%s\n",
i, num_configs, plural(num_configs));
} else {
diff --git a/drivers/usb/core/hcd.c b/drivers/usb/core/hcd.c
index 34de475..6dac3b8 100644
--- a/drivers/usb/core/hcd.c
+++ b/drivers/usb/core/hcd.c
@@ -38,6 +38,7 @@
#include <asm/unaligned.h>
#include <linux/platform_device.h>
#include <linux/workqueue.h>
+#include <linux/mutex.h>
#include <linux/usb.h>
@@ -1275,13 +1276,16 @@ static int map_urb_for_dma(struct usb_hcd *hcd, struct urb *urb,
if (usb_endpoint_xfer_control(&urb->ep->desc)
&& !(urb->transfer_flags & URB_NO_SETUP_DMA_MAP)) {
- if (hcd->self.uses_dma)
+ if (hcd->self.uses_dma) {
urb->setup_dma = dma_map_single(
hcd->self.controller,
urb->setup_packet,
sizeof(struct usb_ctrlrequest),
DMA_TO_DEVICE);
- else if (hcd->driver->flags & HCD_LOCAL_MEM)
+ if (dma_mapping_error(hcd->self.controller,
+ urb->setup_dma))
+ return -EAGAIN;
+ } else if (hcd->driver->flags & HCD_LOCAL_MEM)
ret = hcd_alloc_coherent(
urb->dev->bus, mem_flags,
&urb->setup_dma,
@@ -1293,13 +1297,16 @@ static int map_urb_for_dma(struct usb_hcd *hcd, struct urb *urb,
dir = usb_urb_dir_in(urb) ? DMA_FROM_DEVICE : DMA_TO_DEVICE;
if (ret == 0 && urb->transfer_buffer_length != 0
&& !(urb->transfer_flags & URB_NO_TRANSFER_DMA_MAP)) {
- if (hcd->self.uses_dma)
+ if (hcd->self.uses_dma) {
urb->transfer_dma = dma_map_single (
hcd->self.controller,
urb->transfer_buffer,
urb->transfer_buffer_length,
dir);
- else if (hcd->driver->flags & HCD_LOCAL_MEM) {
+ if (dma_mapping_error(hcd->self.controller,
+ urb->transfer_dma))
+ return -EAGAIN;
+ } else if (hcd->driver->flags & HCD_LOCAL_MEM) {
ret = hcd_alloc_coherent(
urb->dev->bus, mem_flags,
&urb->transfer_dma,
@@ -1589,19 +1596,32 @@ rescan:
}
}
-/* Check whether a new configuration or alt setting for an interface
- * will exceed the bandwidth for the bus (or the host controller resources).
- * Only pass in a non-NULL config or interface, not both!
- * Passing NULL for both new_config and new_intf means the device will be
- * de-configured by issuing a set configuration 0 command.
+/**
+ * Check whether a new bandwidth setting exceeds the bus bandwidth.
+ * @new_config: new configuration to install
+ * @cur_alt: the current alternate interface setting
+ * @new_alt: alternate interface setting that is being installed
+ *
+ * To change configurations, pass in the new configuration in new_config,
+ * and pass NULL for cur_alt and new_alt.
+ *
+ * To reset a device's configuration (put the device in the ADDRESSED state),
+ * pass in NULL for new_config, cur_alt, and new_alt.
+ *
+ * To change alternate interface settings, pass in NULL for new_config,
+ * pass in the current alternate interface setting in cur_alt,
+ * and pass in the new alternate interface setting in new_alt.
+ *
+ * Returns an error if the requested bandwidth change exceeds the
+ * bus bandwidth or host controller internal resources.
*/
-int usb_hcd_check_bandwidth(struct usb_device *udev,
+int usb_hcd_alloc_bandwidth(struct usb_device *udev,
struct usb_host_config *new_config,
- struct usb_interface *new_intf)
+ struct usb_host_interface *cur_alt,
+ struct usb_host_interface *new_alt)
{
int num_intfs, i, j;
- struct usb_interface_cache *intf_cache;
- struct usb_host_interface *alt = 0;
+ struct usb_host_interface *alt = NULL;
int ret = 0;
struct usb_hcd *hcd;
struct usb_host_endpoint *ep;
@@ -1611,7 +1631,7 @@ int usb_hcd_check_bandwidth(struct usb_device *udev,
return 0;
/* Configuration is being removed - set configuration 0 */
- if (!new_config && !new_intf) {
+ if (!new_config && !cur_alt) {
for (i = 1; i < 16; ++i) {
ep = udev->ep_out[i];
if (ep)
@@ -1648,19 +1668,12 @@ int usb_hcd_check_bandwidth(struct usb_device *udev,
}
}
for (i = 0; i < num_intfs; ++i) {
+ /* Set up endpoints for alternate interface setting 0 */
+ alt = usb_find_alt_setting(new_config, i, 0);
+ if (!alt)
+ /* No alt setting 0? Pick the first setting. */
+ alt = &new_config->intf_cache[i]->altsetting[0];
- /* Dig the endpoints for alt setting 0 out of the
- * interface cache for this interface
- */
- intf_cache = new_config->intf_cache[i];
- for (j = 0; j < intf_cache->num_altsetting; j++) {
- if (intf_cache->altsetting[j].desc.bAlternateSetting == 0)
- alt = &intf_cache->altsetting[j];
- }
- if (!alt) {
- printk(KERN_DEBUG "Did not find alt setting 0 for intf %d\n", i);
- continue;
- }
for (j = 0; j < alt->desc.bNumEndpoints; j++) {
ret = hcd->driver->add_endpoint(hcd, udev, &alt->endpoint[j]);
if (ret < 0)
@@ -1668,6 +1681,22 @@ int usb_hcd_check_bandwidth(struct usb_device *udev,
}
}
}
+ if (cur_alt && new_alt) {
+ /* Drop all the endpoints in the current alt setting */
+ for (i = 0; i < cur_alt->desc.bNumEndpoints; i++) {
+ ret = hcd->driver->drop_endpoint(hcd, udev,
+ &cur_alt->endpoint[i]);
+ if (ret < 0)
+ goto reset;
+ }
+ /* Add all the endpoints in the new alt setting */
+ for (i = 0; i < new_alt->desc.bNumEndpoints; i++) {
+ ret = hcd->driver->add_endpoint(hcd, udev,
+ &new_alt->endpoint[i]);
+ if (ret < 0)
+ goto reset;
+ }
+ }
ret = hcd->driver->check_bandwidth(hcd, udev);
reset:
if (ret < 0)
@@ -1984,6 +2013,7 @@ struct usb_hcd *usb_create_hcd (const struct hc_driver *driver,
#ifdef CONFIG_PM
INIT_WORK(&hcd->wakeup_work, hcd_resume_work);
#endif
+ mutex_init(&hcd->bandwidth_mutex);
hcd->driver = driver;
hcd->product_desc = (driver->product_desc) ? driver->product_desc :
diff --git a/drivers/usb/core/hcd.h b/drivers/usb/core/hcd.h
index 79782a1..d8b43ae 100644
--- a/drivers/usb/core/hcd.h
+++ b/drivers/usb/core/hcd.h
@@ -111,6 +111,20 @@ struct usb_hcd {
u64 rsrc_len; /* memory/io resource length */
unsigned power_budget; /* in mA, 0 = no limit */
+ /* bandwidth_mutex should be taken before adding or removing
+ * any new bus bandwidth constraints:
+ * 1. Before adding a configuration for a new device.
+ * 2. Before removing the configuration to put the device into
+ * the addressed state.
+ * 3. Before selecting a different configuration.
+ * 4. Before selecting an alternate interface setting.
+ *
+ * bandwidth_mutex should be dropped after a successful control message
+ * to the device, or resetting the bandwidth after a failed attempt.
+ */
+ struct mutex bandwidth_mutex;
+
+
#define HCD_BUFFER_POOLS 4
struct dma_pool *pool [HCD_BUFFER_POOLS];
@@ -290,9 +304,10 @@ extern void usb_hcd_disable_endpoint(struct usb_device *udev,
extern void usb_hcd_reset_endpoint(struct usb_device *udev,
struct usb_host_endpoint *ep);
extern void usb_hcd_synchronize_unlinks(struct usb_device *udev);
-extern int usb_hcd_check_bandwidth(struct usb_device *udev,
+extern int usb_hcd_alloc_bandwidth(struct usb_device *udev,
struct usb_host_config *new_config,
- struct usb_interface *new_intf);
+ struct usb_host_interface *old_alt,
+ struct usb_host_interface *new_alt);
extern int usb_hcd_get_frame_number(struct usb_device *udev);
extern struct usb_hcd *usb_create_hcd(const struct hc_driver *driver,
diff --git a/drivers/usb/core/hub.c b/drivers/usb/core/hub.c
index 0f857e6..06af970 100644
--- a/drivers/usb/core/hub.c
+++ b/drivers/usb/core/hub.c
@@ -45,7 +45,6 @@ struct usb_hub {
/* buffer for urb ... with extra space in case of babble */
char (*buffer)[8];
- dma_addr_t buffer_dma; /* DMA address for buffer */
union {
struct usb_hub_status hub;
struct usb_port_status port;
@@ -61,6 +60,8 @@ struct usb_hub {
status change */
unsigned long busy_bits[1]; /* ports being reset or
resumed */
+ unsigned long removed_bits[1]; /* ports with a "removed"
+ device present */
#if USB_MAXCHILDREN > 31 /* 8*sizeof(unsigned long) - 1 */
#error event_bits[] is too short!
#endif
@@ -70,6 +71,7 @@ struct usb_hub {
unsigned mA_per_port; /* current for each child */
+ unsigned init_done:1;
unsigned limited_power:1;
unsigned quiescing:1;
unsigned disconnected:1;
@@ -374,12 +376,13 @@ static void kick_khubd(struct usb_hub *hub)
{
unsigned long flags;
- /* Suppress autosuspend until khubd runs */
- atomic_set(&to_usb_interface(hub->intfdev)->pm_usage_cnt, 1);
-
spin_lock_irqsave(&hub_event_lock, flags);
if (!hub->disconnected && list_empty(&hub->event_list)) {
list_add_tail(&hub->event_list, &hub_event_list);
+
+ /* Suppress autosuspend until khubd runs */
+ usb_autopm_get_interface_no_resume(
+ to_usb_interface(hub->intfdev));
wake_up(&khubd_wait);
}
spin_unlock_irqrestore(&hub_event_lock, flags);
@@ -636,8 +639,35 @@ static void hub_port_logical_disconnect(struct usb_hub *hub, int port1)
kick_khubd(hub);
}
+/**
+ * usb_remove_device - disable a device's port on its parent hub
+ * @udev: device to be disabled and removed
+ * Context: @udev locked, must be able to sleep.
+ *
+ * After @udev's port has been disabled, khubd is notified and it will
+ * see that the device has been disconnected. When the device is
+ * physically unplugged and something is plugged in, the events will
+ * be received and processed normally.
+ */
+int usb_remove_device(struct usb_device *udev)
+{
+ struct usb_hub *hub;
+ struct usb_interface *intf;
+
+ if (!udev->parent) /* Can't remove a root hub */
+ return -EINVAL;
+ hub = hdev_to_hub(udev->parent);
+ intf = to_usb_interface(hub->intfdev);
+
+ usb_autopm_get_interface(intf);
+ set_bit(udev->portnum, hub->removed_bits);
+ hub_port_logical_disconnect(hub, udev->portnum);
+ usb_autopm_put_interface(intf);
+ return 0;
+}
+
enum hub_activation_type {
- HUB_INIT, HUB_INIT2, HUB_INIT3,
+ HUB_INIT, HUB_INIT2, HUB_INIT3, /* INITs must come first */
HUB_POST_RESET, HUB_RESUME, HUB_RESET_RESUME,
};
@@ -682,8 +712,8 @@ static void hub_activate(struct usb_hub *hub, enum hub_activation_type type)
msecs_to_jiffies(delay));
/* Suppress autosuspend until init is done */
- atomic_set(&to_usb_interface(hub->intfdev)->
- pm_usage_cnt, 1);
+ usb_autopm_get_interface_no_resume(
+ to_usb_interface(hub->intfdev));
return; /* Continues at init2: below */
} else {
hub_power_on(hub, true);
@@ -731,6 +761,13 @@ static void hub_activate(struct usb_hub *hub, enum hub_activation_type type)
USB_PORT_FEAT_C_ENABLE);
}
+ /* We can forget about a "removed" device when there's a
+ * physical disconnect or the connect status changes.
+ */
+ if (!(portstatus & USB_PORT_STAT_CONNECTION) ||
+ (portchange & USB_PORT_STAT_C_CONNECTION))
+ clear_bit(port1, hub->removed_bits);
+
if (!udev || udev->state == USB_STATE_NOTATTACHED) {
/* Tell khubd to disconnect the device or
* check for a new connection
@@ -783,6 +820,7 @@ static void hub_activate(struct usb_hub *hub, enum hub_activation_type type)
}
init3:
hub->quiescing = 0;
+ hub->init_done = 1;
status = usb_submit_urb(hub->urb, GFP_NOIO);
if (status < 0)
@@ -792,6 +830,10 @@ static void hub_activate(struct usb_hub *hub, enum hub_activation_type type)
/* Scan all ports that need attention */
kick_khubd(hub);
+
+ /* Allow autosuspend if it was suppressed */
+ if (type <= HUB_INIT3)
+ usb_autopm_put_interface_async(to_usb_interface(hub->intfdev));
}
/* Implement the continuations for the delays above */
@@ -819,6 +861,11 @@ static void hub_quiesce(struct usb_hub *hub, enum hub_quiescing_type type)
int i;
cancel_delayed_work_sync(&hub->init_work);
+ if (!hub->init_done) {
+ hub->init_done = 1;
+ usb_autopm_put_interface_no_suspend(
+ to_usb_interface(hub->intfdev));
+ }
/* khubd and related activity won't re-trigger */
hub->quiescing = 1;
@@ -869,8 +916,7 @@ static int hub_configure(struct usb_hub *hub,
int maxp, ret;
char *message = "out of memory";
- hub->buffer = usb_buffer_alloc(hdev, sizeof(*hub->buffer), GFP_KERNEL,
- &hub->buffer_dma);
+ hub->buffer = kmalloc(sizeof(*hub->buffer), GFP_KERNEL);
if (!hub->buffer) {
ret = -ENOMEM;
goto fail;
@@ -1111,8 +1157,6 @@ static int hub_configure(struct usb_hub *hub,
usb_fill_int_urb(hub->urb, hdev, pipe, *hub->buffer, maxp, hub_irq,
hub, endpoint->bInterval);
- hub->urb->transfer_dma = hub->buffer_dma;
- hub->urb->transfer_flags |= URB_NO_TRANSFER_DMA_MAP;
/* maybe cycle the hub leds */
if (hub->has_indicators && blinkenlights)
@@ -1144,7 +1188,10 @@ static void hub_disconnect(struct usb_interface *intf)
/* Take the hub off the event list and don't let it be added again */
spin_lock_irq(&hub_event_lock);
- list_del_init(&hub->event_list);
+ if (!list_empty(&hub->event_list)) {
+ list_del_init(&hub->event_list);
+ usb_autopm_put_interface_no_suspend(intf);
+ }
hub->disconnected = 1;
spin_unlock_irq(&hub_event_lock);
@@ -1162,8 +1209,7 @@ static void hub_disconnect(struct usb_interface *intf)
kfree(hub->port_owners);
kfree(hub->descriptor);
kfree(hub->status);
- usb_buffer_free(hub->hdev, sizeof(*hub->buffer), hub->buffer,
- hub->buffer_dma);
+ kfree(hub->buffer);
kref_put(&hub->kref, hub_release);
}
@@ -1630,7 +1676,7 @@ static int usb_configure_device_otg(struct usb_device *udev)
if (!udev->bus->is_b_host
&& udev->config
&& udev->parent == udev->bus->root_hub) {
- struct usb_otg_descriptor *desc = 0;
+ struct usb_otg_descriptor *desc = NULL;
struct usb_bus *bus = udev->bus;
/* descriptor may appear anywhere in config */
@@ -2123,9 +2169,13 @@ int usb_port_suspend(struct usb_device *udev, pm_message_t msg)
USB_DEVICE_REMOTE_WAKEUP, 0,
NULL, 0,
USB_CTRL_SET_TIMEOUT);
- if (status)
+ if (status) {
dev_dbg(&udev->dev, "won't remote wakeup, status %d\n",
status);
+ /* bail if autosuspend is requested */
+ if (msg.event & PM_EVENT_AUTO)
+ return status;
+ }
}
/* see 7.1.7.6 */
@@ -2134,7 +2184,8 @@ int usb_port_suspend(struct usb_device *udev, pm_message_t msg)
dev_dbg(hub->intfdev, "can't suspend port %d, status %d\n",
port1, status);
/* paranoia: "should not happen" */
- (void) usb_control_msg(udev, usb_sndctrlpipe(udev, 0),
+ if (udev->do_remote_wakeup)
+ (void) usb_control_msg(udev, usb_sndctrlpipe(udev, 0),
USB_REQ_CLEAR_FEATURE, USB_RECIP_DEVICE,
USB_DEVICE_REMOTE_WAKEUP, 0,
NULL, 0,
@@ -2965,6 +3016,13 @@ static void hub_port_connect_change(struct usb_hub *hub, int port1,
usb_disconnect(&hdev->children[port1-1]);
clear_bit(port1, hub->change_bits);
+ /* We can forget about a "removed" device when there's a physical
+ * disconnect or the connect status changes.
+ */
+ if (!(portstatus & USB_PORT_STAT_CONNECTION) ||
+ (portchange & USB_PORT_STAT_C_CONNECTION))
+ clear_bit(port1, hub->removed_bits);
+
if (portchange & (USB_PORT_STAT_C_CONNECTION |
USB_PORT_STAT_C_ENABLE)) {
status = hub_port_debounce(hub, port1);
@@ -2978,8 +3036,11 @@ static void hub_port_connect_change(struct usb_hub *hub, int port1,
}
}
- /* Return now if debouncing failed or nothing is connected */
- if (!(portstatus & USB_PORT_STAT_CONNECTION)) {
+ /* Return now if debouncing failed or nothing is connected or
+ * the device was "removed".
+ */
+ if (!(portstatus & USB_PORT_STAT_CONNECTION) ||
+ test_bit(port1, hub->removed_bits)) {
/* maybe switch power back on (e.g. root hub was reset) */
if ((wHubCharacteristics & HUB_CHAR_LPSM) < 2
@@ -3189,7 +3250,7 @@ static void hub_events(void)
* disconnected while waiting for the lock to succeed. */
usb_lock_device(hdev);
if (unlikely(hub->disconnected))
- goto loop;
+ goto loop2;
/* If the hub has died, clean up after it */
if (hdev->state == USB_STATE_NOTATTACHED) {
@@ -3338,11 +3399,15 @@ static void hub_events(void)
}
}
-loop_autopm:
- /* Allow autosuspend if we're not going to run again */
- if (list_empty(&hub->event_list))
- usb_autopm_enable(intf);
-loop:
+ loop_autopm:
+ /* Balance the usb_autopm_get_interface() above */
+ usb_autopm_put_interface_no_suspend(intf);
+ loop:
+ /* Balance the usb_autopm_get_interface_no_resume() in
+ * kick_khubd() and allow autosuspend.
+ */
+ usb_autopm_put_interface(intf);
+ loop2:
usb_unlock_device(hdev);
kref_put(&hub->kref, hub_release);
@@ -3534,6 +3599,7 @@ static int usb_reset_and_verify_device(struct usb_device *udev)
{
struct usb_device *parent_hdev = udev->parent;
struct usb_hub *parent_hub;
+ struct usb_hcd *hcd = bus_to_hcd(udev->bus);
struct usb_device_descriptor descriptor = udev->descriptor;
int i, ret = 0;
int port1 = udev->portnum;
@@ -3577,6 +3643,16 @@ static int usb_reset_and_verify_device(struct usb_device *udev)
/* Restore the device's previous configuration */
if (!udev->actconfig)
goto done;
+
+ mutex_lock(&hcd->bandwidth_mutex);
+ ret = usb_hcd_alloc_bandwidth(udev, udev->actconfig, NULL, NULL);
+ if (ret < 0) {
+ dev_warn(&udev->dev,
+ "Busted HC? Not enough HCD resources for "
+ "old configuration.\n");
+ mutex_unlock(&hcd->bandwidth_mutex);
+ goto re_enumerate;
+ }
ret = usb_control_msg(udev, usb_sndctrlpipe(udev, 0),
USB_REQ_SET_CONFIGURATION, 0,
udev->actconfig->desc.bConfigurationValue, 0,
@@ -3585,8 +3661,10 @@ static int usb_reset_and_verify_device(struct usb_device *udev)
dev_err(&udev->dev,
"can't restore configuration #%d (error=%d)\n",
udev->actconfig->desc.bConfigurationValue, ret);
+ mutex_unlock(&hcd->bandwidth_mutex);
goto re_enumerate;
}
+ mutex_unlock(&hcd->bandwidth_mutex);
usb_set_device_state(udev, USB_STATE_CONFIGURED);
/* Put interfaces back into the same altsettings as before.
@@ -3596,7 +3674,8 @@ static int usb_reset_and_verify_device(struct usb_device *udev)
* endpoint state.
*/
for (i = 0; i < udev->actconfig->desc.bNumInterfaces; i++) {
- struct usb_interface *intf = udev->actconfig->interface[i];
+ struct usb_host_config *config = udev->actconfig;
+ struct usb_interface *intf = config->interface[i];
struct usb_interface_descriptor *desc;
desc = &intf->cur_altsetting->desc;
@@ -3605,6 +3684,17 @@ static int usb_reset_and_verify_device(struct usb_device *udev)
usb_enable_interface(udev, intf, true);
ret = 0;
} else {
+ /* We've just reset the device, so it will think alt
+ * setting 0 is installed. For usb_set_interface() to
+ * work properly, we need to set the current alternate
+ * interface setting to 0 (or the first alt setting, if
+ * the device doesn't have alt setting 0).
+ */
+ intf->cur_altsetting =
+ usb_find_alt_setting(config, i, 0);
+ if (!intf->cur_altsetting)
+ intf->cur_altsetting =
+ &config->intf_cache[i]->altsetting[0];
ret = usb_set_interface(udev, desc->bInterfaceNumber,
desc->bAlternateSetting);
}
diff --git a/drivers/usb/core/message.c b/drivers/usb/core/message.c
index e80f1af..1b99484 100644
--- a/drivers/usb/core/message.c
+++ b/drivers/usb/core/message.c
@@ -393,13 +393,7 @@ int usb_sg_init(struct usb_sg_request *io, struct usb_device *dev,
if (io->entries <= 0)
return io->entries;
- /* If we're running on an xHCI host controller, queue the whole scatter
- * gather list with one call to urb_enqueue(). This is only for bulk,
- * as that endpoint type does not care how the data gets broken up
- * across frames.
- */
- if (usb_pipebulk(pipe) &&
- bus_to_hcd(dev->bus)->driver->flags & HCD_USB3) {
+ if (dev->bus->sg_tablesize > 0) {
io->urbs = kmalloc(sizeof *io->urbs, mem_flags);
use_sg = true;
} else {
@@ -409,7 +403,7 @@ int usb_sg_init(struct usb_sg_request *io, struct usb_device *dev,
if (!io->urbs)
goto nomem;
- urb_flags = URB_NO_INTERRUPT;
+ urb_flags = 0;
if (dma)
urb_flags |= URB_NO_TRANSFER_DMA_MAP;
if (usb_pipein(pipe))
@@ -441,6 +435,7 @@ int usb_sg_init(struct usb_sg_request *io, struct usb_device *dev,
io->urbs[0]->num_sgs = io->entries;
io->entries = 1;
} else {
+ urb_flags |= URB_NO_INTERRUPT;
for_each_sg(sg, sg, io->entries, i) {
unsigned len;
@@ -1303,6 +1298,7 @@ int usb_set_interface(struct usb_device *dev, int interface, int alternate)
{
struct usb_interface *iface;
struct usb_host_interface *alt;
+ struct usb_hcd *hcd = bus_to_hcd(dev->bus);
int ret;
int manual = 0;
unsigned int epaddr;
@@ -1325,6 +1321,18 @@ int usb_set_interface(struct usb_device *dev, int interface, int alternate)
return -EINVAL;
}
+ /* Make sure we have enough bandwidth for this alternate interface.
+ * Remove the current alt setting and add the new alt setting.
+ */
+ mutex_lock(&hcd->bandwidth_mutex);
+ ret = usb_hcd_alloc_bandwidth(dev, NULL, iface->cur_altsetting, alt);
+ if (ret < 0) {
+ dev_info(&dev->dev, "Not enough bandwidth for altsetting %d\n",
+ alternate);
+ mutex_unlock(&hcd->bandwidth_mutex);
+ return ret;
+ }
+
if (dev->quirks & USB_QUIRK_NO_SET_INTF)
ret = -EPIPE;
else
@@ -1340,8 +1348,13 @@ int usb_set_interface(struct usb_device *dev, int interface, int alternate)
"manual set_interface for iface %d, alt %d\n",
interface, alternate);
manual = 1;
- } else if (ret < 0)
+ } else if (ret < 0) {
+ /* Re-instate the old alt setting */
+ usb_hcd_alloc_bandwidth(dev, NULL, alt, iface->cur_altsetting);
+ mutex_unlock(&hcd->bandwidth_mutex);
return ret;
+ }
+ mutex_unlock(&hcd->bandwidth_mutex);
/* FIXME drivers shouldn't need to replicate/bugfix the logic here
* when they implement async or easily-killable versions of this or
@@ -1423,6 +1436,7 @@ int usb_reset_configuration(struct usb_device *dev)
{
int i, retval;
struct usb_host_config *config;
+ struct usb_hcd *hcd = bus_to_hcd(dev->bus);
if (dev->state == USB_STATE_SUSPENDED)
return -EHOSTUNREACH;
@@ -1438,12 +1452,46 @@ int usb_reset_configuration(struct usb_device *dev)
}
config = dev->actconfig;
+ retval = 0;
+ mutex_lock(&hcd->bandwidth_mutex);
+ /* Make sure we have enough bandwidth for each alternate setting 0 */
+ for (i = 0; i < config->desc.bNumInterfaces; i++) {
+ struct usb_interface *intf = config->interface[i];
+ struct usb_host_interface *alt;
+
+ alt = usb_altnum_to_altsetting(intf, 0);
+ if (!alt)
+ alt = &intf->altsetting[0];
+ if (alt != intf->cur_altsetting)
+ retval = usb_hcd_alloc_bandwidth(dev, NULL,
+ intf->cur_altsetting, alt);
+ if (retval < 0)
+ break;
+ }
+ /* If not, reinstate the old alternate settings */
+ if (retval < 0) {
+reset_old_alts:
+ for (; i >= 0; i--) {
+ struct usb_interface *intf = config->interface[i];
+ struct usb_host_interface *alt;
+
+ alt = usb_altnum_to_altsetting(intf, 0);
+ if (!alt)
+ alt = &intf->altsetting[0];
+ if (alt != intf->cur_altsetting)
+ usb_hcd_alloc_bandwidth(dev, NULL,
+ alt, intf->cur_altsetting);
+ }
+ mutex_unlock(&hcd->bandwidth_mutex);
+ return retval;
+ }
retval = usb_control_msg(dev, usb_sndctrlpipe(dev, 0),
USB_REQ_SET_CONFIGURATION, 0,
config->desc.bConfigurationValue, 0,
NULL, 0, USB_CTRL_SET_TIMEOUT);
if (retval < 0)
- return retval;
+ goto reset_old_alts;
+ mutex_unlock(&hcd->bandwidth_mutex);
/* re-init hc/hcd interface/endpoint state */
for (i = 0; i < config->desc.bNumInterfaces; i++) {
@@ -1585,7 +1633,7 @@ static struct usb_interface_assoc_descriptor *find_iad(struct usb_device *dev,
*
* See usb_queue_reset_device() for more details
*/
-void __usb_queue_reset_device(struct work_struct *ws)
+static void __usb_queue_reset_device(struct work_struct *ws)
{
int rc;
struct usb_interface *iface =
@@ -1652,6 +1700,7 @@ int usb_set_configuration(struct usb_device *dev, int configuration)
int i, ret;
struct usb_host_config *cp = NULL;
struct usb_interface **new_interfaces = NULL;
+ struct usb_hcd *hcd = bus_to_hcd(dev->bus);
int n, nintf;
if (dev->authorized == 0 || configuration == -1)
@@ -1721,12 +1770,11 @@ free_interfaces:
* host controller will not allow submissions to dropped endpoints. If
* this call fails, the device state is unchanged.
*/
- if (cp)
- ret = usb_hcd_check_bandwidth(dev, cp, NULL);
- else
- ret = usb_hcd_check_bandwidth(dev, NULL, NULL);
+ mutex_lock(&hcd->bandwidth_mutex);
+ ret = usb_hcd_alloc_bandwidth(dev, cp, NULL, NULL);
if (ret < 0) {
usb_autosuspend_device(dev);
+ mutex_unlock(&hcd->bandwidth_mutex);
goto free_interfaces;
}
@@ -1752,10 +1800,12 @@ free_interfaces:
dev->actconfig = cp;
if (!cp) {
usb_set_device_state(dev, USB_STATE_ADDRESS);
- usb_hcd_check_bandwidth(dev, NULL, NULL);
+ usb_hcd_alloc_bandwidth(dev, NULL, NULL, NULL);
usb_autosuspend_device(dev);
+ mutex_unlock(&hcd->bandwidth_mutex);
goto free_interfaces;
}
+ mutex_unlock(&hcd->bandwidth_mutex);
usb_set_device_state(dev, USB_STATE_CONFIGURED);
/* Initialize the new interface structures and the
diff --git a/drivers/usb/core/sysfs.c b/drivers/usb/core/sysfs.c
index 7ec3041a..1547700 100644
--- a/drivers/usb/core/sysfs.c
+++ b/drivers/usb/core/sysfs.c
@@ -139,6 +139,16 @@ show_devnum(struct device *dev, struct device_attribute *attr, char *buf)
static DEVICE_ATTR(devnum, S_IRUGO, show_devnum, NULL);
static ssize_t
+show_devpath(struct device *dev, struct device_attribute *attr, char *buf)
+{
+ struct usb_device *udev;
+
+ udev = to_usb_device(dev);
+ return sprintf(buf, "%s\n", udev->devpath);
+}
+static DEVICE_ATTR(devpath, S_IRUGO, show_devpath, NULL);
+
+static ssize_t
show_version(struct device *dev, struct device_attribute *attr, char *buf)
{
struct usb_device *udev;
@@ -317,7 +327,6 @@ static DEVICE_ATTR(autosuspend, S_IRUGO | S_IWUSR,
static const char on_string[] = "on";
static const char auto_string[] = "auto";
-static const char suspend_string[] = "suspend";
static ssize_t
show_level(struct device *dev, struct device_attribute *attr, char *buf)
@@ -325,13 +334,8 @@ show_level(struct device *dev, struct device_attribute *attr, char *buf)
struct usb_device *udev = to_usb_device(dev);
const char *p = auto_string;
- if (udev->state == USB_STATE_SUSPENDED) {
- if (udev->autoresume_disabled)
- p = suspend_string;
- } else {
- if (udev->autosuspend_disabled)
- p = on_string;
- }
+ if (udev->state != USB_STATE_SUSPENDED && udev->autosuspend_disabled)
+ p = on_string;
return sprintf(buf, "%s\n", p);
}
@@ -343,7 +347,7 @@ set_level(struct device *dev, struct device_attribute *attr,
int len = count;
char *cp;
int rc = 0;
- int old_autosuspend_disabled, old_autoresume_disabled;
+ int old_autosuspend_disabled;
cp = memchr(buf, '\n', count);
if (cp)
@@ -351,7 +355,6 @@ set_level(struct device *dev, struct device_attribute *attr,
usb_lock_device(udev);
old_autosuspend_disabled = udev->autosuspend_disabled;
- old_autoresume_disabled = udev->autoresume_disabled;
/* Setting the flags without calling usb_pm_lock is a subject to
* races, but who cares...
@@ -359,28 +362,18 @@ set_level(struct device *dev, struct device_attribute *attr,
if (len == sizeof on_string - 1 &&
strncmp(buf, on_string, len) == 0) {
udev->autosuspend_disabled = 1;
- udev->autoresume_disabled = 0;
rc = usb_external_resume_device(udev, PMSG_USER_RESUME);
} else if (len == sizeof auto_string - 1 &&
strncmp(buf, auto_string, len) == 0) {
udev->autosuspend_disabled = 0;
- udev->autoresume_disabled = 0;
rc = usb_external_resume_device(udev, PMSG_USER_RESUME);
- } else if (len == sizeof suspend_string - 1 &&
- strncmp(buf, suspend_string, len) == 0) {
- udev->autosuspend_disabled = 0;
- udev->autoresume_disabled = 1;
- rc = usb_external_suspend_device(udev, PMSG_USER_SUSPEND);
-
} else
rc = -EINVAL;
- if (rc) {
+ if (rc)
udev->autosuspend_disabled = old_autosuspend_disabled;
- udev->autoresume_disabled = old_autoresume_disabled;
- }
usb_unlock_device(udev);
return (rc < 0 ? rc : count);
}
@@ -508,6 +501,28 @@ static ssize_t usb_dev_authorized_store(struct device *dev,
static DEVICE_ATTR(authorized, 0644,
usb_dev_authorized_show, usb_dev_authorized_store);
+/* "Safely remove a device" */
+static ssize_t usb_remove_store(struct device *dev,
+ struct device_attribute *attr,
+ const char *buf, size_t count)
+{
+ struct usb_device *udev = to_usb_device(dev);
+ int rc = 0;
+
+ usb_lock_device(udev);
+ if (udev->state != USB_STATE_NOTATTACHED) {
+
+ /* To avoid races, first unconfigure and then remove */
+ usb_set_configuration(udev, -1);
+ rc = usb_remove_device(udev);
+ }
+ if (rc == 0)
+ rc = count;
+ usb_unlock_device(udev);
+ return rc;
+}
+static DEVICE_ATTR(remove, 0200, NULL, usb_remove_store);
+
static struct attribute *dev_attrs[] = {
/* current configuration's attributes */
@@ -516,8 +531,8 @@ static struct attribute *dev_attrs[] = {
&dev_attr_bConfigurationValue.attr,
&dev_attr_bmAttributes.attr,
&dev_attr_bMaxPower.attr,
- &dev_attr_urbnum.attr,
/* device attributes */
+ &dev_attr_urbnum.attr,
&dev_attr_idVendor.attr,
&dev_attr_idProduct.attr,
&dev_attr_bcdDevice.attr,
@@ -529,10 +544,12 @@ static struct attribute *dev_attrs[] = {
&dev_attr_speed.attr,
&dev_attr_busnum.attr,
&dev_attr_devnum.attr,
+ &dev_attr_devpath.attr,
&dev_attr_version.attr,
&dev_attr_maxchild.attr,
&dev_attr_quirks.attr,
&dev_attr_authorized.attr,
+ &dev_attr_remove.attr,
NULL,
};
static struct attribute_group dev_attr_grp = {
diff --git a/drivers/usb/core/urb.c b/drivers/usb/core/urb.c
index 0885d4a..e7cae13 100644
--- a/drivers/usb/core/urb.c
+++ b/drivers/usb/core/urb.c
@@ -429,8 +429,16 @@ int usb_submit_urb(struct urb *urb, gfp_t mem_flags)
case USB_ENDPOINT_XFER_ISOC:
case USB_ENDPOINT_XFER_INT:
/* too small? */
- if (urb->interval <= 0)
- return -EINVAL;
+ switch (dev->speed) {
+ case USB_SPEED_VARIABLE:
+ if (urb->interval < 6)
+ return -EINVAL;
+ break;
+ default:
+ if (urb->interval <= 0)
+ return -EINVAL;
+ break;
+ }
/* too big? */
switch (dev->speed) {
case USB_SPEED_SUPER: /* units are 125us */
@@ -438,6 +446,10 @@ int usb_submit_urb(struct urb *urb, gfp_t mem_flags)
if (urb->interval > (1 << 15))
return -EINVAL;
max = 1 << 15;
+ case USB_SPEED_VARIABLE:
+ if (urb->interval > 16)
+ return -EINVAL;
+ break;
case USB_SPEED_HIGH: /* units are microframes */
/* NOTE usb handles 2^15 */
if (urb->interval > (1024 * 8))
@@ -461,8 +473,10 @@ int usb_submit_urb(struct urb *urb, gfp_t mem_flags)
default:
return -EINVAL;
}
- /* Round down to a power of 2, no more than max */
- urb->interval = min(max, 1 << ilog2(urb->interval));
+ if (dev->speed != USB_SPEED_VARIABLE) {
+ /* Round down to a power of 2, no more than max */
+ urb->interval = min(max, 1 << ilog2(urb->interval));
+ }
}
return usb_hcd_submit_urb(urb, mem_flags);
diff --git a/drivers/usb/core/usb.c b/drivers/usb/core/usb.c
index b1b85ab..4e2c6df 100644
--- a/drivers/usb/core/usb.c
+++ b/drivers/usb/core/usb.c
@@ -64,6 +64,43 @@ MODULE_PARM_DESC(autosuspend, "default autosuspend delay");
/**
+ * usb_find_alt_setting() - Given a configuration, find the alternate setting
+ * for the given interface.
+ * @config - the configuration to search (not necessarily the current config).
+ * @iface_num - interface number to search in
+ * @alt_num - alternate interface setting number to search for.
+ *
+ * Search the configuration's interface cache for the given alt setting.
+ */
+struct usb_host_interface *usb_find_alt_setting(
+ struct usb_host_config *config,
+ unsigned int iface_num,
+ unsigned int alt_num)
+{
+ struct usb_interface_cache *intf_cache = NULL;
+ int i;
+
+ for (i = 0; i < config->desc.bNumInterfaces; i++) {
+ if (config->intf_cache[i]->altsetting[0].desc.bInterfaceNumber
+ == iface_num) {
+ intf_cache = config->intf_cache[i];
+ break;
+ }
+ }
+ if (!intf_cache)
+ return NULL;
+ for (i = 0; i < intf_cache->num_altsetting; i++)
+ if (intf_cache->altsetting[i].desc.bAlternateSetting == alt_num)
+ return &intf_cache->altsetting[i];
+
+ printk(KERN_DEBUG "Did not find alt setting %u for intf %u, "
+ "config %u\n", alt_num, iface_num,
+ config->desc.bConfigurationValue);
+ return NULL;
+}
+EXPORT_SYMBOL_GPL(usb_find_alt_setting);
+
+/**
* usb_ifnum_to_if - get the interface object with a given interface number
* @dev: the device whose current configuration is considered
* @ifnum: the desired interface
@@ -130,24 +167,17 @@ struct usb_host_interface *usb_altnum_to_altsetting(
}
EXPORT_SYMBOL_GPL(usb_altnum_to_altsetting);
-struct find_interface_arg {
- int minor;
- struct usb_interface *interface;
-};
-
static int __find_interface(struct device *dev, void *data)
{
- struct find_interface_arg *arg = data;
+ int *minor = data;
struct usb_interface *intf;
if (!is_usb_interface(dev))
return 0;
intf = to_usb_interface(dev);
- if (intf->minor != -1 && intf->minor == arg->minor) {
- arg->interface = intf;
+ if (intf->minor != -1 && intf->minor == *minor)
return 1;
- }
return 0;
}
@@ -156,21 +186,20 @@ static int __find_interface(struct device *dev, void *data)
* @drv: the driver whose current configuration is considered
* @minor: the minor number of the desired device
*
- * This walks the driver device list and returns a pointer to the interface
+ * This walks the bus device list and returns a pointer to the interface
* with the matching minor. Note, this only works for devices that share the
* USB major number.
*/
struct usb_interface *usb_find_interface(struct usb_driver *drv, int minor)
{
- struct find_interface_arg argb;
- int retval;
+ struct device *dev;
+
+ dev = bus_find_device(&usb_bus_type, NULL, &minor, __find_interface);
+
+ /* Drop reference count from bus_find_device */
+ put_device(dev);
- argb.minor = minor;
- argb.interface = NULL;
- /* eat the error, it will be in argb.interface */
- retval = driver_for_each_device(&drv->drvwrap.driver, NULL, &argb,
- __find_interface);
- return argb.interface;
+ return dev ? to_usb_interface(dev) : NULL;
}
EXPORT_SYMBOL_GPL(usb_find_interface);
@@ -1038,7 +1067,7 @@ static struct notifier_block usb_bus_nb = {
struct dentry *usb_debug_root;
EXPORT_SYMBOL_GPL(usb_debug_root);
-struct dentry *usb_debug_devices;
+static struct dentry *usb_debug_devices;
static int usb_debugfs_init(void)
{
diff --git a/drivers/usb/core/usb.h b/drivers/usb/core/usb.h
index 9a8b15e..4c36c7f 100644
--- a/drivers/usb/core/usb.h
+++ b/drivers/usb/core/usb.h
@@ -24,6 +24,7 @@ extern void usb_disable_device(struct usb_device *dev, int skip_ep0);
extern int usb_deauthorize_device(struct usb_device *);
extern int usb_authorize_device(struct usb_device *);
extern void usb_detect_quirks(struct usb_device *udev);
+extern int usb_remove_device(struct usb_device *udev);
extern int usb_get_device_descriptor(struct usb_device *dev,
unsigned int size);
diff --git a/drivers/usb/gadget/Kconfig b/drivers/usb/gadget/Kconfig
index a18e3c5..ee41120 100644
--- a/drivers/usb/gadget/Kconfig
+++ b/drivers/usb/gadget/Kconfig
@@ -732,6 +732,24 @@ config USB_FILE_STORAGE_TEST
behavior of USB Mass Storage hosts. Not needed for
normal operation.
+config USB_MASS_STORAGE
+ tristate "Mass Storage Gadget"
+ depends on BLOCK
+ help
+ The Mass Storage Gadget acts as a USB Mass Storage disk drive.
+ As its storage repository it can use a regular file or a block
+ device (in much the same way as the "loop" device driver),
+ specified as a module parameter or sysfs option.
+
+ This is heavily based on File-backed Storage Gadget and in most
+ cases you will want to use FSG instead. This gadget is mostly
+ here to test the functionality of the Mass Storage Function
+ which may be used with composite framework.
+
+ Say "y" to link the driver statically, or "m" to build
+ a dynamically linked module called "g_file_storage". If unsure,
+ consider File-backed Storage Gadget.
+
config USB_G_SERIAL
tristate "Serial Gadget (with CDC ACM and CDC OBEX support)"
help
@@ -794,6 +812,48 @@ config USB_CDC_COMPOSITE
Say "y" to link the driver statically, or "m" to build a
dynamically linked module.
+config USB_G_MULTI
+ tristate "Multifunction Composite Gadget (EXPERIMENTAL)"
+ depends on BLOCK && NET
+ help
+ The Multifunction Composite Gadget provides Ethernet (RNDIS
+ and/or CDC Ethernet), mass storage and ACM serial link
+ interfaces.
+
+ You will be asked to choose which of the two configurations is
+ to be available in the gadget. At least one configuration must
+ be chosen to make the gadget usable. Selecting more than one
+ configuration will prevent Windows from automatically detecting
+ the gadget as a composite gadget, so an INF file will be needed to
+ use the gadget.
+
+ Say "y" to link the driver statically, or "m" to build a
+ dynamically linked module called "g_multi".
+
+config USB_G_MULTI_RNDIS
+ bool "RNDIS + CDC Serial + Storage configuration"
+ depends on USB_G_MULTI
+ default y
+ help
+ This option enables a configuration with RNDIS, CDC Serial and
+ Mass Storage functions available in the Multifunction Composite
+ Gadget. This is the configuration dedicated for Windows since RNDIS
+ is Microsoft's protocol.
+
+ If unsure, say "y".
+
+config USB_G_MULTI_CDC
+ bool "CDC Ethernet + CDC Serial + Storage configuration"
+ depends on USB_G_MULTI
+ default n
+ help
+ This option enables a configuration with CDC Ethernet (ECM), CDC
+ Serial and Mass Storage functions available in the Multifunction
+ Composite Gadget.
+
+ If unsure, say "y".
+
+
# put drivers that need isochronous transfer support (for audio
# or video class gadget drivers), or specific hardware, here.
diff --git a/drivers/usb/gadget/Makefile b/drivers/usb/gadget/Makefile
index 9d7b87c..2e2c047 100644
--- a/drivers/usb/gadget/Makefile
+++ b/drivers/usb/gadget/Makefile
@@ -39,16 +39,20 @@ g_serial-objs := serial.o
g_midi-objs := gmidi.o
gadgetfs-objs := inode.o
g_file_storage-objs := file_storage.o
+g_mass_storage-objs := mass_storage.o
g_printer-objs := printer.o
g_cdc-objs := cdc2.o
+g_multi-objs := multi.o
obj-$(CONFIG_USB_ZERO) += g_zero.o
obj-$(CONFIG_USB_AUDIO) += g_audio.o
obj-$(CONFIG_USB_ETH) += g_ether.o
obj-$(CONFIG_USB_GADGETFS) += gadgetfs.o
obj-$(CONFIG_USB_FILE_STORAGE) += g_file_storage.o
+obj-$(CONFIG_USB_MASS_STORAGE) += g_mass_storage.o
obj-$(CONFIG_USB_G_SERIAL) += g_serial.o
obj-$(CONFIG_USB_G_PRINTER) += g_printer.o
obj-$(CONFIG_USB_MIDI_GADGET) += g_midi.o
obj-$(CONFIG_USB_CDC_COMPOSITE) += g_cdc.o
+obj-$(CONFIG_USB_G_MULTI) += g_multi.o
diff --git a/drivers/usb/gadget/at91_udc.c b/drivers/usb/gadget/at91_udc.c
index 66450a1..043e04d 100644
--- a/drivers/usb/gadget/at91_udc.c
+++ b/drivers/usb/gadget/at91_udc.c
@@ -892,7 +892,7 @@ static void pullup(struct at91_udc *udc, int is_on)
txvc |= AT91_UDP_TXVC_PUON;
at91_udp_write(udc, AT91_UDP_TXVC, txvc);
- } else if (cpu_is_at91sam9261()) {
+ } else if (cpu_is_at91sam9261() || cpu_is_at91sam9g10()) {
u32 usbpucr;
usbpucr = at91_sys_read(AT91_MATRIX_USBPUCR);
@@ -910,7 +910,7 @@ static void pullup(struct at91_udc *udc, int is_on)
txvc &= ~AT91_UDP_TXVC_PUON;
at91_udp_write(udc, AT91_UDP_TXVC, txvc);
- } else if (cpu_is_at91sam9261()) {
+ } else if (cpu_is_at91sam9261() || cpu_is_at91sam9g10()) {
u32 usbpucr;
usbpucr = at91_sys_read(AT91_MATRIX_USBPUCR);
@@ -1692,7 +1692,7 @@ static int __init at91udc_probe(struct platform_device *pdev)
udc->ep[3].maxpacket = 64;
udc->ep[4].maxpacket = 512;
udc->ep[5].maxpacket = 512;
- } else if (cpu_is_at91sam9261()) {
+ } else if (cpu_is_at91sam9261() || cpu_is_at91sam9g10()) {
udc->ep[3].maxpacket = 64;
} else if (cpu_is_at91sam9263()) {
udc->ep[0].maxpacket = 64;
diff --git a/drivers/usb/gadget/audio.c b/drivers/usb/gadget/audio.c
index a3a0f4a..58f2203 100644
--- a/drivers/usb/gadget/audio.c
+++ b/drivers/usb/gadget/audio.c
@@ -89,120 +89,6 @@ static const struct usb_descriptor_header *otg_desc[] = {
/*-------------------------------------------------------------------------*/
-/**
- * Handle USB audio endpoint set/get command in setup class request
- */
-
-static int audio_set_endpoint_req(struct usb_configuration *c,
- const struct usb_ctrlrequest *ctrl)
-{
- struct usb_composite_dev *cdev = c->cdev;
- int value = -EOPNOTSUPP;
- u16 ep = le16_to_cpu(ctrl->wIndex);
- u16 len = le16_to_cpu(ctrl->wLength);
- u16 w_value = le16_to_cpu(ctrl->wValue);
-
- DBG(cdev, "bRequest 0x%x, w_value 0x%04x, len %d, endpoint %d\n",
- ctrl->bRequest, w_value, len, ep);
-
- switch (ctrl->bRequest) {
- case UAC_SET_CUR:
- value = 0;
- break;
-
- case UAC_SET_MIN:
- break;
-
- case UAC_SET_MAX:
- break;
-
- case UAC_SET_RES:
- break;
-
- case UAC_SET_MEM:
- break;
-
- default:
- break;
- }
-
- return value;
-}
-
-static int audio_get_endpoint_req(struct usb_configuration *c,
- const struct usb_ctrlrequest *ctrl)
-{
- struct usb_composite_dev *cdev = c->cdev;
- int value = -EOPNOTSUPP;
- u8 ep = ((le16_to_cpu(ctrl->wIndex) >> 8) & 0xFF);
- u16 len = le16_to_cpu(ctrl->wLength);
- u16 w_value = le16_to_cpu(ctrl->wValue);
-
- DBG(cdev, "bRequest 0x%x, w_value 0x%04x, len %d, endpoint %d\n",
- ctrl->bRequest, w_value, len, ep);
-
- switch (ctrl->bRequest) {
- case UAC_GET_CUR:
- case UAC_GET_MIN:
- case UAC_GET_MAX:
- case UAC_GET_RES:
- value = 3;
- break;
- case UAC_GET_MEM:
- break;
- default:
- break;
- }
-
- return value;
-}
-
-static int
-audio_setup(struct usb_configuration *c, const struct usb_ctrlrequest *ctrl)
-{
- struct usb_composite_dev *cdev = c->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
- * Audio class messages; interface activation uses set_alt().
- */
- switch (ctrl->bRequestType) {
- case USB_DIR_OUT | USB_TYPE_CLASS | USB_RECIP_ENDPOINT:
- value = audio_set_endpoint_req(c, ctrl);
- break;
-
- case USB_DIR_IN | USB_TYPE_CLASS | USB_RECIP_ENDPOINT:
- value = audio_get_endpoint_req(c, ctrl);
- break;
-
- default:
- ERROR(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, "Audio 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, "Audio response on err %d\n", value);
- }
-
- /* device either stalls (value < 0) or reports success */
- return value;
-}
-
-/*-------------------------------------------------------------------------*/
-
static int __init audio_do_config(struct usb_configuration *c)
{
/* FIXME alloc iConfiguration string, set it in c->strings */
@@ -220,7 +106,6 @@ static int __init audio_do_config(struct usb_configuration *c)
static struct usb_configuration audio_config_driver = {
.label = DRIVER_DESC,
.bind = audio_do_config,
- .setup = audio_setup,
.bConfigurationValue = 1,
/* .iConfiguration = DYNAMIC */
.bmAttributes = USB_CONFIG_ATT_SELFPOWER,
diff --git a/drivers/usb/gadget/composite.c b/drivers/usb/gadget/composite.c
index d05397e..09289bb 100644
--- a/drivers/usb/gadget/composite.c
+++ b/drivers/usb/gadget/composite.c
@@ -373,6 +373,8 @@ static void reset_config(struct usb_composite_dev *cdev)
list_for_each_entry(f, &cdev->config->functions, list) {
if (f->disable)
f->disable(f);
+
+ bitmap_zero(f->endpoints, 32);
}
cdev->config = NULL;
}
@@ -418,10 +420,35 @@ static int set_config(struct usb_composite_dev *cdev,
/* Initialize all interfaces by setting them to altsetting zero. */
for (tmp = 0; tmp < MAX_CONFIG_INTERFACES; tmp++) {
struct usb_function *f = c->interface[tmp];
+ struct usb_descriptor_header **descriptors;
if (!f)
break;
+ /*
+ * Record which endpoints are used by the function. This is used
+ * to dispatch control requests targeted at that endpoint to the
+ * function's setup callback instead of the current
+ * configuration's setup callback.
+ */
+ if (gadget->speed == USB_SPEED_HIGH)
+ descriptors = f->hs_descriptors;
+ else
+ descriptors = f->descriptors;
+
+ for (; *descriptors; ++descriptors) {
+ struct usb_endpoint_descriptor *ep;
+ int addr;
+
+ if ((*descriptors)->bDescriptorType != USB_DT_ENDPOINT)
+ continue;
+
+ ep = (struct usb_endpoint_descriptor *)*descriptors;
+ addr = ((ep->bEndpointAddress & 0x80) >> 3)
+ | (ep->bEndpointAddress & 0x0f);
+ set_bit(addr, f->endpoints);
+ }
+
result = f->set_alt(f, tmp, 0);
if (result < 0) {
DBG(cdev, "interface %d (%s/%p) alt 0 --> %d\n",
@@ -688,6 +715,7 @@ composite_setup(struct usb_gadget *gadget, const struct usb_ctrlrequest *ctrl)
u16 w_value = le16_to_cpu(ctrl->wValue);
u16 w_length = le16_to_cpu(ctrl->wLength);
struct usb_function *f = NULL;
+ u8 endp;
/* partial re-init of the response message; the function or the
* gadget might need to intercept e.g. a control-OUT completion
@@ -800,23 +828,33 @@ unknown:
ctrl->bRequestType, ctrl->bRequest,
w_value, w_index, w_length);
- /* functions always handle their interfaces ... punt other
- * recipients (endpoint, other, WUSB, ...) to the current
+ /* functions always handle their interfaces and endpoints...
+ * punt other recipients (other, WUSB, ...) to the current
* configuration code.
*
* REVISIT it could make sense to let the composite device
* take such requests too, if that's ever needed: to work
* in config 0, etc.
*/
- if ((ctrl->bRequestType & USB_RECIP_MASK)
- == USB_RECIP_INTERFACE) {
+ switch (ctrl->bRequestType & USB_RECIP_MASK) {
+ case USB_RECIP_INTERFACE:
f = cdev->config->interface[intf];
- if (f && f->setup)
- value = f->setup(f, ctrl);
- else
+ break;
+
+ case USB_RECIP_ENDPOINT:
+ endp = ((w_index & 0x80) >> 3) | (w_index & 0x0f);
+ list_for_each_entry(f, &cdev->config->functions, list) {
+ if (test_bit(endp, f->endpoints))
+ break;
+ }
+ if (&f->list == &cdev->config->functions)
f = NULL;
+ break;
}
- if (value < 0 && !f) {
+
+ if (f && f->setup)
+ value = f->setup(f, ctrl);
+ else {
struct usb_configuration *c;
c = cdev->config;
@@ -1054,7 +1092,8 @@ static struct usb_gadget_driver composite_driver = {
.speed = USB_SPEED_HIGH,
.bind = composite_bind,
- .unbind = __exit_p(composite_unbind),
+ /* .unbind = __exit_p(composite_unbind), */
+ .unbind = composite_unbind,
.setup = composite_setup,
.disconnect = composite_disconnect,
@@ -1103,7 +1142,7 @@ int __init usb_composite_register(struct usb_composite_driver *driver)
* This function is used to unregister drivers using the composite
* driver framework.
*/
-void __exit usb_composite_unregister(struct usb_composite_driver *driver)
+void /* __exit */ usb_composite_unregister(struct usb_composite_driver *driver)
{
if (composite != driver)
return;
diff --git a/drivers/usb/gadget/ether.c b/drivers/usb/gadget/ether.c
index 167cb2a..141372b 100644
--- a/drivers/usb/gadget/ether.c
+++ b/drivers/usb/gadget/ether.c
@@ -25,6 +25,14 @@
#include <linux/kernel.h>
#include <linux/utsname.h>
+
+#if defined USB_ETH_RNDIS
+# undef USB_ETH_RNDIS
+#endif
+#ifdef CONFIG_USB_ETH_RNDIS
+# define USB_ETH_RNDIS y
+#endif
+
#include "u_ether.h"
@@ -66,7 +74,7 @@
#define DRIVER_DESC "Ethernet Gadget"
#define DRIVER_VERSION "Memorial Day 2008"
-#ifdef CONFIG_USB_ETH_RNDIS
+#ifdef USB_ETH_RNDIS
#define PREFIX "RNDIS/"
#else
#define PREFIX ""
@@ -87,7 +95,7 @@
static inline bool has_rndis(void)
{
-#ifdef CONFIG_USB_ETH_RNDIS
+#ifdef USB_ETH_RNDIS
return true;
#else
return false;
@@ -110,7 +118,7 @@ static inline bool has_rndis(void)
#include "f_ecm.c"
#include "f_subset.c"
-#ifdef CONFIG_USB_ETH_RNDIS
+#ifdef USB_ETH_RNDIS
#include "f_rndis.c"
#include "rndis.c"
#endif
@@ -251,7 +259,7 @@ static struct usb_configuration rndis_config_driver = {
/*-------------------------------------------------------------------------*/
-#ifdef CONFIG_USB_ETH_EEM
+#ifdef USB_ETH_EEM
static int use_eem = 1;
#else
static int use_eem;
diff --git a/drivers/usb/gadget/f_acm.c b/drivers/usb/gadget/f_acm.c
index 4e36578..d10353d 100644
--- a/drivers/usb/gadget/f_acm.c
+++ b/drivers/usb/gadget/f_acm.c
@@ -4,6 +4,8 @@
* Copyright (C) 2003 Al Borchers (alborchers@steinerpoint.com)
* Copyright (C) 2008 by David Brownell
* Copyright (C) 2008 by Nokia Corporation
+ * Copyright (C) 2009 by Samsung Electronics
+ * Author: Michal Nazarewicz (m.nazarewicz@samsung.com)
*
* This software is distributed under the terms of the GNU General
* Public License ("GPL") as published by the Free Software Foundation,
@@ -99,6 +101,20 @@ static inline struct f_acm *port_to_acm(struct gserial *p)
/* interface and class descriptors: */
+static struct usb_interface_assoc_descriptor
+acm_iad_descriptor = {
+ .bLength = sizeof acm_iad_descriptor,
+ .bDescriptorType = USB_DT_INTERFACE_ASSOCIATION,
+
+ /* .bFirstInterface = DYNAMIC, */
+ .bInterfaceCount = 2, // control + data
+ .bFunctionClass = USB_CLASS_COMM,
+ .bFunctionSubClass = USB_CDC_SUBCLASS_ACM,
+ .bFunctionProtocol = USB_CDC_PROTO_NONE,
+ /* .iFunction = DYNAMIC */
+};
+
+
static struct usb_interface_descriptor acm_control_interface_desc __initdata = {
.bLength = USB_DT_INTERFACE_SIZE,
.bDescriptorType = USB_DT_INTERFACE,
@@ -178,6 +194,7 @@ static struct usb_endpoint_descriptor acm_fs_out_desc __initdata = {
};
static struct usb_descriptor_header *acm_fs_function[] __initdata = {
+ (struct usb_descriptor_header *) &acm_iad_descriptor,
(struct usb_descriptor_header *) &acm_control_interface_desc,
(struct usb_descriptor_header *) &acm_header_desc,
(struct usb_descriptor_header *) &acm_call_mgmt_descriptor,
@@ -216,6 +233,7 @@ static struct usb_endpoint_descriptor acm_hs_out_desc __initdata = {
};
static struct usb_descriptor_header *acm_hs_function[] __initdata = {
+ (struct usb_descriptor_header *) &acm_iad_descriptor,
(struct usb_descriptor_header *) &acm_control_interface_desc,
(struct usb_descriptor_header *) &acm_header_desc,
(struct usb_descriptor_header *) &acm_call_mgmt_descriptor,
@@ -232,11 +250,13 @@ static struct usb_descriptor_header *acm_hs_function[] __initdata = {
#define ACM_CTRL_IDX 0
#define ACM_DATA_IDX 1
+#define ACM_IAD_IDX 2
/* static strings, in UTF-8 */
static struct usb_string acm_string_defs[] = {
[ACM_CTRL_IDX].s = "CDC Abstract Control Model (ACM)",
[ACM_DATA_IDX].s = "CDC ACM Data",
+ [ACM_IAD_IDX ].s = "CDC Serial",
{ /* ZEROES END LIST */ },
};
@@ -563,6 +583,7 @@ acm_bind(struct usb_configuration *c, struct usb_function *f)
if (status < 0)
goto fail;
acm->ctrl_id = status;
+ acm_iad_descriptor.bFirstInterface = status;
acm_control_interface_desc.bInterfaceNumber = status;
acm_union_desc .bMasterInterface0 = status;
@@ -732,6 +753,13 @@ int __init acm_bind_config(struct usb_configuration *c, u8 port_num)
acm_string_defs[ACM_DATA_IDX].id = status;
acm_data_interface_desc.iInterface = status;
+
+ status = usb_string_id(c->cdev);
+ if (status < 0)
+ return status;
+ acm_string_defs[ACM_IAD_IDX].id = status;
+
+ acm_iad_descriptor.iFunction = status;
}
/* allocate and initialize one new instance */
diff --git a/drivers/usb/gadget/f_audio.c b/drivers/usb/gadget/f_audio.c
index 98e9bb9..c43c89f 100644
--- a/drivers/usb/gadget/f_audio.c
+++ b/drivers/usb/gadget/f_audio.c
@@ -445,6 +445,70 @@ static int audio_get_intf_req(struct usb_function *f,
return len;
}
+static int audio_set_endpoint_req(struct usb_function *f,
+ const struct usb_ctrlrequest *ctrl)
+{
+ struct usb_composite_dev *cdev = f->config->cdev;
+ int value = -EOPNOTSUPP;
+ u16 ep = le16_to_cpu(ctrl->wIndex);
+ u16 len = le16_to_cpu(ctrl->wLength);
+ u16 w_value = le16_to_cpu(ctrl->wValue);
+
+ DBG(cdev, "bRequest 0x%x, w_value 0x%04x, len %d, endpoint %d\n",
+ ctrl->bRequest, w_value, len, ep);
+
+ switch (ctrl->bRequest) {
+ case UAC_SET_CUR:
+ value = 0;
+ break;
+
+ case UAC_SET_MIN:
+ break;
+
+ case UAC_SET_MAX:
+ break;
+
+ case UAC_SET_RES:
+ break;
+
+ case UAC_SET_MEM:
+ break;
+
+ default:
+ break;
+ }
+
+ return value;
+}
+
+static int audio_get_endpoint_req(struct usb_function *f,
+ const struct usb_ctrlrequest *ctrl)
+{
+ struct usb_composite_dev *cdev = f->config->cdev;
+ int value = -EOPNOTSUPP;
+ u8 ep = ((le16_to_cpu(ctrl->wIndex) >> 8) & 0xFF);
+ u16 len = le16_to_cpu(ctrl->wLength);
+ u16 w_value = le16_to_cpu(ctrl->wValue);
+
+ DBG(cdev, "bRequest 0x%x, w_value 0x%04x, len %d, endpoint %d\n",
+ ctrl->bRequest, w_value, len, ep);
+
+ switch (ctrl->bRequest) {
+ case UAC_GET_CUR:
+ case UAC_GET_MIN:
+ case UAC_GET_MAX:
+ case UAC_GET_RES:
+ value = 3;
+ break;
+ case UAC_GET_MEM:
+ break;
+ default:
+ break;
+ }
+
+ return value;
+}
+
static int
f_audio_setup(struct usb_function *f, const struct usb_ctrlrequest *ctrl)
{
@@ -455,8 +519,8 @@ f_audio_setup(struct usb_function *f, const struct usb_ctrlrequest *ctrl)
u16 w_value = le16_to_cpu(ctrl->wValue);
u16 w_length = le16_to_cpu(ctrl->wLength);
- /* composite driver infrastructure handles everything except
- * Audio class messages; interface activation uses set_alt().
+ /* composite driver infrastructure handles everything; interface
+ * activation uses set_alt().
*/
switch (ctrl->bRequestType) {
case USB_DIR_OUT | USB_TYPE_CLASS | USB_RECIP_INTERFACE:
@@ -467,6 +531,14 @@ f_audio_setup(struct usb_function *f, const struct usb_ctrlrequest *ctrl)
value = audio_get_intf_req(f, ctrl);
break;
+ case USB_DIR_OUT | USB_TYPE_CLASS | USB_RECIP_ENDPOINT:
+ value = audio_set_endpoint_req(f, ctrl);
+ break;
+
+ case USB_DIR_IN | USB_TYPE_CLASS | USB_RECIP_ENDPOINT:
+ value = audio_get_endpoint_req(f, ctrl);
+ break;
+
default:
ERROR(cdev, "invalid control req%02x.%02x v%04x i%04x l%d\n",
ctrl->bRequestType, ctrl->bRequest,
diff --git a/drivers/usb/gadget/f_mass_storage.c b/drivers/usb/gadget/f_mass_storage.c
new file mode 100644
index 0000000..a37640e
--- /dev/null
+++ b/drivers/usb/gadget/f_mass_storage.c
@@ -0,0 +1,3091 @@
+/*
+ * f_mass_storage.c -- Mass Storage USB Composite Function
+ *
+ * Copyright (C) 2003-2008 Alan Stern
+ * Copyright (C) 2009 Samsung Electronics
+ * Author: Michal Nazarewicz <m.nazarewicz@samsung.com>
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions, and the following disclaimer,
+ * without modification.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. The names of the above-listed copyright holders may not be used
+ * to endorse or promote products derived from this software without
+ * specific prior written permission.
+ *
+ * ALTERNATIVELY, this software may be distributed under the terms of the
+ * GNU General Public License ("GPL") as published by the Free Software
+ * Foundation, either version 2 of that License or (at your option) any
+ * later version.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS
+ * IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
+ * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+ * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
+ * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+ * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+
+/*
+ * The 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
+ * addition to providing an example of a genuinely useful composite
+ * function for a USB device, it also illustrates a technique of
+ * double-buffering for increased throughput.
+ *
+ * Function supports multiple logical units (LUNs). Backing storage
+ * for each LUN is provided by a regular file or a block device.
+ * Access for each LUN can be limited to read-only. Moreover, the
+ * function can indicate that LUN is removable and/or CD-ROM. (The
+ * later implies read-only access.)
+ *
+ * MSF is configured by specifying a fsg_config structure. It has the
+ * following fields:
+ *
+ * nluns Number of LUNs function have (anywhere from 1
+ * to FSG_MAX_LUNS which is 8).
+ * luns An array of LUN configuration values. This
+ * should be filled for each LUN that
+ * function will include (ie. for "nluns"
+ * LUNs). Each element of the array has
+ * the following fields:
+ * ->filename The path to the backing file for the LUN.
+ * Required if LUN is not marked as
+ * removable.
+ * ->ro Flag specifying access to the LUN shall be
+ * read-only. This is implied if CD-ROM
+ * emulation is enabled as well as when
+ * it was impossible to open "filename"
+ * in R/W mode.
+ * ->removable Flag specifying that LUN shall be indicated as
+ * being removable.
+ * ->cdrom Flag specifying that LUN shall be reported as
+ * being a CD-ROM.
+ *
+ * lun_name_format A printf-like format for names of the LUN
+ * devices. This determines how the
+ * directory in sysfs will be named.
+ * Unless you are using several MSFs in
+ * a single gadget (as opposed to single
+ * MSF in many configurations) you may
+ * leave it as NULL (in which case
+ * "lun%d" will be used). In the format
+ * you can use "%d" to index LUNs for
+ * MSF's with more than one LUN. (Beware
+ * that there is only one integer given
+ * as an argument for the format and
+ * specifying invalid format may cause
+ * unspecified behaviour.)
+ * thread_name Name of the kernel thread process used by the
+ * MSF. You can safely set it to NULL
+ * (in which case default "file-storage"
+ * will be used).
+ *
+ * vendor_name
+ * product_name
+ * release Information used as a reply to INQUIRY
+ * request. To use default set to NULL,
+ * NULL, 0xffff respectively. The first
+ * field should be 8 and the second 16
+ * characters or less.
+ *
+ * can_stall Set to permit function to halt bulk endpoints.
+ * Disabled on some USB devices known not
+ * to work correctly. You should set it
+ * to true.
+ *
+ * If "removable" is not set for a LUN then a backing file must be
+ * specified. If it is set, then NULL filename means the LUN's medium
+ * is not loaded (an empty string as "filename" in the fsg_config
+ * structure causes error). The CD-ROM emulation includes a single
+ * data track and no audio tracks; hence there need be only one
+ * backing file per LUN. Note also that the CD-ROM block length is
+ * set to 512 rather than the more common value 2048.
+ *
+ *
+ * MSF includes support for module parameters. If gadget using it
+ * decides to use it, the following module parameters will be
+ * available:
+ *
+ * file=filename[,filename...]
+ * Names of the files or block devices used for
+ * backing storage.
+ * ro=b[,b...] Default false, boolean for read-only access.
+ * removable=b[,b...]
+ * Default true, boolean for removable media.
+ * cdrom=b[,b...] Default false, boolean for whether to emulate
+ * a CD-ROM drive.
+ * luns=N Default N = number of filenames, number of
+ * LUNs to support.
+ * stall Default determined according to the type of
+ * USB device controller (usually true),
+ * boolean to permit the driver to halt
+ * bulk endpoints.
+ *
+ * The module parameters may be prefixed with some string. You need
+ * to consult gadget's documentation or source to verify whether it is
+ * using those module parameters and if it does what are the prefixes
+ * (look for FSG_MODULE_PARAMETERS() macro usage, what's inside it is
+ * the prefix).
+ *
+ *
+ * Requirements are modest; only a bulk-in and a bulk-out endpoint are
+ * needed. The memory requirement amounts to two 16K buffers, size
+ * configurable by a parameter. Support is included for both
+ * full-speed and high-speed operation.
+ *
+ * Note that the driver is slightly non-portable in that it assumes a
+ * single memory/DMA buffer will be useable for bulk-in, bulk-out, and
+ * interrupt-in endpoints. With most device controllers this isn't an
+ * issue, but there may be some with hardware restrictions that prevent
+ * a buffer from being used by more than one endpoint.
+ *
+ *
+ * The pathnames of the backing files and the ro settings are
+ * available in the attribute files "file" and "ro" in the lun<n> (or
+ * to be more precise in a directory which name comes from
+ * "lun_name_format" option!) subdirectory of the gadget's sysfs
+ * directory. If the "removable" option is set, writing to these
+ * files will simulate ejecting/loading the medium (writing an empty
+ * line means eject) and adjusting a write-enable tab. Changes to the
+ * ro setting are not allowed when the medium is loaded or if CD-ROM
+ * emulation is being used.
+ *
+ *
+ * This function is heavily based on "File-backed Storage Gadget" by
+ * Alan Stern which in turn is heavily based on "Gadget Zero" by David
+ * Brownell. The driver's SCSI command interface was based on the
+ * "Information technology - Small Computer System Interface - 2"
+ * document from X3T9.2 Project 375D, Revision 10L, 7-SEP-93,
+ * available at <http://www.t10.org/ftp/t10/drafts/s2/s2-r10l.pdf>.
+ * The single exception is opcode 0x23 (READ FORMAT CAPACITIES), which
+ * was based on the "Universal Serial Bus Mass Storage Class UFI
+ * Command Specification" document, Revision 1.0, December 14, 1998,
+ * available at
+ * <http://www.usb.org/developers/devclass_docs/usbmass-ufi10.pdf>.
+ */
+
+
+/*
+ * Driver Design
+ *
+ * The MSF is fairly straightforward. There is a main kernel
+ * thread that handles most of the work. Interrupt routines field
+ * callbacks from the controller driver: bulk- and interrupt-request
+ * completion notifications, endpoint-0 events, and disconnect events.
+ * Completion events are passed to the main thread by wakeup calls. Many
+ * ep0 requests are handled at interrupt time, but SetInterface,
+ * SetConfiguration, and device reset requests are forwarded to the
+ * thread in the form of "exceptions" using SIGUSR1 signals (since they
+ * should interrupt any ongoing file I/O operations).
+ *
+ * The thread's main routine implements the standard command/data/status
+ * parts of a SCSI interaction. It and its subroutines are full of tests
+ * for pending signals/exceptions -- all this polling is necessary since
+ * the kernel has no setjmp/longjmp equivalents. (Maybe this is an
+ * indication that the driver really wants to be running in userspace.)
+ * An important point is that so long as the thread is alive it keeps an
+ * open reference to the backing file. This will prevent unmounting
+ * the backing file's underlying filesystem and could cause problems
+ * during system shutdown, for example. To prevent such problems, the
+ * thread catches INT, TERM, and KILL signals and converts them into
+ * an EXIT exception.
+ *
+ * In normal operation the main thread is started during the gadget's
+ * fsg_bind() callback and stopped during fsg_unbind(). But it can
+ * also exit when it receives a signal, and there's no point leaving
+ * the gadget running when the thread is dead. At of this moment, MSF
+ * provides no way to deregister the gadget when thread dies -- maybe
+ * a callback functions is needed.
+ *
+ * To provide maximum throughput, the driver uses a circular pipeline of
+ * buffer heads (struct fsg_buffhd). In principle the pipeline can be
+ * arbitrarily long; in practice the benefits don't justify having more
+ * than 2 stages (i.e., double buffering). But it helps to think of the
+ * pipeline as being a long one. Each buffer head contains a bulk-in and
+ * a bulk-out request pointer (since the buffer can be used for both
+ * output and input -- directions always are given from the host's
+ * point of view) as well as a pointer to the buffer and various state
+ * variables.
+ *
+ * Use of the pipeline follows a simple protocol. There is a variable
+ * (fsg->next_buffhd_to_fill) that points to the next buffer head to use.
+ * At any time that buffer head may still be in use from an earlier
+ * request, so each buffer head has a state variable indicating whether
+ * it is EMPTY, FULL, or BUSY. Typical use involves waiting for the
+ * buffer head to be EMPTY, filling the buffer either by file I/O or by
+ * USB I/O (during which the buffer head is BUSY), and marking the buffer
+ * head FULL when the I/O is complete. Then the buffer will be emptied
+ * (again possibly by USB I/O, during which it is marked BUSY) and
+ * finally marked EMPTY again (possibly by a completion routine).
+ *
+ * A module parameter tells the driver to avoid stalling the bulk
+ * endpoints wherever the transport specification allows. This is
+ * necessary for some UDCs like the SuperH, which cannot reliably clear a
+ * halt on a bulk endpoint. However, under certain circumstances the
+ * Bulk-only specification requires a stall. In such cases the driver
+ * will halt the endpoint and set a flag indicating that it should clear
+ * the halt in software during the next device reset. Hopefully this
+ * will permit everything to work correctly. Furthermore, although the
+ * specification allows the bulk-out endpoint to halt when the host sends
+ * too much data, implementing this would cause an unavoidable race.
+ * The driver will always use the "no-stall" approach for OUT transfers.
+ *
+ * One subtle point concerns sending status-stage responses for ep0
+ * requests. Some of these requests, such as device reset, can involve
+ * interrupting an ongoing file I/O operation, which might take an
+ * arbitrarily long time. During that delay the host might give up on
+ * the original ep0 request and issue a new one. When that happens the
+ * driver should not notify the host about completion of the original
+ * request, as the host will no longer be waiting for it. So the driver
+ * assigns to each ep0 request a unique tag, and it keeps track of the
+ * tag value of the request associated with a long-running exception
+ * (device-reset, interface-change, or configuration-change). When the
+ * exception handler is finished, the status-stage response is submitted
+ * only if the current ep0 request tag is equal to the exception request
+ * tag. Thus only the most recently received ep0 request will get a
+ * status-stage response.
+ *
+ * Warning: This driver source file is too long. It ought to be split up
+ * into a header file plus about 3 separate .c files, to handle the details
+ * of the Gadget, USB Mass Storage, and SCSI protocols.
+ */
+
+
+/* #define VERBOSE_DEBUG */
+/* #define DUMP_MSGS */
+
+
+#include <linux/blkdev.h>
+#include <linux/completion.h>
+#include <linux/dcache.h>
+#include <linux/delay.h>
+#include <linux/device.h>
+#include <linux/fcntl.h>
+#include <linux/file.h>
+#include <linux/fs.h>
+#include <linux/kref.h>
+#include <linux/kthread.h>
+#include <linux/limits.h>
+#include <linux/rwsem.h>
+#include <linux/slab.h>
+#include <linux/spinlock.h>
+#include <linux/string.h>
+#include <linux/freezer.h>
+#include <linux/utsname.h>
+
+#include <linux/usb/ch9.h>
+#include <linux/usb/gadget.h>
+
+#include "gadget_chips.h"
+
+
+
+/*------------------------------------------------------------------------*/
+
+#define FSG_DRIVER_DESC "Mass Storage Function"
+#define FSG_DRIVER_VERSION "2009/09/11"
+
+static const char fsg_string_interface[] = "Mass Storage";
+
+
+#define FSG_NO_INTR_EP 1
+#define FSG_BUFFHD_STATIC_BUFFER 1
+#define FSG_NO_DEVICE_STRINGS 1
+#define FSG_NO_OTG 1
+#define FSG_NO_INTR_EP 1
+
+#include "storage_common.c"
+
+
+/*-------------------------------------------------------------------------*/
+
+struct fsg_dev;
+
+
+/* Data shared by all the FSG instances. */
+struct fsg_common {
+ struct usb_gadget *gadget;
+ struct fsg_dev *fsg;
+ struct fsg_dev *prev_fsg;
+
+ /* filesem protects: backing files in use */
+ struct rw_semaphore filesem;
+
+ /* lock protects: state, all the req_busy's */
+ spinlock_t lock;
+
+ struct usb_ep *ep0; /* Copy of gadget->ep0 */
+ struct usb_request *ep0req; /* Copy of cdev->req */
+ unsigned int ep0_req_tag;
+ const char *ep0req_name;
+
+ struct fsg_buffhd *next_buffhd_to_fill;
+ struct fsg_buffhd *next_buffhd_to_drain;
+ struct fsg_buffhd buffhds[FSG_NUM_BUFFERS];
+
+ int cmnd_size;
+ u8 cmnd[MAX_COMMAND_SIZE];
+
+ unsigned int nluns;
+ unsigned int lun;
+ struct fsg_lun *luns;
+ struct fsg_lun *curlun;
+
+ unsigned int bulk_out_maxpacket;
+ enum fsg_state state; /* For exception handling */
+ unsigned int exception_req_tag;
+
+ u8 config, new_config;
+ enum data_direction data_dir;
+ u32 data_size;
+ u32 data_size_from_cmnd;
+ u32 tag;
+ u32 residue;
+ u32 usb_amount_left;
+
+ unsigned int can_stall:1;
+ unsigned int free_storage_on_release:1;
+ unsigned int phase_error:1;
+ unsigned int short_packet_received:1;
+ unsigned int bad_lun_okay:1;
+ unsigned int running:1;
+
+ int thread_wakeup_needed;
+ struct completion thread_notifier;
+ struct task_struct *thread_task;
+
+ /* Callback function to call when thread exits. */
+ void (*thread_exits)(struct fsg_common *common);
+ /* Gadget's private data. */
+ void *private_data;
+
+ /* 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 {
+ const char *filename;
+ char ro;
+ char removable;
+ char cdrom;
+ } luns[FSG_MAX_LUNS];
+
+ const char *lun_name_format;
+ const char *thread_name;
+
+ /* Callback function to call when thread exits. */
+ void (*thread_exits)(struct fsg_common *common);
+ /* Gadget's private data. */
+ void *private_data;
+
+ const char *vendor_name; /* 8 characters or less */
+ const char *product_name; /* 16 characters or less */
+ u16 release;
+
+ char can_stall;
+};
+
+
+struct fsg_dev {
+ struct usb_function function;
+ struct usb_gadget *gadget; /* Copy of cdev->gadget */
+ struct fsg_common *common;
+
+ u16 interface_number;
+
+ unsigned int bulk_in_enabled:1;
+ unsigned int bulk_out_enabled:1;
+
+ unsigned long atomic_bitflags;
+#define IGNORE_BULK_OUT 0
+
+ struct usb_ep *bulk_in;
+ struct usb_ep *bulk_out;
+};
+
+
+static inline int __fsg_is_set(struct fsg_common *common,
+ const char *func, unsigned line)
+{
+ if (common->fsg)
+ return 1;
+ ERROR(common, "common->fsg is NULL in %s at %u\n", func, line);
+ return 0;
+}
+
+#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)
+{
+ return common->state > FSG_STATE_IDLE;
+}
+
+/* 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)
+{
+ unsigned int rem;
+
+ bh->bulk_out_intended_length = length;
+ rem = length % common->bulk_out_maxpacket;
+ if (rem > 0)
+ length += common->bulk_out_maxpacket - rem;
+ bh->outreq->length = length;
+}
+
+/*-------------------------------------------------------------------------*/
+
+static int fsg_set_halt(struct fsg_dev *fsg, struct usb_ep *ep)
+{
+ const char *name;
+
+ if (ep == fsg->bulk_in)
+ name = "bulk-in";
+ else if (ep == fsg->bulk_out)
+ name = "bulk-out";
+ else
+ name = ep->name;
+ DBG(fsg, "%s set halt\n", name);
+ return usb_ep_set_halt(ep);
+}
+
+
+/*-------------------------------------------------------------------------*/
+
+/* These routines may be called in process context or in_irq */
+
+/* Caller must hold fsg->lock */
+static void wakeup_thread(struct fsg_common *common)
+{
+ /* Tell the main thread that something has happened */
+ common->thread_wakeup_needed = 1;
+ if (common->thread_task)
+ 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.
+ * If a lower-or-equal priority exception is in progress, preempt it
+ * and notify the main thread by sending it a signal. */
+ spin_lock_irqsave(&common->lock, flags);
+ if (common->state <= new_state) {
+ common->exception_req_tag = common->ep0_req_tag;
+ common->state = new_state;
+ if (common->thread_task)
+ send_sig_info(SIGUSR1, SEND_SIG_FORCED,
+ common->thread_task);
+ }
+ spin_unlock_irqrestore(&common->lock, flags);
+}
+
+
+/*-------------------------------------------------------------------------*/
+
+static int ep0_queue(struct fsg_common *common)
+{
+ int rc;
+
+ rc = usb_ep_queue(common->ep0, common->ep0req, GFP_ATOMIC);
+ common->ep0->driver_data = common;
+ if (rc != 0 && rc != -ESHUTDOWN) {
+ /* We can't do much more than wait for a reset */
+ WARNING(common, "error in submission: %s --> %d\n",
+ common->ep0->name, rc);
+ }
+ return rc;
+}
+
+/*-------------------------------------------------------------------------*/
+
+/* Bulk and interrupt endpoint completion handlers.
+ * These always run in_irq. */
+
+static void bulk_in_complete(struct usb_ep *ep, struct usb_request *req)
+{
+ struct fsg_common *common = ep->driver_data;
+ struct fsg_buffhd *bh = req->context;
+
+ if (req->status || req->actual != req->length)
+ DBG(common, "%s --> %d, %u/%u\n", __func__,
+ req->status, req->actual, req->length);
+ if (req->status == -ECONNRESET) /* Request was cancelled */
+ usb_ep_fifo_flush(ep);
+
+ /* Hold the lock while we update the request and buffer states */
+ smp_wmb();
+ spin_lock(&common->lock);
+ bh->inreq_busy = 0;
+ bh->state = BUF_STATE_EMPTY;
+ wakeup_thread(common);
+ spin_unlock(&common->lock);
+}
+
+static void bulk_out_complete(struct usb_ep *ep, struct usb_request *req)
+{
+ struct fsg_common *common = ep->driver_data;
+ struct fsg_buffhd *bh = req->context;
+
+ 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);
+ if (req->status == -ECONNRESET) /* Request was cancelled */
+ usb_ep_fifo_flush(ep);
+
+ /* Hold the lock while we update the request and buffer states */
+ smp_wmb();
+ spin_lock(&common->lock);
+ bh->outreq_busy = 0;
+ bh->state = BUF_STATE_FULL;
+ wakeup_thread(common);
+ 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)
+{
+ struct fsg_dev *fsg = fsg_from_func(f);
+ struct usb_request *req = fsg->common->ep0req;
+ u16 w_index = le16_to_cpu(ctrl->wIndex);
+ u16 w_value = le16_to_cpu(ctrl->wValue);
+ u16 w_length = le16_to_cpu(ctrl->wLength);
+
+ if (!fsg->common->config)
+ return -EOPNOTSUPP;
+
+ switch (ctrl->bRequest) {
+
+ case USB_BULK_RESET_REQUEST:
+ if (ctrl->bRequestType !=
+ (USB_DIR_OUT | USB_TYPE_CLASS | USB_RECIP_INTERFACE))
+ break;
+ if (w_index != fsg->interface_number || w_value != 0)
+ return -EDOM;
+
+ /* 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;
+
+ case USB_BULK_GET_MAX_LUN_REQUEST:
+ if (ctrl->bRequestType !=
+ (USB_DIR_IN | USB_TYPE_CLASS | USB_RECIP_INTERFACE))
+ break;
+ if (w_index != fsg->interface_number || w_value != 0)
+ return -EDOM;
+ VDBG(fsg, "get max LUN\n");
+ *(u8 *) req->buf = fsg->common->nluns - 1;
+ return 1;
+ }
+
+ VDBG(fsg,
+ "unknown class-specific control req "
+ "%02x.%02x v%04x i%04x l%u\n",
+ ctrl->bRequestType, ctrl->bRequest,
+ le16_to_cpu(ctrl->wValue), w_index, w_length);
+ return -EOPNOTSUPP;
+}
+
+
+/*-------------------------------------------------------------------------*/
+
+/* All the following routines run in process context */
+
+
+/* Use this for bulk or interrupt transfers, not ep0 */
+static void start_transfer(struct fsg_dev *fsg, struct usb_ep *ep,
+ struct usb_request *req, int *pbusy,
+ enum fsg_buffer_state *state)
+{
+ int rc;
+
+ if (ep == fsg->bulk_in)
+ dump_msg(fsg, "bulk-in", req->buf, req->length);
+
+ spin_lock_irq(&fsg->common->lock);
+ *pbusy = 1;
+ *state = BUF_STATE_BUSY;
+ spin_unlock_irq(&fsg->common->lock);
+ rc = usb_ep_queue(ep, req, GFP_KERNEL);
+ if (rc != 0) {
+ *pbusy = 0;
+ *state = BUF_STATE_EMPTY;
+
+ /* We can't do much more than wait for a reset */
+
+ /* Note: currently the net2280 driver fails zero-length
+ * submissions if DMA is enabled. */
+ if (rc != -ESHUTDOWN && !(rc == -EOPNOTSUPP &&
+ req->length == 0))
+ WARNING(fsg, "error in submission: %s --> %d\n",
+ ep->name, rc);
+ }
+}
+
+#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 int sleep_thread(struct fsg_common *common)
+{
+ int rc = 0;
+
+ /* Wait until a signal arrives or we are woken up */
+ for (;;) {
+ try_to_freeze();
+ set_current_state(TASK_INTERRUPTIBLE);
+ if (signal_pending(current)) {
+ rc = -EINTR;
+ break;
+ }
+ if (common->thread_wakeup_needed)
+ break;
+ schedule();
+ }
+ __set_current_state(TASK_RUNNING);
+ common->thread_wakeup_needed = 0;
+ return rc;
+}
+
+
+/*-------------------------------------------------------------------------*/
+
+static int do_read(struct fsg_common *common)
+{
+ struct fsg_lun *curlun = common->curlun;
+ u32 lba;
+ struct fsg_buffhd *bh;
+ int rc;
+ u32 amount_left;
+ loff_t file_offset, file_offset_tmp;
+ unsigned int amount;
+ unsigned int partial_page;
+ ssize_t nread;
+
+ /* Get the starting Logical Block Address and check that it's
+ * not too big */
+ if (common->cmnd[0] == SC_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
+ * cache) and FUA (Force Unit Access = don't read from the
+ * cache), but we don't implement them. */
+ if ((common->cmnd[1] & ~0x18) != 0) {
+ curlun->sense_data = SS_INVALID_FIELD_IN_CDB;
+ return -EINVAL;
+ }
+ }
+ if (lba >= curlun->num_sectors) {
+ curlun->sense_data = SS_LOGICAL_BLOCK_ADDRESS_OUT_OF_RANGE;
+ return -EINVAL;
+ }
+ file_offset = ((loff_t) lba) << 9;
+
+ /* Carry out the file reads */
+ amount_left = common->data_size_from_cmnd;
+ if (unlikely(amount_left == 0))
+ return -EIO; /* No default reply */
+
+ for (;;) {
+
+ /* Figure out how much we need to read:
+ * Try to read the remaining amount.
+ * But don't read more than the buffer size.
+ * And don't try to read past the end of the file.
+ * Finally, if we're not at a page boundary, don't read past
+ * the next page.
+ * If this means reading 0 then we were asked to read past
+ * the end of file. */
+ amount = min(amount_left, FSG_BUFLEN);
+ amount = min((loff_t) amount,
+ curlun->file_length - file_offset);
+ partial_page = file_offset & (PAGE_CACHE_SIZE - 1);
+ if (partial_page > 0)
+ amount = min(amount, (unsigned int) PAGE_CACHE_SIZE -
+ partial_page);
+
+ /* Wait for the next buffer to become available */
+ bh = common->next_buffhd_to_fill;
+ while (bh->state != BUF_STATE_EMPTY) {
+ rc = sleep_thread(common);
+ if (rc)
+ return rc;
+ }
+
+ /* If we were asked to read past the end of file,
+ * end with an empty buffer. */
+ if (amount == 0) {
+ curlun->sense_data =
+ SS_LOGICAL_BLOCK_ADDRESS_OUT_OF_RANGE;
+ curlun->sense_data_info = file_offset >> 9;
+ curlun->info_valid = 1;
+ bh->inreq->length = 0;
+ bh->state = BUF_STATE_FULL;
+ break;
+ }
+
+ /* Perform the read */
+ file_offset_tmp = file_offset;
+ nread = vfs_read(curlun->filp,
+ (char __user *) bh->buf,
+ amount, &file_offset_tmp);
+ VLDBG(curlun, "file read %u @ %llu -> %d\n", amount,
+ (unsigned long long) file_offset,
+ (int) nread);
+ if (signal_pending(current))
+ return -EINTR;
+
+ if (nread < 0) {
+ LDBG(curlun, "error in file read: %d\n",
+ (int) nread);
+ nread = 0;
+ } else if (nread < amount) {
+ LDBG(curlun, "partial file read: %d/%u\n",
+ (int) nread, amount);
+ nread -= (nread & 511); /* Round down to a block */
+ }
+ file_offset += nread;
+ amount_left -= nread;
+ common->residue -= nread;
+ bh->inreq->length = nread;
+ bh->state = BUF_STATE_FULL;
+
+ /* If an error occurred, report it and its position */
+ if (nread < amount) {
+ curlun->sense_data = SS_UNRECOVERED_READ_ERROR;
+ curlun->sense_data_info = file_offset >> 9;
+ curlun->info_valid = 1;
+ break;
+ }
+
+ if (amount_left == 0)
+ break; /* No more left to read */
+
+ /* Send this buffer and go read some more */
+ bh->inreq->zero = 0;
+ START_TRANSFER_OR(common, bulk_in, bh->inreq,
+ &bh->inreq_busy, &bh->state)
+ /* Don't know what to do if
+ * common->fsg is NULL */
+ return -EIO;
+ common->next_buffhd_to_fill = bh->next;
+ }
+
+ return -EIO; /* No default reply */
+}
+
+
+/*-------------------------------------------------------------------------*/
+
+static int do_write(struct fsg_common *common)
+{
+ struct fsg_lun *curlun = common->curlun;
+ u32 lba;
+ struct fsg_buffhd *bh;
+ int get_some_more;
+ u32 amount_left_to_req, amount_left_to_write;
+ loff_t usb_offset, file_offset, file_offset_tmp;
+ unsigned int amount;
+ unsigned int partial_page;
+ ssize_t nwritten;
+ int rc;
+
+ if (curlun->ro) {
+ curlun->sense_data = SS_WRITE_PROTECTED;
+ return -EINVAL;
+ }
+ spin_lock(&curlun->filp->f_lock);
+ 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 */
+ if (common->cmnd[0] == SC_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
+ * cache) and FUA (Force Unit Access = write directly to the
+ * medium). We don't implement DPO; we implement FUA by
+ * performing synchronous output. */
+ if (common->cmnd[1] & ~0x18) {
+ curlun->sense_data = SS_INVALID_FIELD_IN_CDB;
+ return -EINVAL;
+ }
+ if (common->cmnd[1] & 0x08) { /* FUA */
+ spin_lock(&curlun->filp->f_lock);
+ curlun->filp->f_flags |= O_SYNC;
+ spin_unlock(&curlun->filp->f_lock);
+ }
+ }
+ if (lba >= curlun->num_sectors) {
+ curlun->sense_data = SS_LOGICAL_BLOCK_ADDRESS_OUT_OF_RANGE;
+ return -EINVAL;
+ }
+
+ /* Carry out the file writes */
+ get_some_more = 1;
+ file_offset = usb_offset = ((loff_t) lba) << 9;
+ amount_left_to_req = common->data_size_from_cmnd;
+ amount_left_to_write = common->data_size_from_cmnd;
+
+ while (amount_left_to_write > 0) {
+
+ /* Queue a request for more data from the host */
+ bh = common->next_buffhd_to_fill;
+ if (bh->state == BUF_STATE_EMPTY && get_some_more) {
+
+ /* Figure out how much we want to get:
+ * Try to get the remaining amount.
+ * But don't get more than the buffer size.
+ * And don't try to go past the end of the file.
+ * If we're not at a page boundary,
+ * don't go past the next page.
+ * If this means getting 0, then we were asked
+ * to write past the end of file.
+ * Finally, round down to a block boundary. */
+ amount = min(amount_left_to_req, FSG_BUFLEN);
+ amount = min((loff_t) amount, curlun->file_length -
+ usb_offset);
+ partial_page = usb_offset & (PAGE_CACHE_SIZE - 1);
+ if (partial_page > 0)
+ amount = min(amount,
+ (unsigned int) PAGE_CACHE_SIZE - partial_page);
+
+ if (amount == 0) {
+ get_some_more = 0;
+ curlun->sense_data =
+ SS_LOGICAL_BLOCK_ADDRESS_OUT_OF_RANGE;
+ curlun->sense_data_info = usb_offset >> 9;
+ curlun->info_valid = 1;
+ continue;
+ }
+ amount -= (amount & 511);
+ if (amount == 0) {
+
+ /* Why were we were asked to transfer a
+ * partial block? */
+ get_some_more = 0;
+ continue;
+ }
+
+ /* Get the next buffer */
+ usb_offset += amount;
+ common->usb_amount_left -= amount;
+ amount_left_to_req -= amount;
+ if (amount_left_to_req == 0)
+ get_some_more = 0;
+
+ /* amount is always divisible by 512, hence by
+ * the bulk-out maxpacket size */
+ bh->outreq->length = 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 */
+ return -EIO;
+ common->next_buffhd_to_fill = bh->next;
+ continue;
+ }
+
+ /* Write the received data to the backing file */
+ bh = common->next_buffhd_to_drain;
+ if (bh->state == BUF_STATE_EMPTY && !get_some_more)
+ break; /* We stopped early */
+ if (bh->state == BUF_STATE_FULL) {
+ smp_rmb();
+ common->next_buffhd_to_drain = bh->next;
+ bh->state = BUF_STATE_EMPTY;
+
+ /* Did something go wrong with the transfer? */
+ if (bh->outreq->status != 0) {
+ curlun->sense_data = SS_COMMUNICATION_FAILURE;
+ curlun->sense_data_info = file_offset >> 9;
+ curlun->info_valid = 1;
+ break;
+ }
+
+ amount = bh->outreq->actual;
+ if (curlun->file_length - file_offset < amount) {
+ LERROR(curlun,
+ "write %u @ %llu beyond end %llu\n",
+ amount, (unsigned long long) file_offset,
+ (unsigned long long) curlun->file_length);
+ amount = curlun->file_length - file_offset;
+ }
+
+ /* Perform the write */
+ file_offset_tmp = file_offset;
+ nwritten = vfs_write(curlun->filp,
+ (char __user *) bh->buf,
+ amount, &file_offset_tmp);
+ VLDBG(curlun, "file write %u @ %llu -> %d\n", amount,
+ (unsigned long long) file_offset,
+ (int) nwritten);
+ if (signal_pending(current))
+ return -EINTR; /* Interrupted! */
+
+ if (nwritten < 0) {
+ LDBG(curlun, "error in file write: %d\n",
+ (int) nwritten);
+ nwritten = 0;
+ } else if (nwritten < amount) {
+ LDBG(curlun, "partial file write: %d/%u\n",
+ (int) nwritten, amount);
+ nwritten -= (nwritten & 511);
+ /* Round down to a block */
+ }
+ file_offset += nwritten;
+ amount_left_to_write -= nwritten;
+ common->residue -= nwritten;
+
+ /* If an error occurred, report it and its position */
+ if (nwritten < amount) {
+ curlun->sense_data = SS_WRITE_ERROR;
+ curlun->sense_data_info = file_offset >> 9;
+ curlun->info_valid = 1;
+ break;
+ }
+
+ /* Did the host decide to stop early? */
+ if (bh->outreq->actual != bh->outreq->length) {
+ common->short_packet_received = 1;
+ break;
+ }
+ continue;
+ }
+
+ /* Wait for something to happen */
+ rc = sleep_thread(common);
+ if (rc)
+ return rc;
+ }
+
+ return -EIO; /* No default reply */
+}
+
+
+/*-------------------------------------------------------------------------*/
+
+static int do_synchronize_cache(struct fsg_common *common)
+{
+ struct fsg_lun *curlun = common->curlun;
+ int rc;
+
+ /* We ignore the requested LBA and write out all file's
+ * dirty data buffers. */
+ rc = fsg_lun_fsync_sub(curlun);
+ if (rc)
+ curlun->sense_data = SS_WRITE_ERROR;
+ return 0;
+}
+
+
+/*-------------------------------------------------------------------------*/
+
+static void invalidate_sub(struct fsg_lun *curlun)
+{
+ struct file *filp = curlun->filp;
+ struct inode *inode = filp->f_path.dentry->d_inode;
+ unsigned long rc;
+
+ rc = invalidate_mapping_pages(inode->i_mapping, 0, -1);
+ VLDBG(curlun, "invalidate_inode_pages -> %ld\n", rc);
+}
+
+static int do_verify(struct fsg_common *common)
+{
+ struct fsg_lun *curlun = common->curlun;
+ u32 lba;
+ u32 verification_length;
+ struct fsg_buffhd *bh = common->next_buffhd_to_fill;
+ loff_t file_offset, file_offset_tmp;
+ u32 amount_left;
+ unsigned int amount;
+ ssize_t nread;
+
+ /* 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. */
+ if (common->cmnd[1] & ~0x10) {
+ curlun->sense_data = SS_INVALID_FIELD_IN_CDB;
+ return -EINVAL;
+ }
+
+ verification_length = get_unaligned_be16(&common->cmnd[7]);
+ if (unlikely(verification_length == 0))
+ return -EIO; /* No default reply */
+
+ /* Prepare to carry out the file verify */
+ amount_left = verification_length << 9;
+ file_offset = ((loff_t) lba) << 9;
+
+ /* Write out all the dirty buffers before invalidating them */
+ fsg_lun_fsync_sub(curlun);
+ if (signal_pending(current))
+ return -EINTR;
+
+ invalidate_sub(curlun);
+ if (signal_pending(current))
+ return -EINTR;
+
+ /* Just try to read the requested blocks */
+ while (amount_left > 0) {
+
+ /* Figure out how much we need to read:
+ * Try to read the remaining amount, but not more than
+ * the buffer size.
+ * And don't try to read past the end of the file.
+ * If this means reading 0 then we were asked to read
+ * past the end of file. */
+ amount = min(amount_left, FSG_BUFLEN);
+ amount = min((loff_t) amount,
+ curlun->file_length - file_offset);
+ if (amount == 0) {
+ curlun->sense_data =
+ SS_LOGICAL_BLOCK_ADDRESS_OUT_OF_RANGE;
+ curlun->sense_data_info = file_offset >> 9;
+ curlun->info_valid = 1;
+ break;
+ }
+
+ /* Perform the read */
+ file_offset_tmp = file_offset;
+ nread = vfs_read(curlun->filp,
+ (char __user *) bh->buf,
+ amount, &file_offset_tmp);
+ VLDBG(curlun, "file read %u @ %llu -> %d\n", amount,
+ (unsigned long long) file_offset,
+ (int) nread);
+ if (signal_pending(current))
+ return -EINTR;
+
+ if (nread < 0) {
+ LDBG(curlun, "error in file verify: %d\n",
+ (int) nread);
+ nread = 0;
+ } else if (nread < amount) {
+ LDBG(curlun, "partial file verify: %d/%u\n",
+ (int) nread, amount);
+ nread -= (nread & 511); /* Round down to a sector */
+ }
+ if (nread == 0) {
+ curlun->sense_data = SS_UNRECOVERED_READ_ERROR;
+ curlun->sense_data_info = file_offset >> 9;
+ curlun->info_valid = 1;
+ break;
+ }
+ file_offset += nread;
+ amount_left -= nread;
+ }
+ return 0;
+}
+
+
+/*-------------------------------------------------------------------------*/
+
+static int do_inquiry(struct fsg_common *common, struct fsg_buffhd *bh)
+{
+ struct fsg_lun *curlun = common->curlun;
+ u8 *buf = (u8 *) bh->buf;
+
+ if (!curlun) { /* Unsupported LUNs are okay */
+ common->bad_lun_okay = 1;
+ memset(buf, 0, 36);
+ buf[0] = 0x7f; /* Unsupported, no device-type */
+ buf[4] = 31; /* Additional length */
+ return 36;
+ }
+
+ buf[0] = curlun->cdrom ? TYPE_CDROM : TYPE_DISK;
+ buf[1] = curlun->removable ? 0x80 : 0;
+ buf[2] = 2; /* ANSI SCSI level 2 */
+ buf[3] = 2; /* SCSI-2 INQUIRY data format */
+ buf[4] = 31; /* Additional length */
+ buf[5] = 0; /* No special options */
+ buf[6] = 0;
+ buf[7] = 0;
+ memcpy(buf + 8, common->inquiry_string, sizeof common->inquiry_string);
+ return 36;
+}
+
+
+static int do_request_sense(struct fsg_common *common, struct fsg_buffhd *bh)
+{
+ struct fsg_lun *curlun = common->curlun;
+ u8 *buf = (u8 *) bh->buf;
+ u32 sd, sdinfo;
+ int valid;
+
+ /*
+ * From the SCSI-2 spec., section 7.9 (Unit attention condition):
+ *
+ * If a REQUEST SENSE command is received from an initiator
+ * with a pending unit attention condition (before the target
+ * generates the contingent allegiance condition), then the
+ * target shall either:
+ * a) report any pending sense data and preserve the unit
+ * attention condition on the logical unit, or,
+ * b) report the unit attention condition, may discard any
+ * pending sense data, and clear the unit attention
+ * condition on the logical unit for that initiator.
+ *
+ * FSG normally uses option a); enable this code to use option b).
+ */
+#if 0
+ if (curlun && curlun->unit_attention_data != SS_NO_SENSE) {
+ curlun->sense_data = curlun->unit_attention_data;
+ curlun->unit_attention_data = SS_NO_SENSE;
+ }
+#endif
+
+ if (!curlun) { /* Unsupported LUNs are okay */
+ common->bad_lun_okay = 1;
+ sd = SS_LOGICAL_UNIT_NOT_SUPPORTED;
+ sdinfo = 0;
+ valid = 0;
+ } else {
+ sd = curlun->sense_data;
+ sdinfo = curlun->sense_data_info;
+ valid = curlun->info_valid << 7;
+ curlun->sense_data = SS_NO_SENSE;
+ curlun->sense_data_info = 0;
+ curlun->info_valid = 0;
+ }
+
+ memset(buf, 0, 18);
+ buf[0] = valid | 0x70; /* Valid, current error */
+ buf[2] = SK(sd);
+ put_unaligned_be32(sdinfo, &buf[3]); /* Sense information */
+ buf[7] = 18 - 8; /* Additional sense length */
+ buf[12] = ASC(sd);
+ buf[13] = ASCQ(sd);
+ return 18;
+}
+
+
+static int do_read_capacity(struct fsg_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;
+
+ /* Check the PMI and LBA fields */
+ if (pmi > 1 || (pmi == 0 && lba != 0)) {
+ curlun->sense_data = SS_INVALID_FIELD_IN_CDB;
+ return -EINVAL;
+ }
+
+ put_unaligned_be32(curlun->num_sectors - 1, &buf[0]);
+ /* Max logical block */
+ put_unaligned_be32(512, &buf[4]); /* Block length */
+ return 8;
+}
+
+
+static int do_read_header(struct fsg_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;
+
+ if (common->cmnd[1] & ~0x02) { /* Mask away MSF */
+ curlun->sense_data = SS_INVALID_FIELD_IN_CDB;
+ return -EINVAL;
+ }
+ if (lba >= curlun->num_sectors) {
+ curlun->sense_data = SS_LOGICAL_BLOCK_ADDRESS_OUT_OF_RANGE;
+ return -EINVAL;
+ }
+
+ memset(buf, 0, 8);
+ buf[0] = 0x01; /* 2048 bytes of user data, rest is EC */
+ store_cdrom_address(&buf[4], msf, lba);
+ return 8;
+}
+
+
+static int do_read_toc(struct fsg_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;
+
+ if ((common->cmnd[1] & ~0x02) != 0 || /* Mask away MSF */
+ start_track > 1) {
+ curlun->sense_data = SS_INVALID_FIELD_IN_CDB;
+ return -EINVAL;
+ }
+
+ memset(buf, 0, 20);
+ buf[1] = (20-2); /* TOC data length */
+ buf[2] = 1; /* First track number */
+ buf[3] = 1; /* Last track number */
+ buf[5] = 0x16; /* Data track, copying allowed */
+ buf[6] = 0x01; /* Only track is number 1 */
+ store_cdrom_address(&buf[8], msf, 0);
+
+ buf[13] = 0x16; /* Lead-out track is data */
+ buf[14] = 0xAA; /* Lead-out track number */
+ store_cdrom_address(&buf[16], msf, curlun->num_sectors);
+ return 20;
+}
+
+
+static int do_mode_sense(struct fsg_common *common, struct fsg_buffhd *bh)
+{
+ struct fsg_lun *curlun = common->curlun;
+ int mscmnd = common->cmnd[0];
+ u8 *buf = (u8 *) bh->buf;
+ u8 *buf0 = buf;
+ int pc, page_code;
+ int changeable_values, all_pages;
+ int valid_page = 0;
+ int len, limit;
+
+ if ((common->cmnd[1] & ~0x08) != 0) { /* Mask away DBD */
+ curlun->sense_data = SS_INVALID_FIELD_IN_CDB;
+ return -EINVAL;
+ }
+ pc = common->cmnd[2] >> 6;
+ page_code = common->cmnd[2] & 0x3f;
+ if (pc == 3) {
+ curlun->sense_data = SS_SAVING_PARAMETERS_NOT_SUPPORTED;
+ return -EINVAL;
+ }
+ changeable_values = (pc == 1);
+ all_pages = (page_code == 0x3f);
+
+ /* Write the mode parameter header. Fixed values are: default
+ * medium type, no cache control (DPOFUA), and no block descriptors.
+ * The only variable value is the WriteProtect bit. We will fill in
+ * the mode data length later. */
+ memset(buf, 0, 8);
+ if (mscmnd == SC_MODE_SENSE_6) {
+ buf[2] = (curlun->ro ? 0x80 : 0x00); /* WP, DPOFUA */
+ buf += 4;
+ limit = 255;
+ } else { /* SC_MODE_SENSE_10 */
+ buf[3] = (curlun->ro ? 0x80 : 0x00); /* WP, DPOFUA */
+ buf += 8;
+ limit = 65535; /* Should really be FSG_BUFLEN */
+ }
+
+ /* No block descriptors */
+
+ /* The mode pages, in numerical order. The only page we support
+ * is the Caching page. */
+ if (page_code == 0x08 || all_pages) {
+ valid_page = 1;
+ buf[0] = 0x08; /* Page code */
+ buf[1] = 10; /* Page length */
+ memset(buf+2, 0, 10); /* None of the fields are changeable */
+
+ if (!changeable_values) {
+ buf[2] = 0x04; /* Write cache enable, */
+ /* Read cache not disabled */
+ /* No cache retention priorities */
+ put_unaligned_be16(0xffff, &buf[4]);
+ /* Don't disable prefetch */
+ /* Minimum prefetch = 0 */
+ put_unaligned_be16(0xffff, &buf[8]);
+ /* Maximum prefetch */
+ put_unaligned_be16(0xffff, &buf[10]);
+ /* Maximum prefetch ceiling */
+ }
+ buf += 12;
+ }
+
+ /* Check that a valid page was requested and the mode data length
+ * isn't too long. */
+ len = buf - buf0;
+ if (!valid_page || len > limit) {
+ curlun->sense_data = SS_INVALID_FIELD_IN_CDB;
+ return -EINVAL;
+ }
+
+ /* Store the mode data length */
+ if (mscmnd == SC_MODE_SENSE_6)
+ buf0[0] = len - 1;
+ else
+ put_unaligned_be16(len - 2, buf0);
+ return len;
+}
+
+
+static int do_start_stop(struct fsg_common *common)
+{
+ if (!common->curlun) {
+ return -EINVAL;
+ } else if (!common->curlun->removable) {
+ common->curlun->sense_data = SS_INVALID_COMMAND;
+ return -EINVAL;
+ }
+ return 0;
+}
+
+
+static int do_prevent_allow(struct fsg_common *common)
+{
+ struct fsg_lun *curlun = common->curlun;
+ int prevent;
+
+ if (!common->curlun) {
+ return -EINVAL;
+ } else if (!common->curlun->removable) {
+ common->curlun->sense_data = SS_INVALID_COMMAND;
+ return -EINVAL;
+ }
+
+ prevent = common->cmnd[4] & 0x01;
+ if ((common->cmnd[4] & ~0x01) != 0) { /* Mask away Prevent */
+ curlun->sense_data = SS_INVALID_FIELD_IN_CDB;
+ return -EINVAL;
+ }
+
+ if (curlun->prevent_medium_removal && !prevent)
+ fsg_lun_fsync_sub(curlun);
+ curlun->prevent_medium_removal = prevent;
+ return 0;
+}
+
+
+static int do_read_format_capacities(struct fsg_common *common,
+ struct fsg_buffhd *bh)
+{
+ struct fsg_lun *curlun = common->curlun;
+ u8 *buf = (u8 *) bh->buf;
+
+ buf[0] = buf[1] = buf[2] = 0;
+ buf[3] = 8; /* Only the Current/Maximum Capacity Descriptor */
+ buf += 4;
+
+ put_unaligned_be32(curlun->num_sectors, &buf[0]);
+ /* Number of blocks */
+ put_unaligned_be32(512, &buf[4]); /* Block length */
+ buf[4] = 0x02; /* Current capacity */
+ return 12;
+}
+
+
+static int do_mode_select(struct fsg_common *common, struct fsg_buffhd *bh)
+{
+ struct fsg_lun *curlun = common->curlun;
+
+ /* We don't support MODE SELECT */
+ if (curlun)
+ curlun->sense_data = SS_INVALID_COMMAND;
+ return -EINVAL;
+}
+
+
+/*-------------------------------------------------------------------------*/
+
+static int halt_bulk_in_endpoint(struct fsg_dev *fsg)
+{
+ int rc;
+
+ rc = fsg_set_halt(fsg, fsg->bulk_in);
+ if (rc == -EAGAIN)
+ VDBG(fsg, "delayed bulk-in endpoint halt\n");
+ while (rc != 0) {
+ if (rc != -EAGAIN) {
+ WARNING(fsg, "usb_ep_set_halt -> %d\n", rc);
+ rc = 0;
+ break;
+ }
+
+ /* Wait for a short time and then try again */
+ if (msleep_interruptible(100) != 0)
+ return -EINTR;
+ rc = usb_ep_set_halt(fsg->bulk_in);
+ }
+ return rc;
+}
+
+static int wedge_bulk_in_endpoint(struct fsg_dev *fsg)
+{
+ int rc;
+
+ DBG(fsg, "bulk-in set wedge\n");
+ rc = usb_ep_set_wedge(fsg->bulk_in);
+ if (rc == -EAGAIN)
+ VDBG(fsg, "delayed bulk-in endpoint wedge\n");
+ while (rc != 0) {
+ if (rc != -EAGAIN) {
+ WARNING(fsg, "usb_ep_set_wedge -> %d\n", rc);
+ rc = 0;
+ break;
+ }
+
+ /* Wait for a short time and then try again */
+ if (msleep_interruptible(100) != 0)
+ return -EINTR;
+ rc = usb_ep_set_wedge(fsg->bulk_in);
+ }
+ return rc;
+}
+
+static int pad_with_zeros(struct fsg_dev *fsg)
+{
+ struct fsg_buffhd *bh = fsg->common->next_buffhd_to_fill;
+ u32 nkeep = bh->inreq->length;
+ u32 nsend;
+ int rc;
+
+ bh->state = BUF_STATE_EMPTY; /* For the first iteration */
+ fsg->common->usb_amount_left = nkeep + fsg->common->residue;
+ while (fsg->common->usb_amount_left > 0) {
+
+ /* Wait for the next buffer to be free */
+ while (bh->state != BUF_STATE_EMPTY) {
+ rc = sleep_thread(fsg->common);
+ if (rc)
+ return rc;
+ }
+
+ nsend = min(fsg->common->usb_amount_left, FSG_BUFLEN);
+ memset(bh->buf + nkeep, 0, nsend - nkeep);
+ bh->inreq->length = nsend;
+ bh->inreq->zero = 0;
+ start_transfer(fsg, fsg->bulk_in, bh->inreq,
+ &bh->inreq_busy, &bh->state);
+ bh = fsg->common->next_buffhd_to_fill = bh->next;
+ fsg->common->usb_amount_left -= nsend;
+ nkeep = 0;
+ }
+ return 0;
+}
+
+static int throw_away_data(struct fsg_common *common)
+{
+ struct fsg_buffhd *bh;
+ u32 amount;
+ int rc;
+
+ for (bh = common->next_buffhd_to_drain;
+ bh->state != BUF_STATE_EMPTY || common->usb_amount_left > 0;
+ bh = common->next_buffhd_to_drain) {
+
+ /* Throw away the data in a filled buffer */
+ if (bh->state == BUF_STATE_FULL) {
+ smp_rmb();
+ bh->state = BUF_STATE_EMPTY;
+ common->next_buffhd_to_drain = bh->next;
+
+ /* A short packet or an error ends everything */
+ if (bh->outreq->actual != bh->outreq->length ||
+ bh->outreq->status != 0) {
+ raise_exception(common,
+ FSG_STATE_ABORT_BULK_OUT);
+ return -EINTR;
+ }
+ continue;
+ }
+
+ /* Try to submit another request if we need one */
+ bh = common->next_buffhd_to_fill;
+ if (bh->state == BUF_STATE_EMPTY
+ && 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 */
+ 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 */
+ return -EIO;
+ common->next_buffhd_to_fill = bh->next;
+ common->usb_amount_left -= amount;
+ continue;
+ }
+
+ /* Otherwise wait for something to happen */
+ rc = sleep_thread(common);
+ if (rc)
+ return rc;
+ }
+ return 0;
+}
+
+
+static int finish_reply(struct fsg_common *common)
+{
+ struct fsg_buffhd *bh = common->next_buffhd_to_fill;
+ int rc = 0;
+
+ switch (common->data_dir) {
+ case DATA_DIR_NONE:
+ break; /* Nothing to send */
+
+ /* If we don't know whether the host wants to read or write,
+ * this must be CB or CBI with an unknown command. We mustn't
+ * try to send or receive any data. So stall both bulk pipes
+ * if we can and wait for a reset. */
+ case DATA_DIR_UNKNOWN:
+ if (!common->can_stall) {
+ /* Nothing */
+ } else if (fsg_is_set(common)) {
+ fsg_set_halt(common->fsg, common->fsg->bulk_out);
+ rc = halt_bulk_in_endpoint(common->fsg);
+ } else {
+ /* Don't know what to do if common->fsg is NULL */
+ rc = -EIO;
+ }
+ break;
+
+ /* All but the last buffer of data must have already been sent */
+ case DATA_DIR_TO_HOST:
+ if (common->data_size == 0) {
+ /* Nothing to send */
+
+ /* 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)
+ return -EIO;
+ common->next_buffhd_to_fill = bh->next;
+
+ /* For Bulk-only, if we're allowed to stall then send the
+ * short packet and halt the bulk-in endpoint. If we can't
+ * stall, pad out the remaining data with 0's. */
+ } else if (common->can_stall) {
+ bh->inreq->zero = 1;
+ START_TRANSFER_OR(common, bulk_in, bh->inreq,
+ &bh->inreq_busy, &bh->state)
+ /* Don't know what to do if
+ * common->fsg is NULL */
+ rc = -EIO;
+ common->next_buffhd_to_fill = bh->next;
+ if (common->fsg)
+ rc = halt_bulk_in_endpoint(common->fsg);
+ } else if (fsg_is_set(common)) {
+ rc = pad_with_zeros(common->fsg);
+ } else {
+ /* Don't know what to do if common->fsg is NULL */
+ rc = -EIO;
+ }
+ break;
+
+ /* We have processed all we want from the data the host has sent.
+ * There may still be outstanding bulk-out requests. */
+ case DATA_DIR_FROM_HOST:
+ if (common->residue == 0) {
+ /* Nothing to receive */
+
+ /* Did the host stop sending unexpectedly early? */
+ } else if (common->short_packet_received) {
+ raise_exception(common, FSG_STATE_ABORT_BULK_OUT);
+ rc = -EINTR;
+
+ /* We haven't processed all the incoming data. Even though
+ * we may be allowed to stall, doing so would cause a race.
+ * The controller may already have ACK'ed all the remaining
+ * bulk-out packets, in which case the host wouldn't see a
+ * STALL. Not realizing the endpoint was halted, it wouldn't
+ * clear the halt -- leading to problems later on. */
+#if 0
+ } else if (common->can_stall) {
+ if (fsg_is_set(common))
+ fsg_set_halt(common->fsg,
+ common->fsg->bulk_out);
+ raise_exception(common, FSG_STATE_ABORT_BULK_OUT);
+ rc = -EINTR;
+#endif
+
+ /* We can't stall. Read in the excess data and throw it
+ * all away. */
+ } else {
+ rc = throw_away_data(common);
+ }
+ break;
+ }
+ return rc;
+}
+
+
+static int send_status(struct fsg_common *common)
+{
+ struct fsg_lun *curlun = common->curlun;
+ struct fsg_buffhd *bh;
+ struct bulk_cs_wrap *csw;
+ int rc;
+ u8 status = USB_STATUS_PASS;
+ u32 sd, sdinfo = 0;
+
+ /* Wait for the next buffer to become available */
+ bh = common->next_buffhd_to_fill;
+ while (bh->state != BUF_STATE_EMPTY) {
+ rc = sleep_thread(common);
+ if (rc)
+ return rc;
+ }
+
+ if (curlun) {
+ sd = curlun->sense_data;
+ sdinfo = curlun->sense_data_info;
+ } else if (common->bad_lun_okay)
+ sd = SS_NO_SENSE;
+ else
+ sd = SS_LOGICAL_UNIT_NOT_SUPPORTED;
+
+ if (common->phase_error) {
+ DBG(common, "sending phase-error status\n");
+ status = USB_STATUS_PHASE_ERROR;
+ sd = SS_INVALID_COMMAND;
+ } else if (sd != SS_NO_SENSE) {
+ DBG(common, "sending command-failure status\n");
+ status = USB_STATUS_FAIL;
+ VDBG(common, " sense data: SK x%02x, ASC x%02x, ASCQ x%02x;"
+ " info x%x\n",
+ SK(sd), ASC(sd), ASCQ(sd), sdinfo);
+ }
+
+ /* Store and send the Bulk-only CSW */
+ csw = (void *)bh->buf;
+
+ csw->Signature = cpu_to_le32(USB_BULK_CS_SIG);
+ csw->Tag = common->tag;
+ csw->Residue = cpu_to_le32(common->residue);
+ csw->Status = status;
+
+ 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)
+ /* Don't know what to do if common->fsg is NULL */
+ return -EIO;
+
+ common->next_buffhd_to_fill = bh->next;
+ return 0;
+}
+
+
+/*-------------------------------------------------------------------------*/
+
+/* Check whether the command is properly formed and whether its data size
+ * and direction agree with the values we already have. */
+static int check_command(struct fsg_common *common, int cmnd_size,
+ enum data_direction data_dir, unsigned int mask,
+ int needs_medium, const char *name)
+{
+ int i;
+ int lun = common->cmnd[1] >> 5;
+ static const char dirletter[4] = {'u', 'o', 'i', 'n'};
+ char hdlen[20];
+ struct fsg_lun *curlun;
+
+ hdlen[0] = 0;
+ if (common->data_dir != DATA_DIR_UNKNOWN)
+ sprintf(hdlen, ", H%c=%u", dirletter[(int) common->data_dir],
+ 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. */
+ 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.
+ * Carry out the command, but only transfer as much as
+ * we are allowed. */
+ common->data_size_from_cmnd = common->data_size;
+ common->phase_error = 1;
+ }
+ common->residue = common->data_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) {
+ common->phase_error = 1;
+ return -EINVAL;
+ }
+
+ /* Verify the length of the command itself */
+ if (cmnd_size != common->cmnd_size) {
+
+ /* Special case workaround: There are plenty of buggy SCSI
+ * implementations. Many have issues with cbw->Length
+ * field passing a wrong command size. For those cases we
+ * always try to work around the problem by using the length
+ * sent by the host side provided it is at least as large
+ * as the correct command length.
+ * Examples of such cases would be MS-Windows, which issues
+ * REQUEST SENSE with cbw->Length == 12 where it should
+ * be 6, and xbox360 issuing INQUIRY, TEST UNIT READY and
+ * REQUEST SENSE with cbw->Length == 10 where it should
+ * be 6 as well.
+ */
+ if (cmnd_size <= common->cmnd_size) {
+ DBG(common, "%s is buggy! Expected length %d "
+ "but we got %d\n", name,
+ cmnd_size, common->cmnd_size);
+ cmnd_size = common->cmnd_size;
+ } else {
+ common->phase_error = 1;
+ return -EINVAL;
+ }
+ }
+
+ /* Check that the LUN values are consistent */
+ if (common->lun != lun)
+ DBG(common, "using LUN %d from CBW, not LUN %d from CDB\n",
+ common->lun, lun);
+
+ /* Check the LUN */
+ if (common->lun >= 0 && common->lun < common->nluns) {
+ curlun = &common->luns[common->lun];
+ common->curlun = curlun;
+ if (common->cmnd[0] != SC_REQUEST_SENSE) {
+ curlun->sense_data = SS_NO_SENSE;
+ curlun->sense_data_info = 0;
+ curlun->info_valid = 0;
+ }
+ } else {
+ common->curlun = NULL;
+ curlun = NULL;
+ common->bad_lun_okay = 0;
+
+ /* INQUIRY and REQUEST SENSE commands are explicitly allowed
+ * to use unsupported LUNs; all others may not. */
+ if (common->cmnd[0] != SC_INQUIRY &&
+ common->cmnd[0] != SC_REQUEST_SENSE) {
+ DBG(common, "unsupported LUN %d\n", common->lun);
+ return -EINVAL;
+ }
+ }
+
+ /* If a unit attention condition exists, only INQUIRY and
+ * REQUEST SENSE commands are allowed; anything else must fail. */
+ if (curlun && curlun->unit_attention_data != SS_NO_SENSE &&
+ common->cmnd[0] != SC_INQUIRY &&
+ common->cmnd[0] != SC_REQUEST_SENSE) {
+ curlun->sense_data = curlun->unit_attention_data;
+ curlun->unit_attention_data = SS_NO_SENSE;
+ return -EINVAL;
+ }
+
+ /* Check that only command bytes listed in the mask are non-zero */
+ common->cmnd[1] &= 0x1f; /* Mask away the LUN */
+ for (i = 1; i < cmnd_size; ++i) {
+ if (common->cmnd[i] && !(mask & (1 << i))) {
+ if (curlun)
+ curlun->sense_data = SS_INVALID_FIELD_IN_CDB;
+ return -EINVAL;
+ }
+ }
+
+ /* If the medium isn't mounted and the command needs to access
+ * it, return an error. */
+ if (curlun && !fsg_lun_is_open(curlun) && needs_medium) {
+ curlun->sense_data = SS_MEDIUM_NOT_PRESENT;
+ return -EINVAL;
+ }
+
+ return 0;
+}
+
+
+static int do_scsi_command(struct fsg_common *common)
+{
+ struct fsg_buffhd *bh;
+ int rc;
+ int reply = -EINVAL;
+ int i;
+ static char unknown[16];
+
+ dump_cdb(common);
+
+ /* Wait for the next buffer to become available for data or status */
+ bh = common->next_buffhd_to_fill;
+ common->next_buffhd_to_drain = bh;
+ while (bh->state != BUF_STATE_EMPTY) {
+ rc = sleep_thread(common);
+ if (rc)
+ return rc;
+ }
+ common->phase_error = 0;
+ common->short_packet_received = 0;
+
+ down_read(&common->filesem); /* We're using the backing file */
+ switch (common->cmnd[0]) {
+
+ case SC_INQUIRY:
+ common->data_size_from_cmnd = common->cmnd[4];
+ reply = check_command(common, 6, DATA_DIR_TO_HOST,
+ (1<<4), 0,
+ "INQUIRY");
+ if (reply == 0)
+ reply = do_inquiry(common, bh);
+ break;
+
+ case SC_MODE_SELECT_6:
+ common->data_size_from_cmnd = common->cmnd[4];
+ reply = check_command(common, 6, DATA_DIR_FROM_HOST,
+ (1<<1) | (1<<4), 0,
+ "MODE SELECT(6)");
+ if (reply == 0)
+ reply = do_mode_select(common, bh);
+ break;
+
+ case SC_MODE_SELECT_10:
+ common->data_size_from_cmnd =
+ get_unaligned_be16(&common->cmnd[7]);
+ reply = check_command(common, 10, DATA_DIR_FROM_HOST,
+ (1<<1) | (3<<7), 0,
+ "MODE SELECT(10)");
+ if (reply == 0)
+ reply = do_mode_select(common, bh);
+ break;
+
+ case SC_MODE_SENSE_6:
+ common->data_size_from_cmnd = common->cmnd[4];
+ reply = check_command(common, 6, DATA_DIR_TO_HOST,
+ (1<<1) | (1<<2) | (1<<4), 0,
+ "MODE SENSE(6)");
+ if (reply == 0)
+ reply = do_mode_sense(common, bh);
+ break;
+
+ case SC_MODE_SENSE_10:
+ common->data_size_from_cmnd =
+ get_unaligned_be16(&common->cmnd[7]);
+ reply = check_command(common, 10, DATA_DIR_TO_HOST,
+ (1<<1) | (1<<2) | (3<<7), 0,
+ "MODE SENSE(10)");
+ if (reply == 0)
+ reply = do_mode_sense(common, bh);
+ break;
+
+ case SC_PREVENT_ALLOW_MEDIUM_REMOVAL:
+ common->data_size_from_cmnd = 0;
+ reply = check_command(common, 6, DATA_DIR_NONE,
+ (1<<4), 0,
+ "PREVENT-ALLOW MEDIUM REMOVAL");
+ if (reply == 0)
+ reply = do_prevent_allow(common);
+ break;
+
+ case SC_READ_6:
+ i = common->cmnd[4];
+ common->data_size_from_cmnd = (i == 0 ? 256 : i) << 9;
+ reply = check_command(common, 6, DATA_DIR_TO_HOST,
+ (7<<1) | (1<<4), 1,
+ "READ(6)");
+ if (reply == 0)
+ reply = do_read(common);
+ break;
+
+ case SC_READ_10:
+ common->data_size_from_cmnd =
+ get_unaligned_be16(&common->cmnd[7]) << 9;
+ reply = check_command(common, 10, DATA_DIR_TO_HOST,
+ (1<<1) | (0xf<<2) | (3<<7), 1,
+ "READ(10)");
+ if (reply == 0)
+ reply = do_read(common);
+ break;
+
+ case SC_READ_12:
+ common->data_size_from_cmnd =
+ get_unaligned_be32(&common->cmnd[6]) << 9;
+ reply = check_command(common, 12, DATA_DIR_TO_HOST,
+ (1<<1) | (0xf<<2) | (0xf<<6), 1,
+ "READ(12)");
+ if (reply == 0)
+ reply = do_read(common);
+ break;
+
+ case SC_READ_CAPACITY:
+ common->data_size_from_cmnd = 8;
+ reply = check_command(common, 10, DATA_DIR_TO_HOST,
+ (0xf<<2) | (1<<8), 1,
+ "READ CAPACITY");
+ if (reply == 0)
+ reply = do_read_capacity(common, bh);
+ break;
+
+ case SC_READ_HEADER:
+ if (!common->curlun || !common->curlun->cdrom)
+ goto unknown_cmnd;
+ common->data_size_from_cmnd =
+ get_unaligned_be16(&common->cmnd[7]);
+ reply = check_command(common, 10, DATA_DIR_TO_HOST,
+ (3<<7) | (0x1f<<1), 1,
+ "READ HEADER");
+ if (reply == 0)
+ reply = do_read_header(common, bh);
+ break;
+
+ case SC_READ_TOC:
+ if (!common->curlun || !common->curlun->cdrom)
+ goto unknown_cmnd;
+ common->data_size_from_cmnd =
+ get_unaligned_be16(&common->cmnd[7]);
+ reply = check_command(common, 10, DATA_DIR_TO_HOST,
+ (7<<6) | (1<<1), 1,
+ "READ TOC");
+ if (reply == 0)
+ reply = do_read_toc(common, bh);
+ break;
+
+ case SC_READ_FORMAT_CAPACITIES:
+ common->data_size_from_cmnd =
+ get_unaligned_be16(&common->cmnd[7]);
+ reply = check_command(common, 10, DATA_DIR_TO_HOST,
+ (3<<7), 1,
+ "READ FORMAT CAPACITIES");
+ if (reply == 0)
+ reply = do_read_format_capacities(common, bh);
+ break;
+
+ case SC_REQUEST_SENSE:
+ common->data_size_from_cmnd = common->cmnd[4];
+ reply = check_command(common, 6, DATA_DIR_TO_HOST,
+ (1<<4), 0,
+ "REQUEST SENSE");
+ if (reply == 0)
+ reply = do_request_sense(common, bh);
+ break;
+
+ case SC_START_STOP_UNIT:
+ common->data_size_from_cmnd = 0;
+ reply = check_command(common, 6, DATA_DIR_NONE,
+ (1<<1) | (1<<4), 0,
+ "START-STOP UNIT");
+ if (reply == 0)
+ reply = do_start_stop(common);
+ break;
+
+ case SC_SYNCHRONIZE_CACHE:
+ common->data_size_from_cmnd = 0;
+ reply = check_command(common, 10, DATA_DIR_NONE,
+ (0xf<<2) | (3<<7), 1,
+ "SYNCHRONIZE CACHE");
+ if (reply == 0)
+ reply = do_synchronize_cache(common);
+ break;
+
+ case SC_TEST_UNIT_READY:
+ common->data_size_from_cmnd = 0;
+ reply = check_command(common, 6, DATA_DIR_NONE,
+ 0, 1,
+ "TEST UNIT READY");
+ break;
+
+ /* Although optional, this command is used by MS-Windows. We
+ * support a minimal version: BytChk must be 0. */
+ case SC_VERIFY:
+ common->data_size_from_cmnd = 0;
+ reply = check_command(common, 10, DATA_DIR_NONE,
+ (1<<1) | (0xf<<2) | (3<<7), 1,
+ "VERIFY");
+ if (reply == 0)
+ reply = do_verify(common);
+ break;
+
+ case SC_WRITE_6:
+ i = common->cmnd[4];
+ common->data_size_from_cmnd = (i == 0 ? 256 : i) << 9;
+ reply = check_command(common, 6, DATA_DIR_FROM_HOST,
+ (7<<1) | (1<<4), 1,
+ "WRITE(6)");
+ if (reply == 0)
+ reply = do_write(common);
+ break;
+
+ case SC_WRITE_10:
+ common->data_size_from_cmnd =
+ get_unaligned_be16(&common->cmnd[7]) << 9;
+ reply = check_command(common, 10, DATA_DIR_FROM_HOST,
+ (1<<1) | (0xf<<2) | (3<<7), 1,
+ "WRITE(10)");
+ if (reply == 0)
+ reply = do_write(common);
+ break;
+
+ case SC_WRITE_12:
+ common->data_size_from_cmnd =
+ get_unaligned_be32(&common->cmnd[6]) << 9;
+ reply = check_command(common, 12, DATA_DIR_FROM_HOST,
+ (1<<1) | (0xf<<2) | (0xf<<6), 1,
+ "WRITE(12)");
+ if (reply == 0)
+ reply = do_write(common);
+ break;
+
+ /* Some mandatory commands that we recognize but don't implement.
+ * They don't mean much in this setting. It's left as an exercise
+ * for anyone interested to implement RESERVE and RELEASE in terms
+ * of Posix locks. */
+ case SC_FORMAT_UNIT:
+ case SC_RELEASE:
+ case SC_RESERVE:
+ case SC_SEND_DIAGNOSTIC:
+ /* Fall through */
+
+ default:
+unknown_cmnd:
+ common->data_size_from_cmnd = 0;
+ sprintf(unknown, "Unknown x%02x", common->cmnd[0]);
+ reply = check_command(common, common->cmnd_size,
+ DATA_DIR_UNKNOWN, 0xff, 0, unknown);
+ if (reply == 0) {
+ common->curlun->sense_data = SS_INVALID_COMMAND;
+ reply = -EINVAL;
+ }
+ break;
+ }
+ up_read(&common->filesem);
+
+ if (reply == -EINTR || signal_pending(current))
+ return -EINTR;
+
+ /* Set up the single reply buffer for finish_reply() */
+ 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);
+ bh->inreq->length = reply;
+ bh->state = BUF_STATE_FULL;
+ common->residue -= reply;
+ } /* Otherwise it's already set */
+
+ return 0;
+}
+
+
+/*-------------------------------------------------------------------------*/
+
+static int received_cbw(struct fsg_dev *fsg, struct fsg_buffhd *bh)
+{
+ struct usb_request *req = bh->outreq;
+ struct fsg_bulk_cb_wrap *cbw = req->buf;
+ struct fsg_common *common = fsg->common;
+
+ /* Was this a real packet? Should it be ignored? */
+ if (req->status || test_bit(IGNORE_BULK_OUT, &fsg->atomic_bitflags))
+ return -EINVAL;
+
+ /* Is the CBW valid? */
+ if (req->actual != USB_BULK_CB_WRAP_LEN ||
+ cbw->Signature != cpu_to_le32(
+ USB_BULK_CB_SIG)) {
+ DBG(fsg, "invalid CBW: len %u sig 0x%x\n",
+ req->actual,
+ le32_to_cpu(cbw->Signature));
+
+ /* The Bulk-only spec says we MUST stall the IN endpoint
+ * (6.6.1), so it's unavoidable. It also says we must
+ * retain this state until the next reset, but there's
+ * no way to tell the controller driver it should ignore
+ * Clear-Feature(HALT) requests.
+ *
+ * We aren't required to halt the OUT endpoint; instead
+ * we can simply accept and discard any data received
+ * until the next reset. */
+ wedge_bulk_in_endpoint(fsg);
+ set_bit(IGNORE_BULK_OUT, &fsg->atomic_bitflags);
+ return -EINVAL;
+ }
+
+ /* Is the CBW meaningful? */
+ if (cbw->Lun >= FSG_MAX_LUNS || cbw->Flags & ~USB_BULK_IN_FLAG ||
+ cbw->Length <= 0 || cbw->Length > MAX_COMMAND_SIZE) {
+ DBG(fsg, "non-meaningful CBW: lun = %u, flags = 0x%x, "
+ "cmdlen %u\n",
+ cbw->Lun, cbw->Flags, cbw->Length);
+
+ /* We can do anything we want here, so let's stall the
+ * bulk pipes if we are allowed to. */
+ if (common->can_stall) {
+ fsg_set_halt(fsg, fsg->bulk_out);
+ halt_bulk_in_endpoint(fsg);
+ }
+ return -EINVAL;
+ }
+
+ /* Save the command for later */
+ common->cmnd_size = cbw->Length;
+ memcpy(common->cmnd, cbw->CDB, common->cmnd_size);
+ if (cbw->Flags & USB_BULK_IN_FLAG)
+ common->data_dir = DATA_DIR_TO_HOST;
+ else
+ common->data_dir = DATA_DIR_FROM_HOST;
+ common->data_size = le32_to_cpu(cbw->DataTransferLength);
+ if (common->data_size == 0)
+ common->data_dir = DATA_DIR_NONE;
+ common->lun = cbw->Lun;
+ common->tag = cbw->Tag;
+ return 0;
+}
+
+
+static int get_next_command(struct fsg_common *common)
+{
+ struct fsg_buffhd *bh;
+ int rc = 0;
+
+ /* Wait for the next buffer to become available */
+ bh = common->next_buffhd_to_fill;
+ while (bh->state != BUF_STATE_EMPTY) {
+ rc = sleep_thread(common);
+ if (rc)
+ return rc;
+ }
+
+ /* 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)
+ /* Don't know what to do if common->fsg is NULL */
+ return -EIO;
+
+ /* We will drain the buffer in software, which means we
+ * can reuse it for the next filling. No need to advance
+ * next_buffhd_to_fill. */
+
+ /* Wait for the CBW to arrive */
+ while (bh->state != BUF_STATE_FULL) {
+ rc = sleep_thread(common);
+ if (rc)
+ return rc;
+ }
+ smp_rmb();
+ rc = fsg_is_set(common) ? received_cbw(common->fsg, bh) : -EIO;
+ bh->state = BUF_STATE_EMPTY;
+
+ return rc;
+}
+
+
+/*-------------------------------------------------------------------------*/
+
+static int enable_endpoint(struct fsg_common *common, struct usb_ep *ep,
+ const struct usb_endpoint_descriptor *d)
+{
+ int rc;
+
+ ep->driver_data = common;
+ rc = usb_ep_enable(ep, d);
+ if (rc)
+ ERROR(common, "can't enable %s, result %d\n", ep->name, rc);
+ return rc;
+}
+
+static int alloc_request(struct fsg_common *common, struct usb_ep *ep,
+ struct usb_request **preq)
+{
+ *preq = usb_ep_alloc_request(ep, GFP_ATOMIC);
+ if (*preq)
+ return 0;
+ ERROR(common, "can't allocate request for %s\n", ep->name);
+ return -ENOMEM;
+}
+
+/*
+ * Reset interface setting and re-init endpoint state (toggle etc).
+ * Call with altsetting < 0 to disable the interface. The only other
+ * available altsetting is 0, which enables the interface.
+ */
+static int do_set_interface(struct fsg_common *common, int altsetting)
+{
+ int rc = 0;
+ int i;
+ const struct usb_endpoint_descriptor *d;
+
+ if (common->running)
+ DBG(common, "reset interface\n");
+
+reset:
+ /* Deallocate the requests */
+ if (common->prev_fsg) {
+ struct fsg_dev *fsg = common->prev_fsg;
+
+ for (i = 0; i < FSG_NUM_BUFFERS; ++i) {
+ struct fsg_buffhd *bh = &common->buffhds[i];
+
+ if (bh->inreq) {
+ usb_ep_free_request(fsg->bulk_in, bh->inreq);
+ bh->inreq = NULL;
+ }
+ if (bh->outreq) {
+ usb_ep_free_request(fsg->bulk_out, bh->outreq);
+ bh->outreq = NULL;
+ }
+ }
+
+ /* Disable the endpoints */
+ if (fsg->bulk_in_enabled) {
+ usb_ep_disable(fsg->bulk_in);
+ fsg->bulk_in_enabled = 0;
+ }
+ if (fsg->bulk_out_enabled) {
+ usb_ep_disable(fsg->bulk_out);
+ fsg->bulk_out_enabled = 0;
+ }
+
+ common->prev_fsg = 0;
+ }
+
+ common->running = 0;
+ if (altsetting < 0 || rc != 0)
+ return rc;
+
+ DBG(common, "set interface %d\n", altsetting);
+
+ if (fsg_is_set(common)) {
+ struct fsg_dev *fsg = common->fsg;
+ common->prev_fsg = common->fsg;
+
+ /* Enable the endpoints */
+ d = fsg_ep_desc(common->gadget,
+ &fsg_fs_bulk_in_desc, &fsg_hs_bulk_in_desc);
+ rc = enable_endpoint(common, fsg->bulk_in, d);
+ if (rc)
+ goto reset;
+ fsg->bulk_in_enabled = 1;
+
+ d = fsg_ep_desc(common->gadget,
+ &fsg_fs_bulk_out_desc, &fsg_hs_bulk_out_desc);
+ rc = enable_endpoint(common, fsg->bulk_out, d);
+ if (rc)
+ goto reset;
+ fsg->bulk_out_enabled = 1;
+ common->bulk_out_maxpacket = le16_to_cpu(d->wMaxPacketSize);
+ clear_bit(IGNORE_BULK_OUT, &fsg->atomic_bitflags);
+
+ /* Allocate the requests */
+ for (i = 0; i < FSG_NUM_BUFFERS; ++i) {
+ struct fsg_buffhd *bh = &common->buffhds[i];
+
+ rc = alloc_request(common, fsg->bulk_in, &bh->inreq);
+ if (rc)
+ goto reset;
+ rc = alloc_request(common, fsg->bulk_out, &bh->outreq);
+ if (rc)
+ goto reset;
+ bh->inreq->buf = bh->outreq->buf = bh->buf;
+ bh->inreq->context = bh->outreq->context = bh;
+ bh->inreq->complete = bulk_in_complete;
+ bh->outreq->complete = bulk_out_complete;
+ }
+
+ common->running = 1;
+ for (i = 0; i < common->nluns; ++i)
+ common->luns[i].unit_attention_data = SS_RESET_OCCURRED;
+ return rc;
+ } else {
+ return -EIO;
+ }
+}
+
+
+/*
+ * Change our operational configuration. This code must agree with the code
+ * that returns config descriptors, and with interface altsetting code.
+ *
+ * It's also responsible for power management interactions. Some
+ * configurations might not work with our current power sources.
+ * For now we just assume the gadget is always self-powered.
+ */
+static int do_set_config(struct fsg_common *common, u8 new_config)
+{
+ int rc = 0;
+
+ /* Disable the single interface */
+ if (common->config != 0) {
+ DBG(common, "reset config\n");
+ common->config = 0;
+ rc = do_set_interface(common, -1);
+ }
+
+ /* Enable the interface */
+ if (new_config != 0) {
+ common->config = new_config;
+ rc = do_set_interface(common, 0);
+ if (rc != 0)
+ common->config = 0; /* Reset on errors */
+ }
+ return rc;
+}
+
+
+/****************************** ALT CONFIGS ******************************/
+
+
+static int fsg_set_alt(struct usb_function *f, unsigned intf, unsigned alt)
+{
+ struct fsg_dev *fsg = fsg_from_func(f);
+ fsg->common->prev_fsg = fsg->common->fsg;
+ fsg->common->fsg = fsg;
+ fsg->common->new_config = 1;
+ raise_exception(fsg->common, FSG_STATE_CONFIG_CHANGE);
+ return 0;
+}
+
+static void fsg_disable(struct usb_function *f)
+{
+ struct fsg_dev *fsg = fsg_from_func(f);
+ fsg->common->prev_fsg = fsg->common->fsg;
+ fsg->common->fsg = fsg;
+ fsg->common->new_config = 0;
+ raise_exception(fsg->common, FSG_STATE_CONFIG_CHANGE);
+}
+
+
+/*-------------------------------------------------------------------------*/
+
+static void handle_exception(struct fsg_common *common)
+{
+ siginfo_t info;
+ int sig;
+ int i;
+ struct fsg_buffhd *bh;
+ enum fsg_state old_state;
+ u8 new_config;
+ struct fsg_lun *curlun;
+ unsigned int exception_req_tag;
+ int rc;
+
+ /* Clear the existing signals. Anything but SIGUSR1 is converted
+ * into a high-priority EXIT exception. */
+ for (;;) {
+ sig = dequeue_signal_lock(current, &current->blocked, &info);
+ if (!sig)
+ break;
+ if (sig != SIGUSR1) {
+ if (common->state < FSG_STATE_EXIT)
+ DBG(common, "Main thread exiting on signal\n");
+ raise_exception(common, FSG_STATE_EXIT);
+ }
+ }
+
+ /* Cancel all the pending transfers */
+ if (fsg_is_set(common)) {
+ for (i = 0; i < FSG_NUM_BUFFERS; ++i) {
+ bh = &common->buffhds[i];
+ if (bh->inreq_busy)
+ usb_ep_dequeue(common->fsg->bulk_in, bh->inreq);
+ if (bh->outreq_busy)
+ usb_ep_dequeue(common->fsg->bulk_out,
+ bh->outreq);
+ }
+
+ /* Wait until everything is idle */
+ for (;;) {
+ int num_active = 0;
+ for (i = 0; i < FSG_NUM_BUFFERS; ++i) {
+ bh = &common->buffhds[i];
+ num_active += bh->inreq_busy + bh->outreq_busy;
+ }
+ if (num_active == 0)
+ break;
+ if (sleep_thread(common))
+ return;
+ }
+
+ /* Clear out the controller's fifos */
+ if (common->fsg->bulk_in_enabled)
+ usb_ep_fifo_flush(common->fsg->bulk_in);
+ if (common->fsg->bulk_out_enabled)
+ 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. */
+ spin_lock_irq(&common->lock);
+
+ for (i = 0; i < FSG_NUM_BUFFERS; ++i) {
+ bh = &common->buffhds[i];
+ bh->state = BUF_STATE_EMPTY;
+ }
+ common->next_buffhd_to_fill = &common->buffhds[0];
+ common->next_buffhd_to_drain = &common->buffhds[0];
+ exception_req_tag = common->exception_req_tag;
+ new_config = common->new_config;
+ old_state = common->state;
+
+ if (old_state == FSG_STATE_ABORT_BULK_OUT)
+ common->state = FSG_STATE_STATUS_PHASE;
+ else {
+ for (i = 0; i < common->nluns; ++i) {
+ curlun = &common->luns[i];
+ curlun->prevent_medium_removal = 0;
+ curlun->sense_data = SS_NO_SENSE;
+ curlun->unit_attention_data = SS_NO_SENSE;
+ curlun->sense_data_info = 0;
+ curlun->info_valid = 0;
+ }
+ common->state = FSG_STATE_IDLE;
+ }
+ spin_unlock_irq(&common->lock);
+
+ /* Carry out any extra actions required for the exception */
+ switch (old_state) {
+ case FSG_STATE_ABORT_BULK_OUT:
+ send_status(common);
+ spin_lock_irq(&common->lock);
+ if (common->state == FSG_STATE_STATUS_PHASE)
+ common->state = FSG_STATE_IDLE;
+ spin_unlock_irq(&common->lock);
+ break;
+
+ case FSG_STATE_RESET:
+ /* In case we were forced against our will to halt a
+ * bulk endpoint, clear the halt now. (The SuperH UDC
+ * requires this.) */
+ if (!fsg_is_set(common))
+ break;
+ if (test_and_clear_bit(IGNORE_BULK_OUT,
+ &common->fsg->atomic_bitflags))
+ usb_ep_clear_halt(common->fsg->bulk_in);
+
+ 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
+ * a waste of time. Ditto for the INTERFACE_CHANGE and
+ * CONFIG_CHANGE cases. */
+ /* for (i = 0; i < common->nluns; ++i) */
+ /* common->luns[i].unit_attention_data = */
+ /* SS_RESET_OCCURRED; */
+ break;
+
+ case FSG_STATE_CONFIG_CHANGE:
+ rc = do_set_config(common, new_config);
+ if (common->ep0_req_tag != exception_req_tag)
+ break;
+ if (rc != 0) { /* STALL on errors */
+ DBG(common, "ep0 set halt\n");
+ usb_ep_set_halt(common->ep0);
+ } else { /* Complete the status stage */
+ ep0_queue(common);
+ }
+ break;
+
+ case FSG_STATE_EXIT:
+ case FSG_STATE_TERMINATED:
+ do_set_config(common, 0); /* Free resources */
+ spin_lock_irq(&common->lock);
+ common->state = FSG_STATE_TERMINATED; /* Stop the thread */
+ spin_unlock_irq(&common->lock);
+ break;
+
+ case FSG_STATE_INTERFACE_CHANGE:
+ case FSG_STATE_DISCONNECT:
+ case FSG_STATE_COMMAND_PHASE:
+ case FSG_STATE_DATA_PHASE:
+ case FSG_STATE_STATUS_PHASE:
+ case FSG_STATE_IDLE:
+ break;
+ }
+}
+
+
+/*-------------------------------------------------------------------------*/
+
+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_signal(SIGINT);
+ allow_signal(SIGTERM);
+ allow_signal(SIGKILL);
+ allow_signal(SIGUSR1);
+
+ /* Allow the thread to be frozen */
+ set_freezable();
+
+ /* Arrange for userspace references to be interpreted as kernel
+ * pointers. That way we can pass a kernel pointer to a routine
+ * that expects a __user pointer and it will work okay. */
+ set_fs(get_ds());
+
+ /* The main loop */
+ while (common->state != FSG_STATE_TERMINATED) {
+ if (exception_in_progress(common) || signal_pending(current)) {
+ handle_exception(common);
+ continue;
+ }
+
+ if (!common->running) {
+ sleep_thread(common);
+ continue;
+ }
+
+ if (get_next_command(common))
+ continue;
+
+ spin_lock_irq(&common->lock);
+ if (!exception_in_progress(common))
+ common->state = FSG_STATE_DATA_PHASE;
+ spin_unlock_irq(&common->lock);
+
+ if (do_scsi_command(common) || finish_reply(common))
+ continue;
+
+ spin_lock_irq(&common->lock);
+ if (!exception_in_progress(common))
+ common->state = FSG_STATE_STATUS_PHASE;
+ spin_unlock_irq(&common->lock);
+
+ if (send_status(common))
+ continue;
+
+ spin_lock_irq(&common->lock);
+ if (!exception_in_progress(common))
+ common->state = FSG_STATE_IDLE;
+ spin_unlock_irq(&common->lock);
+ }
+
+ spin_lock_irq(&common->lock);
+ common->thread_task = NULL;
+ spin_unlock_irq(&common->lock);
+
+ if (common->thread_exits)
+ common->thread_exits(common);
+
+ /* Let the unbind and cleanup routines know the thread has exited */
+ complete_and_exit(&common->thread_notifier, 0);
+}
+
+
+/*************************** DEVICE ATTRIBUTES ***************************/
+
+/* Write permission is checked per LUN in store_*() functions. */
+static DEVICE_ATTR(ro, 0644, fsg_show_ro, fsg_store_ro);
+static DEVICE_ATTR(file, 0644, fsg_show_file, fsg_store_file);
+
+
+/****************************** FSG COMMON ******************************/
+
+static void fsg_common_release(struct kref *ref);
+
+static void fsg_lun_release(struct device *dev)
+{
+ /* Nothing needs to be done */
+}
+
+static inline void fsg_common_get(struct fsg_common *common)
+{
+ kref_get(&common->ref);
+}
+
+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)
+{
+ struct usb_gadget *gadget = cdev->gadget;
+ struct fsg_buffhd *bh;
+ struct fsg_lun *curlun;
+ struct fsg_lun_config *lcfg;
+ int nluns, i, rc;
+ char *pathbuf;
+
+ /* Find out how many LUNs there should be */
+ nluns = cfg->nluns;
+ if (nluns < 1 || nluns > FSG_MAX_LUNS) {
+ dev_err(&gadget->dev, "invalid number of LUNs: %u\n", nluns);
+ return ERR_PTR(-EINVAL);
+ }
+
+ /* Allocate? */
+ if (!common) {
+ common = kzalloc(sizeof *common, GFP_KERNEL);
+ if (!common)
+ return ERR_PTR(-ENOMEM);
+ common->free_storage_on_release = 1;
+ } else {
+ memset(common, 0, sizeof common);
+ common->free_storage_on_release = 0;
+ }
+
+ common->private_data = cfg->private_data;
+
+ common->gadget = gadget;
+ common->ep0 = gadget->ep0;
+ common->ep0req = cdev->req;
+
+ /* Maybe allocate device-global string IDs, and patch descriptors */
+ if (fsg_strings[FSG_STRING_INTERFACE].id == 0) {
+ rc = usb_string_id(cdev);
+ if (rc < 0) {
+ kfree(common);
+ return ERR_PTR(rc);
+ }
+ fsg_strings[FSG_STRING_INTERFACE].id = rc;
+ fsg_intf_desc.iInterface = rc;
+ }
+
+ /* Create the LUNs, open their backing files, and register the
+ * LUN devices in sysfs. */
+ curlun = kzalloc(nluns * sizeof *curlun, GFP_KERNEL);
+ if (!curlun) {
+ kfree(common);
+ return ERR_PTR(-ENOMEM);
+ }
+ common->luns = curlun;
+
+ init_rwsem(&common->filesem);
+
+ for (i = 0, lcfg = cfg->luns; i < nluns; ++i, ++curlun, ++lcfg) {
+ curlun->cdrom = !!lcfg->cdrom;
+ curlun->ro = lcfg->cdrom || lcfg->ro;
+ curlun->removable = lcfg->removable;
+ curlun->dev.release = fsg_lun_release;
+ curlun->dev.parent = &gadget->dev;
+ /* curlun->dev.driver = &fsg_driver.driver; XXX */
+ dev_set_drvdata(&curlun->dev, &common->filesem);
+ dev_set_name(&curlun->dev,
+ cfg->lun_name_format
+ ? cfg->lun_name_format
+ : "lun%d",
+ i);
+
+ rc = device_register(&curlun->dev);
+ if (rc) {
+ INFO(common, "failed to register LUN%d: %d\n", i, rc);
+ common->nluns = i;
+ goto error_release;
+ }
+
+ rc = device_create_file(&curlun->dev, &dev_attr_ro);
+ if (rc)
+ goto error_luns;
+ rc = device_create_file(&curlun->dev, &dev_attr_file);
+ if (rc)
+ goto error_luns;
+
+ if (lcfg->filename) {
+ rc = fsg_lun_open(curlun, lcfg->filename);
+ if (rc)
+ goto error_luns;
+ } else if (!curlun->removable) {
+ ERROR(common, "no file given for LUN%d\n", i);
+ rc = -EINVAL;
+ goto error_luns;
+ }
+ }
+ common->nluns = nluns;
+
+
+ /* Data buffers cyclic list */
+ /* Buffers in buffhds are static -- no need for additional
+ * allocation. */
+ bh = common->buffhds;
+ i = FSG_NUM_BUFFERS - 1;
+ do {
+ bh->next = bh + 1;
+ } while (++bh, --i);
+ bh->next = common->buffhds;
+
+
+ /* Prepare inquiryString */
+ if (cfg->release != 0xffff) {
+ i = cfg->release;
+ } else {
+ /* The sa1100 controller is not supported */
+ i = gadget_is_sa1100(gadget)
+ ? -1
+ : usb_gadget_controller_number(gadget);
+ if (i >= 0) {
+ i = 0x0300 + i;
+ } else {
+ WARNING(common, "controller '%s' not recognized\n",
+ gadget->name);
+ 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 "),
+ /* Assume product name dependent on the first LUN */
+ OR(cfg->product_name, common->luns->cdrom
+ ? "File-Stor Gadget"
+ : "File-CD Gadget "),
+ i);
+
+
+ /* 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_sh(common->gadget) ||
+ gadget_is_at91(common->gadget));
+
+
+ spin_lock_init(&common->lock);
+ kref_init(&common->ref);
+
+
+ /* Tell the thread to start working */
+ common->thread_exits = cfg->thread_exits;
+ common->thread_task =
+ kthread_create(fsg_main_thread, common,
+ OR(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);
+#undef OR
+
+
+ /* Information */
+ INFO(common, FSG_DRIVER_DESC ", version: " FSG_DRIVER_VERSION "\n");
+ INFO(common, "Number of LUNs=%d\n", common->nluns);
+
+ pathbuf = kmalloc(PATH_MAX, GFP_KERNEL);
+ for (i = 0, nluns = common->nluns, curlun = common->luns;
+ i < nluns;
+ ++curlun, ++i) {
+ char *p = "(no medium)";
+ if (fsg_lun_is_open(curlun)) {
+ p = "(error)";
+ if (pathbuf) {
+ p = d_path(&curlun->filp->f_path,
+ pathbuf, PATH_MAX);
+ if (IS_ERR(p))
+ p = "(error)";
+ }
+ }
+ LINFO(curlun, "LUN: %s%s%sfile: %s\n",
+ curlun->removable ? "removable " : "",
+ curlun->ro ? "read only " : "",
+ curlun->cdrom ? "CD-ROM " : "",
+ p);
+ }
+ kfree(pathbuf);
+
+ DBG(common, "I/O thread pid: %d\n", task_pid_nr(common->thread_task));
+
+ wake_up_process(common->thread_task);
+
+ 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 */
+ fsg_common_release(&common->ref);
+ complete(&common->thread_notifier);
+ return ERR_PTR(rc);
+}
+
+
+static void fsg_common_release(struct kref *ref)
+{
+ struct fsg_common *common =
+ container_of(ref, struct fsg_common, ref);
+ unsigned i = common->nluns;
+ struct fsg_lun *lun = common->luns;
+
+ /* If the thread isn't already dead, tell it to exit now */
+ 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);
+ }
+
+ /* Beware tempting for -> do-while optimization: when in error
+ * recovery nluns may be zero. */
+
+ for (; i; --i, ++lun) {
+ device_remove_file(&lun->dev, &dev_attr_ro);
+ device_remove_file(&lun->dev, &dev_attr_file);
+ fsg_lun_close(lun);
+ device_unregister(&lun->dev);
+ }
+
+ kfree(common->luns);
+ if (common->free_storage_on_release)
+ kfree(common);
+}
+
+
+/*-------------------------------------------------------------------------*/
+
+
+static void fsg_unbind(struct usb_configuration *c, struct usb_function *f)
+{
+ struct fsg_dev *fsg = fsg_from_func(f);
+
+ DBG(fsg, "unbind\n");
+ fsg_common_put(fsg->common);
+ kfree(fsg);
+}
+
+
+static int fsg_bind(struct usb_configuration *c, struct usb_function *f)
+{
+ struct fsg_dev *fsg = fsg_from_func(f);
+ struct usb_gadget *gadget = c->cdev->gadget;
+ int rc;
+ int i;
+ struct usb_ep *ep;
+
+ fsg->gadget = gadget;
+
+ /* New interface */
+ i = usb_interface_id(c, f);
+ if (i < 0)
+ return i;
+ fsg_intf_desc.bInterfaceNumber = i;
+ fsg->interface_number = i;
+
+ /* Find all the endpoints we will use */
+ ep = usb_ep_autoconfig(gadget, &fsg_fs_bulk_in_desc);
+ if (!ep)
+ goto autoconf_fail;
+ ep->driver_data = fsg->common; /* claim the endpoint */
+ fsg->bulk_in = ep;
+
+ ep = usb_ep_autoconfig(gadget, &fsg_fs_bulk_out_desc);
+ if (!ep)
+ goto autoconf_fail;
+ ep->driver_data = fsg->common; /* claim the endpoint */
+ fsg->bulk_out = ep;
+
+ if (gadget_is_dualspeed(gadget)) {
+ /* Assume endpoint addresses are the same for both speeds */
+ fsg_hs_bulk_in_desc.bEndpointAddress =
+ fsg_fs_bulk_in_desc.bEndpointAddress;
+ fsg_hs_bulk_out_desc.bEndpointAddress =
+ fsg_fs_bulk_out_desc.bEndpointAddress;
+ f->hs_descriptors = fsg_hs_function;
+ }
+
+ return 0;
+
+autoconf_fail:
+ ERROR(fsg, "unable to autoconfigure all endpoints\n");
+ rc = -ENOTSUPP;
+ fsg_unbind(c, f);
+ return rc;
+}
+
+
+/****************************** ADD FUNCTION ******************************/
+
+static struct usb_gadget_strings *fsg_strings_array[] = {
+ &fsg_stringtab,
+ NULL,
+};
+
+static int fsg_add(struct usb_composite_dev *cdev,
+ struct usb_configuration *c,
+ struct fsg_common *common)
+{
+ struct fsg_dev *fsg;
+ int rc;
+
+ fsg = kzalloc(sizeof *fsg, GFP_KERNEL);
+ if (unlikely(!fsg))
+ return -ENOMEM;
+
+ fsg->function.name = FSG_DRIVER_DESC;
+ fsg->function.strings = fsg_strings_array;
+ fsg->function.descriptors = fsg_fs_function;
+ fsg->function.bind = fsg_bind;
+ fsg->function.unbind = fsg_unbind;
+ fsg->function.setup = fsg_setup;
+ fsg->function.set_alt = fsg_set_alt;
+ fsg->function.disable = fsg_disable;
+
+ fsg->common = common;
+ /* 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. */
+
+ rc = usb_add_function(c, &fsg->function);
+
+ if (likely(rc == 0))
+ fsg_common_get(fsg->common);
+ else
+ kfree(fsg);
+
+ return rc;
+}
+
+
+
+/************************* Module parameters *************************/
+
+
+struct fsg_module_parameters {
+ char *file[FSG_MAX_LUNS];
+ int ro[FSG_MAX_LUNS];
+ int removable[FSG_MAX_LUNS];
+ int cdrom[FSG_MAX_LUNS];
+
+ unsigned int file_count, ro_count, removable_count, cdrom_count;
+ unsigned int luns; /* nluns */
+ 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, \
+ S_IRUGO); \
+ MODULE_PARM_DESC(prefix ## name, desc)
+
+#define _FSG_MODULE_PARAM(prefix, params, name, type, desc) \
+ module_param_named(prefix ## name, params.name, type, \
+ S_IRUGO); \
+ MODULE_PARM_DESC(prefix ## name, desc)
+
+#define FSG_MODULE_PARAMETERS(prefix, params) \
+ _FSG_MODULE_PARAM_ARRAY(prefix, params, file, charp, \
+ "names of backing files or devices"); \
+ _FSG_MODULE_PARAM_ARRAY(prefix, params, ro, bool, \
+ "true to force read-only"); \
+ _FSG_MODULE_PARAM_ARRAY(prefix, params, removable, bool, \
+ "true to simulate removable media"); \
+ _FSG_MODULE_PARAM_ARRAY(prefix, params, cdrom, bool, \
+ "true to simulate CD-ROM instead of disk"); \
+ _FSG_MODULE_PARAM(prefix, params, luns, uint, \
+ "number of LUNs"); \
+ _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)
+{
+ struct fsg_lun_config *lun;
+ unsigned i;
+
+ /* Configure LUNs */
+ cfg->nluns =
+ min(params->luns ?: (params->file_count ?: 1u),
+ (unsigned)FSG_MAX_LUNS);
+ for (i = 0, lun = cfg->luns; i < cfg->nluns; ++i, ++lun) {
+ lun->ro = !!params->ro[i];
+ lun->cdrom = !!params->cdrom[i];
+ lun->removable = /* Removable by default */
+ params->removable_count <= i || params->removable[i];
+ lun->filename =
+ params->file_count > i && params->file[i][0]
+ ? params->file[i]
+ : 0;
+ }
+
+ /* Let MSF use defaults */
+ cfg->lun_name_format = 0;
+ cfg->thread_name = 0;
+ cfg->vendor_name = 0;
+ cfg->product_name = 0;
+ cfg->release = 0xffff;
+
+ cfg->thread_exits = 0;
+ cfg->private_data = 0;
+
+ /* Finalise */
+ cfg->can_stall = params->stall;
+}
+
+static inline struct fsg_common *
+fsg_common_from_params(struct fsg_common *common,
+ struct usb_composite_dev *cdev,
+ const struct fsg_module_parameters *params)
+ __attribute__((unused));
+static inline struct fsg_common *
+fsg_common_from_params(struct fsg_common *common,
+ struct usb_composite_dev *cdev,
+ const struct fsg_module_parameters *params)
+{
+ struct fsg_config cfg;
+ fsg_config_from_params(&cfg, params);
+ return fsg_common_init(common, cdev, &cfg);
+}
+
diff --git a/drivers/usb/gadget/f_rndis.c b/drivers/usb/gadget/f_rndis.c
index c9966cc..95dae4c 100644
--- a/drivers/usb/gadget/f_rndis.c
+++ b/drivers/usb/gadget/f_rndis.c
@@ -4,6 +4,8 @@
* Copyright (C) 2003-2005,2008 David Brownell
* Copyright (C) 2003-2004 Robert Schwebel, Benedikt Spranger
* Copyright (C) 2008 Nokia Corporation
+ * Copyright (C) 2009 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
@@ -149,8 +151,8 @@ static struct usb_cdc_call_mgmt_descriptor call_mgmt_descriptor __initdata = {
.bDataInterface = 0x01,
};
-static struct usb_cdc_acm_descriptor acm_descriptor __initdata = {
- .bLength = sizeof acm_descriptor,
+static struct usb_cdc_acm_descriptor rndis_acm_descriptor __initdata = {
+ .bLength = sizeof rndis_acm_descriptor,
.bDescriptorType = USB_DT_CS_INTERFACE,
.bDescriptorSubType = USB_CDC_ACM_TYPE,
@@ -179,6 +181,20 @@ static struct usb_interface_descriptor rndis_data_intf __initdata = {
/* .iInterface = DYNAMIC */
};
+
+static struct usb_interface_assoc_descriptor
+rndis_iad_descriptor = {
+ .bLength = sizeof rndis_iad_descriptor,
+ .bDescriptorType = USB_DT_INTERFACE_ASSOCIATION,
+
+ .bFirstInterface = 0, /* XXX, hardcoded */
+ .bInterfaceCount = 2, // control + data
+ .bFunctionClass = USB_CLASS_COMM,
+ .bFunctionSubClass = USB_CDC_SUBCLASS_ETHERNET,
+ .bFunctionProtocol = USB_CDC_PROTO_NONE,
+ /* .iFunction = DYNAMIC */
+};
+
/* full speed support: */
static struct usb_endpoint_descriptor fs_notify_desc __initdata = {
@@ -208,11 +224,12 @@ static struct usb_endpoint_descriptor fs_out_desc __initdata = {
};
static struct usb_descriptor_header *eth_fs_function[] __initdata = {
+ (struct usb_descriptor_header *) &rndis_iad_descriptor,
/* control interface matches ACM, not Ethernet */
(struct usb_descriptor_header *) &rndis_control_intf,
(struct usb_descriptor_header *) &header_desc,
(struct usb_descriptor_header *) &call_mgmt_descriptor,
- (struct usb_descriptor_header *) &acm_descriptor,
+ (struct usb_descriptor_header *) &rndis_acm_descriptor,
(struct usb_descriptor_header *) &rndis_union_desc,
(struct usb_descriptor_header *) &fs_notify_desc,
/* data interface has no altsetting */
@@ -252,11 +269,12 @@ static struct usb_endpoint_descriptor hs_out_desc __initdata = {
};
static struct usb_descriptor_header *eth_hs_function[] __initdata = {
+ (struct usb_descriptor_header *) &rndis_iad_descriptor,
/* control interface matches ACM, not Ethernet */
(struct usb_descriptor_header *) &rndis_control_intf,
(struct usb_descriptor_header *) &header_desc,
(struct usb_descriptor_header *) &call_mgmt_descriptor,
- (struct usb_descriptor_header *) &acm_descriptor,
+ (struct usb_descriptor_header *) &rndis_acm_descriptor,
(struct usb_descriptor_header *) &rndis_union_desc,
(struct usb_descriptor_header *) &hs_notify_desc,
/* data interface has no altsetting */
@@ -271,6 +289,7 @@ static struct usb_descriptor_header *eth_hs_function[] __initdata = {
static struct usb_string rndis_string_defs[] = {
[0].s = "RNDIS Communications Control",
[1].s = "RNDIS Ethernet Data",
+ [2].s = "RNDIS",
{ } /* end of list */
};
@@ -587,6 +606,7 @@ rndis_bind(struct usb_configuration *c, struct usb_function *f)
if (status < 0)
goto fail;
rndis->ctrl_id = status;
+ rndis_iad_descriptor.bFirstInterface = status;
rndis_control_intf.bInterfaceNumber = status;
rndis_union_desc.bMasterInterface0 = status;
@@ -798,6 +818,13 @@ int __init rndis_bind_config(struct usb_configuration *c, u8 ethaddr[ETH_ALEN])
return status;
rndis_string_defs[1].id = status;
rndis_data_intf.iInterface = status;
+
+ /* IAD iFunction label */
+ status = usb_string_id(c->cdev);
+ if (status < 0)
+ return status;
+ rndis_string_defs[2].id = status;
+ rndis_iad_descriptor.iFunction = status;
}
/* allocate and initialize one new instance */
diff --git a/drivers/usb/gadget/file_storage.c b/drivers/usb/gadget/file_storage.c
index 1e6aa50..fca3407 100644
--- a/drivers/usb/gadget/file_storage.c
+++ b/drivers/usb/gadget/file_storage.c
@@ -248,8 +248,6 @@
#include <linux/freezer.h>
#include <linux/utsname.h>
-#include <asm/unaligned.h>
-
#include <linux/usb/ch9.h>
#include <linux/usb/gadget.h>
@@ -274,21 +272,20 @@
#define DRIVER_NAME "g_file_storage"
#define DRIVER_VERSION "20 November 2008"
-static const char longname[] = DRIVER_DESC;
-static const char shortname[] = DRIVER_NAME;
+static char fsg_string_manufacturer[64];
+static const char fsg_string_product[] = DRIVER_DESC;
+static char fsg_string_serial[13];
+static const char fsg_string_config[] = "Self-powered";
+static const char fsg_string_interface[] = "Mass Storage";
+
+
+#include "storage_common.c"
+
MODULE_DESCRIPTION(DRIVER_DESC);
MODULE_AUTHOR("Alan Stern");
MODULE_LICENSE("Dual BSD/GPL");
-/* Thanks to NetChip Technologies for donating this product ID.
- *
- * DO NOT REUSE THESE IDs with any other driver!! Ever!!
- * Instead: allocate your own, using normal USB-IF procedures. */
-#define DRIVER_VENDOR_ID 0x0525 // NetChip
-#define DRIVER_PRODUCT_ID 0xa4a5 // Linux-USB File-backed Storage Gadget
-
-
/*
* This driver assumes self-powered hardware and has no way for users to
* trigger remote wakeup. It uses autoconfiguration to select endpoints
@@ -298,54 +295,12 @@ MODULE_LICENSE("Dual BSD/GPL");
/*-------------------------------------------------------------------------*/
-#define LDBG(lun,fmt,args...) \
- dev_dbg(&(lun)->dev , fmt , ## args)
-#define MDBG(fmt,args...) \
- pr_debug(DRIVER_NAME ": " fmt , ## args)
-
-#ifndef DEBUG
-#undef VERBOSE_DEBUG
-#undef DUMP_MSGS
-#endif /* !DEBUG */
-
-#ifdef VERBOSE_DEBUG
-#define VLDBG LDBG
-#else
-#define VLDBG(lun,fmt,args...) \
- do { } while (0)
-#endif /* VERBOSE_DEBUG */
-
-#define LERROR(lun,fmt,args...) \
- dev_err(&(lun)->dev , fmt , ## args)
-#define LWARN(lun,fmt,args...) \
- dev_warn(&(lun)->dev , fmt , ## args)
-#define LINFO(lun,fmt,args...) \
- dev_info(&(lun)->dev , fmt , ## args)
-
-#define MINFO(fmt,args...) \
- pr_info(DRIVER_NAME ": " fmt , ## args)
-
-#define DBG(d, fmt, args...) \
- dev_dbg(&(d)->gadget->dev , fmt , ## args)
-#define VDBG(d, fmt, args...) \
- dev_vdbg(&(d)->gadget->dev , fmt , ## args)
-#define ERROR(d, fmt, args...) \
- dev_err(&(d)->gadget->dev , fmt , ## args)
-#define WARNING(d, fmt, args...) \
- dev_warn(&(d)->gadget->dev , fmt , ## args)
-#define INFO(d, fmt, args...) \
- dev_info(&(d)->gadget->dev , fmt , ## args)
-
-
-/*-------------------------------------------------------------------------*/
/* Encapsulate the module parameter settings */
-#define MAX_LUNS 8
-
static struct {
- char *file[MAX_LUNS];
- int ro[MAX_LUNS];
+ char *file[FSG_MAX_LUNS];
+ int ro[FSG_MAX_LUNS];
unsigned int num_filenames;
unsigned int num_ros;
unsigned int nluns;
@@ -372,8 +327,8 @@ static struct {
.removable = 0,
.can_stall = 1,
.cdrom = 0,
- .vendor = DRIVER_VENDOR_ID,
- .product = DRIVER_PRODUCT_ID,
+ .vendor = FSG_VENDOR_ID,
+ .product = FSG_PRODUCT_ID,
.release = 0xffff, // Use controller chip type
.buflen = 16384,
};
@@ -425,125 +380,6 @@ MODULE_PARM_DESC(buflen, "I/O buffer size");
#endif /* CONFIG_USB_FILE_STORAGE_TEST */
-/*-------------------------------------------------------------------------*/
-
-/* SCSI device types */
-#define TYPE_DISK 0x00
-#define TYPE_CDROM 0x05
-
-/* USB protocol value = the transport method */
-#define USB_PR_CBI 0x00 // Control/Bulk/Interrupt
-#define USB_PR_CB 0x01 // Control/Bulk w/o interrupt
-#define USB_PR_BULK 0x50 // Bulk-only
-
-/* USB subclass value = the protocol encapsulation */
-#define USB_SC_RBC 0x01 // Reduced Block Commands (flash)
-#define USB_SC_8020 0x02 // SFF-8020i, MMC-2, ATAPI (CD-ROM)
-#define USB_SC_QIC 0x03 // QIC-157 (tape)
-#define USB_SC_UFI 0x04 // UFI (floppy)
-#define USB_SC_8070 0x05 // SFF-8070i (removable)
-#define USB_SC_SCSI 0x06 // Transparent SCSI
-
-/* Bulk-only data structures */
-
-/* Command Block Wrapper */
-struct bulk_cb_wrap {
- __le32 Signature; // Contains 'USBC'
- u32 Tag; // Unique per command id
- __le32 DataTransferLength; // Size of the data
- u8 Flags; // Direction in bit 7
- u8 Lun; // LUN (normally 0)
- u8 Length; // Of the CDB, <= MAX_COMMAND_SIZE
- u8 CDB[16]; // Command Data Block
-};
-
-#define USB_BULK_CB_WRAP_LEN 31
-#define USB_BULK_CB_SIG 0x43425355 // Spells out USBC
-#define USB_BULK_IN_FLAG 0x80
-
-/* Command Status Wrapper */
-struct bulk_cs_wrap {
- __le32 Signature; // Should = 'USBS'
- u32 Tag; // Same as original command
- __le32 Residue; // Amount not transferred
- u8 Status; // See below
-};
-
-#define USB_BULK_CS_WRAP_LEN 13
-#define USB_BULK_CS_SIG 0x53425355 // Spells out 'USBS'
-#define USB_STATUS_PASS 0
-#define USB_STATUS_FAIL 1
-#define USB_STATUS_PHASE_ERROR 2
-
-/* Bulk-only class specific requests */
-#define USB_BULK_RESET_REQUEST 0xff
-#define USB_BULK_GET_MAX_LUN_REQUEST 0xfe
-
-
-/* CBI Interrupt data structure */
-struct interrupt_data {
- u8 bType;
- u8 bValue;
-};
-
-#define CBI_INTERRUPT_DATA_LEN 2
-
-/* CBI Accept Device-Specific Command request */
-#define USB_CBI_ADSC_REQUEST 0x00
-
-
-#define MAX_COMMAND_SIZE 16 // Length of a SCSI Command Data Block
-
-/* SCSI commands that we recognize */
-#define SC_FORMAT_UNIT 0x04
-#define SC_INQUIRY 0x12
-#define SC_MODE_SELECT_6 0x15
-#define SC_MODE_SELECT_10 0x55
-#define SC_MODE_SENSE_6 0x1a
-#define SC_MODE_SENSE_10 0x5a
-#define SC_PREVENT_ALLOW_MEDIUM_REMOVAL 0x1e
-#define SC_READ_6 0x08
-#define SC_READ_10 0x28
-#define SC_READ_12 0xa8
-#define SC_READ_CAPACITY 0x25
-#define SC_READ_FORMAT_CAPACITIES 0x23
-#define SC_READ_HEADER 0x44
-#define SC_READ_TOC 0x43
-#define SC_RELEASE 0x17
-#define SC_REQUEST_SENSE 0x03
-#define SC_RESERVE 0x16
-#define SC_SEND_DIAGNOSTIC 0x1d
-#define SC_START_STOP_UNIT 0x1b
-#define SC_SYNCHRONIZE_CACHE 0x35
-#define SC_TEST_UNIT_READY 0x00
-#define SC_VERIFY 0x2f
-#define SC_WRITE_6 0x0a
-#define SC_WRITE_10 0x2a
-#define SC_WRITE_12 0xaa
-
-/* SCSI Sense Key/Additional Sense Code/ASC Qualifier values */
-#define SS_NO_SENSE 0
-#define SS_COMMUNICATION_FAILURE 0x040800
-#define SS_INVALID_COMMAND 0x052000
-#define SS_INVALID_FIELD_IN_CDB 0x052400
-#define SS_LOGICAL_BLOCK_ADDRESS_OUT_OF_RANGE 0x052100
-#define SS_LOGICAL_UNIT_NOT_SUPPORTED 0x052500
-#define SS_MEDIUM_NOT_PRESENT 0x023a00
-#define SS_MEDIUM_REMOVAL_PREVENTED 0x055302
-#define SS_NOT_READY_TO_READY_TRANSITION 0x062800
-#define SS_RESET_OCCURRED 0x062900
-#define SS_SAVING_PARAMETERS_NOT_SUPPORTED 0x053900
-#define SS_UNRECOVERED_READ_ERROR 0x031100
-#define SS_WRITE_ERROR 0x030c02
-#define SS_WRITE_PROTECTED 0x072700
-
-#define SK(x) ((u8) ((x) >> 16)) // Sense Key byte, etc.
-#define ASC(x) ((u8) ((x) >> 8))
-#define ASCQ(x) ((u8) (x))
-
-
-/*-------------------------------------------------------------------------*/
-
/*
* These definitions will permit the compiler to avoid generating code for
* parts of the driver that aren't used in the non-TEST version. Even gcc
@@ -566,81 +402,8 @@ struct interrupt_data {
#endif /* CONFIG_USB_FILE_STORAGE_TEST */
-struct lun {
- struct file *filp;
- loff_t file_length;
- loff_t num_sectors;
-
- unsigned int ro : 1;
- unsigned int prevent_medium_removal : 1;
- unsigned int registered : 1;
- unsigned int info_valid : 1;
-
- u32 sense_data;
- u32 sense_data_info;
- u32 unit_attention_data;
-
- struct device dev;
-};
-
-#define backing_file_is_open(curlun) ((curlun)->filp != NULL)
-
-static struct lun *dev_to_lun(struct device *dev)
-{
- return container_of(dev, struct lun, dev);
-}
-
-
-/* Big enough to hold our biggest descriptor */
-#define EP0_BUFSIZE 256
-#define DELAYED_STATUS (EP0_BUFSIZE + 999) // An impossibly large value
-
-/* Number of buffers we will use. 2 is enough for double-buffering */
-#define NUM_BUFFERS 2
-
-enum fsg_buffer_state {
- BUF_STATE_EMPTY = 0,
- BUF_STATE_FULL,
- BUF_STATE_BUSY
-};
-
-struct fsg_buffhd {
- void *buf;
- enum fsg_buffer_state state;
- struct fsg_buffhd *next;
-
- /* The NetChip 2280 is faster, and handles some protocol faults
- * better, if we don't submit any short bulk-out read requests.
- * So we will record the intended request length here. */
- unsigned int bulk_out_intended_length;
-
- struct usb_request *inreq;
- int inreq_busy;
- struct usb_request *outreq;
- int outreq_busy;
-};
-
-enum fsg_state {
- FSG_STATE_COMMAND_PHASE = -10, // This one isn't used anywhere
- FSG_STATE_DATA_PHASE,
- FSG_STATE_STATUS_PHASE,
-
- FSG_STATE_IDLE = 0,
- FSG_STATE_ABORT_BULK_OUT,
- FSG_STATE_RESET,
- FSG_STATE_INTERFACE_CHANGE,
- FSG_STATE_CONFIG_CHANGE,
- FSG_STATE_DISCONNECT,
- FSG_STATE_EXIT,
- FSG_STATE_TERMINATED
-};
+/*-------------------------------------------------------------------------*/
-enum data_direction {
- DATA_DIR_UNKNOWN = 0,
- DATA_DIR_FROM_HOST,
- DATA_DIR_TO_HOST,
- DATA_DIR_NONE
-};
struct fsg_dev {
/* lock protects: state, all the req_busy's, and cbbuf_cmnd */
@@ -662,7 +425,7 @@ struct fsg_dev {
int intreq_busy;
struct fsg_buffhd *intr_buffhd;
- unsigned int bulk_out_maxpacket;
+ unsigned int bulk_out_maxpacket;
enum fsg_state state; // For exception handling
unsigned int exception_req_tag;
@@ -687,7 +450,7 @@ struct fsg_dev {
struct fsg_buffhd *next_buffhd_to_fill;
struct fsg_buffhd *next_buffhd_to_drain;
- struct fsg_buffhd buffhds[NUM_BUFFERS];
+ struct fsg_buffhd buffhds[FSG_NUM_BUFFERS];
int thread_wakeup_needed;
struct completion thread_notifier;
@@ -712,8 +475,8 @@ struct fsg_dev {
u8 cbbuf_cmnd[MAX_COMMAND_SIZE];
unsigned int nluns;
- struct lun *luns;
- struct lun *curlun;
+ struct fsg_lun *luns;
+ struct fsg_lun *curlun;
};
typedef void (*fsg_routine_t)(struct fsg_dev *);
@@ -739,49 +502,9 @@ static void set_bulk_out_req_length(struct fsg_dev *fsg,
static struct fsg_dev *the_fsg;
static struct usb_gadget_driver fsg_driver;
-static void close_backing_file(struct lun *curlun);
-
/*-------------------------------------------------------------------------*/
-#ifdef DUMP_MSGS
-
-static void dump_msg(struct fsg_dev *fsg, const char *label,
- const u8 *buf, unsigned int length)
-{
- if (length < 512) {
- DBG(fsg, "%s, length %u:\n", label, length);
- print_hex_dump(KERN_DEBUG, "", DUMP_PREFIX_OFFSET,
- 16, 1, buf, length, 0);
- }
-}
-
-static void dump_cdb(struct fsg_dev *fsg)
-{}
-
-#else
-
-static void dump_msg(struct fsg_dev *fsg, const char *label,
- const u8 *buf, unsigned int length)
-{}
-
-#ifdef VERBOSE_DEBUG
-
-static void dump_cdb(struct fsg_dev *fsg)
-{
- print_hex_dump(KERN_DEBUG, "SCSI CDB: ", DUMP_PREFIX_NONE,
- 16, 1, fsg->cmnd, fsg->cmnd_size, 0);
-}
-
-#else
-
-static void dump_cdb(struct fsg_dev *fsg)
-{}
-
-#endif /* VERBOSE_DEBUG */
-#endif /* DUMP_MSGS */
-
-
static int fsg_set_halt(struct fsg_dev *fsg, struct usb_ep *ep)
{
const char *name;
@@ -799,26 +522,11 @@ static int fsg_set_halt(struct fsg_dev *fsg, struct usb_ep *ep)
/*-------------------------------------------------------------------------*/
-/* Routines for unaligned data access */
-
-static u32 get_unaligned_be24(u8 *buf)
-{
- return 0xffffff & (u32) get_unaligned_be32(buf - 1);
-}
-
-
-/*-------------------------------------------------------------------------*/
-
/*
* DESCRIPTORS ... most are static, but strings and (full) configuration
* descriptors are built on demand. Also the (static) config and interface
* descriptors are adjusted during fsg_bind().
*/
-#define STRING_MANUFACTURER 1
-#define STRING_PRODUCT 2
-#define STRING_SERIAL 3
-#define STRING_CONFIG 4
-#define STRING_INTERFACE 5
/* There is only one configuration. */
#define CONFIG_VALUE 1
@@ -832,13 +540,13 @@ device_desc = {
.bDeviceClass = USB_CLASS_PER_INTERFACE,
/* The next three values can be overridden by module parameters */
- .idVendor = cpu_to_le16(DRIVER_VENDOR_ID),
- .idProduct = cpu_to_le16(DRIVER_PRODUCT_ID),
+ .idVendor = cpu_to_le16(FSG_VENDOR_ID),
+ .idProduct = cpu_to_le16(FSG_PRODUCT_ID),
.bcdDevice = cpu_to_le16(0xffff),
- .iManufacturer = STRING_MANUFACTURER,
- .iProduct = STRING_PRODUCT,
- .iSerialNumber = STRING_SERIAL,
+ .iManufacturer = FSG_STRING_MANUFACTURER,
+ .iProduct = FSG_STRING_PRODUCT,
+ .iSerialNumber = FSG_STRING_SERIAL,
.bNumConfigurations = 1,
};
@@ -850,86 +558,12 @@ config_desc = {
/* wTotalLength computed by usb_gadget_config_buf() */
.bNumInterfaces = 1,
.bConfigurationValue = CONFIG_VALUE,
- .iConfiguration = STRING_CONFIG,
+ .iConfiguration = FSG_STRING_CONFIG,
.bmAttributes = USB_CONFIG_ATT_ONE | USB_CONFIG_ATT_SELFPOWER,
.bMaxPower = CONFIG_USB_GADGET_VBUS_DRAW / 2,
};
-static struct usb_otg_descriptor
-otg_desc = {
- .bLength = sizeof(otg_desc),
- .bDescriptorType = USB_DT_OTG,
-
- .bmAttributes = USB_OTG_SRP,
-};
-
-/* There is only one interface. */
-static struct usb_interface_descriptor
-intf_desc = {
- .bLength = sizeof intf_desc,
- .bDescriptorType = USB_DT_INTERFACE,
-
- .bNumEndpoints = 2, // Adjusted during fsg_bind()
- .bInterfaceClass = USB_CLASS_MASS_STORAGE,
- .bInterfaceSubClass = USB_SC_SCSI, // Adjusted during fsg_bind()
- .bInterfaceProtocol = USB_PR_BULK, // Adjusted during fsg_bind()
- .iInterface = STRING_INTERFACE,
-};
-
-/* Three full-speed endpoint descriptors: bulk-in, bulk-out,
- * and interrupt-in. */
-
-static struct usb_endpoint_descriptor
-fs_bulk_in_desc = {
- .bLength = USB_DT_ENDPOINT_SIZE,
- .bDescriptorType = USB_DT_ENDPOINT,
-
- .bEndpointAddress = USB_DIR_IN,
- .bmAttributes = USB_ENDPOINT_XFER_BULK,
- /* wMaxPacketSize set by autoconfiguration */
-};
-
-static struct usb_endpoint_descriptor
-fs_bulk_out_desc = {
- .bLength = USB_DT_ENDPOINT_SIZE,
- .bDescriptorType = USB_DT_ENDPOINT,
-
- .bEndpointAddress = USB_DIR_OUT,
- .bmAttributes = USB_ENDPOINT_XFER_BULK,
- /* wMaxPacketSize set by autoconfiguration */
-};
-
-static struct usb_endpoint_descriptor
-fs_intr_in_desc = {
- .bLength = USB_DT_ENDPOINT_SIZE,
- .bDescriptorType = USB_DT_ENDPOINT,
-
- .bEndpointAddress = USB_DIR_IN,
- .bmAttributes = USB_ENDPOINT_XFER_INT,
- .wMaxPacketSize = cpu_to_le16(2),
- .bInterval = 32, // frames -> 32 ms
-};
-
-static const struct usb_descriptor_header *fs_function[] = {
- (struct usb_descriptor_header *) &otg_desc,
- (struct usb_descriptor_header *) &intf_desc,
- (struct usb_descriptor_header *) &fs_bulk_in_desc,
- (struct usb_descriptor_header *) &fs_bulk_out_desc,
- (struct usb_descriptor_header *) &fs_intr_in_desc,
- NULL,
-};
-#define FS_FUNCTION_PRE_EP_ENTRIES 2
-
-
-/*
- * USB 2.0 devices need to expose both high speed and full speed
- * descriptors, unless they only run at full speed.
- *
- * That means alternate endpoint descriptors (bigger packets)
- * and a "device qualifier" ... plus more construction options
- * for the config descriptor.
- */
static struct usb_qualifier_descriptor
dev_qualifier = {
.bLength = sizeof dev_qualifier,
@@ -941,78 +575,6 @@ dev_qualifier = {
.bNumConfigurations = 1,
};
-static struct usb_endpoint_descriptor
-hs_bulk_in_desc = {
- .bLength = USB_DT_ENDPOINT_SIZE,
- .bDescriptorType = USB_DT_ENDPOINT,
-
- /* bEndpointAddress copied from fs_bulk_in_desc during fsg_bind() */
- .bmAttributes = USB_ENDPOINT_XFER_BULK,
- .wMaxPacketSize = cpu_to_le16(512),
-};
-
-static struct usb_endpoint_descriptor
-hs_bulk_out_desc = {
- .bLength = USB_DT_ENDPOINT_SIZE,
- .bDescriptorType = USB_DT_ENDPOINT,
-
- /* bEndpointAddress copied from fs_bulk_out_desc during fsg_bind() */
- .bmAttributes = USB_ENDPOINT_XFER_BULK,
- .wMaxPacketSize = cpu_to_le16(512),
- .bInterval = 1, // NAK every 1 uframe
-};
-
-static struct usb_endpoint_descriptor
-hs_intr_in_desc = {
- .bLength = USB_DT_ENDPOINT_SIZE,
- .bDescriptorType = USB_DT_ENDPOINT,
-
- /* bEndpointAddress copied from fs_intr_in_desc during fsg_bind() */
- .bmAttributes = USB_ENDPOINT_XFER_INT,
- .wMaxPacketSize = cpu_to_le16(2),
- .bInterval = 9, // 2**(9-1) = 256 uframes -> 32 ms
-};
-
-static const struct usb_descriptor_header *hs_function[] = {
- (struct usb_descriptor_header *) &otg_desc,
- (struct usb_descriptor_header *) &intf_desc,
- (struct usb_descriptor_header *) &hs_bulk_in_desc,
- (struct usb_descriptor_header *) &hs_bulk_out_desc,
- (struct usb_descriptor_header *) &hs_intr_in_desc,
- NULL,
-};
-#define HS_FUNCTION_PRE_EP_ENTRIES 2
-
-/* Maxpacket and other transfer characteristics vary by speed. */
-static struct usb_endpoint_descriptor *
-ep_desc(struct usb_gadget *g, struct usb_endpoint_descriptor *fs,
- struct usb_endpoint_descriptor *hs)
-{
- if (gadget_is_dualspeed(g) && g->speed == USB_SPEED_HIGH)
- return hs;
- return fs;
-}
-
-
-/* The CBI specification limits the serial string to 12 uppercase hexadecimal
- * characters. */
-static char manufacturer[64];
-static char serial[13];
-
-/* Static strings, in UTF-8 (for simplicity we use only ASCII characters) */
-static struct usb_string strings[] = {
- {STRING_MANUFACTURER, manufacturer},
- {STRING_PRODUCT, longname},
- {STRING_SERIAL, serial},
- {STRING_CONFIG, "Self-powered"},
- {STRING_INTERFACE, "Mass Storage"},
- {}
-};
-
-static struct usb_gadget_strings stringtab = {
- .language = 0x0409, // en-us
- .strings = strings,
-};
/*
@@ -1032,10 +594,9 @@ static int populate_config_buf(struct usb_gadget *gadget,
if (gadget_is_dualspeed(gadget) && type == USB_DT_OTHER_SPEED_CONFIG)
speed = (USB_SPEED_FULL + USB_SPEED_HIGH) - speed;
- if (gadget_is_dualspeed(gadget) && speed == USB_SPEED_HIGH)
- function = hs_function;
- else
- function = fs_function;
+ function = gadget_is_dualspeed(gadget) && speed == USB_SPEED_HIGH
+ ? (const struct usb_descriptor_header **)fsg_hs_function
+ : (const struct usb_descriptor_header **)fsg_fs_function;
/* for now, don't advertise srp-only devices */
if (!gadget_is_otg(gadget))
@@ -1386,7 +947,7 @@ get_config:
VDBG(fsg, "get string descriptor\n");
/* wIndex == language code */
- value = usb_gadget_get_string(&stringtab,
+ value = usb_gadget_get_string(&fsg_stringtab,
w_value & 0xff, req->buf);
break;
}
@@ -1551,7 +1112,7 @@ static int sleep_thread(struct fsg_dev *fsg)
static int do_read(struct fsg_dev *fsg)
{
- struct lun *curlun = fsg->curlun;
+ struct fsg_lun *curlun = fsg->curlun;
u32 lba;
struct fsg_buffhd *bh;
int rc;
@@ -1677,7 +1238,7 @@ static int do_read(struct fsg_dev *fsg)
static int do_write(struct fsg_dev *fsg)
{
- struct lun *curlun = fsg->curlun;
+ struct fsg_lun *curlun = fsg->curlun;
u32 lba;
struct fsg_buffhd *bh;
int get_some_more;
@@ -1864,33 +1425,14 @@ static int do_write(struct fsg_dev *fsg)
/*-------------------------------------------------------------------------*/
-/* Sync the file data, don't bother with the metadata.
- * This code was copied from fs/buffer.c:sys_fdatasync(). */
-static int fsync_sub(struct lun *curlun)
-{
- struct file *filp = curlun->filp;
-
- if (curlun->ro || !filp)
- return 0;
- return vfs_fsync(filp, filp->f_path.dentry, 1);
-}
-
-static void fsync_all(struct fsg_dev *fsg)
-{
- int i;
-
- for (i = 0; i < fsg->nluns; ++i)
- fsync_sub(&fsg->luns[i]);
-}
-
static int do_synchronize_cache(struct fsg_dev *fsg)
{
- struct lun *curlun = fsg->curlun;
+ struct fsg_lun *curlun = fsg->curlun;
int rc;
/* We ignore the requested LBA and write out all file's
* dirty data buffers. */
- rc = fsync_sub(curlun);
+ rc = fsg_lun_fsync_sub(curlun);
if (rc)
curlun->sense_data = SS_WRITE_ERROR;
return 0;
@@ -1899,7 +1441,7 @@ static int do_synchronize_cache(struct fsg_dev *fsg)
/*-------------------------------------------------------------------------*/
-static void invalidate_sub(struct lun *curlun)
+static void invalidate_sub(struct fsg_lun *curlun)
{
struct file *filp = curlun->filp;
struct inode *inode = filp->f_path.dentry->d_inode;
@@ -1911,7 +1453,7 @@ static void invalidate_sub(struct lun *curlun)
static int do_verify(struct fsg_dev *fsg)
{
- struct lun *curlun = fsg->curlun;
+ struct fsg_lun *curlun = fsg->curlun;
u32 lba;
u32 verification_length;
struct fsg_buffhd *bh = fsg->next_buffhd_to_fill;
@@ -1944,7 +1486,7 @@ static int do_verify(struct fsg_dev *fsg)
file_offset = ((loff_t) lba) << 9;
/* Write out all the dirty buffers before invalidating them */
- fsync_sub(curlun);
+ fsg_lun_fsync_sub(curlun);
if (signal_pending(current))
return -EINTR;
@@ -2041,7 +1583,7 @@ static int do_inquiry(struct fsg_dev *fsg, struct fsg_buffhd *bh)
static int do_request_sense(struct fsg_dev *fsg, struct fsg_buffhd *bh)
{
- struct lun *curlun = fsg->curlun;
+ struct fsg_lun *curlun = fsg->curlun;
u8 *buf = (u8 *) bh->buf;
u32 sd, sdinfo;
int valid;
@@ -2095,7 +1637,7 @@ static int do_request_sense(struct fsg_dev *fsg, struct fsg_buffhd *bh)
static int do_read_capacity(struct fsg_dev *fsg, struct fsg_buffhd *bh)
{
- struct lun *curlun = fsg->curlun;
+ struct fsg_lun *curlun = fsg->curlun;
u32 lba = get_unaligned_be32(&fsg->cmnd[2]);
int pmi = fsg->cmnd[8];
u8 *buf = (u8 *) bh->buf;
@@ -2113,27 +1655,9 @@ static int do_read_capacity(struct fsg_dev *fsg, struct fsg_buffhd *bh)
}
-static void store_cdrom_address(u8 *dest, int msf, u32 addr)
-{
- if (msf) {
- /* Convert to Minutes-Seconds-Frames */
- addr >>= 2; /* Convert to 2048-byte frames */
- addr += 2*75; /* Lead-in occupies 2 seconds */
- dest[3] = addr % 75; /* Frames */
- addr /= 75;
- dest[2] = addr % 60; /* Seconds */
- addr /= 60;
- dest[1] = addr; /* Minutes */
- dest[0] = 0; /* Reserved */
- } else {
- /* Absolute sector */
- put_unaligned_be32(addr, dest);
- }
-}
-
static int do_read_header(struct fsg_dev *fsg, struct fsg_buffhd *bh)
{
- struct lun *curlun = fsg->curlun;
+ struct fsg_lun *curlun = fsg->curlun;
int msf = fsg->cmnd[1] & 0x02;
u32 lba = get_unaligned_be32(&fsg->cmnd[2]);
u8 *buf = (u8 *) bh->buf;
@@ -2156,7 +1680,7 @@ static int do_read_header(struct fsg_dev *fsg, struct fsg_buffhd *bh)
static int do_read_toc(struct fsg_dev *fsg, struct fsg_buffhd *bh)
{
- struct lun *curlun = fsg->curlun;
+ struct fsg_lun *curlun = fsg->curlun;
int msf = fsg->cmnd[1] & 0x02;
int start_track = fsg->cmnd[6];
u8 *buf = (u8 *) bh->buf;
@@ -2184,7 +1708,7 @@ static int do_read_toc(struct fsg_dev *fsg, struct fsg_buffhd *bh)
static int do_mode_sense(struct fsg_dev *fsg, struct fsg_buffhd *bh)
{
- struct lun *curlun = fsg->curlun;
+ struct fsg_lun *curlun = fsg->curlun;
int mscmnd = fsg->cmnd[0];
u8 *buf = (u8 *) bh->buf;
u8 *buf0 = buf;
@@ -2265,7 +1789,7 @@ static int do_mode_sense(struct fsg_dev *fsg, struct fsg_buffhd *bh)
static int do_start_stop(struct fsg_dev *fsg)
{
- struct lun *curlun = fsg->curlun;
+ struct fsg_lun *curlun = fsg->curlun;
int loej, start;
if (!mod_data.removable) {
@@ -2295,7 +1819,7 @@ static int do_start_stop(struct fsg_dev *fsg)
if (loej) { // Simulate an unload/eject
up_read(&fsg->filesem);
down_write(&fsg->filesem);
- close_backing_file(curlun);
+ fsg_lun_close(curlun);
up_write(&fsg->filesem);
down_read(&fsg->filesem);
}
@@ -2303,7 +1827,7 @@ static int do_start_stop(struct fsg_dev *fsg)
/* Our emulation doesn't support mounting; the medium is
* available for use as soon as it is loaded. */
- if (!backing_file_is_open(curlun)) {
+ if (!fsg_lun_is_open(curlun)) {
curlun->sense_data = SS_MEDIUM_NOT_PRESENT;
return -EINVAL;
}
@@ -2315,7 +1839,7 @@ static int do_start_stop(struct fsg_dev *fsg)
static int do_prevent_allow(struct fsg_dev *fsg)
{
- struct lun *curlun = fsg->curlun;
+ struct fsg_lun *curlun = fsg->curlun;
int prevent;
if (!mod_data.removable) {
@@ -2330,7 +1854,7 @@ static int do_prevent_allow(struct fsg_dev *fsg)
}
if (curlun->prevent_medium_removal && !prevent)
- fsync_sub(curlun);
+ fsg_lun_fsync_sub(curlun);
curlun->prevent_medium_removal = prevent;
return 0;
}
@@ -2339,7 +1863,7 @@ static int do_prevent_allow(struct fsg_dev *fsg)
static int do_read_format_capacities(struct fsg_dev *fsg,
struct fsg_buffhd *bh)
{
- struct lun *curlun = fsg->curlun;
+ struct fsg_lun *curlun = fsg->curlun;
u8 *buf = (u8 *) bh->buf;
buf[0] = buf[1] = buf[2] = 0;
@@ -2356,7 +1880,7 @@ static int do_read_format_capacities(struct fsg_dev *fsg,
static int do_mode_select(struct fsg_dev *fsg, struct fsg_buffhd *bh)
{
- struct lun *curlun = fsg->curlun;
+ struct fsg_lun *curlun = fsg->curlun;
/* We don't support MODE SELECT */
curlun->sense_data = SS_INVALID_COMMAND;
@@ -2599,7 +2123,7 @@ static int finish_reply(struct fsg_dev *fsg)
static int send_status(struct fsg_dev *fsg)
{
- struct lun *curlun = fsg->curlun;
+ struct fsg_lun *curlun = fsg->curlun;
struct fsg_buffhd *bh;
int rc;
u8 status = USB_STATUS_PASS;
@@ -2691,7 +2215,7 @@ static int check_command(struct fsg_dev *fsg, int cmnd_size,
int lun = fsg->cmnd[1] >> 5;
static const char dirletter[4] = {'u', 'o', 'i', 'n'};
char hdlen[20];
- struct lun *curlun;
+ struct fsg_lun *curlun;
/* Adjust the expected cmnd_size for protocol encapsulation padding.
* Transparent SCSI doesn't pad. */
@@ -2820,7 +2344,7 @@ static int check_command(struct fsg_dev *fsg, int cmnd_size,
/* If the medium isn't mounted and the command needs to access
* it, return an error. */
- if (curlun && !backing_file_is_open(curlun) && needs_medium) {
+ if (curlun && !fsg_lun_is_open(curlun) && needs_medium) {
curlun->sense_data = SS_MEDIUM_NOT_PRESENT;
return -EINVAL;
}
@@ -3075,8 +2599,8 @@ static int do_scsi_command(struct fsg_dev *fsg)
static int received_cbw(struct fsg_dev *fsg, struct fsg_buffhd *bh)
{
- struct usb_request *req = bh->outreq;
- struct bulk_cb_wrap *cbw = req->buf;
+ struct usb_request *req = bh->outreq;
+ struct fsg_bulk_cb_wrap *cbw = req->buf;
/* Was this a real packet? Should it be ignored? */
if (req->status || test_bit(IGNORE_BULK_OUT, &fsg->atomic_bitflags))
@@ -3105,7 +2629,7 @@ static int received_cbw(struct fsg_dev *fsg, struct fsg_buffhd *bh)
}
/* Is the CBW meaningful? */
- if (cbw->Lun >= MAX_LUNS || cbw->Flags & ~USB_BULK_IN_FLAG ||
+ if (cbw->Lun >= FSG_MAX_LUNS || cbw->Flags & ~USB_BULK_IN_FLAG ||
cbw->Length <= 0 || cbw->Length > MAX_COMMAND_SIZE) {
DBG(fsg, "non-meaningful CBW: lun = %u, flags = 0x%x, "
"cmdlen %u\n",
@@ -3238,7 +2762,7 @@ static int do_set_interface(struct fsg_dev *fsg, int altsetting)
reset:
/* Deallocate the requests */
- for (i = 0; i < NUM_BUFFERS; ++i) {
+ for (i = 0; i < FSG_NUM_BUFFERS; ++i) {
struct fsg_buffhd *bh = &fsg->buffhds[i];
if (bh->inreq) {
@@ -3276,12 +2800,14 @@ reset:
DBG(fsg, "set interface %d\n", altsetting);
/* Enable the endpoints */
- d = ep_desc(fsg->gadget, &fs_bulk_in_desc, &hs_bulk_in_desc);
+ d = fsg_ep_desc(fsg->gadget,
+ &fsg_fs_bulk_in_desc, &fsg_hs_bulk_in_desc);
if ((rc = enable_endpoint(fsg, fsg->bulk_in, d)) != 0)
goto reset;
fsg->bulk_in_enabled = 1;
- d = ep_desc(fsg->gadget, &fs_bulk_out_desc, &hs_bulk_out_desc);
+ d = fsg_ep_desc(fsg->gadget,
+ &fsg_fs_bulk_out_desc, &fsg_hs_bulk_out_desc);
if ((rc = enable_endpoint(fsg, fsg->bulk_out, d)) != 0)
goto reset;
fsg->bulk_out_enabled = 1;
@@ -3289,14 +2815,15 @@ reset:
clear_bit(IGNORE_BULK_OUT, &fsg->atomic_bitflags);
if (transport_is_cbi()) {
- d = ep_desc(fsg->gadget, &fs_intr_in_desc, &hs_intr_in_desc);
+ d = fsg_ep_desc(fsg->gadget,
+ &fsg_fs_intr_in_desc, &fsg_hs_intr_in_desc);
if ((rc = enable_endpoint(fsg, fsg->intr_in, d)) != 0)
goto reset;
fsg->intr_in_enabled = 1;
}
/* Allocate the requests */
- for (i = 0; i < NUM_BUFFERS; ++i) {
+ for (i = 0; i < FSG_NUM_BUFFERS; ++i) {
struct fsg_buffhd *bh = &fsg->buffhds[i];
if ((rc = alloc_request(fsg, fsg->bulk_in, &bh->inreq)) != 0)
@@ -3372,7 +2899,7 @@ static void handle_exception(struct fsg_dev *fsg)
struct fsg_buffhd *bh;
enum fsg_state old_state;
u8 new_config;
- struct lun *curlun;
+ struct fsg_lun *curlun;
unsigned int exception_req_tag;
int rc;
@@ -3392,7 +2919,7 @@ static void handle_exception(struct fsg_dev *fsg)
/* Cancel all the pending transfers */
if (fsg->intreq_busy)
usb_ep_dequeue(fsg->intr_in, fsg->intreq);
- for (i = 0; i < NUM_BUFFERS; ++i) {
+ for (i = 0; i < FSG_NUM_BUFFERS; ++i) {
bh = &fsg->buffhds[i];
if (bh->inreq_busy)
usb_ep_dequeue(fsg->bulk_in, bh->inreq);
@@ -3403,7 +2930,7 @@ static void handle_exception(struct fsg_dev *fsg)
/* Wait until everything is idle */
for (;;) {
num_active = fsg->intreq_busy;
- for (i = 0; i < NUM_BUFFERS; ++i) {
+ for (i = 0; i < FSG_NUM_BUFFERS; ++i) {
bh = &fsg->buffhds[i];
num_active += bh->inreq_busy + bh->outreq_busy;
}
@@ -3425,7 +2952,7 @@ static void handle_exception(struct fsg_dev *fsg)
* state, and the exception. Then invoke the handler. */
spin_lock_irq(&fsg->lock);
- for (i = 0; i < NUM_BUFFERS; ++i) {
+ for (i = 0; i < FSG_NUM_BUFFERS; ++i) {
bh = &fsg->buffhds[i];
bh->state = BUF_STATE_EMPTY;
}
@@ -3506,7 +3033,8 @@ static void handle_exception(struct fsg_dev *fsg)
break;
case FSG_STATE_DISCONNECT:
- fsync_all(fsg);
+ for (i = 0; i < fsg->nluns; ++i)
+ fsg_lun_fsync_sub(fsg->luns + i);
do_set_config(fsg, 0); // Unconfigured state
break;
@@ -3595,201 +3123,10 @@ static int fsg_main_thread(void *fsg_)
/*-------------------------------------------------------------------------*/
-/* If the next two routines are called while the gadget is registered,
- * the caller must own fsg->filesem for writing. */
-
-static int open_backing_file(struct lun *curlun, const char *filename)
-{
- int ro;
- struct file *filp = NULL;
- int rc = -EINVAL;
- struct inode *inode = NULL;
- loff_t size;
- loff_t num_sectors;
- loff_t min_sectors;
-
- /* R/W if we can, R/O if we must */
- ro = curlun->ro;
- if (!ro) {
- filp = filp_open(filename, O_RDWR | O_LARGEFILE, 0);
- if (-EROFS == PTR_ERR(filp))
- ro = 1;
- }
- if (ro)
- filp = filp_open(filename, O_RDONLY | O_LARGEFILE, 0);
- if (IS_ERR(filp)) {
- LINFO(curlun, "unable to open backing file: %s\n", filename);
- return PTR_ERR(filp);
- }
-
- if (!(filp->f_mode & FMODE_WRITE))
- ro = 1;
-
- if (filp->f_path.dentry)
- inode = filp->f_path.dentry->d_inode;
- if (inode && S_ISBLK(inode->i_mode)) {
- if (bdev_read_only(inode->i_bdev))
- ro = 1;
- } else if (!inode || !S_ISREG(inode->i_mode)) {
- LINFO(curlun, "invalid file type: %s\n", filename);
- goto out;
- }
-
- /* If we can't read the file, it's no good.
- * If we can't write the file, use it read-only. */
- if (!filp->f_op || !(filp->f_op->read || filp->f_op->aio_read)) {
- LINFO(curlun, "file not readable: %s\n", filename);
- goto out;
- }
- if (!(filp->f_op->write || filp->f_op->aio_write))
- ro = 1;
-
- size = i_size_read(inode->i_mapping->host);
- if (size < 0) {
- LINFO(curlun, "unable to find file size: %s\n", filename);
- rc = (int) size;
- goto out;
- }
- num_sectors = size >> 9; // File size in 512-byte blocks
- min_sectors = 1;
- if (mod_data.cdrom) {
- num_sectors &= ~3; // Reduce to a multiple of 2048
- min_sectors = 300*4; // Smallest track is 300 frames
- if (num_sectors >= 256*60*75*4) {
- num_sectors = (256*60*75 - 1) * 4;
- LINFO(curlun, "file too big: %s\n", filename);
- LINFO(curlun, "using only first %d blocks\n",
- (int) num_sectors);
- }
- }
- if (num_sectors < min_sectors) {
- LINFO(curlun, "file too small: %s\n", filename);
- rc = -ETOOSMALL;
- goto out;
- }
-
- get_file(filp);
- curlun->ro = ro;
- curlun->filp = filp;
- curlun->file_length = size;
- curlun->num_sectors = num_sectors;
- LDBG(curlun, "open backing file: %s\n", filename);
- rc = 0;
-
-out:
- filp_close(filp, current->files);
- return rc;
-}
-
-
-static void close_backing_file(struct lun *curlun)
-{
- if (curlun->filp) {
- LDBG(curlun, "close backing file\n");
- fput(curlun->filp);
- curlun->filp = NULL;
- }
-}
-
-
-static ssize_t show_ro(struct device *dev, struct device_attribute *attr, char *buf)
-{
- struct lun *curlun = dev_to_lun(dev);
-
- return sprintf(buf, "%d\n", curlun->ro);
-}
-
-static ssize_t show_file(struct device *dev, struct device_attribute *attr,
- char *buf)
-{
- struct lun *curlun = dev_to_lun(dev);
- struct fsg_dev *fsg = dev_get_drvdata(dev);
- char *p;
- ssize_t rc;
-
- down_read(&fsg->filesem);
- if (backing_file_is_open(curlun)) { // Get the complete pathname
- p = d_path(&curlun->filp->f_path, buf, PAGE_SIZE - 1);
- if (IS_ERR(p))
- rc = PTR_ERR(p);
- else {
- rc = strlen(p);
- memmove(buf, p, rc);
- buf[rc] = '\n'; // Add a newline
- buf[++rc] = 0;
- }
- } else { // No file, return 0 bytes
- *buf = 0;
- rc = 0;
- }
- up_read(&fsg->filesem);
- return rc;
-}
-
-
-static ssize_t store_ro(struct device *dev, struct device_attribute *attr,
- const char *buf, size_t count)
-{
- ssize_t rc = count;
- struct lun *curlun = dev_to_lun(dev);
- struct fsg_dev *fsg = dev_get_drvdata(dev);
- int i;
-
- if (sscanf(buf, "%d", &i) != 1)
- return -EINVAL;
-
- /* Allow the write-enable status to change only while the backing file
- * is closed. */
- down_read(&fsg->filesem);
- if (backing_file_is_open(curlun)) {
- LDBG(curlun, "read-only status change prevented\n");
- rc = -EBUSY;
- } else {
- curlun->ro = !!i;
- LDBG(curlun, "read-only status set to %d\n", curlun->ro);
- }
- up_read(&fsg->filesem);
- return rc;
-}
-
-static ssize_t store_file(struct device *dev, struct device_attribute *attr,
- const char *buf, size_t count)
-{
- struct lun *curlun = dev_to_lun(dev);
- struct fsg_dev *fsg = dev_get_drvdata(dev);
- int rc = 0;
-
- if (curlun->prevent_medium_removal && backing_file_is_open(curlun)) {
- LDBG(curlun, "eject attempt prevented\n");
- return -EBUSY; // "Door is locked"
- }
-
- /* Remove a trailing newline */
- if (count > 0 && buf[count-1] == '\n')
- ((char *) buf)[count-1] = 0; // Ugh!
-
- /* Eject current medium */
- down_write(&fsg->filesem);
- if (backing_file_is_open(curlun)) {
- close_backing_file(curlun);
- curlun->unit_attention_data = SS_MEDIUM_NOT_PRESENT;
- }
-
- /* Load new medium */
- if (count > 0 && buf[0]) {
- rc = open_backing_file(curlun, buf);
- if (rc == 0)
- curlun->unit_attention_data =
- SS_NOT_READY_TO_READY_TRANSITION;
- }
- up_write(&fsg->filesem);
- return (rc < 0 ? rc : count);
-}
-
/* The write permissions and store_xxx pointers are set in fsg_bind() */
-static DEVICE_ATTR(ro, 0444, show_ro, NULL);
-static DEVICE_ATTR(file, 0444, show_file, NULL);
+static DEVICE_ATTR(ro, 0444, fsg_show_ro, NULL);
+static DEVICE_ATTR(file, 0444, fsg_show_file, NULL);
/*-------------------------------------------------------------------------*/
@@ -3804,7 +3141,9 @@ static void fsg_release(struct kref *ref)
static void lun_release(struct device *dev)
{
- struct fsg_dev *fsg = dev_get_drvdata(dev);
+ struct rw_semaphore *filesem = dev_get_drvdata(dev);
+ struct fsg_dev *fsg =
+ container_of(filesem, struct fsg_dev, filesem);
kref_put(&fsg->ref, fsg_release);
}
@@ -3813,7 +3152,7 @@ static void /* __init_or_exit */ fsg_unbind(struct usb_gadget *gadget)
{
struct fsg_dev *fsg = get_gadget_data(gadget);
int i;
- struct lun *curlun;
+ struct fsg_lun *curlun;
struct usb_request *req = fsg->ep0req;
DBG(fsg, "unbind\n");
@@ -3825,7 +3164,7 @@ static void /* __init_or_exit */ fsg_unbind(struct usb_gadget *gadget)
if (curlun->registered) {
device_remove_file(&curlun->dev, &dev_attr_ro);
device_remove_file(&curlun->dev, &dev_attr_file);
- close_backing_file(curlun);
+ fsg_lun_close(curlun);
device_unregister(&curlun->dev);
curlun->registered = 0;
}
@@ -3841,7 +3180,7 @@ static void /* __init_or_exit */ fsg_unbind(struct usb_gadget *gadget)
}
/* Free the data buffers */
- for (i = 0; i < NUM_BUFFERS; ++i)
+ for (i = 0; i < FSG_NUM_BUFFERS; ++i)
kfree(fsg->buffhds[i].buf);
/* Free the request and buffer for endpoint 0 */
@@ -3948,7 +3287,7 @@ static int __init fsg_bind(struct usb_gadget *gadget)
struct fsg_dev *fsg = the_fsg;
int rc;
int i;
- struct lun *curlun;
+ struct fsg_lun *curlun;
struct usb_ep *ep;
struct usb_request *req;
char *pathbuf, *p;
@@ -3963,10 +3302,10 @@ static int __init fsg_bind(struct usb_gadget *gadget)
if (mod_data.removable) { // Enable the store_xxx attributes
dev_attr_file.attr.mode = 0644;
- dev_attr_file.store = store_file;
+ dev_attr_file.store = fsg_store_file;
if (!mod_data.cdrom) {
dev_attr_ro.attr.mode = 0644;
- dev_attr_ro.store = store_ro;
+ dev_attr_ro.store = fsg_store_ro;
}
}
@@ -3974,7 +3313,7 @@ static int __init fsg_bind(struct usb_gadget *gadget)
i = mod_data.nluns;
if (i == 0)
i = max(mod_data.num_filenames, 1u);
- if (i > MAX_LUNS) {
+ if (i > FSG_MAX_LUNS) {
ERROR(fsg, "invalid number of LUNs: %d\n", i);
rc = -EINVAL;
goto out;
@@ -3982,7 +3321,7 @@ static int __init fsg_bind(struct usb_gadget *gadget)
/* Create the LUNs, open their backing files, and register the
* LUN devices in sysfs. */
- fsg->luns = kzalloc(i * sizeof(struct lun), GFP_KERNEL);
+ fsg->luns = kzalloc(i * sizeof(struct fsg_lun), GFP_KERNEL);
if (!fsg->luns) {
rc = -ENOMEM;
goto out;
@@ -3991,13 +3330,14 @@ static int __init fsg_bind(struct usb_gadget *gadget)
for (i = 0; i < fsg->nluns; ++i) {
curlun = &fsg->luns[i];
- curlun->ro = mod_data.ro[i];
- if (mod_data.cdrom)
- curlun->ro = 1;
+ curlun->cdrom = !!mod_data.cdrom;
+ curlun->ro = mod_data.cdrom || mod_data.ro[i];
+ curlun->initially_ro = curlun->ro;
+ curlun->removable = mod_data.removable;
curlun->dev.release = lun_release;
curlun->dev.parent = &gadget->dev;
curlun->dev.driver = &fsg_driver.driver;
- dev_set_drvdata(&curlun->dev, fsg);
+ dev_set_drvdata(&curlun->dev, &fsg->filesem);
dev_set_name(&curlun->dev,"%s-lun%d",
dev_name(&gadget->dev), i);
@@ -4016,7 +3356,7 @@ static int __init fsg_bind(struct usb_gadget *gadget)
kref_get(&fsg->ref);
if (mod_data.file[i] && *mod_data.file[i]) {
- if ((rc = open_backing_file(curlun,
+ if ((rc = fsg_lun_open(curlun,
mod_data.file[i])) != 0)
goto out;
} else if (!mod_data.removable) {
@@ -4028,20 +3368,20 @@ static int __init fsg_bind(struct usb_gadget *gadget)
/* Find all the endpoints we will use */
usb_ep_autoconfig_reset(gadget);
- ep = usb_ep_autoconfig(gadget, &fs_bulk_in_desc);
+ ep = usb_ep_autoconfig(gadget, &fsg_fs_bulk_in_desc);
if (!ep)
goto autoconf_fail;
ep->driver_data = fsg; // claim the endpoint
fsg->bulk_in = ep;
- ep = usb_ep_autoconfig(gadget, &fs_bulk_out_desc);
+ ep = usb_ep_autoconfig(gadget, &fsg_fs_bulk_out_desc);
if (!ep)
goto autoconf_fail;
ep->driver_data = fsg; // claim the endpoint
fsg->bulk_out = ep;
if (transport_is_cbi()) {
- ep = usb_ep_autoconfig(gadget, &fs_intr_in_desc);
+ ep = usb_ep_autoconfig(gadget, &fsg_fs_intr_in_desc);
if (!ep)
goto autoconf_fail;
ep->driver_data = fsg; // claim the endpoint
@@ -4055,28 +3395,28 @@ static int __init fsg_bind(struct usb_gadget *gadget)
device_desc.bcdDevice = cpu_to_le16(mod_data.release);
i = (transport_is_cbi() ? 3 : 2); // Number of endpoints
- intf_desc.bNumEndpoints = i;
- intf_desc.bInterfaceSubClass = mod_data.protocol_type;
- intf_desc.bInterfaceProtocol = mod_data.transport_type;
- fs_function[i + FS_FUNCTION_PRE_EP_ENTRIES] = NULL;
+ fsg_intf_desc.bNumEndpoints = i;
+ fsg_intf_desc.bInterfaceSubClass = mod_data.protocol_type;
+ fsg_intf_desc.bInterfaceProtocol = mod_data.transport_type;
+ fsg_fs_function[i + FSG_FS_FUNCTION_PRE_EP_ENTRIES] = NULL;
if (gadget_is_dualspeed(gadget)) {
- hs_function[i + HS_FUNCTION_PRE_EP_ENTRIES] = NULL;
+ fsg_hs_function[i + FSG_HS_FUNCTION_PRE_EP_ENTRIES] = NULL;
/* Assume ep0 uses the same maxpacket value for both speeds */
dev_qualifier.bMaxPacketSize0 = fsg->ep0->maxpacket;
/* Assume endpoint addresses are the same for both speeds */
- hs_bulk_in_desc.bEndpointAddress =
- fs_bulk_in_desc.bEndpointAddress;
- hs_bulk_out_desc.bEndpointAddress =
- fs_bulk_out_desc.bEndpointAddress;
- hs_intr_in_desc.bEndpointAddress =
- fs_intr_in_desc.bEndpointAddress;
+ fsg_hs_bulk_in_desc.bEndpointAddress =
+ fsg_fs_bulk_in_desc.bEndpointAddress;
+ fsg_hs_bulk_out_desc.bEndpointAddress =
+ fsg_fs_bulk_out_desc.bEndpointAddress;
+ fsg_hs_intr_in_desc.bEndpointAddress =
+ fsg_fs_intr_in_desc.bEndpointAddress;
}
if (gadget_is_otg(gadget))
- otg_desc.bmAttributes |= USB_OTG_HNP;
+ fsg_otg_desc.bmAttributes |= USB_OTG_HNP;
rc = -ENOMEM;
@@ -4090,7 +3430,7 @@ static int __init fsg_bind(struct usb_gadget *gadget)
req->complete = ep0_complete;
/* Allocate the data buffers */
- for (i = 0; i < NUM_BUFFERS; ++i) {
+ for (i = 0; i < FSG_NUM_BUFFERS; ++i) {
struct fsg_buffhd *bh = &fsg->buffhds[i];
/* Allocate for the bulk-in endpoint. We assume that
@@ -4101,23 +3441,24 @@ static int __init fsg_bind(struct usb_gadget *gadget)
goto out;
bh->next = bh + 1;
}
- fsg->buffhds[NUM_BUFFERS - 1].next = &fsg->buffhds[0];
+ fsg->buffhds[FSG_NUM_BUFFERS - 1].next = &fsg->buffhds[0];
/* This should reflect the actual gadget power source */
usb_gadget_set_selfpowered(gadget);
- snprintf(manufacturer, sizeof manufacturer, "%s %s with %s",
+ snprintf(fsg_string_manufacturer, sizeof fsg_string_manufacturer,
+ "%s %s with %s",
init_utsname()->sysname, init_utsname()->release,
gadget->name);
/* On a real device, serial[] would be loaded from permanent
* storage. We just encode it from the driver version string. */
- for (i = 0; i < sizeof(serial) - 2; i += 2) {
+ for (i = 0; i < sizeof fsg_string_serial - 2; i += 2) {
unsigned char c = DRIVER_VERSION[i / 2];
if (!c)
break;
- sprintf(&serial[i], "%02X", c);
+ sprintf(&fsg_string_serial[i], "%02X", c);
}
fsg->thread_task = kthread_create(fsg_main_thread, fsg,
@@ -4133,7 +3474,7 @@ static int __init fsg_bind(struct usb_gadget *gadget)
pathbuf = kmalloc(PATH_MAX, GFP_KERNEL);
for (i = 0; i < fsg->nluns; ++i) {
curlun = &fsg->luns[i];
- if (backing_file_is_open(curlun)) {
+ if (fsg_lun_is_open(curlun)) {
p = NULL;
if (pathbuf) {
p = d_path(&curlun->filp->f_path,
@@ -4203,7 +3544,7 @@ static struct usb_gadget_driver fsg_driver = {
#else
.speed = USB_SPEED_FULL,
#endif
- .function = (char *) longname,
+ .function = (char *) fsg_string_product,
.bind = fsg_bind,
.unbind = fsg_unbind,
.disconnect = fsg_disconnect,
@@ -4212,7 +3553,7 @@ static struct usb_gadget_driver fsg_driver = {
.resume = fsg_resume,
.driver = {
- .name = (char *) shortname,
+ .name = DRIVER_NAME,
.owner = THIS_MODULE,
// .release = ...
// .suspend = ...
diff --git a/drivers/usb/gadget/mass_storage.c b/drivers/usb/gadget/mass_storage.c
new file mode 100644
index 0000000..19619fb
--- /dev/null
+++ b/drivers/usb/gadget/mass_storage.c
@@ -0,0 +1,240 @@
+/*
+ * mass_storage.c -- Mass Storage USB Gadget
+ *
+ * Copyright (C) 2003-2008 Alan Stern
+ * Copyright (C) 2009 Samsung Electronics
+ * Author: Michal Nazarewicz <m.nazarewicz@samsung.com>
+ * 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 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
+ */
+
+
+/*
+ * The Mass Storage Gadget acts as a USB Mass Storage device,
+ * appearing to the host as a disk drive or as a CD-ROM drive. In
+ * addition to providing an example of a genuinely useful gadget
+ * driver for a USB device, it also illustrates a technique of
+ * double-buffering for increased throughput. Last but not least, it
+ * gives an easy way to probe the behavior of the Mass Storage drivers
+ * in a USB host.
+ *
+ * Since this file serves only administrative purposes and all the
+ * business logic is implemented in f_mass_storage.* file. Read
+ * comments in this file for more detailed description.
+ */
+
+
+#include <linux/kernel.h>
+#include <linux/utsname.h>
+#include <linux/usb/ch9.h>
+
+
+/*-------------------------------------------------------------------------*/
+
+#define DRIVER_DESC "Mass Storage Gadget"
+#define DRIVER_VERSION "2009/09/11"
+
+/*-------------------------------------------------------------------------*/
+
+/*
+ * 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_mass_storage.c"
+
+/*-------------------------------------------------------------------------*/
+
+static struct usb_device_descriptor msg_device_desc = {
+ .bLength = sizeof msg_device_desc,
+ .bDescriptorType = USB_DT_DEVICE,
+
+ .bcdUSB = cpu_to_le16(0x0200),
+ .bDeviceClass = USB_CLASS_PER_INTERFACE,
+
+ /* Vendor and product id can be overridden by module parameters. */
+ .idVendor = cpu_to_le16(FSG_VENDOR_ID),
+ .idProduct = cpu_to_le16(FSG_PRODUCT_ID),
+ /* .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
+#define STRING_CONFIGURATION_IDX 2
+
+static char manufacturer[50];
+
+static struct usb_string strings_dev[] = {
+ [STRING_MANUFACTURER_IDX].s = manufacturer,
+ [STRING_PRODUCT_IDX].s = DRIVER_DESC,
+ [STRING_CONFIGURATION_IDX].s = "Self Powered",
+ { } /* 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,
+};
+
+
+
+/****************************** Configurations ******************************/
+
+static struct fsg_module_parameters mod_data = {
+ .stall = 1
+};
+FSG_MODULE_PARAMETERS(/* no prefix */, mod_data);
+
+static unsigned long msg_registered = 0;
+static void msg_cleanup(void);
+
+static int __init msg_do_config(struct usb_configuration *c)
+{
+ struct fsg_common *common;
+ struct fsg_config config;
+ int ret;
+
+ if (gadget_is_otg(c->cdev->gadget)) {
+ c->descriptors = otg_desc;
+ c->bmAttributes |= USB_CONFIG_ATT_WAKEUP;
+ }
+
+ fsg_config_from_params(&config, &mod_data);
+ config.thread_exits = (void(*)(struct fsg_common*))&msg_cleanup;
+ common = fsg_common_init(0, c->cdev, &config);
+ if (IS_ERR(common))
+ return PTR_ERR(common);
+
+ ret = fsg_add(c->cdev, c, common);
+ fsg_common_put(common);
+ return ret;
+}
+
+static struct usb_configuration msg_config_driver = {
+ .label = "Linux File-Backed Storage",
+ .bind = msg_do_config,
+ .bConfigurationValue = 1,
+ /* .iConfiguration = DYNAMIC */
+ .bmAttributes = USB_CONFIG_ATT_SELFPOWER,
+};
+
+
+
+/****************************** Gadget Bind ******************************/
+
+
+static int __init msg_bind(struct usb_composite_dev *cdev)
+{
+ struct usb_gadget *gadget = cdev->gadget;
+ int status;
+
+ /* 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)
+ return status;
+ strings_dev[STRING_MANUFACTURER_IDX].id = status;
+ msg_device_desc.iManufacturer = status;
+
+ status = usb_string_id(cdev);
+ if (status < 0)
+ return status;
+ strings_dev[STRING_PRODUCT_IDX].id = status;
+ msg_device_desc.iProduct = status;
+
+ status = usb_string_id(cdev);
+ if (status < 0)
+ return status;
+ strings_dev[STRING_CONFIGURATION_IDX].id = status;
+ msg_config_driver.iConfiguration = status;
+
+ /* register our second configuration */
+ status = usb_add_config(cdev, &msg_config_driver);
+ if (status < 0)
+ return status;
+
+ dev_info(&gadget->dev, DRIVER_DESC ", version: " DRIVER_VERSION "\n");
+ set_bit(0, &msg_registered);
+ return 0;
+}
+
+
+/****************************** Some noise ******************************/
+
+
+static struct usb_composite_driver msg_driver = {
+ .name = "g_mass_storage",
+ .dev = &msg_device_desc,
+ .strings = dev_strings,
+ .bind = msg_bind,
+};
+
+MODULE_DESCRIPTION(DRIVER_DESC);
+MODULE_AUTHOR("Michal Nazarewicz");
+MODULE_LICENSE("GPL");
+
+static int __init msg_init(void)
+{
+ return usb_composite_register(&msg_driver);
+}
+module_init(msg_init);
+
+static void msg_cleanup(void)
+{
+ if (test_and_clear_bit(0, &msg_registered))
+ usb_composite_unregister(&msg_driver);
+}
+module_exit(msg_cleanup);
diff --git a/drivers/usb/gadget/multi.c b/drivers/usb/gadget/multi.c
new file mode 100644
index 0000000..4295601
--- /dev/null
+++ b/drivers/usb/gadget/multi.c
@@ -0,0 +1,358 @@
+/*
+ * multi.c -- Multifunction Composite driver
+ *
+ * Copyright (C) 2008 David Brownell
+ * Copyright (C) 2008 Nokia Corporation
+ * Copyright (C) 2009 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
+ */
+
+
+#include <linux/kernel.h>
+#include <linux/utsname.h>
+
+
+#if defined USB_ETH_RNDIS
+# undef USB_ETH_RNDIS
+#endif
+#ifdef CONFIG_USB_ETH_RNDIS
+# define USB_ETH_RNDIS y
+#endif
+
+
+#define DRIVER_DESC "Multifunction Composite Gadget"
+#define DRIVER_VERSION "2009/07/21"
+
+/*-------------------------------------------------------------------------*/
+
+#define MULTI_VENDOR_NUM 0x0525 /* XXX NetChip */
+#define MULTI_PRODUCT_NUM 0xa4ab /* XXX */
+
+/*-------------------------------------------------------------------------*/
+
+/*
+ * 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 "u_serial.c"
+#include "f_acm.c"
+
+#include "f_ecm.c"
+#include "f_subset.c"
+#ifdef USB_ETH_RNDIS
+# include "f_rndis.c"
+# include "rndis.c"
+#endif
+#include "u_ether.c"
+
+#undef DBG /* u_ether.c has broken idea about macros */
+#undef VDBG /* so clean up after it */
+#undef ERROR
+#undef INFO
+#include "f_mass_storage.c"
+
+/*-------------------------------------------------------------------------*/
+
+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, */
+ .bDeviceClass = 0xEF,
+ .bDeviceSubClass = 2,
+ .bDeviceProtocol = 1,
+ /* .bMaxPacketSize0 = f(hardware) */
+
+ /* Vendor and product id can be overridden by module parameters. */
+ .idVendor = cpu_to_le16(MULTI_VENDOR_NUM),
+ .idProduct = cpu_to_le16(MULTI_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];
+
+
+
+/****************************** Configurations ******************************/
+
+static struct fsg_module_parameters mod_data = {
+ .stall = 1
+};
+FSG_MODULE_PARAMETERS(/* no prefix */, mod_data);
+
+static struct fsg_common *fsg_common;
+
+
+#ifdef USB_ETH_RNDIS
+
+static int __init rndis_do_config(struct usb_configuration *c)
+{
+ int ret;
+
+ if (gadget_is_otg(c->cdev->gadget)) {
+ c->descriptors = otg_desc;
+ c->bmAttributes |= USB_CONFIG_ATT_WAKEUP;
+ }
+
+ ret = rndis_bind_config(c, hostaddr);
+ if (ret < 0)
+ return ret;
+
+ ret = acm_bind_config(c, 0);
+ if (ret < 0)
+ return ret;
+
+ ret = fsg_add(c->cdev, c, fsg_common);
+ if (ret < 0)
+ return ret;
+
+ return 0;
+}
+
+static struct usb_configuration rndis_config_driver = {
+ .label = "Multifunction Composite (RNDIS + MS + ACM)",
+ .bind = rndis_do_config,
+ .bConfigurationValue = 2,
+ /* .iConfiguration = DYNAMIC */
+ .bmAttributes = USB_CONFIG_ATT_SELFPOWER,
+};
+
+#endif
+
+#ifdef CONFIG_USB_G_MULTI_CDC
+
+static int __init cdc_do_config(struct usb_configuration *c)
+{
+ int ret;
+
+ if (gadget_is_otg(c->cdev->gadget)) {
+ c->descriptors = otg_desc;
+ c->bmAttributes |= USB_CONFIG_ATT_WAKEUP;
+ }
+
+ ret = ecm_bind_config(c, hostaddr);
+ if (ret < 0)
+ return ret;
+
+ ret = acm_bind_config(c, 0);
+ if (ret < 0)
+ return ret;
+
+ ret = fsg_add(c->cdev, c, fsg_common);
+ if (ret < 0)
+ return ret;
+ if (ret < 0)
+ return ret;
+
+ return 0;
+}
+
+static struct usb_configuration cdc_config_driver = {
+ .label = "Multifunction Composite (CDC + MS + ACM)",
+ .bind = cdc_do_config,
+ .bConfigurationValue = 1,
+ /* .iConfiguration = DYNAMIC */
+ .bmAttributes = USB_CONFIG_ATT_SELFPOWER,
+};
+
+#endif
+
+
+
+/****************************** Gadget Bind ******************************/
+
+
+static int __init multi_bind(struct usb_composite_dev *cdev)
+{
+ struct usb_gadget *gadget = cdev->gadget;
+ int status, gcnum;
+
+ if (!can_support_ecm(cdev->gadget)) {
+ dev_err(&gadget->dev, "controller '%s' not usable\n",
+ gadget->name);
+ return -EINVAL;
+ }
+
+ /* set up network link layer */
+ status = gether_setup(cdev->gadget, hostaddr);
+ if (status < 0)
+ return status;
+
+ /* set up serial link layer */
+ status = gserial_setup(cdev->gadget, 1);
+ if (status < 0)
+ goto fail0;
+
+ /* set up mass storage function */
+ fsg_common = fsg_common_from_params(0, cdev, &mod_data);
+ if (IS_ERR(fsg_common)) {
+ status = PTR_ERR(fsg_common);
+ goto fail1;
+ }
+
+
+ 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.
+ */
+ WARNING(cdev, "controller '%s' not recognized\n",
+ gadget->name);
+ 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 fail2;
+ strings_dev[STRING_MANUFACTURER_IDX].id = status;
+ device_desc.iManufacturer = status;
+
+ status = usb_string_id(cdev);
+ if (status < 0)
+ goto fail2;
+ strings_dev[STRING_PRODUCT_IDX].id = status;
+ device_desc.iProduct = status;
+
+#ifdef USB_ETH_RNDIS
+ /* register our first configuration */
+ status = usb_add_config(cdev, &rndis_config_driver);
+ if (status < 0)
+ goto fail2;
+#endif
+
+#ifdef CONFIG_USB_G_MULTI_CDC
+ /* register our second configuration */
+ status = usb_add_config(cdev, &cdc_config_driver);
+ if (status < 0)
+ goto fail2;
+#endif
+
+ dev_info(&gadget->dev, DRIVER_DESC ", version: " DRIVER_VERSION "\n");
+ fsg_common_put(fsg_common);
+ return 0;
+
+fail2:
+ fsg_common_put(fsg_common);
+fail1:
+ gserial_cleanup();
+fail0:
+ gether_cleanup();
+ return status;
+}
+
+static int __exit multi_unbind(struct usb_composite_dev *cdev)
+{
+ gserial_cleanup();
+ gether_cleanup();
+ return 0;
+}
+
+
+/****************************** Some noise ******************************/
+
+
+static struct usb_composite_driver multi_driver = {
+ .name = "g_multi",
+ .dev = &device_desc,
+ .strings = dev_strings,
+ .bind = multi_bind,
+ .unbind = __exit_p(multi_unbind),
+};
+
+MODULE_DESCRIPTION(DRIVER_DESC);
+MODULE_AUTHOR("Michal Nazarewicz");
+MODULE_LICENSE("GPL");
+
+static int __init g_multi_init(void)
+{
+ return usb_composite_register(&multi_driver);
+}
+module_init(g_multi_init);
+
+static void __exit g_multi_cleanup(void)
+{
+ usb_composite_unregister(&multi_driver);
+}
+module_exit(g_multi_cleanup);
diff --git a/drivers/usb/gadget/storage_common.c b/drivers/usb/gadget/storage_common.c
new file mode 100644
index 0000000..868d8ee
--- /dev/null
+++ b/drivers/usb/gadget/storage_common.c
@@ -0,0 +1,778 @@
+/*
+ * storage_common.c -- Common definitions for mass storage functionality
+ *
+ * Copyright (C) 2003-2008 Alan Stern
+ * Copyeight (C) 2009 Samsung Electronics
+ * Author: Michal Nazarewicz (m.nazarewicz@samsung.com)
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+
+
+/*
+ * This file requires the following identifiers used in USB strings to
+ * be defined (each of type pointer to char):
+ * - fsg_string_manufacturer -- name of the manufacturer
+ * - fsg_string_product -- name of the product
+ * - fsg_string_serial -- product's serial
+ * - fsg_string_config -- name of the configuration
+ * - fsg_string_interface -- name of the interface
+ * The first four are only needed when FSG_DESCRIPTORS_DEVICE_STRINGS
+ * macro is defined prior to including this file.
+ */
+
+/*
+ * When FSG_NO_INTR_EP is defined fsg_fs_intr_in_desc and
+ * fsg_hs_intr_in_desc objects as well as
+ * FSG_FS_FUNCTION_PRE_EP_ENTRIES and FSG_HS_FUNCTION_PRE_EP_ENTRIES
+ * macros are not defined.
+ *
+ * When FSG_NO_DEVICE_STRINGS is defined FSG_STRING_MANUFACTURER,
+ * FSG_STRING_PRODUCT, FSG_STRING_SERIAL and FSG_STRING_CONFIG are not
+ * defined (as well as corresponding entries in string tables are
+ * missing) and FSG_STRING_INTERFACE has value of zero.
+ *
+ * When FSG_NO_OTG is defined fsg_otg_desc won't be defined.
+ */
+
+/*
+ * When FSG_BUFFHD_STATIC_BUFFER is defined when this file is included
+ * the fsg_buffhd structure's buf field will be an array of FSG_BUFLEN
+ * characters rather then a pointer to void.
+ */
+
+
+#include <asm/unaligned.h>
+
+
+/* Thanks to NetChip Technologies for donating this product ID.
+ *
+ * DO NOT REUSE THESE IDs with any other driver!! Ever!!
+ * Instead: allocate your own, using normal USB-IF procedures. */
+#define FSG_VENDOR_ID 0x0525 /* NetChip */
+#define FSG_PRODUCT_ID 0xa4a5 /* Linux-USB File-backed Storage Gadget */
+
+
+/*-------------------------------------------------------------------------*/
+
+
+#ifndef DEBUG
+#undef VERBOSE_DEBUG
+#undef DUMP_MSGS
+#endif /* !DEBUG */
+
+#ifdef VERBOSE_DEBUG
+#define VLDBG LDBG
+#else
+#define VLDBG(lun, fmt, args...) do { } while (0)
+#endif /* VERBOSE_DEBUG */
+
+#define LDBG(lun, fmt, args...) dev_dbg (&(lun)->dev, fmt, ## args)
+#define LERROR(lun, fmt, args...) dev_err (&(lun)->dev, fmt, ## args)
+#define LWARN(lun, fmt, args...) dev_warn(&(lun)->dev, fmt, ## args)
+#define LINFO(lun, fmt, args...) dev_info(&(lun)->dev, fmt, ## args)
+
+/* Keep those macros in sync with thos in
+ * include/linux/ubs/composite.h or else GCC will complain. If they
+ * are identical (the same names of arguments, white spaces in the
+ * same places) GCC will allow redefinition otherwise (even if some
+ * white space is removed or added) warning will be issued. No
+ * checking if those symbols is defined is performed because warning
+ * is desired when those macros were defined by someone else to mean
+ * something else. */
+#define DBG(d, fmt, args...) dev_dbg(&(d)->gadget->dev , fmt , ## args)
+#define VDBG(d, fmt, args...) dev_vdbg(&(d)->gadget->dev , fmt , ## args)
+#define ERROR(d, fmt, args...) dev_err(&(d)->gadget->dev , fmt , ## args)
+#define WARNING(d, fmt, args...) dev_warn(&(d)->gadget->dev , fmt , ## args)
+#define INFO(d, fmt, args...) dev_info(&(d)->gadget->dev , fmt , ## args)
+
+
+
+#ifdef DUMP_MSGS
+
+# define dump_msg(fsg, /* const char * */ label, \
+ /* const u8 * */ buf, /* unsigned */ length) do { \
+ if (length < 512) { \
+ DBG(fsg, "%s, length %u:\n", label, length); \
+ print_hex_dump(KERN_DEBUG, "", DUMP_PREFIX_OFFSET, \
+ 16, 1, buf, length, 0); \
+ } \
+} while (0)
+
+# define dump_cdb(fsg) do { } while (0)
+
+#else
+
+# define dump_msg(fsg, /* const char * */ label, \
+ /* const u8 * */ buf, /* unsigned */ length) do { } while (0)
+
+# ifdef VERBOSE_DEBUG
+
+# define dump_cdb(fsg) \
+ print_hex_dump(KERN_DEBUG, "SCSI CDB: ", DUMP_PREFIX_NONE, \
+ 16, 1, (fsg)->cmnd, (fsg)->cmnd_size, 0) \
+
+# else
+
+# define dump_cdb(fsg) do { } while (0)
+
+# endif /* VERBOSE_DEBUG */
+
+#endif /* DUMP_MSGS */
+
+
+
+
+
+/*-------------------------------------------------------------------------*/
+
+/* SCSI device types */
+#define TYPE_DISK 0x00
+#define TYPE_CDROM 0x05
+
+/* USB protocol value = the transport method */
+#define USB_PR_CBI 0x00 /* Control/Bulk/Interrupt */
+#define USB_PR_CB 0x01 /* Control/Bulk w/o interrupt */
+#define USB_PR_BULK 0x50 /* Bulk-only */
+
+/* USB subclass value = the protocol encapsulation */
+#define USB_SC_RBC 0x01 /* Reduced Block Commands (flash) */
+#define USB_SC_8020 0x02 /* SFF-8020i, MMC-2, ATAPI (CD-ROM) */
+#define USB_SC_QIC 0x03 /* QIC-157 (tape) */
+#define USB_SC_UFI 0x04 /* UFI (floppy) */
+#define USB_SC_8070 0x05 /* SFF-8070i (removable) */
+#define USB_SC_SCSI 0x06 /* Transparent SCSI */
+
+/* Bulk-only data structures */
+
+/* Command Block Wrapper */
+struct fsg_bulk_cb_wrap {
+ __le32 Signature; /* Contains 'USBC' */
+ u32 Tag; /* Unique per command id */
+ __le32 DataTransferLength; /* Size of the data */
+ u8 Flags; /* Direction in bit 7 */
+ u8 Lun; /* LUN (normally 0) */
+ u8 Length; /* Of the CDB, <= MAX_COMMAND_SIZE */
+ u8 CDB[16]; /* Command Data Block */
+};
+
+#define USB_BULK_CB_WRAP_LEN 31
+#define USB_BULK_CB_SIG 0x43425355 /* Spells out USBC */
+#define USB_BULK_IN_FLAG 0x80
+
+/* Command Status Wrapper */
+struct bulk_cs_wrap {
+ __le32 Signature; /* Should = 'USBS' */
+ u32 Tag; /* Same as original command */
+ __le32 Residue; /* Amount not transferred */
+ u8 Status; /* See below */
+};
+
+#define USB_BULK_CS_WRAP_LEN 13
+#define USB_BULK_CS_SIG 0x53425355 /* Spells out 'USBS' */
+#define USB_STATUS_PASS 0
+#define USB_STATUS_FAIL 1
+#define USB_STATUS_PHASE_ERROR 2
+
+/* Bulk-only class specific requests */
+#define USB_BULK_RESET_REQUEST 0xff
+#define USB_BULK_GET_MAX_LUN_REQUEST 0xfe
+
+
+/* CBI Interrupt data structure */
+struct interrupt_data {
+ u8 bType;
+ u8 bValue;
+};
+
+#define CBI_INTERRUPT_DATA_LEN 2
+
+/* CBI Accept Device-Specific Command request */
+#define USB_CBI_ADSC_REQUEST 0x00
+
+
+/* Length of a SCSI Command Data Block */
+#define MAX_COMMAND_SIZE 16
+
+/* SCSI commands that we recognize */
+#define SC_FORMAT_UNIT 0x04
+#define SC_INQUIRY 0x12
+#define SC_MODE_SELECT_6 0x15
+#define SC_MODE_SELECT_10 0x55
+#define SC_MODE_SENSE_6 0x1a
+#define SC_MODE_SENSE_10 0x5a
+#define SC_PREVENT_ALLOW_MEDIUM_REMOVAL 0x1e
+#define SC_READ_6 0x08
+#define SC_READ_10 0x28
+#define SC_READ_12 0xa8
+#define SC_READ_CAPACITY 0x25
+#define SC_READ_FORMAT_CAPACITIES 0x23
+#define SC_READ_HEADER 0x44
+#define SC_READ_TOC 0x43
+#define SC_RELEASE 0x17
+#define SC_REQUEST_SENSE 0x03
+#define SC_RESERVE 0x16
+#define SC_SEND_DIAGNOSTIC 0x1d
+#define SC_START_STOP_UNIT 0x1b
+#define SC_SYNCHRONIZE_CACHE 0x35
+#define SC_TEST_UNIT_READY 0x00
+#define SC_VERIFY 0x2f
+#define SC_WRITE_6 0x0a
+#define SC_WRITE_10 0x2a
+#define SC_WRITE_12 0xaa
+
+/* SCSI Sense Key/Additional Sense Code/ASC Qualifier values */
+#define SS_NO_SENSE 0
+#define SS_COMMUNICATION_FAILURE 0x040800
+#define SS_INVALID_COMMAND 0x052000
+#define SS_INVALID_FIELD_IN_CDB 0x052400
+#define SS_LOGICAL_BLOCK_ADDRESS_OUT_OF_RANGE 0x052100
+#define SS_LOGICAL_UNIT_NOT_SUPPORTED 0x052500
+#define SS_MEDIUM_NOT_PRESENT 0x023a00
+#define SS_MEDIUM_REMOVAL_PREVENTED 0x055302
+#define SS_NOT_READY_TO_READY_TRANSITION 0x062800
+#define SS_RESET_OCCURRED 0x062900
+#define SS_SAVING_PARAMETERS_NOT_SUPPORTED 0x053900
+#define SS_UNRECOVERED_READ_ERROR 0x031100
+#define SS_WRITE_ERROR 0x030c02
+#define SS_WRITE_PROTECTED 0x072700
+
+#define SK(x) ((u8) ((x) >> 16)) /* Sense Key byte, etc. */
+#define ASC(x) ((u8) ((x) >> 8))
+#define ASCQ(x) ((u8) (x))
+
+
+/*-------------------------------------------------------------------------*/
+
+
+struct fsg_lun {
+ struct file *filp;
+ loff_t file_length;
+ loff_t num_sectors;
+
+ unsigned int initially_ro:1;
+ unsigned int ro:1;
+ unsigned int removable:1;
+ unsigned int cdrom:1;
+ unsigned int prevent_medium_removal:1;
+ unsigned int registered:1;
+ unsigned int info_valid:1;
+
+ u32 sense_data;
+ u32 sense_data_info;
+ u32 unit_attention_data;
+
+ struct device dev;
+};
+
+#define fsg_lun_is_open(curlun) ((curlun)->filp != NULL)
+
+static struct fsg_lun *fsg_lun_from_dev(struct device *dev)
+{
+ return container_of(dev, struct fsg_lun, dev);
+}
+
+
+/* Big enough to hold our biggest descriptor */
+#define EP0_BUFSIZE 256
+#define DELAYED_STATUS (EP0_BUFSIZE + 999) /* An impossibly large value */
+
+/* Number of buffers we will use. 2 is enough for double-buffering */
+#define FSG_NUM_BUFFERS 2
+
+/* Default size of buffer length. */
+#define FSG_BUFLEN ((u32)16384)
+
+/* Maximal number of LUNs supported in mass storage function */
+#define FSG_MAX_LUNS 8
+
+enum fsg_buffer_state {
+ BUF_STATE_EMPTY = 0,
+ BUF_STATE_FULL,
+ BUF_STATE_BUSY
+};
+
+struct fsg_buffhd {
+#ifdef FSG_BUFFHD_STATIC_BUFFER
+ char buf[FSG_BUFLEN];
+#else
+ void *buf;
+#endif
+ enum fsg_buffer_state state;
+ struct fsg_buffhd *next;
+
+ /* The NetChip 2280 is faster, and handles some protocol faults
+ * better, if we don't submit any short bulk-out read requests.
+ * So we will record the intended request length here. */
+ unsigned int bulk_out_intended_length;
+
+ struct usb_request *inreq;
+ int inreq_busy;
+ struct usb_request *outreq;
+ int outreq_busy;
+};
+
+enum fsg_state {
+ /* This one isn't used anywhere */
+ FSG_STATE_COMMAND_PHASE = -10,
+ FSG_STATE_DATA_PHASE,
+ FSG_STATE_STATUS_PHASE,
+
+ FSG_STATE_IDLE = 0,
+ FSG_STATE_ABORT_BULK_OUT,
+ FSG_STATE_RESET,
+ FSG_STATE_INTERFACE_CHANGE,
+ FSG_STATE_CONFIG_CHANGE,
+ FSG_STATE_DISCONNECT,
+ FSG_STATE_EXIT,
+ FSG_STATE_TERMINATED
+};
+
+enum data_direction {
+ DATA_DIR_UNKNOWN = 0,
+ DATA_DIR_FROM_HOST,
+ DATA_DIR_TO_HOST,
+ DATA_DIR_NONE
+};
+
+
+/*-------------------------------------------------------------------------*/
+
+
+static inline u32 get_unaligned_be24(u8 *buf)
+{
+ return 0xffffff & (u32) get_unaligned_be32(buf - 1);
+}
+
+
+/*-------------------------------------------------------------------------*/
+
+
+enum {
+#ifndef FSG_NO_DEVICE_STRINGS
+ FSG_STRING_MANUFACTURER = 1,
+ FSG_STRING_PRODUCT,
+ FSG_STRING_SERIAL,
+ FSG_STRING_CONFIG,
+#endif
+ FSG_STRING_INTERFACE
+};
+
+
+#ifndef FSG_NO_OTG
+static struct usb_otg_descriptor
+fsg_otg_desc = {
+ .bLength = sizeof fsg_otg_desc,
+ .bDescriptorType = USB_DT_OTG,
+
+ .bmAttributes = USB_OTG_SRP,
+};
+#endif
+
+/* There is only one interface. */
+
+static struct usb_interface_descriptor
+fsg_intf_desc = {
+ .bLength = sizeof fsg_intf_desc,
+ .bDescriptorType = USB_DT_INTERFACE,
+
+ .bNumEndpoints = 2, /* Adjusted during fsg_bind() */
+ .bInterfaceClass = USB_CLASS_MASS_STORAGE,
+ .bInterfaceSubClass = USB_SC_SCSI, /* Adjusted during fsg_bind() */
+ .bInterfaceProtocol = USB_PR_BULK, /* Adjusted during fsg_bind() */
+ .iInterface = FSG_STRING_INTERFACE,
+};
+
+/* Three full-speed endpoint descriptors: bulk-in, bulk-out,
+ * and interrupt-in. */
+
+static struct usb_endpoint_descriptor
+fsg_fs_bulk_in_desc = {
+ .bLength = USB_DT_ENDPOINT_SIZE,
+ .bDescriptorType = USB_DT_ENDPOINT,
+
+ .bEndpointAddress = USB_DIR_IN,
+ .bmAttributes = USB_ENDPOINT_XFER_BULK,
+ /* wMaxPacketSize set by autoconfiguration */
+};
+
+static struct usb_endpoint_descriptor
+fsg_fs_bulk_out_desc = {
+ .bLength = USB_DT_ENDPOINT_SIZE,
+ .bDescriptorType = USB_DT_ENDPOINT,
+
+ .bEndpointAddress = USB_DIR_OUT,
+ .bmAttributes = USB_ENDPOINT_XFER_BULK,
+ /* wMaxPacketSize set by autoconfiguration */
+};
+
+#ifndef FSG_NO_INTR_EP
+
+static struct usb_endpoint_descriptor
+fsg_fs_intr_in_desc = {
+ .bLength = USB_DT_ENDPOINT_SIZE,
+ .bDescriptorType = USB_DT_ENDPOINT,
+
+ .bEndpointAddress = USB_DIR_IN,
+ .bmAttributes = USB_ENDPOINT_XFER_INT,
+ .wMaxPacketSize = cpu_to_le16(2),
+ .bInterval = 32, /* frames -> 32 ms */
+};
+
+#ifndef FSG_NO_OTG
+# define FSG_FS_FUNCTION_PRE_EP_ENTRIES 2
+#else
+# define FSG_FS_FUNCTION_PRE_EP_ENTRIES 1
+#endif
+
+#endif
+
+static struct usb_descriptor_header *fsg_fs_function[] = {
+#ifndef FSG_NO_OTG
+ (struct usb_descriptor_header *) &fsg_otg_desc,
+#endif
+ (struct usb_descriptor_header *) &fsg_intf_desc,
+ (struct usb_descriptor_header *) &fsg_fs_bulk_in_desc,
+ (struct usb_descriptor_header *) &fsg_fs_bulk_out_desc,
+#ifndef FSG_NO_INTR_EP
+ (struct usb_descriptor_header *) &fsg_fs_intr_in_desc,
+#endif
+ NULL,
+};
+
+
+/*
+ * USB 2.0 devices need to expose both high speed and full speed
+ * descriptors, unless they only run at full speed.
+ *
+ * That means alternate endpoint descriptors (bigger packets)
+ * and a "device qualifier" ... plus more construction options
+ * for the config descriptor.
+ */
+static struct usb_endpoint_descriptor
+fsg_hs_bulk_in_desc = {
+ .bLength = USB_DT_ENDPOINT_SIZE,
+ .bDescriptorType = USB_DT_ENDPOINT,
+
+ /* bEndpointAddress copied from fs_bulk_in_desc during fsg_bind() */
+ .bmAttributes = USB_ENDPOINT_XFER_BULK,
+ .wMaxPacketSize = cpu_to_le16(512),
+};
+
+static struct usb_endpoint_descriptor
+fsg_hs_bulk_out_desc = {
+ .bLength = USB_DT_ENDPOINT_SIZE,
+ .bDescriptorType = USB_DT_ENDPOINT,
+
+ /* bEndpointAddress copied from fs_bulk_out_desc during fsg_bind() */
+ .bmAttributes = USB_ENDPOINT_XFER_BULK,
+ .wMaxPacketSize = cpu_to_le16(512),
+ .bInterval = 1, /* NAK every 1 uframe */
+};
+
+#ifndef FSG_NO_INTR_EP
+
+static struct usb_endpoint_descriptor
+fsg_hs_intr_in_desc = {
+ .bLength = USB_DT_ENDPOINT_SIZE,
+ .bDescriptorType = USB_DT_ENDPOINT,
+
+ /* bEndpointAddress copied from fs_intr_in_desc during fsg_bind() */
+ .bmAttributes = USB_ENDPOINT_XFER_INT,
+ .wMaxPacketSize = cpu_to_le16(2),
+ .bInterval = 9, /* 2**(9-1) = 256 uframes -> 32 ms */
+};
+
+#ifndef FSG_NO_OTG
+# define FSG_HS_FUNCTION_PRE_EP_ENTRIES 2
+#else
+# define FSG_HS_FUNCTION_PRE_EP_ENTRIES 1
+#endif
+
+#endif
+
+static struct usb_descriptor_header *fsg_hs_function[] = {
+#ifndef FSG_NO_OTG
+ (struct usb_descriptor_header *) &fsg_otg_desc,
+#endif
+ (struct usb_descriptor_header *) &fsg_intf_desc,
+ (struct usb_descriptor_header *) &fsg_hs_bulk_in_desc,
+ (struct usb_descriptor_header *) &fsg_hs_bulk_out_desc,
+#ifndef FSG_NO_INTR_EP
+ (struct usb_descriptor_header *) &fsg_hs_intr_in_desc,
+#endif
+ NULL,
+};
+
+/* Maxpacket and other transfer characteristics vary by speed. */
+static struct usb_endpoint_descriptor *
+fsg_ep_desc(struct usb_gadget *g, struct usb_endpoint_descriptor *fs,
+ struct usb_endpoint_descriptor *hs)
+{
+ if (gadget_is_dualspeed(g) && g->speed == USB_SPEED_HIGH)
+ return hs;
+ return fs;
+}
+
+
+/* Static strings, in UTF-8 (for simplicity we use only ASCII characters) */
+static struct usb_string fsg_strings[] = {
+#ifndef FSG_NO_DEVICE_STRINGS
+ {FSG_STRING_MANUFACTURER, fsg_string_manufacturer},
+ {FSG_STRING_PRODUCT, fsg_string_product},
+ {FSG_STRING_SERIAL, fsg_string_serial},
+ {FSG_STRING_CONFIG, fsg_string_config},
+#endif
+ {FSG_STRING_INTERFACE, fsg_string_interface},
+ {}
+};
+
+static struct usb_gadget_strings fsg_stringtab = {
+ .language = 0x0409, /* en-us */
+ .strings = fsg_strings,
+};
+
+
+ /*-------------------------------------------------------------------------*/
+
+/* If the next two routines are called while the gadget is registered,
+ * the caller must own fsg->filesem for writing. */
+
+static int fsg_lun_open(struct fsg_lun *curlun, const char *filename)
+{
+ int ro;
+ struct file *filp = NULL;
+ int rc = -EINVAL;
+ struct inode *inode = NULL;
+ loff_t size;
+ loff_t num_sectors;
+ loff_t min_sectors;
+
+ /* R/W if we can, R/O if we must */
+ ro = curlun->initially_ro;
+ if (!ro) {
+ filp = filp_open(filename, O_RDWR | O_LARGEFILE, 0);
+ if (-EROFS == PTR_ERR(filp))
+ ro = 1;
+ }
+ if (ro)
+ filp = filp_open(filename, O_RDONLY | O_LARGEFILE, 0);
+ if (IS_ERR(filp)) {
+ LINFO(curlun, "unable to open backing file: %s\n", filename);
+ return PTR_ERR(filp);
+ }
+
+ if (!(filp->f_mode & FMODE_WRITE))
+ ro = 1;
+
+ if (filp->f_path.dentry)
+ inode = filp->f_path.dentry->d_inode;
+ if (inode && S_ISBLK(inode->i_mode)) {
+ if (bdev_read_only(inode->i_bdev))
+ ro = 1;
+ } else if (!inode || !S_ISREG(inode->i_mode)) {
+ LINFO(curlun, "invalid file type: %s\n", filename);
+ goto out;
+ }
+
+ /* If we can't read the file, it's no good.
+ * If we can't write the file, use it read-only. */
+ if (!filp->f_op || !(filp->f_op->read || filp->f_op->aio_read)) {
+ LINFO(curlun, "file not readable: %s\n", filename);
+ goto out;
+ }
+ if (!(filp->f_op->write || filp->f_op->aio_write))
+ ro = 1;
+
+ size = i_size_read(inode->i_mapping->host);
+ if (size < 0) {
+ LINFO(curlun, "unable to find file size: %s\n", filename);
+ rc = (int) size;
+ goto out;
+ }
+ num_sectors = size >> 9; /* File size in 512-byte blocks */
+ min_sectors = 1;
+ if (curlun->cdrom) {
+ num_sectors &= ~3; /* Reduce to a multiple of 2048 */
+ min_sectors = 300*4; /* Smallest track is 300 frames */
+ if (num_sectors >= 256*60*75*4) {
+ num_sectors = (256*60*75 - 1) * 4;
+ LINFO(curlun, "file too big: %s\n", filename);
+ LINFO(curlun, "using only first %d blocks\n",
+ (int) num_sectors);
+ }
+ }
+ if (num_sectors < min_sectors) {
+ LINFO(curlun, "file too small: %s\n", filename);
+ rc = -ETOOSMALL;
+ goto out;
+ }
+
+ get_file(filp);
+ curlun->ro = ro;
+ curlun->filp = filp;
+ curlun->file_length = size;
+ curlun->num_sectors = num_sectors;
+ LDBG(curlun, "open backing file: %s\n", filename);
+ rc = 0;
+
+out:
+ filp_close(filp, current->files);
+ return rc;
+}
+
+
+static void fsg_lun_close(struct fsg_lun *curlun)
+{
+ if (curlun->filp) {
+ LDBG(curlun, "close backing file\n");
+ fput(curlun->filp);
+ curlun->filp = NULL;
+ }
+}
+
+
+/*-------------------------------------------------------------------------*/
+
+/* Sync the file data, don't bother with the metadata.
+ * This code was copied from fs/buffer.c:sys_fdatasync(). */
+static int fsg_lun_fsync_sub(struct fsg_lun *curlun)
+{
+ struct file *filp = curlun->filp;
+
+ if (curlun->ro || !filp)
+ return 0;
+ return vfs_fsync(filp, filp->f_path.dentry, 1);
+}
+
+static void store_cdrom_address(u8 *dest, int msf, u32 addr)
+{
+ if (msf) {
+ /* Convert to Minutes-Seconds-Frames */
+ addr >>= 2; /* Convert to 2048-byte frames */
+ addr += 2*75; /* Lead-in occupies 2 seconds */
+ dest[3] = addr % 75; /* Frames */
+ addr /= 75;
+ dest[2] = addr % 60; /* Seconds */
+ addr /= 60;
+ dest[1] = addr; /* Minutes */
+ dest[0] = 0; /* Reserved */
+ } else {
+ /* Absolute sector */
+ put_unaligned_be32(addr, dest);
+ }
+}
+
+
+/*-------------------------------------------------------------------------*/
+
+
+static ssize_t fsg_show_ro(struct device *dev, struct device_attribute *attr,
+ char *buf)
+{
+ struct fsg_lun *curlun = fsg_lun_from_dev(dev);
+
+ return sprintf(buf, "%d\n", fsg_lun_is_open(curlun)
+ ? curlun->ro
+ : curlun->initially_ro);
+}
+
+static ssize_t fsg_show_file(struct device *dev, struct device_attribute *attr,
+ char *buf)
+{
+ struct fsg_lun *curlun = fsg_lun_from_dev(dev);
+ struct rw_semaphore *filesem = dev_get_drvdata(dev);
+ char *p;
+ ssize_t rc;
+
+ down_read(filesem);
+ if (fsg_lun_is_open(curlun)) { /* Get the complete pathname */
+ p = d_path(&curlun->filp->f_path, buf, PAGE_SIZE - 1);
+ if (IS_ERR(p))
+ rc = PTR_ERR(p);
+ else {
+ rc = strlen(p);
+ memmove(buf, p, rc);
+ buf[rc] = '\n'; /* Add a newline */
+ buf[++rc] = 0;
+ }
+ } else { /* No file, return 0 bytes */
+ *buf = 0;
+ rc = 0;
+ }
+ up_read(filesem);
+ return rc;
+}
+
+
+static ssize_t fsg_store_ro(struct device *dev, struct device_attribute *attr,
+ const char *buf, size_t count)
+{
+ ssize_t rc = count;
+ struct fsg_lun *curlun = fsg_lun_from_dev(dev);
+ struct rw_semaphore *filesem = dev_get_drvdata(dev);
+ int i;
+
+ if (sscanf(buf, "%d", &i) != 1)
+ return -EINVAL;
+
+ /* Allow the write-enable status to change only while the backing file
+ * is closed. */
+ down_read(filesem);
+ if (fsg_lun_is_open(curlun)) {
+ LDBG(curlun, "read-only status change prevented\n");
+ rc = -EBUSY;
+ } else {
+ curlun->ro = !!i;
+ curlun->initially_ro = !!i;
+ LDBG(curlun, "read-only status set to %d\n", curlun->ro);
+ }
+ up_read(filesem);
+ return rc;
+}
+
+static ssize_t fsg_store_file(struct device *dev, struct device_attribute *attr,
+ const char *buf, size_t count)
+{
+ struct fsg_lun *curlun = fsg_lun_from_dev(dev);
+ struct rw_semaphore *filesem = dev_get_drvdata(dev);
+ int rc = 0;
+
+ if (curlun->prevent_medium_removal && fsg_lun_is_open(curlun)) {
+ LDBG(curlun, "eject attempt prevented\n");
+ return -EBUSY; /* "Door is locked" */
+ }
+
+ /* Remove a trailing newline */
+ if (count > 0 && buf[count-1] == '\n')
+ ((char *) buf)[count-1] = 0; /* Ugh! */
+
+ /* Eject current medium */
+ down_write(filesem);
+ if (fsg_lun_is_open(curlun)) {
+ fsg_lun_close(curlun);
+ curlun->unit_attention_data = SS_MEDIUM_NOT_PRESENT;
+ }
+
+ /* Load new medium */
+ if (count > 0 && buf[0]) {
+ rc = fsg_lun_open(curlun, buf);
+ if (rc == 0)
+ curlun->unit_attention_data =
+ SS_NOT_READY_TO_READY_TRANSITION;
+ }
+ up_write(filesem);
+ return (rc < 0 ? rc : count);
+}
diff --git a/drivers/usb/gadget/u_ether.h b/drivers/usb/gadget/u_ether.h
index 91b39ff..fd55f45 100644
--- a/drivers/usb/gadget/u_ether.h
+++ b/drivers/usb/gadget/u_ether.h
@@ -112,7 +112,7 @@ 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 eem_bind_config(struct usb_configuration *c);
-#ifdef CONFIG_USB_ETH_RNDIS
+#ifdef USB_ETH_RNDIS
int rndis_bind_config(struct usb_configuration *c, u8 ethaddr[ETH_ALEN]);
diff --git a/drivers/usb/host/Kconfig b/drivers/usb/host/Kconfig
index 9b43b22..2678a16 100644
--- a/drivers/usb/host/Kconfig
+++ b/drivers/usb/host/Kconfig
@@ -90,14 +90,25 @@ config USB_EHCI_TT_NEWSCHED
config USB_EHCI_BIG_ENDIAN_MMIO
bool
- depends on USB_EHCI_HCD && (PPC_CELLEB || PPC_PS3 || 440EPX || ARCH_IXP4XX)
+ depends on USB_EHCI_HCD && (PPC_CELLEB || PPC_PS3 || 440EPX || ARCH_IXP4XX || XPS_USB_HCD_XILINX)
default y
config USB_EHCI_BIG_ENDIAN_DESC
bool
- depends on USB_EHCI_HCD && (440EPX || ARCH_IXP4XX)
+ depends on USB_EHCI_HCD && (440EPX || ARCH_IXP4XX || XPS_USB_HCD_XILINX)
default y
+config XPS_USB_HCD_XILINX
+ bool "Use Xilinx usb host EHCI controller core"
+ depends on USB_EHCI_HCD && (PPC32 || MICROBLAZE)
+ select USB_EHCI_BIG_ENDIAN_DESC
+ select USB_EHCI_BIG_ENDIAN_MMIO
+ ---help---
+ Xilinx xps USB host controller core is EHCI compilant and has
+ transaction translator built-in. It can be configured to either
+ support both high speed and full speed devices, or high speed
+ devices only.
+
config USB_EHCI_FSL
bool "Support for Freescale on-chip EHCI USB controller"
depends on USB_EHCI_HCD && FSL_SOC
@@ -105,6 +116,13 @@ config USB_EHCI_FSL
---help---
Variation of ARC USB block used in some Freescale chips.
+config USB_EHCI_MXC
+ bool "Support for Freescale on-chip EHCI USB controller"
+ depends on USB_EHCI_HCD && ARCH_MXC
+ select USB_EHCI_ROOT_HUB_TT
+ ---help---
+ Variation of ARC USB block used in some Freescale chips.
+
config USB_EHCI_HCD_PPC_OF
bool "EHCI support for PPC USB controller on OF platform bus"
depends on USB_EHCI_HCD && PPC_OF
diff --git a/drivers/usb/host/ehci-hcd.c b/drivers/usb/host/ehci-hcd.c
index d8f4aaa..5859522 100644
--- a/drivers/usb/host/ehci-hcd.c
+++ b/drivers/usb/host/ehci-hcd.c
@@ -549,7 +549,7 @@ static int ehci_init(struct usb_hcd *hcd)
/* controllers may cache some of the periodic schedule ... */
hcc_params = ehci_readl(ehci, &ehci->caps->hcc_params);
if (HCC_ISOC_CACHE(hcc_params)) // full frame cache
- ehci->i_thresh = 8;
+ ehci->i_thresh = 2 + 8;
else // N microframes cached
ehci->i_thresh = 2 + HCC_ISOC_THRES(hcc_params);
@@ -605,6 +605,8 @@ static int ehci_init(struct usb_hcd *hcd)
}
ehci->command = temp;
+ /* Accept arbitrarily long scatter-gather lists */
+ hcd->self.sg_tablesize = ~0;
return 0;
}
@@ -1105,11 +1107,21 @@ MODULE_LICENSE ("GPL");
#define PLATFORM_DRIVER ehci_fsl_driver
#endif
+#ifdef CONFIG_USB_EHCI_MXC
+#include "ehci-mxc.c"
+#define PLATFORM_DRIVER ehci_mxc_driver
+#endif
+
#ifdef CONFIG_SOC_AU1200
#include "ehci-au1xxx.c"
#define PLATFORM_DRIVER ehci_hcd_au1xxx_driver
#endif
+#ifdef CONFIG_ARCH_OMAP34XX
+#include "ehci-omap.c"
+#define PLATFORM_DRIVER ehci_hcd_omap_driver
+#endif
+
#ifdef CONFIG_PPC_PS3
#include "ehci-ps3.c"
#define PS3_SYSTEM_BUS_DRIVER ps3_ehci_driver
@@ -1120,6 +1132,11 @@ MODULE_LICENSE ("GPL");
#define OF_PLATFORM_DRIVER ehci_hcd_ppc_of_driver
#endif
+#ifdef CONFIG_XPS_USB_HCD_XILINX
+#include "ehci-xilinx-of.c"
+#define OF_PLATFORM_DRIVER ehci_hcd_xilinx_of_driver
+#endif
+
#ifdef CONFIG_PLAT_ORION
#include "ehci-orion.c"
#define PLATFORM_DRIVER ehci_orion_driver
diff --git a/drivers/usb/host/ehci-hub.c b/drivers/usb/host/ehci-hub.c
index 1b6f1c0..2c6571c 100644
--- a/drivers/usb/host/ehci-hub.c
+++ b/drivers/usb/host/ehci-hub.c
@@ -236,7 +236,7 @@ static int ehci_bus_resume (struct usb_hcd *hcd)
}
if (unlikely(ehci->debug)) {
- if (ehci->debug && !dbgp_reset_prep())
+ if (!dbgp_reset_prep())
ehci->debug = NULL;
else
dbgp_external_startup();
diff --git a/drivers/usb/host/ehci-mxc.c b/drivers/usb/host/ehci-mxc.c
new file mode 100644
index 0000000..35c56f4
--- /dev/null
+++ b/drivers/usb/host/ehci-mxc.c
@@ -0,0 +1,296 @@
+/*
+ * Copyright (c) 2008 Sascha Hauer <s.hauer@pengutronix.de>, Pengutronix
+ * Copyright (c) 2009 Daniel Mack <daniel@caiaq.de>
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License as published by the
+ * Free Software Foundation; either version 2 of the License, or (at your
+ * option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
+ * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
+ * for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software Foundation,
+ * Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ */
+
+#include <linux/platform_device.h>
+#include <linux/clk.h>
+#include <linux/delay.h>
+#include <linux/usb/otg.h>
+
+#include <mach/mxc_ehci.h>
+
+#define ULPI_VIEWPORT_OFFSET 0x170
+#define PORTSC_OFFSET 0x184
+#define USBMODE_OFFSET 0x1a8
+#define USBMODE_CM_HOST 3
+
+struct ehci_mxc_priv {
+ struct clk *usbclk, *ahbclk;
+ struct usb_hcd *hcd;
+};
+
+/* called during probe() after chip reset completes */
+static int ehci_mxc_setup(struct usb_hcd *hcd)
+{
+ struct ehci_hcd *ehci = hcd_to_ehci(hcd);
+ int retval;
+
+ /* EHCI registers start at offset 0x100 */
+ ehci->caps = hcd->regs + 0x100;
+ ehci->regs = hcd->regs + 0x100 +
+ HC_LENGTH(ehci_readl(ehci, &ehci->caps->hc_capbase));
+ dbg_hcs_params(ehci, "reset");
+ dbg_hcc_params(ehci, "reset");
+
+ /* cache this readonly data; minimize chip reads */
+ ehci->hcs_params = ehci_readl(ehci, &ehci->caps->hcs_params);
+
+ retval = ehci_halt(ehci);
+ if (retval)
+ return retval;
+
+ /* data structure init */
+ retval = ehci_init(hcd);
+ if (retval)
+ return retval;
+
+ hcd->has_tt = 1;
+
+ ehci->sbrn = 0x20;
+
+ ehci_reset(ehci);
+
+ ehci_port_power(ehci, 0);
+ return 0;
+}
+
+static const struct hc_driver ehci_mxc_hc_driver = {
+ .description = hcd_name,
+ .product_desc = "Freescale On-Chip EHCI Host Controller",
+ .hcd_priv_size = sizeof(struct ehci_hcd),
+
+ /*
+ * generic hardware linkage
+ */
+ .irq = ehci_irq,
+ .flags = HCD_USB2 | HCD_MEMORY,
+
+ /*
+ * basic lifecycle operations
+ */
+ .reset = ehci_mxc_setup,
+ .start = ehci_run,
+ .stop = ehci_stop,
+ .shutdown = ehci_shutdown,
+
+ /*
+ * managing i/o requests and associated device resources
+ */
+ .urb_enqueue = ehci_urb_enqueue,
+ .urb_dequeue = ehci_urb_dequeue,
+ .endpoint_disable = ehci_endpoint_disable,
+
+ /*
+ * scheduling support
+ */
+ .get_frame_number = ehci_get_frame,
+
+ /*
+ * root hub support
+ */
+ .hub_status_data = ehci_hub_status_data,
+ .hub_control = ehci_hub_control,
+ .bus_suspend = ehci_bus_suspend,
+ .bus_resume = ehci_bus_resume,
+ .relinquish_port = ehci_relinquish_port,
+ .port_handed_over = ehci_port_handed_over,
+};
+
+static int ehci_mxc_drv_probe(struct platform_device *pdev)
+{
+ struct mxc_usbh_platform_data *pdata = pdev->dev.platform_data;
+ struct usb_hcd *hcd;
+ struct resource *res;
+ int irq, ret, temp;
+ struct ehci_mxc_priv *priv;
+ struct device *dev = &pdev->dev;
+
+ dev_info(&pdev->dev, "initializing i.MX USB Controller\n");
+
+ if (!pdata) {
+ dev_err(dev, "No platform data given, bailing out.\n");
+ return -EINVAL;
+ }
+
+ irq = platform_get_irq(pdev, 0);
+
+ hcd = usb_create_hcd(&ehci_mxc_hc_driver, dev, dev_name(dev));
+ if (!hcd)
+ return -ENOMEM;
+
+ priv = kzalloc(sizeof(*priv), GFP_KERNEL);
+ if (!priv) {
+ ret = -ENOMEM;
+ goto err_alloc;
+ }
+
+ res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
+ if (!res) {
+ dev_err(dev, "Found HC with no register addr. Check setup!\n");
+ ret = -ENODEV;
+ goto err_get_resource;
+ }
+
+ hcd->rsrc_start = res->start;
+ hcd->rsrc_len = resource_size(res);
+
+ if (!request_mem_region(hcd->rsrc_start, hcd->rsrc_len, hcd_name)) {
+ dev_dbg(dev, "controller already in use\n");
+ ret = -EBUSY;
+ goto err_request_mem;
+ }
+
+ hcd->regs = ioremap(hcd->rsrc_start, hcd->rsrc_len);
+ if (!hcd->regs) {
+ dev_err(dev, "error mapping memory\n");
+ ret = -EFAULT;
+ goto err_ioremap;
+ }
+
+ /* enable clocks */
+ priv->usbclk = clk_get(dev, "usb");
+ if (IS_ERR(priv->usbclk)) {
+ ret = PTR_ERR(priv->usbclk);
+ goto err_clk;
+ }
+ clk_enable(priv->usbclk);
+
+ if (!cpu_is_mx35()) {
+ priv->ahbclk = clk_get(dev, "usb_ahb");
+ if (IS_ERR(priv->ahbclk)) {
+ ret = PTR_ERR(priv->ahbclk);
+ goto err_clk_ahb;
+ }
+ clk_enable(priv->ahbclk);
+ }
+
+ /* set USBMODE to host mode */
+ temp = readl(hcd->regs + USBMODE_OFFSET);
+ writel(temp | USBMODE_CM_HOST, hcd->regs + USBMODE_OFFSET);
+
+ /* set up the PORTSCx register */
+ writel(pdata->portsc, hcd->regs + PORTSC_OFFSET);
+ mdelay(10);
+
+ /* setup USBCONTROL. */
+ ret = mxc_set_usbcontrol(pdev->id, pdata->flags);
+ if (ret < 0)
+ goto err_init;
+
+ /* call platform specific init function */
+ if (pdata->init) {
+ ret = pdata->init(pdev);
+ if (ret) {
+ dev_err(dev, "platform init failed\n");
+ goto err_init;
+ }
+ }
+
+ /* most platforms need some time to settle changed IO settings */
+ mdelay(10);
+
+ /* Initialize the transceiver */
+ if (pdata->otg) {
+ pdata->otg->io_priv = hcd->regs + ULPI_VIEWPORT_OFFSET;
+ if (otg_init(pdata->otg) != 0)
+ dev_err(dev, "unable to init transceiver\n");
+ else if (otg_set_vbus(pdata->otg, 1) != 0)
+ dev_err(dev, "unable to enable vbus on transceiver\n");
+ }
+
+ priv->hcd = hcd;
+ platform_set_drvdata(pdev, priv);
+
+ ret = usb_add_hcd(hcd, irq, IRQF_DISABLED | IRQF_SHARED);
+ if (ret)
+ goto err_add;
+
+ return 0;
+
+err_add:
+ if (pdata && pdata->exit)
+ pdata->exit(pdev);
+err_init:
+ if (priv->ahbclk) {
+ clk_disable(priv->ahbclk);
+ clk_put(priv->ahbclk);
+ }
+err_clk_ahb:
+ clk_disable(priv->usbclk);
+ clk_put(priv->usbclk);
+err_clk:
+ iounmap(hcd->regs);
+err_ioremap:
+ release_mem_region(hcd->rsrc_start, hcd->rsrc_len);
+err_request_mem:
+err_get_resource:
+ kfree(priv);
+err_alloc:
+ usb_put_hcd(hcd);
+ return ret;
+}
+
+static int __exit ehci_mxc_drv_remove(struct platform_device *pdev)
+{
+ struct mxc_usbh_platform_data *pdata = pdev->dev.platform_data;
+ struct ehci_mxc_priv *priv = platform_get_drvdata(pdev);
+ struct usb_hcd *hcd = priv->hcd;
+
+ if (pdata && pdata->exit)
+ pdata->exit(pdev);
+
+ if (pdata->otg)
+ otg_shutdown(pdata->otg);
+
+ usb_remove_hcd(hcd);
+ iounmap(hcd->regs);
+ release_mem_region(hcd->rsrc_start, hcd->rsrc_len);
+ usb_put_hcd(hcd);
+ platform_set_drvdata(pdev, NULL);
+
+ clk_disable(priv->usbclk);
+ clk_put(priv->usbclk);
+ if (priv->ahbclk) {
+ clk_disable(priv->ahbclk);
+ clk_put(priv->ahbclk);
+ }
+
+ kfree(priv);
+
+ return 0;
+}
+
+static void ehci_mxc_drv_shutdown(struct platform_device *pdev)
+{
+ struct ehci_mxc_priv *priv = platform_get_drvdata(pdev);
+ struct usb_hcd *hcd = priv->hcd;
+
+ if (hcd->driver->shutdown)
+ hcd->driver->shutdown(hcd);
+}
+
+MODULE_ALIAS("platform:mxc-ehci");
+
+static struct platform_driver ehci_mxc_driver = {
+ .probe = ehci_mxc_drv_probe,
+ .remove = __exit_p(ehci_mxc_drv_remove),
+ .shutdown = ehci_mxc_drv_shutdown,
+ .driver = {
+ .name = "mxc-ehci",
+ },
+};
diff --git a/drivers/usb/host/ehci-omap.c b/drivers/usb/host/ehci-omap.c
new file mode 100644
index 0000000..12f1ad2
--- /dev/null
+++ b/drivers/usb/host/ehci-omap.c
@@ -0,0 +1,756 @@
+/*
+ * ehci-omap.c - driver for USBHOST on OMAP 34xx processor
+ *
+ * Bus Glue for OMAP34xx USBHOST 3 port EHCI controller
+ * Tested on OMAP3430 ES2.0 SDP
+ *
+ * Copyright (C) 2007-2008 Texas Instruments, Inc.
+ * Author: Vikram Pandita <vikram.pandita@ti.com>
+ *
+ * Copyright (C) 2009 Nokia Corporation
+ * Contact: Felipe Balbi <felipe.balbi@nokia.com>
+ *
+ * Based on "ehci-fsl.c" and "ehci-au1xxx.c" ehci glue layers
+ *
+ * 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
+ *
+ * TODO (last updated Feb 23rd, 2009):
+ * - add kernel-doc
+ * - enable AUTOIDLE
+ * - move DPLL5 programming to clock fw
+ * - add suspend/resume
+ * - move workarounds to board-files
+ */
+
+#include <linux/platform_device.h>
+#include <linux/clk.h>
+#include <linux/gpio.h>
+#include <mach/usb.h>
+
+/*
+ * OMAP USBHOST Register addresses: VIRTUAL ADDRESSES
+ * Use ehci_omap_readl()/ehci_omap_writel() functions
+ */
+
+/* TLL Register Set */
+#define OMAP_USBTLL_REVISION (0x00)
+#define OMAP_USBTLL_SYSCONFIG (0x10)
+#define OMAP_USBTLL_SYSCONFIG_CACTIVITY (1 << 8)
+#define OMAP_USBTLL_SYSCONFIG_SIDLEMODE (1 << 3)
+#define OMAP_USBTLL_SYSCONFIG_ENAWAKEUP (1 << 2)
+#define OMAP_USBTLL_SYSCONFIG_SOFTRESET (1 << 1)
+#define OMAP_USBTLL_SYSCONFIG_AUTOIDLE (1 << 0)
+
+#define OMAP_USBTLL_SYSSTATUS (0x14)
+#define OMAP_USBTLL_SYSSTATUS_RESETDONE (1 << 0)
+
+#define OMAP_USBTLL_IRQSTATUS (0x18)
+#define OMAP_USBTLL_IRQENABLE (0x1C)
+
+#define OMAP_TLL_SHARED_CONF (0x30)
+#define OMAP_TLL_SHARED_CONF_USB_90D_DDR_EN (1 << 6)
+#define OMAP_TLL_SHARED_CONF_USB_180D_SDR_EN (1 << 5)
+#define OMAP_TLL_SHARED_CONF_USB_DIVRATION (1 << 2)
+#define OMAP_TLL_SHARED_CONF_FCLK_REQ (1 << 1)
+#define OMAP_TLL_SHARED_CONF_FCLK_IS_ON (1 << 0)
+
+#define OMAP_TLL_CHANNEL_CONF(num) (0x040 + 0x004 * num)
+#define OMAP_TLL_CHANNEL_CONF_ULPINOBITSTUFF (1 << 11)
+#define OMAP_TLL_CHANNEL_CONF_ULPI_ULPIAUTOIDLE (1 << 10)
+#define OMAP_TLL_CHANNEL_CONF_UTMIAUTOIDLE (1 << 9)
+#define OMAP_TLL_CHANNEL_CONF_ULPIDDRMODE (1 << 8)
+#define OMAP_TLL_CHANNEL_CONF_CHANEN (1 << 0)
+
+#define OMAP_TLL_ULPI_FUNCTION_CTRL(num) (0x804 + 0x100 * num)
+#define OMAP_TLL_ULPI_INTERFACE_CTRL(num) (0x807 + 0x100 * num)
+#define OMAP_TLL_ULPI_OTG_CTRL(num) (0x80A + 0x100 * num)
+#define OMAP_TLL_ULPI_INT_EN_RISE(num) (0x80D + 0x100 * num)
+#define OMAP_TLL_ULPI_INT_EN_FALL(num) (0x810 + 0x100 * num)
+#define OMAP_TLL_ULPI_INT_STATUS(num) (0x813 + 0x100 * num)
+#define OMAP_TLL_ULPI_INT_LATCH(num) (0x814 + 0x100 * num)
+#define OMAP_TLL_ULPI_DEBUG(num) (0x815 + 0x100 * num)
+#define OMAP_TLL_ULPI_SCRATCH_REGISTER(num) (0x816 + 0x100 * num)
+
+#define OMAP_TLL_CHANNEL_COUNT 3
+#define OMAP_TLL_CHANNEL_1_EN_MASK (1 << 1)
+#define OMAP_TLL_CHANNEL_2_EN_MASK (1 << 2)
+#define OMAP_TLL_CHANNEL_3_EN_MASK (1 << 4)
+
+/* UHH Register Set */
+#define OMAP_UHH_REVISION (0x00)
+#define OMAP_UHH_SYSCONFIG (0x10)
+#define OMAP_UHH_SYSCONFIG_MIDLEMODE (1 << 12)
+#define OMAP_UHH_SYSCONFIG_CACTIVITY (1 << 8)
+#define OMAP_UHH_SYSCONFIG_SIDLEMODE (1 << 3)
+#define OMAP_UHH_SYSCONFIG_ENAWAKEUP (1 << 2)
+#define OMAP_UHH_SYSCONFIG_SOFTRESET (1 << 1)
+#define OMAP_UHH_SYSCONFIG_AUTOIDLE (1 << 0)
+
+#define OMAP_UHH_SYSSTATUS (0x14)
+#define OMAP_UHH_HOSTCONFIG (0x40)
+#define OMAP_UHH_HOSTCONFIG_ULPI_BYPASS (1 << 0)
+#define OMAP_UHH_HOSTCONFIG_ULPI_P1_BYPASS (1 << 0)
+#define OMAP_UHH_HOSTCONFIG_ULPI_P2_BYPASS (1 << 11)
+#define OMAP_UHH_HOSTCONFIG_ULPI_P3_BYPASS (1 << 12)
+#define OMAP_UHH_HOSTCONFIG_INCR4_BURST_EN (1 << 2)
+#define OMAP_UHH_HOSTCONFIG_INCR8_BURST_EN (1 << 3)
+#define OMAP_UHH_HOSTCONFIG_INCR16_BURST_EN (1 << 4)
+#define OMAP_UHH_HOSTCONFIG_INCRX_ALIGN_EN (1 << 5)
+#define OMAP_UHH_HOSTCONFIG_P1_CONNECT_STATUS (1 << 8)
+#define OMAP_UHH_HOSTCONFIG_P2_CONNECT_STATUS (1 << 9)
+#define OMAP_UHH_HOSTCONFIG_P3_CONNECT_STATUS (1 << 10)
+
+#define OMAP_UHH_DEBUG_CSR (0x44)
+
+/* EHCI Register Set */
+#define EHCI_INSNREG05_ULPI (0xA4)
+#define EHCI_INSNREG05_ULPI_CONTROL_SHIFT 31
+#define EHCI_INSNREG05_ULPI_PORTSEL_SHIFT 24
+#define EHCI_INSNREG05_ULPI_OPSEL_SHIFT 22
+#define EHCI_INSNREG05_ULPI_REGADD_SHIFT 16
+#define EHCI_INSNREG05_ULPI_EXTREGADD_SHIFT 8
+#define EHCI_INSNREG05_ULPI_WRDATA_SHIFT 0
+
+/*-------------------------------------------------------------------------*/
+
+static inline void ehci_omap_writel(void __iomem *base, u32 reg, u32 val)
+{
+ __raw_writel(val, base + reg);
+}
+
+static inline u32 ehci_omap_readl(void __iomem *base, u32 reg)
+{
+ return __raw_readl(base + reg);
+}
+
+static inline void ehci_omap_writeb(void __iomem *base, u8 reg, u8 val)
+{
+ __raw_writeb(val, base + reg);
+}
+
+static inline u8 ehci_omap_readb(void __iomem *base, u8 reg)
+{
+ return __raw_readb(base + reg);
+}
+
+/*-------------------------------------------------------------------------*/
+
+struct ehci_hcd_omap {
+ struct ehci_hcd *ehci;
+ struct device *dev;
+
+ struct clk *usbhost_ick;
+ struct clk *usbhost2_120m_fck;
+ struct clk *usbhost1_48m_fck;
+ struct clk *usbtll_fck;
+ struct clk *usbtll_ick;
+
+ /* FIXME the following two workarounds are
+ * board specific not silicon-specific so these
+ * should be moved to board-file instead.
+ *
+ * Maybe someone from TI will know better which
+ * board is affected and needs the workarounds
+ * to be applied
+ */
+
+ /* gpio for resetting phy */
+ int reset_gpio_port[OMAP3_HS_USB_PORTS];
+
+ /* phy reset workaround */
+ int phy_reset;
+
+ /* desired phy_mode: TLL, PHY */
+ enum ehci_hcd_omap_mode port_mode[OMAP3_HS_USB_PORTS];
+
+ void __iomem *uhh_base;
+ void __iomem *tll_base;
+ void __iomem *ehci_base;
+};
+
+/*-------------------------------------------------------------------------*/
+
+static void omap_usb_utmi_init(struct ehci_hcd_omap *omap, u8 tll_channel_mask)
+{
+ unsigned reg;
+ int i;
+
+ /* Program the 3 TLL channels upfront */
+ for (i = 0; i < OMAP_TLL_CHANNEL_COUNT; i++) {
+ reg = ehci_omap_readl(omap->tll_base, OMAP_TLL_CHANNEL_CONF(i));
+
+ /* Disable AutoIdle, BitStuffing and use SDR Mode */
+ reg &= ~(OMAP_TLL_CHANNEL_CONF_UTMIAUTOIDLE
+ | OMAP_TLL_CHANNEL_CONF_ULPINOBITSTUFF
+ | OMAP_TLL_CHANNEL_CONF_ULPIDDRMODE);
+ ehci_omap_writel(omap->tll_base, OMAP_TLL_CHANNEL_CONF(i), reg);
+ }
+
+ /* Program Common TLL register */
+ reg = ehci_omap_readl(omap->tll_base, OMAP_TLL_SHARED_CONF);
+ reg |= (OMAP_TLL_SHARED_CONF_FCLK_IS_ON
+ | OMAP_TLL_SHARED_CONF_USB_DIVRATION
+ | OMAP_TLL_SHARED_CONF_USB_180D_SDR_EN);
+ reg &= ~OMAP_TLL_SHARED_CONF_USB_90D_DDR_EN;
+
+ ehci_omap_writel(omap->tll_base, OMAP_TLL_SHARED_CONF, reg);
+
+ /* Enable channels now */
+ for (i = 0; i < OMAP_TLL_CHANNEL_COUNT; i++) {
+ reg = ehci_omap_readl(omap->tll_base, OMAP_TLL_CHANNEL_CONF(i));
+
+ /* Enable only the reg that is needed */
+ if (!(tll_channel_mask & 1<<i))
+ continue;
+
+ reg |= OMAP_TLL_CHANNEL_CONF_CHANEN;
+ ehci_omap_writel(omap->tll_base, OMAP_TLL_CHANNEL_CONF(i), reg);
+
+ ehci_omap_writeb(omap->tll_base,
+ OMAP_TLL_ULPI_SCRATCH_REGISTER(i), 0xbe);
+ dev_dbg(omap->dev, "ULPI_SCRATCH_REG[ch=%d]= 0x%02x\n",
+ i+1, ehci_omap_readb(omap->tll_base,
+ OMAP_TLL_ULPI_SCRATCH_REGISTER(i)));
+ }
+}
+
+/*-------------------------------------------------------------------------*/
+
+/* omap_start_ehc
+ * - Start the TI USBHOST controller
+ */
+static int omap_start_ehc(struct ehci_hcd_omap *omap, struct usb_hcd *hcd)
+{
+ unsigned long timeout = jiffies + msecs_to_jiffies(1000);
+ u8 tll_ch_mask = 0;
+ unsigned reg = 0;
+ int ret = 0;
+
+ dev_dbg(omap->dev, "starting TI EHCI USB Controller\n");
+
+ /* Enable Clocks for USBHOST */
+ omap->usbhost_ick = clk_get(omap->dev, "usbhost_ick");
+ if (IS_ERR(omap->usbhost_ick)) {
+ ret = PTR_ERR(omap->usbhost_ick);
+ goto err_host_ick;
+ }
+ clk_enable(omap->usbhost_ick);
+
+ omap->usbhost2_120m_fck = clk_get(omap->dev, "usbhost_120m_fck");
+ if (IS_ERR(omap->usbhost2_120m_fck)) {
+ ret = PTR_ERR(omap->usbhost2_120m_fck);
+ goto err_host_120m_fck;
+ }
+ clk_enable(omap->usbhost2_120m_fck);
+
+ omap->usbhost1_48m_fck = clk_get(omap->dev, "usbhost_48m_fck");
+ if (IS_ERR(omap->usbhost1_48m_fck)) {
+ ret = PTR_ERR(omap->usbhost1_48m_fck);
+ goto err_host_48m_fck;
+ }
+ clk_enable(omap->usbhost1_48m_fck);
+
+ if (omap->phy_reset) {
+ /* Refer: ISSUE1 */
+ if (gpio_is_valid(omap->reset_gpio_port[0])) {
+ gpio_request(omap->reset_gpio_port[0],
+ "USB1 PHY reset");
+ gpio_direction_output(omap->reset_gpio_port[0], 0);
+ }
+
+ if (gpio_is_valid(omap->reset_gpio_port[1])) {
+ gpio_request(omap->reset_gpio_port[1],
+ "USB2 PHY reset");
+ gpio_direction_output(omap->reset_gpio_port[1], 0);
+ }
+
+ /* Hold the PHY in RESET for enough time till DIR is high */
+ udelay(10);
+ }
+
+ /* Configure TLL for 60Mhz clk for ULPI */
+ omap->usbtll_fck = clk_get(omap->dev, "usbtll_fck");
+ if (IS_ERR(omap->usbtll_fck)) {
+ ret = PTR_ERR(omap->usbtll_fck);
+ goto err_tll_fck;
+ }
+ clk_enable(omap->usbtll_fck);
+
+ omap->usbtll_ick = clk_get(omap->dev, "usbtll_ick");
+ if (IS_ERR(omap->usbtll_ick)) {
+ ret = PTR_ERR(omap->usbtll_ick);
+ goto err_tll_ick;
+ }
+ clk_enable(omap->usbtll_ick);
+
+ /* perform TLL soft reset, and wait until reset is complete */
+ ehci_omap_writel(omap->tll_base, OMAP_USBTLL_SYSCONFIG,
+ OMAP_USBTLL_SYSCONFIG_SOFTRESET);
+
+ /* Wait for TLL reset to complete */
+ while (!(ehci_omap_readl(omap->tll_base, OMAP_USBTLL_SYSSTATUS)
+ & OMAP_USBTLL_SYSSTATUS_RESETDONE)) {
+ cpu_relax();
+
+ if (time_after(jiffies, timeout)) {
+ dev_dbg(omap->dev, "operation timed out\n");
+ ret = -EINVAL;
+ goto err_sys_status;
+ }
+ }
+
+ dev_dbg(omap->dev, "TLL RESET DONE\n");
+
+ /* (1<<3) = no idle mode only for initial debugging */
+ ehci_omap_writel(omap->tll_base, OMAP_USBTLL_SYSCONFIG,
+ OMAP_USBTLL_SYSCONFIG_ENAWAKEUP |
+ OMAP_USBTLL_SYSCONFIG_SIDLEMODE |
+ OMAP_USBTLL_SYSCONFIG_CACTIVITY);
+
+
+ /* Put UHH in NoIdle/NoStandby mode */
+ reg = ehci_omap_readl(omap->uhh_base, OMAP_UHH_SYSCONFIG);
+ reg |= (OMAP_UHH_SYSCONFIG_ENAWAKEUP
+ | OMAP_UHH_SYSCONFIG_SIDLEMODE
+ | OMAP_UHH_SYSCONFIG_CACTIVITY
+ | OMAP_UHH_SYSCONFIG_MIDLEMODE);
+ reg &= ~OMAP_UHH_SYSCONFIG_AUTOIDLE;
+
+ ehci_omap_writel(omap->uhh_base, OMAP_UHH_SYSCONFIG, reg);
+
+ reg = ehci_omap_readl(omap->uhh_base, OMAP_UHH_HOSTCONFIG);
+
+ /* setup ULPI bypass and burst configurations */
+ reg |= (OMAP_UHH_HOSTCONFIG_INCR4_BURST_EN
+ | OMAP_UHH_HOSTCONFIG_INCR8_BURST_EN
+ | OMAP_UHH_HOSTCONFIG_INCR16_BURST_EN);
+ reg &= ~OMAP_UHH_HOSTCONFIG_INCRX_ALIGN_EN;
+
+ if (omap->port_mode[0] == EHCI_HCD_OMAP_MODE_UNKNOWN)
+ reg &= ~OMAP_UHH_HOSTCONFIG_P1_CONNECT_STATUS;
+ if (omap->port_mode[1] == EHCI_HCD_OMAP_MODE_UNKNOWN)
+ reg &= ~OMAP_UHH_HOSTCONFIG_P2_CONNECT_STATUS;
+ if (omap->port_mode[2] == EHCI_HCD_OMAP_MODE_UNKNOWN)
+ reg &= ~OMAP_UHH_HOSTCONFIG_P3_CONNECT_STATUS;
+
+ /* Bypass the TLL module for PHY mode operation */
+ if (omap_rev() <= OMAP3430_REV_ES2_1) {
+ dev_dbg(omap->dev, "OMAP3 ES version <= ES2.1 \n");
+ if ((omap->port_mode[0] == EHCI_HCD_OMAP_MODE_PHY) ||
+ (omap->port_mode[1] == EHCI_HCD_OMAP_MODE_PHY) ||
+ (omap->port_mode[2] == EHCI_HCD_OMAP_MODE_PHY))
+ reg &= ~OMAP_UHH_HOSTCONFIG_ULPI_BYPASS;
+ else
+ reg |= OMAP_UHH_HOSTCONFIG_ULPI_BYPASS;
+ } else {
+ dev_dbg(omap->dev, "OMAP3 ES version > ES2.1\n");
+ if (omap->port_mode[0] == EHCI_HCD_OMAP_MODE_PHY)
+ reg &= ~OMAP_UHH_HOSTCONFIG_ULPI_P1_BYPASS;
+ else if (omap->port_mode[0] == EHCI_HCD_OMAP_MODE_TLL)
+ reg |= OMAP_UHH_HOSTCONFIG_ULPI_P1_BYPASS;
+
+ if (omap->port_mode[1] == EHCI_HCD_OMAP_MODE_PHY)
+ reg &= ~OMAP_UHH_HOSTCONFIG_ULPI_P2_BYPASS;
+ else if (omap->port_mode[1] == EHCI_HCD_OMAP_MODE_TLL)
+ reg |= OMAP_UHH_HOSTCONFIG_ULPI_P2_BYPASS;
+
+ if (omap->port_mode[2] == EHCI_HCD_OMAP_MODE_PHY)
+ reg &= ~OMAP_UHH_HOSTCONFIG_ULPI_P3_BYPASS;
+ else if (omap->port_mode[2] == EHCI_HCD_OMAP_MODE_TLL)
+ reg |= OMAP_UHH_HOSTCONFIG_ULPI_P3_BYPASS;
+
+ }
+ ehci_omap_writel(omap->uhh_base, OMAP_UHH_HOSTCONFIG, reg);
+ dev_dbg(omap->dev, "UHH setup done, uhh_hostconfig=%x\n", reg);
+
+
+ if ((omap->port_mode[0] == EHCI_HCD_OMAP_MODE_TLL) ||
+ (omap->port_mode[1] == EHCI_HCD_OMAP_MODE_TLL) ||
+ (omap->port_mode[2] == EHCI_HCD_OMAP_MODE_TLL)) {
+
+ if (omap->port_mode[0] == EHCI_HCD_OMAP_MODE_TLL)
+ tll_ch_mask |= OMAP_TLL_CHANNEL_1_EN_MASK;
+ if (omap->port_mode[1] == EHCI_HCD_OMAP_MODE_TLL)
+ tll_ch_mask |= OMAP_TLL_CHANNEL_2_EN_MASK;
+ if (omap->port_mode[2] == EHCI_HCD_OMAP_MODE_TLL)
+ tll_ch_mask |= OMAP_TLL_CHANNEL_3_EN_MASK;
+
+ /* Enable UTMI mode for required TLL channels */
+ omap_usb_utmi_init(omap, tll_ch_mask);
+ }
+
+ if (omap->phy_reset) {
+ /* Refer ISSUE1:
+ * Hold the PHY in RESET for enough time till
+ * PHY is settled and ready
+ */
+ udelay(10);
+
+ if (gpio_is_valid(omap->reset_gpio_port[0]))
+ gpio_set_value(omap->reset_gpio_port[0], 1);
+
+ if (gpio_is_valid(omap->reset_gpio_port[1]))
+ gpio_set_value(omap->reset_gpio_port[1], 1);
+ }
+
+ return 0;
+
+err_sys_status:
+ clk_disable(omap->usbtll_ick);
+ clk_put(omap->usbtll_ick);
+
+err_tll_ick:
+ clk_disable(omap->usbtll_fck);
+ clk_put(omap->usbtll_fck);
+
+err_tll_fck:
+ clk_disable(omap->usbhost1_48m_fck);
+ clk_put(omap->usbhost1_48m_fck);
+
+ if (omap->phy_reset) {
+ if (gpio_is_valid(omap->reset_gpio_port[0]))
+ gpio_free(omap->reset_gpio_port[0]);
+
+ if (gpio_is_valid(omap->reset_gpio_port[1]))
+ gpio_free(omap->reset_gpio_port[1]);
+ }
+
+err_host_48m_fck:
+ clk_disable(omap->usbhost2_120m_fck);
+ clk_put(omap->usbhost2_120m_fck);
+
+err_host_120m_fck:
+ clk_disable(omap->usbhost_ick);
+ clk_put(omap->usbhost_ick);
+
+err_host_ick:
+ return ret;
+}
+
+static void omap_stop_ehc(struct ehci_hcd_omap *omap, struct usb_hcd *hcd)
+{
+ unsigned long timeout = jiffies + msecs_to_jiffies(100);
+
+ dev_dbg(omap->dev, "stopping TI EHCI USB Controller\n");
+
+ /* Reset OMAP modules for insmod/rmmod to work */
+ ehci_omap_writel(omap->uhh_base, OMAP_UHH_SYSCONFIG,
+ OMAP_UHH_SYSCONFIG_SOFTRESET);
+ while (!(ehci_omap_readl(omap->uhh_base, OMAP_UHH_SYSSTATUS)
+ & (1 << 0))) {
+ cpu_relax();
+
+ if (time_after(jiffies, timeout))
+ dev_dbg(omap->dev, "operation timed out\n");
+ }
+
+ while (!(ehci_omap_readl(omap->uhh_base, OMAP_UHH_SYSSTATUS)
+ & (1 << 1))) {
+ cpu_relax();
+
+ if (time_after(jiffies, timeout))
+ dev_dbg(omap->dev, "operation timed out\n");
+ }
+
+ while (!(ehci_omap_readl(omap->uhh_base, OMAP_UHH_SYSSTATUS)
+ & (1 << 2))) {
+ cpu_relax();
+
+ if (time_after(jiffies, timeout))
+ dev_dbg(omap->dev, "operation timed out\n");
+ }
+
+ ehci_omap_writel(omap->tll_base, OMAP_USBTLL_SYSCONFIG, (1 << 1));
+
+ while (!(ehci_omap_readl(omap->tll_base, OMAP_USBTLL_SYSSTATUS)
+ & (1 << 0))) {
+ cpu_relax();
+
+ if (time_after(jiffies, timeout))
+ dev_dbg(omap->dev, "operation timed out\n");
+ }
+
+ if (omap->usbtll_fck != NULL) {
+ clk_disable(omap->usbtll_fck);
+ clk_put(omap->usbtll_fck);
+ omap->usbtll_fck = NULL;
+ }
+
+ if (omap->usbhost_ick != NULL) {
+ clk_disable(omap->usbhost_ick);
+ clk_put(omap->usbhost_ick);
+ omap->usbhost_ick = NULL;
+ }
+
+ if (omap->usbhost1_48m_fck != NULL) {
+ clk_disable(omap->usbhost1_48m_fck);
+ clk_put(omap->usbhost1_48m_fck);
+ omap->usbhost1_48m_fck = NULL;
+ }
+
+ if (omap->usbhost2_120m_fck != NULL) {
+ clk_disable(omap->usbhost2_120m_fck);
+ clk_put(omap->usbhost2_120m_fck);
+ omap->usbhost2_120m_fck = NULL;
+ }
+
+ if (omap->usbtll_ick != NULL) {
+ clk_disable(omap->usbtll_ick);
+ clk_put(omap->usbtll_ick);
+ omap->usbtll_ick = NULL;
+ }
+
+ if (omap->phy_reset) {
+ if (gpio_is_valid(omap->reset_gpio_port[0]))
+ gpio_free(omap->reset_gpio_port[0]);
+
+ if (gpio_is_valid(omap->reset_gpio_port[1]))
+ gpio_free(omap->reset_gpio_port[1]);
+ }
+
+ dev_dbg(omap->dev, "Clock to USB host has been disabled\n");
+}
+
+/*-------------------------------------------------------------------------*/
+
+static const struct hc_driver ehci_omap_hc_driver;
+
+/* configure so an HC device and id are always provided */
+/* always called with process context; sleeping is OK */
+
+/**
+ * ehci_hcd_omap_probe - initialize TI-based HCDs
+ *
+ * Allocates basic resources for this USB host controller, and
+ * then invokes the start() method for the HCD associated with it
+ * through the hotplug entry's driver_data.
+ */
+static int ehci_hcd_omap_probe(struct platform_device *pdev)
+{
+ struct ehci_hcd_omap_platform_data *pdata = pdev->dev.platform_data;
+ struct ehci_hcd_omap *omap;
+ struct resource *res;
+ struct usb_hcd *hcd;
+
+ int irq = platform_get_irq(pdev, 0);
+ int ret = -ENODEV;
+
+ if (!pdata) {
+ dev_dbg(&pdev->dev, "missing platform_data\n");
+ goto err_pdata;
+ }
+
+ if (usb_disabled())
+ goto err_disabled;
+
+ omap = kzalloc(sizeof(*omap), GFP_KERNEL);
+ if (!omap) {
+ ret = -ENOMEM;
+ goto err_disabled;
+ }
+
+ hcd = usb_create_hcd(&ehci_omap_hc_driver, &pdev->dev,
+ dev_name(&pdev->dev));
+ if (!hcd) {
+ dev_dbg(&pdev->dev, "failed to create hcd with err %d\n", ret);
+ ret = -ENOMEM;
+ goto err_create_hcd;
+ }
+
+ platform_set_drvdata(pdev, omap);
+ omap->dev = &pdev->dev;
+ omap->phy_reset = pdata->phy_reset;
+ omap->reset_gpio_port[0] = pdata->reset_gpio_port[0];
+ omap->reset_gpio_port[1] = pdata->reset_gpio_port[1];
+ omap->reset_gpio_port[2] = pdata->reset_gpio_port[2];
+ omap->port_mode[0] = pdata->port_mode[0];
+ omap->port_mode[1] = pdata->port_mode[1];
+ omap->port_mode[2] = pdata->port_mode[2];
+ omap->ehci = hcd_to_ehci(hcd);
+ omap->ehci->sbrn = 0x20;
+
+ res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
+
+ hcd->rsrc_start = res->start;
+ hcd->rsrc_len = resource_size(res);
+
+ hcd->regs = ioremap(hcd->rsrc_start, hcd->rsrc_len);
+ if (!hcd->regs) {
+ dev_err(&pdev->dev, "EHCI ioremap failed\n");
+ ret = -ENOMEM;
+ goto err_ioremap;
+ }
+
+ /* we know this is the memory we want, no need to ioremap again */
+ omap->ehci->caps = hcd->regs;
+ omap->ehci_base = hcd->regs;
+
+ res = platform_get_resource(pdev, IORESOURCE_MEM, 1);
+ omap->uhh_base = ioremap(res->start, resource_size(res));
+ if (!omap->uhh_base) {
+ dev_err(&pdev->dev, "UHH ioremap failed\n");
+ ret = -ENOMEM;
+ goto err_uhh_ioremap;
+ }
+
+ res = platform_get_resource(pdev, IORESOURCE_MEM, 2);
+ omap->tll_base = ioremap(res->start, resource_size(res));
+ if (!omap->tll_base) {
+ dev_err(&pdev->dev, "TLL ioremap failed\n");
+ ret = -ENOMEM;
+ goto err_tll_ioremap;
+ }
+
+ ret = omap_start_ehc(omap, hcd);
+ if (ret) {
+ dev_dbg(&pdev->dev, "failed to start ehci\n");
+ goto err_start;
+ }
+
+ omap->ehci->regs = hcd->regs
+ + HC_LENGTH(readl(&omap->ehci->caps->hc_capbase));
+
+ /* cache this readonly data; minimize chip reads */
+ omap->ehci->hcs_params = readl(&omap->ehci->caps->hcs_params);
+
+ /* SET 1 micro-frame Interrupt interval */
+ writel(readl(&omap->ehci->regs->command) | (1 << 16),
+ &omap->ehci->regs->command);
+
+ ret = usb_add_hcd(hcd, irq, IRQF_DISABLED | IRQF_SHARED);
+ if (ret) {
+ dev_dbg(&pdev->dev, "failed to add hcd with err %d\n", ret);
+ goto err_add_hcd;
+ }
+
+ return 0;
+
+err_add_hcd:
+ omap_stop_ehc(omap, hcd);
+
+err_start:
+ iounmap(omap->tll_base);
+
+err_tll_ioremap:
+ iounmap(omap->uhh_base);
+
+err_uhh_ioremap:
+ iounmap(hcd->regs);
+
+err_ioremap:
+ usb_put_hcd(hcd);
+
+err_create_hcd:
+ kfree(omap);
+err_disabled:
+err_pdata:
+ return ret;
+}
+
+/* may be called without controller electrically present */
+/* may be called with controller, bus, and devices active */
+
+/**
+ * ehci_hcd_omap_remove - shutdown processing for EHCI HCDs
+ * @pdev: USB Host Controller being removed
+ *
+ * Reverses the effect of usb_ehci_hcd_omap_probe(), first invoking
+ * the HCD's stop() method. It is always called from a thread
+ * context, normally "rmmod", "apmd", or something similar.
+ */
+static int ehci_hcd_omap_remove(struct platform_device *pdev)
+{
+ struct ehci_hcd_omap *omap = platform_get_drvdata(pdev);
+ struct usb_hcd *hcd = ehci_to_hcd(omap->ehci);
+
+ usb_remove_hcd(hcd);
+ omap_stop_ehc(omap, hcd);
+ iounmap(hcd->regs);
+ iounmap(omap->tll_base);
+ iounmap(omap->uhh_base);
+ usb_put_hcd(hcd);
+
+ return 0;
+}
+
+static void ehci_hcd_omap_shutdown(struct platform_device *pdev)
+{
+ struct ehci_hcd_omap *omap = platform_get_drvdata(pdev);
+ struct usb_hcd *hcd = ehci_to_hcd(omap->ehci);
+
+ if (hcd->driver->shutdown)
+ hcd->driver->shutdown(hcd);
+}
+
+static struct platform_driver ehci_hcd_omap_driver = {
+ .probe = ehci_hcd_omap_probe,
+ .remove = ehci_hcd_omap_remove,
+ .shutdown = ehci_hcd_omap_shutdown,
+ /*.suspend = ehci_hcd_omap_suspend, */
+ /*.resume = ehci_hcd_omap_resume, */
+ .driver = {
+ .name = "ehci-omap",
+ }
+};
+
+/*-------------------------------------------------------------------------*/
+
+static const struct hc_driver ehci_omap_hc_driver = {
+ .description = hcd_name,
+ .product_desc = "OMAP-EHCI Host Controller",
+ .hcd_priv_size = sizeof(struct ehci_hcd),
+
+ /*
+ * generic hardware linkage
+ */
+ .irq = ehci_irq,
+ .flags = HCD_MEMORY | HCD_USB2,
+
+ /*
+ * basic lifecycle operations
+ */
+ .reset = ehci_init,
+ .start = ehci_run,
+ .stop = ehci_stop,
+ .shutdown = ehci_shutdown,
+
+ /*
+ * managing i/o requests and associated device resources
+ */
+ .urb_enqueue = ehci_urb_enqueue,
+ .urb_dequeue = ehci_urb_dequeue,
+ .endpoint_disable = ehci_endpoint_disable,
+ .endpoint_reset = ehci_endpoint_reset,
+
+ /*
+ * scheduling support
+ */
+ .get_frame_number = ehci_get_frame,
+
+ /*
+ * root hub support
+ */
+ .hub_status_data = ehci_hub_status_data,
+ .hub_control = ehci_hub_control,
+ .bus_suspend = ehci_bus_suspend,
+ .bus_resume = ehci_bus_resume,
+
+ .clear_tt_buffer_complete = ehci_clear_tt_buffer_complete,
+};
+
+MODULE_ALIAS("platform:omap-ehci");
+MODULE_AUTHOR("Texas Instruments, Inc.");
+MODULE_AUTHOR("Felipe Balbi <felipe.balbi@nokia.com>");
+
diff --git a/drivers/usb/host/ehci-q.c b/drivers/usb/host/ehci-q.c
index 139a2cc..a427d3b 100644
--- a/drivers/usb/host/ehci-q.c
+++ b/drivers/usb/host/ehci-q.c
@@ -616,9 +616,11 @@ qh_urb_transaction (
) {
struct ehci_qtd *qtd, *qtd_prev;
dma_addr_t buf;
- int len, maxpacket;
+ int len, this_sg_len, maxpacket;
int is_input;
u32 token;
+ int i;
+ struct scatterlist *sg;
/*
* URBs map to sequences of QTDs: one logical transaction
@@ -659,7 +661,20 @@ qh_urb_transaction (
/*
* data transfer stage: buffer setup
*/
- buf = urb->transfer_dma;
+ i = urb->num_sgs;
+ if (len > 0 && i > 0) {
+ sg = urb->sg->sg;
+ buf = sg_dma_address(sg);
+
+ /* urb->transfer_buffer_length may be smaller than the
+ * size of the scatterlist (or vice versa)
+ */
+ this_sg_len = min_t(int, sg_dma_len(sg), len);
+ } else {
+ sg = NULL;
+ buf = urb->transfer_dma;
+ this_sg_len = len;
+ }
if (is_input)
token |= (1 /* "in" */ << 8);
@@ -675,7 +690,9 @@ qh_urb_transaction (
for (;;) {
int this_qtd_len;
- this_qtd_len = qtd_fill(ehci, qtd, buf, len, token, maxpacket);
+ this_qtd_len = qtd_fill(ehci, qtd, buf, this_sg_len, token,
+ maxpacket);
+ this_sg_len -= this_qtd_len;
len -= this_qtd_len;
buf += this_qtd_len;
@@ -691,8 +708,13 @@ qh_urb_transaction (
if ((maxpacket & (this_qtd_len + (maxpacket - 1))) == 0)
token ^= QTD_TOGGLE;
- if (likely (len <= 0))
- break;
+ if (likely(this_sg_len <= 0)) {
+ if (--i <= 0 || len <= 0)
+ break;
+ sg = sg_next(sg);
+ buf = sg_dma_address(sg);
+ this_sg_len = min_t(int, sg_dma_len(sg), len);
+ }
qtd_prev = qtd;
qtd = ehci_qtd_alloc (ehci, flags);
diff --git a/drivers/usb/host/ehci-sched.c b/drivers/usb/host/ehci-sched.c
index a5535b5..1e391e6 100644
--- a/drivers/usb/host/ehci-sched.c
+++ b/drivers/usb/host/ehci-sched.c
@@ -1385,7 +1385,7 @@ sitd_slot_ok (
* given EHCI_TUNE_FLS and the slop). Or, write a smarter scheduler!
*/
-#define SCHEDULE_SLOP 10 /* frames */
+#define SCHEDULE_SLOP 80 /* microframes */
static int
iso_stream_schedule (
@@ -1394,12 +1394,13 @@ iso_stream_schedule (
struct ehci_iso_stream *stream
)
{
- u32 now, start, max, period;
+ u32 now, next, start, period;
int status;
unsigned mod = ehci->periodic_size << 3;
struct ehci_iso_sched *sched = urb->hcpriv;
+ struct pci_dev *pdev;
- if (sched->span > (mod - 8 * SCHEDULE_SLOP)) {
+ if (sched->span > (mod - SCHEDULE_SLOP)) {
ehci_dbg (ehci, "iso request %p too long\n", urb);
status = -EFBIG;
goto fail;
@@ -1418,26 +1419,35 @@ iso_stream_schedule (
now = ehci_readl(ehci, &ehci->regs->frame_index) % mod;
- /* when's the last uframe this urb could start? */
- max = now + mod;
-
/* Typical case: reuse current schedule, stream is still active.
* Hopefully there are no gaps from the host falling behind
* (irq delays etc), but if there are we'll take the next
* slot in the schedule, implicitly assuming URB_ISO_ASAP.
*/
if (likely (!list_empty (&stream->td_list))) {
+ pdev = to_pci_dev(ehci_to_hcd(ehci)->self.controller);
start = stream->next_uframe;
- if (start < now)
- start += mod;
+
+ /* For high speed devices, allow scheduling within the
+ * isochronous scheduling threshold. For full speed devices,
+ * don't. (Work around for Intel ICH9 bug.)
+ */
+ if (!stream->highspeed &&
+ pdev->vendor == PCI_VENDOR_ID_INTEL)
+ next = now + ehci->i_thresh;
+ else
+ next = now;
/* Fell behind (by up to twice the slop amount)? */
- if (start >= max - 2 * 8 * SCHEDULE_SLOP)
+ if (((start - next) & (mod - 1)) >=
+ mod - 2 * SCHEDULE_SLOP)
start += period * DIV_ROUND_UP(
- max - start, period) - mod;
+ (next - start) & (mod - 1),
+ period);
/* Tried to schedule too far into the future? */
- if (unlikely((start + sched->span) >= max)) {
+ if (unlikely(((start - now) & (mod - 1)) + sched->span
+ >= mod - 2 * SCHEDULE_SLOP)) {
status = -EFBIG;
goto fail;
}
@@ -1451,7 +1461,7 @@ iso_stream_schedule (
* can also help high bandwidth if the dma and irq loads don't
* jump until after the queue is primed.
*/
- start = SCHEDULE_SLOP * 8 + (now & ~0x07);
+ start = SCHEDULE_SLOP + (now & ~0x07);
start %= mod;
stream->next_uframe = start;
@@ -1482,7 +1492,7 @@ iso_stream_schedule (
/* no room in the schedule */
ehci_dbg (ehci, "iso %ssched full %p (now %d max %d)\n",
list_empty (&stream->td_list) ? "" : "re",
- urb, now, max);
+ urb, now, now + mod);
status = -ENOSPC;
fail:
diff --git a/drivers/usb/host/ehci-xilinx-of.c b/drivers/usb/host/ehci-xilinx-of.c
new file mode 100644
index 0000000..a5861531
--- /dev/null
+++ b/drivers/usb/host/ehci-xilinx-of.c
@@ -0,0 +1,300 @@
+/*
+ * EHCI HCD (Host Controller Driver) for USB.
+ *
+ * Bus Glue for Xilinx EHCI core on the of_platform bus
+ *
+ * Copyright (c) 2009 Xilinx, Inc.
+ *
+ * Based on "ehci-ppc-of.c" by Valentine Barshak <vbarshak@ru.mvista.com>
+ * and "ehci-ppc-soc.c" by Stefan Roese <sr@denx.de>
+ * and "ohci-ppc-of.c" by Sylvain Munaut <tnt@246tNt.com>
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License as published by the
+ * Free Software Foundation; either version 2 of the License, or (at your
+ * option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
+ * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
+ * for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software Foundation,
+ * Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ *
+ */
+
+#include <linux/signal.h>
+
+#include <linux/of.h>
+#include <linux/of_platform.h>
+
+/**
+ * ehci_xilinx_of_setup - Initialize the device for ehci_reset()
+ * @hcd: Pointer to the usb_hcd device to which the host controller bound
+ *
+ * called during probe() after chip reset completes.
+ */
+static int ehci_xilinx_of_setup(struct usb_hcd *hcd)
+{
+ struct ehci_hcd *ehci = hcd_to_ehci(hcd);
+ int retval;
+
+ retval = ehci_halt(ehci);
+ if (retval)
+ return retval;
+
+ retval = ehci_init(hcd);
+ if (retval)
+ return retval;
+
+ ehci->sbrn = 0x20;
+
+ return ehci_reset(ehci);
+}
+
+/**
+ * ehci_xilinx_port_handed_over - hand the port out if failed to enable it
+ * @hcd: Pointer to the usb_hcd device to which the host controller bound
+ * @portnum:Port number to which the device is attached.
+ *
+ * This function is used as a place to tell the user that the Xilinx USB host
+ * controller does support LS devices. And in an HS only configuration, it
+ * does not support FS devices either. It is hoped that this can help a
+ * confused user.
+ *
+ * There are cases when the host controller fails to enable the port due to,
+ * for example, insufficient power that can be supplied to the device from
+ * the USB bus. In those cases, the messages printed here are not helpful.
+ */
+static int ehci_xilinx_port_handed_over(struct usb_hcd *hcd, int portnum)
+{
+ dev_warn(hcd->self.controller, "port %d cannot be enabled\n", portnum);
+ if (hcd->has_tt) {
+ dev_warn(hcd->self.controller,
+ "Maybe you have connected a low speed device?\n");
+
+ dev_warn(hcd->self.controller,
+ "We do not support low speed devices\n");
+ } else {
+ dev_warn(hcd->self.controller,
+ "Maybe your device is not a high speed device?\n");
+ dev_warn(hcd->self.controller,
+ "The USB host controller does not support full speed "
+ "nor low speed devices\n");
+ dev_warn(hcd->self.controller,
+ "You can reconfigure the host controller to have "
+ "full speed support\n");
+ }
+
+ return 0;
+}
+
+
+static const struct hc_driver ehci_xilinx_of_hc_driver = {
+ .description = hcd_name,
+ .product_desc = "OF EHCI",
+ .hcd_priv_size = sizeof(struct ehci_hcd),
+
+ /*
+ * generic hardware linkage
+ */
+ .irq = ehci_irq,
+ .flags = HCD_MEMORY | HCD_USB2,
+
+ /*
+ * basic lifecycle operations
+ */
+ .reset = ehci_xilinx_of_setup,
+ .start = ehci_run,
+ .stop = ehci_stop,
+ .shutdown = ehci_shutdown,
+
+ /*
+ * managing i/o requests and associated device resources
+ */
+ .urb_enqueue = ehci_urb_enqueue,
+ .urb_dequeue = ehci_urb_dequeue,
+ .endpoint_disable = ehci_endpoint_disable,
+
+ /*
+ * scheduling support
+ */
+ .get_frame_number = ehci_get_frame,
+
+ /*
+ * root hub support
+ */
+ .hub_status_data = ehci_hub_status_data,
+ .hub_control = ehci_hub_control,
+#ifdef CONFIG_PM
+ .bus_suspend = ehci_bus_suspend,
+ .bus_resume = ehci_bus_resume,
+#endif
+ .relinquish_port = NULL,
+ .port_handed_over = ehci_xilinx_port_handed_over,
+
+ .clear_tt_buffer_complete = ehci_clear_tt_buffer_complete,
+};
+
+/**
+ * ehci_hcd_xilinx_of_probe - Probe method for the USB host controller
+ * @op: pointer to the of_device to which the host controller bound
+ * @match: pointer to of_device_id structure, not used
+ *
+ * This function requests resources and sets up appropriate properties for the
+ * host controller. Because the Xilinx USB host controller can be configured
+ * as HS only or HS/FS only, it checks the configuration in the device tree
+ * entry, and sets an appropriate value for hcd->has_tt.
+ */
+static int __devinit
+ehci_hcd_xilinx_of_probe(struct of_device *op, const struct of_device_id *match)
+{
+ struct device_node *dn = op->node;
+ struct usb_hcd *hcd;
+ struct ehci_hcd *ehci;
+ struct resource res;
+ int irq;
+ int rv;
+ int *value;
+
+ if (usb_disabled())
+ return -ENODEV;
+
+ dev_dbg(&op->dev, "initializing XILINX-OF USB Controller\n");
+
+ rv = of_address_to_resource(dn, 0, &res);
+ if (rv)
+ return rv;
+
+ hcd = usb_create_hcd(&ehci_xilinx_of_hc_driver, &op->dev,
+ "XILINX-OF USB");
+ if (!hcd)
+ return -ENOMEM;
+
+ hcd->rsrc_start = res.start;
+ hcd->rsrc_len = res.end - res.start + 1;
+
+ if (!request_mem_region(hcd->rsrc_start, hcd->rsrc_len, hcd_name)) {
+ printk(KERN_ERR __FILE__ ": request_mem_region failed\n");
+ rv = -EBUSY;
+ goto err_rmr;
+ }
+
+ irq = irq_of_parse_and_map(dn, 0);
+ if (irq == NO_IRQ) {
+ printk(KERN_ERR __FILE__ ": irq_of_parse_and_map failed\n");
+ rv = -EBUSY;
+ goto err_irq;
+ }
+
+ hcd->regs = ioremap(hcd->rsrc_start, hcd->rsrc_len);
+ if (!hcd->regs) {
+ printk(KERN_ERR __FILE__ ": ioremap failed\n");
+ rv = -ENOMEM;
+ goto err_ioremap;
+ }
+
+ ehci = hcd_to_ehci(hcd);
+
+ /* This core always has big-endian register interface and uses
+ * big-endian memory descriptors.
+ */
+ ehci->big_endian_mmio = 1;
+ ehci->big_endian_desc = 1;
+
+ /* Check whether the FS support option is selected in the hardware.
+ */
+ value = (int *)of_get_property(dn, "xlnx,support-usb-fs", NULL);
+ if (value && (*value == 1)) {
+ ehci_dbg(ehci, "USB host controller supports FS devices\n");
+ hcd->has_tt = 1;
+ } else {
+ ehci_dbg(ehci,
+ "USB host controller is HS only\n");
+ hcd->has_tt = 0;
+ }
+
+ /* Debug registers are at the first 0x100 region
+ */
+ ehci->caps = hcd->regs + 0x100;
+ ehci->regs = hcd->regs + 0x100 +
+ HC_LENGTH(ehci_readl(ehci, &ehci->caps->hc_capbase));
+
+ /* cache this readonly data; minimize chip reads */
+ ehci->hcs_params = ehci_readl(ehci, &ehci->caps->hcs_params);
+
+ rv = usb_add_hcd(hcd, irq, 0);
+ if (rv == 0)
+ return 0;
+
+ iounmap(hcd->regs);
+
+err_ioremap:
+err_irq:
+ release_mem_region(hcd->rsrc_start, hcd->rsrc_len);
+err_rmr:
+ usb_put_hcd(hcd);
+
+ return rv;
+}
+
+/**
+ * ehci_hcd_xilinx_of_remove - shutdown hcd and release resources
+ * @op: pointer to of_device structure that is to be removed
+ *
+ * Remove the hcd structure, and release resources that has been requested
+ * during probe.
+ */
+static int ehci_hcd_xilinx_of_remove(struct of_device *op)
+{
+ struct usb_hcd *hcd = dev_get_drvdata(&op->dev);
+ dev_set_drvdata(&op->dev, NULL);
+
+ dev_dbg(&op->dev, "stopping XILINX-OF USB Controller\n");
+
+ usb_remove_hcd(hcd);
+
+ iounmap(hcd->regs);
+ release_mem_region(hcd->rsrc_start, hcd->rsrc_len);
+
+ usb_put_hcd(hcd);
+
+ return 0;
+}
+
+/**
+ * ehci_hcd_xilinx_of_shutdown - shutdown the hcd
+ * @op: pointer to of_device structure that is to be removed
+ *
+ * Properly shutdown the hcd, call driver's shutdown routine.
+ */
+static int ehci_hcd_xilinx_of_shutdown(struct of_device *op)
+{
+ struct usb_hcd *hcd = dev_get_drvdata(&op->dev);
+
+ if (hcd->driver->shutdown)
+ hcd->driver->shutdown(hcd);
+
+ return 0;
+}
+
+
+static struct of_device_id ehci_hcd_xilinx_of_match[] = {
+ {.compatible = "xlnx,xps-usb-host-1.00.a",},
+ {},
+};
+MODULE_DEVICE_TABLE(of, ehci_hcd_xilinx_of_match);
+
+static struct of_platform_driver ehci_hcd_xilinx_of_driver = {
+ .name = "xilinx-of-ehci",
+ .match_table = ehci_hcd_xilinx_of_match,
+ .probe = ehci_hcd_xilinx_of_probe,
+ .remove = ehci_hcd_xilinx_of_remove,
+ .shutdown = ehci_hcd_xilinx_of_shutdown,
+ .driver = {
+ .name = "xilinx-of-ehci",
+ .owner = THIS_MODULE,
+ },
+};
diff --git a/drivers/usb/host/isp1362.h b/drivers/usb/host/isp1362.h
index 1a253eb..5151516 100644
--- a/drivers/usb/host/isp1362.h
+++ b/drivers/usb/host/isp1362.h
@@ -534,8 +534,8 @@ struct isp1362_hcd {
/* periodic schedule: isochronous */
struct list_head isoc;
- int istl_flip:1;
- int irq_active:1;
+ unsigned int istl_flip:1;
+ unsigned int irq_active:1;
/* Schedules for the current frame */
struct isp1362_ep_queue atl_queue;
diff --git a/drivers/usb/host/ohci-at91.c b/drivers/usb/host/ohci-at91.c
index 7ccffcb..68b83ab 100644
--- a/drivers/usb/host/ohci-at91.c
+++ b/drivers/usb/host/ohci-at91.c
@@ -35,7 +35,7 @@ extern int usb_disabled(void);
static void at91_start_clock(void)
{
- if (cpu_is_at91sam9261())
+ if (cpu_is_at91sam9261() || cpu_is_at91sam9g10())
clk_enable(hclk);
clk_enable(iclk);
clk_enable(fclk);
@@ -46,7 +46,7 @@ static void at91_stop_clock(void)
{
clk_disable(fclk);
clk_disable(iclk);
- if (cpu_is_at91sam9261())
+ if (cpu_is_at91sam9261() || cpu_is_at91sam9g10())
clk_disable(hclk);
clocked = 0;
}
@@ -142,7 +142,7 @@ static int usb_hcd_at91_probe(const struct hc_driver *driver,
iclk = clk_get(&pdev->dev, "ohci_clk");
fclk = clk_get(&pdev->dev, "uhpck");
- if (cpu_is_at91sam9261())
+ if (cpu_is_at91sam9261() || cpu_is_at91sam9g10())
hclk = clk_get(&pdev->dev, "hck0");
at91_start_hc(pdev);
@@ -155,7 +155,7 @@ static int usb_hcd_at91_probe(const struct hc_driver *driver,
/* Error handling */
at91_stop_hc(pdev);
- if (cpu_is_at91sam9261())
+ if (cpu_is_at91sam9261() || cpu_is_at91sam9g10())
clk_put(hclk);
clk_put(fclk);
clk_put(iclk);
@@ -192,7 +192,7 @@ static void usb_hcd_at91_remove(struct usb_hcd *hcd,
release_mem_region(hcd->rsrc_start, hcd->rsrc_len);
usb_put_hcd(hcd);
- if (cpu_is_at91sam9261())
+ if (cpu_is_at91sam9261() || cpu_is_at91sam9g10())
clk_put(hclk);
clk_put(fclk);
clk_put(iclk);
diff --git a/drivers/usb/host/ohci-pnx4008.c b/drivers/usb/host/ohci-pnx4008.c
index 100bf3d..2769326 100644
--- a/drivers/usb/host/ohci-pnx4008.c
+++ b/drivers/usb/host/ohci-pnx4008.c
@@ -98,8 +98,8 @@
#define ISP1301_I2C_INTERRUPT_RISING 0xE
#define ISP1301_I2C_REG_CLEAR_ADDR 1
-struct i2c_driver isp1301_driver;
-struct i2c_client *isp1301_i2c_client;
+static struct i2c_driver isp1301_driver;
+static struct i2c_client *isp1301_i2c_client;
extern int usb_disabled(void);
extern int ocpi_enable(void);
@@ -120,12 +120,12 @@ static int isp1301_remove(struct i2c_client *client)
return 0;
}
-const struct i2c_device_id isp1301_id[] = {
+static const struct i2c_device_id isp1301_id[] = {
{ "isp1301_pnx", 0 },
{ }
};
-struct i2c_driver isp1301_driver = {
+static struct i2c_driver isp1301_driver = {
.driver = {
.name = "isp1301_pnx",
},
diff --git a/drivers/usb/host/r8a66597-hcd.c b/drivers/usb/host/r8a66597-hcd.c
index e33d362..41dbc70 100644
--- a/drivers/usb/host/r8a66597-hcd.c
+++ b/drivers/usb/host/r8a66597-hcd.c
@@ -822,8 +822,6 @@ static void force_dequeue(struct r8a66597 *r8a66597, u16 pipenum, u16 address)
return;
list_for_each_entry_safe(td, next, list, queue) {
- if (!td)
- continue;
if (td->address != address)
continue;
@@ -2025,8 +2023,6 @@ static struct r8a66597_device *get_r8a66597_device(struct r8a66597 *r8a66597,
struct list_head *list = &r8a66597->child_device;
list_for_each_entry(dev, list, device_list) {
- if (!dev)
- continue;
if (dev->usb_address != addr)
continue;
diff --git a/drivers/usb/host/whci/debug.c b/drivers/usb/host/whci/debug.c
index 2273c81..8c1c610 100644
--- a/drivers/usb/host/whci/debug.c
+++ b/drivers/usb/host/whci/debug.c
@@ -31,17 +31,29 @@ struct whc_dbg {
void qset_print(struct seq_file *s, struct whc_qset *qset)
{
+ static const char *qh_type[] = {
+ "ctrl", "isoc", "bulk", "intr", "rsvd", "rsvd", "rsvd", "lpintr", };
struct whc_std *std;
struct urb *urb = NULL;
int i;
- seq_printf(s, "qset %08x\n", (u32)qset->qset_dma);
+ seq_printf(s, "qset %08x", (u32)qset->qset_dma);
+ if (&qset->list_node == qset->whc->async_list.prev) {
+ seq_printf(s, " (dummy)\n");
+ } else {
+ seq_printf(s, " ep%d%s-%s maxpkt: %d\n",
+ qset->qh.info1 & 0x0f,
+ (qset->qh.info1 >> 4) & 0x1 ? "in" : "out",
+ qh_type[(qset->qh.info1 >> 5) & 0x7],
+ (qset->qh.info1 >> 16) & 0xffff);
+ }
seq_printf(s, " -> %08x\n", (u32)qset->qh.link);
seq_printf(s, " info: %08x %08x %08x\n",
- qset->qh.info1, qset->qh.info2, qset->qh.info3);
- seq_printf(s, " sts: %04x errs: %d\n", qset->qh.status, qset->qh.err_count);
+ qset->qh.info1, qset->qh.info2, qset->qh.info3);
+ seq_printf(s, " sts: %04x errs: %d curwin: %08x\n",
+ qset->qh.status, qset->qh.err_count, qset->qh.cur_window);
seq_printf(s, " TD: sts: %08x opts: %08x\n",
- qset->qh.overlay.qtd.status, qset->qh.overlay.qtd.options);
+ qset->qh.overlay.qtd.status, qset->qh.overlay.qtd.options);
for (i = 0; i < WHCI_QSET_TD_MAX; i++) {
seq_printf(s, " %c%c TD[%d]: sts: %08x opts: %08x ptr: %08x\n",
diff --git a/drivers/usb/host/whci/hcd.c b/drivers/usb/host/whci/hcd.c
index 687b622..e0d3401 100644
--- a/drivers/usb/host/whci/hcd.c
+++ b/drivers/usb/host/whci/hcd.c
@@ -250,6 +250,7 @@ static int whc_probe(struct umc_dev *umc)
}
usb_hcd->wireless = 1;
+ usb_hcd->self.sg_tablesize = 2048; /* somewhat arbitrary */
wusbhc = usb_hcd_to_wusbhc(usb_hcd);
whc = wusbhc_to_whc(wusbhc);
diff --git a/drivers/usb/host/whci/qset.c b/drivers/usb/host/whci/qset.c
index 1b9dc15..7d4204d 100644
--- a/drivers/usb/host/whci/qset.c
+++ b/drivers/usb/host/whci/qset.c
@@ -49,16 +49,19 @@ struct whc_qset *qset_alloc(struct whc *whc, gfp_t mem_flags)
* state
* @urb: an urb for a transfer to this endpoint
*/
-static void qset_fill_qh(struct whc_qset *qset, struct urb *urb)
+static void qset_fill_qh(struct whc *whc, struct whc_qset *qset, struct urb *urb)
{
struct usb_device *usb_dev = urb->dev;
+ struct wusb_dev *wusb_dev = usb_dev->wusb_dev;
struct usb_wireless_ep_comp_descriptor *epcd;
bool is_out;
+ uint8_t phy_rate;
is_out = usb_pipeout(urb->pipe);
- epcd = (struct usb_wireless_ep_comp_descriptor *)qset->ep->extra;
+ qset->max_packet = le16_to_cpu(urb->ep->desc.wMaxPacketSize);
+ epcd = (struct usb_wireless_ep_comp_descriptor *)qset->ep->extra;
if (epcd) {
qset->max_seq = epcd->bMaxSequence;
qset->max_burst = epcd->bMaxBurst;
@@ -67,12 +70,28 @@ static void qset_fill_qh(struct whc_qset *qset, struct urb *urb)
qset->max_burst = 1;
}
+ /*
+ * Initial PHY rate is 53.3 Mbit/s for control endpoints or
+ * the maximum supported by the device for other endpoints
+ * (unless limited by the user).
+ */
+ if (usb_pipecontrol(urb->pipe))
+ phy_rate = UWB_PHY_RATE_53;
+ else {
+ uint16_t phy_rates;
+
+ phy_rates = le16_to_cpu(wusb_dev->wusb_cap_descr->wPHYRates);
+ phy_rate = fls(phy_rates) - 1;
+ if (phy_rate > whc->wusbhc.phy_rate)
+ phy_rate = whc->wusbhc.phy_rate;
+ }
+
qset->qh.info1 = cpu_to_le32(
QH_INFO1_EP(usb_pipeendpoint(urb->pipe))
| (is_out ? QH_INFO1_DIR_OUT : QH_INFO1_DIR_IN)
| usb_pipe_to_qh_type(urb->pipe)
| QH_INFO1_DEV_INFO_IDX(wusb_port_no_to_idx(usb_dev->portnum))
- | QH_INFO1_MAX_PKT_LEN(usb_maxpacket(urb->dev, urb->pipe, is_out))
+ | QH_INFO1_MAX_PKT_LEN(qset->max_packet)
);
qset->qh.info2 = cpu_to_le32(
QH_INFO2_BURST(qset->max_burst)
@@ -86,7 +105,7 @@ static void qset_fill_qh(struct whc_qset *qset, struct urb *urb)
* strength and can presumably guess the Tx power required
* from that? */
qset->qh.info3 = cpu_to_le32(
- QH_INFO3_TX_RATE_53_3
+ QH_INFO3_TX_RATE(phy_rate)
| QH_INFO3_TX_PWR(0) /* 0 == max power */
);
@@ -148,7 +167,7 @@ struct whc_qset *get_qset(struct whc *whc, struct urb *urb,
qset->ep = urb->ep;
urb->ep->hcpriv = qset;
- qset_fill_qh(qset, urb);
+ qset_fill_qh(whc, qset, urb);
}
return qset;
}
@@ -241,6 +260,36 @@ static void qset_remove_qtd(struct whc *whc, struct whc_qset *qset)
qset->ntds--;
}
+static void qset_copy_bounce_to_sg(struct whc *whc, struct whc_std *std)
+{
+ struct scatterlist *sg;
+ void *bounce;
+ size_t remaining, offset;
+
+ bounce = std->bounce_buf;
+ remaining = std->len;
+
+ sg = std->bounce_sg;
+ offset = std->bounce_offset;
+
+ while (remaining) {
+ size_t len;
+
+ len = min(sg->length - offset, remaining);
+ memcpy(sg_virt(sg) + offset, bounce, len);
+
+ bounce += len;
+ remaining -= len;
+
+ offset += len;
+ if (offset >= sg->length) {
+ sg = sg_next(sg);
+ offset = 0;
+ }
+ }
+
+}
+
/**
* qset_free_std - remove an sTD and free it.
* @whc: the WHCI host controller
@@ -249,13 +298,29 @@ static void qset_remove_qtd(struct whc *whc, struct whc_qset *qset)
void qset_free_std(struct whc *whc, struct whc_std *std)
{
list_del(&std->list_node);
- if (std->num_pointers) {
- dma_unmap_single(whc->wusbhc.dev, std->dma_addr,
- std->num_pointers * sizeof(struct whc_page_list_entry),
- DMA_TO_DEVICE);
+ if (std->bounce_buf) {
+ bool is_out = usb_pipeout(std->urb->pipe);
+ dma_addr_t dma_addr;
+
+ if (std->num_pointers)
+ dma_addr = le64_to_cpu(std->pl_virt[0].buf_ptr);
+ else
+ dma_addr = std->dma_addr;
+
+ dma_unmap_single(whc->wusbhc.dev, dma_addr,
+ std->len, is_out ? DMA_TO_DEVICE : DMA_FROM_DEVICE);
+ if (!is_out)
+ qset_copy_bounce_to_sg(whc, std);
+ kfree(std->bounce_buf);
+ }
+ if (std->pl_virt) {
+ if (std->dma_addr)
+ dma_unmap_single(whc->wusbhc.dev, std->dma_addr,
+ std->num_pointers * sizeof(struct whc_page_list_entry),
+ DMA_TO_DEVICE);
kfree(std->pl_virt);
+ std->pl_virt = NULL;
}
-
kfree(std);
}
@@ -293,12 +358,17 @@ static int qset_fill_page_list(struct whc *whc, struct whc_std *std, gfp_t mem_f
{
dma_addr_t dma_addr = std->dma_addr;
dma_addr_t sp, ep;
- size_t std_len = std->len;
size_t pl_len;
int p;
- sp = ALIGN(dma_addr, WHCI_PAGE_SIZE);
- ep = dma_addr + std_len;
+ /* Short buffers don't need a page list. */
+ if (std->len <= WHCI_PAGE_SIZE) {
+ std->num_pointers = 0;
+ return 0;
+ }
+
+ sp = dma_addr & ~(WHCI_PAGE_SIZE-1);
+ ep = dma_addr + std->len;
std->num_pointers = DIV_ROUND_UP(ep - sp, WHCI_PAGE_SIZE);
pl_len = std->num_pointers * sizeof(struct whc_page_list_entry);
@@ -309,7 +379,7 @@ static int qset_fill_page_list(struct whc *whc, struct whc_std *std, gfp_t mem_f
for (p = 0; p < std->num_pointers; p++) {
std->pl_virt[p].buf_ptr = cpu_to_le64(dma_addr);
- dma_addr = ALIGN(dma_addr + WHCI_PAGE_SIZE, WHCI_PAGE_SIZE);
+ dma_addr = (dma_addr + WHCI_PAGE_SIZE) & ~(WHCI_PAGE_SIZE-1);
}
return 0;
@@ -339,6 +409,218 @@ static void urb_dequeue_work(struct work_struct *work)
spin_unlock_irqrestore(&whc->lock, flags);
}
+static struct whc_std *qset_new_std(struct whc *whc, struct whc_qset *qset,
+ struct urb *urb, gfp_t mem_flags)
+{
+ struct whc_std *std;
+
+ std = kzalloc(sizeof(struct whc_std), mem_flags);
+ if (std == NULL)
+ return NULL;
+
+ std->urb = urb;
+ std->qtd = NULL;
+
+ INIT_LIST_HEAD(&std->list_node);
+ list_add_tail(&std->list_node, &qset->stds);
+
+ return std;
+}
+
+static int qset_add_urb_sg(struct whc *whc, struct whc_qset *qset, struct urb *urb,
+ gfp_t mem_flags)
+{
+ size_t remaining;
+ struct scatterlist *sg;
+ int i;
+ int ntds = 0;
+ struct whc_std *std = NULL;
+ struct whc_page_list_entry *entry;
+ dma_addr_t prev_end = 0;
+ size_t pl_len;
+ int p = 0;
+
+ remaining = urb->transfer_buffer_length;
+
+ for_each_sg(urb->sg->sg, sg, urb->num_sgs, i) {
+ dma_addr_t dma_addr;
+ size_t dma_remaining;
+ dma_addr_t sp, ep;
+ int num_pointers;
+
+ if (remaining == 0) {
+ break;
+ }
+
+ dma_addr = sg_dma_address(sg);
+ dma_remaining = min_t(size_t, sg_dma_len(sg), remaining);
+
+ while (dma_remaining) {
+ size_t dma_len;
+
+ /*
+ * We can use the previous std (if it exists) provided that:
+ * - the previous one ended on a page boundary.
+ * - the current one begins on a page boundary.
+ * - the previous one isn't full.
+ *
+ * If a new std is needed but the previous one
+ * was not a whole number of packets then this
+ * sg list cannot be mapped onto multiple
+ * qTDs. Return an error and let the caller
+ * sort it out.
+ */
+ if (!std
+ || (prev_end & (WHCI_PAGE_SIZE-1))
+ || (dma_addr & (WHCI_PAGE_SIZE-1))
+ || std->len + WHCI_PAGE_SIZE > QTD_MAX_XFER_SIZE) {
+ if (std->len % qset->max_packet != 0)
+ return -EINVAL;
+ std = qset_new_std(whc, qset, urb, mem_flags);
+ if (std == NULL) {
+ return -ENOMEM;
+ }
+ ntds++;
+ p = 0;
+ }
+
+ dma_len = dma_remaining;
+
+ /*
+ * If the remainder of this element doesn't
+ * fit in a single qTD, limit the qTD to a
+ * whole number of packets. This allows the
+ * remainder to go into the next qTD.
+ */
+ if (std->len + dma_len > QTD_MAX_XFER_SIZE) {
+ dma_len = (QTD_MAX_XFER_SIZE / qset->max_packet)
+ * qset->max_packet - std->len;
+ }
+
+ std->len += dma_len;
+ std->ntds_remaining = -1; /* filled in later */
+
+ sp = dma_addr & ~(WHCI_PAGE_SIZE-1);
+ ep = dma_addr + dma_len;
+ num_pointers = DIV_ROUND_UP(ep - sp, WHCI_PAGE_SIZE);
+ std->num_pointers += num_pointers;
+
+ pl_len = std->num_pointers * sizeof(struct whc_page_list_entry);
+
+ std->pl_virt = krealloc(std->pl_virt, pl_len, mem_flags);
+ if (std->pl_virt == NULL) {
+ return -ENOMEM;
+ }
+
+ for (;p < std->num_pointers; p++, entry++) {
+ std->pl_virt[p].buf_ptr = cpu_to_le64(dma_addr);
+ dma_addr = (dma_addr + WHCI_PAGE_SIZE) & ~(WHCI_PAGE_SIZE-1);
+ }
+
+ prev_end = dma_addr = ep;
+ dma_remaining -= dma_len;
+ remaining -= dma_len;
+ }
+ }
+
+ /* Now the number of stds is know, go back and fill in
+ std->ntds_remaining. */
+ list_for_each_entry(std, &qset->stds, list_node) {
+ if (std->ntds_remaining == -1) {
+ pl_len = std->num_pointers * sizeof(struct whc_page_list_entry);
+ std->ntds_remaining = ntds--;
+ std->dma_addr = dma_map_single(whc->wusbhc.dev, std->pl_virt,
+ pl_len, DMA_TO_DEVICE);
+ }
+ }
+ return 0;
+}
+
+/**
+ * qset_add_urb_sg_linearize - add an urb with sg list, copying the data
+ *
+ * If the URB contains an sg list whose elements cannot be directly
+ * mapped to qTDs then the data must be transferred via bounce
+ * buffers.
+ */
+static int qset_add_urb_sg_linearize(struct whc *whc, struct whc_qset *qset,
+ struct urb *urb, gfp_t mem_flags)
+{
+ bool is_out = usb_pipeout(urb->pipe);
+ size_t max_std_len;
+ size_t remaining;
+ int ntds = 0;
+ struct whc_std *std = NULL;
+ void *bounce = NULL;
+ struct scatterlist *sg;
+ int i;
+
+ /* limit maximum bounce buffer to 16 * 3.5 KiB ~= 28 k */
+ max_std_len = qset->max_burst * qset->max_packet;
+
+ remaining = urb->transfer_buffer_length;
+
+ for_each_sg(urb->sg->sg, sg, urb->sg->nents, i) {
+ size_t len;
+ size_t sg_remaining;
+ void *orig;
+
+ if (remaining == 0) {
+ break;
+ }
+
+ sg_remaining = min_t(size_t, remaining, sg->length);
+ orig = sg_virt(sg);
+
+ while (sg_remaining) {
+ if (!std || std->len == max_std_len) {
+ std = qset_new_std(whc, qset, urb, mem_flags);
+ if (std == NULL)
+ return -ENOMEM;
+ std->bounce_buf = kmalloc(max_std_len, mem_flags);
+ if (std->bounce_buf == NULL)
+ return -ENOMEM;
+ std->bounce_sg = sg;
+ std->bounce_offset = orig - sg_virt(sg);
+ bounce = std->bounce_buf;
+ ntds++;
+ }
+
+ len = min(sg_remaining, max_std_len - std->len);
+
+ if (is_out)
+ memcpy(bounce, orig, len);
+
+ std->len += len;
+ std->ntds_remaining = -1; /* filled in later */
+
+ bounce += len;
+ orig += len;
+ sg_remaining -= len;
+ remaining -= len;
+ }
+ }
+
+ /*
+ * For each of the new sTDs, map the bounce buffers, create
+ * page lists (if necessary), and fill in std->ntds_remaining.
+ */
+ list_for_each_entry(std, &qset->stds, list_node) {
+ if (std->ntds_remaining != -1)
+ continue;
+
+ std->dma_addr = dma_map_single(&whc->umc->dev, std->bounce_buf, std->len,
+ is_out ? DMA_TO_DEVICE : DMA_FROM_DEVICE);
+
+ if (qset_fill_page_list(whc, std, mem_flags) < 0)
+ return -ENOMEM;
+
+ std->ntds_remaining = ntds--;
+ }
+
+ return 0;
+}
+
/**
* qset_add_urb - add an urb to the qset's queue.
*
@@ -353,10 +635,7 @@ int qset_add_urb(struct whc *whc, struct whc_qset *qset, struct urb *urb,
int remaining = urb->transfer_buffer_length;
u64 transfer_dma = urb->transfer_dma;
int ntds_remaining;
-
- ntds_remaining = DIV_ROUND_UP(remaining, QTD_MAX_XFER_SIZE);
- if (ntds_remaining == 0)
- ntds_remaining = 1;
+ int ret;
wurb = kzalloc(sizeof(struct whc_urb), mem_flags);
if (wurb == NULL)
@@ -366,32 +645,39 @@ int qset_add_urb(struct whc *whc, struct whc_qset *qset, struct urb *urb,
wurb->urb = urb;
INIT_WORK(&wurb->dequeue_work, urb_dequeue_work);
+ if (urb->sg) {
+ ret = qset_add_urb_sg(whc, qset, urb, mem_flags);
+ if (ret == -EINVAL) {
+ qset_free_stds(qset, urb);
+ ret = qset_add_urb_sg_linearize(whc, qset, urb, mem_flags);
+ }
+ if (ret < 0)
+ goto err_no_mem;
+ return 0;
+ }
+
+ ntds_remaining = DIV_ROUND_UP(remaining, QTD_MAX_XFER_SIZE);
+ if (ntds_remaining == 0)
+ ntds_remaining = 1;
+
while (ntds_remaining) {
struct whc_std *std;
size_t std_len;
- std = kmalloc(sizeof(struct whc_std), mem_flags);
- if (std == NULL)
- goto err_no_mem;
-
std_len = remaining;
if (std_len > QTD_MAX_XFER_SIZE)
std_len = QTD_MAX_XFER_SIZE;
- std->urb = urb;
+ std = qset_new_std(whc, qset, urb, mem_flags);
+ if (std == NULL)
+ goto err_no_mem;
+
std->dma_addr = transfer_dma;
std->len = std_len;
std->ntds_remaining = ntds_remaining;
- std->qtd = NULL;
- INIT_LIST_HEAD(&std->list_node);
- list_add_tail(&std->list_node, &qset->stds);
-
- if (std_len > WHCI_PAGE_SIZE) {
- if (qset_fill_page_list(whc, std, mem_flags) < 0)
- goto err_no_mem;
- } else
- std->num_pointers = 0;
+ if (qset_fill_page_list(whc, std, mem_flags) < 0)
+ goto err_no_mem;
ntds_remaining--;
remaining -= std_len;
diff --git a/drivers/usb/host/whci/whcd.h b/drivers/usb/host/whci/whcd.h
index 24e94d9..c80c7d9 100644
--- a/drivers/usb/host/whci/whcd.h
+++ b/drivers/usb/host/whci/whcd.h
@@ -84,6 +84,11 @@ struct whc {
* @len: the length of data in the associated TD.
* @ntds_remaining: number of TDs (starting from this one) in this transfer.
*
+ * @bounce_buf: a bounce buffer if the std was from an urb with a sg
+ * list that could not be mapped to qTDs directly.
+ * @bounce_sg: the first scatterlist element bounce_buf is for.
+ * @bounce_offset: the offset into bounce_sg for the start of bounce_buf.
+ *
* Queued URBs may require more TDs than are available in a qset so we
* use a list of these "software TDs" (sTDs) to hold per-TD data.
*/
@@ -97,6 +102,10 @@ struct whc_std {
int num_pointers;
dma_addr_t dma_addr;
struct whc_page_list_entry *pl_virt;
+
+ void *bounce_buf;
+ struct scatterlist *bounce_sg;
+ unsigned bounce_offset;
};
/**
diff --git a/drivers/usb/host/whci/whci-hc.h b/drivers/usb/host/whci/whci-hc.h
index e8d0001..4d4cbc0 100644
--- a/drivers/usb/host/whci/whci-hc.h
+++ b/drivers/usb/host/whci/whci-hc.h
@@ -172,14 +172,7 @@ struct whc_qhead {
#define QH_INFO3_MAX_DELAY(d) ((d) << 0) /* maximum stream delay in 125 us units (isoc only) */
#define QH_INFO3_INTERVAL(i) ((i) << 16) /* segment interval in 125 us units (isoc only) */
-#define QH_INFO3_TX_RATE_53_3 (0 << 24)
-#define QH_INFO3_TX_RATE_80 (1 << 24)
-#define QH_INFO3_TX_RATE_106_7 (2 << 24)
-#define QH_INFO3_TX_RATE_160 (3 << 24)
-#define QH_INFO3_TX_RATE_200 (4 << 24)
-#define QH_INFO3_TX_RATE_320 (5 << 24)
-#define QH_INFO3_TX_RATE_400 (6 << 24)
-#define QH_INFO3_TX_RATE_480 (7 << 24)
+#define QH_INFO3_TX_RATE(r) ((r) << 24) /* PHY rate (see [ECMA-368] section 10.3.1.1) */
#define QH_INFO3_TX_PWR(p) ((p) << 29) /* transmit power (see [WUSB] section 5.2.1.2) */
#define QH_STATUS_FLOW_CTRL (1 << 15)
@@ -267,8 +260,9 @@ struct whc_qset {
unsigned reset:1;
struct urb *pause_after_urb;
struct completion remove_complete;
- int max_burst;
- int max_seq;
+ uint16_t max_packet;
+ uint8_t max_burst;
+ uint8_t max_seq;
};
static inline void whc_qset_set_link_ptr(u64 *ptr, u64 target)
diff --git a/drivers/usb/host/xhci-hcd.c b/drivers/usb/host/xhci-hcd.c
index 932f999..5e92c72 100644
--- a/drivers/usb/host/xhci-hcd.c
+++ b/drivers/usb/host/xhci-hcd.c
@@ -67,22 +67,14 @@ static int handshake(struct xhci_hcd *xhci, void __iomem *ptr,
}
/*
- * Force HC into halt state.
- *
- * Disable any IRQs and clear the run/stop bit.
- * HC will complete any current and actively pipelined transactions, and
- * should halt within 16 microframes of the run/stop bit being cleared.
- * Read HC Halted bit in the status register to see when the HC is finished.
- * XXX: shouldn't we set HC_STATE_HALT here somewhere?
+ * Disable interrupts and begin the xHCI halting process.
*/
-int xhci_halt(struct xhci_hcd *xhci)
+void xhci_quiesce(struct xhci_hcd *xhci)
{
u32 halted;
u32 cmd;
u32 mask;
- xhci_dbg(xhci, "// Halt the HC\n");
- /* Disable all interrupts from the host controller */
mask = ~(XHCI_IRQS);
halted = xhci_readl(xhci, &xhci->op_regs->status) & STS_HALT;
if (!halted)
@@ -91,6 +83,21 @@ int xhci_halt(struct xhci_hcd *xhci)
cmd = xhci_readl(xhci, &xhci->op_regs->command);
cmd &= mask;
xhci_writel(xhci, cmd, &xhci->op_regs->command);
+}
+
+/*
+ * Force HC into halt state.
+ *
+ * Disable any IRQs and clear the run/stop bit.
+ * HC will complete any current and actively pipelined transactions, and
+ * should halt within 16 microframes of the run/stop bit being cleared.
+ * Read HC Halted bit in the status register to see when the HC is finished.
+ * XXX: shouldn't we set HC_STATE_HALT here somewhere?
+ */
+int xhci_halt(struct xhci_hcd *xhci)
+{
+ xhci_dbg(xhci, "// Halt the HC\n");
+ xhci_quiesce(xhci);
return handshake(xhci, &xhci->op_regs->status,
STS_HALT, STS_HALT, XHCI_MAX_HALT_USEC);
@@ -124,28 +131,6 @@ int xhci_reset(struct xhci_hcd *xhci)
return handshake(xhci, &xhci->op_regs->command, CMD_RESET, 0, 250 * 1000);
}
-/*
- * Stop the HC from processing the endpoint queues.
- */
-static void xhci_quiesce(struct xhci_hcd *xhci)
-{
- /*
- * Queues are per endpoint, so we need to disable an endpoint or slot.
- *
- * To disable a slot, we need to insert a disable slot command on the
- * command ring and ring the doorbell. This will also free any internal
- * resources associated with the slot (which might not be what we want).
- *
- * A Release Endpoint command sounds better - doesn't free internal HC
- * memory, but removes the endpoints from the schedule and releases the
- * bandwidth, disables the doorbells, and clears the endpoint enable
- * flag. Usually used prior to a set interface command.
- *
- * TODO: Implement after command ring code is done.
- */
- BUG_ON(!HC_IS_RUNNING(xhci_to_hcd(xhci)->state));
- xhci_dbg(xhci, "Finished quiescing -- code not written yet\n");
-}
#if 0
/* Set up MSI-X table for entry 0 (may claim other entries later) */
@@ -261,8 +246,14 @@ static void xhci_work(struct xhci_hcd *xhci)
/* Flush posted writes */
xhci_readl(xhci, &xhci->ir_set->irq_pending);
- /* FIXME this should be a delayed service routine that clears the EHB */
- xhci_handle_event(xhci);
+ if (xhci->xhc_state & XHCI_STATE_DYING)
+ xhci_dbg(xhci, "xHCI dying, ignoring interrupt. "
+ "Shouldn't IRQs be disabled?\n");
+ else
+ /* FIXME this should be a delayed service routine
+ * that clears the EHB.
+ */
+ xhci_handle_event(xhci);
/* Clear the event handler busy flag (RW1C); the event ring should be empty. */
temp_64 = xhci_read_64(xhci, &xhci->ir_set->erst_dequeue);
@@ -335,7 +326,7 @@ void xhci_event_ring_work(unsigned long arg)
spin_lock_irqsave(&xhci->lock, flags);
temp = xhci_readl(xhci, &xhci->op_regs->status);
xhci_dbg(xhci, "op reg status = 0x%x\n", temp);
- if (temp == 0xffffffff) {
+ if (temp == 0xffffffff || (xhci->xhc_state & XHCI_STATE_DYING)) {
xhci_dbg(xhci, "HW died, polling stopped.\n");
spin_unlock_irqrestore(&xhci->lock, flags);
return;
@@ -490,8 +481,6 @@ void xhci_stop(struct usb_hcd *hcd)
struct xhci_hcd *xhci = hcd_to_xhci(hcd);
spin_lock_irq(&xhci->lock);
- if (HC_IS_RUNNING(hcd->state))
- xhci_quiesce(xhci);
xhci_halt(xhci);
xhci_reset(xhci);
spin_unlock_irq(&xhci->lock);
@@ -727,16 +716,22 @@ int xhci_urb_enqueue(struct usb_hcd *hcd, struct urb *urb, gfp_t mem_flags)
* atomic context to this function, which may allocate memory.
*/
spin_lock_irqsave(&xhci->lock, flags);
+ if (xhci->xhc_state & XHCI_STATE_DYING)
+ goto dying;
ret = xhci_queue_ctrl_tx(xhci, GFP_ATOMIC, urb,
slot_id, ep_index);
spin_unlock_irqrestore(&xhci->lock, flags);
} else if (usb_endpoint_xfer_bulk(&urb->ep->desc)) {
spin_lock_irqsave(&xhci->lock, flags);
+ if (xhci->xhc_state & XHCI_STATE_DYING)
+ goto dying;
ret = xhci_queue_bulk_tx(xhci, GFP_ATOMIC, urb,
slot_id, ep_index);
spin_unlock_irqrestore(&xhci->lock, flags);
} else if (usb_endpoint_xfer_int(&urb->ep->desc)) {
spin_lock_irqsave(&xhci->lock, flags);
+ if (xhci->xhc_state & XHCI_STATE_DYING)
+ goto dying;
ret = xhci_queue_intr_tx(xhci, GFP_ATOMIC, urb,
slot_id, ep_index);
spin_unlock_irqrestore(&xhci->lock, flags);
@@ -745,6 +740,12 @@ int xhci_urb_enqueue(struct usb_hcd *hcd, struct urb *urb, gfp_t mem_flags)
}
exit:
return ret;
+dying:
+ xhci_dbg(xhci, "Ep 0x%x: URB %p submitted for "
+ "non-responsive xHCI host.\n",
+ urb->ep->desc.bEndpointAddress, urb);
+ spin_unlock_irqrestore(&xhci->lock, flags);
+ return -ESHUTDOWN;
}
/*
@@ -806,6 +807,17 @@ int xhci_urb_dequeue(struct usb_hcd *hcd, struct urb *urb, int status)
kfree(td);
return ret;
}
+ if (xhci->xhc_state & XHCI_STATE_DYING) {
+ xhci_dbg(xhci, "Ep 0x%x: URB %p to be canceled on "
+ "non-responsive xHCI host.\n",
+ urb->ep->desc.bEndpointAddress, urb);
+ /* Let the stop endpoint command watchdog timer (which set this
+ * state) finish cleaning up the endpoint TD lists. We must
+ * have caught it in the middle of dropping a lock and giving
+ * back an URB.
+ */
+ goto done;
+ }
xhci_dbg(xhci, "Cancel URB %p\n", urb);
xhci_dbg(xhci, "Event ring:\n");
@@ -817,12 +829,16 @@ int xhci_urb_dequeue(struct usb_hcd *hcd, struct urb *urb, int status)
xhci_debug_ring(xhci, ep_ring);
td = (struct xhci_td *) urb->hcpriv;
- ep->cancels_pending++;
list_add_tail(&td->cancelled_td_list, &ep->cancelled_td_list);
/* Queue a stop endpoint command, but only if this is
* the first cancellation to be handled.
*/
- if (ep->cancels_pending == 1) {
+ if (!(ep->ep_state & EP_HALT_PENDING)) {
+ ep->ep_state |= EP_HALT_PENDING;
+ ep->stop_cmds_pending++;
+ ep->stop_cmd_timer.expires = jiffies +
+ XHCI_STOP_EP_CMD_TIMEOUT * HZ;
+ add_timer(&ep->stop_cmd_timer);
xhci_queue_stop_endpoint(xhci, urb->dev->slot_id, ep_index);
xhci_ring_cmd_db(xhci);
}
@@ -1246,13 +1262,35 @@ int xhci_check_bandwidth(struct usb_hcd *hcd, struct usb_device *udev)
LAST_CTX_TO_EP_NUM(slot_ctx->dev_info));
xhci_zero_in_ctx(xhci, virt_dev);
- /* Free any old rings */
+ /* Install new rings and free or cache any old rings */
for (i = 1; i < 31; ++i) {
- if (virt_dev->eps[i].new_ring) {
- xhci_ring_free(xhci, virt_dev->eps[i].ring);
- virt_dev->eps[i].ring = virt_dev->eps[i].new_ring;
- virt_dev->eps[i].new_ring = NULL;
+ int rings_cached;
+
+ if (!virt_dev->eps[i].new_ring)
+ continue;
+ /* Only cache or free the old ring if it exists.
+ * It may not if this is the first add of an endpoint.
+ */
+ if (virt_dev->eps[i].ring) {
+ rings_cached = virt_dev->num_rings_cached;
+ if (rings_cached < XHCI_MAX_RINGS_CACHED) {
+ virt_dev->num_rings_cached++;
+ rings_cached = virt_dev->num_rings_cached;
+ virt_dev->ring_cache[rings_cached] =
+ virt_dev->eps[i].ring;
+ xhci_dbg(xhci, "Cached old ring, "
+ "%d ring%s cached\n",
+ rings_cached,
+ (rings_cached > 1) ? "s" : "");
+ } else {
+ xhci_ring_free(xhci, virt_dev->eps[i].ring);
+ xhci_dbg(xhci, "Ring cache full (%d rings), "
+ "freeing ring\n",
+ virt_dev->num_rings_cached);
+ }
}
+ virt_dev->eps[i].ring = virt_dev->eps[i].new_ring;
+ virt_dev->eps[i].new_ring = NULL;
}
return ret;
@@ -1427,16 +1465,27 @@ void xhci_endpoint_reset(struct usb_hcd *hcd,
void xhci_free_dev(struct usb_hcd *hcd, struct usb_device *udev)
{
struct xhci_hcd *xhci = hcd_to_xhci(hcd);
+ struct xhci_virt_device *virt_dev;
unsigned long flags;
u32 state;
+ int i;
if (udev->slot_id == 0)
return;
+ virt_dev = xhci->devs[udev->slot_id];
+ if (!virt_dev)
+ return;
+
+ /* Stop any wayward timer functions (which may grab the lock) */
+ for (i = 0; i < 31; ++i) {
+ virt_dev->eps[i].ep_state &= ~EP_HALT_PENDING;
+ del_timer_sync(&virt_dev->eps[i].stop_cmd_timer);
+ }
spin_lock_irqsave(&xhci->lock, flags);
/* Don't disable the slot if the host controller is dead. */
state = xhci_readl(xhci, &xhci->op_regs->status);
- if (state == 0xffffffff) {
+ if (state == 0xffffffff || (xhci->xhc_state & XHCI_STATE_DYING)) {
xhci_free_virt_device(xhci, udev->slot_id);
spin_unlock_irqrestore(&xhci->lock, flags);
return;
diff --git a/drivers/usb/host/xhci-mem.c b/drivers/usb/host/xhci-mem.c
index b8fd270..bffcef7 100644
--- a/drivers/usb/host/xhci-mem.c
+++ b/drivers/usb/host/xhci-mem.c
@@ -125,6 +125,23 @@ void xhci_ring_free(struct xhci_hcd *xhci, struct xhci_ring *ring)
kfree(ring);
}
+static void xhci_initialize_ring_info(struct xhci_ring *ring)
+{
+ /* The ring is empty, so the enqueue pointer == dequeue pointer */
+ ring->enqueue = ring->first_seg->trbs;
+ ring->enq_seg = ring->first_seg;
+ ring->dequeue = ring->enqueue;
+ ring->deq_seg = ring->first_seg;
+ /* The ring is initialized to 0. The producer must write 1 to the cycle
+ * bit to handover ownership of the TRB, so PCS = 1. The consumer must
+ * compare CCS to the cycle bit to check ownership, so CCS = 1.
+ */
+ ring->cycle_state = 1;
+ /* Not necessary for new rings, but needed for re-initialized rings */
+ ring->enq_updates = 0;
+ ring->deq_updates = 0;
+}
+
/**
* Create a new ring with zero or more segments.
*
@@ -173,17 +190,7 @@ static struct xhci_ring *xhci_ring_alloc(struct xhci_hcd *xhci,
" segment %p (virtual), 0x%llx (DMA)\n",
prev, (unsigned long long)prev->dma);
}
- /* The ring is empty, so the enqueue pointer == dequeue pointer */
- ring->enqueue = ring->first_seg->trbs;
- ring->enq_seg = ring->first_seg;
- ring->dequeue = ring->enqueue;
- ring->deq_seg = ring->first_seg;
- /* The ring is initialized to 0. The producer must write 1 to the cycle
- * bit to handover ownership of the TRB, so PCS = 1. The consumer must
- * compare CCS to the cycle bit to check ownership, so CCS = 1.
- */
- ring->cycle_state = 1;
-
+ xhci_initialize_ring_info(ring);
return ring;
fail:
@@ -191,6 +198,27 @@ fail:
return 0;
}
+/* Zero an endpoint ring (except for link TRBs) and move the enqueue and dequeue
+ * pointers to the beginning of the ring.
+ */
+static void xhci_reinit_cached_ring(struct xhci_hcd *xhci,
+ struct xhci_ring *ring)
+{
+ struct xhci_segment *seg = ring->first_seg;
+ do {
+ memset(seg->trbs, 0,
+ sizeof(union xhci_trb)*TRBS_PER_SEGMENT);
+ /* All endpoint rings have link TRBs */
+ xhci_link_segments(xhci, seg, seg->next, 1);
+ seg = seg->next;
+ } while (seg != ring->first_seg);
+ xhci_initialize_ring_info(ring);
+ /* td list should be empty since all URBs have been cancelled,
+ * but just in case...
+ */
+ INIT_LIST_HEAD(&ring->td_list);
+}
+
#define CTX_SIZE(_hcc) (HCC_64BYTE_CONTEXT(_hcc) ? 64 : 32)
struct xhci_container_ctx *xhci_alloc_container_ctx(struct xhci_hcd *xhci,
@@ -248,6 +276,15 @@ struct xhci_ep_ctx *xhci_get_ep_ctx(struct xhci_hcd *xhci,
(ctx->bytes + (ep_index * CTX_SIZE(xhci->hcc_params)));
}
+static void xhci_init_endpoint_timer(struct xhci_hcd *xhci,
+ struct xhci_virt_ep *ep)
+{
+ init_timer(&ep->stop_cmd_timer);
+ ep->stop_cmd_timer.data = (unsigned long) ep;
+ ep->stop_cmd_timer.function = xhci_stop_endpoint_command_watchdog;
+ ep->xhci = xhci;
+}
+
/* All the xhci_tds in the ring's TD list should be freed at this point */
void xhci_free_virt_device(struct xhci_hcd *xhci, int slot_id)
{
@@ -267,6 +304,12 @@ void xhci_free_virt_device(struct xhci_hcd *xhci, int slot_id)
if (dev->eps[i].ring)
xhci_ring_free(xhci, dev->eps[i].ring);
+ if (dev->ring_cache) {
+ for (i = 0; i < dev->num_rings_cached; i++)
+ xhci_ring_free(xhci, dev->ring_cache[i]);
+ kfree(dev->ring_cache);
+ }
+
if (dev->in_ctx)
xhci_free_container_ctx(xhci, dev->in_ctx);
if (dev->out_ctx)
@@ -309,15 +352,25 @@ int xhci_alloc_virt_device(struct xhci_hcd *xhci, int slot_id,
xhci_dbg(xhci, "Slot %d input ctx = 0x%llx (dma)\n", slot_id,
(unsigned long long)dev->in_ctx->dma);
- /* Initialize the cancellation list for each endpoint */
- for (i = 0; i < 31; i++)
+ /* Initialize the cancellation list and watchdog timers for each ep */
+ for (i = 0; i < 31; i++) {
+ xhci_init_endpoint_timer(xhci, &dev->eps[i]);
INIT_LIST_HEAD(&dev->eps[i].cancelled_td_list);
+ }
/* Allocate endpoint 0 ring */
dev->eps[0].ring = xhci_ring_alloc(xhci, 1, true, flags);
if (!dev->eps[0].ring)
goto fail;
+ /* Allocate pointers to the ring cache */
+ dev->ring_cache = kzalloc(
+ sizeof(struct xhci_ring *)*XHCI_MAX_RINGS_CACHED,
+ flags);
+ if (!dev->ring_cache)
+ goto fail;
+ dev->num_rings_cached = 0;
+
init_completion(&dev->cmd_completion);
INIT_LIST_HEAD(&dev->cmd_list);
@@ -544,8 +597,16 @@ int xhci_endpoint_init(struct xhci_hcd *xhci,
/* Set up the endpoint ring */
virt_dev->eps[ep_index].new_ring =
xhci_ring_alloc(xhci, 1, true, mem_flags);
- if (!virt_dev->eps[ep_index].new_ring)
- return -ENOMEM;
+ if (!virt_dev->eps[ep_index].new_ring) {
+ /* Attempt to use the ring cache */
+ if (virt_dev->num_rings_cached == 0)
+ return -ENOMEM;
+ virt_dev->eps[ep_index].new_ring =
+ virt_dev->ring_cache[virt_dev->num_rings_cached];
+ virt_dev->ring_cache[virt_dev->num_rings_cached] = NULL;
+ virt_dev->num_rings_cached--;
+ xhci_reinit_cached_ring(xhci, virt_dev->eps[ep_index].new_ring);
+ }
ep_ring = virt_dev->eps[ep_index].new_ring;
ep_ctx->deq = ep_ring->first_seg->dma | ep_ring->cycle_state;
@@ -768,14 +829,17 @@ struct xhci_command *xhci_alloc_command(struct xhci_hcd *xhci,
command->in_ctx =
xhci_alloc_container_ctx(xhci, XHCI_CTX_TYPE_INPUT, mem_flags);
- if (!command->in_ctx)
+ if (!command->in_ctx) {
+ kfree(command);
return NULL;
+ }
if (allocate_completion) {
command->completion =
kzalloc(sizeof(struct completion), mem_flags);
if (!command->completion) {
xhci_free_container_ctx(xhci, command->in_ctx);
+ kfree(command);
return NULL;
}
init_completion(command->completion);
@@ -848,6 +912,163 @@ void xhci_mem_cleanup(struct xhci_hcd *xhci)
xhci->page_shift = 0;
}
+static int xhci_test_trb_in_td(struct xhci_hcd *xhci,
+ struct xhci_segment *input_seg,
+ union xhci_trb *start_trb,
+ union xhci_trb *end_trb,
+ dma_addr_t input_dma,
+ struct xhci_segment *result_seg,
+ char *test_name, int test_number)
+{
+ unsigned long long start_dma;
+ unsigned long long end_dma;
+ struct xhci_segment *seg;
+
+ start_dma = xhci_trb_virt_to_dma(input_seg, start_trb);
+ end_dma = xhci_trb_virt_to_dma(input_seg, end_trb);
+
+ seg = trb_in_td(input_seg, start_trb, end_trb, input_dma);
+ if (seg != result_seg) {
+ xhci_warn(xhci, "WARN: %s TRB math test %d failed!\n",
+ test_name, test_number);
+ xhci_warn(xhci, "Tested TRB math w/ seg %p and "
+ "input DMA 0x%llx\n",
+ input_seg,
+ (unsigned long long) input_dma);
+ xhci_warn(xhci, "starting TRB %p (0x%llx DMA), "
+ "ending TRB %p (0x%llx DMA)\n",
+ start_trb, start_dma,
+ end_trb, end_dma);
+ xhci_warn(xhci, "Expected seg %p, got seg %p\n",
+ result_seg, seg);
+ return -1;
+ }
+ return 0;
+}
+
+/* TRB math checks for xhci_trb_in_td(), using the command and event rings. */
+static int xhci_check_trb_in_td_math(struct xhci_hcd *xhci, gfp_t mem_flags)
+{
+ struct {
+ dma_addr_t input_dma;
+ struct xhci_segment *result_seg;
+ } simple_test_vector [] = {
+ /* A zeroed DMA field should fail */
+ { 0, NULL },
+ /* One TRB before the ring start should fail */
+ { xhci->event_ring->first_seg->dma - 16, NULL },
+ /* One byte before the ring start should fail */
+ { xhci->event_ring->first_seg->dma - 1, NULL },
+ /* Starting TRB should succeed */
+ { xhci->event_ring->first_seg->dma, xhci->event_ring->first_seg },
+ /* Ending TRB should succeed */
+ { xhci->event_ring->first_seg->dma + (TRBS_PER_SEGMENT - 1)*16,
+ xhci->event_ring->first_seg },
+ /* One byte after the ring end should fail */
+ { xhci->event_ring->first_seg->dma + (TRBS_PER_SEGMENT - 1)*16 + 1, NULL },
+ /* One TRB after the ring end should fail */
+ { xhci->event_ring->first_seg->dma + (TRBS_PER_SEGMENT)*16, NULL },
+ /* An address of all ones should fail */
+ { (dma_addr_t) (~0), NULL },
+ };
+ struct {
+ struct xhci_segment *input_seg;
+ union xhci_trb *start_trb;
+ union xhci_trb *end_trb;
+ dma_addr_t input_dma;
+ struct xhci_segment *result_seg;
+ } complex_test_vector [] = {
+ /* Test feeding a valid DMA address from a different ring */
+ { .input_seg = xhci->event_ring->first_seg,
+ .start_trb = xhci->event_ring->first_seg->trbs,
+ .end_trb = &xhci->event_ring->first_seg->trbs[TRBS_PER_SEGMENT - 1],
+ .input_dma = xhci->cmd_ring->first_seg->dma,
+ .result_seg = NULL,
+ },
+ /* Test feeding a valid end TRB from a different ring */
+ { .input_seg = xhci->event_ring->first_seg,
+ .start_trb = xhci->event_ring->first_seg->trbs,
+ .end_trb = &xhci->cmd_ring->first_seg->trbs[TRBS_PER_SEGMENT - 1],
+ .input_dma = xhci->cmd_ring->first_seg->dma,
+ .result_seg = NULL,
+ },
+ /* Test feeding a valid start and end TRB from a different ring */
+ { .input_seg = xhci->event_ring->first_seg,
+ .start_trb = xhci->cmd_ring->first_seg->trbs,
+ .end_trb = &xhci->cmd_ring->first_seg->trbs[TRBS_PER_SEGMENT - 1],
+ .input_dma = xhci->cmd_ring->first_seg->dma,
+ .result_seg = NULL,
+ },
+ /* TRB in this ring, but after this TD */
+ { .input_seg = xhci->event_ring->first_seg,
+ .start_trb = &xhci->event_ring->first_seg->trbs[0],
+ .end_trb = &xhci->event_ring->first_seg->trbs[3],
+ .input_dma = xhci->event_ring->first_seg->dma + 4*16,
+ .result_seg = NULL,
+ },
+ /* TRB in this ring, but before this TD */
+ { .input_seg = xhci->event_ring->first_seg,
+ .start_trb = &xhci->event_ring->first_seg->trbs[3],
+ .end_trb = &xhci->event_ring->first_seg->trbs[6],
+ .input_dma = xhci->event_ring->first_seg->dma + 2*16,
+ .result_seg = NULL,
+ },
+ /* TRB in this ring, but after this wrapped TD */
+ { .input_seg = xhci->event_ring->first_seg,
+ .start_trb = &xhci->event_ring->first_seg->trbs[TRBS_PER_SEGMENT - 3],
+ .end_trb = &xhci->event_ring->first_seg->trbs[1],
+ .input_dma = xhci->event_ring->first_seg->dma + 2*16,
+ .result_seg = NULL,
+ },
+ /* TRB in this ring, but before this wrapped TD */
+ { .input_seg = xhci->event_ring->first_seg,
+ .start_trb = &xhci->event_ring->first_seg->trbs[TRBS_PER_SEGMENT - 3],
+ .end_trb = &xhci->event_ring->first_seg->trbs[1],
+ .input_dma = xhci->event_ring->first_seg->dma + (TRBS_PER_SEGMENT - 4)*16,
+ .result_seg = NULL,
+ },
+ /* TRB not in this ring, and we have a wrapped TD */
+ { .input_seg = xhci->event_ring->first_seg,
+ .start_trb = &xhci->event_ring->first_seg->trbs[TRBS_PER_SEGMENT - 3],
+ .end_trb = &xhci->event_ring->first_seg->trbs[1],
+ .input_dma = xhci->cmd_ring->first_seg->dma + 2*16,
+ .result_seg = NULL,
+ },
+ };
+
+ unsigned int num_tests;
+ int i, ret;
+
+ num_tests = sizeof(simple_test_vector) / sizeof(simple_test_vector[0]);
+ for (i = 0; i < num_tests; i++) {
+ ret = xhci_test_trb_in_td(xhci,
+ xhci->event_ring->first_seg,
+ xhci->event_ring->first_seg->trbs,
+ &xhci->event_ring->first_seg->trbs[TRBS_PER_SEGMENT - 1],
+ simple_test_vector[i].input_dma,
+ simple_test_vector[i].result_seg,
+ "Simple", i);
+ if (ret < 0)
+ return ret;
+ }
+
+ num_tests = sizeof(complex_test_vector) / sizeof(complex_test_vector[0]);
+ for (i = 0; i < num_tests; i++) {
+ ret = xhci_test_trb_in_td(xhci,
+ complex_test_vector[i].input_seg,
+ complex_test_vector[i].start_trb,
+ complex_test_vector[i].end_trb,
+ complex_test_vector[i].input_dma,
+ complex_test_vector[i].result_seg,
+ "Complex", i);
+ if (ret < 0)
+ return ret;
+ }
+ xhci_dbg(xhci, "TRB math tests passed.\n");
+ return 0;
+}
+
+
int xhci_mem_init(struct xhci_hcd *xhci, gfp_t flags)
{
dma_addr_t dma;
@@ -951,6 +1172,8 @@ int xhci_mem_init(struct xhci_hcd *xhci, gfp_t flags)
xhci->event_ring = xhci_ring_alloc(xhci, ERST_NUM_SEGS, false, flags);
if (!xhci->event_ring)
goto fail;
+ if (xhci_check_trb_in_td_math(xhci, flags) < 0)
+ goto fail;
xhci->erst.entries = pci_alloc_consistent(to_pci_dev(dev),
sizeof(struct xhci_erst_entry)*ERST_NUM_SEGS, &dma);
diff --git a/drivers/usb/host/xhci-pci.c b/drivers/usb/host/xhci-pci.c
index 06595ec..e097008 100644
--- a/drivers/usb/host/xhci-pci.c
+++ b/drivers/usb/host/xhci-pci.c
@@ -54,6 +54,8 @@ static int xhci_pci_setup(struct usb_hcd *hcd)
struct pci_dev *pdev = to_pci_dev(hcd->self.controller);
int retval;
+ hcd->self.sg_tablesize = TRBS_PER_SEGMENT - 1;
+
xhci->cap_regs = hcd->regs;
xhci->op_regs = hcd->regs +
HC_LENGTH(xhci_readl(xhci, &xhci->cap_regs->hc_capbase));
diff --git a/drivers/usb/host/xhci-ring.c b/drivers/usb/host/xhci-ring.c
index 821b7b4..ee7bc7e 100644
--- a/drivers/usb/host/xhci-ring.c
+++ b/drivers/usb/host/xhci-ring.c
@@ -306,7 +306,7 @@ static void ring_ep_doorbell(struct xhci_hcd *xhci,
/* Don't ring the doorbell for this endpoint if there are pending
* cancellations because the we don't want to interrupt processing.
*/
- if (!ep->cancels_pending && !(ep_state & SET_DEQ_PENDING)
+ if (!(ep_state & EP_HALT_PENDING) && !(ep_state & SET_DEQ_PENDING)
&& !(ep_state & EP_HALTED)) {
field = xhci_readl(xhci, db_addr) & DB_MASK;
xhci_writel(xhci, field | EPI_TO_DB(ep_index), db_addr);
@@ -475,6 +475,35 @@ void xhci_queue_new_dequeue_state(struct xhci_hcd *xhci,
ep->ep_state |= SET_DEQ_PENDING;
}
+static inline void xhci_stop_watchdog_timer_in_irq(struct xhci_hcd *xhci,
+ struct xhci_virt_ep *ep)
+{
+ ep->ep_state &= ~EP_HALT_PENDING;
+ /* Can't del_timer_sync in interrupt, so we attempt to cancel. If the
+ * timer is running on another CPU, we don't decrement stop_cmds_pending
+ * (since we didn't successfully stop the watchdog timer).
+ */
+ if (del_timer(&ep->stop_cmd_timer))
+ ep->stop_cmds_pending--;
+}
+
+/* Must be called with xhci->lock held in interrupt context */
+static void xhci_giveback_urb_in_irq(struct xhci_hcd *xhci,
+ struct xhci_td *cur_td, int status, char *adjective)
+{
+ struct usb_hcd *hcd = xhci_to_hcd(xhci);
+
+ cur_td->urb->hcpriv = NULL;
+ usb_hcd_unlink_urb_from_ep(hcd, cur_td->urb);
+ xhci_dbg(xhci, "Giveback %s URB %p\n", adjective, cur_td->urb);
+
+ spin_unlock(&xhci->lock);
+ usb_hcd_giveback_urb(hcd, cur_td->urb, status);
+ kfree(cur_td);
+ spin_lock(&xhci->lock);
+ xhci_dbg(xhci, "%s URB given back\n", adjective);
+}
+
/*
* When we get a command completion for a Stop Endpoint Command, we need to
* unlink any cancelled TDs from the ring. There are two ways to do that:
@@ -497,9 +526,6 @@ static void handle_stopped_endpoint(struct xhci_hcd *xhci,
struct xhci_td *last_unlinked_td;
struct xhci_dequeue_state deq_state;
-#ifdef CONFIG_USB_HCD_STAT
- ktime_t stop_time = ktime_get();
-#endif
memset(&deq_state, 0, sizeof(deq_state));
slot_id = TRB_TO_SLOT_ID(trb->generic.field[3]);
@@ -507,8 +533,11 @@ static void handle_stopped_endpoint(struct xhci_hcd *xhci,
ep = &xhci->devs[slot_id]->eps[ep_index];
ep_ring = ep->ring;
- if (list_empty(&ep->cancelled_td_list))
+ if (list_empty(&ep->cancelled_td_list)) {
+ xhci_stop_watchdog_timer_in_irq(xhci, ep);
+ ring_ep_doorbell(xhci, slot_id, ep_index);
return;
+ }
/* Fix up the ep ring first, so HW stops executing cancelled TDs.
* We have the xHCI lock, so nothing can modify this list until we drop
@@ -535,9 +564,9 @@ static void handle_stopped_endpoint(struct xhci_hcd *xhci,
* the cancelled TD list for URB completion later.
*/
list_del(&cur_td->td_list);
- ep->cancels_pending--;
}
last_unlinked_td = cur_td;
+ xhci_stop_watchdog_timer_in_irq(xhci, ep);
/* If necessary, queue a Set Transfer Ring Dequeue Pointer command */
if (deq_state.new_deq_ptr && deq_state.new_deq_seg) {
@@ -561,27 +590,136 @@ static void handle_stopped_endpoint(struct xhci_hcd *xhci,
list_del(&cur_td->cancelled_td_list);
/* Clean up the cancelled URB */
-#ifdef CONFIG_USB_HCD_STAT
- hcd_stat_update(xhci->tp_stat, cur_td->urb->actual_length,
- ktime_sub(stop_time, cur_td->start_time));
-#endif
- cur_td->urb->hcpriv = NULL;
- usb_hcd_unlink_urb_from_ep(xhci_to_hcd(xhci), cur_td->urb);
-
- xhci_dbg(xhci, "Giveback cancelled URB %p\n", cur_td->urb);
- spin_unlock(&xhci->lock);
/* Doesn't matter what we pass for status, since the core will
* just overwrite it (because the URB has been unlinked).
*/
- usb_hcd_giveback_urb(xhci_to_hcd(xhci), cur_td->urb, 0);
- kfree(cur_td);
+ xhci_giveback_urb_in_irq(xhci, cur_td, 0, "cancelled");
- spin_lock(&xhci->lock);
+ /* Stop processing the cancelled list if the watchdog timer is
+ * running.
+ */
+ if (xhci->xhc_state & XHCI_STATE_DYING)
+ return;
} while (cur_td != last_unlinked_td);
/* Return to the event handler with xhci->lock re-acquired */
}
+/* Watchdog timer function for when a stop endpoint command fails to complete.
+ * In this case, we assume the host controller is broken or dying or dead. The
+ * host may still be completing some other events, so we have to be careful to
+ * let the event ring handler and the URB dequeueing/enqueueing functions know
+ * through xhci->state.
+ *
+ * The timer may also fire if the host takes a very long time to respond to the
+ * command, and the stop endpoint command completion handler cannot delete the
+ * timer before the timer function is called. Another endpoint cancellation may
+ * sneak in before the timer function can grab the lock, and that may queue
+ * another stop endpoint command and add the timer back. So we cannot use a
+ * simple flag to say whether there is a pending stop endpoint command for a
+ * particular endpoint.
+ *
+ * Instead we use a combination of that flag and a counter for the number of
+ * pending stop endpoint commands. If the timer is the tail end of the last
+ * stop endpoint command, and the endpoint's command is still pending, we assume
+ * the host is dying.
+ */
+void xhci_stop_endpoint_command_watchdog(unsigned long arg)
+{
+ struct xhci_hcd *xhci;
+ struct xhci_virt_ep *ep;
+ struct xhci_virt_ep *temp_ep;
+ struct xhci_ring *ring;
+ struct xhci_td *cur_td;
+ int ret, i, j;
+
+ ep = (struct xhci_virt_ep *) arg;
+ xhci = ep->xhci;
+
+ spin_lock(&xhci->lock);
+
+ ep->stop_cmds_pending--;
+ if (xhci->xhc_state & XHCI_STATE_DYING) {
+ xhci_dbg(xhci, "Stop EP timer ran, but another timer marked "
+ "xHCI as DYING, exiting.\n");
+ spin_unlock(&xhci->lock);
+ return;
+ }
+ if (!(ep->stop_cmds_pending == 0 && (ep->ep_state & EP_HALT_PENDING))) {
+ xhci_dbg(xhci, "Stop EP timer ran, but no command pending, "
+ "exiting.\n");
+ spin_unlock(&xhci->lock);
+ return;
+ }
+
+ xhci_warn(xhci, "xHCI host not responding to stop endpoint command.\n");
+ xhci_warn(xhci, "Assuming host is dying, halting host.\n");
+ /* Oops, HC is dead or dying or at least not responding to the stop
+ * endpoint command.
+ */
+ xhci->xhc_state |= XHCI_STATE_DYING;
+ /* Disable interrupts from the host controller and start halting it */
+ xhci_quiesce(xhci);
+ spin_unlock(&xhci->lock);
+
+ ret = xhci_halt(xhci);
+
+ spin_lock(&xhci->lock);
+ if (ret < 0) {
+ /* This is bad; the host is not responding to commands and it's
+ * not allowing itself to be halted. At least interrupts are
+ * disabled, so we can set HC_STATE_HALT and notify the
+ * USB core. But if we call usb_hc_died(), it will attempt to
+ * disconnect all device drivers under this host. Those
+ * disconnect() methods will wait for all URBs to be unlinked,
+ * so we must complete them.
+ */
+ xhci_warn(xhci, "Non-responsive xHCI host is not halting.\n");
+ xhci_warn(xhci, "Completing active URBs anyway.\n");
+ /* We could turn all TDs on the rings to no-ops. This won't
+ * help if the host has cached part of the ring, and is slow if
+ * we want to preserve the cycle bit. Skip it and hope the host
+ * doesn't touch the memory.
+ */
+ }
+ for (i = 0; i < MAX_HC_SLOTS; i++) {
+ if (!xhci->devs[i])
+ continue;
+ for (j = 0; j < 31; j++) {
+ temp_ep = &xhci->devs[i]->eps[j];
+ ring = temp_ep->ring;
+ if (!ring)
+ continue;
+ xhci_dbg(xhci, "Killing URBs for slot ID %u, "
+ "ep index %u\n", i, j);
+ while (!list_empty(&ring->td_list)) {
+ cur_td = list_first_entry(&ring->td_list,
+ struct xhci_td,
+ td_list);
+ list_del(&cur_td->td_list);
+ if (!list_empty(&cur_td->cancelled_td_list))
+ list_del(&cur_td->cancelled_td_list);
+ xhci_giveback_urb_in_irq(xhci, cur_td,
+ -ESHUTDOWN, "killed");
+ }
+ while (!list_empty(&temp_ep->cancelled_td_list)) {
+ cur_td = list_first_entry(
+ &temp_ep->cancelled_td_list,
+ struct xhci_td,
+ cancelled_td_list);
+ list_del(&cur_td->cancelled_td_list);
+ xhci_giveback_urb_in_irq(xhci, cur_td,
+ -ESHUTDOWN, "killed");
+ }
+ }
+ }
+ spin_unlock(&xhci->lock);
+ xhci_to_hcd(xhci)->state = HC_STATE_HALT;
+ xhci_dbg(xhci, "Calling usb_hc_died()\n");
+ usb_hc_died(xhci_to_hcd(xhci));
+ xhci_dbg(xhci, "xHCI host controller is dead.\n");
+}
+
/*
* When we get a completion for a Set Transfer Ring Dequeue Pointer command,
* we need to clear the set deq pending flag in the endpoint ring state, so that
@@ -765,28 +903,32 @@ static void handle_cmd_completion(struct xhci_hcd *xhci,
virt_dev->in_ctx);
/* Input ctx add_flags are the endpoint index plus one */
ep_index = xhci_last_valid_endpoint(ctrl_ctx->add_flags) - 1;
- ep_ring = xhci->devs[slot_id]->eps[ep_index].ring;
- if (!ep_ring) {
- /* This must have been an initial configure endpoint */
- xhci->devs[slot_id]->cmd_status =
- GET_COMP_CODE(event->status);
- complete(&xhci->devs[slot_id]->cmd_completion);
- break;
- }
- ep_state = xhci->devs[slot_id]->eps[ep_index].ep_state;
- xhci_dbg(xhci, "Completed config ep cmd - last ep index = %d, "
- "state = %d\n", ep_index, ep_state);
+ /* A usb_set_interface() call directly after clearing a halted
+ * condition may race on this quirky hardware.
+ * Not worth worrying about, since this is prototype hardware.
+ */
if (xhci->quirks & XHCI_RESET_EP_QUIRK &&
- ep_state & EP_HALTED) {
+ ep_index != (unsigned int) -1 &&
+ ctrl_ctx->add_flags - SLOT_FLAG ==
+ ctrl_ctx->drop_flags) {
+ ep_ring = xhci->devs[slot_id]->eps[ep_index].ring;
+ ep_state = xhci->devs[slot_id]->eps[ep_index].ep_state;
+ if (!(ep_state & EP_HALTED))
+ goto bandwidth_change;
+ xhci_dbg(xhci, "Completed config ep cmd - "
+ "last ep index = %d, state = %d\n",
+ ep_index, ep_state);
/* Clear our internal halted state and restart ring */
xhci->devs[slot_id]->eps[ep_index].ep_state &=
~EP_HALTED;
ring_ep_doorbell(xhci, slot_id, ep_index);
- } else {
- xhci->devs[slot_id]->cmd_status =
- GET_COMP_CODE(event->status);
- complete(&xhci->devs[slot_id]->cmd_completion);
+ break;
}
+bandwidth_change:
+ xhci_dbg(xhci, "Completed config ep cmd\n");
+ xhci->devs[slot_id]->cmd_status =
+ GET_COMP_CODE(event->status);
+ complete(&xhci->devs[slot_id]->cmd_completion);
break;
case TRB_TYPE(TRB_EVAL_CONTEXT):
virt_dev = xhci->devs[slot_id];
@@ -849,8 +991,7 @@ static void handle_port_status(struct xhci_hcd *xhci,
* TRB in this TD, this function returns that TRB's segment. Otherwise it
* returns 0.
*/
-static struct xhci_segment *trb_in_td(
- struct xhci_segment *start_seg,
+struct xhci_segment *trb_in_td(struct xhci_segment *start_seg,
union xhci_trb *start_trb,
union xhci_trb *end_trb,
dma_addr_t suspect_dma)
@@ -900,6 +1041,45 @@ static struct xhci_segment *trb_in_td(
return 0;
}
+static void xhci_cleanup_halted_endpoint(struct xhci_hcd *xhci,
+ unsigned int slot_id, unsigned int ep_index,
+ struct xhci_td *td, union xhci_trb *event_trb)
+{
+ struct xhci_virt_ep *ep = &xhci->devs[slot_id]->eps[ep_index];
+ ep->ep_state |= EP_HALTED;
+ ep->stopped_td = td;
+ ep->stopped_trb = event_trb;
+ xhci_queue_reset_ep(xhci, slot_id, ep_index);
+ xhci_cleanup_stalled_ring(xhci, td->urb->dev, ep_index);
+ xhci_ring_cmd_db(xhci);
+}
+
+/* Check if an error has halted the endpoint ring. The class driver will
+ * cleanup the halt for a non-default control endpoint if we indicate a stall.
+ * However, a babble and other errors also halt the endpoint ring, and the class
+ * driver won't clear the halt in that case, so we need to issue a Set Transfer
+ * Ring Dequeue Pointer command manually.
+ */
+static int xhci_requires_manual_halt_cleanup(struct xhci_hcd *xhci,
+ struct xhci_ep_ctx *ep_ctx,
+ unsigned int trb_comp_code)
+{
+ /* TRB completion codes that may require a manual halt cleanup */
+ if (trb_comp_code == COMP_TX_ERR ||
+ trb_comp_code == COMP_BABBLE ||
+ trb_comp_code == COMP_SPLIT_ERR)
+ /* The 0.96 spec says a babbling control endpoint
+ * is not halted. The 0.96 spec says it is. Some HW
+ * claims to be 0.95 compliant, but it halts the control
+ * endpoint anyway. Check if a babble halted the
+ * endpoint.
+ */
+ if ((ep_ctx->ep_info & EP_STATE_MASK) == EP_STATE_HALTED)
+ return 1;
+
+ return 0;
+}
+
/*
* If this function returns an error condition, it means it got a Transfer
* event with a corrupted Slot ID, Endpoint ID, or TRB DMA address.
@@ -1002,6 +1182,7 @@ static int handle_tx_event(struct xhci_hcd *xhci,
xhci_warn(xhci, "WARN: TRB error on endpoint\n");
status = -EILSEQ;
break;
+ case COMP_SPLIT_ERR:
case COMP_TX_ERR:
xhci_warn(xhci, "WARN: transfer error on endpoint\n");
status = -EPROTO;
@@ -1015,6 +1196,16 @@ static int handle_tx_event(struct xhci_hcd *xhci,
status = -ENOSR;
break;
default:
+ if (trb_comp_code >= 224 && trb_comp_code <= 255) {
+ /* Vendor defined "informational" completion code,
+ * treat as not-an-error.
+ */
+ xhci_dbg(xhci, "Vendor defined info completion code %u\n",
+ trb_comp_code);
+ xhci_dbg(xhci, "Treating code as success.\n");
+ status = 0;
+ break;
+ }
xhci_warn(xhci, "ERROR Unknown event condition, HC probably busted\n");
urb = NULL;
goto cleanup;
@@ -1043,15 +1234,14 @@ static int handle_tx_event(struct xhci_hcd *xhci,
else
status = 0;
break;
- case COMP_BABBLE:
- /* The 0.96 spec says a babbling control endpoint
- * is not halted. The 0.96 spec says it is. Some HW
- * claims to be 0.95 compliant, but it halts the control
- * endpoint anyway. Check if a babble halted the
- * endpoint.
- */
- if (ep_ctx->ep_info != EP_STATE_HALTED)
+
+ default:
+ if (!xhci_requires_manual_halt_cleanup(xhci,
+ ep_ctx, trb_comp_code))
break;
+ xhci_dbg(xhci, "TRB error code %u, "
+ "halted endpoint index = %u\n",
+ trb_comp_code, ep_index);
/* else fall through */
case COMP_STALL:
/* Did we transfer part of the data (middle) phase? */
@@ -1063,15 +1253,9 @@ static int handle_tx_event(struct xhci_hcd *xhci,
else
td->urb->actual_length = 0;
- ep->stopped_td = td;
- ep->stopped_trb = event_trb;
- xhci_queue_reset_ep(xhci, slot_id, ep_index);
- xhci_cleanup_stalled_ring(xhci, td->urb->dev, ep_index);
- xhci_ring_cmd_db(xhci);
+ xhci_cleanup_halted_endpoint(xhci,
+ slot_id, ep_index, td, event_trb);
goto td_cleanup;
- default:
- /* Others already handled above */
- break;
}
/*
* Did we transfer any data, despite the errors that might have
@@ -1209,16 +1393,25 @@ static int handle_tx_event(struct xhci_hcd *xhci,
ep->stopped_td = td;
ep->stopped_trb = event_trb;
} else {
- if (trb_comp_code == COMP_STALL ||
- trb_comp_code == COMP_BABBLE) {
+ if (trb_comp_code == COMP_STALL) {
/* The transfer is completed from the driver's
* perspective, but we need to issue a set dequeue
* command for this stalled endpoint to move the dequeue
* pointer past the TD. We can't do that here because
- * the halt condition must be cleared first.
+ * the halt condition must be cleared first. Let the
+ * USB class driver clear the stall later.
*/
ep->stopped_td = td;
ep->stopped_trb = event_trb;
+ } else if (xhci_requires_manual_halt_cleanup(xhci,
+ ep_ctx, trb_comp_code)) {
+ /* Other types of errors halt the endpoint, but the
+ * class driver doesn't call usb_reset_endpoint() unless
+ * the error is -EPIPE. Clear the halted status in the
+ * xHCI hardware manually.
+ */
+ xhci_cleanup_halted_endpoint(xhci,
+ slot_id, ep_index, td, event_trb);
} else {
/* Update ring dequeue pointer */
while (ep_ring->dequeue != td->last_trb)
@@ -1249,10 +1442,9 @@ td_cleanup:
}
list_del(&td->td_list);
/* Was this TD slated to be cancelled but completed anyway? */
- if (!list_empty(&td->cancelled_td_list)) {
+ if (!list_empty(&td->cancelled_td_list))
list_del(&td->cancelled_td_list);
- ep->cancels_pending--;
- }
+
/* Leave the TD around for the reset endpoint function to use
* (but only if it's not a control endpoint, since we already
* queued the Set TR dequeue pointer command for stalled
@@ -1331,6 +1523,14 @@ void xhci_handle_event(struct xhci_hcd *xhci)
default:
xhci->error_bitmask |= 1 << 3;
}
+ /* Any of the above functions may drop and re-acquire the lock, so check
+ * to make sure a watchdog timer didn't mark the host as non-responsive.
+ */
+ if (xhci->xhc_state & XHCI_STATE_DYING) {
+ xhci_dbg(xhci, "xHCI host dying, returning from "
+ "event handler.\n");
+ return;
+ }
if (update_ptrs) {
/* Update SW and HC event ring dequeue pointer */
@@ -1555,6 +1755,21 @@ int xhci_queue_intr_tx(struct xhci_hcd *xhci, gfp_t mem_flags,
return xhci_queue_bulk_tx(xhci, GFP_ATOMIC, urb, slot_id, ep_index);
}
+/*
+ * The TD size is the number of bytes remaining in the TD (including this TRB),
+ * right shifted by 10.
+ * It must fit in bits 21:17, so it can't be bigger than 31.
+ */
+static u32 xhci_td_remainder(unsigned int remainder)
+{
+ u32 max = (1 << (21 - 17 + 1)) - 1;
+
+ if ((remainder >> 10) >= max)
+ return max << 17;
+ else
+ return (remainder >> 10) << 17;
+}
+
static int queue_bulk_sg_tx(struct xhci_hcd *xhci, gfp_t mem_flags,
struct urb *urb, int slot_id, unsigned int ep_index)
{
@@ -1612,6 +1827,7 @@ static int queue_bulk_sg_tx(struct xhci_hcd *xhci, gfp_t mem_flags,
do {
u32 field = 0;
u32 length_field = 0;
+ u32 remainder = 0;
/* Don't change the cycle bit of the first TRB until later */
if (first_trb)
@@ -1641,8 +1857,10 @@ static int queue_bulk_sg_tx(struct xhci_hcd *xhci, gfp_t mem_flags,
(unsigned int) (addr + TRB_MAX_BUFF_SIZE) & ~(TRB_MAX_BUFF_SIZE - 1),
(unsigned int) addr + trb_buff_len);
}
+ remainder = xhci_td_remainder(urb->transfer_buffer_length -
+ running_total) ;
length_field = TRB_LEN(trb_buff_len) |
- TD_REMAINDER(urb->transfer_buffer_length - running_total) |
+ remainder |
TRB_INTR_TARGET(0);
queue_trb(xhci, ep_ring, false,
lower_32_bits(addr),
@@ -1755,6 +1973,7 @@ int xhci_queue_bulk_tx(struct xhci_hcd *xhci, gfp_t mem_flags,
/* Queue the first TRB, even if it's zero-length */
do {
+ u32 remainder = 0;
field = 0;
/* Don't change the cycle bit of the first TRB until later */
@@ -1773,8 +1992,10 @@ int xhci_queue_bulk_tx(struct xhci_hcd *xhci, gfp_t mem_flags,
td->last_trb = ep_ring->enqueue;
field |= TRB_IOC;
}
+ remainder = xhci_td_remainder(urb->transfer_buffer_length -
+ running_total);
length_field = TRB_LEN(trb_buff_len) |
- TD_REMAINDER(urb->transfer_buffer_length - running_total) |
+ remainder |
TRB_INTR_TARGET(0);
queue_trb(xhci, ep_ring, false,
lower_32_bits(addr),
@@ -1862,7 +2083,7 @@ int xhci_queue_ctrl_tx(struct xhci_hcd *xhci, gfp_t mem_flags,
/* If there's data, queue data TRBs */
field = 0;
length_field = TRB_LEN(urb->transfer_buffer_length) |
- TD_REMAINDER(urb->transfer_buffer_length) |
+ xhci_td_remainder(urb->transfer_buffer_length) |
TRB_INTR_TARGET(0);
if (urb->transfer_buffer_length > 0) {
if (setup->bRequestType & USB_DIR_IN)
diff --git a/drivers/usb/host/xhci.h b/drivers/usb/host/xhci.h
index 4b254b6..8778135 100644
--- a/drivers/usb/host/xhci.h
+++ b/drivers/usb/host/xhci.h
@@ -652,13 +652,17 @@ struct xhci_virt_ep {
struct xhci_ring *new_ring;
unsigned int ep_state;
#define SET_DEQ_PENDING (1 << 0)
-#define EP_HALTED (1 << 1)
+#define EP_HALTED (1 << 1) /* For stall handling */
+#define EP_HALT_PENDING (1 << 2) /* For URB cancellation */
/* ---- Related to URB cancellation ---- */
struct list_head cancelled_td_list;
- unsigned int cancels_pending;
/* The TRB that was last reported in a stopped endpoint ring */
union xhci_trb *stopped_trb;
struct xhci_td *stopped_td;
+ /* Watchdog timer for stop endpoint command to cancel URBs */
+ struct timer_list stop_cmd_timer;
+ int stop_cmds_pending;
+ struct xhci_hcd *xhci;
};
struct xhci_virt_device {
@@ -673,6 +677,10 @@ struct xhci_virt_device {
struct xhci_container_ctx *out_ctx;
/* Used for addressing devices and configuration changes */
struct xhci_container_ctx *in_ctx;
+ /* Rings saved to ensure old alt settings can be re-instated */
+ struct xhci_ring **ring_cache;
+ int num_rings_cached;
+#define XHCI_MAX_RINGS_CACHED 31
struct xhci_virt_ep eps[31];
struct completion cmd_completion;
/* Status of the last command issued for this device */
@@ -824,9 +832,6 @@ struct xhci_event_cmd {
/* Normal TRB fields */
/* transfer_len bitmasks - bits 0:16 */
#define TRB_LEN(p) ((p) & 0x1ffff)
-/* TD size - number of bytes remaining in the TD (including this TRB):
- * bits 17 - 21. Shift the number of bytes by 10. */
-#define TD_REMAINDER(p) ((((p) >> 10) & 0x1f) << 17)
/* Interrupter Target - which MSI-X vector to target the completion event at */
#define TRB_INTR_TARGET(p) (((p) & 0x3ff) << 22)
#define GET_INTR_TARGET(p) (((p) >> 22) & 0x3ff)
@@ -1022,6 +1027,8 @@ struct xhci_scratchpad {
#define ERST_ENTRIES 1
/* Poll every 60 seconds */
#define POLL_TIMEOUT 60
+/* Stop endpoint command timeout (secs) for URB cancellation watchdog timer */
+#define XHCI_STOP_EP_CMD_TIMEOUT 5
/* XXX: Make these module parameters */
@@ -1083,6 +1090,21 @@ struct xhci_hcd {
struct timer_list event_ring_timer;
int zombie;
#endif
+ /* Host controller watchdog timer structures */
+ unsigned int xhc_state;
+/* Host controller is dying - not responding to commands. "I'm not dead yet!"
+ *
+ * xHC interrupts have been disabled and a watchdog timer will (or has already)
+ * halt the xHCI host, and complete all URBs with an -ESHUTDOWN code. Any code
+ * that sees this status (other than the timer that set it) should stop touching
+ * hardware immediately. Interrupt handlers should return immediately when
+ * they see this status (any time they drop and re-acquire xhci->lock).
+ * xhci_urb_dequeue() should call usb_hcd_check_unlink_urb() and return without
+ * putting the TD on the canceled list, etc.
+ *
+ * There are no reports of xHCI host controllers that display this issue.
+ */
+#define XHCI_STATE_DYING (1 << 0)
/* Statistics */
int noops_submitted;
int noops_handled;
@@ -1223,6 +1245,7 @@ void xhci_unregister_pci(void);
#endif
/* xHCI host controller glue */
+void xhci_quiesce(struct xhci_hcd *xhci);
int xhci_halt(struct xhci_hcd *xhci);
int xhci_reset(struct xhci_hcd *xhci);
int xhci_init(struct usb_hcd *hcd);
@@ -1246,6 +1269,9 @@ void xhci_reset_bandwidth(struct usb_hcd *hcd, struct usb_device *udev);
/* xHCI ring, segment, TRB, and TD functions */
dma_addr_t xhci_trb_virt_to_dma(struct xhci_segment *seg, union xhci_trb *trb);
+struct xhci_segment *trb_in_td(struct xhci_segment *start_seg,
+ union xhci_trb *start_trb, union xhci_trb *end_trb,
+ dma_addr_t suspect_dma);
void xhci_ring_cmd_db(struct xhci_hcd *xhci);
void *xhci_setup_one_noop(struct xhci_hcd *xhci);
void xhci_handle_event(struct xhci_hcd *xhci);
@@ -1278,6 +1304,7 @@ void xhci_cleanup_stalled_ring(struct xhci_hcd *xhci,
void xhci_queue_config_ep_quirk(struct xhci_hcd *xhci,
unsigned int slot_id, unsigned int ep_index,
struct xhci_dequeue_state *deq_state);
+void xhci_stop_endpoint_command_watchdog(unsigned long arg);
/* xHCI roothub code */
int xhci_hub_control(struct usb_hcd *hcd, u16 typeReq, u16 wValue, u16 wIndex,
diff --git a/drivers/usb/misc/usbtest.c b/drivers/usb/misc/usbtest.c
index a9f06d7..3dab0c0 100644
--- a/drivers/usb/misc/usbtest.c
+++ b/drivers/usb/misc/usbtest.c
@@ -213,8 +213,9 @@ static struct urb *simple_alloc_urb (
}
static unsigned pattern = 0;
-module_param (pattern, uint, S_IRUGO);
-MODULE_PARM_DESC(pattern, "i/o pattern (0 == zeroes)");
+static unsigned mod_pattern;
+module_param_named(pattern, mod_pattern, uint, S_IRUGO | S_IWUSR);
+MODULE_PARM_DESC(mod_pattern, "i/o pattern (0 == zeroes)");
static inline void simple_fill_buf (struct urb *urb)
{
@@ -1567,6 +1568,8 @@ usbtest_ioctl (struct usb_interface *intf, unsigned int code, void *buf)
// FIXME USBDEVFS_CONNECTINFO doesn't say how fast the device is.
+ pattern = mod_pattern;
+
if (code != USBTEST_REQUEST)
return -EOPNOTSUPP;
diff --git a/drivers/usb/mon/mon_bin.c b/drivers/usb/mon/mon_bin.c
index 10f3205..385ec05 100644
--- a/drivers/usb/mon/mon_bin.c
+++ b/drivers/usb/mon/mon_bin.c
@@ -16,6 +16,7 @@
#include <linux/compat.h>
#include <linux/mm.h>
#include <linux/smp_lock.h>
+#include <linux/scatterlist.h>
#include <asm/uaccess.h>
@@ -221,7 +222,7 @@ static void mon_free_buff(struct mon_pgmap *map, int npages);
/*
* This is a "chunked memcpy". It does not manipulate any counters.
*/
-static void mon_copy_to_buff(const struct mon_reader_bin *this,
+static unsigned int mon_copy_to_buff(const struct mon_reader_bin *this,
unsigned int off, const unsigned char *from, unsigned int length)
{
unsigned int step_len;
@@ -246,6 +247,7 @@ static void mon_copy_to_buff(const struct mon_reader_bin *this,
from += step_len;
length -= step_len;
}
+ return off;
}
/*
@@ -394,14 +396,44 @@ static inline char mon_bin_get_setup(unsigned char *setupb,
return 0;
}
-static char mon_bin_get_data(const struct mon_reader_bin *rp,
- unsigned int offset, struct urb *urb, unsigned int length)
+static unsigned int mon_bin_get_data(const struct mon_reader_bin *rp,
+ unsigned int offset, struct urb *urb, unsigned int length,
+ char *flag)
{
+ int i;
+ struct scatterlist *sg;
+ unsigned int this_len;
+
+ *flag = 0;
+ if (urb->num_sgs == 0) {
+ if (urb->transfer_buffer == NULL) {
+ *flag = 'Z';
+ return length;
+ }
+ mon_copy_to_buff(rp, offset, urb->transfer_buffer, length);
+ length = 0;
- if (urb->transfer_buffer == NULL)
- return 'Z';
- mon_copy_to_buff(rp, offset, urb->transfer_buffer, length);
- return 0;
+ } else {
+ /* If IOMMU coalescing occurred, we cannot trust sg_page */
+ if (urb->sg->nents != urb->num_sgs) {
+ *flag = 'D';
+ return length;
+ }
+
+ /* Copy up to the first non-addressable segment */
+ for_each_sg(urb->sg->sg, sg, urb->num_sgs, i) {
+ if (length == 0 || PageHighMem(sg_page(sg)))
+ break;
+ this_len = min_t(unsigned int, sg->length, length);
+ offset = mon_copy_to_buff(rp, offset, sg_virt(sg),
+ this_len);
+ length -= this_len;
+ }
+ if (i == 0)
+ *flag = 'D';
+ }
+
+ return length;
}
static void mon_bin_get_isodesc(const struct mon_reader_bin *rp,
@@ -536,8 +568,9 @@ static void mon_bin_event(struct mon_reader_bin *rp, struct urb *urb,
}
if (length != 0) {
- ep->flag_data = mon_bin_get_data(rp, offset, urb, length);
- if (ep->flag_data != 0) { /* Yes, it's 0x00, not '0' */
+ length = mon_bin_get_data(rp, offset, urb, length,
+ &ep->flag_data);
+ if (length > 0) {
delta = (ep->len_cap + PKT_ALIGN-1) & ~(PKT_ALIGN-1);
ep->len_cap -= length;
delta -= (ep->len_cap + PKT_ALIGN-1) & ~(PKT_ALIGN-1);
diff --git a/drivers/usb/mon/mon_text.c b/drivers/usb/mon/mon_text.c
index 9f1a922..047568f 100644
--- a/drivers/usb/mon/mon_text.c
+++ b/drivers/usb/mon/mon_text.c
@@ -10,6 +10,7 @@
#include <linux/time.h>
#include <linux/mutex.h>
#include <linux/debugfs.h>
+#include <linux/scatterlist.h>
#include <asm/uaccess.h>
#include "usb_mon.h"
@@ -137,6 +138,8 @@ static inline char mon_text_get_setup(struct mon_event_text *ep,
static inline char mon_text_get_data(struct mon_event_text *ep, struct urb *urb,
int len, char ev_type, struct mon_bus *mbus)
{
+ void *src;
+
if (len <= 0)
return 'L';
if (len >= DATA_MAX)
@@ -150,10 +153,24 @@ static inline char mon_text_get_data(struct mon_event_text *ep, struct urb *urb,
return '>';
}
- if (urb->transfer_buffer == NULL)
- return 'Z'; /* '0' would be not as pretty. */
+ if (urb->num_sgs == 0) {
+ src = urb->transfer_buffer;
+ if (src == NULL)
+ return 'Z'; /* '0' would be not as pretty. */
+ } else {
+ struct scatterlist *sg = urb->sg->sg;
+
+ /* If IOMMU coalescing occurred, we cannot trust sg_page */
+ if (urb->sg->nents != urb->num_sgs ||
+ PageHighMem(sg_page(sg)))
+ return 'D';
+
+ /* For the text interface we copy only the first sg buffer */
+ len = min_t(int, sg->length, len);
+ src = sg_virt(sg);
+ }
- memcpy(ep->data, urb->transfer_buffer, len);
+ memcpy(ep->data, src, len);
return 0;
}
diff --git a/drivers/usb/musb/Kconfig b/drivers/usb/musb/Kconfig
index b84abd8..d9db864 100644
--- a/drivers/usb/musb/Kconfig
+++ b/drivers/usb/musb/Kconfig
@@ -9,10 +9,9 @@ comment "Enable Host or Gadget support to see Inventra options"
# (M)HDRC = (Multipoint) Highspeed Dual-Role Controller
config USB_MUSB_HDRC
depends on (USB || USB_GADGET)
- depends on (ARM || BLACKFIN)
- select NOP_USB_XCEIV if ARCH_DAVINCI
+ depends on (ARM || (BF54x && !BF544) || (BF52x && !BF522 && !BF523))
+ select NOP_USB_XCEIV if (ARCH_DAVINCI || MACH_OMAP3EVM || BLACKFIN)
select TWL4030_USB if MACH_OMAP_3430SDP
- select NOP_USB_XCEIV if MACH_OMAP3EVM
select USB_OTG_UTILS
tristate 'Inventra Highspeed Dual Role Controller (TI, ADI, ...)'
help
diff --git a/drivers/usb/musb/blackfin.c b/drivers/usb/musb/blackfin.c
index fcec87e..fe4934d 100644
--- a/drivers/usb/musb/blackfin.c
+++ b/drivers/usb/musb/blackfin.c
@@ -53,13 +53,11 @@ void musb_write_fifo(struct musb_hw_ep *hw_ep, u16 len, const u8 *src)
void musb_read_fifo(struct musb_hw_ep *hw_ep, u16 len, u8 *dst)
{
void __iomem *fifo = hw_ep->fifo;
+
+#ifdef CONFIG_BF52x
u8 epnum = hw_ep->epnum;
u16 dma_reg = 0;
- DBG(4, "%cX ep%d fifo %p count %d buf %p\n",
- 'R', hw_ep->epnum, fifo, len, dst);
-
-#ifdef CONFIG_BF52x
invalidate_dcache_range((unsigned int)dst,
(unsigned int)(dst + len));
@@ -102,6 +100,9 @@ void musb_read_fifo(struct musb_hw_ep *hw_ep, u16 len, u8 *dst)
len & 0x01 ? (len >> 1) + 1 : len >> 1);
#endif
+ DBG(4, "%cX ep%d fifo %p count %d buf %p\n",
+ 'R', hw_ep->epnum, fifo, len, dst);
+
dump_fifo_data(dst, len);
}
@@ -225,8 +226,9 @@ int musb_platform_get_vbus_status(struct musb *musb)
return 0;
}
-void musb_platform_set_mode(struct musb *musb, u8 musb_mode)
+int musb_platform_set_mode(struct musb *musb, u8 musb_mode)
{
+ return -EIO;
}
int __init musb_platform_init(struct musb *musb)
@@ -261,10 +263,6 @@ int __init musb_platform_init(struct musb *musb)
SSYNC();
}
- /* TODO
- * Set SIC-IVG register
- */
-
/* Configure PLL oscillator register */
bfin_write_USB_PLLOSC_CTRL(0x30a8);
SSYNC();
diff --git a/drivers/usb/musb/blackfin.h b/drivers/usb/musb/blackfin.h
index a240c1e..10b7d75 100644
--- a/drivers/usb/musb/blackfin.h
+++ b/drivers/usb/musb/blackfin.h
@@ -14,6 +14,43 @@
* Blackfin specific definitions
*/
+/* Anomalies notes:
+ *
+ * 05000450 - USB DMA Mode 1 Short Packet Data Corruption:
+ * MUSB driver is designed to transfer buffer of N * maxpacket size
+ * in DMA mode 1 and leave the rest of the data to the next
+ * transfer in DMA mode 0, so we never transmit a short packet in
+ * DMA mode 1.
+ *
+ * 05000463 - This anomaly doesn't affect this driver since it
+ * never uses L1 or L2 memory as data destination.
+ *
+ * 05000464 - This anomaly doesn't affect this driver since it
+ * never uses L1 or L2 memory as data source.
+ *
+ * 05000465 - The anomaly can be seen when SCLK is over 100 MHz, and there is
+ * no way to workaround for bulk endpoints. Since the wMaxPackSize
+ * of bulk is less than or equal to 512, while the fifo size of
+ * endpoint 5, 6, 7 is 1024, the double buffer mode is enabled
+ * automatically when these endpoints are used for bulk OUT.
+ *
+ * 05000466 - This anomaly doesn't affect this driver since it never mixes
+ * concurrent DMA and core accesses to the TX endpoint FIFOs.
+ *
+ * 05000467 - The workaround for this anomaly will introduce another
+ * anomaly - 05000465.
+ */
+
+/* The Mentor USB DMA engine on BF52x (silicon v0.0 and v0.1) seems to be
+ * unstable in host mode. This may be caused by Anomaly 05000380. After
+ * digging out the root cause, we will change this number accordingly.
+ * So, need to either use silicon v0.2+ or disable DMA mode in MUSB.
+ */
+#if ANOMALY_05000380 && defined(CONFIG_BF52x) && \
+ defined(CONFIG_USB_MUSB_HDRC) && !defined(CONFIG_MUSB_PIO_ONLY)
+# error "Please use PIO mode in MUSB driver on bf52x chip v0.0 and v0.1"
+#endif
+
#undef DUMP_FIFO_DATA
#ifdef DUMP_FIFO_DATA
static void dump_fifo_data(u8 *buf, u16 len)
diff --git a/drivers/usb/musb/musb_core.c b/drivers/usb/musb/musb_core.c
index 547e0e3..49f2346 100644
--- a/drivers/usb/musb/musb_core.c
+++ b/drivers/usb/musb/musb_core.c
@@ -1319,7 +1319,6 @@ static int __init musb_core_init(u16 musb_type, struct musb *musb)
#endif
u8 reg;
char *type;
- u16 hwvers, rev_major, rev_minor;
char aInfo[78], aRevision[32], aDate[12];
void __iomem *mbase = musb->mregs;
int status = 0;
@@ -1391,11 +1390,10 @@ static int __init musb_core_init(u16 musb_type, struct musb *musb)
}
/* log release info */
- hwvers = musb_read_hwvers(mbase);
- rev_major = (hwvers >> 10) & 0x1f;
- rev_minor = hwvers & 0x3ff;
- snprintf(aRevision, 32, "%d.%d%s", rev_major,
- rev_minor, (hwvers & 0x8000) ? "RC" : "");
+ musb->hwvers = musb_read_hwvers(mbase);
+ snprintf(aRevision, 32, "%d.%d%s", MUSB_HWVERS_MAJOR(musb->hwvers),
+ MUSB_HWVERS_MINOR(musb->hwvers),
+ (musb->hwvers & MUSB_HWVERS_RC) ? "RC" : "");
printk(KERN_DEBUG "%s: %sHDRC RTL version %s %s\n",
musb_driver_name, type, aRevision, aDate);
diff --git a/drivers/usb/musb/musb_core.h b/drivers/usb/musb/musb_core.h
index 6aa5f22..03d5090 100644
--- a/drivers/usb/musb/musb_core.h
+++ b/drivers/usb/musb/musb_core.h
@@ -322,6 +322,14 @@ struct musb {
struct clk *clock;
irqreturn_t (*isr)(int, void *);
struct work_struct irq_work;
+#define MUSB_HWVERS_MAJOR(x) ((x >> 10) & 0x1f)
+#define MUSB_HWVERS_MINOR(x) (x & 0x3ff)
+#define MUSB_HWVERS_RC 0x8000
+#define MUSB_HWVERS_1300 0x52C
+#define MUSB_HWVERS_1400 0x590
+#define MUSB_HWVERS_1800 0x720
+#define MUSB_HWVERS_2000 0x800
+ u16 hwvers;
/* this hub status bit is reserved by USB 2.0 and not seen by usbcore */
#define MUSB_PORT_STAT_RESUME (1 << 31)
diff --git a/drivers/usb/musb/musb_dma.h b/drivers/usb/musb/musb_dma.h
index 0a2c4e3..916065b 100644
--- a/drivers/usb/musb/musb_dma.h
+++ b/drivers/usb/musb/musb_dma.h
@@ -80,6 +80,17 @@ struct musb_hw_ep;
#define tusb_dma_omap() 0
#endif
+/* Anomaly 05000456 - USB Receive Interrupt Is Not Generated in DMA Mode 1
+ * Only allow DMA mode 1 to be used when the USB will actually generate the
+ * interrupts we expect.
+ */
+#ifdef CONFIG_BLACKFIN
+# undef USE_MODE1
+# if !ANOMALY_05000456
+# define USE_MODE1
+# endif
+#endif
+
/*
* DMA channel status ... updated by the dma controller driver whenever that
* status changes, and protected by the overall controller spinlock.
diff --git a/drivers/usb/musb/musb_gadget.c b/drivers/usb/musb/musb_gadget.c
index 74073f9..c49b9ba 100644
--- a/drivers/usb/musb/musb_gadget.c
+++ b/drivers/usb/musb/musb_gadget.c
@@ -429,112 +429,102 @@ void musb_g_tx(struct musb *musb, u8 epnum)
DBG(4, "<== %s, txcsr %04x\n", musb_ep->end_point.name, csr);
dma = is_dma_capable() ? musb_ep->dma : NULL;
- do {
- /* REVISIT for high bandwidth, MUSB_TXCSR_P_INCOMPTX
- * probably rates reporting as a host error
+
+ /*
+ * REVISIT: for high bandwidth, MUSB_TXCSR_P_INCOMPTX
+ * probably rates reporting as a host error.
+ */
+ if (csr & MUSB_TXCSR_P_SENTSTALL) {
+ csr |= MUSB_TXCSR_P_WZC_BITS;
+ csr &= ~MUSB_TXCSR_P_SENTSTALL;
+ musb_writew(epio, MUSB_TXCSR, csr);
+ return;
+ }
+
+ if (csr & MUSB_TXCSR_P_UNDERRUN) {
+ /* We NAKed, no big deal... little reason to care. */
+ csr |= MUSB_TXCSR_P_WZC_BITS;
+ csr &= ~(MUSB_TXCSR_P_UNDERRUN | MUSB_TXCSR_TXPKTRDY);
+ musb_writew(epio, MUSB_TXCSR, csr);
+ DBG(20, "underrun on ep%d, req %p\n", epnum, request);
+ }
+
+ if (dma_channel_status(dma) == MUSB_DMA_STATUS_BUSY) {
+ /*
+ * SHOULD NOT HAPPEN... has with CPPI though, after
+ * changing SENDSTALL (and other cases); harmless?
*/
- if (csr & MUSB_TXCSR_P_SENTSTALL) {
- csr |= MUSB_TXCSR_P_WZC_BITS;
- csr &= ~MUSB_TXCSR_P_SENTSTALL;
- musb_writew(epio, MUSB_TXCSR, csr);
- break;
- }
+ DBG(5, "%s dma still busy?\n", musb_ep->end_point.name);
+ return;
+ }
+
+ if (request) {
+ u8 is_dma = 0;
- if (csr & MUSB_TXCSR_P_UNDERRUN) {
- /* we NAKed, no big deal ... little reason to care */
+ if (dma && (csr & MUSB_TXCSR_DMAENAB)) {
+ is_dma = 1;
csr |= MUSB_TXCSR_P_WZC_BITS;
- csr &= ~(MUSB_TXCSR_P_UNDERRUN
- | MUSB_TXCSR_TXPKTRDY);
+ csr &= ~(MUSB_TXCSR_DMAENAB | MUSB_TXCSR_P_UNDERRUN |
+ MUSB_TXCSR_TXPKTRDY);
musb_writew(epio, MUSB_TXCSR, csr);
- DBG(20, "underrun on ep%d, req %p\n", epnum, request);
+ /* Ensure writebuffer is empty. */
+ csr = musb_readw(epio, MUSB_TXCSR);
+ request->actual += musb_ep->dma->actual_len;
+ DBG(4, "TXCSR%d %04x, DMA off, len %zu, req %p\n",
+ epnum, csr, musb_ep->dma->actual_len, request);
}
- if (dma_channel_status(dma) == MUSB_DMA_STATUS_BUSY) {
- /* SHOULD NOT HAPPEN ... has with cppi though, after
- * changing SENDSTALL (and other cases); harmless?
+ if (is_dma || request->actual == request->length) {
+ /*
+ * First, maybe a terminating short packet. Some DMA
+ * engines might handle this by themselves.
*/
- DBG(5, "%s dma still busy?\n", musb_ep->end_point.name);
- break;
- }
-
- if (request) {
- u8 is_dma = 0;
-
- if (dma && (csr & MUSB_TXCSR_DMAENAB)) {
- is_dma = 1;
- csr |= MUSB_TXCSR_P_WZC_BITS;
- csr &= ~(MUSB_TXCSR_DMAENAB
- | MUSB_TXCSR_P_UNDERRUN
- | MUSB_TXCSR_TXPKTRDY);
- musb_writew(epio, MUSB_TXCSR, csr);
- /* ensure writebuffer is empty */
- csr = musb_readw(epio, MUSB_TXCSR);
- request->actual += musb_ep->dma->actual_len;
- DBG(4, "TXCSR%d %04x, dma off, "
- "len %zu, req %p\n",
- epnum, csr,
- musb_ep->dma->actual_len,
- request);
- }
-
- if (is_dma || request->actual == request->length) {
-
- /* First, maybe a terminating short packet.
- * Some DMA engines might handle this by
- * themselves.
- */
- if ((request->zero
- && request->length
- && (request->length
- % musb_ep->packet_sz)
- == 0)
+ if ((request->zero && request->length
+ && request->length % musb_ep->packet_sz == 0)
#ifdef CONFIG_USB_INVENTRA_DMA
- || (is_dma &&
- ((!dma->desired_mode) ||
- (request->actual &
- (musb_ep->packet_sz - 1))))
+ || (is_dma && (!dma->desired_mode ||
+ (request->actual &
+ (musb_ep->packet_sz - 1))))
#endif
- ) {
- /* on dma completion, fifo may not
- * be available yet ...
- */
- if (csr & MUSB_TXCSR_TXPKTRDY)
- break;
-
- DBG(4, "sending zero pkt\n");
- musb_writew(epio, MUSB_TXCSR,
- MUSB_TXCSR_MODE
- | MUSB_TXCSR_TXPKTRDY);
- request->zero = 0;
- }
-
- /* ... or if not, then complete it */
- musb_g_giveback(musb_ep, request, 0);
-
- /* kickstart next transfer if appropriate;
- * the packet that just completed might not
- * be transmitted for hours or days.
- * REVISIT for double buffering...
- * FIXME revisit for stalls too...
+ ) {
+ /*
+ * On DMA completion, FIFO may not be
+ * available yet...
*/
- musb_ep_select(mbase, epnum);
- csr = musb_readw(epio, MUSB_TXCSR);
- if (csr & MUSB_TXCSR_FIFONOTEMPTY)
- break;
- request = musb_ep->desc
- ? next_request(musb_ep)
- : NULL;
- if (!request) {
- DBG(4, "%s idle now\n",
- musb_ep->end_point.name);
- break;
- }
+ if (csr & MUSB_TXCSR_TXPKTRDY)
+ return;
+
+ DBG(4, "sending zero pkt\n");
+ musb_writew(epio, MUSB_TXCSR, MUSB_TXCSR_MODE
+ | MUSB_TXCSR_TXPKTRDY);
+ request->zero = 0;
}
- txstate(musb, to_musb_request(request));
+ /* ... or if not, then complete it. */
+ musb_g_giveback(musb_ep, request, 0);
+
+ /*
+ * Kickstart next transfer if appropriate;
+ * the packet that just completed might not
+ * be transmitted for hours or days.
+ * REVISIT for double buffering...
+ * FIXME revisit for stalls too...
+ */
+ musb_ep_select(mbase, epnum);
+ csr = musb_readw(epio, MUSB_TXCSR);
+ if (csr & MUSB_TXCSR_FIFONOTEMPTY)
+ return;
+
+ if (!musb_ep->desc) {
+ DBG(4, "%s idle now\n",
+ musb_ep->end_point.name);
+ return;
+ } else
+ request = next_request(musb_ep);
}
- } while (0);
+ txstate(musb, to_musb_request(request));
+ }
}
/* ------------------------------------------------------------ */
@@ -966,6 +956,7 @@ static int musb_gadget_enable(struct usb_ep *ep,
musb_ep->desc = desc;
musb_ep->busy = 0;
+ musb_ep->wedged = 0;
status = 0;
pr_debug("%s periph: enabled %s for %s %s, %smaxpacket %d\n",
@@ -1220,7 +1211,7 @@ done:
*
* exported to ep0 code
*/
-int musb_gadget_set_halt(struct usb_ep *ep, int value)
+static int musb_gadget_set_halt(struct usb_ep *ep, int value)
{
struct musb_ep *musb_ep = to_musb_ep(ep);
u8 epnum = musb_ep->current_epnum;
@@ -1262,7 +1253,8 @@ int musb_gadget_set_halt(struct usb_ep *ep, int value)
goto done;
}
}
- }
+ } else
+ musb_ep->wedged = 0;
/* set/clear the stall and toggle bits */
DBG(2, "%s: %s stall\n", ep->name, value ? "set" : "clear");
@@ -1301,6 +1293,21 @@ done:
return status;
}
+/*
+ * Sets the halt feature with the clear requests ignored
+ */
+static int musb_gadget_set_wedge(struct usb_ep *ep)
+{
+ struct musb_ep *musb_ep = to_musb_ep(ep);
+
+ if (!ep)
+ return -EINVAL;
+
+ musb_ep->wedged = 1;
+
+ return usb_ep_set_halt(ep);
+}
+
static int musb_gadget_fifo_status(struct usb_ep *ep)
{
struct musb_ep *musb_ep = to_musb_ep(ep);
@@ -1371,6 +1378,7 @@ static const struct usb_ep_ops musb_ep_ops = {
.queue = musb_gadget_queue,
.dequeue = musb_gadget_dequeue,
.set_halt = musb_gadget_set_halt,
+ .set_wedge = musb_gadget_set_wedge,
.fifo_status = musb_gadget_fifo_status,
.fifo_flush = musb_gadget_fifo_flush
};
diff --git a/drivers/usb/musb/musb_gadget.h b/drivers/usb/musb/musb_gadget.h
index 59502da..c8b1403 100644
--- a/drivers/usb/musb/musb_gadget.h
+++ b/drivers/usb/musb/musb_gadget.h
@@ -75,6 +75,8 @@ struct musb_ep {
/* later things are modified based on usage */
struct list_head req_list;
+ u8 wedged;
+
/* true if lock must be dropped but req_list may not be advanced */
u8 busy;
};
@@ -103,6 +105,4 @@ extern void musb_gadget_cleanup(struct musb *);
extern void musb_g_giveback(struct musb_ep *, struct usb_request *, int);
-extern int musb_gadget_set_halt(struct usb_ep *ep, int value);
-
#endif /* __MUSB_GADGET_H */
diff --git a/drivers/usb/musb/musb_gadget_ep0.c b/drivers/usb/musb/musb_gadget_ep0.c
index 522efb3..8fba3f1 100644
--- a/drivers/usb/musb/musb_gadget_ep0.c
+++ b/drivers/usb/musb/musb_gadget_ep0.c
@@ -199,7 +199,6 @@ service_in_request(struct musb *musb, const struct usb_ctrlrequest *ctrlrequest)
static void musb_g_ep0_giveback(struct musb *musb, struct usb_request *req)
{
musb_g_giveback(&musb->endpoints[0].ep_in, req, 0);
- musb->ep0_state = MUSB_EP0_STAGE_SETUP;
}
/*
@@ -258,30 +257,53 @@ __acquires(musb->lock)
case USB_RECIP_INTERFACE:
break;
case USB_RECIP_ENDPOINT:{
- const u8 num = ctrlrequest->wIndex & 0x0f;
- struct musb_ep *musb_ep;
+ const u8 epnum =
+ ctrlrequest->wIndex & 0x0f;
+ struct musb_ep *musb_ep;
+ struct musb_hw_ep *ep;
+ void __iomem *regs;
+ int is_in;
+ u16 csr;
- if (num == 0
- || num >= MUSB_C_NUM_EPS
- || ctrlrequest->wValue
- != USB_ENDPOINT_HALT)
+ if (epnum == 0 || epnum >= MUSB_C_NUM_EPS ||
+ ctrlrequest->wValue != USB_ENDPOINT_HALT)
break;
- if (ctrlrequest->wIndex & USB_DIR_IN)
- musb_ep = &musb->endpoints[num].ep_in;
+ ep = musb->endpoints + epnum;
+ regs = ep->regs;
+ is_in = ctrlrequest->wIndex & USB_DIR_IN;
+ if (is_in)
+ musb_ep = &ep->ep_in;
else
- musb_ep = &musb->endpoints[num].ep_out;
+ musb_ep = &ep->ep_out;
if (!musb_ep->desc)
break;
- /* REVISIT do it directly, no locking games */
- spin_unlock(&musb->lock);
- musb_gadget_set_halt(&musb_ep->end_point, 0);
- spin_lock(&musb->lock);
+ handled = 1;
+ /* Ignore request if endpoint is wedged */
+ if (musb_ep->wedged)
+ break;
+
+ musb_ep_select(mbase, epnum);
+ if (is_in) {
+ csr = musb_readw(regs, MUSB_TXCSR);
+ csr |= MUSB_TXCSR_CLRDATATOG |
+ MUSB_TXCSR_P_WZC_BITS;
+ csr &= ~(MUSB_TXCSR_P_SENDSTALL |
+ MUSB_TXCSR_P_SENTSTALL |
+ MUSB_TXCSR_TXPKTRDY);
+ musb_writew(regs, MUSB_TXCSR, csr);
+ } else {
+ csr = musb_readw(regs, MUSB_RXCSR);
+ csr |= MUSB_RXCSR_CLRDATATOG |
+ MUSB_RXCSR_P_WZC_BITS;
+ csr &= ~(MUSB_RXCSR_P_SENDSTALL |
+ MUSB_RXCSR_P_SENTSTALL);
+ musb_writew(regs, MUSB_RXCSR, csr);
+ }
/* select ep0 again */
musb_ep_select(mbase, 0);
- handled = 1;
} break;
default:
/* class, vendor, etc ... delegate */
@@ -374,10 +396,8 @@ stall:
int is_in;
u16 csr;
- if (epnum == 0
- || epnum >= MUSB_C_NUM_EPS
- || ctrlrequest->wValue
- != USB_ENDPOINT_HALT)
+ if (epnum == 0 || epnum >= MUSB_C_NUM_EPS ||
+ ctrlrequest->wValue != USB_ENDPOINT_HALT)
break;
ep = musb->endpoints + epnum;
@@ -392,24 +412,20 @@ stall:
musb_ep_select(mbase, epnum);
if (is_in) {
- csr = musb_readw(regs,
- MUSB_TXCSR);
+ csr = musb_readw(regs, MUSB_TXCSR);
if (csr & MUSB_TXCSR_FIFONOTEMPTY)
csr |= MUSB_TXCSR_FLUSHFIFO;
csr |= MUSB_TXCSR_P_SENDSTALL
| MUSB_TXCSR_CLRDATATOG
| MUSB_TXCSR_P_WZC_BITS;
- musb_writew(regs, MUSB_TXCSR,
- csr);
+ musb_writew(regs, MUSB_TXCSR, csr);
} else {
- csr = musb_readw(regs,
- MUSB_RXCSR);
+ csr = musb_readw(regs, MUSB_RXCSR);
csr |= MUSB_RXCSR_P_SENDSTALL
| MUSB_RXCSR_FLUSHFIFO
| MUSB_RXCSR_CLRDATATOG
| MUSB_RXCSR_P_WZC_BITS;
- musb_writew(regs, MUSB_RXCSR,
- csr);
+ musb_writew(regs, MUSB_RXCSR, csr);
}
/* select ep0 again */
diff --git a/drivers/usb/musb/musb_host.c b/drivers/usb/musb/musb_host.c
index e3ab40a..74c4c36 100644
--- a/drivers/usb/musb/musb_host.c
+++ b/drivers/usb/musb/musb_host.c
@@ -1642,18 +1642,18 @@ void musb_host_rx(struct musb *musb, u8 epnum)
c = musb->dma_controller;
if (usb_pipeisoc(pipe)) {
- int status = 0;
+ int d_status = 0;
struct usb_iso_packet_descriptor *d;
d = urb->iso_frame_desc + qh->iso_idx;
if (iso_err) {
- status = -EILSEQ;
+ d_status = -EILSEQ;
urb->error_count++;
}
if (rx_count > d->length) {
- if (status == 0) {
- status = -EOVERFLOW;
+ if (d_status == 0) {
+ d_status = -EOVERFLOW;
urb->error_count++;
}
DBG(2, "** OVERFLOW %d into %d\n",\
@@ -1662,7 +1662,7 @@ void musb_host_rx(struct musb *musb, u8 epnum)
length = d->length;
} else
length = rx_count;
- d->status = status;
+ d->status = d_status;
buf = urb->transfer_dma + d->offset;
} else {
length = rx_count;
diff --git a/drivers/usb/musb/musb_regs.h b/drivers/usb/musb/musb_regs.h
index cc1d71b..473a94e 100644
--- a/drivers/usb/musb/musb_regs.h
+++ b/drivers/usb/musb/musb_regs.h
@@ -465,9 +465,9 @@ static inline u16 musb_read_hwvers(void __iomem *mbase)
return 0;
}
-static inline u16 musb_read_target_reg_base(u8 i, void __iomem *mbase)
+static inline void __iomem *musb_read_target_reg_base(u8 i, void __iomem *mbase)
{
- return 0;
+ return NULL;
}
static inline void musb_write_rxfunaddr(void __iomem *ep_target_regs,
diff --git a/drivers/usb/musb/musbhsdma.c b/drivers/usb/musb/musbhsdma.c
index 5e83f96..a237550 100644
--- a/drivers/usb/musb/musbhsdma.c
+++ b/drivers/usb/musb/musbhsdma.c
@@ -259,6 +259,11 @@ static irqreturn_t dma_controller_irq(int irq, void *private_data)
if (!int_hsdma)
goto done;
+#ifdef CONFIG_BLACKFIN
+ /* Clear DMA interrupt flags */
+ musb_writeb(mbase, MUSB_HSDMA_INTR, int_hsdma);
+#endif
+
for (bchannel = 0; bchannel < MUSB_HSDMA_CHANNELS; bchannel++) {
if (int_hsdma & (1 << bchannel)) {
musb_channel = (struct musb_dma_channel *)
@@ -280,7 +285,7 @@ static irqreturn_t dma_controller_irq(int irq, void *private_data)
channel->actual_len = addr
- musb_channel->start_addr;
- DBG(2, "ch %p, 0x%x -> 0x%x (%d / %d) %s\n",
+ DBG(2, "ch %p, 0x%x -> 0x%x (%zu / %d) %s\n",
channel, musb_channel->start_addr,
addr, channel->actual_len,
musb_channel->len,
@@ -324,11 +329,6 @@ static irqreturn_t dma_controller_irq(int irq, void *private_data)
}
}
-#ifdef CONFIG_BLACKFIN
- /* Clear DMA interrup flags */
- musb_writeb(mbase, MUSB_HSDMA_INTR, int_hsdma);
-#endif
-
retval = IRQ_HANDLED;
done:
spin_unlock_irqrestore(&musb->lock, flags);
diff --git a/drivers/usb/musb/omap2430.c b/drivers/usb/musb/omap2430.c
index 6761d20..83beeac 100644
--- a/drivers/usb/musb/omap2430.c
+++ b/drivers/usb/musb/omap2430.c
@@ -315,7 +315,7 @@ int musb_platform_exit(struct musb *musb)
musb_platform_suspend(musb);
clk_put(musb->clock);
- musb->clock = 0;
+ musb->clock = NULL;
return 0;
}
diff --git a/drivers/usb/otg/Kconfig b/drivers/usb/otg/Kconfig
index aa884d0..de56b3d 100644
--- a/drivers/usb/otg/Kconfig
+++ b/drivers/usb/otg/Kconfig
@@ -41,6 +41,15 @@ config ISP1301_OMAP
This driver can also be built as a module. If so, the module
will be called isp1301_omap.
+config USB_ULPI
+ bool "Generic ULPI Transceiver Driver"
+ depends on ARM
+ help
+ Enable this to support ULPI connected USB OTG transceivers which
+ are likely found on embedded boards.
+
+ The only chip currently supported is NXP's ISP1504
+
config TWL4030_USB
tristate "TWL4030 USB Transceiver Driver"
depends on TWL4030_CORE && REGULATOR_TWL4030
diff --git a/drivers/usb/otg/Makefile b/drivers/usb/otg/Makefile
index 2081678..aeb49a8 100644
--- a/drivers/usb/otg/Makefile
+++ b/drivers/usb/otg/Makefile
@@ -10,6 +10,7 @@ obj-$(CONFIG_USB_GPIO_VBUS) += gpio_vbus.o
obj-$(CONFIG_ISP1301_OMAP) += isp1301_omap.o
obj-$(CONFIG_TWL4030_USB) += twl4030-usb.o
obj-$(CONFIG_NOP_USB_XCEIV) += nop-usb-xceiv.o
+obj-$(CONFIG_USB_ULPI) += ulpi.o
ccflags-$(CONFIG_USB_DEBUG) += -DDEBUG
ccflags-$(CONFIG_USB_GADGET_DEBUG) += -DDEBUG
diff --git a/drivers/usb/otg/twl4030-usb.c b/drivers/usb/otg/twl4030-usb.c
index 9e3e7a5..bd9883f 100644
--- a/drivers/usb/otg/twl4030-usb.c
+++ b/drivers/usb/otg/twl4030-usb.c
@@ -598,12 +598,12 @@ static irqreturn_t twl4030_usb_irq(int irq, void *_twl)
* USB_LINK_VBUS state. musb_hdrc won't care until it
* starts to handle softconnect right.
*/
- twl4030charger_usb_en(status == USB_LINK_VBUS);
-
if (status == USB_LINK_NONE)
twl4030_phy_suspend(twl, 0);
else
twl4030_phy_resume(twl);
+
+ twl4030charger_usb_en(status == USB_LINK_VBUS);
}
sysfs_notify(&twl->dev->kobj, NULL, "vbus");
diff --git a/drivers/usb/otg/ulpi.c b/drivers/usb/otg/ulpi.c
new file mode 100644
index 0000000..8965274
--- /dev/null
+++ b/drivers/usb/otg/ulpi.c
@@ -0,0 +1,136 @@
+/*
+ * Generic ULPI USB transceiver support
+ *
+ * Copyright (C) 2009 Daniel Mack <daniel@caiaq.de>
+ *
+ * Based on sources from
+ *
+ * Sascha Hauer <s.hauer@pengutronix.de>
+ * Freescale Semiconductors
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ */
+
+#include <linux/kernel.h>
+#include <linux/usb.h>
+#include <linux/usb/otg.h>
+#include <linux/usb/ulpi.h>
+
+/* ULPI register addresses */
+#define ULPI_VID_LOW 0x00 /* Vendor ID low */
+#define ULPI_VID_HIGH 0x01 /* Vendor ID high */
+#define ULPI_PID_LOW 0x02 /* Product ID low */
+#define ULPI_PID_HIGH 0x03 /* Product ID high */
+#define ULPI_ITFCTL 0x07 /* Interface Control */
+#define ULPI_OTGCTL 0x0A /* OTG Control */
+
+/* add to above register address to access Set/Clear functions */
+#define ULPI_REG_SET 0x01
+#define ULPI_REG_CLEAR 0x02
+
+/* ULPI OTG Control Register bits */
+#define ID_PULL_UP (1 << 0) /* enable ID Pull Up */
+#define DP_PULL_DOWN (1 << 1) /* enable DP Pull Down */
+#define DM_PULL_DOWN (1 << 2) /* enable DM Pull Down */
+#define DISCHRG_VBUS (1 << 3) /* Discharge Vbus */
+#define CHRG_VBUS (1 << 4) /* Charge Vbus */
+#define DRV_VBUS (1 << 5) /* Drive Vbus */
+#define DRV_VBUS_EXT (1 << 6) /* Drive Vbus external */
+#define USE_EXT_VBUS_IND (1 << 7) /* Use ext. Vbus indicator */
+
+#define ULPI_ID(vendor, product) (((vendor) << 16) | (product))
+
+#define TR_FLAG(flags, a, b) (((flags) & a) ? b : 0)
+
+/* ULPI hardcoded IDs, used for probing */
+static unsigned int ulpi_ids[] = {
+ ULPI_ID(0x04cc, 0x1504), /* NXP ISP1504 */
+};
+
+static int ulpi_set_flags(struct otg_transceiver *otg)
+{
+ unsigned int flags = 0;
+
+ if (otg->flags & USB_OTG_PULLUP_ID)
+ flags |= ID_PULL_UP;
+
+ if (otg->flags & USB_OTG_PULLDOWN_DM)
+ flags |= DM_PULL_DOWN;
+
+ if (otg->flags & USB_OTG_PULLDOWN_DP)
+ flags |= DP_PULL_DOWN;
+
+ if (otg->flags & USB_OTG_EXT_VBUS_INDICATOR)
+ flags |= USE_EXT_VBUS_IND;
+
+ return otg_io_write(otg, flags, ULPI_OTGCTL + ULPI_REG_SET);
+}
+
+static int ulpi_init(struct otg_transceiver *otg)
+{
+ int i, vid, pid;
+
+ vid = (otg_io_read(otg, ULPI_VID_HIGH) << 8) |
+ otg_io_read(otg, ULPI_VID_LOW);
+ pid = (otg_io_read(otg, ULPI_PID_HIGH) << 8) |
+ otg_io_read(otg, ULPI_PID_LOW);
+
+ pr_info("ULPI transceiver vendor/product ID 0x%04x/0x%04x\n", vid, pid);
+
+ for (i = 0; i < ARRAY_SIZE(ulpi_ids); i++)
+ if (ulpi_ids[i] == ULPI_ID(vid, pid))
+ return ulpi_set_flags(otg);
+
+ pr_err("ULPI ID does not match any known transceiver.\n");
+ return -ENODEV;
+}
+
+static int ulpi_set_vbus(struct otg_transceiver *otg, bool on)
+{
+ unsigned int flags = otg_io_read(otg, ULPI_OTGCTL);
+
+ flags &= ~(DRV_VBUS | DRV_VBUS_EXT);
+
+ if (on) {
+ if (otg->flags & USB_OTG_DRV_VBUS)
+ flags |= DRV_VBUS;
+
+ if (otg->flags & USB_OTG_DRV_VBUS_EXT)
+ flags |= DRV_VBUS_EXT;
+ }
+
+ return otg_io_write(otg, flags, ULPI_OTGCTL + ULPI_REG_SET);
+}
+
+struct otg_transceiver *
+otg_ulpi_create(struct otg_io_access_ops *ops,
+ unsigned int flags)
+{
+ struct otg_transceiver *otg;
+
+ otg = kzalloc(sizeof(*otg), GFP_KERNEL);
+ if (!otg)
+ return NULL;
+
+ otg->label = "ULPI";
+ otg->flags = flags;
+ otg->io_ops = ops;
+ otg->init = ulpi_init;
+ otg->set_vbus = ulpi_set_vbus;
+
+ return otg;
+}
+EXPORT_SYMBOL_GPL(otg_ulpi_create);
+
diff --git a/drivers/usb/serial/ark3116.c b/drivers/usb/serial/ark3116.c
index 131e61a..a9c2dec 100644
--- a/drivers/usb/serial/ark3116.c
+++ b/drivers/usb/serial/ark3116.c
@@ -1,4 +1,6 @@
/*
+ * Copyright (C) 2009 by Bart Hartgers (bart.hartgers+ark3116@gmail.com)
+ * Original version:
* Copyright (C) 2006
* Simon Schulz (ark3116_driver <at> auctionant.de)
*
@@ -6,10 +8,13 @@
* - implements a driver for the arkmicro ark3116 chipset (vendor=0x6547,
* productid=0x0232) (used in a datacable called KQ-U8A)
*
- * - based on code by krisfx -> thanks !!
- * (see http://www.linuxquestions.org/questions/showthread.php?p=2184457#post2184457)
+ * Supports full modem status lines, break, hardware flow control. Does not
+ * support software flow control, since I do not know how to enable it in hw.
*
- * - based on logs created by usbsnoopy
+ * This driver is a essentially new implementation. I initially dug
+ * into the old ark3116.c driver and suddenly realized the ark3116 is
+ * a 16450 with a USB interface glued to it. See comments at the
+ * bottom of this file.
*
* 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
@@ -19,15 +24,31 @@
#include <linux/kernel.h>
#include <linux/init.h>
+#include <linux/ioctl.h>
#include <linux/tty.h>
+#include <linux/tty_flip.h>
#include <linux/module.h>
#include <linux/usb.h>
#include <linux/usb/serial.h>
#include <linux/serial.h>
+#include <linux/serial_reg.h>
#include <linux/uaccess.h>
-
+#include <linux/mutex.h>
+#include <linux/spinlock.h>
static int debug;
+/*
+ * Version information
+ */
+
+#define DRIVER_VERSION "v0.5"
+#define DRIVER_AUTHOR "Bart Hartgers <bart.hartgers+ark3116@gmail.com>"
+#define DRIVER_DESC "USB ARK3116 serial/IrDA driver"
+#define DRIVER_DEV_DESC "ARK3116 RS232/IrDA"
+#define DRIVER_NAME "ark3116"
+
+/* usb timeout of 1 second */
+#define ARK_TIMEOUT (1*HZ)
static struct usb_device_id id_table [] = {
{ USB_DEVICE(0x6547, 0x0232) },
@@ -45,118 +66,152 @@ static int is_irda(struct usb_serial *serial)
return 0;
}
-static inline void ARK3116_SND(struct usb_serial *serial, int seq,
- __u8 request, __u8 requesttype,
- __u16 value, __u16 index)
+struct ark3116_private {
+ wait_queue_head_t delta_msr_wait;
+ struct async_icount icount;
+ int irda; /* 1 for irda device */
+
+ /* protects hw register updates */
+ struct mutex hw_lock;
+
+ int quot; /* baudrate divisor */
+ __u32 lcr; /* line control register value */
+ __u32 hcr; /* handshake control register (0x8)
+ * value */
+ __u32 mcr; /* modem contol register value */
+
+ /* protects the status values below */
+ spinlock_t status_lock;
+ __u32 msr; /* modem status register value */
+ __u32 lsr; /* line status register value */
+};
+
+static int ark3116_write_reg(struct usb_serial *serial,
+ unsigned reg, __u8 val)
{
int result;
+ /* 0xfe 0x40 are magic values taken from original driver */
result = usb_control_msg(serial->dev,
usb_sndctrlpipe(serial->dev, 0),
- request, requesttype, value, index,
- NULL, 0x00, 1000);
- dbg("%03d > ok", seq);
+ 0xfe, 0x40, val, reg,
+ NULL, 0, ARK_TIMEOUT);
+ return result;
}
-static inline void ARK3116_RCV(struct usb_serial *serial, int seq,
- __u8 request, __u8 requesttype,
- __u16 value, __u16 index, __u8 expected,
- char *buf)
+static int ark3116_read_reg(struct usb_serial *serial,
+ unsigned reg, unsigned char *buf)
{
int result;
+ /* 0xfe 0xc0 are magic values taken from original driver */
result = usb_control_msg(serial->dev,
usb_rcvctrlpipe(serial->dev, 0),
- request, requesttype, value, index,
- buf, 0x0000001, 1000);
- if (result)
- dbg("%03d < %d bytes [0x%02X]", seq, result,
- ((unsigned char *)buf)[0]);
+ 0xfe, 0xc0, 0, reg,
+ buf, 1, ARK_TIMEOUT);
+ if (result < 0)
+ return result;
else
- dbg("%03d < 0 bytes", seq);
+ return buf[0];
}
-static inline void ARK3116_RCV_QUIET(struct usb_serial *serial,
- __u8 request, __u8 requesttype,
- __u16 value, __u16 index, char *buf)
+static inline int calc_divisor(int bps)
{
- usb_control_msg(serial->dev,
- usb_rcvctrlpipe(serial->dev, 0),
- request, requesttype, value, index,
- buf, 0x0000001, 1000);
+ /* Original ark3116 made some exceptions in rounding here
+ * because windows did the same. Assume that is not really
+ * necessary.
+ * Crystal is 12MHz, probably because of USB, but we divide by 4?
+ */
+ return (12000000 + 2*bps) / (4*bps);
}
static int ark3116_attach(struct usb_serial *serial)
{
- char *buf;
+ struct usb_serial_port *port = serial->port[0];
+ struct ark3116_private *priv;
+
+ /* make sure we have our end-points */
+ if ((serial->num_bulk_in == 0) ||
+ (serial->num_bulk_out == 0) ||
+ (serial->num_interrupt_in == 0)) {
+ dev_err(&serial->dev->dev,
+ "%s - missing endpoint - "
+ "bulk in: %d, bulk out: %d, int in %d\n",
+ KBUILD_MODNAME,
+ serial->num_bulk_in,
+ serial->num_bulk_out,
+ serial->num_interrupt_in);
+ return -EINVAL;
+ }
- buf = kmalloc(1, GFP_KERNEL);
- if (!buf) {
- dbg("error kmalloc -> out of mem?");
+ priv = kzalloc(sizeof(struct ark3116_private),
+ GFP_KERNEL);
+ if (!priv)
return -ENOMEM;
- }
- if (is_irda(serial))
- dbg("IrDA mode");
+ init_waitqueue_head(&priv->delta_msr_wait);
+ mutex_init(&priv->hw_lock);
+ spin_lock_init(&priv->status_lock);
+
+ priv->irda = is_irda(serial);
- /* 3 */
- ARK3116_SND(serial, 3, 0xFE, 0x40, 0x0008, 0x0002);
- ARK3116_SND(serial, 4, 0xFE, 0x40, 0x0008, 0x0001);
- ARK3116_SND(serial, 5, 0xFE, 0x40, 0x0000, 0x0008);
- ARK3116_SND(serial, 6, 0xFE, 0x40, is_irda(serial) ? 0x0001 : 0x0000,
- 0x000B);
+ usb_set_serial_port_data(port, priv);
- if (is_irda(serial)) {
- ARK3116_SND(serial, 1001, 0xFE, 0x40, 0x0000, 0x000C);
- ARK3116_SND(serial, 1002, 0xFE, 0x40, 0x0041, 0x000D);
- ARK3116_SND(serial, 1003, 0xFE, 0x40, 0x0001, 0x000A);
+ /* setup the hardware */
+ ark3116_write_reg(serial, UART_IER, 0);
+ /* disable DMA */
+ ark3116_write_reg(serial, UART_FCR, 0);
+ /* handshake control */
+ priv->hcr = 0;
+ ark3116_write_reg(serial, 0x8 , 0);
+ /* modem control */
+ priv->mcr = 0;
+ ark3116_write_reg(serial, UART_MCR, 0);
+
+ if (!(priv->irda)) {
+ ark3116_write_reg(serial, 0xb , 0);
+ } else {
+ ark3116_write_reg(serial, 0xb , 1);
+ ark3116_write_reg(serial, 0xc , 0);
+ ark3116_write_reg(serial, 0xd , 0x41);
+ ark3116_write_reg(serial, 0xa , 1);
}
- /* <-- seq7 */
- ARK3116_RCV(serial, 7, 0xFE, 0xC0, 0x0000, 0x0003, 0x00, buf);
- ARK3116_SND(serial, 8, 0xFE, 0x40, 0x0080, 0x0003);
- ARK3116_SND(serial, 9, 0xFE, 0x40, 0x001A, 0x0000);
- ARK3116_SND(serial, 10, 0xFE, 0x40, 0x0000, 0x0001);
- ARK3116_SND(serial, 11, 0xFE, 0x40, 0x0000, 0x0003);
-
- /* <-- seq12 */
- ARK3116_RCV(serial, 12, 0xFE, 0xC0, 0x0000, 0x0004, 0x00, buf);
- ARK3116_SND(serial, 13, 0xFE, 0x40, 0x0000, 0x0004);
-
- /* 14 */
- ARK3116_RCV(serial, 14, 0xFE, 0xC0, 0x0000, 0x0004, 0x00, buf);
- ARK3116_SND(serial, 15, 0xFE, 0x40, 0x0000, 0x0004);
-
- /* 16 */
- ARK3116_RCV(serial, 16, 0xFE, 0xC0, 0x0000, 0x0004, 0x00, buf);
- /* --> seq17 */
- ARK3116_SND(serial, 17, 0xFE, 0x40, 0x0001, 0x0004);
-
- /* <-- seq18 */
- ARK3116_RCV(serial, 18, 0xFE, 0xC0, 0x0000, 0x0004, 0x01, buf);
-
- /* --> seq19 */
- ARK3116_SND(serial, 19, 0xFE, 0x40, 0x0003, 0x0004);
-
- /* <-- seq20 */
- /* seems like serial port status info (RTS, CTS, ...) */
- /* returns modem control line status?! */
- ARK3116_RCV(serial, 20, 0xFE, 0xC0, 0x0000, 0x0006, 0xFF, buf);
-
- /* set 9600 baud & do some init?! */
- ARK3116_SND(serial, 147, 0xFE, 0x40, 0x0083, 0x0003);
- ARK3116_SND(serial, 148, 0xFE, 0x40, 0x0038, 0x0000);
- ARK3116_SND(serial, 149, 0xFE, 0x40, 0x0001, 0x0001);
- if (is_irda(serial))
- ARK3116_SND(serial, 1004, 0xFE, 0x40, 0x0000, 0x0009);
- ARK3116_SND(serial, 150, 0xFE, 0x40, 0x0003, 0x0003);
- ARK3116_RCV(serial, 151, 0xFE, 0xC0, 0x0000, 0x0004, 0x03, buf);
- ARK3116_SND(serial, 152, 0xFE, 0x40, 0x0000, 0x0003);
- ARK3116_RCV(serial, 153, 0xFE, 0xC0, 0x0000, 0x0003, 0x00, buf);
- ARK3116_SND(serial, 154, 0xFE, 0x40, 0x0003, 0x0003);
+ /* setup baudrate */
+ ark3116_write_reg(serial, UART_LCR, UART_LCR_DLAB);
- kfree(buf);
+ /* setup for 9600 8N1 */
+ priv->quot = calc_divisor(9600);
+ ark3116_write_reg(serial, UART_DLL, priv->quot & 0xff);
+ ark3116_write_reg(serial, UART_DLM, (priv->quot>>8) & 0xff);
+
+ priv->lcr = UART_LCR_WLEN8;
+ ark3116_write_reg(serial, UART_LCR, UART_LCR_WLEN8);
+
+ ark3116_write_reg(serial, 0xe, 0);
+
+ if (priv->irda)
+ ark3116_write_reg(serial, 0x9, 0);
+
+ dev_info(&serial->dev->dev,
+ "%s using %s mode\n",
+ KBUILD_MODNAME,
+ priv->irda ? "IrDA" : "RS232");
return 0;
}
+static void ark3116_release(struct usb_serial *serial)
+{
+ struct usb_serial_port *port = serial->port[0];
+ struct ark3116_private *priv = usb_get_serial_port_data(port);
+
+ /* device is closed, so URBs and DMA should be down */
+
+ usb_set_serial_port_data(port, NULL);
+
+ mutex_destroy(&priv->hw_lock);
+
+ kfree(priv);
+}
+
static void ark3116_init_termios(struct tty_struct *tty)
{
struct ktermios *termios = tty->termios;
@@ -172,200 +227,189 @@ static void ark3116_set_termios(struct tty_struct *tty,
struct ktermios *old_termios)
{
struct usb_serial *serial = port->serial;
+ struct ark3116_private *priv = usb_get_serial_port_data(port);
struct ktermios *termios = tty->termios;
unsigned int cflag = termios->c_cflag;
- int baud;
- int ark3116_baud;
- char *buf;
- char config;
-
- config = 0;
-
- dbg("%s - port %d", __func__, port->number);
+ int bps = tty_get_baud_rate(tty);
+ int quot;
+ __u8 lcr, hcr, eval;
+
+ /* set data bit count */
+ switch (cflag & CSIZE) {
+ case CS5:
+ lcr = UART_LCR_WLEN5;
+ break;
+ case CS6:
+ lcr = UART_LCR_WLEN6;
+ break;
+ case CS7:
+ lcr = UART_LCR_WLEN7;
+ break;
+ default:
+ case CS8:
+ lcr = UART_LCR_WLEN8;
+ break;
+ }
+ if (cflag & CSTOPB)
+ lcr |= UART_LCR_STOP;
+ if (cflag & PARENB)
+ lcr |= UART_LCR_PARITY;
+ if (!(cflag & PARODD))
+ lcr |= UART_LCR_EPAR;
+#ifdef CMSPAR
+ if (cflag & CMSPAR)
+ lcr |= UART_LCR_SPAR;
+#endif
+ /* handshake control */
+ hcr = (cflag & CRTSCTS) ? 0x03 : 0x00;
+
+ /* calc baudrate */
+ dbg("%s - setting bps to %d", __func__, bps);
+ eval = 0;
+ switch (bps) {
+ case 0:
+ quot = calc_divisor(9600);
+ break;
+ default:
+ if ((bps < 75) || (bps > 3000000))
+ bps = 9600;
+ quot = calc_divisor(bps);
+ break;
+ case 460800:
+ eval = 1;
+ quot = calc_divisor(bps);
+ break;
+ case 921600:
+ eval = 2;
+ quot = calc_divisor(bps);
+ break;
+ }
+ /* Update state: synchronize */
+ mutex_lock(&priv->hw_lock);
- cflag = termios->c_cflag;
- termios->c_cflag &= ~(CMSPAR|CRTSCTS);
+ /* keep old LCR_SBC bit */
+ lcr |= (priv->lcr & UART_LCR_SBC);
- buf = kmalloc(1, GFP_KERNEL);
- if (!buf) {
- dbg("error kmalloc");
- *termios = *old_termios;
- return;
- }
+ dbg("%s - setting hcr:0x%02x,lcr:0x%02x,quot:%d",
+ __func__, hcr, lcr, quot);
- /* set data bit count (8/7/6/5) */
- if (cflag & CSIZE) {
- switch (cflag & CSIZE) {
- case CS5:
- config |= 0x00;
- dbg("setting CS5");
- break;
- case CS6:
- config |= 0x01;
- dbg("setting CS6");
- break;
- case CS7:
- config |= 0x02;
- dbg("setting CS7");
- break;
- default:
- dbg("CSIZE was set but not CS5-CS8, using CS8!");
- /* fall through */
- case CS8:
- config |= 0x03;
- dbg("setting CS8");
- break;
- }
+ /* handshake control */
+ if (priv->hcr != hcr) {
+ priv->hcr = hcr;
+ ark3116_write_reg(serial, 0x8, hcr);
}
- /* set parity (NONE/EVEN/ODD) */
- if (cflag & PARENB) {
- if (cflag & PARODD) {
- config |= 0x08;
- dbg("setting parity to ODD");
- } else {
- config |= 0x18;
- dbg("setting parity to EVEN");
- }
- } else {
- dbg("setting parity to NONE");
+ /* baudrate */
+ if (priv->quot != quot) {
+ priv->quot = quot;
+ priv->lcr = lcr; /* need to write lcr anyway */
+
+ /* disable DMA since transmit/receive is
+ * shadowed by UART_DLL
+ */
+ ark3116_write_reg(serial, UART_FCR, 0);
+
+ ark3116_write_reg(serial, UART_LCR,
+ lcr|UART_LCR_DLAB);
+ ark3116_write_reg(serial, UART_DLL, quot & 0xff);
+ ark3116_write_reg(serial, UART_DLM, (quot>>8) & 0xff);
+
+ /* restore lcr */
+ ark3116_write_reg(serial, UART_LCR, lcr);
+ /* magic baudrate thingy: not sure what it does,
+ * but windows does this as well.
+ */
+ ark3116_write_reg(serial, 0xe, eval);
+
+ /* enable DMA */
+ ark3116_write_reg(serial, UART_FCR, UART_FCR_DMA_SELECT);
+ } else if (priv->lcr != lcr) {
+ priv->lcr = lcr;
+ ark3116_write_reg(serial, UART_LCR, lcr);
}
- /* set stop bit (1/2) */
- if (cflag & CSTOPB) {
- config |= 0x04;
- dbg("setting 2 stop bits");
- } else {
- dbg("setting 1 stop bit");
- }
+ mutex_unlock(&priv->hw_lock);
- /* set baudrate */
- baud = tty_get_baud_rate(tty);
-
- switch (baud) {
- case 75:
- case 150:
- case 300:
- case 600:
- case 1200:
- case 1800:
- case 2400:
- case 4800:
- case 9600:
- case 19200:
- case 38400:
- case 57600:
- case 115200:
- case 230400:
- case 460800:
- /* Report the resulting rate back to the caller */
- tty_encode_baud_rate(tty, baud, baud);
- break;
- /* set 9600 as default (if given baudrate is invalid for example) */
- default:
- tty_encode_baud_rate(tty, 9600, 9600);
- case 0:
- baud = 9600;
+ /* check for software flow control */
+ if (I_IXOFF(tty) || I_IXON(tty)) {
+ dev_warn(&serial->dev->dev,
+ "%s: don't know how to do software flow control\n",
+ KBUILD_MODNAME);
}
- /*
- * found by try'n'error, be careful, maybe there are other options
- * for multiplicator etc! (3.5 for example)
- */
- if (baud == 460800)
- /* strange, for 460800 the formula is wrong
- * if using round() then 9600baud is wrong) */
- ark3116_baud = 7;
- else
- ark3116_baud = 3000000 / baud;
-
- /* ? */
- ARK3116_RCV(serial, 0, 0xFE, 0xC0, 0x0000, 0x0003, 0x03, buf);
-
- /* offset = buf[0]; */
- /* offset = 0x03; */
- /* dbg("using 0x%04X as target for 0x0003:", 0x0080 + offset); */
-
- /* set baudrate */
- dbg("setting baudrate to %d (->reg=%d)", baud, ark3116_baud);
- ARK3116_SND(serial, 147, 0xFE, 0x40, 0x0083, 0x0003);
- ARK3116_SND(serial, 148, 0xFE, 0x40,
- (ark3116_baud & 0x00FF), 0x0000);
- ARK3116_SND(serial, 149, 0xFE, 0x40,
- (ark3116_baud & 0xFF00) >> 8, 0x0001);
- ARK3116_SND(serial, 150, 0xFE, 0x40, 0x0003, 0x0003);
-
- /* ? */
- ARK3116_RCV(serial, 151, 0xFE, 0xC0, 0x0000, 0x0004, 0x03, buf);
- ARK3116_SND(serial, 152, 0xFE, 0x40, 0x0000, 0x0003);
-
- /* set data bit count, stop bit count & parity: */
- dbg("updating bit count, stop bit or parity (cfg=0x%02X)", config);
- ARK3116_RCV(serial, 153, 0xFE, 0xC0, 0x0000, 0x0003, 0x00, buf);
- ARK3116_SND(serial, 154, 0xFE, 0x40, config, 0x0003);
+ /* Don't rewrite B0 */
+ if (tty_termios_baud_rate(termios))
+ tty_termios_encode_baud_rate(termios, bps, bps);
+}
- if (cflag & CRTSCTS)
- dbg("CRTSCTS not supported by chipset?!");
+static void ark3116_close(struct usb_serial_port *port)
+{
+ struct usb_serial *serial = port->serial;
- /* TEST ARK3116_SND(154, 0xFE, 0x40, 0xFFFF, 0x0006); */
+ if (serial->dev) {
+ /* disable DMA */
+ ark3116_write_reg(serial, UART_FCR, 0);
- kfree(buf);
+ /* deactivate interrupts */
+ ark3116_write_reg(serial, UART_IER, 0);
- return;
+ /* shutdown any bulk reads that might be going on */
+ if (serial->num_bulk_out)
+ usb_kill_urb(port->write_urb);
+ if (serial->num_bulk_in)
+ usb_kill_urb(port->read_urb);
+ if (serial->num_interrupt_in)
+ usb_kill_urb(port->interrupt_in_urb);
+ }
}
static int ark3116_open(struct tty_struct *tty, struct usb_serial_port *port)
{
- struct ktermios tmp_termios;
+ struct ark3116_private *priv = usb_get_serial_port_data(port);
struct usb_serial *serial = port->serial;
- char *buf;
- int result = 0;
-
- dbg("%s - port %d", __func__, port->number);
+ unsigned char *buf;
+ int result;
buf = kmalloc(1, GFP_KERNEL);
- if (!buf) {
- dbg("error kmalloc -> out of mem?");
+ if (buf == NULL)
return -ENOMEM;
- }
result = usb_serial_generic_open(tty, port);
- if (result)
+ if (result) {
+ dbg("%s - usb_serial_generic_open failed: %d",
+ __func__, result);
goto err_out;
+ }
- /* open */
- ARK3116_RCV(serial, 111, 0xFE, 0xC0, 0x0000, 0x0003, 0x02, buf);
-
- ARK3116_SND(serial, 112, 0xFE, 0x40, 0x0082, 0x0003);
- ARK3116_SND(serial, 113, 0xFE, 0x40, 0x001A, 0x0000);
- ARK3116_SND(serial, 114, 0xFE, 0x40, 0x0000, 0x0001);
- ARK3116_SND(serial, 115, 0xFE, 0x40, 0x0002, 0x0003);
-
- ARK3116_RCV(serial, 116, 0xFE, 0xC0, 0x0000, 0x0004, 0x03, buf);
- ARK3116_SND(serial, 117, 0xFE, 0x40, 0x0002, 0x0004);
-
- ARK3116_RCV(serial, 118, 0xFE, 0xC0, 0x0000, 0x0004, 0x02, buf);
- ARK3116_SND(serial, 119, 0xFE, 0x40, 0x0000, 0x0004);
-
- ARK3116_RCV(serial, 120, 0xFE, 0xC0, 0x0000, 0x0004, 0x00, buf);
+ /* setup termios */
+ if (tty)
+ ark3116_set_termios(tty, port, NULL);
- ARK3116_SND(serial, 121, 0xFE, 0x40, 0x0001, 0x0004);
+ /* remove any data still left: also clears error state */
+ ark3116_read_reg(serial, UART_RX, buf);
- ARK3116_RCV(serial, 122, 0xFE, 0xC0, 0x0000, 0x0004, 0x01, buf);
+ /* read modem status */
+ priv->msr = ark3116_read_reg(serial, UART_MSR, buf);
+ /* read line status */
+ priv->lsr = ark3116_read_reg(serial, UART_LSR, buf);
- ARK3116_SND(serial, 123, 0xFE, 0x40, 0x0003, 0x0004);
+ result = usb_submit_urb(port->interrupt_in_urb, GFP_KERNEL);
+ if (result) {
+ dev_err(&port->dev, "submit irq_in urb failed %d\n",
+ result);
+ ark3116_close(port);
+ goto err_out;
+ }
- /* returns different values (control lines?!) */
- ARK3116_RCV(serial, 124, 0xFE, 0xC0, 0x0000, 0x0006, 0xFF, buf);
+ /* activate interrupts */
+ ark3116_write_reg(port->serial, UART_IER, UART_IER_MSI|UART_IER_RLSI);
- /* initialise termios */
- if (tty)
- ark3116_set_termios(tty, port, &tmp_termios);
+ /* enable DMA */
+ ark3116_write_reg(port->serial, UART_FCR, UART_FCR_DMA_SELECT);
err_out:
kfree(buf);
-
return result;
}
@@ -373,6 +417,7 @@ static int ark3116_ioctl(struct tty_struct *tty, struct file *file,
unsigned int cmd, unsigned long arg)
{
struct usb_serial_port *port = tty->driver_data;
+ struct ark3116_private *priv = usb_get_serial_port_data(port);
struct serial_struct serstruct;
void __user *user_arg = (void __user *)arg;
@@ -394,9 +439,48 @@ static int ark3116_ioctl(struct tty_struct *tty, struct file *file,
if (copy_from_user(&serstruct, user_arg, sizeof(serstruct)))
return -EFAULT;
return 0;
- default:
- dbg("%s cmd 0x%04x not supported", __func__, cmd);
+ case TIOCMIWAIT:
+ for (;;) {
+ struct async_icount prev = priv->icount;
+ interruptible_sleep_on(&priv->delta_msr_wait);
+ /* see if a signal did it */
+ if (signal_pending(current))
+ return -ERESTARTSYS;
+ if ((prev.rng == priv->icount.rng) &&
+ (prev.dsr == priv->icount.dsr) &&
+ (prev.dcd == priv->icount.dcd) &&
+ (prev.cts == priv->icount.cts))
+ return -EIO;
+ if ((arg & TIOCM_RNG &&
+ (prev.rng != priv->icount.rng)) ||
+ (arg & TIOCM_DSR &&
+ (prev.dsr != priv->icount.dsr)) ||
+ (arg & TIOCM_CD &&
+ (prev.dcd != priv->icount.dcd)) ||
+ (arg & TIOCM_CTS &&
+ (prev.cts != priv->icount.cts)))
+ return 0;
+ }
break;
+ case TIOCGICOUNT: {
+ struct serial_icounter_struct icount;
+ struct async_icount cnow = priv->icount;
+ memset(&icount, 0, sizeof(icount));
+ icount.cts = cnow.cts;
+ icount.dsr = cnow.dsr;
+ icount.rng = cnow.rng;
+ icount.dcd = cnow.dcd;
+ icount.rx = cnow.rx;
+ icount.tx = cnow.tx;
+ icount.frame = cnow.frame;
+ icount.overrun = cnow.overrun;
+ icount.parity = cnow.parity;
+ icount.brk = cnow.brk;
+ icount.buf_overrun = cnow.buf_overrun;
+ if (copy_to_user(user_arg, &icount, sizeof(icount)))
+ return -EFAULT;
+ return 0;
+ }
}
return -ENOIOCTLCMD;
@@ -405,32 +489,273 @@ static int ark3116_ioctl(struct tty_struct *tty, struct file *file,
static int ark3116_tiocmget(struct tty_struct *tty, struct file *file)
{
struct usb_serial_port *port = tty->driver_data;
- struct usb_serial *serial = port->serial;
- char *buf;
- char temp;
+ struct ark3116_private *priv = usb_get_serial_port_data(port);
+ __u32 status;
+ __u32 ctrl;
+ unsigned long flags;
+
+ mutex_lock(&priv->hw_lock);
+ ctrl = priv->mcr;
+ mutex_unlock(&priv->hw_lock);
+
+ spin_lock_irqsave(&priv->status_lock, flags);
+ status = priv->msr;
+ spin_unlock_irqrestore(&priv->status_lock, flags);
+
+ return (status & UART_MSR_DSR ? TIOCM_DSR : 0) |
+ (status & UART_MSR_CTS ? TIOCM_CTS : 0) |
+ (status & UART_MSR_RI ? TIOCM_RI : 0) |
+ (status & UART_MSR_DCD ? TIOCM_CD : 0) |
+ (ctrl & UART_MCR_DTR ? TIOCM_DTR : 0) |
+ (ctrl & UART_MCR_RTS ? TIOCM_RTS : 0) |
+ (ctrl & UART_MCR_OUT1 ? TIOCM_OUT1 : 0) |
+ (ctrl & UART_MCR_OUT2 ? TIOCM_OUT2 : 0);
+}
- /* seems like serial port status info (RTS, CTS, ...) is stored
- * in reg(?) 0x0006
- * pcb connection point 11 = GND -> sets bit4 of response
- * pcb connection point 7 = GND -> sets bit6 of response
+static int ark3116_tiocmset(struct tty_struct *tty, struct file *file,
+ unsigned set, unsigned clr)
+{
+ struct usb_serial_port *port = tty->driver_data;
+ struct ark3116_private *priv = usb_get_serial_port_data(port);
+
+ /* we need to take the mutex here, to make sure that the value
+ * in priv->mcr is actually the one that is in the hardware
*/
- buf = kmalloc(1, GFP_KERNEL);
- if (!buf) {
- dbg("error kmalloc");
- return -ENOMEM;
+ mutex_lock(&priv->hw_lock);
+
+ if (set & TIOCM_RTS)
+ priv->mcr |= UART_MCR_RTS;
+ if (set & TIOCM_DTR)
+ priv->mcr |= UART_MCR_DTR;
+ if (set & TIOCM_OUT1)
+ priv->mcr |= UART_MCR_OUT1;
+ if (set & TIOCM_OUT2)
+ priv->mcr |= UART_MCR_OUT2;
+ if (clr & TIOCM_RTS)
+ priv->mcr &= ~UART_MCR_RTS;
+ if (clr & TIOCM_DTR)
+ priv->mcr &= ~UART_MCR_DTR;
+ if (clr & TIOCM_OUT1)
+ priv->mcr &= ~UART_MCR_OUT1;
+ if (clr & TIOCM_OUT2)
+ priv->mcr &= ~UART_MCR_OUT2;
+
+ ark3116_write_reg(port->serial, UART_MCR, priv->mcr);
+
+ mutex_unlock(&priv->hw_lock);
+
+ return 0;
+}
+
+static void ark3116_break_ctl(struct tty_struct *tty, int break_state)
+{
+ struct usb_serial_port *port = tty->driver_data;
+ struct ark3116_private *priv = usb_get_serial_port_data(port);
+
+ /* LCR is also used for other things: protect access */
+ mutex_lock(&priv->hw_lock);
+
+ if (break_state)
+ priv->lcr |= UART_LCR_SBC;
+ else
+ priv->lcr &= ~UART_LCR_SBC;
+
+ ark3116_write_reg(port->serial, UART_LCR, priv->lcr);
+
+ mutex_unlock(&priv->hw_lock);
+}
+
+static void ark3116_update_msr(struct usb_serial_port *port, __u8 msr)
+{
+ struct ark3116_private *priv = usb_get_serial_port_data(port);
+ unsigned long flags;
+
+ spin_lock_irqsave(&priv->status_lock, flags);
+ priv->msr = msr;
+ spin_unlock_irqrestore(&priv->status_lock, flags);
+
+ if (msr & UART_MSR_ANY_DELTA) {
+ /* update input line counters */
+ if (msr & UART_MSR_DCTS)
+ priv->icount.cts++;
+ if (msr & UART_MSR_DDSR)
+ priv->icount.dsr++;
+ if (msr & UART_MSR_DDCD)
+ priv->icount.dcd++;
+ if (msr & UART_MSR_TERI)
+ priv->icount.rng++;
+ wake_up_interruptible(&priv->delta_msr_wait);
}
+}
- /* read register */
- ARK3116_RCV_QUIET(serial, 0xFE, 0xC0, 0x0000, 0x0006, buf);
- temp = buf[0];
- kfree(buf);
+static void ark3116_update_lsr(struct usb_serial_port *port, __u8 lsr)
+{
+ struct ark3116_private *priv = usb_get_serial_port_data(port);
+ unsigned long flags;
+
+ spin_lock_irqsave(&priv->status_lock, flags);
+ /* combine bits */
+ priv->lsr |= lsr;
+ spin_unlock_irqrestore(&priv->status_lock, flags);
+
+ if (lsr&UART_LSR_BRK_ERROR_BITS) {
+ if (lsr & UART_LSR_BI)
+ priv->icount.brk++;
+ if (lsr & UART_LSR_FE)
+ priv->icount.frame++;
+ if (lsr & UART_LSR_PE)
+ priv->icount.parity++;
+ if (lsr & UART_LSR_OE)
+ priv->icount.overrun++;
+ }
+}
- /* i do not really know if bit4=CTS and bit6=DSR... just a
- * quick guess!
- */
- return (temp & (1<<4) ? TIOCM_CTS : 0)
- | (temp & (1<<6) ? TIOCM_DSR : 0);
+static void ark3116_read_int_callback(struct urb *urb)
+{
+ struct usb_serial_port *port = urb->context;
+ int status = urb->status;
+ const __u8 *data = urb->transfer_buffer;
+ int result;
+
+ switch (status) {
+ case -ECONNRESET:
+ case -ENOENT:
+ case -ESHUTDOWN:
+ /* this urb is terminated, clean up */
+ dbg("%s - urb shutting down with status: %d",
+ __func__, status);
+ return;
+ default:
+ dbg("%s - nonzero urb status received: %d",
+ __func__, status);
+ break;
+ case 0: /* success */
+ /* discovered this by trail and error... */
+ if ((urb->actual_length == 4) && (data[0] == 0xe8)) {
+ const __u8 id = data[1]&UART_IIR_ID;
+ dbg("%s: iir=%02x", __func__, data[1]);
+ if (id == UART_IIR_MSI) {
+ dbg("%s: msr=%02x", __func__, data[3]);
+ ark3116_update_msr(port, data[3]);
+ break;
+ } else if (id == UART_IIR_RLSI) {
+ dbg("%s: lsr=%02x", __func__, data[2]);
+ ark3116_update_lsr(port, data[2]);
+ break;
+ }
+ }
+ /*
+ * Not sure what this data meant...
+ */
+ usb_serial_debug_data(debug, &port->dev,
+ __func__,
+ urb->actual_length,
+ urb->transfer_buffer);
+ break;
+ }
+
+ result = usb_submit_urb(urb, GFP_ATOMIC);
+ if (result)
+ dev_err(&urb->dev->dev,
+ "%s - Error %d submitting interrupt urb\n",
+ __func__, result);
+}
+
+
+/* Data comes in via the bulk (data) URB, erors/interrupts via the int URB.
+ * This means that we cannot be sure which data byte has an associated error
+ * condition, so we report an error for all data in the next bulk read.
+ *
+ * Actually, there might even be a window between the bulk data leaving the
+ * ark and reading/resetting the lsr in the read_bulk_callback where an
+ * interrupt for the next data block could come in.
+ * Without somekind of ordering on the ark, we would have to report the
+ * error for the next block of data as well...
+ * For now, let's pretend this can't happen.
+ */
+
+static void send_to_tty(struct tty_struct *tty,
+ const unsigned char *chars,
+ size_t size, char flag)
+{
+ if (size == 0)
+ return;
+ if (flag == TTY_NORMAL) {
+ tty_insert_flip_string(tty, chars, size);
+ } else {
+ int i;
+ for (i = 0; i < size; ++i)
+ tty_insert_flip_char(tty, chars[i], flag);
+ }
+}
+
+static void ark3116_read_bulk_callback(struct urb *urb)
+{
+ struct usb_serial_port *port = urb->context;
+ struct ark3116_private *priv = usb_get_serial_port_data(port);
+ const __u8 *data = urb->transfer_buffer;
+ int status = urb->status;
+ struct tty_struct *tty;
+ unsigned long flags;
+ int result;
+ char flag;
+ __u32 lsr;
+
+ switch (status) {
+ case -ECONNRESET:
+ case -ENOENT:
+ case -ESHUTDOWN:
+ /* this urb is terminated, clean up */
+ dbg("%s - urb shutting down with status: %d",
+ __func__, status);
+ return;
+ default:
+ dbg("%s - nonzero urb status received: %d",
+ __func__, status);
+ break;
+ case 0: /* success */
+
+ spin_lock_irqsave(&priv->status_lock, flags);
+ lsr = priv->lsr;
+ /* clear error bits */
+ priv->lsr &= ~UART_LSR_BRK_ERROR_BITS;
+ spin_unlock_irqrestore(&priv->status_lock, flags);
+
+ if (unlikely(lsr & UART_LSR_BI))
+ flag = TTY_BREAK;
+ else if (unlikely(lsr & UART_LSR_PE))
+ flag = TTY_PARITY;
+ else if (unlikely(lsr & UART_LSR_FE))
+ flag = TTY_FRAME;
+ else
+ flag = TTY_NORMAL;
+
+ tty = tty_port_tty_get(&port->port);
+ if (tty) {
+ tty_buffer_request_room(tty, urb->actual_length + 1);
+ /* overrun is special, not associated with a char */
+ if (unlikely(lsr & UART_LSR_OE))
+ tty_insert_flip_char(tty, 0, TTY_OVERRUN);
+ send_to_tty(tty, data, urb->actual_length, flag);
+ tty_flip_buffer_push(tty);
+ tty_kref_put(tty);
+ }
+
+ /* Throttle the device if requested by tty */
+ spin_lock_irqsave(&port->lock, flags);
+ port->throttled = port->throttle_req;
+ if (port->throttled) {
+ spin_unlock_irqrestore(&port->lock, flags);
+ return;
+ } else
+ spin_unlock_irqrestore(&port->lock, flags);
+ }
+ /* Continue reading from device */
+ result = usb_submit_urb(urb, GFP_ATOMIC);
+ if (result)
+ dev_err(&urb->dev->dev, "%s - failed resubmitting"
+ " read urb, error %d\n", __func__, result);
}
static struct usb_driver ark3116_driver = {
@@ -450,11 +775,17 @@ static struct usb_serial_driver ark3116_device = {
.usb_driver = &ark3116_driver,
.num_ports = 1,
.attach = ark3116_attach,
+ .release = ark3116_release,
.set_termios = ark3116_set_termios,
.init_termios = ark3116_init_termios,
.ioctl = ark3116_ioctl,
.tiocmget = ark3116_tiocmget,
+ .tiocmset = ark3116_tiocmset,
.open = ark3116_open,
+ .close = ark3116_close,
+ .break_ctl = ark3116_break_ctl,
+ .read_int_callback = ark3116_read_int_callback,
+ .read_bulk_callback = ark3116_read_bulk_callback,
};
static int __init ark3116_init(void)
@@ -465,7 +796,12 @@ static int __init ark3116_init(void)
if (retval)
return retval;
retval = usb_register(&ark3116_driver);
- if (retval)
+ if (retval == 0) {
+ printk(KERN_INFO "%s:"
+ DRIVER_VERSION ":"
+ DRIVER_DESC "\n",
+ KBUILD_MODNAME);
+ } else
usb_serial_deregister(&ark3116_device);
return retval;
}
@@ -480,6 +816,109 @@ module_init(ark3116_init);
module_exit(ark3116_exit);
MODULE_LICENSE("GPL");
+MODULE_AUTHOR(DRIVER_AUTHOR);
+MODULE_DESCRIPTION(DRIVER_DESC);
+
module_param(debug, bool, S_IRUGO | S_IWUSR);
-MODULE_PARM_DESC(debug, "Debug enabled or not");
+MODULE_PARM_DESC(debug, "Enable debug");
+/*
+ * The following describes what I learned from studying the old
+ * ark3116.c driver, disassembling the windows driver, and some lucky
+ * guesses. Since I do not have any datasheet or other
+ * documentation, inaccuracies are almost guaranteed.
+ *
+ * Some specs for the ARK3116 can be found here:
+ * http://web.archive.org/web/20060318000438/
+ * www.arkmicro.com/en/products/view.php?id=10
+ * On that page, 2 GPIO pins are mentioned: I assume these are the
+ * OUT1 and OUT2 pins of the UART, so I added support for those
+ * through the MCR. Since the pins are not available on my hardware,
+ * I could not verify this.
+ * Also, it states there is "on-chip hardware flow control". I have
+ * discovered how to enable that. Unfortunately, I do not know how to
+ * enable XON/XOFF (software) flow control, which would need support
+ * from the chip as well to work. Because of the wording on the web
+ * page there is a real possibility the chip simply does not support
+ * software flow control.
+ *
+ * I got my ark3116 as part of a mobile phone adapter cable. On the
+ * PCB, the following numbered contacts are present:
+ *
+ * 1:- +5V
+ * 2:o DTR
+ * 3:i RX
+ * 4:i DCD
+ * 5:o RTS
+ * 6:o TX
+ * 7:i RI
+ * 8:i DSR
+ * 10:- 0V
+ * 11:i CTS
+ *
+ * On my chip, all signals seem to be 3.3V, but 5V tolerant. But that
+ * may be different for the one you have ;-).
+ *
+ * The windows driver limits the registers to 0-F, so I assume there
+ * are actually 16 present on the device.
+ *
+ * On an UART interrupt, 4 bytes of data come in on the interrupt
+ * endpoint. The bytes are 0xe8 IIR LSR MSR.
+ *
+ * The baudrate seems to be generated from the 12MHz crystal, using
+ * 4-times subsampling. So quot=12e6/(4*baud). Also see description
+ * of register E.
+ *
+ * Registers 0-7:
+ * These seem to be the same as for a regular 16450. The FCR is set
+ * to UART_FCR_DMA_SELECT (0x8), I guess to enable transfers between
+ * the UART and the USB bridge/DMA engine.
+ *
+ * Register 8:
+ * By trial and error, I found out that bit 0 enables hardware CTS,
+ * stopping TX when CTS is +5V. Bit 1 does the same for RTS, making
+ * RTS +5V when the 3116 cannot transfer the data to the USB bus
+ * (verified by disabling the reading URB). Note that as far as I can
+ * tell, the windows driver does NOT use this, so there might be some
+ * hardware bug or something.
+ *
+ * According to a patch provided here
+ * (http://lkml.org/lkml/2009/7/26/56), the ARK3116 can also be used
+ * as an IrDA dongle. Since I do not have such a thing, I could not
+ * investigate that aspect. However, I can speculate ;-).
+ *
+ * - IrDA encodes data differently than RS232. Most likely, one of
+ * the bits in registers 9..E enables the IR ENDEC (encoder/decoder).
+ * - Depending on the IR transceiver, the input and output need to be
+ * inverted, so there are probably bits for that as well.
+ * - IrDA is half-duplex, so there should be a bit for selecting that.
+ *
+ * This still leaves at least two registers unaccounted for. Perhaps
+ * The chip can do XON/XOFF or CRC in HW?
+ *
+ * Register 9:
+ * Set to 0x00 for IrDA, when the baudrate is initialised.
+ *
+ * Register A:
+ * Set to 0x01 for IrDA, at init.
+ *
+ * Register B:
+ * Set to 0x01 for IrDA, 0x00 for RS232, at init.
+ *
+ * Register C:
+ * Set to 00 for IrDA, at init.
+ *
+ * Register D:
+ * Set to 0x41 for IrDA, at init.
+ *
+ * Register E:
+ * Somekind of baudrate override. The windows driver seems to set
+ * this to 0x00 for normal baudrates, 0x01 for 460800, 0x02 for 921600.
+ * Since 460800 and 921600 cannot be obtained by dividing 3MHz by an integer,
+ * it could be somekind of subdivisor thingy.
+ * However,it does not seem to do anything: selecting 921600 (divisor 3,
+ * reg E=2), still gets 1 MHz. I also checked if registers 9, C or F would
+ * work, but they don't.
+ *
+ * Register F: unknown
+ */
diff --git a/drivers/usb/serial/ftdi_sio.c b/drivers/usb/serial/ftdi_sio.c
index ebcc6d0..f99498f 100644
--- a/drivers/usb/serial/ftdi_sio.c
+++ b/drivers/usb/serial/ftdi_sio.c
@@ -598,6 +598,20 @@ static struct usb_device_id id_table_combined [] = {
{ USB_DEVICE(BANDB_VID, BANDB_USOTL4_PID) },
{ USB_DEVICE(BANDB_VID, BANDB_USTL4_PID) },
{ USB_DEVICE(BANDB_VID, BANDB_USO9ML2_PID) },
+ { USB_DEVICE(BANDB_VID, BANDB_USOPTL4_PID) },
+ { USB_DEVICE(BANDB_VID, BANDB_USPTL4_PID) },
+ { USB_DEVICE(BANDB_VID, BANDB_USO9ML2DR_2_PID) },
+ { USB_DEVICE(BANDB_VID, BANDB_USO9ML2DR_PID) },
+ { USB_DEVICE(BANDB_VID, BANDB_USOPTL4DR2_PID) },
+ { USB_DEVICE(BANDB_VID, BANDB_USOPTL4DR_PID) },
+ { USB_DEVICE(BANDB_VID, BANDB_485USB9F_2W_PID) },
+ { USB_DEVICE(BANDB_VID, BANDB_485USB9F_4W_PID) },
+ { USB_DEVICE(BANDB_VID, BANDB_232USB9M_PID) },
+ { USB_DEVICE(BANDB_VID, BANDB_485USBTB_2W_PID) },
+ { USB_DEVICE(BANDB_VID, BANDB_485USBTB_4W_PID) },
+ { USB_DEVICE(BANDB_VID, BANDB_TTL5USB9M_PID) },
+ { USB_DEVICE(BANDB_VID, BANDB_TTL3USB9M_PID) },
+ { USB_DEVICE(BANDB_VID, BANDB_ZZ_PROG1_USB_PID) },
{ USB_DEVICE(FTDI_VID, EVER_ECO_PRO_CDS) },
{ USB_DEVICE(FTDI_VID, FTDI_4N_GALAXY_DE_1_PID) },
{ USB_DEVICE(FTDI_VID, FTDI_4N_GALAXY_DE_2_PID) },
@@ -2195,15 +2209,21 @@ static void ftdi_set_termios(struct tty_struct *tty,
/* Set number of data bits, parity, stop bits */
- termios->c_cflag &= ~CMSPAR;
-
urb_value = 0;
urb_value |= (cflag & CSTOPB ? FTDI_SIO_SET_DATA_STOP_BITS_2 :
FTDI_SIO_SET_DATA_STOP_BITS_1);
- urb_value |= (cflag & PARENB ?
- (cflag & PARODD ? FTDI_SIO_SET_DATA_PARITY_ODD :
- FTDI_SIO_SET_DATA_PARITY_EVEN) :
- FTDI_SIO_SET_DATA_PARITY_NONE);
+ if (cflag & PARENB) {
+ if (cflag & CMSPAR)
+ urb_value |= cflag & PARODD ?
+ FTDI_SIO_SET_DATA_PARITY_MARK :
+ FTDI_SIO_SET_DATA_PARITY_SPACE;
+ else
+ urb_value |= cflag & PARODD ?
+ FTDI_SIO_SET_DATA_PARITY_ODD :
+ FTDI_SIO_SET_DATA_PARITY_EVEN;
+ } else {
+ urb_value |= FTDI_SIO_SET_DATA_PARITY_NONE;
+ }
if (cflag & CSIZE) {
switch (cflag & CSIZE) {
case CS5: urb_value |= 5; dbg("Setting CS5"); break;
diff --git a/drivers/usb/serial/ftdi_sio.h b/drivers/usb/serial/ftdi_sio.h
index 6f31e0d..4586a24 100644
--- a/drivers/usb/serial/ftdi_sio.h
+++ b/drivers/usb/serial/ftdi_sio.h
@@ -662,6 +662,20 @@
#define BANDB_USOTL4_PID 0xAC01 /* USOTL4 Isolated RS-485 Converter */
#define BANDB_USTL4_PID 0xAC02 /* USTL4 RS-485 Converter */
#define BANDB_USO9ML2_PID 0xAC03 /* USO9ML2 Isolated RS-232 Converter */
+#define BANDB_USOPTL4_PID 0xAC11
+#define BANDB_USPTL4_PID 0xAC12
+#define BANDB_USO9ML2DR_2_PID 0xAC16
+#define BANDB_USO9ML2DR_PID 0xAC17
+#define BANDB_USOPTL4DR2_PID 0xAC18 /* USOPTL4R-2 2-port Isolated RS-232 Converter */
+#define BANDB_USOPTL4DR_PID 0xAC19
+#define BANDB_485USB9F_2W_PID 0xAC25
+#define BANDB_485USB9F_4W_PID 0xAC26
+#define BANDB_232USB9M_PID 0xAC27
+#define BANDB_485USBTB_2W_PID 0xAC33
+#define BANDB_485USBTB_4W_PID 0xAC34
+#define BANDB_TTL5USB9M_PID 0xAC49
+#define BANDB_TTL3USB9M_PID 0xAC50
+#define BANDB_ZZ_PROG1_USB_PID 0xBA02
/*
* RM Michaelides CANview USB (http://www.rmcan.com)
diff --git a/drivers/usb/serial/mos7840.c b/drivers/usb/serial/mos7840.c
index f11abf5..485fa9c 100644
--- a/drivers/usb/serial/mos7840.c
+++ b/drivers/usb/serial/mos7840.c
@@ -121,8 +121,14 @@
* moschip_id_table_combined
*/
#define USB_VENDOR_ID_BANDB 0x0856
-#define BANDB_DEVICE_ID_USOPTL4_4 0xAC44
+#define BANDB_DEVICE_ID_USO9ML2_2 0xAC22
+#define BANDB_DEVICE_ID_USO9ML2_4 0xAC24
+#define BANDB_DEVICE_ID_US9ML2_2 0xAC29
+#define BANDB_DEVICE_ID_US9ML2_4 0xAC30
+#define BANDB_DEVICE_ID_USPTL4_2 0xAC31
+#define BANDB_DEVICE_ID_USPTL4_4 0xAC32
#define BANDB_DEVICE_ID_USOPTL4_2 0xAC42
+#define BANDB_DEVICE_ID_USOPTL4_4 0xAC44
/* This driver also supports
* ATEN UC2324 device using Moschip MCS7840
@@ -177,8 +183,14 @@
static struct usb_device_id moschip_port_id_table[] = {
{USB_DEVICE(USB_VENDOR_ID_MOSCHIP, MOSCHIP_DEVICE_ID_7840)},
{USB_DEVICE(USB_VENDOR_ID_MOSCHIP, MOSCHIP_DEVICE_ID_7820)},
- {USB_DEVICE(USB_VENDOR_ID_BANDB, BANDB_DEVICE_ID_USOPTL4_4)},
+ {USB_DEVICE(USB_VENDOR_ID_BANDB, BANDB_DEVICE_ID_USO9ML2_2)},
+ {USB_DEVICE(USB_VENDOR_ID_BANDB, BANDB_DEVICE_ID_USO9ML2_4)},
+ {USB_DEVICE(USB_VENDOR_ID_BANDB, BANDB_DEVICE_ID_US9ML2_2)},
+ {USB_DEVICE(USB_VENDOR_ID_BANDB, BANDB_DEVICE_ID_US9ML2_4)},
+ {USB_DEVICE(USB_VENDOR_ID_BANDB, BANDB_DEVICE_ID_USPTL4_2)},
+ {USB_DEVICE(USB_VENDOR_ID_BANDB, BANDB_DEVICE_ID_USPTL4_4)},
{USB_DEVICE(USB_VENDOR_ID_BANDB, BANDB_DEVICE_ID_USOPTL4_2)},
+ {USB_DEVICE(USB_VENDOR_ID_BANDB, BANDB_DEVICE_ID_USOPTL4_4)},
{USB_DEVICE(USB_VENDOR_ID_ATENINTL, ATENINTL_DEVICE_ID_UC2324)},
{USB_DEVICE(USB_VENDOR_ID_ATENINTL, ATENINTL_DEVICE_ID_UC2322)},
{} /* terminating entry */
@@ -187,8 +199,14 @@ static struct usb_device_id moschip_port_id_table[] = {
static __devinitdata struct usb_device_id moschip_id_table_combined[] = {
{USB_DEVICE(USB_VENDOR_ID_MOSCHIP, MOSCHIP_DEVICE_ID_7840)},
{USB_DEVICE(USB_VENDOR_ID_MOSCHIP, MOSCHIP_DEVICE_ID_7820)},
- {USB_DEVICE(USB_VENDOR_ID_BANDB, BANDB_DEVICE_ID_USOPTL4_4)},
+ {USB_DEVICE(USB_VENDOR_ID_BANDB, BANDB_DEVICE_ID_USO9ML2_2)},
+ {USB_DEVICE(USB_VENDOR_ID_BANDB, BANDB_DEVICE_ID_USO9ML2_4)},
+ {USB_DEVICE(USB_VENDOR_ID_BANDB, BANDB_DEVICE_ID_US9ML2_2)},
+ {USB_DEVICE(USB_VENDOR_ID_BANDB, BANDB_DEVICE_ID_US9ML2_4)},
+ {USB_DEVICE(USB_VENDOR_ID_BANDB, BANDB_DEVICE_ID_USPTL4_2)},
+ {USB_DEVICE(USB_VENDOR_ID_BANDB, BANDB_DEVICE_ID_USPTL4_4)},
{USB_DEVICE(USB_VENDOR_ID_BANDB, BANDB_DEVICE_ID_USOPTL4_2)},
+ {USB_DEVICE(USB_VENDOR_ID_BANDB, BANDB_DEVICE_ID_USOPTL4_4)},
{USB_DEVICE(USB_VENDOR_ID_ATENINTL, ATENINTL_DEVICE_ID_UC2324)},
{USB_DEVICE(USB_VENDOR_ID_ATENINTL, ATENINTL_DEVICE_ID_UC2322)},
{} /* terminating entry */
diff --git a/drivers/usb/serial/option.c b/drivers/usb/serial/option.c
index 0577e4b..9a2b903 100644
--- a/drivers/usb/serial/option.c
+++ b/drivers/usb/serial/option.c
@@ -580,12 +580,48 @@ static struct usb_device_id option_ids[] = {
{ USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, 0x0086, 0xff, 0xff, 0xff) },
{ USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, 0x2002, 0xff, 0xff, 0xff) },
{ USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, 0x2003, 0xff, 0xff, 0xff) },
+ { USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, 0x0104, 0xff, 0xff, 0xff) },
+ { USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, 0x0106, 0xff, 0xff, 0xff) },
+ { USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, 0x0108, 0xff, 0xff, 0xff) },
+ { USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, 0x0113, 0xff, 0xff, 0xff) },
+ { USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, 0x0117, 0xff, 0xff, 0xff) },
+ { USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, 0x0118, 0xff, 0xff, 0xff) },
+ { USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, 0x0121, 0xff, 0xff, 0xff) },
+ { USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, 0x0122, 0xff, 0xff, 0xff) },
+ { USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, 0x0123, 0xff, 0xff, 0xff) },
+ { USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, 0x0124, 0xff, 0xff, 0xff) },
+ { USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, 0x0125, 0xff, 0xff, 0xff) },
+ { USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, 0x0126, 0xff, 0xff, 0xff) },
+ { USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, 0x0128, 0xff, 0xff, 0xff) },
+ { USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, 0x0142, 0xff, 0xff, 0xff) },
+ { USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, 0x0143, 0xff, 0xff, 0xff) },
+ { USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, 0x0144, 0xff, 0xff, 0xff) },
+ { USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, 0x0145, 0xff, 0xff, 0xff) },
+ { USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, 0x0146, 0xff, 0xff, 0xff) },
+ { USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, 0x0147, 0xff, 0xff, 0xff) },
+ { USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, 0x0148, 0xff, 0xff, 0xff) },
+ { USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, 0x0149, 0xff, 0xff, 0xff) },
+ { USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, 0x0150, 0xff, 0xff, 0xff) },
+ { USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, 0x0151, 0xff, 0xff, 0xff) },
+ { USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, 0x0152, 0xff, 0xff, 0xff) },
+ { USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, 0x0153, 0xff, 0xff, 0xff) },
+ { USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, 0x0154, 0xff, 0xff, 0xff) },
+ { USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, 0x0155, 0xff, 0xff, 0xff) },
+ { USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, 0x0156, 0xff, 0xff, 0xff) },
+ { USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, 0x0157, 0xff, 0xff, 0xff) },
+ { USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, 0x0158, 0xff, 0xff, 0xff) },
+ { USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, 0x0159, 0xff, 0xff, 0xff) },
+ { USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, 0x0160, 0xff, 0xff, 0xff) },
+ { USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, 0x0161, 0xff, 0xff, 0xff) },
+ { USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, 0x0162, 0xff, 0xff, 0xff) },
{ USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, 0x0014, 0xff, 0xff, 0xff) }, /* ZTE CDMA products */
{ USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, 0x0027, 0xff, 0xff, 0xff) },
{ USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, 0x0059, 0xff, 0xff, 0xff) },
{ USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, 0x0060, 0xff, 0xff, 0xff) },
{ USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, 0x0070, 0xff, 0xff, 0xff) },
{ USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, 0x0073, 0xff, 0xff, 0xff) },
+ { USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, 0x0130, 0xff, 0xff, 0xff) },
+ { USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, 0x0141, 0xff, 0xff, 0xff) },
{ USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, ZTE_PRODUCT_CDMA_TECH, 0xff, 0xff, 0xff) },
{ USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, ZTE_PRODUCT_AC8710, 0xff, 0xff, 0xff) },
{ USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, ZTE_PRODUCT_AC2726, 0xff, 0xff, 0xff) },
@@ -599,6 +635,7 @@ static struct usb_device_id option_ids[] = {
{ USB_DEVICE(TOSHIBA_VENDOR_ID, TOSHIBA_PRODUCT_G450) },
{ USB_DEVICE(TOSHIBA_VENDOR_ID, TOSHIBA_PRODUCT_HSDPA_MINICARD ) }, /* Toshiba 3G HSDPA == Novatel Expedite EU870D MiniCard */
{ USB_DEVICE(ALINK_VENDOR_ID, 0x9000) },
+ { USB_DEVICE(ALINK_VENDOR_ID, 0xce16) },
{ USB_DEVICE_AND_INTERFACE_INFO(ALINK_VENDOR_ID, ALINK_PRODUCT_3GU, 0xff, 0xff, 0xff) },
{ USB_DEVICE(ALCATEL_VENDOR_ID, ALCATEL_PRODUCT_X060S) },
{ USB_DEVICE(AIRPLUS_VENDOR_ID, AIRPLUS_PRODUCT_MCD650) },
@@ -1312,7 +1349,7 @@ static int option_suspend(struct usb_serial *serial, pm_message_t message)
dbg("%s entered", __func__);
- if (serial->dev->auto_pm) {
+ if (message.event & PM_EVENT_AUTO) {
spin_lock_irq(&intfdata->susp_lock);
b = intfdata->in_flight;
spin_unlock_irq(&intfdata->susp_lock);
diff --git a/drivers/usb/serial/sierra.c b/drivers/usb/serial/sierra.c
index 5019325..ac1b644 100644
--- a/drivers/usb/serial/sierra.c
+++ b/drivers/usb/serial/sierra.c
@@ -16,8 +16,9 @@
Portions based on the option driver by Matthias Urlichs <smurf@smurf.noris.de>
Whom based his on the Keyspan driver by Hugh Blemings <hugh@blemings.org>
*/
-
-#define DRIVER_VERSION "v.1.3.8"
+/* Uncomment to log function calls */
+/* #define DEBUG */
+#define DRIVER_VERSION "v.1.7.16"
#define DRIVER_AUTHOR "Kevin Lloyd, Elina Pasheva, Matthew Safar, Rory Filer"
#define DRIVER_DESC "USB Driver for Sierra Wireless USB modems"
@@ -33,8 +34,10 @@
#define SWIMS_USB_REQUEST_SetPower 0x00
#define SWIMS_USB_REQUEST_SetNmea 0x07
-#define N_IN_URB 8
-#define N_OUT_URB 64
+#define N_IN_URB_HM 8
+#define N_OUT_URB_HM 64
+#define N_IN_URB 4
+#define N_OUT_URB 4
#define IN_BUFLEN 4096
#define MAX_TRANSFER (PAGE_SIZE - 512)
@@ -124,6 +127,23 @@ static int is_blacklisted(const u8 ifnum,
return 0;
}
+static int is_himemory(const u8 ifnum,
+ const struct sierra_iface_info *himemorylist)
+{
+ const u8 *info;
+ int i;
+
+ if (himemorylist) {
+ info = himemorylist->ifaceinfo;
+
+ for (i=0; i < himemorylist->infolen; i++) {
+ if (info[i] == ifnum)
+ return 1;
+ }
+ }
+ return 0;
+}
+
static int sierra_calc_interface(struct usb_serial *serial)
{
int interface;
@@ -186,6 +206,20 @@ static int sierra_probe(struct usb_serial *serial,
return result;
}
+/* interfaces with higher memory requirements */
+static const u8 hi_memory_typeA_ifaces[] = { 0, 2 };
+static const struct sierra_iface_info typeA_interface_list = {
+ .infolen = ARRAY_SIZE(hi_memory_typeA_ifaces),
+ .ifaceinfo = hi_memory_typeA_ifaces,
+};
+
+static const u8 hi_memory_typeB_ifaces[] = { 3, 4, 5, 6 };
+static const struct sierra_iface_info typeB_interface_list = {
+ .infolen = ARRAY_SIZE(hi_memory_typeB_ifaces),
+ .ifaceinfo = hi_memory_typeB_ifaces,
+};
+
+/* 'blacklist' of interfaces not served by this driver */
static const u8 direct_ip_non_serial_ifaces[] = { 7, 8, 9, 10, 11 };
static const struct sierra_iface_info direct_ip_interface_blacklist = {
.infolen = ARRAY_SIZE(direct_ip_non_serial_ifaces),
@@ -286,8 +320,10 @@ struct sierra_port_private {
struct usb_anchor active;
struct usb_anchor delayed;
+ int num_out_urbs;
+ int num_in_urbs;
/* Input endpoints and buffers for this port */
- struct urb *in_urbs[N_IN_URB];
+ struct urb *in_urbs[N_IN_URB_HM];
/* Settings for the port */
int rts_state; /* Handshaking pins (outputs) */
@@ -460,7 +496,7 @@ static int sierra_write(struct tty_struct *tty, struct usb_serial_port *port,
spin_lock_irqsave(&portdata->lock, flags);
dev_dbg(&port->dev, "%s - outstanding_urbs: %d\n", __func__,
portdata->outstanding_urbs);
- if (portdata->outstanding_urbs > N_OUT_URB) {
+ if (portdata->outstanding_urbs > portdata->num_out_urbs) {
spin_unlock_irqrestore(&portdata->lock, flags);
dev_dbg(&port->dev, "%s - write limit hit\n", __func__);
return 0;
@@ -665,7 +701,7 @@ static int sierra_write_room(struct tty_struct *tty)
/* try to give a good number back based on if we have any free urbs at
* this point in time */
spin_lock_irqsave(&portdata->lock, flags);
- if (portdata->outstanding_urbs > N_OUT_URB * 2 / 3) {
+ if (portdata->outstanding_urbs > (portdata->num_out_urbs * 2) / 3) {
spin_unlock_irqrestore(&portdata->lock, flags);
dev_dbg(&port->dev, "%s - write limit hit\n", __func__);
return 0;
@@ -680,7 +716,7 @@ static void sierra_stop_rx_urbs(struct usb_serial_port *port)
int i;
struct sierra_port_private *portdata = usb_get_serial_port_data(port);
- for (i = 0; i < ARRAY_SIZE(portdata->in_urbs); i++)
+ for (i = 0; i < portdata->num_in_urbs; i++)
usb_kill_urb(portdata->in_urbs[i]);
usb_kill_urb(port->interrupt_in_urb);
@@ -695,7 +731,7 @@ static int sierra_submit_rx_urbs(struct usb_serial_port *port, gfp_t mem_flags)
struct sierra_port_private *portdata = usb_get_serial_port_data(port);
ok_cnt = 0;
- for (i = 0; i < ARRAY_SIZE(portdata->in_urbs); i++) {
+ for (i = 0; i < portdata->num_in_urbs; i++) {
urb = portdata->in_urbs[i];
if (!urb)
continue;
@@ -791,7 +827,7 @@ static void sierra_close(struct usb_serial_port *port)
/* Stop reading urbs */
sierra_stop_rx_urbs(port);
/* .. and release them */
- for (i = 0; i < N_IN_URB; i++) {
+ for (i = 0; i < portdata->num_in_urbs; i++) {
sierra_release_urb(portdata->in_urbs[i]);
portdata->in_urbs[i] = NULL;
}
@@ -818,7 +854,7 @@ static int sierra_open(struct tty_struct *tty, struct usb_serial_port *port)
endpoint = port->bulk_in_endpointAddress;
- for (i = 0; i < ARRAY_SIZE(portdata->in_urbs); i++) {
+ for (i = 0; i < portdata->num_in_urbs; i++) {
urb = sierra_setup_urb(serial, endpoint, USB_DIR_IN, port,
IN_BUFLEN, GFP_KERNEL,
sierra_indat_callback);
@@ -869,7 +905,9 @@ static int sierra_startup(struct usb_serial *serial)
{
struct usb_serial_port *port;
struct sierra_port_private *portdata;
+ struct sierra_iface_info *himemoryp = NULL;
int i;
+ u8 ifnum;
dev_dbg(&serial->dev->dev, "%s\n", __func__);
@@ -886,13 +924,40 @@ static int sierra_startup(struct usb_serial *serial)
portdata = kzalloc(sizeof(*portdata), GFP_KERNEL);
if (!portdata) {
dev_dbg(&port->dev, "%s: kmalloc for "
- "sierra_port_private (%d) failed!.\n",
+ "sierra_port_private (%d) failed!\n",
__func__, i);
return -ENOMEM;
}
spin_lock_init(&portdata->lock);
init_usb_anchor(&portdata->active);
init_usb_anchor(&portdata->delayed);
+ ifnum = i;
+ /* Assume low memory requirements */
+ portdata->num_out_urbs = N_OUT_URB;
+ portdata->num_in_urbs = N_IN_URB;
+
+ /* Determine actual memory requirements */
+ if (serial->num_ports == 1) {
+ /* Get interface number for composite device */
+ ifnum = sierra_calc_interface(serial);
+ himemoryp =
+ (struct sierra_iface_info *)&typeB_interface_list;
+ if (is_himemory(ifnum, himemoryp)) {
+ portdata->num_out_urbs = N_OUT_URB_HM;
+ portdata->num_in_urbs = N_IN_URB_HM;
+ }
+ }
+ else {
+ himemoryp =
+ (struct sierra_iface_info *)&typeA_interface_list;
+ if (is_himemory(i, himemoryp)) {
+ portdata->num_out_urbs = N_OUT_URB_HM;
+ portdata->num_in_urbs = N_IN_URB_HM;
+ }
+ }
+ dev_dbg(&serial->dev->dev,
+ "Memory usage (urbs) interface #%d, in=%d, out=%d\n",
+ ifnum,portdata->num_in_urbs, portdata->num_out_urbs );
/* Set the port private data pointer */
usb_set_serial_port_data(port, portdata);
}
@@ -940,7 +1005,7 @@ static int sierra_suspend(struct usb_serial *serial, pm_message_t message)
struct sierra_intf_private *intfdata;
int b;
- if (serial->dev->auto_pm) {
+ if (message.event & PM_EVENT_AUTO) {
intfdata = serial->private;
spin_lock_irq(&intfdata->susp_lock);
b = intfdata->in_flight;
diff --git a/drivers/usb/storage/scsiglue.c b/drivers/usb/storage/scsiglue.c
index cfa26d5..e5e6df3 100644
--- a/drivers/usb/storage/scsiglue.c
+++ b/drivers/usb/storage/scsiglue.c
@@ -73,7 +73,8 @@
static const char* host_info(struct Scsi_Host *host)
{
- return "SCSI emulation for USB Mass Storage devices";
+ struct us_data *us = host_to_us(host);
+ return us->scsi_name;
}
static int slave_alloc (struct scsi_device *sdev)
diff --git a/drivers/usb/storage/transport.c b/drivers/usb/storage/transport.c
index 589f6b4..cc313d1 100644
--- a/drivers/usb/storage/transport.c
+++ b/drivers/usb/storage/transport.c
@@ -666,10 +666,11 @@ void usb_stor_invoke_transport(struct scsi_cmnd *srb, struct us_data *us)
* to wait for at least one CHECK_CONDITION to determine
* SANE_SENSE support
*/
- if ((srb->cmnd[0] == ATA_16 || srb->cmnd[0] == ATA_12) &&
+ if (unlikely((srb->cmnd[0] == ATA_16 || srb->cmnd[0] == ATA_12) &&
result == USB_STOR_TRANSPORT_GOOD &&
!(us->fflags & US_FL_SANE_SENSE) &&
- !(srb->cmnd[2] & 0x20)) {
+ !(us->fflags & US_FL_BAD_SENSE) &&
+ !(srb->cmnd[2] & 0x20))) {
US_DEBUGP("-- SAT supported, increasing auto-sense\n");
us->fflags |= US_FL_SANE_SENSE;
}
@@ -718,6 +719,12 @@ Retry_Sense:
if (test_bit(US_FLIDX_TIMED_OUT, &us->dflags)) {
US_DEBUGP("-- auto-sense aborted\n");
srb->result = DID_ABORT << 16;
+
+ /* If SANE_SENSE caused this problem, disable it */
+ if (sense_size != US_SENSE_SIZE) {
+ us->fflags &= ~US_FL_SANE_SENSE;
+ us->fflags |= US_FL_BAD_SENSE;
+ }
goto Handle_Errors;
}
@@ -727,10 +734,11 @@ Retry_Sense:
* (small) sense request. This fixes some USB GSM modems
*/
if (temp_result == USB_STOR_TRANSPORT_FAILED &&
- (us->fflags & US_FL_SANE_SENSE) &&
- sense_size != US_SENSE_SIZE) {
+ sense_size != US_SENSE_SIZE) {
US_DEBUGP("-- auto-sense failure, retry small sense\n");
sense_size = US_SENSE_SIZE;
+ us->fflags &= ~US_FL_SANE_SENSE;
+ us->fflags |= US_FL_BAD_SENSE;
goto Retry_Sense;
}
@@ -754,6 +762,7 @@ Retry_Sense:
*/
if (srb->sense_buffer[7] > (US_SENSE_SIZE - 8) &&
!(us->fflags & US_FL_SANE_SENSE) &&
+ !(us->fflags & US_FL_BAD_SENSE) &&
(srb->sense_buffer[0] & 0x7C) == 0x70) {
US_DEBUGP("-- SANE_SENSE support enabled\n");
us->fflags |= US_FL_SANE_SENSE;
diff --git a/drivers/usb/storage/unusual_devs.h b/drivers/usb/storage/unusual_devs.h
index d4f034e..64a0a2c 100644
--- a/drivers/usb/storage/unusual_devs.h
+++ b/drivers/usb/storage/unusual_devs.h
@@ -818,6 +818,13 @@ UNUSUAL_DEV( 0x066f, 0x8000, 0x0001, 0x0001,
US_SC_DEVICE, US_PR_DEVICE, NULL,
US_FL_FIX_CAPACITY ),
+/* Reported by Daniel Kukula <daniel.kuku@gmail.com> */
+UNUSUAL_DEV( 0x067b, 0x1063, 0x0100, 0x0100,
+ "Prolific Technology, Inc.",
+ "Prolific Storage Gadget",
+ US_SC_DEVICE, US_PR_DEVICE, NULL,
+ US_FL_BAD_SENSE ),
+
/* Reported by Rogerio Brito <rbrito@ime.usp.br> */
UNUSUAL_DEV( 0x067b, 0x2317, 0x0001, 0x001,
"Prolific Technology, Inc.",
diff --git a/drivers/usb/storage/usb.c b/drivers/usb/storage/usb.c
index 8060b85..5a53d4f 100644
--- a/drivers/usb/storage/usb.c
+++ b/drivers/usb/storage/usb.c
@@ -45,6 +45,10 @@
* 675 Mass Ave, Cambridge, MA 02139, USA.
*/
+#ifdef CONFIG_USB_STORAGE_DEBUG
+#define DEBUG
+#endif
+
#include <linux/sched.h>
#include <linux/errno.h>
#include <linux/freezer.h>
@@ -228,6 +232,7 @@ void fill_inquiry_response(struct us_data *us, unsigned char *data,
if (data_len<36) // You lose.
return;
+ memset(data+8, ' ', 28);
if(data[0]&0x20) { /* USB device currently not connected. Return
peripheral qualifier 001b ("...however, the
physical device is not currently connected
@@ -237,15 +242,15 @@ void fill_inquiry_response(struct us_data *us, unsigned char *data,
device, it may return zeros or ASCII spaces
(20h) in those fields until the data is
available from the device."). */
- memset(data+8,0,28);
} else {
u16 bcdDevice = le16_to_cpu(us->pusb_dev->descriptor.bcdDevice);
- memcpy(data+8, us->unusual_dev->vendorName,
- strlen(us->unusual_dev->vendorName) > 8 ? 8 :
- strlen(us->unusual_dev->vendorName));
- memcpy(data+16, us->unusual_dev->productName,
- strlen(us->unusual_dev->productName) > 16 ? 16 :
- strlen(us->unusual_dev->productName));
+ int n;
+
+ n = strlen(us->unusual_dev->vendorName);
+ memcpy(data+8, us->unusual_dev->vendorName, min(8, n));
+ n = strlen(us->unusual_dev->productName);
+ memcpy(data+16, us->unusual_dev->productName, min(16, n));
+
data[32] = 0x30 + ((bcdDevice>>12) & 0x0F);
data[33] = 0x30 + ((bcdDevice>>8) & 0x0F);
data[34] = 0x30 + ((bcdDevice>>4) & 0x0F);
@@ -459,6 +464,9 @@ static void adjust_quirks(struct us_data *us)
case 'a':
f |= US_FL_SANE_SENSE;
break;
+ case 'b':
+ f |= US_FL_BAD_SENSE;
+ break;
case 'c':
f |= US_FL_FIX_CAPACITY;
break;
@@ -808,14 +816,13 @@ static int usb_stor_scan_thread(void * __us)
{
struct us_data *us = (struct us_data *)__us;
- printk(KERN_DEBUG
- "usb-storage: device found at %d\n", us->pusb_dev->devnum);
+ dev_dbg(&us->pusb_intf->dev, "device found\n");
set_freezable();
/* Wait for the timeout to expire or for a disconnect */
if (delay_use > 0) {
- printk(KERN_DEBUG "usb-storage: waiting for device "
- "to settle before scanning\n");
+ dev_dbg(&us->pusb_intf->dev, "waiting for device to settle "
+ "before scanning\n");
wait_event_freezable_timeout(us->delay_wait,
test_bit(US_FLIDX_DONT_SCAN, &us->dflags),
delay_use * HZ);
@@ -832,7 +839,7 @@ static int usb_stor_scan_thread(void * __us)
mutex_unlock(&us->dev_mutex);
}
scsi_scan_host(us_to_host(us));
- printk(KERN_DEBUG "usb-storage: device scan complete\n");
+ dev_dbg(&us->pusb_intf->dev, "scan complete\n");
/* Should we unbind if no devices were detected? */
}
@@ -840,6 +847,15 @@ static int usb_stor_scan_thread(void * __us)
complete_and_exit(&us->scanning_done, 0);
}
+static unsigned int usb_stor_sg_tablesize(struct usb_interface *intf)
+{
+ struct usb_device *usb_dev = interface_to_usbdev(intf);
+
+ if (usb_dev->bus->sg_tablesize) {
+ return usb_dev->bus->sg_tablesize;
+ }
+ return SG_ALL;
+}
/* First part of general USB mass-storage probing */
int usb_stor_probe1(struct us_data **pus,
@@ -868,6 +884,7 @@ int usb_stor_probe1(struct us_data **pus,
* Allow 16-byte CDBs and thus > 2TB
*/
host->max_cmd_len = 16;
+ host->sg_tablesize = usb_stor_sg_tablesize(intf);
*pus = us = host_to_us(host);
memset(us, 0, sizeof(struct us_data));
mutex_init(&(us->dev_mutex));
@@ -929,6 +946,8 @@ int usb_stor_probe2(struct us_data *us)
result = usb_stor_acquire_resources(us);
if (result)
goto BadDevice;
+ snprintf(us->scsi_name, sizeof(us->scsi_name), "usb-storage %s",
+ dev_name(&us->pusb_intf->dev));
result = scsi_add_host(us_to_host(us), &us->pusb_intf->dev);
if (result) {
printk(KERN_WARNING USB_STORAGE
diff --git a/drivers/usb/storage/usb.h b/drivers/usb/storage/usb.h
index 2609efb..6971713 100644
--- a/drivers/usb/storage/usb.h
+++ b/drivers/usb/storage/usb.h
@@ -132,6 +132,7 @@ struct us_data {
/* SCSI interfaces */
struct scsi_cmnd *srb; /* current srb */
unsigned int tag; /* current dCBWTag */
+ char scsi_name[32]; /* scsi_host name */
/* control and bulk communications data */
struct urb *current_urb; /* USB requests */
diff --git a/drivers/usb/usb-skeleton.c b/drivers/usb/usb-skeleton.c
index b62f2bc..b1e579c 100644
--- a/drivers/usb/usb-skeleton.c
+++ b/drivers/usb/usb-skeleton.c
@@ -358,7 +358,7 @@ retry:
rv = skel_do_read_io(dev, count);
if (rv < 0)
goto exit;
- else if (!file->f_flags & O_NONBLOCK)
+ else if (!(file->f_flags & O_NONBLOCK))
goto retry;
rv = -EAGAIN;
}
@@ -411,7 +411,7 @@ static ssize_t skel_write(struct file *file, const char *user_buffer,
* limit the number of URBs in flight to stop a user from using up all
* RAM
*/
- if (!file->f_flags & O_NONBLOCK) {
+ if (!(file->f_flags & O_NONBLOCK)) {
if (down_interruptible(&dev->limit_sem)) {
retval = -ERESTARTSYS;
goto exit;
diff --git a/drivers/usb/wusbcore/devconnect.c b/drivers/usb/wusbcore/devconnect.c
index 4ac4300..dced419 100644
--- a/drivers/usb/wusbcore/devconnect.c
+++ b/drivers/usb/wusbcore/devconnect.c
@@ -119,10 +119,12 @@ static struct wusb_dev *wusb_dev_alloc(struct wusbhc *wusbhc)
urb = usb_alloc_urb(0, GFP_KERNEL);
if (urb == NULL)
goto err;
+ wusb_dev->set_gtk_urb = urb;
- req = kmalloc(sizeof(struct usb_ctrlrequest), GFP_KERNEL);
+ req = kmalloc(sizeof(*req), GFP_KERNEL);
if (req == NULL)
goto err;
+ wusb_dev->set_gtk_req = req;
req->bRequestType = USB_DIR_OUT | USB_TYPE_STANDARD | USB_RECIP_DEVICE;
req->bRequest = USB_REQ_SET_DESCRIPTOR;
@@ -130,9 +132,6 @@ static struct wusb_dev *wusb_dev_alloc(struct wusbhc *wusbhc)
req->wIndex = 0;
req->wLength = cpu_to_le16(wusbhc->gtk.descr.bLength);
- wusb_dev->set_gtk_urb = urb;
- wusb_dev->set_gtk_req = req;
-
return wusb_dev;
err:
wusb_dev_free(wusb_dev);
diff --git a/drivers/usb/wusbcore/security.c b/drivers/usb/wusbcore/security.c
index 4516c36..edcd2d7 100644
--- a/drivers/usb/wusbcore/security.c
+++ b/drivers/usb/wusbcore/security.c
@@ -205,15 +205,15 @@ int wusb_dev_sec_add(struct wusbhc *wusbhc,
const void *itr, *top;
char buf[64];
- secd = kmalloc(sizeof(struct usb_security_descriptor), GFP_KERNEL);
+ secd = kmalloc(sizeof(*secd), GFP_KERNEL);
if (secd == NULL) {
result = -ENOMEM;
goto out;
}
result = usb_get_descriptor(usb_dev, USB_DT_SECURITY,
- 0, secd, sizeof(struct usb_security_descriptor));
- if (result < sizeof(secd)) {
+ 0, secd, sizeof(*secd));
+ if (result < sizeof(*secd)) {
dev_err(dev, "Can't read security descriptor or "
"not enough data: %d\n", result);
goto out;
diff --git a/drivers/usb/wusbcore/wusbhc.c b/drivers/usb/wusbcore/wusbhc.c
index ee6256f..eab86e4 100644
--- a/drivers/usb/wusbcore/wusbhc.c
+++ b/drivers/usb/wusbcore/wusbhc.c
@@ -147,10 +147,40 @@ static ssize_t wusb_chid_store(struct device *dev,
}
static DEVICE_ATTR(wusb_chid, 0644, wusb_chid_show, wusb_chid_store);
+
+static ssize_t wusb_phy_rate_show(struct device *dev,
+ struct device_attribute *attr,
+ char *buf)
+{
+ struct wusbhc *wusbhc = usbhc_dev_to_wusbhc(dev);
+
+ return sprintf(buf, "%d\n", wusbhc->phy_rate);
+}
+
+static ssize_t wusb_phy_rate_store(struct device *dev,
+ struct device_attribute *attr,
+ const char *buf, size_t size)
+{
+ struct wusbhc *wusbhc = usbhc_dev_to_wusbhc(dev);
+ uint8_t phy_rate;
+ ssize_t result;
+
+ result = sscanf(buf, "%hhu", &phy_rate);
+ if (result != 1)
+ return -EINVAL;
+ if (phy_rate >= UWB_PHY_RATE_INVALID)
+ return -EINVAL;
+
+ wusbhc->phy_rate = phy_rate;
+ return size;
+}
+static DEVICE_ATTR(wusb_phy_rate, 0644, wusb_phy_rate_show, wusb_phy_rate_store);
+
/* Group all the WUSBHC attributes */
static struct attribute *wusbhc_attrs[] = {
&dev_attr_wusb_trust_timeout.attr,
&dev_attr_wusb_chid.attr,
+ &dev_attr_wusb_phy_rate.attr,
NULL,
};
@@ -177,6 +207,8 @@ int wusbhc_create(struct wusbhc *wusbhc)
int result = 0;
wusbhc->trust_timeout = WUSB_TRUST_TIMEOUT_MS;
+ wusbhc->phy_rate = UWB_PHY_RATE_INVALID - 1;
+
mutex_init(&wusbhc->mutex);
result = wusbhc_mmcie_create(wusbhc);
if (result < 0)
diff --git a/drivers/usb/wusbcore/wusbhc.h b/drivers/usb/wusbcore/wusbhc.h
index 797c245..fd2fd4e 100644
--- a/drivers/usb/wusbcore/wusbhc.h
+++ b/drivers/usb/wusbcore/wusbhc.h
@@ -253,6 +253,7 @@ struct wusbhc {
unsigned trust_timeout; /* in jiffies */
struct wusb_ckhdid chid;
+ uint8_t phy_rate;
struct wuie_host_info *wuie_host_info;
struct mutex mutex; /* locks everything else */
diff --git a/include/linux/usb.h b/include/linux/usb.h
index a34fa89..e101a2d 100644
--- a/include/linux/usb.h
+++ b/include/linux/usb.h
@@ -331,6 +331,7 @@ struct usb_bus {
u8 otg_port; /* 0, or number of OTG/HNP port */
unsigned is_b_host:1; /* true during some HNP roleswitches */
unsigned b_hnp_enable:1; /* OTG: did A-Host enable HNP? */
+ unsigned sg_tablesize; /* 0 or largest number of sg list entries */
int devnum_next; /* Next open device number in
* round-robin allocation */
@@ -428,11 +429,9 @@ struct usb_tt;
* @last_busy: time of last use
* @autosuspend_delay: in jiffies
* @connect_time: time device was first connected
- * @auto_pm: autosuspend/resume in progress
* @do_remote_wakeup: remote wakeup should be enabled
* @reset_resume: needs reset instead of resume
* @autosuspend_disabled: autosuspend disabled by the user
- * @autoresume_disabled: autoresume disabled by the user
* @skip_sys_resume: skip the next system resume
* @wusb_dev: if this is a Wireless USB device, link to the WUSB
* specific data for the device.
@@ -513,11 +512,9 @@ struct usb_device {
int autosuspend_delay;
unsigned long connect_time;
- unsigned auto_pm:1;
unsigned do_remote_wakeup:1;
unsigned reset_resume:1;
unsigned autosuspend_disabled:1;
- unsigned autoresume_disabled:1;
unsigned skip_sys_resume:1;
#endif
struct wusb_dev *wusb_dev;
@@ -543,22 +540,20 @@ extern struct usb_device *usb_find_device(u16 vendor_id, u16 product_id);
/* USB autosuspend and autoresume */
#ifdef CONFIG_USB_SUSPEND
-extern int usb_autopm_set_interface(struct usb_interface *intf);
extern int usb_autopm_get_interface(struct usb_interface *intf);
extern void usb_autopm_put_interface(struct usb_interface *intf);
extern int usb_autopm_get_interface_async(struct usb_interface *intf);
extern void usb_autopm_put_interface_async(struct usb_interface *intf);
-static inline void usb_autopm_enable(struct usb_interface *intf)
+static inline void usb_autopm_get_interface_no_resume(
+ struct usb_interface *intf)
{
- atomic_set(&intf->pm_usage_cnt, 0);
- usb_autopm_set_interface(intf);
+ atomic_inc(&intf->pm_usage_cnt);
}
-
-static inline void usb_autopm_disable(struct usb_interface *intf)
+static inline void usb_autopm_put_interface_no_suspend(
+ struct usb_interface *intf)
{
- atomic_set(&intf->pm_usage_cnt, 1);
- usb_autopm_set_interface(intf);
+ atomic_dec(&intf->pm_usage_cnt);
}
static inline void usb_mark_last_busy(struct usb_device *udev)
@@ -568,12 +563,8 @@ static inline void usb_mark_last_busy(struct usb_device *udev)
#else
-static inline int usb_autopm_set_interface(struct usb_interface *intf)
-{ return 0; }
-
static inline int usb_autopm_get_interface(struct usb_interface *intf)
{ return 0; }
-
static inline int usb_autopm_get_interface_async(struct usb_interface *intf)
{ return 0; }
@@ -581,9 +572,11 @@ static inline void usb_autopm_put_interface(struct usb_interface *intf)
{ }
static inline void usb_autopm_put_interface_async(struct usb_interface *intf)
{ }
-static inline void usb_autopm_enable(struct usb_interface *intf)
+static inline void usb_autopm_get_interface_no_resume(
+ struct usb_interface *intf)
{ }
-static inline void usb_autopm_disable(struct usb_interface *intf)
+static inline void usb_autopm_put_interface_no_suspend(
+ struct usb_interface *intf)
{ }
static inline void usb_mark_last_busy(struct usb_device *udev)
{ }
@@ -626,6 +619,10 @@ extern struct usb_interface *usb_ifnum_to_if(const struct usb_device *dev,
unsigned ifnum);
extern struct usb_host_interface *usb_altnum_to_altsetting(
const struct usb_interface *intf, unsigned int altnum);
+extern struct usb_host_interface *usb_find_alt_setting(
+ struct usb_host_config *config,
+ unsigned int iface_num,
+ unsigned int alt_num);
/**
diff --git a/include/linux/usb/composite.h b/include/linux/usb/composite.h
index 4f6bb3d..738ea1a 100644
--- a/include/linux/usb/composite.h
+++ b/include/linux/usb/composite.h
@@ -127,6 +127,7 @@ struct usb_function {
/* private: */
/* internals */
struct list_head list;
+ DECLARE_BITMAP(endpoints, 32);
};
int usb_add_function(struct usb_configuration *, struct usb_function *);
diff --git a/include/linux/usb/otg.h b/include/linux/usb/otg.h
index 2443c0e..52bb917 100644
--- a/include/linux/usb/otg.h
+++ b/include/linux/usb/otg.h
@@ -33,6 +33,23 @@ enum usb_otg_state {
OTG_STATE_A_VBUS_ERR,
};
+#define USB_OTG_PULLUP_ID (1 << 0)
+#define USB_OTG_PULLDOWN_DP (1 << 1)
+#define USB_OTG_PULLDOWN_DM (1 << 2)
+#define USB_OTG_EXT_VBUS_INDICATOR (1 << 3)
+#define USB_OTG_DRV_VBUS (1 << 4)
+#define USB_OTG_DRV_VBUS_EXT (1 << 5)
+
+struct otg_transceiver;
+
+/* for transceivers connected thru an ULPI interface, the user must
+ * provide access ops
+ */
+struct otg_io_access_ops {
+ int (*read)(struct otg_transceiver *otg, u32 reg);
+ int (*write)(struct otg_transceiver *otg, u32 val, u32 reg);
+};
+
/*
* the otg driver needs to interact with both device side and host side
* usb controllers. it decides which controller is active at a given
@@ -42,6 +59,7 @@ enum usb_otg_state {
struct otg_transceiver {
struct device *dev;
const char *label;
+ unsigned int flags;
u8 default_a;
enum usb_otg_state state;
@@ -49,10 +67,17 @@ struct otg_transceiver {
struct usb_bus *host;
struct usb_gadget *gadget;
+ struct otg_io_access_ops *io_ops;
+ void __iomem *io_priv;
+
/* to pass extra port status to the root hub */
u16 port_status;
u16 port_change;
+ /* initialize/shutdown the OTG controller */
+ int (*init)(struct otg_transceiver *otg);
+ void (*shutdown)(struct otg_transceiver *otg);
+
/* bind/unbind the host controller */
int (*set_host)(struct otg_transceiver *otg,
struct usb_bus *host);
@@ -65,6 +90,10 @@ struct otg_transceiver {
int (*set_power)(struct otg_transceiver *otg,
unsigned mA);
+ /* effective for A-peripheral, ignored for B devices */
+ int (*set_vbus)(struct otg_transceiver *otg,
+ bool enabled);
+
/* for non-OTG B devices: set transceiver into suspend mode */
int (*set_suspend)(struct otg_transceiver *otg,
int suspend);
@@ -85,6 +114,38 @@ extern int otg_set_transceiver(struct otg_transceiver *);
extern void usb_nop_xceiv_register(void);
extern void usb_nop_xceiv_unregister(void);
+/* helpers for direct access thru low-level io interface */
+static inline int otg_io_read(struct otg_transceiver *otg, u32 reg)
+{
+ if (otg->io_ops && otg->io_ops->read)
+ return otg->io_ops->read(otg, reg);
+
+ return -EINVAL;
+}
+
+static inline int otg_io_write(struct otg_transceiver *otg, u32 reg, u32 val)
+{
+ if (otg->io_ops && otg->io_ops->write)
+ return otg->io_ops->write(otg, reg, val);
+
+ return -EINVAL;
+}
+
+static inline int
+otg_init(struct otg_transceiver *otg)
+{
+ if (otg->init)
+ return otg->init(otg);
+
+ return 0;
+}
+
+static inline void
+otg_shutdown(struct otg_transceiver *otg)
+{
+ if (otg->shutdown)
+ otg->shutdown(otg);
+}
/* for usb host and peripheral controller drivers */
extern struct otg_transceiver *otg_get_transceiver(void);
@@ -97,6 +158,12 @@ otg_start_hnp(struct otg_transceiver *otg)
return otg->start_hnp(otg);
}
+/* Context: can sleep */
+static inline int
+otg_set_vbus(struct otg_transceiver *otg, bool enabled)
+{
+ return otg->set_vbus(otg, enabled);
+}
/* for HCDs */
static inline int
@@ -105,7 +172,6 @@ otg_set_host(struct otg_transceiver *otg, struct usb_bus *host)
return otg->set_host(otg, host);
}
-
/* for usb peripheral controller drivers */
/* Context: can sleep */
diff --git a/include/linux/usb/ulpi.h b/include/linux/usb/ulpi.h
new file mode 100644
index 0000000..20675c6
--- /dev/null
+++ b/include/linux/usb/ulpi.h
@@ -0,0 +1,7 @@
+#ifndef __LINUX_USB_ULPI_H
+#define __LINUX_USB_ULPI_H
+
+struct otg_transceiver *otg_ulpi_create(struct otg_io_access_ops *ops,
+ unsigned int flags);
+
+#endif /* __LINUX_USB_ULPI_H */
diff --git a/include/linux/usb_usual.h b/include/linux/usb_usual.h
index 3d15fb9..a4b947e 100644
--- a/include/linux/usb_usual.h
+++ b/include/linux/usb_usual.h
@@ -56,7 +56,9 @@
US_FLAG(SANE_SENSE, 0x00008000) \
/* Sane Sense (> 18 bytes) */ \
US_FLAG(CAPACITY_OK, 0x00010000) \
- /* READ CAPACITY response is correct */
+ /* READ CAPACITY response is correct */ \
+ US_FLAG(BAD_SENSE, 0x00020000) \
+ /* Bad Sense (never more than 18 bytes) */
#define US_FLAG(name, value) US_FL_##name = value ,
enum { US_DO_ALL_FLAGS };
diff --git a/scripts/mod/file2alias.c b/scripts/mod/file2alias.c
index 62a9025..6f426af 100644
--- a/scripts/mod/file2alias.c
+++ b/scripts/mod/file2alias.c
@@ -104,7 +104,7 @@ static void device_id_check(const char *modname, const char *device_id,
static void do_usb_entry(struct usb_device_id *id,
unsigned int bcdDevice_initial, int bcdDevice_initial_digits,
unsigned char range_lo, unsigned char range_hi,
- struct module *mod)
+ unsigned char max, struct module *mod)
{
char alias[500];
strcpy(alias, "usb:");
@@ -118,9 +118,22 @@ static void do_usb_entry(struct usb_device_id *id,
sprintf(alias + strlen(alias), "%0*X",
bcdDevice_initial_digits, bcdDevice_initial);
if (range_lo == range_hi)
- sprintf(alias + strlen(alias), "%u", range_lo);
- else if (range_lo > 0 || range_hi < 9)
- sprintf(alias + strlen(alias), "[%u-%u]", range_lo, range_hi);
+ sprintf(alias + strlen(alias), "%X", range_lo);
+ else if (range_lo > 0 || range_hi < max) {
+ if (range_lo > 0x9 || range_hi < 0xA)
+ sprintf(alias + strlen(alias),
+ "[%X-%X]",
+ range_lo,
+ range_hi);
+ else {
+ sprintf(alias + strlen(alias),
+ range_lo < 0x9 ? "[%X-9" : "[%X",
+ range_lo);
+ sprintf(alias + strlen(alias),
+ range_hi > 0xA ? "a-%X]" : "%X]",
+ range_lo);
+ }
+ }
if (bcdDevice_initial_digits < (sizeof(id->bcdDevice_lo) * 2 - 1))
strcat(alias, "*");
@@ -147,10 +160,49 @@ static void do_usb_entry(struct usb_device_id *id,
"MODULE_ALIAS(\"%s\");\n", alias);
}
+/* Handles increment/decrement of BCD formatted integers */
+/* Returns the previous value, so it works like i++ or i-- */
+static unsigned int incbcd(unsigned int *bcd,
+ int inc,
+ unsigned char max,
+ size_t chars)
+{
+ unsigned int init = *bcd, i, j;
+ unsigned long long c, dec = 0;
+
+ /* If bcd is not in BCD format, just increment */
+ if (max > 0x9) {
+ *bcd += inc;
+ return init;
+ }
+
+ /* Convert BCD to Decimal */
+ for (i=0 ; i < chars ; i++) {
+ c = (*bcd >> (i << 2)) & 0xf;
+ c = c > 9 ? 9 : c; /* force to bcd just in case */
+ for (j=0 ; j < i ; j++)
+ c = c * 10;
+ dec += c;
+ }
+
+ /* Do our increment/decrement */
+ dec += inc;
+ *bcd = 0;
+
+ /* Convert back to BCD */
+ for (i=0 ; i < chars ; i++) {
+ for (c=1,j=0 ; j < i ; j++)
+ c = c * 10;
+ c = (dec / c) % 10;
+ *bcd += c << (i << 2);
+ }
+ return init;
+}
+
static void do_usb_entry_multi(struct usb_device_id *id, struct module *mod)
{
unsigned int devlo, devhi;
- unsigned char chi, clo;
+ unsigned char chi, clo, max;
int ndigits;
id->match_flags = TO_NATIVE(id->match_flags);
@@ -162,6 +214,17 @@ static void do_usb_entry_multi(struct usb_device_id *id, struct module *mod)
devhi = id->match_flags & USB_DEVICE_ID_MATCH_DEV_HI ?
TO_NATIVE(id->bcdDevice_hi) : ~0x0U;
+ /* Figure out if this entry is in bcd or hex format */
+ max = 0x9; /* Default to decimal format */
+ for (ndigits = 0 ; ndigits < sizeof(id->bcdDevice_lo) * 2 ; ndigits++) {
+ clo = (devlo >> (ndigits << 2)) & 0xf;
+ chi = ((devhi > 0x9999 ? 0x9999 : devhi) >> (ndigits << 2)) & 0xf;
+ if (clo > max || chi > max) {
+ max = 0xf;
+ break;
+ }
+ }
+
/*
* Some modules (visor) have empty slots as placeholder for
* run-time specification that results in catch-all alias
@@ -173,21 +236,27 @@ static void do_usb_entry_multi(struct usb_device_id *id, struct module *mod)
for (ndigits = sizeof(id->bcdDevice_lo) * 2 - 1; devlo <= devhi; ndigits--) {
clo = devlo & 0xf;
chi = devhi & 0xf;
- if (chi > 9) /* it's bcd not hex */
- chi = 9;
+ if (chi > max) /* If we are in bcd mode, truncate if necessary */
+ chi = max;
devlo >>= 4;
devhi >>= 4;
if (devlo == devhi || !ndigits) {
- do_usb_entry(id, devlo, ndigits, clo, chi, mod);
+ do_usb_entry(id, devlo, ndigits, clo, chi, max, mod);
break;
}
- if (clo > 0)
- do_usb_entry(id, devlo++, ndigits, clo, 9, mod);
-
- if (chi < 9)
- do_usb_entry(id, devhi--, ndigits, 0, chi, mod);
+ if (clo > 0x0)
+ do_usb_entry(id,
+ incbcd(&devlo, 1, max,
+ sizeof(id->bcdDevice_lo) * 2),
+ ndigits, clo, max, max, mod);
+
+ if (chi < max)
+ do_usb_entry(id,
+ incbcd(&devhi, -1, max,
+ sizeof(id->bcdDevice_lo) * 2),
+ ndigits, 0x0, chi, max, mod);
}
}