After commit 1cbd53c8cd85 ("usb: core: introduce per-port over-current counters") usb ports expose a sysfs value 'over_current_count' to user space. This value on its own is not very useful as it requires manual polling. As a solution, fire a udev event from the usb hub device that specifies the values 'OVER_CURRENT_PORT' and 'OVER_CURRENT_COUNT' that indicate the path of the usb port where the over-current event occurred and the value of 'over_current_count' in sysfs. Additionally, call sysfs_notify() so the sysfs value supports poll(). Signed-off-by: Jon Flatley <jflat@xxxxxxxxxxxx> --- Documentation/ABI/testing/sysfs-bus-usb | 9 ++++++- drivers/usb/core/hub.c | 36 +++++++++++++++++++++++++ 2 files changed, 44 insertions(+), 1 deletion(-) diff --git a/Documentation/ABI/testing/sysfs-bus-usb b/Documentation/ABI/testing/sysfs-bus-usb index 08d456e07b53..c4a70f532ec3 100644 --- a/Documentation/ABI/testing/sysfs-bus-usb +++ b/Documentation/ABI/testing/sysfs-bus-usb @@ -219,7 +219,14 @@ Description: ports and report them to the kernel. This attribute is to expose the number of over-current situation occurred on a specific port to user space. This file will contain an unsigned 32 bit value - which wraps to 0 after its maximum is reached. + which wraps to 0 after its maximum is reached. This file supports + poll() for monitoring changes to this value in user space. + + Any time this value changes the corresponding hub device will send a + udev event with the following attributes: + + OVER_CURRENT_PORT=/sys/bus/usb/devices/.../(hub interface)/portX + OVER_CURRENT_COUNT=[current value of this sysfs attribute] What: /sys/bus/usb/devices/.../(hub interface)/portX/usb3_lpm_permit Date: November 2015 diff --git a/drivers/usb/core/hub.c b/drivers/usb/core/hub.c index 462ce49f683a..7801bb30bdba 100644 --- a/drivers/usb/core/hub.c +++ b/drivers/usb/core/hub.c @@ -28,6 +28,7 @@ #include <linux/mutex.h> #include <linux/random.h> #include <linux/pm_qos.h> +#include <linux/kobject.h> #include <linux/uaccess.h> #include <asm/byteorder.h> @@ -5147,6 +5148,40 @@ static void hub_port_connect_change(struct usb_hub *hub, int port1, usb_lock_port(port_dev); } +/* Handle notifying userspace about hub over-current events */ +static void port_over_current_notify(struct usb_port *port_dev) +{ + static char *envp[] = { NULL, NULL, NULL }; + struct device *hub_dev; + char *port_dev_path; + + sysfs_notify(&port_dev->dev.kobj, NULL, "over_current_count"); + + hub_dev = port_dev->dev.parent; + + if (!hub_dev) + return; + + port_dev_path = kobject_get_path(&port_dev->dev.kobj, GFP_KERNEL); + if (!port_dev_path) + return; + + envp[0] = kasprintf(GFP_KERNEL, "OVER_CURRENT_PORT=%s", port_dev_path); + if (!envp[0]) + return; + + envp[1] = kasprintf(GFP_KERNEL, "OVER_CURRENT_COUNT=%u", + port_dev->over_current_count); + if (!envp[1]) + goto exit; + + kobject_uevent_env(&hub_dev->kobj, KOBJ_CHANGE, envp); + + kfree(envp[1]); +exit: + kfree(envp[0]); +} + static void port_event(struct usb_hub *hub, int port1) __must_hold(&port_dev->status_lock) { @@ -5189,6 +5224,7 @@ static void port_event(struct usb_hub *hub, int port1) if (portchange & USB_PORT_STAT_C_OVERCURRENT) { u16 status = 0, unused; port_dev->over_current_count++; + port_over_current_notify(port_dev); dev_dbg(&port_dev->dev, "over-current change #%u\n", port_dev->over_current_count); -- 2.19.0.444.g18242da7ef-goog