/ util / find_usbdebug / find_usbdebug.sh
find_usbdebug.sh
  1  #!/usr/bin/env bash
  2  
  3  # SPDX-License-Identifier: GPL-2.0-only
  4  
  5  LANG=C
  6  # Some tools emit errors that don't matter (bugs in lspci/PCI firmware and lsusb).
  7  # To shown them anyway (e.g. for debugging) comment next line.
  8  exec 2>/dev/null
  9  
 10  if [ "$1" = "-h" ]; then
 11  	printf "Usage: $0 [-h | path to dmesg log]
 12  
 13  This script tries to find USB ports compatible with USB2/EHCI debug devices and
 14  helps you to find their physical locations. To that end, attach at least one
 15  uniquely identifiable device to a USB port and run this script. The device needs
 16  to be visible in the output of \"lsusb -t\" (debug devices are often *not*!).
 17  
 18  After determining compatibility of the USB controllers the script will print the
 19  devices attached to the debug port as shown by lsusb. If nothing shows up simply
 20  switch ports and repeat the process.
 21  
 22  Note: usually only one port is supported for debugging.\n"
 23  	exit 0
 24  fi
 25  uid=`id -u`
 26  if [ "$uid" -ne 0 ]; then
 27  	echo "Must be run as root. Exiting."
 28  	exit 1
 29  fi
 30  
 31  if ! command -v lsusb; then
 32  	echo "lsusb not found. Please install \"usbutils\" from your
 33  distribution's package manager. Exiting."
 34  	exit 1
 35  fi
 36  
 37  if ! command -v lspci; then
 38  	echo "lspci not found. Please install \"pciutils\" from your
 39  distribution's package manager. Exiting."
 40  	exit 1
 41  fi
 42  
 43  dmesgfile=$1
 44  
 45  find_devs_in_tree () {
 46  	bus=$1
 47  	port=$2
 48  
 49  	# lsusb -t uses 3 digits for bus/port nunmbers as of version 016 and later
 50  	if [ $(lsusb -V | cut -f 3 -d " ") -lt 16 ]; then
 51  		busstr=`printf "Bus %02d" "$bus"`
 52  		portstr="Port $port"
 53  	else
 54  		busstr=`printf "Bus %03d" "$bus"`
 55  		portstr=`printf "Port %03d" "$port"`
 56  	fi
 57  
 58  	hubs_to_ignore="8087:0020 8087:0024 8087:8000 8087:8008"
 59  	reqlvl=1
 60  
 61  	found=
 62  	# Iterate over the output of lsusb -t because it contains the physical port numbers
 63  	while IFS='' read -r line; do
 64  		# We need to keep track of the current bus "branch"
 65  		# Look out for lines starting with /: (that indicate a bus)
 66  		if [ "${line#*/:}" != "$line" ]; then
 67  			if [ "${line#*$busstr}" != "$line" ]; then
 68  				cur_bus=$busstr
 69  			else
 70  				cur_bus=
 71  			fi
 72  			continue
 73  		fi
 74  
 75  		# Skip all lines not belonging to the wanted bus number
 76  		if [ "$cur_bus" != "$busstr" ]; then
 77  			continue
 78  		fi
 79  
 80  		# Calculate current USB tier/level
 81  		spaces="${line%%[!' ']*}"
 82  		curlvl=$((${#spaces} / 4))
 83  		if [ $curlvl -ne $reqlvl ]; then
 84  			continue
 85  		fi
 86  
 87  		# Fetch USB IDs of the current device
 88  		dev=`echo ${line#*Dev } | cut -d ',' -f 1`
 89  		lsusbline=`lsusb -s "$bus":"$dev"`
 90  		if [[ ! "$lsusbline" =~ .*([[:xdigit:]]{4}:[[:xdigit:]]{4}) ]]; then
 91  			printf "Unexpected output from \"%s\": \"%s\"\n" "lsusb -s $bus:$dev" "$usbline"
 92  			exit 1
 93  		fi
 94  		ids=${BASH_REMATCH[1]}
 95  
 96  		# Skip over rate matching hubs
 97  		if [[ "$hubs_to_ignore" == *"$ids"* ]]; then
 98  			((reqlvl += 1))
 99  			continue
100  		fi
101  
102  		# Check for matching physical USB port
103  		if [ "${line#*$portstr}" != "$line" ]; then
104  			echo "$lsusbline"
105  			return
106  		fi
107  	done<< EOF
108  $(lsusb -t)
109  EOF
110  	if [ -z "$found" ]; then
111  		echo "none"
112  	fi
113  }
114  
115  debug_lspci_devs=`lspci -nvvD |
116  	grep -i "^[0-9a-f]\|debug port" |
117  	grep -iB1 --no-group-separator "debug port" |
118  	grep -vi "debug port" |
119  	cut -f 1 -d" " |
120  	sort |
121  	xargs echo`
122  
123  if [ -z "$debug_lspci_devs" ]; then
124  	printf "No USB controller with debug capability found by lspci.\n
125  Possible reasons: lspci too old, USB controller does not support a debug device, ... Exiting.\n"
126  	exit 1
127  fi
128  printf "The following PCI devices support a USB debug port (says lspci): $debug_lspci_devs\n"
129  
130  debug_dmesg_devs_with_port=`( test -z "$dmesgfile" &&
131  	dmesg ||
132  	cat "$dmesgfile") |
133  	grep -i "ehci.*debug port" |
134  	sed "s/.* \([0-9a-f]*:*[0-9a-f]\{2\}:[0-9a-f]\{2\}\.[0-9a-f]\).*ebug port /\1 /" |
135  	sort`
136  
137  debug_dmesg_devs=`echo "$debug_dmesg_devs_with_port" |
138  	cut -f 1 -d" " |
139  	xargs echo`
140  
141  if [ -z "$debug_dmesg_devs" ]; then
142  	printf "dmesg does not show any supported ports.\n
143  Possible reasons: dmesg scrolled off, kernel too old, USB controller does not support a debug device, ... Exiting.\n
144  Note: You can specify a file containing kernel messages as an argument to this program (e.g. /var/log/dmesg)."
145  	exit 1
146  fi
147  
148  if [ "$debug_lspci_devs" != "$debug_dmesg_devs" ]; then
149  	echo "lspci and the kernel do not agree on USB debug device support. Exiting."
150  	exit 1
151  fi
152  
153  printf "and the kernel agrees, good.\n\n"
154  
155  while true; do
156  	for dev in $debug_dmesg_devs; do
157  		bus=`lsusb -v |
158  			grep "^Bus\|iSerial.*" |
159  			grep -B1 --no-group-separator "iSerial.*$dev" |
160  			grep "^Bus" |
161  			sed "s/Bus *0*\([0-9a-f]*\).*/\1/"`
162  		port=`echo "$debug_dmesg_devs_with_port" |
163  			grep "^$dev" |
164  			cut -f 2 -d" "`
165  
166  		echo "Device(s) currently connected to the debug-capable port $port on PCI device $dev, USB bus $bus:"
167  
168  		find_devs_in_tree "$bus" "$port"
169  		echo
170  	done
171  
172  	echo "Enter 'q' to abort or anything else to repeat"
173  	read -r r
174  	if [ $? -ne 0 -o "$r" = "q" ]; then
175  		break;
176  	fi
177  done
178  
179  exit 0