aboutsummaryrefslogtreecommitdiffstats
path: root/drivers/usb/core
diff options
context:
space:
mode:
authorLinus Torvalds <torvalds@linux-foundation.org>2022-08-04 11:41:28 -0700
committerLinus Torvalds <torvalds@linux-foundation.org>2022-08-04 11:41:28 -0700
commit9e2e5ea3b28f81512c792f30729edb1db0c21f6a (patch)
tree5543cd085f766f3adf0fee3389b9a7715f956886 /drivers/usb/core
parentMerge tag 'driver-core-6.0-rc1' of git://git.kernel.org/pub/scm/linux/kernel/git/gregkh/driver-core (diff)
parentusb: misc: onboard_usb_hub: Remove duplicated power_on delay (diff)
downloadlinux-dev-9e2e5ea3b28f81512c792f30729edb1db0c21f6a.tar.xz
linux-dev-9e2e5ea3b28f81512c792f30729edb1db0c21f6a.zip
Merge tag 'usb-6.0-rc1' of git://git.kernel.org/pub/scm/linux/kernel/git/gregkh/usb
Pull USB / Thunderbolt updates from Greg KH: "Here is the big set of Thunderbolt and USB changes for 6.0-rc1. Lots of little things here, nothing major, just constant development on some new hardware support and cleanups of older drivers. Highlights are: - lots of typec changes and improvements for new hardware - new gadget controller driver - thunderbolt support for new hardware - the normal set of new usb-serial device ids and cleanups - loads of dwc3 controller fixes and improvements - mtu3 driver updates - testusb fixes for longtime issues (not many people use this tool it seems.) - minor driver fixes and improvements over the USB tree - chromeos platform driver changes were added and then reverted as they depened on some typec changes, but the cross-tree merges caused problems so they will come back later through the platform tree. All of these have been in linux-next for a while now with no reported issues" * tag 'usb-6.0-rc1' of git://git.kernel.org/pub/scm/linux/kernel/git/gregkh/usb: (193 commits) usb: misc: onboard_usb_hub: Remove duplicated power_on delay usb: misc: onboard_usb_hub: Add TI USB8041 hub support usb: misc: onboard_usb_hub: Add reset-gpio support USB: usbsevseg: convert sysfs snprintf to sysfs_emit dt-bindings: usb: Add binding for TI USB8041 hub controller ARM: multi_v7_defconfig: enable USB onboard HUB driver ARM: dts: stm32: add support for USB2514B onboard hub on stm32mp15xx-dkx usb: misc: onboard-hub: add support for Microchip USB2514B USB 2.0 hub dt-bindings: usb: generic-ehci: allow usb-hcd schema properties usb: typec: ucsi: stm32g0: add bootloader support usb: typec: ucsi: stm32g0: add support for stm32g0 controller dt-bindings: usb: typec: add bindings for stm32g0 controller usb: typec: ucsi: Acknowledge the GET_ERROR_STATUS command completion usb: cdns3: change place of 'priv_ep' assignment in cdns3_gadget_ep_dequeue(), cdns3_gadget_ep_enable() usb/chipidea: fix repeated words in comments usb: renesas-xhci: Do not print any log while fw verif success usb: typec: retimer: Add missing id check in match callback USB: xhci: Fix comment typo usb/typec/tcpm: fix repeated words in comments usb/musb: fix repeated words in comments ...
Diffstat (limited to 'drivers/usb/core')
-rw-r--r--drivers/usb/core/Makefile4
-rw-r--r--drivers/usb/core/driver.c2
-rw-r--r--drivers/usb/core/hcd.c34
-rw-r--r--drivers/usb/core/hub.c108
-rw-r--r--drivers/usb/core/hub.h4
-rw-r--r--drivers/usb/core/port.c83
-rw-r--r--drivers/usb/core/sysfs.c79
-rw-r--r--drivers/usb/core/usb.c2
8 files changed, 204 insertions, 112 deletions
diff --git a/drivers/usb/core/Makefile b/drivers/usb/core/Makefile
index 18e874b0441e..7d338e9c0657 100644
--- a/drivers/usb/core/Makefile
+++ b/drivers/usb/core/Makefile
@@ -12,6 +12,10 @@ usbcore-$(CONFIG_OF) += of.o
usbcore-$(CONFIG_USB_PCI) += hcd-pci.o
usbcore-$(CONFIG_ACPI) += usb-acpi.o
+ifdef CONFIG_USB_ONBOARD_HUB
+usbcore-y += ../misc/onboard_usb_hub_pdevs.o
+endif
+
obj-$(CONFIG_USB) += usbcore.o
obj-$(CONFIG_USB_LEDS_TRIGGER_USBPORT) += ledtrig-usbport.o
diff --git a/drivers/usb/core/driver.c b/drivers/usb/core/driver.c
index b87452e22835..7e7e119c253f 100644
--- a/drivers/usb/core/driver.c
+++ b/drivers/usb/core/driver.c
@@ -1482,7 +1482,7 @@ static int usb_suspend_both(struct usb_device *udev, pm_message_t msg)
* @msg: Power Management message describing this state transition
*
* This is the central routine for resuming USB devices. It calls the
- * the resume method for @udev and then calls the resume methods for all
+ * resume method for @udev and then calls the resume methods for all
* the interface drivers in @udev.
*
* Autoresume requests originating from a child device or an interface
diff --git a/drivers/usb/core/hcd.c b/drivers/usb/core/hcd.c
index 06eea8848ccc..a6a87c5d1b05 100644
--- a/drivers/usb/core/hcd.c
+++ b/drivers/usb/core/hcd.c
@@ -1691,7 +1691,6 @@ static void usb_giveback_urb_bh(struct tasklet_struct *t)
spin_lock_irq(&bh->lock);
bh->running = true;
- restart:
list_replace_init(&bh->head, &local_list);
spin_unlock_irq(&bh->lock);
@@ -1705,10 +1704,17 @@ static void usb_giveback_urb_bh(struct tasklet_struct *t)
bh->completing_ep = NULL;
}
- /* check if there are new URBs to giveback */
+ /*
+ * giveback new URBs next time to prevent this function
+ * from not exiting for a long time.
+ */
spin_lock_irq(&bh->lock);
- if (!list_empty(&bh->head))
- goto restart;
+ if (!list_empty(&bh->head)) {
+ if (bh->high_prio)
+ tasklet_hi_schedule(&bh->bh);
+ else
+ tasklet_schedule(&bh->bh);
+ }
bh->running = false;
spin_unlock_irq(&bh->lock);
}
@@ -1737,7 +1743,7 @@ static void usb_giveback_urb_bh(struct tasklet_struct *t)
void usb_hcd_giveback_urb(struct usb_hcd *hcd, struct urb *urb, int status)
{
struct giveback_urb_bh *bh;
- bool running, high_prio_bh;
+ bool running;
/* pass status to tasklet via unlinked */
if (likely(!urb->unlinked))
@@ -1748,13 +1754,10 @@ void usb_hcd_giveback_urb(struct usb_hcd *hcd, struct urb *urb, int status)
return;
}
- if (usb_pipeisoc(urb->pipe) || usb_pipeint(urb->pipe)) {
+ if (usb_pipeisoc(urb->pipe) || usb_pipeint(urb->pipe))
bh = &hcd->high_prio_bh;
- high_prio_bh = true;
- } else {
+ else
bh = &hcd->low_prio_bh;
- high_prio_bh = false;
- }
spin_lock(&bh->lock);
list_add_tail(&urb->urb_list, &bh->head);
@@ -1763,7 +1766,7 @@ void usb_hcd_giveback_urb(struct usb_hcd *hcd, struct urb *urb, int status)
if (running)
;
- else if (high_prio_bh)
+ else if (bh->high_prio)
tasklet_hi_schedule(&bh->bh);
else
tasklet_schedule(&bh->bh);
@@ -2959,6 +2962,7 @@ int usb_add_hcd(struct usb_hcd *hcd,
/* initialize tasklets */
init_giveback_urb_bh(&hcd->high_prio_bh);
+ hcd->high_prio_bh.high_prio = true;
init_giveback_urb_bh(&hcd->low_prio_bh);
/* enable irqs just before we start the controller,
@@ -3033,9 +3037,15 @@ EXPORT_SYMBOL_GPL(usb_add_hcd);
*/
void usb_remove_hcd(struct usb_hcd *hcd)
{
- struct usb_device *rhdev = hcd->self.root_hub;
+ struct usb_device *rhdev;
bool rh_registered;
+ if (!hcd) {
+ pr_debug("%s: hcd is NULL\n", __func__);
+ return;
+ }
+ rhdev = hcd->self.root_hub;
+
dev_info(hcd->self.controller, "remove, state %x\n", hcd->state);
usb_get_dev(rhdev);
diff --git a/drivers/usb/core/hub.c b/drivers/usb/core/hub.c
index 68e9121c1878..2633acde7ac1 100644
--- a/drivers/usb/core/hub.c
+++ b/drivers/usb/core/hub.c
@@ -23,6 +23,7 @@
#include <linux/usb.h>
#include <linux/usbdevice_fs.h>
#include <linux/usb/hcd.h>
+#include <linux/usb/onboard_hub.h>
#include <linux/usb/otg.h>
#include <linux/usb/quirks.h>
#include <linux/workqueue.h>
@@ -613,7 +614,7 @@ static int hub_ext_port_status(struct usb_hub *hub, int port1, int type,
return ret;
}
-static int hub_port_status(struct usb_hub *hub, int port1,
+int usb_hub_port_status(struct usb_hub *hub, int port1,
u16 *status, u16 *change)
{
return hub_ext_port_status(hub, port1, HUB_PORT_STATUS,
@@ -1126,7 +1127,7 @@ static void hub_activate(struct usb_hub *hub, enum hub_activation_type type)
u16 portstatus, portchange;
portstatus = portchange = 0;
- status = hub_port_status(hub, port1, &portstatus, &portchange);
+ status = usb_hub_port_status(hub, port1, &portstatus, &portchange);
if (status)
goto abort;
@@ -1752,6 +1753,8 @@ static void hub_disconnect(struct usb_interface *intf)
if (hub->quirk_disable_autosuspend)
usb_autopm_put_interface(intf);
+ onboard_hub_destroy_pdevs(&hub->onboard_hub_devs);
+
kref_put(&hub->kref, hub_release);
}
@@ -1869,6 +1872,7 @@ static int hub_probe(struct usb_interface *intf, const struct usb_device_id *id)
INIT_DELAYED_WORK(&hub->leds, led_work);
INIT_DELAYED_WORK(&hub->init_work, NULL);
INIT_WORK(&hub->events, hub_event);
+ INIT_LIST_HEAD(&hub->onboard_hub_devs);
spin_lock_init(&hub->irq_urb_lock);
timer_setup(&hub->irq_urb_retry, hub_retry_irq_urb, 0);
usb_get_intf(intf);
@@ -1889,8 +1893,11 @@ static int hub_probe(struct usb_interface *intf, const struct usb_device_id *id)
usb_autopm_get_interface_no_resume(intf);
}
- if (hub_configure(hub, &desc->endpoint[0].desc) >= 0)
+ if (hub_configure(hub, &desc->endpoint[0].desc) >= 0) {
+ onboard_hub_create_pdevs(hdev, &hub->onboard_hub_devs);
+
return 0;
+ }
hub_disconnect(intf);
return -ENODEV;
@@ -2855,7 +2862,7 @@ static int hub_port_wait_reset(struct usb_hub *hub, int port1,
&portstatus, &portchange,
&ext_portstatus);
else
- ret = hub_port_status(hub, port1, &portstatus,
+ ret = usb_hub_port_status(hub, port1, &portstatus,
&portchange);
if (ret < 0)
return ret;
@@ -2956,7 +2963,8 @@ static int hub_port_reset(struct usb_hub *hub, int port1,
* If the caller hasn't explicitly requested a warm reset,
* double check and see if one is needed.
*/
- if (hub_port_status(hub, port1, &portstatus, &portchange) == 0)
+ if (usb_hub_port_status(hub, port1, &portstatus,
+ &portchange) == 0)
if (hub_port_warm_reset_required(hub, port1,
portstatus))
warm = true;
@@ -3008,7 +3016,7 @@ static int hub_port_reset(struct usb_hub *hub, int port1,
* If a USB 3.0 device migrates from reset to an error
* state, re-issue the warm reset.
*/
- if (hub_port_status(hub, port1,
+ if (usb_hub_port_status(hub, port1,
&portstatus, &portchange) < 0)
goto done;
@@ -3074,7 +3082,7 @@ done:
}
/* Check if a port is power on */
-static int port_is_power_on(struct usb_hub *hub, unsigned portstatus)
+int usb_port_is_power_on(struct usb_hub *hub, unsigned int portstatus)
{
int ret = 0;
@@ -3140,13 +3148,13 @@ static int check_port_resume_type(struct usb_device *udev,
}
/* Is the device still present? */
else if (status || port_is_suspended(hub, portstatus) ||
- !port_is_power_on(hub, portstatus)) {
+ !usb_port_is_power_on(hub, portstatus)) {
if (status >= 0)
status = -ENODEV;
} else if (!(portstatus & USB_PORT_STAT_CONNECTION)) {
if (retries--) {
usleep_range(200, 300);
- status = hub_port_status(hub, port1, &portstatus,
+ status = usb_hub_port_status(hub, port1, &portstatus,
&portchange);
goto retry;
}
@@ -3409,7 +3417,7 @@ int usb_port_suspend(struct usb_device *udev, pm_message_t msg)
u16 portstatus, portchange;
portstatus = portchange = 0;
- ret = hub_port_status(hub, port1, &portstatus,
+ ret = usb_hub_port_status(hub, port1, &portstatus,
&portchange);
dev_dbg(&port_dev->dev,
@@ -3587,13 +3595,13 @@ static int wait_for_connected(struct usb_device *udev,
while (delay_ms < 2000) {
if (status || *portstatus & USB_PORT_STAT_CONNECTION)
break;
- if (!port_is_power_on(hub, *portstatus)) {
+ if (!usb_port_is_power_on(hub, *portstatus)) {
status = -ENODEV;
break;
}
msleep(20);
delay_ms += 20;
- status = hub_port_status(hub, port1, portstatus, portchange);
+ status = usb_hub_port_status(hub, port1, portstatus, portchange);
}
dev_dbg(&udev->dev, "Waited %dms for CONNECT\n", delay_ms);
return status;
@@ -3653,7 +3661,7 @@ int usb_port_resume(struct usb_device *udev, pm_message_t msg)
usb_lock_port(port_dev);
/* Skip the initial Clear-Suspend step for a remote wakeup */
- status = hub_port_status(hub, port1, &portstatus, &portchange);
+ status = usb_hub_port_status(hub, port1, &portstatus, &portchange);
if (status == 0 && !port_is_suspended(hub, portstatus)) {
if (portchange & USB_PORT_STAT_C_SUSPEND)
pm_wakeup_event(&udev->dev, 0);
@@ -3678,7 +3686,7 @@ int usb_port_resume(struct usb_device *udev, pm_message_t msg)
* stop resume signaling. Then finish the resume
* sequence.
*/
- status = hub_port_status(hub, port1, &portstatus, &portchange);
+ status = usb_hub_port_status(hub, port1, &portstatus, &portchange);
}
SuspendCleared:
@@ -3791,7 +3799,7 @@ static int check_ports_changed(struct usb_hub *hub)
u16 portstatus, portchange;
int status;
- status = hub_port_status(hub, port1, &portstatus, &portchange);
+ status = usb_hub_port_status(hub, port1, &portstatus, &portchange);
if (!status && portchange)
return 1;
}
@@ -3946,7 +3954,7 @@ static const char * const usb3_lpm_names[] = {
* 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)
+static int usb_req_set_sel(struct usb_device *udev)
{
struct usb_set_sel_req *sel_values;
unsigned long long u1_sel;
@@ -3955,7 +3963,7 @@ static int usb_req_set_sel(struct usb_device *udev, enum usb3_link_state state)
unsigned long long u2_pel;
int ret;
- if (udev->state != USB_STATE_CONFIGURED)
+ if (!udev->parent || udev->speed < USB_SPEED_SUPER || !udev->lpm_capable)
return 0;
/* Convert SEL and PEL stored in ns to us */
@@ -3972,35 +3980,15 @@ static int usb_req_set_sel(struct usb_device *udev, enum usb3_link_state state)
* 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 us or PEL %llu us\n",
- usb3_lpm_names[state], u1_sel, u1_pel);
+ if (u1_sel > USB3_LPM_MAX_U1_SEL_PEL ||
+ u1_pel > USB3_LPM_MAX_U1_SEL_PEL ||
+ u2_sel > USB3_LPM_MAX_U2_SEL_PEL ||
+ u2_pel > USB3_LPM_MAX_U2_SEL_PEL) {
+ dev_dbg(&udev->dev, "Device-initiated U1/U2 disabled due to long SEL or PEL\n");
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.
@@ -4021,6 +4009,10 @@ static int usb_req_set_sel(struct usb_device *udev, enum usb3_link_state state)
sel_values, sizeof *(sel_values),
USB_CTRL_SET_TIMEOUT);
kfree(sel_values);
+
+ if (ret > 0)
+ udev->lpm_devinit_allow = 1;
+
return ret;
}
@@ -4136,6 +4128,9 @@ static bool usb_device_may_initiate_lpm(struct usb_device *udev,
unsigned int sel; /* us */
int i, j;
+ if (!udev->lpm_devinit_allow)
+ return false;
+
if (state == USB3_LPM_U1)
sel = DIV_ROUND_UP(udev->u1_params.sel, 1000);
else if (state == USB3_LPM_U2)
@@ -4184,7 +4179,7 @@ static bool usb_device_may_initiate_lpm(struct usb_device *udev,
static void usb_enable_link_state(struct usb_hcd *hcd, struct usb_device *udev,
enum usb3_link_state state)
{
- int timeout, ret;
+ int timeout;
__u8 u1_mel = udev->bos->ss_cap->bU1devExitLat;
__le16 u2_mel = udev->bos->ss_cap->bU2DevExitLat;
@@ -4196,17 +4191,6 @@ static void usb_enable_link_state(struct usb_hcd *hcd, struct usb_device *udev,
(state == USB3_LPM_U2 && u2_mel == 0))
return;
- /*
- * 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;
- }
-
/* 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
@@ -4486,6 +4470,11 @@ static int hub_handle_remote_wakeup(struct usb_hub *hub, unsigned int port,
return 0;
}
+static int usb_req_set_sel(struct usb_device *udev)
+{
+ return 0;
+}
+
#endif /* CONFIG_PM */
/*
@@ -4554,7 +4543,7 @@ int hub_port_debounce(struct usb_hub *hub, int port1, bool must_be_connected)
struct usb_port *port_dev = hub->ports[port1 - 1];
for (total_time = 0; ; total_time += HUB_DEBOUNCE_STEP) {
- ret = hub_port_status(hub, port1, &portstatus, &portchange);
+ ret = usb_hub_port_status(hub, port1, &portstatus, &portchange);
if (ret < 0)
return ret;
@@ -5011,6 +5000,7 @@ hub_port_init(struct usb_hub *hub, struct usb_device *udev, int port1,
udev->lpm_capable = usb_device_supports_lpm(udev);
udev->lpm_disable_count = 1;
usb_set_lpm_parameters(udev);
+ usb_req_set_sel(udev);
}
}
@@ -5240,7 +5230,7 @@ static void hub_port_connect(struct usb_hub *hub, int port1, u16 portstatus,
* but only if the port isn't owned by someone else.
*/
if (hub_is_port_power_switchable(hub)
- && !port_is_power_on(hub, portstatus)
+ && !usb_port_is_power_on(hub, portstatus)
&& !port_dev->port_owner)
set_port_feature(hdev, port1, USB_PORT_FEAT_POWER);
@@ -5557,7 +5547,7 @@ static void port_event(struct usb_hub *hub, int port1)
clear_bit(port1, hub->event_bits);
clear_bit(port1, hub->wakeup_bits);
- if (hub_port_status(hub, port1, &portstatus, &portchange) < 0)
+ if (usb_hub_port_status(hub, port1, &portstatus, &portchange) < 0)
return;
if (portchange & USB_PORT_STAT_C_CONNECTION) {
@@ -5594,7 +5584,7 @@ static void port_event(struct usb_hub *hub, int port1)
USB_PORT_FEAT_C_OVER_CURRENT);
msleep(100); /* Cool down */
hub_power_on(hub, true);
- hub_port_status(hub, port1, &status, &unused);
+ usb_hub_port_status(hub, port1, &status, &unused);
if (status & USB_PORT_STAT_OVERCURRENT)
dev_err(&port_dev->dev, "over-current condition\n");
}
@@ -5638,7 +5628,7 @@ static void port_event(struct usb_hub *hub, int port1)
u16 unused;
msleep(20);
- hub_port_status(hub, port1, &portstatus, &unused);
+ usb_hub_port_status(hub, port1, &portstatus, &unused);
dev_dbg(&port_dev->dev, "Wait for inactive link disconnect detect\n");
continue;
} else if (!udev || !(portstatus & USB_PORT_STAT_CONNECTION)
diff --git a/drivers/usb/core/hub.h b/drivers/usb/core/hub.h
index 22ea1f4f2d66..b2925856b4cb 100644
--- a/drivers/usb/core/hub.h
+++ b/drivers/usb/core/hub.h
@@ -73,6 +73,7 @@ struct usb_hub {
spinlock_t irq_urb_lock;
struct timer_list irq_urb_retry;
struct usb_port **ports;
+ struct list_head onboard_hub_devs;
};
/**
@@ -121,6 +122,9 @@ 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);
+extern int usb_hub_port_status(struct usb_hub *hub, int port1,
+ u16 *status, u16 *change);
+extern int usb_port_is_power_on(struct usb_hub *hub, unsigned int portstatus);
static inline bool hub_is_port_power_switchable(struct usb_hub *hub)
{
diff --git a/drivers/usb/core/port.c b/drivers/usb/core/port.c
index d5bc36ca5b1f..38c1a4f4fdea 100644
--- a/drivers/usb/core/port.c
+++ b/drivers/usb/core/port.c
@@ -17,6 +17,88 @@ static int usb_port_block_power_off;
static const struct attribute_group *port_dev_group[];
+static ssize_t disable_show(struct device *dev,
+ struct device_attribute *attr, char *buf)
+{
+ struct usb_port *port_dev = to_usb_port(dev);
+ struct usb_device *hdev = to_usb_device(dev->parent->parent);
+ struct usb_hub *hub = usb_hub_to_struct_hub(hdev);
+ struct usb_interface *intf = to_usb_interface(hub->intfdev);
+ int port1 = port_dev->portnum;
+ u16 portstatus, unused;
+ bool disabled;
+ int rc;
+
+ rc = usb_autopm_get_interface(intf);
+ if (rc < 0)
+ return rc;
+
+ usb_lock_device(hdev);
+ if (hub->disconnected) {
+ rc = -ENODEV;
+ goto out_hdev_lock;
+ }
+
+ usb_hub_port_status(hub, port1, &portstatus, &unused);
+ disabled = !usb_port_is_power_on(hub, portstatus);
+
+out_hdev_lock:
+ usb_unlock_device(hdev);
+ usb_autopm_put_interface(intf);
+
+ if (rc)
+ return rc;
+
+ return sysfs_emit(buf, "%s\n", disabled ? "1" : "0");
+}
+
+static ssize_t disable_store(struct device *dev, struct device_attribute *attr,
+ const char *buf, size_t count)
+{
+ struct usb_port *port_dev = to_usb_port(dev);
+ struct usb_device *hdev = to_usb_device(dev->parent->parent);
+ struct usb_hub *hub = usb_hub_to_struct_hub(hdev);
+ struct usb_interface *intf = to_usb_interface(hub->intfdev);
+ int port1 = port_dev->portnum;
+ bool disabled;
+ int rc;
+
+ rc = strtobool(buf, &disabled);
+ if (rc)
+ return rc;
+
+ rc = usb_autopm_get_interface(intf);
+ if (rc < 0)
+ return rc;
+
+ usb_lock_device(hdev);
+ if (hub->disconnected) {
+ rc = -ENODEV;
+ goto out_hdev_lock;
+ }
+
+ if (disabled && port_dev->child)
+ usb_disconnect(&port_dev->child);
+
+ rc = usb_hub_set_port_power(hdev, hub, port1, !disabled);
+
+ if (disabled) {
+ usb_clear_port_feature(hdev, port1, USB_PORT_FEAT_C_CONNECTION);
+ if (!port_dev->is_superspeed)
+ usb_clear_port_feature(hdev, port1, USB_PORT_FEAT_C_ENABLE);
+ }
+
+ if (!rc)
+ rc = count;
+
+out_hdev_lock:
+ usb_unlock_device(hdev);
+ usb_autopm_put_interface(intf);
+
+ return rc;
+}
+static DEVICE_ATTR_RW(disable);
+
static ssize_t location_show(struct device *dev,
struct device_attribute *attr, char *buf)
{
@@ -153,6 +235,7 @@ static struct attribute *port_dev_attrs[] = {
&dev_attr_location.attr,
&dev_attr_quirks.attr,
&dev_attr_over_current_count.attr,
+ &dev_attr_disable.attr,
NULL,
};
diff --git a/drivers/usb/core/sysfs.c b/drivers/usb/core/sysfs.c
index fa2e49d432ff..631574718d8a 100644
--- a/drivers/usb/core/sysfs.c
+++ b/drivers/usb/core/sysfs.c
@@ -35,7 +35,7 @@ static ssize_t field##_show(struct device *dev, \
return -EINTR; \
actconfig = udev->actconfig; \
if (actconfig) \
- rc = sprintf(buf, format_string, \
+ rc = sysfs_emit(buf, format_string, \
actconfig->desc.field); \
usb_unlock_device(udev); \
return rc; \
@@ -61,7 +61,7 @@ static ssize_t bMaxPower_show(struct device *dev,
return -EINTR;
actconfig = udev->actconfig;
if (actconfig)
- rc = sprintf(buf, "%dmA\n", usb_get_max_power(udev, actconfig));
+ rc = sysfs_emit(buf, "%dmA\n", usb_get_max_power(udev, actconfig));
usb_unlock_device(udev);
return rc;
}
@@ -80,7 +80,7 @@ static ssize_t configuration_show(struct device *dev,
return -EINTR;
actconfig = udev->actconfig;
if (actconfig && actconfig->string)
- rc = sprintf(buf, "%s\n", actconfig->string);
+ rc = sysfs_emit(buf, "%s\n", actconfig->string);
usb_unlock_device(udev);
return rc;
}
@@ -114,7 +114,7 @@ static ssize_t devspec_show(struct device *dev, struct device_attribute *attr,
{
struct device_node *of_node = dev->of_node;
- return sprintf(buf, "%pOF\n", of_node);
+ return sysfs_emit(buf, "%pOF\n", of_node);
}
static DEVICE_ATTR_RO(devspec);
#endif
@@ -131,7 +131,7 @@ static ssize_t name##_show(struct device *dev, \
retval = usb_lock_device_interruptible(udev); \
if (retval < 0) \
return -EINTR; \
- retval = sprintf(buf, "%s\n", udev->name); \
+ retval = sysfs_emit(buf, "%s\n", udev->name); \
usb_unlock_device(udev); \
return retval; \
} \
@@ -175,7 +175,7 @@ static ssize_t speed_show(struct device *dev, struct device_attribute *attr,
default:
speed = "unknown";
}
- return sprintf(buf, "%s\n", speed);
+ return sysfs_emit(buf, "%s\n", speed);
}
static DEVICE_ATTR_RO(speed);
@@ -185,7 +185,7 @@ static ssize_t rx_lanes_show(struct device *dev, struct device_attribute *attr,
struct usb_device *udev;
udev = to_usb_device(dev);
- return sprintf(buf, "%d\n", udev->rx_lanes);
+ return sysfs_emit(buf, "%d\n", udev->rx_lanes);
}
static DEVICE_ATTR_RO(rx_lanes);
@@ -195,7 +195,7 @@ static ssize_t tx_lanes_show(struct device *dev, struct device_attribute *attr,
struct usb_device *udev;
udev = to_usb_device(dev);
- return sprintf(buf, "%d\n", udev->tx_lanes);
+ return sysfs_emit(buf, "%d\n", udev->tx_lanes);
}
static DEVICE_ATTR_RO(tx_lanes);
@@ -205,7 +205,7 @@ static ssize_t busnum_show(struct device *dev, struct device_attribute *attr,
struct usb_device *udev;
udev = to_usb_device(dev);
- return sprintf(buf, "%d\n", udev->bus->busnum);
+ return sysfs_emit(buf, "%d\n", udev->bus->busnum);
}
static DEVICE_ATTR_RO(busnum);
@@ -215,7 +215,7 @@ static ssize_t devnum_show(struct device *dev, struct device_attribute *attr,
struct usb_device *udev;
udev = to_usb_device(dev);
- return sprintf(buf, "%d\n", udev->devnum);
+ return sysfs_emit(buf, "%d\n", udev->devnum);
}
static DEVICE_ATTR_RO(devnum);
@@ -225,7 +225,7 @@ static ssize_t devpath_show(struct device *dev, struct device_attribute *attr,
struct usb_device *udev;
udev = to_usb_device(dev);
- return sprintf(buf, "%s\n", udev->devpath);
+ return sysfs_emit(buf, "%s\n", udev->devpath);
}
static DEVICE_ATTR_RO(devpath);
@@ -237,7 +237,7 @@ static ssize_t version_show(struct device *dev, struct device_attribute *attr,
udev = to_usb_device(dev);
bcdUSB = le16_to_cpu(udev->descriptor.bcdUSB);
- return sprintf(buf, "%2x.%02x\n", bcdUSB >> 8, bcdUSB & 0xff);
+ return sysfs_emit(buf, "%2x.%02x\n", bcdUSB >> 8, bcdUSB & 0xff);
}
static DEVICE_ATTR_RO(version);
@@ -247,7 +247,7 @@ static ssize_t maxchild_show(struct device *dev, struct device_attribute *attr,
struct usb_device *udev;
udev = to_usb_device(dev);
- return sprintf(buf, "%d\n", udev->maxchild);
+ return sysfs_emit(buf, "%d\n", udev->maxchild);
}
static DEVICE_ATTR_RO(maxchild);
@@ -257,7 +257,7 @@ static ssize_t quirks_show(struct device *dev, struct device_attribute *attr,
struct usb_device *udev;
udev = to_usb_device(dev);
- return sprintf(buf, "0x%x\n", udev->quirks);
+ return sysfs_emit(buf, "0x%x\n", udev->quirks);
}
static DEVICE_ATTR_RO(quirks);
@@ -267,7 +267,7 @@ static ssize_t avoid_reset_quirk_show(struct device *dev,
struct usb_device *udev;
udev = to_usb_device(dev);
- return sprintf(buf, "%d\n", !!(udev->quirks & USB_QUIRK_RESET));
+ return sysfs_emit(buf, "%d\n", !!(udev->quirks & USB_QUIRK_RESET));
}
static ssize_t avoid_reset_quirk_store(struct device *dev,
@@ -297,7 +297,7 @@ static ssize_t urbnum_show(struct device *dev, struct device_attribute *attr,
struct usb_device *udev;
udev = to_usb_device(dev);
- return sprintf(buf, "%d\n", atomic_read(&udev->urbnum));
+ return sysfs_emit(buf, "%d\n", atomic_read(&udev->urbnum));
}
static DEVICE_ATTR_RO(urbnum);
@@ -305,8 +305,8 @@ static ssize_t ltm_capable_show(struct device *dev,
struct device_attribute *attr, char *buf)
{
if (usb_device_supports_ltm(to_usb_device(dev)))
- return sprintf(buf, "%s\n", "yes");
- return sprintf(buf, "%s\n", "no");
+ return sysfs_emit(buf, "%s\n", "yes");
+ return sysfs_emit(buf, "%s\n", "no");
}
static DEVICE_ATTR_RO(ltm_capable);
@@ -317,7 +317,7 @@ static ssize_t persist_show(struct device *dev, struct device_attribute *attr,
{
struct usb_device *udev = to_usb_device(dev);
- return sprintf(buf, "%d\n", udev->persist_enabled);
+ return sysfs_emit(buf, "%d\n", udev->persist_enabled);
}
static ssize_t persist_store(struct device *dev, struct device_attribute *attr,
@@ -372,7 +372,7 @@ static ssize_t connected_duration_show(struct device *dev,
{
struct usb_device *udev = to_usb_device(dev);
- return sprintf(buf, "%u\n",
+ return sysfs_emit(buf, "%u\n",
jiffies_to_msecs(jiffies - udev->connect_time));
}
static DEVICE_ATTR_RO(connected_duration);
@@ -394,14 +394,14 @@ static ssize_t active_duration_show(struct device *dev,
duration = jiffies_to_msecs(jiffies + udev->active_duration);
else
duration = jiffies_to_msecs(udev->active_duration);
- return sprintf(buf, "%u\n", duration);
+ return sysfs_emit(buf, "%u\n", duration);
}
static DEVICE_ATTR_RO(active_duration);
static ssize_t autosuspend_show(struct device *dev,
struct device_attribute *attr, char *buf)
{
- return sprintf(buf, "%d\n", dev->power.autosuspend_delay / 1000);
+ return sysfs_emit(buf, "%d\n", dev->power.autosuspend_delay / 1000);
}
static ssize_t autosuspend_store(struct device *dev,
@@ -442,7 +442,7 @@ static ssize_t level_show(struct device *dev, struct device_attribute *attr,
warn_level();
if (udev->state != USB_STATE_SUSPENDED && !udev->dev.power.runtime_auto)
p = on_string;
- return sprintf(buf, "%s\n", p);
+ return sysfs_emit(buf, "%s\n", p);
}
static ssize_t level_store(struct device *dev, struct device_attribute *attr,
@@ -490,7 +490,7 @@ static ssize_t usb2_hardware_lpm_show(struct device *dev,
else
p = "disabled";
- return sprintf(buf, "%s\n", p);
+ return sysfs_emit(buf, "%s\n", p);
}
static ssize_t usb2_hardware_lpm_store(struct device *dev,
@@ -529,7 +529,7 @@ static ssize_t usb2_lpm_l1_timeout_show(struct device *dev,
char *buf)
{
struct usb_device *udev = to_usb_device(dev);
- return sprintf(buf, "%d\n", udev->l1_params.timeout);
+ return sysfs_emit(buf, "%d\n", udev->l1_params.timeout);
}
static ssize_t usb2_lpm_l1_timeout_store(struct device *dev,
@@ -552,7 +552,7 @@ static ssize_t usb2_lpm_besl_show(struct device *dev,
struct device_attribute *attr, char *buf)
{
struct usb_device *udev = to_usb_device(dev);
- return sprintf(buf, "%d\n", udev->l1_params.besl);
+ return sysfs_emit(buf, "%d\n", udev->l1_params.besl);
}
static ssize_t usb2_lpm_besl_store(struct device *dev,
@@ -589,7 +589,7 @@ static ssize_t usb3_hardware_lpm_u1_show(struct device *dev,
usb_unlock_device(udev);
- return sprintf(buf, "%s\n", p);
+ return sysfs_emit(buf, "%s\n", p);
}
static DEVICE_ATTR_RO(usb3_hardware_lpm_u1);
@@ -611,7 +611,7 @@ static ssize_t usb3_hardware_lpm_u2_show(struct device *dev,
usb_unlock_device(udev);
- return sprintf(buf, "%s\n", p);
+ return sysfs_emit(buf, "%s\n", p);
}
static DEVICE_ATTR_RO(usb3_hardware_lpm_u2);
@@ -694,7 +694,7 @@ field##_show(struct device *dev, struct device_attribute *attr, \
struct usb_device *udev; \
\
udev = to_usb_device(dev); \
- return sprintf(buf, format_string, \
+ return sysfs_emit(buf, format_string, \
le16_to_cpu(udev->descriptor.field)); \
} \
static DEVICE_ATTR_RO(field)
@@ -711,7 +711,7 @@ field##_show(struct device *dev, struct device_attribute *attr, \
struct usb_device *udev; \
\
udev = to_usb_device(dev); \
- return sprintf(buf, format_string, udev->descriptor.field); \
+ return sysfs_emit(buf, format_string, udev->descriptor.field); \
} \
static DEVICE_ATTR_RO(field)
@@ -727,7 +727,7 @@ static ssize_t authorized_show(struct device *dev,
struct device_attribute *attr, char *buf)
{
struct usb_device *usb_dev = to_usb_device(dev);
- return snprintf(buf, PAGE_SIZE, "%u\n", usb_dev->authorized);
+ return sysfs_emit(buf, "%u\n", usb_dev->authorized);
}
/*
@@ -918,7 +918,7 @@ static ssize_t authorized_default_show(struct device *dev,
struct usb_hcd *hcd;
hcd = bus_to_hcd(usb_bus);
- return snprintf(buf, PAGE_SIZE, "%u\n", hcd->dev_policy);
+ return sysfs_emit(buf, "%u\n", hcd->dev_policy);
}
static ssize_t authorized_default_store(struct device *dev,
@@ -957,7 +957,7 @@ static ssize_t interface_authorized_default_show(struct device *dev,
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));
+ return sysfs_emit(buf, "%u\n", !!HCD_INTF_AUTHORIZED(hcd));
}
/*
@@ -1066,7 +1066,7 @@ iad_##field##_show(struct device *dev, struct device_attribute *attr, \
{ \
struct usb_interface *intf = to_usb_interface(dev); \
\
- return sprintf(buf, format_string, \
+ return sysfs_emit(buf, format_string, \
intf->intf_assoc->field); \
} \
static DEVICE_ATTR_RO(iad_##field)
@@ -1085,7 +1085,7 @@ field##_show(struct device *dev, struct device_attribute *attr, \
{ \
struct usb_interface *intf = to_usb_interface(dev); \
\
- return sprintf(buf, format_string, \
+ return sysfs_emit(buf, format_string, \
intf->cur_altsetting->desc.field); \
} \
static DEVICE_ATTR_RO(field)
@@ -1107,7 +1107,7 @@ static ssize_t interface_show(struct device *dev, struct device_attribute *attr,
string = READ_ONCE(intf->cur_altsetting->string);
if (!string)
return 0;
- return sprintf(buf, "%s\n", string);
+ return sysfs_emit(buf, "%s\n", string);
}
static DEVICE_ATTR_RO(interface);
@@ -1122,7 +1122,8 @@ static ssize_t modalias_show(struct device *dev, struct device_attribute *attr,
udev = interface_to_usbdev(intf);
alt = READ_ONCE(intf->cur_altsetting);
- return sprintf(buf, "usb:v%04Xp%04Xd%04Xdc%02Xdsc%02Xdp%02X"
+ return sysfs_emit(buf,
+ "usb:v%04Xp%04Xd%04Xdc%02Xdsc%02Xdp%02X"
"ic%02Xisc%02Xip%02Xin%02X\n",
le16_to_cpu(udev->descriptor.idVendor),
le16_to_cpu(udev->descriptor.idProduct),
@@ -1150,7 +1151,7 @@ static ssize_t supports_autosuspend_show(struct device *dev,
s = (!dev->driver || to_usb_driver(dev->driver)->supports_autosuspend);
device_unlock(dev);
- return sprintf(buf, "%u\n", s);
+ return sysfs_emit(buf, "%u\n", s);
}
static DEVICE_ATTR_RO(supports_autosuspend);
@@ -1163,7 +1164,7 @@ static ssize_t interface_authorized_show(struct device *dev,
{
struct usb_interface *intf = to_usb_interface(dev);
- return sprintf(buf, "%u\n", intf->authorized);
+ return sysfs_emit(buf, "%u\n", intf->authorized);
}
/*
diff --git a/drivers/usb/core/usb.c b/drivers/usb/core/usb.c
index 2f71636af6e1..11b15d7b357a 100644
--- a/drivers/usb/core/usb.c
+++ b/drivers/usb/core/usb.c
@@ -801,7 +801,7 @@ EXPORT_SYMBOL_GPL(usb_intf_get_dma_device);
* is simple:
*
* When locking both a device and its parent, always lock the
- * the parent first.
+ * parent first.
*/
/**