Compare commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
d3f1069285 | ||
|
|
98bda62181 | ||
|
|
3f65db9984 | ||
|
|
df95c544d4 | ||
|
|
7df3c87892 | ||
|
|
53f12a42c5 | ||
|
|
1b514b5ea8 | ||
|
|
dd09be9dd0 | ||
|
|
b2b7f8a0c6 | ||
|
|
5b28ddf80a | ||
|
|
fe3da0c7a5 | ||
|
|
412098d0ed | ||
|
|
5ecce86ef1 | ||
|
|
247b230212 | ||
|
|
c012dda029 | ||
|
|
eab8c901cf | ||
|
|
21a9fdf45a | ||
|
|
1ed12d1101 | ||
|
|
af4101f060 | ||
|
|
eab17f8a3f | ||
|
|
4bcbada764 | ||
|
|
9fae0f9249 | ||
|
|
774e04816e | ||
|
|
ead1c40aac | ||
|
|
6488433584 | ||
|
|
59088b4787 | ||
|
|
30547cc453 | ||
|
|
58aead38cf | ||
|
|
9c3433cf6b | ||
|
|
97d44fbf4b | ||
|
|
00ee3d1325 | ||
|
|
078a446cee | ||
|
|
efdf93a97a | ||
|
|
025e1a186b | ||
|
|
dc5cbcda16 | ||
|
|
12c15603ed | ||
|
|
3a7da77a99 | ||
|
|
13509d56e5 | ||
|
|
54b5e90976 | ||
|
|
38b8e38c04 | ||
|
|
d245249385 | ||
|
|
8b7306f995 | ||
|
|
ecac6d1934 | ||
|
|
f2966ad248 | ||
|
|
8a7b146892 | ||
|
|
0998a9dfaa | ||
|
|
aded07730e | ||
|
|
58d059384d | ||
|
|
a7692022f1 | ||
|
|
e26693826e | ||
|
|
be6bd2a5b3 | ||
|
|
6c43091b7a | ||
|
|
b57b7d1a3d | ||
|
|
fc86ea8be5 | ||
|
|
91780110f2 | ||
|
|
f0e9953081 | ||
|
|
1c9bf10b87 | ||
|
|
2e2529be27 | ||
|
|
f6348765b4 | ||
|
|
6411c5157c | ||
|
|
d06dc3e5f8 | ||
|
|
dd7f9f7c7b | ||
|
|
3b8dba6e87 | ||
|
|
a259587bf9 | ||
|
|
f23d840cee | ||
|
|
f46ef3a43c | ||
|
|
81af3a29a2 | ||
|
|
5a55524115 | ||
|
|
6b39bcc448 | ||
|
|
4c8e0ce975 | ||
|
|
71344825c7 | ||
|
|
40ce742d04 | ||
|
|
f5e4fa5134 | ||
|
|
28020e8186 | ||
|
|
cbcf67a693 | ||
|
|
d6bf46595a | ||
|
|
2499d116a3 | ||
|
|
74a1c6831a | ||
|
|
a79c483f94 | ||
|
|
75aa23468c | ||
|
|
373a21f4a1 | ||
|
|
89b4cf83ce | ||
|
|
005209f97a | ||
|
|
f0f142fbe0 | ||
|
|
daf9d8965a | ||
|
|
58b12c4aa6 | ||
|
|
4ce3e33447 | ||
|
|
ebd27e4763 | ||
|
|
e60a09fb7e | ||
|
|
828963b586 | ||
|
|
944f4ce6f0 | ||
|
|
73d9a8ccb1 | ||
|
|
3a3967960f | ||
|
|
e38abf27c9 | ||
|
|
05ded88f85 | ||
|
|
dc22c4908b | ||
|
|
c0d9bf7f0d | ||
|
|
1947356ec5 | ||
|
|
2fa4686b33 | ||
|
|
bd06f7f5e5 | ||
|
|
b6d2d7c072 | ||
|
|
3c72f0e1b6 | ||
|
|
6be5693385 | ||
|
|
8e9bb56a29 | ||
|
|
e50d3c6950 | ||
|
|
4db1ccbbd9 | ||
|
|
ccafa01c74 | ||
|
|
cb43b3cbd6 | ||
|
|
9d4e531306 | ||
|
|
7f0e7fdc82 | ||
|
|
f187261085 | ||
|
|
aba69cd640 | ||
|
|
b6dcb75130 | ||
|
|
4e04735a5f | ||
|
|
fc3c686564 | ||
|
|
e826180d06 | ||
|
|
b63117ba6c | ||
|
|
be7796a22c | ||
|
|
1bd4a8d3cc | ||
|
|
1db30f22de | ||
|
|
4c70e654d1 | ||
|
|
3c24f2bdea | ||
|
|
3e610392a9 | ||
|
|
f00dbab0c4 | ||
|
|
bc46646398 | ||
|
|
5fa6c36b73 | ||
|
|
146771f5ca | ||
|
|
bf0bea1edd | ||
|
|
907ad08830 | ||
|
|
7cf0115182 | ||
|
|
adfbead438 | ||
|
|
301774040b | ||
|
|
683037f410 | ||
|
|
57b820c4e8 | ||
|
|
75b223223c | ||
|
|
9a6e2b3e01 | ||
|
|
6e394300c8 | ||
|
|
801150431b | ||
|
|
45d0aac118 | ||
|
|
ee05cd4000 | ||
|
|
74013bb14f | ||
|
|
beb14deba5 | ||
|
|
1212def799 | ||
|
|
95b3bde822 | ||
|
|
f511453f79 | ||
|
|
8653216047 | ||
|
|
e62e0fc8bd | ||
|
|
2ff418f80f | ||
|
|
5d42e8843e | ||
|
|
3c183f73f8 | ||
|
|
3776f12f5c | ||
|
|
5ddec9710a | ||
|
|
f627559005 | ||
|
|
dca92d7f45 | ||
|
|
149dc8537f | ||
|
|
42f7b4fba1 | ||
|
|
17642699d4 | ||
|
|
724fd1e061 | ||
|
|
3eb2dfe0c9 | ||
|
|
73df006d39 | ||
|
|
175f3bb185 | ||
|
|
57061161b6 | ||
|
|
80f97a50e0 | ||
|
|
c265a20d83 | ||
|
|
4937618684 | ||
|
|
1a743b3889 | ||
|
|
425283b774 | ||
|
|
f582576b10 | ||
|
|
16b0f207ed | ||
|
|
fe2bcc7f2d | ||
|
|
a0628c7df5 | ||
|
|
cefe48fe51 | ||
|
|
e8e7fcbe7f | ||
|
|
8a28b4fc88 | ||
|
|
9a96c80af2 | ||
|
|
772e9dc526 | ||
|
|
025f72c876 | ||
|
|
62f80f0ad2 | ||
|
|
803d9c1242 | ||
|
|
ec069a5bcc | ||
|
|
81d11c246c | ||
|
|
85d1d7520a | ||
|
|
810db48a00 | ||
|
|
f9d3dec50f | ||
|
|
236eac610d | ||
|
|
9df9eece3f | ||
|
|
3cd9131795 | ||
|
|
50b68f908e | ||
|
|
f1f3d521b5 | ||
|
|
addca1cb26 | ||
|
|
cbd2a8c080 | ||
|
|
78b3971782 | ||
|
|
201c7e333d | ||
|
|
2c26a4f389 | ||
|
|
266c95e4d7 | ||
|
|
38b19820e4 | ||
|
|
988bddacf7 | ||
|
|
4264856109 | ||
|
|
35d26cb08f | ||
|
|
a9af466702 | ||
|
|
024c8cef49 | ||
|
|
64906ad135 | ||
|
|
4d7a2e28c7 | ||
|
|
a6c5f558bf | ||
|
|
5a6b854c5d | ||
|
|
d31980eb9e | ||
|
|
eb4a6ac275 | ||
|
|
1ba1224644 | ||
|
|
4a78b2dc8c | ||
|
|
0160b430f4 | ||
|
|
c72d36af36 | ||
|
|
7ebb420e7d | ||
|
|
2cf60e177b | ||
|
|
0ce5fb4af3 | ||
|
|
4b5bf09c83 | ||
|
|
be520d6716 | ||
|
|
f3c1b7f3ac | ||
|
|
7a790b32b1 | ||
|
|
293a196c91 | ||
|
|
6b4d2b9cfa | ||
|
|
3c4fb679bd | ||
|
|
f1f69e26fd | ||
|
|
b51ed71bb1 | ||
|
|
65a41eaddd | ||
|
|
5361c85d30 | ||
|
|
a31be093e7 | ||
|
|
f39eefa90c | ||
|
|
eaff9537ef | ||
|
|
2110cf128a | ||
|
|
267e371d3c | ||
|
|
9702f8e4c4 | ||
|
|
524c37d7c7 | ||
|
|
b5387016fa | ||
|
|
5c881da4fe | ||
|
|
0df699659a | ||
|
|
032305ad3a | ||
|
|
b16c7d9723 | ||
|
|
6e1f199571 | ||
|
|
5329193987 | ||
|
|
52cf759bec | ||
|
|
cbbc6160f4 | ||
|
|
a8d3b7d39a | ||
|
|
80095ba617 | ||
|
|
a3d1a35e2f | ||
|
|
8c3df643d0 | ||
|
|
37eefb4b35 | ||
|
|
3f5093f180 | ||
|
|
47b3ee60b6 | ||
|
|
59d20ed9b6 | ||
|
|
f41ed2b943 | ||
|
|
56fa96c494 | ||
|
|
6bfcbd2f9a | ||
|
|
fa7895ad62 | ||
|
|
d405dd7776 | ||
|
|
1ed8b131bc | ||
|
|
9ada6adad7 | ||
|
|
719c6db228 | ||
|
|
a96fdfe10c | ||
|
|
1d1e33f4d5 | ||
|
|
3fda9ccd86 | ||
|
|
3be6667789 | ||
|
|
5103d1c168 | ||
|
|
ce85576882 | ||
|
|
59c60504e7 | ||
|
|
a3c586ef38 | ||
|
|
8bc530d3d0 | ||
|
|
f2c6ac01d9 | ||
|
|
94d9f2c8e8 | ||
|
|
0503e7952d | ||
|
|
787d593996 | ||
|
|
db2a541e5f | ||
|
|
11be13fe53 | ||
|
|
65f8afb908 | ||
|
|
0126b159bf | ||
|
|
31024388ba | ||
|
|
9f2d6b72b7 | ||
|
|
5c5dcb5209 | ||
|
|
be6d47a3ab | ||
|
|
4ff009dbfc | ||
|
|
0998b7381f | ||
|
|
0c339e9123 | ||
|
|
75151e0b00 | ||
|
|
4c4232cc07 | ||
|
|
43845b911b | ||
|
|
0ed45e0710 | ||
|
|
5e30f8e6c0 | ||
|
|
7f3c91ac0c | ||
|
|
3853a2701d | ||
|
|
00a5f81871 | ||
|
|
53d865059d | ||
|
|
b90aedd8f2 | ||
|
|
c4928e5546 | ||
|
|
67ae4598d9 | ||
|
|
6b190eb9f3 | ||
|
|
fae16dabf5 | ||
|
|
b651890a3a | ||
|
|
caa7874bb3 | ||
|
|
53f33bd606 | ||
|
|
57e3fce95e | ||
|
|
52692d05b1 | ||
|
|
d364bd69e4 | ||
|
|
70ab7ff769 | ||
|
|
e30976be76 | ||
|
|
1a55a134c7 | ||
|
|
23f0ba995c | ||
|
|
22a09bd06a | ||
|
|
930a08ec52 | ||
|
|
1701d4dc53 | ||
|
|
1e19a9abe2 | ||
|
|
1098e73337 | ||
|
|
99b0fc4b32 | ||
|
|
10d472d8a6 | ||
|
|
6254f44001 | ||
|
|
c435838376 | ||
|
|
3f7db7e430 | ||
|
|
f98fe32d0c | ||
|
|
d2e8ee0e41 | ||
|
|
a9a455e447 | ||
|
|
3f26bf7348 | ||
|
|
ac302d4a67 | ||
|
|
409bc58e17 | ||
|
|
fbcec2cb5a | ||
|
|
ccb90ed16f | ||
|
|
4389cfcd8b | ||
|
|
5e901fb5c2 | ||
|
|
2974e4b66a | ||
|
|
108ac9b8fc | ||
|
|
de3eca26c3 | ||
|
|
8164166465 | ||
|
|
87fe1e77ae | ||
|
|
a753f6f02e | ||
|
|
c86d4b31f2 | ||
|
|
3c89b22207 | ||
|
|
bebe79c4c9 | ||
|
|
adb0b54691 | ||
|
|
b99436543a | ||
|
|
1194823cd5 | ||
|
|
94716ceb5d | ||
|
|
13120127df | ||
|
|
46037cd54d | ||
|
|
e6cd362a1e | ||
|
|
f21b3a32bd | ||
|
|
35941dd19f | ||
|
|
0f257d38df | ||
|
|
912e68d6eb | ||
|
|
c473646389 | ||
|
|
51cf40d040 | ||
|
|
3fd9cdfa7e | ||
|
|
3189820ba0 | ||
|
|
ed74ec1e97 | ||
|
|
dd95a05487 | ||
|
|
481c02c740 | ||
|
|
0a31a65477 | ||
|
|
2d176b7ccc | ||
|
|
a1fac63710 | ||
|
|
5c7f82e2fe | ||
|
|
e765d17c2b | ||
|
|
8fb9afb955 | ||
|
|
e96780fadb | ||
|
|
2221f0fa32 | ||
|
|
f86b8f32de | ||
|
|
9389db9e9b | ||
|
|
519c3a8df3 | ||
|
|
64422367d5 | ||
|
|
dc2b03e331 | ||
|
|
fcf1b3c310 | ||
|
|
1dd6156a1f | ||
|
|
e75a4c2a95 | ||
|
|
05d8c36afe | ||
|
|
b6c9eaf00f | ||
|
|
ecf5f47960 | ||
|
|
457f117216 | ||
|
|
ebf6ed7526 | ||
|
|
96f2c860fc | ||
|
|
dd52e8e85e | ||
|
|
ecf8c999b1 | ||
|
|
64a1d252cd | ||
|
|
b80d46955c | ||
|
|
0a18c64dcc | ||
|
|
b53c56e7f6 | ||
|
|
17d41b53b7 | ||
|
|
bd98cb0312 | ||
|
|
5566d71522 | ||
|
|
5e1396cc42 | ||
|
|
7fe0678c92 | ||
|
|
7ec25a39a2 | ||
|
|
98e3cde2f5 | ||
|
|
d90c02f951 | ||
|
|
bbb92c86db | ||
|
|
1635236a05 | ||
|
|
5e3a280b5c | ||
|
|
a56e2a419b | ||
|
|
9c01227572 | ||
|
|
6d37a47481 | ||
|
|
87b9e59cdb | ||
|
|
0e86b84efa | ||
|
|
bd595b7f94 | ||
|
|
06d4a3504a | ||
|
|
d68534e833 | ||
|
|
e0356bd751 | ||
|
|
4b770a8abc | ||
|
|
709436ca13 | ||
|
|
28856ef495 | ||
|
|
113bb654aa | ||
|
|
efc1883685 | ||
|
|
72921ba3be | ||
|
|
70be673fca | ||
|
|
bf5205258e | ||
|
|
98ddb0bb29 | ||
|
|
d152f89549 | ||
|
|
c61e94527e | ||
|
|
f76ccaf3d8 | ||
|
|
ebc6cd06ad | ||
|
|
017aaf71d6 | ||
|
|
4eaf16f96d | ||
|
|
718745e7b5 | ||
|
|
06ee1ce38e | ||
|
|
d3e8698e40 | ||
|
|
f122402bdd | ||
|
|
7a7ffd3bf8 | ||
|
|
592b903305 |
No files matched your search
@@ -0,0 +1,19 @@
|
|||||||
|
.git/
|
||||||
|
.gitignore
|
||||||
|
.gitattributes
|
||||||
|
.github/
|
||||||
|
output/
|
||||||
|
installer/output/
|
||||||
|
*.bin
|
||||||
|
html_data.c
|
||||||
|
html_data.h
|
||||||
|
*.o
|
||||||
|
*.rel
|
||||||
|
*.lst
|
||||||
|
*.sym
|
||||||
|
*.asm
|
||||||
|
*.ihx
|
||||||
|
*.img
|
||||||
|
*.map
|
||||||
|
*.mem
|
||||||
|
*.lk
|
||||||
@@ -0,0 +1,28 @@
|
|||||||
|
# Change Log
|
||||||
|
|
||||||
|
## [0.x] - 2026-xx-XX
|
||||||
|
|
||||||
|
## Added
|
||||||
|
|
||||||
|
- Web UI
|
||||||
|
- Compress the embedded assets (minify + gzip) and switch to a single-page layout. #315
|
||||||
|
|
||||||
|
## Changed
|
||||||
|
|
||||||
|
## Fixed
|
||||||
|
|
||||||
|
## Breaking changes
|
||||||
|
|
||||||
|
- Config
|
||||||
|
- VLAN don't accept port `u`-suffix anymore.
|
||||||
|
So `vlan 1 4u` is not valid anymore.
|
||||||
|
Replace it with `vlan 1 4`.
|
||||||
|
- Commands
|
||||||
|
- port zero/`0` is treated as the `CPU_PORT`. #326
|
||||||
|
- Many commands don't accept the CPU_PORT anymore. See #334.
|
||||||
|
When the CPU_PORT is needed, the command/service will add the CPU_PORT automaticly.
|
||||||
|
Only `isolate` accept the CPU_PORT as destination port.
|
||||||
|
|
||||||
|
## [v0.1_aplha] - 2025-11-03
|
||||||
|
|
||||||
|
First release.
|
||||||
@@ -0,0 +1,18 @@
|
|||||||
|
FROM debian:13-slim
|
||||||
|
|
||||||
|
RUN apt-get update && apt-get install -y \
|
||||||
|
make \
|
||||||
|
gcc \
|
||||||
|
sdcc \
|
||||||
|
xxd \
|
||||||
|
python3 \
|
||||||
|
libjson-c-dev \
|
||||||
|
git \
|
||||||
|
&& rm -rf /var/lib/apt/lists/*
|
||||||
|
|
||||||
|
# git safe.directory for mounted repos (Makefile uses git describe)
|
||||||
|
RUN git config --global --add safe.directory /workspace
|
||||||
|
|
||||||
|
WORKDIR /workspace
|
||||||
|
|
||||||
|
CMD ["bash"]
|
||||||
@@ -4,9 +4,11 @@ DEFAULT_CONFIG_LOCATION = 454656
|
|||||||
CONFIG_LOCATION = 458752
|
CONFIG_LOCATION = 458752
|
||||||
HTML_LOCATION = 262144
|
HTML_LOCATION = 262144
|
||||||
|
|
||||||
|
ifeq ($(origin CC),default)
|
||||||
CC = sdcc
|
CC = sdcc
|
||||||
|
endif
|
||||||
CC_FLAGS = -mmcs51 -I. -Ihttpd -Iuip
|
CC_FLAGS = -mmcs51 -I. -Ihttpd -Iuip
|
||||||
ASM = sdas8051
|
ASM ?= sdas8051
|
||||||
AFLAGS= -plosgff
|
AFLAGS= -plosgff
|
||||||
|
|
||||||
SUBDIRS := tools
|
SUBDIRS := tools
|
||||||
@@ -30,76 +32,131 @@ endif
|
|||||||
VERSION_EXTENSION = v$(VERSION)-$(GIT_VERSION)
|
VERSION_EXTENSION = v$(VERSION)-$(GIT_VERSION)
|
||||||
FILENAME_EXTENSION = $(VERSION_EXTENSION)-$(MACHINE)
|
FILENAME_EXTENSION = $(VERSION_EXTENSION)-$(MACHINE)
|
||||||
|
|
||||||
|
# Deterministic build date: honor SOURCE_DATE_EPOCH, else the HEAD commit date,
|
||||||
|
# else wall-clock (no-git fallback). Keeps same-commit builds byte-identical
|
||||||
|
# (BUILD_DATE is baked into the image and covered by the trailing CRC).
|
||||||
|
SOURCE_DATE_EPOCH ?= $(shell git show -s --format=%ct HEAD 2>/dev/null)
|
||||||
|
ifeq ($(SOURCE_DATE_EPOCH),)
|
||||||
|
BUILD_DATE := $(shell date +"%Y-%m-%d %H:%M:%S")
|
||||||
|
else
|
||||||
|
BUILD_DATE := $(shell date -u -d @$(SOURCE_DATE_EPOCH) +"%Y-%m-%d %H:%M:%S" 2>/dev/null \
|
||||||
|
|| date -u -r $(SOURCE_DATE_EPOCH) +"%Y-%m-%d %H:%M:%S")
|
||||||
|
endif
|
||||||
|
|
||||||
all: create_build_dir $(VERSION_HEADER) $(SUBDIRS) $(BUILDDIR)/rtlplayground-$(FILENAME_EXTENSION).bin
|
all: create_build_dir $(VERSION_HEADER) $(SUBDIRS) $(BUILDDIR)/rtlplayground-$(FILENAME_EXTENSION).bin
|
||||||
|
|
||||||
create_build_dir:
|
create_build_dir:
|
||||||
mkdir -p $(BUILDDIR)
|
mkdir -p "$(BUILDDIR)"
|
||||||
mkdir -p $(BUILDDIR)/uip
|
mkdir -p "$(BUILDDIR)/uip"
|
||||||
mkdir -p $(BUILDDIR)/httpd
|
mkdir -p "$(BUILDDIR)/httpd"
|
||||||
|
|
||||||
|
# Keep machine.c in first position to fail immediately on invalid $MACHINE value
|
||||||
|
SRCS = \
|
||||||
|
machine.c \
|
||||||
|
machine_init.c \
|
||||||
|
cmd_editor.c \
|
||||||
|
cmd_parser.c \
|
||||||
|
dhcp.c \
|
||||||
|
html_data.c \
|
||||||
|
rtlplayground.c \
|
||||||
|
syslog.c \
|
||||||
|
udp_apps.c
|
||||||
|
|
||||||
|
# RTL837x
|
||||||
|
SRCS += \
|
||||||
|
rtl837x_bandwidth.c \
|
||||||
|
rtl837x_flash.c \
|
||||||
|
rtl837x_igmp.c \
|
||||||
|
rtl837x_init.c \
|
||||||
|
rtl837x_leds.c \
|
||||||
|
rtl837x_phy.c \
|
||||||
|
rtl837x_pins.c\
|
||||||
|
rtl837x_port.c \
|
||||||
|
rtl837x_stp.c
|
||||||
|
SRCS += \
|
||||||
|
httpd/httpd.c \
|
||||||
|
httpd/page_impl.c
|
||||||
|
SRCS += \
|
||||||
|
uip/timer.c \
|
||||||
|
uip/uip.c \
|
||||||
|
uip/uiplib.c \
|
||||||
|
uip/uip_arp.c \
|
||||||
|
uip/uip-fw.c \
|
||||||
|
uip/uip-neighbor.c \
|
||||||
|
uip/uip-split.c
|
||||||
|
|
||||||
SRCS = rtlplayground.c rtl837x_flash.c rtl837x_leds.c rtl837x_phy.c rtl837x_port.c cmd_parser.c html_data.c rtl837x_igmp.c
|
|
||||||
SRCS += rtl837x_stp.c rtl837x_pins.c dhcp.c machine.c cmd_editor.c rtl837x_bandwidth.c rtl837x_init.c syslog.c
|
|
||||||
SRCS += uip/timer.c uip/uip.c uip/uip_arp.c uip/uiplib.c uip/uip-fw.c uip/uip-neighbor.c uip/uip-split.c udp_apps.c
|
|
||||||
SRCS += httpd/httpd.c httpd/page_impl.c
|
|
||||||
OBJS = ${SRCS:%.c=$(BUILDDIR)/%.rel}
|
OBJS = ${SRCS:%.c=$(BUILDDIR)/%.rel}
|
||||||
DEPS := ${SRCS:%.c=$(BUILDDIR)/%.d}
|
DEPS := ${SRCS:%.c=$(BUILDDIR)/%.d}
|
||||||
HTML := $(shell find $(html) -name '*.js' -or -name '*.html' -or -name '*.svg')
|
HTML := $(shell find html -name '*.js' -or -name '*.html' -or -name '*.svg' -or -name '*.css' -or -name '*.ico')
|
||||||
|
|
||||||
html_data.c html_data.h: $(HTML) tools/output/fileadder
|
# Minified copy of the web UI sources, used as the fileadder input.
|
||||||
tools/output/fileadder -a $(HTML_LOCATION) -s $(IMAGESIZE) -b BANK1 -d html -p html_data
|
# The raw html/ sources stay untouched for development; the minified
|
||||||
|
# copy is a build artifact under output/.
|
||||||
|
HTML_MIN := output/html_min
|
||||||
|
.PHONY: html_min
|
||||||
|
html_min: $(HTML)
|
||||||
|
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
|
||||||
|
|
||||||
|
html_data.c html_data.h &: $(HTML) | tools html_min
|
||||||
|
tools/output/fileadder -a $(HTML_LOCATION) -s $(IMAGESIZE) -b BANK1 -z -d $(HTML_MIN) -p html_data
|
||||||
|
|
||||||
$(VERSION_HEADER):
|
$(VERSION_HEADER):
|
||||||
@echo "#ifndef VERSION_H" > $(VERSION_HEADER)
|
@printf '%s\n' "#ifndef VERSION_H" "#define VERSION_H" \
|
||||||
@echo "#define VERSION_H" >> $(VERSION_HEADER)
|
"#define VERSION_SW \"$(VERSION_EXTENSION)\"" \
|
||||||
@echo "#define VERSION_SW \"$(VERSION_EXTENSION)\"" >> $(VERSION_HEADER)
|
"#define BUILD_DATE \"$(BUILD_DATE)\"" \
|
||||||
@echo "#define BUILD_DATE \"$(shell date +"%Y-%m-%d %H:%M:%S")\"" >> $(VERSION_HEADER)
|
"#endif" > $(VERSION_HEADER)
|
||||||
@echo "#endif" >> $(VERSION_HEADER)
|
|
||||||
|
|
||||||
httpd: html_data.h
|
httpd: html_data.h
|
||||||
|
|
||||||
$(SUBDIRS):
|
$(SUBDIRS):
|
||||||
$(MAKE) -C $@
|
$(MAKE) -C $@
|
||||||
|
|
||||||
clean:
|
clean: $(SUBDIRSCLEAN)
|
||||||
-rm -f html_data.c html_data.h $(VERSION_HEADER)
|
-rm -f html_data.c html_data.h $(VERSION_HEADER)
|
||||||
-if [ -d $(BUILDDIR) ]; then find $(BUILDDIR) -type f ! -name "*.bin" -delete; fi
|
-if [ -d $(BUILDDIR) ]; then find $(BUILDDIR) -type f ! -name "*.bin" -delete; fi
|
||||||
|
|
||||||
distclean:
|
distclean: $(SUBDIRSCLEAN)
|
||||||
-rm -f html_data.c html_data.h $(VERSION_HEADER)
|
-rm -f html_data.c html_data.h $(VERSION_HEADER)
|
||||||
-rm -rf $(BUILDDIR)
|
-rm -rf $(BUILDDIR)
|
||||||
|
|
||||||
$(BUILDDIR)/%.rel: %.c
|
$(SUBDIRSCLEAN):
|
||||||
|
$(MAKE) -C $(@:clean=) clean
|
||||||
|
|
||||||
|
$(BUILDDIR)/%.rel: %.c | create_build_dir html_data.h
|
||||||
$(CC) -MMD $(CC_FLAGS) -o $@ -c $<
|
$(CC) -MMD $(CC_FLAGS) -o $@ -c $<
|
||||||
|
|
||||||
$(BUILDDIR)/%.rel: %.asm
|
$(BUILDDIR)/%.rel: %.asm | create_build_dir
|
||||||
${ASM} ${AFLAGS} -o $@ $<
|
${ASM} ${AFLAGS} -o $@ $<
|
||||||
# mv -f $(addprefix $(basename $^), .lst .rel .sym) .
|
# mv -f $(addprefix $(basename $^), .lst .rel .sym) .
|
||||||
|
|
||||||
$(BUILDDIR)/rtlplayground.ihx: $(OBJS) $(BUILDDIR)/crtstart.rel $(BUILDDIR)/crc16.rel
|
$(BUILDDIR)/rtlplayground.ihx: $(OBJS) $(BUILDDIR)/crtbank.rel $(BUILDDIR)/crc16.rel
|
||||||
$(CC) $(CC_FLAGS) -Wl-bHOME=0x00000 -Wl-bBANK1=0x14000 -Wl-bBANK2=0x24000 -Wl-r -o $@ $^
|
$(CC) $(CC_FLAGS) -Wl-bHOME=0x00000 -Wl-bBANK1=0x14000 -Wl-bBANK2=0x24000 -Wl-r -o $@ $^
|
||||||
|
|
||||||
$(BUILDDIR)/rtlplayground.img: $(BUILDDIR)/rtlplayground.ihx
|
$(BUILDDIR)/rtlplayground.img: $(BUILDDIR)/rtlplayground.ihx
|
||||||
objcopy --input-target=ihex -O binary $< $@
|
objcopy --input-target=ihex -O binary $< $@
|
||||||
|
|
||||||
$(BUILDDIR)/rtlplayground-$(FILENAME_EXTENSION).bin: $(BUILDDIR)/rtlplayground.img
|
$(BUILDDIR)/rtlplayground-$(FILENAME_EXTENSION).bin: $(BUILDDIR)/rtlplayground.img | tools
|
||||||
if [ -e $@ ]; then rm $@; fi
|
if [ -e $@ ]; then rm $@; fi
|
||||||
tools/output/imagebuilder -i $^ $@
|
tools/output/imagebuilder -i $^ $@
|
||||||
tools/output/fileadder -a $(DEFAULT_CONFIG_LOCATION) -s $(IMAGESIZE) -d config.txt $@
|
tools/output/fileadder -a $(DEFAULT_CONFIG_LOCATION) -s $(IMAGESIZE) -d config.txt $@
|
||||||
tools/output/fileadder -a $(CONFIG_LOCATION) -s $(IMAGESIZE) -d config.txt $@
|
tools/output/fileadder -a $(CONFIG_LOCATION) -s $(IMAGESIZE) -d config.txt $@
|
||||||
tools/output/fileadder -a $(HTML_LOCATION) -s $(IMAGESIZE) -d html -p html_data -b BANK1 $@
|
tools/output/fileadder -a $(HTML_LOCATION) -s $(IMAGESIZE) -z -d $(HTML_MIN) -p html_data -b BANK1 $@
|
||||||
tools/output/crc_calculator -u $@
|
tools/output/crc_calculator -u $@
|
||||||
ln -sf $(MACHINE)/rtlplayground-$(FILENAME_EXTENSION).bin output/rtlplayground.bin
|
ln -sf $(MACHINE)/rtlplayground-$(FILENAME_EXTENSION).bin output/rtlplayground.bin
|
||||||
|
|
||||||
.PHONY: clean all $(SUBDIRS) $(VERSION_HEADER)
|
.PHONY: clean distclean all $(SUBDIRS) $(SUBDIRSCLEAN) $(VERSION_HEADER) create_build_dir
|
||||||
|
|
||||||
.PHONY:
|
.PHONY:
|
||||||
machine_check:
|
machine_check:
|
||||||
@mkdir -p $(BUILDDIR)/tmp
|
@mkdir -p $(BUILDDIR)/tmp
|
||||||
@set -eo pipefail; \
|
@set -eo pipefail; \
|
||||||
for MACHINE in `grep -e ' MACHINE_' machine.c | sed -e 's%^.* MACHINE_%%' -e 's%[ ]*//.*$$%%' | sort -u`; \
|
for MACHINE in `grep -E '^[[:space:]]*(//[[:space:]]*)?#define MACHINE_' machine.h | sed -E 's%^[[:space:]]*(//[[:space:]]*)?#define MACHINE_%%' | awk '{print $$1}' | sort -u`; \
|
||||||
do \
|
do \
|
||||||
echo "Checking $${MACHINE}"; \
|
echo "Checking $${MACHINE}"; \
|
||||||
$(CC) $(CC_FLAGS) -DMACHINE_$${MACHINE} -MMD -o $(BUILDDIR)/tmp/machine_check -c machine.c; \
|
$(CC) $(CC_FLAGS) -DMACHINE_$${MACHINE} -MMD -o $(BUILDDIR)/tmp/machine_check -c machine.c; \
|
||||||
|
$(CC) $(CC_FLAGS) -DMACHINE_$${MACHINE} -MMD -o $(BUILDDIR)/tmp/machine_check -c machine_init.c; \
|
||||||
done
|
done
|
||||||
@rm -rf $(BUILDDIR)/tmp
|
@rm -rf $(BUILDDIR)/tmp
|
||||||
|
|
||||||
|
|||||||
@@ -28,8 +28,10 @@ only the following features are provided:
|
|||||||
<img width="1420" height="623" alt="GUI" src="doc/images/gui.png" />
|
<img width="1420" height="623" alt="GUI" src="doc/images/gui.png" />
|
||||||
|
|
||||||
While the firmware provides already considerable improvements over the original managed firmware,
|
While the firmware provides already considerable improvements over the original managed firmware,
|
||||||
the firmware still lacks support for STP and the proprietary loop prevention
|
the firmware still lacks support for the proprietary loop prevention
|
||||||
protocols as well as DHCP. If you need these features, do not install the playground on your managed
|
protocols as well as DHCP. Spanning Tree is available (see doc/stp.md), but is
|
||||||
|
a simplified implementation - read that document before enabling it on a
|
||||||
|
switch you administer over the network. If you need these features, do not install the playground on your managed
|
||||||
devices. In any case, installation is strongly discouraged unless you can at least make
|
devices. In any case, installation is strongly discouraged unless you can at least make
|
||||||
a backup of the original flash content via a SOIC clamp such as also used for BIOS
|
a backup of the original flash content via a SOIC clamp such as also used for BIOS
|
||||||
backups and can re-install that firmware in case something is wrong. For this no soldering
|
backups and can re-install that firmware in case something is wrong. For this no soldering
|
||||||
@@ -60,6 +62,53 @@ still has an older version of sdcc, but you will need sdcc version 4.5 for the c
|
|||||||
sudo apt install make gcc sdcc xxd python-is-python3 libjson-c-dev
|
sudo apt install make gcc sdcc xxd python-is-python3 libjson-c-dev
|
||||||
```
|
```
|
||||||
|
|
||||||
|
<details>
|
||||||
|
<summary>If using Docker (click to expand)</summary>
|
||||||
|
|
||||||
|
### Prerequisites
|
||||||
|
|
||||||
|
Install Docker for your platform:
|
||||||
|
|
||||||
|
- **Linux (Debian/Ubuntu)**: `sudo apt install docker.io` then `sudo usermod -aG docker $USER` (log out and back in)
|
||||||
|
- **Linux (other distros)**: Follow the [Docker Engine install guide](https://docs.docker.com/engine/install/)
|
||||||
|
- **Windows**: Install [Docker Desktop for Windows](https://docs.docker.com/desktop/setup/install/windows-install/)
|
||||||
|
- **macOS**: Install [Docker Desktop for Mac](https://docs.docker.com/desktop/setup/install/mac-install/)
|
||||||
|
|
||||||
|
### Usage
|
||||||
|
|
||||||
|
A Dockerfile is provided for a reproducible build environment:
|
||||||
|
|
||||||
|
```
|
||||||
|
docker build -t rtlplayground-dev .
|
||||||
|
```
|
||||||
|
|
||||||
|
Build the firmware (replace MACHINE with your target, e.g. `DEFAULT_8C_1SFP`):
|
||||||
|
|
||||||
|
```
|
||||||
|
docker run --rm -v $(pwd):/workspace rtlplayground-dev make MACHINE=DEFAULT_8C_1SFP
|
||||||
|
```
|
||||||
|
|
||||||
|
The resulting `.bin` file appears in `output/` on your host.
|
||||||
|
|
||||||
|
Build host tools only:
|
||||||
|
|
||||||
|
```
|
||||||
|
docker run --rm -v $(pwd):/workspace rtlplayground-dev make -C tools
|
||||||
|
```
|
||||||
|
|
||||||
|
Run the web-interface simulator locally:
|
||||||
|
|
||||||
|
```
|
||||||
|
docker run --rm -p 8080:8080 -v $(pwd):/workspace rtlplayground-dev \
|
||||||
|
tools/output/httpd_sim /workspace/html
|
||||||
|
```
|
||||||
|
|
||||||
|
Edit `machine.h` or `config.txt` on your host, then re-run `make` — the
|
||||||
|
source directory is mounted into the container, so changes take effect
|
||||||
|
immediately. To build for a different machine, pass `MACHINE=...`.
|
||||||
|
|
||||||
|
</details>
|
||||||
|
|
||||||
## (1) Compiling for direct chip flashing AND upgrading an existing RTLPlayground running device
|
## (1) Compiling for direct chip flashing AND upgrading an existing RTLPlayground running device
|
||||||
|
|
||||||
Edit machine.h with an editor like vi or nano. Select the correct machine the firmware should build for.
|
Edit machine.h with an editor like vi or nano. Select the correct machine the firmware should build for.
|
||||||
|
|||||||
@@ -40,21 +40,24 @@ void cmd_edit(void) __banked
|
|||||||
{
|
{
|
||||||
while (l != sbuf_ptr) {
|
while (l != sbuf_ptr) {
|
||||||
if (sbuf[l] >= ' ' && sbuf[l] < 127) { // A printable character, copy to command line
|
if (sbuf[l] >= ' ' && sbuf[l] < 127) { // A printable character, copy to command line
|
||||||
if (cmd_line_len >= CMD_BUF_SIZE)
|
// Reserve one byte for the terminating NUL written on Enter. When the
|
||||||
continue;
|
// line is full, drop the character but still fall through to advance the
|
||||||
write_char(sbuf[l]);
|
// serial-ring read pointer below; a 'continue' here would spin forever.
|
||||||
// Shift buffer to right
|
if (cmd_line_len < CMD_BUF_SIZE - 1) {
|
||||||
for (uint8_t i = cmd_line_len; i > cursor; i--)
|
write_char(sbuf[l]);
|
||||||
cmd_buffer[i] = cmd_buffer[i-1];
|
// Shift buffer to right
|
||||||
// Insert char in comand buffer
|
for (uint8_t i = cmd_line_len; i > cursor; i--)
|
||||||
cmd_buffer[cursor++] = sbuf[l];
|
cmd_buffer[i] = cmd_buffer[i-1];
|
||||||
cmd_line_len++;
|
// Insert char in comand buffer
|
||||||
// Print rest of line
|
cmd_buffer[cursor++] = sbuf[l];
|
||||||
for (uint8_t i = cursor; i < cmd_line_len; i++)
|
cmd_line_len++;
|
||||||
write_char(cmd_buffer[i]);
|
// Print rest of line
|
||||||
// Move backwards
|
for (uint8_t i = cursor; i < cmd_line_len; i++)
|
||||||
for (uint8_t i = cursor; i < cmd_line_len; i++)
|
write_char(cmd_buffer[i]);
|
||||||
write_char('\010'); // BS works like cursor-left
|
// Move backwards
|
||||||
|
for (uint8_t i = cursor; i < cmd_line_len; i++)
|
||||||
|
write_char('\010'); // BS works like cursor-left
|
||||||
|
}
|
||||||
} else if (sbuf[l] == '\033') { // ESC-Sequence
|
} else if (sbuf[l] == '\033') { // ESC-Sequence
|
||||||
// Wait until we have at least 3 characters including the ESC character in the serial buffer
|
// Wait until we have at least 3 characters including the ESC character in the serial buffer
|
||||||
if (((sbuf_ptr + SBUF_SIZE - l) & SBUF_MASK) < 3)
|
if (((sbuf_ptr + SBUF_SIZE - l) & SBUF_MASK) < 3)
|
||||||
@@ -190,7 +193,7 @@ void cmd_edit(void) __banked
|
|||||||
// Check whether return was pressed:
|
// Check whether return was pressed:
|
||||||
if (sbuf[l] == '\n' || sbuf[l] == '\r') {
|
if (sbuf[l] == '\n' || sbuf[l] == '\r') {
|
||||||
write_char('\n');
|
write_char('\n');
|
||||||
cmd_buffer[cmd_line_len] = '\0';
|
cmd_buffer[cmd_line_len] = NUL;
|
||||||
// write_char('>'); print_string_x(cmd_buffer); write_char('<');
|
// write_char('>'); print_string_x(cmd_buffer); write_char('<');
|
||||||
// If there is a command we print the prompt after execution
|
// If there is a command we print the prompt after execution
|
||||||
// otherwise immediately because there is nothing to execute
|
// otherwise immediately because there is nothing to execute
|
||||||
|
|||||||
@@ -13,6 +13,7 @@ void cmd_tokenize(void) __banked;
|
|||||||
void cmd_parser(void) __banked;
|
void cmd_parser(void) __banked;
|
||||||
void execute_config(void) __banked;
|
void execute_config(void) __banked;
|
||||||
void execute_commands(__xdata uint8_t *p) __banked;
|
void execute_commands(__xdata uint8_t *p) __banked;
|
||||||
|
void print_ip(__xdata uint8_t *ptr);
|
||||||
void print_sw_version(void) __banked;
|
void print_sw_version(void) __banked;
|
||||||
void clear_command_history(void) __banked;
|
void clear_command_history(void) __banked;
|
||||||
|
|
||||||
|
|||||||
@@ -3,6 +3,8 @@
|
|||||||
;
|
;
|
||||||
.globl _crc_value
|
.globl _crc_value
|
||||||
.globl _crc16
|
.globl _crc16
|
||||||
|
.globl _crc16_bank1
|
||||||
|
.globl __sdcc_banked_ret
|
||||||
.equ BANK, 0x96
|
.equ BANK, 0x96
|
||||||
; .equ DPS, 0x86
|
; .equ DPS, 0x86
|
||||||
; Variable in XMEM holding current CRC16 value, being updated
|
; Variable in XMEM holding current CRC16 value, being updated
|
||||||
@@ -18,7 +20,7 @@ _crc_value::
|
|||||||
.area HOME (CODE)
|
.area HOME (CODE)
|
||||||
.area CSEG (CODE)
|
.area CSEG (CODE)
|
||||||
; .area BANK1 (CODE)
|
; .area BANK1 (CODE)
|
||||||
_crc16:
|
_crc16_bank1:
|
||||||
mov BANK, #1
|
mov BANK, #1
|
||||||
push dph
|
push dph
|
||||||
push dpl
|
push dpl
|
||||||
@@ -52,6 +54,10 @@ _crc16:
|
|||||||
|
|
||||||
.area BANK1 (CODE)
|
.area BANK1 (CODE)
|
||||||
|
|
||||||
|
_crc16:
|
||||||
|
lcall _crc16_bank1
|
||||||
|
ljmp __sdcc_banked_ret
|
||||||
|
|
||||||
crc16_table_l:
|
crc16_table_l:
|
||||||
.byte #0x00, #0xc1, #0x81, #0x40, #0x01, #0xc0, #0x80, #0x41
|
.byte #0x00, #0xc1, #0x81, #0x40, #0x01, #0xc0, #0x80, #0x41
|
||||||
.byte #0x01, #0xc0, #0x80, #0x41, #0x00, #0xc1, #0x81, #0x40
|
.byte #0x01, #0xc0, #0x80, #0x41, #0x00, #0xc1, #0x81, #0x40
|
||||||
|
|||||||
@@ -0,0 +1,29 @@
|
|||||||
|
.area HOME (CODE)
|
||||||
|
.area GSINIT0 (CODE)
|
||||||
|
.area GSINIT1 (CODE)
|
||||||
|
.area GSINIT2 (CODE)
|
||||||
|
.area GSINIT3 (CODE)
|
||||||
|
.area GSINIT4 (CODE)
|
||||||
|
.area GSINIT5 (CODE)
|
||||||
|
.area GSINIT (CODE)
|
||||||
|
.area GSFINAL (CODE)
|
||||||
|
.area CSEG (CODE)
|
||||||
|
|
||||||
|
|
||||||
|
.area HOME (CODE)
|
||||||
|
|
||||||
|
__sdcc_banked_call::
|
||||||
|
push _PSBANK
|
||||||
|
xch a,r0
|
||||||
|
push a
|
||||||
|
mov a,r1
|
||||||
|
push a
|
||||||
|
mov a,r2
|
||||||
|
anl a,#0x1f
|
||||||
|
mov _PSBANK, a
|
||||||
|
xch a, r0
|
||||||
|
ret
|
||||||
|
|
||||||
|
__sdcc_banked_ret::
|
||||||
|
pop _PSBANK
|
||||||
|
ret
|
||||||
@@ -1,17 +0,0 @@
|
|||||||
.area GSFINAL (CODE)
|
|
||||||
|
|
||||||
__sdcc_banked_call::
|
|
||||||
push _PSBANK
|
|
||||||
xch a,r0
|
|
||||||
push a
|
|
||||||
mov a,r1
|
|
||||||
push a
|
|
||||||
mov a,r2
|
|
||||||
anl a,#0x1f
|
|
||||||
mov _PSBANK, a
|
|
||||||
xch a, r0
|
|
||||||
ret
|
|
||||||
|
|
||||||
__sdcc_banked_ret::
|
|
||||||
pop _PSBANK
|
|
||||||
ret
|
|
||||||
@@ -41,6 +41,7 @@ __xdata uip_ipaddr_t server;
|
|||||||
#define DHCP_REBIND_LEN 4
|
#define DHCP_REBIND_LEN 4
|
||||||
#define DHCP_CLIENT_ID 61
|
#define DHCP_CLIENT_ID 61
|
||||||
#define DHCP_CLIENT_ID_LEN 7
|
#define DHCP_CLIENT_ID_LEN 7
|
||||||
|
#define DHCP_HOSTNAME 12
|
||||||
#define DHCP_REQUEST_IP 50
|
#define DHCP_REQUEST_IP 50
|
||||||
#define DHCP_REQUEST_IP_LEN 4
|
#define DHCP_REQUEST_IP_LEN 4
|
||||||
#define DHCP_PARAMS 55
|
#define DHCP_PARAMS 55
|
||||||
@@ -93,7 +94,7 @@ void dhcp_prepare_request(void)
|
|||||||
DHCP_P->hw_len = 6;
|
DHCP_P->hw_len = 6;
|
||||||
DHCP_P->hops = 0;
|
DHCP_P->hops = 0;
|
||||||
|
|
||||||
DHCP_P->tid = HTONS(dhcp_state.transaction_id);
|
DHCP_P->tid = dhcp_state.transaction_id;
|
||||||
DHCP_P->delay = HTONS(0);
|
DHCP_P->delay = HTONS(0);
|
||||||
DHCP_P->flags = 0;
|
DHCP_P->flags = 0;
|
||||||
// Clear fields client_ip to bootp_file
|
// Clear fields client_ip to bootp_file
|
||||||
@@ -116,6 +117,20 @@ void dhcp_addopt_client_id(void)
|
|||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
void dhcp_addopt_hostname(void)
|
||||||
|
{
|
||||||
|
uint8_t len = 0;
|
||||||
|
while (hostname[len])
|
||||||
|
len++;
|
||||||
|
if (!len)
|
||||||
|
return;
|
||||||
|
DHCP_OPT[dhcp_state.opt_ptr++] = DHCP_HOSTNAME;
|
||||||
|
DHCP_OPT[dhcp_state.opt_ptr++] = len;
|
||||||
|
memcpy(&DHCP_OPT[dhcp_state.opt_ptr], hostname, len);
|
||||||
|
dhcp_state.opt_ptr += len;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
void dhcp_addopt_request_ip(void)
|
void dhcp_addopt_request_ip(void)
|
||||||
{
|
{
|
||||||
DHCP_OPT[dhcp_state.opt_ptr++] = DHCP_REQUEST_IP;
|
DHCP_OPT[dhcp_state.opt_ptr++] = DHCP_REQUEST_IP;
|
||||||
@@ -152,6 +167,7 @@ void dhcp_send_discover(void)
|
|||||||
|
|
||||||
dhcp_addopt_client_id();
|
dhcp_addopt_client_id();
|
||||||
dhcp_addopt_request_ip();
|
dhcp_addopt_request_ip();
|
||||||
|
dhcp_addopt_hostname();
|
||||||
|
|
||||||
DHCP_OPT[dhcp_state.opt_ptr++] = DHCP_PARAMS;
|
DHCP_OPT[dhcp_state.opt_ptr++] = DHCP_PARAMS;
|
||||||
DHCP_OPT[dhcp_state.opt_ptr++] = 3;
|
DHCP_OPT[dhcp_state.opt_ptr++] = 3;
|
||||||
@@ -188,6 +204,7 @@ void dhcp_send_request(void)
|
|||||||
dhcp_addopt_client_id();
|
dhcp_addopt_client_id();
|
||||||
dhcp_addopt_request_ip();
|
dhcp_addopt_request_ip();
|
||||||
dhcp_addopt_server_id();
|
dhcp_addopt_server_id();
|
||||||
|
dhcp_addopt_hostname();
|
||||||
|
|
||||||
DHCP_OPT[dhcp_state.opt_ptr++] = DHCP_PARAMS;
|
DHCP_OPT[dhcp_state.opt_ptr++] = DHCP_PARAMS;
|
||||||
DHCP_OPT[dhcp_state.opt_ptr++] = 3;
|
DHCP_OPT[dhcp_state.opt_ptr++] = 3;
|
||||||
@@ -283,7 +300,7 @@ void parse_opts(void)
|
|||||||
|
|
||||||
void parse_dhcp(void)
|
void parse_dhcp(void)
|
||||||
{
|
{
|
||||||
if (!DHCP_P->tid == HTONS(dhcp_state.transaction_id))
|
if (DHCP_P->tid != dhcp_state.transaction_id)
|
||||||
return;
|
return;
|
||||||
if (DHCP_P->cookie[0] != 0x63 || DHCP_P->cookie[1] != 0x82 || DHCP_P->cookie[2] != 0x53 || DHCP_P->cookie[3] != 0x63)
|
if (DHCP_P->cookie[0] != 0x63 || DHCP_P->cookie[1] != 0x82 || DHCP_P->cookie[2] != 0x53 || DHCP_P->cookie[3] != 0x63)
|
||||||
return;
|
return;
|
||||||
@@ -315,6 +332,18 @@ void parse_dhcp(void)
|
|||||||
uip_ipaddr(&uip_hostaddr, dhcp_state.current_ip[0], dhcp_state.current_ip[1], dhcp_state.current_ip[2], dhcp_state.current_ip[3]);
|
uip_ipaddr(&uip_hostaddr, dhcp_state.current_ip[0], dhcp_state.current_ip[1], dhcp_state.current_ip[2], dhcp_state.current_ip[3]);
|
||||||
uip_ipaddr(&uip_draddr, dhcp_state.router[0], dhcp_state.router[1], dhcp_state.router[2], dhcp_state.router[3]);
|
uip_ipaddr(&uip_draddr, dhcp_state.router[0], dhcp_state.router[1], dhcp_state.router[2], dhcp_state.router[3]);
|
||||||
uip_ipaddr(&uip_netmask, dhcp_state.subnet[0], dhcp_state.subnet[1], dhcp_state.subnet[2], dhcp_state.subnet[3]);
|
uip_ipaddr(&uip_netmask, dhcp_state.subnet[0], dhcp_state.subnet[1], dhcp_state.subnet[2], dhcp_state.subnet[3]);
|
||||||
|
/* T1 is optional. Without it the renewal time stays zero, which
|
||||||
|
* makes dhcp_timer zero, and the callback then sends a REQUEST on
|
||||||
|
* every pass through LEASING: client and server trade REQUEST and
|
||||||
|
* ACK as fast as the round trip allows. RFC 2131 has the client
|
||||||
|
* renew at half the lease when the server does not say, and if
|
||||||
|
* there is no lease either, pick something rather than zero. Only
|
||||||
|
* filled in when absent, so a server that asks for a short renewal
|
||||||
|
* still gets it. */
|
||||||
|
if (!dhcp_state.renewal)
|
||||||
|
dhcp_state.renewal = dhcp_state.lease / 2;
|
||||||
|
if (!dhcp_state.renewal)
|
||||||
|
dhcp_state.renewal = 60;
|
||||||
dhcp_state.state = DHCP_LEASING;
|
dhcp_state.state = DHCP_LEASING;
|
||||||
dhcp_state.ticks = SYS_TICK_HZ;
|
dhcp_state.ticks = SYS_TICK_HZ;
|
||||||
dhcp_state.dhcp_timer = dhcp_state.renewal > 0xffff ? 0xffff : dhcp_state.renewal;
|
dhcp_state.dhcp_timer = dhcp_state.renewal > 0xffff ? 0xffff : dhcp_state.renewal;
|
||||||
@@ -324,6 +353,12 @@ void parse_dhcp(void)
|
|||||||
|
|
||||||
void dhcp_start(void) __banked
|
void dhcp_start(void) __banked
|
||||||
{
|
{
|
||||||
|
/* Starting twice would leave the first socket bound: uip_udp_new() takes
|
||||||
|
* the next free slot while the old one keeps port 68, and dhcp_callback()
|
||||||
|
* matches on that port rather than on the connection, so every leftover
|
||||||
|
* runs the state machine too and the timers count down once per socket. */
|
||||||
|
if (dhcp_state.state != DHCP_OFF)
|
||||||
|
dhcp_stop();
|
||||||
uip_ipaddr(server, 255,255,255,255);
|
uip_ipaddr(server, 255,255,255,255);
|
||||||
dhcp_state.conn = uip_udp_new(&server, HTONS(DHCPC_SERVER_PORT));
|
dhcp_state.conn = uip_udp_new(&server, HTONS(DHCPC_SERVER_PORT));
|
||||||
dhcp_state.current_ip[0] = dhcp_state.current_ip[1] = dhcp_state.current_ip[2] = dhcp_state.current_ip[3] = 0;
|
dhcp_state.current_ip[0] = dhcp_state.current_ip[1] = dhcp_state.current_ip[2] = dhcp_state.current_ip[3] = 0;
|
||||||
@@ -334,7 +369,12 @@ void dhcp_start(void) __banked
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
get_random_32();
|
get_random_32();
|
||||||
dhcp_state.transaction_id = SFR_DATA_U32;
|
// Workaround SDCC bug 4070: dhcp_state.transaction_id = SFR_DATA_U32;
|
||||||
|
__xdata uint8_t * tid = &dhcp_state.transaction_id;
|
||||||
|
*tid++ = SFR_DATA_24;
|
||||||
|
*tid++ = SFR_DATA_16;
|
||||||
|
*tid++ = SFR_DATA_8;
|
||||||
|
*tid = SFR_DATA_0;
|
||||||
dhcp_state.state = DHCP_START;
|
dhcp_state.state = DHCP_START;
|
||||||
print_string("dhcp_start done\n");
|
print_string("dhcp_start done\n");
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -64,3 +64,33 @@ Writing 0x1 to register 0x7850 will transmit the frame. The Ethernet frame
|
|||||||
checksum and the TCP checksum are automatically calculated (offloaded) by the
|
checksum and the TCP checksum are automatically calculated (offloaded) by the
|
||||||
ASIC before transmitting on the wire.
|
ASIC before transmitting on the wire.
|
||||||
|
|
||||||
|
|
||||||
|
## The RTL tag words
|
||||||
|
|
||||||
|
The frame header uses the Realtek Remote Control Protocol (RRCP) format or
|
||||||
|
the like.
|
||||||
|
|
||||||
|
The `flags` word:
|
||||||
|
|
||||||
|
```
|
||||||
|
bit15 EFID_EN | 14:12 EFID | 11 PRI_EN | 10:8 PRI |
|
||||||
|
bit7 KEEP | 6 VSEL | 5 LEARN_DIS | 4:0 VIDX
|
||||||
|
```
|
||||||
|
|
||||||
|
All fields are in network byte order.
|
||||||
|
|
||||||
|
* `EFID_EN`, `EFID`: look the destination up under this filtering ID
|
||||||
|
instead of the port's own
|
||||||
|
* `PRI_EN`, `PRI`: force the given priority on the frame
|
||||||
|
* `KEEP`: keep the 802.1Q tagging of the frame exactly as injected,
|
||||||
|
bypassing the egress tagging rules of the port
|
||||||
|
* `VSEL`, `VIDX`: classify the frame into the VLAN at this index of the
|
||||||
|
VLAN table
|
||||||
|
* `LEARN_DIS`: do not learn the source address from this frame
|
||||||
|
|
||||||
|
The `pmask` word: bit 15 is `ALLOW`, bits 14 to 0 are a port mask.
|
||||||
|
|
||||||
|
* `ALLOW` clear: the mask is the egress set, the frame goes to exactly
|
||||||
|
the ports given
|
||||||
|
* `ALLOW` set: the ASIC looks the destination up as usual and the mask
|
||||||
|
only limits which ports the result may use
|
||||||
@@ -0,0 +1,55 @@
|
|||||||
|
# 2G040210GSM
|
||||||
|
|
||||||
|
The following is a documentation for the managed switch marked as `2G040210GSM`
|
||||||
|
and sold by Mokerlink.
|
||||||
|
|
||||||
|
### Label specifications
|
||||||
|
|
||||||
|
- **Name**: 4-port 2.5G Web Managed Switch
|
||||||
|
- **Ports**:
|
||||||
|
- 4 × RJ45: 10/100/1000/2500 Mbps
|
||||||
|
- 2 × SFP+: 1000 / 2500 / 10000 Mbps
|
||||||
|
- **Power**: 12V DC, 1A barrel connector
|
||||||
|
|
||||||
|
### What works
|
||||||
|
The device is fully supported:
|
||||||
|
- All 4 2.5GBASE-T RJ45 ports work at 10/100/1000/2500 Mbps
|
||||||
|
- The SFP+ port supports 1G, 2.5G and 10G modules
|
||||||
|
- LEDs work with the same indiciations as the OEM firmware (use KP_9000_6XHML_X2_V1_1 in machine.h if building yourself or the corresponding pre-compiled binary)
|
||||||
|
- untested due to missing Hardware: SFP+ ports equipped with 1G or 2.5G SFPs.
|
||||||
|
|
||||||
|
### Hardware overview
|
||||||
|
Front
|
||||||
|
|
||||||
|
<img src="photos/2M-PCB43-V1.1-managed/2M-PCB43-V1.1-front.jpeg" width="300" />
|
||||||
|
|
||||||
|
Label
|
||||||
|
|
||||||
|
<img src="photos/2M-PCB43-V1.1-managed/2M-PCB43-V1.1-label.jpeg" width="300" />
|
||||||
|
|
||||||
|
### PCB overview
|
||||||
|
|
||||||
|
**Board markings**
|
||||||
|
- Top silkscreen: 2M-PCB43-V1.1
|
||||||
|
|
||||||
|
Top side
|
||||||
|
|
||||||
|
<img src="photos/2M-PCB43-V1.1-managed/2M-PCB43-V1.1-top.jpeg" width="300" />
|
||||||
|
|
||||||
|
Bottom
|
||||||
|
|
||||||
|
<img src="photos/2M-PCB43-V1.1-managed/2M-PCB43-V1.1-bottom.jpeg" width="300" />
|
||||||
|
|
||||||
|
### J1, serial console
|
||||||
|
|
||||||
|
| `J8` pin | Signal |
|
||||||
|
| -------- | ----------- |
|
||||||
|
| 1 | RX (Input) |
|
||||||
|
| 2 | TX (Output) |
|
||||||
|
| 3 | GND |
|
||||||
|
| 4 | 3V3 |
|
||||||
|
|
||||||
|
|
||||||
|
## Power supply
|
||||||
|
|
||||||
|
Input power is delivered via barell plug, `12V 1A` adapter was provided.
|
||||||
@@ -0,0 +1,39 @@
|
|||||||
|
# FG-4GT-2SX_V2.0
|
||||||
|
|
||||||
|
Following is documentation for unmanaged switch marked as `FG-4GT-2SX_V2.0`.
|
||||||
|
|
||||||
|
Original software is running UART on 9600 baud rate.
|
||||||
|
|
||||||
|
## Brands
|
||||||
|
|
||||||
|
* Ruiying RY-4GT-2SX
|
||||||
|
|
||||||
|
<img src="photos/FG-4GT-2SX_V2.0/RY-4GT-2SX_label.jpg" width="300" />
|
||||||
|
|
||||||
|
## What works
|
||||||
|
|
||||||
|
- All four 2.5GBASE-T RJ45 ports at 10/100/1000/2500 Mbps
|
||||||
|
- Both SFP ports supporting 1G, 2.5G and 10G modules
|
||||||
|
- LEDs
|
||||||
|
|
||||||
|
## PCB overview
|
||||||
|
|
||||||
|
**Board markings**
|
||||||
|
|
||||||
|
- Top silkscreen: FG-4GT-2SX_V2.0
|
||||||
|
|
||||||
|
Front panel
|
||||||
|
|
||||||
|
<img src="photos/FG-4GT-2SX_V2.0/chassis-front.jpg" width="300" />
|
||||||
|
|
||||||
|
Top side
|
||||||
|
|
||||||
|
<img src="photos/FG-4GT-2SX_V2.0/PCB-top.jpg" width="300" />
|
||||||
|
|
||||||
|
Bottom
|
||||||
|
|
||||||
|
<img src="photos/FG-4GT-2SX_V2.0/PCB-bottom.jpg" width="300" />
|
||||||
|
|
||||||
|
## Power supply
|
||||||
|
|
||||||
|
Input power is delivered via barell plug, `12V 1A` adapter was provided.
|
||||||
@@ -0,0 +1,79 @@
|
|||||||
|
# FG-8GT-1SX
|
||||||
|
|
||||||
|
Following is documentation for unmanaged switch marked as `FG-8GT-1SX`.
|
||||||
|
|
||||||
|
Original software is running UART on 9600 baud rate.
|
||||||
|
|
||||||
|
Using SPI clamp in-board is the only method for initial installation.
|
||||||
|
|
||||||
|
### Brands
|
||||||
|
|
||||||
|
| Brand | Type | Managed | PCB | Flash | Chip RTL |
|
||||||
|
|---------|------------|---------|------------|-------------|---------------|
|
||||||
|
| Ruiying | RY-8GT-1SX | No | FG-8GT-1SX | GD25Q80ESIG | 8373N + 8224N |
|
||||||
|
|
||||||
|
### What works
|
||||||
|
|
||||||
|
- All eight 2.5GBASE-T RJ45 ports at 10/100/1000/2500 Mbps
|
||||||
|
- SFP port with 1G/2.5G/10G modules
|
||||||
|
- LEDs
|
||||||
|
|
||||||
|
### PCB overview
|
||||||
|
|
||||||
|
**Board markings**
|
||||||
|
|
||||||
|
- Top silkscreen: FG-8GT-1SX
|
||||||
|
|
||||||
|
Top side:
|
||||||
|
|
||||||
|
<img src="photos/FG-8GT-1SX/PCB-top.jpg" width="600" />
|
||||||
|
|
||||||
|
Bottom:
|
||||||
|
|
||||||
|
<img src="photos/FG-8GT-1SX/PCB-bottom.jpg" width="600" />
|
||||||
|
|
||||||
|
### Serial console
|
||||||
|
|
||||||
|
The PCB has five unpopulated through-holes near the SoC, with a white rectangle surrouding them, labeled as `J18` and a triangle points to the square shaped first pin.
|
||||||
|
|
||||||
|
This is where the "expected" UART header should be soldered at, with redundant 3V3 VCC, but also with missing 0Ω resistors between TX/RX and SoC, so simply soldering a header would not work. One need also add the missing resistors or solder the pads together, while making sure not connecting unrelated pads.
|
||||||
|
|
||||||
|
Numbered from the triangle, the header pinout is:
|
||||||
|
|
||||||
|
| Position | Signal | GPIO | Status |
|
||||||
|
|----------|--------|--------|-------------|
|
||||||
|
| 1 | GND | GND | Internal |
|
||||||
|
| 2 | TX | GPIO31 | Unconnected |
|
||||||
|
| 3 | RX | GPIO32 | Unconnected |
|
||||||
|
| 4 | 3V3 | - | Internal |
|
||||||
|
| 5 | 3V3 | - | Internal |
|
||||||
|
|
||||||
|
There're four resistor pads near the header.
|
||||||
|
|
||||||
|
| Resistor ID | SoC Side | Header Side |
|
||||||
|
|-------------|-------------|-------------|
|
||||||
|
| R1240 | TX / GPIO31 | Pin 2 |
|
||||||
|
| R1243 | 3V3 | - |
|
||||||
|
| R1242 | 3V3 | Pin 3 |
|
||||||
|
| R1241 | RX / GPIO32 | - |
|
||||||
|
|
||||||
|
To get TX working, solder a 0Ω resistor between the pads for R1240 or solder them together.
|
||||||
|
|
||||||
|
For RX However, the designer certainly made a mistake, as the SoC-side lines to the resistor expected for Rx (R1242) and 3V3 (R1241) are swapped. For RX to work, the R1241 SoC side and R1242 header side shall be connected, so either:
|
||||||
|
|
||||||
|
- Solder: R1241 SoC side -> R1241 header side -> R1242 header side
|
||||||
|
- Jump wire: R1241 SoC side -> R1242 header side
|
||||||
|
|
||||||
|
Be sure not to bring R1242 SoC side to the connection as that would wire Rx to 3v3.
|
||||||
|
|
||||||
|
A complete working serial header should look like following on this PCB:
|
||||||
|
|
||||||
|
<img src="photos/FG-8GT-1SX/Serial.png" width="600" />
|
||||||
|
|
||||||
|
- **Settings**: 115200 baud / 8N1 / 3.3V TTL
|
||||||
|
- Connect a USB-TTL adapter: adapter GND → pin 1, RX → pin 2, TX → pin 3
|
||||||
|
|
||||||
|
### Power supply
|
||||||
|
|
||||||
|
Input power is delivered via barell plug, `12V 1A` adapter was provided.
|
||||||
|
|
||||||
@@ -1,17 +1,36 @@
|
|||||||
# Keeplink KP-9000-6XH-X2
|
# Keeplink KP-9000-6XH-X2 / KP-9000-6XHML-X2
|
||||||
|
|
||||||
Following is documentation for unmanaged switch marked as `KP-9000-6XH-X2`.
|
Following is documentation for switches marked as `KP-9000-6XH-X2` or
|
||||||
|
`KP-9000-6XHML-X2`.
|
||||||
|
|
||||||
Using SPI clamp in-board is the only method for initial installation.
|
Using SPI clamp in-board is the only method for initial installation.
|
||||||
|
|
||||||
### Label specifications
|
### Label specifications
|
||||||
|
|
||||||
- **Name**: 4X 2.5G RJ45 Port + 2 X 10G SFP+ Port
|
- **Name**: 4X 2.5G RJ45 Port + 2 X 10G SFP+ Port
|
||||||
- **Model**: KP-9000-6XH-X2
|
- **Model**: KP-9000-6XH-X2 / KP-9000-6XHML-X2
|
||||||
- **Ports**:
|
- **Ports**:
|
||||||
- 4 × RJ45: 10/100/1000/2500 Mbps
|
- 4 × RJ45: 10/100/1000/2500 Mbps
|
||||||
- 2 × SFP+: 1000 / 2500 / 10000 Mbps
|
- 2 × SFP+: 1000 / 2500 / 10000 Mbps
|
||||||
|
|
||||||
|
### Machine target
|
||||||
|
|
||||||
|
These devices exist with different PCB revisions. Select the machine target by
|
||||||
|
the PCB silkscreen, not by the label on the case. The `6XH` / `6XHML`
|
||||||
|
distinction appears to be a stock firmware, SKU or label difference, not a
|
||||||
|
reliable indicator of PCB wiring. The PCB revision defines the hardware layout.
|
||||||
|
|
||||||
|
| PCB silkscreen | Known labels / devices | Recommended machine target | Equivalent target | Legacy target |
|
||||||
|
| --- | --- | --- | --- | --- |
|
||||||
|
| `2M-PCB43-V1.1` | Mokerlink 2G040210GSM web-managed 4+2 switch | `MACHINE_KP_9000_6XHML_X2_V1_1` | `MACHINE_KP_9000_6XH_X2_V1_1` | `MACHINE_KP_9000_6XHML_X2` |
|
||||||
|
| `2M-PCB43-V1.2` | `KP-9000-6XHML-X2` | `MACHINE_KP_9000_6XHML_X2_V1_2` | `MACHINE_KP_9000_6XH_X2_V1_2` | `MACHINE_KP_9000_6XHML_X2` |
|
||||||
|
| `2M-PCB43-V2.1` | `KP-9000-6XH-X2`, `KP-9000-6XHML-X2` | `MACHINE_KP_9000_6XH_X2_V2_1` or `MACHINE_KP_9000_6XHML_X2_V2_1` | same V2.1 layout | `MACHINE_KP_9000_6XH_X2` |
|
||||||
|
|
||||||
|
The V1.1 and V1.2 boards currently use the same V1.x GPIO, SFP, port and LED
|
||||||
|
layout. The V2.1 board uses a different V2.1 layout with custom LED muxing.
|
||||||
|
Legacy targets are preserved for compatibility with already-tested devices, but
|
||||||
|
new builds should prefer the explicit PCB-revision targets.
|
||||||
|
|
||||||
### What works
|
### What works
|
||||||
|
|
||||||
- All four 2.5GBASE-T RJ45 ports at 10/100/1000/2500 Mbps
|
- All four 2.5GBASE-T RJ45 ports at 10/100/1000/2500 Mbps
|
||||||
@@ -19,7 +38,7 @@ Using SPI clamp in-board is the only method for initial installation.
|
|||||||
- LEDs
|
- LEDs
|
||||||
- untested due to missing Hardware: SFP+ ports equipped with 1G or 2.5G SFPs.
|
- untested due to missing Hardware: SFP+ ports equipped with 1G or 2.5G SFPs.
|
||||||
|
|
||||||
### Hardware overview
|
### Hardware overview: 2M-PCB43-V2.1
|
||||||
|
|
||||||
Front side:
|
Front side:
|
||||||
|
|
||||||
@@ -44,6 +63,24 @@ Bottom
|
|||||||
|
|
||||||
<img src="photos/2M-PCB43-V2.1-unmanaged/2M-PCB43-V2.1-bottom.jpg" width="600" />
|
<img src="photos/2M-PCB43-V2.1-unmanaged/2M-PCB43-V2.1-bottom.jpg" width="600" />
|
||||||
|
|
||||||
|
### Hardware overview: 2M-PCB43-V1.1 / V1.2
|
||||||
|
|
||||||
|
The V1.1 board has been reported in the Mokerlink 2G040210GSM web-managed 4+2
|
||||||
|
switch. The V1.2 board has been seen in managed `KP-9000-6XHML-X2` devices.
|
||||||
|
Both use the same V1.x layout.
|
||||||
|
|
||||||
|
Label:
|
||||||
|
|
||||||
|
<img src="photos/KP-9000-6XHML-X2/label.jpg" width="600" />
|
||||||
|
|
||||||
|
Top side:
|
||||||
|
|
||||||
|
<img src="photos/KP-9000-6XHML-X2/pcb_top.jpg" width="600" />
|
||||||
|
|
||||||
|
Bottom:
|
||||||
|
|
||||||
|
<img src="photos/KP-9000-6XHML-X2/pcb_bottom.jpg" width="600" />
|
||||||
|
|
||||||
## Reset Button
|
## Reset Button
|
||||||
|
|
||||||
There's an unpopulated Reset button on the front left side of the PCB.
|
There's an unpopulated Reset button on the front left side of the PCB.
|
||||||
@@ -54,4 +91,3 @@ The front case has already the hole in the metal case, you just have to punch a
|
|||||||
## Power supply
|
## Power supply
|
||||||
|
|
||||||
Input power is delivered via barell plug, `12V 1A` adapter was provided.
|
Input power is delivered via barell plug, `12V 1A` adapter was provided.
|
||||||
|
|
||||||
@@ -0,0 +1,97 @@
|
|||||||
|
Hisource Hi-K0402WS V2
|
||||||
|
|
||||||
|
RTL8372-based 4×2.5G + 2×SFP+ unmanaged switch.
|
||||||
|
|
||||||
|
Using SPI clamp in-board is the only method for initial installation.
|
||||||
|
|
||||||
|
### Label specifications
|
||||||
|
|
||||||
|
- **Manufacturer**: HISOURCE
|
||||||
|
- **Model**: Hi-K0402WS V2
|
||||||
|
- **Ports**:
|
||||||
|
- 4 × RJ45: 10/100/1000/2500 Mbps
|
||||||
|
- 2 × SFP+: 1G / 2.5G / 10G
|
||||||
|
|
||||||
|
### What works
|
||||||
|
|
||||||
|
- All four 2.5GBASE-T RJ45 ports at 10/100/1000/2500 Mbps
|
||||||
|
- Both SFP+ ports supporting 1G, 2.5G and 10G modules
|
||||||
|
- LEDs: amber (2.5G) and green (1G/100M/10M) per copper port; combined link/act on SFP ports
|
||||||
|
|
||||||
|
### PCB overview
|
||||||
|
|
||||||
|
**Board markings**
|
||||||
|
- PCB-K0402WS-V2.0
|
||||||
|
- DIP-K0402WS-V2.0
|
||||||
|
|
||||||
|
**Key components**
|
||||||
|
- U1: SPI NOR flash, 2 MiB
|
||||||
|
- S1: unpopulated slide switch footprint (three through-holes used as serial console)
|
||||||
|
|
||||||
|
Front panel
|
||||||
|
|
||||||
|
<img src="photos/PCB-K0402WS-V2.0/chassis-front.jpg" width="400" />
|
||||||
|
|
||||||
|
Bottom label
|
||||||
|
|
||||||
|
<img src="photos/PCB-K0402WS-V2.0/chassis-bottom.jpg" width="400" />
|
||||||
|
|
||||||
|
Top side (PCB)
|
||||||
|
|
||||||
|
<img src="photos/PCB-K0402WS-V2.0/PCB-top.jpg" width="300" />
|
||||||
|
|
||||||
|
### Port layout
|
||||||
|
|
||||||
|
| Front panel position | Logical port | Physical port | Type |
|
||||||
|
|----------------------|--------------|---------------|---------|
|
||||||
|
| SFP left | 8 | 5 | SFP+ |
|
||||||
|
| RJ45 1 | 4 | 1 | Copper |
|
||||||
|
| RJ45 2 | 5 | 2 | Copper |
|
||||||
|
| RJ45 3 | 6 | 3 | Copper |
|
||||||
|
| RJ45 4 | 7 | 4 | Copper |
|
||||||
|
| SFP right | 3 | 6 | SFP+ |
|
||||||
|
|
||||||
|
### Serial console
|
||||||
|
|
||||||
|
The PCB has three unpopulated through-holes intended for a slide switch, directly connected to UART0.
|
||||||
|
Numbered from the left (SFP port side), the pinout is:
|
||||||
|
|
||||||
|
| Position (left→right) | Signal | GPIO |
|
||||||
|
|-----------------------|--------|--------------------------|
|
||||||
|
| 1 (leftmost) | RX | GPIO32\_UART0\_RX (32) |
|
||||||
|
| 2 (middle) | GND | GND |
|
||||||
|
| 3 (rightmost) | TX | GPIO31\_UART0\_TX (31) |
|
||||||
|
|
||||||
|
- **Settings**: 115200 baud / 8N1 / 3.3V TTL
|
||||||
|
- Connect a USB-TTL adapter: adapter TX → pin 1, GND → pin 2, adapter RX → pin 3
|
||||||
|
|
||||||
|
### LED configuration
|
||||||
|
|
||||||
|
Copper ports use LED SET0, SFP ports use LED SET1.
|
||||||
|
|
||||||
|
| SET | LED0 | LED2 |
|
||||||
|
|------|--------------------------------------------------|---------------------------------------------------|
|
||||||
|
| SET0 | Amber — lights on 2.5G link | Green — lights on 1G / 100M / 10M link |
|
||||||
|
| SET1 | All speeds — lights on any link with activity | — |
|
||||||
|
|
||||||
|
LED pad to physical port mapping:
|
||||||
|
|
||||||
|
| GPIO pads | Port |
|
||||||
|
|-----------|-------------------------|
|
||||||
|
| GPIO8–11 | Physical port 5 (left SFP) |
|
||||||
|
| GPIO12–14 | Physical port 1 (RJ45 1) |
|
||||||
|
| GPIO15–17 | Physical port 2 (RJ45 2) |
|
||||||
|
| GPIO18–20 | Physical port 3 (RJ45 3) |
|
||||||
|
| GPIO21–23 | Physical port 4 (RJ45 4) |
|
||||||
|
| GPIO24–27 | Physical port 6 (right SFP) |
|
||||||
|
|
||||||
|
### SFP GPIO assignments
|
||||||
|
|
||||||
|
| SFP | pin\_detect (ModAbs) | pin\_los | SerDes | I2C SDA | I2C SCL |
|
||||||
|
|------------------|-----------------------------|------------------------|--------|----------------------|--------------------------|
|
||||||
|
| Left (logical 8) | GPIO30\_ACL\_BIT3\_EN | GPIO37 | SDS1 | GPIO39\_I2C\_SDA4 | GPIO40\_I2C\_SCL3\_MDC1 |
|
||||||
|
| Right (logical 3)| GPIO50\_I2C\_SCL2\_UART1\_TX | GPIO51\_I2C\_SDA2\_UART1\_RX | SDS0 | GPIO41\_I2C\_SDA3\_MDIO1 | GPIO40\_I2C\_SCL3\_MDC1 |
|
||||||
|
|
||||||
|
GPIO assignments were verified by observing GPIO state changes during SFP module insertion/removal
|
||||||
|
and cross-checked against an original firmware register dump.
|
||||||
|
`pin_tx_disable` is GPIO\_NA on both ports (original firmware keeps all GPIOs as inputs).
|
||||||
@@ -19,6 +19,9 @@ of the device just above the big label, might be covered by a QC sticker.
|
|||||||
|
|
||||||
<img src="photos/PCB-K0402WS-V3.0/Ztyuav_Z-QWYT0402_label.jpg" width="300" />
|
<img src="photos/PCB-K0402WS-V3.0/Ztyuav_Z-QWYT0402_label.jpg" width="300" />
|
||||||
|
|
||||||
|
* Siscolink SL-G0402F
|
||||||
|
|
||||||
|
<img src="photos/PCB-K0402WS-V3.0/Siscolink SL-G0402F.jpg" width="300"/>
|
||||||
### Programming
|
### Programming
|
||||||
|
|
||||||
Using SPI clamp in-board is the only method for initial installation.
|
Using SPI clamp in-board is the only method for initial installation.
|
||||||
|
|||||||
@@ -1,4 +1,10 @@
|
|||||||
# SWTG018AS-A V2.0
|
# SWTG018AS-A V2.0
|
||||||
|
## Brands
|
||||||
|
| Brand | Type |Managed| PCB | Flash | Chip RTL |
|
||||||
|
|--------|------------------|-------|------------------|-----------------|---------------|
|
||||||
|
| Ampcom | SWTG018AS-A V2.0 | No | SWTG018AS-A V2.0 | 2MB | 8273N + 8224N |
|
||||||
|
| Horaco | HC-SWTGW218AS-A | Yes | SWTG018AS-A V2.0 | 2MB(25Q16JVSIQ) | 8273N + 8224N |
|
||||||
|
|
||||||
|
|
||||||
The following is a documentation for the unmanaged switch marked as `SWTG018AS-A V2.0`.
|
The following is a documentation for the unmanaged switch marked as `SWTG018AS-A V2.0`.
|
||||||
It is e.g. sold under the Ampcom brand, but no branh-markings are found on the device.
|
It is e.g. sold under the Ampcom brand, but no branh-markings are found on the device.
|
||||||
|
|||||||
@@ -0,0 +1,43 @@
|
|||||||
|
### SWTG024AS-A-V2.0.1_4C_2SFP
|
||||||
|
It is highly similar to SWTG024AS-V2.0, with the only difference being the GPIO configuration for the SFP port.
|
||||||
|
|
||||||
|
## Brands
|
||||||
|
|Brand|Type|Managed|PCB|Flash|Chip RTL|
|
||||||
|
|---|---|---|---|---|---|
|
||||||
|
| Horaco | ZX-SG4T2 | | PCB-SWTG024AS-A-V2.0.1_19650 | P25D40SH | ??? |
|
||||||
|
|
||||||
|
### Label specifications
|
||||||
|
|
||||||
|
- **Name**:
|
||||||
|
- **Ports**:
|
||||||
|
- 4 × RJ45: 10/100/1000/2500 Mbps
|
||||||
|
- 2 × SFP+: 1000 / 2500 / 10000 Mbps
|
||||||
|
|
||||||
|
<img src="photos/SWTG024AS-A-V2_0_1_19650/horaco-zx-sg4t2-label.jpg" width="300" />
|
||||||
|
|
||||||
|
### What works
|
||||||
|
The device is fully supported:
|
||||||
|
- ALL 2.5GBASE-T RJ45 ports work at 10/100/1000/2500 Mbps
|
||||||
|
- The SFP+ port supports 1G, 2.5G and 10G modules
|
||||||
|
|
||||||
|
### PCB overview
|
||||||
|
|
||||||
|
**Board markings**
|
||||||
|
- Top silkscreen: PCB-SWTG024AS-A-V2.0.1_19650
|
||||||
|
|
||||||
|
Top side
|
||||||
|
|
||||||
|
<img src="photos/SWTG024AS-A-V2_0_1_19650/horaco-zx-sg4t2-pcb-top.jpg" width="300" />
|
||||||
|
|
||||||
|
Bottom
|
||||||
|
|
||||||
|
<img src="photos/SWTG024AS-A-V2_0_1_19650/horaco-zx-sg4t2-pcb-bottom.jpg" width="300" />
|
||||||
|
|
||||||
|
### J1, serial console
|
||||||
|
|
||||||
|
| `J1` pin | Signal |
|
||||||
|
| -------- | ----------- |
|
||||||
|
| 1 | 3V3 |
|
||||||
|
| 2 | GND |
|
||||||
|
| 3 | RX (Input) |
|
||||||
|
| 4 | TX (Output) |
|
||||||
@@ -0,0 +1,44 @@
|
|||||||
|
### SWTG024AS-A-V2.0.1_5C_1SFP
|
||||||
|
It is highly similar to SWTG024AS-V2.0, with the only difference being the GPIO configuration for the SFP port.
|
||||||
|
|
||||||
|
## Brands
|
||||||
|
|Brand|Type|Managed|PCB|Flash|Chip RTL|
|
||||||
|
|---|---|---|---|---|---|
|
||||||
|
| Horaco | HC-SWTGW215AS | | PCB-SWTG024AS-A-V2.0.1_19650 | W25Q16JV | 8272N |
|
||||||
|
|
||||||
|
### Label specifications
|
||||||
|
|
||||||
|
- **Name**:
|
||||||
|
- **Ports**:
|
||||||
|
- 5 × RJ45: 10/100/1000/2500 Mbps
|
||||||
|
- 1 × SFP+: 1000 / 2500 / 10000 Mbps
|
||||||
|
|
||||||
|
<!-- <img src="" width="300" /> -->
|
||||||
|
|
||||||
|
### What works
|
||||||
|
The device is fully supported:
|
||||||
|
- ALL 2.5GBASE-T RJ45 ports work at 10/100/1000/2500 Mbps
|
||||||
|
- The SFP+ port supports 1G, 2.5G and 10G modules
|
||||||
|
- LEDs work with the same indiciations as the OEM firmware
|
||||||
|
|
||||||
|
### PCB overview
|
||||||
|
|
||||||
|
**Board markings**
|
||||||
|
- Top silkscreen: PCB-SWTG024AS-A-V2.0.1_19650
|
||||||
|
|
||||||
|
Top side
|
||||||
|
|
||||||
|
<!-- <img src="" width="300" /> -->
|
||||||
|
|
||||||
|
Bottom
|
||||||
|
|
||||||
|
<!-- <img src="" width="300" /> -->
|
||||||
|
|
||||||
|
### J1, serial console
|
||||||
|
|
||||||
|
| `J1` pin | Signal |
|
||||||
|
| -------- | ----------- |
|
||||||
|
| 1 | 3V3 |
|
||||||
|
| 2 | GND |
|
||||||
|
| 3 | RX (Input) |
|
||||||
|
| 4 | TX (Output) |
|
||||||
@@ -0,0 +1,49 @@
|
|||||||
|
### SWTG024AS-V2.0
|
||||||
|
|
||||||
|
## Brands
|
||||||
|
|Brand|Type|Managed|PCB|Flash|Chip RTL|
|
||||||
|
|---|---|---|---|---|---|
|
||||||
|
| hongyavision | LG-SG5T1 | No | PCB-SWTG024AS-V2.0_16895 | 25Q40 | 8272 |
|
||||||
|
|
||||||
|
### Label specifications
|
||||||
|
|
||||||
|
- **Name**:
|
||||||
|
- **Ports**:
|
||||||
|
- 5 × RJ45: 10/100/1000/2500 Mbps
|
||||||
|
- 1 × SFP+: 1000 / 2500 / 10000 Mbps
|
||||||
|
- **Power**: 12V DC, 1A 5525 connector
|
||||||
|
|
||||||
|
<img src="photos/SWTG024AS-V2.0/label.jpg" width="300" />
|
||||||
|
|
||||||
|
### What works
|
||||||
|
The device is fully supported:
|
||||||
|
- ALL 2.5GBASE-T RJ45 ports work at 10/100/1000/2500 Mbps
|
||||||
|
- The SFP+ port supports 1G, 2.5G and 10G modules
|
||||||
|
- LEDs work with the same indiciations as the OEM firmware
|
||||||
|
- Online update does not work with 512KiB flash.
|
||||||
|
### PCB overview
|
||||||
|
|
||||||
|
**Board markings**
|
||||||
|
- Top silkscreen: PCB-SWTG024AS-V2.0
|
||||||
|
|
||||||
|
Top side
|
||||||
|
|
||||||
|
<img src="photos/SWTG024AS-V2.0/pcb_top.jpg" width="300" />
|
||||||
|
|
||||||
|
Bottom
|
||||||
|
|
||||||
|
<img src="photos/SWTG024AS-V2.0/pcb_bottom.jpg" width="300" />
|
||||||
|
|
||||||
|
### J1, serial console
|
||||||
|
|
||||||
|
| `J1` pin | Signal |
|
||||||
|
| -------- | ----------- |
|
||||||
|
| 1 | GND |
|
||||||
|
| 2 | RX (Input) |
|
||||||
|
| 3 | TX (Output) |
|
||||||
|
|
||||||
|
Note,`R52`、`R53`may not be installed.You need to bridge them using either solder or resistors.
|
||||||
|
|
||||||
|
## Power supply
|
||||||
|
|
||||||
|
Input power is delivered via barell plug, `12V 1A` adapter was provided.
|
||||||
@@ -9,8 +9,8 @@ Also the RJ45 connectors can be all plastic/non-shielded or with metal shielding
|
|||||||
|Brand|Type|Managed|PCB|PCB Label|Flash|Chip RTL|
|
|Brand|Type|Managed|PCB|PCB Label|Flash|Chip RTL|
|
||||||
|---|---|---|---|---|---|---|
|
|---|---|---|---|---|---|---|
|
||||||
| LIANGUO |SWTG024AS |No| SWTG024AS-v2.0-17452 | CM-23-11-2336 023-17453| 512 KiB | 8272 |
|
| LIANGUO |SWTG024AS |No| SWTG024AS-v2.0-17452 | CM-23-11-2336 023-17453| 512 KiB | 8272 |
|
||||||
| Haraco |ZX-SWTG124AS | Yes | SWTG024AS-v2.0 | ??? | ??? | 8272 |
|
| Horaco |ZX-SWTG124AS | Yes | SWTG024AS-v2.0 | ??? | ??? | 8272 |
|
||||||
| Xikestore |SKS3200M-4GPY2XF | Yes | SWTG024AS-v1.0 | CM-23-08-2043 023-16721 | ??? | 8272 |
|
| Xikestore |SKS3200M-4GPY2XF | Yes | SWTG024AS-v1.0 | CM-23-08-2043 023-16721 | 2048 KiB | 8272 |
|
||||||
| Sodola | SL-SWTG124AS-D | Yes | SWTG024AS-v2.0-17452 | ??? | 2048 KiB | 8272 |
|
| Sodola | SL-SWTG124AS-D | Yes | SWTG024AS-v2.0-17452 | ??? | 2048 KiB | 8272 |
|
||||||
|
|
||||||
## PCB
|
## PCB
|
||||||
|
|||||||
@@ -1,9 +1,11 @@
|
|||||||
### ZX-SWTGW215AS
|
# ZX-SWTGW215AS
|
||||||
|
|
||||||
## Brands
|
## Brands
|
||||||
|
|
||||||
|Brand|Type|Managed|PCB|Flash|Chip RTL|
|
|Brand|Type|Managed|PCB|Flash|Chip RTL|
|
||||||
|---|---|---|---|---|---|
|
|---|---|---|---|---|---|
|
||||||
| Lianguo | ZX-SWTGW215AS | Yes | PCB-SWTG115AS-V2.0 | FM25Q16A | 8272 |
|
| Lianguo | ZX-SWTGW215AS | Yes | PCB-SWTG115AS-V2.0 | FM25Q16A | 8372 |
|
||||||
|
| sold as Lianguo, actual device unbranded | ZX-SWTGW215AS | Yes | PCB-SWTG115AS-V2.1 | W25Q16JVSIQ (2MiB) | 8372 |
|
||||||
|
|
||||||
## RTLPlayground target
|
## RTLPlayground target
|
||||||
|
|
||||||
@@ -12,13 +14,23 @@ Use machine target `MACHINE_LIANGUO_ZX_SWTGW215AS` for this device.
|
|||||||
Physical hardware verification: 5x RJ45 ports + 1x SFP port.
|
Physical hardware verification: 5x RJ45 ports + 1x SFP port.
|
||||||
Port 5 RJ45 is interfaced through a RTL8221B IC.
|
Port 5 RJ45 is interfaced through a RTL8221B IC.
|
||||||
|
|
||||||
## PCB
|
## Label (V2.0)
|
||||||
|
|
||||||
<img src="photos/ZX-SWTGW215AS/pcb_top.jpg" width="300" />
|
<img src="photos/ZX-SWTGW215AS-V2.0/label.jpg" width="300" />
|
||||||
|
|
||||||
# Connectors
|
## PCB (V2.0)
|
||||||
|
|
||||||
## Port overview
|
<img src="photos/ZX-SWTGW215AS-V2.0/pcb_top.jpg" width="300" />
|
||||||
|
<img src="photos/ZX-SWTGW215AS-V2.0/pcb_bottom.jpg" width="300" />
|
||||||
|
|
||||||
|
## PCB (V2.1)
|
||||||
|
|
||||||
|
<img src="photos/ZX-SWTGW215AS-V2.1/pcb-top.jpg" width="300" />
|
||||||
|
<img src="photos/ZX-SWTGW215AS-V2.1/pcb-bottom.jpg" width="300" />
|
||||||
|
|
||||||
|
## Connectors
|
||||||
|
|
||||||
|
### Port overview
|
||||||
|
|
||||||
```
|
```
|
||||||
┌──────────────────────────────────────────────────────────────────────────────────┐
|
┌──────────────────────────────────────────────────────────────────────────────────┐
|
||||||
@@ -36,7 +48,7 @@ Port 5 RJ45 is interfaced through a RTL8221B IC.
|
|||||||
| RJ45 | 3, 4, 5, 6, 7 | 1-5 |
|
| RJ45 | 3, 4, 5, 6, 7 | 1-5 |
|
||||||
| SFP | 8 | 6 |
|
| SFP | 8 | 6 |
|
||||||
|
|
||||||
## J4
|
### J4
|
||||||
|
|
||||||
* Location: SFP connector `J4`.
|
* Location: SFP connector `J4`.
|
||||||
* Connected to: 10GMAC number 8, SerDes 1.
|
* Connected to: 10GMAC number 8, SerDes 1.
|
||||||
@@ -48,10 +60,11 @@ Port 5 RJ45 is interfaced through a RTL8221B IC.
|
|||||||
|5| MODDEF1 – SCL | GPIO40 | I2C SCL |
|
|5| MODDEF1 – SCL | GPIO40 | I2C SCL |
|
||||||
|6| MODDEF0 – PRESENT | GPIO30 | Detect |
|
|6| MODDEF0 – PRESENT | GPIO30 | Detect |
|
||||||
|8| LOS | GPIO37 | RX Loss of Signal |
|
|8| LOS | GPIO37 | RX Loss of Signal |
|
||||||
### Notes
|
|
||||||
|
#### Notes
|
||||||
* Not all signals were mapped mechanically, hence they've been left out of documentation.
|
* Not all signals were mapped mechanically, hence they've been left out of documentation.
|
||||||
|
|
||||||
## T3, Slave Interface
|
### T3, Slave Interface
|
||||||
|
|
||||||
This connector goes to U4 `I2C EEPROM` and U10 `SPI FLASH` (mappings identical to SWTG024AS).
|
This connector goes to U4 `I2C EEPROM` and U10 `SPI FLASH` (mappings identical to SWTG024AS).
|
||||||
|
|
||||||
@@ -65,11 +78,12 @@ For detailed Slave Interface functionality and protocol information, see [T3 doc
|
|||||||
|4| VCC |
|
|4| VCC |
|
||||||
|5| 33R -> U10-P2 | SPI-DO/D1 |
|
|5| 33R -> U10-P2 | SPI-DO/D1 |
|
||||||
|6| U10-P1 | SPI-CS |
|
|6| U10-P1 | SPI-CS |
|
||||||
### Notes
|
|
||||||
|
#### Notes
|
||||||
* 1 pin is square shaped.
|
* 1 pin is square shaped.
|
||||||
|
|
||||||
|
|
||||||
## T5, Serial Console
|
### T5, Serial Console
|
||||||
|
|
||||||
|`T5` pin|GPIO|Signal|
|
|`T5` pin|GPIO|Signal|
|
||||||
|---|---|---|
|
|---|---|---|
|
||||||
@@ -77,10 +91,11 @@ For detailed Slave Interface functionality and protocol information, see [T3 doc
|
|||||||
| 2 | GND | |
|
| 2 | GND | |
|
||||||
| 3 | GPIO32 | U0RXD (Input) |
|
| 3 | GPIO32 | U0RXD (Input) |
|
||||||
| 4 | 3V3 | |
|
| 4 | 3V3 | |
|
||||||
### Notes
|
|
||||||
|
#### Notes
|
||||||
* 1 pin is square shaped.
|
* 1 pin is square shaped.
|
||||||
|
|
||||||
## T8
|
### T8
|
||||||
|
|
||||||
|`T8` pin|GPIO|Signal|
|
|`T8` pin|GPIO|Signal|
|
||||||
|---|---|---|
|
|---|---|---|
|
||||||
@@ -91,18 +106,19 @@ For detailed Slave Interface functionality and protocol information, see [T3 doc
|
|||||||
| 5 | GPIO47 | |
|
| 5 | GPIO47 | |
|
||||||
| 6 | GPIO49 | |
|
| 6 | GPIO49 | |
|
||||||
|
|
||||||
### Notes
|
#### Notes
|
||||||
* 1 pin is square shaped.
|
* 1 pin is square shaped.
|
||||||
* Mapping unverified but assumed the same as [LIANGUO SWTG024AS](SWTG024AS.md#t8).
|
* Mapping unverified but assumed the same as [LIANGUO SWTG024AS](SWTG024AS.md#t8).
|
||||||
|
|
||||||
# Reset Circuit
|
## Reset Circuit
|
||||||
| Function | GPIO |
|
| Function | GPIO |
|
||||||
|---|---|
|
|---|---|
|
||||||
| Reset button | GPIO54 |
|
| Reset button | GPIO54 |
|
||||||
|
|
||||||
### Notes
|
### Notes
|
||||||
* Circuit is active-low
|
* Circuit is active-low
|
||||||
|
|
||||||
# GPIO
|
## GPIO
|
||||||
|
|
||||||
Note: T3/U4/U10-related signal annotations below are copied from [LIANGUO SWTG024AS T3 section](SWTG024AS.md#t3-slave-interface) as well as T8 port from [LIANGUO SWTG024AS T8 section](SWTG024AS.md#t8). They should be treated as assumed identical for ZX-SWTGW215AS as it has not been 100% confirmed true at the moment.
|
Note: T3/U4/U10-related signal annotations below are copied from [LIANGUO SWTG024AS T3 section](SWTG024AS.md#t3-slave-interface) as well as T8 port from [LIANGUO SWTG024AS T8 section](SWTG024AS.md#t8). They should be treated as assumed identical for ZX-SWTGW215AS as it has not been 100% confirmed true at the moment.
|
||||||
|
|
||||||
@@ -141,7 +157,7 @@ Note: T3/U4/U10-related signal annotations below are copied from [LIANGUO SWTG02
|
|||||||
| 40000000 | GPIO30 | J4-6 | SFP DETECT | | GPIO62 | | |
|
| 40000000 | GPIO30 | J4-6 | SFP DETECT | | GPIO62 | | |
|
||||||
| 80000000 | GPIO31 | T5-1 | U0TXD | | GPIO63 | | |
|
| 80000000 | GPIO31 | T5-1 | U0TXD | | GPIO63 | | |
|
||||||
|
|
||||||
# LEDs
|
## LEDs
|
||||||
|
|
||||||
| NAME | GPIO | Port(s) | Function | Notes |
|
| NAME | GPIO | Port(s) | Function | Notes |
|
||||||
| ---- | ---- | ---- | ---- | ---- |
|
| ---- | ---- | ---- | ---- | ---- |
|
||||||
@@ -162,3 +178,6 @@ Note: T3/U4/U10-related signal annotations below are copied from [LIANGUO SWTG02
|
|||||||
|
|
||||||
While [SWTG024AS.md](SWTG024AS.md) can be used as a general reference for hardware concepts and interface specifications, this device should not be assumed to be identical beside the difference implicitely highlighted below. Not all information has been validated for compatibility with the SWTG215AS. Consult the SWTG024AS documentation with caution and verify any critical details against this device's.
|
While [SWTG024AS.md](SWTG024AS.md) can be used as a general reference for hardware concepts and interface specifications, this device should not be assumed to be identical beside the difference implicitely highlighted below. Not all information has been validated for compatibility with the SWTG215AS. Consult the SWTG024AS documentation with caution and verify any critical details against this device's.
|
||||||
|
|
||||||
|
## Flashing
|
||||||
|
|
||||||
|
For devices with Winbond 25Q16JVSIQ, flashing is possible for example using flashprog with device "W25Q16.V". Note that you should not overwrite the whole flash to keep device-specific data (like the MAC address at 0x1fc000). Also, creating a backup of the entire falsh before is of course a very good idea.
|
||||||
@@ -6,6 +6,7 @@
|
|||||||
| Mokerlink | ZX-SWTGW218AS | Yes| SWTG118AS-V2.0-16029 | 2MB (FM25Q16A)| 8273N + 8224N |
|
| Mokerlink | ZX-SWTGW218AS | Yes| SWTG118AS-V2.0-16029 | 2MB (FM25Q16A)| 8273N + 8224N |
|
||||||
| Sodola | | | | | |
|
| Sodola | | | | | |
|
||||||
| Horaco | | | | | |
|
| Horaco | | | | | |
|
||||||
|
| XikeStor | SKS3200-8E1X | Yes | SWTG118AS-V2.1-17462 | 2MB (25Q16JVSIQ) | |
|
||||||
|
|
||||||
|
|
||||||
## Photos
|
## Photos
|
||||||
|
|||||||
|
After Width: | Height: | Size: 1.8 MiB |
|
After Width: | Height: | Size: 715 KiB |
|
After Width: | Height: | Size: 615 KiB |
|
After Width: | Height: | Size: 2.1 MiB |
|
After Width: | Height: | Size: 133 KiB |
|
After Width: | Height: | Size: 207 KiB |
|
After Width: | Height: | Size: 129 KiB |
|
After Width: | Height: | Size: 203 KiB |
|
After Width: | Height: | Size: 1.2 MiB |
|
After Width: | Height: | Size: 1.1 MiB |
|
After Width: | Height: | Size: 1.9 MiB |
|
After Width: | Height: | Size: 170 KiB |
|
After Width: | Height: | Size: 115 KiB |
|
After Width: | Height: | Size: 84 KiB |
|
After Width: | Height: | Size: 3.5 MiB |
|
After Width: | Height: | Size: 455 KiB |
|
After Width: | Height: | Size: 710 KiB |
|
After Width: | Height: | Size: 1.6 MiB |
|
After Width: | Height: | Size: 3.2 MiB |
|
After Width: | Height: | Size: 9.7 MiB |
|
After Width: | Height: | Size: 36 MiB |
|
After Width: | Height: | Size: 476 KiB |
|
After Width: | Height: | Size: 657 KiB |
@@ -65,3 +65,36 @@ ASIC and flushing the table in order to quickly forget the learned entries.
|
|||||||
3c:18:a0:7e:11:00 0x0001 learned 5
|
3c:18:a0:7e:11:00 0x0001 learned 5
|
||||||
1c:2a:a3:23:00:02 0x0001 learned 7
|
1c:2a:a3:23:00:02 0x0001 learned 7
|
||||||
```
|
```
|
||||||
|
|
||||||
|
## Static multicast entries
|
||||||
|
|
||||||
|
Slow-protocol frames such as LACPDUs and STP BPDUs have to reach the CPU
|
||||||
|
without being flooded to the other ports. No bridge relays these frames:
|
||||||
|
their addresses are in the set that 802.1D-2004 clause 7.12.6 forbids a
|
||||||
|
bridge to forward, and what travels the network is the information, with
|
||||||
|
every bridge regenerating BPDUs of its own on its designated ports. The reserved-multicast *trap* action
|
||||||
|
cannot do that on this hardware, because its destination is an external CPU
|
||||||
|
attached to a physical port, which these boards do not populate. The protocol
|
||||||
|
modules therefore leave the reserved-multicast action at *forward* and constrain
|
||||||
|
the egress with a static L2 multicast entry instead: the lookup hits the entry's
|
||||||
|
own port mask rather than the VLAN flood mask. Verified on a SWTGW218AS both
|
||||||
|
ways, with the CPU bit cleared, where delivery stops, and with the CPU bit alone,
|
||||||
|
where nothing egresses.
|
||||||
|
|
||||||
|
`port_l2mc_set()` writes one such entry. The SMI layout is the L2 multicast
|
||||||
|
variant of the table entry:
|
||||||
|
|
||||||
|
```
|
||||||
|
DATA_IN_A = MAC bytes 5..2 -> c2 00 00 <mac_last>
|
||||||
|
DATA_IN_B = MAC[1..0] | vid<<16 | IVL<<29 | pmask[1:0]<<30
|
||||||
|
DATA_IN_C = pmask[9:2]
|
||||||
|
```
|
||||||
|
|
||||||
|
Lookups are IVL, so an entry made for VID 0 is never matched and a caller adds
|
||||||
|
one entry per PVID in use. The write goes through the table access register
|
||||||
|
with the table selector set to the L2 lookup table, `TBL_L2_UNICAST` in the
|
||||||
|
code, a name that despite appearances covers the multicast entries as well.
|
||||||
|
The hardware hashes MAC and VID to pick the bucket slot by itself.
|
||||||
|
Writing the same MAC and VID again replaces the entry rather than adding a
|
||||||
|
second one, so a caller can retarget the mask at will, for instance back to all
|
||||||
|
ports to restore flooding.
|
||||||
@@ -48,9 +48,11 @@ void port_lag_hash_set(__xdata uint8_t lag, __xdata uint8_t hash_bits) __banked;
|
|||||||
## LAG configuration on the Serial Console
|
## LAG configuration on the Serial Console
|
||||||
For testing the following commands are provided on the serial console:
|
For testing the following commands are provided on the serial console:
|
||||||
```
|
```
|
||||||
> lag <LAG-ID> [p1] [p2]...
|
> lag <LAG-ID> <p1> [p2]...
|
||||||
Create or set a LAG. Trunk-ID is 1 or 2. Ports are physical ports
|
Create or set a LAG. LAG-ID is 1 to 4. Ports are physical ports.
|
||||||
If only the LAG-ID is given but no members, the LAG is deleted
|
|
||||||
|
> lag <LAG-ID> d
|
||||||
|
Delete the LAG.
|
||||||
|
|
||||||
> lag show
|
> lag show
|
||||||
Shows information on all 4 lags
|
Shows information on all 4 lags
|
||||||
@@ -92,7 +94,7 @@ The following shows the network configuration
|
|||||||
On _both_ switches create a LAG with ports 1 and 2 inside and the default hash algorithm which takes
|
On _both_ switches create a LAG with ports 1 and 2 inside and the default hash algorithm which takes
|
||||||
source and destination ports into account, e.g. just use the default:
|
source and destination ports into account, e.g. just use the default:
|
||||||
```
|
```
|
||||||
> lag 0 1 2
|
> lag 1 1 2
|
||||||
```
|
```
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
@@ -37,12 +37,13 @@ This list is incomplete.
|
|||||||
| Brand | Partnumber |
|
| Brand | Partnumber |
|
||||||
| ---------- |----------- |
|
| ---------- |----------- |
|
||||||
| GigaDevice | GD25Q32E |
|
| GigaDevice | GD25Q32E |
|
||||||
|
| Fundan | FM25Q16A |
|
||||||
|
| Puya | P25D40SH |
|
||||||
| Winbond | W25Q16JV |
|
| Winbond | W25Q16JV |
|
||||||
| Winbond | W25Q32FV |
|
| Winbond | W25Q32FV |
|
||||||
| Winbond | W25Q32JV |
|
| Winbond | W25Q32JV |
|
||||||
| Winbond | W25Q16JL |
|
| Winbond | W25Q16JL |
|
||||||
| Winbond | W25Q16DV |
|
| Winbond | W25Q16DV |
|
||||||
| Winbond | W25Q80DV |
|
| Winbond | W25Q80DV |
|
||||||
| Fundan | FM25Q16A |
|
|
||||||
|
|
||||||
*NOTE*: Part numbers are incomplete. Part numbers may contain additional information such as package, temperature specifications, and even the number of devices on a reel. So always check the datasheet so that you have the right orderable partnumber.
|
*NOTE*: Part numbers are incomplete. Part numbers may contain additional information such as package, temperature specifications, and even the number of devices on a reel. So always check the datasheet so that you have the right orderable partnumber.
|
||||||
@@ -0,0 +1,155 @@
|
|||||||
|
# Spanning Tree (STP / RSTP)
|
||||||
|
|
||||||
|
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.
|
||||||
|
|
||||||
|
STP can be enabled and controlled via the web interface or the command line,
|
||||||
|
as follows:
|
||||||
|
|
||||||
|
## Quick start
|
||||||
|
|
||||||
|
```
|
||||||
|
stp on # start participating
|
||||||
|
stp off # stop, all ports back to forwarding
|
||||||
|
```
|
||||||
|
|
||||||
|
Live status is on the Spanning Tree page of the web UI (or `/stp.json`),
|
||||||
|
and on the serial console via `stp status`.
|
||||||
|
|
||||||
|
With no other bridge around, the switch elects itself root and every port ends
|
||||||
|
up forwarding — you can leave it on safely. Put the settings in the startup
|
||||||
|
config to make them survive a reboot:
|
||||||
|
|
||||||
|
```
|
||||||
|
stp prio 15
|
||||||
|
stp port 1 edge on
|
||||||
|
stp on
|
||||||
|
```
|
||||||
|
|
||||||
|
## Hardware background
|
||||||
|
|
||||||
|
BPDUs are addressed to `01:80:C2:00:00:00`, a reserved link-local group. The
|
||||||
|
ASIC's Reserved-Multicast action for that address decides what happens to the
|
||||||
|
frame.
|
||||||
|
|
||||||
|
Forwarding to the CPU port works normally: the 8051 sits behind an ordinary
|
||||||
|
port of the internal switch and is an ordinary member of a forwarding mask.
|
||||||
|
The *trap* action does not deliver to it. Its destination is an external CPU
|
||||||
|
attached to a physical port (`cpuTag_externalCpuPort_set`, `EXT_CPU_CTRL` in
|
||||||
|
the vendor SDK), which these boards do not populate. The ACL trap and
|
||||||
|
redirect actions do not deliver to the 8051 either.
|
||||||
|
|
||||||
|
Delivery therefore uses the *forward* action, constrained to the CPU port
|
||||||
|
by a static L2 multicast entry (`port_l2mc_set()`), one per VLAN in use:
|
||||||
|
|
||||||
|
* while STP runs, the entry's member mask is the CPU port only — BPDUs reach
|
||||||
|
the CPU and are not flooded to other ports, as a participating bridge
|
||||||
|
requires;
|
||||||
|
* with STP off, the same entries are retargeted to all ports, restoring the
|
||||||
|
transparency an unmanaged switch is expected to have, so a surrounding
|
||||||
|
spanning tree can span *through* this device.
|
||||||
|
|
||||||
|
A BPDU delivered this way is an ordinary frame to the port's ingress logic
|
||||||
|
and passes through its acceptable-frame-type filter. BPDUs are untagged, so a
|
||||||
|
port set to admit tagged frames only (`ingress <port>t`) never delivers one
|
||||||
|
to the CPU. `stp_setup()` prints a warning for every STP-enabled port in that
|
||||||
|
state.
|
||||||
|
|
||||||
|
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
|
||||||
|
hands it and still passes a received BPDU up to the CPU, which is what lets
|
||||||
|
loop detection go on working on a port it has already blocked.
|
||||||
|
|
||||||
|
## Timers
|
||||||
|
|
||||||
|
`stp_timers()` runs at 50 Hz (the main loop idles on the 200 Hz system tick and
|
||||||
|
STP is called every fourth pass), which is what `STP_HZ` in `rtl837x_stp.h`
|
||||||
|
encodes. All configured values are in seconds:
|
||||||
|
|
||||||
|
| setting | default | range |
|
||||||
|
|---|---|---|
|
||||||
|
| `stp hello <n>` | 2 | 1–10 |
|
||||||
|
| `stp maxage <n>` | 20 | 6–40 |
|
||||||
|
| `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.
|
||||||
|
|
||||||
|
## 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.
|
||||||
|
|
||||||
|
## Bridge settings
|
||||||
|
|
||||||
|
```
|
||||||
|
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>
|
||||||
|
```
|
||||||
|
|
||||||
|
The bridge with the lowest priority wins the root election; ties are broken by
|
||||||
|
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.
|
||||||
|
|
||||||
|
## Per-port settings
|
||||||
|
|
||||||
|
```
|
||||||
|
stp port <1-9> on|off # take part in STP, or stay plain forwarding
|
||||||
|
stp port <1-9> edge on|off|auto # host-facing port handling (default: auto)
|
||||||
|
stp port <1-9> cost <0-200000000> # path cost, 0 = automatic (20000)
|
||||||
|
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
|
||||||
|
```
|
||||||
|
|
||||||
|
**edge** — an edge port forwards immediately and does not trigger a
|
||||||
|
topology change when its link comes and goes; `auto` promotes a port to edge
|
||||||
|
after three seconds without a BPDU, and demotes it as soon as one arrives. Use
|
||||||
|
`edge on` for ports where only hosts are attached.
|
||||||
|
|
||||||
|
**guard** — `bpdu` disables a port as soon as a BPDU arrives on it (a host port
|
||||||
|
should never see one); `root` keeps a port from ever becoming the path to the
|
||||||
|
root, which protects an existing topology from a newly attached bridge that
|
||||||
|
claims a better priority.
|
||||||
|
|
||||||
|
**filter** — the port neither sends nor accepts BPDUs. Useful when the device
|
||||||
|
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.
|
||||||
|
|
||||||
|
## 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:
|
||||||
|
|
||||||
|
```
|
||||||
|
GET /stp.json
|
||||||
|
```
|
||||||
|
|
||||||
|
The `stp status` command prints the same view on the serial console.
|
||||||
|
|
||||||
|
## 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.
|
||||||
@@ -0,0 +1,135 @@
|
|||||||
|
# Supporting Multiple Languages in the Web UI
|
||||||
|
|
||||||
|
The firmware uses a client-side i18n approach
|
||||||
|
all translations are stored in a single JavaScript dictionary embedded in the firmware.
|
||||||
|
No server-side changes are needed.
|
||||||
|
|
||||||
|
## Architecture
|
||||||
|
|
||||||
|
All translation logic lives in `html/i18n.js`. The file contains:
|
||||||
|
|
||||||
|
- A `LANG` object with one sub-object per language (`en`, `ja`, ...)
|
||||||
|
- Language auto-detection (browser language → `localStorage` override)
|
||||||
|
- `t(key)` — look up a translated string
|
||||||
|
- `setLang(lang)` — switch language and update the page
|
||||||
|
- `applyTranslation(el)` — apply translation to one DOM element
|
||||||
|
|
||||||
|
Translation keys are **flat strings** (no nesting). The English keys in `LANG.en` also serve as the fallback when a key is missing in another language.
|
||||||
|
|
||||||
|
## How to Add a New Language
|
||||||
|
|
||||||
|
### 1. Add a dictionary entry in `html/i18n.js`
|
||||||
|
|
||||||
|
Append a new sub-object to the `LANG` object. Every key from `LANG.en` must be present:
|
||||||
|
|
||||||
|
```js
|
||||||
|
var LANG = {
|
||||||
|
en: {
|
||||||
|
nav_overview: 'Overview',
|
||||||
|
nav_port_config: 'Port Configuration',
|
||||||
|
// ... all keys for English
|
||||||
|
},
|
||||||
|
ja: {
|
||||||
|
nav_overview: '概要',
|
||||||
|
nav_port_config: 'ポート設定',
|
||||||
|
// ... all keys for Japanese
|
||||||
|
},
|
||||||
|
LANGCODE: { // ← add your language here
|
||||||
|
nav_overview: '...',
|
||||||
|
nav_port_config: '...',
|
||||||
|
// ... translate every key
|
||||||
|
},
|
||||||
|
};
|
||||||
|
```
|
||||||
|
|
||||||
|
### 2. Add the language to the navigation sidebar
|
||||||
|
|
||||||
|
In `html/navigation.js`, add an `<option>` to the language selector:
|
||||||
|
|
||||||
|
```js
|
||||||
|
+ "<option value='en'>English</option><option value='ja'>日本語</option>"
|
||||||
|
```
|
||||||
|
|
||||||
|
Replace with:
|
||||||
|
|
||||||
|
```js
|
||||||
|
+ "<option value='en'>English</option><option value='ja'>日本語</option><option value='LANGCODE'>Native Name</option>"
|
||||||
|
```
|
||||||
|
|
||||||
|
### 3. Verify auto-detection
|
||||||
|
|
||||||
|
The language detection code in `i18n.js` reads `navigator.language` and normalises it to the first two characters:
|
||||||
|
|
||||||
|
```js
|
||||||
|
var browser = (navigator.language || navigator.userLanguage || 'en').substring(0, 2);
|
||||||
|
return LANG[browser] ? browser : 'en';
|
||||||
|
```
|
||||||
|
|
||||||
|
If the two-letter code matches a key in `LANG`, it will be auto-selected. No changes needed here.
|
||||||
|
|
||||||
|
## Two Translation Mechanisms
|
||||||
|
|
||||||
|
### (A) `data-i18n` attribute (declarative — for HTML)
|
||||||
|
|
||||||
|
Add `data-i18n="key_name"` to any HTML element. The English text goes in the element content as a fallback:
|
||||||
|
|
||||||
|
```html
|
||||||
|
<h1 data-i18n="port_heading">Port Configuration</h1>
|
||||||
|
<input type="button" data-i18n="port_apply" value="Apply">
|
||||||
|
<option data-i18n="port_auto">Auto</option>
|
||||||
|
<title data-i18n="port_title">Port Configuration</title>
|
||||||
|
```
|
||||||
|
|
||||||
|
On page load, `applyTranslation()` sets:
|
||||||
|
|
||||||
|
- `el.value` for `<input type="submit|button">`
|
||||||
|
- `el.textContent` for `<option>`, `<title>`
|
||||||
|
- `el.innerHTML` for everything else
|
||||||
|
|
||||||
|
### (B) `t('key')` call (imperative — for JavaScript strings)
|
||||||
|
|
||||||
|
When generating HTML or text in JavaScript, wrap translatable strings with `t()`:
|
||||||
|
|
||||||
|
```js
|
||||||
|
td.appendChild(document.createTextNode(t('common_port') + i));
|
||||||
|
td.innerHTML = t('port_auto');
|
||||||
|
iHTML += "<tr><td>" + t('port_vendor') + "</td></tr>";
|
||||||
|
```
|
||||||
|
|
||||||
|
### Special Case: Link Speed Display
|
||||||
|
|
||||||
|
The `linkS` array in `html/main.js` maps numeric link states to display strings. The first two entries (`speed_disabled`, `speed_down`) use `t()` for translation; the remaining entries are static literals (they are the same in all languages):
|
||||||
|
|
||||||
|
```js
|
||||||
|
const linkS = [
|
||||||
|
function(){return t('speed_disabled')},
|
||||||
|
function(){return t('speed_down')},
|
||||||
|
"10M", "100M", "1000M", "500M", "10G", "2.5G", "5G"
|
||||||
|
];
|
||||||
|
function linkText(idx) { var v = linkS[idx]; return typeof v === 'function' ? v() : v; }
|
||||||
|
```
|
||||||
|
|
||||||
|
Always use `linkText(idx)` (not `linkS[idx]`) to read these values.
|
||||||
|
|
||||||
|
## Size Considerations
|
||||||
|
|
||||||
|
- `html/i18n.js` is embedded in the firmware filesystem (~14 KB for two languages)
|
||||||
|
- Each new language adds roughly the same number of bytes as the English dictionary (~3–4 KB)
|
||||||
|
- The firmware binary is padded to 512 KiB, so a few extra KB do not change the flash footprint
|
||||||
|
- Values that are identical in all languages should be inlined as literals rather than added to the dictionary (e.g., `"10M"`, `"2.5G"`, `"MAC"`, `"VLAN"`, `"CPU"`)
|
||||||
|
|
||||||
|
## Build
|
||||||
|
|
||||||
|
No special flags are needed. The `html/` directory is embedded by `fileadder` during the build.
|
||||||
|
|
||||||
|
## Script Load Order
|
||||||
|
|
||||||
|
`i18n.js` must be loaded after `main.js` (which defines `t()`'s dependencies like `LANG`) but before any page-specific JS that calls `t()`:
|
||||||
|
|
||||||
|
```html
|
||||||
|
<script src="/main.js"></script>
|
||||||
|
<script src="/i18n.js"></script>
|
||||||
|
<script src="/eee.js"></script> <!-- uses t() -->
|
||||||
|
```
|
||||||
|
|
||||||
|
The `navigation.js` script is loaded last (bottom of `<body>`).
|
||||||
@@ -1,18 +1,43 @@
|
|||||||
# Supported Hardware
|
# Supported Hardware
|
||||||
The following devices have been tested and are fully working:
|
The following devices have been tested and are fully working:
|
||||||
- Horaco ZX_SG4T2
|
|
||||||
- keepLINK kp-9000-6hx-x2 (RTL8372: 4x 2.5GBit + 2x 10GBit SFP+)
|
|
||||||
- keepLINK KP-9000-6XHML-X2, same as above, but Managed
|
|
||||||
- keepLINK kp-9000-6hx-x (RTL8372 + RTL8221B 2.5GBit PHY: 5 x 2.5GBit + 1x 10GBit SFP+)
|
|
||||||
- keepLINK kp-9000-9xh-x-eu (1 x RTL8373 + RTL8224: 8x 2.5GBit + 1x 10GBit SFP+)
|
|
||||||
- Lianguo LG-SWTGW218AS (RTL8373 + RTL8224 PHY: 8x 2.5GBit + 1x 10GBit SFP+)
|
|
||||||
- No-Name ZX-SWTGW215AS, managed version of kp-9000-6hx-x, ordered on
|
|
||||||
AliExpress as keepLINK 5+1 port managed
|
|
||||||
- TrendNet TEG-S562 (RTL8372: 4x 2.5GBit + 2x 10GBit SFP+)
|
|
||||||
- FNS-1200P (RTL8372: 4x 2.5GBit + 2x 10GBit SFP+)
|
|
||||||
|
|
||||||
Other device based on RTL8272/3 that may work are described here: [Up-N-Atoms 2.5 GBit RTL Switch hacking guide]
|
| Brand | Type | Managed | PCB | Flash | Ports |
|
||||||
(https://github.com/up-n-atom/SWTG118AS)
|
|----------|-----------------|---------|---------------------------------------------------------------------------|-------|-------|
|
||||||
|
| Ampcom | WAM902-SWTG018AS| No | [SWTG018AS-A V2.0](devices/SWTG018AS_A_V2_0.md) | | 8 + 1 |
|
||||||
|
| Davuaz | Da-K6501W | No | [PCB-K0501W-V2.0](devices/K0501W_V2_0.md) | | 5 + 1 |
|
||||||
|
| FOXNEO | FNS-1200P | No | [PCB-K0402W-U13-V2.0](devices/FNS-1200P.md) | 2M | 4 + 2 |
|
||||||
|
| Hisource | Hi-K0402WS | No | [PCB-K0402WS-V3.0](devices/PCB-K0402WS-V3.0.md) | | 4 + 2 |
|
||||||
|
| Hisource | Hi-K0402WS | No | [PCB-K0402WS-V2.0](devices/PCB-K0402WS-V2.0.md) | 2M | 4 + 2 |
|
||||||
|
| Hisource | Hi-K0801WS | No | [PCB-KO801W-V2.0](devices/HI-K0801WS.md) | | 8 + 1 |
|
||||||
|
| hongyavision | LG-SG5T1 | No | [PCB-SWTG024AS-V2.0_16895](devices/SWTG024AS-V2.0.md) | 0.5M | 5 + 1 |
|
||||||
|
| Horaco | HC-SWTGW215AS | Yes | [SWTG024AS-A-V2.0.1_19650 5C 1SFP](devices/SWTG024AS-A-V2.0.1_5C_1SFP.md) | ? | 5 + 1 |
|
||||||
|
| Horaco | HC-SWTGW218AS | Yes | [SWTG018AS-A V2.0](devices/SWTG018AS_A_V2_0.md) | | 8 + 1 |
|
||||||
|
| Horaco | ZX310S-4T2XH | Yes | [PCB-SL310S-4T1T1X-V1.0.1-24107](devices/ZX310S-4T2XH.md) | 2M | 5 + 1 |
|
||||||
|
| Horaco | ZX310S-4T2XT | Yes | [PCB-SL310S-4T2XT-V1.0.0-22273](devices/ZX310S-4T2XT.md) | 2M | 6 |
|
||||||
|
| Horaco | ZX-SG4T2 | No | [SWTG024AS-A-V2.0.1_19650_4C_2SFP](devices/SWTG024AS-A-V2.0.1_4C_2SFP.md) | 0.5M | 4 + 2 |
|
||||||
|
| Horaco | ZX-SWTG124AS | Yes | [SWTG024AS-v2.0](devices/SWTG024AS.md) | | 4 + 2 |
|
||||||
|
| Keeplink | KP-9000-6XH-X2 / KP-9000-6XHML-X2 | No/Yes | [2M-PCB43-V1.2 / V2.1](devices/KP-9000-6XH-X2.md) | | 4 + 2 |
|
||||||
|
| 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 |
|
||||||
|
| 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 |
|
||||||
|
| Mokerlink| 2G040210GSM | Yes | [2M-PCB43-V1.1](devices/2M-PCB43-V1.1.md) | | 4 + 2 |
|
||||||
|
| Mokerlink| ZX-SWTGW218AS | Yes | [SWTG118AS-V2.0-16029](devices/SWTGW218AS.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 |
|
||||||
|
| SiscoLink | SL-G0402F | No | [PCB-K0402WS-V3.0](devices/PCB-K0402WS-V3.0.md) | | 4 + 2 |
|
||||||
|
| Sodola | SL-SWTG124AS-D | Yes | [SWTG024AS-v2.0-17452](devices/SWTG024AS.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 |
|
||||||
|
| Xikestore| 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 |
|
||||||
|
| Ztyuav | Z-QWYT0402 | No | [PCB-K0402WS-V3.0](devices/PCB-K0402WS-V3.0.md) | | 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
|
||||||
|
alone does not identify the wiring.
|
||||||
|
|
||||||
|
Other device based on RTL8272/3 that may work are described here: [Up-N-Atoms 2.5 GBit RTL Switch hacking guide](https://github.com/up-n-atom/SWTG118AS)
|
||||||
|
|
||||||
Many of the RTL8272/3 devices come in versions with PoE support. The RTLPlayground usually also
|
Many of the RTL8272/3 devices come in versions with PoE support. The RTLPlayground usually also
|
||||||
works on these, however, no support for configuring PoE is provided, simply because these
|
works on these, however, no support for configuring PoE is provided, simply because these
|
||||||
|
|||||||
@@ -69,9 +69,9 @@ void vlan_delete(uint16_t vlan) __banked;
|
|||||||
# VLAN configuration on the Serial Console
|
# VLAN configuration on the Serial Console
|
||||||
For testing the following commands are provided on the serial console:
|
For testing the following commands are provided on the serial console:
|
||||||
```
|
```
|
||||||
vlan <VLAN-ID> p[t/u]...
|
vlan <VLAN-ID> p[t]...
|
||||||
create or set vlan with given ID and the list of ports as members, a t
|
create or set vlan with given ID and the list of ports as members, a `t`
|
||||||
behind a port defines the port as a tagged member, the u is optional
|
behind a port defines the port as a tagged member.
|
||||||
|
|
||||||
vlan <VLAN-ID> d
|
vlan <VLAN-ID> d
|
||||||
deletes the VLAN
|
deletes the VLAN
|
||||||
|
|||||||
@@ -0,0 +1,106 @@
|
|||||||
|
# Compressing the Web UI Assets
|
||||||
|
|
||||||
|
The Web UI (all files under `html/`) is embedded into the flash image and
|
||||||
|
served over HTTP. Two build-time steps keep it small: a safe minifier and
|
||||||
|
gzip compression of the embedded files.
|
||||||
|
|
||||||
|
## How it works
|
||||||
|
|
||||||
|
```
|
||||||
|
html/ --(minify.py)--> output/html_min/ --(fileadder -z)--> flash image
|
||||||
|
```
|
||||||
|
|
||||||
|
- `tools/minify.py` removes comments (`<!-- -->`, `//`, `/* */`) and
|
||||||
|
line-leading whitespace without touching string or regex literals, so
|
||||||
|
the output is functionally identical to the input. Binary files
|
||||||
|
(`.ico`) are copied through unchanged. The raw sources in `html/` stay
|
||||||
|
untouched for development; the minified copy in `output/` is a pure
|
||||||
|
build artifact. Comments and indentation are pure overhead for the
|
||||||
|
served bytes, so minification alone already reduces the transfer size
|
||||||
|
from 109,814 to 97,730 bytes, independent of gzip.
|
||||||
|
- The Web UI is a single-page application: `index.html` holds all pages
|
||||||
|
as sections (`#/ports`, `#/vlan`, ...) that the sidebar switches
|
||||||
|
between via the URL hash. `login.html` stays separate because it is
|
||||||
|
the authentication gate. Only these two pages plus `main.js`,
|
||||||
|
`style.css`, the three SVG port images and the favicon are embedded.
|
||||||
|
- All JavaScript (i18n, shared helpers, navigation and the per-section
|
||||||
|
scripts) lives in one `html/main.js` bundle that the pages load as
|
||||||
|
their only script, so the browser fetches it once and gzip can
|
||||||
|
compress across all of it. A section's initialisation runs when the
|
||||||
|
section is shown, its polling intervals run only while it is visible,
|
||||||
|
and re-entering a section refetches the data.
|
||||||
|
- `fileadder -z` gzip-compresses every file (zlib, gzip format,
|
||||||
|
`Z_BEST_COMPRESSION`) when it generates the file table
|
||||||
|
(`html_data.c`/`html_data.h`) and when it embeds the files into the
|
||||||
|
final image. Both invocations run on `output/html_min/`, so the table
|
||||||
|
and the image always agree.
|
||||||
|
- The httpd serves each file with `Content-Encoding: gzip` according to
|
||||||
|
the `gzip` flag in its `f_data` entry. The 8051 only copies compressed
|
||||||
|
bytes from flash to the socket; decompression happens in the browser.
|
||||||
|
- Only the static files are compressed. The JSON API responses
|
||||||
|
(`/information.json`, `/vlanlist`, ...) are generated on the fly and
|
||||||
|
stay uncompressed.
|
||||||
|
|
||||||
|
## Measured effect
|
||||||
|
|
||||||
|
Build on any machine (the Web UI is machine-independent):
|
||||||
|
|
||||||
|
| | bytes |
|
||||||
|
|---|---:|
|
||||||
|
| raw sources | 109,814 |
|
||||||
|
| minified (comments and indentation removed) | 97,730 |
|
||||||
|
| gzip (flash + wire) | 25,302 (23.0 %) |
|
||||||
|
|
||||||
|
The minified assets are also what the browser receives when gzip support
|
||||||
|
is missing or disabled, so the transfer size drops in both stages:
|
||||||
|
109,814 → 97,730 bytes from minification, then → 25,302 bytes from gzip.
|
||||||
|
|
||||||
|
The embedded data block ends at `0x5b977` without compression and at
|
||||||
|
`0x462d6` with it, freeing ~86 KB of the 512 KB image.
|
||||||
|
|
||||||
|
The consolidation helps at every step. The 17 scripts gzip to 23,302
|
||||||
|
bytes as separate files but to 17,231 bytes as one `main.js` bundle,
|
||||||
|
because the gzip dictionary spans all of them. The single-page layout
|
||||||
|
then removes the per-page HTML and, more importantly, the repeated
|
||||||
|
downloads: with the multi-page UI the browser fetched `main.js` on every
|
||||||
|
navigation; the SPA fetches everything once and section switches are
|
||||||
|
pure in-page JavaScript (measured on a real device: cold load ~450 ms,
|
||||||
|
section switch ~60-160 ms, the only network traffic afterwards is the
|
||||||
|
JSON polling of the visible section).
|
||||||
|
|
||||||
|
## Notes
|
||||||
|
|
||||||
|
- The files stay well below the `uint16_t` size limit of the file table
|
||||||
|
(largest gzip output: 17.2 KB for the merged `main.js`).
|
||||||
|
- `fileadder` terminates the embedded files with a NUL directly after the
|
||||||
|
content (previously at `data_read + 1`), so the `strlen()`-based size
|
||||||
|
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.
|
||||||
|
|
||||||
|
## Verification
|
||||||
|
|
||||||
|
Decompress what the firmware embeds and compare it with the minified
|
||||||
|
sources:
|
||||||
|
|
||||||
|
```python
|
||||||
|
import gzip, re
|
||||||
|
h = open('html_data.h').read(); c = open('html_data.c').read()
|
||||||
|
starts = dict((k, int(v, 16)) for k, v in
|
||||||
|
re.findall(r'#define FDATA_START_(\w+) 0x([0-9a-f]+)', h))
|
||||||
|
sizes = dict((k, int(v)) for k, v in
|
||||||
|
re.findall(r'#define FDATA_SIZE_(\w+) (\d+)', h))
|
||||||
|
entries = [(p, sizes[z], starts[s]) for p, s, z in
|
||||||
|
re.findall(r'\{"/([^"]+)", FDATA_START_(\w+), '
|
||||||
|
r'FDATA_SIZE_(\w+), \w+, 1\}', c)]
|
||||||
|
d = open('output/rtlplayground.bin', 'rb').read()
|
||||||
|
for name, size, start in entries:
|
||||||
|
assert gzip.decompress(d[start:start+size]) == \
|
||||||
|
open('output/html_min/' + name, 'rb').read()
|
||||||
|
```
|
||||||
|
|
||||||
|
`tools/httpd_sim` serves the raw files from `html/` and does not exercise
|
||||||
|
the gzip path; a browser session against a flashed switch (or `curl
|
||||||
|
--compressed`) is the final check.
|
||||||
@@ -1,20 +0,0 @@
|
|||||||
<!DOCTYPE html>
|
|
||||||
<html>
|
|
||||||
<script src="/main.js"></script>
|
|
||||||
<link rel="stylesheet" href="style.css">
|
|
||||||
<title>Ingress and Egress Bandwidth</title>
|
|
||||||
</head>
|
|
||||||
<body>
|
|
||||||
<nav id="sidebar"></nav>
|
|
||||||
<div style="margin-left:16%;padding:1px 16px;height:1000px;">
|
|
||||||
<div id="ports"></div>
|
|
||||||
<h1>Ingress and Egress Bandwidth</h1>
|
|
||||||
<table id="bwtable">
|
|
||||||
<tr> <th> </th> <th colspan="3"> Ingress </th> <th colspan="2">Egress</th> <th></th></tr>
|
|
||||||
<tr> <th>Port</th> <th>Limit</th> <th>Bandwidth [kBit/s]</th> <th>Flow Control</th> <th>Limit</th> <th>Bandwidth [kBit/s]</th> <th>Apply</th></tr>
|
|
||||||
</table>
|
|
||||||
<script src="/bandwidth.js"></script>
|
|
||||||
</div>
|
|
||||||
<script src="/navigation.js"></script>
|
|
||||||
</body>
|
|
||||||
</html>
|
|
||||||
@@ -1,138 +0,0 @@
|
|||||||
const iLayout = '" type="number" maxlength="10" size="10" onfocus="inputFocus(';
|
|
||||||
function createBW() {
|
|
||||||
var tbl = document.getElementById('bwtable');
|
|
||||||
const limit = '<input type="checkbox" id="limit_port" onchange="exec();">'
|
|
||||||
if (tbl.rows.length <= 2 && numPorts) {
|
|
||||||
console.log("CREATING TABLE ", tbl.rows.length);
|
|
||||||
for (let i = 2; i < 2 + numPorts; i++) {
|
|
||||||
const tr = tbl.insertRow();
|
|
||||||
let td = tr.insertCell(); td.appendChild(document.createTextNode(`Port ${i-1}`));
|
|
||||||
td = tr.insertCell();
|
|
||||||
td.innerHTML = limit.replaceAll("limit_port", "ilimit_port_" + i).replace("exec()", "iClicked(" + i + ")");
|
|
||||||
td = tr.insertCell();
|
|
||||||
td.innerHTML = 'UNLIMITED';
|
|
||||||
td = tr.insertCell();
|
|
||||||
td.innerHTML = limit.replaceAll("limit_port", "fc_port_" + i).replace("exec()", "document.getElementById('bwapply_" + i + "').disabled=false;");
|
|
||||||
td = tr.insertCell();
|
|
||||||
td.innerHTML = limit.replaceAll("limit_port", "elimit_port_" + i).replace("exec()", "eClicked(" + i + ")");
|
|
||||||
td = tr.insertCell();
|
|
||||||
td.innerHTML = 'UNLIMITED';
|
|
||||||
var button = '<button type="button" id="bwapply_' + i + '" style="margin: 0 0 0 24px" onclick="applyBandwidth(' + i + ');">Apply</button>';
|
|
||||||
td = tr.insertCell();
|
|
||||||
td.innerHTML = button;
|
|
||||||
document.getElementById("bwapply_" + i).disabled = true;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
function iClicked(i)
|
|
||||||
{
|
|
||||||
document.getElementById("bwapply_" + i).disabled=false;
|
|
||||||
var tbl = document.getElementById('bwtable');
|
|
||||||
var tr = tbl.rows[i];
|
|
||||||
if (!document.getElementById("ilimit_port_" + i).checked) {
|
|
||||||
tr.cells[2].innerHTML = "UNLIMITED";
|
|
||||||
document.getElementById("fc_port_" + i).disabled = true;
|
|
||||||
document.getElementById("fc_port_" + i).checked = true;
|
|
||||||
} else {
|
|
||||||
tr.cells[2].innerHTML = '<input id="ibw_' + i + iLayout + i + ')" value="0"/>';
|
|
||||||
document.getElementById("fc_port_" + i).disabled = false;
|
|
||||||
document.getElementById("fc_port_" + i).checked = true;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
function eClicked(i)
|
|
||||||
{
|
|
||||||
document.getElementById("bwapply_" + i).disabled=false;
|
|
||||||
var tbl = document.getElementById('bwtable');
|
|
||||||
var tr = tbl.rows[i];
|
|
||||||
if (!document.getElementById("elimit_port_" + i).checked) {
|
|
||||||
tr.cells[5].innerHTML = "UNLIMITED";
|
|
||||||
} else {
|
|
||||||
tr.cells[5].innerHTML = '<input id="ebw_' + i + iLayout + i + ')" value="0"/>';
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
function inputFocus(i)
|
|
||||||
{
|
|
||||||
document.getElementById("bwapply_" + i).disabled=false;
|
|
||||||
}
|
|
||||||
|
|
||||||
async function doCMD(cmd)
|
|
||||||
{
|
|
||||||
console.log("Sending >" + cmd + "<");
|
|
||||||
try {
|
|
||||||
const response = await fetch('/cmd', {
|
|
||||||
method: 'POST',
|
|
||||||
body: cmd
|
|
||||||
});
|
|
||||||
console.log('Completed!', response);
|
|
||||||
} catch(err) {
|
|
||||||
console.error(`Error: ${err}`);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
async function applyBandwidth(i) {
|
|
||||||
var tbl = document.getElementById('bwtable');
|
|
||||||
var tr = tbl.rows[i];
|
|
||||||
var cmd = "bw in " + (i-1) + " off";
|
|
||||||
if (document.getElementById("ilimit_port_" + i).checked)
|
|
||||||
cmd = 'bw in ' + (i-1) + ' ' + parseInt(document.getElementById("ibw_" + i).value).toString(16).padStart(4, "0");;
|
|
||||||
doCMD(cmd);
|
|
||||||
if (document.getElementById("ilimit_port_" + i).checked) {
|
|
||||||
if (!document.getElementById("fc_port_" + i).checked)
|
|
||||||
cmd = "bw in " + (i-1) + " drop";
|
|
||||||
doCMD(cmd);
|
|
||||||
}
|
|
||||||
var cmd = "bw out " + (i-1) + " off";
|
|
||||||
if (document.getElementById("elimit_port_" + i).checked)
|
|
||||||
cmd = 'bw out ' + (i-1) + ' ' + parseInt(document.getElementById("ebw_" + i).value).toString(16).padStart(4, "0");;
|
|
||||||
doCMD(cmd);
|
|
||||||
}
|
|
||||||
|
|
||||||
function getBW() {
|
|
||||||
var xhttp = new XMLHttpRequest();
|
|
||||||
xhttp.onreadystatechange = function() {
|
|
||||||
console.log("IN getBW ");
|
|
||||||
if (this.readyState == 4 && this.status == 200) {
|
|
||||||
const s = JSON.parse(xhttp.responseText);
|
|
||||||
console.log("BW: ", JSON.stringify(s));
|
|
||||||
var tbl = document.getElementById('bwtable');
|
|
||||||
if (tbl.rows.length > 2 && numPorts) {
|
|
||||||
for (let i = 2; i < 2 + numPorts; i++) {
|
|
||||||
p = s[i-2];
|
|
||||||
let n = p.portNum;
|
|
||||||
let tr = tbl.rows[n+1];
|
|
||||||
if (!document.getElementById("bwapply_" + (n+1)).disabled)
|
|
||||||
continue;
|
|
||||||
console.log("Table Update row: " + i + " portNum is " + n + ", pState is " + pState[i-2] + ", row number is " + (n+1));
|
|
||||||
let iBW = parseInt(p.iBW,16) * 16; let eBW = parseInt(p.eBW,16) * 16;
|
|
||||||
document.getElementById("ilimit_port_" + (n+1)).checked = p.iLimited;
|
|
||||||
document.getElementById("elimit_port_" + (n+1)).checked = p.eLimited;
|
|
||||||
if (!p.iLimited) {
|
|
||||||
tr.cells[2].innerHTML = "UNLIMITED";
|
|
||||||
} else {
|
|
||||||
tr.cells[2].innerHTML = '<input id="ibw_' + (n+1) + iLayout + (n+1) + ')" value="' + iBW +'"/>';
|
|
||||||
}
|
|
||||||
if (!p.eLimited) {
|
|
||||||
tr.cells[5].innerHTML = "UNLIMITED";
|
|
||||||
} else {
|
|
||||||
tr.cells[5].innerHTML = '<input id="ebw_' + (n+1) + iLayout + (n+1) + ')" value="' + eBW +'"/>';
|
|
||||||
}
|
|
||||||
document.getElementById("fc_port_" + (n+1)).checked = p.iFC==1?true:false;
|
|
||||||
document.getElementById("fc_port_" + (n+1)).disabled = p.iLimited==1?false:true;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
};
|
|
||||||
xhttp.open("GET", "/bandwidth.json", true);
|
|
||||||
xhttp.timeout = 1500; sendXHTTP(xhttp);
|
|
||||||
}
|
|
||||||
|
|
||||||
window.addEventListener("load", function() {
|
|
||||||
update( () => {
|
|
||||||
createBW();
|
|
||||||
getBW();
|
|
||||||
const interval = setInterval(update, 2000);
|
|
||||||
});
|
|
||||||
});
|
|
||||||
@@ -1,105 +0,0 @@
|
|||||||
var configInterval = Number();
|
|
||||||
var configuration = [];
|
|
||||||
const 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}$/,
|
|
||||||
/^passwd\s+\S+$/,
|
|
||||||
/^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}[tu]?)+$/,
|
|
||||||
/^pvid\s+\d{1,2}\s+\d{1,4}$/,
|
|
||||||
/^ingress(\s+\d{1,2}[tua])+$/,
|
|
||||||
/^ingress\s+[tua]$/,
|
|
||||||
/^port\s+\d{1,2}\s+name\s+\S+$/,
|
|
||||||
/^eee(\s+\d{1,2})?\s+(on|off)$/,
|
|
||||||
/^mirror(\s+\d{1,2})(\s+\d{1,2}[tr]?)+$/,
|
|
||||||
/^lag\s+\d(\s+\d{1,2})+$/,
|
|
||||||
/^laghash\s+\d(\s+\w+)+$/,
|
|
||||||
/^isolate\s+\d{1,2}(\s+(off|\d{1,2}))+$/,
|
|
||||||
/^stp\s+(on|off)$/,
|
|
||||||
/^igmp\s+(on|off)$/,
|
|
||||||
/^mtu\s+\d{1,2}\s+\d+$/,
|
|
||||||
/^bw\s+(in|out)\s+\d{1,2}\s+\S+$/,
|
|
||||||
];
|
|
||||||
const conf_overwrite = [
|
|
||||||
/^ip\b/,
|
|
||||||
/^gw\b/,
|
|
||||||
/^netmask\b/,
|
|
||||||
/^syslog\s+ip\b/,
|
|
||||||
/^syslog\b/,
|
|
||||||
/^passwd\b/,
|
|
||||||
/^vlan\s+\d{1,4}\s+mgmt$/,
|
|
||||||
/^vlan\s+\d{1,4}(?!\s+mgmt\b)/,
|
|
||||||
/^pvid\s+\d{1,2}\b/,
|
|
||||||
/^ingress\b/,
|
|
||||||
/^port\s+\d{1,2}\s+name\b/,
|
|
||||||
/^eee\s+\d{1,2}\b/,
|
|
||||||
/^eee\b/,
|
|
||||||
/^mirror\b/,
|
|
||||||
/^lag\s+\d+\b/,
|
|
||||||
/^laghash\b/,
|
|
||||||
/^isolate\s+\d{1,2}\b/,
|
|
||||||
/^stp\b/,
|
|
||||||
/^igmp\b/,
|
|
||||||
/^mtu\s+\d{1,2}\b/,
|
|
||||||
/^bw\s+(in|out)\s+\d{1,2}\b/,
|
|
||||||
];
|
|
||||||
|
|
||||||
function parseConf(s){
|
|
||||||
var a = s.split(/\r\n|\n/);
|
|
||||||
for (var l = 0; l < a.length; l++) {
|
|
||||||
var line = a[l].trim().replace(/\s+/g, ' ');
|
|
||||||
if (!line.length) continue;
|
|
||||||
const deleteMatch = line.match(/^vlan\s+(\d{1,4})\s+d$/);
|
|
||||||
if (deleteMatch) {
|
|
||||||
const prefix = "vlan " + deleteMatch[1] + " ";
|
|
||||||
configuration = configuration.filter(c =>
|
|
||||||
c !== "vlan " + deleteMatch[1] && !c.startsWith(prefix));
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
console.log(l + ' --> ' + line);
|
|
||||||
var ignore = true;
|
|
||||||
for (const x of conf_cmds)
|
|
||||||
if (x.test(line)) { ignore = false; break; }
|
|
||||||
if (ignore) continue;
|
|
||||||
for (const x of conf_overwrite) {
|
|
||||||
if (x.test(line)) {
|
|
||||||
let m = line.match(x);
|
|
||||||
let matchStr = m[0];
|
|
||||||
configuration = configuration.filter(item =>
|
|
||||||
!(item === matchStr || item.startsWith(matchStr + " ")));
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
configuration.push(line);
|
|
||||||
}
|
|
||||||
console.log("Configuration now:");
|
|
||||||
for (const x of configuration) { console.log(x); }
|
|
||||||
}
|
|
||||||
|
|
||||||
async function fetchConfig() {
|
|
||||||
try {
|
|
||||||
const response = await fetch('/config');
|
|
||||||
console.log("CONFIG: ", response);
|
|
||||||
const t = await response.text();
|
|
||||||
return t;
|
|
||||||
} catch(err) {
|
|
||||||
console.error("Error: ", err);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
async function fetchCmdLog() {
|
|
||||||
try {
|
|
||||||
const response = await fetch('/cmd_log');
|
|
||||||
console.log("CMD-Log: ", response);
|
|
||||||
const t = await response.text();
|
|
||||||
return t;
|
|
||||||
} catch(err) {
|
|
||||||
console.error("Error: ", err);
|
|
||||||
return "";
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,25 +0,0 @@
|
|||||||
<!DOCTYPE html>
|
|
||||||
<html>
|
|
||||||
<script src="/main.js"></script>
|
|
||||||
<link rel="stylesheet" href="style.css">
|
|
||||||
<title>EEE Configuration</title>
|
|
||||||
</head>
|
|
||||||
<body>
|
|
||||||
<nav id="sidebar"></nav>
|
|
||||||
<div style="margin-left:16%;padding:1px 16px;height:1000px;">
|
|
||||||
<div id="ports"></div>
|
|
||||||
<h1>EEE Status</h1>
|
|
||||||
<table id="eeetable">
|
|
||||||
<tr> <th> </th> <th colspan="3"> Advertising </th> <th colspan="3">Link-Partner advertises</th> <th></th></tr>
|
|
||||||
<tr> <th>Port</th> <th>2.5G</th> <th>1G</th> <th>100M</th> <th>2.5G</th> <th>1G</th> <th>100M</th> <th>Active?</th></tr>
|
|
||||||
</table>
|
|
||||||
<div>
|
|
||||||
<input style="width:20%;" class="action" id="eee_enable" onclick="eeeSub(0, 1);" type="button" value="Enable EEE">
|
|
||||||
<input style="width:20%;" class="action" id="eee_enable" onclick="eeeSub(0, 0);" type="button" value="Disable EEE">
|
|
||||||
</div>
|
|
||||||
<script src="/eee.js"></script>
|
|
||||||
<script src="/eee_sub.js"></script>
|
|
||||||
</div>
|
|
||||||
<script src="/navigation.js"></script>
|
|
||||||
</body>
|
|
||||||
</html>
|
|
||||||
@@ -1,52 +0,0 @@
|
|||||||
function createEEE() {
|
|
||||||
var tbl = document.getElementById('eeetable');
|
|
||||||
if (tbl.rows.length <= 2 && numPorts) {
|
|
||||||
console.log("CREATING TABLE ", tbl.rows.length);
|
|
||||||
for (let i = 2; i < 2 + numPorts; i++) {
|
|
||||||
console.log("Table row: " + i + "pState: " + pState[i-2]);
|
|
||||||
const tr = tbl.insertRow();
|
|
||||||
let td = tr.insertCell(); td.appendChild(document.createTextNode(`Port ${i-1}`));
|
|
||||||
for (let j = 0; j < 7; j++) {
|
|
||||||
td = tr.insertCell(); td.appendChild(document.createTextNode(" "));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
function getEEE() {
|
|
||||||
var xhttp = new XMLHttpRequest();
|
|
||||||
xhttp.onreadystatechange = function() {
|
|
||||||
if (this.readyState == 4 && this.status == 200) {
|
|
||||||
const s = JSON.parse(xhttp.responseText);
|
|
||||||
console.log("EEE: ", JSON.stringify(s));
|
|
||||||
var tbl = document.getElementById('eeetable');
|
|
||||||
if (tbl.rows.length > 2 && numPorts) {
|
|
||||||
for (let i = 2; i < 2 + numPorts; i++) {
|
|
||||||
p = s[i-2];
|
|
||||||
let n = p.portNum;
|
|
||||||
console.log("Table Update row: " + i + " portNum is " + n + ", pState is " + pState[i-2]);
|
|
||||||
let tr = tbl.rows[n+1];
|
|
||||||
if (!p.isSFP) {
|
|
||||||
let eee = parseInt(p.eee,2); let lp = parseInt(p.eee_lp,2);
|
|
||||||
tr.cells[1].innerHTML = `${eee&4?"ON":"OFF"}`; tr.cells[2].innerHTML = `${eee&2?"ON":"OFF"}`; tr.cells[3].innerHTML = `${eee&1?"ON":"OFF"}`;
|
|
||||||
tr.cells[4].innerHTML = `${lp&4?"ON":"OFF"}`; tr.cells[5].innerHTML = `${lp&2?"ON":"OFF"}`; tr.cells[6].innerHTML = `${lp&1?"ON":"OFF"}`;
|
|
||||||
tr.cells[7].innerHTML = `${p.active}`;
|
|
||||||
tr.classList.toggle('disabled', pState[i-2] < 0); tr.classList.toggle('isNOK', !p.active); tr.classList.toggle('isOK', p.active);
|
|
||||||
}
|
|
||||||
tr.classList.toggle('isSFP', p.isSFP);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
};
|
|
||||||
xhttp.open("GET", "/eee.json", true);
|
|
||||||
xhttp.timeout = 1500; sendXHTTP(xhttp);
|
|
||||||
}
|
|
||||||
|
|
||||||
window.addEventListener("load", function() {
|
|
||||||
update( () => {
|
|
||||||
createEEE();
|
|
||||||
getEEE();
|
|
||||||
const interval = setInterval(update, 2000);
|
|
||||||
const iCount = setInterval(getEEE, 2000);
|
|
||||||
});
|
|
||||||
});
|
|
||||||
@@ -1,18 +0,0 @@
|
|||||||
async function eeeSub(port, enable) {
|
|
||||||
var cmd = "eee ";
|
|
||||||
if (enable)
|
|
||||||
cmd = cmd + "on";
|
|
||||||
else
|
|
||||||
cmd = cmd + "off";
|
|
||||||
console.log("eeeSub port " + port, ", value " + enable);
|
|
||||||
try {
|
|
||||||
const response = await fetch('/cmd', {
|
|
||||||
method: 'POST',
|
|
||||||
body: cmd
|
|
||||||
});
|
|
||||||
console.log('Completed!', response);
|
|
||||||
} catch(err) {
|
|
||||||
console.error(`Error: ${err}`);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
@@ -1,31 +1,310 @@
|
|||||||
<!DOCTYPE html>
|
<!DOCTYPE html>
|
||||||
<html>
|
<html>
|
||||||
<script src="/main.js"></script>
|
<script src="/main.js"></script>
|
||||||
<script src="/main_info.js"></script>
|
|
||||||
<script>
|
|
||||||
window.addEventListener("load", function() {
|
|
||||||
update( () => {
|
|
||||||
const interval = setInterval(update, 2000);
|
|
||||||
});
|
|
||||||
});
|
|
||||||
</script>
|
|
||||||
<link rel="stylesheet" href="style.css">
|
<link rel="stylesheet" href="style.css">
|
||||||
<title>FreeSwitchOS Main Page</title>
|
<title data-i18n="index_title">FreeSwitchOS Main Page</title>
|
||||||
</head>
|
<style>
|
||||||
|
.popup {
|
||||||
|
display: none;
|
||||||
|
position: fixed;
|
||||||
|
top: 0; left: 0;
|
||||||
|
width: 100%; height: 100%;
|
||||||
|
background: rgba(0, 0, 0, 0.5);
|
||||||
|
justify-content: center;
|
||||||
|
align-items: center;
|
||||||
|
}
|
||||||
|
.popup-content {
|
||||||
|
background: #fff;
|
||||||
|
padding: 20px;
|
||||||
|
border-radius: 5px;
|
||||||
|
text-align: center;
|
||||||
|
height: 80%
|
||||||
|
}
|
||||||
|
.popup .popup-content {
|
||||||
|
overflow-y: scroll;
|
||||||
|
}
|
||||||
|
.tab-bar { display: flex; border-bottom: 2px solid #226; margin-bottom: 0; padding: 1px 16px; padding-bottom: 0; }
|
||||||
|
.tab-btn { padding: 10px 20px; background-color: #ddf; border: none; cursor: pointer; font-size: 1em; border-radius: 8px 8px 0 0; margin-right: 4px; }
|
||||||
|
.tab-btn:hover { background-color: #aaf; }
|
||||||
|
.tab-btn.active { background-color: #aaf; font-weight: bold; border-bottom: 2px solid #aaf; margin-bottom: -2px; }
|
||||||
|
.tab-content { display: none; }
|
||||||
|
.tab-content.active { display: block; }
|
||||||
|
</style>
|
||||||
|
</head>
|
||||||
|
<body>
|
||||||
|
<nav id="sidebar"></nav>
|
||||||
|
<div id="ports" style="margin-left:16%;padding:1px 16px"></div>
|
||||||
|
|
||||||
<body>
|
<div class="page" id="page-overview" style="margin-left:16%;padding:1px 16px;height:1000px;">
|
||||||
<nav id="sidebar"></nav>
|
<h1 data-i18n="index_heading">Switch Configuration</h1>
|
||||||
<div style="margin-left:16%;padding:1px 16px;height:1000px;">
|
<table id="infoTable">
|
||||||
<div id="ports"></div>
|
<tr>
|
||||||
<h1>Switch Configuration</h1>
|
<th colspan="2" data-i18n="index_settings">Settings</th>
|
||||||
<table id="infoTable">
|
</tr>
|
||||||
<tr>
|
<tbody>
|
||||||
<th colspan="2">Settings</th>
|
</tbody>
|
||||||
</tr>
|
</table>
|
||||||
<tbody>
|
</div>
|
||||||
</tbody>
|
|
||||||
</table>
|
<div class="page" id="page-ports" style="margin-left:16%;padding:1px 16px;height:1000px;">
|
||||||
</div>
|
<h1 data-i18n="port_heading">Port Configuration</h1>
|
||||||
<script src="/navigation.js"></script>
|
<table id="speedtable">
|
||||||
</body>
|
<tr> <th data-i18n="port_col_port">Port</th> <th data-i18n="port_col_name">Name</th> <th data-i18n="port_col_speed">Current Link Speed</th><th data-i18n="port_col_devices">Connected devices</th><th data-i18n="port_col_set_speed">Set Speed</th><th data-i18n="port_col_disabled">Disabled</th><th data-i18n="port_col_apply">Apply</th></tr>
|
||||||
|
</table>
|
||||||
|
<h2 style="margin-top:3em" data-i18n="port_mtu_heading">Configure Maximum Frame Size (MTU) forwarded at Port</h2>
|
||||||
|
<table id="mtutable" style="margin-top:1em">
|
||||||
|
</table>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="page" id="page-stat" style="margin-left:16%;padding:1px 16px;height:1000px;">
|
||||||
|
<div id="popup" class="popup">
|
||||||
|
<div class="popup-content">
|
||||||
|
<h2 data-i18n="stat_detailed">Detailed Port Statistics</h2>
|
||||||
|
<div id="popup_text"></div>
|
||||||
|
<button id="closePopup" class="action" data-i18n="stat_close">Close</button>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<h1 data-i18n="stat_heading">Port Statistics</h1>
|
||||||
|
<table id="statstable">
|
||||||
|
<tr> <th data-i18n="stat_col_port">Port</th> <th data-i18n="stat_col_name">Name</th> <th data-i18n="stat_col_link">link</th> <th data-i18n="stat_col_tx_good">TX Good</th> <th data-i18n="stat_col_tx_bad">TX Bad</th> <th data-i18n="stat_col_rx_good">RX Good</th> <th data-i18n="stat_col_rx_bad">RX Bad</th> <th data-i18n="stat_col_all"> All Counters </th></tr>
|
||||||
|
</table>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="page" id="page-vlan" style="margin-left:16%;padding:1px 16px;height:1000px;">
|
||||||
|
<h1 data-i18n="vlan_heading">VLAN Configuration</h1>
|
||||||
|
<form id="vform">
|
||||||
|
<div>
|
||||||
|
<label for="vlanSelect" data-i18n="vlan_select">VLAN Select:</label>
|
||||||
|
<select id="vlanSelect" style="margin: 0 0 0 8px">
|
||||||
|
<option value="" disabled selected data-i18n="vlan_choose">— VLAN Choose —</option>
|
||||||
|
</select>
|
||||||
|
</div>
|
||||||
|
<br/>
|
||||||
|
<div>
|
||||||
|
<label for="vid" data-i18n="vlan_id">VLAN ID:</label>
|
||||||
|
<input type="number" min="1" max="4094" id="vid" name="vid">
|
||||||
|
<button type="button" style="margin: 0 0 0 24px" onclick="fetchVLAN();" data-i18n="vlan_get_config">Get Configuration</button>
|
||||||
|
</div>
|
||||||
|
<br/><br/>
|
||||||
|
<label for="vname" data-i18n="vlan_name">VLAN Name:</label>
|
||||||
|
<input type="text" id="vname" name="vname"><br><br>
|
||||||
|
<br/>
|
||||||
|
<h2 data-i18n="vlan_tagged">Tagged Ports</h2>
|
||||||
|
<div id="tPorts"><button type="button" style="transform: translateY(-100%);margin: 0 50px 0 0" onclick="utClicked(true);" data-i18n="vlan_select_all">Select all</button></div>
|
||||||
|
<h2 data-i18n="vlan_untagged">Untagged Ports</h2>
|
||||||
|
<div id="uPorts"><button type="button" style="transform: translateY(-100%); margin: 0 50px 0 0" onclick="utClicked(false);" data-i18n="vlan_select_all">Select all</button> </div>
|
||||||
|
<h2 data-i18n="vlan_pvid">Use as default VLAN for incoming traffic (PVID)</h2>
|
||||||
|
<div id="pPorts"><button type="button" style="transform: translateY(-100%); margin: 0 50px 0 0" onclick="pvClicked(true);" data-i18n="vlan_select_all">Select all</button> </div>
|
||||||
|
<br/> <input style="width:40%;" class="action" id="vlan_sub" onclick="vlanSub();" type="button" data-i18n="vlan_update" value="Update / Create">
|
||||||
|
</form>
|
||||||
|
<h2 data-i18n="vlan_configured">Configured VLANs</h2>
|
||||||
|
<table id="vlanTable" style="width:90%">
|
||||||
|
<thead>
|
||||||
|
<tr>
|
||||||
|
<th>VLAN</th>
|
||||||
|
<th data-i18n="vlan_col_name">Name</th>
|
||||||
|
<th data-i18n="vlan_col_member">Member Ports</th>
|
||||||
|
<th data-i18n="vlan_col_tagged">Tagged Ports</th>
|
||||||
|
<th data-i18n="vlan_col_untagged">Untagged Ports</th>
|
||||||
|
<th data-i18n="vlan_col_pvid">PVID Ports</th>
|
||||||
|
<th data-i18n="vlan_col_delete">Delete</th>
|
||||||
|
</tr>
|
||||||
|
</thead>
|
||||||
|
<tbody id="vlanTableBody">
|
||||||
|
</tbody>
|
||||||
|
</table>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="page" id="page-l2" style="margin-left:16%;padding:1px 16px;height:1000px;">
|
||||||
|
<h1 data-i18n="l2_heading">L2 Configuration</h1>
|
||||||
|
<p><span data-i18n="l2_shown">Shown:</span> <span id="l2count">-</span></p>
|
||||||
|
<table id="l2table">
|
||||||
|
<tr>
|
||||||
|
<th><span class="l2sort" onclick="l2SortBy('port')"><span data-i18n="l2_col_port">Port</span><span id="l2a_port" class="l2arrow"></span></span><br>
|
||||||
|
<input id="l2f_port" class="l2filter" oninput="l2FilterChanged()" size="5"></th>
|
||||||
|
<th><span class="l2sort" onclick="l2SortBy('mac')">MAC<span id="l2a_mac" class="l2arrow"></span></span><br>
|
||||||
|
<input id="l2f_mac" class="l2filter" oninput="l2FilterChanged()" size="14"></th>
|
||||||
|
<th><span class="l2sort" onclick="l2SortBy('vlan')">VLAN<span id="l2a_vlan" class="l2arrow"></span></span><br>
|
||||||
|
<input id="l2f_vlan" class="l2filter" oninput="l2FilterChanged()" size="5"></th>
|
||||||
|
<th><span class="l2sort" onclick="l2SortBy('type')"><span data-i18n="l2_col_type">Type</span><span id="l2a_type" class="l2arrow"></span></span><br>
|
||||||
|
<input id="l2f_type" class="l2filter" oninput="l2FilterChanged()" size="8"></th>
|
||||||
|
<th data-i18n="l2_col_remove">Remove Entry</th></tr>
|
||||||
|
</table>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="page" id="page-stp" style="margin-left:16%;padding:1px 16px;height:1000px;">
|
||||||
|
<h1>Spanning Tree (RSTP)</h1>
|
||||||
|
<h2>Mode: <select id="stpMode"><option value="off">Disabled</option><option value="on">Enabled</option></select>
|
||||||
|
<input style="width:15%;margin-left: 3em;" class="action" id="stp_sub" onclick="stpSub();" type="button" value="Apply"></h2>
|
||||||
|
<div id="stpStat" style="font-family:monospace;"></div>
|
||||||
|
<h2>Bridge settings</h2>
|
||||||
|
<table id="stpBridge">
|
||||||
|
<tr>
|
||||||
|
<th>Priority</th><th>Version</th><th>Hello [s]</th><th>Max age [s]</th><th>Fwd delay [s]</th><th>Tx hold</th>
|
||||||
|
</tr>
|
||||||
|
<tr>
|
||||||
|
<td><select id="bPrio"></select></td>
|
||||||
|
<td><select id="bVer"><option value="rstp">RSTP</option><option value="stp">STP compat</option></select></td>
|
||||||
|
<td><input id="bHello" type="number" min="1" max="10" style="width:4em"></td>
|
||||||
|
<td><input id="bMaxage" type="number" min="6" max="40" style="width:4em"></td>
|
||||||
|
<td><input id="bFwd" type="number" min="4" max="30" style="width:4em"></td>
|
||||||
|
<td><input id="bTxhold" type="number" min="1" max="10" style="width:4em"></td>
|
||||||
|
</tr>
|
||||||
|
</table>
|
||||||
|
<p style="font-size:small">Changes apply immediately. Edge ports skip the listen period; guard/filter act on received BPDUs.</p>
|
||||||
|
<h2>Port configuration</h2>
|
||||||
|
<table id="stpPortsTbl">
|
||||||
|
<tr>
|
||||||
|
<th>Port</th><th>State</th><th>Path Cost<br><span style="font-weight:normal;font-size:small">(0 = Auto)</span></th><th>Priority</th><th>Edge Port</th><th>BPDU Filter</th><th>Guard</th><th>Point-to-Point</th>
|
||||||
|
</tr>
|
||||||
|
</table>
|
||||||
|
<h2>Port status</h2>
|
||||||
|
<table id="stpStatTbl">
|
||||||
|
<tr>
|
||||||
|
<th>Port</th><th>Port State</th><th>Role</th><th>Designated Bridge</th><th>Designated Port ID</th><th>Designated Cost</th><th>Oper. Edge</th><th>Oper. P2P</th>
|
||||||
|
</tr>
|
||||||
|
</table>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="page" id="page-mirror" style="margin-left:16%;padding:1px 16px;height:1000px;">
|
||||||
|
<h1 data-i18n="mirror_heading">Mirror Configuration</h1>
|
||||||
|
<label class="tswitch"><span data-i18n="mirror_enabled">Enabled:</span> <input id="me" type="checkbox"></label><br/>
|
||||||
|
<label for="mp"><span data-i18n="mirror_port">Mirroring Port:</span></label> <input type="number" id="mp" name="mp" min="1" max="9"/>
|
||||||
|
<h2 data-i18n="mirror_tx">Mirrored Ports (TX)</h2>
|
||||||
|
<div id="mPortsTX"></div>
|
||||||
|
<br />
|
||||||
|
<h2 data-i18n="mirror_rx">Mirrored Ports (RX)</h2>
|
||||||
|
<div id="mPortsRX"></div>
|
||||||
|
<br/> <input style="width:15%;" class="action" id="mirror_sub" onclick="mirrorSub();" type="button" data-i18n="mirror_update" value="Update / Create">
|
||||||
|
<input style="width:15%;" class="action" id="mirror_del" onclick="mirrorDel();" type="button" data-i18n="mirror_disable" value="Disable Mirroring">
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="page" id="page-lag" style="margin-left:16%;padding:1px 16px;height:1000px;">
|
||||||
|
<h1 data-i18n="lag_heading">Link Aggregation Groups Configuration</h1>
|
||||||
|
<h2>LAG 1 <input style="width:15%;margin-left: 3em;" class="action" id="l_sub0" onclick="lagSub(0);" type="button" data-i18n="lag_update" value="Update / Create"></h2>
|
||||||
|
<div id="mLAG0"></div>
|
||||||
|
<br />
|
||||||
|
<h2>LAG 2 <input style="width:15%;margin-left: 3em;" class="action" id="l_sub1" onclick="lagSub(1);" type="button" data-i18n="lag_update" value="Update / Create"></h2>
|
||||||
|
<div id="mLAG1"></div>
|
||||||
|
<br />
|
||||||
|
<h2>LAG 3 <input style="width:15%;margin-left: 3em;" class="action" id="l_sub2" onclick="lagSub(2);" type="button" data-i18n="lag_update" value="Update / Create"></h2>
|
||||||
|
<div id="mLAG2"></div>
|
||||||
|
<br />
|
||||||
|
<h2>LAG 4 <input style="width:15%;margin-left: 3em;" class="action" id="l_sub3" onclick="lagSub(3);" type="button" data-i18n="lag_update" value="Update / Create"></h2>
|
||||||
|
<div id="mLAG3"></div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="page" id="page-eee" style="margin-left:16%;padding:1px 16px;height:1000px;">
|
||||||
|
<h1 data-i18n="eee_heading">EEE Status</h1>
|
||||||
|
<table id="eeetable">
|
||||||
|
<tr> <th> </th> <th colspan="3" data-i18n="eee_advertising"> Advertising </th> <th colspan="3" data-i18n="eee_partner">Link-Partner advertises</th> <th></th></tr>
|
||||||
|
<tr> <th data-i18n="eee_port">Port</th> <th>2.5G</th> <th>1G</th> <th>100M</th> <th>2.5G</th> <th>1G</th> <th>100M</th> <th data-i18n="eee_active">Active?</th></tr>
|
||||||
|
</table>
|
||||||
|
<div>
|
||||||
|
<input style="width:20%;" class="action" id="eee_enable" onclick="eeeSub(0, 1);" type="button" data-i18n="eee_enable" value="Enable EEE">
|
||||||
|
<input style="width:20%;" class="action" id="eee_disable" onclick="eeeSub(0, 0);" type="button" data-i18n="eee_disable" value="Disable EEE">
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="page" id="page-bandwidth" style="margin-left:16%;padding:1px 16px;height:1000px;">
|
||||||
|
<h1 data-i18n="bw_heading">Ingress and Egress Bandwidth</h1>
|
||||||
|
<table id="bwtable">
|
||||||
|
<tr> <th> </th> <th colspan="3" data-i18n="bw_ingress"> Ingress </th> <th colspan="2" data-i18n="bw_egress">Egress</th> <th></th></tr>
|
||||||
|
<tr> <th data-i18n="bw_col_port">Port</th> <th data-i18n="bw_col_limit">Limit</th> <th data-i18n="bw_col_bandwidth">Bandwidth [kBit/s]</th> <th data-i18n="bw_col_flow">Flow Control</th> <th data-i18n="bw_col_limit">Limit</th> <th data-i18n="bw_col_bandwidth">Bandwidth [kBit/s]</th> <th data-i18n="bw_col_apply">Apply</th></tr>
|
||||||
|
</table>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="page" id="page-system" style="margin-left:16%;padding:1px 16px;height:1000px;">
|
||||||
|
<div class="tab-bar">
|
||||||
|
<button class="tab-btn active" onclick="openTab(event, 'system-tab')" data-i18n="sys_tab_system">System</button>
|
||||||
|
<button class="tab-btn" onclick="openTab(event, 'advanced-tab')" data-i18n="sys_tab_advanced">Advanced</button>
|
||||||
|
<button class="tab-btn" onclick="openTab(event, 'console-tab')" data-i18n="sys_tab_console">Console</button>
|
||||||
|
</div>
|
||||||
|
<div id="system-tab" class="tab-content active">
|
||||||
|
<h1 data-i18n="sys_heading">System Settings</h1>
|
||||||
|
<div class="row">
|
||||||
|
<div class="lcol"> <label for="hostname" data-i18n="sys_hostname">Hostname:</label></div>
|
||||||
|
<div class="rcol"> <input id="hostname" type="text" maxlength="23" size="20"/>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="row">
|
||||||
|
<div class="lcol"> <label for="ip" data-i18n="sys_ip">IP address:</label></div>
|
||||||
|
<div class="rcol"> <input id="ip" type="text" size="15"/>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="row">
|
||||||
|
<div class="lcol"> <label for="netmask" data-i18n="sys_netmask">Netmask:</label></div>
|
||||||
|
<div class="rcol"> <input id="netmask" type="text" size="15"/>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="row">
|
||||||
|
<div class="lcol"> <label for="gw" data-i18n="sys_gateway">Gateway:</label></div>
|
||||||
|
<div class="rcol"> <input id="gw" type="text" size="15"/>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="row">
|
||||||
|
<div class="lcol"> <label for="model" data-i18n="sys_model">Model:</label></div>
|
||||||
|
<div class="rcol"> <span id="model">-</span>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="row">
|
||||||
|
<div class="lcol"> <label for="lang-select" data-i18n="sys_language">Language:</label></div>
|
||||||
|
<div class="rcol">
|
||||||
|
<select id="lang-select" onchange="changeLang()">
|
||||||
|
<option value="en">English</option>
|
||||||
|
<option value="ja">日本語</option>
|
||||||
|
<option value="zh">中文</option>
|
||||||
|
</select>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="row">
|
||||||
|
<div class="lcol"> <label for="mgmtvlan" data-i18n="sys_mgmt_vlan">Management VLAN:</label></div>
|
||||||
|
<div class="rcol">
|
||||||
|
<select id="mgmtvlan" onchange="mgmtVlanChanged()">
|
||||||
|
</select>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<br/>
|
||||||
|
<span data-i18n="sys_ip_note">When updating the above settings, remember to point your browser to the new IP afterwards:</span><br/>
|
||||||
|
<input style="width:40%;" class="action" id="ip_sub" onclick="ipSub();" type="button" data-i18n="sys_update" value="Update Settings"><br/>
|
||||||
|
<br/>
|
||||||
|
<span data-i18n="sys_save_label">Save all current settings to Flash:</span><br/>
|
||||||
|
<input style="width:40%;" class="action" id="flash_sub" onclick="flashSave();" type="button" data-i18n="sys_save" value="Save Settings to Flash">
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div id="advanced-tab" class="tab-content">
|
||||||
|
<h1 data-i18n="sys_advanced">Advanced Settings</h1>
|
||||||
|
<div class="lcol"> <label for="config_display" data-i18n="sys_startup_config">Startup configuration:</label></div>
|
||||||
|
<textarea id="config_display" rows="8" cols="60"></textarea>
|
||||||
|
<br/><br/>
|
||||||
|
<span data-i18n="sys_startup_warn">Be careful when saving the directly edited startup configuration, you can lock yourself out:</span><br/>
|
||||||
|
<input style="width:40%;" class="action" id="clear_config" onclick="clearConfig();" type="button" data-i18n="sys_clear_config" value="Clear Startup Config">
|
||||||
|
<br/>
|
||||||
|
<input style="width:40%;" class="action" id="flash_startup_sub" onclick="flashStartupSave();" type="button" data-i18n="sys_save_startup" value="Save Startup Settings to Flash">
|
||||||
|
<br/>
|
||||||
|
<input style="width:40%;" class="action" id="switch_reset" onclick="resetSwitch();" type="button" data-i18n="sys_reset" value="Reset Switch">
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div id="console-tab" class="tab-content">
|
||||||
|
<h1 data-i18n="sys_console">Console Command</h1>
|
||||||
|
<label for="console_command" data-i18n="sys_enter_cmd">Enter command:</label>
|
||||||
|
<input type="text" id="console_cmd" name="console_cmd" style="width:40%;">
|
||||||
|
<input style="width:20%;" class="action" id="cmd_sub" onclick="cmdSub();" type="button" data-i18n="sys_send_cmd" value="Send Command"><br/>
|
||||||
|
<br/><br/>
|
||||||
|
<span data-i18n="sys_console_warn">Be careful when entering console commands, you can lock yourself out!</span><br/>
|
||||||
|
<br/>
|
||||||
|
<pre id="console_out" style="height:22em;overflow:auto;white-space:pre-wrap;border:1px solid;padding:0.5em;"></pre>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="page" id="page-update" style="margin-left:16%;padding:1px 16px;height:1000px;width:40%;">
|
||||||
|
<h1 data-i18n="update_heading">Firmware Update</h1>
|
||||||
|
<form enctype="multipart/form-data" action="/upload" method="POST">
|
||||||
|
<input type="hidden" name="MAX_FILE_SIZE" value="1000000" />
|
||||||
|
<span data-i18n="update_instruction">Choose a firmware update file to upload:</span> <br/> <br/>
|
||||||
|
<input name="uploadedfile" type="file" accept=".bin" /><br />
|
||||||
|
<input style="margin-top:3em" type="submit" data-i18n="update_upload" value="Upload File" />
|
||||||
|
</form>
|
||||||
|
</div>
|
||||||
|
</body>
|
||||||
</html>
|
</html>
|
||||||
@@ -1,19 +0,0 @@
|
|||||||
<!DOCTYPE html>
|
|
||||||
<html>
|
|
||||||
<script src="/main.js"></script>
|
|
||||||
<link rel="stylesheet" href="style.css">
|
|
||||||
<title>FreeSwitchOS L2 Configuration</title>
|
|
||||||
</head>
|
|
||||||
<body>
|
|
||||||
<nav id="sidebar"></nav>
|
|
||||||
<div style="margin-left:16%;padding:1px 16px;height:1000px;">
|
|
||||||
<div id="ports"></div>
|
|
||||||
<h1>L2 Configuration</h1>
|
|
||||||
<table id="l2table">
|
|
||||||
<tr> <th>Port</th> <th>MAC</th> <th>VLAN</th> <th>Type</th> <th>Remove Entry</th></tr>
|
|
||||||
<script src="/l2.js"></script>
|
|
||||||
</table>
|
|
||||||
</div>
|
|
||||||
</body>
|
|
||||||
<script src="/navigation.js"></script>
|
|
||||||
</html>
|
|
||||||
@@ -1,143 +0,0 @@
|
|||||||
var l2GetInterval;
|
|
||||||
var l2Entries = [];
|
|
||||||
var l2CurrentEntry = 0;
|
|
||||||
|
|
||||||
function fillStats() {
|
|
||||||
var tbl = document.getElementById('statstable');
|
|
||||||
if (!numPorts)
|
|
||||||
return;
|
|
||||||
if (tbl.rows.length > 1) {
|
|
||||||
for (let i = 0; i < numPorts; i++) {
|
|
||||||
console.log("Table Update row: " + i + " state " + pState[i] + " is " + linkS[pState[i] +1]);
|
|
||||||
tbl.rows[i+1].cells[1].innerHTML = `${linkS[pState[i]+1]}`;
|
|
||||||
tbl.rows[i+1].cells[2].innerHTML = `${txG[i]} pkts`;
|
|
||||||
tbl.rows[i+1].cells[3].innerHTML = `${txB[i]} pkts`;
|
|
||||||
tbl.rows[i+1].cells[4].innerHTML = `${rxG[i]} pkts`;
|
|
||||||
tbl.rows[i+1].cells[5].innerHTML = `${rxB[i]} pkts`;
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
for (let i = 0; i < numPorts; i++) {
|
|
||||||
console.log("Table row: " + i);
|
|
||||||
const tr = tbl.insertRow();
|
|
||||||
let td = tr.insertCell(); td.appendChild(document.createTextNode(`Port ${i+1}`));
|
|
||||||
td = tr.insertCell(); td.appendChild(document.createTextNode(`${linkS[pState[i]+1]}`));
|
|
||||||
td = tr.insertCell(); td.appendChild(document.createTextNode(`${txG[i]} pkts`));
|
|
||||||
td = tr.insertCell();td.appendChild(document.createTextNode(`${txB[i]} pkts`));
|
|
||||||
td = tr.insertCell();td.appendChild(document.createTextNode(`${rxG[i]} pkts`));
|
|
||||||
td = tr.insertCell();td.appendChild(document.createTextNode(`${rxB[i]} pkts`));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
function l2CMP(a, b)
|
|
||||||
{
|
|
||||||
if (a.port < b.port)
|
|
||||||
return -1;
|
|
||||||
if (a.port > b.port)
|
|
||||||
return 1;
|
|
||||||
if (a.mac < b.mac)
|
|
||||||
return -1;
|
|
||||||
if (a.mac > b.mac)
|
|
||||||
return 1;
|
|
||||||
if (a.vlan < b.vlan)
|
|
||||||
return -1;
|
|
||||||
if (a.vlan > b.vlan)
|
|
||||||
return 1;
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
function uniq(a) {
|
|
||||||
return a.filter(function(item, pos, ary) {
|
|
||||||
return !pos || item.idx != ary[pos - 1].idx;
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
function delL2(idx) {
|
|
||||||
var xhttp = new XMLHttpRequest();
|
|
||||||
xhttp.onreadystatechange = function() {
|
|
||||||
if (this.readyState == 4 && this.status == 200) {
|
|
||||||
var s = JSON.parse(xhttp.responseText);
|
|
||||||
console.log("Entry deletion result: ", s.result);
|
|
||||||
}
|
|
||||||
};
|
|
||||||
xhttp.open("GET", "/l2_del.json?idx=" + idx, true);
|
|
||||||
xhttp.timeout = 1500; xhttp.send();
|
|
||||||
}
|
|
||||||
|
|
||||||
function fillL2(s)
|
|
||||||
{
|
|
||||||
var tbl = document.getElementById('l2table');
|
|
||||||
if (!s.length)
|
|
||||||
return;
|
|
||||||
s.sort(l2CMP);
|
|
||||||
s = uniq(s);
|
|
||||||
var s = s.map(function(e) { e.port = e.port != 9 ? e.port : "CPU"; return e; });
|
|
||||||
console.log("L2: ", JSON.stringify(s));
|
|
||||||
for (let i = 0; i < s.length; i++) {
|
|
||||||
var e = s[i];
|
|
||||||
console.log(i, e);
|
|
||||||
if (tbl.rows[i+1]) {
|
|
||||||
tbl.rows[i+1].cells[0].innerHTML = `${e.port}`;
|
|
||||||
tbl.rows[i+1].cells[1].innerHTML = `${e.mac}`;
|
|
||||||
tbl.rows[i+1].cells[2].innerHTML = `${e.vlan}`;
|
|
||||||
tbl.rows[i+1].cells[4].innerHTML = '<button type="button" onclick="delL2(' + e.idx + ');">Delete</button>';
|
|
||||||
} else {
|
|
||||||
const tr = tbl.insertRow();
|
|
||||||
let td = tr.insertCell(); td.innerHTML = `${e.port}`;
|
|
||||||
td = tr.insertCell(); td.innerHTML = `${e.mac}`;
|
|
||||||
td = tr.insertCell(); td.innerHTML = `${e.vlan}`;
|
|
||||||
td = tr.insertCell(); td.innerHTML = `${e.type}`;
|
|
||||||
td = tr.insertCell(); td.innerHTML = '<button type="button" onclick="delL2(' + e.idx + ');">Delete</button>';
|
|
||||||
}
|
|
||||||
}
|
|
||||||
for (let i = tbl.rows.length - 1; i > s.length; i--)
|
|
||||||
tbl.deleteRow(i);
|
|
||||||
l2Entries = [];
|
|
||||||
}
|
|
||||||
|
|
||||||
function getL2() {
|
|
||||||
var xhttp = new XMLHttpRequest();
|
|
||||||
xhttp.onreadystatechange = function() {
|
|
||||||
if (this.readyState == 4 && this.status == 200) {
|
|
||||||
var s = JSON.parse(xhttp.responseText);
|
|
||||||
var s = s.map(function(e) {
|
|
||||||
e.vlan = parseInt(e.vlan, 16);
|
|
||||||
e.idx = parseInt(e.idx, 16);
|
|
||||||
e.type = e.type == "s" ? "static" : "learned";
|
|
||||||
e.port = e.port == 9 ? 9 : logToPhysPort[e.port];
|
|
||||||
return e;
|
|
||||||
});
|
|
||||||
l2Entries.push(...s);
|
|
||||||
if (l2Entries >= 4096) {
|
|
||||||
l2Entries = [];
|
|
||||||
l2CurrentEntry = 0;
|
|
||||||
clearInterval(l2GetInterval);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
var w = 0;
|
|
||||||
for (var i = l2Entries.length-1; i > 0; i--) {
|
|
||||||
if (l2Entries[0].idx == l2Entries[i].idx) {
|
|
||||||
w = 1;
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if (w) {
|
|
||||||
l2CurrentEntry = 0;
|
|
||||||
fillL2(l2Entries);
|
|
||||||
} else {
|
|
||||||
l2CurrentEntry = s[s.length-1].idx + 1;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
};
|
|
||||||
xhttp.open("GET", "/l2.json?idx=" + l2CurrentEntry, true);
|
|
||||||
xhttp.timeout = 1500; sendXHTTP(xhttp);
|
|
||||||
}
|
|
||||||
|
|
||||||
window.addEventListener("load", function() {
|
|
||||||
update( () => {
|
|
||||||
getL2();
|
|
||||||
const interval = setInterval(update, 2000);
|
|
||||||
l2GetInterval = setInterval(getL2, 1000);
|
|
||||||
});;
|
|
||||||
});
|
|
||||||
|
|
||||||
@@ -1,27 +0,0 @@
|
|||||||
<!DOCTYPE html>
|
|
||||||
<html>
|
|
||||||
<script src="/main.js"></script>
|
|
||||||
<link rel="stylesheet" href="style.css">
|
|
||||||
<title>Link Aggregation Configuration</title>
|
|
||||||
</head>
|
|
||||||
<body>
|
|
||||||
<nav id="sidebar"></nav>
|
|
||||||
<div style="margin-left:16%;padding:1px 16px;height:1000px;">
|
|
||||||
<div id="ports"></div>
|
|
||||||
<h1>Link Aggregation Groups Configuration</h1>
|
|
||||||
<h2>LAG 1 <input style="width:15%;margin-left: 3em;" class="action" id="l_sub0" onclick="lagSub(0);" type="button" value="Update / Create"></h2>
|
|
||||||
<div id="mLAG0"></div>
|
|
||||||
<br />
|
|
||||||
<h2>LAG 2 <input style="width:15%;margin-left: 3em;" class="action" id="l_sub1" onclick="lagSub(1);" type="button" value="Update / Create"></h2>
|
|
||||||
<div id="mLAG1"></div>
|
|
||||||
<br />
|
|
||||||
<h2>LAG 3 <input style="width:15%;margin-left: 3em;" class="action" id="l_sub2" onclick="lagSub(2);" type="button" value="Update / Create"></h2>
|
|
||||||
<div id="mLAG2"></div>
|
|
||||||
<br />
|
|
||||||
<h2>LAG 4 <input style="width:15%;margin-left: 3em;" class="action" id="l_sub3" onclick="lagSub(3);" type="button" value="Update / Create"></h2>
|
|
||||||
<div id="mLAG3"></div>
|
|
||||||
<script src="/lag.js"></script>
|
|
||||||
</div>
|
|
||||||
<script src="/navigation.js"></script>
|
|
||||||
</body>
|
|
||||||
</html>
|
|
||||||
@@ -1,81 +0,0 @@
|
|||||||
var lagInterval = Number();
|
|
||||||
|
|
||||||
function lagForm() {
|
|
||||||
if (!numPorts)
|
|
||||||
return;
|
|
||||||
for (let j=0; j < 4; j++) {
|
|
||||||
var lag = "mLAG" + j
|
|
||||||
console.log("Adding LAG " + lag)
|
|
||||||
var m = document.getElementById(lag);
|
|
||||||
for (let i = 1; i <= numPorts; i++) {
|
|
||||||
const d = document.createElement("div");
|
|
||||||
d.classList.add("cbgroup");
|
|
||||||
const l = document.createElement("label");
|
|
||||||
l.innerHTML = "" + i;
|
|
||||||
l.classList.add("cbgroup");
|
|
||||||
const inp = document.createElement("input");
|
|
||||||
inp.type = "checkbox"; inp.setAttribute("class","psel");
|
|
||||||
inp.id = "p_" + lag + "_" + i;
|
|
||||||
const o = document.createElement("img");
|
|
||||||
if (pIsSFP[i - 1]) {
|
|
||||||
o.src = "sfp.svg"; o.width ="60"; o.height ="60";
|
|
||||||
} else {
|
|
||||||
o.src = "port.svg"; o.width = "40"; o.height = "40";
|
|
||||||
}
|
|
||||||
l.appendChild(inp); l.appendChild(o);
|
|
||||||
d.appendChild(l)
|
|
||||||
m.appendChild(d);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
fetchLag();
|
|
||||||
}
|
|
||||||
|
|
||||||
function setL(p, c){
|
|
||||||
console.log("LAG setting: ", p, " to ", c);
|
|
||||||
document.getElementById(p).checked=c;
|
|
||||||
}
|
|
||||||
|
|
||||||
function fetchLag() {
|
|
||||||
var xhttp = new XMLHttpRequest();
|
|
||||||
xhttp.onreadystatechange = function() {
|
|
||||||
if (this.readyState == 4 && this.status == 200) {
|
|
||||||
const s = JSON.parse(xhttp.responseText);
|
|
||||||
console.log("LAG: ", JSON.stringify(s));
|
|
||||||
for (let l = 0; l < 4; l++) {
|
|
||||||
let members = parseInt(s[l].members, 2);
|
|
||||||
let hash = parseInt(s[l].hash, 16);
|
|
||||||
for (let i = 1; i <= numPorts; i++) {
|
|
||||||
let p = i - 1;
|
|
||||||
if (numPorts < 9)
|
|
||||||
p = physToLogPort[p];
|
|
||||||
setL("p_mLAG"+l+"_"+i, members & (1<<p));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
};
|
|
||||||
xhttp.open("GET", `/lag.json`, true);
|
|
||||||
sendXHTTP(xhttp);
|
|
||||||
}
|
|
||||||
async function lagSub(l) {
|
|
||||||
var cmd = "lag " + l;
|
|
||||||
for (let i = 1; i <= numPorts; i++) {
|
|
||||||
if (document.getElementById("p_mLAG"+l+"_"+i).checked)
|
|
||||||
cmd = cmd + ` ${i}`;
|
|
||||||
}
|
|
||||||
try {
|
|
||||||
const response = await fetch('/cmd', {
|
|
||||||
method: 'POST',
|
|
||||||
body: cmd
|
|
||||||
});
|
|
||||||
console.log('Completed!', response);
|
|
||||||
} catch(err) {
|
|
||||||
console.error(`Error: ${err}`);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
window.addEventListener("load", function() {
|
|
||||||
update( () => {
|
|
||||||
lagForm();
|
|
||||||
const interval = setInterval(update, 2000);
|
|
||||||
});
|
|
||||||
});
|
|
||||||
@@ -1,30 +1,30 @@
|
|||||||
<!DOCTYPE html>
|
<!DOCTYPE html>
|
||||||
<html>
|
<html>
|
||||||
<title>RTL Switch Login</title>
|
<title data-i18n="login_title">RTL Switch Login</title>
|
||||||
<link rel="stylesheet" href="style.css">
|
<link rel="stylesheet" href="style.css">
|
||||||
|
<script src="/main.js"></script>
|
||||||
<script>
|
<script>
|
||||||
function removeNote() {
|
function removeNote() {
|
||||||
document.getElementById("incorrect").innerHTML = "";
|
document.getElementById("incorrect").innerHTML = "";
|
||||||
}
|
}
|
||||||
window.addEventListener("load", function() {
|
window.addEventListener("load", function() {
|
||||||
if (document.referrer.endsWith("login.html"))
|
if (document.referrer.endsWith("login.html"))
|
||||||
document.getElementById("incorrect").innerHTML = "Wrong password!";
|
document.getElementById("incorrect").innerHTML = t('login_wrong');
|
||||||
});
|
});
|
||||||
</script>
|
</script>
|
||||||
</head>
|
</head>
|
||||||
|
|
||||||
<body class="login_page">
|
<body class="login_page">
|
||||||
<div class = "center">
|
<div class = "center">
|
||||||
<h1> RTL Switch Login</h1>
|
<h1 data-i18n="login_heading"> RTL Switch Login</h1>
|
||||||
<form method="post" action="login">
|
<form method="post" action="login">
|
||||||
<div class="txt_field">
|
<div class="txt_field">
|
||||||
<input name="pwd" type="password" onclick="removeNote()" required />
|
<input name="pwd" type="password" autocomplete="current-password" onclick="removeNote()" required />
|
||||||
<span></span>
|
<span></span>
|
||||||
<label>Password</label>
|
<label data-i18n="login_password">Password</label>
|
||||||
</div>
|
</div>
|
||||||
<input type="submit" value="Login"/>
|
<input type="submit" data-i18n="login_login" value="Login"/>
|
||||||
<h3 id="incorrect" style="margin-top: 5em;"></h3>
|
<h3 id="incorrect" style="margin-top: 5em;"></h3>
|
||||||
</form>
|
</form>
|
||||||
</body>
|
</body>
|
||||||
</html>
|
</html>
|
||||||
|
|
||||||
@@ -1,22 +0,0 @@
|
|||||||
document.addEventListener("DOMContentLoaded", function () {
|
|
||||||
fetch('/information.json')
|
|
||||||
.then(response => response.json())
|
|
||||||
.then(data => {
|
|
||||||
const tableBody = document.getElementById('infoTable').querySelector('tbody');
|
|
||||||
|
|
||||||
// Create table rows
|
|
||||||
for (const [key, value] of Object.entries(data)) {
|
|
||||||
const row = document.createElement('tr');
|
|
||||||
const cellKey = document.createElement('td');
|
|
||||||
const cellValue = document.createElement('td');
|
|
||||||
|
|
||||||
cellKey.textContent = key;
|
|
||||||
cellValue.textContent = value;
|
|
||||||
|
|
||||||
row.appendChild(cellKey);
|
|
||||||
row.appendChild(cellValue);
|
|
||||||
tableBody.appendChild(row);
|
|
||||||
}
|
|
||||||
})
|
|
||||||
.catch(error => console.error('Error fetching the data:', error));
|
|
||||||
});
|
|
||||||
@@ -1,26 +0,0 @@
|
|||||||
<!DOCTYPE html>
|
|
||||||
<html>
|
|
||||||
<script src="/main.js"></script>
|
|
||||||
<link rel="stylesheet" href="style.css">
|
|
||||||
<title>Mirror Configuration</title>
|
|
||||||
</head>
|
|
||||||
<body>
|
|
||||||
<nav id="sidebar"></nav>
|
|
||||||
<div style="margin-left:16%;padding:1px 16px;height:1000px;">
|
|
||||||
<div id="ports"></div>
|
|
||||||
<h1>Mirror Configuration</h1>
|
|
||||||
<label class="tswitch">Enabled: <input id="me" type="checkbox"></label><br/>
|
|
||||||
<label for="mp">Mirroring Port:</label> <input type="number" id="mp" name="mp" min="1" max="9"/>
|
|
||||||
<h2>Mirrored Ports (TX)</h2>
|
|
||||||
<div id="mPortsTX"></div>
|
|
||||||
<br />
|
|
||||||
<h2>Mirrored Ports (RX)</h2>
|
|
||||||
<div id="mPortsRX"></div>
|
|
||||||
<br/> <input style="width:15%;" class="action" id="mirror_sub" onclick="mirrorSub();" type="button" value="Update / Create">
|
|
||||||
<input style="width:15%;" class="action" id="mirror_del" onclick="mirrorDel();" type="button" value="Disable Mirroring">
|
|
||||||
<script src="/mirror.js"></script>
|
|
||||||
<script src="/mirror_sub.js"></script>
|
|
||||||
</div>
|
|
||||||
<script src="/navigation.js"></script>
|
|
||||||
</body>
|
|
||||||
</html>
|
|
||||||
@@ -1,64 +0,0 @@
|
|||||||
var mirrorInterval = Number();
|
|
||||||
const mirrors = ["mPortsTX", "mPortsRX"];
|
|
||||||
|
|
||||||
function mirrorForm() {
|
|
||||||
if (!numPorts)
|
|
||||||
return;
|
|
||||||
for (let j=0; j < mirrors.length; j++) {
|
|
||||||
console.log("Adding Mirror " + j)
|
|
||||||
var m = document.getElementById(mirrors[j]);
|
|
||||||
for (let i = 1; i <= numPorts; i++) {
|
|
||||||
const d = document.createElement("div");
|
|
||||||
d.classList.add("cbgroup");
|
|
||||||
const l = document.createElement("label");
|
|
||||||
l.innerHTML = "" + i;
|
|
||||||
l.classList.add("cbgroup");
|
|
||||||
const inp = document.createElement("input");
|
|
||||||
inp.type = "checkbox"; inp.setAttribute("class","psel");
|
|
||||||
inp.id = mirrors[j] + i;
|
|
||||||
const o = document.createElement("img");
|
|
||||||
if (pIsSFP[i - 1]) {
|
|
||||||
o.src = "sfp.svg"; o.width ="60"; o.height ="60";
|
|
||||||
} else {
|
|
||||||
o.src = "port.svg"; o.width = "40"; o.height = "40";
|
|
||||||
}
|
|
||||||
l.appendChild(inp); l.appendChild(o);
|
|
||||||
d.appendChild(l)
|
|
||||||
m.appendChild(d);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
fetchMirror();
|
|
||||||
}
|
|
||||||
|
|
||||||
function setM(p, c){
|
|
||||||
document.getElementById(p).checked=c;
|
|
||||||
}
|
|
||||||
|
|
||||||
function fetchMirror() {
|
|
||||||
var xhttp = new XMLHttpRequest();
|
|
||||||
xhttp.onreadystatechange = function() {
|
|
||||||
if (this.readyState == 4 && this.status == 200) {
|
|
||||||
const s = JSON.parse(xhttp.responseText);
|
|
||||||
console.log("MIRROR: ", JSON.stringify(s));
|
|
||||||
document.getElementById('me').checked = s.enabled;
|
|
||||||
document.getElementById('mp').value = s.mPort;
|
|
||||||
let m_tx = parseInt(s.mirror_tx, 2);
|
|
||||||
let m_rx = parseInt(s.mirror_rx, 2);
|
|
||||||
for (let i = 1; i <= numPorts; i++) {
|
|
||||||
let p = i - 1;
|
|
||||||
if (numPorts < 9)
|
|
||||||
p = physToLogPort[p];
|
|
||||||
setM("mPortsTX"+i, m_tx&(1<<p)); setM("mPortsRX"+i, m_rx&(1<<p));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
};
|
|
||||||
xhttp.open("GET", `/mirror.json`, true);
|
|
||||||
sendXHTTP(xhttp);
|
|
||||||
}
|
|
||||||
|
|
||||||
window.addEventListener("load", function() {
|
|
||||||
update( () => {
|
|
||||||
mirrorForm();
|
|
||||||
const interval = setInterval(update, 2000);
|
|
||||||
});
|
|
||||||
});
|
|
||||||
@@ -1,43 +0,0 @@
|
|||||||
async function mirrorSub() {
|
|
||||||
var cmd = "mirror ";
|
|
||||||
var mp=document.getElementById('mp').value
|
|
||||||
if (!mp) {
|
|
||||||
alert("Set Mirroring Port first");
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
document.getElementById(mirrors[0]+mp).checked=false;document.getElementById(mirrors[1]+mp).checked=false;
|
|
||||||
cmd = cmd + mp;
|
|
||||||
for (let i = 1; i <= numPorts; i++) {
|
|
||||||
if (document.getElementById(mirrors[0] + i).checked && document.getElementById(mirrors[1] + i).checked)
|
|
||||||
cmd = cmd + ` ${i}`;
|
|
||||||
else if (document.getElementById(mirrors[0] + i).checked)
|
|
||||||
cmd = cmd + ` ${i}t`;
|
|
||||||
else if (document.getElementById(mirrors[1] + i).checked)
|
|
||||||
cmd = cmd + ` ${i}r`;
|
|
||||||
}
|
|
||||||
if (cmd.length < 10) {
|
|
||||||
alert("Select Mirrored Ports");
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
try {
|
|
||||||
const response = await fetch('/cmd', {
|
|
||||||
method: 'POST',
|
|
||||||
body: cmd
|
|
||||||
});
|
|
||||||
console.log('Completed!', response);
|
|
||||||
} catch(err) {
|
|
||||||
console.error(`Error: ${err}`);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
async function mirrorDel() {
|
|
||||||
var cmd = "mirror off";
|
|
||||||
try {
|
|
||||||
const response = await fetch('/cmd', {
|
|
||||||
method: 'POST',
|
|
||||||
body: cmd
|
|
||||||
});
|
|
||||||
location.reload();
|
|
||||||
} catch(err) {
|
|
||||||
console.error(`Error: ${err}`);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,12 +0,0 @@
|
|||||||
document.getElementById('sidebar').innerHTML =
|
|
||||||
"<ul><li><a href='index.html'>Overview</a></li>"
|
|
||||||
+ "<li><a href='ports.html'>Port Configuration</a></li>"
|
|
||||||
+ "<li><a href='stat.html'>Port Statistics</a></li>"
|
|
||||||
+ "<li><a href='vlan.html'>VLAN</a></li>"
|
|
||||||
+ "<li><a href='l2.html'>L2 Configuration</a></li>"
|
|
||||||
+ "<li><a href='mirror.html'>Mirroring</a></li>"
|
|
||||||
+ "<li><a href='lag.html'>Link Aggregation</a></li>"
|
|
||||||
+ "<li><a href='eee.html'>EEE</a></li>"
|
|
||||||
+ "<li><a href='bandwidth.html'>Bandwidth Limits</a></li>"
|
|
||||||
+ "<li><a href='system.html'>System Settings</a></li>"
|
|
||||||
+ "<li><a href='update.html'>Firmware Update</a></li></ul>";
|
|
||||||
@@ -1,24 +0,0 @@
|
|||||||
<!DOCTYPE html>
|
|
||||||
<html>
|
|
||||||
<script src="/main.js"></script>
|
|
||||||
<link rel="stylesheet" href="style.css">
|
|
||||||
<title>FreeSwitchOS Port Configuration</title>
|
|
||||||
</head>
|
|
||||||
<body>
|
|
||||||
<nav id="sidebar"></nav>
|
|
||||||
<div style="margin-left:16%;padding:1px 16px;height:1000px;">
|
|
||||||
<div id="ports"></div>
|
|
||||||
<h1>Port Configuration</h1>
|
|
||||||
<form id="vform" action="/vlan.html">
|
|
||||||
<table id="speedtable">
|
|
||||||
<tr> <th>Port</th> <th>Name</th> <th>Current Link Speed</th><th>Set Speed</th><th>Disabled</th><th>Apply</th></tr>
|
|
||||||
</table>
|
|
||||||
<h2 style="margin-top:3em">Configure Maximum Frame Size (MTU) forwarded at Port</h2>
|
|
||||||
<table id="mtutable" style="margin-top:1em">
|
|
||||||
</table>
|
|
||||||
<script src="/ports.js"></script>
|
|
||||||
</form>
|
|
||||||
</div>
|
|
||||||
<script src="/navigation.js"></script>
|
|
||||||
</body>
|
|
||||||
</html>
|
|
||||||
@@ -1,149 +0,0 @@
|
|||||||
var mtus = new Int16Array(10);
|
|
||||||
var clicked = new Int8Array(10);
|
|
||||||
function createPortTable() {
|
|
||||||
var tbl = document.getElementById('speedtable');
|
|
||||||
if (tbl.rows.length <= 2 && numPorts) {
|
|
||||||
const sSelect = '<select name="speed_sel" id="speed_sel">'
|
|
||||||
+ '<option value="auto">Auto</option>'
|
|
||||||
+ '<option value="2g5">2500MBit/Full</option>'
|
|
||||||
+ '<option value="1g">1000MBit/Full</option>'
|
|
||||||
+ '<option value="100m full">100MBit/Full</option>'
|
|
||||||
+ '<option value="100m half">100MBit/Half</option>'
|
|
||||||
+ '<option value="10m full">10MBit/Full</option>'
|
|
||||||
+ '<option value="10m half">10MBit/Half</option>'
|
|
||||||
+ '</select>';
|
|
||||||
const dSwitch = '<input type="checkbox" id="disable_port" onchange="portOnOff();">'
|
|
||||||
for (let i = 1; i <= numPorts; i++) {
|
|
||||||
if (pIsSFP[i-1])
|
|
||||||
continue;
|
|
||||||
console.log("Table row: " + i + "pState: " + pState[i-2]);
|
|
||||||
const tr = tbl.insertRow();
|
|
||||||
let td = tr.insertCell(); td.appendChild(document.createTextNode(`Port ${i}`));
|
|
||||||
let portName = portNames[physToLogPort[i-1]] || '';
|
|
||||||
td = tr.insertCell(); td.appendChild(document.createTextNode(portName));
|
|
||||||
td = tr.insertCell(); td.innerHTML = linkS[pState[i] + 1];
|
|
||||||
td = tr.insertCell(); td.innerHTML = sSelect.replaceAll("speed_sel", "speed_sel_" + i);
|
|
||||||
td = tr.insertCell(); td.innerHTML = dSwitch.replaceAll("disable_port", "disable_port_" + i)
|
|
||||||
.replace("portOnOff()", "portOnOff(" + i + ")");
|
|
||||||
var button = '<button type="button" style="margin: 0 0 0 24px" onclick="applySpeed(' + i + ');">Apply</button>';
|
|
||||||
td = tr.insertCell();
|
|
||||||
td.innerHTML = button;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
tbl = document.getElementById('mtutable');
|
|
||||||
if (tbl.rows.length <= 2 && numPorts) {
|
|
||||||
const mSelect = '<select name="mtu_sel" id="mtu_sel">'
|
|
||||||
+ '<option value="16383">16383</option>'
|
|
||||||
+ '<option value="1522">1522</option>'
|
|
||||||
+ '<option value="1536">1536</option>'
|
|
||||||
+ '<option value="1552">1552</option>'
|
|
||||||
+ '<option value="9216">9216</option>'
|
|
||||||
+ '</select>';
|
|
||||||
var tr = tbl.insertRow();
|
|
||||||
for (let i = 1; i <= numPorts; i++) {
|
|
||||||
let td = tr.insertCell();
|
|
||||||
if (pIsSFP[i-1])
|
|
||||||
td.innerHTML = '<object type="image/svg+xml" data="sfp.svg" width="60"></object>'
|
|
||||||
else
|
|
||||||
td.innerHTML = '<object type="image/svg+xml" data="port.svg" width="40"></object>'
|
|
||||||
}
|
|
||||||
tr = tbl.insertRow();
|
|
||||||
for (let i = 1; i <= numPorts; i++) {
|
|
||||||
let td = tr.insertCell();
|
|
||||||
td.innerHTML = mSelect.replaceAll("mtu_sel", "mtu_sel_" + i);
|
|
||||||
}
|
|
||||||
tr = tbl.insertRow();
|
|
||||||
for (let i = 1; i <= numPorts; i++) {
|
|
||||||
let td = tr.insertCell();
|
|
||||||
td.innerHTML = '<button type="button" style="margin: 0 0 0 24px" onclick="applyMTU(' + i + ');">Apply</button>';
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
function updatePortTable() {
|
|
||||||
console.log("updatePortTable called");
|
|
||||||
var tbl = document.getElementById('speedtable');
|
|
||||||
if (tbl.rows.length <= 2 || !numPorts)
|
|
||||||
return;
|
|
||||||
for (let i = 1; i <= numPorts ; i++) {
|
|
||||||
if (pIsSFP[i-1])
|
|
||||||
continue;
|
|
||||||
tbl.rows[i].cells[2].innerHTML = `${linkS[pState[i-1]+1]}`;
|
|
||||||
if (!clicked[i] && pState[i - 1] < 0) {
|
|
||||||
document.getElementById('speed_sel_' + i).disabled = true;
|
|
||||||
document.getElementById('disable_port_' + i).checked = true;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
async function applySpeed(port) {
|
|
||||||
var speed = document.getElementById('speed_sel_' + port).value;
|
|
||||||
var disabled = document.getElementById('disable_port_' + port).checked;
|
|
||||||
var cmd = "port " + port + " ";
|
|
||||||
if (!disabled)
|
|
||||||
cmd = cmd + speed;
|
|
||||||
else
|
|
||||||
cmd = cmd + "off";
|
|
||||||
console.log("CMD: " + cmd);
|
|
||||||
try {
|
|
||||||
const response = await fetch('/cmd', {
|
|
||||||
method: 'POST',
|
|
||||||
body: cmd
|
|
||||||
});
|
|
||||||
console.log('Completed!', response);
|
|
||||||
} catch(err) {
|
|
||||||
console.error(`Error: ${err}`);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
async function portOnOff(p) {
|
|
||||||
var disabled = document.getElementById('disable_port_' + p).checked;
|
|
||||||
document.getElementById('speed_sel_' + p).disabled = disabled;
|
|
||||||
clicked[p] = 1;
|
|
||||||
}
|
|
||||||
|
|
||||||
async function applyMTU(port) {
|
|
||||||
var mtu = document.getElementById('mtu_sel_' + port).value;
|
|
||||||
var cmd = "mtu " + port + " " + mtu;
|
|
||||||
try {
|
|
||||||
const response = await fetch('/cmd', {
|
|
||||||
method: 'POST',
|
|
||||||
body: cmd
|
|
||||||
});
|
|
||||||
console.log('MTU Completed!', response);
|
|
||||||
getMTUs();
|
|
||||||
} catch(err) {
|
|
||||||
console.error(`Error: ${err}`);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
function getMTUs() {
|
|
||||||
var xhttp = new XMLHttpRequest();
|
|
||||||
xhttp.onreadystatechange = function() {
|
|
||||||
if (this.readyState == 4 && this.status == 200) {
|
|
||||||
const s = JSON.parse(xhttp.responseText);
|
|
||||||
console.log("MTUS: ", JSON.stringify(s));
|
|
||||||
for (let i = 0; i < s.length; i++) {
|
|
||||||
p = s[i];
|
|
||||||
let n = p.portNum;
|
|
||||||
mtus[n] = parseInt(p.mtu, 16);
|
|
||||||
var mtu = document.getElementById('mtu_sel_' + n);
|
|
||||||
if (!mtu)
|
|
||||||
continue;
|
|
||||||
mtu.value = mtus[n];
|
|
||||||
}
|
|
||||||
}
|
|
||||||
};
|
|
||||||
xhttp.open("GET", "/mtu.json", true);
|
|
||||||
xhttp.timeout = 1500; sendXHTTP(xhttp);
|
|
||||||
}
|
|
||||||
|
|
||||||
window.addEventListener("load", function() {
|
|
||||||
update( () => {
|
|
||||||
createPortTable();
|
|
||||||
updatePortTable();
|
|
||||||
getMTUs()
|
|
||||||
const interval = setInterval(update, 2000);
|
|
||||||
const updatePortTableInterval = setInterval(updatePortTable, 1000);
|
|
||||||
});
|
|
||||||
});
|
|
||||||
@@ -1,47 +0,0 @@
|
|||||||
<!DOCTYPE html>
|
|
||||||
<html>
|
|
||||||
<script src="/main.js"></script>
|
|
||||||
<link rel="stylesheet" href="style.css">
|
|
||||||
<title>FreeSwitchOS Port Statistics</title>
|
|
||||||
<style>
|
|
||||||
.popup {
|
|
||||||
display: none;
|
|
||||||
position: fixed;
|
|
||||||
top: 0; left: 0;
|
|
||||||
width: 100%; height: 100%;
|
|
||||||
background: rgba(0, 0, 0, 0.5);
|
|
||||||
justify-content: center;
|
|
||||||
align-items: center;
|
|
||||||
}
|
|
||||||
.popup-content {
|
|
||||||
background: #fff;
|
|
||||||
padding: 20px;
|
|
||||||
border-radius: 5px;
|
|
||||||
text-align: center;
|
|
||||||
height: 80%
|
|
||||||
}
|
|
||||||
.popup .popup-content {
|
|
||||||
overflow-y: scroll;
|
|
||||||
}
|
|
||||||
</style>
|
|
||||||
</head>
|
|
||||||
<body>
|
|
||||||
<nav id="sidebar"></nav>
|
|
||||||
<div style="margin-left:16%;padding:1px 16px;height:1000px;">
|
|
||||||
<div id="ports"></div>
|
|
||||||
<div id="popup" class="popup">
|
|
||||||
<div class="popup-content">
|
|
||||||
<h2>Detailed Port Statistics</h2>
|
|
||||||
<div id="popup_text"></div>
|
|
||||||
<button id="closePopup" class="action">Close</button>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
<h1>Port Statistics</h1>
|
|
||||||
<table id="statstable">
|
|
||||||
<tr> <th>Port</th> <th>Name</th> <th>link</th> <th>TX Good</th> <th>TX Bad</th> <th>RX Good</th> <th>RX Bad</th> <th> All Counters </th></tr>
|
|
||||||
<script src="/stat.js"></script>
|
|
||||||
</table>
|
|
||||||
</div>
|
|
||||||
</body>
|
|
||||||
<script src="/navigation.js"></script>
|
|
||||||
</html>
|
|
||||||
@@ -1,207 +0,0 @@
|
|||||||
const mib_counters = [
|
|
||||||
"Interface in Octets", 8,
|
|
||||||
"", 0,
|
|
||||||
"Interface out Octets", 8,
|
|
||||||
"", 0,
|
|
||||||
"Interface in Unicast Pkts", 8,
|
|
||||||
"", 0,
|
|
||||||
"Interface in Multicast Pkts", 8,
|
|
||||||
"", 0,
|
|
||||||
"Interface in Broadcast Pkts", 8,
|
|
||||||
"", 0,
|
|
||||||
"Interface out Unicast Pkts", 8, // 10
|
|
||||||
"", 0,
|
|
||||||
"Interface out Multicast Pkts", 8,
|
|
||||||
"", 0,
|
|
||||||
"Interface out Broadcast Pkts", 8,
|
|
||||||
"", 0,
|
|
||||||
"Interface out discards", 4,
|
|
||||||
"802.1d Tp Port in discards", 4,
|
|
||||||
"802.3 Single collision frames", 4,
|
|
||||||
"802.3 Multi collision frames", 4,
|
|
||||||
"802.3 Deferred transmissions", 4, // 20
|
|
||||||
"802.3 Late collisions", 4,
|
|
||||||
"802.3 Excessive collisions", 4,
|
|
||||||
"802.3 Symbol errors", 4,
|
|
||||||
"802.3 Control in unknown opcodes", 4,
|
|
||||||
"802.3 In Pause frames", 4,
|
|
||||||
"802.3 Out Pause frames", 4,
|
|
||||||
"Ether drop events", 4,
|
|
||||||
"TX Ether Broadcast Pkts", 4,
|
|
||||||
"TX Ether Multicast Pkts", 4,
|
|
||||||
"TX Ether CRC Align errors", 4, // 30
|
|
||||||
"RX Ether CRC Align errors", 4,
|
|
||||||
"TX Ether Undersized Pkts", 4,
|
|
||||||
"RX Ether Undersized Pkts", 4,
|
|
||||||
"TX Ether Oversized Pkts", 4,
|
|
||||||
"RX Ether Oversized Pkts", 4,
|
|
||||||
"TX Ether Fragments", 4,
|
|
||||||
"RX Ether fragments", 4,
|
|
||||||
"TX Ether Jabbers", 4,
|
|
||||||
"RX Ether Jabbers", 4,
|
|
||||||
"TX Ether Collisions", 4, // 40
|
|
||||||
"TX Ether Pkts 640 Octets", 4,
|
|
||||||
"RX Ether Pkts 640 Octets", 4,
|
|
||||||
"TX Ether 65-127 Octets", 4,
|
|
||||||
"RX Ether 65-127 Octets", 4,
|
|
||||||
"TX Ether Pkts 128-255 Octets", 4,
|
|
||||||
"RX Ether Pkts 128-255 Octets", 4,
|
|
||||||
"TX Ether Pkts 256-511 Octets", 4,
|
|
||||||
"RX Ether Pkts 256-511 Octets", 4,
|
|
||||||
"TX Ether Pkts 512-1023 Octets", 4,
|
|
||||||
"RX Ether Pkts 512-1023 Octets", 4, // 50
|
|
||||||
"TX Ether Pkts 1024-1518 Octets", 4,
|
|
||||||
"RX Ether Pkts 1024-1518 Octets", 4,
|
|
||||||
"", 4,
|
|
||||||
"RX Ether Undersized Drop Pkts", 4, // 54
|
|
||||||
"TX Ether Pkts >1518 Octets", 4,
|
|
||||||
"RX Ether Pkts >1518 Octets", 4,
|
|
||||||
"TX Ether Pkts too large", 4,
|
|
||||||
"RX Ether Pkts too large", 4,
|
|
||||||
"TX Ether Flexible Octets Set 1", 4,
|
|
||||||
"RX Ether Flexible Octets Set 1", 4,// 60
|
|
||||||
"TX Ether Flexible Octets CRC Set 1", 4,
|
|
||||||
"RX Ether Flexible Octets CRC Set 1", 4,
|
|
||||||
"TX Ether Flexible Octets Set 0", 4,
|
|
||||||
"RX Ether Flexible Octets Set 0", 4,
|
|
||||||
"TX Ether Flexible Octets CRC Set 0", 4,
|
|
||||||
"RX Ether Flexible Octets CRC Set 0", 4,
|
|
||||||
"Lenth Field Errors", 4,
|
|
||||||
"False Carriers", 4,
|
|
||||||
"Undersized Octets", 4,
|
|
||||||
"Framing Errors", 4, // 70
|
|
||||||
"", 4,
|
|
||||||
"RX MAC Discards", 4, // 72
|
|
||||||
"RX MAC IPG Short Drop", 4,
|
|
||||||
"", 4,
|
|
||||||
"802.1d TP Learned Entry Discards", 4, // 75
|
|
||||||
"Egress Queue 7 Dropped Pkts", 4,
|
|
||||||
"Egress Queue 6 Dropped Pkts", 4,
|
|
||||||
"Egress Queue 5 Dropped Pkts", 4,
|
|
||||||
"Egress Queue 4 Dropped Pkts", 4,
|
|
||||||
"Egress Queue 3 Dropped Pkts", 4, // 80
|
|
||||||
"Egress Queue 2 Dropped Pkts", 4,
|
|
||||||
"Egress Queue 1 Dropped Pkts", 4,
|
|
||||||
"Egress Queue 0 Dropped Pkts", 4,
|
|
||||||
"Egress Queue 7 Out Pkts", 4,
|
|
||||||
"Egress Queue 6 Out Pkts", 4,
|
|
||||||
"Egress Queue 5 Out Pkts", 4,
|
|
||||||
"Egress Queue 4 Out Pkts", 4,
|
|
||||||
"Egress Queue 3 Out Pkts", 4,
|
|
||||||
"Egress Queue 2 Out Pkts", 4,
|
|
||||||
"Egress Queue 1 Out Pkts", 4, // 90
|
|
||||||
"Egress Queue 0 Out Pkts", 4,
|
|
||||||
"TX Good Counter", 8,
|
|
||||||
"", 0,
|
|
||||||
"RX Good Counter", 8,
|
|
||||||
"", 0,
|
|
||||||
"RX Error Counter", 4,
|
|
||||||
"TX Error Counter", 4,
|
|
||||||
"TX Good Counter PHY", 8,
|
|
||||||
"", 0,
|
|
||||||
"RX Good Counter PHY", 8, // 100
|
|
||||||
"", 0,
|
|
||||||
"RX Error Counter PHY", 4,
|
|
||||||
"TX Error Counter PHY", 4
|
|
||||||
];
|
|
||||||
|
|
||||||
|
|
||||||
function getCounters(port) {
|
|
||||||
var xhttp = new XMLHttpRequest();
|
|
||||||
const popup = document.getElementById('popup');
|
|
||||||
xhttp.onreadystatechange = function() {
|
|
||||||
if (this.readyState == 4 && this.status == 200) {
|
|
||||||
const s = JSON.parse(xhttp.responseText);
|
|
||||||
console.log("Counters: ", JSON.stringify(s));
|
|
||||||
const ptext = document.getElementById('popup_text');
|
|
||||||
var t = "<table style='width:100%'> <tr> <th>Counter</th> <th>Value</th> <th>Counter</th> <th>Value</th></tr> <tr>";
|
|
||||||
console.log("Counter 0: ", BigInt(s[0]).toString(), " length: ", s.length);
|
|
||||||
var c = 0;
|
|
||||||
for (i = 0; i < mib_counters.length; i += 4) {
|
|
||||||
console.log(i, " ", mib_counters[i], ": ", mib_counters[i+1]);
|
|
||||||
if (mib_counters[i] == "" && mib_counters[i + 1] == 8) {
|
|
||||||
console.log("c " + i + ": continue");
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
var count = BigInt(s[i/4]);
|
|
||||||
if (mib_counters[i+1] == 8) {
|
|
||||||
t += "<td>" + mib_counters[i] + "</td><td>" + count.toString() + "</td>";
|
|
||||||
c += 1;
|
|
||||||
} else if (mib_counters[i+1] == 4) {
|
|
||||||
if (mib_counters[i] != "") {
|
|
||||||
t += "<td>" + mib_counters[i] + "</td><td>" + (count >> 32n).toString() + "</td>";
|
|
||||||
c += 1;
|
|
||||||
}
|
|
||||||
if (c == 2) {
|
|
||||||
t += "</tr> <tr>";
|
|
||||||
c = 0;
|
|
||||||
}
|
|
||||||
if (mib_counters[i+2] != "") {
|
|
||||||
t += "<td>" + mib_counters[i+2] + "</td><td>" + (count & 4294967295n).toString() + "</td>";
|
|
||||||
c += 1;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if (c == 2) {
|
|
||||||
t += "</tr> <tr>";
|
|
||||||
c = 0;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
ptext.innerHTML = t + "</tr></table>";
|
|
||||||
popup.style.display = 'flex';
|
|
||||||
}
|
|
||||||
};
|
|
||||||
xhttp.open("GET", "/counters.json?port=" + port, true);
|
|
||||||
xhttp.timeout = 1500; sendXHTTP(xhttp);
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
function fillStats() {
|
|
||||||
var tbl = document.getElementById('statstable');
|
|
||||||
if (!numPorts)
|
|
||||||
return;
|
|
||||||
if (tbl.rows.length > 1) {
|
|
||||||
for (let i = 0; i < numPorts; i++) {
|
|
||||||
console.log("Table Update row: " + i + " state " + pState[i] + " is " + linkS[pState[i] +1]);
|
|
||||||
tbl.rows[i+1].cells[2].innerHTML = `${linkS[pState[i]+1]}`;
|
|
||||||
tbl.rows[i+1].cells[3].innerHTML = `${txG[i]} pkts`;
|
|
||||||
tbl.rows[i+1].cells[4].innerHTML = `${txB[i]} pkts`;
|
|
||||||
tbl.rows[i+1].cells[5].innerHTML = `${rxG[i]} pkts`;
|
|
||||||
tbl.rows[i+1].cells[6].innerHTML = `${rxB[i]} pkts`;
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
for (let i = 0; i < numPorts; i++) {
|
|
||||||
console.log("Table row: " + i);
|
|
||||||
const tr = tbl.insertRow();
|
|
||||||
let td = tr.insertCell(); td.appendChild(document.createTextNode(`Port ${i+1}`));
|
|
||||||
let portName = portNames[physToLogPort[i]] || '';
|
|
||||||
td = tr.insertCell(); td.appendChild(document.createTextNode(portName));
|
|
||||||
td = tr.insertCell(); td.appendChild(document.createTextNode(`${linkS[pState[i]+1]}`));
|
|
||||||
td = tr.insertCell(); td.appendChild(document.createTextNode(`${txG[i]} pkts`));
|
|
||||||
td = tr.insertCell();td.appendChild(document.createTextNode(`${txB[i]} pkts`));
|
|
||||||
td = tr.insertCell();td.appendChild(document.createTextNode(`${rxG[i]} pkts`));
|
|
||||||
td = tr.insertCell();td.appendChild(document.createTextNode(`${rxB[i]} pkts`));
|
|
||||||
var button = '<button type="button" style="margin: 0 0 0 24px" onclick="getCounters(' + i + ');">Show</button>';
|
|
||||||
td = tr.insertCell(); td.innerHTML = button;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
const popup = document.getElementById('popup');
|
|
||||||
const closePopup = document.getElementById('closePopup');
|
|
||||||
closePopup.addEventListener('click', () => {
|
|
||||||
popup.style.display = 'none';
|
|
||||||
});
|
|
||||||
window.addEventListener('click', (event) => {
|
|
||||||
if (event.target === popup) {
|
|
||||||
popup.style.display = 'none';
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
window.addEventListener("load", function() {
|
|
||||||
update( () => {
|
|
||||||
update();
|
|
||||||
fillStats();
|
|
||||||
const stat = setInterval(fillStats, 1000);
|
|
||||||
const interval = setInterval(update, 2000);
|
|
||||||
});
|
|
||||||
});
|
|
||||||
@@ -82,6 +82,7 @@ object, img {
|
|||||||
.isNOK{ color: #900;}
|
.isNOK{ color: #900;}
|
||||||
.isOK{ color: #090;}
|
.isOK{ color: #090;}
|
||||||
.ip{padding:8px 16px;margin-bottom: 1em;margin-left: 1em}
|
.ip{padding:8px 16px;margin-bottom: 1em;margin-left: 1em}
|
||||||
|
.rotext{display:inline-block;padding:8px 16px;margin-bottom: 1em;margin-left: 1em}
|
||||||
.row {display: flex;}
|
.row {display: flex;}
|
||||||
.rcol {flex: 90%;}
|
.rcol {flex: 90%;}
|
||||||
.lcol {flex: 10%;}
|
.lcol {flex: 10%;}
|
||||||
@@ -164,3 +165,8 @@ margin: 30px 0;
|
|||||||
select { text-align-last: right; font-family: monospace}
|
select { text-align-last: right; font-family: monospace}
|
||||||
option { direction: rtl; font-family: sans-serif}
|
option { direction: rtl; font-family: sans-serif}
|
||||||
#vlanTable td { text-align: left; }
|
#vlanTable td { text-align: left; }
|
||||||
|
|
||||||
|
.l2sort{cursor:pointer;user-select:none}
|
||||||
|
.l2sort:hover{text-decoration:underline}
|
||||||
|
.l2arrow{opacity:0.55;font-size:0.85em}
|
||||||
|
.l2filter{width:100%;box-sizing:border-box;font-weight:normal;font-size:0.9em}
|
||||||
@@ -1,84 +0,0 @@
|
|||||||
<!DOCTYPE html>
|
|
||||||
<html>
|
|
||||||
<head>
|
|
||||||
<link rel="stylesheet" href="style.css">
|
|
||||||
<title>System Settings</title>
|
|
||||||
<style>
|
|
||||||
.tab-bar { display: flex; border-bottom: 2px solid #226; margin-bottom: 0; margin-left: 16%; padding: 1px 16px; padding-bottom: 0; }
|
|
||||||
.tab-btn { padding: 10px 20px; background-color: #ddf; border: none; cursor: pointer; font-size: 1em; border-radius: 8px 8px 0 0; margin-right: 4px; }
|
|
||||||
.tab-btn:hover { background-color: #aaf; }
|
|
||||||
.tab-btn.active { background-color: #aaf; font-weight: bold; border-bottom: 2px solid #aaf; margin-bottom: -2px; }
|
|
||||||
.tab-content { display: none; }
|
|
||||||
.tab-content.active { display: block; }
|
|
||||||
</style>
|
|
||||||
</head>
|
|
||||||
<body>
|
|
||||||
<div class="tab-bar">
|
|
||||||
<button class="tab-btn active" onclick="openTab(event, 'system-tab')">System</button>
|
|
||||||
<button class="tab-btn" onclick="openTab(event, 'advanced-tab')">Advanced</button>
|
|
||||||
<button class="tab-btn" onclick="openTab(event, 'console-tab')">Console</button>
|
|
||||||
</div>
|
|
||||||
<nav id="sidebar"></nav>
|
|
||||||
<div style="margin-left:16%;padding:1px 16px;height:1000px;">
|
|
||||||
<div id="ports"></div>
|
|
||||||
|
|
||||||
<div id="system-tab" class="tab-content active">
|
|
||||||
<h1>System Settings</h1>
|
|
||||||
<div class="row">
|
|
||||||
<div class="lcol"> <label for="ip">IP address:</label></div>
|
|
||||||
<div class="rcol"> <input id="ip" class="ip" type="text" minlength="7" maxlength="15" size="15"/></div>
|
|
||||||
</div>
|
|
||||||
<div class="row">
|
|
||||||
<div class="lcol"> <label for="netmask">Netmask:</label></div>
|
|
||||||
<div class="rcol"><input id="netmask" class="ip" type="text" minlength="7" maxlength="15" size="15"/></div>
|
|
||||||
</div>
|
|
||||||
<div class="row">
|
|
||||||
<div class="lcol"> <label for="gw">Gateway:</label></div>
|
|
||||||
<div class="rcol"><input id="gw" class="ip" type="text" minlength="7" maxlength="15" size="15"/></div>
|
|
||||||
</div>
|
|
||||||
<br/>
|
|
||||||
When updating the above settings, remember to point your browser to the new IP afterwards:<br/>
|
|
||||||
<input style="width:40%;" class="action" id="ip_sub" onclick="ipSub();" type="button" value="Update Settings"><br/>
|
|
||||||
<br/>
|
|
||||||
Save all current settings to Flash:<br/>
|
|
||||||
<input style="width:40%;" class="action" id="flash_sub" onclick="flashSave();" type="button" value="Save Settings to Flash">
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<div id="advanced-tab" class="tab-content">
|
|
||||||
<h1>Advanced Settings</h1>
|
|
||||||
<div class="lcol"> <label for="config_display">Startup configuration:</label></div>
|
|
||||||
<textarea id="config_display" rows="8" cols="60"></textarea>
|
|
||||||
<br/><br/>
|
|
||||||
Be careful when saving the directly edited startup configuration, you can lock yourself out:<br/>
|
|
||||||
<input style="width:40%;" class="action" id="clear_config" onclick="clearConfig();" type="button" value="Clear Startup Config">
|
|
||||||
<br/>
|
|
||||||
<input style="width:40%;" class="action" id="flash_startup_sub" onclick="flashStartupSave();" type="button" value="Save Startup Settings to Flash">
|
|
||||||
<br/>
|
|
||||||
<input style="width:40%;" class="action" id="switch_reset" onclick="resetSwitch();" type="button" value="Reset Switch">
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<div id="console-tab" class="tab-content">
|
|
||||||
<h1>Console Command</h1>
|
|
||||||
<label for="console_command">Enter command:</label>
|
|
||||||
<input type="text" id="console_cmd" name="console_cmd" style="width:40%;">
|
|
||||||
<input style="width:20%;" class="action" id="cmd_sub" onclick="cmdSub();" type="button" value="Send Command"><br/>
|
|
||||||
<br/><br/>
|
|
||||||
Be careful when entering console commands, you can lock yourself out!<br/>
|
|
||||||
|
|
||||||
|
|
||||||
</div>
|
|
||||||
|
|
||||||
</div>
|
|
||||||
<script src="/config.js"></script>
|
|
||||||
<script src="/system.js"></script>
|
|
||||||
<script src="/navigation.js"></script>
|
|
||||||
<script>
|
|
||||||
function openTab(evt, tabId) {
|
|
||||||
document.querySelectorAll('.tab-content').forEach(c => c.classList.remove('active'));
|
|
||||||
document.querySelectorAll('.tab-btn').forEach(b => b.classList.remove('active'));
|
|
||||||
document.getElementById(tabId).classList.add('active');
|
|
||||||
evt.currentTarget.classList.add('active');
|
|
||||||
}
|
|
||||||
</script>
|
|
||||||
</body>
|
|
||||||
</html>
|
|
||||||
@@ -1,150 +0,0 @@
|
|||||||
var systemInterval = Number();
|
|
||||||
var isSaving = false;
|
|
||||||
const ips = ["ip", "netmask", "gw"];
|
|
||||||
|
|
||||||
function checkIp(ip) {
|
|
||||||
const ipv4 = /^(\d{1,3}\.){3}\d{1,3}$/;
|
|
||||||
if (!ipv4.test(ip)) {alert(`Invalid ip:${ip}`); return false };
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
async function ipSub() {
|
|
||||||
for (let i=0;i<3;i++) {
|
|
||||||
if (!checkIp(document.getElementById(ips[i]).value))
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
var cmd = '';
|
|
||||||
for (let i=0; i<3;i++){
|
|
||||||
cmd += ips[i]+' '+document.getElementById(ips[i]).value+'\n';
|
|
||||||
}
|
|
||||||
try {
|
|
||||||
const response = await fetch('/cmd', {
|
|
||||||
method: 'POST',
|
|
||||||
body: cmd
|
|
||||||
});
|
|
||||||
console.log('Completed!', response);
|
|
||||||
fetchIP();
|
|
||||||
} catch(err) {
|
|
||||||
console.error(`Error: ${err}`);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
async function cmdSub() {
|
|
||||||
var cmd = document.getElementById('console_cmd').value;
|
|
||||||
try {
|
|
||||||
const response = await fetch('/cmd', {
|
|
||||||
method: 'POST',
|
|
||||||
body: cmd
|
|
||||||
});
|
|
||||||
console.log('Completed!', response);
|
|
||||||
} catch(err) {
|
|
||||||
console.error(`Error: ${err}`);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
async function sendConfig(c) {
|
|
||||||
if (isSaving) return;
|
|
||||||
isSaving = true;
|
|
||||||
clearInterval(systemInterval);
|
|
||||||
const form = new FormData();
|
|
||||||
form.append("MAX_FILE_SIZE", "4096");
|
|
||||||
form.append("configuration", new Blob([c], {type: "application/octet-stream"}), "config.txt");
|
|
||||||
try {
|
|
||||||
const response = await fetch('/config', {
|
|
||||||
method: 'POST',
|
|
||||||
body: form
|
|
||||||
});
|
|
||||||
console.log('Completed!', response);
|
|
||||||
try {
|
|
||||||
await fetch('/cmd_log_clear', { method: 'GET' });
|
|
||||||
} catch(e) {}
|
|
||||||
} catch(err) {
|
|
||||||
console.error(`Error: ${err}`);
|
|
||||||
} finally {
|
|
||||||
isSaving = false;
|
|
||||||
systemInterval = setInterval(fetchIP, 1000);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
async function flashSave() {
|
|
||||||
configuration = [];
|
|
||||||
const savedConfig = await fetchConfig();
|
|
||||||
const cmdLog = await fetchCmdLog();
|
|
||||||
if (savedConfig) parseConf(savedConfig);
|
|
||||||
if (cmdLog) parseConf(cmdLog);
|
|
||||||
const body = configuration.join('\n') + '\n';
|
|
||||||
console.log("CONFIGURATION to save: ", body);
|
|
||||||
await sendConfig(body);
|
|
||||||
}
|
|
||||||
|
|
||||||
async function flashStartupSave() {
|
|
||||||
var configContent = document.getElementById("config_display").value;
|
|
||||||
console.log("CONFIGURATION to save: ", configContent);
|
|
||||||
sendConfig(configContent);
|
|
||||||
// Clear the command log 1 second after initiating the config save
|
|
||||||
setTimeout(() => {
|
|
||||||
fetch('/cmd_log_clear', { method: 'GET' })
|
|
||||||
.then(response => console.log('Command log cleared', response))
|
|
||||||
.catch(err => console.error('Error clearing command log:', err));
|
|
||||||
}, 1000);
|
|
||||||
}
|
|
||||||
|
|
||||||
function clearConfig() {
|
|
||||||
document.getElementById("config_display").value = "";
|
|
||||||
|
|
||||||
// Validate and populate with current IP settings
|
|
||||||
for (let i=0; i<3; i++) {
|
|
||||||
if (!checkIp(document.getElementById(ips[i]).value))
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
var configLines = "";
|
|
||||||
for (let i=0; i<3; i++){
|
|
||||||
var cmd = ips[i]+' '+document.getElementById(ips[i]).value;
|
|
||||||
configLines += cmd + "\n";
|
|
||||||
}
|
|
||||||
|
|
||||||
document.getElementById("config_display").value = configLines;
|
|
||||||
}
|
|
||||||
|
|
||||||
function fetchIP() {
|
|
||||||
var xhttp = new XMLHttpRequest();
|
|
||||||
xhttp.onreadystatechange = function() {
|
|
||||||
if (this.readyState == 4 && this.status == 200) {
|
|
||||||
const s = JSON.parse(xhttp.responseText);
|
|
||||||
console.log("IP: ", s);
|
|
||||||
document.getElementById("ip").value=s.ip_address;
|
|
||||||
document.getElementById("netmask").value=s.ip_netmask;
|
|
||||||
document.getElementById("gw").value=s.ip_gateway;
|
|
||||||
clearInterval(systemInterval);
|
|
||||||
// Fetch and populate the config textbox
|
|
||||||
fetchConfig().then((configText) => {
|
|
||||||
let fullConfig = configText;
|
|
||||||
// Fetch and append cmd_log
|
|
||||||
//return fetchCmdLog().then((cmdLogText) => {
|
|
||||||
// if (cmdLogText) {
|
|
||||||
// fullConfig = fullConfig + cmdLogText;
|
|
||||||
// }
|
|
||||||
document.getElementById("config_display").value = fullConfig;
|
|
||||||
});
|
|
||||||
};
|
|
||||||
}
|
|
||||||
xhttp.open("GET", `/information.json`, true);
|
|
||||||
xhttp.send();
|
|
||||||
}
|
|
||||||
|
|
||||||
function resetSwitch() {
|
|
||||||
if (!confirm('Are you sure you want to reset the switch?')) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
fetch('/reset', { method: 'GET' }).catch(() => {});
|
|
||||||
setTimeout(() => {
|
|
||||||
alert('Switch is resetting. Please wait and refresh the page.');
|
|
||||||
}, 3000);
|
|
||||||
}
|
|
||||||
|
|
||||||
window.addEventListener("load", function() {
|
|
||||||
systemInterval = setInterval(fetchIP, 1000);
|
|
||||||
});
|
|
||||||
@@ -1,19 +0,0 @@
|
|||||||
<!DOCTYPE html>
|
|
||||||
<html>
|
|
||||||
<head>
|
|
||||||
<link rel="stylesheet" href="style.css">
|
|
||||||
<title>Firmware update</title>
|
|
||||||
</head>
|
|
||||||
<body>
|
|
||||||
<nav id="sidebar"></nav>
|
|
||||||
<div style="margin-left:16%;padding:1px 16px;height:1000px;width:40%;">
|
|
||||||
<h1>Firmware Update</h1>
|
|
||||||
<form enctype="multipart/form-data" action="/upload" method="POST">
|
|
||||||
<input type="hidden" name="MAX_FILE_SIZE" value="1000000" />
|
|
||||||
Choose a firmware update file to upload: <br/> <br/>
|
|
||||||
<input name="uploadedfile" type="file" accept=".bin" /><br />
|
|
||||||
<input style="margin-top:3em" type="submit" value="Upload File" />
|
|
||||||
</form>
|
|
||||||
<script src="/navigation.js"></script>
|
|
||||||
</body>
|
|
||||||
</html>
|
|
||||||
@@ -1,58 +0,0 @@
|
|||||||
<!DOCTYPE html>
|
|
||||||
<html>
|
|
||||||
<script src="/main.js"></script>
|
|
||||||
<link rel="stylesheet" href="style.css">
|
|
||||||
<title>FreeSwitchOS VLAN Configuration</title>
|
|
||||||
</head>
|
|
||||||
<body>
|
|
||||||
<nav id="sidebar"></nav>
|
|
||||||
<div style="margin-left:16%;padding:1px 16px;height:1000px;">
|
|
||||||
<div id="ports"></div>
|
|
||||||
<h1>VLAN Configuration</h1>
|
|
||||||
<form id="vform" action="/vlan.html">
|
|
||||||
<div>
|
|
||||||
<label for="vlanSelect">VLAN auswählen:</label>
|
|
||||||
<select id="vlanSelect" style="margin: 0 0 0 8px">
|
|
||||||
<option value="" disabled selected>— VLAN wählen —</option>
|
|
||||||
</select>
|
|
||||||
</div>
|
|
||||||
<br/>
|
|
||||||
<div>
|
|
||||||
<label for="vid">VLAN ID:</label>
|
|
||||||
<input type="number" min="1" max="4094" id="vid" name="vid">
|
|
||||||
<button type="button" style="margin: 0 0 0 24px" onclick="fetchVLAN();">Get Configuration</button>
|
|
||||||
</div>
|
|
||||||
<br/><br/>
|
|
||||||
<label for="vname">VLAN Name:</label>
|
|
||||||
<input type="text" id="vname" name="vname"><br><br>
|
|
||||||
<br/>
|
|
||||||
<h2>Tagged Ports</h2>
|
|
||||||
<div id="tPorts"><button type="button" style="transform: translateY(-100%);margin: 0 50px 0 0" onclick="utClicked(true);">Select all</button></div>
|
|
||||||
<h2>Untagged Ports</h2>
|
|
||||||
<div id="uPorts"><button type="button" style="transform: translateY(-100%); margin: 0 50px 0 0" onclick="utClicked(false);">Select all</button> </div>
|
|
||||||
<h2>Use as default VLAN for incoming traffic (PVID)</h2>
|
|
||||||
<div id="pPorts"><button type="button" style="transform: translateY(-100%); margin: 0 50px 0 0" onclick="pvClicked(true);">Select all</button> </div>
|
|
||||||
<script src="/vlan.js"></script>
|
|
||||||
<br/> <input style="width:40%;" class="action" id="vlan_sub" onclick="vlanSub();" type="button" value="Update / Create">
|
|
||||||
<script src="/vlan_sub.js"></script>
|
|
||||||
</form>
|
|
||||||
<h2>Configured VLANs</h2>
|
|
||||||
<table id="vlanTable" style="width:90%">
|
|
||||||
<thead>
|
|
||||||
<tr>
|
|
||||||
<th>VLAN</th>
|
|
||||||
<th>Name</th>
|
|
||||||
<th>Member Ports</th>
|
|
||||||
<th>Tagged Ports</th>
|
|
||||||
<th>Untagged Ports</th>
|
|
||||||
<th>PVID Ports</th>
|
|
||||||
<th>Delete</th>
|
|
||||||
</tr>
|
|
||||||
</thead>
|
|
||||||
<tbody id="vlanTableBody">
|
|
||||||
</tbody>
|
|
||||||
</table>
|
|
||||||
</div>
|
|
||||||
<script src="/navigation.js"></script>
|
|
||||||
</body>
|
|
||||||
</html>
|
|
||||||
@@ -1,211 +0,0 @@
|
|||||||
var vlanInterval = Number();
|
|
||||||
|
|
||||||
function vlanForm() {
|
|
||||||
if (!numPorts)
|
|
||||||
return;
|
|
||||||
var t = document.getElementById('tPorts');
|
|
||||||
var u = document.getElementById('uPorts');
|
|
||||||
var p = document.getElementById('pPorts');
|
|
||||||
for (let i = 1; i <= numPorts; i++) {
|
|
||||||
const d = document.createElement("div");
|
|
||||||
d.classList.add("cbgroup");
|
|
||||||
const l = document.createElement("label");
|
|
||||||
l.innerHTML = "" + i;
|
|
||||||
l.classList.add("cbgroup");
|
|
||||||
const inp = document.createElement("input");
|
|
||||||
inp.type = "checkbox"; inp.setAttribute("class","psel");
|
|
||||||
inp.id = "tport" + i;
|
|
||||||
inp.setAttribute('onclick', `setC("u", ${i}, false);`);
|
|
||||||
const o = document.createElement("img");
|
|
||||||
if (pIsSFP[i - 1]) {
|
|
||||||
o.src = "sfp.svg"; o.width ="60"; o.height ="60";
|
|
||||||
} else {
|
|
||||||
o.src = "port.svg"; o.width = "40"; o.height = "40";
|
|
||||||
}
|
|
||||||
l.appendChild(inp); l.appendChild(o);
|
|
||||||
d.appendChild(l)
|
|
||||||
t.appendChild(d);
|
|
||||||
var d2=d.cloneNode(true);
|
|
||||||
d2.children[0].children[0].id = "uport" + i;
|
|
||||||
d2.children[0].children[0].setAttribute('onclick', `setC("t", ${i}, false);`);
|
|
||||||
u.appendChild(d2);
|
|
||||||
var d3=d.cloneNode(true);
|
|
||||||
d3.children[0].children[0].id = "pport" + i;
|
|
||||||
d3.children[0].children[0].removeAttribute('onclick');
|
|
||||||
p.appendChild(d3);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
function setC(t, p, c){
|
|
||||||
document.getElementById(t+'port'+p).checked=c;
|
|
||||||
}
|
|
||||||
|
|
||||||
function utClicked(t){
|
|
||||||
for (let i = 1; i <= numPorts; i++) {
|
|
||||||
setC('t', i, t); setC('u', i, !t);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
function pvClicked(p){
|
|
||||||
for (let i = 1; i <= numPorts; i++) {
|
|
||||||
setC('p', i, p);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
function fetchVLAN() {
|
|
||||||
var xhttp = new XMLHttpRequest();
|
|
||||||
xhttp.onreadystatechange = function() {
|
|
||||||
if (this.readyState == 4 && this.status == 200) {
|
|
||||||
const s = JSON.parse(xhttp.responseText);
|
|
||||||
console.log("VLAN: ", JSON.stringify(s));
|
|
||||||
m = parseInt(s.members, 16);
|
|
||||||
document.getElementById('vname').value = s.name;
|
|
||||||
var members = m & 0x3FF;
|
|
||||||
var untag = (m >> 10) & 0x3FF;
|
|
||||||
var pvid = parseInt(s.pvid, 16);
|
|
||||||
console.log("PVID: ", pvid);
|
|
||||||
for (let p = 1; p <= numPorts; p++) {
|
|
||||||
var bit = physToLogPort[p-1];
|
|
||||||
var isMember = (members >> bit) & 1;
|
|
||||||
var isUntag = (untag >> bit) & 1;
|
|
||||||
setC('t', p, isMember && !isUntag);
|
|
||||||
setC('u', p, isMember && isUntag);
|
|
||||||
setC('p', p, (pvid >> bit) & 1);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
};
|
|
||||||
var v=document.getElementById('vid').value
|
|
||||||
if (!v) {
|
|
||||||
alert("Set VLAN ID first");
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
xhttp.open("GET", `/vlan.json?vid=${v}`, true);
|
|
||||||
sendXHTTP(xhttp);
|
|
||||||
}
|
|
||||||
|
|
||||||
function portsToRange(mask, nPorts) {
|
|
||||||
var parts = [];
|
|
||||||
var start = -1, prev = -1;
|
|
||||||
for (var p = 1; p <= nPorts; p++) {
|
|
||||||
var bit = physToLogPort[p - 1];
|
|
||||||
if ((mask >> bit) & 1) {
|
|
||||||
if (start < 0) start = p;
|
|
||||||
prev = p;
|
|
||||||
} else {
|
|
||||||
if (start >= 0) {
|
|
||||||
parts.push(start === prev ? String(start) : start + '-' + prev);
|
|
||||||
start = -1; prev = -1;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if (start >= 0)
|
|
||||||
parts.push(start === prev ? String(start) : start + '-' + prev);
|
|
||||||
return parts.length ? parts.join(',') : '-';
|
|
||||||
}
|
|
||||||
|
|
||||||
async function loadVlanTable() {
|
|
||||||
var tbody = document.getElementById('vlanTableBody');
|
|
||||||
if (!tbody) return;
|
|
||||||
tbody.innerHTML = '';
|
|
||||||
var resp;
|
|
||||||
try { resp = await fetch('/vlanlist'); } catch(e) { return; }
|
|
||||||
if (!resp.ok) return;
|
|
||||||
var vlans = await resp.json();
|
|
||||||
for (var i = 0; i < vlans.length; i++) {
|
|
||||||
var v = vlans[i];
|
|
||||||
var vresp;
|
|
||||||
try { vresp = await fetch('/vlan.json?vid=' + v.id); } catch(e) { continue; }
|
|
||||||
if (!vresp.ok) continue;
|
|
||||||
var s = await vresp.json();
|
|
||||||
var m = parseInt(s.members, 16);
|
|
||||||
var members = m & 0x3FF;
|
|
||||||
var untag = ((m >> 10) & 0x3FF) & members;
|
|
||||||
var tagged = members & ~untag;
|
|
||||||
var pvid = parseInt(s.pvid, 16) & 0x3FF;
|
|
||||||
var tr = document.createElement('tr');
|
|
||||||
var td, a, btn;
|
|
||||||
td = document.createElement('td');
|
|
||||||
a = document.createElement('a');
|
|
||||||
a.href = '#';
|
|
||||||
a.textContent = v.id;
|
|
||||||
(function(vid) {
|
|
||||||
a.onclick = function(e) {
|
|
||||||
e.preventDefault();
|
|
||||||
document.getElementById('vid').value = vid;
|
|
||||||
fetchVLAN();
|
|
||||||
};
|
|
||||||
})(v.id);
|
|
||||||
td.appendChild(a); tr.appendChild(td);
|
|
||||||
td = document.createElement('td');
|
|
||||||
td.textContent = v.name || ''; tr.appendChild(td);
|
|
||||||
td = document.createElement('td');
|
|
||||||
td.textContent = portsToRange(members, numPorts); tr.appendChild(td);
|
|
||||||
td = document.createElement('td');
|
|
||||||
td.textContent = portsToRange(tagged, numPorts); tr.appendChild(td);
|
|
||||||
td = document.createElement('td');
|
|
||||||
td.textContent = portsToRange(untag, numPorts); tr.appendChild(td);
|
|
||||||
td = document.createElement('td');
|
|
||||||
td.textContent = portsToRange(pvid, numPorts); tr.appendChild(td);
|
|
||||||
td = document.createElement('td');
|
|
||||||
if (v.id !== 1) {
|
|
||||||
btn = document.createElement('button');
|
|
||||||
btn.textContent = '✕';
|
|
||||||
(function(vid) {
|
|
||||||
btn.onclick = function() { deleteVlan(vid); };
|
|
||||||
})(v.id);
|
|
||||||
td.appendChild(btn);
|
|
||||||
}
|
|
||||||
tr.appendChild(td);
|
|
||||||
tbody.appendChild(tr);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
function deleteVlan(id) {
|
|
||||||
if (!confirm('Delete VLAN ' + id + '?')) return;
|
|
||||||
fetch('/cmd', { method: 'POST', body: 'vlan ' + id + ' d' })
|
|
||||||
.then(function() { refreshVlanViews(); })
|
|
||||||
.catch(function(err) { console.error('Delete failed:', err); });
|
|
||||||
}
|
|
||||||
|
|
||||||
function refreshVlanViews() {
|
|
||||||
loadVlanList();
|
|
||||||
loadVlanTable();
|
|
||||||
}
|
|
||||||
|
|
||||||
function loadVlanList() {
|
|
||||||
var xhttp = new XMLHttpRequest();
|
|
||||||
xhttp.onreadystatechange = function() {
|
|
||||||
if (this.readyState !== 4) return;
|
|
||||||
var sel = document.getElementById('vlanSelect');
|
|
||||||
if (this.status !== 200) {
|
|
||||||
sel.style.display = 'none';
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
var vlans = JSON.parse(this.responseText);
|
|
||||||
if (!vlans.length) {
|
|
||||||
sel.style.display = 'none';
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
sel.options.length = 1;
|
|
||||||
for (var i = 0; i < vlans.length; i++) {
|
|
||||||
var opt = document.createElement('option');
|
|
||||||
opt.value = vlans[i].id;
|
|
||||||
opt.text = vlans[i].name ? vlans[i].id + ' — ' + vlans[i].name : String(vlans[i].id);
|
|
||||||
sel.appendChild(opt);
|
|
||||||
}
|
|
||||||
};
|
|
||||||
xhttp.open('GET', '/vlanlist', true);
|
|
||||||
sendXHTTP(xhttp);
|
|
||||||
}
|
|
||||||
|
|
||||||
window.addEventListener("load", function() {
|
|
||||||
update( () => {
|
|
||||||
vlanForm();
|
|
||||||
refreshVlanViews();
|
|
||||||
document.getElementById('vlanSelect').onchange = function() {
|
|
||||||
document.getElementById('vid').value = this.value;
|
|
||||||
fetchVLAN();
|
|
||||||
};
|
|
||||||
const interval = setInterval(update, 2000);
|
|
||||||
});
|
|
||||||
});
|
|
||||||
@@ -1,36 +0,0 @@
|
|||||||
async function vlanSub() {
|
|
||||||
var commands = [];
|
|
||||||
var cmd = "vlan ";
|
|
||||||
var v=document.getElementById('vid').value
|
|
||||||
if (!v) {
|
|
||||||
alert("Set VLAN ID first");
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
cmd = cmd + v;
|
|
||||||
if (document.getElementById('vname').value)
|
|
||||||
cmd = cmd + ' ' + document.getElementById('vname').value;
|
|
||||||
for (let i = 1; i <= numPorts; i++) {
|
|
||||||
if (document.getElementById('tport' + i).checked)
|
|
||||||
cmd = cmd + ` ${i}t`;
|
|
||||||
else if (document.getElementById('uport' + i).checked)
|
|
||||||
cmd = cmd + ` ${i}`;
|
|
||||||
}
|
|
||||||
commands.push(cmd);
|
|
||||||
for (let i = 1; i <= numPorts; i++) {
|
|
||||||
if (document.getElementById('pport' + i).checked)
|
|
||||||
commands.push(`pvid ${i} ${v}`);
|
|
||||||
}
|
|
||||||
try {
|
|
||||||
for (let c of commands) {
|
|
||||||
const response = await fetch('/cmd', {
|
|
||||||
method: 'POST',
|
|
||||||
body: c
|
|
||||||
});
|
|
||||||
console.log('Completed!', response);
|
|
||||||
}
|
|
||||||
refreshVlanViews();
|
|
||||||
} catch(err) {
|
|
||||||
console.error(`Error: ${err}`);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
@@ -20,6 +20,8 @@
|
|||||||
#pragma constseg BANK1
|
#pragma constseg BANK1
|
||||||
|
|
||||||
extern volatile __xdata uint8_t sfr_data[4];
|
extern volatile __xdata uint8_t sfr_data[4];
|
||||||
|
extern volatile __xdata uint32_t ticks;
|
||||||
|
extern __xdata uint8_t cmd_capture; /* owned by rtlplayground.c, see write_char_no_syslog() */
|
||||||
extern __code uint8_t * __code hex;
|
extern __code uint8_t * __code hex;
|
||||||
extern __code struct f_data f_data[];
|
extern __code struct f_data f_data[];
|
||||||
extern __code char * __code mime_strings[];
|
extern __code char * __code mime_strings[];
|
||||||
@@ -41,8 +43,19 @@ __xdata uint32_t cont_addr;
|
|||||||
|
|
||||||
// HTTP header properties
|
// HTTP header properties
|
||||||
__xdata uint8_t boundary[72];
|
__xdata uint8_t boundary[72];
|
||||||
__xdata uint8_t *content_type = 0;
|
|
||||||
__xdata uint8_t *session = 0;
|
// a client may split the request anywhere, including inside a boundary or a
|
||||||
|
// part header, so a configuration upload is parsed only once it is complete;
|
||||||
|
// sized for a full config sector plus the multipart framing around it
|
||||||
|
#define CONFIG_UPLOAD_BUF (CONFIG_LEN + 384)
|
||||||
|
__xdata uint8_t config_upload;
|
||||||
|
__xdata uint8_t config_buf[CONFIG_UPLOAD_BUF];
|
||||||
|
// bytes buffered in config_buf so far (config body, or a firmware part
|
||||||
|
// header); accumulates across TCP segments
|
||||||
|
__xdata uint16_t pre_acc;
|
||||||
|
__xdata uint8_t * __xdata content_type = 0;
|
||||||
|
__xdata uint8_t * __xdata session = 0;
|
||||||
|
__xdata uint16_t content_length;
|
||||||
|
|
||||||
// Global variables holding POST state
|
// Global variables holding POST state
|
||||||
__xdata uint16_t bindex; // Current index into the boundary
|
__xdata uint16_t bindex; // Current index into the boundary
|
||||||
@@ -50,11 +63,20 @@ __xdata uint8_t verify_crc;
|
|||||||
__xdata uint32_t max_upload;
|
__xdata uint32_t max_upload;
|
||||||
__xdata uint16_t short_parsed;
|
__xdata uint16_t short_parsed;
|
||||||
|
|
||||||
|
#define POSTBODY_CMD 1
|
||||||
|
#define POSTBODY_LOGIN 2
|
||||||
|
#define POSTBODY_TIMEOUT (5 * SYS_TICK_HZ)
|
||||||
|
__xdata uint8_t postbody_endpoint;
|
||||||
|
__xdata uint16_t postbody_start;
|
||||||
|
|
||||||
__xdata char passwd[21];
|
__xdata char passwd[21];
|
||||||
|
// Set when a verified firmware upload awaits its response ACK, after
|
||||||
|
// which the chip resets to apply the staged image
|
||||||
|
__xdata uint8_t fw_reset_pending;
|
||||||
__xdata char session_id[SESSION_ID_LENGTH + 1];
|
__xdata char session_id[SESSION_ID_LENGTH + 1];
|
||||||
__xdata uint8_t authenticated;
|
__xdata uint8_t authenticated;
|
||||||
__xdata uint32_t now;
|
__xdata uint32_t now;
|
||||||
__xdata uint8_t *timeptr;
|
__xdata uint8_t * __xdata timeptr;
|
||||||
__xdata uint32_t last_session_use;
|
__xdata uint32_t last_session_use;
|
||||||
|
|
||||||
#define TSTATE_NONE 0
|
#define TSTATE_NONE 0
|
||||||
@@ -63,10 +85,11 @@ __xdata uint32_t last_session_use;
|
|||||||
#define TSTATE_CLOSED 3
|
#define TSTATE_CLOSED 3
|
||||||
#define TSTATE_POST 4
|
#define TSTATE_POST 4
|
||||||
#define TSTATE_MULTIPART 5
|
#define TSTATE_MULTIPART 5
|
||||||
|
#define TSTATE_POSTBODY 6
|
||||||
|
|
||||||
extern __xdata uint16_t crc_value;
|
extern __xdata uint16_t crc_value;
|
||||||
__xdata uint16_t crc_final;
|
__xdata uint16_t crc_final;
|
||||||
void crc16(__xdata uint8_t *v) __naked;
|
void crc16_bank1(__xdata uint8_t *v) __naked;
|
||||||
|
|
||||||
|
|
||||||
inline uint8_t is_separator(uint8_t c)
|
inline uint8_t is_separator(uint8_t c)
|
||||||
@@ -77,10 +100,13 @@ inline uint8_t is_separator(uint8_t c)
|
|||||||
|
|
||||||
void httpd_init(void) __banked
|
void httpd_init(void) __banked
|
||||||
{
|
{
|
||||||
__xdata struct httpd_state * __xdata s = &(uip_conn->appstate);
|
config_upload = 0; // xdata is not zeroed by the startup code
|
||||||
// Start listening to port 80
|
// Start listening to port 80
|
||||||
uip_listen(HTONS(80));
|
uip_listen(HTONS(80));
|
||||||
s->tstate = TSTATE_CLOSED;
|
// Not through uip_conn: it only points at a connection while uIP is
|
||||||
|
// handling one, and nothing has set it yet at init time.
|
||||||
|
uip_conns[0].appstate.tstate = TSTATE_CLOSED;
|
||||||
|
fw_reset_pending = 0; // xdata is not zeroed by the startup code
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
@@ -101,21 +127,6 @@ uint8_t find_entry(__xdata uint8_t *e)
|
|||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
char strcmp(__xdata uint8_t *c, __code uint8_t * __xdata d)
|
|
||||||
{
|
|
||||||
uint8_t i = 0;
|
|
||||||
|
|
||||||
while (d[i] && (d[i] == c[i]))
|
|
||||||
i++;
|
|
||||||
|
|
||||||
if (c[i] < d[i])
|
|
||||||
return -1;
|
|
||||||
else if (c[i] > d[i])
|
|
||||||
return 1;
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
bool is_word(__xdata uint8_t *xdata_str_p, __code uint8_t * __xdata code_str_p)
|
bool is_word(__xdata uint8_t *xdata_str_p, __code uint8_t * __xdata code_str_p)
|
||||||
{
|
{
|
||||||
uint8_t u, c;
|
uint8_t u, c;
|
||||||
@@ -124,8 +135,8 @@ bool is_word(__xdata uint8_t *xdata_str_p, __code uint8_t * __xdata code_str_p)
|
|||||||
u = *xdata_str_p++;
|
u = *xdata_str_p++;
|
||||||
c = *code_str_p++;
|
c = *code_str_p++;
|
||||||
|
|
||||||
if (c == '\0') {
|
if (c == NUL) {
|
||||||
if (u != '\0' && u != ' ' && u != '\t' && u != ':' && u != '?' && u != '=' && u != '\n' && u != '\r')
|
if (u != NUL && u != ' ' && u != '\t' && u != ':' && u != '?' && u != '=' && u != '\n' && u != '\r')
|
||||||
return false;
|
return false;
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
@@ -137,6 +148,24 @@ bool is_word(__xdata uint8_t *xdata_str_p, __code uint8_t * __xdata code_str_p)
|
|||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/* name must be lower-case, starting with the '\n' of the previous line's end */
|
||||||
|
__xdata uint8_t *header_value(__xdata uint8_t *p, __code uint8_t *name)
|
||||||
|
{
|
||||||
|
uint8_t u, c;
|
||||||
|
|
||||||
|
while ((c = *name++)) {
|
||||||
|
u = *p++;
|
||||||
|
if (u >= 'A' && u <= 'Z')
|
||||||
|
u += 'a' - 'A';
|
||||||
|
if (u != c)
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
while (*p == ' ' || *p == '\t')
|
||||||
|
p++;
|
||||||
|
return p;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
bool is_url_word_x(__xdata uint8_t *uri_str_p, __xdata uint8_t *src_str_p)
|
bool is_url_word_x(__xdata uint8_t *uri_str_p, __xdata uint8_t *src_str_p)
|
||||||
{
|
{
|
||||||
uint8_t u, s;
|
uint8_t u, s;
|
||||||
@@ -145,8 +174,8 @@ bool is_url_word_x(__xdata uint8_t *uri_str_p, __xdata uint8_t *src_str_p)
|
|||||||
u = *uri_str_p++;
|
u = *uri_str_p++;
|
||||||
s = *src_str_p++;
|
s = *src_str_p++;
|
||||||
|
|
||||||
if (s == '\0') {
|
if (s == NUL) {
|
||||||
if (u != '\0' && u != ' ' && u != '\t' && u != ':' && u != '?' && u != '=' && u != '\n' && u != '\r')
|
if (u != NUL && u != ' ' && u != '\t' && u != ':' && u != '?' && u != '=' && u != '\n' && u != '\r')
|
||||||
return false;
|
return false;
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
@@ -180,7 +209,7 @@ bool is_url_word_x(__xdata uint8_t *uri_str_p, __xdata uint8_t *src_str_p)
|
|||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
bool is_word_x(__xdata uint8_t *lhs_str_p, __xdata uint8_t *rhs_str_p)
|
bool is_word_x(__xdata uint8_t * lhs_str_p, __xdata uint8_t * rhs_str_p)
|
||||||
{
|
{
|
||||||
uint8_t u, c;
|
uint8_t u, c;
|
||||||
|
|
||||||
@@ -188,8 +217,9 @@ bool is_word_x(__xdata uint8_t *lhs_str_p, __xdata uint8_t *rhs_str_p)
|
|||||||
u = *lhs_str_p++;
|
u = *lhs_str_p++;
|
||||||
c = *rhs_str_p++;
|
c = *rhs_str_p++;
|
||||||
|
|
||||||
if (c == '\0') {
|
if (c == NUL) {
|
||||||
if (u != '\0' && u != ' ' && u != '\t' && u != ':' && u != '?' && u != '=' && u != '\n' && u != '\r')
|
/* ';' separates cookies in a Cookie header, so it ends a value too. */
|
||||||
|
if (u != NUL && u != ' ' && u != '\t' && u != ':' && u != '?' && u != '=' && u != '\n' && u != '\r' && u != ';')
|
||||||
return false;
|
return false;
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
@@ -211,7 +241,10 @@ uint8_t parse_short(__xdata uint8_t *p)
|
|||||||
c = *p++ - '0';
|
c = *p++ - '0';
|
||||||
if (c > 9) { break; }
|
if (c > 9) { break; }
|
||||||
err = 0;
|
err = 0;
|
||||||
short_parsed = (short_parsed * 10) + c;
|
if (short_parsed > 6552)
|
||||||
|
short_parsed = 0xffff;
|
||||||
|
else
|
||||||
|
short_parsed = (short_parsed * 10) + c;
|
||||||
}
|
}
|
||||||
return err;
|
return err;
|
||||||
}
|
}
|
||||||
@@ -219,62 +252,82 @@ uint8_t parse_short(__xdata uint8_t *p)
|
|||||||
|
|
||||||
void send_not_found(void)
|
void send_not_found(void)
|
||||||
{
|
{
|
||||||
slen = strtox(outbuf, "HTTP/1.1 404 Not found\r\nContent-Type: text/html\r\n\r\n" \
|
slen = strtox(outbuf, "HTTP/1.1 404 Not found\r\nConnection: close\r\nContent-Type: text/html\r\n\r\n" \
|
||||||
"<!DOCTYPE HTML PUBLIC>\n<title>404 Not Found</title>\n<h1>Not Found</h1>\n");
|
"<!DOCTYPE HTML PUBLIC>\n<title>404 Not Found</title>\n<h1>Not Found</h1>\n");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
void send_bad_request(void)
|
void send_bad_request(void)
|
||||||
{
|
{
|
||||||
slen = strtox(outbuf, "HTTP/1.1 400 Bad Request\r\nContent-Type: text/html\r\n\r\n" \
|
slen = strtox(outbuf, "HTTP/1.1 400 Bad Request\r\nConnection: close\r\nContent-Type: text/html\r\n\r\n" \
|
||||||
"<!DOCTYPE HTML PUBLIC>\n<title>400 Bad Request</title>\n<h1>Bad Request</h1>\n");
|
"<!DOCTYPE HTML PUBLIC>\n<title>400 Bad Request</title>\n<h1>Bad Request</h1>\n");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
void send_to_login(void)
|
void send_to_login(void)
|
||||||
{
|
{
|
||||||
slen = strtox(outbuf, "HTTP/1.1 302 Found\r\n" \
|
slen = strtox(outbuf, "HTTP/1.1 302 Found\r\nConnection: close\r\n" \
|
||||||
"Location: login.html\r\n\r\n");
|
"Location: login.html\r\n\r\n");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
void send_unauthorized(void)
|
void send_unauthorized(void)
|
||||||
{
|
{
|
||||||
slen = strtox(outbuf, "HTTP/1.1 401 Unauthorized\r\n\r\n");
|
slen = strtox(outbuf, "HTTP/1.1 401 Unauthorized\r\nConnection: close\r\n\r\n");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
__xdata uint8_t *skip_boundary(__xdata uint8_t *p)
|
void send_length_required(void)
|
||||||
{
|
{
|
||||||
while (*p) {
|
slen = strtox(outbuf, "HTTP/1.1 411 Length Required\r\nConnection: close\r\n\r\n");
|
||||||
if (is_word_x(p, boundary))
|
|
||||||
return p + strlen_x(boundary);
|
|
||||||
p++;
|
|
||||||
}
|
|
||||||
return p;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
__xdata uint8_t *scan_header(__xdata uint8_t *p)
|
void send_ok(void)
|
||||||
{
|
{
|
||||||
|
slen = strtox(outbuf, "HTTP/1.1 200 OK\r\nConnection: close\r\n\r\n");
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
__xdata uint8_t *scan_header(__xdata uint8_t * __xdata p)
|
||||||
|
{
|
||||||
|
__xdata uint8_t *v;
|
||||||
|
|
||||||
content_type = 0;
|
content_type = 0;
|
||||||
|
content_length = 0;
|
||||||
session = 0;
|
session = 0;
|
||||||
authenticated = 0;
|
authenticated = 0;
|
||||||
|
|
||||||
while (*p != '\r' || *(p + 1) != '\n' || *(p + 2) != '\r' || *(p + 3) != '\n') {
|
while (!strstart(p, "\r\n\r\n")) {
|
||||||
dbg_char(*p);
|
dbg_char(*p);
|
||||||
if (!*p++)
|
if (!*p)
|
||||||
break;
|
break;
|
||||||
if (is_word(p, "\nContent-Type:"))
|
p++;
|
||||||
content_type = p + 15;
|
if ((v = header_value(p, "\ncontent-type:")))
|
||||||
else if (is_word(p, "\nCookie:"))
|
content_type = v;
|
||||||
session = p + 17;
|
else if ((v = header_value(p, "\ncontent-length:"))) {
|
||||||
|
parse_short(v);
|
||||||
|
content_length = short_parsed;
|
||||||
|
} else if ((v = header_value(p, "\ncookie:"))) {
|
||||||
|
/* Scan for the "session" key: the header may hold several
|
||||||
|
* cookies in any order. Match "session" not "session=" -
|
||||||
|
* is_word() requires a separator after the match and '=' is
|
||||||
|
* one, so this also rejects a longer key like "sessionx". */
|
||||||
|
while (*v && *v != '\r' && *v != '\n') {
|
||||||
|
if (is_word(v, "session")) {
|
||||||
|
session = v + 8; /* past "session=" */
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
v++;
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
if (content_type && is_word(content_type, "multipart/form-data; boundary")) {
|
if (content_type && is_word(content_type, "multipart/form-data; boundary")) {
|
||||||
dbg_string("\nFound multipart\n");
|
dbg_string("\nFound multipart\n");
|
||||||
content_type += 30;
|
content_type += 30;
|
||||||
uint8_t i = 0;
|
uint8_t i = 0;
|
||||||
while (content_type[i] != '\r' && content_type[i] != '\n') {
|
while (i < (sizeof(boundary) - 5) &&
|
||||||
|
content_type[i] != '\r' && content_type[i] != '\n') {
|
||||||
boundary[i + 4] = content_type[i];
|
boundary[i + 4] = content_type[i];
|
||||||
i++;
|
i++;
|
||||||
}
|
}
|
||||||
@@ -301,10 +354,12 @@ __xdata uint8_t *scan_header(__xdata uint8_t *p)
|
|||||||
return p;
|
return p;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
void gen_random_bytes(__xdata uint8_t *b, uint8_t bytes)
|
* Generate random HEX-chars at the buffer location.
|
||||||
|
*/
|
||||||
|
void gen_random_hex_chars(__xdata uint8_t * b, __xdata uint8_t bytes)
|
||||||
{
|
{
|
||||||
__xdata uint8_t i = 0;
|
uint8_t i = 0;
|
||||||
while (bytes) {
|
while (bytes) {
|
||||||
if (!i)
|
if (!i)
|
||||||
get_random_32();
|
get_random_32();
|
||||||
@@ -316,21 +371,110 @@ void gen_random_bytes(__xdata uint8_t *b, uint8_t bytes)
|
|||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/* 0: body incomplete, 1: configuration stored, 2: malformed */
|
||||||
|
static uint8_t config_take(void)
|
||||||
|
{
|
||||||
|
// #386: needs static, otherwise it still lands in SRAM/DSEG
|
||||||
|
static __xdata uint16_t cfg_pos, cfg_hdr, cfg_body, cfg_end, cfg_last;
|
||||||
|
__xdata uint8_t cfg_bl;
|
||||||
|
|
||||||
|
cfg_bl = strlen_x(boundary);
|
||||||
|
|
||||||
|
// the body is complete once the closing boundary has arrived
|
||||||
|
cfg_last = 0;
|
||||||
|
while (1) {
|
||||||
|
if (cfg_last + cfg_bl + 1 >= pre_acc)
|
||||||
|
return 0;
|
||||||
|
if (strstart_x(&config_buf[cfg_last], boundary)
|
||||||
|
&& strstart(&config_buf[cfg_last + cfg_bl], "--"))
|
||||||
|
break;
|
||||||
|
cfg_last++;
|
||||||
|
}
|
||||||
|
|
||||||
|
// every part lies ahead of the closing boundary, so it bounds the walk
|
||||||
|
cfg_pos = 0;
|
||||||
|
while (cfg_pos < cfg_last) {
|
||||||
|
if (!strstart_x(&config_buf[cfg_pos], boundary)) {
|
||||||
|
cfg_pos++;
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
cfg_hdr = cfg_pos + cfg_bl;
|
||||||
|
cfg_body = cfg_hdr;
|
||||||
|
while (1) {
|
||||||
|
if (cfg_body + 3 >= cfg_last)
|
||||||
|
return 2;
|
||||||
|
if (strstart(&config_buf[cfg_body], "\r\n\r\n"))
|
||||||
|
break;
|
||||||
|
cfg_body++;
|
||||||
|
}
|
||||||
|
cfg_end = cfg_body;
|
||||||
|
cfg_body += 4;
|
||||||
|
// reaching cfg_last is a match: the last part ends at the closing boundary
|
||||||
|
while (cfg_end < cfg_last && !strstart_x(&config_buf[cfg_end], boundary))
|
||||||
|
cfg_end++;
|
||||||
|
while (cfg_hdr + 8 < cfg_body) {
|
||||||
|
// the part carrying a filename holds the configuration
|
||||||
|
if (strstart(&config_buf[cfg_hdr], "filename")) {
|
||||||
|
// the payload plus its terminator must fit the sector
|
||||||
|
if (cfg_end - cfg_body + 1 > CONFIG_LEN)
|
||||||
|
return 2;
|
||||||
|
config_buf[cfg_end] = 0;
|
||||||
|
flash_region.addr = CONFIG_START;
|
||||||
|
flash_sector_erase();
|
||||||
|
flash_region.addr = CONFIG_START;
|
||||||
|
flash_region.len = cfg_end - cfg_body + 1;
|
||||||
|
flash_write_bytes(config_buf + cfg_body);
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
cfg_hdr++;
|
||||||
|
}
|
||||||
|
cfg_pos = cfg_end;
|
||||||
|
}
|
||||||
|
return 2;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
// 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;
|
||||||
|
|
||||||
|
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)
|
||||||
|
return 0;
|
||||||
|
return pos + 4;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
// Source window for stream_upload(); filled by the caller before the call
|
||||||
|
__xdata struct {
|
||||||
|
__xdata uint8_t *p;
|
||||||
|
uint16_t bptr;
|
||||||
|
uint16_t plen;
|
||||||
|
} upload_settings;
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Reads post data from the http stream and writes it into flash memory
|
* Reads post data from the http stream and writes it into flash memory
|
||||||
* Input: the current position in the TCP buffer (uip_appdata)
|
* Input: upload_settings, set by the caller
|
||||||
* Returns 1: More data to read, 0: Upload complete, all parts reads
|
* Returns 1: More data to read, 0: Upload complete, all parts reads
|
||||||
*/
|
*/
|
||||||
uint8_t stream_upload(uint16_t bptr)
|
uint8_t stream_upload(void)
|
||||||
{
|
{
|
||||||
__xdata uint8_t *p = uip_appdata;
|
|
||||||
__xdata struct httpd_state * __xdata s = &(uip_conn->appstate);
|
__xdata struct httpd_state * __xdata s = &(uip_conn->appstate);
|
||||||
|
|
||||||
dbg_string("Stream_upload called: ");
|
dbg_string("Stream_upload called: ");
|
||||||
dbg_short(bptr); dbg_char('\n');
|
dbg_short(upload_settings.bptr); dbg_char('\n');
|
||||||
|
|
||||||
do {
|
do {
|
||||||
if (bptr >= uip_len) {
|
if (upload_settings.bptr >= upload_settings.plen) {
|
||||||
s->tstate = TSTATE_POST;
|
s->tstate = TSTATE_POST;
|
||||||
return 1;
|
return 1;
|
||||||
}
|
}
|
||||||
@@ -343,17 +487,23 @@ uint8_t stream_upload(uint16_t bptr)
|
|||||||
flash_write_bytes(flash_buf);
|
flash_write_bytes(flash_buf);
|
||||||
uptr += write_len;
|
uptr += write_len;
|
||||||
write_len = 0;
|
write_len = 0;
|
||||||
// TODO: This is a bit premature, what about a nice web-page saying the device will reset???
|
|
||||||
if (verify_crc) {
|
if (verify_crc) {
|
||||||
dbg_string("CRC16: "); dbg_short(crc_final); dbg_char('\n');
|
dbg_string("CRC16: "); dbg_short(crc_final); dbg_char('\n');
|
||||||
|
// Both bodies are 33 bytes; Content-Length lets the
|
||||||
|
// browser complete the response without waiting for
|
||||||
|
// the connection close (which a reset would swallow)
|
||||||
if (crc_final == 0xb001) {
|
if (crc_final == 0xb001) {
|
||||||
print_string("Checksum OK.\nUpload to flash done, will reset!\n");
|
print_string("Checksum OK.\nUpload to flash done, will reset!\n");
|
||||||
// close connection to avoid retries by browser
|
slen = strtox(outbuf, "HTTP/1.1 200 OK\r\nContent-Length: 33\r\n"
|
||||||
uip_close();
|
"Content-Type: text/plain\r\n\r\n"
|
||||||
reset_chip();
|
"OK: checksum verified, rebooting\n");
|
||||||
|
// Reset once the response is fully ACKed
|
||||||
|
fw_reset_pending = 1;
|
||||||
} else {
|
} else {
|
||||||
print_string("Checksum incorrect! Aborting.\n");
|
print_string("Checksum incorrect! Aborting.\n");
|
||||||
uip_close();
|
slen = strtox(outbuf, "HTTP/1.1 400 Bad Request\r\nContent-Length: 33\r\n"
|
||||||
|
"Content-Type: text/plain\r\n\r\n"
|
||||||
|
"NO: checksum failed, not applied\n");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
// Make sure there is a 0 at the end of the uploaded data
|
// Make sure there is a 0 at the end of the uploaded data
|
||||||
@@ -361,18 +511,15 @@ uint8_t stream_upload(uint16_t bptr)
|
|||||||
flash_region.addr = uptr;
|
flash_region.addr = uptr;
|
||||||
flash_region.len = 1;
|
flash_region.len = 1;
|
||||||
flash_write_bytes(flash_buf);
|
flash_write_bytes(flash_buf);
|
||||||
if (bptr >= uip_len)
|
if (upload_settings.bptr >= upload_settings.plen)
|
||||||
return 0;
|
return 0;
|
||||||
if(!verify_crc)
|
|
||||||
//ugly hack to signal connection finished after config upload.
|
|
||||||
uip_close();
|
|
||||||
return 1;
|
return 1;
|
||||||
}
|
}
|
||||||
if (p[bptr] == boundary[bindex]) {
|
if (upload_settings.p[upload_settings.bptr] == boundary[bindex]) {
|
||||||
if (!bindex)
|
if (!bindex)
|
||||||
crc_final = crc_value;
|
crc_final = crc_value;
|
||||||
crc16(p + bptr);
|
crc16_bank1(upload_settings.p + upload_settings.bptr);
|
||||||
bptr++;
|
upload_settings.bptr++;
|
||||||
bindex++;
|
bindex++;
|
||||||
} else {
|
} else {
|
||||||
if (bindex) {
|
if (bindex) {
|
||||||
@@ -380,8 +527,8 @@ uint8_t stream_upload(uint16_t bptr)
|
|||||||
write_len += bindex;
|
write_len += bindex;
|
||||||
bindex = 0;
|
bindex = 0;
|
||||||
}
|
}
|
||||||
crc16(p + bptr);
|
crc16_bank1(upload_settings.p + upload_settings.bptr);
|
||||||
flash_buf[write_len++] = p[bptr++];
|
flash_buf[write_len++] = upload_settings.p[upload_settings.bptr++];
|
||||||
if (write_len >= FLASH_PAGE_SIZE) {
|
if (write_len >= FLASH_PAGE_SIZE) {
|
||||||
dbg_string("len: "); dbg_short(write_len); dbg_char(' ');
|
dbg_string("len: "); dbg_short(write_len); dbg_char(' ');
|
||||||
dbg_string("CRC16: "); dbg_short(crc_value); dbg_char('\n');
|
dbg_string("CRC16: "); dbg_short(crc_value); dbg_char('\n');
|
||||||
@@ -406,12 +553,184 @@ uint8_t stream_upload(uint16_t bptr)
|
|||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
static void handle_config_fragment(__xdata uint8_t *p)
|
||||||
|
{
|
||||||
|
__xdata struct httpd_state * __xdata s = &(uip_conn->appstate);
|
||||||
|
__xdata uint16_t frag_len;
|
||||||
|
uint8_t taken;
|
||||||
|
|
||||||
|
frag_len = uip_len - (p - uip_appdata);
|
||||||
|
if (pre_acc + frag_len >= CONFIG_UPLOAD_BUF) {
|
||||||
|
print_string("Configuration too large, aborting.\n");
|
||||||
|
config_upload = 0;
|
||||||
|
s->tstate = TSTATE_NONE;
|
||||||
|
send_bad_request();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
memcpy(config_buf + pre_acc, p, frag_len);
|
||||||
|
pre_acc += frag_len;
|
||||||
|
taken = config_take();
|
||||||
|
if (!taken) {
|
||||||
|
s->tstate = TSTATE_MULTIPART;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
config_upload = 0;
|
||||||
|
s->tstate = TSTATE_NONE;
|
||||||
|
if (taken == 2) {
|
||||||
|
send_bad_request();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
send_ok();
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
static void handle_firmware_fragment(__xdata uint8_t *p)
|
||||||
|
{
|
||||||
|
__xdata struct httpd_state * __xdata s = &(uip_conn->appstate);
|
||||||
|
__xdata uint16_t frag_len, payload_start;
|
||||||
|
|
||||||
|
frag_len = uip_len - (p - uip_appdata);
|
||||||
|
if (pre_acc + frag_len >= CONFIG_UPLOAD_BUF) {
|
||||||
|
print_string("Firmware upload header too large, aborting.\n");
|
||||||
|
config_upload = 0;
|
||||||
|
s->tstate = TSTATE_NONE;
|
||||||
|
send_bad_request();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
memcpy(config_buf + pre_acc, p, frag_len);
|
||||||
|
pre_acc += frag_len;
|
||||||
|
payload_start = preamble_payload_start(pre_acc);
|
||||||
|
if (!payload_start) {
|
||||||
|
s->tstate = TSTATE_MULTIPART;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
dbg_string("Have content octets\n");
|
||||||
|
|
||||||
|
flash_init(0); // Re-initialize flash for non-DIO operation, otherwise flashing fails
|
||||||
|
set_sys_led_state(SYS_LED_FAST);
|
||||||
|
|
||||||
|
crc_value = 0;
|
||||||
|
bindex = 0;
|
||||||
|
write_len = 0;
|
||||||
|
// A verdict is only built once the upload part completes;
|
||||||
|
// clear any stale response so the completion check in the
|
||||||
|
// appcall POST branch cannot send leftovers
|
||||||
|
slen = 0;
|
||||||
|
upload_settings.p = config_buf;
|
||||||
|
upload_settings.bptr = payload_start;
|
||||||
|
upload_settings.plen = pre_acc;
|
||||||
|
stream_upload();
|
||||||
|
|
||||||
|
dbg_string("Done reading first fragment\n");
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
static void run_cmd_body(__xdata uint8_t *body)
|
||||||
|
{
|
||||||
|
uint16_t hdr_len = strtox(outbuf, HTTP_RESPONCE_TXT);
|
||||||
|
|
||||||
|
slen = hdr_len;
|
||||||
|
cmd_capture = 1;
|
||||||
|
execute_commands(body);
|
||||||
|
if (cmd_capture == 2)
|
||||||
|
slen += strtox(outbuf + slen, CMD_TRUNCATED);
|
||||||
|
cmd_capture = 0;
|
||||||
|
/* Commands that configure something print nothing at all. Saying
|
||||||
|
* so beats an empty body, which reads the same as "nothing ran".
|
||||||
|
* Only on success: a silent failure must not answer with "OK". */
|
||||||
|
if (err_status == ERR_OK && slen == hdr_len)
|
||||||
|
slen += strtox(outbuf + slen, "OK\n");
|
||||||
|
/* On a parse error keep what the parser printed, because that text
|
||||||
|
* is the explanation, and only restate the status. "400 NO" is as
|
||||||
|
* long as "200 OK", so the header does not have to be rebuilt. */
|
||||||
|
if (err_status != ERR_OK) {
|
||||||
|
outbuf[9] = '4';
|
||||||
|
outbuf[10] = '0';
|
||||||
|
outbuf[11] = '0';
|
||||||
|
outbuf[13] = 'N';
|
||||||
|
outbuf[14] = 'O';
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
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;
|
||||||
|
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];
|
||||||
|
slen += strtox(outbuf + slen, "; SameSite=Strict\r\n\r\n");
|
||||||
|
} else {
|
||||||
|
dbg_string("Password invalid!\n");
|
||||||
|
slen = strtox(outbuf, "HTTP/1.1 302 Found\r\nConnection: close\r\nLocation: login.html\r\n\r\n");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
static uint8_t post_body_take(__xdata uint8_t *p)
|
||||||
|
{
|
||||||
|
uint16_t have;
|
||||||
|
|
||||||
|
if (!content_length) {
|
||||||
|
send_length_required();
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
if (content_length >= CONFIG_UPLOAD_BUF) {
|
||||||
|
send_bad_request();
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
have = uip_len - (p - uip_appdata);
|
||||||
|
if (have >= content_length) {
|
||||||
|
p[content_length] = NUL;
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
memcpy(config_buf, p, have);
|
||||||
|
pre_acc = have;
|
||||||
|
postbody_start = ticks;
|
||||||
|
uip_conn->appstate.tstate = TSTATE_POSTBODY;
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
static void post_body_continue(void)
|
||||||
|
{
|
||||||
|
uint16_t take;
|
||||||
|
|
||||||
|
// no header scan runs while the body is pending: content_length is this request's
|
||||||
|
take = content_length - pre_acc;
|
||||||
|
if (take > uip_len)
|
||||||
|
take = uip_len;
|
||||||
|
memcpy(config_buf + pre_acc, uip_appdata, take);
|
||||||
|
pre_acc += take;
|
||||||
|
if (pre_acc < content_length) {
|
||||||
|
postbody_start = ticks;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
config_buf[pre_acc] = NUL;
|
||||||
|
uip_conn->appstate.tstate = TSTATE_NONE;
|
||||||
|
if (postbody_endpoint == POSTBODY_CMD)
|
||||||
|
run_cmd_body(config_buf);
|
||||||
|
else
|
||||||
|
run_login_body(config_buf);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
void handle_post(void)
|
void handle_post(void)
|
||||||
{
|
{
|
||||||
__xdata struct httpd_state * __xdata s = &(uip_conn->appstate);
|
__xdata struct httpd_state * __xdata s = &(uip_conn->appstate);
|
||||||
__xdata uint8_t *p = uip_appdata;
|
__xdata uint8_t *p = uip_appdata;
|
||||||
__xdata uint8_t *request_path = p + 6;
|
__xdata uint8_t *request_path = p + 6;
|
||||||
|
|
||||||
|
if (s->tstate == TSTATE_POSTBODY) {
|
||||||
|
post_body_continue();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
// Was the multipart header sent in multiple packets?
|
// Was the multipart header sent in multiple packets?
|
||||||
if (s->tstate != TSTATE_MULTIPART) {
|
if (s->tstate != TSTATE_MULTIPART) {
|
||||||
dbg_string("Is POST\n");
|
dbg_string("Is POST\n");
|
||||||
@@ -419,10 +738,10 @@ void handle_post(void)
|
|||||||
// Find end of request path
|
// Find end of request path
|
||||||
while (*p && !is_separator(*p))
|
while (*p && !is_separator(*p))
|
||||||
p++;
|
p++;
|
||||||
*p++ = '\0';
|
*p++ = NUL;
|
||||||
|
|
||||||
// Find end of request header
|
// Find end of request header
|
||||||
boundary[0] ='\0';
|
boundary[0] =NUL;
|
||||||
p = scan_header(p);
|
p = scan_header(p);
|
||||||
dbg_string("Boundary: >"); dbg_string_x(boundary); dbg_string("<\n");
|
dbg_string("Boundary: >"); dbg_string_x(boundary); dbg_string("<\n");
|
||||||
if (!*p || !content_type) {
|
if (!*p || !content_type) {
|
||||||
@@ -438,20 +757,20 @@ void handle_post(void)
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
print_string("Firmware upload started.");
|
print_string("Firmware upload started.");
|
||||||
|
config_upload = 0;
|
||||||
uptr = FIRMWARE_UPLOAD_START;
|
uptr = FIRMWARE_UPLOAD_START;
|
||||||
verify_crc = 1;
|
verify_crc = 1;
|
||||||
max_upload = 1024576;
|
max_upload = 1024576;
|
||||||
|
pre_acc = 0;
|
||||||
} else if (is_word(request_path, "config")) {
|
} else if (is_word(request_path, "config")) {
|
||||||
if (!authenticated) {
|
if (!authenticated) {
|
||||||
send_unauthorized();
|
send_unauthorized();
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
dbg_string("Configuration upload, erasing config mem!\n");
|
dbg_string("Configuration upload\n");
|
||||||
uptr = CONFIG_START;
|
|
||||||
verify_crc = 0;
|
verify_crc = 0;
|
||||||
max_upload = 2048;
|
config_upload = 1;
|
||||||
flash_region.addr = CONFIG_START;
|
pre_acc = 0;
|
||||||
flash_sector_erase();
|
|
||||||
}
|
}
|
||||||
// Check for other POST requests, which are not multipart, below
|
// Check for other POST requests, which are not multipart, below
|
||||||
} else {
|
} else {
|
||||||
@@ -464,11 +783,11 @@ void handle_post(void)
|
|||||||
send_unauthorized();
|
send_unauthorized();
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
execute_commands(p);
|
postbody_endpoint = POSTBODY_CMD;
|
||||||
if (err_status != ERR_OK) {
|
if (!post_body_take(p))
|
||||||
send_bad_request();
|
|
||||||
return;
|
return;
|
||||||
}
|
run_cmd_body(p);
|
||||||
|
return;
|
||||||
} else if (is_word(request_path, "login")) {
|
} else if (is_word(request_path, "login")) {
|
||||||
dbg_string("POST login\n");
|
dbg_string("POST login\n");
|
||||||
|
|
||||||
@@ -478,21 +797,11 @@ void handle_post(void)
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
p += 8; // Read also over "pwd="
|
p += 4;
|
||||||
if (is_url_word_x(p, passwd)) {
|
postbody_endpoint = POSTBODY_LOGIN;
|
||||||
dbg_string("Password accepted!\n");
|
if (!post_body_take(p))
|
||||||
read_reg_timer(&last_session_use);
|
return;
|
||||||
gen_random_bytes(session_id, SESSION_ID_LENGTH);
|
run_login_body(p);
|
||||||
session_id[SESSION_ID_LENGTH] = '\0';
|
|
||||||
slen = strtox(outbuf, "HTTP/1.1 302 Found\r\nLocation: index.html\r\n" \
|
|
||||||
"Set-Cookie: session=");
|
|
||||||
for (register uint8_t i = 0; i < SESSION_ID_LENGTH; i++)
|
|
||||||
outbuf[slen++] = session_id[i];
|
|
||||||
slen += strtox(outbuf + slen, "; SameSite=Strict\r\n\r\n");
|
|
||||||
} else {
|
|
||||||
dbg_string("Password invalid!\n");
|
|
||||||
slen = strtox(outbuf, "HTTP/1.1 302 Found\r\nLocation: login.html\r\n\r\n");
|
|
||||||
}
|
|
||||||
return;
|
return;
|
||||||
} else if (s->tstate == TSTATE_MULTIPART || is_word(request_path, "upload") || is_word(request_path, "config")) {
|
} else if (s->tstate == TSTATE_MULTIPART || is_word(request_path, "upload") || is_word(request_path, "config")) {
|
||||||
dbg_string("POST upload/config request\n");
|
dbg_string("POST upload/config request\n");
|
||||||
@@ -505,42 +814,15 @@ void handle_post(void)
|
|||||||
send_bad_request();
|
send_bad_request();
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
// We skip the intial parts as part of the header
|
if (config_upload)
|
||||||
do {
|
handle_config_fragment(p);
|
||||||
p = skip_boundary(p);
|
else
|
||||||
if (!*p) {
|
handle_firmware_fragment(p);
|
||||||
s->tstate = TSTATE_MULTIPART;
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
p = scan_header(p);
|
|
||||||
if (!*p)
|
|
||||||
goto bad_request;
|
|
||||||
if (!content_type) // We are waiting for the part with the octet stream
|
|
||||||
continue;
|
|
||||||
} while (!is_word(content_type, "application/octet-stream"));
|
|
||||||
dbg_string("Have content octets\n");
|
|
||||||
p += 4; // Skip \r\n\r\n sequence at end of preamble of part
|
|
||||||
|
|
||||||
flash_init(0); // Re-initialize flash for non-DIO operation, otherwise flashing fails
|
|
||||||
set_sys_led_state(SYS_LED_FAST);
|
|
||||||
|
|
||||||
crc_value = 0;
|
|
||||||
bindex = 0;
|
|
||||||
write_len = 0;
|
|
||||||
stream_upload(p - uip_appdata);
|
|
||||||
|
|
||||||
dbg_string("Done reading first fragment\n");
|
|
||||||
return;
|
return;
|
||||||
|
|
||||||
} else {
|
} else {
|
||||||
send_not_found();
|
send_not_found();
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
slen = strtox(outbuf, "HTTP/1.1 200 OK\r\n\r\n");
|
|
||||||
return;
|
|
||||||
bad_request:
|
|
||||||
send_bad_request();
|
|
||||||
return;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
@@ -561,7 +843,7 @@ void httpd_appcall(void)
|
|||||||
} else if (uip_closed()) {
|
} else if (uip_closed()) {
|
||||||
dbg_string("Connection closed\n");
|
dbg_string("Connection closed\n");
|
||||||
s->tstate = TSTATE_CLOSED;
|
s->tstate = TSTATE_CLOSED;
|
||||||
} else if (uip_aborted()) {
|
} else if (uip_aborted() || uip_timedout()) {
|
||||||
dbg_string("Connection aborted\n");
|
dbg_string("Connection aborted\n");
|
||||||
uip_close();
|
uip_close();
|
||||||
s->tstate = TSTATE_CLOSED;
|
s->tstate = TSTATE_CLOSED;
|
||||||
@@ -571,6 +853,11 @@ void httpd_appcall(void)
|
|||||||
dbg_string("Closing because everything has been transmitted\n");
|
dbg_string("Closing because everything has been transmitted\n");
|
||||||
uip_close();
|
uip_close();
|
||||||
s->tstate = TSTATE_CLOSED;
|
s->tstate = TSTATE_CLOSED;
|
||||||
|
} else if (s->tstate == TSTATE_POSTBODY
|
||||||
|
&& (uint16_t)ticks - postbody_start > POSTBODY_TIMEOUT) {
|
||||||
|
dbg_string("Body never arrived\n");
|
||||||
|
uip_abort();
|
||||||
|
s->tstate = TSTATE_CLOSED;
|
||||||
}
|
}
|
||||||
} else if (uip_acked() && s->tstate == TSTATE_TX) {
|
} else if (uip_acked() && s->tstate == TSTATE_TX) {
|
||||||
dbg_string("ACK\n");
|
dbg_string("ACK\n");
|
||||||
@@ -604,11 +891,23 @@ void httpd_appcall(void)
|
|||||||
cont_len -= slen;
|
cont_len -= slen;
|
||||||
cont_addr += slen;
|
cont_addr += slen;
|
||||||
s->tstate = TSTATE_TX;
|
s->tstate = TSTATE_TX;
|
||||||
|
} else if (fw_reset_pending) {
|
||||||
|
// The upload verdict has been fully ACKed by the client;
|
||||||
|
// now it is safe to reset and apply the staged image
|
||||||
|
print_string("Resetting to apply update\n");
|
||||||
|
reset_chip();
|
||||||
}
|
}
|
||||||
} else if (uip_newdata() && s->tstate == TSTATE_POST) {
|
} else if (uip_newdata() && s->tstate == TSTATE_POST) {
|
||||||
// Check here maxupload by subtracting uip_len and close socekt if fails!
|
// Check here maxupload by subtracting uip_len and close socekt if fails!
|
||||||
if (max_upload - uip_len > 0) {
|
if (max_upload - uip_len > 0) {
|
||||||
stream_upload(0);
|
upload_settings.p = uip_appdata;
|
||||||
|
upload_settings.bptr = 0;
|
||||||
|
upload_settings.plen = uip_len;
|
||||||
|
stream_upload();
|
||||||
|
// A completed part with a built verdict must go out
|
||||||
|
// through the normal TX path
|
||||||
|
if (s->tstate == TSTATE_NONE && slen)
|
||||||
|
goto do_send;
|
||||||
write_char('.');
|
write_char('.');
|
||||||
} else {
|
} else {
|
||||||
send_bad_request();
|
send_bad_request();
|
||||||
@@ -626,24 +925,31 @@ void httpd_appcall(void)
|
|||||||
dbg_char('\n');
|
dbg_char('\n');
|
||||||
#endif
|
#endif
|
||||||
p = uip_appdata;
|
p = uip_appdata;
|
||||||
if (is_word(p, "POST") || s->tstate == TSTATE_MULTIPART) {
|
if (is_word(p, "POST") || s->tstate == TSTATE_MULTIPART
|
||||||
|
|| s->tstate == TSTATE_POSTBODY) {
|
||||||
handle_post();
|
handle_post();
|
||||||
// If this is an ongoing post stream, then wait for the next packet
|
// If this is an ongoing post stream, then wait for the next packet
|
||||||
if (s->tstate == TSTATE_POST || s->tstate == TSTATE_MULTIPART) {
|
if (s->tstate == TSTATE_POST || s->tstate == TSTATE_MULTIPART
|
||||||
|
|| s->tstate == TSTATE_POSTBODY) {
|
||||||
uip_len = 0;
|
uip_len = 0;
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
goto do_send;
|
goto do_send;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (is_word(p, "GET"))
|
// We only expect a GET request here.
|
||||||
dbg_string("GET request ");
|
if (!is_word(p, "GET")) {
|
||||||
|
send_bad_request();
|
||||||
|
goto do_send;
|
||||||
|
}
|
||||||
|
|
||||||
|
dbg_string("GET request ");
|
||||||
p += 4;
|
p += 4;
|
||||||
scan_header(p);
|
scan_header(p);
|
||||||
__xdata uint8_t *q = p;
|
__xdata uint8_t *q = p;
|
||||||
while (!is_separator(*p))
|
while (*p && !is_separator(*p))
|
||||||
p++;
|
p++;
|
||||||
*p = '\0';
|
*p = NUL;
|
||||||
dbg_string_x(q);
|
dbg_string_x(q);
|
||||||
dbg_char('\n');
|
dbg_char('\n');
|
||||||
|
|
||||||
@@ -665,7 +971,9 @@ void httpd_appcall(void)
|
|||||||
parse_short(q + 15);
|
parse_short(q + 15);
|
||||||
send_vlan(short_parsed);
|
send_vlan(short_parsed);
|
||||||
} else if (is_word(q, "/counters.json")) {
|
} else if (is_word(q, "/counters.json")) {
|
||||||
send_counters(q[20]-'0');
|
uint8_t cport = q[20] - '0';
|
||||||
|
if (send_counters(cport))
|
||||||
|
send_bad_request();
|
||||||
} else if (is_word(q, "/eee.json")) {
|
} else if (is_word(q, "/eee.json")) {
|
||||||
send_eee();
|
send_eee();
|
||||||
} else if (is_word(q, "/bandwidth.json")) {
|
} else if (is_word(q, "/bandwidth.json")) {
|
||||||
@@ -682,6 +990,8 @@ void httpd_appcall(void)
|
|||||||
send_mtu();
|
send_mtu();
|
||||||
} else if (is_word(q, "/lag.json")) {
|
} else if (is_word(q, "/lag.json")) {
|
||||||
send_lag();
|
send_lag();
|
||||||
|
} else if (is_word(q, "/stp.json")) {
|
||||||
|
send_stp();
|
||||||
} else if (is_word(q, "/vlanlist")) {
|
} else if (is_word(q, "/vlanlist")) {
|
||||||
send_vlanlist();
|
send_vlanlist();
|
||||||
} else if (is_word(q, "/config")) {
|
} else if (is_word(q, "/config")) {
|
||||||
@@ -703,6 +1013,7 @@ void httpd_appcall(void)
|
|||||||
if (!authenticated && !(f_data[entry].start == FDATA_START_login_html
|
if (!authenticated && !(f_data[entry].start == FDATA_START_login_html
|
||||||
|| f_data[entry].start == FDATA_START_port_svg
|
|| f_data[entry].start == FDATA_START_port_svg
|
||||||
|| f_data[entry].start == FDATA_START_sfp_svg
|
|| f_data[entry].start == FDATA_START_sfp_svg
|
||||||
|
|| f_data[entry].start == FDATA_START_main_js
|
||||||
|| f_data[entry].start == FDATA_START_style_css)) {
|
|| f_data[entry].start == FDATA_START_style_css)) {
|
||||||
send_to_login();
|
send_to_login();
|
||||||
goto do_send;
|
goto do_send;
|
||||||
@@ -714,7 +1025,16 @@ void httpd_appcall(void)
|
|||||||
|
|
||||||
slen = strtox(outbuf, "HTTP/1.1 200 OK\r\nContent-Type: ");
|
slen = strtox(outbuf, "HTTP/1.1 200 OK\r\nContent-Type: ");
|
||||||
slen += strtox(outbuf + slen, mime_strings[f_data[entry].mime]);
|
slen += strtox(outbuf + slen, mime_strings[f_data[entry].mime]);
|
||||||
slen += strtox(outbuf + slen, "; charset=UTF-8\r\nCache-Control: max-age=60, must-revalidate\r\nAccess-Control-Allow-Origin: *\r\nContent-Security-Policy: style-src 'self' 'unsafe-inline'\r\n\r\n");
|
/* 'unsafe-inline' is needed for the inline onclick handlers
|
||||||
|
* and the inline <script> on login.html. Connection: close is
|
||||||
|
* required because this httpd closes the connection after every
|
||||||
|
* response; without advertising it a browser reuses the socket
|
||||||
|
* from its keep-alive pool and the next request hits the already
|
||||||
|
* closed connection (a POST is then dropped without a retry). */
|
||||||
|
slen += strtox(outbuf + slen, "; charset=UTF-8\r\n");
|
||||||
|
if (f_data[entry].gzip)
|
||||||
|
slen += strtox(outbuf + slen, "Content-Encoding: gzip\r\n");
|
||||||
|
slen += strtox(outbuf + slen, "Cache-Control: max-age=60, must-revalidate\r\nConnection: close\r\nAccess-Control-Allow-Origin: *\r\nContent-Security-Policy: default-src 'self'; script-src 'self' 'unsafe-inline'; style-src 'self' 'unsafe-inline'; img-src 'self' data:; connect-src 'self'; form-action 'self'\r\n\r\n");
|
||||||
|
|
||||||
len_left = f_data[entry].len;
|
len_left = f_data[entry].len;
|
||||||
if (len_left > (TCP_OUTBUF_SIZE - slen)) {
|
if (len_left > (TCP_OUTBUF_SIZE - slen)) {
|
||||||
|
|||||||
@@ -12,6 +12,7 @@
|
|||||||
#include "phy.h"
|
#include "phy.h"
|
||||||
#include "version.h"
|
#include "version.h"
|
||||||
#include "machine.h"
|
#include "machine.h"
|
||||||
|
#include "rtl837x_stp.h"
|
||||||
#include "page_impl.h"
|
#include "page_impl.h"
|
||||||
#include "syslog.h"
|
#include "syslog.h"
|
||||||
|
|
||||||
@@ -26,6 +27,7 @@
|
|||||||
extern __code const struct machine machine;
|
extern __code const struct machine machine;
|
||||||
extern __xdata uint8_t outbuf[TCP_OUTBUF_SIZE];
|
extern __xdata uint8_t outbuf[TCP_OUTBUF_SIZE];
|
||||||
extern __xdata uint16_t slen;
|
extern __xdata uint16_t slen;
|
||||||
|
extern __xdata uint16_t management_vlan;
|
||||||
extern __xdata uint16_t cont_len;
|
extern __xdata uint16_t cont_len;
|
||||||
extern __xdata uint32_t cont_addr;
|
extern __xdata uint32_t cont_addr;
|
||||||
extern __code uint8_t * __code hex;
|
extern __code uint8_t * __code hex;
|
||||||
@@ -45,8 +47,8 @@ extern __xdata char sfp_module_model[2][17];
|
|||||||
extern __xdata char sfp_module_serial[2][17];
|
extern __xdata char sfp_module_serial[2][17];
|
||||||
extern __xdata uint8_t sfp_options[2];
|
extern __xdata uint8_t sfp_options[2];
|
||||||
|
|
||||||
__code uint8_t * __code HTTP_RESPONCE_JSON = "HTTP/1.1 200 OK\r\nContent-Type: application/json\r\n\r\n";
|
__code uint8_t * __code HTTP_RESPONCE_JSON = "HTTP/1.1 200 OK\r\nConnection: close\r\nContent-Type: application/json\r\n\r\n";
|
||||||
__code uint8_t * __code HTTP_RESPONCE_TXT = "HTTP/1.1 200 OK\r\nContent-Type: text/plain\r\n\r\n";
|
__code uint8_t * __code HTTP_RESPONCE_TXT = "HTTP/1.1 200 OK\r\nConnection: close\r\nContent-Type: text/plain\r\n\r\n";
|
||||||
|
|
||||||
// Convert uint8_t to ascii HEX char push on html-buffer.
|
// Convert uint8_t to ascii HEX char push on html-buffer.
|
||||||
void charhex_to_html(char c)
|
void charhex_to_html(char c)
|
||||||
@@ -156,14 +158,14 @@ void sfr_data_to_html(void)
|
|||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
void reg_to_html(register uint16_t reg)
|
void reg_to_html(uint16_t reg)
|
||||||
{
|
{
|
||||||
reg_read_m(reg);
|
reg_read_m(reg);
|
||||||
sfr_data_to_html();
|
sfr_data_to_html();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
void reg_to_html_long(register uint16_t reg)
|
void reg_to_html_long(uint16_t reg)
|
||||||
{
|
{
|
||||||
reg_read_m(reg);
|
reg_read_m(reg);
|
||||||
byte_to_html(sfr_data[0]);
|
byte_to_html(sfr_data[0]);
|
||||||
@@ -176,10 +178,12 @@ void reg_to_html_long(register uint16_t reg)
|
|||||||
void send_sfp_info(uint8_t sfp)
|
void send_sfp_info(uint8_t sfp)
|
||||||
{
|
{
|
||||||
// This loops over the Vendor-name, Vendor OUI, Vendor PN and Vendor rev ASCII fields
|
// This loops over the Vendor-name, Vendor OUI, Vendor PN and Vendor rev ASCII fields
|
||||||
for (uint8_t i = 20; i < 60; i++) {
|
for (uint8_t i = 16; i < 64; i++) {
|
||||||
if (i >= 36 && i < 40) // Skip Non-ASCII codes
|
if (!(i & 0xf) && !sfp_read_block(sfp, i, 16))
|
||||||
|
return;
|
||||||
|
if (i < 20 || i >= 60 || (i >= 36 && i < 40)) // Skip Non-ASCII codes
|
||||||
continue;
|
continue;
|
||||||
uint8_t c = sfp_read_reg(sfp, i);
|
uint8_t c = sfp_buf[i & 0xf];
|
||||||
if (c && c != 0xa0) // a0 is the byte read from a non-existant I2C EEPROM
|
if (c && c != 0xa0) // a0 is the byte read from a non-existant I2C EEPROM
|
||||||
char_to_html(c);
|
char_to_html(c);
|
||||||
}
|
}
|
||||||
@@ -192,32 +196,11 @@ void sfp_send_data(uint8_t slot, uint8_t reg, uint8_t len)
|
|||||||
if (len > 16)
|
if (len > 16)
|
||||||
return;
|
return;
|
||||||
|
|
||||||
if (reg & 0x80) { // Configure SFP readings address (0x51) as I2C device address
|
if (!sfp_read_block(slot, reg, len))
|
||||||
reg &= 0x7f;
|
return;
|
||||||
REG_WRITE(RTL837X_REG_I2C_CTRL, 0x00, 0x1 << (I2C_MEM_ADDR_WIDTH-16) | (len - 1) & 0xf, 0x51 >> 5, (0x51 << 3) & 0xff);
|
|
||||||
} else {
|
|
||||||
REG_WRITE(RTL837X_REG_I2C_CTRL, 0x00, 0x1 << (I2C_MEM_ADDR_WIDTH-16) | (len - 1) & 0xf, 0x50 >> 5, (0x50 << 3) & 0xff);
|
|
||||||
}
|
|
||||||
|
|
||||||
reg_read_m(RTL837X_REG_I2C_CTRL);
|
for (uint8_t i = 0; i < len; i++)
|
||||||
sfr_mask_data(1, 0xfc, i2c_bus_from_scl_pin(machine.sfp_port[slot].i2c.scl) << 5 | i2c_bus_from_sda_pin(machine.sfp_port[slot].i2c.sda) << 2);
|
byte_to_html(sfp_buf[i]);
|
||||||
reg_write_m(RTL837X_REG_I2C_CTRL);
|
|
||||||
|
|
||||||
REG_WRITE(RTL837X_REG_I2C_IN, 0, 0, 0, reg);
|
|
||||||
|
|
||||||
// Execute I2C Read
|
|
||||||
reg_bit_set(RTL837X_REG_I2C_CTRL, 0);
|
|
||||||
|
|
||||||
// Wait for execution to finish
|
|
||||||
do {
|
|
||||||
reg_read_m(RTL837X_REG_I2C_CTRL);
|
|
||||||
} while (sfr_data[3] & 0x1);
|
|
||||||
|
|
||||||
for (uint8_t i = 0; i < len; i++) {
|
|
||||||
if (!(i & 0x3))
|
|
||||||
reg_read_m(RTL837X_REG_I2C_OUT + i);
|
|
||||||
byte_to_html(sfr_data[3 - (i & 0x3)]);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
@@ -240,11 +223,12 @@ void send_basic_info(void)
|
|||||||
itoa_html(uip_netmask[0] >> 8); char_to_html('.');
|
itoa_html(uip_netmask[0] >> 8); char_to_html('.');
|
||||||
itoa_html(uip_netmask[1]); char_to_html('.');
|
itoa_html(uip_netmask[1]); char_to_html('.');
|
||||||
itoa_html(uip_netmask[1] >> 8);
|
itoa_html(uip_netmask[1] >> 8);
|
||||||
slen += strtox(outbuf + slen, "\",\"syslog_server_ip\":\"");
|
slen += strtox(outbuf + slen, "\",\"syslog_server\":\"");
|
||||||
itoa_html(syslog_state.server_ip[0]); char_to_html('.');
|
itoa_html(syslog_state.server_ip[0]); char_to_html('.');
|
||||||
itoa_html(syslog_state.server_ip[1]); char_to_html('.');
|
itoa_html(syslog_state.server_ip[1]); char_to_html('.');
|
||||||
itoa_html(syslog_state.server_ip[2]); char_to_html('.');
|
itoa_html(syslog_state.server_ip[2]); char_to_html('.');
|
||||||
itoa_html(syslog_state.server_ip[3]);
|
itoa_html(syslog_state.server_ip[3]); char_to_html(':');
|
||||||
|
itoa16_html(syslog_state.server_port);
|
||||||
slen += strtox(outbuf + slen, "\",\"mac_address\":\"");
|
slen += strtox(outbuf + slen, "\",\"mac_address\":\"");
|
||||||
byte_to_html(uip_ethaddr.addr[0]); char_to_html(':');
|
byte_to_html(uip_ethaddr.addr[0]); char_to_html(':');
|
||||||
byte_to_html(uip_ethaddr.addr[1]); char_to_html(':');
|
byte_to_html(uip_ethaddr.addr[1]); char_to_html(':');
|
||||||
@@ -252,6 +236,12 @@ void send_basic_info(void)
|
|||||||
byte_to_html(uip_ethaddr.addr[3]); char_to_html(':');
|
byte_to_html(uip_ethaddr.addr[3]); char_to_html(':');
|
||||||
byte_to_html(uip_ethaddr.addr[4]); char_to_html(':');
|
byte_to_html(uip_ethaddr.addr[4]); char_to_html(':');
|
||||||
byte_to_html(uip_ethaddr.addr[5]);
|
byte_to_html(uip_ethaddr.addr[5]);
|
||||||
|
slen += strtox(outbuf + slen, "\",\"hostname\":\"");
|
||||||
|
{
|
||||||
|
__xdata char *hp = hostname; /* sanitized on ingest, emit verbatim */
|
||||||
|
while (*hp)
|
||||||
|
char_to_html(*hp++);
|
||||||
|
}
|
||||||
slen += strtox(outbuf + slen, "\",\"sw_ver\":\"");
|
slen += strtox(outbuf + slen, "\",\"sw_ver\":\"");
|
||||||
slen += strtox(outbuf + slen, VERSION_SW);
|
slen += strtox(outbuf + slen, VERSION_SW);
|
||||||
slen += strtox(outbuf + slen, "\",\"build_date\":\"");
|
slen += strtox(outbuf + slen, "\",\"build_date\":\"");
|
||||||
@@ -302,17 +292,24 @@ void send_vlan(uint16_t vlan)
|
|||||||
slen += strtox(outbuf + slen, "\"}");
|
slen += strtox(outbuf + slen, "\"}");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* Send counters
|
||||||
void send_counters(char port)
|
* Only accepts physical port 1..9.
|
||||||
|
* Returns an error if the port physical don't exists.
|
||||||
|
*/
|
||||||
|
bool send_counters(uint8_t phys_port)
|
||||||
{
|
{
|
||||||
dbg_string("send_counters called: "); dbg_byte(port); dbg_char('\n');
|
uint8_t phys_port_idx = phys_port - 1;
|
||||||
|
if (phys_port_idx > (machine.max_port - machine.min_port))
|
||||||
|
goto err;
|
||||||
|
uint8_t log_port = machine.phys_to_log_port[phys_port_idx];
|
||||||
|
|
||||||
|
dbg_string("send_counters called: "); dbg_byte(phys_port_idx); dbg_char('\n');
|
||||||
slen = strtox(outbuf, HTTP_RESPONCE_JSON);
|
slen = strtox(outbuf, HTTP_RESPONCE_JSON);
|
||||||
dbg_string("sending counters\n");
|
dbg_string("sending counters\n"); dbg_byte(phys_port_idx);
|
||||||
dbg_byte(port);
|
|
||||||
uint8_t i = machine.phys_to_log_port[port];
|
char_to_html('[');
|
||||||
slen += strtox(outbuf + slen, "[");
|
|
||||||
for (uint8_t counter = 0; counter < 0x37; counter++) {
|
for (uint8_t counter = 0; counter < 0x37; counter++) {
|
||||||
STAT_GET(counter, i);
|
STAT_GET(counter, log_port);
|
||||||
slen += strtox(outbuf + slen, "\"0x");
|
slen += strtox(outbuf + slen, "\"0x");
|
||||||
reg_to_html(RTL837X_STAT_V_HIGH);
|
reg_to_html(RTL837X_STAT_V_HIGH);
|
||||||
reg_to_html_long(RTL837X_STAT_V_LOW);
|
reg_to_html_long(RTL837X_STAT_V_LOW);
|
||||||
@@ -321,6 +318,12 @@ void send_counters(char port)
|
|||||||
char_to_html(',');
|
char_to_html(',');
|
||||||
}
|
}
|
||||||
char_to_html(']');
|
char_to_html(']');
|
||||||
|
|
||||||
|
return false;
|
||||||
|
|
||||||
|
err:
|
||||||
|
dbg_string("Error: counters: phy_port_idx don't exists\n");
|
||||||
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
@@ -349,10 +352,12 @@ void send_l2(uint16_t idx)
|
|||||||
*/
|
*/
|
||||||
__xdata uint16_t entry = idx & 0xfff;
|
__xdata uint16_t entry = idx & 0xfff;
|
||||||
__xdata uint16_t first_entry = 0xffff; // An illegal entry index
|
__xdata uint16_t first_entry = 0xffff; // An illegal entry index
|
||||||
|
__bit first = true;
|
||||||
char_to_html('[');
|
char_to_html('[');
|
||||||
while (1) {
|
while (1) {
|
||||||
entries_left--;
|
entries_left--;
|
||||||
uint8_t port = 0;
|
uint8_t port = 0;
|
||||||
|
uint8_t lag;
|
||||||
reg_read_m(RTL837x_TBL_DATA_0);
|
reg_read_m(RTL837x_TBL_DATA_0);
|
||||||
REG_WRITE(RTL837x_TBL_DATA_0, sfr_data[0], sfr_data[1] & 0xfc, sfr_data[2] | (TBL_LUTREAD_NEXT_L2UC << 6), sfr_data[3]);
|
REG_WRITE(RTL837x_TBL_DATA_0, sfr_data[0], sfr_data[1] & 0xfc, sfr_data[2] | (TBL_LUTREAD_NEXT_L2UC << 6), sfr_data[3]);
|
||||||
|
|
||||||
@@ -362,9 +367,22 @@ void send_l2(uint16_t idx)
|
|||||||
} while (sfr_data[3] & TBL_EXECUTE);
|
} while (sfr_data[3] & TBL_EXECUTE);
|
||||||
|
|
||||||
reg_read_m(RTL837x_L2_DATA_OUT_B);
|
reg_read_m(RTL837x_L2_DATA_OUT_B);
|
||||||
if ((sfr_data[0] & 0x20)) { // Check entry is valid
|
__bit valid = (sfr_data[0] & 0x20) != 0;
|
||||||
|
if (valid) {
|
||||||
|
/* separator + 74-byte worst-case entry + closing "]" */
|
||||||
|
if (slen + 76 > TCP_OUTBUF_SIZE)
|
||||||
|
break;
|
||||||
|
if (!first)
|
||||||
|
char_to_html(',');
|
||||||
|
first = false;
|
||||||
|
|
||||||
|
// VLAN, taken from the read above instead of reading the register twice
|
||||||
|
slen += strtox(outbuf + slen, "{\"vlan\":\"");
|
||||||
|
charhex_to_html(sfr_data[0] & 0x0f);
|
||||||
|
byte_to_html(sfr_data[1]);
|
||||||
|
|
||||||
// MAC
|
// MAC
|
||||||
slen += strtox(outbuf + slen, "{\"mac\":\"");
|
slen += strtox(outbuf + slen, "\",\"mac\":\"");
|
||||||
byte_to_html(sfr_data[2]); char_to_html(':');
|
byte_to_html(sfr_data[2]); char_to_html(':');
|
||||||
byte_to_html(sfr_data[3]); char_to_html(':');
|
byte_to_html(sfr_data[3]); char_to_html(':');
|
||||||
port = (sfr_data[0] >> 6) & 0x3;
|
port = (sfr_data[0] >> 6) & 0x3;
|
||||||
@@ -374,47 +392,38 @@ void send_l2(uint16_t idx)
|
|||||||
byte_to_html(sfr_data[2]); char_to_html(':');
|
byte_to_html(sfr_data[2]); char_to_html(':');
|
||||||
byte_to_html(sfr_data[3]);
|
byte_to_html(sfr_data[3]);
|
||||||
|
|
||||||
// VLAN
|
|
||||||
slen += strtox(outbuf + slen, "\",\"vlan\":\"");
|
|
||||||
reg_read_m(RTL837x_L2_DATA_OUT_B);
|
|
||||||
charhex_to_html(sfr_data[0] & 0x0f);
|
|
||||||
byte_to_html(sfr_data[1]);
|
|
||||||
|
|
||||||
// type
|
// type
|
||||||
reg_read_m(RTL837x_L2_DATA_OUT_C);
|
reg_read_m(RTL837x_L2_DATA_OUT_C);
|
||||||
if (sfr_data[2] & 0x1)
|
if (sfr_data[1] & 0x1)
|
||||||
slen += strtox(outbuf + slen, "\",\"type\":\"s\",\"port\":");
|
slen += strtox(outbuf + slen, "\",\"type\":\"s\",\"port\":");
|
||||||
else
|
else
|
||||||
slen += strtox(outbuf + slen, "\",\"type\":\"l\",\"port\":");
|
slen += strtox(outbuf + slen, "\",\"type\":\"l\",\"port\":");
|
||||||
|
|
||||||
port |= (sfr_data[3] & 0x3) << 2;
|
port |= (sfr_data[3] & 0x3) << 2;
|
||||||
itoa_html(port);
|
itoa_html(port);
|
||||||
|
slen += strtox(outbuf + slen, ",\"lag\":");
|
||||||
|
lag = port_lag_of(port);
|
||||||
|
itoa_html(lag == PORT_LAG_NONE ? 0 : lag + 1);
|
||||||
|
}
|
||||||
|
|
||||||
// Index
|
// Index
|
||||||
reg_read_m(RTL837x_TBL_DATA_0);
|
reg_read_m(RTL837x_TBL_DATA_0);
|
||||||
entry = (((uint16_t)sfr_data[2] & 0x0f) << 8) | sfr_data[3];
|
entry = (((uint16_t)sfr_data[2] & 0x0f) << 8) | sfr_data[3];
|
||||||
|
if (valid) {
|
||||||
slen += strtox(outbuf + slen, ",\"idx\":\"");
|
slen += strtox(outbuf + slen, ",\"idx\":\"");
|
||||||
byte_to_html(entry >> 8);
|
byte_to_html(entry >> 8);
|
||||||
byte_to_html(entry);
|
byte_to_html(entry);
|
||||||
char_to_html('"');
|
char_to_html('"');
|
||||||
char_to_html('}');
|
char_to_html('}');
|
||||||
entry += 1; // We want the next entry following after the current entry
|
|
||||||
} else {
|
|
||||||
reg_read_m(RTL837x_TBL_DATA_0);
|
|
||||||
entry = (((uint16_t)sfr_data[2] & 0x0f) << 8) | sfr_data[3] + 1;
|
|
||||||
}
|
}
|
||||||
if (first_entry == 0xffff) {
|
entry += 1; // We want the next entry following after the current entry
|
||||||
char_to_html(',');
|
|
||||||
|
if (first_entry == 0xffff)
|
||||||
first_entry = entry;
|
first_entry = entry;
|
||||||
} else {
|
else if (first_entry == entry || !entries_left)
|
||||||
if (first_entry == entry || !entries_left) {
|
break;
|
||||||
char_to_html(']');
|
|
||||||
break;
|
|
||||||
} else {
|
|
||||||
char_to_html(',');
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
char_to_html(']');
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
@@ -426,42 +435,52 @@ void l2_delete(uint16_t idx)
|
|||||||
__xdata uint8_t entries_left = L2_MAX_TRANSFER;
|
__xdata uint8_t entries_left = L2_MAX_TRANSFER;
|
||||||
|
|
||||||
do {
|
do {
|
||||||
reg_read_m(RTL837X_TBL_CTRL);
|
reg_read(RTL837X_TBL_CTRL);
|
||||||
} while (sfr_data[3] & TBL_EXECUTE);
|
} while (SFR_DATA_0 & TBL_EXECUTE);
|
||||||
slen += strtox(outbuf + slen, "{\"result\":");
|
slen += strtox(outbuf + slen, "{\"result\":");
|
||||||
// First, search for the entry based on the index
|
// First, search for the entry based on the index
|
||||||
reg_read_m(RTL837x_TBL_DATA_0);
|
reg_read_m(RTL837x_TBL_DATA_0);
|
||||||
REG_WRITE(RTL837x_TBL_DATA_0, sfr_data[0], sfr_data[1] & 0xfc, sfr_data[2] | (TBL_LUTREAD_NEXT_L2UC << 6), sfr_data[3]);
|
sfr_data[1] &= 0xfc;
|
||||||
|
sfr_data[2] |= (TBL_LUTREAD_NEXT_L2UC << 6);
|
||||||
|
reg_write_m(RTL837x_TBL_DATA_0);
|
||||||
|
|
||||||
REG_WRITE(RTL837X_TBL_CTRL, (idx >> 8) & 0xf, idx, TBL_L2_UNICAST, TBL_EXECUTE);
|
REG_WRITE(RTL837X_TBL_CTRL, (idx >> 8) & 0xf, idx, TBL_L2_UNICAST, TBL_EXECUTE);
|
||||||
do {
|
do {
|
||||||
reg_read_m(RTL837X_TBL_CTRL);
|
reg_read(RTL837X_TBL_CTRL);
|
||||||
} while (sfr_data[3] & 0x1);
|
} while (SFR_DATA_0 & 0x1);
|
||||||
reg_read_m(RTL837x_L2_DATA_OUT_B);
|
reg_read_m(RTL837x_L2_DATA_OUT_B);
|
||||||
if (!(sfr_data[0] & 0x20)) {
|
if (!(SFR_DATA_24 & 0x20)) {
|
||||||
char_to_html('0');
|
char_to_html('0');
|
||||||
} else {
|
} else {
|
||||||
|
__bit is_our_mac_addr = uip_ethaddr.addr[0] == SFR_DATA_8 && uip_ethaddr.addr[1] == SFR_DATA_0;
|
||||||
sfr_data[0] &= 0x3f; // Clear SPA
|
sfr_data[0] &= 0x3f; // Clear SPA
|
||||||
reg_write_m(RTL837x_TBL_DATA_IN_B);
|
reg_write_m(RTL837x_TBL_DATA_IN_B);
|
||||||
|
|
||||||
// Second half of MAC is copied
|
// Second half of MAC is copied
|
||||||
reg_read_m(RTL837x_L2_DATA_OUT_A);
|
reg_read_m(RTL837x_L2_DATA_OUT_A);
|
||||||
reg_write_m(RTL837x_TBL_DATA_IN_A);
|
if (is_our_mac_addr && uip_ethaddr.addr[2] == SFR_DATA_24 && uip_ethaddr.addr[3] == SFR_DATA_16
|
||||||
|
&& uip_ethaddr.addr[4] == SFR_DATA_8 && uip_ethaddr.addr[5] == SFR_DATA_0) {
|
||||||
|
// the switch's own entry keeps management reachable
|
||||||
|
char_to_html('0');
|
||||||
|
} else {
|
||||||
|
reg_write_m(RTL837x_TBL_DATA_IN_A);
|
||||||
|
|
||||||
reg_read_m(RTL837x_L2_DATA_OUT_C);
|
reg_read_m(RTL837x_L2_DATA_OUT_C);
|
||||||
sfr_data[3] &= 0xc0; // Clear age, auth and second part of ports
|
sfr_data[3] &= 0xc0; // Clear age, auth and second part of ports
|
||||||
sfr_data[1] &= 0xfe; // Clear nosalearn
|
sfr_data[1] &= 0xfe; // Clear nosalearn
|
||||||
reg_write_m(RTL837x_TBL_DATA_IN_C);
|
reg_write_m(RTL837x_TBL_DATA_IN_C);
|
||||||
|
|
||||||
reg_read_m(RTL837x_TBL_DATA_0);
|
reg_read_m(RTL837x_TBL_DATA_0);
|
||||||
REG_WRITE(RTL837x_TBL_DATA_0, sfr_data[0], sfr_data[1], TBL_L2_UNICAST, sfr_data[3]);
|
sfr_data[2] = TBL_L2_UNICAST;
|
||||||
|
reg_write_m(RTL837x_TBL_DATA_0);
|
||||||
|
|
||||||
REG_WRITE(RTL837X_TBL_CTRL, idx >> 8, idx, TBL_L2_UNICAST, TBL_WRITE | TBL_EXECUTE);
|
REG_WRITE(RTL837X_TBL_CTRL, idx >> 8, idx, TBL_L2_UNICAST, TBL_WRITE | TBL_EXECUTE);
|
||||||
do {
|
do {
|
||||||
reg_read_m(RTL837X_TBL_CTRL);
|
reg_read(RTL837X_TBL_CTRL);
|
||||||
} while (sfr_data[3] & TBL_EXECUTE);
|
} while (SFR_DATA_0 & TBL_EXECUTE);
|
||||||
|
|
||||||
char_to_html('1');
|
char_to_html('1');
|
||||||
|
}
|
||||||
}
|
}
|
||||||
char_to_html('}');
|
char_to_html('}');
|
||||||
}
|
}
|
||||||
@@ -511,8 +530,7 @@ void send_lag(void)
|
|||||||
slen += strtox(outbuf + slen, "{\"lagNum\":");
|
slen += strtox(outbuf + slen, "{\"lagNum\":");
|
||||||
itoa_html(l);
|
itoa_html(l);
|
||||||
slen += strtox(outbuf + slen, ",\"members\":\"");
|
slen += strtox(outbuf + slen, ",\"members\":\"");
|
||||||
reg_read_m(RTL837X_TRK_MBR_CTRL_BASE + (l << 2));
|
uint16_t ports = port_lag_members_get(l);
|
||||||
uint16_t ports = ((uint16_t)sfr_data[2] << 8) | sfr_data[3];
|
|
||||||
for (uint8_t i = 0; i < 16; i++) {
|
for (uint8_t i = 0; i < 16; i++) {
|
||||||
bool_to_html(!!(ports & 0x8000));
|
bool_to_html(!!(ports & 0x8000));
|
||||||
ports <<= 1;
|
ports <<= 1;
|
||||||
@@ -527,6 +545,119 @@ 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\":");
|
||||||
|
itoa_html(stp_root_port == 0xff ? 0 : machine.log_to_phys_port[stp_root_port]);
|
||||||
|
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 = machine.min_port; i <= machine.max_port; i++) {
|
||||||
|
slen += strtox(outbuf + slen, "{\"p\":");
|
||||||
|
itoa_html(machine.log_to_phys_port[i]);
|
||||||
|
slen += strtox(outbuf + slen, ",\"st\":");
|
||||||
|
st = (sfr_data[3 - (i >> 2)] >> ((i << 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_eee(void)
|
void send_eee(void)
|
||||||
{
|
{
|
||||||
dbg_string("send_eee called\nsending EEE status\n");
|
dbg_string("send_eee called\nsending EEE status\n");
|
||||||
@@ -660,52 +791,53 @@ void send_status(void)
|
|||||||
slen += strtox(outbuf + slen, "\"");
|
slen += strtox(outbuf + slen, "\"");
|
||||||
|
|
||||||
if (machine.is_sfp[i]) {
|
if (machine.is_sfp[i]) {
|
||||||
|
uint8_t sfp = machine.is_sfp[i] - 1;
|
||||||
slen += strtox(outbuf + slen, ",\"isSFP\":1,\"enabled\":");
|
slen += strtox(outbuf + slen, ",\"isSFP\":1,\"enabled\":");
|
||||||
if (!(sfp_pins_last & (0x1 << ((machine.is_sfp[i] - 1) << 2)))) {
|
if (!(sfp_pins_last & (0x1 << (sfp << 2)))) {
|
||||||
bool_to_html(1);
|
bool_to_html(1);
|
||||||
slen += strtox(outbuf + slen,",\"sfp_options\":\"0x");
|
slen += strtox(outbuf + slen,",\"sfp_options\":\"0x");
|
||||||
byte_to_html(sfp_options[machine.is_sfp[i]-1]);
|
byte_to_html(sfp_options[sfp]);
|
||||||
if (sfp_options[machine.is_sfp[i]-1] & 0x40) {
|
if (sfp_options[sfp] & 0x40) {
|
||||||
slen += strtox(outbuf + slen,"\",\"sfp_temp\":\"0x");
|
slen += strtox(outbuf + slen,"\",\"sfp_temp\":\"0x");
|
||||||
sfp_send_data(machine.is_sfp[i] - 1, 224, 2);
|
sfp_send_data(sfp, 224, 2);
|
||||||
slen += strtox(outbuf + slen,"\",\"sfp_vcc\":\"0x");
|
slen += strtox(outbuf + slen,"\",\"sfp_vcc\":\"0x");
|
||||||
sfp_send_data(machine.is_sfp[i] - 1, 226, 2);
|
sfp_send_data(sfp, 226, 2);
|
||||||
slen += strtox(outbuf + slen,"\",\"sfp_txbias\":\"0x");
|
slen += strtox(outbuf + slen,"\",\"sfp_txbias\":\"0x");
|
||||||
sfp_send_data(machine.is_sfp[i] - 1, 228, 2);
|
sfp_send_data(sfp, 228, 2);
|
||||||
slen += strtox(outbuf + slen,"\",\"sfp_txpower\":\"0x");
|
slen += strtox(outbuf + slen,"\",\"sfp_txpower\":\"0x");
|
||||||
sfp_send_data(machine.is_sfp[i] - 1, 230, 2);
|
sfp_send_data(sfp, 230, 2);
|
||||||
slen += strtox(outbuf + slen,"\",\"sfp_rxpower\":\"0x");
|
slen += strtox(outbuf + slen,"\",\"sfp_rxpower\":\"0x");
|
||||||
sfp_send_data(machine.is_sfp[i] - 1, 232, 2);
|
sfp_send_data(sfp, 232, 2);
|
||||||
if (sfp_options[machine.is_sfp[i]-1] & 0x10) {
|
if (sfp_options[sfp] & 0x10) {
|
||||||
slen += strtox(outbuf + slen,"\",\"sfp_temp_cal\":\"0x");
|
slen += strtox(outbuf + slen,"\",\"sfp_temp_cal\":\"0x");
|
||||||
sfp_send_data(machine.is_sfp[i] - 1, 212, 4);
|
sfp_send_data(sfp, 212, 4);
|
||||||
slen += strtox(outbuf + slen,"\",\"sfp_vcc_cal\":\"0x");
|
slen += strtox(outbuf + slen,"\",\"sfp_vcc_cal\":\"0x");
|
||||||
sfp_send_data(machine.is_sfp[i] - 1, 216, 4);
|
sfp_send_data(sfp, 216, 4);
|
||||||
slen += strtox(outbuf + slen,"\",\"sfp_txbias_cal\":\"0x");
|
slen += strtox(outbuf + slen,"\",\"sfp_txbias_cal\":\"0x");
|
||||||
sfp_send_data(machine.is_sfp[i] - 1, 204, 4);
|
sfp_send_data(sfp, 204, 4);
|
||||||
slen += strtox(outbuf + slen,"\",\"sfp_txpower_cal\":\"0x");
|
slen += strtox(outbuf + slen,"\",\"sfp_txpower_cal\":\"0x");
|
||||||
sfp_send_data(machine.is_sfp[i] - 1, 208, 4);
|
sfp_send_data(sfp, 208, 4);
|
||||||
slen += strtox(outbuf + slen,"\",\"sfp_rxpower_cal\":\"0x");
|
slen += strtox(outbuf + slen,"\",\"sfp_rxpower_cal\":\"0x");
|
||||||
sfp_send_data(machine.is_sfp[i] - 1, 184, 16);
|
sfp_send_data(sfp, 184, 16);
|
||||||
sfp_send_data(machine.is_sfp[i] - 1, 200, 4);
|
sfp_send_data(sfp, 200, 4);
|
||||||
}
|
}
|
||||||
slen += strtox(outbuf + slen,"\",\"sfp_state\":\"0x");
|
slen += strtox(outbuf + slen,"\",\"sfp_state\":\"0x");
|
||||||
sfp_send_data(machine.is_sfp[i] - 1, 238, 1);
|
sfp_send_data(sfp, 238, 1);
|
||||||
}
|
}
|
||||||
slen += strtox(outbuf + slen,"\",\"sfp_vendor\":\"");
|
slen += strtox(outbuf + slen,"\",\"sfp_vendor\":\"");
|
||||||
for (register uint8_t s = 0; s < 16; s++)
|
for (uint8_t s = 0; s < 16 && sfp_module_vendor[sfp][s]; s++)
|
||||||
outbuf[slen++] = sfp_module_vendor[machine.is_sfp[i]-1][s];
|
outbuf[slen++] = sfp_module_vendor[sfp][s];
|
||||||
slen += strtox(outbuf + slen,"\",\"sfp_model\":\"");
|
slen += strtox(outbuf + slen,"\",\"sfp_model\":\"");
|
||||||
for (register uint8_t s = 0; s < 16; s++)
|
for (uint8_t s = 0; s < 16 && sfp_module_model[sfp][s]; s++)
|
||||||
outbuf[slen++] = sfp_module_model[machine.is_sfp[i]-1][s];
|
outbuf[slen++] = sfp_module_model[sfp][s];
|
||||||
slen += strtox(outbuf + slen,"\",\"sfp_serial\":\"");
|
slen += strtox(outbuf + slen,"\",\"sfp_serial\":\"");
|
||||||
for (register uint8_t s = 0; s < 16; s++)
|
for (uint8_t s = 0; s < 16 && sfp_module_serial[sfp][s]; s++)
|
||||||
outbuf[slen++] = sfp_module_serial[machine.is_sfp[i]-1][s];
|
outbuf[slen++] = sfp_module_serial[sfp][s];
|
||||||
slen += strtox(outbuf + slen,"\",\"sfp_los\":");
|
slen += strtox(outbuf + slen,"\",\"sfp_los\":");
|
||||||
if (machine.sfp_port[machine.is_sfp[i]-1].pin_los == GPIO_NA) {
|
if (machine.sfp_port[sfp].pin_los == GPIO_NA) {
|
||||||
slen += strtox(outbuf + slen,"null");
|
slen += strtox(outbuf + slen,"null");
|
||||||
} else {
|
} else {
|
||||||
bool_to_html(sfp_pins_last & (0x2 << (((machine.is_sfp[i]-1) << 2))));
|
bool_to_html(sfp_pins_last & (0x2 << (sfp << 2)));
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
bool_to_html(0);
|
bool_to_html(0);
|
||||||
@@ -813,7 +945,7 @@ found_end:
|
|||||||
if (valid_len > (TCP_OUTBUF_SIZE - slen)) {
|
if (valid_len > (TCP_OUTBUF_SIZE - slen)) {
|
||||||
cont_len = valid_len - (TCP_OUTBUF_SIZE - slen);
|
cont_len = valid_len - (TCP_OUTBUF_SIZE - slen);
|
||||||
valid_len = TCP_OUTBUF_SIZE - slen;
|
valid_len = TCP_OUTBUF_SIZE - slen;
|
||||||
cont_addr = valid_len;
|
cont_addr = CONFIG_START + valid_len;
|
||||||
}
|
}
|
||||||
|
|
||||||
flash_region.addr = CONFIG_START;
|
flash_region.addr = CONFIG_START;
|
||||||
@@ -850,7 +982,9 @@ void send_vlanlist(void)
|
|||||||
uint8_t first = 1;
|
uint8_t first = 1;
|
||||||
|
|
||||||
slen = strtox(outbuf, HTTP_RESPONCE_JSON);
|
slen = strtox(outbuf, HTTP_RESPONCE_JSON);
|
||||||
char_to_html('[');
|
slen += strtox(outbuf + slen, "{\"mgmt\":");
|
||||||
|
itoa16_html(management_vlan);
|
||||||
|
slen += strtox(outbuf + slen, ",\"vlan\":[");
|
||||||
|
|
||||||
for (i = 1; i < 4095; i++) {
|
for (i = 1; i < 4095; i++) {
|
||||||
if (vlan_get(i) < 0)
|
if (vlan_get(i) < 0)
|
||||||
@@ -858,7 +992,7 @@ void send_vlanlist(void)
|
|||||||
if (!(sfr_data[0] & 0x02)) /* bit 1: VLAN table entry valid */
|
if (!(sfr_data[0] & 0x02)) /* bit 1: VLAN table entry valid */
|
||||||
continue;
|
continue;
|
||||||
|
|
||||||
if (slen + 139 > TCP_OUTBUF_SIZE) /* 138 bytes worst-case entry + 1 byte for closing ']' */
|
if (slen + 141 > TCP_OUTBUF_SIZE) /* comma + 138-byte worst-case entry + closing "]}" */
|
||||||
break;
|
break;
|
||||||
|
|
||||||
if (!first)
|
if (!first)
|
||||||
@@ -880,4 +1014,5 @@ void send_vlanlist(void)
|
|||||||
}
|
}
|
||||||
|
|
||||||
char_to_html(']');
|
char_to_html(']');
|
||||||
|
char_to_html('}');
|
||||||
}
|
}
|
||||||
@@ -1,7 +1,11 @@
|
|||||||
#ifndef __PAGE_IMPL_H__
|
#ifndef __PAGE_IMPL_H__
|
||||||
#define __PAGE_IMPL_H__
|
#define __PAGE_IMPL_H__
|
||||||
|
|
||||||
void send_counters(char port);
|
#include <stdbool.h>
|
||||||
|
|
||||||
|
extern __code uint8_t * __code HTTP_RESPONCE_TXT;
|
||||||
|
|
||||||
|
bool send_counters(uint8_t phys_port);
|
||||||
void send_status(void);
|
void send_status(void);
|
||||||
void send_vlan(uint16_t vlan);
|
void send_vlan(uint16_t vlan);
|
||||||
void send_basic_info(void);
|
void send_basic_info(void);
|
||||||
@@ -14,6 +18,7 @@ void send_mtu(void);
|
|||||||
void send_config(void);
|
void send_config(void);
|
||||||
void send_cmd_log(void);
|
void send_cmd_log(void);
|
||||||
void send_lag(void);
|
void send_lag(void);
|
||||||
|
void send_stp(void);
|
||||||
void send_vlanlist(void);
|
void send_vlanlist(void);
|
||||||
|
|
||||||
/* Convert only the lower nibble to ascii HEX char.
|
/* Convert only the lower nibble to ascii HEX char.
|
||||||
|
|||||||
@@ -213,7 +213,7 @@ uint8_t flash_read_status(void)
|
|||||||
* Reads bulk data of length len from the flash memory starging at address src
|
* Reads bulk data of length len from the flash memory starging at address src
|
||||||
* and writes the data into a buffer pointed to by dst in XMEM
|
* and writes the data into a buffer pointed to by dst in XMEM
|
||||||
*/
|
*/
|
||||||
void flash_read_bulk(register __xdata uint8_t *dst, __xdata uint32_t src, register uint16_t len)
|
void flash_read_bulk(__xdata uint8_t *dst, __xdata uint32_t src, uint16_t len)
|
||||||
{
|
{
|
||||||
short status;
|
short status;
|
||||||
do {
|
do {
|
||||||
|
|||||||
@@ -5,9 +5,23 @@
|
|||||||
#include "rtl837x_regs.h"
|
#include "rtl837x_regs.h"
|
||||||
#include "rtl837x_common.h"
|
#include "rtl837x_common.h"
|
||||||
|
|
||||||
#ifdef MACHINE_KP_9000_6XHML_X2
|
#if defined(MACHINE_KP_9000_6XHML_X2) || \
|
||||||
|
defined(MACHINE_KP_9000_6XH_X2_V1_1) || \
|
||||||
|
defined(MACHINE_KP_9000_6XHML_X2_V1_1) || \
|
||||||
|
defined(MACHINE_KP_9000_6XH_X2_V1_2) || \
|
||||||
|
defined(MACHINE_KP_9000_6XHML_X2_V1_2)
|
||||||
__code const struct machine machine = {
|
__code const struct machine machine = {
|
||||||
|
#if defined(MACHINE_KP_9000_6XH_X2_V1_1)
|
||||||
|
.machine_name = "keepLink KP-9000-6XH V1.1",
|
||||||
|
#elif defined(MACHINE_KP_9000_6XHML_X2_V1_1)
|
||||||
|
.machine_name = "keepLink KP-9000-6XHML V1.1",
|
||||||
|
#elif defined(MACHINE_KP_9000_6XH_X2_V1_2)
|
||||||
|
.machine_name = "keepLink KP-9000-6XH V1.2",
|
||||||
|
#elif defined(MACHINE_KP_9000_6XHML_X2_V1_2)
|
||||||
|
.machine_name = "keepLink KP-9000-6XHML V1.2",
|
||||||
|
#else
|
||||||
.machine_name = "keepLink KP-9000-6XHML-X2",
|
.machine_name = "keepLink KP-9000-6XHML-X2",
|
||||||
|
#endif
|
||||||
.isRTL8373 = 0,
|
.isRTL8373 = 0,
|
||||||
.min_port = 3,
|
.min_port = 3,
|
||||||
.max_port = 8,
|
.max_port = 8,
|
||||||
@@ -42,8 +56,6 @@ __code const struct machine machine = {
|
|||||||
},
|
},
|
||||||
};
|
};
|
||||||
|
|
||||||
void machine_custom_init(void) { }
|
|
||||||
|
|
||||||
#elif defined MACHINE_KP_9000_6XH_X
|
#elif defined MACHINE_KP_9000_6XH_X
|
||||||
__code const struct machine machine = {
|
__code const struct machine machine = {
|
||||||
.machine_name = "keepLink KP-9000-6XH-X",
|
.machine_name = "keepLink KP-9000-6XH-X",
|
||||||
@@ -72,11 +84,15 @@ __code const struct machine machine = {
|
|||||||
},
|
},
|
||||||
};
|
};
|
||||||
|
|
||||||
void machine_custom_init(void) { }
|
#elif defined(MACHINE_KP_9000_6XH_X2) || defined(MACHINE_KP_9000_6XH_X2_V2_1) || defined(MACHINE_KP_9000_6XHML_X2_V2_1)
|
||||||
|
|
||||||
#elif defined MACHINE_KP_9000_6XH_X2
|
|
||||||
__code const struct machine machine = {
|
__code const struct machine machine = {
|
||||||
|
#if defined(MACHINE_KP_9000_6XHML_X2_V2_1)
|
||||||
|
.machine_name = "keepLink KP-9000-6XHML V2.1",
|
||||||
|
#elif defined(MACHINE_KP_9000_6XH_X2_V2_1)
|
||||||
|
.machine_name = "keepLink KP-9000-6XH-X2 V2.1",
|
||||||
|
#else
|
||||||
.machine_name = "keepLink KP-9000-6XH-X2",
|
.machine_name = "keepLink KP-9000-6XH-X2",
|
||||||
|
#endif
|
||||||
.isRTL8373 = 0,
|
.isRTL8373 = 0,
|
||||||
.min_port = 3,
|
.min_port = 3,
|
||||||
.max_port = 8,
|
.max_port = 8,
|
||||||
@@ -122,10 +138,6 @@ __code const struct machine machine = {
|
|||||||
},
|
},
|
||||||
};
|
};
|
||||||
|
|
||||||
void machine_custom_init(void) {
|
|
||||||
reg_bit_set(RTL837X_REG_LED_GLB_IO_EN, 6);
|
|
||||||
}
|
|
||||||
|
|
||||||
#elif defined MACHINE_KP_9000_9XH_X_EU
|
#elif defined MACHINE_KP_9000_9XH_X_EU
|
||||||
__code const struct machine machine = {
|
__code const struct machine machine = {
|
||||||
.machine_name = "keepLink KP-9000-9XH-X-EU",
|
.machine_name = "keepLink KP-9000-9XH-X-EU",
|
||||||
@@ -151,8 +163,6 @@ __code const struct machine machine = {
|
|||||||
},
|
},
|
||||||
};
|
};
|
||||||
|
|
||||||
void machine_custom_init(void) { }
|
|
||||||
|
|
||||||
#elif defined MACHINE_KP_9000_9XHML_X_V2_2
|
#elif defined MACHINE_KP_9000_9XHML_X_V2_2
|
||||||
__code const struct machine machine = {
|
__code const struct machine machine = {
|
||||||
.machine_name = "keepLink KP-9000-9XHML-X V2.2",
|
.machine_name = "keepLink KP-9000-9XHML-X V2.2",
|
||||||
@@ -204,8 +214,6 @@ __code const struct machine machine = {
|
|||||||
},
|
},
|
||||||
};
|
};
|
||||||
|
|
||||||
void machine_custom_init(void) { }
|
|
||||||
|
|
||||||
#elif defined MACHINE_KP_9000_9XHML_X_V3_1
|
#elif defined MACHINE_KP_9000_9XHML_X_V3_1
|
||||||
__code const struct machine machine = {
|
__code const struct machine machine = {
|
||||||
.machine_name = "keepLink KP-9000-9XHML-X V3.1",
|
.machine_name = "keepLink KP-9000-9XHML-X V3.1",
|
||||||
@@ -241,8 +249,6 @@ __code const struct machine machine = {
|
|||||||
0x1a, 0x19, 0x1d, 0x1e, 0x1c, 0x1d, 0x20, 0x21},
|
0x1a, 0x19, 0x1d, 0x1e, 0x1c, 0x1d, 0x20, 0x21},
|
||||||
};
|
};
|
||||||
|
|
||||||
void machine_custom_init(void) { }
|
|
||||||
|
|
||||||
#elif defined MACHINE_SWGT024_V2_0_MANAGED
|
#elif defined MACHINE_SWGT024_V2_0_MANAGED
|
||||||
__code const struct machine machine = {
|
__code const struct machine machine = {
|
||||||
.machine_name = "SWGT024 V2.0 Managed",
|
.machine_name = "SWGT024 V2.0 Managed",
|
||||||
@@ -284,8 +290,6 @@ __code const struct machine machine = {
|
|||||||
},
|
},
|
||||||
};
|
};
|
||||||
|
|
||||||
void machine_custom_init(void) { }
|
|
||||||
|
|
||||||
#elif defined MACHINE_SWGT024_V2_0_UNMANAGED
|
#elif defined MACHINE_SWGT024_V2_0_UNMANAGED
|
||||||
__code const struct machine machine = {
|
__code const struct machine machine = {
|
||||||
.machine_name = "SWGT024 V2.0 Unmanaged",
|
.machine_name = "SWGT024 V2.0 Unmanaged",
|
||||||
@@ -327,8 +331,6 @@ __code const struct machine machine = {
|
|||||||
},
|
},
|
||||||
};
|
};
|
||||||
|
|
||||||
void machine_custom_init(void) { }
|
|
||||||
|
|
||||||
#elif defined MACHINE_SWTG018AS_A_V_2_0
|
#elif defined MACHINE_SWTG018AS_A_V_2_0
|
||||||
__code const struct machine machine = {
|
__code const struct machine machine = {
|
||||||
.machine_name = "SWTG018AS-A V2.0",
|
.machine_name = "SWTG018AS-A V2.0",
|
||||||
@@ -344,7 +346,7 @@ __code const struct machine machine = {
|
|||||||
.sfp_port[0].pin_tx_disable = GPIO_NA,
|
.sfp_port[0].pin_tx_disable = GPIO_NA,
|
||||||
.sfp_port[0].sds = 1,
|
.sfp_port[0].sds = 1,
|
||||||
.sfp_port[0].i2c = { .sda = GPIO39_I2C_SDA4, .scl = GPIO40_I2C_SCL3_MDC1 },
|
.sfp_port[0].i2c = { .sda = GPIO39_I2C_SDA4, .scl = GPIO40_I2C_SCL3_MDC1 },
|
||||||
.reset_pin = GPIO_NA,
|
.reset_pin = GPIO48_I2C_SCL1,
|
||||||
.high_leds = { .mux = LED_27 | LED_29, .enable = LED_28_SYS | LED_29 },
|
.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},
|
.port_led_set = { 0, 0, 0, 0, 0, 0, 0, 0, 1},
|
||||||
.led_sets = {
|
.led_sets = {
|
||||||
@@ -368,8 +370,6 @@ __code const struct machine machine = {
|
|||||||
0x1d, 0x20, 0x21 },
|
0x1d, 0x20, 0x21 },
|
||||||
};
|
};
|
||||||
|
|
||||||
void machine_custom_init(void) { }
|
|
||||||
|
|
||||||
#elif defined MACHINE_HG0402XG_V1_1
|
#elif defined MACHINE_HG0402XG_V1_1
|
||||||
__code const struct machine machine = {
|
__code const struct machine machine = {
|
||||||
.machine_name = "HG0402XG V1.1",
|
.machine_name = "HG0402XG V1.1",
|
||||||
@@ -408,13 +408,12 @@ __code const struct machine machine = {
|
|||||||
},
|
},
|
||||||
};
|
};
|
||||||
|
|
||||||
void machine_custom_init(void) { }
|
|
||||||
|
|
||||||
#elif defined MACHINE_SWTGW218AS
|
#elif defined MACHINE_SWTGW218AS
|
||||||
|
|
||||||
__code const struct machine machine = {
|
__code const struct machine machine = {
|
||||||
.machine_name = "SWTGW218AS 8+1 Managed Switch",
|
.machine_name = "SWTGW218AS 8+1 Managed Switch",
|
||||||
.isRTL8373 = 1,
|
.isRTL8373 = 1,
|
||||||
|
.mac_flash_offset = 0x1FC000,
|
||||||
.min_port = 0,
|
.min_port = 0,
|
||||||
.max_port = 8,
|
.max_port = 8,
|
||||||
.n_sfp = 1,
|
.n_sfp = 1,
|
||||||
@@ -442,11 +441,53 @@ __code const struct machine machine = {
|
|||||||
},
|
},
|
||||||
};
|
};
|
||||||
|
|
||||||
void machine_custom_init(void) { }
|
#elif defined MACHINE_PCB_SWTG018AS_V2_1_0 // Sold as Sodola SL902 / Horaco "SWTGW218AS"; the SWTGW218AS label also covers other PCBs with different SFP and LED wiring (see MACHINE_SWTGW218AS)
|
||||||
|
|
||||||
|
__code const struct machine machine = {
|
||||||
|
.machine_name = "SWTGW218AS (SWTG018AS-V2.1.0)",
|
||||||
|
.isRTL8373 = 1,
|
||||||
|
.mac_flash_offset = 0x1FC000,
|
||||||
|
.min_port = 0,
|
||||||
|
.max_port = 8,
|
||||||
|
.n_sfp = 1,
|
||||||
|
.log_to_phys_port = {1, 2, 3, 4, 5, 6, 7, 8, 9},
|
||||||
|
.phys_to_log_port = {0, 1, 2, 3, 4, 5, 6, 7, 8},
|
||||||
|
.is_sfp = {0, 0, 0, 0, 0, 0, 0, 0, 1},
|
||||||
|
.sfp_port[0].pin_detect = GPIO38, // pulled low on module insert
|
||||||
|
.sfp_port[0].pin_los = GPIO_NA, // no LOS pin wired
|
||||||
|
.sfp_port[0].pin_tx_disable = GPIO_NA,
|
||||||
|
.sfp_port[0].sds = 1,
|
||||||
|
.sfp_port[0].i2c = { .sda = GPIO39_I2C_SDA4, .scl = GPIO40_I2C_SCL3_MDC1 },
|
||||||
|
.reset_pin = GPIO54_ACL_BIT2_EN,
|
||||||
|
.high_leds = { .mux = LED_27 | LED_28_SYS | LED_29, .enable = LED_28_SYS | LED_29 },
|
||||||
|
.port_led_set = { 0, 0, 0, 0, 0, 0, 0, 0, 1},
|
||||||
|
// LED wiring matches the SWTG018AS-A V2.0 (same PCB family)
|
||||||
|
.led_sets = {
|
||||||
|
{ /* RJ45: First LED, yellow, second LED: green */
|
||||||
|
LEDS_2G5 | LEDS_LINK,
|
||||||
|
LEDS_2G5 | LEDS_1G | LEDS_100M | LEDS_10M | LEDS_LINK | LEDS_ACT,
|
||||||
|
0,
|
||||||
|
0,
|
||||||
|
}, { /* SFP set (superseded by the raw register override in machine_custom_init) */
|
||||||
|
LEDS_2G5 | LEDS_1G | LEDS_100M | LEDS_10M | LEDS_LINK | LEDS_ACT | LEDS_10G,
|
||||||
|
0,
|
||||||
|
0,
|
||||||
|
0,
|
||||||
|
}},
|
||||||
|
.led_mux_custom = 1,
|
||||||
|
.led_mux = { 0x00, 0x01, 0x04, 0x05, 0x08, // 65e0
|
||||||
|
0x09, 0x0c, 0x09, 0x0d, 0x10, // 65e4
|
||||||
|
0x11, 0x0e, 0x14, 0x11, 0x12, // 65e8
|
||||||
|
0x15, 0x15, 0x16, 0x18, 0x19, // 65ec
|
||||||
|
0x1a, 0x19, 0x1d, 0x1e, 0x1c, // 65f0
|
||||||
|
0x1d, 0x20, 0x21 },
|
||||||
|
};
|
||||||
|
|
||||||
#elif defined MACHINE_LIANGUO_ZX_SWTGW215AS // Has PCB branded PCB-SWTG115AS-V2.0 but is labeled and reports as a ZX-SWTGW215AS, seems to be identical to the "real" ZX-SWTGW215AS except for the LEDs
|
#elif defined MACHINE_LIANGUO_ZX_SWTGW215AS // Has PCB branded PCB-SWTG115AS-V2.0 but is labeled and reports as a ZX-SWTGW215AS, seems to be identical to the "real" ZX-SWTGW215AS except for the LEDs
|
||||||
__code const struct machine machine = {
|
__code const struct machine machine = {
|
||||||
.machine_name = "Lianguo ZX-SWTGW215AS",
|
.machine_name = "Lianguo ZX-SWTGW215AS",
|
||||||
.isRTL8373 = 0,
|
.isRTL8373 = 0,
|
||||||
|
.mac_flash_offset = 0x1FC000,
|
||||||
.min_port = 3,
|
.min_port = 3,
|
||||||
.max_port = 8,
|
.max_port = 8,
|
||||||
.n_sfp = 1,
|
.n_sfp = 1,
|
||||||
@@ -475,8 +516,6 @@ __code const struct machine machine = {
|
|||||||
.led_mux_custom = 0,
|
.led_mux_custom = 0,
|
||||||
};
|
};
|
||||||
|
|
||||||
void machine_custom_init(void) { }
|
|
||||||
|
|
||||||
#elif defined MACHINE_DEFAULT_8C_1SFP
|
#elif defined MACHINE_DEFAULT_8C_1SFP
|
||||||
__code const struct machine machine = {
|
__code const struct machine machine = {
|
||||||
.machine_name = "8+1 SFP Port Switch",
|
.machine_name = "8+1 SFP Port Switch",
|
||||||
@@ -502,8 +541,6 @@ __code const struct machine machine = {
|
|||||||
},
|
},
|
||||||
};
|
};
|
||||||
|
|
||||||
void machine_custom_init(void) { }
|
|
||||||
|
|
||||||
#elif defined MACHINE_TRENDNET_TEG_S562
|
#elif defined MACHINE_TRENDNET_TEG_S562
|
||||||
__code const struct machine machine = {
|
__code const struct machine machine = {
|
||||||
.machine_name = "Trendnet TEG-S562",
|
.machine_name = "Trendnet TEG-S562",
|
||||||
@@ -543,8 +580,6 @@ __code const struct machine machine = {
|
|||||||
|
|
||||||
};
|
};
|
||||||
|
|
||||||
void machine_custom_init(void) { }
|
|
||||||
|
|
||||||
#elif defined(MACHINE_PCB_K0402WS_V3) || defined(MACHINE_HI_K0402WS) // Sold as a variety of devices, see doc/
|
#elif defined(MACHINE_PCB_K0402WS_V3) || defined(MACHINE_HI_K0402WS) // Sold as a variety of devices, see doc/
|
||||||
__code const struct machine machine = {
|
__code const struct machine machine = {
|
||||||
.machine_name = "PCB-K0402WS-V3.0",
|
.machine_name = "PCB-K0402WS-V3.0",
|
||||||
@@ -591,10 +626,6 @@ __code const struct machine machine = {
|
|||||||
},
|
},
|
||||||
};
|
};
|
||||||
|
|
||||||
void machine_custom_init(void) {
|
|
||||||
reg_bit_set(RTL837X_REG_LED_GLB_IO_EN, 6);
|
|
||||||
}
|
|
||||||
|
|
||||||
#elif defined MACHINE_K0501W_V2_0
|
#elif defined MACHINE_K0501W_V2_0
|
||||||
__code const struct machine machine = {
|
__code const struct machine machine = {
|
||||||
.machine_name = "K0501W V2.0",
|
.machine_name = "K0501W V2.0",
|
||||||
@@ -629,8 +660,6 @@ __code const struct machine machine = {
|
|||||||
},
|
},
|
||||||
};
|
};
|
||||||
|
|
||||||
void machine_custom_init(void) { }
|
|
||||||
|
|
||||||
#elif defined MACHINE_ZX310S_4T2XH
|
#elif defined MACHINE_ZX310S_4T2XH
|
||||||
__code const struct machine machine = {
|
__code const struct machine machine = {
|
||||||
.machine_name = "ZX310S-4T2XH",
|
.machine_name = "ZX310S-4T2XH",
|
||||||
@@ -674,8 +703,6 @@ __code const struct machine machine = {
|
|||||||
},
|
},
|
||||||
};
|
};
|
||||||
|
|
||||||
void machine_custom_init(void) { }
|
|
||||||
|
|
||||||
#elif defined MACHINE_STEAMEMO_IG204_V1
|
#elif defined MACHINE_STEAMEMO_IG204_V1
|
||||||
__code const struct machine machine = {
|
__code const struct machine machine = {
|
||||||
.machine_name = "Steamemo IG204 V1",
|
.machine_name = "Steamemo IG204 V1",
|
||||||
@@ -726,8 +753,6 @@ __code const struct machine machine = {
|
|||||||
},
|
},
|
||||||
};
|
};
|
||||||
|
|
||||||
void machine_custom_init(void) { }
|
|
||||||
|
|
||||||
#elif defined MACHINE_HI_K0801WS
|
#elif defined MACHINE_HI_K0801WS
|
||||||
__code const struct machine machine = {
|
__code const struct machine machine = {
|
||||||
.machine_name = "Hi-Source HI-k0801WS",
|
.machine_name = "Hi-Source HI-k0801WS",
|
||||||
@@ -777,11 +802,13 @@ __code const struct machine machine = {
|
|||||||
},
|
},
|
||||||
};
|
};
|
||||||
|
|
||||||
void machine_custom_init(void) { }
|
#elif defined(MACHINE_PCB_K0402WS_V2) || defined(MACHINE_FNS1200P)
|
||||||
|
|
||||||
#elif defined MACHINE_FNS1200P
|
|
||||||
__code const struct machine machine = {
|
__code const struct machine machine = {
|
||||||
|
#if defined(MACHINE_PCB_K0402WS_V2)
|
||||||
|
.machine_name = "PCB-K0402WS-V2.0",
|
||||||
|
#else
|
||||||
.machine_name = "FNS-1200P",
|
.machine_name = "FNS-1200P",
|
||||||
|
#endif
|
||||||
.isRTL8373 = 0,
|
.isRTL8373 = 0,
|
||||||
.min_port = 3,
|
.min_port = 3,
|
||||||
.max_port = 8,
|
.max_port = 8,
|
||||||
@@ -834,11 +861,6 @@ __code const struct machine machine = {
|
|||||||
},
|
},
|
||||||
};
|
};
|
||||||
|
|
||||||
void machine_custom_init(void)
|
|
||||||
{
|
|
||||||
reg_bit_set(RTL837X_REG_LED_GLB_IO_EN, 6);
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
#elif defined MACHINE_PCB_SWTG024AS_A_2_0_1
|
#elif defined MACHINE_PCB_SWTG024AS_A_2_0_1
|
||||||
__code const struct machine machine = {
|
__code const struct machine machine = {
|
||||||
@@ -888,13 +910,91 @@ __code const struct machine machine = {
|
|||||||
},
|
},
|
||||||
};
|
};
|
||||||
|
|
||||||
void machine_custom_init(void)
|
#elif defined MACHINE_SWTG024AS_A_2_0_1_5C_1SFP
|
||||||
{
|
__code const struct machine machine = {
|
||||||
reg_bit_set(RTL837X_REG_LED_GLB_IO_EN, 6);
|
.machine_name = "SWTG024AS-A-V2.0.1-5C-1SFP",
|
||||||
reg_bit_set(RTL837X_REG_LED_MODE, 17);
|
.isRTL8373 = 0,
|
||||||
reg_bit_clear(RTL837X_REG_LED_MODE, 9);
|
.min_port = 3,
|
||||||
reg_bit_clear(RTL837X_REG_LED_MODE, 7);
|
.max_port = 8,
|
||||||
}
|
.n_sfp = 1,
|
||||||
|
.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, 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 = { .sda = GPIO39_I2C_SDA4, .scl = 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, 0, 0, 0, 0, 0, 1},
|
||||||
|
/* Ports 1-5 RJ45 use set 0, port 9 SFP uses set 1
|
||||||
|
* Ports 1-5: Green: 2.5GBit, Amber: 10/100/1000MBit
|
||||||
|
* SFP-port: Blue: 10GBit, Green: 100MBit-2.5GBit
|
||||||
|
*/
|
||||||
|
.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
|
||||||
|
},
|
||||||
|
},
|
||||||
|
.led_mux_custom = 1,
|
||||||
|
.led_mux = {
|
||||||
|
0x00,0x01,0x04,0x05,0x08,0x09,0x0c,0x3f,0x0d,0x10,0x11,0x0e,0x14,0x11,0x12,0x15,0x15,0x16,0x18,0x19,0x1a,0x19,0x1d,0x1e,0x1c,0x1d,0x20,0x21
|
||||||
|
},
|
||||||
|
};
|
||||||
|
|
||||||
|
#elif defined MACHINE_SWTG024AS_V2_0
|
||||||
|
__code const struct machine machine = {
|
||||||
|
.machine_name = "SWTG024AS-V2.0",
|
||||||
|
.isRTL8373 = 0,
|
||||||
|
.min_port = 3,
|
||||||
|
.max_port = 8,
|
||||||
|
.n_sfp = 1,
|
||||||
|
.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, 0, 0, 0, 0, 0, 1},
|
||||||
|
.sfp_port[0].pin_detect = GPIO30_ACL_BIT3_EN,
|
||||||
|
.sfp_port[0].pin_los = GPIO37,
|
||||||
|
.sfp_port[0].pin_tx_disable = GPIO_NA,
|
||||||
|
.sfp_port[0].sds = 1,
|
||||||
|
.sfp_port[0].i2c = { .sda = GPIO39_I2C_SDA4, .scl = 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, 0, 0, 0, 0, 0, 1},
|
||||||
|
/* Ports 1-5 RJ45 use set 0, port 9 SFP uses set 1
|
||||||
|
* Ports 1-5: Green: 2.5GBit, Amber: 10/100/1000MBit
|
||||||
|
* SFP-port: Blue: 10GBit, Amber: 100MBit-2.5GBit
|
||||||
|
*/
|
||||||
|
.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
|
||||||
|
},
|
||||||
|
},
|
||||||
|
.led_mux_custom = 1,
|
||||||
|
.led_mux = {
|
||||||
|
0x00,0x01,0x04,0x05,0x08,0x09,0x0c,0x3f,0x0d,0x10,0x11,0x0e,0x14,0x11,0x12,0x15,0x15,0x16,0x18,0x19,0x1a,0x19,0x1d,0x1e,0x1c,0x1d,0x20,0x21
|
||||||
|
},
|
||||||
|
};
|
||||||
|
|
||||||
#elif defined MACHINE_ZX310S_4T2XT
|
#elif defined MACHINE_ZX310S_4T2XT
|
||||||
__code const struct machine machine = {
|
__code const struct machine machine = {
|
||||||
@@ -933,11 +1033,116 @@ __code const struct machine machine = {
|
|||||||
},
|
},
|
||||||
};
|
};
|
||||||
|
|
||||||
void machine_custom_init(void) {
|
#elif defined MACHINE_FG_4GT_2SX_V2_0
|
||||||
// For this device, the reset value of RTL837X_PIN_MUX_0 is 0x30000000,
|
__code const struct machine machine = {
|
||||||
// which would disables all LEDS, enable them manually:
|
.machine_name = "FG-4GT-2SX_V2.0",
|
||||||
REG_SET(RTL837X_PIN_MUX_0, 0x30db68bf);
|
.isRTL8373 = 0,
|
||||||
}
|
.min_port = 3,
|
||||||
|
.max_port = 8,
|
||||||
|
.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},
|
||||||
|
.is_sfp = {0, 0, 0, 2, 0, 0, 0, 0, 1},
|
||||||
|
|
||||||
|
// Left SFP port
|
||||||
|
.sfp_port[0].pin_detect = GPIO38,
|
||||||
|
.sfp_port[0].pin_los = GPIO_NA,
|
||||||
|
.sfp_port[0].sds = 1,
|
||||||
|
.sfp_port[0].i2c = { .sda = GPIO39_I2C_SDA4, .scl = GPIO40_I2C_SCL3_MDC1 },
|
||||||
|
|
||||||
|
// Right SFP port
|
||||||
|
.sfp_port[1].pin_detect = GPIO37,
|
||||||
|
.sfp_port[1].pin_los = GPIO_NA,
|
||||||
|
.sfp_port[1].sds = 0,
|
||||||
|
.sfp_port[1].i2c = { .sda = GPIO41_I2C_SDA3_MDIO1, .scl = GPIO40_I2C_SCL3_MDC1 },
|
||||||
|
|
||||||
|
.reset_pin = GPIO_NA,
|
||||||
|
.high_leds = { .mux = LED_27 | LED_28_SYS | LED_29, .enable = LED_28_SYS | LED_29 },
|
||||||
|
.port_led_set = { 0, 0, 0, 1, 0, 0, 0, 0, 1},
|
||||||
|
/* Ports 1-4 RJ45 use set 0, port 5-6 SFP uses set 1
|
||||||
|
* Ports 1-4: Green: 2.5GBit, Amber: 10/100/1000MBit
|
||||||
|
* Ports 5-6: Green: 100MBit-10GBit
|
||||||
|
*/
|
||||||
|
.led_sets = {
|
||||||
|
{
|
||||||
|
LEDS_2G5 | LEDS_LINK | LEDS_ACT,
|
||||||
|
LEDS_1G | LEDS_100M | LEDS_10M | LEDS_LINK | LEDS_ACT,
|
||||||
|
0,
|
||||||
|
LEDS_2G5 | LEDS_LINK | LEDS_ACT
|
||||||
|
},
|
||||||
|
{
|
||||||
|
LEDS_10G | LEDS_5G | LEDS_2G5 | LEDS_1G | LEDS_100M | LEDS_10M | LEDS_LINK | LEDS_ACT,
|
||||||
|
LEDS_10G | LEDS_LINK,
|
||||||
|
0,
|
||||||
|
LEDS_COL | LEDS_DUPLEX
|
||||||
|
},
|
||||||
|
{
|
||||||
|
LEDS_1G | LEDS_100M | LEDS_10M | LEDS_LINK | LEDS_ACT,
|
||||||
|
LEDS_2G5 | LEDS_1G | LEDS_LINK,
|
||||||
|
LEDS_5G | LEDS_2G5 | LEDS_LINK | LEDS_ACT,
|
||||||
|
LEDS_10G | LEDS_LINK | LEDS_ACT
|
||||||
|
},
|
||||||
|
{
|
||||||
|
LEDS_TX,
|
||||||
|
LEDS_RX,
|
||||||
|
LEDS_10G | LEDS_TWO_PAIR_5G | LEDS_5G | LEDS_TWO_PAIR_2G5 |
|
||||||
|
LEDS_2G5 | LEDS_TWO_PAIR_1G | LEDS_1G | LEDS_500M | LEDS_100M | LEDS_10M | LEDS_ACT,
|
||||||
|
LEDS_10G | LEDS_TWO_PAIR_5G | LEDS_5G | LEDS_TWO_PAIR_2G5 |
|
||||||
|
LEDS_2G5 | LEDS_TWO_PAIR_1G | LEDS_1G | LEDS_500M | LEDS_100M | LEDS_10M | LEDS_LINK
|
||||||
|
},
|
||||||
|
},
|
||||||
|
.led_mux_custom = 1,
|
||||||
|
.led_mux = {
|
||||||
|
0x0c, 0x0d, 0x0e, 0x10, 0x11, 0x12, 0x14, 0x3f, 0x15, 0x16,
|
||||||
|
0x18, 0x0e, 0x19, 0x11, 0x12, 0x1a, 0x15, 0x16, 0x1c, 0x19,
|
||||||
|
0x1a, 0x1d, 0x1d, 0x1e, 0x1e, 0x20, 0x21, 0x22
|
||||||
|
},
|
||||||
|
};
|
||||||
|
|
||||||
|
#elif defined MACHINE_FG_8GT_1SX
|
||||||
|
__code const struct machine machine = {
|
||||||
|
.machine_name = "FG-8GT-1SX",
|
||||||
|
.isRTL8373 = 1,
|
||||||
|
.min_port = 0,
|
||||||
|
.max_port = 8,
|
||||||
|
.n_sfp = 1,
|
||||||
|
.log_to_phys_port = {1, 2, 3, 4, 5, 6, 7, 8, 9},
|
||||||
|
.phys_to_log_port = {0, 1, 2, 3, 4, 5, 6, 7, 8},
|
||||||
|
.is_sfp = {0, 0, 0, 0, 0, 0, 0, 0, 1},
|
||||||
|
.sfp_port[0].pin_detect = 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 = { .sda = GPIO39_I2C_SDA4, .scl = GPIO40_I2C_SCL3_MDC1 },
|
||||||
|
.reset_pin = GPIO_NA,
|
||||||
|
.high_leds = { .mux = LED_27 | LED_28_SYS | LED_29, .enable = LED_28_SYS | LED_29 },
|
||||||
|
.port_led_set = {0, 0, 0, 0, 0, 0, 0, 0, 1},
|
||||||
|
.led_sets = {
|
||||||
|
/*
|
||||||
|
Ports 1-8: Left Amber 10/100/1000MBit (Vendor no 10M, but we add it)
|
||||||
|
Right Green 2.5GBit
|
||||||
|
SFP-port: Any speed
|
||||||
|
*/
|
||||||
|
{
|
||||||
|
LEDS_2G5 | LEDS_LINK | LEDS_ACT,
|
||||||
|
LEDS_1G | LEDS_100M | LEDS_10M | LEDS_LINK | LEDS_ACT,
|
||||||
|
0,
|
||||||
|
0,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
LEDS_10G | LEDS_2G5 | LEDS_1G | LEDS_100M | LEDS_10M | LEDS_LINK | LEDS_ACT,
|
||||||
|
0,
|
||||||
|
0,
|
||||||
|
0,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
.led_mux_custom = 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,
|
||||||
|
},
|
||||||
|
};
|
||||||
|
|
||||||
#else
|
#else
|
||||||
#error "Please select a machine type in machine.h"
|
#error "Please select a machine type in machine.h"
|
||||||
|
|||||||
@@ -6,9 +6,19 @@
|
|||||||
/*
|
/*
|
||||||
* Select your machine type below
|
* Select your machine type below
|
||||||
*/
|
*/
|
||||||
|
// Legacy KP-9000 4+2 targets. Prefer the PCB-revision-specific targets below.
|
||||||
// #define MACHINE_KP_9000_6XHML_X2
|
// #define MACHINE_KP_9000_6XHML_X2
|
||||||
// #define MACHINE_KP_9000_6XH_X
|
|
||||||
// #define MACHINE_KP_9000_6XH_X2
|
// #define MACHINE_KP_9000_6XH_X2
|
||||||
|
|
||||||
|
// KP-9000 4+2 targets by PCB silkscreen revision.
|
||||||
|
// #define MACHINE_KP_9000_6XH_X2_V1_1
|
||||||
|
// #define MACHINE_KP_9000_6XHML_X2_V1_1
|
||||||
|
// #define MACHINE_KP_9000_6XH_X2_V1_2
|
||||||
|
// #define MACHINE_KP_9000_6XHML_X2_V1_2
|
||||||
|
// #define MACHINE_KP_9000_6XH_X2_V2_1
|
||||||
|
// #define MACHINE_KP_9000_6XHML_X2_V2_1
|
||||||
|
|
||||||
|
// #define MACHINE_KP_9000_6XH_X
|
||||||
// #define MACHINE_KP_9000_9XH_X_EU
|
// #define MACHINE_KP_9000_9XH_X_EU
|
||||||
// #define MACHINE_KP_9000_9XHML_X_V2_2
|
// #define MACHINE_KP_9000_9XHML_X_V2_2
|
||||||
// #define MACHINE_KP_9000_9XHML_X_V3_1
|
// #define MACHINE_KP_9000_9XHML_X_V3_1
|
||||||
@@ -18,7 +28,9 @@
|
|||||||
// #define MACHINE_HG0402XG_V1_1
|
// #define MACHINE_HG0402XG_V1_1
|
||||||
// #define MACHINE_SWTG018AS_A_V_2_0
|
// #define MACHINE_SWTG018AS_A_V_2_0
|
||||||
// #define MACHINE_SWTGW218AS
|
// #define MACHINE_SWTGW218AS
|
||||||
|
// #define MACHINE_PCB_SWTG018AS_V2_1_0
|
||||||
// #define MACHINE_PCB_K0402WS_V3
|
// #define MACHINE_PCB_K0402WS_V3
|
||||||
|
// #define MACHINE_PCB_K0402WS_V2
|
||||||
// #define MACHINE_K0501W_V2_0
|
// #define MACHINE_K0501W_V2_0
|
||||||
// #define MACHINE_LIANGUO_ZX_SWTGW215AS
|
// #define MACHINE_LIANGUO_ZX_SWTGW215AS
|
||||||
// #define MACHINE_ZX310S_4T2XH
|
// #define MACHINE_ZX310S_4T2XH
|
||||||
@@ -28,6 +40,10 @@
|
|||||||
// #define MACHINE_HI_K0801WS
|
// #define MACHINE_HI_K0801WS
|
||||||
// #define MACHINE_FNS1200P
|
// #define MACHINE_FNS1200P
|
||||||
// #define MACHINE_PCB_SWTG024AS_A_2_0_1
|
// #define MACHINE_PCB_SWTG024AS_A_2_0_1
|
||||||
|
// #define MACHINE_SWTG024AS_A_2_0_1_5C_1SFP
|
||||||
|
// #define MACHINE_SWTG024AS_V2_0
|
||||||
|
// #define MACHINE_FG_4GT_2SX_V2_0
|
||||||
|
// #define MACHINE_FG_8GT_1SX
|
||||||
|
|
||||||
typedef struct {
|
typedef struct {
|
||||||
// GPIO pins for SDA/SCL
|
// GPIO pins for SDA/SCL
|
||||||
@@ -82,6 +98,7 @@ typedef struct machine {
|
|||||||
uint32_t led_sets[4][4];
|
uint32_t led_sets[4][4];
|
||||||
uint8_t led_mux_custom;
|
uint8_t led_mux_custom;
|
||||||
uint8_t led_mux[28];
|
uint8_t led_mux[28];
|
||||||
|
uint32_t mac_flash_offset;
|
||||||
};
|
};
|
||||||
|
|
||||||
typedef struct machine_runtime
|
typedef struct machine_runtime
|
||||||
@@ -90,6 +107,6 @@ typedef struct machine_runtime
|
|||||||
uint8_t isN : 1;
|
uint8_t isN : 1;
|
||||||
};
|
};
|
||||||
|
|
||||||
void machine_custom_init(void);
|
void machine_custom_init(void) __banked;
|
||||||
|
|
||||||
#endif
|
#endif
|
||||||
@@ -0,0 +1,122 @@
|
|||||||
|
/*
|
||||||
|
* Per-machine one-shot boot hooks, hosted in BANK2 so board-specific
|
||||||
|
* tables and code do not consume the common bank.
|
||||||
|
*/
|
||||||
|
#include <stdint.h>
|
||||||
|
#include "machine.h"
|
||||||
|
#include "rtl837x_pins.h"
|
||||||
|
#include "rtl837x_leds.h"
|
||||||
|
#include "rtl837x_sfr.h"
|
||||||
|
#include "rtl837x_regs.h"
|
||||||
|
#include "rtl837x_common.h"
|
||||||
|
|
||||||
|
#pragma codeseg BANK2
|
||||||
|
#pragma constseg BANK2
|
||||||
|
|
||||||
|
#if defined(MACHINE_KP_9000_6XH_X2) || \
|
||||||
|
defined(MACHINE_KP_9000_6XH_X2_V2_1) || \
|
||||||
|
defined(MACHINE_KP_9000_6XHML_X2_V2_1)
|
||||||
|
void machine_custom_init(void) __banked
|
||||||
|
{
|
||||||
|
reg_bit_set(RTL837X_REG_LED_GLB_IO_EN, 6);
|
||||||
|
}
|
||||||
|
|
||||||
|
#elif defined MACHINE_PCB_SWTG018AS_V2_1_0
|
||||||
|
// Stock-firmware values for what the LED-set encoding cannot express: the
|
||||||
|
// bi-color SFP LED (blue pin at 10G) and the PIN_MUX_0 routing of that pin
|
||||||
|
// to the LED controller. Runs after leds_setup(), which covers the rest.
|
||||||
|
static __code const struct { uint16_t reg; uint32_t val; } custom_init_regs[] = {
|
||||||
|
{ RTL837X_REG_LED3_0_SET1, 0x00100000UL },
|
||||||
|
{ RTL837X_REG_LED1_0_SET1, 0x01400155UL },
|
||||||
|
{ RTL837X_REG_LED1_0_SET0, 0x01740141UL },
|
||||||
|
{ RTL837X_REG_LED_GLB_IO_EN, 0x7f24977fUL },
|
||||||
|
{ RTL837X_PIN_MUX_0, 0x20db6880UL },
|
||||||
|
};
|
||||||
|
|
||||||
|
void machine_custom_init(void) __banked
|
||||||
|
{
|
||||||
|
uint8_t i;
|
||||||
|
// REG_SET is a multi-statement macro without a do-while wrapper: braces required
|
||||||
|
for (i = 0; i < sizeof(custom_init_regs) / sizeof(custom_init_regs[0]); i++) {
|
||||||
|
REG_SET(custom_init_regs[i].reg, custom_init_regs[i].val);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#elif defined(MACHINE_PCB_K0402WS_V3) || defined(MACHINE_HI_K0402WS)
|
||||||
|
void machine_custom_init(void) __banked
|
||||||
|
{
|
||||||
|
reg_bit_set(RTL837X_REG_LED_GLB_IO_EN, 6);
|
||||||
|
}
|
||||||
|
|
||||||
|
#elif defined(MACHINE_PCB_K0402WS_V2) || defined(MACHINE_FNS1200P)
|
||||||
|
void machine_custom_init(void) __banked
|
||||||
|
{
|
||||||
|
reg_bit_set(RTL837X_REG_LED_GLB_IO_EN, 6);
|
||||||
|
}
|
||||||
|
|
||||||
|
#elif defined MACHINE_PCB_SWTG024AS_A_2_0_1
|
||||||
|
void machine_custom_init(void) __banked
|
||||||
|
{
|
||||||
|
reg_bit_set(RTL837X_REG_LED_GLB_IO_EN, 6);
|
||||||
|
reg_bit_set(RTL837X_REG_LED_MODE, 17);
|
||||||
|
reg_bit_clear(RTL837X_REG_LED_MODE, 9);
|
||||||
|
reg_bit_clear(RTL837X_REG_LED_MODE, 7);
|
||||||
|
}
|
||||||
|
|
||||||
|
#elif defined MACHINE_SWTG024AS_A_2_0_1_5C_1SFP
|
||||||
|
void machine_custom_init(void) __banked
|
||||||
|
{
|
||||||
|
uint16_t pval;
|
||||||
|
|
||||||
|
reg_bit_set(RTL837X_REG_LED_GLB_IO_EN, 6);
|
||||||
|
reg_bit_set(RTL837X_REG_LED_MODE, 17);
|
||||||
|
reg_bit_clear(RTL837X_REG_LED_MODE, 9);
|
||||||
|
reg_bit_clear(RTL837X_REG_LED_MODE, 7);
|
||||||
|
|
||||||
|
// OEM firmware sets these companion SDS0 polarity bits for the RTL8221B.
|
||||||
|
sds_read(0, 0, 0);
|
||||||
|
pval = SFR_DATA_U16;
|
||||||
|
sds_write_v(0, 0, 0, pval | 0x100);
|
||||||
|
|
||||||
|
sds_read(0, 6, 2);
|
||||||
|
pval = SFR_DATA_U16;
|
||||||
|
sds_write_v(0, 6, 2, pval | 0x4000);
|
||||||
|
}
|
||||||
|
|
||||||
|
#elif defined MACHINE_SWTG024AS_V2_0
|
||||||
|
void machine_custom_init(void) __banked
|
||||||
|
{
|
||||||
|
uint16_t pval;
|
||||||
|
|
||||||
|
reg_bit_set(RTL837X_REG_LED_GLB_IO_EN, 6);
|
||||||
|
reg_bit_set(RTL837X_REG_LED_MODE, 17);
|
||||||
|
reg_bit_clear(RTL837X_REG_LED_MODE, 9);
|
||||||
|
reg_bit_clear(RTL837X_REG_LED_MODE, 7);
|
||||||
|
|
||||||
|
// OEM firmware sets these companion SDS0 polarity bits for the RTL8221B.
|
||||||
|
sds_read(0, 0, 0);
|
||||||
|
pval = SFR_DATA_U16;
|
||||||
|
sds_write_v(0, 0, 0, pval | 0x100);
|
||||||
|
|
||||||
|
sds_read(0, 6, 2);
|
||||||
|
pval = SFR_DATA_U16;
|
||||||
|
sds_write_v(0, 6, 2, pval | 0x4000);
|
||||||
|
}
|
||||||
|
|
||||||
|
#elif defined MACHINE_ZX310S_4T2XT
|
||||||
|
void machine_custom_init(void) __banked
|
||||||
|
{
|
||||||
|
// For this device, the reset value of RTL837X_PIN_MUX_0 is 0x30000000,
|
||||||
|
// which would disables all LEDS, enable them manually:
|
||||||
|
REG_SET(RTL837X_PIN_MUX_0, 0x30db68bf);
|
||||||
|
}
|
||||||
|
|
||||||
|
#elif defined MACHINE_FG_4GT_2SX_V2_0
|
||||||
|
void machine_custom_init(void) __banked
|
||||||
|
{
|
||||||
|
REG_SET(RTL837X_REG_LED_GLB_IO_EN, 0x7624155b);
|
||||||
|
}
|
||||||
|
|
||||||
|
#else
|
||||||
|
void machine_custom_init(void) __banked { }
|
||||||
|
#endif
|
||||||
@@ -8,6 +8,7 @@
|
|||||||
#define SYS_TICK_HZ 200
|
#define SYS_TICK_HZ 200
|
||||||
|
|
||||||
#define CPU_PORT 9
|
#define CPU_PORT 9
|
||||||
|
#define NUL '\0'
|
||||||
|
|
||||||
// Define Port-masks for 9-port devices and 6-port devices
|
// Define Port-masks for 9-port devices and 6-port devices
|
||||||
#define PMASK_9 0x1ff
|
#define PMASK_9 0x1ff
|
||||||
@@ -36,6 +37,10 @@ extern __xdata uint8_t sbuf[SBUF_SIZE];
|
|||||||
// Size of the TCP Output buffer
|
// Size of the TCP Output buffer
|
||||||
#define TCP_OUTBUF_SIZE 2500
|
#define TCP_OUTBUF_SIZE 2500
|
||||||
|
|
||||||
|
// Appended when a captured command wrote more than the output buffer holds,
|
||||||
|
// so a clipped listing is visibly clipped instead of silently short.
|
||||||
|
#define CMD_TRUNCATED "\n[output truncated]\n"
|
||||||
|
|
||||||
// Size of the port name, including the terminating null byte
|
// Size of the port name, including the terminating null byte
|
||||||
#define PORT_NAME_SIZE 32
|
#define PORT_NAME_SIZE 32
|
||||||
|
|
||||||
@@ -71,6 +76,9 @@ struct vlan_tag {
|
|||||||
#define VLAN_TAG_SIZE (sizeof (struct vlan_tag))
|
#define VLAN_TAG_SIZE (sizeof (struct vlan_tag))
|
||||||
#define RTL_FRAME_TAG_ID 0x8899
|
#define RTL_FRAME_TAG_ID 0x8899
|
||||||
#define RTL_FRAME_TAG_VERSION 0x04
|
#define RTL_FRAME_TAG_VERSION 0x04
|
||||||
|
/* Bits of the tag's `flags` word, see doc/CpuPort.md. */
|
||||||
|
#define RTL_TAG_LEARN_DIS 0x0020 /* do not learn the source address from this frame */
|
||||||
|
#define RTL_TAG_KEEP 0x0080 /* keep the frame's 802.1Q tag format as injected */
|
||||||
|
|
||||||
// For TX, an 8 byte (plus 4 byte padding when when VLAN is enabled)
|
// For TX, an 8 byte (plus 4 byte padding when when VLAN is enabled)
|
||||||
// header describing the frame to be moved to the Asic is used
|
// header describing the frame to be moved to the Asic is used
|
||||||
@@ -115,20 +123,30 @@ struct flash_region_t {
|
|||||||
|
|
||||||
extern __xdata char port_names[9][PORT_NAME_SIZE];
|
extern __xdata char port_names[9][PORT_NAME_SIZE];
|
||||||
|
|
||||||
|
extern __xdata bool stp_enabled;
|
||||||
|
|
||||||
|
/* System hostname (device identity). Set via `hostname <text>` and the System
|
||||||
|
* Settings page, reported in /information.json. Other modules (e.g. LLDP, which
|
||||||
|
* advertises it as the System Name TLV) read it from here. */
|
||||||
|
extern __xdata char hostname[24];
|
||||||
|
|
||||||
extern __xdata uint8_t uip_buf[UIP_CONF_BUFFER_SIZE+2];
|
extern __xdata uint8_t uip_buf[UIP_CONF_BUFFER_SIZE+2];
|
||||||
extern __xdata struct uip_eth_addr uip_ethaddr;
|
extern __xdata struct uip_eth_addr uip_ethaddr;
|
||||||
|
|
||||||
// Headers for calls in the common code area (HOME/BANK0)
|
// Headers for calls in the common code area (HOME/BANK0)
|
||||||
void print_string_no_syslog(__code char *p);
|
void print_string_no_syslog(__code char *p);
|
||||||
|
void print_string_newline_no_syslog(__code char *p);
|
||||||
void print_string(__code char *p);
|
void print_string(__code char *p);
|
||||||
void print_string_x(__xdata char *p);
|
void print_string_x(__xdata char *p);
|
||||||
void print_long(uint32_t a);
|
void print_long(uint32_t a);
|
||||||
void print_short(uint16_t a);
|
void print_short(uint16_t a);
|
||||||
void print_byte(uint8_t a);
|
void print_byte(uint8_t a);
|
||||||
void itoa(uint8_t v);
|
void itoa(uint8_t v);
|
||||||
|
void itoa_short(uint16_t v);
|
||||||
void print_sfr_data(void);
|
void print_sfr_data(void);
|
||||||
void print_phy_data(void);
|
void print_phy_data(void);
|
||||||
void print_cmd_prompt(void);
|
void print_cmd_prompt(void);
|
||||||
|
void print_phys_port(uint8_t port);
|
||||||
void phy_write_mask(uint16_t phy_mask, uint8_t dev_id, uint16_t reg, uint16_t v);
|
void phy_write_mask(uint16_t phy_mask, uint8_t dev_id, uint16_t reg, uint16_t v);
|
||||||
void phy_write(uint8_t phy_id, uint8_t dev_id, uint16_t reg, uint16_t v);
|
void phy_write(uint8_t phy_id, uint8_t dev_id, uint16_t reg, uint16_t v);
|
||||||
void phy_read(uint8_t phy_id, uint8_t dev_id, uint16_t reg);
|
void phy_read(uint8_t phy_id, uint8_t dev_id, uint16_t reg);
|
||||||
@@ -144,7 +162,8 @@ void sleep(uint16_t t);
|
|||||||
void write_char_no_syslog(char c);
|
void write_char_no_syslog(char c);
|
||||||
void write_char(char c);
|
void write_char(char c);
|
||||||
void print_reg(uint16_t reg);
|
void print_reg(uint16_t reg);
|
||||||
uint8_t sfp_read_reg(uint8_t slot, uint8_t reg);
|
bool sfp_read_block(uint8_t slot, uint8_t reg, uint8_t len) __banked __reentrant;
|
||||||
|
extern __xdata uint8_t sfp_buf[16];
|
||||||
void reg_bit_set(uint16_t reg_addr, char bit);
|
void reg_bit_set(uint16_t reg_addr, char bit);
|
||||||
void reg_bit_clear(uint16_t reg_addr, char bit);
|
void reg_bit_clear(uint16_t reg_addr, char bit);
|
||||||
uint8_t reg_bit_test(uint16_t reg_addr, char bit);
|
uint8_t reg_bit_test(uint16_t reg_addr, char bit);
|
||||||
@@ -152,17 +171,20 @@ void sfr_mask_data(uint8_t n, uint8_t mask, uint8_t set);
|
|||||||
void sfr_set_zero(void);
|
void sfr_set_zero(void);
|
||||||
void reset_chip(void);
|
void reset_chip(void);
|
||||||
void memcpy(__xdata void * __xdata dst, __xdata const void * __xdata src, uint16_t len);
|
void memcpy(__xdata void * __xdata dst, __xdata const void * __xdata src, uint16_t len);
|
||||||
void memcpyc(register __xdata uint8_t *dst, register __code uint8_t *src, register uint16_t len);
|
void memcpyc(__xdata uint8_t *dst, __code uint8_t *src, uint16_t len);
|
||||||
void memset(register __xdata uint8_t *dst, register __xdata uint8_t v, register uint8_t len);
|
void memset(__xdata uint8_t *dst, __xdata uint8_t v, uint8_t len);
|
||||||
uint16_t strlen(register __code const char *s);
|
uint16_t strlen(__code const char *s);
|
||||||
uint16_t strlen_x(register __xdata const char *s);
|
uint16_t strlen_x(__xdata const char *s);
|
||||||
uint16_t strtox(register __xdata uint8_t *dst, register __code const char *s);
|
uint16_t strtox(__xdata uint8_t *dst, __code const char *s);
|
||||||
uint16_t strcpy(register __xdata uint8_t *dst, register const char *s);
|
uint16_t strcpy(__xdata uint8_t *dst, const char *s);
|
||||||
|
char strcmp(__xdata const uint8_t *a, __code const uint8_t *b);
|
||||||
|
bool strstart(__xdata const uint8_t *a, __code const uint8_t *b);
|
||||||
|
bool strstart_x(__xdata const uint8_t *a, __xdata const uint8_t *b);
|
||||||
void tcpip_output(void);
|
void tcpip_output(void);
|
||||||
uint8_t read_flash(uint8_t bank, __code uint8_t *addr);
|
uint8_t read_flash(uint8_t bank, __code uint8_t *addr);
|
||||||
void get_random_32(void);
|
void get_random_32(void);
|
||||||
void read_reg_timer(__xdata uint32_t * tmr);
|
void read_reg_timer(__xdata uint32_t * tmr);
|
||||||
void sfp_print_info(uint8_t sfp);
|
bool sfp_print_info(uint8_t sfp);
|
||||||
bool gpio_pin_test(uint8_t pin);
|
bool gpio_pin_test(uint8_t pin);
|
||||||
void set_sys_led_state(uint8_t state);
|
void set_sys_led_state(uint8_t state);
|
||||||
void sds_read(uint8_t sds_id, uint8_t page, uint8_t reg);
|
void sds_read(uint8_t sds_id, uint8_t page, uint8_t reg);
|
||||||
|
|||||||
@@ -16,6 +16,7 @@
|
|||||||
#include "machine.h"
|
#include "machine.h"
|
||||||
|
|
||||||
extern __code struct machine machine;
|
extern __code struct machine machine;
|
||||||
|
extern __xdata uint8_t igmpEnabled;
|
||||||
|
|
||||||
#include "uip.h"
|
#include "uip.h"
|
||||||
|
|
||||||
@@ -85,22 +86,21 @@ void igmp_setup(void) __banked
|
|||||||
{
|
{
|
||||||
uint8_t i;
|
uint8_t i;
|
||||||
print_string("igmp_setup called\n");
|
print_string("igmp_setup called\n");
|
||||||
|
igmpEnabled = 0;
|
||||||
// For now, forward all unkown IP-MC pkts (2 bits per port. 00: flood via floodmask, 01: drop, 10: trap, 11: to rport)
|
// For now, forward all unkown IP-MC pkts (2 bits per port. 00: flood via floodmask, 01: drop, 10: trap, 11: to rport)
|
||||||
REG_SET(RTL837X_IPV4_PORT_MC_LM_ACT, LOOKUP_MISS_FLOOD);
|
REG_SET(RTL837X_IPV4_PORT_MC_LM_ACT, LOOKUP_MISS_FLOOD);
|
||||||
REG_SET(RTL837X_IPV6_PORT_MC_LM_ACT, LOOKUP_MISS_FLOOD);
|
REG_SET(RTL837X_IPV6_PORT_MC_LM_ACT, LOOKUP_MISS_FLOOD);
|
||||||
|
|
||||||
// Define ports where unknown MC addresses are flooded to:
|
// Define ports where unknown MC addresses are flooded to:
|
||||||
REG_SET(RTL837X_IPV4_UNKN_MC_FLD_PMSK, machine_detected.isRTL8373? PMASK_9: PMASK_6);
|
uint16_t mask = PMASK_6;
|
||||||
REG_SET(RTL837X_IPV6_UNKN_MC_FLD_PMSK, machine_detected.isRTL8373? PMASK_9: PMASK_6);
|
if (machine_detected.isRTL8373)
|
||||||
|
mask = PMASK_9;
|
||||||
|
REG_SET(RTL837X_IPV4_UNKN_MC_FLD_PMSK, mask);
|
||||||
|
REG_SET(RTL837X_IPV6_UNKN_MC_FLD_PMSK, mask);
|
||||||
|
|
||||||
// Enable lookup of IPv4 MC addresses in table
|
// Enable lookup of IPv4 MC addresses in table
|
||||||
reg_bit_set(RTL837X_L2_CTRL, L2_CTRL_LUT_IPMC_HASH);
|
reg_bit_set(RTL837X_L2_CTRL, L2_CTRL_LUT_IPMC_HASH);
|
||||||
|
|
||||||
// Configure per-port IGMP configuration, bits 0-10 enable MC protocol snooping,
|
|
||||||
// bits 16-24 configure max MC group used by that port. For now all protocols are flooded (01)
|
|
||||||
for (i = machine.min_port; i <= machine.max_port; i++)
|
|
||||||
REG_SET(RTL837X_IGMP_PORT_CFG + (i << 2), 0x00ff7c15);
|
|
||||||
|
|
||||||
/* Configure per-port IGMP operations when protocol messages are received
|
/* Configure per-port IGMP operations when protocol messages are received
|
||||||
* bits 0-9 enable MC protocol snooping
|
* bits 0-9 enable MC protocol snooping
|
||||||
* bit 10: Enable dynamic router port learning
|
* bit 10: Enable dynamic router port learning
|
||||||
@@ -135,6 +135,7 @@ void igmp_setup(void) __banked
|
|||||||
void igmp_enable(void) __banked
|
void igmp_enable(void) __banked
|
||||||
{
|
{
|
||||||
print_string("igmp_enable called\n");
|
print_string("igmp_enable called\n");
|
||||||
|
igmpEnabled = 1;
|
||||||
// Configure trapping of unhandled IGMP protocol packets to CPU
|
// Configure trapping of unhandled IGMP protocol packets to CPU
|
||||||
REG_SET(RTL837X_IGMP_TRAP_CFG, IGMP_CPU_PORT | IGMP_TRAP_PRIORITY);
|
REG_SET(RTL837X_IGMP_TRAP_CFG, IGMP_CPU_PORT | IGMP_TRAP_PRIORITY);
|
||||||
|
|
||||||
@@ -223,7 +224,7 @@ void igmp_packet_handler(void) __banked
|
|||||||
#endif
|
#endif
|
||||||
|
|
||||||
#ifdef IPMC_USES_L3MC
|
#ifdef IPMC_USES_L3MC
|
||||||
memset(&entry, 0, sizeof(struct ipmc_table_entry));
|
memset((__xdata uint8_t *)&entry, 0, sizeof(struct ipmc_table_entry));
|
||||||
// For IPv4 MC, the Source-IP is 0.0.0.0
|
// For IPv4 MC, the Source-IP is 0.0.0.0
|
||||||
entry.sip[0] = 0x00; entry.sip[1] = 0x00; entry.sip[2] = 0x00; entry.sip[3] = 0x00;
|
entry.sip[0] = 0x00; entry.sip[1] = 0x00; entry.sip[2] = 0x00; entry.sip[3] = 0x00;
|
||||||
// For IPv4 MC, the Destination-IP is the IPv4 MC address
|
// For IPv4 MC, the Destination-IP is the IPv4 MC address
|
||||||
@@ -235,7 +236,7 @@ void igmp_packet_handler(void) __banked
|
|||||||
* yy = MC_IP[2]
|
* yy = MC_IP[2]
|
||||||
* zz = MC_IP[3]
|
* zz = MC_IP[3]
|
||||||
*/
|
*/
|
||||||
memset(&entry, 0, sizeof(struct l2mc_table_entry));
|
memset((__xdata uint8_t *)&entry, 0, sizeof(struct l2mc_table_entry));
|
||||||
entry.mac[0] = 0x01; entry.mac[1] = 0x00; entry.mac[2] = 0x5e;
|
entry.mac[0] = 0x01; entry.mac[1] = 0x00; entry.mac[2] = 0x5e;
|
||||||
entry.mac[3] = IGMP_I->mc_ip[1] & 0x7f; entry.mac[4] = IGMP_I->mc_ip[2]; entry.mac[5] = IGMP_I->mc_ip[3];
|
entry.mac[3] = IGMP_I->mc_ip[1] & 0x7f; entry.mac[4] = IGMP_I->mc_ip[2]; entry.mac[5] = IGMP_I->mc_ip[3];
|
||||||
entry.vlan = 1; //TODO: Get this out of the packet and compare with VLAN table!
|
entry.vlan = 1; //TODO: Get this out of the packet and compare with VLAN table!
|
||||||
|
|||||||
@@ -262,7 +262,7 @@ void phy_set_speed(void) __banked
|
|||||||
{
|
{
|
||||||
uint16_t v;
|
uint16_t v;
|
||||||
|
|
||||||
print_string("Setting port "); write_char(machine.log_to_phys_port[phy_settings.port] + '0');
|
print_string("Setting port "); print_phys_port(phy_settings.port);
|
||||||
if (machine.n_10g && phy_settings.port == 3)
|
if (machine.n_10g && phy_settings.port == 3)
|
||||||
phy_settings.is10g_port = 1;
|
phy_settings.is10g_port = 1;
|
||||||
if (machine.n_10g == 2 && phy_settings.port == 8)
|
if (machine.n_10g == 2 && phy_settings.port == 8)
|
||||||
@@ -332,21 +332,25 @@ void phy_set_speed(void) __banked
|
|||||||
phy_write(phy_settings.port, PHY_MMD_AN, PHY_ANEG_CTRL, 0x2000); // Clear bit 12: No Autoneg, Set Extended Pages (bit 13)
|
phy_write(phy_settings.port, PHY_MMD_AN, PHY_ANEG_CTRL, 0x2000); // Clear bit 12: No Autoneg, Set Extended Pages (bit 13)
|
||||||
if (phy_settings.speed == PHY_SPEED_10M) {
|
if (phy_settings.speed == PHY_SPEED_10M) {
|
||||||
phy_write(phy_settings.port, PHY_MMD_AN, PHY_ANEG_MGBASE_CTRL, 0x6001);
|
phy_write(phy_settings.port, PHY_MMD_AN, PHY_ANEG_MGBASE_CTRL, 0x6001);
|
||||||
if (!phy_settings.duplex)
|
uint16_t adv10;
|
||||||
phy_write(phy_settings.port, PHY_MMD_AN, PHY_ANEG_ADV, 0x1421);
|
if (phy_settings.duplex == PHY_DUPLEX_HALF)
|
||||||
else if (phy_settings.duplex == 1)
|
adv10 = 0x1421;
|
||||||
phy_write(phy_settings.port, PHY_MMD_AN, PHY_ANEG_ADV, 0x1441);
|
else if (phy_settings.duplex == PHY_DUPLEX_FULL)
|
||||||
|
adv10 = 0x1441;
|
||||||
else
|
else
|
||||||
phy_write(phy_settings.port, PHY_MMD_AN, PHY_ANEG_ADV, 0x1461);
|
adv10 = 0x1461;
|
||||||
|
phy_write(phy_settings.port, PHY_MMD_AN, PHY_ANEG_ADV, adv10);
|
||||||
phy_modify(phy_settings.port, PHY_MMD31, PHY_MMD31_GBCR, 0x0200, 0x0000);
|
phy_modify(phy_settings.port, PHY_MMD31, PHY_MMD31_GBCR, 0x0200, 0x0000);
|
||||||
} else if (phy_settings.speed == PHY_SPEED_100M) {
|
} else if (phy_settings.speed == PHY_SPEED_100M) {
|
||||||
phy_write(phy_settings.port, PHY_MMD_AN, PHY_ANEG_MGBASE_CTRL, 0x6001);
|
phy_write(phy_settings.port, PHY_MMD_AN, PHY_ANEG_MGBASE_CTRL, 0x6001);
|
||||||
if (!phy_settings.duplex)
|
uint16_t adv100;
|
||||||
phy_write(phy_settings.port, PHY_MMD_AN, PHY_ANEG_ADV, 0x1481);
|
if (phy_settings.duplex == PHY_DUPLEX_HALF)
|
||||||
if (phy_settings.duplex == 1)
|
adv100 = 0x1481;
|
||||||
phy_write(phy_settings.port, PHY_MMD_AN, PHY_ANEG_ADV, 0x1501);
|
else if (phy_settings.duplex == PHY_DUPLEX_FULL)
|
||||||
|
adv100 = 0x1501;
|
||||||
else
|
else
|
||||||
phy_write(phy_settings.port, PHY_MMD_AN, PHY_ANEG_ADV, 0x1581);
|
adv100 = 0x1581;
|
||||||
|
phy_write(phy_settings.port, PHY_MMD_AN, PHY_ANEG_ADV, adv100);
|
||||||
phy_modify(phy_settings.port, PHY_MMD31, PHY_MMD31_GBCR, 0x0200, 0x0000);
|
phy_modify(phy_settings.port, PHY_MMD31, PHY_MMD31_GBCR, 0x0200, 0x0000);
|
||||||
} else {
|
} else {
|
||||||
// AN Advertisement Register (MMD 7.0x0010)
|
// AN Advertisement Register (MMD 7.0x0010)
|
||||||
@@ -381,7 +385,7 @@ void phy_set_duplex(void) __banked
|
|||||||
{
|
{
|
||||||
uint16_t v;
|
uint16_t v;
|
||||||
|
|
||||||
print_string("Setting port "); write_char(machine.log_to_phys_port[phy_settings.port] + '0');
|
print_string("Setting port "); print_phys_port(phy_settings.port);
|
||||||
if (phy_settings.duplex)
|
if (phy_settings.duplex)
|
||||||
print_string(" to full duplex");
|
print_string(" to full duplex");
|
||||||
else
|
else
|
||||||
@@ -468,9 +472,13 @@ void phy_show(uint8_t port) __banked
|
|||||||
phy_read(port, PHY_MMD_PMAPMD, 0);
|
phy_read(port, PHY_MMD_PMAPMD, 0);
|
||||||
v = SFR_DATA_U16;
|
v = SFR_DATA_U16;
|
||||||
print_string("\nForced speed: "); print_short(v); write_char('\n');
|
print_string("\nForced speed: "); print_short(v); write_char('\n');
|
||||||
uint8_t s1 = ((v & 0x40) ? 0x2 : 0x0) | ((v & 0x2000) ? 0x1 : 0x0);
|
uint8_t s1 = 0x00;
|
||||||
uint8_t s2 = (v >> 2) & 0xf;
|
if ((uint8_t)v & 0x40)
|
||||||
switch(s1) {
|
s1 = 0x2;
|
||||||
|
if (v & 0x2000)
|
||||||
|
s1 |= 0x1;
|
||||||
|
uint8_t s2 = ((uint8_t)v >> 2) & 0xf;
|
||||||
|
switch(s1 & 0x3) {
|
||||||
case 0:
|
case 0:
|
||||||
print_string("10M\n");
|
print_string("10M\n");
|
||||||
break;
|
break;
|
||||||
@@ -495,8 +503,6 @@ void phy_show(uint8_t port) __banked
|
|||||||
print_string("Unknown\n");
|
print_string("Unknown\n");
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
default:
|
|
||||||
print_string("Unknown\n");
|
|
||||||
}
|
}
|
||||||
phy_read(port, PHY_MMD31, PHY_MMD31_FEDCR);
|
phy_read(port, PHY_MMD31, PHY_MMD31_FEDCR);
|
||||||
v = SFR_DATA_U16;
|
v = SFR_DATA_U16;
|
||||||
@@ -582,7 +588,7 @@ void phy_reset(uint8_t port) __banked
|
|||||||
// Reading only reads the lower 16-bit part of the 32-bit register.
|
// Reading only reads the lower 16-bit part of the 32-bit register.
|
||||||
// When also needing read the upper 16-bits, use register address + 1.
|
// When also needing read the upper 16-bits, use register address + 1.
|
||||||
// Readed values it return via sfr-data.
|
// Readed values it return via sfr-data.
|
||||||
void inline rtl8224_read_reg_u16(uint16_t reg) __banked
|
void rtl8224_read_reg_u16(uint16_t reg) __banked
|
||||||
{
|
{
|
||||||
// void phy_read(uint8_t phy_id, uint8_t dev_id, uint16_t reg)
|
// void phy_read(uint8_t phy_id, uint8_t dev_id, uint16_t reg)
|
||||||
// phy_read(RTL8224_PHY_ID, PHY_MMD30, reg);
|
// phy_read(RTL8224_PHY_ID, PHY_MMD30, reg);
|
||||||
@@ -590,7 +596,7 @@ void inline rtl8224_read_reg_u16(uint16_t reg) __banked
|
|||||||
SFR_SMI_REG_U16 = reg; // c2, c2
|
SFR_SMI_REG_U16 = reg; // c2, c2
|
||||||
|
|
||||||
SFR_SMI_PHY = RTL8224_PHY_ID; // a5
|
SFR_SMI_PHY = RTL8224_PHY_ID; // a5
|
||||||
SFR_SMI_DEV = PHY_MMD30 << 3 | 2; // c4
|
SFR_SMI_DEV = (uint8_t)PHY_MMD30 << 3 | 2; // c4
|
||||||
|
|
||||||
SFR_EXEC_GO = SFR_EXEC_READ_SMI;
|
SFR_EXEC_GO = SFR_EXEC_READ_SMI;
|
||||||
do {
|
do {
|
||||||
@@ -601,7 +607,7 @@ void inline rtl8224_read_reg_u16(uint16_t reg) __banked
|
|||||||
// Registers names are the same as on the RTL837x.
|
// Registers names are the same as on the RTL837x.
|
||||||
// Writing only the lower 16-bit part of the 32-bit register.
|
// Writing only the lower 16-bit part of the 32-bit register.
|
||||||
// When also needing to write the upper 16-bits, use register address + 1.
|
// When also needing to write the upper 16-bits, use register address + 1.
|
||||||
void inline rtl8224_write_reg_u16(uint16_t reg, uint16_t val) __banked
|
void rtl8224_write_reg_u16(uint16_t reg, uint16_t val) __banked
|
||||||
{
|
{
|
||||||
SFR_DATA_U16 = val; // SFR_A6, SFR_A7
|
SFR_DATA_U16 = val; // SFR_A6, SFR_A7
|
||||||
SFR_SMI_REG_U16 = reg; // SFR_C2, SFR_C3
|
SFR_SMI_REG_U16 = reg; // SFR_C2, SFR_C3
|
||||||
@@ -633,7 +639,7 @@ void inline rtl8224_write_reg_u16(uint16_t reg, uint16_t val) __banked
|
|||||||
|
|
||||||
|
|
||||||
// Write to the RTL8224 SDS registers.
|
// Write to the RTL8224 SDS registers.
|
||||||
void rtl8224_sds_write(uint16_t sds_cmd, uint16_t value) __banked
|
void rtl8224_sds_write(uint16_t sds_cmd, __xdata uint16_t value) __banked
|
||||||
{
|
{
|
||||||
// Wait for command bit is cleared
|
// Wait for command bit is cleared
|
||||||
do {
|
do {
|
||||||
|
|||||||
@@ -28,14 +28,16 @@ void phy_show(uint8_t port) __banked;
|
|||||||
void phy_reset(uint8_t port) __banked;
|
void phy_reset(uint8_t port) __banked;
|
||||||
void rtl8224_read_reg_u16(uint16_t reg) __banked;
|
void rtl8224_read_reg_u16(uint16_t reg) __banked;
|
||||||
void rtl8224_write_reg_u16(uint16_t reg, uint16_t val) __banked;
|
void rtl8224_write_reg_u16(uint16_t reg, uint16_t val) __banked;
|
||||||
void rtl8224_sds_write(uint16_t sds_cmd, uint16_t val) __banked;
|
void rtl8224_sds_write(uint16_t sds_cmd, __xdata uint16_t val) __banked;
|
||||||
void phy_config_8261(uint8_t phy, uint8_t sds) __banked;
|
void phy_config_8261(uint8_t phy, uint8_t sds) __banked;
|
||||||
|
|
||||||
#define RTL8224_SDS_WRITE(sds_id, page, reg, v) uint16_t _sdscmd = (uint16_t)(sds_id & 0x01) | (1 << 14) | (1 << 15); \
|
#define RTL8224_SDS_WRITE(sds_id, page, reg, v) do { \
|
||||||
|
uint16_t _sdscmd = (uint16_t)(sds_id & 0x01) | (1 << 14) | (1 << 15); \
|
||||||
_sdscmd |= (page & 0x3F) << 1; \
|
_sdscmd |= (page & 0x3F) << 1; \
|
||||||
_sdscmd |= ((uint16_t)(reg & 0x1f)) << 7; \
|
_sdscmd |= ((uint16_t)(reg & 0x1f)) << 7; \
|
||||||
print_string("CMD: "); print_short(_sdscmd); \
|
print_string("CMD: "); print_short(_sdscmd); \
|
||||||
write_char('-'); print_short(v); \
|
write_char('-'); print_short(v); \
|
||||||
rtl8224_sds_write(_sdscmd, v);
|
rtl8224_sds_write(_sdscmd, v); \
|
||||||
|
} while (0)
|
||||||
|
|
||||||
#endif
|
#endif
|
||||||