From: Alan Stern <stern@rowland.harvard.edu>
To: Michael Grzeschik <m.grzeschik@pengutronix.de>
Cc: linux-usb@vger.kernel.org, gregkh@linuxfoundation.org,
kernel@pengutronix.de
Subject: Re: [PATCH v2] usb: hub: port: add sysfs entry to switch port power
Date: Thu, 2 Jun 2022 10:39:54 -0400 [thread overview]
Message-ID: <YpjLusnGk8ZBlGGd@rowland.harvard.edu> (raw)
In-Reply-To: <20220602012731.2942309-1-m.grzeschik@pengutronix.de>
On Thu, Jun 02, 2022 at 03:27:31AM +0200, Michael Grzeschik wrote:
> In some cases the port of an hub needs to be disabled or switched off
> and on again. E.g. when the connected device needs to be re-enumerated.
> Or it needs to be explicitly disabled while the rest of the usb tree
> stays working.
>
> For this purpose this patch adds an sysfs switch to enable/disable the
> port on any hub. In the case the hub is supporting power switching, the
> power line will be disabled to the connected device.
>
> When the port gets disabled, the associated device gets disconnected and
> removed from the logical usb tree. No further device will be enumerated
> on that port until the port gets enabled again.
A lot better than the description in the earlier patch version!
> Signed-off-by: Michael Grzeschik <m.grzeschik@pengutronix.de>
>
> ---
> v1 -> v2:
> - improved patch description
> - moved usb_hub_set_port_power to end of function
> - renamed value to set
> - removed udev variable
> - added usb_set_configuration set to -1 before removing device
> - calling autosuspend of udev before usb_disconnect, ensuring hub_suspend succeeds
> - removed port_dev->child = NULL assignment
> - directly returning count on no failure success
> - removed test for hub->in_reset
> - using usb_autopm_get_interface/usb_autopm_put_interface around hub handling
> - locking usb_disconnect call
> - using &port_dev->child instead of local udev pointer
> - added Documentation/ABI
>
> Documentation/ABI/testing/sysfs-bus-usb | 13 +++++++
> drivers/usb/core/port.c | 49 +++++++++++++++++++++++++
> 2 files changed, 62 insertions(+)
>
> diff --git a/Documentation/ABI/testing/sysfs-bus-usb b/Documentation/ABI/testing/sysfs-bus-usb
> index 7efe31ed3a25c7..9c87ca50bcab79 100644
> --- a/Documentation/ABI/testing/sysfs-bus-usb
> +++ b/Documentation/ABI/testing/sysfs-bus-usb
> @@ -253,6 +253,19 @@ Description:
> only if the system firmware is capable of describing the
> connection between a port and its connector.
>
> +What: /sys/bus/usb/devices/.../<hub_interface>/port<X>/port_power
> +Date: June 2022
> +Contact: Michael Grzeschik <m.grzeschik@pengutronix.de>
> +Description:
> + To disable or enable a hub port the sysfs file port_power exists
> + for each hub port. When disabling the hub port it is unusable anymore,
> + which means no enumeration will take place on this port until enabled again.
> +
> + When disabling the port set (<hubdev-portX>/port_power to 0) the
> + USB_PORT_FEAT_C_CONNECTION, USB_PORT_FEAT_POWER and (for high speed hubs) the
> + USB_PORT_FEAT_C_ENABLE port features are cleared. It all gets reversed when the
> + port will be enabled again (set <hubdev-portX>/port_power to 1).
This description is rather disorganized. I'd prefer something like this:
This file controls Vbus power to USB ports (but only on hubs
that support power switching -- most hubs don't support it).
When power to a port is turned off, the port is unusable:
Devices attached to the port will not be detected,
initialized, or enumerated.
> +
> What: /sys/bus/usb/devices/.../power/usb2_lpm_l1_timeout
> Date: May 2013
> Contact: Mathias Nyman <mathias.nyman@linux.intel.com>
> diff --git a/drivers/usb/core/port.c b/drivers/usb/core/port.c
> index d5bc36ca5b1f77..3e707db88291e9 100644
> --- a/drivers/usb/core/port.c
> +++ b/drivers/usb/core/port.c
> @@ -17,6 +17,54 @@ static int usb_port_block_power_off;
>
> static const struct attribute_group *port_dev_group[];
>
> +static ssize_t port_power_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 set;
> + int rc;
> +
> + if (!hub)
> + return -EINVAL;
> +
> + rc = strtobool(buf, &set);
> + if (rc)
> + return rc;
> +
> + rc = usb_autopm_get_interface(intf);
> + if (rc < 0)
> + return rc;
You should call usb_lock_device(hdev) here, not later. And after the hub
has been locked, you have to check whether hub->disconnected has been set.
> +
> + if (!set) {
> + 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);
These should be cleared after the port power is disabled, not before.
> + if (port_dev->child) {
> + usb_set_configuration(port_dev->child, -1);
> + usb_autosuspend_device(port_dev->child);
I don't see why either of these is needed. (In fact, some devices
malfunction when you try to unconfigure them.)
> + usb_lock_device(hdev);
> + usb_disconnect(&port_dev->child);
> + usb_unlock_device(hdev);
> + }
> + }
> +
> + rc = usb_hub_set_port_power(hdev, hub, port1, set);
And call usb_unlock_device(hdev) here, after the port feature flags have
been cleared.
> + if (rc) {
> + usb_autopm_put_interface(intf);
> + return rc;
> + }
> +
> + usb_autopm_put_interface(intf);
> +
> + return count;
Simpler:
if (rc == 0)
rc = count;
usb_autopm_put_interface(intf);
return rc;
> +}
> +static DEVICE_ATTR_WO(port_power);
> +
> static ssize_t location_show(struct device *dev,
> struct device_attribute *attr, char *buf)
> {
> @@ -153,6 +201,7 @@ static struct attribute *port_dev_attrs[] = {
> &dev_attr_location.attr,
> &dev_attr_quirks.attr,
> &dev_attr_over_current_count.attr,
> + &dev_attr_port_power.attr,
> NULL,
> };
You might want to disable the new sysfs file (don't create it or have it
return -EOPNOTSUPP) if the hub doesn't support per-port power switching.
Finally, you should add a test to port_event() in hub.c, probably where
the pm_runtime_active() check is. If the port is powered off, return
before doing any of the warm_reset or connect_change processing.
Alan Stern
next prev parent reply other threads:[~2022-06-02 14:40 UTC|newest]
Thread overview: 6+ messages / expand[flat|nested] mbox.gz Atom feed top
2022-06-02 1:27 [PATCH v2] usb: hub: port: add sysfs entry to switch port power Michael Grzeschik
2022-06-02 14:39 ` Alan Stern [this message]
2022-06-02 14:59 ` Michael Grzeschik
2022-06-02 19:16 ` Alan Stern
2022-06-02 21:34 ` Michael Grzeschik
2022-06-03 0:25 ` Alan Stern
Reply instructions:
You may reply publicly to this message via plain-text email
using any one of the following methods:
* Save the following mbox file, import it into your mail client,
and reply-to-all from there: mbox
Avoid top-posting and favor interleaved quoting:
https://en.wikipedia.org/wiki/Posting_style#Interleaved_style
* Reply using the --to, --cc, and --in-reply-to
switches of git-send-email(1):
git send-email \
--in-reply-to=YpjLusnGk8ZBlGGd@rowland.harvard.edu \
--to=stern@rowland.harvard.edu \
--cc=gregkh@linuxfoundation.org \
--cc=kernel@pengutronix.de \
--cc=linux-usb@vger.kernel.org \
--cc=m.grzeschik@pengutronix.de \
/path/to/YOUR_REPLY
https://kernel.org/pub/software/scm/git/docs/git-send-email.html
* If your mail client supports setting the In-Reply-To header
via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line
before the message body.
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox