From patchwork Wed May 31 01:01:34 2023 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Roy Luo X-Patchwork-Id: 688186 Return-Path: X-Spam-Checker-Version: SpamAssassin 3.4.0 (2014-02-07) on aws-us-west-2-korg-lkml-1.web.codeaurora.org Received: from vger.kernel.org (vger.kernel.org [23.128.96.18]) by smtp.lore.kernel.org (Postfix) with ESMTP id 1C005C77B73 for ; Wed, 31 May 2023 01:01:45 +0000 (UTC) Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S233818AbjEaBBm (ORCPT ); Tue, 30 May 2023 21:01:42 -0400 Received: from lindbergh.monkeyblade.net ([23.128.96.19]:32952 "EHLO lindbergh.monkeyblade.net" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S230148AbjEaBBl (ORCPT ); Tue, 30 May 2023 21:01:41 -0400 Received: from mail-yw1-x1149.google.com (mail-yw1-x1149.google.com [IPv6:2607:f8b0:4864:20::1149]) by lindbergh.monkeyblade.net (Postfix) with ESMTPS id 26393F3 for ; Tue, 30 May 2023 18:01:40 -0700 (PDT) Received: by mail-yw1-x1149.google.com with SMTP id 00721157ae682-565a33c35f6so61076397b3.1 for ; Tue, 30 May 2023 18:01:40 -0700 (PDT) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=google.com; s=20221208; t=1685494899; x=1688086899; h=cc:to:from:subject:message-id:mime-version:date:from:to:cc:subject :date:message-id:reply-to; bh=pOgNjtx+eE8z0HyLOOopMR/pNC+bNEk+zPpSK0J4n9k=; b=JDIdmMFsxDbYH6UZ9pYMKLDLvGLA5btWn6PqhTajCP5mnUhuO3F+lv4s5hCoMd2/3Z DUD+ZoPZO6bEAuCz0W/OeJhnAV3lCws6xYfcRh/0rRajy71dwvtAbGdYtRN9C1Ir4G8z fwzoLE+HSupHZtTO3N7zk1j4zcHxtru9w5yTDJ0D3NsH8OkWefghpKdSBF+QgeIodr8h /dsnLh45Wtdx3l1SvfXxFViGEsRb7jaIgAUgSTCPMXKB99zCb30pjizGyDYmBwrCyKNW j/uhgmlCdM8YoumNo6NH7Em5GvkI1yuUms3mpDvHMPY+wLr2WirvSfCSI3zGWjh4GPRL N+AQ== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20221208; t=1685494899; x=1688086899; h=cc:to:from:subject:message-id:mime-version:date:x-gm-message-state :from:to:cc:subject:date:message-id:reply-to; bh=pOgNjtx+eE8z0HyLOOopMR/pNC+bNEk+zPpSK0J4n9k=; b=E6WtAdp/tEEknQBUFolvyNa8XGQmr0MihHCJwBX7rgv2K0lJeWHswSi2Hd0pAeReVQ vKM72fjqvxTzpRHdSiq2nF+GD63a9Hi8MiVveyXrYdUIZiATAEaxDvBZPDEmzG95ShjQ cC3p71LyDFnbrAWM9bexS3ybrXjwWAxZ50rceuJZbHpSQBTOsLCHZgdXbwDopVbjw9SR 2bNpWWIVwC8wvYqpkUMRVfpc1YOe2X5xvrx34pOykgykg/EgdS2oSFO0RJq2P4mp+SL4 xuBAoHn1cr6YnJsVZO5DruTWvcXrC9mSpxcCe8MUNg5iPvMnFQUA0exdNTF2I870ZMdU eBmA== X-Gm-Message-State: AC+VfDz5F1sYQul5cVoWd/qnmh2EjpwS8JTHKy6hbpfkppXrmQ3JMzV/ 2WJ+aGXDWGs6yTzmhtoj5EEhG/IKoEg= X-Google-Smtp-Source: ACHHUZ4o5VA1clccik8M9xqoVeJUOnvjLsXlRfBScsiPy6ow2aPvBMVI0IPGrcP5YsqNLGD2jTZimodU1wY= X-Received: from royluo-cloudtop0.c.googlers.com ([fda3:e722:ac3:cc00:14:4d90:c0a8:bb8]) (user=royluo job=sendgmr) by 2002:a81:ae45:0:b0:566:861:e451 with SMTP id g5-20020a81ae45000000b005660861e451mr2351315ywk.7.1685494899412; Tue, 30 May 2023 18:01:39 -0700 (PDT) Date: Wed, 31 May 2023 01:01:34 +0000 Mime-Version: 1.0 X-Mailer: git-send-email 2.41.0.rc0.172.g3f132b7071-goog Message-ID: <20230531010134.1092942-1-royluo@google.com> Subject: [RFC PATCH v2] usb: core: add sysfs entry for usb device state From: Roy Luo To: raychi@google.com, badhri@google.com, Greg Kroah-Hartman , Alan Stern , Benjamin Tissoires , Michael Grzeschik , Bastien Nocera , Mathias Nyman , Matthias Kaehlcke , Flavio Suligoi , Douglas Anderson , Christophe JAILLET Cc: linux-kernel@vger.kernel.org, linux-usb@vger.kernel.org, Roy Luo Precedence: bulk List-ID: X-Mailing-List: linux-usb@vger.kernel.org Expose usb device state to userland as the information is useful in detecting non-compliant setups and diagnosing enumeration failures. For example: - End-to-end signal integrity issues: the device would fail port reset repeatedly and thus be stuck in POWERED state. - Charge-only cables (missing D+/D- lines): the device would never enter POWERED state as the HC would not see any pullup. What's the status quo? We do have error logs such as "Cannot enable. Maybe the USB cable is bad?" to flag potential setup issues, but there's no good way to expose them to userspace. Why add a sysfs entry in struct usb_port instead of struct usb_device? The struct usb_device is not device_add() to the system until it's in ADDRESS state hence we would miss the first two states. The struct usb_port is a better place to keep the information because its life cycle is longer than the struct usb_device that is attached to the port. Signed-off-by: Roy Luo --- Changes since v1: * Address Greg Kroah-Hartman's comment: replace sysfs_notify with sysfs_notify_dirent and remove the workqueue. * Address Alan Stern's comment: replicate port_dev->child->state in port_dev->state instead of using a pointer to link them. --- Documentation/ABI/testing/sysfs-bus-usb | 9 +++++++++ drivers/usb/core/hub.c | 19 +++++++++++++++++++ drivers/usb/core/hub.h | 2 ++ drivers/usb/core/port.c | 11 +++++++++++ 4 files changed, 41 insertions(+) base-commit: 933174ae28ba72ab8de5b35cb7c98fc211235096 diff --git a/Documentation/ABI/testing/sysfs-bus-usb b/Documentation/ABI/testing/sysfs-bus-usb index cb172db41b34..155770f18f9c 100644 --- a/Documentation/ABI/testing/sysfs-bus-usb +++ b/Documentation/ABI/testing/sysfs-bus-usb @@ -292,6 +292,15 @@ Description: which is marked with early_stop has failed to initialize, it will ignore all future connections until this attribute is clear. +What: /sys/bus/usb/devices/...//port/state +Date: May 2023 +Contact: Roy Luo +Description: + Indicates current state of the USB device attached to the port. Valid + states are: 'not-attached', 'attached', 'powered', + 'reconnecting', 'unauthenticated', 'default', 'addressed', + 'configured', and 'suspended'. + What: /sys/bus/usb/devices/.../power/usb2_lpm_l1_timeout Date: May 2013 Contact: Mathias Nyman diff --git a/drivers/usb/core/hub.c b/drivers/usb/core/hub.c index 97a0f8faea6e..35d94288726b 100644 --- a/drivers/usb/core/hub.c +++ b/drivers/usb/core/hub.c @@ -2018,6 +2018,23 @@ bool usb_device_is_owned(struct usb_device *udev) return !!hub->ports[udev->portnum - 1]->port_owner; } +static void update_port_device_state(struct usb_device *udev) +{ + struct usb_port *port_dev = NULL; + struct usb_hub *hub = NULL; + struct kernfs_node *state_node = NULL; + + if (udev->parent) { + hub = usb_hub_to_struct_hub(udev->parent); + port_dev = hub->ports[udev->portnum - 1]; + WRITE_ONCE(port_dev->state, udev->state); + state_node = sysfs_get_dirent(port_dev->dev.kobj.sd, "state"); + if (state_node) { + sysfs_notify_dirent(state_node); + } + } +} + static void recursively_mark_NOTATTACHED(struct usb_device *udev) { struct usb_hub *hub = usb_hub_to_struct_hub(udev); @@ -2030,6 +2047,7 @@ static void recursively_mark_NOTATTACHED(struct usb_device *udev) if (udev->state == USB_STATE_SUSPENDED) udev->active_duration -= jiffies; udev->state = USB_STATE_NOTATTACHED; + update_port_device_state(udev); } /** @@ -2086,6 +2104,7 @@ void usb_set_device_state(struct usb_device *udev, udev->state != USB_STATE_SUSPENDED) udev->active_duration += jiffies; udev->state = new_state; + update_port_device_state(udev); } else recursively_mark_NOTATTACHED(udev); spin_unlock_irqrestore(&device_state_lock, flags); diff --git a/drivers/usb/core/hub.h b/drivers/usb/core/hub.h index e23833562e4f..cd13fe189726 100644 --- a/drivers/usb/core/hub.h +++ b/drivers/usb/core/hub.h @@ -84,6 +84,7 @@ struct usb_hub { * @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 + * @state: device state of the usb device attached to the port * @location: opaque representation of platform connector location * @status_lock: synchronize port_event() vs usb_port_{suspend|resume} * @portnum: port index num based one @@ -100,6 +101,7 @@ struct usb_port { struct usb_port *peer; struct dev_pm_qos_request *req; enum usb_port_connect_type connect_type; + enum usb_device_state state; usb_port_location_t location; struct mutex status_lock; u32 over_current_count; diff --git a/drivers/usb/core/port.c b/drivers/usb/core/port.c index 06a8f1f84f6f..42b75ac09ccb 100644 --- a/drivers/usb/core/port.c +++ b/drivers/usb/core/port.c @@ -160,6 +160,16 @@ static ssize_t connect_type_show(struct device *dev, } static DEVICE_ATTR_RO(connect_type); +static ssize_t state_show(struct device *dev, + struct device_attribute *attr, char *buf) +{ + struct usb_port *port_dev = to_usb_port(dev); + enum usb_device_state state = READ_ONCE(port_dev->state); + + return sprintf(buf, "%s\n", usb_state_string(state)); +} +static DEVICE_ATTR_RO(state); + static ssize_t over_current_count_show(struct device *dev, struct device_attribute *attr, char *buf) { @@ -259,6 +269,7 @@ static DEVICE_ATTR_RW(usb3_lpm_permit); static struct attribute *port_dev_attrs[] = { &dev_attr_connect_type.attr, + &dev_attr_state.attr, &dev_attr_location.attr, &dev_attr_quirks.attr, &dev_attr_over_current_count.attr,