Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit 564fbee9 authored by Linus Torvalds's avatar Linus Torvalds
Browse files
Pull USB fixes from Greg KH:
 "Here are some USB fixes for 3.16-rc2 that resolve some reported
  issues.  All of these have been in linux-next for a while with no
  problems"

* tag 'usb-3.16-rc2' of git://git.kernel.org/pub/scm/linux/kernel/git/gregkh/usb:
  USB: usbtest: add a timeout for scatter-gather tests
  USB: EHCI: avoid BIOS handover on the HASEE E200
  usb: fix hub-port pm_runtime_enable() vs runtime pm transitions
  usb: quiet peer failure warning, disable poweroff
  usb: improve "not suspended yet" message in hub_suspend()
  xhci: Fix sleeping with IRQs disabled in xhci_stop_device()
  usb: fix ->update_hub_device() vs hdev->maxchild
parents 3c8fb504 32b36eea
Loading
Loading
Loading
Loading
+20 −13
Original line number Original line Diff line number Diff line
@@ -1526,18 +1526,6 @@ static int hub_configure(struct usb_hub *hub,
		dev_dbg(hub_dev, "%umA bus power budget for each child\n",
		dev_dbg(hub_dev, "%umA bus power budget for each child\n",
				hub->mA_per_port);
				hub->mA_per_port);


	/* Update the HCD's internal representation of this hub before khubd
	 * starts getting port status changes for devices under the hub.
	 */
	if (hcd->driver->update_hub_device) {
		ret = hcd->driver->update_hub_device(hcd, hdev,
				&hub->tt, GFP_KERNEL);
		if (ret < 0) {
			message = "can't update HCD hub info";
			goto fail;
		}
	}

	ret = hub_hub_status(hub, &hubstatus, &hubchange);
	ret = hub_hub_status(hub, &hubstatus, &hubchange);
	if (ret < 0) {
	if (ret < 0) {
		message = "can't get hub status";
		message = "can't get hub status";
@@ -1589,10 +1577,28 @@ static int hub_configure(struct usb_hub *hub,
		}
		}
	}
	}
	hdev->maxchild = i;
	hdev->maxchild = i;
	for (i = 0; i < hdev->maxchild; i++) {
		struct usb_port *port_dev = hub->ports[i];

		pm_runtime_put(&port_dev->dev);
	}

	mutex_unlock(&usb_port_peer_mutex);
	mutex_unlock(&usb_port_peer_mutex);
	if (ret < 0)
	if (ret < 0)
		goto fail;
		goto fail;


	/* Update the HCD's internal representation of this hub before khubd
	 * starts getting port status changes for devices under the hub.
	 */
	if (hcd->driver->update_hub_device) {
		ret = hcd->driver->update_hub_device(hcd, hdev,
				&hub->tt, GFP_KERNEL);
		if (ret < 0) {
			message = "can't update HCD hub info";
			goto fail;
		}
	}

	usb_hub_adjust_deviceremovable(hdev, hub->descriptor);
	usb_hub_adjust_deviceremovable(hdev, hub->descriptor);


	hub_activate(hub, HUB_INIT);
	hub_activate(hub, HUB_INIT);
@@ -3458,7 +3464,8 @@ static int hub_suspend(struct usb_interface *intf, pm_message_t msg)
		struct usb_device *udev = port_dev->child;
		struct usb_device *udev = port_dev->child;


		if (udev && udev->can_submit) {
		if (udev && udev->can_submit) {
			dev_warn(&port_dev->dev, "not suspended yet\n");
			dev_warn(&port_dev->dev, "device %s not suspended yet\n",
					dev_name(&udev->dev));
			if (PMSG_IS_AUTO(msg))
			if (PMSG_IS_AUTO(msg))
				return -EBUSY;
				return -EBUSY;
		}
		}
