Linksys MR8300, OpenWrt 22.03.0-rc4, USB port-powered storage devices not working

Thanks for the confirmation. I see that I actually own the EU variant (I am a German customer):

~ # skuapi -g model_sku
modelNumber = MR8300-EU

I think @Vladdrako is on to something, as the product "codename" (or whatever it is), is actually rogue:

~ # cat /etc/product
rogue

and is definitively referenced in the function start_usb:

service_start ()
{
   [ "started" = "`sysevent get ${SERVICE_NAME}-status`" ] && return
   SYSCFG_UsbPortCount=`syscfg get UsbPortCount`
   [ -z "$SYSCFG_UsbPortCount" ] && return
   for i in `seq 1 $SYSCFG_UsbPortCount`
   do
      start_usb_port $i
   done
   start_udevd_for_usb
   MODULE_PATH=/lib/modules/`uname -r`/
   lsmod | grep usbcore 2>&1 > /dev/null
   if [ $? -eq 1 ]; then
       [ -f $MODULE_PATH/usbcore.ko ] && insmod $MODULE_PATH/usbcore.ko
   fi
  lsmod | grep xhci_hcd 2>&1 > /dev/null
   if [ $? -eq 1 ]; then
       [ -f $MODULE_PATH/xhci-hcd.ko ] && insmod $MODULE_PATH/xhci-hcd.ko
   fi
   lsmod | grep ehci_hcd 2>&1 > /dev/null
   if [ $? -eq 1 ]; then
       [ -f $MODULE_PATH/ehci-hcd.ko ] && insmod $MODULE_PATH/ehci-hcd.ko
   fi
   lsmod | grep ohci_hcd 2>&1 > /dev/null
   if [ $? -eq 1 ]; then
       [ -f $MODULE_PATH/ohci-hcd.ko ] && insmod $MODULE_PATH/ohci-hcd.ko
   fi
   lsmod | grep uhci_hcd 2>&1 > /dev/null
   if [ $? -eq 1 ]; then
       [ -f $MODULE_PATH/uhci-hcd.ko ] && insmod $MODULE_PATH/uhci-hcd.ko
   fi
   lsmod | grep usb_libusual 2>&1 > /dev/null
   if [ $? -eq 1 ]; then
       [ -f $MODULE_PATH/usb-libusual.ko ] && insmod $MODULE_PATH/usb-libusual.ko
   fi
   sleep 30 
   MODEL_NAME=`syscfg get device::model_base`
   if [ -z "$MODEL_NAME" ] ; then
       MODEL_NAME=`syscfg get device::modelNumber`
       MODEL_NAME=${MODEL_NAME%-*}
   fi
   if [ "EA9200" = "$MODEL_NAME" ] ; then
                ulog usb service "here,enable usb port again" 
                echo "low" > /proc/bdutil/usbhub
   fi
   if [ "lion" = "`cat /etc/product`" -o "rogue" = "`cat /etc/product`" ]; then
           lion_usb_power_reset
   fi
   sysevent set ${SERVICE_NAME}-status started
   ulog usb service "$PID : udevadm trigger "   
   /sbin/udevadm trigger --subsystem-match=usb --attr-match=bInterfaceClass=07 --action=add
   /sbin/udevadm trigger --subsystem-match=usb --attr-match=bInterfaceClass=08 --action=add
   /sbin/udevadm trigger --subsystem-match=block --action=add
}

which then calls the function lion_usb_power_reset:

lion_usb_power_reset ()
{
        echo "===> RESET LION usb power" > /dev/console
        echo "68" > /sys/class/gpio/export  # gpio.68 (USB_PWR_EN)
        echo "out" >/sys/class/gpio/gpio68/direction
        echo "1" >/sys/class/gpio/gpio68/value  # pull High, then pull Low (it's active Low)
        sleep 2
        echo "0" >/sys/class/gpio/gpio68/value
}

But this is already done early at boot, not while mounting any USB drive, as far as I can judge (from the boot log),

Below just some random snippets from the boot log, which thought might be interesting:

[    1.301607] msm_serial_hsl_init: driver initialized
[    1.306565] uart_tx_gpio is not available
[    1.309993] uart_rx_gpio is not available
[    1.314023] uart_cts_gpio is not available
[    1.318063] uart_rfr_gpio is not available

[    2.545803] input: gpio_keys.9 as /devices/soc.1/gpio_keys.9/input/input0

