From d3e8698e407f8cf3582d1f2c44f0dbb050a4a88f Mon Sep 17 00:00:00 2001
From: donbernhardo <87759236+donbernhardo@users.noreply.github.com>
Date: Wed, 17 Jun 2026 21:16:21 +0200
Subject: [PATCH 01/18] Fix KP-9000-6XHML-X2 LED mux mapping
---
machine.c | 11 +++++++++++
1 file changed, 11 insertions(+)
diff --git a/machine.c b/machine.c
index 04db39a..b735167 100644
--- a/machine.c
+++ b/machine.c
@@ -40,6 +40,17 @@ __code const struct machine machine = {
LEDS_1G | LEDS_LINK,
0 },
},
+ .led_mux_custom = 1,
+ /* Measured front-panel LED pads:
+ * P1 amber/green: 9/10, P2: 12/15, P3: 18/21, P4: 24/25, left SFP: 26.
+ */
+ .led_mux = {
+ 0x3f, 0x3f, 0x3f, 0x3f, 0x3f, 0x3f, 0x3f, 0x3f,
+ 0x3f, 0x10, 0x12, 0x3f,
+ 0x14, 0x3f, 0x3f, 0x16, 0x3f, 0x3f,
+ 0x18, 0x3f, 0x3f, 0x1a, 0x3f, 0x3f,
+ 0x1c, 0x1e, 0x0c, 0x20
+ },
};
void machine_custom_init(void) { }
From 64a1d252cd4cb199b2f7b1227fcddadb0c60bdc4 Mon Sep 17 00:00:00 2001
From: donbernhardo <87759236+donbernhardo@users.noreply.github.com>
Date: Mon, 29 Jun 2026 13:54:47 +0200
Subject: [PATCH 02/18] Split KP-9000 6XH targets by PCB revision
---
Makefile | 2 +-
doc/devices/KP-9000-6XH-X2.md | 40 ++++++++++++++++++++++++++++++-----
doc/supported_devices.md | 4 ++--
machine.c | 14 ++++++++++--
machine.h | 3 +++
5 files changed, 53 insertions(+), 10 deletions(-)
diff --git a/Makefile b/Makefile
index 3b8027b..ada70cf 100644
--- a/Makefile
+++ b/Makefile
@@ -96,7 +96,7 @@ $(BUILDDIR)/rtlplayground-$(FILENAME_EXTENSION).bin: $(BUILDDIR)/rtlplayground.i
machine_check:
@mkdir -p $(BUILDDIR)/tmp
@set -eo pipefail; \
- for MACHINE in `grep -e ' MACHINE_' machine.c | sed -e 's%^.* MACHINE_%%' -e 's%[ ]*//.*$$%%' | sort -u`; \
+ for MACHINE in `grep -E '^[[:space:]]*(//[[:space:]]*)?#define MACHINE_' machine.h | sed -E 's%^[[:space:]]*(//[[:space:]]*)?#define MACHINE_%%' | awk '{print $$1}' | sort -u`; \
do \
echo "Checking $${MACHINE}"; \
$(CC) $(CC_FLAGS) -DMACHINE_$${MACHINE} -MMD -o $(BUILDDIR)/tmp/machine_check -c machine.c; \
diff --git a/doc/devices/KP-9000-6XH-X2.md b/doc/devices/KP-9000-6XH-X2.md
index 4cc21d7..b1c8fdc 100644
--- a/doc/devices/KP-9000-6XH-X2.md
+++ b/doc/devices/KP-9000-6XH-X2.md
@@ -1,17 +1,32 @@
-# Keeplink KP-9000-6XH-X2
+# Keeplink KP-9000-6XH-X2 / KP-9000-6XHML-X2
-Following is documentation for unmanaged switch marked as `KP-9000-6XH-X2`.
+Following is documentation for switches marked as `KP-9000-6XH-X2` or
+`KP-9000-6XHML-X2`.
Using SPI clamp in-board is the only method for initial installation.
### Label specifications
- **Name**: 4X 2.5G RJ45 Port + 2 X 10G SFP+ Port
-- **Model**: KP-9000-6XH-X2
+- **Model**: KP-9000-6XH-X2 / KP-9000-6XHML-X2
- **Ports**:
- 4 × RJ45: 10/100/1000/2500 Mbps
- 2 × SFP+: 1000 / 2500 / 10000 Mbps
+### Machine target
+
+These devices exist with different PCB revisions. Select the machine target by
+the PCB silkscreen, not only by the label on the case.
+
+| PCB silkscreen | Known labels | Recommended machine target | Legacy target |
+| --- | --- | --- | --- |
+| `2M-PCB43-V1.2` | `KP-9000-6XHML-X2` | `MACHINE_KP_9000_6XHML_X2_V1_2` | `MACHINE_KP_9000_6XHML_X2` |
+| `2M-PCB43-V2.1` | `KP-9000-6XH-X2`, `KP-9000-6XHML-X2` | `MACHINE_KP_9000_6XH_X2_V2_1` or `MACHINE_KP_9000_6XHML_X2_V2_1` | `MACHINE_KP_9000_6XH_X2` |
+
+The V1.2 and V2.1 boards use different GPIO, SFP, port and LED mappings.
+The unmanaged and managed labels are firmware/SKU differences and do not by
+themselves identify the PCB wiring.
+
### What works
- All four 2.5GBASE-T RJ45 ports at 10/100/1000/2500 Mbps
@@ -19,7 +34,7 @@ Using SPI clamp in-board is the only method for initial installation.
- LEDs
- untested due to missing Hardware: SFP+ ports equipped with 1G or 2.5G SFPs.
-### Hardware overview
+### Hardware overview: 2M-PCB43-V2.1
Front side:
@@ -44,6 +59,22 @@ Bottom
+### Hardware overview: 2M-PCB43-V1.2
+
+The V1.2 board has been seen in managed `KP-9000-6XHML-X2` devices.
+
+Label:
+
+
+
+Top side:
+
+
+
+Bottom:
+
+
+
## Reset Button
There's an unpopulated Reset button on the front left side of the PCB.
@@ -54,4 +85,3 @@ The front case has already the hole in the metal case, you just have to punch a
## Power supply
Input power is delivered via barell plug, `12V 1A` adapter was provided.
-
diff --git a/doc/supported_devices.md b/doc/supported_devices.md
index 59e54d8..c1b3f9a 100644
--- a/doc/supported_devices.md
+++ b/doc/supported_devices.md
@@ -1,8 +1,8 @@
# Supported Hardware
The following devices have been tested and are fully working:
- Horaco ZX_SG4T2
-- keepLINK kp-9000-6hx-x2 (RTL8372: 4x 2.5GBit + 2x 10GBit SFP+)
-- keepLINK KP-9000-6XHML-X2, same as above, but Managed
+- keepLINK KP-9000-6XH-X2 / KP-9000-6XHML-X2 (RTL8372: 4x 2.5GBit + 2x 10GBit SFP+);
+ select the machine target by PCB revision, see [KP-9000-6XH-X2](devices/KP-9000-6XH-X2.md)
- keepLINK kp-9000-6hx-x (RTL8372 + RTL8221B 2.5GBit PHY: 5 x 2.5GBit + 1x 10GBit SFP+)
- keepLINK kp-9000-9xh-x-eu (1 x RTL8373 + RTL8224: 8x 2.5GBit + 1x 10GBit SFP+)
- Lianguo LG-SWTGW218AS (RTL8373 + RTL8224 PHY: 8x 2.5GBit + 1x 10GBit SFP+)
diff --git a/machine.c b/machine.c
index b735167..96c6f0b 100644
--- a/machine.c
+++ b/machine.c
@@ -5,9 +5,13 @@
#include "rtl837x_regs.h"
#include "rtl837x_common.h"
-#ifdef MACHINE_KP_9000_6XHML_X2
+#if defined(MACHINE_KP_9000_6XHML_X2) || defined(MACHINE_KP_9000_6XHML_X2_V1_2)
__code const struct machine machine = {
+#if defined(MACHINE_KP_9000_6XHML_X2_V1_2)
+ .machine_name = "keepLink KP-9000-6XHML V1.2",
+#else
.machine_name = "keepLink KP-9000-6XHML-X2",
+#endif
.isRTL8373 = 0,
.min_port = 3,
.max_port = 8,
@@ -85,9 +89,15 @@ __code const struct machine machine = {
void machine_custom_init(void) { }
-#elif defined MACHINE_KP_9000_6XH_X2
+#elif defined(MACHINE_KP_9000_6XH_X2) || defined(MACHINE_KP_9000_6XH_X2_V2_1) || defined(MACHINE_KP_9000_6XHML_X2_V2_1)
__code const struct machine machine = {
+#if defined(MACHINE_KP_9000_6XHML_X2_V2_1)
+ .machine_name = "keepLink KP-9000-6XHML V2.1",
+#elif defined(MACHINE_KP_9000_6XH_X2_V2_1)
+ .machine_name = "keepLink KP-9000-6XH-X2 V2.1",
+#else
.machine_name = "keepLink KP-9000-6XH-X2",
+#endif
.isRTL8373 = 0,
.min_port = 3,
.max_port = 8,
diff --git a/machine.h b/machine.h
index 931a17e..3483f2e 100644
--- a/machine.h
+++ b/machine.h
@@ -7,8 +7,11 @@
* Select your machine type below
*/
// #define MACHINE_KP_9000_6XHML_X2
+// #define MACHINE_KP_9000_6XHML_X2_V1_2
+// #define MACHINE_KP_9000_6XHML_X2_V2_1
// #define MACHINE_KP_9000_6XH_X
// #define MACHINE_KP_9000_6XH_X2
+// #define MACHINE_KP_9000_6XH_X2_V2_1
// #define MACHINE_KP_9000_9XH_X_EU
// #define MACHINE_KP_9000_9XHML_X_V2_2
// #define MACHINE_KP_9000_9XHML_X_V3_1
From dd52e8e85e077870a0e675f6894eb3b675934b43 Mon Sep 17 00:00:00 2001
From: donbernhardo <87759236+donbernhardo@users.noreply.github.com>
Date: Mon, 29 Jun 2026 14:12:31 +0200
Subject: [PATCH 03/18] Discard incorrect KP-9000 V1.2 LED mux override
---
machine.c | 11 -----------
1 file changed, 11 deletions(-)
diff --git a/machine.c b/machine.c
index a25fe98..d54998b 100644
--- a/machine.c
+++ b/machine.c
@@ -44,17 +44,6 @@ __code const struct machine machine = {
LEDS_1G | LEDS_LINK,
0 },
},
- .led_mux_custom = 1,
- /* Measured front-panel LED pads:
- * P1 amber/green: 9/10, P2: 12/15, P3: 18/21, P4: 24/25, left SFP: 26.
- */
- .led_mux = {
- 0x3f, 0x3f, 0x3f, 0x3f, 0x3f, 0x3f, 0x3f, 0x3f,
- 0x3f, 0x10, 0x12, 0x3f,
- 0x14, 0x3f, 0x3f, 0x16, 0x3f, 0x3f,
- 0x18, 0x3f, 0x3f, 0x1a, 0x3f, 0x3f,
- 0x1c, 0x1e, 0x0c, 0x20
- },
};
void machine_custom_init(void) { }
From 457f117216818b588ab01d397222b4e835135ab9 Mon Sep 17 00:00:00 2001
From: donbernhardo <87759236+donbernhardo@users.noreply.github.com>
Date: Mon, 29 Jun 2026 20:59:28 +0200
Subject: [PATCH 04/18] Add PCB43 V1.1 and symmetric KP-9000 6XH targets
---
doc/devices/KP-9000-6XH-X2.md | 26 ++++++++++++++++----------
doc/supported_devices.md | 8 +++++---
machine.c | 14 ++++++++++++--
machine.h | 13 ++++++++++---
4 files changed, 43 insertions(+), 18 deletions(-)
diff --git a/doc/devices/KP-9000-6XH-X2.md b/doc/devices/KP-9000-6XH-X2.md
index b1c8fdc..d0f19b9 100644
--- a/doc/devices/KP-9000-6XH-X2.md
+++ b/doc/devices/KP-9000-6XH-X2.md
@@ -16,16 +16,20 @@ Using SPI clamp in-board is the only method for initial installation.
### Machine target
These devices exist with different PCB revisions. Select the machine target by
-the PCB silkscreen, not only by the label on the case.
+the PCB silkscreen, not by the label on the case. The `6XH` / `6XHML`
+distinction appears to be a stock firmware, SKU or label difference, not a
+reliable indicator of PCB wiring. The PCB revision defines the hardware layout.
-| PCB silkscreen | Known labels | Recommended machine target | Legacy target |
-| --- | --- | --- | --- |
-| `2M-PCB43-V1.2` | `KP-9000-6XHML-X2` | `MACHINE_KP_9000_6XHML_X2_V1_2` | `MACHINE_KP_9000_6XHML_X2` |
-| `2M-PCB43-V2.1` | `KP-9000-6XH-X2`, `KP-9000-6XHML-X2` | `MACHINE_KP_9000_6XH_X2_V2_1` or `MACHINE_KP_9000_6XHML_X2_V2_1` | `MACHINE_KP_9000_6XH_X2` |
+| PCB silkscreen | Known labels / devices | Recommended machine target | Equivalent target | Legacy target |
+| --- | --- | --- | --- | --- |
+| `2M-PCB43-V1.1` | Mokerlink 2G040210GSM web-managed 4+2 switch | `MACHINE_KP_9000_6XHML_X2_V1_1` | `MACHINE_KP_9000_6XH_X2_V1_1` | `MACHINE_KP_9000_6XHML_X2` |
+| `2M-PCB43-V1.2` | `KP-9000-6XHML-X2` | `MACHINE_KP_9000_6XHML_X2_V1_2` | `MACHINE_KP_9000_6XH_X2_V1_2` | `MACHINE_KP_9000_6XHML_X2` |
+| `2M-PCB43-V2.1` | `KP-9000-6XH-X2`, `KP-9000-6XHML-X2` | `MACHINE_KP_9000_6XH_X2_V2_1` or `MACHINE_KP_9000_6XHML_X2_V2_1` | same V2.1 layout | `MACHINE_KP_9000_6XH_X2` |
-The V1.2 and V2.1 boards use different GPIO, SFP, port and LED mappings.
-The unmanaged and managed labels are firmware/SKU differences and do not by
-themselves identify the PCB wiring.
+The V1.1 and V1.2 boards currently use the same V1.x GPIO, SFP, port and LED
+layout. The V2.1 board uses a different V2.1 layout with custom LED muxing.
+Legacy targets are preserved for compatibility with already-tested devices, but
+new builds should prefer the explicit PCB-revision targets.
### What works
@@ -59,9 +63,11 @@ Bottom
-### Hardware overview: 2M-PCB43-V1.2
+### Hardware overview: 2M-PCB43-V1.1 / V1.2
-The V1.2 board has been seen in managed `KP-9000-6XHML-X2` devices.
+The V1.1 board has been reported in the Mokerlink 2G040210GSM web-managed 4+2
+switch. The V1.2 board has been seen in managed `KP-9000-6XHML-X2` devices.
+Both use the same V1.x layout.
Label:
diff --git a/doc/supported_devices.md b/doc/supported_devices.md
index b367c1e..bdb21c2 100644
--- a/doc/supported_devices.md
+++ b/doc/supported_devices.md
@@ -13,7 +13,8 @@ The following devices have been tested and are fully working:
| Horaco | ZX310S-4T2XH | Yes | [PCB-SL310S-4T1T1X-V1.0.1-24107](https://github.com/logicog/RTLPlayground/blob/main/doc/devices/ZX310S-4T2XH.md) | 2M | 5 + 1 |
| Horaco | ZX310S-4T2XT | Yes | [PCB-SL310S-4T2XT-V1.0.0-22273](https://github.com/logicog/RTLPlayground/blob/main/doc/devices/ZX310S-4T2XT.md) | 2M | 6 |
| Horaco | ZX-SWTG124AS | Yes | [SWTG024AS-v2.0](https://github.com/logicog/RTLPlayground/blob/main/doc/devices/SWTG024AS.md) | | 4 + 2 |
-| Keeplink | KP-9000-6XH-X2 / KP-9000-6XHML-X2 | No/Yes | [2M-PCB43-V1.2 / V2.1](https://github.com/logicog/RTLPlayground/blob/main/doc/devices/KP-9000-6XH-X2.md) | | 4 + 2 |
+| Keeplink | KP-9000-6XH-X2 / KP-9000-6XHML-X2 | No/Yes | [2M-PCB43-V1.2 / V2.1](https://github.com/logicog/RTLPlayground/blob/main/doc/devices/KP-9000-6XH-X2.md) | | 4 + 2 |
+| Mokerlink | 2G040210GSM | Yes | [2M-PCB43-V1.1](https://github.com/logicog/RTLPlayground/blob/main/doc/devices/KP-9000-6XH-X2.md) | | 4 + 2 |
| keepLINK | KP-9000-9XHML-X | Yes | [2M-PCB23-V2.2](https://github.com/logicog/RTLPlayground/blob/main/doc/devices/2M-PCB23-V2_2.md) | 2M | 8 + 1 |
| keepLINK | KP-9000-9XHML-X | Yes | [2M-PCB23-V3.1](https://github.com/logicog/RTLPlayground/blob/main/doc/devices/2M-PCB23-V3_1.md) | 2M | 8 + 1 |
| LIANGUO | SWTG024AS | No | [SWTG024AS-v2.0-17452](https://github.com/logicog/RTLPlayground/blob/main/doc/devices/SWTG024AS.md) | 0.5M | 4 + 2 |
@@ -26,8 +27,9 @@ The following devices have been tested and are fully working:
| XikeStor | SKS3200-8E1X | Yes | [SWTG118AS-V2.1-17462](https://github.com/logicog/RTLPlayground/blob/main/doc/devices/SWTGW218AS.md) | 2M | 8 + 1 |
| Ztyuav | Z-QWYT0402 | No | [PCB-K0402WS-V3.0](https://github.com/logicog/RTLPlayground/blob/main/doc/devices/PCB-K0402WS-V3.0.md) | | 4 + 2 |
-For KP-9000-6XH-X2 / KP-9000-6XHML-X2 devices, select the machine target by PCB
-revision. The managed/unmanaged label alone does not identify the wiring.
+For KP-9000-6XH-X2 / KP-9000-6XHML-X2 / Mokerlink 2G040210GSM devices, select
+the machine target by PCB revision. The ML/non-ML or managed/unmanaged label
+alone does not identify the wiring.
Other device based on RTL8272/3 that may work are described here: [Up-N-Atoms 2.5 GBit RTL Switch hacking guide](https://github.com/up-n-atom/SWTG118AS)
diff --git a/machine.c b/machine.c
index d54998b..35b545d 100644
--- a/machine.c
+++ b/machine.c
@@ -5,9 +5,19 @@
#include "rtl837x_regs.h"
#include "rtl837x_common.h"
-#if defined(MACHINE_KP_9000_6XHML_X2) || defined(MACHINE_KP_9000_6XHML_X2_V1_2)
+#if defined(MACHINE_KP_9000_6XHML_X2) || \
+ defined(MACHINE_KP_9000_6XH_X2_V1_1) || \
+ defined(MACHINE_KP_9000_6XHML_X2_V1_1) || \
+ defined(MACHINE_KP_9000_6XH_X2_V1_2) || \
+ defined(MACHINE_KP_9000_6XHML_X2_V1_2)
__code const struct machine machine = {
-#if defined(MACHINE_KP_9000_6XHML_X2_V1_2)
+#if defined(MACHINE_KP_9000_6XH_X2_V1_1)
+ .machine_name = "keepLink KP-9000-6XH V1.1",
+#elif defined(MACHINE_KP_9000_6XHML_X2_V1_1)
+ .machine_name = "keepLink KP-9000-6XHML V1.1",
+#elif defined(MACHINE_KP_9000_6XH_X2_V1_2)
+ .machine_name = "keepLink KP-9000-6XH V1.2",
+#elif defined(MACHINE_KP_9000_6XHML_X2_V1_2)
.machine_name = "keepLink KP-9000-6XHML V1.2",
#else
.machine_name = "keepLink KP-9000-6XHML-X2",
diff --git a/machine.h b/machine.h
index 5ae75c9..1da0458 100644
--- a/machine.h
+++ b/machine.h
@@ -6,12 +6,19 @@
/*
* Select your machine type below
*/
+// Legacy KP-9000 4+2 targets. Prefer the PCB-revision-specific targets below.
// #define MACHINE_KP_9000_6XHML_X2
-// #define MACHINE_KP_9000_6XHML_X2_V1_2
-// #define MACHINE_KP_9000_6XHML_X2_V2_1
-// #define MACHINE_KP_9000_6XH_X
// #define MACHINE_KP_9000_6XH_X2
+
+// KP-9000 4+2 targets by PCB silkscreen revision.
+// #define MACHINE_KP_9000_6XH_X2_V1_1
+// #define MACHINE_KP_9000_6XHML_X2_V1_1
+// #define MACHINE_KP_9000_6XH_X2_V1_2
+// #define MACHINE_KP_9000_6XHML_X2_V1_2
// #define MACHINE_KP_9000_6XH_X2_V2_1
+// #define MACHINE_KP_9000_6XHML_X2_V2_1
+
+// #define MACHINE_KP_9000_6XH_X
// #define MACHINE_KP_9000_9XH_X_EU
// #define MACHINE_KP_9000_9XHML_X_V2_2
// #define MACHINE_KP_9000_9XHML_X_V3_1
From 425283b77456734119fb8c20c0cc50cf36f2d05a Mon Sep 17 00:00:00 2001
From: bloqaudio
Date: Mon, 24 Aug 2026 23:36:17 -0500
Subject: [PATCH 05/18] machine: host the per-machine custom init hooks in
BANK2
machine_custom_init() runs once at boot, but its code and any tables it
uses were compiled into machine.c and so into the common bank. On the
SWTG018AS-V2.1.0 variant, whose init carries a 21-entry LED register
table, that overflows bank 0 by 0x66 bytes and main no longer links for
MACHINE_PCB_SWTG018AS_V2_1_0; any machine whose init grows can hit the
same wall. SDCC segment pragmas apply file-wide, so the hooks move to a
new machine_init.c compiled into BANK2, and the prototype becomes
banked. machine_check also compiles the new file per machine so the
hooks keep CI syntax coverage.
---
Makefile | 2 +
machine.c | 141 -------------------------------------------------
machine.h | 2 +-
machine_init.c | 139 ++++++++++++++++++++++++++++++++++++++++++++++++
4 files changed, 142 insertions(+), 142 deletions(-)
create mode 100644 machine_init.c
diff --git a/Makefile b/Makefile
index ec66eb6..301f4f0 100644
--- a/Makefile
+++ b/Makefile
@@ -53,6 +53,7 @@ create_build_dir:
# Keep machine.c in first position to fail immediately on invalid $MACHINE value
SRCS = \
machine.c \
+ machine_init.c \
cmd_editor.c \
cmd_parser.c \
dhcp.c \
@@ -142,6 +143,7 @@ machine_check:
do \
echo "Checking $${MACHINE}"; \
$(CC) $(CC_FLAGS) -DMACHINE_$${MACHINE} -MMD -o $(BUILDDIR)/tmp/machine_check -c machine.c; \
+ $(CC) $(CC_FLAGS) -DMACHINE_$${MACHINE} -MMD -o $(BUILDDIR)/tmp/machine_check -c machine_init.c; \
done
@rm -rf $(BUILDDIR)/tmp
diff --git a/machine.c b/machine.c
index d7d4823..fcdb007 100644
--- a/machine.c
+++ b/machine.c
@@ -42,8 +42,6 @@ __code const struct machine machine = {
},
};
-void machine_custom_init(void) { }
-
#elif defined MACHINE_KP_9000_6XH_X
__code const struct machine machine = {
.machine_name = "keepLink KP-9000-6XH-X",
@@ -72,8 +70,6 @@ __code const struct machine machine = {
},
};
-void machine_custom_init(void) { }
-
#elif defined MACHINE_KP_9000_6XH_X2
__code const struct machine machine = {
.machine_name = "keepLink KP-9000-6XH-X2",
@@ -122,10 +118,6 @@ __code const struct machine machine = {
},
};
-void machine_custom_init(void) {
- reg_bit_set(RTL837X_REG_LED_GLB_IO_EN, 6);
-}
-
#elif defined MACHINE_KP_9000_9XH_X_EU
__code const struct machine machine = {
.machine_name = "keepLink KP-9000-9XH-X-EU",
@@ -151,8 +143,6 @@ __code const struct machine machine = {
},
};
-void machine_custom_init(void) { }
-
#elif defined MACHINE_KP_9000_9XHML_X_V2_2
__code const struct machine machine = {
.machine_name = "keepLink KP-9000-9XHML-X V2.2",
@@ -204,8 +194,6 @@ __code const struct machine machine = {
},
};
-void machine_custom_init(void) { }
-
#elif defined MACHINE_KP_9000_9XHML_X_V3_1
__code const struct machine machine = {
.machine_name = "keepLink KP-9000-9XHML-X V3.1",
@@ -241,8 +229,6 @@ __code const struct machine machine = {
0x1a, 0x19, 0x1d, 0x1e, 0x1c, 0x1d, 0x20, 0x21},
};
-void machine_custom_init(void) { }
-
#elif defined MACHINE_SWGT024_V2_0_MANAGED
__code const struct machine machine = {
.machine_name = "SWGT024 V2.0 Managed",
@@ -284,8 +270,6 @@ __code const struct machine machine = {
},
};
-void machine_custom_init(void) { }
-
#elif defined MACHINE_SWGT024_V2_0_UNMANAGED
__code const struct machine machine = {
.machine_name = "SWGT024 V2.0 Unmanaged",
@@ -327,8 +311,6 @@ __code const struct machine machine = {
},
};
-void machine_custom_init(void) { }
-
#elif defined MACHINE_SWTG018AS_A_V_2_0
__code const struct machine machine = {
.machine_name = "SWTG018AS-A V2.0",
@@ -368,8 +350,6 @@ __code const struct machine machine = {
0x1d, 0x20, 0x21 },
};
-void machine_custom_init(void) { }
-
#elif defined MACHINE_HG0402XG_V1_1
__code const struct machine machine = {
.machine_name = "HG0402XG V1.1",
@@ -408,8 +388,6 @@ __code const struct machine machine = {
},
};
-void machine_custom_init(void) { }
-
#elif defined MACHINE_SWTGW218AS
__code const struct machine machine = {
@@ -443,8 +421,6 @@ __code const struct machine machine = {
},
};
-void machine_custom_init(void) { }
-
#elif defined MACHINE_PCB_SWTG018AS_V2_1_0 // Sold as Sodola SL902 / Horaco "SWTGW218AS"; the SWTGW218AS label also covers other PCBs with different SFP and LED wiring (see MACHINE_SWTGW218AS)
__code const struct machine machine = {
@@ -487,44 +463,6 @@ __code const struct machine machine = {
0x1d, 0x20, 0x21 },
};
-// The LED-set encoding cannot express this board's bi-color SFP LED (green <= 2.5G,
-// blue at 10G), so program the LED register block with the values the stock firmware
-// uses. Runs after leds_setup() and overrides the values computed there.
-// The final entry routes the blue-LED pin to the LED controller via PIN_MUX_0;
-// as a GPIO (the default) no LED register can light it. PIN_MUX_1/2 stay
-// untouched so SFP detect (GPIO38) and i2c remain GPIOs.
-static __code const struct { uint16_t reg; uint32_t val; } custom_init_regs[] = {
- { 0x6520, 0x0023e430UL }, // LED_MODE
- { 0x6524, 0xff001400UL }, // LED3_0_SET3
- { 0x6528, 0x00100000UL }, // LED3_0_SET1
- { 0x652c, 0x007f013fUL }, // LED3_2_SET3
- { 0x6530, 0x02000400UL }, // LED1_0_SET3
- { 0x6534, 0x01400141UL }, // LED3_2_SET2
- { 0x6538, 0x01440170UL }, // LED1_0_SET2
- { 0x653c, 0x18000041UL }, // LED3_2_SET1
- { 0x6540, 0x01400155UL }, // LED1_0_SET1
- { 0x6544, 0x01411000UL }, // LED3_2_SET0
- { 0x6548, 0x01740141UL }, // LED1_0_SET0
- { 0x654c, 0x00010000UL }, // LED_PORT_SET_SEL
- { 0x65d8, 0x3ffb6dffUL }, // LED_GLB_ACTIVE
- { 0x65dc, 0x7f24977fUL }, // LED_GLB_IO_EN
- { 0x65e0, 0x08144040UL }, // LED_GLB_MUX_1
- { 0x65e4, 0x10349309UL }, // LED_GLB_MUX_2
- { 0x65e8, 0x12454391UL }, // LED_GLB_MUX_3
- { 0x65ec, 0x19616555UL }, // LED_GLB_MUX_4
- { 0x65f0, 0x1c79d65aUL }, // LED_GLB_MUX_5
- { 0x65f4, 0x0002181dUL }, // LED_GLB_MUX_6
- { 0x7f8c, 0x20db6880UL }, // PIN_MUX_0
-};
-
-void machine_custom_init(void) {
- uint8_t i;
- // REG_SET is a multi-statement macro without a do-while wrapper: braces required
- for (i = 0; i < sizeof(custom_init_regs) / sizeof(custom_init_regs[0]); i++) {
- REG_SET(custom_init_regs[i].reg, custom_init_regs[i].val);
- }
-}
-
#elif defined MACHINE_LIANGUO_ZX_SWTGW215AS // Has PCB branded PCB-SWTG115AS-V2.0 but is labeled and reports as a ZX-SWTGW215AS, seems to be identical to the "real" ZX-SWTGW215AS except for the LEDs
__code const struct machine machine = {
.machine_name = "Lianguo ZX-SWTGW215AS",
@@ -557,8 +495,6 @@ __code const struct machine machine = {
.led_mux_custom = 0,
};
-void machine_custom_init(void) { }
-
#elif defined MACHINE_DEFAULT_8C_1SFP
__code const struct machine machine = {
.machine_name = "8+1 SFP Port Switch",
@@ -584,8 +520,6 @@ __code const struct machine machine = {
},
};
-void machine_custom_init(void) { }
-
#elif defined MACHINE_TRENDNET_TEG_S562
__code const struct machine machine = {
.machine_name = "Trendnet TEG-S562",
@@ -625,8 +559,6 @@ __code const struct machine machine = {
};
-void machine_custom_init(void) { }
-
#elif defined(MACHINE_PCB_K0402WS_V3) || defined(MACHINE_HI_K0402WS) // Sold as a variety of devices, see doc/
__code const struct machine machine = {
.machine_name = "PCB-K0402WS-V3.0",
@@ -673,10 +605,6 @@ __code const struct machine machine = {
},
};
-void machine_custom_init(void) {
- reg_bit_set(RTL837X_REG_LED_GLB_IO_EN, 6);
-}
-
#elif defined MACHINE_K0501W_V2_0
__code const struct machine machine = {
.machine_name = "K0501W V2.0",
@@ -711,8 +639,6 @@ __code const struct machine machine = {
},
};
-void machine_custom_init(void) { }
-
#elif defined MACHINE_ZX310S_4T2XH
__code const struct machine machine = {
.machine_name = "ZX310S-4T2XH",
@@ -756,8 +682,6 @@ __code const struct machine machine = {
},
};
-void machine_custom_init(void) { }
-
#elif defined MACHINE_STEAMEMO_IG204_V1
__code const struct machine machine = {
.machine_name = "Steamemo IG204 V1",
@@ -808,8 +732,6 @@ __code const struct machine machine = {
},
};
-void machine_custom_init(void) { }
-
#elif defined MACHINE_HI_K0801WS
__code const struct machine machine = {
.machine_name = "Hi-Source HI-k0801WS",
@@ -859,8 +781,6 @@ __code const struct machine machine = {
},
};
-void machine_custom_init(void) { }
-
#elif defined MACHINE_FNS1200P
__code const struct machine machine = {
.machine_name = "FNS-1200P",
@@ -916,11 +836,6 @@ __code const struct machine machine = {
},
};
-void machine_custom_init(void)
-{
- reg_bit_set(RTL837X_REG_LED_GLB_IO_EN, 6);
-}
-
#elif defined MACHINE_PCB_SWTG024AS_A_2_0_1
__code const struct machine machine = {
@@ -970,14 +885,6 @@ __code const struct machine machine = {
},
};
-void machine_custom_init(void)
-{
- reg_bit_set(RTL837X_REG_LED_GLB_IO_EN, 6);
- reg_bit_set(RTL837X_REG_LED_MODE, 17);
- reg_bit_clear(RTL837X_REG_LED_MODE, 9);
- reg_bit_clear(RTL837X_REG_LED_MODE, 7);
-}
-
#elif defined MACHINE_SWTG024AS_A_2_0_1_5C_1SFP
__code const struct machine machine = {
.machine_name = "SWTG024AS-A-V2.0.1-5C-1SFP",
@@ -1021,25 +928,6 @@ __code const struct machine machine = {
},
};
-void machine_custom_init(void)
-{
- uint16_t pval;
-
- reg_bit_set(RTL837X_REG_LED_GLB_IO_EN, 6);
- reg_bit_set(RTL837X_REG_LED_MODE, 17);
- reg_bit_clear(RTL837X_REG_LED_MODE, 9);
- reg_bit_clear(RTL837X_REG_LED_MODE, 7);
-
- // OEM firmware sets these companion SDS0 polarity bits for the RTL8221B.
- sds_read(0, 0, 0);
- pval = SFR_DATA_U16;
- sds_write_v(0, 0, 0, pval | 0x100);
-
- sds_read(0, 6, 2);
- pval = SFR_DATA_U16;
- sds_write_v(0, 6, 2, pval | 0x4000);
-}
-
#elif defined MACHINE_SWTG024AS_V2_0
__code const struct machine machine = {
.machine_name = "SWTG024AS-V2.0",
@@ -1083,25 +971,6 @@ __code const struct machine machine = {
},
};
-void machine_custom_init(void)
-{
- uint16_t pval;
-
- reg_bit_set(RTL837X_REG_LED_GLB_IO_EN, 6);
- reg_bit_set(RTL837X_REG_LED_MODE, 17);
- reg_bit_clear(RTL837X_REG_LED_MODE, 9);
- reg_bit_clear(RTL837X_REG_LED_MODE, 7);
-
- // OEM firmware sets these companion SDS0 polarity bits for the RTL8221B.
- sds_read(0, 0, 0);
- pval = SFR_DATA_U16;
- sds_write_v(0, 0, 0, pval | 0x100);
-
- sds_read(0, 6, 2);
- pval = SFR_DATA_U16;
- sds_write_v(0, 6, 2, pval | 0x4000);
-}
-
#elif defined MACHINE_ZX310S_4T2XT
__code const struct machine machine = {
.machine_name = "ZX310S_4T2XT",
@@ -1139,12 +1008,6 @@ __code const struct machine machine = {
},
};
-void machine_custom_init(void) {
- // For this device, the reset value of RTL837X_PIN_MUX_0 is 0x30000000,
- // which would disables all LEDS, enable them manually:
- REG_SET(RTL837X_PIN_MUX_0, 0x30db68bf);
-}
-
#elif defined MACHINE_FG_4GT_2SX_V2_0
__code const struct machine machine = {
.machine_name = "FG-4GT-2SX_V2.0",
@@ -1211,10 +1074,6 @@ __code const struct machine machine = {
},
};
-void machine_custom_init(void) {
- REG_SET(RTL837X_REG_LED_GLB_IO_EN, 0x7624155b);
-}
-
#else
#error "Please select a machine type in machine.h"
#endif
diff --git a/machine.h b/machine.h
index 29fe39f..ae467cb 100644
--- a/machine.h
+++ b/machine.h
@@ -95,6 +95,6 @@ typedef struct machine_runtime
uint8_t isN : 1;
};
-void machine_custom_init(void);
+void machine_custom_init(void) __banked;
#endif
diff --git a/machine_init.c b/machine_init.c
new file mode 100644
index 0000000..e5669ed
--- /dev/null
+++ b/machine_init.c
@@ -0,0 +1,139 @@
+/*
+ * Per-machine one-shot boot hooks, hosted in BANK2 so board-specific
+ * tables and code do not consume the common bank.
+ */
+#include
+#include "machine.h"
+#include "rtl837x_pins.h"
+#include "rtl837x_leds.h"
+#include "rtl837x_sfr.h"
+#include "rtl837x_regs.h"
+#include "rtl837x_common.h"
+
+#pragma codeseg BANK2
+#pragma constseg BANK2
+
+#if defined MACHINE_KP_9000_6XH_X2
+void machine_custom_init(void) __banked
+{
+ reg_bit_set(RTL837X_REG_LED_GLB_IO_EN, 6);
+}
+
+#elif defined MACHINE_PCB_SWTG018AS_V2_1_0
+// The LED-set encoding cannot express this board's bi-color SFP LED (green <= 2.5G,
+// blue at 10G), so program the LED register block with the values the stock firmware
+// uses. Runs after leds_setup() and overrides the values computed there.
+// The final entry routes the blue-LED pin to the LED controller via PIN_MUX_0;
+// as a GPIO (the default) no LED register can drive it. PIN_MUX_1/2 stay
+// untouched so SFP detect (GPIO38) and i2c remain GPIOs.
+static __code const struct { uint16_t reg; uint32_t val; } custom_init_regs[] = {
+ { 0x6520, 0x0023e430UL }, // LED_MODE
+ { 0x6524, 0xff001400UL }, // LED3_0_SET3
+ { 0x6528, 0x00100000UL }, // LED3_0_SET1
+ { 0x652c, 0x007f013fUL }, // LED3_2_SET3
+ { 0x6530, 0x02000400UL }, // LED1_0_SET3
+ { 0x6534, 0x01400141UL }, // LED3_2_SET2
+ { 0x6538, 0x01440170UL }, // LED1_0_SET2
+ { 0x653c, 0x18000041UL }, // LED3_2_SET1
+ { 0x6540, 0x01400155UL }, // LED1_0_SET1
+ { 0x6544, 0x01411000UL }, // LED3_2_SET0
+ { 0x6548, 0x01740141UL }, // LED1_0_SET0
+ { 0x654c, 0x00010000UL }, // LED_PORT_SET_SEL
+ { 0x65d8, 0x3ffb6dffUL }, // LED_GLB_ACTIVE
+ { 0x65dc, 0x7f24977fUL }, // LED_GLB_IO_EN
+ { 0x65e0, 0x08144040UL }, // LED_GLB_MUX_1
+ { 0x65e4, 0x10349309UL }, // LED_GLB_MUX_2
+ { 0x65e8, 0x12454391UL }, // LED_GLB_MUX_3
+ { 0x65ec, 0x19616555UL }, // LED_GLB_MUX_4
+ { 0x65f0, 0x1c79d65aUL }, // LED_GLB_MUX_5
+ { 0x65f4, 0x0002181dUL }, // LED_GLB_MUX_6
+ { 0x7f8c, 0x20db6880UL }, // PIN_MUX_0
+};
+
+void machine_custom_init(void) __banked
+{
+ uint8_t i;
+ // REG_SET is a multi-statement macro without a do-while wrapper: braces required
+ for (i = 0; i < sizeof(custom_init_regs) / sizeof(custom_init_regs[0]); i++) {
+ REG_SET(custom_init_regs[i].reg, custom_init_regs[i].val);
+ }
+}
+
+#elif defined(MACHINE_PCB_K0402WS_V3) || defined(MACHINE_HI_K0402WS)
+void machine_custom_init(void) __banked
+{
+ reg_bit_set(RTL837X_REG_LED_GLB_IO_EN, 6);
+}
+
+#elif defined MACHINE_FNS1200P
+void machine_custom_init(void) __banked
+{
+ reg_bit_set(RTL837X_REG_LED_GLB_IO_EN, 6);
+}
+
+#elif defined MACHINE_PCB_SWTG024AS_A_2_0_1
+void machine_custom_init(void) __banked
+{
+ reg_bit_set(RTL837X_REG_LED_GLB_IO_EN, 6);
+ reg_bit_set(RTL837X_REG_LED_MODE, 17);
+ reg_bit_clear(RTL837X_REG_LED_MODE, 9);
+ reg_bit_clear(RTL837X_REG_LED_MODE, 7);
+}
+
+#elif defined MACHINE_SWTG024AS_A_2_0_1_5C_1SFP
+void machine_custom_init(void) __banked
+{
+ uint16_t pval;
+
+ reg_bit_set(RTL837X_REG_LED_GLB_IO_EN, 6);
+ reg_bit_set(RTL837X_REG_LED_MODE, 17);
+ reg_bit_clear(RTL837X_REG_LED_MODE, 9);
+ reg_bit_clear(RTL837X_REG_LED_MODE, 7);
+
+ // OEM firmware sets these companion SDS0 polarity bits for the RTL8221B.
+ sds_read(0, 0, 0);
+ pval = SFR_DATA_U16;
+ sds_write_v(0, 0, 0, pval | 0x100);
+
+ sds_read(0, 6, 2);
+ pval = SFR_DATA_U16;
+ sds_write_v(0, 6, 2, pval | 0x4000);
+}
+
+#elif defined MACHINE_SWTG024AS_V2_0
+void machine_custom_init(void) __banked
+{
+ uint16_t pval;
+
+ reg_bit_set(RTL837X_REG_LED_GLB_IO_EN, 6);
+ reg_bit_set(RTL837X_REG_LED_MODE, 17);
+ reg_bit_clear(RTL837X_REG_LED_MODE, 9);
+ reg_bit_clear(RTL837X_REG_LED_MODE, 7);
+
+ // OEM firmware sets these companion SDS0 polarity bits for the RTL8221B.
+ sds_read(0, 0, 0);
+ pval = SFR_DATA_U16;
+ sds_write_v(0, 0, 0, pval | 0x100);
+
+ sds_read(0, 6, 2);
+ pval = SFR_DATA_U16;
+ sds_write_v(0, 6, 2, pval | 0x4000);
+}
+
+#elif defined MACHINE_ZX310S_4T2XT
+void machine_custom_init(void) __banked
+{
+ // For this device, the reset value of RTL837X_PIN_MUX_0 is 0x30000000,
+ // which would disables all LEDS, enable them manually:
+ REG_SET(RTL837X_PIN_MUX_0, 0x30db68bf);
+}
+
+#elif defined MACHINE_FG_4GT_2SX_V2_0
+void machine_custom_init(void) __banked
+{
+ REG_SET(RTL837X_REG_LED_GLB_IO_EN, 0x7624155b);
+}
+
+#else
+void machine_custom_init(void) __banked { }
+#endif
From 1a743b3889316761b71d8df5e26c0d0c2d22df8d Mon Sep 17 00:00:00 2001
From: bloqaudio
Date: Mon, 24 Aug 2026 23:36:51 -0500
Subject: [PATCH 06/18] leds: reduce the SWTG018AS-V2.1.0 custom init to the
effective registers
Measured against the generic leds_setup() output on the board: eight of
the 21 stock values are identical to what leds_setup() computes from
the machine's led_mux table, and eight more revert to the generic
values with no change in LED behaviour in any tested link state (copper
2.5G, SFP 2.5G, SFP 10G). Keep the five with a measurable effect: 6528
selects blue over green at 10G, 6540/6548 carry the SFP and copper LED
set behaviour, 65dc enables the LED outputs, and PIN_MUX_0 routes the
blue pin.
Verified from a clean boot on a SWTG018AS-V2.1.0 board: copper solid
green with activity blink at 2.5G on two ports, SFP green at 2.5G,
SFP blue at 10G, LEDs off on link down.
---
machine_init.c | 35 ++++++++---------------------------
1 file changed, 8 insertions(+), 27 deletions(-)
diff --git a/machine_init.c b/machine_init.c
index e5669ed..215c21d 100644
--- a/machine_init.c
+++ b/machine_init.c
@@ -20,34 +20,15 @@ void machine_custom_init(void) __banked
}
#elif defined MACHINE_PCB_SWTG018AS_V2_1_0
-// The LED-set encoding cannot express this board's bi-color SFP LED (green <= 2.5G,
-// blue at 10G), so program the LED register block with the values the stock firmware
-// uses. Runs after leds_setup() and overrides the values computed there.
-// The final entry routes the blue-LED pin to the LED controller via PIN_MUX_0;
-// as a GPIO (the default) no LED register can drive it. PIN_MUX_1/2 stay
-// untouched so SFP detect (GPIO38) and i2c remain GPIOs.
+// Stock-firmware values for what the LED-set encoding cannot express: the
+// bi-color SFP LED (blue pin at 10G) and the PIN_MUX_0 routing of that pin
+// to the LED controller. Runs after leds_setup(), which covers the rest.
static __code const struct { uint16_t reg; uint32_t val; } custom_init_regs[] = {
- { 0x6520, 0x0023e430UL }, // LED_MODE
- { 0x6524, 0xff001400UL }, // LED3_0_SET3
- { 0x6528, 0x00100000UL }, // LED3_0_SET1
- { 0x652c, 0x007f013fUL }, // LED3_2_SET3
- { 0x6530, 0x02000400UL }, // LED1_0_SET3
- { 0x6534, 0x01400141UL }, // LED3_2_SET2
- { 0x6538, 0x01440170UL }, // LED1_0_SET2
- { 0x653c, 0x18000041UL }, // LED3_2_SET1
- { 0x6540, 0x01400155UL }, // LED1_0_SET1
- { 0x6544, 0x01411000UL }, // LED3_2_SET0
- { 0x6548, 0x01740141UL }, // LED1_0_SET0
- { 0x654c, 0x00010000UL }, // LED_PORT_SET_SEL
- { 0x65d8, 0x3ffb6dffUL }, // LED_GLB_ACTIVE
- { 0x65dc, 0x7f24977fUL }, // LED_GLB_IO_EN
- { 0x65e0, 0x08144040UL }, // LED_GLB_MUX_1
- { 0x65e4, 0x10349309UL }, // LED_GLB_MUX_2
- { 0x65e8, 0x12454391UL }, // LED_GLB_MUX_3
- { 0x65ec, 0x19616555UL }, // LED_GLB_MUX_4
- { 0x65f0, 0x1c79d65aUL }, // LED_GLB_MUX_5
- { 0x65f4, 0x0002181dUL }, // LED_GLB_MUX_6
- { 0x7f8c, 0x20db6880UL }, // PIN_MUX_0
+ { RTL837X_REG_LED3_0_SET1, 0x00100000UL },
+ { RTL837X_REG_LED1_0_SET1, 0x01400155UL },
+ { RTL837X_REG_LED1_0_SET0, 0x01740141UL },
+ { RTL837X_REG_LED_GLB_IO_EN, 0x7f24977fUL },
+ { RTL837X_PIN_MUX_0, 0x20db6880UL },
};
void machine_custom_init(void) __banked
From 801150431b4ea0e3e3659fa61661834b2e88972d Mon Sep 17 00:00:00 2001
From: bloqaudio
Date: Tue, 25 Aug 2026 09:28:54 -0500
Subject: [PATCH 07/18] stp: host the spanning tree module in BANK2
All entry points are already __banked and none of the code runs in
interrupt context, so the module can leave the resident bank. This
relieves pressure on bank 0, which no longer fits a machine with a
custom init table.
---
rtl837x_stp.c | 5 +++++
1 file changed, 5 insertions(+)
diff --git a/rtl837x_stp.c b/rtl837x_stp.c
index 2eb4f92..116c64c 100644
--- a/rtl837x_stp.c
+++ b/rtl837x_stp.c
@@ -14,6 +14,11 @@
#include "uip.h"
#include "machine.h"
+// All entry points are __banked and nothing here runs from an interrupt,
+// so the module does not need to stay in the resident bank
+#pragma codeseg BANK2
+#pragma constseg BANK2
+
extern __code struct machine machine;
extern __xdata uint8_t sfr_data[4];
From 57b820c4e872a45d9ed8f9f5db14e24954563613 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Ren=C3=A9=20van=20Dorst?=
Date: Wed, 5 Aug 2026 15:37:25 +0200
Subject: [PATCH 08/18] httpd: mark pointer variable as __xdata. Otherwise it
is put on SRAM location.
Saves 6 SRAM bytes
---
httpd/httpd.c | 8 ++++----
1 file changed, 4 insertions(+), 4 deletions(-)
diff --git a/httpd/httpd.c b/httpd/httpd.c
index 411ba0c..6ff824e 100644
--- a/httpd/httpd.c
+++ b/httpd/httpd.c
@@ -50,8 +50,8 @@ __xdata uint8_t config_upload;
__xdata uint8_t config_buf[CONFIG_UPLOAD_BUF];
__xdata uint16_t cfg_pos, cfg_hdr, cfg_body, cfg_end, cfg_last;
__xdata uint8_t cfg_bl;
-__xdata uint8_t *content_type = 0;
-__xdata uint8_t *session = 0;
+__xdata uint8_t * __xdata content_type = 0;
+__xdata uint8_t * __xdata session = 0;
// Global variables holding POST state
__xdata uint16_t bindex; // Current index into the boundary
@@ -63,7 +63,7 @@ __xdata char passwd[21];
__xdata char session_id[SESSION_ID_LENGTH + 1];
__xdata uint8_t authenticated;
__xdata uint32_t now;
-__xdata uint8_t *timeptr;
+__xdata uint8_t * __xdata timeptr;
__xdata uint32_t last_session_use;
#define TSTATE_NONE 0
@@ -311,7 +311,7 @@ __xdata uint8_t *scan_header(__xdata uint8_t *p)
}
-void gen_random_bytes(__xdata uint8_t *b, uint8_t bytes)
+void gen_random_bytes(__xdata uint8_t *b, register uint8_t bytes)
{
__xdata uint8_t i = 0;
while (bytes) {
From 683037f41003e076e1e6ce6f888eba2794a4e895 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Ren=C3=A9=20van=20Dorst?=
Date: Wed, 5 Aug 2026 16:13:09 +0200
Subject: [PATCH 09/18] httpd: rename gen_random_bytes to gen_random_hex_chars
This reflexs the function better.
Moving `byte` arguments to __xdata which saves 1 SRAM byte.
---
httpd/httpd.c | 10 ++++++----
1 file changed, 6 insertions(+), 4 deletions(-)
diff --git a/httpd/httpd.c b/httpd/httpd.c
index 6ff824e..a97bb52 100644
--- a/httpd/httpd.c
+++ b/httpd/httpd.c
@@ -310,10 +310,12 @@ __xdata uint8_t *scan_header(__xdata uint8_t *p)
return p;
}
-
-void gen_random_bytes(__xdata uint8_t *b, register uint8_t bytes)
+/*
+ * Generate random HEX-chars at the buffer location.
+ */
+void gen_random_hex_chars(__xdata uint8_t * b, __xdata uint8_t bytes)
{
- __xdata uint8_t i = 0;
+ uint8_t i = 0;
while (bytes) {
if (!i)
get_random_32();
@@ -549,7 +551,7 @@ void handle_post(void)
if (is_url_word_x(p, passwd)) {
dbg_string("Password accepted!\n");
read_reg_timer(&last_session_use);
- gen_random_bytes(session_id, SESSION_ID_LENGTH);
+ gen_random_hex_chars(session_id, SESSION_ID_LENGTH);
session_id[SESSION_ID_LENGTH] = NUL;
slen = strtox(outbuf, "HTTP/1.1 302 Found\r\nConnection: close\r\nLocation: index.html\r\n" \
"Set-Cookie: session=");
From 301774040b67a9c58f9d5745f28f5e19606fbefb Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Ren=C3=A9=20van=20Dorst?=
Date: Wed, 5 Aug 2026 17:05:36 +0200
Subject: [PATCH 10/18] uip: place some pointer in xdata.
Saves 2 bytes.
---
uip/uip.c | 6 +++---
uip/uip.h | 6 +++---
2 files changed, 6 insertions(+), 6 deletions(-)
diff --git a/uip/uip.c b/uip/uip.c
index 6a194c1..0304a17 100644
--- a/uip/uip.c
+++ b/uip/uip.c
@@ -157,7 +157,7 @@ __xdata u16_t uip_len, uip_slen;
__xdata u8_t uip_flags; /* The uip_flags variable is used for
communication between the TCP/IP stack
and the application program. */
-__xdata struct uip_conn *uip_conn; /* uip_conn always points to the current
+__xdata struct uip_conn * __xdata uip_conn; /* uip_conn always points to the current
connection. */
__xdata struct uip_conn uip_conns[UIP_CONNS];
@@ -167,7 +167,7 @@ __xdata u16_t uip_listenports[UIP_LISTENPORTS];
/* The uip_listenports list all currently
listning ports. */
#if UIP_UDP
-__xdata struct uip_udp_conn *uip_udp_conn;
+__xdata struct uip_udp_conn * __xdata uip_udp_conn;
__xdata struct uip_udp_conn uip_udp_conns[UIP_UDP_CONNS];
#endif /* UIP_UDP */
@@ -466,7 +466,7 @@ uip_connect(register __xdata uip_ipaddr_t *ripaddr, __xdata u16_t rport) __banke
/*---------------------------------------------------------------------------*/
#if UIP_UDP
__xdata struct uip_udp_conn *
-uip_udp_new(__xdata uip_ipaddr_t *ripaddr, __xdata u16_t rport) __banked
+uip_udp_new(__xdata uip_ipaddr_t * __xdata ripaddr, __xdata u16_t rport) __banked
{
__xdata struct uip_udp_conn *conn;
diff --git a/uip/uip.h b/uip/uip.h
index 7cd12e2..5f4d8e2 100644
--- a/uip/uip.h
+++ b/uip/uip.h
@@ -763,7 +763,7 @@ void uip_send(__xdata const void *data, __xdata uint16_t len) __banked;
* \return The uip_udp_conn structure for the new connection or NULL
* if no connection could be allocated.
*/
-__xdata struct uip_udp_conn *uip_udp_new(__xdata uip_ipaddr_t *ripaddr, __xdata u16_t rport) __banked;
+__xdata struct uip_udp_conn *uip_udp_new(__xdata uip_ipaddr_t * __xdata ripaddr, __xdata u16_t rport) __banked;
/**
* Removed a UDP connection.
@@ -1193,7 +1193,7 @@ struct uip_conn {
* The uip_conn pointer can be used to access the current TCP
* connection.
*/
-extern __xdata struct uip_conn *uip_conn;
+extern __xdata struct uip_conn * __xdata uip_conn;
/* The array containing all uIP connections. */
extern __xdata struct uip_conn uip_conns[UIP_CONNS];
/**
@@ -1226,7 +1226,7 @@ struct uip_udp_conn {
/**
* The current UDP connection.
*/
-extern __xdata struct uip_udp_conn *uip_udp_conn;
+extern __xdata struct uip_udp_conn * __xdata uip_udp_conn;
extern __xdata struct uip_udp_conn uip_udp_conns[UIP_UDP_CONNS];
#endif /* UIP_UDP */
From adfbead438a6ed0490d40df11123556fbe78d2c2 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Ren=C3=A9=20van=20Dorst?=
Date: Wed, 5 Aug 2026 20:39:45 +0200
Subject: [PATCH 11/18] syslog: syslog_callback() put local variables on xdata.
Save atleast 2 bytes.
---
syslog.c | 8 ++++----
1 file changed, 4 insertions(+), 4 deletions(-)
diff --git a/syslog.c b/syslog.c
index 989e8cc..021ef72 100644
--- a/syslog.c
+++ b/syslog.c
@@ -62,19 +62,19 @@ void syslog_callback(uint16_t lport) __banked
if ((state.readptr != state.writeptr) && state.line_available)
{
- int16_t log_size = state.writeptr - state.readptr;
+ __xdata int16_t log_size = state.writeptr - state.readptr;
if (log_size < 0)
log_size += LOGBUF_SIZE;
// Skipping linefeeds at the start of the log line
- uint16_t log_start = state.readptr;
+ __xdata uint16_t log_start = state.readptr;
while (log_size > 0 && logbuf[log_start] == '\n') {
log_start = (log_start + 1) & (LOGBUF_SIZE - 1);
log_size--;
}
// Skipping linefeeds and whitespaces at the end of the log line
- uint16_t log_end = state.writeptr;
+ __xdata uint16_t log_end = state.writeptr;
while ( (log_size > 0) &&
((logbuf[(log_end-1) & (LOGBUF_SIZE - 1)] == '\n') ||
(logbuf[(log_end-1) & (LOGBUF_SIZE - 1)] == ' ')))
@@ -95,7 +95,7 @@ void syslog_callback(uint16_t lport) __banked
memcpy(SYSLOG_P + 4, logbuf + log_start, LOGBUF_SIZE - log_start);
memcpy(SYSLOG_P + 4 + LOGBUF_SIZE - log_start, logbuf, log_end);
} else {
- memcpy(SYSLOG_P + 4, logbuf + log_start, log_end - log_start);
+ memcpy(SYSLOG_P + 4, logbuf + log_start, log_end - log_start);
}
uip_udp_send(log_size+4);
From 7cf0115182978c2f36ff1981c845db0ed956ca44 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Ren=C3=A9=20van=20Dorst?=
Date: Wed, 5 Aug 2026 21:07:49 +0200
Subject: [PATCH 12/18] httpd: scan_header() put argument on xdata
Saves 2 bytes
---
httpd/httpd.c | 4 ++--
1 file changed, 2 insertions(+), 2 deletions(-)
diff --git a/httpd/httpd.c b/httpd/httpd.c
index a97bb52..eb90878 100644
--- a/httpd/httpd.c
+++ b/httpd/httpd.c
@@ -175,7 +175,7 @@ bool is_url_word_x(__xdata uint8_t *uri_str_p, __xdata uint8_t *src_str_p)
}
-bool is_word_x(__xdata uint8_t *lhs_str_p, __xdata uint8_t *rhs_str_p)
+bool is_word_x(__xdata uint8_t * lhs_str_p, __xdata uint8_t * rhs_str_p)
{
uint8_t u, c;
@@ -251,7 +251,7 @@ __xdata uint8_t *skip_boundary(__xdata uint8_t *p)
}
-__xdata uint8_t *scan_header(__xdata uint8_t *p)
+__xdata uint8_t *scan_header(__xdata uint8_t * __xdata p)
{
content_type = 0;
session = 0;
From 907ad088307ba9aedace6b6dcf86b067faca3ed1 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Ren=C3=A9=20van=20Dorst?=
Date: Thu, 13 Aug 2026 21:59:40 +0200
Subject: [PATCH 13/18] uip: uip_arp_update() put more arguments in xdata
Saves 7-bytes.
---
uip/uip_arp.c | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/uip/uip_arp.c b/uip/uip_arp.c
index 8b8bd00..44bee6c 100644
--- a/uip/uip_arp.c
+++ b/uip/uip_arp.c
@@ -175,7 +175,7 @@ uip_arp_timer(void) __banked
}
/*-----------------------------------------------------------------------------------*/
static void
-uip_arp_update(__xdata u16_t *ipaddr, __xdata struct uip_eth_addr *ethaddr)
+uip_arp_update(__xdata u16_t * __xdata ipaddr, __xdata struct uip_eth_addr * __xdata ethaddr)
{
uint8_t i;
/* Walk through the ARP mapping table and try to find an entry to
From bf0bea1eddac5d93f02ac1efa7b911f1fb945475 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Ren=C3=A9=20van=20Dorst?=
Date: Wed, 26 Aug 2026 21:37:42 +0200
Subject: [PATCH 14/18] Remove register keyword from all the function
arguments.
Adds no value.
---
httpd/httpd.c | 2 +-
httpd/page_impl.c | 10 +++++-----
installer/installer.c | 2 +-
rtl837x_common.h | 14 +++++++-------
rtl837x_port.c | 10 +++++-----
rtl837x_port.h | 10 +++++-----
rtlplayground.c | 16 ++++++++--------
uip/psock.c | 20 +++++++++-----------
uip/psock.h | 4 ++--
uip/uip.c | 4 ++--
10 files changed, 45 insertions(+), 47 deletions(-)
diff --git a/httpd/httpd.c b/httpd/httpd.c
index 411ba0c..45fdfb2 100644
--- a/httpd/httpd.c
+++ b/httpd/httpd.c
@@ -553,7 +553,7 @@ void handle_post(void)
session_id[SESSION_ID_LENGTH] = NUL;
slen = strtox(outbuf, "HTTP/1.1 302 Found\r\nConnection: close\r\nLocation: index.html\r\n" \
"Set-Cookie: session=");
- for (register uint8_t i = 0; i < SESSION_ID_LENGTH; i++)
+ for (uint8_t i = 0; i < SESSION_ID_LENGTH; i++)
outbuf[slen++] = session_id[i];
slen += strtox(outbuf + slen, "; SameSite=Strict\r\n\r\n");
} else {
diff --git a/httpd/page_impl.c b/httpd/page_impl.c
index d55d4fe..2f94568 100644
--- a/httpd/page_impl.c
+++ b/httpd/page_impl.c
@@ -157,14 +157,14 @@ void sfr_data_to_html(void)
}
-void reg_to_html(register uint16_t reg)
+void reg_to_html(uint16_t reg)
{
reg_read_m(reg);
sfr_data_to_html();
}
-void reg_to_html_long(register uint16_t reg)
+void reg_to_html_long(uint16_t reg)
{
reg_read_m(reg);
byte_to_html(sfr_data[0]);
@@ -697,13 +697,13 @@ void send_status(void)
sfp_send_data(sfp, 238, 1);
}
slen += strtox(outbuf + slen,"\",\"sfp_vendor\":\"");
- for (register uint8_t s = 0; s < 16 && sfp_module_vendor[sfp][s]; s++)
+ for (uint8_t s = 0; s < 16 && sfp_module_vendor[sfp][s]; s++)
outbuf[slen++] = sfp_module_vendor[sfp][s];
slen += strtox(outbuf + slen,"\",\"sfp_model\":\"");
- for (register uint8_t s = 0; s < 16 && sfp_module_model[sfp][s]; s++)
+ for (uint8_t s = 0; s < 16 && sfp_module_model[sfp][s]; s++)
outbuf[slen++] = sfp_module_model[sfp][s];
slen += strtox(outbuf + slen,"\",\"sfp_serial\":\"");
- for (register uint8_t s = 0; s < 16 && sfp_module_serial[sfp][s]; s++)
+ for (uint8_t s = 0; s < 16 && sfp_module_serial[sfp][s]; s++)
outbuf[slen++] = sfp_module_serial[sfp][s];
slen += strtox(outbuf + slen,"\",\"sfp_los\":");
if (machine.sfp_port[sfp].pin_los == GPIO_NA) {
diff --git a/installer/installer.c b/installer/installer.c
index 675a142..f86b782 100644
--- a/installer/installer.c
+++ b/installer/installer.c
@@ -213,7 +213,7 @@ uint8_t flash_read_status(void)
* Reads bulk data of length len from the flash memory starging at address src
* and writes the data into a buffer pointed to by dst in XMEM
*/
-void flash_read_bulk(register __xdata uint8_t *dst, __xdata uint32_t src, register uint16_t len)
+void flash_read_bulk(__xdata uint8_t *dst, __xdata uint32_t src, uint16_t len)
{
short status;
do {
diff --git a/rtl837x_common.h b/rtl837x_common.h
index 432260e..c3d83cb 100644
--- a/rtl837x_common.h
+++ b/rtl837x_common.h
@@ -161,13 +161,13 @@ void sfr_mask_data(uint8_t n, uint8_t mask, uint8_t set);
void sfr_set_zero(void);
void reset_chip(void);
void memcpy(__xdata void * __xdata dst, __xdata const void * __xdata src, uint16_t len);
-void memcpyc(register __xdata uint8_t *dst, register __code uint8_t *src, register uint16_t len);
-void memset(register __xdata uint8_t *dst, register __xdata uint8_t v, register uint8_t len);
-uint16_t strlen(register __code const char *s);
-uint16_t strlen_x(register __xdata const char *s);
-uint16_t strtox(register __xdata uint8_t *dst, register __code const char *s);
-uint16_t strcpy(register __xdata uint8_t *dst, register const char *s);
-char strcmp(register __xdata const uint8_t *a, register __code const uint8_t *b);
+void memcpyc(__xdata uint8_t *dst, __code uint8_t *src, uint16_t len);
+void memset(__xdata uint8_t *dst, __xdata uint8_t v, uint8_t len);
+uint16_t strlen(__code const char *s);
+uint16_t strlen_x(__xdata const char *s);
+uint16_t strtox(__xdata uint8_t *dst, __code const char *s);
+uint16_t strcpy(__xdata uint8_t *dst, const char *s);
+char strcmp(__xdata const uint8_t *a, __code const uint8_t *b);
bool strstart(__xdata const uint8_t *a, __code const uint8_t *b);
bool strstart_x(__xdata const uint8_t *a, __xdata const uint8_t *b);
void tcpip_output(void);
diff --git a/rtl837x_port.c b/rtl837x_port.c
index 103ac4e..808521f 100644
--- a/rtl837x_port.c
+++ b/rtl837x_port.c
@@ -30,7 +30,7 @@ __xdata uint32_t l2_head;
__xdata struct vlan_settings vlan_settings;
-void port_mirror_set(register uint8_t port, __xdata uint16_t rx_pmask, __xdata uint16_t tx_pmask) __banked
+void port_mirror_set(uint8_t port, __xdata uint16_t rx_pmask, __xdata uint16_t tx_pmask) __banked
{
print_string("\nport_mirror_set called \n");
print_string("Mirroring port: "); print_byte(port); print_string(" with rx-mask: ");
@@ -162,7 +162,7 @@ void vlan_name_remove(uint16_t vlan) __banked
* Reads VLAN information from VLAN table
* Returns data in sfr_data
*/
-int8_t vlan_get(register uint16_t vlan) __banked
+int8_t vlan_get(uint16_t vlan) __banked
{
if (vlan >= 0xfff) // VLAN 4095 is special
return -1;
@@ -177,7 +177,7 @@ int8_t vlan_get(register uint16_t vlan) __banked
}
-__xdata uint16_t vlan_name(register uint16_t vlan) __banked
+__xdata uint16_t vlan_name(uint16_t vlan) __banked
{
__xdata int16_t i = 0;
__xdata uint8_t begin = 1;
@@ -505,14 +505,14 @@ void port_stats_print(void) __banked
}
-void port_isolate(register uint8_t port, __xdata uint16_t pmask) __banked
+void port_isolate(uint8_t port, __xdata uint16_t pmask) __banked
{
if (port <= machine.max_port)
REG_SET(RTL837X_PORT_ISOLATION_BASE + (port << 2), pmask);
}
-uint16_t port_isolation_get(register uint8_t port) __banked
+uint16_t port_isolation_get(uint8_t port) __banked
{
if (port > machine.max_port)
return 0;
diff --git a/rtl837x_port.h b/rtl837x_port.h
index b459832..b9bc84a 100644
--- a/rtl837x_port.h
+++ b/rtl837x_port.h
@@ -48,8 +48,8 @@ extern __xdata struct vlan_settings vlan_settings;
uint8_t port_l2_forget(void) __banked;
void port_l2_learned(void) __banked;
void port_stats_print(void) __banked;
-int8_t vlan_get(register uint16_t vlan) __banked;
-__xdata uint16_t vlan_name(register uint16_t vlan) __banked;
+int8_t vlan_get(uint16_t vlan) __banked;
+__xdata uint16_t vlan_name(uint16_t vlan) __banked;
void vlan_name_remove(uint16_t vlan) __banked;
void vlan_setup(void) __banked;
void port_pvid_set(uint8_t port, __xdata uint16_t pvid) __banked;
@@ -57,7 +57,7 @@ uint16_t port_pvid_get(uint8_t port) __banked;
void vlan_create(void) __banked;
void vlan_delete(uint16_t vlan) __banked;
void vlan_dump(void) __banked;
-void port_mirror_set(register uint8_t port, __xdata uint16_t rx_pmask, __xdata uint16_t tx_pmask) __banked;
+void port_mirror_set(uint8_t port, __xdata uint16_t rx_pmask, __xdata uint16_t tx_pmask) __banked;
void port_mirror_del(void) __banked;
bool port_ingress_filter(__xdata uint8_t port, __xdata vlan_ingress_mode_t type) __banked;
void port_l2_setup(void) __banked;
@@ -73,7 +73,7 @@ void port_eee_status(uint8_t port) __banked;
void print_port_ingress_filter_mode(vlan_ingress_mode_t mode) __banked;
bool port_ingress_vlan_filter_set(__xdata uint8_t port, __xdata bool enabled) __banked;
bool port_ingress_vlan_filter_get(__xdata uint8_t port) __banked;
-void port_isolate(register uint8_t port, __xdata uint16_t pmask) __banked;
-uint16_t port_isolation_get(register uint8_t port) __banked;
+void port_isolate(uint8_t port, __xdata uint16_t pmask) __banked;
+uint16_t port_isolation_get(uint8_t port) __banked;
#endif
diff --git a/rtlplayground.c b/rtlplayground.c
index f0a3292..cd5e14e 100644
--- a/rtlplayground.c
+++ b/rtlplayground.c
@@ -311,20 +311,20 @@ void memcpy(__xdata void * __xdata dst, __xdata const void * __xdata src, uint16
*d++ = *s++;
}
-void memcpyc(register __xdata uint8_t *dst, register __code uint8_t *src, register uint16_t len)
+void memcpyc(__xdata uint8_t *dst, __code uint8_t *src, uint16_t len)
{
while (len--)
*dst++ = *src++;
}
-void memset(register __xdata uint8_t *dst, register __xdata uint8_t v, register uint8_t len)
+void memset(__xdata uint8_t *dst, __xdata uint8_t v, uint8_t len)
{
while (len--)
*dst++ = v;
}
-uint16_t strtox(register __xdata uint8_t *dst, register __code const char *s)
+uint16_t strtox(__xdata uint8_t *dst, __code const char *s)
{
__xdata uint8_t *b = dst;
while (*s)
@@ -334,7 +334,7 @@ uint16_t strtox(register __xdata uint8_t *dst, register __code const char *s)
}
-uint16_t strlen(register __code const char *s)
+uint16_t strlen(__code const char *s)
{
uint16_t l = 0;
while (s[l])
@@ -343,7 +343,7 @@ uint16_t strlen(register __code const char *s)
}
-uint16_t strlen_x(register __xdata const char *s)
+uint16_t strlen_x(__xdata const char *s)
{
uint16_t l = 0;
while (s[l])
@@ -352,7 +352,7 @@ uint16_t strlen_x(register __xdata const char *s)
}
-char strcmp(register __xdata const uint8_t *a, register __code const uint8_t *b)
+char strcmp(__xdata const uint8_t *a, __code const uint8_t *b)
{
uint8_t i = 0;
@@ -667,7 +667,7 @@ void nic_rx_header(uint16_t ring_ptr)
* data will be returned in the xmem buffer points to
* ring_ptr is the current position of the RX Ring on the ASIC side
*/
-void nic_rx_packet(register uint16_t buffer, register uint16_t ring_ptr)
+void nic_rx_packet(uint16_t buffer, uint16_t ring_ptr)
{
SFR_NIC_DATA_U16LE = buffer;
SFR_NIC_RING_U16LE = ring_ptr;
@@ -1220,7 +1220,7 @@ void handle_tx(void)
}
-static inline uint8_t sfp_rate_to_sds_config(register uint8_t rate)
+static inline uint8_t sfp_rate_to_sds_config(uint8_t rate)
{
if (rate == 0x1 || rate == 0x2)
return SDS_100FX;
diff --git a/uip/psock.c b/uip/psock.c
index eb4d1af..f1c1115 100644
--- a/uip/psock.c
+++ b/uip/psock.c
@@ -75,8 +75,7 @@
/*---------------------------------------------------------------------------*/
inline void
-buf_setup(register __xdata struct psock_buf *buf,
- register __xdata u8_t *bufptr, register u16_t bufsize)
+buf_setup(__xdata struct psock_buf *buf, __xdata u8_t *bufptr, u16_t bufsize)
{
buf->ptr = bufptr;
buf->left = bufsize;
@@ -84,7 +83,7 @@ buf_setup(register __xdata struct psock_buf *buf,
/*---------------------------------------------------------------------------*/
inline u8_t
-buf_bufdata(register __xdata struct psock_buf *buf, register __xdata u8_t **dataptr, register __xdata u16_t *datalen)
+buf_bufdata(__xdata struct psock_buf *buf, __xdata u8_t **dataptr, __xdata u16_t *datalen)
{
if(*datalen < buf->left) {
memcpy(buf->ptr, *dataptr, *datalen);
@@ -145,7 +144,7 @@ buf_bufto(__xdata struct psock_buf *buf, u8_t endmarker,
}
/*---------------------------------------------------------------------------*/
static char
-send_data(register __xdata struct psock *s)
+send_data(__xdata struct psock *s)
{
if(s->state != STATE_DATA_SENT || uip_rexmit()) {
if(s->sendlen > uip_mss()) {
@@ -160,7 +159,7 @@ send_data(register __xdata struct psock *s)
}
/*---------------------------------------------------------------------------*/
static char
-data_acked(register __xdata struct psock *s)
+data_acked(__xdata struct psock *s)
{
if(s->state == STATE_DATA_SENT && uip_acked()) {
if(s->sendlen > uip_mss()) {
@@ -176,8 +175,7 @@ data_acked(register __xdata struct psock *s)
return 0;
}
/*---------------------------------------------------------------------------*/
-PT_THREAD(psock_send(register __xdata struct psock *s, register __xdata const char *buf,
- register uint16_t len))
+PT_THREAD(psock_send(__xdata struct psock *s, __xdata const char *buf, uint16_t len))
{
PT_BEGIN(&s->psockpt);
@@ -218,7 +216,7 @@ PT_THREAD(psock_send(register __xdata struct psock *s, register __xdata const ch
/*---------------------------------------------------------------------------*/
-// PT_THREAD(psock_generator_send(register __xdata struct psock *s,
+// PT_THREAD(psock_generator_send(__xdata struct psock *s,
// unsigned short (*generate)(void *), void *arg))
// {
// PT_BEGIN(&s->psockpt);
@@ -275,7 +273,7 @@ psock_newdata(__xdata struct psock *s)
}
}
/*---------------------------------------------------------------------------*/
-PT_THREAD(psock_readto(register __xdata struct psock *psock, unsigned char c))
+PT_THREAD(psock_readto(__xdata struct psock *psock, unsigned char c))
{
PT_BEGIN(&psock->psockpt);
@@ -304,7 +302,7 @@ PT_THREAD(psock_readto(register __xdata struct psock *psock, unsigned char c))
PT_END(&psock->psockpt);
}
/*---------------------------------------------------------------------------*/
-PT_THREAD(psock_readbuf(register __xdata struct psock *psock))
+PT_THREAD(psock_readbuf(__xdata struct psock *psock))
{
PT_BEGIN(&psock->psockpt);
@@ -334,7 +332,7 @@ PT_THREAD(psock_readbuf(register __xdata struct psock *psock))
}
/*---------------------------------------------------------------------------*/
void
-psock_init(register __xdata struct psock *psock, register __xdata char *buffer, register uint16_t buffersize)
+psock_init(__xdata struct psock *psock, __xdata char *buffer, uint16_t buffersize)
{
psock->state = STATE_NONE;
psock->readlen = 0;
diff --git a/uip/psock.h b/uip/psock.h
index a4967d2..3a6e7e4 100644
--- a/uip/psock.h
+++ b/uip/psock.h
@@ -124,7 +124,7 @@ struct psock {
u8_t state; /* The state of the protosocket. */
};
-void psock_init(__xdata struct psock *psock, register __xdata char *buffer, register uint16_t buffersize);
+void psock_init(__xdata struct psock *psock, __xdata char *buffer, uint16_t buffersize);
/**
* Initialize a protosocket.
*
@@ -158,7 +158,7 @@ void psock_init(__xdata struct psock *psock, register __xdata char *buffer, regi
*/
#define PSOCK_BEGIN(psock) PT_BEGIN(&((psock)->pt))
-PT_THREAD(psock_send(register __xdata struct psock *psock, register __xdata const char *buf, register uint16_t len));
+PT_THREAD(psock_send(__xdata struct psock *psock, __xdata const char *buf, uint16_t len));
/**
* Send data.
*
diff --git a/uip/uip.c b/uip/uip.c
index 6a194c1..27a22be 100644
--- a/uip/uip.c
+++ b/uip/uip.c
@@ -400,7 +400,7 @@ uip_init(void) __banked
/*---------------------------------------------------------------------------*/
#if UIP_ACTIVE_OPEN
__xdata struct uip_conn *
-uip_connect(register __xdata uip_ipaddr_t *ripaddr, __xdata u16_t rport) __banked
+uip_connect(__xdata uip_ipaddr_t *ripaddr, __xdata u16_t rport) __banked
{
__xdata struct uip_conn *conn, *cconn;
@@ -677,7 +677,7 @@ uip_add_rcv_nxt(u16_t n)
void
uip_process(u8_t flag) __banked
{
- register __xdata struct uip_conn *uip_connr = uip_conn;
+ __xdata struct uip_conn *uip_connr = uip_conn;
#if UIP_UDP
if(flag == UIP_UDP_SEND_CONN) {
From 1bd4a8d3cc84decd77c3b6f6a7a67f161605c196 Mon Sep 17 00:00:00 2001
From: bloqaudio
Date: Fri, 21 Aug 2026 08:49:29 -0500
Subject: [PATCH 15/18] httpd: buffer a firmware upload's part header before
streaming
A firmware image cannot be buffered whole, so the upload is streamed to
flash, and stream_upload() already resumes across TCP segments. The
multipart preamble did not: handle_post() walked the part headers from the
start of whichever segment it held, so a client that split inside the
octet-stream part header lost its place, never started streaming, and the
request hung. Firefox splits exactly there, right after filename=".
Buffer the multipart body only until the octet-stream part header is
complete, locate the payload, then stream from that point; later segments
stream as before. The header reuses the configuration buffer, which is idle
during a firmware upload, so no extra memory is needed.
---
httpd/httpd.c | 73 +++++++++++++++++++++++++++------------------------
1 file changed, 39 insertions(+), 34 deletions(-)
diff --git a/httpd/httpd.c b/httpd/httpd.c
index 84413a9..585eb62 100644
--- a/httpd/httpd.c
+++ b/httpd/httpd.c
@@ -49,6 +49,7 @@ __xdata uint8_t boundary[72];
__xdata uint8_t config_upload;
__xdata uint8_t config_buf[CONFIG_UPLOAD_BUF];
__xdata uint16_t cfg_pos, cfg_hdr, cfg_body, cfg_end, cfg_last;
+__xdata uint16_t pre_acc;
__xdata uint8_t cfg_bl;
__xdata uint8_t * __xdata content_type = 0;
__xdata uint8_t * __xdata session = 0;
@@ -240,17 +241,6 @@ void send_unauthorized(void)
}
-__xdata uint8_t *skip_boundary(__xdata uint8_t *p)
-{
- while (*p) {
- if (is_word_x(p, boundary))
- return p + strlen_x(boundary);
- p++;
- }
- return p;
-}
-
-
__xdata uint8_t *scan_header(__xdata uint8_t * __xdata p)
{
content_type = 0;
@@ -386,21 +376,38 @@ static uint8_t config_take(void)
}
+// unlike scan_header(), keeps no auth state, so it may run on every buffered segment
+static uint16_t preamble_payload_start(__xdata uint16_t n)
+{
+ for (cfg_pos = 0; cfg_pos + 24 <= n; cfg_pos++) {
+ if (strstart(&config_buf[cfg_pos], "application/octet-stream"))
+ break;
+ }
+ if (cfg_pos + 24 > n)
+ return 0;
+ cfg_pos += 24;
+ while (cfg_pos + 3 < n && !strstart(&config_buf[cfg_pos], "\r\n\r\n"))
+ cfg_pos++;
+ if (cfg_pos + 3 >= n)
+ return 0;
+ return cfg_pos + 4;
+}
+
+
/*
* Reads post data from the http stream and writes it into flash memory
* Input: the current position in the TCP buffer (uip_appdata)
* Returns 1: More data to read, 0: Upload complete, all parts reads
*/
-uint8_t stream_upload(uint16_t bptr)
+uint8_t stream_upload(__xdata uint8_t *p, uint16_t bptr, uint16_t plen)
{
- __xdata uint8_t *p = uip_appdata;
__xdata struct httpd_state * __xdata s = &(uip_conn->appstate);
dbg_string("Stream_upload called: ");
dbg_short(bptr); dbg_char('\n');
do {
- if (bptr >= uip_len) {
+ if (bptr >= plen) {
s->tstate = TSTATE_POST;
return 1;
}
@@ -431,7 +438,7 @@ uint8_t stream_upload(uint16_t bptr)
flash_region.addr = uptr;
flash_region.len = 1;
flash_write_bytes(flash_buf);
- if (bptr >= uip_len)
+ if (bptr >= plen)
return 0;
if(!verify_crc)
//ugly hack to signal connection finished after config upload.
@@ -512,6 +519,7 @@ void handle_post(void)
uptr = FIRMWARE_UPLOAD_START;
verify_crc = 1;
max_upload = 1024576;
+ pre_acc = 0;
} else if (is_word(request_path, "config")) {
if (!authenticated) {
send_unauthorized();
@@ -600,21 +608,21 @@ void handle_post(void)
slen = strtox(outbuf, "HTTP/1.1 200 OK\r\nConnection: close\r\n\r\n");
return;
}
- // We skip the intial parts as part of the header
- do {
- p = skip_boundary(p);
- if (!*p) {
- s->tstate = TSTATE_MULTIPART;
- return;
- }
- p = scan_header(p);
- if (!*p)
- goto bad_request;
- if (!content_type) // We are waiting for the part with the octet stream
- continue;
- } while (!is_word(content_type, "application/octet-stream"));
+ cfg_pos = uip_len - (p - uip_appdata);
+ if (pre_acc + cfg_pos >= CONFIG_UPLOAD_BUF) {
+ print_string("Firmware upload header too large, aborting.\n");
+ s->tstate = TSTATE_NONE;
+ send_bad_request();
+ return;
+ }
+ memcpy(config_buf + pre_acc, p, cfg_pos);
+ pre_acc += cfg_pos;
+ cfg_end = preamble_payload_start(pre_acc);
+ if (!cfg_end) {
+ s->tstate = TSTATE_MULTIPART;
+ return;
+ }
dbg_string("Have content octets\n");
- p += 4; // Skip \r\n\r\n sequence at end of preamble of part
flash_init(0); // Re-initialize flash for non-DIO operation, otherwise flashing fails
set_sys_led_state(SYS_LED_FAST);
@@ -622,7 +630,7 @@ void handle_post(void)
crc_value = 0;
bindex = 0;
write_len = 0;
- stream_upload(p - uip_appdata);
+ stream_upload(config_buf, cfg_end, pre_acc);
dbg_string("Done reading first fragment\n");
return;
@@ -633,9 +641,6 @@ void handle_post(void)
}
slen = strtox(outbuf, "HTTP/1.1 200 OK\r\nConnection: close\r\n\r\n");
return;
-bad_request:
- send_bad_request();
- return;
}
@@ -703,7 +708,7 @@ void httpd_appcall(void)
} else if (uip_newdata() && s->tstate == TSTATE_POST) {
// Check here maxupload by subtracting uip_len and close socekt if fails!
if (max_upload - uip_len > 0) {
- stream_upload(0);
+ stream_upload(uip_appdata, 0, uip_len);
write_char('.');
} else {
send_bad_request();
From be7796a22c011e2940737ce6af33bf87caef83b1 Mon Sep 17 00:00:00 2001
From: bloqaudio
Date: Tue, 25 Aug 2026 09:06:56 -0500
Subject: [PATCH 16/18] httpd: send a real verdict for firmware uploads
A firmware upload previously ended in a silent connection close, leaving
the client unable to distinguish a verified upload from a failed one.
Send an explicit 200/400 verdict with the CRC result, with
Content-Length so the browser completes the response before the reset,
and only reset the chip once the verdict has been fully ACKed.
The unconditional close after a config upload is gone since the
buffered config path answers with its own response, so drop the
now-unreachable close hack from the streaming path.
---
httpd/httpd.c | 36 ++++++++++++++++++++++++++++--------
1 file changed, 28 insertions(+), 8 deletions(-)
diff --git a/httpd/httpd.c b/httpd/httpd.c
index 585eb62..fa80b01 100644
--- a/httpd/httpd.c
+++ b/httpd/httpd.c
@@ -61,6 +61,9 @@ __xdata uint32_t max_upload;
__xdata uint16_t short_parsed;
__xdata char passwd[21];
+// Set when a verified firmware upload awaits its response ACK, after
+// which the chip resets to apply the staged image
+__xdata uint8_t fw_reset_pending;
__xdata char session_id[SESSION_ID_LENGTH + 1];
__xdata uint8_t authenticated;
__xdata uint32_t now;
@@ -92,6 +95,7 @@ void httpd_init(void) __banked
// Start listening to port 80
uip_listen(HTONS(80));
s->tstate = TSTATE_CLOSED;
+ fw_reset_pending = 0; // xdata is not zeroed by the startup code
}
@@ -420,17 +424,23 @@ uint8_t stream_upload(__xdata uint8_t *p, uint16_t bptr, uint16_t plen)
flash_write_bytes(flash_buf);
uptr += write_len;
write_len = 0;
- // TODO: This is a bit premature, what about a nice web-page saying the device will reset???
if (verify_crc) {
dbg_string("CRC16: "); dbg_short(crc_final); dbg_char('\n');
+ // Both bodies are 33 bytes; Content-Length lets the
+ // browser complete the response without waiting for
+ // the connection close (which a reset would swallow)
if (crc_final == 0xb001) {
print_string("Checksum OK.\nUpload to flash done, will reset!\n");
- // close connection to avoid retries by browser
- uip_close();
- reset_chip();
+ slen = strtox(outbuf, "HTTP/1.1 200 OK\r\nContent-Length: 33\r\n"
+ "Content-Type: text/plain\r\n\r\n"
+ "OK: checksum verified, rebooting\n");
+ // Reset once the response is fully ACKed
+ fw_reset_pending = 1;
} else {
print_string("Checksum incorrect! Aborting.\n");
- uip_close();
+ slen = strtox(outbuf, "HTTP/1.1 400 Bad Request\r\nContent-Length: 33\r\n"
+ "Content-Type: text/plain\r\n\r\n"
+ "NO: checksum failed, not applied\n");
}
}
// Make sure there is a 0 at the end of the uploaded data
@@ -440,9 +450,6 @@ uint8_t stream_upload(__xdata uint8_t *p, uint16_t bptr, uint16_t plen)
flash_write_bytes(flash_buf);
if (bptr >= plen)
return 0;
- if(!verify_crc)
- //ugly hack to signal connection finished after config upload.
- uip_close();
return 1;
}
if (p[bptr] == boundary[bindex]) {
@@ -630,6 +637,10 @@ void handle_post(void)
crc_value = 0;
bindex = 0;
write_len = 0;
+ // A verdict is only built once the upload part completes;
+ // clear any stale response so the completion check in the
+ // appcall POST branch cannot send leftovers
+ slen = 0;
stream_upload(config_buf, cfg_end, pre_acc);
dbg_string("Done reading first fragment\n");
@@ -704,11 +715,20 @@ void httpd_appcall(void)
cont_len -= slen;
cont_addr += slen;
s->tstate = TSTATE_TX;
+ } else if (fw_reset_pending) {
+ // The upload verdict has been fully ACKed by the client;
+ // now it is safe to reset and apply the staged image
+ print_string("Resetting to apply update\n");
+ reset_chip();
}
} else if (uip_newdata() && s->tstate == TSTATE_POST) {
// Check here maxupload by subtracting uip_len and close socekt if fails!
if (max_upload - uip_len > 0) {
stream_upload(uip_appdata, 0, uip_len);
+ // A completed part with a built verdict must go out
+ // through the normal TX path
+ if (s->tstate == TSTATE_NONE && slen)
+ goto do_send;
write_char('.');
} else {
send_bad_request();
From b63117ba6c59417e373c70d16d57fcc466f29218 Mon Sep 17 00:00:00 2001
From: bloqaudio
Date: Thu, 27 Aug 2026 08:51:01 -0500
Subject: [PATCH 17/18] httpd: scan the upload preamble with a register cursor
The preamble scan cursor and the buffered-length parameter carry no
state between calls, so they do not need static xdata slots. As plain
locals the compiler places both in registers, trimming 64 bytes of
BANK1 code and two bytes of xdata.
The offsets shared by config_take() stay static: direct data space is
fully allocated on machines like the SWTGW218AS, so plain locals there
add overlay bytes that no longer link, and xdata-class locals spill
three temporaries into direct space while growing the code by roughly
120 bytes. Document pre_acc, whose accumulation across TCP segments is
why it must remain global.
---
httpd/httpd.c | 21 ++++++++++++---------
1 file changed, 12 insertions(+), 9 deletions(-)
diff --git a/httpd/httpd.c b/httpd/httpd.c
index fa80b01..0e73f57 100644
--- a/httpd/httpd.c
+++ b/httpd/httpd.c
@@ -49,6 +49,7 @@ __xdata uint8_t boundary[72];
__xdata uint8_t config_upload;
__xdata uint8_t config_buf[CONFIG_UPLOAD_BUF];
__xdata uint16_t cfg_pos, cfg_hdr, cfg_body, cfg_end, cfg_last;
+// part-header bytes buffered so far; accumulates across TCP segments
__xdata uint16_t pre_acc;
__xdata uint8_t cfg_bl;
__xdata uint8_t * __xdata content_type = 0;
@@ -381,20 +382,22 @@ static uint8_t config_take(void)
// unlike scan_header(), keeps no auth state, so it may run on every buffered segment
-static uint16_t preamble_payload_start(__xdata uint16_t n)
+static uint16_t preamble_payload_start(uint16_t n)
{
- for (cfg_pos = 0; cfg_pos + 24 <= n; cfg_pos++) {
- if (strstart(&config_buf[cfg_pos], "application/octet-stream"))
+ uint16_t pos;
+
+ for (pos = 0; pos + 24 <= n; pos++) {
+ if (strstart(&config_buf[pos], "application/octet-stream"))
break;
}
- if (cfg_pos + 24 > n)
+ if (pos + 24 > n)
return 0;
- cfg_pos += 24;
- while (cfg_pos + 3 < n && !strstart(&config_buf[cfg_pos], "\r\n\r\n"))
- cfg_pos++;
- if (cfg_pos + 3 >= n)
+ pos += 24;
+ while (pos + 3 < n && !strstart(&config_buf[pos], "\r\n\r\n"))
+ pos++;
+ if (pos + 3 >= n)
return 0;
- return cfg_pos + 4;
+ return pos + 4;
}
From e826180d068d0bd3a43ff471f89564ae90861c9a Mon Sep 17 00:00:00 2001
From: bloqaudio
Date: Sun, 30 Aug 2026 02:15:06 -0500
Subject: [PATCH 18/18] httpd: pass stream_upload() state through an xdata
struct
---
httpd/httpd.c | 37 +++++++++++++++++++++++++------------
1 file changed, 25 insertions(+), 12 deletions(-)
diff --git a/httpd/httpd.c b/httpd/httpd.c
index 0e73f57..23fef5d 100644
--- a/httpd/httpd.c
+++ b/httpd/httpd.c
@@ -401,20 +401,27 @@ static uint16_t preamble_payload_start(uint16_t n)
}
+// Source window for stream_upload(); filled by the caller before the call
+__xdata struct {
+ __xdata uint8_t *p;
+ uint16_t bptr;
+ uint16_t plen;
+} upload_settings;
+
/*
* Reads post data from the http stream and writes it into flash memory
- * Input: the current position in the TCP buffer (uip_appdata)
+ * Input: upload_settings, set by the caller
* Returns 1: More data to read, 0: Upload complete, all parts reads
*/
-uint8_t stream_upload(__xdata uint8_t *p, uint16_t bptr, uint16_t plen)
+uint8_t stream_upload(void)
{
__xdata struct httpd_state * __xdata s = &(uip_conn->appstate);
dbg_string("Stream_upload called: ");
- dbg_short(bptr); dbg_char('\n');
+ dbg_short(upload_settings.bptr); dbg_char('\n');
do {
- if (bptr >= plen) {
+ if (upload_settings.bptr >= upload_settings.plen) {
s->tstate = TSTATE_POST;
return 1;
}
@@ -451,15 +458,15 @@ uint8_t stream_upload(__xdata uint8_t *p, uint16_t bptr, uint16_t plen)
flash_region.addr = uptr;
flash_region.len = 1;
flash_write_bytes(flash_buf);
- if (bptr >= plen)
+ if (upload_settings.bptr >= upload_settings.plen)
return 0;
return 1;
}
- if (p[bptr] == boundary[bindex]) {
+ if (upload_settings.p[upload_settings.bptr] == boundary[bindex]) {
if (!bindex)
crc_final = crc_value;
- crc16(p + bptr);
- bptr++;
+ crc16(upload_settings.p + upload_settings.bptr);
+ upload_settings.bptr++;
bindex++;
} else {
if (bindex) {
@@ -467,8 +474,8 @@ uint8_t stream_upload(__xdata uint8_t *p, uint16_t bptr, uint16_t plen)
write_len += bindex;
bindex = 0;
}
- crc16(p + bptr);
- flash_buf[write_len++] = p[bptr++];
+ crc16(upload_settings.p + upload_settings.bptr);
+ flash_buf[write_len++] = upload_settings.p[upload_settings.bptr++];
if (write_len >= FLASH_PAGE_SIZE) {
dbg_string("len: "); dbg_short(write_len); dbg_char(' ');
dbg_string("CRC16: "); dbg_short(crc_value); dbg_char('\n');
@@ -644,7 +651,10 @@ void handle_post(void)
// clear any stale response so the completion check in the
// appcall POST branch cannot send leftovers
slen = 0;
- stream_upload(config_buf, cfg_end, pre_acc);
+ upload_settings.p = config_buf;
+ upload_settings.bptr = cfg_end;
+ upload_settings.plen = pre_acc;
+ stream_upload();
dbg_string("Done reading first fragment\n");
return;
@@ -727,7 +737,10 @@ void httpd_appcall(void)
} else if (uip_newdata() && s->tstate == TSTATE_POST) {
// Check here maxupload by subtracting uip_len and close socekt if fails!
if (max_upload - uip_len > 0) {
- stream_upload(uip_appdata, 0, uip_len);
+ upload_settings.p = uip_appdata;
+ upload_settings.bptr = 0;
+ upload_settings.plen = uip_len;
+ stream_upload();
// A completed part with a built verdict must go out
// through the normal TX path
if (s->tstate == TSTATE_NONE && slen)