18c2ecf20Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0+ 28c2ecf20Sopenharmony_ci/* 38c2ecf20Sopenharmony_ci * devices.c 48c2ecf20Sopenharmony_ci * (C) Copyright 1999 Randy Dunlap. 58c2ecf20Sopenharmony_ci * (C) Copyright 1999,2000 Thomas Sailer <sailer@ife.ee.ethz.ch>. 68c2ecf20Sopenharmony_ci * (proc file per device) 78c2ecf20Sopenharmony_ci * (C) Copyright 1999 Deti Fliegl (new USB architecture) 88c2ecf20Sopenharmony_ci * 98c2ecf20Sopenharmony_ci ************************************************************* 108c2ecf20Sopenharmony_ci * 118c2ecf20Sopenharmony_ci * <mountpoint>/devices contains USB topology, device, config, class, 128c2ecf20Sopenharmony_ci * interface, & endpoint data. 138c2ecf20Sopenharmony_ci * 148c2ecf20Sopenharmony_ci * I considered using /dev/bus/usb/device# for each device 158c2ecf20Sopenharmony_ci * as it is attached or detached, but I didn't like this for some 168c2ecf20Sopenharmony_ci * reason -- maybe it's just too deep of a directory structure. 178c2ecf20Sopenharmony_ci * I also don't like looking in multiple places to gather and view 188c2ecf20Sopenharmony_ci * the data. Having only one file for ./devices also prevents race 198c2ecf20Sopenharmony_ci * conditions that could arise if a program was reading device info 208c2ecf20Sopenharmony_ci * for devices that are being removed (unplugged). (That is, the 218c2ecf20Sopenharmony_ci * program may find a directory for devnum_12 then try to open it, 228c2ecf20Sopenharmony_ci * but it was just unplugged, so the directory is now deleted. 238c2ecf20Sopenharmony_ci * But programs would just have to be prepared for situations like 248c2ecf20Sopenharmony_ci * this in any plug-and-play environment.) 258c2ecf20Sopenharmony_ci * 268c2ecf20Sopenharmony_ci * 1999-12-16: Thomas Sailer <sailer@ife.ee.ethz.ch> 278c2ecf20Sopenharmony_ci * Converted the whole proc stuff to real 288c2ecf20Sopenharmony_ci * read methods. Now not the whole device list needs to fit 298c2ecf20Sopenharmony_ci * into one page, only the device list for one bus. 308c2ecf20Sopenharmony_ci * Added a poll method to /sys/kernel/debug/usb/devices, to wake 318c2ecf20Sopenharmony_ci * up an eventual usbd 328c2ecf20Sopenharmony_ci * 2000-01-04: Thomas Sailer <sailer@ife.ee.ethz.ch> 338c2ecf20Sopenharmony_ci * Turned into its own filesystem 348c2ecf20Sopenharmony_ci * 2000-07-05: Ashley Montanaro <ashley@compsoc.man.ac.uk> 358c2ecf20Sopenharmony_ci * Converted file reading routine to dump to buffer once 368c2ecf20Sopenharmony_ci * per device, not per bus 378c2ecf20Sopenharmony_ci */ 388c2ecf20Sopenharmony_ci 398c2ecf20Sopenharmony_ci#include <linux/fs.h> 408c2ecf20Sopenharmony_ci#include <linux/mm.h> 418c2ecf20Sopenharmony_ci#include <linux/gfp.h> 428c2ecf20Sopenharmony_ci#include <linux/usb.h> 438c2ecf20Sopenharmony_ci#include <linux/usbdevice_fs.h> 448c2ecf20Sopenharmony_ci#include <linux/usb/hcd.h> 458c2ecf20Sopenharmony_ci#include <linux/mutex.h> 468c2ecf20Sopenharmony_ci#include <linux/uaccess.h> 478c2ecf20Sopenharmony_ci 488c2ecf20Sopenharmony_ci#include "usb.h" 498c2ecf20Sopenharmony_ci 508c2ecf20Sopenharmony_ci/* Define ALLOW_SERIAL_NUMBER if you want to see the serial number of devices */ 518c2ecf20Sopenharmony_ci#define ALLOW_SERIAL_NUMBER 528c2ecf20Sopenharmony_ci 538c2ecf20Sopenharmony_cistatic const char format_topo[] = 548c2ecf20Sopenharmony_ci/* T: Bus=dd Lev=dd Prnt=dd Port=dd Cnt=dd Dev#=ddd Spd=dddd MxCh=dd */ 558c2ecf20Sopenharmony_ci"\nT: Bus=%2.2d Lev=%2.2d Prnt=%2.2d Port=%2.2d Cnt=%2.2d Dev#=%3d Spd=%-4s MxCh=%2d\n"; 568c2ecf20Sopenharmony_ci 578c2ecf20Sopenharmony_cistatic const char format_string_manufacturer[] = 588c2ecf20Sopenharmony_ci/* S: Manufacturer=xxxx */ 598c2ecf20Sopenharmony_ci "S: Manufacturer=%.100s\n"; 608c2ecf20Sopenharmony_ci 618c2ecf20Sopenharmony_cistatic const char format_string_product[] = 628c2ecf20Sopenharmony_ci/* S: Product=xxxx */ 638c2ecf20Sopenharmony_ci "S: Product=%.100s\n"; 648c2ecf20Sopenharmony_ci 658c2ecf20Sopenharmony_ci#ifdef ALLOW_SERIAL_NUMBER 668c2ecf20Sopenharmony_cistatic const char format_string_serialnumber[] = 678c2ecf20Sopenharmony_ci/* S: SerialNumber=xxxx */ 688c2ecf20Sopenharmony_ci "S: SerialNumber=%.100s\n"; 698c2ecf20Sopenharmony_ci#endif 708c2ecf20Sopenharmony_ci 718c2ecf20Sopenharmony_cistatic const char format_bandwidth[] = 728c2ecf20Sopenharmony_ci/* B: Alloc=ddd/ddd us (xx%), #Int=ddd, #Iso=ddd */ 738c2ecf20Sopenharmony_ci "B: Alloc=%3d/%3d us (%2d%%), #Int=%3d, #Iso=%3d\n"; 748c2ecf20Sopenharmony_ci 758c2ecf20Sopenharmony_cistatic const char format_device1[] = 768c2ecf20Sopenharmony_ci/* D: Ver=xx.xx Cls=xx(sssss) Sub=xx Prot=xx MxPS=dd #Cfgs=dd */ 778c2ecf20Sopenharmony_ci "D: Ver=%2x.%02x Cls=%02x(%-5s) Sub=%02x Prot=%02x MxPS=%2d #Cfgs=%3d\n"; 788c2ecf20Sopenharmony_ci 798c2ecf20Sopenharmony_cistatic const char format_device2[] = 808c2ecf20Sopenharmony_ci/* P: Vendor=xxxx ProdID=xxxx Rev=xx.xx */ 818c2ecf20Sopenharmony_ci "P: Vendor=%04x ProdID=%04x Rev=%2x.%02x\n"; 828c2ecf20Sopenharmony_ci 838c2ecf20Sopenharmony_cistatic const char format_config[] = 848c2ecf20Sopenharmony_ci/* C: #Ifs=dd Cfg#=dd Atr=xx MPwr=dddmA */ 858c2ecf20Sopenharmony_ci "C:%c #Ifs=%2d Cfg#=%2d Atr=%02x MxPwr=%3dmA\n"; 868c2ecf20Sopenharmony_ci 878c2ecf20Sopenharmony_cistatic const char format_iad[] = 888c2ecf20Sopenharmony_ci/* A: FirstIf#=dd IfCount=dd Cls=xx(sssss) Sub=xx Prot=xx */ 898c2ecf20Sopenharmony_ci "A: FirstIf#=%2d IfCount=%2d Cls=%02x(%-5s) Sub=%02x Prot=%02x\n"; 908c2ecf20Sopenharmony_ci 918c2ecf20Sopenharmony_cistatic const char format_iface[] = 928c2ecf20Sopenharmony_ci/* I: If#=dd Alt=dd #EPs=dd Cls=xx(sssss) Sub=xx Prot=xx Driver=xxxx*/ 938c2ecf20Sopenharmony_ci "I:%c If#=%2d Alt=%2d #EPs=%2d Cls=%02x(%-5s) Sub=%02x Prot=%02x Driver=%s\n"; 948c2ecf20Sopenharmony_ci 958c2ecf20Sopenharmony_cistatic const char format_endpt[] = 968c2ecf20Sopenharmony_ci/* E: Ad=xx(s) Atr=xx(ssss) MxPS=dddd Ivl=D?s */ 978c2ecf20Sopenharmony_ci "E: Ad=%02x(%c) Atr=%02x(%-4s) MxPS=%4d Ivl=%d%cs\n"; 988c2ecf20Sopenharmony_ci 998c2ecf20Sopenharmony_cistruct class_info { 1008c2ecf20Sopenharmony_ci int class; 1018c2ecf20Sopenharmony_ci char *class_name; 1028c2ecf20Sopenharmony_ci}; 1038c2ecf20Sopenharmony_ci 1048c2ecf20Sopenharmony_cistatic const struct class_info clas_info[] = { 1058c2ecf20Sopenharmony_ci /* max. 5 chars. per name string */ 1068c2ecf20Sopenharmony_ci {USB_CLASS_PER_INTERFACE, ">ifc"}, 1078c2ecf20Sopenharmony_ci {USB_CLASS_AUDIO, "audio"}, 1088c2ecf20Sopenharmony_ci {USB_CLASS_COMM, "comm."}, 1098c2ecf20Sopenharmony_ci {USB_CLASS_HID, "HID"}, 1108c2ecf20Sopenharmony_ci {USB_CLASS_PHYSICAL, "PID"}, 1118c2ecf20Sopenharmony_ci {USB_CLASS_STILL_IMAGE, "still"}, 1128c2ecf20Sopenharmony_ci {USB_CLASS_PRINTER, "print"}, 1138c2ecf20Sopenharmony_ci {USB_CLASS_MASS_STORAGE, "stor."}, 1148c2ecf20Sopenharmony_ci {USB_CLASS_HUB, "hub"}, 1158c2ecf20Sopenharmony_ci {USB_CLASS_CDC_DATA, "data"}, 1168c2ecf20Sopenharmony_ci {USB_CLASS_CSCID, "scard"}, 1178c2ecf20Sopenharmony_ci {USB_CLASS_CONTENT_SEC, "c-sec"}, 1188c2ecf20Sopenharmony_ci {USB_CLASS_VIDEO, "video"}, 1198c2ecf20Sopenharmony_ci {USB_CLASS_PERSONAL_HEALTHCARE, "perhc"}, 1208c2ecf20Sopenharmony_ci {USB_CLASS_AUDIO_VIDEO, "av"}, 1218c2ecf20Sopenharmony_ci {USB_CLASS_BILLBOARD, "blbrd"}, 1228c2ecf20Sopenharmony_ci {USB_CLASS_USB_TYPE_C_BRIDGE, "bridg"}, 1238c2ecf20Sopenharmony_ci {USB_CLASS_WIRELESS_CONTROLLER, "wlcon"}, 1248c2ecf20Sopenharmony_ci {USB_CLASS_MISC, "misc"}, 1258c2ecf20Sopenharmony_ci {USB_CLASS_APP_SPEC, "app."}, 1268c2ecf20Sopenharmony_ci {USB_CLASS_VENDOR_SPEC, "vend."}, 1278c2ecf20Sopenharmony_ci {-1, "unk."} /* leave as last */ 1288c2ecf20Sopenharmony_ci}; 1298c2ecf20Sopenharmony_ci 1308c2ecf20Sopenharmony_ci/*****************************************************************/ 1318c2ecf20Sopenharmony_ci 1328c2ecf20Sopenharmony_cistatic const char *class_decode(const int class) 1338c2ecf20Sopenharmony_ci{ 1348c2ecf20Sopenharmony_ci int ix; 1358c2ecf20Sopenharmony_ci 1368c2ecf20Sopenharmony_ci for (ix = 0; clas_info[ix].class != -1; ix++) 1378c2ecf20Sopenharmony_ci if (clas_info[ix].class == class) 1388c2ecf20Sopenharmony_ci break; 1398c2ecf20Sopenharmony_ci return clas_info[ix].class_name; 1408c2ecf20Sopenharmony_ci} 1418c2ecf20Sopenharmony_ci 1428c2ecf20Sopenharmony_cistatic char *usb_dump_endpoint_descriptor(int speed, char *start, char *end, 1438c2ecf20Sopenharmony_ci const struct usb_endpoint_descriptor *desc) 1448c2ecf20Sopenharmony_ci{ 1458c2ecf20Sopenharmony_ci char dir, unit, *type; 1468c2ecf20Sopenharmony_ci unsigned interval, bandwidth = 1; 1478c2ecf20Sopenharmony_ci 1488c2ecf20Sopenharmony_ci if (start > end) 1498c2ecf20Sopenharmony_ci return start; 1508c2ecf20Sopenharmony_ci 1518c2ecf20Sopenharmony_ci dir = usb_endpoint_dir_in(desc) ? 'I' : 'O'; 1528c2ecf20Sopenharmony_ci 1538c2ecf20Sopenharmony_ci if (speed == USB_SPEED_HIGH) 1548c2ecf20Sopenharmony_ci bandwidth = usb_endpoint_maxp_mult(desc); 1558c2ecf20Sopenharmony_ci 1568c2ecf20Sopenharmony_ci /* this isn't checking for illegal values */ 1578c2ecf20Sopenharmony_ci switch (usb_endpoint_type(desc)) { 1588c2ecf20Sopenharmony_ci case USB_ENDPOINT_XFER_CONTROL: 1598c2ecf20Sopenharmony_ci type = "Ctrl"; 1608c2ecf20Sopenharmony_ci dir = 'B'; /* ctrl is bidirectional */ 1618c2ecf20Sopenharmony_ci break; 1628c2ecf20Sopenharmony_ci case USB_ENDPOINT_XFER_ISOC: 1638c2ecf20Sopenharmony_ci type = "Isoc"; 1648c2ecf20Sopenharmony_ci break; 1658c2ecf20Sopenharmony_ci case USB_ENDPOINT_XFER_BULK: 1668c2ecf20Sopenharmony_ci type = "Bulk"; 1678c2ecf20Sopenharmony_ci break; 1688c2ecf20Sopenharmony_ci case USB_ENDPOINT_XFER_INT: 1698c2ecf20Sopenharmony_ci type = "Int."; 1708c2ecf20Sopenharmony_ci break; 1718c2ecf20Sopenharmony_ci default: /* "can't happen" */ 1728c2ecf20Sopenharmony_ci return start; 1738c2ecf20Sopenharmony_ci } 1748c2ecf20Sopenharmony_ci 1758c2ecf20Sopenharmony_ci interval = usb_decode_interval(desc, speed); 1768c2ecf20Sopenharmony_ci if (interval % 1000) { 1778c2ecf20Sopenharmony_ci unit = 'u'; 1788c2ecf20Sopenharmony_ci } else { 1798c2ecf20Sopenharmony_ci unit = 'm'; 1808c2ecf20Sopenharmony_ci interval /= 1000; 1818c2ecf20Sopenharmony_ci } 1828c2ecf20Sopenharmony_ci 1838c2ecf20Sopenharmony_ci start += sprintf(start, format_endpt, desc->bEndpointAddress, dir, 1848c2ecf20Sopenharmony_ci desc->bmAttributes, type, 1858c2ecf20Sopenharmony_ci usb_endpoint_maxp(desc) * 1868c2ecf20Sopenharmony_ci bandwidth, 1878c2ecf20Sopenharmony_ci interval, unit); 1888c2ecf20Sopenharmony_ci return start; 1898c2ecf20Sopenharmony_ci} 1908c2ecf20Sopenharmony_ci 1918c2ecf20Sopenharmony_cistatic char *usb_dump_interface_descriptor(char *start, char *end, 1928c2ecf20Sopenharmony_ci const struct usb_interface_cache *intfc, 1938c2ecf20Sopenharmony_ci const struct usb_interface *iface, 1948c2ecf20Sopenharmony_ci int setno) 1958c2ecf20Sopenharmony_ci{ 1968c2ecf20Sopenharmony_ci const struct usb_interface_descriptor *desc; 1978c2ecf20Sopenharmony_ci const char *driver_name = ""; 1988c2ecf20Sopenharmony_ci int active = 0; 1998c2ecf20Sopenharmony_ci 2008c2ecf20Sopenharmony_ci if (start > end) 2018c2ecf20Sopenharmony_ci return start; 2028c2ecf20Sopenharmony_ci desc = &intfc->altsetting[setno].desc; 2038c2ecf20Sopenharmony_ci if (iface) { 2048c2ecf20Sopenharmony_ci driver_name = (iface->dev.driver 2058c2ecf20Sopenharmony_ci ? iface->dev.driver->name 2068c2ecf20Sopenharmony_ci : "(none)"); 2078c2ecf20Sopenharmony_ci active = (desc == &iface->cur_altsetting->desc); 2088c2ecf20Sopenharmony_ci } 2098c2ecf20Sopenharmony_ci start += sprintf(start, format_iface, 2108c2ecf20Sopenharmony_ci active ? '*' : ' ', /* mark active altsetting */ 2118c2ecf20Sopenharmony_ci desc->bInterfaceNumber, 2128c2ecf20Sopenharmony_ci desc->bAlternateSetting, 2138c2ecf20Sopenharmony_ci desc->bNumEndpoints, 2148c2ecf20Sopenharmony_ci desc->bInterfaceClass, 2158c2ecf20Sopenharmony_ci class_decode(desc->bInterfaceClass), 2168c2ecf20Sopenharmony_ci desc->bInterfaceSubClass, 2178c2ecf20Sopenharmony_ci desc->bInterfaceProtocol, 2188c2ecf20Sopenharmony_ci driver_name); 2198c2ecf20Sopenharmony_ci return start; 2208c2ecf20Sopenharmony_ci} 2218c2ecf20Sopenharmony_ci 2228c2ecf20Sopenharmony_cistatic char *usb_dump_interface(int speed, char *start, char *end, 2238c2ecf20Sopenharmony_ci const struct usb_interface_cache *intfc, 2248c2ecf20Sopenharmony_ci const struct usb_interface *iface, int setno) 2258c2ecf20Sopenharmony_ci{ 2268c2ecf20Sopenharmony_ci const struct usb_host_interface *desc = &intfc->altsetting[setno]; 2278c2ecf20Sopenharmony_ci int i; 2288c2ecf20Sopenharmony_ci 2298c2ecf20Sopenharmony_ci start = usb_dump_interface_descriptor(start, end, intfc, iface, setno); 2308c2ecf20Sopenharmony_ci for (i = 0; i < desc->desc.bNumEndpoints; i++) { 2318c2ecf20Sopenharmony_ci if (start > end) 2328c2ecf20Sopenharmony_ci return start; 2338c2ecf20Sopenharmony_ci start = usb_dump_endpoint_descriptor(speed, 2348c2ecf20Sopenharmony_ci start, end, &desc->endpoint[i].desc); 2358c2ecf20Sopenharmony_ci } 2368c2ecf20Sopenharmony_ci return start; 2378c2ecf20Sopenharmony_ci} 2388c2ecf20Sopenharmony_ci 2398c2ecf20Sopenharmony_cistatic char *usb_dump_iad_descriptor(char *start, char *end, 2408c2ecf20Sopenharmony_ci const struct usb_interface_assoc_descriptor *iad) 2418c2ecf20Sopenharmony_ci{ 2428c2ecf20Sopenharmony_ci if (start > end) 2438c2ecf20Sopenharmony_ci return start; 2448c2ecf20Sopenharmony_ci start += sprintf(start, format_iad, 2458c2ecf20Sopenharmony_ci iad->bFirstInterface, 2468c2ecf20Sopenharmony_ci iad->bInterfaceCount, 2478c2ecf20Sopenharmony_ci iad->bFunctionClass, 2488c2ecf20Sopenharmony_ci class_decode(iad->bFunctionClass), 2498c2ecf20Sopenharmony_ci iad->bFunctionSubClass, 2508c2ecf20Sopenharmony_ci iad->bFunctionProtocol); 2518c2ecf20Sopenharmony_ci return start; 2528c2ecf20Sopenharmony_ci} 2538c2ecf20Sopenharmony_ci 2548c2ecf20Sopenharmony_ci/* TBD: 2558c2ecf20Sopenharmony_ci * 0. TBDs 2568c2ecf20Sopenharmony_ci * 1. marking active interface altsettings (code lists all, but should mark 2578c2ecf20Sopenharmony_ci * which ones are active, if any) 2588c2ecf20Sopenharmony_ci */ 2598c2ecf20Sopenharmony_cistatic char *usb_dump_config_descriptor(char *start, char *end, 2608c2ecf20Sopenharmony_ci const struct usb_config_descriptor *desc, 2618c2ecf20Sopenharmony_ci int active, int speed) 2628c2ecf20Sopenharmony_ci{ 2638c2ecf20Sopenharmony_ci int mul; 2648c2ecf20Sopenharmony_ci 2658c2ecf20Sopenharmony_ci if (start > end) 2668c2ecf20Sopenharmony_ci return start; 2678c2ecf20Sopenharmony_ci if (speed >= USB_SPEED_SUPER) 2688c2ecf20Sopenharmony_ci mul = 8; 2698c2ecf20Sopenharmony_ci else 2708c2ecf20Sopenharmony_ci mul = 2; 2718c2ecf20Sopenharmony_ci start += sprintf(start, format_config, 2728c2ecf20Sopenharmony_ci /* mark active/actual/current cfg. */ 2738c2ecf20Sopenharmony_ci active ? '*' : ' ', 2748c2ecf20Sopenharmony_ci desc->bNumInterfaces, 2758c2ecf20Sopenharmony_ci desc->bConfigurationValue, 2768c2ecf20Sopenharmony_ci desc->bmAttributes, 2778c2ecf20Sopenharmony_ci desc->bMaxPower * mul); 2788c2ecf20Sopenharmony_ci return start; 2798c2ecf20Sopenharmony_ci} 2808c2ecf20Sopenharmony_ci 2818c2ecf20Sopenharmony_cistatic char *usb_dump_config(int speed, char *start, char *end, 2828c2ecf20Sopenharmony_ci const struct usb_host_config *config, int active) 2838c2ecf20Sopenharmony_ci{ 2848c2ecf20Sopenharmony_ci int i, j; 2858c2ecf20Sopenharmony_ci struct usb_interface_cache *intfc; 2868c2ecf20Sopenharmony_ci struct usb_interface *interface; 2878c2ecf20Sopenharmony_ci 2888c2ecf20Sopenharmony_ci if (start > end) 2898c2ecf20Sopenharmony_ci return start; 2908c2ecf20Sopenharmony_ci if (!config) 2918c2ecf20Sopenharmony_ci /* getting these some in 2.3.7; none in 2.3.6 */ 2928c2ecf20Sopenharmony_ci return start + sprintf(start, "(null Cfg. desc.)\n"); 2938c2ecf20Sopenharmony_ci start = usb_dump_config_descriptor(start, end, &config->desc, active, 2948c2ecf20Sopenharmony_ci speed); 2958c2ecf20Sopenharmony_ci for (i = 0; i < USB_MAXIADS; i++) { 2968c2ecf20Sopenharmony_ci if (config->intf_assoc[i] == NULL) 2978c2ecf20Sopenharmony_ci break; 2988c2ecf20Sopenharmony_ci start = usb_dump_iad_descriptor(start, end, 2998c2ecf20Sopenharmony_ci config->intf_assoc[i]); 3008c2ecf20Sopenharmony_ci } 3018c2ecf20Sopenharmony_ci for (i = 0; i < config->desc.bNumInterfaces; i++) { 3028c2ecf20Sopenharmony_ci intfc = config->intf_cache[i]; 3038c2ecf20Sopenharmony_ci interface = config->interface[i]; 3048c2ecf20Sopenharmony_ci for (j = 0; j < intfc->num_altsetting; j++) { 3058c2ecf20Sopenharmony_ci if (start > end) 3068c2ecf20Sopenharmony_ci return start; 3078c2ecf20Sopenharmony_ci start = usb_dump_interface(speed, 3088c2ecf20Sopenharmony_ci start, end, intfc, interface, j); 3098c2ecf20Sopenharmony_ci } 3108c2ecf20Sopenharmony_ci } 3118c2ecf20Sopenharmony_ci return start; 3128c2ecf20Sopenharmony_ci} 3138c2ecf20Sopenharmony_ci 3148c2ecf20Sopenharmony_ci/* 3158c2ecf20Sopenharmony_ci * Dump the different USB descriptors. 3168c2ecf20Sopenharmony_ci */ 3178c2ecf20Sopenharmony_cistatic char *usb_dump_device_descriptor(char *start, char *end, 3188c2ecf20Sopenharmony_ci const struct usb_device_descriptor *desc) 3198c2ecf20Sopenharmony_ci{ 3208c2ecf20Sopenharmony_ci u16 bcdUSB = le16_to_cpu(desc->bcdUSB); 3218c2ecf20Sopenharmony_ci u16 bcdDevice = le16_to_cpu(desc->bcdDevice); 3228c2ecf20Sopenharmony_ci 3238c2ecf20Sopenharmony_ci if (start > end) 3248c2ecf20Sopenharmony_ci return start; 3258c2ecf20Sopenharmony_ci start += sprintf(start, format_device1, 3268c2ecf20Sopenharmony_ci bcdUSB >> 8, bcdUSB & 0xff, 3278c2ecf20Sopenharmony_ci desc->bDeviceClass, 3288c2ecf20Sopenharmony_ci class_decode(desc->bDeviceClass), 3298c2ecf20Sopenharmony_ci desc->bDeviceSubClass, 3308c2ecf20Sopenharmony_ci desc->bDeviceProtocol, 3318c2ecf20Sopenharmony_ci desc->bMaxPacketSize0, 3328c2ecf20Sopenharmony_ci desc->bNumConfigurations); 3338c2ecf20Sopenharmony_ci if (start > end) 3348c2ecf20Sopenharmony_ci return start; 3358c2ecf20Sopenharmony_ci start += sprintf(start, format_device2, 3368c2ecf20Sopenharmony_ci le16_to_cpu(desc->idVendor), 3378c2ecf20Sopenharmony_ci le16_to_cpu(desc->idProduct), 3388c2ecf20Sopenharmony_ci bcdDevice >> 8, bcdDevice & 0xff); 3398c2ecf20Sopenharmony_ci return start; 3408c2ecf20Sopenharmony_ci} 3418c2ecf20Sopenharmony_ci 3428c2ecf20Sopenharmony_ci/* 3438c2ecf20Sopenharmony_ci * Dump the different strings that this device holds. 3448c2ecf20Sopenharmony_ci */ 3458c2ecf20Sopenharmony_cistatic char *usb_dump_device_strings(char *start, char *end, 3468c2ecf20Sopenharmony_ci struct usb_device *dev) 3478c2ecf20Sopenharmony_ci{ 3488c2ecf20Sopenharmony_ci if (start > end) 3498c2ecf20Sopenharmony_ci return start; 3508c2ecf20Sopenharmony_ci if (dev->manufacturer) 3518c2ecf20Sopenharmony_ci start += sprintf(start, format_string_manufacturer, 3528c2ecf20Sopenharmony_ci dev->manufacturer); 3538c2ecf20Sopenharmony_ci if (start > end) 3548c2ecf20Sopenharmony_ci goto out; 3558c2ecf20Sopenharmony_ci if (dev->product) 3568c2ecf20Sopenharmony_ci start += sprintf(start, format_string_product, dev->product); 3578c2ecf20Sopenharmony_ci if (start > end) 3588c2ecf20Sopenharmony_ci goto out; 3598c2ecf20Sopenharmony_ci#ifdef ALLOW_SERIAL_NUMBER 3608c2ecf20Sopenharmony_ci if (dev->serial) 3618c2ecf20Sopenharmony_ci start += sprintf(start, format_string_serialnumber, 3628c2ecf20Sopenharmony_ci dev->serial); 3638c2ecf20Sopenharmony_ci#endif 3648c2ecf20Sopenharmony_ci out: 3658c2ecf20Sopenharmony_ci return start; 3668c2ecf20Sopenharmony_ci} 3678c2ecf20Sopenharmony_ci 3688c2ecf20Sopenharmony_cistatic char *usb_dump_desc(char *start, char *end, struct usb_device *dev) 3698c2ecf20Sopenharmony_ci{ 3708c2ecf20Sopenharmony_ci int i; 3718c2ecf20Sopenharmony_ci 3728c2ecf20Sopenharmony_ci if (start > end) 3738c2ecf20Sopenharmony_ci return start; 3748c2ecf20Sopenharmony_ci 3758c2ecf20Sopenharmony_ci start = usb_dump_device_descriptor(start, end, &dev->descriptor); 3768c2ecf20Sopenharmony_ci 3778c2ecf20Sopenharmony_ci if (start > end) 3788c2ecf20Sopenharmony_ci return start; 3798c2ecf20Sopenharmony_ci 3808c2ecf20Sopenharmony_ci start = usb_dump_device_strings(start, end, dev); 3818c2ecf20Sopenharmony_ci 3828c2ecf20Sopenharmony_ci for (i = 0; i < dev->descriptor.bNumConfigurations; i++) { 3838c2ecf20Sopenharmony_ci if (start > end) 3848c2ecf20Sopenharmony_ci return start; 3858c2ecf20Sopenharmony_ci start = usb_dump_config(dev->speed, 3868c2ecf20Sopenharmony_ci start, end, dev->config + i, 3878c2ecf20Sopenharmony_ci /* active ? */ 3888c2ecf20Sopenharmony_ci (dev->config + i) == dev->actconfig); 3898c2ecf20Sopenharmony_ci } 3908c2ecf20Sopenharmony_ci return start; 3918c2ecf20Sopenharmony_ci} 3928c2ecf20Sopenharmony_ci 3938c2ecf20Sopenharmony_ci 3948c2ecf20Sopenharmony_ci#ifdef PROC_EXTRA /* TBD: may want to add this code later */ 3958c2ecf20Sopenharmony_ci 3968c2ecf20Sopenharmony_cistatic char *usb_dump_hub_descriptor(char *start, char *end, 3978c2ecf20Sopenharmony_ci const struct usb_hub_descriptor *desc) 3988c2ecf20Sopenharmony_ci{ 3998c2ecf20Sopenharmony_ci int leng = USB_DT_HUB_NONVAR_SIZE; 4008c2ecf20Sopenharmony_ci unsigned char *ptr = (unsigned char *)desc; 4018c2ecf20Sopenharmony_ci 4028c2ecf20Sopenharmony_ci if (start > end) 4038c2ecf20Sopenharmony_ci return start; 4048c2ecf20Sopenharmony_ci start += sprintf(start, "Interface:"); 4058c2ecf20Sopenharmony_ci while (leng && start <= end) { 4068c2ecf20Sopenharmony_ci start += sprintf(start, " %02x", *ptr); 4078c2ecf20Sopenharmony_ci ptr++; leng--; 4088c2ecf20Sopenharmony_ci } 4098c2ecf20Sopenharmony_ci *start++ = '\n'; 4108c2ecf20Sopenharmony_ci return start; 4118c2ecf20Sopenharmony_ci} 4128c2ecf20Sopenharmony_ci 4138c2ecf20Sopenharmony_cistatic char *usb_dump_string(char *start, char *end, 4148c2ecf20Sopenharmony_ci const struct usb_device *dev, char *id, int index) 4158c2ecf20Sopenharmony_ci{ 4168c2ecf20Sopenharmony_ci if (start > end) 4178c2ecf20Sopenharmony_ci return start; 4188c2ecf20Sopenharmony_ci start += sprintf(start, "Interface:"); 4198c2ecf20Sopenharmony_ci if (index <= dev->maxstring && dev->stringindex && 4208c2ecf20Sopenharmony_ci dev->stringindex[index]) 4218c2ecf20Sopenharmony_ci start += sprintf(start, "%s: %.100s ", id, 4228c2ecf20Sopenharmony_ci dev->stringindex[index]); 4238c2ecf20Sopenharmony_ci return start; 4248c2ecf20Sopenharmony_ci} 4258c2ecf20Sopenharmony_ci 4268c2ecf20Sopenharmony_ci#endif /* PROC_EXTRA */ 4278c2ecf20Sopenharmony_ci 4288c2ecf20Sopenharmony_ci/*****************************************************************/ 4298c2ecf20Sopenharmony_ci 4308c2ecf20Sopenharmony_ci/* This is a recursive function. Parameters: 4318c2ecf20Sopenharmony_ci * buffer - the user-space buffer to write data into 4328c2ecf20Sopenharmony_ci * nbytes - the maximum number of bytes to write 4338c2ecf20Sopenharmony_ci * skip_bytes - the number of bytes to skip before writing anything 4348c2ecf20Sopenharmony_ci * file_offset - the offset into the devices file on completion 4358c2ecf20Sopenharmony_ci * The caller must own the device lock. 4368c2ecf20Sopenharmony_ci */ 4378c2ecf20Sopenharmony_cistatic ssize_t usb_device_dump(char __user **buffer, size_t *nbytes, 4388c2ecf20Sopenharmony_ci loff_t *skip_bytes, loff_t *file_offset, 4398c2ecf20Sopenharmony_ci struct usb_device *usbdev, struct usb_bus *bus, 4408c2ecf20Sopenharmony_ci int level, int index, int count) 4418c2ecf20Sopenharmony_ci{ 4428c2ecf20Sopenharmony_ci int chix; 4438c2ecf20Sopenharmony_ci int ret, cnt = 0; 4448c2ecf20Sopenharmony_ci int parent_devnum = 0; 4458c2ecf20Sopenharmony_ci char *pages_start, *data_end, *speed; 4468c2ecf20Sopenharmony_ci unsigned int length; 4478c2ecf20Sopenharmony_ci ssize_t total_written = 0; 4488c2ecf20Sopenharmony_ci struct usb_device *childdev = NULL; 4498c2ecf20Sopenharmony_ci 4508c2ecf20Sopenharmony_ci /* don't bother with anything else if we're not writing any data */ 4518c2ecf20Sopenharmony_ci if (*nbytes <= 0) 4528c2ecf20Sopenharmony_ci return 0; 4538c2ecf20Sopenharmony_ci 4548c2ecf20Sopenharmony_ci if (level > MAX_TOPO_LEVEL) 4558c2ecf20Sopenharmony_ci return 0; 4568c2ecf20Sopenharmony_ci /* allocate 2^1 pages = 8K (on i386); 4578c2ecf20Sopenharmony_ci * should be more than enough for one device */ 4588c2ecf20Sopenharmony_ci pages_start = (char *)__get_free_pages(GFP_NOIO, 1); 4598c2ecf20Sopenharmony_ci if (!pages_start) 4608c2ecf20Sopenharmony_ci return -ENOMEM; 4618c2ecf20Sopenharmony_ci 4628c2ecf20Sopenharmony_ci if (usbdev->parent && usbdev->parent->devnum != -1) 4638c2ecf20Sopenharmony_ci parent_devnum = usbdev->parent->devnum; 4648c2ecf20Sopenharmony_ci /* 4658c2ecf20Sopenharmony_ci * So the root hub's parent is 0 and any device that is 4668c2ecf20Sopenharmony_ci * plugged into the root hub has a parent of 0. 4678c2ecf20Sopenharmony_ci */ 4688c2ecf20Sopenharmony_ci switch (usbdev->speed) { 4698c2ecf20Sopenharmony_ci case USB_SPEED_LOW: 4708c2ecf20Sopenharmony_ci speed = "1.5"; break; 4718c2ecf20Sopenharmony_ci case USB_SPEED_UNKNOWN: /* usb 1.1 root hub code */ 4728c2ecf20Sopenharmony_ci case USB_SPEED_FULL: 4738c2ecf20Sopenharmony_ci speed = "12"; break; 4748c2ecf20Sopenharmony_ci case USB_SPEED_WIRELESS: /* Wireless has no real fixed speed */ 4758c2ecf20Sopenharmony_ci case USB_SPEED_HIGH: 4768c2ecf20Sopenharmony_ci speed = "480"; break; 4778c2ecf20Sopenharmony_ci case USB_SPEED_SUPER: 4788c2ecf20Sopenharmony_ci speed = "5000"; break; 4798c2ecf20Sopenharmony_ci case USB_SPEED_SUPER_PLUS: 4808c2ecf20Sopenharmony_ci speed = "10000"; break; 4818c2ecf20Sopenharmony_ci default: 4828c2ecf20Sopenharmony_ci speed = "??"; 4838c2ecf20Sopenharmony_ci } 4848c2ecf20Sopenharmony_ci data_end = pages_start + sprintf(pages_start, format_topo, 4858c2ecf20Sopenharmony_ci bus->busnum, level, parent_devnum, 4868c2ecf20Sopenharmony_ci index, count, usbdev->devnum, 4878c2ecf20Sopenharmony_ci speed, usbdev->maxchild); 4888c2ecf20Sopenharmony_ci /* 4898c2ecf20Sopenharmony_ci * level = topology-tier level; 4908c2ecf20Sopenharmony_ci * parent_devnum = parent device number; 4918c2ecf20Sopenharmony_ci * index = parent's connector number; 4928c2ecf20Sopenharmony_ci * count = device count at this level 4938c2ecf20Sopenharmony_ci */ 4948c2ecf20Sopenharmony_ci /* If this is the root hub, display the bandwidth information */ 4958c2ecf20Sopenharmony_ci if (level == 0) { 4968c2ecf20Sopenharmony_ci int max; 4978c2ecf20Sopenharmony_ci 4988c2ecf20Sopenharmony_ci /* super/high speed reserves 80%, full/low reserves 90% */ 4998c2ecf20Sopenharmony_ci if (usbdev->speed == USB_SPEED_HIGH || 5008c2ecf20Sopenharmony_ci usbdev->speed >= USB_SPEED_SUPER) 5018c2ecf20Sopenharmony_ci max = 800; 5028c2ecf20Sopenharmony_ci else 5038c2ecf20Sopenharmony_ci max = FRAME_TIME_MAX_USECS_ALLOC; 5048c2ecf20Sopenharmony_ci 5058c2ecf20Sopenharmony_ci /* report "average" periodic allocation over a microsecond. 5068c2ecf20Sopenharmony_ci * the schedules are actually bursty, HCDs need to deal with 5078c2ecf20Sopenharmony_ci * that and just compute/report this average. 5088c2ecf20Sopenharmony_ci */ 5098c2ecf20Sopenharmony_ci data_end += sprintf(data_end, format_bandwidth, 5108c2ecf20Sopenharmony_ci bus->bandwidth_allocated, max, 5118c2ecf20Sopenharmony_ci (100 * bus->bandwidth_allocated + max / 2) 5128c2ecf20Sopenharmony_ci / max, 5138c2ecf20Sopenharmony_ci bus->bandwidth_int_reqs, 5148c2ecf20Sopenharmony_ci bus->bandwidth_isoc_reqs); 5158c2ecf20Sopenharmony_ci 5168c2ecf20Sopenharmony_ci } 5178c2ecf20Sopenharmony_ci data_end = usb_dump_desc(data_end, pages_start + (2 * PAGE_SIZE) - 256, 5188c2ecf20Sopenharmony_ci usbdev); 5198c2ecf20Sopenharmony_ci 5208c2ecf20Sopenharmony_ci if (data_end > (pages_start + (2 * PAGE_SIZE) - 256)) 5218c2ecf20Sopenharmony_ci data_end += sprintf(data_end, "(truncated)\n"); 5228c2ecf20Sopenharmony_ci 5238c2ecf20Sopenharmony_ci length = data_end - pages_start; 5248c2ecf20Sopenharmony_ci /* if we can start copying some data to the user */ 5258c2ecf20Sopenharmony_ci if (length > *skip_bytes) { 5268c2ecf20Sopenharmony_ci length -= *skip_bytes; 5278c2ecf20Sopenharmony_ci if (length > *nbytes) 5288c2ecf20Sopenharmony_ci length = *nbytes; 5298c2ecf20Sopenharmony_ci if (copy_to_user(*buffer, pages_start + *skip_bytes, length)) { 5308c2ecf20Sopenharmony_ci free_pages((unsigned long)pages_start, 1); 5318c2ecf20Sopenharmony_ci return -EFAULT; 5328c2ecf20Sopenharmony_ci } 5338c2ecf20Sopenharmony_ci *nbytes -= length; 5348c2ecf20Sopenharmony_ci *file_offset += length; 5358c2ecf20Sopenharmony_ci total_written += length; 5368c2ecf20Sopenharmony_ci *buffer += length; 5378c2ecf20Sopenharmony_ci *skip_bytes = 0; 5388c2ecf20Sopenharmony_ci } else 5398c2ecf20Sopenharmony_ci *skip_bytes -= length; 5408c2ecf20Sopenharmony_ci 5418c2ecf20Sopenharmony_ci free_pages((unsigned long)pages_start, 1); 5428c2ecf20Sopenharmony_ci 5438c2ecf20Sopenharmony_ci /* Now look at all of this device's children. */ 5448c2ecf20Sopenharmony_ci usb_hub_for_each_child(usbdev, chix, childdev) { 5458c2ecf20Sopenharmony_ci usb_lock_device(childdev); 5468c2ecf20Sopenharmony_ci ret = usb_device_dump(buffer, nbytes, skip_bytes, 5478c2ecf20Sopenharmony_ci file_offset, childdev, bus, 5488c2ecf20Sopenharmony_ci level + 1, chix - 1, ++cnt); 5498c2ecf20Sopenharmony_ci usb_unlock_device(childdev); 5508c2ecf20Sopenharmony_ci if (ret == -EFAULT) 5518c2ecf20Sopenharmony_ci return total_written; 5528c2ecf20Sopenharmony_ci total_written += ret; 5538c2ecf20Sopenharmony_ci } 5548c2ecf20Sopenharmony_ci return total_written; 5558c2ecf20Sopenharmony_ci} 5568c2ecf20Sopenharmony_ci 5578c2ecf20Sopenharmony_cistatic ssize_t usb_device_read(struct file *file, char __user *buf, 5588c2ecf20Sopenharmony_ci size_t nbytes, loff_t *ppos) 5598c2ecf20Sopenharmony_ci{ 5608c2ecf20Sopenharmony_ci struct usb_bus *bus; 5618c2ecf20Sopenharmony_ci ssize_t ret, total_written = 0; 5628c2ecf20Sopenharmony_ci loff_t skip_bytes = *ppos; 5638c2ecf20Sopenharmony_ci int id; 5648c2ecf20Sopenharmony_ci 5658c2ecf20Sopenharmony_ci if (*ppos < 0) 5668c2ecf20Sopenharmony_ci return -EINVAL; 5678c2ecf20Sopenharmony_ci if (nbytes <= 0) 5688c2ecf20Sopenharmony_ci return 0; 5698c2ecf20Sopenharmony_ci 5708c2ecf20Sopenharmony_ci mutex_lock(&usb_bus_idr_lock); 5718c2ecf20Sopenharmony_ci /* print devices for all busses */ 5728c2ecf20Sopenharmony_ci idr_for_each_entry(&usb_bus_idr, bus, id) { 5738c2ecf20Sopenharmony_ci /* recurse through all children of the root hub */ 5748c2ecf20Sopenharmony_ci if (!bus_to_hcd(bus)->rh_registered) 5758c2ecf20Sopenharmony_ci continue; 5768c2ecf20Sopenharmony_ci usb_lock_device(bus->root_hub); 5778c2ecf20Sopenharmony_ci ret = usb_device_dump(&buf, &nbytes, &skip_bytes, ppos, 5788c2ecf20Sopenharmony_ci bus->root_hub, bus, 0, 0, 0); 5798c2ecf20Sopenharmony_ci usb_unlock_device(bus->root_hub); 5808c2ecf20Sopenharmony_ci if (ret < 0) { 5818c2ecf20Sopenharmony_ci mutex_unlock(&usb_bus_idr_lock); 5828c2ecf20Sopenharmony_ci return ret; 5838c2ecf20Sopenharmony_ci } 5848c2ecf20Sopenharmony_ci total_written += ret; 5858c2ecf20Sopenharmony_ci } 5868c2ecf20Sopenharmony_ci mutex_unlock(&usb_bus_idr_lock); 5878c2ecf20Sopenharmony_ci return total_written; 5888c2ecf20Sopenharmony_ci} 5898c2ecf20Sopenharmony_ci 5908c2ecf20Sopenharmony_ciconst struct file_operations usbfs_devices_fops = { 5918c2ecf20Sopenharmony_ci .llseek = no_seek_end_llseek, 5928c2ecf20Sopenharmony_ci .read = usb_device_read, 5938c2ecf20Sopenharmony_ci}; 594