2
0
mirror of https://github.com/edk2-porting/linux-next.git synced 2024-12-29 23:53:55 +08:00
linux-next/drivers/usb/core/hub.h

155 lines
4.6 KiB
C
Raw Normal View History

/*
* usb hub driver head file
*
* Copyright (C) 1999 Linus Torvalds
* Copyright (C) 1999 Johannes Erdfelt
* Copyright (C) 1999 Gregory P. Smith
* Copyright (C) 2001 Brad Hards (bhards@bigpond.net.au)
* Copyright (C) 2012 Intel Corp (tianyu.lan@intel.com)
*
* move struct usb_hub to this file.
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 2 as
* published by the Free Software Foundation.
*
* 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.
*/
#include <linux/usb.h>
#include <linux/usb/ch11.h>
#include <linux/usb/hcd.h>
#include "usb.h"
struct usb_hub {
struct device *intfdev; /* the "interface" device */
struct usb_device *hdev;
struct kref kref;
struct urb *urb; /* for interrupt polling pipe */
/* buffer for urb ... with extra space in case of babble */
u8 (*buffer)[8];
union {
struct usb_hub_status hub;
struct usb_port_status port;
} *status; /* buffer for status reports */
struct mutex status_mutex; /* for the status buffer */
int error; /* last reported error */
int nerrors; /* track consecutive errors */
struct list_head event_list; /* hubs w/data or errs ready */
unsigned long event_bits[1]; /* status change bitmask */
unsigned long change_bits[1]; /* ports with logical connect
status change */
unsigned long removed_bits[1]; /* ports with a "removed"
device present */
unsigned long wakeup_bits[1]; /* ports that have signaled
remote wakeup */
unsigned long power_bits[1]; /* ports that are powered */
unsigned long child_usage_bits[1]; /* ports powered on for
children */
#if USB_MAXCHILDREN > 31 /* 8*sizeof(unsigned long) - 1 */
#error event_bits[] is too short!
#endif
struct usb_hub_descriptor *descriptor; /* class descriptor */
struct usb_tt tt; /* Transaction Translator */
unsigned mA_per_port; /* current for each child */
USB: global suspend and remote wakeup don't mix The hub driver was recently changed to use "global" suspend for system suspend transitions on non-SuperSpeed buses. This means that we don't suspend devices individually by setting the suspend feature on the upstream hub port; instead devices all go into suspend automatically when the root hub stops transmitting packets. The idea was to save time and to avoid certain kinds of wakeup races. Now it turns out that many hubs are buggy; they don't relay wakeup requests from a downstream port to their upstream port if the downstream port's suspend feature is not set (depending on the speed of the downstream port, whether or not the hub is enabled for remote wakeup, and possibly other factors). We can't have hubs dropping wakeup requests. Therefore this patch goes partway back to the old policy: It sets the suspend feature for a port if the device attached to that port or any of its descendants is enabled for wakeup. People will still be able to benefit from the time savings if they don't care about wakeup and leave it disabled on all their devices. In order to accomplish this, the patch adds a new field to the usb_hub structure: wakeup_enabled_descendants is a count of how many devices below a suspended hub are enabled for remote wakeup. A corresponding new subroutine determines the number of wakeup-enabled devices at or below an arbitrary suspended USB device. This should be applied to the 3.10 stable kernel. Signed-off-by: Alan Stern <stern@rowland.harvard.edu> Reported-and-tested-by: Toralf Förster <toralf.foerster@gmx.de> Cc: stable <stable@vger.kernel.org> Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
2013-07-12 02:58:04 +08:00
#ifdef CONFIG_PM
unsigned wakeup_enabled_descendants;
#endif
unsigned limited_power:1;
unsigned quiescing:1;
unsigned disconnected:1;
unsigned in_reset:1;
unsigned quirk_check_port_auto_suspend:1;
unsigned has_indicators:1;
u8 indicator[USB_MAXCHILDREN];
struct delayed_work leds;
struct delayed_work init_work;
struct usb_port **ports;
};
/**
* struct usb port - kernel's representation of a usb port
* @child: usb device attached to the port
* @dev: generic device interface
* @port_owner: port's owner
* @peer: related usb2 and usb3 ports (share the same connector)
* @connect_type: port's connect type
2014-05-21 09:08:40 +08:00
* @location: opaque representation of platform connector location
usb: introduce port status lock In general we do not want khubd to act on port status changes that are the result of in progress resets or USB runtime PM operations. Specifically port power control testing has been able to trigger an unintended disconnect in hub_port_connect_change(), paraphrasing: if ((portstatus & USB_PORT_STAT_CONNECTION) && udev && udev->state != USB_STATE_NOTATTACHED) { if (portstatus & USB_PORT_STAT_ENABLE) { /* Nothing to do */ } else if (udev->state == USB_STATE_SUSPENDED && udev->persist_enabled) { ... } else { /* Don't resuscitate */; } } ...by falling to the "Don't resuscitate" path or missing USB_PORT_STAT_CONNECTION because usb_port_resume() was in the middle of modifying the port status. So, we want a new lock to hold off khubd for a given port while the child device is being suspended, resumed, or reset. The lock ordering rules are now usb_lock_device() => usb_lock_port(). This is mandated by the device core which may hold the device_lock on the usb_device before invoking usb_port_{suspend|resume} which in turn take the status_lock on the usb_port. We attempt to hold the status_lock for the duration of a port_event() run, and drop/re-acquire it when needing to take the device_lock. The lock is also dropped/re-acquired during hub_port_reconnect(). This patch also deletes hub->busy_bits as all use cases are now covered by port PM runtime synchronization or the port->status_lock and it pushes down usb_device_lock() into usb_remote_wakeup(). Acked-by: Alan Stern <stern@rowland.harvard.edu> Signed-off-by: Dan Williams <dan.j.williams@intel.com> Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
2014-05-21 09:09:26 +08:00
* @status_lock: synchronize port_event() vs usb_port_{suspend|resume}
* @portnum: port index num based one
* @is_superspeed cache super-speed status
*/
struct usb_port {
struct usb_device *child;
struct device dev;
struct usb_dev_state *port_owner;
struct usb_port *peer;
enum usb_port_connect_type connect_type;
2014-05-21 09:08:40 +08:00
usb_port_location_t location;
usb: introduce port status lock In general we do not want khubd to act on port status changes that are the result of in progress resets or USB runtime PM operations. Specifically port power control testing has been able to trigger an unintended disconnect in hub_port_connect_change(), paraphrasing: if ((portstatus & USB_PORT_STAT_CONNECTION) && udev && udev->state != USB_STATE_NOTATTACHED) { if (portstatus & USB_PORT_STAT_ENABLE) { /* Nothing to do */ } else if (udev->state == USB_STATE_SUSPENDED && udev->persist_enabled) { ... } else { /* Don't resuscitate */; } } ...by falling to the "Don't resuscitate" path or missing USB_PORT_STAT_CONNECTION because usb_port_resume() was in the middle of modifying the port status. So, we want a new lock to hold off khubd for a given port while the child device is being suspended, resumed, or reset. The lock ordering rules are now usb_lock_device() => usb_lock_port(). This is mandated by the device core which may hold the device_lock on the usb_device before invoking usb_port_{suspend|resume} which in turn take the status_lock on the usb_port. We attempt to hold the status_lock for the duration of a port_event() run, and drop/re-acquire it when needing to take the device_lock. The lock is also dropped/re-acquired during hub_port_reconnect(). This patch also deletes hub->busy_bits as all use cases are now covered by port PM runtime synchronization or the port->status_lock and it pushes down usb_device_lock() into usb_remote_wakeup(). Acked-by: Alan Stern <stern@rowland.harvard.edu> Signed-off-by: Dan Williams <dan.j.williams@intel.com> Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
2014-05-21 09:09:26 +08:00
struct mutex status_lock;
u8 portnum;
unsigned int is_superspeed:1;
};
#define to_usb_port(_dev) \
container_of(_dev, struct usb_port, dev)
extern int usb_hub_create_port_device(struct usb_hub *hub,
int port1);
extern void usb_hub_remove_port_device(struct usb_hub *hub,
int port1);
extern int usb_hub_set_port_power(struct usb_device *hdev, struct usb_hub *hub,
int port1, bool set);
2013-01-23 04:26:30 +08:00
extern struct usb_hub *usb_hub_to_struct_hub(struct usb_device *hdev);
extern int hub_port_debounce(struct usb_hub *hub, int port1,
bool must_be_connected);
extern int usb_clear_port_feature(struct usb_device *hdev,
int port1, int feature);
static inline bool hub_is_port_power_switchable(struct usb_hub *hub)
{
__le16 hcs;
if (!hub)
return false;
hcs = hub->descriptor->wHubCharacteristics;
return (le16_to_cpu(hcs) & HUB_CHAR_LPSM) < HUB_CHAR_NO_LPSM;
}
static inline int hub_is_superspeed(struct usb_device *hdev)
{
return hdev->descriptor.bDeviceProtocol == USB_HUB_PR_SS;
}
static inline unsigned hub_power_on_good_delay(struct usb_hub *hub)
{
unsigned delay = hub->descriptor->bPwrOn2PwrGood * 2;
/* Wait at least 100 msec for power to become stable */
return max(delay, 100U);
}
2013-01-23 04:26:30 +08:00
static inline int hub_port_debounce_be_connected(struct usb_hub *hub,
int port1)
{
return hub_port_debounce(hub, port1, true);
}
static inline int hub_port_debounce_be_stable(struct usb_hub *hub,
int port1)
{
return hub_port_debounce(hub, port1, false);
}