It has been several months since I updated my main router to AX88U. It is undoubtedly an excellent router and I’m quite satisfied with its features.

However, the lack of integrated 2.5GbE port unfortunately shadows its power and ability, not to mention that its processor BCM49408 does natively support 2.5GbE1. To make it worse, recently released AX86U comes with a similar processor BCM4908 and, yeah, has a built-in 2.5GbE port.

The good news is that there is an external solution from Realtek: RTL81562, a USB 3.0 based 2.5GbE controller. Comparing to much expensive 5 or 10GbE equipments, it is fairly “cheap”: you can easily find a RLT8156 based USB dongle for about $30.

But do note that there are two versions of this chip: RTL8156 and RTL8156B. You should look for the B revision (RTL8156B), as the original RTL8156 has a weird stability issue in long time tests3.

In addition, if you bought a USB Type-C dongle, you may also need an “active” or “double side” USB Type-C to USB-A adapter (which usually include a multiplexer chip, e.g. VL1604). Otherwise, you have to figure out which side of the USB Type-C port to insert, since one side will work at the speed of USB 3.0 and the other side will do USB 2.0 only.

No Plug-and-Play?

If you directly insert the USB dongle into AX88U, there is no problem to get it recognized (on Network Map page in router’s web management console). Also in ifconfig -a, you can find that the dongle has been registered as usb0.

However, if you try to bring it up with ifconfig usb0 up, the dmesg will be spammed by cdc_ncm:

cdc_ncm 2-2:1.0 usb0: network connection: connected
cdc_ncm 2-2:1.0 usb0: 2500 mbit/s downlink 2500 mbit/s uplink
cdc_ncm 2-2:1.0 usb0: network connection: connected
cdc_ncm 2-2:1.0 usb0: 2500 mbit/s downlink 2500 mbit/s uplink
cdc_ncm 2-2:1.0 usb0: network connection: connected
cdc_ncm 2-2:1.0 usb0: 2500 mbit/s downlink 2500 mbit/s uplink
cdc_ncm 2-2:1.0 usb0: network connection: connected
cdc_ncm 2-2:1.0 usb0: 2500 mbit/s downlink 2500 mbit/s uplink

The reason is that the built-in driver in Linux kernel has no support for RTL8156 yet5. So we have to build the driver on our own.

Preparation

Compile Linux Kernel

Follow this guide for setting up the compiling environment for Asuswrt-Merlin. Though it is titled for WSL (Windows Subsystem for Linux) 2, you can still follow these steps under WSL 1 (legacy WSL) or pure Linux.

If you’re using legacy WSL like me, you have to follow this comment to run 32-bit applications in legacy WSL, because the provided toolchains are 32-bit.

As we don’t really need the git history, we can just clone the HEAD:

# Clone the HEAD only
git clone --depth 1 https://github.com/RMerl/asuswrt-merlin.ng

Also note that instead of linking brcm-arm-sdk folder under src-rt-6.x.4708:

# `src-rt-6.x.4708` is for AC56U, AC68U and AC87U
ln -s ~/am-toolchains/brcm-arm-sdk ~/asuswrt-merlin.ng/release/src-rt-6.x.4708/toolchains

… in the guide, we link it under src-rt-5.02axhnd:

# `src-rt-5.02axhnd` is for AX88U, AX58U and AX56U
ln -s ~/am-toolchains/brcm-arm-sdk ~/asuswrt-merlin.ng/release/src-rt-5.02axhnd/toolchains

… since we’re targeting AX88U, according to Merlin’s build script6.

Once you got the environment done, run this to build the firmware for AX88U:

# Targeting AX88U
cd release/src-rt-5.02axhnd && make rt-ax88u

It is not necessary to complete the whole firmware compiling, as we only need the generated header files (.h) from the part of compiling Linux kernel.

Therefore once the kernel building starts for a while, you may interrupt the build process by pressing CTRL+C.

Compile Tools

iperf3

Source code for iperf3 can be found at here. At the time of writing, the latest version of iperf3 is 3.9.

Run the following lines to compile iperf3 for AX88U:

