1 | // SPDX-License-Identifier: GPL-2.0+ |
2 | /* |
3 | * devices.c |
4 | * (C) Copyright 1999 Randy Dunlap. |
5 | * (C) Copyright 1999,2000 Thomas Sailer <sailer@ife.ee.ethz.ch>. |
6 | * (proc file per device) |
7 | * (C) Copyright 1999 Deti Fliegl (new USB architecture) |
8 | * |
9 | ************************************************************* |
10 | * |
11 | * <mountpoint>/devices contains USB topology, device, config, class, |
12 | * interface, & endpoint data. |
13 | * |
14 | * I considered using /dev/bus/usb/device# for each device |
15 | * as it is attached or detached, but I didn't like this for some |
16 | * reason -- maybe it's just too deep of a directory structure. |
17 | * I also don't like looking in multiple places to gather and view |
18 | * the data. Having only one file for ./devices also prevents race |
19 | * conditions that could arise if a program was reading device info |
20 | * for devices that are being removed (unplugged). (That is, the |
21 | * program may find a directory for devnum_12 then try to open it, |
22 | * but it was just unplugged, so the directory is now deleted. |
23 | * But programs would just have to be prepared for situations like |
24 | * this in any plug-and-play environment.) |
25 | * |
26 | * 1999-12-16: Thomas Sailer <sailer@ife.ee.ethz.ch> |
27 | * Converted the whole proc stuff to real |
28 | * read methods. Now not the whole device list needs to fit |
29 | * into one page, only the device list for one bus. |
30 | * Added a poll method to /sys/kernel/debug/usb/devices, to wake |
31 | * up an eventual usbd |
32 | * 2000-01-04: Thomas Sailer <sailer@ife.ee.ethz.ch> |
33 | * Turned into its own filesystem |
34 | * 2000-07-05: Ashley Montanaro <ashley@compsoc.man.ac.uk> |
35 | * Converted file reading routine to dump to buffer once |
36 | * per device, not per bus |
37 | */ |
38 | |
39 | #include <linux/fs.h> |
40 | #include <linux/mm.h> |
41 | #include <linux/gfp.h> |
42 | #include <linux/usb.h> |
43 | #include <linux/usbdevice_fs.h> |
44 | #include <linux/usb/hcd.h> |
45 | #include <linux/mutex.h> |
46 | #include <linux/uaccess.h> |
47 | |
48 | #include "usb.h" |
49 | |
50 | /* Define ALLOW_SERIAL_NUMBER if you want to see the serial number of devices */ |
51 | #define ALLOW_SERIAL_NUMBER |
52 | |
53 | static const char format_topo[] = |
54 | /* T: Bus=dd Lev=dd Prnt=dd Port=dd Cnt=dd Dev#=ddd Spd=dddd MxCh=dd */ |
55 | "\nT: Bus=%2.2d Lev=%2.2d Prnt=%2.2d Port=%2.2d Cnt=%2.2d Dev#=%3d Spd=%-4s MxCh=%2d\n" ; |
56 | |
57 | static const char format_string_manufacturer[] = |
58 | /* S: Manufacturer=xxxx */ |
59 | "S: Manufacturer=%.100s\n" ; |
60 | |
61 | static const char format_string_product[] = |
62 | /* S: Product=xxxx */ |
63 | "S: Product=%.100s\n" ; |
64 | |
65 | #ifdef ALLOW_SERIAL_NUMBER |
66 | static const char format_string_serialnumber[] = |
67 | /* S: SerialNumber=xxxx */ |
68 | "S: SerialNumber=%.100s\n" ; |
69 | #endif |
70 | |
71 | static const char format_bandwidth[] = |
72 | /* B: Alloc=ddd/ddd us (xx%), #Int=ddd, #Iso=ddd */ |
73 | "B: Alloc=%3d/%3d us (%2d%%), #Int=%3d, #Iso=%3d\n" ; |
74 | |
75 | static const char format_device1[] = |
76 | /* D: Ver=xx.xx Cls=xx(sssss) Sub=xx Prot=xx MxPS=dd #Cfgs=dd */ |
77 | "D: Ver=%2x.%02x Cls=%02x(%-5s) Sub=%02x Prot=%02x MxPS=%2d #Cfgs=%3d\n" ; |
78 | |
79 | static const char format_device2[] = |
80 | /* P: Vendor=xxxx ProdID=xxxx Rev=xx.xx */ |
81 | "P: Vendor=%04x ProdID=%04x Rev=%2x.%02x\n" ; |
82 | |
83 | static const char format_config[] = |
84 | /* C: #Ifs=dd Cfg#=dd Atr=xx MPwr=dddmA */ |
85 | "C:%c #Ifs=%2d Cfg#=%2d Atr=%02x MxPwr=%3dmA\n" ; |
86 | |
87 | static const char format_iad[] = |
88 | /* A: FirstIf#=dd IfCount=dd Cls=xx(sssss) Sub=xx Prot=xx */ |
89 | "A: FirstIf#=%2d IfCount=%2d Cls=%02x(%-5s) Sub=%02x Prot=%02x\n" ; |
90 | |
91 | static const char format_iface[] = |
92 | /* I: If#=dd Alt=dd #EPs=dd Cls=xx(sssss) Sub=xx Prot=xx Driver=xxxx*/ |
93 | "I:%c If#=%2d Alt=%2d #EPs=%2d Cls=%02x(%-5s) Sub=%02x Prot=%02x Driver=%s\n" ; |
94 | |
95 | static const char format_endpt[] = |
96 | /* E: Ad=xx(s) Atr=xx(ssss) MxPS=dddd Ivl=D?s */ |
97 | "E: Ad=%02x(%c) Atr=%02x(%-4s) MxPS=%4d Ivl=%d%cs\n" ; |
98 | |
99 | struct class_info { |
100 | int class; |
101 | char *class_name; |
102 | }; |
103 | |
104 | static const struct class_info clas_info[] = { |
105 | /* max. 5 chars. per name string */ |
106 | {USB_CLASS_PER_INTERFACE, ">ifc" }, |
107 | {USB_CLASS_AUDIO, "audio" }, |
108 | {USB_CLASS_COMM, "comm." }, |
109 | {USB_CLASS_HID, "HID" }, |
110 | {USB_CLASS_PHYSICAL, "PID" }, |
111 | {USB_CLASS_STILL_IMAGE, "still" }, |
112 | {USB_CLASS_PRINTER, "print" }, |
113 | {USB_CLASS_MASS_STORAGE, "stor." }, |
114 | {USB_CLASS_HUB, "hub" }, |
115 | {USB_CLASS_CDC_DATA, "data" }, |
116 | {USB_CLASS_CSCID, "scard" }, |
117 | {USB_CLASS_CONTENT_SEC, "c-sec" }, |
118 | {USB_CLASS_VIDEO, "video" }, |
119 | {USB_CLASS_PERSONAL_HEALTHCARE, "perhc" }, |
120 | {USB_CLASS_AUDIO_VIDEO, "av" }, |
121 | {USB_CLASS_BILLBOARD, "blbrd" }, |
122 | {USB_CLASS_USB_TYPE_C_BRIDGE, "bridg" }, |
123 | {USB_CLASS_WIRELESS_CONTROLLER, "wlcon" }, |
124 | {USB_CLASS_MISC, "misc" }, |
125 | {USB_CLASS_APP_SPEC, "app." }, |
126 | {USB_CLASS_VENDOR_SPEC, "vend." }, |
127 | {-1, "unk." } /* leave as last */ |
128 | }; |
129 | |
130 | /*****************************************************************/ |
131 | |
132 | static const char *class_decode(const int class) |
133 | { |
134 | int ix; |
135 | |
136 | for (ix = 0; clas_info[ix].class != -1; ix++) |
137 | if (clas_info[ix].class == class) |
138 | break; |
139 | return clas_info[ix].class_name; |
140 | } |
141 | |
142 | static char *usb_dump_endpoint_descriptor(int speed, char *start, char *end, |
143 | const struct usb_endpoint_descriptor *desc) |
144 | { |
145 | char dir, unit, *type; |
146 | unsigned interval, bandwidth = 1; |
147 | |
148 | if (start > end) |
149 | return start; |
150 | |
151 | dir = usb_endpoint_dir_in(epd: desc) ? 'I' : 'O'; |
152 | |
153 | if (speed == USB_SPEED_HIGH) |
154 | bandwidth = usb_endpoint_maxp_mult(epd: desc); |
155 | |
156 | /* this isn't checking for illegal values */ |
157 | switch (usb_endpoint_type(epd: desc)) { |
158 | case USB_ENDPOINT_XFER_CONTROL: |
159 | type = "Ctrl" ; |
160 | dir = 'B'; /* ctrl is bidirectional */ |
161 | break; |
162 | case USB_ENDPOINT_XFER_ISOC: |
163 | type = "Isoc" ; |
164 | break; |
165 | case USB_ENDPOINT_XFER_BULK: |
166 | type = "Bulk" ; |
167 | break; |
168 | case USB_ENDPOINT_XFER_INT: |
169 | type = "Int." ; |
170 | break; |
171 | default: /* "can't happen" */ |
172 | return start; |
173 | } |
174 | |
175 | interval = usb_decode_interval(epd: desc, speed); |
176 | if (interval % 1000) { |
177 | unit = 'u'; |
178 | } else { |
179 | unit = 'm'; |
180 | interval /= 1000; |
181 | } |
182 | |
183 | start += sprintf(buf: start, fmt: format_endpt, desc->bEndpointAddress, dir, |
184 | desc->bmAttributes, type, |
185 | usb_endpoint_maxp(epd: desc) * |
186 | bandwidth, |
187 | interval, unit); |
188 | return start; |
189 | } |
190 | |
191 | static char *usb_dump_interface_descriptor(char *start, char *end, |
192 | const struct usb_interface_cache *intfc, |
193 | const struct usb_interface *iface, |
194 | int setno) |
195 | { |
196 | const struct usb_interface_descriptor *desc; |
197 | const char *driver_name = "" ; |
198 | int active = 0; |
199 | |
200 | if (start > end) |
201 | return start; |
202 | desc = &intfc->altsetting[setno].desc; |
203 | if (iface) { |
204 | driver_name = (iface->dev.driver |
205 | ? iface->dev.driver->name |
206 | : "(none)" ); |
207 | active = (desc == &iface->cur_altsetting->desc); |
208 | } |
209 | start += sprintf(buf: start, fmt: format_iface, |
210 | active ? '*' : ' ', /* mark active altsetting */ |
211 | desc->bInterfaceNumber, |
212 | desc->bAlternateSetting, |
213 | desc->bNumEndpoints, |
214 | desc->bInterfaceClass, |
215 | class_decode(class: desc->bInterfaceClass), |
216 | desc->bInterfaceSubClass, |
217 | desc->bInterfaceProtocol, |
218 | driver_name); |
219 | return start; |
220 | } |
221 | |
222 | static char *usb_dump_interface(int speed, char *start, char *end, |
223 | const struct usb_interface_cache *intfc, |
224 | const struct usb_interface *iface, int setno) |
225 | { |
226 | const struct usb_host_interface *desc = &intfc->altsetting[setno]; |
227 | int i; |
228 | |
229 | start = usb_dump_interface_descriptor(start, end, intfc, iface, setno); |
230 | for (i = 0; i < desc->desc.bNumEndpoints; i++) { |
231 | start = usb_dump_endpoint_descriptor(speed, |
232 | start, end, desc: &desc->endpoint[i].desc); |
233 | } |
234 | return start; |
235 | } |
236 | |
237 | static char *usb_dump_iad_descriptor(char *start, char *end, |
238 | const struct usb_interface_assoc_descriptor *iad) |
239 | { |
240 | if (start > end) |
241 | return start; |
242 | start += sprintf(buf: start, fmt: format_iad, |
243 | iad->bFirstInterface, |
244 | iad->bInterfaceCount, |
245 | iad->bFunctionClass, |
246 | class_decode(class: iad->bFunctionClass), |
247 | iad->bFunctionSubClass, |
248 | iad->bFunctionProtocol); |
249 | return start; |
250 | } |
251 | |
252 | /* TBD: |
253 | * 0. TBDs |
254 | * 1. marking active interface altsettings (code lists all, but should mark |
255 | * which ones are active, if any) |
256 | */ |
257 | static char *usb_dump_config_descriptor(char *start, char *end, |
258 | const struct usb_config_descriptor *desc, |
259 | int active, int speed) |
260 | { |
261 | int mul; |
262 | |
263 | if (start > end) |
264 | return start; |
265 | if (speed >= USB_SPEED_SUPER) |
266 | mul = 8; |
267 | else |
268 | mul = 2; |
269 | start += sprintf(buf: start, fmt: format_config, |
270 | /* mark active/actual/current cfg. */ |
271 | active ? '*' : ' ', |
272 | desc->bNumInterfaces, |
273 | desc->bConfigurationValue, |
274 | desc->bmAttributes, |
275 | desc->bMaxPower * mul); |
276 | return start; |
277 | } |
278 | |
279 | static char *usb_dump_config(int speed, char *start, char *end, |
280 | const struct usb_host_config *config, int active) |
281 | { |
282 | int i, j; |
283 | struct usb_interface_cache *intfc; |
284 | struct usb_interface *interface; |
285 | |
286 | if (start > end) |
287 | return start; |
288 | if (!config) |
289 | /* getting these some in 2.3.7; none in 2.3.6 */ |
290 | return start + sprintf(buf: start, fmt: "(null Cfg. desc.)\n" ); |
291 | start = usb_dump_config_descriptor(start, end, desc: &config->desc, active, |
292 | speed); |
293 | for (i = 0; i < USB_MAXIADS; i++) { |
294 | if (config->intf_assoc[i] == NULL) |
295 | break; |
296 | start = usb_dump_iad_descriptor(start, end, |
297 | iad: config->intf_assoc[i]); |
298 | } |
299 | for (i = 0; i < config->desc.bNumInterfaces; i++) { |
300 | intfc = config->intf_cache[i]; |
301 | interface = config->interface[i]; |
302 | for (j = 0; j < intfc->num_altsetting; j++) { |
303 | start = usb_dump_interface(speed, |
304 | start, end, intfc, iface: interface, setno: j); |
305 | } |
306 | } |
307 | return start; |
308 | } |
309 | |
310 | /* |
311 | * Dump the different USB descriptors. |
312 | */ |
313 | static char *usb_dump_device_descriptor(char *start, char *end, |
314 | const struct usb_device_descriptor *desc) |
315 | { |
316 | u16 bcdUSB = le16_to_cpu(desc->bcdUSB); |
317 | u16 bcdDevice = le16_to_cpu(desc->bcdDevice); |
318 | |
319 | if (start > end) |
320 | return start; |
321 | start += sprintf(buf: start, fmt: format_device1, |
322 | bcdUSB >> 8, bcdUSB & 0xff, |
323 | desc->bDeviceClass, |
324 | class_decode(class: desc->bDeviceClass), |
325 | desc->bDeviceSubClass, |
326 | desc->bDeviceProtocol, |
327 | desc->bMaxPacketSize0, |
328 | desc->bNumConfigurations); |
329 | if (start > end) |
330 | return start; |
331 | start += sprintf(buf: start, fmt: format_device2, |
332 | le16_to_cpu(desc->idVendor), |
333 | le16_to_cpu(desc->idProduct), |
334 | bcdDevice >> 8, bcdDevice & 0xff); |
335 | return start; |
336 | } |
337 | |
338 | /* |
339 | * Dump the different strings that this device holds. |
340 | */ |
341 | static char *usb_dump_device_strings(char *start, char *end, |
342 | struct usb_device *dev) |
343 | { |
344 | if (start > end) |
345 | return start; |
346 | if (dev->manufacturer) |
347 | start += sprintf(buf: start, fmt: format_string_manufacturer, |
348 | dev->manufacturer); |
349 | if (start > end) |
350 | goto out; |
351 | if (dev->product) |
352 | start += sprintf(buf: start, fmt: format_string_product, dev->product); |
353 | if (start > end) |
354 | goto out; |
355 | #ifdef ALLOW_SERIAL_NUMBER |
356 | if (dev->serial) |
357 | start += sprintf(buf: start, fmt: format_string_serialnumber, |
358 | dev->serial); |
359 | #endif |
360 | out: |
361 | return start; |
362 | } |
363 | |
364 | static char *usb_dump_desc(char *start, char *end, struct usb_device *dev) |
365 | { |
366 | int i; |
367 | |
368 | start = usb_dump_device_descriptor(start, end, desc: &dev->descriptor); |
369 | |
370 | start = usb_dump_device_strings(start, end, dev); |
371 | |
372 | for (i = 0; i < dev->descriptor.bNumConfigurations; i++) { |
373 | start = usb_dump_config(speed: dev->speed, |
374 | start, end, config: dev->config + i, |
375 | /* active ? */ |
376 | active: (dev->config + i) == dev->actconfig); |
377 | } |
378 | return start; |
379 | } |
380 | |
381 | /*****************************************************************/ |
382 | |
383 | /* This is a recursive function. Parameters: |
384 | * buffer - the user-space buffer to write data into |
385 | * nbytes - the maximum number of bytes to write |
386 | * skip_bytes - the number of bytes to skip before writing anything |
387 | * file_offset - the offset into the devices file on completion |
388 | * The caller must own the device lock. |
389 | */ |
390 | static ssize_t usb_device_dump(char __user **buffer, size_t *nbytes, |
391 | loff_t *skip_bytes, loff_t *file_offset, |
392 | struct usb_device *usbdev, struct usb_bus *bus, |
393 | int level, int index, int count) |
394 | { |
395 | int chix; |
396 | int ret, cnt = 0; |
397 | int parent_devnum = 0; |
398 | char *pages_start, *data_end, *speed; |
399 | unsigned int length; |
400 | ssize_t total_written = 0; |
401 | struct usb_device *childdev = NULL; |
402 | |
403 | /* don't bother with anything else if we're not writing any data */ |
404 | if (*nbytes <= 0) |
405 | return 0; |
406 | |
407 | if (level > MAX_TOPO_LEVEL) |
408 | return 0; |
409 | /* allocate 2^1 pages = 8K (on i386); |
410 | * should be more than enough for one device */ |
411 | pages_start = (char *)__get_free_pages(GFP_NOIO, order: 1); |
412 | if (!pages_start) |
413 | return -ENOMEM; |
414 | |
415 | if (usbdev->parent && usbdev->parent->devnum != -1) |
416 | parent_devnum = usbdev->parent->devnum; |
417 | /* |
418 | * So the root hub's parent is 0 and any device that is |
419 | * plugged into the root hub has a parent of 0. |
420 | */ |
421 | switch (usbdev->speed) { |
422 | case USB_SPEED_LOW: |
423 | speed = "1.5" ; break; |
424 | case USB_SPEED_UNKNOWN: /* usb 1.1 root hub code */ |
425 | case USB_SPEED_FULL: |
426 | speed = "12" ; break; |
427 | case USB_SPEED_HIGH: |
428 | speed = "480" ; break; |
429 | case USB_SPEED_SUPER: |
430 | speed = "5000" ; break; |
431 | case USB_SPEED_SUPER_PLUS: |
432 | speed = "10000" ; break; |
433 | default: |
434 | speed = "??" ; |
435 | } |
436 | data_end = pages_start + sprintf(buf: pages_start, fmt: format_topo, |
437 | bus->busnum, level, parent_devnum, |
438 | index, count, usbdev->devnum, |
439 | speed, usbdev->maxchild); |
440 | /* |
441 | * level = topology-tier level; |
442 | * parent_devnum = parent device number; |
443 | * index = parent's connector number; |
444 | * count = device count at this level |
445 | */ |
446 | /* If this is the root hub, display the bandwidth information */ |
447 | if (level == 0) { |
448 | int max; |
449 | |
450 | /* super/high speed reserves 80%, full/low reserves 90% */ |
451 | if (usbdev->speed == USB_SPEED_HIGH || |
452 | usbdev->speed >= USB_SPEED_SUPER) |
453 | max = 800; |
454 | else |
455 | max = FRAME_TIME_MAX_USECS_ALLOC; |
456 | |
457 | /* report "average" periodic allocation over a microsecond. |
458 | * the schedules are actually bursty, HCDs need to deal with |
459 | * that and just compute/report this average. |
460 | */ |
461 | data_end += sprintf(buf: data_end, fmt: format_bandwidth, |
462 | bus->bandwidth_allocated, max, |
463 | (100 * bus->bandwidth_allocated + max / 2) |
464 | / max, |
465 | bus->bandwidth_int_reqs, |
466 | bus->bandwidth_isoc_reqs); |
467 | |
468 | } |
469 | data_end = usb_dump_desc(start: data_end, end: pages_start + (2 * PAGE_SIZE) - 256, |
470 | dev: usbdev); |
471 | |
472 | if (data_end > (pages_start + (2 * PAGE_SIZE) - 256)) |
473 | data_end += sprintf(buf: data_end, fmt: "(truncated)\n" ); |
474 | |
475 | length = data_end - pages_start; |
476 | /* if we can start copying some data to the user */ |
477 | if (length > *skip_bytes) { |
478 | length -= *skip_bytes; |
479 | if (length > *nbytes) |
480 | length = *nbytes; |
481 | if (copy_to_user(to: *buffer, from: pages_start + *skip_bytes, n: length)) { |
482 | free_pages(addr: (unsigned long)pages_start, order: 1); |
483 | return -EFAULT; |
484 | } |
485 | *nbytes -= length; |
486 | *file_offset += length; |
487 | total_written += length; |
488 | *buffer += length; |
489 | *skip_bytes = 0; |
490 | } else |
491 | *skip_bytes -= length; |
492 | |
493 | free_pages(addr: (unsigned long)pages_start, order: 1); |
494 | |
495 | /* Now look at all of this device's children. */ |
496 | usb_hub_for_each_child(usbdev, chix, childdev) { |
497 | usb_lock_device(childdev); |
498 | ret = usb_device_dump(buffer, nbytes, skip_bytes, |
499 | file_offset, usbdev: childdev, bus, |
500 | level: level + 1, index: chix - 1, count: ++cnt); |
501 | usb_unlock_device(childdev); |
502 | if (ret == -EFAULT) |
503 | return total_written; |
504 | total_written += ret; |
505 | } |
506 | return total_written; |
507 | } |
508 | |
509 | static ssize_t usb_device_read(struct file *file, char __user *buf, |
510 | size_t nbytes, loff_t *ppos) |
511 | { |
512 | struct usb_bus *bus; |
513 | ssize_t ret, total_written = 0; |
514 | loff_t skip_bytes = *ppos; |
515 | int id; |
516 | |
517 | if (*ppos < 0) |
518 | return -EINVAL; |
519 | if (nbytes <= 0) |
520 | return 0; |
521 | |
522 | mutex_lock(&usb_bus_idr_lock); |
523 | /* print devices for all busses */ |
524 | idr_for_each_entry(&usb_bus_idr, bus, id) { |
525 | /* recurse through all children of the root hub */ |
526 | if (!bus_to_hcd(bus)->rh_registered) |
527 | continue; |
528 | usb_lock_device(bus->root_hub); |
529 | ret = usb_device_dump(buffer: &buf, nbytes: &nbytes, skip_bytes: &skip_bytes, file_offset: ppos, |
530 | usbdev: bus->root_hub, bus, level: 0, index: 0, count: 0); |
531 | usb_unlock_device(bus->root_hub); |
532 | if (ret < 0) { |
533 | mutex_unlock(lock: &usb_bus_idr_lock); |
534 | return ret; |
535 | } |
536 | total_written += ret; |
537 | } |
538 | mutex_unlock(lock: &usb_bus_idr_lock); |
539 | return total_written; |
540 | } |
541 | |
542 | const struct file_operations usbfs_devices_fops = { |
543 | .llseek = no_seek_end_llseek, |
544 | .read = usb_device_read, |
545 | }; |
546 | |