+2 −0
Original line number Original line Diff line number Diff line
@@ -84,6 +84,7 @@ struct usb_hub {
 * @dev: generic device interface
 * @dev: generic device interface
 * @port_owner: port's owner
 * @port_owner: port's owner
 * @peer: related usb2 and usb3 ports (share the same connector)
 * @peer: related usb2 and usb3 ports (share the same connector)
 * @req: default pm qos request for hubs without port power control
 * @connect_type: port's connect type
 * @connect_type: port's connect type
 * @location: opaque representation of platform connector location
 * @location: opaque representation of platform connector location
 * @status_lock: synchronize port_event() vs usb_port_{suspend|resume}
 * @status_lock: synchronize port_event() vs usb_port_{suspend|resume}
@@ -95,6 +96,7 @@ struct usb_port {
	struct device dev;
	struct device dev;
	struct usb_dev_state *port_owner;
	struct usb_dev_state *port_owner;
	struct usb_port *peer;
	struct usb_port *peer;
	struct dev_pm_qos_request *req;
	enum usb_port_connect_type connect_type;
	enum usb_port_connect_type connect_type;
	usb_port_location_t location;
	usb_port_location_t location;
	struct mutex status_lock;
	struct mutex status_lock;
+65 −24
Original line number Original line Diff line number Diff line
@@ -21,6 +21,8 @@


#include "hub.h"
#include "hub.h"


static int usb_port_block_power_off;

static const struct attribute_group *port_dev_group[];
static const struct attribute_group *port_dev_group[];


static ssize_t connect_type_show(struct device *dev,
static ssize_t connect_type_show(struct device *dev,
@@ -66,6 +68,7 @@ static void usb_port_device_release(struct device *dev)
{
{
	struct usb_port *port_dev = to_usb_port(dev);
	struct usb_port *port_dev = to_usb_port(dev);


	kfree(port_dev->req);
	kfree(port_dev);
	kfree(port_dev);
}
}


@@ -142,6 +145,9 @@ static int usb_port_runtime_suspend(struct device *dev)
			== PM_QOS_FLAGS_ALL)
			== PM_QOS_FLAGS_ALL)
		return -EAGAIN;
		return -EAGAIN;


	if (usb_port_block_power_off)
		return -EBUSY;

	usb_autopm_get_interface(intf);
	usb_autopm_get_interface(intf);
	retval = usb_hub_set_port_power(hdev, hub, port1, false);
	retval = usb_hub_set_port_power(hdev, hub, port1, false);
	usb_clear_port_feature(hdev, port1, USB_PORT_FEAT_C_CONNECTION);
	usb_clear_port_feature(hdev, port1, USB_PORT_FEAT_C_CONNECTION);
@@ -190,11 +196,19 @@ static int link_peers(struct usb_port *left, struct usb_port *right)
	if (left->peer || right->peer) {
	if (left->peer || right->peer) {
		struct usb_port *lpeer = left->peer;
		struct usb_port *lpeer = left->peer;
		struct usb_port *rpeer = right->peer;
		struct usb_port *rpeer = right->peer;

		char *method;
		WARN(1, "failed to peer %s and %s (%s -> %p) (%s -> %p)\n",

			dev_name(&left->dev), dev_name(&right->dev),
		if (left->location && left->location == right->location)
			dev_name(&left->dev), lpeer,
			method = "location";
			dev_name(&right->dev), rpeer);
		else
			method = "default";

		pr_warn("usb: failed to peer %s and %s by %s (%s:%s) (%s:%s)\n",
			dev_name(&left->dev), dev_name(&right->dev), method,
			dev_name(&left->dev),
			lpeer ? dev_name(&lpeer->dev) : "none",
			dev_name(&right->dev),
			rpeer ? dev_name(&rpeer->dev) : "none");
		return -EBUSY;
		return -EBUSY;
	}
	}


@@ -251,6 +265,7 @@ static void link_peers_report(struct usb_port *left, struct usb_port *right)
		dev_warn(&left->dev, "failed to peer to %s (%d)\n",
		dev_warn(&left->dev, "failed to peer to %s (%d)\n",
				dev_name(&right->dev), rc);
				dev_name(&right->dev), rc);
		pr_warn_once("usb: port power management may be unreliable\n");
		pr_warn_once("usb: port power management may be unreliable\n");
		usb_port_block_power_off = 1;
	}
	}
}
}


@@ -386,9 +401,13 @@ int usb_hub_create_port_device(struct usb_hub *hub, int port1)
	int retval;
	int retval;


	port_dev = kzalloc(sizeof(*port_dev), GFP_KERNEL);
	port_dev = kzalloc(sizeof(*port_dev), GFP_KERNEL);
	if (!port_dev) {
	if (!port_dev)
		retval = -ENOMEM;
		return -ENOMEM;
		goto exit;

	port_dev->req = kzalloc(sizeof(*(port_dev->req)), GFP_KERNEL);
	if (!port_dev->req) {
		kfree(port_dev);
		return -ENOMEM;
	}
	}


	hub->ports[port1 - 1] = port_dev;
	hub->ports[port1 - 1] = port_dev;
