2005-04-17 02:20:36 +04:00
|
|
|
/*
|
|
|
|
* (C) Copyright Linus Torvalds 1999
|
|
|
|
* (C) Copyright Johannes Erdfelt 1999-2001
|
|
|
|
* (C) Copyright Andreas Gal 1999
|
|
|
|
* (C) Copyright Gregory P. Smith 1999
|
|
|
|
* (C) Copyright Deti Fliegl 1999
|
|
|
|
* (C) Copyright Randy Dunlap 2000
|
|
|
|
* (C) Copyright David Brownell 2000-2002
|
2013-10-05 20:02:07 +04:00
|
|
|
*
|
2005-04-17 02:20:36 +04:00
|
|
|
* 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.
|
|
|
|
*/
|
|
|
|
|
2012-09-07 16:31:45 +04:00
|
|
|
#include <linux/bcd.h>
|
2005-04-17 02:20:36 +04:00
|
|
|
#include <linux/module.h>
|
|
|
|
#include <linux/version.h>
|
|
|
|
#include <linux/kernel.h>
|
2017-04-26 03:56:11 +03:00
|
|
|
#include <linux/sched/task_stack.h>
|
2005-04-17 02:20:36 +04:00
|
|
|
#include <linux/slab.h>
|
|
|
|
#include <linux/completion.h>
|
|
|
|
#include <linux/utsname.h>
|
|
|
|
#include <linux/mm.h>
|
|
|
|
#include <asm/io.h>
|
|
|
|
#include <linux/device.h>
|
|
|
|
#include <linux/dma-mapping.h>
|
2006-01-11 17:55:29 +03:00
|
|
|
#include <linux/mutex.h>
|
2005-04-17 02:20:36 +04:00
|
|
|
#include <asm/irq.h>
|
|
|
|
#include <asm/byteorder.h>
|
2008-01-23 09:58:35 +03:00
|
|
|
#include <asm/unaligned.h>
|
USB: Properly unregister reboot notifier in case of failure in ehci hcd
If some problem occurs during ehci startup, for instance, request_irq fails,
echi hcd driver tries it best to cleanup, but fails to unregister reboot
notifier, which in turn leads to crash on reboot/poweroff.
The following patch resolves this problem by not using reboot notifiers
anymore, but instead making ehci/ohci driver get its own shutdown method. For
PCI, it is done through pci glue, for everything else through platform driver
glue.
One downside: sa1111 does not use platform driver stuff, and does not have its
own shutdown hook, so no 'shutdown' is called for it now. I'm not sure if it
is really necessary on that platform, though.
Signed-off-by: Aleks Gorelov <dared1st@yahoo.com>
Cc: Alan Stern <stern@rowland.harvard.edu>
Cc: David Brownell <david-b@pacbell.net>
Signed-off-by: Andrew Morton <akpm@osdl.org>
Signed-off-by: Greg Kroah-Hartman <gregkh@suse.de>
2006-08-09 04:24:08 +04:00
|
|
|
#include <linux/platform_device.h>
|
2007-03-13 23:37:30 +03:00
|
|
|
#include <linux/workqueue.h>
|
2013-01-26 02:09:42 +04:00
|
|
|
#include <linux/pm_runtime.h>
|
2013-10-06 01:09:15 +04:00
|
|
|
#include <linux/types.h>
|
2005-04-17 02:20:36 +04:00
|
|
|
|
2014-09-24 23:09:44 +04:00
|
|
|
#include <linux/phy/phy.h>
|
2005-04-17 02:20:36 +04:00
|
|
|
#include <linux/usb.h>
|
2010-04-25 01:21:52 +04:00
|
|
|
#include <linux/usb/hcd.h>
|
2013-12-04 01:42:21 +04:00
|
|
|
#include <linux/usb/phy.h>
|
2016-09-23 16:44:13 +03:00
|
|
|
#include <linux/usb/otg.h>
|
2005-04-17 02:20:36 +04:00
|
|
|
|
|
|
|
#include "usb.h"
|
|
|
|
|
|
|
|
|
|
|
|
/*-------------------------------------------------------------------------*/
|
|
|
|
|
|
|
|
/*
|
|
|
|
* USB Host Controller Driver framework
|
|
|
|
*
|
|
|
|
* Plugs into usbcore (usb_bus) and lets HCDs share code, minimizing
|
|
|
|
* HCD-specific behaviors/bugs.
|
|
|
|
*
|
|
|
|
* This does error checks, tracks devices and urbs, and delegates to a
|
|
|
|
* "hc_driver" only for code (and data) that really needs to know about
|
|
|
|
* hardware differences. That includes root hub registers, i/o queues,
|
|
|
|
* and so on ... but as little else as possible.
|
|
|
|
*
|
|
|
|
* Shared code includes most of the "root hub" code (these are emulated,
|
|
|
|
* though each HC's hardware works differently) and PCI glue, plus request
|
|
|
|
* tracking overhead. The HCD code should only block on spinlocks or on
|
|
|
|
* hardware handshaking; blocking on software events (such as other kernel
|
|
|
|
* threads releasing resources, or completing actions) is all generic.
|
|
|
|
*
|
|
|
|
* Happens the USB 2.0 spec says this would be invisible inside the "USBD",
|
|
|
|
* and includes mostly a "HCDI" (HCD Interface) along with some APIs used
|
|
|
|
* only by the hub driver ... and that neither should be seen or used by
|
|
|
|
* usb client device drivers.
|
|
|
|
*
|
|
|
|
* Contributors of ideas or unattributed patches include: David Brownell,
|
|
|
|
* Roman Weissgaerber, Rory Bolt, Greg Kroah-Hartman, ...
|
|
|
|
*
|
|
|
|
* HISTORY:
|
|
|
|
* 2002-02-21 Pull in most of the usb_bus support from usb.c; some
|
|
|
|
* associated cleanup. "usb_hcd" still != "usb_bus".
|
|
|
|
* 2001-12-12 Initial patch version for Linux 2.5.1 kernel.
|
|
|
|
*/
|
|
|
|
|
|
|
|
/*-------------------------------------------------------------------------*/
|
|
|
|
|
2008-10-02 19:48:13 +04:00
|
|
|
/* Keep track of which host controller drivers are loaded */
|
|
|
|
unsigned long usb_hcds_loaded;
|
|
|
|
EXPORT_SYMBOL_GPL(usb_hcds_loaded);
|
|
|
|
|
2005-04-17 02:20:36 +04:00
|
|
|
/* host controllers we manage */
|
2016-01-25 22:30:30 +03:00
|
|
|
DEFINE_IDR (usb_bus_idr);
|
|
|
|
EXPORT_SYMBOL_GPL (usb_bus_idr);
|
2005-04-17 02:20:36 +04:00
|
|
|
|
|
|
|
/* used when allocating bus numbers */
|
|
|
|
#define USB_MAXBUS 64
|
|
|
|
|
|
|
|
/* used when updating list of hcds */
|
2016-02-04 01:35:23 +03:00
|
|
|
DEFINE_MUTEX(usb_bus_idr_lock); /* exported only for usbfs */
|
|
|
|
EXPORT_SYMBOL_GPL (usb_bus_idr_lock);
|
2005-04-17 02:20:36 +04:00
|
|
|
|
|
|
|
/* used for controlling access to virtual root hubs */
|
|
|
|
static DEFINE_SPINLOCK(hcd_root_hub_lock);
|
|
|
|
|
2007-07-18 20:14:24 +04:00
|
|
|
/* used when updating an endpoint's URB list */
|
|
|
|
static DEFINE_SPINLOCK(hcd_urb_list_lock);
|
2005-04-17 02:20:36 +04:00
|
|
|
|
2008-10-21 23:28:46 +04:00
|
|
|
/* used to protect against unlinking URBs after the device is gone */
|
|
|
|
static DEFINE_SPINLOCK(hcd_urb_unlink_lock);
|
|
|
|
|
2005-04-17 02:20:36 +04:00
|
|
|
/* wait queue for synchronous unlinks */
|
|
|
|
DECLARE_WAIT_QUEUE_HEAD(usb_kill_urb_queue);
|
|
|
|
|
2007-07-18 20:14:24 +04:00
|
|
|
static inline int is_root_hub(struct usb_device *udev)
|
|
|
|
{
|
|
|
|
return (udev->parent == NULL);
|
|
|
|
}
|
|
|
|
|
2005-04-17 02:20:36 +04:00
|
|
|
/*-------------------------------------------------------------------------*/
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Sharable chunks of root hub code.
|
|
|
|
*/
|
|
|
|
|
|
|
|
/*-------------------------------------------------------------------------*/
|
2012-09-07 16:31:45 +04:00
|
|
|
#define KERNEL_REL bin2bcd(((LINUX_VERSION_CODE >> 16) & 0x0ff))
|
|
|
|
#define KERNEL_VER bin2bcd(((LINUX_VERSION_CODE >> 8) & 0x0ff))
|
2005-04-17 02:20:36 +04:00
|
|
|
|
2015-12-10 10:59:28 +03:00
|
|
|
/* usb 3.1 root hub device descriptor */
|
|
|
|
static const u8 usb31_rh_dev_descriptor[18] = {
|
|
|
|
0x12, /* __u8 bLength; */
|
|
|
|
USB_DT_DEVICE, /* __u8 bDescriptorType; Device */
|
|
|
|
0x10, 0x03, /* __le16 bcdUSB; v3.1 */
|
|
|
|
|
|
|
|
0x09, /* __u8 bDeviceClass; HUB_CLASSCODE */
|
|
|
|
0x00, /* __u8 bDeviceSubClass; */
|
|
|
|
0x03, /* __u8 bDeviceProtocol; USB 3 hub */
|
|
|
|
0x09, /* __u8 bMaxPacketSize0; 2^9 = 512 Bytes */
|
|
|
|
|
|
|
|
0x6b, 0x1d, /* __le16 idVendor; Linux Foundation 0x1d6b */
|
|
|
|
0x03, 0x00, /* __le16 idProduct; device 0x0003 */
|
|
|
|
KERNEL_VER, KERNEL_REL, /* __le16 bcdDevice */
|
|
|
|
|
|
|
|
0x03, /* __u8 iManufacturer; */
|
|
|
|
0x02, /* __u8 iProduct; */
|
|
|
|
0x01, /* __u8 iSerialNumber; */
|
|
|
|
0x01 /* __u8 bNumConfigurations; */
|
|
|
|
};
|
|
|
|
|
2009-04-28 06:55:01 +04:00
|
|
|
/* usb 3.0 root hub device descriptor */
|
|
|
|
static const u8 usb3_rh_dev_descriptor[18] = {
|
|
|
|
0x12, /* __u8 bLength; */
|
2015-10-04 21:11:35 +03:00
|
|
|
USB_DT_DEVICE, /* __u8 bDescriptorType; Device */
|
2009-04-28 06:55:01 +04:00
|
|
|
0x00, 0x03, /* __le16 bcdUSB; v3.0 */
|
|
|
|
|
|
|
|
0x09, /* __u8 bDeviceClass; HUB_CLASSCODE */
|
|
|
|
0x00, /* __u8 bDeviceSubClass; */
|
|
|
|
0x03, /* __u8 bDeviceProtocol; USB 3.0 hub */
|
|
|
|
0x09, /* __u8 bMaxPacketSize0; 2^9 = 512 Bytes */
|
|
|
|
|
2012-05-05 15:16:51 +04:00
|
|
|
0x6b, 0x1d, /* __le16 idVendor; Linux Foundation 0x1d6b */
|
2010-02-25 21:19:37 +03:00
|
|
|
0x03, 0x00, /* __le16 idProduct; device 0x0003 */
|
2009-04-28 06:55:01 +04:00
|
|
|
KERNEL_VER, KERNEL_REL, /* __le16 bcdDevice */
|
|
|
|
|
|
|
|
0x03, /* __u8 iManufacturer; */
|
|
|
|
0x02, /* __u8 iProduct; */
|
|
|
|
0x01, /* __u8 iSerialNumber; */
|
|
|
|
0x01 /* __u8 bNumConfigurations; */
|
|
|
|
};
|
|
|
|
|
2013-05-31 23:16:13 +04:00
|
|
|
/* usb 2.5 (wireless USB 1.0) root hub device descriptor */
|
|
|
|
static const u8 usb25_rh_dev_descriptor[18] = {
|
|
|
|
0x12, /* __u8 bLength; */
|
2015-10-04 21:11:35 +03:00
|
|
|
USB_DT_DEVICE, /* __u8 bDescriptorType; Device */
|
2013-05-31 23:16:13 +04:00
|
|
|
0x50, 0x02, /* __le16 bcdUSB; v2.5 */
|
|
|
|
|
|
|
|
0x09, /* __u8 bDeviceClass; HUB_CLASSCODE */
|
|
|
|
0x00, /* __u8 bDeviceSubClass; */
|
|
|
|
0x00, /* __u8 bDeviceProtocol; [ usb 2.0 no TT ] */
|
|
|
|
0xFF, /* __u8 bMaxPacketSize0; always 0xFF (WUSB Spec 7.4.1). */
|
|
|
|
|
|
|
|
0x6b, 0x1d, /* __le16 idVendor; Linux Foundation 0x1d6b */
|
|
|
|
0x02, 0x00, /* __le16 idProduct; device 0x0002 */
|
|
|
|
KERNEL_VER, KERNEL_REL, /* __le16 bcdDevice */
|
|
|
|
|
|
|
|
0x03, /* __u8 iManufacturer; */
|
|
|
|
0x02, /* __u8 iProduct; */
|
|
|
|
0x01, /* __u8 iSerialNumber; */
|
|
|
|
0x01 /* __u8 bNumConfigurations; */
|
|
|
|
};
|
|
|
|
|
2005-04-17 02:20:36 +04:00
|
|
|
/* usb 2.0 root hub device descriptor */
|
2013-10-05 20:02:07 +04:00
|
|
|
static const u8 usb2_rh_dev_descriptor[18] = {
|
2005-04-17 02:20:36 +04:00
|
|
|
0x12, /* __u8 bLength; */
|
2015-10-04 21:11:35 +03:00
|
|
|
USB_DT_DEVICE, /* __u8 bDescriptorType; Device */
|
2005-04-17 02:20:36 +04:00
|
|
|
0x00, 0x02, /* __le16 bcdUSB; v2.0 */
|
|
|
|
|
|
|
|
0x09, /* __u8 bDeviceClass; HUB_CLASSCODE */
|
|
|
|
0x00, /* __u8 bDeviceSubClass; */
|
2008-04-04 02:02:56 +04:00
|
|
|
0x00, /* __u8 bDeviceProtocol; [ usb 2.0 no TT ] */
|
2005-10-24 23:41:19 +04:00
|
|
|
0x40, /* __u8 bMaxPacketSize0; 64 Bytes */
|
2005-04-17 02:20:36 +04:00
|
|
|
|
2012-05-05 15:16:51 +04:00
|
|
|
0x6b, 0x1d, /* __le16 idVendor; Linux Foundation 0x1d6b */
|
2008-01-28 20:50:12 +03:00
|
|
|
0x02, 0x00, /* __le16 idProduct; device 0x0002 */
|
2005-04-17 02:20:36 +04:00
|
|
|
KERNEL_VER, KERNEL_REL, /* __le16 bcdDevice */
|
|
|
|
|
|
|
|
0x03, /* __u8 iManufacturer; */
|
|
|
|
0x02, /* __u8 iProduct; */
|
|
|
|
0x01, /* __u8 iSerialNumber; */
|
|
|
|
0x01 /* __u8 bNumConfigurations; */
|
|
|
|
};
|
|
|
|
|
|
|
|
/* no usb 2.0 root hub "device qualifier" descriptor: one speed only */
|
|
|
|
|
|
|
|
/* usb 1.1 root hub device descriptor */
|
2013-10-05 20:02:07 +04:00
|
|
|
static const u8 usb11_rh_dev_descriptor[18] = {
|
2005-04-17 02:20:36 +04:00
|
|
|
0x12, /* __u8 bLength; */
|
2015-10-04 21:11:35 +03:00
|
|
|
USB_DT_DEVICE, /* __u8 bDescriptorType; Device */
|
2005-04-17 02:20:36 +04:00
|
|
|
0x10, 0x01, /* __le16 bcdUSB; v1.1 */
|
|
|
|
|
|
|
|
0x09, /* __u8 bDeviceClass; HUB_CLASSCODE */
|
|
|
|
0x00, /* __u8 bDeviceSubClass; */
|
|
|
|
0x00, /* __u8 bDeviceProtocol; [ low/full speeds only ] */
|
2005-10-24 23:41:19 +04:00
|
|
|
0x40, /* __u8 bMaxPacketSize0; 64 Bytes */
|
2005-04-17 02:20:36 +04:00
|
|
|
|
2012-05-05 15:16:51 +04:00
|
|
|
0x6b, 0x1d, /* __le16 idVendor; Linux Foundation 0x1d6b */
|
2008-01-28 20:50:12 +03:00
|
|
|
0x01, 0x00, /* __le16 idProduct; device 0x0001 */
|
2005-04-17 02:20:36 +04:00
|
|
|
KERNEL_VER, KERNEL_REL, /* __le16 bcdDevice */
|
|
|
|
|
|
|
|
0x03, /* __u8 iManufacturer; */
|
|
|
|
0x02, /* __u8 iProduct; */
|
|
|
|
0x01, /* __u8 iSerialNumber; */
|
|
|
|
0x01 /* __u8 bNumConfigurations; */
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
|
|
/*-------------------------------------------------------------------------*/
|
|
|
|
|
|
|
|
/* Configuration descriptors for our root hubs */
|
|
|
|
|
2013-10-05 20:02:07 +04:00
|
|
|
static const u8 fs_rh_config_descriptor[] = {
|
2005-04-17 02:20:36 +04:00
|
|
|
|
|
|
|
/* one configuration */
|
|
|
|
0x09, /* __u8 bLength; */
|
2015-10-04 21:11:35 +03:00
|
|
|
USB_DT_CONFIG, /* __u8 bDescriptorType; Configuration */
|
2005-04-17 02:20:36 +04:00
|
|
|
0x19, 0x00, /* __le16 wTotalLength; */
|
|
|
|
0x01, /* __u8 bNumInterfaces; (1) */
|
|
|
|
0x01, /* __u8 bConfigurationValue; */
|
|
|
|
0x00, /* __u8 iConfiguration; */
|
2013-10-05 20:02:07 +04:00
|
|
|
0xc0, /* __u8 bmAttributes;
|
2005-04-17 02:20:36 +04:00
|
|
|
Bit 7: must be set,
|
|
|
|
6: Self-powered,
|
|
|
|
5: Remote wakeup,
|
|
|
|
4..0: resvd */
|
|
|
|
0x00, /* __u8 MaxPower; */
|
2013-10-05 20:02:07 +04:00
|
|
|
|
2005-04-17 02:20:36 +04:00
|
|
|
/* USB 1.1:
|
|
|
|
* USB 2.0, single TT organization (mandatory):
|
|
|
|
* one interface, protocol 0
|
|
|
|
*
|
|
|
|
* USB 2.0, multiple TT organization (optional):
|
|
|
|
* two interfaces, protocols 1 (like single TT)
|
|
|
|
* and 2 (multiple TT mode) ... config is
|
|
|
|
* sometimes settable
|
|
|
|
* NOT IMPLEMENTED
|
|
|
|
*/
|
|
|
|
|
|
|
|
/* one interface */
|
|
|
|
0x09, /* __u8 if_bLength; */
|
2015-10-04 21:11:35 +03:00
|
|
|
USB_DT_INTERFACE, /* __u8 if_bDescriptorType; Interface */
|
2005-04-17 02:20:36 +04:00
|
|
|
0x00, /* __u8 if_bInterfaceNumber; */
|
|
|
|
0x00, /* __u8 if_bAlternateSetting; */
|
|
|
|
0x01, /* __u8 if_bNumEndpoints; */
|
|
|
|
0x09, /* __u8 if_bInterfaceClass; HUB_CLASSCODE */
|
|
|
|
0x00, /* __u8 if_bInterfaceSubClass; */
|
|
|
|
0x00, /* __u8 if_bInterfaceProtocol; [usb1.1 or single tt] */
|
|
|
|
0x00, /* __u8 if_iInterface; */
|
2013-10-05 20:02:07 +04:00
|
|
|
|
2005-04-17 02:20:36 +04:00
|
|
|
/* one endpoint (status change endpoint) */
|
|
|
|
0x07, /* __u8 ep_bLength; */
|
2015-10-04 21:11:35 +03:00
|
|
|
USB_DT_ENDPOINT, /* __u8 ep_bDescriptorType; Endpoint */
|
2005-04-17 02:20:36 +04:00
|
|
|
0x81, /* __u8 ep_bEndpointAddress; IN Endpoint 1 */
|
2013-10-05 20:02:07 +04:00
|
|
|
0x03, /* __u8 ep_bmAttributes; Interrupt */
|
|
|
|
0x02, 0x00, /* __le16 ep_wMaxPacketSize; 1 + (MAX_ROOT_PORTS / 8) */
|
2005-04-17 02:20:36 +04:00
|
|
|
0xff /* __u8 ep_bInterval; (255ms -- usb 2.0 spec) */
|
|
|
|
};
|
|
|
|
|
2013-10-05 20:02:07 +04:00
|
|
|
static const u8 hs_rh_config_descriptor[] = {
|
2005-04-17 02:20:36 +04:00
|
|
|
|
|
|
|
/* one configuration */
|
|
|
|
0x09, /* __u8 bLength; */
|
2015-10-04 21:11:35 +03:00
|
|
|
USB_DT_CONFIG, /* __u8 bDescriptorType; Configuration */
|
2005-04-17 02:20:36 +04:00
|
|
|
0x19, 0x00, /* __le16 wTotalLength; */
|
|
|
|
0x01, /* __u8 bNumInterfaces; (1) */
|
|
|
|
0x01, /* __u8 bConfigurationValue; */
|
|
|
|
0x00, /* __u8 iConfiguration; */
|
2013-10-05 20:02:07 +04:00
|
|
|
0xc0, /* __u8 bmAttributes;
|
2005-04-17 02:20:36 +04:00
|
|
|
Bit 7: must be set,
|
|
|
|
6: Self-powered,
|
|
|
|
5: Remote wakeup,
|
|
|
|
4..0: resvd */
|
|
|
|
0x00, /* __u8 MaxPower; */
|
2013-10-05 20:02:07 +04:00
|
|
|
|
2005-04-17 02:20:36 +04:00
|
|
|
/* USB 1.1:
|
|
|
|
* USB 2.0, single TT organization (mandatory):
|
|
|
|
* one interface, protocol 0
|
|
|
|
*
|
|
|
|
* USB 2.0, multiple TT organization (optional):
|
|
|
|
* two interfaces, protocols 1 (like single TT)
|
|
|
|
* and 2 (multiple TT mode) ... config is
|
|
|
|
* sometimes settable
|
|
|
|
* NOT IMPLEMENTED
|
|
|
|
*/
|
|
|
|
|
|
|
|
/* one interface */
|
|
|
|
0x09, /* __u8 if_bLength; */
|
2015-10-04 21:11:35 +03:00
|
|
|
USB_DT_INTERFACE, /* __u8 if_bDescriptorType; Interface */
|
2005-04-17 02:20:36 +04:00
|
|
|
0x00, /* __u8 if_bInterfaceNumber; */
|
|
|
|
0x00, /* __u8 if_bAlternateSetting; */
|
|
|
|
0x01, /* __u8 if_bNumEndpoints; */
|
|
|
|
0x09, /* __u8 if_bInterfaceClass; HUB_CLASSCODE */
|
|
|
|
0x00, /* __u8 if_bInterfaceSubClass; */
|
|
|
|
0x00, /* __u8 if_bInterfaceProtocol; [usb1.1 or single tt] */
|
|
|
|
0x00, /* __u8 if_iInterface; */
|
2013-10-05 20:02:07 +04:00
|
|
|
|
2005-04-17 02:20:36 +04:00
|
|
|
/* one endpoint (status change endpoint) */
|
|
|
|
0x07, /* __u8 ep_bLength; */
|
2015-10-04 21:11:35 +03:00
|
|
|
USB_DT_ENDPOINT, /* __u8 ep_bDescriptorType; Endpoint */
|
2005-04-17 02:20:36 +04:00
|
|
|
0x81, /* __u8 ep_bEndpointAddress; IN Endpoint 1 */
|
2013-10-05 20:02:07 +04:00
|
|
|
0x03, /* __u8 ep_bmAttributes; Interrupt */
|
2006-10-12 07:05:59 +04:00
|
|
|
/* __le16 ep_wMaxPacketSize; 1 + (MAX_ROOT_PORTS / 8)
|
|
|
|
* see hub.c:hub_configure() for details. */
|
|
|
|
(USB_MAXCHILDREN + 1 + 7) / 8, 0x00,
|
2005-04-17 02:20:36 +04:00
|
|
|
0x0c /* __u8 ep_bInterval; (256ms -- usb 2.0 spec) */
|
|
|
|
};
|
|
|
|
|
2009-04-28 06:55:01 +04:00
|
|
|
static const u8 ss_rh_config_descriptor[] = {
|
|
|
|
/* one configuration */
|
|
|
|
0x09, /* __u8 bLength; */
|
2015-10-04 21:11:35 +03:00
|
|
|
USB_DT_CONFIG, /* __u8 bDescriptorType; Configuration */
|
2010-09-13 23:01:02 +04:00
|
|
|
0x1f, 0x00, /* __le16 wTotalLength; */
|
2009-04-28 06:55:01 +04:00
|
|
|
0x01, /* __u8 bNumInterfaces; (1) */
|
|
|
|
0x01, /* __u8 bConfigurationValue; */
|
|
|
|
0x00, /* __u8 iConfiguration; */
|
|
|
|
0xc0, /* __u8 bmAttributes;
|
|
|
|
Bit 7: must be set,
|
|
|
|
6: Self-powered,
|
|
|
|
5: Remote wakeup,
|
|
|
|
4..0: resvd */
|
|
|
|
0x00, /* __u8 MaxPower; */
|
|
|
|
|
|
|
|
/* one interface */
|
|
|
|
0x09, /* __u8 if_bLength; */
|
2015-10-04 21:11:35 +03:00
|
|
|
USB_DT_INTERFACE, /* __u8 if_bDescriptorType; Interface */
|
2009-04-28 06:55:01 +04:00
|
|
|
0x00, /* __u8 if_bInterfaceNumber; */
|
|
|
|
0x00, /* __u8 if_bAlternateSetting; */
|
|
|
|
0x01, /* __u8 if_bNumEndpoints; */
|
|
|
|
0x09, /* __u8 if_bInterfaceClass; HUB_CLASSCODE */
|
|
|
|
0x00, /* __u8 if_bInterfaceSubClass; */
|
|
|
|
0x00, /* __u8 if_bInterfaceProtocol; */
|
|
|
|
0x00, /* __u8 if_iInterface; */
|
|
|
|
|
|
|
|
/* one endpoint (status change endpoint) */
|
|
|
|
0x07, /* __u8 ep_bLength; */
|
2015-10-04 21:11:35 +03:00
|
|
|
USB_DT_ENDPOINT, /* __u8 ep_bDescriptorType; Endpoint */
|
2009-04-28 06:55:01 +04:00
|
|
|
0x81, /* __u8 ep_bEndpointAddress; IN Endpoint 1 */
|
|
|
|
0x03, /* __u8 ep_bmAttributes; Interrupt */
|
|
|
|
/* __le16 ep_wMaxPacketSize; 1 + (MAX_ROOT_PORTS / 8)
|
|
|
|
* see hub.c:hub_configure() for details. */
|
|
|
|
(USB_MAXCHILDREN + 1 + 7) / 8, 0x00,
|
2010-09-13 23:01:02 +04:00
|
|
|
0x0c, /* __u8 ep_bInterval; (256ms -- usb 2.0 spec) */
|
|
|
|
|
|
|
|
/* one SuperSpeed endpoint companion descriptor */
|
|
|
|
0x06, /* __u8 ss_bLength */
|
2015-10-04 21:11:35 +03:00
|
|
|
USB_DT_SS_ENDPOINT_COMP, /* __u8 ss_bDescriptorType; SuperSpeed EP */
|
|
|
|
/* Companion */
|
2010-09-13 23:01:02 +04:00
|
|
|
0x00, /* __u8 ss_bMaxBurst; allows 1 TX between ACKs */
|
|
|
|
0x00, /* __u8 ss_bmAttributes; 1 packet per service interval */
|
|
|
|
0x02, 0x00 /* __le16 ss_wBytesPerInterval; 15 bits for max 15 ports */
|
2009-04-28 06:55:01 +04:00
|
|
|
};
|
|
|
|
|
2011-05-31 23:31:08 +04:00
|
|
|
/* authorized_default behaviour:
|
|
|
|
* -1 is authorized for all devices except wireless (old behaviour)
|
|
|
|
* 0 is unauthorized for all devices
|
|
|
|
* 1 is authorized for all devices
|
|
|
|
*/
|
|
|
|
static int authorized_default = -1;
|
|
|
|
module_param(authorized_default, int, S_IRUGO|S_IWUSR);
|
|
|
|
MODULE_PARM_DESC(authorized_default,
|
|
|
|
"Default USB device authorization: 0 is not authorized, 1 is "
|
|
|
|
"authorized, -1 is authorized except for wireless USB (default, "
|
|
|
|
"old behaviour");
|
2005-04-17 02:20:36 +04:00
|
|
|
/*-------------------------------------------------------------------------*/
|
|
|
|
|
2009-08-25 06:06:41 +04:00
|
|
|
/**
|
|
|
|
* ascii2desc() - Helper routine for producing UTF-16LE string descriptors
|
|
|
|
* @s: Null-terminated ASCII (actually ISO-8859-1) string
|
|
|
|
* @buf: Buffer for USB string descriptor (header + UTF-16LE)
|
|
|
|
* @len: Length (in bytes; may be odd) of descriptor buffer.
|
|
|
|
*
|
2013-08-02 22:10:04 +04:00
|
|
|
* Return: The number of bytes filled in: 2 + 2*strlen(s) or @len,
|
|
|
|
* whichever is less.
|
2009-08-25 06:06:41 +04:00
|
|
|
*
|
2013-08-02 22:10:04 +04:00
|
|
|
* Note:
|
2009-08-25 06:06:41 +04:00
|
|
|
* USB String descriptors can contain at most 126 characters; input
|
|
|
|
* strings longer than that are truncated.
|
2005-04-17 02:20:36 +04:00
|
|
|
*/
|
2009-08-25 06:06:41 +04:00
|
|
|
static unsigned
|
|
|
|
ascii2desc(char const *s, u8 *buf, unsigned len)
|
2005-04-17 02:20:36 +04:00
|
|
|
{
|
2009-08-25 06:06:41 +04:00
|
|
|
unsigned n, t = 2 + 2*strlen(s);
|
|
|
|
|
|
|
|
if (t > 254)
|
|
|
|
t = 254; /* Longest possible UTF string descriptor */
|
|
|
|
if (len > t)
|
|
|
|
len = t;
|
|
|
|
|
|
|
|
t += USB_DT_STRING << 8; /* Now t is first 16 bits to store */
|
|
|
|
|
|
|
|
n = len;
|
|
|
|
while (n--) {
|
|
|
|
*buf++ = t;
|
|
|
|
if (!n--)
|
|
|
|
break;
|
|
|
|
*buf++ = t >> 8;
|
|
|
|
t = (unsigned char)*s++;
|
2005-04-17 02:20:36 +04:00
|
|
|
}
|
2009-08-25 06:06:41 +04:00
|
|
|
return len;
|
2005-04-17 02:20:36 +04:00
|
|
|
}
|
|
|
|
|
2009-08-25 06:06:41 +04:00
|
|
|
/**
|
|
|
|
* rh_string() - provides string descriptors for root hub
|
|
|
|
* @id: the string ID number (0: langids, 1: serial #, 2: product, 3: vendor)
|
2005-04-17 02:20:36 +04:00
|
|
|
* @hcd: the host controller for this root hub
|
2009-08-25 06:06:41 +04:00
|
|
|
* @data: buffer for output packet
|
|
|
|
* @len: length of the provided buffer
|
2005-04-17 02:20:36 +04:00
|
|
|
*
|
|
|
|
* Produces either a manufacturer, product or serial number string for the
|
|
|
|
* virtual root hub device.
|
2013-08-02 22:10:04 +04:00
|
|
|
*
|
|
|
|
* Return: The number of bytes filled in: the length of the descriptor or
|
2009-08-25 06:06:41 +04:00
|
|
|
* of the provided buffer, whichever is less.
|
2005-04-17 02:20:36 +04:00
|
|
|
*/
|
2009-08-25 06:06:41 +04:00
|
|
|
static unsigned
|
|
|
|
rh_string(int id, struct usb_hcd const *hcd, u8 *data, unsigned len)
|
2009-03-13 14:19:18 +03:00
|
|
|
{
|
2009-08-25 06:06:41 +04:00
|
|
|
char buf[100];
|
|
|
|
char const *s;
|
|
|
|
static char const langids[4] = {4, USB_DT_STRING, 0x09, 0x04};
|
2005-04-17 02:20:36 +04:00
|
|
|
|
2013-10-05 20:02:08 +04:00
|
|
|
/* language ids */
|
2009-08-25 06:06:41 +04:00
|
|
|
switch (id) {
|
|
|
|
case 0:
|
|
|
|
/* Array of LANGID codes (0x0409 is MSFT-speak for "en-us") */
|
|
|
|
/* See http://www.usb.org/developers/docs/USB_LANGIDs.pdf */
|
|
|
|
if (len > 4)
|
|
|
|
len = 4;
|
|
|
|
memcpy(data, langids, len);
|
2005-04-17 02:20:36 +04:00
|
|
|
return len;
|
2009-08-25 06:06:41 +04:00
|
|
|
case 1:
|
|
|
|
/* Serial number */
|
|
|
|
s = hcd->self.bus_name;
|
|
|
|
break;
|
|
|
|
case 2:
|
|
|
|
/* Product name */
|
|
|
|
s = hcd->product_desc;
|
|
|
|
break;
|
|
|
|
case 3:
|
|
|
|
/* Manufacturer */
|
2006-10-02 13:18:13 +04:00
|
|
|
snprintf (buf, sizeof buf, "%s %s %s", init_utsname()->sysname,
|
|
|
|
init_utsname()->release, hcd->driver->description);
|
2009-08-25 06:06:41 +04:00
|
|
|
s = buf;
|
|
|
|
break;
|
2005-04-17 02:20:36 +04:00
|
|
|
default:
|
2009-08-25 06:06:41 +04:00
|
|
|
/* Can't happen; caller guarantees it */
|
|
|
|
return 0;
|
2005-04-17 02:20:36 +04:00
|
|
|
}
|
2009-08-25 06:06:41 +04:00
|
|
|
|
|
|
|
return ascii2desc(s, data, len);
|
2005-04-17 02:20:36 +04:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/* Root hub control transfers execute synchronously */
|
|
|
|
static int rh_call_control (struct usb_hcd *hcd, struct urb *urb)
|
|
|
|
{
|
|
|
|
struct usb_ctrlrequest *cmd;
|
2013-10-05 20:02:07 +04:00
|
|
|
u16 typeReq, wValue, wIndex, wLength;
|
2005-04-17 02:20:36 +04:00
|
|
|
u8 *ubuf = urb->transfer_buffer;
|
2009-03-13 14:19:18 +03:00
|
|
|
unsigned len = 0;
|
2007-08-08 19:48:02 +04:00
|
|
|
int status;
|
2008-04-04 02:02:56 +04:00
|
|
|
u8 patch_wakeup = 0;
|
|
|
|
u8 patch_protocol = 0;
|
2013-08-13 21:36:12 +04:00
|
|
|
u16 tbuf_size;
|
|
|
|
u8 *tbuf = NULL;
|
|
|
|
const u8 *bufp;
|
2005-04-17 02:20:36 +04:00
|
|
|
|
2007-08-02 23:05:45 +04:00
|
|
|
might_sleep();
|
|
|
|
|
2007-08-08 19:48:02 +04:00
|
|
|
spin_lock_irq(&hcd_root_hub_lock);
|
|
|
|
status = usb_hcd_link_urb_to_ep(hcd, urb);
|
|
|
|
spin_unlock_irq(&hcd_root_hub_lock);
|
|
|
|
if (status)
|
|
|
|
return status;
|
2007-08-21 23:39:21 +04:00
|
|
|
urb->hcpriv = hcd; /* Indicate it's queued */
|
2007-08-08 19:48:02 +04:00
|
|
|
|
2005-04-17 02:20:36 +04:00
|
|
|
cmd = (struct usb_ctrlrequest *) urb->setup_packet;
|
|
|
|
typeReq = (cmd->bRequestType << 8) | cmd->bRequest;
|
|
|
|
wValue = le16_to_cpu (cmd->wValue);
|
|
|
|
wIndex = le16_to_cpu (cmd->wIndex);
|
|
|
|
wLength = le16_to_cpu (cmd->wLength);
|
|
|
|
|
|
|
|
if (wLength > urb->transfer_buffer_length)
|
|
|
|
goto error;
|
|
|
|
|
2013-08-13 21:36:12 +04:00
|
|
|
/*
|
|
|
|
* tbuf should be at least as big as the
|
|
|
|
* USB hub descriptor.
|
|
|
|
*/
|
|
|
|
tbuf_size = max_t(u16, sizeof(struct usb_hub_descriptor), wLength);
|
|
|
|
tbuf = kzalloc(tbuf_size, GFP_KERNEL);
|
2017-03-24 20:38:28 +03:00
|
|
|
if (!tbuf) {
|
|
|
|
status = -ENOMEM;
|
|
|
|
goto err_alloc;
|
|
|
|
}
|
2013-08-13 21:36:12 +04:00
|
|
|
|
|
|
|
bufp = tbuf;
|
|
|
|
|
|
|
|
|
2005-04-17 02:20:36 +04:00
|
|
|
urb->actual_length = 0;
|
|
|
|
switch (typeReq) {
|
|
|
|
|
|
|
|
/* DEVICE REQUESTS */
|
|
|
|
|
2006-01-24 19:40:27 +03:00
|
|
|
/* The root hub's remote wakeup enable bit is implemented using
|
|
|
|
* driver model wakeup flags. If this system supports wakeup
|
|
|
|
* through USB, userspace may change the default "allow wakeup"
|
|
|
|
* policy through sysfs or these calls.
|
|
|
|
*
|
|
|
|
* Most root hubs support wakeup from downstream devices, for
|
|
|
|
* runtime power management (disabling USB clocks and reducing
|
|
|
|
* VBUS power usage). However, not all of them do so; silicon,
|
|
|
|
* board, and BIOS bugs here are not uncommon, so these can't
|
|
|
|
* be treated quite like external hubs.
|
|
|
|
*
|
|
|
|
* Likewise, not all root hubs will pass wakeup events upstream,
|
|
|
|
* to wake up the whole system. So don't assume root hub and
|
|
|
|
* controller capabilities are identical.
|
|
|
|
*/
|
|
|
|
|
2005-04-17 02:20:36 +04:00
|
|
|
case DeviceRequest | USB_REQ_GET_STATUS:
|
2013-10-05 20:02:07 +04:00
|
|
|
tbuf[0] = (device_may_wakeup(&hcd->self.root_hub->dev)
|
2006-01-24 19:40:27 +03:00
|
|
|
<< USB_DEVICE_REMOTE_WAKEUP)
|
2005-04-17 02:20:36 +04:00
|
|
|
| (1 << USB_DEVICE_SELF_POWERED);
|
2013-10-05 20:02:07 +04:00
|
|
|
tbuf[1] = 0;
|
2005-04-17 02:20:36 +04:00
|
|
|
len = 2;
|
|
|
|
break;
|
|
|
|
case DeviceOutRequest | USB_REQ_CLEAR_FEATURE:
|
|
|
|
if (wValue == USB_DEVICE_REMOTE_WAKEUP)
|
2006-01-24 19:40:27 +03:00
|
|
|
device_set_wakeup_enable(&hcd->self.root_hub->dev, 0);
|
2005-04-17 02:20:36 +04:00
|
|
|
else
|
|
|
|
goto error;
|
|
|
|
break;
|
|
|
|
case DeviceOutRequest | USB_REQ_SET_FEATURE:
|
2006-01-24 19:40:27 +03:00
|
|
|
if (device_can_wakeup(&hcd->self.root_hub->dev)
|
|
|
|
&& wValue == USB_DEVICE_REMOTE_WAKEUP)
|
|
|
|
device_set_wakeup_enable(&hcd->self.root_hub->dev, 1);
|
2005-04-17 02:20:36 +04:00
|
|
|
else
|
|
|
|
goto error;
|
|
|
|
break;
|
|
|
|
case DeviceRequest | USB_REQ_GET_CONFIGURATION:
|
2013-10-05 20:02:07 +04:00
|
|
|
tbuf[0] = 1;
|
2005-04-17 02:20:36 +04:00
|
|
|
len = 1;
|
|
|
|
/* FALLTHROUGH */
|
|
|
|
case DeviceOutRequest | USB_REQ_SET_CONFIGURATION:
|
|
|
|
break;
|
|
|
|
case DeviceRequest | USB_REQ_GET_DESCRIPTOR:
|
|
|
|
switch (wValue & 0xff00) {
|
|
|
|
case USB_DT_DEVICE << 8:
|
2010-12-03 01:45:18 +03:00
|
|
|
switch (hcd->speed) {
|
2015-10-01 18:40:37 +03:00
|
|
|
case HCD_USB31:
|
2015-12-10 10:59:28 +03:00
|
|
|
bufp = usb31_rh_dev_descriptor;
|
|
|
|
break;
|
2009-05-05 14:24:23 +04:00
|
|
|
case HCD_USB3:
|
2009-04-28 06:55:01 +04:00
|
|
|
bufp = usb3_rh_dev_descriptor;
|
2009-05-05 14:24:23 +04:00
|
|
|
break;
|
2013-05-31 23:16:13 +04:00
|
|
|
case HCD_USB25:
|
|
|
|
bufp = usb25_rh_dev_descriptor;
|
|
|
|
break;
|
2009-05-05 14:24:23 +04:00
|
|
|
case HCD_USB2:
|
2005-04-17 02:20:36 +04:00
|
|
|
bufp = usb2_rh_dev_descriptor;
|
2009-05-05 14:24:23 +04:00
|
|
|
break;
|
|
|
|
case HCD_USB11:
|
2005-04-17 02:20:36 +04:00
|
|
|
bufp = usb11_rh_dev_descriptor;
|
2009-05-05 14:24:23 +04:00
|
|
|
break;
|
|
|
|
default:
|
2005-04-17 02:20:36 +04:00
|
|
|
goto error;
|
2009-05-05 14:24:23 +04:00
|
|
|
}
|
2005-04-17 02:20:36 +04:00
|
|
|
len = 18;
|
2008-04-04 02:02:56 +04:00
|
|
|
if (hcd->has_tt)
|
|
|
|
patch_protocol = 1;
|
2005-04-17 02:20:36 +04:00
|
|
|
break;
|
|
|
|
case USB_DT_CONFIG << 8:
|
2010-12-03 01:45:18 +03:00
|
|
|
switch (hcd->speed) {
|
2015-10-01 18:40:37 +03:00
|
|
|
case HCD_USB31:
|
2009-05-05 14:24:23 +04:00
|
|
|
case HCD_USB3:
|
2009-04-28 06:55:01 +04:00
|
|
|
bufp = ss_rh_config_descriptor;
|
|
|
|
len = sizeof ss_rh_config_descriptor;
|
2009-05-05 14:24:23 +04:00
|
|
|
break;
|
2013-05-31 23:16:13 +04:00
|
|
|
case HCD_USB25:
|
2009-05-05 14:24:23 +04:00
|
|
|
case HCD_USB2:
|
2005-04-17 02:20:36 +04:00
|
|
|
bufp = hs_rh_config_descriptor;
|
|
|
|
len = sizeof hs_rh_config_descriptor;
|
2009-05-05 14:24:23 +04:00
|
|
|
break;
|
|
|
|
case HCD_USB11:
|
2005-04-17 02:20:36 +04:00
|
|
|
bufp = fs_rh_config_descriptor;
|
|
|
|
len = sizeof fs_rh_config_descriptor;
|
2009-05-05 14:24:23 +04:00
|
|
|
break;
|
|
|
|
default:
|
|
|
|
goto error;
|
2005-04-17 02:20:36 +04:00
|
|
|
}
|
2006-01-24 19:40:27 +03:00
|
|
|
if (device_can_wakeup(&hcd->self.root_hub->dev))
|
2005-04-17 02:20:36 +04:00
|
|
|
patch_wakeup = 1;
|
|
|
|
break;
|
|
|
|
case USB_DT_STRING << 8:
|
2009-03-13 14:19:18 +03:00
|
|
|
if ((wValue & 0xff) < 4)
|
|
|
|
urb->actual_length = rh_string(wValue & 0xff,
|
|
|
|
hcd, ubuf, wLength);
|
|
|
|
else /* unsupported IDs --> "protocol stall" */
|
2005-04-17 02:20:36 +04:00
|
|
|
goto error;
|
|
|
|
break;
|
2011-10-06 22:54:23 +04:00
|
|
|
case USB_DT_BOS << 8:
|
|
|
|
goto nongeneric;
|
2005-04-17 02:20:36 +04:00
|
|
|
default:
|
|
|
|
goto error;
|
|
|
|
}
|
|
|
|
break;
|
|
|
|
case DeviceRequest | USB_REQ_GET_INTERFACE:
|
2013-10-05 20:02:07 +04:00
|
|
|
tbuf[0] = 0;
|
2005-04-17 02:20:36 +04:00
|
|
|
len = 1;
|
|
|
|
/* FALLTHROUGH */
|
|
|
|
case DeviceOutRequest | USB_REQ_SET_INTERFACE:
|
|
|
|
break;
|
|
|
|
case DeviceOutRequest | USB_REQ_SET_ADDRESS:
|
2013-10-05 20:02:08 +04:00
|
|
|
/* wValue == urb->dev->devaddr */
|
2005-04-17 02:20:36 +04:00
|
|
|
dev_dbg (hcd->self.controller, "root hub device address %d\n",
|
|
|
|
wValue);
|
|
|
|
break;
|
|
|
|
|
|
|
|
/* INTERFACE REQUESTS (no defined feature/status flags) */
|
|
|
|
|
|
|
|
/* ENDPOINT REQUESTS */
|
|
|
|
|
|
|
|
case EndpointRequest | USB_REQ_GET_STATUS:
|
2013-10-05 20:02:08 +04:00
|
|
|
/* ENDPOINT_HALT flag */
|
2013-10-05 20:02:07 +04:00
|
|
|
tbuf[0] = 0;
|
|
|
|
tbuf[1] = 0;
|
2005-04-17 02:20:36 +04:00
|
|
|
len = 2;
|
|
|
|
/* FALLTHROUGH */
|
|
|
|
case EndpointOutRequest | USB_REQ_CLEAR_FEATURE:
|
|
|
|
case EndpointOutRequest | USB_REQ_SET_FEATURE:
|
|
|
|
dev_dbg (hcd->self.controller, "no endpoint features yet\n");
|
|
|
|
break;
|
|
|
|
|
|
|
|
/* CLASS REQUESTS (and errors) */
|
|
|
|
|
|
|
|
default:
|
2011-10-06 22:54:23 +04:00
|
|
|
nongeneric:
|
2005-04-17 02:20:36 +04:00
|
|
|
/* non-generic request */
|
2005-09-27 21:38:54 +04:00
|
|
|
switch (typeReq) {
|
|
|
|
case GetHubStatus:
|
|
|
|
len = 4;
|
|
|
|
break;
|
2015-12-10 10:59:29 +03:00
|
|
|
case GetPortStatus:
|
|
|
|
if (wValue == HUB_PORT_STATUS)
|
|
|
|
len = 4;
|
|
|
|
else
|
|
|
|
/* other port status types return 8 bytes */
|
|
|
|
len = 8;
|
|
|
|
break;
|
2005-09-27 21:38:54 +04:00
|
|
|
case GetHubDescriptor:
|
|
|
|
len = sizeof (struct usb_hub_descriptor);
|
|
|
|
break;
|
2011-10-06 22:54:23 +04:00
|
|
|
case DeviceRequest | USB_REQ_GET_DESCRIPTOR:
|
|
|
|
/* len is returned by hub_control */
|
|
|
|
break;
|
2005-04-17 02:20:36 +04:00
|
|
|
}
|
2005-09-27 21:38:54 +04:00
|
|
|
status = hcd->driver->hub_control (hcd,
|
|
|
|
typeReq, wValue, wIndex,
|
|
|
|
tbuf, wLength);
|
2013-01-21 18:18:00 +04:00
|
|
|
|
|
|
|
if (typeReq == GetHubDescriptor)
|
|
|
|
usb_hub_adjust_deviceremovable(hcd->self.root_hub,
|
|
|
|
(struct usb_hub_descriptor *)tbuf);
|
2005-04-17 02:20:36 +04:00
|
|
|
break;
|
|
|
|
error:
|
|
|
|
/* "protocol stall" on error */
|
|
|
|
status = -EPIPE;
|
|
|
|
}
|
|
|
|
|
2011-10-06 22:54:23 +04:00
|
|
|
if (status < 0) {
|
2005-04-17 02:20:36 +04:00
|
|
|
len = 0;
|
|
|
|
if (status != -EPIPE) {
|
|
|
|
dev_dbg (hcd->self.controller,
|
|
|
|
"CTRL: TypeReq=0x%x val=0x%x "
|
|
|
|
"idx=0x%x len=%d ==> %d\n",
|
|
|
|
typeReq, wValue, wIndex,
|
2005-09-27 21:38:54 +04:00
|
|
|
wLength, status);
|
2005-04-17 02:20:36 +04:00
|
|
|
}
|
2011-10-06 22:54:23 +04:00
|
|
|
} else if (status > 0) {
|
|
|
|
/* hub_control may return the length of data copied. */
|
|
|
|
len = status;
|
|
|
|
status = 0;
|
2005-04-17 02:20:36 +04:00
|
|
|
}
|
|
|
|
if (len) {
|
|
|
|
if (urb->transfer_buffer_length < len)
|
|
|
|
len = urb->transfer_buffer_length;
|
|
|
|
urb->actual_length = len;
|
2013-10-05 20:02:08 +04:00
|
|
|
/* always USB_DIR_IN, toward host */
|
2005-04-17 02:20:36 +04:00
|
|
|
memcpy (ubuf, bufp, len);
|
|
|
|
|
|
|
|
/* report whether RH hardware supports remote wakeup */
|
|
|
|
if (patch_wakeup &&
|
|
|
|
len > offsetof (struct usb_config_descriptor,
|
|
|
|
bmAttributes))
|
|
|
|
((struct usb_config_descriptor *)ubuf)->bmAttributes
|
|
|
|
|= USB_CONFIG_ATT_WAKEUP;
|
2008-04-04 02:02:56 +04:00
|
|
|
|
|
|
|
/* report whether RH hardware has an integrated TT */
|
|
|
|
if (patch_protocol &&
|
|
|
|
len > offsetof(struct usb_device_descriptor,
|
|
|
|
bDeviceProtocol))
|
|
|
|
((struct usb_device_descriptor *) ubuf)->
|
2011-12-08 10:35:22 +04:00
|
|
|
bDeviceProtocol = USB_HUB_PR_HS_SINGLE_TT;
|
2005-04-17 02:20:36 +04:00
|
|
|
}
|
|
|
|
|
2013-08-13 21:36:12 +04:00
|
|
|
kfree(tbuf);
|
2017-03-24 20:38:28 +03:00
|
|
|
err_alloc:
|
2013-08-13 21:36:12 +04:00
|
|
|
|
2005-04-17 02:20:36 +04:00
|
|
|
/* any errors get returned through the urb completion */
|
2007-08-02 23:05:45 +04:00
|
|
|
spin_lock_irq(&hcd_root_hub_lock);
|
2007-08-08 19:48:02 +04:00
|
|
|
usb_hcd_unlink_urb_from_ep(hcd, urb);
|
2007-08-24 23:42:24 +04:00
|
|
|
usb_hcd_giveback_urb(hcd, urb, status);
|
2007-08-02 23:05:45 +04:00
|
|
|
spin_unlock_irq(&hcd_root_hub_lock);
|
2005-04-17 02:20:36 +04:00
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
/*-------------------------------------------------------------------------*/
|
|
|
|
|
|
|
|
/*
|
2005-04-21 23:56:37 +04:00
|
|
|
* Root Hub interrupt transfers are polled using a timer if the
|
|
|
|
* driver requests it; otherwise the driver is responsible for
|
|
|
|
* calling usb_hcd_poll_rh_status() when an event occurs.
|
2005-04-17 02:20:36 +04:00
|
|
|
*
|
2005-04-21 23:56:37 +04:00
|
|
|
* Completions are called in_interrupt(), but they may or may not
|
|
|
|
* be in_irq().
|
2005-04-17 02:20:36 +04:00
|
|
|
*/
|
2005-04-21 23:56:37 +04:00
|
|
|
void usb_hcd_poll_rh_status(struct usb_hcd *hcd)
|
|
|
|
{
|
|
|
|
struct urb *urb;
|
|
|
|
int length;
|
|
|
|
unsigned long flags;
|
2009-07-07 00:05:40 +04:00
|
|
|
char buffer[6]; /* Any root hubs with > 31 ports? */
|
2005-04-17 02:20:36 +04:00
|
|
|
|
2010-06-10 01:34:17 +04:00
|
|
|
if (unlikely(!hcd->rh_pollable))
|
2007-03-13 18:10:52 +03:00
|
|
|
return;
|
2005-04-21 23:56:37 +04:00
|
|
|
if (!hcd->uses_new_polling && !hcd->status_urb)
|
|
|
|
return;
|
2005-04-17 02:20:36 +04:00
|
|
|
|
2005-04-21 23:56:37 +04:00
|
|
|
length = hcd->driver->hub_status_data(hcd, buffer);
|
|
|
|
if (length > 0) {
|
2005-04-17 02:20:36 +04:00
|
|
|
|
2005-04-21 23:56:37 +04:00
|
|
|
/* try to complete the status urb */
|
2007-08-02 23:05:45 +04:00
|
|
|
spin_lock_irqsave(&hcd_root_hub_lock, flags);
|
2005-04-21 23:56:37 +04:00
|
|
|
urb = hcd->status_urb;
|
|
|
|
if (urb) {
|
2010-06-23 00:39:10 +04:00
|
|
|
clear_bit(HCD_FLAG_POLL_PENDING, &hcd->flags);
|
2007-08-08 19:48:02 +04:00
|
|
|
hcd->status_urb = NULL;
|
|
|
|
urb->actual_length = length;
|
|
|
|
memcpy(urb->transfer_buffer, buffer, length);
|
2007-08-02 23:05:45 +04:00
|
|
|
|
2007-08-08 19:48:02 +04:00
|
|
|
usb_hcd_unlink_urb_from_ep(hcd, urb);
|
2007-08-24 23:42:24 +04:00
|
|
|
usb_hcd_giveback_urb(hcd, urb, 0);
|
2007-08-08 19:48:02 +04:00
|
|
|
} else {
|
2005-04-21 23:56:37 +04:00
|
|
|
length = 0;
|
2010-06-23 00:39:10 +04:00
|
|
|
set_bit(HCD_FLAG_POLL_PENDING, &hcd->flags);
|
2007-08-08 19:48:02 +04:00
|
|
|
}
|
2007-08-02 23:05:45 +04:00
|
|
|
spin_unlock_irqrestore(&hcd_root_hub_lock, flags);
|
2005-04-17 02:20:36 +04:00
|
|
|
}
|
|
|
|
|
2005-04-21 23:56:37 +04:00
|
|
|
/* The USB 2.0 spec says 256 ms. This is close enough and won't
|
2007-05-22 23:42:56 +04:00
|
|
|
* exceed that limit if HZ is 100. The math is more clunky than
|
|
|
|
* maybe expected, this is to make sure that all timers for USB devices
|
2011-03-31 05:57:33 +04:00
|
|
|
* fire at the same time to give the CPU a break in between */
|
2010-06-23 00:39:10 +04:00
|
|
|
if (hcd->uses_new_polling ? HCD_POLL_RH(hcd) :
|
2005-04-21 23:56:37 +04:00
|
|
|
(length == 0 && hcd->status_urb != NULL))
|
2007-05-22 23:42:56 +04:00
|
|
|
mod_timer (&hcd->rh_timer, (jiffies/(HZ/4) + 1) * (HZ/4));
|
2005-04-17 02:20:36 +04:00
|
|
|
}
|
2005-04-21 23:56:37 +04:00
|
|
|
EXPORT_SYMBOL_GPL(usb_hcd_poll_rh_status);
|
2005-04-17 02:20:36 +04:00
|
|
|
|
|
|
|
/* timer callback */
|
2005-04-21 23:56:37 +04:00
|
|
|
static void rh_timer_func (unsigned long _hcd)
|
|
|
|
{
|
|
|
|
usb_hcd_poll_rh_status((struct usb_hcd *) _hcd);
|
|
|
|
}
|
|
|
|
|
|
|
|
/*-------------------------------------------------------------------------*/
|
2005-04-17 02:20:36 +04:00
|
|
|
|
2005-04-21 23:56:37 +04:00
|
|
|
static int rh_queue_status (struct usb_hcd *hcd, struct urb *urb)
|
2005-04-17 02:20:36 +04:00
|
|
|
{
|
2005-04-21 23:56:37 +04:00
|
|
|
int retval;
|
2005-04-17 02:20:36 +04:00
|
|
|
unsigned long flags;
|
2009-03-13 14:19:18 +03:00
|
|
|
unsigned len = 1 + (urb->dev->maxchild / 8);
|
2005-04-17 02:20:36 +04:00
|
|
|
|
2005-04-21 23:56:37 +04:00
|
|
|
spin_lock_irqsave (&hcd_root_hub_lock, flags);
|
2007-08-08 19:48:02 +04:00
|
|
|
if (hcd->status_urb || urb->transfer_buffer_length < len) {
|
2005-04-21 23:56:37 +04:00
|
|
|
dev_dbg (hcd->self.controller, "not queuing rh status urb\n");
|
|
|
|
retval = -EINVAL;
|
2007-08-08 19:48:02 +04:00
|
|
|
goto done;
|
|
|
|
}
|
2005-04-17 02:20:36 +04:00
|
|
|
|
2007-08-08 19:48:02 +04:00
|
|
|
retval = usb_hcd_link_urb_to_ep(hcd, urb);
|
|
|
|
if (retval)
|
|
|
|
goto done;
|
2005-04-17 02:20:36 +04:00
|
|
|
|
2007-08-08 19:48:02 +04:00
|
|
|
hcd->status_urb = urb;
|
|
|
|
urb->hcpriv = hcd; /* indicate it's queued */
|
|
|
|
if (!hcd->uses_new_polling)
|
|
|
|
mod_timer(&hcd->rh_timer, (jiffies/(HZ/4) + 1) * (HZ/4));
|
|
|
|
|
|
|
|
/* If a status change has already occurred, report it ASAP */
|
2010-06-23 00:39:10 +04:00
|
|
|
else if (HCD_POLL_PENDING(hcd))
|
2007-08-08 19:48:02 +04:00
|
|
|
mod_timer(&hcd->rh_timer, jiffies);
|
|
|
|
retval = 0;
|
|
|
|
done:
|
2005-04-21 23:56:37 +04:00
|
|
|
spin_unlock_irqrestore (&hcd_root_hub_lock, flags);
|
|
|
|
return retval;
|
2005-04-17 02:20:36 +04:00
|
|
|
}
|
|
|
|
|
|
|
|
static int rh_urb_enqueue (struct usb_hcd *hcd, struct urb *urb)
|
|
|
|
{
|
2007-07-31 01:07:21 +04:00
|
|
|
if (usb_endpoint_xfer_int(&urb->ep->desc))
|
2005-04-21 23:56:37 +04:00
|
|
|
return rh_queue_status (hcd, urb);
|
2007-07-31 01:07:21 +04:00
|
|
|
if (usb_endpoint_xfer_control(&urb->ep->desc))
|
2005-04-17 02:20:36 +04:00
|
|
|
return rh_call_control (hcd, urb);
|
2005-04-21 23:56:37 +04:00
|
|
|
return -EINVAL;
|
2005-04-17 02:20:36 +04:00
|
|
|
}
|
|
|
|
|
|
|
|
/*-------------------------------------------------------------------------*/
|
|
|
|
|
2006-08-12 00:01:45 +04:00
|
|
|
/* Unlinks of root-hub control URBs are legal, but they don't do anything
|
|
|
|
* since these URBs always execute synchronously.
|
2005-04-21 23:56:37 +04:00
|
|
|
*/
|
2007-08-08 19:48:02 +04:00
|
|
|
static int usb_rh_urb_dequeue(struct usb_hcd *hcd, struct urb *urb, int status)
|
2005-04-17 02:20:36 +04:00
|
|
|
{
|
2006-08-12 00:01:45 +04:00
|
|
|
unsigned long flags;
|
2007-08-08 19:48:02 +04:00
|
|
|
int rc;
|
2005-04-17 02:20:36 +04:00
|
|
|
|
2007-08-02 23:05:45 +04:00
|
|
|
spin_lock_irqsave(&hcd_root_hub_lock, flags);
|
2007-08-08 19:48:02 +04:00
|
|
|
rc = usb_hcd_check_unlink_urb(hcd, urb, status);
|
|
|
|
if (rc)
|
|
|
|
goto done;
|
|
|
|
|
2007-07-31 01:07:21 +04:00
|
|
|
if (usb_endpoint_num(&urb->ep->desc) == 0) { /* Control URB */
|
2006-08-12 00:01:45 +04:00
|
|
|
; /* Do nothing */
|
2005-04-21 23:56:37 +04:00
|
|
|
|
|
|
|
} else { /* Status URB */
|
|
|
|
if (!hcd->uses_new_polling)
|
2006-08-12 00:01:45 +04:00
|
|
|
del_timer (&hcd->rh_timer);
|
2005-04-21 23:56:37 +04:00
|
|
|
if (urb == hcd->status_urb) {
|
|
|
|
hcd->status_urb = NULL;
|
2007-08-08 19:48:02 +04:00
|
|
|
usb_hcd_unlink_urb_from_ep(hcd, urb);
|
2007-08-24 23:42:24 +04:00
|
|
|
usb_hcd_giveback_urb(hcd, urb, status);
|
2007-08-02 23:05:45 +04:00
|
|
|
}
|
|
|
|
}
|
2007-08-08 19:48:02 +04:00
|
|
|
done:
|
2007-08-02 23:05:45 +04:00
|
|
|
spin_unlock_irqrestore(&hcd_root_hub_lock, flags);
|
2007-08-08 19:48:02 +04:00
|
|
|
return rc;
|
2005-04-17 02:20:36 +04:00
|
|
|
}
|
|
|
|
|
2007-08-01 07:33:58 +04:00
|
|
|
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Show & store the current value of authorized_default
|
|
|
|
*/
|
2013-08-24 03:05:26 +04:00
|
|
|
static ssize_t authorized_default_show(struct device *dev,
|
|
|
|
struct device_attribute *attr, char *buf)
|
2007-08-01 07:33:58 +04:00
|
|
|
{
|
|
|
|
struct usb_device *rh_usb_dev = to_usb_device(dev);
|
|
|
|
struct usb_bus *usb_bus = rh_usb_dev->bus;
|
2015-08-25 22:10:11 +03:00
|
|
|
struct usb_hcd *hcd;
|
2007-08-01 07:33:58 +04:00
|
|
|
|
2015-08-25 22:10:11 +03:00
|
|
|
hcd = bus_to_hcd(usb_bus);
|
|
|
|
return snprintf(buf, PAGE_SIZE, "%u\n", !!HCD_DEV_AUTHORIZED(hcd));
|
2007-08-01 07:33:58 +04:00
|
|
|
}
|
|
|
|
|
2013-08-24 03:05:26 +04:00
|
|
|
static ssize_t authorized_default_store(struct device *dev,
|
|
|
|
struct device_attribute *attr,
|
|
|
|
const char *buf, size_t size)
|
2007-08-01 07:33:58 +04:00
|
|
|
{
|
|
|
|
ssize_t result;
|
|
|
|
unsigned val;
|
|
|
|
struct usb_device *rh_usb_dev = to_usb_device(dev);
|
|
|
|
struct usb_bus *usb_bus = rh_usb_dev->bus;
|
2015-08-25 22:10:11 +03:00
|
|
|
struct usb_hcd *hcd;
|
2007-08-01 07:33:58 +04:00
|
|
|
|
2015-08-25 22:10:11 +03:00
|
|
|
hcd = bus_to_hcd(usb_bus);
|
2007-08-01 07:33:58 +04:00
|
|
|
result = sscanf(buf, "%u\n", &val);
|
|
|
|
if (result == 1) {
|
2015-08-25 22:10:11 +03:00
|
|
|
if (val)
|
|
|
|
set_bit(HCD_FLAG_DEV_AUTHORIZED, &hcd->flags);
|
|
|
|
else
|
|
|
|
clear_bit(HCD_FLAG_DEV_AUTHORIZED, &hcd->flags);
|
|
|
|
|
2007-08-01 07:33:58 +04:00
|
|
|
result = size;
|
2013-10-05 20:02:10 +04:00
|
|
|
} else {
|
2007-08-01 07:33:58 +04:00
|
|
|
result = -EINVAL;
|
2013-10-05 20:02:10 +04:00
|
|
|
}
|
2007-08-01 07:33:58 +04:00
|
|
|
return result;
|
|
|
|
}
|
2013-08-24 03:05:26 +04:00
|
|
|
static DEVICE_ATTR_RW(authorized_default);
|
2007-08-01 07:33:58 +04:00
|
|
|
|
2015-08-25 22:10:06 +03:00
|
|
|
/*
|
|
|
|
* interface_authorized_default_show - show default authorization status
|
|
|
|
* for USB interfaces
|
|
|
|
*
|
|
|
|
* note: interface_authorized_default is the default value
|
|
|
|
* for initializing the authorized attribute of interfaces
|
|
|
|
*/
|
|
|
|
static ssize_t interface_authorized_default_show(struct device *dev,
|
|
|
|
struct device_attribute *attr, char *buf)
|
|
|
|
{
|
|
|
|
struct usb_device *usb_dev = to_usb_device(dev);
|
|
|
|
struct usb_hcd *hcd = bus_to_hcd(usb_dev->bus);
|
|
|
|
|
|
|
|
return sprintf(buf, "%u\n", !!HCD_INTF_AUTHORIZED(hcd));
|
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
* interface_authorized_default_store - store default authorization status
|
|
|
|
* for USB interfaces
|
|
|
|
*
|
|
|
|
* note: interface_authorized_default is the default value
|
|
|
|
* for initializing the authorized attribute of interfaces
|
|
|
|
*/
|
|
|
|
static ssize_t interface_authorized_default_store(struct device *dev,
|
|
|
|
struct device_attribute *attr, const char *buf, size_t count)
|
|
|
|
{
|
|
|
|
struct usb_device *usb_dev = to_usb_device(dev);
|
|
|
|
struct usb_hcd *hcd = bus_to_hcd(usb_dev->bus);
|
|
|
|
int rc = count;
|
|
|
|
bool val;
|
|
|
|
|
|
|
|
if (strtobool(buf, &val) != 0)
|
|
|
|
return -EINVAL;
|
|
|
|
|
|
|
|
if (val)
|
|
|
|
set_bit(HCD_FLAG_INTF_AUTHORIZED, &hcd->flags);
|
|
|
|
else
|
|
|
|
clear_bit(HCD_FLAG_INTF_AUTHORIZED, &hcd->flags);
|
|
|
|
|
|
|
|
return rc;
|
|
|
|
}
|
|
|
|
static DEVICE_ATTR_RW(interface_authorized_default);
|
|
|
|
|
2007-08-01 07:33:58 +04:00
|
|
|
/* Group all the USB bus attributes */
|
|
|
|
static struct attribute *usb_bus_attrs[] = {
|
|
|
|
&dev_attr_authorized_default.attr,
|
2015-08-25 22:10:06 +03:00
|
|
|
&dev_attr_interface_authorized_default.attr,
|
2007-08-01 07:33:58 +04:00
|
|
|
NULL,
|
|
|
|
};
|
|
|
|
|
|
|
|
static struct attribute_group usb_bus_attr_group = {
|
|
|
|
.name = NULL, /* we want them in the same directory */
|
|
|
|
.attrs = usb_bus_attrs,
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
|
|
|
2005-04-17 02:20:36 +04:00
|
|
|
/*-------------------------------------------------------------------------*/
|
|
|
|
|
|
|
|
/**
|
|
|
|
* usb_bus_init - shared initialization code
|
|
|
|
* @bus: the bus structure being initialized
|
|
|
|
*
|
|
|
|
* This code is used to initialize a usb_bus structure, memory for which is
|
|
|
|
* separately managed.
|
|
|
|
*/
|
|
|
|
static void usb_bus_init (struct usb_bus *bus)
|
|
|
|
{
|
|
|
|
memset (&bus->devmap, 0, sizeof(struct usb_devmap));
|
|
|
|
|
|
|
|
bus->devnum_next = 1;
|
|
|
|
|
|
|
|
bus->root_hub = NULL;
|
|
|
|
bus->busnum = -1;
|
|
|
|
bus->bandwidth_allocated = 0;
|
|
|
|
bus->bandwidth_int_reqs = 0;
|
|
|
|
bus->bandwidth_isoc_reqs = 0;
|
2016-04-25 15:48:38 +03:00
|
|
|
mutex_init(&bus->devnum_next_mutex);
|
2005-04-17 02:20:36 +04:00
|
|
|
}
|
|
|
|
|
|
|
|
/*-------------------------------------------------------------------------*/
|
|
|
|
|
|
|
|
/**
|
|
|
|
* usb_register_bus - registers the USB host controller with the usb core
|
|
|
|
* @bus: pointer to the bus to register
|
|
|
|
* Context: !in_interrupt()
|
|
|
|
*
|
|
|
|
* Assigns a bus number, and links the controller into usbcore data
|
|
|
|
* structures so that it can be seen by scanning the bus list.
|
2013-08-02 22:10:04 +04:00
|
|
|
*
|
|
|
|
* Return: 0 if successful. A negative error code otherwise.
|
2005-04-17 02:20:36 +04:00
|
|
|
*/
|
|
|
|
static int usb_register_bus(struct usb_bus *bus)
|
|
|
|
{
|
2007-08-01 07:33:59 +04:00
|
|
|
int result = -E2BIG;
|
2005-04-17 02:20:36 +04:00
|
|
|
int busnum;
|
|
|
|
|
2016-02-04 01:35:23 +03:00
|
|
|
mutex_lock(&usb_bus_idr_lock);
|
2016-01-25 22:30:30 +03:00
|
|
|
busnum = idr_alloc(&usb_bus_idr, bus, 1, USB_MAXBUS, GFP_KERNEL);
|
|
|
|
if (busnum < 0) {
|
|
|
|
pr_err("%s: failed to get bus number\n", usbcore_name);
|
2007-08-01 07:33:59 +04:00
|
|
|
goto error_find_busnum;
|
2005-04-17 02:20:36 +04:00
|
|
|
}
|
2007-08-01 07:33:59 +04:00
|
|
|
bus->busnum = busnum;
|
2016-02-04 01:35:23 +03:00
|
|
|
mutex_unlock(&usb_bus_idr_lock);
|
2005-04-17 02:20:36 +04:00
|
|
|
|
2005-06-21 08:15:16 +04:00
|
|
|
usb_notify_add_bus(bus);
|
2005-04-17 02:20:36 +04:00
|
|
|
|
2007-08-01 07:33:59 +04:00
|
|
|
dev_info (bus->controller, "new USB bus registered, assigned bus "
|
|
|
|
"number %d\n", bus->busnum);
|
2005-04-17 02:20:36 +04:00
|
|
|
return 0;
|
2007-08-01 07:33:59 +04:00
|
|
|
|
|
|
|
error_find_busnum:
|
2016-02-04 01:35:23 +03:00
|
|
|
mutex_unlock(&usb_bus_idr_lock);
|
2007-08-01 07:33:59 +04:00
|
|
|
return result;
|
2005-04-17 02:20:36 +04:00
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* usb_deregister_bus - deregisters the USB host controller
|
|
|
|
* @bus: pointer to the bus to deregister
|
|
|
|
* Context: !in_interrupt()
|
|
|
|
*
|
|
|
|
* Recycles the bus number, and unlinks the controller from usbcore data
|
|
|
|
* structures so that it won't be seen by scanning the bus list.
|
|
|
|
*/
|
|
|
|
static void usb_deregister_bus (struct usb_bus *bus)
|
|
|
|
{
|
|
|
|
dev_info (bus->controller, "USB bus %d deregistered\n", bus->busnum);
|
|
|
|
|
|
|
|
/*
|
|
|
|
* NOTE: make sure that all the devices are removed by the
|
|
|
|
* controller code, as well as having it call this when cleaning
|
|
|
|
* itself up
|
|
|
|
*/
|
2016-02-04 01:35:23 +03:00
|
|
|
mutex_lock(&usb_bus_idr_lock);
|
2016-01-25 22:30:30 +03:00
|
|
|
idr_remove(&usb_bus_idr, bus->busnum);
|
2016-02-04 01:35:23 +03:00
|
|
|
mutex_unlock(&usb_bus_idr_lock);
|
2005-04-17 02:20:36 +04:00
|
|
|
|
2005-06-21 08:15:16 +04:00
|
|
|
usb_notify_remove_bus(bus);
|
2005-04-17 02:20:36 +04:00
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
2005-04-25 19:25:17 +04:00
|
|
|
* register_root_hub - called by usb_add_hcd() to register a root hub
|
2005-04-17 02:20:36 +04:00
|
|
|
* @hcd: host controller for this root hub
|
|
|
|
*
|
2005-04-25 19:25:17 +04:00
|
|
|
* This function registers the root hub with the USB subsystem. It sets up
|
2006-01-24 02:25:40 +03:00
|
|
|
* the device properly in the device tree and then calls usb_new_device()
|
|
|
|
* to register the usb device. It also assigns the root hub's USB address
|
|
|
|
* (always 1).
|
2013-08-02 22:10:04 +04:00
|
|
|
*
|
|
|
|
* Return: 0 if successful. A negative error code otherwise.
|
2005-04-17 02:20:36 +04:00
|
|
|
*/
|
2006-01-24 02:25:40 +03:00
|
|
|
static int register_root_hub(struct usb_hcd *hcd)
|
2005-04-17 02:20:36 +04:00
|
|
|
{
|
|
|
|
struct device *parent_dev = hcd->self.controller;
|
2006-01-24 02:25:40 +03:00
|
|
|
struct usb_device *usb_dev = hcd->self.root_hub;
|
2005-04-17 02:20:36 +04:00
|
|
|
const int devnum = 1;
|
|
|
|
int retval;
|
|
|
|
|
|
|
|
usb_dev->devnum = devnum;
|
|
|
|
usb_dev->bus->devnum_next = devnum + 1;
|
|
|
|
memset (&usb_dev->bus->devmap.devicemap, 0,
|
|
|
|
sizeof usb_dev->bus->devmap.devicemap);
|
|
|
|
set_bit (devnum, usb_dev->bus->devmap.devicemap);
|
|
|
|
usb_set_device_state(usb_dev, USB_STATE_ADDRESS);
|
|
|
|
|
2016-02-04 01:35:23 +03:00
|
|
|
mutex_lock(&usb_bus_idr_lock);
|
2005-04-17 02:20:36 +04:00
|
|
|
|
2009-02-12 01:11:36 +03:00
|
|
|
usb_dev->ep0.desc.wMaxPacketSize = cpu_to_le16(64);
|
2005-04-17 02:20:36 +04:00
|
|
|
retval = usb_get_device_descriptor(usb_dev, USB_DT_DEVICE_SIZE);
|
|
|
|
if (retval != sizeof usb_dev->descriptor) {
|
2016-02-04 01:35:23 +03:00
|
|
|
mutex_unlock(&usb_bus_idr_lock);
|
2005-04-17 02:20:36 +04:00
|
|
|
dev_dbg (parent_dev, "can't read %s device descriptor %d\n",
|
2008-05-02 08:02:41 +04:00
|
|
|
dev_name(&usb_dev->dev), retval);
|
2005-04-17 02:20:36 +04:00
|
|
|
return (retval < 0) ? retval : -EMSGSIZE;
|
|
|
|
}
|
2015-06-16 04:08:26 +03:00
|
|
|
|
|
|
|
if (le16_to_cpu(usb_dev->descriptor.bcdUSB) >= 0x0201) {
|
2012-05-16 03:58:45 +04:00
|
|
|
retval = usb_get_bos_descriptor(usb_dev);
|
2015-06-16 04:08:26 +03:00
|
|
|
if (!retval) {
|
|
|
|
usb_dev->lpm_capable = usb_device_supports_lpm(usb_dev);
|
2015-12-10 10:59:25 +03:00
|
|
|
} else if (usb_dev->speed >= USB_SPEED_SUPER) {
|
2016-02-04 01:35:23 +03:00
|
|
|
mutex_unlock(&usb_bus_idr_lock);
|
2012-05-16 03:58:45 +04:00
|
|
|
dev_dbg(parent_dev, "can't read %s bos descriptor %d\n",
|
|
|
|
dev_name(&usb_dev->dev), retval);
|
|
|
|
return retval;
|
|
|
|
}
|
|
|
|
}
|
2005-04-17 02:20:36 +04:00
|
|
|
|
|
|
|
retval = usb_new_device (usb_dev);
|
|
|
|
if (retval) {
|
|
|
|
dev_err (parent_dev, "can't register root hub for %s, %d\n",
|
2008-05-02 08:02:41 +04:00
|
|
|
dev_name(&usb_dev->dev), retval);
|
2012-09-26 21:09:53 +04:00
|
|
|
} else {
|
2005-04-17 02:20:36 +04:00
|
|
|
spin_lock_irq (&hcd_root_hub_lock);
|
|
|
|
hcd->rh_registered = 1;
|
|
|
|
spin_unlock_irq (&hcd_root_hub_lock);
|
|
|
|
|
|
|
|
/* Did the HC die before the root hub was registered? */
|
2011-05-18 01:27:12 +04:00
|
|
|
if (HCD_DEAD(hcd))
|
2005-04-17 02:20:36 +04:00
|
|
|
usb_hc_died (hcd); /* This time clean up */
|
|
|
|
}
|
2016-02-04 01:35:23 +03:00
|
|
|
mutex_unlock(&usb_bus_idr_lock);
|
2005-04-17 02:20:36 +04:00
|
|
|
|
|
|
|
return retval;
|
|
|
|
}
|
|
|
|
|
2013-01-26 02:09:42 +04:00
|
|
|
/*
|
|
|
|
* usb_hcd_start_port_resume - a root-hub port is sending a resume signal
|
|
|
|
* @bus: the bus which the root hub belongs to
|
|
|
|
* @portnum: the port which is being resumed
|
|
|
|
*
|
|
|
|
* HCDs should call this function when they know that a resume signal is
|
|
|
|
* being sent to a root-hub port. The root hub will be prevented from
|
|
|
|
* going into autosuspend until usb_hcd_end_port_resume() is called.
|
|
|
|
*
|
|
|
|
* The bus's private lock must be held by the caller.
|
|
|
|
*/
|
|
|
|
void usb_hcd_start_port_resume(struct usb_bus *bus, int portnum)
|
|
|
|
{
|
|
|
|
unsigned bit = 1 << portnum;
|
|
|
|
|
|
|
|
if (!(bus->resuming_ports & bit)) {
|
|
|
|
bus->resuming_ports |= bit;
|
|
|
|
pm_runtime_get_noresume(&bus->root_hub->dev);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
EXPORT_SYMBOL_GPL(usb_hcd_start_port_resume);
|
|
|
|
|
|
|
|
/*
|
|
|
|
* usb_hcd_end_port_resume - a root-hub port has stopped sending a resume signal
|
|
|
|
* @bus: the bus which the root hub belongs to
|
|
|
|
* @portnum: the port which is being resumed
|
|
|
|
*
|
|
|
|
* HCDs should call this function when they know that a resume signal has
|
|
|
|
* stopped being sent to a root-hub port. The root hub will be allowed to
|
|
|
|
* autosuspend again.
|
|
|
|
*
|
|
|
|
* The bus's private lock must be held by the caller.
|
|
|
|
*/
|
|
|
|
void usb_hcd_end_port_resume(struct usb_bus *bus, int portnum)
|
|
|
|
{
|
|
|
|
unsigned bit = 1 << portnum;
|
|
|
|
|
|
|
|
if (bus->resuming_ports & bit) {
|
|
|
|
bus->resuming_ports &= ~bit;
|
|
|
|
pm_runtime_put_noidle(&bus->root_hub->dev);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
EXPORT_SYMBOL_GPL(usb_hcd_end_port_resume);
|
2005-04-17 02:20:36 +04:00
|
|
|
|
|
|
|
/*-------------------------------------------------------------------------*/
|
|
|
|
|
|
|
|
/**
|
|
|
|
* usb_calc_bus_time - approximate periodic transaction time in nanoseconds
|
|
|
|
* @speed: from dev->speed; USB_SPEED_{LOW,FULL,HIGH}
|
|
|
|
* @is_input: true iff the transaction sends data to the host
|
|
|
|
* @isoc: true for isochronous transactions, false for interrupt ones
|
|
|
|
* @bytecount: how many bytes in the transaction.
|
|
|
|
*
|
2013-08-02 22:10:04 +04:00
|
|
|
* Return: Approximate bus time in nanoseconds for a periodic transaction.
|
|
|
|
*
|
|
|
|
* Note:
|
2005-04-17 02:20:36 +04:00
|
|
|
* See USB 2.0 spec section 5.11.3; only periodic transfers need to be
|
|
|
|
* scheduled in software, this function is only used for such scheduling.
|
|
|
|
*/
|
|
|
|
long usb_calc_bus_time (int speed, int is_input, int isoc, int bytecount)
|
|
|
|
{
|
|
|
|
unsigned long tmp;
|
|
|
|
|
|
|
|
switch (speed) {
|
|
|
|
case USB_SPEED_LOW: /* INTR only */
|
|
|
|
if (is_input) {
|
|
|
|
tmp = (67667L * (31L + 10L * BitTime (bytecount))) / 1000L;
|
2013-10-05 20:02:09 +04:00
|
|
|
return 64060L + (2 * BW_HUB_LS_SETUP) + BW_HOST_DELAY + tmp;
|
2005-04-17 02:20:36 +04:00
|
|
|
} else {
|
|
|
|
tmp = (66700L * (31L + 10L * BitTime (bytecount))) / 1000L;
|
2013-10-05 20:02:09 +04:00
|
|
|
return 64107L + (2 * BW_HUB_LS_SETUP) + BW_HOST_DELAY + tmp;
|
2005-04-17 02:20:36 +04:00
|
|
|
}
|
|
|
|
case USB_SPEED_FULL: /* ISOC or INTR */
|
|
|
|
if (isoc) {
|
|
|
|
tmp = (8354L * (31L + 10L * BitTime (bytecount))) / 1000L;
|
2013-10-05 20:02:09 +04:00
|
|
|
return ((is_input) ? 7268L : 6265L) + BW_HOST_DELAY + tmp;
|
2005-04-17 02:20:36 +04:00
|
|
|
} else {
|
|
|
|
tmp = (8354L * (31L + 10L * BitTime (bytecount))) / 1000L;
|
2013-10-05 20:02:09 +04:00
|
|
|
return 9107L + BW_HOST_DELAY + tmp;
|
2005-04-17 02:20:36 +04:00
|
|
|
}
|
|
|
|
case USB_SPEED_HIGH: /* ISOC or INTR */
|
2013-10-05 20:02:08 +04:00
|
|
|
/* FIXME adjust for input vs output */
|
2005-04-17 02:20:36 +04:00
|
|
|
if (isoc)
|
2005-07-29 23:18:28 +04:00
|
|
|
tmp = HS_NSECS_ISO (bytecount);
|
2005-04-17 02:20:36 +04:00
|
|
|
else
|
2005-07-29 23:18:28 +04:00
|
|
|
tmp = HS_NSECS (bytecount);
|
2005-04-17 02:20:36 +04:00
|
|
|
return tmp;
|
|
|
|
default:
|
|
|
|
pr_debug ("%s: bogus device speed!\n", usbcore_name);
|
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
}
|
2008-01-25 20:12:21 +03:00
|
|
|
EXPORT_SYMBOL_GPL(usb_calc_bus_time);
|
2005-04-17 02:20:36 +04:00
|
|
|
|
|
|
|
|
|
|
|
/*-------------------------------------------------------------------------*/
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Generic HC operations.
|
|
|
|
*/
|
|
|
|
|
|
|
|
/*-------------------------------------------------------------------------*/
|
|
|
|
|
2007-08-08 19:48:02 +04:00
|
|
|
/**
|
|
|
|
* usb_hcd_link_urb_to_ep - add an URB to its endpoint queue
|
|
|
|
* @hcd: host controller to which @urb was submitted
|
|
|
|
* @urb: URB being submitted
|
|
|
|
*
|
|
|
|
* Host controller drivers should call this routine in their enqueue()
|
|
|
|
* method. The HCD's private spinlock must be held and interrupts must
|
|
|
|
* be disabled. The actions carried out here are required for URB
|
|
|
|
* submission, as well as for endpoint shutdown and for usb_kill_urb.
|
|
|
|
*
|
2013-08-02 22:10:04 +04:00
|
|
|
* Return: 0 for no error, otherwise a negative error code (in which case
|
2007-08-08 19:48:02 +04:00
|
|
|
* the enqueue() method must fail). If no error occurs but enqueue() fails
|
|
|
|
* anyway, it must call usb_hcd_unlink_urb_from_ep() before releasing
|
|
|
|
* the private spinlock and returning.
|
|
|
|
*/
|
|
|
|
int usb_hcd_link_urb_to_ep(struct usb_hcd *hcd, struct urb *urb)
|
2005-04-17 02:20:36 +04:00
|
|
|
{
|
2007-08-02 23:06:54 +04:00
|
|
|
int rc = 0;
|
2005-04-17 02:20:36 +04:00
|
|
|
|
2007-08-08 19:48:02 +04:00
|
|
|
spin_lock(&hcd_urb_list_lock);
|
2005-04-17 02:20:36 +04:00
|
|
|
|
2007-08-02 23:06:54 +04:00
|
|
|
/* Check that the URB isn't being killed */
|
2008-12-12 16:38:45 +03:00
|
|
|
if (unlikely(atomic_read(&urb->reject))) {
|
2007-08-02 23:06:54 +04:00
|
|
|
rc = -EPERM;
|
|
|
|
goto done;
|
2007-06-09 00:37:49 +04:00
|
|
|
}
|
2005-04-17 02:20:36 +04:00
|
|
|
|
2007-08-02 23:06:54 +04:00
|
|
|
if (unlikely(!urb->ep->enabled)) {
|
|
|
|
rc = -ENOENT;
|
|
|
|
goto done;
|
|
|
|
}
|
2005-04-17 02:20:36 +04:00
|
|
|
|
2007-09-10 19:34:26 +04:00
|
|
|
if (unlikely(!urb->dev->can_submit)) {
|
|
|
|
rc = -EHOSTUNREACH;
|
|
|
|
goto done;
|
|
|
|
}
|
|
|
|
|
2005-04-17 02:20:36 +04:00
|
|
|
/*
|
2007-08-02 23:06:54 +04:00
|
|
|
* Check the host controller's state and add the URB to the
|
|
|
|
* endpoint's queue.
|
2005-04-17 02:20:36 +04:00
|
|
|
*/
|
2011-03-07 19:11:52 +03:00
|
|
|
if (HCD_RH_RUNNING(hcd)) {
|
2007-08-21 23:40:36 +04:00
|
|
|
urb->unlinked = 0;
|
2007-08-02 23:06:54 +04:00
|
|
|
list_add_tail(&urb->urb_list, &urb->ep->urb_list);
|
2011-03-07 19:11:52 +03:00
|
|
|
} else {
|
2007-08-02 23:06:54 +04:00
|
|
|
rc = -ESHUTDOWN;
|
|
|
|
goto done;
|
2005-04-17 02:20:36 +04:00
|
|
|
}
|
2007-08-02 23:06:54 +04:00
|
|
|
done:
|
2007-08-08 19:48:02 +04:00
|
|
|
spin_unlock(&hcd_urb_list_lock);
|
2007-08-02 23:06:54 +04:00
|
|
|
return rc;
|
|
|
|
}
|
2007-08-08 19:48:02 +04:00
|
|
|
EXPORT_SYMBOL_GPL(usb_hcd_link_urb_to_ep);
|
2007-08-02 23:06:54 +04:00
|
|
|
|
2007-08-08 19:48:02 +04:00
|
|
|
/**
|
|
|
|
* usb_hcd_check_unlink_urb - check whether an URB may be unlinked
|
|
|
|
* @hcd: host controller to which @urb was submitted
|
|
|
|
* @urb: URB being checked for unlinkability
|
|
|
|
* @status: error code to store in @urb if the unlink succeeds
|
|
|
|
*
|
|
|
|
* Host controller drivers should call this routine in their dequeue()
|
|
|
|
* method. The HCD's private spinlock must be held and interrupts must
|
|
|
|
* be disabled. The actions carried out here are required for making
|
|
|
|
* sure than an unlink is valid.
|
|
|
|
*
|
2013-08-02 22:10:04 +04:00
|
|
|
* Return: 0 for no error, otherwise a negative error code (in which case
|
2007-08-08 19:48:02 +04:00
|
|
|
* the dequeue() method must fail). The possible error codes are:
|
|
|
|
*
|
|
|
|
* -EIDRM: @urb was not submitted or has already completed.
|
|
|
|
* The completion function may not have been called yet.
|
|
|
|
*
|
|
|
|
* -EBUSY: @urb has already been unlinked.
|
|
|
|
*/
|
|
|
|
int usb_hcd_check_unlink_urb(struct usb_hcd *hcd, struct urb *urb,
|
2007-08-02 23:06:54 +04:00
|
|
|
int status)
|
|
|
|
{
|
|
|
|
struct list_head *tmp;
|
|
|
|
|
|
|
|
/* insist the urb is still queued */
|
|
|
|
list_for_each(tmp, &urb->ep->urb_list) {
|
|
|
|
if (tmp == &urb->urb_list)
|
|
|
|
break;
|
|
|
|
}
|
2007-08-08 19:48:02 +04:00
|
|
|
if (tmp != &urb->urb_list)
|
|
|
|
return -EIDRM;
|
2005-04-17 02:20:36 +04:00
|
|
|
|
2007-08-02 23:06:54 +04:00
|
|
|
/* Any status except -EINPROGRESS means something already started to
|
|
|
|
* unlink this URB from the hardware. So there's no more work to do.
|
2005-04-17 02:20:36 +04:00
|
|
|
*/
|
2007-08-21 23:40:36 +04:00
|
|
|
if (urb->unlinked)
|
2007-08-08 19:48:02 +04:00
|
|
|
return -EBUSY;
|
2007-08-21 23:40:36 +04:00
|
|
|
urb->unlinked = status;
|
2007-08-08 19:48:02 +04:00
|
|
|
return 0;
|
2007-08-02 23:06:54 +04:00
|
|
|
}
|
2007-08-08 19:48:02 +04:00
|
|
|
EXPORT_SYMBOL_GPL(usb_hcd_check_unlink_urb);
|
2007-08-02 23:06:54 +04:00
|
|
|
|
2007-08-08 19:48:02 +04:00
|
|
|
/**
|
|
|
|
* usb_hcd_unlink_urb_from_ep - remove an URB from its endpoint queue
|
|
|
|
* @hcd: host controller to which @urb was submitted
|
|
|
|
* @urb: URB being unlinked
|
|
|
|
*
|
|
|
|
* Host controller drivers should call this routine before calling
|
|
|
|
* usb_hcd_giveback_urb(). The HCD's private spinlock must be held and
|
|
|
|
* interrupts must be disabled. The actions carried out here are required
|
|
|
|
* for URB completion.
|
|
|
|
*/
|
|
|
|
void usb_hcd_unlink_urb_from_ep(struct usb_hcd *hcd, struct urb *urb)
|
2007-08-02 23:06:54 +04:00
|
|
|
{
|
|
|
|
/* clear all state linking urb to this dev (and hcd) */
|
2007-08-08 19:48:02 +04:00
|
|
|
spin_lock(&hcd_urb_list_lock);
|
2007-08-02 23:06:54 +04:00
|
|
|
list_del_init(&urb->urb_list);
|
2007-08-08 19:48:02 +04:00
|
|
|
spin_unlock(&hcd_urb_list_lock);
|
2007-08-02 23:06:54 +04:00
|
|
|
}
|
2007-08-08 19:48:02 +04:00
|
|
|
EXPORT_SYMBOL_GPL(usb_hcd_unlink_urb_from_ep);
|
2007-08-02 23:06:54 +04:00
|
|
|
|
2008-01-23 09:58:35 +03:00
|
|
|
/*
|
|
|
|
* Some usb host controllers can only perform dma using a small SRAM area.
|
|
|
|
* The usb core itself is however optimized for host controllers that can dma
|
|
|
|
* using regular system memory - like pci devices doing bus mastering.
|
|
|
|
*
|
2014-09-18 13:25:04 +04:00
|
|
|
* To support host controllers with limited dma capabilities we provide dma
|
2008-01-23 09:58:35 +03:00
|
|
|
* bounce buffers. This feature can be enabled using the HCD_LOCAL_MEM flag.
|
|
|
|
* For this to work properly the host controller code must first use the
|
|
|
|
* function dma_declare_coherent_memory() to point out which memory area
|
|
|
|
* that should be used for dma allocations.
|
|
|
|
*
|
|
|
|
* The HCD_LOCAL_MEM flag then tells the usb code to allocate all data for
|
|
|
|
* dma using dma_alloc_coherent() which in turn allocates from the memory
|
|
|
|
* area pointed out with dma_declare_coherent_memory().
|
|
|
|
*
|
|
|
|
* So, to summarize...
|
|
|
|
*
|
|
|
|
* - We need "local" memory, canonical example being
|
|
|
|
* a small SRAM on a discrete controller being the
|
|
|
|
* only memory that the controller can read ...
|
|
|
|
* (a) "normal" kernel memory is no good, and
|
|
|
|
* (b) there's not enough to share
|
|
|
|
*
|
|
|
|
* - The only *portable* hook for such stuff in the
|
|
|
|
* DMA framework is dma_declare_coherent_memory()
|
|
|
|
*
|
|
|
|
* - So we use that, even though the primary requirement
|
2014-01-04 09:54:41 +04:00
|
|
|
* is that the memory be "local" (hence addressable
|
2008-01-23 09:58:35 +03:00
|
|
|
* by that device), not "coherent".
|
|
|
|
*
|
|
|
|
*/
|
|
|
|
|
|
|
|
static int hcd_alloc_coherent(struct usb_bus *bus,
|
|
|
|
gfp_t mem_flags, dma_addr_t *dma_handle,
|
|
|
|
void **vaddr_handle, size_t size,
|
|
|
|
enum dma_data_direction dir)
|
|
|
|
{
|
|
|
|
unsigned char *vaddr;
|
|
|
|
|
2010-06-28 18:56:45 +04:00
|
|
|
if (*vaddr_handle == NULL) {
|
|
|
|
WARN_ON_ONCE(1);
|
|
|
|
return -EFAULT;
|
|
|
|
}
|
|
|
|
|
2008-01-23 09:58:35 +03:00
|
|
|
vaddr = hcd_buffer_alloc(bus, size + sizeof(vaddr),
|
|
|
|
mem_flags, dma_handle);
|
|
|
|
if (!vaddr)
|
|
|
|
return -ENOMEM;
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Store the virtual address of the buffer at the end
|
|
|
|
* of the allocated dma buffer. The size of the buffer
|
|
|
|
* may be uneven so use unaligned functions instead
|
|
|
|
* of just rounding up. It makes sense to optimize for
|
|
|
|
* memory footprint over access speed since the amount
|
|
|
|
* of memory available for dma may be limited.
|
|
|
|
*/
|
|
|
|
put_unaligned((unsigned long)*vaddr_handle,
|
|
|
|
(unsigned long *)(vaddr + size));
|
|
|
|
|
|
|
|
if (dir == DMA_TO_DEVICE)
|
|
|
|
memcpy(vaddr, *vaddr_handle, size);
|
|
|
|
|
|
|
|
*vaddr_handle = vaddr;
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
static void hcd_free_coherent(struct usb_bus *bus, dma_addr_t *dma_handle,
|
|
|
|
void **vaddr_handle, size_t size,
|
|
|
|
enum dma_data_direction dir)
|
|
|
|
{
|
|
|
|
unsigned char *vaddr = *vaddr_handle;
|
|
|
|
|
|
|
|
vaddr = (void *)get_unaligned((unsigned long *)(vaddr + size));
|
|
|
|
|
|
|
|
if (dir == DMA_FROM_DEVICE)
|
|
|
|
memcpy(vaddr, *vaddr_handle, size);
|
|
|
|
|
|
|
|
hcd_buffer_free(bus, size + sizeof(vaddr), *vaddr_handle, *dma_handle);
|
|
|
|
|
|
|
|
*vaddr_handle = vaddr;
|
|
|
|
*dma_handle = 0;
|
|
|
|
}
|
|
|
|
|
2011-01-27 06:06:47 +03:00
|
|
|
void usb_hcd_unmap_urb_setup_for_dma(struct usb_hcd *hcd, struct urb *urb)
|
2010-04-02 21:27:28 +04:00
|
|
|
{
|
2016-02-16 18:10:57 +03:00
|
|
|
if (IS_ENABLED(CONFIG_HAS_DMA) &&
|
|
|
|
(urb->transfer_flags & URB_SETUP_MAP_SINGLE))
|
2017-04-03 11:26:52 +03:00
|
|
|
dma_unmap_single(hcd->self.sysdev,
|
2010-04-02 21:27:28 +04:00
|
|
|
urb->setup_dma,
|
|
|
|
sizeof(struct usb_ctrlrequest),
|
|
|
|
DMA_TO_DEVICE);
|
|
|
|
else if (urb->transfer_flags & URB_SETUP_MAP_LOCAL)
|
|
|
|
hcd_free_coherent(urb->dev->bus,
|
|
|
|
&urb->setup_dma,
|
|
|
|
(void **) &urb->setup_packet,
|
|
|
|
sizeof(struct usb_ctrlrequest),
|
|
|
|
DMA_TO_DEVICE);
|
|
|
|
|
2010-10-01 02:21:55 +04:00
|
|
|
/* Make it safe to call this routine more than once */
|
|
|
|
urb->transfer_flags &= ~(URB_SETUP_MAP_SINGLE | URB_SETUP_MAP_LOCAL);
|
|
|
|
}
|
2011-01-27 06:06:47 +03:00
|
|
|
EXPORT_SYMBOL_GPL(usb_hcd_unmap_urb_setup_for_dma);
|
2010-10-01 02:21:55 +04:00
|
|
|
|
2011-01-27 06:06:48 +03:00
|
|
|
static void unmap_urb_for_dma(struct usb_hcd *hcd, struct urb *urb)
|
|
|
|
{
|
|
|
|
if (hcd->driver->unmap_urb_for_dma)
|
|
|
|
hcd->driver->unmap_urb_for_dma(hcd, urb);
|
|
|
|
else
|
|
|
|
usb_hcd_unmap_urb_for_dma(hcd, urb);
|
|
|
|
}
|
|
|
|
|
2011-01-27 06:06:47 +03:00
|
|
|
void usb_hcd_unmap_urb_for_dma(struct usb_hcd *hcd, struct urb *urb)
|
2010-10-01 02:21:55 +04:00
|
|
|
{
|
|
|
|
enum dma_data_direction dir;
|
|
|
|
|
2011-01-27 06:06:47 +03:00
|
|
|
usb_hcd_unmap_urb_setup_for_dma(hcd, urb);
|
2010-10-01 02:21:55 +04:00
|
|
|
|
2010-04-02 21:27:28 +04:00
|
|
|
dir = usb_urb_dir_in(urb) ? DMA_FROM_DEVICE : DMA_TO_DEVICE;
|
2016-02-16 18:10:57 +03:00
|
|
|
if (IS_ENABLED(CONFIG_HAS_DMA) &&
|
|
|
|
(urb->transfer_flags & URB_DMA_MAP_SG))
|
2017-03-13 05:18:41 +03:00
|
|
|
dma_unmap_sg(hcd->self.sysdev,
|
2010-05-01 22:20:01 +04:00
|
|
|
urb->sg,
|
2010-04-02 21:27:28 +04:00
|
|
|
urb->num_sgs,
|
|
|
|
dir);
|
2016-02-16 18:10:57 +03:00
|
|
|
else if (IS_ENABLED(CONFIG_HAS_DMA) &&
|
|
|
|
(urb->transfer_flags & URB_DMA_MAP_PAGE))
|
2017-03-13 05:18:41 +03:00
|
|
|
dma_unmap_page(hcd->self.sysdev,
|
2010-04-02 21:27:28 +04:00
|
|
|
urb->transfer_dma,
|
|
|
|
urb->transfer_buffer_length,
|
|
|
|
dir);
|
2016-02-16 18:10:57 +03:00
|
|
|
else if (IS_ENABLED(CONFIG_HAS_DMA) &&
|
|
|
|
(urb->transfer_flags & URB_DMA_MAP_SINGLE))
|
2017-03-13 05:18:41 +03:00
|
|
|
dma_unmap_single(hcd->self.sysdev,
|
2010-04-02 21:27:28 +04:00
|
|
|
urb->transfer_dma,
|
|
|
|
urb->transfer_buffer_length,
|
|
|
|
dir);
|
|
|
|
else if (urb->transfer_flags & URB_MAP_LOCAL)
|
|
|
|
hcd_free_coherent(urb->dev->bus,
|
|
|
|
&urb->transfer_dma,
|
|
|
|
&urb->transfer_buffer,
|
|
|
|
urb->transfer_buffer_length,
|
|
|
|
dir);
|
|
|
|
|
|
|
|
/* Make it safe to call this routine more than once */
|
2010-10-01 02:21:55 +04:00
|
|
|
urb->transfer_flags &= ~(URB_DMA_MAP_SG | URB_DMA_MAP_PAGE |
|
2010-04-02 21:27:28 +04:00
|
|
|
URB_DMA_MAP_SINGLE | URB_MAP_LOCAL);
|
|
|
|
}
|
2011-01-27 06:06:47 +03:00
|
|
|
EXPORT_SYMBOL_GPL(usb_hcd_unmap_urb_for_dma);
|
2010-04-02 21:27:28 +04:00
|
|
|
|
2008-01-23 09:58:35 +03:00
|
|
|
static int map_urb_for_dma(struct usb_hcd *hcd, struct urb *urb,
|
|
|
|
gfp_t mem_flags)
|
2011-01-27 06:06:48 +03:00
|
|
|
{
|
|
|
|
if (hcd->driver->map_urb_for_dma)
|
|
|
|
return hcd->driver->map_urb_for_dma(hcd, urb, mem_flags);
|
|
|
|
else
|
|
|
|
return usb_hcd_map_urb_for_dma(hcd, urb, mem_flags);
|
|
|
|
}
|
|
|
|
|
|
|
|
int usb_hcd_map_urb_for_dma(struct usb_hcd *hcd, struct urb *urb,
|
|
|
|
gfp_t mem_flags)
|
2007-08-02 23:06:54 +04:00
|
|
|
{
|
2008-01-23 09:58:35 +03:00
|
|
|
enum dma_data_direction dir;
|
|
|
|
int ret = 0;
|
|
|
|
|
2007-08-02 23:06:54 +04:00
|
|
|
/* Map the URB's buffers for DMA access.
|
|
|
|
* Lower level HCD code should use *_dma exclusively,
|
2009-04-28 06:59:01 +04:00
|
|
|
* unless it uses pio or talks to another transport,
|
|
|
|
* or uses the provided scatter gather list for bulk.
|
2005-04-17 02:20:36 +04:00
|
|
|
*/
|
2008-01-23 09:58:35 +03:00
|
|
|
|
2010-05-01 00:35:37 +04:00
|
|
|
if (usb_endpoint_xfer_control(&urb->ep->desc)) {
|
2010-11-18 16:24:17 +03:00
|
|
|
if (hcd->self.uses_pio_for_control)
|
|
|
|
return ret;
|
2016-02-16 18:10:57 +03:00
|
|
|
if (IS_ENABLED(CONFIG_HAS_DMA) && hcd->self.uses_dma) {
|
2017-04-26 03:56:11 +03:00
|
|
|
if (is_vmalloc_addr(urb->setup_packet)) {
|
|
|
|
WARN_ONCE(1, "setup packet is not dma capable\n");
|
|
|
|
return -EAGAIN;
|
|
|
|
} else if (object_is_on_stack(urb->setup_packet)) {
|
|
|
|
WARN_ONCE(1, "setup packet is on stack\n");
|
|
|
|
return -EAGAIN;
|
|
|
|
}
|
|
|
|
|
2008-01-23 09:58:35 +03:00
|
|
|
urb->setup_dma = dma_map_single(
|
2017-03-13 05:18:41 +03:00
|
|
|
hcd->self.sysdev,
|
2005-04-17 02:20:36 +04:00
|
|
|
urb->setup_packet,
|
2008-01-23 09:58:35 +03:00
|
|
|
sizeof(struct usb_ctrlrequest),
|
2005-04-17 02:20:36 +04:00
|
|
|
DMA_TO_DEVICE);
|
2017-03-13 05:18:41 +03:00
|
|
|
if (dma_mapping_error(hcd->self.sysdev,
|
2009-11-05 19:37:03 +03:00
|
|
|
urb->setup_dma))
|
|
|
|
return -EAGAIN;
|
2010-04-02 21:27:28 +04:00
|
|
|
urb->transfer_flags |= URB_SETUP_MAP_SINGLE;
|
2010-05-12 19:38:12 +04:00
|
|
|
} else if (hcd->driver->flags & HCD_LOCAL_MEM) {
|
2008-01-23 09:58:35 +03:00
|
|
|
ret = hcd_alloc_coherent(
|
|
|
|
urb->dev->bus, mem_flags,
|
|
|
|
&urb->setup_dma,
|
|
|
|
(void **)&urb->setup_packet,
|
|
|
|
sizeof(struct usb_ctrlrequest),
|
|
|
|
DMA_TO_DEVICE);
|
2010-04-02 21:27:28 +04:00
|
|
|
if (ret)
|
|
|
|
return ret;
|
|
|
|
urb->transfer_flags |= URB_SETUP_MAP_LOCAL;
|
2010-05-12 19:38:12 +04:00
|
|
|
}
|
2008-01-23 09:58:35 +03:00
|
|
|
}
|
|
|
|
|
|
|
|
dir = usb_urb_dir_in(urb) ? DMA_FROM_DEVICE : DMA_TO_DEVICE;
|
2010-04-02 21:27:28 +04:00
|
|
|
if (urb->transfer_buffer_length != 0
|
2008-01-23 09:58:35 +03:00
|
|
|
&& !(urb->transfer_flags & URB_NO_TRANSFER_DMA_MAP)) {
|
2016-02-16 18:10:57 +03:00
|
|
|
if (IS_ENABLED(CONFIG_HAS_DMA) && hcd->self.uses_dma) {
|
2010-04-02 21:27:28 +04:00
|
|
|
if (urb->num_sgs) {
|
2012-07-09 17:57:00 +04:00
|
|
|
int n;
|
|
|
|
|
|
|
|
/* We don't support sg for isoc transfers ! */
|
|
|
|
if (usb_endpoint_xfer_isoc(&urb->ep->desc)) {
|
|
|
|
WARN_ON(1);
|
|
|
|
return -EINVAL;
|
|
|
|
}
|
|
|
|
|
|
|
|
n = dma_map_sg(
|
2017-03-13 05:18:41 +03:00
|
|
|
hcd->self.sysdev,
|
2010-05-01 22:20:01 +04:00
|
|
|
urb->sg,
|
2010-04-02 21:27:28 +04:00
|
|
|
urb->num_sgs,
|
|
|
|
dir);
|
|
|
|
if (n <= 0)
|
|
|
|
ret = -EAGAIN;
|
|
|
|
else
|
|
|
|
urb->transfer_flags |= URB_DMA_MAP_SG;
|
2011-12-04 02:41:31 +04:00
|
|
|
urb->num_mapped_sgs = n;
|
|
|
|
if (n != urb->num_sgs)
|
2010-04-02 21:27:28 +04:00
|
|
|
urb->transfer_flags |=
|
|
|
|
URB_DMA_SG_COMBINED;
|
|
|
|
} else if (urb->sg) {
|
2010-05-01 22:20:01 +04:00
|
|
|
struct scatterlist *sg = urb->sg;
|
2010-04-02 21:27:28 +04:00
|
|
|
urb->transfer_dma = dma_map_page(
|
2017-03-13 05:18:41 +03:00
|
|
|
hcd->self.sysdev,
|
2010-04-02 21:27:28 +04:00
|
|
|
sg_page(sg),
|
|
|
|
sg->offset,
|
|
|
|
urb->transfer_buffer_length,
|
|
|
|
dir);
|
2017-03-13 05:18:41 +03:00
|
|
|
if (dma_mapping_error(hcd->self.sysdev,
|
2009-11-05 19:37:03 +03:00
|
|
|
urb->transfer_dma))
|
2010-04-02 21:27:28 +04:00
|
|
|
ret = -EAGAIN;
|
|
|
|
else
|
|
|
|
urb->transfer_flags |= URB_DMA_MAP_PAGE;
|
2014-05-08 20:25:56 +04:00
|
|
|
} else if (is_vmalloc_addr(urb->transfer_buffer)) {
|
|
|
|
WARN_ONCE(1, "transfer buffer not dma capable\n");
|
|
|
|
ret = -EAGAIN;
|
2017-04-26 03:56:11 +03:00
|
|
|
} else if (object_is_on_stack(urb->transfer_buffer)) {
|
|
|
|
WARN_ONCE(1, "transfer buffer is on stack\n");
|
|
|
|
ret = -EAGAIN;
|
2010-04-02 21:27:28 +04:00
|
|
|
} else {
|
|
|
|
urb->transfer_dma = dma_map_single(
|
2017-03-13 05:18:41 +03:00
|
|
|
hcd->self.sysdev,
|
2010-04-02 21:27:28 +04:00
|
|
|
urb->transfer_buffer,
|
|
|
|
urb->transfer_buffer_length,
|
|
|
|
dir);
|
2017-03-13 05:18:41 +03:00
|
|
|
if (dma_mapping_error(hcd->self.sysdev,
|
2010-04-02 21:27:28 +04:00
|
|
|
urb->transfer_dma))
|
|
|
|
ret = -EAGAIN;
|
|
|
|
else
|
|
|
|
urb->transfer_flags |= URB_DMA_MAP_SINGLE;
|
|
|
|
}
|
2009-11-05 19:37:03 +03:00
|
|
|
} else if (hcd->driver->flags & HCD_LOCAL_MEM) {
|
2008-01-23 09:58:35 +03:00
|
|
|
ret = hcd_alloc_coherent(
|
|
|
|
urb->dev->bus, mem_flags,
|
|
|
|
&urb->transfer_dma,
|
|
|
|
&urb->transfer_buffer,
|
|
|
|
urb->transfer_buffer_length,
|
|
|
|
dir);
|
2010-04-02 21:27:28 +04:00
|
|
|
if (ret == 0)
|
|
|
|
urb->transfer_flags |= URB_MAP_LOCAL;
|
2008-01-23 09:58:35 +03:00
|
|
|
}
|
2010-04-02 21:27:28 +04:00
|
|
|
if (ret && (urb->transfer_flags & (URB_SETUP_MAP_SINGLE |
|
|
|
|
URB_SETUP_MAP_LOCAL)))
|
2011-01-27 06:06:47 +03:00
|
|
|
usb_hcd_unmap_urb_for_dma(hcd, urb);
|
2005-04-17 02:20:36 +04:00
|
|
|
}
|
2008-01-23 09:58:35 +03:00
|
|
|
return ret;
|
2007-08-02 23:06:54 +04:00
|
|
|
}
|
2011-01-27 06:06:48 +03:00
|
|
|
EXPORT_SYMBOL_GPL(usb_hcd_map_urb_for_dma);
|
2005-04-17 02:20:36 +04:00
|
|
|
|
2007-08-02 23:06:54 +04:00
|
|
|
/*-------------------------------------------------------------------------*/
|
|
|
|
|
|
|
|
/* may be called in any context with a valid urb->dev usecount
|
|
|
|
* caller surrenders "ownership" of urb
|
|
|
|
* expects usb_submit_urb() to have sanity checked and conditioned all
|
|
|
|
* inputs in the urb
|
|
|
|
*/
|
|
|
|
int usb_hcd_submit_urb (struct urb *urb, gfp_t mem_flags)
|
|
|
|
{
|
|
|
|
int status;
|
|
|
|
struct usb_hcd *hcd = bus_to_hcd(urb->dev->bus);
|
|
|
|
|
|
|
|
/* increment urb's reference count as part of giving it to the HCD
|
|
|
|
* (which will control it). HCD guarantees that it either returns
|
|
|
|
* an error or calls giveback(), but not both.
|
|
|
|
*/
|
|
|
|
usb_get_urb(urb);
|
|
|
|
atomic_inc(&urb->use_count);
|
2007-10-04 01:56:03 +04:00
|
|
|
atomic_inc(&urb->dev->urbnum);
|
2007-08-02 23:06:54 +04:00
|
|
|
usbmon_urb_submit(&hcd->self, urb);
|
|
|
|
|
|
|
|
/* NOTE requirements on root-hub callers (usbfs and the hub
|
|
|
|
* driver, for now): URBs' urb->transfer_buffer must be
|
|
|
|
* valid and usb_buffer_{sync,unmap}() not be needed, since
|
|
|
|
* they could clobber root hub response data. Also, control
|
|
|
|
* URBs must be submitted in process context with interrupts
|
|
|
|
* enabled.
|
|
|
|
*/
|
2008-01-23 09:58:35 +03:00
|
|
|
|
2010-04-02 21:27:28 +04:00
|
|
|
if (is_root_hub(urb->dev)) {
|
2007-08-08 19:48:02 +04:00
|
|
|
status = rh_urb_enqueue(hcd, urb);
|
2010-04-02 21:27:28 +04:00
|
|
|
} else {
|
|
|
|
status = map_urb_for_dma(hcd, urb, mem_flags);
|
|
|
|
if (likely(status == 0)) {
|
|
|
|
status = hcd->driver->urb_enqueue(hcd, urb, mem_flags);
|
|
|
|
if (unlikely(status))
|
2011-01-27 06:06:48 +03:00
|
|
|
unmap_urb_for_dma(hcd, urb);
|
2010-04-02 21:27:28 +04:00
|
|
|
}
|
|
|
|
}
|
2007-08-02 23:06:54 +04:00
|
|
|
|
|
|
|
if (unlikely(status)) {
|
2005-04-17 02:20:36 +04:00
|
|
|
usbmon_urb_submit_error(&hcd->self, urb, status);
|
2007-08-21 23:39:21 +04:00
|
|
|
urb->hcpriv = NULL;
|
2007-08-02 23:06:54 +04:00
|
|
|
INIT_LIST_HEAD(&urb->urb_list);
|
|
|
|
atomic_dec(&urb->use_count);
|
2007-10-04 01:56:03 +04:00
|
|
|
atomic_dec(&urb->dev->urbnum);
|
2008-12-12 16:38:45 +03:00
|
|
|
if (atomic_read(&urb->reject))
|
2007-08-02 23:06:54 +04:00
|
|
|
wake_up(&usb_kill_urb_queue);
|
|
|
|
usb_put_urb(urb);
|
2005-04-17 02:20:36 +04:00
|
|
|
}
|
|
|
|
return status;
|
|
|
|
}
|
|
|
|
|
|
|
|
/*-------------------------------------------------------------------------*/
|
|
|
|
|
|
|
|
/* this makes the hcd giveback() the urb more quickly, by kicking it
|
|
|
|
* off hardware queues (which may take a while) and returning it as
|
|
|
|
* soon as practical. we've already set up the urb's return status,
|
|
|
|
* but we can't know if the callback completed already.
|
|
|
|
*/
|
2007-08-08 19:48:02 +04:00
|
|
|
static int unlink1(struct usb_hcd *hcd, struct urb *urb, int status)
|
2005-04-17 02:20:36 +04:00
|
|
|
{
|
|
|
|
int value;
|
|
|
|
|
2007-07-18 20:14:24 +04:00
|
|
|
if (is_root_hub(urb->dev))
|
2007-08-08 19:48:02 +04:00
|
|
|
value = usb_rh_urb_dequeue(hcd, urb, status);
|
2005-04-17 02:20:36 +04:00
|
|
|
else {
|
|
|
|
|
|
|
|
/* The only reason an HCD might fail this call is if
|
|
|
|
* it has not yet fully queued the urb to begin with.
|
|
|
|
* Such failures should be harmless. */
|
2007-08-08 19:48:02 +04:00
|
|
|
value = hcd->driver->urb_dequeue(hcd, urb, status);
|
2005-04-17 02:20:36 +04:00
|
|
|
}
|
|
|
|
return value;
|
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
* called in any context
|
|
|
|
*
|
|
|
|
* caller guarantees urb won't be recycled till both unlink()
|
|
|
|
* and the urb's completion function return
|
|
|
|
*/
|
2006-08-30 19:27:36 +04:00
|
|
|
int usb_hcd_unlink_urb (struct urb *urb, int status)
|
2005-04-17 02:20:36 +04:00
|
|
|
{
|
2007-08-02 23:06:54 +04:00
|
|
|
struct usb_hcd *hcd;
|
2015-01-30 20:58:26 +03:00
|
|
|
struct usb_device *udev = urb->dev;
|
2008-10-21 23:28:46 +04:00
|
|
|
int retval = -EIDRM;
|
|
|
|
unsigned long flags;
|
2005-04-17 02:20:36 +04:00
|
|
|
|
2008-10-21 23:28:46 +04:00
|
|
|
/* Prevent the device and bus from going away while
|
|
|
|
* the unlink is carried out. If they are already gone
|
|
|
|
* then urb->use_count must be 0, since disconnected
|
|
|
|
* devices can't have any active URBs.
|
|
|
|
*/
|
|
|
|
spin_lock_irqsave(&hcd_urb_unlink_lock, flags);
|
|
|
|
if (atomic_read(&urb->use_count) > 0) {
|
|
|
|
retval = 0;
|
2015-01-30 20:58:26 +03:00
|
|
|
usb_get_dev(udev);
|
2008-10-21 23:28:46 +04:00
|
|
|
}
|
|
|
|
spin_unlock_irqrestore(&hcd_urb_unlink_lock, flags);
|
|
|
|
if (retval == 0) {
|
|
|
|
hcd = bus_to_hcd(urb->dev->bus);
|
|
|
|
retval = unlink1(hcd, urb, status);
|
2015-01-30 20:58:26 +03:00
|
|
|
if (retval == 0)
|
|
|
|
retval = -EINPROGRESS;
|
|
|
|
else if (retval != -EIDRM && retval != -EBUSY)
|
2017-05-16 15:38:08 +03:00
|
|
|
dev_dbg(&udev->dev, "hcd_unlink_urb %pK fail %d\n",
|
2015-01-30 20:58:26 +03:00
|
|
|
urb, retval);
|
|
|
|
usb_put_dev(udev);
|
2008-10-21 23:28:46 +04:00
|
|
|
}
|
2005-04-17 02:20:36 +04:00
|
|
|
return retval;
|
|
|
|
}
|
|
|
|
|
|
|
|
/*-------------------------------------------------------------------------*/
|
|
|
|
|
USB: HCD: support giveback of URB in tasklet context
This patch implements the mechanism of giveback of URB in
tasklet context, so that hardware interrupt handling time for
usb host controller can be saved much, and HCD interrupt handling
can be simplified.
Motivations:
1), on some arch(such as ARM), DMA mapping/unmapping is a bit
time-consuming, for example: when accessing usb mass storage
via EHCI on pandaboard, the common length of transfer buffer is 120KB,
the time consumed on DMA unmapping may reach hundreds of microseconds;
even on A15 based box, the time is still about scores of microseconds
2), on some arch, reading DMA coherent memoery is very time-consuming,
the most common example is usb video class driver[1]
3), driver's complete() callback may do much things which is driver
specific, so the time is consumed unnecessarily in hardware irq context.
4), running driver's complete() callback in hardware irq context causes
that host controller driver has to release its lock in interrupt handler,
so reacquiring the lock after return may busy wait a while and increase
interrupt handling time. More seriously, releasing the HCD lock makes
HCD becoming quite complicated to deal with introduced races.
So the patch proposes to run giveback of URB in tasklet context, then
time consumed in HCD irq handling doesn't depend on drivers' complete and
DMA mapping/unmapping any more, also we can simplify HCD since the HCD
lock isn't needed to be released during irq handling.
The patch should be reasonable and doable:
1), for drivers, they don't care if the complete() is called in hard irq
context or softirq context
2), the biggest change is the situation in which usb_submit_urb() is called
in complete() callback, so the introduced tasklet schedule delay might be a
con, but it shouldn't be a big deal:
- control/bulk asynchronous transfer isn't sensitive to schedule
delay
- the patch schedules giveback of periodic URBs using
tasklet_hi_schedule, so the introduced delay should be very
small
- for ISOC transfer, generally, drivers submit several URBs
concurrently to avoid interrupt delay, so it is OK with the
little schedule delay.
- for interrupt transfer, generally, drivers only submit one URB
at the same time, but interrupt transfer is often used in event
report, polling, ... situations, and a little delay should be OK.
Considered that HCDs may optimize on submitting URB in complete(), the
patch may cause the optimization not working, so introduces one flag to mark
if the HCD supports to run giveback URB in tasklet context. When all HCDs
are ready, the flag can be removed.
[1], http://marc.info/?t=136438111600010&r=1&w=2
Cc: Oliver Neukum <oliver@neukum.org>
Acked-by: Alan Stern <stern@rowland.harvard.edu>
Signed-off-by: Ming Lei <ming.lei@canonical.com>
Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
2013-07-03 18:53:07 +04:00
|
|
|
static void __usb_hcd_giveback_urb(struct urb *urb)
|
|
|
|
{
|
|
|
|
struct usb_hcd *hcd = bus_to_hcd(urb->dev->bus);
|
2013-10-09 19:01:41 +04:00
|
|
|
struct usb_anchor *anchor = urb->anchor;
|
USB: HCD: support giveback of URB in tasklet context
This patch implements the mechanism of giveback of URB in
tasklet context, so that hardware interrupt handling time for
usb host controller can be saved much, and HCD interrupt handling
can be simplified.
Motivations:
1), on some arch(such as ARM), DMA mapping/unmapping is a bit
time-consuming, for example: when accessing usb mass storage
via EHCI on pandaboard, the common length of transfer buffer is 120KB,
the time consumed on DMA unmapping may reach hundreds of microseconds;
even on A15 based box, the time is still about scores of microseconds
2), on some arch, reading DMA coherent memoery is very time-consuming,
the most common example is usb video class driver[1]
3), driver's complete() callback may do much things which is driver
specific, so the time is consumed unnecessarily in hardware irq context.
4), running driver's complete() callback in hardware irq context causes
that host controller driver has to release its lock in interrupt handler,
so reacquiring the lock after return may busy wait a while and increase
interrupt handling time. More seriously, releasing the HCD lock makes
HCD becoming quite complicated to deal with introduced races.
So the patch proposes to run giveback of URB in tasklet context, then
time consumed in HCD irq handling doesn't depend on drivers' complete and
DMA mapping/unmapping any more, also we can simplify HCD since the HCD
lock isn't needed to be released during irq handling.
The patch should be reasonable and doable:
1), for drivers, they don't care if the complete() is called in hard irq
context or softirq context
2), the biggest change is the situation in which usb_submit_urb() is called
in complete() callback, so the introduced tasklet schedule delay might be a
con, but it shouldn't be a big deal:
- control/bulk asynchronous transfer isn't sensitive to schedule
delay
- the patch schedules giveback of periodic URBs using
tasklet_hi_schedule, so the introduced delay should be very
small
- for ISOC transfer, generally, drivers submit several URBs
concurrently to avoid interrupt delay, so it is OK with the
little schedule delay.
- for interrupt transfer, generally, drivers only submit one URB
at the same time, but interrupt transfer is often used in event
report, polling, ... situations, and a little delay should be OK.
Considered that HCDs may optimize on submitting URB in complete(), the
patch may cause the optimization not working, so introduces one flag to mark
if the HCD supports to run giveback URB in tasklet context. When all HCDs
are ready, the flag can be removed.
[1], http://marc.info/?t=136438111600010&r=1&w=2
Cc: Oliver Neukum <oliver@neukum.org>
Acked-by: Alan Stern <stern@rowland.harvard.edu>
Signed-off-by: Ming Lei <ming.lei@canonical.com>
Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
2013-07-03 18:53:07 +04:00
|
|
|
int status = urb->unlinked;
|
|
|
|
unsigned long flags;
|
|
|
|
|
|
|
|
urb->hcpriv = NULL;
|
|
|
|
if (unlikely((urb->transfer_flags & URB_SHORT_NOT_OK) &&
|
|
|
|
urb->actual_length < urb->transfer_buffer_length &&
|
|
|
|
!status))
|
|
|
|
status = -EREMOTEIO;
|
|
|
|
|
|
|
|
unmap_urb_for_dma(hcd, urb);
|
|
|
|
usbmon_urb_complete(&hcd->self, urb, status);
|
2013-10-09 19:01:41 +04:00
|
|
|
usb_anchor_suspend_wakeups(anchor);
|
USB: HCD: support giveback of URB in tasklet context
This patch implements the mechanism of giveback of URB in
tasklet context, so that hardware interrupt handling time for
usb host controller can be saved much, and HCD interrupt handling
can be simplified.
Motivations:
1), on some arch(such as ARM), DMA mapping/unmapping is a bit
time-consuming, for example: when accessing usb mass storage
via EHCI on pandaboard, the common length of transfer buffer is 120KB,
the time consumed on DMA unmapping may reach hundreds of microseconds;
even on A15 based box, the time is still about scores of microseconds
2), on some arch, reading DMA coherent memoery is very time-consuming,
the most common example is usb video class driver[1]
3), driver's complete() callback may do much things which is driver
specific, so the time is consumed unnecessarily in hardware irq context.
4), running driver's complete() callback in hardware irq context causes
that host controller driver has to release its lock in interrupt handler,
so reacquiring the lock after return may busy wait a while and increase
interrupt handling time. More seriously, releasing the HCD lock makes
HCD becoming quite complicated to deal with introduced races.
So the patch proposes to run giveback of URB in tasklet context, then
time consumed in HCD irq handling doesn't depend on drivers' complete and
DMA mapping/unmapping any more, also we can simplify HCD since the HCD
lock isn't needed to be released during irq handling.
The patch should be reasonable and doable:
1), for drivers, they don't care if the complete() is called in hard irq
context or softirq context
2), the biggest change is the situation in which usb_submit_urb() is called
in complete() callback, so the introduced tasklet schedule delay might be a
con, but it shouldn't be a big deal:
- control/bulk asynchronous transfer isn't sensitive to schedule
delay
- the patch schedules giveback of periodic URBs using
tasklet_hi_schedule, so the introduced delay should be very
small
- for ISOC transfer, generally, drivers submit several URBs
concurrently to avoid interrupt delay, so it is OK with the
little schedule delay.
- for interrupt transfer, generally, drivers only submit one URB
at the same time, but interrupt transfer is often used in event
report, polling, ... situations, and a little delay should be OK.
Considered that HCDs may optimize on submitting URB in complete(), the
patch may cause the optimization not working, so introduces one flag to mark
if the HCD supports to run giveback URB in tasklet context. When all HCDs
are ready, the flag can be removed.
[1], http://marc.info/?t=136438111600010&r=1&w=2
Cc: Oliver Neukum <oliver@neukum.org>
Acked-by: Alan Stern <stern@rowland.harvard.edu>
Signed-off-by: Ming Lei <ming.lei@canonical.com>
Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
2013-07-03 18:53:07 +04:00
|
|
|
usb_unanchor_urb(urb);
|
2014-09-25 00:43:21 +04:00
|
|
|
if (likely(status == 0))
|
|
|
|
usb_led_activity(USB_LED_EVENT_HOST);
|
USB: HCD: support giveback of URB in tasklet context
This patch implements the mechanism of giveback of URB in
tasklet context, so that hardware interrupt handling time for
usb host controller can be saved much, and HCD interrupt handling
can be simplified.
Motivations:
1), on some arch(such as ARM), DMA mapping/unmapping is a bit
time-consuming, for example: when accessing usb mass storage
via EHCI on pandaboard, the common length of transfer buffer is 120KB,
the time consumed on DMA unmapping may reach hundreds of microseconds;
even on A15 based box, the time is still about scores of microseconds
2), on some arch, reading DMA coherent memoery is very time-consuming,
the most common example is usb video class driver[1]
3), driver's complete() callback may do much things which is driver
specific, so the time is consumed unnecessarily in hardware irq context.
4), running driver's complete() callback in hardware irq context causes
that host controller driver has to release its lock in interrupt handler,
so reacquiring the lock after return may busy wait a while and increase
interrupt handling time. More seriously, releasing the HCD lock makes
HCD becoming quite complicated to deal with introduced races.
So the patch proposes to run giveback of URB in tasklet context, then
time consumed in HCD irq handling doesn't depend on drivers' complete and
DMA mapping/unmapping any more, also we can simplify HCD since the HCD
lock isn't needed to be released during irq handling.
The patch should be reasonable and doable:
1), for drivers, they don't care if the complete() is called in hard irq
context or softirq context
2), the biggest change is the situation in which usb_submit_urb() is called
in complete() callback, so the introduced tasklet schedule delay might be a
con, but it shouldn't be a big deal:
- control/bulk asynchronous transfer isn't sensitive to schedule
delay
- the patch schedules giveback of periodic URBs using
tasklet_hi_schedule, so the introduced delay should be very
small
- for ISOC transfer, generally, drivers submit several URBs
concurrently to avoid interrupt delay, so it is OK with the
little schedule delay.
- for interrupt transfer, generally, drivers only submit one URB
at the same time, but interrupt transfer is often used in event
report, polling, ... situations, and a little delay should be OK.
Considered that HCDs may optimize on submitting URB in complete(), the
patch may cause the optimization not working, so introduces one flag to mark
if the HCD supports to run giveback URB in tasklet context. When all HCDs
are ready, the flag can be removed.
[1], http://marc.info/?t=136438111600010&r=1&w=2
Cc: Oliver Neukum <oliver@neukum.org>
Acked-by: Alan Stern <stern@rowland.harvard.edu>
Signed-off-by: Ming Lei <ming.lei@canonical.com>
Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
2013-07-03 18:53:07 +04:00
|
|
|
|
|
|
|
/* pass ownership to the completion handler */
|
|
|
|
urb->status = status;
|
|
|
|
|
|
|
|
/*
|
|
|
|
* We disable local IRQs here avoid possible deadlock because
|
|
|
|
* drivers may call spin_lock() to hold lock which might be
|
|
|
|
* acquired in one hard interrupt handler.
|
|
|
|
*
|
|
|
|
* The local_irq_save()/local_irq_restore() around complete()
|
|
|
|
* will be removed if current USB drivers have been cleaned up
|
|
|
|
* and no one may trigger the above deadlock situation when
|
|
|
|
* running complete() in tasklet.
|
|
|
|
*/
|
|
|
|
local_irq_save(flags);
|
|
|
|
urb->complete(urb);
|
|
|
|
local_irq_restore(flags);
|
|
|
|
|
2013-10-09 19:01:41 +04:00
|
|
|
usb_anchor_resume_wakeups(anchor);
|
USB: HCD: support giveback of URB in tasklet context
This patch implements the mechanism of giveback of URB in
tasklet context, so that hardware interrupt handling time for
usb host controller can be saved much, and HCD interrupt handling
can be simplified.
Motivations:
1), on some arch(such as ARM), DMA mapping/unmapping is a bit
time-consuming, for example: when accessing usb mass storage
via EHCI on pandaboard, the common length of transfer buffer is 120KB,
the time consumed on DMA unmapping may reach hundreds of microseconds;
even on A15 based box, the time is still about scores of microseconds
2), on some arch, reading DMA coherent memoery is very time-consuming,
the most common example is usb video class driver[1]
3), driver's complete() callback may do much things which is driver
specific, so the time is consumed unnecessarily in hardware irq context.
4), running driver's complete() callback in hardware irq context causes
that host controller driver has to release its lock in interrupt handler,
so reacquiring the lock after return may busy wait a while and increase
interrupt handling time. More seriously, releasing the HCD lock makes
HCD becoming quite complicated to deal with introduced races.
So the patch proposes to run giveback of URB in tasklet context, then
time consumed in HCD irq handling doesn't depend on drivers' complete and
DMA mapping/unmapping any more, also we can simplify HCD since the HCD
lock isn't needed to be released during irq handling.
The patch should be reasonable and doable:
1), for drivers, they don't care if the complete() is called in hard irq
context or softirq context
2), the biggest change is the situation in which usb_submit_urb() is called
in complete() callback, so the introduced tasklet schedule delay might be a
con, but it shouldn't be a big deal:
- control/bulk asynchronous transfer isn't sensitive to schedule
delay
- the patch schedules giveback of periodic URBs using
tasklet_hi_schedule, so the introduced delay should be very
small
- for ISOC transfer, generally, drivers submit several URBs
concurrently to avoid interrupt delay, so it is OK with the
little schedule delay.
- for interrupt transfer, generally, drivers only submit one URB
at the same time, but interrupt transfer is often used in event
report, polling, ... situations, and a little delay should be OK.
Considered that HCDs may optimize on submitting URB in complete(), the
patch may cause the optimization not working, so introduces one flag to mark
if the HCD supports to run giveback URB in tasklet context. When all HCDs
are ready, the flag can be removed.
[1], http://marc.info/?t=136438111600010&r=1&w=2
Cc: Oliver Neukum <oliver@neukum.org>
Acked-by: Alan Stern <stern@rowland.harvard.edu>
Signed-off-by: Ming Lei <ming.lei@canonical.com>
Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
2013-07-03 18:53:07 +04:00
|
|
|
atomic_dec(&urb->use_count);
|
|
|
|
if (unlikely(atomic_read(&urb->reject)))
|
|
|
|
wake_up(&usb_kill_urb_queue);
|
|
|
|
usb_put_urb(urb);
|
|
|
|
}
|
|
|
|
|
|
|
|
static void usb_giveback_urb_bh(unsigned long param)
|
|
|
|
{
|
|
|
|
struct giveback_urb_bh *bh = (struct giveback_urb_bh *)param;
|
|
|
|
struct list_head local_list;
|
|
|
|
|
|
|
|
spin_lock_irq(&bh->lock);
|
|
|
|
bh->running = true;
|
|
|
|
restart:
|
|
|
|
list_replace_init(&bh->head, &local_list);
|
|
|
|
spin_unlock_irq(&bh->lock);
|
|
|
|
|
|
|
|
while (!list_empty(&local_list)) {
|
|
|
|
struct urb *urb;
|
|
|
|
|
|
|
|
urb = list_entry(local_list.next, struct urb, urb_list);
|
|
|
|
list_del_init(&urb->urb_list);
|
2013-09-03 21:58:43 +04:00
|
|
|
bh->completing_ep = urb->ep;
|
USB: HCD: support giveback of URB in tasklet context
This patch implements the mechanism of giveback of URB in
tasklet context, so that hardware interrupt handling time for
usb host controller can be saved much, and HCD interrupt handling
can be simplified.
Motivations:
1), on some arch(such as ARM), DMA mapping/unmapping is a bit
time-consuming, for example: when accessing usb mass storage
via EHCI on pandaboard, the common length of transfer buffer is 120KB,
the time consumed on DMA unmapping may reach hundreds of microseconds;
even on A15 based box, the time is still about scores of microseconds
2), on some arch, reading DMA coherent memoery is very time-consuming,
the most common example is usb video class driver[1]
3), driver's complete() callback may do much things which is driver
specific, so the time is consumed unnecessarily in hardware irq context.
4), running driver's complete() callback in hardware irq context causes
that host controller driver has to release its lock in interrupt handler,
so reacquiring the lock after return may busy wait a while and increase
interrupt handling time. More seriously, releasing the HCD lock makes
HCD becoming quite complicated to deal with introduced races.
So the patch proposes to run giveback of URB in tasklet context, then
time consumed in HCD irq handling doesn't depend on drivers' complete and
DMA mapping/unmapping any more, also we can simplify HCD since the HCD
lock isn't needed to be released during irq handling.
The patch should be reasonable and doable:
1), for drivers, they don't care if the complete() is called in hard irq
context or softirq context
2), the biggest change is the situation in which usb_submit_urb() is called
in complete() callback, so the introduced tasklet schedule delay might be a
con, but it shouldn't be a big deal:
- control/bulk asynchronous transfer isn't sensitive to schedule
delay
- the patch schedules giveback of periodic URBs using
tasklet_hi_schedule, so the introduced delay should be very
small
- for ISOC transfer, generally, drivers submit several URBs
concurrently to avoid interrupt delay, so it is OK with the
little schedule delay.
- for interrupt transfer, generally, drivers only submit one URB
at the same time, but interrupt transfer is often used in event
report, polling, ... situations, and a little delay should be OK.
Considered that HCDs may optimize on submitting URB in complete(), the
patch may cause the optimization not working, so introduces one flag to mark
if the HCD supports to run giveback URB in tasklet context. When all HCDs
are ready, the flag can be removed.
[1], http://marc.info/?t=136438111600010&r=1&w=2
Cc: Oliver Neukum <oliver@neukum.org>
Acked-by: Alan Stern <stern@rowland.harvard.edu>
Signed-off-by: Ming Lei <ming.lei@canonical.com>
Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
2013-07-03 18:53:07 +04:00
|
|
|
__usb_hcd_giveback_urb(urb);
|
2013-09-03 21:58:43 +04:00
|
|
|
bh->completing_ep = NULL;
|
USB: HCD: support giveback of URB in tasklet context
This patch implements the mechanism of giveback of URB in
tasklet context, so that hardware interrupt handling time for
usb host controller can be saved much, and HCD interrupt handling
can be simplified.
Motivations:
1), on some arch(such as ARM), DMA mapping/unmapping is a bit
time-consuming, for example: when accessing usb mass storage
via EHCI on pandaboard, the common length of transfer buffer is 120KB,
the time consumed on DMA unmapping may reach hundreds of microseconds;
even on A15 based box, the time is still about scores of microseconds
2), on some arch, reading DMA coherent memoery is very time-consuming,
the most common example is usb video class driver[1]
3), driver's complete() callback may do much things which is driver
specific, so the time is consumed unnecessarily in hardware irq context.
4), running driver's complete() callback in hardware irq context causes
that host controller driver has to release its lock in interrupt handler,
so reacquiring the lock after return may busy wait a while and increase
interrupt handling time. More seriously, releasing the HCD lock makes
HCD becoming quite complicated to deal with introduced races.
So the patch proposes to run giveback of URB in tasklet context, then
time consumed in HCD irq handling doesn't depend on drivers' complete and
DMA mapping/unmapping any more, also we can simplify HCD since the HCD
lock isn't needed to be released during irq handling.
The patch should be reasonable and doable:
1), for drivers, they don't care if the complete() is called in hard irq
context or softirq context
2), the biggest change is the situation in which usb_submit_urb() is called
in complete() callback, so the introduced tasklet schedule delay might be a
con, but it shouldn't be a big deal:
- control/bulk asynchronous transfer isn't sensitive to schedule
delay
- the patch schedules giveback of periodic URBs using
tasklet_hi_schedule, so the introduced delay should be very
small
- for ISOC transfer, generally, drivers submit several URBs
concurrently to avoid interrupt delay, so it is OK with the
little schedule delay.
- for interrupt transfer, generally, drivers only submit one URB
at the same time, but interrupt transfer is often used in event
report, polling, ... situations, and a little delay should be OK.
Considered that HCDs may optimize on submitting URB in complete(), the
patch may cause the optimization not working, so introduces one flag to mark
if the HCD supports to run giveback URB in tasklet context. When all HCDs
are ready, the flag can be removed.
[1], http://marc.info/?t=136438111600010&r=1&w=2
Cc: Oliver Neukum <oliver@neukum.org>
Acked-by: Alan Stern <stern@rowland.harvard.edu>
Signed-off-by: Ming Lei <ming.lei@canonical.com>
Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
2013-07-03 18:53:07 +04:00
|
|
|
}
|
|
|
|
|
|
|
|
/* check if there are new URBs to giveback */
|
|
|
|
spin_lock_irq(&bh->lock);
|
|
|
|
if (!list_empty(&bh->head))
|
|
|
|
goto restart;
|
|
|
|
bh->running = false;
|
|
|
|
spin_unlock_irq(&bh->lock);
|
|
|
|
}
|
|
|
|
|
2007-07-18 20:08:02 +04:00
|
|
|
/**
|
|
|
|
* usb_hcd_giveback_urb - return URB from HCD to device driver
|
|
|
|
* @hcd: host controller returning the URB
|
|
|
|
* @urb: urb being returned to the USB device driver.
|
2007-08-24 23:42:24 +04:00
|
|
|
* @status: completion status code for the URB.
|
2007-07-18 20:08:02 +04:00
|
|
|
* Context: in_interrupt()
|
|
|
|
*
|
|
|
|
* This hands the URB from HCD to its USB device driver, using its
|
|
|
|
* completion function. The HCD has freed all per-urb resources
|
|
|
|
* (and is done using urb->hcpriv). It also released all HCD locks;
|
|
|
|
* the device driver won't cause problems if it frees, modifies,
|
|
|
|
* or resubmits this URB.
|
2007-08-21 23:40:36 +04:00
|
|
|
*
|
2007-08-24 23:42:24 +04:00
|
|
|
* If @urb was unlinked, the value of @status will be overridden by
|
2007-08-21 23:40:36 +04:00
|
|
|
* @urb->unlinked. Erroneous short transfers are detected in case
|
|
|
|
* the HCD hasn't checked for them.
|
2007-07-18 20:08:02 +04:00
|
|
|
*/
|
2007-08-24 23:42:24 +04:00
|
|
|
void usb_hcd_giveback_urb(struct usb_hcd *hcd, struct urb *urb, int status)
|
2007-07-18 20:08:02 +04:00
|
|
|
{
|
USB: HCD: support giveback of URB in tasklet context
This patch implements the mechanism of giveback of URB in
tasklet context, so that hardware interrupt handling time for
usb host controller can be saved much, and HCD interrupt handling
can be simplified.
Motivations:
1), on some arch(such as ARM), DMA mapping/unmapping is a bit
time-consuming, for example: when accessing usb mass storage
via EHCI on pandaboard, the common length of transfer buffer is 120KB,
the time consumed on DMA unmapping may reach hundreds of microseconds;
even on A15 based box, the time is still about scores of microseconds
2), on some arch, reading DMA coherent memoery is very time-consuming,
the most common example is usb video class driver[1]
3), driver's complete() callback may do much things which is driver
specific, so the time is consumed unnecessarily in hardware irq context.
4), running driver's complete() callback in hardware irq context causes
that host controller driver has to release its lock in interrupt handler,
so reacquiring the lock after return may busy wait a while and increase
interrupt handling time. More seriously, releasing the HCD lock makes
HCD becoming quite complicated to deal with introduced races.
So the patch proposes to run giveback of URB in tasklet context, then
time consumed in HCD irq handling doesn't depend on drivers' complete and
DMA mapping/unmapping any more, also we can simplify HCD since the HCD
lock isn't needed to be released during irq handling.
The patch should be reasonable and doable:
1), for drivers, they don't care if the complete() is called in hard irq
context or softirq context
2), the biggest change is the situation in which usb_submit_urb() is called
in complete() callback, so the introduced tasklet schedule delay might be a
con, but it shouldn't be a big deal:
- control/bulk asynchronous transfer isn't sensitive to schedule
delay
- the patch schedules giveback of periodic URBs using
tasklet_hi_schedule, so the introduced delay should be very
small
- for ISOC transfer, generally, drivers submit several URBs
concurrently to avoid interrupt delay, so it is OK with the
little schedule delay.
- for interrupt transfer, generally, drivers only submit one URB
at the same time, but interrupt transfer is often used in event
report, polling, ... situations, and a little delay should be OK.
Considered that HCDs may optimize on submitting URB in complete(), the
patch may cause the optimization not working, so introduces one flag to mark
if the HCD supports to run giveback URB in tasklet context. When all HCDs
are ready, the flag can be removed.
[1], http://marc.info/?t=136438111600010&r=1&w=2
Cc: Oliver Neukum <oliver@neukum.org>
Acked-by: Alan Stern <stern@rowland.harvard.edu>
Signed-off-by: Ming Lei <ming.lei@canonical.com>
Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
2013-07-03 18:53:07 +04:00
|
|
|
struct giveback_urb_bh *bh;
|
|
|
|
bool running, high_prio_bh;
|
2007-07-18 20:08:02 +04:00
|
|
|
|
USB: HCD: support giveback of URB in tasklet context
This patch implements the mechanism of giveback of URB in
tasklet context, so that hardware interrupt handling time for
usb host controller can be saved much, and HCD interrupt handling
can be simplified.
Motivations:
1), on some arch(such as ARM), DMA mapping/unmapping is a bit
time-consuming, for example: when accessing usb mass storage
via EHCI on pandaboard, the common length of transfer buffer is 120KB,
the time consumed on DMA unmapping may reach hundreds of microseconds;
even on A15 based box, the time is still about scores of microseconds
2), on some arch, reading DMA coherent memoery is very time-consuming,
the most common example is usb video class driver[1]
3), driver's complete() callback may do much things which is driver
specific, so the time is consumed unnecessarily in hardware irq context.
4), running driver's complete() callback in hardware irq context causes
that host controller driver has to release its lock in interrupt handler,
so reacquiring the lock after return may busy wait a while and increase
interrupt handling time. More seriously, releasing the HCD lock makes
HCD becoming quite complicated to deal with introduced races.
So the patch proposes to run giveback of URB in tasklet context, then
time consumed in HCD irq handling doesn't depend on drivers' complete and
DMA mapping/unmapping any more, also we can simplify HCD since the HCD
lock isn't needed to be released during irq handling.
The patch should be reasonable and doable:
1), for drivers, they don't care if the complete() is called in hard irq
context or softirq context
2), the biggest change is the situation in which usb_submit_urb() is called
in complete() callback, so the introduced tasklet schedule delay might be a
con, but it shouldn't be a big deal:
- control/bulk asynchronous transfer isn't sensitive to schedule
delay
- the patch schedules giveback of periodic URBs using
tasklet_hi_schedule, so the introduced delay should be very
small
- for ISOC transfer, generally, drivers submit several URBs
concurrently to avoid interrupt delay, so it is OK with the
little schedule delay.
- for interrupt transfer, generally, drivers only submit one URB
at the same time, but interrupt transfer is often used in event
report, polling, ... situations, and a little delay should be OK.
Considered that HCDs may optimize on submitting URB in complete(), the
patch may cause the optimization not working, so introduces one flag to mark
if the HCD supports to run giveback URB in tasklet context. When all HCDs
are ready, the flag can be removed.
[1], http://marc.info/?t=136438111600010&r=1&w=2
Cc: Oliver Neukum <oliver@neukum.org>
Acked-by: Alan Stern <stern@rowland.harvard.edu>
Signed-off-by: Ming Lei <ming.lei@canonical.com>
Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
2013-07-03 18:53:07 +04:00
|
|
|
/* pass status to tasklet via unlinked */
|
|
|
|
if (likely(!urb->unlinked))
|
|
|
|
urb->unlinked = status;
|
2007-08-22 21:06:53 +04:00
|
|
|
|
USB: HCD: support giveback of URB in tasklet context
This patch implements the mechanism of giveback of URB in
tasklet context, so that hardware interrupt handling time for
usb host controller can be saved much, and HCD interrupt handling
can be simplified.
Motivations:
1), on some arch(such as ARM), DMA mapping/unmapping is a bit
time-consuming, for example: when accessing usb mass storage
via EHCI on pandaboard, the common length of transfer buffer is 120KB,
the time consumed on DMA unmapping may reach hundreds of microseconds;
even on A15 based box, the time is still about scores of microseconds
2), on some arch, reading DMA coherent memoery is very time-consuming,
the most common example is usb video class driver[1]
3), driver's complete() callback may do much things which is driver
specific, so the time is consumed unnecessarily in hardware irq context.
4), running driver's complete() callback in hardware irq context causes
that host controller driver has to release its lock in interrupt handler,
so reacquiring the lock after return may busy wait a while and increase
interrupt handling time. More seriously, releasing the HCD lock makes
HCD becoming quite complicated to deal with introduced races.
So the patch proposes to run giveback of URB in tasklet context, then
time consumed in HCD irq handling doesn't depend on drivers' complete and
DMA mapping/unmapping any more, also we can simplify HCD since the HCD
lock isn't needed to be released during irq handling.
The patch should be reasonable and doable:
1), for drivers, they don't care if the complete() is called in hard irq
context or softirq context
2), the biggest change is the situation in which usb_submit_urb() is called
in complete() callback, so the introduced tasklet schedule delay might be a
con, but it shouldn't be a big deal:
- control/bulk asynchronous transfer isn't sensitive to schedule
delay
- the patch schedules giveback of periodic URBs using
tasklet_hi_schedule, so the introduced delay should be very
small
- for ISOC transfer, generally, drivers submit several URBs
concurrently to avoid interrupt delay, so it is OK with the
little schedule delay.
- for interrupt transfer, generally, drivers only submit one URB
at the same time, but interrupt transfer is often used in event
report, polling, ... situations, and a little delay should be OK.
Considered that HCDs may optimize on submitting URB in complete(), the
patch may cause the optimization not working, so introduces one flag to mark
if the HCD supports to run giveback URB in tasklet context. When all HCDs
are ready, the flag can be removed.
[1], http://marc.info/?t=136438111600010&r=1&w=2
Cc: Oliver Neukum <oliver@neukum.org>
Acked-by: Alan Stern <stern@rowland.harvard.edu>
Signed-off-by: Ming Lei <ming.lei@canonical.com>
Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
2013-07-03 18:53:07 +04:00
|
|
|
if (!hcd_giveback_urb_in_bh(hcd) && !is_root_hub(urb->dev)) {
|
|
|
|
__usb_hcd_giveback_urb(urb);
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (usb_pipeisoc(urb->pipe) || usb_pipeint(urb->pipe)) {
|
|
|
|
bh = &hcd->high_prio_bh;
|
|
|
|
high_prio_bh = true;
|
|
|
|
} else {
|
|
|
|
bh = &hcd->low_prio_bh;
|
|
|
|
high_prio_bh = false;
|
|
|
|
}
|
|
|
|
|
|
|
|
spin_lock(&bh->lock);
|
|
|
|
list_add_tail(&urb->urb_list, &bh->head);
|
|
|
|
running = bh->running;
|
|
|
|
spin_unlock(&bh->lock);
|
|
|
|
|
|
|
|
if (running)
|
|
|
|
;
|
|
|
|
else if (high_prio_bh)
|
|
|
|
tasklet_hi_schedule(&bh->bh);
|
|
|
|
else
|
|
|
|
tasklet_schedule(&bh->bh);
|
2007-07-18 20:08:02 +04:00
|
|
|
}
|
2008-01-25 20:12:21 +03:00
|
|
|
EXPORT_SYMBOL_GPL(usb_hcd_giveback_urb);
|
2007-07-18 20:08:02 +04:00
|
|
|
|
|
|
|
/*-------------------------------------------------------------------------*/
|
|
|
|
|
2007-09-10 19:33:05 +04:00
|
|
|
/* Cancel all URBs pending on this endpoint and wait for the endpoint's
|
|
|
|
* queue to drain completely. The caller must first insure that no more
|
|
|
|
* URBs can be submitted for this endpoint.
|
2005-04-17 02:20:36 +04:00
|
|
|
*/
|
2007-09-10 19:33:05 +04:00
|
|
|
void usb_hcd_flush_endpoint(struct usb_device *udev,
|
2006-08-30 19:27:36 +04:00
|
|
|
struct usb_host_endpoint *ep)
|
2005-04-17 02:20:36 +04:00
|
|
|
{
|
|
|
|
struct usb_hcd *hcd;
|
|
|
|
struct urb *urb;
|
|
|
|
|
2007-09-10 19:33:05 +04:00
|
|
|
if (!ep)
|
|
|
|
return;
|
2007-08-02 23:06:54 +04:00
|
|
|
might_sleep();
|
2006-08-30 19:32:52 +04:00
|
|
|
hcd = bus_to_hcd(udev->bus);
|
2005-04-17 02:20:36 +04:00
|
|
|
|
2007-09-10 19:33:05 +04:00
|
|
|
/* No more submits can occur */
|
2007-08-02 23:06:54 +04:00
|
|
|
spin_lock_irq(&hcd_urb_list_lock);
|
2007-11-22 02:13:10 +03:00
|
|
|
rescan:
|
2005-04-17 02:20:36 +04:00
|
|
|
list_for_each_entry (urb, &ep->urb_list, urb_list) {
|
2007-07-31 01:07:21 +04:00
|
|
|
int is_in;
|
2005-04-17 02:20:36 +04:00
|
|
|
|
2007-08-21 23:40:36 +04:00
|
|
|
if (urb->unlinked)
|
2005-04-17 02:20:36 +04:00
|
|
|
continue;
|
|
|
|
usb_get_urb (urb);
|
2007-07-31 01:07:21 +04:00
|
|
|
is_in = usb_urb_dir_in(urb);
|
2007-07-18 20:14:24 +04:00
|
|
|
spin_unlock(&hcd_urb_list_lock);
|
2005-04-17 02:20:36 +04:00
|
|
|
|
2007-08-08 19:48:02 +04:00
|
|
|
/* kick hcd */
|
|
|
|
unlink1(hcd, urb, -ESHUTDOWN);
|
|
|
|
dev_dbg (hcd->self.controller,
|
2017-05-16 15:38:08 +03:00
|
|
|
"shutdown urb %pK ep%d%s%s\n",
|
2007-08-08 19:48:02 +04:00
|
|
|
urb, usb_endpoint_num(&ep->desc),
|
|
|
|
is_in ? "in" : "out",
|
|
|
|
({ char *s;
|
|
|
|
|
|
|
|
switch (usb_endpoint_type(&ep->desc)) {
|
|
|
|
case USB_ENDPOINT_XFER_CONTROL:
|
|
|
|
s = ""; break;
|
|
|
|
case USB_ENDPOINT_XFER_BULK:
|
|
|
|
s = "-bulk"; break;
|
|
|
|
case USB_ENDPOINT_XFER_INT:
|
|
|
|
s = "-intr"; break;
|
|
|
|
default:
|
2013-10-05 20:02:07 +04:00
|
|
|
s = "-iso"; break;
|
2007-08-08 19:48:02 +04:00
|
|
|
};
|
|
|
|
s;
|
|
|
|
}));
|
2005-04-17 02:20:36 +04:00
|
|
|
usb_put_urb (urb);
|
|
|
|
|
|
|
|
/* list contents may have changed */
|
2007-11-22 02:13:10 +03:00
|
|
|
spin_lock(&hcd_urb_list_lock);
|
2005-04-17 02:20:36 +04:00
|
|
|
goto rescan;
|
|
|
|
}
|
2007-08-02 23:06:54 +04:00
|
|
|
spin_unlock_irq(&hcd_urb_list_lock);
|
2005-04-17 02:20:36 +04:00
|
|
|
|
2007-09-10 19:33:05 +04:00
|
|
|
/* Wait until the endpoint queue is completely empty */
|
2006-08-12 00:01:45 +04:00
|
|
|
while (!list_empty (&ep->urb_list)) {
|
2007-07-18 20:14:24 +04:00
|
|
|
spin_lock_irq(&hcd_urb_list_lock);
|
2006-08-12 00:01:45 +04:00
|
|
|
|
|
|
|
/* The list may have changed while we acquired the spinlock */
|
|
|
|
urb = NULL;
|
|
|
|
if (!list_empty (&ep->urb_list)) {
|
|
|
|
urb = list_entry (ep->urb_list.prev, struct urb,
|
|
|
|
urb_list);
|
|
|
|
usb_get_urb (urb);
|
|
|
|
}
|
2007-07-18 20:14:24 +04:00
|
|
|
spin_unlock_irq(&hcd_urb_list_lock);
|
2006-08-12 00:01:45 +04:00
|
|
|
|
|
|
|
if (urb) {
|
|
|
|
usb_kill_urb (urb);
|
|
|
|
usb_put_urb (urb);
|
|
|
|
}
|
|
|
|
}
|
2005-04-17 02:20:36 +04:00
|
|
|
}
|
|
|
|
|
USB: Check bandwidth when switching alt settings.
Make the USB core check the bandwidth when switching from one
interface alternate setting to another. Also check the bandwidth
when resetting a configuration (so that alt setting 0 is used). If
this check fails, the device's state is unchanged. If the device
refuses the new alt setting, re-instate the old alt setting in the
host controller hardware.
If a USB device doesn't have an alternate interface setting 0, install
the first alt setting in its descriptors when a new configuration is
requested, or the device is reset.
Add a mutex per root hub to protect bandwidth operations:
adding/reseting/changing configurations, and changing alternate interface
settings. We want to ensure that the xHCI host controller and the USB
device are set up for the same configurations and alternate settings.
There are two (possibly three) steps to do this:
1. The host controller needs to check that bandwidth is available for a
different setting, by issuing and waiting for a configure endpoint
command.
2. Once that returns successfully, a control message is sent to the
device.
3. If that fails, the host controller must be notified through another
configure endpoint command.
The mutex is used to make these three operations seem atomic, to prevent
another driver from using more bandwidth for a different device while
we're in the middle of these operations.
While we're touching the bandwidth code, rename usb_hcd_check_bandwidth()
to usb_hcd_alloc_bandwidth(). This function does more than just check
that the bandwidth change won't exceed the bus bandwidth; it actually
changes the bandwidth configuration in the xHCI host controller.
Signed-off-by: Sarah Sharp <sarah.a.sharp@linux.intel.com>
Signed-off-by: Greg Kroah-Hartman <gregkh@suse.de>
2009-12-03 20:44:36 +03:00
|
|
|
/**
|
2009-12-13 21:17:16 +03:00
|
|
|
* usb_hcd_alloc_bandwidth - check whether a new bandwidth setting exceeds
|
|
|
|
* the bus bandwidth
|
|
|
|
* @udev: target &usb_device
|
USB: Check bandwidth when switching alt settings.
Make the USB core check the bandwidth when switching from one
interface alternate setting to another. Also check the bandwidth
when resetting a configuration (so that alt setting 0 is used). If
this check fails, the device's state is unchanged. If the device
refuses the new alt setting, re-instate the old alt setting in the
host controller hardware.
If a USB device doesn't have an alternate interface setting 0, install
the first alt setting in its descriptors when a new configuration is
requested, or the device is reset.
Add a mutex per root hub to protect bandwidth operations:
adding/reseting/changing configurations, and changing alternate interface
settings. We want to ensure that the xHCI host controller and the USB
device are set up for the same configurations and alternate settings.
There are two (possibly three) steps to do this:
1. The host controller needs to check that bandwidth is available for a
different setting, by issuing and waiting for a configure endpoint
command.
2. Once that returns successfully, a control message is sent to the
device.
3. If that fails, the host controller must be notified through another
configure endpoint command.
The mutex is used to make these three operations seem atomic, to prevent
another driver from using more bandwidth for a different device while
we're in the middle of these operations.
While we're touching the bandwidth code, rename usb_hcd_check_bandwidth()
to usb_hcd_alloc_bandwidth(). This function does more than just check
that the bandwidth change won't exceed the bus bandwidth; it actually
changes the bandwidth configuration in the xHCI host controller.
Signed-off-by: Sarah Sharp <sarah.a.sharp@linux.intel.com>
Signed-off-by: Greg Kroah-Hartman <gregkh@suse.de>
2009-12-03 20:44:36 +03:00
|
|
|
* @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.
|
|
|
|
*
|
2013-08-02 22:10:04 +04:00
|
|
|
* Return: An error if the requested bandwidth change exceeds the
|
USB: Check bandwidth when switching alt settings.
Make the USB core check the bandwidth when switching from one
interface alternate setting to another. Also check the bandwidth
when resetting a configuration (so that alt setting 0 is used). If
this check fails, the device's state is unchanged. If the device
refuses the new alt setting, re-instate the old alt setting in the
host controller hardware.
If a USB device doesn't have an alternate interface setting 0, install
the first alt setting in its descriptors when a new configuration is
requested, or the device is reset.
Add a mutex per root hub to protect bandwidth operations:
adding/reseting/changing configurations, and changing alternate interface
settings. We want to ensure that the xHCI host controller and the USB
device are set up for the same configurations and alternate settings.
There are two (possibly three) steps to do this:
1. The host controller needs to check that bandwidth is available for a
different setting, by issuing and waiting for a configure endpoint
command.
2. Once that returns successfully, a control message is sent to the
device.
3. If that fails, the host controller must be notified through another
configure endpoint command.
The mutex is used to make these three operations seem atomic, to prevent
another driver from using more bandwidth for a different device while
we're in the middle of these operations.
While we're touching the bandwidth code, rename usb_hcd_check_bandwidth()
to usb_hcd_alloc_bandwidth(). This function does more than just check
that the bandwidth change won't exceed the bus bandwidth; it actually
changes the bandwidth configuration in the xHCI host controller.
Signed-off-by: Sarah Sharp <sarah.a.sharp@linux.intel.com>
Signed-off-by: Greg Kroah-Hartman <gregkh@suse.de>
2009-12-03 20:44:36 +03:00
|
|
|
* bus bandwidth or host controller internal resources.
|
USB: Support for bandwidth allocation.
Originally, the USB core had no support for allocating bandwidth when a
particular configuration or alternate setting for an interface was
selected. Instead, the device driver's URB submission would fail if
there was not enough bandwidth for a periodic endpoint. Drivers could
work around this, by using the scatter-gather list API to guarantee
bandwidth.
This patch adds host controller API to allow the USB core to allocate or
deallocate bandwidth for an endpoint. Endpoints are added to or dropped
from a copy of the current schedule by calling add_endpoint() or
drop_endpoint(), and then the schedule is atomically evaluated with a
call to check_bandwidth(). This allows all the endpoints for a new
configuration or alternate setting to be added at the same time that the
endpoints from the old configuration or alt setting are dropped.
Endpoints must be added to the schedule before any URBs are submitted to
them. The HCD must be allowed to reject a new configuration or alt
setting before the control transfer is sent to the device requesting the
change. It may reject the change because there is not enough bandwidth,
not enough internal resources (such as memory on an embedded host
controller), or perhaps even for security reasons in a virtualized
environment.
If the call to check_bandwidth() fails, the USB core must call
reset_bandwidth(). This causes the schedule to be reverted back to the
state it was in just after the last successful check_bandwidth() call.
If the call succeeds, the host controller driver (and hardware) will have
changed its internal state to match the new configuration or alternate
setting. The USB core can then issue a control transfer to the device to
change the configuration or alt setting. This allows the core to test new
configurations or alternate settings before unbinding drivers bound to
interfaces in the old configuration.
WIP:
The USB core must add endpoints from all interfaces in a configuration
to the schedule, because a driver may claim that interface at any time.
A slight optimization might be to add the endpoints to the schedule once
a driver claims that interface. FIXME
This patch does not cover changing alternate settings, but it does
handle a configuration change or de-configuration. FIXME
The code for managing the schedule is currently HCD specific. A generic
scheduling algorithm could be added for host controllers without
built-in scheduling support. For now, if a host controller does not
define the check_bandwidth() function, the call to
usb_hcd_check_bandwidth() will always succeed.
Signed-off-by: Sarah Sharp <sarah.a.sharp@linux.intel.com>
Signed-off-by: Greg Kroah-Hartman <gregkh@suse.de>
2009-04-28 06:58:26 +04:00
|
|
|
*/
|
USB: Check bandwidth when switching alt settings.
Make the USB core check the bandwidth when switching from one
interface alternate setting to another. Also check the bandwidth
when resetting a configuration (so that alt setting 0 is used). If
this check fails, the device's state is unchanged. If the device
refuses the new alt setting, re-instate the old alt setting in the
host controller hardware.
If a USB device doesn't have an alternate interface setting 0, install
the first alt setting in its descriptors when a new configuration is
requested, or the device is reset.
Add a mutex per root hub to protect bandwidth operations:
adding/reseting/changing configurations, and changing alternate interface
settings. We want to ensure that the xHCI host controller and the USB
device are set up for the same configurations and alternate settings.
There are two (possibly three) steps to do this:
1. The host controller needs to check that bandwidth is available for a
different setting, by issuing and waiting for a configure endpoint
command.
2. Once that returns successfully, a control message is sent to the
device.
3. If that fails, the host controller must be notified through another
configure endpoint command.
The mutex is used to make these three operations seem atomic, to prevent
another driver from using more bandwidth for a different device while
we're in the middle of these operations.
While we're touching the bandwidth code, rename usb_hcd_check_bandwidth()
to usb_hcd_alloc_bandwidth(). This function does more than just check
that the bandwidth change won't exceed the bus bandwidth; it actually
changes the bandwidth configuration in the xHCI host controller.
Signed-off-by: Sarah Sharp <sarah.a.sharp@linux.intel.com>
Signed-off-by: Greg Kroah-Hartman <gregkh@suse.de>
2009-12-03 20:44:36 +03:00
|
|
|
int usb_hcd_alloc_bandwidth(struct usb_device *udev,
|
USB: Support for bandwidth allocation.
Originally, the USB core had no support for allocating bandwidth when a
particular configuration or alternate setting for an interface was
selected. Instead, the device driver's URB submission would fail if
there was not enough bandwidth for a periodic endpoint. Drivers could
work around this, by using the scatter-gather list API to guarantee
bandwidth.
This patch adds host controller API to allow the USB core to allocate or
deallocate bandwidth for an endpoint. Endpoints are added to or dropped
from a copy of the current schedule by calling add_endpoint() or
drop_endpoint(), and then the schedule is atomically evaluated with a
call to check_bandwidth(). This allows all the endpoints for a new
configuration or alternate setting to be added at the same time that the
endpoints from the old configuration or alt setting are dropped.
Endpoints must be added to the schedule before any URBs are submitted to
them. The HCD must be allowed to reject a new configuration or alt
setting before the control transfer is sent to the device requesting the
change. It may reject the change because there is not enough bandwidth,
not enough internal resources (such as memory on an embedded host
controller), or perhaps even for security reasons in a virtualized
environment.
If the call to check_bandwidth() fails, the USB core must call
reset_bandwidth(). This causes the schedule to be reverted back to the
state it was in just after the last successful check_bandwidth() call.
If the call succeeds, the host controller driver (and hardware) will have
changed its internal state to match the new configuration or alternate
setting. The USB core can then issue a control transfer to the device to
change the configuration or alt setting. This allows the core to test new
configurations or alternate settings before unbinding drivers bound to
interfaces in the old configuration.
WIP:
The USB core must add endpoints from all interfaces in a configuration
to the schedule, because a driver may claim that interface at any time.
A slight optimization might be to add the endpoints to the schedule once
a driver claims that interface. FIXME
This patch does not cover changing alternate settings, but it does
handle a configuration change or de-configuration. FIXME
The code for managing the schedule is currently HCD specific. A generic
scheduling algorithm could be added for host controllers without
built-in scheduling support. For now, if a host controller does not
define the check_bandwidth() function, the call to
usb_hcd_check_bandwidth() will always succeed.
Signed-off-by: Sarah Sharp <sarah.a.sharp@linux.intel.com>
Signed-off-by: Greg Kroah-Hartman <gregkh@suse.de>
2009-04-28 06:58:26 +04:00
|
|
|
struct usb_host_config *new_config,
|
USB: Check bandwidth when switching alt settings.
Make the USB core check the bandwidth when switching from one
interface alternate setting to another. Also check the bandwidth
when resetting a configuration (so that alt setting 0 is used). If
this check fails, the device's state is unchanged. If the device
refuses the new alt setting, re-instate the old alt setting in the
host controller hardware.
If a USB device doesn't have an alternate interface setting 0, install
the first alt setting in its descriptors when a new configuration is
requested, or the device is reset.
Add a mutex per root hub to protect bandwidth operations:
adding/reseting/changing configurations, and changing alternate interface
settings. We want to ensure that the xHCI host controller and the USB
device are set up for the same configurations and alternate settings.
There are two (possibly three) steps to do this:
1. The host controller needs to check that bandwidth is available for a
different setting, by issuing and waiting for a configure endpoint
command.
2. Once that returns successfully, a control message is sent to the
device.
3. If that fails, the host controller must be notified through another
configure endpoint command.
The mutex is used to make these three operations seem atomic, to prevent
another driver from using more bandwidth for a different device while
we're in the middle of these operations.
While we're touching the bandwidth code, rename usb_hcd_check_bandwidth()
to usb_hcd_alloc_bandwidth(). This function does more than just check
that the bandwidth change won't exceed the bus bandwidth; it actually
changes the bandwidth configuration in the xHCI host controller.
Signed-off-by: Sarah Sharp <sarah.a.sharp@linux.intel.com>
Signed-off-by: Greg Kroah-Hartman <gregkh@suse.de>
2009-12-03 20:44:36 +03:00
|
|
|
struct usb_host_interface *cur_alt,
|
|
|
|
struct usb_host_interface *new_alt)
|
USB: Support for bandwidth allocation.
Originally, the USB core had no support for allocating bandwidth when a
particular configuration or alternate setting for an interface was
selected. Instead, the device driver's URB submission would fail if
there was not enough bandwidth for a periodic endpoint. Drivers could
work around this, by using the scatter-gather list API to guarantee
bandwidth.
This patch adds host controller API to allow the USB core to allocate or
deallocate bandwidth for an endpoint. Endpoints are added to or dropped
from a copy of the current schedule by calling add_endpoint() or
drop_endpoint(), and then the schedule is atomically evaluated with a
call to check_bandwidth(). This allows all the endpoints for a new
configuration or alternate setting to be added at the same time that the
endpoints from the old configuration or alt setting are dropped.
Endpoints must be added to the schedule before any URBs are submitted to
them. The HCD must be allowed to reject a new configuration or alt
setting before the control transfer is sent to the device requesting the
change. It may reject the change because there is not enough bandwidth,
not enough internal resources (such as memory on an embedded host
controller), or perhaps even for security reasons in a virtualized
environment.
If the call to check_bandwidth() fails, the USB core must call
reset_bandwidth(). This causes the schedule to be reverted back to the
state it was in just after the last successful check_bandwidth() call.
If the call succeeds, the host controller driver (and hardware) will have
changed its internal state to match the new configuration or alternate
setting. The USB core can then issue a control transfer to the device to
change the configuration or alt setting. This allows the core to test new
configurations or alternate settings before unbinding drivers bound to
interfaces in the old configuration.
WIP:
The USB core must add endpoints from all interfaces in a configuration
to the schedule, because a driver may claim that interface at any time.
A slight optimization might be to add the endpoints to the schedule once
a driver claims that interface. FIXME
This patch does not cover changing alternate settings, but it does
handle a configuration change or de-configuration. FIXME
The code for managing the schedule is currently HCD specific. A generic
scheduling algorithm could be added for host controllers without
built-in scheduling support. For now, if a host controller does not
define the check_bandwidth() function, the call to
usb_hcd_check_bandwidth() will always succeed.
Signed-off-by: Sarah Sharp <sarah.a.sharp@linux.intel.com>
Signed-off-by: Greg Kroah-Hartman <gregkh@suse.de>
2009-04-28 06:58:26 +04:00
|
|
|
{
|
|
|
|
int num_intfs, i, j;
|
2009-11-18 01:53:41 +03:00
|
|
|
struct usb_host_interface *alt = NULL;
|
USB: Support for bandwidth allocation.
Originally, the USB core had no support for allocating bandwidth when a
particular configuration or alternate setting for an interface was
selected. Instead, the device driver's URB submission would fail if
there was not enough bandwidth for a periodic endpoint. Drivers could
work around this, by using the scatter-gather list API to guarantee
bandwidth.
This patch adds host controller API to allow the USB core to allocate or
deallocate bandwidth for an endpoint. Endpoints are added to or dropped
from a copy of the current schedule by calling add_endpoint() or
drop_endpoint(), and then the schedule is atomically evaluated with a
call to check_bandwidth(). This allows all the endpoints for a new
configuration or alternate setting to be added at the same time that the
endpoints from the old configuration or alt setting are dropped.
Endpoints must be added to the schedule before any URBs are submitted to
them. The HCD must be allowed to reject a new configuration or alt
setting before the control transfer is sent to the device requesting the
change. It may reject the change because there is not enough bandwidth,
not enough internal resources (such as memory on an embedded host
controller), or perhaps even for security reasons in a virtualized
environment.
If the call to check_bandwidth() fails, the USB core must call
reset_bandwidth(). This causes the schedule to be reverted back to the
state it was in just after the last successful check_bandwidth() call.
If the call succeeds, the host controller driver (and hardware) will have
changed its internal state to match the new configuration or alternate
setting. The USB core can then issue a control transfer to the device to
change the configuration or alt setting. This allows the core to test new
configurations or alternate settings before unbinding drivers bound to
interfaces in the old configuration.
WIP:
The USB core must add endpoints from all interfaces in a configuration
to the schedule, because a driver may claim that interface at any time.
A slight optimization might be to add the endpoints to the schedule once
a driver claims that interface. FIXME
This patch does not cover changing alternate settings, but it does
handle a configuration change or de-configuration. FIXME
The code for managing the schedule is currently HCD specific. A generic
scheduling algorithm could be added for host controllers without
built-in scheduling support. For now, if a host controller does not
define the check_bandwidth() function, the call to
usb_hcd_check_bandwidth() will always succeed.
Signed-off-by: Sarah Sharp <sarah.a.sharp@linux.intel.com>
Signed-off-by: Greg Kroah-Hartman <gregkh@suse.de>
2009-04-28 06:58:26 +04:00
|
|
|
int ret = 0;
|
|
|
|
struct usb_hcd *hcd;
|
|
|
|
struct usb_host_endpoint *ep;
|
|
|
|
|
|
|
|
hcd = bus_to_hcd(udev->bus);
|
|
|
|
if (!hcd->driver->check_bandwidth)
|
|
|
|
return 0;
|
|
|
|
|
|
|
|
/* Configuration is being removed - set configuration 0 */
|
USB: Check bandwidth when switching alt settings.
Make the USB core check the bandwidth when switching from one
interface alternate setting to another. Also check the bandwidth
when resetting a configuration (so that alt setting 0 is used). If
this check fails, the device's state is unchanged. If the device
refuses the new alt setting, re-instate the old alt setting in the
host controller hardware.
If a USB device doesn't have an alternate interface setting 0, install
the first alt setting in its descriptors when a new configuration is
requested, or the device is reset.
Add a mutex per root hub to protect bandwidth operations:
adding/reseting/changing configurations, and changing alternate interface
settings. We want to ensure that the xHCI host controller and the USB
device are set up for the same configurations and alternate settings.
There are two (possibly three) steps to do this:
1. The host controller needs to check that bandwidth is available for a
different setting, by issuing and waiting for a configure endpoint
command.
2. Once that returns successfully, a control message is sent to the
device.
3. If that fails, the host controller must be notified through another
configure endpoint command.
The mutex is used to make these three operations seem atomic, to prevent
another driver from using more bandwidth for a different device while
we're in the middle of these operations.
While we're touching the bandwidth code, rename usb_hcd_check_bandwidth()
to usb_hcd_alloc_bandwidth(). This function does more than just check
that the bandwidth change won't exceed the bus bandwidth; it actually
changes the bandwidth configuration in the xHCI host controller.
Signed-off-by: Sarah Sharp <sarah.a.sharp@linux.intel.com>
Signed-off-by: Greg Kroah-Hartman <gregkh@suse.de>
2009-12-03 20:44:36 +03:00
|
|
|
if (!new_config && !cur_alt) {
|
USB: Support for bandwidth allocation.
Originally, the USB core had no support for allocating bandwidth when a
particular configuration or alternate setting for an interface was
selected. Instead, the device driver's URB submission would fail if
there was not enough bandwidth for a periodic endpoint. Drivers could
work around this, by using the scatter-gather list API to guarantee
bandwidth.
This patch adds host controller API to allow the USB core to allocate or
deallocate bandwidth for an endpoint. Endpoints are added to or dropped
from a copy of the current schedule by calling add_endpoint() or
drop_endpoint(), and then the schedule is atomically evaluated with a
call to check_bandwidth(). This allows all the endpoints for a new
configuration or alternate setting to be added at the same time that the
endpoints from the old configuration or alt setting are dropped.
Endpoints must be added to the schedule before any URBs are submitted to
them. The HCD must be allowed to reject a new configuration or alt
setting before the control transfer is sent to the device requesting the
change. It may reject the change because there is not enough bandwidth,
not enough internal resources (such as memory on an embedded host
controller), or perhaps even for security reasons in a virtualized
environment.
If the call to check_bandwidth() fails, the USB core must call
reset_bandwidth(). This causes the schedule to be reverted back to the
state it was in just after the last successful check_bandwidth() call.
If the call succeeds, the host controller driver (and hardware) will have
changed its internal state to match the new configuration or alternate
setting. The USB core can then issue a control transfer to the device to
change the configuration or alt setting. This allows the core to test new
configurations or alternate settings before unbinding drivers bound to
interfaces in the old configuration.
WIP:
The USB core must add endpoints from all interfaces in a configuration
to the schedule, because a driver may claim that interface at any time.
A slight optimization might be to add the endpoints to the schedule once
a driver claims that interface. FIXME
This patch does not cover changing alternate settings, but it does
handle a configuration change or de-configuration. FIXME
The code for managing the schedule is currently HCD specific. A generic
scheduling algorithm could be added for host controllers without
built-in scheduling support. For now, if a host controller does not
define the check_bandwidth() function, the call to
usb_hcd_check_bandwidth() will always succeed.
Signed-off-by: Sarah Sharp <sarah.a.sharp@linux.intel.com>
Signed-off-by: Greg Kroah-Hartman <gregkh@suse.de>
2009-04-28 06:58:26 +04:00
|
|
|
for (i = 1; i < 16; ++i) {
|
|
|
|
ep = udev->ep_out[i];
|
|
|
|
if (ep)
|
|
|
|
hcd->driver->drop_endpoint(hcd, udev, ep);
|
|
|
|
ep = udev->ep_in[i];
|
|
|
|
if (ep)
|
|
|
|
hcd->driver->drop_endpoint(hcd, udev, ep);
|
|
|
|
}
|
|
|
|
hcd->driver->check_bandwidth(hcd, udev);
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
/* Check if the HCD says there's enough bandwidth. Enable all endpoints
|
|
|
|
* each interface's alt setting 0 and ask the HCD to check the bandwidth
|
|
|
|
* of the bus. There will always be bandwidth for endpoint 0, so it's
|
|
|
|
* ok to exclude it.
|
|
|
|
*/
|
|
|
|
if (new_config) {
|
|
|
|
num_intfs = new_config->desc.bNumInterfaces;
|
|
|
|
/* Remove endpoints (except endpoint 0, which is always on the
|
|
|
|
* schedule) from the old config from the schedule
|
|
|
|
*/
|
|
|
|
for (i = 1; i < 16; ++i) {
|
|
|
|
ep = udev->ep_out[i];
|
|
|
|
if (ep) {
|
|
|
|
ret = hcd->driver->drop_endpoint(hcd, udev, ep);
|
|
|
|
if (ret < 0)
|
|
|
|
goto reset;
|
|
|
|
}
|
|
|
|
ep = udev->ep_in[i];
|
|
|
|
if (ep) {
|
|
|
|
ret = hcd->driver->drop_endpoint(hcd, udev, ep);
|
|
|
|
if (ret < 0)
|
|
|
|
goto reset;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
for (i = 0; i < num_intfs; ++i) {
|
2010-01-06 01:33:29 +03:00
|
|
|
struct usb_host_interface *first_alt;
|
|
|
|
int iface_num;
|
|
|
|
|
|
|
|
first_alt = &new_config->intf_cache[i]->altsetting[0];
|
|
|
|
iface_num = first_alt->desc.bInterfaceNumber;
|
2009-12-03 20:44:34 +03:00
|
|
|
/* Set up endpoints for alternate interface setting 0 */
|
2010-01-06 01:33:29 +03:00
|
|
|
alt = usb_find_alt_setting(new_config, iface_num, 0);
|
USB: Check bandwidth when switching alt settings.
Make the USB core check the bandwidth when switching from one
interface alternate setting to another. Also check the bandwidth
when resetting a configuration (so that alt setting 0 is used). If
this check fails, the device's state is unchanged. If the device
refuses the new alt setting, re-instate the old alt setting in the
host controller hardware.
If a USB device doesn't have an alternate interface setting 0, install
the first alt setting in its descriptors when a new configuration is
requested, or the device is reset.
Add a mutex per root hub to protect bandwidth operations:
adding/reseting/changing configurations, and changing alternate interface
settings. We want to ensure that the xHCI host controller and the USB
device are set up for the same configurations and alternate settings.
There are two (possibly three) steps to do this:
1. The host controller needs to check that bandwidth is available for a
different setting, by issuing and waiting for a configure endpoint
command.
2. Once that returns successfully, a control message is sent to the
device.
3. If that fails, the host controller must be notified through another
configure endpoint command.
The mutex is used to make these three operations seem atomic, to prevent
another driver from using more bandwidth for a different device while
we're in the middle of these operations.
While we're touching the bandwidth code, rename usb_hcd_check_bandwidth()
to usb_hcd_alloc_bandwidth(). This function does more than just check
that the bandwidth change won't exceed the bus bandwidth; it actually
changes the bandwidth configuration in the xHCI host controller.
Signed-off-by: Sarah Sharp <sarah.a.sharp@linux.intel.com>
Signed-off-by: Greg Kroah-Hartman <gregkh@suse.de>
2009-12-03 20:44:36 +03:00
|
|
|
if (!alt)
|
|
|
|
/* No alt setting 0? Pick the first setting. */
|
2010-01-06 01:33:29 +03:00
|
|
|
alt = first_alt;
|
USB: Check bandwidth when switching alt settings.
Make the USB core check the bandwidth when switching from one
interface alternate setting to another. Also check the bandwidth
when resetting a configuration (so that alt setting 0 is used). If
this check fails, the device's state is unchanged. If the device
refuses the new alt setting, re-instate the old alt setting in the
host controller hardware.
If a USB device doesn't have an alternate interface setting 0, install
the first alt setting in its descriptors when a new configuration is
requested, or the device is reset.
Add a mutex per root hub to protect bandwidth operations:
adding/reseting/changing configurations, and changing alternate interface
settings. We want to ensure that the xHCI host controller and the USB
device are set up for the same configurations and alternate settings.
There are two (possibly three) steps to do this:
1. The host controller needs to check that bandwidth is available for a
different setting, by issuing and waiting for a configure endpoint
command.
2. Once that returns successfully, a control message is sent to the
device.
3. If that fails, the host controller must be notified through another
configure endpoint command.
The mutex is used to make these three operations seem atomic, to prevent
another driver from using more bandwidth for a different device while
we're in the middle of these operations.
While we're touching the bandwidth code, rename usb_hcd_check_bandwidth()
to usb_hcd_alloc_bandwidth(). This function does more than just check
that the bandwidth change won't exceed the bus bandwidth; it actually
changes the bandwidth configuration in the xHCI host controller.
Signed-off-by: Sarah Sharp <sarah.a.sharp@linux.intel.com>
Signed-off-by: Greg Kroah-Hartman <gregkh@suse.de>
2009-12-03 20:44:36 +03:00
|
|
|
|
USB: Support for bandwidth allocation.
Originally, the USB core had no support for allocating bandwidth when a
particular configuration or alternate setting for an interface was
selected. Instead, the device driver's URB submission would fail if
there was not enough bandwidth for a periodic endpoint. Drivers could
work around this, by using the scatter-gather list API to guarantee
bandwidth.
This patch adds host controller API to allow the USB core to allocate or
deallocate bandwidth for an endpoint. Endpoints are added to or dropped
from a copy of the current schedule by calling add_endpoint() or
drop_endpoint(), and then the schedule is atomically evaluated with a
call to check_bandwidth(). This allows all the endpoints for a new
configuration or alternate setting to be added at the same time that the
endpoints from the old configuration or alt setting are dropped.
Endpoints must be added to the schedule before any URBs are submitted to
them. The HCD must be allowed to reject a new configuration or alt
setting before the control transfer is sent to the device requesting the
change. It may reject the change because there is not enough bandwidth,
not enough internal resources (such as memory on an embedded host
controller), or perhaps even for security reasons in a virtualized
environment.
If the call to check_bandwidth() fails, the USB core must call
reset_bandwidth(). This causes the schedule to be reverted back to the
state it was in just after the last successful check_bandwidth() call.
If the call succeeds, the host controller driver (and hardware) will have
changed its internal state to match the new configuration or alternate
setting. The USB core can then issue a control transfer to the device to
change the configuration or alt setting. This allows the core to test new
configurations or alternate settings before unbinding drivers bound to
interfaces in the old configuration.
WIP:
The USB core must add endpoints from all interfaces in a configuration
to the schedule, because a driver may claim that interface at any time.
A slight optimization might be to add the endpoints to the schedule once
a driver claims that interface. FIXME
This patch does not cover changing alternate settings, but it does
handle a configuration change or de-configuration. FIXME
The code for managing the schedule is currently HCD specific. A generic
scheduling algorithm could be added for host controllers without
built-in scheduling support. For now, if a host controller does not
define the check_bandwidth() function, the call to
usb_hcd_check_bandwidth() will always succeed.
Signed-off-by: Sarah Sharp <sarah.a.sharp@linux.intel.com>
Signed-off-by: Greg Kroah-Hartman <gregkh@suse.de>
2009-04-28 06:58:26 +04:00
|
|
|
for (j = 0; j < alt->desc.bNumEndpoints; j++) {
|
|
|
|
ret = hcd->driver->add_endpoint(hcd, udev, &alt->endpoint[j]);
|
|
|
|
if (ret < 0)
|
|
|
|
goto reset;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
USB: Check bandwidth when switching alt settings.
Make the USB core check the bandwidth when switching from one
interface alternate setting to another. Also check the bandwidth
when resetting a configuration (so that alt setting 0 is used). If
this check fails, the device's state is unchanged. If the device
refuses the new alt setting, re-instate the old alt setting in the
host controller hardware.
If a USB device doesn't have an alternate interface setting 0, install
the first alt setting in its descriptors when a new configuration is
requested, or the device is reset.
Add a mutex per root hub to protect bandwidth operations:
adding/reseting/changing configurations, and changing alternate interface
settings. We want to ensure that the xHCI host controller and the USB
device are set up for the same configurations and alternate settings.
There are two (possibly three) steps to do this:
1. The host controller needs to check that bandwidth is available for a
different setting, by issuing and waiting for a configure endpoint
command.
2. Once that returns successfully, a control message is sent to the
device.
3. If that fails, the host controller must be notified through another
configure endpoint command.
The mutex is used to make these three operations seem atomic, to prevent
another driver from using more bandwidth for a different device while
we're in the middle of these operations.
While we're touching the bandwidth code, rename usb_hcd_check_bandwidth()
to usb_hcd_alloc_bandwidth(). This function does more than just check
that the bandwidth change won't exceed the bus bandwidth; it actually
changes the bandwidth configuration in the xHCI host controller.
Signed-off-by: Sarah Sharp <sarah.a.sharp@linux.intel.com>
Signed-off-by: Greg Kroah-Hartman <gregkh@suse.de>
2009-12-03 20:44:36 +03:00
|
|
|
if (cur_alt && new_alt) {
|
USB: Fix duplicate sysfs problem after device reset.
Borislav Petkov reports issues with duplicate sysfs endpoint files after a
resume from a hibernate. It turns out that the code to support alternate
settings under xHCI has issues when a device with a non-default alternate
setting is reset during the hibernate:
[ 427.681810] Restarting tasks ...
[ 427.681995] hub 1-0:1.0: state 7 ports 6 chg 0004 evt 0000
[ 427.682019] usb usb3: usb resume
[ 427.682030] ohci_hcd 0000:00:12.0: wakeup root hub
[ 427.682191] hub 1-0:1.0: port 2, status 0501, change 0000, 480 Mb/s
[ 427.682205] usb 1-2: usb wakeup-resume
[ 427.682226] usb 1-2: finish reset-resume
[ 427.682886] done.
[ 427.734658] ehci_hcd 0000:00:12.2: port 2 high speed
[ 427.734663] ehci_hcd 0000:00:12.2: GetStatus port 2 status 001005 POWER sig=se0 PE CONNECT
[ 427.746682] hub 3-0:1.0: hub_reset_resume
[ 427.746693] hub 3-0:1.0: trying to enable port power on non-switchable hub
[ 427.786715] usb 1-2: reset high speed USB device using ehci_hcd and address 2
[ 427.839653] ehci_hcd 0000:00:12.2: port 2 high speed
[ 427.839666] ehci_hcd 0000:00:12.2: GetStatus port 2 status 001005 POWER sig=se0 PE CONNECT
[ 427.847717] ohci_hcd 0000:00:12.0: GetStatus roothub.portstatus [1] = 0x00010100 CSC PPS
[ 427.915497] hub 1-2:1.0: remove_intf_ep_devs: if: ffff88022f9e8800 ->ep_devs_created: 1
[ 427.915774] hub 1-2:1.0: remove_intf_ep_devs: bNumEndpoints: 1
[ 427.915934] hub 1-2:1.0: if: ffff88022f9e8800: endpoint devs removed.
[ 427.916158] hub 1-2:1.0: create_intf_ep_devs: if: ffff88022f9e8800 ->ep_devs_created: 0, ->unregistering: 0
[ 427.916434] hub 1-2:1.0: create_intf_ep_devs: bNumEndpoints: 1
[ 427.916609] ep_81: create, parent hub
[ 427.916632] ------------[ cut here ]------------
[ 427.916644] WARNING: at fs/sysfs/dir.c:477 sysfs_add_one+0x82/0x96()
[ 427.916649] Hardware name: System Product Name
[ 427.916653] sysfs: cannot create duplicate filename '/devices/pci0000:00/0000:00:12.2/usb1/1-2/1-2:1.0/ep_81'
[ 427.916658] Modules linked in: binfmt_misc kvm_amd kvm powernow_k8 cpufreq_ondemand cpufreq_powersave cpufreq_userspace freq_table cpufreq_conservative ipv6 vfat fat
+8250_pnp 8250 pcspkr ohci_hcd serial_core k10temp edac_core
[ 427.916694] Pid: 278, comm: khubd Not tainted 2.6.33-rc2-00187-g08d869a-dirty #13
[ 427.916699] Call Trace:
The problem is caused by a mismatch between the USB core's view of the
device state and the USB device and xHCI host's view of the device state.
After the device reset and re-configuration, the device and the xHCI host
think they are using alternate setting 0 of all interfaces. However, the
USB core keeps track of the old state, which may include non-zero
alternate settings. It uses intf->cur_altsetting to keep the endpoint
sysfs files for the old state across the reset.
The bandwidth allocation functions need to know what the xHCI host thinks
the current alternate settings are, so original patch set
intf->cur_altsetting to the alternate setting 0. This caused duplicate
endpoint files to be created.
The solution is to not set intf->cur_altsetting before calling
usb_set_interface() in usb_reset_and_verify_device(). Instead, we add a
new flag to struct usb_interface to tell usb_hcd_alloc_bandwidth() to use
alternate setting 0 as the currently installed alternate setting.
Signed-off-by: Sarah Sharp <sarah.a.sharp@linux.intel.com>
Tested-by: Borislav Petkov <petkovbb@googlemail.com>
Cc: Alan Stern <stern@rowland.harvard.edu>
Signed-off-by: Greg Kroah-Hartman <gregkh@suse.de>
2010-01-06 21:16:51 +03:00
|
|
|
struct usb_interface *iface = usb_ifnum_to_if(udev,
|
|
|
|
cur_alt->desc.bInterfaceNumber);
|
|
|
|
|
2011-08-10 03:31:54 +04:00
|
|
|
if (!iface)
|
|
|
|
return -EINVAL;
|
USB: Fix duplicate sysfs problem after device reset.
Borislav Petkov reports issues with duplicate sysfs endpoint files after a
resume from a hibernate. It turns out that the code to support alternate
settings under xHCI has issues when a device with a non-default alternate
setting is reset during the hibernate:
[ 427.681810] Restarting tasks ...
[ 427.681995] hub 1-0:1.0: state 7 ports 6 chg 0004 evt 0000
[ 427.682019] usb usb3: usb resume
[ 427.682030] ohci_hcd 0000:00:12.0: wakeup root hub
[ 427.682191] hub 1-0:1.0: port 2, status 0501, change 0000, 480 Mb/s
[ 427.682205] usb 1-2: usb wakeup-resume
[ 427.682226] usb 1-2: finish reset-resume
[ 427.682886] done.
[ 427.734658] ehci_hcd 0000:00:12.2: port 2 high speed
[ 427.734663] ehci_hcd 0000:00:12.2: GetStatus port 2 status 001005 POWER sig=se0 PE CONNECT
[ 427.746682] hub 3-0:1.0: hub_reset_resume
[ 427.746693] hub 3-0:1.0: trying to enable port power on non-switchable hub
[ 427.786715] usb 1-2: reset high speed USB device using ehci_hcd and address 2
[ 427.839653] ehci_hcd 0000:00:12.2: port 2 high speed
[ 427.839666] ehci_hcd 0000:00:12.2: GetStatus port 2 status 001005 POWER sig=se0 PE CONNECT
[ 427.847717] ohci_hcd 0000:00:12.0: GetStatus roothub.portstatus [1] = 0x00010100 CSC PPS
[ 427.915497] hub 1-2:1.0: remove_intf_ep_devs: if: ffff88022f9e8800 ->ep_devs_created: 1
[ 427.915774] hub 1-2:1.0: remove_intf_ep_devs: bNumEndpoints: 1
[ 427.915934] hub 1-2:1.0: if: ffff88022f9e8800: endpoint devs removed.
[ 427.916158] hub 1-2:1.0: create_intf_ep_devs: if: ffff88022f9e8800 ->ep_devs_created: 0, ->unregistering: 0
[ 427.916434] hub 1-2:1.0: create_intf_ep_devs: bNumEndpoints: 1
[ 427.916609] ep_81: create, parent hub
[ 427.916632] ------------[ cut here ]------------
[ 427.916644] WARNING: at fs/sysfs/dir.c:477 sysfs_add_one+0x82/0x96()
[ 427.916649] Hardware name: System Product Name
[ 427.916653] sysfs: cannot create duplicate filename '/devices/pci0000:00/0000:00:12.2/usb1/1-2/1-2:1.0/ep_81'
[ 427.916658] Modules linked in: binfmt_misc kvm_amd kvm powernow_k8 cpufreq_ondemand cpufreq_powersave cpufreq_userspace freq_table cpufreq_conservative ipv6 vfat fat
+8250_pnp 8250 pcspkr ohci_hcd serial_core k10temp edac_core
[ 427.916694] Pid: 278, comm: khubd Not tainted 2.6.33-rc2-00187-g08d869a-dirty #13
[ 427.916699] Call Trace:
The problem is caused by a mismatch between the USB core's view of the
device state and the USB device and xHCI host's view of the device state.
After the device reset and re-configuration, the device and the xHCI host
think they are using alternate setting 0 of all interfaces. However, the
USB core keeps track of the old state, which may include non-zero
alternate settings. It uses intf->cur_altsetting to keep the endpoint
sysfs files for the old state across the reset.
The bandwidth allocation functions need to know what the xHCI host thinks
the current alternate settings are, so original patch set
intf->cur_altsetting to the alternate setting 0. This caused duplicate
endpoint files to be created.
The solution is to not set intf->cur_altsetting before calling
usb_set_interface() in usb_reset_and_verify_device(). Instead, we add a
new flag to struct usb_interface to tell usb_hcd_alloc_bandwidth() to use
alternate setting 0 as the currently installed alternate setting.
Signed-off-by: Sarah Sharp <sarah.a.sharp@linux.intel.com>
Tested-by: Borislav Petkov <petkovbb@googlemail.com>
Cc: Alan Stern <stern@rowland.harvard.edu>
Signed-off-by: Greg Kroah-Hartman <gregkh@suse.de>
2010-01-06 21:16:51 +03:00
|
|
|
if (iface->resetting_device) {
|
|
|
|
/*
|
|
|
|
* The USB core just reset the device, so the xHCI host
|
|
|
|
* and the device will think alt setting 0 is installed.
|
|
|
|
* However, the USB core will pass in the alternate
|
|
|
|
* setting installed before the reset as cur_alt. Dig
|
|
|
|
* out the alternate setting 0 structure, or the first
|
|
|
|
* alternate setting if a broken device doesn't have alt
|
|
|
|
* setting 0.
|
|
|
|
*/
|
|
|
|
cur_alt = usb_altnum_to_altsetting(iface, 0);
|
|
|
|
if (!cur_alt)
|
|
|
|
cur_alt = &iface->altsetting[0];
|
|
|
|
}
|
|
|
|
|
USB: Check bandwidth when switching alt settings.
Make the USB core check the bandwidth when switching from one
interface alternate setting to another. Also check the bandwidth
when resetting a configuration (so that alt setting 0 is used). If
this check fails, the device's state is unchanged. If the device
refuses the new alt setting, re-instate the old alt setting in the
host controller hardware.
If a USB device doesn't have an alternate interface setting 0, install
the first alt setting in its descriptors when a new configuration is
requested, or the device is reset.
Add a mutex per root hub to protect bandwidth operations:
adding/reseting/changing configurations, and changing alternate interface
settings. We want to ensure that the xHCI host controller and the USB
device are set up for the same configurations and alternate settings.
There are two (possibly three) steps to do this:
1. The host controller needs to check that bandwidth is available for a
different setting, by issuing and waiting for a configure endpoint
command.
2. Once that returns successfully, a control message is sent to the
device.
3. If that fails, the host controller must be notified through another
configure endpoint command.
The mutex is used to make these three operations seem atomic, to prevent
another driver from using more bandwidth for a different device while
we're in the middle of these operations.
While we're touching the bandwidth code, rename usb_hcd_check_bandwidth()
to usb_hcd_alloc_bandwidth(). This function does more than just check
that the bandwidth change won't exceed the bus bandwidth; it actually
changes the bandwidth configuration in the xHCI host controller.
Signed-off-by: Sarah Sharp <sarah.a.sharp@linux.intel.com>
Signed-off-by: Greg Kroah-Hartman <gregkh@suse.de>
2009-12-03 20:44:36 +03:00
|
|
|
/* 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;
|
|
|
|
}
|
|
|
|
}
|
USB: Support for bandwidth allocation.
Originally, the USB core had no support for allocating bandwidth when a
particular configuration or alternate setting for an interface was
selected. Instead, the device driver's URB submission would fail if
there was not enough bandwidth for a periodic endpoint. Drivers could
work around this, by using the scatter-gather list API to guarantee
bandwidth.
This patch adds host controller API to allow the USB core to allocate or
deallocate bandwidth for an endpoint. Endpoints are added to or dropped
from a copy of the current schedule by calling add_endpoint() or
drop_endpoint(), and then the schedule is atomically evaluated with a
call to check_bandwidth(). This allows all the endpoints for a new
configuration or alternate setting to be added at the same time that the
endpoints from the old configuration or alt setting are dropped.
Endpoints must be added to the schedule before any URBs are submitted to
them. The HCD must be allowed to reject a new configuration or alt
setting before the control transfer is sent to the device requesting the
change. It may reject the change because there is not enough bandwidth,
not enough internal resources (such as memory on an embedded host
controller), or perhaps even for security reasons in a virtualized
environment.
If the call to check_bandwidth() fails, the USB core must call
reset_bandwidth(). This causes the schedule to be reverted back to the
state it was in just after the last successful check_bandwidth() call.
If the call succeeds, the host controller driver (and hardware) will have
changed its internal state to match the new configuration or alternate
setting. The USB core can then issue a control transfer to the device to
change the configuration or alt setting. This allows the core to test new
configurations or alternate settings before unbinding drivers bound to
interfaces in the old configuration.
WIP:
The USB core must add endpoints from all interfaces in a configuration
to the schedule, because a driver may claim that interface at any time.
A slight optimization might be to add the endpoints to the schedule once
a driver claims that interface. FIXME
This patch does not cover changing alternate settings, but it does
handle a configuration change or de-configuration. FIXME
The code for managing the schedule is currently HCD specific. A generic
scheduling algorithm could be added for host controllers without
built-in scheduling support. For now, if a host controller does not
define the check_bandwidth() function, the call to
usb_hcd_check_bandwidth() will always succeed.
Signed-off-by: Sarah Sharp <sarah.a.sharp@linux.intel.com>
Signed-off-by: Greg Kroah-Hartman <gregkh@suse.de>
2009-04-28 06:58:26 +04:00
|
|
|
ret = hcd->driver->check_bandwidth(hcd, udev);
|
|
|
|
reset:
|
|
|
|
if (ret < 0)
|
|
|
|
hcd->driver->reset_bandwidth(hcd, udev);
|
|
|
|
return ret;
|
|
|
|
}
|
|
|
|
|
2007-09-10 19:33:05 +04:00
|
|
|
/* Disables the endpoint: synchronizes with the hcd to make sure all
|
|
|
|
* endpoint state is gone from hardware. usb_hcd_flush_endpoint() must
|
|
|
|
* have been called previously. Use for set_configuration, set_interface,
|
|
|
|
* driver removal, physical disconnect.
|
|
|
|
*
|
|
|
|
* example: a qh stored in ep->hcpriv, holding state related to endpoint
|
|
|
|
* type, maxpacket size, toggle, halt status, and scheduling.
|
|
|
|
*/
|
|
|
|
void usb_hcd_disable_endpoint(struct usb_device *udev,
|
|
|
|
struct usb_host_endpoint *ep)
|
|
|
|
{
|
|
|
|
struct usb_hcd *hcd;
|
|
|
|
|
|
|
|
might_sleep();
|
|
|
|
hcd = bus_to_hcd(udev->bus);
|
|
|
|
if (hcd->driver->endpoint_disable)
|
|
|
|
hcd->driver->endpoint_disable(hcd, ep);
|
|
|
|
}
|
|
|
|
|
2009-04-08 21:36:28 +04:00
|
|
|
/**
|
|
|
|
* usb_hcd_reset_endpoint - reset host endpoint state
|
|
|
|
* @udev: USB device.
|
|
|
|
* @ep: the endpoint to reset.
|
|
|
|
*
|
|
|
|
* Resets any host endpoint state such as the toggle bit, sequence
|
|
|
|
* number and current window.
|
|
|
|
*/
|
|
|
|
void usb_hcd_reset_endpoint(struct usb_device *udev,
|
|
|
|
struct usb_host_endpoint *ep)
|
|
|
|
{
|
|
|
|
struct usb_hcd *hcd = bus_to_hcd(udev->bus);
|
|
|
|
|
|
|
|
if (hcd->driver->endpoint_reset)
|
|
|
|
hcd->driver->endpoint_reset(hcd, ep);
|
|
|
|
else {
|
|
|
|
int epnum = usb_endpoint_num(&ep->desc);
|
|
|
|
int is_out = usb_endpoint_dir_out(&ep->desc);
|
|
|
|
int is_control = usb_endpoint_xfer_control(&ep->desc);
|
|
|
|
|
|
|
|
usb_settoggle(udev, epnum, is_out, 0);
|
|
|
|
if (is_control)
|
|
|
|
usb_settoggle(udev, epnum, !is_out, 0);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2010-04-05 21:55:58 +04:00
|
|
|
/**
|
|
|
|
* usb_alloc_streams - allocate bulk endpoint stream IDs.
|
|
|
|
* @interface: alternate setting that includes all endpoints.
|
|
|
|
* @eps: array of endpoints that need streams.
|
|
|
|
* @num_eps: number of endpoints in the array.
|
|
|
|
* @num_streams: number of streams to allocate.
|
|
|
|
* @mem_flags: flags hcd should use to allocate memory.
|
|
|
|
*
|
2013-08-02 22:10:04 +04:00
|
|
|
* Sets up a group of bulk endpoints to have @num_streams stream IDs available.
|
2010-04-05 21:55:58 +04:00
|
|
|
* Drivers may queue multiple transfers to different stream IDs, which may
|
|
|
|
* complete in a different order than they were queued.
|
2013-08-02 22:10:04 +04:00
|
|
|
*
|
|
|
|
* Return: On success, the number of allocated streams. On failure, a negative
|
|
|
|
* error code.
|
2010-04-05 21:55:58 +04:00
|
|
|
*/
|
|
|
|
int usb_alloc_streams(struct usb_interface *interface,
|
|
|
|
struct usb_host_endpoint **eps, unsigned int num_eps,
|
|
|
|
unsigned int num_streams, gfp_t mem_flags)
|
|
|
|
{
|
|
|
|
struct usb_hcd *hcd;
|
|
|
|
struct usb_device *dev;
|
2013-10-09 19:19:25 +04:00
|
|
|
int i, ret;
|
2010-04-05 21:55:58 +04:00
|
|
|
|
|
|
|
dev = interface_to_usbdev(interface);
|
|
|
|
hcd = bus_to_hcd(dev->bus);
|
|
|
|
if (!hcd->driver->alloc_streams || !hcd->driver->free_streams)
|
|
|
|
return -EINVAL;
|
2015-12-10 10:59:25 +03:00
|
|
|
if (dev->speed < USB_SPEED_SUPER)
|
2010-04-05 21:55:58 +04:00
|
|
|
return -EINVAL;
|
2014-10-01 13:29:14 +04:00
|
|
|
if (dev->state < USB_STATE_CONFIGURED)
|
|
|
|
return -ENODEV;
|
2010-04-05 21:55:58 +04:00
|
|
|
|
2013-10-09 19:19:25 +04:00
|
|
|
for (i = 0; i < num_eps; i++) {
|
|
|
|
/* Streams only apply to bulk endpoints. */
|
2010-04-05 21:55:58 +04:00
|
|
|
if (!usb_endpoint_xfer_bulk(&eps[i]->desc))
|
|
|
|
return -EINVAL;
|
2013-10-09 19:19:25 +04:00
|
|
|
/* Re-alloc is not allowed */
|
|
|
|
if (eps[i]->streams)
|
|
|
|
return -EINVAL;
|
|
|
|
}
|
2010-04-05 21:55:58 +04:00
|
|
|
|
2013-10-09 19:19:25 +04:00
|
|
|
ret = hcd->driver->alloc_streams(hcd, dev, eps, num_eps,
|
2010-04-05 21:55:58 +04:00
|
|
|
num_streams, mem_flags);
|
2013-10-09 19:19:25 +04:00
|
|
|
if (ret < 0)
|
|
|
|
return ret;
|
|
|
|
|
|
|
|
for (i = 0; i < num_eps; i++)
|
|
|
|
eps[i]->streams = ret;
|
|
|
|
|
|
|
|
return ret;
|
2010-04-05 21:55:58 +04:00
|
|
|
}
|
|
|
|
EXPORT_SYMBOL_GPL(usb_alloc_streams);
|
|
|
|
|
|
|
|
/**
|
|
|
|
* usb_free_streams - free bulk endpoint stream IDs.
|
|
|
|
* @interface: alternate setting that includes all endpoints.
|
|
|
|
* @eps: array of endpoints to remove streams from.
|
|
|
|
* @num_eps: number of endpoints in the array.
|
|
|
|
* @mem_flags: flags hcd should use to allocate memory.
|
|
|
|
*
|
|
|
|
* Reverts a group of bulk endpoints back to not using stream IDs.
|
|
|
|
* Can fail if we are given bad arguments, or HCD is broken.
|
2013-08-30 16:03:59 +04:00
|
|
|
*
|
2013-10-09 19:19:23 +04:00
|
|
|
* Return: 0 on success. On failure, a negative error code.
|
2010-04-05 21:55:58 +04:00
|
|
|
*/
|
2013-08-30 16:03:59 +04:00
|
|
|
int usb_free_streams(struct usb_interface *interface,
|
2010-04-05 21:55:58 +04:00
|
|
|
struct usb_host_endpoint **eps, unsigned int num_eps,
|
|
|
|
gfp_t mem_flags)
|
|
|
|
{
|
|
|
|
struct usb_hcd *hcd;
|
|
|
|
struct usb_device *dev;
|
2013-10-09 19:19:25 +04:00
|
|
|
int i, ret;
|
2010-04-05 21:55:58 +04:00
|
|
|
|
|
|
|
dev = interface_to_usbdev(interface);
|
|
|
|
hcd = bus_to_hcd(dev->bus);
|
2015-12-10 10:59:25 +03:00
|
|
|
if (dev->speed < USB_SPEED_SUPER)
|
2013-08-30 16:03:59 +04:00
|
|
|
return -EINVAL;
|
2010-04-05 21:55:58 +04:00
|
|
|
|
2013-10-09 19:19:25 +04:00
|
|
|
/* Double-free is not allowed */
|
2010-04-05 21:55:58 +04:00
|
|
|
for (i = 0; i < num_eps; i++)
|
2013-10-09 19:19:25 +04:00
|
|
|
if (!eps[i] || !eps[i]->streams)
|
2013-08-30 16:03:59 +04:00
|
|
|
return -EINVAL;
|
2010-04-05 21:55:58 +04:00
|
|
|
|
2013-10-09 19:19:25 +04:00
|
|
|
ret = hcd->driver->free_streams(hcd, dev, eps, num_eps, mem_flags);
|
|
|
|
if (ret < 0)
|
|
|
|
return ret;
|
|
|
|
|
|
|
|
for (i = 0; i < num_eps; i++)
|
|
|
|
eps[i]->streams = 0;
|
|
|
|
|
|
|
|
return ret;
|
2010-04-05 21:55:58 +04:00
|
|
|
}
|
|
|
|
EXPORT_SYMBOL_GPL(usb_free_streams);
|
|
|
|
|
2008-10-21 23:28:46 +04:00
|
|
|
/* Protect against drivers that try to unlink URBs after the device
|
|
|
|
* is gone, by waiting until all unlinks for @udev are finished.
|
|
|
|
* Since we don't currently track URBs by device, simply wait until
|
|
|
|
* nothing is running in the locked region of usb_hcd_unlink_urb().
|
|
|
|
*/
|
|
|
|
void usb_hcd_synchronize_unlinks(struct usb_device *udev)
|
|
|
|
{
|
|
|
|
spin_lock_irq(&hcd_urb_unlink_lock);
|
|
|
|
spin_unlock_irq(&hcd_urb_unlink_lock);
|
|
|
|
}
|
|
|
|
|
2005-04-17 02:20:36 +04:00
|
|
|
/*-------------------------------------------------------------------------*/
|
|
|
|
|
2007-07-18 20:08:02 +04:00
|
|
|
/* called in any context */
|
|
|
|
int usb_hcd_get_frame_number (struct usb_device *udev)
|
|
|
|
{
|
|
|
|
struct usb_hcd *hcd = bus_to_hcd(udev->bus);
|
|
|
|
|
2011-03-07 19:11:52 +03:00
|
|
|
if (!HCD_RH_RUNNING(hcd))
|
2007-07-18 20:08:02 +04:00
|
|
|
return -ESHUTDOWN;
|
|
|
|
return hcd->driver->get_frame_number (hcd);
|
|
|
|
}
|
|
|
|
|
|
|
|
/*-------------------------------------------------------------------------*/
|
|
|
|
|
2005-09-23 09:32:11 +04:00
|
|
|
#ifdef CONFIG_PM
|
2005-04-17 02:20:36 +04:00
|
|
|
|
2008-11-26 00:39:18 +03:00
|
|
|
int hcd_bus_suspend(struct usb_device *rhdev, pm_message_t msg)
|
2005-04-17 02:20:36 +04:00
|
|
|
{
|
2015-12-23 16:26:51 +03:00
|
|
|
struct usb_hcd *hcd = bus_to_hcd(rhdev->bus);
|
2007-05-30 23:34:36 +04:00
|
|
|
int status;
|
|
|
|
int old_state = hcd->state;
|
2005-04-17 02:20:36 +04:00
|
|
|
|
2011-09-27 23:54:22 +04:00
|
|
|
dev_dbg(&rhdev->dev, "bus %ssuspend, wakeup %d\n",
|
|
|
|
(PMSG_IS_AUTO(msg) ? "auto-" : ""),
|
|
|
|
rhdev->do_remote_wakeup);
|
2011-03-07 19:11:52 +03:00
|
|
|
if (HCD_DEAD(hcd)) {
|
|
|
|
dev_dbg(&rhdev->dev, "skipped %s of dead bus\n", "suspend");
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
2007-05-30 23:34:36 +04:00
|
|
|
if (!hcd->driver->bus_suspend) {
|
|
|
|
status = -ENOENT;
|
|
|
|
} else {
|
2011-03-07 19:11:52 +03:00
|
|
|
clear_bit(HCD_FLAG_RH_RUNNING, &hcd->flags);
|
2007-05-30 23:34:36 +04:00
|
|
|
hcd->state = HC_STATE_QUIESCING;
|
|
|
|
status = hcd->driver->bus_suspend(hcd);
|
|
|
|
}
|
|
|
|
if (status == 0) {
|
|
|
|
usb_set_device_state(rhdev, USB_STATE_SUSPENDED);
|
2005-09-23 09:32:11 +04:00
|
|
|
hcd->state = HC_STATE_SUSPENDED;
|
USB: fix race between root-hub suspend and remote wakeup
This patch (as1533) fixes a race between root-hub suspend and remote
wakeup. If a wakeup event occurs while a root hub is suspending, it
might not cause the suspend to fail. Although the host controller
drivers check for pending wakeup events at the start of their
bus_suspend routines, they generally do not check for wakeup events
while the routines are running.
In addition, if a wakeup event occurs any time after khubd is frozen
and before the root hub is fully suspended, it might not cause a
system sleep transition to fail. For example, the host controller
drivers do not fail root-hub suspends when a connect-change event is
pending.
To fix both these issues, this patch causes hcd_bus_suspend() to query
the controller driver's hub_status_data method after a root hub is
suspended, if the root hub is enabled for wakeup. Any pending status
changes will count as wakeup events, causing the root hub to be
resumed and the overall suspend to fail with -EBUSY.
A significant point is that not all events are reflected immediately
in the status bits. Both EHCI and UHCI controllers notify the CPU
when remote wakeup begins on a port, but the port's suspend-change
status bit doesn't get set until after the port has completed the
transition out of the suspend state, some 25 milliseconds later.
Consequently, the patch will interpret any nonzero return value from
hub_status_data as indicating a pending event, even if none of the
status bits are set in the data buffer. Follow-up patches make the
necessary changes to ehci-hcd and uhci-hcd.
Signed-off-by: Alan Stern <stern@rowland.harvard.edu>
CC: Sarah Sharp <sarah.a.sharp@linux.intel.com>
CC: Chen Peter-B29397 <B29397@freescale.com>
Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
2012-04-03 23:24:18 +04:00
|
|
|
|
|
|
|
/* Did we race with a root-hub wakeup event? */
|
|
|
|
if (rhdev->do_remote_wakeup) {
|
|
|
|
char buffer[6];
|
|
|
|
|
|
|
|
status = hcd->driver->hub_status_data(hcd, buffer);
|
|
|
|
if (status != 0) {
|
|
|
|
dev_dbg(&rhdev->dev, "suspend raced with wakeup event\n");
|
|
|
|
hcd_bus_resume(rhdev, PMSG_AUTO_RESUME);
|
|
|
|
status = -EBUSY;
|
|
|
|
}
|
|
|
|
}
|
2007-05-30 23:34:36 +04:00
|
|
|
} else {
|
2011-03-07 19:11:52 +03:00
|
|
|
spin_lock_irq(&hcd_root_hub_lock);
|
|
|
|
if (!HCD_DEAD(hcd)) {
|
|
|
|
set_bit(HCD_FLAG_RH_RUNNING, &hcd->flags);
|
|
|
|
hcd->state = old_state;
|
|
|
|
}
|
|
|
|
spin_unlock_irq(&hcd_root_hub_lock);
|
2007-05-30 23:34:36 +04:00
|
|
|
dev_dbg(&rhdev->dev, "bus %s fail, err %d\n",
|
2005-09-23 09:32:11 +04:00
|
|
|
"suspend", status);
|
2007-05-30 23:34:36 +04:00
|
|
|
}
|
2005-09-23 09:32:11 +04:00
|
|
|
return status;
|
2005-04-17 02:20:36 +04:00
|
|
|
}
|
|
|
|
|
2008-11-26 00:39:18 +03:00
|
|
|
int hcd_bus_resume(struct usb_device *rhdev, pm_message_t msg)
|
2005-04-17 02:20:36 +04:00
|
|
|
{
|
2015-12-23 16:26:51 +03:00
|
|
|
struct usb_hcd *hcd = bus_to_hcd(rhdev->bus);
|
2007-05-30 23:34:36 +04:00
|
|
|
int status;
|
2007-06-22 00:25:35 +04:00
|
|
|
int old_state = hcd->state;
|
2005-04-17 02:20:36 +04:00
|
|
|
|
2011-09-27 23:54:22 +04:00
|
|
|
dev_dbg(&rhdev->dev, "usb %sresume\n",
|
|
|
|
(PMSG_IS_AUTO(msg) ? "auto-" : ""));
|
2011-03-07 19:11:52 +03:00
|
|
|
if (HCD_DEAD(hcd)) {
|
|
|
|
dev_dbg(&rhdev->dev, "skipped %s of dead bus\n", "resume");
|
|
|
|
return 0;
|
|
|
|
}
|
2005-10-14 01:08:02 +04:00
|
|
|
if (!hcd->driver->bus_resume)
|
2005-09-23 09:32:11 +04:00
|
|
|
return -ENOENT;
|
2011-03-07 19:11:52 +03:00
|
|
|
if (HCD_RH_RUNNING(hcd))
|
2005-09-23 09:32:24 +04:00
|
|
|
return 0;
|
2007-05-30 23:34:36 +04:00
|
|
|
|
2005-09-23 09:32:11 +04:00
|
|
|
hcd->state = HC_STATE_RESUMING;
|
2007-05-30 23:34:36 +04:00
|
|
|
status = hcd->driver->bus_resume(hcd);
|
2011-02-02 21:59:33 +03:00
|
|
|
clear_bit(HCD_FLAG_WAKEUP_PENDING, &hcd->flags);
|
2007-05-30 23:34:36 +04:00
|
|
|
if (status == 0) {
|
2012-10-19 19:03:39 +04:00
|
|
|
struct usb_device *udev;
|
|
|
|
int port1;
|
|
|
|
|
2011-03-07 19:11:52 +03:00
|
|
|
spin_lock_irq(&hcd_root_hub_lock);
|
|
|
|
if (!HCD_DEAD(hcd)) {
|
|
|
|
usb_set_device_state(rhdev, rhdev->actconfig
|
|
|
|
? USB_STATE_CONFIGURED
|
|
|
|
: USB_STATE_ADDRESS);
|
|
|
|
set_bit(HCD_FLAG_RH_RUNNING, &hcd->flags);
|
|
|
|
hcd->state = HC_STATE_RUNNING;
|
|
|
|
}
|
|
|
|
spin_unlock_irq(&hcd_root_hub_lock);
|
2012-10-19 19:03:39 +04:00
|
|
|
|
|
|
|
/*
|
|
|
|
* Check whether any of the enabled ports on the root hub are
|
|
|
|
* unsuspended. If they are then a TRSMRCY delay is needed
|
|
|
|
* (this is what the USB-2 spec calls a "global resume").
|
|
|
|
* Otherwise we can skip the delay.
|
|
|
|
*/
|
|
|
|
usb_hub_for_each_child(rhdev, port1, udev) {
|
|
|
|
if (udev->state != USB_STATE_NOTATTACHED &&
|
|
|
|
!udev->port_is_suspended) {
|
|
|
|
usleep_range(10000, 11000); /* TRSMRCY */
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
2007-05-30 23:34:36 +04:00
|
|
|
} else {
|
2007-06-22 00:25:35 +04:00
|
|
|
hcd->state = old_state;
|
2007-05-30 23:34:36 +04:00
|
|
|
dev_dbg(&rhdev->dev, "bus %s fail, err %d\n",
|
2005-09-23 09:32:11 +04:00
|
|
|
"resume", status);
|
2007-06-22 00:25:35 +04:00
|
|
|
if (status != -ESHUTDOWN)
|
|
|
|
usb_hc_died(hcd);
|
2005-09-23 09:32:11 +04:00
|
|
|
}
|
|
|
|
return status;
|
2005-04-17 02:20:36 +04:00
|
|
|
}
|
|
|
|
|
2007-03-13 23:37:30 +03:00
|
|
|
/* Workqueue routine for root-hub remote wakeup */
|
|
|
|
static void hcd_resume_work(struct work_struct *work)
|
|
|
|
{
|
|
|
|
struct usb_hcd *hcd = container_of(work, struct usb_hcd, wakeup_work);
|
|
|
|
struct usb_device *udev = hcd->self.root_hub;
|
|
|
|
|
2010-01-08 20:56:30 +03:00
|
|
|
usb_remote_wakeup(udev);
|
2007-03-13 23:37:30 +03:00
|
|
|
}
|
|
|
|
|
2005-04-17 02:20:36 +04:00
|
|
|
/**
|
2013-10-05 20:02:07 +04:00
|
|
|
* usb_hcd_resume_root_hub - called by HCD to resume its root hub
|
2005-04-17 02:20:36 +04:00
|
|
|
* @hcd: host controller for this root hub
|
|
|
|
*
|
|
|
|
* The USB host controller calls this function when its root hub is
|
|
|
|
* suspended (with the remote wakeup feature enabled) and a remote
|
2007-03-13 23:37:30 +03:00
|
|
|
* wakeup request is received. The routine submits a workqueue request
|
|
|
|
* to resume the root hub (that is, manage its downstream ports again).
|
2005-04-17 02:20:36 +04:00
|
|
|
*/
|
|
|
|
void usb_hcd_resume_root_hub (struct usb_hcd *hcd)
|
|
|
|
{
|
|
|
|
unsigned long flags;
|
|
|
|
|
|
|
|
spin_lock_irqsave (&hcd_root_hub_lock, flags);
|
2010-06-25 22:02:35 +04:00
|
|
|
if (hcd->rh_registered) {
|
|
|
|
set_bit(HCD_FLAG_WAKEUP_PENDING, &hcd->flags);
|
2010-01-08 20:57:28 +03:00
|
|
|
queue_work(pm_wq, &hcd->wakeup_work);
|
2010-06-25 22:02:35 +04:00
|
|
|
}
|
2005-04-17 02:20:36 +04:00
|
|
|
spin_unlock_irqrestore (&hcd_root_hub_lock, flags);
|
|
|
|
}
|
2005-09-23 09:32:11 +04:00
|
|
|
EXPORT_SYMBOL_GPL(usb_hcd_resume_root_hub);
|
2005-04-17 02:20:36 +04:00
|
|
|
|
2014-11-30 01:47:05 +03:00
|
|
|
#endif /* CONFIG_PM */
|
2005-04-17 02:20:36 +04:00
|
|
|
|
|
|
|
/*-------------------------------------------------------------------------*/
|
|
|
|
|
|
|
|
#ifdef CONFIG_USB_OTG
|
|
|
|
|
|
|
|
/**
|
|
|
|
* usb_bus_start_enum - start immediate enumeration (for OTG)
|
|
|
|
* @bus: the bus (must use hcd framework)
|
|
|
|
* @port_num: 1-based number of port; usually bus->otg_port
|
|
|
|
* Context: in_interrupt()
|
|
|
|
*
|
|
|
|
* Starts enumeration, with an immediate reset followed later by
|
2014-09-19 19:32:23 +04:00
|
|
|
* hub_wq identifying and possibly configuring the device.
|
2005-04-17 02:20:36 +04:00
|
|
|
* This is needed by OTG controller drivers, where it helps meet
|
|
|
|
* HNP protocol timing requirements for starting a port reset.
|
2013-08-02 22:10:04 +04:00
|
|
|
*
|
|
|
|
* Return: 0 if successful.
|
2005-04-17 02:20:36 +04:00
|
|
|
*/
|
|
|
|
int usb_bus_start_enum(struct usb_bus *bus, unsigned port_num)
|
|
|
|
{
|
|
|
|
struct usb_hcd *hcd;
|
|
|
|
int status = -EOPNOTSUPP;
|
|
|
|
|
|
|
|
/* NOTE: since HNP can't start by grabbing the bus's address0_sem,
|
|
|
|
* boards with root hubs hooked up to internal devices (instead of
|
|
|
|
* just the OTG port) may need more attention to resetting...
|
|
|
|
*/
|
2015-12-23 16:26:51 +03:00
|
|
|
hcd = bus_to_hcd(bus);
|
2005-04-17 02:20:36 +04:00
|
|
|
if (port_num && hcd->driver->start_port_reset)
|
|
|
|
status = hcd->driver->start_port_reset(hcd, port_num);
|
|
|
|
|
2014-09-19 19:32:23 +04:00
|
|
|
/* allocate hub_wq shortly after (first) root port reset finishes;
|
2005-04-17 02:20:36 +04:00
|
|
|
* it may issue others, until at least 50 msecs have passed.
|
|
|
|
*/
|
|
|
|
if (status == 0)
|
|
|
|
mod_timer(&hcd->rh_timer, jiffies + msecs_to_jiffies(10));
|
|
|
|
return status;
|
|
|
|
}
|
2008-01-25 20:12:21 +03:00
|
|
|
EXPORT_SYMBOL_GPL(usb_bus_start_enum);
|
2005-04-17 02:20:36 +04:00
|
|
|
|
|
|
|
#endif
|
|
|
|
|
|
|
|
/*-------------------------------------------------------------------------*/
|
|
|
|
|
|
|
|
/**
|
|
|
|
* usb_hcd_irq - hook IRQs to HCD framework (bus glue)
|
|
|
|
* @irq: the IRQ being raised
|
|
|
|
* @__hcd: pointer to the HCD whose IRQ is being signaled
|
|
|
|
*
|
|
|
|
* If the controller isn't HALTed, calls the driver's irq handler.
|
|
|
|
* Checks whether the controller is now dead.
|
2013-08-02 22:10:04 +04:00
|
|
|
*
|
|
|
|
* Return: %IRQ_HANDLED if the IRQ was handled. %IRQ_NONE otherwise.
|
2005-04-17 02:20:36 +04:00
|
|
|
*/
|
IRQ: Maintain regs pointer globally rather than passing to IRQ handlers
Maintain a per-CPU global "struct pt_regs *" variable which can be used instead
of passing regs around manually through all ~1800 interrupt handlers in the
Linux kernel.
The regs pointer is used in few places, but it potentially costs both stack
space and code to pass it around. On the FRV arch, removing the regs parameter
from all the genirq function results in a 20% speed up of the IRQ exit path
(ie: from leaving timer_interrupt() to leaving do_IRQ()).
Where appropriate, an arch may override the generic storage facility and do
something different with the variable. On FRV, for instance, the address is
maintained in GR28 at all times inside the kernel as part of general exception
handling.
Having looked over the code, it appears that the parameter may be handed down
through up to twenty or so layers of functions. Consider a USB character
device attached to a USB hub, attached to a USB controller that posts its
interrupts through a cascaded auxiliary interrupt controller. A character
device driver may want to pass regs to the sysrq handler through the input
layer which adds another few layers of parameter passing.
I've build this code with allyesconfig for x86_64 and i386. I've runtested the
main part of the code on FRV and i386, though I can't test most of the drivers.
I've also done partial conversion for powerpc and MIPS - these at least compile
with minimal configurations.
This will affect all archs. Mostly the changes should be relatively easy.
Take do_IRQ(), store the regs pointer at the beginning, saving the old one:
struct pt_regs *old_regs = set_irq_regs(regs);
And put the old one back at the end:
set_irq_regs(old_regs);
Don't pass regs through to generic_handle_irq() or __do_IRQ().
In timer_interrupt(), this sort of change will be necessary:
- update_process_times(user_mode(regs));
- profile_tick(CPU_PROFILING, regs);
+ update_process_times(user_mode(get_irq_regs()));
+ profile_tick(CPU_PROFILING);
I'd like to move update_process_times()'s use of get_irq_regs() into itself,
except that i386, alone of the archs, uses something other than user_mode().
Some notes on the interrupt handling in the drivers:
(*) input_dev() is now gone entirely. The regs pointer is no longer stored in
the input_dev struct.
(*) finish_unlinks() in drivers/usb/host/ohci-q.c needs checking. It does
something different depending on whether it's been supplied with a regs
pointer or not.
(*) Various IRQ handler function pointers have been moved to type
irq_handler_t.
Signed-Off-By: David Howells <dhowells@redhat.com>
(cherry picked from 1b16e7ac850969f38b375e511e3fa2f474a33867 commit)
2006-10-05 17:55:46 +04:00
|
|
|
irqreturn_t usb_hcd_irq (int irq, void *__hcd)
|
2005-04-17 02:20:36 +04:00
|
|
|
{
|
|
|
|
struct usb_hcd *hcd = __hcd;
|
2008-07-01 20:19:22 +04:00
|
|
|
irqreturn_t rc;
|
2005-04-17 02:20:36 +04:00
|
|
|
|
2011-11-03 20:03:38 +04:00
|
|
|
if (unlikely(HCD_DEAD(hcd) || !HCD_HW_ACCESSIBLE(hcd)))
|
2008-07-01 20:19:22 +04:00
|
|
|
rc = IRQ_NONE;
|
2011-11-03 20:03:38 +04:00
|
|
|
else if (hcd->driver->irq(hcd) == IRQ_NONE)
|
2008-07-01 20:19:22 +04:00
|
|
|
rc = IRQ_NONE;
|
2011-11-03 20:03:38 +04:00
|
|
|
else
|
2008-07-01 20:19:22 +04:00
|
|
|
rc = IRQ_HANDLED;
|
|
|
|
|
|
|
|
return rc;
|
2005-04-17 02:20:36 +04:00
|
|
|
}
|
2010-07-22 03:56:08 +04:00
|
|
|
EXPORT_SYMBOL_GPL(usb_hcd_irq);
|
2005-04-17 02:20:36 +04:00
|
|
|
|
|
|
|
/*-------------------------------------------------------------------------*/
|
|
|
|
|
|
|
|
/**
|
|
|
|
* usb_hc_died - report abnormal shutdown of a host controller (bus glue)
|
|
|
|
* @hcd: pointer to the HCD representing the controller
|
|
|
|
*
|
|
|
|
* This is called by bus glue to report a USB host controller that died
|
|
|
|
* while operations may still have been pending. It's called automatically
|
usb: Make core allocate resources per PCI-device.
Introduce the notion of a PCI device that may be associated with more than
one USB host controller driver (struct usb_hcd). This patch is the start
of the work to separate the xHCI host controller into two roothubs: a USB
3.0 roothub with SuperSpeed-only ports, and a USB 2.0 roothub with
HS/FS/LS ports.
One usb_hcd structure is designated to be the "primary HCD", and a pointer
is added to the usb_hcd structure to keep track of that. A new function
call, usb_hcd_is_primary_hcd() is added to check whether the USB hcd is
marked as the primary HCD (or if it is not part of a roothub pair). To
allow the USB core and xHCI driver to access either roothub in a pair, a
"shared_hcd" pointer is added to the usb_hcd structure.
Add a new function, usb_create_shared_hcd(), that does roothub allocation
for paired roothubs. It will act just like usb_create_hcd() did if the
primary_hcd pointer argument is NULL. If it is passed a non-NULL
primary_hcd pointer, it sets usb_hcd->shared_hcd and usb_hcd->primary_hcd
fields. It will also skip the bandwidth_mutex allocation, and set the
secondary hcd's bandwidth_mutex pointer to the primary HCD's mutex.
IRQs are only allocated once for the primary roothub.
Introduce a new usb_hcd driver flag that indicates the host controller
driver wants to create two roothubs. If the HCD_SHARED flag is set, then
the USB core PCI probe methods will allocate a second roothub, and make
sure that second roothub gets freed during rmmod and in initialization
error paths.
When usb_hc_died() is called with the primary HCD, make sure that any
roothubs that share that host controller are also marked as being dead.
Signed-off-by: Sarah Sharp <sarah.a.sharp@linux.intel.com>
2010-10-29 02:40:26 +04:00
|
|
|
* by the PCI glue, so only glue for non-PCI busses should need to call it.
|
|
|
|
*
|
|
|
|
* Only call this function with the primary HCD.
|
2005-04-17 02:20:36 +04:00
|
|
|
*/
|
|
|
|
void usb_hc_died (struct usb_hcd *hcd)
|
|
|
|
{
|
|
|
|
unsigned long flags;
|
|
|
|
|
|
|
|
dev_err (hcd->self.controller, "HC died; cleaning up\n");
|
|
|
|
|
|
|
|
spin_lock_irqsave (&hcd_root_hub_lock, flags);
|
2011-03-07 19:11:52 +03:00
|
|
|
clear_bit(HCD_FLAG_RH_RUNNING, &hcd->flags);
|
|
|
|
set_bit(HCD_FLAG_DEAD, &hcd->flags);
|
2005-04-17 02:20:36 +04:00
|
|
|
if (hcd->rh_registered) {
|
2010-06-23 00:39:10 +04:00
|
|
|
clear_bit(HCD_FLAG_POLL_RH, &hcd->flags);
|
2005-04-17 02:20:36 +04:00
|
|
|
|
2014-09-19 19:32:23 +04:00
|
|
|
/* make hub_wq clean up old urbs and devices */
|
2005-04-17 02:20:36 +04:00
|
|
|
usb_set_device_state (hcd->self.root_hub,
|
|
|
|
USB_STATE_NOTATTACHED);
|
2014-09-19 19:32:22 +04:00
|
|
|
usb_kick_hub_wq(hcd->self.root_hub);
|
2005-04-17 02:20:36 +04:00
|
|
|
}
|
usb: Make core allocate resources per PCI-device.
Introduce the notion of a PCI device that may be associated with more than
one USB host controller driver (struct usb_hcd). This patch is the start
of the work to separate the xHCI host controller into two roothubs: a USB
3.0 roothub with SuperSpeed-only ports, and a USB 2.0 roothub with
HS/FS/LS ports.
One usb_hcd structure is designated to be the "primary HCD", and a pointer
is added to the usb_hcd structure to keep track of that. A new function
call, usb_hcd_is_primary_hcd() is added to check whether the USB hcd is
marked as the primary HCD (or if it is not part of a roothub pair). To
allow the USB core and xHCI driver to access either roothub in a pair, a
"shared_hcd" pointer is added to the usb_hcd structure.
Add a new function, usb_create_shared_hcd(), that does roothub allocation
for paired roothubs. It will act just like usb_create_hcd() did if the
primary_hcd pointer argument is NULL. If it is passed a non-NULL
primary_hcd pointer, it sets usb_hcd->shared_hcd and usb_hcd->primary_hcd
fields. It will also skip the bandwidth_mutex allocation, and set the
secondary hcd's bandwidth_mutex pointer to the primary HCD's mutex.
IRQs are only allocated once for the primary roothub.
Introduce a new usb_hcd driver flag that indicates the host controller
driver wants to create two roothubs. If the HCD_SHARED flag is set, then
the USB core PCI probe methods will allocate a second roothub, and make
sure that second roothub gets freed during rmmod and in initialization
error paths.
When usb_hc_died() is called with the primary HCD, make sure that any
roothubs that share that host controller are also marked as being dead.
Signed-off-by: Sarah Sharp <sarah.a.sharp@linux.intel.com>
2010-10-29 02:40:26 +04:00
|
|
|
if (usb_hcd_is_primary_hcd(hcd) && hcd->shared_hcd) {
|
|
|
|
hcd = hcd->shared_hcd;
|
|
|
|
if (hcd->rh_registered) {
|
|
|
|
clear_bit(HCD_FLAG_POLL_RH, &hcd->flags);
|
|
|
|
|
2014-09-19 19:32:23 +04:00
|
|
|
/* make hub_wq clean up old urbs and devices */
|
usb: Make core allocate resources per PCI-device.
Introduce the notion of a PCI device that may be associated with more than
one USB host controller driver (struct usb_hcd). This patch is the start
of the work to separate the xHCI host controller into two roothubs: a USB
3.0 roothub with SuperSpeed-only ports, and a USB 2.0 roothub with
HS/FS/LS ports.
One usb_hcd structure is designated to be the "primary HCD", and a pointer
is added to the usb_hcd structure to keep track of that. A new function
call, usb_hcd_is_primary_hcd() is added to check whether the USB hcd is
marked as the primary HCD (or if it is not part of a roothub pair). To
allow the USB core and xHCI driver to access either roothub in a pair, a
"shared_hcd" pointer is added to the usb_hcd structure.
Add a new function, usb_create_shared_hcd(), that does roothub allocation
for paired roothubs. It will act just like usb_create_hcd() did if the
primary_hcd pointer argument is NULL. If it is passed a non-NULL
primary_hcd pointer, it sets usb_hcd->shared_hcd and usb_hcd->primary_hcd
fields. It will also skip the bandwidth_mutex allocation, and set the
secondary hcd's bandwidth_mutex pointer to the primary HCD's mutex.
IRQs are only allocated once for the primary roothub.
Introduce a new usb_hcd driver flag that indicates the host controller
driver wants to create two roothubs. If the HCD_SHARED flag is set, then
the USB core PCI probe methods will allocate a second roothub, and make
sure that second roothub gets freed during rmmod and in initialization
error paths.
When usb_hc_died() is called with the primary HCD, make sure that any
roothubs that share that host controller are also marked as being dead.
Signed-off-by: Sarah Sharp <sarah.a.sharp@linux.intel.com>
2010-10-29 02:40:26 +04:00
|
|
|
usb_set_device_state(hcd->self.root_hub,
|
|
|
|
USB_STATE_NOTATTACHED);
|
2014-09-19 19:32:22 +04:00
|
|
|
usb_kick_hub_wq(hcd->self.root_hub);
|
usb: Make core allocate resources per PCI-device.
Introduce the notion of a PCI device that may be associated with more than
one USB host controller driver (struct usb_hcd). This patch is the start
of the work to separate the xHCI host controller into two roothubs: a USB
3.0 roothub with SuperSpeed-only ports, and a USB 2.0 roothub with
HS/FS/LS ports.
One usb_hcd structure is designated to be the "primary HCD", and a pointer
is added to the usb_hcd structure to keep track of that. A new function
call, usb_hcd_is_primary_hcd() is added to check whether the USB hcd is
marked as the primary HCD (or if it is not part of a roothub pair). To
allow the USB core and xHCI driver to access either roothub in a pair, a
"shared_hcd" pointer is added to the usb_hcd structure.
Add a new function, usb_create_shared_hcd(), that does roothub allocation
for paired roothubs. It will act just like usb_create_hcd() did if the
primary_hcd pointer argument is NULL. If it is passed a non-NULL
primary_hcd pointer, it sets usb_hcd->shared_hcd and usb_hcd->primary_hcd
fields. It will also skip the bandwidth_mutex allocation, and set the
secondary hcd's bandwidth_mutex pointer to the primary HCD's mutex.
IRQs are only allocated once for the primary roothub.
Introduce a new usb_hcd driver flag that indicates the host controller
driver wants to create two roothubs. If the HCD_SHARED flag is set, then
the USB core PCI probe methods will allocate a second roothub, and make
sure that second roothub gets freed during rmmod and in initialization
error paths.
When usb_hc_died() is called with the primary HCD, make sure that any
roothubs that share that host controller are also marked as being dead.
Signed-off-by: Sarah Sharp <sarah.a.sharp@linux.intel.com>
2010-10-29 02:40:26 +04:00
|
|
|
}
|
|
|
|
}
|
2005-04-17 02:20:36 +04:00
|
|
|
spin_unlock_irqrestore (&hcd_root_hub_lock, flags);
|
usb: Make core allocate resources per PCI-device.
Introduce the notion of a PCI device that may be associated with more than
one USB host controller driver (struct usb_hcd). This patch is the start
of the work to separate the xHCI host controller into two roothubs: a USB
3.0 roothub with SuperSpeed-only ports, and a USB 2.0 roothub with
HS/FS/LS ports.
One usb_hcd structure is designated to be the "primary HCD", and a pointer
is added to the usb_hcd structure to keep track of that. A new function
call, usb_hcd_is_primary_hcd() is added to check whether the USB hcd is
marked as the primary HCD (or if it is not part of a roothub pair). To
allow the USB core and xHCI driver to access either roothub in a pair, a
"shared_hcd" pointer is added to the usb_hcd structure.
Add a new function, usb_create_shared_hcd(), that does roothub allocation
for paired roothubs. It will act just like usb_create_hcd() did if the
primary_hcd pointer argument is NULL. If it is passed a non-NULL
primary_hcd pointer, it sets usb_hcd->shared_hcd and usb_hcd->primary_hcd
fields. It will also skip the bandwidth_mutex allocation, and set the
secondary hcd's bandwidth_mutex pointer to the primary HCD's mutex.
IRQs are only allocated once for the primary roothub.
Introduce a new usb_hcd driver flag that indicates the host controller
driver wants to create two roothubs. If the HCD_SHARED flag is set, then
the USB core PCI probe methods will allocate a second roothub, and make
sure that second roothub gets freed during rmmod and in initialization
error paths.
When usb_hc_died() is called with the primary HCD, make sure that any
roothubs that share that host controller are also marked as being dead.
Signed-off-by: Sarah Sharp <sarah.a.sharp@linux.intel.com>
2010-10-29 02:40:26 +04:00
|
|
|
/* Make sure that the other roothub is also deallocated. */
|
2005-04-17 02:20:36 +04:00
|
|
|
}
|
|
|
|
EXPORT_SYMBOL_GPL (usb_hc_died);
|
|
|
|
|
|
|
|
/*-------------------------------------------------------------------------*/
|
|
|
|
|
USB: HCD: support giveback of URB in tasklet context
This patch implements the mechanism of giveback of URB in
tasklet context, so that hardware interrupt handling time for
usb host controller can be saved much, and HCD interrupt handling
can be simplified.
Motivations:
1), on some arch(such as ARM), DMA mapping/unmapping is a bit
time-consuming, for example: when accessing usb mass storage
via EHCI on pandaboard, the common length of transfer buffer is 120KB,
the time consumed on DMA unmapping may reach hundreds of microseconds;
even on A15 based box, the time is still about scores of microseconds
2), on some arch, reading DMA coherent memoery is very time-consuming,
the most common example is usb video class driver[1]
3), driver's complete() callback may do much things which is driver
specific, so the time is consumed unnecessarily in hardware irq context.
4), running driver's complete() callback in hardware irq context causes
that host controller driver has to release its lock in interrupt handler,
so reacquiring the lock after return may busy wait a while and increase
interrupt handling time. More seriously, releasing the HCD lock makes
HCD becoming quite complicated to deal with introduced races.
So the patch proposes to run giveback of URB in tasklet context, then
time consumed in HCD irq handling doesn't depend on drivers' complete and
DMA mapping/unmapping any more, also we can simplify HCD since the HCD
lock isn't needed to be released during irq handling.
The patch should be reasonable and doable:
1), for drivers, they don't care if the complete() is called in hard irq
context or softirq context
2), the biggest change is the situation in which usb_submit_urb() is called
in complete() callback, so the introduced tasklet schedule delay might be a
con, but it shouldn't be a big deal:
- control/bulk asynchronous transfer isn't sensitive to schedule
delay
- the patch schedules giveback of periodic URBs using
tasklet_hi_schedule, so the introduced delay should be very
small
- for ISOC transfer, generally, drivers submit several URBs
concurrently to avoid interrupt delay, so it is OK with the
little schedule delay.
- for interrupt transfer, generally, drivers only submit one URB
at the same time, but interrupt transfer is often used in event
report, polling, ... situations, and a little delay should be OK.
Considered that HCDs may optimize on submitting URB in complete(), the
patch may cause the optimization not working, so introduces one flag to mark
if the HCD supports to run giveback URB in tasklet context. When all HCDs
are ready, the flag can be removed.
[1], http://marc.info/?t=136438111600010&r=1&w=2
Cc: Oliver Neukum <oliver@neukum.org>
Acked-by: Alan Stern <stern@rowland.harvard.edu>
Signed-off-by: Ming Lei <ming.lei@canonical.com>
Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
2013-07-03 18:53:07 +04:00
|
|
|
static void init_giveback_urb_bh(struct giveback_urb_bh *bh)
|
|
|
|
{
|
|
|
|
|
|
|
|
spin_lock_init(&bh->lock);
|
|
|
|
INIT_LIST_HEAD(&bh->head);
|
|
|
|
tasklet_init(&bh->bh, usb_giveback_urb_bh, (unsigned long)bh);
|
|
|
|
}
|
|
|
|
|
2017-03-13 05:18:41 +03:00
|
|
|
struct usb_hcd *__usb_create_hcd(const struct hc_driver *driver,
|
|
|
|
struct device *sysdev, struct device *dev, const char *bus_name,
|
usb: Make core allocate resources per PCI-device.
Introduce the notion of a PCI device that may be associated with more than
one USB host controller driver (struct usb_hcd). This patch is the start
of the work to separate the xHCI host controller into two roothubs: a USB
3.0 roothub with SuperSpeed-only ports, and a USB 2.0 roothub with
HS/FS/LS ports.
One usb_hcd structure is designated to be the "primary HCD", and a pointer
is added to the usb_hcd structure to keep track of that. A new function
call, usb_hcd_is_primary_hcd() is added to check whether the USB hcd is
marked as the primary HCD (or if it is not part of a roothub pair). To
allow the USB core and xHCI driver to access either roothub in a pair, a
"shared_hcd" pointer is added to the usb_hcd structure.
Add a new function, usb_create_shared_hcd(), that does roothub allocation
for paired roothubs. It will act just like usb_create_hcd() did if the
primary_hcd pointer argument is NULL. If it is passed a non-NULL
primary_hcd pointer, it sets usb_hcd->shared_hcd and usb_hcd->primary_hcd
fields. It will also skip the bandwidth_mutex allocation, and set the
secondary hcd's bandwidth_mutex pointer to the primary HCD's mutex.
IRQs are only allocated once for the primary roothub.
Introduce a new usb_hcd driver flag that indicates the host controller
driver wants to create two roothubs. If the HCD_SHARED flag is set, then
the USB core PCI probe methods will allocate a second roothub, and make
sure that second roothub gets freed during rmmod and in initialization
error paths.
When usb_hc_died() is called with the primary HCD, make sure that any
roothubs that share that host controller are also marked as being dead.
Signed-off-by: Sarah Sharp <sarah.a.sharp@linux.intel.com>
2010-10-29 02:40:26 +04:00
|
|
|
struct usb_hcd *primary_hcd)
|
2005-04-17 02:20:36 +04:00
|
|
|
{
|
|
|
|
struct usb_hcd *hcd;
|
|
|
|
|
2005-09-07 02:18:34 +04:00
|
|
|
hcd = kzalloc(sizeof(*hcd) + driver->hcd_priv_size, GFP_KERNEL);
|
2016-08-25 20:38:58 +03:00
|
|
|
if (!hcd)
|
2005-04-17 02:20:36 +04:00
|
|
|
return NULL;
|
usb: Make core allocate resources per PCI-device.
Introduce the notion of a PCI device that may be associated with more than
one USB host controller driver (struct usb_hcd). This patch is the start
of the work to separate the xHCI host controller into two roothubs: a USB
3.0 roothub with SuperSpeed-only ports, and a USB 2.0 roothub with
HS/FS/LS ports.
One usb_hcd structure is designated to be the "primary HCD", and a pointer
is added to the usb_hcd structure to keep track of that. A new function
call, usb_hcd_is_primary_hcd() is added to check whether the USB hcd is
marked as the primary HCD (or if it is not part of a roothub pair). To
allow the USB core and xHCI driver to access either roothub in a pair, a
"shared_hcd" pointer is added to the usb_hcd structure.
Add a new function, usb_create_shared_hcd(), that does roothub allocation
for paired roothubs. It will act just like usb_create_hcd() did if the
primary_hcd pointer argument is NULL. If it is passed a non-NULL
primary_hcd pointer, it sets usb_hcd->shared_hcd and usb_hcd->primary_hcd
fields. It will also skip the bandwidth_mutex allocation, and set the
secondary hcd's bandwidth_mutex pointer to the primary HCD's mutex.
IRQs are only allocated once for the primary roothub.
Introduce a new usb_hcd driver flag that indicates the host controller
driver wants to create two roothubs. If the HCD_SHARED flag is set, then
the USB core PCI probe methods will allocate a second roothub, and make
sure that second roothub gets freed during rmmod and in initialization
error paths.
When usb_hc_died() is called with the primary HCD, make sure that any
roothubs that share that host controller are also marked as being dead.
Signed-off-by: Sarah Sharp <sarah.a.sharp@linux.intel.com>
2010-10-29 02:40:26 +04:00
|
|
|
if (primary_hcd == NULL) {
|
2016-04-25 15:48:38 +03:00
|
|
|
hcd->address0_mutex = kmalloc(sizeof(*hcd->address0_mutex),
|
|
|
|
GFP_KERNEL);
|
|
|
|
if (!hcd->address0_mutex) {
|
|
|
|
kfree(hcd);
|
|
|
|
dev_dbg(dev, "hcd address0 mutex alloc failed\n");
|
|
|
|
return NULL;
|
|
|
|
}
|
|
|
|
mutex_init(hcd->address0_mutex);
|
usb: Make core allocate resources per PCI-device.
Introduce the notion of a PCI device that may be associated with more than
one USB host controller driver (struct usb_hcd). This patch is the start
of the work to separate the xHCI host controller into two roothubs: a USB
3.0 roothub with SuperSpeed-only ports, and a USB 2.0 roothub with
HS/FS/LS ports.
One usb_hcd structure is designated to be the "primary HCD", and a pointer
is added to the usb_hcd structure to keep track of that. A new function
call, usb_hcd_is_primary_hcd() is added to check whether the USB hcd is
marked as the primary HCD (or if it is not part of a roothub pair). To
allow the USB core and xHCI driver to access either roothub in a pair, a
"shared_hcd" pointer is added to the usb_hcd structure.
Add a new function, usb_create_shared_hcd(), that does roothub allocation
for paired roothubs. It will act just like usb_create_hcd() did if the
primary_hcd pointer argument is NULL. If it is passed a non-NULL
primary_hcd pointer, it sets usb_hcd->shared_hcd and usb_hcd->primary_hcd
fields. It will also skip the bandwidth_mutex allocation, and set the
secondary hcd's bandwidth_mutex pointer to the primary HCD's mutex.
IRQs are only allocated once for the primary roothub.
Introduce a new usb_hcd driver flag that indicates the host controller
driver wants to create two roothubs. If the HCD_SHARED flag is set, then
the USB core PCI probe methods will allocate a second roothub, and make
sure that second roothub gets freed during rmmod and in initialization
error paths.
When usb_hc_died() is called with the primary HCD, make sure that any
roothubs that share that host controller are also marked as being dead.
Signed-off-by: Sarah Sharp <sarah.a.sharp@linux.intel.com>
2010-10-29 02:40:26 +04:00
|
|
|
hcd->bandwidth_mutex = kmalloc(sizeof(*hcd->bandwidth_mutex),
|
|
|
|
GFP_KERNEL);
|
|
|
|
if (!hcd->bandwidth_mutex) {
|
2017-05-07 02:53:46 +03:00
|
|
|
kfree(hcd->address0_mutex);
|
usb: Make core allocate resources per PCI-device.
Introduce the notion of a PCI device that may be associated with more than
one USB host controller driver (struct usb_hcd). This patch is the start
of the work to separate the xHCI host controller into two roothubs: a USB
3.0 roothub with SuperSpeed-only ports, and a USB 2.0 roothub with
HS/FS/LS ports.
One usb_hcd structure is designated to be the "primary HCD", and a pointer
is added to the usb_hcd structure to keep track of that. A new function
call, usb_hcd_is_primary_hcd() is added to check whether the USB hcd is
marked as the primary HCD (or if it is not part of a roothub pair). To
allow the USB core and xHCI driver to access either roothub in a pair, a
"shared_hcd" pointer is added to the usb_hcd structure.
Add a new function, usb_create_shared_hcd(), that does roothub allocation
for paired roothubs. It will act just like usb_create_hcd() did if the
primary_hcd pointer argument is NULL. If it is passed a non-NULL
primary_hcd pointer, it sets usb_hcd->shared_hcd and usb_hcd->primary_hcd
fields. It will also skip the bandwidth_mutex allocation, and set the
secondary hcd's bandwidth_mutex pointer to the primary HCD's mutex.
IRQs are only allocated once for the primary roothub.
Introduce a new usb_hcd driver flag that indicates the host controller
driver wants to create two roothubs. If the HCD_SHARED flag is set, then
the USB core PCI probe methods will allocate a second roothub, and make
sure that second roothub gets freed during rmmod and in initialization
error paths.
When usb_hc_died() is called with the primary HCD, make sure that any
roothubs that share that host controller are also marked as being dead.
Signed-off-by: Sarah Sharp <sarah.a.sharp@linux.intel.com>
2010-10-29 02:40:26 +04:00
|
|
|
kfree(hcd);
|
|
|
|
dev_dbg(dev, "hcd bandwidth mutex alloc failed\n");
|
|
|
|
return NULL;
|
|
|
|
}
|
|
|
|
mutex_init(hcd->bandwidth_mutex);
|
|
|
|
dev_set_drvdata(dev, hcd);
|
|
|
|
} else {
|
2014-05-21 05:08:28 +04:00
|
|
|
mutex_lock(&usb_port_peer_mutex);
|
2016-04-25 15:48:38 +03:00
|
|
|
hcd->address0_mutex = primary_hcd->address0_mutex;
|
usb: Make core allocate resources per PCI-device.
Introduce the notion of a PCI device that may be associated with more than
one USB host controller driver (struct usb_hcd). This patch is the start
of the work to separate the xHCI host controller into two roothubs: a USB
3.0 roothub with SuperSpeed-only ports, and a USB 2.0 roothub with
HS/FS/LS ports.
One usb_hcd structure is designated to be the "primary HCD", and a pointer
is added to the usb_hcd structure to keep track of that. A new function
call, usb_hcd_is_primary_hcd() is added to check whether the USB hcd is
marked as the primary HCD (or if it is not part of a roothub pair). To
allow the USB core and xHCI driver to access either roothub in a pair, a
"shared_hcd" pointer is added to the usb_hcd structure.
Add a new function, usb_create_shared_hcd(), that does roothub allocation
for paired roothubs. It will act just like usb_create_hcd() did if the
primary_hcd pointer argument is NULL. If it is passed a non-NULL
primary_hcd pointer, it sets usb_hcd->shared_hcd and usb_hcd->primary_hcd
fields. It will also skip the bandwidth_mutex allocation, and set the
secondary hcd's bandwidth_mutex pointer to the primary HCD's mutex.
IRQs are only allocated once for the primary roothub.
Introduce a new usb_hcd driver flag that indicates the host controller
driver wants to create two roothubs. If the HCD_SHARED flag is set, then
the USB core PCI probe methods will allocate a second roothub, and make
sure that second roothub gets freed during rmmod and in initialization
error paths.
When usb_hc_died() is called with the primary HCD, make sure that any
roothubs that share that host controller are also marked as being dead.
Signed-off-by: Sarah Sharp <sarah.a.sharp@linux.intel.com>
2010-10-29 02:40:26 +04:00
|
|
|
hcd->bandwidth_mutex = primary_hcd->bandwidth_mutex;
|
|
|
|
hcd->primary_hcd = primary_hcd;
|
|
|
|
primary_hcd->primary_hcd = primary_hcd;
|
|
|
|
hcd->shared_hcd = primary_hcd;
|
|
|
|
primary_hcd->shared_hcd = hcd;
|
2014-05-21 05:08:28 +04:00
|
|
|
mutex_unlock(&usb_port_peer_mutex);
|
2010-10-15 19:55:24 +04:00
|
|
|
}
|
|
|
|
|
2006-08-30 19:32:52 +04:00
|
|
|
kref_init(&hcd->kref);
|
2005-04-17 02:20:36 +04:00
|
|
|
|
|
|
|
usb_bus_init(&hcd->self);
|
|
|
|
hcd->self.controller = dev;
|
2017-03-13 05:18:41 +03:00
|
|
|
hcd->self.sysdev = sysdev;
|
2005-04-17 02:20:36 +04:00
|
|
|
hcd->self.bus_name = bus_name;
|
2017-03-13 05:18:41 +03:00
|
|
|
hcd->self.uses_dma = (sysdev->dma_mask != NULL);
|
2005-04-17 02:20:36 +04:00
|
|
|
|
|
|
|
init_timer(&hcd->rh_timer);
|
2005-04-21 23:56:37 +04:00
|
|
|
hcd->rh_timer.function = rh_timer_func;
|
|
|
|
hcd->rh_timer.data = (unsigned long) hcd;
|
2014-11-30 01:47:05 +03:00
|
|
|
#ifdef CONFIG_PM
|
2007-03-13 23:37:30 +03:00
|
|
|
INIT_WORK(&hcd->wakeup_work, hcd_resume_work);
|
|
|
|
#endif
|
2005-04-17 02:20:36 +04:00
|
|
|
|
|
|
|
hcd->driver = driver;
|
2010-12-03 01:45:18 +03:00
|
|
|
hcd->speed = driver->flags & HCD_MASK;
|
2005-04-17 02:20:36 +04:00
|
|
|
hcd->product_desc = (driver->product_desc) ? driver->product_desc :
|
|
|
|
"USB Host Controller";
|
|
|
|
return hcd;
|
|
|
|
}
|
2017-03-13 05:18:41 +03:00
|
|
|
EXPORT_SYMBOL_GPL(__usb_create_hcd);
|
|
|
|
|
|
|
|
/**
|
|
|
|
* usb_create_shared_hcd - create and initialize an HCD structure
|
|
|
|
* @driver: HC driver that will use this hcd
|
|
|
|
* @dev: device for this HC, stored in hcd->self.controller
|
|
|
|
* @bus_name: value to store in hcd->self.bus_name
|
|
|
|
* @primary_hcd: a pointer to the usb_hcd structure that is sharing the
|
|
|
|
* PCI device. Only allocate certain resources for the primary HCD
|
|
|
|
* Context: !in_interrupt()
|
|
|
|
*
|
|
|
|
* Allocate a struct usb_hcd, with extra space at the end for the
|
|
|
|
* HC driver's private data. Initialize the generic members of the
|
|
|
|
* hcd structure.
|
|
|
|
*
|
|
|
|
* Return: On success, a pointer to the created and initialized HCD structure.
|
|
|
|
* On failure (e.g. if memory is unavailable), %NULL.
|
|
|
|
*/
|
|
|
|
struct usb_hcd *usb_create_shared_hcd(const struct hc_driver *driver,
|
|
|
|
struct device *dev, const char *bus_name,
|
|
|
|
struct usb_hcd *primary_hcd)
|
|
|
|
{
|
|
|
|
return __usb_create_hcd(driver, dev, dev, bus_name, primary_hcd);
|
|
|
|
}
|
usb: Make core allocate resources per PCI-device.
Introduce the notion of a PCI device that may be associated with more than
one USB host controller driver (struct usb_hcd). This patch is the start
of the work to separate the xHCI host controller into two roothubs: a USB
3.0 roothub with SuperSpeed-only ports, and a USB 2.0 roothub with
HS/FS/LS ports.
One usb_hcd structure is designated to be the "primary HCD", and a pointer
is added to the usb_hcd structure to keep track of that. A new function
call, usb_hcd_is_primary_hcd() is added to check whether the USB hcd is
marked as the primary HCD (or if it is not part of a roothub pair). To
allow the USB core and xHCI driver to access either roothub in a pair, a
"shared_hcd" pointer is added to the usb_hcd structure.
Add a new function, usb_create_shared_hcd(), that does roothub allocation
for paired roothubs. It will act just like usb_create_hcd() did if the
primary_hcd pointer argument is NULL. If it is passed a non-NULL
primary_hcd pointer, it sets usb_hcd->shared_hcd and usb_hcd->primary_hcd
fields. It will also skip the bandwidth_mutex allocation, and set the
secondary hcd's bandwidth_mutex pointer to the primary HCD's mutex.
IRQs are only allocated once for the primary roothub.
Introduce a new usb_hcd driver flag that indicates the host controller
driver wants to create two roothubs. If the HCD_SHARED flag is set, then
the USB core PCI probe methods will allocate a second roothub, and make
sure that second roothub gets freed during rmmod and in initialization
error paths.
When usb_hc_died() is called with the primary HCD, make sure that any
roothubs that share that host controller are also marked as being dead.
Signed-off-by: Sarah Sharp <sarah.a.sharp@linux.intel.com>
2010-10-29 02:40:26 +04:00
|
|
|
EXPORT_SYMBOL_GPL(usb_create_shared_hcd);
|
|
|
|
|
|
|
|
/**
|
|
|
|
* usb_create_hcd - create and initialize an HCD structure
|
|
|
|
* @driver: HC driver that will use this hcd
|
|
|
|
* @dev: device for this HC, stored in hcd->self.controller
|
|
|
|
* @bus_name: value to store in hcd->self.bus_name
|
|
|
|
* Context: !in_interrupt()
|
|
|
|
*
|
|
|
|
* Allocate a struct usb_hcd, with extra space at the end for the
|
|
|
|
* HC driver's private data. Initialize the generic members of the
|
|
|
|
* hcd structure.
|
|
|
|
*
|
2013-08-02 22:10:04 +04:00
|
|
|
* Return: On success, a pointer to the created and initialized HCD
|
|
|
|
* structure. On failure (e.g. if memory is unavailable), %NULL.
|
usb: Make core allocate resources per PCI-device.
Introduce the notion of a PCI device that may be associated with more than
one USB host controller driver (struct usb_hcd). This patch is the start
of the work to separate the xHCI host controller into two roothubs: a USB
3.0 roothub with SuperSpeed-only ports, and a USB 2.0 roothub with
HS/FS/LS ports.
One usb_hcd structure is designated to be the "primary HCD", and a pointer
is added to the usb_hcd structure to keep track of that. A new function
call, usb_hcd_is_primary_hcd() is added to check whether the USB hcd is
marked as the primary HCD (or if it is not part of a roothub pair). To
allow the USB core and xHCI driver to access either roothub in a pair, a
"shared_hcd" pointer is added to the usb_hcd structure.
Add a new function, usb_create_shared_hcd(), that does roothub allocation
for paired roothubs. It will act just like usb_create_hcd() did if the
primary_hcd pointer argument is NULL. If it is passed a non-NULL
primary_hcd pointer, it sets usb_hcd->shared_hcd and usb_hcd->primary_hcd
fields. It will also skip the bandwidth_mutex allocation, and set the
secondary hcd's bandwidth_mutex pointer to the primary HCD's mutex.
IRQs are only allocated once for the primary roothub.
Introduce a new usb_hcd driver flag that indicates the host controller
driver wants to create two roothubs. If the HCD_SHARED flag is set, then
the USB core PCI probe methods will allocate a second roothub, and make
sure that second roothub gets freed during rmmod and in initialization
error paths.
When usb_hc_died() is called with the primary HCD, make sure that any
roothubs that share that host controller are also marked as being dead.
Signed-off-by: Sarah Sharp <sarah.a.sharp@linux.intel.com>
2010-10-29 02:40:26 +04:00
|
|
|
*/
|
|
|
|
struct usb_hcd *usb_create_hcd(const struct hc_driver *driver,
|
|
|
|
struct device *dev, const char *bus_name)
|
|
|
|
{
|
2017-03-13 05:18:41 +03:00
|
|
|
return __usb_create_hcd(driver, dev, dev, bus_name, NULL);
|
usb: Make core allocate resources per PCI-device.
Introduce the notion of a PCI device that may be associated with more than
one USB host controller driver (struct usb_hcd). This patch is the start
of the work to separate the xHCI host controller into two roothubs: a USB
3.0 roothub with SuperSpeed-only ports, and a USB 2.0 roothub with
HS/FS/LS ports.
One usb_hcd structure is designated to be the "primary HCD", and a pointer
is added to the usb_hcd structure to keep track of that. A new function
call, usb_hcd_is_primary_hcd() is added to check whether the USB hcd is
marked as the primary HCD (or if it is not part of a roothub pair). To
allow the USB core and xHCI driver to access either roothub in a pair, a
"shared_hcd" pointer is added to the usb_hcd structure.
Add a new function, usb_create_shared_hcd(), that does roothub allocation
for paired roothubs. It will act just like usb_create_hcd() did if the
primary_hcd pointer argument is NULL. If it is passed a non-NULL
primary_hcd pointer, it sets usb_hcd->shared_hcd and usb_hcd->primary_hcd
fields. It will also skip the bandwidth_mutex allocation, and set the
secondary hcd's bandwidth_mutex pointer to the primary HCD's mutex.
IRQs are only allocated once for the primary roothub.
Introduce a new usb_hcd driver flag that indicates the host controller
driver wants to create two roothubs. If the HCD_SHARED flag is set, then
the USB core PCI probe methods will allocate a second roothub, and make
sure that second roothub gets freed during rmmod and in initialization
error paths.
When usb_hc_died() is called with the primary HCD, make sure that any
roothubs that share that host controller are also marked as being dead.
Signed-off-by: Sarah Sharp <sarah.a.sharp@linux.intel.com>
2010-10-29 02:40:26 +04:00
|
|
|
}
|
2008-01-25 20:12:21 +03:00
|
|
|
EXPORT_SYMBOL_GPL(usb_create_hcd);
|
2005-04-17 02:20:36 +04:00
|
|
|
|
usb: Make core allocate resources per PCI-device.
Introduce the notion of a PCI device that may be associated with more than
one USB host controller driver (struct usb_hcd). This patch is the start
of the work to separate the xHCI host controller into two roothubs: a USB
3.0 roothub with SuperSpeed-only ports, and a USB 2.0 roothub with
HS/FS/LS ports.
One usb_hcd structure is designated to be the "primary HCD", and a pointer
is added to the usb_hcd structure to keep track of that. A new function
call, usb_hcd_is_primary_hcd() is added to check whether the USB hcd is
marked as the primary HCD (or if it is not part of a roothub pair). To
allow the USB core and xHCI driver to access either roothub in a pair, a
"shared_hcd" pointer is added to the usb_hcd structure.
Add a new function, usb_create_shared_hcd(), that does roothub allocation
for paired roothubs. It will act just like usb_create_hcd() did if the
primary_hcd pointer argument is NULL. If it is passed a non-NULL
primary_hcd pointer, it sets usb_hcd->shared_hcd and usb_hcd->primary_hcd
fields. It will also skip the bandwidth_mutex allocation, and set the
secondary hcd's bandwidth_mutex pointer to the primary HCD's mutex.
IRQs are only allocated once for the primary roothub.
Introduce a new usb_hcd driver flag that indicates the host controller
driver wants to create two roothubs. If the HCD_SHARED flag is set, then
the USB core PCI probe methods will allocate a second roothub, and make
sure that second roothub gets freed during rmmod and in initialization
error paths.
When usb_hc_died() is called with the primary HCD, make sure that any
roothubs that share that host controller are also marked as being dead.
Signed-off-by: Sarah Sharp <sarah.a.sharp@linux.intel.com>
2010-10-29 02:40:26 +04:00
|
|
|
/*
|
|
|
|
* Roothubs that share one PCI device must also share the bandwidth mutex.
|
|
|
|
* Don't deallocate the bandwidth_mutex until the last shared usb_hcd is
|
|
|
|
* deallocated.
|
|
|
|
*
|
2016-06-27 17:23:10 +03:00
|
|
|
* Make sure to deallocate the bandwidth_mutex only when the last HCD is
|
|
|
|
* freed. When hcd_release() is called for either hcd in a peer set,
|
|
|
|
* invalidate the peer's ->shared_hcd and ->primary_hcd pointers.
|
usb: Make core allocate resources per PCI-device.
Introduce the notion of a PCI device that may be associated with more than
one USB host controller driver (struct usb_hcd). This patch is the start
of the work to separate the xHCI host controller into two roothubs: a USB
3.0 roothub with SuperSpeed-only ports, and a USB 2.0 roothub with
HS/FS/LS ports.
One usb_hcd structure is designated to be the "primary HCD", and a pointer
is added to the usb_hcd structure to keep track of that. A new function
call, usb_hcd_is_primary_hcd() is added to check whether the USB hcd is
marked as the primary HCD (or if it is not part of a roothub pair). To
allow the USB core and xHCI driver to access either roothub in a pair, a
"shared_hcd" pointer is added to the usb_hcd structure.
Add a new function, usb_create_shared_hcd(), that does roothub allocation
for paired roothubs. It will act just like usb_create_hcd() did if the
primary_hcd pointer argument is NULL. If it is passed a non-NULL
primary_hcd pointer, it sets usb_hcd->shared_hcd and usb_hcd->primary_hcd
fields. It will also skip the bandwidth_mutex allocation, and set the
secondary hcd's bandwidth_mutex pointer to the primary HCD's mutex.
IRQs are only allocated once for the primary roothub.
Introduce a new usb_hcd driver flag that indicates the host controller
driver wants to create two roothubs. If the HCD_SHARED flag is set, then
the USB core PCI probe methods will allocate a second roothub, and make
sure that second roothub gets freed during rmmod and in initialization
error paths.
When usb_hc_died() is called with the primary HCD, make sure that any
roothubs that share that host controller are also marked as being dead.
Signed-off-by: Sarah Sharp <sarah.a.sharp@linux.intel.com>
2010-10-29 02:40:26 +04:00
|
|
|
*/
|
2014-05-21 05:08:28 +04:00
|
|
|
static void hcd_release(struct kref *kref)
|
2006-08-30 19:32:52 +04:00
|
|
|
{
|
|
|
|
struct usb_hcd *hcd = container_of (kref, struct usb_hcd, kref);
|
|
|
|
|
2014-05-21 05:08:28 +04:00
|
|
|
mutex_lock(&usb_port_peer_mutex);
|
|
|
|
if (hcd->shared_hcd) {
|
|
|
|
struct usb_hcd *peer = hcd->shared_hcd;
|
|
|
|
|
|
|
|
peer->shared_hcd = NULL;
|
2016-06-27 17:23:10 +03:00
|
|
|
peer->primary_hcd = NULL;
|
|
|
|
} else {
|
|
|
|
kfree(hcd->address0_mutex);
|
|
|
|
kfree(hcd->bandwidth_mutex);
|
2014-05-21 05:08:28 +04:00
|
|
|
}
|
|
|
|
mutex_unlock(&usb_port_peer_mutex);
|
2006-08-30 19:32:52 +04:00
|
|
|
kfree(hcd);
|
|
|
|
}
|
|
|
|
|
|
|
|
struct usb_hcd *usb_get_hcd (struct usb_hcd *hcd)
|
|
|
|
{
|
|
|
|
if (hcd)
|
|
|
|
kref_get (&hcd->kref);
|
|
|
|
return hcd;
|
|
|
|
}
|
2008-01-25 20:12:21 +03:00
|
|
|
EXPORT_SYMBOL_GPL(usb_get_hcd);
|
2006-08-30 19:32:52 +04:00
|
|
|
|
2005-04-17 02:20:36 +04:00
|
|
|
void usb_put_hcd (struct usb_hcd *hcd)
|
|
|
|
{
|
2006-08-30 19:32:52 +04:00
|
|
|
if (hcd)
|
|
|
|
kref_put (&hcd->kref, hcd_release);
|
2005-04-17 02:20:36 +04:00
|
|
|
}
|
2008-01-25 20:12:21 +03:00
|
|
|
EXPORT_SYMBOL_GPL(usb_put_hcd);
|
2005-04-17 02:20:36 +04:00
|
|
|
|
usb: Make core allocate resources per PCI-device.
Introduce the notion of a PCI device that may be associated with more than
one USB host controller driver (struct usb_hcd). This patch is the start
of the work to separate the xHCI host controller into two roothubs: a USB
3.0 roothub with SuperSpeed-only ports, and a USB 2.0 roothub with
HS/FS/LS ports.
One usb_hcd structure is designated to be the "primary HCD", and a pointer
is added to the usb_hcd structure to keep track of that. A new function
call, usb_hcd_is_primary_hcd() is added to check whether the USB hcd is
marked as the primary HCD (or if it is not part of a roothub pair). To
allow the USB core and xHCI driver to access either roothub in a pair, a
"shared_hcd" pointer is added to the usb_hcd structure.
Add a new function, usb_create_shared_hcd(), that does roothub allocation
for paired roothubs. It will act just like usb_create_hcd() did if the
primary_hcd pointer argument is NULL. If it is passed a non-NULL
primary_hcd pointer, it sets usb_hcd->shared_hcd and usb_hcd->primary_hcd
fields. It will also skip the bandwidth_mutex allocation, and set the
secondary hcd's bandwidth_mutex pointer to the primary HCD's mutex.
IRQs are only allocated once for the primary roothub.
Introduce a new usb_hcd driver flag that indicates the host controller
driver wants to create two roothubs. If the HCD_SHARED flag is set, then
the USB core PCI probe methods will allocate a second roothub, and make
sure that second roothub gets freed during rmmod and in initialization
error paths.
When usb_hc_died() is called with the primary HCD, make sure that any
roothubs that share that host controller are also marked as being dead.
Signed-off-by: Sarah Sharp <sarah.a.sharp@linux.intel.com>
2010-10-29 02:40:26 +04:00
|
|
|
int usb_hcd_is_primary_hcd(struct usb_hcd *hcd)
|
|
|
|
{
|
|
|
|
if (!hcd->primary_hcd)
|
|
|
|
return 1;
|
|
|
|
return hcd == hcd->primary_hcd;
|
|
|
|
}
|
|
|
|
EXPORT_SYMBOL_GPL(usb_hcd_is_primary_hcd);
|
|
|
|
|
2013-03-19 12:48:12 +04:00
|
|
|
int usb_hcd_find_raw_port_number(struct usb_hcd *hcd, int port1)
|
|
|
|
{
|
|
|
|
if (!hcd->driver->find_raw_port_number)
|
|
|
|
return port1;
|
|
|
|
|
|
|
|
return hcd->driver->find_raw_port_number(hcd, port1);
|
|
|
|
}
|
|
|
|
|
2010-10-21 22:14:54 +04:00
|
|
|
static int usb_hcd_request_irqs(struct usb_hcd *hcd,
|
|
|
|
unsigned int irqnum, unsigned long irqflags)
|
|
|
|
{
|
|
|
|
int retval;
|
|
|
|
|
|
|
|
if (hcd->driver->irq) {
|
2012-11-13 22:52:52 +04:00
|
|
|
|
2010-10-21 22:14:54 +04:00
|
|
|
snprintf(hcd->irq_descr, sizeof(hcd->irq_descr), "%s:usb%d",
|
|
|
|
hcd->driver->description, hcd->self.busnum);
|
|
|
|
retval = request_irq(irqnum, &usb_hcd_irq, irqflags,
|
|
|
|
hcd->irq_descr, hcd);
|
|
|
|
if (retval != 0) {
|
|
|
|
dev_err(hcd->self.controller,
|
|
|
|
"request interrupt %d failed\n",
|
|
|
|
irqnum);
|
|
|
|
return retval;
|
|
|
|
}
|
|
|
|
hcd->irq = irqnum;
|
|
|
|
dev_info(hcd->self.controller, "irq %d, %s 0x%08llx\n", irqnum,
|
|
|
|
(hcd->driver->flags & HCD_MEMORY) ?
|
|
|
|
"io mem" : "io base",
|
|
|
|
(unsigned long long)hcd->rsrc_start);
|
|
|
|
} else {
|
2012-02-29 18:46:23 +04:00
|
|
|
hcd->irq = 0;
|
2010-10-21 22:14:54 +04:00
|
|
|
if (hcd->rsrc_start)
|
|
|
|
dev_info(hcd->self.controller, "%s 0x%08llx\n",
|
|
|
|
(hcd->driver->flags & HCD_MEMORY) ?
|
|
|
|
"io mem" : "io base",
|
|
|
|
(unsigned long long)hcd->rsrc_start);
|
|
|
|
}
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
2014-05-21 05:08:28 +04:00
|
|
|
/*
|
|
|
|
* Before we free this root hub, flush in-flight peering attempts
|
|
|
|
* and disable peer lookups
|
|
|
|
*/
|
|
|
|
static void usb_put_invalidate_rhdev(struct usb_hcd *hcd)
|
|
|
|
{
|
|
|
|
struct usb_device *rhdev;
|
|
|
|
|
|
|
|
mutex_lock(&usb_port_peer_mutex);
|
|
|
|
rhdev = hcd->self.root_hub;
|
|
|
|
hcd->self.root_hub = NULL;
|
|
|
|
mutex_unlock(&usb_port_peer_mutex);
|
|
|
|
usb_put_dev(rhdev);
|
|
|
|
}
|
|
|
|
|
2005-04-17 02:20:36 +04:00
|
|
|
/**
|
|
|
|
* usb_add_hcd - finish generic HCD structure initialization and register
|
|
|
|
* @hcd: the usb_hcd structure to initialize
|
|
|
|
* @irqnum: Interrupt line to allocate
|
|
|
|
* @irqflags: Interrupt type flags
|
|
|
|
*
|
|
|
|
* Finish the remaining parts of generic HCD initialization: allocate the
|
|
|
|
* buffers of consistent memory, register the bus, request the IRQ line,
|
|
|
|
* and call the driver's reset() and start() routines.
|
|
|
|
*/
|
|
|
|
int usb_add_hcd(struct usb_hcd *hcd,
|
|
|
|
unsigned int irqnum, unsigned long irqflags)
|
|
|
|
{
|
2005-04-25 19:25:17 +04:00
|
|
|
int retval;
|
|
|
|
struct usb_device *rhdev;
|
2005-04-17 02:20:36 +04:00
|
|
|
|
2014-09-24 23:05:50 +04:00
|
|
|
if (IS_ENABLED(CONFIG_USB_PHY) && !hcd->usb_phy) {
|
2017-03-13 05:18:41 +03:00
|
|
|
struct usb_phy *phy = usb_get_phy_dev(hcd->self.sysdev, 0);
|
2013-12-04 01:42:22 +04:00
|
|
|
|
|
|
|
if (IS_ERR(phy)) {
|
|
|
|
retval = PTR_ERR(phy);
|
|
|
|
if (retval == -EPROBE_DEFER)
|
|
|
|
return retval;
|
|
|
|
} else {
|
|
|
|
retval = usb_phy_init(phy);
|
|
|
|
if (retval) {
|
|
|
|
usb_put_phy(phy);
|
|
|
|
return retval;
|
|
|
|
}
|
2014-09-24 23:05:50 +04:00
|
|
|
hcd->usb_phy = phy;
|
2013-12-04 01:42:22 +04:00
|
|
|
hcd->remove_phy = 1;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2014-10-30 20:41:16 +03:00
|
|
|
if (IS_ENABLED(CONFIG_GENERIC_PHY) && !hcd->phy) {
|
2017-03-13 05:18:41 +03:00
|
|
|
struct phy *phy = phy_get(hcd->self.sysdev, "usb");
|
2014-09-24 23:09:44 +04:00
|
|
|
|
|
|
|
if (IS_ERR(phy)) {
|
|
|
|
retval = PTR_ERR(phy);
|
|
|
|
if (retval == -EPROBE_DEFER)
|
|
|
|
goto err_phy;
|
|
|
|
} else {
|
|
|
|
retval = phy_init(phy);
|
|
|
|
if (retval) {
|
|
|
|
phy_put(phy);
|
|
|
|
goto err_phy;
|
|
|
|
}
|
|
|
|
retval = phy_power_on(phy);
|
|
|
|
if (retval) {
|
|
|
|
phy_exit(phy);
|
|
|
|
phy_put(phy);
|
|
|
|
goto err_phy;
|
|
|
|
}
|
|
|
|
hcd->phy = phy;
|
2014-10-30 20:41:16 +03:00
|
|
|
hcd->remove_phy = 1;
|
2014-09-24 23:09:44 +04:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2005-04-17 02:20:36 +04:00
|
|
|
dev_info(hcd->self.controller, "%s\n", hcd->product_desc);
|
|
|
|
|
2011-05-31 23:31:08 +04:00
|
|
|
/* Keep old behaviour if authorized_default is not in [0, 1]. */
|
2015-08-25 22:10:11 +03:00
|
|
|
if (authorized_default < 0 || authorized_default > 1) {
|
|
|
|
if (hcd->wireless)
|
|
|
|
clear_bit(HCD_FLAG_DEV_AUTHORIZED, &hcd->flags);
|
|
|
|
else
|
|
|
|
set_bit(HCD_FLAG_DEV_AUTHORIZED, &hcd->flags);
|
|
|
|
} else {
|
|
|
|
if (authorized_default)
|
|
|
|
set_bit(HCD_FLAG_DEV_AUTHORIZED, &hcd->flags);
|
|
|
|
else
|
|
|
|
clear_bit(HCD_FLAG_DEV_AUTHORIZED, &hcd->flags);
|
|
|
|
}
|
[PATCH] USB: Fix USB suspend/resume crasher (#2)
This patch closes the IRQ race and makes various other OHCI & EHCI code
path safer vs. suspend/resume.
I've been able to (finally !) successfully suspend and resume various
Mac models, with or without USB mouse plugged, or plugging while asleep,
or unplugging while asleep etc... all without a crash.
Alan, please verify the UHCI bit I did, I only verified that it builds.
It's very simple so I wouldn't expect any issue there. If you aren't
confident, then just drop the hunks that change uhci-hcd.c
I also made the patch a little bit more "safer" by making sure the store
to the interrupt register that disables interrupts is not posted before
I set the flag and drop the spinlock.
Without this patch, you cannot reliably sleep/wakeup any recent Mac, and
I suspect PCs have some more sneaky issues too (they don't frankly crash
with machine checks because x86 tend to silently swallow PCI errors but
that won't last afaik, at least PCI Express will blow up in those
situations, but the USB code may still misbehave).
Signed-off-by: Benjamin Herrenschmidt <benh@kernel.crashing.org>
Signed-off-by: Greg Kroah-Hartman <gregkh@suse.de>
Signed-off-by: Greg Kroah-Hartman <gregkh@suse.de>
2005-11-25 01:59:46 +03:00
|
|
|
set_bit(HCD_FLAG_HW_ACCESSIBLE, &hcd->flags);
|
|
|
|
|
2015-08-25 22:10:06 +03:00
|
|
|
/* per default all interfaces are authorized */
|
|
|
|
set_bit(HCD_FLAG_INTF_AUTHORIZED, &hcd->flags);
|
|
|
|
|
2006-01-24 02:25:40 +03:00
|
|
|
/* HC is in reset state, but accessible. Now do the one-time init,
|
2014-09-19 19:32:23 +04:00
|
|
|
* bottom up so that hcds can customize the root hubs before hub_wq
|
2006-01-24 02:25:40 +03:00
|
|
|
* starts talking to them. (Note, bus id is assigned early too.)
|
|
|
|
*/
|
2015-07-14 16:28:30 +03:00
|
|
|
retval = hcd_buffer_create(hcd);
|
|
|
|
if (retval != 0) {
|
2017-03-13 05:18:41 +03:00
|
|
|
dev_dbg(hcd->self.sysdev, "pool alloc failed\n");
|
2014-09-24 23:09:44 +04:00
|
|
|
goto err_create_buf;
|
2005-04-17 02:20:36 +04:00
|
|
|
}
|
|
|
|
|
2015-07-14 16:28:30 +03:00
|
|
|
retval = usb_register_bus(&hcd->self);
|
|
|
|
if (retval < 0)
|
2005-04-25 19:25:17 +04:00
|
|
|
goto err_register_bus;
|
2005-04-17 02:20:36 +04:00
|
|
|
|
2015-04-30 12:32:52 +03:00
|
|
|
rhdev = usb_alloc_dev(NULL, &hcd->self, 0);
|
|
|
|
if (rhdev == NULL) {
|
2017-03-13 05:18:41 +03:00
|
|
|
dev_err(hcd->self.sysdev, "unable to allocate root hub\n");
|
2006-01-24 02:25:40 +03:00
|
|
|
retval = -ENOMEM;
|
|
|
|
goto err_allocate_root_hub;
|
|
|
|
}
|
2014-05-21 05:08:28 +04:00
|
|
|
mutex_lock(&usb_port_peer_mutex);
|
2010-06-10 01:34:17 +04:00
|
|
|
hcd->self.root_hub = rhdev;
|
2014-05-21 05:08:28 +04:00
|
|
|
mutex_unlock(&usb_port_peer_mutex);
|
2009-04-28 06:54:10 +04:00
|
|
|
|
2010-12-03 01:45:18 +03:00
|
|
|
switch (hcd->speed) {
|
2009-04-28 06:54:10 +04:00
|
|
|
case HCD_USB11:
|
|
|
|
rhdev->speed = USB_SPEED_FULL;
|
|
|
|
break;
|
|
|
|
case HCD_USB2:
|
|
|
|
rhdev->speed = USB_SPEED_HIGH;
|
|
|
|
break;
|
2013-05-31 23:16:13 +04:00
|
|
|
case HCD_USB25:
|
|
|
|
rhdev->speed = USB_SPEED_WIRELESS;
|
|
|
|
break;
|
2009-04-28 06:54:10 +04:00
|
|
|
case HCD_USB3:
|
|
|
|
rhdev->speed = USB_SPEED_SUPER;
|
|
|
|
break;
|
2015-12-10 10:59:26 +03:00
|
|
|
case HCD_USB31:
|
|
|
|
rhdev->speed = USB_SPEED_SUPER_PLUS;
|
|
|
|
break;
|
2009-04-28 06:54:10 +04:00
|
|
|
default:
|
2011-04-14 13:22:32 +04:00
|
|
|
retval = -EINVAL;
|
2010-06-10 01:34:05 +04:00
|
|
|
goto err_set_rh_speed;
|
2009-04-28 06:54:10 +04:00
|
|
|
}
|
2006-01-24 02:25:40 +03:00
|
|
|
|
2006-05-02 09:07:13 +04:00
|
|
|
/* wakeup flag init defaults to "everything works" for root hubs,
|
|
|
|
* but drivers can override it in reset() if needed, along with
|
|
|
|
* recording the overall controller's system wakeup capability.
|
|
|
|
*/
|
2011-09-26 19:23:38 +04:00
|
|
|
device_set_wakeup_capable(&rhdev->dev, 1);
|
2006-05-02 09:07:13 +04:00
|
|
|
|
2011-03-07 19:11:52 +03:00
|
|
|
/* HCD_FLAG_RH_RUNNING doesn't matter until the root hub is
|
|
|
|
* registered. But since the controller can die at any time,
|
|
|
|
* let's initialize the flag before touching the hardware.
|
|
|
|
*/
|
|
|
|
set_bit(HCD_FLAG_RH_RUNNING, &hcd->flags);
|
|
|
|
|
2006-01-24 02:25:40 +03:00
|
|
|
/* "reset" is misnamed; its role is now one-time init. the controller
|
|
|
|
* should already have been reset (and boot firmware kicked off etc).
|
|
|
|
*/
|
2015-07-14 16:28:30 +03:00
|
|
|
if (hcd->driver->reset) {
|
|
|
|
retval = hcd->driver->reset(hcd);
|
|
|
|
if (retval < 0) {
|
|
|
|
dev_err(hcd->self.controller, "can't setup: %d\n",
|
|
|
|
retval);
|
|
|
|
goto err_hcd_driver_setup;
|
|
|
|
}
|
2006-01-24 02:25:40 +03:00
|
|
|
}
|
2010-06-10 01:34:17 +04:00
|
|
|
hcd->rh_pollable = 1;
|
2006-01-24 02:25:40 +03:00
|
|
|
|
2006-01-24 19:40:27 +03:00
|
|
|
/* NOTE: root hub and controller capabilities may not be the same */
|
|
|
|
if (device_can_wakeup(hcd->self.controller)
|
|
|
|
&& device_can_wakeup(&hcd->self.root_hub->dev))
|
2006-01-24 02:25:40 +03:00
|
|
|
dev_dbg(hcd->self.controller, "supports USB remote wakeup\n");
|
|
|
|
|
USB: HCD: support giveback of URB in tasklet context
This patch implements the mechanism of giveback of URB in
tasklet context, so that hardware interrupt handling time for
usb host controller can be saved much, and HCD interrupt handling
can be simplified.
Motivations:
1), on some arch(such as ARM), DMA mapping/unmapping is a bit
time-consuming, for example: when accessing usb mass storage
via EHCI on pandaboard, the common length of transfer buffer is 120KB,
the time consumed on DMA unmapping may reach hundreds of microseconds;
even on A15 based box, the time is still about scores of microseconds
2), on some arch, reading DMA coherent memoery is very time-consuming,
the most common example is usb video class driver[1]
3), driver's complete() callback may do much things which is driver
specific, so the time is consumed unnecessarily in hardware irq context.
4), running driver's complete() callback in hardware irq context causes
that host controller driver has to release its lock in interrupt handler,
so reacquiring the lock after return may busy wait a while and increase
interrupt handling time. More seriously, releasing the HCD lock makes
HCD becoming quite complicated to deal with introduced races.
So the patch proposes to run giveback of URB in tasklet context, then
time consumed in HCD irq handling doesn't depend on drivers' complete and
DMA mapping/unmapping any more, also we can simplify HCD since the HCD
lock isn't needed to be released during irq handling.
The patch should be reasonable and doable:
1), for drivers, they don't care if the complete() is called in hard irq
context or softirq context
2), the biggest change is the situation in which usb_submit_urb() is called
in complete() callback, so the introduced tasklet schedule delay might be a
con, but it shouldn't be a big deal:
- control/bulk asynchronous transfer isn't sensitive to schedule
delay
- the patch schedules giveback of periodic URBs using
tasklet_hi_schedule, so the introduced delay should be very
small
- for ISOC transfer, generally, drivers submit several URBs
concurrently to avoid interrupt delay, so it is OK with the
little schedule delay.
- for interrupt transfer, generally, drivers only submit one URB
at the same time, but interrupt transfer is often used in event
report, polling, ... situations, and a little delay should be OK.
Considered that HCDs may optimize on submitting URB in complete(), the
patch may cause the optimization not working, so introduces one flag to mark
if the HCD supports to run giveback URB in tasklet context. When all HCDs
are ready, the flag can be removed.
[1], http://marc.info/?t=136438111600010&r=1&w=2
Cc: Oliver Neukum <oliver@neukum.org>
Acked-by: Alan Stern <stern@rowland.harvard.edu>
Signed-off-by: Ming Lei <ming.lei@canonical.com>
Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
2013-07-03 18:53:07 +04:00
|
|
|
/* initialize tasklets */
|
|
|
|
init_giveback_urb_bh(&hcd->high_prio_bh);
|
|
|
|
init_giveback_urb_bh(&hcd->low_prio_bh);
|
|
|
|
|
2012-02-14 04:25:57 +04:00
|
|
|
/* enable irqs just before we start the controller,
|
|
|
|
* if the BIOS provides legacy PCI irqs.
|
|
|
|
*/
|
|
|
|
if (usb_hcd_is_primary_hcd(hcd) && irqnum) {
|
usb: Make core allocate resources per PCI-device.
Introduce the notion of a PCI device that may be associated with more than
one USB host controller driver (struct usb_hcd). This patch is the start
of the work to separate the xHCI host controller into two roothubs: a USB
3.0 roothub with SuperSpeed-only ports, and a USB 2.0 roothub with
HS/FS/LS ports.
One usb_hcd structure is designated to be the "primary HCD", and a pointer
is added to the usb_hcd structure to keep track of that. A new function
call, usb_hcd_is_primary_hcd() is added to check whether the USB hcd is
marked as the primary HCD (or if it is not part of a roothub pair). To
allow the USB core and xHCI driver to access either roothub in a pair, a
"shared_hcd" pointer is added to the usb_hcd structure.
Add a new function, usb_create_shared_hcd(), that does roothub allocation
for paired roothubs. It will act just like usb_create_hcd() did if the
primary_hcd pointer argument is NULL. If it is passed a non-NULL
primary_hcd pointer, it sets usb_hcd->shared_hcd and usb_hcd->primary_hcd
fields. It will also skip the bandwidth_mutex allocation, and set the
secondary hcd's bandwidth_mutex pointer to the primary HCD's mutex.
IRQs are only allocated once for the primary roothub.
Introduce a new usb_hcd driver flag that indicates the host controller
driver wants to create two roothubs. If the HCD_SHARED flag is set, then
the USB core PCI probe methods will allocate a second roothub, and make
sure that second roothub gets freed during rmmod and in initialization
error paths.
When usb_hc_died() is called with the primary HCD, make sure that any
roothubs that share that host controller are also marked as being dead.
Signed-off-by: Sarah Sharp <sarah.a.sharp@linux.intel.com>
2010-10-29 02:40:26 +04:00
|
|
|
retval = usb_hcd_request_irqs(hcd, irqnum, irqflags);
|
|
|
|
if (retval)
|
|
|
|
goto err_request_irq;
|
|
|
|
}
|
2005-04-17 02:20:36 +04:00
|
|
|
|
2011-03-12 00:46:17 +03:00
|
|
|
hcd->state = HC_STATE_RUNNING;
|
2010-12-01 20:22:05 +03:00
|
|
|
retval = hcd->driver->start(hcd);
|
|
|
|
if (retval < 0) {
|
2005-04-17 02:20:36 +04:00
|
|
|
dev_err(hcd->self.controller, "startup error %d\n", retval);
|
2005-04-25 19:25:17 +04:00
|
|
|
goto err_hcd_driver_start;
|
2005-04-17 02:20:36 +04:00
|
|
|
}
|
|
|
|
|
2006-01-24 02:25:40 +03:00
|
|
|
/* starting here, usbcore will pay attention to this root hub */
|
2015-07-14 16:28:30 +03:00
|
|
|
retval = register_root_hub(hcd);
|
|
|
|
if (retval != 0)
|
2005-04-25 19:25:17 +04:00
|
|
|
goto err_register_root_hub;
|
|
|
|
|
2007-08-01 07:33:58 +04:00
|
|
|
retval = sysfs_create_group(&rhdev->dev.kobj, &usb_bus_attr_group);
|
|
|
|
if (retval < 0) {
|
|
|
|
printk(KERN_ERR "Cannot register USB bus sysfs attributes: %d\n",
|
|
|
|
retval);
|
|
|
|
goto error_create_attr_group;
|
|
|
|
}
|
2010-06-23 00:39:10 +04:00
|
|
|
if (hcd->uses_new_polling && HCD_POLL_RH(hcd))
|
2005-04-21 23:56:37 +04:00
|
|
|
usb_hcd_poll_rh_status(hcd);
|
2011-09-26 19:23:38 +04:00
|
|
|
|
2005-04-17 02:20:36 +04:00
|
|
|
return retval;
|
|
|
|
|
2007-08-01 07:33:58 +04:00
|
|
|
error_create_attr_group:
|
2011-03-07 19:11:52 +03:00
|
|
|
clear_bit(HCD_FLAG_RH_RUNNING, &hcd->flags);
|
2010-06-10 01:34:05 +04:00
|
|
|
if (HC_IS_RUNNING(hcd->state))
|
|
|
|
hcd->state = HC_STATE_QUIESCING;
|
|
|
|
spin_lock_irq(&hcd_root_hub_lock);
|
|
|
|
hcd->rh_registered = 0;
|
|
|
|
spin_unlock_irq(&hcd_root_hub_lock);
|
|
|
|
|
2014-11-30 01:47:05 +03:00
|
|
|
#ifdef CONFIG_PM
|
2010-06-10 01:34:05 +04:00
|
|
|
cancel_work_sync(&hcd->wakeup_work);
|
|
|
|
#endif
|
2016-02-04 01:35:23 +03:00
|
|
|
mutex_lock(&usb_bus_idr_lock);
|
2010-06-10 01:34:17 +04:00
|
|
|
usb_disconnect(&rhdev); /* Sets rhdev to NULL */
|
2016-02-04 01:35:23 +03:00
|
|
|
mutex_unlock(&usb_bus_idr_lock);
|
2006-01-24 02:25:40 +03:00
|
|
|
err_register_root_hub:
|
2010-06-10 01:34:17 +04:00
|
|
|
hcd->rh_pollable = 0;
|
2010-06-23 00:39:10 +04:00
|
|
|
clear_bit(HCD_FLAG_POLL_RH, &hcd->flags);
|
2010-06-10 01:34:17 +04:00
|
|
|
del_timer_sync(&hcd->rh_timer);
|
2005-04-25 19:25:17 +04:00
|
|
|
hcd->driver->stop(hcd);
|
2010-06-10 01:34:05 +04:00
|
|
|
hcd->state = HC_STATE_HALT;
|
2010-06-23 00:39:10 +04:00
|
|
|
clear_bit(HCD_FLAG_POLL_RH, &hcd->flags);
|
2010-06-10 01:34:05 +04:00
|
|
|
del_timer_sync(&hcd->rh_timer);
|
2006-01-24 02:25:40 +03:00
|
|
|
err_hcd_driver_start:
|
2012-02-29 18:46:23 +04:00
|
|
|
if (usb_hcd_is_primary_hcd(hcd) && hcd->irq > 0)
|
2005-04-17 02:20:36 +04:00
|
|
|
free_irq(irqnum, hcd);
|
2006-01-24 02:25:40 +03:00
|
|
|
err_request_irq:
|
|
|
|
err_hcd_driver_setup:
|
2010-06-10 01:34:05 +04:00
|
|
|
err_set_rh_speed:
|
2014-05-21 05:08:28 +04:00
|
|
|
usb_put_invalidate_rhdev(hcd);
|
2006-01-24 02:25:40 +03:00
|
|
|
err_allocate_root_hub:
|
2005-04-17 02:20:36 +04:00
|
|
|
usb_deregister_bus(&hcd->self);
|
2006-01-24 02:25:40 +03:00
|
|
|
err_register_bus:
|
2005-04-17 02:20:36 +04:00
|
|
|
hcd_buffer_destroy(hcd);
|
2014-09-24 23:09:44 +04:00
|
|
|
err_create_buf:
|
2014-10-30 20:41:16 +03:00
|
|
|
if (IS_ENABLED(CONFIG_GENERIC_PHY) && hcd->remove_phy && hcd->phy) {
|
2014-09-24 23:09:44 +04:00
|
|
|
phy_power_off(hcd->phy);
|
|
|
|
phy_exit(hcd->phy);
|
|
|
|
phy_put(hcd->phy);
|
|
|
|
hcd->phy = NULL;
|
|
|
|
}
|
|
|
|
err_phy:
|
2014-09-24 23:05:50 +04:00
|
|
|
if (hcd->remove_phy && hcd->usb_phy) {
|
|
|
|
usb_phy_shutdown(hcd->usb_phy);
|
|
|
|
usb_put_phy(hcd->usb_phy);
|
|
|
|
hcd->usb_phy = NULL;
|
2013-12-04 01:42:21 +04:00
|
|
|
}
|
2005-04-17 02:20:36 +04:00
|
|
|
return retval;
|
2013-10-05 20:02:07 +04:00
|
|
|
}
|
2008-01-25 20:12:21 +03:00
|
|
|
EXPORT_SYMBOL_GPL(usb_add_hcd);
|
2005-04-17 02:20:36 +04:00
|
|
|
|
|
|
|
/**
|
|
|
|
* usb_remove_hcd - shutdown processing for generic HCDs
|
|
|
|
* @hcd: the usb_hcd structure to remove
|
|
|
|
* Context: !in_interrupt()
|
|
|
|
*
|
|
|
|
* Disconnects the root hub, then reverses the effects of usb_add_hcd(),
|
|
|
|
* invoking the HCD's stop() method.
|
|
|
|
*/
|
|
|
|
void usb_remove_hcd(struct usb_hcd *hcd)
|
|
|
|
{
|
2010-06-10 01:34:17 +04:00
|
|
|
struct usb_device *rhdev = hcd->self.root_hub;
|
|
|
|
|
2005-04-17 02:20:36 +04:00
|
|
|
dev_info(hcd->self.controller, "remove, state %x\n", hcd->state);
|
|
|
|
|
2010-06-10 01:34:17 +04:00
|
|
|
usb_get_dev(rhdev);
|
|
|
|
sysfs_remove_group(&rhdev->dev.kobj, &usb_bus_attr_group);
|
2010-06-10 01:34:05 +04:00
|
|
|
|
2011-03-07 19:11:52 +03:00
|
|
|
clear_bit(HCD_FLAG_RH_RUNNING, &hcd->flags);
|
2005-04-17 02:20:36 +04:00
|
|
|
if (HC_IS_RUNNING (hcd->state))
|
|
|
|
hcd->state = HC_STATE_QUIESCING;
|
|
|
|
|
|
|
|
dev_dbg(hcd->self.controller, "roothub graceful disconnect\n");
|
|
|
|
spin_lock_irq (&hcd_root_hub_lock);
|
|
|
|
hcd->rh_registered = 0;
|
|
|
|
spin_unlock_irq (&hcd_root_hub_lock);
|
2005-11-18 01:10:32 +03:00
|
|
|
|
2014-11-30 01:47:05 +03:00
|
|
|
#ifdef CONFIG_PM
|
2007-05-30 00:34:52 +04:00
|
|
|
cancel_work_sync(&hcd->wakeup_work);
|
2007-03-13 23:37:30 +03:00
|
|
|
#endif
|
|
|
|
|
2016-02-04 01:35:23 +03:00
|
|
|
mutex_lock(&usb_bus_idr_lock);
|
2010-06-10 01:34:17 +04:00
|
|
|
usb_disconnect(&rhdev); /* Sets rhdev to NULL */
|
2016-02-04 01:35:23 +03:00
|
|
|
mutex_unlock(&usb_bus_idr_lock);
|
2005-04-17 02:20:36 +04:00
|
|
|
|
USB: HCD: support giveback of URB in tasklet context
This patch implements the mechanism of giveback of URB in
tasklet context, so that hardware interrupt handling time for
usb host controller can be saved much, and HCD interrupt handling
can be simplified.
Motivations:
1), on some arch(such as ARM), DMA mapping/unmapping is a bit
time-consuming, for example: when accessing usb mass storage
via EHCI on pandaboard, the common length of transfer buffer is 120KB,
the time consumed on DMA unmapping may reach hundreds of microseconds;
even on A15 based box, the time is still about scores of microseconds
2), on some arch, reading DMA coherent memoery is very time-consuming,
the most common example is usb video class driver[1]
3), driver's complete() callback may do much things which is driver
specific, so the time is consumed unnecessarily in hardware irq context.
4), running driver's complete() callback in hardware irq context causes
that host controller driver has to release its lock in interrupt handler,
so reacquiring the lock after return may busy wait a while and increase
interrupt handling time. More seriously, releasing the HCD lock makes
HCD becoming quite complicated to deal with introduced races.
So the patch proposes to run giveback of URB in tasklet context, then
time consumed in HCD irq handling doesn't depend on drivers' complete and
DMA mapping/unmapping any more, also we can simplify HCD since the HCD
lock isn't needed to be released during irq handling.
The patch should be reasonable and doable:
1), for drivers, they don't care if the complete() is called in hard irq
context or softirq context
2), the biggest change is the situation in which usb_submit_urb() is called
in complete() callback, so the introduced tasklet schedule delay might be a
con, but it shouldn't be a big deal:
- control/bulk asynchronous transfer isn't sensitive to schedule
delay
- the patch schedules giveback of periodic URBs using
tasklet_hi_schedule, so the introduced delay should be very
small
- for ISOC transfer, generally, drivers submit several URBs
concurrently to avoid interrupt delay, so it is OK with the
little schedule delay.
- for interrupt transfer, generally, drivers only submit one URB
at the same time, but interrupt transfer is often used in event
report, polling, ... situations, and a little delay should be OK.
Considered that HCDs may optimize on submitting URB in complete(), the
patch may cause the optimization not working, so introduces one flag to mark
if the HCD supports to run giveback URB in tasklet context. When all HCDs
are ready, the flag can be removed.
[1], http://marc.info/?t=136438111600010&r=1&w=2
Cc: Oliver Neukum <oliver@neukum.org>
Acked-by: Alan Stern <stern@rowland.harvard.edu>
Signed-off-by: Ming Lei <ming.lei@canonical.com>
Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
2013-07-03 18:53:07 +04:00
|
|
|
/*
|
|
|
|
* tasklet_kill() isn't needed here because:
|
|
|
|
* - driver's disconnect() called from usb_disconnect() should
|
|
|
|
* make sure its URBs are completed during the disconnect()
|
|
|
|
* callback
|
|
|
|
*
|
|
|
|
* - it is too late to run complete() here since driver may have
|
|
|
|
* been removed already now
|
|
|
|
*/
|
|
|
|
|
2010-06-10 01:34:17 +04:00
|
|
|
/* Prevent any more root-hub status calls from the timer.
|
|
|
|
* The HCD might still restart the timer (if a port status change
|
|
|
|
* interrupt occurs), but usb_hcd_poll_rh_status() won't invoke
|
|
|
|
* the hub_status_data() callback.
|
|
|
|
*/
|
|
|
|
hcd->rh_pollable = 0;
|
2010-06-23 00:39:10 +04:00
|
|
|
clear_bit(HCD_FLAG_POLL_RH, &hcd->flags);
|
2010-06-10 01:34:17 +04:00
|
|
|
del_timer_sync(&hcd->rh_timer);
|
|
|
|
|
2005-04-17 02:20:36 +04:00
|
|
|
hcd->driver->stop(hcd);
|
|
|
|
hcd->state = HC_STATE_HALT;
|
|
|
|
|
2010-06-10 01:34:17 +04:00
|
|
|
/* In case the HCD restarted the timer, stop it again. */
|
2010-06-23 00:39:10 +04:00
|
|
|
clear_bit(HCD_FLAG_POLL_RH, &hcd->flags);
|
2007-03-13 18:10:52 +03:00
|
|
|
del_timer_sync(&hcd->rh_timer);
|
|
|
|
|
usb: Make core allocate resources per PCI-device.
Introduce the notion of a PCI device that may be associated with more than
one USB host controller driver (struct usb_hcd). This patch is the start
of the work to separate the xHCI host controller into two roothubs: a USB
3.0 roothub with SuperSpeed-only ports, and a USB 2.0 roothub with
HS/FS/LS ports.
One usb_hcd structure is designated to be the "primary HCD", and a pointer
is added to the usb_hcd structure to keep track of that. A new function
call, usb_hcd_is_primary_hcd() is added to check whether the USB hcd is
marked as the primary HCD (or if it is not part of a roothub pair). To
allow the USB core and xHCI driver to access either roothub in a pair, a
"shared_hcd" pointer is added to the usb_hcd structure.
Add a new function, usb_create_shared_hcd(), that does roothub allocation
for paired roothubs. It will act just like usb_create_hcd() did if the
primary_hcd pointer argument is NULL. If it is passed a non-NULL
primary_hcd pointer, it sets usb_hcd->shared_hcd and usb_hcd->primary_hcd
fields. It will also skip the bandwidth_mutex allocation, and set the
secondary hcd's bandwidth_mutex pointer to the primary HCD's mutex.
IRQs are only allocated once for the primary roothub.
Introduce a new usb_hcd driver flag that indicates the host controller
driver wants to create two roothubs. If the HCD_SHARED flag is set, then
the USB core PCI probe methods will allocate a second roothub, and make
sure that second roothub gets freed during rmmod and in initialization
error paths.
When usb_hc_died() is called with the primary HCD, make sure that any
roothubs that share that host controller are also marked as being dead.
Signed-off-by: Sarah Sharp <sarah.a.sharp@linux.intel.com>
2010-10-29 02:40:26 +04:00
|
|
|
if (usb_hcd_is_primary_hcd(hcd)) {
|
2012-02-29 18:46:23 +04:00
|
|
|
if (hcd->irq > 0)
|
usb: Make core allocate resources per PCI-device.
Introduce the notion of a PCI device that may be associated with more than
one USB host controller driver (struct usb_hcd). This patch is the start
of the work to separate the xHCI host controller into two roothubs: a USB
3.0 roothub with SuperSpeed-only ports, and a USB 2.0 roothub with
HS/FS/LS ports.
One usb_hcd structure is designated to be the "primary HCD", and a pointer
is added to the usb_hcd structure to keep track of that. A new function
call, usb_hcd_is_primary_hcd() is added to check whether the USB hcd is
marked as the primary HCD (or if it is not part of a roothub pair). To
allow the USB core and xHCI driver to access either roothub in a pair, a
"shared_hcd" pointer is added to the usb_hcd structure.
Add a new function, usb_create_shared_hcd(), that does roothub allocation
for paired roothubs. It will act just like usb_create_hcd() did if the
primary_hcd pointer argument is NULL. If it is passed a non-NULL
primary_hcd pointer, it sets usb_hcd->shared_hcd and usb_hcd->primary_hcd
fields. It will also skip the bandwidth_mutex allocation, and set the
secondary hcd's bandwidth_mutex pointer to the primary HCD's mutex.
IRQs are only allocated once for the primary roothub.
Introduce a new usb_hcd driver flag that indicates the host controller
driver wants to create two roothubs. If the HCD_SHARED flag is set, then
the USB core PCI probe methods will allocate a second roothub, and make
sure that second roothub gets freed during rmmod and in initialization
error paths.
When usb_hc_died() is called with the primary HCD, make sure that any
roothubs that share that host controller are also marked as being dead.
Signed-off-by: Sarah Sharp <sarah.a.sharp@linux.intel.com>
2010-10-29 02:40:26 +04:00
|
|
|
free_irq(hcd->irq, hcd);
|
|
|
|
}
|
2010-06-10 01:34:17 +04:00
|
|
|
|
2005-04-17 02:20:36 +04:00
|
|
|
usb_deregister_bus(&hcd->self);
|
|
|
|
hcd_buffer_destroy(hcd);
|
2014-09-24 23:09:44 +04:00
|
|
|
|
2014-10-30 20:41:16 +03:00
|
|
|
if (IS_ENABLED(CONFIG_GENERIC_PHY) && hcd->remove_phy && hcd->phy) {
|
2014-09-24 23:09:44 +04:00
|
|
|
phy_power_off(hcd->phy);
|
|
|
|
phy_exit(hcd->phy);
|
|
|
|
phy_put(hcd->phy);
|
|
|
|
hcd->phy = NULL;
|
|
|
|
}
|
2014-09-24 23:05:50 +04:00
|
|
|
if (hcd->remove_phy && hcd->usb_phy) {
|
|
|
|
usb_phy_shutdown(hcd->usb_phy);
|
|
|
|
usb_put_phy(hcd->usb_phy);
|
|
|
|
hcd->usb_phy = NULL;
|
2013-12-04 01:42:21 +04:00
|
|
|
}
|
2014-05-21 05:08:28 +04:00
|
|
|
|
|
|
|
usb_put_invalidate_rhdev(hcd);
|
2017-01-13 06:04:22 +03:00
|
|
|
hcd->flags = 0;
|
2005-04-17 02:20:36 +04:00
|
|
|
}
|
2008-01-25 20:12:21 +03:00
|
|
|
EXPORT_SYMBOL_GPL(usb_remove_hcd);
|
2005-04-17 02:20:36 +04:00
|
|
|
|
USB: Properly unregister reboot notifier in case of failure in ehci hcd
If some problem occurs during ehci startup, for instance, request_irq fails,
echi hcd driver tries it best to cleanup, but fails to unregister reboot
notifier, which in turn leads to crash on reboot/poweroff.
The following patch resolves this problem by not using reboot notifiers
anymore, but instead making ehci/ohci driver get its own shutdown method. For
PCI, it is done through pci glue, for everything else through platform driver
glue.
One downside: sa1111 does not use platform driver stuff, and does not have its
own shutdown hook, so no 'shutdown' is called for it now. I'm not sure if it
is really necessary on that platform, though.
Signed-off-by: Aleks Gorelov <dared1st@yahoo.com>
Cc: Alan Stern <stern@rowland.harvard.edu>
Cc: David Brownell <david-b@pacbell.net>
Signed-off-by: Andrew Morton <akpm@osdl.org>
Signed-off-by: Greg Kroah-Hartman <gregkh@suse.de>
2006-08-09 04:24:08 +04:00
|
|
|
void
|
2013-10-05 20:02:06 +04:00
|
|
|
usb_hcd_platform_shutdown(struct platform_device *dev)
|
USB: Properly unregister reboot notifier in case of failure in ehci hcd
If some problem occurs during ehci startup, for instance, request_irq fails,
echi hcd driver tries it best to cleanup, but fails to unregister reboot
notifier, which in turn leads to crash on reboot/poweroff.
The following patch resolves this problem by not using reboot notifiers
anymore, but instead making ehci/ohci driver get its own shutdown method. For
PCI, it is done through pci glue, for everything else through platform driver
glue.
One downside: sa1111 does not use platform driver stuff, and does not have its
own shutdown hook, so no 'shutdown' is called for it now. I'm not sure if it
is really necessary on that platform, though.
Signed-off-by: Aleks Gorelov <dared1st@yahoo.com>
Cc: Alan Stern <stern@rowland.harvard.edu>
Cc: David Brownell <david-b@pacbell.net>
Signed-off-by: Andrew Morton <akpm@osdl.org>
Signed-off-by: Greg Kroah-Hartman <gregkh@suse.de>
2006-08-09 04:24:08 +04:00
|
|
|
{
|
|
|
|
struct usb_hcd *hcd = platform_get_drvdata(dev);
|
|
|
|
|
|
|
|
if (hcd->driver->shutdown)
|
|
|
|
hcd->driver->shutdown(hcd);
|
|
|
|
}
|
2008-01-25 20:12:21 +03:00
|
|
|
EXPORT_SYMBOL_GPL(usb_hcd_platform_shutdown);
|
USB: Properly unregister reboot notifier in case of failure in ehci hcd
If some problem occurs during ehci startup, for instance, request_irq fails,
echi hcd driver tries it best to cleanup, but fails to unregister reboot
notifier, which in turn leads to crash on reboot/poweroff.
The following patch resolves this problem by not using reboot notifiers
anymore, but instead making ehci/ohci driver get its own shutdown method. For
PCI, it is done through pci glue, for everything else through platform driver
glue.
One downside: sa1111 does not use platform driver stuff, and does not have its
own shutdown hook, so no 'shutdown' is called for it now. I'm not sure if it
is really necessary on that platform, though.
Signed-off-by: Aleks Gorelov <dared1st@yahoo.com>
Cc: Alan Stern <stern@rowland.harvard.edu>
Cc: David Brownell <david-b@pacbell.net>
Signed-off-by: Andrew Morton <akpm@osdl.org>
Signed-off-by: Greg Kroah-Hartman <gregkh@suse.de>
2006-08-09 04:24:08 +04:00
|
|
|
|
2005-04-17 02:20:36 +04:00
|
|
|
/*-------------------------------------------------------------------------*/
|
|
|
|
|
2016-08-18 16:45:04 +03:00
|
|
|
#if IS_ENABLED(CONFIG_USB_MON)
|
2005-04-17 02:20:36 +04:00
|
|
|
|
2015-11-28 18:07:10 +03:00
|
|
|
const struct usb_mon_operations *mon_ops;
|
2005-04-17 02:20:36 +04:00
|
|
|
|
|
|
|
/*
|
|
|
|
* The registration is unlocked.
|
|
|
|
* We do it this way because we do not want to lock in hot paths.
|
|
|
|
*
|
|
|
|
* Notice that the code is minimally error-proof. Because usbmon needs
|
|
|
|
* symbols from usbcore, usbcore gets referenced and cannot be unloaded first.
|
|
|
|
*/
|
2013-10-05 20:02:07 +04:00
|
|
|
|
2015-11-28 18:07:10 +03:00
|
|
|
int usb_mon_register(const struct usb_mon_operations *ops)
|
2005-04-17 02:20:36 +04:00
|
|
|
{
|
|
|
|
|
|
|
|
if (mon_ops)
|
|
|
|
return -EBUSY;
|
|
|
|
|
|
|
|
mon_ops = ops;
|
|
|
|
mb();
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
EXPORT_SYMBOL_GPL (usb_mon_register);
|
|
|
|
|
|
|
|
void usb_mon_deregister (void)
|
|
|
|
{
|
|
|
|
|
|
|
|
if (mon_ops == NULL) {
|
|
|
|
printk(KERN_ERR "USB: monitor was not registered\n");
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
mon_ops = NULL;
|
|
|
|
mb();
|
|
|
|
}
|
|
|
|
EXPORT_SYMBOL_GPL (usb_mon_deregister);
|
|
|
|
|
2008-11-14 07:31:21 +03:00
|
|
|
#endif /* CONFIG_USB_MON || CONFIG_USB_MON_MODULE */
|