# Create `build` folder as installation prefix
mkdir build
# We need the static-linked binary
./configure --host=arm-linux --prefix=`pwd`/build --enable-static --disable-shared
# Build the binary and install it into `build` folder
make && make install

The iperf3 binary should be generated in ./build/bin/iperf3. Or you can get the pre-compiled one in section Appendix: Pre-compiled Binaries: Pre-compiled Tools.

For the iperf3 running on Windows, please check this post.

ethtool

Source code for ethtool can be found at here. At the time of writing, the latest version of ethtool is 5.8.

Run the following lines to compile ethtool for AX88U:

# Create `build` folder as installation prefix
mkdir build
# Disable netlink, as we don't need it at all in our case.
# This also helps eliminate the dependency on `libmnl`
./configure --host=arm-linux --prefix=`pwd`/build --disable-netlink
# Build the binary and install it into `build` folder
make & make install

The ethtool binary should be generated in ./build/bin/ethtool. Or you can get the pre-compiled one in section Appendix: Pre-compiled Binaries: Pre-compiled Tools.

Compile r8152 Driver

It might be confusing at the beginning, but yes, the driver for RTL8156B is called r8152. The source code can be downloaded from here. At the time of writing, the r8152 driver version is v2.13.0.

I added a line to function rtl8152_get_drvinfo in r8152.c, so we can verify the chip version is RTL8156B via ethtool:

--- source-2.13.0/r8152-original.c     2020-04-20 01:36:49.000000000 -0700
+++ source-2.13.0/r8152-modified.c     2020-10-06 23:47:07.123659400 -0700
@@ -16369,6 +16369,7 @@
        strlcpy(info->driver, MODULENAME, sizeof(info->driver));
        strlcpy(info->version, DRIVER_VERSION, sizeof(info->version));
        usb_make_path(tp->udev, info->bus_info, sizeof(info->bus_info));
+       snprintf(info->fw_version, sizeof(info->fw_version), "0x%04x", tp->version);
 } 

 #if LINUX_VERSION_CODE < KERNEL_VERSION(4,20,0)

Since we’re targeting AX88U, we need to call aarch64-linux cross-compiler and set the architecture to arm64:

# Architecture: arm64
# Cross-compiler: aarch64-linux
# Kernel source code directory: ~/asuswrt-merlin.ng/release/src-rt-5.02axhnd/kernel/linux-4.1
make ARCH=arm64 CROSS_COMPILE=aarch64-linux- -C ~/asuswrt-merlin.ng/release/src-rt-5.02axhnd/kernel/linux-4.1 M=`pwd` modules

If you prefer using Makefile instead, it can be found in section Appendix: Scripts: Makefile.

If everything goes well, the r8152.ko should be generated in the working directory. You can also get the pre-compiled one in section Appendix: Pre-compiled Binaries: Pre-compiled Drivers.

Installation

Upload r8152.ko into /jffs/drivers/, iperf3 and ethtool into /jffs/tools/. Remember to create drivers and tools folders in /jffs first.

Load Driver

As we discussed in section No Plug-and-Play?, cdc_ncm will try to take control of the USB dongle, so we need to unload it first.

We need to check which modules depends on cdc_ncm with lsmod | grep cdc_ncm:

# admin@ax88u:/# lsmod | grep cdc_ncm
cdc_ncm                16787  1 cdc_mbim
usbnet                 21074  7 cdc_mbim,qmi_wwan,cdc_ncm,rndis_host,cdc_ether,ax88179_178a,asix
usbcore               166572 23 uas,usb_storage,cdc_mbim,qmi_wwan,cdc_wdm,cdc_ncm,rndis_host,cdc_ether,ax88179_178a,asix,cdc_acm,usbnet,usblp,ohci_pci,ohci_platform,ohci_hcd,ehci_pci,ehci_platform,ehci_hcd,xhci_pci,xhci_plat_hcd,xhci_hcd

So we have to unload cdc_mbim first and cdc_ncm later:

# Unload `cdc_mbim`, as it depends on `cdc_ncm`
rmmod cdc_mbim
# Unload `cdc_ncm`
rmmod cdc_ncm

