So usually the order in onboot.lst isn't significant.I can confirm that. Putting firmware-mediatek.tcz (for example) at very bottom of onboot.lst has no ill effect--after boot process is complete, firmware has been loaded and device is operable.
The udev trigger for new modules on boot is delayed until after all extensions are loaded.curaga, can you please help me see how this is the case? This is the sequence of events as best as I can tell: /init -> /sbin/init (Busybox init) -> /etc/inittab -> /etc/init.d/rcS -> /etc/init.d/tc-config
/usr/bin/tce-setup "booting"
tce-setup fires off udev if it detects /etc/sysconfig/newmodules.Do you know what creates /etc/sysconfig/newmodules?I found it: It's /usr/bin/tce-load:
...
[ -n "`find /mnt/test/ -type d -name modules`" ] && MODULES=TRUE
...
update_system() {
if [ "$BOOTING" ]; then
[ "$MODULES" ] && sudo /bin/touch /etc/sysconfig/newmodules
So if tce-load loads an extension that contains modules, it creates the newmodules flag. The new modules flag causes tce-setup to fire off udev after all extensions have been loaded. Mystery solved. Thank you both very much.Nice find, Rich. I almost have the complete picture now. ...Yeah, there's a lot going on there. It can be tricky trying to follow it all.
$ dmesg | grep firmware # the failed firmware load here is not a surprise
[ 0.071368] Spectre V2 : Enabling Restricted Speculation for firmware calls
[ 2.880738] psmouse serio2: trackpoint: IBM TrackPoint firmware: 0x0e, buttons: 3/3
[ 13.148438] platform regulatory.0: Direct firmware load for regulatory.db failed with error -2
[ 13.233707] usb 2-1.2: Direct firmware load for ath9k_htc/htc_9271-1.4.0.fw failed with error -2
[ 13.285943] usb 2-1.2: Direct firmware load for htc_9271.fw failed with error -2
[ 13.295216] usb 2-1.2: no suitable firmware found!
[ 13.295221] usb 2-1.2: ath9k_htc: Failed to get firmware htc_9271.fw
$ tce-load -i firmware-atheros
$ sudo depmod -a
$ sudo udevadm trigger
$ dmesg | grep firmware # why no new attempt at loading firmware?
[ 0.071368] Spectre V2 : Enabling Restricted Speculation for firmware calls
[ 2.880738] psmouse serio2: trackpoint: IBM TrackPoint firmware: 0x0e, buttons: 3/3
[ 13.148438] platform regulatory.0: Direct firmware load for regulatory.db failed with error -2
[ 13.233707] usb 2-1.2: Direct firmware load for ath9k_htc/htc_9271-1.4.0.fw failed with error -2
[ 13.285943] usb 2-1.2: Direct firmware load for htc_9271.fw failed with error -2
[ 13.295216] usb 2-1.2: no suitable firmware found!
[ 13.295221] usb 2-1.2: ath9k_htc: Failed to get firmware htc_9271.fw
Then I run ifconfig -a there is no wlan1 in the output (obviously, since firmware has not been loaded onto the device). I must manually unplug the adapter and plug it back in for it to work.The driver is what loads the firmware. The firmware must be loaded before (or during boot) the kernel device driver is loaded.I figured it's the driver that loads the firmware. But that doesn't explain everything either, because the following doesn't work :-\
$ tce-load -i firmware-atheros
firmware-atheros.tcz: OK
$ sudo modprobe -r ath9k_htc
$ sudo modprobe ath9k_htc
Just to clarify that all this is just an exercise in understanding. All my gear is working fine.
sudo modprobe -r ath9k_htc
tce-load -i firmware-atheros
sudo udevadm trigger
Hi GNUserHi Rich. Yes lsmod shows the driver still loaded. The above commands do not cause the firmware to get loaded.
Despite the firmware failure, does lsmod show that the
driver is still loaded? If it does, see if this works:Code: [Select]sudo modprobe -r ath9k_htc
tce-load -i firmware-atheros
sudo udevadm trigger
#!/bin/sh
Debug="test"
# Uncomment the next line to turn on debugging messages.
Debug="echo"
# Vendor ID and product ID (8644:8003).
VID="8644"
PID="8003"
# Find the modalias file that contains our VID and PID.
MODALIS="$(find /sys -name modalias | xargs grep "v""$VID""p""$PID" 2> /dev/null)"
if [ -n "$MODALIS" ]
then
"$Debug" "$MODALIS"
# Grab the path to modalias.
UEVENT="${MODALIS%/*}"
# Grab the device designation from the end of the path.
DEVICE="${UEVENT##*/}"
# Complete path to uevent file.
UEVENT="$UEVENT""/uevent"
"$Debug" "DEVICE=$DEVICE"
"$Debug" "UEVENT=$UEVENT"
fi
if [ -f "$UEVENT" ]
then
# Grab the driver name from uevent file.
DRIVER="$(grep "DRIVER" $UEVENT | cut -d '=' -f2)"
"$Debug" "DRIVER=$DRIVER"
fi
if [ -n "$DRIVER" ]
then
# Find the directory named for the driver.
BINDPATH="$(find /sys -type d -name "$DRIVER")"
# That directory should contain the unbind and bind files.
BINDFILE="$BINDPATH""/bind"
UNBINDFILE="$BINDPATH""/unbind"
"$Debug" "BINDPATH=$BINDPATH"
"$Debug" "BINDFILE=$BINDFILE"
"$Debug" "UNBINDFILE=$UNBINDFILE"
fi
if [ -e "$UNBINDFILE" ]
then
# Remove device.
echo "$DEVICE" | sudo tee "$UNBINDFILE" 1> /dev/null
fi
"$Debug" "Sleep ..."
sleep 3
"$Debug" "... Wake up"
if [ -e "$BINDFILE" ]
then
# Add device back.
echo "$DEVICE" | sudo tee "$BINDFILE" 1> /dev/null
fi
I don't have any USB Wifi devices, but I tested it with a thumb drive.bruno@x230:~$ lsusb
...
Bus 002 Device 003: ID 040d:3801 VIA Technologies, Inc.
...
MODALIS="$(find /sys -name modalias | xargs grep "v""$VID""p""$PID" 2> /dev/null)"
Because the two relevant files for my usb device are these (notice no "modalias" in the name).../sys/devices/pci0000:00/0000:00:1a.0/usb2/2-1/2-1.2/uevent
/sys/devices/pci0000:00/0000:00:1a.0/usb2/2-1/2-1.2/idProduct
$ sudo cat /sys/devices/pci0000:00/0000:00:1a.0/usb2/2-1/2-1.2/uevent
MAJOR=189
MINOR=130
DEVNAME=bus/usb/002/003
DEVTYPE=usb_device
DRIVER=usb
PRODUCT=40d/3801/108
TYPE=255/255/255
BUSNUM=002
DEVNUM=003
$ sudo cat /sys/devices/pci0000:00/0000:00:1a.0/usb2/2-1/2-1.2/idProduct
3801
Also, notice how VID is 040d but the uevent file contains 40d (missing leading zero). Why, oh why?#!/bin/sh
productId=3801 # get this from lsusb
productFile=$(find /sys -name 'idProduct' | xargs grep -l "$productId" 2>/dev/null)
device=$(basename $(dirname $productFile)) # e.g., device=2-1.2
sudo sh -c "echo $device >/sys/bus/usb/drivers/usb/unbind 2>/dev/null"
sudo sh -c "echo $device >/sys/bus/usb/drivers/usb/bind"
...and it's working great:$ ifconfig -a wlan1
ifconfig: wlan1: error fetching interface information: Device not found
$ tce-load -i firmware-atheros
firmware-atheros.tcz: OK
$ ./USBReset.sh # instead of manually detaching+reattaching the device
$ ifconfig -a wlan1
wlan1 Link encap:Ethernet HWaddr 00:12:7B:20:6B:A0
BROADCAST MULTICAST MTU:1500 Metric:1
RX packets:0 errors:0 dropped:0 overruns:0 frame:0
TX packets:0 errors:0 dropped:0 overruns:0 carrier:0
collisions:0 txqueuelen:1000
RX bytes:0 (0.0 B) TX bytes:0 (0.0 B)
Thanks for pointing me in the right direction with this!
... (notice no "modalias" in the name)...I guess not all devices have aliases assign to them.
... Also, notice how VID is 040d but the uevent file contains 40d (missing leading zero). Why, oh why? ...Now that you mention it, I recall running into that a couple of years ago.
... Anyway, inspired by your script, I created this variation...A couple of notes:
... and it's working great: ...Hey, no fair. That's what I was shooting for. ;D
#!/bin/sh
Debug="test"
# Uncomment the next line to turn on debugging messages.
Debug="echo"
# Vendor ID and product ID (8644:8003).
VID="8644"
PID="8003"
# Strips leading zeros from variable. If variable is all
# zeros, variable will be set to a single zero.
while [ ${#VID} -gt 1 ] # ${#VID} returns string length of variable.
do
# If first character of variable is not "0", end loop.
[ ${VID:0:1} != "0" ] && break
# Remove first character from variable.
VID="${VID:1}"
done
# Strips leading zeros from variable. If variable is all
# zeros, variable will be set to a single zero.
while [ ${#PID} -gt 1 ] # ${#PID} returns string length of variable.
do
# If first character of variable is not "0", end loop.
[ ${PID:0:1} != "0" ] && break
# Remove first character from variable.
PID="${PID:1}"
done
# Find the modalias file that contains our VID and PID.
UEVENTS="$(find /sys -name uevent | xargs grep "$VID""/""$PID" 2> /dev/null)"
if [ -n "$UEVENTS" ]
then
# Grab the last entry from $UEVENTS.
for UEVENT in $UEVENTS
do
UEVENT="$UEVENT"
done
"$Debug" "UEVENT=$UEVENT"
# Complete path to uevent file after stripping from :PRODUCT to end of line.
UEVENT="${UEVENT%:PRODUCT*}"
# Strip uevent file name from path.
DEVICE="${UEVENT%/*}"
"$Debug" "DEVICE=$DEVICE"
# Grab the device designation from the end of the path.
DEVICE="${DEVICE##*/}"
"$Debug" "UEVENT=$UEVENT"
"$Debug" "DEVICE=$DEVICE"
fi
if [ -f "$UEVENT" ]
then
# Grab the driver name from uevent file.
DRIVER="$(grep "DRIVER" $UEVENT | cut -d '=' -f2)"
"$Debug" "DRIVER=$DRIVER"
fi
if [ -n "$DRIVER" ]
then
# Find the directory named for the driver.
BINDPATH="$(find /sys -type d -name "$DRIVER")"
# That directory should contain the unbind and bind files.
BINDFILE="$BINDPATH""/bind"
UNBINDFILE="$BINDPATH""/unbind"
"$Debug" "BINDPATH=$BINDPATH"
"$Debug" "BINDFILE=$BINDFILE"
"$Debug" "UNBINDFILE=$UNBINDFILE"
fi
if [ -e "$UNBINDFILE" ]
then
# Remove device.
echo "$DEVICE" | sudo tee "$UNBINDFILE" 1> /dev/null
fi
"$Debug" "Sleep ..."
sleep 3
"$Debug" "... Wake up"
if [ -e "$BINDFILE" ]
then
# Add device back.
echo "$DEVICE" | sudo tee "$BINDFILE" 1> /dev/null
fi
... Thanks for pointing me in the right direction with this!Thanks for providing an interesting puzzle. :)
Thanks for providing an interesting puzzle. :)Only a hacker would say such a thing :)
$ ifconfig -a wlan1
ifconfig: wlan1: error fetching interface information: Device not found
$ tce-load -i firmware-atheros
firmware-atheros.tcz: OK
$ ./USBreset.sh
UEVENT=/sys/devices/pci0000:00/0000:00:1a.0/usb1/1-1/1-1.2/uevent:PRODUCT=40d/3801/108
DEVICE=/sys/devices/pci0000:00/0000:00:1a.0/usb1/1-1/1-1.2
UEVENT=/sys/devices/pci0000:00/0000:00:1a.0/usb1/1-1/1-1.2/uevent
DEVICE=1-1.2
DRIVER=
Sleep ...
... Wake up
$ ifconfig -a wlan1
ifconfig: wlan1: error fetching interface information: Device not found
#!/bin/sh
# Syntax: $ USBReset.sh <vendorId> <productId>
# Usage example: $ USBReset.sh
Debug="test"
# Uncomment the next line to turn on debugging messages.
Debug="echo"
# Vendor ID and product ID (e.g., 8644 8003).
VID="$1"
PID="$2"
# Strips leading zeros from variable. If variable is all
# zeros, variable will be set to a single zero.
while [ ${#VID} -gt 1 ] # ${#VID} returns string length of variable.
do
# If first character of variable is not "0", end loop.
[ ${VID:0:1} != "0" ] && break
# Remove first character from variable.
VID="${VID:1}"
done
# Strips leading zeros from variable. If variable is all
# zeros, variable will be set to a single zero.
while [ ${#PID} -gt 1 ] # ${#PID} returns string length of variable.
do
# If first character of variable is not "0", end loop.
[ ${PID:0:1} != "0" ] && break
# Remove first character from variable.
PID="${PID:1}"
done
# Find the modalias file that contains our VID and PID.
UEVENTS="$(find /sys -name uevent | xargs grep "$VID""/""$PID" 2> /dev/null)"
if [ -n "$UEVENTS" ]
then
# Grab the last entry from $UEVENTS.
for UEVENT in $UEVENTS
do
UEVENT="$UEVENT"
done
"$Debug" "UEVENT=$UEVENT"
# Complete path to uevent file after stripping from :PRODUCT to end of line.
UEVENT="${UEVENT%:PRODUCT*}"
# Strip uevent file name from path.
DEVICE="${UEVENT%/*}"
"$Debug" "DEVICE=$DEVICE"
# Grab the device designation from the end of the path.
DEVICE="${DEVICE##*/}"
"$Debug" "UEVENT=$UEVENT"
"$Debug" "DEVICE=$DEVICE"
fi
# Remove device.
echo "$DEVICE" | sudo tee /sys/bus/usb/drivers/usb/unbind >/dev/null 2>&1
"$Debug" "Sleep ..."
sleep 3
"$Debug" "... Wake up"
# Add device back.
echo "$DEVICE" | sudo tee /sys/bus/usb/drivers/usb/bind >/dev/null
$ ifconfig -a wlan1
ifconfig: wlan1: error fetching interface information: Device not found
$ tce-load -i firmware-atheros
firmware-atheros.tcz: OK
$ ./USBreset.sh 040d 3801
UEVENT=/sys/devices/pci0000:00/0000:00:1a.0/usb2/2-1/2-1.2/uevent:PRODUCT=40d/3801/108
DEVICE=/sys/devices/pci0000:00/0000:00:1a.0/usb2/2-1/2-1.2
UEVENT=/sys/devices/pci0000:00/0000:00:1a.0/usb2/2-1/2-1.2/uevent
DEVICE=2-1.2
Sleep ...
... Wake up
$ ifconfig -a wlan1
wlan1 Link encap:Ethernet HWaddr 00:12:7B:20:6B:A0
BROADCAST MULTICAST MTU:1500 Metric:1
RX packets:0 errors:0 dropped:0 overruns:0 frame:0
TX packets:0 errors:0 dropped:0 overruns:0 carrier:0
collisions:0 txqueuelen:1000
RX bytes:0 (0.0 B) TX bytes:0 (0.0 B)
Hi Rich. Why not unbind+bind using the generic /sys/bus/usb/drivers/usb/unbind and /sys/bus/usb/drivers/usb/bind? ...There are a bunch of directories under /sys/bus/usb/drivers/usb/.
#!/bin/sh
Debug="test"
# Uncomment the next line to turn on debugging messages.
Debug="echo"
# Vendor ID and product ID (8644:8003).
VID="8644"
PID="8003"
# Notes
# This is the driver property we are looking for.
# ID_USB_DRIVER=usb-storage
#
# This won't work because it searches for "ID_VENDOR_ID=0644 OR ID_MODEL_ID=0200"
# udevadm trigger -v -n -t devices -p ID_VENDOR_ID=0644 -p ID_MODEL_ID=0200
#
# Returns properties for the device listed in the path.
# udevadm info -q property -p /sys/devices/pci0000:00/0000:00:1d.7/usb1/1-7
udevadm trigger -v -n -t devices -p ID_VENDOR_ID="$VID" | sort > VIDs.txt
udevadm trigger -v -n -t devices -p ID_MODEL_ID="$PID" | sort > PIDs.txt
# Since we had to search for VID and PID separately, we check which
# results are common to both lists for an exact match.
DEVICES="$(comm -12 VIDs.txt PIDs.txt)"
[ -z "$DEVICES" ] && echo "No DEVICES found" && exit 1
for DEVICE in $DEVICES
do
ID_USB_DRIVER="$(udevadm info -q property -p "$DEVICE" | grep "ID_USB_DRIVER=" | cut -d = -f2)"
"$Debug" "DEVICE=$DEVICE ID_USB_DRIVER=$ID_USB_DRIVER"
[ -n "$ID_USB_DRIVER" ] && break
done
[ -z "$ID_USB_DRIVER" ] && echo "No ID_USB_DRIVER found" && exit 1
BINDPATH="$(find /sys/bus/usb/drivers/ -type d -name "$ID_USB_DRIVER")"
[ -z "$BINDPATH" ] && echo "No BINDPATH found" && exit 1
# That directory should contain the unbind and bind files.
BINDFILE="$BINDPATH""/bind"
UNBINDFILE="$BINDPATH""/unbind"
"$Debug" "BINDPATH=$BINDPATH"
"$Debug" "BINDFILE=$BINDFILE"
"$Debug" "UNBINDFILE=$UNBINDFILE"
# Needed for embedding a newline character in "for ITEM in ..." loop.
# Without it, sort and cut won't be applied to all ITEMs in LIST.
NL="
"
# BINDPATH directory contains links to devices using that driver.
# ${#ITEM} returns string length for sorting later on.
# "/sys/${ITEM##*../}" converts ../../../../devices/... to /sys/devices/...
for ITEM in $(readlink "$BINDPATH"/*)
do
LIST=$LIST$(printf "%04d %s\n" ${#ITEM} "/sys/${ITEM##*../}")$NL
done
"$Debug" "LIST=$LIST"
# We want to sort longest to shortest and remove the string lengths.
CHOICES="$(echo "$LIST" | sort -r | cut -c 6-)"
"$Debug" "CHOICES=$CHOICES"
# Yhis tries to find and remove needle (CHOICE) from haystack (DEVICE).
# If it succeeds, we found the link to our device.
for CHOICE in $CHOICES
do
if [ "$DEVICE" != "${DEVICE/$CHOICE/}" ]
then
# Remove leading path information.
USBPORT="${CHOICE##*/}"
"$Debug" "USBPORT=$USBPORT"
break
fi
done
if [ -e "$UNBINDFILE" ]
then
# Remove device.
echo "$USBPORT" | sudo tee "$UNBINDFILE" 1> /dev/null
fi
"$Debug" "Sleep ..."
sleep 3
"$Debug" "... Wake up"
if [ -e "$BINDFILE" ]
then
# Add device back.
echo "$USBPORT" | sudo tee "$BINDFILE" 1> /dev/null
fi
# Clean up.
rm -rf VIDs.txt
rm -rf PIDs.txt
$ ./USBreset.sh
DEVICE=/sys/devices/pci0000:00/0000:00:1a.0/usb2/2-1/2-1.2 ID_USB_DRIVER=
No ID_USB_DRIVER found
...it would be nice to simulate usb device disconnect+reconnect at software level, it seems non-trivial...I was on to something ;)
While it would be nice to simulate usb device disconnect+reconnect at software level, it seems non-trivial and more trouble than it's worth.
a bit of a tangent , idk how the lines are drawn (tbh) between "software level" and hwBy "software level" I mean anything other than manually pulling out the usb device and pushing it back into the usb port.
udevadm info -q property -p /sys/devices/pci0000:00/0000:00:1a.0/usb2/2-1/2-1.2
$ udevadm info -q property -p /sys/devices/pci0000:00/0000:00:1a.0/usb2/2-1/2-1.2
BUSNUM=002
DEVNAME=/dev/bus/usb/002/003
DEVNUM=003
DEVPATH=/devices/pci0000:00/0000:00:1a.0/usb2/2-1/2-1.2
DEVTYPE=usb_device
ID_BUS=usb
ID_MODEL=VIA_USB2.0_WLAN
ID_MODEL_ENC=VIA\x20USB2.0\x20WLAN
ID_MODEL_ID=3801
ID_REVISION=0108
ID_SERIAL=VIA_TECH_VIA_USB2.0_WLAN_12345
ID_SERIAL_SHORT=12345
ID_USB_INTERFACES=:ff0000:
ID_VENDOR=VIA_TECH
ID_VENDOR_ENC=VIA\x20TECH
ID_VENDOR_ID=040d
MAJOR=189
MINOR=130
PRODUCT=40d/3801/108
SUBSYSTEM=usb
TYPE=255/255/255
USEC_INITIALIZED=2151963
udevadm info -a -p /sys/devices/pci0000:00/0000:00:1a.0/usb2/2-1/2-1.2
$ udevadm info -a -p /sys/devices/pci0000:00/0000:00:1a.0/usb2/2-1/2-1.2
Udevadm info starts with the device specified by the devpath and then
walks up the chain of parent devices. It prints for every device
found, all possible attributes in the udev rules key format.
A rule to match, can be composed by the attributes of the device
and the attributes from one single parent device.
looking at device '/devices/pci0000:00/0000:00:1a.0/usb2/2-1/2-1.2':
KERNEL=="2-1.2"
SUBSYSTEM=="usb"
DRIVER==""
ATTR{configuration}==""
ATTR{bMaxPacketSize0}=="64"
ATTR{bDeviceClass}=="ff"
ATTR{bcdDevice}=="0108"
ATTR{bNumInterfaces}==""
ATTR{bConfigurationValue}==""
ATTR{manufacturer}=="VIA TECH"
ATTR{bNumConfigurations}=="1"
ATTR{authorized}=="1"
ATTR{speed}=="480"
ATTR{idProduct}=="3801"
ATTR{urbnum}=="11"
ATTR{devnum}=="6"
ATTR{product}=="VIA USB2.0 WLAN"
ATTR{maxchild}=="0"
ATTR{bmAttributes}==""
ATTR{bDeviceSubClass}=="ff"
ATTR{bMaxPower}==""
ATTR{rx_lanes}=="1"
ATTR{removable}=="unknown"
ATTR{idVendor}=="040d"
ATTR{version}==" 2.00"
ATTR{avoid_reset_quirk}=="0"
ATTR{serial}=="12345"
ATTR{bDeviceProtocol}=="ff"
ATTR{tx_lanes}=="1"
ATTR{ltm_capable}=="no"
ATTR{devpath}=="1.2"
ATTR{busnum}=="2"
ATTR{quirks}=="0x0"
looking at parent device '/devices/pci0000:00/0000:00:1a.0/usb2/2-1':
KERNELS=="2-1"
SUBSYSTEMS=="usb"
DRIVERS=="usb"
ATTRS{configuration}==""
ATTRS{bMaxPacketSize0}=="64"
ATTRS{bDeviceClass}=="09"
ATTRS{bcdDevice}=="0000"
ATTRS{bNumInterfaces}==" 1"
ATTRS{bConfigurationValue}=="1"
ATTRS{bNumConfigurations}=="1"
ATTRS{authorized}=="1"
ATTRS{speed}=="480"
ATTRS{idProduct}=="0024"
ATTRS{urbnum}=="61"
ATTRS{devnum}=="2"
ATTRS{maxchild}=="6"
ATTRS{bmAttributes}=="e0"
ATTRS{bDeviceSubClass}=="00"
ATTRS{bMaxPower}=="0mA"
ATTRS{rx_lanes}=="1"
ATTRS{removable}=="unknown"
ATTRS{idVendor}=="8087"
ATTRS{version}==" 2.00"
ATTRS{avoid_reset_quirk}=="0"
ATTRS{bDeviceProtocol}=="01"
ATTRS{tx_lanes}=="1"
ATTRS{ltm_capable}=="no"
ATTRS{devpath}=="1"
ATTRS{busnum}=="2"
ATTRS{quirks}=="0x0"
looking at parent device '/devices/pci0000:00/0000:00:1a.0/usb2':
KERNELS=="usb2"
SUBSYSTEMS=="usb"
DRIVERS=="usb"
ATTRS{configuration}==""
ATTRS{bMaxPacketSize0}=="64"
ATTRS{bDeviceClass}=="09"
ATTRS{bcdDevice}=="0606"
ATTRS{bNumInterfaces}==" 1"
ATTRS{bConfigurationValue}=="1"
ATTRS{manufacturer}=="Linux 6.6.8-tinycore64 ehci_hcd"
ATTRS{bNumConfigurations}=="1"
ATTRS{authorized}=="1"
ATTRS{speed}=="480"
ATTRS{idProduct}=="0002"
ATTRS{urbnum}=="26"
ATTRS{devnum}=="1"
ATTRS{product}=="EHCI Host Controller"
ATTRS{maxchild}=="3"
ATTRS{bmAttributes}=="e0"
ATTRS{bDeviceSubClass}=="00"
ATTRS{bMaxPower}=="0mA"
ATTRS{rx_lanes}=="1"
ATTRS{removable}=="unknown"
ATTRS{idVendor}=="1d6b"
ATTRS{interface_authorized_default}=="1"
ATTRS{authorized_default}=="1"
ATTRS{version}==" 2.00"
ATTRS{avoid_reset_quirk}=="0"
ATTRS{serial}=="0000:00:1a.0"
ATTRS{bDeviceProtocol}=="00"
ATTRS{tx_lanes}=="1"
ATTRS{ltm_capable}=="no"
ATTRS{devpath}=="0"
ATTRS{busnum}=="2"
ATTRS{quirks}=="0x0"
looking at parent device '/devices/pci0000:00/0000:00:1a.0':
KERNELS=="0000:00:1a.0"
SUBSYSTEMS=="pci"
DRIVERS=="ehci-pci"
ATTRS{power_state}=="D0"
ATTRS{broken_parity_status}=="0"
ATTRS{subsystem_device}=="0x1e2d"
ATTRS{dma_mask_bits}=="32"
ATTRS{vendor}=="0x8086"
ATTRS{local_cpus}=="f"
ATTRS{companion}==""
ATTRS{class}=="0x0c0320"
ATTRS{msi_bus}=="1"
ATTRS{device}=="0x1e2d"
ATTRS{local_cpulist}=="0-3"
ATTRS{driver_override}=="(null)"
ATTRS{d3cold_allowed}=="1"
ATTRS{irq}=="19"
ATTRS{revision}=="0x04"
ATTRS{reset_method}=="af_flr pm"
ATTRS{consistent_dma_mask_bits}=="32"
ATTRS{ari_enabled}=="0"
ATTRS{uframe_periodic_max}=="100"
ATTRS{enable}=="1"
ATTRS{subsystem_vendor}=="0x8086"
looking at parent device '/devices/pci0000:00':
KERNELS=="pci0000:00"
SUBSYSTEMS==""
DRIVERS==""
ATTRS{waiting_for_supplier}=="0"
By "software level" I mean anything other than manually pulling out the usb device and pushing it back into the usb port.
#!/bin/sh
# Script to reset a USB device written by Richard A. Rost September 4,2024
# This script attempts to replicate the behavior of unplugging/replugging
# a USB device by unbinding/binding the devices driver.
# *************************************************************************** #
#
# Default behavior for Debug and Reset can be changed here.
# -d command line switch turns on debugging messages.
Debug="test"
# Uncomment the next line to turn on debugging messages.
#Debug="echo"
# -r command line switch requests resetting the device.
Reset="No"
# Uncomment the next line to request resetting the device.
#Reset="Yes"
# *************************************************************************** #
# --------------------------------- Aliases --------------------------------- #
alias cp='busybox cp'
alias cut='busybox cut'
alias mkdir='busybox mkdir -p'
alias rm='busybox rm -rf'
alias tr='busybox tr'
# ------------------------------- End Aliases ------------------------------- #
# -------------------------------- Constants -------------------------------- #
# Current version.
VERSION="Version 0.1"
# Maximum number of command line parameters we accept.
MAXPARAMS=2
# Number of command line parameters passed in.
PARAMCOUNT=$#
# List of parameters passed in.
PARAMLIST=$@
# Needed for embedding a newline character in some commands.
NL="
"
# These are the properties we'll be searching for in WalkTheSysPath().
SEARCHTERMS="SUBSYSTEM= DRIVER="
# ------------------------------ End Constants ------------------------------ #
# -------------------------------- Variables -------------------------------- #
# Set if user passes in /dev/, such as /dev/sdg , /dev/sdg1 , or /dev/ttyUSB0.
DEV=""
# Set if user passes in VID:PID. VID and PID must each be 4 digits separated by
# a colon. VID and PID are hex and may contain letters. Both uppercase and
# lowercase are acceptable.
VIDPID=""
# VID and PID separated with colon removed.
VID=""
PID=""
# Variable used for temporary lists.
TMPLIST=""
# The path we traverse from end to beginning in search for its driver.
DEVPATH=""
# Working copy of DEVPATH used by WalkTheSysPath().
SEARCHPATH=""
# The text after the last slash in DEVPATH. Echoed into unbind/bind to
# identify which USB device to effect.
ENDPATH=""
# These are the properties we are scanning for. We want to find:
# [ SUBSYSTEM == "usb" ] && [ DRIVER != "" ]
SUBSYSTEM=""
DRIVER=""
# Driver directory.
BINDPATH=""
# That directory should contain the unbind and bind files.
BINDFILE=""
UNBINDFILE=""
# ------------------------------ End Variables ------------------------------ #
# -------------------------------- Functions -------------------------------- #
# --------------------------------------------------------------------------- #
ParseCommand()
{
# [ $PARAMCOUNT -gt $MAXPARAMS ] && Usage "To many parameters entered."
for ITEM in $PARAMLIST
do
case $ITEM in
-h) Usage ;;
-help) Usage ;;
--help) Usage ;;
-d) # Throw away -d to avoid Invalid option error. It's handled in Main.
;;
-r) # Request device reset.
Reset="Yes"
;;
"/dev/"?*)
# Skip if DEV already defined.
[ -n "$DEV" ] && continue
# Skip if VIDPID already defined.
[ -n "$VIDPID" ] && continue
DEV="$ITEM"
# The results are saved along with string lengths for sorting later.
for DEVPATH in $(udevadm trigger -v -n -t devices -p DEVNAME="$DEV")
do
TMPLIST=$TMPLIST$(printf "%04d %s\n" ${#DEVPATH} "$DEVPATH")$NL
done
"$Debug" "TMPLIST=$TMPLIST"
;;
[0-9a-fA-F][0-9a-fA-F][0-9a-fA-F][0-9a-fA-F]:[0-9a-fA-F][0-9a-fA-F][0-9a-fA-F][0-9a-fA-F])
# Skip if DEV already defined.
[ -n "$DEV" ] && continue
# Skip if VIDPID already defined.
[ -n "$VIDPID" ] && continue
# Covert uppercase characters to lowercase.
VIDPID="$(echo "$ITEM" | busybox tr '[A-Z]' '[a-z]')"
# Separate VID from PID and remove colon.
VID="$(echo "$VIDPID" | cut -c 1-4)"
PID="$(echo "$VIDPID" | cut -c 6-9)"
"$Debug" "VID=$VID PID=$PID"
# This won't work because it searches for "ID_VENDOR_ID=0644 OR ID_MODEL_ID=0200"
# udevadm trigger -v -n -t devices -p ID_VENDOR_ID=0644 -p ID_MODEL_ID=0200
udevadm trigger -v -n -t devices -p ID_VENDOR_ID="$VID" | sort > VIDs.txt
udevadm trigger -v -n -t devices -p ID_MODEL_ID="$PID" | sort > PIDs.txt
# Since we had to search for VID and PID separately, we check which
# results are common to both lists for an exact match. The results
# are saved along with string lengths for sorting later.
for DEVPATH in $(comm -12 VIDs.txt PIDs.txt)
do
TMPLIST=$TMPLIST$(printf "%04d %s\n" ${#DEVPATH} "$DEVPATH")$NL
done
# Clean up the files we created.
rm VIDs.txt PIDs.txt
"$Debug" "TMPLIST=$TMPLIST"
;;
*)
echo "Invalid option."
exit 1
;;
esac
done
}
# --------------------------------------------------------------------------- #
# --------------------------------------------------------------------------- #
ParseList()
{
# Parse list of DEVPATHs returned by udevadm and save the longest line.
# Sort our list, longest lines first, remove string length, and
# save the first entry (longest string).
DEVPATH="$(echo "$TMPLIST" | sort -r | cut -c 6- | head -n 1)"
"$Debug" "DEVPATH=$DEVPATH"
TMPLIST=""
# Make sure we got result.
[ -z "$DEVPATH" ] && echo "No DEVPATH found" && exit 1
# Make sure it's USB device.
[ ! $(echo "$DEVPATH" | grep -E '/usb[0-9]') ] && echo "Not a USB device" && exit 1
}
# --------------------------------------------------------------------------- #
# --------------------------------------------------------------------------- #
SetupBindPaths()
{
# Find the drivers directory.
BINDPATH="$(find /sys/bus/usb/drivers/ -type d -name "$DRIVER")"
[ -z "$BINDPATH" ] && echo "No BINDPATH found" && exit 1
# That directory should contain the unbind and bind files.
BINDFILE="$BINDPATH""/bind"
UNBINDFILE="$BINDPATH""/unbind"
"$Debug" "BINDPATH=$BINDPATH"
"$Debug" "BINDFILE=$BINDFILE"
"$Debug" "UNBINDFILE=$UNBINDFILE"
# Make sure BINDPATH contains correct ENDPATH link.
[ "$(realpath "$BINDPATH/$ENDPATH")" != "$SEARCHPATH" ] && echo "Bad BINDPATH" && exit 1
}
# --------------------------------------------------------------------------- #
# --------------------------------------------------------------------------- #
Usage()
{
[ "$1" != "" ] && echo "$1" && echo
echo "
$0 $VERSION
This script attempts to reset a USB device without an unplug/replug
sequence using a supplied /dev/device OR VID:PID. The device will
only be reset if the -r switch is included.
Usage:
$0 /dev/device | VID:PID [ -d -r ]
/dev/device Must point to a connected USB device.
VID:PID Two 4 digit IDs separated by a colon.
Since IDs are hex, they may contain
letters. Upper and/or lower case are OK.
-d Turn on debugging messages.
-r Request device reset.
Command line switches must be given separately, ie -d -r, not -dr.
"
exit
}
# --------------------------------------------------------------------------- #
# --------------------------------------------------------------------------- #
WalkTheSysPath()
{
# This walks up the DEVPATH by shortening it one entry at a time in an
# effort to located its driver.
"$Debug" "$NL""WalkTheSysPath(): Searching for SUBSYSTEM and DRIVER."
# SEARCHPATH is our working copy of DEVPATH.
SEARCHPATH="$DEVPATH"
# Copy the text after the last slash in SEARCHPATH.
ENDPATH="${SEARCHPATH##*/}"
# When ENDPATH begins with usbN* where N is a digit, we've hit the USB border.
while [ ! $(echo "$ENDPATH" | grep -E '^usb[0-9]') ]
do
"$Debug" "$NL""SEARCHPATH=$SEARCHPATH"
INFO="$(udevadm info -q property -p "$SEARCHPATH" 2> /dev/null)"
for ST in $SEARCHTERMS
do
# Search for our search term (i.e. SUBSYSTEM=).
T="$(echo "$INFO" | grep -E "^$ST")"
# Remove our search term (i.e. SUBSYSTEM=) from the result.
T="${T/$ST/}"
case $ST in
"SUBSYSTEM=")
# If the result matched our search term, save it.
SUBSYSTEM="$T"
"$Debug" "$ST$T"
;;
"DRIVER=")
DRIVER="$T"
"$Debug" "$ST$T"
;;
esac
done
if [ "$SUBSYSTEM" == "usb" ] && [ "$DRIVER" != "" ]
then
# We found what we were searching for.
"$Debug" "$NL""WalkTheSysPath(): Search complete."
"$Debug" "SEARCHPATH=$SEARCHPATH"
"$Debug" "ENDPATH=$ENDPATH SUBSYSTEM=$SUBSYSTEM DRIVER=$DRIVER"
return
fi
# Clear the properties for the next pass.
SUBSYSTEM=""
DRIVER=""
# Shorten the path and update ENDPATH for the next pass
SEARCHPATH="${SEARCHPATH%/*}"
ENDPATH="${SEARCHPATH##*/}"
done
"$Debug" "$NL""WalkTheSysPath(): Search failed."
}
# --------------------------------------------------------------------------- #
# ------------------------------ End Functions ------------------------------ #
# ---------------------------------- Main ----------------------------------- #
# Program starts here.
[ $PARAMCOUNT -eq 0 ] && Usage
for ITEM in $PARAMLIST
do
# We want this to happen before any debug messages are encountered.
# Turn on debugging messages if option -d is specified.
[ "$ITEM" == "-d" ] && Debug="echo" && break
done
ParseCommand
ParseList
WalkTheSysPath
SetupBindPaths
# See if device reset (-r) was requested.
if [ "$Reset" == "Yes" ]
then
if [ -e "$UNBINDFILE" ]
then
# Remove device.
echo "$ENDPATH" | sudo tee "$UNBINDFILE" 1> /dev/null
fi
"$Debug" "Sleep ..."
sleep 3
"$Debug" "... Wake up"
if [ -e "$BINDFILE" ]
then
# Add device back.
echo "$ENDPATH" | sudo tee "$BINDFILE" 1> /dev/null
fi
fi
exit
tc@E310:~$ ./Scripting/USB_Reset/USBreset.sh -h
./Scripting/USB_Reset/USBreset.sh Version 0.1
This script attempts to reset a USB device without an unplug/replug
sequence using a supplied /dev/device OR VID:PID. The device will
only be reset if the -r switch is included.
Usage:
./Scripting/USB_Reset/USBreset.sh /dev/device | VID:PID [ -d -r ]
/dev/device Must point to a connected USB device.
VID:PID Two 4 digit IDs separated by a colon.
Since IDs are hex, they may contain
letters. Upper and/or lower case are OK.
-d Turn on debugging messages.
-r Request device reset.
Command line switches must be given separately, ie -d -r, not -dr.
tc@E310:~$ ./Scripting/USB_Reset/USBreset.sh 8644:8003 -d -r > USBtest.txt
VID=8644 PID=8003
TMPLIST=0045 /sys/devices/pci0000:00/0000:00:1d.7/usb1/1-5
0089 /sys/devices/pci0000:00/0000:00:1d.7/usb1/1-5/1-5:1.0/host7/target7:0:0/7:0:0:0/block/sdg
0094 /sys/devices/pci0000:00/0000:00:1d.7/usb1/1-5/1-5:1.0/host7/target7:0:0/7:0:0:0/block/sdg/sdg1
DEVPATH=/sys/devices/pci0000:00/0000:00:1d.7/usb1/1-5/1-5:1.0/host7/target7:0:0/7:0:0:0/block/sdg/sdg1
WalkTheSysPath(): Searching for SUBSYSTEM and DRIVER.
SEARCHPATH=/sys/devices/pci0000:00/0000:00:1d.7/usb1/1-5/1-5:1.0/host7/target7:0:0/7:0:0:0/block/sdg/sdg1
SUBSYSTEM=block
DRIVER=
SEARCHPATH=/sys/devices/pci0000:00/0000:00:1d.7/usb1/1-5/1-5:1.0/host7/target7:0:0/7:0:0:0/block/sdg
SUBSYSTEM=block
DRIVER=
SEARCHPATH=/sys/devices/pci0000:00/0000:00:1d.7/usb1/1-5/1-5:1.0/host7/target7:0:0/7:0:0:0/block
SUBSYSTEM=
DRIVER=
SEARCHPATH=/sys/devices/pci0000:00/0000:00:1d.7/usb1/1-5/1-5:1.0/host7/target7:0:0/7:0:0:0
SUBSYSTEM=scsi
DRIVER=sd
SEARCHPATH=/sys/devices/pci0000:00/0000:00:1d.7/usb1/1-5/1-5:1.0/host7/target7:0:0
SUBSYSTEM=scsi
DRIVER=
SEARCHPATH=/sys/devices/pci0000:00/0000:00:1d.7/usb1/1-5/1-5:1.0/host7
SUBSYSTEM=scsi
DRIVER=
SEARCHPATH=/sys/devices/pci0000:00/0000:00:1d.7/usb1/1-5/1-5:1.0
SUBSYSTEM=usb
DRIVER=usb-storage
WalkTheSysPath(): Search complete.
SEARCHPATH=/sys/devices/pci0000:00/0000:00:1d.7/usb1/1-5/1-5:1.0
ENDPATH=1-5:1.0 SUBSYSTEM=usb DRIVER=usb-storage
BINDPATH=/sys/bus/usb/drivers/usb-storage
BINDFILE=/sys/bus/usb/drivers/usb-storage/bind
UNBINDFILE=/sys/bus/usb/drivers/usb-storage/unbind
Sleep ...
... Wake up
TMPLIST shows the results udevadm returned for the VID:PID which I$ ifconfig -a wlan1
ifconfig: wlan1: error fetching interface information: Device not found
$ tce-load -i firmware-atheros
firmware-atheros.tcz: OK
$ ./USBReset.sh 040d:3801 -d -r
VID=040d PID=3801
TMPLIST=0051 /sys/devices/pci0000:00/0000:00:1a.0/usb2/2-1/2-1.2
DEVPATH=/sys/devices/pci0000:00/0000:00:1a.0/usb2/2-1/2-1.2
WalkTheSysPath(): Searching for SUBSYSTEM and DRIVER.
SEARCHPATH=/sys/devices/pci0000:00/0000:00:1a.0/usb2/2-1/2-1.2
SUBSYSTEM=usb
DRIVER=
SEARCHPATH=/sys/devices/pci0000:00/0000:00:1a.0/usb2/2-1
SUBSYSTEM=usb
DRIVER=usb
WalkTheSysPath(): Search complete.
SEARCHPATH=/sys/devices/pci0000:00/0000:00:1a.0/usb2/2-1
ENDPATH=2-1 SUBSYSTEM=usb DRIVER=usb
BINDPATH=/sys/bus/usb/drivers/usb
BINDFILE=/sys/bus/usb/drivers/usb/bind
UNBINDFILE=/sys/bus/usb/drivers/usb/unbind
Sleep ...
... Wake up
$ ifconfig -a wlan1
wlan1 Link encap:Ethernet HWaddr 00:12:7B:20:6B:A0
BROADCAST MULTICAST MTU:1500 Metric:1
RX packets:0 errors:0 dropped:0 overruns:0 frame:0
TX packets:0 errors:0 dropped:0 overruns:0 carrier:0
collisions:0 txqueuelen:1000
RX bytes:0 (0.0 B) TX bytes:0 (0.0 B)
You're back and you mean business! ...Some of us are stubborn and too dumb to know when to quit. :)
... It works like a charm now: ...How about that, sometimes being persistent does pay off.
... Wow, you are definitely not one to settle for superficial/accidental solutions. This is impressively thorough. ...I did my best to cover all the bases. I added the option of using /dev/device for a
... Many thanks, Rich.You are quite welcome. Thank you for your patience. If you have
tc@E310:~$ udevadm trigger -v -n -t devices -a address=00:13:20:c4:4a:20
/sys/devices/pci0000:00/0000:00:1e.0/0000:03:08.0/net/eth0
tc@E310:~$
Some of us are stubborn and too dumb to know when to quit. :)Ha! I know how that goes. Sometimes I just want a quick fix to get a problem out of the way. But sometimes I want to throw an atom bomb on the problem so that I never have to rehash a quick fix again.
Could you please post the result of this command using your MAC address:
$ sudo udevadm trigger -v -n -t devices -a address=00:12:7b:20:6b:a0
/sys/devices/pci0000:00/0000:00:1a.0/usb2/2-1/2-1.2/2-1.2:1.0/net/wlan1
...Excellent. When I have a chance later tonight or tomorrow, I willCode: [Select]$ sudo udevadm trigger -v -n -t devices -a address=00:12:7b:20:6b:a0
/sys/devices/pci0000:00/0000:00:1a.0/usb2/2-1/2-1.2/2-1.2:1.0/net/wlan1
# Covert uppercase characters to lowercase.
VIDPID="$(echo "$ITEM" | busybox tr '[A-Z]' '[a-z]')"
/home/tc/Scripting/USB_Reset/USBreset.sh Version 0.1
This script attempts to reset a USB device without an unplug/replug
sequence using a supplied /dev/device OR VID:PID. The device will
only be reset if the -r switch is included.
Usage:
/home/tc/Scripting/USB_Reset/USBreset.sh /dev/device | VID:PID [ -d -r ]
/dev/device Must point to a connected USB device.
VID:PID Two 4 digit IDs separated by a colon.
Since IDs are hex, they may contain
letters. Upper and/or lower case are OK.
-d Turn on debugging messages.
-r Request device reset.
Command line switches must be given separately, ie -d -r, not -dr.
#!/bin/sh
# Script to reset a USB device written by Richard A. Rost September 4,2024
# This script attempts to replicate the behavior of unplugging/replugging
# a USB device by unbinding/binding the devices driver.
#
# Special thanks to GNUser for his patience and help in testing and debugging.
#
# ------------------------------- Change Log -------------------------------- #
# Version 0.1 Sep 5,2024
# First working release.
#
# Version 0.2 Sep 6,2024
# In addition to /dev/device Or VID:PID, A third choice of MAC address is now
# available.
# Upper to lower case conversion was turned into function ToLower().
#
# ----------------------------- End Change Log ------------------------------ #
# *************************************************************************** #
#
# Default behavior for Debug and Reset can be changed here.
# -d command line switch turns on debugging messages.
Debug="test"
# Uncomment the next line to turn on debugging messages.
#Debug="echo"
# -r command line switch requests resetting the device.
Reset="No"
# Uncomment the next line to request resetting the device.
#Reset="Yes"
# *************************************************************************** #
# --------------------------------- Aliases --------------------------------- #
alias cp='busybox cp'
alias cut='busybox cut'
alias mkdir='busybox mkdir -p'
alias rm='busybox rm -rf'
alias tr='busybox tr'
# ------------------------------- End Aliases ------------------------------- #
# -------------------------------- Constants -------------------------------- #
# Current version.
VERSION="Version 0.2"
# Maximum number of command line parameters we accept.
MAXPARAMS=2
# Number of command line parameters passed in.
PARAMCOUNT=$#
# List of parameters passed in.
PARAMLIST=$@
# Needed for embedding a newline character in some commands.
NL="
"
# These are the properties we'll be searching for in WalkTheSysPath().
SEARCHTERMS="SUBSYSTEM= DRIVER="
# ------------------------------ End Constants ------------------------------ #
# -------------------------------- Variables -------------------------------- #
# Set if user passes in /dev/, such as /dev/sdg , /dev/sdg1 , or /dev/ttyUSB0.
DEV=""
# Set if user passes in VID:PID. VID and PID must each be 4 digits separated by
# a colon. VID and PID are hex and may contain letters. Both uppercase and
# lowercase are acceptable.
VIDPID=""
# VID and PID separated with colon removed.
VID=""
PID=""
# Set if user passes in a MAC address.
MACADDR=""
# Variable used for temporary lists.
TMPLIST=""
# The path we traverse from end to beginning in search for its driver.
DEVPATH=""
# Working copy of DEVPATH used by WalkTheSysPath().
SEARCHPATH=""
# The text after the last slash in DEVPATH. Echoed into unbind/bind to
# identify which USB device to effect.
ENDPATH=""
# These are the properties we are scanning for. We want to find:
# [ SUBSYSTEM == "usb" ] && [ DRIVER != "" ]
SUBSYSTEM=""
DRIVER=""
# Driver directory.
BINDPATH=""
# That directory should contain the unbind and bind files.
BINDFILE=""
UNBINDFILE=""
# ------------------------------ End Variables ------------------------------ #
# -------------------------------- Functions -------------------------------- #
# --------------------------------------------------------------------------- #
ParseCommand()
{
for ITEM in $PARAMLIST
do
case $ITEM in
-h) Usage ;;
-help) Usage ;;
--help) Usage ;;
-d) # Throw away -d to avoid Invalid option error. It's handled in Main.
;;
-r) # Request device reset.
Reset="Yes"
;;
"/dev/"?*)
# Skip if DEV already defined.
[ -n "$DEV" ] && continue
# Skip if VIDPID already defined.
[ -n "$VIDPID" ] && continue
# Skip if MACADDR already defined.
[ -n "$MACADDR" ] && continue
DEV="$ITEM"
# The results are saved along with string lengths for sorting later.
for DEVPATH in $(udevadm trigger -v -n -t devices -p DEVNAME="$DEV")
do
TMPLIST=$TMPLIST$(printf "%04d %s\n" ${#DEVPATH} "$DEVPATH")$NL
done
"$Debug" "TMPLIST=$TMPLIST"
;;
[0-9a-fA-F][0-9a-fA-F][0-9a-fA-F][0-9a-fA-F]:[0-9a-fA-F][0-9a-fA-F][0-9a-fA-F][0-9a-fA-F])
# Skip if DEV already defined.
[ -n "$DEV" ] && continue
# Skip if VIDPID already defined.
[ -n "$VIDPID" ] && continue
# Skip if MACADDR already defined.
[ -n "$MACADDR" ] && continue
# Covert uppercase characters to lowercase.
VIDPID="$(ToLower "$ITEM")"
# Separate VID from PID and remove colon.
VID="$(echo "$VIDPID" | cut -c 1-4)"
PID="$(echo "$VIDPID" | cut -c 6-9)"
"$Debug" "VID=$VID PID=$PID"
# This won't work because it searches for "ID_VENDOR_ID=0644 OR ID_MODEL_ID=0200"
# udevadm trigger -v -n -t devices -p ID_VENDOR_ID=0644 -p ID_MODEL_ID=0200
udevadm trigger -v -n -t devices -p ID_VENDOR_ID="$VID" | sort > VIDs.txt
udevadm trigger -v -n -t devices -p ID_MODEL_ID="$PID" | sort > PIDs.txt
# Since we had to search for VID and PID separately, we check which
# results are common to both lists for an exact match. The results
# are saved along with string lengths for sorting later.
for DEVPATH in $(comm -12 VIDs.txt PIDs.txt)
do
TMPLIST=$TMPLIST$(printf "%04d %s\n" ${#DEVPATH} "$DEVPATH")$NL
done
# Clean up the files we created.
rm VIDs.txt PIDs.txt
"$Debug" "TMPLIST=$TMPLIST"
;;
[0-9a-fA-F][0-9a-fA-F]:[0-9a-fA-F][0-9a-fA-F]:[0-9a-fA-F][0-9a-fA-F]:[0-9a-fA-F][0-9a-fA-F]:[0-9a-fA-F][0-9a-fA-F]:[0-9a-fA-F][0-9a-fA-F])
# Skip if DEV already defined.
[ -n "$DEV" ] && continue
# Skip if VIDPID already defined.
[ -n "$VIDPID" ] && continue
# Skip if MACADDR already defined.
[ -n "$MACADDR" ] && continue
# Covert uppercase characters to lowercase.
MACADDR="$(ToLower "$ITEM")"
"$Debug" "MACADDR=$MACADDR"
# The results are saved along with string lengths for sorting later.
for DEVPATH in $(udevadm trigger -v -n -t devices -a address="$MACADDR")
do
TMPLIST=$TMPLIST$(printf "%04d %s\n" ${#DEVPATH} "$DEVPATH")$NL
done
"$Debug" "TMPLIST=$TMPLIST"
;;
*)
echo "Invalid option."
exit 1
;;
esac
done
}
# --------------------------------------------------------------------------- #
# --------------------------------------------------------------------------- #
ParseList()
{
# Parse list of DEVPATHs returned by udevadm and save the longest line.
# Sort our list, longest lines first, remove string length, and
# save the first entry (longest string).
DEVPATH="$(echo "$TMPLIST" | sort -r | cut -c 6- | head -n 1)"
"$Debug" "DEVPATH=$DEVPATH"
TMPLIST=""
# Make sure we got result.
[ -z "$DEVPATH" ] && echo "No DEVPATH found" && exit 1
# Make sure it's USB device.
[ ! $(echo "$DEVPATH" | grep -E '/usb[0-9]') ] && echo "Not a USB device" && exit 1
}
# --------------------------------------------------------------------------- #
# --------------------------------------------------------------------------- #
SetupBindPaths()
{
# Find the drivers directory.
BINDPATH="$(find /sys/bus/usb/drivers/ -type d -name "$DRIVER")"
[ -z "$BINDPATH" ] && echo "No BINDPATH found" && exit 1
# That directory should contain the unbind and bind files.
BINDFILE="$BINDPATH""/bind"
UNBINDFILE="$BINDPATH""/unbind"
"$Debug" "BINDPATH=$BINDPATH"
"$Debug" "BINDFILE=$BINDFILE"
"$Debug" "UNBINDFILE=$UNBINDFILE"
# Make sure BINDPATH contains correct ENDPATH link.
[ "$(realpath "$BINDPATH/$ENDPATH")" != "$SEARCHPATH" ] && echo "Bad BINDPATH" && exit 1
}
# --------------------------------------------------------------------------- #
# --------------------------------------------------------------------------- #
ToLower()
{
# Converts any uppercase characters in a string to lowercase.
# Usage:
# Dest="$(ToLower Src)"
echo "$(echo "$1" | tr '[A-Z]' '[a-z]')"
}
# --------------------------------------------------------------------------- #
# --------------------------------------------------------------------------- #
Usage()
{
[ "$1" != "" ] && echo "$1" && echo
echo "
${0##*/} $VERSION
This script attempts to reset a USB device without an unplug/replug
sequence using a supplied /dev/device OR VID:PID OR MAC address.
The device will only be reset if the -r switch is included.
Usage:
${0##*/} /dev/device | VID:PID | XX:XX:XX:XX:XX:XX [ -d -r ]
/dev/device Must point to a connected USB device.
VID:PID Two 4 digit IDs separated by a colon.
Since IDs are hex, they may contain
letters. Upper and/or lower case are OK.
XX:XX:XX:XX:XX:XX Six 2 digit numbers separated by colons
that make up the MAC address of a network
interface device. Since these numbers are
hex, they may contain letters. Upper and/or
lower case are OK.
-d Turn on debugging messages.
-r Request device reset.
Command line switches must be given separately, ie -d -r, not -dr.
"
exit
}
# --------------------------------------------------------------------------- #
# --------------------------------------------------------------------------- #
WalkTheSysPath()
{
# This walks up the DEVPATH by shortening it one entry at a time in an
# effort to located its driver.
"$Debug" "$NL""WalkTheSysPath(): Searching for SUBSYSTEM and DRIVER."
# SEARCHPATH is our working copy of DEVPATH.
SEARCHPATH="$DEVPATH"
# Copy the text after the last slash in SEARCHPATH.
ENDPATH="${SEARCHPATH##*/}"
# When ENDPATH begins with usbN* where N is a digit, we've hit the USB border.
while [ ! $(echo "$ENDPATH" | grep -E '^usb[0-9]') ]
do
"$Debug" "$NL""SEARCHPATH=$SEARCHPATH"
INFO="$(udevadm info -q property -p "$SEARCHPATH" 2> /dev/null)"
for ST in $SEARCHTERMS
do
# Search for our search term (i.e. SUBSYSTEM=).
T="$(echo "$INFO" | grep -E "^$ST")"
# Remove our search term (i.e. SUBSYSTEM=) from the result.
T="${T/$ST/}"
case $ST in
"SUBSYSTEM=")
# If the result matched our search term, save it.
SUBSYSTEM="$T"
"$Debug" "$ST$T"
;;
"DRIVER=")
DRIVER="$T"
"$Debug" "$ST$T"
;;
esac
done
if [ "$SUBSYSTEM" == "usb" ] && [ "$DRIVER" != "" ]
then
# We found what we were searching for.
"$Debug" "$NL""WalkTheSysPath(): Search complete."
"$Debug" "SEARCHPATH=$SEARCHPATH"
"$Debug" "ENDPATH=$ENDPATH SUBSYSTEM=$SUBSYSTEM DRIVER=$DRIVER"
return
fi
# Clear the properties for the next pass.
SUBSYSTEM=""
DRIVER=""
# Shorten the path and update ENDPATH for the next pass
SEARCHPATH="${SEARCHPATH%/*}"
ENDPATH="${SEARCHPATH##*/}"
done
"$Debug" "$NL""WalkTheSysPath(): Search failed."
}
# --------------------------------------------------------------------------- #
# ------------------------------ End Functions ------------------------------ #
# ---------------------------------- Main ----------------------------------- #
# Program starts here.
[ $PARAMCOUNT -eq 0 ] && Usage
for ITEM in $PARAMLIST
do
# We want this to happen before any debug messages are encountered.
# Turn on debugging messages if option -d is specified.
[ "$ITEM" == "-d" ] && Debug="echo" && break
done
ParseCommand
ParseList
WalkTheSysPath
SetupBindPaths
# See if device reset (-r) was requested.
if [ "$Reset" == "Yes" ]
then
if [ -e "$UNBINDFILE" ]
then
# Remove device.
echo "$ENDPATH" | sudo tee "$UNBINDFILE" 1> /dev/null
fi
"$Debug" "Sleep ..."
sleep 3
"$Debug" "... Wake up"
if [ -e "$BINDFILE" ]
then
# Add device back.
echo "$ENDPATH" | sudo tee "$BINDFILE" 1> /dev/null
fi
fi
exit
tc@E310:~/ReplaceMe$ ~/Scripting/USB_Reset/USBreset.sh -d 00:13:20:C4:4A:20
MACADDR=00:13:20:c4:4a:20
TMPLIST=0058 /sys/devices/pci0000:00/0000:00:1e.0/0000:03:08.0/net/eth0
DEVPATH=/sys/devices/pci0000:00/0000:00:1e.0/0000:03:08.0/net/eth0
Not a USB device
tc@E310:~/ReplaceMe$
I wanted to get back to you sooner, but sometimes real life interferes with my linux addiction ;D ...No worries, there was no rush. Besides, patience is one of my specialties.
... At this point I can't think of any way of making this script better. ...But if you do, let me know. :)