diff options
| author | Ingo Molnar <mingo@kernel.org> | 2024-04-09 09:28:41 +0200 | 
|---|---|---|
| committer | Ingo Molnar <mingo@kernel.org> | 2024-04-09 09:28:41 +0200 | 
| commit | 0e6ebfd163645d300fdf4abedd1718195ad293bc (patch) | |
| tree | 510614aa8a43af846e27c7581adda801eaf03fc6 /drivers/usb/core | |
| parent | 186d7ef52c1f0c41450dedbdf6d6325d0a84e4c5 (diff) | |
| parent | fec50db7033ea478773b159e0e2efb135270e3b7 (diff) | |
Merge tag 'v6.9-rc3' into x86/cpu, to pick up fixes
Signed-off-by: Ingo Molnar <mingo@kernel.org>
Diffstat (limited to 'drivers/usb/core')
| -rw-r--r-- | drivers/usb/core/Kconfig | 27 | ||||
| -rw-r--r-- | drivers/usb/core/driver.c | 8 | ||||
| -rw-r--r-- | drivers/usb/core/endpoint.c | 2 | ||||
| -rw-r--r-- | drivers/usb/core/hcd.c | 20 | ||||
| -rw-r--r-- | drivers/usb/core/hub.c | 52 | ||||
| -rw-r--r-- | drivers/usb/core/hub.h | 2 | ||||
| -rw-r--r-- | drivers/usb/core/message.c | 7 | ||||
| -rw-r--r-- | drivers/usb/core/of.c | 71 | ||||
| -rw-r--r-- | drivers/usb/core/phy.c | 120 | ||||
| -rw-r--r-- | drivers/usb/core/phy.h | 3 | ||||
| -rw-r--r-- | drivers/usb/core/port.c | 42 | ||||
| -rw-r--r-- | drivers/usb/core/sysfs.c | 119 | ||||
| -rw-r--r-- | drivers/usb/core/usb-acpi.c | 46 | ||||
| -rw-r--r-- | drivers/usb/core/usb.c | 2 | ||||
| -rw-r--r-- | drivers/usb/core/usb.h | 8 | 
15 files changed, 442 insertions, 87 deletions
diff --git a/drivers/usb/core/Kconfig b/drivers/usb/core/Kconfig index 351ede4b5de2..58e3ca7e4793 100644 --- a/drivers/usb/core/Kconfig +++ b/drivers/usb/core/Kconfig @@ -116,3 +116,30 @@ config USB_AUTOSUSPEND_DELAY  	  The default value Linux has always had is 2 seconds.  Change  	  this value if you want a different delay and cannot modify  	  the command line or module parameter. + +config USB_DEFAULT_AUTHORIZATION_MODE +	int "Default authorization mode for USB devices" +	range 0 2 +	default 1 +	depends on USB +	help +	  Select the default USB device authorization mode. Can be overridden +	  with usbcore.authorized_default command line or module parameter. + +	  This option allows you to choose whether USB devices that are +	  connected to the system can be used by default, or if they are +	  locked down. + +	  With value 0 all connected USB devices with the exception of root +	  hub require user space authorization before they can be used. + +	  With value 1 (default) no user space authorization is required to +	  use connected USB devices. + +	  With value 2 all connected USB devices with exception of internal +	  USB devices require user space authorization before they can be +	  used. Note that in this mode the differentiation between internal +	  and external USB devices relies on ACPI, and on systems without +	  ACPI selecting value 2 is analogous to selecting value 0. + +	  If unsure, keep the default value. diff --git a/drivers/usb/core/driver.c b/drivers/usb/core/driver.c index e01b1913d02b..e02ba15f6e34 100644 --- a/drivers/usb/core/driver.c +++ b/drivers/usb/core/driver.c @@ -1710,9 +1710,7 @@ int usb_autoresume_device(struct usb_device *udev)  {  	int	status; -	status = pm_runtime_get_sync(&udev->dev); -	if (status < 0) -		pm_runtime_put_sync(&udev->dev); +	status = pm_runtime_resume_and_get(&udev->dev);  	dev_vdbg(&udev->dev, "%s: cnt %d -> %d\n",  			__func__, atomic_read(&udev->dev.power.usage_count),  			status); @@ -1818,9 +1816,7 @@ int usb_autopm_get_interface(struct usb_interface *intf)  {  	int	status; -	status = pm_runtime_get_sync(&intf->dev); -	if (status < 0) -		pm_runtime_put_sync(&intf->dev); +	status = pm_runtime_resume_and_get(&intf->dev);  	dev_vdbg(&intf->dev, "%s: cnt %d -> %d\n",  			__func__, atomic_read(&intf->dev.power.usage_count),  			status); diff --git a/drivers/usb/core/endpoint.c b/drivers/usb/core/endpoint.c index a2530811cf7d..4b38b87a1343 100644 --- a/drivers/usb/core/endpoint.c +++ b/drivers/usb/core/endpoint.c @@ -141,7 +141,7 @@ static void ep_device_release(struct device *dev)  	kfree(ep_dev);  } -struct device_type usb_ep_device_type = { +const struct device_type usb_ep_device_type = {  	.name =		"usb_endpoint",  	.release = ep_device_release,  }; diff --git a/drivers/usb/core/hcd.c b/drivers/usb/core/hcd.c index edf74458474a..c0e005670d67 100644 --- a/drivers/usb/core/hcd.c +++ b/drivers/usb/core/hcd.c @@ -357,12 +357,10 @@ static const u8 ss_rh_config_descriptor[] = {  #define USB_AUTHORIZE_ALL	1  #define USB_AUTHORIZE_INTERNAL	2 -static int authorized_default = USB_AUTHORIZE_WIRED; +static int authorized_default = CONFIG_USB_DEFAULT_AUTHORIZATION_MODE;  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, 2 is authorized for internal devices, -1 is " -		"authorized (default, same as 1)"); +		"Default USB device authorization: 0 is not authorized, 1 is authorized (default), 2 is authorized for internal devices, -1 is authorized (same as 1)");  /*-------------------------------------------------------------------------*/  /** @@ -2795,10 +2793,16 @@ int usb_add_hcd(struct usb_hcd *hcd,  	struct usb_device *rhdev;  	struct usb_hcd *shared_hcd; -	if (!hcd->skip_phy_initialization && usb_hcd_is_primary_hcd(hcd)) { -		hcd->phy_roothub = usb_phy_roothub_alloc(hcd->self.sysdev); -		if (IS_ERR(hcd->phy_roothub)) -			return PTR_ERR(hcd->phy_roothub); +	if (!hcd->skip_phy_initialization) { +		if (usb_hcd_is_primary_hcd(hcd)) { +			hcd->phy_roothub = usb_phy_roothub_alloc(hcd->self.sysdev); +			if (IS_ERR(hcd->phy_roothub)) +				return PTR_ERR(hcd->phy_roothub); +		} else { +			hcd->phy_roothub = usb_phy_roothub_alloc_usb3_phy(hcd->self.sysdev); +			if (IS_ERR(hcd->phy_roothub)) +				return PTR_ERR(hcd->phy_roothub); +		}  		retval = usb_phy_roothub_init(hcd->phy_roothub);  		if (retval) diff --git a/drivers/usb/core/hub.c b/drivers/usb/core/hub.c index e38a4124f610..9446660e231b 100644 --- a/drivers/usb/core/hub.c +++ b/drivers/usb/core/hub.c @@ -37,6 +37,7 @@  #include <asm/byteorder.h>  #include "hub.h" +#include "phy.h"  #include "otg_productlist.h"  #define USB_VENDOR_GENESYS_LOGIC		0x05e3 @@ -129,7 +130,6 @@ EXPORT_SYMBOL_GPL(ehci_cf_port_reset_rwsem);  #define HUB_DEBOUNCE_STEP	  25  #define HUB_DEBOUNCE_STABLE	 100 -static void hub_release(struct kref *kref);  static int usb_reset_and_verify_device(struct usb_device *udev);  static int hub_port_disable(struct usb_hub *hub, int port1, int set_state);  static bool hub_port_warm_reset_required(struct usb_hub *hub, int port1, @@ -634,6 +634,34 @@ static int hub_ext_port_status(struct usb_hub *hub, int port1, int type,  		ret = 0;  	}  	mutex_unlock(&hub->status_mutex); + +	/* +	 * There is no need to lock status_mutex here, because status_mutex +	 * protects hub->status, and the phy driver only checks the port +	 * status without changing the status. +	 */ +	if (!ret) { +		struct usb_device *hdev = hub->hdev; + +		/* +		 * Only roothub will be notified of connection changes, +		 * since the USB PHY only cares about changes at the next +		 * level. +		 */ +		if (is_root_hub(hdev)) { +			struct usb_hcd *hcd = bus_to_hcd(hdev->bus); +			bool connect; +			bool connect_change; + +			connect_change = *change & USB_PORT_STAT_C_CONNECTION; +			connect = *status & USB_PORT_STAT_CONNECTION; +			if (connect_change && connect) +				usb_phy_roothub_notify_connect(hcd->phy_roothub, port1 - 1); +			else if (connect_change) +				usb_phy_roothub_notify_disconnect(hcd->phy_roothub, port1 - 1); +		} +	} +  	return ret;  } @@ -691,14 +719,14 @@ static void kick_hub_wq(struct usb_hub *hub)  	 */  	intf = to_usb_interface(hub->intfdev);  	usb_autopm_get_interface_no_resume(intf); -	kref_get(&hub->kref); +	hub_get(hub);  	if (queue_work(hub_wq, &hub->events))  		return;  	/* the work has already been scheduled */  	usb_autopm_put_interface_async(intf); -	kref_put(&hub->kref, hub_release); +	hub_put(hub);  }  void usb_kick_hub_wq(struct usb_device *hdev) @@ -1066,7 +1094,7 @@ static void hub_activate(struct usb_hub *hub, enum hub_activation_type type)  			goto init2;  		goto init3;  	} -	kref_get(&hub->kref); +	hub_get(hub);  	/* The superspeed hub except for root hub has to use Hub Depth  	 * value as an offset into the route string to locate the bits @@ -1314,7 +1342,7 @@ static void hub_activate(struct usb_hub *hub, enum hub_activation_type type)  		device_unlock(&hdev->dev);  	} -	kref_put(&hub->kref, hub_release); +	hub_put(hub);  }  /* Implement the continuations for the delays above */ @@ -1730,6 +1758,16 @@ static void hub_release(struct kref *kref)  	kfree(hub);  } +void hub_get(struct usb_hub *hub) +{ +	kref_get(&hub->kref); +} + +void hub_put(struct usb_hub *hub) +{ +	kref_put(&hub->kref, hub_release); +} +  static unsigned highspeed_hubs;  static void hub_disconnect(struct usb_interface *intf) @@ -1778,7 +1816,7 @@ static void hub_disconnect(struct usb_interface *intf)  	onboard_hub_destroy_pdevs(&hub->onboard_hub_devs); -	kref_put(&hub->kref, hub_release); +	hub_put(hub);  }  static bool hub_descriptor_is_sane(struct usb_host_interface *desc) @@ -5905,7 +5943,7 @@ out_hdev_lock:  	/* Balance the stuff in kick_hub_wq() and allow autosuspend */  	usb_autopm_put_interface(intf); -	kref_put(&hub->kref, hub_release); +	hub_put(hub);  	kcov_remote_stop();  } diff --git a/drivers/usb/core/hub.h b/drivers/usb/core/hub.h index 43ce21c96a51..183b69dc2955 100644 --- a/drivers/usb/core/hub.h +++ b/drivers/usb/core/hub.h @@ -129,6 +129,8 @@ extern void usb_hub_remove_port_device(struct usb_hub *hub,  extern int usb_hub_set_port_power(struct usb_device *hdev, struct usb_hub *hub,  		int port1, bool set);  extern struct usb_hub *usb_hub_to_struct_hub(struct usb_device *hdev); +extern void hub_get(struct usb_hub *hub); +extern void hub_put(struct usb_hub *hub);  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, diff --git a/drivers/usb/core/message.c b/drivers/usb/core/message.c index 077dfe48d01c..d2b2787be409 100644 --- a/drivers/usb/core/message.c +++ b/drivers/usb/core/message.c @@ -1198,6 +1198,8 @@ EXPORT_SYMBOL_GPL(usb_get_status);   * same status code used to report a true stall.   *   * This call is synchronous, and may not be used in an interrupt context. + * If a thread in your driver uses this call, make sure your disconnect() + * method can wait for it to complete.   *   * Return: Zero on success, or else the status code returned by the   * underlying usb_control_msg() call. @@ -1516,7 +1518,8 @@ void usb_enable_interface(struct usb_device *dev,   * This call is synchronous, and may not be used in an interrupt context.   * Also, drivers must not change altsettings while urbs are scheduled for   * endpoints in that interface; all such urbs must first be completed - * (perhaps forced by unlinking). + * (perhaps forced by unlinking). If a thread in your driver uses this call, + * make sure your disconnect() method can wait for it to complete.   *   * Return: Zero on success, or else the status code returned by the   * underlying usb_control_msg() call. @@ -1849,7 +1852,7 @@ static int usb_if_uevent(const struct device *dev, struct kobj_uevent_env *env)  	return 0;  } -struct device_type usb_if_device_type = { +const struct device_type usb_if_device_type = {  	.name =		"usb_interface",  	.release =	usb_release_interface,  	.uevent =	usb_if_uevent, diff --git a/drivers/usb/core/of.c b/drivers/usb/core/of.c index db4ccf9ce3d9..f1a499ee482c 100644 --- a/drivers/usb/core/of.c +++ b/drivers/usb/core/of.c @@ -8,6 +8,7 @@   */  #include <linux/of.h> +#include <linux/of_graph.h>  #include <linux/usb/of.h>  /** @@ -75,6 +76,76 @@ bool usb_of_has_combined_node(struct usb_device *udev)  }  EXPORT_SYMBOL_GPL(usb_of_has_combined_node); +static bool usb_of_has_devices_or_graph(const struct usb_device *hub) +{ +	const struct device_node *np = hub->dev.of_node; +	struct device_node *child; + +	if (of_graph_is_present(np)) +		return true; + +	for_each_child_of_node(np, child) +		if (of_property_present(child, "reg")) +			return true; + +	return false; +} + +/** + * usb_of_get_connect_type() - get a USB hub's port connect_type + * @hub: hub to which port is for @port1 + * @port1: one-based index of port + * + * Get the connect_type of @port1 based on the device node for @hub. If the + * port is described in the OF graph, the connect_type is "hotplug". If the + * @hub has a child device has with a 'reg' property equal to @port1 the + * connect_type is "hard-wired". If there isn't an OF graph or child node at + * all then the connect_type is "unknown". Otherwise, the port is considered + * "unused" because it isn't described at all. + * + * Return: A connect_type for @port1 based on the device node for @hub. + */ +enum usb_port_connect_type usb_of_get_connect_type(struct usb_device *hub, int port1) +{ +	struct device_node *np, *child, *ep, *remote_np; +	enum usb_port_connect_type connect_type; + +	/* Only set connect_type if binding has ports/hardwired devices. */ +	if (!usb_of_has_devices_or_graph(hub)) +		return USB_PORT_CONNECT_TYPE_UNKNOWN; + +	/* Assume port is unused if there's a graph or a child node. */ +	connect_type = USB_PORT_NOT_USED; + +	np = hub->dev.of_node; +	/* +	 * Hotplug ports are connected to an available remote node, e.g. +	 * usb-a-connector compatible node, in the OF graph. +	 */ +	if (of_graph_is_present(np)) { +		ep = of_graph_get_endpoint_by_regs(np, port1, -1); +		if (ep) { +			remote_np = of_graph_get_remote_port_parent(ep); +			of_node_put(ep); +			if (of_device_is_available(remote_np)) +				connect_type = USB_PORT_CONNECT_TYPE_HOT_PLUG; +			of_node_put(remote_np); +		} +	} + +	/* +	 * Hard-wired ports are child nodes with a reg property corresponding +	 * to the port number, i.e. a usb device. +	 */ +	child = usb_of_get_device_node(hub, port1); +	if (of_device_is_available(child)) +		connect_type = USB_PORT_CONNECT_TYPE_HARD_WIRED; +	of_node_put(child); + +	return connect_type; +} +EXPORT_SYMBOL_GPL(usb_of_get_connect_type); +  /**   * usb_of_get_interface_node() - get a USB interface node   * @udev: USB device of interface diff --git a/drivers/usb/core/phy.c b/drivers/usb/core/phy.c index fb1588e7c282..faa20054ad5a 100644 --- a/drivers/usb/core/phy.c +++ b/drivers/usb/core/phy.c @@ -19,6 +19,30 @@ struct usb_phy_roothub {  	struct list_head	list;  }; +/* Allocate the roothub_entry by specific name of phy */ +static int usb_phy_roothub_add_phy_by_name(struct device *dev, const char *name, +					   struct list_head *list) +{ +	struct usb_phy_roothub *roothub_entry; +	struct phy *phy; + +	phy = devm_of_phy_get(dev, dev->of_node, name); +	if (IS_ERR(phy)) +		return PTR_ERR(phy); + +	roothub_entry = devm_kzalloc(dev, sizeof(*roothub_entry), GFP_KERNEL); +	if (!roothub_entry) +		return -ENOMEM; + +	INIT_LIST_HEAD(&roothub_entry->list); + +	roothub_entry->phy = phy; + +	list_add_tail(&roothub_entry->list, list); + +	return 0; +} +  static int usb_phy_roothub_add_phy(struct device *dev, int index,  				   struct list_head *list)  { @@ -65,6 +89,9 @@ struct usb_phy_roothub *usb_phy_roothub_alloc(struct device *dev)  	INIT_LIST_HEAD(&phy_roothub->list); +	if (!usb_phy_roothub_add_phy_by_name(dev, "usb2-phy", &phy_roothub->list)) +		return phy_roothub; +  	for (i = 0; i < num_phys; i++) {  		err = usb_phy_roothub_add_phy(dev, i, &phy_roothub->list);  		if (err) @@ -75,6 +102,41 @@ struct usb_phy_roothub *usb_phy_roothub_alloc(struct device *dev)  }  EXPORT_SYMBOL_GPL(usb_phy_roothub_alloc); +/** + * usb_phy_roothub_alloc_usb3_phy - alloc the roothub + * @dev: the device of the host controller + * + * Allocate the usb phy roothub if the host use a generic usb3-phy. + * + * Return: On success, a pointer to the usb_phy_roothub. Otherwise, + * %NULL if no use usb3 phy or %-ENOMEM if out of memory. + */ +struct usb_phy_roothub *usb_phy_roothub_alloc_usb3_phy(struct device *dev) +{ +	struct usb_phy_roothub *phy_roothub; +	int num_phys; + +	if (!IS_ENABLED(CONFIG_GENERIC_PHY)) +		return NULL; + +	num_phys = of_count_phandle_with_args(dev->of_node, "phys", +					      "#phy-cells"); +	if (num_phys <= 0) +		return NULL; + +	phy_roothub = devm_kzalloc(dev, sizeof(*phy_roothub), GFP_KERNEL); +	if (!phy_roothub) +		return ERR_PTR(-ENOMEM); + +	INIT_LIST_HEAD(&phy_roothub->list); + +	if (!usb_phy_roothub_add_phy_by_name(dev, "usb3-phy", &phy_roothub->list)) +		return phy_roothub; + +	return NULL; +} +EXPORT_SYMBOL_GPL(usb_phy_roothub_alloc_usb3_phy); +  int usb_phy_roothub_init(struct usb_phy_roothub *phy_roothub)  {  	struct usb_phy_roothub *roothub_entry; @@ -172,6 +234,64 @@ int usb_phy_roothub_calibrate(struct usb_phy_roothub *phy_roothub)  }  EXPORT_SYMBOL_GPL(usb_phy_roothub_calibrate); +/** + * usb_phy_roothub_notify_connect() - connect notification + * @phy_roothub: the phy of roothub, if the host use a generic phy. + * @port: the port index for connect + * + * If the phy needs to get connection status, the callback can be used. + * Returns: %0 if successful, a negative error code otherwise + */ +int usb_phy_roothub_notify_connect(struct usb_phy_roothub *phy_roothub, int port) +{ +	struct usb_phy_roothub *roothub_entry; +	struct list_head *head; +	int err; + +	if (!phy_roothub) +		return 0; + +	head = &phy_roothub->list; + +	list_for_each_entry(roothub_entry, head, list) { +		err = phy_notify_connect(roothub_entry->phy, port); +		if (err) +			return err; +	} + +	return 0; +} +EXPORT_SYMBOL_GPL(usb_phy_roothub_notify_connect); + +/** + * usb_phy_roothub_notify_disconnect() - disconnect notification + * @phy_roothub: the phy of roothub, if the host use a generic phy. + * @port: the port index for disconnect + * + * If the phy needs to get connection status, the callback can be used. + * Returns: %0 if successful, a negative error code otherwise + */ +int usb_phy_roothub_notify_disconnect(struct usb_phy_roothub *phy_roothub, int port) +{ +	struct usb_phy_roothub *roothub_entry; +	struct list_head *head; +	int err; + +	if (!phy_roothub) +		return 0; + +	head = &phy_roothub->list; + +	list_for_each_entry(roothub_entry, head, list) { +		err = phy_notify_disconnect(roothub_entry->phy, port); +		if (err) +			return err; +	} + +	return 0; +} +EXPORT_SYMBOL_GPL(usb_phy_roothub_notify_disconnect); +  int usb_phy_roothub_power_on(struct usb_phy_roothub *phy_roothub)  {  	struct usb_phy_roothub *roothub_entry; diff --git a/drivers/usb/core/phy.h b/drivers/usb/core/phy.h index 20a267cd986b..88b49c0ea6b5 100644 --- a/drivers/usb/core/phy.h +++ b/drivers/usb/core/phy.h @@ -12,6 +12,7 @@ struct device;  struct usb_phy_roothub;  struct usb_phy_roothub *usb_phy_roothub_alloc(struct device *dev); +struct usb_phy_roothub *usb_phy_roothub_alloc_usb3_phy(struct device *dev);  int usb_phy_roothub_init(struct usb_phy_roothub *phy_roothub);  int usb_phy_roothub_exit(struct usb_phy_roothub *phy_roothub); @@ -19,6 +20,8 @@ int usb_phy_roothub_exit(struct usb_phy_roothub *phy_roothub);  int usb_phy_roothub_set_mode(struct usb_phy_roothub *phy_roothub,  			     enum phy_mode mode);  int usb_phy_roothub_calibrate(struct usb_phy_roothub *phy_roothub); +int usb_phy_roothub_notify_connect(struct usb_phy_roothub *phy_roothub, int port); +int usb_phy_roothub_notify_disconnect(struct usb_phy_roothub *phy_roothub, int port);  int usb_phy_roothub_power_on(struct usb_phy_roothub *phy_roothub);  void usb_phy_roothub_power_off(struct usb_phy_roothub *phy_roothub); diff --git a/drivers/usb/core/port.c b/drivers/usb/core/port.c index 4d63496f98b6..686c01af03e6 100644 --- a/drivers/usb/core/port.c +++ b/drivers/usb/core/port.c @@ -11,6 +11,7 @@  #include <linux/slab.h>  #include <linux/pm_qos.h>  #include <linux/component.h> +#include <linux/usb/of.h>  #include "hub.h" @@ -55,11 +56,22 @@ static ssize_t disable_show(struct device *dev,  	u16 portstatus, unused;  	bool disabled;  	int rc; +	struct kernfs_node *kn; +	hub_get(hub);  	rc = usb_autopm_get_interface(intf);  	if (rc < 0) -		return rc; +		goto out_hub_get; +	/* +	 * Prevent deadlock if another process is concurrently +	 * trying to unregister hdev. +	 */ +	kn = sysfs_break_active_protection(&dev->kobj, &attr->attr); +	if (!kn) { +		rc = -ENODEV; +		goto out_autopm; +	}  	usb_lock_device(hdev);  	if (hub->disconnected) {  		rc = -ENODEV; @@ -69,9 +81,13 @@ static ssize_t disable_show(struct device *dev,  	usb_hub_port_status(hub, port1, &portstatus, &unused);  	disabled = !usb_port_is_power_on(hub, portstatus); -out_hdev_lock: + out_hdev_lock:  	usb_unlock_device(hdev); +	sysfs_unbreak_active_protection(kn); + out_autopm:  	usb_autopm_put_interface(intf); + out_hub_get: +	hub_put(hub);  	if (rc)  		return rc; @@ -89,15 +105,26 @@ static ssize_t disable_store(struct device *dev, struct device_attribute *attr,  	int port1 = port_dev->portnum;  	bool disabled;  	int rc; +	struct kernfs_node *kn;  	rc = kstrtobool(buf, &disabled);  	if (rc)  		return rc; +	hub_get(hub);  	rc = usb_autopm_get_interface(intf);  	if (rc < 0) -		return rc; +		goto out_hub_get; +	/* +	 * Prevent deadlock if another process is concurrently +	 * trying to unregister hdev. +	 */ +	kn = sysfs_break_active_protection(&dev->kobj, &attr->attr); +	if (!kn) { +		rc = -ENODEV; +		goto out_autopm; +	}  	usb_lock_device(hdev);  	if (hub->disconnected) {  		rc = -ENODEV; @@ -118,9 +145,13 @@ static ssize_t disable_store(struct device *dev, struct device_attribute *attr,  	if (!rc)  		rc = count; -out_hdev_lock: + out_hdev_lock:  	usb_unlock_device(hdev); +	sysfs_unbreak_active_protection(kn); + out_autopm:  	usb_autopm_put_interface(intf); + out_hub_get: +	hub_put(hub);  	return rc;  } @@ -429,7 +460,7 @@ static const struct dev_pm_ops usb_port_pm_ops = {  #endif  }; -struct device_type usb_port_device_type = { +const struct device_type usb_port_device_type = {  	.name =		"usb_port",  	.release =	usb_port_device_release,  	.pm =		&usb_port_pm_ops, @@ -709,6 +740,7 @@ int usb_hub_create_port_device(struct usb_hub *hub, int port1)  		return -ENOMEM;  	} +	port_dev->connect_type = usb_of_get_connect_type(hdev, port1);  	hub->ports[port1 - 1] = port_dev;  	port_dev->portnum = port1;  	set_bit(port1, hub->power_bits); diff --git a/drivers/usb/core/sysfs.c b/drivers/usb/core/sysfs.c index 5d21718afb05..d83231d6736a 100644 --- a/drivers/usb/core/sysfs.c +++ b/drivers/usb/core/sysfs.c @@ -273,9 +273,10 @@ static ssize_t avoid_reset_quirk_store(struct device *dev,  				      const char *buf, size_t count)  {  	struct usb_device	*udev = to_usb_device(dev); -	int			val, rc; +	bool			val; +	int			rc; -	if (sscanf(buf, "%d", &val) != 1 || val < 0 || val > 1) +	if (kstrtobool(buf, &val) != 0)  		return -EINVAL;  	rc = usb_lock_device_interruptible(udev);  	if (rc < 0) @@ -322,13 +323,14 @@ static ssize_t persist_store(struct device *dev, struct device_attribute *attr,  			     const char *buf, size_t count)  {  	struct usb_device *udev = to_usb_device(dev); -	int value, rc; +	bool value; +	int rc;  	/* Hubs are always enabled for USB_PERSIST */  	if (udev->descriptor.bDeviceClass == USB_CLASS_HUB)  		return -EPERM; -	if (sscanf(buf, "%d", &value) != 1) +	if (kstrtobool(buf, &value) != 0)  		return -EINVAL;  	rc = usb_lock_device_interruptible(udev); @@ -739,14 +741,14 @@ static ssize_t authorized_store(struct device *dev,  {  	ssize_t result;  	struct usb_device *usb_dev = to_usb_device(dev); -	unsigned val; -	result = sscanf(buf, "%u\n", &val); -	if (result != 1) +	bool val; + +	if (kstrtobool(buf, &val) != 0)  		result = -EINVAL; -	else if (val == 0) -		result = usb_deauthorize_device(usb_dev); -	else +	else if (val)  		result = usb_authorize_device(usb_dev); +	else +		result = usb_deauthorize_device(usb_dev);  	return result < 0 ? result : size;  }  static DEVICE_ATTR_IGNORE_LOCKDEP(authorized, S_IRUGO | S_IWUSR, @@ -847,16 +849,10 @@ static const struct attribute_group dev_string_attr_grp = {  	.is_visible =	dev_string_attrs_are_visible,  }; -const struct attribute_group *usb_device_groups[] = { -	&dev_attr_grp, -	&dev_string_attr_grp, -	NULL -}; -  /* Binary descriptors */  static ssize_t -read_descriptors(struct file *filp, struct kobject *kobj, +descriptors_read(struct file *filp, struct kobject *kobj,  		struct bin_attribute *attr,  		char *buf, loff_t off, size_t count)  { @@ -878,7 +874,7 @@ read_descriptors(struct file *filp, struct kobject *kobj,  			srclen = sizeof(struct usb_device_descriptor);  		} else {  			src = udev->rawdescriptors[cfgno]; -			srclen = __le16_to_cpu(udev->config[cfgno].desc. +			srclen = le16_to_cpu(udev->config[cfgno].desc.  					wTotalLength);  		}  		if (off < srclen) { @@ -893,11 +889,69 @@ read_descriptors(struct file *filp, struct kobject *kobj,  	}  	return count - nleft;  } +static BIN_ATTR_RO(descriptors, 18 + 65535); /* dev descr + max-size raw descriptor */ + +static ssize_t +bos_descriptors_read(struct file *filp, struct kobject *kobj, +		struct bin_attribute *attr, +		char *buf, loff_t off, size_t count) +{ +	struct device *dev = kobj_to_dev(kobj); +	struct usb_device *udev = to_usb_device(dev); +	struct usb_host_bos *bos = udev->bos; +	struct usb_bos_descriptor *desc; +	size_t desclen, n = 0; + +	if (bos) { +		desc = bos->desc; +		desclen = le16_to_cpu(desc->wTotalLength); +		if (off < desclen) { +			n = min(count, desclen - (size_t) off); +			memcpy(buf, (void *) desc + off, n); +		} +	} +	return n; +} +static BIN_ATTR_RO(bos_descriptors, 65535); /* max-size BOS */ + +/* When modifying this list, be sure to modify dev_bin_attrs_are_visible() + * accordingly. + */ +static struct bin_attribute *dev_bin_attrs[] = { +	&bin_attr_descriptors, +	&bin_attr_bos_descriptors, +	NULL +}; + +static umode_t dev_bin_attrs_are_visible(struct kobject *kobj, +		struct bin_attribute *a, int n) +{ +	struct device *dev = kobj_to_dev(kobj); +	struct usb_device *udev = to_usb_device(dev); + +	/* +	 * There's no need to check if the descriptors attribute should +	 * be visible because all devices have a device descriptor. The +	 * bos_descriptors attribute should be visible if and only if +	 * the device has a BOS, so check if it exists here. +	 */ +	if (a == &bin_attr_bos_descriptors) { +		if (udev->bos == NULL) +			return 0; +	} +	return a->attr.mode; +} + +static const struct attribute_group dev_bin_attr_grp = { +	.bin_attrs =		dev_bin_attrs, +	.is_bin_visible =	dev_bin_attrs_are_visible, +}; -static struct bin_attribute dev_bin_attr_descriptors = { -	.attr = {.name = "descriptors", .mode = 0444}, -	.read = read_descriptors, -	.size = 18 + 65535,	/* dev descr + max-size raw descriptor */ +const struct attribute_group *usb_device_groups[] = { +	&dev_attr_grp, +	&dev_string_attr_grp, +	&dev_bin_attr_grp, +	NULL  };  /* @@ -1015,10 +1069,6 @@ int usb_create_sysfs_dev_files(struct usb_device *udev)  	struct device *dev = &udev->dev;  	int retval; -	retval = device_create_bin_file(dev, &dev_bin_attr_descriptors); -	if (retval) -		goto error; -  	retval = add_persist_attributes(dev);  	if (retval)  		goto error; @@ -1048,7 +1098,6 @@ void usb_remove_sysfs_dev_files(struct usb_device *udev)  	remove_power_attributes(dev);  	remove_persist_attributes(dev); -	device_remove_bin_file(dev, &dev_bin_attr_descriptors);  }  /* Interface Association Descriptor fields */ @@ -1168,14 +1217,24 @@ static ssize_t interface_authorized_store(struct device *dev,  {  	struct usb_interface *intf = to_usb_interface(dev);  	bool val; +	struct kernfs_node *kn;  	if (kstrtobool(buf, &val) != 0)  		return -EINVAL; -	if (val) +	if (val) {  		usb_authorize_interface(intf); -	else -		usb_deauthorize_interface(intf); +	} else { +		/* +		 * Prevent deadlock if another process is concurrently +		 * trying to unregister intf. +		 */ +		kn = sysfs_break_active_protection(&dev->kobj, &attr->attr); +		if (kn) { +			usb_deauthorize_interface(intf); +			sysfs_unbreak_active_protection(kn); +		} +	}  	return count;  } diff --git a/drivers/usb/core/usb-acpi.c b/drivers/usb/core/usb-acpi.c index a34b22537d7c..7f8a912d4fe2 100644 --- a/drivers/usb/core/usb-acpi.c +++ b/drivers/usb/core/usb-acpi.c @@ -142,12 +142,19 @@ int usb_acpi_set_power_state(struct usb_device *hdev, int index, bool enable)  }  EXPORT_SYMBOL_GPL(usb_acpi_set_power_state); -static enum usb_port_connect_type usb_acpi_get_connect_type(acpi_handle handle, -		struct acpi_pld_info *pld) +/* + * Private to usb-acpi, all the core needs to know is that + * port_dev->location is non-zero when it has been set by the firmware. + */ +#define USB_ACPI_LOCATION_VALID (1 << 31) + +static void +usb_acpi_get_connect_type(struct usb_port *port_dev, acpi_handle *handle)  {  	enum usb_port_connect_type connect_type = USB_PORT_CONNECT_TYPE_UNKNOWN;  	struct acpi_buffer buffer = { ACPI_ALLOCATE_BUFFER, NULL };  	union acpi_object *upc = NULL; +	struct acpi_pld_info *pld = NULL;  	acpi_status status;  	/* @@ -158,6 +165,12 @@ static enum usb_port_connect_type usb_acpi_get_connect_type(acpi_handle handle,  	 * a usb device is directly hard-wired to the port. If no visible and  	 * no connectable, the port would be not used.  	 */ + +	status = acpi_get_physical_device_location(handle, &pld); +	if (ACPI_SUCCESS(status) && pld) +		port_dev->location = USB_ACPI_LOCATION_VALID | +			pld->group_token << 8 | pld->group_position; +  	status = acpi_evaluate_object(handle, "_UPC", NULL, &buffer);  	if (ACPI_FAILURE(status))  		goto out; @@ -166,25 +179,22 @@ static enum usb_port_connect_type usb_acpi_get_connect_type(acpi_handle handle,  	if (!upc || (upc->type != ACPI_TYPE_PACKAGE) || upc->package.count != 4)  		goto out; +	/* UPC states port is connectable */  	if (upc->package.elements[0].integer.value) -		if (pld->user_visible) +		if (!pld) +			; /* keep connect_type as unknown */ +		else if (pld->user_visible)  			connect_type = USB_PORT_CONNECT_TYPE_HOT_PLUG;  		else  			connect_type = USB_PORT_CONNECT_TYPE_HARD_WIRED; -	else if (!pld->user_visible) +	else  		connect_type = USB_PORT_NOT_USED;  out: +	port_dev->connect_type = connect_type;  	kfree(upc); -	return connect_type; +	ACPI_FREE(pld);  } - -/* - * Private to usb-acpi, all the core needs to know is that - * port_dev->location is non-zero when it has been set by the firmware. - */ -#define USB_ACPI_LOCATION_VALID (1 << 31) -  static struct acpi_device *  usb_acpi_get_companion_for_port(struct usb_port *port_dev)  { @@ -222,22 +232,12 @@ static struct acpi_device *  usb_acpi_find_companion_for_port(struct usb_port *port_dev)  {  	struct acpi_device *adev; -	struct acpi_pld_info *pld; -	acpi_handle *handle; -	acpi_status status;  	adev = usb_acpi_get_companion_for_port(port_dev);  	if (!adev)  		return NULL; -	handle = adev->handle; -	status = acpi_get_physical_device_location(handle, &pld); -	if (ACPI_SUCCESS(status) && pld) { -		port_dev->location = USB_ACPI_LOCATION_VALID -			| pld->group_token << 8 | pld->group_position; -		port_dev->connect_type = usb_acpi_get_connect_type(handle, pld); -		ACPI_FREE(pld); -	} +	usb_acpi_get_connect_type(port_dev, adev->handle);  	return adev;  } diff --git a/drivers/usb/core/usb.c b/drivers/usb/core/usb.c index dc8d9228a5e7..a0c432b14b20 100644 --- a/drivers/usb/core/usb.c +++ b/drivers/usb/core/usb.c @@ -592,7 +592,7 @@ static char *usb_devnode(const struct device *dev,  			 usb_dev->bus->busnum, usb_dev->devnum);  } -struct device_type usb_device_type = { +const struct device_type usb_device_type = {  	.name =		"usb_device",  	.release =	usb_release_dev,  	.uevent =	usb_dev_uevent, diff --git a/drivers/usb/core/usb.h b/drivers/usb/core/usb.h index bfecb50773b6..b8324ea05b20 100644 --- a/drivers/usb/core/usb.h +++ b/drivers/usb/core/usb.h @@ -144,10 +144,10 @@ static inline int usb_disable_usb2_hardware_lpm(struct usb_device *udev)  extern const struct class usbmisc_class;  extern const struct bus_type usb_bus_type;  extern struct mutex usb_port_peer_mutex; -extern struct device_type usb_device_type; -extern struct device_type usb_if_device_type; -extern struct device_type usb_ep_device_type; -extern struct device_type usb_port_device_type; +extern const struct device_type usb_device_type; +extern const struct device_type usb_if_device_type; +extern const struct device_type usb_ep_device_type; +extern const struct device_type usb_port_device_type;  extern struct usb_device_driver usb_generic_driver;  static inline int is_usb_device(const struct device *dev)  | 
