// SPDX-License-Identifier: GPL-2.0+ /* * devices.c * (C) Copyright 1999 Randy Dunlap. * (C) Copyright 1999,2000 Thomas Sailer <sailer@ife.ee.ethz.ch>. * (proc file per device) * (C) Copyright 1999 Deti Fliegl (new USB architecture) * ************************************************************* * * <mountpoint>/devices contains USB topology, device, config, class, * interface, & endpoint data. * * I considered using /dev/bus/usb/device# for each device * as it is attached or detached, but I didn't like this for some * reason -- maybe it's just too deep of a directory structure. * I also don't like looking in multiple places to gather and view * the data. Having only one file for ./devices also prevents race * conditions that could arise if a program was reading device info * for devices that are being removed (unplugged). (That is, the * program may find a directory for devnum_12 then try to open it, * but it was just unplugged, so the directory is now deleted. * But programs would just have to be prepared for situations like * this in any plug-and-play environment.) * * 1999-12-16: Thomas Sailer <sailer@ife.ee.ethz.ch> * Converted the whole proc stuff to real * read methods. Now not the whole device list needs to fit * into one page, only the device list for one bus. * Added a poll method to /sys/kernel/debug/usb/devices, to wake * up an eventual usbd * 2000-01-04: Thomas Sailer <sailer@ife.ee.ethz.ch> * Turned into its own filesystem * 2000-07-05: Ashley Montanaro <ashley@compsoc.man.ac.uk> * Converted file reading routine to dump to buffer once * per device, not per bus
*/
if (speed == USB_SPEED_HIGH)
bandwidth = usb_endpoint_maxp_mult(desc);
/* this isn't checking for illegal values */ switch (usb_endpoint_type(desc)) { case USB_ENDPOINT_XFER_CONTROL:
type = "Ctrl";
dir = 'B'; /* ctrl is bidirectional */ break; case USB_ENDPOINT_XFER_ISOC:
type = "Isoc"; break; case USB_ENDPOINT_XFER_BULK:
type = "Bulk"; break; case USB_ENDPOINT_XFER_INT:
type = "Int."; break; default: /* "can't happen" */ return start;
}
interval = usb_decode_interval(desc, speed); if (interval % 1000) {
unit = 'u';
} else {
unit = 'm';
interval /= 1000;
}
/* TBD: * 0. TBDs * 1. marking active interface altsettings (code lists all, but should mark * which ones are active, if any)
*/ staticchar *usb_dump_config_descriptor(char *start, char *end, conststruct usb_config_descriptor *desc, int active, int speed)
{ int mul;
if (start > end) return start; if (speed >= USB_SPEED_SUPER)
mul = 8; else
mul = 2;
start += sprintf(start, format_config, /* mark active/actual/current cfg. */
active ? '*' : ' ',
desc->bNumInterfaces,
desc->bConfigurationValue,
desc->bmAttributes,
desc->bMaxPower * mul); return start;
}
staticchar *usb_dump_config(int speed, char *start, char *end, conststruct usb_host_config *config, int active)
{ int i, j; struct usb_interface_cache *intfc; struct usb_interface *interface;
if (start > end) return start; if (!config) /* getting these some in 2.3.7; none in 2.3.6 */ return start + sprintf(start, "(null Cfg. desc.)\n");
start = usb_dump_config_descriptor(start, end, &config->desc, active,
speed); for (i = 0; i < USB_MAXIADS; i++) { if (config->intf_assoc[i] == NULL) break;
start = usb_dump_iad_descriptor(start, end,
config->intf_assoc[i]);
} for (i = 0; i < config->desc.bNumInterfaces; i++) {
intfc = config->intf_cache[i];
interface = config->interface[i]; for (j = 0; j < intfc->num_altsetting; j++) {
start = usb_dump_interface(speed,
start, end, intfc, interface, j);
}
} return start;
}
/* * Dump the different USB descriptors.
*/ staticchar *usb_dump_device_descriptor(char *start, char *end, conststruct usb_device_descriptor *desc)
{
u16 bcdUSB = le16_to_cpu(desc->bcdUSB);
u16 bcdDevice = le16_to_cpu(desc->bcdDevice);
/* This is a recursive function. Parameters: * buffer - the user-space buffer to write data into * nbytes - the maximum number of bytes to write * skip_bytes - the number of bytes to skip before writing anything * file_offset - the offset into the devices file on completion * The caller must own the device lock.
*/ static ssize_t usb_device_dump(char __user **buffer, size_t *nbytes,
loff_t *skip_bytes, loff_t *file_offset, struct usb_device *usbdev, struct usb_bus *bus, int level, int index, int count)
{ int chix; int ret, cnt = 0; int parent_devnum = 0; char *pages_start, *data_end, *speed; unsignedint length;
ssize_t total_written = 0; struct usb_device *childdev = NULL;
/* don't bother with anything else if we're not writing any data */ if (*nbytes <= 0) return 0;
if (level > MAX_TOPO_LEVEL) return 0; /* allocate 2^1 pages = 8K (on i386);
* should be more than enough for one device */
pages_start = (char *)__get_free_pages(GFP_NOIO, 1); if (!pages_start) return -ENOMEM;
if (usbdev->parent && usbdev->parent->devnum != -1)
parent_devnum = usbdev->parent->devnum; /* * So the root hub's parent is 0 and any device that is * plugged into the root hub has a parent of 0.
*/ switch (usbdev->speed) { case USB_SPEED_LOW:
speed = "1.5"; break; case USB_SPEED_UNKNOWN: /* usb 1.1 root hub code */ case USB_SPEED_FULL:
speed = "12"; break; case USB_SPEED_HIGH:
speed = "480"; break; case USB_SPEED_SUPER:
speed = "5000"; break; case USB_SPEED_SUPER_PLUS:
speed = "10000"; break; default:
speed = "??";
}
data_end = pages_start + sprintf(pages_start, format_topo,
bus->busnum, level, parent_devnum,
index, count, usbdev->devnum,
speed, usbdev->maxchild); /* * level = topology-tier level; * parent_devnum = parent device number; * index = parent's connector number; * count = device count at this level
*/ /* If this is the root hub, display the bandwidth information */ if (level == 0) { int max;
/* super/high speed reserves 80%, full/low reserves 90% */ if (usbdev->speed == USB_SPEED_HIGH ||
usbdev->speed >= USB_SPEED_SUPER)
max = 800; else
max = FRAME_TIME_MAX_USECS_ALLOC;
/* report "average" periodic allocation over a microsecond. * the schedules are actually bursty, HCDs need to deal with * that and just compute/report this average.
*/
data_end += sprintf(data_end, format_bandwidth,
bus->bandwidth_allocated, max,
(100 * bus->bandwidth_allocated + max / 2)
/ max,
bus->bandwidth_int_reqs,
bus->bandwidth_isoc_reqs);
Die Informationen auf dieser Webseite wurden
nach bestem Wissen sorgfältig zusammengestellt. Es wird jedoch weder Vollständigkeit, noch Richtigkeit,
noch Qualität der bereit gestellten Informationen zugesichert.
Bemerkung:
Die farbliche Syntaxdarstellung und die Messung sind noch experimentell.