Now we can load r8152.ko and also load cdc_ncm and cdc_mbim back:

# Load `r8152` driver
insmod /jffs/drivers/r8152.ko
# Load `cdc_ncm`
insmod cdc_ncm
# Load `cdc_mbim`
insmod cdc_mbim

If everything goes well, system log (tail /tmp/syslog.log) should reflect this:

# admin@ax88u:/# tail /tmp/syslog.log
Oct  7 21:27:42 kernel: usbcore: deregistering interface driver cdc_mbim
Oct  7 21:27:42 kernel: usbcore: registered new interface driver r8152
Oct  7 21:27:42 kernel: usb 2-2: reset SuperSpeed USB device number 2 using xhci-hcd
Oct  7 21:27:42 kernel: netif_napi_add() called with weight 256 on device eth%d
Oct  7 21:27:42 kernel: r8152 2-2:1.0 eth8: v2.13.0 (2020/04/20)
Oct  7 21:27:42 kernel: r8152 2-2:1.0 eth8: This product is covered by one or more of the following patents:
Oct  7 21:27:42 kernel: 		US6,570,884, US6,115,776, and US6,327,625.
Oct  7 21:27:42 hotplug: add net eth8.
Oct  7 21:27:42 kernel: usbcore: registered new interface driver cdc_ncm
Oct  7 21:27:42 kernel: usbcore: registered new interface driver cdc_mbim

As you can see, the adapter is registered as eth8, thus if we run ifconfig eth8:

eth8      Link encap:Ethernet  HWaddr 00:E0:4C:00:FA:KE
          inet6 addr: fe80::2e0:4cff:fe00:fake/64 Scope:Link
          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)

Also if you check the Network Map page in Web management UI, the adapter should show up there:

Adapter shown in Network Map page

Then we can update nvram variables, add eth8 to br0 and bring eth8 up:

# Update nvram variables
nvram set br0_ifnames="$(nvram get br0_ifnames) eth8"
nvram set lan_ifnames="$(nvram get lan_ifnames) eth8"
nvram set wired_ifnames="$(nvram get wired_ifnames) eth8"

# Add eth8 into br0 and bring it up
brctl addif br0 eth8
ifconfig eth8 allmulti up

Finally we can connect the ethernet cable to the USB dongle.

Run /jffs/tools/ethtool eth8 to verify the driver is correctly loaded and the negotiated link speed is 2500Mb/s full-duplex:

# admin@ax88u:/# /jffs/tools/ethtool eth8
Settings for eth8:
        Supported ports: [ MII ]
        Supported link modes:   10baseT/Half 10baseT/Full
                                100baseT/Half 100baseT/Full
                                1000baseT/Full
                                2500baseX/Full
        Supported pause frame use: No
        Supports auto-negotiation: Yes
        Supported FEC modes: Not reported
        Advertised link modes:  10baseT/Half 10baseT/Full
                                100baseT/Half 100baseT/Full
                                1000baseT/Full
                                2500baseX/Full
        Advertised pause frame use: Symmetric Receive-only
        Advertised auto-negotiation: Yes
        Advertised FEC modes: Not reported
        Link partner advertised link modes:  10baseT/Half 10baseT/Full
                                             100baseT/Half 100baseT/Full
                                             1000baseT/Full
        Link partner advertised pause frame use: No
        Link partner advertised auto-negotiation: Yes
        Link partner advertised FEC modes: Not reported
        Speed: 2500Mb/s
        Duplex: Full
        Port: MII
        PHYAD: 32
        Transceiver: internal
        Auto-negotiation: on
        Supports Wake-on: pumbg
        Wake-on: g
        Current message level: 0x00007fff (32767)
                               drv probe link timer ifdown ifup rx_err tx_err tx_queued intr tx_done rx_status pktdata hw wol
        Link detected: yes

Tip: Complete scripts for RTL8156B can be found in Appendix: Scripts: Scripts for RTL8156B.

Verify RTL Version

To tell if the dongle is really RTL8156B, you can run /jffs/tools/ethtool --driver eth8:

