summaryrefslogtreecommitdiff
path: root/util/find_usbdebug/find_usbdebug.sh
blob: de370b1fe87bccdef3a1069ba4f20b9b232354fe (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
#!/usr/bin/env bash

# SPDX-License-Identifier: GPL-2.0-only

LANG=C
# Some tools emit errors that don't matter (bugs in lspci/PCI firmware and lsusb).
# To shown them anyway (e.g. for debugging) comment next line.
exec 2>/dev/null

if [ "$1" = "-h" ]; then
	printf "Usage: $0 [-h | path to dmesg log]

This script tries to find USB ports compatible with USB2/EHCI debug devices and
helps you to find their physical locations. To that end, attach at least one
uniquely identifiable device to a USB port and run this script. The device needs
to be visible in the output of \"lsusb -t\" (debug devices are often *not*!).

After determining compatibility of the USB controllers the script will print the
devices attached to the debug port as shown by lsusb. If nothing shows up simply
switch ports and repeat the process.

Note: usually only one port is supported for debugging.\n"
	exit 0
fi
uid=`id -u`
if [ "$uid" -ne 0 ]; then
	echo "Must be run as root. Exiting."
	exit 1
fi
dmesgfile=$1

find_devs_in_tree () {
	bus=$1
	port=$2
	busstr=`printf "Bus %02d" "$bus"`
	portstr="Port $port"

	hubs_to_ignore="8087:0020 8087:0024 8087:8000 8087:8008"
	reqlvl=1

	found=
	# Iterate over the output of lsusb -t because it contains the physical port numbers
	while IFS='' read -r line; do
		# We need to keep track of the current bus "branch"
		# Look out for lines starting with /: (that indicate a bus)
		if [ "${line#*/:}" != "$line" ]; then
			if [ "${line#*$busstr}" != "$line" ]; then
				cur_bus=$busstr
			else
				cur_bus=
			fi
			continue
		fi

		# Skip all lines not belonging to the wanted bus number
		if [ "$cur_bus" != "$busstr" ]; then
			continue
		fi

		# Calculate current USB tier/level
		spaces="${line%%[!' ']*}"
		curlvl=$((${#spaces} / 4))
		if [ $curlvl -ne $reqlvl ]; then
			continue
		fi

		# Fetch USB IDs of the current device
		dev=`echo ${line#*Dev } | cut -d ',' -f 1`
		lsusbline=`lsusb -s "$bus":"$dev"`
		if [[ ! "$lsusbline" =~ .*([[:xdigit:]]{4}:[[:xdigit:]]{4}) ]]; then
			printf "Unexpected output from \"%s\": \"%s\"\n" "lsusb -s $bus:$dev" "$usbline"
			exit 1
		fi
		ids=${BASH_REMATCH[1]}

		# Skip over rate matching hubs
		if [[ "$hubs_to_ignore" == *"$ids"* ]]; then
			((reqlvl += 1))
			continue
		fi

		# Check for matching physical USB port
		if [ "${line#*$portstr}" != "$line" ]; then
			echo "$lsusbline"
			return
		fi
	done<< EOF
$(lsusb -t)
EOF
	if [ -z "$found" ]; then
		echo "none"
	fi
}

debug_lspci_devs=`lspci -nvvD |
	grep -i "^[0-9a-f]\|debug port" |
	grep -iB1 --no-group-separator "debug port" |
	grep -vi "debug port" |
	cut -f 1 -d" " |
	sort |
	xargs echo`

if [ -z "$debug_lspci_devs" ]; then
	printf "No USB controller with debug capability found by lspci.\n
Possible reasons: lspci too old, USB controller does not support a debug device, ... Exiting.\n"
	exit 1
fi
printf "The following PCI devices support a USB debug port (says lspci): $debug_lspci_devs\n"

debug_dmesg_devs_with_port=`( test -z "$dmesgfile" &&
	dmesg ||
	cat "$dmesgfile") |
	grep -i "ehci.*debug port" |
	sed "s/.* \([0-9a-f]*:*[0-9a-f]\{2\}:[0-9a-f]\{2\}\.[0-9a-f]\).*ebug port /\1 /" |
	sort`

debug_dmesg_devs=`echo "$debug_dmesg_devs_with_port" |
	cut -f 1 -d" " |
	xargs echo`

if [ -z "$debug_dmesg_devs" ]; then
	printf "dmesg does not show any supported ports.\n
Possible reasons: dmesg scrolled off, kernel too old, USB controller does not support a debug device, ... Exiting.\n
Note: You can specify a file containing kernel messages as an argument to this program (e.g. /var/log/dmesg)."
	exit 1
fi

if [ "$debug_lspci_devs" != "$debug_dmesg_devs" ]; then
	echo "lspci and the kernel do not agree on USB debug device support. Exiting."
	exit 1
fi

printf "and the kernel agrees, good.\n\n"

while true; do
	for dev in $debug_dmesg_devs; do
		bus=`lsusb -v |
			grep "^Bus\|iSerial.*" |
			grep -B1 --no-group-separator "iSerial.*$dev" |
			grep "^Bus" |
			sed "s/Bus *0*\([0-9a-f]*\).*/\1/"`
		port=`echo "$debug_dmesg_devs_with_port" |
			grep "^$dev" |
			cut -f 2 -d" "`

		echo "Device(s) currently connected to the debug-capable port $port on PCI device $dev, USB bus $bus:"

		find_devs_in_tree "$bus" "$port"
		echo
	done

	echo "Enter 'q' to abort or anything else to repeat"
	read -r r
	if [ $? -ne 0 -o "$r" = "q" ]; then
		break;
	fi
done

exit 0