Ethernet LEDs control for TP-Link TL-WR1043ND v2/v3

Long story short, I had to turn off all LEDs for a TP-Link TL-WR1043ND v3 model, but encountered two issues:

  • Newer versions of OpenWrt (ath79) don't support ethernet LEDs control through MDIO.
  • Older versions of OpenWrt (ar71xx) support it, but their LED control device files are only linked to one of three possible states.

Each LED has three states linked to the interface's speed (1000, 100 and 10 mbps). Each state has a two bits value (00 = off, 01 = blink, 10 = on, 11 = hardware controlled).

Older versions of OpenWrt only manage the 1000 mbps state.
When a device connected to one of the ethernet ports shutdown its network interface or power off, the state may change. In my case, it started reading the two bits value from the 10 mbps state, defaulted at 11 (hardware controlled), with the behavior of "on" if the network interface is down but the endpoint device is powered on, and "off" if the cable is disconnected or the endpoint device is powered off.
This utility makes sure to change all three values.

Current options are as follow:
off: Turn all ethernet LEDs off.
on: Turn all ethernet LEDs on.
blink: Set all ethernet LEDs to blinking.
default: Revert all ethernet LEDs to default (hardware controlled).

The options above affect every single ethernet LED as that's what my needs required, however, you can easily add new options by using the template macros to only affect specific LED(s).

Here is an example on how to set up a LAN1 toggle (on/off) option.

LEDS_ROUTINE_BEGIN(lan1_toggle)
    /* LAN1 values are stored in CTRL 3 */
    if (CTRL_INDEX != 3)
        CTRL_SKIP;

    /* LAN1 values are stored in the higher bits */
    /* bits [4, 10) */
    CTRL_HI_VALUE &= ~0x0150; /* 0x0150: 0b0000000101010000, zero first bits */
    CTRL_HI_VALUE ^= 0x02A0;  /* 0x02A0: 0b0000001010100000, toggle second bits */
LEDS_ROUTINE_END("Toggled LAN1 LED.")

OPTIONS_BEGIN
    ... (other options)
    OPTION(lan1_toggle)
OPTIONS_END

The pre-compiled binary file was linked against uClibc for the older versions of OpenWrt, therefore, you will probably have to compile the source code in order to use it on the newer versions.

  1. opkg update
  2. opkg install gcc (this model doesn't have enough disk space to install it, you will need to install it in the RAM or in a disk connected through the USB port)
  3. gcc -std=c89 -O2 -Wall -Wextra -Wpedantic -o eth-leds eth-leds.c

Related: /sys/class/leds/tp-link:green:lan1 missing on WR1043ND

Binary and Source Code: https://mega.nz/file/JY8TFbzA#FnYDH7_RPB1wxYFYTDSwivc329k_KeYuSroGgQHV8QI

This is broken on 23.05 because the code for ag71xx was not updated for later versions of kernel 5.15.

22.03 (5.10): https://elixir.bootlin.com/linux/v5.10/source/drivers/net/ethernet/atheros/ag71xx.c#L1844
23.05.04 (5.15.162): https://elixir.bootlin.com/linux/v5.15.162/source/drivers/net/ethernet/atheros/ag71xx.c#L1855

Comment hinting at the change:

 * int (*ndo_do_ioctl)(struct net_device *dev, struct ifreq *ifr, int cmd);
 *	Old-style ioctl entry point. This is used internally by the
 *	appletalk and ieee802154 subsystems but is no longer called by
 *	the device ioctl handler.

OpenWrt 23.05 still using ndo_do_ioctl for the ag71xx driver: https://github.com/openwrt/openwrt/blob/openwrt-23.05/target/linux/ath79/files/drivers/net/ethernet/atheros/ag71xx/ag71xx_main.c#L1505

Because ndo_eth_ioctl is NULL, the driver's ioctl handler isn't called anymore; ioctl calls fail and set errno to EOPNOTSUPP.
Any chance to get a contributor to patch this out?

Thank you for the report.

It would be better to report such problem in the issue tracker at https://github.com/openwrt/openwrt/issues or on the mailing list.

I created this PR to fix the problem and multiple similar ones in master and will backport this to 23.05 later:

2 Likes

Thank you. I haven't reported on github for the same reason I didn't report this one there aswell: Odhcp6c issue with my ISP's configuration
I don't have an account and I have reasons (which I'll not be discussing here) to not create one. For odhcp6c, the repository there seems abandoned for the time being and issues and prs go unanswered.
I still think it's better than not reporting at all, more so when I've already provided the cause and possible solution(s) to the aforementioned issues.

As for this issue in question, this is another case where Linux makes it very hard for you to love it. Breaking backwards compatibility for no good reason in a patch kernel version in an obscure commit. Even Microsoft tends to keep "stone age" code around to avoid breaking older, functional programs.

This is a purely cosmetic change intended to help readers find
their way through the implementation.

Too much time spent making their CoC "inclusive", but allowing random contributors to break backwards compatibility for cosmetic reasons, sigh.

I couldn't lzma the kernel's image to the same size as before (around 250 KB more), and I didn't feel like tweaking the compression or recompiling the kernel, and considering there seems to be a 2.4G AP issue at the moment, I'll probably wait this out.
kmod-mdio-netlink is working correctly at the moment, so mdio-tools is a good enough replacement for the time being (adding netlink support to my code requires way too much work, more than what I am willing to put).

The following commands have the same effect as eth-leds off (if someone else needs it):

mdio mdio.0 phy 24 raw 0 0
mdio mdio.0 phy 17 raw 8 0/0x3FFF
mdio mdio.0 phy 17 raw 9 0/0x3FFF
mdio mdio.0 phy 17 raw 10 0/0x3FFF
mdio mdio.0 phy 17 raw 11 0/0x3FFF
mdio mdio.0 phy 17 raw 12 0/0x3FFF
mdio mdio.0 phy 17 raw 13 0/0x3FFF
mdio mdio.0 phy 17 raw 14 0/0x00FF
mdio mdio.0 phy 17 raw 15 0/0xFC00

@hauke Hey, do you still need this (device ioctl) tested? I couldn't test before because this device was the sole AP providing 2.4 GHz to the household. It has now been delegated to a mere 5 ports switch and I can flash it again (and test this), if you still need someone to confirm it's working correctly.