# admin@ax88u:/# /jffs/tools/ethtool --driver eth8
driver: r8152
version: v2.13.0 (2020/04/20)
firmware-version: 0x000e
expansion-rom-version:
bus-info: usb-xhci-hcd.0-2
supports-statistics: yes
supports-test: no
supports-eeprom-access: no
supports-register-dump: no
supports-priv-flags: no
  • If firmware-version is either 0x000b or 0x000c, the internal chip is RTL8156.
  • If firmware-version is either 0x000d or 0x000e, the internal chip is RTL8156B.

As you can see, mine firmware-version is 0x000e thus it is RTL8156B.

Note: In order to show the firmware-version, the added line to function rtl8152_get_drvinfo in r8152.c is necessary. Check deatils about it in section Preparation: Compile r8152 driver.

Performance Tuning

Complete scripts for RTL8156B can be found in Appendix: Scripts: Scripts for RTL8156B.

Receive (RX)

By default, hardware interrupts for xhci-hcd:usb1 on AX88U is only handled by CPU core 4 (cat /proc/irq/28/smp_affinity_list).

However, as RTL8156B controller only has 1 RX queue, we can’t spread those hardware interrupts to other cores via changing smp_affinity_list.

Thus we have to enable RPS (Receive Packet Steering). RPS helps to distribute software interrupts (softirq) to multiple cores. You can check here for more details about RPS.

To enable RPS on eth8:

# Enable RPS (Receive Packet Steering) on eth8
# Since AX88U has four cores, so we set it to 'f'
echo f > /sys/class/net/eth8/queues/rx-0/rps_cpus

At least in my test, enabling RPS alone allows the adapter to reach its maxmium performance in iperf3.

