162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0 262306a36Sopenharmony_ci/* 362306a36Sopenharmony_ci * drivers/usb/core/sysfs.c 462306a36Sopenharmony_ci * 562306a36Sopenharmony_ci * (C) Copyright 2002 David Brownell 662306a36Sopenharmony_ci * (C) Copyright 2002,2004 Greg Kroah-Hartman 762306a36Sopenharmony_ci * (C) Copyright 2002,2004 IBM Corp. 862306a36Sopenharmony_ci * 962306a36Sopenharmony_ci * All of the sysfs file attributes for usb devices and interfaces. 1062306a36Sopenharmony_ci * 1162306a36Sopenharmony_ci * Released under the GPLv2 only. 1262306a36Sopenharmony_ci */ 1362306a36Sopenharmony_ci 1462306a36Sopenharmony_ci 1562306a36Sopenharmony_ci#include <linux/kernel.h> 1662306a36Sopenharmony_ci#include <linux/kstrtox.h> 1762306a36Sopenharmony_ci#include <linux/string.h> 1862306a36Sopenharmony_ci#include <linux/usb.h> 1962306a36Sopenharmony_ci#include <linux/usb/hcd.h> 2062306a36Sopenharmony_ci#include <linux/usb/quirks.h> 2162306a36Sopenharmony_ci#include <linux/of.h> 2262306a36Sopenharmony_ci#include "usb.h" 2362306a36Sopenharmony_ci 2462306a36Sopenharmony_ci/* Active configuration fields */ 2562306a36Sopenharmony_ci#define usb_actconfig_show(field, format_string) \ 2662306a36Sopenharmony_cistatic ssize_t field##_show(struct device *dev, \ 2762306a36Sopenharmony_ci struct device_attribute *attr, char *buf) \ 2862306a36Sopenharmony_ci{ \ 2962306a36Sopenharmony_ci struct usb_device *udev; \ 3062306a36Sopenharmony_ci struct usb_host_config *actconfig; \ 3162306a36Sopenharmony_ci ssize_t rc; \ 3262306a36Sopenharmony_ci \ 3362306a36Sopenharmony_ci udev = to_usb_device(dev); \ 3462306a36Sopenharmony_ci rc = usb_lock_device_interruptible(udev); \ 3562306a36Sopenharmony_ci if (rc < 0) \ 3662306a36Sopenharmony_ci return -EINTR; \ 3762306a36Sopenharmony_ci actconfig = udev->actconfig; \ 3862306a36Sopenharmony_ci if (actconfig) \ 3962306a36Sopenharmony_ci rc = sysfs_emit(buf, format_string, \ 4062306a36Sopenharmony_ci actconfig->desc.field); \ 4162306a36Sopenharmony_ci usb_unlock_device(udev); \ 4262306a36Sopenharmony_ci return rc; \ 4362306a36Sopenharmony_ci} \ 4462306a36Sopenharmony_ci 4562306a36Sopenharmony_ci#define usb_actconfig_attr(field, format_string) \ 4662306a36Sopenharmony_ci usb_actconfig_show(field, format_string) \ 4762306a36Sopenharmony_ci static DEVICE_ATTR_RO(field) 4862306a36Sopenharmony_ci 4962306a36Sopenharmony_ciusb_actconfig_attr(bNumInterfaces, "%2d\n"); 5062306a36Sopenharmony_ciusb_actconfig_attr(bmAttributes, "%2x\n"); 5162306a36Sopenharmony_ci 5262306a36Sopenharmony_cistatic ssize_t bMaxPower_show(struct device *dev, 5362306a36Sopenharmony_ci struct device_attribute *attr, char *buf) 5462306a36Sopenharmony_ci{ 5562306a36Sopenharmony_ci struct usb_device *udev; 5662306a36Sopenharmony_ci struct usb_host_config *actconfig; 5762306a36Sopenharmony_ci ssize_t rc; 5862306a36Sopenharmony_ci 5962306a36Sopenharmony_ci udev = to_usb_device(dev); 6062306a36Sopenharmony_ci rc = usb_lock_device_interruptible(udev); 6162306a36Sopenharmony_ci if (rc < 0) 6262306a36Sopenharmony_ci return -EINTR; 6362306a36Sopenharmony_ci actconfig = udev->actconfig; 6462306a36Sopenharmony_ci if (actconfig) 6562306a36Sopenharmony_ci rc = sysfs_emit(buf, "%dmA\n", usb_get_max_power(udev, actconfig)); 6662306a36Sopenharmony_ci usb_unlock_device(udev); 6762306a36Sopenharmony_ci return rc; 6862306a36Sopenharmony_ci} 6962306a36Sopenharmony_cistatic DEVICE_ATTR_RO(bMaxPower); 7062306a36Sopenharmony_ci 7162306a36Sopenharmony_cistatic ssize_t configuration_show(struct device *dev, 7262306a36Sopenharmony_ci struct device_attribute *attr, char *buf) 7362306a36Sopenharmony_ci{ 7462306a36Sopenharmony_ci struct usb_device *udev; 7562306a36Sopenharmony_ci struct usb_host_config *actconfig; 7662306a36Sopenharmony_ci ssize_t rc; 7762306a36Sopenharmony_ci 7862306a36Sopenharmony_ci udev = to_usb_device(dev); 7962306a36Sopenharmony_ci rc = usb_lock_device_interruptible(udev); 8062306a36Sopenharmony_ci if (rc < 0) 8162306a36Sopenharmony_ci return -EINTR; 8262306a36Sopenharmony_ci actconfig = udev->actconfig; 8362306a36Sopenharmony_ci if (actconfig && actconfig->string) 8462306a36Sopenharmony_ci rc = sysfs_emit(buf, "%s\n", actconfig->string); 8562306a36Sopenharmony_ci usb_unlock_device(udev); 8662306a36Sopenharmony_ci return rc; 8762306a36Sopenharmony_ci} 8862306a36Sopenharmony_cistatic DEVICE_ATTR_RO(configuration); 8962306a36Sopenharmony_ci 9062306a36Sopenharmony_ci/* configuration value is always present, and r/w */ 9162306a36Sopenharmony_ciusb_actconfig_show(bConfigurationValue, "%u\n"); 9262306a36Sopenharmony_ci 9362306a36Sopenharmony_cistatic ssize_t bConfigurationValue_store(struct device *dev, 9462306a36Sopenharmony_ci struct device_attribute *attr, 9562306a36Sopenharmony_ci const char *buf, size_t count) 9662306a36Sopenharmony_ci{ 9762306a36Sopenharmony_ci struct usb_device *udev = to_usb_device(dev); 9862306a36Sopenharmony_ci int config, value, rc; 9962306a36Sopenharmony_ci 10062306a36Sopenharmony_ci if (sscanf(buf, "%d", &config) != 1 || config < -1 || config > 255) 10162306a36Sopenharmony_ci return -EINVAL; 10262306a36Sopenharmony_ci rc = usb_lock_device_interruptible(udev); 10362306a36Sopenharmony_ci if (rc < 0) 10462306a36Sopenharmony_ci return -EINTR; 10562306a36Sopenharmony_ci value = usb_set_configuration(udev, config); 10662306a36Sopenharmony_ci usb_unlock_device(udev); 10762306a36Sopenharmony_ci return (value < 0) ? value : count; 10862306a36Sopenharmony_ci} 10962306a36Sopenharmony_cistatic DEVICE_ATTR_IGNORE_LOCKDEP(bConfigurationValue, S_IRUGO | S_IWUSR, 11062306a36Sopenharmony_ci bConfigurationValue_show, bConfigurationValue_store); 11162306a36Sopenharmony_ci 11262306a36Sopenharmony_ci#ifdef CONFIG_OF 11362306a36Sopenharmony_cistatic ssize_t devspec_show(struct device *dev, struct device_attribute *attr, 11462306a36Sopenharmony_ci char *buf) 11562306a36Sopenharmony_ci{ 11662306a36Sopenharmony_ci struct device_node *of_node = dev->of_node; 11762306a36Sopenharmony_ci 11862306a36Sopenharmony_ci return sysfs_emit(buf, "%pOF\n", of_node); 11962306a36Sopenharmony_ci} 12062306a36Sopenharmony_cistatic DEVICE_ATTR_RO(devspec); 12162306a36Sopenharmony_ci#endif 12262306a36Sopenharmony_ci 12362306a36Sopenharmony_ci/* String fields */ 12462306a36Sopenharmony_ci#define usb_string_attr(name) \ 12562306a36Sopenharmony_cistatic ssize_t name##_show(struct device *dev, \ 12662306a36Sopenharmony_ci struct device_attribute *attr, char *buf) \ 12762306a36Sopenharmony_ci{ \ 12862306a36Sopenharmony_ci struct usb_device *udev; \ 12962306a36Sopenharmony_ci int retval; \ 13062306a36Sopenharmony_ci \ 13162306a36Sopenharmony_ci udev = to_usb_device(dev); \ 13262306a36Sopenharmony_ci retval = usb_lock_device_interruptible(udev); \ 13362306a36Sopenharmony_ci if (retval < 0) \ 13462306a36Sopenharmony_ci return -EINTR; \ 13562306a36Sopenharmony_ci retval = sysfs_emit(buf, "%s\n", udev->name); \ 13662306a36Sopenharmony_ci usb_unlock_device(udev); \ 13762306a36Sopenharmony_ci return retval; \ 13862306a36Sopenharmony_ci} \ 13962306a36Sopenharmony_cistatic DEVICE_ATTR_RO(name) 14062306a36Sopenharmony_ci 14162306a36Sopenharmony_ciusb_string_attr(product); 14262306a36Sopenharmony_ciusb_string_attr(manufacturer); 14362306a36Sopenharmony_ciusb_string_attr(serial); 14462306a36Sopenharmony_ci 14562306a36Sopenharmony_cistatic ssize_t speed_show(struct device *dev, struct device_attribute *attr, 14662306a36Sopenharmony_ci char *buf) 14762306a36Sopenharmony_ci{ 14862306a36Sopenharmony_ci struct usb_device *udev; 14962306a36Sopenharmony_ci char *speed; 15062306a36Sopenharmony_ci 15162306a36Sopenharmony_ci udev = to_usb_device(dev); 15262306a36Sopenharmony_ci 15362306a36Sopenharmony_ci switch (udev->speed) { 15462306a36Sopenharmony_ci case USB_SPEED_LOW: 15562306a36Sopenharmony_ci speed = "1.5"; 15662306a36Sopenharmony_ci break; 15762306a36Sopenharmony_ci case USB_SPEED_UNKNOWN: 15862306a36Sopenharmony_ci case USB_SPEED_FULL: 15962306a36Sopenharmony_ci speed = "12"; 16062306a36Sopenharmony_ci break; 16162306a36Sopenharmony_ci case USB_SPEED_HIGH: 16262306a36Sopenharmony_ci speed = "480"; 16362306a36Sopenharmony_ci break; 16462306a36Sopenharmony_ci case USB_SPEED_SUPER: 16562306a36Sopenharmony_ci speed = "5000"; 16662306a36Sopenharmony_ci break; 16762306a36Sopenharmony_ci case USB_SPEED_SUPER_PLUS: 16862306a36Sopenharmony_ci if (udev->ssp_rate == USB_SSP_GEN_2x2) 16962306a36Sopenharmony_ci speed = "20000"; 17062306a36Sopenharmony_ci else 17162306a36Sopenharmony_ci speed = "10000"; 17262306a36Sopenharmony_ci break; 17362306a36Sopenharmony_ci default: 17462306a36Sopenharmony_ci speed = "unknown"; 17562306a36Sopenharmony_ci } 17662306a36Sopenharmony_ci return sysfs_emit(buf, "%s\n", speed); 17762306a36Sopenharmony_ci} 17862306a36Sopenharmony_cistatic DEVICE_ATTR_RO(speed); 17962306a36Sopenharmony_ci 18062306a36Sopenharmony_cistatic ssize_t rx_lanes_show(struct device *dev, struct device_attribute *attr, 18162306a36Sopenharmony_ci char *buf) 18262306a36Sopenharmony_ci{ 18362306a36Sopenharmony_ci struct usb_device *udev; 18462306a36Sopenharmony_ci 18562306a36Sopenharmony_ci udev = to_usb_device(dev); 18662306a36Sopenharmony_ci return sysfs_emit(buf, "%d\n", udev->rx_lanes); 18762306a36Sopenharmony_ci} 18862306a36Sopenharmony_cistatic DEVICE_ATTR_RO(rx_lanes); 18962306a36Sopenharmony_ci 19062306a36Sopenharmony_cistatic ssize_t tx_lanes_show(struct device *dev, struct device_attribute *attr, 19162306a36Sopenharmony_ci char *buf) 19262306a36Sopenharmony_ci{ 19362306a36Sopenharmony_ci struct usb_device *udev; 19462306a36Sopenharmony_ci 19562306a36Sopenharmony_ci udev = to_usb_device(dev); 19662306a36Sopenharmony_ci return sysfs_emit(buf, "%d\n", udev->tx_lanes); 19762306a36Sopenharmony_ci} 19862306a36Sopenharmony_cistatic DEVICE_ATTR_RO(tx_lanes); 19962306a36Sopenharmony_ci 20062306a36Sopenharmony_cistatic ssize_t busnum_show(struct device *dev, struct device_attribute *attr, 20162306a36Sopenharmony_ci char *buf) 20262306a36Sopenharmony_ci{ 20362306a36Sopenharmony_ci struct usb_device *udev; 20462306a36Sopenharmony_ci 20562306a36Sopenharmony_ci udev = to_usb_device(dev); 20662306a36Sopenharmony_ci return sysfs_emit(buf, "%d\n", udev->bus->busnum); 20762306a36Sopenharmony_ci} 20862306a36Sopenharmony_cistatic DEVICE_ATTR_RO(busnum); 20962306a36Sopenharmony_ci 21062306a36Sopenharmony_cistatic ssize_t devnum_show(struct device *dev, struct device_attribute *attr, 21162306a36Sopenharmony_ci char *buf) 21262306a36Sopenharmony_ci{ 21362306a36Sopenharmony_ci struct usb_device *udev; 21462306a36Sopenharmony_ci 21562306a36Sopenharmony_ci udev = to_usb_device(dev); 21662306a36Sopenharmony_ci return sysfs_emit(buf, "%d\n", udev->devnum); 21762306a36Sopenharmony_ci} 21862306a36Sopenharmony_cistatic DEVICE_ATTR_RO(devnum); 21962306a36Sopenharmony_ci 22062306a36Sopenharmony_cistatic ssize_t devpath_show(struct device *dev, struct device_attribute *attr, 22162306a36Sopenharmony_ci char *buf) 22262306a36Sopenharmony_ci{ 22362306a36Sopenharmony_ci struct usb_device *udev; 22462306a36Sopenharmony_ci 22562306a36Sopenharmony_ci udev = to_usb_device(dev); 22662306a36Sopenharmony_ci return sysfs_emit(buf, "%s\n", udev->devpath); 22762306a36Sopenharmony_ci} 22862306a36Sopenharmony_cistatic DEVICE_ATTR_RO(devpath); 22962306a36Sopenharmony_ci 23062306a36Sopenharmony_cistatic ssize_t version_show(struct device *dev, struct device_attribute *attr, 23162306a36Sopenharmony_ci char *buf) 23262306a36Sopenharmony_ci{ 23362306a36Sopenharmony_ci struct usb_device *udev; 23462306a36Sopenharmony_ci u16 bcdUSB; 23562306a36Sopenharmony_ci 23662306a36Sopenharmony_ci udev = to_usb_device(dev); 23762306a36Sopenharmony_ci bcdUSB = le16_to_cpu(udev->descriptor.bcdUSB); 23862306a36Sopenharmony_ci return sysfs_emit(buf, "%2x.%02x\n", bcdUSB >> 8, bcdUSB & 0xff); 23962306a36Sopenharmony_ci} 24062306a36Sopenharmony_cistatic DEVICE_ATTR_RO(version); 24162306a36Sopenharmony_ci 24262306a36Sopenharmony_cistatic ssize_t maxchild_show(struct device *dev, struct device_attribute *attr, 24362306a36Sopenharmony_ci char *buf) 24462306a36Sopenharmony_ci{ 24562306a36Sopenharmony_ci struct usb_device *udev; 24662306a36Sopenharmony_ci 24762306a36Sopenharmony_ci udev = to_usb_device(dev); 24862306a36Sopenharmony_ci return sysfs_emit(buf, "%d\n", udev->maxchild); 24962306a36Sopenharmony_ci} 25062306a36Sopenharmony_cistatic DEVICE_ATTR_RO(maxchild); 25162306a36Sopenharmony_ci 25262306a36Sopenharmony_cistatic ssize_t quirks_show(struct device *dev, struct device_attribute *attr, 25362306a36Sopenharmony_ci char *buf) 25462306a36Sopenharmony_ci{ 25562306a36Sopenharmony_ci struct usb_device *udev; 25662306a36Sopenharmony_ci 25762306a36Sopenharmony_ci udev = to_usb_device(dev); 25862306a36Sopenharmony_ci return sysfs_emit(buf, "0x%x\n", udev->quirks); 25962306a36Sopenharmony_ci} 26062306a36Sopenharmony_cistatic DEVICE_ATTR_RO(quirks); 26162306a36Sopenharmony_ci 26262306a36Sopenharmony_cistatic ssize_t avoid_reset_quirk_show(struct device *dev, 26362306a36Sopenharmony_ci struct device_attribute *attr, char *buf) 26462306a36Sopenharmony_ci{ 26562306a36Sopenharmony_ci struct usb_device *udev; 26662306a36Sopenharmony_ci 26762306a36Sopenharmony_ci udev = to_usb_device(dev); 26862306a36Sopenharmony_ci return sysfs_emit(buf, "%d\n", !!(udev->quirks & USB_QUIRK_RESET)); 26962306a36Sopenharmony_ci} 27062306a36Sopenharmony_ci 27162306a36Sopenharmony_cistatic ssize_t avoid_reset_quirk_store(struct device *dev, 27262306a36Sopenharmony_ci struct device_attribute *attr, 27362306a36Sopenharmony_ci const char *buf, size_t count) 27462306a36Sopenharmony_ci{ 27562306a36Sopenharmony_ci struct usb_device *udev = to_usb_device(dev); 27662306a36Sopenharmony_ci int val, rc; 27762306a36Sopenharmony_ci 27862306a36Sopenharmony_ci if (sscanf(buf, "%d", &val) != 1 || val < 0 || val > 1) 27962306a36Sopenharmony_ci return -EINVAL; 28062306a36Sopenharmony_ci rc = usb_lock_device_interruptible(udev); 28162306a36Sopenharmony_ci if (rc < 0) 28262306a36Sopenharmony_ci return -EINTR; 28362306a36Sopenharmony_ci if (val) 28462306a36Sopenharmony_ci udev->quirks |= USB_QUIRK_RESET; 28562306a36Sopenharmony_ci else 28662306a36Sopenharmony_ci udev->quirks &= ~USB_QUIRK_RESET; 28762306a36Sopenharmony_ci usb_unlock_device(udev); 28862306a36Sopenharmony_ci return count; 28962306a36Sopenharmony_ci} 29062306a36Sopenharmony_cistatic DEVICE_ATTR_RW(avoid_reset_quirk); 29162306a36Sopenharmony_ci 29262306a36Sopenharmony_cistatic ssize_t urbnum_show(struct device *dev, struct device_attribute *attr, 29362306a36Sopenharmony_ci char *buf) 29462306a36Sopenharmony_ci{ 29562306a36Sopenharmony_ci struct usb_device *udev; 29662306a36Sopenharmony_ci 29762306a36Sopenharmony_ci udev = to_usb_device(dev); 29862306a36Sopenharmony_ci return sysfs_emit(buf, "%d\n", atomic_read(&udev->urbnum)); 29962306a36Sopenharmony_ci} 30062306a36Sopenharmony_cistatic DEVICE_ATTR_RO(urbnum); 30162306a36Sopenharmony_ci 30262306a36Sopenharmony_cistatic ssize_t ltm_capable_show(struct device *dev, 30362306a36Sopenharmony_ci struct device_attribute *attr, char *buf) 30462306a36Sopenharmony_ci{ 30562306a36Sopenharmony_ci if (usb_device_supports_ltm(to_usb_device(dev))) 30662306a36Sopenharmony_ci return sysfs_emit(buf, "%s\n", "yes"); 30762306a36Sopenharmony_ci return sysfs_emit(buf, "%s\n", "no"); 30862306a36Sopenharmony_ci} 30962306a36Sopenharmony_cistatic DEVICE_ATTR_RO(ltm_capable); 31062306a36Sopenharmony_ci 31162306a36Sopenharmony_ci#ifdef CONFIG_PM 31262306a36Sopenharmony_ci 31362306a36Sopenharmony_cistatic ssize_t persist_show(struct device *dev, struct device_attribute *attr, 31462306a36Sopenharmony_ci char *buf) 31562306a36Sopenharmony_ci{ 31662306a36Sopenharmony_ci struct usb_device *udev = to_usb_device(dev); 31762306a36Sopenharmony_ci 31862306a36Sopenharmony_ci return sysfs_emit(buf, "%d\n", udev->persist_enabled); 31962306a36Sopenharmony_ci} 32062306a36Sopenharmony_ci 32162306a36Sopenharmony_cistatic ssize_t persist_store(struct device *dev, struct device_attribute *attr, 32262306a36Sopenharmony_ci const char *buf, size_t count) 32362306a36Sopenharmony_ci{ 32462306a36Sopenharmony_ci struct usb_device *udev = to_usb_device(dev); 32562306a36Sopenharmony_ci int value, rc; 32662306a36Sopenharmony_ci 32762306a36Sopenharmony_ci /* Hubs are always enabled for USB_PERSIST */ 32862306a36Sopenharmony_ci if (udev->descriptor.bDeviceClass == USB_CLASS_HUB) 32962306a36Sopenharmony_ci return -EPERM; 33062306a36Sopenharmony_ci 33162306a36Sopenharmony_ci if (sscanf(buf, "%d", &value) != 1) 33262306a36Sopenharmony_ci return -EINVAL; 33362306a36Sopenharmony_ci 33462306a36Sopenharmony_ci rc = usb_lock_device_interruptible(udev); 33562306a36Sopenharmony_ci if (rc < 0) 33662306a36Sopenharmony_ci return -EINTR; 33762306a36Sopenharmony_ci udev->persist_enabled = !!value; 33862306a36Sopenharmony_ci usb_unlock_device(udev); 33962306a36Sopenharmony_ci return count; 34062306a36Sopenharmony_ci} 34162306a36Sopenharmony_cistatic DEVICE_ATTR_RW(persist); 34262306a36Sopenharmony_ci 34362306a36Sopenharmony_cistatic int add_persist_attributes(struct device *dev) 34462306a36Sopenharmony_ci{ 34562306a36Sopenharmony_ci int rc = 0; 34662306a36Sopenharmony_ci 34762306a36Sopenharmony_ci if (is_usb_device(dev)) { 34862306a36Sopenharmony_ci struct usb_device *udev = to_usb_device(dev); 34962306a36Sopenharmony_ci 35062306a36Sopenharmony_ci /* Hubs are automatically enabled for USB_PERSIST, 35162306a36Sopenharmony_ci * no point in creating the attribute file. 35262306a36Sopenharmony_ci */ 35362306a36Sopenharmony_ci if (udev->descriptor.bDeviceClass != USB_CLASS_HUB) 35462306a36Sopenharmony_ci rc = sysfs_add_file_to_group(&dev->kobj, 35562306a36Sopenharmony_ci &dev_attr_persist.attr, 35662306a36Sopenharmony_ci power_group_name); 35762306a36Sopenharmony_ci } 35862306a36Sopenharmony_ci return rc; 35962306a36Sopenharmony_ci} 36062306a36Sopenharmony_ci 36162306a36Sopenharmony_cistatic void remove_persist_attributes(struct device *dev) 36262306a36Sopenharmony_ci{ 36362306a36Sopenharmony_ci sysfs_remove_file_from_group(&dev->kobj, 36462306a36Sopenharmony_ci &dev_attr_persist.attr, 36562306a36Sopenharmony_ci power_group_name); 36662306a36Sopenharmony_ci} 36762306a36Sopenharmony_ci 36862306a36Sopenharmony_cistatic ssize_t connected_duration_show(struct device *dev, 36962306a36Sopenharmony_ci struct device_attribute *attr, char *buf) 37062306a36Sopenharmony_ci{ 37162306a36Sopenharmony_ci struct usb_device *udev = to_usb_device(dev); 37262306a36Sopenharmony_ci 37362306a36Sopenharmony_ci return sysfs_emit(buf, "%u\n", 37462306a36Sopenharmony_ci jiffies_to_msecs(jiffies - udev->connect_time)); 37562306a36Sopenharmony_ci} 37662306a36Sopenharmony_cistatic DEVICE_ATTR_RO(connected_duration); 37762306a36Sopenharmony_ci 37862306a36Sopenharmony_ci/* 37962306a36Sopenharmony_ci * If the device is resumed, the last time the device was suspended has 38062306a36Sopenharmony_ci * been pre-subtracted from active_duration. We add the current time to 38162306a36Sopenharmony_ci * get the duration that the device was actually active. 38262306a36Sopenharmony_ci * 38362306a36Sopenharmony_ci * If the device is suspended, the active_duration is up-to-date. 38462306a36Sopenharmony_ci */ 38562306a36Sopenharmony_cistatic ssize_t active_duration_show(struct device *dev, 38662306a36Sopenharmony_ci struct device_attribute *attr, char *buf) 38762306a36Sopenharmony_ci{ 38862306a36Sopenharmony_ci struct usb_device *udev = to_usb_device(dev); 38962306a36Sopenharmony_ci int duration; 39062306a36Sopenharmony_ci 39162306a36Sopenharmony_ci if (udev->state != USB_STATE_SUSPENDED) 39262306a36Sopenharmony_ci duration = jiffies_to_msecs(jiffies + udev->active_duration); 39362306a36Sopenharmony_ci else 39462306a36Sopenharmony_ci duration = jiffies_to_msecs(udev->active_duration); 39562306a36Sopenharmony_ci return sysfs_emit(buf, "%u\n", duration); 39662306a36Sopenharmony_ci} 39762306a36Sopenharmony_cistatic DEVICE_ATTR_RO(active_duration); 39862306a36Sopenharmony_ci 39962306a36Sopenharmony_cistatic ssize_t autosuspend_show(struct device *dev, 40062306a36Sopenharmony_ci struct device_attribute *attr, char *buf) 40162306a36Sopenharmony_ci{ 40262306a36Sopenharmony_ci return sysfs_emit(buf, "%d\n", dev->power.autosuspend_delay / 1000); 40362306a36Sopenharmony_ci} 40462306a36Sopenharmony_ci 40562306a36Sopenharmony_cistatic ssize_t autosuspend_store(struct device *dev, 40662306a36Sopenharmony_ci struct device_attribute *attr, const char *buf, 40762306a36Sopenharmony_ci size_t count) 40862306a36Sopenharmony_ci{ 40962306a36Sopenharmony_ci int value; 41062306a36Sopenharmony_ci 41162306a36Sopenharmony_ci if (sscanf(buf, "%d", &value) != 1 || value >= INT_MAX/1000 || 41262306a36Sopenharmony_ci value <= -INT_MAX/1000) 41362306a36Sopenharmony_ci return -EINVAL; 41462306a36Sopenharmony_ci 41562306a36Sopenharmony_ci pm_runtime_set_autosuspend_delay(dev, value * 1000); 41662306a36Sopenharmony_ci return count; 41762306a36Sopenharmony_ci} 41862306a36Sopenharmony_cistatic DEVICE_ATTR_RW(autosuspend); 41962306a36Sopenharmony_ci 42062306a36Sopenharmony_cistatic const char on_string[] = "on"; 42162306a36Sopenharmony_cistatic const char auto_string[] = "auto"; 42262306a36Sopenharmony_ci 42362306a36Sopenharmony_cistatic void warn_level(void) 42462306a36Sopenharmony_ci{ 42562306a36Sopenharmony_ci static int level_warned; 42662306a36Sopenharmony_ci 42762306a36Sopenharmony_ci if (!level_warned) { 42862306a36Sopenharmony_ci level_warned = 1; 42962306a36Sopenharmony_ci printk(KERN_WARNING "WARNING! power/level is deprecated; " 43062306a36Sopenharmony_ci "use power/control instead\n"); 43162306a36Sopenharmony_ci } 43262306a36Sopenharmony_ci} 43362306a36Sopenharmony_ci 43462306a36Sopenharmony_cistatic ssize_t level_show(struct device *dev, struct device_attribute *attr, 43562306a36Sopenharmony_ci char *buf) 43662306a36Sopenharmony_ci{ 43762306a36Sopenharmony_ci struct usb_device *udev = to_usb_device(dev); 43862306a36Sopenharmony_ci const char *p = auto_string; 43962306a36Sopenharmony_ci 44062306a36Sopenharmony_ci warn_level(); 44162306a36Sopenharmony_ci if (udev->state != USB_STATE_SUSPENDED && !udev->dev.power.runtime_auto) 44262306a36Sopenharmony_ci p = on_string; 44362306a36Sopenharmony_ci return sysfs_emit(buf, "%s\n", p); 44462306a36Sopenharmony_ci} 44562306a36Sopenharmony_ci 44662306a36Sopenharmony_cistatic ssize_t level_store(struct device *dev, struct device_attribute *attr, 44762306a36Sopenharmony_ci const char *buf, size_t count) 44862306a36Sopenharmony_ci{ 44962306a36Sopenharmony_ci struct usb_device *udev = to_usb_device(dev); 45062306a36Sopenharmony_ci int len = count; 45162306a36Sopenharmony_ci char *cp; 45262306a36Sopenharmony_ci int rc = count; 45362306a36Sopenharmony_ci int rv; 45462306a36Sopenharmony_ci 45562306a36Sopenharmony_ci warn_level(); 45662306a36Sopenharmony_ci cp = memchr(buf, '\n', count); 45762306a36Sopenharmony_ci if (cp) 45862306a36Sopenharmony_ci len = cp - buf; 45962306a36Sopenharmony_ci 46062306a36Sopenharmony_ci rv = usb_lock_device_interruptible(udev); 46162306a36Sopenharmony_ci if (rv < 0) 46262306a36Sopenharmony_ci return -EINTR; 46362306a36Sopenharmony_ci 46462306a36Sopenharmony_ci if (len == sizeof on_string - 1 && 46562306a36Sopenharmony_ci strncmp(buf, on_string, len) == 0) 46662306a36Sopenharmony_ci usb_disable_autosuspend(udev); 46762306a36Sopenharmony_ci 46862306a36Sopenharmony_ci else if (len == sizeof auto_string - 1 && 46962306a36Sopenharmony_ci strncmp(buf, auto_string, len) == 0) 47062306a36Sopenharmony_ci usb_enable_autosuspend(udev); 47162306a36Sopenharmony_ci 47262306a36Sopenharmony_ci else 47362306a36Sopenharmony_ci rc = -EINVAL; 47462306a36Sopenharmony_ci 47562306a36Sopenharmony_ci usb_unlock_device(udev); 47662306a36Sopenharmony_ci return rc; 47762306a36Sopenharmony_ci} 47862306a36Sopenharmony_cistatic DEVICE_ATTR_RW(level); 47962306a36Sopenharmony_ci 48062306a36Sopenharmony_cistatic ssize_t usb2_hardware_lpm_show(struct device *dev, 48162306a36Sopenharmony_ci struct device_attribute *attr, char *buf) 48262306a36Sopenharmony_ci{ 48362306a36Sopenharmony_ci struct usb_device *udev = to_usb_device(dev); 48462306a36Sopenharmony_ci const char *p; 48562306a36Sopenharmony_ci 48662306a36Sopenharmony_ci if (udev->usb2_hw_lpm_allowed == 1) 48762306a36Sopenharmony_ci p = "enabled"; 48862306a36Sopenharmony_ci else 48962306a36Sopenharmony_ci p = "disabled"; 49062306a36Sopenharmony_ci 49162306a36Sopenharmony_ci return sysfs_emit(buf, "%s\n", p); 49262306a36Sopenharmony_ci} 49362306a36Sopenharmony_ci 49462306a36Sopenharmony_cistatic ssize_t usb2_hardware_lpm_store(struct device *dev, 49562306a36Sopenharmony_ci struct device_attribute *attr, 49662306a36Sopenharmony_ci const char *buf, size_t count) 49762306a36Sopenharmony_ci{ 49862306a36Sopenharmony_ci struct usb_device *udev = to_usb_device(dev); 49962306a36Sopenharmony_ci bool value; 50062306a36Sopenharmony_ci int ret; 50162306a36Sopenharmony_ci 50262306a36Sopenharmony_ci ret = usb_lock_device_interruptible(udev); 50362306a36Sopenharmony_ci if (ret < 0) 50462306a36Sopenharmony_ci return -EINTR; 50562306a36Sopenharmony_ci 50662306a36Sopenharmony_ci ret = kstrtobool(buf, &value); 50762306a36Sopenharmony_ci 50862306a36Sopenharmony_ci if (!ret) { 50962306a36Sopenharmony_ci udev->usb2_hw_lpm_allowed = value; 51062306a36Sopenharmony_ci if (value) 51162306a36Sopenharmony_ci ret = usb_enable_usb2_hardware_lpm(udev); 51262306a36Sopenharmony_ci else 51362306a36Sopenharmony_ci ret = usb_disable_usb2_hardware_lpm(udev); 51462306a36Sopenharmony_ci } 51562306a36Sopenharmony_ci 51662306a36Sopenharmony_ci usb_unlock_device(udev); 51762306a36Sopenharmony_ci 51862306a36Sopenharmony_ci if (!ret) 51962306a36Sopenharmony_ci return count; 52062306a36Sopenharmony_ci 52162306a36Sopenharmony_ci return ret; 52262306a36Sopenharmony_ci} 52362306a36Sopenharmony_cistatic DEVICE_ATTR_RW(usb2_hardware_lpm); 52462306a36Sopenharmony_ci 52562306a36Sopenharmony_cistatic ssize_t usb2_lpm_l1_timeout_show(struct device *dev, 52662306a36Sopenharmony_ci struct device_attribute *attr, 52762306a36Sopenharmony_ci char *buf) 52862306a36Sopenharmony_ci{ 52962306a36Sopenharmony_ci struct usb_device *udev = to_usb_device(dev); 53062306a36Sopenharmony_ci return sysfs_emit(buf, "%d\n", udev->l1_params.timeout); 53162306a36Sopenharmony_ci} 53262306a36Sopenharmony_ci 53362306a36Sopenharmony_cistatic ssize_t usb2_lpm_l1_timeout_store(struct device *dev, 53462306a36Sopenharmony_ci struct device_attribute *attr, 53562306a36Sopenharmony_ci const char *buf, size_t count) 53662306a36Sopenharmony_ci{ 53762306a36Sopenharmony_ci struct usb_device *udev = to_usb_device(dev); 53862306a36Sopenharmony_ci u16 timeout; 53962306a36Sopenharmony_ci 54062306a36Sopenharmony_ci if (kstrtou16(buf, 0, &timeout)) 54162306a36Sopenharmony_ci return -EINVAL; 54262306a36Sopenharmony_ci 54362306a36Sopenharmony_ci udev->l1_params.timeout = timeout; 54462306a36Sopenharmony_ci 54562306a36Sopenharmony_ci return count; 54662306a36Sopenharmony_ci} 54762306a36Sopenharmony_cistatic DEVICE_ATTR_RW(usb2_lpm_l1_timeout); 54862306a36Sopenharmony_ci 54962306a36Sopenharmony_cistatic ssize_t usb2_lpm_besl_show(struct device *dev, 55062306a36Sopenharmony_ci struct device_attribute *attr, char *buf) 55162306a36Sopenharmony_ci{ 55262306a36Sopenharmony_ci struct usb_device *udev = to_usb_device(dev); 55362306a36Sopenharmony_ci return sysfs_emit(buf, "%d\n", udev->l1_params.besl); 55462306a36Sopenharmony_ci} 55562306a36Sopenharmony_ci 55662306a36Sopenharmony_cistatic ssize_t usb2_lpm_besl_store(struct device *dev, 55762306a36Sopenharmony_ci struct device_attribute *attr, 55862306a36Sopenharmony_ci const char *buf, size_t count) 55962306a36Sopenharmony_ci{ 56062306a36Sopenharmony_ci struct usb_device *udev = to_usb_device(dev); 56162306a36Sopenharmony_ci u8 besl; 56262306a36Sopenharmony_ci 56362306a36Sopenharmony_ci if (kstrtou8(buf, 0, &besl) || besl > 15) 56462306a36Sopenharmony_ci return -EINVAL; 56562306a36Sopenharmony_ci 56662306a36Sopenharmony_ci udev->l1_params.besl = besl; 56762306a36Sopenharmony_ci 56862306a36Sopenharmony_ci return count; 56962306a36Sopenharmony_ci} 57062306a36Sopenharmony_cistatic DEVICE_ATTR_RW(usb2_lpm_besl); 57162306a36Sopenharmony_ci 57262306a36Sopenharmony_cistatic ssize_t usb3_hardware_lpm_u1_show(struct device *dev, 57362306a36Sopenharmony_ci struct device_attribute *attr, char *buf) 57462306a36Sopenharmony_ci{ 57562306a36Sopenharmony_ci struct usb_device *udev = to_usb_device(dev); 57662306a36Sopenharmony_ci const char *p; 57762306a36Sopenharmony_ci int rc; 57862306a36Sopenharmony_ci 57962306a36Sopenharmony_ci rc = usb_lock_device_interruptible(udev); 58062306a36Sopenharmony_ci if (rc < 0) 58162306a36Sopenharmony_ci return -EINTR; 58262306a36Sopenharmony_ci 58362306a36Sopenharmony_ci if (udev->usb3_lpm_u1_enabled) 58462306a36Sopenharmony_ci p = "enabled"; 58562306a36Sopenharmony_ci else 58662306a36Sopenharmony_ci p = "disabled"; 58762306a36Sopenharmony_ci 58862306a36Sopenharmony_ci usb_unlock_device(udev); 58962306a36Sopenharmony_ci 59062306a36Sopenharmony_ci return sysfs_emit(buf, "%s\n", p); 59162306a36Sopenharmony_ci} 59262306a36Sopenharmony_cistatic DEVICE_ATTR_RO(usb3_hardware_lpm_u1); 59362306a36Sopenharmony_ci 59462306a36Sopenharmony_cistatic ssize_t usb3_hardware_lpm_u2_show(struct device *dev, 59562306a36Sopenharmony_ci struct device_attribute *attr, char *buf) 59662306a36Sopenharmony_ci{ 59762306a36Sopenharmony_ci struct usb_device *udev = to_usb_device(dev); 59862306a36Sopenharmony_ci const char *p; 59962306a36Sopenharmony_ci int rc; 60062306a36Sopenharmony_ci 60162306a36Sopenharmony_ci rc = usb_lock_device_interruptible(udev); 60262306a36Sopenharmony_ci if (rc < 0) 60362306a36Sopenharmony_ci return -EINTR; 60462306a36Sopenharmony_ci 60562306a36Sopenharmony_ci if (udev->usb3_lpm_u2_enabled) 60662306a36Sopenharmony_ci p = "enabled"; 60762306a36Sopenharmony_ci else 60862306a36Sopenharmony_ci p = "disabled"; 60962306a36Sopenharmony_ci 61062306a36Sopenharmony_ci usb_unlock_device(udev); 61162306a36Sopenharmony_ci 61262306a36Sopenharmony_ci return sysfs_emit(buf, "%s\n", p); 61362306a36Sopenharmony_ci} 61462306a36Sopenharmony_cistatic DEVICE_ATTR_RO(usb3_hardware_lpm_u2); 61562306a36Sopenharmony_ci 61662306a36Sopenharmony_cistatic struct attribute *usb2_hardware_lpm_attr[] = { 61762306a36Sopenharmony_ci &dev_attr_usb2_hardware_lpm.attr, 61862306a36Sopenharmony_ci &dev_attr_usb2_lpm_l1_timeout.attr, 61962306a36Sopenharmony_ci &dev_attr_usb2_lpm_besl.attr, 62062306a36Sopenharmony_ci NULL, 62162306a36Sopenharmony_ci}; 62262306a36Sopenharmony_cistatic const struct attribute_group usb2_hardware_lpm_attr_group = { 62362306a36Sopenharmony_ci .name = power_group_name, 62462306a36Sopenharmony_ci .attrs = usb2_hardware_lpm_attr, 62562306a36Sopenharmony_ci}; 62662306a36Sopenharmony_ci 62762306a36Sopenharmony_cistatic struct attribute *usb3_hardware_lpm_attr[] = { 62862306a36Sopenharmony_ci &dev_attr_usb3_hardware_lpm_u1.attr, 62962306a36Sopenharmony_ci &dev_attr_usb3_hardware_lpm_u2.attr, 63062306a36Sopenharmony_ci NULL, 63162306a36Sopenharmony_ci}; 63262306a36Sopenharmony_cistatic const struct attribute_group usb3_hardware_lpm_attr_group = { 63362306a36Sopenharmony_ci .name = power_group_name, 63462306a36Sopenharmony_ci .attrs = usb3_hardware_lpm_attr, 63562306a36Sopenharmony_ci}; 63662306a36Sopenharmony_ci 63762306a36Sopenharmony_cistatic struct attribute *power_attrs[] = { 63862306a36Sopenharmony_ci &dev_attr_autosuspend.attr, 63962306a36Sopenharmony_ci &dev_attr_level.attr, 64062306a36Sopenharmony_ci &dev_attr_connected_duration.attr, 64162306a36Sopenharmony_ci &dev_attr_active_duration.attr, 64262306a36Sopenharmony_ci NULL, 64362306a36Sopenharmony_ci}; 64462306a36Sopenharmony_cistatic const struct attribute_group power_attr_group = { 64562306a36Sopenharmony_ci .name = power_group_name, 64662306a36Sopenharmony_ci .attrs = power_attrs, 64762306a36Sopenharmony_ci}; 64862306a36Sopenharmony_ci 64962306a36Sopenharmony_cistatic int add_power_attributes(struct device *dev) 65062306a36Sopenharmony_ci{ 65162306a36Sopenharmony_ci int rc = 0; 65262306a36Sopenharmony_ci 65362306a36Sopenharmony_ci if (is_usb_device(dev)) { 65462306a36Sopenharmony_ci struct usb_device *udev = to_usb_device(dev); 65562306a36Sopenharmony_ci rc = sysfs_merge_group(&dev->kobj, &power_attr_group); 65662306a36Sopenharmony_ci if (udev->usb2_hw_lpm_capable == 1) 65762306a36Sopenharmony_ci rc = sysfs_merge_group(&dev->kobj, 65862306a36Sopenharmony_ci &usb2_hardware_lpm_attr_group); 65962306a36Sopenharmony_ci if ((udev->speed == USB_SPEED_SUPER || 66062306a36Sopenharmony_ci udev->speed == USB_SPEED_SUPER_PLUS) && 66162306a36Sopenharmony_ci udev->lpm_capable == 1) 66262306a36Sopenharmony_ci rc = sysfs_merge_group(&dev->kobj, 66362306a36Sopenharmony_ci &usb3_hardware_lpm_attr_group); 66462306a36Sopenharmony_ci } 66562306a36Sopenharmony_ci 66662306a36Sopenharmony_ci return rc; 66762306a36Sopenharmony_ci} 66862306a36Sopenharmony_ci 66962306a36Sopenharmony_cistatic void remove_power_attributes(struct device *dev) 67062306a36Sopenharmony_ci{ 67162306a36Sopenharmony_ci sysfs_unmerge_group(&dev->kobj, &usb2_hardware_lpm_attr_group); 67262306a36Sopenharmony_ci sysfs_unmerge_group(&dev->kobj, &power_attr_group); 67362306a36Sopenharmony_ci} 67462306a36Sopenharmony_ci 67562306a36Sopenharmony_ci#else 67662306a36Sopenharmony_ci 67762306a36Sopenharmony_ci#define add_persist_attributes(dev) 0 67862306a36Sopenharmony_ci#define remove_persist_attributes(dev) do {} while (0) 67962306a36Sopenharmony_ci 68062306a36Sopenharmony_ci#define add_power_attributes(dev) 0 68162306a36Sopenharmony_ci#define remove_power_attributes(dev) do {} while (0) 68262306a36Sopenharmony_ci 68362306a36Sopenharmony_ci#endif /* CONFIG_PM */ 68462306a36Sopenharmony_ci 68562306a36Sopenharmony_ci 68662306a36Sopenharmony_ci/* Descriptor fields */ 68762306a36Sopenharmony_ci#define usb_descriptor_attr_le16(field, format_string) \ 68862306a36Sopenharmony_cistatic ssize_t \ 68962306a36Sopenharmony_cifield##_show(struct device *dev, struct device_attribute *attr, \ 69062306a36Sopenharmony_ci char *buf) \ 69162306a36Sopenharmony_ci{ \ 69262306a36Sopenharmony_ci struct usb_device *udev; \ 69362306a36Sopenharmony_ci \ 69462306a36Sopenharmony_ci udev = to_usb_device(dev); \ 69562306a36Sopenharmony_ci return sysfs_emit(buf, format_string, \ 69662306a36Sopenharmony_ci le16_to_cpu(udev->descriptor.field)); \ 69762306a36Sopenharmony_ci} \ 69862306a36Sopenharmony_cistatic DEVICE_ATTR_RO(field) 69962306a36Sopenharmony_ci 70062306a36Sopenharmony_ciusb_descriptor_attr_le16(idVendor, "%04x\n"); 70162306a36Sopenharmony_ciusb_descriptor_attr_le16(idProduct, "%04x\n"); 70262306a36Sopenharmony_ciusb_descriptor_attr_le16(bcdDevice, "%04x\n"); 70362306a36Sopenharmony_ci 70462306a36Sopenharmony_ci#define usb_descriptor_attr(field, format_string) \ 70562306a36Sopenharmony_cistatic ssize_t \ 70662306a36Sopenharmony_cifield##_show(struct device *dev, struct device_attribute *attr, \ 70762306a36Sopenharmony_ci char *buf) \ 70862306a36Sopenharmony_ci{ \ 70962306a36Sopenharmony_ci struct usb_device *udev; \ 71062306a36Sopenharmony_ci \ 71162306a36Sopenharmony_ci udev = to_usb_device(dev); \ 71262306a36Sopenharmony_ci return sysfs_emit(buf, format_string, udev->descriptor.field); \ 71362306a36Sopenharmony_ci} \ 71462306a36Sopenharmony_cistatic DEVICE_ATTR_RO(field) 71562306a36Sopenharmony_ci 71662306a36Sopenharmony_ciusb_descriptor_attr(bDeviceClass, "%02x\n"); 71762306a36Sopenharmony_ciusb_descriptor_attr(bDeviceSubClass, "%02x\n"); 71862306a36Sopenharmony_ciusb_descriptor_attr(bDeviceProtocol, "%02x\n"); 71962306a36Sopenharmony_ciusb_descriptor_attr(bNumConfigurations, "%d\n"); 72062306a36Sopenharmony_ciusb_descriptor_attr(bMaxPacketSize0, "%d\n"); 72162306a36Sopenharmony_ci 72262306a36Sopenharmony_ci 72362306a36Sopenharmony_ci/* show if the device is authorized (1) or not (0) */ 72462306a36Sopenharmony_cistatic ssize_t authorized_show(struct device *dev, 72562306a36Sopenharmony_ci struct device_attribute *attr, char *buf) 72662306a36Sopenharmony_ci{ 72762306a36Sopenharmony_ci struct usb_device *usb_dev = to_usb_device(dev); 72862306a36Sopenharmony_ci return sysfs_emit(buf, "%u\n", usb_dev->authorized); 72962306a36Sopenharmony_ci} 73062306a36Sopenharmony_ci 73162306a36Sopenharmony_ci/* 73262306a36Sopenharmony_ci * Authorize a device to be used in the system 73362306a36Sopenharmony_ci * 73462306a36Sopenharmony_ci * Writing a 0 deauthorizes the device, writing a 1 authorizes it. 73562306a36Sopenharmony_ci */ 73662306a36Sopenharmony_cistatic ssize_t authorized_store(struct device *dev, 73762306a36Sopenharmony_ci struct device_attribute *attr, const char *buf, 73862306a36Sopenharmony_ci size_t size) 73962306a36Sopenharmony_ci{ 74062306a36Sopenharmony_ci ssize_t result; 74162306a36Sopenharmony_ci struct usb_device *usb_dev = to_usb_device(dev); 74262306a36Sopenharmony_ci unsigned val; 74362306a36Sopenharmony_ci result = sscanf(buf, "%u\n", &val); 74462306a36Sopenharmony_ci if (result != 1) 74562306a36Sopenharmony_ci result = -EINVAL; 74662306a36Sopenharmony_ci else if (val == 0) 74762306a36Sopenharmony_ci result = usb_deauthorize_device(usb_dev); 74862306a36Sopenharmony_ci else 74962306a36Sopenharmony_ci result = usb_authorize_device(usb_dev); 75062306a36Sopenharmony_ci return result < 0 ? result : size; 75162306a36Sopenharmony_ci} 75262306a36Sopenharmony_cistatic DEVICE_ATTR_IGNORE_LOCKDEP(authorized, S_IRUGO | S_IWUSR, 75362306a36Sopenharmony_ci authorized_show, authorized_store); 75462306a36Sopenharmony_ci 75562306a36Sopenharmony_ci/* "Safely remove a device" */ 75662306a36Sopenharmony_cistatic ssize_t remove_store(struct device *dev, struct device_attribute *attr, 75762306a36Sopenharmony_ci const char *buf, size_t count) 75862306a36Sopenharmony_ci{ 75962306a36Sopenharmony_ci struct usb_device *udev = to_usb_device(dev); 76062306a36Sopenharmony_ci int rc = 0; 76162306a36Sopenharmony_ci 76262306a36Sopenharmony_ci usb_lock_device(udev); 76362306a36Sopenharmony_ci if (udev->state != USB_STATE_NOTATTACHED) { 76462306a36Sopenharmony_ci 76562306a36Sopenharmony_ci /* To avoid races, first unconfigure and then remove */ 76662306a36Sopenharmony_ci usb_set_configuration(udev, -1); 76762306a36Sopenharmony_ci rc = usb_remove_device(udev); 76862306a36Sopenharmony_ci } 76962306a36Sopenharmony_ci if (rc == 0) 77062306a36Sopenharmony_ci rc = count; 77162306a36Sopenharmony_ci usb_unlock_device(udev); 77262306a36Sopenharmony_ci return rc; 77362306a36Sopenharmony_ci} 77462306a36Sopenharmony_cistatic DEVICE_ATTR_IGNORE_LOCKDEP(remove, S_IWUSR, NULL, remove_store); 77562306a36Sopenharmony_ci 77662306a36Sopenharmony_ci 77762306a36Sopenharmony_cistatic struct attribute *dev_attrs[] = { 77862306a36Sopenharmony_ci /* current configuration's attributes */ 77962306a36Sopenharmony_ci &dev_attr_configuration.attr, 78062306a36Sopenharmony_ci &dev_attr_bNumInterfaces.attr, 78162306a36Sopenharmony_ci &dev_attr_bConfigurationValue.attr, 78262306a36Sopenharmony_ci &dev_attr_bmAttributes.attr, 78362306a36Sopenharmony_ci &dev_attr_bMaxPower.attr, 78462306a36Sopenharmony_ci /* device attributes */ 78562306a36Sopenharmony_ci &dev_attr_urbnum.attr, 78662306a36Sopenharmony_ci &dev_attr_idVendor.attr, 78762306a36Sopenharmony_ci &dev_attr_idProduct.attr, 78862306a36Sopenharmony_ci &dev_attr_bcdDevice.attr, 78962306a36Sopenharmony_ci &dev_attr_bDeviceClass.attr, 79062306a36Sopenharmony_ci &dev_attr_bDeviceSubClass.attr, 79162306a36Sopenharmony_ci &dev_attr_bDeviceProtocol.attr, 79262306a36Sopenharmony_ci &dev_attr_bNumConfigurations.attr, 79362306a36Sopenharmony_ci &dev_attr_bMaxPacketSize0.attr, 79462306a36Sopenharmony_ci &dev_attr_speed.attr, 79562306a36Sopenharmony_ci &dev_attr_rx_lanes.attr, 79662306a36Sopenharmony_ci &dev_attr_tx_lanes.attr, 79762306a36Sopenharmony_ci &dev_attr_busnum.attr, 79862306a36Sopenharmony_ci &dev_attr_devnum.attr, 79962306a36Sopenharmony_ci &dev_attr_devpath.attr, 80062306a36Sopenharmony_ci &dev_attr_version.attr, 80162306a36Sopenharmony_ci &dev_attr_maxchild.attr, 80262306a36Sopenharmony_ci &dev_attr_quirks.attr, 80362306a36Sopenharmony_ci &dev_attr_avoid_reset_quirk.attr, 80462306a36Sopenharmony_ci &dev_attr_authorized.attr, 80562306a36Sopenharmony_ci &dev_attr_remove.attr, 80662306a36Sopenharmony_ci &dev_attr_ltm_capable.attr, 80762306a36Sopenharmony_ci#ifdef CONFIG_OF 80862306a36Sopenharmony_ci &dev_attr_devspec.attr, 80962306a36Sopenharmony_ci#endif 81062306a36Sopenharmony_ci NULL, 81162306a36Sopenharmony_ci}; 81262306a36Sopenharmony_cistatic const struct attribute_group dev_attr_grp = { 81362306a36Sopenharmony_ci .attrs = dev_attrs, 81462306a36Sopenharmony_ci}; 81562306a36Sopenharmony_ci 81662306a36Sopenharmony_ci/* When modifying this list, be sure to modify dev_string_attrs_are_visible() 81762306a36Sopenharmony_ci * accordingly. 81862306a36Sopenharmony_ci */ 81962306a36Sopenharmony_cistatic struct attribute *dev_string_attrs[] = { 82062306a36Sopenharmony_ci &dev_attr_manufacturer.attr, 82162306a36Sopenharmony_ci &dev_attr_product.attr, 82262306a36Sopenharmony_ci &dev_attr_serial.attr, 82362306a36Sopenharmony_ci NULL 82462306a36Sopenharmony_ci}; 82562306a36Sopenharmony_ci 82662306a36Sopenharmony_cistatic umode_t dev_string_attrs_are_visible(struct kobject *kobj, 82762306a36Sopenharmony_ci struct attribute *a, int n) 82862306a36Sopenharmony_ci{ 82962306a36Sopenharmony_ci struct device *dev = kobj_to_dev(kobj); 83062306a36Sopenharmony_ci struct usb_device *udev = to_usb_device(dev); 83162306a36Sopenharmony_ci 83262306a36Sopenharmony_ci if (a == &dev_attr_manufacturer.attr) { 83362306a36Sopenharmony_ci if (udev->manufacturer == NULL) 83462306a36Sopenharmony_ci return 0; 83562306a36Sopenharmony_ci } else if (a == &dev_attr_product.attr) { 83662306a36Sopenharmony_ci if (udev->product == NULL) 83762306a36Sopenharmony_ci return 0; 83862306a36Sopenharmony_ci } else if (a == &dev_attr_serial.attr) { 83962306a36Sopenharmony_ci if (udev->serial == NULL) 84062306a36Sopenharmony_ci return 0; 84162306a36Sopenharmony_ci } 84262306a36Sopenharmony_ci return a->mode; 84362306a36Sopenharmony_ci} 84462306a36Sopenharmony_ci 84562306a36Sopenharmony_cistatic const struct attribute_group dev_string_attr_grp = { 84662306a36Sopenharmony_ci .attrs = dev_string_attrs, 84762306a36Sopenharmony_ci .is_visible = dev_string_attrs_are_visible, 84862306a36Sopenharmony_ci}; 84962306a36Sopenharmony_ci 85062306a36Sopenharmony_ciconst struct attribute_group *usb_device_groups[] = { 85162306a36Sopenharmony_ci &dev_attr_grp, 85262306a36Sopenharmony_ci &dev_string_attr_grp, 85362306a36Sopenharmony_ci NULL 85462306a36Sopenharmony_ci}; 85562306a36Sopenharmony_ci 85662306a36Sopenharmony_ci/* Binary descriptors */ 85762306a36Sopenharmony_ci 85862306a36Sopenharmony_cistatic ssize_t 85962306a36Sopenharmony_ciread_descriptors(struct file *filp, struct kobject *kobj, 86062306a36Sopenharmony_ci struct bin_attribute *attr, 86162306a36Sopenharmony_ci char *buf, loff_t off, size_t count) 86262306a36Sopenharmony_ci{ 86362306a36Sopenharmony_ci struct device *dev = kobj_to_dev(kobj); 86462306a36Sopenharmony_ci struct usb_device *udev = to_usb_device(dev); 86562306a36Sopenharmony_ci size_t nleft = count; 86662306a36Sopenharmony_ci size_t srclen, n; 86762306a36Sopenharmony_ci int cfgno; 86862306a36Sopenharmony_ci void *src; 86962306a36Sopenharmony_ci 87062306a36Sopenharmony_ci /* The binary attribute begins with the device descriptor. 87162306a36Sopenharmony_ci * Following that are the raw descriptor entries for all the 87262306a36Sopenharmony_ci * configurations (config plus subsidiary descriptors). 87362306a36Sopenharmony_ci */ 87462306a36Sopenharmony_ci for (cfgno = -1; cfgno < udev->descriptor.bNumConfigurations && 87562306a36Sopenharmony_ci nleft > 0; ++cfgno) { 87662306a36Sopenharmony_ci if (cfgno < 0) { 87762306a36Sopenharmony_ci src = &udev->descriptor; 87862306a36Sopenharmony_ci srclen = sizeof(struct usb_device_descriptor); 87962306a36Sopenharmony_ci } else { 88062306a36Sopenharmony_ci src = udev->rawdescriptors[cfgno]; 88162306a36Sopenharmony_ci srclen = __le16_to_cpu(udev->config[cfgno].desc. 88262306a36Sopenharmony_ci wTotalLength); 88362306a36Sopenharmony_ci } 88462306a36Sopenharmony_ci if (off < srclen) { 88562306a36Sopenharmony_ci n = min(nleft, srclen - (size_t) off); 88662306a36Sopenharmony_ci memcpy(buf, src + off, n); 88762306a36Sopenharmony_ci nleft -= n; 88862306a36Sopenharmony_ci buf += n; 88962306a36Sopenharmony_ci off = 0; 89062306a36Sopenharmony_ci } else { 89162306a36Sopenharmony_ci off -= srclen; 89262306a36Sopenharmony_ci } 89362306a36Sopenharmony_ci } 89462306a36Sopenharmony_ci return count - nleft; 89562306a36Sopenharmony_ci} 89662306a36Sopenharmony_ci 89762306a36Sopenharmony_cistatic struct bin_attribute dev_bin_attr_descriptors = { 89862306a36Sopenharmony_ci .attr = {.name = "descriptors", .mode = 0444}, 89962306a36Sopenharmony_ci .read = read_descriptors, 90062306a36Sopenharmony_ci .size = 18 + 65535, /* dev descr + max-size raw descriptor */ 90162306a36Sopenharmony_ci}; 90262306a36Sopenharmony_ci 90362306a36Sopenharmony_ci/* 90462306a36Sopenharmony_ci * Show & store the current value of authorized_default 90562306a36Sopenharmony_ci */ 90662306a36Sopenharmony_cistatic ssize_t authorized_default_show(struct device *dev, 90762306a36Sopenharmony_ci struct device_attribute *attr, char *buf) 90862306a36Sopenharmony_ci{ 90962306a36Sopenharmony_ci struct usb_device *rh_usb_dev = to_usb_device(dev); 91062306a36Sopenharmony_ci struct usb_bus *usb_bus = rh_usb_dev->bus; 91162306a36Sopenharmony_ci struct usb_hcd *hcd; 91262306a36Sopenharmony_ci 91362306a36Sopenharmony_ci hcd = bus_to_hcd(usb_bus); 91462306a36Sopenharmony_ci return sysfs_emit(buf, "%u\n", hcd->dev_policy); 91562306a36Sopenharmony_ci} 91662306a36Sopenharmony_ci 91762306a36Sopenharmony_cistatic ssize_t authorized_default_store(struct device *dev, 91862306a36Sopenharmony_ci struct device_attribute *attr, 91962306a36Sopenharmony_ci const char *buf, size_t size) 92062306a36Sopenharmony_ci{ 92162306a36Sopenharmony_ci ssize_t result; 92262306a36Sopenharmony_ci unsigned int val; 92362306a36Sopenharmony_ci struct usb_device *rh_usb_dev = to_usb_device(dev); 92462306a36Sopenharmony_ci struct usb_bus *usb_bus = rh_usb_dev->bus; 92562306a36Sopenharmony_ci struct usb_hcd *hcd; 92662306a36Sopenharmony_ci 92762306a36Sopenharmony_ci hcd = bus_to_hcd(usb_bus); 92862306a36Sopenharmony_ci result = sscanf(buf, "%u\n", &val); 92962306a36Sopenharmony_ci if (result == 1) { 93062306a36Sopenharmony_ci hcd->dev_policy = val <= USB_DEVICE_AUTHORIZE_INTERNAL ? 93162306a36Sopenharmony_ci val : USB_DEVICE_AUTHORIZE_ALL; 93262306a36Sopenharmony_ci result = size; 93362306a36Sopenharmony_ci } else { 93462306a36Sopenharmony_ci result = -EINVAL; 93562306a36Sopenharmony_ci } 93662306a36Sopenharmony_ci return result; 93762306a36Sopenharmony_ci} 93862306a36Sopenharmony_cistatic DEVICE_ATTR_RW(authorized_default); 93962306a36Sopenharmony_ci 94062306a36Sopenharmony_ci/* 94162306a36Sopenharmony_ci * interface_authorized_default_show - show default authorization status 94262306a36Sopenharmony_ci * for USB interfaces 94362306a36Sopenharmony_ci * 94462306a36Sopenharmony_ci * note: interface_authorized_default is the default value 94562306a36Sopenharmony_ci * for initializing the authorized attribute of interfaces 94662306a36Sopenharmony_ci */ 94762306a36Sopenharmony_cistatic ssize_t interface_authorized_default_show(struct device *dev, 94862306a36Sopenharmony_ci struct device_attribute *attr, char *buf) 94962306a36Sopenharmony_ci{ 95062306a36Sopenharmony_ci struct usb_device *usb_dev = to_usb_device(dev); 95162306a36Sopenharmony_ci struct usb_hcd *hcd = bus_to_hcd(usb_dev->bus); 95262306a36Sopenharmony_ci 95362306a36Sopenharmony_ci return sysfs_emit(buf, "%u\n", !!HCD_INTF_AUTHORIZED(hcd)); 95462306a36Sopenharmony_ci} 95562306a36Sopenharmony_ci 95662306a36Sopenharmony_ci/* 95762306a36Sopenharmony_ci * interface_authorized_default_store - store default authorization status 95862306a36Sopenharmony_ci * for USB interfaces 95962306a36Sopenharmony_ci * 96062306a36Sopenharmony_ci * note: interface_authorized_default is the default value 96162306a36Sopenharmony_ci * for initializing the authorized attribute of interfaces 96262306a36Sopenharmony_ci */ 96362306a36Sopenharmony_cistatic ssize_t interface_authorized_default_store(struct device *dev, 96462306a36Sopenharmony_ci struct device_attribute *attr, const char *buf, size_t count) 96562306a36Sopenharmony_ci{ 96662306a36Sopenharmony_ci struct usb_device *usb_dev = to_usb_device(dev); 96762306a36Sopenharmony_ci struct usb_hcd *hcd = bus_to_hcd(usb_dev->bus); 96862306a36Sopenharmony_ci int rc = count; 96962306a36Sopenharmony_ci bool val; 97062306a36Sopenharmony_ci 97162306a36Sopenharmony_ci if (kstrtobool(buf, &val) != 0) 97262306a36Sopenharmony_ci return -EINVAL; 97362306a36Sopenharmony_ci 97462306a36Sopenharmony_ci if (val) 97562306a36Sopenharmony_ci set_bit(HCD_FLAG_INTF_AUTHORIZED, &hcd->flags); 97662306a36Sopenharmony_ci else 97762306a36Sopenharmony_ci clear_bit(HCD_FLAG_INTF_AUTHORIZED, &hcd->flags); 97862306a36Sopenharmony_ci 97962306a36Sopenharmony_ci return rc; 98062306a36Sopenharmony_ci} 98162306a36Sopenharmony_cistatic DEVICE_ATTR_RW(interface_authorized_default); 98262306a36Sopenharmony_ci 98362306a36Sopenharmony_ci/* Group all the USB bus attributes */ 98462306a36Sopenharmony_cistatic struct attribute *usb_bus_attrs[] = { 98562306a36Sopenharmony_ci &dev_attr_authorized_default.attr, 98662306a36Sopenharmony_ci &dev_attr_interface_authorized_default.attr, 98762306a36Sopenharmony_ci NULL, 98862306a36Sopenharmony_ci}; 98962306a36Sopenharmony_ci 99062306a36Sopenharmony_cistatic const struct attribute_group usb_bus_attr_group = { 99162306a36Sopenharmony_ci .name = NULL, /* we want them in the same directory */ 99262306a36Sopenharmony_ci .attrs = usb_bus_attrs, 99362306a36Sopenharmony_ci}; 99462306a36Sopenharmony_ci 99562306a36Sopenharmony_ci 99662306a36Sopenharmony_cistatic int add_default_authorized_attributes(struct device *dev) 99762306a36Sopenharmony_ci{ 99862306a36Sopenharmony_ci int rc = 0; 99962306a36Sopenharmony_ci 100062306a36Sopenharmony_ci if (is_usb_device(dev)) 100162306a36Sopenharmony_ci rc = sysfs_create_group(&dev->kobj, &usb_bus_attr_group); 100262306a36Sopenharmony_ci 100362306a36Sopenharmony_ci return rc; 100462306a36Sopenharmony_ci} 100562306a36Sopenharmony_ci 100662306a36Sopenharmony_cistatic void remove_default_authorized_attributes(struct device *dev) 100762306a36Sopenharmony_ci{ 100862306a36Sopenharmony_ci if (is_usb_device(dev)) { 100962306a36Sopenharmony_ci sysfs_remove_group(&dev->kobj, &usb_bus_attr_group); 101062306a36Sopenharmony_ci } 101162306a36Sopenharmony_ci} 101262306a36Sopenharmony_ci 101362306a36Sopenharmony_ciint usb_create_sysfs_dev_files(struct usb_device *udev) 101462306a36Sopenharmony_ci{ 101562306a36Sopenharmony_ci struct device *dev = &udev->dev; 101662306a36Sopenharmony_ci int retval; 101762306a36Sopenharmony_ci 101862306a36Sopenharmony_ci retval = device_create_bin_file(dev, &dev_bin_attr_descriptors); 101962306a36Sopenharmony_ci if (retval) 102062306a36Sopenharmony_ci goto error; 102162306a36Sopenharmony_ci 102262306a36Sopenharmony_ci retval = add_persist_attributes(dev); 102362306a36Sopenharmony_ci if (retval) 102462306a36Sopenharmony_ci goto error; 102562306a36Sopenharmony_ci 102662306a36Sopenharmony_ci retval = add_power_attributes(dev); 102762306a36Sopenharmony_ci if (retval) 102862306a36Sopenharmony_ci goto error; 102962306a36Sopenharmony_ci 103062306a36Sopenharmony_ci if (is_root_hub(udev)) { 103162306a36Sopenharmony_ci retval = add_default_authorized_attributes(dev); 103262306a36Sopenharmony_ci if (retval) 103362306a36Sopenharmony_ci goto error; 103462306a36Sopenharmony_ci } 103562306a36Sopenharmony_ci return retval; 103662306a36Sopenharmony_ci 103762306a36Sopenharmony_cierror: 103862306a36Sopenharmony_ci usb_remove_sysfs_dev_files(udev); 103962306a36Sopenharmony_ci return retval; 104062306a36Sopenharmony_ci} 104162306a36Sopenharmony_ci 104262306a36Sopenharmony_civoid usb_remove_sysfs_dev_files(struct usb_device *udev) 104362306a36Sopenharmony_ci{ 104462306a36Sopenharmony_ci struct device *dev = &udev->dev; 104562306a36Sopenharmony_ci 104662306a36Sopenharmony_ci if (is_root_hub(udev)) 104762306a36Sopenharmony_ci remove_default_authorized_attributes(dev); 104862306a36Sopenharmony_ci 104962306a36Sopenharmony_ci remove_power_attributes(dev); 105062306a36Sopenharmony_ci remove_persist_attributes(dev); 105162306a36Sopenharmony_ci device_remove_bin_file(dev, &dev_bin_attr_descriptors); 105262306a36Sopenharmony_ci} 105362306a36Sopenharmony_ci 105462306a36Sopenharmony_ci/* Interface Association Descriptor fields */ 105562306a36Sopenharmony_ci#define usb_intf_assoc_attr(field, format_string) \ 105662306a36Sopenharmony_cistatic ssize_t \ 105762306a36Sopenharmony_ciiad_##field##_show(struct device *dev, struct device_attribute *attr, \ 105862306a36Sopenharmony_ci char *buf) \ 105962306a36Sopenharmony_ci{ \ 106062306a36Sopenharmony_ci struct usb_interface *intf = to_usb_interface(dev); \ 106162306a36Sopenharmony_ci \ 106262306a36Sopenharmony_ci return sysfs_emit(buf, format_string, \ 106362306a36Sopenharmony_ci intf->intf_assoc->field); \ 106462306a36Sopenharmony_ci} \ 106562306a36Sopenharmony_cistatic DEVICE_ATTR_RO(iad_##field) 106662306a36Sopenharmony_ci 106762306a36Sopenharmony_ciusb_intf_assoc_attr(bFirstInterface, "%02x\n"); 106862306a36Sopenharmony_ciusb_intf_assoc_attr(bInterfaceCount, "%02d\n"); 106962306a36Sopenharmony_ciusb_intf_assoc_attr(bFunctionClass, "%02x\n"); 107062306a36Sopenharmony_ciusb_intf_assoc_attr(bFunctionSubClass, "%02x\n"); 107162306a36Sopenharmony_ciusb_intf_assoc_attr(bFunctionProtocol, "%02x\n"); 107262306a36Sopenharmony_ci 107362306a36Sopenharmony_ci/* Interface fields */ 107462306a36Sopenharmony_ci#define usb_intf_attr(field, format_string) \ 107562306a36Sopenharmony_cistatic ssize_t \ 107662306a36Sopenharmony_cifield##_show(struct device *dev, struct device_attribute *attr, \ 107762306a36Sopenharmony_ci char *buf) \ 107862306a36Sopenharmony_ci{ \ 107962306a36Sopenharmony_ci struct usb_interface *intf = to_usb_interface(dev); \ 108062306a36Sopenharmony_ci \ 108162306a36Sopenharmony_ci return sysfs_emit(buf, format_string, \ 108262306a36Sopenharmony_ci intf->cur_altsetting->desc.field); \ 108362306a36Sopenharmony_ci} \ 108462306a36Sopenharmony_cistatic DEVICE_ATTR_RO(field) 108562306a36Sopenharmony_ci 108662306a36Sopenharmony_ciusb_intf_attr(bInterfaceNumber, "%02x\n"); 108762306a36Sopenharmony_ciusb_intf_attr(bAlternateSetting, "%2d\n"); 108862306a36Sopenharmony_ciusb_intf_attr(bNumEndpoints, "%02x\n"); 108962306a36Sopenharmony_ciusb_intf_attr(bInterfaceClass, "%02x\n"); 109062306a36Sopenharmony_ciusb_intf_attr(bInterfaceSubClass, "%02x\n"); 109162306a36Sopenharmony_ciusb_intf_attr(bInterfaceProtocol, "%02x\n"); 109262306a36Sopenharmony_ci 109362306a36Sopenharmony_cistatic ssize_t interface_show(struct device *dev, struct device_attribute *attr, 109462306a36Sopenharmony_ci char *buf) 109562306a36Sopenharmony_ci{ 109662306a36Sopenharmony_ci struct usb_interface *intf; 109762306a36Sopenharmony_ci char *string; 109862306a36Sopenharmony_ci 109962306a36Sopenharmony_ci intf = to_usb_interface(dev); 110062306a36Sopenharmony_ci string = READ_ONCE(intf->cur_altsetting->string); 110162306a36Sopenharmony_ci if (!string) 110262306a36Sopenharmony_ci return 0; 110362306a36Sopenharmony_ci return sysfs_emit(buf, "%s\n", string); 110462306a36Sopenharmony_ci} 110562306a36Sopenharmony_cistatic DEVICE_ATTR_RO(interface); 110662306a36Sopenharmony_ci 110762306a36Sopenharmony_cistatic ssize_t modalias_show(struct device *dev, struct device_attribute *attr, 110862306a36Sopenharmony_ci char *buf) 110962306a36Sopenharmony_ci{ 111062306a36Sopenharmony_ci struct usb_interface *intf; 111162306a36Sopenharmony_ci struct usb_device *udev; 111262306a36Sopenharmony_ci struct usb_host_interface *alt; 111362306a36Sopenharmony_ci 111462306a36Sopenharmony_ci intf = to_usb_interface(dev); 111562306a36Sopenharmony_ci udev = interface_to_usbdev(intf); 111662306a36Sopenharmony_ci alt = READ_ONCE(intf->cur_altsetting); 111762306a36Sopenharmony_ci 111862306a36Sopenharmony_ci return sysfs_emit(buf, 111962306a36Sopenharmony_ci "usb:v%04Xp%04Xd%04Xdc%02Xdsc%02Xdp%02X" 112062306a36Sopenharmony_ci "ic%02Xisc%02Xip%02Xin%02X\n", 112162306a36Sopenharmony_ci le16_to_cpu(udev->descriptor.idVendor), 112262306a36Sopenharmony_ci le16_to_cpu(udev->descriptor.idProduct), 112362306a36Sopenharmony_ci le16_to_cpu(udev->descriptor.bcdDevice), 112462306a36Sopenharmony_ci udev->descriptor.bDeviceClass, 112562306a36Sopenharmony_ci udev->descriptor.bDeviceSubClass, 112662306a36Sopenharmony_ci udev->descriptor.bDeviceProtocol, 112762306a36Sopenharmony_ci alt->desc.bInterfaceClass, 112862306a36Sopenharmony_ci alt->desc.bInterfaceSubClass, 112962306a36Sopenharmony_ci alt->desc.bInterfaceProtocol, 113062306a36Sopenharmony_ci alt->desc.bInterfaceNumber); 113162306a36Sopenharmony_ci} 113262306a36Sopenharmony_cistatic DEVICE_ATTR_RO(modalias); 113362306a36Sopenharmony_ci 113462306a36Sopenharmony_cistatic ssize_t supports_autosuspend_show(struct device *dev, 113562306a36Sopenharmony_ci struct device_attribute *attr, 113662306a36Sopenharmony_ci char *buf) 113762306a36Sopenharmony_ci{ 113862306a36Sopenharmony_ci int s; 113962306a36Sopenharmony_ci 114062306a36Sopenharmony_ci s = device_lock_interruptible(dev); 114162306a36Sopenharmony_ci if (s < 0) 114262306a36Sopenharmony_ci return -EINTR; 114362306a36Sopenharmony_ci /* Devices will be autosuspended even when an interface isn't claimed */ 114462306a36Sopenharmony_ci s = (!dev->driver || to_usb_driver(dev->driver)->supports_autosuspend); 114562306a36Sopenharmony_ci device_unlock(dev); 114662306a36Sopenharmony_ci 114762306a36Sopenharmony_ci return sysfs_emit(buf, "%u\n", s); 114862306a36Sopenharmony_ci} 114962306a36Sopenharmony_cistatic DEVICE_ATTR_RO(supports_autosuspend); 115062306a36Sopenharmony_ci 115162306a36Sopenharmony_ci/* 115262306a36Sopenharmony_ci * interface_authorized_show - show authorization status of an USB interface 115362306a36Sopenharmony_ci * 1 is authorized, 0 is deauthorized 115462306a36Sopenharmony_ci */ 115562306a36Sopenharmony_cistatic ssize_t interface_authorized_show(struct device *dev, 115662306a36Sopenharmony_ci struct device_attribute *attr, char *buf) 115762306a36Sopenharmony_ci{ 115862306a36Sopenharmony_ci struct usb_interface *intf = to_usb_interface(dev); 115962306a36Sopenharmony_ci 116062306a36Sopenharmony_ci return sysfs_emit(buf, "%u\n", intf->authorized); 116162306a36Sopenharmony_ci} 116262306a36Sopenharmony_ci 116362306a36Sopenharmony_ci/* 116462306a36Sopenharmony_ci * interface_authorized_store - authorize or deauthorize an USB interface 116562306a36Sopenharmony_ci */ 116662306a36Sopenharmony_cistatic ssize_t interface_authorized_store(struct device *dev, 116762306a36Sopenharmony_ci struct device_attribute *attr, const char *buf, size_t count) 116862306a36Sopenharmony_ci{ 116962306a36Sopenharmony_ci struct usb_interface *intf = to_usb_interface(dev); 117062306a36Sopenharmony_ci bool val; 117162306a36Sopenharmony_ci 117262306a36Sopenharmony_ci if (kstrtobool(buf, &val) != 0) 117362306a36Sopenharmony_ci return -EINVAL; 117462306a36Sopenharmony_ci 117562306a36Sopenharmony_ci if (val) 117662306a36Sopenharmony_ci usb_authorize_interface(intf); 117762306a36Sopenharmony_ci else 117862306a36Sopenharmony_ci usb_deauthorize_interface(intf); 117962306a36Sopenharmony_ci 118062306a36Sopenharmony_ci return count; 118162306a36Sopenharmony_ci} 118262306a36Sopenharmony_cistatic struct device_attribute dev_attr_interface_authorized = 118362306a36Sopenharmony_ci __ATTR(authorized, S_IRUGO | S_IWUSR, 118462306a36Sopenharmony_ci interface_authorized_show, interface_authorized_store); 118562306a36Sopenharmony_ci 118662306a36Sopenharmony_cistatic struct attribute *intf_attrs[] = { 118762306a36Sopenharmony_ci &dev_attr_bInterfaceNumber.attr, 118862306a36Sopenharmony_ci &dev_attr_bAlternateSetting.attr, 118962306a36Sopenharmony_ci &dev_attr_bNumEndpoints.attr, 119062306a36Sopenharmony_ci &dev_attr_bInterfaceClass.attr, 119162306a36Sopenharmony_ci &dev_attr_bInterfaceSubClass.attr, 119262306a36Sopenharmony_ci &dev_attr_bInterfaceProtocol.attr, 119362306a36Sopenharmony_ci &dev_attr_modalias.attr, 119462306a36Sopenharmony_ci &dev_attr_supports_autosuspend.attr, 119562306a36Sopenharmony_ci &dev_attr_interface_authorized.attr, 119662306a36Sopenharmony_ci NULL, 119762306a36Sopenharmony_ci}; 119862306a36Sopenharmony_cistatic const struct attribute_group intf_attr_grp = { 119962306a36Sopenharmony_ci .attrs = intf_attrs, 120062306a36Sopenharmony_ci}; 120162306a36Sopenharmony_ci 120262306a36Sopenharmony_cistatic struct attribute *intf_assoc_attrs[] = { 120362306a36Sopenharmony_ci &dev_attr_iad_bFirstInterface.attr, 120462306a36Sopenharmony_ci &dev_attr_iad_bInterfaceCount.attr, 120562306a36Sopenharmony_ci &dev_attr_iad_bFunctionClass.attr, 120662306a36Sopenharmony_ci &dev_attr_iad_bFunctionSubClass.attr, 120762306a36Sopenharmony_ci &dev_attr_iad_bFunctionProtocol.attr, 120862306a36Sopenharmony_ci NULL, 120962306a36Sopenharmony_ci}; 121062306a36Sopenharmony_ci 121162306a36Sopenharmony_cistatic umode_t intf_assoc_attrs_are_visible(struct kobject *kobj, 121262306a36Sopenharmony_ci struct attribute *a, int n) 121362306a36Sopenharmony_ci{ 121462306a36Sopenharmony_ci struct device *dev = kobj_to_dev(kobj); 121562306a36Sopenharmony_ci struct usb_interface *intf = to_usb_interface(dev); 121662306a36Sopenharmony_ci 121762306a36Sopenharmony_ci if (intf->intf_assoc == NULL) 121862306a36Sopenharmony_ci return 0; 121962306a36Sopenharmony_ci return a->mode; 122062306a36Sopenharmony_ci} 122162306a36Sopenharmony_ci 122262306a36Sopenharmony_cistatic const struct attribute_group intf_assoc_attr_grp = { 122362306a36Sopenharmony_ci .attrs = intf_assoc_attrs, 122462306a36Sopenharmony_ci .is_visible = intf_assoc_attrs_are_visible, 122562306a36Sopenharmony_ci}; 122662306a36Sopenharmony_ci 122762306a36Sopenharmony_cistatic ssize_t wireless_status_show(struct device *dev, 122862306a36Sopenharmony_ci struct device_attribute *attr, char *buf) 122962306a36Sopenharmony_ci{ 123062306a36Sopenharmony_ci struct usb_interface *intf; 123162306a36Sopenharmony_ci 123262306a36Sopenharmony_ci intf = to_usb_interface(dev); 123362306a36Sopenharmony_ci if (intf->wireless_status == USB_WIRELESS_STATUS_DISCONNECTED) 123462306a36Sopenharmony_ci return sysfs_emit(buf, "%s\n", "disconnected"); 123562306a36Sopenharmony_ci return sysfs_emit(buf, "%s\n", "connected"); 123662306a36Sopenharmony_ci} 123762306a36Sopenharmony_cistatic DEVICE_ATTR_RO(wireless_status); 123862306a36Sopenharmony_ci 123962306a36Sopenharmony_cistatic struct attribute *intf_wireless_status_attrs[] = { 124062306a36Sopenharmony_ci &dev_attr_wireless_status.attr, 124162306a36Sopenharmony_ci NULL 124262306a36Sopenharmony_ci}; 124362306a36Sopenharmony_ci 124462306a36Sopenharmony_cistatic umode_t intf_wireless_status_attr_is_visible(struct kobject *kobj, 124562306a36Sopenharmony_ci struct attribute *a, int n) 124662306a36Sopenharmony_ci{ 124762306a36Sopenharmony_ci struct device *dev = kobj_to_dev(kobj); 124862306a36Sopenharmony_ci struct usb_interface *intf = to_usb_interface(dev); 124962306a36Sopenharmony_ci 125062306a36Sopenharmony_ci if (a != &dev_attr_wireless_status.attr || 125162306a36Sopenharmony_ci intf->wireless_status != USB_WIRELESS_STATUS_NA) 125262306a36Sopenharmony_ci return a->mode; 125362306a36Sopenharmony_ci return 0; 125462306a36Sopenharmony_ci} 125562306a36Sopenharmony_ci 125662306a36Sopenharmony_cistatic const struct attribute_group intf_wireless_status_attr_grp = { 125762306a36Sopenharmony_ci .attrs = intf_wireless_status_attrs, 125862306a36Sopenharmony_ci .is_visible = intf_wireless_status_attr_is_visible, 125962306a36Sopenharmony_ci}; 126062306a36Sopenharmony_ci 126162306a36Sopenharmony_ciint usb_update_wireless_status_attr(struct usb_interface *intf) 126262306a36Sopenharmony_ci{ 126362306a36Sopenharmony_ci struct device *dev = &intf->dev; 126462306a36Sopenharmony_ci int ret; 126562306a36Sopenharmony_ci 126662306a36Sopenharmony_ci ret = sysfs_update_group(&dev->kobj, &intf_wireless_status_attr_grp); 126762306a36Sopenharmony_ci if (ret < 0) 126862306a36Sopenharmony_ci return ret; 126962306a36Sopenharmony_ci 127062306a36Sopenharmony_ci sysfs_notify(&dev->kobj, NULL, "wireless_status"); 127162306a36Sopenharmony_ci kobject_uevent(&dev->kobj, KOBJ_CHANGE); 127262306a36Sopenharmony_ci 127362306a36Sopenharmony_ci return 0; 127462306a36Sopenharmony_ci} 127562306a36Sopenharmony_ci 127662306a36Sopenharmony_ciconst struct attribute_group *usb_interface_groups[] = { 127762306a36Sopenharmony_ci &intf_attr_grp, 127862306a36Sopenharmony_ci &intf_assoc_attr_grp, 127962306a36Sopenharmony_ci &intf_wireless_status_attr_grp, 128062306a36Sopenharmony_ci NULL 128162306a36Sopenharmony_ci}; 128262306a36Sopenharmony_ci 128362306a36Sopenharmony_civoid usb_create_sysfs_intf_files(struct usb_interface *intf) 128462306a36Sopenharmony_ci{ 128562306a36Sopenharmony_ci struct usb_device *udev = interface_to_usbdev(intf); 128662306a36Sopenharmony_ci struct usb_host_interface *alt = intf->cur_altsetting; 128762306a36Sopenharmony_ci 128862306a36Sopenharmony_ci if (intf->sysfs_files_created || intf->unregistering) 128962306a36Sopenharmony_ci return; 129062306a36Sopenharmony_ci 129162306a36Sopenharmony_ci if (!alt->string && !(udev->quirks & USB_QUIRK_CONFIG_INTF_STRINGS)) 129262306a36Sopenharmony_ci alt->string = usb_cache_string(udev, alt->desc.iInterface); 129362306a36Sopenharmony_ci if (alt->string && device_create_file(&intf->dev, &dev_attr_interface)) { 129462306a36Sopenharmony_ci /* This is not a serious error */ 129562306a36Sopenharmony_ci dev_dbg(&intf->dev, "interface string descriptor file not created\n"); 129662306a36Sopenharmony_ci } 129762306a36Sopenharmony_ci intf->sysfs_files_created = 1; 129862306a36Sopenharmony_ci} 129962306a36Sopenharmony_ci 130062306a36Sopenharmony_civoid usb_remove_sysfs_intf_files(struct usb_interface *intf) 130162306a36Sopenharmony_ci{ 130262306a36Sopenharmony_ci if (!intf->sysfs_files_created) 130362306a36Sopenharmony_ci return; 130462306a36Sopenharmony_ci 130562306a36Sopenharmony_ci device_remove_file(&intf->dev, &dev_attr_interface); 130662306a36Sopenharmony_ci intf->sysfs_files_created = 0; 130762306a36Sopenharmony_ci} 1308