| #!/bin/sh |
| # SPDX-License-Identifier: GPL-2.0-or-later |
| # |
| # Copyright (c) 2009 Greg Kroah-Hartman <greg@kroah.com> |
| # Copyright (c) 2009 Randy Dunlap <rdunlap@xenotime.net> |
| # Copyright (c) 2009 Frans Pop <elendil@planet.nl> |
| # |
| # When checking with shellcheck ignore |
| # "In POSIX sh, local is undefined." |
| # every modern shell supports it. |
| # shellcheck disable=SC3043 |
| |
| print_string() { |
| file=$1 |
| name=$2 |
| if [ -f "$file" ]; then |
| echo "S: $name=$(cat "$file")" |
| fi |
| } |
| |
| class_decode() { |
| class=$1 # v4: in hex |
| |
| case $class in |
| "00") echo ">ifc " ;; |
| "01") echo "audio" ;; |
| "02") echo "commc" ;; |
| "03") echo "HID " ;; |
| "05") echo "PID " ;; |
| "06") echo "still" ;; |
| "07") echo "print" ;; |
| "08") echo "stor." ;; |
| "09") echo "hub " ;; |
| "0a") echo "data " ;; |
| "0b") echo "scard" ;; |
| "0d") echo "c-sec" ;; |
| "0e") echo "video" ;; |
| "0f") echo "perhc" ;; |
| "10") echo "av " ;; |
| "11") echo "blbrd" ;; |
| "12") echo "bridg" ;; |
| "dc") echo "diagd" ;; |
| "e0") echo "wlcon" ;; |
| "ef") echo "misc " ;; |
| "fe") echo "app. " ;; |
| "ff") echo "vend." ;; |
| "*") echo "unk. " ;; |
| esac |
| } |
| |
| print_endpoint() { |
| eppath=$1 |
| |
| read -r addr < "$eppath/bEndpointAddress" |
| read -r attr < "$eppath/bmAttributes" |
| read -r dir < "$eppath/direction" |
| |
| if [ "$dir" = "in" ]; then |
| dir="I" |
| elif [ "$dir" = "out" ]; then |
| dir="O" |
| elif [ "$dir" = "both" ]; then |
| dir="B" |
| fi |
| |
| read -r eptype < "$eppath/type" |
| if [ "$eptype" = Control ]; then |
| eptype="Ctrl" |
| elif [ "$eptype" = Interrupt ]; then |
| eptype="Int." |
| fi |
| |
| read -r maxps_hex < "$eppath/wMaxPacketSize" |
| maxps_hex="0x$maxps_hex" |
| # Extract MaxPS size (bits 0-10) and multiplicity values (bits 11-12) |
| |
| maxps=$(($(printf "%4i*%s\n" $((maxps_hex & 0x7ff)) \ |
| $((1 + ($((maxps_hex >> 11)) & 0x3)))))) |
| |
| read -r interval < "$eppath/interval" |
| |
| printf "E: Ad=%s(%s) Atr=%s(%s) MxPS=%4s Ivl=%s\n" \ |
| "$addr" "$dir" "$attr" "$eptype" "$maxps" "$interval" |
| } |
| |
| print_interface() { |
| ifpath=$1 |
| |
| read -r ifnum < "$ifpath/bInterfaceNumber" |
| read -r altset < "$ifpath/bAlternateSetting" |
| read -r numeps < "$ifpath/bNumEndpoints" |
| read -r class < "$ifpath/bInterfaceClass" |
| read -r subclass < "$ifpath/bInterfaceSubClass" |
| read -r protocol < "$ifpath/bInterfaceProtocol" |
| |
| if [ -L "$ifpath/driver" ]; then # v4: allow for no driver |
| driver=$(readlink "$ifpath/driver") |
| driver=${driver##*/} |
| else |
| driver="(none)" |
| fi |
| classname=$(class_decode "$class") |
| printf "I: If#=%2i Alt=%2s #EPs=%2i Cls=%s(%s) Sub=%s Prot=%s Driver=%s\n" \ |
| "0x${ifnum#0}" "${altset}" "0x${numeps#0}" "$class" "$classname" "$subclass" \ |
| "$protocol" "$driver" |
| |
| for endpoint in "$ifpath"/ep_?? |
| do |
| if [ -L "$endpoint" ] || [ -d "$endpoint" ]; then # v4: verify endpoint exists |
| print_endpoint "$endpoint" |
| fi |
| done |
| } |
| |
| print_device() { |
| local devpath="$1" |
| local parent="$2" |
| local level="$3" |
| local count="$4" |
| local devnum |
| |
| [ -d "$devpath" ] || return |
| cd "$devpath" || return |
| |
| read -r busnum < "busnum" |
| read -r devnum < "devnum" |
| |
| if [ "$level" -gt 0 ]; then |
| port=$((${devpath##*[-.]} - 1)) |
| else |
| port=0 |
| fi |
| |
| read -r speed < "speed" |
| read -r maxchild < "maxchild" |
| printf "\nT: Bus=%02i Lev=%02i Prnt=%02i Port=%02i Cnt=%02i Dev#=%3i Spd=%-4s MxCh=%2i\n" \ |
| "$busnum" "$level" "$parent" "$port" "$count" "$devnum" "$speed" "$maxchild" |
| |
| read -r ver < "version" |
| read -r devclass < "bDeviceClass" |
| read -r devsubclass < "bDeviceSubClass" |
| read -r devprotocol < "bDeviceProtocol" |
| read -r maxps0 < "bMaxPacketSize0" |
| read -r numconfigs < "bNumConfigurations" |
| classname="$(class_decode "$devclass")" |
| printf "D: Ver=%5s Cls=%s(%s) Sub=%s Prot=%s MxPS=%2i #Cfgs=%3i\n" \ |
| "$ver" "$devclass" "$classname" "$devsubclass" "$devprotocol" \ |
| "$maxps0" "$numconfigs" |
| |
| read -r vendid < "idVendor" |
| read -r prodid < "idProduct" |
| read -r bcddevice < "bcdDevice" |
| revmajor="${bcddevice%%??}" |
| revminor="${bcddevice##??}" |
| printf "P: Vendor=%s ProdID=%s Rev=%s.%s\n" \ |
| "$vendid" "$prodid" "$revmajor" "$revminor" |
| |
| print_string manufacturer "Manufacturer" |
| print_string product Product |
| print_string serial SerialNumber |
| |
| read -r numifs < "bNumInterfaces" |
| read -r cfgnum < "bConfigurationValue" |
| read -r attr < "bmAttributes" |
| read -r maxpower < "bMaxPower" |
| printf "C: #Ifs=%2i Cfg#=%2i Atr=%s MxPwr=%s\n" \ |
| "$numifs" "$cfgnum" "$attr" "$maxpower" |
| |
| # There's not really any useful info in endpoint 00 |
| #print_endpoint $devpath/ep_00 |
| |
| for interface in "$busnum"-*:?.* |
| do |
| print_interface "$devpath/$interface" |
| done |
| |
| local devcount=0 |
| for subdev in "$busnum"-* |
| do |
| echo "$subdev" | grep -Eq "^$busnum-[0-9]+(\.[0-9]+)*$" \ |
| || continue |
| |
| devcount=$((devcount + 1)) |
| if [ -d "$devpath/$subdev" ]; then |
| print_device "$devpath/$subdev" \ |
| "$devnum" "$((level +1))" "$devcount" |
| fi |
| done |
| } |
| |
| if [ ! -d /sys/bus ]; then |
| echo "Error: directory /sys/bus does not exist; is sysfs mounted?" >&2 |
| exit 1 |
| fi |
| |
| for device in $(find /sys/bus/usb/devices -name 'usb*' | sed -E 's#^.*/##g' | sort -V) |
| do |
| print_device "/sys/bus/usb/devices/$device" 0 0 0 |
| done |