Compare commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
855d587243 | ||
|
|
aa12adc098 | ||
|
|
4db49b2ee5 | ||
|
|
294174d152 | ||
|
|
19bcf86e80 | ||
|
|
8d467b2099 | ||
|
|
a45d0f659f | ||
|
|
820477cd27 | ||
|
|
e9171d7d22 | ||
|
|
dd0ecc5438 | ||
|
|
5e1ea935a7 | ||
|
|
57e7d7dd2a | ||
|
|
855383052b | ||
|
|
acaa80b732 | ||
|
|
233ff5a728 | ||
|
|
c2f5a91763 | ||
|
|
ed8dc0f43c | ||
|
|
7949975191 | ||
|
|
1374272db6 | ||
|
|
20fb626cc1 | ||
|
|
31a488e4e9 | ||
|
|
14bc823024 | ||
|
|
87c161dc70 | ||
|
|
04ab083652 | ||
|
|
1836c7dcb6 | ||
|
|
e33cd9d0c5 | ||
|
|
71880a95f5 | ||
|
|
494320b191 | ||
|
|
eb13d4ab04 | ||
|
|
a15500c493 | ||
|
|
a89467febc | ||
|
|
706abcf78d | ||
|
|
c868482669 | ||
|
|
9bd2d446d7 | ||
|
|
c3a6a46852 | ||
|
|
7a0f248424 | ||
|
|
23e2f5a99e | ||
|
|
81a7393a81 | ||
|
|
dc16b25d62 | ||
|
|
ffbd2c512d | ||
|
|
ba18fa19b0 | ||
|
|
70127018da | ||
|
|
e26a9e3db9 | ||
|
|
d29d7dcf98 | ||
|
|
734cce381a | ||
|
|
dfc96761b3 | ||
|
|
94d4ace88b | ||
|
|
284b2b003d | ||
|
|
68a8a9ee92 | ||
|
|
0045a74df0 | ||
|
|
0bdb238c0a | ||
|
|
033616ef63 | ||
|
|
a57aeda053 | ||
|
|
556899d346 | ||
|
|
cc734ecac2 | ||
|
|
e5d23f11de | ||
|
|
3c648d5936 | ||
|
|
4658667459 | ||
|
|
e966b153af | ||
|
|
5ecd0e6a32 | ||
|
|
d8b52d50de | ||
|
|
1f6f7fbcd4 | ||
|
|
c7ea28393a | ||
|
|
2f787feebf | ||
|
|
4c1266dca9 | ||
|
|
4d11d70356 | ||
|
|
ff94e383aa | ||
|
|
9f7e10fe7c | ||
|
|
b67e3eb602 | ||
|
|
5d731393f1 | ||
|
|
0e2a5fb7bc | ||
|
|
79c9d0deab | ||
|
|
73c56ecfe5 | ||
|
|
5e9f3324d5 | ||
|
|
94739644b7 | ||
|
|
257d8aac4f | ||
|
|
d5019b41df | ||
|
|
eae7f10d7a | ||
|
|
1b43addb11 | ||
|
|
f8c1ab4198 | ||
|
|
11a5feb49d | ||
|
|
1e7e1f42cc | ||
|
|
ea37126e87 | ||
|
|
670e0f02ec | ||
|
|
b4e7865bb7 | ||
|
|
15b0bf9664 | ||
|
|
8bd07ee649 | ||
|
|
3e1031b0f0 | ||
|
|
9918a6267c | ||
|
|
bea162962e | ||
|
|
a2ce5550fc | ||
|
|
1d91c1bf2c | ||
|
|
9d8d6f03cc | ||
|
|
98f3f15e7e | ||
|
|
6f223d3f66 | ||
|
|
fe1df83a88 | ||
|
|
e7b7cb0253 | ||
|
|
4a3cd7b73c | ||
|
|
f538a9344b | ||
|
|
f2b81af702 | ||
|
|
651bdb6489 | ||
|
|
841e1bf95e | ||
|
|
7e652f3fb7 | ||
|
|
90be2f47a3 | ||
|
|
859a50bd82 | ||
|
|
75b06ac90a | ||
|
|
d2edb92ddf | ||
|
|
0099c2a03a | ||
|
|
d29adafe3c | ||
|
|
59894e68cb | ||
|
|
6af072eb2c | ||
|
|
8cae3f1212 | ||
|
|
9aaddcd9b9 | ||
|
|
196e430ec6 | ||
|
|
9424aa3a3b | ||
|
|
7a57416e4c | ||
|
|
fc53003022 | ||
|
|
47878ac580 | ||
|
|
d83f91f9e8 | ||
|
|
708b6b7f28 | ||
|
|
9d07416641 | ||
|
|
4747eac9bf | ||
|
|
ecf152990f | ||
|
|
d4ed753715 | ||
|
|
ea230615f9 | ||
|
|
8f3cabdaff | ||
|
|
eafe2e96e8 | ||
|
|
c4dc930ca8 | ||
|
|
180de32060 | ||
|
|
68fd5d41a7 | ||
|
|
6263b7e4c6 | ||
|
|
ee190a86a1 | ||
|
|
97b46071e0 | ||
|
|
ed37d75aba | ||
|
|
3496d12111 | ||
|
|
cfb7a589a8 | ||
|
|
5391d3ec32 | ||
|
|
f6e4f13ebd | ||
|
|
dbb8fe1249 | ||
|
|
620edc1ba3 | ||
|
|
48455a57b2 | ||
|
|
7d62b24a42 | ||
|
|
2c200417b5 | ||
|
|
2a5f1de8f7 | ||
|
|
9d4eef2dd6 | ||
|
|
55fd2d5fb6 | ||
|
|
aae055123e | ||
|
|
5a11515c36 | ||
|
|
934014c68c | ||
|
|
5662285a3e | ||
|
|
1d94e99612 | ||
|
|
a134335308 | ||
|
|
6495310ba9 | ||
|
|
5962c3a8a6 | ||
|
|
a6e452e638 | ||
|
|
7f4c276285 | ||
|
|
f9fdc6b81d | ||
|
|
d9185b3505 | ||
|
|
3d7ef55ced | ||
|
|
44779404df | ||
|
|
1997d7379b | ||
|
|
b5c249995f | ||
|
|
c61fb6d2fe | ||
|
|
c11099b9ec | ||
|
|
13d0781455 | ||
|
|
b2514bf176 | ||
|
|
c36136047c | ||
|
|
fc327810ed | ||
|
|
b7b4594406 | ||
|
|
aa750d8ff1 | ||
|
|
17b428a603 | ||
|
|
0345bf7bf2 | ||
|
|
1e8382bd56 | ||
|
|
8dc914eb07 | ||
|
|
a528436572 | ||
|
|
2cdeda3bc9 | ||
|
|
a72528b724 | ||
|
|
1e073b20b6 | ||
|
|
efcbc8b4a8 | ||
|
|
13e9dbbc7b | ||
|
|
f48c84b18e | ||
|
|
3bfcabbb99 | ||
|
|
5eecab4844 | ||
|
|
fd1d14a3e7 | ||
|
|
34cebe5971 | ||
|
|
4269745521 | ||
|
|
af3be9a067 | ||
|
|
e8a719d911 | ||
|
|
e74c4f148a | ||
|
|
d1d5d5e18f | ||
|
|
892920a627 | ||
|
|
eb0e90df5c | ||
|
|
2ce168883b | ||
|
|
e54313eded | ||
|
|
300f599551 | ||
|
|
73a355cff5 | ||
|
|
e53f5e7134 | ||
|
|
3509f7da04 | ||
|
|
55d10ae3fc | ||
|
|
88e706c295 | ||
|
|
e59158e006 | ||
|
|
e0c9338357 | ||
|
|
e7df748b6a | ||
|
|
5cb1acfc15 | ||
|
|
46864ca9a7 | ||
|
|
00b42db5da | ||
|
|
84fe44cc9a | ||
|
|
6326428406 | ||
|
|
f33c4e3dc9 | ||
|
|
4a615cb4bd | ||
|
|
030772f1ef | ||
|
|
4ce8adf954 | ||
|
|
66c58a50df | ||
|
|
48c12fa0bb | ||
|
|
d2d7ab7ff1 | ||
|
|
f43bb4eac4 | ||
|
|
4e8eb6d766 | ||
|
|
c5c6b97ad1 | ||
|
|
229fad3809 | ||
|
|
52522968c7 | ||
|
|
7d04ea1980 | ||
|
|
34d6024eeb | ||
|
|
d52aeca609 | ||
|
|
e085fc9cc9 | ||
|
|
f621563479 | ||
|
|
0fe70f6b62 | ||
|
|
745c170f0a | ||
|
|
5b4b0efb16 | ||
|
|
b818a94202 | ||
|
|
bbd4656810 | ||
|
|
8b061d5fb9 | ||
|
|
5ed4d7afc7 | ||
|
|
e73d82ec33 | ||
|
|
52832ff299 | ||
|
|
787a82e846 | ||
|
|
3653f90356 | ||
|
|
9ff114d234 | ||
|
|
0abe83e3e5 | ||
|
|
2a7b8f9838 | ||
|
|
7fdf9ca86d | ||
|
|
2b364943c2 | ||
|
|
a8227f1855 | ||
|
|
34a0e2d338 | ||
|
|
4878a36638 | ||
|
|
dd7a5ebb95 | ||
|
|
3ef7e45649 | ||
|
|
2a7bfdc119 | ||
|
|
367bef2b2f | ||
|
|
cad74017a9 | ||
|
|
3af63af4a4 | ||
|
|
b7cfc73752 | ||
|
|
51345a07e6 | ||
|
|
67fbad38ce | ||
|
|
67439efd2b | ||
|
|
b4b8ad5738 | ||
|
|
eb0106d9f4 | ||
|
|
f0537d759f | ||
|
|
36d5ee3ddd | ||
|
|
6441e85752 | ||
|
|
053c16b84e | ||
|
|
4e9fb19e05 | ||
|
|
0a0e1856ae | ||
|
|
cedd81cf00 | ||
|
|
bf00f9aa2c | ||
|
|
f46cb02828 | ||
|
|
0779222194 | ||
|
|
44ce073869 | ||
|
|
c20ec6b081 | ||
|
|
ed62e46790 | ||
|
|
12595f806a | ||
|
|
58c9af242d | ||
|
|
dc9bc5ec7d | ||
|
|
44792699bf | ||
|
|
12db61faf7 | ||
|
|
519c224dd2 | ||
|
|
b442417a8a | ||
|
|
87717a0790 | ||
|
|
81fdb14213 | ||
|
|
6fd32d290c | ||
|
|
6272d949cd | ||
|
|
9c4d49bc8c | ||
|
|
95fae8bad2 | ||
|
|
6dfad825c0 | ||
|
|
c8c524b71f | ||
|
|
96977b5d88 | ||
|
|
a84c5dcd0c | ||
|
|
b1d5352214 | ||
|
|
677e01fa18 | ||
|
|
b41452026f | ||
|
|
0197c9b6cf | ||
|
|
2ed9974c04 | ||
|
|
11d2c843e5 | ||
|
|
28f832a24d | ||
|
|
d71a303f6c | ||
|
|
74364ed7e8 | ||
|
|
e6cf97b895 | ||
|
|
1cdc8d2cf7 | ||
|
|
a453ec72dc | ||
|
|
edfb86bff4 | ||
|
|
45d4fc19c4 | ||
|
|
d75ad636f9 | ||
|
|
6b96c3b70b | ||
|
|
35ab8cc5e0 | ||
|
|
06fbf85bce | ||
|
|
3c869e927a | ||
|
|
ab6a66a223 | ||
|
|
636981c3b8 | ||
|
|
101b4937e1 | ||
|
|
e9b8ab5b79 | ||
|
|
32b5539670 | ||
|
|
fccafe8059 | ||
|
|
68405f70d2 | ||
|
|
e7301dc39f | ||
|
|
94b6688e57 | ||
|
|
3679b46ff2 | ||
|
|
52fe23570c | ||
|
|
6557fc53e5 | ||
|
|
4b2d7002dc | ||
|
|
b82b957750 | ||
|
|
8284554f69 | ||
|
|
47aa6ab7bf | ||
|
|
42ea80a480 | ||
|
|
e63d1bf577 | ||
|
|
1c78d32493 | ||
|
|
b910692caf | ||
|
|
9c21d94b65 | ||
|
|
7f715e9218 | ||
|
|
ad27566030 | ||
|
|
0722b147fe | ||
|
|
4e4e6a55ba | ||
|
|
dc6734236c | ||
|
|
70c14fb23e | ||
|
|
73a81ec031 | ||
|
|
cac7fbe080 | ||
|
|
6e0e907afb | ||
|
|
a8a770f27f | ||
|
|
69110531b7 | ||
|
|
00a415c755 | ||
|
|
f485102d26 | ||
|
|
4d915a0d80 | ||
|
|
9d4ba57cd0 | ||
|
|
ba9bc2e845 | ||
|
|
76344e172c | ||
|
|
f59be9cf2b | ||
|
|
17fdbf39f0 |
@@ -0,0 +1,21 @@
|
||||
name: Build firmware
|
||||
|
||||
on:
|
||||
push:
|
||||
branches: ['**']
|
||||
|
||||
jobs:
|
||||
build:
|
||||
runs-on: ubuntu-latest
|
||||
container:
|
||||
image: debian:trixie
|
||||
|
||||
steps:
|
||||
- uses: actions/checkout@v6
|
||||
|
||||
- name: Install dependencies
|
||||
run: |
|
||||
apt update
|
||||
apt install make gcc sdcc xxd python-is-python3 libjson-c-dev -y
|
||||
- name: Make project
|
||||
run: make MACHINE="KP_9000_6XHML_X2"
|
||||
@@ -1,7 +1,9 @@
|
||||
.gitignore
|
||||
.idea/
|
||||
output/
|
||||
html_data.c
|
||||
html_data.h
|
||||
version.h
|
||||
tools/httpd_sim
|
||||
tools/injector
|
||||
tools/fileadder
|
||||
@@ -0,0 +1,21 @@
|
||||
MIT License
|
||||
|
||||
Copyright (c) 2025 The RTLPlayground Contributors
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
of this software and associated documentation files (the "Software"), to deal
|
||||
in the Software without restriction, including without limitation the rights
|
||||
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
copies of the Software, and to permit persons to whom the Software is
|
||||
furnished to do so, subject to the following conditions:
|
||||
|
||||
The above copyright notice and this permission notice shall be included in all
|
||||
copies or substantial portions of the Software.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
SOFTWARE.
|
||||
@@ -1,11 +1,11 @@
|
||||
BOOTLOADER_ADDRESS=0x100
|
||||
|
||||
VERSION=0.1.0
|
||||
IMAGESIZE = 524288
|
||||
DEFAULT_CONFIG_LOCATION = 454656
|
||||
CONFIG_LOCATION = 458752
|
||||
HTML_LOCATION = 262144
|
||||
|
||||
CC = sdcc
|
||||
CC_FLAGS = -mmcs51 -Ihttpd -Iuip
|
||||
CC_FLAGS = -mmcs51 -I. -Ihttpd -Iuip
|
||||
ASM = sdas8051
|
||||
AFLAGS= -plosgff
|
||||
|
||||
@@ -13,19 +13,33 @@ SUBDIRS := tools uip httpd
|
||||
SUBDIRSCLEAN=$(addsuffix clean,$(SUBDIRS))
|
||||
|
||||
BUILDDIR = output/
|
||||
VERSION_HEADER := version.h
|
||||
|
||||
all: create_build_dir $(SUBDIRS) $(BUILDDIR)rtlplayground.bin
|
||||
ifeq ($(MACHINE),)
|
||||
else
|
||||
CC_FLAGS += -DMACHINE_$(MACHINE)
|
||||
endif
|
||||
|
||||
all: create_build_dir $(VERSION_HEADER) $(SUBDIRS) $(BUILDDIR)rtlplayground.bin
|
||||
|
||||
create_build_dir:
|
||||
mkdir -p $(BUILDDIR)
|
||||
|
||||
SRCS = rtlplayground.c rtl837x_flash.c rtl837x_phy.c rtl837x_port.c cmd_parser.c html_data.c rtl837x_igmp.c rtl837x_stp.c
|
||||
SRCS = rtlplayground.c rtl837x_flash.c rtl837x_leds.c rtl837x_phy.c rtl837x_port.c cmd_parser.c html_data.c rtl837x_igmp.c \
|
||||
rtl837x_stp.c rtl837x_pins.c dhcp.c machine.c cmd_editor.c rtl837x_bandwidth.c
|
||||
OBJS = ${SRCS:%.c=$(BUILDDIR)%.rel}
|
||||
OBJS += uip/$(BUILDDIR)/timer.rel uip/$(BUILDDIR)/uip-fw.rel uip/$(BUILDDIR)/uip-neighbor.rel uip/$(BUILDDIR)/uip-split.rel uip/$(BUILDDIR)/uip.rel uip/$(BUILDDIR)/uip_arp.rel uip/$(BUILDDIR)/uiplib.rel httpd/$(BUILDDIR)/httpd.rel httpd/$(BUILDDIR)/page_impl.rel
|
||||
OBJS += uip/$(BUILDDIR)/timer.rel uip/$(BUILDDIR)/uip-fw.rel uip/$(BUILDDIR)/uip-split.rel uip/$(BUILDDIR)/uip.rel uip/$(BUILDDIR)/uip_arp.rel uip/$(BUILDDIR)/uiplib.rel httpd/$(BUILDDIR)/httpd.rel httpd/$(BUILDDIR)/page_impl.rel
|
||||
|
||||
html_data.c html_data.h: html tools
|
||||
tools/$(BUILDDIR)fileadder -a $(HTML_LOCATION) -s $(IMAGESIZE) -b BANK1 -d html -p html_data
|
||||
|
||||
$(VERSION_HEADER):
|
||||
@echo "#ifndef VERSION_H" > $(VERSION_HEADER)
|
||||
@echo "#define VERSION_H" >> $(VERSION_HEADER)
|
||||
@echo "#define VERSION_SW \"v$(VERSION)-g$(shell git rev-parse --short HEAD)\"" >> $(VERSION_HEADER)
|
||||
@echo "#define BUILD_DATE \"$(shell date +"%Y-%m-%d %H:%M:%S")\"" >> $(VERSION_HEADER)
|
||||
@echo "#endif" >> $(VERSION_HEADER)
|
||||
|
||||
httpd: html_data.h
|
||||
|
||||
$(SUBDIRS):
|
||||
@@ -34,7 +48,7 @@ $(SUBDIRS):
|
||||
clean:
|
||||
-make -C uip clean
|
||||
-make -C httpd clean
|
||||
-rm html_data.c html_data.h
|
||||
-rm html_data.c html_data.h $(VERSION_HEADER)
|
||||
-rm -r $(BUILDDIR)
|
||||
|
||||
$(BUILDDIR)crtstart.rel: crtstart.asm
|
||||
@@ -50,18 +64,16 @@ $(BUILDDIR)%.rel: $(BUILDDIR)%.asm
|
||||
${ASM} ${AFLAGS} -o $@ $<
|
||||
# mv -f $(addprefix $(basename $^), .lst .rel .sym) .
|
||||
|
||||
$(BUILDDIR)rtlplayground.ihx: $(BUILDDIR)crtstart.rel $(OBJS) $(BUILDDIR)crc16.rel
|
||||
$(CC) $(CC_FLAGS) -Wl-bHOME=${BOOTLOADER_ADDRESS} -Wl-bBANK1=0x14000 -Wl-r -o $@ $^
|
||||
$(BUILDDIR)rtlplayground.ihx: $(OBJS) $(BUILDDIR)crtstart.rel $(BUILDDIR)crc16.rel
|
||||
$(CC) $(CC_FLAGS) -Wl-bHOME=0x00000 -Wl-bBANK1=0x14000 -Wl-bBANK2=0x24000 -Wl-r -o $@ $^
|
||||
|
||||
$(BUILDDIR)rtlplayground.img: $(BUILDDIR)rtlplayground.ihx
|
||||
objcopy --input-target=ihex -O binary $< $@
|
||||
|
||||
$(BUILDDIR)rtlplayground.bin: $(BUILDDIR)rtlplayground.img
|
||||
if [ -e $@ ]; then rm $@; fi
|
||||
echo "0000000: 00 40" | xxd -r - $@
|
||||
cat $< >> $@
|
||||
truncate --size=16K $@
|
||||
dd if=$< skip=80 bs=1024 >>$@
|
||||
tools/$(BUILDDIR)imagebuilder -i $^ $@
|
||||
tools/$(BUILDDIR)fileadder -a $(DEFAULT_CONFIG_LOCATION) -s $(IMAGESIZE) -d config.txt $@
|
||||
tools/$(BUILDDIR)fileadder -a $(CONFIG_LOCATION) -s $(IMAGESIZE) -d config.txt $@
|
||||
tools/$(BUILDDIR)fileadder -a $(HTML_LOCATION) -s $(IMAGESIZE) -d html -p html_data $@
|
||||
tools/$(BUILDDIR)crc_calculator -u $@
|
||||
|
||||
@@ -1,195 +1,100 @@
|
||||
# RTLPlayground
|
||||
A Playground for Firmware development for RTL8372/RTL8373 based 2.5GBit Switches.
|
||||
A Playground for Firmware development for advanced user of RTL8372/RTL8373 based 2.5GBit Switches.
|
||||
|
||||
For each hardware configuration of these devices, there is usually a managed and an
|
||||
umanaged version sold, with mostly identical hardware. The aim is to provide management
|
||||
features also for unmanaged devices with additional features such as Management VLAN,
|
||||
dhcp servers, multi-language support, IPv6 and TLS-encrypted web-pages.
|
||||
dhcp servers, multi-language support, IPv6 and TLS-encrypted web-pages. At present, however
|
||||
only the following features are provided:
|
||||
- A modern web-interface with mouse-over to display further information
|
||||
- A serial console interface to configure all features
|
||||
- IGMP to configure Multicast streaming
|
||||
- Port configuration showing detailed informtion about own and Link-partner advertised
|
||||
Speed settins and configuration of these settings on the local side
|
||||
- Per-port configuration of frame sizes (MTUs) for Jumbo-Frame support or limiting MTUs
|
||||
for particular devices
|
||||
- EEE (Energy Efficient Ethernet) can be configured per-port. Detailed information is
|
||||
provided for support offered by the link partner and the EEE status of a port.
|
||||
- VLAN configuration
|
||||
- SFP information is displayed on the inserted modules, the current sensor values such as
|
||||
temperatures, RX and TX power are displayed in the CLI and as mouse-over on the web
|
||||
- Mirror configuration
|
||||
- Link Aggregation Groups can be set up
|
||||
- Detailed information on port packet statistics
|
||||
- Configuration saved to flash via the web-interface
|
||||
- Firmware updates via the web
|
||||
- Installation as a firmware upgrade from the original web-interface
|
||||
|
||||
The playground currently provides a minimal alternative firmware for both the managed and unmanaged switches.
|
||||
When used with unmanaged switches, it will provide some management features such as
|
||||
setting up VLANs, mirroring ports and provide a Web-Server (currently no functions,
|
||||
really), but will need to be configured via a serial connection. Installation on
|
||||
managed devices only makes sense for developers, as plenty of features of the managed
|
||||
switches are not supported, yet.
|
||||
<img width="1420" height="623" alt="GUI" src="doc/images/gui.png" />
|
||||
|
||||
At this point, the firmware can be installed on the hardware as given below,
|
||||
all of the ports and SFP-slots will be supported. The following has been tested:
|
||||
On the keepLINK kp-9000-6hx-x (RTL8372 + RTL8221B 2.5GBit PHY: 5 x 2.5GBit + 1x 10GBit SFP+),
|
||||
at present the system will provide the same featurs as a dumb switch plus a tiny
|
||||
TCP stack that will allow to reply to ARP and ping messages, thus enabling pinging the device.
|
||||
VLAN and mirroring can be configured (but not saved to flash).
|
||||
The ports served by the RTL8372 will be 100M/1G/2.5G auto-detect. Port 5 to RTL8221B PHY
|
||||
SerDes configuration works and supports 1GBit and 2.5GBit Ethernet (SGMII/HISGMII).
|
||||
SFP module insert/removal identification and reading of the SFP EEProm works. SFP
|
||||
module configuration works, too, tested for 1G, 2.5G and 10G Ethernet and Fiber modules.
|
||||
While the firmware provides already considerable improvements over the original managed firmware,
|
||||
the firmware still lacks support for STP and the proprietary loop prevention
|
||||
protocols as well as DHCP. If you need these features, do not install the playground on your managed
|
||||
devices. In any case, installation is strongly discouraged unless you can at least make
|
||||
a backup of the original flash content via a SOIC clamp such as also used for BIOS
|
||||
backups and can re-install that firmware in case something is wrong. For this no soldering
|
||||
skills are necessary.
|
||||
|
||||
The 4-Port Ethernet + 2 Port SFP+ devices (e.g. KP-9000-6HX-x2) are fully supported, too
|
||||
(e.g. KP-9000-6hx-x2) with the same features as above. In particular all fiber/Ethernet
|
||||
modules work in both SFP+ ports.
|
||||
|
||||
On the 9-port devices with RTL8273 + RTL8224 (for example kp-9000-9xh-x) all ports will
|
||||
work for switching and CPU-access, the SFP+ port will work normally and TCP connectivity
|
||||
will work as above. Not all features of the RTL8224-ports (the first 4) have been tested.
|
||||
The firmware supports all hardware featues of devices with
|
||||
- 4 2.5GBit ports + 2 SFP+ ports
|
||||
- 5 2.5GBIT + 1 SFP+ port
|
||||
- 8 2.5GBit + 1 SFP+ port
|
||||
Devices sold usually have a fairly common design, however there may be differences in the LED
|
||||
configuration (switches have LEDs with different colours and use types of LEDs). The list
|
||||
of tested devices can be found in [Supported devices](doc/supported_devices.md).
|
||||
|
||||
To do meaningful development you will need to use a serial console, so soldering skills
|
||||
are required. Flashing must be done via a SOIC-8 PatchClamp or by soldering a socket
|
||||
for the flash chip.
|
||||
|
||||
UPDATE: The Code comes with a port of the [uIP](https://github.com/adamdunkels/uip)
|
||||
TCP/IP stack and includes a minimal web-server that can be used to work with the switch,
|
||||
so if you use a patch-clamp for updating the firmware (~3 USD/EUR), you can try this
|
||||
out without the need to solder anything. See the instructions below.
|
||||
Note that updating the firmware of a managed switch with the images created in this
|
||||
project via the OEM web-interface will not work, because it is currently unknown how
|
||||
to generate the require checksum, see this
|
||||
[issue](https://github.com/up-n-atom/SWTG118AS/issues/4).
|
||||
However, if you use the patch-clamp to flash, this is not a problem.
|
||||
|
||||
If you don't want to open your device, you can use the project's code to learn about the
|
||||
devices by looking at the image using e.g. Ghidra.
|
||||
devices by looking at the image using e.g. Ghidra. If you want to contribute to the
|
||||
design of the web-interface or get a feeling for the interface first, a standalone
|
||||
device simulator is provided, which runs entirely under Linux as a local webserver.
|
||||
|
||||
## Compiling
|
||||
Install the following particular build requisites (Debian 12, should work on Ubuntu)
|
||||
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:
|
||||
```
|
||||
sudo apt install sdcc xxd python
|
||||
sudo apt install make gcc sdcc xxd python-is-python3 libjson-c-dev
|
||||
```
|
||||
|
||||
Edit machine.h with an editor like vi or nano. Select the correct machine the firmware should build for.
|
||||
Now, building the firmware image should work:
|
||||
```
|
||||
$ make
|
||||
sdas8051 -plosgff crtstart.asm
|
||||
sdcc -mmcs51 -c rtlplayground.c
|
||||
sdcc -mmcs51 -c rtl837x_flash.c
|
||||
sdcc -mmcs51 -Wl-bHOME=0x100 -o rtlplayground.ihx crtstart.rel rtlplayground.rel rtl837x_flash.rel
|
||||
objcopy --input-target=ihex -O binary rtlplayground.ihx rtlplayground.img
|
||||
if [ -e rtlplayground.bin ]; then rm rtlplayground.bin; fi
|
||||
echo "0000000: 00 40" | xxd -r - rtlplayground.bin
|
||||
cat rtlplayground.img >> rtlplayground.bin
|
||||
make
|
||||
```
|
||||
Note, that the image generated ends in .bin, not .img, in order to make
|
||||
IMSProg happy.
|
||||
|
||||
Managed switches can be updated from the existing original firmware using an upgrade image.
|
||||
In the `installer`folder of the source code you will need to run `make` which will build
|
||||
an image out of `rtlplayground.bin` built in the previous step:
|
||||
```
|
||||
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
|
||||
cp ../output//rtlplayground.bin output/
|
||||
./output//updatebuilder -i output/rtlinstaller.ihx output/rtlplayground.bin
|
||||
Input file size: 524288
|
||||
Bytes read: 524288
|
||||
EOF
|
||||
Payload sum 1 is: 0x29d10
|
||||
Payload sum 2 is: 0x29d10
|
||||
Payload sum with header is: 0x2b0fc
|
||||
Payload sum is: 0xad8a75
|
||||
Header checksum is: 0x4c3
|
||||
```
|
||||
The resulting image can be found in `RTLPlayground/installer/output/rtlplayground.bin`
|
||||
> [!CAUTION]
|
||||
> DO NOT UPLOAD THE UPGADE IMAGE UNLESS YOU CAN MAKE A BACKUP USING A SOIC CLAMP OF THE
|
||||
> ORIGINAL FIRMWARE!
|
||||
|
||||
## Installation
|
||||
You can play with the image using ghidra or flash real Switch Hardware
|
||||
|
||||
### Supported Hardware
|
||||
If you do not have an RTL837x-based switch device such as the ones
|
||||
mentionned here: [Up-N-Atoms 2.5 GBit RTL Switch hacking guide]
|
||||
(https://github.com/up-n-atom/SWTG118AS) or one of the other that
|
||||
deployment was tested on, including:
|
||||
- keepLINK kp-9000-6hx-x2 (RTL8372: 4x 2.5GBit + 2x 10GBit SFP+)
|
||||
- keepLINK KP-9000-6XHML-X2, same as above, but Managed
|
||||
- keepLINK kp-9000-6hx-x (RTL8372 + RTL8221B 2.5GBit PHY: 5 x 2.5GBit + 1x 10GBit SFP+)
|
||||
- keepLINK kp-9000-9xh-x-eu (1 x RTL8373 + RTL8224: 8x 2.5GBit + 1x 10GBit SFP+)
|
||||
- Lianguo LG-SWTGW218AS (RTL8373 + RTL8224 PHY: 8x 2.5GBit + 1x 10GBit SFP+)
|
||||
- No-Name ZX-SWTGW215AS, managed version of kp-9000-6hx-x, ordered on
|
||||
AliExpress as keepLINK 5+1 port managed
|
||||
|
||||
### Understanding the image using ghidra
|
||||
Start ghidra, load file starting from offset 0x0002 into
|
||||
memory starting at 0x0000. The lengthe is 0x10000. Select generic 8051, big
|
||||
endian.
|
||||
|
||||
After loading, the boot vector is at 0x0000, which will jump to 0x0100 for
|
||||
the boot routine.
|
||||
|
||||
The firmware uses only bank 1 of the RTL837x since it is quite short.
|
||||
Otherwise the firmware would be organized as follows
|
||||
```
|
||||
--------------------------- 0x0000 ---------------------------------
|
||||
Boot-Vector
|
||||
ISRs
|
||||
Common Code
|
||||
Trampoline for inter-bank calls
|
||||
Inter-bank calls, calling trampoline, one for each callable function
|
||||
|
||||
----- Bank 1 0x4000 ------ ---- Bank 2 0x4000 ----- -------- .....
|
||||
Overlay 1 Overlay 2 Overlay n
|
||||
|
||||
--------- 0xffff --------- -------- 0xffff -------- -------- 0xffff
|
||||
```
|
||||
The RTL837x firmware images are organized as follows:
|
||||
The first 2 bytes of the image give the size of the prefetched data at the
|
||||
start of the CPU power up. The default is 0x4000 (bytes: 0x00 0x40), which
|
||||
means that the entire shared area of the code memory in all banks,
|
||||
0x4000 bytes is read immediately into the code RAM.
|
||||
|
||||
Common code starts at
|
||||
0x0002 in the image and has length 0x3ffd, the first bank starts at 0x4000
|
||||
in the image, is mapped to 0x4000 and has length 0xc000. The second bank
|
||||
starts at 0x10000, is mapped to 0x4000 and has length 0xc000. The third
|
||||
bank would start at 0x1c000 and would again be mapped to 0x4000.
|
||||
There are about 30 banks in use for managed switches, unmanaged ones use
|
||||
2-3, while the hardware would allow to use 0x3f banks, i.e. up to 4 MB of
|
||||
flash.
|
||||
|
||||
The current image uses Common BANK0 and the first BANK1 via sdccs __banked
|
||||
function keyword and custom banking trampoline code for the RTL837x in
|
||||
assembler.
|
||||
|
||||
|
||||
### Hardware supported by the code so far
|
||||
|
||||
-The following hardware is supported:
|
||||
- Clock generation, including different divider settings
|
||||
- Interrupt control for timer, serial, external irqs 0, 1
|
||||
- Serial console via SFRs
|
||||
- Flash operations via SFRs
|
||||
- Bank switching via SFRs
|
||||
- Access to Switch registers via SFRs
|
||||
- LED setup
|
||||
- Reset
|
||||
- Some switch settings such as MAC configuration
|
||||
- GPIO to detect SFP module insert/removal/RX-LOS
|
||||
- I2C to read SFP EEPROM on 1 and 2 SFP slot devices
|
||||
- NIC setup
|
||||
- L2 learning table access, L2 table flushing
|
||||
- VLAN setup/configuration
|
||||
- Port mirroring
|
||||
- Access to PHYs via MDIO (clause 45 via SFR):
|
||||
- Internal PHYs of RTL8372 and RTL8373
|
||||
- RTL8221 (1x2.5GBit port on devices with 5 ports)
|
||||
- RTL8224 (4x2.5GBit ports on devices with 8 ports)
|
||||
- SerDes settings of SoC via SFR:
|
||||
- Configure SFPs with 10Gbit/2.5Gbit/1Gbit (Ethernet and Fiber SFP(+) tested)
|
||||
- RTL8221, RTL8224
|
||||
- NIC TX and RX of packets via SFRs
|
||||
- send and receive Ethernet frames via SFRs and Switch registers
|
||||
- RTL-tags and VLAN ingress-tag decoding for CPU-port
|
||||
|
||||
Ethernet frame RX IRQ via IRQ1 is conceptually understood, but not activated. RX is
|
||||
currently done via polling, which allows ping-times of <10ms.
|
||||
|
||||
The RTL8372/3 have 256 bytes of internal RAM (INTMEM) accessible through MOV
|
||||
instructions, which are used for the stack and important globals. Some of
|
||||
these are bit-adressable, e.g. for storing global flags.
|
||||
|
||||
Additionally, 64kB of extended RAM (XMEM) is built in, which is accessed
|
||||
through the MOVX instruction. It is used for global variables, for most
|
||||
of the function argument passing that is not done using the 8 registers
|
||||
R0-R7 or registers A/B, and for local variables (which requires extremely
|
||||
careful planning). The flash memory is transparently accessible for code
|
||||
being executed and can be used to store configuration. Access is done through
|
||||
the MOVC instruction, possibly setting the bank register before and
|
||||
resetting it to access the entire 4MB space. Code is prefetched from flash
|
||||
and cached in a small RAM automatically by the HW.
|
||||
|
||||
The peripherial functions are accessed through 2 different mechanisms:
|
||||
- Special Function Registers (SFRs, 0x80-0xff) for banking, timers, UART, access to
|
||||
switch registers, MDIO, SPI (flash) and NIC transfers. Some SFRs are not
|
||||
used for HW purposes and can be used as RAM. Some SFRs are bit-adressable,
|
||||
allowing for very tight event wait loops (a single 2-byte instruction).
|
||||
- 0x10000 switch registers, which appear to be very similar to the registers
|
||||
of the RTL838x, for which source code and datasheets are available. This
|
||||
controls clock dividers, GPIO/LEDs and general switch functionality.
|
||||
|
||||
The playground image shows access to the different types of memory using the
|
||||
SDCC compiler. Any support of Linux or e.g. Zephyr would require porting gcc.
|
||||
There are FreeRTOS ports to 8051 processors using sdcc, however.
|
||||
|
||||
|
||||
### Installation on an actual switch
|
||||
You can play with the image using ghidra or flash real Switch Hardware. For
|
||||
ghidra see this information about [Ghidra images](ghidra.md).
|
||||
|
||||
> [!CAUTION]
|
||||
> NOTE THAT WHILE THIS PROCEDURE HAS BEEN SUCCESSFULLY TESTED ON ALL DEVICES ABOVE,
|
||||
@@ -197,8 +102,15 @@ There are FreeRTOS ports to 8051 processors using sdcc, however.
|
||||
> ANY OTHER EQUIPMENT INVOLVED OR HARM YOURSELF BY OPENING THE ELECTRONIC
|
||||
> DEVICE. OPENING THE SWITCH WILL VOID ITS WARRANTY.
|
||||
|
||||
There is no support for uploading the firmware via ethernet. Instead you
|
||||
need to open the switch and flash the image directly onto the flash chip,
|
||||
You can upload the upgrade image of managed switches via the web interface of the
|
||||
original firmware just as if you were installing a firmware upgrade. However,
|
||||
this is strongly discouraged, as you may brick your device, unless you can make
|
||||
firmware backups via a SOIC clamp or soldered flash socket, first!
|
||||
|
||||
For unmanaged devices, the only way to install RTLPlayground is by flashing the
|
||||
Flash memory directly.
|
||||
|
||||
You will need to open your switch to flash the image directly onto the flash chip,
|
||||
which is done easiest using a SOIC-8 clip (alternatively you de-solder the
|
||||
flash chip and install a SOIC adapter):
|
||||
- Disconnect power from switch
|
||||
@@ -217,6 +129,10 @@ devices, set 8N1 @ 115200 baud and power up the switch.
|
||||
The device will perform some examples and provide a minimal console, the
|
||||
documentation of which can be found in the source code rtlplayground.c`.
|
||||
|
||||
## The web-interface
|
||||
The web-interface can be reached under the [default 192.168.10.247](http://192.168.10.247).
|
||||
The default password is `1234`.
|
||||
|
||||
## The command line
|
||||
The command line is very rudimentary and mostly for testing purposes.
|
||||
The following is a boot-log with some examples:
|
||||
@@ -307,9 +223,12 @@ Enjoy playing!
|
||||
## Other documents
|
||||
The following documents give further documentation on specific features of
|
||||
the RTL837x SoCs:
|
||||
- [RTL8372/3 Feature support](doc/hardware.md)
|
||||
- [CPU Port](doc/CpuPort.md)
|
||||
- [L2 learning](doc/l2.md)
|
||||
- [Mirroring](doc/mirroring.md)
|
||||
- [CPU Port](doc/CpuPort.md)
|
||||
- [IGMP (IP-MC streaming)](doc/igmp.md)
|
||||
- [SFP+ ports](doc/sfp.md)
|
||||
- [Trunking aka. port aggregation](doc/trunking.md)
|
||||
- [VLAN](doc/vlan.md)
|
||||
- [Modifications and Flash replacement](doc/mods.md)
|
||||
|
||||
@@ -0,0 +1,208 @@
|
||||
#include "cmd_parser.h"
|
||||
#include "machine.h"
|
||||
|
||||
#pragma codeseg BANK2
|
||||
#pragma constseg BANK2
|
||||
|
||||
// Position in the serial buffer
|
||||
__xdata uint8_t l;
|
||||
// Properties of currently edited command line in cmd_buffer[CMD_BUF_SIZE]
|
||||
__xdata uint8_t cursor;
|
||||
__xdata uint8_t cmd_line_len;
|
||||
__xdata uint8_t current_cmdline[CMD_BUF_SIZE];
|
||||
__xdata uint16_t history_editptr;
|
||||
|
||||
|
||||
extern __xdata uint8_t cmd_history[CMD_HISTORY_SIZE];
|
||||
extern __xdata uint16_t cmd_history_ptr;
|
||||
|
||||
void cmd_editor_init(void) __banked
|
||||
{
|
||||
l = sbuf_ptr; // We have printed out entered characters until l
|
||||
cursor = 0;
|
||||
cmd_line_len = 0;
|
||||
cmd_available = 0;
|
||||
history_editptr = 0xffff;
|
||||
}
|
||||
|
||||
/*
|
||||
* Allows editing the current command line held in cmd_buffer[CMD_BUF_SIZE] by
|
||||
* identifying new characters typed or up to 4-byte escape sequences in the
|
||||
* serial buffer ring sbuf[SBUF_SIZE].
|
||||
* Upon detecting new characters or escape sequences, the cmd_buffer and the
|
||||
* representation of the command line in the terminal are updated.
|
||||
* To debug, the easist is to interpose a tty-interceptor between the physical
|
||||
* serial device and a logical one created by interceptty:
|
||||
* sudo interceptty -s 'ispeed 115200 ospeed 115200' /dev/ttyUSB0 /dev/tmpS
|
||||
* picocom -b 115200 /dev/tmpS
|
||||
*/
|
||||
void cmd_edit(void) __banked
|
||||
{
|
||||
while (l != sbuf_ptr) {
|
||||
if (sbuf[l] >= ' ' && sbuf[l] < 127) { // A printable character, copy to command line
|
||||
if (cmd_line_len >= CMD_BUF_SIZE)
|
||||
continue;
|
||||
write_char(sbuf[l]);
|
||||
// Shift buffer to right
|
||||
for (uint8_t i = cmd_line_len; i > cursor; i--)
|
||||
cmd_buffer[i] = cmd_buffer[i-1];
|
||||
// Insert char in comand buffer
|
||||
cmd_buffer[cursor++] = sbuf[l];
|
||||
cmd_line_len++;
|
||||
// Print rest of line
|
||||
for (uint8_t i = cursor; i < cmd_line_len; i++)
|
||||
write_char(cmd_buffer[i]);
|
||||
// Move backwards
|
||||
for (uint8_t i = cursor; i < cmd_line_len; i++)
|
||||
write_char('\010'); // BS works like cursor-left
|
||||
} else if (sbuf[l] == '\033') { // ESC-Sequence
|
||||
// Wait until we have at least 3 characters including the ESC character in the serial buffer
|
||||
if (((sbuf_ptr + SBUF_SIZE - l) & SBUF_MASK) < 3)
|
||||
continue;
|
||||
if (((sbuf_ptr > l ? sbuf_ptr - l : SBUF_SIZE + sbuf_ptr - l) >= 4)
|
||||
&& sbuf[l] == '\033' && sbuf[(l + 1) & SBUF_MASK] == '[' && sbuf[(l + 2) & SBUF_MASK] == '3' && sbuf[(l + 3) & SBUF_MASK] == '~') { // DEL
|
||||
if (cursor < cmd_line_len) {
|
||||
write_char('\033'); write_char('['); write_char('1'); write_char('P'); // Delete to end of line
|
||||
cmd_line_len--;
|
||||
for (uint8_t i = cursor; i < cmd_line_len; i++) {
|
||||
cmd_buffer[i] = cmd_buffer[i+1];
|
||||
write_char(cmd_buffer[i]);
|
||||
}
|
||||
for (uint8_t i = cursor; i < cmd_line_len; i++)
|
||||
write_char('\010');
|
||||
}
|
||||
l += 4;
|
||||
l &= SBUF_MASK;
|
||||
continue;
|
||||
} else if (sbuf[l] == '\033' && sbuf[(l + 1) & SBUF_MASK] == '[' && sbuf[(l + 2) & SBUF_MASK] == 'D') { // <CURSOR-LEFT>
|
||||
if (cursor) {
|
||||
write_char('\010'); // BS works like cursor-left
|
||||
cursor--;
|
||||
}
|
||||
l += 3;
|
||||
l &= SBUF_MASK;
|
||||
continue;
|
||||
} else if (sbuf[l] == '\033' && sbuf[(l + 1) & SBUF_MASK] == '[' && sbuf[(l + 2) & SBUF_MASK] == 'C') { // <CURSOR-RIGHT>
|
||||
if (cursor < cmd_line_len) {
|
||||
write_char('\033'); write_char('['); write_char('C');
|
||||
cursor++;
|
||||
}
|
||||
l += 3;
|
||||
l &= SBUF_MASK;
|
||||
continue;
|
||||
} else if (sbuf[l] == '\033' && sbuf[(l + 1) & SBUF_MASK] == '[' && sbuf[(l + 2) & SBUF_MASK] == 'A') { // <CURSOR-UP>
|
||||
for (uint8_t i = 0; i < cmd_line_len; i++)
|
||||
current_cmdline[i] = cmd_buffer[i];
|
||||
current_cmdline[cmd_line_len] = 0;
|
||||
__xdata uint16_t p;
|
||||
if (history_editptr == 0xffff)
|
||||
p = (cmd_history_ptr - 2) & CMD_HISTORY_MASK;
|
||||
else
|
||||
p = history_editptr;
|
||||
// Move cursor to beginning of line
|
||||
write_char('\033'); write_char('['); itoa(cursor + 2); write_char('D');
|
||||
cursor = 0;
|
||||
while (cmd_history[p] && cmd_history[p] != '\n') {
|
||||
cursor++;
|
||||
p--;
|
||||
p &= CMD_HISTORY_MASK;
|
||||
}
|
||||
history_editptr = (p - 1) & CMD_HISTORY_MASK;
|
||||
p = (p + 1) & CMD_HISTORY_MASK;
|
||||
if (cursor) {
|
||||
print_string("\033[2K> "); // Clear entire line: ^[[2K and print new prompt
|
||||
for (uint8_t i = 0; i < cursor; i++) {
|
||||
cmd_buffer[i] = cmd_history[p];
|
||||
write_char(cmd_buffer[i]);
|
||||
p = (p+1) & CMD_HISTORY_MASK;
|
||||
}
|
||||
cmd_line_len = cursor;
|
||||
} else {
|
||||
print_string("\033[2C"); // Move 2 right to start of editing space
|
||||
}
|
||||
l += 3;
|
||||
l &= SBUF_MASK;
|
||||
continue;
|
||||
} else if (sbuf[l] == '\033' && sbuf[(l + 1) & SBUF_MASK] == '[' && sbuf[(l + 2) & SBUF_MASK] == 'B') { // <CURSOR-DOWN>
|
||||
if (history_editptr != 0xffff) {
|
||||
__xdata uint16_t p = (history_editptr + 2) & CMD_HISTORY_MASK;
|
||||
// Move cursor to beginning of line
|
||||
write_char('\033'); write_char('['); itoa(cursor + 2); write_char('D');
|
||||
print_string("\033[2K> "); // Clear entire line: ^[[2K and print new prompt
|
||||
uint8_t i = 0;
|
||||
while (cmd_history[p] && cmd_history[p] != '\n') {
|
||||
p = (p + 1) & CMD_HISTORY_MASK;
|
||||
}
|
||||
p = (p + 1) & CMD_HISTORY_MASK;
|
||||
while (cmd_history[p] && cmd_history[p] != '\n') {
|
||||
cmd_buffer[i] = cmd_history[p];
|
||||
write_char(cmd_buffer[i++]);
|
||||
p = (p + 1) & CMD_HISTORY_MASK;
|
||||
}
|
||||
history_editptr = (p - 1) & CMD_HISTORY_MASK;
|
||||
if (!i) {
|
||||
if (current_cmdline[i]) {
|
||||
while (current_cmdline[i]) {
|
||||
cmd_buffer[i] = current_cmdline[i];
|
||||
write_char(cmd_buffer[i++]);
|
||||
}
|
||||
} else {
|
||||
write_char('\033'); write_char('['); write_char('C');
|
||||
}
|
||||
history_editptr = 0xffff;
|
||||
// Move cursor right
|
||||
}
|
||||
cmd_line_len = i;
|
||||
cursor = 0;
|
||||
// Move cursor again to beginning of line
|
||||
write_char('\033'); write_char('['); itoa(i); write_char('D');
|
||||
} else {
|
||||
// If we are at the last entry of the history, just move the cursor to the end of the line
|
||||
if (cursor < cmd_line_len) {
|
||||
write_char('\033'); write_char('['); itoa(cmd_line_len - cursor); write_char('C');
|
||||
}
|
||||
cursor = cmd_line_len;
|
||||
}
|
||||
l += 3;
|
||||
l &= SBUF_MASK;
|
||||
continue;
|
||||
} else { // An unknown or not yet complete Escape sequence: wait
|
||||
continue;
|
||||
}
|
||||
} else if (sbuf[l] == 127) { // Backspace
|
||||
if (cursor > 0) {
|
||||
write_char('\010');
|
||||
for (uint8_t i = cursor; i < cmd_line_len; i++)
|
||||
write_char(cmd_buffer[i]);
|
||||
write_char(' '); // Overwrite end of line
|
||||
// Move backwards n steps:
|
||||
for (uint8_t i = cursor; i <= cmd_line_len; i++)
|
||||
write_char('\010');
|
||||
cursor--;
|
||||
for (uint8_t i = cursor; i <= cmd_line_len; i++)
|
||||
cmd_buffer[i] = cmd_buffer[i+1];
|
||||
cmd_line_len--;
|
||||
}
|
||||
}
|
||||
// If the command buffer is currently in use, we cannot copy to it
|
||||
if (cmd_available)
|
||||
break;
|
||||
// Check whether return was pressed:
|
||||
if (sbuf[l] == '\n' || sbuf[l] == '\r') {
|
||||
write_char('\n');
|
||||
cmd_buffer[cmd_line_len] = '\0';
|
||||
// write_char('>'); print_string_x(cmd_buffer); write_char('<');
|
||||
// If there is a command we print the prompt after execution
|
||||
// otherwise immediately because there is nothing to execute
|
||||
if (cmd_line_len)
|
||||
cmd_available = 1;
|
||||
else
|
||||
print_string("\n> ");
|
||||
cursor = 0;
|
||||
cmd_line_len = 0;
|
||||
history_editptr = 0xffff;
|
||||
}
|
||||
l++;
|
||||
l &= SBUF_MASK;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,9 @@
|
||||
#ifndef _CMD_EDITOR_H_
|
||||
#define _CMD_EDITOR_H_
|
||||
|
||||
#include <stdint.h>
|
||||
|
||||
void cmd_editor_init(void) __banked;
|
||||
void cmd_edit(void) __banked;
|
||||
|
||||
#endif
|
||||
@@ -5,10 +5,12 @@
|
||||
|
||||
#include "rtl837x_common.h"
|
||||
|
||||
extern __xdata uint8_t cmd_buffer[SBUF_SIZE];
|
||||
extern __xdata uint8_t cmd_buffer[CMD_BUF_SIZE];
|
||||
extern __xdata uint8_t cmd_available;
|
||||
|
||||
uint8_t cmd_tokenize(void) __banked;
|
||||
void cmd_parser(void) __banked;
|
||||
void execute_config(void) __banked;
|
||||
void print_sw_version(void) __banked;
|
||||
void clear_command_history(void) __banked;
|
||||
#endif
|
||||
|
||||
@@ -1,44 +1,4 @@
|
||||
.globl __start__stack
|
||||
;--------------------------------------------------------
|
||||
; Stack segment in internal ram
|
||||
;--------------------------------------------------------
|
||||
.area SSEG (DATA)
|
||||
__start__stack:
|
||||
.ds 1
|
||||
|
||||
.area VECTOR (CODE)
|
||||
.globl __interrupt_vect
|
||||
__interrupt_vect:
|
||||
ljmp __sdcc_gsinit_startup
|
||||
ljmp _isr_ext0 ; 0x03
|
||||
.ds 5
|
||||
ljmp _isr_timer0 ; 0x0b
|
||||
.ds 5
|
||||
ljmp _isr_ext1 ; 0x13
|
||||
.ds 5
|
||||
reti
|
||||
.ds 7
|
||||
ljmp _isr_serial ; 0x23
|
||||
.ds 5
|
||||
reti ; 0x2b TIMER 2 IRQ
|
||||
.ds 7
|
||||
reti ; 0x33 NOT used by DW8051
|
||||
.ds 7
|
||||
reti ; 0x3b Serial port 1 RX/TX IRQ
|
||||
.ds 7
|
||||
ljmp _isr_ext2 ; 0x43
|
||||
.ds 5
|
||||
ljmp _isr_ext3 ; 0x4b
|
||||
|
||||
.globl __start__stack
|
||||
|
||||
.area GSINIT0 (CODE)
|
||||
|
||||
__sdcc_gsinit_startup::
|
||||
mov sp,#__start__stack - 1
|
||||
|
||||
.area GSFINAL (CODE)
|
||||
ljmp _bootloader
|
||||
|
||||
__sdcc_banked_call::
|
||||
push _PSBANK
|
||||
|
||||
@@ -0,0 +1,18 @@
|
||||
#ifndef __DEBUG_H__
|
||||
#define __DEBUG_H__
|
||||
|
||||
#ifdef DEBUG
|
||||
#define dbg_string(s) print_string(s)
|
||||
#define dbg_string_x(s) print_string_x(s)
|
||||
#define dbg_byte(s) print_byte(s)
|
||||
#define dbg_short(s) print_short(s)
|
||||
#define dbg_char(s) write_char(s)
|
||||
#else
|
||||
#define dbg_string(s)
|
||||
#define dbg_string_x(s)
|
||||
#define dbg_byte(s)
|
||||
#define dbg_short(s)
|
||||
#define dbg_char(s)
|
||||
#endif
|
||||
|
||||
#endif
|
||||
@@ -0,0 +1,637 @@
|
||||
/*
|
||||
* This is a DHCP client implementation for the RTL837x-based switches
|
||||
*/
|
||||
|
||||
// #define REGDBG
|
||||
// #define DEBUG
|
||||
|
||||
#include <stdint.h>
|
||||
#include "rtl837x_sfr.h"
|
||||
#include "rtl837x_common.h"
|
||||
#include "dhcp.h"
|
||||
#include "uip.h"
|
||||
#include "uip/uip.h"
|
||||
|
||||
__xdata struct dhcp_state dhcp_state;
|
||||
__xdata uip_ipaddr_t server;
|
||||
|
||||
#define BOOTP_REQUEST 1
|
||||
#define BOOTP_REPY 2
|
||||
|
||||
#define DHCP_HW_TYPE_ETH 1
|
||||
|
||||
#define DHCP_SUBNET_MASK 1
|
||||
#define DHCP_SUBNET_MASK_LEN 4
|
||||
#define DHCP_ROUTER 3
|
||||
#define DHCP_ROUTER_LEN 4
|
||||
#define DHCP_DNS 6
|
||||
#define DHCP_DNS_LEN 4
|
||||
#define DHCP_BROADCAST 28
|
||||
#define DHCP_BROADCAST_LEN 4
|
||||
#define DHCP_SERVER_ID 54
|
||||
#define DHCP_SERVER_ID_LEN 4
|
||||
#define DHCP_MESSAGE_TYPE 53
|
||||
#define DHCP_MESSAGE_TYPE_LEN 1
|
||||
#define DHCP_MESSAGE_DISCOVER 1
|
||||
#define DHCP_MESSAGE_OFFER 2
|
||||
#define DHCP_MESSAGE_REQUEST 3
|
||||
#define DHCP_MESSAGE_NACK 4
|
||||
#define DHCP_MESSAGE_ACK 5
|
||||
#define DHCP_LEASE 51
|
||||
#define DHCP_LEASE_LEN 4
|
||||
#define DHCP_RENEWAL 58
|
||||
#define DHCP_RENEWAL_LEN 4
|
||||
#define DHCP_REBIND 59
|
||||
#define DHCP_REBIND_LEN 4
|
||||
#define DHCP_CLIENT_ID 61
|
||||
#define DHCP_CLIENT_ID_LEN 7
|
||||
#define DHCP_REQUEST_IP 50
|
||||
#define DHCP_REQUEST_IP_LEN 4
|
||||
#define DHCP_CLIENT_NAME 12
|
||||
#define DHCP_PARAMS 55
|
||||
#define DHCP_VENDOR_ID 60
|
||||
#define DHCP_CLIENT_ID 61
|
||||
#define DHCP_PARAM_SUBNET 1
|
||||
#define DHCP_PARAM_ROUTER 3
|
||||
#define DHCP_PARAM_DNS 6
|
||||
#define DHCP_END 255
|
||||
|
||||
#define LEASE_TIME 43200
|
||||
#define RENEWAL_TIME 21600
|
||||
#define REBIND_TIME 21600
|
||||
|
||||
#pragma codeseg BANK2
|
||||
#pragma constseg BANK2
|
||||
|
||||
|
||||
struct dhcp_pkt {
|
||||
uint8_t type;
|
||||
uint8_t hw;
|
||||
uint8_t hw_len;
|
||||
uint8_t hops;
|
||||
uint32_t tid;
|
||||
uint16_t delay;
|
||||
uint16_t flags;
|
||||
uint8_t client_ip[4];
|
||||
uint8_t your_ip[4];
|
||||
uint8_t next_server_ip[4];
|
||||
uint8_t relay_ip[4];
|
||||
uint8_t client_addr[6];
|
||||
uint8_t client_pad[10];
|
||||
uint8_t server_name[64];
|
||||
uint8_t file[128];
|
||||
uint8_t cookie[4];
|
||||
};
|
||||
|
||||
#define DHCP_P ((__xdata struct dhcp_pkt *)uip_appdata)
|
||||
#define DHCP_OPT ((__xdata uint8_t *)(uip_appdata) + sizeof (struct dhcp_pkt))
|
||||
|
||||
__xdata uint32_t long_value;
|
||||
__xdata struct dhcpd_cstate cstates[DHCPD_MAX_CLIENTS];
|
||||
__xdata uint8_t client_idx;
|
||||
__xdata uint16_t dhcpd_vlan;
|
||||
|
||||
void dhcp_print_ip(uint8_t *a)
|
||||
{
|
||||
itoa(a[0]); write_char('.');
|
||||
itoa(a[1]); write_char('.');
|
||||
itoa(a[2]); write_char('.');
|
||||
itoa(a[3]);
|
||||
}
|
||||
|
||||
|
||||
void dhcp_prepare_msg(void)
|
||||
{
|
||||
DHCP_P->type = BOOTP_REQUEST;
|
||||
DHCP_P->hw = DHCP_HW_TYPE_ETH;
|
||||
DHCP_P->hw_len = 6;
|
||||
DHCP_P->hops = 0;
|
||||
|
||||
DHCP_P->tid = dhcp_state.transaction_id; // In network byte order
|
||||
DHCP_P->delay = HTONS(0);
|
||||
DHCP_P->flags = 0;
|
||||
// Clear fields client_ip to bootp_file
|
||||
memset(DHCP_P->client_ip, 0, 224);
|
||||
memcpy(DHCP_P->client_addr, uip_ethaddr.addr, 6);
|
||||
DHCP_P->cookie[0] = 0x63;
|
||||
DHCP_P->cookie[1] = 0x82;
|
||||
DHCP_P->cookie[2] = 0x53;
|
||||
DHCP_P->cookie[3] = 0x63;
|
||||
}
|
||||
|
||||
|
||||
void dhcp_addopt_client_id(void)
|
||||
{
|
||||
DHCP_OPT[dhcp_state.opt_ptr++] = DHCP_CLIENT_ID;
|
||||
DHCP_OPT[dhcp_state.opt_ptr++] = DHCP_CLIENT_ID_LEN;
|
||||
DHCP_OPT[dhcp_state.opt_ptr++] = DHCP_HW_TYPE_ETH;
|
||||
memcpy(&DHCP_OPT[dhcp_state.opt_ptr], uip_ethaddr.addr, 6);
|
||||
dhcp_state.opt_ptr += 6;
|
||||
}
|
||||
|
||||
|
||||
void dhcp_addopt_request_ip(void)
|
||||
{
|
||||
DHCP_OPT[dhcp_state.opt_ptr++] = DHCP_REQUEST_IP;
|
||||
DHCP_OPT[dhcp_state.opt_ptr++] = DHCP_REQUEST_IP_LEN;
|
||||
DHCP_OPT[dhcp_state.opt_ptr++] = dhcp_state.current_ip[0];
|
||||
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];
|
||||
}
|
||||
|
||||
|
||||
void dhcp_addopt_server_id(void)
|
||||
{
|
||||
DHCP_OPT[dhcp_state.opt_ptr++] = DHCP_SERVER_ID;
|
||||
DHCP_OPT[dhcp_state.opt_ptr++] = DHCP_SERVER_ID_LEN;
|
||||
DHCP_OPT[dhcp_state.opt_ptr++] = dhcp_state.server[0];
|
||||
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];
|
||||
}
|
||||
|
||||
|
||||
void dhcp_addopt_subnet(void)
|
||||
{
|
||||
DHCP_OPT[dhcp_state.opt_ptr++] = DHCP_SUBNET_MASK;
|
||||
DHCP_OPT[dhcp_state.opt_ptr++] = DHCP_SUBNET_MASK_LEN;
|
||||
DHCP_OPT[dhcp_state.opt_ptr++] = dhcp_state.subnet[0];
|
||||
DHCP_OPT[dhcp_state.opt_ptr++] = dhcp_state.subnet[1];
|
||||
DHCP_OPT[dhcp_state.opt_ptr++] = dhcp_state.subnet[2];
|
||||
DHCP_OPT[dhcp_state.opt_ptr++] = dhcp_state.subnet[3];
|
||||
}
|
||||
|
||||
|
||||
void dhcp_addopt_router(void)
|
||||
{
|
||||
DHCP_OPT[dhcp_state.opt_ptr++] = DHCP_ROUTER;
|
||||
DHCP_OPT[dhcp_state.opt_ptr++] = DHCP_ROUTER_LEN;
|
||||
DHCP_OPT[dhcp_state.opt_ptr++] = dhcp_state.router[0];
|
||||
DHCP_OPT[dhcp_state.opt_ptr++] = dhcp_state.router[1];
|
||||
DHCP_OPT[dhcp_state.opt_ptr++] = dhcp_state.router[2];
|
||||
DHCP_OPT[dhcp_state.opt_ptr++] = dhcp_state.router[3];
|
||||
}
|
||||
|
||||
|
||||
void dhcp_addopt_lease(void)
|
||||
{
|
||||
DHCP_OPT[dhcp_state.opt_ptr++] = DHCP_LEASE;
|
||||
DHCP_OPT[dhcp_state.opt_ptr++] = DHCP_LEASE_LEN;
|
||||
DHCP_OPT[dhcp_state.opt_ptr++] = 0;
|
||||
DHCP_OPT[dhcp_state.opt_ptr++] = 0;
|
||||
DHCP_OPT[dhcp_state.opt_ptr++] = LEASE_TIME >> 8;
|
||||
DHCP_OPT[dhcp_state.opt_ptr++] = LEASE_TIME & 0xff;
|
||||
}
|
||||
|
||||
|
||||
void dhcp_addopt_renewal(void)
|
||||
{
|
||||
DHCP_OPT[dhcp_state.opt_ptr++] = DHCP_RENEWAL;
|
||||
DHCP_OPT[dhcp_state.opt_ptr++] = DHCP_RENEWAL_LEN;
|
||||
DHCP_OPT[dhcp_state.opt_ptr++] = 0;
|
||||
DHCP_OPT[dhcp_state.opt_ptr++] = 0;
|
||||
DHCP_OPT[dhcp_state.opt_ptr++] = RENEWAL_TIME >> 8;
|
||||
DHCP_OPT[dhcp_state.opt_ptr++] = RENEWAL_TIME & 0xff;
|
||||
}
|
||||
|
||||
|
||||
void dhcp_addopt_rebind(void)
|
||||
{
|
||||
DHCP_OPT[dhcp_state.opt_ptr++] = DHCP_REBIND;
|
||||
DHCP_OPT[dhcp_state.opt_ptr++] = DHCP_REBIND_LEN;
|
||||
DHCP_OPT[dhcp_state.opt_ptr++] = 0;
|
||||
DHCP_OPT[dhcp_state.opt_ptr++] = 0;
|
||||
DHCP_OPT[dhcp_state.opt_ptr++] = REBIND_TIME >> 8;
|
||||
DHCP_OPT[dhcp_state.opt_ptr++] = REBIND_TIME & 0xff;
|
||||
}
|
||||
|
||||
|
||||
void dhcp_send_discover(void)
|
||||
{
|
||||
print_string("dhcp_send_discover called\n");
|
||||
dhcp_prepare_msg();
|
||||
|
||||
dhcp_state.opt_ptr = 0;
|
||||
DHCP_OPT[dhcp_state.opt_ptr++] = DHCP_MESSAGE_TYPE;
|
||||
DHCP_OPT[dhcp_state.opt_ptr++] = DHCP_MESSAGE_TYPE_LEN;
|
||||
DHCP_OPT[dhcp_state.opt_ptr++] = DHCP_MESSAGE_DISCOVER;
|
||||
|
||||
dhcp_addopt_client_id();
|
||||
dhcp_addopt_request_ip();
|
||||
|
||||
DHCP_OPT[dhcp_state.opt_ptr++] = DHCP_PARAMS;
|
||||
DHCP_OPT[dhcp_state.opt_ptr++] = 3;
|
||||
DHCP_OPT[dhcp_state.opt_ptr++] = DHCP_PARAM_SUBNET;
|
||||
DHCP_OPT[dhcp_state.opt_ptr++] = DHCP_PARAM_ROUTER;
|
||||
DHCP_OPT[dhcp_state.opt_ptr++] = DHCP_PARAM_DNS;
|
||||
|
||||
DHCP_OPT[dhcp_state.opt_ptr++] = DHCP_END;
|
||||
// Padding to 300 bytes
|
||||
DHCP_OPT[dhcp_state.opt_ptr++] = 0;
|
||||
DHCP_OPT[dhcp_state.opt_ptr++] = 0;
|
||||
DHCP_OPT[dhcp_state.opt_ptr++] = 0;
|
||||
DHCP_OPT[dhcp_state.opt_ptr++] = 0;
|
||||
DHCP_OPT[dhcp_state.opt_ptr++] = 0;
|
||||
DHCP_OPT[dhcp_state.opt_ptr++] = 0;
|
||||
|
||||
uip_udp_send(sizeof(struct dhcp_pkt) + dhcp_state.opt_ptr);
|
||||
dhcp_state.state = DHCP_DISCOVER_SENT;
|
||||
dhcp_state.ticks = SYS_TICK_HZ;
|
||||
dhcp_state.dhcp_timer = 30; // Timeout for discover
|
||||
}
|
||||
|
||||
|
||||
void dhcp_send_request(void)
|
||||
{
|
||||
print_string("dhcp_send_request called\n");
|
||||
dhcp_prepare_msg();
|
||||
|
||||
dhcp_state.opt_ptr = 0;
|
||||
DHCP_OPT[dhcp_state.opt_ptr++] = DHCP_MESSAGE_TYPE;
|
||||
DHCP_OPT[dhcp_state.opt_ptr++] = DHCP_MESSAGE_TYPE_LEN;
|
||||
DHCP_OPT[dhcp_state.opt_ptr++] = DHCP_MESSAGE_REQUEST;
|
||||
|
||||
dhcp_addopt_client_id();
|
||||
dhcp_addopt_request_ip();
|
||||
dhcp_addopt_server_id();
|
||||
|
||||
DHCP_OPT[dhcp_state.opt_ptr++] = DHCP_PARAMS;
|
||||
DHCP_OPT[dhcp_state.opt_ptr++] = 3;
|
||||
DHCP_OPT[dhcp_state.opt_ptr++] = DHCP_PARAM_SUBNET;
|
||||
DHCP_OPT[dhcp_state.opt_ptr++] = DHCP_PARAM_ROUTER;
|
||||
DHCP_OPT[dhcp_state.opt_ptr++] = DHCP_PARAM_DNS;
|
||||
|
||||
DHCP_OPT[dhcp_state.opt_ptr++] = DHCP_END;
|
||||
// Padding to 300 bytes
|
||||
DHCP_OPT[dhcp_state.opt_ptr++] = 0;
|
||||
DHCP_OPT[dhcp_state.opt_ptr++] = 0;
|
||||
DHCP_OPT[dhcp_state.opt_ptr++] = 0;
|
||||
DHCP_OPT[dhcp_state.opt_ptr++] = 0;
|
||||
DHCP_OPT[dhcp_state.opt_ptr++] = 0;
|
||||
DHCP_OPT[dhcp_state.opt_ptr++] = 0;
|
||||
|
||||
uip_udp_send(sizeof(struct dhcp_pkt) + dhcp_state.opt_ptr);
|
||||
dhcp_state.state = DHCP_REQUEST_SENT;
|
||||
dhcp_state.ticks = SYS_TICK_HZ;
|
||||
dhcp_state.dhcp_timer = 30; // Timeout for request
|
||||
}
|
||||
|
||||
|
||||
void dhcp_send_reply(uint8_t rtype)
|
||||
{
|
||||
print_string("dhcp_send_reply called\n");
|
||||
dhcp_prepare_msg();
|
||||
DHCP_P->type = BOOTP_REPY;
|
||||
DHCP_P->client_addr[0] = cstates[client_idx].mac[0]; DHCP_P->client_addr[1] = cstates[client_idx].mac[1];
|
||||
DHCP_P->client_addr[2] = cstates[client_idx].mac[2]; DHCP_P->client_addr[3] = cstates[client_idx].mac[3];
|
||||
DHCP_P->client_addr[4] = cstates[client_idx].mac[4]; DHCP_P->client_addr[5] = cstates[client_idx].mac[5];
|
||||
|
||||
if (rtype != DHCP_MESSAGE_NACK) {
|
||||
DHCP_P->your_ip[0] = dhcp_state.server[0];
|
||||
DHCP_P->your_ip[1] = dhcp_state.server[1];
|
||||
DHCP_P->your_ip[2] = dhcp_state.server[2];
|
||||
DHCP_P->your_ip[3] = DHCPD_START_IP + client_idx;
|
||||
}
|
||||
|
||||
dhcp_state.opt_ptr = 0;
|
||||
DHCP_OPT[dhcp_state.opt_ptr++] = DHCP_MESSAGE_TYPE;
|
||||
DHCP_OPT[dhcp_state.opt_ptr++] = DHCP_MESSAGE_TYPE_LEN;
|
||||
DHCP_OPT[dhcp_state.opt_ptr++] = rtype;
|
||||
|
||||
if (rtype != DHCP_MESSAGE_NACK) {
|
||||
dhcp_addopt_subnet();
|
||||
dhcp_addopt_router();
|
||||
dhcp_addopt_server_id();
|
||||
dhcp_addopt_rebind();
|
||||
dhcp_addopt_lease();
|
||||
dhcp_addopt_renewal();
|
||||
}
|
||||
|
||||
DHCP_OPT[dhcp_state.opt_ptr++] = DHCP_END;
|
||||
uip_udp_send(sizeof(struct dhcp_pkt) + dhcp_state.opt_ptr);
|
||||
}
|
||||
|
||||
|
||||
void ip_opt(uint8_t * __xdata 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++];
|
||||
// There may be more than one IP option, such as 2 DNS servers advertised
|
||||
dhcp_state.opt_ptr += len - 4;
|
||||
}
|
||||
|
||||
|
||||
void long_opt(void)
|
||||
{
|
||||
dhcp_state.opt_ptr++;
|
||||
dhcp_state.opt_ptr++;
|
||||
long_value = DHCP_OPT[dhcp_state.opt_ptr++];
|
||||
long_value <<= 8;
|
||||
long_value |= DHCP_OPT[dhcp_state.opt_ptr++];
|
||||
long_value <<= 8;
|
||||
long_value |= DHCP_OPT[dhcp_state.opt_ptr++];
|
||||
long_value <<= 8;
|
||||
long_value |= DHCP_OPT[dhcp_state.opt_ptr++];
|
||||
}
|
||||
|
||||
|
||||
void print_txt_opt(void)
|
||||
{
|
||||
dhcp_state.opt_ptr++;
|
||||
for (uint8_t l = DHCP_OPT[dhcp_state.opt_ptr++]; l ; l--)
|
||||
write_char(DHCP_OPT[dhcp_state.opt_ptr++]);
|
||||
}
|
||||
|
||||
|
||||
void print_eth_opt(void)
|
||||
{
|
||||
dhcp_state.opt_ptr++;
|
||||
for (uint8_t l = DHCP_OPT[dhcp_state.opt_ptr++]; l ; l--)
|
||||
print_byte(DHCP_OPT[dhcp_state.opt_ptr++]);
|
||||
}
|
||||
|
||||
|
||||
void parse_opts(void)
|
||||
{
|
||||
while (DHCP_OPT[dhcp_state.opt_ptr] && DHCP_OPT[dhcp_state.opt_ptr] != DHCP_END) {
|
||||
switch(DHCP_OPT[dhcp_state.opt_ptr]) {
|
||||
case DHCP_SUBNET_MASK:
|
||||
ip_opt(&dhcp_state.subnet[0]);
|
||||
break;
|
||||
case DHCP_ROUTER:
|
||||
ip_opt(&dhcp_state.router[0]);
|
||||
break;
|
||||
case DHCP_DNS:
|
||||
ip_opt(&dhcp_state.dns[0]);
|
||||
break;
|
||||
case DHCP_SERVER_ID:
|
||||
ip_opt(&dhcp_state.server[0]);
|
||||
break;
|
||||
case DHCP_BROADCAST:
|
||||
ip_opt(&dhcp_state.broadcast[0]);
|
||||
break;
|
||||
case DHCP_REQUEST_IP:
|
||||
ip_opt(&dhcp_state.current_ip[0]);
|
||||
break;
|
||||
case DHCP_LEASE:
|
||||
long_opt();
|
||||
dhcp_state.lease = long_value;
|
||||
break;
|
||||
case DHCP_REBIND:
|
||||
long_opt();
|
||||
dhcp_state.rebind = long_value;
|
||||
break;
|
||||
case DHCP_RENEWAL:
|
||||
long_opt();
|
||||
dhcp_state.renewal = long_value;
|
||||
break;
|
||||
case DHCP_CLIENT_NAME:
|
||||
print_string("Client name: ");
|
||||
print_txt_opt();
|
||||
write_char('\n');
|
||||
break;
|
||||
case DHCP_VENDOR_ID:
|
||||
print_string("Vendor ID: ");
|
||||
print_txt_opt();
|
||||
write_char('\n');
|
||||
break;
|
||||
case DHCP_CLIENT_ID:
|
||||
print_string("Client ID: ");
|
||||
print_eth_opt();
|
||||
write_char('\n');
|
||||
break;
|
||||
case DHCP_PARAMS:
|
||||
print_string("PARAMS request (ignored)\n");
|
||||
dhcp_state.opt_ptr++;
|
||||
dhcp_state.opt_ptr += DHCP_OPT[dhcp_state.opt_ptr];
|
||||
dhcp_state.opt_ptr++;
|
||||
break;
|
||||
case DHCP_END:
|
||||
break;
|
||||
default:
|
||||
print_string("Unknown DHCP option: "); print_byte(DHCP_OPT[dhcp_state.opt_ptr]); write_char('\n');
|
||||
dhcp_state.opt_ptr++;
|
||||
dhcp_state.opt_ptr += DHCP_OPT[dhcp_state.opt_ptr];
|
||||
dhcp_state.opt_ptr++;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
void find_client(void)
|
||||
{
|
||||
uint8_t i;
|
||||
for (i = 0; i < DHCPD_MAX_CLIENTS; i++) {
|
||||
if (cstates[i].mac[0] == DHCP_P->client_addr[0] && cstates[i].mac[1] == DHCP_P->client_addr[1]
|
||||
&& cstates[i].mac[2] == DHCP_P->client_addr[2] && cstates[i].mac[3] == DHCP_P->client_addr[3]
|
||||
&& cstates[i].mac[4] == DHCP_P->client_addr[4] && cstates[i].mac[5] == DHCP_P->client_addr[5]
|
||||
)
|
||||
break;
|
||||
}
|
||||
if (i < DHCPD_MAX_CLIENTS) {
|
||||
client_idx = i;
|
||||
return;
|
||||
}
|
||||
|
||||
client_idx = 255;
|
||||
}
|
||||
|
||||
|
||||
void find_slot(void)
|
||||
{
|
||||
for (client_idx = 0; client_idx < DHCPD_MAX_CLIENTS; client_idx++) {
|
||||
if (!cstates[client_idx].cstate)
|
||||
return;
|
||||
}
|
||||
client_idx = 255;
|
||||
return;
|
||||
}
|
||||
|
||||
|
||||
void parse_dhcp_response(void)
|
||||
{
|
||||
if (!DHCP_P->tid == dhcp_state.transaction_id)
|
||||
return;
|
||||
if (DHCP_P->cookie[0] != 0x63 || DHCP_P->cookie[1] != 0x82 || DHCP_P->cookie[2] != 0x53 || DHCP_P->cookie[3] != 0x63)
|
||||
return;
|
||||
|
||||
dhcp_state.opt_ptr = 0;
|
||||
if (DHCP_OPT[dhcp_state.opt_ptr++] != DHCP_MESSAGE_TYPE || DHCP_OPT[dhcp_state.opt_ptr++] != DHCP_MESSAGE_TYPE_LEN)
|
||||
return;
|
||||
if (DHCP_OPT[dhcp_state.opt_ptr] == DHCP_MESSAGE_OFFER) {
|
||||
dhcp_state.opt_ptr++;
|
||||
dhcp_state.current_ip[0] = DHCP_P->your_ip[0];
|
||||
dhcp_state.current_ip[1] = DHCP_P->your_ip[1];
|
||||
dhcp_state.current_ip[2] = DHCP_P->your_ip[2];
|
||||
dhcp_state.current_ip[3] = DHCP_P->your_ip[3];
|
||||
parse_opts();
|
||||
print_string("DHCP offer received for IP "); dhcp_print_ip(dhcp_state.current_ip);
|
||||
write_char('\n');
|
||||
dhcp_send_request();
|
||||
} else if (DHCP_OPT[dhcp_state.opt_ptr++] == DHCP_MESSAGE_ACK) {
|
||||
parse_opts();
|
||||
print_string("DHCP ACK, our IP is "); dhcp_print_ip(dhcp_state.current_ip);
|
||||
write_char('\n');
|
||||
print_string("DHCP netmask "); dhcp_print_ip(dhcp_state.subnet);
|
||||
write_char('\n');
|
||||
print_string("DHCP gateway "); dhcp_print_ip(dhcp_state.router);
|
||||
write_char('\n');
|
||||
print_string("DHCP lease-time ");
|
||||
print_long(dhcp_state.lease);
|
||||
write_char('\n');
|
||||
uip_ipaddr(&uip_hostaddr, dhcp_state.current_ip[0], dhcp_state.current_ip[1], dhcp_state.current_ip[2], dhcp_state.current_ip[3]);
|
||||
uip_ipaddr(&uip_draddr, dhcp_state.router[0], dhcp_state.router[1], dhcp_state.router[2], dhcp_state.router[3]);
|
||||
uip_ipaddr(&uip_netmask, dhcp_state.subnet[0], dhcp_state.subnet[1], dhcp_state.subnet[2], dhcp_state.subnet[3]);
|
||||
dhcp_state.state = DHCP_LEASING;
|
||||
dhcp_state.ticks = SYS_TICK_HZ;
|
||||
dhcp_state.dhcp_timer = dhcp_state.renewal > 0xffff ? 0xffff : dhcp_state.renewal;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
void parse_dhcp_request(void)
|
||||
{
|
||||
print_string("parse_dhcp_request called\n");
|
||||
if (DHCP_P->cookie[0] != 0x63 || DHCP_P->cookie[1] != 0x82 || DHCP_P->cookie[2] != 0x53 || DHCP_P->cookie[3] != 0x63)
|
||||
return;
|
||||
|
||||
dhcp_state.opt_ptr = 0;
|
||||
if (DHCP_OPT[dhcp_state.opt_ptr++] != DHCP_MESSAGE_TYPE || DHCP_OPT[dhcp_state.opt_ptr++] != DHCP_MESSAGE_TYPE_LEN)
|
||||
return;
|
||||
if (DHCP_OPT[dhcp_state.opt_ptr] == DHCP_MESSAGE_DISCOVER) {
|
||||
dhcp_state.opt_ptr++;
|
||||
find_client();
|
||||
if (client_idx == 255)
|
||||
find_slot();
|
||||
// If there is no empty slot, we play possum and do not answer to the request
|
||||
if (client_idx == 255)
|
||||
return;
|
||||
|
||||
cstates[client_idx].cstate = CSTATE_OFFERED;
|
||||
cstates[client_idx].mac[0] = DHCP_P->client_addr[0]; cstates[client_idx].mac[1] = DHCP_P->client_addr[1];
|
||||
cstates[client_idx].mac[2] = DHCP_P->client_addr[2]; cstates[client_idx].mac[3] = DHCP_P->client_addr[3];
|
||||
cstates[client_idx].mac[4] = DHCP_P->client_addr[4]; cstates[client_idx].mac[5] = DHCP_P->client_addr[5];
|
||||
dhcp_state.transaction_id = DHCP_P->tid;
|
||||
parse_opts();
|
||||
dhcp_send_reply(DHCP_MESSAGE_OFFER);
|
||||
} else if (DHCP_OPT[dhcp_state.opt_ptr++] == DHCP_MESSAGE_REQUEST) {
|
||||
find_client();
|
||||
if (client_idx == 255) {
|
||||
dhcp_send_reply(DHCP_MESSAGE_NACK);
|
||||
return;
|
||||
}
|
||||
parse_opts();
|
||||
dhcp_send_reply(DHCP_MESSAGE_ACK);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
void dhcp_start(void) __banked
|
||||
{
|
||||
uip_ipaddr(server, 255,255,255,255);
|
||||
dhcp_state.conn = uip_udp_new(&server, HTONS(DHCP_SERVER_PORT));
|
||||
dhcp_state.current_ip[0] = dhcp_state.current_ip[1] = dhcp_state.current_ip[2] = dhcp_state.current_ip[3] = 0;
|
||||
if(dhcp_state.conn) {
|
||||
uip_udp_bind(dhcp_state.conn, HTONS(DHCP_CLIENT_PORT));
|
||||
} else {
|
||||
print_string("dhcp_start failed to set up socket\n");
|
||||
return;
|
||||
}
|
||||
get_random_32();
|
||||
dhcp_state.transaction_id = SFR_DATA_U32;
|
||||
dhcp_state.state = DHCP_START;
|
||||
print_string("dhcp_start done\n");
|
||||
}
|
||||
|
||||
|
||||
void dhcpd_start(void) __banked
|
||||
{
|
||||
memset(&cstates[0], 0, sizeof (struct dhcpd_cstate) * DHCPD_MAX_CLIENTS);
|
||||
dhcp_state.conn = uip_udp_new(0, 0);
|
||||
if(dhcp_state.conn) {
|
||||
uip_udp_bind(dhcp_state.conn, HTONS(DHCP_SERVER_PORT));
|
||||
} else {
|
||||
print_string("dhcpd_start failed to set up socket\n");
|
||||
return;
|
||||
}
|
||||
if (!dhcpd_vlan)
|
||||
print_string("dhcpd: enabling for all VLANs\n");
|
||||
else
|
||||
print_string("dhcpd: enabling for VLAN "); print_short(dhcpd_vlan); write_char('\n');
|
||||
dhcp_state.state = DHCP_SERVER;
|
||||
|
||||
dhcp_state.server[1] = uip_hostaddr[0] >> 8; dhcp_state.server[0] = uip_hostaddr[0] & 0xff;
|
||||
dhcp_state.server[3] = uip_hostaddr[1] >> 8; dhcp_state.server[2] = uip_hostaddr[1] & 0xff;
|
||||
|
||||
dhcp_state.router[1] = uip_draddr[0] >> 8; dhcp_state.router[0] = uip_draddr[0] & 0xff;
|
||||
dhcp_state.router[3] = uip_draddr[1] >> 8; dhcp_state.router[2] = uip_draddr[1] & 0xff;
|
||||
|
||||
dhcp_state.subnet[1] = uip_netmask[0] >> 8; dhcp_state.subnet[0] = uip_netmask[0] & 0xff;
|
||||
dhcp_state.subnet[3] = uip_netmask[1] >> 8; dhcp_state.subnet[2] = uip_netmask[1] & 0xff;
|
||||
|
||||
dhcp_state.broadcast[0] = dhcp_state.router[0]; dhcp_state.broadcast[1] = dhcp_state.router[1];
|
||||
dhcp_state.broadcast[2] = dhcp_state.router[2]; dhcp_state.broadcast[3] = 0xff;
|
||||
|
||||
for (uint8_t i = 0; i < DHCPD_MAX_CLIENTS; i++) {
|
||||
cstates[i].cstate = CSTATE_NONE;
|
||||
}
|
||||
// TODO: DNS, correct broadcast address
|
||||
|
||||
print_string("dhcpd_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;
|
||||
}
|
||||
|
||||
void dhcpd_stop(void) __banked
|
||||
{
|
||||
print_string("dhcpd_stop called\n");
|
||||
uip_udp_remove(dhcp_state.conn);
|
||||
dhcp_state.state = DHCP_OFF;
|
||||
}
|
||||
|
||||
|
||||
void dhcp_callback(void) __banked
|
||||
{
|
||||
if (!dhcp_state.state)
|
||||
return;
|
||||
if (uip_closed()) {
|
||||
print_string("Closed\n");
|
||||
return;
|
||||
} else if (dhcp_state.state == DHCP_SERVER && uip_newdata()) {
|
||||
parse_dhcp_request();
|
||||
} else if (uip_newdata()) {
|
||||
parse_dhcp_response();
|
||||
} else {
|
||||
if (dhcp_state.state == DHCP_START) {
|
||||
dhcp_send_discover();
|
||||
} else if (!--dhcp_state.ticks) {
|
||||
// print_string("Timer: "); print_short(dhcp_state.ticks); write_char(' '); print_short(dhcp_state.dhcp_timer);
|
||||
dhcp_state.dhcp_timer--;
|
||||
dhcp_state.ticks = SYS_TICK_HZ;
|
||||
}
|
||||
if (!dhcp_state.dhcp_timer) {
|
||||
switch (dhcp_state.state) {
|
||||
case DHCP_DISCOVER_SENT:
|
||||
dhcp_send_discover();
|
||||
break;
|
||||
case DHCP_LEASING:
|
||||
case DHCP_REQUEST_SENT:
|
||||
dhcp_send_request();
|
||||
break;
|
||||
default:
|
||||
print_string("UNKNOWN STATE\n");
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,65 @@
|
||||
#ifndef _DHCP_H_
|
||||
#define _DHCP_H_
|
||||
|
||||
#include "uipopt.h"
|
||||
#include <stdint.h>
|
||||
#define DHCPD_MAX_CLIENTS 20
|
||||
#define DHCPD_START_IP 100
|
||||
|
||||
#define DHCP_SERVER_PORT 67
|
||||
#define DHCP_CLIENT_PORT 68
|
||||
|
||||
#define DHCP_OFF 0
|
||||
#define DHCP_START 1
|
||||
#define DHCP_DISCOVER_SENT 2
|
||||
#define DHCP_REQUEST_SENT 3
|
||||
#define DHCP_LEASING 4
|
||||
#define DHCP_SERVER 5
|
||||
|
||||
#define CSTATE_NONE 0
|
||||
#define CSTATE_OFFERED 1
|
||||
#define CSTATE_LEASED 2
|
||||
|
||||
void dhcp_start(void) __banked;
|
||||
void dhcp_stop(void) __banked;
|
||||
void dhcp_callback(void) __banked;
|
||||
|
||||
struct dhcp_state {
|
||||
uint8_t state;
|
||||
uint32_t transaction_id;
|
||||
uint16_t dhcp_timer;
|
||||
uint8_t ticks;
|
||||
uint16_t opt_ptr;
|
||||
uint8_t current_ip[4];
|
||||
uint8_t server[4];
|
||||
uint8_t router[4];
|
||||
uint8_t subnet[4];
|
||||
uint8_t dns[4];
|
||||
uint8_t broadcast[4];
|
||||
uint32_t lease;
|
||||
uint32_t rebind;
|
||||
uint32_t renewal;
|
||||
|
||||
struct uip_udp_conn *conn;
|
||||
};
|
||||
|
||||
struct dhcpd_cstate {
|
||||
uint8_t cstate;
|
||||
uint16_t timer;
|
||||
uint32_t transaction_id;
|
||||
uint8_t mac[6];
|
||||
uint8_t ip[4];
|
||||
};
|
||||
|
||||
|
||||
void dhcpd_start(void) __banked;
|
||||
void dhcpd_stop(void) __banked;
|
||||
|
||||
typedef struct dhcp_state uip_udp_appstate_t;
|
||||
|
||||
/* Finally we define the application function to be called by uIP. */
|
||||
#ifndef UIP_UDP_APPCALL
|
||||
#define UIP_UDP_APPCALL dhcp_callback
|
||||
#endif /* UIP_APPCALL */
|
||||
|
||||
#endif
|
||||
@@ -0,0 +1,238 @@
|
||||
# Egress and Ingress Bandwidth Control
|
||||
The RTL8372/3 allows to control the bandwidth of data transmitted (egress) and/or
|
||||
admitted (ingress) at any given port. Once admitted, packets are internally switched
|
||||
at wire-speed, since the backplane of the devices has a bandwidth of 60GBit/s.
|
||||
|
||||
The devices schedules transmission of packets by assigning packets to 8 queues
|
||||
implemented in hardware per port, which share a total of 8Mbit of memory internal
|
||||
to the switching part of the SoCs. Packets are assigned to the respective queues
|
||||
based on the priority assigned to a packet, which can be based on various
|
||||
properties of a packet such as IEEE 802.1P priority, DSCP value, physical port
|
||||
number, destination or source MAC, Ether-Type-based, CVID, SVID, IPv4 source or
|
||||
destination IP, IPv4/IPv6 TOS field, IPv6 Flow Label and even TCP/UDP
|
||||
source/destination port. Once in a queue, packets are scheduled for egress
|
||||
based on differnent configurable algorithms.
|
||||
|
||||
RTLPlayground currently allows only to control the bandwidth at ingress at a port
|
||||
or just before packets leave a port. There is no control of the priority assignment
|
||||
or queue scheduling mechanisms. The bandwidth can be controlled in steps of 16Kbit/s
|
||||
from 16Kbit/s to 10Gbp/s.
|
||||
|
||||
The bandwidth control as currently implemented allows e.g. to assign a certain
|
||||
share of bandwidth to an attached device (e.g. to share an uplink), or simulate
|
||||
connections with low bandwidth and even bad connectivity with packet drops when
|
||||
ingress is not controlled by Flow Control but by simply droping packets.
|
||||
|
||||
## Ingress/Egress control
|
||||
The relevant registers for controlling Ingress and Egress at a port are:
|
||||
```
|
||||
#define RTL837X_IGBW_CTRL 0x4c10
|
||||
#define IGBW_INC_BYPASS_PKT 0x100
|
||||
#define IGBW_INC_IFG 0x80
|
||||
#define IGBW_ADM_DHCP 0x20
|
||||
#define IGBW_ADM_ARPREQ 0x10
|
||||
#define IGBW_ADM_RMA 0x08
|
||||
#define IGBW_ADM_BPDU 0x04
|
||||
#define IGBW_ADM_RTKPKT 0x02
|
||||
#define IGBW_ADM_IGMP 0x01
|
||||
#define RTL837X_IGBW_PORT_CTRL 0x4C18
|
||||
#define RTL837X_IGBW_PORT_FC_CTRL 0x4C8C
|
||||
#define RTL837X_EGBW_PORT_CTRL 0x1c34
|
||||
#define RTL837X_EGBW_CTRL 0x447c
|
||||
#define EGBW_INC_IFG 0x02
|
||||
#define EGBW_CPUMODE 0x01
|
||||
```
|
||||
`RTL837X_IGBW_CTRL/RTL837X_EGBW_CTRL` control the behaviour of the bandwidth control
|
||||
at ingress and egress. The flags such as `IGBW_ADM_DHCP`control whether certain types
|
||||
of packets such as DHCP are exempt from being ingress controlled. The
|
||||
`IGBW_INC_IFG/EGBW_INC_IFG` flags control whether the Inter Frame Gaps are part of
|
||||
the bandwidth being controlled. `EGBW_CPUMODE` controls whether packets generated
|
||||
by the internal CPU are subject to egress control.
|
||||
|
||||
`RTL837X_IGBW_PORT_CTRL/RTL837X_EGBW_PORT_CTRL` configure the bandwidth for ingress
|
||||
and egress at a port.
|
||||
|
||||
`RTL837X_IGBW_PORT_FC_CTRL` configures whether packets are bandwidth-controlled using
|
||||
Flow Control (port-bit set), or simply dropped (port-bit clear).
|
||||
|
||||
## Ingress/Egress bandwidth API
|
||||
The code currently provides the following functions:
|
||||
```
|
||||
void bandwidth_setup(void) __banked;
|
||||
void bandwidth_ingress_set(uint8_t port, __xdata uint32_t bw) __banked;
|
||||
void bandwidth_ingress_disable(uint8_t port) __banked;
|
||||
void bandwidth_ingress_drop(uint8_t port) __banked;
|
||||
void bandwidth_egress_set(uint8_t port, __xdata uint32_t bw) __banked;
|
||||
void bandwidth_egress_disable(uint8_t port) __banked;
|
||||
void bandwidth_status(uint8_t port) __banked;
|
||||
```c
|
||||
|
||||
`bandwidth_setup()` is called at boot-time and configures excluding all special packets
|
||||
that may be for the CPU and packets outgoing from the CPU to be excluded from bandwidth
|
||||
control. IFG is not part of the bandwidth calculation.
|
||||
|
||||
`bandwidth_ingress_set()` enables ingress bandwidth control for a particular port given
|
||||
the specified bandwidth. This also enabled Flow Control at a port.
|
||||
|
||||
`bandwidth_ingress_set()` enables egress bandwidth control for a particular port given
|
||||
the specified bandwidth
|
||||
|
||||
`bandwidth_ingress_disable() / bandwidth_egress_disable()` disable ingress and egress
|
||||
bandwidth control at a given port
|
||||
|
||||
`bandwidth_ingress_drop(port)` configures packets exceeding bandwidth limitations to
|
||||
simply be dropped
|
||||
|
||||
`bandwidth_status(port)` shows the current bandwidth control status for a given port
|
||||
|
||||
## Bandwidth control configuration on the Serial Console
|
||||
The following commands are provided on the serial console:
|
||||
```
|
||||
> bw [in|out|status] <port> [<hexvalue>|off|drop]
|
||||
Configures or shows the status of bandwidth control
|
||||
```
|
||||
The bandwidth is given as the `<hexvalue>` in Kbit/s. Note that the control is only
|
||||
possible at a granularity of 16 Kbit/s and the minimum value is also 16 Kbit/s. The
|
||||
hexadecimal numbers must be given in full bytes, i.e. have an even number of digits.
|
||||
|
||||
To enable bandwidth control of ingress for physical port 2 to be set to 256 Kbit/s
|
||||
do:
|
||||
```
|
||||
> bw in 2 0100
|
||||
```
|
||||
|
||||
To drop packets when the bandwidth is exceeeded at port 2 do:
|
||||
```
|
||||
> bw in 2 drop
|
||||
```
|
||||
|
||||
To disable bandwidth control for incoming packets on port 2 do:
|
||||
```
|
||||
> bw in 2 off
|
||||
```
|
||||
|
||||
## Bandwidth configuration via the Web Interface
|
||||
Not implemented, yet!
|
||||
|
||||
## A Test using iperf3
|
||||
The following is and example how to test bandwidth control with a signle Linux device using
|
||||
network namespaces to route packets between a client and a server on the same Linux device
|
||||
through an external switch.
|
||||
|
||||
You will need 2 network intefaces on the linux device, say, 2 USB-Ethernet controllers called
|
||||
eth0 and eth1:
|
||||
```
|
||||
$ sudo ip netns add client
|
||||
$ sudo ip netns add server
|
||||
|
||||
$ sudo ip link set dev eth0 netns client
|
||||
$ sudo ip link set dev eth1 netns server
|
||||
|
||||
$ sudo ip netns exec client ip link set dev eth0 up
|
||||
$ sudo ip netns exec server ip link set dev eth1 up
|
||||
|
||||
$ sudo ip netns exec client ip addr add dev eth0 192.168.99.1/24
|
||||
$ sudo ip netns exec server ip addr add dev eth1 192.168.99.2/24
|
||||
|
||||
$ sudo ip netns exec server iperf3 -s
|
||||
```
|
||||
This will start an iper3 server in the above shell.
|
||||
|
||||
In a different shell you can now run the iperf3 client against your server:
|
||||
```
|
||||
$ sudo ip netns exec client iperf -c 192.168.99.2
|
||||
```
|
||||
The LEDs on your switch where your network adapters are connected should start to flicker.
|
||||
On a 1GBit connection, you should see:
|
||||
```
|
||||
$ sudo ip netns exec client iperf3 -c 192.168.99.2
|
||||
Connecting to host 192.168.99.2, port 5201
|
||||
[ 5] local 192.168.99.1 port 46776 connected to 192.168.99.2 port 5201
|
||||
[ ID] Interval Transfer Bitrate Retr Cwnd
|
||||
[ 5] 0.00-1.00 sec 114 MBytes 952 Mbits/sec 0 339 KBytes
|
||||
[ 5] 1.00-2.00 sec 113 MBytes 946 Mbits/sec 0 356 KBytes
|
||||
[ 5] 2.00-3.00 sec 112 MBytes 937 Mbits/sec 0 390 KBytes
|
||||
[ 5] 3.00-4.00 sec 112 MBytes 942 Mbits/sec 0 390 KBytes
|
||||
[ 5] 4.00-5.00 sec 112 MBytes 943 Mbits/sec 0 390 KBytes
|
||||
[ 5] 5.00-6.00 sec 112 MBytes 944 Mbits/sec 0 390 KBytes
|
||||
[ 5] 6.00-7.00 sec 112 MBytes 938 Mbits/sec 0 390 KBytes
|
||||
[ 5] 7.00-8.00 sec 112 MBytes 942 Mbits/sec 0 410 KBytes
|
||||
[ 5] 8.00-9.00 sec 113 MBytes 947 Mbits/sec 0 410 KBytes
|
||||
[ 5] 9.00-10.00 sec 112 MBytes 940 Mbits/sec 0 410 KBytes
|
||||
- - - - - - - - - - - - - - - - - - - - - - - - -
|
||||
[ ID] Interval Transfer Bitrate Retr
|
||||
[ 5] 0.00-10.00 sec 1.10 GBytes 943 Mbits/sec 0 sender
|
||||
[ 5] 0.00-10.00 sec 1.10 GBytes 941 Mbits/sec receiver
|
||||
```
|
||||
|
||||
Now, we limit ingress on port 1 (connected to eth0) to 4 MBit/s:
|
||||
```> bw in 1 1000
|
||||
bandwidth_ingress_set called, port 04
|
||||
RTL837X_IGBW_PORT_CTRL:0x00100100
|
||||
RTL837X_IGBW_PORT_FC_CTRL:0x00000010
|
||||
```
|
||||
|
||||
We now get:
|
||||
```
|
||||
$ sudo ip netns exec client iperf3 -c 192.168.99.2
|
||||
[ 5] local 192.168.99.1 port 43324 connected to 192.168.99.2 port 5201
|
||||
[ ID] Interval Transfer Bitrate Retr Cwnd
|
||||
[ 5] 0.00-1.00 sec 1.12 MBytes 9.43 Mbits/sec 0 160 KBytes
|
||||
[ 5] 1.00-2.00 sec 640 KBytes 5.24 Mbits/sec 0 160 KBytes
|
||||
[ 5] 2.00-3.00 sec 384 KBytes 3.15 Mbits/sec 0 160 KBytes
|
||||
[ 5] 3.00-4.00 sec 384 KBytes 3.15 Mbits/sec 0 160 KBytes
|
||||
[ 5] 4.00-5.00 sec 640 KBytes 5.24 Mbits/sec 0 160 KBytes
|
||||
[ 5] 5.00-6.00 sec 256 KBytes 2.10 Mbits/sec 0 160 KBytes
|
||||
[ 5] 6.00-7.00 sec 640 KBytes 5.24 Mbits/sec 0 160 KBytes
|
||||
[ 5] 7.00-8.00 sec 384 KBytes 3.15 Mbits/sec 0 160 KBytes
|
||||
[ 5] 8.00-9.00 sec 640 KBytes 5.24 Mbits/sec 0 160 KBytes
|
||||
[ 5] 9.00-10.00 sec 256 KBytes 2.10 Mbits/sec 0 160 KBytes
|
||||
- - - - - - - - - - - - - - - - - - - - - - - - -
|
||||
[ ID] Interval Transfer Bitrate Retr
|
||||
[ 5] 0.00-10.00 sec 5.25 MBytes 4.40 Mbits/sec 0 sender
|
||||
[ 5] 0.00-10.16 sec 4.75 MBytes 3.92 Mbits/sec receiver
|
||||
|
||||
iperf Done.
|
||||
```
|
||||
Which is the 4Mbit/s we configured. There are no packet drops (retries) because
|
||||
Flow Control is used to signal the Ethernet adapter on the incoming interface
|
||||
(port 1 of the router) to slow down.
|
||||
|
||||
We can also configure a mere 256KBit/s and packet drop to simulate a bad connection:
|
||||
```
|
||||
> bw in 1 0100
|
||||
bandwidth_ingress_set called, port 04
|
||||
RTL837X_IGBW_PORT_CTRL:0x00100010
|
||||
RTL837X_IGBW_PORT_FC_CTRL:0x00000010
|
||||
|
||||
> bw in 1 drop
|
||||
RTL837X_IGBW_PORT_FC_CTRL:0x00000000
|
||||
```
|
||||
|
||||
We now get:
|
||||
```
|
||||
$ sudo ip netns exec client iperf3 -c 192.168.99.2
|
||||
Connecting to host 192.168.99.2, port 5201
|
||||
[ 5] local 192.168.99.1 port 46060 connected to 192.168.99.2 port 5201
|
||||
[ ID] Interval Transfer Bitrate Retr Cwnd
|
||||
[ 5] 0.00-1.00 sec 384 KBytes 3.14 Mbits/sec 2 1.41 KBytes
|
||||
[ 5] 1.00-2.00 sec 0.00 Bytes 0.00 bits/sec 54 1.41 KBytes
|
||||
[ 5] 2.00-3.00 sec 0.00 Bytes 0.00 bits/sec 31 29.7 KBytes
|
||||
[ 5] 3.00-4.00 sec 0.00 Bytes 0.00 bits/sec 2 1.41 KBytes
|
||||
[ 5] 4.00-5.00 sec 0.00 Bytes 0.00 bits/sec 23 1.41 KBytes
|
||||
[ 5] 5.00-6.00 sec 128 KBytes 1.05 Mbits/sec 16 14.1 KBytes
|
||||
[ 5] 6.00-7.00 sec 0.00 Bytes 0.00 bits/sec 2 1.41 KBytes
|
||||
[ 5] 7.00-8.00 sec 0.00 Bytes 0.00 bits/sec 11 1.41 KBytes
|
||||
[ 5] 8.00-9.00 sec 128 KBytes 1.05 Mbits/sec 9 8.48 KBytes
|
||||
[ 5] 9.00-10.00 sec 0.00 Bytes 0.00 bits/sec 2 1.41 KBytes
|
||||
- - - - - - - - - - - - - - - - - - - - - - - - -
|
||||
[ ID] Interval Transfer Bitrate Retr
|
||||
[ 5] 0.00-10.00 sec 640 KBytes 524 Kbits/sec 152 sender
|
||||
[ 5] 0.00-10.00 sec 256 KBytes 210 Kbits/sec receiver
|
||||
|
||||
iperf Done.
|
||||
```
|
||||
Which shows a large number of retries due to dropped packets and an average number
|
||||
of received packets (the client sends the packets to the server, and they are sent
|
||||
back to the client by the server) of 210 KBit/s, the number is higher for the transmitted
|
||||
packets, because they may include dropped packets.
|
||||
@@ -0,0 +1,161 @@
|
||||
### 2M-PCB23-V3.1
|
||||
|
||||
## Brands
|
||||
|Brand|Type|Managed|PCB|Flash|Chip RTL|
|
||||
|---|---|---|---|---|---|
|
||||
| keepLINK | KP-9000-9XHML-X | Yes| 2M-PCB23-V3.1 | 2M| 8273N + 8224N |
|
||||
|
||||
## PCB
|
||||
|
||||
<img src="photos/2M-PCB23-V3.1-managed/2M-PCB23-V3.1-top.jpeg" width="300" />
|
||||
|
||||
## Port overview
|
||||
|
||||
```
|
||||
┌─────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────┐
|
||||
│ ┌──────────┐ │
|
||||
│ ┌─────────┐ ┌─────────┐ ┌─────────┐ ┌─────────┐ ┌─────────┐ ┌─────────┐ ┌─────────┐ ┌─────────┐ │ SFP (J4) │ │
|
||||
│ │ RJ45 │ │ RJ45 │ │ RJ45 │ │ RJ45 │ │ RJ45 │ │ RJ45 │ │ RJ45 │ │ RJ45 │ │ PORT 9 │ │
|
||||
│ │ PORT 1 │ │ PORT 2 │ │ PORT 3 │ │ PORT 4 │ │ PORT 5 │ │ PORT 6 │ │ PORT 7 │ │ PORT 8 │ │ MAC 8 │ O (PWR) │
|
||||
│ O │ MAC 0 │ │ MAC 1 │ │ MAC 2 │ │ MAC 3 │ │ MAC 4 │ │ MAC 5 │ │ MAC 6 │ │ MAC 7 │ │ SerDes 1 │ O (SFP) │
|
||||
│ RST └─────────┘ └─────────┘ └─────────┘ └─────────┘ └─────────┘ └─────────┘ └─────────┘ └─────────┘ └──────────┘ │
|
||||
└─────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────┘
|
||||
```
|
||||
|
||||
# Connectors
|
||||
|`J4` SFP PINs | Signal | Component | GPIO | Notes |
|
||||
|---|---|---|---|---|
|
||||
|2| TX_FAULT | | --- | |
|
||||
|3| TX_DISABLE | | --- | Pull down - 0R |
|
||||
|4| MODDEF2 – SDA | b-r273 | GPIO39-SDA4 | |
|
||||
|5| MODDEF1 – SCL | b-r274 | GPIO40-SCL3 | |
|
||||
|6| MODDEF0 – PRESENT | B-R275 | GPIO38 | |
|
||||
|7| RATE SEL | | --- | |
|
||||
|8| LOS | B-R276 | GPIO38 | |
|
||||
|9| TO? | | --- | |
|
||||
|
||||
Note: component numbering `<L>-<REFDES>-<SIDE>`
|
||||
* L: Layer, T=Top, B=Bottom
|
||||
* REFDEES: full silkscreen like `R123`
|
||||
* SIDE: Side of the component. when the rj45 are facing towards you are you can read the silkscreen normal.
|
||||
L = Left, R=right, B=bottom, T=top or P with a pin number.
|
||||
|
||||
### T8, serial console
|
||||
|`T9` pin|GPIO|Signal|
|
||||
|---|---|---|
|
||||
| 1 | GPIO31 | U0TXD (Output) |
|
||||
| 2 | GND | PWR |
|
||||
| 3 | GPIO32 | U0RXD (Input) |
|
||||
| 4 | 3V3 | PWR |
|
||||
|
||||
### T7, Slave Interface
|
||||
|`T7` pin|what|Signal|Components|
|
||||
|---|---|---|---|
|
||||
| 1 | Slave Interface | Slave SCK/SCL/MDC/EE_SCL | U7-6 |
|
||||
| 2 | GND | | |
|
||||
| 3 | Slave Interface | Slave SDI/SDA/MDIO/EE_SDA | U7-5 |
|
||||
| 4 | 3V3 | | |
|
||||
| 5 | GPIO? | | |
|
||||
| 6 | GPIO? | | |
|
||||
|
||||
`U7` can be a standard I2C eeprom memory, like `24LC32`.
|
||||
|
||||
The Slave Interface allows an extenal host to controll the SOC even if the internal MCU is used.
|
||||
Depending on the `IF_SEL` bootstrap resistors, this can me `I2C`, `SPI` or `SMI`.
|
||||
On this device it is `I2C` on address `0b1011100` or `0x5c` (7-bit notation).
|
||||
|
||||
* I2c Read: must be a write_read opperation `<Dev-ADDR><RegAddr15:8><RegAddr7:0>` `<DevAddr><Data7:0><Data15:8><Data23:16><Data31:24>`.
|
||||
* I2c Write: `<Dev-ADDR><RegAddr15:8><RegAddr7:0><DevAddr><Data7:0><Data15:8><Data23:16><Data31:24>`.
|
||||
|
||||
Example register `0x0004` return chip id `0x00, 0x00, 0x72, 0x83` = `0x83720000`.
|
||||
|
||||
### T9
|
||||
|`T9` SMI | Signal | Component | GPIO | Notes |
|
||||
|---|---|---|---|---|
|
||||
|1| MDO | SMI-MDO | GPIO41 | |
|
||||
|2| GND | PWR | | |
|
||||
|3| MDC | SMI-MDC | GPio40 | |
|
||||
|
||||
### T10
|
||||
|`T10` pin|what|Signal|
|
||||
|---|---|---|
|
||||
| 1 | GPIO49 | |
|
||||
| 2 | GPIO47 | |
|
||||
| 3 | 3V3 | |
|
||||
| 4 | GPIO48 | |
|
||||
| 5 | GND | |
|
||||
| 6 | GPIO46 | |
|
||||
|
||||
# Reset ciruit
|
||||
|
||||
Reset-line found at `T-D6-L`, `T-R83`, `T-R97`, `T-R94`, `T-R93` active-low.
|
||||
|
||||
# GPIO
|
||||
|
||||
| HEX VAL. | GPIO | Component | What | | GPIO | Component | What |
|
||||
| -------- | ------ | ---- | ---- | ---- | ---- | ---- | ---- |
|
||||
| 00000001 | GPIO00 | T-R34-R, P1-LED-YL |? | | GPIO32 | B-r126-r | U0RXD |
|
||||
| 00000002 | GPIO01 | T-R99-R, P1-LED-GR |? | | GPIO33 | | |
|
||||
| 00000004 | GPIO02 | |? | | GPIO34 | B-R172, To RTL8224 | SMI-MDC0 |
|
||||
| 00000008 | GPIO03 | T-R113-R, P2-LED-GR |? | | GPIO35 | B-R173, To RTL8224 | SMI-MDIO0 |
|
||||
| 00000010 | GPIO04 | T-R115-R, P3-LED-YL |? | | GPIO36 | | | |
|
||||
| 00000020 | GPIO05 | T-R117-R, P3-LED-GR |? | | GPIO37 | To RTL8224 ?? | Already driver low | |
|
||||
| 00000040 | GPIO06 | |? | | GPIO38 | sfp-6 via B-R275, sfp-8 via B-R276 | SFP-PRESENT |
|
||||
| 00000080 | GPIO07 | |? | | GPIO39 | sfp-4, b-r273, B-r143 | I2C4-SDA |
|
||||
| 00000100 | GPIO08 | | | | GPIO40 | T9-2, sfp-5 b-r274, B-r146 | I2C3-SCL |
|
||||
| 00000200 | GPIO09 | |LEDx[^1] | | GPIO41 | T9-1, B-r143 | | I2C3-SDA (Unused) |
|
||||
| 00000400 | GPIO10 | | | | GPIO42 | U6?8?-P6, T-R | SPI-MEMORY, CLK |
|
||||
| 00000800 | GPIO11 | |LEDx[^1] | | GPIO43 | U6?8?-P5, T-R | SPI-MEMORY, DI,IO0 |
|
||||
| 00001000 | GPIO12 | |LEDx[^1] | | GPIO44 | U6?8?-P2, T-R | SPI-MEMORY, DO,IO1 |
|
||||
| 00002000 | GPIO13 | |LEDx[^1] | | GPIO45 | U6?8?-P1, T-R | SPI-MEMORY, CS |
|
||||
| 00004000 | GPIO14 | |LEDx[^1] | | GPIO46 | T10-6 | SPI0-SCK |
|
||||
| 00008000 | GPIO15 | |LEDx[^1] | | GPIO47 | T10-2 | SPI0-SDA |
|
||||
| 00010000 | GPIO16 | |LEDx[^1] | | GPIO48 | T10-4, J6 (BUTTON RESET) | SPI1-SCK |
|
||||
| 00020000 | GPIO17 | |LEDx[^1] | | GPIO49 | T10-1 | SPI1-SDA |
|
||||
| 00040000 | GPIO18 | |LEDx[^1] | | GPIO50 | | |
|
||||
| 00080000 | GPIO19 | |LEDx[^1] | | GPIO51 | | |
|
||||
| 00100000 | GPIO20 | |LEDx[^1] | | GPIO52 | | |
|
||||
| 00200000 | GPIO21 | P7-LED-GR, B-R139 |LEDx[^1] | | GPIO53 | | |
|
||||
| 00400000 | GPIO22 | |LEDx[^1] | | GPIO54 | | |
|
||||
| 00800000 | GPIO23 | |LEDx[^1] | | GPIO55 | | |
|
||||
| 01000000 | GPIO24 | P8-led-yellow,b-r154 |LEDx | | GPIO56 | | |
|
||||
| 02000000 | GPIO25 | | | | GPIO57 | | |
|
||||
| 04000000 | GPIO26 | |LEDx | | GPIO58 | | |
|
||||
| 08000000 | GPIO27 | |? | | GPIO59 | | |
|
||||
| 10000000 | GPIO28 | | | | GPIO60 | | |
|
||||
| 20000000 | GPIO29 | | | | GPIO61 | | |
|
||||
| 40000000 | GPIO30 | RTL8224N Reset |RTL8224 | | GPIO62 | To RTL8224 | Already driver | |
|
||||
| 80000000 | GPIO31 | B-r129-r? |U0TXD | | GPIO63 | | |
|
||||
|
||||
## GPIO Register Input value
|
||||
|
||||
GPIO 1: 0a7ffbdd
|
||||
GPIO 0: effb6dff
|
||||
|
||||
# LEDs
|
||||
|
||||
| NAME | COMPONENTS | GPIO |
|
||||
| ---- | ---------- | ---- |
|
||||
| SYSTEM | | ? |
|
||||
| SFP | | ? |
|
||||
|
||||
|
||||
# Power supply
|
||||
|
||||
Board has two supply rails.
|
||||
`0.95` and `3.3` volt.
|
||||
|
||||
## `0.95` Core Voltage.
|
||||
|
||||
Voltage is crated by a `MP2225GJ` Buck converter.
|
||||
0.95V must be within 3%.
|
||||
|
||||
## `3.3` Voltage
|
||||
|
||||
Voltage is crated by a `MP2225GJ` Buck converter.
|
||||
3.3V must be within 4.5%.
|
||||
Chip can deliver up to 5A and the sweetspot is at 2A.
|
||||
So higher power SFP-modules should work.
|
||||
|
||||
|
||||
[^1]: LEDs are found by just plugin a RJ45 connector and see with cmd `gpio` the status change. But the bit pattern for port 1,2 are diffrent from port 3,4.
|
||||
@@ -0,0 +1,62 @@
|
||||
# Hisource Hi-K0402WS
|
||||
|
||||
Following is documentation for unmanaged switch marked as `Hi-K0402WS`.
|
||||
|
||||
Original software is running UART on 9600 baud rate.
|
||||
|
||||
Using SPI clamp in-board is the only method for initial installation.
|
||||
|
||||
The board has two flash chips `BY25Q16BS` with 16M-bit 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
|
||||
|
||||
- **Name**: 2.5G Ethernet Switch
|
||||
- **Model**: Hi-K0402WS
|
||||
- **Ports**:
|
||||
- 4 × RJ45: 10/100/1000/2500 Mbps
|
||||
- 2 × SFP: 1000 / 2500 / 10000 Mbps
|
||||
|
||||
### What works (expected from label + similar devices)
|
||||
|
||||
- All four 2.5GBASE-T RJ45 ports at 10/100/1000/2500 Mbps
|
||||
- Both SFP ports supporting 1G, 2.5G and 10G modules
|
||||
- LEDs
|
||||
|
||||
### PCB overview
|
||||
|
||||
**Board markings**
|
||||
- Top silkscreen: PCB-KO4022W-V3.0 / DIP-KO4022WS-V3.0
|
||||
|
||||
Top side
|
||||
|
||||
<img src="photos/K0402W-V3.0-unmanaged\PCB-top.jpg" width="300" />
|
||||
|
||||
Bottom
|
||||
|
||||
<img src="photos/K0402W-V3.0-unmanaged\PCB-bottom.jpg" width="300" />
|
||||
|
||||
### T2, serial console
|
||||
|
||||
| `J2` pin | Signal |
|
||||
| -------- | ----------- |
|
||||
| 1 | 3V3 |
|
||||
| 2 | RX (Input) |
|
||||
| 3 | TX (Output) |
|
||||
| 4 | GND |
|
||||
|
||||
|
||||
## Power supply
|
||||
|
||||
Input power is delivered via barell plug, `12V 1A` adapter was provided.
|
||||
Board has two supply rails. `0.95` and `3.3` volt.
|
||||
|
||||
### `0.95` Core Voltage
|
||||
|
||||
Voltage is made by a `Techcode TD1720` .
|
||||
|
||||
### `3.3` Voltage
|
||||
|
||||
Voltage is created by chip marked as `Techcode TD1720`.
|
||||
|
||||
**There seems to have been a miscalculation when choosing the inductor and the device is ~25% more efficient with an 5V power supply.**
|
||||
@@ -0,0 +1,54 @@
|
||||
# SWTG018AS-A V2.0
|
||||
|
||||
The following is a documentation for the unmanaged switch marked as `SWTG018AS-A V2.0`.
|
||||
It is e.g. sold under the Ampcom brand, but no branh-markings are found on the device.
|
||||
|
||||
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.
|
||||
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
|
||||
|
||||
- **Name**: 9-Ports 2.5G Ethernet Switch
|
||||
- **Ports**:
|
||||
- 8 × RJ45: 10/100/1000/2500 Mbps
|
||||
- 1 × SFP+: 1000 / 2500 / 10000 Mbps
|
||||
- **Power**: 12V DC, 1A barrel connector
|
||||
|
||||
<img src="photos/SWTG018AS_A_V_2_0/label.jpg" width="300" />
|
||||
|
||||
### What works
|
||||
The device is fully supported:
|
||||
- All 8 2.5GBASE-T RJ45 ports work at 10/100/1000/2500 Mbps
|
||||
- The SFP+ port supports 1G, 2.5G and 10G modules
|
||||
- LEDs work with the same indiciations as the OEM firmware
|
||||
|
||||
### PCB overview
|
||||
|
||||
**Board markings**
|
||||
- Top silkscreen: SWTG018AS-A-V2.0.1_19649
|
||||
|
||||
Top side
|
||||
|
||||
<img src="photos/SWTG018AS_A_V_2_0/pcb_top.jpg" width="300" />
|
||||
|
||||
Bottom
|
||||
|
||||
<img src="photos/SWTG018AS_A_V_2_0/pcb_bottom.jpg" width="300" />
|
||||
|
||||
### J1, serial console
|
||||
|
||||
| `J1` pin | Signal |
|
||||
| -------- | ----------- |
|
||||
| 1 | 3V3 |
|
||||
| 2 | GND |
|
||||
| 3 | RX (Input) |
|
||||
| 4 | TX (Output) |
|
||||
|
||||
|
||||
## Power supply
|
||||
|
||||
Input power is delivered via barell plug, `12V 1A` adapter was provided.
|
||||
@@ -8,32 +8,56 @@ Also the RJ45 connectors can be all plastic/non-shielded or with metal shielding
|
||||
## Brands
|
||||
|Brand|Type|Managed|PCB|PCB Label|Flash|Chip RTL|
|
||||
|---|---|---|---|---|---|---|
|
||||
| LIANGUO |SWTG024AS |No| SWTG024AS-v2.0 | CM-23-11-2336 023-17453| 512kB| 8272 |
|
||||
| LIANGUO |SWTG024AS |No| SWTG024AS-v2.0-17452 | CM-23-11-2336 023-17453| 512 KiB | 8272 |
|
||||
| Haraco |ZX-SWTG124AS | Yes | SWTG024AS-v2.0 | ??? | ??? | 8272 |
|
||||
| Xikestore |SKS3200M-4GPY2XF | Yes | SWTG024AS-v1.0 | CM-23-08-2043 023-16721 | ??? | 8272 |
|
||||
| Sodola | SL-SWTG124AS-D | Yes | SWTG024AS-v2.0-17452 | ??? | 2048 KiB | 8272 |
|
||||
|
||||
## PCB
|
||||
|
||||
<img src="photos/SWTG024AS-v2.0-unmanaged/SWTG024AS-v2.0-top-uman.png" width="300" />
|
||||
|
||||
# SWTG024AS-v2.0 managed vs unmanged
|
||||
Changes I found with my board vs [Managed version](https://github.com/up-n-atom/SWTG118AS/tree/main/photos/SWGT024AS-v2.0) of the PCB.
|
||||
|
||||
### Bottom
|
||||
* R105: Installed, goes to R10-PullDown SFP2 -> TX DISABLE
|
||||
* R105: Installed, goes to R10-PullDown SFP2 (J2) -> TX-DISABLE
|
||||
* R85: Not Installed (Connected to K1 Reset Button)
|
||||
* R90: Not installed (System Led)
|
||||
* LED3: Not installed (System Led)
|
||||
### Top
|
||||
* K1: Not installed (Reset Button)
|
||||
* R95: Installed (SFP2 signal RX-LOS), means that the managed-version can´t use the RX-LOS function.
|
||||
* R270: Installed (SFP1 signal RX-LOS), same here as above.
|
||||
* R268: Installed (SFP2 signal TX_DISABLE, but R262 200R pull-down is to high to drive by the SOC, needs mod!)
|
||||
* U5: Flash is only 512kB instead of 2/4 MBit.
|
||||
* R95: Installed (SFP2 (J2) signal RX-LOS), means that the managed-version can´t use the RX-LOS function.
|
||||
* R270: Installed (SFP1 (J4) signal RX-LOS), same here as above.
|
||||
* R268: Installed (SFP2 (J2) signal TX-DISABLE, but R262 200R pull-down is to high to drive by the SOC, needs mod!)
|
||||
* U5: Flash is only 512 KiB instead of 2/4 MiB.
|
||||
|
||||
### Notes
|
||||
* `TX Disable`-SFP2 and Button `K1` share the same GPIO pin via `R105` and `R85`.
|
||||
But via `R88`, `TX Disable`-SFP2 can be mapped to `GPIO36`.
|
||||
* `TX Disable` pull-down resistos on both SFP are to low to drive by the SOC.
|
||||
* `TX-Disable`-SFP2 and Button `K1` share the same GPIO pin via `R105` and `R85`.
|
||||
But via `R88`, `TX-Disable`-SFP2 can be mapped to `GPIO36`.
|
||||
* `TX-Disable` pull-down resistos on both SFP are to low to drive by the SOC.
|
||||
We need to make a `Best`-BOM variant so we can use all the featues.
|
||||
|
||||
# Connectors
|
||||
|
||||
## Port overview
|
||||
|
||||
```
|
||||
┌────────────────────────────────────────────────────────────────────────────────────────┐
|
||||
│ ┌──────────┐ ┌──────────┐ │
|
||||
│ ┌─────────┐ ┌─────────┐ ┌─────────┐ ┌─────────┐ │ SFP (J4) │ │ SFP (J2) │ │
|
||||
│ │ RJ45 │ │ RJ45 │ │ RJ45 │ │ RJ45 │ │ PORT 5 │ │ PORT 6 │ │
|
||||
│ │ PORT 1 │ │ PORT 2 │ │ PORT 3 │ │ PORT 4 │ │ MAC 8 │ │ MAC 3 │ │
|
||||
│ O │ MAC 4 │ │ MAC 5 │ │ MAC 6 │ │ MAC 7 │ │ SerDes 1 │ │ SerDes 0 │ │
|
||||
│ RST └─────────┘ └─────────┘ └─────────┘ └─────────┘ └──────────┘ └──────────┘ │
|
||||
└────────────────────────────────────────────────────────────────────────────────────────┘
|
||||
```
|
||||
|
||||
## J4
|
||||
|
||||
* Location: Left SFP connector `J4`.
|
||||
* Connected to: 10GMAC number 8, second SerDes.
|
||||
|
||||
|`J4` SFP1 PINs | Signal | Component | GPIO | Notes |
|
||||
|---|---|---|---|---|
|
||||
|2| TX_FAULT | B-R262 | --- | |
|
||||
@@ -45,6 +69,11 @@ Changes I found with my board vs [Managed version](https://github.com/up-n-atom/
|
||||
|8| LOS | B-R258, T-R270 | GPIO37 | |
|
||||
|9| TO? | B-R256 | --- | |
|
||||
|
||||
## J2
|
||||
|
||||
* Location: Right SFP connector `J2`.
|
||||
* Connected to: 10GMAC number 3, first SerDes.
|
||||
|
||||
|`J2` SFP2 PINs | Signal | Component | GPIO | Notes |
|
||||
|---|---|---|---|---|
|
||||
|2| TX_FAULT | B-R70 | --- | |
|
||||
@@ -62,19 +91,27 @@ Note: component numbering `<L>-<REFDES>-<SIDE>`
|
||||
* SIDE: Side of the component. when the rj45 are facing towards you are you can read the silkscreen normal.
|
||||
L = Left, R=right, B=bottom, T=top or P with a pin number.
|
||||
|
||||
### T3
|
||||
This connector seems to go to U4 and U10.
|
||||
I thing is used to connect a external CPU to controlle the SOC.
|
||||
Even to program the flash via the SOC.
|
||||
### T3, Slave Interface
|
||||
This connector goes to U4 `I2C EEPROM` and U10 `SPI FLASH`.
|
||||
Signals are based on that `U4` is likely a I2C-EEPROM, `U10` is likely other SPI-chip.
|
||||
|`T3` pin|what|Signal|
|
||||
|---|---|---|
|
||||
|1| U4-P6, 33R U10-P6 | I2C-SCL, SPI-CLK |
|
||||
|1| U4-P6, 33R U10-P6 | I2C-SCL, SPI-CLK, Slave SCK/SCL/MDC/EE_SCL |
|
||||
|2| GND | --- |
|
||||
|3| U4-P5, U10-P5 | I2C-SDA, SPI-DI/DO |
|
||||
|3| U4-P5, U10-P5 | I2C-SDA, SPI-DI/DO, Slave SDI/SDA/MDIO/EE_SDA |
|
||||
|4| VCC |
|
||||
|5| 33R -> U10-P2 | SPI-DO/D1 |
|
||||
|6| U10-P1 | SPI-CS |
|
||||
Note: 1 pin is square shaped.
|
||||
|
||||
The Slave Interface allows an extenal host to controll the SOC even if the internal MCU is used.
|
||||
Depending on the `IF_SEL` bootstrap resistors, this can me `I2C`, `SPI` or `SMI`.
|
||||
On this device it is `I2C` on address `0b1011100` or `0x5c` (7-bit notation).
|
||||
|
||||
* I2c Read: must be a write_read opperation `<Dev-ADDR><RegAddr15:8><RegAddr7:0>` `<DevAddr><Data7:0><Data15:8><Data23:16><Data31:24>`.
|
||||
* I2c Write: `<Dev-ADDR><RegAddr15:8><RegAddr7:0><DevAddr><Data7:0><Data15:8><Data23:16><Data31:24>`.
|
||||
|
||||
Example register `0x0004` return chip id `0x00, 0x00, 0x72, 0x83` = `0x83720000`.
|
||||
|
||||
### T5, serial console
|
||||
|`T5` pin|GPIO|Signal|
|
||||
@@ -83,6 +120,7 @@ Signals are based on that `U4` is likely a I2C-EEPROM, `U10` is likely other SPI
|
||||
| 2 | GND | |
|
||||
| 3 | GPIO32 | U0RXD (Input) |
|
||||
| 4 | 3V3 | |
|
||||
Note: 1 pin is square shaped.
|
||||
|
||||
### T8
|
||||
|`T8` pin|what|Signal|
|
||||
@@ -93,6 +131,7 @@ Signals are based on that `U4` is likely a I2C-EEPROM, `U10` is likely other SPI
|
||||
| 4 | 3V3 | |
|
||||
| 5 | GPIO47 | |
|
||||
| 6 | GPIO49 | |
|
||||
Note: 1 pin is square shaped.
|
||||
|
||||
# Reset ciruit
|
||||
| Cmp | Function |
|
||||
@@ -110,9 +149,9 @@ Reset-line found at `T-D3-D` active-low.
|
||||
| 00000002 | GPIO01 | T-C152-T |? | | GPIO33 | | |
|
||||
| 00000004 | GPIO02 | T-C153-T |? | | GPIO34 | | |
|
||||
| 00000008 | GPIO03 | T-R33-T |? | | GPIO35 | | |
|
||||
| 00000010 | GPIO04 | B-C155 |? | | GPIO36 | T-R88-L, T-R84-B | Optional SFP-TX-DIS[^2], Reset |
|
||||
| 00000010 | GPIO04 | B-C155 |? | | GPIO36 | T-R88-L, T-R84-B | Optional SFP2 TX-DISABLE[^2], Reset |
|
||||
| 00000020 | GPIO05 | B-C156 |? | | GPIO37 | SFP1-8, T-R270 | SFP-LOS |
|
||||
| 00000040 | GPIO06 | T-C157-T |? | | GPIO38 | SFP1-3, T-R268 | SFP-TX-DIS[^2] |
|
||||
| 00000040 | GPIO06 | T-C157-T |? | | GPIO38 | SFP1-3, T-R268 | SFP1 TX-DISABLE[^2] |
|
||||
| 00000080 | GPIO07 | T-C158-T, R165 |? | | GPIO39 | SFP1-4, T-R266 | I2C-SDA4 |
|
||||
| 00000100 | GPIO08 | | | | GPIO40 | SFP2-5, T-R87; SFP1-5, T-R267; | I2C-SCL |
|
||||
| 00000200 | GPIO09 | SFP2-LED, T-R36-T |LED-SFP2 | | GPIO41 | SFP2-4, T-R85 | I2C-SDA |
|
||||
@@ -128,7 +167,7 @@ Reset-line found at `T-D3-D` active-low.
|
||||
| 00080000 | GPIO19 | PORT3-LED-GREEN |LEDx[^1] | | GPIO51 | SFP2-8, T-R95 | SFP-LOS |
|
||||
| 00100000 | GPIO20 | PORT3-LED-YELLOW |LEDx | | GPIO52 | | |
|
||||
| 00200000 | GPIO21 | |LEDx[^1] | | GPIO53 | | |
|
||||
| 00400000 | GPIO22 | PORT4-LED-GREEN |LEDx[^1] | | GPIO54 | SFP2-3, T-R105-L | SFP-TX-DIS[^2] or via T-R85 to RESET[^3], T-R84-T |
|
||||
| 00400000 | GPIO22 | PORT4-LED-GREEN |LEDx[^1] | | GPIO54 | SFP2-3, T-R105-L | SFP2 TX-DISABLE[^2] or via T-R85 to RESET[^3], T-R84-T |
|
||||
| 00800000 | GPIO23 | PORT4-LED-YELLOW |LEDx | | GPIO55 | T-R78-B | |
|
||||
| 01000000 | GPIO24 | SFP1-LED-J4, T-R35 |LED-SFP1 | | GPIO56 | | |
|
||||
| 02000000 | GPIO25 | | | | GPIO57 | | |
|
||||
@@ -170,5 +209,5 @@ So higher power SFP-modules should work.
|
||||
|
||||
|
||||
[^1]: LEDs are found by just plugin a RJ45 connector and see with cmd `gpio` the status change. But the bit pattern for port 1,2 are diffrent from port 3,4.
|
||||
[^2]: Only on the unmanaged verions are `R10` and `R268` placed. But the very low pull-down resistor `R10` & `R262` prevent to SOC to drive does pins. A mod is needed.
|
||||
[^2]: Only on the unmanaged verions are `R10` and `R268` placed. But the very low pull-down resistor `R10` and `R262` prevent to SOC to drive does pins. A mod is needed.
|
||||
[^3]: GPIO54 is used for the reset-button. `T-R85` is placed.
|
||||
|
||||
@@ -0,0 +1,24 @@
|
||||
### ZX-SWTGW218AS
|
||||
|
||||
## Brands
|
||||
|Brand|Type|Managed|PCB|Flash|Chip RTL|
|
||||
|---|---|---|---|---|---|
|
||||
| Mokerlink | ZX-SWTGW218AS | Yes| SWTG118AS-V2.0-16029 | 2MB (FM25Q16A)| 8273N + 8224N |
|
||||
| Sodola | | | | | |
|
||||
| Horaco | | | | | |
|
||||
|
||||
|
||||
## Photos
|
||||
|
||||
<img src="photos/SWTGW218AS-managed/front.png" width="800" />
|
||||
|
||||
<img src="photos/SWTGW218AS-managed/label.jpg" width="800" />
|
||||
|
||||
## PCB
|
||||
|
||||
See up'n'atom's Repo:
|
||||
|
||||
https://github.com/up-n-atom/SWTG118AS/blob/main/photos/SWTG118AS-v2.0/SWTG118AS-v2.0-pcb-top.JPG
|
||||
|
||||
https://github.com/up-n-atom/SWTG118AS/blob/main/photos/SWTG118AS-v2.0/SWTG118AS-v2.0-pcb-bottom.JPG
|
||||
|
||||
@@ -0,0 +1,175 @@
|
||||
# TrendNet TEG-S562
|
||||
|
||||
Following is documentation for unmanaged switch marked as `TEG-S563/EU H/W: V1.0R`.
|
||||
|
||||
Original software is running UART on 57600 baud rate. The software does not allow to
|
||||
do anything fancy via serial. There is `IP` configuration which can be printed as well.
|
||||
There might be also some flash upload procedure, but using SPI clamp in-board seems to
|
||||
be easier method.
|
||||
|
||||
The memory chip is `Winbond W25Q16JV` with 16M-bit size.
|
||||
|
||||
## What does work
|
||||
|
||||
1. 2.5G ports on all advertised speeds.
|
||||
2. SFP+ communication.
|
||||
3. Serial, Web UI.
|
||||
|
||||
## Known issues
|
||||
|
||||
1. LEDs are not initialized properly.
|
||||
|
||||
## PCB
|
||||
|
||||
Manufacturer information be found [on the product page](https://www.trendnet.com/support/support-detail.asp?prod=105_TEG-S562).
|
||||
|
||||
Top side
|
||||
|
||||
<img src="photos/TEG-S562/TEG-S562-v1.0R-top.jpg" width="300" />
|
||||
|
||||
Bottom
|
||||
|
||||
<img src="photos/TEG-S562/TEG-S562-v1.0R-bottom.jpg" width="300" />
|
||||
|
||||
## Connectors
|
||||
|
||||
### Port overview
|
||||
|
||||
```
|
||||
┌─────────────────────────────────────────────────────────────────────────────┐
|
||||
│ ┌──────────┐ ┌──────────┐ │
|
||||
│ ┌─────────┐ ┌─────────┐ ┌─────────┐ ┌─────────┐ │ SFP │ │ SFP │ │
|
||||
│ │ RJ45 │ │ RJ45 │ │ RJ45 │ │ RJ45 │ │ PORT 5 │ │ PORT 6 │ │
|
||||
│ │ PORT 1 │ │ PORT 2 │ │ PORT 3 │ │ PORT 4 │ │ MAC ? │ │ MAC ? │ │
|
||||
│ │ MAC 4 │ │ MAC 5 │ │ MAC 6 │ │ MAC 7 │ │ SerDes ? │ │ SerDes ? │ │
|
||||
│ └─────────┘ └─────────┘ └─────────┘ └─────────┘ └──────────┘ └──────────┘ │
|
||||
└─────────────────────────────────────────────────────────────────────────────┘
|
||||
```
|
||||
|
||||
### J2, serial console
|
||||
|
||||
| `J2` pin | Signal |
|
||||
| -------- | ----------- |
|
||||
| 1 | 3V3 |
|
||||
| 2 | TX (Output) |
|
||||
| 3 | RX (Input) |
|
||||
| 4 | GND |
|
||||
|
||||
Note: 1 pin is square shaped, towards the power input.
|
||||
|
||||
### J5, power pass-thru
|
||||
|
||||
| `J5` pin | Signal |
|
||||
| -------- | ------ |
|
||||
| 1 | 12V |
|
||||
| 2 | 12V |
|
||||
| 3 | GND |
|
||||
| 4 | GND |
|
||||
|
||||
Note: 1 pin is square shaped.
|
||||
|
||||
### U5, I2C eeprom placeholder
|
||||
|
||||
| `J5` pin | Signal |
|
||||
| -------- | ------------- |
|
||||
| 1 | GND |
|
||||
| 2 | GND |
|
||||
| 3 | GND |
|
||||
| 4 | GND |
|
||||
| 5 | 3V3 |
|
||||
| 6 | ??? Logic Low |
|
||||
| 7 | SCL |
|
||||
| 8 | SDA |
|
||||
|
||||
SOC I2C address is 0x5c.
|
||||
|
||||
### SW1 GPIO switch?
|
||||
|
||||
Not populated but looks like a switch for selecting
|
||||
GPIO level. Missing resistors in place.
|
||||
|
||||
GPIO mapping unknown.
|
||||
|
||||
### S2 Reset ciruit
|
||||
|
||||
Not populated but looks like a button can be added on `S2` connector,
|
||||
requires additional `R571` resistor which will pull signal to ground.
|
||||
|
||||
GPIO mapping unknown.
|
||||
|
||||
### GPIO
|
||||
|
||||
| HEX VAL. | GPIO | When | GPIO | When |
|
||||
| -------- | ------ | ----------------| ------ | -----------------------|
|
||||
| 00000001 | GPIO00 | | GPIO32 | |
|
||||
| 00000002 | GPIO01 | | GPIO33 | |
|
||||
| 00000004 | GPIO02 | | GPIO34 | Random changes |
|
||||
| 00000008 | GPIO03 | | GPIO35 | |
|
||||
| 00000010 | GPIO04 | | GPIO36 | SFP1 Present |
|
||||
| 00000020 | GPIO05 | | GPIO37 | SFP1 RX Los |
|
||||
| 00000040 | GPIO06 | | GPIO38 | SFP2 Present |
|
||||
| 00000080 | GPIO07 | | GPIO39 | |
|
||||
| 00000100 | GPIO08 | | GPIO40 | |
|
||||
| 00000200 | GPIO09 | | GPIO41 | |
|
||||
| 00000400 | GPIO10 | | GPIO42 | Random changes |
|
||||
| 00000800 | GPIO11 | | GPIO43 | |
|
||||
| 00001000 | GPIO12 | PORT1 Link | GPIO44 | |
|
||||
| 00002000 | GPIO13 | PORT1-LED-GREEN | GPIO45 | |
|
||||
| 00004000 | GPIO14 | PORT1-LED-AMBER | GPIO46 | SFP1 I2C CLK |
|
||||
| 00008000 | GPIO15 | PORT2 Link | GPIO47 | SFP1 I2C SDA |
|
||||
| 00010000 | GPIO16 | PORT2-LED-GREEN | GPIO48 | SFP2 I2C CLK |
|
||||
| 00020000 | GPIO17 | PORT2-LED-AMBER | GPIO49 | SFP2 I2C SDA |
|
||||
| 00040000 | GPIO18 | PORT3 Link | GPIO50 | SFP2 Rx LOS |
|
||||
| 00080000 | GPIO19 | PORT3-LED-GREEN | GPIO51 | SFP1 TX Disable |
|
||||
| 00100000 | GPIO20 | PORT4-LED-AMBER | GPIO52 | |
|
||||
| 00200000 | GPIO21 | PORT4 Link | GPIO53 | |
|
||||
| 00400000 | GPIO22 | PORT4-LED-GREEN | GPIO54 | SFP2 TX Disable |
|
||||
| 00800000 | GPIO23 | PORT4-LED-AMBER | GPIO55 | |
|
||||
| 01000000 | GPIO24 | | GPIO56 | |
|
||||
| 02000000 | GPIO25 | | GPIO57 | |
|
||||
| 04000000 | GPIO26 | | GPIO58 | |
|
||||
| 08000000 | GPIO27 | | GPIO59 | |
|
||||
| 10000000 | GPIO28 | | GPIO60 | |
|
||||
| 20000000 | GPIO29 | | GPIO61 | |
|
||||
| 40000000 | GPIO30 | | GPIO62 | |
|
||||
| 80000000 | GPIO31 | | GPIO63 | |
|
||||
|
||||
## LEDs
|
||||
|
||||
Leds are not yet working as in stock firmware. This will be handled later.
|
||||
|
||||
Ports 1-4 are amber for 100M/1G links, Green for 2.5G.
|
||||
Port 5-6 are green for 10G/1G link. Both should flash on activity.
|
||||
|
||||
| NAME | When active |
|
||||
| ---------------- | ---------------|
|
||||
| PWR | 3V3 |
|
||||
| SFP1 | |
|
||||
| SFP2 | |
|
||||
| PORT1-LED-GREEN | - |
|
||||
| PORT2-LED-GREEN | PORT2 2.5G |
|
||||
| PORT3-LED-GREEN | PORT3 2.5G |
|
||||
| PORT4-LED-GREEN | PORT4 2.5G |
|
||||
| PORT1-LED-AMBER | PORT1 1GB/100M |
|
||||
| PORT2-LED-AMBER | PORT2 1GB/100M |
|
||||
| PORT3-LED-AMBER | PORT3 1GB/100M |
|
||||
| PORT4-LED-AMBER | PORT4 1GB/100M |
|
||||
|
||||
## Power supply
|
||||
|
||||
Input power is delivered via barell plug, `12V 1A` adapter was provided.
|
||||
Board has two supply rails. `0.95` and `3.3` volt.
|
||||
|
||||
### `0.95` Core Voltage
|
||||
|
||||
Voltage is made by a `APW8713` (U3).
|
||||
|
||||
### `3.3` Voltage
|
||||
|
||||
Voltage is crated regulated by chip marke as `GoIAT` (U2).
|
||||
|
||||
## SFP SPI
|
||||
|
||||
There is separate clock and data lines for both SFP modules. MSDA/MSCK 0 and 1 need to be enabled.
|
||||
|
||||
SFP1 slot is connected to SPI0. SFP2 slot is connected to SPI1.
|
||||
|
After Width: | Height: | Size: 1.3 MiB |
|
After Width: | Height: | Size: 1.4 MiB |
|
After Width: | Height: | Size: 754 KiB |
|
After Width: | Height: | Size: 521 KiB |
|
After Width: | Height: | Size: 1.0 MiB |
|
After Width: | Height: | Size: 3.1 MiB |
|
After Width: | Height: | Size: 2.8 MiB |
|
After Width: | Height: | Size: 2.6 MiB |
|
After Width: | Height: | Size: 1.3 MiB |
|
After Width: | Height: | Size: 1.6 MiB |
|
After Width: | Height: | Size: 1.6 MiB |
|
After Width: | Height: | Size: 876 KiB |
|
After Width: | Height: | Size: 1.5 MiB |
|
After Width: | Height: | Size: 1.8 MiB |
|
After Width: | Height: | Size: 1.2 MiB |
|
After Width: | Height: | Size: 558 KiB |
|
After Width: | Height: | Size: 1.8 MiB |
|
After Width: | Height: | Size: 2.6 MiB |
@@ -0,0 +1,41 @@
|
||||
# Understanding the image using ghidra
|
||||
Start ghidra, load file starting from offset 0x0002 into
|
||||
memory starting at 0x0000. The lengthe is 0x10000. Select generic 8051, big
|
||||
endian.
|
||||
|
||||
After loading, the boot vector is at 0x0000, which will jump to 0x0100 for
|
||||
the boot routine.
|
||||
|
||||
The firmware uses only bank 1 of the RTL837x since it is quite short.
|
||||
Otherwise the firmware would be organized as follows
|
||||
```
|
||||
--------------------------- 0x0000 ---------------------------------
|
||||
Boot-Vector
|
||||
ISRs
|
||||
Common Code
|
||||
Trampoline for inter-bank calls
|
||||
Inter-bank calls, calling trampoline, one for each callable function
|
||||
|
||||
----- Bank 1 0x4000 ------ ---- Bank 2 0x4000 ----- -------- .....
|
||||
Overlay 1 Overlay 2 Overlay n
|
||||
|
||||
--------- 0xffff --------- -------- 0xffff -------- -------- 0xffff
|
||||
```
|
||||
The RTL837x firmware images are organized as follows:
|
||||
The first 2 bytes of the image give the size of the prefetched data at the
|
||||
start of the CPU power up. The default is 0x4000 (bytes: 0x00 0x40), which
|
||||
means that the entire shared area of the code memory in all banks,
|
||||
0x4000 bytes is read immediately into the code RAM.
|
||||
|
||||
Common code starts at
|
||||
0x0002 in the image and has length 0x3ffd, the first bank starts at 0x4000
|
||||
in the image, is mapped to 0x4000 and has length 0xc000. The second bank
|
||||
starts at 0x10000, is mapped to 0x4000 and has length 0xc000. The third
|
||||
bank would start at 0x1c000 and would again be mapped to 0x4000.
|
||||
There are about 30 banks in use for managed switches, unmanaged ones use
|
||||
2-3, while the hardware would allow to use 0x3f banks, i.e. up to 4 MB of
|
||||
flash.
|
||||
|
||||
The current image uses Common BANK0 and the first BANK1 via sdccs __banked
|
||||
function keyword and custom banking trampoline code for the RTL837x in
|
||||
assembler.
|
||||
@@ -0,0 +1,58 @@
|
||||
#RTL8272/3 features
|
||||
|
||||
The following hardware features of the RTL8372/3 is supported:
|
||||
- Clock generation, including different divider settings
|
||||
- Interrupt control for timer, serial, external irqs 0, 1
|
||||
- Serial console via SFRs
|
||||
- Flash operations via SFRs
|
||||
- Bank switching via SFRs
|
||||
- Access to Switch registers via SFRs
|
||||
- LED setup
|
||||
- Reset
|
||||
- Some switch settings such as MAC configuration
|
||||
- GPIO to detect SFP module insert/removal/RX-LOS (depending on device/module support)
|
||||
- I2C to read SFP EEPROM on 1 and 2 SFP slot devices
|
||||
- NIC setup
|
||||
- L2 learning table access, L2 table flushing
|
||||
- VLAN setup/configuration
|
||||
- Port mirroring
|
||||
- Access to PHYs via MDIO (clause 45 via SFR):
|
||||
- Internal PHYs of RTL8372 and RTL8373
|
||||
- RTL8221 (1x2.5GBit port on devices with 5 ports)
|
||||
- RTL8224 (4x2.5GBit ports on devices with 8 ports)
|
||||
- SerDes settings of SoC via SFR:
|
||||
- Configure SFPs with 10Gbit/2.5Gbit/1Gbit (Ethernet and Fiber SFP(+) tested)
|
||||
- RTL8221, RTL8224
|
||||
- NIC TX and RX of packets via SFRs
|
||||
- send and receive Ethernet frames via SFRs and Switch registers
|
||||
- RTL-tags and VLAN ingress-tag decoding for CPU-port
|
||||
|
||||
Ethernet frame RX IRQ via IRQ1 is conceptually understood, but not activated. RX is
|
||||
currently done via polling, which allows ping-times of <10ms.
|
||||
|
||||
The RTL8372/3 have 256 bytes of internal RAM (INTMEM) accessible through MOV
|
||||
instructions, which are used for the stack and important globals. Some of
|
||||
these are bit-adressable, e.g. for storing global flags.
|
||||
|
||||
Additionally, 64kB of extended RAM (XMEM) is built in, which is accessed
|
||||
through the MOVX instruction. It is used for global variables, for most
|
||||
of the function argument passing that is not done using the 8 registers
|
||||
R0-R7 or registers A/B, and for local variables (which requires extremely
|
||||
careful planning). The flash memory is transparently accessible for code
|
||||
being executed and can be used to store configuration. Access is done through
|
||||
the MOVC instruction, possibly setting the bank register before and
|
||||
resetting it to access the entire 4MB space. Code is prefetched from flash
|
||||
and cached in a small RAM automatically by the HW.
|
||||
|
||||
The peripherial functions are accessed through 2 different mechanisms:
|
||||
- Special Function Registers (SFRs, 0x80-0xff) for banking, timers, UART, access to
|
||||
switch registers, MDIO, SPI (flash) and NIC transfers. Some SFRs are not
|
||||
used for HW purposes and can be used as RAM. Some SFRs are bit-adressable,
|
||||
allowing for very tight event wait loops (a single 2-byte instruction).
|
||||
- 0x10000 switch registers, which appear to be very similar to the registers
|
||||
of the RTL838x, for which source code and datasheets are available. This
|
||||
controls clock dividers, GPIO/LEDs and general switch functionality.
|
||||
|
||||
The playground image shows access to the different types of memory using the
|
||||
SDCC compiler. Any support of Linux or e.g. Zephyr would require porting gcc.
|
||||
There are FreeRTOS ports to 8051 processors using sdcc, however.
|
||||
@@ -0,0 +1,135 @@
|
||||
# IGMP (Internet Group Management Protocol) and MLD (Multicast Listener Discovery)
|
||||
IGMP (for IPv4) and MLD (for IPv6) are protocols that control the distribution
|
||||
of Layer-3 Multicast packets on the LAN, which otherwise would be flooded across the
|
||||
entire network. For this to work, IGMP/MLD messages are sent, in particular
|
||||
from MC consumers (e.g. the video-player that plays an IP-Multicast stream), but
|
||||
also Multicast-aware routers to control switching of the IP-MC or underlying
|
||||
L2-MC packets. The main usage in home networks is IPTV.
|
||||
|
||||
The RTL8372/3 SoC supports managing IPv4-MC using either Destination-IP (the IPv4
|
||||
multicast group address)/Source-IP (typically 0.0.0.0) matching or via controlling
|
||||
the switching of the underlying L2-MC packets (i.e. packets in 01:00:5e:xx:yy:zz, where
|
||||
xx:yy:zz are the LSBs of the IPv4-MC address). The DIP/SIP-based switching is
|
||||
not VLAN-aware, meaning a stream will be available in all VLANs if subscribed to.
|
||||
This is not a problem in a typical home network, however. The L2-based method
|
||||
is VLAN aware, but currently not supported in the software.
|
||||
|
||||
Although there is hardware support for IPv6/MLD-based Multicast management (i.e. intelligent
|
||||
management by the switch), the current software does not implement managing IPv6 Multicast.
|
||||
Instead, all IPv6 Multicast pakets will be flooded to all ports, just as an unmanaged
|
||||
switch would do.
|
||||
|
||||
The current software support works by trapping IGMP packets (only v3 supported, which
|
||||
is used in the vast majority of today's networks) to the CPU of the switch which will
|
||||
update the L3 and L2 switching tables to include switch ports in a stream or remove
|
||||
them. This trapping to the CPU is also called IGMP snooping. While there is support
|
||||
in the HW to handle IGMP/MLD packets (v3 has only limited support) entirely in hardware
|
||||
and even send out reports, it is currently not understood
|
||||
how this works, and instead IGMP is handled entirely in software, which also allows
|
||||
to fully support IGMPv3 packet which are the standard in present-day networks.
|
||||
|
||||
## IP-MC control
|
||||
The relevant registers for controlling IP-MC switching are:
|
||||
```
|
||||
#define RTL837X_IPV4_PORT_MC_LM_ACT 0x4f78
|
||||
#define RTL837X_IPV6_PORT_MC_LM_ACT 0x4f7c
|
||||
#define RTL837X_IGMP_PORT_CFG 0x52a0
|
||||
#define IGMP_MAX_GROUP 0x00ff0000
|
||||
#define IGMP_PROTOCOL_ENABLE 0x00007c00
|
||||
#define IGMP_TRAP 0x0000002a
|
||||
#define IGMP_FLOOD 0x00000015
|
||||
#define IGMP_ASIC 0x00000000
|
||||
#define RTL837X_IGMP_ROUTER_PORT 0x529c
|
||||
#define RTL837X_IPV4_UNKN_MC_FLD_PMSK 0x5368
|
||||
#define RTL837X_IPV6_UNKN_MC_FLD_PMSK 0x536c
|
||||
#define RTL837X_IGMP_TRAP_CFG 0x50bc
|
||||
#define IGMP_TRAP_PRIORITY 0x7
|
||||
#define IGMP_CPU_PORT 0x00010000
|
||||
```
|
||||
`RTL837X_IPV4_PORT_MC_LM_ACT/RTL837X_IPV6_PORT_MC_LM_ACT` control the action when an
|
||||
IP-MC packet is encountered at a switch port and there is no rule for forwarding in
|
||||
the forwarding tables. The default action is to flood such Lookup-Miss packets to all
|
||||
ports. This is the configuration without IGMP/MLD enabled.
|
||||
|
||||
When IGMP/MLD is turned on, the Lookup-Miss action will be changed to drop such packets
|
||||
unless a rule is found in the forwarding tables, which will need to be configured by
|
||||
IGMP packets.
|
||||
|
||||
Switching on IGMP also configures all ports via `RTL837X_IGMP_PORT_CFG` to trap all
|
||||
incoming IGMP packets to the CPU. `RTL837X_IGMP_TRAP_CFG` then is used to configure
|
||||
priority and CPU-Port of trapped IGMP/MLD packets.
|
||||
|
||||
Configuration of the IP-MC-forwarding to the listening ports is done by managing the
|
||||
forwarding tables of the switch, see [L2 learning](l2.md).
|
||||
|
||||
|
||||
## IGMP API
|
||||
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;
|
||||
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.
|
||||
|
||||
`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_show()` prints out the IGMP configuration on the CLI.
|
||||
|
||||
|
||||
## IGMP configuration on the Serial Console
|
||||
For testing the following commands are provided on the serial console:
|
||||
```
|
||||
> igmp [on/off]
|
||||
Enables or disables IGMP
|
||||
|
||||
> igmp show
|
||||
Shows information on IGMP
|
||||
```
|
||||
|
||||
## LAG configuration via the Web Interface
|
||||
Not implemented, yet!
|
||||
|
||||
## A Test with IP-MC streaming using vlc
|
||||
The following is a simple test verifying the IGMP and IP-MC switching capabilities.
|
||||
|
||||
You will need 2 Linux/Windows devices with a GUI plus a switch.
|
||||
|
||||
Connect the switch to an MC-aware router (e.g. to your home network). Connect the 2 Linux/Windows
|
||||
devices to the switch. The connection to the router makes sure that Linux/Windows will send
|
||||
out IGMP messages on the ports connected to the switch, which they will only do if they are aware
|
||||
that there is a MC-aware router in the network. Make sure the 2 GUI devices are in the home network
|
||||
(e.g. via DHCP).
|
||||
|
||||
Start streaming on one of the Linux/Windows machines:
|
||||
```
|
||||
$ vlc your_video.mp4 --sout="#std{access=udp, mux=ts, dst=239.255.0.1:8090}"
|
||||
```
|
||||
At this point you should see all switch ports flickering heavily as the MC stream is switched to all
|
||||
switch ports, including flooding your home network. If you do not see any packets arriving at the switch,
|
||||
you can force the output interface of vlc by using `--miface=<ifname>`
|
||||
|
||||
Enable IGMP on the switch-CLI:
|
||||
```
|
||||
> igmp on
|
||||
```
|
||||
The flickering should now stop on all ports except the port where the streaming device is connected:
|
||||
the switch drops all IP-MC packets as there are no listeners.
|
||||
|
||||
Now, on the second Linux/Windows device start listening to the stream:
|
||||
```
|
||||
$ vlc udp://@239.255.0.1:8090
|
||||
```
|
||||
You should see the port-led of the port the displaying machine is connected to, to start flickering
|
||||
and after some synchronization, the video should start playing.
|
||||
|
||||
Stopping vlc should also switching of the IP-MC frames to the listening device, i.e. the port-leds
|
||||
should stop flickering.
|
||||
|
After Width: | Height: | Size: 72 KiB |
|
After Width: | Height: | Size: 83 KiB |
@@ -0,0 +1,227 @@
|
||||
# Link Aggregation (aka Trunking)
|
||||
|
||||
The RTL827x allows to combine multiple ports to a single logical link
|
||||
(Link Aggregation / Trunking) according to IEEE 802.3ad. LAGs allow to
|
||||
combine the individual physical links into a single link with the combined
|
||||
throughput and automatic redundancy when one of the link fails.
|
||||
Up to 4 Link Aggregation Groups (LAGs) can be defined on the switch devices.
|
||||
|
||||
## LAG control
|
||||
Four registers `RTL837X_TRK_MBR_CTRL_BASE(lag) (0x4f38-0x4f44)` define the LAG membership
|
||||
via a port mask of the logical port numbers.
|
||||
|
||||
A hash algorithm applied to L2, L3 and L4 properties of a packet are used to decide which
|
||||
of the links (ports) is being used to transfer the packet. The possible properties used in the
|
||||
hash are:
|
||||
```
|
||||
#define LAG_HASH_SOURCE_PORT_NUMBER 0x01
|
||||
#define LAG_HASH_L2_SMAC 0x02
|
||||
#define LAG_HASH_L2_DMAC 0x04
|
||||
#define LAG_HASH_L3_SIP 0x08
|
||||
#define LAG_HASH_L3_DIP 0x10
|
||||
#define LAG_HASH_L4_SPORT 0x20
|
||||
#define LAG_HASH_L4_DPORT 0x40
|
||||
#define LAG_HASH_DEFAULT (LAG_HASH_L2_SMAC | LAG_HASH_L2_DMAC | LAG_HASH_L3_SIP | LAG_HASH_L3_DIP | LAG_HASH_L4_SPORT | LAG_HASH_L4_DPORT)
|
||||
```
|
||||
The hash algorithm used to select links (exit ports) is defined for each LAG individually in
|
||||
`RTL837X_TRK_HASH_CTRL_BASE (0x4f48-0x4f54)`.
|
||||
|
||||
## Trunking API
|
||||
The code currently provides the following functions:
|
||||
```
|
||||
/*
|
||||
* Configure LAGs
|
||||
* Sets the members via port bitmask of a given Link Aggregation Group
|
||||
* The groups have numbers 0-3
|
||||
* The bitmask represents up to 10 ports
|
||||
* If currently no LAG has algorithm used, a default is applied
|
||||
*/
|
||||
void port_lag_members_set(__xdata uint8_t lag, __xdata uint16_t members) __banked;
|
||||
|
||||
/*
|
||||
* Configures the hash algorithm used for a LAG
|
||||
* lag is the Group to configure and hash is a bitmask
|
||||
*/
|
||||
void port_lag_hash_set(__xdata uint8_t lag, __xdata uint8_t hash_bits) __banked;
|
||||
```
|
||||
|
||||
## LAG configuration on the Serial Console
|
||||
For testing the following commands are provided on the serial console:
|
||||
```
|
||||
> lag <LAG-ID> [p1] [p2]...
|
||||
Create or set a LAG. Trunk-ID is 1 or 2. Ports are physical ports
|
||||
If only the LAG-ID is given but no members, the LAG is deleted
|
||||
|
||||
> lag show
|
||||
Shows information on all 4 lags
|
||||
|
||||
> laghash 0 [hash1] [hash2]...
|
||||
Uses the given packet properties when hashing the packet to select the link
|
||||
Names for the hashes are spa, smac, dmac, sip, dip, sport, dport
|
||||
```
|
||||
When a lag is creates, by default the hash is based on smac, dmac, sip, dip, sport, dport. When you
|
||||
use your own hash settings, make sure that the hash always uses both the source and destination
|
||||
property of the packet, as otherwise pakets will not be routed symmetrically.
|
||||
|
||||
## LAG configuration via the Web Interface
|
||||
In the web-interface select Link Aggregation in the left navigation panel. The page will look like this:
|
||||

|
||||
Each of th 4 LAGs is configured separately. After the web-page has loaded, the current configuration
|
||||
can be edited by clicking on the port-images to include that port or exclude it from a LAG.
|
||||
When pressing on the Create/Update button, the LAG will be automatically created if not yet done, or
|
||||
updated. If a lage is updated to not having any members, then it is effectively deleted.
|
||||
|
||||
All LAGs are created with the default hash-function (see above). This currently cannot be changed
|
||||
from the Web.
|
||||
|
||||
## A Test using a single Linux Desktop
|
||||
The following is a simple test using 2 RTL 2.5 GBit switches with at least 1 SFP+-port each. You
|
||||
will also need 4 10GBit SFP+ modules (DAC or Fiber) and 2 SFP+ ports on your desktop.
|
||||
|
||||
The following shows the network configuration
|
||||
```
|
||||
----------------- -----------------
|
||||
Linux Comuter | | 2.5 GBit | | same Linux Computer
|
||||
---------- 10G | P1 |------------| P1 | 10G ----------
|
||||
192.168.9.1 | SFP+ |==========| Switch 1 | 2.5 GBit | Switch 2 |==========| SFP+ | 192.168.9.2
|
||||
enp1s0f0 ---------- | P2 |------------| P2 | ---------- enp1s0f1
|
||||
| | | |
|
||||
------------------ -----------------
|
||||
```
|
||||
|
||||
On _both_ switches create a LAG with ports 1 and 2 inside and the default hash algorithm which takes
|
||||
source and destination ports into account, e.g. just use the default:
|
||||
```
|
||||
> lag 0 1 2
|
||||
```
|
||||
|
||||
|
||||
The following shows the configuration on the desktop using a dual 10GBit card with 2 SFP+ modules:
|
||||
```
|
||||
[234690.755634] ixgbe: Intel(R) 10 Gigabit PCI Express Network Driver
|
||||
[234690.755637] ixgbe: Copyright (c) 1999-2016 Intel Corporation.
|
||||
[234690.921614] ixgbe 0000:01:00.0: Multiqueue Enabled: Rx Queue count = 12, Tx Queue count = 12 XDP Queue count = 0
|
||||
[234690.921914] ixgbe 0000:01:00.0: 32.000 Gb/s available PCIe bandwidth (5.0 GT/s PCIe x8 link)
|
||||
[234690.921999] ixgbe 0000:01:00.0: MAC: 2, PHY: 19, SFP+: 5, PBA No: FFFFFF-0FF
|
||||
[234690.922002] ixgbe 0000:01:00.0: 28:41:c6:xx:xx:aa
|
||||
[234690.924946] ixgbe 0000:01:00.0: Intel(R) 10 Gigabit Network Connection
|
||||
[234690.990024] ixgbe 0000:01:00.0 enp1s0f0: renamed from eth0
|
||||
[234691.056447] ixgbe 0000:01:00.0: registered PHC device on enp1s0f0
|
||||
[234691.089417] ixgbe 0000:01:00.1: Multiqueue Enabled: Rx Queue count = 12, Tx Queue count = 12 XDP Queue count = 0
|
||||
[234691.089706] ixgbe 0000:01:00.1: 32.000 Gb/s available PCIe bandwidth (5.0 GT/s PCIe x8 link)
|
||||
[234691.089788] ixgbe 0000:01:00.1: MAC: 2, PHY: 19, SFP+: 18, PBA No: FFFFFF-0FF
|
||||
[234691.089790] ixgbe 0000:01:00.1: 28:41:c6:xx:xx:ab
|
||||
[234691.160997] ixgbe 0000:01:00.1: Intel(R) 10 Gigabit Network Connection
|
||||
[234691.166102] ixgbe 0000:01:00.1 enp1s0f1: renamed from eth0
|
||||
[234691.231579] ixgbe 0000:01:00.1: registered PHC device on enp1s0f1
|
||||
[234691.236965] ixgbe 0000:01:00.0 enp1s0f0: detected SFP+: 5
|
||||
[234691.485031] ixgbe 0000:01:00.0 enp1s0f0: NIC Link is Up 10 Gbps, Flow Control: RX/TX
|
||||
[234691.557003] ixgbe 0000:01:00.1 enp1s0f1: detected SFP+: 18
|
||||
[234691.753061] ixgbe 0000:01:00.1 enp1s0f1: NIC Link is Up 10 Gbps, Flow Control: RX/TX
|
||||
```
|
||||
|
||||
Now set up 2 network namespaces and put each interface inside one:
|
||||
```
|
||||
sudo ip netns add netns_eth0
|
||||
sudo ip netns add netns_eth1
|
||||
sudo ip link set enp1s0f0 netns netns_eth0
|
||||
sudo ip link set enp1s0f1 netns netns_eth1
|
||||
```
|
||||
|
||||
Configure network interface addresses 192.168.9.2 and 192.168.9.1 in each namespace:
|
||||
```
|
||||
sudo ip netns exec netns_eth0 ifconfig enp1s0f0 192.168.9.1 netmask 255.255.255.0
|
||||
|
||||
sudo ip netns exec netns_eth0 ip a
|
||||
1: lo: <LOOPBACK> mtu 65536 qdisc noop state DOWN group default qlen 1000
|
||||
link/loopback 00:00:00:00:00:00 brd 00:00:00:00:00:00
|
||||
24: enp1s0f0: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 qdisc mq state UP group default qlen 1000
|
||||
link/ether 28:41:c6:xx:xx:aa brd ff:ff:ff:ff:ff:ff
|
||||
altname enx2841c6xxxxaa
|
||||
inet 192.168.9.1/24 scope global enp1s0f0
|
||||
valid_lft forever preferred_lft forever
|
||||
inet6 fe80::2a41:c6ff:fexx:xxaa/64 scope link proto kernel_ll
|
||||
valid_lft forever preferred_lft forever
|
||||
|
||||
sudo ip netns exec netns_eth1 ifconfig enp1s0f1 192.168.9.2 netmask 255.255.255.0
|
||||
|
||||
sudo ip netns exec netns_eth1 ip a
|
||||
1: lo: <LOOPBACK> mtu 65536 qdisc noop state DOWN group default qlen 1000
|
||||
link/loopback 00:00:00:00:00:00 brd 00:00:00:00:00:00
|
||||
25: enp1s0f1: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 qdisc mq state UP group default qlen 1000
|
||||
link/ether 28:41:c6:xx:xx:ab brd ff:ff:ff:ff:ff:ff
|
||||
altname enx2841c6xxxxab
|
||||
inet 192.168.9.2/24 scope global enp1s0f1
|
||||
valid_lft forever preferred_lft forever
|
||||
inet6 fe80::2a41:c6ff:fexx:xxab/64 scope link proto kernel_ll
|
||||
valid_lft forever preferred_lft forever
|
||||
```
|
||||
Test is using ping. On both switches one of the 2.5Gbit links and all 10GBit links should show activity:
|
||||
```
|
||||
$ sudo ip netns exec netns_eth1 ping 192.168.9.1
|
||||
PING 192.168.9.1 (192.168.9.1) 56(84) bytes of data.
|
||||
64 bytes from 192.168.9.1: icmp_seq=1 ttl=64 time=0.082 ms
|
||||
64 bytes from 192.168.9.1: icmp_seq=2 ttl=64 time=0.130 ms
|
||||
^C
|
||||
--- 192.168.9.1 ping statistics ---
|
||||
2 packets transmitted, 2 received, 0% packet loss, time 1030ms
|
||||
rtt min/avg/max/mdev = 0.082/0.106/0.130/0.024 ms
|
||||
```
|
||||
You can also verify that the redundancy works by unplugging the active link, the ping should continue
|
||||
undisturbed with the other link now tranporting the pakets.
|
||||
|
||||
In 2 shells, start 2 instances of iperf, listening on 2 different ports. You will need to make sure that
|
||||
the hash algorithm assigns different switch ports for the different port numbers. You can check this by
|
||||
running the iperf3 client against each server instance and verify that different links show activity:
|
||||
```
|
||||
sudo ip netns exec netns_eth0 iperf3 -s
|
||||
|
||||
sudo ip netns exec netns_eth0 iperf3 -s -p 5333
|
||||
```
|
||||
|
||||
Now you can run the clients in parallel:
|
||||
```
|
||||
$ sudo ip netns exec netns_eth1 iperf3 -c 192.168.9.1 & sudo ip netns exec netns_eth1 iperf3 -p 5333 -c 192.168.9.1
|
||||
[1] 295484
|
||||
Connecting to host 192.168.9.1, port 5201
|
||||
[ 5] local 192.168.9.2 port 60996 connected to 192.168.9.1 port 5201
|
||||
Connecting to host 192.168.9.1, port 5333
|
||||
[ 5] local 192.168.9.2 port 39660 connected to 192.168.9.1 port 5333
|
||||
[ ID] Interval Transfer Bitrate Retr Cwnd
|
||||
[ 5] 0.00-1.00 sec 283 MBytes 2.37 Gbits/sec 485 272 KBytes
|
||||
[ ID] Interval Transfer Bitrate Retr Cwnd
|
||||
[ 5] 0.00-1.00 sec 283 MBytes 2.37 Gbits/sec 479 379 KBytes
|
||||
[ 5] 1.00-2.00 sec 280 MBytes 2.35 Gbits/sec 444 260 KBytes
|
||||
[ 5] 1.00-2.00 sec 280 MBytes 2.35 Gbits/sec 578 267 KBytes
|
||||
[ 5] 2.00-3.00 sec 281 MBytes 2.36 Gbits/sec 385 263 KBytes
|
||||
[ 5] 2.00-3.00 sec 280 MBytes 2.35 Gbits/sec 373 375 KBytes
|
||||
[ 5] 3.00-4.00 sec 280 MBytes 2.35 Gbits/sec 430 385 KBytes
|
||||
[ 5] 3.00-4.00 sec 280 MBytes 2.35 Gbits/sec 452 273 KBytes
|
||||
[ 5] 4.00-5.00 sec 281 MBytes 2.36 Gbits/sec 319 256 KBytes
|
||||
[ 5] 4.00-5.00 sec 281 MBytes 2.36 Gbits/sec 425 269 KBytes
|
||||
[ 5] 5.00-6.00 sec 280 MBytes 2.35 Gbits/sec 364 264 KBytes
|
||||
[ 5] 5.00-6.00 sec 281 MBytes 2.36 Gbits/sec 561 264 KBytes
|
||||
[ 5] 6.00-7.00 sec 281 MBytes 2.35 Gbits/sec 446 255 KBytes
|
||||
[ 5] 6.00-7.00 sec 280 MBytes 2.35 Gbits/sec 582 263 KBytes
|
||||
[ 5] 7.00-8.00 sec 281 MBytes 2.35 Gbits/sec 494 263 KBytes
|
||||
[ 5] 7.00-8.00 sec 280 MBytes 2.35 Gbits/sec 539 181 KBytes
|
||||
[ 5] 8.00-9.00 sec 281 MBytes 2.36 Gbits/sec 617 389 KBytes
|
||||
[ 5] 8.00-9.00 sec 280 MBytes 2.35 Gbits/sec 490 232 KBytes
|
||||
[ 5] 9.00-10.00 sec 281 MBytes 2.35 Gbits/sec 363 215 KBytes
|
||||
- - - - - - - - - - - - - - - - - - - - - - - - -
|
||||
[ ID] Interval Transfer Bitrate Retr
|
||||
[ 5] 0.00-10.00 sec 2.74 GBytes 2.36 Gbits/sec 4347 sender
|
||||
[ 5] 0.00-10.00 sec 2.74 GBytes 2.35 Gbits/sec receiver
|
||||
|
||||
iperf Done.
|
||||
[ 5] 9.00-10.00 sec 282 MBytes 2.36 Gbits/sec 536 380 KBytes
|
||||
- - - - - - - - - - - - - - - - - - - - - - - - -
|
||||
[ ID] Interval Transfer Bitrate Retr
|
||||
[ 5] 0.00-10.00 sec 2.74 GBytes 2.36 Gbits/sec 5015 sender
|
||||
[ 5] 0.00-10.00 sec 2.74 GBytes 2.35 Gbits/sec receiver
|
||||
|
||||
iperf Done.
|
||||
[1]+ Done sudo ip netns exec netns_eth1 iperf3 -c 192.168.9.1
|
||||
```
|
||||
As you can see, the total throughput was 4.71 GBit/sec which is close to the
|
||||
maximum possible with a single 5GBit link.
|
||||
@@ -0,0 +1,46 @@
|
||||
# Modifications
|
||||
|
||||
## SPI-Flash Memory
|
||||
|
||||
SPI-Flash memory can be replaced with an other type. Because the chip is defect, like #69 & #70 or you have an unmanaged-switch with a small flash size, and want to convert it to a managed-variant to run `RTLPlayground` software on it. Currently `RTLPlayground`-firmware expects `8 MBit / 1 MiB`.
|
||||
|
||||
### Size
|
||||
|
||||
The SOC (RTL837x network chip) used 24-bit address to access the device. This means that in theory, a memory size up to `2^24 x 8-bit = 16777216 x 8-bit = 128 MBit or 16 MiB` could be used, this is *untested*!
|
||||
|
||||
### Speed
|
||||
|
||||
The SOC (RTL837x network chip) is connected via an SPI-BUS to the flash memory. SPI-BUS frequency is `62.5 MHz`.
|
||||
Look in the datasheet for `AC Electrical Characteristics` and lookup symbol `Fr`. Maximum value should be equal or higher than `62.5 MHz`.
|
||||
A device that supports the highest possible clock speed is not a better device, nor is the SOC going to run faster. So pick one which have common frequency between `80 MHz` to `133 MHz`.
|
||||
Memory speed is dictated by the SPI-BUS clock frequency which is `62.5 MHz`.
|
||||
|
||||
### SPI Operation
|
||||
|
||||
By default, SPI-BUS uses `CLK`, `CS`, `DI` and `DO`. To increase the data throughput without increasing the bus frequency, a single command can run in `DUAL SPI operation`. This means that for a specific command `DI` and `DO` are both used to transfer the data to/from the device. So it makes the data transfer up-to twice as fast. Although the SOC datasheet doesn’t mention it, our software is making use of this mode.
|
||||
|
||||
### Package
|
||||
|
||||
Most use package are `SO8`-type may also called `SOIC8`-type. Which can also have different width. Like `150-mil`, `208-mil` or `300-mil`. Best to measure what you need and confirm the measurement with the device-datasheet.
|
||||
|
||||
### Specification
|
||||
|
||||
1. Size: At least `8 MBit / 1 MiB` (theoretic max. `128 MBit / 16 MiB`, but is not *tested*!)
|
||||
2. Speed: `62.5 MHz` or better.
|
||||
3. Support for `DUAL SPI operation`.
|
||||
Device need support for command `BBh`, `Dual I/O Fast Read` or `Fast Read Dual I/O`.
|
||||
|
||||
### Known Working
|
||||
|
||||
This list is incomplete.
|
||||
|
||||
| Brand | Partnumber |
|
||||
| ---------- |----------- |
|
||||
| GigaDevice | GD25Q32E |
|
||||
| Winbond | W25Q16JV |
|
||||
| Winbond | W25Q32FV |
|
||||
| Winbond | W25Q32JV |
|
||||
| Winbond | W25Q16JL |
|
||||
| Fundan | FM25Q16A |
|
||||
|
||||
*NOTE*: Part numbers are incomplete. Part numbers may contain additional information such as package, temperature specifications, and even the number of devices on a reel. So always check the datasheet so that you have the right orderable partnumber.
|
||||
@@ -0,0 +1,26 @@
|
||||
# Supported Hardware
|
||||
The following devices have been tested and are fully working:
|
||||
- Horaco ZX_SG4T2
|
||||
- keepLINK kp-9000-6hx-x2 (RTL8372: 4x 2.5GBit + 2x 10GBit SFP+)
|
||||
- keepLINK KP-9000-6XHML-X2, same as above, but Managed
|
||||
- keepLINK kp-9000-6hx-x (RTL8372 + RTL8221B 2.5GBit PHY: 5 x 2.5GBit + 1x 10GBit SFP+)
|
||||
- keepLINK kp-9000-9xh-x-eu (1 x RTL8373 + RTL8224: 8x 2.5GBit + 1x 10GBit SFP+)
|
||||
- Lianguo LG-SWTGW218AS (RTL8373 + RTL8224 PHY: 8x 2.5GBit + 1x 10GBit SFP+)
|
||||
- No-Name ZX-SWTGW215AS, managed version of kp-9000-6hx-x, ordered on
|
||||
AliExpress as keepLINK 5+1 port managed
|
||||
- TrendNet TEG-S562 (RTL8372: 4x 2.5GBit + 2x 10GBit SFP+)
|
||||
|
||||
Other device based on RTL8272/3 that may work are described here: [Up-N-Atoms 2.5 GBit RTL Switch hacking guide]
|
||||
(https://github.com/up-n-atom/SWTG118AS)
|
||||
|
||||
Many of the RTL8272/3 devices come in versions with PoE support. The RTLPlayground usually also
|
||||
works on these, however, no support for configuring PoE is provided, simply because these
|
||||
devices usually just provide PoE on all ports without further configuration possibilitites.
|
||||
|
||||
The following forum also discusses this type of switches: [ServeTheHome](https://forums.servethehome.com/index.php?threads/horaco-2-5gbe-managed-switch-8-x-2-5gbe-1-10gb-sfp.41571/)
|
||||
|
||||
There are also 16-port unmanaged devices with RTL8272 SoCs, however these devices do not have
|
||||
serial consoles and use 4 independent RTL8272 SoCs. No central control is provided by RTLPlayground,
|
||||
even if it has been successfully demonstrated to install RTLPlayground to individual SoCs.
|
||||
- [GigaPlus GP-S25-1602](https://www.servethehome.com/gigaplus-gp-s25-1602-review-a-cheap-16-port-2-5gbe-and-2-port-10g-switch/)
|
||||
- [Vimin VM S251602P 16 Port 2.5G PoE Switch With 2x 10G SFP+](https://www.servethehome.com/vimin-vm-s251602p-16-port-2-5g-poe-switch-review-cyperf/vimin-vm-s251602p-16-port-2-5g-poe-switch-with-2x-10g-sfp-battery-2/)
|
||||
@@ -1,25 +0,0 @@
|
||||
# Trunking
|
||||
|
||||
The RTL827x allows to combine multiple ports to a single logical port
|
||||
(trunking). Up to 2 trunk groups can be defined.
|
||||
|
||||
## Trunking control
|
||||
Two registers RTL837x_TRUNK_CTRL_A (0x4f38) and RTL837x_TRUNK_CTRL_B (0x4f3c)
|
||||
define the trunk groups. Each holds a bitmap of ports making up the trunk
|
||||
group.
|
||||
|
||||
## Trunking API
|
||||
The code currently provides the following functions:
|
||||
```
|
||||
void trunk_set(uint8_t group, uint16_t mask) __banked
|
||||
```
|
||||
|
||||
# VLAN configuration on the Serial Console
|
||||
For testing the following commands are provided on the serial console:
|
||||
```
|
||||
trunk <TRUNK-ID> [p1] [p2]...
|
||||
create or set a trunk group. Trunk-ID is 1 or 2. ports a physical ports
|
||||
|
||||
trunk <VLAN-ID> d
|
||||
deletes the trunk group
|
||||
```
|
||||
@@ -0,0 +1,20 @@
|
||||
<!DOCTYPE html>
|
||||
<html>
|
||||
<script src="/main.js"></script>
|
||||
<link rel="stylesheet" href="style.css">
|
||||
<title>Ingress and Egress Bandwidth</title>
|
||||
</head>
|
||||
<body>
|
||||
<nav id="sidebar"></nav>
|
||||
<div style="margin-left:16%;padding:1px 16px;height:1000px;">
|
||||
<div id="ports"></div>
|
||||
<h1>Ingress and Egress Bandwidth</h1>
|
||||
<table id="bwtable">
|
||||
<tr> <th> </th> <th colspan="3"> Ingress </th> <th colspan="2">Egress</th> <th></th></tr>
|
||||
<tr> <th>Port</th> <th>Limit</th> <th>Bandwidth [kBit/s]</th> <th>Flow Control</th> <th>Limit</th> <th>Bandwidth [kBit/s]</th> <th>Apply</th></tr>
|
||||
</table>
|
||||
<script src="/bandwidth.js"></script>
|
||||
</div>
|
||||
<script src="/navigation.js"></script>
|
||||
</body>
|
||||
</html>
|
||||
@@ -0,0 +1,136 @@
|
||||
const iLayout = '" type="number" maxlength="10" size="10" onfocus="inputFocus(';
|
||||
function createBW() {
|
||||
var tbl = document.getElementById('bwtable');
|
||||
const limit = '<input type="checkbox" id="limit_port" onchange="exec();">'
|
||||
if (tbl.rows.length <= 2 && numPorts) {
|
||||
clearInterval(createBWInterval);
|
||||
console.log("CREATING TABLE ", tbl.rows.length);
|
||||
for (let i = 2; i < 2 + numPorts; i++) {
|
||||
const tr = tbl.insertRow();
|
||||
let td = tr.insertCell(); td.appendChild(document.createTextNode(`Port ${i-1}`));
|
||||
td = tr.insertCell();
|
||||
td.innerHTML = limit.replaceAll("limit_port", "ilimit_port_" + i).replace("exec()", "iClicked(" + i + ")");
|
||||
td = tr.insertCell();
|
||||
td.innerHTML = 'UNLIMITED';
|
||||
td = tr.insertCell();
|
||||
td.innerHTML = limit.replaceAll("limit_port", "fc_port_" + i).replace("exec()", "document.getElementById('bwapply_" + i + "').disabled=false;");
|
||||
td = tr.insertCell();
|
||||
td.innerHTML = limit.replaceAll("limit_port", "elimit_port_" + i).replace("exec()", "eClicked(" + i + ")");
|
||||
td = tr.insertCell();
|
||||
td.innerHTML = 'UNLIMITED';
|
||||
var button = '<button type="button" id="bwapply_' + i + '" style="margin: 0 0 0 24px" onclick="applyBandwidth(' + i + ');">Apply</button>';
|
||||
td = tr.insertCell();
|
||||
td.innerHTML = button;
|
||||
document.getElementById("bwapply_" + i).disabled = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
function iClicked(i)
|
||||
{
|
||||
document.getElementById("bwapply_" + i).disabled=false;
|
||||
var tbl = document.getElementById('bwtable');
|
||||
var tr = tbl.rows[i];
|
||||
if (!document.getElementById("ilimit_port_" + i).checked) {
|
||||
tr.cells[2].innerHTML = "UNLIMITED";
|
||||
document.getElementById("fc_port_" + i).disabled = true;
|
||||
document.getElementById("fc_port_" + i).checked = true;
|
||||
} else {
|
||||
tr.cells[2].innerHTML = '<input id="ibw_' + i + iLayout + i + ')" value="0"/>';
|
||||
document.getElementById("fc_port_" + i).disabled = false;
|
||||
document.getElementById("fc_port_" + i).checked = true;
|
||||
}
|
||||
}
|
||||
|
||||
function eClicked(i)
|
||||
{
|
||||
document.getElementById("bwapply_" + i).disabled=false;
|
||||
var tbl = document.getElementById('bwtable');
|
||||
var tr = tbl.rows[i];
|
||||
if (!document.getElementById("elimit_port_" + i).checked) {
|
||||
tr.cells[5].innerHTML = "UNLIMITED";
|
||||
} else {
|
||||
tr.cells[5].innerHTML = '<input id="ebw_' + i + iLayout + i + ')" value="0"/>';
|
||||
}
|
||||
}
|
||||
|
||||
function inputFocus(i)
|
||||
{
|
||||
document.getElementById("bwapply_" + i).disabled=false;
|
||||
}
|
||||
|
||||
async function doCMD(cmd)
|
||||
{
|
||||
console.log("Sending >" + cmd + "<");
|
||||
try {
|
||||
const response = await fetch('/cmd', {
|
||||
method: 'POST',
|
||||
body: cmd
|
||||
});
|
||||
console.log('Completed!', response);
|
||||
} catch(err) {
|
||||
console.error(`Error: ${err}`);
|
||||
}
|
||||
}
|
||||
|
||||
async function applyBandwidth(i) {
|
||||
var tbl = document.getElementById('bwtable');
|
||||
var tr = tbl.rows[i];
|
||||
var cmd = "bw in " + (i-1) + " off";
|
||||
if (document.getElementById("ilimit_port_" + i).checked)
|
||||
cmd = 'bw in ' + (i-1) + ' ' + parseInt(document.getElementById("ibw_" + i).value).toString(16).padStart(4, "0");;
|
||||
doCMD(cmd);
|
||||
if (document.getElementById("ilimit_port_" + i).checked) {
|
||||
if (!document.getElementById("fc_port_" + i).checked)
|
||||
cmd = "bw in " + (i-1) + " drop";
|
||||
doCMD(cmd);
|
||||
}
|
||||
var cmd = "bw out " + (i-1) + " off";
|
||||
if (document.getElementById("elimit_port_" + i).checked)
|
||||
cmd = 'bw out ' + (i-1) + ' ' + parseInt(document.getElementById("ebw_" + i).value).toString(16).padStart(4, "0");;
|
||||
doCMD(cmd);
|
||||
}
|
||||
|
||||
function getBW() {
|
||||
var xhttp = new XMLHttpRequest();
|
||||
xhttp.onreadystatechange = function() {
|
||||
if (this.readyState == 4 && this.status == 200) {
|
||||
const s = JSON.parse(xhttp.responseText);
|
||||
console.log("BW: ", JSON.stringify(s));
|
||||
var tbl = document.getElementById('bwtable');
|
||||
if (tbl.rows.length > 2 && numPorts) {
|
||||
for (let i = 2; i < 2 + numPorts; i++) {
|
||||
p = s[i-2];
|
||||
let n = p.portNum;
|
||||
let tr = tbl.rows[n+1];
|
||||
if (!document.getElementById("bwapply_" + (n+1)).disabled)
|
||||
continue;
|
||||
console.log("Table Update row: " + i + " portNum is " + n + ", pState is " + pState[i-2] + ", row number is " + (n+1));
|
||||
let iBW = parseInt(p.iBW,16) * 16; let eBW = parseInt(p.eBW,16) * 16;
|
||||
document.getElementById("ilimit_port_" + (n+1)).checked = p.iLimited;
|
||||
document.getElementById("elimit_port_" + (n+1)).checked = p.eLimited;
|
||||
if (!p.iLimited) {
|
||||
tr.cells[2].innerHTML = "UNLIMITED";
|
||||
} else {
|
||||
tr.cells[2].innerHTML = '<input id="ibw_' + (n+1) + iLayout + (n+1) + ')" value="' + iBW +'"/>';
|
||||
}
|
||||
if (!p.eLimited) {
|
||||
tr.cells[5].innerHTML = "UNLIMITED";
|
||||
} else {
|
||||
tr.cells[5].innerHTML = '<input id="ebw_' + (n+1) + iLayout + (n+1) + ')" value="' + eBW +'"/>';
|
||||
}
|
||||
document.getElementById("fc_port_" + (n+1)).checked = p.iFC==1?true:false;
|
||||
document.getElementById("fc_port_" + (n+1)).disabled = p.iLimited==1?false:true;
|
||||
}
|
||||
}
|
||||
}
|
||||
};
|
||||
xhttp.open("GET", "/bandwidth.json", true);
|
||||
xhttp.timeout = 1500; xhttp.send();
|
||||
}
|
||||
|
||||
window.addEventListener("load", function() {
|
||||
getBW();
|
||||
const iCount = setInterval(getBW, 2000);
|
||||
});
|
||||
const createBWInterval = setInterval(createBW, 1010);
|
||||
@@ -0,0 +1,56 @@
|
||||
var configInterval = Number();
|
||||
var configuration = [];
|
||||
const conf_cmds = [
|
||||
/ip\s+(\d{1,3}\.){3}\d{1,3}/, /gw\s+(\d{1,3}\.){3}\d{1,3}/, /netmask\s+(\d{1,3}\.){3}\d{1,3}/,
|
||||
/eee(\s+\d)?\s+(on|off)/, /mirror(\s+(\d|10))(\s+(\d|10)(t|r)?)+/, /vlan\s+(\d{1,4})(\s+(\d|10)(t|u)?)+/
|
||||
];
|
||||
const conf_overwrite = [
|
||||
/ip/, /gw/, /netmask/, /eee\s+\w+/, /eee(\s+\w)/, /mirror/, /vlan\s+(\d{1,4})/
|
||||
];
|
||||
|
||||
function parseConf(s){
|
||||
var a = s.split(/\r\n|\n/);
|
||||
for (var l = 0; l < a.length; l++) {
|
||||
if (!a[l].length || a[l] == "\n" || a[l] == "\r\n")
|
||||
continue;
|
||||
console.log(l + ' --> ' + a[l]);
|
||||
var ignore = true;
|
||||
for (const x of conf_cmds)
|
||||
if (x.test(a[l])) ignore = false;
|
||||
if (ignore) continue;
|
||||
for (const x of conf_overwrite) {
|
||||
if (x.test(a[l])) {
|
||||
console.log("Match ", x, " to ", a[l]);
|
||||
m = a[l].match(x);
|
||||
console.log("Starts with ", m[0]);
|
||||
configuration = configuration.filter(item => !(item.startsWith(m[0])));
|
||||
}
|
||||
}
|
||||
configuration.push(a[l]);
|
||||
}
|
||||
console.log("Configuration now:");
|
||||
for (const x of configuration) { console.log(x); }
|
||||
}
|
||||
|
||||
async function fetchConfig() {
|
||||
try {
|
||||
const response = await fetch('/config');
|
||||
console.log("CONFIG: ", response);
|
||||
const t = await response.text();
|
||||
return t;
|
||||
} catch(err) {
|
||||
console.error("Error: ", err);
|
||||
}
|
||||
}
|
||||
|
||||
async function fetchCmdLog() {
|
||||
try {
|
||||
const response = await fetch('/cmd_log');
|
||||
console.log("CMD-Log: ", response);
|
||||
const t = await response.text();
|
||||
return t;
|
||||
} catch(err) {
|
||||
console.error("Error: ", err);
|
||||
return "";
|
||||
}
|
||||
}
|
||||
@@ -1,6 +1,5 @@
|
||||
<!DOCTYPE html>
|
||||
<html>
|
||||
<script src="/ports.js"></script>
|
||||
<script src="/main.js"></script>
|
||||
<link rel="stylesheet" href="style.css">
|
||||
<title>EEE Configuration</title>
|
||||
|
||||
@@ -1,8 +1,9 @@
|
||||
function createEEE() {
|
||||
var tbl = document.getElementById('eeetable');
|
||||
if (tbl.rows.length <= 2) {
|
||||
if (tbl.rows.length <= 2 && numPorts) {
|
||||
clearInterval(createEEEInterval);
|
||||
console.log("CREATING TABLE ", tbl.rows.length);
|
||||
for (let i = 2; i < 8; i++) {
|
||||
for (let i = 2; i < 2 + numPorts; i++) {
|
||||
console.log("Table row: " + i + "pState: " + pState[i-2]);
|
||||
const tr = tbl.insertRow();
|
||||
let td = tr.insertCell(); td.appendChild(document.createTextNode(`Port ${i-1}`));
|
||||
@@ -20,8 +21,8 @@ function getEEE() {
|
||||
const s = JSON.parse(xhttp.responseText);
|
||||
console.log("EEE: ", JSON.stringify(s));
|
||||
var tbl = document.getElementById('eeetable');
|
||||
if (tbl.rows.length > 2) {
|
||||
for (let i = 2; i < 8; i++) {
|
||||
if (tbl.rows.length > 2 && numPorts) {
|
||||
for (let i = 2; i < 2 + numPorts; i++) {
|
||||
p = s[i-2];
|
||||
let n = p.portNum;
|
||||
console.log("Table Update row: " + i + " portNum is " + n + ", pState is " + pState[i-2]);
|
||||
@@ -46,4 +47,4 @@ window.addEventListener("load", function() {
|
||||
getEEE();
|
||||
const iCount = setInterval(getEEE, 2000);
|
||||
});
|
||||
const stat = setInterval(createEEE, 1000);
|
||||
const createEEEInterval = setInterval(createEEE, 1000);
|
||||
|
||||
@@ -1,6 +1,5 @@
|
||||
<!DOCTYPE html>
|
||||
<html>
|
||||
<script src="/ports.js"></script>
|
||||
<script src="/main.js"></script>
|
||||
<script src="/main_info.js"></script>
|
||||
<link rel="stylesheet" href="style.css">
|
||||
|
||||
@@ -0,0 +1,19 @@
|
||||
<!DOCTYPE html>
|
||||
<html>
|
||||
<script src="/main.js"></script>
|
||||
<link rel="stylesheet" href="style.css">
|
||||
<title>FreeSwitchOS L2 Configuration</title>
|
||||
</head>
|
||||
<body>
|
||||
<nav id="sidebar"></nav>
|
||||
<div style="margin-left:16%;padding:1px 16px;height:1000px;">
|
||||
<div id="ports"></div>
|
||||
<h1>L2 Configuration</h1>
|
||||
<table id="l2table">
|
||||
<tr> <th>Port</th> <th>MAC</th> <th>VLAN</th> <th>Type</th> <th>Remove Entry</th></tr>
|
||||
<script src="/l2.js"></script>
|
||||
</table>
|
||||
</div>
|
||||
</body>
|
||||
<script src="/navigation.js"></script>
|
||||
</html>
|
||||
@@ -0,0 +1,139 @@
|
||||
var l2GetInterval;
|
||||
var l2Entries = [];
|
||||
var l2CurrentEntry = 0;
|
||||
|
||||
function fillStats() {
|
||||
var tbl = document.getElementById('statstable');
|
||||
if (!numPorts)
|
||||
return;
|
||||
if (tbl.rows.length > 1) {
|
||||
for (let i = 0; i < numPorts; i++) {
|
||||
console.log("Table Update row: " + i + " state " + pState[i] + " is " + linkS[pState[i] +1]);
|
||||
tbl.rows[i+1].cells[1].innerHTML = `${linkS[pState[i]+1]}`;
|
||||
tbl.rows[i+1].cells[2].innerHTML = `${txG[i]} pkts`;
|
||||
tbl.rows[i+1].cells[3].innerHTML = `${txB[i]} pkts`;
|
||||
tbl.rows[i+1].cells[4].innerHTML = `${rxG[i]} pkts`;
|
||||
tbl.rows[i+1].cells[5].innerHTML = `${rxB[i]} pkts`;
|
||||
}
|
||||
} else {
|
||||
for (let i = 0; i < numPorts; i++) {
|
||||
console.log("Table row: " + i);
|
||||
const tr = tbl.insertRow();
|
||||
let td = tr.insertCell(); td.appendChild(document.createTextNode(`Port ${i+1}`));
|
||||
td = tr.insertCell(); td.appendChild(document.createTextNode(`${linkS[pState[i]+1]}`));
|
||||
td = tr.insertCell(); td.appendChild(document.createTextNode(`${txG[i]} pkts`));
|
||||
td = tr.insertCell();td.appendChild(document.createTextNode(`${txB[i]} pkts`));
|
||||
td = tr.insertCell();td.appendChild(document.createTextNode(`${rxG[i]} pkts`));
|
||||
td = tr.insertCell();td.appendChild(document.createTextNode(`${rxB[i]} pkts`));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
function l2CMP(a, b)
|
||||
{
|
||||
if (a.port < b.port)
|
||||
return -1;
|
||||
if (a.port > b.port)
|
||||
return 1;
|
||||
if (a.mac < b.mac)
|
||||
return -1;
|
||||
if (a.mac > b.mac)
|
||||
return 1;
|
||||
if (a.vlan < b.vlan)
|
||||
return -1;
|
||||
if (a.vlan > b.vlan)
|
||||
return 1;
|
||||
return 0;
|
||||
}
|
||||
|
||||
function uniq(a) {
|
||||
return a.filter(function(item, pos, ary) {
|
||||
return !pos || item.idx != ary[pos - 1].idx;
|
||||
});
|
||||
}
|
||||
|
||||
function delL2(idx) {
|
||||
var xhttp = new XMLHttpRequest();
|
||||
xhttp.onreadystatechange = function() {
|
||||
if (this.readyState == 4 && this.status == 200) {
|
||||
var s = JSON.parse(xhttp.responseText);
|
||||
console.log("Entry deletion result: ", s.result);
|
||||
}
|
||||
};
|
||||
xhttp.open("GET", "/l2_del.json?idx=" + idx, true);
|
||||
xhttp.timeout = 1500; xhttp.send();
|
||||
}
|
||||
|
||||
function fillL2(s)
|
||||
{
|
||||
var tbl = document.getElementById('l2table');
|
||||
if (!s.length)
|
||||
return;
|
||||
s.sort(l2CMP);
|
||||
s = uniq(s);
|
||||
var s = s.map(function(e) { e.port = e.port != 9 ? e.port : "CPU"; return e; });
|
||||
console.log("L2: ", JSON.stringify(s));
|
||||
for (let i = 0; i < s.length; i++) {
|
||||
var e = s[i];
|
||||
console.log(i, e);
|
||||
if (tbl.rows[i+1]) {
|
||||
tbl.rows[i+1].cells[0].innerHTML = `${e.port}`;
|
||||
tbl.rows[i+1].cells[1].innerHTML = `${e.mac}`;
|
||||
tbl.rows[i+1].cells[2].innerHTML = `${e.vlan}`;
|
||||
tbl.rows[i+1].cells[4].innerHTML = '<button type="button" onclick="delL2(' + e.idx + ');">Delete</button>';
|
||||
} else {
|
||||
const tr = tbl.insertRow();
|
||||
let td = tr.insertCell(); td.innerHTML = `${e.port}`;
|
||||
td = tr.insertCell(); td.innerHTML = `${e.mac}`;
|
||||
td = tr.insertCell(); td.innerHTML = `${e.vlan}`;
|
||||
td = tr.insertCell(); td.innerHTML = `${e.type}`;
|
||||
td = tr.insertCell(); td.innerHTML = '<button type="button" onclick="delL2(' + e.idx + ');">Delete</button>';
|
||||
}
|
||||
}
|
||||
for (let i = tbl.rows.length - 1; i > s.length; i--)
|
||||
tbl.deleteRow(i);
|
||||
l2Entries = [];
|
||||
}
|
||||
|
||||
function getL2() {
|
||||
var xhttp = new XMLHttpRequest();
|
||||
xhttp.onreadystatechange = function() {
|
||||
if (this.readyState == 4 && this.status == 200) {
|
||||
var s = JSON.parse(xhttp.responseText);
|
||||
var s = s.map(function(e) {
|
||||
e.vlan = parseInt(e.vlan, 16);
|
||||
e.idx = parseInt(e.idx, 16);
|
||||
e.type = e.type == "s" ? "static" : "learned";
|
||||
e.port = e.port == 9 ? 9 : logToPhysPort[e.port];
|
||||
return e;
|
||||
});
|
||||
l2Entries.push(...s);
|
||||
if (l2Entries >= 4096) {
|
||||
l2Entries = [];
|
||||
l2CurrentEntry = 0;
|
||||
clearInterval(l2GetInterval);
|
||||
return;
|
||||
}
|
||||
var w = 0;
|
||||
for (var i = l2Entries.length-1; i > 0; i--) {
|
||||
if (l2Entries[0].idx == l2Entries[i].idx) {
|
||||
w = 1;
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (w) {
|
||||
l2CurrentEntry = 0;
|
||||
fillL2(l2Entries);
|
||||
} else {
|
||||
l2CurrentEntry = s[s.length-1].idx + 1;
|
||||
}
|
||||
}
|
||||
};
|
||||
xhttp.open("GET", "/l2.json?idx=" + l2CurrentEntry, true);
|
||||
xhttp.timeout = 1500; xhttp.send();
|
||||
}
|
||||
|
||||
window.addEventListener("load", function() {
|
||||
l2GetInterval = setInterval(getL2, 1000);
|
||||
});
|
||||
|
||||
@@ -0,0 +1,27 @@
|
||||
<!DOCTYPE html>
|
||||
<html>
|
||||
<script src="/main.js"></script>
|
||||
<link rel="stylesheet" href="style.css">
|
||||
<title>Link Aggregation Configuration</title>
|
||||
</head>
|
||||
<body>
|
||||
<nav id="sidebar"></nav>
|
||||
<div style="margin-left:16%;padding:1px 16px;height:1000px;">
|
||||
<div id="ports"></div>
|
||||
<h1>Link Aggregation Groups Configuration</h1>
|
||||
<h2>LAG 1 <input style="width:15%;margin-left: 3em;" class="action" id="l_sub0" onclick="lagSub(0);" type="button" value="Update / Create"></h2>
|
||||
<div id="mLAG0"></div>
|
||||
<br />
|
||||
<h2>LAG 2 <input style="width:15%;margin-left: 3em;" class="action" id="l_sub1" onclick="lagSub(1);" type="button" value="Update / Create"></h2>
|
||||
<div id="mLAG1"></div>
|
||||
<br />
|
||||
<h2>LAG 3 <input style="width:15%;margin-left: 3em;" class="action" id="l_sub2" onclick="lagSub(2);" type="button" value="Update / Create"></h2>
|
||||
<div id="mLAG2"></div>
|
||||
<br />
|
||||
<h2>LAG 4 <input style="width:15%;margin-left: 3em;" class="action" id="l_sub3" onclick="lagSub(3);" type="button" value="Update / Create"></h2>
|
||||
<div id="mLAG3"></div>
|
||||
<script src="/lag.js"></script>
|
||||
</div>
|
||||
<script src="/navigation.js"></script>
|
||||
</body>
|
||||
</html>
|
||||
@@ -0,0 +1,78 @@
|
||||
var lagInterval = Number();
|
||||
|
||||
function lagForm() {
|
||||
if (!numPorts)
|
||||
return;
|
||||
clearInterval(lagInterval);
|
||||
for (let j=0; j < 4; j++) {
|
||||
var lag = "mLAG" + j
|
||||
console.log("Adding LAG " + lag)
|
||||
var m = document.getElementById(lag);
|
||||
for (let i = 1; i <= numPorts; i++) {
|
||||
const d = document.createElement("div");
|
||||
d.classList.add("cbgroup");
|
||||
const l = document.createElement("label");
|
||||
l.innerHTML = "" + i;
|
||||
l.classList.add("cbgroup");
|
||||
const inp = document.createElement("input");
|
||||
inp.type = "checkbox"; inp.setAttribute("class","psel");
|
||||
inp.id = "p_" + lag + "_" + i;
|
||||
const o = document.createElement("img");
|
||||
if (pIsSFP[i - 1]) {
|
||||
o.src = "sfp.svg"; o.width ="60"; o.height ="60";
|
||||
} else {
|
||||
o.src = "port.svg"; o.width = "40"; o.height = "40";
|
||||
}
|
||||
l.appendChild(inp); l.appendChild(o);
|
||||
d.appendChild(l)
|
||||
m.appendChild(d);
|
||||
}
|
||||
}
|
||||
fetchLag();
|
||||
}
|
||||
|
||||
function setL(p, c){
|
||||
console.log("LAG setting: ", p, " to ", c);
|
||||
document.getElementById(p).checked=c;
|
||||
}
|
||||
window.addEventListener("load", function() {
|
||||
lagInterval = setInterval(lagForm, 200);
|
||||
});
|
||||
|
||||
function fetchLag() {
|
||||
var xhttp = new XMLHttpRequest();
|
||||
xhttp.onreadystatechange = function() {
|
||||
if (this.readyState == 4 && this.status == 200) {
|
||||
const s = JSON.parse(xhttp.responseText);
|
||||
console.log("LAG: ", JSON.stringify(s));
|
||||
for (let l = 0; l < 4; l++) {
|
||||
let members = parseInt(s[l].members, 2);
|
||||
let hash = parseInt(s[l].hash, 16);
|
||||
for (let i = 1; i <= numPorts; i++) {
|
||||
let p = i - 1;
|
||||
if (numPorts < 9)
|
||||
p = physToLogPort[p];
|
||||
setL("p_mLAG"+l+"_"+i, members & (1<<p));
|
||||
}
|
||||
}
|
||||
}
|
||||
};
|
||||
xhttp.open("GET", `/lag.json`, true);
|
||||
xhttp.send();
|
||||
}
|
||||
async function lagSub(l) {
|
||||
var cmd = "lag " + l;
|
||||
for (let i = 1; i <= numPorts; i++) {
|
||||
if (document.getElementById("p_mLAG"+l+"_"+i).checked)
|
||||
cmd = cmd + ` ${i}`;
|
||||
}
|
||||
try {
|
||||
const response = await fetch('/cmd', {
|
||||
method: 'POST',
|
||||
body: cmd
|
||||
});
|
||||
console.log('Completed!', response);
|
||||
} catch(err) {
|
||||
console.error(`Error: ${err}`);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,30 @@
|
||||
<!DOCTYPE html>
|
||||
<html>
|
||||
<title>RTL Switch Login</title>
|
||||
<link rel="stylesheet" href="style.css">
|
||||
<script>
|
||||
function removeNote() {
|
||||
document.getElementById("incorrect").innerHTML = "";
|
||||
}
|
||||
window.addEventListener("load", function() {
|
||||
if (document.referrer.endsWith("login.html"))
|
||||
document.getElementById("incorrect").innerHTML = "Wrong password!";
|
||||
});
|
||||
</script>
|
||||
</head>
|
||||
|
||||
<body class="login_page">
|
||||
<div class = "center">
|
||||
<h1> RTL Switch Login</h1>
|
||||
<form method="post" action="login">
|
||||
<div class="txt_field">
|
||||
<input name="pwd" type="password" onclick="removeNote()" required />
|
||||
<span></span>
|
||||
<label>Password</label>
|
||||
</div>
|
||||
<input type="submit" value="Login"/>
|
||||
<h3 id="incorrect" style="margin-top: 5em;"></h3>
|
||||
</form>
|
||||
</body>
|
||||
</html>
|
||||
|
||||
@@ -2,14 +2,47 @@ var txG = new BigInt64Array(10);
|
||||
var txB = new BigInt64Array(10);
|
||||
var rxG = new BigInt64Array(10);
|
||||
var rxB = new BigInt64Array(10);
|
||||
const linkS = ["Disabled", "No Link", "100M", "1000M", "NO", "NO", "2.5G"];
|
||||
const linkS = ["Disabled", "Down", "10M", "100M", "1000M", "500M", "10G", "2.5G", "5G"];
|
||||
var pState = new Int8Array(10);
|
||||
var pIsSFP = new Int8Array(10);
|
||||
var pAdvertised = new Int8Array(10);
|
||||
var numPorts = 0;
|
||||
var logToPhysPort = new Int8Array(10);
|
||||
var physToLogPort = new Int8Array(10);
|
||||
function drawPorts() {
|
||||
var f = document.getElementById('ports');
|
||||
console.log("DRAWING PORTS: ", numPorts);
|
||||
for (let i = 0; i < numPorts; i++) {
|
||||
console.log("DRAWING isSFP: ", pIsSFP[i]);
|
||||
const d = document.createElement("div");
|
||||
d.classList.add('tooltip');
|
||||
const s = document.createElement("span");
|
||||
s.classList.add("tooltiptext");
|
||||
s.innerHTML = "Tooltip text";
|
||||
s.id="tt_" + (i+1);
|
||||
const l = document.createElement("object");
|
||||
d.appendChild(l);
|
||||
d.appendChild(s);
|
||||
l.type = "image/svg+xml";
|
||||
if (!pIsSFP[i]) {
|
||||
l.data = "port.svg";
|
||||
l.width ="40";
|
||||
l.height ="40";
|
||||
} else {
|
||||
l.data = "sfp.svg";
|
||||
l.width = "60";
|
||||
l.height = "60";
|
||||
}
|
||||
l.id="port" + (i+1);
|
||||
f.appendChild(d);
|
||||
}
|
||||
}
|
||||
|
||||
function update() {
|
||||
var xhttp = new XMLHttpRequest();
|
||||
xhttp.onreadystatechange = function() {
|
||||
if (this.readyState == 4 && this.status == 401)
|
||||
document.location = "/login.html"
|
||||
if (this.readyState == 4 && this.status == 200) {
|
||||
const s = JSON.parse(xhttp.responseText);
|
||||
if (!numPorts) {
|
||||
@@ -22,10 +55,14 @@ function update() {
|
||||
for (let i = 0; i < s.length; i++) {
|
||||
p = s[i];
|
||||
let n = p.portNum;
|
||||
logToPhysPort[p.logPort] = n;
|
||||
physToLogPort[n-1] = p.logPort;
|
||||
let pid = "port" + n;
|
||||
let ttid = "tt_" + n;
|
||||
n--;
|
||||
txG[n] = BigInt(p.txG); txB[n] = BigInt(p.txB); rxG[n] = BigInt(p.rxG); rxB[n] = BigInt(p.rxB);
|
||||
var psvg = document.getElementById(pid);
|
||||
var tt = document.getElementById(ttid);
|
||||
if (psvg == null || !psvg.contentDocument)
|
||||
continue;
|
||||
var bgs = psvg.contentDocument.getElementsByClassName("bg");
|
||||
@@ -35,17 +72,37 @@ function update() {
|
||||
bgs[0].style.fill = "red";
|
||||
leds[0].style.fill = "black"; leds[1].style.fill = "black";
|
||||
psvg.style.opacity = 0.4;
|
||||
tt.innerHTML = "Not enabled.";
|
||||
} else {
|
||||
psvg.style.opacity = 1.0;
|
||||
pState[n] = p.link;
|
||||
if (p.link == 5) {
|
||||
if (p.link == 4 || p.link == 5 || p.link == 6) {
|
||||
leds[0].style.fill = "green"; leds[1].style.fill = "orange";
|
||||
} else if (p.link == 2) {
|
||||
} else if (p.link == 1 || p.link == 2 || p.link == 3) {
|
||||
leds[0].style.fill = "green"; leds[1].style.fill = "green";
|
||||
} else {
|
||||
leds[0].style.fill = "black"; leds[1].style.fill = "black";
|
||||
psvg.style.opacity = 0.4
|
||||
}
|
||||
var iHTML = "<table border=\"0\" class=\"tt_table\">";
|
||||
iHTML += "<tr><td align=\"left\">Link speed</td><td>:</td><td>" + linkS[p.link + 1] + "</td></tr>";
|
||||
if (p.isSFP) {
|
||||
pAdvertised[n] = 0;
|
||||
iHTML += "<tr><td>Vendor</td><td>:</td><td>" + p.sfp_vendor + "</td></tr>";
|
||||
iHTML += "<tr><td>Model</td><td>:</td><td>" + p.sfp_model + "</td></tr>";
|
||||
iHTML += "<tr><td>Serial</td><td>:</td><td>" + p.sfp_serial + "</td></tr>";
|
||||
if (p.sfp_options & 0x40) {
|
||||
iHTML += "<tr><td>Temp</td><td>:</td><td>" + (Number(p.sfp_temp) >> 8) + "." + ((Number(p.sfp_temp) & 0xff)/256.0 * 100).toFixed(0) + " ℃</td></tr>";
|
||||
iHTML += "<tr><td>Vcc</td><td>:</td><td>" + (Number(p.sfp_vcc) / 10000.0).toFixed(2) + " V</td></tr>";
|
||||
iHTML += "<tr><td>TX-Bias</td><td>:</td><td>" + (Number(p.sfp_txbias) / 500.0).toFixed(1) + " mA</td></tr>";
|
||||
iHTML += "<tr><td>TX-Power</td><td>:</td><td>" + (Number(p.sfp_txpower) / 10.0).toFixed(0) + " mW</td></tr>";
|
||||
iHTML += "<tr><td>RX-Power</td><td>:</td><td>" + (Number(p.sfp_rxpower) / 10.0).toFixed(0) + " mW</td></tr>";
|
||||
}
|
||||
} else {
|
||||
pAdvertised[n] = parseInt(p.adv, 2);
|
||||
}
|
||||
iHTML += "</table>";
|
||||
tt.innerHTML = iHTML;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,6 +1,5 @@
|
||||
<!DOCTYPE html>
|
||||
<html>
|
||||
<script src="/ports.js"></script>
|
||||
<script src="/main.js"></script>
|
||||
<link rel="stylesheet" href="style.css">
|
||||
<title>Mirror Configuration</title>
|
||||
@@ -10,7 +9,7 @@
|
||||
<div style="margin-left:16%;padding:1px 16px;height:1000px;">
|
||||
<div id="ports"></div>
|
||||
<h1>Mirror Configuration</h1>
|
||||
<label class="tswitch">Enabled: <input id="me" type="checkbox"><span class="slider"></span></label><br/>
|
||||
<label class="tswitch">Enabled: <input id="me" type="checkbox"></label><br/>
|
||||
<label for="mp">Mirroring Port:</label> <input type="number" id="mp" name="mp" min="1" max="9"/>
|
||||
<h2>Mirrored Ports (TX)</h2>
|
||||
<div id="mPortsTX"></div>
|
||||
|
||||
@@ -49,8 +49,10 @@ function fetchMirror() {
|
||||
let m_tx = parseInt(s.mirror_tx, 2);
|
||||
let m_rx = parseInt(s.mirror_rx, 2);
|
||||
for (let i = 1; i <= numPorts; i++) {
|
||||
setM("mPortsTX"+i, m_tx&1); setM("mPortsRX"+i, m_rx&1);
|
||||
m_tx = m_tx >> 1; m_rx = m_tx >> 1;
|
||||
let p = i - 1;
|
||||
if (numPorts < 9)
|
||||
p = physToLogPort[p];
|
||||
setM("mPortsTX"+i, m_tx&(1<<p)); setM("mPortsRX"+i, m_rx&(1<<p));
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
@@ -1,9 +1,12 @@
|
||||
document.getElementById('sidebar').innerHTML =
|
||||
"<ul><li><a href='index.html'>Overview</a></li>"
|
||||
+ "<li><a href='ports.html'>Port Configuration</a></li>"
|
||||
+ "<li><a href='stat.html'>Port Statistics</a></li>"
|
||||
+ "<li><a href='vlan.html'>VLAN</a></li>"
|
||||
+ "<li><a href='l2.html'>L2 Configuration</a></li>"
|
||||
+ "<li><a href='mirror.html'>Mirroring</a></li>"
|
||||
+ "<li><a href='trunk.html'>Port Aggregation</a></li>"
|
||||
+ "<li><a href='lag.html'>Link Aggregation</a></li>"
|
||||
+ "<li><a href='eee.html'>EEE</a></li>"
|
||||
+ "<li><a href='bandwidth.html'>Bandwidth Limits</a></li>"
|
||||
+ "<li><a href='system.html'>System Settings</a></li>"
|
||||
+ "<li><a href='update.html'>Firmware Update</a></li></ul>";
|
||||
|
||||
|
||||
@@ -0,0 +1,24 @@
|
||||
<!DOCTYPE html>
|
||||
<html>
|
||||
<script src="/main.js"></script>
|
||||
<link rel="stylesheet" href="style.css">
|
||||
<title>FreeSwitchOS Port Configuration</title>
|
||||
</head>
|
||||
<body>
|
||||
<nav id="sidebar"></nav>
|
||||
<div style="margin-left:16%;padding:1px 16px;height:1000px;">
|
||||
<div id="ports"></div>
|
||||
<h1>Port Configuration</h1>
|
||||
<form id="vform" action="/vlan.html">
|
||||
<table id="speedtable">
|
||||
<tr> <th>Port</th> <th>Current Link Speed</th><th>Set Speed</th><th>Disabled</th><th>Apply</th></tr>
|
||||
</table>
|
||||
<h2 style="margin-top:3em">Configure Maximum Frame Size (MTU) forwarded at Port</h2>
|
||||
<table id="mtutable" style="margin-top:1em">
|
||||
</table>
|
||||
<script src="/ports.js"></script>
|
||||
</form>
|
||||
</div>
|
||||
<script src="/navigation.js"></script>
|
||||
</body>
|
||||
</html>
|
||||
@@ -1,21 +1,146 @@
|
||||
function drawPorts() {
|
||||
var f = document.getElementById('ports');
|
||||
console.log("DRAWING PORTS: ", numPorts);
|
||||
for (let i = 0; i < numPorts; i++) {
|
||||
console.log("DRAWING isSFP: ", pIsSFP[i]);
|
||||
const l = document.createElement("object");
|
||||
l.type = "image/svg+xml";
|
||||
if (!pIsSFP[i]) {
|
||||
l.data = "port.svg";
|
||||
l.width ="40";
|
||||
l.height ="40";
|
||||
} else {
|
||||
l.data = "sfp.svg";
|
||||
l.width = "60";
|
||||
l.height = "60";
|
||||
var mtus = new Int16Array(10);
|
||||
var clicked = new Int8Array(10);
|
||||
function createPortTable() {
|
||||
var tbl = document.getElementById('speedtable');
|
||||
if (tbl.rows.length <= 2 && numPorts) {
|
||||
clearInterval(pTableInterval);
|
||||
const sSelect = '<select name="speed_sel" id="speed_sel">'
|
||||
+ '<option value="auto">Auto</option>'
|
||||
+ '<option value="2g5">2500MBit/Full</option>'
|
||||
+ '<option value="1g">1000MBit/Full</option>'
|
||||
+ '<option value="100m full">100MBit/Full</option>'
|
||||
+ '<option value="100m half">100MBit/Half</option>'
|
||||
+ '<option value="10m full">10MBit/Full</option>'
|
||||
+ '<option value="10m half">10MBit/Half</option>'
|
||||
+ '</select>';
|
||||
const dSwitch = '<input type="checkbox" id="disable_port" onchange="portOnOff();">'
|
||||
for (let i = 1; i <= numPorts; i++) {
|
||||
if (pIsSFP[i-1])
|
||||
continue;
|
||||
console.log("Table row: " + i + "pState: " + pState[i-2]);
|
||||
const tr = tbl.insertRow();
|
||||
let td = tr.insertCell(); td.appendChild(document.createTextNode(`Port ${i}`));
|
||||
td = tr.insertCell(); td.innerHTML = linkS[pState[i] + 1];
|
||||
td = tr.insertCell(); td.innerHTML = sSelect.replaceAll("speed_sel", "speed_sel_" + i);
|
||||
td = tr.insertCell(); td.innerHTML = dSwitch.replaceAll("disable_port", "disable_port_" + i)
|
||||
.replace("portOnOff()", "portOnOff(" + i + ")");
|
||||
var button = '<button type="button" style="margin: 0 0 0 24px" onclick="applySpeed(' + i + ');">Apply</button>';
|
||||
td = tr.insertCell();
|
||||
td.innerHTML = button;
|
||||
}
|
||||
l.id="port" + (i+1);
|
||||
f.appendChild(l);
|
||||
}
|
||||
console.log("DRAWING DONE ");
|
||||
tbl = document.getElementById('mtutable');
|
||||
if (tbl.rows.length <= 2 && numPorts) {
|
||||
const mSelect = '<select name="mtu_sel" id="mtu_sel">'
|
||||
+ '<option value="16383">16383</option>'
|
||||
+ '<option value="1522">1522</option>'
|
||||
+ '<option value="1536">1536</option>'
|
||||
+ '<option value="1552">1552</option>'
|
||||
+ '<option value="9216">9216</option>'
|
||||
+ '</select>';
|
||||
var tr = tbl.insertRow();
|
||||
for (let i = 1; i <= numPorts; i++) {
|
||||
let td = tr.insertCell();
|
||||
if (pIsSFP[i-1])
|
||||
td.innerHTML = '<object type="image/svg+xml" data="sfp.svg" width="60"></object>'
|
||||
else
|
||||
td.innerHTML = '<object type="image/svg+xml" data="port.svg" width="40"></object>'
|
||||
}
|
||||
tr = tbl.insertRow();
|
||||
for (let i = 1; i <= numPorts; i++) {
|
||||
let td = tr.insertCell();
|
||||
td.innerHTML = mSelect.replaceAll("mtu_sel", "mtu_sel_" + i);
|
||||
}
|
||||
tr = tbl.insertRow();
|
||||
for (let i = 1; i <= numPorts; i++) {
|
||||
let td = tr.insertCell();
|
||||
td.innerHTML = '<button type="button" style="margin: 0 0 0 24px" onclick="applyMTU(' + i + ');">Apply</button>';
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
function updatePortTable() {
|
||||
console.log("updatePortTable called");
|
||||
var tbl = document.getElementById('speedtable');
|
||||
if (tbl.rows.length <= 2 || !numPorts)
|
||||
return;
|
||||
for (let i = 1; i <= numPorts ; i++) {
|
||||
if (pIsSFP[i-1])
|
||||
continue;
|
||||
tbl.rows[i].cells[1].innerHTML = `${linkS[pState[i-1]+1]}`;
|
||||
if (!clicked[i] && pState[i - 1] < 0) {
|
||||
document.getElementById('speed_sel_' + i).disabled = true;
|
||||
document.getElementById('disable_port_' + i).checked = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
async function applySpeed(port) {
|
||||
var speed = document.getElementById('speed_sel_' + port).value;
|
||||
var disabled = document.getElementById('disable_port_' + port).checked;
|
||||
var cmd = "port " + port + " ";
|
||||
if (!disabled)
|
||||
cmd = cmd + speed;
|
||||
else
|
||||
cmd = cmd + "off";
|
||||
console.log("CMD: " + cmd);
|
||||
try {
|
||||
const response = await fetch('/cmd', {
|
||||
method: 'POST',
|
||||
body: cmd
|
||||
});
|
||||
console.log('Completed!', response);
|
||||
} catch(err) {
|
||||
console.error(`Error: ${err}`);
|
||||
}
|
||||
}
|
||||
|
||||
async function portOnOff(p) {
|
||||
var disabled = document.getElementById('disable_port_' + p).checked;
|
||||
document.getElementById('speed_sel_' + p).disabled = disabled;
|
||||
clicked[p] = 1;
|
||||
}
|
||||
|
||||
async function applyMTU(port) {
|
||||
var mtu = document.getElementById('mtu_sel_' + port).value;
|
||||
var cmd = "mtu " + port + " " + mtu;
|
||||
try {
|
||||
const response = await fetch('/cmd', {
|
||||
method: 'POST',
|
||||
body: cmd
|
||||
});
|
||||
console.log('MTU Completed!', response);
|
||||
getMTUs();
|
||||
} catch(err) {
|
||||
console.error(`Error: ${err}`);
|
||||
}
|
||||
}
|
||||
|
||||
function getMTUs() {
|
||||
var xhttp = new XMLHttpRequest();
|
||||
xhttp.onreadystatechange = function() {
|
||||
if (this.readyState == 4 && this.status == 200) {
|
||||
const s = JSON.parse(xhttp.responseText);
|
||||
console.log("MTUS: ", JSON.stringify(s));
|
||||
for (let i = 0; i < s.length; i++) {
|
||||
p = s[i];
|
||||
let n = p.portNum;
|
||||
mtus[n] = parseInt(p.mtu, 16);
|
||||
var mtu = document.getElementById('mtu_sel_' + n);
|
||||
if (!mtu)
|
||||
continue;
|
||||
mtu.value = mtus[n];
|
||||
clearInterval(pMTUInterval);
|
||||
}
|
||||
}
|
||||
};
|
||||
xhttp.open("GET", "/mtu.json", true);
|
||||
xhttp.timeout = 1500; xhttp.send();
|
||||
}
|
||||
|
||||
window.addEventListener("load", function() {
|
||||
const updatePortTableInterval = setInterval(updatePortTable, 1000);
|
||||
});
|
||||
|
||||
const pTableInterval = setInterval(createPortTable, 1000);
|
||||
const pMTUInterval = setInterval(getMTUs, 1200);
|
||||
|
||||
@@ -1,17 +1,44 @@
|
||||
<!DOCTYPE html>
|
||||
<html>
|
||||
<script src="/ports.js"></script>
|
||||
<script src="/main.js"></script>
|
||||
<link rel="stylesheet" href="style.css">
|
||||
<title>FreeSwitchOS Port Statistics</title>
|
||||
<style>
|
||||
.popup {
|
||||
display: none;
|
||||
position: fixed;
|
||||
top: 0; left: 0;
|
||||
width: 100%; height: 100%;
|
||||
background: rgba(0, 0, 0, 0.5);
|
||||
justify-content: center;
|
||||
align-items: center;
|
||||
}
|
||||
.popup-content {
|
||||
background: #fff;
|
||||
padding: 20px;
|
||||
border-radius: 5px;
|
||||
text-align: center;
|
||||
height: 80%
|
||||
}
|
||||
.popup .popup-content {
|
||||
overflow-y: scroll;
|
||||
}
|
||||
</style>
|
||||
</head>
|
||||
<body>
|
||||
<nav id="sidebar"></nav>
|
||||
<div style="margin-left:16%;padding:1px 16px;height:1000px;">
|
||||
<div id="ports"></div>
|
||||
<div id="popup" class="popup">
|
||||
<div class="popup-content">
|
||||
<h2>Detailed Port Statistics</h2>
|
||||
<div id="popup_text"></div>
|
||||
<button id="closePopup" class="action">Close</button>
|
||||
</div>
|
||||
</div>
|
||||
<h1>Port Statistics</h1>
|
||||
<table id="statstable">
|
||||
<tr> <th>Port</th> <th>link</th> <th>TX Good</th> <th>TX Bad</th> <th>RX Good</th> <th>RX Bad</th> </tr>
|
||||
<tr> <th>Port</th> <th>link</th> <th>TX Good</th> <th>TX Bad</th> <th>RX Good</th> <th>RX Bad</th> <th> All Counters </th></tr>
|
||||
<script src="/stat.js"></script>
|
||||
</table>
|
||||
</div>
|
||||
|
||||
@@ -1,7 +1,166 @@
|
||||
const mib_counters = [
|
||||
"Interface in Octets", 8,
|
||||
"", 0,
|
||||
"Interface out Octets", 8,
|
||||
"", 0,
|
||||
"Interface in Unicast Pkts", 8,
|
||||
"", 0,
|
||||
"Interface in Multicast Pkts", 8,
|
||||
"", 0,
|
||||
"Interface in Broadcast Pkts", 8,
|
||||
"", 0,
|
||||
"Interface out Unicast Pkts", 8, // 10
|
||||
"", 0,
|
||||
"Interface out Multicast Pkts", 8,
|
||||
"", 0,
|
||||
"Interface out Broadcast Pkts", 8,
|
||||
"", 0,
|
||||
"Interface out discards", 4,
|
||||
"802.1d Tp Port in discards", 4,
|
||||
"802.3 Single collision frames", 4,
|
||||
"802.3 Multi collision frames", 4,
|
||||
"802.3 Deferred transmissions", 4, // 20
|
||||
"802.3 Late collisions", 4,
|
||||
"802.3 Excessive collisions", 4,
|
||||
"802.3 Symbol errors", 4,
|
||||
"802.3 Control in unknown opcodes", 4,
|
||||
"802.3 In Pause frames", 4,
|
||||
"802.3 Out Pause frames", 4,
|
||||
"Ether drop events", 4,
|
||||
"TX Ether Broadcast Pkts", 4,
|
||||
"TX Ether Multicast Pkts", 4,
|
||||
"TX Ether CRC Align errors", 4, // 30
|
||||
"RX Ether CRC Align errors", 4,
|
||||
"TX Ether Undersized Pkts", 4,
|
||||
"RX Ether Undersized Pkts", 4,
|
||||
"TX Ether Oversized Pkts", 4,
|
||||
"RX Ether Oversized Pkts", 4,
|
||||
"TX Ether Fragments", 4,
|
||||
"RX Ether fragments", 4,
|
||||
"TX Ether Jabbers", 4,
|
||||
"RX Ether Jabbers", 4,
|
||||
"TX Ether Collisions", 4, // 40
|
||||
"TX Ether Pkts 640 Octets", 4,
|
||||
"RX Ether Pkts 640 Octets", 4,
|
||||
"TX Ether 65-127 Octets", 4,
|
||||
"RX Ether 65-127 Octets", 4,
|
||||
"TX Ether Pkts 128-255 Octets", 4,
|
||||
"RX Ether Pkts 128-255 Octets", 4,
|
||||
"TX Ether Pkts 256-511 Octets", 4,
|
||||
"RX Ether Pkts 256-511 Octets", 4,
|
||||
"TX Ether Pkts 512-1023 Octets", 4,
|
||||
"RX Ether Pkts 512-1023 Octets", 4, // 50
|
||||
"TX Ether Pkts 1024-1518 Octets", 4,
|
||||
"RX Ether Pkts 1024-1518 Octets", 4,
|
||||
"", 4,
|
||||
"RX Ether Undersized Drop Pkts", 4, // 54
|
||||
"TX Ether Pkts >1518 Octets", 4,
|
||||
"RX Ether Pkts >1518 Octets", 4,
|
||||
"TX Ether Pkts too large", 4,
|
||||
"RX Ether Pkts too large", 4,
|
||||
"TX Ether Flexible Octets Set 1", 4,
|
||||
"RX Ether Flexible Octets Set 1", 4,// 60
|
||||
"TX Ether Flexible Octets CRC Set 1", 4,
|
||||
"RX Ether Flexible Octets CRC Set 1", 4,
|
||||
"TX Ether Flexible Octets Set 0", 4,
|
||||
"RX Ether Flexible Octets Set 0", 4,
|
||||
"TX Ether Flexible Octets CRC Set 0", 4,
|
||||
"RX Ether Flexible Octets CRC Set 0", 4,
|
||||
"Lenth Field Errors", 4,
|
||||
"False Carriers", 4,
|
||||
"Undersized Octets", 4,
|
||||
"Framing Errors", 4, // 70
|
||||
"", 4,
|
||||
"RX MAC Discards", 4, // 72
|
||||
"RX MAC IPG Short Drop", 4,
|
||||
"", 4,
|
||||
"802.1d TP Learned Entry Discards", 4, // 75
|
||||
"Egress Queue 7 Dropped Pkts", 4,
|
||||
"Egress Queue 6 Dropped Pkts", 4,
|
||||
"Egress Queue 5 Dropped Pkts", 4,
|
||||
"Egress Queue 4 Dropped Pkts", 4,
|
||||
"Egress Queue 3 Dropped Pkts", 4, // 80
|
||||
"Egress Queue 2 Dropped Pkts", 4,
|
||||
"Egress Queue 1 Dropped Pkts", 4,
|
||||
"Egress Queue 0 Dropped Pkts", 4,
|
||||
"Egress Queue 7 Out Pkts", 4,
|
||||
"Egress Queue 6 Out Pkts", 4,
|
||||
"Egress Queue 5 Out Pkts", 4,
|
||||
"Egress Queue 4 Out Pkts", 4,
|
||||
"Egress Queue 3 Out Pkts", 4,
|
||||
"Egress Queue 2 Out Pkts", 4,
|
||||
"Egress Queue 1 Out Pkts", 4, // 90
|
||||
"Egress Queue 0 Out Pkts", 4,
|
||||
"TX Good Counter", 8,
|
||||
"", 0,
|
||||
"RX Good Counter", 8,
|
||||
"", 0,
|
||||
"RX Error Counter", 4,
|
||||
"TX Error Counter", 4,
|
||||
"TX Good Counter PHY", 8,
|
||||
"", 0,
|
||||
"RX Good Counter PHY", 8, // 100
|
||||
"", 0,
|
||||
"RX Error Counter PHY", 4,
|
||||
"TX Error Counter PHY", 4
|
||||
];
|
||||
|
||||
|
||||
function getCounters(port) {
|
||||
var xhttp = new XMLHttpRequest();
|
||||
const popup = document.getElementById('popup');
|
||||
xhttp.onreadystatechange = function() {
|
||||
if (this.readyState == 4 && this.status == 200) {
|
||||
const s = JSON.parse(xhttp.responseText);
|
||||
console.log("Counters: ", JSON.stringify(s));
|
||||
const ptext = document.getElementById('popup_text');
|
||||
var t = "<table style='width:100%'> <tr> <th>Counter</th> <th>Value</th> <th>Counter</th> <th>Value</th></tr> <tr>";
|
||||
console.log("Counter 0: ", BigInt(s[0]).toString(), " length: ", s.length);
|
||||
var c = 0;
|
||||
for (i = 0; i < mib_counters.length; i += 4) {
|
||||
console.log(i, " ", mib_counters[i], ": ", mib_counters[i+1]);
|
||||
if (mib_counters[i] == "" && mib_counters[i + 1] == 8) {
|
||||
console.log("c " + i + ": continue");
|
||||
continue;
|
||||
}
|
||||
var count = BigInt(s[i/4]);
|
||||
if (mib_counters[i+1] == 8) {
|
||||
t += "<td>" + mib_counters[i] + "</td><td>" + count.toString() + "</td>";
|
||||
c += 1;
|
||||
} else if (mib_counters[i+1] == 4) {
|
||||
if (mib_counters[i] != "") {
|
||||
t += "<td>" + mib_counters[i] + "</td><td>" + (count >> 32n).toString() + "</td>";
|
||||
c += 1;
|
||||
}
|
||||
if (c == 2) {
|
||||
t += "</tr> <tr>";
|
||||
c = 0;
|
||||
}
|
||||
if (mib_counters[i+2] != "") {
|
||||
t += "<td>" + mib_counters[i+2] + "</td><td>" + (count & 4294967295n).toString() + "</td>";
|
||||
c += 1;
|
||||
}
|
||||
}
|
||||
if (c == 2) {
|
||||
t += "</tr> <tr>";
|
||||
c = 0;
|
||||
}
|
||||
}
|
||||
ptext.innerHTML = t + "</tr></table>";
|
||||
popup.style.display = 'flex';
|
||||
}
|
||||
};
|
||||
xhttp.open("GET", "/counters.json?port=" + port, true);
|
||||
xhttp.timeout = 1500; xhttp.send();
|
||||
}
|
||||
|
||||
|
||||
function fillStats() {
|
||||
var tbl = document.getElementById('statstable');
|
||||
if (!numPorts)
|
||||
return;
|
||||
if (tbl.rows.length > 1) {
|
||||
for (let i = 0; i < 6; i++) {
|
||||
for (let i = 0; i < numPorts; i++) {
|
||||
console.log("Table Update row: " + i + " state " + pState[i] + " is " + linkS[pState[i] +1]);
|
||||
tbl.rows[i+1].cells[1].innerHTML = `${linkS[pState[i]+1]}`;
|
||||
tbl.rows[i+1].cells[2].innerHTML = `${txG[i]} pkts`;
|
||||
@@ -10,7 +169,7 @@ function fillStats() {
|
||||
tbl.rows[i+1].cells[5].innerHTML = `${rxB[i]} pkts`;
|
||||
}
|
||||
} else {
|
||||
for (let i = 0; i < 6; i++) {
|
||||
for (let i = 0; i < numPorts; i++) {
|
||||
console.log("Table row: " + i);
|
||||
const tr = tbl.insertRow();
|
||||
let td = tr.insertCell(); td.appendChild(document.createTextNode(`Port ${i+1}`));
|
||||
@@ -19,8 +178,21 @@ function fillStats() {
|
||||
td = tr.insertCell();td.appendChild(document.createTextNode(`${txB[i]} pkts`));
|
||||
td = tr.insertCell();td.appendChild(document.createTextNode(`${rxG[i]} pkts`));
|
||||
td = tr.insertCell();td.appendChild(document.createTextNode(`${rxB[i]} pkts`));
|
||||
var button = '<button type="button" style="margin: 0 0 0 24px" onclick="getCounters(' + i + ');">Show</button>';
|
||||
td = tr.insertCell(); td.innerHTML = button;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
const stat = setInterval(fillStats, 1000);
|
||||
|
||||
const popup = document.getElementById('popup');
|
||||
const closePopup = document.getElementById('closePopup');
|
||||
closePopup.addEventListener('click', () => {
|
||||
popup.style.display = 'none';
|
||||
});
|
||||
window.addEventListener('click', (event) => {
|
||||
if (event.target === popup) {
|
||||
popup.style.display = 'none';
|
||||
}
|
||||
});
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
h1, h2 {
|
||||
h1, h2, h3 {
|
||||
color: #226;
|
||||
}
|
||||
ul {
|
||||
@@ -6,8 +6,9 @@ ul {
|
||||
margin: 0;
|
||||
padding: 0;
|
||||
width: 12%;
|
||||
height: 100%;
|
||||
height: calc(100% - 50px);
|
||||
position: fixed;
|
||||
top: 50px;
|
||||
overflow: auto;
|
||||
}
|
||||
|
||||
@@ -26,7 +27,7 @@ li a:hover {
|
||||
}
|
||||
|
||||
table, th, td {
|
||||
border: 1px solid navy;
|
||||
border: 1px solid #226;
|
||||
padding: 8px;
|
||||
}
|
||||
table {
|
||||
@@ -38,10 +39,17 @@ td {
|
||||
text-align: right;
|
||||
}
|
||||
|
||||
input[type=submit] { padding: 8px 16px;background-color:#aaf;color:#000; margin-bottom: 2em;}
|
||||
input[type=submit] { padding: 8px 16px;background-color:#aaf;color:#000; margin-bottom: 2em;border-radius: 15px;width: 100%;}
|
||||
input[type=submit]:hover { background-color: #226; color: white;}
|
||||
button {padding: 8px; background-color:#99f; color:#000;}
|
||||
input[type=file] { padding: 8px 16px;background-color:#aaf;color:#000; margin-bottom: 2em;border-radius: 15px;width: 60%;}
|
||||
input[type=file]:hover { background-color: #226; color: white;}
|
||||
b
|
||||
button {padding: 8px; background-color:#99f; color:#000;border-radius: 15px;}
|
||||
button:hover { background-color: #226; color: white;}
|
||||
.action{padding: 8px 16px;margin-top: 2em;margin-right: 3em; background-color: #aaf;color: #000;border-radius: 15px; width: 100%;}
|
||||
.action:hover { background-color: #226; color: white;}
|
||||
|
||||
/* Port selection inputs */
|
||||
.psel {
|
||||
position: absolute;
|
||||
opacity: 0;
|
||||
@@ -58,7 +66,7 @@ button:hover { background-color: #226; color: white;}
|
||||
/* outline: 2px solid #f00;*/
|
||||
opacity: 1.0;
|
||||
}
|
||||
object {
|
||||
object, img {
|
||||
margin: 0.5em 0.5em;
|
||||
}
|
||||
.cbgroup {
|
||||
@@ -73,4 +81,85 @@ object {
|
||||
.isSFP{ opacity: .4; background-color: #660;}
|
||||
.isNOK{ color: #900;}
|
||||
.isOK{ color: #090;}
|
||||
.action{padding: 8px 16px;margin-top: 2em;margin-right: 3em; background-color: #aaf;color: #000;}
|
||||
.ip{padding:8px 16px;margin-bottom: 1em;margin-left: 1em}
|
||||
.row {display: flex;}
|
||||
.rcol {flex: 90%;}
|
||||
.lcol {flex: 10%;}
|
||||
|
||||
/* Login page */
|
||||
.login_page {margin: 0; padding: 0; background: #aaf; height: 100vh; overflow: hidden;}
|
||||
.center{
|
||||
position: absolute; top: 50%; left: 50%;
|
||||
transform: translate(-50%, -40% );
|
||||
width: 500px; height: 400px;
|
||||
background: white;
|
||||
border-radius: 2px;
|
||||
}
|
||||
|
||||
.center h1 {
|
||||
text-align: center;
|
||||
border-bottom: 1px solid silver;
|
||||
}
|
||||
|
||||
.center form {
|
||||
padding: 0 60px;
|
||||
box-sizing: border-box;
|
||||
}
|
||||
form .txt_field{
|
||||
position: relative;
|
||||
border-bottom: 2px solid #adadad;
|
||||
margin: 30px 0;
|
||||
}
|
||||
.txt_field input { width: 100%; padding: 0 5px; height: 40px; font-size: 16px; border: none;
|
||||
background: none;
|
||||
outline: none;}
|
||||
.txt_field label { position: absolute; top: 50%; left: 5px; color:#adadad; transform: translateY(-50%);
|
||||
font-size: 16px; pointer-events: none;transition: .5s; }
|
||||
.txt_field span::before{ content:''; position: absolute; top: 40px; left:0; width: 100%; height: 2px; background: #aaf;}
|
||||
.txt_field input:focus ~ label,
|
||||
.txt_field input:valid ~ label { top: -5px; color: #aaf; }
|
||||
|
||||
.tooltip {
|
||||
position: relative; display: inline-block; cursor: pointer;
|
||||
}
|
||||
|
||||
|
||||
.tooltiptext {
|
||||
visibility: hidden;
|
||||
width: 260px;
|
||||
background-color: #226;
|
||||
color: #fff;
|
||||
text-align: center;
|
||||
border-radius: 6px;
|
||||
padding: 5px 0;
|
||||
position: absolute;
|
||||
z-index: 1;
|
||||
top: 80%;
|
||||
left: 50%;
|
||||
margin-left: -130px;
|
||||
}
|
||||
|
||||
.tooltiptext::after {
|
||||
content: "";
|
||||
position: absolute;
|
||||
bottom: 100%;
|
||||
left: 50%;
|
||||
margin-left: -5px;
|
||||
border-width: 5px;
|
||||
border-style: solid;
|
||||
border-color: transparent transparent black transparent;
|
||||
}
|
||||
|
||||
|
||||
.tooltip:hover .tooltiptext {
|
||||
visibility: visible;
|
||||
opacity: 1;
|
||||
}
|
||||
.tt_table {
|
||||
width: 100%; font-size:90%;
|
||||
}
|
||||
.tt_table td {
|
||||
text-align: left;
|
||||
}
|
||||
select { text-align-last: right; font-family: monospace}
|
||||
option { direction: rtl; font-family: sans-serif}
|
||||
|
||||
@@ -0,0 +1,85 @@
|
||||
<!DOCTYPE html>
|
||||
<html>
|
||||
<head>
|
||||
<link rel="stylesheet" href="style.css">
|
||||
<title>System Settings</title>
|
||||
<style>
|
||||
.tab-bar { display: flex; border-bottom: 2px solid #226; margin-bottom: 0; margin-left: 16%; padding: 1px 16px; padding-bottom: 0; }
|
||||
.tab-btn { padding: 10px 20px; background-color: #ddf; border: none; cursor: pointer; font-size: 1em; border-radius: 8px 8px 0 0; margin-right: 4px; }
|
||||
.tab-btn:hover { background-color: #aaf; }
|
||||
.tab-btn.active { background-color: #aaf; font-weight: bold; border-bottom: 2px solid #aaf; margin-bottom: -2px; }
|
||||
.tab-content { display: none; }
|
||||
.tab-content.active { display: block; }
|
||||
</style>
|
||||
</head>
|
||||
<body>
|
||||
<div class="tab-bar">
|
||||
<button class="tab-btn active" onclick="openTab(event, 'system-tab')">System</button>
|
||||
<button class="tab-btn" onclick="openTab(event, 'advanced-tab')">Advanced</button>
|
||||
</div>
|
||||
<nav id="sidebar"></nav>
|
||||
<div style="margin-left:16%;padding:1px 16px;height:1000px;">
|
||||
<div id="ports"></div>
|
||||
|
||||
<div id="system-tab" class="tab-content active">
|
||||
<h1>System Settings</h1>
|
||||
<label class="dhcpon">DHCP client endabled: <input id="dhcp" type="checkbox" onchange="dhcpClicked(this)"></label><br/><br/>
|
||||
<div class="row">
|
||||
<div class="lcol"> <label for="ip">IP address:</label></div>
|
||||
<div class="rcol"> <input id="ip" class="ip" type="text" minlength="7" maxlength="15" size="15"/></div>
|
||||
</div>
|
||||
<div class="row">
|
||||
<div class="lcol"> <label for="netmask">Netmask:</label></div>
|
||||
<div class="rcol"><input id="netmask" class="ip" type="text" minlength="7" maxlength="15" size="15"/></div>
|
||||
</div>
|
||||
<div class="row">
|
||||
<div class="lcol"> <label for="gw">Gateway:</label></div>
|
||||
<div class="rcol"><input id="gw" class="ip" type="text" minlength="7" maxlength="15" size="15"/></div>
|
||||
</div>
|
||||
<br/>
|
||||
When updating the above settings, remember to point your browser to the new IP afterwards:<br/>
|
||||
<input style="width:40%;" class="action" id="ip_sub" onclick="ipSub();" type="button" value="Update Settings"><br/>
|
||||
<br/>
|
||||
Save all current settings to Flash:<br/>
|
||||
<input style="width:40%;" class="action" id="flash_sub" onclick="flashSave();" type="button" value="Save Settings to Flash">
|
||||
</div>
|
||||
|
||||
<div id="advanced-tab" class="tab-content">
|
||||
<h1>Advanced Settings</h1>
|
||||
<div class="lcol"> <label for="config_display">Startup configuration:</label></div>
|
||||
<textarea id="config_display" rows="8" cols="60"></textarea>
|
||||
<br/><br/>
|
||||
Be careful when saving the directly edited startup configuration, you can lock yourself out:<br/>
|
||||
<input style="width:40%;" class="action" id="clear_config" onclick="clearConfig();" type="button" value="Clear Startup Config">
|
||||
<br/>
|
||||
<input style="width:40%;" class="action" id="flash_startup_sub" onclick="flashStartupSave();" type="button" value="Save Startup Settings to Flash">
|
||||
<br/>
|
||||
<input style="width:40%;" class="action" id="switch_reset" onclick="resetSwitch();" type="button" value="Reset Switch">
|
||||
</div>
|
||||
<div class="row">
|
||||
<div class="lcol"> <label for="gw">Gateway:</label></div>
|
||||
<div class="rcol"><input id="gw" class="ip" type="text" minlength="7" maxlength="15" size="15"/></div>
|
||||
</div><br/>
|
||||
When updating the above settings, remember to point your browser to the new IP afterwards:<br/>
|
||||
<input style="width:40%;" class="action" id="ip_sub" onclick="ipSub();" type="button" value="Update Settings"><br/>
|
||||
<br/><br/>
|
||||
<label class="dhcpdon">Enable DHCP Server: <input id="dhcpd" type="checkbox"></label><br/><br/>
|
||||
<label class="dhcpdvlan">Limit DHCP Server to VLAN (0: serve all VLANs): <input type="number" min="0" max="2047" value="0" id="dhcpd_vid" name="dhcpd_vid"></label><br/>
|
||||
<input style="width:40%;" class="action" id="dhcpd_sub" onclick="dhcpdSub();" type="button" value="Change DHCPD State"><br/><br/><br/>
|
||||
Save all current settings to Flash:<br/>
|
||||
<input style="width:40%;" class="action" id="flash_sub" onclick="flashSave();" type="button" value="Save Settings to Flash">
|
||||
|
||||
</div>
|
||||
<script src="/config.js"></script>
|
||||
<script src="/system.js"></script>
|
||||
<script src="/navigation.js"></script>
|
||||
<script>
|
||||
function openTab(evt, tabId) {
|
||||
document.querySelectorAll('.tab-content').forEach(c => c.classList.remove('active'));
|
||||
document.querySelectorAll('.tab-btn').forEach(b => b.classList.remove('active'));
|
||||
document.getElementById(tabId).classList.add('active');
|
||||
evt.currentTarget.classList.add('active');
|
||||
}
|
||||
</script>
|
||||
</body>
|
||||
</html>
|
||||
@@ -0,0 +1,178 @@
|
||||
var systemInterval = Number();
|
||||
const ips = ["ip", "netmask", "gw"];
|
||||
|
||||
function checkIp(ip) {
|
||||
const ipv4 = /^(\d{1,3}\.){3}\d{1,3}$/;
|
||||
if (!ipv4.test(ip)) {alert(`Invalid ip:${ip}`); return false };
|
||||
return true;
|
||||
}
|
||||
|
||||
async function ipSub() {
|
||||
if (document.getElementById('dhcp').checked) {
|
||||
var cmd = "ip dhcp";
|
||||
try {
|
||||
const response = await fetch('/cmd', {
|
||||
method: 'POST',
|
||||
body: cmd
|
||||
});
|
||||
console.log('Completed!', response);
|
||||
systemInterval = setInterval(fetchIP, 10000);
|
||||
} catch(err) {
|
||||
console.error(`Error: ${err}`);
|
||||
}
|
||||
return;
|
||||
}
|
||||
for (let i=0;i<3;i++) {
|
||||
if (!checkIp(document.getElementById(ips[i]).value))
|
||||
return;
|
||||
}
|
||||
for (let i=0; i<3;i++){
|
||||
var cmd = ips[i]+' '+document.getElementById(ips[i]).value;
|
||||
try {
|
||||
const response = await fetch('/cmd', {
|
||||
method: 'POST',
|
||||
body: cmd
|
||||
});
|
||||
console.log('Completed!', response);
|
||||
fetchIP();
|
||||
} catch(err) {
|
||||
console.error(`Error: ${err}`);
|
||||
}
|
||||
}
|
||||
}
|
||||
async function dhcpdSub() {
|
||||
var dhcpd_cmd = "dhcpd off";
|
||||
if (document.getElementById('dhcpd').checked) {
|
||||
dhcpd_cmd = "dhcpd on";
|
||||
var v=document.getElementById('dhcpd_vid').value
|
||||
if (v && v!= 0)
|
||||
dhcpd_cmd = dhcpd_cmd + " " + v;
|
||||
}
|
||||
try {
|
||||
console.log("Sending: ", dhcpd_cmd);
|
||||
const response = await fetch('/cmd', {
|
||||
method: 'POST',
|
||||
body: dhcpd_cmd
|
||||
});
|
||||
console.log('Completed!', response);
|
||||
} catch(err) {
|
||||
console.error(`Error: ${err}`);
|
||||
}
|
||||
}
|
||||
function dhcpClicked(e)
|
||||
{
|
||||
console.log("dhcpClicked called");
|
||||
if (e.checked) {
|
||||
for (let i=0; i<3;i++)
|
||||
document.getElementById(ips[i]).disabled = true;
|
||||
document.getElementById('dhcpd').disabled = true;
|
||||
} else {
|
||||
console.log("dhcpClicked off");
|
||||
for (let i=0; i<3;i++)
|
||||
document.getElementById(ips[i]).disabled = false;
|
||||
document.getElementById('dhcpd').disabled = false;
|
||||
}
|
||||
}
|
||||
async function sendConfig(c) {
|
||||
const form = new FormData();
|
||||
form.append("MAX_FILE_SIZE", "4096");
|
||||
form.append("configuration", new Blob([c], {type: "application/octet-stream"}));
|
||||
try {
|
||||
const response = await fetch('/config', {
|
||||
method: 'POST',
|
||||
body: form
|
||||
});
|
||||
console.log('Completed!', response);
|
||||
} catch(err) {
|
||||
console.error(`Error: ${err}`);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
async function flashSave() {
|
||||
fetchConfig().then((s) => {
|
||||
parseConf(s);
|
||||
fetchCmdLog().then((s) => {
|
||||
parseConf(s);
|
||||
var body = "";
|
||||
for (const x of configuration) { body = body + x + "\n"; }
|
||||
console.log("CONFIGURATION to save: ", body);
|
||||
sendConfig(body);
|
||||
});
|
||||
});
|
||||
setTimeout(() => {
|
||||
fetchIP();
|
||||
}, 500);
|
||||
}
|
||||
|
||||
async function flashStartupSave() {
|
||||
var configContent = document.getElementById("config_display").value;
|
||||
console.log("CONFIGURATION to save: ", configContent);
|
||||
sendConfig(configContent);
|
||||
// Clear the command log 1 second after initiating the config save
|
||||
setTimeout(() => {
|
||||
fetch('/cmd_log_clear', { method: 'GET' })
|
||||
.then(response => console.log('Command log cleared', response))
|
||||
.catch(err => console.error('Error clearing command log:', err));
|
||||
}, 1000);
|
||||
}
|
||||
|
||||
function clearConfig() {
|
||||
document.getElementById("config_display").value = "";
|
||||
|
||||
// Validate and populate with current IP settings
|
||||
for (let i=0; i<3; i++) {
|
||||
if (!checkIp(document.getElementById(ips[i]).value))
|
||||
return;
|
||||
}
|
||||
|
||||
var configLines = "";
|
||||
for (let i=0; i<3; i++){
|
||||
var cmd = ips[i]+' '+document.getElementById(ips[i]).value;
|
||||
configLines += cmd + "\n";
|
||||
}
|
||||
|
||||
document.getElementById("config_display").value = configLines;
|
||||
}
|
||||
|
||||
function fetchIP() {
|
||||
var xhttp = new XMLHttpRequest();
|
||||
xhttp.onreadystatechange = function() {
|
||||
if (this.readyState == 4 && this.status == 200) {
|
||||
const s = JSON.parse(xhttp.responseText);
|
||||
console.log("IP: ", s);
|
||||
document.getElementById("ip").value=s.ip_address;
|
||||
document.getElementById("netmask").value=s.ip_netmask;
|
||||
document.getElementById("gw").value=s.ip_gateway;
|
||||
document.getElementById('dhcp').checked = s.dhcp_client;
|
||||
document.getElementById('dhcpd').checked = s.dhcp_server;
|
||||
clearInterval(systemInterval);
|
||||
// Fetch and populate the config textbox
|
||||
fetchConfig().then((configText) => {
|
||||
let fullConfig = configText;
|
||||
// Fetch and append cmd_log
|
||||
//return fetchCmdLog().then((cmdLogText) => {
|
||||
// if (cmdLogText) {
|
||||
// fullConfig = fullConfig + cmdLogText;
|
||||
// }
|
||||
document.getElementById("config_display").value = fullConfig;
|
||||
});
|
||||
};
|
||||
}
|
||||
xhttp.open("GET", `/information.json`, true);
|
||||
xhttp.send();
|
||||
}
|
||||
|
||||
function resetSwitch() {
|
||||
if (!confirm('Are you sure you want to reset the switch?')) {
|
||||
return;
|
||||
}
|
||||
fetch('/reset', { method: 'GET' }).catch(() => {});
|
||||
setTimeout(() => {
|
||||
alert('Switch is resetting. Please wait and refresh the page.');
|
||||
}, 3000);
|
||||
}
|
||||
|
||||
window.addEventListener("load", function() {
|
||||
systemInterval = setInterval(fetchIP, 1000);
|
||||
});
|
||||
@@ -6,13 +6,13 @@
|
||||
</head>
|
||||
<body>
|
||||
<nav id="sidebar"></nav>
|
||||
<div style="margin-left:16%;padding:1px 16px;height:1000px;">
|
||||
<div id="ports"></div>
|
||||
<div style="margin-left:16%;padding:1px 16px;height:1000px;width:40%;">
|
||||
<h1>Firmware Update</h1>
|
||||
<form enctype="multipart/form-data" action="/upload" method="POST">
|
||||
<input type="hidden" name="MAX_FILE_SIZE" value="1000000" />
|
||||
Choose a firmware update file to upload: <input name="uploadedfile" type="file" accept=".bin" /><br />
|
||||
<input type="submit" value="Upload File" />
|
||||
Choose a firmware update file to upload: <br/> <br/>
|
||||
<input name="uploadedfile" type="file" accept=".bin" /><br />
|
||||
<input style="margin-top:3em" type="submit" value="Upload File" />
|
||||
</form>
|
||||
<script src="/navigation.js"></script>
|
||||
</body>
|
||||
|
||||
@@ -1,6 +1,5 @@
|
||||
<!DOCTYPE html>
|
||||
<html>
|
||||
<script src="/ports.js"></script>
|
||||
<script src="/main.js"></script>
|
||||
<link rel="stylesheet" href="style.css">
|
||||
<title>FreeSwitchOS VLAN Configuration</title>
|
||||
@@ -24,6 +23,8 @@
|
||||
<div id="tPorts"><button type="button" style="transform: translateY(-100%);margin: 0 50px 0 0" onclick="utClicked(true);">Select all</button></div>
|
||||
<h2>Untagged Ports</h2>
|
||||
<div id="uPorts"><button type="button" style="transform: translateY(-100%); margin: 0 50px 0 0" onclick="utClicked(false);">Select all</button> </div>
|
||||
<h2>Use as default VLAN for incoming traffic (PVID)</h2>
|
||||
<div id="pPorts"><button type="button" style="transform: translateY(-100%); margin: 0 50px 0 0" onclick="pvClicked(true);">Select all</button> </div>
|
||||
<script src="/vlan.js"></script>
|
||||
<br/> <input style="width:40%;" class="action" id="vlan_sub" onclick="vlanSub();" type="button" value="Update / Create">
|
||||
<script src="/vlan_sub.js"></script>
|
||||
|
||||
@@ -6,6 +6,7 @@ function vlanForm() {
|
||||
clearInterval(vlanInterval);
|
||||
var t = document.getElementById('tPorts');
|
||||
var u = document.getElementById('uPorts');
|
||||
var p = document.getElementById('pPorts');
|
||||
for (let i = 1; i <= numPorts; i++) {
|
||||
const d = document.createElement("div");
|
||||
d.classList.add("cbgroup");
|
||||
@@ -29,11 +30,20 @@ function vlanForm() {
|
||||
d2.children[0].children[0].id = "uport" + i;
|
||||
d2.children[0].children[0].setAttribute('onclick', `setC("t", ${i}, false);`);
|
||||
u.appendChild(d2);
|
||||
var d3=d.cloneNode(true);
|
||||
d3.children[0].children[0].id = "pport" + i;
|
||||
d3.children[0].children[0].removeAttribute('onclick');
|
||||
p.appendChild(d3);
|
||||
}
|
||||
}
|
||||
|
||||
function setC(t, p, c){
|
||||
document.getElementById(t+'port'+p).checked=c;
|
||||
// When a tagged port is checked, automatically select the PVID port as well
|
||||
const tportElem = document.getElementById('tport'+p);
|
||||
if (tportElem && tportElem.checked) {
|
||||
document.getElementById('pport'+p).checked=true;
|
||||
}
|
||||
}
|
||||
|
||||
function utClicked(t){
|
||||
@@ -42,6 +52,12 @@ function utClicked(t){
|
||||
}
|
||||
}
|
||||
|
||||
function pvClicked(p){
|
||||
for (let i = 1; i <= numPorts; i++) {
|
||||
setC('p', i, p);
|
||||
}
|
||||
}
|
||||
|
||||
window.addEventListener("load", function() {
|
||||
vlanInterval = setInterval(vlanForm, 100);
|
||||
});
|
||||
@@ -55,8 +71,9 @@ function fetchVLAN() {
|
||||
m = parseInt(s.members, 16);
|
||||
document.getElementById('vname').value = s.name;
|
||||
for (let i = 1; i <= numPorts; i++) {
|
||||
setC('t', i, (m>>(10+i-1))&1);
|
||||
setC('u', i, (m>>(i-1))&1);
|
||||
setC('t', i, (m>>(10+i-1))&1);
|
||||
setC('u', i, (m>>(i-1))&1);
|
||||
setC('p', i, (m>>(20+i-1))&1);
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
async function vlanSub() {
|
||||
var commands = [];
|
||||
var cmd = "vlan ";
|
||||
var v=document.getElementById('vid').value
|
||||
if (!v) {
|
||||
@@ -14,12 +15,19 @@ async function vlanSub() {
|
||||
else if (document.getElementById('uport' + i).checked)
|
||||
cmd = cmd + ` ${i}`;
|
||||
}
|
||||
commands.push(cmd);
|
||||
for (let i = 1; i <= numPorts; i++) {
|
||||
if (document.getElementById('pport' + i).checked)
|
||||
commands.push(`pvid ${i} ${v}`);
|
||||
}
|
||||
try {
|
||||
const response = await fetch('/cmd', {
|
||||
method: 'POST',
|
||||
body: cmd
|
||||
});
|
||||
console.log('Completed!', response);
|
||||
for (let c of commands) {
|
||||
const response = await fetch('/cmd', {
|
||||
method: 'POST',
|
||||
body: c
|
||||
});
|
||||
console.log('Completed!', response);
|
||||
}
|
||||
} catch(err) {
|
||||
console.error(`Error: ${err}`);
|
||||
}
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
CC = sdcc
|
||||
CC_FLAGS = -mmcs51 -I. -I../uip
|
||||
CC_FLAGS = -mmcs51 -I. -I.. -I../uip
|
||||
ASM = sdas8051
|
||||
AFLAGS= -plosgff
|
||||
|
||||
|
||||
@@ -1,14 +1,18 @@
|
||||
|
||||
#include "httpd.h"
|
||||
#include "page_impl.h"
|
||||
#include "../rtl837x_common.h"
|
||||
#include "../cmd_parser.h"
|
||||
#include "../rtl837x_flash.h"
|
||||
#include "rtl837x_common.h"
|
||||
#include "rtl837x_regs.h"
|
||||
#include "cmd_parser.h"
|
||||
#include "rtl837x_flash.h"
|
||||
#include "uip.h"
|
||||
#include "../html_data.h"
|
||||
#include "html_data.h"
|
||||
|
||||
// Upload Firmware to 1M
|
||||
#define FIRMWARE_UPLOAD_START 0x100000
|
||||
// #define DEBUG
|
||||
#include "debug.h"
|
||||
|
||||
#define SESSION_ID_LENGTH 12
|
||||
#define SESSION_TIMEOUT 200
|
||||
|
||||
// SPI FLASH MEMORY PAGE SIZE.
|
||||
#define FLASHMEM_PAGE_SIZE 0x100
|
||||
@@ -18,12 +22,15 @@
|
||||
#pragma codeseg BANK1
|
||||
#pragma constseg BANK1
|
||||
|
||||
extern volatile __xdata uint8_t sfr_data[4];
|
||||
extern __code uint8_t * __code hex;
|
||||
extern __code struct f_data f_data[];
|
||||
extern __code char * __code mime_strings[];
|
||||
extern __xdata struct flash_region_t flash_region;
|
||||
extern __xdata uint32_t flash_size;
|
||||
|
||||
// Flash buffer to optimize flash writing speed, write_len is the current filling position
|
||||
extern __xdata uint8_t flash_buf[512];
|
||||
extern __xdata uint8_t flash_buf[FLASH_BUF_SIZE];
|
||||
__xdata uint32_t uptr; // Current flash write position
|
||||
__xdata uint16_t write_len;
|
||||
|
||||
@@ -31,18 +38,28 @@ __xdata uint8_t outbuf[TCP_OUTBUF_SIZE];
|
||||
__xdata uint8_t entry;
|
||||
__xdata uint16_t slen;
|
||||
__xdata uint16_t o_idx;
|
||||
__xdata uint16_t mpos;
|
||||
__xdata uint16_t len_left;
|
||||
__xdata uint16_t cont_len;
|
||||
__xdata uint32_t cont_addr;
|
||||
|
||||
// HTTP header properties
|
||||
__xdata uint8_t boundary[72];
|
||||
__xdata uint8_t *content_type = 0;
|
||||
__xdata uint8_t *session = 0;
|
||||
|
||||
// Global variables holding POST state
|
||||
__xdata uint16_t bindex; // Current index into the boundary
|
||||
|
||||
__xdata uint8_t verify_crc;
|
||||
__xdata uint32_t max_upload;
|
||||
__xdata uint16_t short_parsed;
|
||||
|
||||
__xdata char passwd[21];
|
||||
__xdata char session_id[SESSION_ID_LENGTH + 1];
|
||||
__xdata uint8_t authenticated;
|
||||
__xdata uint32_t now;
|
||||
__xdata uint8_t *timeptr;
|
||||
__xdata uint32_t last_session_use;
|
||||
|
||||
#define TSTATE_NONE 0
|
||||
#define TSTATE_TX 1
|
||||
#define TSTATE_ACKED 2
|
||||
@@ -161,6 +178,19 @@ void send_bad_request(void)
|
||||
}
|
||||
|
||||
|
||||
void send_to_login(void)
|
||||
{
|
||||
slen = strtox(outbuf, "HTTP/1.1 302 Found\r\n" \
|
||||
"Location: login.html\r\n\r\n");
|
||||
}
|
||||
|
||||
|
||||
void send_unauthorized(void)
|
||||
{
|
||||
slen = strtox(outbuf, "HTTP/1.1 401 Unauthorized\r\n\r\n");
|
||||
}
|
||||
|
||||
|
||||
__xdata uint8_t *skip_boundary(__xdata uint8_t *p)
|
||||
{
|
||||
while (*p) {
|
||||
@@ -175,16 +205,20 @@ __xdata uint8_t *skip_boundary(__xdata uint8_t *p)
|
||||
__xdata uint8_t *scan_header(__xdata uint8_t *p)
|
||||
{
|
||||
content_type = 0;
|
||||
session = 0;
|
||||
authenticated = 0;
|
||||
|
||||
while (*p != '\r' || *(p + 1) != '\n' || *(p + 2) != '\r' || *(p + 3) != '\n') {
|
||||
write_char(*p);
|
||||
dbg_char(*p);
|
||||
if (!*p++)
|
||||
break;
|
||||
if (is_word(p, "\nContent-Type:"))
|
||||
content_type = p + 15;
|
||||
else if (is_word(p, "\nCookie:"))
|
||||
session = p + 17;
|
||||
}
|
||||
if (content_type && is_word(content_type, "multipart/form-data; boundary")) {
|
||||
print_string("\nFound multiplart\n");
|
||||
dbg_string("\nFound multipart\n");
|
||||
content_type += 30;
|
||||
uint8_t i = 0;
|
||||
while (content_type[i] != '\r' && content_type[i] != '\n') {
|
||||
@@ -198,10 +232,37 @@ __xdata uint8_t *scan_header(__xdata uint8_t *p)
|
||||
boundary[3] = '-';
|
||||
boundary[i + 4] = 0;
|
||||
}
|
||||
|
||||
read_reg_timer(&now);
|
||||
|
||||
if (session) {
|
||||
if (now - last_session_use > SESSION_TIMEOUT) {
|
||||
dbg_string("Session expired\n");
|
||||
} else {
|
||||
if (is_word_x(session, session_id))
|
||||
authenticated = 1;
|
||||
else
|
||||
dbg_string("Invalid session cookie!\n");
|
||||
}
|
||||
}
|
||||
return p;
|
||||
}
|
||||
|
||||
|
||||
void gen_random_bytes(__xdata uint8_t *b, uint8_t bytes)
|
||||
{
|
||||
__xdata 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;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
* Reads post data from the http stream and writes it into flash memory
|
||||
* Input: the current position in the TCP buffer (uip_appdata)
|
||||
@@ -212,8 +273,8 @@ uint8_t stream_upload(uint16_t bptr)
|
||||
__xdata uint8_t *p = uip_appdata;
|
||||
__xdata struct httpd_state * __xdata s = &(uip_conn->appstate);
|
||||
|
||||
print_string("Stream_upload called: ");
|
||||
print_short(bptr); write_char('\n');
|
||||
dbg_string("Stream_upload called: ");
|
||||
dbg_short(bptr); dbg_char('\n');
|
||||
|
||||
do {
|
||||
if (bptr >= uip_len) {
|
||||
@@ -223,23 +284,35 @@ uint8_t stream_upload(uint16_t bptr)
|
||||
// Have we reached the end of the part?
|
||||
if (!boundary[bindex]) {
|
||||
s->tstate = TSTATE_NONE;
|
||||
print_string("len 2: "); print_short(write_len); write_char(' ');
|
||||
dbg_string("len 2: "); dbg_short(write_len); dbg_char(' ');
|
||||
flash_region.addr = uptr;
|
||||
flash_region.len = write_len;
|
||||
flash_write_bytes(flash_buf);
|
||||
uptr += write_len;
|
||||
write_len = 0;
|
||||
// TODO: This is a bit premature, what about a nice web-page saying the device will reset???
|
||||
print_string("CRC16: "); print_short(crc_final); write_char('\n');
|
||||
if (crc_final == 0xb001) {
|
||||
print_string("Checksum OK.");
|
||||
} else {
|
||||
print_string("Checksum incorrect!");
|
||||
if (verify_crc) {
|
||||
dbg_string("CRC16: "); dbg_short(crc_final); dbg_char('\n');
|
||||
if (crc_final == 0xb001) {
|
||||
print_string("Checksum OK.");
|
||||
} else {
|
||||
print_string("Checksum incorrect!");
|
||||
}
|
||||
print_string("\nUpload to flash done, will reset!\n");
|
||||
// close connection to avoid retries by browser
|
||||
uip_close();
|
||||
reset_chip();
|
||||
}
|
||||
print_string("Upload to flash done, will reset!\n");
|
||||
reset_chip();
|
||||
// Make sure there is a 0 at the end of the uploaded data
|
||||
flash_buf[0] = 0;
|
||||
flash_region.addr = uptr;
|
||||
flash_region.len = 1;
|
||||
flash_write_bytes(flash_buf);
|
||||
if (bptr >= uip_len)
|
||||
return 0;
|
||||
if(!verify_crc)
|
||||
//ugly hack to signal connection finished after config upload.
|
||||
uip_close();
|
||||
return 1;
|
||||
}
|
||||
if (p[bptr] == boundary[bindex]) {
|
||||
@@ -257,8 +330,8 @@ uint8_t stream_upload(uint16_t bptr)
|
||||
crc16(p + bptr);
|
||||
flash_buf[write_len++] = p[bptr++];
|
||||
if (write_len >= FLASHMEM_PAGE_SIZE) {
|
||||
print_string("len: "); print_short(write_len); write_char(' ');
|
||||
print_string("CRC16: "); print_short(crc_value); write_char('\n');
|
||||
dbg_string("len: "); dbg_short(write_len); dbg_char(' ');
|
||||
dbg_string("CRC16: "); dbg_short(crc_value); dbg_char('\n');
|
||||
flash_region.addr = uptr;
|
||||
flash_region.len = FLASHMEM_PAGE_SIZE;
|
||||
flash_write_bytes(flash_buf);
|
||||
@@ -282,7 +355,7 @@ void handle_post(void)
|
||||
__xdata uint8_t *p = uip_appdata;
|
||||
__xdata uint8_t *request_path = p + 6;
|
||||
|
||||
print_string("Is POST\n");
|
||||
dbg_string("Is POST\n");
|
||||
p += 5; // Skip post
|
||||
// Find end of request path
|
||||
while (*p && !is_separator(*p))
|
||||
@@ -292,9 +365,9 @@ void handle_post(void)
|
||||
// Find end of request header
|
||||
boundary[0] ='\0';
|
||||
p = scan_header(p);
|
||||
print_string("Boundary: >"); print_string_x(boundary); print_string("<\n");
|
||||
dbg_string("Boundary: >"); dbg_string_x(boundary); dbg_string("<\n");
|
||||
if (!*p || !content_type) {
|
||||
print_string("Bad Request!\n");
|
||||
dbg_string("Bad Request!\n");
|
||||
send_not_found();
|
||||
return;
|
||||
}
|
||||
@@ -302,15 +375,40 @@ void handle_post(void)
|
||||
if (is_word(request_path, "cmd")) {
|
||||
register uint8_t i = 0;
|
||||
p += 4;
|
||||
if (!authenticated) {
|
||||
send_unauthorized();
|
||||
return;
|
||||
}
|
||||
while (*p && *p != '\n' && *p != '\r')
|
||||
cmd_buffer[i++] = *p++;
|
||||
cmd_buffer[i] = '\0';
|
||||
if (i)
|
||||
cmd_available = 1;
|
||||
} else if (is_word(request_path, "upload")) {
|
||||
print_string("POST upload request\n");
|
||||
} else if (is_word(request_path, "login")) {
|
||||
dbg_string("POST login\n");
|
||||
p += 8; // Read also over "pwd="
|
||||
if (is_word_x(p, passwd)) {
|
||||
dbg_string("Password accepted!\n");
|
||||
read_reg_timer(&last_session_use);
|
||||
gen_random_bytes(session_id, SESSION_ID_LENGTH);
|
||||
session_id[SESSION_ID_LENGTH] = '\0';
|
||||
slen = strtox(outbuf, "HTTP/1.1 302 Found\r\nLocation: index.html\r\n" \
|
||||
"Set-Cookie: session=");
|
||||
for (register uint8_t i = 0; i < SESSION_ID_LENGTH; i++)
|
||||
outbuf[slen++] = session_id[i];
|
||||
slen += strtox(outbuf + slen, "; SameSite=Strict\r\n\r\n");
|
||||
} else {
|
||||
slen = strtox(outbuf, "HTTP/1.1 302 Found\r\nLocation: login.html\r\n\r\n");
|
||||
}
|
||||
return;
|
||||
} else if (is_word(request_path, "upload") || is_word(request_path, "config")) {
|
||||
dbg_string("POST upload/config request\n");
|
||||
if (!authenticated) {
|
||||
send_unauthorized();
|
||||
return;
|
||||
}
|
||||
if (!boundary[0]) {
|
||||
print_string("Bad request, no boundary!\n");
|
||||
dbg_string("Bad request, no boundary!\n");
|
||||
send_bad_request();
|
||||
return;
|
||||
}
|
||||
@@ -325,16 +423,37 @@ void handle_post(void)
|
||||
if (!content_type) // We are waiting for the part with the octet stream
|
||||
continue;
|
||||
} while (!is_word(content_type, "application/octet-stream"));
|
||||
print_string("Have content octets\n");
|
||||
dbg_string("Have content octets\n");
|
||||
p += 4; // Skip \r\n\r\n sequence at end of preamble of part
|
||||
|
||||
uptr = FIRMWARE_UPLOAD_START;
|
||||
if (is_word(request_path, "upload")) {
|
||||
if (flash_size < FIRMWARE_UPLOAD_START*2)
|
||||
{
|
||||
print_string("Flash too small for firmware upload!\n");
|
||||
send_bad_request();
|
||||
return;
|
||||
}
|
||||
print_string("Firmware upload started.");
|
||||
uptr = FIRMWARE_UPLOAD_START;
|
||||
verify_crc = 1;
|
||||
max_upload = 1024576;
|
||||
} else {
|
||||
dbg_string("Configuration upload, erasing config mem!\n");
|
||||
uptr = CONFIG_START;
|
||||
verify_crc = 0;
|
||||
max_upload = 2048;
|
||||
flash_region.addr = CONFIG_START;
|
||||
flash_sector_erase();
|
||||
}
|
||||
flash_init(0); // Re-initialize flash for non-DIO operation, otherwise flashing fails
|
||||
set_sys_led_state(SYS_LED_FAST);
|
||||
|
||||
crc_value = 0;
|
||||
bindex = 0;
|
||||
write_len = 0;
|
||||
stream_upload(p - uip_appdata);
|
||||
|
||||
print_string("Done reading first fragment\n");
|
||||
dbg_string("Done reading first fragment\n");
|
||||
return;
|
||||
|
||||
} else {
|
||||
@@ -353,26 +472,26 @@ void httpd_appcall(void)
|
||||
{
|
||||
__xdata struct httpd_state * __xdata s = &(uip_conn->appstate);
|
||||
|
||||
write_char('P');
|
||||
dbg_char('P');
|
||||
if(uip_connected() && s->tstate == TSTATE_CLOSED) {
|
||||
print_string("Connected...\n");
|
||||
dbg_string("Connected...\n");
|
||||
s->tstate = TSTATE_NONE;
|
||||
} else if (uip_closed()) {
|
||||
print_string("Connection closed\n");
|
||||
dbg_string("Connection closed\n");
|
||||
s->tstate = TSTATE_CLOSED;
|
||||
} else if (uip_aborted()) {
|
||||
print_string("Connection aborted\n");
|
||||
dbg_string("Connection aborted\n");
|
||||
uip_close();
|
||||
s->tstate = TSTATE_CLOSED;
|
||||
} else if (uip_poll()) {
|
||||
uip_len = 0;
|
||||
if (s->tstate == TSTATE_ACKED) {
|
||||
print_string("Closing because everything has been transmitted\n");
|
||||
dbg_string("Closing because everything has been transmitted\n");
|
||||
uip_close();
|
||||
s->tstate = TSTATE_CLOSED;
|
||||
}
|
||||
} else if (uip_acked() && s->tstate == TSTATE_TX) {
|
||||
print_string("ACK\n");
|
||||
dbg_string("ACK\n");
|
||||
if (slen > uip_mss()) {
|
||||
slen -= uip_mss();
|
||||
o_idx += uip_mss();
|
||||
@@ -384,26 +503,46 @@ void httpd_appcall(void)
|
||||
s->tstate = TSTATE_ACKED;
|
||||
|
||||
if (slen > uip_mss()) {
|
||||
print_string("Sending A: "); print_short(slen); write_char('\n');
|
||||
dbg_string("Sending A: "); dbg_short(slen); dbg_char('\n');
|
||||
uip_send(outbuf + o_idx, uip_mss());
|
||||
print_string("Sending A done\n");
|
||||
s->tstate = TSTATE_TX;
|
||||
} else if (slen > 0) {
|
||||
print_string("Sending B: "); print_short(slen); write_char('\n');
|
||||
dbg_string("Sending B: "); dbg_short(slen); dbg_char('\n');
|
||||
uip_send(outbuf + o_idx, slen);
|
||||
print_string("Sending B done\n");
|
||||
s->tstate = TSTATE_TX;
|
||||
} else if (cont_len) {
|
||||
dbg_string("CONT cont_len: "); dbg_short(cont_len);
|
||||
slen = cont_len > uip_mss() ? uip_mss() : cont_len;
|
||||
if (slen > TCP_OUTBUF_SIZE)
|
||||
slen = TCP_OUTBUF_SIZE;
|
||||
flash_region.addr = cont_addr;
|
||||
flash_region.len = slen;
|
||||
flash_read_bulk(outbuf);
|
||||
uip_send(outbuf, slen);
|
||||
cont_len -= slen;
|
||||
cont_addr += slen;
|
||||
s->tstate = TSTATE_TX;
|
||||
}
|
||||
} else if (uip_newdata() && s->tstate == TSTATE_POST) {
|
||||
stream_upload(0);
|
||||
// Check here maxupload by subtracting uip_len and close socekt if fails!
|
||||
if (max_upload - uip_len > 0) {
|
||||
stream_upload(0);
|
||||
write_char('.');
|
||||
} else {
|
||||
send_bad_request();
|
||||
goto do_send;
|
||||
}
|
||||
} else if (uip_newdata() && s->tstate != TSTATE_TX) {
|
||||
write_char('<'); print_short(uip_len); write_char('\n');
|
||||
cont_len = 0;
|
||||
dbg_char('<'); dbg_short(uip_len); dbg_char('\n');
|
||||
__xdata uint8_t *p = uip_appdata;
|
||||
// Mark end of request header with \0
|
||||
p[uip_len] = 0;
|
||||
#ifdef DEBUG
|
||||
while (*p)
|
||||
write_char(*p++);
|
||||
write_char('\n');
|
||||
dbg_char(*p++);
|
||||
dbg_char('\n');
|
||||
#endif
|
||||
p = uip_appdata;
|
||||
if (is_word(p, "POST")) {
|
||||
handle_post();
|
||||
@@ -416,20 +555,26 @@ void httpd_appcall(void)
|
||||
}
|
||||
|
||||
if (is_word(p, "GET"))
|
||||
print_string("GET request ");
|
||||
dbg_string("GET request ");
|
||||
p += 4;
|
||||
scan_header(p);
|
||||
__xdata uint8_t *q = p;
|
||||
while (!is_separator(*p))
|
||||
p++;
|
||||
*p = '\0';
|
||||
print_string_x(q);
|
||||
write_char('\n');
|
||||
dbg_string_x(q);
|
||||
dbg_char('\n');
|
||||
|
||||
s->tstate = TSTATE_NONE;
|
||||
entry = find_entry(q);
|
||||
print_string("Entry is: "); print_byte(entry); write_char('\n');
|
||||
dbg_string("Entry is: "); dbg_byte(entry); dbg_char('\n');
|
||||
if (entry == 0xff) {
|
||||
print_string("Not file entry\n");
|
||||
if (!authenticated) {
|
||||
dbg_string("Not authorized!\n");
|
||||
send_unauthorized();
|
||||
goto do_send;
|
||||
}
|
||||
dbg_string("Not file entry\n");
|
||||
if (!strcmp(q, "/status.json")) {
|
||||
send_status();
|
||||
} else if (!strcmp(q, "/information.json")) {
|
||||
@@ -438,50 +583,90 @@ void httpd_appcall(void)
|
||||
parse_short(q + 15);
|
||||
send_vlan(short_parsed);
|
||||
} else if (is_word(q, "/counters.json")) {
|
||||
send_counters(q[19]-'0');
|
||||
send_counters(q[20]-'0');
|
||||
} else if (is_word(q, "/eee.json")) {
|
||||
send_eee();
|
||||
} else if (is_word(q, "/bandwidth.json")) {
|
||||
send_bandwidth();
|
||||
} else if (is_word(q, "/l2.json")) {
|
||||
parse_short(q + 13); // e.g.: /l2.json?idx=10
|
||||
send_l2(short_parsed);
|
||||
} else if (is_word(q, "/l2_del.json")) {
|
||||
parse_short(q + 17);
|
||||
l2_delete(short_parsed);
|
||||
} else if (is_word(q, "/mirror.json")) {
|
||||
send_mirror();
|
||||
} else if (is_word(q, "/mtu.json")) {
|
||||
send_mtu();
|
||||
} else if (is_word(q, "/lag.json")) {
|
||||
send_lag();
|
||||
} else if (is_word(q, "/config")) {
|
||||
send_config();
|
||||
} else if (is_word(q, "/cmd_log")) {
|
||||
send_cmd_log();
|
||||
} else if (is_word(q, "/cmd_log_clear")) {
|
||||
clear_command_history();
|
||||
send_mtu(); // dummy response
|
||||
} else if (is_word(q, "/reset")) {
|
||||
uip_close();
|
||||
delay(1000); //wait for the close packet to be sent, otherwise the browser will retry
|
||||
reset_chip();
|
||||
} else {
|
||||
send_not_found();
|
||||
}
|
||||
} else {
|
||||
print_string("Have entry\n");
|
||||
dbg_string("Have entry, authenticated: "); dbg_byte(authenticated); dbg_char('\n');
|
||||
if (!authenticated && !(f_data[entry].start == FDATA_START_login_html
|
||||
|| f_data[entry].start == FDATA_START_port_svg
|
||||
|| f_data[entry].start == FDATA_START_sfp_svg
|
||||
|| f_data[entry].start == FDATA_START_style_css)) {
|
||||
send_to_login();
|
||||
goto do_send;
|
||||
}
|
||||
// A web-page is actively accessed, we can reset session time-out
|
||||
reg_read_m(RTL837X_REG_SEC_COUNTER);
|
||||
timeptr = (uint8_t*)&last_session_use; // last_session_use is Little endian
|
||||
timeptr[0] = sfr_data[3]; timeptr[1] = sfr_data[2]; timeptr[2] = sfr_data[1]; timeptr[3] = sfr_data[0];
|
||||
|
||||
slen = strtox(outbuf, "HTTP/1.1 200 OK\r\nContent-Type: ");
|
||||
slen += strtox(outbuf + slen, mime_strings[f_data[entry].mime]);
|
||||
slen += strtox(outbuf + slen, "\r\nCache-Control: max-age=2592000\r\n\r\n");
|
||||
len_left = f_data[entry].len;
|
||||
slen += strtox(outbuf + slen, "; charset=UTF-8\r\nCache-Control: max-age=60, must-revalidate\r\nAccess-Control-Allow-Origin: *\r\n\r\n");
|
||||
|
||||
print_string("MIME: "); print_string(mime_strings[f_data[entry].mime]); write_char('\n');
|
||||
len_left = f_data[entry].len;
|
||||
if (len_left > (TCP_OUTBUF_SIZE - slen)) {
|
||||
cont_len = len_left - (TCP_OUTBUF_SIZE - slen);
|
||||
len_left = TCP_OUTBUF_SIZE - slen;
|
||||
cont_addr = f_data[entry].start + len_left;
|
||||
}
|
||||
dbg_string("MIME: "); dbg_string(mime_strings[f_data[entry].mime]); dbg_char('\n');
|
||||
flash_region.addr = f_data[entry].start;
|
||||
flash_region.len = len_left;
|
||||
flash_read_bulk(outbuf + slen);
|
||||
slen += len_left;
|
||||
}
|
||||
do_send:
|
||||
print_string("slen: "); print_short(slen); write_char('\n');
|
||||
dbg_string("slen: "); dbg_short(slen); dbg_char('\n');
|
||||
o_idx = 0;
|
||||
if (slen > uip_mss()) {
|
||||
print_string("Sending a: "); print_short(slen); write_char('\n');
|
||||
dbg_string("Sending a: "); dbg_short(slen); dbg_char('\n');
|
||||
uip_send(outbuf + o_idx, uip_mss());
|
||||
print_string("Sending a done\n");
|
||||
dbg_string("Sending a done\n");
|
||||
} else {
|
||||
print_string("Sending b: "); print_short(slen); write_char('\n');
|
||||
dbg_string("Sending b: "); dbg_short(slen); dbg_char('\n');
|
||||
uip_send(outbuf + o_idx, slen);
|
||||
print_string("Sending b done\n");
|
||||
dbg_string("Sending b done\n");
|
||||
}
|
||||
s->tstate = TSTATE_TX;
|
||||
} else if (uip_rexmit()) { // Connection established, need to rexmit?
|
||||
print_string("RETRANSMIT requested\n");
|
||||
dbg_string("RETRANSMIT requested\n");
|
||||
if (slen > uip_mss()) {
|
||||
print_string("Sending C: "); print_short(slen); write_char('\n');
|
||||
dbg_string("Sending C: "); dbg_short(slen); dbg_char('\n');
|
||||
uip_send(outbuf + o_idx, uip_mss());
|
||||
print_string("Sending C done\n");
|
||||
dbg_string("Sending C done\n");
|
||||
} else if (slen > 0) {
|
||||
print_string("Sending D: "); print_short(slen); write_char('\n');
|
||||
dbg_string("Sending D: "); dbg_short(slen); dbg_char('\n');
|
||||
uip_send(outbuf + o_idx, slen);
|
||||
print_string("Sending D done\n");
|
||||
dbg_string("Sending D done\n");
|
||||
}
|
||||
s->tstate = TSTATE_TX;
|
||||
uip_len = 0;
|
||||
|
||||
@@ -1,50 +1,55 @@
|
||||
// #define REGDBG 1
|
||||
|
||||
#include "../rtl837x_sfr.h"
|
||||
#include "../rtl837x_common.h"
|
||||
#include "../rtl837x_regs.h"
|
||||
#include "../rtl837x_port.h"
|
||||
#include "rtl837x_sfr.h"
|
||||
#include "rtl837x_common.h"
|
||||
#include "rtl837x_regs.h"
|
||||
#include "rtl837x_port.h"
|
||||
#include "rtl837x_flash.h"
|
||||
#include "rtl837x_pins.h"
|
||||
#include "uip.h"
|
||||
#include "../html_data.h"
|
||||
#include "html_data.h"
|
||||
#include <stdint.h>
|
||||
#include "../phy.h"
|
||||
#include "dhcp.h"
|
||||
#include "phy.h"
|
||||
#include "version.h"
|
||||
#include "machine.h"
|
||||
#include "page_impl.h"
|
||||
|
||||
// #define DEBUG
|
||||
#include "debug.h"
|
||||
|
||||
|
||||
#define L2_MAX_TRANSFER 30
|
||||
|
||||
#pragma codeseg BANK1
|
||||
#pragma constseg BANK1
|
||||
|
||||
extern __code const struct machine machine;
|
||||
extern __xdata uint8_t outbuf[TCP_OUTBUF_SIZE];
|
||||
extern __xdata uint16_t slen;
|
||||
extern __xdata uint16_t cont_len;
|
||||
extern __xdata uint32_t cont_addr;
|
||||
extern __code uint8_t * __code hex;
|
||||
extern __xdata uip_ipaddr_t uip_hostaddr, uip_draddr, uip_netmask;
|
||||
extern __code struct uip_eth_addr uip_ethaddr;
|
||||
extern __code uint8_t log_to_phys_port[9];
|
||||
extern __code uint8_t phys_to_log_port[6];
|
||||
|
||||
extern __xdata uint8_t minPort;
|
||||
extern __xdata uint8_t maxPort;
|
||||
extern __xdata uint8_t nSFPPorts;
|
||||
extern __xdata uint8_t sfr_data[4];
|
||||
extern __xdata uint8_t cpuPort;
|
||||
extern __xdata uint8_t isRTL8373;
|
||||
extern __xdata uint8_t sfp_pins_last;
|
||||
extern __xdata uint8_t vlan_names[VLAN_NAMES_SIZE];
|
||||
|
||||
extern __xdata uint8_t cmd_history[CMD_HISTORY_SIZE];
|
||||
extern __xdata uint16_t cmd_history_ptr;
|
||||
|
||||
extern __xdata struct flash_region_t flash_region;
|
||||
|
||||
extern __xdata char sfp_module_vendor[2][17];
|
||||
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 struct dhcp_state dhcp_state;
|
||||
|
||||
__code uint8_t * __code HTTP_RESPONCE_JSON = "HTTP/1.1 200 OK\r\nContent-Type: application/json\r\n\r\n";
|
||||
|
||||
/* Convert only the lower nibble to ascii HEX char.
|
||||
For convenience the upper nibble is masked out.
|
||||
*/
|
||||
inline char itohex(uint8_t val) {
|
||||
// Ignore upper nibble for convenience.
|
||||
val &= 0x0f;
|
||||
val -= 10;
|
||||
|
||||
// 10 or above
|
||||
if ((int8_t)val >= 0)
|
||||
val += ('a' - '0' - 10);
|
||||
|
||||
return val + ('0' + 10);
|
||||
}
|
||||
__code uint8_t * __code HTTP_RESPONCE_TXT = "HTTP/1.1 200 OK\r\nContent-Type: text/plain\r\n\r\n";
|
||||
|
||||
// Convert uint8_t to ascii HEX char push on html-buffer.
|
||||
void charhex_to_html(char c)
|
||||
@@ -95,17 +100,21 @@ void itoa_html(uint8_t v)
|
||||
char_to_html('0' + (v % 10));
|
||||
}
|
||||
|
||||
void string_to_html(register char *s)
|
||||
{
|
||||
while (*s) char_to_html(*s++);
|
||||
}
|
||||
|
||||
uint16_t stat_content(void)
|
||||
{
|
||||
print_string("stat_content called\n");
|
||||
dbg_string("stat_content called\n");
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
uint16_t port_status(void)
|
||||
{
|
||||
print_string("port_status called\n");
|
||||
dbg_string("port_status called\n");
|
||||
return 0;
|
||||
}
|
||||
|
||||
@@ -143,10 +152,68 @@ void reg_to_html(register uint16_t reg)
|
||||
sfr_data_to_html();
|
||||
}
|
||||
|
||||
|
||||
void reg_to_html_long(register uint16_t reg)
|
||||
{
|
||||
reg_read_m(reg);
|
||||
byte_to_html(sfr_data[0]);
|
||||
byte_to_html(sfr_data[1]);
|
||||
byte_to_html(sfr_data[2]);
|
||||
byte_to_html(sfr_data[3]);
|
||||
}
|
||||
|
||||
|
||||
void send_sfp_info(uint8_t sfp)
|
||||
{
|
||||
// This loops over the Vendor-name, Vendor OUI, Vendor PN and Vendor rev ASCII fields
|
||||
for (uint8_t i = 20; i < 60; i++) {
|
||||
if (i >= 36 && i < 40) // Skip Non-ASCII codes
|
||||
continue;
|
||||
uint8_t c = sfp_read_reg(sfp, i);
|
||||
if (c && c != 0xa0) // a0 is the byte read from a non-existant I2C EEPROM
|
||||
char_to_html(c);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
void sfp_send_data(uint8_t slot, uint8_t reg, uint8_t len)
|
||||
{
|
||||
if (reg & 0x80) { // Configure SFP readings address (0x51) as I2C device address
|
||||
reg &= 0x7f;
|
||||
REG_WRITE(RTL837X_REG_I2C_CTRL, 0x00, 0x1 << (I2C_MEM_ADDR_WIDTH-16) | len & 0xf, 0x51 >> 5, (0x51 << 3) & 0xff);
|
||||
} else {
|
||||
REG_WRITE(RTL837X_REG_I2C_CTRL, 0x00, 0x1 << (I2C_MEM_ADDR_WIDTH-16) | len & 0xf, 0x50 >> 5, (0x50 << 3) & 0xff);
|
||||
}
|
||||
|
||||
reg_read_m(RTL837X_REG_I2C_CTRL);
|
||||
sfr_mask_data(1, 0xfc, i2c_bus_from_scl_pin(machine.sfp_port[slot].i2c.scl) << 5 | i2c_bus_from_sda_pin(machine.sfp_port[slot].i2c.sda) << 2);
|
||||
reg_write_m(RTL837X_REG_I2C_CTRL);
|
||||
|
||||
REG_WRITE(RTL837X_REG_I2C_IN, 0, 0, 0, reg);
|
||||
|
||||
// Execute I2C Read
|
||||
reg_bit_set(RTL837X_REG_I2C_CTRL, 0);
|
||||
|
||||
// Wait for execution to finish
|
||||
do {
|
||||
reg_read_m(RTL837X_REG_I2C_CTRL);
|
||||
} while (sfr_data[3] & 0x1);
|
||||
|
||||
for (uint8_t i = 0; i < len & 0xf; i++) {
|
||||
if (!(i & 0x3))
|
||||
reg_read_m(RTL837X_REG_I2C_OUT + (i >> 2));
|
||||
if (len & 0x80)
|
||||
char_to_html(sfr_data[3 - (i & 0x3)]);
|
||||
else
|
||||
byte_to_html(sfr_data[3 - (i & 0x3)]);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
void send_basic_info(void)
|
||||
{
|
||||
slen = strtox(outbuf, HTTP_RESPONCE_JSON);
|
||||
print_string("send_basic_info called\n");
|
||||
dbg_string("send_basic_info called\n");
|
||||
slen += strtox(outbuf + slen, "{\"ip_address\":\"");
|
||||
itoa_html(uip_hostaddr[0]); char_to_html('.');
|
||||
itoa_html(uip_hostaddr[0] >> 8); char_to_html('.');
|
||||
@@ -169,15 +236,37 @@ void send_basic_info(void)
|
||||
byte_to_html(uip_ethaddr.addr[3]); char_to_html(':');
|
||||
byte_to_html(uip_ethaddr.addr[4]); char_to_html(':');
|
||||
byte_to_html(uip_ethaddr.addr[5]);
|
||||
slen += strtox(outbuf + slen, "\",\"sw_ver\":\"v0.1-ge4c48586\",\"hw_ver\":\"SWGT024-V2.0\"}");
|
||||
// slen += strtox(outbuf + slen, "\"}");
|
||||
slen += strtox(outbuf + slen, "\",\"sw_ver\":\"");
|
||||
slen += strtox(outbuf + slen, VERSION_SW);
|
||||
slen += strtox(outbuf + slen, "\",\"build_date\":\"");
|
||||
slen += strtox(outbuf + slen, BUILD_DATE);
|
||||
slen += strtox(outbuf + slen, "\",\"hw_ver\":\"");
|
||||
slen += strtox(outbuf + slen, machine.machine_name);
|
||||
slen += strtox(outbuf + slen, "\",\"flash_size\":\"");
|
||||
string_to_html(get_flash_size_str());
|
||||
|
||||
slen += strtox(outbuf + slen, "\",\"sfp_slot_0\":\"");
|
||||
send_sfp_info(0);
|
||||
char_to_html('"');
|
||||
if (machine.n_sfp == 2) {
|
||||
slen += strtox(outbuf + slen, ",\"sfp_slot_1\":\"");
|
||||
send_sfp_info(1);
|
||||
char_to_html('"');
|
||||
}
|
||||
if (dhcp_state.state != DHCP_OFF && dhcp_state.state != DHCP_SERVER)
|
||||
slen += strtox(outbuf + slen, ",\"dhcp_client\":1,\"dhcp_server\":0");
|
||||
else if (dhcp_state.state == DHCP_SERVER)
|
||||
slen += strtox(outbuf + slen, ",\"dhcp_client\":0,\"dhcp_server\":1");
|
||||
else
|
||||
slen += strtox(outbuf + slen, ",\"dhcp_client\":0,\"dhcp_server\":0");
|
||||
char_to_html('}');
|
||||
}
|
||||
|
||||
|
||||
void send_vlan(uint16_t vlan)
|
||||
{
|
||||
slen = strtox(outbuf, HTTP_RESPONCE_JSON);
|
||||
print_string("sending VLAN\n");
|
||||
dbg_string("sending VLAN\n");
|
||||
//{"members":"0x00060011"}
|
||||
slen += strtox(outbuf + slen, "{\"members\":\"0x");
|
||||
vlan_get(vlan);
|
||||
@@ -185,7 +274,7 @@ void send_vlan(uint16_t vlan)
|
||||
slen += strtox(outbuf + slen, "\",\"name\":\"");
|
||||
__xdata uint16_t n = vlan_name(vlan);
|
||||
if (n== 0xffff) {
|
||||
print_string("VLAN has no name\n");
|
||||
dbg_string("VLAN has no name\n");
|
||||
} else {
|
||||
while(vlan_names[n] && vlan_names[n] != ' ')
|
||||
char_to_html(vlan_names[n++]);
|
||||
@@ -196,22 +285,163 @@ void send_vlan(uint16_t vlan)
|
||||
|
||||
void send_counters(char port)
|
||||
{
|
||||
print_string("send_counters called: "); print_byte(port); write_char('\n');
|
||||
dbg_string("send_counters called: "); dbg_byte(port); dbg_char('\n');
|
||||
slen = strtox(outbuf, HTTP_RESPONCE_JSON);
|
||||
print_string("sending counters\n");
|
||||
|
||||
port--;
|
||||
uint8_t i = isRTL8373 ? port - 1: phys_to_log_port[port];
|
||||
slen += strtox(outbuf + slen, "{\"portNum\":");
|
||||
itoa_html(i + 1);
|
||||
for (uint8_t j = 0; j < 0x3f; j++) {
|
||||
STAT_GET(j, i);
|
||||
slen += strtox(outbuf + slen, ",\"cnt_");
|
||||
itoa_html(j);
|
||||
slen += strtox(outbuf + slen, "\":\"0x");
|
||||
dbg_string("sending counters\n");
|
||||
dbg_byte(port);
|
||||
uint8_t i = machine.phys_to_log_port[port];
|
||||
slen += strtox(outbuf + slen, "[");
|
||||
for (uint8_t counter = 0; counter < 0x37; counter++) {
|
||||
STAT_GET(counter, i);
|
||||
slen += strtox(outbuf + slen, "\"0x");
|
||||
reg_to_html(RTL837X_STAT_V_HIGH);
|
||||
reg_to_html(RTL837X_STAT_V_LOW);
|
||||
reg_to_html_long(RTL837X_STAT_V_LOW);
|
||||
char_to_html('\"');
|
||||
if (counter != 0x36)
|
||||
char_to_html(',');
|
||||
}
|
||||
char_to_html(']');
|
||||
}
|
||||
|
||||
|
||||
void send_l2(uint16_t idx)
|
||||
{
|
||||
slen = strtox(outbuf, HTTP_RESPONCE_JSON);
|
||||
dbg_string("sending L2\n");
|
||||
dbg_short(idx);
|
||||
__xdata uint8_t entries_left = L2_MAX_TRANSFER;
|
||||
|
||||
do {
|
||||
reg_read_m(RTL837X_TBL_CTRL);
|
||||
} while (sfr_data[3] & TBL_EXECUTE);
|
||||
|
||||
/* The L2 table in the ASIC can hold up to 4096 (0x1000) entries, which
|
||||
* are accessed using an index. The index is the hash of the MAC address
|
||||
* and forwarding ID (basically VID). The hash-table is 4-way associative,
|
||||
* i.e. for a given hash value, 4 entries with that same hash can be stored
|
||||
* (i.e. the hash points to a bucket with up to 4 entries).
|
||||
* When the table or a hash bucket is full, further entries will lead to
|
||||
* L2 flooding.
|
||||
* To find all entries, we start with entry-index 0 and iteratively search for
|
||||
* the next entry (with the next higher index), until we arrive again at the first
|
||||
* entry. The indices are sorted, so if an entry has a smaller index than
|
||||
* the previous one, we know that we have wrapped around the entire table.
|
||||
*/
|
||||
__xdata uint16_t entry = idx & 0xfff;
|
||||
__xdata uint16_t first_entry = 0xffff; // An illegal entry index
|
||||
char_to_html('[');
|
||||
while (1) {
|
||||
entries_left--;
|
||||
uint8_t port = 0;
|
||||
reg_read_m(RTL837x_TBL_DATA_0);
|
||||
REG_WRITE(RTL837x_TBL_DATA_0, sfr_data[0], sfr_data[1] & 0xfc, sfr_data[2] | (TBL_LUTREAD_NEXT_L2UC << 6), sfr_data[3]);
|
||||
|
||||
REG_WRITE(RTL837X_TBL_CTRL, entry >> 8, entry, TBL_L2_UNICAST, TBL_EXECUTE);
|
||||
do {
|
||||
reg_read_m(RTL837X_TBL_CTRL);
|
||||
} while (sfr_data[3] & TBL_EXECUTE);
|
||||
|
||||
reg_read_m(RTL837x_L2_DATA_OUT_B);
|
||||
if ((sfr_data[0] & 0x20)) { // Check entry is valid
|
||||
// MAC
|
||||
slen += strtox(outbuf + slen, "{\"mac\":\"");
|
||||
byte_to_html(sfr_data[2]); char_to_html(':');
|
||||
byte_to_html(sfr_data[3]); char_to_html(':');
|
||||
port = (sfr_data[0] >> 6) & 0x3;
|
||||
reg_read_m(RTL837x_L2_DATA_OUT_A);
|
||||
byte_to_html(sfr_data[0]); char_to_html(':');
|
||||
byte_to_html(sfr_data[1]); char_to_html(':');
|
||||
byte_to_html(sfr_data[2]); char_to_html(':');
|
||||
byte_to_html(sfr_data[3]);
|
||||
|
||||
// VLAN
|
||||
slen += strtox(outbuf + slen, "\",\"vlan\":\"");
|
||||
reg_read_m(RTL837x_L2_DATA_OUT_B);
|
||||
charhex_to_html(sfr_data[0] & 0x0f);
|
||||
byte_to_html(sfr_data[1]);
|
||||
|
||||
// type
|
||||
reg_read_m(RTL837x_L2_DATA_OUT_C);
|
||||
if (sfr_data[2] & 0x1)
|
||||
slen += strtox(outbuf + slen, "\",\"type\":\"s\",\"port\":");
|
||||
else
|
||||
slen += strtox(outbuf + slen, "\",\"type\":\"l\",\"port\":");
|
||||
|
||||
port |= (sfr_data[3] & 0x3) << 2;
|
||||
itoa_html(port);
|
||||
|
||||
// Index
|
||||
reg_read_m(RTL837x_TBL_DATA_0);
|
||||
entry = (((uint16_t)sfr_data[2] & 0x0f) << 8) | sfr_data[3];
|
||||
slen += strtox(outbuf + slen, ",\"idx\":\"");
|
||||
byte_to_html(entry >> 8);
|
||||
byte_to_html(entry);
|
||||
char_to_html('"');
|
||||
char_to_html('}');
|
||||
entry += 1; // We want the next entry following after the current entry
|
||||
} else {
|
||||
reg_read_m(RTL837x_TBL_DATA_0);
|
||||
entry = (((uint16_t)sfr_data[2] & 0x0f) << 8) | sfr_data[3] + 1;
|
||||
}
|
||||
if (first_entry == 0xffff) {
|
||||
char_to_html(',');
|
||||
first_entry = entry;
|
||||
} else {
|
||||
if (first_entry == entry || !entries_left) {
|
||||
char_to_html(']');
|
||||
break;
|
||||
} else {
|
||||
char_to_html(',');
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
void l2_delete(uint16_t idx)
|
||||
{
|
||||
slen = strtox(outbuf, HTTP_RESPONCE_JSON);
|
||||
dbg_string("L2 DELETE\n");
|
||||
dbg_short(idx);
|
||||
__xdata uint8_t entries_left = L2_MAX_TRANSFER;
|
||||
|
||||
do {
|
||||
reg_read_m(RTL837X_TBL_CTRL);
|
||||
} while (sfr_data[3] & TBL_EXECUTE);
|
||||
slen += strtox(outbuf + slen, "{\"result\":");
|
||||
// First, search for the entry based on the index
|
||||
reg_read_m(RTL837x_TBL_DATA_0);
|
||||
REG_WRITE(RTL837x_TBL_DATA_0, sfr_data[0], sfr_data[1] & 0xfc, sfr_data[2] | (TBL_LUTREAD_NEXT_L2UC << 6), sfr_data[3]);
|
||||
|
||||
REG_WRITE(RTL837X_TBL_CTRL, (idx >> 8) & 0xf, idx, TBL_L2_UNICAST, TBL_EXECUTE);
|
||||
do {
|
||||
reg_read_m(RTL837X_TBL_CTRL);
|
||||
} while (sfr_data[3] & 0x1);
|
||||
reg_read_m(RTL837x_L2_DATA_OUT_B);
|
||||
if (!(sfr_data[0] & 0x20)) {
|
||||
char_to_html('0');
|
||||
} else {
|
||||
sfr_data[0] &= 0x3f; // Clear SPA
|
||||
reg_write_m(RTL837x_TBL_DATA_IN_B);
|
||||
|
||||
// Second half of MAC is copied
|
||||
reg_read_m(RTL837x_L2_DATA_OUT_A);
|
||||
reg_write_m(RTL837x_TBL_DATA_IN_A);
|
||||
|
||||
reg_read_m(RTL837x_L2_DATA_OUT_C);
|
||||
sfr_data[3] &= 0xc0; // Clear age, auth and second part of ports
|
||||
sfr_data[1] &= 0xfe; // Clear nosalearn
|
||||
reg_write_m(RTL837x_TBL_DATA_IN_C);
|
||||
|
||||
reg_read_m(RTL837x_TBL_DATA_0);
|
||||
REG_WRITE(RTL837x_TBL_DATA_0, sfr_data[0], sfr_data[1], TBL_L2_UNICAST, sfr_data[3]);
|
||||
|
||||
REG_WRITE(RTL837X_TBL_CTRL, idx >> 8, idx, TBL_L2_UNICAST, TBL_WRITE | TBL_EXECUTE);
|
||||
do {
|
||||
reg_read_m(RTL837X_TBL_CTRL);
|
||||
} while (sfr_data[3] & TBL_EXECUTE);
|
||||
|
||||
char_to_html('1');
|
||||
}
|
||||
char_to_html('}');
|
||||
}
|
||||
@@ -219,9 +449,8 @@ void send_counters(char port)
|
||||
|
||||
void send_mirror(void)
|
||||
{
|
||||
print_string("send_eee called\n");
|
||||
dbg_string("send_mirror called\n");
|
||||
slen = strtox(outbuf, HTTP_RESPONCE_JSON);
|
||||
print_string("sending EEE status\n");
|
||||
|
||||
reg_read_m(RTL837x_MIRROR_CTRL);
|
||||
uint8_t mPort = sfr_data[3];
|
||||
@@ -230,24 +459,21 @@ void send_mirror(void)
|
||||
} else {
|
||||
slen += strtox(outbuf + slen, "{\"enabled\":0,\"mPort\":");
|
||||
}
|
||||
if (!isRTL8373)
|
||||
itoa_html(log_to_phys_port[mPort >> 1]);
|
||||
else
|
||||
itoa_html((mPort >> 1) + 1);
|
||||
itoa_html(machine.log_to_phys_port[mPort >> 1]);
|
||||
|
||||
reg_read_m(RTL837x_MIRROR_CONF);
|
||||
uint16_t m = sfr_data[0];
|
||||
m = (m << 8) | sfr_data[1];
|
||||
slen += strtox(outbuf + slen, ",\"mirror_rx\":\"");
|
||||
for (uint8_t i = 0; i < 16; i++) {
|
||||
bool_to_html(m & 0x8000);
|
||||
bool_to_html(!!(m & 0x8000));
|
||||
m <<= 1;
|
||||
}
|
||||
m = sfr_data[2];
|
||||
m = (m << 8) | sfr_data[3];
|
||||
slen += strtox(outbuf + slen, "\",\"mirror_tx\":\"");
|
||||
for (uint8_t i = 0; i < 16; i++) {
|
||||
bool_to_html(m & 0x8000);
|
||||
bool_to_html(!!(m & 0x8000));
|
||||
m <<= 1;
|
||||
}
|
||||
char_to_html('\"');
|
||||
@@ -255,23 +481,46 @@ void send_mirror(void)
|
||||
}
|
||||
|
||||
|
||||
void send_lag(void)
|
||||
{
|
||||
dbg_string("send_lag called\n");
|
||||
slen = strtox(outbuf, HTTP_RESPONCE_JSON);
|
||||
|
||||
char_to_html('[');
|
||||
for (uint8_t l=0; l < 4; l++) {
|
||||
slen += strtox(outbuf + slen, "{\"lagNum\":");
|
||||
itoa_html(l);
|
||||
slen += strtox(outbuf + slen, ",\"members\":\"");
|
||||
reg_read_m(RTL837X_TRK_MBR_CTRL_BASE + (l << 2));
|
||||
uint16_t ports = ((uint16_t)sfr_data[2] << 8) | sfr_data[3];
|
||||
for (uint8_t i = 0; i < 16; i++) {
|
||||
bool_to_html(!!(ports & 0x8000));
|
||||
ports <<= 1;
|
||||
}
|
||||
slen += strtox(outbuf + slen, "\",\"hash\":\"");
|
||||
reg_read_m(RTL837X_TRK_HASH_CTRL_BASE + (l << 2));
|
||||
sfr_data_to_html();
|
||||
slen += strtox(outbuf + slen, "\"},");
|
||||
}
|
||||
slen -=1; // remove comma
|
||||
char_to_html(']');
|
||||
}
|
||||
|
||||
|
||||
void send_eee(void)
|
||||
{
|
||||
print_string("send_eee called\nsending EEE status\n");
|
||||
dbg_string("send_eee called\nsending EEE status\n");
|
||||
slen = strtox(outbuf, HTTP_RESPONCE_JSON);
|
||||
|
||||
reg_read_m(RTL8373_PHY_EEE_ABLTY);
|
||||
uint8_t eee_ablty = sfr_data[3];
|
||||
|
||||
char_to_html('[');
|
||||
for (uint8_t i = minPort; i <= maxPort; i++) {
|
||||
for (uint8_t i = machine.min_port; i <= machine.max_port; i++) {
|
||||
slen += strtox(outbuf + slen, "{\"portNum\":");
|
||||
if (!isRTL8373)
|
||||
itoa_html(log_to_phys_port[i]);
|
||||
else
|
||||
itoa_html(i + 1);
|
||||
itoa_html(machine.log_to_phys_port[i]);
|
||||
|
||||
if (IS_SFP(i)) {
|
||||
if (machine.is_sfp[i]) {
|
||||
slen += strtox(outbuf + slen, ",\"isSFP\":1");
|
||||
} else {
|
||||
slen += strtox(outbuf + slen, ",\"isSFP\":0,\"eee\":\"");
|
||||
@@ -299,7 +548,73 @@ void send_eee(void)
|
||||
bool_to_html(eee_ablty & (1 << i));
|
||||
}
|
||||
char_to_html('}');
|
||||
if (i < maxPort)
|
||||
if (i < machine.max_port)
|
||||
char_to_html(',');
|
||||
else
|
||||
char_to_html(']');
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
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++) {
|
||||
slen += strtox(outbuf + slen, "{\"portNum\":");
|
||||
itoa_html(machine.log_to_phys_port[i]);
|
||||
slen += strtox(outbuf + slen, ",\"iLimited\":");
|
||||
reg_read_m(RTL837X_IGBW_PORT_CTRL + i * 4);
|
||||
if (sfr_data[1] & 0x10)
|
||||
char_to_html('1');
|
||||
else
|
||||
char_to_html('0');
|
||||
slen += strtox(outbuf + slen, ",\"iBW\":\"");
|
||||
byte_to_html(sfr_data[1] & 0x0f);
|
||||
byte_to_html(sfr_data[2]);
|
||||
byte_to_html(sfr_data[3]);
|
||||
slen += strtox(outbuf + slen, "\",\"iFC\":");
|
||||
if (reg_bit_test(RTL837X_IGBW_PORT_FC_CTRL, i))
|
||||
char_to_html('1');
|
||||
else
|
||||
char_to_html('0');
|
||||
reg_read_m(RTL837X_EGBW_PORT_CTRL + i * 1024);
|
||||
slen += strtox(outbuf + slen, ",\"eLimited\":");
|
||||
if (sfr_data[1] & 0x10)
|
||||
char_to_html('1');
|
||||
else
|
||||
char_to_html('0');
|
||||
slen += strtox(outbuf + slen, ",\"eBW\":\"");
|
||||
byte_to_html(sfr_data[1] & 0x0f);
|
||||
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(',');
|
||||
else
|
||||
char_to_html(']');
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
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++) {
|
||||
slen += strtox(outbuf + slen, "{\"portNum\":");
|
||||
itoa_html(machine.log_to_phys_port[i]);
|
||||
slen += strtox(outbuf + slen, ",\"mtu\":\"0x");
|
||||
reg_read_m(RTL8373_REG_MAC_L2_PORT_MAX_LEN + ((uint16_t) i << 8));
|
||||
uint16_t mtu = SFR_DATA_U16 & 0x3fff;
|
||||
byte_to_html(mtu >> 8);
|
||||
byte_to_html(mtu & 0xff);
|
||||
char_to_html('"');
|
||||
char_to_html('}');
|
||||
if (i < machine.max_port)
|
||||
char_to_html(',');
|
||||
else
|
||||
char_to_html(']');
|
||||
@@ -310,41 +625,91 @@ void send_eee(void)
|
||||
void send_status(void)
|
||||
{
|
||||
slen = strtox(outbuf, HTTP_RESPONCE_JSON);
|
||||
print_string("sending status\n");
|
||||
dbg_string("sending status\n");
|
||||
char_to_html('[');
|
||||
|
||||
for (uint8_t i = minPort; i <= maxPort; i++) {
|
||||
for (uint8_t i = machine.min_port; i <= machine.max_port; i++) {
|
||||
slen += strtox(outbuf + slen, "{\"portNum\":");
|
||||
if (!isRTL8373)
|
||||
itoa_html(log_to_phys_port[i]);
|
||||
else
|
||||
itoa_html(i + 1);
|
||||
itoa_html(machine.log_to_phys_port[i]);
|
||||
slen += strtox(outbuf + slen, ",\"logPort\":");
|
||||
itoa_html(i);
|
||||
|
||||
if (IS_SFP(i)) {
|
||||
slen += strtox(outbuf + slen, ",\"isSFP\":1,\"enabled\":");
|
||||
bool_to_html(!((sfp_pins_last >> (i == maxPort ? 0 : 4)) & 1));
|
||||
|
||||
slen += strtox(outbuf + slen, ",\"link\":");
|
||||
uint8_t rate = sfp_read_reg(i == maxPort ? 0 : 1, 12);
|
||||
if (rate == 0xd)
|
||||
char_to_html('2'); // 1000BX
|
||||
else if (rate == 0x1f)
|
||||
char_to_html('5'); // 2G5
|
||||
else if (rate > 0x65 && rate < 0x70)
|
||||
char_to_html('4'); // 10G "4" is not a valid value for port LINK speed
|
||||
else
|
||||
char_to_html('1'); // 100M ???
|
||||
if (machine.is_sfp[i]) {
|
||||
slen += strtox(outbuf + slen, ",\"isSFP\":1,\"enabled\":");
|
||||
if (!(sfp_pins_last & (0x1 << ((machine.is_sfp[i] - 1) << 2)))) {
|
||||
bool_to_html(1);
|
||||
slen += strtox(outbuf + slen,",\"sfp_options\":\"0x");
|
||||
byte_to_html(sfp_options[machine.is_sfp[i]-1]);
|
||||
if (sfp_options[machine.is_sfp[i]-1] & 0x40) {
|
||||
sfp_send_data(machine.is_sfp[i] - 1, 92, 1);
|
||||
slen += strtox(outbuf + slen,"\",\"sfp_temp\":\"0x");
|
||||
sfp_send_data(machine.is_sfp[i] - 1, 224, 2);
|
||||
slen += strtox(outbuf + slen,"\",\"sfp_vcc\":\"0x");
|
||||
sfp_send_data(machine.is_sfp[i] - 1, 226, 2);
|
||||
slen += strtox(outbuf + slen,"\",\"sfp_txbias\":\"0x");
|
||||
sfp_send_data(machine.is_sfp[i] - 1, 228, 2);
|
||||
slen += strtox(outbuf + slen,"\",\"sfp_txpower\":\"0x");
|
||||
sfp_send_data(machine.is_sfp[i] - 1, 230, 2);
|
||||
slen += strtox(outbuf + slen,"\",\"sfp_rxpower\":\"0x");
|
||||
sfp_send_data(machine.is_sfp[i] - 1, 232, 2);
|
||||
slen += strtox(outbuf + slen,"\",\"sfp_state\":\"0x");
|
||||
sfp_send_data(machine.is_sfp[i] - 1, 238, 1);
|
||||
}
|
||||
slen += strtox(outbuf + slen,"\",\"sfp_vendor\":\"");
|
||||
for (register uint8_t s = 0; s < 16; s++)
|
||||
outbuf[slen++] = sfp_module_vendor[machine.is_sfp[i]-1][s];
|
||||
slen += strtox(outbuf + slen,"\",\"sfp_model\":\"");
|
||||
for (register uint8_t s = 0; s < 16; s++)
|
||||
outbuf[slen++] = sfp_module_model[machine.is_sfp[i]-1][s];
|
||||
slen += strtox(outbuf + slen,"\",\"sfp_serial\":\"");
|
||||
for (register uint8_t s = 0; s < 16; s++)
|
||||
outbuf[slen++] = sfp_module_serial[machine.is_sfp[i]-1][s];
|
||||
char_to_html('"');
|
||||
} else {
|
||||
bool_to_html(0);
|
||||
}
|
||||
} else {
|
||||
slen += strtox(outbuf + slen, ",\"isSFP\":0,\"enabled\":");
|
||||
phy_read(i, 0x1f, 0xa610);
|
||||
slen += strtox(outbuf + slen, ",\"isSFP\":0,\"enabled\":");
|
||||
phy_read(i, PHY_MMD31, 0xa610);
|
||||
bool_to_html(SFR_DATA_8 == 0x20);
|
||||
|
||||
slen += strtox(outbuf + slen, ",\"link\":");
|
||||
reg_read_m(RTL837X_REG_LINKS);
|
||||
uint8_t b = sfr_data[3 - (i >> 1)];
|
||||
b = (i & 1) ? b >> 4 : b & 0xf;
|
||||
char_to_html('0' + b);
|
||||
slen += strtox(outbuf + slen, ",\"adv\":\"");
|
||||
phy_read(i, PHY_MMD_AN, PHY_ANEG_MGBASE_CTRL);
|
||||
uint16_t w = SFR_DATA_U16;
|
||||
bool_to_html(!!(w & 0x80)); // 2500BaseN-Full
|
||||
phy_read(i, PHY_MMD31, PHY_MMD31_GBCR);
|
||||
w = SFR_DATA_U16;
|
||||
bool_to_html(!!(w & 0x0200)); // 1000Base-Full
|
||||
phy_read(i, PHY_MMD_AN, PHY_ANEG_ADV);
|
||||
w = SFR_DATA_U16;
|
||||
bool_to_html(!!(w & 0x0100)); // 100Base-Full
|
||||
bool_to_html(!!(w & 0x80)); // 100Base-Half
|
||||
bool_to_html(!!(w & 0x40)); // 10Base-Full
|
||||
bool_to_html(!!(w & 0x20)); // 10Base-Half
|
||||
char_to_html('"');
|
||||
}
|
||||
|
||||
slen += strtox(outbuf + slen, ",\"link\":");
|
||||
|
||||
uint8_t b = 0;
|
||||
// Determine link state
|
||||
reg_read_m(RTL837X_REG_LINKS_STS);
|
||||
if(!((sfr_data[(i / 8) + 1] >> (i % 8 ) & 1)))
|
||||
{
|
||||
b = 0; //link down
|
||||
}
|
||||
else
|
||||
{
|
||||
//Determine link speed
|
||||
if (i < 8)
|
||||
reg_read_m(RTL837X_REG_LINKS);
|
||||
else
|
||||
reg_read_m(RTL837X_REG_LINKS_89);
|
||||
|
||||
b = sfr_data[3 - ((i & 7) >> 1)];
|
||||
b = ((i & 1) ? b >> 4 : b & 0xf) + 1;
|
||||
}
|
||||
char_to_html('0' + b);
|
||||
|
||||
STAT_GET(STAT_COUNTER_TX_PKTS, i);
|
||||
slen += strtox(outbuf + slen, ",\"txG\":\"0x");
|
||||
reg_to_html(RTL837X_STAT_V_HIGH);
|
||||
@@ -363,9 +728,71 @@ void send_status(void)
|
||||
STAT_GET(STAT_COUNTER_ERR_PKTS, i);
|
||||
reg_to_html(RTL837X_STAT_V_HIGH); // 32bit RX packet errors
|
||||
slen += strtox(outbuf + slen, "\"}");
|
||||
if (i < maxPort)
|
||||
if (i < machine.max_port)
|
||||
char_to_html(',');
|
||||
else
|
||||
char_to_html(']');
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
void send_config(void)
|
||||
{
|
||||
dbg_string("send_config called\n");
|
||||
__xdata uint32_t pos = CONFIG_START;
|
||||
__xdata uint16_t valid_len = 0;
|
||||
__xdata uint16_t len_left = CONFIG_LEN;
|
||||
__xdata uint8_t flash_buf[256];
|
||||
|
||||
slen = strtox(outbuf, HTTP_RESPONCE_TXT);
|
||||
|
||||
// Scan through config to find the end of valid data (null terminator)
|
||||
do {
|
||||
flash_region.addr = pos;
|
||||
flash_region.len = (len_left > 256) ? 256 : len_left;
|
||||
flash_read_bulk(flash_buf);
|
||||
|
||||
// Look for null terminator in this chunk
|
||||
for (uint16_t i = 0; i < flash_region.len; i++) {
|
||||
if (flash_buf[i] == 0) {
|
||||
// Found end of valid data
|
||||
valid_len += i;
|
||||
goto found_end;
|
||||
}
|
||||
}
|
||||
|
||||
valid_len += flash_region.len;
|
||||
len_left -= flash_region.len;
|
||||
pos += flash_region.len;
|
||||
} while (len_left > 0);
|
||||
|
||||
found_end:
|
||||
// Now send the valid data
|
||||
if (valid_len > (TCP_OUTBUF_SIZE - slen)) {
|
||||
cont_len = valid_len - (TCP_OUTBUF_SIZE - slen);
|
||||
valid_len = TCP_OUTBUF_SIZE - slen;
|
||||
cont_addr = valid_len;
|
||||
}
|
||||
|
||||
flash_region.addr = CONFIG_START;
|
||||
flash_region.len = valid_len;
|
||||
flash_read_bulk(outbuf + slen);
|
||||
slen += valid_len;
|
||||
}
|
||||
|
||||
void send_cmd_log(void)
|
||||
{
|
||||
dbg_string("send_cmd_log called\n");
|
||||
slen = strtox(outbuf, HTTP_RESPONCE_TXT);
|
||||
__xdata uint16_t p = (cmd_history_ptr + 1) & CMD_HISTORY_MASK;
|
||||
__xdata uint8_t found_begin = 0;
|
||||
dbg_string("History ptr: ");
|
||||
dbg_short(cmd_history_ptr); dbg_char('\n');
|
||||
while (p != cmd_history_ptr) {
|
||||
if (!cmd_history[p] || cmd_history[p] == '\n')
|
||||
found_begin = 1;
|
||||
if (found_begin && cmd_history[p])
|
||||
outbuf[slen++] = cmd_history[p];
|
||||
p = (p + 1) & CMD_HISTORY_MASK;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -3,9 +3,31 @@
|
||||
|
||||
void send_counters(char port);
|
||||
void send_status(void);
|
||||
void send_vlan(register uint16_t vlan);
|
||||
void send_vlan(uint16_t vlan);
|
||||
void send_basic_info(void);
|
||||
void send_bandwidth(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_config(void);
|
||||
void send_cmd_log(void);
|
||||
void send_lag(void);
|
||||
|
||||
/* Convert only the lower nibble to ascii HEX char.
|
||||
For convenience the upper nibble is masked out.
|
||||
*/
|
||||
inline char itohex(uint8_t val) {
|
||||
// Ignore upper nibble for convenience.
|
||||
val &= 0x0f;
|
||||
val -= 10;
|
||||
|
||||
// 10 or above
|
||||
if ((int8_t)val >= 0)
|
||||
val += ('a' - '0' - 10);
|
||||
|
||||
return val + ('0' + 10);
|
||||
}
|
||||
|
||||
#endif
|
||||
|
||||
@@ -11,7 +11,7 @@
|
||||
#include "../rtl837x_sfr.h"
|
||||
#include "../rtl837x_regs.h"
|
||||
|
||||
#define SYS_TICK_HZ 100
|
||||
// See setup_serial_timer1() for valid baudrate settings!
|
||||
#define SERIAL_BAUD_RATE 57600
|
||||
#define CLOCK_HZ 125000000
|
||||
|
||||
@@ -26,10 +26,6 @@
|
||||
#define CLOCK_DIV 0
|
||||
#endif
|
||||
|
||||
volatile __xdata uint32_t ticks;
|
||||
volatile __xdata uint8_t sec_counter;
|
||||
volatile __xdata uint16_t sleep_ticks;
|
||||
|
||||
// We buffer 1 sector as this is also the erase size
|
||||
__xdata uint8_t buffer[0x1000];
|
||||
__xdata uint8_t dio_enabled;
|
||||
@@ -38,16 +34,6 @@ __code uint8_t * __code hex = "0123456789abcdef";
|
||||
|
||||
void isr_timer0(void) __interrupt(1)
|
||||
{
|
||||
TR0 = 0; // Stop timer 0
|
||||
TH0 = (0x10000 - (CLOCK_HZ / SYS_TICK_HZ / 32)) >> 8;
|
||||
TL0 = (0x10000 - (CLOCK_HZ / SYS_TICK_HZ / 32)) % 0xff;
|
||||
|
||||
ticks++;
|
||||
if (sleep_ticks > 0)
|
||||
sleep_ticks--;
|
||||
sec_counter++;
|
||||
|
||||
TR0 = 1; // Re-start timer 0
|
||||
}
|
||||
|
||||
|
||||
@@ -102,25 +88,52 @@ void print_short(uint16_t a)
|
||||
}
|
||||
}
|
||||
|
||||
void setup_serial(void)
|
||||
/* Set up serial port 0 using Timer 1 as baudrate generator.
|
||||
* For x Bd these settings are needed, see table below.
|
||||
* NOTE: Settings only valid for F_SYS = 125 MHz!
|
||||
* | Wanted | | TMR | F_SYS | | Actual | |
|
||||
* | baudrate | SMOD0 | DIV | DIV | TH1 | baudrate | Error |
|
||||
* | -------- | ----- | --- | ----- | ---- | -------- | ------ |
|
||||
* | 1200 | 0 | 12 | 255 | 0x01 | 1276.6 | 6.00% |
|
||||
* | 2400 | 0 | 12 | 136 | 0x78 | 2393.5 | −0.27% |
|
||||
* | 4800 | 0 | 4 | 203 | 0x35 | 4810.7 | 0.22% |
|
||||
* | 9600 | 1 | 4 | 203 | 0x35 | 9621.3 | 0.22% |
|
||||
* | 14400 | 1 | 4 | 136 | 0x78 | 14361.2 | −0.27% |
|
||||
* | 19200 | 1 | 4 | 102 | 0x9a | 19148.3 | −0.27% |
|
||||
* | 38400 | 1 | 4 | 51 | 0xcd | 38296.6 | −0.27% |
|
||||
* | 57600 | 1 | 4 | 34 | 0xde | 57444.9 | −0.27% |
|
||||
* | 115200 | 1 | 4 | 17 | 0xef | 114889.7 | −0.27% |
|
||||
*/
|
||||
#if CLOCK_HZ != 125000000
|
||||
#warning "SERIAL 0 baudrate setting may only valid for F_CPU = 125 MHz!"
|
||||
#endif
|
||||
void setup_serial_timer1(void)
|
||||
{
|
||||
IE = 0;
|
||||
// Timer 1: Mode 2: automatic reload
|
||||
TMOD &= 0x0F;
|
||||
TMOD |= 0x20; // Timer1: Mode2: Timer, 8-bit with auto-reload
|
||||
CKCON |= 0x10; // Timer1 clock divider: F_SYS / 4: T2M = 1, Timer 1 uses clk/4
|
||||
|
||||
T2CON = 0x34; // Enable RCLK/TCLK (serial transmit/receive clock for T2), TR2 (Timer 2 RUN), disable CP/RL2 (bit 0)
|
||||
SCON = 0x50; // Mode = 1: ASYNC 8N1 with T2 as baud-rate generator, REN_0 Receive enable
|
||||
PCON |= 0x80; // SMOD0 = 1; Double the Baud Rate, don't divide Timer 1 Overflag signal.
|
||||
|
||||
// The RCAP2 registers contain the high/low byte that is loaded into
|
||||
// timer2 when T2 overflows to 0x10000
|
||||
RCAP2H = (0x10000 - (CLOCK_HZ / SERIAL_BAUD_RATE / 32)) >> 8;
|
||||
RCAP2L = (0x10000 - (CLOCK_HZ / SERIAL_BAUD_RATE / 32)) % 0xff;
|
||||
SCON = 0x50; // Mode = 1: ASYNC 8N1 with Timer 2 as baud-rate generator, REN_0 Receive enable
|
||||
|
||||
PCON |= 0x80; // Double the Baud Rate
|
||||
/* The TH1 register contain the reload value, timer1 when T1 overflows to 0x100.
|
||||
* NOTE: compiler computs the wrong value. 0xF0 is calculated but 0xEF is the right value for 115200.
|
||||
* Also https://www.keil.com/products/c51/baudrate.asp confirms this.
|
||||
* Added 32 before div by 64 to make sure rounding is correct so that the results are right.
|
||||
*
|
||||
* TH1 = 0x100 - (2^SMOD0 * F_SYS) / ( TMR1_DIV / BAUDRATE * 32)
|
||||
*/
|
||||
TH1 = (0x100 - (((CLOCK_HZ / SERIAL_BAUD_RATE) + 32) / (4 * 16))) & 0xff;
|
||||
|
||||
SCON = 0x50;
|
||||
TI = 1;
|
||||
RI = 0;
|
||||
TCON |= 0x40; // Start timer 1
|
||||
|
||||
ES = 1; // Enable serial IRQ
|
||||
ET1 = 0; // Timer1 Interrupt is NOT wanted!
|
||||
TI = 1; // Set TI-interrupt/flag, to flag that the TX-buf is empty.
|
||||
RI = 0; // Clear RI-interrupt flag
|
||||
|
||||
ES = 0; // Disable serial IRQ, software is only printing data and just polls TI-flag.
|
||||
}
|
||||
|
||||
/*
|
||||
@@ -345,10 +358,7 @@ void installer(void)
|
||||
IE = 0;
|
||||
EIE = 0; // SFR e8: EIE. Disable all external IRQs
|
||||
|
||||
// Disable all interrupts (global interrupt enable bit)
|
||||
EA = 0; // SFR A8.7 / IE.7
|
||||
|
||||
setup_serial();
|
||||
setup_serial_timer1();
|
||||
print_string("\nRTLPlayground installer starting...\n");
|
||||
|
||||
// Initialize flash functions with disable DIO because writing does not work otherwise
|
||||
|
||||
@@ -0,0 +1,397 @@
|
||||
#include "machine.h"
|
||||
#include "rtl837x_pins.h"
|
||||
#include "rtl837x_leds.h"
|
||||
#include "rtl837x_regs.h"
|
||||
#include "rtl837x_common.h"
|
||||
|
||||
#ifdef MACHINE_KP_9000_6XHML_X2
|
||||
__code const struct machine machine = {
|
||||
.machine_name = "keepLink KP-9000-6XHML-X2",
|
||||
.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 = GPIO50_I2C_SCL2_UART1_TX,
|
||||
.sfp_port[0].pin_los = GPIO10_LED10,
|
||||
.sfp_port[0].pin_tx_disable = GPIO_NA,
|
||||
.sfp_port[0].sds = 0,
|
||||
.sfp_port[0].i2c = { .sda = GPIO41_I2C_SDA3_MDIO1, .scl = GPIO40_I2C_SCL3_MDC1 },
|
||||
// Right SFP port (6)
|
||||
.sfp_port[1].pin_detect = GPIO30_ACL_BIT3_EN,
|
||||
.sfp_port[1].pin_los = GPIO37,
|
||||
.sfp_port[1].pin_tx_disable = GPIO_NA,
|
||||
.sfp_port[1].sds = 1,
|
||||
.sfp_port[1].i2c = { .sda = GPIO39_I2C_SDA4, .scl = GPIO40_I2C_SCL3_MDC1 },
|
||||
.reset_pin = GPIO54_ACL_BIT2_EN,
|
||||
.high_leds = { .mux = LED_27 | LED_29, .enable = LED_28 | LED_29 },
|
||||
.port_led_set = { 0, 0, 0, 0, 0, 0, 0, 0, 0},
|
||||
/* Conditions for LED on:
|
||||
* dual led orange: ledset_0 & ledset_2
|
||||
* dual led green: ledset_2 & !ledset_0
|
||||
* single right led green: ledset_0 & !ledset_1
|
||||
*/
|
||||
.led_sets = { { LEDS_2G5 | LEDS_1G | LEDS_100M | LEDS_10M | LEDS_LINK | LEDS_ACT | LEDS_10G,
|
||||
LEDS_2G5 | LEDS_LINK | LEDS_10G,
|
||||
LEDS_1G | LEDS_LINK,
|
||||
0 },
|
||||
},
|
||||
};
|
||||
|
||||
void machine_custom_init(void) { }
|
||||
|
||||
#elif defined MACHINE_KP_9000_6XH_X
|
||||
__code const struct machine machine = {
|
||||
.machine_name = "keepLink KP-9000-6XH-X",
|
||||
.isRTL8373 = 0,
|
||||
.min_port = 3,
|
||||
.max_port = 8,
|
||||
.n_sfp = 1,
|
||||
.log_to_phys_port = {0, 0, 0, 5, 1, 2, 3, 4, 6},
|
||||
.phys_to_log_port = {4, 5, 6, 7, 3, 8, 0, 0, 0},
|
||||
.is_sfp = {0, 0, 0, 0, 0, 0, 0, 0, 1},
|
||||
.sfp_port[0].pin_detect = GPIO30_ACL_BIT3_EN,
|
||||
.sfp_port[0].pin_los = GPIO37,
|
||||
.sfp_port[0].pin_tx_disable = GPIO_NA,
|
||||
.sfp_port[0].sds = 1,
|
||||
.sfp_port[0].i2c = { .sda = GPIO39_I2C_SDA4, .scl = GPIO40_I2C_SCL3_MDC1 },
|
||||
.reset_pin = GPIO_NA,
|
||||
/* Conditions for LED on:
|
||||
* dual led orange: ledset_0 & ledset_2
|
||||
* dual led green: ledset_2 & !ledset_0
|
||||
* single right led green: ledset_0 & !ledset_1
|
||||
*/
|
||||
.led_sets = { { LEDS_2G5 | LEDS_1G | LEDS_100M | LEDS_10M | LEDS_LINK | LEDS_ACT | LEDS_10G,
|
||||
LEDS_2G5 | LEDS_LINK | LEDS_10G,
|
||||
LEDS_1G | LEDS_LINK,
|
||||
0 },
|
||||
},
|
||||
};
|
||||
|
||||
void machine_custom_init(void) { }
|
||||
|
||||
#elif defined MACHINE_KP_9000_9XH_X_EU
|
||||
__code const struct machine machine = {
|
||||
.machine_name = "keepLink KP-9000-9XH-X-EU",
|
||||
.isRTL8373 = 1,
|
||||
.min_port = 0,
|
||||
.max_port = 8,
|
||||
.n_sfp = 1,
|
||||
.log_to_phys_port = {1, 2, 3, 4, 5, 6, 7, 8, 9},
|
||||
.phys_to_log_port = {0, 1, 2, 3, 4, 5, 6, 7, 8},
|
||||
.is_sfp = {0, 0, 0, 0, 0, 0, 0, 0, 1},
|
||||
.sfp_port[0].pin_detect = GPIO30_ACL_BIT3_EN,
|
||||
.sfp_port[0].pin_los = GPIO37,
|
||||
.sfp_port[0].pin_tx_disable = GPIO_NA,
|
||||
.sfp_port[0].sds = 1,
|
||||
.sfp_port[0].i2c = { .sda = GPIO39_I2C_SDA4, .scl = GPIO40_I2C_SCL3_MDC1 },
|
||||
.reset_pin = GPIO_NA,
|
||||
.high_leds = { .mux = LED_27 | LED_29, .enable = LED_28 | LED_29 },
|
||||
.port_led_set = { 0, 0, 0, 0, 0, 0, 0, 0, 0},
|
||||
.led_sets = { { 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_2G5 | LEDS_LINK,
|
||||
LEDS_1G | LEDS_LINK,
|
||||
LEDS_2G5 | LEDS_LINK | LEDS_ACT },
|
||||
},
|
||||
};
|
||||
|
||||
void machine_custom_init(void) { }
|
||||
|
||||
#elif defined MACHINE_KP_9000_9XHML_X
|
||||
__code const struct machine machine = {
|
||||
.machine_name = "keepLink KP-9000-9XHML-X",
|
||||
.isRTL8373 = 1,
|
||||
.min_port = 0,
|
||||
.max_port = 8,
|
||||
.n_sfp = 1,
|
||||
.log_to_phys_port = {1, 2, 3, 4, 5, 6, 7, 8, 9},
|
||||
.phys_to_log_port = {0, 1, 2, 3, 4, 5, 6, 7, 8},
|
||||
.is_sfp = {0, 0, 0, 0, 0, 0, 0, 0, 1},
|
||||
.sfp_port[0].pin_detect = GPIO38,
|
||||
.sfp_port[0].pin_los = GPIO_NA,
|
||||
.sfp_port[0].sds = 1,
|
||||
.sfp_port[0].i2c = { .sda = GPIO39_I2C_SDA4, .scl = GPIO40_I2C_SCL3_MDC1 },
|
||||
.reset_pin = GPIO48_I2C_SCL1,
|
||||
.high_leds = { .mux = LED_27 | LED_29, .enable = LED_28 | LED_29 },
|
||||
.port_led_set = { 0, 0, 0, 0, 0, 0, 0, 0, 1},
|
||||
.led_sets = {
|
||||
{ /* RJ45: First LED, yellow, second LED: green */
|
||||
LEDS_2G5 | LEDS_LINK,
|
||||
LEDS_2G5 | LEDS_1G | LEDS_100M | LEDS_10M | LEDS_LINK | LEDS_ACT,
|
||||
0,
|
||||
0,
|
||||
}, { /* SFP PORT, SINGLE GREEN LED */
|
||||
LEDS_2G5 | LEDS_1G | LEDS_100M | LEDS_10M | LEDS_LINK | LEDS_ACT | LEDS_10G,
|
||||
0,
|
||||
0,
|
||||
0,
|
||||
}},
|
||||
.led_mux_custom = 1,
|
||||
.led_mux = {0x00, 0x01, 0x04, 0x05, 0x08, 0x09, 0x0c, 0x09, 0x0d, 0x10,
|
||||
0x11, 0x0e, 0x14, 0x11, 0x12, 0x15, 0x15, 0x16, 0x18, 0x19,
|
||||
0x1a, 0x19, 0x1d, 0x1e, 0x1c, 0x1d, 0x20, 0x21},
|
||||
};
|
||||
|
||||
void machine_custom_init(void) { }
|
||||
|
||||
#elif defined MACHINE_SWGT024_V2_0
|
||||
__code const struct machine machine = {
|
||||
.machine_name = "SWGT024 V2.0",
|
||||
.isRTL8373 = 0,
|
||||
.min_port = 3,
|
||||
.max_port = 8,
|
||||
.n_sfp = 2,
|
||||
.log_to_phys_port = {0, 0, 0, 6, 1, 2, 3, 4, 5},
|
||||
.phys_to_log_port = {4, 5, 6, 7, 8, 3, 0, 0, 0},
|
||||
.is_sfp= {0, 0, 0, 2, 0, 0, 0, 0, 1},
|
||||
// Left SFP port (J4)
|
||||
.sfp_port[0].pin_detect = GPIO30_ACL_BIT3_EN,
|
||||
.sfp_port[0].pin_los = GPIO37,
|
||||
.sfp_port[0].pin_tx_disable = GPIO_NA,
|
||||
.sfp_port[0].sds = 1,
|
||||
.sfp_port[0].i2c = { .sda = GPIO39_I2C_SDA4, .scl = GPIO40_I2C_SCL3_MDC1 }, /* GPIO 39 */
|
||||
// Right SFP port (J2)
|
||||
.sfp_port[1].pin_detect = GPIO50_I2C_SCL2_UART1_TX,
|
||||
.sfp_port[1].pin_los = GPIO51_I2C_SDA2_UART1_RX,
|
||||
.sfp_port[1].pin_tx_disable = GPIO_NA,
|
||||
.sfp_port[1].sds = 0,
|
||||
.sfp_port[1].i2c = { .sda = GPIO41_I2C_SDA3_MDIO1, .scl = GPIO40_I2C_SCL3_MDC1 }, /* GPIO 40 */
|
||||
.reset_pin = GPIO36_PWM_OUT,
|
||||
.high_leds = { .mux = LED_27 | LED_29, .enable = LED_28 | LED_29 },
|
||||
.port_led_set = { 0, 0, 0, 0, 0, 0, 0, 0, 0},
|
||||
/* Conditions for LED on:
|
||||
* dual led orange: ledset_0 & ledset_2
|
||||
* dual led green: ledset_2 & !ledset_0
|
||||
* single right led green: ledset_0 & !ledset_1
|
||||
*/
|
||||
.led_sets = { { LEDS_2G5 | LEDS_1G | LEDS_100M | LEDS_10M | LEDS_LINK | LEDS_ACT | LEDS_10G,
|
||||
LEDS_2G5 | LEDS_LINK | LEDS_10G,
|
||||
LEDS_1G | LEDS_LINK,
|
||||
0 },
|
||||
},
|
||||
};
|
||||
|
||||
void machine_custom_init(void) { }
|
||||
|
||||
#elif defined MACHINE_SWTG018AS_A_V_2_0
|
||||
__code const struct machine machine = {
|
||||
.machine_name = "SWTG018AS-A V2.0",
|
||||
.isRTL8373 = 1,
|
||||
.min_port = 0,
|
||||
.max_port = 8,
|
||||
.n_sfp = 1,
|
||||
.log_to_phys_port = {1, 2, 3, 4, 5, 6, 7, 8, 9},
|
||||
.phys_to_log_port = {0, 1, 2, 3, 4, 5, 6, 7, 8},
|
||||
.is_sfp = {0, 0, 0, 0, 0, 0, 0, 0, 1},
|
||||
.sfp_port[0].pin_detect = GPIO38,
|
||||
.sfp_port[0].pin_los = GPIO_NA,
|
||||
.sfp_port[0].pin_tx_disable = GPIO_NA,
|
||||
.sfp_port[0].sds = 1,
|
||||
.sfp_port[0].i2c = { .sda = GPIO39_I2C_SDA4, .scl = GPIO40_I2C_SCL3_MDC1 },
|
||||
.reset_pin = GPIO_NA,
|
||||
.high_leds = { .mux = LED_27 | LED_29, .enable = LED_28 | LED_29 },
|
||||
.port_led_set = { 0, 0, 0, 0, 0, 0, 0, 0, 1},
|
||||
.led_sets = {
|
||||
{ /* RJ45: First LED, yellow, second LED: green */
|
||||
LEDS_2G5 | LEDS_LINK,
|
||||
LEDS_2G5 | LEDS_1G | LEDS_100M | LEDS_10M | LEDS_LINK | LEDS_ACT,
|
||||
0,
|
||||
0,
|
||||
}, { /* SFP PORT, SINGLE GREEN LED */
|
||||
LEDS_2G5 | LEDS_1G | LEDS_100M | LEDS_10M | LEDS_LINK | LEDS_ACT | LEDS_10G,
|
||||
0,
|
||||
0,
|
||||
0,
|
||||
}},
|
||||
.led_mux_custom = 1,
|
||||
.led_mux = { 0x00, 0x01, 0x04, 0x05, 0x08, // 65e0
|
||||
0x09, 0x0c, 0x09, 0x0d, 0x10, // 65e4
|
||||
0x11, 0x0e, 0x14, 0x11, 0x12, // 65e8
|
||||
0x15, 0x15, 0x16, 0x18, 0x19, // 65ec
|
||||
0x1a, 0x19, 0x1d, 0x1e, 0x1c, // 65f0
|
||||
0x1d, 0x20, 0x21 },
|
||||
};
|
||||
|
||||
void machine_custom_init(void) { }
|
||||
|
||||
#elif defined MACHINE_HG0402XG_V1_1
|
||||
__code const struct machine machine = {
|
||||
.machine_name = "HG0402XG V1.1",
|
||||
.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, 2, 0, 0, 0, 0, 1},
|
||||
.sfp_port[0].pin_detect = 50,
|
||||
.sfp_port[0].pin_los = 10,
|
||||
.sfp_port[0].pin_tx_disable = 0xFF,
|
||||
.sfp_port[0].sds = 1,
|
||||
.sfp_port[0].i2c_bus ={ .sda = GPIO41_I2C_SDA3_MDIO1, .scl = GPIO40_I2C_SCL3_MDC1 },
|
||||
.sfp_port[1].pin_detect = 30,
|
||||
.sfp_port[1].pin_los = 51,
|
||||
.sfp_port[1].pin_tx_disable = 0xFF,
|
||||
.sfp_port[1].sds = 0,
|
||||
.sfp_port[1].i2c_bus = { .sda = GPIO39_I2C_SDA4, .scl = GPIO40_I2C_SCL3_MDC1 },
|
||||
.reset_pin = GPIO_NA,
|
||||
.high_leds = { .mux = LED_27 , .enable = LED_27 | LED_29 },
|
||||
.port_led_set = { 0, 0, 0, 1, 0, 0, 0, 0, 1},
|
||||
/* The Ethernet ports have 1 amber LED (left) and 1 green LED (right)
|
||||
* The SFP ports have also 1 amber LED and 1 green LED
|
||||
* Ethernet ports use LED-set 0, SFP ports use LED-set 1
|
||||
*/
|
||||
.led_sets = { { LEDS_10M | LEDS_LINK | LEDS_ACT,
|
||||
LEDS_1G | LEDS_100M | LEDS_10M | LEDS_2G5 | LEDS_LINK | LEDS_ACT,
|
||||
LEDS_2G5 | LEDS_LINK | LEDS_ACT,
|
||||
0 },
|
||||
{ LEDS_100M | LEDS_10M | LEDS_LINK,
|
||||
LEDS_2G5 | LEDS_1G | LEDS_100M | LEDS_10M | LEDS_10M | LEDS_LINK | LEDS_ACT | LEDS_10G,
|
||||
LEDS_10G | LEDS_LINK,
|
||||
0 },
|
||||
},
|
||||
};
|
||||
|
||||
void machine_custom_init(void) { }
|
||||
|
||||
#elif defined MACHINE_SWTGW218AS
|
||||
|
||||
__code const struct machine machine = {
|
||||
.machine_name = "SWTGW218AS 8+1 Managed Switch",
|
||||
.isRTL8373 = 1,
|
||||
.min_port = 0,
|
||||
.max_port = 8,
|
||||
.n_sfp = 1,
|
||||
.log_to_phys_port = {1, 2, 3, 4, 5, 6, 7, 8, 9},
|
||||
.phys_to_log_port = {0, 1, 2, 3, 4, 5, 6, 7, 8},
|
||||
.is_sfp = {0, 0, 0, 0, 0, 0, 0, 0, 1},
|
||||
.sfp_port[0].pin_detect = GPIO30_ACL_BIT3_EN,
|
||||
.sfp_port[0].pin_los = GPIO37,
|
||||
.sfp_port[0].pin_tx_disable = GPIO_NA,
|
||||
.sfp_port[0].sds = 1,
|
||||
.sfp_port[0].i2c = { .sda = GPIO39_I2C_SDA4, .scl = GPIO40_I2C_SCL3_MDC1 },
|
||||
.reset_pin = GPIO54_ACL_BIT2_EN,
|
||||
.high_leds = { .mux = LED_27 | LED_28 | LED_29, .enable = LED_28 | LED_29 },
|
||||
.port_led_set = { 0, 0, 0, 0, 0, 0, 0, 0, 1},
|
||||
.led_sets = { { LEDS_2G5 | LEDS_LINK | LEDS_ACT, // Green LED (right)
|
||||
0, // unused
|
||||
LEDS_1G | LEDS_100M | LEDS_10M | LEDS_LINK | LEDS_ACT, // Amber LED (left)
|
||||
0
|
||||
}, // unused
|
||||
{ LEDS_10G | LEDS_5G | LEDS_2G5 | LEDS_1G | LEDS_100M | LEDS_LINK | LEDS_ACT, // SFP LED
|
||||
0, // unused
|
||||
0, // unused
|
||||
0
|
||||
}, // unused },
|
||||
},
|
||||
};
|
||||
|
||||
void machine_custom_init(void) { }
|
||||
|
||||
#elif defined MACHINE_DEFAULT_8C_1SFP
|
||||
__code const struct machine machine = {
|
||||
.machine_name = "8+1 SFP Port Switch",
|
||||
.isRTL8373 = 1,
|
||||
.min_port = 0,
|
||||
.max_port = 8,
|
||||
.n_sfp = 1,
|
||||
.log_to_phys_port = {1, 2, 3, 4, 5, 6, 7, 8, 9},
|
||||
.phys_to_log_port = {0, 1, 2, 3, 4, 5, 6, 7, 8},
|
||||
.is_sfp = {0, 0, 0, 0, 0, 0, 0, 0, 1},
|
||||
.sfp_port[0].pin_detect = GPIO30_ACL_BIT3_EN,
|
||||
.sfp_port[0].pin_los = GPIO37,
|
||||
.sfp_port[0].pin_tx_disable = GPIO_NA,
|
||||
.sfp_port[0].sds = 1,
|
||||
.sfp_port[0].i2c = { .sda = GPIO39_I2C_SDA4, .scl = GPIO40_I2C_SCL3_MDC1 },
|
||||
.reset_pin = GPIO_NA,
|
||||
.high_leds = { .mux = LED_27 | LED_29, .enable = LED_28 | LED_29 },
|
||||
.port_led_set = { 0, 0, 0, 0, 0, 0, 0, 0, 0},
|
||||
.led_sets = { { 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_2G5 | LEDS_LINK,
|
||||
LEDS_1G | LEDS_LINK,
|
||||
LEDS_2G5 | LEDS_LINK | LEDS_ACT },
|
||||
},
|
||||
};
|
||||
|
||||
void machine_custom_init(void) { }
|
||||
|
||||
#elif defined MACHINE_TRENDNET_TEG_S562
|
||||
__code const struct machine machine = {
|
||||
.machine_name = "Trendnet TEG-S562",
|
||||
.isRTL8373 = 0,
|
||||
.min_port = 3,
|
||||
.max_port = 8,
|
||||
.n_sfp = 2,
|
||||
.log_to_phys_port = {0, 0, 0, 6, 1, 2, 3, 4, 5},
|
||||
.phys_to_log_port = {4, 5, 6, 7, 3, 8, 0, 0, 0},
|
||||
.is_sfp = {0, 0, 0, 2, 0, 0, 0, 0, 1},
|
||||
.sfp_port[0].pin_detect = GPIO36_PWM_OUT,
|
||||
.sfp_port[0].pin_los = GPIO37,
|
||||
.sfp_port[0].pin_tx_disable = GPIO51_I2C_SDA2_UART1_RX,
|
||||
.sfp_port[0].sds = 0,
|
||||
.sfp_port[0].i2c = { .sda = GPIO47_I2C_SDA0, .scl = GPIO46_I2C_SCL0 },
|
||||
.sfp_port[1].pin_detect = GPIO38,
|
||||
.sfp_port[1].pin_los = GPIO50_I2C_SCL2_UART1_TX,
|
||||
.sfp_port[1].pin_tx_disable = GPIO54_ACL_BIT2_EN,
|
||||
.sfp_port[1].sds = 1,
|
||||
.sfp_port[1].i2c = { .sda = GPIO49_I2C_SDA1, .scl = GPIO48_I2C_SCL1 },
|
||||
.reset_pin = GPIO_NA,
|
||||
};
|
||||
|
||||
void machine_custom_init(void) { }
|
||||
|
||||
#elif defined MACHINE_HI_K0402WS
|
||||
__code const struct machine machine = {
|
||||
.machine_name = "HiSource HI-K0402WS",
|
||||
.isRTL8373 = 0,
|
||||
.min_port = 3,
|
||||
.max_port = 8,
|
||||
.n_sfp = 2,
|
||||
.log_to_phys_port = {0, 0, 0, 6, 1, 2, 3, 4, 5},
|
||||
.phys_to_log_port = {4, 5, 6, 7, 8, 3, 0, 0, 0},
|
||||
.is_sfp = {0, 0, 0, 2, 0, 0, 0, 0, 1},
|
||||
|
||||
// Left SFP port
|
||||
.sfp_port[0].pin_detect = GPIO38,
|
||||
.sfp_port[0].pin_los = GPIO_NA,
|
||||
.sfp_port[0].sds = 1,
|
||||
.sfp_port[0].i2c = { .sda = GPIO39_I2C_SDA4, .scl = GPIO40_I2C_SCL3_MDC1 },
|
||||
|
||||
// Right SFP port
|
||||
.sfp_port[1].pin_detect = GPIO37,
|
||||
.sfp_port[1].pin_los = GPIO_NA,
|
||||
.sfp_port[1].sds = 0,
|
||||
.sfp_port[1].i2c = { .sda = GPIO41_I2C_SDA3_MDIO1, .scl = GPIO40_I2C_SCL3_MDC1 },
|
||||
|
||||
.reset_pin = GPIO_NA,
|
||||
.high_leds = { .mux = LED_28 | LED_29, .enable = LED_27 | LED_28 | LED_29 },
|
||||
.port_led_set = { 0, 0, 0, 1, 0, 0, 0, 0, 1},
|
||||
.led_sets = {
|
||||
{
|
||||
LEDS_2G5 | LEDS_LINK | LEDS_10M | LEDS_ACT,
|
||||
LEDS_1G | LEDS_100M | LEDS_10M | LEDS_LINK | LEDS_ACT | LEDS_10G,
|
||||
LEDS_1G | LEDS_LINK,
|
||||
0
|
||||
},
|
||||
{
|
||||
LEDS_10G | LEDS_2G5 | LEDS_1G | LEDS_100M | LEDS_10M | LEDS_LINK,
|
||||
LEDS_10G | LEDS_2G5 | LEDS_1G | LEDS_100M | LEDS_10M | LEDS_ACT,
|
||||
0,
|
||||
0
|
||||
},
|
||||
},
|
||||
.led_mux_custom = 1,
|
||||
.led_mux = {
|
||||
0x00,0x01,0x04,0x05,0x08,0x09,0x0c,0x3f,0x0d,0x10,0x11,0x0e,0x14,0x11,0x12,0x15,0x15,0x16,0x18,0x19,0x1a,0x19,0x1d,0x1e,0x1c,0x1d,0x20,0x21
|
||||
},
|
||||
};
|
||||
|
||||
void machine_custom_init(void) {
|
||||
reg_bit_set(RTL837X_REG_LED_GLB_IO_EN, 6);
|
||||
}
|
||||
#else
|
||||
#error "Please select a machine type in machine.h"
|
||||
#endif
|
||||
@@ -0,0 +1,76 @@
|
||||
#ifndef _MACHINE_H_
|
||||
#define _MACHINE_H_
|
||||
|
||||
#include <stdint.h>
|
||||
|
||||
/*
|
||||
* Select your machine type below
|
||||
*/
|
||||
// #define MACHINE_KP_9000_6XHML_X2
|
||||
// #define MACHINE_KP_9000_6XH_X
|
||||
// #define MACHINE_KP_9000_9XH_X_EU
|
||||
// #define MACHINE_KP_9000_9XHML_X
|
||||
// #define MACHINE_SWGT024_V2_0
|
||||
// #define MACHINE_HORACO_ZX_SG4T2
|
||||
// #define MACHINE_TRENDNET_TEG_S562
|
||||
// #define MACHINE_HG0402XG_V1_1
|
||||
// #define MACHINE_SWTG018AS_A_V_2_0
|
||||
// #define MACHINE_SWTGW218AS
|
||||
// #define MACHINE_HI_K0402WS
|
||||
// #define MACHINE_DEFAULT_8C_1SFP
|
||||
|
||||
typedef struct {
|
||||
// GPIO pins for SDA/SCL
|
||||
uint8_t sda;
|
||||
uint8_t scl;
|
||||
} i2c_bus_t;
|
||||
|
||||
|
||||
#define LED_27 1
|
||||
#define LED_28 2
|
||||
#define LED_29 4
|
||||
|
||||
struct high_leds {
|
||||
// Defines MUX and LED enabling for pins 27-29
|
||||
uint8_t mux : 3;
|
||||
uint8_t enable : 3;
|
||||
uint8_t reserved : 2;
|
||||
};
|
||||
|
||||
struct sfp_port
|
||||
{
|
||||
uint8_t pin_detect; // gpio number 0-63, 0xFF = don't have it?
|
||||
uint8_t pin_los; // gpio number 0-63, 0xFF = don't have it?
|
||||
uint8_t pin_tx_disable; // gpio number 0-63, 0xFF = not present
|
||||
uint8_t sds;
|
||||
i2c_bus_t i2c;
|
||||
};
|
||||
|
||||
typedef struct machine {
|
||||
char machine_name[30];
|
||||
uint8_t isRTL8373;
|
||||
uint8_t min_port;
|
||||
uint8_t max_port;
|
||||
uint8_t n_sfp;
|
||||
uint8_t log_to_phys_port[9];
|
||||
uint8_t phys_to_log_port[9]; // Starts at 0 for port 1
|
||||
uint8_t is_sfp[9]; // 0 for non-SFP ports 1 or 2 for the I2C port number
|
||||
// sfp_port[0] is the first SFP-port from the left on the device, sfp_port[1] the next if present
|
||||
struct sfp_port sfp_port[2];
|
||||
uint8_t reset_pin;
|
||||
struct high_leds high_leds;
|
||||
uint8_t port_led_set[9];
|
||||
uint32_t led_sets[4][4];
|
||||
uint8_t led_mux_custom;
|
||||
uint8_t led_mux[28];
|
||||
};
|
||||
|
||||
typedef struct machine_runtime
|
||||
{
|
||||
uint8_t isRTL8373 : 1;
|
||||
uint8_t isN : 1;
|
||||
};
|
||||
|
||||
void machine_custom_init(void);
|
||||
|
||||
#endif
|
||||
@@ -11,15 +11,19 @@
|
||||
/*
|
||||
* Define PHY pages
|
||||
*/
|
||||
#define PHY_MMD_PMAPMD 1
|
||||
#define PHY_MMD_AN 7
|
||||
#define PHY_SDS_CTRL 30
|
||||
#define PHY_MMD_CTRL 31
|
||||
|
||||
#define PHY_MMD30 30
|
||||
#define PHY_MMD31 31
|
||||
|
||||
/*
|
||||
* Define registers in Auto-Negotiation page
|
||||
*/
|
||||
#define PHY_ANEG_CTRL 0x00
|
||||
#define PHY_ANEG_ADV 0x10
|
||||
#define PHY_ANEG_LP_ABILITY 0x13
|
||||
#define PHY_ANEG_MGBASE_CTRL 0x20
|
||||
#define PHY_ANEG_MGBASE_ADV 0x21
|
||||
#define PHY_EEE_ADV 0x3c
|
||||
#define PHY_EEE_LP_ABILITY 0x3d
|
||||
#define PHY_EEE_ADV2 0x3e
|
||||
@@ -29,8 +33,26 @@
|
||||
#define PHY_EEE_BIT_1G 0x04
|
||||
#define PHY_EEE_BIT_100M 0x02
|
||||
|
||||
/*
|
||||
* MMD 31 Registers
|
||||
*/
|
||||
#define PHY_MMD31_FEDCR 0xa400
|
||||
#define PHY_MMD31_GBCR 0xa412
|
||||
#define PHY_MMD31_GANLPAR 0xa414
|
||||
#define PHY_MMD31_PHYCR2 0xa432
|
||||
#define PHY_MMD31_PHYSR 0xa434
|
||||
|
||||
|
||||
|
||||
/*
|
||||
* Define registers in Control page
|
||||
*/
|
||||
#define PHY_CTRL_5 0x7582
|
||||
|
||||
/*
|
||||
* Duplex settings
|
||||
*/
|
||||
#define PHY_DUPLEX_HALF 0
|
||||
#define PHY_DUPLEX_FULL 1
|
||||
#define PHY_DUPLEX_BOTH 2
|
||||
#endif
|
||||
|
||||
@@ -0,0 +1,122 @@
|
||||
// #define REGDBG
|
||||
// #define DEBUG
|
||||
|
||||
#include "rtl837x_common.h"
|
||||
#include "rtl837x_sfr.h"
|
||||
#include "rtl837x_regs.h"
|
||||
#include "rtl837x_bandwidth.h"
|
||||
#include "machine.h"
|
||||
|
||||
#pragma codeseg BANK2
|
||||
#pragma constseg BANK2
|
||||
|
||||
extern __xdata uint8_t sfr_data[4];
|
||||
|
||||
void bandwidth_setup(void) __banked
|
||||
{
|
||||
print_string("bandwidth_setup called\n");
|
||||
// Exclude all packets possibly for the CPU port, but do not include bypassed packets or Inter-Frame-Gap into bandwidth
|
||||
REG_SET(RTL837X_IGBW_CTRL, IGBW_ADM_DHCP | IGBW_ADM_ARPREQ | IGBW_ADM_RMA | IGBW_ADM_BPDU | IGBW_ADM_RTKPKT | IGBW_ADM_IGMP);
|
||||
|
||||
// We do not count IFG for Egress and allways allow CPU-traffic
|
||||
REG_SET(RTL837X_EGBW_CTRL, EGBW_CPUMODE);
|
||||
|
||||
print_string("RTL837X_IGBW_CTRL: "); print_reg(RTL837X_IGBW_CTRL); write_char('\n');
|
||||
print_string("RTL837X_EGBW_CTRL: "); print_reg(RTL837X_EGBW_CTRL); write_char('\n');
|
||||
print_string("bandwidth_setup done\n");
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
* Set the ingress bandwidth
|
||||
* bw: Bandwidth in kb
|
||||
*/
|
||||
void bandwidth_ingress_set(uint8_t port, __xdata uint32_t bw) __banked
|
||||
{
|
||||
__xdata uint8_t * __xdata bwptr = &bw;
|
||||
|
||||
print_string("bandwidth_ingress_set called, port "); print_byte(port); write_char('\n');
|
||||
sfr_data[0] = 0;
|
||||
sfr_data[1] = 0x10 | (*(bwptr + 2) >> 4); // Set bit 20 to enable ingress bandwidth control
|
||||
sfr_data[2] = (*(bwptr + 2) << 4) | (*(bwptr + 1) >> 4);
|
||||
sfr_data[3] = (*(bwptr) >> 4) | (*(bwptr + 1) << 4);
|
||||
reg_write_m(RTL837X_IGBW_PORT_CTRL + port * 4);
|
||||
|
||||
// We enable Flow Control instead of just dropping packets
|
||||
reg_bit_set(RTL837X_IGBW_PORT_FC_CTRL, port);
|
||||
}
|
||||
|
||||
|
||||
void bandwidth_ingress_drop(uint8_t port) __banked
|
||||
{
|
||||
reg_bit_clear(RTL837X_IGBW_PORT_FC_CTRL, port);
|
||||
print_string("RTL837X_IGBW_PORT_FC_CTRL:"); print_reg(RTL837X_IGBW_PORT_FC_CTRL); write_char('\n');
|
||||
}
|
||||
|
||||
|
||||
void bandwidth_ingress_fc(uint8_t port) __banked
|
||||
{
|
||||
reg_bit_set(RTL837X_IGBW_PORT_FC_CTRL, port);
|
||||
print_string("RTL837X_IGBW_PORT_FC_CTRL:"); print_reg(RTL837X_IGBW_PORT_FC_CTRL); write_char('\n');
|
||||
}
|
||||
|
||||
|
||||
void bandwidth_ingress_disable(uint8_t port) __banked
|
||||
{
|
||||
print_string("Ingress bandwidth limit disabled, port "); print_byte(port); write_char('\n');
|
||||
REG_SET(RTL837X_IGBW_PORT_CTRL + port * 4, 0x0fffff);
|
||||
}
|
||||
|
||||
|
||||
void bandwidth_egress_set(uint8_t port, __xdata uint32_t bw) __banked
|
||||
{
|
||||
__xdata uint8_t * __xdata bwptr = &bw;
|
||||
|
||||
print_string("bandwidth_egress_set called, port "); print_byte(port); write_char('\n');
|
||||
sfr_data[0] = 0;
|
||||
sfr_data[1] = 0x10 | (*(bwptr + 2) >> 4); // Set bit 20 to enable egress bandwidth control
|
||||
sfr_data[2] = (*(bwptr + 2) << 4) | (*(bwptr + 1) >> 4);
|
||||
sfr_data[3] = (*(bwptr) >> 4) | (*(bwptr + 1) << 4);
|
||||
reg_write_m(RTL837X_EGBW_PORT_CTRL + port * 1024);
|
||||
}
|
||||
|
||||
|
||||
void bandwidth_egress_disable(uint8_t port) __banked
|
||||
{
|
||||
print_string("Egress bandwidth limit disabled, port "); print_byte(port); write_char('\n');
|
||||
REG_SET(RTL837X_EGBW_PORT_CTRL + port * 1024, 0x0fffff);
|
||||
}
|
||||
|
||||
|
||||
void bandwidth_status(uint8_t port) __banked
|
||||
{
|
||||
print_string("ingress: ");
|
||||
reg_read_m(RTL837X_IGBW_PORT_CTRL + port * 4);
|
||||
if (sfr_data[1] & 0x10) {
|
||||
print_string("enabled: ");
|
||||
sfr_data[1] &= 0xef;
|
||||
print_string("0x");
|
||||
print_byte(sfr_data[1]);
|
||||
print_byte(sfr_data[2]);
|
||||
print_byte(sfr_data[3]);
|
||||
write_char('0');
|
||||
write_char('\n');
|
||||
} else {
|
||||
print_string("disabled\n");
|
||||
}
|
||||
|
||||
print_string("egress: ");
|
||||
reg_read_m(RTL837X_EGBW_PORT_CTRL + port * 1024);
|
||||
if (sfr_data[1] & 0x10) {
|
||||
print_string("enabled: ");
|
||||
sfr_data[1] &= 0xef;
|
||||
print_string("0x");
|
||||
print_byte(sfr_data[1]);
|
||||
print_byte(sfr_data[2]);
|
||||
print_byte(sfr_data[3]);
|
||||
write_char('0');
|
||||
write_char('\n');
|
||||
} else {
|
||||
print_string("disabled\n");
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,15 @@
|
||||
#ifndef _RTL837X_BANDWIDTH_H_
|
||||
#define _RTL837X_BANDWIDTH_H_
|
||||
|
||||
#include <stdint.h>
|
||||
|
||||
void bandwidth_setup(void) __banked;
|
||||
void bandwidth_ingress_set(uint8_t port, __xdata uint32_t bw) __banked;
|
||||
void bandwidth_ingress_disable(uint8_t port) __banked;
|
||||
void bandwidth_ingress_drop(uint8_t port) __banked;
|
||||
void bandwidth_ingress_fc(uint8_t port) __banked;
|
||||
void bandwidth_egress_set(uint8_t port, __xdata uint32_t bw) __banked;
|
||||
void bandwidth_egress_disable(uint8_t port) __banked;
|
||||
void bandwidth_status(uint8_t port) __banked;
|
||||
|
||||
#endif
|
||||
@@ -3,22 +3,35 @@
|
||||
|
||||
#include "uip/uip-conf.h"
|
||||
#include <stdint.h>
|
||||
#include <stdbool.h>
|
||||
|
||||
// This has to be set to the number of SFP+ ports, i.e. 1 or 2
|
||||
#define NSFP 2
|
||||
// SCL and SDA pin numbers for SFP cage 0 and SFP cage 1
|
||||
#define SCL_PIN 3
|
||||
#define SDA_PIN_0 4
|
||||
#define SDA_PIN_1 3
|
||||
#define SYS_TICK_HZ 200
|
||||
|
||||
#define CPU_PORT 9
|
||||
|
||||
// Define Port-masks for 9-port devices and 6-port devices
|
||||
#define PMASK_9 0x1ff
|
||||
#define PMASK_6 0x1f8
|
||||
#define PMASK_CPU 0x200
|
||||
|
||||
// The serial buffer. Defines the command line size
|
||||
// Must be 2^x and <= 128
|
||||
#define SBUF_SIZE 128
|
||||
// Defines a port mask for dropping all packets on Lookup-miss
|
||||
#define LOOKUP_MISS_DROP_6 0x00015540
|
||||
#define LOOKUP_MISS_DROP_9 0x00015555
|
||||
#define LOOKUP_MISS_FLOOD 0x00000000
|
||||
|
||||
/* Buffer for serial input, SBUF_SIZE must be power of 2 < 256
|
||||
* Writing to this buffer is under the sole control of the serial ISR
|
||||
* Note that key-presses such as <cursor-left> can create multiple
|
||||
* keys (3 to 4) being sent via the serial line, so this must be
|
||||
* sufficiently large */
|
||||
#define SBUF_SIZE 16
|
||||
#define SBUF_MASK (SBUF_SIZE - 1)
|
||||
|
||||
extern __xdata volatile uint8_t sbuf_ptr;
|
||||
extern __xdata uint8_t sbuf[SBUF_SIZE];
|
||||
|
||||
// Define the command buffer size, Must be 2^x and <= 128
|
||||
#define CMD_BUF_SIZE 128
|
||||
|
||||
// Size of the TCP Output buffer
|
||||
#define TCP_OUTBUF_SIZE 2500
|
||||
@@ -26,12 +39,28 @@
|
||||
// Size of the memory area dedicated to VLAN-names
|
||||
#define VLAN_NAMES_SIZE 1024
|
||||
|
||||
// Size of the flash buffer used for writing to flash, must be a multiple of the flash page size (0x100)
|
||||
#define FLASH_BUF_SIZE 512
|
||||
|
||||
// For RX data, a propriatary RTL FRAME is inserted. Instead of 0x0800 for IPv4,
|
||||
// the RTL_FRAME_TAG_ID is used as part of an 8-byte tag. When VLAN is activated,
|
||||
// the VLAN tag is inserted after the RTL tag
|
||||
// See here for the RTL tag: https://github.com/torvalds/linux/commit/1521d5adfc2b557e15f97283c8b7ad688c3ebc40
|
||||
#define RTL_TAG_SIZE 8
|
||||
#define VLAN_TAG_SIZE 4
|
||||
struct rtl_tag {
|
||||
uint16_t tag; // This is 0x8899 for the RTL837X
|
||||
uint8_t version; // Version is 4
|
||||
uint8_t reason;
|
||||
uint16_t flags;
|
||||
uint16_t pmask; // A bit mask for a TX pkt, 4-bit port-number for RX
|
||||
};
|
||||
|
||||
struct vlan_tag {
|
||||
uint16_t svlan; // Service VLAN
|
||||
uint16_t vlan;
|
||||
};
|
||||
|
||||
#define RTL_TAG_SIZE (sizeof (struct rtl_tag))
|
||||
#define VLAN_TAG_SIZE (sizeof (struct vlan_tag))
|
||||
#define RTL_FRAME_TAG_ID 0x8899
|
||||
|
||||
// For RX and TX, an 8 byte header describing the frame to be moved to the Asic
|
||||
@@ -41,11 +70,18 @@
|
||||
// This is the standard size of an Ethernet frame header
|
||||
#define ETHER_HEADER_SIZE 14
|
||||
|
||||
#if NSFP == 1
|
||||
#define IS_SFP(port) (i == maxPort)
|
||||
#else
|
||||
#define IS_SFP(port) (i == maxPort || i == 3)
|
||||
#endif
|
||||
#define DEFAULT_CONFIG_START 0x6f000
|
||||
#define CONFIG_START 0x70000
|
||||
#define CONFIG_LEN 0x1000
|
||||
#define CODE0_SIZE 0x4000
|
||||
#define CODE_BANK_SIZE 0xc000
|
||||
|
||||
// Store update image after running image
|
||||
#define FIRMWARE_UPLOAD_START 0x80000
|
||||
|
||||
// Constants for the circular command buffer, the size must be 2^n
|
||||
#define CMD_HISTORY_SIZE 0x400
|
||||
#define CMD_HISTORY_MASK (CMD_HISTORY_SIZE - 1)
|
||||
|
||||
/**
|
||||
* Representation of a 48-bit Ethernet address.
|
||||
@@ -60,13 +96,17 @@ struct flash_region_t {
|
||||
};
|
||||
|
||||
extern __xdata uint8_t uip_buf[UIP_CONF_BUFFER_SIZE+2];
|
||||
|
||||
extern __xdata struct uip_eth_addr uip_ethaddr;
|
||||
extern __xdata uint16_t rx_packet_vlan;
|
||||
extern __xdata uint16_t dhcpd_vlan;
|
||||
|
||||
// Headers for calls in the common code area (HOME/BANK0)
|
||||
void print_string(__code char *p);
|
||||
void print_long(__xdata uint32_t a);
|
||||
void print_string_x(__xdata char *p);
|
||||
void print_long(uint32_t a);
|
||||
void print_short(uint16_t a);
|
||||
void print_byte(uint8_t a);
|
||||
void itoa(uint8_t v);
|
||||
void print_sfr_data(void);
|
||||
void print_phy_data(void);
|
||||
void phy_write_mask(uint16_t phy_mask, uint8_t dev_id, uint16_t reg, uint16_t v);
|
||||
@@ -84,6 +124,8 @@ void print_reg(uint16_t reg);
|
||||
uint8_t sfp_read_reg(uint8_t slot, uint8_t reg);
|
||||
void reg_bit_set(uint16_t reg_addr, char bit);
|
||||
void reg_bit_clear(uint16_t reg_addr, char bit);
|
||||
uint8_t reg_bit_test(uint16_t reg_addr, char bit);
|
||||
void sfr_mask_data(uint8_t n, uint8_t mask, uint8_t set);
|
||||
void sfr_set_zero(void);
|
||||
void reset_chip(void);
|
||||
void memcpy(__xdata void * __xdata dst, __xdata const void * __xdata src, uint16_t len);
|
||||
@@ -92,8 +134,13 @@ void memset(register __xdata uint8_t *dst, register __xdata uint8_t v, register
|
||||
uint16_t strlen(register __code const char *s);
|
||||
uint16_t strlen_x(register __xdata const char *s);
|
||||
uint16_t strtox(register __xdata uint8_t *dst, register __code const char *s);
|
||||
uint16_t strcpy(register __xdata uint8_t *dst, register const char *s);
|
||||
void tcpip_output(void);
|
||||
void print_string_x(__xdata char *p);
|
||||
|
||||
uint8_t read_flash(uint8_t bank, __code uint8_t *addr);
|
||||
void get_random_32(void);
|
||||
void read_reg_timer(uint32_t * tmr);
|
||||
void sfp_print_info(uint8_t sfp);
|
||||
bool gpio_pin_test(uint8_t pin);
|
||||
void set_sys_led_state(uint8_t state);
|
||||
|
||||
#endif
|
||||
|
||||
@@ -8,17 +8,20 @@
|
||||
#include "rtl837x_sfr.h"
|
||||
|
||||
__xdata uint8_t dio_enabled;
|
||||
__xdata uint8_t markbuf[16];
|
||||
extern __xdata uint16_t mpos;
|
||||
__xdata struct flash_region_t flash_region;
|
||||
|
||||
__xdata uint32_t flash_size;
|
||||
__xdata uint8_t flash_capacity_code;
|
||||
|
||||
// For the flash commands, see e.g. Windbond W25Q32JV datasheet
|
||||
#define CMD_WRITE_STATUS 0x01
|
||||
#define CMD_PAGE_PROGRAM 0x02
|
||||
#define CMD_READ 0x03
|
||||
// Don't use command `READ 0x03`, because on many device this command can't run at maximum SPI-clock speed.
|
||||
// Use `Fast READ 0x0b` instead!
|
||||
//#define CMD_READ 0x03
|
||||
#define CMD_READ_STATUS 0x05
|
||||
#define CMD_WRITE_ENABLE 0x06
|
||||
#define CMD_FREAD 0x0b
|
||||
#define CMD_FREAD 0x0b
|
||||
#define CMD_SECTOR_ERASE 0x20
|
||||
#define CMD_READ_SECURITY_REGS 0x48
|
||||
#define CMD_READ_UNIQUE_ID 0x4b
|
||||
@@ -87,7 +90,7 @@ uint8_t flash_read_status(void)
|
||||
|
||||
// setup status read command
|
||||
SFR_FLASH_TCONF = 0x11;
|
||||
SFR_FLASH_CMD_R = 5;
|
||||
SFR_FLASH_CMD_R = CMD_READ_STATUS;
|
||||
|
||||
// execute and wait for controller done
|
||||
SFR_FLASH_EXEC_GO = 1;
|
||||
@@ -119,9 +122,10 @@ void flash_read_uid(void)
|
||||
print_byte(SFR_FLASH_DATA8);
|
||||
print_byte(SFR_FLASH_DATA16);
|
||||
print_byte(SFR_FLASH_DATA24);
|
||||
write_char(' ');
|
||||
|
||||
SFR_FLASH_DUMMYCYCLES = 24; // Doesn't seem to work; we get the same data as for the first transfer
|
||||
SFR_FLASH_EXEC_GO = 1;
|
||||
SFR_FLASH_DUMMYCYCLES = 24;
|
||||
while(SFR_FLASH_EXEC_BUSY);
|
||||
|
||||
print_byte(SFR_FLASH_DATA0);
|
||||
@@ -132,6 +136,19 @@ void flash_read_uid(void)
|
||||
flash_configure_mmio();
|
||||
}
|
||||
|
||||
__code char* get_flash_size_str(void)
|
||||
{
|
||||
switch (flash_capacity_code) {
|
||||
case 0x12: return "256 KB";
|
||||
case 0x13: return "512 KB";
|
||||
case 0x14: return "1 MB";
|
||||
case 0x15: return "2 MB";
|
||||
case 0x16: return "4 MB";
|
||||
case 0x17: return "8 MB";
|
||||
case 0x18: return "16 MB";
|
||||
default: return "unknown";
|
||||
}
|
||||
}
|
||||
|
||||
void flash_read_jedecid(void)
|
||||
{
|
||||
@@ -148,15 +165,18 @@ void flash_read_jedecid(void)
|
||||
SFR_FLASH_EXEC_GO = 1;
|
||||
while(SFR_FLASH_EXEC_BUSY);
|
||||
|
||||
print_string("Flash information:\n");
|
||||
print_string(" Manufacturer ID: 0x");
|
||||
print_byte(SFR_FLASH_DATA0);
|
||||
print_string("\n Memory Type: 0x");
|
||||
print_byte(SFR_FLASH_DATA8);
|
||||
print_byte(SFR_FLASH_DATA16);
|
||||
print_byte(SFR_FLASH_DATA24);
|
||||
print_string("\n Capacity: 0x");
|
||||
flash_capacity_code = SFR_FLASH_DATA16;
|
||||
flash_size = 1UL << flash_capacity_code;
|
||||
print_byte(flash_capacity_code);
|
||||
print_string(" = "); print_string(get_flash_size_str()); write_char('\n');
|
||||
|
||||
// Reset slow read mode
|
||||
SFR_FLASH_MODEB = 0x0;
|
||||
SFR_FLASH_CMD_R = CMD_FREAD;
|
||||
SFR_FLASH_DUMMYCYCLES = 8;
|
||||
flash_configure_mmio();
|
||||
}
|
||||
|
||||
|
||||
@@ -186,51 +206,6 @@ void flash_write_enable(void)
|
||||
} while (!(status & 0x2));
|
||||
}
|
||||
|
||||
|
||||
void flash_dump(uint8_t len)
|
||||
{
|
||||
short status;
|
||||
do {
|
||||
status = flash_read_status();
|
||||
print_short(status);
|
||||
} while (status & 0x1);
|
||||
|
||||
// Set fast read mode
|
||||
if (dio_enabled) {
|
||||
SFR_FLASH_MODEB = 0x18;
|
||||
SFR_FLASH_CMD_R = CMD_FREAD_DIO;
|
||||
SFR_FLASH_DUMMYCYCLES = 4;
|
||||
} else {
|
||||
SFR_FLASH_MODEB = 0x0;
|
||||
SFR_FLASH_CMD_R = CMD_FREAD; // Fast read
|
||||
SFR_FLASH_DUMMYCYCLES = 8; // Add 8 dummy clocks after read?
|
||||
}
|
||||
// Read 4 bytes
|
||||
SFR_FLASH_TCONF = 4;
|
||||
while (len) {
|
||||
SFR_FLASH_ADDR16 = flash_region.addr >> 16;
|
||||
SFR_FLASH_ADDR8 = flash_region.addr >> 8;
|
||||
SFR_FLASH_ADDR0 = flash_region.addr;
|
||||
flash_region.addr += 4;
|
||||
|
||||
SFR_FLASH_EXEC_GO = 1;
|
||||
while(SFR_FLASH_EXEC_BUSY);
|
||||
|
||||
print_short(SFR_FLASH_DATA0);
|
||||
if (len == 1)
|
||||
return;
|
||||
print_short(SFR_FLASH_DATA8);
|
||||
if (len == 2)
|
||||
return;
|
||||
print_short(SFR_FLASH_DATA16);
|
||||
if (len == 3)
|
||||
return;
|
||||
print_short(SFR_FLASH_DATA24);
|
||||
|
||||
len -= 4;
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* Reads bulk data of length len from the flash memory starging at address src
|
||||
* and writes the data into a buffer pointed to by dst in XMEM
|
||||
@@ -249,8 +224,8 @@ void flash_read_bulk(__xdata uint8_t *dst)
|
||||
SFR_FLASH_DUMMYCYCLES = 4;
|
||||
} else {
|
||||
SFR_FLASH_MODEB = 0x0;
|
||||
SFR_FLASH_CMD_R = CMD_READ;
|
||||
SFR_FLASH_DUMMYCYCLES = 0;
|
||||
SFR_FLASH_CMD_R = CMD_FREAD; // Fast read
|
||||
SFR_FLASH_DUMMYCYCLES = 8; // Add 8 dummy clocks
|
||||
}
|
||||
|
||||
|
||||
@@ -283,7 +258,7 @@ void flash_read_bulk(__xdata uint8_t *dst)
|
||||
}
|
||||
|
||||
|
||||
void flash_read_security()
|
||||
void flash_read_security(void)
|
||||
{
|
||||
while (flash_read_status() & 0x1);
|
||||
|
||||
@@ -313,9 +288,11 @@ void flash_read_security()
|
||||
if (flash_region.len == 3)
|
||||
break;
|
||||
print_byte(SFR_FLASH_DATA24);
|
||||
|
||||
write_char(' ');
|
||||
flash_region.len -= 4;
|
||||
} while(flash_region.len);
|
||||
|
||||
flash_configure_mmio();
|
||||
}
|
||||
|
||||
|
||||
@@ -338,8 +315,8 @@ void flash_sector_erase(void)
|
||||
|
||||
void flash_write_bytes(__xdata uint8_t *ptr)
|
||||
{
|
||||
write_char('>'); print_long(flash_region.addr); write_char(':'); print_short(flash_region.len); write_char('-'); print_byte(*ptr); write_char('\n');
|
||||
while(1) {
|
||||
// write_char('\n'); write_char('>'); print_long(flash_region.addr); write_char(':'); print_short(flash_region.len); write_char('-'); print_byte(*ptr); // write_char('\n');
|
||||
while(1) {
|
||||
flash_write_enable();
|
||||
SFR_FLASH_CMD = CMD_PAGE_PROGRAM;
|
||||
SFR_FLASH_TCONF = 0x40 | 8 | 4; // Bytes written is 4, 8 enables write, 0x40 is unknown
|
||||
@@ -365,7 +342,6 @@ void flash_write_bytes(__xdata uint8_t *ptr)
|
||||
flash_region.len -= 4;
|
||||
flash_region.addr += 4;
|
||||
};
|
||||
|
||||
while (flash_read_status() & 0x1);
|
||||
flash_configure_mmio();
|
||||
}
|
||||
|
||||
@@ -10,4 +10,6 @@ void flash_read_security(void);
|
||||
void flash_sector_erase(void);
|
||||
void flash_read_bulk(__xdata uint8_t *dst);
|
||||
void flash_write_bytes(__xdata uint8_t *ptr);
|
||||
__code char* get_flash_size_str(void);
|
||||
|
||||
#endif
|
||||
|
||||
@@ -6,52 +6,369 @@
|
||||
// #define REGDBG
|
||||
// #define DEBUG
|
||||
|
||||
#define IPMC_USES_L3MC
|
||||
|
||||
#include <stdint.h>
|
||||
#include "rtl837x_common.h"
|
||||
#include "rtl837x_sfr.h"
|
||||
#include "rtl837x_regs.h"
|
||||
#include "rtl837x_igmp.h"
|
||||
#include "machine.h"
|
||||
|
||||
extern __code struct machine machine;
|
||||
|
||||
#include "uip.h"
|
||||
|
||||
#pragma codeseg BANK1
|
||||
#pragma constseg BANK1
|
||||
|
||||
extern __xdata uint8_t minPort;
|
||||
extern __xdata uint8_t maxPort;
|
||||
extern __xdata uint8_t nSFPPorts;
|
||||
extern __xdata uint8_t cpuPort;
|
||||
extern __xdata uint8_t isRTL8373;
|
||||
extern __xdata uint8_t sfr_data[4];
|
||||
extern __xdata struct machine_runtime machine_detected;
|
||||
extern __xdata uint8_t uip_buf[UIP_CONF_BUFFER_SIZE + 2];
|
||||
|
||||
__xdata uint16_t idx;
|
||||
|
||||
#ifdef IPMC_USES_L3MC
|
||||
struct ipmc_table_entry {
|
||||
uint8_t sip[4];
|
||||
uint8_t dip[4];
|
||||
uint16_t pmask;
|
||||
uint8_t igmp_index;
|
||||
uint8_t igmp_asic;
|
||||
};
|
||||
static __xdata struct ipmc_table_entry entry;
|
||||
#else
|
||||
struct l2mc_table_entry {
|
||||
uint8_t mac[6];
|
||||
uint16_t vlan;
|
||||
uint16_t pmask;
|
||||
uint8_t is_svl;
|
||||
uint8_t igmp_index;
|
||||
uint8_t igmp_asic;
|
||||
};
|
||||
static __xdata struct l2mc_table_entry entry;
|
||||
#endif
|
||||
|
||||
struct igmp_pkt {
|
||||
uint8_t ipv4mc_addr[6];
|
||||
uint8_t src_addr[6];
|
||||
struct rtl_tag rtl_tag;
|
||||
uint16_t ipv4_tag;
|
||||
uint8_t hlen;
|
||||
uint8_t dscp;
|
||||
uint16_t len;
|
||||
uint16_t id;
|
||||
uint16_t flags;
|
||||
uint8_t ttl;
|
||||
uint8_t protocol;
|
||||
uint16_t checksum;
|
||||
uint8_t src_ip[4];
|
||||
uint8_t dst_ip[4];
|
||||
uint8_t ip_opt;
|
||||
uint8_t ip_len;
|
||||
uint16_t ra;
|
||||
uint8_t igmp_type;
|
||||
uint8_t igmp_res1;
|
||||
uint16_t igmp_checksum;
|
||||
uint16_t igmp_res2;
|
||||
uint16_t igmp_records;
|
||||
uint8_t igmp_rtype;
|
||||
uint8_t igmp_auxlen;
|
||||
uint16_t igmp_nsrc;
|
||||
uint8_t mc_ip[4];
|
||||
};
|
||||
#define IGMP_I ((__xdata struct igmp_pkt *)&uip_buf[0])
|
||||
|
||||
|
||||
void igmp_setup(void) __banked
|
||||
{
|
||||
uint8_t i;
|
||||
print_string("igmp_setup called\n");
|
||||
// For now, forward all unkown IP-MC pkts (2 bits per port. 00: flood via floodmask, 01: drop, 10: trap, 11: to rport)
|
||||
REG_SET(RTL837X_IPV4_PORT_MC_LM_ACT, LOOKUP_MISS_FLOOD);
|
||||
REG_SET(RTL837X_IPV6_PORT_MC_LM_ACT, LOOKUP_MISS_FLOOD);
|
||||
|
||||
// For now, forward all unkown MC pkts (2 bits per port. 00: flood via floodmask, 01: drop, 10: trap, 11: to rport)
|
||||
REG_SET(RTL837X_MC_LOOKUPMISS_ACTIONS, 0x00000000); //0x4f78
|
||||
// Define ports where unknown MC addresses are flooded to:
|
||||
if (isRTL8373) {
|
||||
REG_SET(RTL837X_MC_FLOODMASK, PMASK_9); // R5368-000001f8
|
||||
} else {
|
||||
REG_SET(RTL837X_MC_FLOODMASK, PMASK_6);
|
||||
}
|
||||
REG_SET(RTL837X_IPV4_UNKN_MC_FLD_PMSK, machine_detected.isRTL8373? PMASK_9: PMASK_6);
|
||||
REG_SET(RTL837X_IPV6_UNKN_MC_FLD_PMSK, machine_detected.isRTL8373? PMASK_9: PMASK_6);
|
||||
|
||||
// Enable lookup of IPv4 MC addresses in table
|
||||
reg_bit_set(RTL837X_L2_CTRL, 3); // 0x5350
|
||||
reg_bit_set(RTL837X_L2_CTRL, L2_CTRL_LUT_IPMC_HASH);
|
||||
|
||||
// Configure per-port IGMP configuration, bits 0-10 enable MC protocol snooping,
|
||||
// bits 16-24 configure max MC group used by that port. For now all protocols are flooded (01)
|
||||
for (i = minPort; i <= maxPort; i++)
|
||||
for (i = machine.min_port; i <= machine.max_port; i++)
|
||||
REG_SET(RTL837X_IGMP_PORT_CFG + (i << 2), 0x00ff7c15);
|
||||
|
||||
/* Configure per-port IGMP operations when protocol messages are received
|
||||
* bits 0-9 enable MC protocol snooping
|
||||
* bit 10: Enable dynamic router port learning
|
||||
* bit 11: Enable MRP (Multicast Routing Protocol)
|
||||
* bit 12: Allow fast leave
|
||||
* bit 13: Allow IGMP reporting
|
||||
* bit 14: Allow queries
|
||||
* bits 16-24 configure max MC group used by that port.
|
||||
* Operations for IGMP packets are:
|
||||
* 00: handle in HW by ASIC
|
||||
* 01: flood
|
||||
* 10: trap
|
||||
* 11: drop
|
||||
* Bits 0-1: IGMPv1, 2-3: IGMPv2, 4-5: IGMPv3, 6-7: MLDv1 (for IPv6), 8-9: MLDv2
|
||||
* For now the IGMP protocols are flooded (01), MLD which MAC-based is handled by ASIC
|
||||
* All messages are allowed and maximum MC group is 0xff
|
||||
*/
|
||||
for (i = machine.min_port; i <= machine.max_port; i++)
|
||||
REG_SET(RTL837X_IGMP_PORT_CFG + (i << 2), IGMP_MAX_GROUP | IGMP_PROTOCOL_ENABLE | IGMP_FLOOD);
|
||||
|
||||
/* // Allow all physical ports to be dynamic router ports
|
||||
reg_read_m(RTL837X_IGMP_ROUTER_PORT);
|
||||
if (machine_detected.isRTL8373) {
|
||||
REG_WRITE(RTL837X_IGMP_ROUTER_PORT, PMASK_9 >> 8, PMASK_9 & 0xff, sfr_data[1], sfr_data[0]);
|
||||
} else {
|
||||
REG_WRITE(RTL837X_IGMP_ROUTER_PORT, PMASK_6 >> 8, PMASK_6 & 0xff, sfr_data[1], sfr_data[0]);
|
||||
}
|
||||
*/
|
||||
}
|
||||
|
||||
|
||||
void igmp_enable(void) __banked
|
||||
{
|
||||
uint8_t i;
|
||||
print_string("igmp_enable called\n");
|
||||
// Configure trapping of unhandled IGMP protocol packets to CPU
|
||||
REG_SET(RTL837X_IGMP_TRAP_CFG, IGMP_CPU_PORT | IGMP_TRAP_PRIORITY);
|
||||
|
||||
REG_SET(0x50bc, 00010007); // Trap control?
|
||||
|
||||
// Drop unknown MC messages
|
||||
REG_SET(RTL837X_MC_LOOKUPMISS_ACTIONS, 0x00015540); //0x4f78
|
||||
// Drop unknown IP-MC packets
|
||||
REG_SET(RTL837X_IPV4_PORT_MC_LM_ACT, machine_detected.isRTL8373? LOOKUP_MISS_DROP_9: LOOKUP_MISS_DROP_6);
|
||||
// REG_SET(RTL837X_IPV6_PORT_MC_LM_ACT, machine_detected.isRTL8373? LOOKUP_MISS_DROP_9: LOOKUP_MISS_DROP_6);
|
||||
|
||||
// Configure per-port IGMP configuration, bits 0-10 enable MC protocol snooping,
|
||||
// bits 16-24 configure max MC group used by that port. Trap to CPU (10)
|
||||
for (i = minPort; i <= maxPort; i++)
|
||||
REG_SET(RTL837X_IGMP_PORT_CFG + (i << 2), 0x00ff7c2a); // 0x00ff7000: Handling by ASIC (00)
|
||||
for (uint8_t i = machine.min_port; i <= machine.max_port; i++) {
|
||||
REG_SET(RTL837X_IGMP_PORT_CFG + (i << 2), IGMP_MAX_GROUP | IGMP_PROTOCOL_ENABLE | IGMP_TRAP);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
* Configures the IGMP static router port(s) that will receive all IGMP
|
||||
* Report and Leave messages
|
||||
*/
|
||||
void igmp_router_port_set(uint16_t pmask) __banked
|
||||
{
|
||||
print_string("igmp_router_port_set: "); print_short(pmask); print_string(", currently set to:\n");
|
||||
reg_read_m(RTL837X_IGMP_ROUTER_PORT);
|
||||
print_sfr_data(); write_char('\n');
|
||||
REG_WRITE(RTL837X_IGMP_ROUTER_PORT, sfr_data[0], sfr_data[1], pmask >> 8, pmask & 0xff);
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
* IGMP show the current entries and state
|
||||
*/
|
||||
void igmp_show(void) __banked
|
||||
{
|
||||
print_string("igmp_show called\n");
|
||||
for (uint8_t i = machine.min_port; i <= machine.max_port; i++) {
|
||||
write_char('0' + i); write_char(':');
|
||||
reg_read_m(RTL837X_IGMP_PORT_CFG + (i << 2));
|
||||
print_sfr_data();
|
||||
write_char('\n');
|
||||
}
|
||||
// TODO: print all L3MC entries in the table
|
||||
}
|
||||
|
||||
|
||||
#ifdef IPMC_USES_L3MC
|
||||
void entry_to_l3mc(void)
|
||||
{
|
||||
REG_WRITE(RTL837x_TBL_DATA_IN_A, entry.sip[0], entry.sip[1], entry.sip[2], entry.sip[3]);
|
||||
REG_WRITE(RTL837x_TBL_DATA_IN_B, ((entry.pmask & 0x3) << 6) | (entry.dip[0] & 0xf) | 0x10, entry.dip[1], entry.dip[2], entry.dip[3]);
|
||||
REG_WRITE(RTL837x_TBL_DATA_IN_C, 0x00, entry.igmp_asic & 1, entry.igmp_index, entry.pmask >> 2);
|
||||
}
|
||||
#else
|
||||
void entry_to_l2mc(void)
|
||||
{
|
||||
// R5cb8-5e004201 R5cbc-20010100 R5cc0-00000020 R5cac-00000403
|
||||
REG_WRITE(RTL837x_TBL_DATA_IN_A, entry.mac[2], entry.mac[3], entry.mac[4], entry.mac[5]);
|
||||
REG_WRITE(RTL837x_TBL_DATA_IN_B, 0x20 | ((entry.pmask & 0x3) << 6) | (entry.vlan >> 8), entry.vlan & 0xff, entry.mac[0], entry.mac[1]);
|
||||
REG_WRITE(RTL837x_TBL_DATA_IN_C, 0x00, entry.igmp_asic & 1, entry.igmp_index, entry.pmask >> 2);
|
||||
}
|
||||
#endif
|
||||
|
||||
|
||||
void igmp_packet_handler(void) __banked
|
||||
{
|
||||
// By default we do not send anything out
|
||||
uip_len = 0;
|
||||
|
||||
#ifdef DEBUG
|
||||
print_string("\nIPv4 MC packet:\n");
|
||||
for (uint8_t i = 0; i < 80; i++) {
|
||||
print_byte(uip_buf[i]);
|
||||
write_char(' ');
|
||||
}
|
||||
write_char('\n');
|
||||
#endif
|
||||
if (IGMP_I->protocol != 2)
|
||||
return;
|
||||
#ifdef DEBUG
|
||||
print_string("Found IGMP, type: "); print_byte(IGMP_I->igmp_type); write_char('\n');
|
||||
#endif
|
||||
// We react to IGMPv1/v2 and v3 membership reports
|
||||
if (!(IGMP_I->igmp_type == 0x12 || IGMP_I->igmp_type == 0x16 || IGMP_I->igmp_type == 0x22))
|
||||
return;
|
||||
#ifdef DEBUG
|
||||
print_string("IGMP membership report, type "); print_byte(IGMP_I->igmp_rtype); write_char('\n');
|
||||
#endif
|
||||
|
||||
#ifdef IPMC_USES_L3MC
|
||||
memset(&entry, 0, sizeof(struct ipmc_table_entry));
|
||||
// For IPv4 MC, the Source-IP is 0.0.0.0
|
||||
entry.sip[0] = 0x00; entry.sip[1] = 0x00; entry.sip[2] = 0x00; entry.sip[3] = 0x00;
|
||||
// For IPv4 MC, the Destination-IP is the IPv4 MC address
|
||||
entry.dip[0] = IGMP_I->mc_ip[0]; entry.dip[1] = IGMP_I->mc_ip[1]; entry.dip[2] = IGMP_I->mc_ip[2]; entry.dip[3] = IGMP_I->mc_ip[3];
|
||||
entry_to_l3mc();
|
||||
#else
|
||||
/* The L2 Multicast MAC for IP-Multicast is 01:00:5e:xx:yy:zz, where
|
||||
* xx = MC_IP[1] & 0x7f
|
||||
* yy = MC_IP[2]
|
||||
* zz = MC_IP[3]
|
||||
*/
|
||||
memset(&entry, 0, sizeof(struct l2mc_table_entry));
|
||||
entry.mac[0] = 0x01; entry.mac[1] = 0x00; entry.mac[2] = 0x5e;
|
||||
entry.mac[3] = IGMP_I->mc_ip[1] & 0x7f; entry.mac[4] = IGMP_I->mc_ip[2]; entry.mac[5] = IGMP_I->mc_ip[3];
|
||||
entry.vlan = 1; //TODO: Get this out of the packet and compare with VLAN table!
|
||||
entry_to_ipmc();
|
||||
#endif
|
||||
// Wait for any pending Table operations to end
|
||||
do {
|
||||
reg_read_m(RTL837X_TBL_CTRL);
|
||||
} while (sfr_data[3] & 1);
|
||||
|
||||
reg_read_m(RTL837x_TBL_DATA_0);
|
||||
#ifdef DEBUG
|
||||
print_sfr_data();
|
||||
#endif
|
||||
sfr_data[2] &= 0x3f; // Sets the Read-method to 0 (why MAC-lookup?) and clear the CLEAR-Entry bit
|
||||
sfr_data[1] &= 0xf8;
|
||||
reg_write_m(RTL837x_TBL_DATA_0);
|
||||
#ifdef DEBUG
|
||||
print_string(" l2 ctrl now: ");
|
||||
print_sfr_data();
|
||||
#endif
|
||||
// First try to find entry to see whether it needs to be updated
|
||||
REG_WRITE(RTL837X_TBL_CTRL, 0x00, 0x00, TBL_L2_UNICAST, TBL_EXECUTE);
|
||||
do {
|
||||
reg_read_m(RTL837X_TBL_CTRL);
|
||||
} while (sfr_data[3] & 0x1);
|
||||
#ifdef DEBUG
|
||||
print_string("\nsearch done\n");
|
||||
print_string("Table data searched:\n");
|
||||
reg_read_m(RTL837x_TBL_DATA_IN_A);
|
||||
print_sfr_data(); write_char(' ');
|
||||
reg_read_m(RTL837x_TBL_DATA_IN_B);
|
||||
print_sfr_data(); write_char(' ');
|
||||
reg_read_m(RTL837x_TBL_DATA_IN_C);
|
||||
print_sfr_data(); write_char('\n');
|
||||
print_string("Table data gotten:\n");
|
||||
reg_read_m(RTL837x_L2_DATA_OUT_A); write_char(' ');
|
||||
print_sfr_data();
|
||||
reg_read_m(RTL837x_L2_DATA_OUT_B);
|
||||
print_sfr_data(); write_char(' ');
|
||||
reg_read_m(RTL837x_L2_DATA_OUT_C);
|
||||
print_sfr_data(); write_char('\n');
|
||||
print_string("Result: ");
|
||||
#endif
|
||||
reg_read_m(RTL837x_TBL_DATA_0);
|
||||
#ifdef DEBUG
|
||||
print_sfr_data();
|
||||
#endif
|
||||
idx = ((sfr_data[2] & 0xf) << 8) | sfr_data[3];
|
||||
if (IGMP_I->igmp_rtype == 0x4) {// Join group
|
||||
if (sfr_data[2] & 0x10) {
|
||||
print_string("\nIGMP-Entry FOUND\n");
|
||||
reg_read_m(RTL837x_L2_DATA_OUT_B);
|
||||
entry.pmask = sfr_data[0] >> 6;
|
||||
reg_read_m(RTL837x_L2_DATA_OUT_C);
|
||||
entry.pmask |= ((uint16_t)sfr_data[3]) << 2;
|
||||
}
|
||||
// Update (found) entry with portmask from trapped Packet
|
||||
entry.pmask |= (1L << (IGMP_I->rtl_tag.pmask >> 8)); // Swap bytes from network order, only 4 LSB count
|
||||
// print_string("\nPort-Mask: "); print_short(entry.pmask); write_char('\n');
|
||||
} else if (IGMP_I->igmp_rtype == 0x3){ // Leave group
|
||||
if (sfr_data[2] & 0x10) {
|
||||
print_string("\nIGMP_Entry FOUND\n");
|
||||
reg_read_m(RTL837x_L2_DATA_OUT_B);
|
||||
entry.pmask = sfr_data[0] >> 6;
|
||||
reg_read_m(RTL837x_L2_DATA_OUT_C);
|
||||
entry.pmask |= ((uint16_t)sfr_data[3]) << 2;
|
||||
#ifdef DEBUG
|
||||
print_string("Portmask: ");
|
||||
print_short(entry.pmask);
|
||||
print_string("Index: ");
|
||||
print_short(idx);
|
||||
write_char('\n');
|
||||
#endif
|
||||
// Remove portmask of IGMP packet from entry
|
||||
entry.pmask &= ~(1L << (IGMP_I->rtl_tag.pmask >> 8)); // Swap bytes from network order, only 4 LSB count
|
||||
// print_string("\nPort-Mask: "); print_short(entry.pmask); write_char('\n');
|
||||
} else {
|
||||
print_string("IGMP Entry already deleted\n");
|
||||
return;
|
||||
}
|
||||
if (!entry.pmask && idx) { // No more ports in that group and an actual entry?
|
||||
// Delete Entry
|
||||
reg_read_m(RTL837x_TBL_DATA_0);
|
||||
sfr_data[1] |= 0x04; // Clear entry
|
||||
reg_write_m(RTL837x_TBL_DATA_0);
|
||||
REG_WRITE(RTL837X_TBL_CTRL, idx >> 8, idx & 0xff, TBL_L2_UNICAST, TBL_WRITE | TBL_EXECUTE);
|
||||
do {
|
||||
reg_read_m(RTL837X_TBL_CTRL);
|
||||
} while (sfr_data[3] & 0x1);
|
||||
print_string("IGMP Entry deleted\n");
|
||||
return;
|
||||
}
|
||||
} else { // Unknown message: ignore.
|
||||
return;
|
||||
}
|
||||
|
||||
if (!entry.pmask)
|
||||
return;
|
||||
print_string("Updating IGMP entry\n");
|
||||
// Write the updated entry
|
||||
#ifdef IPMC_USES_L3MC
|
||||
entry_to_l3mc();
|
||||
#else
|
||||
entry_to_ipmc();
|
||||
#endif
|
||||
reg_read_m(RTL837x_TBL_DATA_0);
|
||||
#ifdef DEBUG
|
||||
print_sfr_data();
|
||||
#endif
|
||||
sfr_data[2] &= 0x3f; // Set the Read-method to 0 and clear the CLEAR-Entry bit
|
||||
sfr_data[1] &= 0xf8;
|
||||
reg_write_m(RTL837x_TBL_DATA_0);
|
||||
#ifdef DEBUG
|
||||
print_string(" l2 ctrl now: ");
|
||||
print_sfr_data();
|
||||
#endif
|
||||
reg_read_m(RTL837X_TBL_CTRL);
|
||||
REG_WRITE(RTL837X_TBL_CTRL, sfr_data[0], sfr_data[1], TBL_L2_UNICAST, TBL_WRITE | TBL_EXECUTE);
|
||||
do {
|
||||
reg_read_m(RTL837X_TBL_CTRL);
|
||||
} while (sfr_data[3] & 0x1);
|
||||
#ifdef DEBUG
|
||||
print_string("\nupdate done\n");
|
||||
print_string("Table data written:\n");
|
||||
reg_read_m(RTL837x_TBL_DATA_IN_A);
|
||||
print_sfr_data(); write_char(' ');
|
||||
reg_read_m(RTL837x_TBL_DATA_IN_B);
|
||||
print_sfr_data(); write_char(' ');
|
||||
reg_read_m(RTL837x_TBL_DATA_IN_C);
|
||||
print_sfr_data(); write_char('\n');
|
||||
print_string("Result: ");
|
||||
reg_read_m(RTL837x_TBL_DATA_0);
|
||||
print_sfr_data();
|
||||
#endif
|
||||
}
|
||||
|
||||
@@ -5,5 +5,8 @@
|
||||
|
||||
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;
|
||||
void igmp_show(void) __banked;
|
||||
|
||||
#endif
|
||||
|
||||
@@ -0,0 +1,296 @@
|
||||
/*
|
||||
* This is a driver implementation for the IGMP features for the RTL827x platform
|
||||
* This code is in the Public Domain
|
||||
*/
|
||||
|
||||
// #define REGDBG
|
||||
// #define DEBUG
|
||||
|
||||
#define IPMC_USES_L3MC
|
||||
|
||||
#include <stdint.h>
|
||||
#include "rtl837x_common.h"
|
||||
#include "rtl837x_sfr.h"
|
||||
#include "rtl837x_regs.h"
|
||||
#include "rtl837x_leds.h"
|
||||
#include "machine.h"
|
||||
|
||||
extern __code struct machine machine;
|
||||
|
||||
#include "uip.h"
|
||||
|
||||
#pragma codeseg BANK2
|
||||
#pragma constseg BANK2
|
||||
|
||||
extern __xdata uint8_t sfr_data[4];
|
||||
|
||||
void leds_dump(void) __banked
|
||||
{
|
||||
print_string("RTL837X_PIN_MUX_0: "); print_reg(RTL837X_PIN_MUX_0); write_char('\n');
|
||||
print_string("RTL837X_REG_LED_GLB_IO_EN: "); print_reg(RTL837X_REG_LED_GLB_IO_EN); write_char('\n');
|
||||
print_string("RTL837X_REG_LED1_0_SET0: "); print_reg(RTL837X_REG_LED1_0_SET0); write_char('\n');
|
||||
print_string("RTL837X_REG_LED3_2_SET0: "); print_reg(RTL837X_REG_LED3_2_SET0); write_char('\n');
|
||||
print_string("RTL837X_REG_LED1_0_SET1: "); print_reg(RTL837X_REG_LED1_0_SET1); write_char('\n');
|
||||
print_string("RTL837X_REG_LED3_2_SET1: "); print_reg(RTL837X_REG_LED3_2_SET1); write_char('\n');
|
||||
print_string("RTL837X_REG_LED1_0_SET2: "); print_reg(RTL837X_REG_LED1_0_SET2); write_char('\n');
|
||||
print_string("RTL837X_REG_LED3_2_SET2: "); print_reg(RTL837X_REG_LED3_2_SET2); write_char('\n');
|
||||
print_string("RTL837X_REG_LED1_0_SET3: "); print_reg(RTL837X_REG_LED1_0_SET3); write_char('\n');
|
||||
print_string("RTL837X_REG_LED3_0_SET1: "); print_reg(RTL837X_REG_LED3_0_SET1); write_char('\n');
|
||||
print_string("RTL837X_REG_LED3_0_SET3: "); print_reg(RTL837X_REG_LED3_0_SET3); write_char('\n');
|
||||
print_string("RTL837X_LED_PORT_SET_SEL: "); print_reg(RTL837X_LED_PORT_SET_SEL); write_char('\n');
|
||||
print_string("RTL837X_REG_LED_GLB_MUX_1: "); print_reg(RTL837X_REG_LED_GLB_MUX_1); write_char('\n');
|
||||
print_string("RTL837X_REG_LED_GLB_MUX_2: "); print_reg(RTL837X_REG_LED_GLB_MUX_2); write_char('\n');
|
||||
print_string("RTL837X_REG_LED_GLB_MUX_3: "); print_reg(RTL837X_REG_LED_GLB_MUX_3); write_char('\n');
|
||||
print_string("RTL837X_REG_LED_GLB_MUX_4: "); print_reg(RTL837X_REG_LED_GLB_MUX_4); write_char('\n');
|
||||
print_string("RTL837X_REG_LED_GLB_MUX_5: "); print_reg(RTL837X_REG_LED_GLB_MUX_5); write_char('\n');
|
||||
print_string("RTL837X_REG_LED_GLB_MUX_6: "); print_reg(RTL837X_REG_LED_GLB_MUX_6); write_char('\n');
|
||||
print_string("RTL837X_REG_LED_GLB_ACTIVE: "); print_reg(RTL837X_REG_LED_GLB_ACTIVE); write_char('\n');
|
||||
print_string("LED pad Configuration:\n");
|
||||
for (uint8_t i = 0; i < 28; i++) {
|
||||
print_byte(i);
|
||||
write_char(' ');
|
||||
}
|
||||
write_char('\n');
|
||||
for (uint8_t i = 0; i < 28; i++) {
|
||||
switch (i % 5) {
|
||||
case 0: // 0
|
||||
reg_read_m(RTL837X_REG_LED_GLB_MUX_1 + (i / 5) * 4);
|
||||
print_byte(sfr_data[3] & 0x3f);
|
||||
break;
|
||||
case 1: // 6
|
||||
print_byte(((sfr_data[3] >> 6) | (sfr_data[2] << 2)) & 0x3f);
|
||||
break;
|
||||
case 2: // 12
|
||||
print_byte(((sfr_data[1] << 4) | (sfr_data[2] >> 4)) & 0x3f);
|
||||
break;
|
||||
case 3: // 18
|
||||
print_byte((sfr_data[1] >> 2) & 0x3f);
|
||||
break;
|
||||
case 4: // 24
|
||||
print_byte(sfr_data[0] & 0x3f);
|
||||
break;
|
||||
}
|
||||
write_char(' ');
|
||||
}
|
||||
write_char('\n');
|
||||
print_string("LED-set Configuration:\n");
|
||||
print_string("LED-ID\t\t0\t\t1\t\t2\t\t3\n");
|
||||
for (__xdata uint8_t set = 0; set < 4; set++) {
|
||||
print_string("SET "); write_char('0' + set); write_char(':');
|
||||
for (__xdata uint8_t ledid = 0; ledid < 4; ledid++) {
|
||||
print_string("\t ");
|
||||
uint8_t b;
|
||||
if (set < 2) {
|
||||
reg_read_m(RTL837X_REG_LED3_0_SET1);
|
||||
b = sfr_data[3-((set << 1) + (ledid >> 1))];
|
||||
print_byte(ledid & 1 ? b >> 4 : b & 0xf);
|
||||
} else {
|
||||
reg_read_m(RTL837X_REG_LED3_0_SET3);
|
||||
b = sfr_data[3-(((set-2) << 1) + (ledid >> 1))];
|
||||
print_byte(ledid & 1 ? b >> 4 : b & 0xf);
|
||||
}
|
||||
reg_read_m(RTL837X_REG_LED1_0_SET0 - set * 8 - ((ledid >> 1) * 4));
|
||||
if (! (ledid & 1)) { // LEDID 0, 2
|
||||
print_byte(sfr_data[2]); print_byte(sfr_data[3]);
|
||||
} else {
|
||||
print_byte(sfr_data[0]); print_byte(sfr_data[1]);
|
||||
}
|
||||
}
|
||||
write_char('\n');
|
||||
}
|
||||
for (uint8_t i = machine.min_port; i <= machine.max_port; i++) {
|
||||
reg_read_m(RTL837X_LED_PORT_SET_SEL);
|
||||
__xdata uint8_t set = sfr_data[3 - (i >> 2)];
|
||||
set = (set >> ((i & 3) << 1));
|
||||
print_string("Port "); write_char('0' + i); print_string(": SET ");
|
||||
write_char('0' + set);
|
||||
print_string(": ");
|
||||
for (__xdata uint8_t ledid = 0; ledid < 4; ledid++) {
|
||||
write_char('(');
|
||||
reg_read_m(RTL837X_REG_LED1_0_SET0 - set * 8 - ((ledid >> 1) * 4));
|
||||
if (ledid & 1) { // LEDID 1, 3
|
||||
sfr_data[2] = sfr_data[0];
|
||||
sfr_data[3] = sfr_data[1];
|
||||
}
|
||||
if (sfr_data[3] & 0x01)
|
||||
print_string(" 2G5");
|
||||
if (sfr_data[3] & 0x02)
|
||||
print_string(" TWO_1G");
|
||||
if (sfr_data[3] & 0x04)
|
||||
print_string(" 1G");
|
||||
if (sfr_data[3] & 0x08)
|
||||
print_string(" 500M");
|
||||
if (sfr_data[3] & 0x10)
|
||||
print_string(" 100M");
|
||||
if (sfr_data[3] & 0x20)
|
||||
print_string(" 10M");
|
||||
if (sfr_data[3] & 0x40)
|
||||
print_string(" LINK");
|
||||
if (sfr_data[3] & 0x80)
|
||||
print_string(" LINK_FLASH");
|
||||
if (sfr_data[2] & 0x01)
|
||||
print_string(" ACT");
|
||||
if (sfr_data[2] & 0x02)
|
||||
print_string(" RX");
|
||||
if (sfr_data[2] & 0x04)
|
||||
print_string(" TX");
|
||||
if (sfr_data[2] & 0x08)
|
||||
print_string(" COL");
|
||||
if (sfr_data[2] & 0x10)
|
||||
print_string(" DUPLEX");
|
||||
if (sfr_data[2] & 0x20)
|
||||
print_string(" TRAINING");
|
||||
if (sfr_data[2] & 0x40)
|
||||
print_string(" MASTER");
|
||||
__xdata uint8_t b;
|
||||
if (set < 2) {
|
||||
reg_read_m(RTL837X_REG_LED3_0_SET1);
|
||||
b = sfr_data[3-((set << 1) + (ledid >> 1))];
|
||||
} else {
|
||||
reg_read_m(RTL837X_REG_LED3_0_SET3);
|
||||
b = sfr_data[3-(((set-2) << 1) + (ledid >> 1))];
|
||||
}
|
||||
b = ledid & 1 ? b >> 4 : b & 0xf;
|
||||
if (b & 0x1)
|
||||
print_string(" 10G");
|
||||
if (b & 0x2)
|
||||
print_string(" TWO_5G");
|
||||
if (b & 0x4)
|
||||
print_string(" 5G");
|
||||
if (b & 0x8)
|
||||
print_string(" TWO_2G5");
|
||||
print_string("), ");
|
||||
}
|
||||
write_char('\n');
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
void leds_setup(void) __banked
|
||||
{
|
||||
print_string("leds_setup called\n");
|
||||
REG_SET(RTL837X_REG_LED_MODE, 0x0021e6b0);
|
||||
|
||||
// Disable RLDP (Realtek Loop Detection Protocol) LEDs on loop detection
|
||||
reg_read_m(RTL837X_REG_LED_RLDP_1);
|
||||
sfr_mask_data(0, 0x03, 0);
|
||||
reg_write_m(RTL837X_REG_LED_RLDP_1);
|
||||
|
||||
// Set up all Port-LEDs to belong to RLDP
|
||||
sfr_data[3] = sfr_data[2] = sfr_data[1] = sfr_data[0] = 0;
|
||||
for (uint8_t i = machine.min_port; i <= (machine.max_port > 7 ? 7 : machine.max_port); i++)
|
||||
sfr_data[3 - (i >> 2)] |= i & 1 ? 0xf0 : 0x0f;
|
||||
reg_write_m(RTL837X_REG_LED_RLDP_2);
|
||||
if (machine.max_port == 8)
|
||||
REG_SET(RTL837X_REG_LED_RLDP_3, 0x0000000f); // Port 8
|
||||
|
||||
// Configure high LEDs 27-29: mux and LED enable
|
||||
if (machine.high_leds.mux & LED_27)
|
||||
reg_bit_set(RTL837X_PIN_MUX_0, 27);
|
||||
else
|
||||
reg_bit_clear(RTL837X_PIN_MUX_0, 27);
|
||||
|
||||
if (machine.high_leds.mux & LED_28)
|
||||
reg_bit_set(RTL837X_PIN_MUX_0, 28);
|
||||
else
|
||||
reg_bit_clear(RTL837X_PIN_MUX_0, 28);
|
||||
|
||||
if (machine.high_leds.mux & LED_29)
|
||||
reg_bit_set(RTL837X_PIN_MUX_0, 29);
|
||||
else
|
||||
reg_bit_clear(RTL837X_PIN_MUX_0, 29);
|
||||
|
||||
if (machine.high_leds.enable & LED_27)
|
||||
reg_bit_set(RTL837X_REG_LED_GLB_IO_EN, 27);
|
||||
else
|
||||
reg_bit_clear(RTL837X_REG_LED_GLB_IO_EN, 27);
|
||||
|
||||
if (machine.high_leds.enable & LED_28)
|
||||
reg_bit_set(RTL837X_REG_LED_GLB_IO_EN, 28);
|
||||
else
|
||||
reg_bit_clear(RTL837X_REG_LED_GLB_IO_EN, 28);
|
||||
|
||||
if (machine.high_leds.enable & LED_29)
|
||||
reg_bit_set(RTL837X_REG_LED_GLB_IO_EN, 29);
|
||||
else
|
||||
reg_bit_clear(RTL837X_REG_LED_GLB_IO_EN, 29);
|
||||
|
||||
// Configure the LED-mux
|
||||
if (machine.led_mux_custom) {
|
||||
print_string("Configuring custom LED-muxes: ");
|
||||
for (uint8_t i = 0; i < 28; i++) {
|
||||
switch (i % 5) {
|
||||
case 0: // 0
|
||||
sfr_data[3] = machine.led_mux[i];
|
||||
break;
|
||||
case 1: // 6
|
||||
sfr_data[3] |= machine.led_mux[i] << 6;
|
||||
sfr_data[2] = machine.led_mux[i] >> 2;
|
||||
break;
|
||||
case 2: // 12
|
||||
sfr_data[2] |= machine.led_mux[i] << 4;
|
||||
sfr_data[1] = machine.led_mux[i] >> 4;
|
||||
break;
|
||||
case 3: // 18
|
||||
sfr_data[1] |= machine.led_mux[i] << 2;
|
||||
break;
|
||||
case 4: // 24
|
||||
sfr_data[0] = machine.led_mux[i];
|
||||
print_sfr_data(); write_char(' ');
|
||||
reg_write_m(RTL837X_REG_LED_GLB_MUX_1 + (i / 5) * 4);
|
||||
break;
|
||||
}
|
||||
write_char(' ');
|
||||
}
|
||||
sfr_data[0] = 0; sfr_data[1] &= 0xf;
|
||||
print_sfr_data(); write_char('\n');
|
||||
reg_write_m(RTL837X_REG_LED_GLB_MUX_6);
|
||||
}
|
||||
|
||||
// Configure the LED-set of a port
|
||||
sfr_data[3] = sfr_data[2] = sfr_data[1] = sfr_data[0] = 0;
|
||||
for (uint8_t i = machine.min_port; i <= machine.max_port; i++)
|
||||
sfr_data[3 - (i >> 2)] |= machine.port_led_set[i] << ((i & 3) << 1);
|
||||
reg_write_m(RTL837X_LED_PORT_SET_SEL);
|
||||
|
||||
// Configure the LED-sets
|
||||
sfr_data[3] = sfr_data[2] = sfr_data[1] = sfr_data[0] = 0;
|
||||
reg_write_m(RTL837X_REG_LED3_0_SET1);
|
||||
reg_write_m(RTL837X_REG_LED3_0_SET3);
|
||||
__code uint8_t * __xdata lptr = &machine.led_sets[0][0];
|
||||
for (__xdata uint8_t set = 0; set < 4; set++) {
|
||||
sfr_data[0] = *(lptr + 5);
|
||||
sfr_data[1] = *(lptr + 4);
|
||||
sfr_data[2] = *(lptr + 1);
|
||||
sfr_data[3] = *(lptr);
|
||||
reg_write_m(RTL837X_REG_LED1_0_SET0 - set * 8);
|
||||
|
||||
if (set < 2) {
|
||||
reg_read_m(RTL837X_REG_LED3_0_SET1);
|
||||
sfr_data[3 - (set << 1)] = (*(lptr + 6) << 4) | (*(lptr + 2));
|
||||
reg_write_m(RTL837X_REG_LED3_0_SET1);
|
||||
} else {
|
||||
reg_read_m(RTL837X_REG_LED3_0_SET3);
|
||||
sfr_data[3 - (set << 1)] = (*(lptr + 6) << 4) | (*(lptr + 2));
|
||||
reg_write_m(RTL837X_REG_LED3_0_SET3);
|
||||
}
|
||||
lptr += 8;
|
||||
sfr_data[0] = *(lptr + 5);
|
||||
sfr_data[1] = *(lptr + 4);
|
||||
sfr_data[2] = *(lptr + 1);
|
||||
sfr_data[3] = *(lptr);
|
||||
reg_write_m(RTL837X_REG_LED1_0_SET0 - set * 8 - 4);
|
||||
|
||||
if (set < 2) {
|
||||
reg_read_m(RTL837X_REG_LED3_0_SET1);
|
||||
sfr_data[2 - (set << 1)] = (*(lptr + 6) << 4) | (*(lptr + 2));
|
||||
reg_write_m(RTL837X_REG_LED3_0_SET1);
|
||||
} else {
|
||||
reg_read_m(RTL837X_REG_LED3_0_SET3);
|
||||
sfr_data[2 - (set << 1)] = (*(lptr + 6) << 4) | (*(lptr + 2));
|
||||
reg_write_m(RTL837X_REG_LED3_0_SET3);
|
||||
}
|
||||
lptr += 8;
|
||||
}
|
||||
print_string("leds_setup done\n");
|
||||
}
|
||||
@@ -0,0 +1,28 @@
|
||||
#ifndef _RTL837X_LEDS_H_
|
||||
#define _RTL837X_LEDS_H_
|
||||
|
||||
#define LEDS_2G5 0x00001
|
||||
#define LEDS_TWO_PAIR_1G 0x00002
|
||||
#define LEDS_1G 0x00004
|
||||
#define LEDS_500M 0x00008
|
||||
#define LEDS_100M 0x00010
|
||||
#define LEDS_10M 0x00020
|
||||
#define LEDS_LINK 0x00040
|
||||
#define LEDS_LINK_FLASH 0x00080
|
||||
#define LEDS_ACT 0x00100
|
||||
#define LEDS_RX 0x00200
|
||||
#define LEDS_TX 0x00400
|
||||
#define LEDS_COL 0x00800
|
||||
#define LEDS_DUPLEX 0x01000
|
||||
#define LEDS_TRAINING 0x02000
|
||||
#define LEDS_MASTER 0x04000
|
||||
#define LEDS_10G 0x10000
|
||||
#define LEDS_TWO_PAIR_5G 0x20000
|
||||
#define LEDS_5G 0x40000
|
||||
#define LEDS_TWO_PAIR_2G5 0x80000
|
||||
|
||||
#include <stdint.h>
|
||||
void leds_dump(void) __banked;
|
||||
void leds_setup(void) __banked;
|
||||
|
||||
#endif
|
||||
@@ -15,14 +15,26 @@
|
||||
#include "rtl837x_regs.h"
|
||||
#include "rtl837x_phy.h"
|
||||
#include "phy.h"
|
||||
#include "machine.h"
|
||||
|
||||
#pragma codeseg BANK1
|
||||
#pragma constseg BANK1
|
||||
#pragma codeseg BANK2
|
||||
#pragma constseg BANK2
|
||||
|
||||
extern __code uint16_t bit_mask[16];
|
||||
extern __code const struct machine machine;
|
||||
extern __xdata struct machine_runtime machine_detected;
|
||||
|
||||
__xdata struct phy_settings phy_settings;
|
||||
|
||||
__code uint16_t rtl8224_ca[42] = {
|
||||
// SDS-settings for RTL8224 first SerDes which is connected to the RTL837x-SOC.
|
||||
// Array contrains register-value, and SDS-CMD, which already encodes (sds_index, page, reg).
|
||||
// This array is used in phy_config_8224().
|
||||
//
|
||||
// Note: Adding `Swapping the RX for N-devices`-setting on the end of the array, didn't work.
|
||||
// Setting will apply but still no packets flow.
|
||||
// Settings are `0x2000, 0xc10c`,
|
||||
__code uint16_t rtl8224_sds0_setttings[42] = {
|
||||
// SDS_DATA, SDS_CMD
|
||||
0x4480, 0xc842,
|
||||
0x0400, 0xc9c2,
|
||||
0x6d02, 0xcc42,
|
||||
@@ -46,48 +58,30 @@ __code uint16_t rtl8224_ca[42] = {
|
||||
0, 0
|
||||
};
|
||||
|
||||
__code uint16_t rtl8224_cb[60] = {
|
||||
0xc45c, 0xc18c, 0x8040,
|
||||
0x0030, 0xc040, 0x8040,
|
||||
0x0010, 0xc040, 0x8040,
|
||||
0x0050, 0xc040, 0x8040,
|
||||
0x00d0, 0xc040, 0x8040,
|
||||
0x0cd0, 0xc040, 0x8040,
|
||||
0x04d0, 0xc040, 0x8040,
|
||||
0x04d0, 0xc040, 0x8040,
|
||||
0x0cd0, 0xc040, 0x8040,
|
||||
0x00d0, 0xc040, 0x8040,
|
||||
0x00d0, 0xc040, 0x8040,
|
||||
0x0050, 0xc040, 0x8040,
|
||||
0x0010, 0xc040, 0x8040,
|
||||
0x0010, 0xc040, 0x8040,
|
||||
0x0030, 0xc040, 0x8040,
|
||||
0x0000, 0xc040, 0x803e,
|
||||
0x000b, 0xc03e, 0x803e,
|
||||
0x0000, 0xc03e, 0x8042,
|
||||
0x4906, 0xc042, 0x82ec,
|
||||
0xffff,0,0
|
||||
};
|
||||
|
||||
void rtl8224_phy_enable(void) __banked
|
||||
{
|
||||
uint16_t pval;
|
||||
|
||||
// p001e.0a90:00f3 R02f8-000000f3 R02f4-000000fc P000001.1e000a90:00fc
|
||||
print_string("\r\nrtl8224_phy_enable called\r\n");
|
||||
phy_read(RTL8224_PHY_ID, 0x1e, 0xa90);
|
||||
phy_read(RTL8224_PHY_ID, PHY_MMD30, RTL837X_CFG_PHY_MDI_REVERSE);
|
||||
pval = SFR_DATA_U16;
|
||||
|
||||
// PHY Initialization:
|
||||
REG_WRITE(0x2f8, 0, 0, pval >> 8, pval);
|
||||
|
||||
pval &= 0xfff0;
|
||||
pval |= 0x0c;
|
||||
REG_WRITE(0x2f4, 0, 0, pval >> 8, pval);
|
||||
|
||||
phy_write(RTL8224_PHY_ID, 0x1e, 0xa90, pval);
|
||||
phy_write(RTL8224_PHY_ID, PHY_MMD30, RTL837X_CFG_PHY_MDI_REVERSE, pval);
|
||||
delay(50);
|
||||
|
||||
if (machine_detected.isN) {
|
||||
print_string(" N-settings");
|
||||
// TX_POLARITY_SWAP
|
||||
rtl8224_write_reg_u16(RTL837X_CFG_PHY_TX_POLARITY_SWAP, 0x596A);
|
||||
}
|
||||
|
||||
print_string("\r\nrtl8224_phy_enable done\r\n");
|
||||
}
|
||||
|
||||
@@ -100,50 +94,50 @@ void phy_config(uint8_t phy) __banked
|
||||
delay(20);
|
||||
// PHY configuration: External 8221B?
|
||||
// p081e.75f3:ffff P000100.1e0075f3:fffe
|
||||
phy_modify(phy, 0x1e, 0x75f3, 0x0001, 0x0000);
|
||||
phy_modify(phy, PHY_MMD30, 0x75f3, 0x0001, 0x0000);
|
||||
delay(20);
|
||||
|
||||
// p081e.697a:ffff P000100.1e00697a:ffc1 / p031e.697a:0003 P000008.1e00697a:0001
|
||||
// SERDES OPTION 1 Register (MMD 30.0x6) bits 0-5: 0x01: Set HiSGMII+SGMII
|
||||
phy_modify(phy, 0x1e, 0x697a, 0x003f, 0x0001);
|
||||
phy_modify(phy, PHY_MMD30, 0x697a, 0x003f, 0x0001);
|
||||
delay(20);
|
||||
|
||||
// p031f.a432:0811 P000008.1f00a432:0831
|
||||
// PHYCR2 PHY Specific Control Register 2, MMD 31. 0xA432), set bit 5: enable EEE
|
||||
phy_modify(phy, 0x1f, 0xa432, 0x0000, 0x0020);
|
||||
phy_modify(phy, PHY_MMD31, PHY_MMD31_PHYCR2, 0x0000, 0x0020);
|
||||
|
||||
// p0307.003e:0000 P000008.0700003e:0001
|
||||
// EEE avertisment 2 register MMMD 7.0x003e, set bit 0: 2.5G has EEE capability
|
||||
phy_modify(phy, 0x7, 0x3e, 0x0000, 0x0001);
|
||||
phy_modify(phy, PHY_MMD_AN, PHY_EEE_ADV2, 0x0000, 0x0001);
|
||||
delay(20);
|
||||
|
||||
// p031f.a442:043c P000008.1f00a442:0430
|
||||
// Unknown, but clear bits 2/3
|
||||
phy_modify(phy, 0x1f, 0xa442, 0x0006, 0x0000);
|
||||
phy_modify(phy, PHY_MMD31, 0xa442, 0x000c, 0x0000);
|
||||
delay(20);
|
||||
|
||||
// P000100.1e0075b5:e084
|
||||
phy_write(phy, 0x1e, 0x75b5, 0xe084);
|
||||
phy_write(phy, PHY_MMD30, 0x75b5, 0xe084);
|
||||
delay(20);
|
||||
|
||||
// p031e.75b2:0000 P000008.1e0075b2:0060
|
||||
// set bits 5/6
|
||||
phy_modify(phy, 0x1e, 0x75b2, 0x0000, 0x0060);
|
||||
phy_modify(phy, PHY_MMD30, 0x75b2, 0x0000, 0x0060);
|
||||
delay(20);
|
||||
|
||||
// p081f.d040:ffff P000100.1f00d040:feff
|
||||
// LCR6 (LED Control Register 6, MMD 31.D040), set bits 8/9 to 0b10
|
||||
phy_modify(phy, 0x1e, 0xd040, 0x0300, 0x0200);
|
||||
phy_modify(phy, PHY_MMD30, 0xd040, 0x0300, 0x0200);
|
||||
delay(20);
|
||||
|
||||
// p081f.a400:ffff P000100.1f00a400:ffff, then: p081f.a400:ffff P000100.1f00a400:bfff
|
||||
// p031f.a400:1040 P000008.1f00a400:5040, then: p031f.a400:5040 P000008.1f00a400:1040
|
||||
// FEDCR (Fast Ethernet Duplex Control Register, MMD 31.0xA400)
|
||||
// Set bit 14, sleep, then clear again, according to the datasheet these bits are reserved
|
||||
phy_modify(phy, 0x1f, 0xa400, 0x0000, 0x4000);
|
||||
phy_modify(phy, PHY_MMD31, PHY_MMD31_FEDCR, 0x0000, 0x4000);
|
||||
delay(20);
|
||||
|
||||
phy_modify(phy, 0x1f, 0xa400, 0x4000, 0x0000);
|
||||
phy_modify(phy, PHY_MMD31, PHY_MMD31_FEDCR, 0x4000, 0x0000);
|
||||
delay(20);
|
||||
|
||||
print_string("\r\n phy config done\r\n");
|
||||
@@ -153,10 +147,18 @@ void phy_config(uint8_t phy) __banked
|
||||
void phy_config_8224(void) __banked
|
||||
{
|
||||
uint16_t pval;
|
||||
print_string("\r\nphy_config_8224 called\r\n");
|
||||
print_string("\r\nphy_config_8224 called\r\nRTL8224 ID: ");
|
||||
|
||||
// Print RTL8224 chip id
|
||||
rtl8224_read_reg_u16(RTL837X_REG_CHIP_ID + 1);
|
||||
print_short(SFR_DATA_U16);
|
||||
rtl8224_read_reg_u16(RTL837X_REG_CHIP_ID);
|
||||
print_byte(SFR_DATA_U16 >> 8);
|
||||
print_byte(SFR_DATA_U16);
|
||||
write_char('\n');
|
||||
|
||||
// p001e.7b20:0bff R02f8-00000bff R02f4-00000bed P000001.1e007b20:0bed
|
||||
phy_read(RTL8224_PHY_ID, 0x1e, 0x7b20);
|
||||
phy_read(RTL8224_PHY_ID, PHY_MMD30, 0x7b20);
|
||||
pval = SFR_DATA_U16;
|
||||
|
||||
REG_WRITE(0x2f8, 0, 0, pval >> 8, pval);
|
||||
@@ -164,16 +166,16 @@ void phy_config_8224(void) __banked
|
||||
pval |= 0x000d;
|
||||
REG_WRITE(0x2f4, 0, 0, pval >> 8, pval);
|
||||
|
||||
phy_write(RTL8224_PHY_ID, 0x1e, 0x7b20, pval);
|
||||
phy_write(RTL8224_PHY_ID, PHY_MMD30, 0x7b20, pval);
|
||||
|
||||
uint8_t i = 0;
|
||||
while (rtl8224_ca[i]) {
|
||||
phy_write(RTL8224_PHY_ID, 0x1e, 0x400, rtl8224_ca[i]);
|
||||
while (rtl8224_sds0_setttings[i]) {
|
||||
rtl8224_write_reg_u16(RTL837X_SDS_INDACS_WRITE_DATA, rtl8224_sds0_setttings[i]);
|
||||
i++;
|
||||
phy_write(RTL8224_PHY_ID, 0x1e, 0x3f8, rtl8224_ca[i]);
|
||||
rtl8224_write_reg_u16(RTL837X_SDS_INDACS_CMD, rtl8224_sds0_setttings[i]);
|
||||
i++;
|
||||
do {
|
||||
phy_read(RTL8224_PHY_ID, 0x1e, 0x3f8);
|
||||
rtl8224_read_reg_u16(0x3f8);
|
||||
} while (SFR_DATA_8 & 0x80);
|
||||
}
|
||||
|
||||
@@ -182,61 +184,376 @@ void phy_config_8224(void) __banked
|
||||
|
||||
|
||||
/*
|
||||
* Set Speed, duplex and flow control mode of a PHY
|
||||
* Set Speed of a PHY
|
||||
* See e.g. RTL8221B datasheet
|
||||
* duplex: 0: half, 1: full, 2: both
|
||||
*/
|
||||
void phy_set_mode(uint8_t port, uint8_t speed, uint8_t flow_control, uint8_t duplex) __banked
|
||||
void phy_set_speed(void) __banked
|
||||
{
|
||||
uint16_t v;
|
||||
phy_read(port, 0x1f, 0xa610);
|
||||
|
||||
print_string("Setting port "); write_char(machine.log_to_phys_port[phy_settings.port] + '0');
|
||||
if (phy_settings.speed == PHY_OFF) {
|
||||
print_string(" to disabled");
|
||||
} else {
|
||||
print_string(" to speed ");
|
||||
switch(phy_settings.speed) {
|
||||
case PHY_SPEED_AUTO:
|
||||
print_string("auto");
|
||||
break;
|
||||
case PHY_SPEED_10M:
|
||||
print_string("10M");
|
||||
if (phy_settings.duplex)
|
||||
print_string(" full duplex");
|
||||
else
|
||||
print_string(" half duplex");
|
||||
break;
|
||||
case PHY_SPEED_100M:
|
||||
print_string("100M");
|
||||
if (phy_settings.duplex)
|
||||
print_string(" full duplex");
|
||||
else
|
||||
print_string(" half duplex");
|
||||
break;
|
||||
case PHY_SPEED_1G:
|
||||
print_string("1G");
|
||||
break;
|
||||
case PHY_SPEED_2G5:
|
||||
print_string("2G5");
|
||||
break;
|
||||
default:
|
||||
print_string("UNKNOWN");
|
||||
break;
|
||||
}
|
||||
}
|
||||
write_char('\n');
|
||||
|
||||
phy_read(phy_settings.port, PHY_MMD31, 0xa610);
|
||||
v = SFR_DATA_U16;
|
||||
if (speed == PHY_OFF) {
|
||||
phy_write(port, 0x1f, 0xa610, v | 0x0800);
|
||||
if (phy_settings.speed == PHY_OFF) {
|
||||
phy_write(phy_settings.port, PHY_MMD31, 0xa610, v | 0x0800);
|
||||
return;
|
||||
}
|
||||
// Port is on, make sure of it:
|
||||
if (v & 0x0800)
|
||||
phy_write(port, 0x1f, 0xa610, v & 0xf7ff);
|
||||
phy_write(phy_settings.port, PHY_MMD31, 0xa610, v & 0xf7ff);
|
||||
|
||||
if (speed == PHY_SPEED_AUTO) {
|
||||
if (phy_settings.speed == PHY_SPEED_AUTO) {
|
||||
// AN Advertisement Register (MMD 7.0x0010)
|
||||
phy_write(port, PHY_MMD_AN, 0x10, 0x1001); // bits 0-4: 0x1 (802.3 supported), Extended Next Page format used
|
||||
// bits 0-4: 0x1 (802.3 supported), Extended Next Page format used
|
||||
phy_write(phy_settings.port, PHY_MMD_AN, PHY_ANEG_ADV, 0x15e1);
|
||||
// Multi-GBASE-TBASE-T AN Control 1 Register (MMD 7.0x0020)
|
||||
phy_write(port, PHY_MMD_AN, 0x20, 0x6081); // bit 14: SLAVE, bit 13: Multi-Port device, bit 8: 2.5GBit available, 1: LD Loop timin enableed
|
||||
phy_write(port, PHY_MMD_AN, 0x00, 0x3200); // Restart AN
|
||||
// bit 14: SLAVE, bit 13: Multi-Port device, bit 8: 2.5GBit available, 1: LD
|
||||
phy_write(phy_settings.port, PHY_MMD_AN, PHY_ANEG_MGBASE_CTRL, 0x6081);
|
||||
// GBCR (1000Base-T Control Register, MMD 31.0xA412)
|
||||
phy_modify(phy_settings.port, PHY_MMD31, PHY_MMD31_GBCR, 0x0000, 0x0200); // Loop timing enabled
|
||||
phy_write(phy_settings.port, PHY_MMD_AN, PHY_ANEG_CTRL, 0x3200); // Restart AN
|
||||
} else {
|
||||
// AN Control Register (MMD 7.0x0000)
|
||||
phy_write(port, PHY_MMD_AN, 0x00, 0x2000); // Clear bit 12: No Autoneg, Set Extended Pages (bit 13)
|
||||
// AN Advertisement Register (MMD 7.0x0010)
|
||||
phy_write(port, PHY_MMD_AN, 0x10, 0x1001); // bits 0-4: 0x1 (802.3 supported), Extended Next Page format used
|
||||
if (speed == PHY_SPEED_1G) {
|
||||
// Multi-GBASE-TBASE-T AN Control 1 Register (MMD 7.0x0020)
|
||||
phy_write(port, PHY_MMD_AN, 0x20, 0x6001); // bit 14: SLAVE, bit 13: Multi-Port device, 1: LD Loop timin enableed
|
||||
// GBCR (1000Base-T Control Register, MMD 31.0xA412)
|
||||
phy_modify(port, 0x1f, 0xa412, 0x0000, 0x02000);
|
||||
} else if (speed == PHY_SPEED_2G5) {
|
||||
// Multi-GBASE-TBASE-T AN Control 1 Register (MMD 7.0x0020)
|
||||
phy_write(port, PHY_MMD_AN, 0x20, 0x6081); // bit 14: SLAVE, bit 13: Multi-Port device, bit 8: 2.5GBit available, 1: LD Loop timin enableed
|
||||
// GBCR (1000Base-T Control Register, MMD 31.0xA412)
|
||||
phy_modify(port, 0x1f, 0xa412, 0x02000, 0x0000);
|
||||
phy_write(phy_settings.port, PHY_MMD_AN, PHY_ANEG_CTRL, 0x2000); // Clear bit 12: No Autoneg, Set Extended Pages (bit 13)
|
||||
if (phy_settings.speed == PHY_SPEED_10M) {
|
||||
phy_write(phy_settings.port, PHY_MMD_AN, PHY_ANEG_MGBASE_CTRL, 0x6001);
|
||||
if (!phy_settings.duplex)
|
||||
phy_write(phy_settings.port, PHY_MMD_AN, PHY_ANEG_ADV, 0x1421);
|
||||
else if (phy_settings.duplex == 1)
|
||||
phy_write(phy_settings.port, PHY_MMD_AN, PHY_ANEG_ADV, 0x1441);
|
||||
else
|
||||
phy_write(phy_settings.port, PHY_MMD_AN, PHY_ANEG_ADV, 0x1461);
|
||||
phy_modify(phy_settings.port, PHY_MMD31, PHY_MMD31_GBCR, 0x0200, 0x0000);
|
||||
} else if (phy_settings.speed == PHY_SPEED_100M) {
|
||||
phy_write(phy_settings.port, PHY_MMD_AN, PHY_ANEG_MGBASE_CTRL, 0x6001);
|
||||
if (!phy_settings.duplex)
|
||||
phy_write(phy_settings.port, PHY_MMD_AN, PHY_ANEG_ADV, 0x1481);
|
||||
if (phy_settings.duplex == 1)
|
||||
phy_write(phy_settings.port, PHY_MMD_AN, PHY_ANEG_ADV, 0x1501);
|
||||
else
|
||||
phy_write(phy_settings.port, PHY_MMD_AN, PHY_ANEG_ADV, 0x1581);
|
||||
phy_modify(phy_settings.port, PHY_MMD31, PHY_MMD31_GBCR, 0x0200, 0x0000);
|
||||
} else {
|
||||
// AN Advertisement Register (MMD 7.0x0010)
|
||||
// bits 0-4: 0x1 (802.3 supported), Extended Next Page format used
|
||||
phy_write(phy_settings.port, PHY_MMD_AN, PHY_ANEG_ADV, 0x1001);
|
||||
if (phy_settings.speed == PHY_SPEED_1G) {
|
||||
// Multi-GBASE-TBASE-T AN Control 1 Register (MMD 7.0x0020)
|
||||
// bit 14: SLAVE, bit 13: Multi-Port device, 1: LD Loop timin enableed
|
||||
phy_write(phy_settings.port, PHY_MMD_AN, PHY_ANEG_MGBASE_CTRL, 0x6001);
|
||||
// GBCR (1000Base-T Control Register, MMD 31.0xA412)
|
||||
phy_modify(phy_settings.port, PHY_MMD31, PHY_MMD31_GBCR, 0x0000, 0x0200);
|
||||
} else if (phy_settings.speed == PHY_SPEED_2G5) {
|
||||
// Multi-GBASE-TBASE-T AN Control 1 Register (MMD 7.0x0020)
|
||||
// bit 14: SLAVE, bit 13: Multi-Port device, bit 8: 2.5GBit available, 1: LD Loop timin enableed
|
||||
phy_write(phy_settings.port, PHY_MMD_AN, PHY_ANEG_MGBASE_CTRL, 0x6081);
|
||||
// GBCR (1000Base-T Control Register, MMD 31.0xA412)
|
||||
phy_modify(phy_settings.port, PHY_MMD31, PHY_MMD31_GBCR, 0x0200, 0x0000);
|
||||
}
|
||||
}
|
||||
phy_write(port, PHY_MMD_AN, 0x00, 0x3200); // Enable AN
|
||||
phy_write(phy_settings.port, PHY_MMD_AN, PHY_ANEG_CTRL, 0x3000); // Enable AN
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
void phy_set_duplex(void) __banked
|
||||
{
|
||||
uint16_t v;
|
||||
|
||||
print_string("Setting port "); write_char(machine.log_to_phys_port[phy_settings.port] + '0');
|
||||
if (phy_settings.duplex)
|
||||
print_string(" to full duplex");
|
||||
else
|
||||
print_string(" to half duplex");
|
||||
write_char('\n');
|
||||
|
||||
phy_read(phy_settings.port, PHY_MMD_AN, PHY_ANEG_CTRL);
|
||||
v = SFR_DATA_U16;
|
||||
if (!(v & 0x1000)) { // AN disabled, we are in forced mode
|
||||
phy_read(phy_settings.port, PHY_MMD31, PHY_MMD31_FEDCR);
|
||||
v = SFR_DATA_U16;
|
||||
if (phy_settings.duplex)
|
||||
v |= 0x0100;
|
||||
else
|
||||
v &= 0xfeff;
|
||||
phy_write(phy_settings.port, PHY_MMD31, PHY_MMD31_FEDCR, v);
|
||||
return;
|
||||
}
|
||||
// Disable AN
|
||||
phy_write(phy_settings.port, PHY_MMD_AN, PHY_ANEG_CTRL, 0x2000);
|
||||
phy_read(phy_settings.port, PHY_MMD_AN, PHY_ANEG_ADV);
|
||||
v = SFR_DATA_U16;
|
||||
if (v & 0x0060) {
|
||||
if (phy_settings.duplex)
|
||||
phy_modify(phy_settings.port, PHY_MMD_AN, PHY_ANEG_ADV, 0xffbf, 0x0040);
|
||||
else
|
||||
phy_modify(phy_settings.port, PHY_MMD_AN, PHY_ANEG_ADV, 0xffdf, 0x0020);
|
||||
}
|
||||
if (v & 0x0180) {
|
||||
if (phy_settings.duplex)
|
||||
phy_modify(phy_settings.port, PHY_MMD_AN, PHY_ANEG_ADV, 0xfeff, 0x0100);
|
||||
else
|
||||
phy_modify(phy_settings.port, PHY_MMD_AN, PHY_ANEG_ADV, 0xff7f, 0x0080);
|
||||
}
|
||||
// Restart AN
|
||||
phy_write(phy_settings.port, PHY_MMD_AN, PHY_ANEG_CTRL, 0x3000);
|
||||
}
|
||||
|
||||
|
||||
void phy_show(uint8_t port) __banked
|
||||
{
|
||||
uint16_t v;
|
||||
|
||||
// The actual PHY speed is in a Realtek propriatary register
|
||||
print_string("\nLink speed: ");
|
||||
phy_read(port, PHY_MMD31, PHY_MMD31_PHYSR);
|
||||
v = SFR_DATA_U16;
|
||||
switch(((v & 0x0600) >> 7) | ((v & 0x0030) >> 4)) {
|
||||
case 0:
|
||||
print_string("10M");
|
||||
break;
|
||||
case 1:
|
||||
print_string("100M");
|
||||
break;
|
||||
case 2:
|
||||
print_string("1000M");
|
||||
break;
|
||||
case 3:
|
||||
print_string("500M");
|
||||
break;
|
||||
case 4:
|
||||
print_string("10G");
|
||||
break;
|
||||
case 5:
|
||||
print_string("2500M");
|
||||
break;
|
||||
case 6:
|
||||
print_string("5G");
|
||||
break;
|
||||
default:
|
||||
print_string("10M");
|
||||
}
|
||||
if (v & 0x8)
|
||||
print_string(" full duplex");
|
||||
else
|
||||
print_string(" half duplex");
|
||||
|
||||
phy_read(port, PHY_MMD_AN, PHY_ANEG_CTRL);
|
||||
v = SFR_DATA_U16;
|
||||
if (!(v & 0x1000)) { // AN disabled, we are in forced mode
|
||||
phy_read(port, PHY_MMD_PMAPMD, 0);
|
||||
v = SFR_DATA_U16;
|
||||
print_string("\nForced speed: "); print_short(v); write_char('\n');
|
||||
uint8_t s1 = ((v & 0x40) ? 0x2 : 0x0) | ((v & 0x2000) ? 0x1 : 0x0);
|
||||
uint8_t s2 = (v >> 2) & 0xf;
|
||||
switch(s1) {
|
||||
case 0:
|
||||
print_string("10M\n");
|
||||
break;
|
||||
case 1:
|
||||
print_string("100M\n");
|
||||
break;
|
||||
case 2:
|
||||
print_string("1000M\n");
|
||||
break;
|
||||
case 3:
|
||||
switch (s2) {
|
||||
case 0:
|
||||
print_string("10G\n");
|
||||
break;
|
||||
case 6:
|
||||
print_string("2500M\n");
|
||||
break;
|
||||
case 7:
|
||||
print_string("5G\n");
|
||||
break;
|
||||
default:
|
||||
print_string("Unknown\n");
|
||||
}
|
||||
break;
|
||||
default:
|
||||
print_string("Unknown\n");
|
||||
}
|
||||
phy_read(port, PHY_MMD31, PHY_MMD31_FEDCR);
|
||||
v = SFR_DATA_U16;
|
||||
print_string("Duplex: "); print_short(v); print_string(" enabled: ");
|
||||
if (v & 0x100)
|
||||
print_string("yes");
|
||||
else
|
||||
print_string("no");
|
||||
write_char('\n');
|
||||
|
||||
} else {
|
||||
print_string("\nAN enabled, advertising:");
|
||||
phy_read(port, PHY_MMD_AN, PHY_ANEG_ADV);
|
||||
v = SFR_DATA_U16;
|
||||
if (v & 0x0020)
|
||||
print_string(" 10Base-Half");
|
||||
if (v & 0x0040)
|
||||
print_string(" 10Base-Full");
|
||||
if (v & 0x0080)
|
||||
print_string(" 100Base-Half");
|
||||
if (v & 0x0100)
|
||||
print_string(" 100Base-Full");
|
||||
phy_read(port, PHY_MMD31, PHY_MMD31_GBCR);
|
||||
v = SFR_DATA_U16;
|
||||
if (v & 0x0200)
|
||||
print_string(" 1000Base-Full");
|
||||
phy_read(port, PHY_MMD_AN, PHY_ANEG_MGBASE_CTRL);
|
||||
v = SFR_DATA_U16;
|
||||
if (v & 0x0080)
|
||||
print_string(" 2500BaseN-Full");
|
||||
}
|
||||
phy_read(port, PHY_MMD_AN, PHY_ANEG_LP_ABILITY);
|
||||
v = SFR_DATA_U16;
|
||||
print_string("\nLink Partner advertises:");
|
||||
if (v & 0x0020)
|
||||
print_string(" 10Base-Half");
|
||||
if (v & 0x0040)
|
||||
print_string(" 10Base-Full");
|
||||
if (v & 0x0080)
|
||||
print_string(" 100Base-Half");
|
||||
if (v & 0x0100)
|
||||
print_string(" 100Base-Full");
|
||||
phy_read(port, PHY_MMD31, PHY_MMD31_GANLPAR);
|
||||
v = SFR_DATA_U16;
|
||||
if (v & 0x0400)
|
||||
print_string(" 1000Base-Half");
|
||||
if (v & 0x0800)
|
||||
print_string(" 1000Base-Full");
|
||||
phy_read(port, PHY_MMD_AN, PHY_ANEG_MGBASE_ADV);
|
||||
v = SFR_DATA_U16;
|
||||
if (v & 0x0020)
|
||||
print_string(" 2500Base-Full");
|
||||
if (v & 0x0040)
|
||||
print_string(" 5000Base-Full");
|
||||
if (v & 0x0800)
|
||||
print_string(" 10GBase-Full");
|
||||
write_char('\n');
|
||||
}
|
||||
|
||||
|
||||
void phy_reset(uint8_t port) __banked
|
||||
{
|
||||
uint16_t v;
|
||||
phy_read(port, PHY_MMD_CTRL, 0xa610);
|
||||
phy_read(port, PHY_MMD31, 0xa610);
|
||||
v = SFR_DATA_U16;
|
||||
// If PHY off, do nothing
|
||||
if (v & 0x0800)
|
||||
return;
|
||||
|
||||
// Disable PHY
|
||||
phy_write(port, PHY_MMD_CTRL, 0xa610, v | 0x0800);
|
||||
phy_write(port, PHY_MMD31, 0xa610, v | 0x0800);
|
||||
delay(2);
|
||||
// Re-enable PHY
|
||||
phy_write(port, PHY_MMD_CTRL, 0xa610, v & 0xf7ff);
|
||||
phy_write(port, PHY_MMD31, 0xa610, v & 0xf7ff);
|
||||
}
|
||||
|
||||
// Read RTL8224 register.
|
||||
// Registers names are the same as on the RTL837x.
|
||||
// Reading only reads the lower 16-bit part of the 32-bit register.
|
||||
// When also needing read the upper 16-bits, use register address + 1.
|
||||
// Readed values it return via sfr-data.
|
||||
void inline rtl8224_read_reg_u16(uint16_t reg) __banked
|
||||
{
|
||||
// void phy_read(uint8_t phy_id, uint8_t dev_id, uint16_t reg)
|
||||
// phy_read(RTL8224_PHY_ID, PHY_MMD30, reg);
|
||||
|
||||
SFR_SMI_REG_U16 = reg; // c2, c2
|
||||
|
||||
SFR_SMI_PHY = RTL8224_PHY_ID; // a5
|
||||
SFR_SMI_DEV = PHY_MMD30 << 3 | 2; // c4
|
||||
|
||||
SFR_EXEC_GO = SFR_EXEC_READ_SMI;
|
||||
do {
|
||||
} while (SFR_EXEC_STATUS != 0);
|
||||
}
|
||||
|
||||
// Write RTL8224 register.
|
||||
// Registers names are the same as on the RTL837x.
|
||||
// Writing only the lower 16-bit part of the 32-bit register.
|
||||
// When also needing to write the upper 16-bits, use register address + 1.
|
||||
void inline rtl8224_write_reg_u16(uint16_t reg, uint16_t val) __banked
|
||||
{
|
||||
SFR_DATA_U16 = val; // SFR_A6, SFR_A7
|
||||
SFR_SMI_REG_U16 = reg; // SFR_C2, SFR_C3
|
||||
|
||||
//void phy_write(uint8_t phy_id, uint8_t dev_id, uint16_t reg, uint16_t v)
|
||||
// phy_write(RTL8224_PHY_ID, PHY_MMD30, reg, val);
|
||||
|
||||
uint16_t phy_mask = bit_mask[RTL8224_PHY_ID];
|
||||
|
||||
SFR_SMI_PHYMASK = phy_mask; // SFR_C5
|
||||
SFR_SMI_DEV = (phy_mask >> 8) | PHY_MMD30 << 3 | 2; // SFR_C4: bit 2 can also be set for some option
|
||||
SFR_EXEC_GO = SFR_EXEC_WRITE_SMI;
|
||||
do {
|
||||
} while (SFR_EXEC_STATUS != 0);
|
||||
}
|
||||
|
||||
// // Modify RTL8224 register.
|
||||
// // Registers names are the same as on the RTL837x.
|
||||
// // Modifies only the lower 16-bit part of the 32-bit register.
|
||||
// // When also needing to modifie the upper 16-bits, use register address + 1.
|
||||
// void rtl8224_modify_reg_u16(uint16_t reg, uint16_t clear, uint16_t set) __banked
|
||||
// {
|
||||
// phy_read(RTL8224_PHY_ID, PHY_MMD30, reg);
|
||||
// uint16_t pval = SFR_DATA_U16;
|
||||
// pval &= ~(clear);
|
||||
// pval |= set;
|
||||
// phy_write(RTL8224_PHY_ID, PHY_MMD30, reg, pval);
|
||||
// }
|
||||
|
||||
|
||||
// Write to the RTL8224 SDS registers.
|
||||
void rtl8224_sds_write(uint16_t sds_cmd, uint16_t value) __banked
|
||||
{
|
||||
// Wait for command bit is cleared
|
||||
do {
|
||||
rtl8224_read_reg_u16(RTL837X_SDS_INDACS_CMD);
|
||||
} while (SFR_DATA_8 & 0x80);
|
||||
|
||||
rtl8224_write_reg_u16(RTL837X_SDS_INDACS_WRITE_DATA, value);
|
||||
|
||||
rtl8224_write_reg_u16(RTL837X_SDS_INDACS_CMD, sds_cmd);
|
||||
|
||||
// Wait for command bit is cleared
|
||||
do {
|
||||
rtl8224_read_reg_u16(RTL837X_SDS_INDACS_CMD);
|
||||
} while (SFR_DATA_8 & 0x80);
|
||||
}
|
||||
|
||||
@@ -1,15 +1,39 @@
|
||||
#ifndef _RTL837X_PHY_H_
|
||||
#define _RTL837X_PHY_H_
|
||||
|
||||
#define PHY_SPEED_AUTO 0x1
|
||||
#define PHY_SPEED_1G 0x2
|
||||
#define PHY_SPEED_2G5 0x3
|
||||
#define PHY_SPEED_10M 0x2
|
||||
#define PHY_SPEED_100M 0x3
|
||||
#define PHY_SPEED_1G 0x4
|
||||
#define PHY_SPEED_2G5 0x5
|
||||
#define PHY_SPEED_5G 0x6
|
||||
#define PHY_SPEED_10G 0x7
|
||||
#define PHY_SPEED_AUTO 0x10
|
||||
#define PHY_OFF 0xff
|
||||
|
||||
struct phy_settings {
|
||||
uint8_t duplex;
|
||||
uint8_t port;
|
||||
uint8_t speed;
|
||||
};
|
||||
|
||||
extern __xdata struct phy_settings phy_settings;
|
||||
|
||||
void rtl8224_phy_enable(void) __banked;
|
||||
void phy_config(uint8_t phy) __banked;
|
||||
void phy_config_8224(void) __banked;
|
||||
void phy_set_mode(uint8_t port, uint8_t speed, uint8_t flow_control, uint8_t duplex) __banked;
|
||||
void phy_set_speed(void) __banked;
|
||||
void phy_set_duplex(void) __banked;
|
||||
void phy_show(uint8_t port) __banked;
|
||||
void phy_reset(uint8_t port) __banked;
|
||||
void rtl8224_read_reg_u16(uint16_t reg) __banked;
|
||||
void rtl8224_write_reg_u16(uint16_t reg, uint16_t val) __banked;
|
||||
void rtl8224_sds_write(uint16_t sds_cmd, uint16_t val) __banked;
|
||||
|
||||
#define RTL8224_SDS_WRITE(sds_id, page, reg, v) uint16_t _sdscmd = (uint16_t)(sds_id & 0x01) | (1 << 14) | (1 << 15); \
|
||||
_sdscmd |= (page & 0x3F) << 1; \
|
||||
_sdscmd |= ((uint16_t)(reg & 0x1f)) << 7; \
|
||||
print_string("CMD: "); print_short(_sdscmd); \
|
||||
write_char('-'); print_short(v); \
|
||||
rtl8224_sds_write(_sdscmd, v);
|
||||
|
||||
#endif
|
||||
|
||||
@@ -0,0 +1,120 @@
|
||||
#include "rtl837x_pins.h"
|
||||
#include "rtl837x_common.h"
|
||||
#include "rtl837x_regs.h"
|
||||
|
||||
uint8_t i2c_bus_from_sda_pin(uint8_t sda_pin) {
|
||||
switch (sda_pin) {
|
||||
case GPIO47_I2C_SDA0:
|
||||
return 0;
|
||||
case GPIO49_I2C_SDA1:
|
||||
return 1;
|
||||
case GPIO51_I2C_SDA2_UART1_RX:
|
||||
return 2;
|
||||
case GPIO41_I2C_SDA3_MDIO1:
|
||||
return 3;
|
||||
case GPIO39_I2C_SDA4:
|
||||
return 4;
|
||||
default:
|
||||
return 0xFF;
|
||||
}
|
||||
}
|
||||
|
||||
uint8_t i2c_bus_from_scl_pin(uint8_t scl_pin) {
|
||||
switch (scl_pin) {
|
||||
case GPIO46_I2C_SCL0:
|
||||
return 0;
|
||||
case GPIO48_I2C_SCL1:
|
||||
return 1;
|
||||
case GPIO50_I2C_SCL2_UART1_TX:
|
||||
return 2;
|
||||
case GPIO40_I2C_SCL3_MDC1:
|
||||
return 3;
|
||||
default:
|
||||
return 0xFF;
|
||||
}
|
||||
}
|
||||
|
||||
/* Returns RTL837X_REG_GPIO_XX_OUTPUT register address */
|
||||
static uint16_t gpio_output_reg(uint8_t pin) {
|
||||
return pin < 32 ? RTL837X_REG_GPIO_00_31_OUTPUT : RTL837X_REG_GPIO_32_63_OUTPUT;
|
||||
}
|
||||
|
||||
/* Returns RTL837X_REG_GPIO_XX_DIRECTION register address */
|
||||
static uint16_t gpio_direction_reg(uint8_t pin) {
|
||||
return pin < 32 ? RTL837X_REG_GPIO_00_31_DIRECTION : RTL837X_REG_GPIO_32_63_DIRECTION;
|
||||
}
|
||||
|
||||
|
||||
/* Enable GPIO functions for pin */
|
||||
static void gpio_mux_setup(uint8_t pin)
|
||||
{
|
||||
// Some GPIOs require setting MUX registers to enable GPIO
|
||||
switch (pin) {
|
||||
case GPIO10_LED10:
|
||||
reg_bit_clear(RTL837X_PIN_MUX_0, 10);
|
||||
break;
|
||||
case GPIO30_ACL_BIT3_EN:
|
||||
reg_bit_clear(RTL837X_PIN_MUX_2, 3);
|
||||
break;
|
||||
case GPIO36_PWM_OUT:
|
||||
reg_bit_set(RTL837X_PIN_MUX_1, 30);
|
||||
break;
|
||||
case GPIO37:
|
||||
case GPIO38:
|
||||
// Intentionally empty, always GPIO
|
||||
break;
|
||||
case GPIO46_I2C_SCL0:
|
||||
// Bit 7-8 0b00 -> GPIO
|
||||
reg_read_m(RTL837X_PIN_MUX_1);
|
||||
sfr_mask_data(0, 0x80, 0x00);
|
||||
sfr_mask_data(1, 0x01, 0x00);
|
||||
reg_write_m(RTL837X_PIN_MUX_1);
|
||||
break;
|
||||
case GPIO50_I2C_SCL2_UART1_TX:
|
||||
// Bit 15-16 0b00 -> GPIO
|
||||
reg_read_m(RTL837X_PIN_MUX_1);
|
||||
sfr_mask_data(1, 0x80, 0x00);
|
||||
sfr_mask_data(2, 0x01, 0x00);
|
||||
reg_write_m(RTL837X_PIN_MUX_1);
|
||||
break;
|
||||
case GPIO51_I2C_SDA2_UART1_RX:
|
||||
// Bit 17-18 0b00 -> GPIO
|
||||
reg_read_m(RTL837X_PIN_MUX_1);
|
||||
sfr_mask_data(2, 0x06, 0x00);
|
||||
reg_write_m(RTL837X_PIN_MUX_1);
|
||||
break;
|
||||
case GPIO54_ACL_BIT2_EN:
|
||||
reg_bit_clear(RTL837X_PIN_MUX_2, 2);
|
||||
break;
|
||||
case GPIO_NA:
|
||||
print_string("Attemped to assign GPIO function to N/A pin!");
|
||||
break;
|
||||
default:
|
||||
print_string("GPIO MUX setup not implemented for pin="); print_byte(pin); print_string("\n");
|
||||
}
|
||||
}
|
||||
|
||||
void gpio_input_setup(uint8_t pin) {
|
||||
if (pin == GPIO_NA) {
|
||||
return;
|
||||
}
|
||||
|
||||
gpio_mux_setup(pin);
|
||||
reg_bit_clear(gpio_direction_reg(pin), (pin % 32));
|
||||
}
|
||||
|
||||
void gpio_output_setup(uint8_t pin, __xdata uint8_t initial_val) {
|
||||
if (pin == GPIO_NA) {
|
||||
return;
|
||||
}
|
||||
gpio_mux_setup(pin);
|
||||
|
||||
// We need to setup value before enabling output on PIN
|
||||
if (initial_val) {
|
||||
reg_bit_set(gpio_output_reg(pin), (pin % 32));
|
||||
} else {
|
||||
reg_bit_clear(gpio_output_reg(pin), (pin % 32));
|
||||
}
|
||||
|
||||
reg_bit_set(gpio_direction_reg(pin), (pin % 32));
|
||||
}
|
||||
@@ -0,0 +1,93 @@
|
||||
#ifndef _RTL837X_PINS_H_
|
||||
#define _RTL837X_PINS_H_
|
||||
|
||||
#include <stdint.h>
|
||||
|
||||
#define GPIO0_LED0 0
|
||||
#define GPIO1_LED1 1
|
||||
#define GPIO2_LED2 2
|
||||
#define GPIO3_LED3 3
|
||||
#define GPIO4_LED4 4
|
||||
#define GPIO5_LED5 5
|
||||
#define GPIO6_LED6 6
|
||||
#define GPIO7_LED7 7
|
||||
#define GPIO8_LED8 8
|
||||
#define GPIO9_LED9 9
|
||||
#define GPIO10_LED10 10
|
||||
#define GPIO11_LED11 11
|
||||
#define GPIO12_LED12 12
|
||||
#define GPIO13_LED13 13
|
||||
#define GPIO14_LED14 14
|
||||
#define GPIO15_LED15 15
|
||||
#define GPIO16_LED16 16
|
||||
#define GPIO17_LED17 17
|
||||
#define GPIO18_LED18 18
|
||||
#define GPIO19_LED19 19
|
||||
#define GPIO20_LED20 20
|
||||
#define GPIO21_LED21 21
|
||||
#define GPIO22_LED22 22
|
||||
#define GPIO23_LED23 23
|
||||
#define GPIO24_LED24 24
|
||||
#define GPIO25_LED25 25
|
||||
#define GPIO26_LED26 26
|
||||
#define GPIO27_LED27 27
|
||||
#define GPIO28_SYS_LED 28
|
||||
#define GPIO29_GLB_RLDP_LED_EN 29
|
||||
#define GPIO30_ACL_BIT3_EN 30
|
||||
#define GPIO31_UART0_TX 31
|
||||
#define GPIO32_UART0_RX 32
|
||||
#define GPIO33_INT 33
|
||||
#define GPIO34_MDC0 34
|
||||
#define GPIO35_MDIO0 35
|
||||
#define GPIO36_PWM_OUT 36
|
||||
#define GPIO37 37
|
||||
#define GPIO38 38
|
||||
#define GPIO39_I2C_SDA4 39
|
||||
#define GPIO40_I2C_SCL3_MDC1 40
|
||||
#define GPIO41_I2C_SDA3_MDIO1 41
|
||||
#define GPIO42_SPI 42
|
||||
#define GPIO43_SPI 43
|
||||
#define GPIO44_SPI 44
|
||||
#define GPIO45_SPI 45
|
||||
#define GPIO46_I2C_SCL0 46
|
||||
#define GPIO47_I2C_SDA0 47
|
||||
#define GPIO48_I2C_SCL1 48
|
||||
#define GPIO49_I2C_SDA1 49
|
||||
#define GPIO50_I2C_SCL2_UART1_TX 50
|
||||
#define GPIO51_I2C_SDA2_UART1_RX 51
|
||||
#define GPIO52_ACL_BIT0_EN 52
|
||||
#define GPIO53_ACL_BIT1_EN 53
|
||||
#define GPIO54_ACL_BIT2_EN 54
|
||||
#define GPIO55_PTP_CLK_IN 55
|
||||
#define GPIO56_PTP_CLK_OUT 56
|
||||
#define GPIO57_PTP_TOD_OUT 57
|
||||
#define GPIO58_PTP_PPS_OUT 58
|
||||
#define GPIO59_PTP_TOD_IN 59
|
||||
#define GPIO60_PTP_PPS_IN 60
|
||||
#define GPIO61_SYNCELOCK0 61
|
||||
#define GPIO62_SYNCELOCK1 62
|
||||
#define GPIO63_MDIO 63
|
||||
|
||||
/* Not available GPIO */
|
||||
#define GPIO_NA 0xFF
|
||||
|
||||
/* Convert SDA PIN GPIO to I2C bus number */
|
||||
uint8_t i2c_bus_from_sda_pin(uint8_t sda_pin);
|
||||
|
||||
/* Convert SCL PIN GPIO to I2C bus number */
|
||||
uint8_t i2c_bus_from_scl_pin(uint8_t scl_pin);
|
||||
|
||||
/*
|
||||
* Setup a GPIO pin as input
|
||||
* pin: GPIO pin number 0-63
|
||||
*/
|
||||
void gpio_input_setup(uint8_t pin);
|
||||
|
||||
/*
|
||||
* Setup a GPIO pin as output
|
||||
* pin: GPIO pin number 0-63
|
||||
* initial_val: 1 for bit set in RTL837X_REG_GPIO_xx_OUTPUT, 0 for bit not set
|
||||
*/
|
||||
void gpio_output_setup(uint8_t pin, __xdata uint8_t initial_val);
|
||||
|
||||
#endif
|
||||
@@ -13,39 +13,22 @@
|
||||
#include "rtl837x_port.h"
|
||||
#include "rtl837x_phy.h"
|
||||
#include "phy.h"
|
||||
#include "machine.h"
|
||||
|
||||
#pragma codeseg BANK1
|
||||
#pragma constseg BANK1
|
||||
|
||||
extern __code uint8_t * __code hex;
|
||||
extern __code uint16_t bit_mask[16];
|
||||
extern __xdata uint8_t minPort;
|
||||
extern __xdata uint8_t maxPort;
|
||||
extern __xdata uint8_t nSFPPorts;
|
||||
extern __code struct machine machine;
|
||||
extern __xdata uint8_t sfr_data[4];
|
||||
extern __xdata uint8_t cpuPort;
|
||||
extern __xdata uint16_t vlan_ptr;
|
||||
extern __xdata uint8_t vlan_names[VLAN_NAMES_SIZE];
|
||||
|
||||
extern __xdata uint8_t isRTL8373;
|
||||
extern __xdata struct machine_runtime machine_detected;
|
||||
|
||||
__xdata uint32_t l2_head;
|
||||
|
||||
// The mapping of logical to physical ports on the RTL8372
|
||||
// Port 6 is always an SFP+ port. Port 5 may be RTL8221 or SFP+
|
||||
__code uint8_t log_to_phys_port[9] = {
|
||||
0, 0, 0, 5, 1, 2, 3, 4, 6
|
||||
};
|
||||
|
||||
#if NSFP == 2
|
||||
__code uint8_t is_sfp[9] = {
|
||||
0, 0, 0, 1, 0, 0, 0, 0, 1
|
||||
};
|
||||
#else
|
||||
__code uint8_t is_sfp[9] = {
|
||||
0, 0, 0, 0, 0, 0, 0, 0, 1
|
||||
};
|
||||
#endif
|
||||
__xdata struct vlan_settings vlan_settings;
|
||||
|
||||
void port_mirror_set(register uint8_t port, __xdata uint16_t rx_pmask, __xdata uint16_t tx_pmask) __banked
|
||||
{
|
||||
@@ -65,7 +48,7 @@ void port_mirror_del(void) __banked
|
||||
}
|
||||
|
||||
|
||||
void port_ingress_filter(register uint8_t port, uint8_t type) __banked
|
||||
void port_ingress_filter(__xdata uint8_t port, __xdata uint8_t type) __banked
|
||||
{
|
||||
if (type & 0x1)
|
||||
reg_bit_set(RTL837x_REG_INGRESS, port << 1);
|
||||
@@ -140,27 +123,31 @@ __xdata uint16_t vlan_name(register uint16_t vlan) __banked
|
||||
|
||||
|
||||
/*
|
||||
* Create a VLAN
|
||||
* The arguments are passed in global structure vlan_settings
|
||||
* A member that is not tagged, is untagged
|
||||
*/
|
||||
void vlan_create(register uint16_t vlan, register uint16_t members, register uint16_t tagged) __banked
|
||||
void vlan_create(void) __banked
|
||||
{
|
||||
// For now, the CPU-port is always a tagged member:
|
||||
members |= 0x0200; // Set 10th bit
|
||||
tagged |= 0x0200;
|
||||
print_string("\nvlan_create called\nvlan: "); print_short(vlan);
|
||||
print_string(", members: "); print_short(members);
|
||||
print_string(", tagged: "); print_short(tagged); write_char('\n');
|
||||
vlan_settings.members |= 0x0200; // Set 10th bit
|
||||
vlan_settings.tagged |= 0x0200;
|
||||
|
||||
print_string("\nvlan_create called\nvlan: "); print_short(vlan_settings.vlan);
|
||||
print_string(", members: "); print_short(vlan_settings.members);
|
||||
print_string(", tagged: "); print_short(vlan_settings.tagged); write_char('\n');
|
||||
|
||||
uint16_t a = (~vlan_settings.members) ^ vlan_settings.tagged ^ vlan_settings.members;
|
||||
|
||||
uint16_t a = (~members) ^ tagged ^ members;
|
||||
// On RTL8372, port-bits 0-2 must be 0, although they are not members
|
||||
if (!isRTL8373) {
|
||||
if (!machine_detected.isRTL8373) {
|
||||
a &= 0x1f8;
|
||||
tagged &= 0x3f8;
|
||||
vlan_settings.tagged &= 0x3f8;
|
||||
}
|
||||
|
||||
// Initialize VLAN table with VLAN 1
|
||||
REG_WRITE(RTL837x_TBL_DATA_IN_A, 0x02, (a >> 6) & 0x0f, (a << 2) | (members >> 8), members);
|
||||
REG_WRITE(RTL837X_TBL_CTRL, vlan >> 8, vlan, TBL_VLAN, TBL_WRITE | TBL_EXECUTE);
|
||||
REG_WRITE(RTL837x_TBL_DATA_IN_A, 0x02, (a >> 6) & 0x0f, (a << 2) | (vlan_settings.members >> 8), vlan_settings.members);
|
||||
REG_WRITE(RTL837X_TBL_CTRL, vlan_settings.vlan >> 8, vlan_settings.vlan, TBL_VLAN, TBL_WRITE | TBL_EXECUTE);
|
||||
do {
|
||||
reg_read_m(RTL837X_TBL_CTRL);
|
||||
} while (sfr_data[3] & TBL_EXECUTE);
|
||||
@@ -184,18 +171,15 @@ void vlan_setup(void) __banked
|
||||
vlan_names[0] = 0;
|
||||
|
||||
// Initialize VLAN table for VLAN 1, by disabling that entry
|
||||
if (isRTL8373) {
|
||||
REG_SET(RTL837x_TBL_DATA_IN_A, 0x0007ffff);
|
||||
} else {
|
||||
REG_SET(RTL837x_TBL_DATA_IN_A, 0x0007e3f8);
|
||||
}
|
||||
REG_SET(RTL837x_TBL_DATA_IN_A, machine_detected.isRTL8373? 0x0007ffff : 0x0007e3f8);
|
||||
|
||||
REG_SET(RTL837X_TBL_CTRL, 0x00010303);
|
||||
do {
|
||||
reg_read_m(RTL837X_TBL_CTRL);
|
||||
} while (sfr_data[3] & TBL_EXECUTE);
|
||||
|
||||
// Set PVID 1 for every port. TODO: Skip unused ports!
|
||||
for (uint8_t i = minPort; i <= maxPort + 1; i++) { // Do this also for the CPU port (+1)
|
||||
for (uint8_t i = machine.min_port; i <= machine.max_port + 1; i++) { // Do this also for the CPU port (+1)
|
||||
uint16_t reg = RTL837x_PVID_BASE_REG + ((i >> 1) << 2);
|
||||
#ifdef DEBUG
|
||||
print_byte(i); write_char(':'); write_char(' '); print_short(reg); write_char('=');
|
||||
@@ -212,10 +196,10 @@ void vlan_setup(void) __banked
|
||||
write_char(' '); write_char('A'); write_char('>'); print_sfr_data();
|
||||
#endif
|
||||
|
||||
// EGRESS filtering for port: removal of additional VLAN tag
|
||||
reg_bit_clear(0x6738, i << 1);
|
||||
reg_bit_clear(0x6738, (i << 1) + 1);
|
||||
reg_bit_set(0x4e18, i);
|
||||
// EGRESS filtering for port: removal of additional VLAN tag (mode 0x3 for each port)
|
||||
reg_bit_clear(RTL837X_VLAN_PORT_EGR_TAG, i << 1);
|
||||
reg_bit_clear(RTL837X_VLAN_PORT_EGR_TAG, (i << 1) + 1);
|
||||
reg_bit_set(RTL837X_VLAN_PORT_IGR_FLTR, i);
|
||||
|
||||
#ifdef DEBUG
|
||||
print_string("\n");
|
||||
@@ -226,27 +210,18 @@ void vlan_setup(void) __banked
|
||||
REG_SET(RTL837x_REG_INGRESS, 0); // No filtering for all ports
|
||||
|
||||
// Enable 4k VLAN
|
||||
REG_SET(0x4e14, 4);
|
||||
REG_SET(0x4e30, 0);
|
||||
REG_SET(0x4e34, 0);
|
||||
REG_SET(RTL837X_VLAN_CTRL, VLAN_CVLAN_FILTER);
|
||||
REG_SET(RTL837X_VLAN_L2_LRN_DIS_0, 0);
|
||||
REG_SET(RTL837X_VLAN_L2_LRN_DIS_1, 0);
|
||||
|
||||
// Enable VLAN 1: Ports 0-9, i.e. including the CPU port are untagged members
|
||||
if (isRTL8373) {
|
||||
REG_SET(RTL837x_TBL_DATA_IN_A, 0x0207ffff); // 02: Entry valid, 7ffff: membership
|
||||
} else {
|
||||
REG_SET(RTL837x_TBL_DATA_IN_A, 0x0207e3f8);
|
||||
}
|
||||
REG_SET(RTL837x_TBL_DATA_IN_A, machine_detected.isRTL8373? 0x0207ffff : 0x0207e3f8); // 02: Entry valid, 7...: membership
|
||||
|
||||
REG_SET(RTL837X_TBL_CTRL, 0x00010303); // Write VLAN 1
|
||||
do {
|
||||
reg_read_m(RTL837X_TBL_CTRL);
|
||||
} while (sfr_data[3] & TBL_EXECUTE);
|
||||
|
||||
// Configure trunking
|
||||
if (isRTL8373) {
|
||||
REG_SET(0x4f4c, 0x0000007e); // Removes RTL VLAN-Tags
|
||||
REG_SET(0x4f48, 0x0000007e); // Adds 802.1Q VLAN-Tags to tagged ports
|
||||
}
|
||||
|
||||
#ifdef DEBUG
|
||||
print_string("\nvlan_setup, REG 0x6738: "); print_reg(0x6738);
|
||||
print_string("\nvlan_setup, REG 0x4e18: "); print_reg(0x4e18);
|
||||
@@ -261,18 +236,6 @@ void vlan_setup(void) __banked
|
||||
}
|
||||
|
||||
|
||||
void trunk_set(uint8_t group, uint16_t mask) __banked
|
||||
{
|
||||
if (group == 1) {
|
||||
REG_WRITE(RTL837x_TRUNK_CTRL_A, 0, 0, mask >> 8, mask);
|
||||
} else if (group == 2) {
|
||||
REG_WRITE(RTL837x_TRUNK_CTRL_B, 0, 0, mask >> 8, mask);
|
||||
} else {
|
||||
print_string("\nTrunk group must be 1 or 2\n");
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
* Forget all dynamic L2 learned entries
|
||||
*/
|
||||
@@ -284,11 +247,7 @@ uint8_t port_l2_forget(void) __banked
|
||||
REG_SET(RTL837x_L2_TBL_FLUSH_CNF, 0x0);
|
||||
|
||||
// Flush L2 table for all ports by setting the ports and the flush-exec bit (bit 16)
|
||||
if (isRTL8373) {
|
||||
REG_SET(RTL837x_L2_TBL_FLUSH_CTRL, L2_TBL_FLUSH_EXEC | PMASK_9);
|
||||
} else {
|
||||
REG_SET(RTL837x_L2_TBL_FLUSH_CTRL, L2_TBL_FLUSH_EXEC | PMASK_6);
|
||||
}
|
||||
REG_SET(RTL837x_L2_TBL_FLUSH_CTRL, L2_TBL_FLUSH_EXEC | (machine_detected.isRTL8373 ? PMASK_9 : PMASK_6));
|
||||
|
||||
// Wait for flush completed
|
||||
do {
|
||||
@@ -311,14 +270,23 @@ void port_l2_learned(void) __banked
|
||||
__xdata uint16_t first_entry = 0xffff; // Table does not have that many entries
|
||||
|
||||
while (1) {
|
||||
uint8_t port = 0, other = 0;
|
||||
uint8_t port = 0;
|
||||
reg_read_m(RTL837x_TBL_DATA_0);
|
||||
REG_WRITE(RTL837x_TBL_DATA_0, sfr_data[0], sfr_data[1],sfr_data[2] | 0xc0, sfr_data[3]);
|
||||
|
||||
REG_WRITE(RTL837X_TBL_CTRL, entry >> 8, entry, TBL_L2_UNICAST, 0x1);
|
||||
REG_WRITE(RTL837X_TBL_CTRL, (entry >> 8) & 0xf, entry, TBL_L2_UNICAST, TBL_EXECUTE);
|
||||
do {
|
||||
reg_read_m(RTL837X_TBL_CTRL);
|
||||
} while (sfr_data[3] & 0x1);
|
||||
} while (sfr_data[3] & TBL_EXECUTE);
|
||||
|
||||
reg_read_m(RTL837x_TBL_DATA_0);
|
||||
entry = (((uint16_t)sfr_data[2] & 0x0f) << 8) | sfr_data[3];
|
||||
if (first_entry == 0xffff) {
|
||||
first_entry = entry;
|
||||
} else {
|
||||
if (first_entry == entry)
|
||||
break;
|
||||
}
|
||||
|
||||
// MAC
|
||||
reg_read_m(RTL837x_L2_DATA_OUT_B);
|
||||
@@ -326,7 +294,6 @@ void port_l2_learned(void) __banked
|
||||
print_byte(sfr_data[2]); write_char(':');
|
||||
print_byte(sfr_data[3]); write_char(':');
|
||||
port = (sfr_data[0] >> 6) & 0x3;
|
||||
other = sfr_data[0];
|
||||
reg_read_m(RTL837x_L2_DATA_OUT_A);
|
||||
print_byte(sfr_data[0]); write_char(':');
|
||||
print_byte(sfr_data[1]); write_char(':');
|
||||
@@ -335,7 +302,7 @@ void port_l2_learned(void) __banked
|
||||
|
||||
// VLAN
|
||||
reg_read_m(RTL837x_L2_DATA_OUT_B);
|
||||
print_short( ((uint16_t) (sfr_data[0] & 0x0f)) | sfr_data[1]); // VLAN
|
||||
print_short( (((uint16_t) (sfr_data[0] & 0x0f)) << 8) | sfr_data[1]); // VLAN
|
||||
|
||||
// type
|
||||
reg_read_m(RTL837x_L2_DATA_OUT_C);
|
||||
@@ -346,22 +313,12 @@ void port_l2_learned(void) __banked
|
||||
|
||||
port |= (sfr_data[3] & 0x3) << 2;
|
||||
if (port < 9)
|
||||
write_char('1' + port);
|
||||
write_char(machine.log_to_phys_port[port] + '0');
|
||||
else
|
||||
print_string("10");
|
||||
print_string("CPU");
|
||||
}
|
||||
reg_read_m(RTL837x_TBL_DATA_0);
|
||||
entry = (((uint16_t)sfr_data[2] & 0x0f) << 8) | sfr_data[3] + 1;
|
||||
if (first_entry == 0xffff) {
|
||||
first_entry = entry;
|
||||
} else {
|
||||
if (first_entry == entry)
|
||||
break;
|
||||
}
|
||||
#ifdef DEBUG
|
||||
write_char(' '); print_sfr_data();
|
||||
write_char(' '); print_byte(other);
|
||||
#endif
|
||||
|
||||
entry++;
|
||||
print_string("\n");
|
||||
}
|
||||
}
|
||||
@@ -376,19 +333,17 @@ void port_l2_setup(void) __banked
|
||||
|
||||
port_l2_forget();
|
||||
|
||||
for (uint8_t i = minPort; i <= maxPort; i++) {
|
||||
uint16_t reg = 0x5384 + (i << 2);
|
||||
for (uint8_t i = machine.min_port; i <= machine.max_port; i++) {
|
||||
// Limit the number of automatically learned MAC-Entries per port to 0x1040
|
||||
uint16_t reg = RTL837X_L2_LRN_PORT_CONSTRAINT + (i << 2);
|
||||
REG_SET(reg, 0x00001040);
|
||||
|
||||
// All ports may communicate with each other and CPU-Port
|
||||
reg = RTL837X_PORT_ISOLATION_BASE + (i << 2);
|
||||
if(isRTL8373) {
|
||||
REG_SET(reg, PMASK_9 | PMASK_CPU);
|
||||
} else {
|
||||
REG_SET(reg, PMASK_6 | PMASK_CPU);
|
||||
}
|
||||
REG_SET(reg, PMASK_CPU | (machine_detected.isRTL8373? PMASK_9 : PMASK_6));
|
||||
}
|
||||
reg_bit_set(0x4f80, 0);
|
||||
// When maximim entries learned, then simply flood the packet
|
||||
reg_bit_set(RTL837X_L2_LRN_PORT_CONSTRT_ACT, 0);
|
||||
|
||||
print_string("\nport_l2_setup done\n");
|
||||
}
|
||||
@@ -397,67 +352,76 @@ void port_l2_setup(void) __banked
|
||||
void port_stats_print(void) __banked
|
||||
{
|
||||
print_string("\n Port\tState\tLink\tTxGood\t\tTxBad\t\tRxGood\t\tRxBad\n");
|
||||
for (uint8_t i = minPort; i <= maxPort; i++) {
|
||||
write_char('1' + i); write_char('\t');
|
||||
phy_read(i, 0x1f, 0xa610); // p001f.a610:2058
|
||||
if (i <= maxPort - nSFPPorts) {
|
||||
for (uint8_t i = machine.min_port; i <= machine.max_port; i++) {
|
||||
write_char('0' + machine.log_to_phys_port[i]); write_char('\t');
|
||||
|
||||
if (!machine.is_sfp[i]) {
|
||||
phy_read(i, PHY_MMD31, 0xa610);
|
||||
if (SFR_DATA_8 == 0x20)
|
||||
print_string("On\t");
|
||||
else
|
||||
print_string("Off\t");
|
||||
reg_read_m(RTL837X_REG_LINKS);
|
||||
uint8_t b = sfr_data[3 - (i >> 1)];
|
||||
b = (i & 1) ? b >> 4 : b & 0xf;
|
||||
switch (b) {
|
||||
case 0:
|
||||
print_string("Down\t");
|
||||
break;
|
||||
case 1:
|
||||
print_string("100M\t");
|
||||
break;
|
||||
case 2:
|
||||
print_string("1000M\t");
|
||||
break;
|
||||
case 5:
|
||||
print_string("2.5G\t");
|
||||
break;
|
||||
default:
|
||||
print_string("Up\t");
|
||||
break;
|
||||
}
|
||||
} else { // An SFP Module TODO: This is for 1 module devices
|
||||
reg_read_m(RTL837X_REG_GPIO_00_31_INPUT);
|
||||
if (!(sfr_data[0] & 0x40)) {
|
||||
print_string("SFP OK\t");
|
||||
} else { // An SFP Module
|
||||
if (!gpio_pin_test(machine.sfp_port[machine.is_sfp[i]-1].pin_detect)) {
|
||||
print_string("SFP IN\t");
|
||||
} else {
|
||||
print_string("NO SFP\t");
|
||||
}
|
||||
reg_read_m(RTL837X_REG_GPIO_32_63_INPUT);
|
||||
if (sfr_data[3] & 0x20) {
|
||||
print_string("Down\t");
|
||||
} else {
|
||||
uint8_t rate = sfp_read_reg(0, 12);
|
||||
if (rate == 0xd)
|
||||
print_string("1000BX\t");
|
||||
else if (rate == 0x1f)
|
||||
print_string("2500G\t");
|
||||
else if (rate > 0x65 && rate < 0x70)
|
||||
print_string("10G\t");
|
||||
else
|
||||
print_string("Up\t");
|
||||
}
|
||||
}
|
||||
STAT_GET(0x2f, i);
|
||||
|
||||
uint8_t b = 0;
|
||||
|
||||
// Determine link state
|
||||
reg_read_m(RTL837X_REG_LINKS_STS);
|
||||
if(!((sfr_data[(i / 8) + 1] >> ( i % 8 ) & 1)))
|
||||
{
|
||||
b = 99;
|
||||
}
|
||||
else
|
||||
{
|
||||
if (i < 8)
|
||||
reg_read_m(RTL837X_REG_LINKS);
|
||||
else
|
||||
reg_read_m(RTL837X_REG_LINKS_89);
|
||||
|
||||
b = sfr_data[3 - ((i & 7) >> 1)];
|
||||
b = (i & 1) ? b >> 4 : b & 0xf;
|
||||
}
|
||||
|
||||
switch (b) {
|
||||
case 0:
|
||||
print_string("10M\t");
|
||||
break;
|
||||
case 1:
|
||||
print_string("100M\t");
|
||||
break;
|
||||
case 2:
|
||||
print_string("1000M\t");
|
||||
break;
|
||||
case 4:
|
||||
print_string("10G\t");
|
||||
break;
|
||||
case 5:
|
||||
print_string("2.5G\t");
|
||||
break;
|
||||
case 99:
|
||||
print_string("Down\t");
|
||||
break;
|
||||
default:
|
||||
print_string("Up\t");
|
||||
break;
|
||||
}
|
||||
STAT_GET(STAT_COUNTER_TX_PKTS, i);
|
||||
print_reg(RTL837X_STAT_V_LOW); write_char('\t');
|
||||
|
||||
STAT_GET(0x30, i);
|
||||
STAT_GET(STAT_COUNTER_ERR_PKTS, i);
|
||||
print_reg(RTL837X_STAT_V_LOW); write_char('\t');
|
||||
|
||||
STAT_GET(STAT_COUNTER_RX_PKTS, i);
|
||||
print_reg(RTL837X_STAT_V_LOW); write_char('\t');
|
||||
|
||||
STAT_GET(STAT_COUNTER_ERR_PKTS, i);
|
||||
print_reg(RTL837X_STAT_V_HIGH); write_char('\t');
|
||||
|
||||
STAT_GET(0x2e, i);
|
||||
print_reg(RTL837X_STAT_V_LOW); write_char('\t');
|
||||
|
||||
STAT_GET(0x30, i);
|
||||
print_reg(RTL837X_STAT_V_LOW); write_char('\t');
|
||||
print_string("\n");
|
||||
}
|
||||
}
|
||||
@@ -465,14 +429,14 @@ void port_stats_print(void) __banked
|
||||
|
||||
void port_isolate(register uint8_t port, __xdata uint16_t pmask)
|
||||
{
|
||||
if (port <= maxPort)
|
||||
if (port <= machine.max_port)
|
||||
REG_SET(RTL837X_PORT_ISOLATION_BASE + (port << 2), pmask);
|
||||
}
|
||||
|
||||
|
||||
uint16_t port_isolation_get(register uint8_t port)
|
||||
{
|
||||
if (port > maxPort)
|
||||
if (port > machine.max_port)
|
||||
return 0;
|
||||
|
||||
reg_read_m(RTL837X_PORT_ISOLATION_BASE + (port << 2));
|
||||
@@ -480,23 +444,55 @@ uint16_t port_isolation_get(register uint8_t port)
|
||||
}
|
||||
|
||||
|
||||
void port_eee_enable(uint8_t port) __banked
|
||||
void port_eee_enable(__xdata uint8_t port,__xdata uint8_t speed) __banked
|
||||
{
|
||||
if (is_sfp[port])
|
||||
return;
|
||||
|
||||
REG_SET(RTL8373_EEE_CTRL_BASE + (port << 2), EEE_100 | EEE_1000 | EEE_2G5);
|
||||
// Enable EEE advertisement for 100/1000BASE-T via EEE Advertisement Reg
|
||||
phy_write(port, PHY_MMD_AN, PHY_EEE_ADV, PHY_EEE_BIT_1G | PHY_EEE_BIT_100M);
|
||||
// Enable EEE advertisement for 2.5GBASE-T via EEE Advertisement Reg 2
|
||||
phy_write(port, PHY_MMD_AN, PHY_EEE_ADV2, PHY_EEE_BIT_2G5);
|
||||
phy_reset(port);
|
||||
if (machine.is_sfp[port])
|
||||
{
|
||||
print_string("EEE can't be enabled for SFP port "); print_byte(port); print_string("\n");
|
||||
return;
|
||||
}
|
||||
|
||||
print_string("EEE on for "); print_byte(port); print_string(" speed ");
|
||||
// Enable all speeds up to the specified speed
|
||||
if ((speed & (EEE_100 | EEE_1000 | EEE_2G5)) == EEE_100) {
|
||||
print_string("100m\n");
|
||||
REG_SET(RTL8373_EEE_CTRL_BASE + (port << 2), EEE_100);
|
||||
// Enable EEE advertisement for 100BASE-T via EEE Advertisement Reg
|
||||
phy_write(port, PHY_MMD_AN, PHY_EEE_ADV, PHY_EEE_BIT_100M);
|
||||
if (!(speed & EEE_NORESET))
|
||||
phy_reset(port);
|
||||
return;
|
||||
}
|
||||
if ((speed & (EEE_100 | EEE_1000 | EEE_2G5)) == EEE_1000) {
|
||||
print_string("1g\n");
|
||||
REG_SET(RTL8373_EEE_CTRL_BASE + (port << 2), EEE_100 | EEE_1000);
|
||||
// Disable EEE advertisement for 2.5GBASE-T via EEE Advertisement Reg 2
|
||||
phy_write(port, PHY_MMD_AN, PHY_EEE_ADV2, 0);
|
||||
// Enable EEE advertisement for 100/1000BASE-T via EEE Advertisement Reg
|
||||
phy_write(port, PHY_MMD_AN, PHY_EEE_ADV, PHY_EEE_BIT_1G | PHY_EEE_BIT_100M);
|
||||
if (!(speed & EEE_NORESET))
|
||||
phy_reset(port);
|
||||
return;
|
||||
}
|
||||
if ((speed & (EEE_100 | EEE_1000 | EEE_2G5)) == EEE_2G5) {
|
||||
print_string("2g5\n");
|
||||
REG_SET(RTL8373_EEE_CTRL_BASE + (port << 2), EEE_100 | EEE_1000 | EEE_2G5);
|
||||
// Enable EEE advertisement for 100/1000BASE-T via EEE Advertisement Reg
|
||||
phy_write(port, PHY_MMD_AN, PHY_EEE_ADV, PHY_EEE_BIT_1G | PHY_EEE_BIT_100M);
|
||||
// Enable EEE advertisement for 2.5GBASE-T via EEE Advertisement Reg 2
|
||||
phy_write(port, PHY_MMD_AN, PHY_EEE_ADV2, PHY_EEE_BIT_2G5);
|
||||
if (!(speed & EEE_NORESET))
|
||||
phy_reset(port);
|
||||
return;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
|
||||
void port_eee_disable(uint8_t port) __banked
|
||||
{
|
||||
if (is_sfp[port])
|
||||
if (machine.is_sfp[port])
|
||||
return;
|
||||
|
||||
print_string("EEE off for "); print_byte(port); write_char('\n');
|
||||
@@ -511,9 +507,9 @@ void port_eee_disable(uint8_t port) __banked
|
||||
|
||||
void port_eee_status(uint8_t port) __banked
|
||||
{
|
||||
print_string("Port: "); write_char('0' + log_to_phys_port[port]);
|
||||
print_string("Port: "); write_char('0' + machine.log_to_phys_port[port]);
|
||||
print_string(": ");
|
||||
if (is_sfp[port]) {
|
||||
if (machine.is_sfp[port]) {
|
||||
print_string("SFP\n");
|
||||
return;
|
||||
}
|
||||
@@ -564,17 +560,17 @@ void port_eee_status(uint8_t port) __banked
|
||||
}
|
||||
|
||||
|
||||
void port_eee_enable_all(void) __banked
|
||||
void port_eee_enable_all(__xdata uint8_t speed) __banked
|
||||
{
|
||||
for (uint8_t i = minPort; i <= maxPort; i++) {
|
||||
port_eee_enable(i);
|
||||
for (uint8_t i = machine.min_port; i <= machine.max_port; i++) {
|
||||
port_eee_enable(i, speed);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
void port_eee_disable_all(void) __banked
|
||||
{
|
||||
for (uint8_t i = minPort; i <= maxPort; i++) {
|
||||
for (uint8_t i = machine.min_port; i <= machine.max_port; i++) {
|
||||
port_eee_disable(i);
|
||||
}
|
||||
}
|
||||
@@ -582,7 +578,7 @@ void port_eee_disable_all(void) __banked
|
||||
|
||||
void port_eee_status_all(void) __banked
|
||||
{
|
||||
for (uint8_t i = minPort; i <= maxPort; i++) {
|
||||
for (uint8_t i = machine.min_port; i <= machine.max_port; i++) {
|
||||
port_eee_status(i);
|
||||
}
|
||||
}
|
||||
@@ -597,3 +593,35 @@ void port_rldp_on(__xdata uint16_t p_ms)
|
||||
REG_SET(RTL837X_RMA0_CONF, 0x00000000); // R4ecc
|
||||
REG_SET(RTL837X_RMA_CONF, 0x00000000); // R4ecc
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
* Configure LAGs
|
||||
* Sets the members via port bitmask of a given Link Aggregation Group
|
||||
* The groups have numbers 0-3
|
||||
* The bitmask represents up to 10 ports
|
||||
* If currently no LAG has algorithm used, a default is applied
|
||||
*/
|
||||
void port_lag_members_set(__xdata uint8_t lag, __xdata uint16_t members) __banked
|
||||
{
|
||||
print_string("port_lag_members_set, lag: "); print_byte(lag); print_string(", members: "); print_short(members);
|
||||
if (lag > 3)
|
||||
print_string("Link aggregation group must be 0-3!");
|
||||
reg_read_m(RTL837X_TRK_HASH_CTRL_BASE + (lag << 2));
|
||||
if (!(sfr_data[0] | sfr_data [1] | sfr_data [2] | sfr_data [3]))
|
||||
REG_SET(RTL837X_TRK_HASH_CTRL_BASE, LAG_HASH_DEFAULT);
|
||||
REG_WRITE(RTL837X_TRK_MBR_CTRL_BASE + (lag << 2), 0, 0, members >> 8, members & 0xff);
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
* Configures the hash algorithm used for a LAG
|
||||
* lag is the Group to configure and hash is a bitmask
|
||||
*/
|
||||
void port_lag_hash_set(__xdata uint8_t lag, __xdata uint8_t hash_bits) __banked
|
||||
{
|
||||
print_string("port_lag_hash_set, lag: "); print_byte(lag); print_string(", hash: "); print_byte(hash_bits);
|
||||
if (lag > 3)
|
||||
print_string("Link aggregation group must be 0-3!");
|
||||
REG_WRITE(RTL837X_TRK_HASH_CTRL_BASE + (lag << 2), 0, 0, 0, hash_bits);
|
||||
}
|
||||
|
||||
@@ -3,9 +3,9 @@
|
||||
|
||||
#include <stdint.h>
|
||||
|
||||
#define STAT_COUNTER_TX_PKTS 0x2e
|
||||
#define STAT_COUNTER_RX_PKTS 0x2f
|
||||
#define STAT_COUNTER_ERR_PKTS 0x30
|
||||
#define STAT_COUNTER_TX_PKTS 46
|
||||
#define STAT_COUNTER_RX_PKTS 47
|
||||
#define STAT_COUNTER_ERR_PKTS 48
|
||||
|
||||
#define STAT_GET(cnt, port) \
|
||||
REG_WRITE(RTL837X_STAT_GET, 0x00, 0x00, cnt >> 3, (cnt << 5) | (port << 1) | 1); \
|
||||
@@ -13,6 +13,14 @@
|
||||
reg_read_m(RTL837X_STAT_GET); \
|
||||
} while (sfr_data[3] & 0x1);
|
||||
|
||||
struct vlan_settings {
|
||||
uint16_t vlan;
|
||||
uint16_t members;
|
||||
uint16_t tagged;
|
||||
};
|
||||
|
||||
extern __xdata struct vlan_settings vlan_settings;
|
||||
|
||||
uint8_t port_l2_forget(void) __banked;
|
||||
void port_l2_learned(void) __banked;
|
||||
void port_stats_print(void) __banked;
|
||||
@@ -20,17 +28,18 @@ int8_t vlan_get(register uint16_t vlan) __banked;
|
||||
__xdata uint16_t vlan_name(register uint16_t vlan) __banked;
|
||||
void vlan_setup(void) __banked;
|
||||
void port_pvid_set(uint8_t port, __xdata uint16_t pvid) __banked;
|
||||
void vlan_create(register uint16_t vlan, register uint16_t members, register uint16_t tagged) __banked;
|
||||
void vlan_create(void) __banked;
|
||||
void vlan_delete(uint16_t vlan) __banked;
|
||||
void port_mirror_set(register uint8_t port, __xdata uint16_t rx_pmask, __xdata uint16_t tx_pmask) __banked;
|
||||
void port_mirror_del(void) __banked;
|
||||
void port_ingress_filter(register uint8_t port, uint8_t type) __banked;
|
||||
void port_ingress_filter(__xdata uint8_t port, __xdata uint8_t type) __banked;
|
||||
void port_l2_setup(void) __banked;
|
||||
void trunk_set(uint8_t group, uint16_t mask) __banked;
|
||||
void port_eee_enable_all(void) __banked;
|
||||
void port_lag_members_set(__xdata uint8_t lag, __xdata uint16_t members) __banked;
|
||||
void port_lag_hash_set(__xdata uint8_t lag, __xdata uint8_t hash) __banked;
|
||||
void port_eee_enable_all(__xdata uint8_t speed) __banked;
|
||||
void port_eee_disable_all(void) __banked;
|
||||
void port_eee_status_all(void) __banked;
|
||||
void port_eee_enable(uint8_t port) __banked;
|
||||
void port_eee_enable(__xdata uint8_t port, __xdata uint8_t speed) __banked;
|
||||
void port_eee_disable(uint8_t port) __banked;
|
||||
void port_eee_status(uint8_t port) __banked;
|
||||
#endif
|
||||
|
||||
@@ -1,24 +1,71 @@
|
||||
#ifndef _RTL837X_REGS_H_
|
||||
#define _RTL837X_REGS_H_
|
||||
|
||||
#define RTL837X_REG_HW_CONF 0x6040
|
||||
#define RTL837X_REG_CHIP_ID 0x0004
|
||||
#define RTL837X_REG_CHIP_INFO 0x000c
|
||||
#define RTL837X_REG_CHIP_UUID 0x0010
|
||||
#define RTL837X_REG_CHIP_LOT_NO 0x0014
|
||||
#define RTL837X_REG_RESET 0x0024
|
||||
#define RESET_SOC_BIT 0
|
||||
#define RESET_NIC_BIT 2
|
||||
|
||||
#define RTL837X_REG_HW_CONF 0x6040
|
||||
// Bits 4 & 5: CLOCK DIVIDER from 125MHz for Timer
|
||||
|
||||
#define RTL837X_REG_LED_MODE 0x6520
|
||||
#define SYS_LED_OFF 0
|
||||
#define SYS_LED_FAST 1
|
||||
#define SYS_LED_SLOW 2
|
||||
#define SYS_LED_ON 3
|
||||
|
||||
#define RTL837X_REG_LED_MODE 0x6520
|
||||
// Defines the LED Mode for steering the Port LEDS and the System LED
|
||||
// BIT 17 set: LED solid on
|
||||
// Bytes 0/1 hold the LED mode, e.g. serial, RTL8231?
|
||||
// Blink rate is defined by setAsicRegBits(0x6520,0xe00000,rate);
|
||||
#define RTL837X_REG_LED_GLB_MUX_1 0x65E0
|
||||
#define RTL837X_REG_LED_GLB_MUX_2 0x65E4
|
||||
#define RTL837X_REG_LED_GLB_MUX_3 0x65E8
|
||||
#define RTL837X_REG_LED_GLB_MUX_4 0x65EC
|
||||
#define RTL837X_REG_LED_GLB_MUX_5 0x65F0
|
||||
#define RTL837X_REG_LED_GLB_MUX_6 0x65F4
|
||||
#define RTL837X_REG_LED_GLB_ACTIVE 0x65D8
|
||||
#define RTL837X_REG_LED_GLB_IO_EN 0x65DC
|
||||
#define RTL837X_REG_LED3_0_SET3 0x6524
|
||||
#define RTL837X_REG_LED3_0_SET1 0x6528
|
||||
#define RTL837X_REG_LED1_0_SET3 0x6530
|
||||
#define RTL837X_REG_LED3_2_SET2 0x6534
|
||||
#define RTL837X_REG_LED1_0_SET2 0x6538
|
||||
#define RTL837X_REG_LED3_2_SET1 0x653C
|
||||
#define RTL837X_REG_LED1_0_SET1 0x6540
|
||||
#define RTL837X_REG_LED3_2_SET0 0x6544
|
||||
#define RTL837X_REG_LED1_0_SET0 0x6548
|
||||
#define RTL837X_LED_PORT_SET_SEL 0x654c
|
||||
|
||||
#define RTL837X_REG_SMI_CTRL 0x6454
|
||||
#define RTL837X_REG_RESET 0x0024
|
||||
// Writing 0x01 into this register causes a reset of the entire SoC
|
||||
// SMI control
|
||||
#define RTL837X_REG_SMI_PORT0_5_ADDR 0x644C
|
||||
#define RTL837X_REG_SMI_PORT6_9_ADDR 0x6450
|
||||
#define RTL837X_REG_SMI_CTRL 0x6454
|
||||
#define RTL837X_REG_SMI_MAC_TYPE 0x6330
|
||||
#define RTL837X_REG_SMI_PORT_POLLING 0x6334
|
||||
|
||||
#define RTL837X_REG_SEC_COUNTER 0x06f4
|
||||
#define RTL837X_REG_SEC_COUNTER2 0x06f8
|
||||
// Used for counting seconds
|
||||
|
||||
#define RTL837X_REG_SDS_MODES 0x7b20
|
||||
|
||||
/*
|
||||
* SDS
|
||||
*/
|
||||
#define RTL837X_SDS_INDACS_CMD 0x3F8
|
||||
#define RTL837X_SDS_INDACS_WRITE_DATA 0x400
|
||||
#define RTL837X_REG_SDS_MODES 0x7b20
|
||||
|
||||
/*
|
||||
* PHY
|
||||
*/
|
||||
#define RTL837X_CFG_PHY_TX_POLARITY_SWAP 0xA94
|
||||
#define RTL837X_CFG_PHY_MDI_REVERSE 0xA90
|
||||
|
||||
/*
|
||||
* 5 Bits each give the state of the 2 SerDes of the RTL8372
|
||||
* Values are:
|
||||
@@ -31,7 +78,10 @@
|
||||
#define SDS_10GR 0x1a
|
||||
#define SDS_OFF 0x1f
|
||||
|
||||
#define RTL837X_REG_LINKS 0x63f0
|
||||
#define RTL837X_REG_LINKS 0x63f0
|
||||
#define RTL837X_REG_LINKS_89 0x63f4
|
||||
#define RTL837X_REG_LINKS_STS 0x63E8
|
||||
|
||||
/* Each nibble encodes the link state of a port.
|
||||
Port 0 appears to be the CPU port
|
||||
The RTL8372 serves ports 4-7, port 3 is the RTL8221
|
||||
@@ -46,6 +96,7 @@
|
||||
|
||||
#define RTL837X_PIN_MUX_0 0x7f8c
|
||||
#define RTL837X_PIN_MUX_1 0x7f90
|
||||
#define RTL837X_PIN_MUX_2 0x7f94
|
||||
|
||||
// Output Registers
|
||||
#define RTL837X_REG_GPIO_00_31_OUTPUT 0x3c
|
||||
@@ -77,11 +128,19 @@
|
||||
/*
|
||||
* NIC Related registers
|
||||
*/
|
||||
#define RTL837X_REG_RX_CTRL 0x785c
|
||||
#define RTL837X_REG_TX_CTRL 0x7860
|
||||
#define RTL837X_REG_RX_AVAIL 0x7874
|
||||
#define RTL837X_REG_RX_RINGPTR 0x787c
|
||||
#define RTL837X_REG_RX_DONE 0x784c
|
||||
#define RTL837X_REG_NIC_BUFFSIZE_TX 0x7844
|
||||
#define RTL837X_REG_NIC_RXBUFF_RX 0x7848
|
||||
#define RTL837X_REG_NIC_RXCMD 0x784c
|
||||
#define RTL837X_REG_NIC_TXCMD 0x7850
|
||||
#define RTL837X_REG_RX_CTRL 0x785c
|
||||
#define RTL837X_REG_TX_CTRL 0x7860
|
||||
#define RTL837X_REG_NIC_RX_BUFF_DATA 0x7874
|
||||
#define RTL837X_REG_CPU_RX_CURR_PKT 0x787c
|
||||
#define RTL837X_REG_NIC_TX_CURR_PKT 0x7884
|
||||
#define RTL837X_REG_CPU_TX_CURR_PKT 0x7890
|
||||
#define RTL837X_REG_CPU_TAG 0x6720
|
||||
#define RTL837X_REG_CPU_TAG_AWARE_PMASK 0x603C
|
||||
#define RTL837X_REG_MAC_FORCE_MODE 0x6344
|
||||
|
||||
/*
|
||||
* Statistics related registers
|
||||
@@ -105,18 +164,43 @@
|
||||
// Table types
|
||||
#define TBL_L2_UNICAST 0x04
|
||||
#define TBL_VLAN 0x03
|
||||
// Table read methods for the L2 table (TBL_L2_UNICAST):
|
||||
#define TBL_LUTREAD_MAC 0
|
||||
#define TBL_LUTREAD_ADDRESS 1
|
||||
#define TBL_LUTREAD_NEXT_ADDRESS 2
|
||||
#define TBL_LUTREAD_NEXT_L2UC 3
|
||||
#define TBL_LUTREAD_NEXT_L2MC 4
|
||||
#define TBL_LUTREAD_NEXT_L3MC 5
|
||||
#define TBL_LUTREAD_NEXT_L2L3MC 6
|
||||
#define TBL_LUTREAD_NEXT_L2UCSPA 7
|
||||
|
||||
#define RTL837X_L2_CTRL 0x5350
|
||||
#define L2_CTRL_LUT_IPMC_HASH 3
|
||||
#define RTL837x_TBL_DATA_0 0x5cb0
|
||||
#define RTL837x_L2_DATA_OUT_A 0x5ccc
|
||||
#define RTL837x_L2_DATA_OUT_B 0x5cd0
|
||||
#define RTL837x_L2_DATA_OUT_C 0x5cd4
|
||||
#define RTL837x_TBL_DATA_IN_A 0x5cb8
|
||||
#define RTL837x_TBL_DATA_IN_B 0x5cbc
|
||||
#define RTL837x_TBL_DATA_IN_C 0x5cc0
|
||||
#define RTL837x_PVID_BASE_REG 0x4e1c
|
||||
|
||||
#define RTL837x_L2_TBL_FLUSH_CTRL 0x53d4
|
||||
#define L2_TBL_FLUSH_EXEC 0x10000
|
||||
#define RTL837x_L2_TBL_FLUSH_CNF 0x53dc
|
||||
#define RTL837X_L2_LRN_PORT_CONSTRAINT 0x5384
|
||||
#define RTL837X_L2_LRN_PORT_CONSTRT_ACT 0x4f80
|
||||
#define RTL8373_REG_MAC_L2_PORT_MAX_LEN 0x1250
|
||||
|
||||
/*
|
||||
* VLAN configuration
|
||||
*/
|
||||
#define RTL837X_VLAN_CTRL 0x4e14
|
||||
#define VLAN_CVLAN_FILTER 0x4
|
||||
#define RTL837X_VLAN_PORT_EGR_TAG 0x6738
|
||||
#define RTL837X_VLAN_PORT_IGR_FLTR 0x4e18
|
||||
#define RTL837X_VLAN_L2_LRN_DIS_0 0x4e30
|
||||
#define RTL837X_VLAN_L2_LRN_DIS_1 0x4e34
|
||||
|
||||
/*
|
||||
* Egress / ingress filtering
|
||||
@@ -134,10 +218,18 @@
|
||||
#define RTL837x_MIRROR_CTRL 0x6048
|
||||
|
||||
/*
|
||||
* Trunking
|
||||
* Link Aggregation aka Trunking
|
||||
*/
|
||||
#define RTL837x_TRUNK_CTRL_A 0x4f38
|
||||
#define RTL837x_TRUNK_CTRL_B 0x4f3c
|
||||
#define RTL837X_TRK_MBR_CTRL_BASE 0x4f38
|
||||
#define RTL837X_TRK_HASH_CTRL_BASE 0x4f48
|
||||
#define LAG_HASH_SOURCE_PORT_NUMBER 0x01
|
||||
#define LAG_HASH_L2_SMAC 0x02
|
||||
#define LAG_HASH_L2_DMAC 0x04
|
||||
#define LAG_HASH_L3_SIP 0x08
|
||||
#define LAG_HASH_L3_DIP 0x10
|
||||
#define LAG_HASH_L4_SPORT 0x20
|
||||
#define LAG_HASH_L4_DPORT 0x40
|
||||
#define LAG_HASH_DEFAULT (LAG_HASH_L2_SMAC | LAG_HASH_L2_DMAC | LAG_HASH_L3_SIP | LAG_HASH_L3_DIP | LAG_HASH_L4_SPORT | LAG_HASH_L4_DPORT)
|
||||
|
||||
/*
|
||||
* Port isolation
|
||||
@@ -147,9 +239,20 @@
|
||||
/*
|
||||
* Multicast handling
|
||||
*/
|
||||
#define RTL837X_MC_LOOKUPMISS_ACTIONS 0x4f78
|
||||
#define RTL837X_IPV4_PORT_MC_LM_ACT 0x4f78
|
||||
#define RTL837X_IPV6_PORT_MC_LM_ACT 0x4f7c
|
||||
#define RTL837X_IGMP_PORT_CFG 0x52a0
|
||||
#define RTL837X_MC_FLOODMASK 0x5368
|
||||
#define IGMP_MAX_GROUP 0x00ff0000
|
||||
#define IGMP_PROTOCOL_ENABLE 0x00007c00
|
||||
#define IGMP_TRAP 0x0000002a
|
||||
#define IGMP_FLOOD 0x00000015
|
||||
#define IGMP_ASIC 0x00000000
|
||||
#define RTL837X_IGMP_ROUTER_PORT 0x529c
|
||||
#define RTL837X_IPV4_UNKN_MC_FLD_PMSK 0x5368
|
||||
#define RTL837X_IPV6_UNKN_MC_FLD_PMSK 0x536c
|
||||
#define RTL837X_IGMP_TRAP_CFG 0x50bc
|
||||
#define IGMP_TRAP_PRIORITY 0x7
|
||||
#define IGMP_CPU_PORT 0x00010000
|
||||
|
||||
/*
|
||||
* Loop detection / STP
|
||||
@@ -158,6 +261,9 @@
|
||||
#define RTL837X_RMA0_CONF 0x4ecc
|
||||
#define RTL837X_RMA_CONF 0x4f1c
|
||||
#define RTL837X_MSTP_STATES 0x5310
|
||||
#define RTL837X_REG_LED_RLDP_1 0x65F8
|
||||
#define RTL837X_REG_LED_RLDP_2 0x65FC
|
||||
#define RTL837X_REG_LED_RLDP_3 0x6600
|
||||
|
||||
/*
|
||||
* EEE
|
||||
@@ -169,7 +275,34 @@
|
||||
#define EEE_100 0x01
|
||||
#define EEE_1000 0x04
|
||||
#define EEE_2G5 0x10
|
||||
#define EEE_NORESET 0x80
|
||||
|
||||
/*
|
||||
* RANDOM
|
||||
*/
|
||||
#define RTL837X_RLDP_RLPP 0x106C
|
||||
#define RLDP_RND_EN 3
|
||||
#define RTL837X_RAND_NUM0 0x107C
|
||||
#define RTL837X_RAND_NUM1 0x1080
|
||||
|
||||
/*
|
||||
* Bandwidth control
|
||||
*/
|
||||
#define RTL837X_IGBW_CTRL 0x4c10
|
||||
#define IGBW_INC_BYPASS_PKT 0x100
|
||||
#define IGBW_INC_IFG 0x80
|
||||
#define IGBW_ADM_DHCP 0x20
|
||||
#define IGBW_ADM_ARPREQ 0x10
|
||||
#define IGBW_ADM_RMA 0x08
|
||||
#define IGBW_ADM_BPDU 0x04
|
||||
#define IGBW_ADM_RTKPKT 0x02
|
||||
#define IGBW_ADM_IGMP 0x01
|
||||
#define RTL837X_IGBW_PORT_CTRL 0x4C18
|
||||
#define RTL837X_IGBW_PORT_FC_CTRL 0x4C8C
|
||||
#define RTL837X_EGBW_PORT_CTRL 0x1c34
|
||||
#define RTL837X_EGBW_CTRL 0x447c
|
||||
#define EGBW_INC_IFG 0x02
|
||||
#define EGBW_CPUMODE 0x01
|
||||
|
||||
#ifdef REGDBG
|
||||
|
||||
|
||||
@@ -40,11 +40,9 @@ __sfr __at(0x91) EXIF;
|
||||
__sfr __at(0xf8) EIP;
|
||||
__sbit __at(0xf9) PX3;
|
||||
|
||||
/* SFR control registers for serial communication */
|
||||
__sfr __at(0xc8) T2CON;
|
||||
__sfr __at(0xca) RCAP2L;
|
||||
__sfr __at(0xcb) RCAP2H;
|
||||
|
||||
/* MPAGE page (sdcc _XPAGE) register for "movx @Ri" */
|
||||
__sfr __at(0x92) _XPAGE; /* _XPAGE is used by sdcc in xstack mode */
|
||||
__sfr __at(0x92) MPAGE;
|
||||
/* SFR Bank control register: 0x0-3f. A value of 0 is bank 1 */
|
||||
__sfr __at(0x96) PSBANK;
|
||||
// SFR used to store return bank for trampoline
|
||||
@@ -97,3 +95,19 @@ __sfr __at(0xb4) SFR_NIC_DATA_H;
|
||||
__sfr16 __at(0xb6b5) SFR_NIC_RING_U16LE;
|
||||
__sfr __at(0xb5) SFR_NIC_RING_L;
|
||||
__sfr __at(0xb6) SFR_NIC_RING_H;
|
||||
|
||||
/* Standard 8051 sfr */
|
||||
// Timer 0 value
|
||||
__sfr16 __at(0x8c8a) T0_U16;
|
||||
|
||||
// Timer 1 enable interrupt
|
||||
__sbit __at(0xad) ET2;
|
||||
|
||||
// Timer 2
|
||||
__sfr __at(0xcc) TL2;
|
||||
__sfr __at(0xcd) TH2;
|
||||
__sfr16 __at(0xcdcc) T2_U16;
|
||||
__sfr __at(0xc8) T2CON;
|
||||
__sfr __at(0xca) RCAP2L;
|
||||
__sfr __at(0xcb) RCAP2H;
|
||||
__sfr16 __at(0xcbca) RCAP2_U16;
|
||||
|
||||
@@ -12,15 +12,12 @@
|
||||
#include "rtl837x_regs.h"
|
||||
#include "rtl837x_stp.h"
|
||||
#include "uip.h"
|
||||
#include "machine.h"
|
||||
|
||||
extern __xdata uint8_t minPort;
|
||||
extern __xdata uint8_t maxPort;
|
||||
extern __xdata uint8_t nSFPPorts;
|
||||
extern __xdata uint8_t cpuPort;
|
||||
extern __xdata uint8_t isRTL8373;
|
||||
extern __code struct machine machine;
|
||||
extern __xdata uint8_t sfr_data[4];
|
||||
|
||||
extern __code struct uip_eth_addr uip_ethaddr;
|
||||
extern __xdata struct uip_eth_addr uip_ethaddr;
|
||||
|
||||
extern __xdata uint8_t uip_buf[UIP_CONF_BUFFER_SIZE + 2];
|
||||
|
||||
@@ -38,16 +35,6 @@ __xdata uint16_t port_timers[10];
|
||||
__xdata uint16_t port_hello[10];
|
||||
|
||||
|
||||
// 8899 04 0000 20 0004
|
||||
struct rtl_tag {
|
||||
uint16_t tag;
|
||||
uint8_t version;
|
||||
uint16_t dummy;
|
||||
uint8_t flag;
|
||||
uint16_t pmask;
|
||||
};
|
||||
|
||||
|
||||
struct stp_pkt {
|
||||
uint8_t stp_addr[6];
|
||||
uint8_t src_addr[6];
|
||||
@@ -164,8 +151,8 @@ void stp_cnf_send(uint8_t port)
|
||||
|
||||
STP_O->rtl_tag.tag = HTONS(0x8899);
|
||||
STP_O->rtl_tag.version = 0x04;
|
||||
STP_O->rtl_tag.dummy = 0x0000;
|
||||
STP_O->rtl_tag.flag = 0x20; // WHY ???
|
||||
STP_O->rtl_tag.reason = 0x00;
|
||||
STP_O->rtl_tag.flags = 0x0020; // Disable L2 learning
|
||||
STP_O->rtl_tag.pmask = HTONS(((uint16_t)1) << port);
|
||||
|
||||
STP_O->msg_len = HTONS(0x27);
|
||||
@@ -177,9 +164,9 @@ void stp_cnf_send(uint8_t port)
|
||||
STP_O->bpdu_type = 0x00; // Config
|
||||
STP_O->flags = 0x81;
|
||||
|
||||
memcpyc(STP_O->src_addr, uip_ethaddr.addr, 6);
|
||||
memcpy(STP_O->src_addr, uip_ethaddr.addr, 6);
|
||||
memcpy(STP_O->root.mac, root_bridge.mac, 6);
|
||||
memcpyc(STP_O->bridge.mac, uip_ethaddr.addr, 6);
|
||||
memcpy(STP_O->bridge.mac, uip_ethaddr.addr, 6);
|
||||
|
||||
STP_O->root.prio = root_bridge.prio;
|
||||
STP_O->root.ext = 0x00;
|
||||
@@ -203,7 +190,7 @@ void stp_cnf_send(uint8_t port)
|
||||
|
||||
void stp_timers(void) __banked
|
||||
{
|
||||
for (uint8_t i = minPort; i <= maxPort; i++) {
|
||||
for (uint8_t i = machine.min_port; i <= machine.max_port; i++) {
|
||||
port_hello[i]--;
|
||||
if (!port_hello[i]) {
|
||||
port_hello[i] = TIME_HELLO;
|
||||
@@ -219,7 +206,7 @@ void stp_setup(void) __banked
|
||||
{
|
||||
print_string("Enabling STP: ");
|
||||
sfr_data[0] = sfr_data[1] = sfr_data[2] = sfr_data[3] = 0;
|
||||
for (uint8_t i = minPort; i <= maxPort; i++) {
|
||||
for (uint8_t i = machine.min_port; i <= machine.max_port; i++) {
|
||||
// Set STP port state to blocking
|
||||
// States are: 00 disable, 01 blocking, 10 learning, 11 forwarding
|
||||
uint8_t bit_mask = 0b01 << ( (i << 1) & 0x7);
|
||||
@@ -234,14 +221,14 @@ void stp_setup(void) __banked
|
||||
|
||||
root_bridge.prio = 0x80; // This corresponds to 32768
|
||||
root_bridge.ext = 0x00;
|
||||
memcpyc(root_bridge.mac, uip_ethaddr.addr, 6);
|
||||
memcpy(root_bridge.mac, uip_ethaddr.addr, 6);
|
||||
}
|
||||
|
||||
|
||||
void stp_off(void) __banked
|
||||
{
|
||||
sfr_data[0] = sfr_data[1] = sfr_data[2] = sfr_data[3] = 0;
|
||||
for (uint8_t i = minPort; i <= maxPort; i++) {
|
||||
for (uint8_t i = machine.min_port; i <= machine.max_port; i++) {
|
||||
// Set STP port state to forwarding
|
||||
// States are: 00 disable, 01 blocking, 10 learning, 11 forwarding
|
||||
uint8_t bit_mask = 0b11 << ( (i << 1) & 0x7);
|
||||
|
||||