Re: [RFC 06/13] USB: Add support to enable/disable USB3 link states.

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]


On 05/17/2012 05:55 AM, Sarah Sharp wrote:
> There are various functions within the USB core that will need to
> disable USB 3.0 link power states.  For example, when a USB device
> driver is being bound to an interface, we need to disable USB 3.0 LPM
> until we know if the driver will allow hub-initiated LPM transitions.
> Another example is when the USB core is switching alternate interface
> settings.  The USB 3.0 timeout values are dependent on what endpoints
> are enabled, so we want to ensure that LPM is disabled until the new alt
> setting is fully installed.
> 
> Multiple functions need to disable LPM, and those functions can even be
> nested.  For example, usb_bind_interface() could disable LPM, and then
> call into the driver probe function, which may attempt to switch to a
> different alt setting.  Therefore, we need to keep a count of the number
> of functions that require LPM to be disabled at any point in time.
> 
> Introduce two new USB core API calls, usb_disable_lpm() and
> usb_enable_lpm().  These functions increment and decrement a new
> variable in the usb_device, lpm_disable_count.  These two function calls
> should always be paired.  Even if usb_disable_lpm() fails,
> usb_enable_lpm() must be called to balance the lpm_disable_count.  The
> bandwidth_mutex must be held when these new functions are called.
> 
> Introduce a new variable (timeout) in the usb3_lpm_params structure to
> keep track of the currently enabled U1/U2 timeout values.  When
> usb_disable_lpm() is called, and the USB device has the U1 or U2
> timeouts set to a non-zero value (meaning either device-initiated or
> hub-initiated LPM is enabled), attempt to disable LPM, regardless of the
> state of the lpm_disable_count.  We want to ensure that all callers can
> be guaranteed that LPM is disabled if usb_disable_lpm() returns zero.
> 
> Otherwise the following scenario could occur:
> 
> 1. Driver A is being bound to interface 1.  usb_probe_interface()
> disables LPM.  Driver A doesn't care if hub-initiated LPM is enabled, so
> even though usb_disable_lpm() fails, the probe of the driver continues,
> and the bandwidth mutex is dropped.
> 
> 2. Meanwhile, Driver B is being bound to interface 2.
> usb_probe_interface() grabs the bandwidth mutex and calls
> usb_disable_lpm().  That call should attempt to disable LPM, even
> though the lpm_disable_count is set to 1 by Driver A.
> 
> For usb_enable_lpm(), we attempt to enable LPM only when the
> lpm_disable_count is zero.  If some step in enabling LPM fails, it will
> only have a minimal impact on power consumption, and all USB device
> drivers should still work properly.  Therefore don't bother to return
> any error codes.
> 
> Don't enable device-initiated LPM if the device is unconfigured.  The
> USB device will only accept the U1/U2_ENABLE control transfers in the
> configured state.  Do enable hub-initiated LPM in that case, since
> devices are allowed to accept the LGO_Ux link commands in any state.
> 
> Don't enable or disable LPM if the device is marked as not being LPM
> capable.  This can happen if:
>  - the USB device doesn't have a SS BOS descriptor,
>  - the device's parent hub has a zeroed bHeaderDecodeLatency value, or
>  - the xHCI host doesn't support LPM.
> 
> Signed-off-by: Sarah Sharp <sarah.a.sharp@xxxxxxxxxxxxxxx>
> ---
>  drivers/usb/core/hub.c   |  365 ++++++++++++++++++++++++++++++++++++++++++++++
>  include/linux/usb.h      |    9 +
>  include/linux/usb/ch11.h |    2 +
>  include/linux/usb/ch9.h  |   45 ++++++
>  include/linux/usb/hcd.h  |    9 +
>  5 files changed, 430 insertions(+), 0 deletions(-)
> 
> diff --git a/drivers/usb/core/hub.c b/drivers/usb/core/hub.c
> index 5219507..b170505 100644
> --- a/drivers/usb/core/hub.c
> +++ b/drivers/usb/core/hub.c
> @@ -3050,11 +3050,376 @@ void usb_root_hub_lost_power(struct usb_device *rhdev)
>  }
>  EXPORT_SYMBOL_GPL(usb_root_hub_lost_power);
>  
> +static const char * const usb3_lpm_names[]  = {
> +	"U0",
> +	"U1",
> +	"U2",
> +	"U3",
> +};
> +
> +/*
> + * Send a Set SEL control transfer to the device, prior to enabling
> + * device-initiated U1 or U2.  This lets the device know the exit latencies from
> + * the time the device initiates a U1 or U2 exit, to the time it will receive a
> + * packet from the host.
> + *
> + * This function will fail if the SEL or PEL values for udev are greater than
> + * the maximum allowed values for the link state to be enabled.
> + */
> +static int usb_req_set_sel(struct usb_device *udev, enum usb3_link_state state)
> +{
> +	struct usb_set_sel_req *sel_values;
> +	unsigned long long u1_sel;
> +	unsigned long long u1_pel;
> +	unsigned long long u2_sel;
> +	unsigned long long u2_pel;
> +
> +	/* Convert SEL and PEL stored in ns to us */
> +	u1_sel = DIV_ROUND_UP(udev->u1_params.sel, 1000);
> +	u1_pel = DIV_ROUND_UP(udev->u1_params.pel, 1000);
> +	u2_sel = DIV_ROUND_UP(udev->u2_params.sel, 1000);
> +	u2_pel = DIV_ROUND_UP(udev->u2_params.pel, 1000);
> +
> +	/*
> +	 * Make sure that the calculated SEL and PEL values for the link
> +	 * state we're enabling aren't bigger than the max SEL/PEL
> +	 * value that will fit in the SET SEL control transfer.
> +	 * Otherwise the device would get an incorrect idea of the exit
> +	 * latency for the link state, and could start a device-initiated
> +	 * U1/U2 when the exit latencies are too high.
> +	 */
> +	if ((state == USB3_LPM_U1 &&
> +				(u1_sel > USB3_LPM_MAX_U1_SEL_PEL ||
> +				 u1_pel > USB3_LPM_MAX_U1_SEL_PEL)) ||
> +			(state == USB3_LPM_U2 &&
> +			 (u2_sel > USB3_LPM_MAX_U2_SEL_PEL ||
> +			  u2_pel > USB3_LPM_MAX_U2_SEL_PEL))) {
> +		dev_dbg(&udev->dev, "Device-initiated %s disabled due "
> +				"to long SEL %llu ms or PEL %llu ms\n",
> +				usb3_lpm_names[state], u1_sel, u1_pel);
> +		return -EINVAL;
> +	}
> +
> +	/*
> +	 * If we're enabling device-initiated LPM for one link state,
> +	 * but the other link state has a too high SEL or PEL value,
> +	 * just set those values to the max in the Set SEL request.
> +	 */
> +	if (u1_sel > USB3_LPM_MAX_U1_SEL_PEL)
> +		u1_sel = USB3_LPM_MAX_U1_SEL_PEL;
> +
> +	if (u1_pel > USB3_LPM_MAX_U1_SEL_PEL)
> +		u1_pel = USB3_LPM_MAX_U1_SEL_PEL;
> +
> +	if (u2_sel > USB3_LPM_MAX_U2_SEL_PEL)
> +		u2_sel = USB3_LPM_MAX_U2_SEL_PEL;
> +
> +	if (u2_pel > USB3_LPM_MAX_U2_SEL_PEL)
> +		u2_pel = USB3_LPM_MAX_U2_SEL_PEL;
> +
> +	/*
> +	 * usb_enable_lpm() can be called as part of a failed device reset,
> +	 * which may be initiated by an error path of a mass storage driver.
> +	 * Therefore, use GFP_NOIO.
> +	 */
> +	sel_values = kmalloc(sizeof *(sel_values), GFP_NOIO);
> +	if (!sel_values)
> +		return -ENOMEM;
> +
> +	sel_values->u1_sel = u1_sel;
> +	sel_values->u1_pel = u1_pel;
> +	sel_values->u2_sel = cpu_to_le16(u2_sel);
> +	sel_values->u2_pel = cpu_to_le16(u2_pel);
> +
> +	return usb_control_msg(udev, usb_sndctrlpipe(udev, 0),
> +			USB_REQ_SET_SEL,
> +			USB_RECIP_DEVICE,
> +			0, 0,
> +			sel_values, sizeof *(sel_values),
> +			USB_CTRL_SET_TIMEOUT);

sel_values is allocated but not freed?

Thanks,
Andiry

> +}
> +
> +/*
> + * Enable or disable device-initiated U1 or U2 transitions.
> + */
> +static int usb_set_device_initiated_lpm(struct usb_device *udev,
> +		enum usb3_link_state state, bool enable)
> +{
> +	int ret;
> +	int feature;
> +
> +	switch (state) {
> +	case USB3_LPM_U1:
> +		feature = USB_DEVICE_U1_ENABLE;
> +		break;
> +	case USB3_LPM_U2:
> +		feature = USB_DEVICE_U2_ENABLE;
> +		break;
> +	default:
> +		dev_warn(&udev->dev, "%s: Can't %s non-U1 or U2 state.\n",
> +				__func__, enable ? "enable" : "disable");
> +		return -EINVAL;
> +	}
> +
> +	if (udev->state != USB_STATE_CONFIGURED) {
> +		dev_dbg(&udev->dev, "%s: Can't %s %s state "
> +				"for unconfigured device.\n",
> +				__func__, enable ? "enable" : "disable",
> +				usb3_lpm_names[state]);
> +		return 0;
> +	}
> +
> +	if (enable) {
> +		/*
> +		 * First, let the device know about the exit latencies
> +		 * associated with the link state we're about to enable.
> +		 */
> +		ret = usb_req_set_sel(udev, state);
> +		if (ret < 0) {
> +			dev_warn(&udev->dev, "Set SEL for device-initiated "
> +					"%s failed.\n", usb3_lpm_names[state]);
> +			return -EBUSY;
> +		}
> +		/*
> +		 * Now send the control transfer to enable device-initiated LPM
> +		 * for either U1 or U2.
> +		 */
> +		ret = usb_control_msg(udev, usb_sndctrlpipe(udev, 0),
> +				USB_REQ_SET_FEATURE,
> +				USB_RECIP_DEVICE,
> +				feature,
> +				0, NULL, 0,
> +				USB_CTRL_SET_TIMEOUT);
> +	} else {
> +		ret = usb_control_msg(udev, usb_sndctrlpipe(udev, 0),
> +				USB_REQ_CLEAR_FEATURE,
> +				USB_RECIP_DEVICE,
> +				feature,
> +				0, NULL, 0,
> +				USB_CTRL_SET_TIMEOUT);
> +	}
> +	if (ret < 0) {
> +		dev_warn(&udev->dev, "%s of device-initiated %s failed.\n",
> +				enable ? "Enable" : "Disable",
> +				usb3_lpm_names[state]);
> +		return -EBUSY;
> +	}
> +	return 0;
> +}
> +
> +static int usb_set_lpm_timeout(struct usb_device *udev,
> +		enum usb3_link_state state, int timeout)
> +{
> +	int ret;
> +	int feature;
> +
> +	switch (state) {
> +	case USB3_LPM_U1:
> +		feature = USB_PORT_FEAT_U1_TIMEOUT;
> +		break;
> +	case USB3_LPM_U2:
> +		feature = USB_PORT_FEAT_U2_TIMEOUT;
> +		break;
> +	default:
> +		dev_warn(&udev->dev, "%s: Can't set timeout for non-U1 or U2 state.\n",
> +				__func__);
> +		return -EINVAL;
> +	}
> +
> +	if (state == USB3_LPM_U1 && timeout > USB3_LPM_U1_MAX_TIMEOUT &&
> +			timeout != USB3_LPM_DEVICE_INITIATED) {
> +		dev_warn(&udev->dev, "Failed to set %s timeout to 0x%x, "
> +				"which is a reserved value.\n",
> +				usb3_lpm_names[state], timeout);
> +		return -EINVAL;
> +	}
> +
> +	ret = set_port_feature(udev->parent,
> +			USB_PORT_LPM_TIMEOUT(timeout) | udev->portnum,
> +			feature);
> +	if (ret < 0) {
> +		dev_warn(&udev->dev, "Failed to set %s timeout to 0x%x,"
> +				"error code %i\n", usb3_lpm_names[state],
> +				timeout, ret);
> +		return -EBUSY;
> +	}
> +	if (state == USB3_LPM_U1)
> +		udev->u1_params.timeout = timeout;
> +	else
> +		udev->u2_params.timeout = timeout;
> +	return 0;
> +}
> +
> +/*
> + * Enable the hub-initiated U1/U2 idle timeouts, and enable device-initiated
> + * U1/U2 entry.
> + *
> + * We will attempt to enable U1 or U2, but there are no guarantees that the
> + * control transfers to set the hub timeout or enable device-initiated U1/U2
> + * will be successful.
> + *
> + * If we cannot set the parent hub U1/U2 timeout, we attempt to let the xHCI
> + * driver know about it.  If that call fails, it should be harmless, and just
> + * take up more slightly more bus bandwidth for unnecessary U1/U2 exit latency.
> + */
> +static void usb_enable_link_state(struct usb_hcd *hcd, struct usb_device *udev,
> +		enum usb3_link_state state)
> +{
> +	int timeout;
> +
> +	/* We allow the host controller to set the U1/U2 timeout internally
> +	 * first, so that it can change its schedule to account for the
> +	 * additional latency to send data to a device in a lower power
> +	 * link state.
> +	 */
> +	timeout = hcd->driver->enable_usb3_lpm_timeout(hcd, udev, state);
> +
> +	/* xHCI host controller doesn't want to enable this LPM state. */
> +	if (timeout == 0)
> +		return;
> +
> +	if (timeout < 0) {
> +		dev_warn(&udev->dev, "Could not enable %s link state, "
> +				"xHCI error %i.\n", usb3_lpm_names[state],
> +				timeout);
> +		return;
> +	}
> +
> +	if (usb_set_lpm_timeout(udev, state, timeout))
> +		/* If we can't set the parent hub U1/U2 timeout,
> +		 * device-initiated LPM won't be allowed either, so let the xHCI
> +		 * host know that this link state won't be enabled.
> +		 */
> +		hcd->driver->disable_usb3_lpm_timeout(hcd, udev, state);
> +
> +	/* Only a configured device will accept the Set Feature U1/U2_ENABLE */
> +	else if (udev->actconfig)
> +		usb_set_device_initiated_lpm(udev, state, true);
> +
> +}
> +
> +/*
> + * Disable the hub-initiated U1/U2 idle timeouts, and disable device-initiated
> + * U1/U2 entry.
> + *
> + * If this function returns -EBUSY, the parent hub will still allow U1/U2 entry.
> + * If zero is returned, the parent will not allow the link to go into U1/U2.
> + *
> + * If zero is returned, device-initiated U1/U2 entry may still be enabled, but
> + * it won't have an effect on the bus link state because the parent hub will
> + * still disallow device-initiated U1/U2 entry.
> + *
> + * If zero is returned, the xHCI host controller may still think U1/U2 entry is
> + * possible.  The result will be slightly more bus bandwidth will be taken up
> + * (to account for U1/U2 exit latency), but it should be harmless.
> + */
> +static int usb_disable_link_state(struct usb_hcd *hcd, struct usb_device *udev,
> +		enum usb3_link_state state)
> +{
> +	int feature;
> +
> +	switch (state) {
> +	case USB3_LPM_U1:
> +		feature = USB_PORT_FEAT_U1_TIMEOUT;
> +		break;
> +	case USB3_LPM_U2:
> +		feature = USB_PORT_FEAT_U2_TIMEOUT;
> +		break;
> +	default:
> +		dev_warn(&udev->dev, "%s: Can't disable non-U1 or U2 state.\n",
> +				__func__);
> +		return -EINVAL;
> +	}
> +
> +	if (usb_set_lpm_timeout(udev, state, 0))
> +		return -EBUSY;
> +
> +	usb_set_device_initiated_lpm(udev, state, false);
> +
> +	if (hcd->driver->disable_usb3_lpm_timeout(hcd, udev, state))
> +		dev_warn(&udev->dev, "Could not disable xHCI %s timeout, "
> +				"bus schedule bandwidth may be impacted.\n",
> +				usb3_lpm_names[state]);
> +	return 0;
> +}
> +
> +/*
> + * Disable hub-initiated and device-initiated U1 and U2 entry.
> + * Caller must own the bandwidth_mutex.
> + */
> +int usb_disable_lpm(struct usb_device *udev)
> +{
> +	struct usb_hcd *hcd;
> +
> +	if (!udev || !udev->parent ||
> +			udev->speed != USB_SPEED_SUPER ||
> +			!udev->lpm_capable)
> +		return 0;
> +
> +	hcd = bus_to_hcd(udev->bus);
> +	if (!hcd || !hcd->driver->disable_usb3_lpm_timeout)
> +		return 0;
> +
> +	udev->lpm_disable_count++;
> +	if ((udev->u1_params.timeout == 0 && udev->u1_params.timeout == 0))
> +		return 0;
> +
> +	/* If LPM is enabled, attempt to disable it. */
> +	if (usb_disable_link_state(hcd, udev, USB3_LPM_U1))
> +		return -EBUSY;
> +	if (usb_disable_link_state(hcd, udev, USB3_LPM_U2))
> +		return -EBUSY;
> +
> +	return 0;
> +}
> +EXPORT_SYMBOL_GPL(usb_disable_lpm);
> +
> +/*
> + * Attempt to enable device-initiated and hub-initiated U1 and U2 entry.  The
> + * xHCI host policy may prevent U1 or U2 from being enabled.
> + *
> + * Other callers may have disabled link PM, so U1 and U2 entry will be disabled
> + * until the lpm_disable_count drops to zero.  Caller must own the
> + * bandwidth_mutex.
> + */
> +void usb_enable_lpm(struct usb_device *udev)
> +{
> +	struct usb_hcd *hcd;
> +
> +	if (!udev || !udev->parent ||
> +			udev->speed != USB_SPEED_SUPER ||
> +			!udev->lpm_capable)
> +		return;
> +
> +	udev->lpm_disable_count--;
> +	hcd = bus_to_hcd(udev->bus);
> +	/* Double check that we can both enable and disable LPM.
> +	 * Device must be configured to accept set feature U1/U2 timeout.
> +	 */
> +	if (!hcd || !hcd->driver->enable_usb3_lpm_timeout ||
> +			!hcd->driver->disable_usb3_lpm_timeout)
> +		return;
> +
> +	if (udev->lpm_disable_count > 0)
> +		return;
> +
> +	usb_enable_link_state(hcd, udev, USB3_LPM_U1);
> +	usb_enable_link_state(hcd, udev, USB3_LPM_U2);
> +}
> +EXPORT_SYMBOL_GPL(usb_enable_lpm);
> +
>  #else	/* CONFIG_PM */
>  
>  #define hub_suspend		NULL
>  #define hub_resume		NULL
>  #define hub_reset_resume	NULL
> +
> +int usb_disable_lpm(struct usb_device *udev)
> +{
> +	return 0;
> +}
> +
> +void usb_enable_lpm(struct usb_device *udev) { }
>  #endif
>  
>  
> diff --git a/include/linux/usb.h b/include/linux/usb.h
> index 998c276..6ef80e8 100644
> --- a/include/linux/usb.h
> +++ b/include/linux/usb.h
> @@ -409,6 +409,12 @@ struct usb3_lpm_parameters {
>  	 * it will get data.
>  	 */
>  	unsigned long long sel;
> +	/*
> +	 * The idle timeout value that is currently programmed into the parent
> +	 * hub for this device.  When the timer counts to zero, the parent hub
> +	 * will initiate an LPM transition to either U1 or U2.
> +	 */
> +	int timeout;
>  };
>  
>  /**
> @@ -542,6 +548,7 @@ struct usb_device {
>  	enum usb_device_removable removable;
>  	struct usb3_lpm_parameters u1_params;
>  	struct usb3_lpm_parameters u2_params;
> +	unsigned lpm_disable_count;
>  };
>  #define	to_usb_device(d) container_of(d, struct usb_device, dev)
>  
> @@ -576,6 +583,8 @@ extern int usb_autopm_get_interface_async(struct usb_interface *intf);
>  extern void usb_autopm_put_interface_async(struct usb_interface *intf);
>  extern void usb_autopm_get_interface_no_resume(struct usb_interface *intf);
>  extern void usb_autopm_put_interface_no_suspend(struct usb_interface *intf);
> +extern int usb_disable_lpm(struct usb_device *udev);
> +extern void usb_enable_lpm(struct usb_device *udev);
>  
>  static inline void usb_mark_last_busy(struct usb_device *udev)
>  {
> diff --git a/include/linux/usb/ch11.h b/include/linux/usb/ch11.h
> index f1d26b6..b6c2863 100644
> --- a/include/linux/usb/ch11.h
> +++ b/include/linux/usb/ch11.h
> @@ -76,6 +76,8 @@
>  #define USB_PORT_FEAT_C_BH_PORT_RESET		29
>  #define USB_PORT_FEAT_FORCE_LINKPM_ACCEPT	30
>  
> +#define USB_PORT_LPM_TIMEOUT(p)			(((p) & 0xff) << 8)
> +
>  /* USB 3.0 hub remote wake mask bits, see table 10-14 */
>  #define USB_PORT_FEAT_REMOTE_WAKE_CONNECT	(1 << 8)
>  #define USB_PORT_FEAT_REMOTE_WAKE_DISCONNECT	(1 << 9)
> diff --git a/include/linux/usb/ch9.h b/include/linux/usb/ch9.h
> index e785d85..43bce9d 100644
> --- a/include/linux/usb/ch9.h
> +++ b/include/linux/usb/ch9.h
> @@ -935,6 +935,51 @@ enum usb_device_state {
>  	 */
>  };
>  
> +enum usb3_link_state {
> +	USB3_LPM_U0 = 0,
> +	USB3_LPM_U1,
> +	USB3_LPM_U2,
> +	USB3_LPM_U3
> +};
> +
> +/*
> + * A U1 timeout of 0x0 means the parent hub will reject any transitions to U1.
> + * 0xff means the parent hub will accept transitions to U1, but will not
> + * initiate a transition.
> + *
> + * A U1 timeout of 0x1 to 0x7F also causes the hub to initiate a transition to
> + * U1 after that many microseconds.  Timeouts of 0x80 to 0xFE are reserved
> + * values.
> + *
> + * A U2 timeout of 0x0 means the parent hub will reject any transitions to U2.
> + * 0xff means the parent hub will accept transitions to U2, but will not
> + * initiate a transition.
> + *
> + * A U2 timeout of 0x1 to 0xFE also causes the hub to initiate a transition to
> + * U2 after N*256 microseconds.  Therefore a U2 timeout value of 0x1 means a U2
> + * idle timer of 256 microseconds, 0x2 means 512 microseconds, 0xFE means
> + * 65.024ms.
> + */
> +#define USB3_LPM_DISABLED		0x0
> +#define USB3_LPM_U1_MAX_TIMEOUT		0x7F
> +#define USB3_LPM_U2_MAX_TIMEOUT		0xFE
> +#define USB3_LPM_DEVICE_INITIATED	0xFF
> +
> +struct usb_set_sel_req {
> +	__u8	u1_sel;
> +	__u8	u1_pel;
> +	__le16	u2_sel;
> +	__le16	u2_pel;
> +} __attribute__ ((packed));
> +
> +/*
> + * The Set System Exit Latency control transfer provides one byte each for
> + * U1 SEL and U1 PEL, so the max exit latency is 0xFF.  U2 SEL and U2 PEL each
> + * are two bytes long.
> + */
> +#define USB3_LPM_MAX_U1_SEL_PEL		0xFF
> +#define USB3_LPM_MAX_U2_SEL_PEL		0xFFFF
> +
>  /*-------------------------------------------------------------------------*/
>  
>  /*
> diff --git a/include/linux/usb/hcd.h b/include/linux/usb/hcd.h
> index bbb9464..7f855d5 100644
> --- a/include/linux/usb/hcd.h
> +++ b/include/linux/usb/hcd.h
> @@ -344,6 +344,15 @@ struct hc_driver {
>  		 */
>  	int	(*update_device)(struct usb_hcd *, struct usb_device *);
>  	int	(*set_usb2_hw_lpm)(struct usb_hcd *, struct usb_device *, int);
> +	/* USB 3.0 Link Power Management */
> +		/* Returns the USB3 hub-encoded value for the U1/U2 timeout. */
> +	int	(*enable_usb3_lpm_timeout)(struct usb_hcd *,
> +			struct usb_device *, enum usb3_link_state state);
> +		/* The xHCI host controller can still fail the command to
> +		 * disable the LPM timeouts, so this can return an error code.
> +		 */
> +	int	(*disable_usb3_lpm_timeout)(struct usb_hcd *,
> +			struct usb_device *, enum usb3_link_state state);
>  };
>  
>  extern int usb_hcd_link_urb_to_ep(struct usb_hcd *hcd, struct urb *urb);


--
To unsubscribe from this list: send the line "unsubscribe linux-usb" in
the body of a message to majordomo@xxxxxxxxxxxxxxx
More majordomo info at  http://vger.kernel.org/majordomo-info.html


B and H Foto and Electronics Corp.

[Linux Media]     [Video for Linux]     [Linux Input]     [Linux Audio Users]     [Photo]     [Yosemite News]    [Yosemite Photos]    [Free Online Dating]     [Linux Kernel]     [Linux SCSI]     [Old Linux USB Devel Archive]     [More Archives]

Add to Google Powered by Linux