Compare commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
1400431337 | ||
|
|
119b5ddf13 | ||
|
|
231b43b264 | ||
|
|
e53ebddc21 | ||
|
|
f23eb6bebb | ||
|
|
cd51cffb1d | ||
|
|
01fc5db9a3 | ||
|
|
bdca888ecd | ||
|
|
e8431999e5 | ||
|
|
7ca4d2c8dc | ||
|
|
32dd21bbd3 | ||
|
|
92f9025109 | ||
|
|
b65aa82d78 | ||
|
|
851eb39344 | ||
|
|
970e98f4c5 | ||
|
|
4d009bc8e8 | ||
|
|
eb51128ec7 | ||
|
|
b86fd37eaa | ||
|
|
b92a773cad | ||
|
|
068e7467af | ||
|
|
da4f5058a7 | ||
|
|
fee4f17bff | ||
|
|
e19271fc44 | ||
|
|
aff6ac0923 | ||
|
|
7e11be0d97 | ||
|
|
23862bc648 | ||
|
|
6c62791d23 | ||
|
|
413690a748 | ||
|
|
3910888052 | ||
|
|
a0cb3c1850 | ||
|
|
73bb4c8af2 | ||
|
|
0443ba428b | ||
|
|
62abd08aa2 | ||
|
|
70426ff24a | ||
|
|
2d62cc493d | ||
|
|
bbce2d6727 | ||
|
|
36de7b7241 | ||
|
|
ea320d813d | ||
|
|
ca2abcc4e1 | ||
|
|
4d7159092c | ||
|
|
98763e3bff | ||
|
|
80a30b705e | ||
|
|
4c3e1fde81 | ||
|
|
5cd14e6227 | ||
|
|
5105018d90 | ||
|
|
5bc7cf43db | ||
|
|
c776d10129 | ||
|
|
7b4d0d55d2 | ||
|
|
e95e450813 | ||
|
|
fe1d4119d0 | ||
|
|
c5f62bff95 | ||
|
|
7535c0d9b7 | ||
|
|
a57319557d | ||
|
|
1b29fb687f | ||
|
|
2e62f9eea7 | ||
|
|
5c6a82d50f | ||
|
|
1d158d21d1 | ||
|
|
26c420d26a | ||
|
|
eb394af95f | ||
|
|
4a4caeffd7 | ||
|
|
391360f517 | ||
|
|
ed8e4b1a56 | ||
|
|
78f28488c1 | ||
|
|
e1c79ec4cd | ||
|
|
a7db0ca8b6 | ||
|
|
70f59b64f5 | ||
|
|
f0cbaba7b1 | ||
|
|
2d5424fedc | ||
|
|
23c8b2a8b3 | ||
|
|
c9f6362da5 | ||
|
|
742aef5461 | ||
|
|
a77d219baf | ||
|
|
f94204e56f | ||
|
|
8d6adedb56 | ||
|
|
c3f9acea10 | ||
|
|
c46261e052 | ||
|
|
834403b97b | ||
|
|
48087d3520 | ||
|
|
1d359afba5 | ||
|
|
ec14dbf323 | ||
|
|
deedb92c5d | ||
|
|
a18c334139 | ||
|
|
337b171ac4 | ||
|
|
53eb3f5486 | ||
|
|
241edfdbda | ||
|
|
714c9eb343 | ||
|
|
f2c89fd75b | ||
|
|
ca2f1d101d | ||
|
|
9cc859ad30 | ||
|
|
0181276ec6 | ||
|
|
905e588071 | ||
|
|
bb16302a80 | ||
|
|
50fda40fd9 | ||
|
|
9c65039bab | ||
|
|
7e0bc93b55 | ||
|
|
e1e1292ab0 | ||
|
|
975caaff02 | ||
|
|
cffe4793b1 | ||
|
|
9ed1e89c8b | ||
|
|
ba2f759df3 | ||
|
|
884a1ac016 | ||
|
|
51ed063dcb | ||
|
|
0da6318777 | ||
|
|
d13a5d125d | ||
|
|
889c94404c | ||
|
|
0f98734ae2 | ||
|
|
2d0abbbca0 | ||
|
|
fdcdfa9b45 | ||
|
|
c043c671d7 | ||
|
|
11a24783e4 | ||
|
|
e864c32980 | ||
|
|
bb329daab0 | ||
|
|
89c8903454 | ||
|
|
63a3bc50d6 | ||
|
|
a94be7d524 | ||
|
|
50be5308d9 | ||
|
|
9731a33fa9 | ||
|
|
c3dddd9c90 | ||
|
|
6e5617c82f | ||
|
|
6638748e3c | ||
|
|
98965b73f6 | ||
|
|
ef0d9d872e | ||
|
|
7d7585fa6d | ||
|
|
13801241ca | ||
|
|
94ca35f65e | ||
|
|
69714c8a75 | ||
|
|
2bc6939f86 | ||
|
|
aea537f8e7 | ||
|
|
7346977f91 | ||
|
|
7b0a07efaa | ||
|
|
3c00ab0ae9 | ||
|
|
7673ef8322 | ||
|
|
c9e8403c6e | ||
|
|
3deee14f94 | ||
|
|
6d539bfb1b | ||
|
|
7a44d8181f | ||
|
|
d698e180e4 | ||
|
|
566e26ace2 | ||
|
|
674dc23a27 | ||
|
|
620edafd62 | ||
|
|
413d10bbcc | ||
|
|
0faceab1d0 | ||
|
|
7e12f0a62c | ||
|
|
491a919105 | ||
|
|
2588965eee | ||
|
|
827d3b389c | ||
|
|
ef224ec8f4 | ||
|
|
0a3d7d360a | ||
|
|
9561f239b4 | ||
|
|
b06ff76a21 | ||
|
|
b28ead65dc | ||
|
|
a079c803d9 | ||
|
|
cc3ae654a0 | ||
|
|
2a9f9b2508 | ||
|
|
57d6337940 | ||
|
|
3b58eb36e5 | ||
|
|
34c0ef77d0 | ||
|
|
e6182dbe0d | ||
|
|
1d5e4cd722 | ||
|
|
a02116a24b | ||
|
|
98369c37fe | ||
|
|
d104b3129d | ||
|
|
138acfadb1 | ||
|
|
08f8498b39 | ||
|
|
dd521cc79d | ||
|
|
723d16b4cb | ||
|
|
2c2349dcef | ||
|
|
6d1380fb36 | ||
|
|
eacf13690c | ||
|
|
a1998c70d7 | ||
|
|
4ed74f05db | ||
|
|
0de18ff457 | ||
|
|
3195a14870 | ||
|
|
3ffb311e95 | ||
|
|
f0c672418c | ||
|
|
41ed689ae0 | ||
|
|
11eadc5a49 | ||
|
|
24d2cd7bab | ||
|
|
28c09737b2 | ||
|
|
3e1d3f40d2 | ||
|
|
389dc2af76 | ||
|
|
4963fbe50a | ||
|
|
44b5ab0e64 | ||
|
|
088321deaf | ||
|
|
3af1b049ee | ||
|
|
6a5d3cdf2c | ||
|
|
d21ea52c46 | ||
|
|
c6cc3ee595 | ||
|
|
424a963301 | ||
|
|
1344e02fb7 | ||
|
|
2ae297bc20 | ||
|
|
ce77cc273d | ||
|
|
6376a10288 | ||
|
|
a202842107 | ||
|
|
6fad2836ae | ||
|
|
2415015a87 | ||
|
|
d9fc20addf | ||
|
|
c15b2d48da | ||
|
|
f17ca1c9fc | ||
|
|
be5dee04e3 | ||
|
|
196d22d7c3 | ||
|
|
496ac8b757 | ||
|
|
bf670aed0b | ||
|
|
6af5580433 | ||
|
|
6194356f66 | ||
|
|
bd56c1a8c5 | ||
|
|
d562fd661e | ||
|
|
0b6b613c03 | ||
|
|
f325bdbd11 | ||
|
|
680a40dab5 | ||
|
|
97b15f4022 | ||
|
|
c263a8ab64 | ||
|
|
d5ccd38dd3 | ||
|
|
adecc26ae2 | ||
|
|
44ae7a8d3b | ||
|
|
c3fc541339 | ||
|
|
6fdb19f18c | ||
|
|
70f8964918 | ||
|
|
aba58e7f99 | ||
|
|
2404e7a9b6 | ||
|
|
6a31b4a279 | ||
|
|
6cd1f683bb | ||
|
|
7e90f6a126 | ||
|
|
9bd301447b | ||
|
|
9745dfe6ae | ||
|
|
d4345c1bf8 | ||
|
|
fcaacd4429 | ||
|
|
0f63244ca1 | ||
|
|
913e9780b8 | ||
|
|
0d748fa808 | ||
|
|
bd974e0aaf | ||
|
|
56686185ce | ||
|
|
ed069b67fd | ||
|
|
b1026490a8 | ||
|
|
28ea2184c2 | ||
|
|
d5f84cc2fb | ||
|
|
682ac462c4 | ||
|
|
ecc8356f4d | ||
|
|
3a8d1d323f | ||
|
|
e125ebd097 | ||
|
|
4050555667 | ||
|
|
8224b06f9e | ||
|
|
69151362a0 | ||
|
|
24e83d5b74 | ||
|
|
e890e429a5 | ||
|
|
813a62ca7d | ||
|
|
b096efd63a | ||
|
|
3d81df0cad | ||
|
|
2481810906 | ||
|
|
03311debe6 | ||
|
|
e4adec9c11 | ||
|
|
8a7c847ad9 | ||
|
|
b94d2aeabc | ||
|
|
d525229b42 | ||
|
|
73b23808cf | ||
|
|
4a14f50932 | ||
|
|
1bfe632174 | ||
|
|
265a80bb25 | ||
|
|
64efd3fb9f | ||
|
|
2e6425f5e6 | ||
|
|
6bae5c5c90 | ||
|
|
c2980e42d8 | ||
|
|
1856aeba28 | ||
|
|
4e4a2ea4db | ||
|
|
4ccbf51976 | ||
|
|
0df48fbb1c | ||
|
|
25040070e5 | ||
|
|
edf6351d85 | ||
|
|
c3b4cc893b | ||
|
|
b83b67d234 | ||
|
|
efd61be64e | ||
|
|
875933b9f9 | ||
|
|
0e85137e7a | ||
|
|
da7909f422 | ||
|
|
14b2ce1c49 | ||
|
|
84d9408151 | ||
|
|
c02f660c08 | ||
|
|
05c0571154 | ||
|
|
8790f0dca4 | ||
|
|
98af999304 | ||
|
|
0a996fa6d0 | ||
|
|
441e575661 | ||
|
|
a27118a428 | ||
|
|
01709431dc | ||
|
|
de0d413c0c | ||
|
|
b3ac10cae7 | ||
|
|
4e00f5a592 | ||
|
|
eff3f42ae1 | ||
|
|
dda873a633 | ||
|
|
9b2bd7f1b8 | ||
|
|
2dfe531fe0 | ||
|
|
72c2625121 | ||
|
|
58fd1a3ccc | ||
|
|
4f1a2f04d8 | ||
|
|
cedc795b82 | ||
|
|
ccf541a06b | ||
|
|
d5124df89e | ||
|
|
3eacc9c8a5 | ||
|
|
735672ccf1 | ||
|
|
1ebee4d6e9 | ||
|
|
24c0a95734 | ||
|
|
706dea63a2 | ||
|
|
f88f655f5a | ||
|
|
a6b0918885 | ||
|
|
1d6fe690a4 | ||
|
|
e67c928eb7 | ||
|
|
46f8ea8712 | ||
|
|
b0eab52a2e | ||
|
|
8ce062c349 | ||
|
|
f768220a73 | ||
|
|
eab2e26510 | ||
|
|
b0ed56d423 | ||
|
|
36a064d03d | ||
|
|
2d01b8b7d7 | ||
|
|
fa632cdc6e | ||
|
|
177fb76e1b | ||
|
|
ff57084799 | ||
|
|
2dc5520928 | ||
|
|
7c11142a04 | ||
|
|
957a5b0e23 | ||
|
|
c773230a9b | ||
|
|
4f009539b0 | ||
|
|
bb90e0c9ba | ||
|
|
0e9c997d06 | ||
|
|
5ab247e996 | ||
|
|
d80717f3f0 | ||
|
|
24069439b0 | ||
|
|
4117f549cc | ||
|
|
2e24b24426 | ||
|
|
b71239f394 | ||
|
|
6e69f5bba3 | ||
|
|
1224335b66 | ||
|
|
e5b19bc594 | ||
|
|
89fcc6836d | ||
|
|
f9d4ab0074 | ||
|
|
6f68ccef28 | ||
|
|
8e687ef28e | ||
|
|
87db2dd4b8 | ||
|
|
aac1aed4c8 | ||
|
|
f0aea3dcac | ||
|
|
f10ab30f6d | ||
|
|
de431c6d84 | ||
|
|
3cd6d60e0b | ||
|
|
67b8c718fb | ||
|
|
26547f3e01 | ||
|
|
13b75a73c9 | ||
|
|
c6fceae724 | ||
|
|
068ec4c510 | ||
|
|
3a92459190 | ||
|
|
5ffeb3d16a | ||
|
|
de705380c9 | ||
|
|
e4f3315af3 | ||
|
|
07ab04e668 | ||
|
|
c5ce24ff78 | ||
|
|
bcb237158f | ||
|
|
f103daaafa | ||
|
|
168add47a3 | ||
|
|
423886dc54 | ||
|
|
4a9137387c | ||
|
|
2fc08f40d2 | ||
|
|
4a7fd7a098 | ||
|
|
19cd1200b4 | ||
|
|
333adbaf19 | ||
|
|
8051061fd7 | ||
|
|
8ae3f5e70f | ||
|
|
9ce328d155 | ||
|
|
2e463a2f30 | ||
|
|
d0374541c5 | ||
|
|
422dc3f24d | ||
|
|
4b01ff9f8a | ||
|
|
456a4d9d3f | ||
|
|
02e9189f5f | ||
|
|
aff7d9c098 | ||
|
|
770926abd1 | ||
|
|
13f04d8acb | ||
|
|
49284fee15 | ||
|
|
db9ce7543e | ||
|
|
7a14c2d4a5 | ||
|
|
92948079a9 | ||
|
|
e406a3e82b | ||
|
|
c43b955db0 | ||
|
|
0321763e91 | ||
|
|
4bbbcd7d04 | ||
|
|
682ed861fa | ||
|
|
3a9045b97e | ||
|
|
4b9f6c319f | ||
|
|
6e2701bc8c | ||
|
|
ac3b4b17ae | ||
|
|
f87eb70c6a | ||
|
|
214cc6b737 | ||
|
|
40177f6318 | ||
|
|
d3dda09079 | ||
|
|
277e729f2c | ||
|
|
2c231fdf69 | ||
|
|
689751ffe6 | ||
|
|
340fdd97e5 | ||
|
|
675faf0471 | ||
|
|
175ca9464a | ||
|
|
94187461c6 | ||
|
|
fa0eb91586 | ||
|
|
2f872ea08e | ||
|
|
ab2d30a05b | ||
|
|
d37ce07119 | ||
|
|
9733ff61a0 | ||
|
|
36459fdd76 | ||
|
|
a4e3b4368b | ||
|
|
52b1d1a4ea | ||
|
|
48f28ae7ff | ||
|
|
c514eb8bf3 | ||
|
|
575274786a | ||
|
|
eb83b8be09 | ||
|
|
cdc84c75e4 | ||
|
|
6be1871bb4 | ||
|
|
4686515de3 | ||
|
|
f0b311fc43 |
No files matched your search
@@ -3,6 +3,7 @@
|
||||
.gitattributes
|
||||
.github/
|
||||
output/
|
||||
toolchain/
|
||||
installer/output/
|
||||
*.bin
|
||||
html_data.c
|
||||
|
||||
@@ -5,6 +5,11 @@ on:
|
||||
branches: ['**']
|
||||
pull_request:
|
||||
|
||||
permissions:
|
||||
contents: read
|
||||
pull-requests: write
|
||||
issues: write
|
||||
|
||||
jobs:
|
||||
build:
|
||||
runs-on: ubuntu-latest
|
||||
@@ -13,16 +18,41 @@ jobs:
|
||||
|
||||
steps:
|
||||
- uses: actions/checkout@v6
|
||||
with:
|
||||
path: branch
|
||||
|
||||
- uses: actions/checkout@v6
|
||||
with:
|
||||
path: main
|
||||
ref: main
|
||||
|
||||
- name: Install dependencies
|
||||
run: |
|
||||
apt update
|
||||
apt install make gcc sdcc xxd python-is-python3 libjson-c-dev zlib1g-dev -y
|
||||
apt install make gcc g++ bison flex libboost-dev patch bzip2 curl ca-certificates xxd python-is-python3 libjson-c-dev zlib1g-dev -y
|
||||
- name: Cache the patched SDCC
|
||||
uses: actions/cache@v4
|
||||
with:
|
||||
path: branch/toolchain
|
||||
key: sdcc-${{ hashFiles('branch/sdcc/*') }}
|
||||
- name: Run the host-side unit tests
|
||||
run: make -C test
|
||||
- name: Check if machine.c can be compiled for all machines
|
||||
run: make machine_check
|
||||
run: cd branch && make -C test
|
||||
- name: Check that every machine definition can be compiled
|
||||
run: cd branch && make machine_check
|
||||
- name: Make project (4x2/RTL8372)
|
||||
run: make CI=1 MACHINE="KP_9000_6XHML_X2"
|
||||
run: cd branch && make CI=1 MACHINE="KP_9000_6XHML_X2" && cp output/KP_9000_6XHML_X2/rtlplayground.map /tmp/branch.map
|
||||
- name: Make project (8x1/RTL8373)
|
||||
run: make CI=1 MACHINE="KP_9000_9XHML_X_V3_1"
|
||||
run: cd branch && make CI=1 MACHINE="KP_9000_9XHML_X_V3_1"
|
||||
- name: Make project with the health instrumentation
|
||||
run: cd branch && make clean && make CI=1 MACHINE="KP_9000_6XHML_X2" HEALTH=1
|
||||
- name: Make main
|
||||
run: |
|
||||
ln -s ../branch/toolchain main/toolchain
|
||||
PATH="$(echo "$PWD"/branch/toolchain/sdcc-*/bin):$PATH"
|
||||
cd main && make CI=1 MACHINE="KP_9000_6XHML_X2" && cp output/KP_9000_6XHML_X2/rtlplayground.map /tmp/main.map
|
||||
- name: Calculate bank growth
|
||||
run: |
|
||||
sed -rn 's/^([XCD]SEG|(BANK[0-9])).*=\s+([0-9]+)\. bytes.*$/MAIN_\1=\3/p' /tmp/main.map | sort -u >> "$GITHUB_ENV"
|
||||
sed -rn 's/^([XCD]SEG|(BANK[0-9])).*=\s+([0-9]+)\. bytes.*$/BRANCH_\1=\3/p' /tmp/branch.map | sort -u >> "$GITHUB_ENV"
|
||||
du -sb main/output/KP_9000_6XHML_X2/html_min | awk '{print "MAIN_WEB="$1}' >> "$GITHUB_ENV"
|
||||
du -sb branch/output/KP_9000_6XHML_X2/html_min | awk '{print "BRANCH_WEB="$1}' >>"$GITHUB_ENV"
|
||||
@@ -1,13 +1,17 @@
|
||||
.gitignore
|
||||
.idea/
|
||||
output/
|
||||
output-main/
|
||||
toolchain/
|
||||
html_data.c
|
||||
html_data.h
|
||||
version.h
|
||||
tools/httpd_sim
|
||||
tools/injector
|
||||
tools/fileadder
|
||||
build/
|
||||
|
||||
# host-test stand-ins for the two generated headers
|
||||
!test/stub/html_data.h
|
||||
!test/stub/version.h
|
||||
.vscode
|
||||
@@ -4,6 +4,17 @@
|
||||
|
||||
## Added
|
||||
|
||||
- Devices
|
||||
- Support the hongyavision HYWS-SGT0204S (PCB marking `2G5F4_10G2_V1.01`), an
|
||||
RTL8372 based 4x2.5G + 2xSFP+ unmanaged switch with a 512 KB Bright Moon
|
||||
`T25S40` flash. It is a sibling of the KP-9000-6XH-X2 (2M-PCB43-V1.1) with
|
||||
the two SFP+ cages wired the other way round (physical 5 is on SDS1,
|
||||
physical 6 on SDS0), no reset button fitted, wrong silkscreen labels on the
|
||||
serial header, `LED_GLB_IO_EN` bit 6 has to be set for the port LEDs to
|
||||
work, and the fitted T25S40 cannot be read in DIO mode on this board, so the
|
||||
target selects single-IO flash reads (`flash_sio`). New machine target
|
||||
`MACHINE_2G5F4_10G2_V1_01`, see `doc/devices/2G5F4_10G2_V1.01.md`.
|
||||
|
||||
- Web UI
|
||||
- Compress the embedded assets (minify + gzip) and switch to a single-page layout. #315
|
||||
- Replace the multi-page UI with a themed single-page app: light, dark and Selenized themes following the browser by default,
|
||||
@@ -14,12 +25,34 @@
|
||||
|
||||
## Fixed
|
||||
|
||||
- Time
|
||||
- `rtl8372_init()` sets SDS 0 to SGMII as the stock firmware does. The masks were 0, so on RTL8372
|
||||
boards without 10G ports both SerDes stayed off, and with SDS 0 off `RTL837X_REG_SEC_COUNTER`
|
||||
does not hold a written value: NTP never retried after a failed first attempt at boot and
|
||||
froze the clock after a sync.
|
||||
|
||||
- Web UI
|
||||
- Firefox no longer fills a saved login into the new admin password fields. It also put the
|
||||
user name into the NTP poll interval, which marked the Time card as edited, so it kept its
|
||||
defaults (NTP off, no server, UTC-12:00) instead of the switch's settings.
|
||||
- Accept firmware uploads regardless of the file part's MIME type, including
|
||||
`application/macbinary` sent by Chrome on macOS for `.bin` files.
|
||||
|
||||
- Health
|
||||
- `HEALTH=1` builds no longer write past the phase timing arrays at the LLDP checkpoint, which
|
||||
corrupted the `link` slow counter and the DHCP client state. #658
|
||||
- Config
|
||||
- A startup configuration line longer than the command buffer no longer stops the replay:
|
||||
the offending line is skipped and the following lines are still applied.
|
||||
- The web UI and the configuration upload now refuse a line the replay cannot take,
|
||||
instead of writing it to flash.
|
||||
|
||||
- Startup
|
||||
- Initialised and zero-initialised globals above xdata 0x4000 now get their values at boot.
|
||||
Hardware reset leaves SFR 0x97 at 1, which maps xdata 0x4000-0xbfff to the flash image,
|
||||
and it was cleared only in `main()`, after the C startup code had copied and cleared them.
|
||||
This showed as a random web session timeout and LLDP blocked on some ports without config.
|
||||
|
||||
## Breaking changes
|
||||
|
||||
- Config
|
||||
|
||||
@@ -3,7 +3,14 @@ FROM debian:13-slim
|
||||
RUN apt-get update && apt-get install -y \
|
||||
make \
|
||||
gcc \
|
||||
sdcc \
|
||||
g++ \
|
||||
bison \
|
||||
flex \
|
||||
libboost-dev \
|
||||
patch \
|
||||
bzip2 \
|
||||
curl \
|
||||
ca-certificates \
|
||||
xxd \
|
||||
python3 \
|
||||
libjson-c-dev \
|
||||
|
||||
@@ -4,27 +4,63 @@ DEFAULT_CONFIG_LOCATION = 454656
|
||||
CONFIG_LOCATION = 458752
|
||||
HTML_LOCATION = 262144
|
||||
|
||||
# Startup configuration embedded in the image. Override it to build a specific
|
||||
# machine with its own configuration: make MACHINE=<name> CONFIG=<file>
|
||||
CONFIG ?= config.txt
|
||||
|
||||
SDCC_TOOLCHAIN = sdcc-$(shell cat sdcc/* | cksum | cut -d' ' -f1)
|
||||
SDCC_PREFIX = toolchain/$(SDCC_TOOLCHAIN)
|
||||
ifeq ($(origin CC),default)
|
||||
CC = sdcc
|
||||
SDCC = $(SDCC_PREFIX)/bin/sdcc
|
||||
CC = $(SDCC)
|
||||
ASM = $(SDCC_PREFIX)/bin/sdas8051
|
||||
SDCC_REV := $(shell git log -1 --format=%h -- sdcc 2>/dev/null)
|
||||
ifneq ($(SDCC_REV),)
|
||||
ifneq ($(shell git status --porcelain --untracked-files=no -- sdcc 2>/dev/null),)
|
||||
SDCC_REV := $(SDCC_REV)-dirty
|
||||
endif
|
||||
CC_FLAGS = -mmcs51 -I. -Ihttpd -Iuip
|
||||
endif
|
||||
SDCC_BANNER = $(if $(SDCC_REV),sdcc/ at $(SDCC_REV),$(SDCC_TOOLCHAIN)) ($(words $(wildcard sdcc/*.patch)) patches)
|
||||
else
|
||||
SDCC_BANNER = system
|
||||
endif
|
||||
CC_FLAGS = -mmcs51 -I. -Imachine -Ihttpd -Iuip
|
||||
ASM ?= sdas8051
|
||||
AFLAGS= -plosgff
|
||||
|
||||
SUBDIRS := tools
|
||||
SUBDIRSCLEAN=$(addsuffix clean,$(SUBDIRS))
|
||||
|
||||
ifeq ($(MACHINE),)
|
||||
MACHINE:= $(shell grep "^\s*#define MACHINE_" machine.h | sed "s/^\s*#define MACHINE_//")
|
||||
else
|
||||
CC_FLAGS += -DMACHINE_$(MACHINE)
|
||||
# $MACHINE selects machine/$(MACHINE).c as the board definition and is passed
|
||||
# on as -DMACHINE_NAME, which machine.h uses to include machine/$(MACHINE).h.
|
||||
# Board selection itself needs no MACHINE_* symbol for #if chains, so the
|
||||
# definition is all the compiler has to know.
|
||||
ifneq ($(MACHINE),)
|
||||
CC_FLAGS += -DMACHINE_NAME=$(MACHINE)
|
||||
endif
|
||||
# Health instrumentation and the "health" console command: HEALTH=1 gmake ...
|
||||
ifneq ($(HEALTH),)
|
||||
CC_FLAGS += -DHEALTH
|
||||
endif
|
||||
|
||||
ifeq ($(CI),1)
|
||||
CC_FLAGS += --Werror
|
||||
endif
|
||||
|
||||
BUILDDIR = output/$(MACHINE)
|
||||
# The board definition being built: one file per MACHINE_* symbol in machine.h.
|
||||
MACHINE_SRC = machine/$(MACHINE).c
|
||||
|
||||
MACHINE_INIT_DEFAULT := machine/default_machine_init.c
|
||||
MACHINE_INIT_SRC := $(wildcard machine/$(MACHINE)_init.c)
|
||||
ifeq ($(MACHINE_INIT_SRC),)
|
||||
MACHINE_INIT_SRC := $(MACHINE_INIT_DEFAULT)
|
||||
endif
|
||||
|
||||
ifndef BUILDDIR
|
||||
BUILDDIR = output/$(MACHINE)
|
||||
endif
|
||||
|
||||
CONFIG_STAMP := $(BUILDDIR)/.config
|
||||
VERSION_HEADER := version.h
|
||||
|
||||
GIT_VERSION := $(shell git rev-parse --short HEAD)
|
||||
@@ -47,17 +83,41 @@ BUILD_DATE := $(shell date -u -d @$(SOURCE_DATE_EPOCH) +"%Y-%m-%d %H:%M:%S" 2>/d
|
||||
|| date -u -r $(SOURCE_DATE_EPOCH) +"%Y-%m-%d %H:%M:%S")
|
||||
endif
|
||||
|
||||
all: create_build_dir $(VERSION_HEADER) $(SUBDIRS) $(BUILDDIR)/rtlplayground-$(FILENAME_EXTENSION).bin
|
||||
# $(MACHINE_SRC) and $(CONFIG) are listed here so an unknown $MACHINE or a
|
||||
# missing config file fails before anything else is built, including the tools/
|
||||
# subdirectory.
|
||||
all: create_build_dir $(VERSION_HEADER) $(MACHINE_SRC) $(CONFIG) $(SUBDIRS) $(BUILDDIR)/rtlplayground-$(FILENAME_EXTENSION).bin
|
||||
|
||||
create_build_dir:
|
||||
bank_size:
|
||||
tools/bank_occupancy.sh "$(MACHINE)"
|
||||
|
||||
create_build_dir: $(SDCC)
|
||||
mkdir -p "$(BUILDDIR)"
|
||||
mkdir -p "$(BUILDDIR)/uip"
|
||||
mkdir -p "$(BUILDDIR)/httpd"
|
||||
mkdir -p "$(BUILDDIR)/machine"
|
||||
|
||||
# Keep machine.c in first position to fail immediately on invalid $MACHINE value
|
||||
# Every machine is a pair machine/<MACHINE>.c + machine/<MACHINE>.h. Selecting
|
||||
# them by name means no #if/#elif chain, so an unknown or empty $MACHINE is
|
||||
# caught here instead of by a #error further down the build.
|
||||
$(MACHINE_SRC):
|
||||
@echo "ERROR: MACHINE='$(MACHINE)' has no definition (expected $(MACHINE_SRC))." >&2
|
||||
@echo " Every machine is one file pair under machine/, named after the" >&2
|
||||
@echo " MACHINE_* symbol it defines; see machine/*.c for the full list." >&2
|
||||
@echo " Example: make MACHINE=KP_9000_6XHML_X2" >&2
|
||||
@false
|
||||
|
||||
# Fail with a clear message when CONFIG names a file that does not exist.
|
||||
$(CONFIG):
|
||||
@echo "ERROR: CONFIG='$(CONFIG)' does not exist." >&2
|
||||
@echo " Point CONFIG at a configuration file, or omit it to use config.txt." >&2
|
||||
@false
|
||||
|
||||
# Keep the per-machine definition in first position to fail immediately on an
|
||||
# invalid $MACHINE value
|
||||
SRCS = \
|
||||
machine.c \
|
||||
machine_init.c \
|
||||
$(MACHINE_SRC) \
|
||||
$(MACHINE_INIT_SRC) \
|
||||
cmd_editor.c \
|
||||
cmd_parser.c \
|
||||
dhcp.c \
|
||||
@@ -65,23 +125,37 @@ SRCS = \
|
||||
rtlplayground.c \
|
||||
boot.c \
|
||||
sfp.c \
|
||||
dns.c \
|
||||
dns_cli.c \
|
||||
sessions.c \
|
||||
ntp.c \
|
||||
ntp_cli.c \
|
||||
syslog.c \
|
||||
udp_apps.c
|
||||
|
||||
# RTL837x
|
||||
SRCS += \
|
||||
rtl837x_bandwidth.c \
|
||||
rtl837x_cabletest.c \
|
||||
rtl837x_flash.c \
|
||||
rtl837x_igmp.c \
|
||||
rtl837x_init.c \
|
||||
rtl837x_nic.c \
|
||||
rtl837x_leds.c \
|
||||
rtl837x_leds_dump.c \
|
||||
rtl837x_phy.c \
|
||||
rtl837x_pins.c\
|
||||
rtl837x_port.c \
|
||||
rtl837x_stp.c
|
||||
rtl837x_stp.c \
|
||||
rtl837x_lldp.c \
|
||||
rtl837x_storm.c \
|
||||
rtl837x_lacp.c \
|
||||
rtl837x_trap.c \
|
||||
rtl837x_stp_cli.c
|
||||
SRCS += \
|
||||
httpd/httpd.c \
|
||||
httpd/page_impl.c
|
||||
httpd/page_impl.c \
|
||||
httpd/page_stp.c
|
||||
SRCS += \
|
||||
uip/timer.c \
|
||||
uip/uip.c \
|
||||
@@ -98,71 +172,120 @@ HTML := $(shell find html -name '*.js' -or -name '*.html' -or -name '*.svg' -or
|
||||
# Minified copy of the web UI sources, used as the fileadder input.
|
||||
# The raw html/ sources stay untouched for development; the minified
|
||||
# copy is a build artifact under output/.
|
||||
HTML_MIN := output/html_min
|
||||
HTML_MIN := $(BUILDDIR)/html_min
|
||||
HTML_MIN_LIST := $(BUILDDIR)/html_min.list
|
||||
HTML_MIN_STAMP := $(BUILDDIR)/html_min.stamp
|
||||
.PHONY: html_min
|
||||
html_min: $(HTML)
|
||||
html_min: $(HTML_MIN_STAMP)
|
||||
|
||||
$(HTML_MIN_LIST): FORCE
|
||||
@mkdir -p $(BUILDDIR)
|
||||
@echo $(sort $(HTML)) > $@.tmp
|
||||
@cmp -s $@.tmp $@ && rm -f $@.tmp || mv $@.tmp $@
|
||||
|
||||
$(HTML_MIN_STAMP): $(HTML) $(HTML_MIN_LIST) tools/minify.py $(if $(wildcard $(HTML_MIN)),,FORCE)
|
||||
rm -rf $(HTML_MIN)
|
||||
mkdir -p $(HTML_MIN)
|
||||
@for f in $(HTML); do python3 tools/minify.py $$f $(HTML_MIN)/$$(basename $$f) || exit 1; done
|
||||
@touch $@
|
||||
|
||||
html_data.c html_data.h &: $(HTML) | tools html_min
|
||||
tools/output/fileadder -a $(HTML_LOCATION) -s $(IMAGESIZE) -b BANK1 -z -d $(HTML_MIN) -p html_data
|
||||
html_data.c html_data.h &: $(HTML_MIN_STAMP) | tools
|
||||
tools/$(BUILDDIR)/fileadder -a $(HTML_LOCATION) -s $(IMAGESIZE) -b BANK1 -z -d $(HTML_MIN) -p html_data
|
||||
|
||||
$(VERSION_HEADER):
|
||||
$(VERSION_HEADER): FORCE
|
||||
@printf '%s\n' "#ifndef VERSION_H" "#define VERSION_H" \
|
||||
"#define VERSION_SW \"$(VERSION_EXTENSION)\"" \
|
||||
"#define BUILD_DATE \"$(BUILD_DATE)\"" \
|
||||
"#endif" > $(VERSION_HEADER)
|
||||
"#define SDCC_TOOLCHAIN \"$(SDCC_BANNER)\"" \
|
||||
"#endif" > $(VERSION_HEADER).tmp
|
||||
@cmp -s $(VERSION_HEADER).tmp $(VERSION_HEADER) \
|
||||
&& rm -f $(VERSION_HEADER).tmp \
|
||||
|| mv $(VERSION_HEADER).tmp $(VERSION_HEADER)
|
||||
|
||||
httpd: html_data.h
|
||||
|
||||
$(SUBDIRS):
|
||||
$(MAKE) -C $@
|
||||
$(MAKE) -C $@ BUILDDIR=$(BUILDDIR)
|
||||
|
||||
clean: $(SUBDIRSCLEAN)
|
||||
-rm -f html_data.c html_data.h $(VERSION_HEADER)
|
||||
-rm -f html_data.c html_data.h $(VERSION_HEADER) $(HTML_MIN_LIST) $(HTML_MIN_STAMP)
|
||||
-if [ -d $(BUILDDIR) ]; then find $(BUILDDIR) -type f ! -name "*.bin" -delete; fi
|
||||
|
||||
distclean: $(SUBDIRSCLEAN)
|
||||
-rm -f html_data.c html_data.h $(VERSION_HEADER)
|
||||
-rm -f html_data.c html_data.h $(VERSION_HEADER) $(HTML_MIN_LIST) $(HTML_MIN_STAMP)
|
||||
-rm -rf $(BUILDDIR)
|
||||
-rm -rf toolchain
|
||||
|
||||
ifneq ($(SDCC),)
|
||||
$(SDCC):
|
||||
sh sdcc/build.sh $(abspath $(SDCC_PREFIX))
|
||||
endif
|
||||
|
||||
$(SUBDIRSCLEAN):
|
||||
$(MAKE) -C $(@:clean=) clean
|
||||
|
||||
$(BUILDDIR)/%.rel: %.c | create_build_dir html_data.h
|
||||
# Objects depend on the flags they were built with, through a stamp file that
|
||||
# is rewritten only when CC_FLAGS actually changes.
|
||||
CCFLAGS_STAMP := $(BUILDDIR)/.ccflags
|
||||
|
||||
.PHONY: FORCE
|
||||
FORCE:
|
||||
|
||||
$(CCFLAGS_STAMP): FORCE | create_build_dir
|
||||
@echo '$(CC_FLAGS)' | cmp -s - $@ 2>/dev/null || echo '$(CC_FLAGS)' > $@
|
||||
|
||||
# The same for $(CONFIG): its path is stamped so switching to a config file
|
||||
# older than the image still rebuilds it, while $(CONFIG) itself as a normal
|
||||
# prerequisite covers edits to the selected file.
|
||||
$(CONFIG_STAMP): FORCE | create_build_dir
|
||||
@echo '$(CONFIG)' | cmp -s - $@ 2>/dev/null || echo '$(CONFIG)' > $@
|
||||
|
||||
$(BUILDDIR)/%.rel: %.c $(CCFLAGS_STAMP) | create_build_dir html_data.h
|
||||
$(CC) -MMD $(CC_FLAGS) -o $@ -c $<
|
||||
|
||||
$(BUILDDIR)/%.rel: %.asm | create_build_dir
|
||||
$(BUILDDIR)/%.rel: %.asm $(CCFLAGS_STAMP) | create_build_dir
|
||||
${ASM} ${AFLAGS} -o $@ $<
|
||||
# mv -f $(addprefix $(basename $^), .lst .rel .sym) .
|
||||
|
||||
$(BUILDDIR)/rtlplayground.ihx: $(OBJS) $(BUILDDIR)/crtbank.rel $(BUILDDIR)/crc16.rel
|
||||
$(CC) $(CC_FLAGS) --xram-size 49151 -Wl-bHOME=0x00000 -Wl-bBANK1=0x14000 -Wl-bBANK2=0x24000 -Wl-bBANK3=0x34000 -Wl-r -o $@ $^
|
||||
$(CC) $(CC_FLAGS) --xram-size 49151 -Wl-bHOME=0x00000 -Wl-bBANK1=0x14000 -Wl-bBANK2=0x24000 -Wl-bBANK3=0x34000 -Wl-bBANK4=0x44000 -Wl-r -o $@ $^
|
||||
|
||||
$(BUILDDIR)/rtlplayground.img: $(BUILDDIR)/rtlplayground.ihx
|
||||
objcopy --input-target=ihex -O binary $< $@
|
||||
|
||||
$(BUILDDIR)/rtlplayground-$(FILENAME_EXTENSION).bin: $(BUILDDIR)/rtlplayground.img | tools
|
||||
$(BUILDDIR)/rtlplayground-$(FILENAME_EXTENSION).bin: $(BUILDDIR)/rtlplayground.img $(CONFIG) $(CONFIG_STAMP) | tools
|
||||
if [ -e $@ ]; then rm $@; fi
|
||||
tools/output/imagebuilder -i $^ $@
|
||||
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 $(HTML_LOCATION) -s $(IMAGESIZE) -z -d $(HTML_MIN) -p html_data -b BANK1 $@
|
||||
tools/output/crc_calculator -u $@
|
||||
ln -sf $(MACHINE)/rtlplayground-$(FILENAME_EXTENSION).bin output/rtlplayground.bin
|
||||
tools/$(BUILDDIR)/imagebuilder -i $(BUILDDIR)/rtlplayground.img $@
|
||||
tools/$(BUILDDIR)/fileadder -a $(DEFAULT_CONFIG_LOCATION) -s $(IMAGESIZE) -d "$(CONFIG)" $@
|
||||
tools/$(BUILDDIR)/fileadder -a $(CONFIG_LOCATION) -s $(IMAGESIZE) -d "$(CONFIG)" $@
|
||||
tools/$(BUILDDIR)/fileadder -a $(HTML_LOCATION) -s $(IMAGESIZE) -z -d $(HTML_MIN) -b BANK1 $@
|
||||
tools/$(BUILDDIR)/crc_calculator -u $@
|
||||
ln -sf $(MACHINE)/rtlplayground-$(FILENAME_EXTENSION).bin $(BUILDDIR)/../rtlplayground.bin
|
||||
|
||||
.PHONY: clean distclean all $(SUBDIRS) $(SUBDIRSCLEAN) $(VERSION_HEADER) create_build_dir
|
||||
.PHONY: clean distclean all $(SUBDIRS) $(SUBDIRSCLEAN) create_build_dir
|
||||
|
||||
.PHONY:
|
||||
machine_check:
|
||||
# Every machine/ definition is compiled, so a machine added to machine/ is
|
||||
# covered without registering it anywhere. A board only counts once both its
|
||||
# <machine>.c and its <machine>.h are present: the header is what the rest of
|
||||
# the build includes, so a .c without one is an incomplete board. The _init.c
|
||||
# files are excluded here: they are the boot hooks, compiled separately below
|
||||
# (the board's own if it exists, otherwise default_machine_init.c). The
|
||||
# machine-specific defines are filtered out of $(CC_FLAGS) and supplied per
|
||||
# iteration instead, so this also works as "make machine_check MACHINE=...".
|
||||
MACHINE_DEFS := $(sort $(filter $(patsubst machine/%.h,%,$(wildcard machine/*.h)),$(patsubst machine/%.c,%,$(filter-out machine/%_init.c,$(wildcard machine/*.c)))))
|
||||
MACHINE_LESS_CC_FLAGS = $(filter-out -DMACHINE_%,$(CC_FLAGS))
|
||||
|
||||
.PHONY: machine_check
|
||||
machine_check: $(SDCC)
|
||||
@mkdir -p $(BUILDDIR)/tmp
|
||||
@set -eo pipefail; \
|
||||
for MACHINE in `grep -E '^[[:space:]]*(//[[:space:]]*)?#define MACHINE_' machine.h | sed -E 's%^[[:space:]]*(//[[:space:]]*)?#define MACHINE_%%' | awk '{print $$1}' | sort -u`; \
|
||||
for MACHINE in $(MACHINE_DEFS); \
|
||||
do \
|
||||
echo "Checking $${MACHINE}"; \
|
||||
$(CC) $(CC_FLAGS) -DMACHINE_$${MACHINE} -MMD -o $(BUILDDIR)/tmp/machine_check -c machine.c; \
|
||||
$(CC) $(CC_FLAGS) -DMACHINE_$${MACHINE} -MMD -o $(BUILDDIR)/tmp/machine_check -c machine_init.c; \
|
||||
$(CC) $(MACHINE_LESS_CC_FLAGS) -DMACHINE_NAME=$${MACHINE} -MMD -o $(BUILDDIR)/tmp/machine_check -c machine/$${MACHINE}.c; \
|
||||
INIT=machine/$${MACHINE}_init.c; \
|
||||
if [ ! -e $$INIT ] && [ ! -L $$INIT ]; then INIT=$(MACHINE_INIT_DEFAULT); fi; \
|
||||
$(CC) $(MACHINE_LESS_CC_FLAGS) -DMACHINE_NAME=$${MACHINE} -MMD -o $(BUILDDIR)/tmp/machine_check -c $$INIT; \
|
||||
done
|
||||
@rm -rf $(BUILDDIR)/tmp
|
||||
|
||||
|
||||
@@ -56,12 +56,19 @@ device simulator is provided, which runs entirely under Linux as a local webserv
|
||||
|
||||
## (0) Compiling Requirements
|
||||
|
||||
Install the following particular build requisites (Debian 12/13), note that Ubuntu 24.04
|
||||
still has an older version of sdcc, but you will need sdcc version 4.5 for the code to compile:
|
||||
Install the following particular build requisites (Debian 12/13):
|
||||
```
|
||||
sudo apt install make gcc sdcc xxd python-is-python3 libjson-c-dev zlib1g-dev
|
||||
sudo apt install make gcc g++ bison flex libboost-dev patch bzip2 curl xxd python-is-python3 libjson-c-dev zlib1g-dev
|
||||
```
|
||||
|
||||
The first `make` downloads SDCC 4.6.0, applies the fixes in `sdcc/` and builds it into
|
||||
`toolchain/`, which takes a few minutes. Later builds reuse it. The released SDCC 4.5.0
|
||||
and 4.6.0 miscompile parts of this code, see [SDCC bug #4153](https://sourceforge.net/p/sdcc/bugs/4153/)
|
||||
and [#4154](https://sourceforge.net/p/sdcc/bugs/4154/); both fixes are in SDCC trunk since
|
||||
r17039. The third patch is for [#4070](https://sourceforge.net/p/sdcc/bugs/4070/), where the
|
||||
assembler drops the top byte of a `__sfr32` address list; it is proposed upstream and lets
|
||||
`SFR_DATA_U32` be used again. To use an SDCC you installed yourself instead, run `make CC=sdcc ...`.
|
||||
|
||||
<details>
|
||||
<summary>If using Docker (click to expand)</summary>
|
||||
|
||||
@@ -90,6 +97,14 @@ docker run --rm -v $(pwd):/workspace rtlplayground-dev make MACHINE=DEFAULT_8C_1
|
||||
|
||||
The resulting `.bin` file appears in `output/` on your host.
|
||||
|
||||
Build the OEM web upgrade bin (after building the firmware bin)
|
||||
|
||||
```
|
||||
docker run --rm -v $(pwd):/workspace rtlplayground-dev make -C installer
|
||||
```
|
||||
|
||||
The resulting `rtlplayground_oem_upgrade.bin` file appears in `installer/output/` on your host.
|
||||
|
||||
Build host tools only:
|
||||
|
||||
```
|
||||
@@ -103,24 +118,36 @@ docker run --rm -p 8080:8080 -v $(pwd):/workspace rtlplayground-dev \
|
||||
tools/output/httpd_sim /workspace/html
|
||||
```
|
||||
|
||||
Edit `machine.h` or `config.txt` on your host, then re-run `make` — the
|
||||
Edit `config.txt` on your host, then re-run `make` — the
|
||||
source directory is mounted into the container, so changes take effect
|
||||
immediately. To build for a different machine, pass `MACHINE=...`.
|
||||
immediately. To build for a different machine, pass `MACHINE=<name>`, which
|
||||
selects `machine/<name>.c` (see [Adding a new device](doc/adding-a-device.md)).
|
||||
|
||||
</details>
|
||||
|
||||
## (1) Compiling for direct chip flashing AND upgrading an existing RTLPlayground running device
|
||||
|
||||
Edit machine.h with an editor like vi or nano. Select the correct machine the firmware should build for.
|
||||
Pass the machine that matches your device, for example:
|
||||
```
|
||||
make MACHINE=DEFAULT_8C_1SFP
|
||||
```
|
||||
The name selects the board definition `machine/<name>.c`; `make` without a
|
||||
`MACHINE` value stops with an error listing what is expected. If your board is
|
||||
not in the list yet, see [Adding a new device](doc/adding-a-device.md).
|
||||
|
||||
To embed a machine-specific startup configuration, pass `CONFIG`:
|
||||
```
|
||||
make MACHINE=DEFAULT_8C_1SFP CONFIG=configs/DEFAULT_8C_1SFP.txt
|
||||
```
|
||||
`CONFIG` defaults to `config.txt`; the selected file is embedded in the image
|
||||
and changing it triggers a rebuild.
|
||||
|
||||
> [!TIP]
|
||||
> You can write configuration parameters in config.txt (see below) in order your switch to get
|
||||
> straight at the first boot, a correct IP configuration.
|
||||
|
||||
Now, building the firmware image should work:
|
||||
```
|
||||
make
|
||||
```
|
||||
Now, building the firmware image should work.
|
||||
|
||||
Note, that the image generated ends in .bin, not .img, in order to make IMSProg happy.
|
||||
|
||||
image location is stored in `RTLPlayground/output/rtlplayground_version_machine.bin`
|
||||
@@ -158,9 +185,9 @@ example of compilation console output
|
||||
RTLPlayground/installer$ make
|
||||
mkdir -p output
|
||||
gcc updatebuilder.c -o output/updatebuilder
|
||||
sdas8051 -plosgff -o output/crtstart.rel crtstart.asm
|
||||
sdcc -mmcs51 --code-loc 0x1000 -o output/installer.rel -c installer.c
|
||||
sdcc -mmcs51 -Wl-bHOME=0x1100 -Wl-r -o output/rtlinstaller.ihx output/crtstart.rel output/installer.rel
|
||||
../toolchain/sdcc-3390765201/bin/sdas8051 -plosgff -o output/crtstart.rel crtstart.asm
|
||||
../toolchain/sdcc-3390765201/bin/sdcc -mmcs51 --code-loc 0x1000 -o output/installer.rel -c installer.c
|
||||
../toolchain/sdcc-3390765201/bin/sdcc -mmcs51 -Wl-bHOME=0x1100 -Wl-r -o output/rtlinstaller.ihx output/crtstart.rel output/installer.rel
|
||||
./output/updatebuilder -i output/rtlinstaller.ihx -o output/rtlplayground_oem_upgrade.bin ../output/rtlplayground.bin
|
||||
Input file size: 524288
|
||||
Bytes read: 524288
|
||||
@@ -186,7 +213,7 @@ Go to "Firmware update" tab, select the correct file.
|
||||
|
||||
> [!IMPORTANT]
|
||||
> If your device already runs RTLPlayground, you must upload the binary file /RTLPlayground/output/rtlplayground_Version_Machine.bin
|
||||
> If your device is OEM, you must upload the binary file /RTLPlayground/installer/outputrtlplayground_oem_upgrade.bin
|
||||
> If your device is OEM, you must upload the binary file /RTLPlayground/installer/output/rtlplayground_oem_upgrade.bin
|
||||
|
||||
> [!CAUTION]
|
||||
> Check one more time that your device matches the machine type before flashing.
|
||||
@@ -344,6 +371,7 @@ netmask zzz.zzz.zzz.zzz = Network mask of the switch
|
||||
port x name xxx = Name xxx the port number x
|
||||
port z 1g = Set 1g speed for port z
|
||||
igmp on/off = Turn IGMP on or off
|
||||
session xxxx = Web session timeout in seconds (default 200)
|
||||
```
|
||||
[To be continue]
|
||||
|
||||
@@ -352,11 +380,14 @@ Enjoy playing!
|
||||
## (11) Other documents
|
||||
|
||||
The following documents give further documentation on specific features of the RTL837x SoCs:
|
||||
- [Adding a new device](doc/adding-a-device.md)
|
||||
- [RTL8372/3 Feature support](doc/hardware.md)
|
||||
- [CPU Port](doc/CpuPort.md)
|
||||
- [L2 learning](doc/l2.md)
|
||||
- [IGMP (IP-MC streaming)](doc/igmp.md)
|
||||
- [SFP+ ports](doc/sfp.md)
|
||||
- [Trunking aka. port aggregation](doc/trunking.md)
|
||||
- [Trunking aka. port aggregation](doc/link_aggregation.md)
|
||||
- [LACP (802.3ad link aggregation)](doc/lacp.md)
|
||||
- [VLAN](doc/vlan.md)
|
||||
- [Storm control](doc/storm_control.md)
|
||||
- [Modifications and Flash replacement](doc/mods.md)
|
||||
@@ -21,6 +21,7 @@ extern __code const struct machine machine;
|
||||
extern __xdata struct machine_runtime machine_detected;
|
||||
extern __xdata uint8_t sfr_data[4];
|
||||
extern volatile __xdata uint32_t ticks;
|
||||
__xdata uint32_t rtl8224_release_tick;
|
||||
extern __xdata uint8_t tx_seq;
|
||||
extern __code const uint8_t * __code const hex;
|
||||
|
||||
@@ -124,18 +125,6 @@ void early_boot_handle_button(void) __banked
|
||||
void sds_config(uint8_t sds, uint8_t mode) __banked
|
||||
{
|
||||
print_string("sds_config sds: "); print_byte(sds); print_string(", mode: "); print_byte(mode); write_char('\n');
|
||||
sds_config_mac(sds, mode);
|
||||
|
||||
if (mode == SDS_10GR || mode == SDS_QXGMII)
|
||||
sds_write_v(sds, 0x21, 0x10, 0x4480); // Q002110:6480
|
||||
else
|
||||
sds_write_v(sds, 0x21, 0x10, 0x6480); // Q002110:6480
|
||||
sds_write_v(sds, 0x21, 0x13, 0x0400); // Q002113:0400
|
||||
sds_write_v(sds, 0x21, 0x18, 0x6d02); // Q002118:6d02
|
||||
sds_write_v(sds, 0x21, 0x1b, 0x424e); // Q00211b:424e
|
||||
sds_write_v(sds, 0x21, 0x1d, 0x0002); // Q00211d:0002
|
||||
sds_write_v(sds, 0x36, 0x1c, 0x1390); // Q00361c:1390
|
||||
sds_write_v(sds, 0x36, 0x14, 0x003f); // Q003614:003f
|
||||
|
||||
uint8_t page = 0;
|
||||
uint16_t v = 0;
|
||||
@@ -164,6 +153,21 @@ void sds_config(uint8_t sds, uint8_t mode) __banked
|
||||
print_string("Error in SDS Mode\n");
|
||||
return;
|
||||
}
|
||||
|
||||
sds_config_mac(sds, mode);
|
||||
|
||||
if (mode == SDS_10GR || mode == SDS_QXGMII)
|
||||
sds_write_v(sds, 0x21, 0x10, 0x4480); // Q002110:6480
|
||||
else
|
||||
sds_write_v(sds, 0x21, 0x10, 0x6480); // Q002110:6480
|
||||
|
||||
sds_write_v(sds, 0x21, 0x13, 0x0400); // Q002113:0400
|
||||
sds_write_v(sds, 0x21, 0x18, 0x6d02); // Q002118:6d02
|
||||
sds_write_v(sds, 0x21, 0x1b, 0x424e); // Q00211b:424e
|
||||
sds_write_v(sds, 0x21, 0x1d, 0x0002); // Q00211d:0002
|
||||
sds_write_v(sds, 0x36, 0x1c, 0x1390); // Q00361c:1390
|
||||
sds_write_v(sds, 0x36, 0x14, 0x003f); // Q003614:003f
|
||||
|
||||
sds_write_v(sds, 0x36, 0x10, v); // Q003610:0200
|
||||
|
||||
if (page == 0x2e) { // 10G Fiber / SDS_QXGMII
|
||||
@@ -240,10 +244,10 @@ void rtl8224_enable(void) __banked
|
||||
reg_bit_clear(RTL837X_REG_GPIO_32_63_OUTPUT, 4);
|
||||
// Configure Pin as output
|
||||
reg_bit_set(RTL837X_REG_GPIO_32_63_DIRECTION, 4);
|
||||
delay(100);
|
||||
delay(4);
|
||||
// Set pin 4 high
|
||||
reg_bit_set(RTL837X_REG_GPIO_32_63_OUTPUT, 4);
|
||||
delay(500);
|
||||
rtl8224_release_tick = ticks;
|
||||
}
|
||||
|
||||
|
||||
@@ -256,7 +260,7 @@ void nic_setup(void) __banked
|
||||
|
||||
// This sets the size of the RX buffer, the filling level is in 0x7874
|
||||
// R7848-000004ff
|
||||
REG_SET(RTL837X_REG_NIC_RXBUFF_RX, 0x4ff);
|
||||
REG_SET(RTL837X_REG_NIC_RXBUFF_RX, RTL837X_NIC_RX_LAST_SLOT);
|
||||
|
||||
// R7844-000007fe
|
||||
REG_SET(RTL837X_REG_NIC_BUFFSIZE_TX, 0x7fe);
|
||||
@@ -295,6 +299,7 @@ void nic_setup(void) __banked
|
||||
|
||||
// Sequence number of TX packets
|
||||
tx_seq = 0;
|
||||
REG_SET(RTL837X_NIC_INT_MSK, NIC_INT_RXIE);
|
||||
}
|
||||
|
||||
|
||||
|
||||
@@ -5,6 +5,8 @@
|
||||
// #define DEBUG
|
||||
// #define REGDBG 1
|
||||
|
||||
#include <8051.h>
|
||||
#include "cmd_parser.h"
|
||||
#include "rtl837x_common.h"
|
||||
#include "rtl837x_port.h"
|
||||
#include "rtl837x_flash.h"
|
||||
@@ -12,15 +14,24 @@
|
||||
#include "rtl837x_regs.h"
|
||||
#include "rtl837x_sfr.h"
|
||||
#include "rtl837x_stp.h"
|
||||
#include "rtl837x_lacp.h"
|
||||
#include "rtl837x_igmp.h"
|
||||
#include "rtl837x_bandwidth.h"
|
||||
#include "rtl837x_lldp.h"
|
||||
#include "rtl837x_storm.h"
|
||||
#include "rtl837x_trap.h"
|
||||
#include "rtl837x_cabletest.h"
|
||||
#include "sfp.h"
|
||||
#include "dhcp.h"
|
||||
#include "syslog.h"
|
||||
#include "sessions.h"
|
||||
#include "dns.h"
|
||||
#include "ntp.h"
|
||||
#include "uip/uip.h"
|
||||
#include "version.h"
|
||||
|
||||
#include "machine.h"
|
||||
#include "rtl837x_leds.h"
|
||||
#include "phy.h"
|
||||
|
||||
#pragma codeseg BANK2
|
||||
@@ -29,6 +40,7 @@
|
||||
extern __code const struct machine machine;
|
||||
extern __xdata bool stp_enabled;
|
||||
extern __code const uint8_t log_to_phys_port[9];
|
||||
extern __xdata bool lldp_enabled;
|
||||
|
||||
extern volatile __xdata uint32_t ticks;
|
||||
extern volatile __xdata uint8_t sfr_data[4];
|
||||
@@ -41,6 +53,8 @@ extern __xdata struct flash_region_t flash_region;
|
||||
|
||||
extern __xdata char passwd[21];
|
||||
|
||||
extern __xdata uint16_t session_timeout;
|
||||
|
||||
extern __xdata struct dhcp_state dhcp_state;
|
||||
|
||||
__xdata uint8_t vlan_names[VLAN_NAMES_SIZE];
|
||||
@@ -59,7 +73,6 @@ __xdata uint8_t hexvalue[4] = { 0 };
|
||||
|
||||
// Buffer for writing to flash 0x1fd000, copy to 0x1fe000
|
||||
__xdata uint8_t cmd_buffer[CMD_BUF_SIZE];
|
||||
__xdata uint8_t cmd_available;
|
||||
|
||||
__xdata char save_cmd;
|
||||
|
||||
@@ -155,10 +168,15 @@ uint8_t atoi_hex(uint8_t idx)
|
||||
break;
|
||||
}
|
||||
|
||||
if (h_idx == 8) {
|
||||
h_idx = 0;
|
||||
break;
|
||||
}
|
||||
|
||||
// swap hex nibbles
|
||||
val = (val >> 4) | (val << 4);
|
||||
|
||||
if (c - '0' < 10) {
|
||||
if ((uint8_t)(c - '0') < 10) {
|
||||
val |= c - '0';
|
||||
} else {
|
||||
c |= 0x20;
|
||||
@@ -249,7 +267,7 @@ uint8_t cmd_parse_port(uint8_t idx) {
|
||||
return 0;
|
||||
|
||||
port = machine.phys_to_log_port[port];
|
||||
if (port < machine.min_port || port > machine.max_port)
|
||||
if (!MACHINE_PORT_VALID(port))
|
||||
return 0;
|
||||
|
||||
atoi_results_u8 = port;
|
||||
@@ -524,11 +542,21 @@ void parse_lag(void)
|
||||
goto err;
|
||||
|
||||
if (cmd_compare(2, "d")) {
|
||||
lacp_lag_set(group, 0);
|
||||
port_lag_members_set(group, 0);
|
||||
return;
|
||||
}
|
||||
|
||||
uint8_t w = 2;
|
||||
__xdata uint8_t lacp_mode = 0;
|
||||
if (cmd_words_len > 2 && cmd_compare(2, "lacp")) {
|
||||
lacp_mode = 1;
|
||||
w = 3;
|
||||
if (cmd_words_len > 3 && cmd_compare(3, "off")) {
|
||||
lacp_lag_set(group, 0);
|
||||
return;
|
||||
}
|
||||
}
|
||||
while (w < cmd_words_len) {
|
||||
// Parse port.
|
||||
if (cmd_parse_port_separator(cmd_words_b[w++]) == 0)
|
||||
@@ -536,10 +564,15 @@ void parse_lag(void)
|
||||
|
||||
members |= ((uint16_t)1) << atoi_results_u8;
|
||||
}
|
||||
port_lag_members_set(group, members);
|
||||
if (lacp_mode) {
|
||||
lacp_lag_set(group, members);
|
||||
} else {
|
||||
lacp_lag_set(group, 0);
|
||||
port_lag_members_set(group, members);
|
||||
}
|
||||
return;
|
||||
err:
|
||||
cmd_error("lag (show | <1-4> (d | <port>...))\n");
|
||||
cmd_error("lag (show | <1-4> (d | [lacp] <port>...))\n");
|
||||
}
|
||||
|
||||
|
||||
@@ -711,9 +744,9 @@ void parse_isolate(void)
|
||||
}
|
||||
|
||||
if (cmd_compare(2, "off")) {
|
||||
for (uint8_t i = machine.min_port; i <= machine.max_port; i++)
|
||||
for (uint8_t i = MACHINE_MIN_PORT; i <= MACHINE_MAX_PORT; i++)
|
||||
members |= ((uint16_t)1) << i;
|
||||
members |= 0x200; // CPU-port
|
||||
members |= PMASK_CPU; // CPU-port
|
||||
port_isolate(port_configured, members);
|
||||
return;
|
||||
}
|
||||
@@ -765,7 +798,7 @@ void parse_ingress(void)
|
||||
if (!cmd_is_space_or_nul(idx))
|
||||
goto err;
|
||||
// Setting mode for all ports at once
|
||||
for (log_port = machine.min_port; log_port <= machine.max_port; log_port++) {
|
||||
for (log_port = MACHINE_MIN_PORT; log_port <= MACHINE_MAX_PORT; log_port++) {
|
||||
if (!port_ingress_filter(log_port, mode)) {
|
||||
cmd_error("cannot set the ingress filter for port "); print_phys_port(log_port); write_char('\n');
|
||||
return;
|
||||
@@ -817,7 +850,7 @@ void parse_mirror(void)
|
||||
print_string("NOT Enabled: ");
|
||||
}
|
||||
print_string("Mirroring port: ");
|
||||
print_phys_port(mirroring_port);
|
||||
print_phys_port(mPort >> 1);
|
||||
reg_read_m(RTL837x_MIRROR_CONF);
|
||||
uint16_t m = sfr_data[0];
|
||||
m = (m << 8) | sfr_data[1];
|
||||
@@ -837,9 +870,14 @@ void parse_mirror(void)
|
||||
if (cmd_words_len < 2)
|
||||
goto err;
|
||||
|
||||
// The first argument is the mirroring (monitor) port
|
||||
uint8_t ret = cmd_parse_port(cmd_words_b[1]);
|
||||
if (ret == 0)
|
||||
goto err;
|
||||
mirroring_port = atoi_results_u8;
|
||||
|
||||
uint8_t w = 2;
|
||||
uint8_t port;
|
||||
uint8_t ret;
|
||||
while (w < cmd_words_len) {
|
||||
uint8_t idx = cmd_words_b[w++];
|
||||
ret = cmd_parse_port(idx);
|
||||
@@ -849,10 +887,6 @@ void parse_mirror(void)
|
||||
idx += ret;
|
||||
port = atoi_results_u8;
|
||||
|
||||
// Use the first port argument as mirroring_port
|
||||
if (w == 2)
|
||||
mirroring_port = port;
|
||||
|
||||
ret = cmd_buffer[idx];
|
||||
uint16_t pmask = ((uint16_t)1) << port;
|
||||
if (ret != 't')
|
||||
@@ -875,6 +909,8 @@ void parse_port(void)
|
||||
cmd_error("\nUsage:" \
|
||||
"\nport <port> [show|on|off]" \
|
||||
"\nport <port> [10m|100m|1g|2g5|duplex] [half|full]" \
|
||||
"\nport <port> lldp [block|permit]" \
|
||||
"\nport <port> cabletest [show]" \
|
||||
"\nport <port> name [custom port name]\n");
|
||||
return;
|
||||
}
|
||||
@@ -893,10 +929,13 @@ void parse_port(void)
|
||||
print_string_x(port_names[phy_settings.port]);
|
||||
if (!machine.is_sfp[phy_settings.port]) {
|
||||
phy_show(phy_settings.port);
|
||||
} else {
|
||||
write_char('\n');
|
||||
}
|
||||
port_media_show(phy_settings.port);
|
||||
} else if (cmd_compare(2, "name")) {
|
||||
uint8_t i = 0;
|
||||
while ( (i < PORT_NAME_SIZE-1) && (cmd_buffer[cmd_words_b[3] + i] != NUL) ) {
|
||||
while ( cmd_words_len > 3 && (i < PORT_NAME_SIZE-1) && (cmd_buffer[cmd_words_b[3] + i] != NUL) ) {
|
||||
port_names[phy_settings.port][i] = name_char(cmd_buffer[cmd_words_b[3] + i]);
|
||||
i++;
|
||||
}
|
||||
@@ -904,8 +943,21 @@ void parse_port(void)
|
||||
print_string("\nName set to: \"");
|
||||
print_string_x(port_names[phy_settings.port]);
|
||||
print_string("\"\n");
|
||||
} else if (cmd_compare(2, "lldp")) {
|
||||
if (cmd_compare(3, "block")){
|
||||
lldp_logical_port_status &= ~((uint16_t)1 << phy_settings.port);
|
||||
} else if (cmd_compare(3, "permit")) {
|
||||
lldp_logical_port_status |= (uint16_t)1 << phy_settings.port;
|
||||
} else {
|
||||
print_string ("Unknown port <port> lldp [block|permit] command\n");
|
||||
}
|
||||
} else if (machine.is_sfp[phy_settings.port]) {
|
||||
print_string(" is SFP no PHY information available.\n");
|
||||
} else if (cmd_compare(2, "cabletest")) {
|
||||
if (cmd_words_len > 3 && cmd_compare(3, "show"))
|
||||
cabletest_show();
|
||||
else
|
||||
cabletest_run();
|
||||
} else if (cmd_compare(2, "10m")) {
|
||||
print_string(" 10M\n");
|
||||
phy_settings.speed = PHY_SPEED_10M;
|
||||
@@ -968,7 +1020,7 @@ void parse_mtu(void)
|
||||
uint8_t p;
|
||||
|
||||
if (cmd_compare(1, "show")) {
|
||||
for (p = machine.min_port; p <= machine.max_port; p++) {
|
||||
for (p = MACHINE_MIN_PORT; p <= MACHINE_MAX_PORT; p++) {
|
||||
reg_read_m(RTL8373_REG_MAC_L2_PORT_MAX_LEN + ((uint16_t) p << 8));
|
||||
uint16_t mtu = SFR_DATA_U16 & 0x3fff;
|
||||
print_string("Port "); print_phys_port(p);
|
||||
@@ -1165,6 +1217,32 @@ err:
|
||||
}
|
||||
|
||||
|
||||
static void print_temperature(uint16_t reg)
|
||||
{
|
||||
int16_t v;
|
||||
|
||||
reg_read(reg);
|
||||
v = SFR_DATA_U16;
|
||||
if (v < 0) {
|
||||
write_char('-');
|
||||
v = -v;
|
||||
}
|
||||
itoa(v >> 7);
|
||||
write_char('.');
|
||||
itoa(((v & 0x7f) * 10) >> 7);
|
||||
print_string(" C\n");
|
||||
}
|
||||
|
||||
|
||||
void parse_temp(void)
|
||||
{
|
||||
print_string("\nChip temperature: ");
|
||||
print_temperature(RTL837X_TM_RESULT);
|
||||
print_string("At power-on: ");
|
||||
print_temperature(RTL837X_TM_RESULT_POWERON);
|
||||
}
|
||||
|
||||
|
||||
void parse_sdsget(void)
|
||||
{
|
||||
__xdata uint8_t sds_id, page, reg, hex_size;
|
||||
@@ -1402,6 +1480,9 @@ void parse_passwd(void)
|
||||
passwd[j++] = c;
|
||||
} while (c != NUL && j < 20);
|
||||
passwd[j] = NUL;
|
||||
/* Logins made with the old password end here. With one session the next
|
||||
* login pushed a leftover one out; with several it would stay valid. */
|
||||
session_init();
|
||||
return;
|
||||
}
|
||||
cmd_error("Missing password\n");
|
||||
@@ -1541,6 +1622,57 @@ err:
|
||||
cmd_error("bw [in|out|status] <port> [<hexvalue>|off|drop|fc]\n");
|
||||
}
|
||||
|
||||
static __code const char * __code const storm_types[STORM_TYPES] = { "bcast", "mcast", "ucast", "umcast" };
|
||||
|
||||
void parse_storm(void)
|
||||
{
|
||||
uint8_t port, type, d;
|
||||
__xdata uint32_t rate;
|
||||
__xdata uint8_t *p;
|
||||
|
||||
if (cmd_words_len == 1)
|
||||
return storm_show();
|
||||
if (cmd_words_len < 4 || !cmd_parse_port_separator(cmd_words_b[1]))
|
||||
goto err;
|
||||
port = atoi_results_u8;
|
||||
|
||||
type = 0;
|
||||
while (!cmd_compare(2, storm_types[type])) {
|
||||
if (++type == STORM_TYPES)
|
||||
goto err;
|
||||
}
|
||||
|
||||
if (cmd_words_len == 4 && cmd_compare(3, "off"))
|
||||
return storm_off(port, type);
|
||||
if (cmd_words_len != 5)
|
||||
goto err;
|
||||
|
||||
rate = 0;
|
||||
p = &cmd_buffer[cmd_words_b[3]];
|
||||
do {
|
||||
d = *p++ - '0';
|
||||
if (d > 9 || rate > 99999999)
|
||||
goto err;
|
||||
rate = (rate << 3) + (rate << 1) + d;
|
||||
} while (*p != ' ' && *p != NUL);
|
||||
|
||||
if (cmd_compare(4, "pps")) {
|
||||
if (!rate || rate > 0xfffff)
|
||||
goto err;
|
||||
storm_set(port, type, rate, 1);
|
||||
} else if (cmd_compare(4, "kbps")) {
|
||||
if (!rate || rate > 10000000)
|
||||
goto err;
|
||||
storm_set(port, type, rate, 0);
|
||||
} else {
|
||||
goto err;
|
||||
}
|
||||
return;
|
||||
|
||||
err:
|
||||
print_string("usage: storm [<port> bcast|mcast|ucast|umcast <rate> pps|kbps|off]\n");
|
||||
}
|
||||
|
||||
void parse_syslog(void)
|
||||
{
|
||||
if (cmd_words_len < 2) // no argument -> print status
|
||||
@@ -1609,6 +1741,25 @@ void parse_syslog(void)
|
||||
}
|
||||
}
|
||||
|
||||
void parse_session(void)
|
||||
{
|
||||
if (cmd_words_len >= 2) {
|
||||
/* atoi_short() returns zero for no digits and for a value past
|
||||
* 65535; a zero-second timeout is not useful either, so both
|
||||
* tests are needed. */
|
||||
if (!atoi_short(cmd_words_b[1]) || !atoi_results_short) {
|
||||
print_string("Must be 1-65535 s\n");
|
||||
return;
|
||||
}
|
||||
session_timeout = atoi_results_short;
|
||||
}
|
||||
/* Also the confirmation after setting: one message for both. */
|
||||
print_string("Session timeout: ");
|
||||
itoa_short(session_timeout);
|
||||
print_string(" s\n");
|
||||
}
|
||||
|
||||
|
||||
// Parse command into words
|
||||
// cmd_words_len contains the number of words found.
|
||||
// cmd_words_b[] contains only start of a word offset.
|
||||
@@ -1689,6 +1840,8 @@ void print_gpio_status(void) {
|
||||
void print_sw_version(void) __banked {
|
||||
print_string("Software version: " VERSION_SW);
|
||||
print_string("\nBuild date: " BUILD_DATE);
|
||||
print_string("\nCompiler: SDCC " STR(__SDCC_VERSION_MAJOR) "." STR(__SDCC_VERSION_MINOR) "." STR(__SDCC_VERSION_PATCH) " #" STR(__SDCC_REVISION));
|
||||
print_string("\nToolchain: " SDCC_TOOLCHAIN);
|
||||
print_string("\nHardware: ");
|
||||
print_string(machine.machine_name);
|
||||
write_char('\n');
|
||||
@@ -1751,6 +1904,10 @@ void cmd_parser(void) __banked
|
||||
parse_mtu();
|
||||
} else if (cmd_compare(0, "syslog")) {
|
||||
parse_syslog();
|
||||
} else if (cmd_compare(0, "dns")) {
|
||||
dns_parse();
|
||||
} else if (cmd_compare(0, "ntp") || cmd_compare(0, "time")) {
|
||||
ntp_parse();
|
||||
} else if (cmd_compare(0, "ip")) {
|
||||
if (cmd_compare(1, "dhcp")) {
|
||||
dhcp_start();
|
||||
@@ -1847,12 +2004,23 @@ void cmd_parser(void) __banked
|
||||
}
|
||||
} else if (cmd_compare(0, "stp")) {
|
||||
stp_parse();
|
||||
#ifdef HEALTH
|
||||
} else if (cmd_compare(0, "health")) {
|
||||
health_show();
|
||||
#endif
|
||||
} else if (cmd_compare(0, "lacp")) {
|
||||
if (cmd_compare(1, "show"))
|
||||
lacp_show();
|
||||
else if (cmd_compare(1, "off"))
|
||||
lacp_off();
|
||||
else
|
||||
cmd_error("lacp (show | off); a group starts LACP with: lag <1-4> lacp <port>...\n");
|
||||
} else if (cmd_compare(0, "pvid")) {
|
||||
if (cmd_words_len == 3 && cmd_parse_port_separator(cmd_words_b[1]) != 0
|
||||
&& atoi_short(cmd_words_b[2]) && atoi_results_short && atoi_results_short <= 4094)
|
||||
&& atoi_short(cmd_words_b[2]) && atoi_results_short <= 4094)
|
||||
port_pvid_set(atoi_results_u8, atoi_results_short);
|
||||
else {
|
||||
cmd_error("pvid <port> <1-4094>\n");
|
||||
cmd_error("pvid <port> <0-4094>\n");
|
||||
}
|
||||
} else if (cmd_compare(0, "vlan")) {
|
||||
parse_vlan();
|
||||
@@ -1869,6 +2037,8 @@ void cmd_parser(void) __banked
|
||||
write_char('\n');
|
||||
} else if (cmd_compare(0, "gpio")) {
|
||||
print_gpio_status();
|
||||
} else if (cmd_compare(0, "leds")) {
|
||||
leds_dump();
|
||||
} else if (cmd_compare(0, "regget")) {
|
||||
parse_regget();
|
||||
} else if (cmd_compare(0, "regset")) {
|
||||
@@ -1889,13 +2059,12 @@ void cmd_parser(void) __banked
|
||||
parse_eee();
|
||||
} else if (cmd_compare(0, "bw")) {
|
||||
parse_bw();
|
||||
} else if (cmd_compare(0, "temp")) {
|
||||
parse_temp();
|
||||
} else if (cmd_compare(0, "storm")) {
|
||||
parse_storm();
|
||||
} else if (cmd_compare(0, "version")) {
|
||||
print_sw_version();
|
||||
} else if (cmd_compare(0, "time")) {
|
||||
print_string(" Tick counter: "); print_long(ticks); print_string(" Sec Counter: ");
|
||||
reg_read_m(RTL837X_REG_SEC_COUNTER);
|
||||
print_sfr_data();
|
||||
write_char('\n');
|
||||
} else if (cmd_compare(0, "history")) {
|
||||
__xdata uint16_t p = (cmd_history_ptr + 1) & CMD_HISTORY_MASK;
|
||||
__xdata uint8_t found_begin = 0;
|
||||
@@ -1911,6 +2080,18 @@ void cmd_parser(void) __banked
|
||||
}
|
||||
} else if (cmd_compare(0, "ingress")) {
|
||||
parse_ingress();
|
||||
} else if (cmd_compare(0, "session")) {
|
||||
parse_session();
|
||||
} else if (cmd_compare(0, "lldp")) {
|
||||
if (cmd_compare(1, "on")) {
|
||||
print_string("LLDP enabled\n");
|
||||
lldp_enabled = 1;
|
||||
trap_rma_set(RTL837X_RMA0E_CONF, RTL837X_RMA_ACT_TRAP);
|
||||
} else {
|
||||
print_string("LLDP disabled\n");
|
||||
lldp_enabled = 0;
|
||||
trap_rma_set(RTL837X_RMA0E_CONF, RTL837X_RMA_ACT_FORWARD);
|
||||
}
|
||||
}
|
||||
else {
|
||||
cmd_error("Unknown command\n");
|
||||
@@ -1980,7 +2161,7 @@ void execute_config(void) __banked
|
||||
skipping = 1;
|
||||
}
|
||||
c = flash_buf[cfg_idx++];
|
||||
if (c == 0 || c == '\n') {
|
||||
if (c == 0 || c == '\n' || c == '\r') {
|
||||
if (skipping) {
|
||||
skipping = 0;
|
||||
} else {
|
||||
@@ -2051,3 +2232,162 @@ void execute_commands(__xdata uint8_t *p) __banked {
|
||||
p++;
|
||||
};
|
||||
}
|
||||
|
||||
#ifdef HEALTH
|
||||
/*
|
||||
* Health instrumentation. The counters live in xdata and are fed from the
|
||||
* main loop through the banked entry points below; the "health" command
|
||||
* prints everything in one go. All figures are raw hex, diffing two dumps
|
||||
* gives the rates. Phase times are in system ticks (SYS_TICK_HZ = 200,
|
||||
* 5 ms each); a phase counts as slow when it spans 2 or more ticks.
|
||||
*/
|
||||
extern __xdata uint16_t len_left; /* httpd: bytes still to send of the current file */
|
||||
extern __xdata uint8_t entry; /* httpd: index of the file being sent */
|
||||
|
||||
static __xdata uint32_t health_loops;
|
||||
__xdata uint16_t health_rx_frames;
|
||||
|
||||
static __xdata uint16_t ph_last_t;
|
||||
static __xdata uint8_t ph_max[HEALTH_PHASES];
|
||||
static __xdata uint16_t ph_slow[HEALTH_PHASES];
|
||||
|
||||
/* The ISR increments ticks between the two byte reads of a 16-bit load, so
|
||||
* read until two loads agree. */
|
||||
static uint16_t ticks_lo(void)
|
||||
{
|
||||
uint16_t a, b;
|
||||
a = (uint16_t)ticks;
|
||||
do {
|
||||
b = a;
|
||||
a = (uint16_t)ticks;
|
||||
} while (a != b);
|
||||
return a;
|
||||
}
|
||||
|
||||
|
||||
void health_loop_start(void) __banked
|
||||
{
|
||||
health_loops++;
|
||||
ph_last_t = ticks_lo();
|
||||
}
|
||||
|
||||
|
||||
void health_phase(uint8_t id) __banked
|
||||
{
|
||||
__xdata uint16_t now = ticks_lo();
|
||||
__xdata uint16_t d = now - ph_last_t;
|
||||
ph_last_t = now;
|
||||
if (d > 0xff)
|
||||
d = 0xff;
|
||||
if ((uint8_t)d > ph_max[id])
|
||||
ph_max[id] = d;
|
||||
if (d >= 2)
|
||||
ph_slow[id]++;
|
||||
}
|
||||
|
||||
|
||||
/* Fill the stack area above the current frame with a pattern; the dump scans
|
||||
* for how much of it survived. The 8051 stack grows upward, so everything
|
||||
* above SP is free at this point. Interrupts are held off so an ISR does not
|
||||
* push into the area mid-paint. */
|
||||
void health_stack_paint(void) __banked
|
||||
{
|
||||
__xdata uint8_t from = SP + 4;
|
||||
__xdata uint8_t ea = EA;
|
||||
EA = 0;
|
||||
for (uint8_t i = 0xff; i >= from; i--)
|
||||
*(__idata uint8_t *)i = 0xa5;
|
||||
EA = ea;
|
||||
}
|
||||
|
||||
|
||||
static void ph_line(__code const char *name, uint8_t id)
|
||||
{
|
||||
print_string(name);
|
||||
print_string(" max ");
|
||||
print_byte(ph_max[id]);
|
||||
print_string(" slow ");
|
||||
print_short(ph_slow[id]);
|
||||
write_char('\n');
|
||||
}
|
||||
|
||||
|
||||
void health_show(void) __banked
|
||||
{
|
||||
__xdata uint8_t i;
|
||||
|
||||
print_string("up ");
|
||||
reg_read_m(RTL837X_REG_SEC_COUNTER);
|
||||
print_byte(sfr_data[0]); print_byte(sfr_data[1]);
|
||||
print_byte(sfr_data[2]); print_byte(sfr_data[3]);
|
||||
print_string(" ticks ");
|
||||
print_long(ticks);
|
||||
print_string(" loops ");
|
||||
print_long(health_loops);
|
||||
print_string(" rx ");
|
||||
print_short(health_rx_frames);
|
||||
write_char('\n');
|
||||
|
||||
/* Phase timing: max duration seen (ticks of 5 ms) and slow-pass count */
|
||||
ph_line("link", HEALTH_PH_LINK);
|
||||
ph_line("sfp ", HEALTH_PH_SFP);
|
||||
ph_line("rx ", HEALTH_PH_RX);
|
||||
ph_line("tx ", HEALTH_PH_TX);
|
||||
ph_line("stp ", HEALTH_PH_STP);
|
||||
ph_line("cmd ", HEALTH_PH_CMD);
|
||||
ph_line("lldp", HEALTH_PH_LLDP);
|
||||
|
||||
/* Stack: current depth plus how much of the painted area is untouched */
|
||||
print_string("sp ");
|
||||
print_byte(SP);
|
||||
print_string(" untouched ");
|
||||
i = 0;
|
||||
while (*(__idata uint8_t *)(0xff - i) == 0xa5 && i < 0x86)
|
||||
i++;
|
||||
print_byte(i);
|
||||
write_char('\n');
|
||||
|
||||
/* The TCP connection slot. One on this configuration; a build with more
|
||||
* has to say which slot each line is, so make that a visible decision. */
|
||||
#if UIP_CONNS != 1
|
||||
#error "health: the tcp dump prints a single slot, extend it for UIP_CONNS > 1"
|
||||
#endif
|
||||
i = 0;
|
||||
{
|
||||
print_string("tcp ");
|
||||
print_string("st ");
|
||||
print_byte(uip_conns[i].tcpstateflags);
|
||||
print_string(" tmr ");
|
||||
print_byte(uip_conns[i].timer);
|
||||
print_string(" rtx ");
|
||||
print_byte(uip_conns[i].nrtx);
|
||||
print_string(" rto ");
|
||||
print_byte(uip_conns[i].rto);
|
||||
print_string(" len ");
|
||||
print_short(uip_conns[i].len);
|
||||
print_string(" mss ");
|
||||
print_short(uip_conns[i].mss);
|
||||
write_char('\n');
|
||||
print_string(" lport ");
|
||||
print_short(HTONS(uip_conns[i].lport));
|
||||
print_string(" rport ");
|
||||
print_short(HTONS(uip_conns[i].rport));
|
||||
print_string(" rip ");
|
||||
print_short(HTONS(uip_conns[i].ripaddr[0]));
|
||||
write_char(' ');
|
||||
print_short(HTONS(uip_conns[i].ripaddr[1]));
|
||||
write_char('\n');
|
||||
}
|
||||
|
||||
/* httpd transfer state and the protocol flags */
|
||||
print_string("httpd left ");
|
||||
print_short(len_left);
|
||||
print_string(" entry ");
|
||||
print_byte(entry);
|
||||
print_string(" stp ");
|
||||
print_byte(stp_enabled);
|
||||
print_string(" mvlan ");
|
||||
print_short(management_vlan);
|
||||
write_char('\n');
|
||||
}
|
||||
#endif
|
||||
@@ -6,9 +6,36 @@
|
||||
#include "rtl837x_common.h"
|
||||
|
||||
extern __xdata uint8_t cmd_buffer[CMD_BUF_SIZE];
|
||||
extern __xdata uint8_t cmd_available;
|
||||
extern __bit __at(0x03) cmd_available;
|
||||
extern __xdata uint8_t err_status;
|
||||
|
||||
/* Health instrumentation, built with HEALTH=1. Without it the hooks below
|
||||
* compile to nothing and the "health" command does not exist. */
|
||||
#ifdef HEALTH
|
||||
/* Phase ids, in main-loop order */
|
||||
#define HEALTH_PH_LINK 0
|
||||
#define HEALTH_PH_SFP 1
|
||||
#define HEALTH_PH_RX 2
|
||||
#define HEALTH_PH_TX 3
|
||||
#define HEALTH_PH_STP 4
|
||||
#define HEALTH_PH_CMD 5
|
||||
#define HEALTH_PH_LLDP 6
|
||||
#define HEALTH_PHASES (HEALTH_PH_LLDP + 1) /* last id + 1: sizes the arrays in cmd_parser.c */
|
||||
|
||||
extern __xdata uint16_t health_rx_frames;
|
||||
#define health_rx_frame() health_rx_frames++
|
||||
|
||||
void health_loop_start(void) __banked; /* once per idle() pass, resets the phase baseline */
|
||||
void health_phase(uint8_t id) __banked; /* after each phase; accounts ticks since last checkpoint */
|
||||
void health_stack_paint(void) __banked; /* paint the unused stack area once at boot */
|
||||
void health_show(void) __banked; /* "health" CLI dump */
|
||||
#else
|
||||
#define health_rx_frame()
|
||||
#define health_loop_start()
|
||||
#define health_phase(id)
|
||||
#define health_stack_paint()
|
||||
#endif
|
||||
|
||||
void cmd_tokenize(void) __banked;
|
||||
void cmd_parser(void) __banked;
|
||||
void execute_config(void) __banked;
|
||||
|
||||
@@ -51,7 +51,7 @@ __xdata uip_ipaddr_t server;
|
||||
#define DHCP_END 255
|
||||
|
||||
#pragma codeseg BANK3
|
||||
#pragma constseg BANK2
|
||||
#pragma constseg BANK3
|
||||
|
||||
struct dhcp_pkt {
|
||||
uint8_t type;
|
||||
@@ -139,7 +139,6 @@ void dhcp_addopt_request_ip(void)
|
||||
DHCP_OPT[dhcp_state.opt_ptr++] = dhcp_state.current_ip[1];
|
||||
DHCP_OPT[dhcp_state.opt_ptr++] = dhcp_state.current_ip[2];
|
||||
DHCP_OPT[dhcp_state.opt_ptr++] = dhcp_state.current_ip[3];
|
||||
memcpy(&DHCP_OPT[dhcp_state.opt_ptr], uip_ethaddr.addr, 4);
|
||||
}
|
||||
|
||||
|
||||
@@ -151,13 +150,11 @@ void dhcp_addopt_server_id(void)
|
||||
DHCP_OPT[dhcp_state.opt_ptr++] = dhcp_state.server[1];
|
||||
DHCP_OPT[dhcp_state.opt_ptr++] = dhcp_state.server[2];
|
||||
DHCP_OPT[dhcp_state.opt_ptr++] = dhcp_state.server[3];
|
||||
memcpy(&DHCP_OPT[dhcp_state.opt_ptr], uip_ethaddr.addr, 4);
|
||||
}
|
||||
|
||||
|
||||
void dhcp_send_discover(void)
|
||||
{
|
||||
print_string("dhcp_send_discover called\n");
|
||||
dhcp_prepare_request();
|
||||
|
||||
dhcp_state.opt_ptr = 0;
|
||||
@@ -193,7 +190,6 @@ void dhcp_send_discover(void)
|
||||
|
||||
void dhcp_send_request(void)
|
||||
{
|
||||
print_string("dhcp_send_request called\n");
|
||||
dhcp_prepare_request();
|
||||
|
||||
dhcp_state.opt_ptr = 0;
|
||||
@@ -232,19 +228,21 @@ void ip_opt(__xdata uint8_t * ip)
|
||||
{
|
||||
dhcp_state.opt_ptr++;
|
||||
uint8_t len = DHCP_OPT[dhcp_state.opt_ptr++];
|
||||
*ip++ = DHCP_OPT[dhcp_state.opt_ptr++];
|
||||
*ip++ = DHCP_OPT[dhcp_state.opt_ptr++];
|
||||
*ip++ = DHCP_OPT[dhcp_state.opt_ptr++];
|
||||
*ip++ = DHCP_OPT[dhcp_state.opt_ptr++];
|
||||
if (len >= 4)
|
||||
memcpy(ip, &DHCP_OPT[dhcp_state.opt_ptr], 4);
|
||||
// There may be more than one IP option, such as 2 DNS servers advertised
|
||||
dhcp_state.opt_ptr += len - 4;
|
||||
dhcp_state.opt_ptr += len;
|
||||
}
|
||||
|
||||
|
||||
void long_opt(void)
|
||||
uint8_t long_opt(void)
|
||||
{
|
||||
dhcp_state.opt_ptr++;
|
||||
dhcp_state.opt_ptr++;
|
||||
uint8_t len = DHCP_OPT[dhcp_state.opt_ptr++];
|
||||
if (len < 4) {
|
||||
dhcp_state.opt_ptr += len;
|
||||
return 0;
|
||||
}
|
||||
long_value = DHCP_OPT[dhcp_state.opt_ptr++];
|
||||
long_value <<= 8;
|
||||
long_value |= DHCP_OPT[dhcp_state.opt_ptr++];
|
||||
@@ -252,6 +250,8 @@ void long_opt(void)
|
||||
long_value |= DHCP_OPT[dhcp_state.opt_ptr++];
|
||||
long_value <<= 8;
|
||||
long_value |= DHCP_OPT[dhcp_state.opt_ptr++];
|
||||
dhcp_state.opt_ptr += len - 4;
|
||||
return 1;
|
||||
}
|
||||
|
||||
|
||||
@@ -275,16 +275,16 @@ void parse_opts(void)
|
||||
ip_opt(&dhcp_state.broadcast[0]);
|
||||
break;
|
||||
case DHCP_LEASE:
|
||||
long_opt();
|
||||
dhcp_state.lease = long_value;
|
||||
if (long_opt())
|
||||
dhcp_state.lease = long_value;
|
||||
break;
|
||||
case DHCP_REBIND:
|
||||
long_opt();
|
||||
dhcp_state.rebind = long_value;
|
||||
if (long_opt())
|
||||
dhcp_state.rebind = long_value;
|
||||
break;
|
||||
case DHCP_RENEWAL:
|
||||
long_opt();
|
||||
dhcp_state.renewal = long_value;
|
||||
if (long_opt())
|
||||
dhcp_state.renewal = long_value;
|
||||
break;
|
||||
case DHCP_END:
|
||||
break;
|
||||
@@ -376,13 +376,11 @@ void dhcp_start(void) __banked
|
||||
*tid++ = SFR_DATA_8;
|
||||
*tid = SFR_DATA_0;
|
||||
dhcp_state.state = DHCP_START;
|
||||
print_string("dhcp_start done\n");
|
||||
}
|
||||
|
||||
|
||||
void dhcp_stop(void) __banked
|
||||
{
|
||||
print_string("dhcp_stop called\n");
|
||||
uip_udp_remove(dhcp_state.conn);
|
||||
dhcp_state.state = DHCP_OFF;
|
||||
}
|
||||
|
||||
@@ -0,0 +1,238 @@
|
||||
/*
|
||||
* DNS resolver for the RTL837x platform
|
||||
* This code is in the Public Domain
|
||||
*/
|
||||
|
||||
#include <stdint.h>
|
||||
#include "rtl837x_common.h"
|
||||
#include "cmd_parser.h"
|
||||
#include "uip/uip.h"
|
||||
#include "dhcp.h"
|
||||
#include "dns.h"
|
||||
|
||||
#pragma codeseg BANK3
|
||||
#pragma constseg BANK3
|
||||
|
||||
#define DNS_PORT 53
|
||||
#define DNS_TRIES 6
|
||||
#define DNS_TIMEOUT (2 * SYS_TICK_HZ)
|
||||
|
||||
extern __xdata struct dhcp_state dhcp_state;
|
||||
extern volatile __xdata uint32_t ticks;
|
||||
|
||||
__xdata struct dns_state dns_state;
|
||||
__xdata uip_ipaddr_t dns_ip;
|
||||
uint8_t * __xdata dns_cand[3];
|
||||
|
||||
|
||||
static uint8_t dns_used(__xdata uint8_t *a) __reentrant
|
||||
{
|
||||
return a[0] | a[1] | a[2] | a[3];
|
||||
}
|
||||
|
||||
|
||||
static uint8_t dns_name_is_ip(void) __reentrant
|
||||
{
|
||||
__xdata char *s = dns_state.name;
|
||||
uint8_t i, n;
|
||||
uint16_t v;
|
||||
|
||||
for (i = 0; i < 4; i++) {
|
||||
v = 0;
|
||||
n = 0;
|
||||
while (*s >= '0' && *s <= '9') {
|
||||
v = v * 10 + (*s++ - '0');
|
||||
if (++n > 3 || v > 255)
|
||||
return 0;
|
||||
}
|
||||
if (!n)
|
||||
return 0;
|
||||
dns_state.addr[i] = v;
|
||||
if (i < 3 && *s++ != '.')
|
||||
return 0;
|
||||
}
|
||||
return !*s;
|
||||
}
|
||||
|
||||
|
||||
static void dns_next(void) __reentrant
|
||||
{
|
||||
uint8_t k = 0;
|
||||
|
||||
if (dns_state.conn) {
|
||||
uip_udp_remove(dns_state.conn);
|
||||
dns_state.conn = 0;
|
||||
}
|
||||
if (dns_used(dns_state.server[0]))
|
||||
dns_cand[k++] = dns_state.server[0];
|
||||
if (dns_used(dns_state.server[1]))
|
||||
dns_cand[k++] = dns_state.server[1];
|
||||
if (dns_used(dhcp_state.dns))
|
||||
dns_cand[k++] = dhcp_state.dns;
|
||||
if (!k || dns_state.tries >= DNS_TRIES) {
|
||||
dns_state.status = DNS_FAILED;
|
||||
if (dns_state.verbose)
|
||||
print_string(k ? "DNS: no answer\n" : "DNS: no server\n");
|
||||
return;
|
||||
}
|
||||
k = dns_state.tries % k;
|
||||
uip_ipaddr(dns_ip, dns_cand[k][0], dns_cand[k][1], dns_cand[k][2], dns_cand[k][3]);
|
||||
dns_state.conn = uip_udp_new(&dns_ip, HTONS(DNS_PORT));
|
||||
if (!dns_state.conn) {
|
||||
dns_state.status = DNS_FAILED;
|
||||
print_string("DNS: no free UDP connection\n");
|
||||
return;
|
||||
}
|
||||
dns_state.send = 1;
|
||||
}
|
||||
|
||||
|
||||
static void dns_query(void) __reentrant
|
||||
{
|
||||
__xdata uint8_t *p = (__xdata uint8_t *)uip_appdata;
|
||||
__xdata char *n = dns_state.name;
|
||||
__xdata uint8_t *label;
|
||||
uint16_t len = 12;
|
||||
|
||||
dns_state.id++;
|
||||
p[0] = dns_state.id >> 8;
|
||||
p[1] = dns_state.id;
|
||||
p[2] = 0x01;
|
||||
p[3] = 0x00;
|
||||
p[4] = 0;
|
||||
p[5] = 1;
|
||||
p[6] = p[7] = p[8] = p[9] = p[10] = p[11] = 0;
|
||||
while (*n) {
|
||||
label = p + len++;
|
||||
*label = 0;
|
||||
while (*n && *n != '.') {
|
||||
p[len++] = *n++;
|
||||
(*label)++;
|
||||
}
|
||||
if (*n)
|
||||
n++;
|
||||
}
|
||||
p[len++] = 0;
|
||||
p[len++] = 0;
|
||||
p[len++] = 1;
|
||||
p[len++] = 0;
|
||||
p[len++] = 1;
|
||||
uip_udp_send(len);
|
||||
}
|
||||
|
||||
|
||||
static uint16_t dns_skip_name(__xdata uint8_t *p, uint16_t i, uint16_t len) __reentrant
|
||||
{
|
||||
while (i < len) {
|
||||
if (!p[i])
|
||||
return i + 1;
|
||||
if ((p[i] & 0xc0) == 0xc0)
|
||||
return i + 2;
|
||||
i += p[i] + 1;
|
||||
}
|
||||
return len;
|
||||
}
|
||||
|
||||
|
||||
static void dns_answer(void) __reentrant
|
||||
{
|
||||
__xdata uint8_t *p = (__xdata uint8_t *)uip_appdata;
|
||||
uint16_t len = uip_datalen();
|
||||
uint16_t i, an;
|
||||
|
||||
if (dns_state.status != DNS_PENDING || len < 12 || !(p[2] & 0x80)
|
||||
|| p[0] != (uint8_t)(dns_state.id >> 8) || p[1] != (uint8_t)dns_state.id)
|
||||
return;
|
||||
an = ((uint16_t)p[6] << 8) | p[7];
|
||||
i = dns_skip_name(p, 12, len) + 4;
|
||||
while (!(p[3] & 0x0f) && an-- && i + 10 <= len) {
|
||||
i = dns_skip_name(p, i, len);
|
||||
if (i + 10 > len)
|
||||
break;
|
||||
if (!p[i] && p[i + 1] == 1 && !p[i + 8] && p[i + 9] == 4 && i + 14 <= len) {
|
||||
memcpy(dns_state.addr, p + i + 10, 4);
|
||||
dns_state.status = DNS_DONE;
|
||||
uip_udp_remove(dns_state.conn);
|
||||
dns_state.conn = 0;
|
||||
if (dns_state.verbose) {
|
||||
print_string("DNS: ");
|
||||
print_string_x(dns_state.name);
|
||||
print_string(" is ");
|
||||
print_ip(dns_state.addr);
|
||||
write_char('\n');
|
||||
}
|
||||
return;
|
||||
}
|
||||
i += 10 + (((uint16_t)p[i + 8] << 8) | p[i + 9]);
|
||||
}
|
||||
dns_state.tries = DNS_TRIES;
|
||||
dns_next();
|
||||
}
|
||||
|
||||
|
||||
void dns_init(void) __banked
|
||||
{
|
||||
memset(dns_state.server, 0, sizeof(dns_state.server));
|
||||
dns_state.name[0] = 0;
|
||||
dns_state.status = DNS_IDLE;
|
||||
dns_state.conn = 0;
|
||||
}
|
||||
|
||||
|
||||
void dns_lookup(void) __banked
|
||||
{
|
||||
if (dns_name_is_ip()) {
|
||||
dns_state.status = DNS_DONE;
|
||||
return;
|
||||
}
|
||||
dns_state.tries = 0;
|
||||
dns_state.status = DNS_PENDING;
|
||||
dns_next();
|
||||
}
|
||||
|
||||
|
||||
void dns_show(void) __banked
|
||||
{
|
||||
print_string("DNS servers ");
|
||||
print_ip(dns_state.server[0]);
|
||||
write_char(' ');
|
||||
print_ip(dns_state.server[1]);
|
||||
print_string(", from DHCP ");
|
||||
print_ip(dhcp_state.dns);
|
||||
write_char('\n');
|
||||
if (!dns_state.name[0])
|
||||
return;
|
||||
print_string("Last lookup ");
|
||||
print_string_x(dns_state.name);
|
||||
print_string(": ");
|
||||
if (dns_state.status == DNS_DONE)
|
||||
print_ip(dns_state.addr);
|
||||
else if (dns_state.status == DNS_PENDING)
|
||||
print_string("pending");
|
||||
else
|
||||
print_string("failed");
|
||||
write_char('\n');
|
||||
}
|
||||
|
||||
|
||||
void dns_callback(uint16_t lport) __banked __reentrant
|
||||
{
|
||||
if (!dns_state.conn || lport != dns_state.conn->lport)
|
||||
return;
|
||||
if (uip_newdata()) {
|
||||
dns_answer();
|
||||
return;
|
||||
}
|
||||
if (dns_state.status != DNS_PENDING)
|
||||
return;
|
||||
if (dns_state.send) {
|
||||
dns_state.send = 0;
|
||||
dns_state.sent = ticks;
|
||||
dns_query();
|
||||
return;
|
||||
}
|
||||
if (ticks - dns_state.sent >= DNS_TIMEOUT) {
|
||||
dns_state.tries++;
|
||||
dns_next();
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,36 @@
|
||||
#ifndef _DNS_H_
|
||||
#define _DNS_H_
|
||||
|
||||
#include <stdint.h>
|
||||
|
||||
#define DNS_NAME_LEN 48
|
||||
|
||||
#define DNS_IDLE 0
|
||||
#define DNS_PENDING 1
|
||||
#define DNS_DONE 2
|
||||
#define DNS_FAILED 3
|
||||
|
||||
struct uip_udp_conn;
|
||||
|
||||
struct dns_state {
|
||||
uint8_t server[2][4]; /* configured servers, 0.0.0.0 = unused */
|
||||
char name[DNS_NAME_LEN]; /* name of the current or last lookup */
|
||||
uint8_t addr[4]; /* address found by the last lookup */
|
||||
uint8_t status; /* DNS_* */
|
||||
uint8_t verbose; /* print the result of the lookup */
|
||||
uint8_t tries; /* queries sent for the current lookup */
|
||||
uint8_t send; /* a query is due on the connection */
|
||||
uint16_t id;
|
||||
uint32_t sent; /* ticks when the last query went out */
|
||||
struct uip_udp_conn *conn;
|
||||
};
|
||||
|
||||
extern __xdata struct dns_state dns_state;
|
||||
|
||||
void dns_init(void) __banked;
|
||||
void dns_lookup(void) __banked;
|
||||
void dns_show(void) __banked;
|
||||
void dns_callback(uint16_t lport) __banked __reentrant;
|
||||
void dns_parse(void) __banked __reentrant;
|
||||
|
||||
#endif
|
||||
@@ -0,0 +1,63 @@
|
||||
/*
|
||||
* Command line interface of the DNS resolver for the RTL837x platform
|
||||
* This code is in the Public Domain
|
||||
*/
|
||||
|
||||
#include <stdint.h>
|
||||
#include "rtl837x_common.h"
|
||||
#include "cmd_parser.h"
|
||||
#include "dns.h"
|
||||
|
||||
#pragma codeseg BANK2
|
||||
#pragma constseg BANK2
|
||||
|
||||
extern __xdata uint8_t cmd_buffer[CMD_BUF_SIZE];
|
||||
extern __xdata uint8_t cmd_words_len;
|
||||
extern __xdata uint8_t cmd_words_b[15];
|
||||
extern __xdata uint8_t ip[4];
|
||||
uint8_t cmd_compare(uint8_t start, __code const uint8_t * cmd);
|
||||
uint8_t parse_ip(uint8_t idx);
|
||||
|
||||
|
||||
void dns_parse(void) __banked __reentrant
|
||||
{
|
||||
__xdata uint8_t *s;
|
||||
uint8_t n;
|
||||
|
||||
if (cmd_words_len < 2) {
|
||||
dns_show();
|
||||
return;
|
||||
}
|
||||
if (cmd_compare(1, "server") && cmd_words_len >= 3 && parse_ip(cmd_words_b[2])) {
|
||||
memcpy(dns_state.server[0], ip, 4);
|
||||
memset(dns_state.server[1], 0, 4);
|
||||
if (cmd_words_len >= 4) {
|
||||
if (!parse_ip(cmd_words_b[3]))
|
||||
goto err;
|
||||
memcpy(dns_state.server[1], ip, 4);
|
||||
}
|
||||
return;
|
||||
}
|
||||
if (cmd_compare(1, "lookup") && cmd_words_len >= 3) {
|
||||
if (dns_state.status == DNS_PENDING) {
|
||||
print_string("DNS: a lookup is already running\n");
|
||||
return;
|
||||
}
|
||||
s = &cmd_buffer[cmd_words_b[2]];
|
||||
for (n = 0; s[n] && s[n] != ' '; n++) {
|
||||
if (n == DNS_NAME_LEN - 1)
|
||||
goto err;
|
||||
dns_state.name[n] = s[n];
|
||||
}
|
||||
dns_state.name[n] = 0;
|
||||
dns_state.verbose = 1;
|
||||
dns_lookup();
|
||||
if (dns_state.status == DNS_DONE) {
|
||||
print_ip(dns_state.addr);
|
||||
write_char('\n');
|
||||
}
|
||||
return;
|
||||
}
|
||||
err:
|
||||
print_string("Error: dns [server <ip> [<ip>]|lookup <name>]\n");
|
||||
}
|
||||
@@ -34,6 +34,21 @@ Next, transfer the actual packet over by repeating the SFR function with a
|
||||
pointer to the frame on the ASIC directly after the frame header and a
|
||||
length as given by the length in the frame header + 7, again divided by 8.
|
||||
|
||||
The RX ring's inclusive last slot is configured as `0x4ff`, giving a byte
|
||||
range of `0x0000` through `0x27ff`. If the descriptor occupies the last slot
|
||||
at `0x27f8`, its payload starts at `0x0000`, not `0x2800`. The RX copy code
|
||||
normalizes that starting address before DMA. It relies on the hardware to
|
||||
wrap transfers that start inside the ring and cross its end, as observed
|
||||
on SWTGW218AS. Programming an initial address of `0x2800` does not wrap
|
||||
and can instead read zeros or stale TX data outside the RX ring.
|
||||
|
||||
Frame lengths are checked against `UIP_CONF_BUFFER_SIZE` before copying.
|
||||
DMA works in whole eight-byte units. `UIP_CONF_BUFFER_ALLOC_SIZE` rounds
|
||||
the backing storage up to the next complete word: 1,560 bytes for the
|
||||
1,556-byte logical frame limit. This padding allows a maximum-size frame
|
||||
to be transferred directly without a separate tail copy. The logical
|
||||
frame limit and advertised MSS are unchanged.
|
||||
|
||||
The received frame will have an RTL proprietary Ethernet frame type of
|
||||
0x8899 (RRPC) where normally the frame type 0x0800 for IPv4 would be located.
|
||||
Further 6 bytes follow describing the frame, before the normal IPv4 data
|
||||
|
||||
@@ -0,0 +1,220 @@
|
||||
# Adding a new device
|
||||
|
||||
A device is supported by adding one board header `.h` and board definition `.c` file
|
||||
under `machine/`.
|
||||
the build selects the definition by name, and CI compiles every file it finds.
|
||||
|
||||
To compile for your added device, do:
|
||||
```
|
||||
make MACHINE=<NAME>
|
||||
```
|
||||
|
||||
## What a new device consists of
|
||||
|
||||
| File | Purpose |
|
||||
|------|---------|
|
||||
| `machine/<NAME>.c` | the `struct machine machine` board definition |
|
||||
| `machine/<NAME>.h` | `MACHINE_MODEL` (the model string shown in the UI and the CLI) and `MACHINE_CHIPSET` (`RTL8372` or `RTL8373`) |
|
||||
| `machine/<NAME>_init.c` | optional `machine_custom_init()` boot hook, for registers the machine definition cannot express |
|
||||
| `doc/devices/<name>.md` + `doc/supported_devices.md` | (only if the board is verified and you want to support it publicly) user-facing documentation |
|
||||
|
||||
`<NAME>` is the machine symbol without its `MACHINE_` prefix, so `MACHINE` is
|
||||
passed exactly as spelled on the command line: `make MACHINE=MY_BOARD_1_0`
|
||||
needs `machine/MY_BOARD_1_0.c` and `machine/MY_BOARD_1_0.h`.
|
||||
|
||||
## Step 1: the board header
|
||||
|
||||
`machine/<NAME>.h` carries the product string and the SoC family. Use the
|
||||
existing files as the template:
|
||||
|
||||
```c
|
||||
#ifndef _MACHINE_MY_BOARD_1_0_H_
|
||||
#define _MACHINE_MY_BOARD_1_0_H_
|
||||
|
||||
#define MACHINE_MODEL "Brand Model V1.0"
|
||||
#define MACHINE_CHIPSET RTL8373
|
||||
|
||||
#endif
|
||||
```
|
||||
|
||||
`MACHINE_CHIPSET` is `RTL8373` or `RTL8372`; a build without it will fail. It selects the default
|
||||
logical port range: `MACHINE_MIN_PORT` is `0` for RTL8373 and `3` for RTL8372, while
|
||||
`MACHINE_MAX_PORT` is `8`, the highest logical port index. A board with a different range defines
|
||||
`MACHINE_MIN_PORT` and/or `MACHINE_MAX_PORT` in its header to override the
|
||||
defaults.
|
||||
|
||||
The device product string is what the CLI or the Web-Interface will print to identify the device; `machine.h` includes it by name.
|
||||
|
||||
> [!NOTE]
|
||||
> `machine_name` is a `char[30]` and `MACHINE_MODEL` is copied into it at
|
||||
> compile time. A longer model string still builds, but SDCC emits
|
||||
> `warning 147: excess elements in array of chars initializer` and the name is
|
||||
> truncated in the UI. Keep it to 29 characters or less.
|
||||
|
||||
## Step 2: the board definition
|
||||
|
||||
`machine/<NAME>.c` starts with the shared prelude and then defines the single
|
||||
`machine` object. Copy the closest existing board and adjust it — do not start
|
||||
from a blank file, the field meanings are easier to see in a real example.
|
||||
The best choice is usually a device that has the same number and type of Ethernet
|
||||
ports and the same number of SFP+ cages.
|
||||
|
||||
```c
|
||||
#include "machine_defs.h"
|
||||
|
||||
__code const struct machine machine = {
|
||||
MACHINE_COMMON_SETTING,
|
||||
.n_sfp = 1,
|
||||
.log_to_phys_port = {1, 2, 3, 4, 5, 6, 7, 8, 9},
|
||||
.phys_to_log_port = {0, 1, 2, 3, 4, 5, 6, 7, 8},
|
||||
.is_sfp = {0, 0, 0, 0, 0, 0, 0, 0, 1},
|
||||
|
||||
.sfp_port[0].pin_detect = GPIO30_ACL_BIT3_EN,
|
||||
.sfp_port[0].pin_los = GPIO37,
|
||||
.sfp_port[0].pin_tx_disable = GPIO_NA,
|
||||
.sfp_port[0].sds = 1,
|
||||
.sfp_port[0].i2c = I2CBUS( GPIO39_I2C_SDA4, GPIO40_I2C_SCL3_MDC1 ),
|
||||
.reset_pin = GPIO_NA,
|
||||
|
||||
.high_leds = { .mux = LED_27 | LED_29, .enable = LED_28_SYS | LED_29 },
|
||||
.port_led_set = { 0, 0, 0, 0, 0, 0, 0, 0, 1 },
|
||||
.led_sets = {
|
||||
{ /* RJ45 ports */
|
||||
LEDS_2G5 | LEDS_LINK,
|
||||
LEDS_2G5 | LEDS_1G | LEDS_100M | LEDS_10M | LEDS_LINK | LEDS_ACT,
|
||||
0,
|
||||
0
|
||||
}, { /* SFP port, single green LED */
|
||||
LEDS_10G | LEDS_2G5 | LEDS_1G | LEDS_100M | LEDS_10M | LEDS_LINK | LEDS_ACT,
|
||||
0,
|
||||
0,
|
||||
0
|
||||
}
|
||||
},
|
||||
};
|
||||
```
|
||||
|
||||
`MACHINE_COMMON_SETTING` (from `machine_defs.h`) initializes `machine_name` and
|
||||
`isRTL8373` from the header's `MACHINE_MODEL` and `MACHINE_CHIPSET`. The logical
|
||||
port range comes from `MACHINE_MIN_PORT` and `MACHINE_MAX_PORT` in `machine.h`.
|
||||
Keep it as the first initializer and list the board-specific fields after it.
|
||||
|
||||
### Fields
|
||||
|
||||
| Field | Meaning |
|
||||
|-------|---------|
|
||||
| `MACHINE_COMMON_SETTING` | expands to `machine_name` (`MACHINE_MODEL`) and `isRTL8373` (`1` for `RTL8373`, `0` for `RTL8372`); both are derived from the header's `MACHINE_CHIPSET` (see Step 1) |
|
||||
| `n_sfp` | number of SFP+ cages |
|
||||
| `n_10g` | SerDes 10G capability used by the RTL837xN init: `0` none, `1` one, `2` two 10G-capable SerDes |
|
||||
| `log_to_phys_port[9]` | logical port (0-based index) → physical front-panel port number |
|
||||
| `phys_to_log_port[9]` | the inverse, physical port 1 is index 0, unused slots are 0 |
|
||||
| `is_sfp[9]` | `0` for copper, `1` for left slot and `2` for right slot |
|
||||
| `sfp_port[2]` | per-cage wiring, `sfp_port[0]` is the leftmost cage |
|
||||
| `reset_pin` | GPIO of the reset button, `GPIO_NA` if unpopulated |
|
||||
| `high_leds` | mux/enable for the LED pins 27–29 (`LED_27`, `LED_28_SYS`, `LED_29`) |
|
||||
| `port_led_set[9]` | which LED set (0–3) each logical port uses |
|
||||
| `led_sets[4][4]` | Each port can have up to 4 GPIO output pins assigned. 4 sets of LEDs with 4 LED-conditions per set; the conditions define which GPIO pins become active for a port given a port link-state. Which LEDs are then enabled depends on the wiring of the pins to up to 3 LEDs for that port, which can make a logical combination of the 4 LED output-pins. |
|
||||
| `led_mux_custom` / `led_mux[28]` | `1` plus the 28 mux values when the stock LED muxing does not fit the board |
|
||||
| `mac_flash_offset` | flash offset of the factory MAC address, omit or `0` if the board does not store one |
|
||||
|
||||
Each `struct sfp_port` entry holds `pin_detect`, `pin_los`, `pin_tx_disable`,
|
||||
`sds` (which SerDes drives the cage) and `i2c` (built with `I2CBUS(sda, scl)`,
|
||||
or `GPIO_NA`-based values where a cage has no such pin).
|
||||
|
||||
`I2CBUS(...)` only accepts the pin pairs listed in `machine_defs.h`. A wrong
|
||||
pair is caught at compile time: SDCC reports
|
||||
`error 176: sizeof applied to an incomplete type` pointing at the offending
|
||||
`.i2c` line, because the macro deliberately turns an invalid bus number into an
|
||||
array of size 0.
|
||||
|
||||
### Reusing an existing definition
|
||||
|
||||
If the new board is electrically identical to one that already exists — same
|
||||
port mapping, same SFP wiring, same LEDs — do not copy the file. Make the new
|
||||
`.c` a symlink and keep only a distinct `.h`, so the two boards cannot drift
|
||||
apart:
|
||||
|
||||
```
|
||||
ln -s KP_9000_6XHML_X2.c machine/MY_BOARD_1_0.c
|
||||
```
|
||||
|
||||
The new board still needs `machine/MY_BOARD_1_0.h` (pick the version number on
|
||||
the PCB) with its own `MACHINE_MODEL` and `MACHINE_CHIPSET`: `MACHINE_MODEL` is
|
||||
what distinguishes the products, and `MACHINE_CHIPSET` must match the SoC of
|
||||
the board definition being reused.
|
||||
Existing examples: `machine/PCB_K0402WS_V3.c`, `machine/KP_9000_9XHML_X_V3_2.c`
|
||||
and the six `KP_9000_*` revision variants.
|
||||
|
||||
Two files that differ only in whitespace or comments are duplicates and should
|
||||
be collapsed this way.
|
||||
|
||||
### Optional custom init hook
|
||||
|
||||
If the board needs custom initialization, add
|
||||
`machine/<NAME>_init.c` defining `machine_custom_init()`. The Makefile picks the
|
||||
file up by name and compiles it.
|
||||
|
||||
Boards that need the same hook share one file: keep the code in a single
|
||||
`machine/<NAME>_init.c` whose `machine/<NAME>.c` is a real file (not a symlink),
|
||||
and make the other boards' `_init.c` symlinks to it, for example:
|
||||
|
||||
```
|
||||
ln -s KP_9000_6XH_X2_init.c machine/MY_BOARD_1_0_init.c
|
||||
```
|
||||
|
||||
## Step 3: build and verify
|
||||
|
||||
```
|
||||
make MACHINE=MY_BOARD_1_0
|
||||
```
|
||||
|
||||
The generated flash image appears as `output/rtlplayground-<version>-MY_BOARD_1_0.bin`. An
|
||||
unknown or empty `MACHINE` fails immediately with a message naming the expected
|
||||
file, before anything is compiled.
|
||||
|
||||
Then run the checks CI runs:
|
||||
|
||||
```
|
||||
make machine_check # compiles machine/<NAME>.c and its optional _init.c for every machine
|
||||
make -C test # host-side unit tests
|
||||
```
|
||||
|
||||
`make machine_check` discovers the definitions with
|
||||
`$(wildcard machine/*.c)`, so a new file is covered without registering it
|
||||
anywhere. CI (`.github/workflows/build.yml`) runs it on every push and pull
|
||||
request, alongside full builds for a 4+2 and an 8+1 board.
|
||||
|
||||
A successful compile only proves the definition is syntactically valid. The
|
||||
actual behaviour — port numbering, SFP detection, LEDs — has to be confirmed on
|
||||
hardware, and the console commands (`port`, `stat`, `sfp`, `leds`) are the
|
||||
tools for that.
|
||||
|
||||
## Step 4: documentation
|
||||
|
||||
Once the board has been tested on hardware:
|
||||
|
||||
1. Add a row to the table in [Supported devices](supported_devices.md).
|
||||
2. Add `doc/devices/<name>.md` describing the board, the build target
|
||||
(`make MACHINE=MY_BOARD_1_0`), what works and what is unverified, plus
|
||||
photos and the PCB silkscreen marking.
|
||||
3. If the board shares a PCB with other revisions, follow
|
||||
`doc/devices/KP-9000-6XH-X2.md` and document which `MACHINE` to pick for
|
||||
which silkscreen.
|
||||
|
||||
### Identifying the CPU variant
|
||||
|
||||
For RTL8373-based devices, use the read-only console command below to distinguish
|
||||
the N and non-N CPU variants:
|
||||
|
||||
```text
|
||||
regget 4
|
||||
```
|
||||
|
||||
| Register 0x0004 value | CPU |
|
||||
|---|---|
|
||||
| `0x83730000` | RTL8373 (non-N) |
|
||||
| `0x83737000` | RTL8373N |
|
||||
|
||||
Record the identified CPU in the device documentation. These values identify
|
||||
only the CPU variant; document PHY and flash-chip models separately when verified.
|
||||
|
||||
@@ -0,0 +1,178 @@
|
||||
hongyavision HYWS-SGT0204S
|
||||
|
||||
RTL8372-based 4×2.5G + 2×SFP+ unmanaged switch.
|
||||
|
||||
Also marked on the PCB as `2G5F4_10G2_V1.01`.
|
||||
|
||||
Stock firmware runs a factory test console on UART at 9600 baud 8N1 (banner
|
||||
`RTL8371B start`, prompt `RTL8371B:`). There is no management CLI and no Web
|
||||
interface.
|
||||
|
||||
Using SPI clamp in-board is the only method for initial installation and update.
|
||||
Erase the whole chip and verify the write (`flashrom -v`) before powering the
|
||||
board, so an incomplete write is caught before the board is powered up.
|
||||
|
||||
Stock flash chip is 512 KB (Bright Moon `T25S40`, 4 Mbit, SOP-8), which the
|
||||
RTLPlayground image fills exactly.
|
||||
|
||||
### Label specifications
|
||||
|
||||
Case, top / front / nameplate:
|
||||
|
||||
<img src="photos/2G5F4_10G2_V1.01/assembled-top.jpg" width="300" />
|
||||
<img src="photos/2G5F4_10G2_V1.01/assembled-front.jpg" width="300" />
|
||||
<img src="photos/2G5F4_10G2_V1.01/assembled-bottom.jpg" width="300" />
|
||||
|
||||
- **Manufacturer**: hongyavision (Shenzhen Hongyavision Electronic Technology
|
||||
Co., Ltd.)
|
||||
- **Model**: HYWS-SGT0204S — the nameplate is printed in Chinese and reads
|
||||
"10/100/1000/2500M fibre switch" and "4x 2.5G copper port + 2x 10G SFP+
|
||||
optical port"
|
||||
- **Ports**:
|
||||
- 4 × RJ45: 10/100/1000/2500 Mbps
|
||||
- 2 × SFP+: 1G / 2.5G / 10G
|
||||
- **Power**: 12 V DC, 2 A
|
||||
- **Serial number**: `S/N-YYYYMMDDNNNN`, e.g. `S/N-202312010028`
|
||||
|
||||
The top of the case only carries generic printing ("2.5G+10G High Speed Fiber
|
||||
Transceiver / 4-Port 2.5G Ethernet Switch+10G Fiber"), so the nameplate on the
|
||||
bottom is what identifies the device.
|
||||
|
||||
### 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
|
||||
- Copper LEDs: white (2.5G) and blue (link/activity)
|
||||
- SFP LEDs: one single LED per port (link/activity)
|
||||
- SFP EEPROM and DDM readable on both cages, verified with a 10G module
|
||||
(`Rate: 0x67`, module identity and DDM values via the `sfp` console command)
|
||||
- Serial console at 115200 8N1 (RTLPlayground) / 9600 8N1 (stock)
|
||||
|
||||
### Not verified
|
||||
|
||||
- The reset button footprint is unpopulated and the case has no opening for it,
|
||||
so it could not be tested.
|
||||
|
||||
### PCB overview
|
||||
|
||||
**Board markings**
|
||||
- Top silkscreen: `2G5F4_10G2_V1.01`
|
||||
|
||||
**Key components**
|
||||
- Main switch: RTL8372 (RTL8371B family), reported as `RTL8372` (non-N) by
|
||||
RTLPlayground. The chip sits under a heatsink and its package marking is not
|
||||
readable.
|
||||
- U1: SPI NOR flash, Bright Moon `T25S40`, 4 Mbit / 512 KB, SOP-8
|
||||
- A 3-hole reset button footprint is present but unpopulated, and the case has
|
||||
no opening for it, so there is no usable reset button
|
||||
|
||||
Top side:
|
||||
|
||||
<img src="photos/2G5F4_10G2_V1.01/pcb_top.jpg" width="600" />
|
||||
|
||||
Bottom:
|
||||
|
||||
<img src="photos/2G5F4_10G2_V1.01/pcb_bottom.jpg" width="600" />
|
||||
|
||||
The stock flash chip, Bright Moon `T25S40` (SOP-8, 4 Mbit / 512 KB):
|
||||
|
||||
<img src="photos/2G5F4_10G2_V1.01/flash_t25s40.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+ |
|
||||
|
||||
Copper ports 1–4 match the KP-9000-6XH-X2 (2M-PCB43-V1.1) layout, which this
|
||||
board is a sibling of. The two SFP+ cages are wired the other way round
|
||||
compared to that board: physical port 5 is on SDS1 and physical port 6 is on
|
||||
SDS0.
|
||||
|
||||
### Serial console
|
||||
|
||||
The board has three through-holes used as the serial console, next to the
|
||||
heatsink in the middle of the board and visible in the top side photo above (the
|
||||
silkscreen labels are printed rotated by 90°). **The silkscreen labels are
|
||||
wrong**: they are shifted relative to the actual signals.
|
||||
|
||||
| Silkscreen label | Actual signal |
|
||||
|------------------|---------------|
|
||||
| TX | GND |
|
||||
| RX | TX |
|
||||
| GND | RX |
|
||||
|
||||
Wiring (following the measured signals, not the labels):
|
||||
|
||||
| USB-TTL adapter | Board pad |
|
||||
|-----------------|----------------------|
|
||||
| GND | pad labelled `TX` |
|
||||
| RX | pad labelled `RX` |
|
||||
| TX | pad labelled `GND` |
|
||||
|
||||
- **Settings**: 115200 baud / 8N1 / 3.3V TTL (RTLPlayground), 9600 baud for the
|
||||
stock firmware
|
||||
- The stock firmware only prints a few bytes and a factory test menu
|
||||
(`?`, `rst`, `f`, `r`, `10`, `100`, `g1`, `sd`, `eye`, …); it is not an
|
||||
interactive management console
|
||||
|
||||
### LED configuration
|
||||
|
||||
Case silkscreen, below the RJ45 ports:
|
||||
|
||||
```
|
||||
White : 2.5G Blue : 1000M/2500M L/A
|
||||
```
|
||||
|
||||
The front panel also carries a `PWR` LED and one LED per SFP cage (`SFP1`,
|
||||
`SFP2`) on the right hand side.
|
||||
|
||||
| Port | LED | Linked, idle | With traffic |
|
||||
|------------|--------------|--------------|--------------|
|
||||
| Copper | white | 2.5G only | same |
|
||||
| Copper | blue | solid | blinks (L/A) |
|
||||
| SFP | single LED | solid | blinks (L/A) |
|
||||
|
||||
Measured LED-ID mapping (from the RTL837x LED set registers):
|
||||
|
||||
| LED-ID | Function |
|
||||
|--------|---------------------------------|
|
||||
| LED-ID0| copper blue LED |
|
||||
| LED-ID1| copper white LED |
|
||||
| LED-ID2| SFP single LED |
|
||||
|
||||
**Important**: `RTL837X_REG_LED_GLB_IO_EN` bit 6 must be set in
|
||||
`machine_custom_init()`. Without it the RTL837x "solid" conditions (LINK/speed)
|
||||
do not drive the LEDs at all and only `ACT` does, which makes the white LED
|
||||
glow faintly at non-2.5G speeds.
|
||||
|
||||
### SFP GPIO assignments
|
||||
|
||||
| SFP | pin\_detect (ModAbs) | pin\_los | SerDes | I2C SDA | I2C SCL |
|
||||
|-------------------|-------------------------------|------------------|--------|-------------------------|--------------------------|
|
||||
| Left (logical 8, physical 5) | GPIO30\_ACL\_BIT3\_EN | GPIO37 | SDS1 | GPIO39\_I2C\_SDA4 | GPIO40\_I2C\_SCL3\_MDC1 |
|
||||
| Right (logical 3, physical 6)| GPIO50\_I2C\_SCL2\_UART1\_TX | GPIO10\_LED10 | SDS0 | GPIO41\_I2C\_SDA3\_MDIO1 | GPIO40\_I2C\_SCL3\_MDC1 |
|
||||
|
||||
`pin_tx_disable` is GPIO\_NA on both ports. Both I2C buses are confirmed
|
||||
working: with the same 10G module inserted, the `sfp` console command reads its
|
||||
vendor and model from the left cage (`Slot 1`) and from the right cage
|
||||
(`Slot 2`), DDM values included. The insertion also shows which SerDes drives
|
||||
each cage - the left cage reports `sds_config sds: 01` and the right cage
|
||||
`sds: 00`, which matches the table above.
|
||||
|
||||
### Machine target
|
||||
|
||||
Build for this device with `make MACHINE=2G5F4_10G2_V1_01`, which selects the
|
||||
`machine/2G5F4_10G2_V1_01.c` board definition. It is derived from
|
||||
`machine/KP_9000_6XHML_X2.c` with the SFP cage order and the reset pin changed.
|
||||
The fitted T25S40 cannot be read in DIO mode on this board: an image that leaves
|
||||
DIO enabled does not come up at all, both at the default read clock divider of 4
|
||||
and at 8 (two boards tested), while the same image with single-IO boots and runs
|
||||
normally. The board definition therefore sets `flash_sio`, which makes
|
||||
`flash_init()` use `CMD_FREAD` at divider 8. Only the management plane is
|
||||
slower, the ASIC keeps forwarding in hardware.
|
||||
@@ -0,0 +1,40 @@
|
||||
### 2M-PCB23-V3.2
|
||||
|
||||
## Brands
|
||||
|Brand|Type|Managed|PCB|Flash|Chip RTL|
|
||||
|---|---|---|---|---|---|
|
||||
| keepLINK | KP-9000-9XHML-X | Yes| 2M-PCB23-V3.2 | 2M (Winbond 25Q) | |
|
||||
|
||||
The original web interface reports this board as hardware version V3.2
|
||||
(original firmware V100.9.25, January 2026).
|
||||
|
||||
## PCB
|
||||
|
||||
<img src="photos/2M-PCB23-V3.2-managed/2M-PCB23-V3.2-top.jpeg" width="300" />
|
||||
|
||||
## Compared to V3.1
|
||||
|
||||
The component placement matches [2M-PCB23-V3.1](2M-PCB23-V3_1.md): same
|
||||
positions for the flash (U8), the T7/T8/T9/T10 headers, the SFP cage and its
|
||||
LED, and the unpopulated U7/U9 footprints. Build with
|
||||
`make MACHINE=KP_9000_9XHML_X_V3_2`, whose board file is a symlink to the V3.1
|
||||
definition and only differs in the reported name.
|
||||
|
||||
## Tested
|
||||
|
||||
Installed from the original firmware through its web interface with
|
||||
`installer/output/rtlplayground_oem_upgrade.bin`, then:
|
||||
|
||||
- RJ45 ports link at 2.5G; LEDs: orange at 2.5G, green below
|
||||
- SFP port: module detected with vendor/model/serial and DOM readings
|
||||
(temperature, Vcc, TX bias, TX/RX power), link up at 10G
|
||||
- factory MAC read from flash (0x1FC000)
|
||||
- reset button (GPIO48): held for more than 10 s it restores the default config
|
||||
|
||||
## Flash access
|
||||
|
||||
Reading the flash in-circuit with a SOIC-8 clip did not work: the chip ID
|
||||
comes back as `EF FF FF` (only the manufacturer byte is right) and reads
|
||||
return 0xff, the same as on a V3.1 board. The switch chip is powered through
|
||||
the clip and keeps driving the SPI lines. Make sure a way back is acceptable
|
||||
before installing, or hold the switch chip in reset while reading.
|
||||
@@ -15,7 +15,7 @@ and sold by Mokerlink.
|
||||
The device is fully supported:
|
||||
- All 4 2.5GBASE-T RJ45 ports work at 10/100/1000/2500 Mbps
|
||||
- The SFP+ port supports 1G, 2.5G and 10G modules
|
||||
- LEDs work with the same indiciations as the OEM firmware (use KP_9000_6XHML_X2_V1_1 in machine.h if building yourself or the corresponding pre-compiled binary)
|
||||
- LEDs work with the same indiciations as the OEM firmware (build with `make MACHINE=KP_9000_6XHML_X2_V1_1` if building yourself, or use the corresponding pre-compiled binary)
|
||||
- untested due to missing Hardware: SFP+ ports equipped with 1G or 2.5G SFPs.
|
||||
|
||||
### Hardware overview
|
||||
|
||||
@@ -1,11 +1,13 @@
|
||||
# MokerLink POE-2G080110GS
|
||||
|
||||
The `POE-2G080110GS` is an unmanaged 8x2.5G RJ45 and 1xSFP+ PoE switch sold by Mokerlink. There is also a `POE-2G080110GSM` managed version of the switch which may be similar but this has not been verified on actual hardware.
|
||||
The`KP-9000-9XHP-X` is the KeepLink rebrand for same product.
|
||||
|
||||
## Brands
|
||||
| Brand | Type |Managed| PCB | Flash | Chip RTL |
|
||||
|--------|------------------|-------|------------------|-----------------|---------------|
|
||||
| Mokerlink | POE-2G080110GS | No | 2M-PCB51-ML-V1.1 | 4MB (W25Q32JV) | 8373N + 8224N |
|
||||
| KeepLink | KP-9000-9XHP-X | No | 2M-PCB51-ML-V1.1 | 4MB (W25Q32JV) | 8373N + 8224N |
|
||||
|
||||
## Hardware overview
|
||||
Front
|
||||
|
||||
@@ -0,0 +1,75 @@
|
||||
# Hasivo S1100WP-8GT-1SX-SE
|
||||
|
||||
RTL8373-based 8×2.5G PoE + 1×SFP switch.
|
||||
|
||||
### Label specifications
|
||||
|
||||
- **Manufacturer**: Hasivo
|
||||
- **Model**: S1100WP-8GT-1SX-SE
|
||||
- **Ports**:
|
||||
- 8 × RJ45: 10/100/1000/2500 Mbps with PoE
|
||||
- 1 × SFP: 1G / 2.5G / 10G
|
||||
- **Power input**: DC 52V 2.5A
|
||||
- **Stock firmware defaults**: IP 192.168.0.1, user/password `admin`/`admin`
|
||||
|
||||
### What works
|
||||
|
||||
- All eight 2.5GBASE-T RJ45 ports at 10/100/1000/2500 Mbps (PoE is not configurable via RTLPlayground)
|
||||
- SFP port supporting 1G, 2.5G and 10G modules
|
||||
- LEDs: green (2.5G) and amber (1G/100M/10M) per copper port; combined link/act on the SFP port
|
||||
|
||||
### Photos
|
||||
|
||||
Front panel:
|
||||
|
||||
<img src="photos/HASIVO_S1100WP-8GT-1SX-SE/front.jpg" width="600" />
|
||||
|
||||
Label:
|
||||
|
||||
<img src="photos/HASIVO_S1100WP-8GT-1SX-SE/label.jpg" width="600" />
|
||||
|
||||
### PCB overview
|
||||
|
||||
Top side:
|
||||
|
||||
<img src="photos/HASIVO_S1100WP-8GT-1SX-SE/pcb_top.jpg" width="600" />
|
||||
|
||||
Bottom side:
|
||||
|
||||
<img src="photos/HASIVO_S1100WP-8GT-1SX-SE/pcb_bottom.jpg" width="600" />
|
||||
|
||||
### Port layout
|
||||
|
||||
| Logical port | Physical port | Type |
|
||||
|--------------|---------------|--------|
|
||||
| 1-8 | 1-8 | Copper |
|
||||
| 9 | 9 | SFP |
|
||||
|
||||
### LED configuration
|
||||
|
||||
Copper ports use LED SET0, the SFP port uses LED SET1.
|
||||
|
||||
| SET | LED0 | LED2 |
|
||||
|------|-----------------------------------------------|------------------------------------------------------|
|
||||
| SET0 | Green — lights on 2.5G link with activity | Amber — lights on 1G / 100M / 10M link with activity |
|
||||
| SET1 | All speeds — lights on any link with activity | — |
|
||||
|
||||
### SFP GPIO assignments
|
||||
|
||||
| SFP (logical 9) | pin_detect (ModAbs) | pin_los | pin_tx_disable | SerDes | I2C SDA | I2C SCL |
|
||||
|-----------------|---------------------|---------|----------------|--------|-----------------|---------------------|
|
||||
| SFP | GPIO30_ACL_BIT3_EN | GPIO37 | GPIO_NA | SDS1 | GPIO39_I2C_SDA4 | GPIO40_I2C_SCL3_MDC1 |
|
||||
|
||||
### Machine configuration
|
||||
|
||||
- `reset_pin`: GPIO54_ACL_BIT2_EN
|
||||
- High LEDs (pads 27-29): mux = `LED_27 | LED_28_SYS | LED_29`, enable = `LED_28_SYS | LED_29`
|
||||
|
||||
### PoE
|
||||
|
||||
The 8 copper ports support PoE powered by a DC 52V 2.5A input. Power management is handled by two HS104PTI chipsets over I2C (GPIO47_I2C_SDA0, GPIO46_I2C_SCL0):
|
||||
|
||||
* Chip 1 (I2C address 0x1A): 2.5G Ports 1–4
|
||||
* Chip 2 (I2C address 0x2A): 2.5G Ports 5–8
|
||||
|
||||
Note: The current implementation does not yet support PoE management. Consequently, PoE currently operates in standalone mode without software control.
|
||||
@@ -0,0 +1,41 @@
|
||||
# Hasivo S1100P-8GT-1SX-S
|
||||
|
||||
RTL8373N switch with 8 × 2.5G RJ45 ports and 1 × SFP port. GD25Q16 2 MiB flash.
|
||||
|
||||
## Port mapping
|
||||
|
||||
| Front port | ASIC MAC | Interface |
|
||||
| --- | --- | --- |
|
||||
| 1–8 | 0–7 | RJ45 |
|
||||
| 9 | 8 | SFP (SDS1) |
|
||||
|
||||
## Board signals
|
||||
|
||||
| Function | GPIO |
|
||||
| --- | --- |
|
||||
| SFP detect | 38 |
|
||||
| SFP LOS | 37 |
|
||||
| SFP TX disable | Not connected (`GPIO_NA`) |
|
||||
| SFP I2C SDA / SCL | 39 / 40 |
|
||||
| Reset | 54 |
|
||||
|
||||
PoE is managed by an RTL8238B and a separate STC8H8K64U controller, not directly by the RTL8373N. RTLPlayground does not control PoE on this board.
|
||||
|
||||
## Build
|
||||
|
||||
`make MACHINE=HASIVO_S1100P_8GT_1SX_S CI=1`
|
||||
|
||||
## Photos
|
||||
|
||||
Front panel:
|
||||
|
||||

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

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

|
||||
|
||||
@@ -0,0 +1,124 @@
|
||||
# ONT-S207CW-62TS-SE / Binardat 2G06-04210GSM / Binardat 2G06-04210GS
|
||||
|
||||
## Overview
|
||||
|
||||
ONT-S207CW-62TS-SE, Binardat 2G06-04210GSM (managed), and Binardat 2G06-04210GS (unmanaged) are **switches with identical hardware**:
|
||||
- **CPU:** RTL8372N (confirmed via serial: "Detecting CPU: RTL8372N")
|
||||
- **Flash:** GD25Q128E (16MB)
|
||||
- **Ports:** 4x 2.5G RJ45 + 2x 10G SFP+
|
||||
- **Console:** 115200 baud (RTLPlayground firmware) / 9600 baud (stock firmware)
|
||||
|
||||
**Note:** These devices are **RTL8372N-based** with **LAN ports mounted upside-down on the PCB**, requiring a custom configuration. The `MACHINE_ONT_S207CW_62TS_SE` definition uses a **custom configuration** with:
|
||||
- Corrected port mapping (physical ports 1-4 = RJ45, 5-6 = SFP)
|
||||
- Optimized LED sets for different port wiring (RJ45 Ports 1-2 have reversed LED wiring)
|
||||
- Custom LED mux configuration
|
||||
|
||||
## Device Photos
|
||||
|
||||
### Binardat 2G06-04210GSM
|
||||
|
||||
**Front View:**
|
||||
<img src="photos/ONT-S207CW-Binardat-2G06-04210GSM/binardat-2g06-04210gsm-front.jpg" width="400" />
|
||||
|
||||
**Back View:**
|
||||
<img src="photos/ONT-S207CW-Binardat-2G06-04210GSM/binardat-2g06-04210gsm-back.jpg" width="400" />
|
||||
|
||||
**PCB View:**
|
||||
<img src="photos/ONT-S207CW-Binardat-2G06-04210GSM/binardat-2g06-04210gsm-pcb.jpg" width="400" />
|
||||
|
||||
*Photos courtesy of [andreas5232](https://github.com/andreas5232) from [emesix/ONT-S207CW-62TS-SE#1](https://github.com/emesix/ONT-S207CW-62TS-SE/issues/1)*
|
||||
|
||||
## Hardware Specification
|
||||
|
||||
| Feature | Value |
|
||||
|---|---|
|
||||
| **CPU** | RTL8372N |
|
||||
| **Flash** | GD25Q128E (16MB) |
|
||||
| **RAM** | Integrated in RTL8372N |
|
||||
| **RJ45 Ports** | 4x 2.5GBase-T (Ports 1-4) |
|
||||
| **SFP+ Ports** | 2x 10G (Ports 5-6) |
|
||||
| **Console** | UART (115200 baud for RTLPlayground, 9600 baud for stock) |
|
||||
| **Stock Web UI IP** | 192.168.2.1 |
|
||||
| **Loader Mode IP** | 192.168.10.247 |
|
||||
|
||||
## Port Layout
|
||||
|
||||
```
|
||||
┌─────────────────────────────────────────────┐
|
||||
│ ┌─────┐ ┌─────┐ ┌─────┐ ┌─────┐ ┌─────┐ ┌─────┐ │
|
||||
│ │RJ45 │ │RJ45 │ │RJ45 │ │RJ45 │ │SFP+ │ │SFP+ │ │
|
||||
│ │ 1 │ │ 2 │ │ 3 │ │ 4 │ │ 5 │ │ 6 │ │
|
||||
│ └─────┘ └─────┘ └─────┘ └─────┘ └─────┘ └─────┘ │
|
||||
│ │
|
||||
│ [System LEDs] │
|
||||
└─────────────────────────────────────────────┘
|
||||
```
|
||||
|
||||
## LED Behavior
|
||||
|
||||
### RJ45 Ports (Physical 1-4)
|
||||
|
||||
| LED Color | Speed |
|
||||
|---|---|
|
||||
| **Green** | 2.5G |
|
||||
| **Orange** | 1G / 100M / 10M |
|
||||
| **Off** | No link |
|
||||
|
||||
### SFP+ Ports (Physical 5-6)
|
||||
|
||||
| LED Color | Speed |
|
||||
|---|---|
|
||||
| **Green** | 10G |
|
||||
| **Orange** | 2.5G / 1G |
|
||||
| **Off** | No link / No SFP module |
|
||||
|
||||
## Port to Logical Mapping
|
||||
|
||||
Custom mapping for ONT-S207CW-62TS-SE:
|
||||
|
||||
| Physical Port | Logical Port | Type | LED Set |
|
||||
|---|---|---|---|
|
||||
| 1 | 7 | RJ45 | 2 |
|
||||
| 2 | 6 | RJ45 | 2 |
|
||||
| 3 | 5 | RJ45 | 0 |
|
||||
| 4 | 4 | RJ45 | 0 |
|
||||
| 5 | 3 | SFP+ (SDS0) | 3 |
|
||||
| 6 | 8 | SFP+ (SDS1) | 3 |
|
||||
|
||||
**Note:** Ports 1-2 (RJ45) have reversed LED wiring and use LED-Set 2. SFP Ports 5-6 use LED-Set 3 to account for wiring differences.
|
||||
|
||||
## Flashing Instructions
|
||||
|
||||
See the [README.md](../../README.md) for general flashing instructions.
|
||||
For compilation, use command `make MACHINE=ONT_S207CW_62TS_SE` to specify the machine.
|
||||
If you are using managed version of the switch, you can then also compile the installer and flash using GUI - run command `cd installer` and finally `make`.
|
||||
|
||||
## Stock Firmware Information
|
||||
|
||||
| Property | Value |
|
||||
|---|---|
|
||||
| **Default IP** | 192.168.2.1 (ONT) / 192.168.10.247 (Loader Mode) |
|
||||
| **Default Login** | admin/admin (may vary by OEM) |
|
||||
| **Console Baud** | 9600 (stock) / 115200 (RTLPlayground) |
|
||||
| **CPU String** | "RTL8372N" |
|
||||
| **Loader Mode IP** | 192.168.10.247 |
|
||||
|
||||
## Configuration Details
|
||||
|
||||
- **CPU:** RTL8372N
|
||||
- **SFP:** SDS0 on logical port 3 (GPIO37), SDS1 on logical port 8 (GPIO38)
|
||||
- **Port Mapping:** Physical 1-4 → Logical 7-4, Physical 5-6 → Logical 3,8
|
||||
- **LED Wiring:** RJ45 Ports 1-2 and SFP Port 6 have reversed LED pins (orange/green swapped)
|
||||
- **LED Sets:** Set 0 for RJ45 Ports 3-4, Set 1 unused, Set 2 for RJ45 Ports 1-2, Set 3 for SFP Ports 5-6
|
||||
|
||||
## Notes
|
||||
|
||||
- ONT-S207CW-62TS-SE, Binardat 2G06-04210GSM (managed), and Binardat 2G06-04210GS (unmanaged) are **RTL8372N-based** with **LAN ports mounted upside-down on the PCB**
|
||||
- Web UI shows correct physical port order (1-2-3-4 for RJ45, 5-6 for SFP)
|
||||
- Loader Mode always remains accessible at `192.168.10.247` for recovery
|
||||
- Serial console: 9600 baud (stock) → 115200 baud (RTLPlayground)
|
||||
|
||||
## References
|
||||
|
||||
- [GitHub Issue: emesix/ONT-S207CW-62TS-SE#1](https://github.com/emesix/ONT-S207CW-62TS-SE/issues/1) - Original photos and discussion
|
||||
- [PR: logicog/RTLPlayground#465](https://github.com/logicog/RTLPlayground/pull/465) - Feedback and improvements
|
||||
@@ -0,0 +1,93 @@
|
||||
# PCB-AFGW SWTG218ANS V2.0.2
|
||||
|
||||
RTL8373-based 8×2.5G PoE + 1×10G SFP+ web-managed switch, sold as the
|
||||
**RealHD PSW8-25MG**.
|
||||
|
||||
### Label specifications
|
||||
|
||||
- **Manufacturer**: RealHD
|
||||
- **Model**: PSW8-25MG
|
||||
- **PCB marking**: `PCB-AFGW SWTG218ANS_V2.0.2` (silkscreened as `PCB-AFGW SWTG218ANS_V2.0.2_23308`)
|
||||
- **Ports**:
|
||||
- 8 × RJ45: 10/100/1000/2500 Mbps with PoE
|
||||
- 1 × SFP+: 1G / 2.5G / 10G (front panel marking `9(10G)`)
|
||||
- **Power input**: internal AC PSU, `100-240V~ 50/60Hz 2A MAX`, DC output `53.5V 2.8A 150W`
|
||||
- **Front panel LEDs**: `PWR`, `SYS`, per RJ45 port `Link/Act` and `PoE`, per SFP port 2.5G (green) and 10G (blue)
|
||||
- **Stock firmware defaults**: web-managed PoE switch
|
||||
|
||||
### What works
|
||||
|
||||
- All eight 2.5GBASE-T RJ45 ports at 10/100/1000/2500 Mbps (PoE is not configurable via RTLPlayground)
|
||||
- SFP+ port supporting 1G, 2.5G and 10G modules
|
||||
- LEDs: green (2.5G) and amber (1G/100M/10M) per RJ45 port with activity, green (1G/2.5G) and blue (10G) on the SFP port, plus the `SYS` LED
|
||||
|
||||
### Photos
|
||||
|
||||
Front panel:
|
||||
|
||||
<img src="photos/PCB-AFGW_SWTG218ANS_V2.0.2/device_front.jpg" width="800" />
|
||||
|
||||
Board mounted in the case, with the internal PSU above it:
|
||||
|
||||
<img src="photos/PCB-AFGW_SWTG218ANS_V2.0.2/dev_top_w_pcb.jpg" width="800" />
|
||||
|
||||
### PCB overview
|
||||
|
||||
Top side:
|
||||
|
||||
<img src="photos/PCB-AFGW_SWTG218ANS_V2.0.2/pcb_front.jpg" width="800" />
|
||||
|
||||
Bottom side:
|
||||
|
||||
<img src="photos/PCB-AFGW_SWTG218ANS_V2.0.2/pcb_back.jpg" width="800" />
|
||||
|
||||
A 4-pad UART header with `RXD` / `TXD` silkscreened sits next to the right heatsink.
|
||||
|
||||
### Port layout
|
||||
|
||||
| Logical port | Physical port | Type |
|
||||
|--------------|---------------|--------|
|
||||
| 1-8 | 1-8 | RJ45 |
|
||||
| 9 | 9 | SFP+ |
|
||||
|
||||
### LED configuration
|
||||
|
||||
RJ45 ports use LED SET0, the SFP+ port uses LED SET1. The board needs the custom
|
||||
LED mux (`led_mux_custom = 1`), and `RTL837X_REG_LED_GLB_IO_EN` is written to
|
||||
`0x7f24977f` in `machine_custom_init()` so the LED pins are enabled as wired here.
|
||||
|
||||
| SET | LED pin 0 | LED pin 1 |
|
||||
|------|-----------------------------------------------|-------------------------------------------------------|
|
||||
| SET0 | Green — lights on 2.5G link with activity | Amber — lights on 1G / 100M / 10M link with activity |
|
||||
| SET1 | All speeds — lights on any link with activity | 10G — lights on 10G link with activity |
|
||||
|
||||
On the front panel this gives one bi-color `Link/Act` LED per RJ45 port (green at
|
||||
2.5G, amber at 10/100/1000M, blinking with activity) and one `PoE` LED per RJ45
|
||||
port, which is driven by the PoE hardware and not by the switch. The SFP+ port has
|
||||
a green 2.5G LED and a blue 10G LED.
|
||||
|
||||
### SFP GPIO assignments
|
||||
|
||||
| SFP (logical 9) | pin_detect (ModAbs) | pin_los | pin_tx_disable | SerDes | I2C SDA | I2C SCL |
|
||||
|-----------------|---------------------|---------|----------------|--------|------------------|----------------------|
|
||||
| SFP | GPIO38 | GPIO_NA | GPIO_NA | SDS1 | GPIO39_I2C_SDA4 | GPIO40_I2C_SCL3_MDC1 |
|
||||
|
||||
### Machine configuration
|
||||
|
||||
```
|
||||
make MACHINE=REALHD_PSW8_25MG
|
||||
```
|
||||
|
||||
### Programming
|
||||
|
||||
The serial console runs at 8N1 on 115200 baud with the RTLPlayground firmware
|
||||
(see the UART header note under *PCB overview*). The stock firmware baud rate 57600. The flash chip was 2MB.
|
||||
|
||||
### PoE
|
||||
|
||||
The 8 RJ45 ports support PoE, powered by the internal 150W AC PSU (DC output
|
||||
53.5V 2.8A) mounted above the main board.
|
||||
|
||||
Note: RTLPlayground does not provide PoE management for this board. The PoE
|
||||
hardware runs with its own configuration, the same as on the other PoE boards in
|
||||
this project.
|
||||
@@ -0,0 +1,72 @@
|
||||
# PCB-K0402WS-V1.0
|
||||
|
||||
This is the first revision of the PCB-K0402WS series of boards; it's very similar to 2.0 in terms of layout/wiring.
|
||||
|
||||
The board does have footprints for a VLAN modeswitch at the front and a second flash chip on the bottom of the PCB, but these have been seen to ship unpopulated.
|
||||
|
||||
### Brands
|
||||
|
||||
* YuanLey YS25-0402 <sup><sub>Note: there are [multiple revisions](PCB-K0402WS-V3.0.md) of the YS25-0402. This first revision lacks the "Vlan" switch cutout on the front of the enclosure.</sub></sup>
|
||||
|
||||
### Programming
|
||||
|
||||
Use a SOIC-8 clamp to the flash chip (U1 on silkscreen) at the top of the board, leaving it otherwise unpowered.
|
||||
|
||||
The board has been seen to ship with a single [BoyaMicro BY25Q16BSSIG](https://www.lcsc.com/datasheet/C382744.pdf) 16Mbit (2MiB) chip, though exact BOM may vary.
|
||||
|
||||
<a href="photos/PCB-K0402WS-V1.0/flash.jpg"><img src="photos/PCB-K0402WS-V1.0/flash.jpg" width="300" /></a>
|
||||
|
||||
### 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 (Power LED is always on)
|
||||
|
||||
### PCB overview
|
||||
|
||||
#### Board markings
|
||||
- Top silkscreen: PCB-K0402W-V1.0 / DIP-K0402WS-V1.0
|
||||
|
||||
#### Key components
|
||||
- U1: SPI NOR flash, 2 MiB
|
||||
- S1: unpopulated slide switch footprint (three through-holes used as serial console)
|
||||
|
||||
Front panel
|
||||
|
||||
<a href="photos/PCB-K0402WS-V1.0/ports.jpg"><img src="photos/PCB-K0402WS-V1.0/ports.jpg" width="300" /></a>
|
||||
|
||||
PCB top
|
||||
|
||||
<a href="photos/PCB-K0402WS-V1.0/pcb-top.jpg"><img src="photos/PCB-K0402WS-V1.0/pcb-top.jpg" width="300" /></a>
|
||||
|
||||
PCB bottom
|
||||
|
||||
<a href="photos/PCB-K0402WS-V1.0/pcb-bottom.jpg"><img src="photos/PCB-K0402WS-V1.0/pcb-bottom.jpg" width="300" /></a>
|
||||
|
||||
Bottom and rear
|
||||
|
||||
<a href="photos/PCB-K0402WS-V1.0/bottom.jpg"><img src="photos/PCB-K0402WS-V1.0/bottom.jpg" width="300" /></a>
|
||||
|
||||
### Power supply
|
||||
|
||||
Input power is delivered via a standard DC barrel jack (5.5x2.1mm) with an included 12V 1A adapter.
|
||||
|
||||
There is an optional grounding screw next to the DC input.
|
||||
|
||||
### Serial console
|
||||
|
||||
The PCB has a slide switch footprint (unpopulated) at the front right that is connected to the UART. It is labeled as "S1" on the silkscreen.
|
||||
|
||||
Facing the top front (port) side of the board, the pinout from left to right is:
|
||||
|
||||
| Position | Signal | GPIO |
|
||||
|----------|--------|-------------------|
|
||||
| 1 | RX | GPIO32\_UART0\_RX |
|
||||
| 2 | GND | N/A |
|
||||
| 3 | TX | GPIO31\_UART0\_TX |
|
||||
|
||||
Standard 0.1" / 2.54mm pin headers will not fit, so wires or clips may be used instead.
|
||||
|
||||
The image below shows some oversized clips clumsily attached.
|
||||
|
||||
<a href="photos/PCB-K0402WS-V1.0/uart.jpg"><img src="photos/PCB-K0402WS-V1.0/uart.jpg" width="300" /></a>
|
||||
@@ -19,9 +19,14 @@ 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" />
|
||||
|
||||
* Siscolink SL-G0402F
|
||||
* SICSOLINK SL-G0402F
|
||||
|
||||
<img src="photos/PCB-K0402WS-V3.0/SICSOLINK SL-G0402F.jpg" width="300"/>
|
||||
|
||||
* YuanLey YS25-0402 <sup><sub>Note: there are [multiple revisions](PCB-K0402WS-V1.0.md) of the YS25-0402. This is the newer revision that has a "Vlan" switch on the front.</sub></sup>
|
||||
|
||||
<img src="photos/PCB-K0402WS-V3.0/YuanLey_YS25-0402.jpg" width="300"/>
|
||||
|
||||
<img src="photos/PCB-K0402WS-V3.0/Siscolink SL-G0402F.jpg" width="300"/>
|
||||
### Programming
|
||||
|
||||
Using SPI clamp in-board is the only method for initial installation.
|
||||
@@ -43,7 +48,7 @@ For the initial flash (at least with flashrom), the bin file produced by the bui
|
||||
### PCB overview
|
||||
|
||||
**Board markings**
|
||||
- Top silkscreen: PCB-KO4022W-V3.0 / DIP-KO4022WS-V3.0
|
||||
- Top silkscreen: PCB-K0402W-V3.0 / DIP-K0402WS-V3.0
|
||||
|
||||
Top side
|
||||
|
||||
|
||||
@@ -0,0 +1,148 @@
|
||||
# Sirivision SR-S25G2109F (S25G1109FP V1.3)
|
||||
|
||||
RTL8373 (non-N) with an RTL8224 quad 2.5G PHY: 8 × 2.5G RJ45 + 1 × SFP+,
|
||||
2 MB flash, no PoE.
|
||||
|
||||
## Label specifications
|
||||
|
||||
| Item | Value |
|
||||
|------|-------|
|
||||
| Manufacturer | Sirivision |
|
||||
| Model | SR-S25G2109F |
|
||||
| Ports | 8 × RJ45 10/100/1000/2500 Mbps, 1 × SFP+ 1G/2.5G/10G |
|
||||
| Power input | 12 V DC, 1.5 A |
|
||||
| Stock firmware | version 1.0.8.0 |
|
||||
| Stock defaults | IP 192.168.1.199, user `admin`, password `admin` |
|
||||
| Serial number | 001008130002003A000005 (also on the PCB barcode sticker) |
|
||||
|
||||
The label does not carry a MAC address. After a factory initialisation the
|
||||
stock firmware uses exactly the values printed on the label.
|
||||
|
||||
## PCB silkscreen and naming
|
||||
|
||||
Top side: `S25G1109FP V1.3 SIRIVISION`. That model name belongs to an unmanaged
|
||||
PoE switch; this board omits the PoE section entirely. What is actually shipped
|
||||
is the label model `SR-S25G2109F`: a simply managed, non-PoE switch. Its stock
|
||||
firmware provides a web interface, but it responds very slowly.
|
||||
|
||||
## CPU, PHY and flash
|
||||
|
||||
| Part | Identification |
|
||||
|------|----------------|
|
||||
| SoC | RTL8373, non-N: `regget 4` returns `0x83730000` and the boot console prints `Detecting CPU: RTL8373` |
|
||||
| PHY | RTL8224, reported as `RTL8224 ID: 0x82240000` |
|
||||
| Flash | GigaDevice 25Q16ESIG (U7), 2 MB, JEDEC id `c8 40 15` |
|
||||
|
||||
The two large ICs sit under soldered heatsinks, their markings are not readable.
|
||||
|
||||
## Build
|
||||
|
||||
```
|
||||
make MACHINE=S25G1109FP_V1_3 CI=1
|
||||
```
|
||||
|
||||
The resulting image can be uploaded through the web interface (`System` page)
|
||||
or the stock web UI.
|
||||
|
||||
## Port mapping
|
||||
|
||||
| Front port | ASIC MAC | Interface |
|
||||
|------------|----------|-----------|
|
||||
| 1–8 | 0–7 | RJ45 2.5G |
|
||||
| 9 | 8 | SFP+ (SDS1) |
|
||||
|
||||
## Board signals
|
||||
|
||||
| Function | GPIO |
|
||||
|----------|------|
|
||||
| SFP detect (ModAbs) | 30, low with a module inserted |
|
||||
| SFP LOS | 37, high without light |
|
||||
| SFP TX disable | not connected, `GPIO_NA` |
|
||||
| SFP I2C | SDA 39 / SCL 40, `I2CBUS(GPIO39_I2C_SDA4, GPIO40_I2C_SCL3_MDC1)` |
|
||||
| Reset button | 54 |
|
||||
|
||||
ModAbs and LOS are separate signals here: GPIO30 tells whether a module is
|
||||
inserted, GPIO37 whether it receives light, so a module is identified even
|
||||
before the link comes up and loss of signal is reported. GPIO38 and GPIO48,
|
||||
which the 8+1 reference boards use for ModAbs and for the reset button, never
|
||||
change on this board; with GPIO38 as ModAbs the firmware believed a module was
|
||||
permanently present and retried the I2C read forever.
|
||||
|
||||
Serial console: the stock firmware runs the loader at 57600 baud and the runtime
|
||||
at 9600 baud, RTLPlayground uses 115200.
|
||||
|
||||
## Front panel
|
||||
|
||||
Each copper port has two LEDs, labelled `Link/Act` (left, yellow) and `2.5G`
|
||||
(right, green). The SFP cage has a single green `Link/Act` LED; next to it are
|
||||
a `Power` LED and the `Reset` button.
|
||||
|
||||
## LED configuration
|
||||
|
||||
Copper ports use LED set 0, the SFP port uses LED set 1:
|
||||
|
||||
| Set | Function 0 (left / SFP) | Function 1 (right) |
|
||||
|-----|-------------------------|--------------------|
|
||||
| SET0 | all speeds, link with activity | 2.5G link |
|
||||
| SET1 | all speeds, link with activity | not populated |
|
||||
|
||||
The board is wired in the stock port order — LED pin `3n+m` belongs to port
|
||||
`n+1`, LED function `m` — so the mux table is the chip default with a single
|
||||
change: the SFP LED sits on pin 25 and is driven by LED function 0, while the
|
||||
default table maps pin 25 to function 1, which is unpopulated in SET1. Without
|
||||
that change the SFP LED stays dark.
|
||||
|
||||
A speed condition alone leaves an LED dark, `LEDS_LINK` has to be set as well.
|
||||
|
||||
## Reset button
|
||||
|
||||
| Hold time while running | Effect |
|
||||
|-------------------------|--------|
|
||||
| less than 3 s | no action |
|
||||
| 3–10 s | reset the switch chip, the firmware reboots (~20 s of downtime) |
|
||||
| more than 10 s | additionally restore the default configuration |
|
||||
|
||||
The system LED blinks slowly while the button is held. Holding the button
|
||||
during power-up instead waits for the release and restores the default
|
||||
configuration when it is released after 10–30 s.
|
||||
|
||||
## MAC address
|
||||
|
||||
The factory MAC sits at `0x1FC000`, six bytes followed by `0xFF` padding. The
|
||||
stock value is a locally administered address (`1A:5D:23:47:4D:EB`), which the
|
||||
firmware rejects, so the device runs with a UUID-derived MAC (`06:…`). A valid
|
||||
globally administered address stored there is used instead.
|
||||
|
||||
## What works
|
||||
|
||||
- all eight 2.5GBASE-T ports at 10/100/1000/2500 Mbps
|
||||
- SFP+ with 10G and 1G modules, including DDM data where the module provides it
|
||||
- 10G: Finisar FCBG110SD1C05 AOC (no DDM), rate `0x67`, SerDes mode `1a`
|
||||
- 1G: ETU ESB5312-3SCD20 (DDM), rate `0x0d`, SerDes mode `04`
|
||||
- LEDs, module insert/remove detection, loss of signal reporting, reset button
|
||||
|
||||
## Unverified
|
||||
|
||||
- 2.5G SFP modules, none was available for testing
|
||||
|
||||
## Photos
|
||||
|
||||
Front panel:
|
||||
|
||||

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

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

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

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

|
||||
@@ -0,0 +1,32 @@
|
||||
### SIRIVISION_S25G2206FP_V1_1
|
||||
|
||||
## Brands
|
||||
|Brand|Type|Managed|PCB|Flash|Chip RTL|
|
||||
|---|---|---|---|---|---|
|
||||
| YuanLey | YS25-0402M | Yes | SIRIVISION_S25G2206FP_V1_1 | 2M | 8372 |
|
||||
|
||||
### Label specifications
|
||||
|
||||
- **Name**:
|
||||
- **Ports**:
|
||||
- 4 × RJ45: 10/100/1000/2500 Mbps
|
||||
- 2 × SFP+: 1000 / 2500 / 10000 Mbps
|
||||
- **Power**: 12V DC, 1A 5525 connector
|
||||
|
||||
### What works
|
||||
The device is fully supported:
|
||||
- ALL 2.5GBASE-T RJ45 ports work at 10/100/1000/2500 Mbps
|
||||
- ALL SFP+ ports support 1G, 2.5G and 10G modules
|
||||
- LEDs work with the same indiciations as the OEM firmware
|
||||
### PCB overview
|
||||
|
||||
**Board markings**
|
||||
- Top silkscreen: SIRIVISION S25G2206FP V1.1
|
||||
|
||||
Top side
|
||||
|
||||
<img src="photos/SIRIVISION_S25G2206FP_V1_1/dev_pcb_top.jpg" width="300" />
|
||||
|
||||
## Power supply
|
||||
|
||||
Input power is delivered via barell plug, `12V 1A` adapter was provided.
|
||||
@@ -1,9 +1,11 @@
|
||||
# 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 |
|
||||
| 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 |
|
||||
| GoodTop | GT-ST018M | Yes | SWTG018AS-A V2.0 | 2MB | 8273N + 8224N |
|
||||
| MokerLink | 2G08110GSN | No | SWTG018AS-A V2.0 | 512KB(25Q40) | 8273N + 8224N |
|
||||
|
||||
|
||||
The following is a documentation for the unmanaged switch marked as `SWTG018AS-A V2.0`.
|
||||
@@ -13,7 +15,7 @@ The original software is running UART on 9600 baud rate.
|
||||
|
||||
Using a SOIC clamp in-board is the only method for initial installation.
|
||||
|
||||
The board has a single flash chips `BS` with 4M-bit size. The front switch, switches between the two flash chips.
|
||||
The board has a single flash chips `BS` with 2M-byte size. The front switch, switches between the two flash chips.
|
||||
These can be programed independently by using said switch - so it is e.g. possible to run the original and new firmware in parallel.
|
||||
|
||||
### Label specifications
|
||||
|
||||
@@ -2,9 +2,11 @@
|
||||
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 | ??? |
|
||||
|Brand |Type |Managed|PCB |Flash |Chip RTL|
|
||||
|---------|--------------|-------|------------------------------|----------------|--------|
|
||||
| Horaco | ZX-SG4T2 | No | PCB-SWTG024AS-A-V2.0.1_19650 | 512K (P25D40SH)| 8372N |
|
||||
| Horaco | HC-SWTG124AS | Yes | PCB-SWTG024AS-A-V2.0.1_19650 | 2M (W25Q16JV)| 8372N |
|
||||
| GoodTop | GT-ST024M | Yes | PCB-SWTG024AS-A-V2.0.1 | 2M | 8372N |
|
||||
|
||||
### Label specifications
|
||||
|
||||
@@ -33,6 +35,8 @@ Bottom
|
||||
|
||||
<img src="photos/SWTG024AS-A-V2_0_1_19650/horaco-zx-sg4t2-pcb-bottom.jpg" width="300" />
|
||||
|
||||
On Horaco HC-SWTG124AS with board PCB-SWTG024AS-A-V2.0.1_19650 the reset button is soldered at `SW1` (on the top left, GPIO48).
|
||||
|
||||
### J1, serial console
|
||||
|
||||
| `J1` pin | Signal |
|
||||
|
||||
@@ -46,4 +46,4 @@ Note,`R52`、`R53`may not be installed.You need to bridge them using either sold
|
||||
|
||||
## 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,48 @@
|
||||
### SWTG024AS-V2.1
|
||||
|
||||
## Brands
|
||||
|Brand|Type|Managed|PCB|Flash|Chip RTL|
|
||||
|---|---|---|---|---|---|
|
||||
| Xikestor | SKS3200-4E2X | Yes | PCB-SWTG024AS-V2.1.0_19023 | 25Q16A | 8372N |
|
||||
| Solola | SL902-SWTG024AS | Yes | PCB-SWTG024AS-V2.1.0_19023 | | 8372N |
|
||||
|
||||
### Label specifications
|
||||
|
||||
- **Ports**:
|
||||
- 4 × RJ45: 10/100/1000/2500 Mbps
|
||||
- 2 × SFP+: 1000/2500/10000 Mbps
|
||||
- **Power**: 1A @ 12V DC
|
||||
|
||||
#### Xikestore SKS-3200-4E2X label
|
||||
<img src="photos/SWTG024AS-V2.1.0/label.jpg" width="300" />
|
||||
|
||||
### What works
|
||||
- ALL 2.5GBASE-T RJ45 ports work at 10/100/1000/2500 Mbps
|
||||
- The SFP+ ports supports 1G, 2.5G and 10G modules.
|
||||
- LEDs work with the same indiciations as the OEM firmware.
|
||||
|
||||
### PCB overview
|
||||
|
||||
**Board markings**
|
||||
- Top silkscreen: PCB-SWTG024AS-V2.1.0
|
||||
|
||||
Top side
|
||||
|
||||
<img src="photos/SWTG024AS-V2.1.0/pcb-top.jpg" width="300" />
|
||||
|
||||
Bottom
|
||||
|
||||
<img src="photos/SWTG024AS-V2.1.0/pcb-bottom.jpg" width="300" />
|
||||
|
||||
### Serial console
|
||||
|
||||
The four-pin through-hole serial header can be seen to the right of the CPU heatsink in the photo above. It is labelled J1, with pin 1 (Indicated by a triangle on the silkscren) at the bottom. the pinout is also on the silkscreen:
|
||||
|
||||
| Pin | Label |
|
||||
|-----|--------|
|
||||
| 1 | TX |
|
||||
| 2 | RX |
|
||||
| 3 | GND |
|
||||
| 4 | 3V3 |
|
||||
|
||||
Adding a header is a few moments work with a soldering iron. Only pins 1-3 are required for serial port use, and the stock firmware outputs at 57600bps at boot.
|
||||
@@ -9,7 +9,7 @@
|
||||
|
||||
## RTLPlayground target
|
||||
|
||||
Use machine target `MACHINE_LIANGUO_ZX_SWTGW215AS` for this device.
|
||||
Build for this device with `make MACHINE=LIANGUO_ZX_SWTGW215AS`, which selects the `machine/LIANGUO_ZX_SWTGW215AS.c` board definition.
|
||||
|
||||
Physical hardware verification: 5x RJ45 ports + 1x SFP port.
|
||||
Port 5 RJ45 is interfaced through a RTL8221B IC.
|
||||
|
||||
@@ -5,9 +5,15 @@
|
||||
|---|---|---|---|---|---|
|
||||
| Mokerlink | ZX-SWTGW218AS | Yes| SWTG118AS-V2.0-16029 | 2MB (FM25Q16A)| 8273N + 8224N |
|
||||
| Sodola | | | | | |
|
||||
| Horaco | | | | | |
|
||||
| Horaco | HC-SWTGW218AS | Yes | SWTG118AS-V2.1 | 2MB | 8373 (non-N) |
|
||||
| XikeStor | SKS3200-8E1X | Yes | SWTG118AS-V2.1-17462 | 2MB (25Q16JVSIQ) | |
|
||||
|
||||
All boards in this table use `MACHINE=SWTGW218AS`. The Horaco HC-SWTGW218AS is also sold with the
|
||||
[SWTG018AS-A V2.0](SWTG018AS_A_V2_0.md) board, which needs a different firmware. Check the marking
|
||||
on the PCB before flashing.
|
||||
|
||||
The PHY and flash-chip models for the tested Horaco units remain unverified.
|
||||
|
||||
|
||||
## Photos
|
||||
|
||||
|
||||
@@ -0,0 +1,36 @@
|
||||
### HG0402XG V1.1
|
||||
|
||||
## Brands
|
||||
|Brand|Type|Managed|PCB|Flash|Chip RTL|
|
||||
|---|---|---|---|---|---|
|
||||
| ZoeRax | HG0402XG | No| HG0402XG V1.1 | 1M (Winbond 25Q) | |
|
||||
|
||||
### Label specifications
|
||||
|
||||
- **Name**: ZOERAX 2.5G 2SFP 4-Port Gigabit Switch
|
||||
- **Model**: HG0402XG-EU
|
||||
- **Ports**:
|
||||
- 4 × RJ45: 10/100/1000/2500 Mbps
|
||||
- 2 × SFP+: 1000 / 2500 / 10000 Mbps
|
||||
<img src="photos/HG0402XG_V1_1/HG0402XG_V1_1_bottom.jpg" width="300" />
|
||||
|
||||
## PCB
|
||||
|
||||
<img src="photos/HG0402XG_V1_1/HG0402XG_V1_1_top.jpg" width="300" />
|
||||
<img src="photos/HG0402XG_V1_1/HG0402XG_V1_1_bottom.jpg" width="300" />
|
||||
|
||||
Be aware that the UART header is metric, the pitch is 2mm. The JST PH2.0mm header shown was addeded after purchase
|
||||
|
||||
The slider switch controls microcontroller U4 in order to configure the VLAN on/off settings. U4 can reset the SoC
|
||||
and also write into its registers to configure VLAN. With RTLPlayground the slider cannot be used for anything else
|
||||
than triggering reset. Any change in the position will trigger this reset.
|
||||
|
||||
### Installation
|
||||
|
||||
Using SPI clamp on-board is the only method for initial installation.
|
||||
|
||||
### What works
|
||||
|
||||
- All four 2.5GBASE-T RJ45 ports at 10/100/1000/2500 Mbps
|
||||
- SFP ports with various 1G/10G Fiber and Ethernet modules
|
||||
- LEDs
|
||||
|
After Width: | Height: | Size: 254 KiB |
|
After Width: | Height: | Size: 90 KiB |
|
After Width: | Height: | Size: 234 KiB |
|
After Width: | Height: | Size: 98 KiB |
|
After Width: | Height: | Size: 434 KiB |
|
After Width: | Height: | Size: 565 KiB |
|
After Width: | Height: | Size: 480 KiB |
|
After Width: | Height: | Size: 82 KiB |
|
After Width: | Height: | Size: 93 KiB |
|
After Width: | Height: | Size: 220 KiB |
|
After Width: | Height: | Size: 273 KiB |
|
After Width: | Height: | Size: 3.0 MiB |
|
After Width: | Height: | Size: 3.6 MiB |
|
After Width: | Height: | Size: 2.8 MiB |
|
After Width: | Height: | Size: 19 KiB |
|
After Width: | Height: | Size: 308 KiB |
|
After Width: | Height: | Size: 262 KiB |
|
After Width: | Height: | Size: 546 KiB |
|
After Width: | Height: | Size: 478 KiB |
|
After Width: | Height: | Size: 639 KiB |
|
After Width: | Height: | Size: 4.0 MiB |
|
After Width: | Height: | Size: 469 KiB |
|
After Width: | Height: | Size: 1.9 MiB |
|
After Width: | Height: | Size: 1.9 MiB |
|
After Width: | Height: | Size: 1.1 MiB |
|
After Width: | Height: | Size: 944 KiB |
|
After Width: | Height: | Size: 1.7 MiB |
|
After Width: | Height: | Size: 1.6 MiB |
|
After Width: | Height: | Size: 1.2 MiB |
|
After Width: | Height: | Size: 229 KiB |
|
After Width: | Height: | Size: 804 KiB |
|
After Width: | Height: | Size: 88 KiB |
|
After Width: | Height: | Size: 75 KiB |
|
After Width: | Height: | Size: 282 KiB |
|
After Width: | Height: | Size: 253 KiB |
|
After Width: | Height: | Size: 270 KiB |
|
After Width: | Height: | Size: 1.7 MiB |
|
After Width: | Height: | Size: 67 KiB |
|
After Width: | Height: | Size: 398 KiB |
|
After Width: | Height: | Size: 283 KiB |
@@ -0,0 +1,23 @@
|
||||
# DNS
|
||||
|
||||
The switch can resolve host names to IPv4 addresses, for the NTP client and
|
||||
for anything else that is given a name instead of an address.
|
||||
|
||||
```
|
||||
dns server <ip> [<ip>] # one or two servers, 0.0.0.0 removes them
|
||||
dns lookup <name> # resolve a name and print the address
|
||||
dns # show the servers and the last lookup
|
||||
```
|
||||
|
||||
The answer to a lookup arrives after the command has returned, so it is
|
||||
printed on the serial console and in the syslog. In the console of the web
|
||||
interface, run `dns` afterwards to see it.
|
||||
|
||||
Without a configured server the resolver uses the one handed out by DHCP.
|
||||
Only address records are looked up; a query goes to the servers in turn,
|
||||
every two seconds, and gives up after six tries. A name that already is a
|
||||
dotted address is taken as it is, without a query.
|
||||
|
||||
The DNS servers are set in the Network card of the System page as well.
|
||||
Settings made there or on the console are kept only once the configuration is
|
||||
saved.
|
||||
@@ -0,0 +1,80 @@
|
||||
# The health command
|
||||
|
||||
`health` prints a one-page snapshot of the firmware's vital signs on the
|
||||
serial console. It exists for the situations where the switch misbehaves and
|
||||
every probe from the network measures the network instead of the device: the
|
||||
dump is taken from inside, costs one command, and works while HTTP does not.
|
||||
|
||||
```
|
||||
> health
|
||||
up 0000001f ticks 0x00001f4c loops 0x000016da rx 0x024e
|
||||
link max 01 slow 0x0000
|
||||
sfp max 74 slow 0x0001
|
||||
rx max 00 slow 0x0000
|
||||
tx max 00 slow 0x0000
|
||||
stp max 04 slow 0x0001
|
||||
cmd max 02 slow 0x0001
|
||||
lldp max 00 slow 0x0000
|
||||
sp 80 untouched 62
|
||||
tcp st 00 tmr 00 rtx 00 rto 00 len 0x0000 mss 0x05b4
|
||||
lport 0x0000 rport 0x0000 rip 0x0000 0x0000
|
||||
httpd left 0x0000 entry 00 stp 01 mvlan 0x0002
|
||||
```
|
||||
|
||||
The instrumentation is not part of a default build. It costs about 1.1 kB
|
||||
of bank 2, a few bytes of xdata and seven banked calls per main-loop pass,
|
||||
which is nothing a user needs unless they are chasing a problem, so it is
|
||||
switched on at build time:
|
||||
|
||||
```
|
||||
make MACHINE=SWTGW218AS HEALTH=1
|
||||
```
|
||||
|
||||
Without it the hooks compile to nothing and `health` is not a command.
|
||||
|
||||
All figures are raw hex. The counters are cumulative: take two dumps a known
|
||||
time apart and the differences give the rates.
|
||||
|
||||
## The first line
|
||||
|
||||
`up` is the seconds counter, `ticks` the system tick (200 per second),
|
||||
`loops` the number of main-loop passes and `rx` the frames handle_rx has
|
||||
taken off the NIC. A healthy loop completes one pass per tick, so the
|
||||
difference in `loops` tracks the difference in `ticks`; a loop that falls
|
||||
behind is being held up by one of the phases below. The `rx` rate separates
|
||||
a flooded CPU port from a stalled loop, which look identical from outside.
|
||||
|
||||
## Phases
|
||||
|
||||
Each line is one section of the main loop: the link poll, SFP handling,
|
||||
frame reception, TCP transmission, the STP timers, the LLDP timer and the command dispatch.
|
||||
`max` is the worst time that section has taken, in ticks of 5 ms; `slow`
|
||||
counts the passes that took two ticks or more. A misbehaving subsystem
|
||||
names itself here, which is the difference between knowing that the loop is
|
||||
slow and knowing why.
|
||||
|
||||
A dump of its own output takes the console some milliseconds, so running
|
||||
`health` repeatedly increments the `cmd` counters by itself. That is the
|
||||
instrument observing itself, not a fault.
|
||||
|
||||
## Stack
|
||||
|
||||
At boot the free stack area is painted with a pattern. `sp` is the live
|
||||
stack pointer; `untouched` counts the painted bytes still intact at the top,
|
||||
which is the closest thing to a high-water mark this hardware offers. A
|
||||
shrinking `untouched` across dumps means something is reaching deeper than
|
||||
anything before it.
|
||||
|
||||
## The TCP slot
|
||||
|
||||
The connection table is printed whole, one slot per pair of lines: TCP
|
||||
state (3 is ESTABLISHED, 0 closed), retransmission timer and counter, bytes
|
||||
in flight, negotiated MSS, then the ports and the peer address. With a
|
||||
single slot serving the entire web interface, one glance answers the
|
||||
question every management outage starts with: who is holding it. A slot
|
||||
showing state 3 with nothing in flight and a peer that no longer answers is
|
||||
a connection whose owner went away.
|
||||
|
||||
`httpd left` and `entry` describe the file transfer the web server believes
|
||||
it is in the middle of, and the trailing flags show whether STP is running
|
||||
and which VLAN carries management.
|
||||
@@ -0,0 +1,572 @@
|
||||
# LEDs HOWTO using firmware function tracing
|
||||
|
||||
This is a tutorial to show how to figure out the LED configuration for
|
||||
unmanaged switches (on managed ones one can just print out the registers
|
||||
from the command line) without using an I2C port on the device together
|
||||
an I2C-capable microcontroller. You will only need to have a SOIC clamp
|
||||
and will have to run ghidra to do a bit of reversing.
|
||||
|
||||
The additional advantage of this method is that it not only gives the LED
|
||||
configuration but also the full PHY configuration and SDS configuration e.g.
|
||||
in case of additional PHYs being used on the device.
|
||||
|
||||
We will use the ZoeRax HG0402XG V1.1 as an example device.
|
||||
|
||||
The first step is to read out the flash with a SOIC clamp.
|
||||
Save the image as
|
||||
`zoerax_hg0402xg.bin`
|
||||
|
||||
The next step is to use a decompiler such as ghidra to identify the
|
||||
functions used to write or read registers, SDS or to the PHY.
|
||||
|
||||
<img src="images/ghidra_open.png" width="300" />
|
||||
|
||||
First, load the code in BANK0 into ghidra. The type of code is
|
||||
8051 default big-endian. The code starts at offset 0x0002 in the file
|
||||
and has length 0x3ffd
|
||||
|
||||
<img src="images/ghidra_add_block.png" width="300" />
|
||||
Add BANK1 into ghidra by using File->Add to program...
|
||||
and add the code starting at 0x4000 in `zoerax_hg0402xg.bin` with length
|
||||
0xc000. Repeat for BANK2 (starts at 0x10000) and BANK3 (starts at 0x1c0000).
|
||||
They both have length 0xc000.
|
||||
|
||||
<img src="images/ghidra_search_read_reg.png" width="300" />
|
||||
|
||||
Now, find the read_reg function. An example of the read-register function
|
||||
is given below. Note the critical bit, namely the execution of the
|
||||
read-function of an ASIC register via an SFR:
|
||||
```
|
||||
CODE1::4dc9 75 a0 01 MOV SFR_EXEC_GO,#0x1
|
||||
```
|
||||
|
||||
This sequence of bytes is very specific. Replace 0x01 with 0x03 and you have
|
||||
the regster write SFR, 0x05 reads the SerDes, 0x07 writes it. 0x09 reads
|
||||
from the PHY and 0x0b writes to it.
|
||||
|
||||
|
||||
```
|
||||
**************************************************************
|
||||
* FUNCTION *
|
||||
**************************************************************
|
||||
undefined reg_read()
|
||||
undefined <UNASSIGNED> <RETURN>
|
||||
reg_read
|
||||
CODE1::4da9 ab 07 MOV R3,BANK0_R7 = ??
|
||||
CODE1::4dab aa 06 MOV R2,BANK0_R6 = ??
|
||||
CODE1::4dad e4 CLR A
|
||||
CODE1::4dae ff MOV R7,A
|
||||
CODE1::4daf fe MOV R6,A
|
||||
CODE1::4db0 fd MOV R5,A
|
||||
CODE1::4db1 fc MOV R4,A
|
||||
CODE1::4db2 90 00 c9 MOV DPTR,#0xc9
|
||||
CODE1::4db5 12 06 15 LCALL FUN_CODE_0615 undefined FUN_CODE_0615()
|
||||
CODE1::4db8 e4 CLR A
|
||||
CODE1::4db9 ff MOV R7,A
|
||||
CODE1::4dba fe MOV R6,A
|
||||
CODE1::4dbb fd MOV R5,A
|
||||
CODE1::4dbc fc MOV R4,A
|
||||
CODE1::4dbd 90 00 c5 MOV DPTR,#0xc5
|
||||
CODE1::4dc0 12 06 15 LCALL FUN_CODE_0615 undefined FUN_CODE_0615()
|
||||
CODE1::4dc3 ea MOV A,R2
|
||||
CODE1::4dc4 f5 a2 MOV SFR_A_ADDRH,A = ??
|
||||
CODE1::4dc6 eb MOV A,R3
|
||||
CODE1::4dc7 f5 a3 MOV SFR_A_ADDRL,A = ??
|
||||
CODE1::4dc9 75 a0 01 MOV SFR_EXEC_GO,#0x1 <<<<<<<< Executes READ REG
|
||||
LAB_CODE1__4dcc XREF[1]: CODE1::4dcf(j)
|
||||
CODE1::4dcc af a1 MOV R7,SFR_EXEC_STATUS = ??
|
||||
CODE1::4dce ef MOV A,R7
|
||||
CODE1::4dcf 70 fb JNZ LAB_CODE1__4dcc
|
||||
CODE1::4dd1 af a4 MOV R7,DAT_SFR_a4 = ??
|
||||
CODE1::4dd3 fc MOV R4,A
|
||||
CODE1::4dd4 fd MOV R5,A
|
||||
CODE1::4dd5 fe MOV R6,A
|
||||
CODE1::4dd6 90 00 c9 MOV DPTR,#0xc9
|
||||
CODE1::4dd9 12 06 15 LCALL FUN_CODE_0615 undefined FUN_CODE_0615()
|
||||
CODE1::4ddc 90 00 c5 MOV DPTR,#0xc5
|
||||
CODE1::4ddf 12 05 f9 LCALL FUN_CODE_05f9 undefined FUN_CODE_05f9()
|
||||
CODE1::4de2 c0 00 PUSH BANK0_R0
|
||||
CODE1::4de4 90 00 c9 MOV DPTR,#0xc9
|
||||
CODE1::4de7 12 05 dd LCALL read_xmem_32_b undefined read_xmem_32_b()
|
||||
CODE1::4dea 78 18 MOV R0,#0x18
|
||||
CODE1::4dec 12 05 86 LCALL FUN_CODE_0586 undefined FUN_CODE_0586()
|
||||
CODE1::4def d0 00 POP BANK0_R0
|
||||
CODE1::4df1 12 05 48 LCALL FUN_CODE_0548 undefined FUN_CODE_0548()
|
||||
CODE1::4df4 90 00 c5 MOV DPTR,#0xc5
|
||||
CODE1::4df7 12 06 15 LCALL FUN_CODE_0615 undefined FUN_CODE_0615()
|
||||
CODE1::4dfa af a5 MOV R7,DAT_SFR_a5 = ??
|
||||
CODE1::4dfc e4 CLR A
|
||||
CODE1::4dfd fc MOV R4,A
|
||||
CODE1::4dfe fd MOV R5,A
|
||||
CODE1::4dff fe MOV R6,A
|
||||
CODE1::4e00 90 00 c9 MOV DPTR,#0xc9
|
||||
CODE1::4e03 12 06 15 LCALL FUN_CODE_0615 undefined FUN_CODE_0615()
|
||||
CODE1::4e06 90 00 c5 MOV DPTR,#0xc5
|
||||
CODE1::4e09 12 05 f9 LCALL FUN_CODE_05f9 undefined FUN_CODE_05f9()
|
||||
CODE1::4e0c c0 00 PUSH BANK0_R0
|
||||
CODE1::4e0e 90 00 c9 MOV DPTR,#0xc9
|
||||
CODE1::4e11 12 05 dd LCALL read_xmem_32_b undefined read_xmem_32_b()
|
||||
CODE1::4e14 78 10 MOV R0,#0x10
|
||||
CODE1::4e16 12 05 86 LCALL FUN_CODE_0586 undefined FUN_CODE_0586()
|
||||
CODE1::4e19 d0 00 POP BANK0_R0
|
||||
CODE1::4e1b 12 05 48 LCALL FUN_CODE_0548 undefined FUN_CODE_0548()
|
||||
CODE1::4e1e 90 00 c5 MOV DPTR,#0xc5
|
||||
CODE1::4e21 12 06 15 LCALL FUN_CODE_0615 undefined FUN_CODE_0615()
|
||||
CODE1::4e24 af a6 MOV R7,DAT_SFR_a6 = ??
|
||||
CODE1::4e26 e4 CLR A
|
||||
CODE1::4e27 fc MOV R4,A
|
||||
CODE1::4e28 fd MOV R5,A
|
||||
CODE1::4e29 fe MOV R6,A
|
||||
CODE1::4e2a 90 00 c9 MOV DPTR,#0xc9
|
||||
CODE1::4e2d 12 06 15 LCALL FUN_CODE_0615 undefined FUN_CODE_0615()
|
||||
CODE1::4e30 90 00 c5 MOV DPTR,#0xc5
|
||||
CODE1::4e33 12 05 f9 LCALL FUN_CODE_05f9 undefined FUN_CODE_05f9()
|
||||
CODE1::4e36 c0 00 PUSH BANK0_R0
|
||||
CODE1::4e38 90 00 c9 MOV DPTR,#0xc9
|
||||
CODE1::4e3b 12 05 dd LCALL read_xmem_32_b undefined read_xmem_32_b()
|
||||
CODE1::4e3e 78 08 MOV R0,#0x8
|
||||
CODE1::4e40 12 05 86 LCALL FUN_CODE_0586 undefined FUN_CODE_0586()
|
||||
CODE1::4e43 d0 00 POP BANK0_R0
|
||||
CODE1::4e45 12 05 48 LCALL FUN_CODE_0548 undefined FUN_CODE_0548()
|
||||
CODE1::4e48 90 00 c5 MOV DPTR,#0xc5
|
||||
CODE1::4e4b 12 06 15 LCALL FUN_CODE_0615 undefined FUN_CODE_0615()
|
||||
CODE1::4e4e af a7 MOV R7,DAT_SFR_a7 = ??
|
||||
CODE1::4e50 e4 CLR A
|
||||
CODE1::4e51 fc MOV R4,A
|
||||
CODE1::4e52 fd MOV R5,A
|
||||
CODE1::4e53 fe MOV R6,A
|
||||
CODE1::4e54 90 00 c9 MOV DPTR,#0xc9
|
||||
CODE1::4e57 12 06 15 LCALL FUN_CODE_0615 undefined FUN_CODE_0615()
|
||||
CODE1::4e5a 90 00 c5 MOV DPTR,#0xc5
|
||||
CODE1::4e5d 12 05 dd LCALL read_xmem_32_b undefined read_xmem_32_b()
|
||||
CODE1::4e60 90 00 c9 MOV DPTR,#0xc9
|
||||
CODE1::4e63 12 05 f9 LCALL FUN_CODE_05f9 undefined FUN_CODE_05f9()
|
||||
CODE1::4e66 12 05 48 LCALL FUN_CODE_0548 undefined FUN_CODE_0548()
|
||||
CODE1::4e69 90 00 c5 MOV DPTR,#0xc5
|
||||
CODE1::4e6c 12 06 15 LCALL FUN_CODE_0615 undefined FUN_CODE_0615()
|
||||
CODE1::4e6f 90 00 c5 MOV DPTR,#0xc5
|
||||
CODE1::4e72 02 05 dd LJMP read_xmem_32_b undefined read_xmem_32_b()
|
||||
-- Flow Override: CALL_RETURN (CALL_TERMINATOR)
|
||||
|
||||
```
|
||||
|
||||
INSERT ghidra_search_read_reg.png
|
||||
|
||||
Identify all the read and write function to the HW of the SoC using the find
|
||||
in memory function.
|
||||
|
||||
The next task is to adapt the intercept routines in a special assembler
|
||||
file. Don't worry, you do not need to understand assembler to do this.
|
||||
|
||||
```
|
||||
; Assembler file to intercept register writes/reads and PHY writes/reads
|
||||
; assemble with as31
|
||||
; Compile with
|
||||
; as31 -Fbyte -Orwintercept_zoerax_hg0402xg.hex rwintercept_zoerax_hg0402xg.asm
|
||||
; ./injector zoerax_hg0402xg.bin RTLPlayground/rwintercept_zoerax_hg0402xg.hex zoerax_hg0402xg_patched.bin
|
||||
; Start at 0x3b00, where there is always some space in BANK 0
|
||||
.equ valptr, 0x199
|
||||
|
||||
|
||||
.equ SFR_DATA_0, 0xa7
|
||||
.equ SFR_DATA_8, 0xa6
|
||||
.equ SFR_DATA_16, 0xa5
|
||||
.equ SFR_DATA_24, 0xa4
|
||||
|
||||
; Intercept Register Write
|
||||
.org 0x4d62
|
||||
lcall reg_write
|
||||
nop
|
||||
|
||||
; Intercept Register Read
|
||||
.org 0x4dad
|
||||
lcall reg_read_head
|
||||
.org 0x4e6f
|
||||
lcall reg_read_tail
|
||||
|
||||
; Intercept PHY write
|
||||
.org 0x522c
|
||||
lcall phy_write
|
||||
|
||||
; Intercept PHY read
|
||||
.org 0x526f
|
||||
lcall phy_read_head
|
||||
.org 0x5295
|
||||
ljmp phy_read_tail
|
||||
|
||||
; Intercept SDS write
|
||||
.org 0x503c
|
||||
lcall sds_write
|
||||
nop
|
||||
|
||||
; Intercept SDS read
|
||||
.org 0x5061
|
||||
lcall sds_read_head
|
||||
nop
|
||||
.org 0x507b
|
||||
ljmp sds_read_tail
|
||||
|
||||
;
|
||||
; BEGIN actual code at unused end of BANK0
|
||||
;
|
||||
.org 0x3b00
|
||||
|
||||
;
|
||||
; print the address of a register write. The address is in R6/R7. A and r4 can be scratched
|
||||
;
|
||||
reg_write:
|
||||
mov a, #'R'
|
||||
acall putc
|
||||
|
||||
mov a, r6
|
||||
acall wbyte
|
||||
|
||||
mov a, r7
|
||||
acall wbyte
|
||||
|
||||
mov a, #'-'
|
||||
acall putc
|
||||
|
||||
mov dptr, #valptr
|
||||
movx a, @dptr
|
||||
acall wbyte
|
||||
mov dptr, #valptr + 1
|
||||
movx a, @dptr
|
||||
acall wbyte
|
||||
mov dptr, #valptr + 2
|
||||
movx a, @dptr
|
||||
acall wbyte
|
||||
mov dptr, #valptr + 3
|
||||
movx a, @dptr
|
||||
acall wbyte
|
||||
|
||||
mov a, #' '
|
||||
acall putc
|
||||
|
||||
mov a, r7
|
||||
mov r5, a
|
||||
mov a, r6
|
||||
mov r4, a
|
||||
ret
|
||||
[...]
|
||||
```
|
||||
|
||||
The full assembler file can be found here: XXXXXXXXXX
|
||||
|
||||
|
||||
Once you have assembled the code, inject the bytes into the existing image,
|
||||
creating a patched version:
|
||||
```
|
||||
$ as31 -Fbyte -Orwintercept_zoerax_hg0402xg.hex rwintercept_zoerax_hg0402xg.asm
|
||||
Begin Pass #1
|
||||
Begin Pass #2
|
||||
./injector zoerax_hg0402xg.bin rwintercept_zoerax_hg0402xg.hex zoerax_hg0402xg_patched.bin
|
||||
Input file size: 1048576
|
||||
Bytes read:: 1048576
|
||||
Patched 417 bytes.
|
||||
```
|
||||
|
||||
Now flash `zoerax_hg0402xg_patched.bin` onto device.
|
||||
|
||||
Connect a serial adapter and plug in power. You will see:
|
||||
|
||||
```
|
||||
$ picocom -b 9600 /dev/ttyUSB0
|
||||
picocom v3.1
|
||||
|
||||
port is : /dev/ttyUSB0
|
||||
flowcontrol : none
|
||||
baudrate is : 9600
|
||||
parity is : none
|
||||
databits are : 8
|
||||
stopbits are : 1
|
||||
escape is : C-a
|
||||
local echo is : no
|
||||
noinit is : no
|
||||
noreset is : no
|
||||
hangup is : no
|
||||
nolock is : no
|
||||
send_cmd is : sz -vv
|
||||
receive_cmd is : rz -vv -E
|
||||
imap is :
|
||||
omap is :
|
||||
emap is : crcrlf,delbs,
|
||||
logfile is : none
|
||||
initstring : none
|
||||
exit_after is : not set
|
||||
exit is : no
|
||||
|
||||
Type [C-a] [C-h] to see available commands
|
||||
Terminal ready
|
||||
[The printout only starts once the serial peripherial has been set up. Initially its timing is off:]
|
||||
�.Ӛ�҂���������30-00005555 r6330:00005555 R6330-00005515 [RTL837X_REG_SMI_MAC_TYPE]
|
||||
[RTL837X_REG_SMI_PORT_POLLING: ]
|
||||
r6334:00000000 R6334-000000f0
|
||||
r6454:00000000 R6454-00007000
|
||||
|
||||
[Read SoC-Revision]
|
||||
r000c:00200000 R000c-002a0000 r000c:202a6818 r000c:202a6818 R000c-20206818
|
||||
p001e.000d:ffff R02f8-0000ffff R02f4-0000fffa P000001.1e00000d:fffa p001e.000d:ffff p001e.000d:ffff
|
||||
R02f8-0000ffff R02f4-0000fff0 P000001.1e00000d:fff0
|
||||
|
||||
[RTL837X_REG_LED_MODE being configured:]
|
||||
r6520:0021fdb0 R6520-0021e7b0 r6520:0021e7b0 R6520-0021e6b0
|
||||
|
||||
[RLDP Leds configured via RTL837X_REG_LED_RLDP_X:]
|
||||
r65f8:00000018 R65f8-0000001b R65fc-fffff000 r6600:00000000 R6600-0000000f
|
||||
|
||||
[RTL837X_REG_LED_GLB_IO_EN: ]
|
||||
r65dc:5fffff00 R65dc-7fffff00 r65dc:7fffff00 R65dc-77ffff00
|
||||
|
||||
[RTL837X_PIN_MUX_0, makes serial go out of sync:]
|
||||
r7f8c:30000000 R7f8c-30000000 r7f8c:300��.Ӛ�҂���������30-00005555
|
||||
|
||||
r6330:00005555 R6330-00005515 r6334:00000000 R6334-000000f0
|
||||
|
||||
r6454:00000000 R6454-00007000
|
||||
|
||||
[Read SoC-Revision (again)]
|
||||
r000c:00200000 R000c-002a0000 r000c:202a6818 r000c:202a6818 R000c-20206818
|
||||
p001e.000d:ffff R02f8-0000ffff R02f4-0000fffa P000001.1e00000d:fffa p001e.000d:ffff p001e.000d:ffff
|
||||
R02f8-0000ffff R02f4-0000fff0
|
||||
P000001.1e00000d:fff0
|
||||
|
||||
#### LED configuration ########
|
||||
[RTL837X_REG_LED_MODE being configured (final):]
|
||||
r6520:0021fdb0 R6520-0021e7b0 r6520:0021e7b0 R6520-0021e6b0
|
||||
|
||||
[More RTL837X_REG_LED_RLDP]
|
||||
r65f8:00000018 R65f8-0000001b R65fc-fffff000 r6600:00000000 R6600-0000000f
|
||||
|
||||
[RTL837X_REG_LED_GLB_IO_EN]
|
||||
r65dc:5fffff00 R65dc-7fffff00 r65dc:7fffff00 R65dc-77ffff00
|
||||
|
||||
[RTL837X_PIN_MUX_0
|
||||
r7f8c:30000000 R7f8c-30000000 r7f8c:30000000 R7f8c-38000000
|
||||
|
||||
[RTL837X_LED_PORT_SET_SEL]
|
||||
r654c:00000000 R654c-00010000
|
||||
|
||||
[RTL837X_REG_LED1_0_SET0]
|
||||
R6548-00000141
|
||||
|
||||
[RTL837X_REG_LED3_2_SET0]
|
||||
r6544:01411000 R6544-01410174
|
||||
|
||||
[RTL837X_REG_LED1_0_SET1]
|
||||
r6540:00440070 R6540-0044017f
|
||||
|
||||
[RTL837X_REG_LED3_0_SET1]
|
||||
r6528:00000000 R6528-000f0000
|
||||
|
||||
[SDS 0 being configure:]
|
||||
r7b20:00000bff R7b20-000003ff r7b20:000003ff R7b20-000003fa r7b20:000003fa R7b20-000003fa
|
||||
Q002110:4480 Q002113:0400 Q002118:6d02 Q00211b:424e Q00211d:0002 Q00361c:1390
|
||||
Q003614:003f Q003610:0200 Q002e04:0080 Q002e06:0408 Q002e07:020d Q002e09:0601
|
||||
Q002e0b:222c Q002e0c:a217 Q002e0d:fe40 Q002e15:f5c1 Q002e16:0443 Q002e1d:abb0
|
||||
Q000612:5078 Q000706:9401 Q000708:9401 Q00070a:9401 Q00070c:9401 Q001f0b:0003
|
||||
Q000603:c45c Q00061f:2100 q001f0b:0003 Q001f0b:000f q002000:0000 Q002000:0030
|
||||
q002000:0030 Q002000:0010 q002000:0010 Q002000:0050 q002000:0050 Q002000:00d0
|
||||
q002000:00d0 Q002000:0cd0 q002000:0cd0 Q002000:04d0 q002000:04d0 Q002000:04d0
|
||||
q002000:04d0 Q002000:0cd0 q002000:0cd0 Q002000:00d0 q002000:00d0 Q002000:00d0
|
||||
q002000:00d0 Q002000:0050 q002000:0050 Q002000:0010 q002000:0010 Q002000:0010
|
||||
q002000:0010 Q002000:0030 q002000:0030 Q002000:0000 q001f00:0000 Q001f00:000b
|
||||
q001f00:000b Q001f00:0000
|
||||
|
||||
[SDS 1 being configured]
|
||||
r7b20:000003fa R7b20-000003fa r7b20:000003fa R7b20-0000035a r7b20:0000035a R7b20-0000035a
|
||||
Q012110:4480 Q012113:0400 Q012118:6d02 Q01211b:424e Q01211d:0002 Q01361c:1390
|
||||
Q013614:003f Q013610:0200 Q012e04:0080 Q012e06:0408 Q012e07:020d Q012e09:0601
|
||||
Q012e0b:222c Q012e0c:a217 Q012e0d:fe40 Q012e15:f5c1 Q012e16:0443 Q012e1d:abb0
|
||||
Q010612:5078 Q010706:9401 Q010708:9401 Q01070a:9401 Q01070c:9401 Q011f0b:0003
|
||||
Q010603:c45c Q01061f:2100 q011f0b:0003 Q011f0b:000f q012000:0000 Q012000:0030
|
||||
q012000:0030 Q012000:0010 q012000:0010 Q012000:0050 q012000:0050 Q012000:00d0
|
||||
q012000:00d0 Q012000:0cd0 q012000:0cd0 Q012000:04d0 q012000:04d0 Q012000:04d0
|
||||
q012000:04d0 Q012000:0cd0 q012000:0cd0 Q012000:00d0 q012000:00d0 Q012000:00d0
|
||||
q012000:00d0 Q012000:0050 q012000:0050 Q012000:0010 q012000:0010 Q012000:0010
|
||||
q012000:0010 Q012000:0030 q012000:0030 Q012000:0000 q011f00:0000 Q011f00:000b
|
||||
q011f00:000b Q011f00:0000 q012100:4902 Q012100:4906 q013605:0000 Q013605:4000
|
||||
Q011f02:001f q011f15:0096 q002100:4902 Q002100:4906 q003605:0000 Q003605:4000
|
||||
Q001f02:001f q001f15:00a6
|
||||
|
||||
r0a90:000000f3 R0a90-000000fc P0000f0.1f00a610:2858 r5fd4:0002914a
|
||||
R5fd4-001a914a r1538:00000e33 R1538-00000e37 r1538:00000e37 R1538-00000e37
|
||||
r1538:00000e37 R1538-00000f37 r1638:00000e33 R1638-00000e37 r1638:00000e37
|
||||
R1638-00000e37 r1638:00000e37 R1638-00000f37 r1738:00000e33 R1738-00000e37
|
||||
r1738:00000e37 R1738-00000e37 r1738:00000e37 R1738-00000f37 r1838:00000e33
|
||||
R1838-00000e37 r1838:00000e37 R1838-00000e37 r1838:00000e37 R1838-00000f37
|
||||
r1938:00000e33 R1938-00000e37 r1938:00000e37 R1938-00000e37 r1938:00000e37
|
||||
R1938-00000f37 r1a38:00000e33 R1a38-00000e37 r1a38:00000e37 R1a38-00000e37
|
||||
r1a38:00000e37 R1a38-00000f37 r0b7c:000000d8 R0b7c-000000f8 R7124-00001050
|
||||
R7128-00001050 R712c-00001050 R7130-00001050 R7134-00001050 R7138-00001050
|
||||
R713c-00001050 R7140-00001050 R7144-00001050 R7148-00001050 r6040:00000030
|
||||
R6040-00000031 p041f.a436:8195 P000010.1f00a436:0005 p041f.a438:02d4
|
||||
P000010.1f00a436:801e p041f.a438:0000 p041f.b820:0000 P000010.1f00b820:0010
|
||||
p041f.b800:00c0 p041f.a436:801e P000010.1f00a436:8023 p041f.a438:0000
|
||||
P000010.1f00a438:1802 p041f.a436:8025
|
||||
|
||||
[PHY with ID 5 being patched] P000010.1f00a436:b82e p041f.a438:0000
|
||||
P000010.1f00a438:0001 p041f.b820:0010 P000010.1f00b820:0090
|
||||
P000010.1f00a436:a016 P000010.1f00a438:0000 P000010.1f00a436:a012
|
||||
P000010.1f00a438:0000 P000010.1f00a436:a014 P000010.1f00a438:1800
|
||||
P000010.1f00a438:8010 P000010.1f00a438:1800 P000010.1f00a438:8013
|
||||
P000010.1f00a438:1800 P000010.1f00a438:8026 P000010.1f00a438:1800
|
||||
P000010.1f00a438:802a P000010.1f00a438:1800 P000010.1f00a438:802e
|
||||
P000010.1f00a438:1800 P000010.1f00a438:802e P000010.1f00a438:1800
|
||||
P000010.1f00a438:802e P000010.1f00a438:1800 P000010.1f00a438:802e
|
||||
...
|
||||
```
|
||||
Where:
|
||||
```
|
||||
r: read-reg
|
||||
R: write-reg
|
||||
q: read SDS
|
||||
Q: write SDS
|
||||
p: read PHY
|
||||
P: write SDS
|
||||
```
|
||||
|
||||
The LED configuration has been marked above. We now write exactly those registers
|
||||
into the registers of `rtlplayground.c` on startup:
|
||||
|
||||
```
|
||||
[...]
|
||||
set_sys_led_state(SYS_LED_ON);
|
||||
|
||||
// [RTL837X_REG_LED_MODE being configured (final):]
|
||||
// r6520:0021fdb0 R6520-0021e7b0 r6520:0021e7b0 R6520-0021e6b0
|
||||
REG_SET(RTL837X_REG_LED_MODE, 0x0021e6b0);
|
||||
|
||||
// [More RTL837X_REG_LED_RLDP]
|
||||
// r65f8:00000018 R65f8-0000001b R65fc-fffff000 r6600:00000000 R6600-0000000f
|
||||
REG_SET(0x65f8, 0x0000001b);
|
||||
REG_SET(0x65fc, 0xfffff000);
|
||||
REG_SET(0x6600, 0x0000000f);
|
||||
|
||||
// [RTL837X_REG_LED_GLB_IO_EN]
|
||||
// r65dc:5fffff00 R65dc-7fffff00 r65dc:7fffff00 R65dc-77ffff00
|
||||
REG_SET(0x65dc, 0x77ffff00);
|
||||
|
||||
// [RTL837X_PIN_MUX_0
|
||||
// r7f8c:30000000 R7f8c-30000000 r7f8c:30000000 R7f8c-38000000
|
||||
REG_SET(0x7f8c, 0x38000000);
|
||||
|
||||
// [RTL837X_LED_PORT_SET_SEL]
|
||||
// r654c:00000000 R654c-00010000
|
||||
REG_SET(0x654c, 0x00010040);
|
||||
|
||||
// [RTL837X_REG_LED1_0_SET0]
|
||||
// R6548-00000141
|
||||
REG_SET(0x6548, 0x00000141);
|
||||
|
||||
// [RTL837X_REG_LED3_2_SET0]
|
||||
// r6544:01411000 R6544-01410174
|
||||
REG_SET(0x6544, 0x01410174);
|
||||
|
||||
// [RTL837X_REG_LED1_0_SET1]
|
||||
// r6540:00440070 R6540-0044017f
|
||||
REG_SET(0x6540, 0x0044017f);
|
||||
|
||||
// [RTL837X_REG_LED3_0_SET1]
|
||||
// r6528:00000000 R6528-000f0000
|
||||
REG_SET(0x6528, 0x000f0000);
|
||||
|
||||
cmd_editor_init();
|
||||
[...]
|
||||
```
|
||||
|
||||
Create an initial configuraiton for your device by copying over an existing one
|
||||
for the same device type, i.e. same port-configuraiton and same SoC,
|
||||
give it a new name. Make sure that the led_mux_custom and led_mux fields are not
|
||||
used, because our device does not customize the LED muxes.
|
||||
|
||||
Compile and flash onto your device.
|
||||
|
||||
Now, boot the switch and issue the `leds`command, which will print out:
|
||||
```
|
||||
RTL837X_PIN_MUX_0: 0x38000000
|
||||
RTL837X_REG_LED_GLB_IO_EN: 0x77ffff00
|
||||
RTL837X_REG_LED1_0_SET0: 0x00000141
|
||||
RTL837X_REG_LED3_2_SET0: 0x01410174
|
||||
RTL837X_REG_LED1_0_SET1: 0x0044017f
|
||||
RTL837X_REG_LED3_2_SET1: 0x18000040
|
||||
RTL837X_REG_LED1_0_SET2: 0x00000000
|
||||
RTL837X_REG_LED3_2_SET2: 0x00000000
|
||||
RTL837X_REG_LED1_0_SET3: 0x00000000
|
||||
RTL837X_REG_LED3_2_SET3: 0x00000000
|
||||
RTL837X_REG_LED3_0_SET1: 0x000f0000
|
||||
RTL837X_REG_LED3_0_SET3: 0x00000000
|
||||
RTL837X_LED_PORT_SET_SEL: 0x00010040
|
||||
RTL837X_REG_LED_GLB_MUX_1: 0x3fffffff
|
||||
RTL837X_REG_LED_GLB_MUX_2: 0x0c3fffff
|
||||
RTL837X_REG_LED_GLB_MUX_3: 0x1245038d
|
||||
RTL837X_REG_LED_GLB_MUX_4: 0x19616554
|
||||
RTL837X_REG_LED_GLB_MUX_5: 0x2079d71a
|
||||
RTL837X_REG_LED_GLB_MUX_6: 0x000238a1
|
||||
RTL837X_REG_LED_GLB_ACTIVE: 0x3ffbedff
|
||||
RTL837X_REG_LED_MODE: 0x0021e6b0
|
||||
LED pad Configuration:
|
||||
00 01 02 03 04 05 06 07 08 09 0a 0b 0c 0d 0e 0f 10 11 12 13 14 15 16 17 18 19 1a 1b
|
||||
3f 3f 3f 3f 3f 3f 3f 3f 0f 0c 0d 0e 10 11 12 14 15 16 18 19 1a 1c 1d 1e 20 21 22 23
|
||||
LED-set Configuration:
|
||||
LED-ID 0 1 2 3
|
||||
SET 0: 000141 000000 000174 000141
|
||||
SET 1: 0f017f 000044 000040 001800
|
||||
SET 2: 000000 000000 000000 000000
|
||||
SET 3: 000000 000000 000000 000000
|
||||
Port 3: SET 1: ( 2G5 TWO_1G 1G 500M 100M 10M LINK ACT 10G TWO_5G 5G TWO_2G5), ( 1G LINK), ( LINK), ( COL DUPLEX),
|
||||
Port 4: SET 0: ( 2G5 LINK ACT), (), ( 1G 100M 10M LINK ACT), ( 2G5 LINK ACT),
|
||||
Port 5: SET 0: ( 2G5 LINK ACT), (), ( 1G 100M 10M LINK ACT), ( 2G5 LINK ACT),
|
||||
Port 6: SET 0: ( 2G5 LINK ACT), (), ( 1G 100M 10M LINK ACT), ( 2G5 LINK ACT),
|
||||
Port 7: SET 0: ( 2G5 LINK ACT), (), ( 1G 100M 10M LINK ACT), ( 2G5 LINK ACT),
|
||||
Port 8: SET 1: ( 2G5 TWO_1G 1G 500M 100M 10M LINK ACT 10G TWO_5G 5G TWO_2G5), ( 1G LINK), ( LINK), ( COL DUPLEX),
|
||||
|
||||
```
|
||||
Together with the GPIO information, this will give you the following machine configuration:
|
||||
```
|
||||
_code const struct machine machine = {
|
||||
.machine_name = "ZOERAX HG0402XG",
|
||||
.isRTL8373 = 0,
|
||||
.min_port = 3,
|
||||
.max_port = 8,
|
||||
.n_sfp = 2,
|
||||
.log_to_phys_port = {0, 0, 0, 5, 1, 2, 3, 4, 6},
|
||||
.phys_to_log_port = {4, 5, 6, 7, 3, 8, 0, 0, 0},
|
||||
.is_sfp = {0, 0, 0, 1, 0, 0, 0, 0, 2},
|
||||
// Left SFP port (5)
|
||||
.sfp_port[0].pin_detect = GPIO30_ACL_BIT3_EN,
|
||||
.sfp_port[0].pin_los = GPIO_NA,
|
||||
.sfp_port[0].pin_tx_disable = GPIO_NA,
|
||||
.sfp_port[0].sds = 1,
|
||||
.sfp_port[0].i2c = I2CBUS( GPIO39_I2C_SDA4, GPIO40_I2C_SCL3_MDC1 ),
|
||||
// Right SFP port (6)
|
||||
.sfp_port[1].pin_detect = GPIO50_I2C_SCL2_UART1_TX,
|
||||
.sfp_port[1].pin_los = GPIO_NA,
|
||||
.sfp_port[1].pin_tx_disable = GPIO_NA,
|
||||
.sfp_port[1].sds = 0,
|
||||
.sfp_port[1].i2c = I2CBUS( GPIO41_I2C_SDA3_MDIO1, GPIO40_I2C_SCL3_MDC1 ),
|
||||
.reset_pin = GPIO_NA,
|
||||
.high_leds = { .mux = LED_27 | LED_29, .enable = LED_28_SYS | LED_29 },
|
||||
.port_led_set = { 0, 0, 0, 1, 0, 0, 0, 0, 1},
|
||||
/* On Ethernet: Left led is orange, right green
|
||||
* 100M and 1G give right led green with activity
|
||||
* 2.5GBit adds solid left orange LED
|
||||
* LEDs for the SFP-ports are the same as for the Ethernet ports
|
||||
*/
|
||||
.led_sets = { { LEDS_2G5 | LEDS_LINK | LEDS_ACT,
|
||||
0,
|
||||
LEDS_1G | LEDS_100M | LEDS_10M | LEDS_LINK | LEDS_ACT,
|
||||
LEDS_2G5 | LEDS_LINK | LEDS_ACT
|
||||
},
|
||||
{ LEDS_2G5 | LEDS_TWO_PAIR_1G | LEDS_1G | LEDS_500M | LEDS_100M | LEDS_10M |
|
||||
LEDS_LINK | LEDS_ACT | LEDS_10G | LEDS_TWO_PAIR_5G | LEDS_5G | LEDS_TWO_PAIR_2G5,
|
||||
LEDS_1G | LEDS_LINK,
|
||||
LEDS_LINK,
|
||||
LEDS_COL | LEDS_DUPLEX
|
||||
}
|
||||
}
|
||||
|
||||
};
|
||||
```
|
||||
|
||||
|
||||
@@ -69,18 +69,26 @@ The code currently provides the following functions:
|
||||
void igmp_setup(void) __banked;
|
||||
void igmp_enable(void) __banked;
|
||||
void igmp_router_port_set(uint16_t pmask) __banked;
|
||||
void igmp_packet_handler(void) __banked;
|
||||
uint8_t igmp_packet_handler(void) __banked;
|
||||
void igmp_show(void) __banked;
|
||||
```c
|
||||
`igmp_setup()` is called at boot-time and configures flooding of all IP-MC packets by
|
||||
default, as otherwise no IP-MC would be possible in the network.
|
||||
default, as otherwise no IP-MC would be possible in the network. `igmp off` calls it as
|
||||
well; groups learned while IGMP was on stay in the table, but are no longer looked up.
|
||||
|
||||
`igmp_enable()`starts IGMP which cause IGMP packets to be handled by the CPU and forwarding
|
||||
of IP-MC packets to be limited to only subscribed ports.
|
||||
|
||||
`igmp_router_port_set()`configures forwarding ports for IGMP messages.
|
||||
|
||||
`igmp_packet_handler()` implements handling of trapped IGMP packets by the CPU.
|
||||
`igmp_packet_handler()` implements handling of trapped IGMP packets by the CPU. It is called
|
||||
for frames to 01:00:5e:xx:xx:xx and returns 0 when the frame is not a trapped IGMP packet,
|
||||
so that it is passed on to the IP stack. IGMPv1/v2 reports (0x12, 0x16) join the group given
|
||||
in the IGMP header, IGMPv2 leaves (0x17) leave it, IGMPv3 reports use the first group record
|
||||
(type 4 joins, type 3 leaves). Trapped IGMP frames arrive without a VLAN tag.
|
||||
Reports for groups in 224.0.0.0/24 are ignored: the switch floods that block
|
||||
even with IGMP on, and an entry for a group such as 224.0.0.251 (mDNS) would
|
||||
otherwise limit it to the ports that reported it, as RFC 4541 warns against.
|
||||
|
||||
`igmp_show()` prints out the IGMP configuration on the CLI.
|
||||
|
||||
|
||||
|
After Width: | Height: | Size: 75 KiB |
|
After Width: | Height: | Size: 72 KiB |
|
After Width: | Height: | Size: 100 KiB |
@@ -73,13 +73,14 @@ 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.
|
||||
does that: it delivers to the port named in `EXT_CPU_CTRL` (0x6724), which
|
||||
`trap_init()` sets to 9, the CPU port, at startup; with the reset value 0xf it
|
||||
names no port and the trapped frame is dropped. STP traps its address while it
|
||||
runs (see stp.md). Where the action is left at *forward*, the egress can be
|
||||
constrained 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:
|
||||
|
||||
@@ -0,0 +1,155 @@
|
||||
# LACP (Link Aggregation Control Protocol, IEEE 802.3ad)
|
||||
|
||||
A static link aggregation group works only if both ends are configured for the
|
||||
same links, and nothing checks that they are. LACP lets the two ends agree on
|
||||
which links reach the same partner, aggregates only those, and takes a link out
|
||||
of the group when the partner stops answering.
|
||||
|
||||
The switch runs LACP in software on the 8051. LACP frames belong to the Slow
|
||||
Protocols, with destination `01:80:C2:00:00:02` and ethertype `0x8809`; the
|
||||
subtype of LACP frames is 1. The implementation has the Receive, Selection and
|
||||
Mux machines of 802.3ad clause 43 and does not aggregate links whose partners
|
||||
differ. Up to `LACP_NUM_LAGS` (4) groups can run LACP; the others stay static.
|
||||
|
||||
## Getting the frames to the CPU
|
||||
|
||||
While LACP runs, the reserved multicast action for `01:80:C2:00:00:02` is
|
||||
*trap*, the same mechanism STP uses for its BPDUs, see [stp.md](stp.md). A
|
||||
trapped LACPDU has no VLAN tag after the RTL tag, and `TRAP_RX_BODY()` gives the
|
||||
offset of the LACPDU in the received frame. When the last group leaves LACP,
|
||||
the action goes back to *drop*.
|
||||
|
||||
## What has to match across the members
|
||||
|
||||
802.3ad requires that every link in an aggregate be full duplex and run at the
|
||||
same speed. Beyond the standard, anything that changes how a frame is treated
|
||||
has to be identical across members, because the hash picks the member per frame
|
||||
and a difference would make the behaviour depend on that choice:
|
||||
|
||||
* must match: speed and duplex, VLAN membership and pvid, ingress filtering,
|
||||
port isolation
|
||||
* may differ: EEE, LED configuration, counters, cable diagnostics
|
||||
|
||||
None of this is enforced yet — `port_lag_members_set()` writes a member mask and
|
||||
a hash setting and validates nothing. See #377 for the discussion of whether a
|
||||
mismatch should be rejected or applied to the whole group.
|
||||
|
||||
## Protocol timing
|
||||
|
||||
The timer values are in units of the LACP timer, which steps every four ticks
|
||||
of the system clock, 50 times per second:
|
||||
|
||||
| Constant | Value | Time |
|
||||
|----------------------|-------|------|
|
||||
| `LACP_FAST_PERIODIC` | 50 | 1 s |
|
||||
| `LACP_SLOW_PERIODIC` | 1500 | 30 s |
|
||||
| `LACP_SHORT_TIMEOUT` | 200 | 4 s |
|
||||
|
||||
802.3ad specifies a fast periodic time of 1 s with a short timeout of 3 s, and a
|
||||
slow periodic time of 30 s with a long timeout of 90 s. The switch transmits at
|
||||
the rate the partner asks for with its TIMEOUT bit. Its own timeout is always
|
||||
the short one, one second above the standard, so a partner that stops sending
|
||||
while its link stays up leaves the trunk after 4 s.
|
||||
|
||||
The Partner block of a transmitted LACPDU repeats the partner's actor
|
||||
information as it was last received: system priority, system, key, port
|
||||
priority, port and state. A Linux partner sets our SYNC only when this block
|
||||
matches what it sent.
|
||||
|
||||
## LACP API
|
||||
|
||||
```
|
||||
void lacp_init(void) __banked;
|
||||
void lacp_off(void) __banked;
|
||||
void lacp_lag_set(uint8_t lag, __xdata uint16_t ports) __banked;
|
||||
void lacp_in(void) __banked;
|
||||
void lacp_timers(void) __banked;
|
||||
void lacp_show(void) __banked;
|
||||
```
|
||||
|
||||
`lacp_init()` has to run before the startup configuration replays, because xdata
|
||||
is not zeroed and the port-to-LAG map uses `0xff` as "no LAG"; a stray zero would
|
||||
make a port look like it belongs to LAG 0.
|
||||
|
||||
## LACP configuration on the serial console
|
||||
|
||||
A group is put under LACP by adding `lacp` to the `lag` command:
|
||||
|
||||
```
|
||||
lag 1 lacp 7 8 # ports 7 and 8 become LACP candidates of group 1
|
||||
lag 1 lacp off # group 1 leaves LACP, its trunk is cleared (so does "lag 1 lacp")
|
||||
lag 1 d # group 1 disabled, whether it ran LACP or not
|
||||
lag 1 7 8 # plain static aggregation, no protocol
|
||||
```
|
||||
|
||||
A group put under LACP gives up the static trunk it had: the hardware trunk is
|
||||
cleared and rebuilt from the ports that converge. The other way round, `lag 1 d`
|
||||
and a static member list take the group out of LACP before they touch the
|
||||
trunk. A port named in another LACP group moves to the new one. The protocol
|
||||
starts with the first group put under LACP and stops when the last one is taken
|
||||
out; there is no separate switch for it. Two commands act on the whole engine:
|
||||
|
||||
```
|
||||
lacp off # every group leaves LACP, protocol stopped
|
||||
lacp show
|
||||
```
|
||||
|
||||
`lacp show` prints the engine state, then one line per group with its candidate
|
||||
ports, the elected aggregator and the trunk members actually programmed into the
|
||||
hardware, then one line per port with the actor and partner state bytes and the
|
||||
receive-machine state.
|
||||
|
||||
## LACP configuration via the Web Interface
|
||||
|
||||
Each group on the LAG page has a Static/LACP mode next to its member ports;
|
||||
for an LACP group it shows the elected aggregator and the active members. Below
|
||||
the groups a table has one row per LACP port with its group, actor and partner
|
||||
state, receive state, LACPDU count and partner system. The page reads
|
||||
`/lacp.json` every three seconds while it is open. The member checkboxes of a
|
||||
static group come from the hardware registers in `/lag.json`.
|
||||
|
||||
## A test with a Linux 802.3ad bond
|
||||
|
||||
Two ports of the switch to two interfaces of one host:
|
||||
|
||||
```
|
||||
ip link add bond0 type bond mode 802.3ad
|
||||
ip link set enp1s0 down && ip link set enp1s0 master bond0
|
||||
ip link set enp2s0 down && ip link set enp2s0 master bond0
|
||||
ip link set bond0 up
|
||||
```
|
||||
|
||||
On the switch, with those two ports as the group:
|
||||
|
||||
```
|
||||
lag 1 lacp 7 8
|
||||
```
|
||||
|
||||
`lacp show` should then give both ports an actor state of `0x3f` (ACTIVITY,
|
||||
TIMEOUT, AGGREGATION, SYNC, COLLECTING and DISTRIBUTING), a partner state with
|
||||
SYNC set, and the partner's system MAC. `lag show` lists the trunk members as
|
||||
programmed in the hardware and should show both ports.
|
||||
|
||||
On the host, `/proc/net/bonding/bond0` should show the same `Aggregator ID` for
|
||||
both slaves; different IDs mean that each link is in a group of its own. Both
|
||||
churn states should read `none`.
|
||||
|
||||
Further checks:
|
||||
|
||||
* `tcpdump -i enp1s0 ether proto 0x8809` on each slave shows the LACPDUs of the
|
||||
switch and of that slave, never those of the other slave.
|
||||
* The host log has no `illegal loopback`.
|
||||
* The three reserved bytes after the actor state in a LACPDU from the switch
|
||||
are zero.
|
||||
|
||||
When one slave goes down, the group narrows to the remaining member, and the
|
||||
slave rejoins after it comes back.
|
||||
|
||||
## Known limits
|
||||
|
||||
* No Churn Detection machines and no Marker protocol.
|
||||
* Coupled Mux: collecting and distributing are enabled together.
|
||||
* One aggregator per group.
|
||||
* Selection compares the partner's system only, not its key: two aggregates
|
||||
that one partner system offers on the same group land in one trunk here.
|
||||
* Member compatibility is not validated; see #377.
|
||||
@@ -227,3 +227,7 @@ iperf Done.
|
||||
```
|
||||
As you can see, the total throughput was 4.71 GBit/sec which is close to the
|
||||
maximum possible with a single 5GBit link.
|
||||
|
||||
## LACP
|
||||
|
||||
Dynamic aggregation with LACP is described in [lacp.md](lacp.md).
|
||||
@@ -0,0 +1,33 @@
|
||||
# Time
|
||||
|
||||
The switch has no battery backed clock. It counts seconds from power on, and
|
||||
it can take the date and time from an NTP server once the network is up. The
|
||||
time is lost at every restart and set again at the first synchronisation.
|
||||
|
||||
```
|
||||
ntp server <name|ip> # default pool.ntp.org
|
||||
ntp interval <1-1440> # minutes between two synchronisations, default 60
|
||||
ntp timezone <+hh:mm> # offset from UTC, -12:00 to +14:00
|
||||
ntp dst off|eu|us # daylight saving rule
|
||||
ntp on
|
||||
ntp off
|
||||
ntp # settings and time
|
||||
time # local date and time
|
||||
```
|
||||
|
||||
The client speaks SNTP version 4 and synchronises at once when turned on,
|
||||
then after every interval. A server given by name is resolved through the DNS
|
||||
resolver before every synchronisation. An answer from an unsynchronised
|
||||
server is ignored, and a failed attempt is repeated after 30 seconds.
|
||||
|
||||
The local time is UTC plus the time zone offset, plus one hour while daylight
|
||||
saving time applies:
|
||||
|
||||
* `eu`: from 01:00 UTC on the last Sunday of March to 01:00 UTC on the last
|
||||
Sunday of October;
|
||||
* `us`: from 02:00 local time on the second Sunday of March to 02:00 local
|
||||
time on the first Sunday of November.
|
||||
|
||||
The same settings, with the current local time and the last synchronisation,
|
||||
are in the Time card of the System page. Settings made there or on the
|
||||
console are kept only once the configuration is saved.
|
||||
@@ -0,0 +1,54 @@
|
||||
# Storm Control
|
||||
Storm control limits the rate at which a port admits broadcast, multicast,
|
||||
unknown unicast and unknown multicast frames. Frames of a type above its limit
|
||||
are dropped at ingress, so a loop or a misbehaving host cannot flood the rest
|
||||
of the network through that port. Known unicast traffic is not affected.
|
||||
|
||||
## Commands
|
||||
```
|
||||
storm <port> bcast|mcast|ucast|umcast <rate> pps
|
||||
storm <port> bcast|mcast|ucast|umcast <rate> kbps
|
||||
storm <port> bcast|mcast|ucast|umcast off
|
||||
storm
|
||||
```
|
||||
`ucast` is unicast to a MAC address the switch has not learned yet, and
|
||||
`umcast` is multicast without an entry in the address table. Multicast groups
|
||||
get their entries from IGMP snooping, so without `igmp on` all multicast is
|
||||
unknown and only the `umcast` limit applies to it; `mcast` covers the groups
|
||||
IGMP has learned. A limit in
|
||||
packets per second goes from 1 to 1048575, a limit in kbit/s from 1 to
|
||||
10000000. `storm` without arguments prints the limits of every port. The
|
||||
limits are kept in the configuration like any other setting, and the Bandwidth
|
||||
page of the web interface has a Storm control card to set them.
|
||||
|
||||
## Registers
|
||||
Each port and type has an enable bit and a meter index, and each limit uses
|
||||
one of the 64 shared meters of the switch. RTLPlayground gives every port and
|
||||
type its own meter, index `24 + port * 4 + type`, so one port's storm does not
|
||||
eat into the limit of another. Storm control uses meters 24 to 63 and leaves
|
||||
0 to 23 to the ACL. The limit of a meter is in `RTL837X_METER_RATE + 4 *
|
||||
meter`, and bit `meter % 32` of `RTL837X_METER_MODE + 4 * (meter / 32)`
|
||||
selects pps instead of kbit/s.
|
||||
```
|
||||
#define RTL837X_STORM_CTRL 0x54e4 /* + 4 * type, bit = port */
|
||||
#define RTL837X_STORM_MIDX 0x54f4 /* + 8 * type + 4 * (port / 5), 6 bits per port */
|
||||
#define RTL837X_METER_RATE 0x5cf0 /* + 4 * meter, 24 bits, kbit/s or pps */
|
||||
#define RTL837X_METER_MODE 0x5ef0 /* + 4 * (meter / 32), bit set = pps */
|
||||
```
|
||||
In kbit/s mode one step of the rate is 1 kbit/s. Measured with 100 and 1000 byte
|
||||
frames, the meter counts a frame with its FCS, but without preamble and
|
||||
interframe gap. Every meter has a bit that adds them, in `0x5f08 + 4 * (meter
|
||||
/ 32)`, which storm control leaves cleared. `IGBW_INC_IFG` and `EGBW_INC_IFG`
|
||||
are the matching bits of the bandwidth control.
|
||||
|
||||
An ingress bandwidth limit on the same port works on the same arriving frames
|
||||
as the storm limit rather than after it, and the two together pass far less
|
||||
than either one: a 1 Mbit/s `umcast` limit with a 10 Mbit/s ingress limit let
|
||||
0.12 to 0.16 Mbit/s of a 1.4 Gbit/s stream through, where each alone passed
|
||||
1.17 and 10.2 Mbit/s.
|
||||
|
||||
The type is 0 for broadcast, 1 for multicast, 2 for unknown unicast and 3 for
|
||||
unknown multicast. The burst size of a meter (0x5df0 + 4 * meter) counts packets
|
||||
in pps mode; its default of 0x2000 lets 8192 frames through before a pps limit
|
||||
bites, so a pps limit sets the burst to one second of traffic. A kbit/s limit
|
||||
puts back the default.
|
||||
@@ -2,9 +2,11 @@
|
||||
|
||||
The switch can take part in a spanning tree (IEEE 802.1D / 802.1w) so that
|
||||
redundant links between bridges are blocked instead of forming a loop. The
|
||||
implementation elects a root bridge from the BPDUs it receives, promotes ports
|
||||
to forwarding once their listen period expires, ages the root out when it goes
|
||||
silent, and blocks a port on which it sees its own BPDU.
|
||||
implementation elects a root bridge from the BPDUs it receives, picks the
|
||||
cheapest path to it as the root port, blocks ports on which a better bridge is
|
||||
already designated, promotes ports to forwarding once their listen period
|
||||
expires, drops information a neighbour stopped sending, and blocks a port on
|
||||
which it sees its own BPDU.
|
||||
|
||||
STP can be enabled and controlled via the web interface or the command line,
|
||||
as follows:
|
||||
@@ -35,54 +37,34 @@ 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.
|
||||
While STP runs, the action is *trap*. A trap delivers to the port named in
|
||||
`EXT_CPU_CTRL` (0x6724), whose reset value 0xf names no port, and with that
|
||||
value the trapped frame is dropped at ingress. `trap_init()` sets it to 9, the
|
||||
port the 8051 sits behind, once at startup. The trap needs no per-VLAN L2
|
||||
entries, so it does not depend on which VLANs exist or on the PVIDs, and turning
|
||||
STP on no longer walks the whole VLAN table. BPDUs reach the CPU on a port that
|
||||
STP holds in blocking with either action: measured on a SWTGW218AS by sending
|
||||
BPDUs into a blocked port with forward and with trap, both ports stopped being
|
||||
edge and waited out the forward delay. The chip exempts this address from the
|
||||
ingress state check; the slow-protocols address used by LACP is not exempt.
|
||||
|
||||
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:
|
||||
A trapped frame reaches the CPU in the format it came in with: unlike a
|
||||
forwarded one it has no VLAN tag inserted after the RTL tag, and the RTL tag
|
||||
carries the trap reason. `stp_in()` reads the BPDU from the offset
|
||||
`TRAP_RX_BODY()` returns, which works for both.
|
||||
|
||||
* 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.
|
||||
With STP off, the action goes back to *forward* and a static L2 multicast
|
||||
entry per VLAN in use (`port_l2mc_set()`) targets 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.
|
||||
|
||||
## Link aggregation
|
||||
|
||||
A LAG (Link Aggregation Group) is handled like an additional port by the STP
|
||||
protocol, with its own timers and states. The switch devices support up to 4
|
||||
LAGs, which are shown alongside the physical ports in the STP status. A group
|
||||
carries its own path cost, priority, edge and guard settings, and gets its own
|
||||
row on the Spanning Tree page.
|
||||
|
||||
```
|
||||
stp lag 1 cost 10000 # the group decides, not its members
|
||||
stp lag 1 edge off
|
||||
```
|
||||
|
||||
LAGs are configured via the `lag` command. Once a port is a member of a LAG it
|
||||
can no longer be configured individually for STP, so `stp port <n>` on a member
|
||||
tells you which group to configure instead. Membership is re-read from the
|
||||
aggregation registers once a second, so a group changing under LACP is picked
|
||||
up without any coordination between the two.
|
||||
|
||||
The switch hardware does not handle STP state for a LAG as a whole. State
|
||||
changes for the members have to be done by the firmware, updating every member
|
||||
port, which is possible using a single register write. BPDUs go out through the
|
||||
lowest member and carry the group's own port id. Losing one member of a live
|
||||
LAG is not a topology change; the logical port only goes down with its last
|
||||
link.
|
||||
The port's acceptable-frame-type filter treats the two actions differently.
|
||||
BPDUs are untagged, so a port set to admit tagged frames only
|
||||
(`ingress <port>t`) drops a forwarded BPDU before it reaches the CPU, while a
|
||||
trapped one passes. Both were measured on a SWTGW218AS with `ingress 3t`:
|
||||
LACPDUs, delivered by forward, stopped arriving on port 3, and BPDUs sent into
|
||||
the same port, delivered by trap, kept arriving. A port that admits tagged
|
||||
frames only therefore takes part in STP.
|
||||
|
||||
Port states live in `RTL837X_MSTP_STATES (0x5310)`, two bits per port:
|
||||
`00` disabled, `01` blocking, `10` learning, `11` forwarding. A port in
|
||||
@@ -103,19 +85,83 @@ encodes. All configured values are in seconds:
|
||||
| `stp fwd <n>` | 15 | 4–30 |
|
||||
| `stp txhold <n>` | 6 | 1–10 |
|
||||
|
||||
A port entering the tree spends `fwd` seconds in blocking before it forwards
|
||||
(an edge port skips the wait). Root information is discarded after `maxage`
|
||||
seconds without a BPDU, and the switch then reclaims the root role.
|
||||
802.1D also requires `2 * (fwd - 1) >= maxage >= 2 * (hello + 1)`. A
|
||||
command typed on the console or sent by the web page that would break it is
|
||||
refused and the value stays as it was, so change the values in an order that
|
||||
keeps the rule at every step (for a larger max age raise the forward delay
|
||||
first). The web page shows the error, and a refused command is not recorded
|
||||
in the command history, so saving the configuration does not pick it up. Lines of the saved configuration are taken one by one whatever their
|
||||
order, and `stp on` checks the result: if the rule is broken it warns and
|
||||
starts with 2/20/15 instead.
|
||||
|
||||
The values above are what the switch uses while it is the root. Otherwise it
|
||||
takes max age and forward delay from the BPDUs on its root port, as 802.1D
|
||||
requires, and passes them on in its own BPDUs with the message age one second
|
||||
higher; the hello time stays its own.
|
||||
|
||||
A port entering the tree discards for one forward delay, then learns
|
||||
addresses without forwarding for a second one, and only then forwards (an
|
||||
edge port skips the wait). In RSTP mode on a point-to-point link
|
||||
(`p2p auto` or `on`) it can go faster: a designated port that is still
|
||||
discarding sets the proposal flag, and forwards as soon as the bridge on the
|
||||
other side answers with an agreement. When the root port receives a proposal,
|
||||
the switch first puts its other non-edge designated ports back to discarding
|
||||
(they propose in turn), then forwards the root port and answers with an
|
||||
agreement; this sync happens once per root port, later proposals are only
|
||||
answered. An alternate or backup port that receives a proposal does the same
|
||||
sync once for the information it holds and answers with an agreement in a
|
||||
BPDU with the alternate role, so the bridge on the other side does not have
|
||||
to wait out its timers; it stays discarding itself. An agreement is accepted
|
||||
from a root or an alternate port on the other side. A new root port forwards at once, after every port that was root within
|
||||
the last forward delay has been put back to discarding; such a recent root
|
||||
port stays closed, even to an agreement, until that forward delay is over.
|
||||
A new root port that was a backup port within the last two hello times does
|
||||
not take the fast path and waits for its timers.
|
||||
|
||||
A designated port that hears worse information from a bridge that still
|
||||
claims to be designated and learning or forwarding on the same link has a
|
||||
dispute: usually one direction of the link is lost and the other bridge does
|
||||
not hear this switch's BPDUs. The port goes back to discarding, and every
|
||||
further such BPDU restarts its listen period, so it does not forward while
|
||||
the dispute lasts.
|
||||
|
||||
With `stp version rstp` each port still talks 802.1D to a neighbour that
|
||||
does: once a Config BPDU or TCN arrives and the migrate time (3 s since the
|
||||
port came up or last switched) is over, the port sends Config BPDUs, uses
|
||||
the 802.1D TC window and reports changes with TCN if it is the root port. An
|
||||
RST BPDU after the migrate time switches it back, and so does a link that
|
||||
comes up again or `stp port <n> mcheck`. Any worse information heard on a designated port, disputed or
|
||||
not, is answered with a BPDU at once instead of at the next hello, so the
|
||||
other bridge learns sooner that it is not the designated one. Information heard on a port stays
|
||||
valid for three of the sender's hello times; a BPDU whose message age has
|
||||
reached its max age is not used at all. When the root port's information
|
||||
expires the switch moves to the next best port, or reclaims the root role if
|
||||
there is none.
|
||||
|
||||
Among ports offering the same root, the lowest root path cost wins, then the
|
||||
designated bridge, the designated port ID, and finally this switch's own port
|
||||
ID. A worse BPDU from a different bridge does not replace what a port holds;
|
||||
a worse one from the same designated bridge and port does.
|
||||
The number in a port ID is the one on the front panel, and 101 and up for
|
||||
the LAGs, so it matches the port labels on both ends of a link.
|
||||
|
||||
## Topology changes
|
||||
|
||||
A change on a local non-edge port (the link coming or going, a port promoted
|
||||
to forwarding) flushes the addresses learned on it and sets the TC flag in
|
||||
our BPDUs for `maxage + fwd` seconds. A TC flag received in a BPDU is passed
|
||||
on: the switch flushes the other non-edge ports once and keeps the flag in
|
||||
its own BPDUs until one hello after the last flagged frame, so the
|
||||
notification crosses the switch instead of dying at it. A legacy TCN is
|
||||
acknowledged with TCA and then treated like a local change.
|
||||
A topology change is detected when a non-edge port enters forwarding. That
|
||||
port, and every other forwarding non-edge port, then carries the TC flag in
|
||||
its BPDUs for hello time plus one second (max age plus forward delay with
|
||||
`stp version stp`), and each of the other ports flushes the addresses it
|
||||
learned. The root port takes part too: while its TC window runs it sends
|
||||
BPDUs toward the root, so the change reaches the rest of the network. With
|
||||
`stp version stp` the root port sends TCN instead, until a Config BPDU with
|
||||
TCA comes back.
|
||||
|
||||
A TC flag received on a forwarding root or designated port is passed on the
|
||||
same way to the other ports, not back to the port it came in on; a port that
|
||||
is still discarding or learning drops it. A legacy TCN is acknowledged with
|
||||
TCA and then treated like a received TC. A port that turns
|
||||
alternate, loses its link, or is blocked for a loop flushes its own addresses
|
||||
without raising a topology change, and an edge port never raises one.
|
||||
|
||||
## Bridge settings
|
||||
|
||||
@@ -123,6 +169,8 @@ acknowledged with TCA and then treated like a local change.
|
||||
stp prio <0-15> # bridge priority = n * 4096, default 8 (32768)
|
||||
stp version rstp|stp # RST BPDUs (default) or legacy Config BPDUs
|
||||
stp hello|maxage|fwd|txhold <seconds>
|
||||
stp pathcost long|short # automatic port costs, 802.1D-2004 (default) or 802.1D-1998
|
||||
stp bpdu flood|filter # BPDUs while STP is off: pass them on (default) or drop them
|
||||
```
|
||||
|
||||
The bridge with the lowest priority wins the root election; ties are broken by
|
||||
@@ -130,6 +178,17 @@ the MAC address. If you do not want this switch to become the root of an
|
||||
existing network, give it a worse priority than the current root — `stp prio 15`
|
||||
(61440) is the usual "never me" value.
|
||||
|
||||
A port without a configured cost takes it from its link speed. The long
|
||||
method uses the 802.1D-2004 values (20000 for 1G, 2000 for 10G); the short
|
||||
one the 802.1D-1998 table for bridges that still use 16-bit costs (100 for
|
||||
10M, 19 for 100M, 4 for 1G, 2 for 10G, and 3 for 2.5G and 5G, which that
|
||||
table does not list). All bridges in a network should use the same method.
|
||||
|
||||
`stp bpdu filter` keeps BPDUs from being passed between ports while STP is
|
||||
off, instead of the default flooding that lets a surrounding spanning tree
|
||||
run through the switch. It takes effect at once when STP is off, otherwise
|
||||
when STP is turned off.
|
||||
|
||||
## Per-port settings
|
||||
|
||||
```
|
||||
@@ -140,6 +199,7 @@ stp port <1-9> prio <0-240> # port priority, steps of 16
|
||||
stp port <1-9> guard none|bpdu|root
|
||||
stp port <1-9> filter on|off # neither send nor accept BPDUs
|
||||
stp port <1-9> p2p auto|on|off
|
||||
stp port <1-9> mcheck # try RSTP again on a port that fell back to STP
|
||||
```
|
||||
|
||||
**edge** — an edge port forwards immediately and does not trigger a
|
||||
@@ -156,26 +216,76 @@ claims a better priority.
|
||||
on the far side reacts badly to them (some unmanaged switches with loop
|
||||
prevention cut the link) but you still want STP on the rest of the ports.
|
||||
|
||||
## Link aggregation
|
||||
|
||||
A LAG (Link Aggregation Group) is one port of the tree, with its own roles,
|
||||
timers and states. The switch supports up to 4 LAGs; each one takes the place
|
||||
of its member ports in the STP status and gets its own row on the Spanning
|
||||
Tree page. A group carries its own path cost, priority, edge, guard and
|
||||
point-to-point settings, and takes the same commands as a port:
|
||||
|
||||
```
|
||||
stp lag <1-4> on|off|mcheck|edge|cost|prio|guard|filter|p2p ...
|
||||
stp lag 1 cost 10000 # the group decides, not its members
|
||||
```
|
||||
|
||||
LAGs are configured with the `lag` command. Once a port is a member of a LAG
|
||||
it can no longer be configured on its own for STP, and `stp port <n>` on a
|
||||
member names the group to configure instead. Membership is read from the
|
||||
aggregation registers once a second, so a group changing under LACP is picked
|
||||
up without any coordination between the two; a port that joins or leaves a
|
||||
group, and the group itself, start over from discarding. The automatic path
|
||||
cost of a group is the one of its lowest member that has a link.
|
||||
|
||||
The hardware keeps no STP state for a LAG as a whole, so every state change is
|
||||
written to all member ports, in a single register write. BPDUs go out through
|
||||
the lowest member that has a link and carry the group's own port id, and a
|
||||
BPDU received on any member belongs to the group. Losing one member of a live
|
||||
LAG is not a topology change; the group only goes down with its last link.
|
||||
|
||||
## Status
|
||||
|
||||
The Spanning Tree page shows the elected root (priority and MAC), the path cost
|
||||
to it, the root port, the topology-change counter and, per port, the live state
|
||||
read from the ASIC together with the configured options. The same data is
|
||||
available as JSON:
|
||||
to it, the root port, the topology-change counter and the time since it last
|
||||
moved, the max age and forward
|
||||
delay in use (the root's, when this switch is not the root) and, per port, the
|
||||
live state read from the ASIC, or "No link", together with the configured
|
||||
options. For a port taking part in the tree it also shows the designated
|
||||
bridge, port and cost, and whether it talks RSTP or 802.1D to its neighbour;
|
||||
the Check button runs `stp port <n> mcheck`. The same data is available as
|
||||
JSON:
|
||||
|
||||
```
|
||||
GET /stp.json
|
||||
```
|
||||
|
||||
The `stp status` command prints the same view on the serial console.
|
||||
The Counters section of the same page (or `/stpcnt.json`) shows per port the
|
||||
BPDUs received and sent, those of them that carried the topology change flag
|
||||
(TCNs included), and the time since the last BPDU arrived. The counters start
|
||||
from zero with `stp on`, and `stp clear` resets them; the topology change
|
||||
counter is not reset.
|
||||
|
||||
The `stp status` command prints the same view on the serial console. A port
|
||||
is reported with one of the roles Root, Designated, Alternate, Backup or
|
||||
Disabled; Disabled covers a port with STP turned off on it, one shut down by
|
||||
BPDU guard and one without link.
|
||||
|
||||
The console's `age[s]` column (previously labelled `bpdu`) is the elapsed
|
||||
time since the last BPDU, capped at 255 seconds; it is not a receive count.
|
||||
Use the web counters or `/stpcnt.json` for full receive counts.
|
||||
|
||||
An own BPDU received back on another port can temporarily hold its sending
|
||||
port in discarding, even if that sending port has received no BPDUs itself.
|
||||
When the hold expires, auto-edge is restored if the sending port has remained
|
||||
silent. A continuing loop keeps the hold active.
|
||||
|
||||
BPDUs shorter than their type allows, judged by the 802.3 length field, are
|
||||
dropped: 7 bytes for a TCN, 38 for a Config BPDU and 39 for an RST BPDU,
|
||||
LLC header included.
|
||||
|
||||
## Limitations
|
||||
|
||||
* One spanning-tree instance; no MSTP, no per-VLAN trees.
|
||||
* No proposal/agreement handshake — an RST-capable neighbour will still
|
||||
converge, but through the timers rather than the fast transition.
|
||||
* Port roles are approximated: the root port and designated ports are
|
||||
distinguished, alternate/backup are not.
|
||||
* Topology changes propagate away from the root only: nothing is announced
|
||||
on the root port (no TCN and no BPDUs at all), so bridges upstream rely on
|
||||
their own detection.
|
||||
* A backup port, one that hears the BPDUs of another port of this switch,
|
||||
is blocked and reported as Backup, but keeps sending BPDUs: loop detection
|
||||
on that segment relies on them.
|
||||
@@ -4,39 +4,58 @@ The following devices have been tested and are fully working:
|
||||
| Brand | Type | Managed | PCB | Flash | Ports |
|
||||
|----------|-----------------|---------|---------------------------------------------------------------------------|-------|-------|
|
||||
| Ampcom | WAM902-SWTG018AS| No | [SWTG018AS-A V2.0](devices/SWTG018AS_A_V2_0.md) | | 8 + 1 |
|
||||
| Binardat | 2G06-04210GSM | No | [PCB-SWTG024AS-A-2.0.1](devices/ONT-S207CW-62TS-SE.md) | 0.5M | 4 + 2 |
|
||||
| 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 |
|
||||
| Goalake | IG108 | No | [PB-2132](devices/PB-2132.md) | 4M | 8 + 1 |
|
||||
| Hisource | Hi-K0402WS | No | [PCB-K0402WS-V3.0](devices/PCB-K0402WS-V3.0.md) | | 4 + 2 |
|
||||
| GoodTop | GT-ST018M | Yes | [SWTG018AS-A V2.0](devices/SWTG018AS_A_V2_0.md) | 2M | 8 + 1 |
|
||||
| GoodTop | GT-ST024M | Yes | [SWTG024AS-A V2.0.1](devices/SWTG024AS-A-V2.0.1_4C_2SFP.md) | 2M | 4 + 2 |
|
||||
| Guangnianwei | 7018-2.5G | No | [F7008-2.5GPOE-V1.2](devices/F7008-2.5.md) | 8M | 8 + 1 |
|
||||
| Hasivo | S1100WP_8GT_1SX_SE | Yes | [S1100WP_8GT_1SX_SE-V1.05](devices/HASIVO_S1100WP_8GT_1SX_SE.md) | 2M | 8 + 1 |
|
||||
| Hisource | Hi-K0402WS | No | [PCB-K0402WS-V2.0](devices/PCB-K0402WS-V2.0.md) | 2M | 4 + 2 |
|
||||
| Hisource | Hi-K0402WS | No | [PCB-K0402WS-V3.0](devices/PCB-K0402WS-V3.0.md) | | 4 + 2 |
|
||||
| Hisource | Hi-K0801WS | No | [PCB-KO801W-V2.0](devices/HI-K0801WS.md) | | 8 + 1 |
|
||||
| hongyavision | HYWS-SGT0204S | No | [2G5F4_10G2_V1.01](devices/2G5F4_10G2_V1.01.md) | 0.5M | 4 + 2 |
|
||||
| hongyavision | LG-SG5T1 | No | [PCB-SWTG024AS-V2.0_16895](devices/SWTG024AS-V2.0.md) | 0.5M | 5 + 1 |
|
||||
| Horaco | HC-SWTG124AS | Yes | [SWTG024AS-A-V2.0.1](devices/SWTG024AS-A-V2.0.1_4C_2SFP.md) | 2M | 4 + 2 |
|
||||
| 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 | HC-SWTGW218AS | Yes | [SWTG118AS-V2.1](devices/SWTGW218AS.md) | 2M | 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-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 |
|
||||
| keepLINK | KP-9000-9XHML-X | Yes | [2M-PCB23-V3.2](devices/2M-PCB23-V3_2.md) | 2M | 8 + 1 |
|
||||
| keepLINK | KP-9000-9XHP-X | No | [2M-PCB51-ML-V1.1](devices/2M-PCB51-ML-V1_1.md) | 4M | 8 + 1 |
|
||||
| LIANGUO | HYWS-SGT0108S | No | [2G5F_20G_V1.01](devices/HYWS-SGT0108S.md) | 0.5M | 8 + 1 |
|
||||
| LIANGUO | SWTG024AS | No | [SWTG024AS-v2.0-17452](devices/SWTG024AS.md) | 0.5M | 4 + 2 |
|
||||
| Lianguo | ZX-SWTGW215AS | Yes | [PCB-SWTG115AS-V2.0 / PCB-SWTG115AS-V2.1](devices/SWTGW215AS.md) | 2M | 5 + 1 |
|
||||
| Lianguo | HYWS-SGT0108S | No | [2G5F_20G_V1.01](devices/HYWS-SGT0108S.md) | 0.5M | 8 + 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 |
|
||||
| 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| POE-2G080110GS | No | [2M-PCB51-ML-V1.1](devices/2M-PCB51-ML-V1_1.md) | 4M | 8 + 1 |
|
||||
| MokerLink| ZX-SWTGW218AS | Yes | [SWTG118AS-V2.0-16029](devices/SWTGW218AS.md) | 2M | 8 + 1 |
|
||||
| MokerLink| 2G08110GSN | No | [SWTG018AS-A V2.0](devices/SWTG018AS_A_V2_0.md) | 0.5M | 8 + 1 |
|
||||
| Netcore | GS9 | No | [PB-2132](devices/PB-2132.md) | 4M | 8 + 1 |
|
||||
| ONT | ONT-S207CW-62TS-SE | No | [PCB-SWTG024AS-A-2.0.1](devices/ONT-S207CW-62TS-SE.md) | 0.5M | 4 + 2 |
|
||||
| RealHD | PSW8-25MG | Yes | [PCB-AFGW SWTG218ANS V2.0.2](devices/PCB-AFGW_SWTG218ANS_V2.0.2.md) | 2M | 8 + 1 |
|
||||
| Ruiying | RY-4GT-2SX | No | [FG-4GT-2SX_V2.0](devices/FG-4GT-2SX_V2.0.md) | 4M | 4 + 2 |
|
||||
| Ruiying | RY-8GT-1SX | No | [FG-8GT-1SX](devices/FG-8GT-1SX.md) | 1M | 8 + 1 |
|
||||
| SiscoLink | SL-G0402F | No | [PCB-K0402WS-V3.0](devices/PCB-K0402WS-V3.0.md) | | 4 + 2 |
|
||||
| SICSOLINK| SL-G0402F | No | [PCB-K0402WS-V3.0](devices/PCB-K0402WS-V3.0.md) | | 4 + 2 |
|
||||
| Sirivision | SR-S25G2109F | Yes | [S25G1109FP V1.3](devices/S25G1109FP_V1_3.md) | 2M | 8 + 1 |
|
||||
| Sodola | SL-SWTG124AS-D | Yes | [SWTG024AS-v2.0-17452](devices/SWTG024AS.md) | 2M | 4 + 2 |
|
||||
| Sodola | SL902-SWTG024AS | Yes | [SWTG024AS-v2.1.0](devices/SWTG024AS-V2.1.0.md) | 2M | 4 + 2 |
|
||||
| Steamemo | IG204-V1 | No | [PB-2131](devices/STEAMEMO_IG204_V1.md) | | 4 + 2 |
|
||||
| TrendNet | TEG-S562 | No | [TEG-S563/EU H/W: V1.0R](devices/TEG-S562.md) | 2M | 4 + 2 |
|
||||
| Xikestore| SKS3200M-4GPY2XF| Yes | [SWTG024AS-v1.0](devices/SWTG024AS.md) | | 4 + 2 |
|
||||
| TRENDnet | TEG-S562 | No | [TEG-S563/EU H/W: V1.0R](devices/TEG-S562.md) | 2M | 4 + 2 |
|
||||
| XikeStor | SKS3200M-4GPY2XF| Yes | [SWTG024AS-v1.0](devices/SWTG024AS.md) | | 4 + 2 |
|
||||
| XikeStor | SKS3200-8E1X | Yes | [SWTG118AS-V2.1-17462](devices/SWTGW218AS.md) | 2M | 8 + 1 |
|
||||
| XikeStor | SKS3200-4E2X | Yes | [SWTG024AS-v2.1.0](devices/SWTG024AS-V2.1.0.md) | 2M | 4 + 2 |
|
||||
| YuanLey | YS25-0402 | No | [PCB-K0402WS-V1.0](devices/PCB-K0402WS-V1.0.md) | 2M | 4 + 2 |
|
||||
| YuanLey | YS25-0402 | No | [PCB-K0402WS-V3.0](devices/PCB-K0402WS-V3.0.md) | 2M | 4 + 2 |
|
||||
| YuanLey | YS25-0402M | Yes | [SIRIVISION_S25G2206FP_V1_1](devices/SIRIVISION_S25G2206FP_V1_1.md) | 2M | 4 + 2 |
|
||||
| Ztyuav | Z-QWYT0402 | No | [PCB-K0402WS-V3.0](devices/PCB-K0402WS-V3.0.md) | | 4 + 2 |
|
||||
| Guangnianwei | 7018-2.5G | No | [F7008-2.5GPOE-V1.2](devices/F7008-2.5.md) | 8M | 8 + 1 |
|
||||
| ZoeRax | HG0402XG | No | [HG0402XG_V1.1](devices/ZOERAX_HG0402XG_V1.1.md) | 1M | 4 + 2 |
|
||||
|
||||
For KP-9000-6XH-X2 / KP-9000-6XHML-X2 / Mokerlink 2G040210GSM devices, select
|
||||
the machine target by PCB revision. The ML/non-ML or managed/unmanaged label
|
||||
|
||||
@@ -79,8 +79,11 @@ JSON polling of the visible section).
|
||||
computation no longer depends on uninitialised buffer content.
|
||||
- `version.h` is now written by a single atomic `printf` (change from
|
||||
upstream) instead of five separate `echo`s, so a parallel build
|
||||
(`make -j`) can no longer read a half-written copy. It stays in
|
||||
`.PHONY` so it is regenerated on every build.
|
||||
(`make -j`) can no longer read a half-written copy. It depends on
|
||||
`FORCE`, so its recipe runs on every build, but the file is only
|
||||
rewritten when its content changes. The minified copy in
|
||||
`output/html_min/` is redone only when a web file, the list of web
|
||||
files or `tools/minify.py` changes.
|
||||
|
||||
## Verification
|
||||
|
||||
|
||||
@@ -33,7 +33,7 @@
|
||||
html,body{height:100%}
|
||||
body{
|
||||
font:14px/1.45 system-ui,-apple-system,"Segoe UI",Roboto,sans-serif;
|
||||
background:var(--bg);color:var(--fg);display:flex;
|
||||
background:var(--bg);color:var(--fg);display:flex;overflow:hidden;
|
||||
}
|
||||
button,input,select,textarea{font:inherit;color:var(--fg)}
|
||||
a{color:var(--ac);text-decoration:none}
|
||||
@@ -55,11 +55,11 @@ a{color:var(--ac);text-decoration:none}
|
||||
#navfoot{padding:10px 16px;border-top:1px solid var(--bd);font-size:12px;color:var(--fg2)}
|
||||
#wrap{flex:1;min-width:0;display:flex;flex-direction:column}
|
||||
#top{
|
||||
display:flex;align-items:center;gap:10px;padding:10px 20px;
|
||||
display:flex;align-items:center;flex-wrap:wrap;gap:10px;padding:8px 20px;
|
||||
background:var(--panel);border-bottom:1px solid var(--bd);position:sticky;top:0;z-index:5;
|
||||
}
|
||||
#burger{display:none;background:none;border:0;cursor:pointer;padding:4px}
|
||||
#ttitle{font-size:16px;font-weight:650;flex:1}
|
||||
#ttitle{font-size:16px;font-weight:650;min-width:120px}
|
||||
#dirty{
|
||||
display:none;font-size:12px;font-weight:600;color:var(--warn);
|
||||
border:1px solid var(--warn);border-radius:20px;padding:2px 10px;
|
||||
@@ -72,8 +72,8 @@ select.ctl,button.ctl{
|
||||
button.pri{background:var(--ac);color:var(--acfg);border-color:transparent;font-weight:600}
|
||||
button.pri:hover,button.ctl:hover{filter:brightness(1.07)}
|
||||
button:disabled{opacity:.45;cursor:default;filter:none}
|
||||
main{padding:18px 20px 40px;max-width:1400px;width:100%}
|
||||
section.tab{display:none}
|
||||
main{padding:18px 20px 40px;width:100%;overflow-y:auto;}
|
||||
section.tab{display:none;max-width:1400px;}
|
||||
section.tab.act{display:block}
|
||||
.card{
|
||||
background:var(--panel);border:1px solid var(--bd);border-radius:10px;
|
||||
@@ -89,9 +89,11 @@ table.t th{
|
||||
padding:6px 8px;border-bottom:1px solid var(--bd);white-space:nowrap;
|
||||
}
|
||||
table.t th[data-sort]{cursor:pointer;user-select:none}
|
||||
table.t th.num{text-align:right}
|
||||
table.t td{padding:6px 8px;border-bottom:1px solid var(--bd);vertical-align:middle}
|
||||
table.t tr:last-child td{border-bottom:0}
|
||||
table.t tr:hover td{background:var(--sel)}
|
||||
table.t tr.sel{background:var(--panel2); font-weight: bold;}
|
||||
.num{font-variant-numeric:tabular-nums;text-align:right}
|
||||
.scrollx{overflow-x:auto}
|
||||
input.in,select.in,textarea.in{
|
||||
@@ -112,21 +114,31 @@ label.row span.lb{width:110px;flex:none;color:var(--fg2);font-size:13px}
|
||||
}
|
||||
.switch input:checked+i{background:var(--ok)}
|
||||
.switch input:checked+i:before{transform:translateX(15px)}
|
||||
#strip{display:flex;gap:8px;flex-wrap:wrap;padding:4px 0 2px}
|
||||
#strip{display:flex;gap:6px;flex:1;min-width:0;overflow-x:auto;scrollbar-width:thin}
|
||||
.port:first-child{margin-left:auto}
|
||||
.port:last-child{margin-right:auto}
|
||||
.port{
|
||||
width:46px;border:2px solid var(--bd);border-radius:7px;padding:5px 0 3px;
|
||||
width:40px;border:2px solid var(--bd);border-radius:7px;padding:3px 0 2px;
|
||||
text-align:center;cursor:pointer;background:var(--panel2);position:relative;
|
||||
}
|
||||
.port.sfp{width:56px}
|
||||
.port .pn{font-weight:650;font-size:13px}
|
||||
.port .ps{font-size:10px;color:var(--fg2);display:block;line-height:1.2}
|
||||
.port.sfp{width:50px}
|
||||
.port .pn{font-weight:650;font-size:12px}
|
||||
.port .ps{font-size:9px;color:var(--fg2);display:block;line-height:1.2}
|
||||
.port .jack{
|
||||
display:block;width:18px;height:12px;margin:3px auto 2px;border-radius:2px;
|
||||
border:2px solid var(--fg2);border-top-width:5px;opacity:.55;
|
||||
display:block;width:21px;height:12px;margin:4px auto 2px;border-radius:2px;
|
||||
border:2px solid var(--fg2);border-top-width:8px;opacity:.55;position:relative;
|
||||
}
|
||||
.port.sfp .jack{width:28px;height:10px;border-top-width:3px}
|
||||
.port .jack:before{content:"";
|
||||
position:absolute;bottom:100%;left:50%;transform:translateX(-50%);
|
||||
width:6px;height:5px;margin-bottom:7px;border-radius: 1px 1px 0 0;
|
||||
background-color:var(--fg2);
|
||||
}
|
||||
.port.sfp .jack{width:24px;height:16px;margin:0 auto 2px;border-top-width:5px}
|
||||
.port.sfp .jack:before{width:10px;margin-bottom:2px;border-radius:0 0 1px 1px;background-color:var(--panel2)}
|
||||
.port.up{border-color:var(--pc);background:color-mix(in srgb,var(--pc) 10%,var(--panel2))}
|
||||
.port.up .jack{border-color:var(--pc);opacity:1}
|
||||
.port.up .jack:before{background-color:var(--pc)}
|
||||
.port.up.sfp .jack:before{background-color:color-mix(in srgb,var(--pc) 10%,var(--panel2))}
|
||||
.port.up .ps{color:var(--pc);font-weight:600}
|
||||
.port.dis{opacity:.45;border-style:dashed}
|
||||
.badge{font-size:11px;font-weight:600;border-radius:10px;padding:1px 8px;background:var(--panel2);color:var(--fg2)}
|
||||
@@ -183,6 +195,10 @@ button.danger{background:var(--bad);color:#fff;border-color:transparent}
|
||||
#ttitle{font-size:14px;overflow:hidden;text-overflow:ellipsis;white-space:nowrap}
|
||||
#dirty{padding:2px 7px}
|
||||
}
|
||||
@media (max-width:720px){
|
||||
#ttitle{flex:1}
|
||||
#strip{order:3;flex-basis:100%}
|
||||
}
|
||||
</style>
|
||||
<script>
|
||||
(function(){
|
||||
@@ -215,15 +231,13 @@ button.danger{background:var(--bad);color:#fff;border-color:transparent}
|
||||
<svg width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="var(--fg)" stroke-width="2"><path d="M4 6h16M4 12h16M4 18h16"/></svg>
|
||||
</button>
|
||||
<span id="ttitle"></span>
|
||||
<div id="strip"></div>
|
||||
<span id="dirty" data-i18n="hdr_dirty" data-i18n-t="hdr_dirty_t">unsaved changes</span>
|
||||
<button class="ctl pri" id="saveBtn" data-i18n="hdr_save" data-i18n-t="hdr_save_t">Save to flash</button>
|
||||
<button class="ctl" id="saveBtn" data-i18n="hdr_save" data-i18n-t="hdr_save_t">Save to flash</button>
|
||||
</header>
|
||||
|
||||
<main>
|
||||
<section class="tab act" id="tab-dash">
|
||||
<div class="card"><h2><span data-i18n="d_ports">Ports</span> <span class="hint" data-i18n="d_ports_h">click a port for details</span></h2>
|
||||
<div id="strip"></div>
|
||||
</div>
|
||||
<div class="grid2">
|
||||
<div class="card"><h2 data-i18n="d_system">System</h2><table class="t" id="sysinfo"></table></div>
|
||||
<div class="card"><h2><span data-i18n="d_traffic">Traffic</span> <span class="hint" data-i18n="d_traffic_h">packets/s, live</span></h2>
|
||||
@@ -237,7 +251,7 @@ button.danger{background:var(--bad);color:#fff;border-color:transparent}
|
||||
<section class="tab" id="tab-ports">
|
||||
<div class="card"><h2 data-i18n="pt_title">Port configuration</h2>
|
||||
<div class="scrollx"><table class="t" id="ptable">
|
||||
<thead><tr><th data-i18n="c_port">Port</th><th data-i18n="c_name">Name</th><th data-i18n="c_link">Link</th><th data-i18n="pt_conn">Connected</th><th data-i18n="pt_speed">Speed</th><th data-i18n="c_enabled">Enabled</th><th data-i18n="pt_mtu">MTU</th><th></th></tr></thead>
|
||||
<thead><tr><th data-i18n="c_port">Port</th><th data-i18n="c_name">Name</th><th data-i18n="c_link">Link</th><th data-i18n="pt_conn">Devices learned</th><th data-i18n="pt_speed">Speed</th><th data-i18n="c_enabled">Enabled</th><th data-i18n="pt_mtu">MTU</th><th></th></tr></thead>
|
||||
<tbody></tbody></table></div>
|
||||
<p class="small mut" style="margin-top:8px" data-i18n="pt_sfp_note">SFP speed is set per slot and is not saved in the startup config.</p>
|
||||
</div>
|
||||
@@ -250,6 +264,8 @@ button.danger{background:var(--bad);color:#fff;border-color:transparent}
|
||||
<p class="small mono" id="stpids">-</p>
|
||||
<div id="stpbridge" style="display:flex;gap:14px;flex-wrap:wrap;align-items:flex-end;margin-top:10px">
|
||||
<label><span class="small mut" data-i18n="stp_version">Version</span><br><select class="in" id="stpver"><option value="rstp">RSTP</option><option value="stp" data-i18n="stp_v_stp">STP compatible</option></select></label>
|
||||
<label><span class="small mut" data-i18n="stp_pcs">Path cost</span><br><select class="in" id="stppcs"><option value="long" data-i18n="stp_pcs_long">Long (32 bit)</option><option value="short" data-i18n="stp_pcs_short">Short (16 bit)</option></select></label>
|
||||
<label><span class="small mut" data-i18n="stp_bh">BPDUs while disabled</span><br><select class="in" id="stpbh"><option value="flood" data-i18n="stp_bh_flood">Flood</option><option value="filter" data-i18n="stp_bh_filter">Filter</option></select></label>
|
||||
<label><span class="small mut" data-i18n="stp_prio">Bridge priority</span><br><select class="in" id="stpprio"></select></label>
|
||||
<label><span class="small mut" data-i18n="stp_hello">Hello [s]</span><br><input class="in sm" id="stphello" type="number" min="1" max="10"></label>
|
||||
<label><span class="small mut" data-i18n="stp_maxage">Max age [s]</span><br><input class="in sm" id="stpmaxage" type="number" min="6" max="40"></label>
|
||||
@@ -267,9 +283,14 @@ button.danger{background:var(--bad);color:#fff;border-color:transparent}
|
||||
</div>
|
||||
<div class="card"><h2 data-i18n="stp_status">Port status</h2>
|
||||
<div class="scrollx"><table class="t" id="stpstat">
|
||||
<thead><tr><th data-i18n="c_port">Port</th><th data-i18n="stp_role">Role</th><th data-i18n="c_state">State</th><th data-i18n="stp_db">Designated bridge</th><th data-i18n="stp_dp">Designated port</th><th data-i18n="stp_dc">Designated cost</th><th data-i18n="stp_oedge">Oper. edge</th><th data-i18n="stp_op2p">Oper. P2P</th></tr></thead>
|
||||
<thead><tr><th data-i18n="c_port">Port</th><th data-i18n="stp_role">Role</th><th data-i18n="c_state">State</th><th data-i18n="stp_db">Designated bridge</th><th data-i18n="stp_dp">Designated port</th><th data-i18n="stp_dc">Designated cost</th><th data-i18n="stp_oedge">Oper. edge</th><th data-i18n="stp_op2p">Oper. P2P</th><th data-i18n="stp_proto">Protocol</th><th></th></tr></thead>
|
||||
<tbody></tbody></table></div>
|
||||
</div>
|
||||
<div class="card"><details id="stpcntd"><summary><h2 style="display:inline" data-i18n="stp_cnt">Counters</h2></summary>
|
||||
<div class="scrollx"><table class="t" id="stpcnt">
|
||||
<thead><tr><th data-i18n="c_port">Port</th><th data-i18n="stp_crx">BPDUs received</th><th data-i18n="stp_ctx">BPDUs sent</th><th data-i18n="stp_ctrx">TC received</th><th data-i18n="stp_cttx">TC sent</th><th data-i18n="stp_cage">Last BPDU [s]</th></tr></thead>
|
||||
<tbody></tbody></table></div></details>
|
||||
</div>
|
||||
</section>
|
||||
|
||||
<section class="tab" id="tab-stats">
|
||||
@@ -281,27 +302,21 @@ button.danger{background:var(--bad);color:#fff;border-color:transparent}
|
||||
</section>
|
||||
|
||||
<section class="tab" id="tab-vlan">
|
||||
<div class="card"><h2><span data-i18n="v_title">VLANs</span> <span class="hint"><span data-i18n="v_mgmt">Management VLAN</span>: <span id="vmgmtcur">-</span></span></h2>
|
||||
<div class="card"><h2><span data-i18n="v_title">VLANs</span><span id="vmgmtcur"></span></h2>
|
||||
<div class="scrollx"><table class="t" id="vtable">
|
||||
<thead><tr><th data-i18n="v_vid">VID</th><th data-i18n="c_name">Name</th><th data-i18n="v_members">Members</th><th data-i18n="v_tagged">Tagged</th><th data-i18n="v_untagged">Untagged</th><th data-i18n="v_pvid_on">PVID on</th><th></th></tr></thead>
|
||||
<tbody></tbody></table></div>
|
||||
<p class="small mut" id="vempty" style="display:none;margin-top:6px" data-i18n="v_empty">No VLANs configured.</p>
|
||||
<button class="ctl pri" id="vlan_add" data-i18n="v_add">Add a new VLAN</button>
|
||||
</div>
|
||||
<div class="card"><h2 data-i18n="v_editor">VLAN editor</h2>
|
||||
<div style="display:flex;gap:10px;flex-wrap:wrap;align-items:center;margin-bottom:10px">
|
||||
<label><span data-i18n="v_vid">VID</span> <input class="in sm" id="vvid" type="number" min="1" max="4094"></label>
|
||||
<label><span data-i18n="c_name">Name</span> <input class="in" id="vname" size="12" maxlength="15" data-i18n-p="v_optional"></label>
|
||||
<button class="ctl" id="vload" data-i18n="c_load">Load</button>
|
||||
<button class="ctl pri" id="vapply" data-i18n="c_apply">Apply</button>
|
||||
<button class="ctl" id="vmgmt" data-i18n="v_setmgmt" data-i18n-t="v_setmgmt_t">Set as mgmt VLAN</button>
|
||||
</div>
|
||||
<div class="card"><h2><span data-i18n="v_editor">VLAN editor</span> <span class="hint" id="vtitle"></span></h2>
|
||||
<div><label class="row"><span class="lb" style="width:150px;" data-i18n="v_setmgmt_t"></span>
|
||||
<span class="switch"><input type="checkbox" id="vmgmtpromote"><i></i></span>
|
||||
</label></div>
|
||||
<div class="scrollx"><table class="t" id="vedit"><tbody></tbody></table></div>
|
||||
<div style="margin-top:10px"><button class="ctl pri" id="vapply" data-i18n="c_apply">Apply</button></div>
|
||||
<p class="small mut" style="margin-top:6px" data-i18n="v_legend">U = untagged member, T = tagged member, - = not a member. PVID assigns this VLAN to untagged ingress traffic.</p>
|
||||
</div>
|
||||
<div class="card"><h2><span data-i18n="v_ingress">Ingress filtering</span> <span class="hint" data-i18n="v_ingress_h">write-only: the state is not readable from the switch</span></h2>
|
||||
<div class="scrollx"><table class="t" id="ingress"><tbody></tbody></table></div>
|
||||
<div style="margin-top:10px"><button class="ctl pri" id="ingapply" data-i18n="v_ing_apply">Apply ingress modes</button></div>
|
||||
</div>
|
||||
</section>
|
||||
|
||||
<section class="tab" id="tab-l2">
|
||||
@@ -331,8 +346,36 @@ button.danger{background:var(--bad);color:#fff;border-color:transparent}
|
||||
</div>
|
||||
</section>
|
||||
|
||||
<section class="tab" id="tab-lldp">
|
||||
<div class="card"><h2 data-i18n="lldp_title">LLDP</h2>
|
||||
<label class="row"><span class="lb" data-i18n="lldp_enabled">Enabled</span>
|
||||
<span class="switch"><input type="checkbox" id="lldp_en"><i></i></span>
|
||||
</label>
|
||||
<div class="scrollx"><table class="t" id="lldp_table">
|
||||
<thead><tr><th data-i18n="c_port">Port Number</th><th data-i18n="lldp_portname">Name</th><th data-i18n="lldp_enable">Send Frames</th></tr></thead>
|
||||
<tbody></tbody></table></div>
|
||||
</div>
|
||||
</section>
|
||||
|
||||
<section class="tab" id="tab-isolate">
|
||||
<div class="card"><h2 data-i18n="iso_title">Port isolation</h2>
|
||||
<p class="small mut" style="margin-bottom:10px" data-i18n="iso_intro">A frame entering a port may only leave through the ports ticked in its row.</p>
|
||||
<div class="scrollx"><table class="t" id="isotbl" style="width:auto"><thead></thead><tbody></tbody></table></div>
|
||||
<p class="small mut" style="margin-top:6px" data-i18n="iso_legend">The cell of the port itself is always allowed.</p>
|
||||
<div style="display:flex;gap:12px;margin-top:10px">
|
||||
<button class="ctl pri" id="isoapply" data-i18n="c_apply">Apply</button>
|
||||
<button class="ctl" id="isoall" data-i18n="iso_all">Allow all</button>
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
||||
|
||||
<section class="tab" id="tab-lag">
|
||||
<div id="lagwrap" class="grid2"></div>
|
||||
<div class="card" style="margin-top:14px"><h2 data-i18n="lacp_heading">LACP (802.3ad)</h2>
|
||||
<div class="scrollx"><table class="t" id="lacptbl">
|
||||
<thead><tr><th data-i18n="c_port">Port</th><th>LAG</th><th data-i18n="lacp_actor">Actor</th><th data-i18n="lacp_partner">Partner</th><th data-i18n="lacp_rxstate">RX state</th><th data-i18n="lacp_rx">LACPDUs received</th><th data-i18n="lacp_psys">Partner system</th></tr></thead>
|
||||
<tbody></tbody></table></div>
|
||||
</div>
|
||||
<p class="small mut" style="margin-top:10px" data-i18n="lag_note">A LAG needs at least one member to be saved in the startup config; applying an empty group clears it.</p>
|
||||
</section>
|
||||
|
||||
@@ -351,6 +394,12 @@ button.danger{background:var(--bad);color:#fff;border-color:transparent}
|
||||
<thead><tr><th data-i18n="c_port">Port</th><th data-i18n="bw_in">Ingress limit</th><th>Mbit/s</th><th data-i18n="bw_exceed">When exceeded</th><th data-i18n="bw_out">Egress limit</th><th>Mbit/s</th><th></th></tr></thead>
|
||||
<tbody></tbody></table></div>
|
||||
</div>
|
||||
<div class="card"><h2><span data-i18n="sc_title">Storm control</span> <span class="hint" data-i18n="sc_h">per port and traffic type, empty = off</span></h2>
|
||||
<div class="scrollx"><table class="t" id="sctable">
|
||||
<thead><tr><th data-i18n="c_port">Port</th><th data-i18n="sc_bcast">Broadcast</th><th data-i18n="sc_mcast">Multicast</th><th data-i18n="sc_ucast">Unknown unicast</th><th data-i18n="sc_umcast">Unknown multicast</th><th></th></tr></thead>
|
||||
<tbody></tbody></table></div>
|
||||
<p class="small mut" style="margin-top:8px" data-i18n="sc_note">Frames of a type above its limit are dropped where they enter the switch.</p>
|
||||
</div>
|
||||
</section>
|
||||
|
||||
<section class="tab" id="tab-system">
|
||||
@@ -360,6 +409,9 @@ button.danger{background:var(--bad);color:#fff;border-color:transparent}
|
||||
<label class="row"><span class="lb" data-i18n="i_ip">IP address</span><input class="in" id="sy-ip" size="15"></label>
|
||||
<label class="row"><span class="lb" data-i18n="i_mask">Netmask</span><input class="in" id="sy-mask" size="15"></label>
|
||||
<label class="row"><span class="lb" data-i18n="i_gw">Gateway</span><input class="in" id="sy-gw" size="15"></label>
|
||||
<label class="row"><span class="lb" data-i18n="sy_dns1">DNS server</span><input class="in" id="sy-dns1" size="15"></label>
|
||||
<label class="row"><span class="lb" data-i18n="sy_dns2">Second DNS</span><input class="in" id="sy-dns2" size="15"></label>
|
||||
<p class="small mut" id="sy-dnsdhcp"></p>
|
||||
<div style="display:flex;gap:10px;margin-top:12px">
|
||||
<button class="ctl pri" id="sy-apply" data-i18n="c_apply">Apply</button>
|
||||
<button class="ctl" id="sy-dhcp" data-i18n="sy_dhcp" data-i18n-t="sy_dhcp_t">Use DHCP</button>
|
||||
@@ -375,11 +427,29 @@ button.danger{background:var(--bad);color:#fff;border-color:transparent}
|
||||
<input class="in" id="sy-sysip" style="flex:1;min-width:0;width:0" data-i18n-p="sy_sysip">
|
||||
<span class="small mut" data-i18n="sy_port">Port</span>
|
||||
<input class="in" id="sy-sysport" style="width:64px" type="number" min="1" max="65535"></div>
|
||||
<label class="row" style="justify-content:space-between"><span data-i18n="sy_sesstmo">Session timeout (s)</span>
|
||||
<input class="in" id="sy-sesstmo" style="width:80px" type="number" min="1" max="65535"></label>
|
||||
<p class="small mut" data-i18n="sy_services_note">Service state reflects the startup config; runtime state is not readable.</p>
|
||||
</div>
|
||||
<div class="card" id="sy-ntpcard"><h2 data-i18n="sy_time">Time</h2>
|
||||
<label class="row" style="justify-content:space-between"><span data-i18n="sy_ntp">NTP</span>
|
||||
<span class="switch"><input type="checkbox" id="sy-ntp"><i></i></span></label>
|
||||
<label class="row"><span class="lb" data-i18n="sy_ntpsrv">NTP server</span><input class="in" id="sy-ntpsrv" maxlength="47" data-i18n-p="sy_ntpsrv_p"></label>
|
||||
<label class="row"><span class="lb" data-i18n="sy_ntpint">Poll interval [min]</span><input class="in sm" id="sy-ntpint" type="number" min="1" max="1440"></label>
|
||||
<label class="row"><span class="lb" data-i18n="sy_tz">Time zone</span><select class="in" id="sy-tz"></select></label>
|
||||
<label class="row"><span class="lb" data-i18n="sy_dst">Daylight saving</span><select class="in" id="sy-dst"><option value="off" data-i18n="c_offc">Off</option><option value="eu" data-i18n="sy_dst_eu">Europe</option><option value="us" data-i18n="sy_dst_us">United States</option></select></label>
|
||||
<div style="display:flex;gap:10px;margin-top:8px">
|
||||
<button class="ctl pri" id="sy-ntpapply" data-i18n="c_apply">Apply</button>
|
||||
<button class="ctl" id="sy-ntprefresh" data-i18n="c_refresh">Refresh</button>
|
||||
</div>
|
||||
<label class="row" style="margin-top:10px"><span class="lb" data-i18n="sy_now">Local time</span><span class="mono" id="sy-now">-</span></label>
|
||||
<label class="row"><span class="lb" data-i18n="sy_sync">Last synchronisation</span><span id="sy-sync">-</span></label>
|
||||
<label class="row"><span class="lb" data-i18n="sy_ntpaddr">Server address</span><span class="mono" id="sy-ntpaddr">-</span></label>
|
||||
<p class="small mut" style="margin-top:8px" data-i18n="sy_time_note">The switch has no battery backed clock.</p>
|
||||
</div>
|
||||
<div class="card"><h2 data-i18n="sy_password">Admin password</h2>
|
||||
<label class="row"><span class="lb" data-i18n="sy_newpw">New password</span><input class="in" id="sy-pw1" type="password" maxlength="20"></label>
|
||||
<label class="row"><span class="lb" data-i18n="sy_repeat">Repeat</span><input class="in" id="sy-pw2" type="password" maxlength="20"></label>
|
||||
<label class="row"><span class="lb" data-i18n="sy_newpw">New password</span><input class="in" id="sy-pw1" type="password" autocomplete="new-password" maxlength="20"></label>
|
||||
<label class="row"><span class="lb" data-i18n="sy_repeat">Repeat</span><input class="in" id="sy-pw2" type="password" autocomplete="new-password" maxlength="20"></label>
|
||||
<button class="ctl pri" id="sy-pwapply" style="margin-top:8px" data-i18n="sy_pwapply">Change password</button>
|
||||
<p class="small mut" style="margin-top:8px" data-i18n="sy_pw_note">Takes effect immediately; save to flash to keep it after reboot.</p>
|
||||
</div>
|
||||
@@ -393,10 +463,13 @@ button.danger{background:var(--bad);color:#fff;border-color:transparent}
|
||||
<div class="card"><h2 data-i18n="sy_display">Display</h2>
|
||||
<label class="row"><span class="lb" data-i18n="sy_lang">Language</span>
|
||||
<select class="in" id="langSel">
|
||||
<option value="de">Deutsch</option>
|
||||
<option value="en">English</option>
|
||||
<option value="es">Español</option>
|
||||
<option value="fr">Français</option>
|
||||
<option value="ja">日本語</option>
|
||||
<option value="zh">中文</option>
|
||||
<option value="es">Español</option>
|
||||
|
||||
</select></label>
|
||||
<label class="row"><span class="lb" data-i18n="sy_theme">Theme</span>
|
||||
<select class="in" id="themeSel">
|
||||
|
||||
@@ -81,17 +81,23 @@ button:hover{filter:brightness(1.1)}
|
||||
<script>
|
||||
(function(){
|
||||
var L={
|
||||
de:["Switch-Anmeldung","RTLPlayground-Verwaltungsoberfläche","Passwort","Anmelden","Falsches Passwort, bitte erneut versuchen."],
|
||||
en:["Switch Login","RTLPlayground management interface","Password","Sign in","Wrong password, try again."],
|
||||
ja:["スイッチ ログイン","RTLPlayground 管理インターフェース","パスワード","ログイン","パスワードが違います。もう一度お試しください。"],
|
||||
zh:["交换机登录","RTLPlayground 管理界面","密码","登录","密码错误,请重试。"],
|
||||
es:["Inicio de sesión del switch","Interfaz de gestión de RTLPlayground","Contraseña","Iniciar sesión","Contraseña incorrecta, inténtalo de nuevo."]
|
||||
es:["Inicio de sesión del switch","Interfaz de gestión de RTLPlayground","Contraseña","Iniciar sesión","Contraseña incorrecta, inténtalo de nuevo."],
|
||||
fr:["Utilisateur du commutateur","Interface de management RTLPlayground","Mot de passe","Se connecter","Mot de passe erronné, essayez à nouveau."]
|
||||
};
|
||||
var l=null;
|
||||
try{l=localStorage.getItem("rtl_lang");}catch(e){}
|
||||
if(!L[l])l=(navigator.language||"en").slice(0,2);
|
||||
var s=L[l]||L.en;
|
||||
document.title=s[0];
|
||||
document.getElementById("hn").textContent=s[0];
|
||||
// Name the tab after the switch: the hostname seen at the last login here,
|
||||
// otherwise the address, so several open switches can be told apart
|
||||
var host=null;
|
||||
try{host=localStorage.getItem("rtl_host");}catch(e){}
|
||||
document.title=(host||location.hostname)+" – "+s[0];
|
||||
document.getElementById("hn").textContent=host||s[0];
|
||||
document.getElementById("sub").textContent=s[1];
|
||||
document.getElementById("lp").textContent=s[2];
|
||||
document.getElementById("lb").textContent=s[3];
|
||||
|
||||
@@ -6,13 +6,17 @@
|
||||
#include "cmd_parser.h"
|
||||
#include "rtl837x_flash.h"
|
||||
#include "uip.h"
|
||||
#include "sessions.h"
|
||||
#include "html_data.h"
|
||||
|
||||
// #define DEBUG
|
||||
#include "debug.h"
|
||||
|
||||
#define SESSION_ID_LENGTH 12
|
||||
#define SESSION_TIMEOUT 200
|
||||
#define SESSION_TIMEOUT_DEFAULT 200
|
||||
|
||||
/* Seconds of inactivity after which a session cookie stops validating.
|
||||
* Configurable with the "session" command, see cmd_parser.c. */
|
||||
__xdata uint16_t session_timeout = SESSION_TIMEOUT_DEFAULT;
|
||||
|
||||
#define CMARK_S 6
|
||||
|
||||
@@ -51,8 +55,8 @@ __xdata uint8_t boundary[72];
|
||||
#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
|
||||
// bytes buffered in config_buf so far (HTTP header, POST 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;
|
||||
@@ -71,13 +75,9 @@ __xdata uint8_t postbody_endpoint;
|
||||
__xdata uint16_t postbody_start;
|
||||
|
||||
__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];
|
||||
// Up to four logins at once, see sessions.c
|
||||
__xdata uint8_t authenticated;
|
||||
__xdata uint32_t now;
|
||||
__xdata uint32_t last_session_use;
|
||||
|
||||
#define TSTATE_NONE 0
|
||||
#define TSTATE_TX 1
|
||||
@@ -86,6 +86,7 @@ __xdata uint32_t last_session_use;
|
||||
#define TSTATE_POST 4
|
||||
#define TSTATE_MULTIPART 5
|
||||
#define TSTATE_POSTBODY 6
|
||||
#define TSTATE_HEADER 7
|
||||
|
||||
extern __xdata uint16_t crc_value;
|
||||
__xdata uint16_t crc_final;
|
||||
@@ -105,8 +106,9 @@ void httpd_init(void) __banked
|
||||
uip_listen(HTONS(80));
|
||||
// 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
|
||||
for (uint8_t i = 0; i < UIP_CONNS; i++)
|
||||
uip_conns[i].appstate.tstate = TSTATE_CLOSED;
|
||||
session_init();
|
||||
}
|
||||
|
||||
|
||||
@@ -341,38 +343,11 @@ __xdata uint8_t *scan_header(__xdata uint8_t * __xdata p)
|
||||
|
||||
read_reg_timer(&now);
|
||||
|
||||
if (session && session_id[0]) {
|
||||
if (now - last_session_use > SESSION_TIMEOUT) {
|
||||
dbg_string("Session expired\n");
|
||||
} else {
|
||||
if (is_word_x(session, session_id)) {
|
||||
authenticated = 1;
|
||||
last_session_use = now;
|
||||
} else {
|
||||
dbg_string("Invalid session cookie!\n");
|
||||
}
|
||||
}
|
||||
}
|
||||
if (session)
|
||||
session_check();
|
||||
return p;
|
||||
}
|
||||
|
||||
/*
|
||||
* Generate random HEX-chars at the buffer location.
|
||||
*/
|
||||
void gen_random_hex_chars(__xdata uint8_t * b, __xdata uint8_t bytes)
|
||||
{
|
||||
uint8_t i = 0;
|
||||
while (bytes) {
|
||||
if (!i)
|
||||
get_random_32();
|
||||
b[--bytes] = itohex(sfr_data[i]);
|
||||
if (!bytes) { break; }
|
||||
b[--bytes] = itohex(sfr_data[i] >> 4 | sfr_data[i] << 4);
|
||||
i = (i + 1) & 0x3;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/* 0: body incomplete, 1: configuration stored, 2: malformed */
|
||||
static uint8_t config_take(void)
|
||||
{
|
||||
@@ -446,15 +421,9 @@ static uint8_t config_take(void)
|
||||
// unlike scan_header(), keeps no auth state, so it may run on every buffered segment
|
||||
static uint16_t preamble_payload_start(uint16_t n)
|
||||
{
|
||||
uint16_t pos;
|
||||
// Skip the HTTP header separator retained by handle_post().
|
||||
uint16_t pos = 4;
|
||||
|
||||
for (pos = 0; pos + 24 <= n; pos++) {
|
||||
if (strstart(&config_buf[pos], "application/octet-stream"))
|
||||
break;
|
||||
}
|
||||
if (pos + 24 > n)
|
||||
return 0;
|
||||
pos += 24;
|
||||
while (pos + 3 < n && !strstart(&config_buf[pos], "\r\n\r\n"))
|
||||
pos++;
|
||||
if (pos + 3 >= n)
|
||||
@@ -507,7 +476,7 @@ uint8_t stream_upload(void)
|
||||
"Content-Type: text/plain\r\n\r\n"
|
||||
"OK: checksum verified, rebooting\n");
|
||||
// Reset once the response is fully ACKed
|
||||
fw_reset_pending = 1;
|
||||
s->fw_reset_pending = 1;
|
||||
} else {
|
||||
print_string("Checksum incorrect! Aborting.\n");
|
||||
slen = strtox(outbuf, "HTTP/1.1 400 Bad Request\r\nContent-Length: 33\r\n"
|
||||
@@ -535,9 +504,10 @@ uint8_t stream_upload(void)
|
||||
memcpy(flash_buf + write_len, boundary, bindex);
|
||||
write_len += bindex;
|
||||
bindex = 0;
|
||||
} else {
|
||||
crc16_bank1(upload_settings.p + upload_settings.bptr);
|
||||
flash_buf[write_len++] = upload_settings.p[upload_settings.bptr++];
|
||||
}
|
||||
crc16_bank1(upload_settings.p + upload_settings.bptr);
|
||||
flash_buf[write_len++] = upload_settings.p[upload_settings.bptr++];
|
||||
if (write_len >= FLASH_PAGE_SIZE) {
|
||||
/* The staged image spans FIRMWARE_UPLOAD_START to twice that,
|
||||
* the span check_and_flash_update_image() reads back. Nothing
|
||||
@@ -569,7 +539,6 @@ uint8_t stream_upload(void)
|
||||
memcpy(flash_buf, flash_buf + FLASH_PAGE_SIZE, write_len);
|
||||
}
|
||||
}
|
||||
bindex = 0;
|
||||
}
|
||||
} while(1);
|
||||
}
|
||||
@@ -679,13 +648,10 @@ static void run_login_body(__xdata uint8_t *body)
|
||||
{
|
||||
if (strstart(body, "pwd=") && is_url_word_x(body + 4, passwd)) {
|
||||
dbg_string("Password accepted!\n");
|
||||
read_reg_timer(&last_session_use);
|
||||
gen_random_hex_chars(session_id, SESSION_ID_LENGTH);
|
||||
session_id[SESSION_ID_LENGTH] = NUL;
|
||||
read_reg_timer(&now);
|
||||
slen = strtox(outbuf, "HTTP/1.1 302 Found\r\nConnection: close\r\nLocation: index.html\r\n" \
|
||||
"Set-Cookie: session=");
|
||||
for (uint8_t i = 0; i < SESSION_ID_LENGTH; i++)
|
||||
outbuf[slen++] = session_id[i];
|
||||
session_new();
|
||||
slen += strtox(outbuf + slen, "; SameSite=Strict\r\n\r\n");
|
||||
} else {
|
||||
dbg_string("Password invalid!\n");
|
||||
@@ -742,7 +708,42 @@ static void post_body_continue(void)
|
||||
}
|
||||
|
||||
|
||||
void handle_post(void)
|
||||
|
||||
/*
|
||||
* Buffer an HTTP request header across TCP receive events. Stop copying at
|
||||
* CRLFCRLF so any body bytes in the final segment stay in uip_appdata.
|
||||
*
|
||||
* Return the body offset in the current receive buffer, 0 while incomplete,
|
||||
* or 0xffff after sending an error response.
|
||||
*/
|
||||
static uint16_t request_header_take(void)
|
||||
{
|
||||
uint16_t i;
|
||||
|
||||
for (i = 0; i < uip_len; i++) {
|
||||
if (pre_acc + 1 >= CONFIG_UPLOAD_BUF) {
|
||||
uip_conn->appstate.tstate = TSTATE_NONE;
|
||||
send_bad_request();
|
||||
return 0xffff;
|
||||
}
|
||||
config_buf[pre_acc++] = ((__xdata uint8_t *)uip_appdata)[i];
|
||||
config_buf[pre_acc] = NUL;
|
||||
if (pre_acc >= 4
|
||||
&& config_buf[pre_acc - 4] == '\r'
|
||||
&& config_buf[pre_acc - 3] == '\n'
|
||||
&& config_buf[pre_acc - 2] == '\r'
|
||||
&& config_buf[pre_acc - 1] == '\n') {
|
||||
uip_conn->appstate.tstate = TSTATE_NONE;
|
||||
return i + 1;
|
||||
}
|
||||
}
|
||||
|
||||
postbody_start = ticks;
|
||||
uip_conn->appstate.tstate = TSTATE_HEADER;
|
||||
return 0;
|
||||
}
|
||||
|
||||
void handle_post(uint16_t body_off)
|
||||
{
|
||||
__xdata struct httpd_state * __xdata s = &(uip_conn->appstate);
|
||||
__xdata uint8_t *p = uip_appdata;
|
||||
@@ -755,6 +756,9 @@ void handle_post(void)
|
||||
|
||||
// Was the multipart header sent in multiple packets?
|
||||
if (s->tstate != TSTATE_MULTIPART) {
|
||||
p = config_buf;
|
||||
request_path = p + 6;
|
||||
|
||||
dbg_string("Is POST\n");
|
||||
p += 5; // Skip post
|
||||
// Find end of request path
|
||||
@@ -768,9 +772,12 @@ void handle_post(void)
|
||||
dbg_string("Boundary: >"); dbg_string_x(boundary); dbg_string("<\n");
|
||||
if (!*p || !content_type) {
|
||||
dbg_string("Bad Request!\n");
|
||||
send_not_found();
|
||||
send_bad_request();
|
||||
return;
|
||||
}
|
||||
|
||||
pre_acc = 0;
|
||||
p = (__xdata uint8_t *)uip_appdata + body_off;
|
||||
if (is_word(request_path, "upload")) {
|
||||
if (!authenticated) {
|
||||
send_unauthorized();
|
||||
@@ -796,7 +803,10 @@ void handle_post(void)
|
||||
dbg_string("Configuration upload\n");
|
||||
verify_crc = 0;
|
||||
config_upload = 1;
|
||||
pre_acc = 0;
|
||||
// config_take() matches CRLF-prefixed delimiters, including the first.
|
||||
config_buf[0] = '\r';
|
||||
config_buf[1] = '\n';
|
||||
pre_acc = 2;
|
||||
}
|
||||
// Check for other POST requests, which are not multipart, below
|
||||
} else {
|
||||
@@ -820,7 +830,6 @@ void handle_post(void)
|
||||
handle_firmware_fragment(p);
|
||||
return;
|
||||
} else if (is_word(request_path, "cmd")) {
|
||||
p += 4;
|
||||
if (!authenticated) {
|
||||
send_unauthorized();
|
||||
return;
|
||||
@@ -839,7 +848,6 @@ void handle_post(void)
|
||||
return;
|
||||
}
|
||||
|
||||
p += 4;
|
||||
postbody_endpoint = POSTBODY_LOGIN;
|
||||
if (!post_body_take(p))
|
||||
return;
|
||||
@@ -870,9 +878,10 @@ void httpd_appcall(void)
|
||||
print_byte(s->tstate);
|
||||
write_char(' ');
|
||||
#endif
|
||||
if(uip_connected() && s->tstate == TSTATE_CLOSED) {
|
||||
if(uip_connected()) {
|
||||
dbg_string("Connected...\n");
|
||||
s->tstate = TSTATE_NONE;
|
||||
s->fw_reset_pending = 0; // explicitly initialize reused XDATA appstate
|
||||
} else if (uip_closed()) {
|
||||
dbg_string("Connection closed\n");
|
||||
s->tstate = TSTATE_CLOSED;
|
||||
@@ -886,9 +895,9 @@ void httpd_appcall(void)
|
||||
dbg_string("Closing because everything has been transmitted\n");
|
||||
uip_close();
|
||||
s->tstate = TSTATE_CLOSED;
|
||||
} else if (s->tstate == TSTATE_POSTBODY
|
||||
} else if ((s->tstate == TSTATE_POSTBODY || s->tstate == TSTATE_HEADER)
|
||||
&& (uint16_t)ticks - postbody_start > POSTBODY_TIMEOUT) {
|
||||
dbg_string("Body never arrived\n");
|
||||
dbg_string("HTTP request never completed\n");
|
||||
uip_abort();
|
||||
s->tstate = TSTATE_CLOSED;
|
||||
}
|
||||
@@ -924,7 +933,7 @@ void httpd_appcall(void)
|
||||
cont_len -= slen;
|
||||
cont_addr += slen;
|
||||
s->tstate = TSTATE_TX;
|
||||
} else if (fw_reset_pending) {
|
||||
} else if (s->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");
|
||||
@@ -951,6 +960,7 @@ void httpd_appcall(void)
|
||||
cont_len = 0;
|
||||
dbg_char('<'); dbg_short(uip_len); dbg_char('\n');
|
||||
__xdata uint8_t *p = uip_appdata;
|
||||
uint16_t body_off = 0;
|
||||
// Mark end of request header with \0
|
||||
p[uip_len] = 0;
|
||||
#ifdef DEBUG
|
||||
@@ -959,9 +969,21 @@ void httpd_appcall(void)
|
||||
dbg_char('\n');
|
||||
#endif
|
||||
p = uip_appdata;
|
||||
if (is_word(p, "POST") || s->tstate == TSTATE_MULTIPART
|
||||
|| s->tstate == TSTATE_POSTBODY) {
|
||||
handle_post();
|
||||
if (s->tstate != TSTATE_MULTIPART && s->tstate != TSTATE_POSTBODY) {
|
||||
if (s->tstate != TSTATE_HEADER)
|
||||
pre_acc = 0;
|
||||
body_off = request_header_take();
|
||||
if (!body_off) {
|
||||
uip_len = 0;
|
||||
return;
|
||||
}
|
||||
if (body_off == 0xffff)
|
||||
goto do_send;
|
||||
}
|
||||
|
||||
if (s->tstate == TSTATE_MULTIPART || s->tstate == TSTATE_POSTBODY
|
||||
|| is_word(config_buf, "POST")) {
|
||||
handle_post(body_off);
|
||||
// If this is an ongoing post stream, then wait for the next packet
|
||||
if (s->tstate == TSTATE_POST || s->tstate == TSTATE_MULTIPART
|
||||
|| s->tstate == TSTATE_POSTBODY) {
|
||||
@@ -971,12 +993,11 @@ void httpd_appcall(void)
|
||||
goto do_send;
|
||||
}
|
||||
|
||||
// We only expect a GET request here.
|
||||
p = config_buf;
|
||||
if (!is_word(p, "GET")) {
|
||||
send_bad_request();
|
||||
goto do_send;
|
||||
}
|
||||
|
||||
dbg_string("GET request ");
|
||||
p += 4;
|
||||
scan_header(p);
|
||||
@@ -1012,6 +1033,8 @@ void httpd_appcall(void)
|
||||
send_eee();
|
||||
} else if (is_word(q, "/bandwidth.json")) {
|
||||
send_bandwidth();
|
||||
} else if (is_word(q, "/storm.json")) {
|
||||
send_storm();
|
||||
} else if (is_word(q, "/l2.json")) {
|
||||
parse_short(q + 13); // e.g.: /l2.json?idx=10
|
||||
send_l2(short_parsed);
|
||||
@@ -1020,12 +1043,20 @@ void httpd_appcall(void)
|
||||
l2_delete(short_parsed);
|
||||
} else if (is_word(q, "/mirror.json")) {
|
||||
send_mirror();
|
||||
} else if (is_word(q, "/isolation.json")) {
|
||||
send_isolation();
|
||||
} else if (is_word(q, "/mtu.json")) {
|
||||
send_mtu();
|
||||
} else if (is_word(q, "/lag.json")) {
|
||||
send_lag();
|
||||
} else if (is_word(q, "/stp.json")) {
|
||||
send_stp();
|
||||
} else if (is_word(q, "/lldp.json")) {
|
||||
send_lldp();
|
||||
} else if (is_word(q, "/lacp.json")) {
|
||||
send_lacp();
|
||||
} else if (is_word(q, "/stpcnt.json")) {
|
||||
send_stp_counters();
|
||||
} else if (is_word(q, "/vlanlist")) {
|
||||
send_vlanlist();
|
||||
} else if (is_word(q, "/config")) {
|
||||
|
||||
@@ -12,6 +12,7 @@
|
||||
for each TCP connection. */
|
||||
typedef struct httpd_state {
|
||||
uint8_t tstate;
|
||||
uint8_t fw_reset_pending;
|
||||
} uip_tcp_appstate_t;
|
||||
|
||||
/* Finally we define the application function to be called by uIP. */
|
||||
|
||||
@@ -3,6 +3,7 @@
|
||||
#include "rtl837x_sfr.h"
|
||||
#include "rtl837x_common.h"
|
||||
#include "rtl837x_regs.h"
|
||||
#include "rtl837x_storm.h"
|
||||
#include "rtl837x_port.h"
|
||||
#include "rtl837x_flash.h"
|
||||
#include "rtl837x_pins.h"
|
||||
@@ -13,6 +14,7 @@
|
||||
#include "version.h"
|
||||
#include "machine.h"
|
||||
#include "rtl837x_stp.h"
|
||||
#include "rtl837x_lacp.h"
|
||||
#include "page_impl.h"
|
||||
#include "syslog.h"
|
||||
|
||||
@@ -28,6 +30,7 @@ extern __code const struct machine machine;
|
||||
extern __xdata uint8_t outbuf[TCP_OUTBUF_SIZE];
|
||||
extern __xdata uint16_t slen;
|
||||
extern __xdata uint16_t management_vlan;
|
||||
extern __xdata uint16_t session_timeout;
|
||||
extern __xdata uint16_t cont_len;
|
||||
extern __xdata uint32_t cont_addr;
|
||||
extern __code const uint8_t * __code const hex;
|
||||
@@ -47,6 +50,9 @@ extern __xdata char sfp_module_model[2][17];
|
||||
extern __xdata char sfp_module_serial[2][17];
|
||||
extern __xdata uint8_t sfp_options[2];
|
||||
|
||||
extern __xdata uint16_t lldp_logical_port_status;
|
||||
extern __xdata bool lldp_enabled;
|
||||
|
||||
__code const uint8_t * __code const HTTP_RESPONCE_JSON = "HTTP/1.1 200 OK\r\nConnection: close\r\nContent-Type: application/json\r\n\r\n";
|
||||
__code const uint8_t * __code const HTTP_RESPONCE_TXT = "HTTP/1.1 200 OK\r\nConnection: close\r\nContent-Type: text/plain\r\n\r\n";
|
||||
|
||||
@@ -114,12 +120,14 @@ void itoa_html(uint8_t v)
|
||||
char_to_html('0' + (v % 10));
|
||||
}
|
||||
|
||||
void itoa16_html(uint16_t v) /* sufficient for VLAN IDs (max 4094) */
|
||||
void itoa16_html(uint16_t v) /* up to five digits (65535) */
|
||||
{
|
||||
uint8_t print_zeros = 0;
|
||||
uint8_t d;
|
||||
d = v / 1000;
|
||||
d = v / 10000;
|
||||
if (d) { char_to_html('0' + d); print_zeros = 1; }
|
||||
d = (v / 1000) % 10;
|
||||
if (d || print_zeros) { char_to_html('0' + d); print_zeros = 1; }
|
||||
d = (v / 100) % 10;
|
||||
if (d || print_zeros) { char_to_html('0' + d); print_zeros = 1; }
|
||||
d = (v / 10) % 10;
|
||||
@@ -132,6 +140,23 @@ void string_to_html(__code const char *s)
|
||||
while (*s) char_to_html(*s++);
|
||||
}
|
||||
|
||||
static void temp_to_html(void)
|
||||
{
|
||||
int16_t v;
|
||||
|
||||
reg_read(RTL837X_TM_RESULT);
|
||||
v = SFR_DATA_U16;
|
||||
if (v < 0) {
|
||||
char_to_html('-');
|
||||
v = -v;
|
||||
}
|
||||
itoa_html(v >> 7);
|
||||
char_to_html('.');
|
||||
itoa_html(((v & 0x7f) * 10) >> 7);
|
||||
char_to_html(' ');
|
||||
char_to_html('C');
|
||||
}
|
||||
|
||||
uint16_t stat_content(void)
|
||||
{
|
||||
dbg_string("stat_content called\n");
|
||||
@@ -258,7 +283,9 @@ void send_basic_info(void)
|
||||
itoa_html(syslog_state.server_ip[2]); char_to_html('.');
|
||||
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, "\",\"session_timeout\":");
|
||||
itoa16_html(session_timeout);
|
||||
slen += strtox(outbuf + slen, ",\"mac_address\":\"");
|
||||
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[2]); char_to_html(':');
|
||||
@@ -277,6 +304,8 @@ void send_basic_info(void)
|
||||
slen += strtox(outbuf + slen, BUILD_DATE);
|
||||
slen += strtox(outbuf + slen, "\",\"hw_ver\":\"");
|
||||
slen += strtox(outbuf + slen, machine.machine_name);
|
||||
slen += strtox(outbuf + slen, "\",\"chip_temp\":\"");
|
||||
temp_to_html();
|
||||
slen += strtox(outbuf + slen, "\",\"flash_size\":\"");
|
||||
string_to_html(get_flash_size_str());
|
||||
|
||||
@@ -312,7 +341,7 @@ void send_vlan(uint16_t vlan)
|
||||
}
|
||||
slen += strtox(outbuf + slen, "\",\"pvid\":\"0x");
|
||||
uint16_t pvid_mask = 0;
|
||||
for (uint8_t i = machine.min_port; i <= machine.max_port; i++) {
|
||||
for (uint8_t i = MACHINE_MIN_PORT; i <= MACHINE_MAX_PORT; i++) {
|
||||
if (port_pvid_get(i) == vlan)
|
||||
pvid_mask |= (1 << i);
|
||||
}
|
||||
@@ -328,7 +357,7 @@ void send_vlan(uint16_t vlan)
|
||||
bool send_counters(uint8_t phys_port)
|
||||
{
|
||||
uint8_t phys_port_idx = phys_port - 1;
|
||||
if (phys_port_idx > (machine.max_port - machine.min_port))
|
||||
if (phys_port_idx > (MACHINE_MAX_PORT - MACHINE_MIN_PORT))
|
||||
goto err;
|
||||
uint8_t log_port = machine.phys_to_log_port[phys_port_idx];
|
||||
|
||||
@@ -571,145 +600,83 @@ 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)
|
||||
void send_lacp(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");
|
||||
dbg_string("send_lacp called\n");
|
||||
slen = strtox(outbuf, HTTP_RESPONCE_JSON);
|
||||
|
||||
slen += strtox(outbuf + slen, "{\"on\":");
|
||||
bool_to_html(stp_enabled);
|
||||
slen += strtox(outbuf + slen, ",\"rstp\":");
|
||||
bool_to_html(stp_rstp);
|
||||
slen += strtox(outbuf + slen, ",\"prio\":");
|
||||
itoa_html(stp_prio >> 4);
|
||||
slen += strtox(outbuf + slen, ",\"hello\":");
|
||||
itoa_html(stp_hello_s);
|
||||
slen += strtox(outbuf + slen, ",\"maxage\":");
|
||||
itoa_html(stp_maxage_s);
|
||||
slen += strtox(outbuf + slen, ",\"fwd\":");
|
||||
itoa_html(stp_fwddelay_s);
|
||||
slen += strtox(outbuf + slen, ",\"txhold\":");
|
||||
itoa_html(stp_txhold);
|
||||
slen += strtox(outbuf + slen, ",\"rootPrio\":\"");
|
||||
byte_to_html(root_bridge.prio);
|
||||
byte_to_html(root_bridge.ext);
|
||||
slen += strtox(outbuf + slen, "\",\"rootMac\":\"");
|
||||
for (j = 0; j < 6; j++)
|
||||
byte_to_html(root_bridge.mac[j]);
|
||||
slen += strtox(outbuf + slen, "\",\"myMac\":\"");
|
||||
for (j = 0; j < 6; j++)
|
||||
byte_to_html(uip_ethaddr.addr[j]);
|
||||
slen += strtox(outbuf + slen, "\",\"cost\":\"");
|
||||
byte_to_html(root_bridge_cost >> 24);
|
||||
byte_to_html(root_bridge_cost >> 16);
|
||||
byte_to_html(root_bridge_cost >> 8);
|
||||
byte_to_html(root_bridge_cost);
|
||||
slen += strtox(outbuf + slen, "\",\"weRoot\":");
|
||||
bool_to_html(stp_root_port == 0xff ? 1 : 0);
|
||||
slen += strtox(outbuf + slen, ",\"rootPort\":");
|
||||
j = stp_root_port;
|
||||
if (j != 0xff && j >= STP_LAG_BASE) {
|
||||
j = 0;
|
||||
while (j < STP_LAG_BASE && !((stp_lag_mask[stp_root_port - STP_LAG_BASE] >> j) & 1))
|
||||
j++;
|
||||
if (j >= STP_LAG_BASE)
|
||||
j = 0;
|
||||
bool_to_html(lacpEnabled);
|
||||
slen += strtox(outbuf + slen, ",\"lags\":[");
|
||||
for (uint8_t l = 0; l < LACP_NUM_LAGS; l++) {
|
||||
slen += strtox(outbuf + slen, "{\"cfg\":\"");
|
||||
byte_to_html(lacp_lag_ports[l] >> 8);
|
||||
byte_to_html(lacp_lag_ports[l]);
|
||||
slen += strtox(outbuf + slen, "\",\"aggValid\":");
|
||||
bool_to_html(lacp_agg_valid[l]);
|
||||
slen += strtox(outbuf + slen, ",\"agg\":\"");
|
||||
for (uint8_t j = 0; j < MAC_ADDR_LEN; j++)
|
||||
byte_to_html(lacp_agg_sys[l][j]);
|
||||
slen += strtox(outbuf + slen, "\",\"members\":\"");
|
||||
byte_to_html(lacp_members_last[l] >> 8);
|
||||
byte_to_html(lacp_members_last[l]);
|
||||
slen += strtox(outbuf + slen, "\"},");
|
||||
}
|
||||
itoa_html(j == 0xff ? 0 : machine.log_to_phys_port[j]);
|
||||
slen += strtox(outbuf + slen, ",\"tc\":\"");
|
||||
byte_to_html(stp_tc_count >> 8);
|
||||
byte_to_html(stp_tc_count);
|
||||
slen += strtox(outbuf + slen, "\",\"ports\":[");
|
||||
reg_read_m(RTL837X_MSTP_STATES);
|
||||
for (i = 0; i < STP_ENTITIES; i++) {
|
||||
if (i < STP_LAG_BASE && (i < machine.min_port || i > machine.max_port))
|
||||
continue;
|
||||
if (i < STP_LAG_BASE && stp_ent_of[i] != i)
|
||||
continue;
|
||||
if (i >= STP_LAG_BASE && !stp_lag_mask[i - STP_LAG_BASE])
|
||||
continue;
|
||||
slen -= 1; // remove comma
|
||||
slen += strtox(outbuf + slen, "],\"ports\":[");
|
||||
for (uint8_t i = MACHINE_MIN_PORT; i <= MACHINE_MAX_PORT; i++) {
|
||||
slen += strtox(outbuf + slen, "{\"p\":");
|
||||
itoa_html(i < STP_LAG_BASE ? machine.log_to_phys_port[i] : 0);
|
||||
itoa_html(machine.log_to_phys_port[i]);
|
||||
slen += strtox(outbuf + slen, ",\"lag\":");
|
||||
itoa_html(i < STP_LAG_BASE ? 0 : i - STP_LAG_BASE + 1);
|
||||
slen += strtox(outbuf + slen, ",\"mbr\":");
|
||||
itoa16_html(i < STP_LAG_BASE ? 0 : stp_lag_mask[i - STP_LAG_BASE]);
|
||||
slen += strtox(outbuf + slen, ",\"st\":");
|
||||
j = i;
|
||||
if (i >= STP_LAG_BASE) {
|
||||
j = 0;
|
||||
while (j < STP_LAG_BASE && !((stp_lag_mask[i - STP_LAG_BASE] >> j) & 1))
|
||||
j++;
|
||||
if (j >= STP_LAG_BASE)
|
||||
j = 0;
|
||||
}
|
||||
st = (sfr_data[3 - (j >> 2)] >> ((j << 1) & 0x7)) & 0x3;
|
||||
itoa_html(st);
|
||||
slen += strtox(outbuf + slen, ",\"role\":");
|
||||
if (!(stp_pflags[i] & STP_PF_ENABLED) || (stp_pflags[i] & STP_PF_TRIPPED))
|
||||
itoa_html(0);
|
||||
else if (i == stp_root_port)
|
||||
itoa_html(1);
|
||||
else if (st == 3)
|
||||
itoa_html(2);
|
||||
else
|
||||
itoa_html(3);
|
||||
slen += strtox(outbuf + slen, ",\"f\":");
|
||||
itoa_html(stp_pflags[i]);
|
||||
slen += strtox(outbuf + slen, ",\"pc\":\"");
|
||||
pi_u32 = stp_pcost[i]; u32hex_html();
|
||||
slen += strtox(outbuf + slen, "\",\"prio\":");
|
||||
itoa_html(stp_pprio[i]);
|
||||
slen += strtox(outbuf + slen, ",\"p2\":");
|
||||
itoa_html(stp_pp2p[i]);
|
||||
dsg = stp_dpid[i] && stp_bpdu_age[i] < (uint16_t)stp_maxage_s * STP_HZ;
|
||||
slen += strtox(outbuf + slen, ",\"db\":\"");
|
||||
if (dsg) {
|
||||
pi_prio = stp_dbridge[i].prio; pi_ext = stp_dbridge[i].ext;
|
||||
pi_mac = stp_dbridge[i].mac;
|
||||
} else {
|
||||
pi_prio = stp_prio; pi_ext = 0;
|
||||
pi_mac = uip_ethaddr.addr;
|
||||
}
|
||||
bridge_to_html();
|
||||
slen += strtox(outbuf + slen, "\",\"dp\":\"");
|
||||
byte_to_html(dsg ? (stp_dpid[i] >> 8) : stp_pprio[i]);
|
||||
byte_to_html(dsg ? stp_dpid[i] : (i + 1));
|
||||
slen += strtox(outbuf + slen, "\",\"dc\":\"");
|
||||
pi_u32 = dsg ? stp_dcost[i] : root_bridge_cost; u32hex_html();
|
||||
itoa_html(lacp_port_lag[i]);
|
||||
slen += strtox(outbuf + slen, ",\"a\":\"");
|
||||
byte_to_html(lacp_actor_state[i]);
|
||||
slen += strtox(outbuf + slen, "\",\"pt\":\"");
|
||||
byte_to_html(lacp_partner_state[i]);
|
||||
slen += strtox(outbuf + slen, "\",\"rs\":");
|
||||
itoa_html(lacp_rx_state[i]);
|
||||
slen += strtox(outbuf + slen, ",\"rx\":\"");
|
||||
byte_to_html(lacp_rx_count[i] >> 8);
|
||||
byte_to_html(lacp_rx_count[i]);
|
||||
slen += strtox(outbuf + slen, "\",\"psys\":\"");
|
||||
for (uint8_t j = 0; j < MAC_ADDR_LEN; j++)
|
||||
byte_to_html(lacp_partner_sys[i][j]);
|
||||
slen += strtox(outbuf + slen, "\"},");
|
||||
}
|
||||
slen -= 1; // remove comma
|
||||
slen += strtox(outbuf + slen, "]}");
|
||||
}
|
||||
|
||||
void send_lldp(void)
|
||||
{
|
||||
dbg_string("send_lldp called\n");
|
||||
slen = strtox(outbuf, HTTP_RESPONCE_JSON);
|
||||
|
||||
slen += strtox(outbuf + slen, "{\"on\":");
|
||||
bool_to_html(lldp_enabled);
|
||||
slen += strtox(outbuf + slen, ",\"portStatus\":[");
|
||||
|
||||
for (uint8_t i = MACHINE_MIN_PORT; i <= MACHINE_MAX_PORT; i++) {
|
||||
slen += strtox(outbuf + slen, "{\"portNum\":");
|
||||
itoa_html(machine.log_to_phys_port[i]);
|
||||
|
||||
slen += strtox(outbuf + slen, ",\"logPort\":");
|
||||
itoa_html(i);
|
||||
slen += strtox(outbuf + slen, ",\"name\":\"");
|
||||
for (uint8_t j = 0; j < PORT_NAME_SIZE && port_names[i][j]; j++) {
|
||||
json_char_to_html(port_names[i][j]);
|
||||
}
|
||||
|
||||
slen += strtox(outbuf + slen, "\",\"permitted\":");
|
||||
bool_to_html(1 & (lldp_logical_port_status >> i));
|
||||
slen += strtox(outbuf + slen, "}");
|
||||
|
||||
if (i < MACHINE_MAX_PORT)
|
||||
char_to_html(',');
|
||||
}
|
||||
char_to_html(']');
|
||||
char_to_html('}');
|
||||
}
|
||||
|
||||
|
||||
void send_eee(void)
|
||||
@@ -721,7 +688,7 @@ void send_eee(void)
|
||||
uint8_t eee_ablty = sfr_data[3];
|
||||
|
||||
char_to_html('[');
|
||||
for (uint8_t i = machine.min_port; i <= machine.max_port; i++) {
|
||||
for (uint8_t i = MACHINE_MIN_PORT; i <= MACHINE_MAX_PORT; i++) {
|
||||
slen += strtox(outbuf + slen, "{\"portNum\":");
|
||||
itoa_html(machine.log_to_phys_port[i]);
|
||||
|
||||
@@ -753,11 +720,10 @@ void send_eee(void)
|
||||
bool_to_html(eee_ablty & (1 << i));
|
||||
}
|
||||
char_to_html('}');
|
||||
if (i < machine.max_port)
|
||||
if (i < MACHINE_MAX_PORT)
|
||||
char_to_html(',');
|
||||
else
|
||||
char_to_html(']');
|
||||
}
|
||||
char_to_html(']');
|
||||
}
|
||||
|
||||
|
||||
@@ -766,7 +732,7 @@ void send_bandwidth(void)
|
||||
dbg_string("send_bandwidth called\n");
|
||||
slen = strtox(outbuf, HTTP_RESPONCE_JSON);
|
||||
char_to_html('[');
|
||||
for (uint8_t i = machine.min_port; i <= machine.max_port; i++) {
|
||||
for (uint8_t i = MACHINE_MIN_PORT; i <= MACHINE_MAX_PORT; i++) {
|
||||
slen += strtox(outbuf + slen, "{\"portNum\":");
|
||||
itoa_html(machine.log_to_phys_port[i]);
|
||||
slen += strtox(outbuf + slen, ",\"iLimited\":");
|
||||
@@ -796,11 +762,66 @@ void send_bandwidth(void)
|
||||
byte_to_html(sfr_data[3]);
|
||||
char_to_html('"');
|
||||
char_to_html('}');
|
||||
if (i < machine.max_port)
|
||||
if (i < MACHINE_MAX_PORT)
|
||||
char_to_html(',');
|
||||
else
|
||||
char_to_html(']');
|
||||
}
|
||||
char_to_html(']');
|
||||
}
|
||||
|
||||
|
||||
void send_storm(void)
|
||||
{
|
||||
__xdata uint8_t i, t, idx;
|
||||
|
||||
slen = strtox(outbuf, HTTP_RESPONCE_JSON);
|
||||
char_to_html('[');
|
||||
for (i = MACHINE_MIN_PORT; i <= MACHINE_MAX_PORT; i++) {
|
||||
slen += strtox(outbuf + slen, "{\"portNum\":");
|
||||
itoa_html(machine.log_to_phys_port[i]);
|
||||
slen += strtox(outbuf + slen, ",\"en\":\"");
|
||||
for (t = 0; t < STORM_TYPES; t++)
|
||||
char_to_html(reg_bit_test(RTL837X_STORM_CTRL + (t << 2), i) ? '1' : '0');
|
||||
slen += strtox(outbuf + slen, "\",\"pps\":\"");
|
||||
for (t = 0; t < STORM_TYPES; t++) {
|
||||
idx = STORM_METER(i, t);
|
||||
char_to_html(reg_bit_test(RTL837X_METER_MODE + ((idx >> 5) << 2), idx & 0x1f) ? '1' : '0');
|
||||
}
|
||||
slen += strtox(outbuf + slen, "\",\"rate\":\"");
|
||||
for (t = 0; t < STORM_TYPES; t++) {
|
||||
reg_read_m(RTL837X_METER_RATE + (STORM_METER(i, t) << 2));
|
||||
byte_to_html(sfr_data[1]);
|
||||
byte_to_html(sfr_data[2]);
|
||||
byte_to_html(sfr_data[3]);
|
||||
}
|
||||
char_to_html('"');
|
||||
char_to_html('}');
|
||||
if (i < MACHINE_MAX_PORT)
|
||||
char_to_html(',');
|
||||
}
|
||||
char_to_html(']');
|
||||
}
|
||||
|
||||
|
||||
void send_isolation(void)
|
||||
{
|
||||
__xdata uint16_t allow;
|
||||
|
||||
dbg_string("send_isolation called\n");
|
||||
slen = strtox(outbuf, HTTP_RESPONCE_JSON);
|
||||
char_to_html('[');
|
||||
for (uint8_t i = MACHINE_MIN_PORT; i <= MACHINE_MAX_PORT; i++) {
|
||||
slen += strtox(outbuf + slen, "{\"portNum\":");
|
||||
itoa_html(machine.log_to_phys_port[i]);
|
||||
slen += strtox(outbuf + slen, ",\"allow\":\"");
|
||||
allow = port_isolation_get(i);
|
||||
byte_to_html(allow >> 8);
|
||||
byte_to_html(allow & 0xff);
|
||||
char_to_html('"');
|
||||
char_to_html('}');
|
||||
if (i < MACHINE_MAX_PORT)
|
||||
char_to_html(',');
|
||||
}
|
||||
char_to_html(']');
|
||||
}
|
||||
|
||||
|
||||
@@ -809,7 +830,7 @@ void send_mtu(void)
|
||||
dbg_string("send_mtu called\n");
|
||||
slen = strtox(outbuf, HTTP_RESPONCE_JSON);
|
||||
char_to_html('[');
|
||||
for (uint8_t i = machine.min_port; i <= machine.max_port; i++) {
|
||||
for (uint8_t i = MACHINE_MIN_PORT; i <= MACHINE_MAX_PORT; i++) {
|
||||
slen += strtox(outbuf + slen, "{\"portNum\":");
|
||||
itoa_html(machine.log_to_phys_port[i]);
|
||||
slen += strtox(outbuf + slen, ",\"mtu\":\"0x");
|
||||
@@ -819,11 +840,10 @@ void send_mtu(void)
|
||||
byte_to_html(mtu & 0xff);
|
||||
char_to_html('"');
|
||||
char_to_html('}');
|
||||
if (i < machine.max_port)
|
||||
if (i < MACHINE_MAX_PORT)
|
||||
char_to_html(',');
|
||||
else
|
||||
char_to_html(']');
|
||||
}
|
||||
char_to_html(']');
|
||||
}
|
||||
|
||||
|
||||
@@ -833,7 +853,7 @@ void send_status(void)
|
||||
dbg_string("sending status\n");
|
||||
char_to_html('[');
|
||||
|
||||
for (uint8_t i = machine.min_port; i <= machine.max_port; i++) {
|
||||
for (uint8_t i = MACHINE_MIN_PORT; i <= MACHINE_MAX_PORT; i++) {
|
||||
slen += strtox(outbuf + slen, "{\"portNum\":");
|
||||
itoa_html(machine.log_to_phys_port[i]);
|
||||
slen += strtox(outbuf + slen, ",\"logPort\":");
|
||||
@@ -950,12 +970,14 @@ void send_status(void)
|
||||
slen += strtox(outbuf + slen, "\",\"rxB\":\"0x");
|
||||
STAT_GET(STAT_COUNTER_ERR_PKTS, i);
|
||||
reg_to_html(RTL837X_STAT_V_HIGH); // 32bit RX packet errors
|
||||
|
||||
slen += strtox(outbuf + slen, "\",\"macs\":\"0x");
|
||||
reg_to_html(RTL837X_L2_LRN_PORT_CONSTRT_CNT + (i << 2));
|
||||
slen += strtox(outbuf + slen, "\"}");
|
||||
if (i < machine.max_port)
|
||||
if (i < MACHINE_MAX_PORT)
|
||||
char_to_html(',');
|
||||
else
|
||||
char_to_html(']');
|
||||
}
|
||||
char_to_html(']');
|
||||
}
|
||||
|
||||
|
||||
@@ -1036,8 +1058,7 @@ void send_vlanlist(void)
|
||||
slen += strtox(outbuf + slen, ",\"vlan\":[");
|
||||
|
||||
for (i = 1; i < 4095; i++) {
|
||||
if (vlan_get(i) < 0)
|
||||
continue;
|
||||
vlan_get(i);
|
||||
if (!(sfr_data[0] & 0x02)) /* bit 1: VLAN table entry valid */
|
||||
continue;
|
||||
|
||||
|
||||
@@ -5,20 +5,27 @@
|
||||
|
||||
extern __code const uint8_t * __code const HTTP_RESPONCE_TXT;
|
||||
|
||||
void itoa16_html(uint16_t v);
|
||||
|
||||
bool send_counters(uint8_t phys_port);
|
||||
void send_status(void);
|
||||
void send_vlan(uint16_t vlan);
|
||||
void send_basic_info(void);
|
||||
void send_bandwidth(void);
|
||||
void send_storm(void);
|
||||
void send_eee(void);
|
||||
void send_l2(uint16_t idx);
|
||||
void l2_delete(uint16_t idx);
|
||||
void send_mirror(void);
|
||||
void send_mtu(void);
|
||||
void send_isolation(void);
|
||||
void send_config(void);
|
||||
void send_cmd_log(void);
|
||||
void send_lag(void);
|
||||
void send_stp(void);
|
||||
void send_stp(void) __banked;
|
||||
void send_lldp(void);
|
||||
void send_lacp(void);
|
||||
void send_stp_counters(void) __banked;
|
||||
void send_vlanlist(void);
|
||||
|
||||
/* Convert only the lower nibble to ascii HEX char.
|
||||
|
||||
@@ -0,0 +1,195 @@
|
||||
#include "rtl837x_sfr.h"
|
||||
#include "rtl837x_common.h"
|
||||
#include "rtl837x_regs.h"
|
||||
#include "rtl837x_port.h"
|
||||
#include "uip.h"
|
||||
#include <stdint.h>
|
||||
#include "phy.h"
|
||||
#include "machine.h"
|
||||
#include "rtl837x_stp.h"
|
||||
#include "page_impl.h"
|
||||
#include "debug.h"
|
||||
|
||||
#pragma codeseg BANK4
|
||||
#pragma constseg BANK4
|
||||
|
||||
extern __xdata uint8_t outbuf[TCP_OUTBUF_SIZE];
|
||||
extern __xdata uint16_t slen;
|
||||
|
||||
static __code const uint8_t stp_json_hdr[] = "HTTP/1.1 200 OK\r\nConnection: close\r\nContent-Type: application/json\r\n\r\n";
|
||||
|
||||
static void stp_bool_html(char c)
|
||||
{
|
||||
outbuf[slen++] = c ? '1' : '0';
|
||||
}
|
||||
|
||||
static void stp_byte_html(uint8_t val)
|
||||
{
|
||||
outbuf[slen++] = itohex(val >> 4);
|
||||
outbuf[slen++] = itohex(val);
|
||||
}
|
||||
|
||||
static void stp_itoa_html(uint8_t v)
|
||||
{
|
||||
uint8_t t = v / 100;
|
||||
uint8_t print_zeros = t;
|
||||
if (print_zeros)
|
||||
outbuf[slen++] = '0' + t;
|
||||
t = (v / 10) % 10;
|
||||
print_zeros |= t;
|
||||
if (print_zeros)
|
||||
outbuf[slen++] = '0' + t;
|
||||
outbuf[slen++] = '0' + (v % 10);
|
||||
}
|
||||
|
||||
|
||||
static __xdata uint32_t pi_u32;
|
||||
static __xdata uint8_t pi_prio, pi_ext;
|
||||
static __xdata uint8_t * __xdata pi_mac;
|
||||
|
||||
|
||||
static void u32hex_html(void)
|
||||
{
|
||||
__xdata uint8_t *b = (__xdata uint8_t *)&pi_u32;
|
||||
stp_byte_html(b[3]);
|
||||
stp_byte_html(b[2]);
|
||||
stp_byte_html(b[1]);
|
||||
stp_byte_html(b[0]);
|
||||
}
|
||||
|
||||
|
||||
static void bridge_to_html(void)
|
||||
{
|
||||
stp_byte_html(pi_prio);
|
||||
stp_byte_html(pi_ext);
|
||||
for (uint8_t i = 0; i < 6; i++)
|
||||
stp_byte_html(pi_mac[i]);
|
||||
}
|
||||
|
||||
|
||||
void send_stp(void) __banked
|
||||
{
|
||||
static __xdata uint8_t i, j, st, dsg;
|
||||
|
||||
dbg_string("send_stp called\n");
|
||||
slen = strtox(outbuf, stp_json_hdr);
|
||||
|
||||
slen += strtox(outbuf + slen, "{\"on\":");
|
||||
stp_bool_html(stp_enabled);
|
||||
slen += strtox(outbuf + slen, ",\"rstp\":");
|
||||
stp_bool_html(stp_rstp);
|
||||
slen += strtox(outbuf + slen, ",\"prio\":");
|
||||
stp_itoa_html(stp_prio >> 4);
|
||||
slen += strtox(outbuf + slen, ",\"hello\":");
|
||||
stp_itoa_html(stp_hello_s);
|
||||
slen += strtox(outbuf + slen, ",\"maxage\":");
|
||||
stp_itoa_html(stp_maxage_s);
|
||||
slen += strtox(outbuf + slen, ",\"fwd\":");
|
||||
stp_itoa_html(stp_fwddelay_s);
|
||||
slen += strtox(outbuf + slen, ",\"txhold\":");
|
||||
stp_itoa_html(stp_txhold);
|
||||
slen += strtox(outbuf + slen, ",\"pcs\":");
|
||||
stp_itoa_html(stp_pcost_short);
|
||||
slen += strtox(outbuf + slen, ",\"bh\":");
|
||||
stp_itoa_html(stp_bpdu_filter);
|
||||
slen += strtox(outbuf + slen, ",\"tcs\":\"");
|
||||
pi_u32 = stp_tc_secs; u32hex_html();
|
||||
slen += strtox(outbuf + slen, "\",\"rMaxage\":");
|
||||
stp_itoa_html(stp_root_port == 0xff ? stp_maxage_s : stp_root_maxage);
|
||||
slen += strtox(outbuf + slen, ",\"rFwd\":");
|
||||
stp_itoa_html(stp_root_port == 0xff ? stp_fwddelay_s : stp_root_fwd);
|
||||
slen += strtox(outbuf + slen, ",\"rootPrio\":\"");
|
||||
stp_byte_html(root_bridge.prio);
|
||||
stp_byte_html(root_bridge.ext);
|
||||
slen += strtox(outbuf + slen, "\",\"rootMac\":\"");
|
||||
for (j = 0; j < 6; j++)
|
||||
stp_byte_html(root_bridge.mac[j]);
|
||||
slen += strtox(outbuf + slen, "\",\"myMac\":\"");
|
||||
for (j = 0; j < 6; j++)
|
||||
stp_byte_html(uip_ethaddr.addr[j]);
|
||||
slen += strtox(outbuf + slen, "\",\"cost\":\"");
|
||||
stp_byte_html(root_bridge_cost >> 24);
|
||||
stp_byte_html(root_bridge_cost >> 16);
|
||||
stp_byte_html(root_bridge_cost >> 8);
|
||||
stp_byte_html(root_bridge_cost);
|
||||
slen += strtox(outbuf + slen, "\",\"weRoot\":");
|
||||
stp_bool_html(stp_root_port == 0xff ? 1 : 0);
|
||||
slen += strtox(outbuf + slen, ",\"tc\":\"");
|
||||
stp_byte_html(stp_tc_count >> 8);
|
||||
stp_byte_html(stp_tc_count);
|
||||
slen += strtox(outbuf + slen, "\",\"ports\":[");
|
||||
for (i = 0; i < STP_ENTITIES; i++) {
|
||||
j = stp_ent_id(i);
|
||||
if (!j)
|
||||
continue;
|
||||
slen += strtox(outbuf + slen, "{\"p\":");
|
||||
stp_itoa_html(j);
|
||||
slen += strtox(outbuf + slen, ",\"st\":");
|
||||
st = stp_port_state(i);
|
||||
stp_itoa_html(st);
|
||||
slen += strtox(outbuf + slen, ",\"role\":");
|
||||
stp_itoa_html(stp_port_role(i));
|
||||
slen += strtox(outbuf + slen, ",\"f\":");
|
||||
stp_itoa_html(stp_pflags[i]);
|
||||
slen += strtox(outbuf + slen, ",\"pc\":\"");
|
||||
pi_u32 = stp_pcost[i]; u32hex_html();
|
||||
slen += strtox(outbuf + slen, "\",\"prio\":");
|
||||
stp_itoa_html(stp_pprio[i]);
|
||||
slen += strtox(outbuf + slen, ",\"p2\":");
|
||||
stp_itoa_html(stp_pp2p[i]);
|
||||
slen += strtox(outbuf + slen, ",\"lk\":");
|
||||
stp_itoa_html((stp_link_prev >> i) & 1);
|
||||
slen += strtox(outbuf + slen, ",\"lg\":");
|
||||
stp_itoa_html((stp_legacy >> i) & 1);
|
||||
dsg = stp_dpid[i] && stp_info_while[i];
|
||||
slen += strtox(outbuf + slen, ",\"db\":\"");
|
||||
if (dsg) {
|
||||
pi_prio = stp_dbridge[i].prio; pi_ext = stp_dbridge[i].ext;
|
||||
pi_mac = stp_dbridge[i].mac;
|
||||
} else {
|
||||
pi_prio = stp_prio; pi_ext = 0;
|
||||
pi_mac = uip_ethaddr.addr;
|
||||
}
|
||||
bridge_to_html();
|
||||
slen += strtox(outbuf + slen, "\",\"dp\":\"");
|
||||
stp_byte_html(dsg ? (stp_dpid[i] >> 8) : stp_pprio[i]);
|
||||
stp_byte_html(dsg ? stp_dpid[i] : j);
|
||||
slen += strtox(outbuf + slen, "\",\"dc\":\"");
|
||||
pi_u32 = dsg ? stp_dcost[i] : root_bridge_cost; u32hex_html();
|
||||
slen += strtox(outbuf + slen, "\"},");
|
||||
}
|
||||
slen -= 1; // remove comma
|
||||
slen += strtox(outbuf + slen, "]}");
|
||||
}
|
||||
|
||||
|
||||
void send_stp_counters(void) __banked
|
||||
{
|
||||
uint8_t i, j;
|
||||
|
||||
slen = strtox(outbuf, stp_json_hdr);
|
||||
slen += strtox(outbuf + slen, "{\"on\":");
|
||||
stp_bool_html(stp_enabled);
|
||||
slen += strtox(outbuf + slen, ",\"hz\":");
|
||||
stp_itoa_html(STP_HZ);
|
||||
slen += strtox(outbuf + slen, ",\"tc\":\"");
|
||||
stp_byte_html(stp_tc_count >> 8);
|
||||
stp_byte_html(stp_tc_count);
|
||||
slen += strtox(outbuf + slen, "\",\"ports\":[");
|
||||
for (i = 0; i < STP_ENTITIES; i++) {
|
||||
j = stp_ent_id(i);
|
||||
if (!j)
|
||||
continue;
|
||||
slen += strtox(outbuf + slen, "{\"p\":");
|
||||
stp_itoa_html(j);
|
||||
slen += strtox(outbuf + slen, ",\"c\":\"");
|
||||
for (j = 0; j < STP_CNT_N; j++) {
|
||||
pi_u32 = stp_cnt[j][i]; u32hex_html();
|
||||
}
|
||||
stp_byte_html(stp_bpdu_age[i] >> 8);
|
||||
stp_byte_html(stp_bpdu_age[i]);
|
||||
slen += strtox(outbuf + slen, "\"},");
|
||||
}
|
||||
slen -= 1;
|
||||
slen += strtox(outbuf + slen, "]}");
|
||||
}
|
||||
@@ -1,9 +1,14 @@
|
||||
CODE_LOCATION=0x1000
|
||||
INSTALLER_ADDRESS=0x1100
|
||||
|
||||
CC = sdcc
|
||||
SDCC_PREFIX = ../toolchain/sdcc-$(shell cat ../sdcc/* | cksum | cut -d' ' -f1)
|
||||
ifeq ($(origin CC),default)
|
||||
SDCC = $(SDCC_PREFIX)/bin/sdcc
|
||||
CC = $(SDCC)
|
||||
ASM = $(SDCC_PREFIX)/bin/sdas8051
|
||||
endif
|
||||
CC_FLAGS = -mmcs51
|
||||
ASM = sdas8051
|
||||
ASM ?= sdas8051
|
||||
AFLAGS= -plosgff
|
||||
|
||||
BUILDDIR = output
|
||||
@@ -13,9 +18,14 @@ OBJS = ${SRCS:%.c=$(BUILDDIR)/%.rel}
|
||||
|
||||
all: create_build_dir $(BUILDDIR)/updatebuilder $(BUILDDIR)/rtlplayground_oem_upgrade.bin
|
||||
|
||||
create_build_dir:
|
||||
create_build_dir: $(SDCC)
|
||||
mkdir -p $(BUILDDIR)
|
||||
|
||||
ifneq ($(SDCC),)
|
||||
$(SDCC):
|
||||
sh ../sdcc/build.sh $(abspath $(SDCC_PREFIX))
|
||||
endif
|
||||
|
||||
$(BUILDDIR)/updatebuilder: updatebuilder.c
|
||||
gcc $^ -o $@
|
||||
|
||||
|
||||