31 Commits
Author SHA1 Message Date
bloq a202842107 Merge pull request #597 from DrDoof/web/port-strip-topbar
web: keep the port strip in the header on every tab
2026-10-16 22:57:37 -05:00
d00f 6fad2836ae web: keep the port strip on one row
The strip no longer wraps. Ports shrink to fit the row and scroll
when they cannot, so the header on a phone stays at two rows. The
strip moves under the title only below 720 px, where it no longer
fits beside it. Drop the unused d_ports strings and the strip title.
2026-10-17 00:07:04 +02:00
d00f 2415015a87 web: keep the port strip in the header on every tab
The row of port icons lived in a card on the Dashboard, so link
state and speed were out of sight on every other tab. Move it into the
header between the title and the save button, a little smaller, and
poll /status.json whenever the page is open instead of only on the
Dashboard, Ports and Statistics tabs; the existing idle and hidden-tab
gating still pauses it. On narrow screens the strip wraps to its own
row under the title.
2026-10-17 00:06:27 +02:00
René van Dorst d9fc20addf Merge pull request #565 from DrDoof/feat/mac-count
port: show how many MAC addresses each port has learned
2026-10-16 21:57:26 +00:00
d00f c15b2d48da html: call the MAC count column Devices learned
The column counts the addresses the switch learns from incoming frames,
so "Connected" suggested a connection it does not show. The translations
follow the words the L2 tab already uses for learned entries.
2026-10-16 23:34:02 +02:00
René van Dorst f17ca1c9fc Merge pull request #626 from vankxr/device/gt-st024m
Add GoodTop GT-ST024M device
2026-10-16 21:01:59 +00:00
René van Dorst be5dee04e3 Merge pull request #619 from hlyi/machine_refactoring_doc
update doc based upon new machine file structure (from #612)
2026-10-16 20:39:53 +00:00
logicog 496ac8b757 Merge pull request #576 from bloqaudio/feat/cable-test
port: add a cable test command
2026-10-16 17:41:31 +02:00
logicog bf670aed0b Merge pull request #580 from avicarmeli/fix/startup-crlf
Fix CRLF handling in startup configuration parser
2026-10-16 17:28:37 +02:00
logicog 6af5580433 Merge pull request #612 from hlyi/machine_doc_update
create doc for adding-a-device
2026-10-16 17:22:18 +02:00
logicog 6194356f66 Merge pull request #630 from DrDoof/fix/atoi-hex-bounds
cmd_parser: stop atoi_hex() at eight digits and at non-hex characters
2026-10-16 17:20:28 +02:00
logicog bd56c1a8c5 Merge pull request #572 from vDorst/feat_swgt024_v20_mac_local
MACHINE_SWGT024_V2_0_MANAGED: Add mac_flash_offset
2026-10-15 21:33:04 +02:00
d00f d562fd661e cmd_parser: stop atoi_hex() at eight digits and at non-hex characters
hexvalue[] holds four bytes, but atoi_hex() stored every digit it was
given, so a ninth digit was written past the array into whatever follows
it in xdata. More than eight digits now return 0, as any bad input does.

c - '0' < 10 is computed as an int, so characters below '0' such as '-',
'.' or '/' passed as digits and "-1" came out as 0xdf. The difference is
now taken as uint8_t, so they are rejected.
2026-10-15 20:38:55 +02:00
René van Dorst 0b6b613c03 MACHINE_SWGT024_V2_0_MANAGED: Add mac_flash_offset 2026-10-15 18:30:46 +02:00
João Silva f325bdbd11 Add GT-ST024M
Signed-off-by: João Silva <jgc3silva@gmail.com>
2026-10-15 12:55:05 +01:00
HL Yi adecc26ae2 update doc based upoin logicog's feedback 2026-10-14 21:31:26 -05:00
HL Yi 44ae7a8d3b move modification of current files into a diff PR 2026-10-14 20:37:57 -05:00
HL Yi c3fc541339 update doc based upon new machine structure 2026-10-14 20:23:35 -05:00
logicog 28ea2184c2 Merge pull request #562 from brunoz78/de-isolation
i18n(de): translate the remaining untranslated texts (isolation, DNS, NTP, LLDP, LACP)
2026-10-14 21:15:41 +02:00
Brunoz78andClaude Opus 5.5 d5f84cc2fb i18n(de): translate the LACP settings of the LAG page
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-14 21:09:46 +02:00
Brunoz78andClaude Opus 5.5 682ac462c4 i18n(de): translate the LLDP page
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-14 21:09:46 +02:00
Brunoz78andClaude Opus 5.5 ecc8356f4d i18n(de): translate the NTP settings
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-14 21:09:46 +02:00
Brunoz78andClaude Opus 5.5 3a8d1d323f i18n(de): translate the DNS server settings
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-14 21:09:46 +02:00
Brunoz78andClaude Opus 5.5 e125ebd097 i18n(de): translate the port isolation page
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-14 21:09:46 +02:00
bloqaudio 4050555667 port: add a cable test command
"port <port> cabletest" runs the cable diagnostic of the copper PHYs
and reports one line per pair: ok, or open, short, short to another
pair or impedance mismatch with the distance to the fault.

The PHY part follows the cable_test_start and cable_test_get_status
ops of the Linux phy_driver and takes the port number:
rtl_phy_cable_test_start() triggers the test and
rtl_phy_cable_test_get_status() returns true once all four pairs are
done, with a result code and a length per pair. The decode order is
the one of the Linux driver. The same two functions drive the RTL8224
and the internal PHYs of the RTL8372 and RTL8373.

The command only powers the port down and returns. A state machine
called from handle_tick() calls the start op after one second, polls
the status op, stores the result, clears the trigger, also after a
timeout, and restores the control register, so the main loop never
waits. The result is printed when it is ready and "port <port>
cabletest show" prints it again. One test runs at a time.

The result flags are the low byte of the per-pair status word. The
vendor code reads the high byte, which is always zero on the RTL8373.

A link partner that keeps transmitting makes the PHY report an
inter-pair short on every pair, and forcing the speed with
autonegotiation off, as the Linux driver does, does not take the link
down. The port is therefore powered down for a second before the
test. The control register is restored afterwards with an
autonegotiation restart, so speed settings survive and the link
returns.

A test started on a port that is switched off never finishes and
leaves the PHY unusable until the switch is reset, so such a port is
refused, and a test is dropped if the port is switched off during the
wait. SFP ports are refused by the existing check. The external copper
PHYs of the RTL8372 boards are refused because the test has not been
run on one.

The distance is the raw length word minus 150, at 43 counts per 50 cm.
Fitted on open cables of 0.9 m, 2 m and 7.6 m; the pairs of one cable
differ by about 0.2 m. The Linux formula, raw minus 620 at 78 counts
per metre, gives 0, 0 and 2.4 m for these cables.

An unplugged port has no defined result: some pairs report open at
about 0.5 m and others a short or a mismatch at 2 to 3 m.
2026-10-14 12:42:27 -05:00
hlyi e890e429a5 Update adding-a-device.md 2026-10-14 00:22:08 -05:00
HL Yi 813a62ca7d add document for adding a new device. clean up existing document due to split machin.c 2026-10-14 00:11:01 -05:00
d00f 8a7c847ad9 tools: httpd_sim serves the MAC count per port
The simulator answered /status.json without the "macs" field the Ports page now reads. It counts the dynamic entries of its own L2 table for the port, so the column and /l2.json agree and the two static entries show the difference.
2026-10-13 01:35:03 +02:00
d00f b94d2aeabc port: count the learned MACs in the Connected column
The column listed the MAC addresses of the L2 table entries of a port, which cost a chain of paged /l2.json requests every tenth status poll. The status JSON carries the count now, so the column reads it from there and the page does not touch the L2 table at all.

The register counts the dynamic entries, so the static ones the list used to include are not in the number.
2026-10-13 01:27:33 +02:00
avicarmeli 0e85137e7a fix: accept CR delimiters in flash startup configuration 2026-10-12 10:43:36 +03:00
DrDoof 706dea63a2 port: show how many MAC addresses each port has learned
The switch keeps a count of the dynamic L2 entries learned on every
port in L2_LRN_PORT_CONSTRT_CNT (0x53ac + 4 * port, 13 bits). `stat`
prints it in a new MACs column and /status.json carries it as "macs".

On a SWTGW218AS the count matched the L2 table port by port, static
entries are not included, and taking a port down drops it to 0.
2026-10-10 01:11:11 +02:00
26 changed files with 599 additions and 662 deletions

No files matched your search

+4 -5
View File
@@ -66,7 +66,6 @@ create_build_dir:
mkdir -p "$(BUILDDIR)/uip"
mkdir -p "$(BUILDDIR)/httpd"
mkdir -p "$(BUILDDIR)/machine"
mkdir -p "$(BUILDDIR)/crypto"
# Every machine is a pair machine/<MACHINE>.c + machine/<MACHINE>.h. Selecting
# them by name means no #if/#elif chain, so an unknown or empty $MACHINE is
@@ -95,12 +94,12 @@ SRCS = \
ntp.c \
ntp_cli.c \
syslog.c \
udp_apps.c \
crypto/chacha20.c
udp_apps.c
# RTL837x
SRCS += \
rtl837x_bandwidth.c \
rtl837x_cabletest.c \
rtl837x_flash.c \
rtl837x_igmp.c \
rtl837x_init.c \
@@ -185,8 +184,8 @@ $(BUILDDIR)/%.rel: %.asm $(CCFLAGS_STAMP) | create_build_dir
${ASM} ${AFLAGS} -o $@ $<
# mv -f $(addprefix $(basename $^), .lst .rel .sym) .
$(BUILDDIR)/rtlplayground.ihx: $(OBJS) $(BUILDDIR)/crtbank.rel $(BUILDDIR)/crc16.rel $(BUILDDIR)/crypto/chacha_8051.rel
$(CC) $(CC_FLAGS) --xram-size 48896 -Wl-bHOME=0x00000 -Wl-bBANK1=0x14000 -Wl-bBANK2=0x24000 -Wl-bBANK3=0x34000 -Wl-r -o $@ $^
$(BUILDDIR)/rtlplayground.ihx: $(OBJS) $(BUILDDIR)/crtbank.rel $(BUILDDIR)/crc16.rel
$(CC) $(CC_FLAGS) --xram-size 49151 -Wl-bHOME=0x00000 -Wl-bBANK1=0x14000 -Wl-bBANK2=0x24000 -Wl-bBANK3=0x34000 -Wl-r -o $@ $^
$(BUILDDIR)/rtlplayground.img: $(BUILDDIR)/rtlplayground.ihx
objcopy --input-target=ihex -O binary $< $@
+13 -7
View File
@@ -103,24 +103,29 @@ docker run --rm -p 8080:8080 -v $(pwd):/workspace rtlplayground-dev \
tools/output/httpd_sim /workspace/html
```
Edit `machine.h` or `config.txt` on your host, then re-run `make` — the
Edit `config.txt` on your host, then re-run `make` — the
source directory is mounted into the container, so changes take effect
immediately. To build for a different machine, pass `MACHINE=...`.
immediately. To build for a different machine, pass `MACHINE=<name>`, which
selects `machine/<name>.c` (see [Adding a new device](doc/adding-a-device.md)).
</details>
## (1) Compiling for direct chip flashing AND upgrading an existing RTLPlayground running device
Edit machine.h with an editor like vi or nano. Select the correct machine the firmware should build for.
Pass the machine that matches your device, for example:
```
make MACHINE=DEFAULT_8C_1SFP
```
The name selects the board definition `machine/<name>.c`; `make` without a
`MACHINE` value stops with an error listing what is expected. If your board is
not in the list yet, see [Adding a new device](doc/adding-a-device.md).
> [!TIP]
> You can write configuration parameters in config.txt (see below) in order your switch to get
> straight at the first boot, a correct IP configuration.
Now, building the firmware image should work:
```
make
```
Now, building the firmware image should work.
Note, that the image generated ends in .bin, not .img, in order to make IMSProg happy.
image location is stored in `RTLPlayground/output/rtlplayground_version_machine.bin`
@@ -353,6 +358,7 @@ Enjoy playing!
## (11) Other documents
The following documents give further documentation on specific features of the RTL837x SoCs:
- [Adding a new device](doc/adding-a-device.md)
- [RTL8372/3 Feature support](doc/hardware.md)
- [CPU Port](doc/CpuPort.md)
- [L2 learning](doc/l2.md)
+14 -2
View File
@@ -20,6 +20,7 @@
#include "rtl837x_lldp.h"
#include "rtl837x_storm.h"
#include "rtl837x_trap.h"
#include "rtl837x_cabletest.h"
#include "sfp.h"
#include "dhcp.h"
#include "syslog.h"
@@ -166,10 +167,15 @@ uint8_t atoi_hex(uint8_t idx)
break;
}
if (h_idx == 8) {
h_idx = 0;
break;
}
// swap hex nibbles
val = (val >> 4) | (val << 4);
if (c - '0' < 10) {
if ((uint8_t)(c - '0') < 10) {
val |= c - '0';
} else {
c |= 0x20;
@@ -903,6 +909,7 @@ void parse_port(void)
"\nport <port> [show|on|off]" \
"\nport <port> [10m|100m|1g|2g5|duplex] [half|full]" \
"\nport <port> lldp [block|permit]" \
"\nport <port> cabletest [show]" \
"\nport <port> name [custom port name]\n");
return;
}
@@ -945,6 +952,11 @@ void parse_port(void)
}
} else if (machine.is_sfp[phy_settings.port]) {
print_string(" is SFP no PHY information available.\n");
} else if (cmd_compare(2, "cabletest")) {
if (cmd_words_len > 3 && cmd_compare(3, "show"))
cabletest_show();
else
cabletest_run();
} else if (cmd_compare(2, "10m")) {
print_string(" 10M\n");
phy_settings.speed = PHY_SPEED_10M;
@@ -2143,7 +2155,7 @@ void execute_config(void) __banked
skipping = 1;
}
c = flash_buf[cfg_idx++];
if (c == 0 || c == '\n') {
if (c == 0 || c == '\n' || c == '\r') {
if (skipping) {
skipping = 0;
} else {
-23
View File
@@ -1,23 +0,0 @@
#ifndef _CHACHA_H_
#define _CHACHA_H_
#include <stdint.h>
struct chacha20_t {
uint8_t wstate[64]; // 64 bytes written here
uint8_t constant[16]; // 128 bit constant
uint8_t key[32]; // 256-bit secret key
uint32_t cnt; // 32-bit block counter 1, 2.. Big Endian!
uint8_t nonce[12]; // 96-bit nonce
__xdata uint8_t *plaintext;
uint16_t length;
__xdata uint8_t *cyphertext;
};
// Encrypt a plaintext with ChaCha20 as per RFC7539
void chacha20_encrypt(void);
// Test ChaCha20 using the example from RFC7539
void chacha20_test(void);
#endif
-103
View File
@@ -1,103 +0,0 @@
#include "rtl837x_common.h"
#include "chacha.h"
void chacha_20(void);
void chacha_update(void);
void chacha_count(void);
// generate a block of ChaCha20 keystream as per RFC7539
__xdata struct chacha20_t __at(0xbf00) chacha20;
#ifdef CHACHA20_TEST
__xdata uint8_t plaintext[256];
__xdata uint8_t cyphertext[256];
#endif
#ifdef DEBUG
void chacha20_print_block(void)
{
for (uint8_t i=0; i < 64; i++) {
print_byte(*(uint8_t * __xdata)(chacha20.wstate + i));
if (i%4 == 3)
write_char(' ');
}
}
#endif
void chacha20_encrypt(void)
{
__xdata uint8_t *p = chacha20.plaintext;
while (chacha20.length) {
register uint8_t i;
chacha_count();
memcpy(chacha20.wstate, chacha20.wstate + 64, 64);
#ifdef DEBUG
chacha20_print_block(); write_char('\n');
#endif
chacha_20();
chacha_update();
#ifdef DEBUG
chacha20_print_block(); write_char('\n');
#endif
for (i = 0; i < ((chacha20.length > 64) ? 64 : chacha20.length); i++)
*chacha20.cyphertext++ = *p++ ^ chacha20.wstate[i];
chacha20.length -= chacha20.length > 64 ? 64 : chacha20.length;
#ifdef DEBUG
print_string("\n round done\n");
#endif
};
}
#ifdef CHACHA20_TEST
// Test the example of RFC 7539 Section 2.4.2
void chacha20_test(void)
{
__code uint8_t chacha_c[16] =
{ 0x61, 0x70, 0x78, 0x65, 0x33, 0x20, 0x64, 0x6e,
0x79, 0x62, 0x2d, 0x32, 0x6b, 0x20, 0x65, 0x74 };
__code uint8_t key[32] =
{ 0x03, 0x02, 0x01, 0x00, 0x07, 0x06, 0x05, 0x04,
0x0b, 0x0a, 0x09, 0x08, 0x0f, 0x0e, 0x0d, 0x0c,
0x13, 0x12, 0x11, 0x10, 0x17, 0x16, 0x15, 0x14,
0x1b, 0x1a, 0x19, 0x18, 0x1f, 0x1e, 0x1d, 0x1c };
__code uint8_t nonce[12] = {
0x00, 0x00, 0x00, 0x00, 0x4a, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00 };
__code uint8_t sunscreen[] = "Ladies and Gentlemen of the class of '99: If I could offer you only one tip for " \
"the future, sunscreen would be it.";
/*
Ciphertext Sunscreen (from RFC 7539 Section 2.4.2):
000 6e 2e 35 9a 25 68 f9 80 41 ba 07 28 dd 0d 69 81 n.5.%h..A..(..i.
016 e9 7e 7a ec 1d 43 60 c2 0a 27 af cc fd 9f ae 0b .~z..C`..'......
032 f9 1b 65 c5 52 47 33 ab 8f 59 3d ab cd 62 b3 57 ..e.RG3..Y=..b.W
048 16 39 d6 24 e6 51 52 ab 8f 53 0c 35 9f 08 61 d8 .9.$.QR..S.5..a.
064 07 ca 0d bf 50 0d 6a 61 56 a3 8e 08 8a 22 b6 5e ....P.jaV....".^
080 52 bc 51 4d 16 cc f8 06 81 8c e9 1a b7 79 37 36 R.QM.........y76
096 5a f9 0b bf 74 a3 5b e6 b4 0b 8e ed f2 78 5e 42 Z...t.[......x^B
112 87 4d
*/
strcpy(plaintext, sunscreen);
memcpyc(chacha20.constant, chacha_c, 16);
memcpyc(chacha20.key, key, 32);
chacha20.cnt = 0;
memcpyc(chacha20.nonce, nonce, 12);
chacha20.plaintext = plaintext;
chacha20.length = strlen_x(plaintext);
chacha20.cyphertext = cyphertext;
print_string("Encrypting...\n");
chacha20_encrypt();
print_string("Cypertext:\n");
uint16_t len = strlen_x(plaintext);
for (uint8_t i = 0; i < len; i++) {
print_byte(cyphertext[i]); write_char(' ');
}
}
#endif
-455
View File
@@ -1,455 +0,0 @@
; ChaCha Quater-Round implementation in Assembler
.module chacha_8051
; Global variables:
.globl _chacha_20
.globl _chacha_update
.globl _chacha_count
;#define CHACHA_QR(A, B, C, D) { \
; A += B; D ^= A; D = ROTL32(D, 16); \
; C += D; B ^= C; B = ROTL32(B, 12); \
; A += B; D ^= A; D = ROTL32(D, 8); \
; C += D; B ^= C; B = ROTL32(B, 7); \
;}
; r0 r1 r2 r3
; CHACHA_QR( v[ 0], v[ 4], v[ 8], v[12] );
.area HOME (CODE)
store_32:
ar7 = 0x07
ar6 = 0x06
ar5 = 0x05
ar4 = 0x04
ar3 = 0x03
ar2 = 0x02
ar1 = 0x01
ar0 = 0x00
mov a, r7
movx @dptr, a
dec dpl
mov a, r6
movx @dptr, a
dec dpl
mov a, r5
movx @dptr, a
dec dpl
mov a, r4
movx @dptr, a
ret
rol_b:
clr c
mov a, r7
rlc a
mov r7, a
mov a, r6
rlc a
mov r6, a
mov a, r5
rlc a
mov r5, a
mov a, r4
rlc a
mov r4, a
clr a
addc a, r7
mov r7, a
djnz dpl, rol_b
ret
chacha_plus_xor:
; Load A into registers r4-r7, A pointed to by r0
mov dptr, #_chacha20
mov dpl, r0
movx a, @dptr
mov r7, a
dec dpl
movx a, @dptr
mov r6, a
dec dpl
movx a, @dptr
mov r5, a
dec dpl
movx a, @dptr
mov r4, a
; A += B, B pointed to by r1
mov dpl, r1
movx a, @dptr
add a, r7
mov r7, a
dec dpl
movx a, @dptr
addc a, r6
mov r6, a
dec dpl
movx a, @dptr
addc a, r5
mov r5, a
dec dpl
movx a, @dptr
addc a, r4
mov r4, a
mov dpl, r0 ; Store A back
mov a, r7
movx @dptr, a
dec dpl
mov a, r6
movx @dptr, a
dec dpl
mov a, r5
movx @dptr, a
dec dpl
mov a, r4
movx @dptr, a
; D ^= A
mov dpl, r3
movx a, @dptr
xrl a, r7
mov r7, a
dec dpl
movx a, @dptr
xrl a, r6
mov r6, a
dec dpl
movx a, @dptr
xrl a, r5
mov r5, a
dec dpl
movx a, @dptr
xrl a, r4
mov r4, a
dec dpl
ret
chacha_qr:
; QR Part: A += B; D ^= A; D = ROTL32(D, 16);
acall chacha_plus_xor
; rotate left 16. D is r4, r5, r6, r7 -> r6, r7, r4, r5
mov a, r6
xch a, r4
mov r6, a
mov a, r7
xch a, r5
mov r7, a
mov dpl, r3 ; Store D
acall store_32
;QR Part: C += D; B ^= C; B = ROTL32(B, 12);
; Swap A <-> C and D <-> B
mov a, r0
xch a, r2
mov r0, a
mov a, r1
xch a, r3
mov r1, a
acall chacha_plus_xor
; rotate left 12. D is r4, r5, r6, r7 -> r5, r6, r7, r4
mov a, r4
xch a, r7
xch a, r6
xch a, r5
mov r4, a
mov dpl, #4
acall rol_b
mov dpl, r3 ; Store D (being B)
acall store_32
; Swap A <-> C and D <-> B
mov a, r0
xch a, r2
mov r0, a
mov a, r1
xch a, r3
mov r1, a
; QR Part A += B; D ^= A; D = ROTL32(D, 8);
acall chacha_plus_xor
mov a, r4
xch a, r7
xch a, r6
xch a, r5
mov r4, a
mov dpl, r3 ; Store D
acall store_32
; QR Part
; C += D; B ^= C; B = ROTL32(B, 7);
line4:
; Swap A <-> C and D <-> B
mov a, r0
xch a, r2
mov r0, a
mov a, r1
xch a, r3
mov r1, a
acall chacha_plus_xor
; Roll left 7 bits, start by rolling 8 bits left, then roll 1 to the right
mov a, r4
xch a, r7
xch a, r6
xch a, r5
mov r4, a
clr c
mov a, r4
rrc a
mov r4, a
mov a, r5
rrc a
mov r5, a
mov a, r6
rrc a
mov r6, a
mov a, r7
rrc a
mov r7, a
clr a
rrc a
add a, r4
mov r4, a
; Swap A <-> C and D <-> B
mov a, r0
xch a, r2
mov r0, a
mov a, r1
xch a, r3
mov r1, a
mov dpl, r1 ; Store B
acall store_32
ret
;
; Implementation of 20 ChaCha Rounds (10 Double Rounds)
;
_chacha_20:
push acc
push b
push dpl
push dph
push ar7
push ar6
push ar5
push ar4
push ar3
push ar2
push ar1
push ar0
push psw
mov b, #10 ; 10 Double rounds
chacha_20_loop:
; CHACHA_QR( v[ 0], v[ 4], v[ 8], v[12] );
mov r0, #3
mov r1, #19
mov r2, #35
mov r3, #51
acall chacha_qr
; CHACHA_QR( v[ 1], v[ 5], v[ 9], v[13] ); 7 23 39 55
mov r0, #7
mov r1, #23
mov r2, #39
mov r3, #55
acall chacha_qr
; CHACHA_QR( v[ 2], v[ 6], v[10], v[14] ); 11 27 43 59
mov r0, #11
mov r1, #27
mov r2, #43
mov r3, #59
acall chacha_qr
; CHACHA_QR( v[ 3], v[ 7], v[11], v[15] ); 15 31 47 63
mov r0, #15
mov r1, #31
mov r2, #47
mov r3, #63
acall chacha_qr
; CHACHA_QR( v[ 0], v[ 5], v[10], v[15] ); 3 23 43 63
mov r0, #3
mov r1, #23
mov r2, #43
mov r3, #63
acall chacha_qr
; CHACHA_QR( v[ 1], v[ 6], v[11], v[12] ); 7 27 47 51
mov r0, #7
mov r1, #27
mov r2, #47
mov r3, #51
acall chacha_qr
; CHACHA_QR( v[ 2], v[ 7], v[ 8], v[13] ); 11 31 35 55
mov r0, #11
mov r1, #31
mov r2, #35
mov r3, #55
acall chacha_qr
; CHACHA_QR( v[ 3], v[ 4], v[ 9], v[14] ); 15 19 39 59
mov r0, #15
mov r1, #19
mov r2, #39
mov r3, #59
acall chacha_qr
djnz b, chacha_20_loop
pop psw
pop ar0
pop ar1
pop ar2
pop ar3
pop ar4
pop ar5
pop ar6
pop ar7
pop dph
pop dpl
pop b
pop acc
ret
;
; Update ChaCHa state
;
_chacha_update:
push acc
push b
push dpl
push dph
push ar4
push ar3
push ar2
push ar1
push ar0
push psw
mov dptr, #_chacha20
mov b, #16 ; 16 uint32
update_loop:
mov a, b ; multiply by 4 and subtract 1
rl a
rl a
dec a
; Load Working State variable into registers r0-r3
mov dpl, a
orl a, #64 ; Save pointer to state
mov r4, a
movx a, @dptr
mov r3, a
dec dpl
movx a, @dptr
mov r2, a
dec dpl
movx a, @dptr
mov r1, a
dec dpl
movx a, @dptr
mov r0, a
; Add to State variable
mov a, r4
mov dpl, a
movx a, @dptr
add a, r3
mov r3, a
dec dpl
movx a, @dptr
addc a, r2
mov r2, a
dec dpl
movx a, @dptr
addc a, r1
mov r1, a
dec dpl
movx a, @dptr
addc a, r0
mov r0, a
; Store back state variable in LSB, first sequence (serialized)
mov a, r4
xrl a, #64 ; Save pointer to working state
mov dpl, a
mov a, r0
movx @dptr, a
dec dpl
mov a, r1
movx @dptr, a
dec dpl
mov a, r2
movx @dptr, a
dec dpl
mov a, r3
movx @dptr, a
djnz b, update_loop
pop psw
pop ar0
pop ar1
pop ar2
pop ar3
pop ar4
pop dph
pop dpl
pop b
pop acc
ret
;
; Increase counter in state
;
_chacha_count:
push acc
push dpl
push dph
mov dptr, #_chacha20
mov dpl, #112 + 3
movx a, @dptr
inc a
movx @dptr, a
jnz chacha_count_done
dec dpl
movx a, @dptr
inc a
movx @dptr, a
jnz chacha_count_done
dec dpl
movx a, @dptr
inc a
movx @dptr, a
jnz chacha_count_done
dec dpl
movx a, @dptr
inc a
movx @dptr, a
chacha_count_done:
pop dph
pop dpl
pop acc
ret
+179
View File
@@ -0,0 +1,179 @@
# Adding a new device
A device is supported by adding one board definition `.h` and board configuration `.c` file
under `machine/`.
the build selects the definition by name, and CI compiles every file it finds.
To compile for your added device, do:
```
make MACHINE=<NAME>
```
## What a new device consists of
| File | Purpose |
|------|---------|
| `machine/<NAME>.c` | the `struct machine machine` board definition |
| `machine/<NAME>.h` | `MACHINE_MODEL`, the model string shown in the UI and the CLI |
| `doc/devices/<name>.md` + `doc/supported_devices.md` | (only if the board is verified and you want to support it publicly) user-facing documentation |
`<NAME>` is the machine symbol without its `MACHINE_` prefix, so `MACHINE` is
passed exactly as spelled on the command line: `make MACHINE=MY_BOARD_1_0`
needs `machine/MY_BOARD_1_0.c` and `machine/MY_BOARD_1_0.h`.
## Step 1: the model header
`machine/<NAME>.h` only carries the product string. Use the existing files as
the template:
```c
#ifndef _MACHINE_MY_BOARD_1_0_H_
#define _MACHINE_MY_BOARD_1_0_H_
#define MACHINE_MODEL "Brand Model V1.0"
#endif
```
The device product string is what the CLI or the Web-Interface will print to identify the device; `machine.h` includes it by name.
> [!NOTE]
> `machine_name` is a `char[30]` and `MACHINE_MODEL` is copied into it at
> compile time. A longer model string still builds, but SDCC emits
> `warning 147: excess elements in array of chars initializer` and the name is
> truncated in the UI. Keep it to 29 characters or less.
## Step 2: the board definition
`machine/<NAME>.c` starts with the shared prelude and then defines the single
`machine` object. Copy the closest existing board and adjust it — do not start
from a blank file, the field meanings are easier to see in a real example.
The best choice is usually a device that has the same number and type of Ethernet
ports and the same number of SFP+ cages.
```c
#include "machine_defs.h"
__code const struct machine machine = {
.machine_name = MACHINE_MODEL,
.isRTL8373 = 1,
.min_port = 0,
.max_port = 8,
.n_sfp = 1,
.log_to_phys_port = {1, 2, 3, 4, 5, 6, 7, 8, 9},
.phys_to_log_port = {0, 1, 2, 3, 4, 5, 6, 7, 8},
.is_sfp = {0, 0, 0, 0, 0, 0, 0, 0, 1},
.sfp_port[0].pin_detect = GPIO30_ACL_BIT3_EN,
.sfp_port[0].pin_los = GPIO37,
.sfp_port[0].pin_tx_disable = GPIO_NA,
.sfp_port[0].sds = 1,
.sfp_port[0].i2c = I2CBUS( GPIO39_I2C_SDA4, GPIO40_I2C_SCL3_MDC1 ),
.reset_pin = GPIO_NA,
.high_leds = { .mux = LED_27 | LED_29, .enable = LED_28_SYS | LED_29 },
.port_led_set = { 0, 0, 0, 0, 0, 0, 0, 0, 1 },
.led_sets = {
{ /* RJ45 ports */
LEDS_2G5 | LEDS_LINK,
LEDS_2G5 | LEDS_1G | LEDS_100M | LEDS_10M | LEDS_LINK | LEDS_ACT,
0,
0
}, { /* SFP port, single green LED */
LEDS_10G | LEDS_2G5 | LEDS_1G | LEDS_100M | LEDS_10M | LEDS_LINK | LEDS_ACT,
0,
0,
0
}
},
};
```
### Fields
| Field | Meaning |
|-------|---------|
| `machine_name` | always `MACHINE_MODEL` |
| `isRTL8373` | `1` for an RTL8373 SoC, `0` for RTL8372 |
| `min_port` / `max_port` | range of logical port numbers the device has; `min_port` is `0` for RTL8373 boards and `3` for RTL8372 boards (logical ports 1–2 are the SoC's internal ports on the 8372), `max_port` is usually 8. |
| `n_sfp` | number of SFP+ cages |
| `n_10g` | SerDes 10G capability used by the RTL837xN init: `0` none, `1` one, `2` two 10G-capable SerDes |
| `log_to_phys_port[9]` | logical port (0-based index) → physical front-panel port number |
| `phys_to_log_port[9]` | the inverse, physical port 1 is index 0, unused slots are 0 |
| `is_sfp[9]` | `0` for copper, `1` for left slot and `2` for right slot |
| `sfp_port[2]` | per-cage wiring, `sfp_port[0]` is the leftmost cage |
| `reset_pin` | GPIO of the reset button, `GPIO_NA` if unpopulated |
| `high_leds` | mux/enable for the LED pins 27–29 (`LED_27`, `LED_28_SYS`, `LED_29`) |
| `port_led_set[9]` | which LED set (0–3) each logical port uses |
| `led_sets[4][4]` | Each port can have up to 4 GPIO output pins assigned. 4 sets of LEDs with 4 LED-conditions per set; the conditions define which GPIO pins become active for a port given a port link-state. Which LEDs are then enabled depends on the wiring of the pins to up to 3 LEDs for that port, which can make a logical combination of the 4 LED output-pins. |
| `led_mux_custom` / `led_mux[28]` | `1` plus the 28 mux values when the stock LED muxing does not fit the board |
| `mac_flash_offset` | flash offset of the factory MAC address, omit or `0` if the board does not store one |
Each `struct sfp_port` entry holds `pin_detect`, `pin_los`, `pin_tx_disable`,
`sds` (which SerDes drives the cage) and `i2c` (built with `I2CBUS(sda, scl)`,
or `GPIO_NA`-based values where a cage has no such pin).
`I2CBUS(...)` only accepts the pin pairs listed in `machine_defs.h`. A wrong
pair is caught at compile time: SDCC reports
`error 176: sizeof applied to an incomplete type` pointing at the offending
`.i2c` line, because the macro deliberately turns an invalid bus number into an
array of size 0.
### Reusing an existing definition
If the new board is electrically identical to one that already exists — same
port mapping, same SFP wiring, same LEDs — do not copy the file. Make the new
`.c` a symlink and keep only a distinct `.h`, so the two boards cannot drift
apart:
```
ln -s KP_9000_6XHML_X2.c machine/MY_BOARD_1_0.c
```
The new board still needs `machine/MY_BOARD_1_0.h` (pick the version number on the PCB) with its own
`MACHINE_MODEL`, because `MACHINE_MODEL` is what distinguishes the products.
Existing examples: `machine/PCB_K0402WS_V3.c`, `machine/KP_9000_9XHML_X_V3_2.c`
and the six `KP_9000_*` revision variants.
Two files that differ only in whitespace or comments are duplicates and should
be collapsed this way.
## Step 3: build and verify
```
make MACHINE=MY_BOARD_1_0
```
The generated flash image appears as `output/rtlplayground-<version>-MY_BOARD_1_0.bin`. An
unknown or empty `MACHINE` fails immediately with a message naming the expected
file, before anything is compiled.
Then run the checks CI runs:
```
make machine_check # compiles machine/<NAME>.c and machine_init.c for every machine
make -C test # host-side unit tests
```
`make machine_check` discovers the definitions with
`$(wildcard machine/*.c)`, so a new file is covered without registering it
anywhere. CI (`.github/workflows/build.yml`) runs it on every push and pull
request, alongside full builds for a 4+2 and an 8+1 board.
A successful compile only proves the definition is syntactically valid. The
actual behaviour — port numbering, SFP detection, LEDs — has to be confirmed on
hardware, and the console commands (`port`, `stat`, `sfp`, `leds`) are the
tools for that.
## Step 4: documentation
Once the board has been tested on hardware:
1. Add a row to the table in [Supported devices](supported_devices.md).
2. Add `doc/devices/<name>.md` describing the board, the build target
(`make MACHINE=MY_BOARD_1_0`), what works and what is unverified, plus
photos and the PCB silkscreen marking.
3. If the board shares a PCB with other revisions, follow
`doc/devices/KP-9000-6XH-X2.md` and document which `MACHINE` to pick for
which silkscreen.
+2 -2
View File
@@ -17,8 +17,8 @@ The original web interface reports this board as hardware version V3.2
The component placement matches [2M-PCB23-V3.1](2M-PCB23-V3_1.md): same
positions for the flash (U8), the T7/T8/T9/T10 headers, the SFP cage and its
LED, and the unpopulated U7/U9 footprints. Build with
`MACHINE=KP_9000_9XHML_X_V3_2`, which uses the V3.1 machine definition and
only differs in the reported name.
`make MACHINE=KP_9000_9XHML_X_V3_2`, whose board file is a symlink to the V3.1
definition and only differs in the reported name.
## Tested
+1 -1
View File
@@ -15,7 +15,7 @@ and sold by Mokerlink.
The device is fully supported:
- All 4 2.5GBASE-T RJ45 ports work at 10/100/1000/2500 Mbps
- The SFP+ port supports 1G, 2.5G and 10G modules
- LEDs work with the same indiciations as the OEM firmware (use KP_9000_6XHML_X2_V1_1 in machine.h if building yourself or the corresponding pre-compiled binary)
- LEDs work with the same indiciations as the OEM firmware (build with `make MACHINE=KP_9000_6XHML_X2_V1_1` if building yourself, or use the corresponding pre-compiled binary)
- untested due to missing Hardware: SFP+ ports equipped with 1G or 2.5G SFPs.
### Hardware overview
@@ -5,6 +5,7 @@ It is highly similar to SWTG024AS-V2.0, with the only difference being the GPIO
|Brand|Type|Managed|PCB|Flash|Chip RTL|
|---|---|---|---|---|---|
| Horaco | ZX-SG4T2 | | PCB-SWTG024AS-A-V2.0.1_19650 | P25D40SH | ??? |
| GoodTop | GT-ST024M | Yes | PCB-SWTG024AS-A-V2.0.1 | 2M | ??? |
### Label specifications
+1 -1
View File
@@ -9,7 +9,7 @@
## RTLPlayground target
Use machine target `MACHINE_LIANGUO_ZX_SWTGW215AS` for this device.
Build for this device with `make MACHINE=LIANGUO_ZX_SWTGW215AS`, which selects the `machine/LIANGUO_ZX_SWTGW215AS.c` board definition.
Physical hardware verification: 5x RJ45 ports + 1x SFP port.
Port 5 RJ45 is interfaced through a RTL8221B IC.
+1
View File
@@ -8,6 +8,7 @@ The following devices have been tested and are fully working:
| FOXNEO | FNS-1200P | No | [PCB-K0402W-U13-V2.0](devices/FNS-1200P.md) | 2M | 4 + 2 |
| Goalake | IG108 | No | [PB-2132](devices/PB-2132.md) | 4M | 8 + 1 |
| GoodTop | GT-ST018M | Yes | [SWTG018AS-A V2.0](devices/SWTG018AS_A_V2_0.md) | ? | 8 + 1 |
| GoodTop | GT-ST024M | Yes | [SWTG024AS-A V2.0.1](devices/SWTG024AS-A-V2.0.1_4C_2SFP.md) | 2M | 4 + 2 |
| Hasivo | S1100WP_8GT_1SX_SE | Yes | [S1100WP_8GT_1SX_SE-V1.05](devices/HASIVO_S1100WP_8GT_1SX_SE.md) | 2M | 8 + 1 |
| Hisource | Hi-K0402WS | No | [PCB-K0402WS-V3.0](devices/PCB-K0402WS-V3.0.md) | | 4 + 2 |
| Hisource | Hi-K0402WS | No | [PCB-K0402WS-V2.0](devices/PCB-K0402WS-V2.0.md) | 2M | 4 + 2 |
+32 -47
View File
@@ -11,15 +11,15 @@ th_sel_light:"Selenized Light",th_sel_dark:"Selenized Dark",
c_port:"Port",c_name:"Name",c_link:"Link",c_apply:"Apply",c_cancel:"Cancel",c_confirm:"Confirm",
c_close:"Close",c_refresh:"Refresh",c_off:"off",c_offc:"Off",c_down:"down",c_on:"On",c_auto:"Auto",c_delete:"Delete",
c_type:"Type",c_state:"State",c_enabled:"Enabled",c_load:"Load",c_disable:"Disable",c_sfp:"SFP",
c_full:"full",c_half:"half",c_devices:"devices",c_yes:"yes",c_no:"no",
d_ports:"Ports",d_ports_h:"click a port for details",d_system:"System",d_traffic:"Traffic",
c_full:"full",c_half:"half",c_yes:"yes",c_no:"no",
d_system:"System",d_traffic:"Traffic",
d_traffic_h:"packets/s, live",d_txpps:"TX pps",d_rxpps:"RX pps",d_txbad:"TX bad",d_rxbad:"RX bad",
i_host:"Hostname",i_ip:"IP address",i_mask:"Netmask",i_gw:"Gateway",i_mac:"MAC",i_fw:"Firmware",
i_built:"Built",i_hw:"Hardware",i_temp:"Temperature",i_flash:"Flash",i_syslog:"Syslog",
p_state:"State",p_disabled:"disabled",p_up:"up",p_txgb:"TX good / bad",p_rxgb:"RX good / bad",
p_pkts:"pkts",p_module:"Module",p_temp:"Temperature",p_vcc:"Vcc",p_txbias:"TX bias",p_txpower:"TX power",
p_rxpower:"RX power",p_txfault:"TX fault",p_txdis:"TX disabled",p_rxlos:"RX LOS",p_adv:"Advertising",
pt_title:"Port configuration",pt_conn:"Connected",pt_speed:"Speed",pt_mtu:"MTU",
pt_title:"Port configuration",pt_conn:"Devices learned",pt_speed:"Speed",pt_mtu:"MTU",
pt_sfp_note:"SFP speed is set per slot and is not saved in the startup config.",
pt_name_err:"Port name: 1-15 characters, no spaces",pt_mtu_err:"MTU must be 64-16383",
stp_title:"Spanning tree",stp_h:"RSTP, 802.1w",stp_version:"Version",stp_v_stp:"STP compatible",
@@ -142,15 +142,15 @@ th_sel_light:"Selenized ライト",th_sel_dark:"Selenized ダーク",
c_port:"ポート",c_name:"名前",c_link:"リンク",c_apply:"適用",c_cancel:"キャンセル",c_confirm:"確認",
c_close:"閉じる",c_refresh:"更新",c_off:"オフ",c_offc:"オフ",c_down:"リンクダウン",c_on:"オン",c_auto:"自動",c_delete:"削除",
c_type:"タイプ",c_state:"状態",c_enabled:"有効",c_load:"読み込み",c_disable:"無効化",c_sfp:"SFP",
c_full:"全二重",c_half:"半二重",c_devices:"デバイス",c_yes:"はい",c_no:"いいえ",
d_ports:"ポート",d_ports_h:"ポートをクリックすると詳細を表示",d_system:"システム",d_traffic:"トラフィック",
c_full:"全二重",c_half:"半二重",c_yes:"はい",c_no:"いいえ",
d_system:"システム",d_traffic:"トラフィック",
d_traffic_h:"パケット/秒、リアルタイム",d_txpps:"TX pps",d_rxpps:"RX pps",d_txbad:"TX 異常",d_rxbad:"RX 異常",
i_host:"ホスト名",i_ip:"IP アドレス",i_mask:"ネットマスク",i_gw:"ゲートウェイ",i_mac:"MAC",i_fw:"ファームウェア",
i_built:"ビルド日",i_hw:"ハードウェア",i_temp:"温度",i_flash:"フラッシュ",i_syslog:"Syslog",
p_state:"状態",p_disabled:"無効",p_up:"アップ",p_txgb:"TX 正常 / 異常",p_rxgb:"RX 正常 / 異常",
p_pkts:"pkts",p_module:"モジュール",p_temp:"温度",p_vcc:"電圧",p_txbias:"TX バイアス",p_txpower:"TX 電力",
p_rxpower:"RX 電力",p_txfault:"TX 障害",p_txdis:"TX 無効",p_rxlos:"RX 信号ロス",p_adv:"アドバタイズ",
pt_title:"ポート設定",pt_conn:"接続デバイス",pt_speed:"速度",pt_mtu:"MTU",
pt_title:"ポート設定",pt_conn:"学習済みデバイス",pt_speed:"速度",pt_mtu:"MTU",
pt_sfp_note:"SFP の速度はスロット単位で設定され、起動設定には保存されません。",
pt_name_err:"ポート名: 1〜15 文字、空白不可",pt_mtu_err:"MTU は 64〜16383 の範囲で指定してください",
stp_title:"スパニングツリー",stp_h:"RSTP, 802.1w",stp_version:"バージョン",stp_v_stp:"STP 互換",
@@ -255,15 +255,15 @@ th_sel_light:"Selenized 浅色",th_sel_dark:"Selenized 深色",
c_port:"端口",c_name:"名称",c_link:"链路",c_apply:"应用",c_cancel:"取消",c_confirm:"确认",
c_close:"关闭",c_refresh:"刷新",c_off:"关",c_offc:"关",c_down:"未连接",c_on:"开",c_auto:"自动",c_delete:"删除",
c_type:"类型",c_state:"状态",c_enabled:"启用",c_load:"读取",c_disable:"禁用",c_sfp:"SFP",
c_full:"全双工",c_half:"半双工",c_devices:"台设备",c_yes:"是",c_no:"否",
d_ports:"端口",d_ports_h:"点击端口查看详情",d_system:"系统",d_traffic:"流量",
c_full:"全双工",c_half:"半双工",c_yes:"是",c_no:"否",
d_system:"系统",d_traffic:"流量",
d_traffic_h:"包/秒,实时",d_txpps:"TX pps",d_rxpps:"RX pps",d_txbad:"TX 错误",d_rxbad:"RX 错误",
i_host:"主机名",i_ip:"IP 地址",i_mask:"子网掩码",i_gw:"网关",i_mac:"MAC",i_fw:"固件",
i_built:"构建日期",i_hw:"硬件",i_temp:"温度",i_flash:"Flash",i_syslog:"Syslog",
p_state:"状态",p_disabled:"已禁用",p_up:"已连接",p_txgb:"TX 正常 / 错误",p_rxgb:"RX 正常 / 错误",
p_pkts:"个包",p_module:"模块",p_temp:"温度",p_vcc:"供电电压",p_txbias:"TX 偏置电流",p_txpower:"TX 光功率",
p_rxpower:"RX 光功率",p_txfault:"TX 故障",p_txdis:"TX 禁用",p_rxlos:"RX 信号丢失",p_adv:"通告能力",
pt_title:"端口配置",pt_conn:"已连接设备",pt_speed:"速率",pt_mtu:"MTU",
pt_title:"端口配置",pt_conn:"已学习设备",pt_speed:"速率",pt_mtu:"MTU",
pt_sfp_note:"SFP 速率按插槽设置,不会保存到启动配置。",
pt_name_err:"端口名称: 1-15 个字符,不能包含空格",pt_mtu_err:"MTU 范围为 64-16383",
stp_title:"生成树",stp_h:"RSTP, 802.1w",stp_version:"版本",stp_v_stp:"STP 兼容",
@@ -368,15 +368,15 @@ th_sel_light:"Selenized claro",th_sel_dark:"Selenized oscuro",
c_port:"Puerto",c_name:"Nombre",c_link:"Enlace",c_apply:"Aplicar",c_cancel:"Cancelar",c_confirm:"Confirmar",
c_close:"Cerrar",c_refresh:"Actualizar",c_off:"apagado",c_offc:"Apagado",c_down:"caído",c_on:"Encendido",c_auto:"Auto",c_delete:"Eliminar",
c_type:"Tipo",c_state:"Estado",c_enabled:"Habilitado",c_load:"Cargar",c_disable:"Deshabilitar",c_sfp:"SFP",
c_full:"dúplex completo",c_half:"semidúplex",c_devices:"dispositivos",c_yes:"sí",c_no:"no",
d_ports:"Puertos",d_ports_h:"pulsa un puerto para ver detalles",d_system:"Sistema",d_traffic:"Tráfico",
c_full:"dúplex completo",c_half:"semidúplex",c_yes:"sí",c_no:"no",
d_system:"Sistema",d_traffic:"Tráfico",
d_traffic_h:"paquetes/s, en vivo",d_txpps:"TX pps",d_rxpps:"RX pps",d_txbad:"TX erróneos",d_rxbad:"RX erróneos",
i_host:"Nombre de host",i_ip:"Dirección IP",i_mask:"Máscara de red",i_gw:"Puerta de enlace",i_mac:"MAC",i_fw:"Firmware",
i_built:"Compilado",i_hw:"Hardware",i_flash:"Flash",i_syslog:"Syslog",
p_state:"Estado",p_disabled:"deshabilitado",p_up:"activo",p_txgb:"TX correctos / erróneos",p_rxgb:"RX correctos / erróneos",
p_pkts:"paquetes",p_module:"Módulo",p_temp:"Temperatura",p_vcc:"Vcc",p_txbias:"Polarización TX",p_txpower:"Potencia TX",
p_rxpower:"Potencia RX",p_txfault:"Fallo TX",p_txdis:"TX deshabilitado",p_rxlos:"Pérdida RX (LOS)",p_adv:"Anunciado",
pt_title:"Configuración de puerto",pt_conn:"Conectado",pt_speed:"Velocidad",pt_mtu:"MTU",
pt_title:"Configuración de puerto",pt_conn:"Dispositivos aprendidos",pt_speed:"Velocidad",pt_mtu:"MTU",
pt_sfp_note:"La velocidad del SFP se ajusta por ranura y no se guarda en la configuración de inicio.",
pt_name_err:"Nombre de puerto: 1-15 caracteres, sin espacios",pt_mtu_err:"El MTU debe estar entre 64 y 16383",
stp_title:"Árbol de expansión",stp_h:"RSTP, 802.1w",stp_version:"Versión",stp_v_stp:"STP compatible",
@@ -480,15 +480,15 @@ th_sel_light:"Clair (sélénisé)",th_sel_dark:"Sombre (sélénisé)",
c_port:"Port",c_name:"Nom",c_link:"Lien",c_apply:"Appliquer",c_cancel:"Annuler",c_confirm:"Confirmer",
c_close:"Fermer",c_refresh:"Raffraichir",c_off:"arrêt",c_offc:"Arrêt",c_down:"arrêt",c_on:"Marche",c_auto:"Auto",c_delete:"Supprimer",
c_type:"Type",c_state:"Etat",c_enabled:"Activé",c_load:"Charger",c_disable:"Désactivé",c_sfp:"SFP",
c_full:"full",c_half:"half",c_devices:"Equipements",c_yes:"oui",c_no:"non",
d_ports:"Ports",d_ports_h:"Cliquez ou survollez pour plus de détails",d_system:"Système",d_traffic:"Trafic",
c_full:"full",c_half:"half",c_yes:"oui",c_no:"non",
d_system:"Système",d_traffic:"Trafic",
d_traffic_h:"paquets/s (en direct)",d_txpps:"TX pps",d_rxpps:"RX pps",d_txbad:"TX KO",d_rxbad:"RX KO",
i_host:"Nom d'hote",i_ip:"Adresse IP",i_mask:"Masque réseau",i_gw:"Passerelle",i_mac:"Adresse MAC",i_fw:"Micro logiciel",
i_built:"Compilé le",i_hw:"Matériel",i_temp:"Température",i_flash:"Taille Flash",i_syslog:"Journal système",
p_state:"Etat",p_disabled:"Inactif",p_up:"Actif",p_txgb:"TX bon / mauvais",p_rxgb:"RX bon / mauvais",
p_pkts:"paquets",p_module:"Module",p_temp:"Température",p_vcc:"Vcc",p_txbias:"TX biais",p_txpower:"TX puissance",
p_rxpower:"RX puissance",p_txfault:"TX erreur",p_txdis:"TX désactivé",p_rxlos:"RX pertes de signaux",p_adv:"Capacités",
pt_title:"Configuration du port",pt_conn:"Connecté à",pt_speed:"Vitesse",pt_mtu:"MTU",
pt_title:"Configuration du port",pt_conn:"Appareils appris",pt_speed:"Vitesse",pt_mtu:"MTU",
pt_sfp_note:"La vitesse SFP est paramétrée pour le port et n'est pas enregistrée dans la configuration de démarrage.",
pt_name_err:"Nom du port: 1 à 15 caractères, sans espaces",pt_mtu_err:"MTU doit être comrpis entre 64 et 16383",
stp_title:"Arborescence",stp_h:"RSTP, 802.1w",stp_version:"Protocole",stp_v_stp:"Compatible STP",
@@ -588,7 +588,7 @@ fw_timeout:"Le commutateur n'est plus joignable depuis 150 s: verifiez l'aliment
},
de:{
nav_dash:"Übersicht",nav_ports:"Ports",nav_stp:"Spanning Tree",nav_stats:"Statistik",
nav_vlan:"VLANs",nav_l2:"MAC-Tabelle",nav_mirror:"Spiegelung",nav_lag:"LAG",nav_eee:"EEE",
nav_vlan:"VLANs",nav_l2:"MAC-Tabelle",nav_mirror:"Spiegelung",nav_isolate:"Isolation",nav_lag:"LAG",nav_eee:"EEE",
nav_bw:"Bandbreite",nav_system:"System",nav_fw:"Firmware",
hdr_dirty:"ungespeicherte Änderungen",hdr_dirty_t:"Laufende Konfiguration weicht von der Startkonfiguration ab",
hdr_save:"Im Flash speichern",hdr_save_t:"Laufende Konfiguration dauerhaft im Flash speichern",
@@ -597,15 +597,15 @@ th_sel_light:"Selenized Hell",th_sel_dark:"Selenized Dunkel",
c_port:"Port",c_name:"Name",c_link:"Link",c_apply:"Übernehmen",c_cancel:"Abbrechen",c_confirm:"Bestätigen",
c_close:"Schliessen",c_refresh:"Aktualisieren",c_off:"aus",c_offc:"Aus",c_down:"getrennt",c_on:"Ein",c_auto:"Auto",c_delete:"Löschen",
c_type:"Typ",c_state:"Zustand",c_enabled:"Aktiviert",c_load:"Laden",c_disable:"Deaktivieren",c_sfp:"SFP",
c_full:"voll",c_half:"halb",c_devices:"Geräte",c_yes:"ja",c_no:"nein",
d_ports:"Ports",d_ports_h:"Port anklicken für Details",d_system:"System",d_traffic:"Datenverkehr",
c_full:"voll",c_half:"halb",c_yes:"ja",c_no:"nein",
d_system:"System",d_traffic:"Datenverkehr",
d_traffic_h:"Pakete/s, live",d_txpps:"TX Pakete/s",d_rxpps:"RX Pakete/s",d_txbad:"TX fehlerhaft",d_rxbad:"RX fehlerhaft",
i_host:"Hostname",i_ip:"IP-Adresse",i_mask:"Netzmaske",i_gw:"Gateway",i_mac:"MAC",i_fw:"Firmware",
i_built:"Erstellt",i_hw:"Hardware",i_temp:"Temperatur",i_flash:"Flash",i_syslog:"Syslog",
p_state:"Zustand",p_disabled:"deaktiviert",p_up:"verbunden",p_txgb:"TX gut / fehlerhaft",p_rxgb:"RX gut / fehlerhaft",
p_pkts:"Pakete",p_module:"Modul",p_temp:"Temperatur",p_vcc:"Vcc",p_txbias:"TX-Bias",p_txpower:"Sendeleistung",
p_rxpower:"Empfangsleistung",p_txfault:"TX-Fehler",p_txdis:"TX deaktiviert",p_rxlos:"RX LOS",p_adv:"Angeboten",
pt_title:"Port-Konfiguration",pt_conn:"Verbunden",pt_speed:"Geschwindigkeit",pt_mtu:"MTU",
pt_title:"Port-Konfiguration",pt_conn:"Gelernte Geräte",pt_speed:"Geschwindigkeit",pt_mtu:"MTU",
pt_sfp_note:"Die SFP-Geschwindigkeit wird pro Steckplatz gesetzt und nicht in der Startkonfiguration gespeichert.",
pt_name_err:"Port-Name: 1-15 Zeichen, keine Leerzeichen",pt_mtu_err:"MTU muss zwischen 64 und 16383 liegen",
stp_title:"Spanning Tree",stp_h:"RSTP, 802.1w",stp_version:"Version",stp_v_stp:"STP-kompatibel",
@@ -626,6 +626,7 @@ stp_en_q:"Spanning Tree aktivieren?",
stp_en_d:"Die Ports starten blockiert und brauchen bis zum doppelten Forward Delay, bis sie weiterleiten; Edge-Ports sind sofort wieder aktiv.",
stp_dis_q:"Spanning Tree deaktivieren?",stp_dis_d:"Alle Ports leiten sofort weiter; der Schutz vor Netzwerkschleifen entfällt.",
stp_cost_err:"Pfadkosten müssen zwischen 0 und 200000000 liegen",
nav_lldp:"LLDP",lldp_enabled:"LLDP aktiv",lldp_title:"Dienststatus",lldp_portname:"Name",lldp_enable:"Pakete senden",
st_title:"Port-Statistik",st_h:"Summen seit dem Start",st_txg:"TX gut",st_txb:"TX fehlerhaft",st_rxg:"RX gut",
st_rxb:"RX fehlerhaft",st_details:"Details",st_counters:"MIB-Zähler",st_nonzero:"nur ungleich null",
st_autoref:"automatisch aktualisieren",st_counter:"Zähler",st_value:"Wert",st_fail:"Zähler konnten nicht geladen werden",
@@ -648,8 +649,12 @@ l2_learned:"gelernt",l2_loading:"lädt...",l2_failed:"Laden fehlgeschlagen",l2_e
l2_del_t:"Eintrag löschen",l2_flush_q:"Alle gelernten MAC-Einträge löschen?",
m_title:"Port-Spiegelung",m_active:"aktiv",m_monitor:"Monitor-Port",m_mirror:"Spiegeln",m_both:"Beide",
m_note:"Beide = RX und TX des Ports auf den Monitor-Port spiegeln.",m_none:"Mindestens einen zu spiegelnden Port wählen",
iso_title:"Port-Isolation",iso_from:"Von Port",iso_to:"Zu Port",iso_all:"Alle erlauben",
iso_intro:"Ein Paket, das über einen Port hereinkommt, darf den Switch nur über die Ports verlassen, die in seiner Zeile angehakt sind. Die Isolation wirkt vor der Weiterleitung: Ein nicht angehakter Port ist direkt nicht erreichbar, egal was in der MAC-Tabelle steht.",
iso_legend:"Das Feld des Ports selbst ist immer erlaubt. Eine rote Portnummer markiert einen Port, der nicht ganz offen ist.",
lag_hash:"Hash:",lag_note:"Eine LAG braucht mindestens ein Mitglied, um in der Startkonfiguration gespeichert zu werden; eine leere Gruppe zu übernehmen löscht sie.",
lag_clear_q:"LAG {n} auflösen?",lag_clear_d:"Alle Mitglieds-Ports kehren in den Normalbetrieb zurück.",
lacp_heading:"LACP (802.3ad)",lag_mode:"Modus",lag_static:"Statisch",lacp_neg:"wird ausgehandelt",lacp_agg:"Aggregator",lacp_members:"aktive Mitglieder",lacp_hint:"LACP: Ports auswählen und Übernehmen drücken, um die Aushandlung zu starten",lacp_actor:"Lokal",lacp_partner:"Partner",lacp_rxstate:"Empfangsstatus",lacp_rx:"Empfangene LACPDUs",lacp_psys:"Partner-System",
e_title:"Energy Efficient Ethernet",e_adv:"Angeboten",e_lp:"Gegenstelle",e_active:"Aktiv",e_enable:"Aktivieren",
e_note:"Angebotene und von der Gegenstelle unterstützte Stufen je Geschwindigkeit: 100M, 1G, 2.5G. SFP-Ports unterstützen kein EEE.",
e_idle:"inaktiv",e_na:"k. A.",
@@ -672,7 +677,8 @@ sy_ip_err:"Ungültige IP / Netzmaske / Gateway",sy_host_err:"Hostname: 1-23 druc
sy_net_q:"Netzwerkeinstellungen übernehmen?",sy_net_d:"Die Management-IP ändert sich auf {ip}: Diese Seite muss dort neu geöffnet werden.",
sy_ip_changed:"IP geändert, neu verbinden unter http://{ip}/",sy_dhcp_q:"Auf DHCP umstellen?",
sy_dhcp_d:"Der Switch bezieht seine Adresse per DHCP. Du musst seine neue IP herausfinden, um dich wieder zu verbinden.",
sy_sysip_err:"Ungültige IP des Syslog-Servers",sy_sysport_err:"Syslog-Port muss zwischen 1 und 65535 liegen",
sy_sysip_err:"Ungültige IP des Syslog-Servers",sy_dns1:"DNS-Server",sy_dns2:"Zweiter DNS-Server",sy_dns_err:"Ungültige IP des DNS-Servers",sy_dns_dhcp:"von DHCP: {ip}",sy_sysport_err:"Syslog-Port muss zwischen 1 und 65535 liegen",
sy_time:"Uhrzeit",sy_ntp:"NTP",sy_ntpsrv:"NTP-Server",sy_ntpsrv_p:"Hostname oder IPv4-Adresse",sy_ntpint:"Abfrageintervall [min]",sy_tz:"Zeitzone",sy_dst:"Sommerzeit",sy_dst_eu:"Europa",sy_dst_us:"USA",sy_now:"Lokale Zeit",sy_sync:"Letzte Synchronisation",sy_ntpaddr:"Server-Adresse",sy_nosync:"nicht synchronisiert",sy_waiting:"warte auf den Server",sy_ago:"vor {n} min",sy_ntp_err:"Hostname oder IPv4-Adresse des NTP-Servers eingeben",sy_ntpint_err:"Abfrageintervall muss zwischen 1 und 1440 liegen",sy_time_note:"Der Switch hat keine batteriegepufferte Uhr: Die Zeit geht bei jedem Neustart verloren und wird neu vom NTP-Server bezogen, sobald das Netzwerk bereit ist. Im Flash speichern, damit die Einstellungen erhalten bleiben.",
sy_pw_len:"Passwort: 1-20 Zeichen",sy_pw_space:"Das Passwort darf keine Leerzeichen enthalten",sy_pw_match:"Die Passwörter stimmen nicht überein",
sy_pw_q:"Admin-Passwort ändern?",sy_pw_d:"Gilt sofort für neue Anmeldungen. Im Flash speichern, damit es erhalten bleibt.",
sy_reboot_q:"Switch neu starten?",sy_reboot_d:"Es gibt UNGESPEICHERTE Änderungen, sie gehen verloren. Zuerst im Flash speichern, wenn du sie behalten willst.",
@@ -958,6 +964,7 @@ function pollStatus(){
});
}
var statusPoller=new Poller(pollStatus,2500);
statusPoller.start();
function needPorts(fn){
if(S.n)return fn();
pollStatus().then(fn).catch(function(){});
@@ -1102,8 +1109,7 @@ function dashStatus(){
});
}
tabHooks.dash={
enter:function(){statusPoller.start();pollInfo().catch(function(){})},
leave:function(){statusPoller.stop()},
enter:function(){pollInfo().catch(function(){})},
status:dashStatus,
};
@@ -1147,31 +1153,12 @@ function loadMtu(){
});
});
}
/* One learned MAC is the attached device; several means a switch or AP
* sits behind the port, so only the count is shown. */
function portsMacs(){
l2Load().then(function(all){
var by={};
all.forEach(function(e){
if(e.pport==="CPU")return;
var l=by[e.pport]=by[e.pport]||[];
if(l.indexOf(e.mac)<0)l.push(e.mac);
});
S.ports.forEach(function(p){
var el=$("pmac"+(p.portNum-1));
if(!el)return;
var m=by[p.portNum]||[];
el.textContent=!m.length?"-":(m.length===1?m[0]:m.length+" "+t("c_devices"));
el.title=m.join("\n");
});
}).catch(function(){});
}
var _macTick=0;
function portsStatus(){
buildPorts();
if(++_macTick%10===0)portsMacs();
S.ports.forEach(function(p){
var i=p.portNum-1,el=$("plink"+i);
var mc=$("pmac"+i);
if(mc)mc.textContent=parseInt(p.macs,16)&0x1fff;
if(el)el.innerHTML=linkBadge(p);
var sel=$("pspd"+i),v=p.isSFP?0:ADVSEL[parseInt(p.adv,2)];
if(sel&&v&&document.activeElement!==sel)sel.value=v;
@@ -1199,8 +1186,7 @@ function applyPort(i){
postCmds(cmds).then(loadMtu).catch(function(){});
}
tabHooks.ports={
enter:function(){statusPoller.start();needPorts(function(){buildPorts();portsMacs()})},
leave:function(){statusPoller.stop();l2Gen++},
enter:function(){needPorts(buildPorts)},
status:portsStatus,
};
@@ -1456,8 +1442,7 @@ function statsStatus(){
});
}
tabHooks.stats={
enter:function(){statusPoller.start()},
leave:function(){statusPoller.stop();if(ctrPoll)ctrPoll.stop()},
leave:function(){if(ctrPoll)ctrPoll.stop()},
status:statsStatus,
};
+18 -14
View File
@@ -55,11 +55,11 @@ a{color:var(--ac);text-decoration:none}
#navfoot{padding:10px 16px;border-top:1px solid var(--bd);font-size:12px;color:var(--fg2)}
#wrap{flex:1;min-width:0;display:flex;flex-direction:column}
#top{
display:flex;align-items:center;gap:10px;padding:10px 20px;
display:flex;align-items:center;flex-wrap:wrap;gap:10px;padding:8px 20px;
background:var(--panel);border-bottom:1px solid var(--bd);position:sticky;top:0;z-index:5;
}
#burger{display:none;background:none;border:0;cursor:pointer;padding:4px}
#ttitle{font-size:16px;font-weight:650;flex:1}
#ttitle{font-size:16px;font-weight:650;min-width:120px}
#dirty{
display:none;font-size:12px;font-weight:600;color:var(--warn);
border:1px solid var(--warn);border-radius:20px;padding:2px 10px;
@@ -112,19 +112,21 @@ label.row span.lb{width:110px;flex:none;color:var(--fg2);font-size:13px}
}
.switch input:checked+i{background:var(--ok)}
.switch input:checked+i:before{transform:translateX(15px)}
#strip{display:flex;gap:8px;flex-wrap:wrap;padding:4px 0 2px}
#strip{display:flex;gap:6px;flex:1;min-width:0;overflow-x:auto;scrollbar-width:thin}
.port:first-child{margin-left:auto}
.port:last-child{margin-right:auto}
.port{
width:46px;border:2px solid var(--bd);border-radius:7px;padding:5px 0 3px;
width:40px;border:2px solid var(--bd);border-radius:7px;padding:3px 0 2px;
text-align:center;cursor:pointer;background:var(--panel2);position:relative;
}
.port.sfp{width:56px}
.port .pn{font-weight:650;font-size:13px}
.port .ps{font-size:10px;color:var(--fg2);display:block;line-height:1.2}
.port.sfp{width:50px}
.port .pn{font-weight:650;font-size:12px}
.port .ps{font-size:9px;color:var(--fg2);display:block;line-height:1.2}
.port .jack{
display:block;width:18px;height:12px;margin:3px auto 2px;border-radius:2px;
border:2px solid var(--fg2);border-top-width:5px;opacity:.55;
display:block;width:16px;height:10px;margin:2px auto 1px;border-radius:2px;
border:2px solid var(--fg2);border-top-width:4px;opacity:.55;
}
.port.sfp .jack{width:28px;height:10px;border-top-width:3px}
.port.sfp .jack{width:24px;height:9px;border-top-width:3px}
.port.up{border-color:var(--pc);background:color-mix(in srgb,var(--pc) 10%,var(--panel2))}
.port.up .jack{border-color:var(--pc);opacity:1}
.port.up .ps{color:var(--pc);font-weight:600}
@@ -183,6 +185,10 @@ button.danger{background:var(--bad);color:#fff;border-color:transparent}
#ttitle{font-size:14px;overflow:hidden;text-overflow:ellipsis;white-space:nowrap}
#dirty{padding:2px 7px}
}
@media (max-width:720px){
#ttitle{flex:1}
#strip{order:3;flex-basis:100%}
}
</style>
<script>
(function(){
@@ -215,15 +221,13 @@ button.danger{background:var(--bad);color:#fff;border-color:transparent}
<svg width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="var(--fg)" stroke-width="2"><path d="M4 6h16M4 12h16M4 18h16"/></svg>
</button>
<span id="ttitle"></span>
<div id="strip"></div>
<span id="dirty" data-i18n="hdr_dirty" data-i18n-t="hdr_dirty_t">unsaved changes</span>
<button class="ctl pri" id="saveBtn" data-i18n="hdr_save" data-i18n-t="hdr_save_t">Save to flash</button>
</header>
<main>
<section class="tab act" id="tab-dash">
<div class="card"><h2><span data-i18n="d_ports">Ports</span> <span class="hint" data-i18n="d_ports_h">click a port for details</span></h2>
<div id="strip"></div>
</div>
<div class="grid2">
<div class="card"><h2 data-i18n="d_system">System</h2><table class="t" id="sysinfo"></table></div>
<div class="card"><h2><span data-i18n="d_traffic">Traffic</span> <span class="hint" data-i18n="d_traffic_h">packets/s, live</span></h2>
@@ -237,7 +241,7 @@ button.danger{background:var(--bad);color:#fff;border-color:transparent}
<section class="tab" id="tab-ports">
<div class="card"><h2 data-i18n="pt_title">Port configuration</h2>
<div class="scrollx"><table class="t" id="ptable">
<thead><tr><th data-i18n="c_port">Port</th><th data-i18n="c_name">Name</th><th data-i18n="c_link">Link</th><th data-i18n="pt_conn">Connected</th><th data-i18n="pt_speed">Speed</th><th data-i18n="c_enabled">Enabled</th><th data-i18n="pt_mtu">MTU</th><th></th></tr></thead>
<thead><tr><th data-i18n="c_port">Port</th><th data-i18n="c_name">Name</th><th data-i18n="c_link">Link</th><th data-i18n="pt_conn">Devices learned</th><th data-i18n="pt_speed">Speed</th><th data-i18n="c_enabled">Enabled</th><th data-i18n="pt_mtu">MTU</th><th></th></tr></thead>
<tbody></tbody></table></div>
<p class="small mut" style="margin-top:8px" data-i18n="pt_sfp_note">SFP speed is set per slot and is not saved in the startup config.</p>
</div>
+3
View File
@@ -1111,6 +1111,9 @@ void send_status(void)
slen += strtox(outbuf + slen, "\",\"rxB\":\"0x");
STAT_GET(STAT_COUNTER_ERR_PKTS, i);
reg_to_html(RTL837X_STAT_V_HIGH); // 32bit RX packet errors
slen += strtox(outbuf + slen, "\",\"macs\":\"0x");
reg_to_html(RTL837X_L2_LRN_PORT_CONSTRT_CNT + (i << 2));
slen += strtox(outbuf + slen, "\"}");
if (i < machine.max_port)
char_to_html(',');
+1
View File
@@ -0,0 +1 @@
PCB_SWTG024AS_A_2_0_1.c
+6
View File
@@ -0,0 +1,6 @@
#ifndef _MACHINE_GT_ST024M_H_
#define _MACHINE_GT_ST024M_H_
#define MACHINE_MODEL "GoodTop GT-ST024M"
#endif
+1
View File
@@ -38,4 +38,5 @@ __code const struct machine machine = {
0,
},
},
.mac_flash_offset = 0x1FC000,
};
+216
View File
@@ -0,0 +1,216 @@
#include <stdint.h>
#include <stdbool.h>
#include "rtl837x_common.h"
#include "rtl837x_sfr.h"
#include "rtl837x_cabletest.h"
#include "rtl837x_phy.h"
#include "phy.h"
#include "machine.h"
#pragma codeseg BANK3
#pragma constseg BANK3
#define RTCT_CTRL 0xa422
#define RTCT_CTRL_START 0x00f1
#define RTCT_CTRL_DONE 0x8000
#define RTCT_SRAM_ADDR 0xa436
#define RTCT_SRAM_DATA 0xa438
#define RTCT_SRAM_STATUS 0x8026
#define RTCT_SRAM_LEN 0x8028
#define RTCT_ST_BUSY 0x01
#define RTCT_ST_MISMATCH 0x06
#define RTCT_ST_OPEN 0x08
#define RTCT_ST_SHORT 0x10
#define RTCT_ST_OK 0x20
#define RTCT_ST_DONE 0x40
#define RTCT_ST_CROSS_SHORT 0x80
#define RTCT_LEN_ZERO 150
#define RTCT_LEN_PER_50CM 43
#define PHY_POWER 0xa610
#define PHY_POWER_OFF 0x0800
#define BMCR_PDOWN_1G_FULL 0x0940
#define BMCR_1G_FULL 0x0140
#define BMCR_ANRESTART 0x0200
#define CT_IDLE 0
#define CT_SILENCE 1
#define CT_RUN 2
#define CT_POLL_TICKS (SYS_TICK_HZ / 20)
#define CT_POLLS 100
extern volatile __xdata uint32_t ticks;
extern __code const struct machine machine;
extern __xdata struct machine_runtime machine_detected;
__xdata uint8_t ct_state = CT_IDLE;
__xdata uint8_t ct_port;
__xdata uint8_t ct_last;
__xdata uint8_t ct_polls;
__xdata uint8_t ct_valid;
__xdata uint8_t ct_have = 0;
__xdata uint8_t ct_pair;
__xdata uint8_t ct_flags;
__xdata uint16_t ct_bmcr;
__xdata uint8_t ct_result[4];
__xdata uint16_t ct_len[4];
void rtl_phy_cable_test_start(uint8_t port) __banked
{
phy_write(port, PHY_MMD31, PHY_MMD31_FEDCR, BMCR_1G_FULL);
phy_write(port, PHY_MMD31, RTCT_CTRL, RTCT_CTRL_START);
}
bool rtl_phy_cable_test_get_status(uint8_t port) __banked
{
phy_read(port, PHY_MMD31, RTCT_CTRL);
if (!(SFR_DATA_U16 & RTCT_CTRL_DONE))
return false;
for (ct_pair = 0; ct_pair < 4; ct_pair++) {
// The flags are the low byte of the word, not the high one.
phy_write(port, PHY_MMD31, RTCT_SRAM_ADDR, RTCT_SRAM_STATUS + (ct_pair << 2));
phy_read(port, PHY_MMD31, RTCT_SRAM_DATA);
ct_flags = SFR_DATA_U16;
if (!(ct_flags & RTCT_ST_DONE))
return false;
if (ct_flags & RTCT_ST_OK)
ct_result[ct_pair] = CABLE_RESULT_OK;
else if (ct_flags & RTCT_ST_OPEN)
ct_result[ct_pair] = CABLE_RESULT_OPEN;
else if (ct_flags & RTCT_ST_SHORT)
ct_result[ct_pair] = CABLE_RESULT_SAME_SHORT;
else if (ct_flags & RTCT_ST_BUSY)
ct_result[ct_pair] = CABLE_RESULT_UNSPEC;
else if (ct_flags & RTCT_ST_CROSS_SHORT)
ct_result[ct_pair] = CABLE_RESULT_CROSS_SHORT;
else if (ct_flags & RTCT_ST_MISMATCH)
ct_result[ct_pair] = CABLE_RESULT_MISMATCH;
else
ct_result[ct_pair] = CABLE_RESULT_UNSPEC;
phy_write(port, PHY_MMD31, RTCT_SRAM_ADDR, RTCT_SRAM_LEN + (ct_pair << 2));
phy_read(port, PHY_MMD31, RTCT_SRAM_DATA);
ct_len[ct_pair] = SFR_DATA_U16;
ct_len[ct_pair] = ct_len[ct_pair] > RTCT_LEN_ZERO ? ct_len[ct_pair] - RTCT_LEN_ZERO : 0;
ct_len[ct_pair] = ct_len[ct_pair] * 5 / RTCT_LEN_PER_50CM;
}
return true;
}
static void cabletest_print(void)
{
print_string("Cable test port ");
print_phys_port(ct_port);
if (!ct_valid) {
print_string(": timed out\n");
return;
}
write_char('\n');
for (ct_pair = 0; ct_pair < 4; ct_pair++) {
print_string("Pair ");
write_char('A' + ct_pair);
switch (ct_result[ct_pair]) {
case CABLE_RESULT_OK:
print_string(": ok\n");
continue;
case CABLE_RESULT_OPEN:
print_string(": open at ");
break;
case CABLE_RESULT_SAME_SHORT:
print_string(": short at ");
break;
case CABLE_RESULT_CROSS_SHORT:
print_string(": short to another pair at ");
break;
case CABLE_RESULT_MISMATCH:
print_string(": impedance mismatch at ");
break;
default:
print_string(": unknown\n");
continue;
}
itoa_short(ct_len[ct_pair] / 10);
write_char('.');
write_char('0' + ct_len[ct_pair] % 10);
print_string(" m\n");
}
}
void cabletest_run(void) __banked
{
if (ct_state) {
print_string("Cable test running on port ");
print_phys_port(ct_port);
write_char('\n');
return;
}
if (!machine_detected.isRTL8373 && (phy_settings.port < 4 || phy_settings.port > 7)) {
print_string("No cable test on this PHY\n");
return;
}
// A test started on a port that is off never ends and blocks the PHY until reset.
phy_read(phy_settings.port, PHY_MMD31, PHY_POWER);
if (SFR_DATA_U16 & PHY_POWER_OFF) {
print_string("Port is off\n");
return;
}
ct_port = phy_settings.port;
phy_read(ct_port, PHY_MMD31, PHY_MMD31_FEDCR);
ct_bmcr = SFR_DATA_U16;
// Forcing the speed leaves the link up; only a power-down silences the partner.
phy_write(ct_port, PHY_MMD31, PHY_MMD31_FEDCR, BMCR_PDOWN_1G_FULL);
ct_last = ticks;
ct_have = 0;
ct_state = CT_SILENCE;
print_string("Cable test started\n");
}
void cabletest_show(void) __banked
{
if (ct_state)
print_string("Cable test running\n");
else if (ct_port != phy_settings.port || !ct_have)
print_string("No cable test result\n");
else
cabletest_print();
}
void cabletest_tick(void) __banked
{
if (ct_state == CT_SILENCE) {
if (!TICKS_DUE(ct_last, SYS_TICK_HZ))
return;
phy_read(ct_port, PHY_MMD31, PHY_POWER);
if (SFR_DATA_U16 & PHY_POWER_OFF) {
phy_write(ct_port, PHY_MMD31, PHY_MMD31_FEDCR, ct_bmcr);
ct_state = CT_IDLE;
print_string("Cable test aborted, port is off\n");
return;
}
rtl_phy_cable_test_start(ct_port);
ct_polls = CT_POLLS;
ct_last = ticks;
ct_state = CT_RUN;
return;
}
if (ct_state != CT_RUN || !TICKS_DUE(ct_last, CT_POLL_TICKS))
return;
ct_last = ticks;
ct_valid = rtl_phy_cable_test_get_status(ct_port);
if (!ct_valid && --ct_polls)
return;
phy_write(ct_port, PHY_MMD31, RTCT_CTRL, 0);
phy_write(ct_port, PHY_MMD31, PHY_MMD31_FEDCR, ct_bmcr | BMCR_ANRESTART);
ct_have = 1;
ct_state = CT_IDLE;
cabletest_print();
}
+21
View File
@@ -0,0 +1,21 @@
#ifndef _RTL837X_CABLETEST_H_
#define _RTL837X_CABLETEST_H_
#include <stdint.h>
#include <stdbool.h>
#define CABLE_RESULT_UNSPEC 0
#define CABLE_RESULT_OK 1
#define CABLE_RESULT_OPEN 2
#define CABLE_RESULT_SAME_SHORT 3
#define CABLE_RESULT_CROSS_SHORT 4
#define CABLE_RESULT_MISMATCH 5
void rtl_phy_cable_test_start(uint8_t port) __banked;
bool rtl_phy_cable_test_get_status(uint8_t port) __banked;
void cabletest_run(void) __banked;
void cabletest_show(void) __banked;
void cabletest_tick(void) __banked;
#endif
+4 -1
View File
@@ -510,7 +510,7 @@ void port_media_show(uint8_t port) __banked
void port_stats_print(void) __banked
{
print_string("\nPort\tState\tLink\tTxGood\t\tTxBad\t\tRxGood\t\tRxBad\n");
print_string("\nPort\tState\tLink\tTxGood\t\tTxBad\t\tRxGood\t\tRxBad\t\tMACs\n");
for (uint8_t i = machine.min_port; i <= machine.max_port; i++) {
print_phys_port(i); write_char('\t');
@@ -584,6 +584,9 @@ void port_stats_print(void) __banked
STAT_GET(STAT_COUNTER_ERR_PKTS, i);
print_reg(RTL837X_STAT_V_HIGH); write_char('\t');
reg_read(RTL837X_L2_LRN_PORT_CONSTRT_CNT + (i << 2));
itoa_short(SFR_DATA_U16 & L2_LRN_PORT_CNT_MASK);
print_string("\n");
}
}
+2
View File
@@ -214,6 +214,8 @@
#define RTL837x_L2_TBL_FLUSH_CNF 0x53dc
#define RTL837X_L2_LRN_PORT_CONSTRAINT 0x5384
#define RTL837X_L2_LRN_PORT_CONSTRT_ACT 0x4f80
#define RTL837X_L2_LRN_PORT_CONSTRT_CNT 0x53ac
#define L2_LRN_PORT_CNT_MASK 0x1fff
#define RTL8373_REG_MAC_L2_PORT_MAX_LEN 0x1250
/*
+2 -1
View File
@@ -19,6 +19,7 @@
#include "rtl837x_bandwidth.h"
#include "rtl837x_init.h"
#include "rtl837x_lldp.h"
#include "rtl837x_cabletest.h"
#include "dhcp.h"
#include "cmd_parser.h"
#include "cmd_editor.h"
@@ -33,7 +34,6 @@
#include "httpd/page_impl.h"
#include "boot.h"
#include "sfp.h"
#include "crypto/chacha.h"
extern __code const struct machine machine;
extern __xdata uint32_t flash_size;
@@ -1302,6 +1302,7 @@ static void handle_tick(void)
health_phase(HEALTH_PH_STP);
lldp_tick();
health_phase(HEALTH_LLDP);
cabletest_tick();
}
+18
View File
@@ -185,6 +185,23 @@ static void t_l2_page(void)
CHECK(balanced(), "every page is valid JSON");
}
static void t_status_macs(void)
{
const char *p1, *end, *mac;
printf("[test] /status.json reports the MAC addresses learned on each port\n");
hw_reset(); out_reset();
hw_reg_set(RTL837X_L2_LRN_PORT_CONSTRT_CNT + (1 << 2), 0x21);
send_status();
p1 = strstr(body(), "\"logPort\":1,");
end = p1 ? strchr(p1, '}') : NULL;
mac = p1 ? strstr(p1, "\"macs\":\"0x21\"") : NULL;
CHECK(mac && mac < end, "port 1 shows its learned count");
CHECK(has("\"macs\":\"0x0\""), "a port without learned addresses shows 0");
CHECK(slen <= TCP_OUTBUF_SIZE, "fits the buffer");
CHECK(balanced(), "valid JSON");
}
static void t_status_counters(void)
{
printf("[test] /status.json prints 64-bit packet counters in full\n");
@@ -211,6 +228,7 @@ int main(void)
t_l2();
t_l2_page();
t_status_counters();
t_status_macs();
printf("\n%d checks, %d failed\n", tests_run, tests_failed);
return tests_failed ? 1 : 0;
}
+52
View File
@@ -0,0 +1,52 @@
#!/usr/bin/env python3
"""Compile the current firmware execute_config() with mocked flash/tokenizer edges."""
import pathlib, subprocess
root=pathlib.Path(__file__).resolve().parent.parent
src=(root/'cmd_parser.c').read_text()
start=src.index('void execute_config(void) __banked')
end=src.index('// Execute multiple commands',start)
body=src[start:end]
out=root/'test/build';out.mkdir(exist_ok=True)
harness=r"""
#include <stdint.h>
#include <stdio.h>
#include <string.h>
#include <assert.h>
#define __xdata
#define __banked
#define CONFIG_START 0
#define CONFIG_LEN 1024
#define FLASH_READ_BURST_SIZE 256
#define CMD_BUF_SIZE 64
#define ERR_OK 0
#define NUL 0
#define PASSWORD "test-default"
uint8_t flash_buf[256],cmd_buffer[64],save_cmd,err_status;
char passwd[32],image[1024],seen[16][64];int count;
struct {uint32_t addr;uint16_t len;} flash_region;
void strtox(char*d,const char*s){strcpy(d,s);}
void flash_read_bulk(uint8_t*d){memcpy(d,image+flash_region.addr,256);}
void print_string(const char*s){(void)s;}
void print_string_x(uint8_t*s){(void)s;}
void write_char(char c){(void)c;}
void clear_command_history(void){}
void cmd_tokenize(void){err_status=cmd_buffer[0]=='#'?1:0;}
void cmd_parser(void){assert(count<16);strcpy(seen[count++],(char*)cmd_buffer);}
"""
tests=r"""
void run(const char*s){memset(image,0,sizeof image);strncpy(image,s,sizeof image-1);count=0;execute_config();assert(save_cmd==1);}
int main(void){
run("one\ntwo\n");assert(count==2&&!strcmp(seen[1],"two"));
run("one\rtwo\r");assert(count==2&&!strcmp(seen[0],"one"));
run("one\r\ntwo\r\n");assert(count==2&&!strcmp(seen[1],"two"));
run("\r\n# ignored\r\none\r\n\r\ntwo");assert(count==2&&!strcmp(seen[0],"one"));
run("one\0ignored");assert(count==1&&!strcmp(seen[0],"one"));
char longline[300];memset(longline,'x',sizeof longline);strcpy(longline+280,"\r\nok\r\n");run(longline);assert(count==1&&!strcmp(seen[0],"ok"));
/* CR at the end of a flash burst, LF at the start of the next. */
char boundary[300];memset(boundary,'\n',255);strcpy(boundary+255,"\r\nok\r\n");run(boundary);assert(count==1&&!strcmp(seen[0],"ok"));
puts("startup CR/LF/CRLF, comments, NUL, overlong and burst-boundary cases passed");return 0;
}
"""
(out/'startup_crlf.c').write_text(harness+body+tests)
subprocess.run(['gcc','-std=gnu11','-Wall','-Wextra','-Werror','-g','-fsanitize=address,undefined','-fno-omit-frame-pointer','-no-pie',str(out/'startup_crlf.c'),'-o',str(out/'test_startup_crlf')],check=True)
subprocess.run([str(out/'test_startup_crlf')],check=True)
+7
View File
@@ -56,6 +56,7 @@ time_t last_session_use;
uint64_t txG[PORTS], txB[PORTS], rxG[PORTS], rxB[PORTS];
char txG_buff[20], txB_buff[20], rxG_buff[20], rxB_buff[20], counter_buf[20], counter_name[20];
char macs_buff[8];
char sfp_temp[8], sfp_vcc[8], sfp_txbias[8], sfp_txpower[8], sfp_rxpower[8], sfp_laser[8], sfp_options[6];
char num_buff[20];
char l2_idx_buff[20];
@@ -180,6 +181,12 @@ void send_status(int s)
json_object_object_add(v, "txB", json_object_new_string(txB_buff));
json_object_object_add(v, "rxG", json_object_new_string(rxG_buff));
json_object_object_add(v, "rxB", json_object_new_string(rxB_buff));
uint16_t macs = 0;
for (int e = 0; e < L2_ENTRY_NUM; e++)
if (l2_entries[e].port == physToLogPort[i-1] && l2_entries[e].type != 's')
macs++;
sprintf(macs_buff, "0x%04x", macs);
json_object_object_add(v, "macs", json_object_new_string(macs_buff));
if (i >= PORTS - NSFP) {
uint16_t temp = 0x28fb + rand() / (RAND_MAX / 100);
uint16_t vcc = 0x7eda + rand() / (RAND_MAX / 100);