Cudy LT700 Outdoor v1 is an outdoor 4G LTE Wi-Fi 5 router (Cudy board
ID R43).
Hardware:
- SoC: MediaTek MT7621AT
- Flash: 16 MiB SPI NOR (XM25QH128C)
- RAM: 128 MiB DDR3
- WLAN: 2.4 GHz (MediaTek MT7603E, 11n), 5 GHz (MediaTek MT7663, 11ac)
- Ethernet: 1x 10/100/1000 Mbps LAN
- Power: 48-57 V passive PoE
- Buttons: 1x Reset button
- LEDs: 8x Green
- LTE: internal USB modem Quectel EG060W (Cat 6)
- Serial console: 3.3V, 115200 8N1, unpopulated 4-pin header left of the
SoC; pinout from top: VCC, GND, TX, RX
MAC addresses:
+---------+-------------------+-----------+
| | MAC | Algorithm |
+---------+-------------------+-----------+
| LAN | d4:0d:ab:xx:xx:xx | label |
| WLAN 2g | d4:0d:ab:xx:xx:xx | label |
| WLAN 5g | d4:0d:ab:xx:xx:xx | label +1 |
+---------+-------------------+-----------+
The internal LTE modem is a Quectel EG060W and needs the option driver
IDs added in a separate commit. gpio-export applies the initial output
value raw, ignoring the active-low flag, so output = <0> is what pulls
the modem power enable low and powers it at boot.
Migration to OpenWrt:
- Obtain the RSA signed intermediate firmware from Cudy:
openwrt-ramips-mt7621-cudy_lt700-outdoor-v1-squashfs-flash.bin
- Connect a computer to the LAN port and flash the intermediate firmware
via the OEM web interface
- OpenWrt is now accessible via 192.168.1.1
- Install the regular OpenWrt sysupgrade image from LuCI or over SSH
Revert back to OEM firmware:
- Configure a computer with IP 192.168.1.88 and run a TFTP server that
serves the OEM recovery image as recovery.bin
- Hold the reset button while powering on the device; the bootloader
fetches recovery.bin and restores the OEM firmware
Signed-off-by: Carlo Szelinsky <github@szelinsky.de>
Tested-by: achim <achim@cloudystars.net>
Link: https://github.com/openwrt/openwrt/pull/24689
Signed-off-by: Jonas Jelonek <jonas@jonasjelonek.de>
The ncm.json entry is picked from AT+CGMI alone, so every modem from a
vendor has to share one dialect. That does not hold for Quectel: the
EG060W answers "operation not supported" to AT+QICSGP on context 1, the
attached default bearer, while EC25/EG25 need AT+QICSGP because it is
what carries username, password and auth.
Read AT+CGMM as well and prefer a "<manufacturer>-<model>" entry, falling
back to the plain manufacturer entry, so existing profiles behave exactly
as before. The region suffix is dropped, EG060W-EAAA becomes eg060w.
json_is_a() is used rather than a bare json_select() so a modem without a
model specific entry does not log a warning.
Add quectel-eg060w, which configures the context with AT+CGDCONT.
Signed-off-by: Carlo Szelinsky <github@szelinsky.de>
Tested-by: Fredrik Hallin <fredappsdev@outlook.com>
Link: https://github.com/openwrt/openwrt/pull/24689
Signed-off-by: Jonas Jelonek <jonas@jonasjelonek.de>
Some modems raise carrier as soon as the data call is enabled and drop it
again briefly while the connection is finalised. On a Quectel EG060W the drop
is about a second long and lands about two seconds after AT+QNETDEVCTL, with
no configuration command involved at all. Not every modem does it: a Fibocom
FG621-EA sampled at ~90 Hz over three cycles never dropped carrier.
netifd turns that transient into a permanent failure: the DHCP sub-interface
is dynamic and created only once during setup, and a dynamic interface is
removed on carrier loss (IFC_REMOVE in interface_check_state()), so nothing
ever recreates it. udhcpc is usually still sending discovers when the drop
kills it, and the interface is left with no address and no route. If the drop
lands after a lease was granted the lease is stale instead, and a renew does
not recover that either because it unicasts to a server that no longer exists.
Wait for the carrier to stay up for a few seconds before handing the interface
to netifd, so the drop falls outside the window netifd is watching.
directip.sh already waits for carrier for the same class of reason ("wait for
carrier to avoid firmware stability bugs") and qmi.sh sleeps unconditionally
while waiting for registration; unlike those this is tunable, and linksettle 0
keeps the previous behaviour.
The wait gives up after three times linksettle, floored at 30s so it always
outlasts the settle window, and continues anyway. Profiles that only reach
data state at finalize, further down, have nothing to wait for here and skip
it; mtk1 is marked with linkatfinalize for that.
Measured on an EG060W (Cudy LT700 Outdoor), three runs each: with linksettle 0
udhcpc never obtains a lease, the dynamic interface is torn down and the link
stays dead; with the 3s default the lease, route and traffic come up every
time, and netifd logs no carrier loss at all.
On a modem without the drop the wait only adds linksettle seconds to the
bring-up (14s vs 17s on the FG621-EA). It stays on by default anyway: a missed
drop leaves the link permanently dead rather than slow, and it is not known in
advance which modems do it. linksettle 0 turns it off where it is not needed.
Signed-off-by: Carlo Szelinsky <github@szelinsky.de>
Tested-by: Fredrik Hallin <fredappsdev@outlook.com>
Link: https://github.com/openwrt/openwrt/pull/24689
Signed-off-by: Jonas Jelonek <jonas@jonasjelonek.de>
proto_ncm_setup() parses the AT+CGMI reply with RS='\r?\n',
proto_ncm_teardown() does not. The manufacturer ends up as "quectel\r",
json_select fails with "Unsupported modem" and teardown returns before
sending the disconnect command, leaving the data call up on the modem.
A blank CRLF line hits the same path: \r is not whitespace for the
default FS, so a record holding only \r still has NF == 1 and is taken
as the manufacturer.
This affects every vendor whose reply is a single token. "zte" comes out
the same either way, its reply is "ZTE INCORPORATED" so the CR sits past
$1.
Note that teardown now reaches the disconnect command on modems where it
silently never did, so those commands actually get sent on ifdown.
Signed-off-by: Carlo Szelinsky <github@szelinsky.de>
Link: https://github.com/openwrt/openwrt/pull/24689
Signed-off-by: Jonas Jelonek <jonas@jonasjelonek.de>
The Quectel EG060W (Cat 6 LTE) modem, as used by the Cudy LT700 Outdoor,
exposes its DIAG, NMEA and AT command interfaces as vendor-specific
(ff/00/00) under product ID 0x6004. Without a matching entry the option
driver does not bind these interfaces, so no /dev/ttyUSB* ports are
created and the modem cannot be configured.
Backport the upstream commit adding the 0x6004 product ID. The CDC-NCM
data interfaces use a standard class and are not affected.
Also refresh hack-6.18/780-usb-net-MeigLink_modem_support.patch, whose
option.c hunks are applied after this change and shift by one line.
Signed-off-by: Carlo Szelinsky <github@szelinsky.de>
Link: https://github.com/openwrt/openwrt/pull/24689
Signed-off-by: Jonas Jelonek <jonas@jonasjelonek.de>
Historically, GRO on bcm53xx target is disabled because it increases
forwarding overhead. As fraglist GRO is now used by default, this
overhead is minimized.
Signed-off-by: Qingfang Deng <dqfext@gmail.com>
Link: https://github.com/openwrt/openwrt/pull/23463
Signed-off-by: Jonas Jelonek <jonas@jonasjelonek.de>
This commit adds support for the Sirivision SR-ST31212F, a 12-port 10G
switch with 12x SFP+.
- SoC: Realtek RTL9313
- RAM: 512 MiB DDR3 (Micron D9PXV)
- Flash: SPI-NOR 16 MiB (Winbond W25Q128)
- Ethernet: 12x SFP+
- LEDs: 1x power (hardware blink), 1x system green (SYS_LED/GPIO),
1x SFP+ LED/port (link/act)
- Button: none
- Console: Cisco-style RJ45, 115200 8n1
- Fan: 2x 12V 2pin fan, always full speed
- Power: 12V DC from internal PSU, input AC 100-240V 50/60Hz
Board Layout
-----------
PCB ST31212F_V1.0, front, power side on the right:
SFP1-12 along the left. SoC RTL9313 and RAM (Micron D9PXV) center.
W25Q128 near SFP5/6. Two RTL8231 near SFP10-11.
Console, PWR/SYS LEDs, 74HC125, MA3232, HC164 near front-bottom.
12V header, two 2-pin fan headers, empty 14-pin and 6-pin headers.
MAC address is read from u-boot-env ethaddr.
U-Boot image magic is 0x93000000.
SFP cage sideband uses GPIOs from the SoC and 2x RTL8231.
Those pads are shared with the generic SPI master;
QSPI NOR uses a separate controller.
So pinmux_disable_spi0 / pinmux_disable_spi0_cs0 /
pinmux_disable_spi0_cs1 are required to release the pins to be GPIOs.
SerDes TX polarity is inverted on all SFP cages;
without this, port LEDs work but traffic does not.
System LED is controlled via SYS_LED by default,
here I use pinmux_disable_sys_led to switch it to GPIO0 from SoC
to make it more functional in openwrt.
OpenWrt installation
--------------------
1. Serial console, 115200 8n1, press Esc to stop U-Boot
2. Load initramfs-kernel.bin via YMODEM (loady) or TFTP and bootm.
Optional for tftpboot:
Use "printenv" to check the network settings of U-Boot.
You can change the ip address of the device by
"setenv ipaddr XXX.XXX.XXX.XXX" /
"setenv netmask XXX.XXX.XXX.XXX".
And you also need "setenv serverip XXX.XXX.XXX.XXX" to setup
your tftp server ip.
Then initialize the network by running "rtk network on".
And you may need the "rtk 10g PORT ..." command for
different SFP modules. Use "help rtk" for more details.
3. Backup the whole flash if you have not yet. Since there's no backup
runtime partition in this tiny flash, you will lose your original
runtime once you write openwrt in it.
Then sysupgrade the squashfs-sysupgrade.bin image.
Signed-off-by: Hui Chen <shinmarucc@hotmail.com>
Link: https://github.com/openwrt/openwrt/pull/25202
Signed-off-by: Jonas Jelonek <jonas@jonasjelonek.de>
It showed up after a kernel refresh on qualcommax, as it only shows up
with ALL_KMODS.
So, to avoid dropping it again from target config, move it to generic.
Fixes: 4f9740c734 ("qualcommax: refresh config")
Signed-off-by: Robert Marko <robimarko@gmail.com>
Move some vlan leftovers in the central include
into their designated location.
Assisted-by: ChatGPT (OpenAI GPT-5.6 Sol)
Link: https://github.com/openwrt/openwrt/pull/25222
Signed-off-by: Markus Stockhausen <markus.stockhausen@gmx.de>
Resets in the remoteproc node are now NOP, so no point in keeping them.
Allows dropping the WCSSAON patches.
Signed-off-by: Robert Marko <robimarko@gmail.com>
During the WCSS secure conversion, loading of M3 FW was forgotten.
Unlike on IPQ807x, it does not lead to a FW crash but we should still load
it.
Fixes: d7502b4694 ("qualcommax: ipq807x/ipq60xx: use secure WCSS driver")
Signed-off-by: Robert Marko <robimarko@gmail.com>
During the WCSS secure conversion, loading of M3 FW was forgotten.
This will lead to a remoteproc crash, so make sure to load the M3 FW.
Fixes: d7502b4694 ("qualcommax: ipq807x/ipq60xx: use secure WCSS driver")
Signed-off-by: Robert Marko <robimarko@gmail.com>
After a kernel config refresh for qualcommax it showed up again.
Instead of disabling it per target. disable by default in generic.
Signed-off-by: Robert Marko <robimarko@gmail.com>
Currently, getting the required UNIPHY derived clocks requires a global
clock lookup by name, so follow IPQ6018 and pass them directly via DT
reference to GCC.
Signed-off-by: Robert Marko <robimarko@gmail.com>
Use the WCSS secure driver for Q6 remoteproc on IPQ807x and IPQ6018.
This allows dropping a bunch of patches for the current overloaded driver.
Link: https://github.com/openwrt/openwrt/pull/25223
Signed-off-by: Robert Marko <robimarko@gmail.com>
The encoder ORs the source port field twice: first the raw port, then
the value it composes from the trunk or stack device and the port. Both
land on the same ten bits, so what reaches the hardware is the two
values ORed together rather than the composed one.
For an entry that came from the decoder without a trunk the two agree,
because the decoder masked the port down to six bits and the composition
puts the stack device back above it. For a trunk entry the decoder
leaves the whole field in port and the composition rebuilds it from
trunk, so the first OR contributes bits the second does not, and the
entry is written with a source port that is neither.
Drop the first OR: the composition below covers both branches.
Compile-tested on realtek/rtl930x and realtek/rtl838x. Not exercised on
hardware: the driver writes no trunk entry of its own, and a trunk entry
read back and rewritten is the case this affects.
Assisted-by: Claude:claude-opus-5
Signed-off-by: Gennaro Cimmino <gcimmino@rayonra.net>
Link: https://github.com/openwrt/openwrt/pull/25220
Signed-off-by: Markus Stockhausen <markus.stockhausen@gmx.de>
RTL930x and RTL839x decode and encode the age of an L2 entry as two
bits, so the top one is never read and never written. The GPL SDK field
lists give the field three bits on both: RTL9300_L2_UC_FIELDS in
src/hal/chipdef/longan/rtk_longan_tableField_list.c has AGE at lsp 17,
and RTL8390_L2_UC_FIELDS in the cypress list has it at lsp 21, each for
3 bits. RTL931x already masks with 7, and RTL8380 really does have a
two-bit field, so both are left alone.
An entry read at an age above three came back as that age minus four,
and the driver writes the age back on every change it makes to an
existing entry, so a long-lived entry could be aged down. The only age
the driver sets by itself is on entries it also marks static, where
ageing does not apply.
On RTL839x the encoder also wrote the field with no mask at all, where
an age above seven would have run into the source port sitting directly
above it. Nothing feeds it such a value today - it comes from a decode
or from a constant - but the field is now bounded like the others.
Compile-tested on realtek/rtl930x and realtek/rtl838x. Not tested on
RTL839x hardware: none here.
Assisted-by: Claude:claude-opus-5
Signed-off-by: Gennaro Cimmino <gcimmino@rayonra.net>
Link: https://github.com/openwrt/openwrt/pull/25220
Signed-off-by: Markus Stockhausen <markus.stockhausen@gmx.de>
Taking a next hop down means going back to the hash slot whose index the
installer recorded and rewriting whatever is there. It is checked for
being valid and for nothing else, and validity is the one thing a slot
that has been reused also has.
Routes through one gateway share a single L2 entry: the installer finds
the first route's entry already there and returns without changing it,
so the second route records the same index. Once the first route is torn
down, what the second one goes on to rewrite depends on what became of
that slot. If the entry was static it was invalidated, and the slot is
free for the switch to learn into or for another next hop to take. If it
was a learned entry the teardown left it in place with its next hop bit
cleared, and the second teardown then stamps it again with the VID the
route saved - which for a next hop that adopted an entry is whatever was
in that slot, and for one that created it is zero.
Ask the entry whether it is still the one that was installed, comparing
it on the seed the installer searches by, and leave it alone otherwise.
Both ways of failing that question are reported as errors: a slot that
no longer matches the address and VID the installer searched by, and one
that is empty or no longer a next hop.
The second of those is what a sibling route's teardown leaves behind, so
it will be seen on a working setup until the sharing itself is fixed:
the first route to go still takes the entry away from the others, which
go on pointing at it. Nothing counts the routes sharing a next hop. The
vendor SDK keeps a per-entry nh_used_cnt[] for exactly that, incremented
when a next hop is installed and decremented on removal, and only
releases the entry once it reaches zero; this driver has nowhere to put
such a count yet.
RTL839x runs this code too. Its config sets the setup op, which is what
the FIB and netevent notifiers gate on, so its L3 offload uses these
helpers in every build with no Kconfig symbol involved. The change can
only skip a write, never add one, though the sibling case now logs where
it did not before. It was not tested there - no RTL838x, RTL839x or
RTL931x hardware here.
Functionally tested on a Hasivo S1100W-8XGT-SE (RTL9303), kernel
6.18.52, with CONFIG_NET_DSA_RTL83XX_RTL930X_L3_OFFLOAD=y, in two
arrangements. Two routes through one gateway, torn down with either of
them going first: they share one L2 entry, and the one removed second
finds it no longer a next hop and leaves it alone. Then one route
through each of two gateways: they get an entry each, and removing one
leaves traffic through the other forwarded. No teardown found a slot
holding something else.
Assisted-by: Claude:claude-opus-5
Signed-off-by: Gennaro Cimmino <gcimmino@rayonra.net>
Link: https://github.com/openwrt/openwrt/pull/25220
Signed-off-by: Markus Stockhausen <markus.stockhausen@gmx.de>
The next hop installer declares its entry descriptor on the stack and
fills the fields it cares about, trusting the search loop above to have
filled in the rest. It has not. On RTL930x and RTL931x the reader
returns as soon as it finds the valid bit clear, so the descriptor it
was handed keeps whatever was there: stack contents on the first slot
examined, or the contents of a neighbour entry read earlier in the same
bucket.
What is left over then reaches the hardware, because the encoder takes
the destination port from fields this branch never sets:
if (e->is_trunk) {
r[2] |= BIT(30);
port = e->stack_dev << 9 | (e->port & 0x3f);
}
so the entry can be written as a trunk entry, or aimed at another stack
device, instead of at port-ignore. On RTL931x the same descriptor also
carries is_open_flow, is_pe_forward and the L2 tunnel fields into the
row.
Clear the descriptor where the entry is built rather than at its
declaration: the loop above reuses it across reads, so an initialiser
there would have been overwritten by the time this branch runs. The
assignments this makes redundant go with it, including the two ahead of
the loop - the other branch takes the MAC and the port from the entry it
adopts.
RTL838x and RTL839x decode the row before deciding validity, so the
fields their encoders read hold the previous occupant's values rather
than stack contents. One of those, vid, is read by the RTL838x encoder
and is now zero instead; the next hop path never set it, and the row it
lands in has its route id written over the same bits.
Tested on a Hasivo S1100W-8XGT-SE (RTL9303), kernel 6.18.52, with
CONFIG_NET_DSA_RTL83XX_RTL930X_L3_OFFLOAD=y, to the extent the bench
allows: routes through a gateway are installed and torn down as before,
and traffic through them is forwarded by the hardware. That exercises
the branch which adopts an entry already in the table, not the one this
patch rewrites - a next hop is only installed once the neighbour is
valid, and by then the switch has learned that MAC, so the free slot
case needs an entry that has aged out or been evicted. It was not
reached on hardware here.
Assisted-by: Claude:claude-opus-5
Signed-off-by: Gennaro Cimmino <gcimmino@rayonra.net>
Link: https://github.com/openwrt/openwrt/pull/25220
Signed-off-by: Markus Stockhausen <markus.stockhausen@gmx.de>
The L2 unicast entry encoder writes the source-MAC block flag to bit 17
of the last word while the decoder in rtl930x_fill_l2_entry() reads it
from bit 16. One of the two has to be wrong, and it is the encoder: the
GPL SDK field list for this table, RTL9300_L2_UC_FIELDS in
src/hal/chipdef/longan/rtk_longan_tableField_list.c, places SA_BLK at
lsp 16 and AGE at lsp 17 for 3 bits.
Bit 17 is therefore not a spare bit but the low bit of AGE, which the
next line writes as well. Both are ORed in, so the entry came out with
the flag dropped and the low bit of its age forced to one.
Nothing in the driver sets block_sa today: the FDB paths build their
entry from a cleared descriptor, the next hop paths carry back whatever
the hardware reported, and no user interface reaches the flag. So no
behaviour changes here - the encoder simply stops disagreeing with the
decoder.
The other three families agree with their own decoders already, and
their bit positions are untouched.
Compile-tested on realtek/rtl930x and realtek/rtl838x.
Assisted-by: Claude:claude-opus-5
Signed-off-by: Gennaro Cimmino <gcimmino@rayonra.net>
Link: https://github.com/openwrt/openwrt/pull/25220
Signed-off-by: Markus Stockhausen <markus.stockhausen@gmx.de>
The two helpers that install and take down an L2 next hop entry still
carry the rtl83xx_ prefix the driver is moving away from. They are not
tied to a family: they work through priv->r, and the per-SoC differences
live in the table accessors they call.
Rename only these two. The other rtl83xx_ symbols are left alone; this
is the pair the L3 offload work is about to touch.
No functional change: both are module-internal and neither is exported.
The three pr_debug and pr_err lines inside them print __func__, so what
those emit changes with the name.
Compile-tested on realtek/rtl930x and realtek/rtl838x, with
CONFIG_NET_DSA_RTL83XX_RTL930X_L3_OFFLOAD both set and unset.
Assisted-by: Claude:claude-opus-5
Signed-off-by: Gennaro Cimmino <gcimmino@rayonra.net>
Link: https://github.com/openwrt/openwrt/pull/25220
Signed-off-by: Markus Stockhausen <markus.stockhausen@gmx.de>
Following on from e23b37b, it makes sense to also enable searching
the history rather that just relying on up and down arrow while
scanning the results.
On both x86 and ath79, busybox binary is ~4.2k bigger. The resulting
x86 .apk is 447 bytes bigger and mips_24kc .apk is 324 bytes bigger.
Signed-off-by: Eric Fahlgren <ericfahlgren@gmail.com>
Link: https://github.com/openwrt/openwrt/pull/23953
Signed-off-by: Robert Marko <robimarko@gmail.com>
Passing UNIPHY clocks as DT references directly to GCC allows us to drop
the old global name hack we needed for SSDK.
Signed-off-by: Robert Marko <robimarko@gmail.com>
Add support for the DrayTek VigorSwitch G2282x, a 28-port L2 access
switch based on the Realtek RTL9301.
- SoC: Realtek RTL9301
- RAM: 256 MiB
- Flash: SPI-NOR 64 MiB
- Ethernet: 24x GE copper + 4x 10G SFP+
- LEDs: 1x system status ("SYS"), 1x "MONITOR", per-port
link/activity via led_set
- Buttons: Reset, Stack
- Console: UART, 115200 8n1
Same chip and port configuration as rtl9301_d-link_dgs-1250-28x.dts,
so the &switch0/&mdio_bus0-2/ethernet-ports block is derived from
that file, with PHYs enclosed in ethernet-phy-package nodes. Ports
0-7 use qsgmii and ports 8-23 use usxgmii; a uniform qsgmii mode
fails PCS configuration (-EOPNOTSUPP) on ports 8-23. led_set1 for
SFP+ cages supports 10M/100M/1G/2.5G/5G/10G range, referencing the
identically wired rtl9301_d-link_dgs-1250-28x.dts board.
GPIO expanders
------------
Two RTL8231 I2C/MDC GPIO expanders are present. The one at address
0x0 backs the system LED (pin 29, always-on ~1 Hz blink, no status
encoding) and the 4 SFP+ cages' module-detect lines (read-only),
LOS is not wired on board. The one at address 0x18 drives a
single-digit 7-segment "Stack ID" display (pins 0-6: segments,
active-low; pin 8: decimal point; pin 7: a second corner dot with
no slot in the gpio-7-segment binding) plus the "MONITOR" LED
(pin 22, red, active-low) and the reset (pin 9) / stack (pin 10)
buttons. The digit display is declared but left disabled: enabling
it requires kmod package support 7 segment display. Both RTL8231's
also carry a disabled led-controller child, since mfd-core
unconditionally registers the rtl8231-leds cell and its probe
fails without a matching node.
Hardware monitor
------------
The native SoC i2c_mst1 engine carries an ADT7475 hardware monitor
and the 4 SFP+ cages on one shared master. The board is passively
cooled (no fan), so the ADT7475 here is monitor-only.
Flash layout
------------
64 MiB SPI-NOR, dual-image A/B: two uImage-capable "Kernel"/
"Kernel2" partitions (compatible = "openwrt,uimage", "denx,uimage"),
selected by a "bootpartition" variable stored in the Factory
partition rather than standard U-Boot env (so fw_setenv/saveenv
cannot read or persist it -- use the LOADER's own "setsys"/
"printsys"/"savesys" commands instead). platform.sh self-upgrades
whichever slot is currently active instead of flipping slots, so
the normal staged sysupgrade rewrite stays safe and no separate
boot-slot-select step is needed after flashing. bootpartition is
also validated in platform_check_image, since do_stage2 does not
check platform_do_upgrade's exit status and an unreadable
bootpartition would otherwise look like a successful upgrade.
MAC Address
------------
The MAC address is read from the BDINFO partition's U-Boot-env
"ethaddr" variable via the label-mac-device alias, and
board.d/02_network assigns it to the LAN bridge/ports.
Initial Flashing
------------
1. Attach to the serial console, 115200 8n1.
2. Power on and interrupt the U-Boot banner by pressing x to
reach the LOADER's password-protected console.
3. Set
printsys
setsys ipaddr <IP>
setsys serverip <IP>
rtk network on
savesys
The LOADER's network stack is off by default and neither ping nor
tftpboot will do anything without it. PHY init takes a few
seconds with no console output; this is not a hang.
Confirm with:
ping <serverip>
4. Optionally pin which flash slot this install will target, since
sysupgrade later targets whichever slot is active at that time.
This is stored in the Factory partition, not standard U-Boot env,
so it needs the LOADER's own "sys" commands rather than setenv/
saveenv:
printsys # confirm current bootpartition
setsys bootpartition <0|1>
printsys # confirm the new value in RAM
savesys # persists it to Factory
printsys # confirm it actually persisted
5. Serve the initramfs-kernel.bin image from a TFTP server reachable
at <serverip> (e.g. "atftpd --daemon --no-fork --directory
<dir-containing-the-image>"), then fetch and boot it directly
from the LOADER prompt:
tftpboot 81000000 initramfs-kernel.bin
bootm 81000000
tftpboot reports "Bytes transferred = N"; compare this against
the local image's exact file size before running bootm -- a
truncated transfer will otherwise fail confusingly partway
through boot instead of at the transfer itself. The load address
0x81000000 is above the uImage header's own Load Address
(0x80100000); loading at or below that address lets the in-place
LZMA decompressor overwrite its own not-yet-read input.
6. The resulting OpenWrt console comes up on the default LAN address
(192.168.1.1). Set network.lan.ipaddr via UCI to a reachable
address if needed.
7. Send the squashfs-sysupgrade.bin image to the device (e.g. via
"scp -O", since dropbear here only speaks legacy SCP, not SFTP)
and run sysupgrade as normal.
Assisted-By: Claude Sonnet <noreply@anthropic.com>
Signed-off-by: Jakob Baumann <jakobbaumann@riseup.net>
Link: https://github.com/openwrt/openwrt/pull/25171
Signed-off-by: Markus Stockhausen <markus.stockhausen@gmx.de>
The route table dump shows that a route is programmed and, through the
hit bit, that the hardware matched it. It says nothing about what the
packet then becomes, and that is where a routed packet is currently
lost. The next hop names both halves of it: a DMAC entry in the L2
table, which carries the destination MAC and the port the packet leaves
by, and an egress interface, which carries the VLAN it is sent into and
the source MAC it is sent with. None of those three tables can be read
back anywhere, on entries the driver writes and never revisits.
Sweep L3_NEXTHOP and follow each row that is in use into both:
NH_ID DMAC_IDX INTF VID SMAC V NH STATIC TRK PORT MAC RVID NH_RID
2 36 0 1 00:82:44:xx:xx:xx 1 1 0 0 0 c6:a4:9e:xx:xx:xx 1 2
NH_RID is the route id the driver stores in the L2 entry in place of the
VID, so a chain that resolves can be told from one whose DMAC index
happens to land on somebody else's entry. The three values that name an
action rather than an entry - drop, trap to CPU, trap to master CPU -
are above the table and are printed as what they are, because reading
the L2 table at those indexes would only produce a warning.
Reading the egress interface back needs a getter beside the setter that
was already there; it decodes the same layout.
The next hop table has no valid bit, so a row counts as in use when
either field is set; a next hop that legitimately holds DMAC index 0 on
interface 0 is not shown. The sweep takes the table lock one row at a
time rather than across the whole run, because that lock is shared by
every L3 table and route programming waits behind it; seq_file may
repeat the sweep on a larger buffer before a read completes.
RTL930x only: the file is created by the dbgfs_init op, which is set in
otto_l3_930x_cfg alone and only under a symbol no target config sets.
Functionally tested on a Hasivo S1100W-8XGT-SE (RTL9303), kernel
6.18.44, with CONFIG_NET_DSA_RTL83XX_RTL930X_L3_OFFLOAD=y: the line is a
prefix route through a gateway that resolves, read back from the running
switch. NH_RID matches the next hop the row was read from, and the VID
and SMAC are the VLAN and the MAC the driver picked for that interface.
The getter decodes what the setter encodes, so on an entry the driver
wrote the file reports the driver's own state rather than an independent
reading of the hardware layout.
Assisted-by: Claude:claude-opus-5
Signed-off-by: Gennaro Cimmino <gcimmino@rayonra.net>
Link: https://github.com/openwrt/openwrt/pull/25203
Signed-off-by: Markus Stockhausen <markus.stockhausen@gmx.de>
Every route the driver offloads also gets a PIE rule, and for a prefix
route that rule carries a forwarding action, PIE_ACT_ROUTE_UC, pointed
at the route's L2 next hop. On RTL930x that rule is what stops the
packet: the route matches, its HIT bit is set, the next hop and the L2
entry behind it hold the right values, and nothing leaves the switch.
RTL930x does not need the rule at all. otto_l3_930x_route_write() puts
the destination and its mask into the route entry itself, so the entry
matches on its own. RTL838x and RTL839x are the reason the rule exists:
otto_l3_839x_route_write() stores only the gateway and the source-MAC
index, so on those the PIE rule is the only thing that ever matches a
destination. Skip it where the L3 tables match on their own, and say
which model the family uses in the config rather than test for a family.
The teardown used the rule's id to decide whether to remove the L2 next
hop as well, because both were allocated in otto_l3_nexthop_update().
That is no longer true on RTL930x, so record on the next hop itself
whether it was written, and only when the write reported success:
rtl83xx_l2_nexthop_add() returns -1 on a full hash bucket and leaves
l2_id at zero, which the teardown would otherwise take for a real index
and use to clear whatever entry sits in the first L2 slot. Using
attr.valid would not work either - the local host route branch of
otto_l3_fib_add_v4() sets it without ever adding a next hop.
Measured on a Hasivo S1100W-8XGT-SE (RTL9303, RTL8264B PHYs, USXGMII),
kernel 6.18.44, with CONFIG_NET_DSA_RTL83XX_RTL930X_L3_OFFLOAD=y. Two
clients on two ports, routes through one of them, and ip_forward=0 on
the switch, so the CPU cannot forward. Same board and same rig:
route main this patch
10.40.2.0/24 0/3 3/3, 3/3, 3/3
10.40.9.9/32 3/3 3/3, 3/3, 3/3
The /32 is a host route and already forwarded: it goes into a different
table and gets a PIE rule that carries only a counter, no action. Only
the prefix branch sets one.
That the packets do not reach the CPU was read from /proc/net/snmp, with
a same-size control in the same window:
400 pings routed to 10.40.2.7 InReceives +10, +13
400 pings sent to the switch InReceives +402, +405
5 s of no traffic at all InReceives +1, +20
ForwDatagrams stayed at 0 throughout. The return direction is plain L2 -
the client subnet is connected on the receiver - so the route is
exercised in the request direction only.
Two add/remove cycles of both routes, plus removing and re-adding the
switch's own address, produced no new message from the teardown path:
the only line in the whole log is "prefix route 192.168.1.0/24 was not
in hardware" on the connected route, which main prints in the same
place.
RTL838x, RTL839x and RTL931x are unchanged in what they program.
RTL931x and RTL838x never reach this function - their configs set no
setup op, and both notifiers return early without one. RTL839x does,
and there use_l3_tables stays false, so it still gets its rule.
Its teardown now removes an L2 next hop that a failed PIE rule used to
strand. Not tested on those families: no hardware here. Compile-tested
on realtek/rtl930x with the symbol on and off.
Assisted-by: Claude:claude-opus-5
Signed-off-by: Gennaro Cimmino <gcimmino@rayonra.net>
Link: https://github.com/openwrt/openwrt/pull/25205
Signed-off-by: Markus Stockhausen <markus.stockhausen@gmx.de>
Both AQR113C PHYs can return invalid MDIO data immediately after reset
is deasserted. Firmware detection then skips the NVMEM image and PHY
attachment times out.
Add a 100 ms reset assertion delay and wait 250 ms after deassertion
before probing each PHY. Cold-boot testing confirms both 10G ports
attach without the generic firmware-ID workaround.
Signed-off-by: Robert Marko <robimarko@gmail.com>
Remove stale symbols consistent with
c56ae856ddb96b92b6dd22bd7564269ae301e1c2
Signed-off-by: John Audia <therealgraysky@proton.me>
Link: https://github.com/openwrt/openwrt/pull/24800
Signed-off-by: Robert Marko <robimarko@gmail.com>
6.18.45 pulled in b5964aac51e0 ("netfilter: flowtable: consolidate xmit
path") as a dependency of the CVE fix 8aecf0bbcc72. Since then the
FLOW_OFFLOAD_XMIT_NEIGH path no longer transmits on the dst device, it
looks the output device up by flow_offload_tuple.ifidx, which is fed from
route->tuple[dir].out.ifindex.
That field is only assigned in nft_dev_forward_path() when the forward
path could be discovered, nft_default_forward_path() never sets it. A
flow whose egress path cannot be resolved is therefore offloaded with an
interface index of 0, nf_flow_offload_ip_hook() drops the packet and
tears the flow down, the flow_offload expression offloads it again right
away and the loss repeats instead of settling.
Bridged Wi-Fi is the common case: mac80211 fails .ndo_fill_forward_path
with -EOPNOTSUPP unless the driver implements .net_fill_forward_path, and
with WED the path ends in DEV_PATH_MTK_WDMA, which the parser does not
handle either, so both ways end up without an output device. This matches
the reports of sporadic packet loss towards wireless clients with
software flow offloading enabled on mediatek/filogic.
Add a patch that falls back to the dst device, which is the device the
neighbour is looked up on and the device the packets were transmitted on
before the xmit paths were merged. Mainline avoids the situation
differently since 871df5007eda ("netfilter: flowtable: bail out if
forward path cannot be discovered"), which sits on top of the forward
path discovery rework and does not apply to 6.18.y. 6.12 does not carry
the rework and is not affected.
Fixes: da5c83ece675 ("kernel: bump 6.18 to 6.18.45")
Assisted-by: Claude:claude-opus-5
Signed-off-by: Hauke Mehrtens <hauke@hauke-m.de>
Link: https://github.com/openwrt/openwrt/pull/24800
Signed-off-by: Robert Marko <robimarko@gmail.com>
Since 6.18.45 backported b5964aac51e0 ("netfilter: flowtable: consolidate
xmit path"), FLOW_OFFLOAD_XMIT_NEIGH looks the output device up by
route->tuple[dir].out.ifindex instead of transmitting on the dst device.
Our copy of the route setup sets that field only for
FLOW_OFFLOAD_XMIT_DIRECT and only discovers the forward path when both
directions are FLOW_OFFLOAD_XMIT_NEIGH, so a flow with an undiscoverable
path, a non-ethernet egress or an xfrm dst in one direction gets an
ifindex of 0: every packet is dropped, the flow is torn down and
offloaded again.
Set the ifindex for every xmit type and look each direction up on its
own, as upstream does in the nftables path (b5964aac51e0 and 2bdc536c9da7
"netfilter: always set route tuple out ifindex"), and fall back to the
dst device where upstream leaves the ifindex at 0, so those flows stay
offloaded.
Fixes: da5c83ece675 ("kernel: bump 6.18 to 6.18.45")
Assisted-by: Claude:claude-opus-5
Signed-off-by: Hauke Mehrtens <hauke@hauke-m.de>
Link: https://github.com/openwrt/openwrt/pull/24800
Signed-off-by: Robert Marko <robimarko@gmail.com>
RTL930x and RTL931x use the same sequence to move a running SerDes to
another PLL. Consolidate the implementations using the existing variant
operations for deactivation, PLL selection, mode restoration and
activation.
Switch RTL930x to the same valid assumption that RTL931x already uses:
Assume that the requested PLL is always the opposite one to be able to
drop the last remaining invariant between both, to unify both
implementations.
While at it, make the RTL930x set_power helper return its error instead
of discarding it silently.
Assisted-by: ChatGPT (OpenAI GPT-5.6 Sol)
Link: https://github.com/openwrt/openwrt/pull/25176
Signed-off-by: Jonas Jelonek <jonas@jonasjelonek.de>
Continuing after CMU setup fails leaves the SerDes without a usable clock
configuration. Propagate the error from config_hw_mode() so pcs_config()
aborts before programming and activating the target mode.
Assisted-by: ChatGPT (OpenAI GPT-5.6 Sol)
Link: https://github.com/openwrt/openwrt/pull/25176
Signed-off-by: Jonas Jelonek <jonas@jonasjelonek.de>
USXGMII may fail to come up on SerDes lanes that previously ran SGMII
or 1000Base-X. Updating the MAC mode alone does not reliably reconfigure
the analog clock path.
Run CMU configuration for every active RTL930x mode and check clock
readiness after programming the target mode. Preserve the existing choice
between MAC-driven and IP-driven mode setup. Restore PLL-migrated neighbors
through their saved mode and USXGMII submode so MAC-driven modes no longer
depend on stale IP mode state.
This marks a breaking point of our understanding of the SerDes setup
under RTL930x hardware. While we assumed so far that MAC-driven modes
are entirely setup and controlled by the MAC setup, this appears to be
not true based on the issue. More likely is that default or preset
configuration on certain SerDes happens to be correct for all our needs
so far. But if we drive the those SerDes with modes the SDK hasn't
intended, such issues will surface.
Link: https://github.com/openwrt/openwrt/issues/25065
Assisted-by: ChatGPT (OpenAI GPT-5.6 Sol)
Link: https://github.com/openwrt/openwrt/pull/25176
Signed-off-by: Jonas Jelonek <jonas@jonasjelonek.de>
Add the RTL9302B variant as a dedicated compatible to the board support
patch for properly setup on devices with this SoC.
Link: https://github.com/openwrt/openwrt/pull/25208
Signed-off-by: Jonas Jelonek <jonas@jonasjelonek.de>
Upstream is working towards making GPIOLIB_LEGACY optional. Get ahead of
that by gating off such code as is done upstream.
Signed-off-by: Rosen Penev <rosenp@gmail.com>
Link: https://github.com/openwrt/openwrt/pull/24985
Signed-off-by: Jonas Jelonek <jonas@jonasjelonek.de>
The use of a modulo operation on 64-bit integers broke the build on
32-bit targets which do not provide such operations.
Seen on the buildbots as:
mips-openwrt-linux-musl-ld: drivers/mtd/parsers/routerbootpart.o:
in function `routerboot_partitions_parse':
drivers/mtd/parsers/routerbootpart.c:
(.text.routerboot_partitions_parse+0x58c):
undefined reference to `__umoddi3'
Fix this by using mtd_mod_by_eb() instead of open-coding the alginment
check.
Fixes: ada1dcb2b4 ("generic: mikrotik: size RouterBoot partitions so they are usable")
Signed-off-by: Daniel Golle <daniel@makrotopia.org>
Found while working through https://github.com/openwrt/openwrt/issues/25129.
otto_l3_alloc_egress_intf() looks for an egress interface that already
carries the router MAC and reuses it, whatever VLAN that interface was
made for. The MAC it matches on is the one of the netdev the route
points at, and a VLAN device inherits it from the bridge, so on a router
with several VLANs they all carry the same one: the first VLAN to ask
takes an interface and every other VLAN is handed that one.
That matters because the entry behind it holds DST_VID, the VLAN the
routed packet is sent into, so every VLAN but the first would forward
into the first one's domain. The vendor SDK keys interfaces the same way
this patch does - dal_longan_l3_intf_create() stores one interface index
per VID and writes that VID as the egress DST_VID - and in that mode it
sizes the table at 64, the number of source MAC slots, which is what the
array here is indexed by.
Match on the VLAN as well as the MAC, and keep the descriptors in the
array that struct otto_l3_ctrl already had for them, declared and never
used. Two dev_dbg lines are added with it, because the allocator had no
output at all and the choice it makes is not visible anywhere else.
An interface is still never released - nothing frees one today - so with
this change a box that creates and destroys VLAN interfaces burns a slot
each time and has 64 of them.
Measured on a Hasivo S1100W-8XGT-SE (RTL9303), kernel 6.18.44, with
CONFIG_NET_DSA_RTL83XX_RTL930X_L3_OFFLOAD=y, from a clean boot: an
address on the bridge VLAN, then an interface on VLAN 30 and a route
through it.
add IPv4 route 192.168.1.1/32 (VLAN 1, MAC 00:82:44:xx:xx:xx)
new egress interface 0 for VLAN 1
add IPv4 route 192.168.1.0/24 (VLAN 1, MAC 00:82:44:xx:xx:xx)
reusing egress interface 0 for VLAN 1
add IPv4 route 10.79.0.1/32 (VLAN 30, MAC 00:82:44:xx:xx:xx)
new egress interface 1 for VLAN 30
add IPv4 route 10.79.0.0/24 (VLAN 30, MAC 00:82:44:xx:xx:xx)
reusing egress interface 1 for VLAN 30
The same MAC on both VLANs is what the third column shows. By
inspection, with the VLAN out of the comparison the search ends at the
first entry holding that MAC, which is interface 0; the DST_VID that
would then be used is not something I measured on the wire.
RTL930x only: the interface table and its MAC table are written through
ops set in otto_l3_930x_cfg alone, under a symbol no target config sets,
and the call sits in the set_router_mac branch, an op no other family
has.
Assisted-by: Claude:claude-opus-5
Signed-off-by: Gennaro Cimmino <gcimmino@rayonra.net>
Link: https://github.com/openwrt/openwrt/pull/25204
Signed-off-by: Markus Stockhausen <markus.stockhausen@gmx.de>
Dropping the rt2x00 MTD EEPROM patch changes the modules the package
builds, so bump the release for users to receive the update.
Signed-off-by: Daniel Golle <daniel@makrotopia.org>
Eight of the rt2x00 patches either carry no From/Date/Subject header at
all or lack the leading "From <sha> Mon Sep 17 00:00:00 2001" line, so
"git am" cannot apply them and the contribution checks reject them. Take
the author, date and subject of each from the commit which first added
it to this tree.
Signed-off-by: Daniel Golle <daniel@makrotopia.org>