WelcomeWelcome | FAQFAQ | DownloadsDownloads | WikiWiki

Author Topic: how do firmware extensions work without startup script?  (Read 2417 times)

Offline GNUser

  • Wiki Author
  • Hero Member
  • *****
  • Posts: 1494
Re: how do firmware extensions work without startup script?
« Reply #30 on: September 03, 2024, 12:30:58 PM »
Sure thing:

Code: [Select]
$ 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

Offline Rich

  • Administrator
  • Hero Member
  • *****
  • Posts: 11573
Re: how do firmware extensions work without startup script?
« Reply #31 on: September 03, 2024, 02:31:39 PM »
Hi GNUser
I think I need a deeper look. How about this please:
Code: [Select]
udevadm info -a -p /sys/devices/pci0000:00/0000:00:1a.0/usb2/2-1/2-1.2

Offline GNUser

  • Wiki Author
  • Hero Member
  • *****
  • Posts: 1494
Re: how do firmware extensions work without startup script?
« Reply #32 on: September 03, 2024, 02:43:50 PM »
Code: [Select]
$ 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"

Offline mocore

  • Hero Member
  • *****
  • Posts: 633
  • ~.~
Re: how do firmware extensions work without startup script?
« Reply #33 on: September 04, 2024, 03:32:50 AM »
By "software level" I mean anything other than manually pulling out the usb device and pushing it back into the usb port.

posted in case it of any use (i might be barking ... up the wrong tree)
 this builds on the previously linked uhubctl  https://github.com/octoprobe/usbhubctl and mentions navigating usb topology
so seamingly similar (atleast to me) domain 

Offline GNUser

  • Wiki Author
  • Hero Member
  • *****
  • Posts: 1494
Re: how do firmware extensions work without startup script?
« Reply #34 on: September 04, 2024, 07:39:44 AM »
Thanks, mocore. I'll check out usbhubctl as it sounds useful. That being said, I think this particular little problem is solvable without external utilities (i.e., using only what's available in TCL base).

Offline Rich

  • Administrator
  • Hero Member
  • *****
  • Posts: 11573
Re: how do firmware extensions work without startup script?
« Reply #35 on: September 05, 2024, 04:07:28 PM »
Hi GNUser
OK, I'm back.

Here's the new cut:
Code: [Select]
#!/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

Help is included:
Code: [Select]
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.

The default behavior is  debugging messages  and  device reset  are off.
Use -d and -r to turn them on. There are settings at the beginning of the
script if you want to have them default to on. There are no - switches to
turn them off.

This is what it looks like resetting a thumb drive with debugging messages on:
Code: [Select]
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
prefixed with their string lengths so I could grab the longest one.

DEVPATH contains the chosen string.

WalkTheSysPath() asks udevadm for the properties of SEARCHPATH.
It's looking for a match on SUBSYSTEM == "usb" && DRIVER != ""
If it doesn't match, go up one level on SEARCHPATH and try again
This continues until a match is found, or fails if it reaches .../usb[0-9]

Once found, the bind path, bind files, and what to echo into them
are determined.

The new (and hopefully working) version is attached.

Offline GNUser

  • Wiki Author
  • Hero Member
  • *****
  • Posts: 1494
Re: how do firmware extensions work without startup script?
« Reply #36 on: September 05, 2024, 04:22:25 PM »
You're back and you mean business!

It works like a charm now:

Code: [Select]
$ 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)

Wow, you are definitely not one to settle for superficial/accidental solutions. This is impressively thorough.

I manage several machines from a distance, so I expect that over time I will find several applications for this script.

Many thanks, Rich.

Offline Rich

  • Administrator
  • Hero Member
  • *****
  • Posts: 11573
Re: how do firmware extensions work without startup script?
« Reply #37 on: September 05, 2024, 05:13:32 PM »
Hi GNUser
You're back and you mean business! ...
Some of us are stubborn and too dumb to know when to quit. :)

Quote
... It works like a charm now: ...
How about that, sometimes being persistent does pay off.

Quote
... 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
system with multiple USB devices with the same VID:PID on the off chance they
show up under /dev/.

Quote
... Many thanks, Rich.
You are quite welcome. Thank you for your patience. If you have
any problems or questions, let me know.

Offline Rich

  • Administrator
  • Hero Member
  • *****
  • Posts: 11573
Re: how do firmware extensions work without startup script?
« Reply #38 on: September 05, 2024, 05:29:19 PM »
Hi GNUser
I had another idea I'd like to investigate.

Could you please post the result of this command using your MAC address:
Code: [Select]
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:~$

Offline GNUser

  • Wiki Author
  • Hero Member
  • *****
  • Posts: 1494
Re: how do firmware extensions work without startup script?
« Reply #39 on: September 06, 2024, 09:32:44 AM »
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:
Code: [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

Offline Rich

  • Administrator
  • Hero Member
  • *****
  • Posts: 11573
Re: how do firmware extensions work without startup script?
« Reply #40 on: September 06, 2024, 09:48:30 AM »
Hi GNUser
...
Code: [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
Excellent. When I have a chance later tonight or tomorrow, I will
add MAC address as another choice. That will guarantee a way
to distinguish between multiple devices with identical VID:PID
identifiers.

Offline GNUser

  • Wiki Author
  • Hero Member
  • *****
  • Posts: 1494
Re: how do firmware extensions work without startup script?
« Reply #41 on: September 06, 2024, 10:08:37 AM »
Note that when I first tried this command, I got no output. I had copied and pasted the MAC address that  ifconfig  outputted, which uses capital letters. It seems the  address=  option to  udevadm  requires letters in the MAC address to be lowercase.

Offline Rich

  • Administrator
  • Hero Member
  • *****
  • Posts: 11573
Re: how do firmware extensions work without startup script?
« Reply #42 on: September 06, 2024, 11:55:43 AM »
Hi GNUser
Thanks for the heads up, but I'm afraid I already dealt
with that. If you look at where VIDPID gets processed
in  ParseCommand() , you'll find:
Code: [Select]
# Covert uppercase characters to lowercase.
VIDPID="$(echo "$ITEM" | busybox tr '[A-Z]' '[a-z]')"

In fact, it's even mentioned in the usage message:
Quote
/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.

I planned on giving the MAC address the same treatment.

Now that you got me thinking about it, I'm going to make
a  ToLower()  function since I'm using it twice. :)

Offline Rich

  • Administrator
  • Hero Member
  • *****
  • Posts: 11573
Re: how do firmware extensions work without startup script?
« Reply #43 on: September 06, 2024, 01:44:35 PM »
Hi GNUser
Version 0.2 is ready for testing:
Code: [Select]
#!/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

MAC address has been added as choice for specifying a device.
The help message has been updated to reflect that addition.

While the script found my network device from the (uppercase) MAC
address, it's not USB based so it quits after identifying  DEVPATH :
Code: [Select]
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$

Tag, you're it. ;D

Offline GNUser

  • Wiki Author
  • Hero Member
  • *****
  • Posts: 1494
Re: how do firmware extensions work without startup script?
« Reply #44 on: September 07, 2024, 04:22:34 PM »
Hi Rich. I wanted to get back to you sooner, but sometimes real life interferes with my linux addiction ;D

Version 0.2 does exactly what it says on the tin. At this point I can't think of any way of making this script better.

You remain a distinguished member of the wizards' guild.