#!/bin/bash

# skeletal version from GregKH
# lots of updates by Randy Dunlap <rdunlap@xenotime.net>
#
# usage:
# usb-devices.sh [-x]
# -x => debug

# TBD list:
# - add indicator for first line of a device
# - put '*' on active config & interface

print_string() {
	file=$1
	name=$2
	if [ -f $file ]
	then
		echo "S:  $name=`cat $file`"
	fi
}

class_decode() {
	local class=$1		# v4: in hex

	case $class in
	"00") echo ">ifc " ;;
	"01") echo "audio" ;;
	"02") echo "commc" ;;
	"03") echo "HID  " ;;
	"05") echo "phys " ;;
	"06") echo "image" ;;
	"07") echo "print" ;;
	"08") echo "mstor" ;;
	"09") echo "hub  " ;;
	"0a") echo "comdt" ;;
	"0b") echo "smcrd" ;;
	"0d") echo "cosec" ;;
	"0e") echo "video" ;;
	"0f") echo "perhc" ;;
	"dc") echo "diagd" ;;
	"e0") echo "wlcon" ;;
	"ef") echo "misc " ;;
	"fe") echo "apspe" ;;
	"ff") echo "vendr" ;;
	"*") echo "unk  " ;;
	esac
}

print_endpoint() {
	endpoint=$1
	if [ "$debug" = "1" ]; then
		echo "endpoint=$endpoint"
	fi

	addr=`cat $endpoint/bEndpointAddress`
	attr=`cat $endpoint/bmAttributes`
	dir=`cat $endpoint/direction`
	eptype=`cat $endpoint/type`
	maxps=`cat $endpoint/wMaxPacketSize`
	interval=`cat $endpoint/interval`

	printf "E:  Ad=%s(%s) Atr=%s(%s) MxPS=%s Ivl=%s\n" \
		$addr $dir $attr $eptype $maxps $interval
}

print_interface() {
	interface=$1
	local cwd

	cwd=`/bin/pwd`
	cd $interface
	if [ "$debug" = "1" ]; then
		echo "cd to interface=$interface"
		echo -n "pwd="
		/bin/pwd
	fi

	ifnum=`cat bInterfaceNumber`
	altset=`cat bAlternateSetting`
	numeps=`cat bNumEndpoints`
	class=`cat bInterfaceClass`
	subclass=`cat bInterfaceSubClass`
	protocol=`cat bInterfaceProtocol`
	if [ -L driver ]; then		# v4: allow for no driver
		driver=`readlink driver`
		driver=`basename $driver`
	else
		driver="(none)"
	fi
	classname=`class_decode $class`
	printf "I:  If#=%s Alt=%s #EPs=%s Cls=%s(%s) Sub=%s Prot=%s Driver=%s\n" \
		$ifnum $altset $numeps $class $classname $subclass $protocol $driver

	##for endpoint in $interface/ep_??
	for endpoint in ep_??
	do
		if [ -L $endpoint ]; then	# v4: verify endpoint exists
			print_endpoint $endpoint
		fi
	done

	if [ "$debug" = "1" ]; then
		echo "cd back to cwd=$cwd"
	fi
	cd $cwd
}

print_device() {
	device=$1
	local cwd

	cwd=`/bin/pwd`
	cd $device
	if [ "$debug" = "1" ]; then
		echo "cd to device=$device"
		echo -n "pwd="
		/bin/pwd
	fi

	busnum=`cat busnum`
	devnum=`cat devnum`
	speed=`cat speed`
	maxchild=`cat maxchild`
	printf "\nT:  Bus=%s Dev#=%s Spd=%s MxCh=%s\n" \
		$busnum $devnum $speed $maxchild

	ver=`cat version`
	devclass=`cat bDeviceClass`
	devsubclass=`cat bDeviceSubClass`
	devprotocol=`cat bDeviceProtocol`
	maxps0=`cat bMaxPacketSize0`
	numconfigs=`cat bNumConfigurations`
	classname=`class_decode $devclass`
	printf "D:  Ver=%s Cls=%s(%s) Sub=%s Prot=%s MxPS=%s #Cfgs=%s\n" \
		$ver $devclass $classname $devsubclass $devprotocol $maxps0 $numconfigs

	vendid=`cat idVendor`
	prodid=`cat idProduct`
	revmajor=`cat bcdDevice | cut -c 1-2`
	revminor=`cat bcdDevice | cut -c 3-4`
	leadmajor=`echo $revmajor | cut -c 1`
	leadminor=`echo $revminor | cut -c 1`
	if [ "$leadmajor" = "0" ]; then
		revmajor=`cat bcdDevice | cut -c 2`
	fi
	if [ "$leadminor" = "0" ]; then
		revminor=`cat bcdDevice | cut -c 4`
	fi
	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

	numifs=`cat bNumInterfaces`
	cfgnum=`cat bConfigurationValue`
	attr=`cat bmAttributes`
	maxpower=`cat bMaxPower`
	printf "C:  #Ifs=%s Cfg#=%s Atr=%s MxPwr=%s\n" \
		$numifs $cfgnum $attr $maxpower

	for endpoint in ep_??
	do
		print_endpoint $endpoint
	done

	for interface in *:?.*
	do
		print_interface $interface
	done

	busnum=`cat busnum`
	for subdev in $busnum-? $busnum-?? $busnum-???
	do
		if [ -d $subdev ]; then
			if [ "$debug" = "1" ]; then
				echo "try subdev=$subdev"
			fi
			print_device $subdev
		fi
	done

	if [ "$debug" = "1" ]; then
		echo "cd back to cwd=$cwd"
	fi
	cd $cwd
}

debug=
if [ "$1" == "-x" ]; then
	debug="1"
	shift
fi

##print_device /sys/bus/usb/devices/usb1
for dev in /sys/bus/usb/devices/usb*
do
	print_device $dev
done