I updated the firmware version to see if there are some relevant changes I could spot (spoiler: I couldn't)

Booting rogue (firmware version 1.1.10.210186)
 SKU is MR8300-EU
[utopia] Not setting ppp_clamp_mtu on dslite protocol
wan, sysevent received: phylink_wan_state
mosquitto aborting: not Master
smart connect client, generating client device data
Entry tsmb lan-status, 1
service_guardian.sh is called with lan-status
backhaul_switching status event system_state-normal received
wifi, wifi_physical_start(ath0)
===> RESET LION usb power
nss_build_bypass is called with ipv4_wan_ipaddr
Restarting nfqrecv service...
lldpd event ipv4_wan_ipaddr 0.0.0.0 received.
Auto channel
wifi, wifi_virtual_start(ath0)
Entry tsmb tsmb-stop, 1
/tmp/.ebt: line 3: ebtables: not found
/tmp/.ebt: line 4: ebtables: not found
/tmp/.ebt: line 5: ebtables: not found
wifi, wifi_user_start(ath0)
fastpath, sysevent received: qos_enabled
wifi_user, ath0 TxBF enabled
wifi, primary AP: ath0 is up (Sat Feb 11 13:47:42 PST 2023)
wifi, wifi_guest_start(ath0)
wifi, guest ath2 is disabled, do not start wifi guest
wifi, smart::mode is unconfigured do not start smart connect setup and config wifi
add_storage_drivers ===========
wifi, wifi_physical_start(ath1)

I will try to install the snapshot release now (after updating the Linksys firmware), but I doubt it'll make any difference.

okay, installed snapshot, but that didn't bring me any further. I tried to re-create what @Vladdrako did above with the GPIO PIN power and I found the following working solution:

# install tools
opkg install gpiod-tools gpioctl-sysfs
# figure out the base of the GPIO chip
cat /sys/class/gpio/gpiochip*/base | head -n1
412

# as the 68th PIN is referenced in Linksys' firmware with the power reset, add that to the base to determine the exact PIN: 412 + 68 = 480
# export the PIN 480
echo "480" > /sys/class/gpio/export

# set PIN direction
echo "out" > /sys/class/gpio/gpio480/direction
 
# set PIN to active_low
echo "1" > /sys/class/gpio/gpio480/active_low

# disable USB port (you should receive a disconnect of USB device of the kernel with that command - see logread or dmesg)
echo "0" > /sys/class/gpio/gpio480/value

# enable USB port (you should receive a connect of a connected USB device from the kernel - see logread or dmesg)
echo "1" > /sys/class/gpio/gpio480/value

# profit
mount /dev/sda1 /mnt/
[ 1162.319823] EXT4-fs (sda1): mounted filesystem with ordered data mode. Opts: (null). Quota mode: disabled.
root@OpenWrt:/# dd if=/dev/zero of=/mnt/test.file.10G bs=1M count=10000
10000+0 records in
10000+0 records out
root@OpenWrt:/# du -hs /mnt/test.file*
1000.0M /mnt/test.file
9.8G    /mnt/test.file.10G
root@OpenWrt:/#

This works obviously only temporarily until the system is rebooted. I guess one needs to change the DTS-definition so that the kernel is applying an active_low to pin #480 from the get-go. I have honestly know idea how to achieve that. It would be great if somebody who has some experience in working with DTS-definitions would be so glad to help me out here.
I am more than happy to try to changes that need to be applied, but I would need a helping hand here.

One question remains for me though: Why does it work for some people and for others it doesn't? There must be something different between those PCBs marketed for EU and the other variants (eg. CA as from @clauded)

2 Likes

:+1: That sounds very good! Finally a solution. :slightly_smiling_face:

A question from a novice. Do your findings mean that all what needs to be done to fix the MR8300's USB problem in OpenWrt is to implement the equivalent of the Linksys firmware's "lion_usb_power_reset ()" function into the OpenWrt firmware? Or is it purely that pin # 480 has to be assigned the property "active_low"?

to be perfectly honest: I don't know.
If the kernel can be instructed to apply my above GPIO settings on pin 68 when it starts up, that would be the solution, I think. I think when the kernel loads and applies those settings, it is early enough for extroot to work on a USB device, which is probably why most people are interested in the USB functionality (including myself).
These kind of settings are applied via a DTS definition, if I am not mistaken.
It would 'just' need someone that is capable of modifying the (probably existing) DTS definition for the MR8300 to apply the above settings.
What I am still worried about is whether it will break the USB implementation for users that have it working currently. My only guess is right now - as said - that there are differences in the PCBs across regions. But that all it is: a guess.

Would you mind letting us know the brands and product names of those 3 USB devices?

Kingston DataTraveler 3.0 64 Gb usb stick, Samsung T5 SSD and an old Kingston 16 Gb usb stick.

Just out of curiosity, I fiddled around with multiple USB devices, including a 2.5 5TB HDD (WD Elements). I even added a non-active USB Hub inbetween and plugged in a SanDisk 3.2 Gen USB stick along with the 2.5 5TB HDD and it worked without any issue whatsoever.

I measured the power draw of the USB hub with one of those USB testers and when both devices are plugged in - and especially during spinning up the disk on the 2.5 HDD - I could see a power draw of up to 0.7x A (0.71 - 0.78ish), which is IMHO perfectly fine.

So I don't think it has something to do which USB sticks/drives you are going to use, it is rather the above pin configuration that has to be applied.

I left one of my USB sticks plugged over night to see if there is any disconnect, but no, everything worked as expected. So I really think applying active_low on pin #480 is the solution here, which needs to be translated to the DTS definition file from someone cappable.

What I noticed, though, is that the speed of all USB devices is quite limited, as it will write with max 6 MB/s. I think the USB chip (?) on the MR8300 is not capable of handling more. But in all honesty, 6 MB/s should be more than enough for an extroot, I think. That is if I measured correctly and the bottleneck is not somewhere else (I used dd if=/dev/zero | pv | dd of=/mnt/1/test.file bs=1M count=2500)
Read speed where actually what I would have expected (~180 MiB according to pv -r /mnt/1/test.file | cat > /dev/null).

Hopefully someone capable of modifying the DTS definition sees this thread an chimes in! :slight_smile:
Again, I'd be more than happy in help testing the modified DTS definition, but I cannot modify the existing DTS definition, unfortunately.

Thanks! :slight_smile:

// edit
Okay, while reading from the USB HDD ( pv -r /mnt/2/big.file | cat > /dev/null), I get read speeds around ~80MiB, but the power draw is close to 0.9A! So IMHO this is definetively not a power draw issue at all :slight_smile:

// edit #2

I guess my testing was bad, as I did this all on serial and now switched to SSH. I don't know how this could have affected the testing, but copying a big file (not the same as above) from one USB device to another (both connected with the aforementioned non-active USB hub), gives me read and write speeds between 40-80 MiB, which translates to ~41,943 MB/s - 83,8861 MB/s. That should be perfectly fine.

I fiddled around a bit more and came up with the following workaround for a working extroot.
If you only want to e.g. store media files or so on a USB device, that you intend to share via DLNA etc, you can apply the pin configuration as shown below to your /etc/rc.local and afterwards ensure that your mount point (e.g. /media) is mounted or re-mounted properly and you can entirely skip remounting of everything else.

The following can be added to /etc/rc.local to remount / (root) after the system has almost finished booting (rc.local is one of the last things that is done during boot) and we have full control of it:

# Put your custom commands here that should be executed once
# the system init finished. By default this file does nothing.
# as the 68th pin is referenced in Linksys' firmware with the power reset, add that to the base to determine the exact pin: 412 + 68 = 480
# export the pin 480
logger -t "rc.local" "Exporting pin 480"
echo "480" > /sys/class/gpio/export

# set pin direction
logger -t "rc.local" "Setting pin direction"
echo "out" > /sys/class/gpio/gpio480/direction
 
# set pin to active_low
logger -t "rc.local" "Setting pin to active_low"
echo "1" > /sys/class/gpio/gpio480/active_low

# disable USB port (you should receive a disconnect of USB device of the kernel with that command - see logread or dmesg)
logger -t "rc.local" "Disabling USB"
echo "0" > /sys/class/gpio/gpio480/value

# sleep to ensure that kernel recognizes the change of the GPIO pin
logger -t "rc.local" "Sleeping 2 seconds to ensure kernel is recognizing the changed value of pin 480"
sleep 2

# enable USB port (you should receive a connect of a connected USB device from the kernel - see logread or dmesg)
logger -t "rc.local" "Enabling USB again"
echo "1" > /sys/class/gpio/gpio480/value

# give the USB device a bit of time to spin up
logger -t "rc.local" "Sleep 5 seconds to allow the kernel to load the modules and spin up any disks"
sleep 5

# unmount overlay
logger -t "rc.local" "Unmounting /overlay"
umount /overlay

# /tmp will get overridden by another tmpfs by mount_root, but we need the
# initial one because it contains the ubus named socket.
logger -t "rc.local" "Bind mounting /tmp to /mnt/tmp"
mkdir /mnt/tmp
mount --bind /tmp /mnt/tmp

# Re-run mount_root now that we have a block device that it will recognize
# as an extroot. This sleep is needed, otherwise procd seems to freak out
# and the watchdog timer doesn't get reset. Not sure exactly why.
logger -t "rc.local" "Sleeping 5 seconds to ensure mounting extroot works without any issues"
sleep 5
logger -t "rc.local" "Remounting root as extroot"
PREINIT=1 mount_root

# Free the new tmpfs just created by mount_root. Since it will never be used,
# its just wasting memory.
logger -t "rc.local" "Unmounting /tmp"
umount -l /tmp

# Put the original tmpfs back to where it was in the VFS, primarily so that
# programs can find the ubus socket.
logger -t "rc.local" "Bind mounting /rom/mnt/tmp to /tmp"
mount --bind /rom/mnt/tmp /tmp

# Need to re-run this too for some reason, otherwise some other mounts are not
# mounted after mount_root, eg. /rwm.
logger -t "rc.local" "Triggering mounting of all partitions again"
block mount

# Reload rpcd to register rpc objects on the extroot
logger -t "rc.local" "Reloading rpcd"
if [ -e "/etc/init.d/rpcd" ]; then
  /etc/init.d/rpcd restart
fi

# unmount temporary mounts
logger -t "rc.local" "Unmounting not needed mount point /rom/rom"
umount /rom/rom
logger -t "rc.local" "Unmounting temporary mount point /rom/mnt/tmp"
umount /rom/mnt/tmp

Be aware, this is me simply fiddling around, it is NOT meant as production-use workaround; It is merely a proof of concept.
I have validated that it works currently on snapshot, but will try to validate on 22.03 and 21.02 during the upcoming week.

Still, I think this should only be used as a temporary workaround until a proper fix is in place, but this workaround proves at least that changing the pin state seems to be working.

Please be mindful that booting takes a bit more time as OpenWrt will try to mount the extroot during boot and will fail, as the device continously disconnects (as the pin has not the proper value, yet).

1 Like

1- How can I check my actual pin config?
2- If I have the same pin config as your original pin config but no problem on my router, do you have an explanation?

So, I wanted to know what is actually happening to the USB power when performing the gpio commands and also the behavior of the USB drive. Therefore hooked up my little oscilloscope to the USB 5V line of the MR8300's USB port and plugged in one of my USB3 thumb drives. My idea was to do one step at a time and verify the results.

Interestingly I've noticed the following.

I started the MR8300 and inserted the thumb drive. As expected I saw the usual power dropouts on the oscilloscope and OpenWrt's logfile showed the ethernal loop of "new SuperSpeed Gen 1 USB device ..." and "USB disconnected ...".
Pulled the thumb drive out.

Then I executed the first line "echo "480" > /sys/class/gpio/export" and inserted the thumb drive again. To my surprise the oscilloscope didn't show the slightest hint of a voltage drop and the OpenWrt log only showed one successful discovery of the USB thumb drive. I was then able to copy files to and from the thumb drive without problem.

To make sure this wasn't just a fluke I tested this a few more times (restarted the MR8300 in between) and also with another USB drive. The results were consistent.

I guess it makes sense to also perform the "power off" and "power on" commands if the USB drive isn't manually removed and re-inserted.

But could you maybe test again if you really do need to invert the low/high logic?
After all, according to the function from the script made by Linksys even in the OEM firmware "echo "1" >/sys/class/gpio/gpio68/value" switches the USB power off and "0" switches it back on.

In case you were to get similar results as I, then maybe we should backtrack on changing the logic of the power on/off via the "echo "1" > /sys/class/gpio/gpio480/active_low" command.

@clauded
To your first question:
grep gpio68 /sys/kernel/debug/gpio

For me it is active, low:
gpio68 : out low func0 2mA pull down, check for pin 68

To your second question:
IMHO there must be different PCBs out there. Clearly it is working for you perfectly well, so my guess is that there is a different PCB for the EU market as for the Canadian market (and other markets as well). But that's just a guess.

To your first question:
grep gpio68 /sys/kernel/debug/gpio

grep: /sys/kernel/debug/gpio: No such file or directory

you need to first install the tools - forgot to mention

# install tools
opkg install gpiod-tools gpioctl-sysfs

I think you really only need gpioctl-sysfs

You have a good point there.
I'll verify tomorrow with my unit (it's getting late for me) and post an answer here.

Thanks for helping out, really appreciate it!

Also a reboot is needed...

So my pin is set as: gpio68 : out low func1 2mA pull down

I have verified it on my end now, and I need to actively set the pin to active_low otherwise it will simply disconnect the USB device again.

I suppose the .dts should contain something like this (untested!):

keys {
	[...]
	};

gpio_export {
		compatible = "gpio-export";
 		usb_power_pins {
			gpio-export,name = "usb_power_pins";
			gpio-export,output = <0>;
			gpios = <&tlmm 68 GPIO_ACTIVE_LOW>;
		};
	};

In a modern way a startup script that uses gpioset utility will be needed, where active_low could be specified on the command line as -l or --active-low

Specifying a usb-power node in the xx8300.dtsi could work. I think you can almost copy-paste from the Fritzbox 7530.dts.

Not exactly. For example, if my memory serves me well, we do not need gpio-hog; as that will prevent the pin state manipulation from the userspace.
Here is a better example: https://github.com/openwrt/openwrt/blob/v22.03.3/target/linux/ipq40xx/files/arch/arm/boot/dts/qcom-ipq4019-oap100.dts#L143