But if you are looking for more RX tweaks, here are some examples:

  • Increase net.core.netdev_max_backlog. The default value is 1000 and you may increase it to 2500 via:
    # 1000 is the default for 1GbE, thus 2500 seems reasonable for 2.5GbE
    echo 2500 > /proc/sys/net/core/netdev_max_backlog
    
  • Increase RX interrupt coalescing (to decrase # interrupts). The defuault rx-usecs is 15 and you may change it via:
    # <N> is the microseconds to delay an RX interrupt after packet arrival
    /jffs/tool/ethtool -C eth8 rx-usecs <N>
    
  • Increase RX ring buffer size. The default value is 100 and you may change it to 4096 (which is the maxmium):
    # 4096 is the maxmium supported value for RX ring buffer
    /jffs/tool/ethtool -G eth8 rx 4096
    

More information can be found here, here and here.

Transmit (TX)

Unlike RPS, XPS (Transmit Packet Steering) will not help increase performance in our case, because

For a network device with a single transmission queue, XPS configuration has no effect, since there is no choice in this case.

Linux Kernel Documentation — Scaling in the Linux Networking Stack

… and RTL8156B has only 1 TX queue.

But we can increase txqueuelen to 2500 on eth8 anyway:

# 1000 is the default for 1GbE, thus 2500 seems reasonable for 2.5GbE
ifconfig eth8 txqueuelen 2500

Somehow Realtek didn’t enable USB scatter/gather for kernel version below 5.2.3 (AX88U’s kernel version is 4.1.51), according to r8152.c:

#if LINUX_VERSION_CODE >= KERNEL_VERSION(5,2,3)
    if (usb_device_no_sg_constraint(udev))
        tp->sg_use = true;
#endif /* LINUX_VERSION_CODE >= KERNEL_VERSION(5,2,3) */

… and without USB scatter/gather enabled, the TX performance is really hurt.

Of course you can modify the driver, but I found a way to enable it externally:

# Enable USB scatter/gather on eth8
echo enable > /sys/class/net/eth8/rtl_adv/sg_en

Note: This option only works for r8152 driver supported RTL chips.

Performance Evaluation

iperf3

AX88U is the iperf3 server and my Windows PC is the client.

PC to AX88U, achieving ~2.35Gbits/sec:

PS E:\Tools\iperf-3.9-win64> .\iperf3.exe -c 192.168.50.1
Connecting to host 192.168.50.1, port 5201
[  5] local 192.168.50.X port 13610 connected to 192.168.50.1 port 5201
[ ID] Interval           Transfer     Bitrate
[  5]   0.00-1.00   sec   280 MBytes  2.35 Gbits/sec
[  5]   1.00-2.00   sec   279 MBytes  2.34 Gbits/sec
[  5]   2.00-3.00   sec   279 MBytes  2.34 Gbits/sec
[  5]   3.00-4.00   sec   279 MBytes  2.34 Gbits/sec
[  5]   4.00-5.00   sec   279 MBytes  2.34 Gbits/sec
[  5]   5.00-6.00   sec   279 MBytes  2.34 Gbits/sec
[  5]   6.00-7.00   sec   279 MBytes  2.34 Gbits/sec
[  5]   7.00-8.00   sec   279 MBytes  2.34 Gbits/sec
[  5]   8.00-9.00   sec   279 MBytes  2.34 Gbits/sec
[  5]   9.00-10.00  sec   279 MBytes  2.34 Gbits/sec
- - - - - - - - - - - - - - - - - - - - - - - - -
[ ID] Interval           Transfer     Bitrate
[  5]   0.00-10.00  sec  2.73 GBytes  2.34 Gbits/sec              sender
[  5]   0.00-10.04  sec  2.73 GBytes  2.33 Gbits/sec              receiver

iperf Done.

AX88U to PC, achieving ~2.38Gbits/sec:

PS E:\Tools\iperf-3.9-win64> .\iperf3.exe -c 192.168.50.1 -R
Connecting to host 192.168.50.1, port 5201
Reverse mode, remote host 192.168.50.1 is sending
[  5] local 192.168.50.X port 13619 connected to 192.168.50.1 port 5201
[ ID] Interval           Transfer     Bitrate
[  5]   0.00-1.00   sec   284 MBytes  2.38 Gbits/sec
[  5]   1.00-2.00   sec   283 MBytes  2.37 Gbits/sec
[  5]   2.00-3.00   sec   283 MBytes  2.37 Gbits/sec
[  5]   3.00-4.00   sec   283 MBytes  2.37 Gbits/sec
[  5]   4.00-5.00   sec   283 MBytes  2.37 Gbits/sec
[  5]   5.00-6.00   sec   283 MBytes  2.37 Gbits/sec
[  5]   6.00-7.00   sec   283 MBytes  2.37 Gbits/sec
[  5]   7.00-8.00   sec   283 MBytes  2.37 Gbits/sec
[  5]   8.00-9.00   sec   282 MBytes  2.36 Gbits/sec
[  5]   9.00-10.00  sec   283 MBytes  2.37 Gbits/sec
- - - - - - - - - - - - - - - - - - - - - - - - -
[ ID] Interval           Transfer     Bitrate         Retr
[  5]   0.00-10.03  sec  2.77 GBytes  2.37 Gbits/sec    0         sender
[  5]   0.00-10.00  sec  2.76 GBytes  2.37 Gbits/sec              receiver

iperf Done.

SMB File Transfer

Here we test a real-world application: SMB file transfer.

My Windows PC acts the SMB server, while my Surface Pro (connected via wire with USB 1GbE adapter) and my Macbook Pro (connected via 802.11ac WiFi, negotiated link speed: 1300Mbps) are trying to copy a 20GB file simultaneously.

Consider the overhead of SMB protocol and the relatively less powerful BCM49408 comparing to x86 CPUs, I’d say the result below is fairly good by the speed reaching ~195.7MB/s and averaging at around 177.3MB/s.

SMB file transfer speed test result

Tip: To quickly create a dummy file of 20GB on Windows: fsutil file createNew 20GB.txt 21474836480. The last parameter is the specified file size in bytes.

What about 5GbE?

At the time of writing, the only USB solution for 5GbE is Aquantia’s (acquired by Marvell7) AQC111U. Like I said before, 5GbE is not cheap: the cheapest 5GbE adapter I found costs $60.

Since AQC111U is a USB 3.1 Gen 1 based solution, even with 9K jumbo frame enabled, theoretically the maxmium speed you can reach is about 3.96 Gbits/sec considering the overhead from both USB8 and Ethernet9.

Not to mention that BCM49408 seems not able to handle this speed: after applied all the tweaks described in section Performance Tuning, I can only get ~3.35 Gbits/sec from AX88U to PC, and ~1.75 Gbits/secs from PC to AX88U in iperf3 test.

There are also some other issues:

  • AQC111U driver seems unstable. Somehow my AX88U got kernel panic and suddenly rebooted after running iperf3 for a short while.
  • Connection lost after a while. It becomes very hot during transmission and will eventually lose ethernet connection. Not sure if the latter is caused by AQC111U or the thermal design of the particular adapter I have.

Compile aqc111 Driver

If you still want to give it a try, you can download the driver code here.

Choose Linux Kernel 3.10 and Higher in the PLATFORM droplist and Marvell AQtion USB 3.1 Linux Driver is the one to look for.

At the time of writing, the least driver version is v1.3.3.0.

Since we’re targeting AX88U, we need to call aarch64-linux cross-compiler and set the architecture to arm64:

# Architecture: arm64
# Cross-compiler: aarch64-linux
# Kernel source code directory: ~/asuswrt-merlin.ng/release/src-rt-5.02axhnd/kernel/linux-4.1
make ARCH=arm64 CROSS_COMPILE=aarch64-linux- -C ~/asuswrt-merlin.ng/release/src-rt-5.02axhnd/kernel/linux-4.1 SUBDIRS=`pwd` modules

If you prefer using Makefile instead, it can be found in section Appendix: Scripts: Makefile.

If everything goes well, the aqc111.ko should be generated in the working directory. You can also get the pre-compiled one in section Appendix: Pre-compiled Binaries: Pre-compiled Drivers.

Installation & Performance Tuning

First upload aqc111.ko into /jffs/drivers/. Remember to create drivers folder under /jffs if not exist.

The installation is a little bit different from RTL8156B. Instead of cdc_ncm, we need to unload cdc_ether:

# Unload `rndis_host`, as it depends on `cdc_ether`
rmmod rndis_host
# Unload `cdc_ether`
rmmod cdc_ether
# Load `aqc111.ko`
insmod /jffs/drivers/aqc111.ko
# Load `cdc_ether` back
insmod cdc_ether
# Load `rndis_host` back
insmod rndis_host

Tuning performance for AQC111U is similar to RTL8156B. However you can’t adjust interrupt coalescing or ring buffer size, since aqc111 driver doesn’t support them. You can refer to section Performance Tuning for more details.

Tip: Complete scripts for AQC111U can be found in Appendix: Scripts: Scripts for AQC111U.

Conclusion

Actually early this year, I’ve already started to try to bring 2.5GbE to AX88U. At that time, however, there was only the original RTL8156 chip and I couldn’t make it reach the maximum performance in iperf3 test.

Finally several months later, Realtek fixed the problem and released the RTL8156B revision. I’m so happy to see that I finally got the next generation of network and made my router future-proof.

Another good news is that QNAP recently announced an affordable ($100) 5-port 2.5GbE switch QNAP QSW-1105-5T10, which is passive-cooled and small in size - pretty suitable for home networking.

I hope you enjoy this post and upgrade your network to the next generation :)