@@ -404,31 +423,53 @@ int usb_hub_create_port_device(struct usb_hub *hub, int port1)
			port1);
			port1);
	mutex_init(&port_dev->status_lock);
	mutex_init(&port_dev->status_lock);
	retval = device_register(&port_dev->dev);
	retval = device_register(&port_dev->dev);
	if (retval)
	if (retval) {
		goto error_register;
		put_device(&port_dev->dev);
		return retval;
	}

	/* Set default policy of port-poweroff disabled. */
	retval = dev_pm_qos_add_request(&port_dev->dev, port_dev->req,
			DEV_PM_QOS_FLAGS, PM_QOS_FLAG_NO_POWER_OFF);
	if (retval < 0) {
		device_unregister(&port_dev->dev);
		return retval;
	}


	find_and_link_peer(hub, port1);
	find_and_link_peer(hub, port1);


	/*
	 * Enable runtime pm and hold a refernce that hub_configure()
	 * will drop once the PM_QOS_NO_POWER_OFF flag state has been set
	 * and the hub has been fully registered (hdev->maxchild set).
	 */
	pm_runtime_set_active(&port_dev->dev);
	pm_runtime_set_active(&port_dev->dev);
	pm_runtime_get_noresume(&port_dev->dev);
	pm_runtime_enable(&port_dev->dev);
	device_enable_async_suspend(&port_dev->dev);


	/*
	/*
	 * Do not enable port runtime pm if the hub does not support
	 * Keep hidden the ability to enable port-poweroff if the hub
	 * power switching.  Also, userspace must have final say of
	 * does not support power switching.
	 * whether a port is permitted to power-off.  Do not enable
	 * runtime pm if we fail to expose pm_qos_no_power_off.
	 */
	 */
	if (hub_is_port_power_switchable(hub)
	if (!hub_is_port_power_switchable(hub))
			&& dev_pm_qos_expose_flags(&port_dev->dev,
		return 0;
			PM_QOS_FLAG_NO_POWER_OFF) == 0)
		pm_runtime_enable(&port_dev->dev);


	device_enable_async_suspend(&port_dev->dev);
	/* Attempt to let userspace take over the policy. */
	retval = dev_pm_qos_expose_flags(&port_dev->dev,
			PM_QOS_FLAG_NO_POWER_OFF);
	if (retval < 0) {
		dev_warn(&port_dev->dev, "failed to expose pm_qos_no_poweroff\n");
		return 0;
		return 0;
	}


error_register:
	/* Userspace owns the policy, drop the kernel 'no_poweroff' request. */
	put_device(&port_dev->dev);
	retval = dev_pm_qos_remove_request(port_dev->req);
exit:
	if (retval >= 0) {
	return retval;
		kfree(port_dev->req);
		port_dev->req = NULL;
	}
	return 0;
}
}


void usb_hub_remove_port_device(struct usb_hub *hub, int port1)
void usb_hub_remove_port_device(struct usb_hub *hub, int port1)
+16 −3
Original line number Original line Diff line number Diff line
@@ -656,6 +656,14 @@ static const struct dmi_system_id ehci_dmi_nohandoff_table[] = {
			DMI_MATCH(DMI_BIOS_VERSION, "Lucid-"),
			DMI_MATCH(DMI_BIOS_VERSION, "Lucid-"),
		},
		},
	},
	},
	{
		/* HASEE E200 */
		.matches = {
			DMI_MATCH(DMI_BOARD_VENDOR, "HASEE"),
			DMI_MATCH(DMI_BOARD_NAME, "E210"),
			DMI_MATCH(DMI_BIOS_VERSION, "6.00"),
		},
	},
	{ }
	{ }
};
};


@@ -665,9 +673,14 @@ static void ehci_bios_handoff(struct pci_dev *pdev,
{
{
	int try_handoff = 1, tried_handoff = 0;
	int try_handoff = 1, tried_handoff = 0;


	/* The Pegatron Lucid tablet sporadically waits for 98 seconds trying
	/*
	 * the handoff on its unused controller.  Skip it. */
	 * The Pegatron Lucid tablet sporadically waits for 98 seconds trying
	if (pdev->vendor == 0x8086 && pdev->device == 0x283a) {
	 * the handoff on its unused controller.  Skip it.
	 *
	 * The HASEE E200 hangs when the semaphore is set (bugzilla #77021).
	 */
	if (pdev->vendor == 0x8086 && (pdev->device == 0x283a ||
			pdev->device == 0x27cc)) {
		if (dmi_check_system(ehci_dmi_nohandoff_table))
		if (dmi_check_system(ehci_dmi_nohandoff_table))
			try_handoff = 0;
			try_handoff = 0;
	}
	}
+1 −1
Original line number Original line Diff line number Diff line
@@ -287,7 +287,7 @@ static int xhci_stop_device(struct xhci_hcd *xhci, int slot_id, int suspend)
		if (virt_dev->eps[i].ring && virt_dev->eps[i].ring->dequeue) {
		if (virt_dev->eps[i].ring && virt_dev->eps[i].ring->dequeue) {
			struct xhci_command *command;
			struct xhci_command *command;
			command = xhci_alloc_command(xhci, false, false,
			command = xhci_alloc_command(xhci, false, false,
						     GFP_NOIO);
						     GFP_NOWAIT);
			if (!command) {
			if (!command) {
				spin_unlock_irqrestore(&xhci->lock, flags);
				spin_unlock_irqrestore(&xhci->lock, flags);
				xhci_free_command(xhci, cmd);
				xhci_free_command(xhci, cmd);
Loading