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 | ||
|
|
7b4d0d55d2 | ||
|
|
e95e450813 | ||
|
|
fe1d4119d0 | ||
|
|
c5f62bff95 | ||
|
|
7535c0d9b7 | ||
|
|
a57319557d | ||
|
|
1b29fb687f | ||
|
|
2e62f9eea7 | ||
|
|
5c6a82d50f | ||
|
|
1d158d21d1 | ||
|
|
26c420d26a | ||
|
|
eb394af95f | ||
|
|
4a4caeffd7 | ||
|
|
391360f517 | ||
|
|
ed8e4b1a56 | ||
|
|
78f28488c1 | ||
|
|
e1c79ec4cd | ||
|
|
a7db0ca8b6 | ||
|
|
70f59b64f5 | ||
|
|
f0cbaba7b1 | ||
|
|
2d5424fedc | ||
|
|
23c8b2a8b3 | ||
|
|
c9f6362da5 | ||
|
|
742aef5461 | ||
|
|
a77d219baf | ||
|
|
f94204e56f | ||
|
|
8d6adedb56 | ||
|
|
c3f9acea10 | ||
|
|
c46261e052 | ||
|
|
834403b97b | ||
|
|
48087d3520 | ||
|
|
1d359afba5 | ||
|
|
ec14dbf323 | ||
|
|
deedb92c5d | ||
|
|
a18c334139 | ||
|
|
337b171ac4 | ||
|
|
53eb3f5486 | ||
|
|
241edfdbda | ||
|
|
714c9eb343 | ||
|
|
f2c89fd75b | ||
|
|
ca2f1d101d | ||
|
|
9cc859ad30 | ||
|
|
0181276ec6 | ||
|
|
905e588071 | ||
|
|
bb16302a80 | ||
|
|
50fda40fd9 | ||
|
|
9c65039bab | ||
|
|
7e0bc93b55 | ||
|
|
e1e1292ab0 | ||
|
|
975caaff02 | ||
|
|
cffe4793b1 | ||
|
|
9ed1e89c8b | ||
|
|
ba2f759df3 | ||
|
|
884a1ac016 | ||
|
|
51ed063dcb | ||
|
|
0da6318777 | ||
|
|
d13a5d125d | ||
|
|
889c94404c | ||
|
|
0f98734ae2 | ||
|
|
2d0abbbca0 | ||
|
|
fdcdfa9b45 | ||
|
|
c043c671d7 | ||
|
|
7d7585fa6d | ||
|
|
13801241ca | ||
|
|
7b0a07efaa | ||
|
|
3c00ab0ae9 | ||
|
|
3deee14f94 | ||
|
|
6d539bfb1b | ||
|
|
827d3b389c | ||
|
|
ef224ec8f4 | ||
|
|
2a9f9b2508 | ||
|
|
723d16b4cb | ||
|
|
a1998c70d7 | ||
|
|
196d22d7c3 | ||
|
|
680a40dab5 | ||
|
|
97b15f4022 | ||
|
|
c263a8ab64 | ||
|
|
d5ccd38dd3 | ||
|
|
6bae5c5c90 | ||
|
|
c2980e42d8 | ||
|
|
875933b9f9 | ||
|
|
c02f660c08 | ||
|
|
d0374541c5 |
No files matched your search
@@ -3,6 +3,7 @@
|
||||
.gitattributes
|
||||
.github/
|
||||
output/
|
||||
toolchain/
|
||||
installer/output/
|
||||
*.bin
|
||||
html_data.c
|
||||
|
||||
@@ -29,7 +29,12 @@ jobs:
|
||||
- name: Install dependencies
|
||||
run: |
|
||||
apt update
|
||||
apt install make gcc sdcc xxd python-is-python3 libjson-c-dev zlib1g-dev -y
|
||||
apt install make gcc g++ bison flex libboost-dev patch bzip2 curl ca-certificates xxd python-is-python3 libjson-c-dev zlib1g-dev -y
|
||||
- name: Cache the patched SDCC
|
||||
uses: actions/cache@v4
|
||||
with:
|
||||
path: branch/toolchain
|
||||
key: sdcc-${{ hashFiles('branch/sdcc/*') }}
|
||||
- name: Run the host-side unit tests
|
||||
run: cd branch && make -C test
|
||||
- name: Check that every machine definition can be compiled
|
||||
@@ -41,7 +46,10 @@ jobs:
|
||||
- name: Make project with the health instrumentation
|
||||
run: cd branch && make clean && make CI=1 MACHINE="KP_9000_6XHML_X2" HEALTH=1
|
||||
- name: Make main
|
||||
run: cd main && make CI=1 MACHINE="KP_9000_6XHML_X2" && cp output/KP_9000_6XHML_X2/rtlplayground.map /tmp/main.map
|
||||
run: |
|
||||
ln -s ../branch/toolchain main/toolchain
|
||||
PATH="$(echo "$PWD"/branch/toolchain/sdcc-*/bin):$PATH"
|
||||
cd main && make CI=1 MACHINE="KP_9000_6XHML_X2" && cp output/KP_9000_6XHML_X2/rtlplayground.map /tmp/main.map
|
||||
- name: Calculate bank growth
|
||||
run: |
|
||||
sed -rn 's/^([XCD]SEG|(BANK[0-9])).*=\s+([0-9]+)\. bytes.*$/MAIN_\1=\3/p' /tmp/main.map | sort -u >> "$GITHUB_ENV"
|
||||
|
||||
@@ -2,6 +2,7 @@
|
||||
.idea/
|
||||
output/
|
||||
output-main/
|
||||
toolchain/
|
||||
html_data.c
|
||||
html_data.h
|
||||
version.h
|
||||
|
||||
@@ -25,12 +25,34 @@
|
||||
|
||||
## Fixed
|
||||
|
||||
- Time
|
||||
- `rtl8372_init()` sets SDS 0 to SGMII as the stock firmware does. The masks were 0, so on RTL8372
|
||||
boards without 10G ports both SerDes stayed off, and with SDS 0 off `RTL837X_REG_SEC_COUNTER`
|
||||
does not hold a written value: NTP never retried after a failed first attempt at boot and
|
||||
froze the clock after a sync.
|
||||
|
||||
- Web UI
|
||||
- Firefox no longer fills a saved login into the new admin password fields. It also put the
|
||||
user name into the NTP poll interval, which marked the Time card as edited, so it kept its
|
||||
defaults (NTP off, no server, UTC-12:00) instead of the switch's settings.
|
||||
- Accept firmware uploads regardless of the file part's MIME type, including
|
||||
`application/macbinary` sent by Chrome on macOS for `.bin` files.
|
||||
|
||||
- Health
|
||||
- `HEALTH=1` builds no longer write past the phase timing arrays at the LLDP checkpoint, which
|
||||
corrupted the `link` slow counter and the DHCP client state. #658
|
||||
- Config
|
||||
- A startup configuration line longer than the command buffer no longer stops the replay:
|
||||
the offending line is skipped and the following lines are still applied.
|
||||
- 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
|
||||
|
||||
@@ -3,7 +3,14 @@ FROM debian:13-slim
|
||||
RUN apt-get update && apt-get install -y \
|
||||
make \
|
||||
gcc \
|
||||
sdcc \
|
||||
g++ \
|
||||
bison \
|
||||
flex \
|
||||
libboost-dev \
|
||||
patch \
|
||||
bzip2 \
|
||||
curl \
|
||||
ca-certificates \
|
||||
xxd \
|
||||
python3 \
|
||||
libjson-c-dev \
|
||||
|
||||
@@ -4,8 +4,25 @@ DEFAULT_CONFIG_LOCATION = 454656
|
||||
CONFIG_LOCATION = 458752
|
||||
HTML_LOCATION = 262144
|
||||
|
||||
# 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)
|
||||
CC = sdcc
|
||||
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
|
||||
@@ -43,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)
|
||||
@@ -65,14 +83,15 @@ 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)"
|
||||
|
||||
create_build_dir:
|
||||
create_build_dir: $(SDCC)
|
||||
mkdir -p "$(BUILDDIR)"
|
||||
mkdir -p "$(BUILDDIR)/uip"
|
||||
mkdir -p "$(BUILDDIR)/httpd"
|
||||
@@ -88,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 = \
|
||||
@@ -102,6 +127,7 @@ SRCS = \
|
||||
sfp.c \
|
||||
dns.c \
|
||||
dns_cli.c \
|
||||
sessions.c \
|
||||
ntp.c \
|
||||
ntp_cli.c \
|
||||
syslog.c \
|
||||
@@ -124,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 \
|
||||
@@ -145,19 +173,30 @@ HTML := $(shell find html -name '*.js' -or -name '*.html' -or -name '*.svg' -or
|
||||
# The raw html/ sources stay untouched for development; the minified
|
||||
# copy is a build artifact under output/.
|
||||
HTML_MIN := $(BUILDDIR)/html_min
|
||||
HTML_MIN_LIST := $(BUILDDIR)/html_min.list
|
||||
HTML_MIN_STAMP := $(BUILDDIR)/html_min.stamp
|
||||
.PHONY: html_min
|
||||
html_min: $(HTML)
|
||||
html_min: $(HTML_MIN_STAMP)
|
||||
|
||||
$(HTML_MIN_LIST): FORCE
|
||||
@mkdir -p $(BUILDDIR)
|
||||
@echo $(sort $(HTML)) > $@.tmp
|
||||
@cmp -s $@.tmp $@ && rm -f $@.tmp || mv $@.tmp $@
|
||||
|
||||
$(HTML_MIN_STAMP): $(HTML) $(HTML_MIN_LIST) tools/minify.py $(if $(wildcard $(HTML_MIN)),,FORCE)
|
||||
rm -rf $(HTML_MIN)
|
||||
mkdir -p $(HTML_MIN)
|
||||
@for f in $(HTML); do python3 tools/minify.py $$f $(HTML_MIN)/$$(basename $$f) || exit 1; done
|
||||
@touch $@
|
||||
|
||||
html_data.c html_data.h &: $(HTML) | tools html_min
|
||||
html_data.c html_data.h &: $(HTML_MIN_STAMP) | tools
|
||||
tools/$(BUILDDIR)/fileadder -a $(HTML_LOCATION) -s $(IMAGESIZE) -b BANK1 -z -d $(HTML_MIN) -p html_data
|
||||
|
||||
$(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 \
|
||||
@@ -169,12 +208,18 @@ $(SUBDIRS):
|
||||
$(MAKE) -C $@ BUILDDIR=$(BUILDDIR)
|
||||
|
||||
clean: $(SUBDIRSCLEAN)
|
||||
-rm -f html_data.c html_data.h $(VERSION_HEADER)
|
||||
-rm -f html_data.c html_data.h $(VERSION_HEADER) $(HTML_MIN_LIST) $(HTML_MIN_STAMP)
|
||||
-if [ -d $(BUILDDIR) ]; then find $(BUILDDIR) -type f ! -name "*.bin" -delete; fi
|
||||
|
||||
distclean: $(SUBDIRSCLEAN)
|
||||
-rm -f html_data.c html_data.h $(VERSION_HEADER)
|
||||
-rm -f html_data.c html_data.h $(VERSION_HEADER) $(HTML_MIN_LIST) $(HTML_MIN_STAMP)
|
||||
-rm -rf $(BUILDDIR)
|
||||
-rm -rf toolchain
|
||||
|
||||
ifneq ($(SDCC),)
|
||||
$(SDCC):
|
||||
sh sdcc/build.sh $(abspath $(SDCC_PREFIX))
|
||||
endif
|
||||
|
||||
$(SUBDIRSCLEAN):
|
||||
$(MAKE) -C $(@:clean=) clean
|
||||
@@ -189,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 $<
|
||||
|
||||
@@ -197,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
|
||||
@@ -214,16 +265,18 @@ $(BUILDDIR)/rtlplayground-$(FILENAME_EXTENSION).bin: $(BUILDDIR)/rtlplayground.i
|
||||
.PHONY: clean distclean all $(SUBDIRS) $(SUBDIRSCLEAN) create_build_dir
|
||||
|
||||
# Every machine/ definition is compiled, so a machine added to machine/ is
|
||||
# covered without registering it anywhere. The _init.c files are excluded here:
|
||||
# they are the boot hooks, compiled separately below (the board's own if it
|
||||
# exists, otherwise default_machine_init.c). The machine-specific defines are
|
||||
# filtered out of $(CC_FLAGS) and supplied per iteration instead, so this also
|
||||
# works as "make machine_check MACHINE=...".
|
||||
MACHINE_DEFS := $(sort $(patsubst machine/%.c,%,$(filter-out machine/%_init.c,$(wildcard machine/*.c))))
|
||||
# covered without registering it anywhere. A board only counts once both its
|
||||
# <machine>.c and its <machine>.h are present: the header is what the rest of
|
||||
# the build includes, so a .c without one is an incomplete board. The _init.c
|
||||
# files are excluded here: they are the boot hooks, compiled separately below
|
||||
# (the board's own if it exists, otherwise default_machine_init.c). The
|
||||
# machine-specific defines are filtered out of $(CC_FLAGS) and supplied per
|
||||
# iteration instead, so this also works as "make machine_check MACHINE=...".
|
||||
MACHINE_DEFS := $(sort $(filter $(patsubst machine/%.h,%,$(wildcard machine/*.h)),$(patsubst machine/%.c,%,$(filter-out machine/%_init.c,$(wildcard machine/*.c)))))
|
||||
MACHINE_LESS_CC_FLAGS = $(filter-out -DMACHINE_%,$(CC_FLAGS))
|
||||
|
||||
.PHONY: machine_check
|
||||
machine_check:
|
||||
machine_check: $(SDCC)
|
||||
@mkdir -p $(BUILDDIR)/tmp
|
||||
@set -eo pipefail; \
|
||||
for MACHINE in $(MACHINE_DEFS); \
|
||||
|
||||
@@ -56,12 +56,19 @@ device simulator is provided, which runs entirely under Linux as a local webserv
|
||||
|
||||
## (0) Compiling Requirements
|
||||
|
||||
Install the following particular build requisites (Debian 12/13), note that Ubuntu 24.04
|
||||
still has an older version of sdcc, but you will need sdcc version 4.5 for the code to compile:
|
||||
Install the following particular build requisites (Debian 12/13):
|
||||
```
|
||||
sudo apt install make gcc sdcc xxd python-is-python3 libjson-c-dev zlib1g-dev
|
||||
sudo apt install make gcc g++ bison flex libboost-dev patch bzip2 curl xxd python-is-python3 libjson-c-dev zlib1g-dev
|
||||
```
|
||||
|
||||
The first `make` downloads SDCC 4.6.0, applies the fixes in `sdcc/` and builds it into
|
||||
`toolchain/`, which takes a few minutes. Later builds reuse it. The released SDCC 4.5.0
|
||||
and 4.6.0 miscompile parts of this code, see [SDCC bug #4153](https://sourceforge.net/p/sdcc/bugs/4153/)
|
||||
and [#4154](https://sourceforge.net/p/sdcc/bugs/4154/); both fixes are in SDCC trunk since
|
||||
r17039. The third patch is for [#4070](https://sourceforge.net/p/sdcc/bugs/4070/), where the
|
||||
assembler drops the top byte of a `__sfr32` address list; it is proposed upstream and lets
|
||||
`SFR_DATA_U32` be used again. To use an SDCC you installed yourself instead, run `make CC=sdcc ...`.
|
||||
|
||||
<details>
|
||||
<summary>If using Docker (click to expand)</summary>
|
||||
|
||||
@@ -90,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:
|
||||
|
||||
```
|
||||
@@ -120,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.
|
||||
@@ -163,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
|
||||
|
||||
@@ -24,6 +24,7 @@
|
||||
#include "sfp.h"
|
||||
#include "dhcp.h"
|
||||
#include "syslog.h"
|
||||
#include "sessions.h"
|
||||
#include "dns.h"
|
||||
#include "ntp.h"
|
||||
#include "uip/uip.h"
|
||||
@@ -849,7 +850,7 @@ void parse_mirror(void)
|
||||
print_string("NOT Enabled: ");
|
||||
}
|
||||
print_string("Mirroring port: ");
|
||||
print_phys_port(mirroring_port);
|
||||
print_phys_port(mPort >> 1);
|
||||
reg_read_m(RTL837x_MIRROR_CONF);
|
||||
uint16_t m = sfr_data[0];
|
||||
m = (m << 8) | sfr_data[1];
|
||||
@@ -1479,6 +1480,9 @@ void parse_passwd(void)
|
||||
passwd[j++] = c;
|
||||
} while (c != NUL && j < 20);
|
||||
passwd[j] = NUL;
|
||||
/* Logins made with the old password end here. With one session the next
|
||||
* login pushed a leftover one out; with several it would stay valid. */
|
||||
session_init();
|
||||
return;
|
||||
}
|
||||
cmd_error("Missing password\n");
|
||||
@@ -1836,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');
|
||||
@@ -2011,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();
|
||||
@@ -2329,6 +2335,7 @@ void health_show(void) __banked
|
||||
ph_line("tx ", HEALTH_PH_TX);
|
||||
ph_line("stp ", HEALTH_PH_STP);
|
||||
ph_line("cmd ", HEALTH_PH_CMD);
|
||||
ph_line("lldp", HEALTH_PH_LLDP);
|
||||
|
||||
/* Stack: current depth plus how much of the painted area is untouched */
|
||||
print_string("sp ");
|
||||
|
||||
@@ -19,8 +19,8 @@ extern __xdata uint8_t err_status;
|
||||
#define HEALTH_PH_TX 3
|
||||
#define HEALTH_PH_STP 4
|
||||
#define HEALTH_PH_CMD 5
|
||||
#define HEALTH_PHASES 6
|
||||
#define HEALTH_LLDP 7
|
||||
#define HEALTH_PH_LLDP 6
|
||||
#define HEALTH_PHASES (HEALTH_PH_LLDP + 1) /* last id + 1: sizes the arrays in cmd_parser.c */
|
||||
|
||||
extern __xdata uint16_t health_rx_frames;
|
||||
#define health_rx_frame() health_rx_frames++
|
||||
|
||||
@@ -38,8 +38,8 @@ existing files as the template:
|
||||
```
|
||||
|
||||
`MACHINE_CHIPSET` is `RTL8373` or `RTL8372`; a build without it will fail. It selects the default
|
||||
logical port range: `min_port` is `0` for RTL8373 and `3` for RTL8372, while `max_port` is `8`,
|
||||
the highest logical port index. A board with a different range defines
|
||||
logical port range: `MACHINE_MIN_PORT` is `0` for RTL8373 and `3` for RTL8372, while
|
||||
`MACHINE_MAX_PORT` is `8`, the highest logical port index. A board with a different range defines
|
||||
`MACHINE_MIN_PORT` and/or `MACHINE_MAX_PORT` in its header to override the
|
||||
defaults.
|
||||
|
||||
@@ -94,16 +94,16 @@ __code const struct machine machine = {
|
||||
};
|
||||
```
|
||||
|
||||
`MACHINE_COMMON_SETTING` (from `machine_defs.h`) initializes `machine_name`,
|
||||
`isRTL8373`, `min_port` and `max_port` from the header's `MACHINE_MODEL` and
|
||||
`MACHINE_CHIPSET`. Keep it as the first initializer and list the
|
||||
board-specific fields after it.
|
||||
`MACHINE_COMMON_SETTING` (from `machine_defs.h`) initializes `machine_name` and
|
||||
`isRTL8373` from the header's `MACHINE_MODEL` and `MACHINE_CHIPSET`. The logical
|
||||
port range comes from `MACHINE_MIN_PORT` and `MACHINE_MAX_PORT` in `machine.h`.
|
||||
Keep it as the first initializer and list the board-specific fields after it.
|
||||
|
||||
### Fields
|
||||
|
||||
| Field | Meaning |
|
||||
|-------|---------|
|
||||
| `MACHINE_COMMON_SETTING` | expands to `machine_name` (`MACHINE_MODEL`), `isRTL8373` (`1` for `RTL8373`, `0` for `RTL8372`), `min_port` and `max_port`; all are derived from the header's `MACHINE_CHIPSET` (see Step 1) |
|
||||
| `MACHINE_COMMON_SETTING` | expands to `machine_name` (`MACHINE_MODEL`) and `isRTL8373` (`1` for `RTL8373`, `0` for `RTL8372`); both are derived from the header's `MACHINE_CHIPSET` (see Step 1) |
|
||||
| `n_sfp` | number of SFP+ cages |
|
||||
| `n_10g` | SerDes 10G capability used by the RTL837xN init: `0` none, `1` one, `2` two 10G-capable SerDes |
|
||||
| `log_to_phys_port[9]` | logical port (0-based index) → physical front-panel port number |
|
||||
|
||||
@@ -1,11 +1,13 @@
|
||||
# MokerLink POE-2G080110GS
|
||||
|
||||
The `POE-2G080110GS` is an unmanaged 8x2.5G RJ45 and 1xSFP+ PoE switch sold by Mokerlink. There is also a `POE-2G080110GSM` managed version of the switch which may be similar but this has not been verified on actual hardware.
|
||||
The`KP-9000-9XHP-X` is the KeepLink rebrand for same product.
|
||||
|
||||
## Brands
|
||||
| Brand | Type |Managed| PCB | Flash | Chip RTL |
|
||||
|--------|------------------|-------|------------------|-----------------|---------------|
|
||||
| Mokerlink | POE-2G080110GS | No | 2M-PCB51-ML-V1.1 | 4MB (W25Q32JV) | 8373N + 8224N |
|
||||
| KeepLink | KP-9000-9XHP-X | No | 2M-PCB51-ML-V1.1 | 4MB (W25Q32JV) | 8373N + 8224N |
|
||||
|
||||
## Hardware overview
|
||||
Front
|
||||
|
||||
@@ -0,0 +1,93 @@
|
||||
# PCB-AFGW SWTG218ANS V2.0.2
|
||||
|
||||
RTL8373-based 8×2.5G PoE + 1×10G SFP+ web-managed switch, sold as the
|
||||
**RealHD PSW8-25MG**.
|
||||
|
||||
### Label specifications
|
||||
|
||||
- **Manufacturer**: RealHD
|
||||
- **Model**: PSW8-25MG
|
||||
- **PCB marking**: `PCB-AFGW SWTG218ANS_V2.0.2` (silkscreened as `PCB-AFGW SWTG218ANS_V2.0.2_23308`)
|
||||
- **Ports**:
|
||||
- 8 × RJ45: 10/100/1000/2500 Mbps with PoE
|
||||
- 1 × SFP+: 1G / 2.5G / 10G (front panel marking `9(10G)`)
|
||||
- **Power input**: internal AC PSU, `100-240V~ 50/60Hz 2A MAX`, DC output `53.5V 2.8A 150W`
|
||||
- **Front panel LEDs**: `PWR`, `SYS`, per RJ45 port `Link/Act` and `PoE`, per SFP port 2.5G (green) and 10G (blue)
|
||||
- **Stock firmware defaults**: web-managed PoE switch
|
||||
|
||||
### What works
|
||||
|
||||
- All eight 2.5GBASE-T RJ45 ports at 10/100/1000/2500 Mbps (PoE is not configurable via RTLPlayground)
|
||||
- SFP+ port supporting 1G, 2.5G and 10G modules
|
||||
- LEDs: green (2.5G) and amber (1G/100M/10M) per RJ45 port with activity, green (1G/2.5G) and blue (10G) on the SFP port, plus the `SYS` LED
|
||||
|
||||
### Photos
|
||||
|
||||
Front panel:
|
||||
|
||||
<img src="photos/PCB-AFGW_SWTG218ANS_V2.0.2/device_front.jpg" width="800" />
|
||||
|
||||
Board mounted in the case, with the internal PSU above it:
|
||||
|
||||
<img src="photos/PCB-AFGW_SWTG218ANS_V2.0.2/dev_top_w_pcb.jpg" width="800" />
|
||||
|
||||
### PCB overview
|
||||
|
||||
Top side:
|
||||
|
||||
<img src="photos/PCB-AFGW_SWTG218ANS_V2.0.2/pcb_front.jpg" width="800" />
|
||||
|
||||
Bottom side:
|
||||
|
||||
<img src="photos/PCB-AFGW_SWTG218ANS_V2.0.2/pcb_back.jpg" width="800" />
|
||||
|
||||
A 4-pad UART header with `RXD` / `TXD` silkscreened sits next to the right heatsink.
|
||||
|
||||
### Port layout
|
||||
|
||||
| Logical port | Physical port | Type |
|
||||
|--------------|---------------|--------|
|
||||
| 1-8 | 1-8 | RJ45 |
|
||||
| 9 | 9 | SFP+ |
|
||||
|
||||
### LED configuration
|
||||
|
||||
RJ45 ports use LED SET0, the SFP+ port uses LED SET1. The board needs the custom
|
||||
LED mux (`led_mux_custom = 1`), and `RTL837X_REG_LED_GLB_IO_EN` is written to
|
||||
`0x7f24977f` in `machine_custom_init()` so the LED pins are enabled as wired here.
|
||||
|
||||
| SET | LED pin 0 | LED pin 1 |
|
||||
|------|-----------------------------------------------|-------------------------------------------------------|
|
||||
| SET0 | Green — lights on 2.5G link with activity | Amber — lights on 1G / 100M / 10M link with activity |
|
||||
| SET1 | All speeds — lights on any link with activity | 10G — lights on 10G link with activity |
|
||||
|
||||
On the front panel this gives one bi-color `Link/Act` LED per RJ45 port (green at
|
||||
2.5G, amber at 10/100/1000M, blinking with activity) and one `PoE` LED per RJ45
|
||||
port, which is driven by the PoE hardware and not by the switch. The SFP+ port has
|
||||
a green 2.5G LED and a blue 10G LED.
|
||||
|
||||
### SFP GPIO assignments
|
||||
|
||||
| SFP (logical 9) | pin_detect (ModAbs) | pin_los | pin_tx_disable | SerDes | I2C SDA | I2C SCL |
|
||||
|-----------------|---------------------|---------|----------------|--------|------------------|----------------------|
|
||||
| SFP | GPIO38 | GPIO_NA | GPIO_NA | SDS1 | GPIO39_I2C_SDA4 | GPIO40_I2C_SCL3_MDC1 |
|
||||
|
||||
### Machine configuration
|
||||
|
||||
```
|
||||
make MACHINE=REALHD_PSW8_25MG
|
||||
```
|
||||
|
||||
### Programming
|
||||
|
||||
The serial console runs at 8N1 on 115200 baud with the RTLPlayground firmware
|
||||
(see the UART header note under *PCB overview*). The stock firmware baud rate 57600. The flash chip was 2MB.
|
||||
|
||||
### PoE
|
||||
|
||||
The 8 RJ45 ports support PoE, powered by the internal 150W AC PSU (DC output
|
||||
53.5V 2.8A) mounted above the main board.
|
||||
|
||||
Note: RTLPlayground does not provide PoE management for this board. The PoE
|
||||
hardware runs with its own configuration, the same as on the other PoE boards in
|
||||
this project.
|
||||
@@ -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.
|
||||
|
After Width: | Height: | Size: 4.0 MiB |
|
After Width: | Height: | Size: 469 KiB |
|
After Width: | Height: | Size: 1.9 MiB |
|
After Width: | Height: | Size: 1.9 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 |
@@ -14,6 +14,7 @@ rx max 00 slow 0x0000
|
||||
tx max 00 slow 0x0000
|
||||
stp max 04 slow 0x0001
|
||||
cmd max 02 slow 0x0001
|
||||
lldp max 00 slow 0x0000
|
||||
sp 80 untouched 62
|
||||
tcp st 00 tmr 00 rtx 00 rto 00 len 0x0000 mss 0x05b4
|
||||
lport 0x0000 rport 0x0000 rip 0x0000 0x0000
|
||||
@@ -46,7 +47,7 @@ a flooded CPU port from a stalled loop, which look identical from outside.
|
||||
## Phases
|
||||
|
||||
Each line is one section of the main loop: the link poll, SFP handling,
|
||||
frame reception, TCP transmission, the STP timers and the command dispatch.
|
||||
frame reception, TCP transmission, the STP timers, the LLDP timer and the command dispatch.
|
||||
`max` is the worst time that section has taken, in ticks of 5 ms; `slow`
|
||||
counts the passes that took two ticks or more. A misbehaving subsystem
|
||||
names itself here, which is the difference between knowing that the loop is
|
||||
|
||||
@@ -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.
|
||||
@@ -29,6 +29,7 @@ The following devices have been tested and are fully working:
|
||||
| keepLINK | KP-9000-9XHML-X | Yes | [2M-PCB23-V2.2](devices/2M-PCB23-V2_2.md) | 2M | 8 + 1 |
|
||||
| keepLINK | KP-9000-9XHML-X | Yes | [2M-PCB23-V3.1](devices/2M-PCB23-V3_1.md) | 2M | 8 + 1 |
|
||||
| keepLINK | KP-9000-9XHML-X | Yes | [2M-PCB23-V3.2](devices/2M-PCB23-V3_2.md) | 2M | 8 + 1 |
|
||||
| keepLINK | KP-9000-9XHP-X | No | [2M-PCB51-ML-V1.1](devices/2M-PCB51-ML-V1_1.md) | 4M | 8 + 1 |
|
||||
| LIANGUO | HYWS-SGT0108S | No | [2G5F_20G_V1.01](devices/HYWS-SGT0108S.md) | 0.5M | 8 + 1 |
|
||||
| LIANGUO | SWTG024AS | No | [SWTG024AS-v2.0-17452](devices/SWTG024AS.md) | 0.5M | 4 + 2 |
|
||||
| LIANGUO | ZX-SWTGW215AS | Yes | [PCB-SWTG115AS-V2.0 / PCB-SWTG115AS-V2.1](devices/SWTGW215AS.md) | 2M | 5 + 1 |
|
||||
@@ -38,18 +39,23 @@ The following devices have been tested and are fully working:
|
||||
| MokerLink| 2G08110GSN | No | [SWTG018AS-A V2.0](devices/SWTG018AS_A_V2_0.md) | 0.5M | 8 + 1 |
|
||||
| Netcore | GS9 | No | [PB-2132](devices/PB-2132.md) | 4M | 8 + 1 |
|
||||
| ONT | ONT-S207CW-62TS-SE | No | [PCB-SWTG024AS-A-2.0.1](devices/ONT-S207CW-62TS-SE.md) | 0.5M | 4 + 2 |
|
||||
| RealHD | PSW8-25MG | Yes | [PCB-AFGW SWTG218ANS V2.0.2](devices/PCB-AFGW_SWTG218ANS_V2.0.2.md) | 2M | 8 + 1 |
|
||||
| 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
|
||||
|
||||
@@ -79,8 +79,11 @@ JSON polling of the visible section).
|
||||
computation no longer depends on uninitialised buffer content.
|
||||
- `version.h` is now written by a single atomic `printf` (change from
|
||||
upstream) instead of five separate `echo`s, so a parallel build
|
||||
(`make -j`) can no longer read a half-written copy. It stays in
|
||||
`.PHONY` so it is regenerated on every build.
|
||||
(`make -j`) can no longer read a half-written copy. It depends on
|
||||
`FORCE`, so its recipe runs on every build, but the file is only
|
||||
rewritten when its content changes. The minified copy in
|
||||
`output/html_min/` is redone only when a web file, the list of web
|
||||
files or `tools/minify.py` changes.
|
||||
|
||||
## Verification
|
||||
|
||||
|
||||
@@ -16,7 +16,7 @@ d_system:"System",d_traffic:"Traffic",
|
||||
d_traffic_h:"packets/s, live",d_txpps:"TX pps",d_rxpps:"RX pps",d_txbad:"TX bad",d_rxbad:"RX bad",
|
||||
i_host:"Hostname",i_ip:"IP address",i_mask:"Netmask",i_gw:"Gateway",i_mac:"MAC",i_fw:"Firmware",
|
||||
i_built:"Built",i_hw:"Hardware",i_temp:"Temperature",i_flash:"Flash",i_syslog:"Syslog",
|
||||
p_state:"State",p_disabled:"disabled",p_up:"up",p_txgb:"TX good / bad",p_rxgb:"RX good / bad",
|
||||
p_state:"State",p_disabled:"disabled",p_up:"up",
|
||||
p_pkts:"pkts",p_module:"Module",p_temp:"Temperature",p_vcc:"Vcc",p_txbias:"TX bias",p_txpower:"TX power",
|
||||
p_rxpower:"RX power",p_txfault:"TX fault",p_txdis:"TX disabled",p_rxlos:"RX LOS",p_adv:"Advertising",
|
||||
pt_title:"Port configuration",pt_conn:"Devices learned",pt_speed:"Speed",pt_mtu:"MTU",
|
||||
@@ -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?",
|
||||
@@ -147,7 +151,7 @@ d_system:"システム",d_traffic:"トラフィック",
|
||||
d_traffic_h:"パケット/秒、リアルタイム",d_txpps:"TX pps",d_rxpps:"RX pps",d_txbad:"TX 異常",d_rxbad:"RX 異常",
|
||||
i_host:"ホスト名",i_ip:"IP アドレス",i_mask:"ネットマスク",i_gw:"ゲートウェイ",i_mac:"MAC",i_fw:"ファームウェア",
|
||||
i_built:"ビルド日",i_hw:"ハードウェア",i_temp:"温度",i_flash:"フラッシュ",i_syslog:"Syslog",
|
||||
p_state:"状態",p_disabled:"無効",p_up:"アップ",p_txgb:"TX 正常 / 異常",p_rxgb:"RX 正常 / 異常",
|
||||
p_state:"状態",p_disabled:"無効",p_up:"アップ",
|
||||
p_pkts:"pkts",p_module:"モジュール",p_temp:"温度",p_vcc:"電圧",p_txbias:"TX バイアス",p_txpower:"TX 電力",
|
||||
p_rxpower:"RX 電力",p_txfault:"TX 障害",p_txdis:"TX 無効",p_rxlos:"RX 信号ロス",p_adv:"アドバタイズ",
|
||||
pt_title:"ポート設定",pt_conn:"学習済みデバイス",pt_speed:"速度",pt_mtu:"MTU",
|
||||
@@ -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:"エントリ",
|
||||
@@ -260,7 +262,7 @@ d_system:"系统",d_traffic:"流量",
|
||||
d_traffic_h:"包/秒,实时",d_txpps:"TX pps",d_rxpps:"RX pps",d_txbad:"TX 错误",d_rxbad:"RX 错误",
|
||||
i_host:"主机名",i_ip:"IP 地址",i_mask:"子网掩码",i_gw:"网关",i_mac:"MAC",i_fw:"固件",
|
||||
i_built:"构建日期",i_hw:"硬件",i_temp:"温度",i_flash:"Flash",i_syslog:"Syslog",
|
||||
p_state:"状态",p_disabled:"已禁用",p_up:"已连接",p_txgb:"TX 正常 / 错误",p_rxgb:"RX 正常 / 错误",
|
||||
p_state:"状态",p_disabled:"已禁用",p_up:"已连接",
|
||||
p_pkts:"个包",p_module:"模块",p_temp:"温度",p_vcc:"供电电压",p_txbias:"TX 偏置电流",p_txpower:"TX 光功率",
|
||||
p_rxpower:"RX 光功率",p_txfault:"TX 故障",p_txdis:"TX 禁用",p_rxlos:"RX 信号丢失",p_adv:"通告能力",
|
||||
pt_title:"端口配置",pt_conn:"已学习设备",pt_speed:"速率",pt_mtu:"MTU",
|
||||
@@ -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:"条",
|
||||
@@ -373,7 +373,7 @@ d_system:"Sistema",d_traffic:"Tráfico",
|
||||
d_traffic_h:"paquetes/s, en vivo",d_txpps:"TX pps",d_rxpps:"RX pps",d_txbad:"TX erróneos",d_rxbad:"RX erróneos",
|
||||
i_host:"Nombre de host",i_ip:"Dirección IP",i_mask:"Máscara de red",i_gw:"Puerta de enlace",i_mac:"MAC",i_fw:"Firmware",
|
||||
i_built:"Compilado",i_hw:"Hardware",i_flash:"Flash",i_syslog:"Syslog",
|
||||
p_state:"Estado",p_disabled:"deshabilitado",p_up:"activo",p_txgb:"TX correctos / erróneos",p_rxgb:"RX correctos / erróneos",
|
||||
p_state:"Estado",p_disabled:"deshabilitado",p_up:"activo",
|
||||
p_pkts:"paquetes",p_module:"Módulo",p_temp:"Temperatura",p_vcc:"Vcc",p_txbias:"Polarización TX",p_txpower:"Potencia TX",
|
||||
p_rxpower:"Potencia RX",p_txfault:"Fallo TX",p_txdis:"TX deshabilitado",p_rxlos:"Pérdida RX (LOS)",p_adv:"Anunciado",
|
||||
pt_title:"Configuración de puerto",pt_conn:"Dispositivos aprendidos",pt_speed:"Velocidad",pt_mtu:"MTU",
|
||||
@@ -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,12 +465,13 @@ 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",
|
||||
nav_vlan:"LANs virtuels",nav_l2:"Table MAC",nav_mirror:"Mise en mirroir",nav_lag:"Aggrégation",nav_eee:"Energie",
|
||||
nav_bw:"Bande passante",nav_system:"Système",nav_fw:"Micro logiciel",
|
||||
nav_bw:"Bande passante",nav_system:"Système",nav_fw:"Micro logiciel",nav_lldp:"Couche de Liaison",
|
||||
hdr_dirty:"Changements non sauvegardés",hdr_dirty_t:"La configuration utilisée diffère de celle au démarrage",
|
||||
hdr_save:"Sauvegarder en flash",hdr_save_t:"Maintenir la configuration actuelle en flash",
|
||||
th_auto:"Système",th_auto_sel:"Système (sélénisé)",sy_display:"Affichage",sy_theme:"Thème",sy_display_note:"Enregistré seulement dans ce navigateur.",th_light:"Clair",th_dark:"Sombre",
|
||||
@@ -485,7 +484,7 @@ d_system:"Système",d_traffic:"Trafic",
|
||||
d_traffic_h:"paquets/s (en direct)",d_txpps:"TX pps",d_rxpps:"RX pps",d_txbad:"TX KO",d_rxbad:"RX KO",
|
||||
i_host:"Nom d'hote",i_ip:"Adresse IP",i_mask:"Masque réseau",i_gw:"Passerelle",i_mac:"Adresse MAC",i_fw:"Micro logiciel",
|
||||
i_built:"Compilé le",i_hw:"Matériel",i_temp:"Température",i_flash:"Taille Flash",i_syslog:"Journal système",
|
||||
p_state:"Etat",p_disabled:"Inactif",p_up:"Actif",p_txgb:"TX bon / mauvais",p_rxgb:"RX bon / mauvais",
|
||||
p_state:"Etat",p_disabled:"Inactif",p_up:"Actif",
|
||||
p_pkts:"paquets",p_module:"Module",p_temp:"Température",p_vcc:"Vcc",p_txbias:"TX biais",p_txpower:"TX puissance",
|
||||
p_rxpower:"RX puissance",p_txfault:"TX erreur",p_txdis:"TX désactivé",p_rxlos:"RX pertes de signaux",p_adv:"Capacités",
|
||||
pt_title:"Configuration du port",pt_conn:"Appareils appris",pt_speed:"Vitesse",pt_mtu:"MTU",
|
||||
@@ -509,6 +508,7 @@ stp_en_q:"Activer l'arbrescence?",
|
||||
stp_en_d:"Les ports sont d'abord bloqués et cela prend deux fois le délai de retransmission pour atteindre l'état de retransmission; les ports en bordures recouvrent immédiatement leur état.",
|
||||
stp_dis_q:"Désactiver l'arborescence ?",stp_dis_d:"Tous les ports vont être retransmis; la protection de boucles est perdue.",
|
||||
stp_cost_err:"Le coût du chemin doit être compris entre 0 et 200000000",
|
||||
lldp_enabled: "Activer LLDP",lldp_title:"Protocole de découverte de la couche de liason (LLDP)",lldp_portname:"Nom",lldp_enable:"Envoi de trames",
|
||||
st_title:"Statiqtique du port",st_h:"cumul depuis le démarrage",st_txg:"TX OK",st_txb:"TX KO",st_rxg:"RX OK",
|
||||
st_rxb:"RX KO",st_details:"Détails",st_counters:"Compteur MIB",st_nonzero:"non-zero seulement",
|
||||
st_autoref:"rafraichissement automatique",st_counter:"Compteur",st_value:"Valeur",st_fail:"Erreur au chargement des compteurs",
|
||||
@@ -517,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",
|
||||
@@ -542,6 +540,9 @@ e_idle:"Inactif",e_na:"N/A",
|
||||
bw_title:"Limitations de bande passante",bw_h:"Mbit/s, 0.016-10000",bw_in:"Limite entrante",bw_out:"Limite sortante",
|
||||
bw_exceed:"Lorsque dépassé",bw_fc:"Limiter",bw_drop:"Rejetter",
|
||||
bw_in_err:"La limite entrante doit être comprise entre 0.016 et 10000 Mbit/s",bw_out_err:"La limite sortante doit être comprise entre 0.016 et 10000 Mbit/s",
|
||||
sc_title:"Contrôle des tempêtes",sc_h:"par port et par type de diffusion (générale ou groupée), vide = inactif",sc_bcast:"Générale (Broadcast)",sc_mcast:"Groupée (Multicast)",
|
||||
sc_ucast:"Unicast inconnu",sc_umcast:"Multicast inconnu",sc_err:" La limite de tempête doit être comprise entre 1 à 1048575 pps ou 1 à 10000000 kbit/s",
|
||||
sc_note:"Les trames de chacun des types ci-dessus dépassant les limites sont abandonnées lorsqu'elles arrivent sur le communtateur.",
|
||||
sy_network:"Réseau",sy_dhcp:"Uiliser DHCP",sy_dhcp_t:"Aquérir l'adresse via le protocole DHCP",sy_services:"Services",
|
||||
sy_igmp:"Surveillance IGMP",sy_sysip:"IP du serveur...",sy_server:"Serveur",sy_port:"Port",
|
||||
sy_sesstmo:"Temps mort de session (s)",sy_sesstmo_err:"Le temps morts de session doit être compris en 1-65535 s",
|
||||
@@ -555,7 +556,14 @@ sy_ip_err:"Erreur d'IP / masque réseau / passerelle",sy_host_err:"Nom d'hôte :
|
||||
sy_net_q:"Appliquer les paramètres réseau?",sy_net_d:"L'adresse IP de management va devenir {ip}: merci de réouvrir le page depuis cette adresse.",
|
||||
sy_ip_changed:"L'adresse IP a changée, reconnectez-vous à http://{ip}/",sy_dhcp_q:"Passer en DHCP?",
|
||||
sy_dhcp_d:"Le commutateur va demander une addresse via le DHCP. Vous devrez rechercher sa prochaine adresse IP pour vous reconnecter.",
|
||||
sy_sysip_err:"Adresse IP du serveur Syslog invalide",sy_sysport_err:"Le port Syslog doit être compris entre 1 et 65535",
|
||||
sy_sysip_err:"Adresse IP du serveur Syslog invalide",sy_dns1:"DNS principal",sy_dns2:"DNS secondaire",sy_dns_err:"Adresse IP du DNS invalide",sy_dns_dhcp:"émis par le DHCP: {ip}",
|
||||
sy_time:"Temps",sy_ntp:"NTP",sy_ntpsrv:"Serveur NTP",sy_ntpsrv_p:"Nom d'hôte ou adresse IPv4",sy_ntpint:"intervalle de scrutation [min]",
|
||||
sy_tz:"Fuseau horaire",sy_dst:"Heure d'été",sy_dst_eu:"Europe",
|
||||
sy_dst_us:"Etats-Unis",sy_now:"Heure locale",
|
||||
sy_sync:"Dernière synchronisation",sy_ntpaddr:"Adresse du serveur",sy_nosync:"Désynchronisé",sy_waiting:"En attente du serveur",
|
||||
sy_ago:"depuis {n} minutes",sy_ntp_err:"Entrez le nom d'hôte ou l'adresse IPv4 du serveur NTP",sy_ntpint_err:"L'intervalle de scrutation doit être compris entre 1 et 1440",
|
||||
sy_time_note:"Le commutateur n'a pas d'horloge sauvegardée par batterie: L'heure est donc perdue à chaque redémarrage et doit être récupérée grâce au serveur NTP une fois le réseau fonctionnel. Sauvegardez en mémoire flash pour enregistrer les paramètres.",
|
||||
sy_sysport_err:"Le port Syslog doit être compris entre 1 et 65535",
|
||||
sy_pw_len:"Mot de passe : 1 à 20 caractères",sy_pw_space:"Le mot de passe ne doit pas contenir d'espaces",sy_pw_match:"Les mots de passe ne correspondent pas",
|
||||
sy_pw_q:"Changer le mot de passe d'administration?",sy_pw_d:"Cela prend effet immédiatement dès la prochaine connection. Enregistrez en Flash pour rendre la modification persistante.",
|
||||
sy_reboot_q:"Redémarrer le commutateur ?",sy_reboot_d:"Il y a des paramètres NON SAUVEGARDES qui vont être perdus. Sauvegardez-les au préalable en mémoire flash si vous souhaitez les conserver.",
|
||||
@@ -602,7 +610,7 @@ d_system:"System",d_traffic:"Datenverkehr",
|
||||
d_traffic_h:"Pakete/s, live",d_txpps:"TX Pakete/s",d_rxpps:"RX Pakete/s",d_txbad:"TX fehlerhaft",d_rxbad:"RX fehlerhaft",
|
||||
i_host:"Hostname",i_ip:"IP-Adresse",i_mask:"Netzmaske",i_gw:"Gateway",i_mac:"MAC",i_fw:"Firmware",
|
||||
i_built:"Erstellt",i_hw:"Hardware",i_temp:"Temperatur",i_flash:"Flash",i_syslog:"Syslog",
|
||||
p_state:"Zustand",p_disabled:"deaktiviert",p_up:"verbunden",p_txgb:"TX gut / fehlerhaft",p_rxgb:"RX gut / fehlerhaft",
|
||||
p_state:"Zustand",p_disabled:"deaktiviert",p_up:"verbunden",
|
||||
p_pkts:"Pakete",p_module:"Modul",p_temp:"Temperatur",p_vcc:"Vcc",p_txbias:"TX-Bias",p_txpower:"Sendeleistung",
|
||||
p_rxpower:"Empfangsleistung",p_txfault:"TX-Fehler",p_txdis:"TX deaktiviert",p_rxlos:"RX LOS",p_adv:"Angeboten",
|
||||
pt_title:"Port-Konfiguration",pt_conn:"Gelernte Geräte",pt_speed:"Geschwindigkeit",pt_mtu:"MTU",
|
||||
@@ -633,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?",
|
||||
@@ -800,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;
|
||||
@@ -836,6 +862,7 @@ function isConfCmd(line){
|
||||
function setDirty(d){
|
||||
S.dirty=d;
|
||||
$("dirty").classList.toggle("show",d);
|
||||
$("saveBtn").classList.toggle("pri",d);
|
||||
}
|
||||
function postCmd(cmd,quiet){
|
||||
return api("/cmd",{method:"POST",body:cmd}).then(function(r){
|
||||
@@ -850,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);
|
||||
@@ -936,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;
|
||||
@@ -957,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();
|
||||
@@ -1024,8 +1051,10 @@ function portRows(p){
|
||||
var rows=[[t("c_port"),String(p.portNum)],[t("c_type"),p.isSFP?"SFP":"RJ45"]];
|
||||
if(p.name)rows.push([t("c_name"),p.name]);
|
||||
rows.push([t("p_state"),!p.enabled?t("p_disabled"):(p.link>0?t("p_up")+" "+LINKS[p.link]:t("c_down"))]);
|
||||
rows.push([t("p_txgb"),BigInt(p.txG)+" / "+BigInt(p.txB)+" "+t("p_pkts")]);
|
||||
rows.push([t("p_rxgb"),BigInt(p.rxG)+" / "+BigInt(p.rxB)+" "+t("p_pkts")]);
|
||||
rows.push([t("st_txg"),BigInt(p.txG)+" "+t("p_pkts")]);
|
||||
rows.push([t("st_txb"),BigInt(p.txB)+" "+t("p_pkts")]);
|
||||
rows.push([t("st_rxg"),BigInt(p.rxG)+" "+t("p_pkts")]);
|
||||
rows.push([t("st_rxb"),BigInt(p.rxB)+" "+t("p_pkts")]);
|
||||
if(p.isSFP){
|
||||
if(p.sfp_vendor)rows.push([t("p_module"),[p.sfp_vendor,p.sfp_model,p.sfp_serial].filter(Boolean).join(" / ")]);
|
||||
var ext=p.sfp_options&0x40;
|
||||
@@ -1202,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();
|
||||
@@ -1225,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){
|
||||
@@ -1239,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");
|
||||
@@ -1250,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;
|
||||
@@ -1268,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){
|
||||
@@ -1307,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);
|
||||
@@ -1325,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()}};
|
||||
|
||||
@@ -1461,131 +1517,282 @@ function rangeStr(list){
|
||||
}
|
||||
return parts.join(", ");
|
||||
}
|
||||
function vlanRefresh(){
|
||||
var tb=$("vtable").tBodies[0];
|
||||
return getJSON("/vlanlist").then(function(d){
|
||||
var vl=d.vlan||[];
|
||||
$("vmgmtcur").textContent=d.mgmt?String(d.mgmt):t("v_mgmt_none");
|
||||
$("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){
|
||||
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.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;
|
||||
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(unt));
|
||||
tr.insertCell().textContent=rangeStr(maskToPorts(pv));
|
||||
|
||||
function vlanRefresh() {
|
||||
var tb = $("vtable").tBodies[0];
|
||||
tb.style="cursor:pointer;"
|
||||
return getJSON("/vlanlist").then(function(d) {
|
||||
var vl = d.vlan || [];
|
||||
$("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) {
|
||||
var tr = tb.insertRow();
|
||||
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,
|
||||
tag = mem & ~unt;
|
||||
var pv = parseInt(d.pvid, 16) & 0x3ff;
|
||||
tr.insertCell().textContent = rangeStr(maskToPorts(mem));
|
||||
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(){
|
||||
confirmModal(t("v_del_q",{n:v.id}),t("v_del_d"),function(){
|
||||
postCmd("vlan "+v.id+" d").then(vlanRefresh).catch(function(){});
|
||||
});
|
||||
}}));
|
||||
}).catch(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");
|
||||
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.querySelectorAll("button").forEach(function(b){b.classList.remove("on")});
|
||||
this.classList.add("on");
|
||||
}}));
|
||||
|
||||
function buildVlanEdit() {
|
||||
var tb = $("vedit").tBodies[0];
|
||||
if (tb.rows.length || !S.n) return;
|
||||
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.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;}
|
||||
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;
|
||||
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);
|
||||
}
|
||||
toast(t("v_loaded",{n:vid}),"ok");
|
||||
}).catch(function(){toast(t("v_notfound",{n:vid}),"err")});
|
||||
}
|
||||
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;
|
||||
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++;}
|
||||
}
|
||||
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(!any){toast(t("v_ing_none"),"err");return;}
|
||||
postCmd(cmd).catch(function(){});
|
||||
}
|
||||
$("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(){})})},
|
||||
};
|
||||
|
||||
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(vid) {
|
||||
if (!vid || vid < 1 || vid > 4094) {
|
||||
toast(t("v_vid_err"), "err");
|
||||
return;
|
||||
}
|
||||
getJSON("/vlan.json?vid=" + vid).then(function(d) {
|
||||
$("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).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 = $("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);
|
||||
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 (thisVlanInStructure.tagged.length + thisVlanInStructure.untagged.length == 0) {
|
||||
toast(t("v_nomember"), "err");
|
||||
return;
|
||||
}
|
||||
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);
|
||||
}
|
||||
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;
|
||||
@@ -2243,14 +2450,14 @@ var CONF_OVERWRITE=[
|
||||
/^ntp\s+server\b/,/^ntp\s+interval\b/,/^ntp\s+timezone\b/,/^ntp\s+dst\b/,
|
||||
/^vlan\s+\d{1,4}\s+mgmt$/,/^vlan\s+\d{1,4}(?!\s+mgmt\b)/,
|
||||
/^pvid\s+\d{1,2}\b/,
|
||||
/^port\s+\d{1,2}(?!\s+name\b)/,/^port\s+\d{1,2}\s+name\b/,
|
||||
/^port\s+\d{1,2}(?!\s+(name|lldp)\b)/,/^port\s+\d{1,2}\s+name\b/,
|
||||
/^mirror\b/,
|
||||
/^lag\s+\d\b/,/^laghash\s+\d\b/,/^isolate\s+\d{1,2}\b/,
|
||||
/^stp\s+(prio|hello|maxage|fwd|txhold|version)\b/,
|
||||
/^stp\s+(port\s+\d{1,2}|lag\s+[1-4])\s+(edge|cost|prio|guard|filter|p2p)\b/,
|
||||
/^igmp\b/,/^mtu\s+\d{1,2}\b/,/^storm\s+\d{1,2}\s+(bcast|mcast|ucast|umcast)\b/,
|
||||
];
|
||||
var CONF_TOGGLE=[/^(syslog)\s+(on|off)$/,/^(ntp)\s+(on|off)$/,/^(stp)\s+(on|off)$/,/^(stp\s+(port\s+\d{1,2}|lag\s+[1-4]))\s+(on|off)$/];
|
||||
var CONF_TOGGLE=[/^(syslog)\s+(on|off)$/,/^(ntp)\s+(on|off)$/,/^(stp)\s+(on|off)$/,/^(lldp)\s+(on|off)$/,/^(stp\s+(port\s+\d{1,2}|lag\s+[1-4]))\s+(on|off)$/];
|
||||
function mergeConf(base,texts){
|
||||
var conf=base.slice();
|
||||
function drop(rx){conf=conf.filter(function(c){return!rx.test(c)})}
|
||||
@@ -2268,6 +2475,7 @@ function mergeConf(base,texts){
|
||||
}
|
||||
if((m=line.match(/^lag (\d) lacp off$/))){drop(new RegExp("^lag "+m[1]+" lacp( |$)"));return;}
|
||||
if(line==="mirror off"){drop(/^mirror /);return;}
|
||||
if((m=line.match(/^(port \d{1,2} lldp) (permit|block)$/))){drop(new RegExp("^"+m[1]+" (permit|block)$"));conf.push(line);return;}
|
||||
if(!isConfCmd(line))return;
|
||||
if((m=line.match(/^ingress (.+)$/))){
|
||||
if(/^[tua]$/.test(m[1])){drop(/^ingress /);conf.push(line);return;}
|
||||
@@ -2300,7 +2508,7 @@ function mergeConf(base,texts){
|
||||
var key=line.match(rx)[0];
|
||||
conf=conf.filter(function(item){
|
||||
return!(item===key||(item.indexOf(key+" ")===0
|
||||
&&!/\smgmt$/.test(item)&&item.indexOf(key+" name ")!==0));
|
||||
&&!/\smgmt$/.test(item)&&item.indexOf(key+" name ")!==0&&item.indexOf(key+" lldp ")!==0));
|
||||
});
|
||||
break;
|
||||
}
|
||||
|
||||
@@ -33,7 +33,7 @@
|
||||
html,body{height:100%}
|
||||
body{
|
||||
font:14px/1.45 system-ui,-apple-system,"Segoe UI",Roboto,sans-serif;
|
||||
background:var(--bg);color:var(--fg);display:flex;
|
||||
background:var(--bg);color:var(--fg);display:flex;overflow:hidden;
|
||||
}
|
||||
button,input,select,textarea{font:inherit;color:var(--fg)}
|
||||
a{color:var(--ac);text-decoration:none}
|
||||
@@ -72,8 +72,8 @@ select.ctl,button.ctl{
|
||||
button.pri{background:var(--ac);color:var(--acfg);border-color:transparent;font-weight:600}
|
||||
button.pri:hover,button.ctl:hover{filter:brightness(1.07)}
|
||||
button:disabled{opacity:.45;cursor:default;filter:none}
|
||||
main{padding:18px 20px 40px;max-width:1400px;width:100%}
|
||||
section.tab{display:none}
|
||||
main{padding:18px 20px 40px;width:100%;overflow-y:auto;}
|
||||
section.tab{display:none;max-width:1400px;}
|
||||
section.tab.act{display:block}
|
||||
.card{
|
||||
background:var(--panel);border:1px solid var(--bd);border-radius:10px;
|
||||
@@ -89,9 +89,11 @@ table.t th{
|
||||
padding:6px 8px;border-bottom:1px solid var(--bd);white-space:nowrap;
|
||||
}
|
||||
table.t th[data-sort]{cursor:pointer;user-select:none}
|
||||
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{
|
||||
@@ -123,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)}
|
||||
@@ -223,7 +233,7 @@ button.danger{background:var(--bad);color:#fff;border-color:transparent}
|
||||
<span id="ttitle"></span>
|
||||
<div id="strip"></div>
|
||||
<span id="dirty" data-i18n="hdr_dirty" data-i18n-t="hdr_dirty_t">unsaved changes</span>
|
||||
<button class="ctl pri" id="saveBtn" data-i18n="hdr_save" data-i18n-t="hdr_save_t">Save to flash</button>
|
||||
<button class="ctl" id="saveBtn" data-i18n="hdr_save" data-i18n-t="hdr_save_t">Save to flash</button>
|
||||
</header>
|
||||
|
||||
<main>
|
||||
@@ -254,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>
|
||||
@@ -271,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">
|
||||
@@ -285,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">
|
||||
@@ -437,8 +448,8 @@ button.danger{background:var(--bad);color:#fff;border-color:transparent}
|
||||
<p class="small mut" style="margin-top:8px" data-i18n="sy_time_note">The switch has no battery backed clock.</p>
|
||||
</div>
|
||||
<div class="card"><h2 data-i18n="sy_password">Admin password</h2>
|
||||
<label class="row"><span class="lb" data-i18n="sy_newpw">New password</span><input class="in" id="sy-pw1" type="password" maxlength="20"></label>
|
||||
<label class="row"><span class="lb" data-i18n="sy_repeat">Repeat</span><input class="in" id="sy-pw2" type="password" maxlength="20"></label>
|
||||
<label class="row"><span class="lb" data-i18n="sy_newpw">New password</span><input class="in" id="sy-pw1" type="password" autocomplete="new-password" maxlength="20"></label>
|
||||
<label class="row"><span class="lb" data-i18n="sy_repeat">Repeat</span><input class="in" id="sy-pw2" type="password" autocomplete="new-password" maxlength="20"></label>
|
||||
<button class="ctl pri" id="sy-pwapply" style="margin-top:8px" data-i18n="sy_pwapply">Change password</button>
|
||||
<p class="small mut" style="margin-top:8px" data-i18n="sy_pw_note">Takes effect immediately; save to flash to keep it after reboot.</p>
|
||||
</div>
|
||||
|
||||
@@ -6,12 +6,12 @@
|
||||
#include "cmd_parser.h"
|
||||
#include "rtl837x_flash.h"
|
||||
#include "uip.h"
|
||||
#include "sessions.h"
|
||||
#include "html_data.h"
|
||||
|
||||
// #define DEBUG
|
||||
#include "debug.h"
|
||||
|
||||
#define SESSION_ID_LENGTH 12
|
||||
#define SESSION_TIMEOUT_DEFAULT 200
|
||||
|
||||
/* Seconds of inactivity after which a session cookie stops validating.
|
||||
@@ -75,10 +75,9 @@ __xdata uint8_t postbody_endpoint;
|
||||
__xdata uint16_t postbody_start;
|
||||
|
||||
__xdata char passwd[21];
|
||||
__xdata char session_id[SESSION_ID_LENGTH + 1];
|
||||
// Up to four logins at once, see sessions.c
|
||||
__xdata uint8_t authenticated;
|
||||
__xdata uint32_t now;
|
||||
__xdata uint32_t last_session_use;
|
||||
|
||||
#define TSTATE_NONE 0
|
||||
#define TSTATE_TX 1
|
||||
@@ -109,6 +108,7 @@ void httpd_init(void) __banked
|
||||
// handling one, and nothing has set it yet at init time.
|
||||
for (uint8_t i = 0; i < UIP_CONNS; i++)
|
||||
uip_conns[i].appstate.tstate = TSTATE_CLOSED;
|
||||
session_init();
|
||||
}
|
||||
|
||||
|
||||
@@ -343,38 +343,11 @@ __xdata uint8_t *scan_header(__xdata uint8_t * __xdata p)
|
||||
|
||||
read_reg_timer(&now);
|
||||
|
||||
if (session && session_id[0]) {
|
||||
if (now - last_session_use > session_timeout) {
|
||||
dbg_string("Session expired\n");
|
||||
} else {
|
||||
if (is_word_x(session, session_id)) {
|
||||
authenticated = 1;
|
||||
last_session_use = now;
|
||||
} else {
|
||||
dbg_string("Invalid session cookie!\n");
|
||||
}
|
||||
}
|
||||
}
|
||||
if (session)
|
||||
session_check();
|
||||
return p;
|
||||
}
|
||||
|
||||
/*
|
||||
* Generate random HEX-chars at the buffer location.
|
||||
*/
|
||||
void gen_random_hex_chars(__xdata uint8_t * b, __xdata uint8_t bytes)
|
||||
{
|
||||
uint8_t i = 0;
|
||||
while (bytes) {
|
||||
if (!i)
|
||||
get_random_32();
|
||||
b[--bytes] = itohex(sfr_data[i]);
|
||||
if (!bytes) { break; }
|
||||
b[--bytes] = itohex(sfr_data[i] >> 4 | sfr_data[i] << 4);
|
||||
i = (i + 1) & 0x3;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/* 0: body incomplete, 1: configuration stored, 2: malformed */
|
||||
static uint8_t config_take(void)
|
||||
{
|
||||
@@ -448,15 +421,9 @@ static uint8_t config_take(void)
|
||||
// unlike scan_header(), keeps no auth state, so it may run on every buffered segment
|
||||
static uint16_t preamble_payload_start(uint16_t n)
|
||||
{
|
||||
uint16_t pos;
|
||||
// Skip the HTTP header separator retained by handle_post().
|
||||
uint16_t pos = 4;
|
||||
|
||||
for (pos = 0; pos + 24 <= n; pos++) {
|
||||
if (strstart(&config_buf[pos], "application/octet-stream"))
|
||||
break;
|
||||
}
|
||||
if (pos + 24 > n)
|
||||
return 0;
|
||||
pos += 24;
|
||||
while (pos + 3 < n && !strstart(&config_buf[pos], "\r\n\r\n"))
|
||||
pos++;
|
||||
if (pos + 3 >= n)
|
||||
@@ -537,9 +504,10 @@ uint8_t stream_upload(void)
|
||||
memcpy(flash_buf + write_len, boundary, bindex);
|
||||
write_len += bindex;
|
||||
bindex = 0;
|
||||
} else {
|
||||
crc16_bank1(upload_settings.p + upload_settings.bptr);
|
||||
flash_buf[write_len++] = upload_settings.p[upload_settings.bptr++];
|
||||
}
|
||||
crc16_bank1(upload_settings.p + upload_settings.bptr);
|
||||
flash_buf[write_len++] = upload_settings.p[upload_settings.bptr++];
|
||||
if (write_len >= FLASH_PAGE_SIZE) {
|
||||
/* The staged image spans FIRMWARE_UPLOAD_START to twice that,
|
||||
* the span check_and_flash_update_image() reads back. Nothing
|
||||
@@ -571,7 +539,6 @@ uint8_t stream_upload(void)
|
||||
memcpy(flash_buf, flash_buf + FLASH_PAGE_SIZE, write_len);
|
||||
}
|
||||
}
|
||||
bindex = 0;
|
||||
}
|
||||
} while(1);
|
||||
}
|
||||
@@ -681,13 +648,10 @@ static void run_login_body(__xdata uint8_t *body)
|
||||
{
|
||||
if (strstart(body, "pwd=") && is_url_word_x(body + 4, passwd)) {
|
||||
dbg_string("Password accepted!\n");
|
||||
read_reg_timer(&last_session_use);
|
||||
gen_random_hex_chars(session_id, SESSION_ID_LENGTH);
|
||||
session_id[SESSION_ID_LENGTH] = NUL;
|
||||
read_reg_timer(&now);
|
||||
slen = strtox(outbuf, "HTTP/1.1 302 Found\r\nConnection: close\r\nLocation: index.html\r\n" \
|
||||
"Set-Cookie: session=");
|
||||
for (uint8_t i = 0; i < SESSION_ID_LENGTH; i++)
|
||||
outbuf[slen++] = session_id[i];
|
||||
session_new();
|
||||
slen += strtox(outbuf + slen, "; SameSite=Strict\r\n\r\n");
|
||||
} else {
|
||||
dbg_string("Password invalid!\n");
|
||||
@@ -1091,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;
|
||||
|
||||
@@ -84,10 +84,6 @@ struct sfp_port
|
||||
struct machine {
|
||||
char machine_name[30];
|
||||
uint8_t isRTL8373;
|
||||
// Lowest logical port number
|
||||
uint8_t min_port;
|
||||
// Highest logical port number
|
||||
uint8_t max_port;
|
||||
uint8_t n_sfp;
|
||||
uint8_t n_10g;
|
||||
uint8_t log_to_phys_port[9];
|
||||
|
||||
@@ -0,0 +1,31 @@
|
||||
#include "machine_defs.h"
|
||||
|
||||
__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},
|
||||
.is_sfp = {0, 0, 0, 0, 0, 0, 0, 0, 1},
|
||||
.sfp_port[0].pin_detect = GPIO38,
|
||||
.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 ),
|
||||
.reset_pin = GPIO48_I2C_SCL1,
|
||||
.high_leds = { .mux = LED_28_SYS | LED_29, .enable = LED_28_SYS | LED_29 },
|
||||
.port_led_set = { 0, 0, 0, 0, 0, 0, 0, 0, 1},
|
||||
.led_mux = { 0x00, 0x01, 0x04, 0x05, 0x08, 0x09, 0x0c, 0x09, 0x0d, 0x10, 0x11, 0x0e, 0x14, 0x11, 0x12, 0x15, 0x15, 0x16, 0x18, 0x19, 0x1a, 0x19, 0x1d, 0x1e, 0x1c, 0x1d, 0x20, 0x21 },
|
||||
.led_mux_custom = 1,
|
||||
.led_sets = { { LEDS_2G5 | LEDS_LINK | LEDS_ACT,
|
||||
LEDS_1G | LEDS_100M | LEDS_10M | LEDS_LINK | LEDS_ACT,
|
||||
LEDS_DUPLEX,
|
||||
LEDS_2G5 | LEDS_LINK | LEDS_ACT
|
||||
},
|
||||
{ LEDS_2G5 | LEDS_1G | LEDS_100M | LEDS_LINK | LEDS_ACT,
|
||||
LEDS_10G | LEDS_LINK | LEDS_ACT,
|
||||
LEDS_2G5 | LEDS_LINK,
|
||||
LEDS_COL | LEDS_DUPLEX
|
||||
},
|
||||
},
|
||||
};
|
||||
@@ -0,0 +1,6 @@
|
||||
#include "machine_init_defs.h"
|
||||
|
||||
void machine_custom_init(void) __banked
|
||||
{
|
||||
REG_SET(RTL837X_REG_LED_GLB_IO_EN, 0x7f24977f);
|
||||
}
|
||||
@@ -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 @@
|
||||
PCB_AFGW_SWTG218ANS_V_2_0_2.c
|
||||
@@ -0,0 +1,7 @@
|
||||
#ifndef _MACHINE_SWTG218ANS_V_2_0_2_H_
|
||||
#define _MACHINE_SWTG218ANS_V_2_0_2_H_
|
||||
|
||||
#define MACHINE_MODEL "RealHD PSW8-25MG"
|
||||
#define MACHINE_CHIPSET RTL8373
|
||||
|
||||
#endif
|
||||
@@ -0,0 +1 @@
|
||||
PCB_AFGW_SWTG218ANS_V_2_0_2_init.c
|
||||
@@ -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},
|
||||
|
||||
@@ -43,8 +43,6 @@
|
||||
|
||||
#define MACHINE_COMMON_SETTING \
|
||||
.machine_name = MACHINE_MODEL, \
|
||||
.isRTL8373 = MACHINE_CHIPSET == RTL8373, \
|
||||
.min_port = MACHINE_MIN_PORT, \
|
||||
.max_port = MACHINE_MAX_PORT
|
||||
.isRTL8373 = MACHINE_CHIPSET == RTL8373
|
||||
|
||||
#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
|
||||
@@ -154,6 +156,7 @@ void print_long(uint32_t a);
|
||||
void print_short(uint16_t a);
|
||||
void print_byte(uint8_t a);
|
||||
void itoa(uint8_t v);
|
||||
void itoa_no_syslog(uint8_t v);
|
||||
void itoa_short(uint16_t v);
|
||||
void print_sfr_data(void);
|
||||
void print_phy_data(void);
|
||||
|
||||
@@ -221,9 +221,11 @@ void rtl8372_init(void) __banked
|
||||
} else if(machine.n_10g == 2) {
|
||||
REG_SET(RTL837X_REG_SDS_MODES, 0x1ad); // Both 10g ports use SDS_QXGMII
|
||||
} else {
|
||||
// Replace the reset value 0xbff (both SDS off) instead of ORing into it:
|
||||
// RTL837X_REG_SEC_COUNTER only holds a written value while SDS 0 is on
|
||||
reg_read_m(RTL837X_REG_SDS_MODES);
|
||||
sfr_mask_data(1, 0, 0x03);
|
||||
sfr_mask_data(0, 0, 0xe2);
|
||||
sfr_mask_data(1, 0xff, 0x03);
|
||||
sfr_mask_data(0, 0xff, 0xe2);
|
||||
reg_write_m(RTL837X_REG_SDS_MODES);
|
||||
}
|
||||
|
||||
|
||||
@@ -271,7 +271,7 @@ void vlan_setup(void) __banked
|
||||
wait_table_ready();
|
||||
|
||||
// Set PVID 1 for every port. TODO: Skip unused ports!
|
||||
for (uint8_t i = MACHINE_MIN_PORT; i <= MACHINE_MAX_PORT + 1; i++) { // Do this also for the CPU port (+1)
|
||||
for (uint8_t i = MACHINE_MIN_PORT; i <= CPU_PORT; i++) { // CPU MAC is independent of the last user port
|
||||
uint16_t reg = RTL837x_PVID_BASE_REG + ((i >> 1) << 2);
|
||||
#ifdef DEBUG
|
||||
print_byte(i); write_char(':'); write_char(' '); print_short(reg); write_char('=');
|
||||
@@ -307,7 +307,7 @@ void vlan_setup(void) __banked
|
||||
REG_SET(RTL837X_VLAN_L2_LRN_DIS_0, 0);
|
||||
REG_SET(RTL837X_VLAN_L2_LRN_DIS_1, 0);
|
||||
|
||||
// Enable VLAN 1: Ports 0-9, i.e. including the CPU port are untagged members
|
||||
// Enable VLAN 1: user ports untagged, CPU MAC9 tagged (untag bit 19 clear)
|
||||
REG_SET(RTL837x_TBL_DATA_IN_A, machine_detected.isRTL8373? 0x0207ffff : 0x0207e3f8); // 02: Entry valid, 7...: membership
|
||||
|
||||
REG_SET(RTL837X_TBL_CTRL, 0x00010303); // Write VLAN 1
|
||||
|
||||
@@ -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");
|
||||
}
|
||||
@@ -305,6 +305,19 @@ void itoa(uint8_t v)
|
||||
}
|
||||
|
||||
|
||||
void itoa_no_syslog(uint8_t v)
|
||||
{
|
||||
uint8_t t = (v / 100);
|
||||
uint8_t print_zeros = t;
|
||||
if (print_zeros)
|
||||
write_char_no_syslog('0' + t);
|
||||
t = (v / 10) % 10;
|
||||
print_zeros |= t;
|
||||
if (print_zeros)
|
||||
write_char_no_syslog('0' + t);
|
||||
write_char_no_syslog('0' + (v % 10));
|
||||
}
|
||||
|
||||
/* Same as itoa(), one decade wider: enough for a port number. Kept separate
|
||||
* rather than widening itoa() itself, because every existing caller passes a
|
||||
* byte and would start paying for 16-bit divisions it does not need. */
|
||||
@@ -1203,7 +1216,7 @@ static void handle_tick(void)
|
||||
lacp_timers();
|
||||
health_phase(HEALTH_PH_STP);
|
||||
lldp_tick();
|
||||
health_phase(HEALTH_LLDP);
|
||||
health_phase(HEALTH_PH_LLDP);
|
||||
cabletest_tick();
|
||||
}
|
||||
|
||||
@@ -1554,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)
|
||||
{
|
||||
@@ -1564,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
|
||||
@@ -0,0 +1,15 @@
|
||||
https://sourceforge.net/p/sdcc/bugs/4153/
|
||||
|
||||
Index: src/mcs51/ralloc.c
|
||||
===================================================================
|
||||
--- src/mcs51/ralloc.c (revision 17011)
|
||||
+++ src/mcs51/ralloc.c (working copy)
|
||||
@@ -1208,7 +1208,7 @@
|
||||
int change = 0;
|
||||
|
||||
/* if the result has been spilt then cannot share */
|
||||
- if (opsym->isspilt)
|
||||
+ if (result->isspilt || opsym->isspilt)
|
||||
return 0;
|
||||
|
||||
for (;;)
|
||||
@@ -0,0 +1,24 @@
|
||||
https://sourceforge.net/p/sdcc/bugs/4154/
|
||||
|
||||
Index: src/mcs51/ralloc.c
|
||||
===================================================================
|
||||
--- src/mcs51/ralloc.c (revision 17011)
|
||||
+++ src/mcs51/ralloc.c (working copy)
|
||||
@@ -641,6 +641,7 @@
|
||||
spillThis (symbol * sym)
|
||||
{
|
||||
int i;
|
||||
+ bool ownsRegs = bitVectBitValue (_G.regAssigned, sym->key);
|
||||
/* if this is rematerializable or has a spillLocation
|
||||
we are okay, else we need to create a spillLocation
|
||||
for it */
|
||||
@@ -658,7 +659,8 @@
|
||||
{
|
||||
if (sym->regs[i])
|
||||
{
|
||||
- freeReg (sym->regs[i]);
|
||||
+ if (ownsRegs)
|
||||
+ freeReg (sym->regs[i]);
|
||||
sym->regs[i] = NULL;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,130 @@
|
||||
https://sourceforge.net/p/sdcc/bugs/4070/
|
||||
|
||||
Index: sdas/asxxsrc/asexpr.c
|
||||
===================================================================
|
||||
--- sdas/asxxsrc/asexpr.c
|
||||
+++ sdas/asxxsrc/asexpr.c
|
||||
@@ -863,18 +863,59 @@
|
||||
return((n & s_mask) ? (n | ~v_mask) : (n & v_mask));
|
||||
}
|
||||
|
||||
-/*)Function void exprmasks(esp)
|
||||
+/*)Function void exprmasks(n)
|
||||
*
|
||||
* int n T Line Bytes in Address
|
||||
*
|
||||
* The function exprmasks() configures the assembler
|
||||
- * for 16, 24, or 32-Bit Data/Addresses.
|
||||
+ * for 16, 24, or 32-Bit Data/Addresses. The arithmetic
|
||||
+ * precision follows the address width unless a port
|
||||
+ * calls valmasks() afterwards.
|
||||
*
|
||||
* local variables:
|
||||
* none
|
||||
*
|
||||
* global variables:
|
||||
* int a_bytes T Line Bytes in Address
|
||||
+ *
|
||||
+ * functions called:
|
||||
+ * void valmasks() asexpr.c
|
||||
+ *
|
||||
+ * side effects:
|
||||
+ * The address width and arithmetic precision
|
||||
+ * parameters are set.
|
||||
+ */
|
||||
+
|
||||
+void
|
||||
+exprmasks(int n)
|
||||
+{
|
||||
+ switch(n) {
|
||||
+ default:
|
||||
+ n = 2;
|
||||
+ case 2:
|
||||
+ case 3:
|
||||
+ case 4:
|
||||
+ break;
|
||||
+ }
|
||||
+ a_bytes = n;
|
||||
+ valmasks(n);
|
||||
+}
|
||||
+
|
||||
+/*)Function void valmasks(n)
|
||||
+ *
|
||||
+ * int n Bytes of arithmetic precision
|
||||
+ *
|
||||
+ * The function valmasks() configures the 16, 24, or
|
||||
+ * 32-Bit arithmetic precision independently of the
|
||||
+ * T Line address width. A port whose addresses fit
|
||||
+ * in 24 bits but whose symbols may hold 32-bit values
|
||||
+ * (the 8051 __sfr32 address lists, for example) uses
|
||||
+ * exprmasks(3) followed by valmasks(4).
|
||||
+ *
|
||||
+ * local variables:
|
||||
+ * none
|
||||
+ *
|
||||
+ * global variables:
|
||||
* a_uint a_mask Address mask
|
||||
* a_uint s_mask Sign mask
|
||||
* a_uint v_mask Value mask
|
||||
@@ -885,16 +926,13 @@
|
||||
* side effects:
|
||||
* The arithmetic precision parameters are set.
|
||||
*/
|
||||
-
|
||||
+
|
||||
void
|
||||
-exprmasks(int n)
|
||||
+valmasks(int n)
|
||||
{
|
||||
- a_bytes = n;
|
||||
-
|
||||
#ifdef LONGINT
|
||||
- switch(a_bytes) {
|
||||
+ switch(n) {
|
||||
default:
|
||||
- a_bytes = 2;
|
||||
case 2:
|
||||
a_mask = (a_uint) 0x0000FFFFl;
|
||||
s_mask = (a_uint) 0x00008000l;
|
||||
@@ -914,9 +952,8 @@
|
||||
break;
|
||||
}
|
||||
#else
|
||||
- switch(a_bytes) {
|
||||
+ switch(n) {
|
||||
default:
|
||||
- a_bytes = 2;
|
||||
case 2:
|
||||
a_mask = (a_uint) 0x0000FFFF;
|
||||
s_mask = (a_uint) 0x00008000;
|
||||
Index: sdas/asxxsrc/asxxxx.h
|
||||
===================================================================
|
||||
--- sdas/asxxsrc/asxxxx.h
|
||||
+++ sdas/asxxsrc/asxxxx.h
|
||||
@@ -1354,6 +1354,7 @@
|
||||
extern int digit(int c, int r);
|
||||
extern int is_digit(int c, int r);
|
||||
extern void exprmasks(int n);
|
||||
+extern void valmasks(int n);
|
||||
extern void expr(struct expr *esp, int n);
|
||||
extern void binop(int c, struct expr *esp, struct expr *re);
|
||||
extern int is_abs(struct expr *esp);
|
||||
Index: sdas/as8051/i51mch.c
|
||||
===================================================================
|
||||
--- sdas/as8051/i51mch.c
|
||||
+++ sdas/as8051/i51mch.c
|
||||
@@ -782,9 +782,12 @@
|
||||
hilo = 1;
|
||||
|
||||
/*
|
||||
- * Address Space
|
||||
+ * Address Space: 24-bit addresses in the T lines,
|
||||
+ * 32-bit arithmetic so that __sfr32 address lists
|
||||
+ * like 0xa4a5a6a7 keep their most significant byte.
|
||||
*/
|
||||
- exprmasks(3);
|
||||
+ exprmasks(3);
|
||||
+ valmasks(4);
|
||||
|
||||
/*
|
||||
* First time only:
|
||||
@@ -0,0 +1,26 @@
|
||||
#!/bin/sh
|
||||
set -eu
|
||||
V=4.6.0
|
||||
SHA=5fd6a93e5997ce01756868fe35e441095cfb637894a80c262514a634094973b6
|
||||
PREFIX=$1
|
||||
D=$(cd "$(dirname "$0")" && pwd)
|
||||
W=$(mktemp -d)
|
||||
trap 'rm -rf "$W"' EXIT
|
||||
curl -fL -o "$W/sdcc.tar.bz2" "https://downloads.sourceforge.net/project/sdcc/sdcc/$V/sdcc-src-$V.tar.bz2"
|
||||
if command -v sha256sum >/dev/null; then
|
||||
echo "$SHA $W/sdcc.tar.bz2" | sha256sum -c -
|
||||
else
|
||||
echo "$SHA $W/sdcc.tar.bz2" | shasum -a 256 -c -
|
||||
fi
|
||||
tar -C "$W" -xjf "$W/sdcc.tar.bz2"
|
||||
cd "$W/sdcc-$V"
|
||||
for p in "$D"/*.patch; do
|
||||
patch -p0 < "$p"
|
||||
done
|
||||
off=""
|
||||
for p in z80 z180 r2k r2ka r3ka r4k r5k r6k sm83 tlcs90 ez80 ez80_z80 z80n r800 ds390 ds400 pic14 pic16 hc08 s08 stm8 pdk13 pdk14 pdk15 mos6502 mos65c02 huc6280 f8 f8l; do
|
||||
off="$off --disable-$p-port"
|
||||
done
|
||||
./configure --prefix="$PREFIX" $off --disable-sdcdb --disable-ucsim --disable-non-free
|
||||
make -j"$(getconf _NPROCESSORS_ONLN)"
|
||||
make install
|
||||
@@ -0,0 +1,128 @@
|
||||
/*
|
||||
* Login sessions of the web interface.
|
||||
*
|
||||
* Up to SESSIONS logins at once, each with its own cookie and its own expiry.
|
||||
* With a single one, logging in from a second browser (or a phone) ended the
|
||||
* first browser's session at its next request.
|
||||
*
|
||||
* This sits in bank 3: the web server's own bank is full, so httpd.c only
|
||||
* calls in here. It shares three things with httpd.c: the cookie value it
|
||||
* parsed (session), the time read for the request (now) and the result
|
||||
* (authenticated); the reply is written to outbuf at slen.
|
||||
*/
|
||||
|
||||
#pragma codeseg BANK3
|
||||
#pragma constseg BANK3
|
||||
|
||||
#include <stdint.h>
|
||||
#include "rtl837x_common.h"
|
||||
#include "sessions.h"
|
||||
|
||||
extern volatile __xdata uint8_t sfr_data[4];
|
||||
extern __xdata uint16_t session_timeout;
|
||||
extern __xdata uint32_t now;
|
||||
extern __xdata uint8_t authenticated;
|
||||
extern __xdata uint8_t * __xdata session;
|
||||
extern __xdata uint8_t outbuf[];
|
||||
extern __xdata uint16_t slen;
|
||||
|
||||
__xdata char session_id[SESSIONS][SESSION_ID_LENGTH + 1];
|
||||
__xdata uint32_t last_session_use[SESSIONS];
|
||||
|
||||
static __xdata uint8_t ss_slot, ss_i, ss_oldest, ss_c;
|
||||
/* The slot whose cookie the last request came with; only meaningful while
|
||||
* authenticated is set, which only session_check() does */
|
||||
static __xdata uint8_t ss_hit;
|
||||
|
||||
static __code const char ss_hex[] = "0123456789abcdef";
|
||||
/* What may follow a cookie value: ';' separates cookies in a Cookie header */
|
||||
static __code const char ss_end[] = " \t:?=\n\r;";
|
||||
|
||||
/* Leaf function, so the compiler keeps its 32-bit scratch in the overlay
|
||||
* rather than in internal RAM. */
|
||||
static uint8_t ss_stale(void)
|
||||
{
|
||||
if (now - last_session_use[ss_slot] > session_timeout)
|
||||
return 1;
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* Likewise a leaf: the 32-bit comparison needs no stack of its own this way */
|
||||
static uint8_t ss_older(void)
|
||||
{
|
||||
if (last_session_use[ss_slot] < last_session_use[ss_oldest])
|
||||
return 1;
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* Whether the cookie value is the id of slot ss_slot */
|
||||
static uint8_t ss_match(void)
|
||||
{
|
||||
for (ss_i = 0; ss_i < SESSION_ID_LENGTH; ss_i++) {
|
||||
if (session[ss_i] != session_id[ss_slot][ss_i])
|
||||
return 0;
|
||||
}
|
||||
ss_c = session[SESSION_ID_LENGTH];
|
||||
if (!ss_c)
|
||||
return 1;
|
||||
for (ss_i = 0; ss_end[ss_i]; ss_i++) {
|
||||
if (ss_c == ss_end[ss_i])
|
||||
return 1;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
void session_init(void) __banked
|
||||
{
|
||||
for (ss_slot = 0; ss_slot < SESSIONS; ss_slot++)
|
||||
session_id[ss_slot][0] = NUL;
|
||||
}
|
||||
|
||||
void session_check(void) __banked
|
||||
{
|
||||
for (ss_slot = 0; ss_slot < SESSIONS; ss_slot++) {
|
||||
if (!session_id[ss_slot][0])
|
||||
continue;
|
||||
if (ss_stale()) {
|
||||
session_id[ss_slot][0] = NUL;
|
||||
} else if (ss_match()) {
|
||||
authenticated = 1;
|
||||
ss_hit = ss_slot;
|
||||
last_session_use[ss_slot] = now;
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void session_new(void) __banked
|
||||
{
|
||||
if (authenticated && session_id[ss_hit][0]) {
|
||||
/* The login came with a cookie that is still valid: its slot gets the
|
||||
* new id. A second slot for the same browser would leave the old one
|
||||
* looking fresh and push out someone else at the next login. */
|
||||
ss_slot = ss_hit;
|
||||
} else {
|
||||
/* A free or expired slot, else the least recently used */
|
||||
ss_oldest = 0;
|
||||
for (ss_slot = 0; ss_slot < SESSIONS; ss_slot++) {
|
||||
if (!session_id[ss_slot][0] || ss_stale())
|
||||
break;
|
||||
if (ss_older())
|
||||
ss_oldest = ss_slot;
|
||||
}
|
||||
if (ss_slot == SESSIONS)
|
||||
ss_slot = ss_oldest;
|
||||
}
|
||||
|
||||
last_session_use[ss_slot] = now;
|
||||
for (ss_i = 0; ss_i < SESSION_ID_LENGTH; ss_i++) {
|
||||
if (!(ss_i & 7))
|
||||
get_random_32();
|
||||
ss_c = sfr_data[(ss_i >> 1) & 3];
|
||||
if (ss_i & 1)
|
||||
ss_c >>= 4;
|
||||
session_id[ss_slot][ss_i] = ss_hex[ss_c & 0xf];
|
||||
outbuf[slen++] = ss_hex[ss_c & 0xf];
|
||||
}
|
||||
session_id[ss_slot][SESSION_ID_LENGTH] = NUL;
|
||||
}
|
||||
@@ -0,0 +1,19 @@
|
||||
#ifndef _SESSIONS_H_
|
||||
#define _SESSIONS_H_
|
||||
|
||||
#include <stdint.h>
|
||||
|
||||
#define SESSIONS 4
|
||||
#define SESSION_ID_LENGTH 12
|
||||
|
||||
extern __xdata char session_id[SESSIONS][SESSION_ID_LENGTH + 1];
|
||||
extern __xdata uint32_t last_session_use[SESSIONS];
|
||||
|
||||
/* No session at boot */
|
||||
void session_init(void) __banked;
|
||||
/* Sets authenticated if the cookie in session belongs to a live session */
|
||||
void session_check(void) __banked;
|
||||
/* Starts a session at now and writes its id to outbuf at slen */
|
||||
void session_new(void) __banked;
|
||||
|
||||
#endif
|
||||
@@ -29,6 +29,9 @@ TESTS = $(BUILDDIR)/test_cmd_editor \
|
||||
$(BUILDDIR)/test_page_json \
|
||||
$(BUILDDIR)/test_httpd_tx \
|
||||
$(BUILDDIR)/test_tick_gate \
|
||||
$(BUILDDIR)/test_cpu_vlan_full \
|
||||
$(BUILDDIR)/test_cpu_vlan_restricted \
|
||||
$(BUILDDIR)/test_cpu_vlan_8372 \
|
||||
$(BUILDDIR)/test_nic_rx \
|
||||
$(BUILDDIR)/test_lacp
|
||||
|
||||
@@ -69,6 +72,14 @@ $(BUILDDIR)/test_lacp: test_lacp.c $(SRCROOT)/rtl837x_lacp.c | $(BUILDDIR)
|
||||
# this test carries the environment below them itself.
|
||||
$(BUILDDIR)/test_httpd_tx: test_httpd_tx.c $(SRCROOT)/httpd/httpd.c $(SRCROOT)/uip/uip.c | $(BUILDDIR)
|
||||
$(CC) $(CFLAGS) $(filter %.c,$^) -o $@ $(LDFLAGS)
|
||||
# the login sessions httpd.c calls into
|
||||
$(BUILDDIR)/test_httpd_tx: $(SRCROOT)/sessions.c
|
||||
|
||||
# CPU VLAN initialization with full and reduced user-port ranges.
|
||||
$(BUILDDIR)/test_cpu_vlan_restricted: CFLAGS += -DMACHINE_MAX_PORT=7
|
||||
$(BUILDDIR)/test_cpu_vlan_8372: CFLAGS += -DMACHINE_MAX_PORT=7 -DMACHINE_MIN_PORT=3 -DRTLP_TEST_IS8373=0
|
||||
$(BUILDDIR)/test_cpu_vlan_full $(BUILDDIR)/test_cpu_vlan_restricted $(BUILDDIR)/test_cpu_vlan_8372: test_cpu_vlan.c $(TABLE_ENV) | $(BUILDDIR)
|
||||
$(CC) $(CFLAGS) $(filter %.c,$^) -o $@ $(LDFLAGS)
|
||||
|
||||
run: $(TESTS)
|
||||
@fail=0; for t in $(TESTS); do \
|
||||
|
||||
@@ -57,15 +57,16 @@ const uint8_t hex_chars[] = "0123456789abcdef";
|
||||
uint8_t *hex = (uint8_t *)hex_chars;
|
||||
|
||||
/* ---- a 9-port RTL8373 whose logical ports map to themselves ---- */
|
||||
#ifndef RTLP_TEST_IS8373
|
||||
#define RTLP_TEST_IS8373 1
|
||||
#endif
|
||||
const struct machine machine = {
|
||||
.machine_name = "HOSTTEST",
|
||||
.min_port = 0,
|
||||
.max_port = 8,
|
||||
.n_sfp = 0,
|
||||
.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 },
|
||||
};
|
||||
struct machine_runtime machine_detected = { .isRTL8373 = 1 };
|
||||
struct machine_runtime machine_detected = { .isRTL8373 = RTLP_TEST_IS8373 };
|
||||
|
||||
/* ---- firmware state the modules read or write ---- */
|
||||
uint8_t outbuf[TCP_OUTBUF_SIZE];
|
||||
@@ -120,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
|
||||
@@ -0,0 +1,24 @@
|
||||
#include <stdint.h>
|
||||
#include <stdio.h>
|
||||
#include "rtl837x_common.h"
|
||||
#include "rtl837x_port.h"
|
||||
#include "machine.h"
|
||||
#include "support.h"
|
||||
#include "hw_mock.h"
|
||||
extern const struct machine machine;
|
||||
extern struct machine_runtime machine_detected;
|
||||
int main(void) {
|
||||
hw_reset();out_reset();
|
||||
hw_reg_set(RTL837X_VLAN_PORT_EGR_TAG,0xa50fffff);
|
||||
hw_reg_set(RTL837x_PVID_BASE_REG+16,0x5a123456);
|
||||
vlan_setup();
|
||||
uint32_t untouched=(1u<<(MACHINE_MIN_PORT*2))-1;
|
||||
CHECK(hw_reg_get(RTL837X_VLAN_PORT_EGR_TAG)==(0xa5000000|untouched),"CPU egress set, unrelated bits preserved");
|
||||
CHECK(hw_reg_get(RTL837x_PVID_BASE_REG+16)==0x5a001001,"CPU PVID initialized explicitly");
|
||||
uint16_t active=((1u<<(MACHINE_MAX_PORT+1))-1)&~((1u<<MACHINE_MIN_PORT)-1);
|
||||
CHECK(hw_reg_get(RTL837X_VLAN_PORT_IGR_FLTR)==(active|PMASK_CPU),"CPU ingress included, excluded user ports rejected");
|
||||
CHECK(hw_vlan_word(1)==(machine_detected.isRTL8373?0x0207ffff:0x0207e3f8),"existing family VLAN membership preserved");
|
||||
CHECK(hw_vlan_word(2)==0,"no unrelated VLAN changed");
|
||||
printf("CPU VLAN min=%u max=%u RTL8373=%u: %d checks, %d failed\n",MACHINE_MIN_PORT,MACHINE_MAX_PORT,machine_detected.isRTL8373,tests_run,tests_failed);
|
||||
return tests_failed?1:0;
|
||||
}
|
||||
@@ -1,11 +1,10 @@
|
||||
/*
|
||||
* Host-side bench for the httpd transmit path.
|
||||
* Host-side bench for the httpd transmit and firmware upload paths.
|
||||
*
|
||||
* Compiles the UNMODIFIED httpd/httpd.c and uip/uip.c and drives them through a
|
||||
* real handshake and a real GET from a client that moves its receive window.
|
||||
* real handshake, GETs with moving receive windows, and multipart POSTs.
|
||||
* Everything below the two modules - flash, console, JSON pages - is mocked
|
||||
* here, so what the bench observes is the byte stream the firmware would put on
|
||||
* the wire.
|
||||
* here, so the bench observes the wire response and the bytes staged to flash.
|
||||
*
|
||||
* The file served out of the simulated flash carries a position-dependent
|
||||
* pattern, so a duplicated or skipped range shows up as a mismatch at a known
|
||||
@@ -82,6 +81,9 @@ static uint8_t written_config[CONFIG_LEN];
|
||||
static uint16_t written_config_len;
|
||||
static uint32_t config_erase_addr, config_write_addr;
|
||||
static int config_erases, config_writes;
|
||||
static uint8_t flash_written[1024];
|
||||
static size_t flash_written_len;
|
||||
static int flash_write_bad;
|
||||
|
||||
const char * const mime_strings[] = { "text/html", "image/svg+xml", "image/x-icon",
|
||||
"image/png", "text/javascript", "text/css", "text/plain" };
|
||||
@@ -107,11 +109,21 @@ void flash_sector_erase(void)
|
||||
|
||||
void flash_write_bytes(uint8_t *ptr)
|
||||
{
|
||||
config_writes++;
|
||||
config_write_addr = flash_region.addr;
|
||||
written_config_len = flash_region.len;
|
||||
if (written_config_len <= sizeof(written_config))
|
||||
memcpy(written_config, ptr, written_config_len);
|
||||
if (flash_region.addr < FIRMWARE_UPLOAD_START) {
|
||||
config_writes++;
|
||||
config_write_addr = flash_region.addr;
|
||||
written_config_len = flash_region.len;
|
||||
if (written_config_len <= sizeof(written_config))
|
||||
memcpy(written_config, ptr, written_config_len);
|
||||
return;
|
||||
}
|
||||
if (flash_region.addr != FIRMWARE_UPLOAD_START + flash_written_len ||
|
||||
flash_region.len > sizeof(flash_written) - flash_written_len) {
|
||||
flash_write_bad = 1;
|
||||
return;
|
||||
}
|
||||
memcpy(flash_written + flash_written_len, ptr, flash_region.len);
|
||||
flash_written_len += flash_region.len;
|
||||
}
|
||||
const char *get_flash_size_str(void) { return "512 kB"; }
|
||||
void crc16_bank1(uint8_t *v) { (void)v; }
|
||||
@@ -120,7 +132,9 @@ void reset_chip(void) { reset_calls++; }
|
||||
void delay(uint16_t t) { (void)t; }
|
||||
void write_char(char c) { (void)c; }
|
||||
void write_char_no_syslog(char c) { (void)c; }
|
||||
void itoa_no_syslog(uint8_t v) { (void)v; }
|
||||
void print_string(const char *p) { (void)p; }
|
||||
void print_string_no_syslog(const char *p) { (void)p; }
|
||||
void print_string_newline_no_syslog(const char *p) { (void)p; }
|
||||
void set_sys_led_state(uint8_t state) { (void)state; }
|
||||
void cmd_parser(void) { }
|
||||
@@ -130,7 +144,8 @@ void clear_command_history(void) { }
|
||||
void udp_callbacks(void) { }
|
||||
void tcpip_output(void) { }
|
||||
void get_random_32(void) { }
|
||||
void read_reg_timer(uint32_t *tmr) { *tmr = 0; }
|
||||
static uint32_t fake_now;
|
||||
void read_reg_timer(uint32_t *tmr) { *tmr = fake_now; }
|
||||
|
||||
/* uip-conf.h asks uIP not to define the packet buffer: on the switch it lives
|
||||
* at a fixed XDATA address, so the bench provides the storage itself. */
|
||||
@@ -179,7 +194,9 @@ void send_lldp(void) {}
|
||||
|
||||
extern uint8_t authenticated;
|
||||
extern char session_id[];
|
||||
extern uint32_t last_session_use[];
|
||||
extern char passwd[21];
|
||||
extern uint16_t session_timeout;
|
||||
|
||||
/* uip.c defines the listen table but no header declares it; the bench reads it
|
||||
* to confirm the port httpd_init() asked for is the one uIP is watching. */
|
||||
@@ -535,7 +552,7 @@ static void scenario_bad_l4_checksum(void)
|
||||
|
||||
|
||||
/* Private HTTP lifecycle values; drive the real callback, not a model. */
|
||||
enum { TSTATE_NONE = 0, TSTATE_TX = 1 };
|
||||
enum { TSTATE_NONE = 0, TSTATE_TX = 1, TSTATE_POST = 4 };
|
||||
extern uint16_t slen, cont_len, sent_len, bindex, crc_final, write_len;
|
||||
extern uint8_t boundary[], verify_crc, outbuf[];
|
||||
extern struct { uint8_t *p; uint16_t bptr, plen; } upload_settings;
|
||||
@@ -863,6 +880,77 @@ static void scenario_split_get_header_terminator(void)
|
||||
"fragmented GET terminator: CRLFCRLF may span receive events");
|
||||
}
|
||||
|
||||
/* The sessions of the login tests below: four in use, slot 1 the least recently used */
|
||||
#define ID_STRIDE 13 /* SESSION_ID_LENGTH + 1 */
|
||||
|
||||
static const char *slot_id(int i)
|
||||
{
|
||||
return session_id + i * ID_STRIDE;
|
||||
}
|
||||
|
||||
static void fill_sessions(void)
|
||||
{
|
||||
static const char *const ids[4] = { "0123456789ab", "aaaaaaaaaaaa", "bbbbbbbbbbbb", "cccccccccccc" };
|
||||
static const uint32_t used[4] = { 50, 10, 60, 70 };
|
||||
|
||||
fake_now = 100;
|
||||
for (int i = 0; i < 4; i++) {
|
||||
strcpy(session_id + i * ID_STRIDE, ids[i]);
|
||||
last_session_use[i] = used[i];
|
||||
}
|
||||
}
|
||||
|
||||
static int others_kept(int first, int second, int third)
|
||||
{
|
||||
static const char *const ids[4] = { "0123456789ab", "aaaaaaaaaaaa", "bbbbbbbbbbbb", "cccccccccccc" };
|
||||
|
||||
return !strcmp(slot_id(first), ids[first]) && !strcmp(slot_id(second), ids[second]) &&
|
||||
!strcmp(slot_id(third), ids[third]);
|
||||
}
|
||||
|
||||
static void scenario_login_with_valid_cookie_reuses_its_slot(void)
|
||||
{
|
||||
static const char post[] =
|
||||
"POST /login HTTP/1.1\r\n"
|
||||
"Cookie: session=0123456789ab\r\n"
|
||||
"Content-Length: 8\r\n"
|
||||
"Content-Type: application/x-www-form-urlencoded\r\n"
|
||||
"\r\n"
|
||||
"pwd=1234";
|
||||
|
||||
login_session_start();
|
||||
fill_sessions();
|
||||
client_send(TCP_ACK | TCP_PSH, post, (int)strlen(post));
|
||||
CHECK(stream_contains("Location: index.html\r\n"),
|
||||
"login with a valid cookie: accepted");
|
||||
CHECK(strcmp(slot_id(0), "0123456789ab") != 0,
|
||||
"login with a valid cookie: its own slot gets the new id");
|
||||
CHECK(others_kept(1, 2, 3),
|
||||
"login with a valid cookie: nobody else is pushed out");
|
||||
fake_now = 0;
|
||||
}
|
||||
|
||||
static void scenario_login_without_cookie_replaces_least_recently_used(void)
|
||||
{
|
||||
static const char post[] =
|
||||
"POST /login HTTP/1.1\r\n"
|
||||
"Content-Length: 8\r\n"
|
||||
"Content-Type: application/x-www-form-urlencoded\r\n"
|
||||
"\r\n"
|
||||
"pwd=1234";
|
||||
|
||||
login_session_start();
|
||||
fill_sessions();
|
||||
client_send(TCP_ACK | TCP_PSH, post, (int)strlen(post));
|
||||
CHECK(stream_contains("Location: index.html\r\n"),
|
||||
"login without a cookie: accepted");
|
||||
CHECK(strcmp(slot_id(1), "aaaaaaaaaaaa") != 0,
|
||||
"login without a cookie: the least recently used slot is taken");
|
||||
CHECK(others_kept(0, 2, 3),
|
||||
"login without a cookie: the other three stay");
|
||||
fake_now = 0;
|
||||
}
|
||||
|
||||
static void scenario_fragmented_get_without_cookie(void)
|
||||
{
|
||||
static const char first[] =
|
||||
@@ -1012,6 +1100,171 @@ static void scenario_config_upload(void)
|
||||
"multipart/config: every split writes the exact configuration and NUL to its sector");
|
||||
}
|
||||
|
||||
/* Requiring octet-stream would leave alternate or absent MIME types buffered
|
||||
* as headers forever instead of staging the binary and checking its CRC. */
|
||||
static void scenario_upload(const char *name, const char *part_type, int split)
|
||||
{
|
||||
static const uint8_t payload[] = { 0x00, 0xff, 'R', '\r', '\n', '-', 0x80, 0x00 };
|
||||
static const uint8_t staged[] = { 0x00, 0xff, 'R', '\r', '\n', '-', 0x80, 0x00, 0x00 };
|
||||
static const char closing[] = "\r\n--upload-boundary--\r\n";
|
||||
char preamble[256], request[512];
|
||||
uint32_t saved_flash_size = flash_size;
|
||||
int preamble_len, header_len, request_len;
|
||||
|
||||
printf("\n-- upload: %s --\n", name);
|
||||
session_start(MSS_FULL);
|
||||
strcpy(session_id, "0123456789ab");
|
||||
session_timeout = 200;
|
||||
last_session_use[0] = 0; /* read_reg_timer() returns zero in this bench. */
|
||||
flash_size = 0x100000;
|
||||
flash_written_len = 0;
|
||||
flash_write_bad = 0;
|
||||
reset_calls = 0;
|
||||
|
||||
preamble_len = snprintf(preamble, sizeof(preamble),
|
||||
"--upload-boundary\r\n"
|
||||
"Content-Disposition: form-data; name=\"file\"; filename=\"firmware.bin\"\r\n"
|
||||
"%s\r\n", part_type);
|
||||
header_len = snprintf(request, sizeof(request),
|
||||
"POST /upload HTTP/1.1\r\nHost: sw\r\n"
|
||||
"Cookie: session=0123456789ab\r\n"
|
||||
"Content-Type: multipart/form-data; boundary=upload-boundary\r\n"
|
||||
"Content-Length: %zu\r\n\r\n%s",
|
||||
(size_t)preamble_len + sizeof(payload) + sizeof(closing) - 1, preamble);
|
||||
memcpy(request + header_len, payload, sizeof(payload));
|
||||
memcpy(request + header_len + sizeof(payload), closing, sizeof(closing) - 1);
|
||||
request_len = header_len + sizeof(payload) + sizeof(closing) - 1;
|
||||
if (split) {
|
||||
/* Cut the part's CRLFCRLF between its final CR and LF. */
|
||||
client_send(TCP_ACK | TCP_PSH, request, header_len - 1);
|
||||
CHECK(stream_len == 0 && flash_written_len == 0,
|
||||
"upload: incomplete part headers stage nothing and await more data");
|
||||
client_send(TCP_ACK | TCP_PSH, request + header_len - 1,
|
||||
request_len - header_len + 1);
|
||||
} else {
|
||||
client_send(TCP_ACK | TCP_PSH, request, request_len);
|
||||
}
|
||||
drain(MSS_FULL);
|
||||
|
||||
CHECK(!flash_write_bad && flash_written_len == sizeof(staged) &&
|
||||
!memcmp(flash_written, staged, sizeof(staged)),
|
||||
"upload: exact binary payload plus zero terminator staged at image address");
|
||||
CHECK(stream_len >= 12 && !memcmp(stream, "HTTP/1.1 400", 12),
|
||||
"upload: invalid firmware is rejected with HTTP 400");
|
||||
CHECK(!uip_conn->appstate.fw_reset_pending && reset_calls == 0,
|
||||
"upload: deliberately invalid CRC never schedules or triggers reset");
|
||||
flash_size = saved_flash_size;
|
||||
}
|
||||
|
||||
extern uint32_t uptr;
|
||||
|
||||
#define UPLOAD_LEN 600
|
||||
#define UPLOAD_BOUNDARY "\r\n--XyZ"
|
||||
#define UPLOAD_TRAILER UPLOAD_BOUNDARY "--\r\n"
|
||||
|
||||
static int upload_in_two(uint8_t *body, uint16_t body_len, uint16_t split)
|
||||
{
|
||||
uptr = FIRMWARE_UPLOAD_START;
|
||||
write_len = 0;
|
||||
bindex = 0;
|
||||
verify_crc = 0;
|
||||
flash_written_len = 0;
|
||||
flash_write_bad = 0;
|
||||
strcpy((char *)boundary, UPLOAD_BOUNDARY);
|
||||
uip_conn = &uip_conns[0];
|
||||
uip_conn->appstate.tstate = TSTATE_POST;
|
||||
|
||||
upload_settings.p = body;
|
||||
upload_settings.bptr = 0;
|
||||
upload_settings.plen = split;
|
||||
stream_upload();
|
||||
if (uip_conn->appstate.tstate == TSTATE_POST) {
|
||||
upload_settings.p = body + split;
|
||||
upload_settings.bptr = 0;
|
||||
upload_settings.plen = body_len - split;
|
||||
stream_upload();
|
||||
}
|
||||
return uip_conn->appstate.tstate == TSTATE_NONE
|
||||
&& uptr == FIRMWARE_UPLOAD_START + UPLOAD_LEN && !flash_write_bad
|
||||
&& flash_written_len == UPLOAD_LEN + 1 && !flash_written[UPLOAD_LEN];
|
||||
}
|
||||
|
||||
static void scenario_upload_cr_before_boundary(void)
|
||||
{
|
||||
uint8_t body[UPLOAD_LEN + sizeof(UPLOAD_TRAILER)];
|
||||
uint16_t body_len, split;
|
||||
int completed = 0, intact = 0, splits = 0;
|
||||
|
||||
for (uint16_t i = 0; i < UPLOAD_LEN; i++)
|
||||
body[i] = pattern(i);
|
||||
body[UPLOAD_LEN - 2] = 'x';
|
||||
body[UPLOAD_LEN - 1] = '\r';
|
||||
body_len = UPLOAD_LEN + sizeof(UPLOAD_TRAILER) - 1;
|
||||
memcpy(body + UPLOAD_LEN, UPLOAD_TRAILER, sizeof(UPLOAD_TRAILER) - 1);
|
||||
|
||||
for (split = 0; split <= body_len; split++) {
|
||||
splits++;
|
||||
if (upload_in_two(body, body_len, split)) {
|
||||
completed++;
|
||||
if (!memcmp(flash_written, body, UPLOAD_LEN))
|
||||
intact++;
|
||||
}
|
||||
}
|
||||
if (verbose)
|
||||
printf(" %d of %d splits complete, %d intact\n", completed, splits, intact);
|
||||
|
||||
CHECK(completed == splits,
|
||||
"upload: an image ending in CR still finds the closing boundary");
|
||||
CHECK(intact == splits,
|
||||
"upload: the CR and the bytes before it reach the flash unchanged");
|
||||
}
|
||||
|
||||
static int upload_whole_and_split(uint8_t *body, uint16_t body_len)
|
||||
{
|
||||
return upload_in_two(body, body_len, body_len) && !memcmp(flash_written, body, UPLOAD_LEN)
|
||||
&& upload_in_two(body, body_len, UPLOAD_LEN) && !memcmp(flash_written, body, UPLOAD_LEN);
|
||||
}
|
||||
|
||||
static void scenario_upload_last_byte(void)
|
||||
{
|
||||
uint8_t body[UPLOAD_LEN + sizeof(UPLOAD_TRAILER)];
|
||||
uint16_t body_len = UPLOAD_LEN + sizeof(UPLOAD_TRAILER) - 1;
|
||||
int bad = -1;
|
||||
|
||||
for (uint16_t i = 0; i < UPLOAD_LEN; i++)
|
||||
body[i] = pattern(i);
|
||||
memcpy(body + UPLOAD_LEN, UPLOAD_TRAILER, sizeof(UPLOAD_TRAILER) - 1);
|
||||
for (int last = 0; last < 256 && bad < 0; last++) {
|
||||
body[UPLOAD_LEN - 1] = last;
|
||||
if (!upload_whole_and_split(body, body_len))
|
||||
bad = last;
|
||||
}
|
||||
if (bad >= 0)
|
||||
printf(" last byte 0x%02x loses the closing boundary\n", bad);
|
||||
CHECK(bad < 0, "upload: the part ends after the payload whatever its last byte");
|
||||
|
||||
body[UPLOAD_LEN - 1] = '\r';
|
||||
body[UPLOAD_LEN - 2] = '\r';
|
||||
CHECK(upload_whole_and_split(body, body_len), "upload: an even run of CR before the boundary");
|
||||
body[UPLOAD_LEN - 3] = '\r';
|
||||
CHECK(upload_whole_and_split(body, body_len), "upload: an odd run of CR before the boundary");
|
||||
}
|
||||
|
||||
static void scenario_upload_broken_matches(void)
|
||||
{
|
||||
uint8_t body[UPLOAD_LEN + sizeof(UPLOAD_TRAILER)];
|
||||
uint16_t body_len = UPLOAD_LEN + sizeof(UPLOAD_TRAILER) - 1;
|
||||
|
||||
memcpy(body + UPLOAD_LEN, UPLOAD_TRAILER, sizeof(UPLOAD_TRAILER) - 1);
|
||||
memset(body, '\r', UPLOAD_LEN);
|
||||
CHECK(upload_whole_and_split(body, body_len),
|
||||
"upload: a run of CR longer than a flash page is staged intact");
|
||||
for (uint16_t i = 0; i < UPLOAD_LEN; i++)
|
||||
body[i] = "\r\n-"[i % 3];
|
||||
CHECK(upload_whole_and_split(body, body_len),
|
||||
"upload: repeated partial delimiters are staged as payload");
|
||||
}
|
||||
|
||||
int main(int argc, char **argv)
|
||||
{
|
||||
if (argc > 1 && !strcmp(argv[1], "-v"))
|
||||
@@ -1040,6 +1293,15 @@ int main(int argc, char **argv)
|
||||
scenario_oversized_get_header();
|
||||
scenario_split_method_names();
|
||||
scenario_config_upload();
|
||||
scenario_upload("octet-stream control", "Content-Type: application/octet-stream\r\n", 0);
|
||||
scenario_upload("MacBinary, split part terminator", "Content-Type: application/macbinary\r\n", 1);
|
||||
scenario_upload("no part Content-Type", "", 0);
|
||||
scenario_upload_cr_before_boundary();
|
||||
scenario_upload_last_byte();
|
||||
scenario_upload_broken_matches();
|
||||
|
||||
scenario_login_with_valid_cookie_reuses_its_slot();
|
||||
scenario_login_without_cookie_replaces_least_recently_used();
|
||||
|
||||
printf("\n%s (%d failure%s)\n",
|
||||
failures ? "BENCH: FAILURES" : "BENCH: ALL PASS",
|
||||
|
||||
@@ -14,8 +14,11 @@
|
||||
uint8_t uip_buf[UIP_CONF_BUFFER_ALLOC_SIZE];
|
||||
uint16_t uip_len;
|
||||
struct uip_eth_addr uip_ethaddr = { .addr = {0x02,0x11,0x22,0x33,0x44,0x55} };
|
||||
const struct machine machine = { .min_port = 0, .max_port = 7,
|
||||
const struct machine machine = {
|
||||
.log_to_phys_port = {5, 1, 2, 3, 4, 6, 7, 8, 9} };
|
||||
/* Logical ports of the mock machine above */
|
||||
#define TEST_MIN_PORT 0
|
||||
#define TEST_MAX_PORT 7
|
||||
struct machine_runtime machine_detected = { .isRTL8373 = 1 };
|
||||
uint8_t lacpEnabled;
|
||||
uint16_t management_vlan; /* mocked; slow-protocol path zeroes it per-frame */
|
||||
@@ -216,7 +219,7 @@ int main(int argc, char **argv)
|
||||
lacp_lag_set(0, 0x00ff);
|
||||
step(8);
|
||||
int all_tx = 1, sane = 1;
|
||||
for (int i = machine.min_port; i <= machine.max_port; i++) {
|
||||
for (int i = TEST_MIN_PORT; i <= TEST_MAX_PORT; i++) {
|
||||
if (!tx_count[i]) all_tx = 0;
|
||||
if (last_tx[i].ethertype != HTONS(0x8809) || last_tx[i].subtype != 1
|
||||
|| last_tx[i].dst[5] != 0x02
|
||||
|
||||
@@ -98,6 +98,7 @@
|
||||
#include "../rtl837x_regs.h"
|
||||
|
||||
extern __xdata uint8_t rx_headers[16];
|
||||
extern volatile __xdata uint32_t ticks;
|
||||
|
||||
/*---------------------------------------------------------------------------*/
|
||||
/* Variable definitions. */
|
||||
@@ -243,6 +244,9 @@ __xdata struct uip_stats uip_stat;
|
||||
|
||||
#if UIP_LOGGING == 1
|
||||
#define UIP_LOG(m) print_string_newline_no_syslog(m);
|
||||
#define FRAGDROP_WINDOW (5 * SYS_TICK_HZ)
|
||||
static __xdata u16_t fragdrop_mark;
|
||||
static __xdata u8_t fragdrop_count;
|
||||
#else
|
||||
#define UIP_LOG(m)
|
||||
#endif /* UIP_LOGGING == 1 */
|
||||
@@ -710,6 +714,17 @@ uip_process(u8_t flag) __banked
|
||||
/* Check if we were invoked because of the perodic timer fireing. */
|
||||
} else if(flag == UIP_TIMER) {
|
||||
// print_string("T1\n");
|
||||
#if UIP_LOGGING == 1
|
||||
if(fragdrop_count && (u16_t)ticks - fragdrop_mark >= FRAGDROP_WINDOW) {
|
||||
print_string_no_syslog("ip: fragments dropped: ");
|
||||
itoa_no_syslog(fragdrop_count);
|
||||
if(fragdrop_count == 0xff)
|
||||
write_char_no_syslog('+');
|
||||
write_char_no_syslog('\n');
|
||||
fragdrop_mark = (u16_t)ticks;
|
||||
fragdrop_count = 0;
|
||||
}
|
||||
#endif /* UIP_LOGGING == 1 */
|
||||
#if UIP_IDLE_TIMEOUT
|
||||
if(uip_connr == uip_conns) {
|
||||
uip_idle_age = 0;
|
||||
@@ -915,7 +930,10 @@ uip_process(u8_t flag) __banked
|
||||
#else /* UIP_REASSEMBLY */
|
||||
UIP_STAT(++uip_stat.ip.drop);
|
||||
UIP_STAT(++uip_stat.ip.fragerr);
|
||||
UIP_LOG("ip: fragment dropped.");
|
||||
#if UIP_LOGGING == 1
|
||||
if(fragdrop_count != 0xff)
|
||||
++fragdrop_count;
|
||||
#endif /* UIP_LOGGING == 1 */
|
||||
goto drop;
|
||||
#endif /* UIP_REASSEMBLY */
|
||||
}
|
||||
|
||||