Compare commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
1400431337 | ||
|
|
119b5ddf13 | ||
|
|
231b43b264 | ||
|
|
e53ebddc21 | ||
|
|
f23eb6bebb | ||
|
|
cd51cffb1d | ||
|
|
01fc5db9a3 | ||
|
|
bdca888ecd | ||
|
|
e8431999e5 | ||
|
|
7ca4d2c8dc | ||
|
|
32dd21bbd3 | ||
|
|
92f9025109 | ||
|
|
b65aa82d78 | ||
|
|
851eb39344 | ||
|
|
970e98f4c5 | ||
|
|
4d009bc8e8 | ||
|
|
eb51128ec7 | ||
|
|
b86fd37eaa | ||
|
|
b92a773cad | ||
|
|
068e7467af | ||
|
|
da4f5058a7 | ||
|
|
fee4f17bff | ||
|
|
e19271fc44 | ||
|
|
aff6ac0923 | ||
|
|
7e11be0d97 | ||
|
|
23862bc648 | ||
|
|
6c62791d23 | ||
|
|
413690a748 | ||
|
|
3910888052 | ||
|
|
a0cb3c1850 | ||
|
|
73bb4c8af2 | ||
|
|
0443ba428b | ||
|
|
62abd08aa2 | ||
|
|
70426ff24a | ||
|
|
2d62cc493d | ||
|
|
bbce2d6727 | ||
|
|
36de7b7241 | ||
|
|
ea320d813d | ||
|
|
ca2abcc4e1 | ||
|
|
4d7159092c | ||
|
|
98763e3bff | ||
|
|
80a30b705e | ||
|
|
4c3e1fde81 | ||
|
|
5cd14e6227 | ||
|
|
5105018d90 | ||
|
|
5bc7cf43db | ||
|
|
c776d10129 | ||
|
|
e95e450813 | ||
|
|
c5f62bff95 | ||
|
|
7535c0d9b7 | ||
|
|
a57319557d | ||
|
|
1b29fb687f | ||
|
|
2e62f9eea7 | ||
|
|
1d158d21d1 | ||
|
|
2d5424fedc | ||
|
|
23c8b2a8b3 | ||
|
|
c9f6362da5 | ||
|
|
742aef5461 | ||
|
|
a77d219baf | ||
|
|
f94204e56f | ||
|
|
8d6adedb56 | ||
|
|
c3f9acea10 | ||
|
|
c46261e052 | ||
|
|
834403b97b | ||
|
|
48087d3520 | ||
|
|
1d359afba5 | ||
|
|
ec14dbf323 | ||
|
|
deedb92c5d | ||
|
|
3c00ab0ae9 | ||
|
|
6d539bfb1b | ||
|
|
2a9f9b2508 | ||
|
|
723d16b4cb | ||
|
|
c263a8ab64 | ||
|
|
c02f660c08 |
No files matched your search
@@ -47,6 +47,12 @@
|
||||
- The web UI and the configuration upload now refuse a line the replay cannot take,
|
||||
instead of writing it to flash.
|
||||
|
||||
- Startup
|
||||
- Initialised and zero-initialised globals above xdata 0x4000 now get their values at boot.
|
||||
Hardware reset leaves SFR 0x97 at 1, which maps xdata 0x4000-0xbfff to the flash image,
|
||||
and it was cleared only in `main()`, after the C startup code had copied and cleared them.
|
||||
This showed as a random web session timeout and LLDP blocked on some ports without config.
|
||||
|
||||
## Breaking changes
|
||||
|
||||
- Config
|
||||
|
||||
@@ -4,11 +4,25 @@ DEFAULT_CONFIG_LOCATION = 454656
|
||||
CONFIG_LOCATION = 458752
|
||||
HTML_LOCATION = 262144
|
||||
|
||||
SDCC_PREFIX = toolchain/sdcc-$(shell cat sdcc/* | cksum | cut -d' ' -f1)
|
||||
# Startup configuration embedded in the image. Override it to build a specific
|
||||
# machine with its own configuration: make MACHINE=<name> CONFIG=<file>
|
||||
CONFIG ?= config.txt
|
||||
|
||||
SDCC_TOOLCHAIN = sdcc-$(shell cat sdcc/* | cksum | cut -d' ' -f1)
|
||||
SDCC_PREFIX = toolchain/$(SDCC_TOOLCHAIN)
|
||||
ifeq ($(origin CC),default)
|
||||
SDCC = $(SDCC_PREFIX)/bin/sdcc
|
||||
CC = $(SDCC)
|
||||
ASM = $(SDCC_PREFIX)/bin/sdas8051
|
||||
SDCC_REV := $(shell git log -1 --format=%h -- sdcc 2>/dev/null)
|
||||
ifneq ($(SDCC_REV),)
|
||||
ifneq ($(shell git status --porcelain --untracked-files=no -- sdcc 2>/dev/null),)
|
||||
SDCC_REV := $(SDCC_REV)-dirty
|
||||
endif
|
||||
endif
|
||||
SDCC_BANNER = $(if $(SDCC_REV),sdcc/ at $(SDCC_REV),$(SDCC_TOOLCHAIN)) ($(words $(wildcard sdcc/*.patch)) patches)
|
||||
else
|
||||
SDCC_BANNER = system
|
||||
endif
|
||||
CC_FLAGS = -mmcs51 -I. -Imachine -Ihttpd -Iuip
|
||||
ASM ?= sdas8051
|
||||
@@ -46,6 +60,7 @@ ifndef BUILDDIR
|
||||
BUILDDIR = output/$(MACHINE)
|
||||
endif
|
||||
|
||||
CONFIG_STAMP := $(BUILDDIR)/.config
|
||||
VERSION_HEADER := version.h
|
||||
|
||||
GIT_VERSION := $(shell git rev-parse --short HEAD)
|
||||
@@ -68,9 +83,10 @@ BUILD_DATE := $(shell date -u -d @$(SOURCE_DATE_EPOCH) +"%Y-%m-%d %H:%M:%S" 2>/d
|
||||
|| date -u -r $(SOURCE_DATE_EPOCH) +"%Y-%m-%d %H:%M:%S")
|
||||
endif
|
||||
|
||||
# $(MACHINE_SRC) is listed here so an unknown $MACHINE fails before anything else
|
||||
# is built, including the tools/ subdirectory.
|
||||
all: create_build_dir $(VERSION_HEADER) $(MACHINE_SRC) $(SUBDIRS) $(BUILDDIR)/rtlplayground-$(FILENAME_EXTENSION).bin
|
||||
# $(MACHINE_SRC) and $(CONFIG) are listed here so an unknown $MACHINE or a
|
||||
# missing config file fails before anything else is built, including the tools/
|
||||
# subdirectory.
|
||||
all: create_build_dir $(VERSION_HEADER) $(MACHINE_SRC) $(CONFIG) $(SUBDIRS) $(BUILDDIR)/rtlplayground-$(FILENAME_EXTENSION).bin
|
||||
|
||||
bank_size:
|
||||
tools/bank_occupancy.sh "$(MACHINE)"
|
||||
@@ -91,6 +107,12 @@ $(MACHINE_SRC):
|
||||
@echo " Example: make MACHINE=KP_9000_6XHML_X2" >&2
|
||||
@false
|
||||
|
||||
# Fail with a clear message when CONFIG names a file that does not exist.
|
||||
$(CONFIG):
|
||||
@echo "ERROR: CONFIG='$(CONFIG)' does not exist." >&2
|
||||
@echo " Point CONFIG at a configuration file, or omit it to use config.txt." >&2
|
||||
@false
|
||||
|
||||
# Keep the per-machine definition in first position to fail immediately on an
|
||||
# invalid $MACHINE value
|
||||
SRCS = \
|
||||
@@ -128,10 +150,12 @@ SRCS += \
|
||||
rtl837x_lldp.c \
|
||||
rtl837x_storm.c \
|
||||
rtl837x_lacp.c \
|
||||
rtl837x_trap.c
|
||||
rtl837x_trap.c \
|
||||
rtl837x_stp_cli.c
|
||||
SRCS += \
|
||||
httpd/httpd.c \
|
||||
httpd/page_impl.c
|
||||
httpd/page_impl.c \
|
||||
httpd/page_stp.c
|
||||
SRCS += \
|
||||
uip/timer.c \
|
||||
uip/uip.c \
|
||||
@@ -172,6 +196,7 @@ $(VERSION_HEADER): FORCE
|
||||
@printf '%s\n' "#ifndef VERSION_H" "#define VERSION_H" \
|
||||
"#define VERSION_SW \"$(VERSION_EXTENSION)\"" \
|
||||
"#define BUILD_DATE \"$(BUILD_DATE)\"" \
|
||||
"#define SDCC_TOOLCHAIN \"$(SDCC_BANNER)\"" \
|
||||
"#endif" > $(VERSION_HEADER).tmp
|
||||
@cmp -s $(VERSION_HEADER).tmp $(VERSION_HEADER) \
|
||||
&& rm -f $(VERSION_HEADER).tmp \
|
||||
@@ -209,6 +234,12 @@ FORCE:
|
||||
$(CCFLAGS_STAMP): FORCE | create_build_dir
|
||||
@echo '$(CC_FLAGS)' | cmp -s - $@ 2>/dev/null || echo '$(CC_FLAGS)' > $@
|
||||
|
||||
# The same for $(CONFIG): its path is stamped so switching to a config file
|
||||
# older than the image still rebuilds it, while $(CONFIG) itself as a normal
|
||||
# prerequisite covers edits to the selected file.
|
||||
$(CONFIG_STAMP): FORCE | create_build_dir
|
||||
@echo '$(CONFIG)' | cmp -s - $@ 2>/dev/null || echo '$(CONFIG)' > $@
|
||||
|
||||
$(BUILDDIR)/%.rel: %.c $(CCFLAGS_STAMP) | create_build_dir html_data.h
|
||||
$(CC) -MMD $(CC_FLAGS) -o $@ -c $<
|
||||
|
||||
@@ -217,16 +248,16 @@ $(BUILDDIR)/%.rel: %.asm $(CCFLAGS_STAMP) | create_build_dir
|
||||
# mv -f $(addprefix $(basename $^), .lst .rel .sym) .
|
||||
|
||||
$(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 $@ $^
|
||||
$(CC) $(CC_FLAGS) --xram-size 49151 -Wl-bHOME=0x00000 -Wl-bBANK1=0x14000 -Wl-bBANK2=0x24000 -Wl-bBANK3=0x34000 -Wl-bBANK4=0x44000 -Wl-r -o $@ $^
|
||||
|
||||
$(BUILDDIR)/rtlplayground.img: $(BUILDDIR)/rtlplayground.ihx
|
||||
objcopy --input-target=ihex -O binary $< $@
|
||||
|
||||
$(BUILDDIR)/rtlplayground-$(FILENAME_EXTENSION).bin: $(BUILDDIR)/rtlplayground.img | tools
|
||||
$(BUILDDIR)/rtlplayground-$(FILENAME_EXTENSION).bin: $(BUILDDIR)/rtlplayground.img $(CONFIG) $(CONFIG_STAMP) | tools
|
||||
if [ -e $@ ]; then rm $@; fi
|
||||
tools/$(BUILDDIR)/imagebuilder -i $^ $@
|
||||
tools/$(BUILDDIR)/fileadder -a $(DEFAULT_CONFIG_LOCATION) -s $(IMAGESIZE) -d config.txt $@
|
||||
tools/$(BUILDDIR)/fileadder -a $(CONFIG_LOCATION) -s $(IMAGESIZE) -d config.txt $@
|
||||
tools/$(BUILDDIR)/imagebuilder -i $(BUILDDIR)/rtlplayground.img $@
|
||||
tools/$(BUILDDIR)/fileadder -a $(DEFAULT_CONFIG_LOCATION) -s $(IMAGESIZE) -d "$(CONFIG)" $@
|
||||
tools/$(BUILDDIR)/fileadder -a $(CONFIG_LOCATION) -s $(IMAGESIZE) -d "$(CONFIG)" $@
|
||||
tools/$(BUILDDIR)/fileadder -a $(HTML_LOCATION) -s $(IMAGESIZE) -z -d $(HTML_MIN) -b BANK1 $@
|
||||
tools/$(BUILDDIR)/crc_calculator -u $@
|
||||
ln -sf $(MACHINE)/rtlplayground-$(FILENAME_EXTENSION).bin $(BUILDDIR)/../rtlplayground.bin
|
||||
|
||||
@@ -97,6 +97,14 @@ docker run --rm -v $(pwd):/workspace rtlplayground-dev make MACHINE=DEFAULT_8C_1
|
||||
|
||||
The resulting `.bin` file appears in `output/` on your host.
|
||||
|
||||
Build the OEM web upgrade bin (after building the firmware bin)
|
||||
|
||||
```
|
||||
docker run --rm -v $(pwd):/workspace rtlplayground-dev make -C installer
|
||||
```
|
||||
|
||||
The resulting `rtlplayground_oem_upgrade.bin` file appears in `installer/output/` on your host.
|
||||
|
||||
Build host tools only:
|
||||
|
||||
```
|
||||
@@ -127,6 +135,13 @@ 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).
|
||||
|
||||
To embed a machine-specific startup configuration, pass `CONFIG`:
|
||||
```
|
||||
make MACHINE=DEFAULT_8C_1SFP CONFIG=configs/DEFAULT_8C_1SFP.txt
|
||||
```
|
||||
`CONFIG` defaults to `config.txt`; the selected file is embedded in the image
|
||||
and changing it triggers a rebuild.
|
||||
|
||||
> [!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.
|
||||
@@ -170,9 +185,9 @@ example of compilation console output
|
||||
RTLPlayground/installer$ make
|
||||
mkdir -p output
|
||||
gcc updatebuilder.c -o output/updatebuilder
|
||||
sdas8051 -plosgff -o output/crtstart.rel crtstart.asm
|
||||
sdcc -mmcs51 --code-loc 0x1000 -o output/installer.rel -c installer.c
|
||||
sdcc -mmcs51 -Wl-bHOME=0x1100 -Wl-r -o output/rtlinstaller.ihx output/crtstart.rel output/installer.rel
|
||||
../toolchain/sdcc-3390765201/bin/sdas8051 -plosgff -o output/crtstart.rel crtstart.asm
|
||||
../toolchain/sdcc-3390765201/bin/sdcc -mmcs51 --code-loc 0x1000 -o output/installer.rel -c installer.c
|
||||
../toolchain/sdcc-3390765201/bin/sdcc -mmcs51 -Wl-bHOME=0x1100 -Wl-r -o output/rtlinstaller.ihx output/crtstart.rel output/installer.rel
|
||||
./output/updatebuilder -i output/rtlinstaller.ihx -o output/rtlplayground_oem_upgrade.bin ../output/rtlplayground.bin
|
||||
Input file size: 524288
|
||||
Bytes read: 524288
|
||||
|
||||
@@ -1840,6 +1840,8 @@ void print_gpio_status(void) {
|
||||
void print_sw_version(void) __banked {
|
||||
print_string("Software version: " VERSION_SW);
|
||||
print_string("\nBuild date: " BUILD_DATE);
|
||||
print_string("\nCompiler: SDCC " STR(__SDCC_VERSION_MAJOR) "." STR(__SDCC_VERSION_MINOR) "." STR(__SDCC_VERSION_PATCH) " #" STR(__SDCC_REVISION));
|
||||
print_string("\nToolchain: " SDCC_TOOLCHAIN);
|
||||
print_string("\nHardware: ");
|
||||
print_string(machine.machine_name);
|
||||
write_char('\n');
|
||||
@@ -2015,10 +2017,10 @@ void cmd_parser(void) __banked
|
||||
cmd_error("lacp (show | off); a group starts LACP with: lag <1-4> lacp <port>...\n");
|
||||
} else if (cmd_compare(0, "pvid")) {
|
||||
if (cmd_words_len == 3 && cmd_parse_port_separator(cmd_words_b[1]) != 0
|
||||
&& atoi_short(cmd_words_b[2]) && atoi_results_short && atoi_results_short <= 4094)
|
||||
&& atoi_short(cmd_words_b[2]) && atoi_results_short <= 4094)
|
||||
port_pvid_set(atoi_results_u8, atoi_results_short);
|
||||
else {
|
||||
cmd_error("pvid <port> <1-4094>\n");
|
||||
cmd_error("pvid <port> <0-4094>\n");
|
||||
}
|
||||
} else if (cmd_compare(0, "vlan")) {
|
||||
parse_vlan();
|
||||
|
||||
@@ -0,0 +1,148 @@
|
||||
# Sirivision SR-S25G2109F (S25G1109FP V1.3)
|
||||
|
||||
RTL8373 (non-N) with an RTL8224 quad 2.5G PHY: 8 × 2.5G RJ45 + 1 × SFP+,
|
||||
2 MB flash, no PoE.
|
||||
|
||||
## Label specifications
|
||||
|
||||
| Item | Value |
|
||||
|------|-------|
|
||||
| Manufacturer | Sirivision |
|
||||
| Model | SR-S25G2109F |
|
||||
| Ports | 8 × RJ45 10/100/1000/2500 Mbps, 1 × SFP+ 1G/2.5G/10G |
|
||||
| Power input | 12 V DC, 1.5 A |
|
||||
| Stock firmware | version 1.0.8.0 |
|
||||
| Stock defaults | IP 192.168.1.199, user `admin`, password `admin` |
|
||||
| Serial number | 001008130002003A000005 (also on the PCB barcode sticker) |
|
||||
|
||||
The label does not carry a MAC address. After a factory initialisation the
|
||||
stock firmware uses exactly the values printed on the label.
|
||||
|
||||
## PCB silkscreen and naming
|
||||
|
||||
Top side: `S25G1109FP V1.3 SIRIVISION`. That model name belongs to an unmanaged
|
||||
PoE switch; this board omits the PoE section entirely. What is actually shipped
|
||||
is the label model `SR-S25G2109F`: a simply managed, non-PoE switch. Its stock
|
||||
firmware provides a web interface, but it responds very slowly.
|
||||
|
||||
## CPU, PHY and flash
|
||||
|
||||
| Part | Identification |
|
||||
|------|----------------|
|
||||
| SoC | RTL8373, non-N: `regget 4` returns `0x83730000` and the boot console prints `Detecting CPU: RTL8373` |
|
||||
| PHY | RTL8224, reported as `RTL8224 ID: 0x82240000` |
|
||||
| Flash | GigaDevice 25Q16ESIG (U7), 2 MB, JEDEC id `c8 40 15` |
|
||||
|
||||
The two large ICs sit under soldered heatsinks, their markings are not readable.
|
||||
|
||||
## Build
|
||||
|
||||
```
|
||||
make MACHINE=S25G1109FP_V1_3 CI=1
|
||||
```
|
||||
|
||||
The resulting image can be uploaded through the web interface (`System` page)
|
||||
or the stock web UI.
|
||||
|
||||
## Port mapping
|
||||
|
||||
| Front port | ASIC MAC | Interface |
|
||||
|------------|----------|-----------|
|
||||
| 1–8 | 0–7 | RJ45 2.5G |
|
||||
| 9 | 8 | SFP+ (SDS1) |
|
||||
|
||||
## Board signals
|
||||
|
||||
| Function | GPIO |
|
||||
|----------|------|
|
||||
| SFP detect (ModAbs) | 30, low with a module inserted |
|
||||
| SFP LOS | 37, high without light |
|
||||
| SFP TX disable | not connected, `GPIO_NA` |
|
||||
| SFP I2C | SDA 39 / SCL 40, `I2CBUS(GPIO39_I2C_SDA4, GPIO40_I2C_SCL3_MDC1)` |
|
||||
| Reset button | 54 |
|
||||
|
||||
ModAbs and LOS are separate signals here: GPIO30 tells whether a module is
|
||||
inserted, GPIO37 whether it receives light, so a module is identified even
|
||||
before the link comes up and loss of signal is reported. GPIO38 and GPIO48,
|
||||
which the 8+1 reference boards use for ModAbs and for the reset button, never
|
||||
change on this board; with GPIO38 as ModAbs the firmware believed a module was
|
||||
permanently present and retried the I2C read forever.
|
||||
|
||||
Serial console: the stock firmware runs the loader at 57600 baud and the runtime
|
||||
at 9600 baud, RTLPlayground uses 115200.
|
||||
|
||||
## Front panel
|
||||
|
||||
Each copper port has two LEDs, labelled `Link/Act` (left, yellow) and `2.5G`
|
||||
(right, green). The SFP cage has a single green `Link/Act` LED; next to it are
|
||||
a `Power` LED and the `Reset` button.
|
||||
|
||||
## LED configuration
|
||||
|
||||
Copper ports use LED set 0, the SFP port uses LED set 1:
|
||||
|
||||
| Set | Function 0 (left / SFP) | Function 1 (right) |
|
||||
|-----|-------------------------|--------------------|
|
||||
| SET0 | all speeds, link with activity | 2.5G link |
|
||||
| SET1 | all speeds, link with activity | not populated |
|
||||
|
||||
The board is wired in the stock port order — LED pin `3n+m` belongs to port
|
||||
`n+1`, LED function `m` — so the mux table is the chip default with a single
|
||||
change: the SFP LED sits on pin 25 and is driven by LED function 0, while the
|
||||
default table maps pin 25 to function 1, which is unpopulated in SET1. Without
|
||||
that change the SFP LED stays dark.
|
||||
|
||||
A speed condition alone leaves an LED dark, `LEDS_LINK` has to be set as well.
|
||||
|
||||
## Reset button
|
||||
|
||||
| Hold time while running | Effect |
|
||||
|-------------------------|--------|
|
||||
| less than 3 s | no action |
|
||||
| 3–10 s | reset the switch chip, the firmware reboots (~20 s of downtime) |
|
||||
| more than 10 s | additionally restore the default configuration |
|
||||
|
||||
The system LED blinks slowly while the button is held. Holding the button
|
||||
during power-up instead waits for the release and restores the default
|
||||
configuration when it is released after 10–30 s.
|
||||
|
||||
## MAC address
|
||||
|
||||
The factory MAC sits at `0x1FC000`, six bytes followed by `0xFF` padding. The
|
||||
stock value is a locally administered address (`1A:5D:23:47:4D:EB`), which the
|
||||
firmware rejects, so the device runs with a UUID-derived MAC (`06:…`). A valid
|
||||
globally administered address stored there is used instead.
|
||||
|
||||
## What works
|
||||
|
||||
- all eight 2.5GBASE-T ports at 10/100/1000/2500 Mbps
|
||||
- SFP+ with 10G and 1G modules, including DDM data where the module provides it
|
||||
- 10G: Finisar FCBG110SD1C05 AOC (no DDM), rate `0x67`, SerDes mode `1a`
|
||||
- 1G: ETU ESB5312-3SCD20 (DDM), rate `0x0d`, SerDes mode `04`
|
||||
- LEDs, module insert/remove detection, loss of signal reporting, reset button
|
||||
|
||||
## Unverified
|
||||
|
||||
- 2.5G SFP modules, none was available for testing
|
||||
|
||||
## Photos
|
||||
|
||||
Front panel:
|
||||
|
||||

|
||||
|
||||
Label:
|
||||
|
||||

|
||||
|
||||
PCB top side:
|
||||
|
||||

|
||||
|
||||
PCB bottom side:
|
||||
|
||||

|
||||
|
||||
Flash chip, GigaDevice 25Q16ESIG:
|
||||
|
||||

|
||||
@@ -5,7 +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 | No | PCB-SWTG024AS-A-V2.0.1_19650 | 512K (P25D40SH)| 8372N |
|
||||
| Horaco | HC-SWTG124AS | Yes | PCB-SWTG024AS-A-V2.0.1 | 2M (W25Q16JV)| 8372N |
|
||||
| Horaco | HC-SWTG124AS | Yes | PCB-SWTG024AS-A-V2.0.1_19650 | 2M (W25Q16JV)| 8372N |
|
||||
| GoodTop | GT-ST024M | Yes | PCB-SWTG024AS-A-V2.0.1 | 2M | 8372N |
|
||||
|
||||
### Label specifications
|
||||
@@ -35,6 +35,8 @@ Bottom
|
||||
|
||||
<img src="photos/SWTG024AS-A-V2_0_1_19650/horaco-zx-sg4t2-pcb-bottom.jpg" width="300" />
|
||||
|
||||
On Horaco HC-SWTG124AS with board PCB-SWTG024AS-A-V2.0.1_19650 the reset button is soldered at `SW1` (on the top left, GPIO48).
|
||||
|
||||
### J1, serial console
|
||||
|
||||
| `J1` pin | Signal |
|
||||
|
||||
@@ -0,0 +1,48 @@
|
||||
### SWTG024AS-V2.1
|
||||
|
||||
## Brands
|
||||
|Brand|Type|Managed|PCB|Flash|Chip RTL|
|
||||
|---|---|---|---|---|---|
|
||||
| Xikestor | SKS3200-4E2X | Yes | PCB-SWTG024AS-V2.1.0_19023 | 25Q16A | 8372N |
|
||||
| Solola | SL902-SWTG024AS | Yes | PCB-SWTG024AS-V2.1.0_19023 | | 8372N |
|
||||
|
||||
### Label specifications
|
||||
|
||||
- **Ports**:
|
||||
- 4 × RJ45: 10/100/1000/2500 Mbps
|
||||
- 2 × SFP+: 1000/2500/10000 Mbps
|
||||
- **Power**: 1A @ 12V DC
|
||||
|
||||
#### Xikestore SKS-3200-4E2X label
|
||||
<img src="photos/SWTG024AS-V2.1.0/label.jpg" width="300" />
|
||||
|
||||
### What works
|
||||
- ALL 2.5GBASE-T RJ45 ports work at 10/100/1000/2500 Mbps
|
||||
- The SFP+ ports supports 1G, 2.5G and 10G modules.
|
||||
- LEDs work with the same indiciations as the OEM firmware.
|
||||
|
||||
### PCB overview
|
||||
|
||||
**Board markings**
|
||||
- Top silkscreen: PCB-SWTG024AS-V2.1.0
|
||||
|
||||
Top side
|
||||
|
||||
<img src="photos/SWTG024AS-V2.1.0/pcb-top.jpg" width="300" />
|
||||
|
||||
Bottom
|
||||
|
||||
<img src="photos/SWTG024AS-V2.1.0/pcb-bottom.jpg" width="300" />
|
||||
|
||||
### Serial console
|
||||
|
||||
The four-pin through-hole serial header can be seen to the right of the CPU heatsink in the photo above. It is labelled J1, with pin 1 (Indicated by a triangle on the silkscren) at the bottom. the pinout is also on the silkscreen:
|
||||
|
||||
| Pin | Label |
|
||||
|-----|--------|
|
||||
| 1 | TX |
|
||||
| 2 | RX |
|
||||
| 3 | GND |
|
||||
| 4 | 3V3 |
|
||||
|
||||
Adding a header is a few moments work with a soldering iron. Only pins 1-3 are required for serial port use, and the stock firmware outputs at 57600bps at boot.
|
||||
@@ -0,0 +1,36 @@
|
||||
### HG0402XG V1.1
|
||||
|
||||
## Brands
|
||||
|Brand|Type|Managed|PCB|Flash|Chip RTL|
|
||||
|---|---|---|---|---|---|
|
||||
| ZoeRax | HG0402XG | No| HG0402XG V1.1 | 1M (Winbond 25Q) | |
|
||||
|
||||
### Label specifications
|
||||
|
||||
- **Name**: ZOERAX 2.5G 2SFP 4-Port Gigabit Switch
|
||||
- **Model**: HG0402XG-EU
|
||||
- **Ports**:
|
||||
- 4 × RJ45: 10/100/1000/2500 Mbps
|
||||
- 2 × SFP+: 1000 / 2500 / 10000 Mbps
|
||||
<img src="photos/HG0402XG_V1_1/HG0402XG_V1_1_bottom.jpg" width="300" />
|
||||
|
||||
## PCB
|
||||
|
||||
<img src="photos/HG0402XG_V1_1/HG0402XG_V1_1_top.jpg" width="300" />
|
||||
<img src="photos/HG0402XG_V1_1/HG0402XG_V1_1_bottom.jpg" width="300" />
|
||||
|
||||
Be aware that the UART header is metric, the pitch is 2mm. The JST PH2.0mm header shown was addeded after purchase
|
||||
|
||||
The slider switch controls microcontroller U4 in order to configure the VLAN on/off settings. U4 can reset the SoC
|
||||
and also write into its registers to configure VLAN. With RTLPlayground the slider cannot be used for anything else
|
||||
than triggering reset. Any change in the position will trigger this reset.
|
||||
|
||||
### Installation
|
||||
|
||||
Using SPI clamp on-board is the only method for initial installation.
|
||||
|
||||
### What works
|
||||
|
||||
- All four 2.5GBASE-T RJ45 ports at 10/100/1000/2500 Mbps
|
||||
- SFP ports with various 1G/10G Fiber and Ethernet modules
|
||||
- LEDs
|
||||
|
After Width: | Height: | Size: 3.0 MiB |
|
After Width: | Height: | Size: 3.6 MiB |
|
After Width: | Height: | Size: 2.8 MiB |
|
After Width: | Height: | Size: 88 KiB |
|
After Width: | Height: | Size: 75 KiB |
|
After Width: | Height: | Size: 282 KiB |
|
After Width: | Height: | Size: 253 KiB |
|
After Width: | Height: | Size: 270 KiB |
|
After Width: | Height: | Size: 67 KiB |
|
After Width: | Height: | Size: 398 KiB |
|
After Width: | Height: | Size: 283 KiB |
@@ -0,0 +1,572 @@
|
||||
# LEDs HOWTO using firmware function tracing
|
||||
|
||||
This is a tutorial to show how to figure out the LED configuration for
|
||||
unmanaged switches (on managed ones one can just print out the registers
|
||||
from the command line) without using an I2C port on the device together
|
||||
an I2C-capable microcontroller. You will only need to have a SOIC clamp
|
||||
and will have to run ghidra to do a bit of reversing.
|
||||
|
||||
The additional advantage of this method is that it not only gives the LED
|
||||
configuration but also the full PHY configuration and SDS configuration e.g.
|
||||
in case of additional PHYs being used on the device.
|
||||
|
||||
We will use the ZoeRax HG0402XG V1.1 as an example device.
|
||||
|
||||
The first step is to read out the flash with a SOIC clamp.
|
||||
Save the image as
|
||||
`zoerax_hg0402xg.bin`
|
||||
|
||||
The next step is to use a decompiler such as ghidra to identify the
|
||||
functions used to write or read registers, SDS or to the PHY.
|
||||
|
||||
<img src="images/ghidra_open.png" width="300" />
|
||||
|
||||
First, load the code in BANK0 into ghidra. The type of code is
|
||||
8051 default big-endian. The code starts at offset 0x0002 in the file
|
||||
and has length 0x3ffd
|
||||
|
||||
<img src="images/ghidra_add_block.png" width="300" />
|
||||
Add BANK1 into ghidra by using File->Add to program...
|
||||
and add the code starting at 0x4000 in `zoerax_hg0402xg.bin` with length
|
||||
0xc000. Repeat for BANK2 (starts at 0x10000) and BANK3 (starts at 0x1c0000).
|
||||
They both have length 0xc000.
|
||||
|
||||
<img src="images/ghidra_search_read_reg.png" width="300" />
|
||||
|
||||
Now, find the read_reg function. An example of the read-register function
|
||||
is given below. Note the critical bit, namely the execution of the
|
||||
read-function of an ASIC register via an SFR:
|
||||
```
|
||||
CODE1::4dc9 75 a0 01 MOV SFR_EXEC_GO,#0x1
|
||||
```
|
||||
|
||||
This sequence of bytes is very specific. Replace 0x01 with 0x03 and you have
|
||||
the regster write SFR, 0x05 reads the SerDes, 0x07 writes it. 0x09 reads
|
||||
from the PHY and 0x0b writes to it.
|
||||
|
||||
|
||||
```
|
||||
**************************************************************
|
||||
* FUNCTION *
|
||||
**************************************************************
|
||||
undefined reg_read()
|
||||
undefined <UNASSIGNED> <RETURN>
|
||||
reg_read
|
||||
CODE1::4da9 ab 07 MOV R3,BANK0_R7 = ??
|
||||
CODE1::4dab aa 06 MOV R2,BANK0_R6 = ??
|
||||
CODE1::4dad e4 CLR A
|
||||
CODE1::4dae ff MOV R7,A
|
||||
CODE1::4daf fe MOV R6,A
|
||||
CODE1::4db0 fd MOV R5,A
|
||||
CODE1::4db1 fc MOV R4,A
|
||||
CODE1::4db2 90 00 c9 MOV DPTR,#0xc9
|
||||
CODE1::4db5 12 06 15 LCALL FUN_CODE_0615 undefined FUN_CODE_0615()
|
||||
CODE1::4db8 e4 CLR A
|
||||
CODE1::4db9 ff MOV R7,A
|
||||
CODE1::4dba fe MOV R6,A
|
||||
CODE1::4dbb fd MOV R5,A
|
||||
CODE1::4dbc fc MOV R4,A
|
||||
CODE1::4dbd 90 00 c5 MOV DPTR,#0xc5
|
||||
CODE1::4dc0 12 06 15 LCALL FUN_CODE_0615 undefined FUN_CODE_0615()
|
||||
CODE1::4dc3 ea MOV A,R2
|
||||
CODE1::4dc4 f5 a2 MOV SFR_A_ADDRH,A = ??
|
||||
CODE1::4dc6 eb MOV A,R3
|
||||
CODE1::4dc7 f5 a3 MOV SFR_A_ADDRL,A = ??
|
||||
CODE1::4dc9 75 a0 01 MOV SFR_EXEC_GO,#0x1 <<<<<<<< Executes READ REG
|
||||
LAB_CODE1__4dcc XREF[1]: CODE1::4dcf(j)
|
||||
CODE1::4dcc af a1 MOV R7,SFR_EXEC_STATUS = ??
|
||||
CODE1::4dce ef MOV A,R7
|
||||
CODE1::4dcf 70 fb JNZ LAB_CODE1__4dcc
|
||||
CODE1::4dd1 af a4 MOV R7,DAT_SFR_a4 = ??
|
||||
CODE1::4dd3 fc MOV R4,A
|
||||
CODE1::4dd4 fd MOV R5,A
|
||||
CODE1::4dd5 fe MOV R6,A
|
||||
CODE1::4dd6 90 00 c9 MOV DPTR,#0xc9
|
||||
CODE1::4dd9 12 06 15 LCALL FUN_CODE_0615 undefined FUN_CODE_0615()
|
||||
CODE1::4ddc 90 00 c5 MOV DPTR,#0xc5
|
||||
CODE1::4ddf 12 05 f9 LCALL FUN_CODE_05f9 undefined FUN_CODE_05f9()
|
||||
CODE1::4de2 c0 00 PUSH BANK0_R0
|
||||
CODE1::4de4 90 00 c9 MOV DPTR,#0xc9
|
||||
CODE1::4de7 12 05 dd LCALL read_xmem_32_b undefined read_xmem_32_b()
|
||||
CODE1::4dea 78 18 MOV R0,#0x18
|
||||
CODE1::4dec 12 05 86 LCALL FUN_CODE_0586 undefined FUN_CODE_0586()
|
||||
CODE1::4def d0 00 POP BANK0_R0
|
||||
CODE1::4df1 12 05 48 LCALL FUN_CODE_0548 undefined FUN_CODE_0548()
|
||||
CODE1::4df4 90 00 c5 MOV DPTR,#0xc5
|
||||
CODE1::4df7 12 06 15 LCALL FUN_CODE_0615 undefined FUN_CODE_0615()
|
||||
CODE1::4dfa af a5 MOV R7,DAT_SFR_a5 = ??
|
||||
CODE1::4dfc e4 CLR A
|
||||
CODE1::4dfd fc MOV R4,A
|
||||
CODE1::4dfe fd MOV R5,A
|
||||
CODE1::4dff fe MOV R6,A
|
||||
CODE1::4e00 90 00 c9 MOV DPTR,#0xc9
|
||||
CODE1::4e03 12 06 15 LCALL FUN_CODE_0615 undefined FUN_CODE_0615()
|
||||
CODE1::4e06 90 00 c5 MOV DPTR,#0xc5
|
||||
CODE1::4e09 12 05 f9 LCALL FUN_CODE_05f9 undefined FUN_CODE_05f9()
|
||||
CODE1::4e0c c0 00 PUSH BANK0_R0
|
||||
CODE1::4e0e 90 00 c9 MOV DPTR,#0xc9
|
||||
CODE1::4e11 12 05 dd LCALL read_xmem_32_b undefined read_xmem_32_b()
|
||||
CODE1::4e14 78 10 MOV R0,#0x10
|
||||
CODE1::4e16 12 05 86 LCALL FUN_CODE_0586 undefined FUN_CODE_0586()
|
||||
CODE1::4e19 d0 00 POP BANK0_R0
|
||||
CODE1::4e1b 12 05 48 LCALL FUN_CODE_0548 undefined FUN_CODE_0548()
|
||||
CODE1::4e1e 90 00 c5 MOV DPTR,#0xc5
|
||||
CODE1::4e21 12 06 15 LCALL FUN_CODE_0615 undefined FUN_CODE_0615()
|
||||
CODE1::4e24 af a6 MOV R7,DAT_SFR_a6 = ??
|
||||
CODE1::4e26 e4 CLR A
|
||||
CODE1::4e27 fc MOV R4,A
|
||||
CODE1::4e28 fd MOV R5,A
|
||||
CODE1::4e29 fe MOV R6,A
|
||||
CODE1::4e2a 90 00 c9 MOV DPTR,#0xc9
|
||||
CODE1::4e2d 12 06 15 LCALL FUN_CODE_0615 undefined FUN_CODE_0615()
|
||||
CODE1::4e30 90 00 c5 MOV DPTR,#0xc5
|
||||
CODE1::4e33 12 05 f9 LCALL FUN_CODE_05f9 undefined FUN_CODE_05f9()
|
||||
CODE1::4e36 c0 00 PUSH BANK0_R0
|
||||
CODE1::4e38 90 00 c9 MOV DPTR,#0xc9
|
||||
CODE1::4e3b 12 05 dd LCALL read_xmem_32_b undefined read_xmem_32_b()
|
||||
CODE1::4e3e 78 08 MOV R0,#0x8
|
||||
CODE1::4e40 12 05 86 LCALL FUN_CODE_0586 undefined FUN_CODE_0586()
|
||||
CODE1::4e43 d0 00 POP BANK0_R0
|
||||
CODE1::4e45 12 05 48 LCALL FUN_CODE_0548 undefined FUN_CODE_0548()
|
||||
CODE1::4e48 90 00 c5 MOV DPTR,#0xc5
|
||||
CODE1::4e4b 12 06 15 LCALL FUN_CODE_0615 undefined FUN_CODE_0615()
|
||||
CODE1::4e4e af a7 MOV R7,DAT_SFR_a7 = ??
|
||||
CODE1::4e50 e4 CLR A
|
||||
CODE1::4e51 fc MOV R4,A
|
||||
CODE1::4e52 fd MOV R5,A
|
||||
CODE1::4e53 fe MOV R6,A
|
||||
CODE1::4e54 90 00 c9 MOV DPTR,#0xc9
|
||||
CODE1::4e57 12 06 15 LCALL FUN_CODE_0615 undefined FUN_CODE_0615()
|
||||
CODE1::4e5a 90 00 c5 MOV DPTR,#0xc5
|
||||
CODE1::4e5d 12 05 dd LCALL read_xmem_32_b undefined read_xmem_32_b()
|
||||
CODE1::4e60 90 00 c9 MOV DPTR,#0xc9
|
||||
CODE1::4e63 12 05 f9 LCALL FUN_CODE_05f9 undefined FUN_CODE_05f9()
|
||||
CODE1::4e66 12 05 48 LCALL FUN_CODE_0548 undefined FUN_CODE_0548()
|
||||
CODE1::4e69 90 00 c5 MOV DPTR,#0xc5
|
||||
CODE1::4e6c 12 06 15 LCALL FUN_CODE_0615 undefined FUN_CODE_0615()
|
||||
CODE1::4e6f 90 00 c5 MOV DPTR,#0xc5
|
||||
CODE1::4e72 02 05 dd LJMP read_xmem_32_b undefined read_xmem_32_b()
|
||||
-- Flow Override: CALL_RETURN (CALL_TERMINATOR)
|
||||
|
||||
```
|
||||
|
||||
INSERT ghidra_search_read_reg.png
|
||||
|
||||
Identify all the read and write function to the HW of the SoC using the find
|
||||
in memory function.
|
||||
|
||||
The next task is to adapt the intercept routines in a special assembler
|
||||
file. Don't worry, you do not need to understand assembler to do this.
|
||||
|
||||
```
|
||||
; Assembler file to intercept register writes/reads and PHY writes/reads
|
||||
; assemble with as31
|
||||
; Compile with
|
||||
; as31 -Fbyte -Orwintercept_zoerax_hg0402xg.hex rwintercept_zoerax_hg0402xg.asm
|
||||
; ./injector zoerax_hg0402xg.bin RTLPlayground/rwintercept_zoerax_hg0402xg.hex zoerax_hg0402xg_patched.bin
|
||||
; Start at 0x3b00, where there is always some space in BANK 0
|
||||
.equ valptr, 0x199
|
||||
|
||||
|
||||
.equ SFR_DATA_0, 0xa7
|
||||
.equ SFR_DATA_8, 0xa6
|
||||
.equ SFR_DATA_16, 0xa5
|
||||
.equ SFR_DATA_24, 0xa4
|
||||
|
||||
; Intercept Register Write
|
||||
.org 0x4d62
|
||||
lcall reg_write
|
||||
nop
|
||||
|
||||
; Intercept Register Read
|
||||
.org 0x4dad
|
||||
lcall reg_read_head
|
||||
.org 0x4e6f
|
||||
lcall reg_read_tail
|
||||
|
||||
; Intercept PHY write
|
||||
.org 0x522c
|
||||
lcall phy_write
|
||||
|
||||
; Intercept PHY read
|
||||
.org 0x526f
|
||||
lcall phy_read_head
|
||||
.org 0x5295
|
||||
ljmp phy_read_tail
|
||||
|
||||
; Intercept SDS write
|
||||
.org 0x503c
|
||||
lcall sds_write
|
||||
nop
|
||||
|
||||
; Intercept SDS read
|
||||
.org 0x5061
|
||||
lcall sds_read_head
|
||||
nop
|
||||
.org 0x507b
|
||||
ljmp sds_read_tail
|
||||
|
||||
;
|
||||
; BEGIN actual code at unused end of BANK0
|
||||
;
|
||||
.org 0x3b00
|
||||
|
||||
;
|
||||
; print the address of a register write. The address is in R6/R7. A and r4 can be scratched
|
||||
;
|
||||
reg_write:
|
||||
mov a, #'R'
|
||||
acall putc
|
||||
|
||||
mov a, r6
|
||||
acall wbyte
|
||||
|
||||
mov a, r7
|
||||
acall wbyte
|
||||
|
||||
mov a, #'-'
|
||||
acall putc
|
||||
|
||||
mov dptr, #valptr
|
||||
movx a, @dptr
|
||||
acall wbyte
|
||||
mov dptr, #valptr + 1
|
||||
movx a, @dptr
|
||||
acall wbyte
|
||||
mov dptr, #valptr + 2
|
||||
movx a, @dptr
|
||||
acall wbyte
|
||||
mov dptr, #valptr + 3
|
||||
movx a, @dptr
|
||||
acall wbyte
|
||||
|
||||
mov a, #' '
|
||||
acall putc
|
||||
|
||||
mov a, r7
|
||||
mov r5, a
|
||||
mov a, r6
|
||||
mov r4, a
|
||||
ret
|
||||
[...]
|
||||
```
|
||||
|
||||
The full assembler file can be found here: XXXXXXXXXX
|
||||
|
||||
|
||||
Once you have assembled the code, inject the bytes into the existing image,
|
||||
creating a patched version:
|
||||
```
|
||||
$ as31 -Fbyte -Orwintercept_zoerax_hg0402xg.hex rwintercept_zoerax_hg0402xg.asm
|
||||
Begin Pass #1
|
||||
Begin Pass #2
|
||||
./injector zoerax_hg0402xg.bin rwintercept_zoerax_hg0402xg.hex zoerax_hg0402xg_patched.bin
|
||||
Input file size: 1048576
|
||||
Bytes read:: 1048576
|
||||
Patched 417 bytes.
|
||||
```
|
||||
|
||||
Now flash `zoerax_hg0402xg_patched.bin` onto device.
|
||||
|
||||
Connect a serial adapter and plug in power. You will see:
|
||||
|
||||
```
|
||||
$ picocom -b 9600 /dev/ttyUSB0
|
||||
picocom v3.1
|
||||
|
||||
port is : /dev/ttyUSB0
|
||||
flowcontrol : none
|
||||
baudrate is : 9600
|
||||
parity is : none
|
||||
databits are : 8
|
||||
stopbits are : 1
|
||||
escape is : C-a
|
||||
local echo is : no
|
||||
noinit is : no
|
||||
noreset is : no
|
||||
hangup is : no
|
||||
nolock is : no
|
||||
send_cmd is : sz -vv
|
||||
receive_cmd is : rz -vv -E
|
||||
imap is :
|
||||
omap is :
|
||||
emap is : crcrlf,delbs,
|
||||
logfile is : none
|
||||
initstring : none
|
||||
exit_after is : not set
|
||||
exit is : no
|
||||
|
||||
Type [C-a] [C-h] to see available commands
|
||||
Terminal ready
|
||||
[The printout only starts once the serial peripherial has been set up. Initially its timing is off:]
|
||||
�.Ӛ�҂���������30-00005555 r6330:00005555 R6330-00005515 [RTL837X_REG_SMI_MAC_TYPE]
|
||||
[RTL837X_REG_SMI_PORT_POLLING: ]
|
||||
r6334:00000000 R6334-000000f0
|
||||
r6454:00000000 R6454-00007000
|
||||
|
||||
[Read SoC-Revision]
|
||||
r000c:00200000 R000c-002a0000 r000c:202a6818 r000c:202a6818 R000c-20206818
|
||||
p001e.000d:ffff R02f8-0000ffff R02f4-0000fffa P000001.1e00000d:fffa p001e.000d:ffff p001e.000d:ffff
|
||||
R02f8-0000ffff R02f4-0000fff0 P000001.1e00000d:fff0
|
||||
|
||||
[RTL837X_REG_LED_MODE being configured:]
|
||||
r6520:0021fdb0 R6520-0021e7b0 r6520:0021e7b0 R6520-0021e6b0
|
||||
|
||||
[RLDP Leds configured via RTL837X_REG_LED_RLDP_X:]
|
||||
r65f8:00000018 R65f8-0000001b R65fc-fffff000 r6600:00000000 R6600-0000000f
|
||||
|
||||
[RTL837X_REG_LED_GLB_IO_EN: ]
|
||||
r65dc:5fffff00 R65dc-7fffff00 r65dc:7fffff00 R65dc-77ffff00
|
||||
|
||||
[RTL837X_PIN_MUX_0, makes serial go out of sync:]
|
||||
r7f8c:30000000 R7f8c-30000000 r7f8c:300��.Ӛ�҂���������30-00005555
|
||||
|
||||
r6330:00005555 R6330-00005515 r6334:00000000 R6334-000000f0
|
||||
|
||||
r6454:00000000 R6454-00007000
|
||||
|
||||
[Read SoC-Revision (again)]
|
||||
r000c:00200000 R000c-002a0000 r000c:202a6818 r000c:202a6818 R000c-20206818
|
||||
p001e.000d:ffff R02f8-0000ffff R02f4-0000fffa P000001.1e00000d:fffa p001e.000d:ffff p001e.000d:ffff
|
||||
R02f8-0000ffff R02f4-0000fff0
|
||||
P000001.1e00000d:fff0
|
||||
|
||||
#### LED configuration ########
|
||||
[RTL837X_REG_LED_MODE being configured (final):]
|
||||
r6520:0021fdb0 R6520-0021e7b0 r6520:0021e7b0 R6520-0021e6b0
|
||||
|
||||
[More RTL837X_REG_LED_RLDP]
|
||||
r65f8:00000018 R65f8-0000001b R65fc-fffff000 r6600:00000000 R6600-0000000f
|
||||
|
||||
[RTL837X_REG_LED_GLB_IO_EN]
|
||||
r65dc:5fffff00 R65dc-7fffff00 r65dc:7fffff00 R65dc-77ffff00
|
||||
|
||||
[RTL837X_PIN_MUX_0
|
||||
r7f8c:30000000 R7f8c-30000000 r7f8c:30000000 R7f8c-38000000
|
||||
|
||||
[RTL837X_LED_PORT_SET_SEL]
|
||||
r654c:00000000 R654c-00010000
|
||||
|
||||
[RTL837X_REG_LED1_0_SET0]
|
||||
R6548-00000141
|
||||
|
||||
[RTL837X_REG_LED3_2_SET0]
|
||||
r6544:01411000 R6544-01410174
|
||||
|
||||
[RTL837X_REG_LED1_0_SET1]
|
||||
r6540:00440070 R6540-0044017f
|
||||
|
||||
[RTL837X_REG_LED3_0_SET1]
|
||||
r6528:00000000 R6528-000f0000
|
||||
|
||||
[SDS 0 being configure:]
|
||||
r7b20:00000bff R7b20-000003ff r7b20:000003ff R7b20-000003fa r7b20:000003fa R7b20-000003fa
|
||||
Q002110:4480 Q002113:0400 Q002118:6d02 Q00211b:424e Q00211d:0002 Q00361c:1390
|
||||
Q003614:003f Q003610:0200 Q002e04:0080 Q002e06:0408 Q002e07:020d Q002e09:0601
|
||||
Q002e0b:222c Q002e0c:a217 Q002e0d:fe40 Q002e15:f5c1 Q002e16:0443 Q002e1d:abb0
|
||||
Q000612:5078 Q000706:9401 Q000708:9401 Q00070a:9401 Q00070c:9401 Q001f0b:0003
|
||||
Q000603:c45c Q00061f:2100 q001f0b:0003 Q001f0b:000f q002000:0000 Q002000:0030
|
||||
q002000:0030 Q002000:0010 q002000:0010 Q002000:0050 q002000:0050 Q002000:00d0
|
||||
q002000:00d0 Q002000:0cd0 q002000:0cd0 Q002000:04d0 q002000:04d0 Q002000:04d0
|
||||
q002000:04d0 Q002000:0cd0 q002000:0cd0 Q002000:00d0 q002000:00d0 Q002000:00d0
|
||||
q002000:00d0 Q002000:0050 q002000:0050 Q002000:0010 q002000:0010 Q002000:0010
|
||||
q002000:0010 Q002000:0030 q002000:0030 Q002000:0000 q001f00:0000 Q001f00:000b
|
||||
q001f00:000b Q001f00:0000
|
||||
|
||||
[SDS 1 being configured]
|
||||
r7b20:000003fa R7b20-000003fa r7b20:000003fa R7b20-0000035a r7b20:0000035a R7b20-0000035a
|
||||
Q012110:4480 Q012113:0400 Q012118:6d02 Q01211b:424e Q01211d:0002 Q01361c:1390
|
||||
Q013614:003f Q013610:0200 Q012e04:0080 Q012e06:0408 Q012e07:020d Q012e09:0601
|
||||
Q012e0b:222c Q012e0c:a217 Q012e0d:fe40 Q012e15:f5c1 Q012e16:0443 Q012e1d:abb0
|
||||
Q010612:5078 Q010706:9401 Q010708:9401 Q01070a:9401 Q01070c:9401 Q011f0b:0003
|
||||
Q010603:c45c Q01061f:2100 q011f0b:0003 Q011f0b:000f q012000:0000 Q012000:0030
|
||||
q012000:0030 Q012000:0010 q012000:0010 Q012000:0050 q012000:0050 Q012000:00d0
|
||||
q012000:00d0 Q012000:0cd0 q012000:0cd0 Q012000:04d0 q012000:04d0 Q012000:04d0
|
||||
q012000:04d0 Q012000:0cd0 q012000:0cd0 Q012000:00d0 q012000:00d0 Q012000:00d0
|
||||
q012000:00d0 Q012000:0050 q012000:0050 Q012000:0010 q012000:0010 Q012000:0010
|
||||
q012000:0010 Q012000:0030 q012000:0030 Q012000:0000 q011f00:0000 Q011f00:000b
|
||||
q011f00:000b Q011f00:0000 q012100:4902 Q012100:4906 q013605:0000 Q013605:4000
|
||||
Q011f02:001f q011f15:0096 q002100:4902 Q002100:4906 q003605:0000 Q003605:4000
|
||||
Q001f02:001f q001f15:00a6
|
||||
|
||||
r0a90:000000f3 R0a90-000000fc P0000f0.1f00a610:2858 r5fd4:0002914a
|
||||
R5fd4-001a914a r1538:00000e33 R1538-00000e37 r1538:00000e37 R1538-00000e37
|
||||
r1538:00000e37 R1538-00000f37 r1638:00000e33 R1638-00000e37 r1638:00000e37
|
||||
R1638-00000e37 r1638:00000e37 R1638-00000f37 r1738:00000e33 R1738-00000e37
|
||||
r1738:00000e37 R1738-00000e37 r1738:00000e37 R1738-00000f37 r1838:00000e33
|
||||
R1838-00000e37 r1838:00000e37 R1838-00000e37 r1838:00000e37 R1838-00000f37
|
||||
r1938:00000e33 R1938-00000e37 r1938:00000e37 R1938-00000e37 r1938:00000e37
|
||||
R1938-00000f37 r1a38:00000e33 R1a38-00000e37 r1a38:00000e37 R1a38-00000e37
|
||||
r1a38:00000e37 R1a38-00000f37 r0b7c:000000d8 R0b7c-000000f8 R7124-00001050
|
||||
R7128-00001050 R712c-00001050 R7130-00001050 R7134-00001050 R7138-00001050
|
||||
R713c-00001050 R7140-00001050 R7144-00001050 R7148-00001050 r6040:00000030
|
||||
R6040-00000031 p041f.a436:8195 P000010.1f00a436:0005 p041f.a438:02d4
|
||||
P000010.1f00a436:801e p041f.a438:0000 p041f.b820:0000 P000010.1f00b820:0010
|
||||
p041f.b800:00c0 p041f.a436:801e P000010.1f00a436:8023 p041f.a438:0000
|
||||
P000010.1f00a438:1802 p041f.a436:8025
|
||||
|
||||
[PHY with ID 5 being patched] P000010.1f00a436:b82e p041f.a438:0000
|
||||
P000010.1f00a438:0001 p041f.b820:0010 P000010.1f00b820:0090
|
||||
P000010.1f00a436:a016 P000010.1f00a438:0000 P000010.1f00a436:a012
|
||||
P000010.1f00a438:0000 P000010.1f00a436:a014 P000010.1f00a438:1800
|
||||
P000010.1f00a438:8010 P000010.1f00a438:1800 P000010.1f00a438:8013
|
||||
P000010.1f00a438:1800 P000010.1f00a438:8026 P000010.1f00a438:1800
|
||||
P000010.1f00a438:802a P000010.1f00a438:1800 P000010.1f00a438:802e
|
||||
P000010.1f00a438:1800 P000010.1f00a438:802e P000010.1f00a438:1800
|
||||
P000010.1f00a438:802e P000010.1f00a438:1800 P000010.1f00a438:802e
|
||||
...
|
||||
```
|
||||
Where:
|
||||
```
|
||||
r: read-reg
|
||||
R: write-reg
|
||||
q: read SDS
|
||||
Q: write SDS
|
||||
p: read PHY
|
||||
P: write SDS
|
||||
```
|
||||
|
||||
The LED configuration has been marked above. We now write exactly those registers
|
||||
into the registers of `rtlplayground.c` on startup:
|
||||
|
||||
```
|
||||
[...]
|
||||
set_sys_led_state(SYS_LED_ON);
|
||||
|
||||
// [RTL837X_REG_LED_MODE being configured (final):]
|
||||
// r6520:0021fdb0 R6520-0021e7b0 r6520:0021e7b0 R6520-0021e6b0
|
||||
REG_SET(RTL837X_REG_LED_MODE, 0x0021e6b0);
|
||||
|
||||
// [More RTL837X_REG_LED_RLDP]
|
||||
// r65f8:00000018 R65f8-0000001b R65fc-fffff000 r6600:00000000 R6600-0000000f
|
||||
REG_SET(0x65f8, 0x0000001b);
|
||||
REG_SET(0x65fc, 0xfffff000);
|
||||
REG_SET(0x6600, 0x0000000f);
|
||||
|
||||
// [RTL837X_REG_LED_GLB_IO_EN]
|
||||
// r65dc:5fffff00 R65dc-7fffff00 r65dc:7fffff00 R65dc-77ffff00
|
||||
REG_SET(0x65dc, 0x77ffff00);
|
||||
|
||||
// [RTL837X_PIN_MUX_0
|
||||
// r7f8c:30000000 R7f8c-30000000 r7f8c:30000000 R7f8c-38000000
|
||||
REG_SET(0x7f8c, 0x38000000);
|
||||
|
||||
// [RTL837X_LED_PORT_SET_SEL]
|
||||
// r654c:00000000 R654c-00010000
|
||||
REG_SET(0x654c, 0x00010040);
|
||||
|
||||
// [RTL837X_REG_LED1_0_SET0]
|
||||
// R6548-00000141
|
||||
REG_SET(0x6548, 0x00000141);
|
||||
|
||||
// [RTL837X_REG_LED3_2_SET0]
|
||||
// r6544:01411000 R6544-01410174
|
||||
REG_SET(0x6544, 0x01410174);
|
||||
|
||||
// [RTL837X_REG_LED1_0_SET1]
|
||||
// r6540:00440070 R6540-0044017f
|
||||
REG_SET(0x6540, 0x0044017f);
|
||||
|
||||
// [RTL837X_REG_LED3_0_SET1]
|
||||
// r6528:00000000 R6528-000f0000
|
||||
REG_SET(0x6528, 0x000f0000);
|
||||
|
||||
cmd_editor_init();
|
||||
[...]
|
||||
```
|
||||
|
||||
Create an initial configuraiton for your device by copying over an existing one
|
||||
for the same device type, i.e. same port-configuraiton and same SoC,
|
||||
give it a new name. Make sure that the led_mux_custom and led_mux fields are not
|
||||
used, because our device does not customize the LED muxes.
|
||||
|
||||
Compile and flash onto your device.
|
||||
|
||||
Now, boot the switch and issue the `leds`command, which will print out:
|
||||
```
|
||||
RTL837X_PIN_MUX_0: 0x38000000
|
||||
RTL837X_REG_LED_GLB_IO_EN: 0x77ffff00
|
||||
RTL837X_REG_LED1_0_SET0: 0x00000141
|
||||
RTL837X_REG_LED3_2_SET0: 0x01410174
|
||||
RTL837X_REG_LED1_0_SET1: 0x0044017f
|
||||
RTL837X_REG_LED3_2_SET1: 0x18000040
|
||||
RTL837X_REG_LED1_0_SET2: 0x00000000
|
||||
RTL837X_REG_LED3_2_SET2: 0x00000000
|
||||
RTL837X_REG_LED1_0_SET3: 0x00000000
|
||||
RTL837X_REG_LED3_2_SET3: 0x00000000
|
||||
RTL837X_REG_LED3_0_SET1: 0x000f0000
|
||||
RTL837X_REG_LED3_0_SET3: 0x00000000
|
||||
RTL837X_LED_PORT_SET_SEL: 0x00010040
|
||||
RTL837X_REG_LED_GLB_MUX_1: 0x3fffffff
|
||||
RTL837X_REG_LED_GLB_MUX_2: 0x0c3fffff
|
||||
RTL837X_REG_LED_GLB_MUX_3: 0x1245038d
|
||||
RTL837X_REG_LED_GLB_MUX_4: 0x19616554
|
||||
RTL837X_REG_LED_GLB_MUX_5: 0x2079d71a
|
||||
RTL837X_REG_LED_GLB_MUX_6: 0x000238a1
|
||||
RTL837X_REG_LED_GLB_ACTIVE: 0x3ffbedff
|
||||
RTL837X_REG_LED_MODE: 0x0021e6b0
|
||||
LED pad Configuration:
|
||||
00 01 02 03 04 05 06 07 08 09 0a 0b 0c 0d 0e 0f 10 11 12 13 14 15 16 17 18 19 1a 1b
|
||||
3f 3f 3f 3f 3f 3f 3f 3f 0f 0c 0d 0e 10 11 12 14 15 16 18 19 1a 1c 1d 1e 20 21 22 23
|
||||
LED-set Configuration:
|
||||
LED-ID 0 1 2 3
|
||||
SET 0: 000141 000000 000174 000141
|
||||
SET 1: 0f017f 000044 000040 001800
|
||||
SET 2: 000000 000000 000000 000000
|
||||
SET 3: 000000 000000 000000 000000
|
||||
Port 3: SET 1: ( 2G5 TWO_1G 1G 500M 100M 10M LINK ACT 10G TWO_5G 5G TWO_2G5), ( 1G LINK), ( LINK), ( COL DUPLEX),
|
||||
Port 4: SET 0: ( 2G5 LINK ACT), (), ( 1G 100M 10M LINK ACT), ( 2G5 LINK ACT),
|
||||
Port 5: SET 0: ( 2G5 LINK ACT), (), ( 1G 100M 10M LINK ACT), ( 2G5 LINK ACT),
|
||||
Port 6: SET 0: ( 2G5 LINK ACT), (), ( 1G 100M 10M LINK ACT), ( 2G5 LINK ACT),
|
||||
Port 7: SET 0: ( 2G5 LINK ACT), (), ( 1G 100M 10M LINK ACT), ( 2G5 LINK ACT),
|
||||
Port 8: SET 1: ( 2G5 TWO_1G 1G 500M 100M 10M LINK ACT 10G TWO_5G 5G TWO_2G5), ( 1G LINK), ( LINK), ( COL DUPLEX),
|
||||
|
||||
```
|
||||
Together with the GPIO information, this will give you the following machine configuration:
|
||||
```
|
||||
_code const struct machine machine = {
|
||||
.machine_name = "ZOERAX HG0402XG",
|
||||
.isRTL8373 = 0,
|
||||
.min_port = 3,
|
||||
.max_port = 8,
|
||||
.n_sfp = 2,
|
||||
.log_to_phys_port = {0, 0, 0, 5, 1, 2, 3, 4, 6},
|
||||
.phys_to_log_port = {4, 5, 6, 7, 3, 8, 0, 0, 0},
|
||||
.is_sfp = {0, 0, 0, 1, 0, 0, 0, 0, 2},
|
||||
// Left SFP port (5)
|
||||
.sfp_port[0].pin_detect = GPIO30_ACL_BIT3_EN,
|
||||
.sfp_port[0].pin_los = GPIO_NA,
|
||||
.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 ),
|
||||
// Right SFP port (6)
|
||||
.sfp_port[1].pin_detect = GPIO50_I2C_SCL2_UART1_TX,
|
||||
.sfp_port[1].pin_los = GPIO_NA,
|
||||
.sfp_port[1].pin_tx_disable = GPIO_NA,
|
||||
.sfp_port[1].sds = 0,
|
||||
.sfp_port[1].i2c = I2CBUS( GPIO41_I2C_SDA3_MDIO1, 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, 1, 0, 0, 0, 0, 1},
|
||||
/* On Ethernet: Left led is orange, right green
|
||||
* 100M and 1G give right led green with activity
|
||||
* 2.5GBit adds solid left orange LED
|
||||
* LEDs for the SFP-ports are the same as for the Ethernet ports
|
||||
*/
|
||||
.led_sets = { { LEDS_2G5 | LEDS_LINK | LEDS_ACT,
|
||||
0,
|
||||
LEDS_1G | LEDS_100M | LEDS_10M | LEDS_LINK | LEDS_ACT,
|
||||
LEDS_2G5 | LEDS_LINK | LEDS_ACT
|
||||
},
|
||||
{ LEDS_2G5 | LEDS_TWO_PAIR_1G | LEDS_1G | LEDS_500M | LEDS_100M | LEDS_10M |
|
||||
LEDS_LINK | LEDS_ACT | LEDS_10G | LEDS_TWO_PAIR_5G | LEDS_5G | LEDS_TWO_PAIR_2G5,
|
||||
LEDS_1G | LEDS_LINK,
|
||||
LEDS_LINK,
|
||||
LEDS_COL | LEDS_DUPLEX
|
||||
}
|
||||
}
|
||||
|
||||
};
|
||||
```
|
||||
|
||||
|
||||
|
After Width: | Height: | Size: 75 KiB |
|
After Width: | Height: | Size: 72 KiB |
|
After Width: | Height: | Size: 100 KiB |
@@ -2,9 +2,11 @@
|
||||
|
||||
The switch can take part in a spanning tree (IEEE 802.1D / 802.1w) so that
|
||||
redundant links between bridges are blocked instead of forming a loop. The
|
||||
implementation elects a root bridge from the BPDUs it receives, promotes ports
|
||||
to forwarding once their listen period expires, ages the root out when it goes
|
||||
silent, and blocks a port on which it sees its own BPDU.
|
||||
implementation elects a root bridge from the BPDUs it receives, picks the
|
||||
cheapest path to it as the root port, blocks ports on which a better bridge is
|
||||
already designated, promotes ports to forwarding once their listen period
|
||||
expires, drops information a neighbour stopped sending, and blocks a port on
|
||||
which it sees its own BPDU.
|
||||
|
||||
STP can be enabled and controlled via the web interface or the command line,
|
||||
as follows:
|
||||
@@ -64,32 +66,6 @@ LACPDUs, delivered by forward, stopped arriving on port 3, and BPDUs sent into
|
||||
the same port, delivered by trap, kept arriving. A port that admits tagged
|
||||
frames only therefore takes part in STP.
|
||||
|
||||
## Link aggregation
|
||||
|
||||
A LAG (Link Aggregation Group) is handled like an additional port by the STP
|
||||
protocol, with its own timers and states. The switch devices support up to 4
|
||||
LAGs, which are shown alongside the physical ports in the STP status. A group
|
||||
carries its own path cost, priority, edge and guard settings, and gets its own
|
||||
row on the Spanning Tree page.
|
||||
|
||||
```
|
||||
stp lag 1 cost 10000 # the group decides, not its members
|
||||
stp lag 1 edge off
|
||||
```
|
||||
|
||||
LAGs are configured via the `lag` command. Once a port is a member of a LAG it
|
||||
can no longer be configured individually for STP, so `stp port <n>` on a member
|
||||
tells you which group to configure instead. Membership is re-read from the
|
||||
aggregation registers once a second, so a group changing under LACP is picked
|
||||
up without any coordination between the two.
|
||||
|
||||
The switch hardware does not handle STP state for a LAG as a whole. State
|
||||
changes for the members have to be done by the firmware, updating every member
|
||||
port, which is possible using a single register write. BPDUs go out through the
|
||||
lowest member and carry the group's own port id. Losing one member of a live
|
||||
LAG is not a topology change; the logical port only goes down with its last
|
||||
link.
|
||||
|
||||
Port states live in `RTL837X_MSTP_STATES (0x5310)`, two bits per port:
|
||||
`00` disabled, `01` blocking, `10` learning, `11` forwarding. A port in
|
||||
blocking forwards nothing between ports, but it still sends what the CPU
|
||||
@@ -109,19 +85,83 @@ encodes. All configured values are in seconds:
|
||||
| `stp fwd <n>` | 15 | 4–30 |
|
||||
| `stp txhold <n>` | 6 | 1–10 |
|
||||
|
||||
A port entering the tree spends `fwd` seconds in blocking before it forwards
|
||||
(an edge port skips the wait). Root information is discarded after `maxage`
|
||||
seconds without a BPDU, and the switch then reclaims the root role.
|
||||
802.1D also requires `2 * (fwd - 1) >= maxage >= 2 * (hello + 1)`. A
|
||||
command typed on the console or sent by the web page that would break it is
|
||||
refused and the value stays as it was, so change the values in an order that
|
||||
keeps the rule at every step (for a larger max age raise the forward delay
|
||||
first). The web page shows the error, and a refused command is not recorded
|
||||
in the command history, so saving the configuration does not pick it up. Lines of the saved configuration are taken one by one whatever their
|
||||
order, and `stp on` checks the result: if the rule is broken it warns and
|
||||
starts with 2/20/15 instead.
|
||||
|
||||
The values above are what the switch uses while it is the root. Otherwise it
|
||||
takes max age and forward delay from the BPDUs on its root port, as 802.1D
|
||||
requires, and passes them on in its own BPDUs with the message age one second
|
||||
higher; the hello time stays its own.
|
||||
|
||||
A port entering the tree discards for one forward delay, then learns
|
||||
addresses without forwarding for a second one, and only then forwards (an
|
||||
edge port skips the wait). In RSTP mode on a point-to-point link
|
||||
(`p2p auto` or `on`) it can go faster: a designated port that is still
|
||||
discarding sets the proposal flag, and forwards as soon as the bridge on the
|
||||
other side answers with an agreement. When the root port receives a proposal,
|
||||
the switch first puts its other non-edge designated ports back to discarding
|
||||
(they propose in turn), then forwards the root port and answers with an
|
||||
agreement; this sync happens once per root port, later proposals are only
|
||||
answered. An alternate or backup port that receives a proposal does the same
|
||||
sync once for the information it holds and answers with an agreement in a
|
||||
BPDU with the alternate role, so the bridge on the other side does not have
|
||||
to wait out its timers; it stays discarding itself. An agreement is accepted
|
||||
from a root or an alternate port on the other side. A new root port forwards at once, after every port that was root within
|
||||
the last forward delay has been put back to discarding; such a recent root
|
||||
port stays closed, even to an agreement, until that forward delay is over.
|
||||
A new root port that was a backup port within the last two hello times does
|
||||
not take the fast path and waits for its timers.
|
||||
|
||||
A designated port that hears worse information from a bridge that still
|
||||
claims to be designated and learning or forwarding on the same link has a
|
||||
dispute: usually one direction of the link is lost and the other bridge does
|
||||
not hear this switch's BPDUs. The port goes back to discarding, and every
|
||||
further such BPDU restarts its listen period, so it does not forward while
|
||||
the dispute lasts.
|
||||
|
||||
With `stp version rstp` each port still talks 802.1D to a neighbour that
|
||||
does: once a Config BPDU or TCN arrives and the migrate time (3 s since the
|
||||
port came up or last switched) is over, the port sends Config BPDUs, uses
|
||||
the 802.1D TC window and reports changes with TCN if it is the root port. An
|
||||
RST BPDU after the migrate time switches it back, and so does a link that
|
||||
comes up again or `stp port <n> mcheck`. Any worse information heard on a designated port, disputed or
|
||||
not, is answered with a BPDU at once instead of at the next hello, so the
|
||||
other bridge learns sooner that it is not the designated one. Information heard on a port stays
|
||||
valid for three of the sender's hello times; a BPDU whose message age has
|
||||
reached its max age is not used at all. When the root port's information
|
||||
expires the switch moves to the next best port, or reclaims the root role if
|
||||
there is none.
|
||||
|
||||
Among ports offering the same root, the lowest root path cost wins, then the
|
||||
designated bridge, the designated port ID, and finally this switch's own port
|
||||
ID. A worse BPDU from a different bridge does not replace what a port holds;
|
||||
a worse one from the same designated bridge and port does.
|
||||
The number in a port ID is the one on the front panel, and 101 and up for
|
||||
the LAGs, so it matches the port labels on both ends of a link.
|
||||
|
||||
## Topology changes
|
||||
|
||||
A change on a local non-edge port (the link coming or going, a port promoted
|
||||
to forwarding) flushes the addresses learned on it and sets the TC flag in
|
||||
our BPDUs for `maxage + fwd` seconds. A TC flag received in a BPDU is passed
|
||||
on: the switch flushes the other non-edge ports once and keeps the flag in
|
||||
its own BPDUs until one hello after the last flagged frame, so the
|
||||
notification crosses the switch instead of dying at it. A legacy TCN is
|
||||
acknowledged with TCA and then treated like a local change.
|
||||
A topology change is detected when a non-edge port enters forwarding. That
|
||||
port, and every other forwarding non-edge port, then carries the TC flag in
|
||||
its BPDUs for hello time plus one second (max age plus forward delay with
|
||||
`stp version stp`), and each of the other ports flushes the addresses it
|
||||
learned. The root port takes part too: while its TC window runs it sends
|
||||
BPDUs toward the root, so the change reaches the rest of the network. With
|
||||
`stp version stp` the root port sends TCN instead, until a Config BPDU with
|
||||
TCA comes back.
|
||||
|
||||
A TC flag received on a forwarding root or designated port is passed on the
|
||||
same way to the other ports, not back to the port it came in on; a port that
|
||||
is still discarding or learning drops it. A legacy TCN is acknowledged with
|
||||
TCA and then treated like a received TC. A port that turns
|
||||
alternate, loses its link, or is blocked for a loop flushes its own addresses
|
||||
without raising a topology change, and an edge port never raises one.
|
||||
|
||||
## Bridge settings
|
||||
|
||||
@@ -129,6 +169,8 @@ acknowledged with TCA and then treated like a local change.
|
||||
stp prio <0-15> # bridge priority = n * 4096, default 8 (32768)
|
||||
stp version rstp|stp # RST BPDUs (default) or legacy Config BPDUs
|
||||
stp hello|maxage|fwd|txhold <seconds>
|
||||
stp pathcost long|short # automatic port costs, 802.1D-2004 (default) or 802.1D-1998
|
||||
stp bpdu flood|filter # BPDUs while STP is off: pass them on (default) or drop them
|
||||
```
|
||||
|
||||
The bridge with the lowest priority wins the root election; ties are broken by
|
||||
@@ -136,6 +178,17 @@ the MAC address. If you do not want this switch to become the root of an
|
||||
existing network, give it a worse priority than the current root — `stp prio 15`
|
||||
(61440) is the usual "never me" value.
|
||||
|
||||
A port without a configured cost takes it from its link speed. The long
|
||||
method uses the 802.1D-2004 values (20000 for 1G, 2000 for 10G); the short
|
||||
one the 802.1D-1998 table for bridges that still use 16-bit costs (100 for
|
||||
10M, 19 for 100M, 4 for 1G, 2 for 10G, and 3 for 2.5G and 5G, which that
|
||||
table does not list). All bridges in a network should use the same method.
|
||||
|
||||
`stp bpdu filter` keeps BPDUs from being passed between ports while STP is
|
||||
off, instead of the default flooding that lets a surrounding spanning tree
|
||||
run through the switch. It takes effect at once when STP is off, otherwise
|
||||
when STP is turned off.
|
||||
|
||||
## Per-port settings
|
||||
|
||||
```
|
||||
@@ -146,6 +199,7 @@ stp port <1-9> prio <0-240> # port priority, steps of 16
|
||||
stp port <1-9> guard none|bpdu|root
|
||||
stp port <1-9> filter on|off # neither send nor accept BPDUs
|
||||
stp port <1-9> p2p auto|on|off
|
||||
stp port <1-9> mcheck # try RSTP again on a port that fell back to STP
|
||||
```
|
||||
|
||||
**edge** — an edge port forwards immediately and does not trigger a
|
||||
@@ -162,26 +216,76 @@ claims a better priority.
|
||||
on the far side reacts badly to them (some unmanaged switches with loop
|
||||
prevention cut the link) but you still want STP on the rest of the ports.
|
||||
|
||||
## Link aggregation
|
||||
|
||||
A LAG (Link Aggregation Group) is one port of the tree, with its own roles,
|
||||
timers and states. The switch supports up to 4 LAGs; each one takes the place
|
||||
of its member ports in the STP status and gets its own row on the Spanning
|
||||
Tree page. A group carries its own path cost, priority, edge, guard and
|
||||
point-to-point settings, and takes the same commands as a port:
|
||||
|
||||
```
|
||||
stp lag <1-4> on|off|mcheck|edge|cost|prio|guard|filter|p2p ...
|
||||
stp lag 1 cost 10000 # the group decides, not its members
|
||||
```
|
||||
|
||||
LAGs are configured with the `lag` command. Once a port is a member of a LAG
|
||||
it can no longer be configured on its own for STP, and `stp port <n>` on a
|
||||
member names the group to configure instead. Membership is read from the
|
||||
aggregation registers once a second, so a group changing under LACP is picked
|
||||
up without any coordination between the two; a port that joins or leaves a
|
||||
group, and the group itself, start over from discarding. The automatic path
|
||||
cost of a group is the one of its lowest member that has a link.
|
||||
|
||||
The hardware keeps no STP state for a LAG as a whole, so every state change is
|
||||
written to all member ports, in a single register write. BPDUs go out through
|
||||
the lowest member that has a link and carry the group's own port id, and a
|
||||
BPDU received on any member belongs to the group. Losing one member of a live
|
||||
LAG is not a topology change; the group only goes down with its last link.
|
||||
|
||||
## Status
|
||||
|
||||
The Spanning Tree page shows the elected root (priority and MAC), the path cost
|
||||
to it, the root port, the topology-change counter and, per port, the live state
|
||||
read from the ASIC together with the configured options. The same data is
|
||||
available as JSON:
|
||||
to it, the root port, the topology-change counter and the time since it last
|
||||
moved, the max age and forward
|
||||
delay in use (the root's, when this switch is not the root) and, per port, the
|
||||
live state read from the ASIC, or "No link", together with the configured
|
||||
options. For a port taking part in the tree it also shows the designated
|
||||
bridge, port and cost, and whether it talks RSTP or 802.1D to its neighbour;
|
||||
the Check button runs `stp port <n> mcheck`. The same data is available as
|
||||
JSON:
|
||||
|
||||
```
|
||||
GET /stp.json
|
||||
```
|
||||
|
||||
The `stp status` command prints the same view on the serial console.
|
||||
The Counters section of the same page (or `/stpcnt.json`) shows per port the
|
||||
BPDUs received and sent, those of them that carried the topology change flag
|
||||
(TCNs included), and the time since the last BPDU arrived. The counters start
|
||||
from zero with `stp on`, and `stp clear` resets them; the topology change
|
||||
counter is not reset.
|
||||
|
||||
The `stp status` command prints the same view on the serial console. A port
|
||||
is reported with one of the roles Root, Designated, Alternate, Backup or
|
||||
Disabled; Disabled covers a port with STP turned off on it, one shut down by
|
||||
BPDU guard and one without link.
|
||||
|
||||
The console's `age[s]` column (previously labelled `bpdu`) is the elapsed
|
||||
time since the last BPDU, capped at 255 seconds; it is not a receive count.
|
||||
Use the web counters or `/stpcnt.json` for full receive counts.
|
||||
|
||||
An own BPDU received back on another port can temporarily hold its sending
|
||||
port in discarding, even if that sending port has received no BPDUs itself.
|
||||
When the hold expires, auto-edge is restored if the sending port has remained
|
||||
silent. A continuing loop keeps the hold active.
|
||||
|
||||
BPDUs shorter than their type allows, judged by the 802.3 length field, are
|
||||
dropped: 7 bytes for a TCN, 38 for a Config BPDU and 39 for an RST BPDU,
|
||||
LLC header included.
|
||||
|
||||
## Limitations
|
||||
|
||||
* One spanning-tree instance; no MSTP, no per-VLAN trees.
|
||||
* No proposal/agreement handshake — an RST-capable neighbour will still
|
||||
converge, but through the timers rather than the fast transition.
|
||||
* Port roles are approximated: the root port and designated ports are
|
||||
distinguished, alternate/backup are not.
|
||||
* Topology changes propagate away from the root only: nothing is announced
|
||||
on the root port (no TCN and no BPDUs at all), so bridges upstream rely on
|
||||
their own detection.
|
||||
* A backup port, one that hears the BPDUs of another port of this switch,
|
||||
is blocked and reported as Backup, but keeps sending BPDUs: loop detection
|
||||
on that segment relies on them.
|
||||
@@ -43,15 +43,19 @@ The following devices have been tested and are fully working:
|
||||
| Ruiying | RY-4GT-2SX | No | [FG-4GT-2SX_V2.0](devices/FG-4GT-2SX_V2.0.md) | 4M | 4 + 2 |
|
||||
| Ruiying | RY-8GT-1SX | No | [FG-8GT-1SX](devices/FG-8GT-1SX.md) | 1M | 8 + 1 |
|
||||
| SICSOLINK| SL-G0402F | No | [PCB-K0402WS-V3.0](devices/PCB-K0402WS-V3.0.md) | | 4 + 2 |
|
||||
| Sirivision | SR-S25G2109F | Yes | [S25G1109FP V1.3](devices/S25G1109FP_V1_3.md) | 2M | 8 + 1 |
|
||||
| Sodola | SL-SWTG124AS-D | Yes | [SWTG024AS-v2.0-17452](devices/SWTG024AS.md) | 2M | 4 + 2 |
|
||||
| Sodola | SL902-SWTG024AS | Yes | [SWTG024AS-v2.1.0](devices/SWTG024AS-V2.1.0.md) | 2M | 4 + 2 |
|
||||
| Steamemo | IG204-V1 | No | [PB-2131](devices/STEAMEMO_IG204_V1.md) | | 4 + 2 |
|
||||
| TRENDnet | TEG-S562 | No | [TEG-S563/EU H/W: V1.0R](devices/TEG-S562.md) | 2M | 4 + 2 |
|
||||
| XikeStor | SKS3200M-4GPY2XF| Yes | [SWTG024AS-v1.0](devices/SWTG024AS.md) | | 4 + 2 |
|
||||
| XikeStor | SKS3200-8E1X | Yes | [SWTG118AS-V2.1-17462](devices/SWTGW218AS.md) | 2M | 8 + 1 |
|
||||
| XikeStor | SKS3200-4E2X | Yes | [SWTG024AS-v2.1.0](devices/SWTG024AS-V2.1.0.md) | 2M | 4 + 2 |
|
||||
| YuanLey | YS25-0402 | No | [PCB-K0402WS-V1.0](devices/PCB-K0402WS-V1.0.md) | 2M | 4 + 2 |
|
||||
| YuanLey | YS25-0402 | No | [PCB-K0402WS-V3.0](devices/PCB-K0402WS-V3.0.md) | 2M | 4 + 2 |
|
||||
| YuanLey | YS25-0402M | Yes | [SIRIVISION_S25G2206FP_V1_1](devices/SIRIVISION_S25G2206FP_V1_1.md) | 2M | 4 + 2 |
|
||||
| Ztyuav | Z-QWYT0402 | No | [PCB-K0402WS-V3.0](devices/PCB-K0402WS-V3.0.md) | | 4 + 2 |
|
||||
| ZoeRax | HG0402XG | No | [HG0402XG_V1.1](devices/ZOERAX_HG0402XG_V1.1.md) | 1M | 4 + 2 |
|
||||
|
||||
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
|
||||
|
||||
@@ -41,23 +41,27 @@ stp_en_d:"Ports start blocked and take up to twice the forward delay to reach fo
|
||||
stp_dis_q:"Disable spanning tree?",stp_dis_d:"All ports go straight to forwarding; loop protection is lost.",
|
||||
stp_cost_err:"Path cost must be 0-200000000",
|
||||
lldp_enabled: "LLDP Enabled",lldp_title:"Service Status",lldp_portname:"Name",lldp_enable:"Send Frames",
|
||||
stp_pcs:"Path cost",stp_pcs_long:"Long (32 bit)",stp_pcs_short:"Short (16 bit)",
|
||||
stp_bh:"BPDUs while disabled",stp_bh_flood:"Flood",stp_bh_filter:"Filter",
|
||||
stp_r4:"Backup",stp_times:"max age / forward delay",stp_proto:"Protocol",stp_nolink:"no link",stp_lasttc:"last",
|
||||
stp_mcheck:"Check",stp_mcheck_t:"Send RST BPDUs again to find out whether the neighbour still needs 802.1D",
|
||||
stp_cnt:"Counters",stp_crx:"BPDUs received",stp_ctx:"BPDUs sent",stp_ctrx:"TC received",stp_cttx:"TC sent",stp_cage:"Last BPDU [s]",
|
||||
st_title:"Port statistics",st_h:"totals since boot",st_txg:"TX good",st_txb:"TX bad",st_rxg:"RX good",
|
||||
st_rxb:"RX bad",st_details:"Details",st_counters:"MIB counters",st_nonzero:"non-zero only",
|
||||
st_autoref:"auto-refresh",st_counter:"Counter",st_value:"Value",st_fail:"failed to load counters",
|
||||
v_title:"VLANs",v_vid:"VID",v_members:"Members",v_tagged:"Tagged",v_untagged:"Untagged",v_pvid_on:"PVID on",
|
||||
v_title:"VLANs",v_vid:"VLAN ID",v_members:"Members",v_tagged:"Tagged",v_untagged:"Untagged",v_pvid_on:"PVID on",
|
||||
v_empty:"No VLANs configured.",v_mgmt:"Management VLAN",v_mgmt_none:"untagged",v_editor:"VLAN editor",
|
||||
v_optional:"optional",v_setmgmt:"Set as mgmt VLAN",v_setmgmt_t:"Make this the management VLAN",
|
||||
v_member:"Member",v_pvid:"PVID",
|
||||
v_legend:"U = untagged member, T = tagged member, - = not a member. PVID assigns this VLAN to untagged ingress traffic.",
|
||||
v_ingress:"Ingress filtering",v_ingress_h:"write-only: the state is not readable from the switch",
|
||||
v_accept:"Accept",v_ing_all:"All",v_ing_apply:"Apply ingress modes",v_del_t:"Delete VLAN",
|
||||
v_optional:"optional",v_setmgmt_q:"Set {n} as the management ID?",v_setmgmt_t:"Management VLAN",
|
||||
v_member:"Member",v_pvid:"PVID",v_add:"Add a new VLAN", v_mgmt_c:"WARNING: You must be able to access this VLAN to continue to manage the switch.",
|
||||
v_legend:"U = untagged member, T = tagged member, - = not a member. PVID is automatically assigned based on the untagged membership.",
|
||||
v_accept:"Accept",v_ing_all:"All",v_del_t:"Delete VLAN",v_rename:"Rename",v_rename_t:"Rename VLAN",
|
||||
v_del_q:"Delete VLAN {n}?",v_del_d:"Ports keep their PVID until reassigned.",
|
||||
v_vid_err:"Enter a VLAN ID (1-4094)",v_name_err:"Name must start with a letter (letters, digits, _)",
|
||||
v_vid_err:"Enter a VLAN ID (1-4094)",v_name_err:"Name must start with a letter (letters, digits, _) and cannot be just 'd' or 'mgmt'",
|
||||
v_loaded:"Loaded VLAN {n}",v_notfound:"VLAN {n} not found (a new one can still be created)",
|
||||
v_nomember:"Select at least one member port (or delete the VLAN instead)",
|
||||
v_ing_none:"Pick an ingress mode for at least one port",v_vid_first:"Enter a VLAN ID first",
|
||||
v_mgmt_q:"Set VLAN {n} as management VLAN?",
|
||||
v_nomember:"Select at least one member port (or delete the VLAN instead)",v_vid_first:"Enter a VLAN ID first",
|
||||
v_mgmt_q:"Set VLAN {n} as management VLAN?",v_vid_err:"VLAN ID already exists",
|
||||
v_mgmt_d:"The switch will start tagging its own traffic with that VLAN. If the port you are connected through does not carry it, this page becomes unreachable and the setting can only be undone over the console.",
|
||||
v_mgmt_tt:"The management VLAN cannot be deleted, you need to assign management to another VLAN first.",
|
||||
l2_title:"MAC address table",l2_filter:"filter...",l2_flush:"Flush learned entries",l2_static:"static",
|
||||
l2_learned:"learned",l2_loading:"loading...",l2_failed:"load failed",l2_entries:"entries",
|
||||
l2_del_t:"Delete entry",l2_flush_q:"Flush all learned MAC entries?",
|
||||
@@ -179,14 +183,12 @@ v_empty:"VLAN は設定されていません。",v_mgmt:"管理 VLAN",v_mgmt_non
|
||||
v_optional:"任意",v_setmgmt:"管理 VLAN に設定",v_setmgmt_t:"この VLAN を管理 VLAN にする",
|
||||
v_member:"メンバー",v_pvid:"PVID",
|
||||
v_legend:"U = タグ無しメンバー、T = タグ付きメンバー、- = 非メンバー。PVID はタグ無し受信トラフィックをこの VLAN に割り当てます。",
|
||||
v_ingress:"受信フィルタ",v_ingress_h:"書き込み専用: 現在の状態はスイッチから読み取れません",
|
||||
v_accept:"受け入れ",v_ing_all:"すべて",v_ing_apply:"受信モードを適用",v_del_t:"VLAN を削除",
|
||||
v_del_q:"VLAN {n} を削除しますか?",v_del_d:"ポートの PVID は再割り当てされるまで維持されます。",
|
||||
v_vid_err:"VLAN ID (1〜4094) を入力してください",v_name_err:"名前は英字で始めてください (英字、数字、_)",
|
||||
v_loaded:"VLAN {n} を読み込みました",v_notfound:"VLAN {n} は存在しません (新規作成は可能です)",
|
||||
v_nomember:"メンバーポートを 1 つ以上選択してください (または VLAN を削除してください)",
|
||||
v_ing_none:"1 つ以上のポートの受信モードを選択してください",v_vid_first:"先に VLAN ID を入力してください",
|
||||
v_mgmt_q:"VLAN {n} を管理 VLAN に設定しますか?",
|
||||
v_vid_first:"先に VLAN ID を入力してください",v_mgmt_q:"VLAN {n} を管理 VLAN に設定しますか?",
|
||||
v_mgmt_d:"スイッチは自身のトラフィックにこの VLAN のタグを付けるようになります。接続中のポートがこの VLAN を通さない場合、このページにアクセスできなくなり、コンソールからしか元に戻せません。",
|
||||
l2_title:"MAC アドレステーブル",l2_filter:"フィルタ...",l2_flush:"学習エントリを消去",l2_static:"静的",
|
||||
l2_learned:"学習",l2_loading:"読み込み中...",l2_failed:"読み込み失敗",l2_entries:"エントリ",
|
||||
@@ -292,14 +294,12 @@ v_empty:"尚未配置 VLAN。",v_mgmt:"管理 VLAN",v_mgmt_none:"untagged",v_edi
|
||||
v_optional:"可选",v_setmgmt:"设为管理 VLAN",v_setmgmt_t:"将此 VLAN 设为管理 VLAN",
|
||||
v_member:"成员",v_pvid:"PVID",
|
||||
v_legend:"U = untagged 成员,T = tagged 成员,- = 非成员。PVID 将入方向的未标记流量归入此 VLAN。",
|
||||
v_ingress:"入方向过滤",v_ingress_h:"只写: 当前状态无法从交换机读取",
|
||||
v_accept:"接受",v_ing_all:"全部",v_ing_apply:"应用入方向模式",v_del_t:"删除 VLAN",
|
||||
v_del_q:"删除 VLAN {n}?",v_del_d:"端口的 PVID 在重新分配前保持不变。",
|
||||
v_vid_err:"请输入 VLAN ID (1-4094)",v_name_err:"名称必须以字母开头 (字母、数字、_)",
|
||||
v_loaded:"已读取 VLAN {n}",v_notfound:"VLAN {n} 不存在 (仍可新建)",
|
||||
v_nomember:"请至少选择一个成员端口 (或改为删除该 VLAN)",
|
||||
v_ing_none:"请至少为一个端口选择入方向模式",v_vid_first:"请先输入 VLAN ID",
|
||||
v_mgmt_q:"将 VLAN {n} 设为管理 VLAN?",
|
||||
v_vid_first:"请先输入 VLAN ID",v_mgmt_q:"将 VLAN {n} 设为管理 VLAN?",
|
||||
v_mgmt_d:"交换机将开始为自身流量打上该 VLAN 标签。如果您所连接的端口不属于该 VLAN,本页面将无法访问,且只能通过控制台撤销此设置。",
|
||||
l2_title:"MAC 地址表",l2_filter:"筛选...",l2_flush:"清除学习条目",l2_static:"静态",
|
||||
l2_learned:"动态学习",l2_loading:"加载中...",l2_failed:"加载失败",l2_entries:"条",
|
||||
@@ -405,14 +405,12 @@ v_empty:"No hay VLANs configuradas.",v_mgmt:"VLAN de gestión",v_mgmt_none:"sin
|
||||
v_optional:"opcional",v_setmgmt:"Establecer como VLAN de gestión",v_setmgmt_t:"Hacer esta la VLAN de gestión",
|
||||
v_member:"Miembro",v_pvid:"PVID",
|
||||
v_legend:"U = miembro sin etiquetar, T = miembro etiquetado, - = no miembro. PVID asigna esta VLAN al tráfico entrante sin etiquetar.",
|
||||
v_ingress:"Filtrado de entrada",v_ingress_h:"solo escritura: el estado no se puede leer desde el switch",
|
||||
v_accept:"Aceptar",v_ing_all:"Todos",v_ing_apply:"Aplicar modos de entrada",v_del_t:"Eliminar VLAN",
|
||||
v_del_q:"¿Eliminar VLAN {n}?",v_del_d:"Los puertos conservan su PVID hasta que se reasignen.",
|
||||
v_vid_err:"Introduce un ID de VLAN (1-4094)",v_name_err:"El nombre debe empezar por una letra (letras, dígitos, _)",
|
||||
v_loaded:"VLAN {n} cargada",v_notfound:"VLAN {n} no encontrada (aún se puede crear una nueva)",
|
||||
v_nomember:"Selecciona al menos un puerto miembro (o elimina la VLAN)",
|
||||
v_ing_none:"Elige un modo de entrada para al menos un puerto",v_vid_first:"Introduce primero un ID de VLAN",
|
||||
v_mgmt_q:"¿Establecer VLAN {n} como VLAN de gestión?",
|
||||
v_vid_first:"Introduce primero un ID de VLAN",v_mgmt_q:"¿Establecer VLAN {n} como VLAN de gestión?",
|
||||
v_mgmt_d:"El switch empezará a etiquetar su propio tráfico con esa VLAN. Si el puerto por el que te conectas no la transporta, esta página dejará de ser accesible y el ajuste solo podrá deshacerse por la consola.",
|
||||
l2_title:"Tabla de direcciones MAC",l2_filter:"filtrar...",l2_flush:"Vaciar entradas aprendidas",l2_static:"estática",
|
||||
l2_learned:"aprendida",l2_loading:"cargando...",l2_failed:"error de carga",l2_entries:"entradas",
|
||||
@@ -467,7 +465,8 @@ fw_noreboot:"no se detectó reinicio: lo más probable es que la imagen fuera re
|
||||
fw_applied:"actualización aplicada",fw_done_t:"Firmware actualizado",
|
||||
fw_done:"El switch verificó la imagen y se reinició con ella. La sesión se restableció, así que se te pedirá que vuelvas a iniciar sesión.",
|
||||
fw_login:"Ir al inicio de sesión",fw_rebooting:"el switch se está reiniciando...",
|
||||
fw_timeout:"el switch no ha vuelto tras 150 s: comprueba la alimentación / consola serie"
|
||||
fw_timeout:"el switch no ha vuelto tras 150 s: comprueba la alimentación / consola serie",
|
||||
nav_isolate:"Aislamiento",nav_lldp:"LLDP",i_temp:"Temperatura",lldp_enabled:"LLDP activado",lldp_title:"Estado del servicio",lldp_portname:"Nombre",lldp_enable:"Enviar tramas",stp_pcs:"Coste de ruta",stp_pcs_long:"Largo (32 bits)",stp_pcs_short:"Corto (16 bits)",stp_bh:"BPDU con STP desactivado",stp_bh_flood:"Inundar",stp_bh_filter:"Filtrar",stp_r4:"Respaldo",stp_times:"edad máxima / retardo de reenvío",stp_proto:"Protocolo",stp_nolink:"sin enlace",stp_lasttc:"último",stp_mcheck:"Comprobar",stp_mcheck_t:"Reenviar BPDUs RST para saber si el vecino sigue necesitando 802.1D",stp_cnt:"Contadores",stp_crx:"BPDUs recibidas",stp_ctx:"BPDUs enviadas",stp_ctrx:"TC recibidas",stp_cttx:"TC enviadas",stp_cage:"Última BPDU [s]",v_setmgmt_q:"¿Fijar {n} como ID de gestión?",v_add:"Añadir una VLAN",v_mgmt_c:"AVISO: debes poder acceder a esta VLAN para seguir gestionando el switch.",v_rename:"Renombrar",v_rename_t:"Renombrar VLAN",v_mgmt_tt:"La VLAN de gestión no se puede borrar, primero hay que asignar la gestión a otra VLAN.",iso_title:"Aislamiento de puertos",iso_from:"Desde el puerto",iso_to:"Hacia el puerto",iso_all:"Permitir todo",iso_intro:"Una trama que entra por un puerto solo puede salir por los puertos marcados en su fila. El aislamiento se decide antes del reenvío, así que un puerto sin marcar es inalcanzable de forma directa, diga lo que diga la tabla de direcciones.",iso_legend:"La celda del propio puerto siempre está permitida. Un número de puerto en rojo indica un puerto que no está del todo abierto.",lacp_heading:"LACP (802.3ad)",lag_mode:"Modo",lag_static:"Estático",lacp_neg:"negociando",lacp_agg:"agregador",lacp_members:"miembros activos",lacp_hint:"LACP: elige los puertos candidatos y pulsa Aplicar para iniciar la negociación",lacp_actor:"Local",lacp_partner:"Remoto",lacp_rxstate:"Estado RX",lacp_rx:"LACPDUs recibidas",lacp_psys:"Sistema remoto",sc_title:"Control de tormentas",sc_h:"por puerto y tipo de tráfico, vacío = desactivado",sc_bcast:"Difusión",sc_mcast:"Multidifusión",sc_ucast:"Unidifusión desconocida",sc_umcast:"Multidifusión desconocida",sc_err:"El límite de tormenta debe ser 1-1048575 pps o 1-10000000 kbit/s",sc_note:"Las tramas de un tipo por encima de su límite se descartan a la entrada del switch.",sy_dns1:"Servidor DNS",sy_dns2:"DNS secundario",sy_dns_err:"IP del servidor DNS no válida",sy_dns_dhcp:"desde DHCP: {ip}",sy_time:"Hora",sy_ntp:"NTP",sy_ntpsrv:"Servidor NTP",sy_ntpsrv_p:"nombre de host o dirección IPv4",sy_ntpint:"Intervalo de consulta [min]",sy_tz:"Zona horaria",sy_dst:"Horario de verano",sy_dst_eu:"Europa",sy_dst_us:"Estados Unidos",sy_now:"Hora local",sy_sync:"Última sincronización",sy_ntpaddr:"Dirección del servidor",sy_nosync:"sin sincronizar",sy_waiting:"esperando al servidor",sy_ago:"hace {n} min",sy_ntp_err:"Introduce el nombre de host o la dirección IPv4 del servidor NTP",sy_ntpint_err:"El intervalo de consulta debe ser 1-1440",sy_time_note:"El switch no tiene reloj con batería: la hora se pierde en cada arranque y se vuelve a fijar desde el servidor NTP cuando la red está lista. Guarda en flash para conservar los ajustes.",cw_longline:"La línea {n} mide {m} bytes: el switch solo puede ejecutar líneas de hasta 126 bytes y la saltaría al arrancar, así que se rechaza la escritura. Acórtala primero.",fw_other:"Esta imagen no se compiló para este switch ({m}): los pines de SFP, LEDs y botones pueden no coincidir. Súbela solo si estás seguro."
|
||||
},
|
||||
fr:{
|
||||
nav_dash:"Tableau de bord",nav_ports:"Ports",nav_stp:"Arborescence",nav_stats:"Statistiques",
|
||||
@@ -518,14 +517,12 @@ v_empty:"Aucun VLANs configuré.",v_mgmt:"VLAN de management",v_mgmt_none:"déta
|
||||
v_optional:"optionel",v_setmgmt:"Mette en VLAN de management",v_setmgmt_t:"Mettre celui-ci en VLAN de management",
|
||||
v_member:"Membre",v_pvid:"PVID",
|
||||
v_legend:"U = Membre détagué (UnTagged), T = membre tagué (Tagged), - = non membre. PVID (Port VLan ID) assigne ce VLAN au trafic entrant non tagué.",
|
||||
v_ingress:"Filtrage entrant",v_ingress_h:"en ériture seule: cet état n'est pas lisible depuis le communtateur réseau",
|
||||
v_accept:"Accepté",v_ing_all:"Tous",v_ing_apply:"Appliquer les filtes",v_del_t:"Supprimer le VLAN",
|
||||
v_del_q:"Supprimer le VLAN {n}?",v_del_d:"Les ports conservent leur PVID tant qu'ils ne sont pas réassignés.",
|
||||
v_vid_err:"Entrez un identifiant VLAN (1-4094)",v_name_err:"Le nom doit commencer par une lettre (lettre, chiffres, _)",
|
||||
v_loaded:"VLAN chargés {n}",v_notfound:"VLAN {n} non trouvé (un nouveau peut toutefois être créé)",
|
||||
v_nomember:"Selectionnez au moins un port membre (ou supprimez le VLAN)",
|
||||
v_ing_none:"Sélectionnez le mode entrant pour au moins un port",v_vid_first:"Entrez un identifiant VLAN d'abord",
|
||||
v_mgmt_q:"Mettre ce VLAN {n} en tant que VLAN de management?",
|
||||
v_vid_first:"Entrez un identifiant VLAN d'abord",v_mgmt_q:"Mettre ce VLAN {n} en tant que VLAN de management?",
|
||||
v_mgmt_d:"Ce commutateur commencera par taguer son propre trafic avec ce VLAN. Si le port avec lequel vous êtes connecté ne le permet pas, cette page deviendra inaccessible et les paramètres ne pourront plus être changés que via la console.",
|
||||
l2_title:"Table des adresses MAC",l2_filter:"filtre...",l2_flush:"Effacer les entrées apprises",l2_static:"static",
|
||||
l2_learned:"appris",l2_loading:"chargement...",l2_failed:"chargement non réussi",l2_entries:"entrées",
|
||||
@@ -644,17 +641,18 @@ st_autoref:"automatisch aktualisieren",st_counter:"Zähler",st_value:"Wert",st_f
|
||||
v_title:"VLANs",v_vid:"VID",v_members:"Mitglieder",v_tagged:"Tagged",v_untagged:"Untagged",v_pvid_on:"PVID auf",
|
||||
v_empty:"Keine VLANs konfiguriert.",v_mgmt:"Management-VLAN",v_mgmt_none:"untagged",v_editor:"VLAN-Editor",
|
||||
v_optional:"optional",v_setmgmt:"Als Management-VLAN",v_setmgmt_t:"Dieses VLAN zum Management-VLAN machen",
|
||||
v_setmgmt_q:"{n} als Management-VLAN setzen?",v_add:"Neues VLAN hinzufügen",
|
||||
v_mgmt_c:"WARNUNG: Du musst dieses VLAN erreichen können, um den Switch weiter verwalten zu können.",
|
||||
v_member:"Mitglied",v_pvid:"PVID",
|
||||
v_legend:"U = Mitglied untagged, T = Mitglied tagged, - = kein Mitglied. PVID ordnet eingehenden Verkehr ohne Tag diesem VLAN zu.",
|
||||
v_ingress:"Ingress-Filter",v_ingress_h:"nur schreibbar: der Zustand lässt sich vom Switch nicht auslesen",
|
||||
v_accept:"Annehmen",v_ing_all:"Alle",v_ing_apply:"Ingress-Modi übernehmen",v_del_t:"VLAN löschen",
|
||||
v_accept:"Annehmen",v_ing_all:"Alle",v_ing_apply:"Ingress-Modi übernehmen",v_del_t:"VLAN löschen",v_rename:"Umbenennen",v_rename_t:"VLAN umbenennen",
|
||||
v_del_q:"VLAN {n} löschen?",v_del_d:"Die Ports behalten ihre PVID, bis sie neu zugewiesen wird.",
|
||||
v_vid_err:"VLAN-ID eingeben (1-4094)",v_name_err:"Der Name muss mit einem Buchstaben beginnen (Buchstaben, Ziffern, _)",
|
||||
v_loaded:"VLAN {n} geladen",v_notfound:"VLAN {n} nicht gefunden (es kann neu angelegt werden)",
|
||||
v_nomember:"Mindestens einen Mitglieds-Port wählen (oder das VLAN stattdessen löschen)",
|
||||
v_ing_none:"Für mindestens einen Port einen Ingress-Modus wählen",v_vid_first:"Zuerst eine VLAN-ID eingeben",
|
||||
v_mgmt_q:"VLAN {n} als Management-VLAN setzen?",
|
||||
v_vid_first:"Zuerst eine VLAN-ID eingeben",v_mgmt_q:"VLAN {n} als Management-VLAN setzen?",
|
||||
v_mgmt_d:"Der Switch versieht dann seinen eigenen Verkehr mit diesem VLAN-Tag. Führt der Port, über den du verbunden bist, dieses VLAN nicht, ist diese Seite nicht mehr erreichbar und die Einstellung lässt sich nur noch über die serielle Konsole zurücknehmen.",
|
||||
v_mgmt_tt:"Das Management-VLAN lässt sich nicht löschen. Lege zuerst ein anderes VLAN als Management-VLAN fest.",
|
||||
l2_title:"MAC-Adresstabelle",l2_filter:"filtern...",l2_flush:"Gelernte Einträge löschen",l2_static:"statisch",
|
||||
l2_learned:"gelernt",l2_loading:"lädt...",l2_failed:"Laden fehlgeschlagen",l2_entries:"Einträge",
|
||||
l2_del_t:"Eintrag löschen",l2_flush_q:"Alle gelernten MAC-Einträge löschen?",
|
||||
@@ -811,34 +809,51 @@ function getJSON(p){return api(p).then(function(r){if(!r.ok)throw new Error(p+"
|
||||
function getText(p){return api(p).then(function(r){if(!r.ok)throw new Error(p+" "+r.status);return r.body})}
|
||||
|
||||
var CONF_CMDS=[
|
||||
/^ip\s+(\d{1,3}\.){3}\d{1,3}$/,/^ip\s+dhcp$/,
|
||||
/^gw\s+(\d{1,3}\.){3}\d{1,3}$/,/^netmask\s+(\d{1,3}\.){3}\d{1,3}$/,
|
||||
/^syslog\s+(on|off)$/,/^syslog\s+ip\s+(\d{1,3}\.){3}\d{1,3}$/,/^syslog\s+port\s+\d{1,5}$/,
|
||||
/^ip\s+(\d{1,3}\.){3}\d{1,3}$/,
|
||||
/^ip\s+dhcp$/,
|
||||
/^gw\s+(\d{1,3}\.){3}\d{1,3}$/,
|
||||
/^netmask\s+(\d{1,3}\.){3}\d{1,3}$/,
|
||||
/^syslog\s+(on|off)$/,
|
||||
/^syslog\s+ip\s+(\d{1,3}\.){3}\d{1,3}$/,
|
||||
/^syslog\s+port\s+\d{1,5}$/,
|
||||
/^session\s+\d{1,5}$/,
|
||||
/^dns\s+server\s+(\d{1,3}\.){3}\d{1,3}(\s+(\d{1,3}\.){3}\d{1,3})?$/,
|
||||
/^ntp\s+(on|off)$/,/^ntp\s+server\s+[A-Za-z0-9.-]{1,47}$/,/^ntp\s+interval\s+\d{1,4}$/,
|
||||
/^ntp\s+timezone\s+[+-]?\d{1,2}(:\d{2})?$/,/^ntp\s+dst\s+(off|eu|us)$/,
|
||||
/^ntp\s+(on|off)$/,
|
||||
/^ntp\s+server\s+[A-Za-z0-9.-]{1,47}$/,
|
||||
/^ntp\s+interval\s+\d{1,4}$/,
|
||||
/^ntp\s+timezone\s+[+-]?\d{1,2}(:\d{2})?$/,
|
||||
/^ntp\s+dst\s+(off|eu|us)$/,
|
||||
/^passwd\s+\S+$/,/^hostname\s+\S{1,23}$/,
|
||||
/^vlan\s+\d{1,4}\s+d$/,/^vlan\s+\d{1,4}\s+mgmt$/,
|
||||
/^vlan\s+\d{1,4}(\s+[a-zA-Z]\w*)?(\s+\d{1,2}t?)+$/,
|
||||
/^pvid\s+\d{1,2}\s+\d{1,4}$/,
|
||||
/^ingress(\s+\d{1,2}[tua])+$/,/^ingress\s+[tua]$/,
|
||||
/^port\s+\d{1,2}\s+(10m|100m|1g|2g5|5g|10g|auto|on|off)(\s+(half|full))?$/,
|
||||
/^port\s+\d{1,2}\s+name\s+\S+$/,
|
||||
/^port\s+\d{1,2}\s+lldp\s+(permit|block)+$/,
|
||||
/^eee\s+(on|off)(\s+\d{1,2})?(\s+(100m|1g|2g5))?$/,
|
||||
/^mirror(\s+\d{1,2})(\s+\d{1,2}[tr]?)+$/,/^mirror\s+off$/,
|
||||
/^lag\s+[1-4](\s+\d{1,2})+$/,/^lag\s+[1-4]\s+d$/,/^lag\s+[1-4]\s+lacp(\s+\d{1,2})+$/,/^lag\s+[1-4]\s+lacp\s+off$/,/^laghash\s+[1-4](\s+\w+)+$/,
|
||||
/^isolate\s+\d{1,2}(\s+(off|\d{1,2}))+$/,
|
||||
/^stp\s+(on|off)$/,/^stp\s+(prio|hello|maxage|fwd|txhold)\s+\d{1,2}$/,
|
||||
/^vlan\s+\d+\s+(d|mgmt)$/m,
|
||||
/^vlan\s+\d+(\s+[a-zA-Z]\w*)?(\s+\d+t?)*$/m,
|
||||
/^pvid\s+\d+\s+\d+$/m,
|
||||
/^ingress(\s+\d+[tua])+$/,/^ingress\s+[tua]$/,
|
||||
/^port\s+\d+\s+(10m|100m|1g|2g5|5g|10g|auto|on|off)(\s+(half|full))?$/,
|
||||
/^port\s+\d+\s+name\s+\S+$/,
|
||||
/^port\s+\d+\s+lldp\s+(permit|block)+$/,
|
||||
/^eee\s+(on|off)(\s+\d+)?(\s+(100m|1g|2g5))?$/,
|
||||
/^mirror(\s+\d+)(\s+\d+[tr]?)+$/,/^mirror\s+off$/,
|
||||
/^lag\s+[1-4](\s+\d+)+$/,/^lag\s+[1-4]\s+d$/,
|
||||
/^lag\s+[1-4]\s+lacp(\s+\d+)+$/,
|
||||
/^lag\s+[1-4]\s+lacp\s+off$/,
|
||||
/^laghash\s+[1-4](\s+\w+)+$/,
|
||||
/^isolate\s+\d+(\s+(off|\d+))+$/,
|
||||
/^stp\s+(on|off)$/,/^stp\s+(prio|hello|maxage|fwd|txhold)\s+\d+$/,
|
||||
/^stp\s+version\s+(rstp|stp)$/,
|
||||
/^stp\s+(port\s+\d{1,2}|lag\s+[1-4])\s+(on|off)$/,/^stp\s+(port\s+\d{1,2}|lag\s+[1-4])\s+edge\s+(on|off|auto)$/,
|
||||
/^stp\s+(port\s+\d{1,2}|lag\s+[1-4])\s+cost\s+\d{1,9}$/,/^stp\s+(port\s+\d{1,2}|lag\s+[1-4])\s+prio\s+\d{1,3}$/,
|
||||
/^stp\s+(port\s+\d{1,2}|lag\s+[1-4])\s+guard\s+(none|bpdu|root)$/,/^stp\s+(port\s+\d{1,2}|lag\s+[1-4])\s+filter\s+(on|off)$/,
|
||||
/^stp\s+(port\s+\d{1,2}|lag\s+[1-4])\s+p2p\s+(auto|on|off)$/,
|
||||
/^igmp\s+(on|off)$/,/^mtu\s+\d{1,2}\s+\d+$/,/^lldp\s+(on|off)$/,
|
||||
/^bw\s+(in|out)\s+\d{1,2}\s+\S+$/,
|
||||
/^storm\s+\d{1,2}\s+(bcast|mcast|ucast|umcast)\s+(off|\d{1,8}\s+(pps|kbps))$/,
|
||||
/^stp\s+pathcost\s+(long|short)$/,
|
||||
/^stp\s+bpdu\s+(filter|flood)$/,
|
||||
/^stp\s+(port\s+\d+|lag\s+[1-4])\s+(on|off)$/,
|
||||
/^stp\s+(port\s+\d+|lag\s+[1-4])\s+edge\s+(on|off|auto)$/,
|
||||
/^stp\s+(port\s+\d+|lag\s+[1-4])\s+cost\s+\d+$/,
|
||||
/^stp\s+(port\s+\d+|lag\s+[1-4])\s+prio\s+\d+$/,
|
||||
/^stp\s+(port\s+\d+|lag\s+[1-4])\s+guard\s+(none|bpdu|root)$/,
|
||||
/^stp\s+(port\s+\d+|lag\s+[1-4])\s+filter\s+(on|off)$/,
|
||||
/^stp\s+(port\s+\d=|lag\s+[1-4])\s+p2p\s+(auto|on|off)$/,
|
||||
/^igmp\s+(on|off)$/,
|
||||
/^mtu\s+\d+\s+\d+$/,
|
||||
/^lldp\s+(on|off)$/,
|
||||
/^bw\s+(in|out)\s+\d+\s+\S+$/,
|
||||
/^storm\s+\d+\s+(bcast|mcast|ucast|umcast)\s+(off|\d+\s+(pps|kbps))$/,
|
||||
];
|
||||
function isConfCmd(line){
|
||||
for(var i=0;i<CONF_CMDS.length;i++)if(CONF_CMDS[i].test(line))return true;
|
||||
@@ -862,7 +877,6 @@ function postCmds(list){
|
||||
list.forEach(function(c){p=p.then(function(){return postCmd(c,true)})});
|
||||
return p.then(function(){toast(t("t_cmds",{n:list.length}),"ok")});
|
||||
}
|
||||
|
||||
function toast(msg,cls){
|
||||
var el=h("div",{class:"toast "+(cls||""),text:msg});
|
||||
$("toasts").appendChild(el);
|
||||
@@ -948,6 +962,7 @@ function byPort(a){return a.sort(function(x,y){return x.portNum-y.portNum})}
|
||||
function pollStatus(){
|
||||
return getJSON("/status.json").then(function(s){
|
||||
byPort(s);
|
||||
S.ports=s;
|
||||
var now=Date.now();
|
||||
if(!S.n){
|
||||
S.n=s.length;
|
||||
@@ -969,7 +984,7 @@ function pollStatus(){
|
||||
};
|
||||
});
|
||||
}
|
||||
S.prev=s;S.prevT=now;S.ports=s;
|
||||
S.prev=s;S.prevT=now;
|
||||
updateStrip();
|
||||
var hk=tabHooks[curTab];
|
||||
if(hk&&hk.status)hk.status();
|
||||
@@ -1216,12 +1231,14 @@ function fmtBridge(hex){
|
||||
if(!hex||hex.length<16)return"-";
|
||||
return parseInt(hex.slice(0,4),16)+" / "+hex.slice(4).replace(/(..)(?=.)/g,"$1:");
|
||||
}
|
||||
function stpEnt(pt){pt.lag=pt.p>100?pt.p-100:0}
|
||||
function stpKey(pt){return pt.lag?"L"+pt.lag:String(pt.p)}
|
||||
function stpName(pt){return pt.lag?"LAG"+pt.lag+" ("+maskToPorts(pt.mbr).join(",")+")":String(pt.p)}
|
||||
function stpName(pt){return pt.lag?"LAG"+pt.lag:String(pt.p)}
|
||||
function stpDhms(x){return Math.floor(x/86400)+"d "+Math.floor(x/3600)%24+"h "+Math.floor(x/60)%60+"m "+x%60+"s"}
|
||||
function stpPre(k){return k.charAt(0)==="L"?"stp lag "+k.slice(1)+" ":"stp port "+k+" "}
|
||||
function stpBuild(ports){
|
||||
var tb=$("stpcfg").tBodies[0],st=$("stpstat").tBodies[0];
|
||||
tb.innerHTML="";st.innerHTML="";
|
||||
var tb=$("stpcfg").tBodies[0],st=$("stpstat").tBodies[0],ct=$("stpcnt").tBodies[0];
|
||||
tb.innerHTML="";st.innerHTML="";ct.innerHTML="";
|
||||
ports.forEach(function(pt){
|
||||
var p=stpKey(pt);
|
||||
var tr=tb.insertRow();
|
||||
@@ -1239,7 +1256,12 @@ function stpBuild(ports){
|
||||
tr.insertCell().appendChild(h("button",{class:"ctl",text:t("c_apply"),onclick:function(){stpApplyPort(p)}}));
|
||||
var sr=st.insertRow();
|
||||
sr.insertCell().textContent=stpName(pt);
|
||||
["ro","st","db","dp","dc","oe","op"].forEach(function(k){sr.insertCell().id="st"+k+p});
|
||||
["ro","st","db","dp","dc","oe","op","pv"].forEach(function(k){sr.insertCell().id="st"+k+p});
|
||||
sr.insertCell().appendChild(h("button",{class:"ctl",id:"stmc"+p,text:t("stp_mcheck"),title:t("stp_mcheck_t"),
|
||||
onclick:function(){postCmd(stpPre(p)+"mcheck").then(stpLoad).catch(function(){})}}));
|
||||
var cr=ct.insertRow();
|
||||
cr.insertCell().textContent=stpName(pt);
|
||||
["rx","tx","trx","ttx","age"].forEach(function(k){cr.insertCell().id="sc"+k+p});
|
||||
});
|
||||
}
|
||||
function stpPortVals(pt){
|
||||
@@ -1253,10 +1275,21 @@ function stpPortVals(pt){
|
||||
p2p:["auto","on","off"][pt.p2]||"auto",
|
||||
};
|
||||
}
|
||||
function stpCnt(c){
|
||||
c.ports.forEach(function(pt){
|
||||
stpEnt(pt);
|
||||
var p=stpKey(pt);
|
||||
if(!$("scrx"+p))return;
|
||||
var v=[0,1,2,3].map(function(k){return parseInt(pt.c.slice(8*k,8*k+8),16)}),age=parseInt(pt.c.slice(32,36),16);
|
||||
["rx","tx","trx","ttx"].forEach(function(k,i){$("sc"+k+p).textContent=v[i]});
|
||||
$("scage"+p).textContent=!v[0]?"-":(age===0xffff?">":"")+Math.floor(age/c.hz);
|
||||
});
|
||||
}
|
||||
function stpLoad(){
|
||||
return getJSON("/stp.json").then(function(s){
|
||||
s.ports.sort(function(a,b){return((a.lag||0)-(b.lag||0))||(a.p-b.p)});
|
||||
var sig=s.ports.map(function(pt){return stpKey(pt)+":"+(pt.mbr||0)}).join();
|
||||
s.ports.forEach(stpEnt);
|
||||
s.ports.sort(function(a,b){return(a.lag-b.lag)||(a.p-b.p)});
|
||||
var sig=s.ports.map(stpKey).join();
|
||||
if(sig!==stpSig){stpSig=sig;stpBuild(s.ports);}
|
||||
stpCur=s;
|
||||
var en=$("stpen");
|
||||
@@ -1264,16 +1297,19 @@ function stpLoad(){
|
||||
var msg;
|
||||
if(!s.on)msg=t("stp_off_msg");
|
||||
else{
|
||||
var me=(s.prio*4096).toString(16).padStart(4,"0")+s.myMac;
|
||||
var me=(s.prio*4096).toString(16).padStart(4,"0")+s.myMac,tc=parseInt(s.tc,16),rp="-";
|
||||
s.ports.forEach(function(pt){if(pt.role===1)rp=stpName(pt)});
|
||||
msg=t("stp_bridge")+" "+fmtBridge(me)+(s.weRoot
|
||||
?" ("+t("stp_root_self")+")"
|
||||
:"; "+t("stp_root")+" "+fmtBridge(s.rootPrio+s.rootMac)+" "+t("stp_via")+" "+s.rootPort+", "+t("stp_cost")+" "+parseInt(s.cost,16))
|
||||
+"; "+t("stp_tc")+": "+parseInt(s.tc,16);
|
||||
:"; "+t("stp_root")+" "+fmtBridge(s.rootPrio+s.rootMac)+" "+t("stp_via")+" "+rp+", "+t("stp_cost")+" "+parseInt(s.cost,16))
|
||||
+"; "+t("stp_tc")+": "+tc+(tc?" ("+t("stp_lasttc")+" "+stpDhms(parseInt(s.tcs,16))+")":"")
|
||||
+"; "+t("stp_times")+" "+s.rMaxage+" / "+s.rFwd+" s";
|
||||
}
|
||||
$("stpids").textContent=msg;
|
||||
var bc=$("stpbridge");
|
||||
if(!bc.dataset.dirty){
|
||||
$("stpver").value=s.rstp?"rstp":"stp";
|
||||
$("stppcs").value=s.pcs?"short":"long";$("stpbh").value=s.bh?"filter":"flood";
|
||||
$("stpprio").value=String(s.prio);
|
||||
$("stphello").value=s.hello;$("stpmaxage").value=s.maxage;
|
||||
$("stpfwd").value=s.fwd;$("stptxhold").value=s.txhold;
|
||||
@@ -1282,18 +1318,21 @@ function stpLoad(){
|
||||
var p=stpKey(pt);
|
||||
var trip=(pt.f&STP_PF.TRIP)?" "+badge(t("stp_trip"),"bad"):"";
|
||||
$("stro"+p).textContent=s.on&&pt.role?t("stp_r"+pt.role):"-";
|
||||
$("stst"+p).innerHTML=s.on?badge(t("stp_s"+pt.st),pt.st===3?"ok":"")+trip:"-";
|
||||
$("stst"+p).innerHTML=s.on?(pt.lk?badge(t("stp_s"+pt.st),pt.st===3?"ok":""):badge(t("stp_nolink")))+trip:"-";
|
||||
$("stdb"+p).textContent=s.on?fmtBridge(pt.db):"-";
|
||||
$("stdp"+p).textContent=s.on?parseInt(pt.dp.slice(0,2),16)+"."+parseInt(pt.dp.slice(2),16):"-";
|
||||
$("stdc"+p).textContent=s.on?parseInt(pt.dc,16):"-";
|
||||
$("stoe"+p).textContent=s.on?t((pt.f&STP_PF.OPEREDGE)?"c_yes":"c_no"):"-";
|
||||
$("stop"+p).textContent=s.on?t(pt.p2===2?"c_no":"c_yes"):"-";
|
||||
$("stpv"+p).textContent=s.on&&pt.role?(s.rstp&&!pt.lg?"RSTP":"STP"):"-";
|
||||
$("stmc"+p).disabled=!(s.on&&pt.role&&s.rstp);
|
||||
var row=$("sten"+p).closest("tr");
|
||||
if(row.dataset.dirty)return;
|
||||
var v=stpPortVals(pt);
|
||||
$("sten"+p).checked=v.en;$("sted"+p).value=v.edge;$("stco"+p).value=v.cost;
|
||||
$("stpr"+p).value=v.prio;$("stgu"+p).value=v.guard;$("stfi"+p).value=v.filter;$("stpp"+p).value=v.p2p;
|
||||
});
|
||||
if($("stpcntd").open)return getJSON("/stpcnt.json").then(stpCnt);
|
||||
}).catch(function(){});
|
||||
}
|
||||
function stpApplyPort(p){
|
||||
@@ -1321,6 +1360,8 @@ $("stpbapply").addEventListener("click",function(){
|
||||
var ver=$("stpver").value,prio=$("stpprio").value;
|
||||
if(ver!==(s.rstp?"rstp":"stp"))cmds.push("stp version "+ver);
|
||||
if(prio!==String(s.prio))cmds.push("stp prio "+prio);
|
||||
if($("stppcs").value!==(s.pcs?"short":"long"))cmds.push("stp pathcost "+$("stppcs").value);
|
||||
if($("stpbh").value!==(s.bh?"filter":"flood"))cmds.push("stp bpdu "+$("stpbh").value);
|
||||
[["stphello","hello"],["stpmaxage","maxage"],["stpfwd","fwd"],["stptxhold","txhold"]].forEach(function(f){
|
||||
var v=$(f[0]).value;
|
||||
if(String(v)!==String(s[f[1]]))cmds.push("stp "+f[1]+" "+v);
|
||||
@@ -1339,6 +1380,7 @@ $("stpen").addEventListener("change",function(){
|
||||
var sel=$("stpprio");
|
||||
for(var i=0;i<16;i++)sel.appendChild(h("option",{value:String(i),text:i+" ("+(i*4096)+")"}));
|
||||
})();
|
||||
$("stpcntd").addEventListener("toggle",function(){if(this.open)stpLoad()});
|
||||
var stpPoller=new Poller(stpLoad,3000);
|
||||
tabHooks.stp={enter:function(){stpPoller.start()},leave:function(){stpPoller.stop()}};
|
||||
|
||||
@@ -1475,131 +1517,282 @@ function rangeStr(list){
|
||||
}
|
||||
return parts.join(", ");
|
||||
}
|
||||
|
||||
function vlanRefresh() {
|
||||
var tb = $("vtable").tBodies[0];
|
||||
tb.style="cursor:pointer;"
|
||||
return getJSON("/vlanlist").then(function(d) {
|
||||
var vl = d.vlan || [];
|
||||
$("vmgmtcur").textContent=d.mgmt?String(d.mgmt):t("v_mgmt_none");
|
||||
$("vmgmtcur").dataset.id = d.mgmt;
|
||||
$("vempty").style.display = vl.length ? "none" : "";
|
||||
tb.innerHTML = "";
|
||||
var p = Promise.resolve();
|
||||
vl.forEach(function(v) {
|
||||
p=p.then(function(){return getJSON("/vlan.json?vid="+v.id).catch(function(){return null})}).then(function(d){
|
||||
p = p.then(function() {
|
||||
return getJSON("/vlan.json?vid=" + v.id).catch(function() {
|
||||
return null
|
||||
})
|
||||
}).then(function(d) {
|
||||
var tr = tb.insertRow();
|
||||
tr.insertCell().appendChild(h("a",{href:"#vlan",text:String(v.id),onclick:function(e){
|
||||
e.preventDefault();$("vvid").value=v.id;vlanLoad();
|
||||
}}));
|
||||
tr.onclick = function(e) {
|
||||
e.preventDefault();
|
||||
vlanLoad(v.id);
|
||||
};
|
||||
var l = h("a", {
|
||||
href: "#vlan",
|
||||
text: String(v.id) + ($("vmgmtcur").dataset.id == v.id ? " ("+t("v_mgmt")+")":""),
|
||||
"data-id": v.id,
|
||||
"data-name": v.name
|
||||
});
|
||||
tr.insertCell().appendChild(l);
|
||||
tr.insertCell().textContent = v.name || "";
|
||||
if(!d){for(var c=0;c<4;c++)tr.insertCell().textContent="?";}
|
||||
else{
|
||||
var m=parseInt(d.members,16),mem=m&0x3ff,unt=((m>>10)&0x3ff)&mem;
|
||||
if (!d) {
|
||||
for (var c = 0; c < 4; c++) tr.insertCell().textContent = "?";
|
||||
} else {
|
||||
var m = parseInt(d.members, 16),
|
||||
mem = m & 0x3ff,
|
||||
unt = ((m >> 10) & 0x3ff) & mem,
|
||||
tag = mem & ~unt;
|
||||
var pv = parseInt(d.pvid, 16) & 0x3ff;
|
||||
tr.insertCell().textContent = rangeStr(maskToPorts(mem));
|
||||
tr.insertCell().textContent=rangeStr(maskToPorts(mem&~unt));
|
||||
tr.insertCell().textContent = rangeStr(maskToPorts(tag));
|
||||
tr.insertCell().textContent = rangeStr(maskToPorts(unt));
|
||||
tr.insertCell().textContent = rangeStr(maskToPorts(pv));
|
||||
l.dataset.t = maskToPorts(tag);
|
||||
l.dataset.u = maskToPorts(unt);
|
||||
}
|
||||
var del=tr.insertCell();
|
||||
if(v.id!==1)del.appendChild(h("button",{class:"ctl",text:"\u2715",title:t("v_del_t"),onclick:function(){
|
||||
var act = tr.insertCell();
|
||||
act.onclick=function(e){e.stopPropagation()};
|
||||
act.appendChild(h("button", {
|
||||
class: "ctl",
|
||||
text: t("v_rename"),
|
||||
title: t("v_rename_t"),
|
||||
onclick: function() {
|
||||
var body = h("div");
|
||||
body.appendChild(h("p", null, [
|
||||
h("label", {class: "row"}, [
|
||||
h("span",{class:"lb", "data-i18n":"c_name"},[document.createTextNode(t("c_name"))]),
|
||||
h("input", {id: "nvlrename", class: "in", size: "12", maxlength: "15", "data-i18n-p": "v_optional", value:(v.name||"")})
|
||||
])
|
||||
]));
|
||||
modal("Rename VLAN "+v.id, body, [h("button", {
|
||||
class: "ctl pri", text: t("v_rename_t"),
|
||||
onclick: function(){
|
||||
if(!validVlanName($("nvlrename").value)) return;
|
||||
postCmd(vlan2str(v.id,$("nvlrename").value,mySplit(l.dataset.t),mySplit(l.dataset.u))).then(function(){closeModal();vlanRefresh()});
|
||||
}
|
||||
})]);
|
||||
},
|
||||
}));
|
||||
if (v.id == $("vmgmtcur").dataset.id) {
|
||||
act.appendChild(h("span",{class:"ctl", title:t("v_mgmt_tt"), class:"hint", style:"color: var(--fg2); font-size: 150%; padding-left: 10px;"},[document.createTextNode("\u{1F6C8}")]));
|
||||
} else {
|
||||
act.appendChild(h("button", {
|
||||
class: "ctl",
|
||||
text: "\u2715",
|
||||
title: t("v_del_t"),
|
||||
style: "margin-left: 5px;",
|
||||
onclick: function() {
|
||||
confirmModal(t("v_del_q", { n: v.id }), t("v_del_d"), function() {
|
||||
postCmd("vlan " + v.id + " d").then(vlanRefresh).catch(function() {});
|
||||
});
|
||||
}}));
|
||||
}
|
||||
}));
|
||||
}
|
||||
}).catch(function() {});
|
||||
});
|
||||
return p;
|
||||
});
|
||||
}
|
||||
|
||||
function buildVlanEdit() {
|
||||
var tb = $("vedit").tBodies[0];
|
||||
if (tb.rows.length || !S.n) return;
|
||||
var hd=tb.insertRow();hd.insertCell().className="mut";
|
||||
var rM=tb.insertRow();rM.insertCell().textContent=t("v_member");
|
||||
var rP=tb.insertRow();rP.insertCell().textContent=t("v_pvid");
|
||||
var hd = tb.insertRow();
|
||||
hd.insertCell().className = "mut";
|
||||
var rN = tb.insertRow();
|
||||
rN.insertCell().textContent = t("c_name");
|
||||
var rM = tb.insertRow();
|
||||
rM.insertCell().textContent = t("v_member");
|
||||
var rP = tb.insertRow();
|
||||
rP.insertCell().textContent = t("v_pvid");
|
||||
for (var p = 1; p <= S.n; p++)(function(p) {
|
||||
hd.insertCell().innerHTML = '<b>' + p + '</b>';
|
||||
var seg = h("span", { class: "seg", id: "vm" + p });
|
||||
["-", "U", "T"].forEach(function(s, ix) {
|
||||
seg.appendChild(h("button",{text:s,"data-v":ix,onclick:function(){
|
||||
seg.appendChild(h("button", {
|
||||
text: s,
|
||||
"data-v": ix,
|
||||
onclick: function() {
|
||||
seg.querySelectorAll("button").forEach(function(b) { b.classList.remove("on") });
|
||||
this.classList.add("on");
|
||||
}}));
|
||||
}
|
||||
}));
|
||||
});
|
||||
seg.children[0].classList.add("on");
|
||||
rN.insertCell().appendChild(document.createTextNode((S.ports.filter(x=>x.portNum==p)[0]).name));
|
||||
rM.insertCell().appendChild(seg);
|
||||
rP.insertCell().appendChild(h("input",{type:"checkbox",id:"vp"+p}));
|
||||
rP.insertCell().appendChild(h("span", { id: "vp" + p }));
|
||||
})(p);
|
||||
var it=$("ingress").tBodies[0];
|
||||
var ih=it.insertRow();ih.insertCell().className="mut";
|
||||
var ir=it.insertRow();ir.insertCell().textContent=t("v_accept");
|
||||
for(var q=1;q<=S.n;q++)(function(q){
|
||||
ih.insertCell().innerHTML='<b>'+q+'</b>';
|
||||
var sel=h("select",{class:"in",id:"ing"+q});
|
||||
[["","-"],["a",t("v_ing_all")],["u",t("v_untagged")],["t",t("v_tagged")]].forEach(function(o){
|
||||
sel.appendChild(h("option",{value:o[0],text:o[1]}));
|
||||
});
|
||||
ir.insertCell().appendChild(sel);
|
||||
})(q);
|
||||
}
|
||||
|
||||
function segVal(p) {
|
||||
return Number($("vm" + p).querySelector("button.on").getAttribute("data-v"));
|
||||
}
|
||||
|
||||
function segSet(p, v) {
|
||||
$("vm" + p).querySelectorAll("button").forEach(function(b, i) { b.classList.toggle("on", i === v) });
|
||||
}
|
||||
function vlanLoad(){
|
||||
var vid=parseInt($("vvid").value,10);
|
||||
if(!vid||vid<1||vid>4094){toast(t("v_vid_err"),"err");return;}
|
||||
|
||||
function vlanLoad(vid) {
|
||||
if (!vid || vid < 1 || vid > 4094) {
|
||||
toast(t("v_vid_err"), "err");
|
||||
return;
|
||||
}
|
||||
getJSON("/vlan.json?vid=" + vid).then(function(d) {
|
||||
$("vname").value=d.name||"";
|
||||
var m=parseInt(d.members,16),mem=m&0x3ff,unt=((m>>10)&0x3ff)&mem;
|
||||
$("vtitle").innerText = "VLAN " + vid + (d.name ? " (" + (d.name) + ")" : "");
|
||||
$("vtitle").dataset.id = vid;
|
||||
$("vtitle").dataset.name = d.name;
|
||||
var m = parseInt(d.members, 16),
|
||||
mem = m & 0x3ff,
|
||||
unt = ((m >> 10) & 0x3ff) & mem;
|
||||
var pv = parseInt(d.pvid, 16) & 0x3ff;
|
||||
for (var p = 1; p <= S.n; p++) {
|
||||
var bit = S.physToLog[p - 1];
|
||||
segSet(p, (mem >> bit) & 1 ? (((unt >> bit) & 1) ? 1 : 2) : 0);
|
||||
$("vp"+p).checked=!!((pv>>bit)&1);
|
||||
$("vp" + p).innerText = (!!((pv >> bit) & 1))?"Y":"N";
|
||||
}
|
||||
$("vmgmtpromote").checked = ($("vmgmtcur").dataset.id == vid);
|
||||
toast(t("v_loaded", {n: vid}), "ok");
|
||||
for(const vlanRow of $("vtable").tBodies[0].rows) {
|
||||
vlanRow.className=vlanRow.firstChild.firstChild.dataset.id == vid ? "sel":"";
|
||||
}
|
||||
}).catch(function() {toast(t("v_notfound", {n: vid}), "err")});
|
||||
}
|
||||
|
||||
function vlan2str(i, n, t, u){
|
||||
return ("vlan " + i + (n ? " " + n : "") + " " + t.map((x)=>x+"t").concat(u).join(" ")).trim();
|
||||
}
|
||||
function pvidcmd(i, p){
|
||||
return "pvid " + p + " " + i;
|
||||
}
|
||||
function mySplit(a){
|
||||
if(a.trim()=="") return [];
|
||||
return a.trim().split(",");
|
||||
}
|
||||
function vlanApply() {
|
||||
var vid=parseInt($("vvid").value,10);
|
||||
if(!vid||vid<1||vid>4094){toast(t("v_vid_err"),"err");return;}
|
||||
var name=$("vname").value.trim();
|
||||
if(name&&!/^[a-zA-Z]\w*$/.test(name)){toast(t("v_name_err"),"err");return;}
|
||||
var cmd="vlan "+vid+(name?" "+name:""),members=0;
|
||||
var vid = $("vtitle").dataset.id;
|
||||
var vname = $("vtitle").dataset.name;
|
||||
if (!vid || vid < 1 || vid > 4094) {
|
||||
toast(t("v_vid_err"), "err");
|
||||
return;
|
||||
}
|
||||
var top_cmds = [], tail_cmds = [], vlansPerPort=[], thisVlanInStructure={id:vid, tagged:[], untagged:[]};
|
||||
for (var p = 1; p <= S.n; p++) {
|
||||
var v = segVal(p);
|
||||
if(v===1){cmd+=" "+p;members++;}
|
||||
else if(v===2){cmd+=" "+p+"t";members++;}
|
||||
vlansPerPort[p]={t:[],u:[]};
|
||||
if (v === 1) {
|
||||
thisVlanInStructure.untagged.push(p);
|
||||
vlansPerPort[p].u.push(vid);
|
||||
} else if (v === 2) {
|
||||
thisVlanInStructure.tagged.push(p);
|
||||
vlansPerPort[p].t.push(vid);
|
||||
}
|
||||
if(!members){toast(t("v_nomember"),"err");return;}
|
||||
var cmds=[cmd];
|
||||
for(var q=1;q<=S.n;q++)if($("vp"+q).checked)cmds.push("pvid "+q+" "+vid);
|
||||
postCmds(cmds).then(vlanRefresh).catch(function(){});
|
||||
}
|
||||
function ingressApply(){
|
||||
var cmd="ingress",any=false;
|
||||
for(var p=1;p<=S.n;p++){
|
||||
var v=$("ing"+p).value;
|
||||
if(v){cmd+=" "+p+v;any=true;}
|
||||
if (thisVlanInStructure.tagged.length + thisVlanInStructure.untagged.length == 0) {
|
||||
toast(t("v_nomember"), "err");
|
||||
return;
|
||||
}
|
||||
if(!any){toast(t("v_ing_none"),"err");return;}
|
||||
postCmd(cmd).catch(function(){});
|
||||
for(const vlanRow of $("vtable").tBodies[0].rows){
|
||||
var rowData = vlanRow.firstChild.firstChild.dataset,
|
||||
rowUntagged = mySplit(rowData.u),
|
||||
rowTagged = mySplit(rowData.t);
|
||||
if(rowData.id == vid) continue;
|
||||
for(const newVlanPort of thisVlanInStructure.untagged) {
|
||||
if(rowUntagged.includes(""+newVlanPort)) rowUntagged.splice(rowUntagged.indexOf(""+newVlanPort),1);
|
||||
}
|
||||
$("vload").addEventListener("click",vlanLoad);
|
||||
$("vapply").addEventListener("click",vlanApply);
|
||||
$("ingapply").addEventListener("click",ingressApply);
|
||||
$("vmgmt").addEventListener("click",function(){
|
||||
var vid=parseInt($("vvid").value,10);
|
||||
if(!vid){toast(t("v_vid_first"),"err");return;}
|
||||
confirmModal(t("v_mgmt_q",{n:vid}),t("v_mgmt_d"),
|
||||
function(){postCmd("vlan "+vid+" mgmt").then(vlanRefresh).catch(function(){})});
|
||||
});
|
||||
tabHooks.vlan={
|
||||
enter:function(){needPorts(function(){buildVlanEdit();vlanRefresh().catch(function(){})})},
|
||||
};
|
||||
for(const rut of rowUntagged) {
|
||||
vlansPerPort[rut].u.push(rowData.id);
|
||||
}
|
||||
for(const rt of rowTagged) {
|
||||
vlansPerPort[rt].t.push(rowData.id)
|
||||
}
|
||||
top_cmds.push(vlan2str(rowData.id,rowData.name,rowTagged,rowUntagged));
|
||||
}
|
||||
tail_cmds.push(vlan2str(vid, vname, thisVlanInStructure.tagged, thisVlanInStructure.untagged));
|
||||
|
||||
for (p in vlansPerPort){/*park all ports with only tagged membership*/
|
||||
if (vlansPerPort[p].u.length == 0 && vlansPerPort[p].t.length > 0) {
|
||||
tail_cmds.push(pvidcmd(0, p));
|
||||
}
|
||||
}
|
||||
for(const up of thisVlanInStructure.untagged){
|
||||
tail_cmds.push(pvidcmd(vid,up));
|
||||
}
|
||||
if($("vmgmtpromote").checked){/*must be the last command*/
|
||||
tail_cmds.push("vlan " + vid + " mgmt");
|
||||
}
|
||||
var cmd = top_cmds.concat(tail_cmds);
|
||||
postCmd(cmd.join("\n"),true).then(function(){toast(t("t_cmds",{n:cmd.length}),"ok")}).then(vlanRefresh).catch(function() {}).then(function() {
|
||||
vlanLoad(vid)
|
||||
});
|
||||
}
|
||||
$("vapply").addEventListener("click", vlanApply);
|
||||
$("vlan_add").addEventListener("click", function() {
|
||||
var body = h("div");
|
||||
body.appendChild(h("p", null, [
|
||||
h("label", {class: "row"}, [
|
||||
h("span",{class:"lb", "data-i18n":"v_vid"},[document.createTextNode(t("v_vid"))]),
|
||||
h("input", {id: "nvlid", class: "in sm", type: "number", min: "1", max: "4094"})
|
||||
])
|
||||
]));
|
||||
body.appendChild(h("p", null, [
|
||||
h("label", {class: "row"}, [
|
||||
h("span",{class:"lb", "data-i18n":"c_name"},[document.createTextNode(t("c_name"))]),
|
||||
h("input", {id: "nvlname", class: "in", size: "12", maxlength: "15", "data-i18n-p": "v_optional"})
|
||||
])
|
||||
]));
|
||||
modal("Add VLAN", body, [h("button", {
|
||||
class: "ctl pri", text: t("v_add"),
|
||||
onclick: addVlan
|
||||
})]);
|
||||
});
|
||||
function validVlanName(n){
|
||||
if (n && (!/^[a-zA-Z]\w*$/.test(n) || n == "d" || n == "mgmt")) {
|
||||
toast(t("v_name_err"), "err");
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
function addVlan() {
|
||||
var vid = parseInt($("nvlid").value, 10);
|
||||
for (const r of $("vtable").tBodies[0].rows) {
|
||||
if (r.firstChild.firstChild.dataset.id == vid) {
|
||||
toast(t("v_vid_err"), "err");
|
||||
return;
|
||||
}
|
||||
}
|
||||
var name = $("nvlname").value;
|
||||
if(!validVlanName(name)) return false;
|
||||
var cmd = "vlan " + vid + (name ? " " + name : "");
|
||||
postCmd(cmd).then(function() {
|
||||
closeModal();
|
||||
vlanRefresh().then(function(){vlanLoad(vid)});
|
||||
}).catch(function() {});
|
||||
}
|
||||
tabHooks.vlan = {
|
||||
enter: function() {
|
||||
needPorts(function() {
|
||||
buildVlanEdit();
|
||||
vlanRefresh().catch(function() {}).then(function() {
|
||||
VlanInit();
|
||||
})
|
||||
})
|
||||
},
|
||||
};
|
||||
function VlanInit() {
|
||||
vlanLoad($("vtable").tBodies[0].firstChild.firstChild.firstChild.dataset.id)
|
||||
};
|
||||
var l2Rows=[],l2SortCol="pport",l2SortDir=1,l2Gen=0;
|
||||
function l2Load(){
|
||||
var seen={},all=[],idx=0,guard=0,gen=++l2Gen;
|
||||
|
||||
@@ -93,6 +93,7 @@ table.t th.num{text-align:right}
|
||||
table.t td{padding:6px 8px;border-bottom:1px solid var(--bd);vertical-align:middle}
|
||||
table.t tr:last-child td{border-bottom:0}
|
||||
table.t tr:hover td{background:var(--sel)}
|
||||
table.t tr.sel{background:var(--panel2); font-weight: bold;}
|
||||
.num{font-variant-numeric:tabular-nums;text-align:right}
|
||||
.scrollx{overflow-x:auto}
|
||||
input.in,select.in,textarea.in{
|
||||
@@ -124,12 +125,20 @@ label.row span.lb{width:110px;flex:none;color:var(--fg2);font-size:13px}
|
||||
.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:16px;height:10px;margin:2px auto 1px;border-radius:2px;
|
||||
border:2px solid var(--fg2);border-top-width:4px;opacity:.55;
|
||||
display:block;width:21px;height:12px;margin:4px auto 2px;border-radius:2px;
|
||||
border:2px solid var(--fg2);border-top-width:8px;opacity:.55;position:relative;
|
||||
}
|
||||
.port.sfp .jack{width:24px;height:9px;border-top-width:3px}
|
||||
.port .jack:before{content:"";
|
||||
position:absolute;bottom:100%;left:50%;transform:translateX(-50%);
|
||||
width:6px;height:5px;margin-bottom:7px;border-radius: 1px 1px 0 0;
|
||||
background-color:var(--fg2);
|
||||
}
|
||||
.port.sfp .jack{width:24px;height:16px;margin:0 auto 2px;border-top-width:5px}
|
||||
.port.sfp .jack:before{width:10px;margin-bottom:2px;border-radius:0 0 1px 1px;background-color:var(--panel2)}
|
||||
.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 .jack:before{background-color:var(--pc)}
|
||||
.port.up.sfp .jack:before{background-color:color-mix(in srgb,var(--pc) 10%,var(--panel2))}
|
||||
.port.up .ps{color:var(--pc);font-weight:600}
|
||||
.port.dis{opacity:.45;border-style:dashed}
|
||||
.badge{font-size:11px;font-weight:600;border-radius:10px;padding:1px 8px;background:var(--panel2);color:var(--fg2)}
|
||||
@@ -255,6 +264,8 @@ button.danger{background:var(--bad);color:#fff;border-color:transparent}
|
||||
<p class="small mono" id="stpids">-</p>
|
||||
<div id="stpbridge" style="display:flex;gap:14px;flex-wrap:wrap;align-items:flex-end;margin-top:10px">
|
||||
<label><span class="small mut" data-i18n="stp_version">Version</span><br><select class="in" id="stpver"><option value="rstp">RSTP</option><option value="stp" data-i18n="stp_v_stp">STP compatible</option></select></label>
|
||||
<label><span class="small mut" data-i18n="stp_pcs">Path cost</span><br><select class="in" id="stppcs"><option value="long" data-i18n="stp_pcs_long">Long (32 bit)</option><option value="short" data-i18n="stp_pcs_short">Short (16 bit)</option></select></label>
|
||||
<label><span class="small mut" data-i18n="stp_bh">BPDUs while disabled</span><br><select class="in" id="stpbh"><option value="flood" data-i18n="stp_bh_flood">Flood</option><option value="filter" data-i18n="stp_bh_filter">Filter</option></select></label>
|
||||
<label><span class="small mut" data-i18n="stp_prio">Bridge priority</span><br><select class="in" id="stpprio"></select></label>
|
||||
<label><span class="small mut" data-i18n="stp_hello">Hello [s]</span><br><input class="in sm" id="stphello" type="number" min="1" max="10"></label>
|
||||
<label><span class="small mut" data-i18n="stp_maxage">Max age [s]</span><br><input class="in sm" id="stpmaxage" type="number" min="6" max="40"></label>
|
||||
@@ -272,9 +283,14 @@ button.danger{background:var(--bad);color:#fff;border-color:transparent}
|
||||
</div>
|
||||
<div class="card"><h2 data-i18n="stp_status">Port status</h2>
|
||||
<div class="scrollx"><table class="t" id="stpstat">
|
||||
<thead><tr><th data-i18n="c_port">Port</th><th data-i18n="stp_role">Role</th><th data-i18n="c_state">State</th><th data-i18n="stp_db">Designated bridge</th><th data-i18n="stp_dp">Designated port</th><th data-i18n="stp_dc">Designated cost</th><th data-i18n="stp_oedge">Oper. edge</th><th data-i18n="stp_op2p">Oper. P2P</th></tr></thead>
|
||||
<thead><tr><th data-i18n="c_port">Port</th><th data-i18n="stp_role">Role</th><th data-i18n="c_state">State</th><th data-i18n="stp_db">Designated bridge</th><th data-i18n="stp_dp">Designated port</th><th data-i18n="stp_dc">Designated cost</th><th data-i18n="stp_oedge">Oper. edge</th><th data-i18n="stp_op2p">Oper. P2P</th><th data-i18n="stp_proto">Protocol</th><th></th></tr></thead>
|
||||
<tbody></tbody></table></div>
|
||||
</div>
|
||||
<div class="card"><details id="stpcntd"><summary><h2 style="display:inline" data-i18n="stp_cnt">Counters</h2></summary>
|
||||
<div class="scrollx"><table class="t" id="stpcnt">
|
||||
<thead><tr><th data-i18n="c_port">Port</th><th data-i18n="stp_crx">BPDUs received</th><th data-i18n="stp_ctx">BPDUs sent</th><th data-i18n="stp_ctrx">TC received</th><th data-i18n="stp_cttx">TC sent</th><th data-i18n="stp_cage">Last BPDU [s]</th></tr></thead>
|
||||
<tbody></tbody></table></div></details>
|
||||
</div>
|
||||
</section>
|
||||
|
||||
<section class="tab" id="tab-stats">
|
||||
@@ -286,27 +302,21 @@ button.danger{background:var(--bad);color:#fff;border-color:transparent}
|
||||
</section>
|
||||
|
||||
<section class="tab" id="tab-vlan">
|
||||
<div class="card"><h2><span data-i18n="v_title">VLANs</span> <span class="hint"><span data-i18n="v_mgmt">Management VLAN</span>: <span id="vmgmtcur">-</span></span></h2>
|
||||
<div class="card"><h2><span data-i18n="v_title">VLANs</span><span id="vmgmtcur"></span></h2>
|
||||
<div class="scrollx"><table class="t" id="vtable">
|
||||
<thead><tr><th data-i18n="v_vid">VID</th><th data-i18n="c_name">Name</th><th data-i18n="v_members">Members</th><th data-i18n="v_tagged">Tagged</th><th data-i18n="v_untagged">Untagged</th><th data-i18n="v_pvid_on">PVID on</th><th></th></tr></thead>
|
||||
<tbody></tbody></table></div>
|
||||
<p class="small mut" id="vempty" style="display:none;margin-top:6px" data-i18n="v_empty">No VLANs configured.</p>
|
||||
<button class="ctl pri" id="vlan_add" data-i18n="v_add">Add a new VLAN</button>
|
||||
</div>
|
||||
<div class="card"><h2 data-i18n="v_editor">VLAN editor</h2>
|
||||
<div style="display:flex;gap:10px;flex-wrap:wrap;align-items:center;margin-bottom:10px">
|
||||
<label><span data-i18n="v_vid">VID</span> <input class="in sm" id="vvid" type="number" min="1" max="4094"></label>
|
||||
<label><span data-i18n="c_name">Name</span> <input class="in" id="vname" size="12" maxlength="15" data-i18n-p="v_optional"></label>
|
||||
<button class="ctl" id="vload" data-i18n="c_load">Load</button>
|
||||
<button class="ctl pri" id="vapply" data-i18n="c_apply">Apply</button>
|
||||
<button class="ctl" id="vmgmt" data-i18n="v_setmgmt" data-i18n-t="v_setmgmt_t">Set as mgmt VLAN</button>
|
||||
</div>
|
||||
<div class="card"><h2><span data-i18n="v_editor">VLAN editor</span> <span class="hint" id="vtitle"></span></h2>
|
||||
<div><label class="row"><span class="lb" style="width:150px;" data-i18n="v_setmgmt_t"></span>
|
||||
<span class="switch"><input type="checkbox" id="vmgmtpromote"><i></i></span>
|
||||
</label></div>
|
||||
<div class="scrollx"><table class="t" id="vedit"><tbody></tbody></table></div>
|
||||
<div style="margin-top:10px"><button class="ctl pri" id="vapply" data-i18n="c_apply">Apply</button></div>
|
||||
<p class="small mut" style="margin-top:6px" data-i18n="v_legend">U = untagged member, T = tagged member, - = not a member. PVID assigns this VLAN to untagged ingress traffic.</p>
|
||||
</div>
|
||||
<div class="card"><h2><span data-i18n="v_ingress">Ingress filtering</span> <span class="hint" data-i18n="v_ingress_h">write-only: the state is not readable from the switch</span></h2>
|
||||
<div class="scrollx"><table class="t" id="ingress"><tbody></tbody></table></div>
|
||||
<div style="margin-top:10px"><button class="ctl pri" id="ingapply" data-i18n="v_ing_apply">Apply ingress modes</button></div>
|
||||
</div>
|
||||
</section>
|
||||
|
||||
<section class="tab" id="tab-l2">
|
||||
|
||||
@@ -1055,6 +1055,8 @@ void httpd_appcall(void)
|
||||
send_lldp();
|
||||
} else if (is_word(q, "/lacp.json")) {
|
||||
send_lacp();
|
||||
} else if (is_word(q, "/stpcnt.json")) {
|
||||
send_stp_counters();
|
||||
} else if (is_word(q, "/vlanlist")) {
|
||||
send_vlanlist();
|
||||
} else if (is_word(q, "/config")) {
|
||||
|
||||
@@ -600,143 +600,6 @@ void send_lag(void)
|
||||
}
|
||||
|
||||
|
||||
static __xdata uint32_t pi_u32;
|
||||
static __xdata uint8_t pi_prio, pi_ext;
|
||||
static __xdata uint8_t * __xdata pi_mac;
|
||||
|
||||
|
||||
static void u32hex_html(void)
|
||||
{
|
||||
__xdata uint8_t *b = (__xdata uint8_t *)&pi_u32;
|
||||
byte_to_html(b[3]);
|
||||
byte_to_html(b[2]);
|
||||
byte_to_html(b[1]);
|
||||
byte_to_html(b[0]);
|
||||
}
|
||||
|
||||
|
||||
static void bridge_to_html(void)
|
||||
{
|
||||
byte_to_html(pi_prio);
|
||||
byte_to_html(pi_ext);
|
||||
for (uint8_t i = 0; i < 6; i++)
|
||||
byte_to_html(pi_mac[i]);
|
||||
}
|
||||
|
||||
void send_stp(void)
|
||||
{
|
||||
uint8_t i, j, st, dsg;
|
||||
|
||||
dbg_string("send_stp called\n");
|
||||
slen = strtox(outbuf, HTTP_RESPONCE_JSON);
|
||||
|
||||
slen += strtox(outbuf + slen, "{\"on\":");
|
||||
bool_to_html(stp_enabled);
|
||||
slen += strtox(outbuf + slen, ",\"rstp\":");
|
||||
bool_to_html(stp_rstp);
|
||||
slen += strtox(outbuf + slen, ",\"prio\":");
|
||||
itoa_html(stp_prio >> 4);
|
||||
slen += strtox(outbuf + slen, ",\"hello\":");
|
||||
itoa_html(stp_hello_s);
|
||||
slen += strtox(outbuf + slen, ",\"maxage\":");
|
||||
itoa_html(stp_maxage_s);
|
||||
slen += strtox(outbuf + slen, ",\"fwd\":");
|
||||
itoa_html(stp_fwddelay_s);
|
||||
slen += strtox(outbuf + slen, ",\"txhold\":");
|
||||
itoa_html(stp_txhold);
|
||||
slen += strtox(outbuf + slen, ",\"rootPrio\":\"");
|
||||
byte_to_html(root_bridge.prio);
|
||||
byte_to_html(root_bridge.ext);
|
||||
slen += strtox(outbuf + slen, "\",\"rootMac\":\"");
|
||||
for (j = 0; j < 6; j++)
|
||||
byte_to_html(root_bridge.mac[j]);
|
||||
slen += strtox(outbuf + slen, "\",\"myMac\":\"");
|
||||
for (j = 0; j < 6; j++)
|
||||
byte_to_html(uip_ethaddr.addr[j]);
|
||||
slen += strtox(outbuf + slen, "\",\"cost\":\"");
|
||||
byte_to_html(root_bridge_cost >> 24);
|
||||
byte_to_html(root_bridge_cost >> 16);
|
||||
byte_to_html(root_bridge_cost >> 8);
|
||||
byte_to_html(root_bridge_cost);
|
||||
slen += strtox(outbuf + slen, "\",\"weRoot\":");
|
||||
bool_to_html(stp_root_port == 0xff ? 1 : 0);
|
||||
slen += strtox(outbuf + slen, ",\"rootPort\":");
|
||||
j = stp_root_port;
|
||||
if (j != 0xff && j >= STP_LAG_BASE) {
|
||||
j = 0;
|
||||
while (j < STP_LAG_BASE && !((stp_lag_mask[stp_root_port - STP_LAG_BASE] >> j) & 1))
|
||||
j++;
|
||||
if (j >= STP_LAG_BASE)
|
||||
j = 0;
|
||||
}
|
||||
itoa_html(j == 0xff ? 0 : machine.log_to_phys_port[j]);
|
||||
slen += strtox(outbuf + slen, ",\"tc\":\"");
|
||||
byte_to_html(stp_tc_count >> 8);
|
||||
byte_to_html(stp_tc_count);
|
||||
slen += strtox(outbuf + slen, "\",\"ports\":[");
|
||||
reg_read_m(RTL837X_MSTP_STATES);
|
||||
for (i = 0; i < STP_ENTITIES; i++) {
|
||||
if (i < STP_LAG_BASE && !MACHINE_PORT_VALID(i))
|
||||
continue;
|
||||
if (i < STP_LAG_BASE && stp_ent_of[i] != i)
|
||||
continue;
|
||||
if (i >= STP_LAG_BASE && !stp_lag_mask[i - STP_LAG_BASE])
|
||||
continue;
|
||||
slen += strtox(outbuf + slen, "{\"p\":");
|
||||
itoa_html(i < STP_LAG_BASE ? machine.log_to_phys_port[i] : 0);
|
||||
slen += strtox(outbuf + slen, ",\"lag\":");
|
||||
itoa_html(i < STP_LAG_BASE ? 0 : i - STP_LAG_BASE + 1);
|
||||
slen += strtox(outbuf + slen, ",\"mbr\":");
|
||||
itoa16_html(i < STP_LAG_BASE ? 0 : stp_lag_mask[i - STP_LAG_BASE]);
|
||||
slen += strtox(outbuf + slen, ",\"st\":");
|
||||
j = i;
|
||||
if (i >= STP_LAG_BASE) {
|
||||
j = 0;
|
||||
while (j < STP_LAG_BASE && !((stp_lag_mask[i - STP_LAG_BASE] >> j) & 1))
|
||||
j++;
|
||||
if (j >= STP_LAG_BASE)
|
||||
j = 0;
|
||||
}
|
||||
st = (sfr_data[3 - (j >> 2)] >> ((j << 1) & 0x7)) & 0x3;
|
||||
itoa_html(st);
|
||||
slen += strtox(outbuf + slen, ",\"role\":");
|
||||
if (!(stp_pflags[i] & STP_PF_ENABLED) || (stp_pflags[i] & STP_PF_TRIPPED))
|
||||
itoa_html(0);
|
||||
else if (i == stp_root_port)
|
||||
itoa_html(1);
|
||||
else if (st == 3)
|
||||
itoa_html(2);
|
||||
else
|
||||
itoa_html(3);
|
||||
slen += strtox(outbuf + slen, ",\"f\":");
|
||||
itoa_html(stp_pflags[i]);
|
||||
slen += strtox(outbuf + slen, ",\"pc\":\"");
|
||||
pi_u32 = stp_pcost[i]; u32hex_html();
|
||||
slen += strtox(outbuf + slen, "\",\"prio\":");
|
||||
itoa_html(stp_pprio[i]);
|
||||
slen += strtox(outbuf + slen, ",\"p2\":");
|
||||
itoa_html(stp_pp2p[i]);
|
||||
dsg = stp_dpid[i] && stp_bpdu_age[i] < (uint16_t)stp_maxage_s * STP_HZ;
|
||||
slen += strtox(outbuf + slen, ",\"db\":\"");
|
||||
if (dsg) {
|
||||
pi_prio = stp_dbridge[i].prio; pi_ext = stp_dbridge[i].ext;
|
||||
pi_mac = stp_dbridge[i].mac;
|
||||
} else {
|
||||
pi_prio = stp_prio; pi_ext = 0;
|
||||
pi_mac = uip_ethaddr.addr;
|
||||
}
|
||||
bridge_to_html();
|
||||
slen += strtox(outbuf + slen, "\",\"dp\":\"");
|
||||
byte_to_html(dsg ? (stp_dpid[i] >> 8) : stp_pprio[i]);
|
||||
byte_to_html(dsg ? stp_dpid[i] : (i + 1));
|
||||
slen += strtox(outbuf + slen, "\",\"dc\":\"");
|
||||
pi_u32 = dsg ? stp_dcost[i] : root_bridge_cost; u32hex_html();
|
||||
slen += strtox(outbuf + slen, "\"},");
|
||||
}
|
||||
slen -= 1; // remove comma
|
||||
slen += strtox(outbuf + slen, "]}");
|
||||
}
|
||||
|
||||
void send_lacp(void)
|
||||
{
|
||||
dbg_string("send_lacp called\n");
|
||||
@@ -810,12 +673,12 @@ void send_lldp(void)
|
||||
|
||||
if (i < MACHINE_MAX_PORT)
|
||||
char_to_html(',');
|
||||
else
|
||||
char_to_html(']');
|
||||
}
|
||||
char_to_html(']');
|
||||
char_to_html('}');
|
||||
}
|
||||
|
||||
|
||||
void send_eee(void)
|
||||
{
|
||||
dbg_string("send_eee called\nsending EEE status\n");
|
||||
@@ -859,9 +722,8 @@ void send_eee(void)
|
||||
char_to_html('}');
|
||||
if (i < MACHINE_MAX_PORT)
|
||||
char_to_html(',');
|
||||
else
|
||||
char_to_html(']');
|
||||
}
|
||||
char_to_html(']');
|
||||
}
|
||||
|
||||
|
||||
@@ -902,9 +764,8 @@ void send_bandwidth(void)
|
||||
char_to_html('}');
|
||||
if (i < MACHINE_MAX_PORT)
|
||||
char_to_html(',');
|
||||
else
|
||||
char_to_html(']');
|
||||
}
|
||||
char_to_html(']');
|
||||
}
|
||||
|
||||
|
||||
@@ -936,9 +797,8 @@ void send_storm(void)
|
||||
char_to_html('}');
|
||||
if (i < MACHINE_MAX_PORT)
|
||||
char_to_html(',');
|
||||
else
|
||||
char_to_html(']');
|
||||
}
|
||||
char_to_html(']');
|
||||
}
|
||||
|
||||
|
||||
@@ -982,9 +842,8 @@ void send_mtu(void)
|
||||
char_to_html('}');
|
||||
if (i < MACHINE_MAX_PORT)
|
||||
char_to_html(',');
|
||||
else
|
||||
char_to_html(']');
|
||||
}
|
||||
char_to_html(']');
|
||||
}
|
||||
|
||||
|
||||
@@ -1117,9 +976,8 @@ void send_status(void)
|
||||
slen += strtox(outbuf + slen, "\"}");
|
||||
if (i < MACHINE_MAX_PORT)
|
||||
char_to_html(',');
|
||||
else
|
||||
char_to_html(']');
|
||||
}
|
||||
char_to_html(']');
|
||||
}
|
||||
|
||||
|
||||
|
||||
@@ -22,9 +22,10 @@ void send_isolation(void);
|
||||
void send_config(void);
|
||||
void send_cmd_log(void);
|
||||
void send_lag(void);
|
||||
void send_stp(void);
|
||||
void send_stp(void) __banked;
|
||||
void send_lldp(void);
|
||||
void send_lacp(void);
|
||||
void send_stp_counters(void) __banked;
|
||||
void send_vlanlist(void);
|
||||
|
||||
/* Convert only the lower nibble to ascii HEX char.
|
||||
|
||||
@@ -0,0 +1,195 @@
|
||||
#include "rtl837x_sfr.h"
|
||||
#include "rtl837x_common.h"
|
||||
#include "rtl837x_regs.h"
|
||||
#include "rtl837x_port.h"
|
||||
#include "uip.h"
|
||||
#include <stdint.h>
|
||||
#include "phy.h"
|
||||
#include "machine.h"
|
||||
#include "rtl837x_stp.h"
|
||||
#include "page_impl.h"
|
||||
#include "debug.h"
|
||||
|
||||
#pragma codeseg BANK4
|
||||
#pragma constseg BANK4
|
||||
|
||||
extern __xdata uint8_t outbuf[TCP_OUTBUF_SIZE];
|
||||
extern __xdata uint16_t slen;
|
||||
|
||||
static __code const uint8_t stp_json_hdr[] = "HTTP/1.1 200 OK\r\nConnection: close\r\nContent-Type: application/json\r\n\r\n";
|
||||
|
||||
static void stp_bool_html(char c)
|
||||
{
|
||||
outbuf[slen++] = c ? '1' : '0';
|
||||
}
|
||||
|
||||
static void stp_byte_html(uint8_t val)
|
||||
{
|
||||
outbuf[slen++] = itohex(val >> 4);
|
||||
outbuf[slen++] = itohex(val);
|
||||
}
|
||||
|
||||
static void stp_itoa_html(uint8_t v)
|
||||
{
|
||||
uint8_t t = v / 100;
|
||||
uint8_t print_zeros = t;
|
||||
if (print_zeros)
|
||||
outbuf[slen++] = '0' + t;
|
||||
t = (v / 10) % 10;
|
||||
print_zeros |= t;
|
||||
if (print_zeros)
|
||||
outbuf[slen++] = '0' + t;
|
||||
outbuf[slen++] = '0' + (v % 10);
|
||||
}
|
||||
|
||||
|
||||
static __xdata uint32_t pi_u32;
|
||||
static __xdata uint8_t pi_prio, pi_ext;
|
||||
static __xdata uint8_t * __xdata pi_mac;
|
||||
|
||||
|
||||
static void u32hex_html(void)
|
||||
{
|
||||
__xdata uint8_t *b = (__xdata uint8_t *)&pi_u32;
|
||||
stp_byte_html(b[3]);
|
||||
stp_byte_html(b[2]);
|
||||
stp_byte_html(b[1]);
|
||||
stp_byte_html(b[0]);
|
||||
}
|
||||
|
||||
|
||||
static void bridge_to_html(void)
|
||||
{
|
||||
stp_byte_html(pi_prio);
|
||||
stp_byte_html(pi_ext);
|
||||
for (uint8_t i = 0; i < 6; i++)
|
||||
stp_byte_html(pi_mac[i]);
|
||||
}
|
||||
|
||||
|
||||
void send_stp(void) __banked
|
||||
{
|
||||
static __xdata uint8_t i, j, st, dsg;
|
||||
|
||||
dbg_string("send_stp called\n");
|
||||
slen = strtox(outbuf, stp_json_hdr);
|
||||
|
||||
slen += strtox(outbuf + slen, "{\"on\":");
|
||||
stp_bool_html(stp_enabled);
|
||||
slen += strtox(outbuf + slen, ",\"rstp\":");
|
||||
stp_bool_html(stp_rstp);
|
||||
slen += strtox(outbuf + slen, ",\"prio\":");
|
||||
stp_itoa_html(stp_prio >> 4);
|
||||
slen += strtox(outbuf + slen, ",\"hello\":");
|
||||
stp_itoa_html(stp_hello_s);
|
||||
slen += strtox(outbuf + slen, ",\"maxage\":");
|
||||
stp_itoa_html(stp_maxage_s);
|
||||
slen += strtox(outbuf + slen, ",\"fwd\":");
|
||||
stp_itoa_html(stp_fwddelay_s);
|
||||
slen += strtox(outbuf + slen, ",\"txhold\":");
|
||||
stp_itoa_html(stp_txhold);
|
||||
slen += strtox(outbuf + slen, ",\"pcs\":");
|
||||
stp_itoa_html(stp_pcost_short);
|
||||
slen += strtox(outbuf + slen, ",\"bh\":");
|
||||
stp_itoa_html(stp_bpdu_filter);
|
||||
slen += strtox(outbuf + slen, ",\"tcs\":\"");
|
||||
pi_u32 = stp_tc_secs; u32hex_html();
|
||||
slen += strtox(outbuf + slen, "\",\"rMaxage\":");
|
||||
stp_itoa_html(stp_root_port == 0xff ? stp_maxage_s : stp_root_maxage);
|
||||
slen += strtox(outbuf + slen, ",\"rFwd\":");
|
||||
stp_itoa_html(stp_root_port == 0xff ? stp_fwddelay_s : stp_root_fwd);
|
||||
slen += strtox(outbuf + slen, ",\"rootPrio\":\"");
|
||||
stp_byte_html(root_bridge.prio);
|
||||
stp_byte_html(root_bridge.ext);
|
||||
slen += strtox(outbuf + slen, "\",\"rootMac\":\"");
|
||||
for (j = 0; j < 6; j++)
|
||||
stp_byte_html(root_bridge.mac[j]);
|
||||
slen += strtox(outbuf + slen, "\",\"myMac\":\"");
|
||||
for (j = 0; j < 6; j++)
|
||||
stp_byte_html(uip_ethaddr.addr[j]);
|
||||
slen += strtox(outbuf + slen, "\",\"cost\":\"");
|
||||
stp_byte_html(root_bridge_cost >> 24);
|
||||
stp_byte_html(root_bridge_cost >> 16);
|
||||
stp_byte_html(root_bridge_cost >> 8);
|
||||
stp_byte_html(root_bridge_cost);
|
||||
slen += strtox(outbuf + slen, "\",\"weRoot\":");
|
||||
stp_bool_html(stp_root_port == 0xff ? 1 : 0);
|
||||
slen += strtox(outbuf + slen, ",\"tc\":\"");
|
||||
stp_byte_html(stp_tc_count >> 8);
|
||||
stp_byte_html(stp_tc_count);
|
||||
slen += strtox(outbuf + slen, "\",\"ports\":[");
|
||||
for (i = 0; i < STP_ENTITIES; i++) {
|
||||
j = stp_ent_id(i);
|
||||
if (!j)
|
||||
continue;
|
||||
slen += strtox(outbuf + slen, "{\"p\":");
|
||||
stp_itoa_html(j);
|
||||
slen += strtox(outbuf + slen, ",\"st\":");
|
||||
st = stp_port_state(i);
|
||||
stp_itoa_html(st);
|
||||
slen += strtox(outbuf + slen, ",\"role\":");
|
||||
stp_itoa_html(stp_port_role(i));
|
||||
slen += strtox(outbuf + slen, ",\"f\":");
|
||||
stp_itoa_html(stp_pflags[i]);
|
||||
slen += strtox(outbuf + slen, ",\"pc\":\"");
|
||||
pi_u32 = stp_pcost[i]; u32hex_html();
|
||||
slen += strtox(outbuf + slen, "\",\"prio\":");
|
||||
stp_itoa_html(stp_pprio[i]);
|
||||
slen += strtox(outbuf + slen, ",\"p2\":");
|
||||
stp_itoa_html(stp_pp2p[i]);
|
||||
slen += strtox(outbuf + slen, ",\"lk\":");
|
||||
stp_itoa_html((stp_link_prev >> i) & 1);
|
||||
slen += strtox(outbuf + slen, ",\"lg\":");
|
||||
stp_itoa_html((stp_legacy >> i) & 1);
|
||||
dsg = stp_dpid[i] && stp_info_while[i];
|
||||
slen += strtox(outbuf + slen, ",\"db\":\"");
|
||||
if (dsg) {
|
||||
pi_prio = stp_dbridge[i].prio; pi_ext = stp_dbridge[i].ext;
|
||||
pi_mac = stp_dbridge[i].mac;
|
||||
} else {
|
||||
pi_prio = stp_prio; pi_ext = 0;
|
||||
pi_mac = uip_ethaddr.addr;
|
||||
}
|
||||
bridge_to_html();
|
||||
slen += strtox(outbuf + slen, "\",\"dp\":\"");
|
||||
stp_byte_html(dsg ? (stp_dpid[i] >> 8) : stp_pprio[i]);
|
||||
stp_byte_html(dsg ? stp_dpid[i] : j);
|
||||
slen += strtox(outbuf + slen, "\",\"dc\":\"");
|
||||
pi_u32 = dsg ? stp_dcost[i] : root_bridge_cost; u32hex_html();
|
||||
slen += strtox(outbuf + slen, "\"},");
|
||||
}
|
||||
slen -= 1; // remove comma
|
||||
slen += strtox(outbuf + slen, "]}");
|
||||
}
|
||||
|
||||
|
||||
void send_stp_counters(void) __banked
|
||||
{
|
||||
uint8_t i, j;
|
||||
|
||||
slen = strtox(outbuf, stp_json_hdr);
|
||||
slen += strtox(outbuf + slen, "{\"on\":");
|
||||
stp_bool_html(stp_enabled);
|
||||
slen += strtox(outbuf + slen, ",\"hz\":");
|
||||
stp_itoa_html(STP_HZ);
|
||||
slen += strtox(outbuf + slen, ",\"tc\":\"");
|
||||
stp_byte_html(stp_tc_count >> 8);
|
||||
stp_byte_html(stp_tc_count);
|
||||
slen += strtox(outbuf + slen, "\",\"ports\":[");
|
||||
for (i = 0; i < STP_ENTITIES; i++) {
|
||||
j = stp_ent_id(i);
|
||||
if (!j)
|
||||
continue;
|
||||
slen += strtox(outbuf + slen, "{\"p\":");
|
||||
stp_itoa_html(j);
|
||||
slen += strtox(outbuf + slen, ",\"c\":\"");
|
||||
for (j = 0; j < STP_CNT_N; j++) {
|
||||
pi_u32 = stp_cnt[j][i]; u32hex_html();
|
||||
}
|
||||
stp_byte_html(stp_bpdu_age[i] >> 8);
|
||||
stp_byte_html(stp_bpdu_age[i]);
|
||||
slen += strtox(outbuf + slen, "\"},");
|
||||
}
|
||||
slen -= 1;
|
||||
slen += strtox(outbuf + slen, "]}");
|
||||
}
|
||||
@@ -1,9 +1,14 @@
|
||||
CODE_LOCATION=0x1000
|
||||
INSTALLER_ADDRESS=0x1100
|
||||
|
||||
CC = sdcc
|
||||
SDCC_PREFIX = ../toolchain/sdcc-$(shell cat ../sdcc/* | cksum | cut -d' ' -f1)
|
||||
ifeq ($(origin CC),default)
|
||||
SDCC = $(SDCC_PREFIX)/bin/sdcc
|
||||
CC = $(SDCC)
|
||||
ASM = $(SDCC_PREFIX)/bin/sdas8051
|
||||
endif
|
||||
CC_FLAGS = -mmcs51
|
||||
ASM = sdas8051
|
||||
ASM ?= sdas8051
|
||||
AFLAGS= -plosgff
|
||||
|
||||
BUILDDIR = output
|
||||
@@ -13,9 +18,14 @@ OBJS = ${SRCS:%.c=$(BUILDDIR)/%.rel}
|
||||
|
||||
all: create_build_dir $(BUILDDIR)/updatebuilder $(BUILDDIR)/rtlplayground_oem_upgrade.bin
|
||||
|
||||
create_build_dir:
|
||||
create_build_dir: $(SDCC)
|
||||
mkdir -p $(BUILDDIR)
|
||||
|
||||
ifneq ($(SDCC),)
|
||||
$(SDCC):
|
||||
sh ../sdcc/build.sh $(abspath $(SDCC_PREFIX))
|
||||
endif
|
||||
|
||||
$(BUILDDIR)/updatebuilder: updatebuilder.c
|
||||
gcc $^ -o $@
|
||||
|
||||
|
||||
@@ -348,7 +348,7 @@ void installer(void)
|
||||
{
|
||||
|
||||
CKCON = 0; // Initial Clock configuration
|
||||
SFR_97 = 0; // HADDR?
|
||||
MAP_FLASH_PAGE_TO_XDATA = 0; // HADDR?
|
||||
|
||||
// Set in managed mode:
|
||||
SFR_b9 = 0x00;
|
||||
|
||||
@@ -2,6 +2,7 @@
|
||||
|
||||
__code const struct machine machine = {
|
||||
MACHINE_COMMON_SETTING,
|
||||
.mac_flash_offset = 0x1FC000,
|
||||
.n_sfp = 2,
|
||||
.log_to_phys_port = {0, 0, 0, 5, 1, 2, 3, 4, 6},
|
||||
.phys_to_log_port = {4, 5, 6, 7, 3, 8, 0, 0, 0},
|
||||
@@ -21,7 +22,7 @@ __code const struct machine machine = {
|
||||
.sfp_port[1].sds = 1,
|
||||
.sfp_port[1].i2c = I2CBUS( GPIO39_I2C_SDA4, GPIO40_I2C_SCL3_MDC1 ),
|
||||
|
||||
.reset_pin = GPIO_NA,
|
||||
.reset_pin = GPIO48_I2C_SCL1,
|
||||
.high_leds = { .mux = LED_28_SYS | LED_29, .enable = LED_27 | LED_28_SYS | LED_29 },
|
||||
.port_led_set = { 0, 0, 0, 1, 0, 0, 0, 0, 1},
|
||||
.led_sets = {
|
||||
|
||||
@@ -0,0 +1,54 @@
|
||||
#include "machine_defs.h"
|
||||
|
||||
__code const struct machine machine = {
|
||||
MACHINE_COMMON_SETTING,
|
||||
.n_sfp = 2,
|
||||
.log_to_phys_port = {0, 0, 0, 5, 1, 2, 3, 4, 6},
|
||||
.phys_to_log_port = {4, 5, 6, 7, 3, 8, 0, 0, 0},
|
||||
.is_sfp = {0, 0, 0, 1, 0, 0, 0, 0, 2},
|
||||
|
||||
// SFP port on SDS0 / logical port 3
|
||||
.sfp_port[0].pin_detect = GPIO37,
|
||||
.sfp_port[0].pin_los = GPIO_NA,
|
||||
.sfp_port[0].pin_tx_disable = GPIO_NA,
|
||||
.sfp_port[0].sds = 0,
|
||||
.sfp_port[0].i2c = I2CBUS( GPIO41_I2C_SDA3_MDIO1, GPIO40_I2C_SCL3_MDC1 ),
|
||||
|
||||
// SFP port on SDS1 / logical port 8
|
||||
.sfp_port[1].pin_detect = GPIO38,
|
||||
.sfp_port[1].pin_los = GPIO_NA,
|
||||
.sfp_port[1].pin_tx_disable = GPIO_NA,
|
||||
.sfp_port[1].sds = 1,
|
||||
.sfp_port[1].i2c = I2CBUS( GPIO39_I2C_SDA4, GPIO40_I2C_SCL3_MDC1 ),
|
||||
|
||||
.reset_pin = GPIO_NA,
|
||||
.high_leds = { .mux = LED_28_SYS | LED_29, .enable = LED_27 | LED_28_SYS | LED_29 },
|
||||
.port_led_set = { 0, 0, 0, 1, 0, 0, 0, 0, 1 },
|
||||
.led_sets = {
|
||||
{
|
||||
LEDS_2G5 | LEDS_LINK | LEDS_ACT,
|
||||
LEDS_1G | LEDS_100M | LEDS_10M | LEDS_LINK | LEDS_ACT,
|
||||
0,
|
||||
0
|
||||
},
|
||||
{
|
||||
LEDS_2G5 | LEDS_1G | LEDS_100M | LEDS_LINK | LEDS_ACT,
|
||||
LEDS_10G | LEDS_LINK | LEDS_ACT,
|
||||
0,
|
||||
0
|
||||
},
|
||||
},
|
||||
|
||||
.led_mux_custom = 1,
|
||||
.led_mux = {
|
||||
0x00, 0x01, 0x04, 0x05, 0x08,
|
||||
0x09, 0x0c, 0x3f, 0x0d, 0x10,
|
||||
0x11, 0x3f, 0x14, 0x3f, 0x3f,
|
||||
0x15, 0x3f, 0x3f, 0x18, 0x3f,
|
||||
0x3f, 0x19, 0x3f, 0x3f, 0x1c,
|
||||
0x1d, 0x20, 0x21
|
||||
},
|
||||
};
|
||||
|
||||
// Boot 1-4G 6Y
|
||||
// 1-4Y 5G 6Y
|
||||
@@ -0,0 +1,7 @@
|
||||
#ifndef _MACHINE_SWTG024AS_V2_1_0_H_
|
||||
#define _MACHINE_SWTG024AS_V2_1_0_H_
|
||||
|
||||
#define MACHINE_MODEL "SWTG024AS-V2.1.0"
|
||||
#define MACHINE_CHIPSET RTL8372
|
||||
|
||||
#endif
|
||||
@@ -0,0 +1,22 @@
|
||||
#include "machine_init_defs.h"
|
||||
|
||||
void machine_custom_init(void) __banked
|
||||
{
|
||||
// Stock LED_MODE: [8:5]=1, [12:9]=2 (leds_setup hard-codes 5/3); same as clearing bits 7 and 9
|
||||
reg_bit_set(RTL837X_REG_LED_MODE, 17);
|
||||
reg_bit_clear(RTL837X_REG_LED_MODE, 9);
|
||||
reg_bit_clear(RTL837X_REG_LED_MODE, 7);
|
||||
|
||||
// Stock LED_GLB_IO_EN [27:0] = 0x0f249740: enable LED pins 6, 8, 9, 10, 12, 15, 18, 21, 24-27
|
||||
reg_bit_set(RTL837X_REG_LED_GLB_IO_EN, 6);
|
||||
reg_bit_set(RTL837X_REG_LED_GLB_IO_EN, 8);
|
||||
reg_bit_set(RTL837X_REG_LED_GLB_IO_EN, 9);
|
||||
reg_bit_set(RTL837X_REG_LED_GLB_IO_EN, 10);
|
||||
reg_bit_set(RTL837X_REG_LED_GLB_IO_EN, 12);
|
||||
reg_bit_set(RTL837X_REG_LED_GLB_IO_EN, 15);
|
||||
reg_bit_set(RTL837X_REG_LED_GLB_IO_EN, 18);
|
||||
reg_bit_set(RTL837X_REG_LED_GLB_IO_EN, 21);
|
||||
reg_bit_set(RTL837X_REG_LED_GLB_IO_EN, 24);
|
||||
reg_bit_set(RTL837X_REG_LED_GLB_IO_EN, 25);
|
||||
reg_bit_set(RTL837X_REG_LED_GLB_IO_EN, 26);
|
||||
}
|
||||
@@ -0,0 +1,53 @@
|
||||
#include "machine_defs.h"
|
||||
|
||||
// Sirivision S25G1109FP V1.3 (label: SR-S25G2109F): 8x2.5G RJ45 + 1x10G SFP+,
|
||||
// 2 MB flash, RTL8373 (non-N) + RTL8224. Derived from the KP-9000-9XHML-X V3.1
|
||||
// definition; every value below was confirmed on hardware, see the device doc
|
||||
// in doc/devices/S25G1109FP_V1_3.md.
|
||||
__code const struct machine machine = {
|
||||
MACHINE_COMMON_SETTING,
|
||||
.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},
|
||||
/* ModAbs is GPIO30 (low with a module inserted, high with an empty cage),
|
||||
* LOS is GPIO37 (high without light). GPIO38, the ModAbs pin of the
|
||||
* KP-9000 8+1 reference, is tied low here and never changes, which made the
|
||||
* firmware believe a module was always present. */
|
||||
.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 button: GPIO54 reads high when idle and low while pressed.
|
||||
* GPIO48, used by the KP-9000 reference, never changes on this board. */
|
||||
.reset_pin = GPIO54_ACL_BIT2_EN,
|
||||
.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: left LED = link/activity, right LED = 2.5G */
|
||||
LEDS_2G5 | LEDS_1G | LEDS_100M | LEDS_10M | LEDS_LINK | LEDS_ACT,
|
||||
/* speed-only conditions leave the LED dark: the LINK bit has to be
|
||||
* set as well, otherwise the LED controller stays off */
|
||||
LEDS_2G5 | LEDS_LINK,
|
||||
0,
|
||||
0,
|
||||
}, { /* SFP PORT, SINGLE GREEN LED */
|
||||
LEDS_2G5 | LEDS_1G | LEDS_100M | LEDS_10M | LEDS_LINK | LEDS_ACT | LEDS_10G,
|
||||
0,
|
||||
0,
|
||||
0,
|
||||
}},
|
||||
/* 6 bits per LED pin, encoded as (port << 2) | led. This is the chip
|
||||
* default for port-ordered wiring (pin 3n+m = port n+1, function m) with
|
||||
* one change: pin 25 drives the SFP LED with function 0, the only function
|
||||
* populated in led_sets[1]. See the device doc for the details. */
|
||||
.led_mux_custom = 1,
|
||||
.led_mux = {0x00, 0x01, 0x02, 0x04, 0x05, 0x06, 0x08, 0x09, 0x0a, 0x0c,
|
||||
0x0d, 0x0e, 0x10, 0x11, 0x12, 0x14, 0x15, 0x16, 0x18, 0x19,
|
||||
0x1a, 0x1c, 0x1d, 0x1e, 0x20, 0x20, 0x22, 0x23},
|
||||
/* Factory MAC at flash 0x1FC000, verified in the stock dump. The stock
|
||||
* value is a locally administered address, which is rejected, so a
|
||||
* UUID-derived MAC is used unless a valid one is stored here. */
|
||||
.mac_flash_offset = 0x1FC000,
|
||||
};
|
||||
@@ -0,0 +1,10 @@
|
||||
#ifndef _MACHINE_S25G1109FP_V1_3_H_
|
||||
#define _MACHINE_S25G1109FP_V1_3_H_
|
||||
|
||||
/* machine_name[] in struct machine is 30 bytes -> keep this <= 29 chars */
|
||||
/* The product is sold as SR-S25G2109F; S25G1109FP is the PCB silkscreen and
|
||||
* belongs to a different (unmanaged PoE) model that shares this PCB. */
|
||||
#define MACHINE_MODEL "Sirivision SR-S25G2109F"
|
||||
#define MACHINE_CHIPSET RTL8373
|
||||
|
||||
#endif
|
||||
@@ -2,6 +2,7 @@
|
||||
|
||||
__code const struct machine machine = {
|
||||
MACHINE_COMMON_SETTING,
|
||||
.mac_flash_offset = 0x1FC000,
|
||||
.n_sfp = 2,
|
||||
.log_to_phys_port = {0, 0, 0, 6, 1, 2, 3, 4, 5},
|
||||
.phys_to_log_port = {4, 5, 6, 7, 8, 3, 0, 0, 0},
|
||||
|
||||
@@ -2,6 +2,7 @@
|
||||
|
||||
__code const struct machine machine = {
|
||||
MACHINE_COMMON_SETTING,
|
||||
.mac_flash_offset = 0x1FC000,
|
||||
.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},
|
||||
|
||||
@@ -0,0 +1,41 @@
|
||||
#include "machine_defs.h"
|
||||
__code const struct machine machine = {
|
||||
MACHINE_COMMON_SETTING,
|
||||
.n_sfp = 2,
|
||||
.log_to_phys_port = {0, 0, 0, 5, 1, 2, 3, 4, 6},
|
||||
.phys_to_log_port = {4, 5, 6, 7, 3, 8, 0, 0, 0},
|
||||
.is_sfp = {0, 0, 0, 1, 0, 0, 0, 0, 2},
|
||||
// Left SFP port (5)
|
||||
.sfp_port[0].pin_detect = GPIO30_ACL_BIT3_EN,
|
||||
.sfp_port[0].pin_los = GPIO_NA,
|
||||
.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 ),
|
||||
// Right SFP port (6)
|
||||
.sfp_port[1].pin_detect = GPIO50_I2C_SCL2_UART1_TX,
|
||||
.sfp_port[1].pin_los = GPIO_NA,
|
||||
.sfp_port[1].pin_tx_disable = GPIO_NA,
|
||||
.sfp_port[1].sds = 0,
|
||||
.sfp_port[1].i2c = I2CBUS( GPIO41_I2C_SDA3_MDIO1, 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, 1, 0, 0, 0, 0, 1},
|
||||
/* On Ethernet: Left led is orange, right green
|
||||
* 100M and 1G give right led green with activity
|
||||
* 2.5GBit adds solid left orange LED
|
||||
* LEDs for the SFP-ports are the same as for the Ethernet ports
|
||||
*/
|
||||
.led_sets = { { LEDS_2G5 | LEDS_LINK | LEDS_ACT,
|
||||
0,
|
||||
LEDS_1G | LEDS_100M | LEDS_10M | LEDS_LINK | LEDS_ACT,
|
||||
LEDS_2G5 | LEDS_LINK | LEDS_ACT
|
||||
},
|
||||
{ LEDS_2G5 | LEDS_TWO_PAIR_1G | LEDS_1G | LEDS_500M | LEDS_100M | LEDS_10M |
|
||||
LEDS_LINK | LEDS_ACT | LEDS_10G | LEDS_TWO_PAIR_5G | LEDS_5G | LEDS_TWO_PAIR_2G5,
|
||||
LEDS_1G | LEDS_LINK,
|
||||
LEDS_LINK,
|
||||
LEDS_COL | LEDS_DUPLEX
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
@@ -0,0 +1,7 @@
|
||||
#ifndef _MACHINE_ZOERAX_HG0402XG_V1_1_H_
|
||||
#define _MACHINE_ZOERAX_HG0402XG_V1_1_H_
|
||||
|
||||
#define MACHINE_MODEL "ZoeRax HG0402XG V1.1"
|
||||
#define MACHINE_CHIPSET RTL8372
|
||||
|
||||
#endif
|
||||
@@ -19,6 +19,8 @@
|
||||
#define MAC_ADDR_LEN 6
|
||||
#define PORT_BIT(p) ((uint16_t)1 << (p))
|
||||
#define NUL '\0'
|
||||
#define STR_(x) #x
|
||||
#define STR(x) STR_(x)
|
||||
|
||||
// Define Port-masks for 9-port devices and 6-port devices
|
||||
#define PMASK_9 0x1ff
|
||||
|
||||
@@ -55,7 +55,7 @@ __sfr __at(0xbb) SFR_BANK_RET;
|
||||
|
||||
__sfr __at(0x8e) CKCON;
|
||||
|
||||
__sfr __at(0x97) SFR_97; // HADDR?
|
||||
__sfr __at(0x97) MAP_FLASH_PAGE_TO_XDATA; // 1 (hardware reset default): xdata 0x4000-0xbfff reads the flash image; 0: RAM
|
||||
__sfr __at(0xb9) SFR_b9;
|
||||
__sfr __at(0xba) SFR_ba;
|
||||
|
||||
|
||||
@@ -2,13 +2,21 @@
|
||||
#define _RTL837X_STP_H_
|
||||
|
||||
#include <stdint.h>
|
||||
#include "rtl837x_common.h"
|
||||
void stp_in(void) __banked;
|
||||
void stp_setup(void) __banked;
|
||||
void stp_timers(void) __banked;
|
||||
void stp_off(void) __banked;
|
||||
void stp_parse(void) __banked __reentrant;
|
||||
void stp_defaults(void) __banked;
|
||||
void stp_status(void) __banked;
|
||||
void stp_port_admin(uint8_t port, uint8_t on) __banked;
|
||||
void stp_prio_apply(void) __banked;
|
||||
void stp_port_mcheck(uint8_t port) __banked;
|
||||
uint8_t stp_port_role(uint8_t port) __banked;
|
||||
uint8_t stp_port_state(uint8_t port) __banked;
|
||||
uint8_t stp_ent_id(uint8_t e) __banked;
|
||||
void stp_lag_map(void) __banked;
|
||||
void stp_counters_clear(void) __banked __reentrant;
|
||||
|
||||
/* Tick rate of stp_timers(), also used by the web UI. */
|
||||
#define STP_HZ 50
|
||||
@@ -43,22 +51,38 @@ extern __xdata uint8_t stp_txhold;
|
||||
#define STP_PF_TRIPPED 0x80 /* runtime: disabled by BPDU guard */
|
||||
|
||||
extern __xdata uint8_t stp_pflags[STP_ENTITIES];
|
||||
extern __xdata uint32_t stp_pcost[STP_ENTITIES]; /* path cost; 0 = auto (20000) */
|
||||
extern __xdata uint32_t stp_pcost[STP_ENTITIES];
|
||||
extern __xdata uint8_t stp_pprio[STP_ENTITIES];
|
||||
extern __xdata uint8_t stp_pp2p[STP_ENTITIES];
|
||||
extern __xdata uint8_t stp_ent_of[STP_PORTS]; /* the STP entity a port answers to: itself, or its lag */
|
||||
extern __xdata uint16_t stp_lag_mask[STP_LAG_COUNT]; /* member ports of each lag; 0 = not part of STP */
|
||||
extern __xdata uint8_t stp_ent_of[STP_PORTS];
|
||||
extern __xdata uint16_t stp_lag_mask[STP_LAG_COUNT];
|
||||
|
||||
/* Last-heard designated info per port (from received BPDUs); consult
|
||||
* stp_bpdu_age to decide whether it is still current. */
|
||||
extern __xdata struct bridge stp_dbridge[STP_ENTITIES];
|
||||
extern __xdata uint16_t stp_dpid[STP_ENTITIES];
|
||||
extern __xdata uint32_t stp_dcost[STP_ENTITIES];
|
||||
extern __xdata uint16_t stp_bpdu_age[STP_ENTITIES]; /* ticks since a BPDU was heard */
|
||||
extern __xdata uint16_t stp_bpdu_age[STP_ENTITIES];
|
||||
#define STP_CNT_RX 0 /* BPDUs taken in on an STP port */
|
||||
#define STP_CNT_TX 1 /* BPDUs put on the wire */
|
||||
#define STP_CNT_TCRX 2 /* BPDUs received with the TC flag, TCNs */
|
||||
#define STP_CNT_TCTX 3 /* BPDUs sent with the TC flag, TCNs */
|
||||
#define STP_CNT_N 4
|
||||
extern __xdata uint32_t stp_cnt[STP_CNT_N][STP_ENTITIES];
|
||||
extern __xdata uint16_t stp_info_while[STP_ENTITIES];
|
||||
|
||||
extern __xdata struct bridge root_bridge;
|
||||
extern __xdata uint32_t root_bridge_cost;
|
||||
extern __xdata uint8_t stp_root_port;
|
||||
extern __xdata uint8_t stp_root_maxage;
|
||||
extern __xdata uint8_t stp_root_fwd;
|
||||
extern __xdata uint16_t stp_link_prev;
|
||||
extern __xdata uint16_t stp_alt;
|
||||
extern __xdata uint16_t stp_backup;
|
||||
extern __xdata uint16_t stp_legacy;
|
||||
extern __xdata uint16_t stp_rrwhile[STP_ENTITIES];
|
||||
extern __xdata uint16_t stp_rbwhile[STP_ENTITIES];
|
||||
extern __xdata uint16_t stp_tc_count;
|
||||
extern __xdata uint32_t stp_tc_secs;
|
||||
extern __xdata uint8_t stp_pcost_short;
|
||||
extern __xdata uint8_t stp_bpdu_filter;
|
||||
|
||||
#endif
|
||||
@@ -0,0 +1,228 @@
|
||||
/*
|
||||
* Command line interface of the Spanning Tree Protocol for the RTL837x platform
|
||||
* This code is in the Public Domain
|
||||
*/
|
||||
|
||||
#include <stdint.h>
|
||||
#include "rtl837x_common.h"
|
||||
#include "rtl837x_stp.h"
|
||||
#include "machine.h"
|
||||
|
||||
#pragma codeseg BANK2
|
||||
#pragma constseg BANK2
|
||||
|
||||
extern __xdata uint8_t cmd_buffer[CMD_BUF_SIZE];
|
||||
extern __xdata uint8_t cmd_words_len;
|
||||
extern __xdata uint8_t cmd_words_b[15];
|
||||
uint8_t cmd_compare(uint8_t start, __code const uint8_t * cmd);
|
||||
uint8_t atoi_byte(uint8_t idx);
|
||||
uint8_t cmd_parse_port_separator(uint8_t idx);
|
||||
extern __xdata uint8_t atoi_results_u8;
|
||||
extern __xdata char save_cmd;
|
||||
extern __xdata uint8_t err_status;
|
||||
|
||||
#define TIMES_OK(h, m, f) ((uint16_t)2 * ((f) - 1) >= (m) && (m) >= (uint16_t)2 * ((h) + 1))
|
||||
|
||||
__xdata uint32_t stp_cli_cost;
|
||||
__xdata uint8_t stp_cli_val;
|
||||
|
||||
|
||||
void stp_parse(void) __banked __reentrant
|
||||
{
|
||||
uint8_t port;
|
||||
|
||||
if (cmd_compare(1, "on")) {
|
||||
print_string("STP enabled\n");
|
||||
stp_enabled = 1;
|
||||
stp_setup();
|
||||
return;
|
||||
}
|
||||
if (cmd_compare(1, "off")) {
|
||||
print_string("STP disabled\n");
|
||||
stp_off();
|
||||
stp_enabled = 0;
|
||||
return;
|
||||
}
|
||||
if (cmd_compare(1, "status")) {
|
||||
stp_status();
|
||||
return;
|
||||
}
|
||||
if (cmd_compare(1, "clear")) {
|
||||
stp_counters_clear();
|
||||
return;
|
||||
}
|
||||
if (cmd_words_len < 3)
|
||||
goto err;
|
||||
|
||||
if (cmd_compare(1, "port") || cmd_compare(1, "lag")) {
|
||||
if (cmd_words_len < 4)
|
||||
goto err;
|
||||
stp_lag_map();
|
||||
if (cmd_compare(1, "lag")) {
|
||||
if (!atoi_byte(cmd_words_b[2]) || !atoi_results_u8 || atoi_results_u8 > STP_LAG_COUNT)
|
||||
goto err;
|
||||
port = STP_LAG_BASE - 1 + atoi_results_u8;
|
||||
} else {
|
||||
if (!cmd_parse_port_separator(cmd_words_b[2]))
|
||||
goto err;
|
||||
port = atoi_results_u8;
|
||||
if (stp_ent_of[port] != port) {
|
||||
print_string("Error: the port is in lag ");
|
||||
write_char('1' + stp_ent_of[port] - STP_LAG_BASE);
|
||||
write_char('\n');
|
||||
err_status = ERR_INVALID_ARGUMENT;
|
||||
return;
|
||||
}
|
||||
}
|
||||
if (cmd_words_len < 5 && !cmd_compare(3, "on") && !cmd_compare(3, "off")
|
||||
&& !cmd_compare(3, "mcheck"))
|
||||
goto err;
|
||||
if (cmd_compare(3, "on")) {
|
||||
stp_pflags[port] |= STP_PF_ENABLED;
|
||||
stp_pflags[port] &= ~STP_PF_TRIPPED;
|
||||
if (stp_enabled) { /* (re)join: listen first */
|
||||
stp_port_admin(port, 1);
|
||||
}
|
||||
} else if (cmd_compare(3, "mcheck")) {
|
||||
if (stp_enabled)
|
||||
stp_port_mcheck(port);
|
||||
} else if (cmd_compare(3, "off")) {
|
||||
stp_pflags[port] &= ~STP_PF_ENABLED;
|
||||
if (stp_enabled)
|
||||
stp_port_admin(port, 0); /* plain forwarding */
|
||||
} else if (cmd_compare(3, "edge")) {
|
||||
/* Also drop the *operational* edge flag: it is what exempts the
|
||||
* port from topology changes and lets it skip the listen period,
|
||||
* so leaving it set would keep the old behaviour until the next
|
||||
* "stp off"/"stp on". An admin edge is operational immediately. */
|
||||
stp_pflags[port] &= ~(STP_PF_ADMEDGE | STP_PF_AUTOEDGE | STP_PF_OPEREDGE);
|
||||
if (cmd_compare(4, "on"))
|
||||
stp_pflags[port] |= STP_PF_ADMEDGE | STP_PF_OPEREDGE;
|
||||
else if (cmd_compare(4, "auto"))
|
||||
stp_pflags[port] |= STP_PF_AUTOEDGE;
|
||||
else if (!cmd_compare(4, "off"))
|
||||
goto err;
|
||||
} else if (cmd_compare(3, "cost")) {
|
||||
/* raw 802.1D value, 0..200000000; 0 = auto (speed-based) */
|
||||
stp_cli_cost = 0;
|
||||
{
|
||||
__xdata uint8_t *cp = &cmd_buffer[cmd_words_b[4]];
|
||||
if (*cp < '0' || *cp > '9')
|
||||
goto err;
|
||||
while (*cp >= '0' && *cp <= '9') {
|
||||
stp_cli_cost = stp_cli_cost * 10 + (*cp - '0');
|
||||
cp++;
|
||||
}
|
||||
}
|
||||
if (stp_cli_cost > 200000000UL)
|
||||
goto err;
|
||||
stp_pcost[port] = stp_cli_cost;
|
||||
} else if (cmd_compare(3, "p2p")) {
|
||||
if (cmd_compare(4, "auto"))
|
||||
stp_pp2p[port] = 0;
|
||||
else if (cmd_compare(4, "on"))
|
||||
stp_pp2p[port] = 1;
|
||||
else if (cmd_compare(4, "off"))
|
||||
stp_pp2p[port] = 2;
|
||||
else
|
||||
goto err;
|
||||
} else if (cmd_compare(3, "prio")) {
|
||||
if (!atoi_byte(cmd_words_b[4]))
|
||||
goto err;
|
||||
if (atoi_results_u8 > 240 || (atoi_results_u8 & 0x0f))
|
||||
goto err;
|
||||
stp_pprio[port] = atoi_results_u8;
|
||||
} else if (cmd_compare(3, "guard")) {
|
||||
stp_pflags[port] &= ~(STP_PF_BPDUGUARD | STP_PF_ROOTGUARD);
|
||||
if (cmd_compare(4, "bpdu"))
|
||||
stp_pflags[port] |= STP_PF_BPDUGUARD;
|
||||
else if (cmd_compare(4, "root"))
|
||||
stp_pflags[port] |= STP_PF_ROOTGUARD;
|
||||
else if (!cmd_compare(4, "none"))
|
||||
goto err;
|
||||
} else if (cmd_compare(3, "filter")) {
|
||||
if (cmd_compare(4, "on"))
|
||||
stp_pflags[port] |= STP_PF_FILTER;
|
||||
else if (cmd_compare(4, "off"))
|
||||
stp_pflags[port] &= ~STP_PF_FILTER;
|
||||
else
|
||||
goto err;
|
||||
} else {
|
||||
goto err;
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
if (!atoi_byte(cmd_words_b[2])) {
|
||||
if (cmd_compare(1, "version")) {
|
||||
if (cmd_compare(2, "rstp"))
|
||||
stp_rstp = 1;
|
||||
else if (cmd_compare(2, "stp"))
|
||||
stp_rstp = 0;
|
||||
else
|
||||
goto err;
|
||||
return;
|
||||
}
|
||||
if (cmd_compare(1, "pathcost")) {
|
||||
if (cmd_compare(2, "long"))
|
||||
stp_pcost_short = 0;
|
||||
else if (cmd_compare(2, "short"))
|
||||
stp_pcost_short = 1;
|
||||
else
|
||||
goto err;
|
||||
return;
|
||||
}
|
||||
if (cmd_compare(1, "bpdu")) {
|
||||
if (cmd_compare(2, "filter"))
|
||||
stp_bpdu_filter = 1;
|
||||
else if (cmd_compare(2, "flood"))
|
||||
stp_bpdu_filter = 0;
|
||||
else
|
||||
goto err;
|
||||
if (!stp_enabled)
|
||||
stp_off();
|
||||
return;
|
||||
}
|
||||
goto err;
|
||||
}
|
||||
stp_cli_val = atoi_results_u8;
|
||||
|
||||
if (cmd_compare(1, "prio")) {
|
||||
if (stp_cli_val > 15)
|
||||
goto err;
|
||||
stp_prio = stp_cli_val << 4; /* n * 4096, as the BPDU's high byte */
|
||||
stp_prio_apply();
|
||||
} else if (cmd_compare(1, "hello")) {
|
||||
if (stp_cli_val < 1 || stp_cli_val > 10)
|
||||
goto err;
|
||||
if (save_cmd && !TIMES_OK(stp_cli_val, stp_maxage_s, stp_fwddelay_s))
|
||||
goto times;
|
||||
stp_hello_s = stp_cli_val;
|
||||
} else if (cmd_compare(1, "maxage")) {
|
||||
if (stp_cli_val < 6 || stp_cli_val > 40)
|
||||
goto err;
|
||||
if (save_cmd && !TIMES_OK(stp_hello_s, stp_cli_val, stp_fwddelay_s))
|
||||
goto times;
|
||||
stp_maxage_s = stp_cli_val;
|
||||
} else if (cmd_compare(1, "fwd")) {
|
||||
if (stp_cli_val < 4 || stp_cli_val > 30)
|
||||
goto err;
|
||||
if (save_cmd && !TIMES_OK(stp_hello_s, stp_maxage_s, stp_cli_val))
|
||||
goto times;
|
||||
stp_fwddelay_s = stp_cli_val;
|
||||
} else if (cmd_compare(1, "txhold")) {
|
||||
if (stp_cli_val < 1 || stp_cli_val > 10)
|
||||
goto err;
|
||||
stp_txhold = stp_cli_val;
|
||||
} else {
|
||||
goto err;
|
||||
}
|
||||
return;
|
||||
times:
|
||||
print_string("Error: needs 2*(fwd-1) >= maxage >= 2*(hello+1)\n");
|
||||
err_status = ERR_INVALID_ARGUMENT;
|
||||
return;
|
||||
err:
|
||||
err_status = ERR_INVALID_ARGUMENT;
|
||||
print_string("Error: stp on|off|status|clear | prio <0-15> | hello <1-10> | maxage <6-40> | fwd <4-30> | txhold <1-10> | version rstp|stp | pathcost long|short | bpdu filter|flood | port <1-9>|lag <1-4> on|off|mcheck|edge|cost|prio|guard|filter ...\n");
|
||||
}
|
||||
@@ -1567,6 +1567,15 @@ void check_and_flash_update_image(void)
|
||||
}
|
||||
|
||||
|
||||
/* Called by the SDCC startup code before it copies XINIT to XISEG and clears XSEG.
|
||||
* Hardware reset starts us with MAP_FLASH_PAGE_TO_XDATA = 1, which maps xdata
|
||||
* 0x4000-0xbfff to the flash image (reads return flash, writes are lost), so it
|
||||
* must be 0 by then. */
|
||||
unsigned char __sdcc_external_startup(void)
|
||||
{
|
||||
MAP_FLASH_PAGE_TO_XDATA = 0;
|
||||
return 0;
|
||||
}
|
||||
|
||||
void main(void)
|
||||
{
|
||||
@@ -1577,7 +1586,6 @@ void main(void)
|
||||
sbuf_ptr = 0;
|
||||
|
||||
CKCON = 0; // Initial Clock configuration
|
||||
SFR_97 = 0; // HADDR?
|
||||
|
||||
// Set in managed mode:
|
||||
SFR_b9 = 0x00;
|
||||
|
||||
@@ -0,0 +1,410 @@
|
||||
; Assembler file to intercept register writes/reads and PHY writes/reads
|
||||
; assemble with as31
|
||||
; Compile with
|
||||
; as31 -Fbyte -Orwintercept_zoerax_hg0402xg.hex rwintercept_zoerax_hg0402xg.asm
|
||||
; ./injector zoerax_hg0402xg.bin RTLPlayground/rwintercept_zoerax_hg0402xg.hex zoerax_hg0402xg_patched.bin
|
||||
; Start at 0x3b00, where there is always some space in BANK 0
|
||||
.equ valptr, 0xc7
|
||||
|
||||
|
||||
.equ SFR_DATA_0, 0xa7
|
||||
.equ SFR_DATA_8, 0xa6
|
||||
.equ SFR_DATA_16, 0xa5
|
||||
.equ SFR_DATA_24, 0xa4
|
||||
|
||||
; Intercept Register Write
|
||||
.org 0x4d62
|
||||
lcall reg_write
|
||||
nop
|
||||
|
||||
; Intercept Register Read
|
||||
.org 0x4dad
|
||||
lcall reg_read_head
|
||||
.org 0x4e6f
|
||||
lcall reg_read_tail
|
||||
|
||||
; Intercept PHY write
|
||||
.org 0x522c
|
||||
lcall phy_write
|
||||
|
||||
; Intercept PHY read
|
||||
.org 0x526f
|
||||
lcall phy_read_head
|
||||
.org 0x5295
|
||||
ljmp phy_read_tail
|
||||
|
||||
; Intercept SDS write
|
||||
.org 0x503c
|
||||
lcall sds_write
|
||||
nop
|
||||
|
||||
; Intercept SDS read
|
||||
.org 0x5061
|
||||
lcall sds_read_head
|
||||
nop
|
||||
.org 0x507b
|
||||
ljmp sds_read_tail
|
||||
|
||||
;
|
||||
; BEGIN actual code at unused end of BANK0
|
||||
;
|
||||
.org 0x3b00
|
||||
|
||||
;
|
||||
; print the address of a register write. The address is in R6/R7. A and r4 can be scratched
|
||||
;
|
||||
reg_write:
|
||||
mov a, #'R'
|
||||
acall putc
|
||||
|
||||
mov a, r6
|
||||
acall wbyte
|
||||
|
||||
mov a, r7
|
||||
acall wbyte
|
||||
|
||||
mov a, #'-'
|
||||
acall putc
|
||||
|
||||
mov dptr, #valptr
|
||||
movx a, @dptr
|
||||
acall wbyte
|
||||
mov dptr, #valptr + 1
|
||||
movx a, @dptr
|
||||
acall wbyte
|
||||
mov dptr, #valptr + 2
|
||||
movx a, @dptr
|
||||
acall wbyte
|
||||
mov dptr, #valptr + 3
|
||||
movx a, @dptr
|
||||
acall wbyte
|
||||
|
||||
mov a, #' '
|
||||
acall putc
|
||||
|
||||
mov a, r7
|
||||
mov r5, a
|
||||
mov a, r6
|
||||
mov r4, a
|
||||
ret
|
||||
|
||||
;
|
||||
; print the address of a register read The address is in R2/R3. Registers 4-7 are scratch
|
||||
;
|
||||
reg_read_head:
|
||||
mov a, #'r'
|
||||
acall putc
|
||||
|
||||
mov a, r2
|
||||
acall wbyte
|
||||
|
||||
mov a, r3
|
||||
acall wbyte
|
||||
|
||||
mov a, #':'
|
||||
acall putc
|
||||
clr a
|
||||
mov r7, a
|
||||
mov r6, a
|
||||
mov r5, a
|
||||
mov r4, a
|
||||
ret
|
||||
|
||||
reg_read_tail:
|
||||
mov dptr, #0xc5
|
||||
movx a, @dptr
|
||||
acall wbyte
|
||||
mov dptr, #0xc5 + 1
|
||||
movx a, @dptr
|
||||
acall wbyte
|
||||
mov dptr, #0xc5 + 2
|
||||
movx a, @dptr
|
||||
acall wbyte
|
||||
mov dptr, #0xc5 + 3
|
||||
movx a, @dptr
|
||||
acall wbyte
|
||||
|
||||
mov a, #' '
|
||||
acall putc
|
||||
|
||||
mov dptr,#0xc5
|
||||
ret
|
||||
|
||||
|
||||
;
|
||||
; print phy write options
|
||||
;
|
||||
phy_write:
|
||||
mov a, #'P'
|
||||
acall putc
|
||||
|
||||
mov a, r4
|
||||
acall wbyte
|
||||
|
||||
; phymask
|
||||
mov a, r6
|
||||
acall wbyte
|
||||
mov a, r7
|
||||
acall wbyte
|
||||
|
||||
mov a, #'.'
|
||||
acall putc
|
||||
|
||||
; dev_id?
|
||||
mov a, r5
|
||||
acall wbyte
|
||||
|
||||
; x_phywrite_opt
|
||||
mov dptr, #0xf1
|
||||
movx a, @dptr
|
||||
acall wbyte
|
||||
|
||||
; The register going to SFR_C2 and SFR_C3
|
||||
mov a, r2
|
||||
acall wbyte
|
||||
mov a, r3
|
||||
acall wbyte
|
||||
|
||||
mov a, #':'
|
||||
acall putc
|
||||
|
||||
mov dptr, #0xf2
|
||||
movx a, @dptr
|
||||
acall wbyte
|
||||
|
||||
|
||||
mov dptr, #0xf3
|
||||
movx a, @dptr
|
||||
acall wbyte
|
||||
|
||||
mov a, #' '
|
||||
acall putc
|
||||
|
||||
mov dptr, #0xeb
|
||||
mov r4, #0
|
||||
ret
|
||||
|
||||
;
|
||||
; print phy read options
|
||||
;
|
||||
phy_read_head:
|
||||
mov r1, 4 ; actually r4
|
||||
|
||||
mov a, #'p'
|
||||
acall putc
|
||||
|
||||
mov a, r7
|
||||
acall wbyte
|
||||
|
||||
mov a, r5
|
||||
acall wbyte
|
||||
|
||||
mov a, #'.'
|
||||
acall putc
|
||||
|
||||
mov a, r2
|
||||
acall wbyte
|
||||
mov a, r3
|
||||
acall wbyte
|
||||
|
||||
mov a, #':'
|
||||
acall putc
|
||||
|
||||
mov r1, 7
|
||||
mov a, r2
|
||||
ret
|
||||
|
||||
phy_read_tail:
|
||||
mov r6, 4
|
||||
mov a, r6
|
||||
acall wbyte
|
||||
mov a, r7
|
||||
acall wbyte
|
||||
|
||||
mov a, #' '
|
||||
acall putc
|
||||
mov r4, 6
|
||||
ret
|
||||
|
||||
|
||||
;
|
||||
; print SDS write options
|
||||
;
|
||||
sds_write:
|
||||
mov a, r4
|
||||
mov r2, a ; R2 is scratch
|
||||
|
||||
mov a, #'Q'
|
||||
acall putc
|
||||
|
||||
|
||||
mov a, r7
|
||||
acall wbyte
|
||||
mov a, r5
|
||||
acall wbyte
|
||||
mov a, r3
|
||||
acall wbyte
|
||||
|
||||
mov a, #':'
|
||||
acall putc
|
||||
|
||||
mov dptr, #0xe1
|
||||
movx a, @dptr
|
||||
acall wbyte
|
||||
|
||||
|
||||
mov dptr, #0xe2
|
||||
movx a, @dptr
|
||||
acall wbyte
|
||||
|
||||
mov a, #' '
|
||||
acall putc
|
||||
|
||||
mov a, r7
|
||||
mov r1, a
|
||||
mov a, r5
|
||||
mov r7, a
|
||||
mov a, r2
|
||||
mov r4, a
|
||||
ret
|
||||
|
||||
;
|
||||
; print SDS read options
|
||||
;
|
||||
sds_read_head:
|
||||
mov a, r4
|
||||
mov r1, a ; R1 is scratch
|
||||
|
||||
mov a, #'q'
|
||||
acall putc
|
||||
|
||||
mov a, r7 ; sds_index bit
|
||||
acall wbyte
|
||||
mov a, r5 ; SFR_94 (2 x r5 | r7)
|
||||
acall wbyte
|
||||
mov a, r3 ; SFR_93
|
||||
acall wbyte
|
||||
|
||||
mov a, #':'
|
||||
acall putc
|
||||
|
||||
mov a, r1
|
||||
mov r4, a ; R1 is scratch
|
||||
|
||||
mov a, r7
|
||||
mov r1, a
|
||||
mov a, r5
|
||||
mov r7, a
|
||||
|
||||
ret
|
||||
|
||||
;
|
||||
; print SDS read result
|
||||
;
|
||||
sds_read_tail:
|
||||
mov a, r4
|
||||
mov r6, a ; R6 is returned and preserved
|
||||
|
||||
mov a, r7 ; = r7 = SFR_DATA_8
|
||||
acall wbyte
|
||||
mov r7, SFR_DATA_0
|
||||
mov a, r7 ; SFR_DATA_0
|
||||
acall wbyte
|
||||
|
||||
mov a, #' '
|
||||
acall putc
|
||||
|
||||
mov a, r6 ; R6 is returned and preserved
|
||||
mov r4, a
|
||||
ret
|
||||
|
||||
;
|
||||
; Write a byte as hex to serial
|
||||
;
|
||||
nop
|
||||
nop
|
||||
wbyte:
|
||||
mov r4, a
|
||||
swap a
|
||||
anl a, #0x0f
|
||||
mov dptr,#hex
|
||||
movc a, @a+dptr
|
||||
|
||||
jb ET0, wbyte1
|
||||
ret
|
||||
wbyte1:
|
||||
jbc TI, wbyte2
|
||||
sjmp wbyte1
|
||||
wbyte2:
|
||||
mov SBUF,a
|
||||
|
||||
mov a, r4
|
||||
anl a, #0x0f
|
||||
mov dptr,#hex
|
||||
movc a, @a+dptr
|
||||
|
||||
wbyte3:
|
||||
jbc TI, wbyte4
|
||||
sjmp wbyte3
|
||||
wbyte4:
|
||||
mov SBUF,a
|
||||
|
||||
ret
|
||||
|
||||
;
|
||||
; Intercept isr_ext0
|
||||
;
|
||||
isr_ext0:
|
||||
mov a, #'i'
|
||||
acall putc
|
||||
mov r0,#0x51
|
||||
inc @r0
|
||||
ret
|
||||
|
||||
;
|
||||
; Intercept isr_ext1
|
||||
;
|
||||
isr_ext1:
|
||||
mov a, #'I'
|
||||
acall putc
|
||||
mov r0,#0x25
|
||||
mov a, @r0
|
||||
ret
|
||||
;
|
||||
; Intercept isr_ext2
|
||||
;
|
||||
isr_ext2:
|
||||
mov a, #'J'
|
||||
acall putc
|
||||
mov r0, #0x1e
|
||||
mov a, #0xa
|
||||
ret
|
||||
|
||||
;
|
||||
; Intercept isr_ext3
|
||||
;
|
||||
isr_ext3:
|
||||
mov a, #'j'
|
||||
acall putc
|
||||
mov r0,#0x1e
|
||||
mov a,#0x14
|
||||
ret
|
||||
|
||||
;
|
||||
; Write a character to serial
|
||||
;
|
||||
nop
|
||||
nop
|
||||
putc:
|
||||
jb ET0, putc1
|
||||
ret
|
||||
putc1:
|
||||
jbc TI,putc2
|
||||
sjmp putc1
|
||||
putc2:
|
||||
mov SBUF,a
|
||||
ret
|
||||
|
||||
hex:
|
||||
.byte "0123456789abcdef"
|
||||
.end
|
||||
@@ -121,6 +121,12 @@ uint8_t stp_pflags[STP_ENTITIES];
|
||||
uint8_t stp_pp2p[STP_ENTITIES];
|
||||
uint8_t stp_pprio[STP_ENTITIES];
|
||||
uint8_t stp_prio = 0x80, stp_root_port = 0xff, stp_rstp = 1, stp_txhold = 6;
|
||||
uint8_t stp_bpdu_filter, stp_pcost_short, stp_root_fwd = 15, stp_root_maxage = 20;
|
||||
uint16_t stp_legacy, stp_link_prev = 0x3ff;
|
||||
uint16_t stp_info_while[STP_ENTITIES];
|
||||
uint32_t stp_cnt[STP_CNT_N][STP_ENTITIES];
|
||||
uint32_t stp_tc_secs;
|
||||
uint8_t stp_ent_id(uint8_t e) { return e >= STP_LAG_BASE ? e + 101 - STP_LAG_BASE : machine.log_to_phys_port[e]; }
|
||||
|
||||
/* ---- SFP: no module present, reads fail ---- */
|
||||
uint8_t sfp_buf[16];
|
||||
|
||||
@@ -0,0 +1,29 @@
|
||||
CC ?= cc
|
||||
SRCROOT = ../..
|
||||
CFLAGS = -std=c11 -g -O1 -Wall -Wextra -Werror -Wno-unused-parameter -Wno-unknown-pragmas \
|
||||
-Wno-pointer-sign \
|
||||
-fpack-struct=1 \
|
||||
-DRTLP_HOST_TEST \
|
||||
-fsanitize=address,undefined -fno-omit-frame-pointer \
|
||||
-include ../sdcc_shim.h -I../stub -I$(SRCROOT) -I$(SRCROOT)/httpd -I$(SRCROOT)/uip
|
||||
ifeq ($(findstring clang,$(shell $(CC) --version 2>/dev/null)),)
|
||||
CFLAGS += -Wno-builtin-declaration-mismatch
|
||||
endif
|
||||
LDFLAGS = -fsanitize=address,undefined
|
||||
|
||||
SRCS = stp_sim.c $(SRCROOT)/rtl837x_stp.c $(SRCROOT)/rtl837x_stp_cli.c
|
||||
|
||||
all: build/stp_sim
|
||||
./build/stp_sim
|
||||
|
||||
verbose: build/stp_sim
|
||||
./build/stp_sim -v
|
||||
|
||||
build/stp_sim: $(SRCS) $(SRCROOT)/rtl837x_stp.h
|
||||
mkdir -p build
|
||||
$(CC) $(CFLAGS) $(SRCS) -o $@ $(LDFLAGS)
|
||||
|
||||
clean:
|
||||
rm -rf build
|
||||
|
||||
.PHONY: all verbose clean
|
||||