Appendix: Pre-compiled Binaries

Pre-compiled Drivers

Pre-compiled drivers for AX88U (store them into /jffs/drivers/):

  • r8152 driver v2.13.0: r8152.ko. SHA1: e3a9f7a868baae6756bde4665a45852fcd578bdd
  • aqc111 driver v1.3.3.0: aqc111.ko. SHA1: 9dc4768154e065d19a274dcc33235a6b58fd9fb9

Pre-compiled Tools

Pre-compiled tools for AX88U (store them into /jffs/tools):

  • ethtool 5.8: ethtool. SHA1: 6936931589476b2001667c50a8f91d713be705fb
  • iperf3 3.9: iperf3. SHA1: ec19717ecb56a5b5bbfeda3316cd7e712cb6bde0

Appendix: Scripts

Makefile

Here are the Makefiles for drivers targeting AX88U.

  • Makefile for r8152: r8152/Makefile. SHA1: dc22346bdae40f5ea5bb2a0c193dc922fa3e4b60
  • Makefile for aqc111: acq111/Makefile. SHA1: 37dfc73f2e4b6676e5dd712ad38b389f0140fb76

Scripts for RTL8156B

/jffs/scripts/init-start

#!/bin/sh

# Make sure the script is indeed invoked
touch /tmp/001-init-start
logger -t "rtl8156" "init-start: loading RTL8156 driver..."

