mirror of
https://mirrors.bfsu.edu.cn/git/linux.git
synced 2024-11-11 12:28:41 +08:00
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 ...
This commit is contained in:
commit
748e566b7e
@ -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
|
||||
|
@ -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.
|
||||
|
@ -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
|
||||
|
@ -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.
|
||||
|
||||
|
@ -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.
|
||||
|
||||
|
@ -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>
|
||||
|
@ -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
|
||||
|
92
arch/arm/plat-mxc/ehci.c
Normal file
92
arch/arm/plat-mxc/ehci.c
Normal file
@ -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);
|
||||
|
37
arch/arm/plat-mxc/include/mach/mxc_ehci.h
Normal file
37
arch/arm/plat-mxc/include/mach/mxc_ehci.h
Normal file
@ -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 */
|
||||
|
@ -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 {
|
||||
|
@ -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)
|
||||
|
@ -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
|
||||
|
||||
|
@ -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.
|
||||
|
@ -44,3 +44,5 @@ obj-y += early/
|
||||
|
||||
obj-$(CONFIG_USB_ATM) += atm/
|
||||
obj-$(CONFIG_USB_SPEEDTOUCH) += atm/
|
||||
|
||||
obj-$(CONFIG_USB_ULPI) += otg/
|
||||
|
@ -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!! */
|
||||
{ }
|
||||
};
|
||||
|
||||
|
@ -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;
|
||||
}
|
||||
|
@ -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 "
|
||||
|
||||
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);
|
||||
}
|
||||
|
||||
return retval;
|
||||
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);
|
||||
|
@ -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;
|
||||
|
||||
|
@ -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 {
|
||||
|
@ -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 :
|
||||
|
@ -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,
|
||||
|
@ -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);
|
||||
}
|
||||
|
@ -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
|
||||
|
@ -138,6 +138,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)
|
||||
{
|
||||
@ -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 = {
|
||||
|
@ -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);
|
||||
|
@ -63,6 +63,43 @@ MODULE_PARM_DESC(autosuspend, "default autosuspend delay");
|
||||
#endif
|
||||
|
||||
|
||||
/**
|
||||
* 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
|
||||
@ -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;
|
||||
|
||||
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;
|
||||
dev = bus_find_device(&usb_bus_type, NULL, &minor, __find_interface);
|
||||
|
||||
/* Drop reference count from bus_find_device */
|
||||
put_device(dev);
|
||||
|
||||
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)
|
||||
{
|
||||
|
@ -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);
|
||||
|
@ -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.
|
||||
|
||||
|
@ -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
|
||||
|
||||
|
@ -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;
|
||||
|
@ -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,
|
||||
|
@ -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;
|
||||
|
@ -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;
|
||||
|
@ -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 */
|
||||
|
@ -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,
|
||||
|
3091
drivers/usb/gadget/f_mass_storage.c
Normal file
3091
drivers/usb/gadget/f_mass_storage.c
Normal file
File diff suppressed because it is too large
Load Diff
@ -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 */
|
||||
|
File diff suppressed because it is too large
Load Diff
240
drivers/usb/gadget/mass_storage.c
Normal file
240
drivers/usb/gadget/mass_storage.c
Normal file
@ -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);
|
358
drivers/usb/gadget/multi.c
Normal file
358
drivers/usb/gadget/multi.c
Normal file
@ -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);
|
778
drivers/usb/gadget/storage_common.c
Normal file
778
drivers/usb/gadget/storage_common.c
Normal file
@ -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);
|
||||
}
|
@ -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]);
|
||||
|
||||
|
@ -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
|
||||
|
@ -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
|
||||
|
@ -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();
|
||||
|
296
drivers/usb/host/ehci-mxc.c
Normal file
296
drivers/usb/host/ehci-mxc.c
Normal file
@ -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",
|
||||
},
|
||||
};
|
756
drivers/usb/host/ehci-omap.c
Normal file
756
drivers/usb/host/ehci-omap.c
Normal file
@ -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>");
|
||||
|
@ -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);
|
||||
|
@ -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:
|
||||
|
300
drivers/usb/host/ehci-xilinx-of.c
Normal file
300
drivers/usb/host/ehci-xilinx-of.c
Normal file
@ -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,
|
||||
},
|
||||
};
|
@ -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;
|
||||
|
@ -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);
|
||||
|
@ -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",
|
||||
},
|
||||
|
@ -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;
|
||||
|
||||
|
@ -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",
|
||||
|
@ -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);
|
||||
|
@ -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);
|
||||
kfree(std->pl_virt);
|
||||
}
|
||||
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;
|
||||
|
@ -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;
|
||||
};
|
||||
|
||||
/**
|
||||
|
@ -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)
|
||||
|
@ -66,6 +66,25 @@ static int handshake(struct xhci_hcd *xhci, void __iomem *ptr,
|
||||
return -ETIMEDOUT;
|
||||
}
|
||||
|
||||
/*
|
||||
* Disable interrupts and begin the xHCI halting process.
|
||||
*/
|
||||
void xhci_quiesce(struct xhci_hcd *xhci)
|
||||
{
|
||||
u32 halted;
|
||||
u32 cmd;
|
||||
u32 mask;
|
||||
|
||||
mask = ~(XHCI_IRQS);
|
||||
halted = xhci_readl(xhci, &xhci->op_regs->status) & STS_HALT;
|
||||
if (!halted)
|
||||
mask &= ~CMD_RUN;
|
||||
|
||||
cmd = xhci_readl(xhci, &xhci->op_regs->command);
|
||||
cmd &= mask;
|
||||
xhci_writel(xhci, cmd, &xhci->op_regs->command);
|
||||
}
|
||||
|
||||
/*
|
||||
* Force HC into halt state.
|
||||
*
|
||||
@ -77,20 +96,8 @@ static int handshake(struct xhci_hcd *xhci, void __iomem *ptr,
|
||||
*/
|
||||
int xhci_halt(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)
|
||||
mask &= ~CMD_RUN;
|
||||
|
||||
cmd = xhci_readl(xhci, &xhci->op_regs->command);
|
||||
cmd &= mask;
|
||||
xhci_writel(xhci, cmd, &xhci->op_regs->command);
|
||||
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;
|
||||
|
@ -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);
|
||||
|
@ -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));
|
||||
|
@ -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)
|
||||
|
@ -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,
|
||||
|
@ -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;
|
||||
|
||||
|
@ -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;
|
||||
|
||||
if (urb->transfer_buffer == NULL)
|
||||
return 'Z';
|
||||
mon_copy_to_buff(rp, offset, urb->transfer_buffer, length);
|
||||
return 0;
|
||||
*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;
|
||||
|
||||
} 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);
|
||||
|
@ -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;
|
||||
|
||||
memcpy(ep->data, urb->transfer_buffer, len);
|
||||
/* 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, src, len);
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
@ -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
|
||||
|
@ -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();
|
||||
|
@ -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)
|
||||
|
@ -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);
|
||||
|
||||
|
@ -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)
|
||||
|
@ -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.
|
||||
|
@ -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) {
|
||||
DBG(5, "%s dma still busy?\n", musb_ep->end_point.name);
|
||||
return;
|
||||
}
|
||||
|
||||
if (request) {
|
||||
u8 is_dma = 0;
|
||||
|
||||
if (dma && (csr & MUSB_TXCSR_DMAENAB)) {
|
||||
is_dma = 1;
|
||||
csr |= MUSB_TXCSR_P_WZC_BITS;
|
||||
csr &= ~MUSB_TXCSR_P_SENTSTALL;
|
||||
csr &= ~(MUSB_TXCSR_DMAENAB | MUSB_TXCSR_P_UNDERRUN |
|
||||
MUSB_TXCSR_TXPKTRDY);
|
||||
musb_writew(epio, MUSB_TXCSR, csr);
|
||||
break;
|
||||
/* 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 (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 (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
|
||||
};
|
||||
|
@ -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 */
|
||||
|
@ -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 */
|
||||
|
@ -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;
|
||||
|
@ -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,
|
||||
|
@ -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);
|
||||
|
@ -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;
|
||||
}
|
||||
|
@ -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
|
||||
|
@ -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
|
||||
|
@ -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");
|
||||
|
||||
|
136
drivers/usb/otg/ulpi.c
Normal file
136
drivers/usb/otg/ulpi.c
Normal file
@ -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);
|
||||
|
File diff suppressed because it is too large
Load Diff
@ -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;
|
||||
|
@ -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)
|
||||
|
@ -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 */
|
||||
|
@ -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);
|
||||
|
@ -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;
|
||||
|
@ -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)
|
||||
|
@ -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;
|
||||
|
@ -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.",
|
||||
|
@ -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
|
||||
|
@ -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 */
|
||||
|
@ -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;
|
||||
|
@ -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);
|
||||
|
@ -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;
|
||||
|
@ -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)
|
||||
|
@ -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 */
|
||||
|
Some files were not shown because too many files have changed in this diff Show More
Loading…
Reference in New Issue
Block a user