# It's safe to `insmod` here, since `usbcore` has already been 
# installed before `init-start` was called. 
# init.c: sysinit() -> init_wl() -> 
#         init-broadcom.c: eval("insmod", "usbcore");
insmod /jffs/drivers/r8152.ko

# Note `insmod r8152.ko` must be executed before `cdc_ncm` module 
# is loaded into kernel. Otherwise `cdc_ncm` will take control 
# of the USB 2.5GbE adapter.
# If `r8152.ko` can't be loaded before `cdc_ncm`, then you should 
# `rmmod cdc_ncm`,` insmod r8152.ko` and `insmod cdc_ncm`
# (remember to rm/insmod modules depending on `cdc_ncm` first)

logger -t "rtl8156" "init-start: all done"
date >> /tmp/001-init-start

/jffs/scripts/services-start

#!/bin/sh

# Make sure the script is indeed invoked
touch /tmp/001-services-start
logger -t "rtl8156" "services-start: setting up eth8..."

# Update nvram variables
nvram set br0_ifnames="$(nvram get br0_ifnames) eth8"
nvram set lan_ifnames="$(nvram get lan_ifnames) eth8"
nvram set wired_ifnames="$(nvram get wired_ifnames) eth8"

# Add eth8 into br0
brctl addif br0 eth8

# Set TX queue length to 2500 on eth8 and bring it up
ifconfig eth8 txqueuelen 2500 allmulti up

# Enable USB scatter/gather on eth8
echo enable > /sys/class/net/eth8/rtl_adv/sg_en

# Enable RPS (Receive Packet Steering) on eth8.
# Since AX88U has four cores, we set it to 'f'
echo f > /sys/class/net/eth8/queues/rx-0/rps_cpus

logger -t "rtl8156" "services-start: all done"
date >> /tmp/001-services-start

Scripts for AQC111U

Though AQC111U works on AX88U, its maximum performance is unachievable.

Read section What about 5GbE? for more details.

/jffs/scripts/services-start

#!/bin/sh

# Make sure the script is indeed invoked
touch /tmp/001-services-start
logger -t "aqc111" "services-start: loading AQC111 driver..."

# No need to `rmmod cdc_ether` here, as `cdc_ncm` will be loaded 
# after `services-start` is called
insmod /jffs/drivers/aqc111.ko

# Note `insmod aqc111.ko` must be executed before `cdc_ether` 
# module is loaded into kernel. Otherwise `cdc_ether` will take 
# control of the USB 5GbE adapter.
# If `aqc111.ko` can't be loaded before `cdc_ether`, then you should 
# `rmmod cdc_ether`, `insmod aqc111.ko` and `insmod cdc_ether` 
# (remember to rm/insmod modules depending on cdc_ether first)
logger -t "aqc111" "services-start: AQC111 driver loaded"

# Now we can setup the new interface
logger -t "aqc111" "services-start: setting up eth8..."

# Update nvram variables
nvram set br0_ifnames="$(nvram get br0_ifnames) eth8"
nvram set lan_ifnames="$(nvram get lan_ifnames) eth8"
nvram set wired_ifnames="$(nvram get wired_ifnames) eth8"

# Add eth8 into br0 and bring eth8 up
brctl addif br0 eth8
ifconfig eth8 allmulti up

# Enable RPS (Receive Packet Steering) on eth8.
# Since AX88U has four cores, we set it to 'f'.
echo f > /sys/class/net/eth8/queues/rx-0/rps_cpus

logger -t "aqc111" "services-start: all done"
date >> /tmp/001-services-start

References