130 Commits
Author SHA1 Message Date
Rob Emery 1400431337 Merge pull request #575 from logicog/zoerax
doc: Add HOWTO for using firmware tracing to figure out LED configuration
2026-10-29 08:54:01 +00:00
Rob Emery 119b5ddf13 Merge pull request #743 from gnacho/i18n/es-strings-20261028
l10n: complete the Spanish translation
2026-10-29 08:26:02 +00:00
René van Dorst 231b43b264 Merge pull request #742 from vankxr/device/add-mac
Add MAC offset for GT_ST024M and YS25_0402M
2026-10-28 20:01:19 +00:00
René van Dorst e53ebddc21 Merge pull request #727 from andrebrait/fix/json-array-close
httpd: close the port arrays after their loops
2026-10-28 19:46:25 +00:00
gnacho f23eb6bebb l10n: complete the Spanish translation 2026-10-28 20:45:41 +01:00
João Silva cd51cffb1d Add MAC offset for GT_ST024M and YS25_0402M
Signed-off-by: João Silva <jgc3silva@gmail.com>
2026-10-28 19:06:55 +00:00
Rob Emery 01fc5db9a3 Merge pull request #737 from DrDoof/fix/stp-port-id-panel
stp: use the front panel number as the port ID
2026-10-28 15:32:38 +00:00
DrDoof bdca888ecd stp: use the front panel number as the port ID
The port ID in the BPDUs was the chip port plus one, so on most boards
it did not match the port label, neither on the Spanning Tree page nor
on the neighbour. Send the front panel number instead, and 101 and up
for the LAGs, the same numbers the page already uses for the Port
column. A returned BPDU of our own is mapped back to the port by that
number.

Scenario 66 gives one port a front panel number that differs from its
chip port.
2026-10-28 16:05:12 +01:00
Andre Brait e8431999e5 httpd: close the port arrays after their loops
send_lldp(), send_eee(), send_bandwidth(), send_storm(), send_mtu() and
send_status() wrote the closing ']' inside the loop, on the last port.
Write it once after the loop, as send_isolation() already does, so the
array is closed however the loop ends.
2026-10-28 11:08:54 +01:00
René van Dorst 7ca4d2c8dc Merge pull request #723 from zoeimogen/main
SWTG024AS-V2.1.0 support (Xikestor SKS3200-4E2X, Sodola SL902-SWTG024AS)
2026-10-28 08:09:02 +01:00
Zoe O'Connell 32dd21bbd3 Add additional information on SWTG024AS-V2.1.0 2026-10-28 00:15:26 +00:00
feelfree69 92f9025109 Merge pull request #671 from gtytup/s25g1109fp
Add Sirivision SR-S25G2109F device (PCB S25G1109FP V1.3)
2026-10-27 22:38:33 +01:00
feelfree69 b65aa82d78 Merge pull request #616 from logicog/zoerax_hg0402xg
New Device: Add support for ZoeRax HG0402XG V1.1
2026-10-27 22:36:49 +01:00
logicog 851eb39344 Merge pull request #444 from DrDoof/feat/rstp
stp: full RSTP and a reworked Spanning Tree page
2026-10-27 22:34:54 +01:00
João Silva 970e98f4c5 test/stp: pack host structs like SDCC and cover the edge recovery
Build the harness with -fpack-struct=1 and assert the frame struct sizes,
so the host reads wire bytes the way the firmware lays them out. Add
scenarios for a port that regains edge after a hold, one that heard a BPDU
and stays non-edge, auto-edge switched off, and a captured RouterOS BPDU
fed in on every port, tagged and untagged.
2026-10-27 22:11:24 +01:00
João Silva 4d009bc8e8 stp: give auto-edge back to a port held by its own BPDU
stp_loop_hold_peer() takes operEdge away from the port that sent the
BPDU we got back, though that port heard nothing itself. When the hold
ran out the port stayed non-edge until its next link change, and every
later state change on it counted as a topology change. Restore edge when
the hold ends with the port still silent and auto-edge on, before the
transition is reported, so the recovery itself is not a change. Hellos
go out on edge ports too, so a loop that persists is caught again.

The console column that shows seconds since the last BPDU is now called
age[s]; it never was a receive count.
2026-10-27 22:11:24 +01:00
d00f eb51128ec7 stp: a port without carrier does not become the root port
stp_reselect() took every port with fresh information as a candidate, so a
BPDU credited to a port whose carrier the once-a-second link poll had not
seen yet made that port the root port before the port was set up for it.
A candidate now needs its link bit as well; the poll that sees the carrier
a moment later runs the selection again. Scenario 54.
2026-10-27 22:11:24 +01:00
d00f b86fd37eaa stp: pass a topology change on only from a forwarding port
stp_in() propagated the TC flag of any BPDU that did not arrive on an
alternate port, and acknowledged and propagated a TCN from any port, so a
port still in its listen period or held discarding in a dispute could flush
the addresses of every other port and move the topology change counter.
802.1D-2004 17.31 keeps the Topology Change machine in LEARNING until the
port forwards and drops rcvdTc and rcvdTcn there. Both paths now ask for
the forwarding state first; an alternate port is discarding, so the old
check is covered by the new one.

Scenario 53 plays a TC flag and a TCN into a port that is still discarding,
and then into the same port once it forwards. Scenario 51 used a TCN on a
port one tick short of its edge transition as the trigger for the clock it
tests; it now waits for the transition.
2026-10-27 22:11:24 +01:00
d00f b92a773cad test/stp: tag the frames the simulator feeds in
The frame the simulator builds carries a vlan_tag between the RTL tag and the BPDU, but left its TPID zero. stp_in() now takes the body offset from TRAP_RX_BODY(), which reads those two bytes, so it took the untagged branch and parsed the body four bytes early. Writing 0x8100 makes the frame what its layout says it is.
2026-10-27 22:11:24 +01:00
d00f 068e7467af test/stp: stub the RMA action the trap now sets
Reception moved to the RMA trap in main, so rtl837x_stp.c calls trap_rma_set() from stp_setup() and stp_off(). The simulator defines neither, so it does not link. Linking rtl837x_trap.c instead would pull trap_init() and the register primitives it needs, which the simulator does not mock.
2026-10-27 22:11:24 +01:00
DrDoof da4f5058a7 test/stp: declarations first, one statement per line
stp_sim.c declared variables in the middle of the scenarios and put
whole loops and conditions on one line. Each block now starts with its
declarations; an initializer that depends on what happens before it
stays where it was as an assignment. Bodies of if and for are on lines
of their own, functions are separated by an empty line, and lines stay
within 120 columns. The output of stp_sim -v is unchanged.
2026-10-27 22:11:24 +01:00
DrDoof fee4f17bff test/stp: print each scenario heading once
Scenarios 4 and 7 start from the state scenario 3 leaves behind, and 38
from the one 37 leaves, and they called those scenarios for it, so the
headings of 3 and 37 showed up again in the middle of the run. The setup
of 3 and 37 now lives in functions of its own that the scenarios call.
2026-10-27 22:11:24 +01:00
DrDoof e19271fc44 test/stp: build the simulator against the firmware headers
test/stp/shim held hand-written copies of the headers the STP code
includes, and they had drifted: the uIP buffer was 600 bytes instead of
1556, struct machine had three fields, port_ingress_filter_get()
returned a plain uint8_t. The simulator now compiles rtl837x_stp.c and
rtl837x_stp_cli.c the way test/ compiles firmware sources, with the real
headers and sdcc_shim.h forced in, and with the same warnings, including
-Wno-pointer-sign for the string literals passed to cmd_compare(). It
stays on -std=c11: in gnu11 mode glibc defines LITTLE_ENDIAN, which
uip-conf.h names as the byte order, and HTONS() stops swapping. Its own
print_string() and cmd_compare() take the firmware's argument types.
2026-10-27 22:11:24 +01:00
DrDoof aff6ac0923 stp: full RSTP and a reworked Spanning Tree page
* stp: select the root port by cost and block redundant links
* stp: move the protocol to BANK4 and keep the command line in BANK2
* stp: implement the RSTP state machines of 802.1D-2004 clause 17
* stp: counters, more settings and a fuller Spanning Tree page
* stp: make a link aggregation group one port of the RSTP tree
* web: serve the Spanning Tree JSON from BANK4 as well

Kept in BANK1, the larger Spanning Tree page would leave under 1 kB
there for the rest of the web server and every page still to come, so
send_stp() and send_stp_counters() move to httpd/page_stp.c in BANK4
with the protocol, as banked calls, with their own copies of the few output
helpers and of the JSON header, so nothing in BANK4 calls into BANK1
directly or hands it a pointer into its code space. BANK4 takes the
free flash between BANK3 and the web files.

KP_9000_6XHML_X2: BANK1 44336, BANK2 36969, BANK3 11942, BANK4 20965
bytes of 49152.
2026-10-27 22:11:24 +01:00
logicog 7e11be0d97 Add ZoeRax HG0402XG V1.1 to supported devices 2026-10-27 22:05:22 +01:00
logicog 23862bc648 New Device: Add support for ZoeRax HG0402XG V1.1
The ZoeRax HG0402XG is an unmanaged 4 2.5GBit Ethernet, 2 SFP+
device.
2026-10-27 22:05:22 +01:00
feelfree69 6c62791d23 Merge pull request #722 from frogamic/fix-docker-oem-build
Document oem installer build in docker
2026-10-27 22:04:23 +01:00
feelfree69 413690a748 Merge pull request #711 from DrDoof/fix/installer-toolchain
installer: build with the toolchain from the tree
2026-10-27 22:03:33 +01:00
Zoë O'Connell 3910888052 Merge branch 'logicog:main' into main 2026-10-27 17:24:37 +00:00
Zoe O'Connell a0cb3c1850 SWTG024AS-V2.1.0 2026-10-27 17:24:03 +00:00
feelfree69 73bb4c8af2 Merge pull request #709 from L4ky/add-config-path-to-makefile
Add CONFIG parameter to make
2026-10-27 18:21:42 +01:00
feelfree69 0443ba428b Merge pull request #661 from Kamikaze94/feature/improve-strip-rj45-icon
WebUI: Change port strip icon for rj45 ports, latch added
2026-10-27 18:12:43 +01:00
feelfree69 62abd08aa2 Merge pull request #660 from Kamikaze94/bugfix/strip-sfp-icon
WebUI: Fix port strip icon for sfp ports
2026-10-27 18:11:25 +01:00
feelfree69 70426ff24a Merge pull request #720 from brunoz78/de-vlan
i18n(de): translate the new VLAN manager texts
2026-10-27 16:39:20 +01:00
feelfree69 2d62cc493d Merge pull request #719 from nappairam/swtg018as-v2-factory-mac
machine: read factory MAC on SWTG018AS-A V2.0
2026-10-27 16:37:08 +01:00
Dominic Shelton bbce2d6727 doc: Document Docker oem installer build 2026-10-28 00:52:31 +11:00
Mariappan Ramasamy 36de7b7241 machine: read factory MAC on SWTG018AS-A V2.0
Horaco HC-SWTGW218AS units on this PCB keep their factory MAC at
0x1FC000, the offset the other boards in machine/ read it from, so use
it instead of a generated locally administered one. If the flash holds
no valid MAC there, the generated one is still used.
2026-10-27 18:47:22 +08:00
Brunoz78andClaude Opus 5.5 ea320d813d i18n(de): translate the new VLAN manager texts
#609 added six texts to the VLAN page without a German translation.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-27 10:02:36 +01:00
Luca S. ca2abcc4e1 Merge branch 'main' into add-config-path-to-makefile 2026-10-26 20:01:58 +01:00
René van Dorst 4d7159092c Merge pull request #673 from andrebrait/fix/xdata-startup
startup: clear SFR 0x97 before the C runtime initialises xdata
2026-10-26 17:15:32 +00:00
Andre Brait 98763e3bff startup: clear MAP_FLASH_PAGE_TO_XDATA before the C runtime initialises xdata
Hardware reset starts the firmware with SFR 0x97 = 1, which maps xdata
0x4000-0xbfff to the flash image: reads return flash bytes, writes are
lost. main() cleared it only after __mcs51_genXINIT and the XSEG clear
had run, so XISEG (initialised globals) and the part of XSEG above
0x4000 kept stale RAM contents, e.g. session_timeout = 4484 and LLDP
blocked on logical ports 0, 3 and 5. Clear it in
__sdcc_external_startup(), which SDCC calls before both.

Rename SFR_97 to MAP_FLASH_PAGE_TO_XDATA.
2026-10-26 17:08:27 +01:00
logicog 80a30b705e Merge pull request #706 from DrDoof/feat/banner-compiler
build: show the compiler and the toolchain in the version banner
2026-10-26 14:41:50 +01:00
feelfree69 4c3e1fde81 Merge pull request #609 from mintsoft/vlan-manager-fixes
VLAN manager UI/fixes
2026-10-26 13:59:39 +01:00
feelfree69 5cd14e6227 Merge pull request #714 from rwalli/doc-hc-swtg124as-19650
doc: add HC-SWTG124AS on PCB-SWTG024AS-A-V2.0.1_19650 with SW1 reset …
2026-10-26 12:34:47 +01:00
rwalli@gmx.at 5105018d90 machine: reset button on GPIO48 for HC-SWTG124AS, drop braces around _19650
SW1 on PCB-SWTG024AS-A-V2.0.1_19650 is wired to GPIO48. Measured on
HC-SWTG124AS: GPIO48 reads 1 idle and 0 while the button is held.
HC_SWTG124AS.c is a symlink to PCB_SWTG024AS_A_2_0_1.c.
2026-10-26 12:23:28 +01:00
rwalli@gmx.at 5bc7cf43db doc: merge duplicate HC-SWTG124AS rows, note _19650 variant in existing entry 2026-10-26 12:00:38 +01:00
rwalli@gmx.at c776d10129 doc: add HC-SWTG124AS on PCB-SWTG024AS-A-V2.0.1_19650 with SW1 reset note 2026-10-26 11:30:31 +01:00
feelfree69 7b4d0d55d2 Merge pull request #713 from dobodu/main
Fix double sy_sysport_err and add new translations
2026-10-26 08:45:15 +01:00
Luca S. e95e450813 Add CONFIG parameter on make 2026-10-26 08:27:28 +01:00
dobodu fe1d4119d0 Fix double sy_sysport_err and add new translations
Removes
double entries for sy_sysport_err corrected
Add
sy_dns1, sy_dns2, sy_dns_err & sy_dns_dhcp translations
2026-10-26 00:13:49 +01:00
d00f c5f62bff95 build: name the sdcc/ revision in the toolchain line
A commit hash can be looked up directly, a checksum cannot. Print the last commit that touched sdcc/, with -dirty for local changes, and fall back to the checksum when git is not available. The toolchain directory keeps the checksum.
2026-10-25 22:49:37 +01:00
d00f 7535c0d9b7 installer: build with the toolchain from the tree
The installer Makefile called whatever sdcc was on the PATH, so an
installer built by hand came out of the system compiler, without the
fixes in sdcc/. Use the same toolchain/sdcc-<cksum> as the main Makefile,
built on demand by sdcc/build.sh if a bare installer build gets there
first, and keep CC= on the command line as the way to pick another
compiler. The README example shows the new command lines.
2026-10-25 22:44:51 +01:00
Luca S. a57319557d Add CONFIG parameter on make 2026-10-25 22:42:06 +01:00
d00f 1b29fb687f build: count the sdcc patches in the toolchain line
The checksum changes with every patch but does not say how many there are. Print the number of patches in sdcc/ next to it.
2026-10-25 21:23:32 +01:00
Rob Emery 2e62f9eea7 Adding the name of the port to the UI so it's easier to see what
you are changing
2026-10-25 16:33:54 +00:00
feelfree69 5c6a82d50f Merge pull request #568 from DrDoof/fix/upload-boundary-cr
httpd: re-check the byte that breaks a partial boundary match
2026-10-25 16:52:47 +01:00
d00f 1d158d21d1 build: show the compiler and the toolchain in the version banner
The boot banner and the version command print the SDCC release and
revision from the compiler's own macros, and the name of the in-tree
toolchain the Makefile built, which is the checksum of sdcc/ and so
changes with every patch in it. A build with CC given on the command
line says "system" instead, since that compiler need not carry the
patches. A plain release and the same release with the patches report
the same revision, so the toolchain line is what tells them apart.

Costs 66 bytes of BANK2.
2026-10-25 16:04:49 +01:00
feelfree69 26c420d26a Merge pull request #701 from hlyi/PSW8_25MG
Add REALHD_PSW8_25MG support and makefile enhancement
2026-10-25 15:52:26 +01:00
HL Yi eb394af95f add REALHD_PSW8_25MG support 2026-10-25 09:11:35 -05:00
feelfree69 4a4caeffd7 Merge pull request #703 from hlyi/make-chg
makefile enhancement for machine build
2026-10-25 14:37:47 +01:00
HL Yi 391360f517 makefile enhancement that only when both .h and .c exist to be considered as a device. It allows generic pcb-specific c not listed as a device 2026-10-25 06:21:45 -05:00
feelfree69 ed8e4b1a56 Merge pull request #560 from brunoz78/multi-session
httpd: four login sessions instead of one
2026-10-25 11:52:56 +01:00
feelfree69 78f28488c1 Merge pull request #534 from DrDoof/fix/html-min-stamp
build: minify the web files only when they change
2026-10-25 11:48:16 +01:00
Brunoz78andClaude Sonnet 5.5 e1c79ec4cd httpd: reuse the slot of a valid cookie, end sessions on passwd
Review feedback on #560 (DrDoof):

- A login that came with a cookie that is still valid took a second
  slot. The first one looked fresh, so the next login could push out
  someone who is using the switch. The slot of the cookie now gets the
  new id.
- Changing the password leaves no old login valid: session_init() from
  parse_passwd(). With one session the next login pushed a leftover one
  out, with several it would stay valid until the next restart.
- The 32-bit comparison of the last use times moves into a leaf function,
  so the compiler does not give it a 4-byte scratch in internal RAM.

Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
2026-10-25 10:51:23 +01:00
Brunoz78andClaude Opus 5.5 a7db0ca8b6 httpd: keep the login sessions in bank 3
The web server's bank had no room left for the four sessions once the
original firmware's more robust header handling came in. Picking a slot,
checking a cookie and starting a session move to sessions.c in bank 3;
httpd.c only calls session_init(), session_check() and session_new().
The random id is written straight into the reply, which drops
gen_random_hex_chars() from bank 1.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-25 10:51:05 +01:00
Claude 70f59b64f5 httpd: four login sessions instead of one
Logging in from a second browser ended the first one's session at its next
request: two browsers open on the switch logged each other out every few
seconds. Up to four sessions now, each with its own cookie and expiry; a
fifth login replaces the least recently used. The expiry check is a leaf
function so its 32-bit scratch stays out of internal RAM (stack still
159 bytes with HEALTH).

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01Tx2W7xVecMoKbBCkD2i8PG
(cherry picked from commit 03330f4077ef8872d861d951c8e44ece3c573935)
2026-10-25 10:51:05 +01:00
feelfree69 f0cbaba7b1 Merge pull request #672 from dobodu/main
French translation update
2026-10-25 10:20:24 +01:00
Rob Emery 2d5424fedc Fixing double firing of load vlan 1 when the tab is loaded 2026-10-25 09:06:30 +00:00
Rob Emery 23c8b2a8b3 Fixing the unsaved changes not appearing. Previously if a set of
changes were sent as a single cmd using \n separation then the
dirty regex would skip it as $ wouldn't match \n. with /m modifier
the regex will match
2026-10-25 09:06:30 +00:00
Rob Emery c9f6362da5 Previously the vlan 2 regex was assuming there were ports, now
it'll accept just the definition.
Also throughout the regex we have over-specified the number of digits
sometimes ports are 4 digits long, sometimes they are 2. This seems
unnecessary, 1 or more is sufficient
2026-10-25 09:06:30 +00:00
Rob Emery 742aef5461 Adding highlighting to the currently selected VLAN in the top table
so that it is more clear it is selected
2026-10-25 09:06:30 +00:00
Rob Emery a77d219baf Moving the mgmt switch into the bottom section so it applies when
the user clicks apply on the specific vlan. In order for this to
work when the user is simultaneously changing the vlan that their
machine is connected through and the mgmt vlan id, we need to be
able to send a batch of commands through to the switch atomically
so the switch can process all of them and the momentary disconnection
of the machine doesn't matter
2026-10-25 09:06:30 +00:00
Rob Emery f94204e56f Adding rename support for vlan 2026-10-25 09:06:30 +00:00
Rob Emery 8d6adedb56 Removing my changes to the sim, in favour of #629 2026-10-25 09:06:30 +00:00
Rob Emery c3f9acea10 Tidying up the new VLAN page and adding error messages
When adding a new VLAN, we should load it after addition
Prevent deleting the mgmt vlan
Adding a button to promote a vlan to be the management VLAN
We can't create a vlan called d or mgmt as that clashes with the
commands to delete and promote a vlan
Making the Management VLAN more obvious and adding a "confused"
disabled button to where delete and promote are
Switching to the i image and making it so you can click the entire
row apart from the cell where the buttons are (to prevent accidents)
Cleaning up the last bits of ingress filtering I can see
2026-10-25 09:06:30 +00:00
Rob Emery c46261e052 Using PVID=0 for the situation where the port is only in tagged
membership. This was tested on HI_K0801WS and it appears to not
have any negative side-effects
2026-10-25 09:06:30 +00:00
Rob Emery 834403b97b Parking ports with only tagged membership into VLAN 4094 for now
I think this should also resolve the work-flow where you can get
a port's PVID and tagged membership to the same vlan
2026-10-25 09:06:30 +00:00
Rob Emery 48087d3520 Decoupling the load from the spinner so we can get rid of it
Also making the first VLAN in the table load when we load the UI

Removing the tickbox for the PVID, I'll swap this out to ticks
or something later on, but the PVID will be automatically set
based on the untagged membership, otherwise it will be 1

Send the vlan with name command when attempting to add a new VLAN
Because of the structure of the vlan command, we need to send the name as well if its set

Set the PVID based on the untagged membership and reload the
table when applied. We need to block setting the PVID to the tagged by proxy

Blue button

Need to provide array of pvid commands not concat
Storing state of each vlan so we can refer later

Blue button
2026-10-25 09:06:30 +00:00
Rob Emery 1d359afba5 Adding vlanlist support into http_sim so it's easier to work on
Also improving vlan.json to mirror the real output
2026-10-25 09:06:30 +00:00
gtytup ec14dbf323 doc: add Sirivision SR-S25G2109F (S25G1109FP V1.3)
Device page following doc/adding-a-device.md: label data, PCB versus label
naming, CPU identified with 'regget 4' (RTL8373, non-N), separate PHY and flash
identification, build target, PCB silkscreen, port mapping, board signals, front
panel and LED configuration, reset button, MAC location, what works and what is
unverified, plus photos.  Row added to supported_devices.md.
2026-10-25 15:37:28 +08:00
gtytup deedb92c5d machine: add Sirivision S25G1109FP V1.3 (8x2.5G + 1xSFP+)
RTL8373 (non-N) with an RTL8224 quad 2.5G PHY, 2 MB GD25Q16ESIG flash, no PoE.
The wiring is close to the KP-9000-9XHML-X V3.1 (same 8+1 topology, same I2C on
GPIO39/40), but three things differ and were determined on hardware:

- ModAbs is GPIO30 (low with a module inserted, high with an empty cage) and LOS
  is GPIO37 (high without light), not GPIO38.  GPIO38, the ModAbs pin of the
  KP-9000 reference, is tied low on this board, which made the firmware believe a
  module was permanently inserted and retry the I2C read forever.
- The reset button is GPIO54, not GPIO48.  GPIO48 never changes here, so the
  button appeared to do nothing.
- The LED wiring follows the stock port order (pin 3n+m belongs to port n+1,
  LED function m), so the mux table is the chip default except that pin 25 --
  the SFP LED -- is driven by function 0, the only populated function in SET1.
  Per copper port the left LED shows link/activity and the right LED shows
  2.5G, matching the front panel labels.

Verified on hardware: all eight 2.5G ports at 10/100/1000/2500 Mbps, 1G and 10G
SFP modules including DDM data, both LED columns, module insert/remove and the
reset button.
2026-10-25 15:37:28 +08:00
logicog a18c334139 Merge pull request #649 from DrDoof/build/sdcc-in-tree
build: build SDCC 4.6.0 with the mcs51 fixes in the tree
2026-10-25 06:18:24 +01:00
DrDoof 337b171ac4 build: minify the web files only when they change
html_min was phony, so every make deleted output/html_min and minified
all of html/ again, even with nothing changed. A stamp file now stands for
the minified copy and depends on the sources and on minify.py. A list of
the source names, rewritten only when it changes, is a dependency too, so
adding or removing a file in html/ still redoes the copy and a removed
file does not stay behind in the image. If output/html_min is missing,
the stamp is remade in the same run.

The note on version.h in webui-compression.md still said it was phony.
2026-10-25 03:11:03 +01:00
d00f 53eb3f5486 Merge pull request #583 from avicarmeli/fix/cpu-vlan-init
Fix CPU port PVID initialization independent of user port range
2026-10-25 02:44:18 +01:00
d00f 241edfdbda test: every last byte, CR runs and broken matches through the upload
Three more bench scenarios around the closing boundary. Every value
of the last image byte, uploaded whole and split right after the
payload, must end the part. A run of CR before the boundary of even
length passed before the fix and an odd one did not, so both are
checked. An image made of CR only, longer than a flash page, and one
made of repeated "\r\n-" exercise the broken matches back to back:
each one appends to the page buffer without a byte written, and the
page flush after both paths has to keep up.
2026-10-25 02:49:21 +02:00
DrDoof 714c9eb343 httpd: re-check the byte that breaks a partial boundary match
stream_upload() matches the closing multipart boundary byte by byte.
When a byte breaks a partial match, the matched prefix goes to flash as
data, and so does the breaking byte, without being compared against the
start of the boundary.

The boundary starts with "\r\n--", so an image whose last byte is '\r'
never completes: that '\r' starts a match, the '\r' of the boundary
breaks it and is written as data, and the rest of the boundary no
longer matches. The browser waits for a reply that never comes.

Flush the matched prefix and look at the breaking byte again from the
start of the boundary. A boundary may not contain CR, so no longer
suffix of the matched prefix can start a new match.
2026-10-25 02:49:14 +02:00
feelfree69 f2c89fd75b Merge pull request #651 from Kamikaze94/bugfix/sidebar-scrolling
WebUI: Lock sidebar layout and move scrolling to main element
2026-10-25 00:21:08 +02:00
avicarmeli ca2f1d101d Merge upstream main into CPU VLAN fix 2026-10-24 23:38:25 +03:00
d00f 9cc859ad30 build: carry the sdas fix for SDCC bug 4070
The 8051 assembler sign-extends every value to 24 bits, so the top
byte of a __sfr32 address list like 0xa4a5a6a7 comes out as 0xff.
The patch keeps 32-bit arithmetic while leaving the 24-bit T line
addresses alone, so the relocations are unchanged. Proposed upstream
in the bug thread. With it SFR_DATA_U32 can be used again, which
saves about 240 bytes of code in REG_SET.
2026-10-24 21:59:37 +02:00
d00f 0181276ec6 ci: build the in-tree SDCC and keep it between runs
The workflow and the Dockerfile install what building SDCC needs instead
of the distribution's sdcc, and the workflow caches toolchain/ keyed on
the contents of sdcc/.
2026-10-24 21:59:37 +02:00
d00f 905e588071 build: build SDCC 4.6.0 with two mcs51 fixes in the tree
Released SDCC 4.5.0 and 4.6.0 miscompile some of our code in the mcs51
register allocator (SDCC bugs 4153 and 4154, fixed in trunk r17025 and
r17039). The first make now downloads the 4.6.0 sources, checks their
sha256, applies the two fixes from sdcc/ and installs the compiler into
toolchain/, named after a checksum of sdcc/, so a changed patch builds a
new one. make CC=sdcc still uses an installed compiler.
2026-10-24 21:58:46 +02:00
feelfree69 bb16302a80 Merge pull request #622 from bloqaudio/fix/port-counter-lines
web: give each packet counter of a port its own line
2026-10-24 19:56:44 +02:00
feelfree69 50fda40fd9 Merge pull request #584 from bloqaudio/fix/mirror-status
mirror: show the configured monitor port in "mirror status"
2026-10-24 19:54:44 +02:00
feelfree69 9c65039bab Merge pull request #623 from bloqaudio/fix/save-button-dirty
web: highlight Save to flash only when there are unsaved changes
2026-10-24 19:39:55 +02:00
feelfree69 7e0bc93b55 Merge pull request #503 from bloqaudio/fix/uip-fragment-log-rate-limit
uip: rate-limit the dropped-fragment log
2026-10-24 16:40:33 +02:00
feelfree69 e1e1292ab0 Merge pull request #621 from bloqaudio/fix/num-header-align
web: right-align the headers of number columns
2026-10-24 16:36:11 +02:00
feelfree69 975caaff02 Merge pull request #633 from bloqaudio/fix/save-merge-lldp
web: keep LLDP lines from piling up in the saved configuration
2026-10-24 16:05:59 +02:00
feelfree69 cffe4793b1 Merge pull request #695 from L4ky/add-keeplink-kp90009xhpx
Add KeepLink KP-9000-9XHP-X as brand of POE-2G080110GS
2026-10-24 13:53:01 +02:00
feelfree69 9ed1e89c8b Merge pull request #698 from rwalli/fix/firefox-pw-autofill
Web UI: keep Firefox from autofilling the new admin password
2026-10-24 13:50:26 +02:00
feelfree69 ba2f759df3 Merge pull request #696 from rwalli/fix/uptime-ram-counter
rtl8372_init: set SDS 0 to SGMII as the stock firmware does
2026-10-24 12:44:58 +02:00
robs 884a1ac016 CHANGELOG: SDS 0 init on RTL8372 boards without 10G ports 2026-10-24 08:45:03 +02:00
robs 51ed063dcb rtl8372_init: set SDS 0 to SGMII as the stock firmware does
The masks were 0, so 0x3e2 was ORed into the reset value 0xbff and both
SerDes stayed off on boards without 10G ports (n_10g == 0), e.g. the
HC-SWTG124AS. With SDS 0 off RTL837X_REG_SEC_COUNTER does not hold a
written value: it reads 0, so NTP never retried after a failed first
attempt at boot and froze the clock after a sync.

Tested with regset on an HC-SWTG124AS: the register keeps a write with
SDS 0 in SGMII or 10GR, not with SDS 0 off (also not with only SDS 1
on).
2026-10-24 08:30:27 +02:00
robs 0da6318777 Web UI: keep Firefox from autofilling the new admin password
Firefox filled a saved login into the System tab's password fields and
its user name into the NTP poll interval, the input before them. That
input event marked the Time card as edited, so ntpLoad() left NTP off,
the server empty and the time zone at UTC-12:00.
2026-10-24 01:20:45 +02:00
Luca S. d13a5d125d Add KeepLink KP-9000-9XHP-X as brand of POE-2G080110GS 2026-10-24 00:21:28 +02:00
feelfree69 889c94404c Merge pull request #656 from andrebrait/fix/upload-any-part-type
Accept firmware uploads independent of file MIME type
2026-10-23 08:15:07 +02:00
feelfree69 0f98734ae2 Merge branch 'main' into fix/upload-any-part-type 2026-10-23 08:09:07 +02:00
feelfree69 2d0abbbca0 Merge pull request #691 from hlyi/clean-min-max-port
clean up min_port and max_port from struct after PR#684
2026-10-23 08:08:14 +02:00
feelfree69 fdcdfa9b45 Merge pull request #659 from andrebrait/fix/health-lldp-phase
health: give the LLDP phase a valid slot in the phase arrays
2026-10-23 08:07:31 +02:00
HL Yi c043c671d7 clean up min_port and max_port from struct after PR#684 2026-10-22 18:34:44 -05:00
Andre Brait 7d7585fa6d fix(health): give the LLDP phase a valid slot in the phase arrays
In HEALTH=1 builds health_phase(HEALTH_LLDP) used id 7 while ph_max[] and
ph_slow[] had HEALTH_PHASES = 6 entries. The LLDP checkpoint therefore
wrote one byte past ph_max (into the high byte of ph_slow[0]) and, when
the phase took two ticks or more, incremented two bytes past ph_slow
(dhcp_state.state and transaction_id on KP_9000_6XHML_X2).

Rename it HEALTH_PH_LLDP = 6, derive HEALTH_PHASES from it so the arrays
always cover the last phase, print an lldp line in "health" and document
it.

Fixes #658
2026-10-22 11:32:05 +02:00
Andre Brait 13801241ca fix: accept firmware uploads independent of part MIME type
Chrome on macOS labels .bin files application/macbinary. Searching for application/octet-stream mistakes the image bytes for more part headers and aborts when the preamble buffer fills.

Find the multipart header terminator instead, after the HTTP separator retained by handle_post. Keep authentication, CRC checks and staging limits unchanged. Cover MacBinary, an omitted part Content-Type, and a split header terminator with exact staged-byte checks.
2026-10-22 11:26:31 +02:00
Kamikaze94 7b0a07efaa Merge branch 'logicog:main' into bugfix/sidebar-scrolling 2026-10-22 01:37:32 +02:00
Kamikaze94 3c00ab0ae9 WebUI: Cleanup color settings 2026-10-22 00:40:46 +02:00
dobodu 3deee14f94 French translation update
Add LLDP
Add storm control
Add NTP and syslog 
translations
2026-10-21 10:49:29 +02:00
Kamikaze94 6d539bfb1b WebUI: Change port strip sfp icon 2026-10-20 22:46:15 +02:00
avicarmeli 827d3b389c Fix Makefile test list continuation after merge 2026-10-20 16:55:24 +03:00
avicarmeli ef224ec8f4 Merge branch 'main' into fix/cpu-vlan-init 2026-10-20 16:44:04 +03:00
Kamikaze94 2a9f9b2508 WebUI: Change port strip rj45 icon, latch added 2026-10-19 23:58:57 +02:00
Kamikaze94 723d16b4cb WebUI: Fix port strip icon for sfp ports 2026-10-19 18:32:37 +02:00
Kamikaze94 a1998c70d7 WebUI: Lock sidebar layout and move scrolling to main element 2026-10-19 00:51:19 +02:00
bloqaudio 196d22d7c3 web: keep LLDP lines from piling up in the saved configuration
Save to flash merges the running log into the startup configuration.
Neither its toggle nor its overwrite rules knew "lldp on|off", so every
switch of LLDP added another line: four switches left four lines.
"port <n> lldp permit|block" fell under the rule for "port <n> <speed>",
so setting a port's speed dropped its LLDP setting from the saved
configuration, and the other way round.

"lldp on|off" is now a toggle, and a per-port LLDP setting replaces only
an earlier per-port LLDP setting of that port.
2026-10-16 10:43:08 -05:00
bloqaudio 680a40dab5 web: highlight Save to flash only when there are unsaved changes
The button in the header was always drawn as the primary action, on
every page, so it competed with each page's own buttons, for example
the upload button on the Firmware page. It is now a plain button and
turns primary together with the "unsaved changes" badge.
2026-10-15 00:12:14 -05:00
bloqaudio 97b15f4022 web: give each packet counter of a port its own line
The port tooltip and the port details showed "TX good / bad: 8794 / 0
pkts", which reads as 8794 bad packets. Each counter now has its own
line, labelled as on the Statistics page, and the two strings only that
line used are removed from every language.
2026-10-15 00:11:04 -05:00
logicog c263a8ab64 Add assmebler injection script 2026-10-15 06:53:23 +02:00
bloqaudio d5ccd38dd3 web: right-align the headers of number columns
"table.t th" sets text-align:left and is more specific than ".num", so
the headers of the counter columns on the dashboard and the statistics
page stayed left-aligned above right-aligned values.
2026-10-14 23:48:46 -05:00
avicarmeli 6bae5c5c90 test: run CPU VLAN variants in the standard host suite 2026-10-12 20:16:21 +03:00
bloqaudio c2980e42d8 mirror: show the configured monitor port in "mirror status"
"mirror status" printed the local mirroring_port, which is still 0 at
that point, so it named the same port whatever was configured. Print
the port from RTL837x_MIRROR_CTRL instead, which the function has
already read to tell whether mirroring is enabled. send_mirror() decodes
the register the same way for the web interface.
2026-10-12 12:02:02 -05:00
avicarmeli 875933b9f9 fix: initialize CPU MAC9 VLAN state independently of user port count 2026-10-12 10:43:42 +03:00
logicog c02f660c08 doc: Add HOWTO for using firmware tracing to figure LED configuration
This adds a howto for figuring out LED configuration via tracing
of functions in the originam firmware image. The method also provides
information on PHY and SDS configuration, including PHY patches.
2026-10-11 22:31:33 +02:00
bloqaudio d0374541c5 uip: rate-limit the dropped-fragment log
uip has no reassembly, so every fragment it receives is discarded and
logged. A neighbour that broadcasts fragmented traffic therefore produces a
continuous stream of "ip: fragment dropped." rather than an occasional one,
at roughly thirteen lines a second on a quiet segment, which buries every
other console message.

The message carries real information: a sudden burst of it says something
about the network. What carries none is repeating it per fragment. Report
the count once per five second window instead, which keeps the signal and
ends the flood.

The count is printed through the no_syslog primitives, as the original
message was, so the flood cannot move from the console into the log ring.
itoa() writes through write_char(), which also feeds the ring, so it gets
a no_syslog twin beside it rather than a private copy of its digits.
2026-09-25 08:20:27 -05:00
81 changed files with 6747 additions and 1040 deletions

No files matched your search

+1
View File
@@ -3,6 +3,7 @@
.gitattributes
.github/
output/
toolchain/
installer/output/
*.bin
html_data.c
+10 -2
View File
@@ -29,7 +29,12 @@ jobs:
- name: Install dependencies
run: |
apt update
apt install make gcc sdcc xxd python-is-python3 libjson-c-dev zlib1g-dev -y
apt install make gcc g++ bison flex libboost-dev patch bzip2 curl ca-certificates xxd python-is-python3 libjson-c-dev zlib1g-dev -y
- name: Cache the patched SDCC
uses: actions/cache@v4
with:
path: branch/toolchain
key: sdcc-${{ hashFiles('branch/sdcc/*') }}
- name: Run the host-side unit tests
run: cd branch && make -C test
- name: Check that every machine definition can be compiled
@@ -41,7 +46,10 @@ jobs:
- name: Make project with the health instrumentation
run: cd branch && make clean && make CI=1 MACHINE="KP_9000_6XHML_X2" HEALTH=1
- name: Make main
run: cd main && make CI=1 MACHINE="KP_9000_6XHML_X2" && cp output/KP_9000_6XHML_X2/rtlplayground.map /tmp/main.map
run: |
ln -s ../branch/toolchain main/toolchain
PATH="$(echo "$PWD"/branch/toolchain/sdcc-*/bin):$PATH"
cd main && make CI=1 MACHINE="KP_9000_6XHML_X2" && cp output/KP_9000_6XHML_X2/rtlplayground.map /tmp/main.map
- name: Calculate bank growth
run: |
sed -rn 's/^([XCD]SEG|(BANK[0-9])).*=\s+([0-9]+)\. bytes.*$/MAIN_\1=\3/p' /tmp/main.map | sort -u >> "$GITHUB_ENV"
+1
View File
@@ -2,6 +2,7 @@
.idea/
output/
output-main/
toolchain/
html_data.c
html_data.h
version.h
+22
View File
@@ -25,12 +25,34 @@
## Fixed
- Time
- `rtl8372_init()` sets SDS 0 to SGMII as the stock firmware does. The masks were 0, so on RTL8372
boards without 10G ports both SerDes stayed off, and with SDS 0 off `RTL837X_REG_SEC_COUNTER`
does not hold a written value: NTP never retried after a failed first attempt at boot and
froze the clock after a sync.
- Web UI
- Firefox no longer fills a saved login into the new admin password fields. It also put the
user name into the NTP poll interval, which marked the Time card as edited, so it kept its
defaults (NTP off, no server, UTC-12:00) instead of the switch's settings.
- Accept firmware uploads regardless of the file part's MIME type, including
`application/macbinary` sent by Chrome on macOS for `.bin` files.
- Health
- `HEALTH=1` builds no longer write past the phase timing arrays at the LLDP checkpoint, which
corrupted the `link` slow counter and the DHCP client state. #658
- Config
- A startup configuration line longer than the command buffer no longer stops the replay:
the offending line is skipped and the following lines are still applied.
- The web UI and the configuration upload now refuse a line the replay cannot take,
instead of writing it to flash.
- Startup
- Initialised and zero-initialised globals above xdata 0x4000 now get their values at boot.
Hardware reset leaves SFR 0x97 at 1, which maps xdata 0x4000-0xbfff to the flash image,
and it was cleared only in `main()`, after the C startup code had copied and cleared them.
This showed as a random web session timeout and LLDP blocked on some ports without config.
## Breaking changes
- Config
+8 -1
View File
@@ -3,7 +3,14 @@ FROM debian:13-slim
RUN apt-get update && apt-get install -y \
make \
gcc \
sdcc \
g++ \
bison \
flex \
libboost-dev \
patch \
bzip2 \
curl \
ca-certificates \
xxd \
python3 \
libjson-c-dev \
+76 -23
View File
@@ -4,8 +4,25 @@ DEFAULT_CONFIG_LOCATION = 454656
CONFIG_LOCATION = 458752
HTML_LOCATION = 262144
# Startup configuration embedded in the image. Override it to build a specific
# machine with its own configuration: make MACHINE=<name> CONFIG=<file>
CONFIG ?= config.txt
SDCC_TOOLCHAIN = sdcc-$(shell cat sdcc/* | cksum | cut -d' ' -f1)
SDCC_PREFIX = toolchain/$(SDCC_TOOLCHAIN)
ifeq ($(origin CC),default)
CC = sdcc
SDCC = $(SDCC_PREFIX)/bin/sdcc
CC = $(SDCC)
ASM = $(SDCC_PREFIX)/bin/sdas8051
SDCC_REV := $(shell git log -1 --format=%h -- sdcc 2>/dev/null)
ifneq ($(SDCC_REV),)
ifneq ($(shell git status --porcelain --untracked-files=no -- sdcc 2>/dev/null),)
SDCC_REV := $(SDCC_REV)-dirty
endif
endif
SDCC_BANNER = $(if $(SDCC_REV),sdcc/ at $(SDCC_REV),$(SDCC_TOOLCHAIN)) ($(words $(wildcard sdcc/*.patch)) patches)
else
SDCC_BANNER = system
endif
CC_FLAGS = -mmcs51 -I. -Imachine -Ihttpd -Iuip
ASM ?= sdas8051
@@ -43,6 +60,7 @@ ifndef BUILDDIR
BUILDDIR = output/$(MACHINE)
endif
CONFIG_STAMP := $(BUILDDIR)/.config
VERSION_HEADER := version.h
GIT_VERSION := $(shell git rev-parse --short HEAD)
@@ -65,14 +83,15 @@ BUILD_DATE := $(shell date -u -d @$(SOURCE_DATE_EPOCH) +"%Y-%m-%d %H:%M:%S" 2>/d
|| date -u -r $(SOURCE_DATE_EPOCH) +"%Y-%m-%d %H:%M:%S")
endif
# $(MACHINE_SRC) is listed here so an unknown $MACHINE fails before anything else
# is built, including the tools/ subdirectory.
all: create_build_dir $(VERSION_HEADER) $(MACHINE_SRC) $(SUBDIRS) $(BUILDDIR)/rtlplayground-$(FILENAME_EXTENSION).bin
# $(MACHINE_SRC) and $(CONFIG) are listed here so an unknown $MACHINE or a
# missing config file fails before anything else is built, including the tools/
# subdirectory.
all: create_build_dir $(VERSION_HEADER) $(MACHINE_SRC) $(CONFIG) $(SUBDIRS) $(BUILDDIR)/rtlplayground-$(FILENAME_EXTENSION).bin
bank_size:
tools/bank_occupancy.sh "$(MACHINE)"
create_build_dir:
create_build_dir: $(SDCC)
mkdir -p "$(BUILDDIR)"
mkdir -p "$(BUILDDIR)/uip"
mkdir -p "$(BUILDDIR)/httpd"
@@ -88,6 +107,12 @@ $(MACHINE_SRC):
@echo " Example: make MACHINE=KP_9000_6XHML_X2" >&2
@false
# Fail with a clear message when CONFIG names a file that does not exist.
$(CONFIG):
@echo "ERROR: CONFIG='$(CONFIG)' does not exist." >&2
@echo " Point CONFIG at a configuration file, or omit it to use config.txt." >&2
@false
# Keep the per-machine definition in first position to fail immediately on an
# invalid $MACHINE value
SRCS = \
@@ -102,6 +127,7 @@ SRCS = \
sfp.c \
dns.c \
dns_cli.c \
sessions.c \
ntp.c \
ntp_cli.c \
syslog.c \
@@ -124,10 +150,12 @@ SRCS += \
rtl837x_lldp.c \
rtl837x_storm.c \
rtl837x_lacp.c \
rtl837x_trap.c
rtl837x_trap.c \
rtl837x_stp_cli.c
SRCS += \
httpd/httpd.c \
httpd/page_impl.c
httpd/page_impl.c \
httpd/page_stp.c
SRCS += \
uip/timer.c \
uip/uip.c \
@@ -145,19 +173,30 @@ HTML := $(shell find html -name '*.js' -or -name '*.html' -or -name '*.svg' -or
# The raw html/ sources stay untouched for development; the minified
# copy is a build artifact under output/.
HTML_MIN := $(BUILDDIR)/html_min
HTML_MIN_LIST := $(BUILDDIR)/html_min.list
HTML_MIN_STAMP := $(BUILDDIR)/html_min.stamp
.PHONY: html_min
html_min: $(HTML)
html_min: $(HTML_MIN_STAMP)
$(HTML_MIN_LIST): FORCE
@mkdir -p $(BUILDDIR)
@echo $(sort $(HTML)) > $@.tmp
@cmp -s $@.tmp $@ && rm -f $@.tmp || mv $@.tmp $@
$(HTML_MIN_STAMP): $(HTML) $(HTML_MIN_LIST) tools/minify.py $(if $(wildcard $(HTML_MIN)),,FORCE)
rm -rf $(HTML_MIN)
mkdir -p $(HTML_MIN)
@for f in $(HTML); do python3 tools/minify.py $$f $(HTML_MIN)/$$(basename $$f) || exit 1; done
@touch $@
html_data.c html_data.h &: $(HTML) | tools html_min
html_data.c html_data.h &: $(HTML_MIN_STAMP) | tools
tools/$(BUILDDIR)/fileadder -a $(HTML_LOCATION) -s $(IMAGESIZE) -b BANK1 -z -d $(HTML_MIN) -p html_data
$(VERSION_HEADER): FORCE
@printf '%s\n' "#ifndef VERSION_H" "#define VERSION_H" \
"#define VERSION_SW \"$(VERSION_EXTENSION)\"" \
"#define BUILD_DATE \"$(BUILD_DATE)\"" \
"#define SDCC_TOOLCHAIN \"$(SDCC_BANNER)\"" \
"#endif" > $(VERSION_HEADER).tmp
@cmp -s $(VERSION_HEADER).tmp $(VERSION_HEADER) \
&& rm -f $(VERSION_HEADER).tmp \
@@ -169,12 +208,18 @@ $(SUBDIRS):
$(MAKE) -C $@ BUILDDIR=$(BUILDDIR)
clean: $(SUBDIRSCLEAN)
-rm -f html_data.c html_data.h $(VERSION_HEADER)
-rm -f html_data.c html_data.h $(VERSION_HEADER) $(HTML_MIN_LIST) $(HTML_MIN_STAMP)
-if [ -d $(BUILDDIR) ]; then find $(BUILDDIR) -type f ! -name "*.bin" -delete; fi
distclean: $(SUBDIRSCLEAN)
-rm -f html_data.c html_data.h $(VERSION_HEADER)
-rm -f html_data.c html_data.h $(VERSION_HEADER) $(HTML_MIN_LIST) $(HTML_MIN_STAMP)
-rm -rf $(BUILDDIR)
-rm -rf toolchain
ifneq ($(SDCC),)
$(SDCC):
sh sdcc/build.sh $(abspath $(SDCC_PREFIX))
endif
$(SUBDIRSCLEAN):
$(MAKE) -C $(@:clean=) clean
@@ -189,6 +234,12 @@ FORCE:
$(CCFLAGS_STAMP): FORCE | create_build_dir
@echo '$(CC_FLAGS)' | cmp -s - $@ 2>/dev/null || echo '$(CC_FLAGS)' > $@
# The same for $(CONFIG): its path is stamped so switching to a config file
# older than the image still rebuilds it, while $(CONFIG) itself as a normal
# prerequisite covers edits to the selected file.
$(CONFIG_STAMP): FORCE | create_build_dir
@echo '$(CONFIG)' | cmp -s - $@ 2>/dev/null || echo '$(CONFIG)' > $@
$(BUILDDIR)/%.rel: %.c $(CCFLAGS_STAMP) | create_build_dir html_data.h
$(CC) -MMD $(CC_FLAGS) -o $@ -c $<
@@ -197,16 +248,16 @@ $(BUILDDIR)/%.rel: %.asm $(CCFLAGS_STAMP) | create_build_dir
# mv -f $(addprefix $(basename $^), .lst .rel .sym) .
$(BUILDDIR)/rtlplayground.ihx: $(OBJS) $(BUILDDIR)/crtbank.rel $(BUILDDIR)/crc16.rel
$(CC) $(CC_FLAGS) --xram-size 49151 -Wl-bHOME=0x00000 -Wl-bBANK1=0x14000 -Wl-bBANK2=0x24000 -Wl-bBANK3=0x34000 -Wl-r -o $@ $^
$(CC) $(CC_FLAGS) --xram-size 49151 -Wl-bHOME=0x00000 -Wl-bBANK1=0x14000 -Wl-bBANK2=0x24000 -Wl-bBANK3=0x34000 -Wl-bBANK4=0x44000 -Wl-r -o $@ $^
$(BUILDDIR)/rtlplayground.img: $(BUILDDIR)/rtlplayground.ihx
objcopy --input-target=ihex -O binary $< $@
$(BUILDDIR)/rtlplayground-$(FILENAME_EXTENSION).bin: $(BUILDDIR)/rtlplayground.img | tools
$(BUILDDIR)/rtlplayground-$(FILENAME_EXTENSION).bin: $(BUILDDIR)/rtlplayground.img $(CONFIG) $(CONFIG_STAMP) | tools
if [ -e $@ ]; then rm $@; fi
tools/$(BUILDDIR)/imagebuilder -i $^ $@
tools/$(BUILDDIR)/fileadder -a $(DEFAULT_CONFIG_LOCATION) -s $(IMAGESIZE) -d config.txt $@
tools/$(BUILDDIR)/fileadder -a $(CONFIG_LOCATION) -s $(IMAGESIZE) -d config.txt $@
tools/$(BUILDDIR)/imagebuilder -i $(BUILDDIR)/rtlplayground.img $@
tools/$(BUILDDIR)/fileadder -a $(DEFAULT_CONFIG_LOCATION) -s $(IMAGESIZE) -d "$(CONFIG)" $@
tools/$(BUILDDIR)/fileadder -a $(CONFIG_LOCATION) -s $(IMAGESIZE) -d "$(CONFIG)" $@
tools/$(BUILDDIR)/fileadder -a $(HTML_LOCATION) -s $(IMAGESIZE) -z -d $(HTML_MIN) -b BANK1 $@
tools/$(BUILDDIR)/crc_calculator -u $@
ln -sf $(MACHINE)/rtlplayground-$(FILENAME_EXTENSION).bin $(BUILDDIR)/../rtlplayground.bin
@@ -214,16 +265,18 @@ $(BUILDDIR)/rtlplayground-$(FILENAME_EXTENSION).bin: $(BUILDDIR)/rtlplayground.i
.PHONY: clean distclean all $(SUBDIRS) $(SUBDIRSCLEAN) create_build_dir
# Every machine/ definition is compiled, so a machine added to machine/ is
# covered without registering it anywhere. The _init.c files are excluded here:
# they are the boot hooks, compiled separately below (the board's own if it
# exists, otherwise default_machine_init.c). The machine-specific defines are
# filtered out of $(CC_FLAGS) and supplied per iteration instead, so this also
# works as "make machine_check MACHINE=...".
MACHINE_DEFS := $(sort $(patsubst machine/%.c,%,$(filter-out machine/%_init.c,$(wildcard machine/*.c))))
# covered without registering it anywhere. A board only counts once both its
# <machine>.c and its <machine>.h are present: the header is what the rest of
# the build includes, so a .c without one is an incomplete board. The _init.c
# files are excluded here: they are the boot hooks, compiled separately below
# (the board's own if it exists, otherwise default_machine_init.c). The
# machine-specific defines are filtered out of $(CC_FLAGS) and supplied per
# iteration instead, so this also works as "make machine_check MACHINE=...".
MACHINE_DEFS := $(sort $(filter $(patsubst machine/%.h,%,$(wildcard machine/*.h)),$(patsubst machine/%.c,%,$(filter-out machine/%_init.c,$(wildcard machine/*.c)))))
MACHINE_LESS_CC_FLAGS = $(filter-out -DMACHINE_%,$(CC_FLAGS))
.PHONY: machine_check
machine_check:
machine_check: $(SDCC)
@mkdir -p $(BUILDDIR)/tmp
@set -eo pipefail; \
for MACHINE in $(MACHINE_DEFS); \
+28 -6
View File
@@ -56,12 +56,19 @@ device simulator is provided, which runs entirely under Linux as a local webserv
## (0) Compiling Requirements
Install the following particular build requisites (Debian 12/13), note that Ubuntu 24.04
still has an older version of sdcc, but you will need sdcc version 4.5 for the code to compile:
Install the following particular build requisites (Debian 12/13):
```
sudo apt install make gcc sdcc xxd python-is-python3 libjson-c-dev zlib1g-dev
sudo apt install make gcc g++ bison flex libboost-dev patch bzip2 curl xxd python-is-python3 libjson-c-dev zlib1g-dev
```
The first `make` downloads SDCC 4.6.0, applies the fixes in `sdcc/` and builds it into
`toolchain/`, which takes a few minutes. Later builds reuse it. The released SDCC 4.5.0
and 4.6.0 miscompile parts of this code, see [SDCC bug #4153](https://sourceforge.net/p/sdcc/bugs/4153/)
and [#4154](https://sourceforge.net/p/sdcc/bugs/4154/); both fixes are in SDCC trunk since
r17039. The third patch is for [#4070](https://sourceforge.net/p/sdcc/bugs/4070/), where the
assembler drops the top byte of a `__sfr32` address list; it is proposed upstream and lets
`SFR_DATA_U32` be used again. To use an SDCC you installed yourself instead, run `make CC=sdcc ...`.
<details>
<summary>If using Docker (click to expand)</summary>
@@ -90,6 +97,14 @@ docker run --rm -v $(pwd):/workspace rtlplayground-dev make MACHINE=DEFAULT_8C_1
The resulting `.bin` file appears in `output/` on your host.
Build the OEM web upgrade bin (after building the firmware bin)
```
docker run --rm -v $(pwd):/workspace rtlplayground-dev make -C installer
```
The resulting `rtlplayground_oem_upgrade.bin` file appears in `installer/output/` on your host.
Build host tools only:
```
@@ -120,6 +135,13 @@ The name selects the board definition `machine/<name>.c`; `make` without a
`MACHINE` value stops with an error listing what is expected. If your board is
not in the list yet, see [Adding a new device](doc/adding-a-device.md).
To embed a machine-specific startup configuration, pass `CONFIG`:
```
make MACHINE=DEFAULT_8C_1SFP CONFIG=configs/DEFAULT_8C_1SFP.txt
```
`CONFIG` defaults to `config.txt`; the selected file is embedded in the image
and changing it triggers a rebuild.
> [!TIP]
> You can write configuration parameters in config.txt (see below) in order your switch to get
> straight at the first boot, a correct IP configuration.
@@ -163,9 +185,9 @@ example of compilation console output
RTLPlayground/installer$ make
mkdir -p output
gcc updatebuilder.c -o output/updatebuilder
sdas8051 -plosgff -o output/crtstart.rel crtstart.asm
sdcc -mmcs51 --code-loc 0x1000 -o output/installer.rel -c installer.c
sdcc -mmcs51 -Wl-bHOME=0x1100 -Wl-r -o output/rtlinstaller.ihx output/crtstart.rel output/installer.rel
../toolchain/sdcc-3390765201/bin/sdas8051 -plosgff -o output/crtstart.rel crtstart.asm
../toolchain/sdcc-3390765201/bin/sdcc -mmcs51 --code-loc 0x1000 -o output/installer.rel -c installer.c
../toolchain/sdcc-3390765201/bin/sdcc -mmcs51 -Wl-bHOME=0x1100 -Wl-r -o output/rtlinstaller.ihx output/crtstart.rel output/installer.rel
./output/updatebuilder -i output/rtlinstaller.ihx -o output/rtlplayground_oem_upgrade.bin ../output/rtlplayground.bin
Input file size: 524288
Bytes read: 524288
+10 -3
View File
@@ -24,6 +24,7 @@
#include "sfp.h"
#include "dhcp.h"
#include "syslog.h"
#include "sessions.h"
#include "dns.h"
#include "ntp.h"
#include "uip/uip.h"
@@ -849,7 +850,7 @@ void parse_mirror(void)
print_string("NOT Enabled: ");
}
print_string("Mirroring port: ");
print_phys_port(mirroring_port);
print_phys_port(mPort >> 1);
reg_read_m(RTL837x_MIRROR_CONF);
uint16_t m = sfr_data[0];
m = (m << 8) | sfr_data[1];
@@ -1479,6 +1480,9 @@ void parse_passwd(void)
passwd[j++] = c;
} while (c != NUL && j < 20);
passwd[j] = NUL;
/* Logins made with the old password end here. With one session the next
* login pushed a leftover one out; with several it would stay valid. */
session_init();
return;
}
cmd_error("Missing password\n");
@@ -1836,6 +1840,8 @@ void print_gpio_status(void) {
void print_sw_version(void) __banked {
print_string("Software version: " VERSION_SW);
print_string("\nBuild date: " BUILD_DATE);
print_string("\nCompiler: SDCC " STR(__SDCC_VERSION_MAJOR) "." STR(__SDCC_VERSION_MINOR) "." STR(__SDCC_VERSION_PATCH) " #" STR(__SDCC_REVISION));
print_string("\nToolchain: " SDCC_TOOLCHAIN);
print_string("\nHardware: ");
print_string(machine.machine_name);
write_char('\n');
@@ -2011,10 +2017,10 @@ void cmd_parser(void) __banked
cmd_error("lacp (show | off); a group starts LACP with: lag <1-4> lacp <port>...\n");
} else if (cmd_compare(0, "pvid")) {
if (cmd_words_len == 3 && cmd_parse_port_separator(cmd_words_b[1]) != 0
&& atoi_short(cmd_words_b[2]) && atoi_results_short && atoi_results_short <= 4094)
&& atoi_short(cmd_words_b[2]) && atoi_results_short <= 4094)
port_pvid_set(atoi_results_u8, atoi_results_short);
else {
cmd_error("pvid <port> <1-4094>\n");
cmd_error("pvid <port> <0-4094>\n");
}
} else if (cmd_compare(0, "vlan")) {
parse_vlan();
@@ -2329,6 +2335,7 @@ void health_show(void) __banked
ph_line("tx ", HEALTH_PH_TX);
ph_line("stp ", HEALTH_PH_STP);
ph_line("cmd ", HEALTH_PH_CMD);
ph_line("lldp", HEALTH_PH_LLDP);
/* Stack: current depth plus how much of the painted area is untouched */
print_string("sp ");
+2 -2
View File
@@ -19,8 +19,8 @@ extern __xdata uint8_t err_status;
#define HEALTH_PH_TX 3
#define HEALTH_PH_STP 4
#define HEALTH_PH_CMD 5
#define HEALTH_PHASES 6
#define HEALTH_LLDP 7
#define HEALTH_PH_LLDP 6
#define HEALTH_PHASES (HEALTH_PH_LLDP + 1) /* last id + 1: sizes the arrays in cmd_parser.c */
extern __xdata uint16_t health_rx_frames;
#define health_rx_frame() health_rx_frames++
+7 -7
View File
@@ -38,8 +38,8 @@ existing files as the template:
```
`MACHINE_CHIPSET` is `RTL8373` or `RTL8372`; a build without it will fail. It selects the default
logical port range: `min_port` is `0` for RTL8373 and `3` for RTL8372, while `max_port` is `8`,
the highest logical port index. A board with a different range defines
logical port range: `MACHINE_MIN_PORT` is `0` for RTL8373 and `3` for RTL8372, while
`MACHINE_MAX_PORT` is `8`, the highest logical port index. A board with a different range defines
`MACHINE_MIN_PORT` and/or `MACHINE_MAX_PORT` in its header to override the
defaults.
@@ -94,16 +94,16 @@ __code const struct machine machine = {
};
```
`MACHINE_COMMON_SETTING` (from `machine_defs.h`) initializes `machine_name`,
`isRTL8373`, `min_port` and `max_port` from the header's `MACHINE_MODEL` and
`MACHINE_CHIPSET`. Keep it as the first initializer and list the
board-specific fields after it.
`MACHINE_COMMON_SETTING` (from `machine_defs.h`) initializes `machine_name` and
`isRTL8373` from the header's `MACHINE_MODEL` and `MACHINE_CHIPSET`. The logical
port range comes from `MACHINE_MIN_PORT` and `MACHINE_MAX_PORT` in `machine.h`.
Keep it as the first initializer and list the board-specific fields after it.
### Fields
| Field | Meaning |
|-------|---------|
| `MACHINE_COMMON_SETTING` | expands to `machine_name` (`MACHINE_MODEL`), `isRTL8373` (`1` for `RTL8373`, `0` for `RTL8372`), `min_port` and `max_port`; all are derived from the header's `MACHINE_CHIPSET` (see Step 1) |
| `MACHINE_COMMON_SETTING` | expands to `machine_name` (`MACHINE_MODEL`) and `isRTL8373` (`1` for `RTL8373`, `0` for `RTL8372`); both are derived from the header's `MACHINE_CHIPSET` (see Step 1) |
| `n_sfp` | number of SFP+ cages |
| `n_10g` | SerDes 10G capability used by the RTL837xN init: `0` none, `1` one, `2` two 10G-capable SerDes |
| `log_to_phys_port[9]` | logical port (0-based index) → physical front-panel port number |
+2
View File
@@ -1,11 +1,13 @@
# MokerLink POE-2G080110GS
The `POE-2G080110GS` is an unmanaged 8x2.5G RJ45 and 1xSFP+ PoE switch sold by Mokerlink. There is also a `POE-2G080110GSM` managed version of the switch which may be similar but this has not been verified on actual hardware.
The`KP-9000-9XHP-X` is the KeepLink rebrand for same product.
## Brands
| Brand | Type |Managed| PCB | Flash | Chip RTL |
|--------|------------------|-------|------------------|-----------------|---------------|
| Mokerlink | POE-2G080110GS | No | 2M-PCB51-ML-V1.1 | 4MB (W25Q32JV) | 8373N + 8224N |
| KeepLink | KP-9000-9XHP-X | No | 2M-PCB51-ML-V1.1 | 4MB (W25Q32JV) | 8373N + 8224N |
## Hardware overview
Front
+93
View File
@@ -0,0 +1,93 @@
# PCB-AFGW SWTG218ANS V2.0.2
RTL8373-based 8×2.5G PoE + 1×10G SFP+ web-managed switch, sold as the
**RealHD PSW8-25MG**.
### Label specifications
- **Manufacturer**: RealHD
- **Model**: PSW8-25MG
- **PCB marking**: `PCB-AFGW SWTG218ANS_V2.0.2` (silkscreened as `PCB-AFGW SWTG218ANS_V2.0.2_23308`)
- **Ports**:
- 8 × RJ45: 10/100/1000/2500 Mbps with PoE
- 1 × SFP+: 1G / 2.5G / 10G (front panel marking `9(10G)`)
- **Power input**: internal AC PSU, `100-240V~ 50/60Hz 2A MAX`, DC output `53.5V 2.8A 150W`
- **Front panel LEDs**: `PWR`, `SYS`, per RJ45 port `Link/Act` and `PoE`, per SFP port 2.5G (green) and 10G (blue)
- **Stock firmware defaults**: web-managed PoE switch
### What works
- All eight 2.5GBASE-T RJ45 ports at 10/100/1000/2500 Mbps (PoE is not configurable via RTLPlayground)
- SFP+ port supporting 1G, 2.5G and 10G modules
- LEDs: green (2.5G) and amber (1G/100M/10M) per RJ45 port with activity, green (1G/2.5G) and blue (10G) on the SFP port, plus the `SYS` LED
### Photos
Front panel:
<img src="photos/PCB-AFGW_SWTG218ANS_V2.0.2/device_front.jpg" width="800" />
Board mounted in the case, with the internal PSU above it:
<img src="photos/PCB-AFGW_SWTG218ANS_V2.0.2/dev_top_w_pcb.jpg" width="800" />
### PCB overview
Top side:
<img src="photos/PCB-AFGW_SWTG218ANS_V2.0.2/pcb_front.jpg" width="800" />
Bottom side:
<img src="photos/PCB-AFGW_SWTG218ANS_V2.0.2/pcb_back.jpg" width="800" />
A 4-pad UART header with `RXD` / `TXD` silkscreened sits next to the right heatsink.
### Port layout
| Logical port | Physical port | Type |
|--------------|---------------|--------|
| 1-8 | 1-8 | RJ45 |
| 9 | 9 | SFP+ |
### LED configuration
RJ45 ports use LED SET0, the SFP+ port uses LED SET1. The board needs the custom
LED mux (`led_mux_custom = 1`), and `RTL837X_REG_LED_GLB_IO_EN` is written to
`0x7f24977f` in `machine_custom_init()` so the LED pins are enabled as wired here.
| SET | LED pin 0 | LED pin 1 |
|------|-----------------------------------------------|-------------------------------------------------------|
| SET0 | Green — lights on 2.5G link with activity | Amber — lights on 1G / 100M / 10M link with activity |
| SET1 | All speeds — lights on any link with activity | 10G — lights on 10G link with activity |
On the front panel this gives one bi-color `Link/Act` LED per RJ45 port (green at
2.5G, amber at 10/100/1000M, blinking with activity) and one `PoE` LED per RJ45
port, which is driven by the PoE hardware and not by the switch. The SFP+ port has
a green 2.5G LED and a blue 10G LED.
### SFP GPIO assignments
| SFP (logical 9) | pin_detect (ModAbs) | pin_los | pin_tx_disable | SerDes | I2C SDA | I2C SCL |
|-----------------|---------------------|---------|----------------|--------|------------------|----------------------|
| SFP | GPIO38 | GPIO_NA | GPIO_NA | SDS1 | GPIO39_I2C_SDA4 | GPIO40_I2C_SCL3_MDC1 |
### Machine configuration
```
make MACHINE=REALHD_PSW8_25MG
```
### Programming
The serial console runs at 8N1 on 115200 baud with the RTLPlayground firmware
(see the UART header note under *PCB overview*). The stock firmware baud rate 57600. The flash chip was 2MB.
### PoE
The 8 RJ45 ports support PoE, powered by the internal 150W AC PSU (DC output
53.5V 2.8A) mounted above the main board.
Note: RTLPlayground does not provide PoE management for this board. The PoE
hardware runs with its own configuration, the same as on the other PoE boards in
this project.
+148
View File
@@ -0,0 +1,148 @@
# Sirivision SR-S25G2109F (S25G1109FP V1.3)
RTL8373 (non-N) with an RTL8224 quad 2.5G PHY: 8 × 2.5G RJ45 + 1 × SFP+,
2 MB flash, no PoE.
## Label specifications
| Item | Value |
|------|-------|
| Manufacturer | Sirivision |
| Model | SR-S25G2109F |
| Ports | 8 × RJ45 10/100/1000/2500 Mbps, 1 × SFP+ 1G/2.5G/10G |
| Power input | 12 V DC, 1.5 A |
| Stock firmware | version 1.0.8.0 |
| Stock defaults | IP 192.168.1.199, user `admin`, password `admin` |
| Serial number | 001008130002003A000005 (also on the PCB barcode sticker) |
The label does not carry a MAC address. After a factory initialisation the
stock firmware uses exactly the values printed on the label.
## PCB silkscreen and naming
Top side: `S25G1109FP V1.3 SIRIVISION`. That model name belongs to an unmanaged
PoE switch; this board omits the PoE section entirely. What is actually shipped
is the label model `SR-S25G2109F`: a simply managed, non-PoE switch. Its stock
firmware provides a web interface, but it responds very slowly.
## CPU, PHY and flash
| Part | Identification |
|------|----------------|
| SoC | RTL8373, non-N: `regget 4` returns `0x83730000` and the boot console prints `Detecting CPU: RTL8373` |
| PHY | RTL8224, reported as `RTL8224 ID: 0x82240000` |
| Flash | GigaDevice 25Q16ESIG (U7), 2 MB, JEDEC id `c8 40 15` |
The two large ICs sit under soldered heatsinks, their markings are not readable.
## Build
```
make MACHINE=S25G1109FP_V1_3 CI=1
```
The resulting image can be uploaded through the web interface (`System` page)
or the stock web UI.
## Port mapping
| Front port | ASIC MAC | Interface |
|------------|----------|-----------|
| 1–8 | 0–7 | RJ45 2.5G |
| 9 | 8 | SFP+ (SDS1) |
## Board signals
| Function | GPIO |
|----------|------|
| SFP detect (ModAbs) | 30, low with a module inserted |
| SFP LOS | 37, high without light |
| SFP TX disable | not connected, `GPIO_NA` |
| SFP I2C | SDA 39 / SCL 40, `I2CBUS(GPIO39_I2C_SDA4, GPIO40_I2C_SCL3_MDC1)` |
| Reset button | 54 |
ModAbs and LOS are separate signals here: GPIO30 tells whether a module is
inserted, GPIO37 whether it receives light, so a module is identified even
before the link comes up and loss of signal is reported. GPIO38 and GPIO48,
which the 8+1 reference boards use for ModAbs and for the reset button, never
change on this board; with GPIO38 as ModAbs the firmware believed a module was
permanently present and retried the I2C read forever.
Serial console: the stock firmware runs the loader at 57600 baud and the runtime
at 9600 baud, RTLPlayground uses 115200.
## Front panel
Each copper port has two LEDs, labelled `Link/Act` (left, yellow) and `2.5G`
(right, green). The SFP cage has a single green `Link/Act` LED; next to it are
a `Power` LED and the `Reset` button.
## LED configuration
Copper ports use LED set 0, the SFP port uses LED set 1:
| Set | Function 0 (left / SFP) | Function 1 (right) |
|-----|-------------------------|--------------------|
| SET0 | all speeds, link with activity | 2.5G link |
| SET1 | all speeds, link with activity | not populated |
The board is wired in the stock port order — LED pin `3n+m` belongs to port
`n+1`, LED function `m` — so the mux table is the chip default with a single
change: the SFP LED sits on pin 25 and is driven by LED function 0, while the
default table maps pin 25 to function 1, which is unpopulated in SET1. Without
that change the SFP LED stays dark.
A speed condition alone leaves an LED dark, `LEDS_LINK` has to be set as well.
## Reset button
| Hold time while running | Effect |
|-------------------------|--------|
| less than 3 s | no action |
| 3–10 s | reset the switch chip, the firmware reboots (~20 s of downtime) |
| more than 10 s | additionally restore the default configuration |
The system LED blinks slowly while the button is held. Holding the button
during power-up instead waits for the release and restores the default
configuration when it is released after 10–30 s.
## MAC address
The factory MAC sits at `0x1FC000`, six bytes followed by `0xFF` padding. The
stock value is a locally administered address (`1A:5D:23:47:4D:EB`), which the
firmware rejects, so the device runs with a UUID-derived MAC (`06:…`). A valid
globally administered address stored there is used instead.
## What works
- all eight 2.5GBASE-T ports at 10/100/1000/2500 Mbps
- SFP+ with 10G and 1G modules, including DDM data where the module provides it
- 10G: Finisar FCBG110SD1C05 AOC (no DDM), rate `0x67`, SerDes mode `1a`
- 1G: ETU ESB5312-3SCD20 (DDM), rate `0x0d`, SerDes mode `04`
- LEDs, module insert/remove detection, loss of signal reporting, reset button
## Unverified
- 2.5G SFP modules, none was available for testing
## Photos
Front panel:
![Front panel](photos/S25G1109FP_V1_3/front.jpg)
Label:
![Label](photos/S25G1109FP_V1_3/label.jpg)
PCB top side:
![PCB top side](photos/S25G1109FP_V1_3/pcb_top.jpg)
PCB bottom side:
![PCB bottom side](photos/S25G1109FP_V1_3/pcb_bottom.jpg)
Flash chip, GigaDevice 25Q16ESIG:
![Flash chip](photos/S25G1109FP_V1_3/flash.jpg)
+3 -1
View File
@@ -5,7 +5,7 @@ It is highly similar to SWTG024AS-V2.0, with the only difference being the GPIO
|Brand |Type |Managed|PCB |Flash |Chip RTL|
|---------|--------------|-------|------------------------------|----------------|--------|
| Horaco | ZX-SG4T2 | No | PCB-SWTG024AS-A-V2.0.1_19650 | 512K (P25D40SH)| 8372N |
| Horaco | HC-SWTG124AS | Yes | PCB-SWTG024AS-A-V2.0.1 | 2M (W25Q16JV)| 8372N |
| Horaco | HC-SWTG124AS | Yes | PCB-SWTG024AS-A-V2.0.1_19650 | 2M (W25Q16JV)| 8372N |
| GoodTop | GT-ST024M | Yes | PCB-SWTG024AS-A-V2.0.1 | 2M | 8372N |
### Label specifications
@@ -35,6 +35,8 @@ Bottom
<img src="photos/SWTG024AS-A-V2_0_1_19650/horaco-zx-sg4t2-pcb-bottom.jpg" width="300" />
On Horaco HC-SWTG124AS with board PCB-SWTG024AS-A-V2.0.1_19650 the reset button is soldered at `SW1` (on the top left, GPIO48).
### J1, serial console
| `J1` pin | Signal |
+48
View File
@@ -0,0 +1,48 @@
### SWTG024AS-V2.1
## Brands
|Brand|Type|Managed|PCB|Flash|Chip RTL|
|---|---|---|---|---|---|
| Xikestor | SKS3200-4E2X | Yes | PCB-SWTG024AS-V2.1.0_19023 | 25Q16A | 8372N |
| Solola | SL902-SWTG024AS | Yes | PCB-SWTG024AS-V2.1.0_19023 | | 8372N |
### Label specifications
- **Ports**:
- 4 × RJ45: 10/100/1000/2500 Mbps
- 2 × SFP+: 1000/2500/10000 Mbps
- **Power**: 1A @ 12V DC
#### Xikestore SKS-3200-4E2X label
<img src="photos/SWTG024AS-V2.1.0/label.jpg" width="300" />
### What works
- ALL 2.5GBASE-T RJ45 ports work at 10/100/1000/2500 Mbps
- The SFP+ ports supports 1G, 2.5G and 10G modules.
- LEDs work with the same indiciations as the OEM firmware.
### PCB overview
**Board markings**
- Top silkscreen: PCB-SWTG024AS-V2.1.0
Top side
<img src="photos/SWTG024AS-V2.1.0/pcb-top.jpg" width="300" />
Bottom
<img src="photos/SWTG024AS-V2.1.0/pcb-bottom.jpg" width="300" />
### Serial console
The four-pin through-hole serial header can be seen to the right of the CPU heatsink in the photo above. It is labelled J1, with pin 1 (Indicated by a triangle on the silkscren) at the bottom. the pinout is also on the silkscreen:
| Pin | Label |
|-----|--------|
| 1 | TX |
| 2 | RX |
| 3 | GND |
| 4 | 3V3 |
Adding a header is a few moments work with a soldering iron. Only pins 1-3 are required for serial port use, and the stock firmware outputs at 57600bps at boot.
Binary file not shown.

After

Width:  |  Height:  |  Size: 4.0 MiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 469 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.9 MiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.9 MiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 88 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 75 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 282 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 253 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 270 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 67 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 398 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 283 KiB

+2 -1
View File
@@ -14,6 +14,7 @@ rx max 00 slow 0x0000
tx max 00 slow 0x0000
stp max 04 slow 0x0001
cmd max 02 slow 0x0001
lldp max 00 slow 0x0000
sp 80 untouched 62
tcp st 00 tmr 00 rtx 00 rto 00 len 0x0000 mss 0x05b4
lport 0x0000 rport 0x0000 rip 0x0000 0x0000
@@ -46,7 +47,7 @@ a flooded CPU port from a stalled loop, which look identical from outside.
## Phases
Each line is one section of the main loop: the link poll, SFP handling,
frame reception, TCP transmission, the STP timers and the command dispatch.
frame reception, TCP transmission, the STP timers, the LLDP timer and the command dispatch.
`max` is the worst time that section has taken, in ticks of 5 ms; `slow`
counts the passes that took two ticks or more. A misbehaving subsystem
names itself here, which is the difference between knowing that the loop is
+572
View File
@@ -0,0 +1,572 @@
# LEDs HOWTO using firmware function tracing
This is a tutorial to show how to figure out the LED configuration for
unmanaged switches (on managed ones one can just print out the registers
from the command line) without using an I2C port on the device together
an I2C-capable microcontroller. You will only need to have a SOIC clamp
and will have to run ghidra to do a bit of reversing.
The additional advantage of this method is that it not only gives the LED
configuration but also the full PHY configuration and SDS configuration e.g.
in case of additional PHYs being used on the device.
We will use the ZoeRax HG0402XG V1.1 as an example device.
The first step is to read out the flash with a SOIC clamp.
Save the image as
`zoerax_hg0402xg.bin`
The next step is to use a decompiler such as ghidra to identify the
functions used to write or read registers, SDS or to the PHY.
<img src="images/ghidra_open.png" width="300" />
First, load the code in BANK0 into ghidra. The type of code is
8051 default big-endian. The code starts at offset 0x0002 in the file
and has length 0x3ffd
<img src="images/ghidra_add_block.png" width="300" />
Add BANK1 into ghidra by using File->Add to program...
and add the code starting at 0x4000 in `zoerax_hg0402xg.bin` with length
0xc000. Repeat for BANK2 (starts at 0x10000) and BANK3 (starts at 0x1c0000).
They both have length 0xc000.
<img src="images/ghidra_search_read_reg.png" width="300" />
Now, find the read_reg function. An example of the read-register function
is given below. Note the critical bit, namely the execution of the
read-function of an ASIC register via an SFR:
```
CODE1::4dc9 75 a0 01 MOV SFR_EXEC_GO,#0x1
```
This sequence of bytes is very specific. Replace 0x01 with 0x03 and you have
the regster write SFR, 0x05 reads the SerDes, 0x07 writes it. 0x09 reads
from the PHY and 0x0b writes to it.
```
**************************************************************
* FUNCTION *
**************************************************************
undefined reg_read()
undefined <UNASSIGNED> <RETURN>
reg_read
CODE1::4da9 ab 07 MOV R3,BANK0_R7 = ??
CODE1::4dab aa 06 MOV R2,BANK0_R6 = ??
CODE1::4dad e4 CLR A
CODE1::4dae ff MOV R7,A
CODE1::4daf fe MOV R6,A
CODE1::4db0 fd MOV R5,A
CODE1::4db1 fc MOV R4,A
CODE1::4db2 90 00 c9 MOV DPTR,#0xc9
CODE1::4db5 12 06 15 LCALL FUN_CODE_0615 undefined FUN_CODE_0615()
CODE1::4db8 e4 CLR A
CODE1::4db9 ff MOV R7,A
CODE1::4dba fe MOV R6,A
CODE1::4dbb fd MOV R5,A
CODE1::4dbc fc MOV R4,A
CODE1::4dbd 90 00 c5 MOV DPTR,#0xc5
CODE1::4dc0 12 06 15 LCALL FUN_CODE_0615 undefined FUN_CODE_0615()
CODE1::4dc3 ea MOV A,R2
CODE1::4dc4 f5 a2 MOV SFR_A_ADDRH,A = ??
CODE1::4dc6 eb MOV A,R3
CODE1::4dc7 f5 a3 MOV SFR_A_ADDRL,A = ??
CODE1::4dc9 75 a0 01 MOV SFR_EXEC_GO,#0x1 <<<<<<<< Executes READ REG
LAB_CODE1__4dcc XREF[1]: CODE1::4dcf(j)
CODE1::4dcc af a1 MOV R7,SFR_EXEC_STATUS = ??
CODE1::4dce ef MOV A,R7
CODE1::4dcf 70 fb JNZ LAB_CODE1__4dcc
CODE1::4dd1 af a4 MOV R7,DAT_SFR_a4 = ??
CODE1::4dd3 fc MOV R4,A
CODE1::4dd4 fd MOV R5,A
CODE1::4dd5 fe MOV R6,A
CODE1::4dd6 90 00 c9 MOV DPTR,#0xc9
CODE1::4dd9 12 06 15 LCALL FUN_CODE_0615 undefined FUN_CODE_0615()
CODE1::4ddc 90 00 c5 MOV DPTR,#0xc5
CODE1::4ddf 12 05 f9 LCALL FUN_CODE_05f9 undefined FUN_CODE_05f9()
CODE1::4de2 c0 00 PUSH BANK0_R0
CODE1::4de4 90 00 c9 MOV DPTR,#0xc9
CODE1::4de7 12 05 dd LCALL read_xmem_32_b undefined read_xmem_32_b()
CODE1::4dea 78 18 MOV R0,#0x18
CODE1::4dec 12 05 86 LCALL FUN_CODE_0586 undefined FUN_CODE_0586()
CODE1::4def d0 00 POP BANK0_R0
CODE1::4df1 12 05 48 LCALL FUN_CODE_0548 undefined FUN_CODE_0548()
CODE1::4df4 90 00 c5 MOV DPTR,#0xc5
CODE1::4df7 12 06 15 LCALL FUN_CODE_0615 undefined FUN_CODE_0615()
CODE1::4dfa af a5 MOV R7,DAT_SFR_a5 = ??
CODE1::4dfc e4 CLR A
CODE1::4dfd fc MOV R4,A
CODE1::4dfe fd MOV R5,A
CODE1::4dff fe MOV R6,A
CODE1::4e00 90 00 c9 MOV DPTR,#0xc9
CODE1::4e03 12 06 15 LCALL FUN_CODE_0615 undefined FUN_CODE_0615()
CODE1::4e06 90 00 c5 MOV DPTR,#0xc5
CODE1::4e09 12 05 f9 LCALL FUN_CODE_05f9 undefined FUN_CODE_05f9()
CODE1::4e0c c0 00 PUSH BANK0_R0
CODE1::4e0e 90 00 c9 MOV DPTR,#0xc9
CODE1::4e11 12 05 dd LCALL read_xmem_32_b undefined read_xmem_32_b()
CODE1::4e14 78 10 MOV R0,#0x10
CODE1::4e16 12 05 86 LCALL FUN_CODE_0586 undefined FUN_CODE_0586()
CODE1::4e19 d0 00 POP BANK0_R0
CODE1::4e1b 12 05 48 LCALL FUN_CODE_0548 undefined FUN_CODE_0548()
CODE1::4e1e 90 00 c5 MOV DPTR,#0xc5
CODE1::4e21 12 06 15 LCALL FUN_CODE_0615 undefined FUN_CODE_0615()
CODE1::4e24 af a6 MOV R7,DAT_SFR_a6 = ??
CODE1::4e26 e4 CLR A
CODE1::4e27 fc MOV R4,A
CODE1::4e28 fd MOV R5,A
CODE1::4e29 fe MOV R6,A
CODE1::4e2a 90 00 c9 MOV DPTR,#0xc9
CODE1::4e2d 12 06 15 LCALL FUN_CODE_0615 undefined FUN_CODE_0615()
CODE1::4e30 90 00 c5 MOV DPTR,#0xc5
CODE1::4e33 12 05 f9 LCALL FUN_CODE_05f9 undefined FUN_CODE_05f9()
CODE1::4e36 c0 00 PUSH BANK0_R0
CODE1::4e38 90 00 c9 MOV DPTR,#0xc9
CODE1::4e3b 12 05 dd LCALL read_xmem_32_b undefined read_xmem_32_b()
CODE1::4e3e 78 08 MOV R0,#0x8
CODE1::4e40 12 05 86 LCALL FUN_CODE_0586 undefined FUN_CODE_0586()
CODE1::4e43 d0 00 POP BANK0_R0
CODE1::4e45 12 05 48 LCALL FUN_CODE_0548 undefined FUN_CODE_0548()
CODE1::4e48 90 00 c5 MOV DPTR,#0xc5
CODE1::4e4b 12 06 15 LCALL FUN_CODE_0615 undefined FUN_CODE_0615()
CODE1::4e4e af a7 MOV R7,DAT_SFR_a7 = ??
CODE1::4e50 e4 CLR A
CODE1::4e51 fc MOV R4,A
CODE1::4e52 fd MOV R5,A
CODE1::4e53 fe MOV R6,A
CODE1::4e54 90 00 c9 MOV DPTR,#0xc9
CODE1::4e57 12 06 15 LCALL FUN_CODE_0615 undefined FUN_CODE_0615()
CODE1::4e5a 90 00 c5 MOV DPTR,#0xc5
CODE1::4e5d 12 05 dd LCALL read_xmem_32_b undefined read_xmem_32_b()
CODE1::4e60 90 00 c9 MOV DPTR,#0xc9
CODE1::4e63 12 05 f9 LCALL FUN_CODE_05f9 undefined FUN_CODE_05f9()
CODE1::4e66 12 05 48 LCALL FUN_CODE_0548 undefined FUN_CODE_0548()
CODE1::4e69 90 00 c5 MOV DPTR,#0xc5
CODE1::4e6c 12 06 15 LCALL FUN_CODE_0615 undefined FUN_CODE_0615()
CODE1::4e6f 90 00 c5 MOV DPTR,#0xc5
CODE1::4e72 02 05 dd LJMP read_xmem_32_b undefined read_xmem_32_b()
-- Flow Override: CALL_RETURN (CALL_TERMINATOR)
```
INSERT ghidra_search_read_reg.png
Identify all the read and write function to the HW of the SoC using the find
in memory function.
The next task is to adapt the intercept routines in a special assembler
file. Don't worry, you do not need to understand assembler to do this.
```
; Assembler file to intercept register writes/reads and PHY writes/reads
; assemble with as31
; Compile with
; as31 -Fbyte -Orwintercept_zoerax_hg0402xg.hex rwintercept_zoerax_hg0402xg.asm
; ./injector zoerax_hg0402xg.bin RTLPlayground/rwintercept_zoerax_hg0402xg.hex zoerax_hg0402xg_patched.bin
; Start at 0x3b00, where there is always some space in BANK 0
.equ valptr, 0x199
.equ SFR_DATA_0, 0xa7
.equ SFR_DATA_8, 0xa6
.equ SFR_DATA_16, 0xa5
.equ SFR_DATA_24, 0xa4
; Intercept Register Write
.org 0x4d62
lcall reg_write
nop
; Intercept Register Read
.org 0x4dad
lcall reg_read_head
.org 0x4e6f
lcall reg_read_tail
; Intercept PHY write
.org 0x522c
lcall phy_write
; Intercept PHY read
.org 0x526f
lcall phy_read_head
.org 0x5295
ljmp phy_read_tail
; Intercept SDS write
.org 0x503c
lcall sds_write
nop
; Intercept SDS read
.org 0x5061
lcall sds_read_head
nop
.org 0x507b
ljmp sds_read_tail
;
; BEGIN actual code at unused end of BANK0
;
.org 0x3b00
;
; print the address of a register write. The address is in R6/R7. A and r4 can be scratched
;
reg_write:
mov a, #'R'
acall putc
mov a, r6
acall wbyte
mov a, r7
acall wbyte
mov a, #'-'
acall putc
mov dptr, #valptr
movx a, @dptr
acall wbyte
mov dptr, #valptr + 1
movx a, @dptr
acall wbyte
mov dptr, #valptr + 2
movx a, @dptr
acall wbyte
mov dptr, #valptr + 3
movx a, @dptr
acall wbyte
mov a, #' '
acall putc
mov a, r7
mov r5, a
mov a, r6
mov r4, a
ret
[...]
```
The full assembler file can be found here: XXXXXXXXXX
Once you have assembled the code, inject the bytes into the existing image,
creating a patched version:
```
$ as31 -Fbyte -Orwintercept_zoerax_hg0402xg.hex rwintercept_zoerax_hg0402xg.asm
Begin Pass #1
Begin Pass #2
./injector zoerax_hg0402xg.bin rwintercept_zoerax_hg0402xg.hex zoerax_hg0402xg_patched.bin
Input file size: 1048576
Bytes read:: 1048576
Patched 417 bytes.
```
Now flash `zoerax_hg0402xg_patched.bin` onto device.
Connect a serial adapter and plug in power. You will see:
```
$ picocom -b 9600 /dev/ttyUSB0
picocom v3.1
port is : /dev/ttyUSB0
flowcontrol : none
baudrate is : 9600
parity is : none
databits are : 8
stopbits are : 1
escape is : C-a
local echo is : no
noinit is : no
noreset is : no
hangup is : no
nolock is : no
send_cmd is : sz -vv
receive_cmd is : rz -vv -E
imap is :
omap is :
emap is : crcrlf,delbs,
logfile is : none
initstring : none
exit_after is : not set
exit is : no
Type [C-a] [C-h] to see available commands
Terminal ready
[The printout only starts once the serial peripherial has been set up. Initially its timing is off:]
�.Ӛ�҂���������30-00005555 r6330:00005555 R6330-00005515 [RTL837X_REG_SMI_MAC_TYPE]
[RTL837X_REG_SMI_PORT_POLLING: ]
r6334:00000000 R6334-000000f0
r6454:00000000 R6454-00007000
[Read SoC-Revision]
r000c:00200000 R000c-002a0000 r000c:202a6818 r000c:202a6818 R000c-20206818
p001e.000d:ffff R02f8-0000ffff R02f4-0000fffa P000001.1e00000d:fffa p001e.000d:ffff p001e.000d:ffff
R02f8-0000ffff R02f4-0000fff0 P000001.1e00000d:fff0
[RTL837X_REG_LED_MODE being configured:]
r6520:0021fdb0 R6520-0021e7b0 r6520:0021e7b0 R6520-0021e6b0
[RLDP Leds configured via RTL837X_REG_LED_RLDP_X:]
r65f8:00000018 R65f8-0000001b R65fc-fffff000 r6600:00000000 R6600-0000000f
[RTL837X_REG_LED_GLB_IO_EN: ]
r65dc:5fffff00 R65dc-7fffff00 r65dc:7fffff00 R65dc-77ffff00
[RTL837X_PIN_MUX_0, makes serial go out of sync:]
r7f8c:30000000 R7f8c-30000000 r7f8c:300��.Ӛ�҂���������30-00005555
r6330:00005555 R6330-00005515 r6334:00000000 R6334-000000f0
r6454:00000000 R6454-00007000
[Read SoC-Revision (again)]
r000c:00200000 R000c-002a0000 r000c:202a6818 r000c:202a6818 R000c-20206818
p001e.000d:ffff R02f8-0000ffff R02f4-0000fffa P000001.1e00000d:fffa p001e.000d:ffff p001e.000d:ffff
R02f8-0000ffff R02f4-0000fff0
P000001.1e00000d:fff0
#### LED configuration ########
[RTL837X_REG_LED_MODE being configured (final):]
r6520:0021fdb0 R6520-0021e7b0 r6520:0021e7b0 R6520-0021e6b0
[More RTL837X_REG_LED_RLDP]
r65f8:00000018 R65f8-0000001b R65fc-fffff000 r6600:00000000 R6600-0000000f
[RTL837X_REG_LED_GLB_IO_EN]
r65dc:5fffff00 R65dc-7fffff00 r65dc:7fffff00 R65dc-77ffff00
[RTL837X_PIN_MUX_0
r7f8c:30000000 R7f8c-30000000 r7f8c:30000000 R7f8c-38000000
[RTL837X_LED_PORT_SET_SEL]
r654c:00000000 R654c-00010000
[RTL837X_REG_LED1_0_SET0]
R6548-00000141
[RTL837X_REG_LED3_2_SET0]
r6544:01411000 R6544-01410174
[RTL837X_REG_LED1_0_SET1]
r6540:00440070 R6540-0044017f
[RTL837X_REG_LED3_0_SET1]
r6528:00000000 R6528-000f0000
[SDS 0 being configure:]
r7b20:00000bff R7b20-000003ff r7b20:000003ff R7b20-000003fa r7b20:000003fa R7b20-000003fa
Q002110:4480 Q002113:0400 Q002118:6d02 Q00211b:424e Q00211d:0002 Q00361c:1390
Q003614:003f Q003610:0200 Q002e04:0080 Q002e06:0408 Q002e07:020d Q002e09:0601
Q002e0b:222c Q002e0c:a217 Q002e0d:fe40 Q002e15:f5c1 Q002e16:0443 Q002e1d:abb0
Q000612:5078 Q000706:9401 Q000708:9401 Q00070a:9401 Q00070c:9401 Q001f0b:0003
Q000603:c45c Q00061f:2100 q001f0b:0003 Q001f0b:000f q002000:0000 Q002000:0030
q002000:0030 Q002000:0010 q002000:0010 Q002000:0050 q002000:0050 Q002000:00d0
q002000:00d0 Q002000:0cd0 q002000:0cd0 Q002000:04d0 q002000:04d0 Q002000:04d0
q002000:04d0 Q002000:0cd0 q002000:0cd0 Q002000:00d0 q002000:00d0 Q002000:00d0
q002000:00d0 Q002000:0050 q002000:0050 Q002000:0010 q002000:0010 Q002000:0010
q002000:0010 Q002000:0030 q002000:0030 Q002000:0000 q001f00:0000 Q001f00:000b
q001f00:000b Q001f00:0000
[SDS 1 being configured]
r7b20:000003fa R7b20-000003fa r7b20:000003fa R7b20-0000035a r7b20:0000035a R7b20-0000035a
Q012110:4480 Q012113:0400 Q012118:6d02 Q01211b:424e Q01211d:0002 Q01361c:1390
Q013614:003f Q013610:0200 Q012e04:0080 Q012e06:0408 Q012e07:020d Q012e09:0601
Q012e0b:222c Q012e0c:a217 Q012e0d:fe40 Q012e15:f5c1 Q012e16:0443 Q012e1d:abb0
Q010612:5078 Q010706:9401 Q010708:9401 Q01070a:9401 Q01070c:9401 Q011f0b:0003
Q010603:c45c Q01061f:2100 q011f0b:0003 Q011f0b:000f q012000:0000 Q012000:0030
q012000:0030 Q012000:0010 q012000:0010 Q012000:0050 q012000:0050 Q012000:00d0
q012000:00d0 Q012000:0cd0 q012000:0cd0 Q012000:04d0 q012000:04d0 Q012000:04d0
q012000:04d0 Q012000:0cd0 q012000:0cd0 Q012000:00d0 q012000:00d0 Q012000:00d0
q012000:00d0 Q012000:0050 q012000:0050 Q012000:0010 q012000:0010 Q012000:0010
q012000:0010 Q012000:0030 q012000:0030 Q012000:0000 q011f00:0000 Q011f00:000b
q011f00:000b Q011f00:0000 q012100:4902 Q012100:4906 q013605:0000 Q013605:4000
Q011f02:001f q011f15:0096 q002100:4902 Q002100:4906 q003605:0000 Q003605:4000
Q001f02:001f q001f15:00a6
r0a90:000000f3 R0a90-000000fc P0000f0.1f00a610:2858 r5fd4:0002914a
R5fd4-001a914a r1538:00000e33 R1538-00000e37 r1538:00000e37 R1538-00000e37
r1538:00000e37 R1538-00000f37 r1638:00000e33 R1638-00000e37 r1638:00000e37
R1638-00000e37 r1638:00000e37 R1638-00000f37 r1738:00000e33 R1738-00000e37
r1738:00000e37 R1738-00000e37 r1738:00000e37 R1738-00000f37 r1838:00000e33
R1838-00000e37 r1838:00000e37 R1838-00000e37 r1838:00000e37 R1838-00000f37
r1938:00000e33 R1938-00000e37 r1938:00000e37 R1938-00000e37 r1938:00000e37
R1938-00000f37 r1a38:00000e33 R1a38-00000e37 r1a38:00000e37 R1a38-00000e37
r1a38:00000e37 R1a38-00000f37 r0b7c:000000d8 R0b7c-000000f8 R7124-00001050
R7128-00001050 R712c-00001050 R7130-00001050 R7134-00001050 R7138-00001050
R713c-00001050 R7140-00001050 R7144-00001050 R7148-00001050 r6040:00000030
R6040-00000031 p041f.a436:8195 P000010.1f00a436:0005 p041f.a438:02d4
P000010.1f00a436:801e p041f.a438:0000 p041f.b820:0000 P000010.1f00b820:0010
p041f.b800:00c0 p041f.a436:801e P000010.1f00a436:8023 p041f.a438:0000
P000010.1f00a438:1802 p041f.a436:8025
[PHY with ID 5 being patched] P000010.1f00a436:b82e p041f.a438:0000
P000010.1f00a438:0001 p041f.b820:0010 P000010.1f00b820:0090
P000010.1f00a436:a016 P000010.1f00a438:0000 P000010.1f00a436:a012
P000010.1f00a438:0000 P000010.1f00a436:a014 P000010.1f00a438:1800
P000010.1f00a438:8010 P000010.1f00a438:1800 P000010.1f00a438:8013
P000010.1f00a438:1800 P000010.1f00a438:8026 P000010.1f00a438:1800
P000010.1f00a438:802a P000010.1f00a438:1800 P000010.1f00a438:802e
P000010.1f00a438:1800 P000010.1f00a438:802e P000010.1f00a438:1800
P000010.1f00a438:802e P000010.1f00a438:1800 P000010.1f00a438:802e
...
```
Where:
```
r: read-reg
R: write-reg
q: read SDS
Q: write SDS
p: read PHY
P: write SDS
```
The LED configuration has been marked above. We now write exactly those registers
into the registers of `rtlplayground.c` on startup:
```
[...]
set_sys_led_state(SYS_LED_ON);
// [RTL837X_REG_LED_MODE being configured (final):]
// r6520:0021fdb0 R6520-0021e7b0 r6520:0021e7b0 R6520-0021e6b0
REG_SET(RTL837X_REG_LED_MODE, 0x0021e6b0);
// [More RTL837X_REG_LED_RLDP]
// r65f8:00000018 R65f8-0000001b R65fc-fffff000 r6600:00000000 R6600-0000000f
REG_SET(0x65f8, 0x0000001b);
REG_SET(0x65fc, 0xfffff000);
REG_SET(0x6600, 0x0000000f);
// [RTL837X_REG_LED_GLB_IO_EN]
// r65dc:5fffff00 R65dc-7fffff00 r65dc:7fffff00 R65dc-77ffff00
REG_SET(0x65dc, 0x77ffff00);
// [RTL837X_PIN_MUX_0
// r7f8c:30000000 R7f8c-30000000 r7f8c:30000000 R7f8c-38000000
REG_SET(0x7f8c, 0x38000000);
// [RTL837X_LED_PORT_SET_SEL]
// r654c:00000000 R654c-00010000
REG_SET(0x654c, 0x00010040);
// [RTL837X_REG_LED1_0_SET0]
// R6548-00000141
REG_SET(0x6548, 0x00000141);
// [RTL837X_REG_LED3_2_SET0]
// r6544:01411000 R6544-01410174
REG_SET(0x6544, 0x01410174);
// [RTL837X_REG_LED1_0_SET1]
// r6540:00440070 R6540-0044017f
REG_SET(0x6540, 0x0044017f);
// [RTL837X_REG_LED3_0_SET1]
// r6528:00000000 R6528-000f0000
REG_SET(0x6528, 0x000f0000);
cmd_editor_init();
[...]
```
Create an initial configuraiton for your device by copying over an existing one
for the same device type, i.e. same port-configuraiton and same SoC,
give it a new name. Make sure that the led_mux_custom and led_mux fields are not
used, because our device does not customize the LED muxes.
Compile and flash onto your device.
Now, boot the switch and issue the `leds`command, which will print out:
```
RTL837X_PIN_MUX_0: 0x38000000
RTL837X_REG_LED_GLB_IO_EN: 0x77ffff00
RTL837X_REG_LED1_0_SET0: 0x00000141
RTL837X_REG_LED3_2_SET0: 0x01410174
RTL837X_REG_LED1_0_SET1: 0x0044017f
RTL837X_REG_LED3_2_SET1: 0x18000040
RTL837X_REG_LED1_0_SET2: 0x00000000
RTL837X_REG_LED3_2_SET2: 0x00000000
RTL837X_REG_LED1_0_SET3: 0x00000000
RTL837X_REG_LED3_2_SET3: 0x00000000
RTL837X_REG_LED3_0_SET1: 0x000f0000
RTL837X_REG_LED3_0_SET3: 0x00000000
RTL837X_LED_PORT_SET_SEL: 0x00010040
RTL837X_REG_LED_GLB_MUX_1: 0x3fffffff
RTL837X_REG_LED_GLB_MUX_2: 0x0c3fffff
RTL837X_REG_LED_GLB_MUX_3: 0x1245038d
RTL837X_REG_LED_GLB_MUX_4: 0x19616554
RTL837X_REG_LED_GLB_MUX_5: 0x2079d71a
RTL837X_REG_LED_GLB_MUX_6: 0x000238a1
RTL837X_REG_LED_GLB_ACTIVE: 0x3ffbedff
RTL837X_REG_LED_MODE: 0x0021e6b0
LED pad Configuration:
00 01 02 03 04 05 06 07 08 09 0a 0b 0c 0d 0e 0f 10 11 12 13 14 15 16 17 18 19 1a 1b
3f 3f 3f 3f 3f 3f 3f 3f 0f 0c 0d 0e 10 11 12 14 15 16 18 19 1a 1c 1d 1e 20 21 22 23
LED-set Configuration:
LED-ID 0 1 2 3
SET 0: 000141 000000 000174 000141
SET 1: 0f017f 000044 000040 001800
SET 2: 000000 000000 000000 000000
SET 3: 000000 000000 000000 000000
Port 3: SET 1: ( 2G5 TWO_1G 1G 500M 100M 10M LINK ACT 10G TWO_5G 5G TWO_2G5), ( 1G LINK), ( LINK), ( COL DUPLEX),
Port 4: SET 0: ( 2G5 LINK ACT), (), ( 1G 100M 10M LINK ACT), ( 2G5 LINK ACT),
Port 5: SET 0: ( 2G5 LINK ACT), (), ( 1G 100M 10M LINK ACT), ( 2G5 LINK ACT),
Port 6: SET 0: ( 2G5 LINK ACT), (), ( 1G 100M 10M LINK ACT), ( 2G5 LINK ACT),
Port 7: SET 0: ( 2G5 LINK ACT), (), ( 1G 100M 10M LINK ACT), ( 2G5 LINK ACT),
Port 8: SET 1: ( 2G5 TWO_1G 1G 500M 100M 10M LINK ACT 10G TWO_5G 5G TWO_2G5), ( 1G LINK), ( LINK), ( COL DUPLEX),
```
Together with the GPIO information, this will give you the following machine configuration:
```
_code const struct machine machine = {
.machine_name = "ZOERAX HG0402XG",
.isRTL8373 = 0,
.min_port = 3,
.max_port = 8,
.n_sfp = 2,
.log_to_phys_port = {0, 0, 0, 5, 1, 2, 3, 4, 6},
.phys_to_log_port = {4, 5, 6, 7, 3, 8, 0, 0, 0},
.is_sfp = {0, 0, 0, 1, 0, 0, 0, 0, 2},
// Left SFP port (5)
.sfp_port[0].pin_detect = GPIO30_ACL_BIT3_EN,
.sfp_port[0].pin_los = GPIO_NA,
.sfp_port[0].pin_tx_disable = GPIO_NA,
.sfp_port[0].sds = 1,
.sfp_port[0].i2c = I2CBUS( GPIO39_I2C_SDA4, GPIO40_I2C_SCL3_MDC1 ),
// Right SFP port (6)
.sfp_port[1].pin_detect = GPIO50_I2C_SCL2_UART1_TX,
.sfp_port[1].pin_los = GPIO_NA,
.sfp_port[1].pin_tx_disable = GPIO_NA,
.sfp_port[1].sds = 0,
.sfp_port[1].i2c = I2CBUS( GPIO41_I2C_SDA3_MDIO1, GPIO40_I2C_SCL3_MDC1 ),
.reset_pin = GPIO_NA,
.high_leds = { .mux = LED_27 | LED_29, .enable = LED_28_SYS | LED_29 },
.port_led_set = { 0, 0, 0, 1, 0, 0, 0, 0, 1},
/* On Ethernet: Left led is orange, right green
* 100M and 1G give right led green with activity
* 2.5GBit adds solid left orange LED
* LEDs for the SFP-ports are the same as for the Ethernet ports
*/
.led_sets = { { LEDS_2G5 | LEDS_LINK | LEDS_ACT,
0,
LEDS_1G | LEDS_100M | LEDS_10M | LEDS_LINK | LEDS_ACT,
LEDS_2G5 | LEDS_LINK | LEDS_ACT
},
{ LEDS_2G5 | LEDS_TWO_PAIR_1G | LEDS_1G | LEDS_500M | LEDS_100M | LEDS_10M |
LEDS_LINK | LEDS_ACT | LEDS_10G | LEDS_TWO_PAIR_5G | LEDS_5G | LEDS_TWO_PAIR_2G5,
LEDS_1G | LEDS_LINK,
LEDS_LINK,
LEDS_COL | LEDS_DUPLEX
}
}
};
```
Binary file not shown.

After

Width:  |  Height:  |  Size: 75 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 72 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 100 KiB

+154 -50
View File
@@ -2,9 +2,11 @@
The switch can take part in a spanning tree (IEEE 802.1D / 802.1w) so that
redundant links between bridges are blocked instead of forming a loop. The
implementation elects a root bridge from the BPDUs it receives, promotes ports
to forwarding once their listen period expires, ages the root out when it goes
silent, and blocks a port on which it sees its own BPDU.
implementation elects a root bridge from the BPDUs it receives, picks the
cheapest path to it as the root port, blocks ports on which a better bridge is
already designated, promotes ports to forwarding once their listen period
expires, drops information a neighbour stopped sending, and blocks a port on
which it sees its own BPDU.
STP can be enabled and controlled via the web interface or the command line,
as follows:
@@ -64,32 +66,6 @@ LACPDUs, delivered by forward, stopped arriving on port 3, and BPDUs sent into
the same port, delivered by trap, kept arriving. A port that admits tagged
frames only therefore takes part in STP.
## Link aggregation
A LAG (Link Aggregation Group) is handled like an additional port by the STP
protocol, with its own timers and states. The switch devices support up to 4
LAGs, which are shown alongside the physical ports in the STP status. A group
carries its own path cost, priority, edge and guard settings, and gets its own
row on the Spanning Tree page.
```
stp lag 1 cost 10000 # the group decides, not its members
stp lag 1 edge off
```
LAGs are configured via the `lag` command. Once a port is a member of a LAG it
can no longer be configured individually for STP, so `stp port <n>` on a member
tells you which group to configure instead. Membership is re-read from the
aggregation registers once a second, so a group changing under LACP is picked
up without any coordination between the two.
The switch hardware does not handle STP state for a LAG as a whole. State
changes for the members have to be done by the firmware, updating every member
port, which is possible using a single register write. BPDUs go out through the
lowest member and carry the group's own port id. Losing one member of a live
LAG is not a topology change; the logical port only goes down with its last
link.
Port states live in `RTL837X_MSTP_STATES (0x5310)`, two bits per port:
`00` disabled, `01` blocking, `10` learning, `11` forwarding. A port in
blocking forwards nothing between ports, but it still sends what the CPU
@@ -109,19 +85,83 @@ encodes. All configured values are in seconds:
| `stp fwd <n>` | 15 | 4–30 |
| `stp txhold <n>` | 6 | 1–10 |
A port entering the tree spends `fwd` seconds in blocking before it forwards
(an edge port skips the wait). Root information is discarded after `maxage`
seconds without a BPDU, and the switch then reclaims the root role.
802.1D also requires `2 * (fwd - 1) >= maxage >= 2 * (hello + 1)`. A
command typed on the console or sent by the web page that would break it is
refused and the value stays as it was, so change the values in an order that
keeps the rule at every step (for a larger max age raise the forward delay
first). The web page shows the error, and a refused command is not recorded
in the command history, so saving the configuration does not pick it up. Lines of the saved configuration are taken one by one whatever their
order, and `stp on` checks the result: if the rule is broken it warns and
starts with 2/20/15 instead.
The values above are what the switch uses while it is the root. Otherwise it
takes max age and forward delay from the BPDUs on its root port, as 802.1D
requires, and passes them on in its own BPDUs with the message age one second
higher; the hello time stays its own.
A port entering the tree discards for one forward delay, then learns
addresses without forwarding for a second one, and only then forwards (an
edge port skips the wait). In RSTP mode on a point-to-point link
(`p2p auto` or `on`) it can go faster: a designated port that is still
discarding sets the proposal flag, and forwards as soon as the bridge on the
other side answers with an agreement. When the root port receives a proposal,
the switch first puts its other non-edge designated ports back to discarding
(they propose in turn), then forwards the root port and answers with an
agreement; this sync happens once per root port, later proposals are only
answered. An alternate or backup port that receives a proposal does the same
sync once for the information it holds and answers with an agreement in a
BPDU with the alternate role, so the bridge on the other side does not have
to wait out its timers; it stays discarding itself. An agreement is accepted
from a root or an alternate port on the other side. A new root port forwards at once, after every port that was root within
the last forward delay has been put back to discarding; such a recent root
port stays closed, even to an agreement, until that forward delay is over.
A new root port that was a backup port within the last two hello times does
not take the fast path and waits for its timers.
A designated port that hears worse information from a bridge that still
claims to be designated and learning or forwarding on the same link has a
dispute: usually one direction of the link is lost and the other bridge does
not hear this switch's BPDUs. The port goes back to discarding, and every
further such BPDU restarts its listen period, so it does not forward while
the dispute lasts.
With `stp version rstp` each port still talks 802.1D to a neighbour that
does: once a Config BPDU or TCN arrives and the migrate time (3 s since the
port came up or last switched) is over, the port sends Config BPDUs, uses
the 802.1D TC window and reports changes with TCN if it is the root port. An
RST BPDU after the migrate time switches it back, and so does a link that
comes up again or `stp port <n> mcheck`. Any worse information heard on a designated port, disputed or
not, is answered with a BPDU at once instead of at the next hello, so the
other bridge learns sooner that it is not the designated one. Information heard on a port stays
valid for three of the sender's hello times; a BPDU whose message age has
reached its max age is not used at all. When the root port's information
expires the switch moves to the next best port, or reclaims the root role if
there is none.
Among ports offering the same root, the lowest root path cost wins, then the
designated bridge, the designated port ID, and finally this switch's own port
ID. A worse BPDU from a different bridge does not replace what a port holds;
a worse one from the same designated bridge and port does.
The number in a port ID is the one on the front panel, and 101 and up for
the LAGs, so it matches the port labels on both ends of a link.
## Topology changes
A change on a local non-edge port (the link coming or going, a port promoted
to forwarding) flushes the addresses learned on it and sets the TC flag in
our BPDUs for `maxage + fwd` seconds. A TC flag received in a BPDU is passed
on: the switch flushes the other non-edge ports once and keeps the flag in
its own BPDUs until one hello after the last flagged frame, so the
notification crosses the switch instead of dying at it. A legacy TCN is
acknowledged with TCA and then treated like a local change.
A topology change is detected when a non-edge port enters forwarding. That
port, and every other forwarding non-edge port, then carries the TC flag in
its BPDUs for hello time plus one second (max age plus forward delay with
`stp version stp`), and each of the other ports flushes the addresses it
learned. The root port takes part too: while its TC window runs it sends
BPDUs toward the root, so the change reaches the rest of the network. With
`stp version stp` the root port sends TCN instead, until a Config BPDU with
TCA comes back.
A TC flag received on a forwarding root or designated port is passed on the
same way to the other ports, not back to the port it came in on; a port that
is still discarding or learning drops it. A legacy TCN is acknowledged with
TCA and then treated like a received TC. A port that turns
alternate, loses its link, or is blocked for a loop flushes its own addresses
without raising a topology change, and an edge port never raises one.
## Bridge settings
@@ -129,6 +169,8 @@ acknowledged with TCA and then treated like a local change.
stp prio <0-15> # bridge priority = n * 4096, default 8 (32768)
stp version rstp|stp # RST BPDUs (default) or legacy Config BPDUs
stp hello|maxage|fwd|txhold <seconds>
stp pathcost long|short # automatic port costs, 802.1D-2004 (default) or 802.1D-1998
stp bpdu flood|filter # BPDUs while STP is off: pass them on (default) or drop them
```
The bridge with the lowest priority wins the root election; ties are broken by
@@ -136,6 +178,17 @@ the MAC address. If you do not want this switch to become the root of an
existing network, give it a worse priority than the current root — `stp prio 15`
(61440) is the usual "never me" value.
A port without a configured cost takes it from its link speed. The long
method uses the 802.1D-2004 values (20000 for 1G, 2000 for 10G); the short
one the 802.1D-1998 table for bridges that still use 16-bit costs (100 for
10M, 19 for 100M, 4 for 1G, 2 for 10G, and 3 for 2.5G and 5G, which that
table does not list). All bridges in a network should use the same method.
`stp bpdu filter` keeps BPDUs from being passed between ports while STP is
off, instead of the default flooding that lets a surrounding spanning tree
run through the switch. It takes effect at once when STP is off, otherwise
when STP is turned off.
## Per-port settings
```
@@ -146,6 +199,7 @@ stp port <1-9> prio <0-240> # port priority, steps of 16
stp port <1-9> guard none|bpdu|root
stp port <1-9> filter on|off # neither send nor accept BPDUs
stp port <1-9> p2p auto|on|off
stp port <1-9> mcheck # try RSTP again on a port that fell back to STP
```
**edge** — an edge port forwards immediately and does not trigger a
@@ -162,26 +216,76 @@ claims a better priority.
on the far side reacts badly to them (some unmanaged switches with loop
prevention cut the link) but you still want STP on the rest of the ports.
## Link aggregation
A LAG (Link Aggregation Group) is one port of the tree, with its own roles,
timers and states. The switch supports up to 4 LAGs; each one takes the place
of its member ports in the STP status and gets its own row on the Spanning
Tree page. A group carries its own path cost, priority, edge, guard and
point-to-point settings, and takes the same commands as a port:
```
stp lag <1-4> on|off|mcheck|edge|cost|prio|guard|filter|p2p ...
stp lag 1 cost 10000 # the group decides, not its members
```
LAGs are configured with the `lag` command. Once a port is a member of a LAG
it can no longer be configured on its own for STP, and `stp port <n>` on a
member names the group to configure instead. Membership is read from the
aggregation registers once a second, so a group changing under LACP is picked
up without any coordination between the two; a port that joins or leaves a
group, and the group itself, start over from discarding. The automatic path
cost of a group is the one of its lowest member that has a link.
The hardware keeps no STP state for a LAG as a whole, so every state change is
written to all member ports, in a single register write. BPDUs go out through
the lowest member that has a link and carry the group's own port id, and a
BPDU received on any member belongs to the group. Losing one member of a live
LAG is not a topology change; the group only goes down with its last link.
## Status
The Spanning Tree page shows the elected root (priority and MAC), the path cost
to it, the root port, the topology-change counter and, per port, the live state
read from the ASIC together with the configured options. The same data is
available as JSON:
to it, the root port, the topology-change counter and the time since it last
moved, the max age and forward
delay in use (the root's, when this switch is not the root) and, per port, the
live state read from the ASIC, or "No link", together with the configured
options. For a port taking part in the tree it also shows the designated
bridge, port and cost, and whether it talks RSTP or 802.1D to its neighbour;
the Check button runs `stp port <n> mcheck`. The same data is available as
JSON:
```
GET /stp.json
```
The `stp status` command prints the same view on the serial console.
The Counters section of the same page (or `/stpcnt.json`) shows per port the
BPDUs received and sent, those of them that carried the topology change flag
(TCNs included), and the time since the last BPDU arrived. The counters start
from zero with `stp on`, and `stp clear` resets them; the topology change
counter is not reset.
The `stp status` command prints the same view on the serial console. A port
is reported with one of the roles Root, Designated, Alternate, Backup or
Disabled; Disabled covers a port with STP turned off on it, one shut down by
BPDU guard and one without link.
The console's `age[s]` column (previously labelled `bpdu`) is the elapsed
time since the last BPDU, capped at 255 seconds; it is not a receive count.
Use the web counters or `/stpcnt.json` for full receive counts.
An own BPDU received back on another port can temporarily hold its sending
port in discarding, even if that sending port has received no BPDUs itself.
When the hold expires, auto-edge is restored if the sending port has remained
silent. A continuing loop keeps the hold active.
BPDUs shorter than their type allows, judged by the 802.3 length field, are
dropped: 7 bytes for a TCN, 38 for a Config BPDU and 39 for an RST BPDU,
LLC header included.
## Limitations
* One spanning-tree instance; no MSTP, no per-VLAN trees.
* No proposal/agreement handshake — an RST-capable neighbour will still
converge, but through the timers rather than the fast transition.
* Port roles are approximated: the root port and designated ports are
distinguished, alternate/backup are not.
* Topology changes propagate away from the root only: nothing is announced
on the root port (no TCN and no BPDUs at all), so bridges upstream rely on
their own detection.
* A backup port, one that hears the BPDUs of another port of this switch,
is blocked and reported as Backup, but keeps sending BPDUs: loop detection
on that segment relies on them.
+6
View File
@@ -29,6 +29,7 @@ The following devices have been tested and are fully working:
| keepLINK | KP-9000-9XHML-X | Yes | [2M-PCB23-V2.2](devices/2M-PCB23-V2_2.md) | 2M | 8 + 1 |
| keepLINK | KP-9000-9XHML-X | Yes | [2M-PCB23-V3.1](devices/2M-PCB23-V3_1.md) | 2M | 8 + 1 |
| keepLINK | KP-9000-9XHML-X | Yes | [2M-PCB23-V3.2](devices/2M-PCB23-V3_2.md) | 2M | 8 + 1 |
| keepLINK | KP-9000-9XHP-X | No | [2M-PCB51-ML-V1.1](devices/2M-PCB51-ML-V1_1.md) | 4M | 8 + 1 |
| LIANGUO | HYWS-SGT0108S | No | [2G5F_20G_V1.01](devices/HYWS-SGT0108S.md) | 0.5M | 8 + 1 |
| LIANGUO | SWTG024AS | No | [SWTG024AS-v2.0-17452](devices/SWTG024AS.md) | 0.5M | 4 + 2 |
| LIANGUO | ZX-SWTGW215AS | Yes | [PCB-SWTG115AS-V2.0 / PCB-SWTG115AS-V2.1](devices/SWTGW215AS.md) | 2M | 5 + 1 |
@@ -38,18 +39,23 @@ The following devices have been tested and are fully working:
| MokerLink| 2G08110GSN | No | [SWTG018AS-A V2.0](devices/SWTG018AS_A_V2_0.md) | 0.5M | 8 + 1 |
| Netcore | GS9 | No | [PB-2132](devices/PB-2132.md) | 4M | 8 + 1 |
| ONT | ONT-S207CW-62TS-SE | No | [PCB-SWTG024AS-A-2.0.1](devices/ONT-S207CW-62TS-SE.md) | 0.5M | 4 + 2 |
| RealHD | PSW8-25MG | Yes | [PCB-AFGW SWTG218ANS V2.0.2](devices/PCB-AFGW_SWTG218ANS_V2.0.2.md) | 2M | 8 + 1 |
| Ruiying | RY-4GT-2SX | No | [FG-4GT-2SX_V2.0](devices/FG-4GT-2SX_V2.0.md) | 4M | 4 + 2 |
| Ruiying | RY-8GT-1SX | No | [FG-8GT-1SX](devices/FG-8GT-1SX.md) | 1M | 8 + 1 |
| SICSOLINK| SL-G0402F | No | [PCB-K0402WS-V3.0](devices/PCB-K0402WS-V3.0.md) | | 4 + 2 |
| Sirivision | SR-S25G2109F | Yes | [S25G1109FP V1.3](devices/S25G1109FP_V1_3.md) | 2M | 8 + 1 |
| Sodola | SL-SWTG124AS-D | Yes | [SWTG024AS-v2.0-17452](devices/SWTG024AS.md) | 2M | 4 + 2 |
| Sodola | SL902-SWTG024AS | Yes | [SWTG024AS-v2.1.0](devices/SWTG024AS-V2.1.0.md) | 2M | 4 + 2 |
| Steamemo | IG204-V1 | No | [PB-2131](devices/STEAMEMO_IG204_V1.md) | | 4 + 2 |
| TRENDnet | TEG-S562 | No | [TEG-S563/EU H/W: V1.0R](devices/TEG-S562.md) | 2M | 4 + 2 |
| XikeStor | SKS3200M-4GPY2XF| Yes | [SWTG024AS-v1.0](devices/SWTG024AS.md) | | 4 + 2 |
| XikeStor | SKS3200-8E1X | Yes | [SWTG118AS-V2.1-17462](devices/SWTGW218AS.md) | 2M | 8 + 1 |
| XikeStor | SKS3200-4E2X | Yes | [SWTG024AS-v2.1.0](devices/SWTG024AS-V2.1.0.md) | 2M | 4 + 2 |
| YuanLey | YS25-0402 | No | [PCB-K0402WS-V1.0](devices/PCB-K0402WS-V1.0.md) | 2M | 4 + 2 |
| YuanLey | YS25-0402 | No | [PCB-K0402WS-V3.0](devices/PCB-K0402WS-V3.0.md) | 2M | 4 + 2 |
| YuanLey | YS25-0402M | Yes | [SIRIVISION_S25G2206FP_V1_1](devices/SIRIVISION_S25G2206FP_V1_1.md) | 2M | 4 + 2 |
| Ztyuav | Z-QWYT0402 | No | [PCB-K0402WS-V3.0](devices/PCB-K0402WS-V3.0.md) | | 4 + 2 |
| ZoeRax | HG0402XG | No | [HG0402XG_V1.1](devices/ZOERAX_HG0402XG_V1.1.md) | 1M | 4 + 2 |
For KP-9000-6XH-X2 / KP-9000-6XHML-X2 / Mokerlink 2G040210GSM devices, select
the machine target by PCB revision. The ML/non-ML or managed/unmanaged label
+5 -2
View File
@@ -79,8 +79,11 @@ JSON polling of the visible section).
computation no longer depends on uninitialised buffer content.
- `version.h` is now written by a single atomic `printf` (change from
upstream) instead of five separate `echo`s, so a parallel build
(`make -j`) can no longer read a half-written copy. It stays in
`.PHONY` so it is regenerated on every build.
(`make -j`) can no longer read a half-written copy. It depends on
`FORCE`, so its recipe runs on every build, but the file is only
rewritten when its content changes. The minified copy in
`output/html_min/` is redone only when a web file, the list of web
files or `tools/minify.py` changes.
## Verification
+398 -190
View File
@@ -16,7 +16,7 @@ d_system:"System",d_traffic:"Traffic",
d_traffic_h:"packets/s, live",d_txpps:"TX pps",d_rxpps:"RX pps",d_txbad:"TX bad",d_rxbad:"RX bad",
i_host:"Hostname",i_ip:"IP address",i_mask:"Netmask",i_gw:"Gateway",i_mac:"MAC",i_fw:"Firmware",
i_built:"Built",i_hw:"Hardware",i_temp:"Temperature",i_flash:"Flash",i_syslog:"Syslog",
p_state:"State",p_disabled:"disabled",p_up:"up",p_txgb:"TX good / bad",p_rxgb:"RX good / bad",
p_state:"State",p_disabled:"disabled",p_up:"up",
p_pkts:"pkts",p_module:"Module",p_temp:"Temperature",p_vcc:"Vcc",p_txbias:"TX bias",p_txpower:"TX power",
p_rxpower:"RX power",p_txfault:"TX fault",p_txdis:"TX disabled",p_rxlos:"RX LOS",p_adv:"Advertising",
pt_title:"Port configuration",pt_conn:"Devices learned",pt_speed:"Speed",pt_mtu:"MTU",
@@ -41,23 +41,27 @@ stp_en_d:"Ports start blocked and take up to twice the forward delay to reach fo
stp_dis_q:"Disable spanning tree?",stp_dis_d:"All ports go straight to forwarding; loop protection is lost.",
stp_cost_err:"Path cost must be 0-200000000",
lldp_enabled: "LLDP Enabled",lldp_title:"Service Status",lldp_portname:"Name",lldp_enable:"Send Frames",
stp_pcs:"Path cost",stp_pcs_long:"Long (32 bit)",stp_pcs_short:"Short (16 bit)",
stp_bh:"BPDUs while disabled",stp_bh_flood:"Flood",stp_bh_filter:"Filter",
stp_r4:"Backup",stp_times:"max age / forward delay",stp_proto:"Protocol",stp_nolink:"no link",stp_lasttc:"last",
stp_mcheck:"Check",stp_mcheck_t:"Send RST BPDUs again to find out whether the neighbour still needs 802.1D",
stp_cnt:"Counters",stp_crx:"BPDUs received",stp_ctx:"BPDUs sent",stp_ctrx:"TC received",stp_cttx:"TC sent",stp_cage:"Last BPDU [s]",
st_title:"Port statistics",st_h:"totals since boot",st_txg:"TX good",st_txb:"TX bad",st_rxg:"RX good",
st_rxb:"RX bad",st_details:"Details",st_counters:"MIB counters",st_nonzero:"non-zero only",
st_autoref:"auto-refresh",st_counter:"Counter",st_value:"Value",st_fail:"failed to load counters",
v_title:"VLANs",v_vid:"VID",v_members:"Members",v_tagged:"Tagged",v_untagged:"Untagged",v_pvid_on:"PVID on",
v_title:"VLANs",v_vid:"VLAN ID",v_members:"Members",v_tagged:"Tagged",v_untagged:"Untagged",v_pvid_on:"PVID on",
v_empty:"No VLANs configured.",v_mgmt:"Management VLAN",v_mgmt_none:"untagged",v_editor:"VLAN editor",
v_optional:"optional",v_setmgmt:"Set as mgmt VLAN",v_setmgmt_t:"Make this the management VLAN",
v_member:"Member",v_pvid:"PVID",
v_legend:"U = untagged member, T = tagged member, - = not a member. PVID assigns this VLAN to untagged ingress traffic.",
v_ingress:"Ingress filtering",v_ingress_h:"write-only: the state is not readable from the switch",
v_accept:"Accept",v_ing_all:"All",v_ing_apply:"Apply ingress modes",v_del_t:"Delete VLAN",
v_optional:"optional",v_setmgmt_q:"Set {n} as the management ID?",v_setmgmt_t:"Management VLAN",
v_member:"Member",v_pvid:"PVID",v_add:"Add a new VLAN", v_mgmt_c:"WARNING: You must be able to access this VLAN to continue to manage the switch.",
v_legend:"U = untagged member, T = tagged member, - = not a member. PVID is automatically assigned based on the untagged membership.",
v_accept:"Accept",v_ing_all:"All",v_del_t:"Delete VLAN",v_rename:"Rename",v_rename_t:"Rename VLAN",
v_del_q:"Delete VLAN {n}?",v_del_d:"Ports keep their PVID until reassigned.",
v_vid_err:"Enter a VLAN ID (1-4094)",v_name_err:"Name must start with a letter (letters, digits, _)",
v_vid_err:"Enter a VLAN ID (1-4094)",v_name_err:"Name must start with a letter (letters, digits, _) and cannot be just 'd' or 'mgmt'",
v_loaded:"Loaded VLAN {n}",v_notfound:"VLAN {n} not found (a new one can still be created)",
v_nomember:"Select at least one member port (or delete the VLAN instead)",
v_ing_none:"Pick an ingress mode for at least one port",v_vid_first:"Enter a VLAN ID first",
v_mgmt_q:"Set VLAN {n} as management VLAN?",
v_nomember:"Select at least one member port (or delete the VLAN instead)",v_vid_first:"Enter a VLAN ID first",
v_mgmt_q:"Set VLAN {n} as management VLAN?",v_vid_err:"VLAN ID already exists",
v_mgmt_d:"The switch will start tagging its own traffic with that VLAN. If the port you are connected through does not carry it, this page becomes unreachable and the setting can only be undone over the console.",
v_mgmt_tt:"The management VLAN cannot be deleted, you need to assign management to another VLAN first.",
l2_title:"MAC address table",l2_filter:"filter...",l2_flush:"Flush learned entries",l2_static:"static",
l2_learned:"learned",l2_loading:"loading...",l2_failed:"load failed",l2_entries:"entries",
l2_del_t:"Delete entry",l2_flush_q:"Flush all learned MAC entries?",
@@ -147,7 +151,7 @@ d_system:"システム",d_traffic:"トラフィック",
d_traffic_h:"パケット/秒、リアルタイム",d_txpps:"TX pps",d_rxpps:"RX pps",d_txbad:"TX 異常",d_rxbad:"RX 異常",
i_host:"ホスト名",i_ip:"IP アドレス",i_mask:"ネットマスク",i_gw:"ゲートウェイ",i_mac:"MAC",i_fw:"ファームウェア",
i_built:"ビルド日",i_hw:"ハードウェア",i_temp:"温度",i_flash:"フラッシュ",i_syslog:"Syslog",
p_state:"状態",p_disabled:"無効",p_up:"アップ",p_txgb:"TX 正常 / 異常",p_rxgb:"RX 正常 / 異常",
p_state:"状態",p_disabled:"無効",p_up:"アップ",
p_pkts:"pkts",p_module:"モジュール",p_temp:"温度",p_vcc:"電圧",p_txbias:"TX バイアス",p_txpower:"TX 電力",
p_rxpower:"RX 電力",p_txfault:"TX 障害",p_txdis:"TX 無効",p_rxlos:"RX 信号ロス",p_adv:"アドバタイズ",
pt_title:"ポート設定",pt_conn:"学習済みデバイス",pt_speed:"速度",pt_mtu:"MTU",
@@ -179,14 +183,12 @@ v_empty:"VLAN は設定されていません。",v_mgmt:"管理 VLAN",v_mgmt_non
v_optional:"任意",v_setmgmt:"管理 VLAN に設定",v_setmgmt_t:"この VLAN を管理 VLAN にする",
v_member:"メンバー",v_pvid:"PVID",
v_legend:"U = タグ無しメンバー、T = タグ付きメンバー、- = 非メンバー。PVID はタグ無し受信トラフィックをこの VLAN に割り当てます。",
v_ingress:"受信フィルタ",v_ingress_h:"書き込み専用: 現在の状態はスイッチから読み取れません",
v_accept:"受け入れ",v_ing_all:"すべて",v_ing_apply:"受信モードを適用",v_del_t:"VLAN を削除",
v_del_q:"VLAN {n} を削除しますか?",v_del_d:"ポートの PVID は再割り当てされるまで維持されます。",
v_vid_err:"VLAN ID (1〜4094) を入力してください",v_name_err:"名前は英字で始めてください (英字、数字、_)",
v_loaded:"VLAN {n} を読み込みました",v_notfound:"VLAN {n} は存在しません (新規作成は可能です)",
v_nomember:"メンバーポートを 1 つ以上選択してください (または VLAN を削除してください)",
v_ing_none:"1 つ以上のポートの受信モードを選択してください",v_vid_first:"先に VLAN ID を入力してください",
v_mgmt_q:"VLAN {n} を管理 VLAN に設定しますか?",
v_vid_first:"先に VLAN ID を入力してください",v_mgmt_q:"VLAN {n} を管理 VLAN に設定しますか?",
v_mgmt_d:"スイッチは自身のトラフィックにこの VLAN のタグを付けるようになります。接続中のポートがこの VLAN を通さない場合、このページにアクセスできなくなり、コンソールからしか元に戻せません。",
l2_title:"MAC アドレステーブル",l2_filter:"フィルタ...",l2_flush:"学習エントリを消去",l2_static:"静的",
l2_learned:"学習",l2_loading:"読み込み中...",l2_failed:"読み込み失敗",l2_entries:"エントリ",
@@ -260,7 +262,7 @@ d_system:"系统",d_traffic:"流量",
d_traffic_h:"包/秒,实时",d_txpps:"TX pps",d_rxpps:"RX pps",d_txbad:"TX 错误",d_rxbad:"RX 错误",
i_host:"主机名",i_ip:"IP 地址",i_mask:"子网掩码",i_gw:"网关",i_mac:"MAC",i_fw:"固件",
i_built:"构建日期",i_hw:"硬件",i_temp:"温度",i_flash:"Flash",i_syslog:"Syslog",
p_state:"状态",p_disabled:"已禁用",p_up:"已连接",p_txgb:"TX 正常 / 错误",p_rxgb:"RX 正常 / 错误",
p_state:"状态",p_disabled:"已禁用",p_up:"已连接",
p_pkts:"个包",p_module:"模块",p_temp:"温度",p_vcc:"供电电压",p_txbias:"TX 偏置电流",p_txpower:"TX 光功率",
p_rxpower:"RX 光功率",p_txfault:"TX 故障",p_txdis:"TX 禁用",p_rxlos:"RX 信号丢失",p_adv:"通告能力",
pt_title:"端口配置",pt_conn:"已学习设备",pt_speed:"速率",pt_mtu:"MTU",
@@ -292,14 +294,12 @@ v_empty:"尚未配置 VLAN。",v_mgmt:"管理 VLAN",v_mgmt_none:"untagged",v_edi
v_optional:"可选",v_setmgmt:"设为管理 VLAN",v_setmgmt_t:"将此 VLAN 设为管理 VLAN",
v_member:"成员",v_pvid:"PVID",
v_legend:"U = untagged 成员,T = tagged 成员,- = 非成员。PVID 将入方向的未标记流量归入此 VLAN。",
v_ingress:"入方向过滤",v_ingress_h:"只写: 当前状态无法从交换机读取",
v_accept:"接受",v_ing_all:"全部",v_ing_apply:"应用入方向模式",v_del_t:"删除 VLAN",
v_del_q:"删除 VLAN {n}?",v_del_d:"端口的 PVID 在重新分配前保持不变。",
v_vid_err:"请输入 VLAN ID (1-4094)",v_name_err:"名称必须以字母开头 (字母、数字、_)",
v_loaded:"已读取 VLAN {n}",v_notfound:"VLAN {n} 不存在 (仍可新建)",
v_nomember:"请至少选择一个成员端口 (或改为删除该 VLAN)",
v_ing_none:"请至少为一个端口选择入方向模式",v_vid_first:"请先输入 VLAN ID",
v_mgmt_q:"将 VLAN {n} 设为管理 VLAN?",
v_vid_first:"请先输入 VLAN ID",v_mgmt_q:"将 VLAN {n} 设为管理 VLAN?",
v_mgmt_d:"交换机将开始为自身流量打上该 VLAN 标签。如果您所连接的端口不属于该 VLAN,本页面将无法访问,且只能通过控制台撤销此设置。",
l2_title:"MAC 地址表",l2_filter:"筛选...",l2_flush:"清除学习条目",l2_static:"静态",
l2_learned:"动态学习",l2_loading:"加载中...",l2_failed:"加载失败",l2_entries:"条",
@@ -373,7 +373,7 @@ d_system:"Sistema",d_traffic:"Tráfico",
d_traffic_h:"paquetes/s, en vivo",d_txpps:"TX pps",d_rxpps:"RX pps",d_txbad:"TX erróneos",d_rxbad:"RX erróneos",
i_host:"Nombre de host",i_ip:"Dirección IP",i_mask:"Máscara de red",i_gw:"Puerta de enlace",i_mac:"MAC",i_fw:"Firmware",
i_built:"Compilado",i_hw:"Hardware",i_flash:"Flash",i_syslog:"Syslog",
p_state:"Estado",p_disabled:"deshabilitado",p_up:"activo",p_txgb:"TX correctos / erróneos",p_rxgb:"RX correctos / erróneos",
p_state:"Estado",p_disabled:"deshabilitado",p_up:"activo",
p_pkts:"paquetes",p_module:"Módulo",p_temp:"Temperatura",p_vcc:"Vcc",p_txbias:"Polarización TX",p_txpower:"Potencia TX",
p_rxpower:"Potencia RX",p_txfault:"Fallo TX",p_txdis:"TX deshabilitado",p_rxlos:"Pérdida RX (LOS)",p_adv:"Anunciado",
pt_title:"Configuración de puerto",pt_conn:"Dispositivos aprendidos",pt_speed:"Velocidad",pt_mtu:"MTU",
@@ -405,14 +405,12 @@ v_empty:"No hay VLANs configuradas.",v_mgmt:"VLAN de gestión",v_mgmt_none:"sin
v_optional:"opcional",v_setmgmt:"Establecer como VLAN de gestión",v_setmgmt_t:"Hacer esta la VLAN de gestión",
v_member:"Miembro",v_pvid:"PVID",
v_legend:"U = miembro sin etiquetar, T = miembro etiquetado, - = no miembro. PVID asigna esta VLAN al tráfico entrante sin etiquetar.",
v_ingress:"Filtrado de entrada",v_ingress_h:"solo escritura: el estado no se puede leer desde el switch",
v_accept:"Aceptar",v_ing_all:"Todos",v_ing_apply:"Aplicar modos de entrada",v_del_t:"Eliminar VLAN",
v_del_q:"¿Eliminar VLAN {n}?",v_del_d:"Los puertos conservan su PVID hasta que se reasignen.",
v_vid_err:"Introduce un ID de VLAN (1-4094)",v_name_err:"El nombre debe empezar por una letra (letras, dígitos, _)",
v_loaded:"VLAN {n} cargada",v_notfound:"VLAN {n} no encontrada (aún se puede crear una nueva)",
v_nomember:"Selecciona al menos un puerto miembro (o elimina la VLAN)",
v_ing_none:"Elige un modo de entrada para al menos un puerto",v_vid_first:"Introduce primero un ID de VLAN",
v_mgmt_q:"¿Establecer VLAN {n} como VLAN de gestión?",
v_vid_first:"Introduce primero un ID de VLAN",v_mgmt_q:"¿Establecer VLAN {n} como VLAN de gestión?",
v_mgmt_d:"El switch empezará a etiquetar su propio tráfico con esa VLAN. Si el puerto por el que te conectas no la transporta, esta página dejará de ser accesible y el ajuste solo podrá deshacerse por la consola.",
l2_title:"Tabla de direcciones MAC",l2_filter:"filtrar...",l2_flush:"Vaciar entradas aprendidas",l2_static:"estática",
l2_learned:"aprendida",l2_loading:"cargando...",l2_failed:"error de carga",l2_entries:"entradas",
@@ -467,12 +465,13 @@ fw_noreboot:"no se detectó reinicio: lo más probable es que la imagen fuera re
fw_applied:"actualización aplicada",fw_done_t:"Firmware actualizado",
fw_done:"El switch verificó la imagen y se reinició con ella. La sesión se restableció, así que se te pedirá que vuelvas a iniciar sesión.",
fw_login:"Ir al inicio de sesión",fw_rebooting:"el switch se está reiniciando...",
fw_timeout:"el switch no ha vuelto tras 150 s: comprueba la alimentación / consola serie"
fw_timeout:"el switch no ha vuelto tras 150 s: comprueba la alimentación / consola serie",
nav_isolate:"Aislamiento",nav_lldp:"LLDP",i_temp:"Temperatura",lldp_enabled:"LLDP activado",lldp_title:"Estado del servicio",lldp_portname:"Nombre",lldp_enable:"Enviar tramas",stp_pcs:"Coste de ruta",stp_pcs_long:"Largo (32 bits)",stp_pcs_short:"Corto (16 bits)",stp_bh:"BPDU con STP desactivado",stp_bh_flood:"Inundar",stp_bh_filter:"Filtrar",stp_r4:"Respaldo",stp_times:"edad máxima / retardo de reenvío",stp_proto:"Protocolo",stp_nolink:"sin enlace",stp_lasttc:"último",stp_mcheck:"Comprobar",stp_mcheck_t:"Reenviar BPDUs RST para saber si el vecino sigue necesitando 802.1D",stp_cnt:"Contadores",stp_crx:"BPDUs recibidas",stp_ctx:"BPDUs enviadas",stp_ctrx:"TC recibidas",stp_cttx:"TC enviadas",stp_cage:"Última BPDU [s]",v_setmgmt_q:"¿Fijar {n} como ID de gestión?",v_add:"Añadir una VLAN",v_mgmt_c:"AVISO: debes poder acceder a esta VLAN para seguir gestionando el switch.",v_rename:"Renombrar",v_rename_t:"Renombrar VLAN",v_mgmt_tt:"La VLAN de gestión no se puede borrar, primero hay que asignar la gestión a otra VLAN.",iso_title:"Aislamiento de puertos",iso_from:"Desde el puerto",iso_to:"Hacia el puerto",iso_all:"Permitir todo",iso_intro:"Una trama que entra por un puerto solo puede salir por los puertos marcados en su fila. El aislamiento se decide antes del reenvío, así que un puerto sin marcar es inalcanzable de forma directa, diga lo que diga la tabla de direcciones.",iso_legend:"La celda del propio puerto siempre está permitida. Un número de puerto en rojo indica un puerto que no está del todo abierto.",lacp_heading:"LACP (802.3ad)",lag_mode:"Modo",lag_static:"Estático",lacp_neg:"negociando",lacp_agg:"agregador",lacp_members:"miembros activos",lacp_hint:"LACP: elige los puertos candidatos y pulsa Aplicar para iniciar la negociación",lacp_actor:"Local",lacp_partner:"Remoto",lacp_rxstate:"Estado RX",lacp_rx:"LACPDUs recibidas",lacp_psys:"Sistema remoto",sc_title:"Control de tormentas",sc_h:"por puerto y tipo de tráfico, vacío = desactivado",sc_bcast:"Difusión",sc_mcast:"Multidifusión",sc_ucast:"Unidifusión desconocida",sc_umcast:"Multidifusión desconocida",sc_err:"El límite de tormenta debe ser 1-1048575 pps o 1-10000000 kbit/s",sc_note:"Las tramas de un tipo por encima de su límite se descartan a la entrada del switch.",sy_dns1:"Servidor DNS",sy_dns2:"DNS secundario",sy_dns_err:"IP del servidor DNS no válida",sy_dns_dhcp:"desde DHCP: {ip}",sy_time:"Hora",sy_ntp:"NTP",sy_ntpsrv:"Servidor NTP",sy_ntpsrv_p:"nombre de host o dirección IPv4",sy_ntpint:"Intervalo de consulta [min]",sy_tz:"Zona horaria",sy_dst:"Horario de verano",sy_dst_eu:"Europa",sy_dst_us:"Estados Unidos",sy_now:"Hora local",sy_sync:"Última sincronización",sy_ntpaddr:"Dirección del servidor",sy_nosync:"sin sincronizar",sy_waiting:"esperando al servidor",sy_ago:"hace {n} min",sy_ntp_err:"Introduce el nombre de host o la dirección IPv4 del servidor NTP",sy_ntpint_err:"El intervalo de consulta debe ser 1-1440",sy_time_note:"El switch no tiene reloj con batería: la hora se pierde en cada arranque y se vuelve a fijar desde el servidor NTP cuando la red está lista. Guarda en flash para conservar los ajustes.",cw_longline:"La línea {n} mide {m} bytes: el switch solo puede ejecutar líneas de hasta 126 bytes y la saltaría al arrancar, así que se rechaza la escritura. Acórtala primero.",fw_other:"Esta imagen no se compiló para este switch ({m}): los pines de SFP, LEDs y botones pueden no coincidir. Súbela solo si estás seguro."
},
fr:{
nav_dash:"Tableau de bord",nav_ports:"Ports",nav_stp:"Arborescence",nav_stats:"Statistiques",
nav_vlan:"LANs virtuels",nav_l2:"Table MAC",nav_mirror:"Mise en mirroir",nav_lag:"Aggrégation",nav_eee:"Energie",
nav_bw:"Bande passante",nav_system:"Système",nav_fw:"Micro logiciel",
nav_bw:"Bande passante",nav_system:"Système",nav_fw:"Micro logiciel",nav_lldp:"Couche de Liaison",
hdr_dirty:"Changements non sauvegardés",hdr_dirty_t:"La configuration utilisée diffère de celle au démarrage",
hdr_save:"Sauvegarder en flash",hdr_save_t:"Maintenir la configuration actuelle en flash",
th_auto:"Système",th_auto_sel:"Système (sélénisé)",sy_display:"Affichage",sy_theme:"Thème",sy_display_note:"Enregistré seulement dans ce navigateur.",th_light:"Clair",th_dark:"Sombre",
@@ -485,7 +484,7 @@ d_system:"Système",d_traffic:"Trafic",
d_traffic_h:"paquets/s (en direct)",d_txpps:"TX pps",d_rxpps:"RX pps",d_txbad:"TX KO",d_rxbad:"RX KO",
i_host:"Nom d'hote",i_ip:"Adresse IP",i_mask:"Masque réseau",i_gw:"Passerelle",i_mac:"Adresse MAC",i_fw:"Micro logiciel",
i_built:"Compilé le",i_hw:"Matériel",i_temp:"Température",i_flash:"Taille Flash",i_syslog:"Journal système",
p_state:"Etat",p_disabled:"Inactif",p_up:"Actif",p_txgb:"TX bon / mauvais",p_rxgb:"RX bon / mauvais",
p_state:"Etat",p_disabled:"Inactif",p_up:"Actif",
p_pkts:"paquets",p_module:"Module",p_temp:"Température",p_vcc:"Vcc",p_txbias:"TX biais",p_txpower:"TX puissance",
p_rxpower:"RX puissance",p_txfault:"TX erreur",p_txdis:"TX désactivé",p_rxlos:"RX pertes de signaux",p_adv:"Capacités",
pt_title:"Configuration du port",pt_conn:"Appareils appris",pt_speed:"Vitesse",pt_mtu:"MTU",
@@ -509,6 +508,7 @@ stp_en_q:"Activer l'arbrescence?",
stp_en_d:"Les ports sont d'abord bloqués et cela prend deux fois le délai de retransmission pour atteindre l'état de retransmission; les ports en bordures recouvrent immédiatement leur état.",
stp_dis_q:"Désactiver l'arborescence ?",stp_dis_d:"Tous les ports vont être retransmis; la protection de boucles est perdue.",
stp_cost_err:"Le coût du chemin doit être compris entre 0 et 200000000",
lldp_enabled: "Activer LLDP",lldp_title:"Protocole de découverte de la couche de liason (LLDP)",lldp_portname:"Nom",lldp_enable:"Envoi de trames",
st_title:"Statiqtique du port",st_h:"cumul depuis le démarrage",st_txg:"TX OK",st_txb:"TX KO",st_rxg:"RX OK",
st_rxb:"RX KO",st_details:"Détails",st_counters:"Compteur MIB",st_nonzero:"non-zero seulement",
st_autoref:"rafraichissement automatique",st_counter:"Compteur",st_value:"Valeur",st_fail:"Erreur au chargement des compteurs",
@@ -517,14 +517,12 @@ v_empty:"Aucun VLANs configuré.",v_mgmt:"VLAN de management",v_mgmt_none:"déta
v_optional:"optionel",v_setmgmt:"Mette en VLAN de management",v_setmgmt_t:"Mettre celui-ci en VLAN de management",
v_member:"Membre",v_pvid:"PVID",
v_legend:"U = Membre détagué (UnTagged), T = membre tagué (Tagged), - = non membre. PVID (Port VLan ID) assigne ce VLAN au trafic entrant non tagué.",
v_ingress:"Filtrage entrant",v_ingress_h:"en ériture seule: cet état n'est pas lisible depuis le communtateur réseau",
v_accept:"Accepté",v_ing_all:"Tous",v_ing_apply:"Appliquer les filtes",v_del_t:"Supprimer le VLAN",
v_del_q:"Supprimer le VLAN {n}?",v_del_d:"Les ports conservent leur PVID tant qu'ils ne sont pas réassignés.",
v_vid_err:"Entrez un identifiant VLAN (1-4094)",v_name_err:"Le nom doit commencer par une lettre (lettre, chiffres, _)",
v_loaded:"VLAN chargés {n}",v_notfound:"VLAN {n} non trouvé (un nouveau peut toutefois être créé)",
v_nomember:"Selectionnez au moins un port membre (ou supprimez le VLAN)",
v_ing_none:"Sélectionnez le mode entrant pour au moins un port",v_vid_first:"Entrez un identifiant VLAN d'abord",
v_mgmt_q:"Mettre ce VLAN {n} en tant que VLAN de management?",
v_vid_first:"Entrez un identifiant VLAN d'abord",v_mgmt_q:"Mettre ce VLAN {n} en tant que VLAN de management?",
v_mgmt_d:"Ce commutateur commencera par taguer son propre trafic avec ce VLAN. Si le port avec lequel vous êtes connecté ne le permet pas, cette page deviendra inaccessible et les paramètres ne pourront plus être changés que via la console.",
l2_title:"Table des adresses MAC",l2_filter:"filtre...",l2_flush:"Effacer les entrées apprises",l2_static:"static",
l2_learned:"appris",l2_loading:"chargement...",l2_failed:"chargement non réussi",l2_entries:"entrées",
@@ -542,6 +540,9 @@ e_idle:"Inactif",e_na:"N/A",
bw_title:"Limitations de bande passante",bw_h:"Mbit/s, 0.016-10000",bw_in:"Limite entrante",bw_out:"Limite sortante",
bw_exceed:"Lorsque dépassé",bw_fc:"Limiter",bw_drop:"Rejetter",
bw_in_err:"La limite entrante doit être comprise entre 0.016 et 10000 Mbit/s",bw_out_err:"La limite sortante doit être comprise entre 0.016 et 10000 Mbit/s",
sc_title:"Contrôle des tempêtes",sc_h:"par port et par type de diffusion (générale ou groupée), vide = inactif",sc_bcast:"Générale (Broadcast)",sc_mcast:"Groupée (Multicast)",
sc_ucast:"Unicast inconnu",sc_umcast:"Multicast inconnu",sc_err:" La limite de tempête doit être comprise entre 1 à 1048575 pps ou 1 à 10000000 kbit/s",
sc_note:"Les trames de chacun des types ci-dessus dépassant les limites sont abandonnées lorsqu'elles arrivent sur le communtateur.",
sy_network:"Réseau",sy_dhcp:"Uiliser DHCP",sy_dhcp_t:"Aquérir l'adresse via le protocole DHCP",sy_services:"Services",
sy_igmp:"Surveillance IGMP",sy_sysip:"IP du serveur...",sy_server:"Serveur",sy_port:"Port",
sy_sesstmo:"Temps mort de session (s)",sy_sesstmo_err:"Le temps morts de session doit être compris en 1-65535 s",
@@ -555,7 +556,14 @@ sy_ip_err:"Erreur d'IP / masque réseau / passerelle",sy_host_err:"Nom d'hôte :
sy_net_q:"Appliquer les paramètres réseau?",sy_net_d:"L'adresse IP de management va devenir {ip}: merci de réouvrir le page depuis cette adresse.",
sy_ip_changed:"L'adresse IP a changée, reconnectez-vous à http://{ip}/",sy_dhcp_q:"Passer en DHCP?",
sy_dhcp_d:"Le commutateur va demander une addresse via le DHCP. Vous devrez rechercher sa prochaine adresse IP pour vous reconnecter.",
sy_sysip_err:"Adresse IP du serveur Syslog invalide",sy_sysport_err:"Le port Syslog doit être compris entre 1 et 65535",
sy_sysip_err:"Adresse IP du serveur Syslog invalide",sy_dns1:"DNS principal",sy_dns2:"DNS secondaire",sy_dns_err:"Adresse IP du DNS invalide",sy_dns_dhcp:"émis par le DHCP: {ip}",
sy_time:"Temps",sy_ntp:"NTP",sy_ntpsrv:"Serveur NTP",sy_ntpsrv_p:"Nom d'hôte ou adresse IPv4",sy_ntpint:"intervalle de scrutation [min]",
sy_tz:"Fuseau horaire",sy_dst:"Heure d'été",sy_dst_eu:"Europe",
sy_dst_us:"Etats-Unis",sy_now:"Heure locale",
sy_sync:"Dernière synchronisation",sy_ntpaddr:"Adresse du serveur",sy_nosync:"Désynchronisé",sy_waiting:"En attente du serveur",
sy_ago:"depuis {n} minutes",sy_ntp_err:"Entrez le nom d'hôte ou l'adresse IPv4 du serveur NTP",sy_ntpint_err:"L'intervalle de scrutation doit être compris entre 1 et 1440",
sy_time_note:"Le commutateur n'a pas d'horloge sauvegardée par batterie: L'heure est donc perdue à chaque redémarrage et doit être récupérée grâce au serveur NTP une fois le réseau fonctionnel. Sauvegardez en mémoire flash pour enregistrer les paramètres.",
sy_sysport_err:"Le port Syslog doit être compris entre 1 et 65535",
sy_pw_len:"Mot de passe : 1 à 20 caractères",sy_pw_space:"Le mot de passe ne doit pas contenir d'espaces",sy_pw_match:"Les mots de passe ne correspondent pas",
sy_pw_q:"Changer le mot de passe d'administration?",sy_pw_d:"Cela prend effet immédiatement dès la prochaine connection. Enregistrez en Flash pour rendre la modification persistante.",
sy_reboot_q:"Redémarrer le commutateur ?",sy_reboot_d:"Il y a des paramètres NON SAUVEGARDES qui vont être perdus. Sauvegardez-les au préalable en mémoire flash si vous souhaitez les conserver.",
@@ -602,7 +610,7 @@ d_system:"System",d_traffic:"Datenverkehr",
d_traffic_h:"Pakete/s, live",d_txpps:"TX Pakete/s",d_rxpps:"RX Pakete/s",d_txbad:"TX fehlerhaft",d_rxbad:"RX fehlerhaft",
i_host:"Hostname",i_ip:"IP-Adresse",i_mask:"Netzmaske",i_gw:"Gateway",i_mac:"MAC",i_fw:"Firmware",
i_built:"Erstellt",i_hw:"Hardware",i_temp:"Temperatur",i_flash:"Flash",i_syslog:"Syslog",
p_state:"Zustand",p_disabled:"deaktiviert",p_up:"verbunden",p_txgb:"TX gut / fehlerhaft",p_rxgb:"RX gut / fehlerhaft",
p_state:"Zustand",p_disabled:"deaktiviert",p_up:"verbunden",
p_pkts:"Pakete",p_module:"Modul",p_temp:"Temperatur",p_vcc:"Vcc",p_txbias:"TX-Bias",p_txpower:"Sendeleistung",
p_rxpower:"Empfangsleistung",p_txfault:"TX-Fehler",p_txdis:"TX deaktiviert",p_rxlos:"RX LOS",p_adv:"Angeboten",
pt_title:"Port-Konfiguration",pt_conn:"Gelernte Geräte",pt_speed:"Geschwindigkeit",pt_mtu:"MTU",
@@ -633,17 +641,18 @@ st_autoref:"automatisch aktualisieren",st_counter:"Zähler",st_value:"Wert",st_f
v_title:"VLANs",v_vid:"VID",v_members:"Mitglieder",v_tagged:"Tagged",v_untagged:"Untagged",v_pvid_on:"PVID auf",
v_empty:"Keine VLANs konfiguriert.",v_mgmt:"Management-VLAN",v_mgmt_none:"untagged",v_editor:"VLAN-Editor",
v_optional:"optional",v_setmgmt:"Als Management-VLAN",v_setmgmt_t:"Dieses VLAN zum Management-VLAN machen",
v_setmgmt_q:"{n} als Management-VLAN setzen?",v_add:"Neues VLAN hinzufügen",
v_mgmt_c:"WARNUNG: Du musst dieses VLAN erreichen können, um den Switch weiter verwalten zu können.",
v_member:"Mitglied",v_pvid:"PVID",
v_legend:"U = Mitglied untagged, T = Mitglied tagged, - = kein Mitglied. PVID ordnet eingehenden Verkehr ohne Tag diesem VLAN zu.",
v_ingress:"Ingress-Filter",v_ingress_h:"nur schreibbar: der Zustand lässt sich vom Switch nicht auslesen",
v_accept:"Annehmen",v_ing_all:"Alle",v_ing_apply:"Ingress-Modi übernehmen",v_del_t:"VLAN löschen",
v_accept:"Annehmen",v_ing_all:"Alle",v_ing_apply:"Ingress-Modi übernehmen",v_del_t:"VLAN löschen",v_rename:"Umbenennen",v_rename_t:"VLAN umbenennen",
v_del_q:"VLAN {n} löschen?",v_del_d:"Die Ports behalten ihre PVID, bis sie neu zugewiesen wird.",
v_vid_err:"VLAN-ID eingeben (1-4094)",v_name_err:"Der Name muss mit einem Buchstaben beginnen (Buchstaben, Ziffern, _)",
v_loaded:"VLAN {n} geladen",v_notfound:"VLAN {n} nicht gefunden (es kann neu angelegt werden)",
v_nomember:"Mindestens einen Mitglieds-Port wählen (oder das VLAN stattdessen löschen)",
v_ing_none:"Für mindestens einen Port einen Ingress-Modus wählen",v_vid_first:"Zuerst eine VLAN-ID eingeben",
v_mgmt_q:"VLAN {n} als Management-VLAN setzen?",
v_vid_first:"Zuerst eine VLAN-ID eingeben",v_mgmt_q:"VLAN {n} als Management-VLAN setzen?",
v_mgmt_d:"Der Switch versieht dann seinen eigenen Verkehr mit diesem VLAN-Tag. Führt der Port, über den du verbunden bist, dieses VLAN nicht, ist diese Seite nicht mehr erreichbar und die Einstellung lässt sich nur noch über die serielle Konsole zurücknehmen.",
v_mgmt_tt:"Das Management-VLAN lässt sich nicht löschen. Lege zuerst ein anderes VLAN als Management-VLAN fest.",
l2_title:"MAC-Adresstabelle",l2_filter:"filtern...",l2_flush:"Gelernte Einträge löschen",l2_static:"statisch",
l2_learned:"gelernt",l2_loading:"lädt...",l2_failed:"Laden fehlgeschlagen",l2_entries:"Einträge",
l2_del_t:"Eintrag löschen",l2_flush_q:"Alle gelernten MAC-Einträge löschen?",
@@ -800,34 +809,51 @@ function getJSON(p){return api(p).then(function(r){if(!r.ok)throw new Error(p+"
function getText(p){return api(p).then(function(r){if(!r.ok)throw new Error(p+" "+r.status);return r.body})}
var CONF_CMDS=[
/^ip\s+(\d{1,3}\.){3}\d{1,3}$/,/^ip\s+dhcp$/,
/^gw\s+(\d{1,3}\.){3}\d{1,3}$/,/^netmask\s+(\d{1,3}\.){3}\d{1,3}$/,
/^syslog\s+(on|off)$/,/^syslog\s+ip\s+(\d{1,3}\.){3}\d{1,3}$/,/^syslog\s+port\s+\d{1,5}$/,
/^ip\s+(\d{1,3}\.){3}\d{1,3}$/,
/^ip\s+dhcp$/,
/^gw\s+(\d{1,3}\.){3}\d{1,3}$/,
/^netmask\s+(\d{1,3}\.){3}\d{1,3}$/,
/^syslog\s+(on|off)$/,
/^syslog\s+ip\s+(\d{1,3}\.){3}\d{1,3}$/,
/^syslog\s+port\s+\d{1,5}$/,
/^session\s+\d{1,5}$/,
/^dns\s+server\s+(\d{1,3}\.){3}\d{1,3}(\s+(\d{1,3}\.){3}\d{1,3})?$/,
/^ntp\s+(on|off)$/,/^ntp\s+server\s+[A-Za-z0-9.-]{1,47}$/,/^ntp\s+interval\s+\d{1,4}$/,
/^ntp\s+timezone\s+[+-]?\d{1,2}(:\d{2})?$/,/^ntp\s+dst\s+(off|eu|us)$/,
/^ntp\s+(on|off)$/,
/^ntp\s+server\s+[A-Za-z0-9.-]{1,47}$/,
/^ntp\s+interval\s+\d{1,4}$/,
/^ntp\s+timezone\s+[+-]?\d{1,2}(:\d{2})?$/,
/^ntp\s+dst\s+(off|eu|us)$/,
/^passwd\s+\S+$/,/^hostname\s+\S{1,23}$/,
/^vlan\s+\d{1,4}\s+d$/,/^vlan\s+\d{1,4}\s+mgmt$/,
/^vlan\s+\d{1,4}(\s+[a-zA-Z]\w*)?(\s+\d{1,2}t?)+$/,
/^pvid\s+\d{1,2}\s+\d{1,4}$/,
/^ingress(\s+\d{1,2}[tua])+$/,/^ingress\s+[tua]$/,
/^port\s+\d{1,2}\s+(10m|100m|1g|2g5|5g|10g|auto|on|off)(\s+(half|full))?$/,
/^port\s+\d{1,2}\s+name\s+\S+$/,
/^port\s+\d{1,2}\s+lldp\s+(permit|block)+$/,
/^eee\s+(on|off)(\s+\d{1,2})?(\s+(100m|1g|2g5))?$/,
/^mirror(\s+\d{1,2})(\s+\d{1,2}[tr]?)+$/,/^mirror\s+off$/,
/^lag\s+[1-4](\s+\d{1,2})+$/,/^lag\s+[1-4]\s+d$/,/^lag\s+[1-4]\s+lacp(\s+\d{1,2})+$/,/^lag\s+[1-4]\s+lacp\s+off$/,/^laghash\s+[1-4](\s+\w+)+$/,
/^isolate\s+\d{1,2}(\s+(off|\d{1,2}))+$/,
/^stp\s+(on|off)$/,/^stp\s+(prio|hello|maxage|fwd|txhold)\s+\d{1,2}$/,
/^vlan\s+\d+\s+(d|mgmt)$/m,
/^vlan\s+\d+(\s+[a-zA-Z]\w*)?(\s+\d+t?)*$/m,
/^pvid\s+\d+\s+\d+$/m,
/^ingress(\s+\d+[tua])+$/,/^ingress\s+[tua]$/,
/^port\s+\d+\s+(10m|100m|1g|2g5|5g|10g|auto|on|off)(\s+(half|full))?$/,
/^port\s+\d+\s+name\s+\S+$/,
/^port\s+\d+\s+lldp\s+(permit|block)+$/,
/^eee\s+(on|off)(\s+\d+)?(\s+(100m|1g|2g5))?$/,
/^mirror(\s+\d+)(\s+\d+[tr]?)+$/,/^mirror\s+off$/,
/^lag\s+[1-4](\s+\d+)+$/,/^lag\s+[1-4]\s+d$/,
/^lag\s+[1-4]\s+lacp(\s+\d+)+$/,
/^lag\s+[1-4]\s+lacp\s+off$/,
/^laghash\s+[1-4](\s+\w+)+$/,
/^isolate\s+\d+(\s+(off|\d+))+$/,
/^stp\s+(on|off)$/,/^stp\s+(prio|hello|maxage|fwd|txhold)\s+\d+$/,
/^stp\s+version\s+(rstp|stp)$/,
/^stp\s+(port\s+\d{1,2}|lag\s+[1-4])\s+(on|off)$/,/^stp\s+(port\s+\d{1,2}|lag\s+[1-4])\s+edge\s+(on|off|auto)$/,
/^stp\s+(port\s+\d{1,2}|lag\s+[1-4])\s+cost\s+\d{1,9}$/,/^stp\s+(port\s+\d{1,2}|lag\s+[1-4])\s+prio\s+\d{1,3}$/,
/^stp\s+(port\s+\d{1,2}|lag\s+[1-4])\s+guard\s+(none|bpdu|root)$/,/^stp\s+(port\s+\d{1,2}|lag\s+[1-4])\s+filter\s+(on|off)$/,
/^stp\s+(port\s+\d{1,2}|lag\s+[1-4])\s+p2p\s+(auto|on|off)$/,
/^igmp\s+(on|off)$/,/^mtu\s+\d{1,2}\s+\d+$/,/^lldp\s+(on|off)$/,
/^bw\s+(in|out)\s+\d{1,2}\s+\S+$/,
/^storm\s+\d{1,2}\s+(bcast|mcast|ucast|umcast)\s+(off|\d{1,8}\s+(pps|kbps))$/,
/^stp\s+pathcost\s+(long|short)$/,
/^stp\s+bpdu\s+(filter|flood)$/,
/^stp\s+(port\s+\d+|lag\s+[1-4])\s+(on|off)$/,
/^stp\s+(port\s+\d+|lag\s+[1-4])\s+edge\s+(on|off|auto)$/,
/^stp\s+(port\s+\d+|lag\s+[1-4])\s+cost\s+\d+$/,
/^stp\s+(port\s+\d+|lag\s+[1-4])\s+prio\s+\d+$/,
/^stp\s+(port\s+\d+|lag\s+[1-4])\s+guard\s+(none|bpdu|root)$/,
/^stp\s+(port\s+\d+|lag\s+[1-4])\s+filter\s+(on|off)$/,
/^stp\s+(port\s+\d=|lag\s+[1-4])\s+p2p\s+(auto|on|off)$/,
/^igmp\s+(on|off)$/,
/^mtu\s+\d+\s+\d+$/,
/^lldp\s+(on|off)$/,
/^bw\s+(in|out)\s+\d+\s+\S+$/,
/^storm\s+\d+\s+(bcast|mcast|ucast|umcast)\s+(off|\d+\s+(pps|kbps))$/,
];
function isConfCmd(line){
for(var i=0;i<CONF_CMDS.length;i++)if(CONF_CMDS[i].test(line))return true;
@@ -836,6 +862,7 @@ function isConfCmd(line){
function setDirty(d){
S.dirty=d;
$("dirty").classList.toggle("show",d);
$("saveBtn").classList.toggle("pri",d);
}
function postCmd(cmd,quiet){
return api("/cmd",{method:"POST",body:cmd}).then(function(r){
@@ -850,7 +877,6 @@ function postCmds(list){
list.forEach(function(c){p=p.then(function(){return postCmd(c,true)})});
return p.then(function(){toast(t("t_cmds",{n:list.length}),"ok")});
}
function toast(msg,cls){
var el=h("div",{class:"toast "+(cls||""),text:msg});
$("toasts").appendChild(el);
@@ -936,6 +962,7 @@ function byPort(a){return a.sort(function(x,y){return x.portNum-y.portNum})}
function pollStatus(){
return getJSON("/status.json").then(function(s){
byPort(s);
S.ports=s;
var now=Date.now();
if(!S.n){
S.n=s.length;
@@ -957,7 +984,7 @@ function pollStatus(){
};
});
}
S.prev=s;S.prevT=now;S.ports=s;
S.prev=s;S.prevT=now;
updateStrip();
var hk=tabHooks[curTab];
if(hk&&hk.status)hk.status();
@@ -1024,8 +1051,10 @@ function portRows(p){
var rows=[[t("c_port"),String(p.portNum)],[t("c_type"),p.isSFP?"SFP":"RJ45"]];
if(p.name)rows.push([t("c_name"),p.name]);
rows.push([t("p_state"),!p.enabled?t("p_disabled"):(p.link>0?t("p_up")+" "+LINKS[p.link]:t("c_down"))]);
rows.push([t("p_txgb"),BigInt(p.txG)+" / "+BigInt(p.txB)+" "+t("p_pkts")]);
rows.push([t("p_rxgb"),BigInt(p.rxG)+" / "+BigInt(p.rxB)+" "+t("p_pkts")]);
rows.push([t("st_txg"),BigInt(p.txG)+" "+t("p_pkts")]);
rows.push([t("st_txb"),BigInt(p.txB)+" "+t("p_pkts")]);
rows.push([t("st_rxg"),BigInt(p.rxG)+" "+t("p_pkts")]);
rows.push([t("st_rxb"),BigInt(p.rxB)+" "+t("p_pkts")]);
if(p.isSFP){
if(p.sfp_vendor)rows.push([t("p_module"),[p.sfp_vendor,p.sfp_model,p.sfp_serial].filter(Boolean).join(" / ")]);
var ext=p.sfp_options&0x40;
@@ -1202,12 +1231,14 @@ function fmtBridge(hex){
if(!hex||hex.length<16)return"-";
return parseInt(hex.slice(0,4),16)+" / "+hex.slice(4).replace(/(..)(?=.)/g,"$1:");
}
function stpEnt(pt){pt.lag=pt.p>100?pt.p-100:0}
function stpKey(pt){return pt.lag?"L"+pt.lag:String(pt.p)}
function stpName(pt){return pt.lag?"LAG"+pt.lag+" ("+maskToPorts(pt.mbr).join(",")+")":String(pt.p)}
function stpName(pt){return pt.lag?"LAG"+pt.lag:String(pt.p)}
function stpDhms(x){return Math.floor(x/86400)+"d "+Math.floor(x/3600)%24+"h "+Math.floor(x/60)%60+"m "+x%60+"s"}
function stpPre(k){return k.charAt(0)==="L"?"stp lag "+k.slice(1)+" ":"stp port "+k+" "}
function stpBuild(ports){
var tb=$("stpcfg").tBodies[0],st=$("stpstat").tBodies[0];
tb.innerHTML="";st.innerHTML="";
var tb=$("stpcfg").tBodies[0],st=$("stpstat").tBodies[0],ct=$("stpcnt").tBodies[0];
tb.innerHTML="";st.innerHTML="";ct.innerHTML="";
ports.forEach(function(pt){
var p=stpKey(pt);
var tr=tb.insertRow();
@@ -1225,7 +1256,12 @@ function stpBuild(ports){
tr.insertCell().appendChild(h("button",{class:"ctl",text:t("c_apply"),onclick:function(){stpApplyPort(p)}}));
var sr=st.insertRow();
sr.insertCell().textContent=stpName(pt);
["ro","st","db","dp","dc","oe","op"].forEach(function(k){sr.insertCell().id="st"+k+p});
["ro","st","db","dp","dc","oe","op","pv"].forEach(function(k){sr.insertCell().id="st"+k+p});
sr.insertCell().appendChild(h("button",{class:"ctl",id:"stmc"+p,text:t("stp_mcheck"),title:t("stp_mcheck_t"),
onclick:function(){postCmd(stpPre(p)+"mcheck").then(stpLoad).catch(function(){})}}));
var cr=ct.insertRow();
cr.insertCell().textContent=stpName(pt);
["rx","tx","trx","ttx","age"].forEach(function(k){cr.insertCell().id="sc"+k+p});
});
}
function stpPortVals(pt){
@@ -1239,10 +1275,21 @@ function stpPortVals(pt){
p2p:["auto","on","off"][pt.p2]||"auto",
};
}
function stpCnt(c){
c.ports.forEach(function(pt){
stpEnt(pt);
var p=stpKey(pt);
if(!$("scrx"+p))return;
var v=[0,1,2,3].map(function(k){return parseInt(pt.c.slice(8*k,8*k+8),16)}),age=parseInt(pt.c.slice(32,36),16);
["rx","tx","trx","ttx"].forEach(function(k,i){$("sc"+k+p).textContent=v[i]});
$("scage"+p).textContent=!v[0]?"-":(age===0xffff?">":"")+Math.floor(age/c.hz);
});
}
function stpLoad(){
return getJSON("/stp.json").then(function(s){
s.ports.sort(function(a,b){return((a.lag||0)-(b.lag||0))||(a.p-b.p)});
var sig=s.ports.map(function(pt){return stpKey(pt)+":"+(pt.mbr||0)}).join();
s.ports.forEach(stpEnt);
s.ports.sort(function(a,b){return(a.lag-b.lag)||(a.p-b.p)});
var sig=s.ports.map(stpKey).join();
if(sig!==stpSig){stpSig=sig;stpBuild(s.ports);}
stpCur=s;
var en=$("stpen");
@@ -1250,16 +1297,19 @@ function stpLoad(){
var msg;
if(!s.on)msg=t("stp_off_msg");
else{
var me=(s.prio*4096).toString(16).padStart(4,"0")+s.myMac;
var me=(s.prio*4096).toString(16).padStart(4,"0")+s.myMac,tc=parseInt(s.tc,16),rp="-";
s.ports.forEach(function(pt){if(pt.role===1)rp=stpName(pt)});
msg=t("stp_bridge")+" "+fmtBridge(me)+(s.weRoot
?" ("+t("stp_root_self")+")"
:"; "+t("stp_root")+" "+fmtBridge(s.rootPrio+s.rootMac)+" "+t("stp_via")+" "+s.rootPort+", "+t("stp_cost")+" "+parseInt(s.cost,16))
+"; "+t("stp_tc")+": "+parseInt(s.tc,16);
:"; "+t("stp_root")+" "+fmtBridge(s.rootPrio+s.rootMac)+" "+t("stp_via")+" "+rp+", "+t("stp_cost")+" "+parseInt(s.cost,16))
+"; "+t("stp_tc")+": "+tc+(tc?" ("+t("stp_lasttc")+" "+stpDhms(parseInt(s.tcs,16))+")":"")
+"; "+t("stp_times")+" "+s.rMaxage+" / "+s.rFwd+" s";
}
$("stpids").textContent=msg;
var bc=$("stpbridge");
if(!bc.dataset.dirty){
$("stpver").value=s.rstp?"rstp":"stp";
$("stppcs").value=s.pcs?"short":"long";$("stpbh").value=s.bh?"filter":"flood";
$("stpprio").value=String(s.prio);
$("stphello").value=s.hello;$("stpmaxage").value=s.maxage;
$("stpfwd").value=s.fwd;$("stptxhold").value=s.txhold;
@@ -1268,18 +1318,21 @@ function stpLoad(){
var p=stpKey(pt);
var trip=(pt.f&STP_PF.TRIP)?" "+badge(t("stp_trip"),"bad"):"";
$("stro"+p).textContent=s.on&&pt.role?t("stp_r"+pt.role):"-";
$("stst"+p).innerHTML=s.on?badge(t("stp_s"+pt.st),pt.st===3?"ok":"")+trip:"-";
$("stst"+p).innerHTML=s.on?(pt.lk?badge(t("stp_s"+pt.st),pt.st===3?"ok":""):badge(t("stp_nolink")))+trip:"-";
$("stdb"+p).textContent=s.on?fmtBridge(pt.db):"-";
$("stdp"+p).textContent=s.on?parseInt(pt.dp.slice(0,2),16)+"."+parseInt(pt.dp.slice(2),16):"-";
$("stdc"+p).textContent=s.on?parseInt(pt.dc,16):"-";
$("stoe"+p).textContent=s.on?t((pt.f&STP_PF.OPEREDGE)?"c_yes":"c_no"):"-";
$("stop"+p).textContent=s.on?t(pt.p2===2?"c_no":"c_yes"):"-";
$("stpv"+p).textContent=s.on&&pt.role?(s.rstp&&!pt.lg?"RSTP":"STP"):"-";
$("stmc"+p).disabled=!(s.on&&pt.role&&s.rstp);
var row=$("sten"+p).closest("tr");
if(row.dataset.dirty)return;
var v=stpPortVals(pt);
$("sten"+p).checked=v.en;$("sted"+p).value=v.edge;$("stco"+p).value=v.cost;
$("stpr"+p).value=v.prio;$("stgu"+p).value=v.guard;$("stfi"+p).value=v.filter;$("stpp"+p).value=v.p2p;
});
if($("stpcntd").open)return getJSON("/stpcnt.json").then(stpCnt);
}).catch(function(){});
}
function stpApplyPort(p){
@@ -1307,6 +1360,8 @@ $("stpbapply").addEventListener("click",function(){
var ver=$("stpver").value,prio=$("stpprio").value;
if(ver!==(s.rstp?"rstp":"stp"))cmds.push("stp version "+ver);
if(prio!==String(s.prio))cmds.push("stp prio "+prio);
if($("stppcs").value!==(s.pcs?"short":"long"))cmds.push("stp pathcost "+$("stppcs").value);
if($("stpbh").value!==(s.bh?"filter":"flood"))cmds.push("stp bpdu "+$("stpbh").value);
[["stphello","hello"],["stpmaxage","maxage"],["stpfwd","fwd"],["stptxhold","txhold"]].forEach(function(f){
var v=$(f[0]).value;
if(String(v)!==String(s[f[1]]))cmds.push("stp "+f[1]+" "+v);
@@ -1325,6 +1380,7 @@ $("stpen").addEventListener("change",function(){
var sel=$("stpprio");
for(var i=0;i<16;i++)sel.appendChild(h("option",{value:String(i),text:i+" ("+(i*4096)+")"}));
})();
$("stpcntd").addEventListener("toggle",function(){if(this.open)stpLoad()});
var stpPoller=new Poller(stpLoad,3000);
tabHooks.stp={enter:function(){stpPoller.start()},leave:function(){stpPoller.stop()}};
@@ -1461,131 +1517,282 @@ function rangeStr(list){
}
return parts.join(", ");
}
function vlanRefresh(){
var tb=$("vtable").tBodies[0];
return getJSON("/vlanlist").then(function(d){
var vl=d.vlan||[];
$("vmgmtcur").textContent=d.mgmt?String(d.mgmt):t("v_mgmt_none");
$("vempty").style.display=vl.length?"none":"";
tb.innerHTML="";
var p=Promise.resolve();
vl.forEach(function(v){
p=p.then(function(){return getJSON("/vlan.json?vid="+v.id).catch(function(){return null})}).then(function(d){
var tr=tb.insertRow();
tr.insertCell().appendChild(h("a",{href:"#vlan",text:String(v.id),onclick:function(e){
e.preventDefault();$("vvid").value=v.id;vlanLoad();
}}));
tr.insertCell().textContent=v.name||"";
if(!d){for(var c=0;c<4;c++)tr.insertCell().textContent="?";}
else{
var m=parseInt(d.members,16),mem=m&0x3ff,unt=((m>>10)&0x3ff)&mem;
var pv=parseInt(d.pvid,16)&0x3ff;
tr.insertCell().textContent=rangeStr(maskToPorts(mem));
tr.insertCell().textContent=rangeStr(maskToPorts(mem&~unt));
tr.insertCell().textContent=rangeStr(maskToPorts(unt));
tr.insertCell().textContent=rangeStr(maskToPorts(pv));
function vlanRefresh() {
var tb = $("vtable").tBodies[0];
tb.style="cursor:pointer;"
return getJSON("/vlanlist").then(function(d) {
var vl = d.vlan || [];
$("vmgmtcur").dataset.id = d.mgmt;
$("vempty").style.display = vl.length ? "none" : "";
tb.innerHTML = "";
var p = Promise.resolve();
vl.forEach(function(v) {
p = p.then(function() {
return getJSON("/vlan.json?vid=" + v.id).catch(function() {
return null
})
}).then(function(d) {
var tr = tb.insertRow();
tr.onclick = function(e) {
e.preventDefault();
vlanLoad(v.id);
};
var l = h("a", {
href: "#vlan",
text: String(v.id) + ($("vmgmtcur").dataset.id == v.id ? " ("+t("v_mgmt")+")":""),
"data-id": v.id,
"data-name": v.name
});
tr.insertCell().appendChild(l);
tr.insertCell().textContent = v.name || "";
if (!d) {
for (var c = 0; c < 4; c++) tr.insertCell().textContent = "?";
} else {
var m = parseInt(d.members, 16),
mem = m & 0x3ff,
unt = ((m >> 10) & 0x3ff) & mem,
tag = mem & ~unt;
var pv = parseInt(d.pvid, 16) & 0x3ff;
tr.insertCell().textContent = rangeStr(maskToPorts(mem));
tr.insertCell().textContent = rangeStr(maskToPorts(tag));
tr.insertCell().textContent = rangeStr(maskToPorts(unt));
tr.insertCell().textContent = rangeStr(maskToPorts(pv));
l.dataset.t = maskToPorts(tag);
l.dataset.u = maskToPorts(unt);
}
var del=tr.insertCell();
if(v.id!==1)del.appendChild(h("button",{class:"ctl",text:"\u2715",title:t("v_del_t"),onclick:function(){
confirmModal(t("v_del_q",{n:v.id}),t("v_del_d"),function(){
postCmd("vlan "+v.id+" d").then(vlanRefresh).catch(function(){});
});
}}));
}).catch(function(){});
var act = tr.insertCell();
act.onclick=function(e){e.stopPropagation()};
act.appendChild(h("button", {
class: "ctl",
text: t("v_rename"),
title: t("v_rename_t"),
onclick: function() {
var body = h("div");
body.appendChild(h("p", null, [
h("label", {class: "row"}, [
h("span",{class:"lb", "data-i18n":"c_name"},[document.createTextNode(t("c_name"))]),
h("input", {id: "nvlrename", class: "in", size: "12", maxlength: "15", "data-i18n-p": "v_optional", value:(v.name||"")})
])
]));
modal("Rename VLAN "+v.id, body, [h("button", {
class: "ctl pri", text: t("v_rename_t"),
onclick: function(){
if(!validVlanName($("nvlrename").value)) return;
postCmd(vlan2str(v.id,$("nvlrename").value,mySplit(l.dataset.t),mySplit(l.dataset.u))).then(function(){closeModal();vlanRefresh()});
}
})]);
},
}));
if (v.id == $("vmgmtcur").dataset.id) {
act.appendChild(h("span",{class:"ctl", title:t("v_mgmt_tt"), class:"hint", style:"color: var(--fg2); font-size: 150%; padding-left: 10px;"},[document.createTextNode("\u{1F6C8}")]));
} else {
act.appendChild(h("button", {
class: "ctl",
text: "\u2715",
title: t("v_del_t"),
style: "margin-left: 5px;",
onclick: function() {
confirmModal(t("v_del_q", { n: v.id }), t("v_del_d"), function() {
postCmd("vlan " + v.id + " d").then(vlanRefresh).catch(function() {});
});
}
}));
}
}).catch(function() {});
});
return p;
});
}
function buildVlanEdit(){
var tb=$("vedit").tBodies[0];
if(tb.rows.length||!S.n)return;
var hd=tb.insertRow();hd.insertCell().className="mut";
var rM=tb.insertRow();rM.insertCell().textContent=t("v_member");
var rP=tb.insertRow();rP.insertCell().textContent=t("v_pvid");
for(var p=1;p<=S.n;p++)(function(p){
hd.insertCell().innerHTML='<b>'+p+'</b>';
var seg=h("span",{class:"seg",id:"vm"+p});
["-","U","T"].forEach(function(s,ix){
seg.appendChild(h("button",{text:s,"data-v":ix,onclick:function(){
seg.querySelectorAll("button").forEach(function(b){b.classList.remove("on")});
this.classList.add("on");
}}));
function buildVlanEdit() {
var tb = $("vedit").tBodies[0];
if (tb.rows.length || !S.n) return;
var hd = tb.insertRow();
hd.insertCell().className = "mut";
var rN = tb.insertRow();
rN.insertCell().textContent = t("c_name");
var rM = tb.insertRow();
rM.insertCell().textContent = t("v_member");
var rP = tb.insertRow();
rP.insertCell().textContent = t("v_pvid");
for (var p = 1; p <= S.n; p++)(function(p) {
hd.insertCell().innerHTML = '<b>' + p + '</b>';
var seg = h("span", { class: "seg", id: "vm" + p });
["-", "U", "T"].forEach(function(s, ix) {
seg.appendChild(h("button", {
text: s,
"data-v": ix,
onclick: function() {
seg.querySelectorAll("button").forEach(function(b) { b.classList.remove("on") });
this.classList.add("on");
}
}));
});
seg.children[0].classList.add("on");
rN.insertCell().appendChild(document.createTextNode((S.ports.filter(x=>x.portNum==p)[0]).name));
rM.insertCell().appendChild(seg);
rP.insertCell().appendChild(h("input",{type:"checkbox",id:"vp"+p}));
rP.insertCell().appendChild(h("span", { id: "vp" + p }));
})(p);
var it=$("ingress").tBodies[0];
var ih=it.insertRow();ih.insertCell().className="mut";
var ir=it.insertRow();ir.insertCell().textContent=t("v_accept");
for(var q=1;q<=S.n;q++)(function(q){
ih.insertCell().innerHTML='<b>'+q+'</b>';
var sel=h("select",{class:"in",id:"ing"+q});
[["","-"],["a",t("v_ing_all")],["u",t("v_untagged")],["t",t("v_tagged")]].forEach(function(o){
sel.appendChild(h("option",{value:o[0],text:o[1]}));
});
ir.insertCell().appendChild(sel);
})(q);
}
function segVal(p){
return Number($("vm"+p).querySelector("button.on").getAttribute("data-v"));
}
function segSet(p,v){
$("vm"+p).querySelectorAll("button").forEach(function(b,i){b.classList.toggle("on",i===v)});
}
function vlanLoad(){
var vid=parseInt($("vvid").value,10);
if(!vid||vid<1||vid>4094){toast(t("v_vid_err"),"err");return;}
getJSON("/vlan.json?vid="+vid).then(function(d){
$("vname").value=d.name||"";
var m=parseInt(d.members,16),mem=m&0x3ff,unt=((m>>10)&0x3ff)&mem;
var pv=parseInt(d.pvid,16)&0x3ff;
for(var p=1;p<=S.n;p++){
var bit=S.physToLog[p-1];
segSet(p,(mem>>bit)&1?(((unt>>bit)&1)?1:2):0);
$("vp"+p).checked=!!((pv>>bit)&1);
}
toast(t("v_loaded",{n:vid}),"ok");
}).catch(function(){toast(t("v_notfound",{n:vid}),"err")});
}
function vlanApply(){
var vid=parseInt($("vvid").value,10);
if(!vid||vid<1||vid>4094){toast(t("v_vid_err"),"err");return;}
var name=$("vname").value.trim();
if(name&&!/^[a-zA-Z]\w*$/.test(name)){toast(t("v_name_err"),"err");return;}
var cmd="vlan "+vid+(name?" "+name:""),members=0;
for(var p=1;p<=S.n;p++){
var v=segVal(p);
if(v===1){cmd+=" "+p;members++;}
else if(v===2){cmd+=" "+p+"t";members++;}
}
if(!members){toast(t("v_nomember"),"err");return;}
var cmds=[cmd];
for(var q=1;q<=S.n;q++)if($("vp"+q).checked)cmds.push("pvid "+q+" "+vid);
postCmds(cmds).then(vlanRefresh).catch(function(){});
}
function ingressApply(){
var cmd="ingress",any=false;
for(var p=1;p<=S.n;p++){
var v=$("ing"+p).value;
if(v){cmd+=" "+p+v;any=true;}
}
if(!any){toast(t("v_ing_none"),"err");return;}
postCmd(cmd).catch(function(){});
}
$("vload").addEventListener("click",vlanLoad);
$("vapply").addEventListener("click",vlanApply);
$("ingapply").addEventListener("click",ingressApply);
$("vmgmt").addEventListener("click",function(){
var vid=parseInt($("vvid").value,10);
if(!vid){toast(t("v_vid_first"),"err");return;}
confirmModal(t("v_mgmt_q",{n:vid}),t("v_mgmt_d"),
function(){postCmd("vlan "+vid+" mgmt").then(vlanRefresh).catch(function(){})});
});
tabHooks.vlan={
enter:function(){needPorts(function(){buildVlanEdit();vlanRefresh().catch(function(){})})},
};
function segVal(p) {
return Number($("vm" + p).querySelector("button.on").getAttribute("data-v"));
}
function segSet(p, v) {
$("vm" + p).querySelectorAll("button").forEach(function(b, i) { b.classList.toggle("on", i === v) });
}
function vlanLoad(vid) {
if (!vid || vid < 1 || vid > 4094) {
toast(t("v_vid_err"), "err");
return;
}
getJSON("/vlan.json?vid=" + vid).then(function(d) {
$("vtitle").innerText = "VLAN " + vid + (d.name ? " (" + (d.name) + ")" : "");
$("vtitle").dataset.id = vid;
$("vtitle").dataset.name = d.name;
var m = parseInt(d.members, 16),
mem = m & 0x3ff,
unt = ((m >> 10) & 0x3ff) & mem;
var pv = parseInt(d.pvid, 16) & 0x3ff;
for (var p = 1; p <= S.n; p++) {
var bit = S.physToLog[p - 1];
segSet(p, (mem >> bit) & 1 ? (((unt >> bit) & 1) ? 1 : 2) : 0);
$("vp" + p).innerText = (!!((pv >> bit) & 1))?"Y":"N";
}
$("vmgmtpromote").checked = ($("vmgmtcur").dataset.id == vid);
toast(t("v_loaded", {n: vid}), "ok");
for(const vlanRow of $("vtable").tBodies[0].rows) {
vlanRow.className=vlanRow.firstChild.firstChild.dataset.id == vid ? "sel":"";
}
}).catch(function() {toast(t("v_notfound", {n: vid}), "err")});
}
function vlan2str(i, n, t, u){
return ("vlan " + i + (n ? " " + n : "") + " " + t.map((x)=>x+"t").concat(u).join(" ")).trim();
}
function pvidcmd(i, p){
return "pvid " + p + " " + i;
}
function mySplit(a){
if(a.trim()=="") return [];
return a.trim().split(",");
}
function vlanApply() {
var vid = $("vtitle").dataset.id;
var vname = $("vtitle").dataset.name;
if (!vid || vid < 1 || vid > 4094) {
toast(t("v_vid_err"), "err");
return;
}
var top_cmds = [], tail_cmds = [], vlansPerPort=[], thisVlanInStructure={id:vid, tagged:[], untagged:[]};
for (var p = 1; p <= S.n; p++) {
var v = segVal(p);
vlansPerPort[p]={t:[],u:[]};
if (v === 1) {
thisVlanInStructure.untagged.push(p);
vlansPerPort[p].u.push(vid);
} else if (v === 2) {
thisVlanInStructure.tagged.push(p);
vlansPerPort[p].t.push(vid);
}
}
if (thisVlanInStructure.tagged.length + thisVlanInStructure.untagged.length == 0) {
toast(t("v_nomember"), "err");
return;
}
for(const vlanRow of $("vtable").tBodies[0].rows){
var rowData = vlanRow.firstChild.firstChild.dataset,
rowUntagged = mySplit(rowData.u),
rowTagged = mySplit(rowData.t);
if(rowData.id == vid) continue;
for(const newVlanPort of thisVlanInStructure.untagged) {
if(rowUntagged.includes(""+newVlanPort)) rowUntagged.splice(rowUntagged.indexOf(""+newVlanPort),1);
}
for(const rut of rowUntagged) {
vlansPerPort[rut].u.push(rowData.id);
}
for(const rt of rowTagged) {
vlansPerPort[rt].t.push(rowData.id)
}
top_cmds.push(vlan2str(rowData.id,rowData.name,rowTagged,rowUntagged));
}
tail_cmds.push(vlan2str(vid, vname, thisVlanInStructure.tagged, thisVlanInStructure.untagged));
for (p in vlansPerPort){/*park all ports with only tagged membership*/
if (vlansPerPort[p].u.length == 0 && vlansPerPort[p].t.length > 0) {
tail_cmds.push(pvidcmd(0, p));
}
}
for(const up of thisVlanInStructure.untagged){
tail_cmds.push(pvidcmd(vid,up));
}
if($("vmgmtpromote").checked){/*must be the last command*/
tail_cmds.push("vlan " + vid + " mgmt");
}
var cmd = top_cmds.concat(tail_cmds);
postCmd(cmd.join("\n"),true).then(function(){toast(t("t_cmds",{n:cmd.length}),"ok")}).then(vlanRefresh).catch(function() {}).then(function() {
vlanLoad(vid)
});
}
$("vapply").addEventListener("click", vlanApply);
$("vlan_add").addEventListener("click", function() {
var body = h("div");
body.appendChild(h("p", null, [
h("label", {class: "row"}, [
h("span",{class:"lb", "data-i18n":"v_vid"},[document.createTextNode(t("v_vid"))]),
h("input", {id: "nvlid", class: "in sm", type: "number", min: "1", max: "4094"})
])
]));
body.appendChild(h("p", null, [
h("label", {class: "row"}, [
h("span",{class:"lb", "data-i18n":"c_name"},[document.createTextNode(t("c_name"))]),
h("input", {id: "nvlname", class: "in", size: "12", maxlength: "15", "data-i18n-p": "v_optional"})
])
]));
modal("Add VLAN", body, [h("button", {
class: "ctl pri", text: t("v_add"),
onclick: addVlan
})]);
});
function validVlanName(n){
if (n && (!/^[a-zA-Z]\w*$/.test(n) || n == "d" || n == "mgmt")) {
toast(t("v_name_err"), "err");
return false;
}
return true;
}
function addVlan() {
var vid = parseInt($("nvlid").value, 10);
for (const r of $("vtable").tBodies[0].rows) {
if (r.firstChild.firstChild.dataset.id == vid) {
toast(t("v_vid_err"), "err");
return;
}
}
var name = $("nvlname").value;
if(!validVlanName(name)) return false;
var cmd = "vlan " + vid + (name ? " " + name : "");
postCmd(cmd).then(function() {
closeModal();
vlanRefresh().then(function(){vlanLoad(vid)});
}).catch(function() {});
}
tabHooks.vlan = {
enter: function() {
needPorts(function() {
buildVlanEdit();
vlanRefresh().catch(function() {}).then(function() {
VlanInit();
})
})
},
};
function VlanInit() {
vlanLoad($("vtable").tBodies[0].firstChild.firstChild.firstChild.dataset.id)
};
var l2Rows=[],l2SortCol="pport",l2SortDir=1,l2Gen=0;
function l2Load(){
var seen={},all=[],idx=0,guard=0,gen=++l2Gen;
@@ -2243,14 +2450,14 @@ var CONF_OVERWRITE=[
/^ntp\s+server\b/,/^ntp\s+interval\b/,/^ntp\s+timezone\b/,/^ntp\s+dst\b/,
/^vlan\s+\d{1,4}\s+mgmt$/,/^vlan\s+\d{1,4}(?!\s+mgmt\b)/,
/^pvid\s+\d{1,2}\b/,
/^port\s+\d{1,2}(?!\s+name\b)/,/^port\s+\d{1,2}\s+name\b/,
/^port\s+\d{1,2}(?!\s+(name|lldp)\b)/,/^port\s+\d{1,2}\s+name\b/,
/^mirror\b/,
/^lag\s+\d\b/,/^laghash\s+\d\b/,/^isolate\s+\d{1,2}\b/,
/^stp\s+(prio|hello|maxage|fwd|txhold|version)\b/,
/^stp\s+(port\s+\d{1,2}|lag\s+[1-4])\s+(edge|cost|prio|guard|filter|p2p)\b/,
/^igmp\b/,/^mtu\s+\d{1,2}\b/,/^storm\s+\d{1,2}\s+(bcast|mcast|ucast|umcast)\b/,
];
var CONF_TOGGLE=[/^(syslog)\s+(on|off)$/,/^(ntp)\s+(on|off)$/,/^(stp)\s+(on|off)$/,/^(stp\s+(port\s+\d{1,2}|lag\s+[1-4]))\s+(on|off)$/];
var CONF_TOGGLE=[/^(syslog)\s+(on|off)$/,/^(ntp)\s+(on|off)$/,/^(stp)\s+(on|off)$/,/^(lldp)\s+(on|off)$/,/^(stp\s+(port\s+\d{1,2}|lag\s+[1-4]))\s+(on|off)$/];
function mergeConf(base,texts){
var conf=base.slice();
function drop(rx){conf=conf.filter(function(c){return!rx.test(c)})}
@@ -2268,6 +2475,7 @@ function mergeConf(base,texts){
}
if((m=line.match(/^lag (\d) lacp off$/))){drop(new RegExp("^lag "+m[1]+" lacp( |$)"));return;}
if(line==="mirror off"){drop(/^mirror /);return;}
if((m=line.match(/^(port \d{1,2} lldp) (permit|block)$/))){drop(new RegExp("^"+m[1]+" (permit|block)$"));conf.push(line);return;}
if(!isConfCmd(line))return;
if((m=line.match(/^ingress (.+)$/))){
if(/^[tua]$/.test(m[1])){drop(/^ingress /);conf.push(line);return;}
@@ -2300,7 +2508,7 @@ function mergeConf(base,texts){
var key=line.match(rx)[0];
conf=conf.filter(function(item){
return!(item===key||(item.indexOf(key+" ")===0
&&!/\smgmt$/.test(item)&&item.indexOf(key+" name ")!==0));
&&!/\smgmt$/.test(item)&&item.indexOf(key+" name ")!==0&&item.indexOf(key+" lldp ")!==0));
});
break;
}
+34 -23
View File
@@ -33,7 +33,7 @@
html,body{height:100%}
body{
font:14px/1.45 system-ui,-apple-system,"Segoe UI",Roboto,sans-serif;
background:var(--bg);color:var(--fg);display:flex;
background:var(--bg);color:var(--fg);display:flex;overflow:hidden;
}
button,input,select,textarea{font:inherit;color:var(--fg)}
a{color:var(--ac);text-decoration:none}
@@ -72,8 +72,8 @@ select.ctl,button.ctl{
button.pri{background:var(--ac);color:var(--acfg);border-color:transparent;font-weight:600}
button.pri:hover,button.ctl:hover{filter:brightness(1.07)}
button:disabled{opacity:.45;cursor:default;filter:none}
main{padding:18px 20px 40px;max-width:1400px;width:100%}
section.tab{display:none}
main{padding:18px 20px 40px;width:100%;overflow-y:auto;}
section.tab{display:none;max-width:1400px;}
section.tab.act{display:block}
.card{
background:var(--panel);border:1px solid var(--bd);border-radius:10px;
@@ -89,9 +89,11 @@ table.t th{
padding:6px 8px;border-bottom:1px solid var(--bd);white-space:nowrap;
}
table.t th[data-sort]{cursor:pointer;user-select:none}
table.t th.num{text-align:right}
table.t td{padding:6px 8px;border-bottom:1px solid var(--bd);vertical-align:middle}
table.t tr:last-child td{border-bottom:0}
table.t tr:hover td{background:var(--sel)}
table.t tr.sel{background:var(--panel2); font-weight: bold;}
.num{font-variant-numeric:tabular-nums;text-align:right}
.scrollx{overflow-x:auto}
input.in,select.in,textarea.in{
@@ -123,12 +125,20 @@ label.row span.lb{width:110px;flex:none;color:var(--fg2);font-size:13px}
.port .pn{font-weight:650;font-size:12px}
.port .ps{font-size:9px;color:var(--fg2);display:block;line-height:1.2}
.port .jack{
display:block;width:16px;height:10px;margin:2px auto 1px;border-radius:2px;
border:2px solid var(--fg2);border-top-width:4px;opacity:.55;
display:block;width:21px;height:12px;margin:4px auto 2px;border-radius:2px;
border:2px solid var(--fg2);border-top-width:8px;opacity:.55;position:relative;
}
.port.sfp .jack{width:24px;height:9px;border-top-width:3px}
.port .jack:before{content:"";
position:absolute;bottom:100%;left:50%;transform:translateX(-50%);
width:6px;height:5px;margin-bottom:7px;border-radius: 1px 1px 0 0;
background-color:var(--fg2);
}
.port.sfp .jack{width:24px;height:16px;margin:0 auto 2px;border-top-width:5px}
.port.sfp .jack:before{width:10px;margin-bottom:2px;border-radius:0 0 1px 1px;background-color:var(--panel2)}
.port.up{border-color:var(--pc);background:color-mix(in srgb,var(--pc) 10%,var(--panel2))}
.port.up .jack{border-color:var(--pc);opacity:1}
.port.up .jack:before{background-color:var(--pc)}
.port.up.sfp .jack:before{background-color:color-mix(in srgb,var(--pc) 10%,var(--panel2))}
.port.up .ps{color:var(--pc);font-weight:600}
.port.dis{opacity:.45;border-style:dashed}
.badge{font-size:11px;font-weight:600;border-radius:10px;padding:1px 8px;background:var(--panel2);color:var(--fg2)}
@@ -223,7 +233,7 @@ button.danger{background:var(--bad);color:#fff;border-color:transparent}
<span id="ttitle"></span>
<div id="strip"></div>
<span id="dirty" data-i18n="hdr_dirty" data-i18n-t="hdr_dirty_t">unsaved changes</span>
<button class="ctl pri" id="saveBtn" data-i18n="hdr_save" data-i18n-t="hdr_save_t">Save to flash</button>
<button class="ctl" id="saveBtn" data-i18n="hdr_save" data-i18n-t="hdr_save_t">Save to flash</button>
</header>
<main>
@@ -254,6 +264,8 @@ button.danger{background:var(--bad);color:#fff;border-color:transparent}
<p class="small mono" id="stpids">-</p>
<div id="stpbridge" style="display:flex;gap:14px;flex-wrap:wrap;align-items:flex-end;margin-top:10px">
<label><span class="small mut" data-i18n="stp_version">Version</span><br><select class="in" id="stpver"><option value="rstp">RSTP</option><option value="stp" data-i18n="stp_v_stp">STP compatible</option></select></label>
<label><span class="small mut" data-i18n="stp_pcs">Path cost</span><br><select class="in" id="stppcs"><option value="long" data-i18n="stp_pcs_long">Long (32 bit)</option><option value="short" data-i18n="stp_pcs_short">Short (16 bit)</option></select></label>
<label><span class="small mut" data-i18n="stp_bh">BPDUs while disabled</span><br><select class="in" id="stpbh"><option value="flood" data-i18n="stp_bh_flood">Flood</option><option value="filter" data-i18n="stp_bh_filter">Filter</option></select></label>
<label><span class="small mut" data-i18n="stp_prio">Bridge priority</span><br><select class="in" id="stpprio"></select></label>
<label><span class="small mut" data-i18n="stp_hello">Hello [s]</span><br><input class="in sm" id="stphello" type="number" min="1" max="10"></label>
<label><span class="small mut" data-i18n="stp_maxage">Max age [s]</span><br><input class="in sm" id="stpmaxage" type="number" min="6" max="40"></label>
@@ -271,9 +283,14 @@ button.danger{background:var(--bad);color:#fff;border-color:transparent}
</div>
<div class="card"><h2 data-i18n="stp_status">Port status</h2>
<div class="scrollx"><table class="t" id="stpstat">
<thead><tr><th data-i18n="c_port">Port</th><th data-i18n="stp_role">Role</th><th data-i18n="c_state">State</th><th data-i18n="stp_db">Designated bridge</th><th data-i18n="stp_dp">Designated port</th><th data-i18n="stp_dc">Designated cost</th><th data-i18n="stp_oedge">Oper. edge</th><th data-i18n="stp_op2p">Oper. P2P</th></tr></thead>
<thead><tr><th data-i18n="c_port">Port</th><th data-i18n="stp_role">Role</th><th data-i18n="c_state">State</th><th data-i18n="stp_db">Designated bridge</th><th data-i18n="stp_dp">Designated port</th><th data-i18n="stp_dc">Designated cost</th><th data-i18n="stp_oedge">Oper. edge</th><th data-i18n="stp_op2p">Oper. P2P</th><th data-i18n="stp_proto">Protocol</th><th></th></tr></thead>
<tbody></tbody></table></div>
</div>
<div class="card"><details id="stpcntd"><summary><h2 style="display:inline" data-i18n="stp_cnt">Counters</h2></summary>
<div class="scrollx"><table class="t" id="stpcnt">
<thead><tr><th data-i18n="c_port">Port</th><th data-i18n="stp_crx">BPDUs received</th><th data-i18n="stp_ctx">BPDUs sent</th><th data-i18n="stp_ctrx">TC received</th><th data-i18n="stp_cttx">TC sent</th><th data-i18n="stp_cage">Last BPDU [s]</th></tr></thead>
<tbody></tbody></table></div></details>
</div>
</section>
<section class="tab" id="tab-stats">
@@ -285,27 +302,21 @@ button.danger{background:var(--bad);color:#fff;border-color:transparent}
</section>
<section class="tab" id="tab-vlan">
<div class="card"><h2><span data-i18n="v_title">VLANs</span> <span class="hint"><span data-i18n="v_mgmt">Management VLAN</span>: <span id="vmgmtcur">-</span></span></h2>
<div class="card"><h2><span data-i18n="v_title">VLANs</span><span id="vmgmtcur"></span></h2>
<div class="scrollx"><table class="t" id="vtable">
<thead><tr><th data-i18n="v_vid">VID</th><th data-i18n="c_name">Name</th><th data-i18n="v_members">Members</th><th data-i18n="v_tagged">Tagged</th><th data-i18n="v_untagged">Untagged</th><th data-i18n="v_pvid_on">PVID on</th><th></th></tr></thead>
<tbody></tbody></table></div>
<p class="small mut" id="vempty" style="display:none;margin-top:6px" data-i18n="v_empty">No VLANs configured.</p>
<button class="ctl pri" id="vlan_add" data-i18n="v_add">Add a new VLAN</button>
</div>
<div class="card"><h2 data-i18n="v_editor">VLAN editor</h2>
<div style="display:flex;gap:10px;flex-wrap:wrap;align-items:center;margin-bottom:10px">
<label><span data-i18n="v_vid">VID</span> <input class="in sm" id="vvid" type="number" min="1" max="4094"></label>
<label><span data-i18n="c_name">Name</span> <input class="in" id="vname" size="12" maxlength="15" data-i18n-p="v_optional"></label>
<button class="ctl" id="vload" data-i18n="c_load">Load</button>
<button class="ctl pri" id="vapply" data-i18n="c_apply">Apply</button>
<button class="ctl" id="vmgmt" data-i18n="v_setmgmt" data-i18n-t="v_setmgmt_t">Set as mgmt VLAN</button>
</div>
<div class="card"><h2><span data-i18n="v_editor">VLAN editor</span> <span class="hint" id="vtitle"></span></h2>
<div><label class="row"><span class="lb" style="width:150px;" data-i18n="v_setmgmt_t"></span>
<span class="switch"><input type="checkbox" id="vmgmtpromote"><i></i></span>
</label></div>
<div class="scrollx"><table class="t" id="vedit"><tbody></tbody></table></div>
<div style="margin-top:10px"><button class="ctl pri" id="vapply" data-i18n="c_apply">Apply</button></div>
<p class="small mut" style="margin-top:6px" data-i18n="v_legend">U = untagged member, T = tagged member, - = not a member. PVID assigns this VLAN to untagged ingress traffic.</p>
</div>
<div class="card"><h2><span data-i18n="v_ingress">Ingress filtering</span> <span class="hint" data-i18n="v_ingress_h">write-only: the state is not readable from the switch</span></h2>
<div class="scrollx"><table class="t" id="ingress"><tbody></tbody></table></div>
<div style="margin-top:10px"><button class="ctl pri" id="ingapply" data-i18n="v_ing_apply">Apply ingress modes</button></div>
</div>
</section>
<section class="tab" id="tab-l2">
@@ -437,8 +448,8 @@ button.danger{background:var(--bad);color:#fff;border-color:transparent}
<p class="small mut" style="margin-top:8px" data-i18n="sy_time_note">The switch has no battery backed clock.</p>
</div>
<div class="card"><h2 data-i18n="sy_password">Admin password</h2>
<label class="row"><span class="lb" data-i18n="sy_newpw">New password</span><input class="in" id="sy-pw1" type="password" maxlength="20"></label>
<label class="row"><span class="lb" data-i18n="sy_repeat">Repeat</span><input class="in" id="sy-pw2" type="password" maxlength="20"></label>
<label class="row"><span class="lb" data-i18n="sy_newpw">New password</span><input class="in" id="sy-pw1" type="password" autocomplete="new-password" maxlength="20"></label>
<label class="row"><span class="lb" data-i18n="sy_repeat">Repeat</span><input class="in" id="sy-pw2" type="password" autocomplete="new-password" maxlength="20"></label>
<button class="ctl pri" id="sy-pwapply" style="margin-top:8px" data-i18n="sy_pwapply">Change password</button>
<p class="small mut" style="margin-top:8px" data-i18n="sy_pw_note">Takes effect immediately; save to flash to keep it after reboot.</p>
</div>
+14 -48
View File
@@ -6,12 +6,12 @@
#include "cmd_parser.h"
#include "rtl837x_flash.h"
#include "uip.h"
#include "sessions.h"
#include "html_data.h"
// #define DEBUG
#include "debug.h"
#define SESSION_ID_LENGTH 12
#define SESSION_TIMEOUT_DEFAULT 200
/* Seconds of inactivity after which a session cookie stops validating.
@@ -75,10 +75,9 @@ __xdata uint8_t postbody_endpoint;
__xdata uint16_t postbody_start;
__xdata char passwd[21];
__xdata char session_id[SESSION_ID_LENGTH + 1];
// Up to four logins at once, see sessions.c
__xdata uint8_t authenticated;
__xdata uint32_t now;
__xdata uint32_t last_session_use;
#define TSTATE_NONE 0
#define TSTATE_TX 1
@@ -109,6 +108,7 @@ void httpd_init(void) __banked
// handling one, and nothing has set it yet at init time.
for (uint8_t i = 0; i < UIP_CONNS; i++)
uip_conns[i].appstate.tstate = TSTATE_CLOSED;
session_init();
}
@@ -343,38 +343,11 @@ __xdata uint8_t *scan_header(__xdata uint8_t * __xdata p)
read_reg_timer(&now);
if (session && session_id[0]) {
if (now - last_session_use > session_timeout) {
dbg_string("Session expired\n");
} else {
if (is_word_x(session, session_id)) {
authenticated = 1;
last_session_use = now;
} else {
dbg_string("Invalid session cookie!\n");
}
}
}
if (session)
session_check();
return p;
}
/*
* Generate random HEX-chars at the buffer location.
*/
void gen_random_hex_chars(__xdata uint8_t * b, __xdata uint8_t bytes)
{
uint8_t i = 0;
while (bytes) {
if (!i)
get_random_32();
b[--bytes] = itohex(sfr_data[i]);
if (!bytes) { break; }
b[--bytes] = itohex(sfr_data[i] >> 4 | sfr_data[i] << 4);
i = (i + 1) & 0x3;
}
}
/* 0: body incomplete, 1: configuration stored, 2: malformed */
static uint8_t config_take(void)
{
@@ -448,15 +421,9 @@ static uint8_t config_take(void)
// unlike scan_header(), keeps no auth state, so it may run on every buffered segment
static uint16_t preamble_payload_start(uint16_t n)
{
uint16_t pos;
// Skip the HTTP header separator retained by handle_post().
uint16_t pos = 4;
for (pos = 0; pos + 24 <= n; pos++) {
if (strstart(&config_buf[pos], "application/octet-stream"))
break;
}
if (pos + 24 > n)
return 0;
pos += 24;
while (pos + 3 < n && !strstart(&config_buf[pos], "\r\n\r\n"))
pos++;
if (pos + 3 >= n)
@@ -537,9 +504,10 @@ uint8_t stream_upload(void)
memcpy(flash_buf + write_len, boundary, bindex);
write_len += bindex;
bindex = 0;
} else {
crc16_bank1(upload_settings.p + upload_settings.bptr);
flash_buf[write_len++] = upload_settings.p[upload_settings.bptr++];
}
crc16_bank1(upload_settings.p + upload_settings.bptr);
flash_buf[write_len++] = upload_settings.p[upload_settings.bptr++];
if (write_len >= FLASH_PAGE_SIZE) {
/* The staged image spans FIRMWARE_UPLOAD_START to twice that,
* the span check_and_flash_update_image() reads back. Nothing
@@ -571,7 +539,6 @@ uint8_t stream_upload(void)
memcpy(flash_buf, flash_buf + FLASH_PAGE_SIZE, write_len);
}
}
bindex = 0;
}
} while(1);
}
@@ -681,13 +648,10 @@ static void run_login_body(__xdata uint8_t *body)
{
if (strstart(body, "pwd=") && is_url_word_x(body + 4, passwd)) {
dbg_string("Password accepted!\n");
read_reg_timer(&last_session_use);
gen_random_hex_chars(session_id, SESSION_ID_LENGTH);
session_id[SESSION_ID_LENGTH] = NUL;
read_reg_timer(&now);
slen = strtox(outbuf, "HTTP/1.1 302 Found\r\nConnection: close\r\nLocation: index.html\r\n" \
"Set-Cookie: session=");
for (uint8_t i = 0; i < SESSION_ID_LENGTH; i++)
outbuf[slen++] = session_id[i];
session_new();
slen += strtox(outbuf + slen, "; SameSite=Strict\r\n\r\n");
} else {
dbg_string("Password invalid!\n");
@@ -1091,6 +1055,8 @@ void httpd_appcall(void)
send_lldp();
} else if (is_word(q, "/lacp.json")) {
send_lacp();
} else if (is_word(q, "/stpcnt.json")) {
send_stp_counters();
} else if (is_word(q, "/vlanlist")) {
send_vlanlist();
} else if (is_word(q, "/config")) {
+7 -149
View File
@@ -600,143 +600,6 @@ void send_lag(void)
}
static __xdata uint32_t pi_u32;
static __xdata uint8_t pi_prio, pi_ext;
static __xdata uint8_t * __xdata pi_mac;
static void u32hex_html(void)
{
__xdata uint8_t *b = (__xdata uint8_t *)&pi_u32;
byte_to_html(b[3]);
byte_to_html(b[2]);
byte_to_html(b[1]);
byte_to_html(b[0]);
}
static void bridge_to_html(void)
{
byte_to_html(pi_prio);
byte_to_html(pi_ext);
for (uint8_t i = 0; i < 6; i++)
byte_to_html(pi_mac[i]);
}
void send_stp(void)
{
uint8_t i, j, st, dsg;
dbg_string("send_stp called\n");
slen = strtox(outbuf, HTTP_RESPONCE_JSON);
slen += strtox(outbuf + slen, "{\"on\":");
bool_to_html(stp_enabled);
slen += strtox(outbuf + slen, ",\"rstp\":");
bool_to_html(stp_rstp);
slen += strtox(outbuf + slen, ",\"prio\":");
itoa_html(stp_prio >> 4);
slen += strtox(outbuf + slen, ",\"hello\":");
itoa_html(stp_hello_s);
slen += strtox(outbuf + slen, ",\"maxage\":");
itoa_html(stp_maxage_s);
slen += strtox(outbuf + slen, ",\"fwd\":");
itoa_html(stp_fwddelay_s);
slen += strtox(outbuf + slen, ",\"txhold\":");
itoa_html(stp_txhold);
slen += strtox(outbuf + slen, ",\"rootPrio\":\"");
byte_to_html(root_bridge.prio);
byte_to_html(root_bridge.ext);
slen += strtox(outbuf + slen, "\",\"rootMac\":\"");
for (j = 0; j < 6; j++)
byte_to_html(root_bridge.mac[j]);
slen += strtox(outbuf + slen, "\",\"myMac\":\"");
for (j = 0; j < 6; j++)
byte_to_html(uip_ethaddr.addr[j]);
slen += strtox(outbuf + slen, "\",\"cost\":\"");
byte_to_html(root_bridge_cost >> 24);
byte_to_html(root_bridge_cost >> 16);
byte_to_html(root_bridge_cost >> 8);
byte_to_html(root_bridge_cost);
slen += strtox(outbuf + slen, "\",\"weRoot\":");
bool_to_html(stp_root_port == 0xff ? 1 : 0);
slen += strtox(outbuf + slen, ",\"rootPort\":");
j = stp_root_port;
if (j != 0xff && j >= STP_LAG_BASE) {
j = 0;
while (j < STP_LAG_BASE && !((stp_lag_mask[stp_root_port - STP_LAG_BASE] >> j) & 1))
j++;
if (j >= STP_LAG_BASE)
j = 0;
}
itoa_html(j == 0xff ? 0 : machine.log_to_phys_port[j]);
slen += strtox(outbuf + slen, ",\"tc\":\"");
byte_to_html(stp_tc_count >> 8);
byte_to_html(stp_tc_count);
slen += strtox(outbuf + slen, "\",\"ports\":[");
reg_read_m(RTL837X_MSTP_STATES);
for (i = 0; i < STP_ENTITIES; i++) {
if (i < STP_LAG_BASE && !MACHINE_PORT_VALID(i))
continue;
if (i < STP_LAG_BASE && stp_ent_of[i] != i)
continue;
if (i >= STP_LAG_BASE && !stp_lag_mask[i - STP_LAG_BASE])
continue;
slen += strtox(outbuf + slen, "{\"p\":");
itoa_html(i < STP_LAG_BASE ? machine.log_to_phys_port[i] : 0);
slen += strtox(outbuf + slen, ",\"lag\":");
itoa_html(i < STP_LAG_BASE ? 0 : i - STP_LAG_BASE + 1);
slen += strtox(outbuf + slen, ",\"mbr\":");
itoa16_html(i < STP_LAG_BASE ? 0 : stp_lag_mask[i - STP_LAG_BASE]);
slen += strtox(outbuf + slen, ",\"st\":");
j = i;
if (i >= STP_LAG_BASE) {
j = 0;
while (j < STP_LAG_BASE && !((stp_lag_mask[i - STP_LAG_BASE] >> j) & 1))
j++;
if (j >= STP_LAG_BASE)
j = 0;
}
st = (sfr_data[3 - (j >> 2)] >> ((j << 1) & 0x7)) & 0x3;
itoa_html(st);
slen += strtox(outbuf + slen, ",\"role\":");
if (!(stp_pflags[i] & STP_PF_ENABLED) || (stp_pflags[i] & STP_PF_TRIPPED))
itoa_html(0);
else if (i == stp_root_port)
itoa_html(1);
else if (st == 3)
itoa_html(2);
else
itoa_html(3);
slen += strtox(outbuf + slen, ",\"f\":");
itoa_html(stp_pflags[i]);
slen += strtox(outbuf + slen, ",\"pc\":\"");
pi_u32 = stp_pcost[i]; u32hex_html();
slen += strtox(outbuf + slen, "\",\"prio\":");
itoa_html(stp_pprio[i]);
slen += strtox(outbuf + slen, ",\"p2\":");
itoa_html(stp_pp2p[i]);
dsg = stp_dpid[i] && stp_bpdu_age[i] < (uint16_t)stp_maxage_s * STP_HZ;
slen += strtox(outbuf + slen, ",\"db\":\"");
if (dsg) {
pi_prio = stp_dbridge[i].prio; pi_ext = stp_dbridge[i].ext;
pi_mac = stp_dbridge[i].mac;
} else {
pi_prio = stp_prio; pi_ext = 0;
pi_mac = uip_ethaddr.addr;
}
bridge_to_html();
slen += strtox(outbuf + slen, "\",\"dp\":\"");
byte_to_html(dsg ? (stp_dpid[i] >> 8) : stp_pprio[i]);
byte_to_html(dsg ? stp_dpid[i] : (i + 1));
slen += strtox(outbuf + slen, "\",\"dc\":\"");
pi_u32 = dsg ? stp_dcost[i] : root_bridge_cost; u32hex_html();
slen += strtox(outbuf + slen, "\"},");
}
slen -= 1; // remove comma
slen += strtox(outbuf + slen, "]}");
}
void send_lacp(void)
{
dbg_string("send_lacp called\n");
@@ -810,12 +673,12 @@ void send_lldp(void)
if (i < MACHINE_MAX_PORT)
char_to_html(',');
else
char_to_html(']');
}
char_to_html(']');
char_to_html('}');
}
void send_eee(void)
{
dbg_string("send_eee called\nsending EEE status\n");
@@ -859,9 +722,8 @@ void send_eee(void)
char_to_html('}');
if (i < MACHINE_MAX_PORT)
char_to_html(',');
else
char_to_html(']');
}
char_to_html(']');
}
@@ -902,9 +764,8 @@ void send_bandwidth(void)
char_to_html('}');
if (i < MACHINE_MAX_PORT)
char_to_html(',');
else
char_to_html(']');
}
char_to_html(']');
}
@@ -936,9 +797,8 @@ void send_storm(void)
char_to_html('}');
if (i < MACHINE_MAX_PORT)
char_to_html(',');
else
char_to_html(']');
}
char_to_html(']');
}
@@ -982,9 +842,8 @@ void send_mtu(void)
char_to_html('}');
if (i < MACHINE_MAX_PORT)
char_to_html(',');
else
char_to_html(']');
}
char_to_html(']');
}
@@ -1117,9 +976,8 @@ void send_status(void)
slen += strtox(outbuf + slen, "\"}");
if (i < MACHINE_MAX_PORT)
char_to_html(',');
else
char_to_html(']');
}
char_to_html(']');
}
+2 -1
View File
@@ -22,9 +22,10 @@ void send_isolation(void);
void send_config(void);
void send_cmd_log(void);
void send_lag(void);
void send_stp(void);
void send_stp(void) __banked;
void send_lldp(void);
void send_lacp(void);
void send_stp_counters(void) __banked;
void send_vlanlist(void);
/* Convert only the lower nibble to ascii HEX char.
+195
View File
@@ -0,0 +1,195 @@
#include "rtl837x_sfr.h"
#include "rtl837x_common.h"
#include "rtl837x_regs.h"
#include "rtl837x_port.h"
#include "uip.h"
#include <stdint.h>
#include "phy.h"
#include "machine.h"
#include "rtl837x_stp.h"
#include "page_impl.h"
#include "debug.h"
#pragma codeseg BANK4
#pragma constseg BANK4
extern __xdata uint8_t outbuf[TCP_OUTBUF_SIZE];
extern __xdata uint16_t slen;
static __code const uint8_t stp_json_hdr[] = "HTTP/1.1 200 OK\r\nConnection: close\r\nContent-Type: application/json\r\n\r\n";
static void stp_bool_html(char c)
{
outbuf[slen++] = c ? '1' : '0';
}
static void stp_byte_html(uint8_t val)
{
outbuf[slen++] = itohex(val >> 4);
outbuf[slen++] = itohex(val);
}
static void stp_itoa_html(uint8_t v)
{
uint8_t t = v / 100;
uint8_t print_zeros = t;
if (print_zeros)
outbuf[slen++] = '0' + t;
t = (v / 10) % 10;
print_zeros |= t;
if (print_zeros)
outbuf[slen++] = '0' + t;
outbuf[slen++] = '0' + (v % 10);
}
static __xdata uint32_t pi_u32;
static __xdata uint8_t pi_prio, pi_ext;
static __xdata uint8_t * __xdata pi_mac;
static void u32hex_html(void)
{
__xdata uint8_t *b = (__xdata uint8_t *)&pi_u32;
stp_byte_html(b[3]);
stp_byte_html(b[2]);
stp_byte_html(b[1]);
stp_byte_html(b[0]);
}
static void bridge_to_html(void)
{
stp_byte_html(pi_prio);
stp_byte_html(pi_ext);
for (uint8_t i = 0; i < 6; i++)
stp_byte_html(pi_mac[i]);
}
void send_stp(void) __banked
{
static __xdata uint8_t i, j, st, dsg;
dbg_string("send_stp called\n");
slen = strtox(outbuf, stp_json_hdr);
slen += strtox(outbuf + slen, "{\"on\":");
stp_bool_html(stp_enabled);
slen += strtox(outbuf + slen, ",\"rstp\":");
stp_bool_html(stp_rstp);
slen += strtox(outbuf + slen, ",\"prio\":");
stp_itoa_html(stp_prio >> 4);
slen += strtox(outbuf + slen, ",\"hello\":");
stp_itoa_html(stp_hello_s);
slen += strtox(outbuf + slen, ",\"maxage\":");
stp_itoa_html(stp_maxage_s);
slen += strtox(outbuf + slen, ",\"fwd\":");
stp_itoa_html(stp_fwddelay_s);
slen += strtox(outbuf + slen, ",\"txhold\":");
stp_itoa_html(stp_txhold);
slen += strtox(outbuf + slen, ",\"pcs\":");
stp_itoa_html(stp_pcost_short);
slen += strtox(outbuf + slen, ",\"bh\":");
stp_itoa_html(stp_bpdu_filter);
slen += strtox(outbuf + slen, ",\"tcs\":\"");
pi_u32 = stp_tc_secs; u32hex_html();
slen += strtox(outbuf + slen, "\",\"rMaxage\":");
stp_itoa_html(stp_root_port == 0xff ? stp_maxage_s : stp_root_maxage);
slen += strtox(outbuf + slen, ",\"rFwd\":");
stp_itoa_html(stp_root_port == 0xff ? stp_fwddelay_s : stp_root_fwd);
slen += strtox(outbuf + slen, ",\"rootPrio\":\"");
stp_byte_html(root_bridge.prio);
stp_byte_html(root_bridge.ext);
slen += strtox(outbuf + slen, "\",\"rootMac\":\"");
for (j = 0; j < 6; j++)
stp_byte_html(root_bridge.mac[j]);
slen += strtox(outbuf + slen, "\",\"myMac\":\"");
for (j = 0; j < 6; j++)
stp_byte_html(uip_ethaddr.addr[j]);
slen += strtox(outbuf + slen, "\",\"cost\":\"");
stp_byte_html(root_bridge_cost >> 24);
stp_byte_html(root_bridge_cost >> 16);
stp_byte_html(root_bridge_cost >> 8);
stp_byte_html(root_bridge_cost);
slen += strtox(outbuf + slen, "\",\"weRoot\":");
stp_bool_html(stp_root_port == 0xff ? 1 : 0);
slen += strtox(outbuf + slen, ",\"tc\":\"");
stp_byte_html(stp_tc_count >> 8);
stp_byte_html(stp_tc_count);
slen += strtox(outbuf + slen, "\",\"ports\":[");
for (i = 0; i < STP_ENTITIES; i++) {
j = stp_ent_id(i);
if (!j)
continue;
slen += strtox(outbuf + slen, "{\"p\":");
stp_itoa_html(j);
slen += strtox(outbuf + slen, ",\"st\":");
st = stp_port_state(i);
stp_itoa_html(st);
slen += strtox(outbuf + slen, ",\"role\":");
stp_itoa_html(stp_port_role(i));
slen += strtox(outbuf + slen, ",\"f\":");
stp_itoa_html(stp_pflags[i]);
slen += strtox(outbuf + slen, ",\"pc\":\"");
pi_u32 = stp_pcost[i]; u32hex_html();
slen += strtox(outbuf + slen, "\",\"prio\":");
stp_itoa_html(stp_pprio[i]);
slen += strtox(outbuf + slen, ",\"p2\":");
stp_itoa_html(stp_pp2p[i]);
slen += strtox(outbuf + slen, ",\"lk\":");
stp_itoa_html((stp_link_prev >> i) & 1);
slen += strtox(outbuf + slen, ",\"lg\":");
stp_itoa_html((stp_legacy >> i) & 1);
dsg = stp_dpid[i] && stp_info_while[i];
slen += strtox(outbuf + slen, ",\"db\":\"");
if (dsg) {
pi_prio = stp_dbridge[i].prio; pi_ext = stp_dbridge[i].ext;
pi_mac = stp_dbridge[i].mac;
} else {
pi_prio = stp_prio; pi_ext = 0;
pi_mac = uip_ethaddr.addr;
}
bridge_to_html();
slen += strtox(outbuf + slen, "\",\"dp\":\"");
stp_byte_html(dsg ? (stp_dpid[i] >> 8) : stp_pprio[i]);
stp_byte_html(dsg ? stp_dpid[i] : j);
slen += strtox(outbuf + slen, "\",\"dc\":\"");
pi_u32 = dsg ? stp_dcost[i] : root_bridge_cost; u32hex_html();
slen += strtox(outbuf + slen, "\"},");
}
slen -= 1; // remove comma
slen += strtox(outbuf + slen, "]}");
}
void send_stp_counters(void) __banked
{
uint8_t i, j;
slen = strtox(outbuf, stp_json_hdr);
slen += strtox(outbuf + slen, "{\"on\":");
stp_bool_html(stp_enabled);
slen += strtox(outbuf + slen, ",\"hz\":");
stp_itoa_html(STP_HZ);
slen += strtox(outbuf + slen, ",\"tc\":\"");
stp_byte_html(stp_tc_count >> 8);
stp_byte_html(stp_tc_count);
slen += strtox(outbuf + slen, "\",\"ports\":[");
for (i = 0; i < STP_ENTITIES; i++) {
j = stp_ent_id(i);
if (!j)
continue;
slen += strtox(outbuf + slen, "{\"p\":");
stp_itoa_html(j);
slen += strtox(outbuf + slen, ",\"c\":\"");
for (j = 0; j < STP_CNT_N; j++) {
pi_u32 = stp_cnt[j][i]; u32hex_html();
}
stp_byte_html(stp_bpdu_age[i] >> 8);
stp_byte_html(stp_bpdu_age[i]);
slen += strtox(outbuf + slen, "\"},");
}
slen -= 1;
slen += strtox(outbuf + slen, "]}");
}
+13 -3
View File
@@ -1,9 +1,14 @@
CODE_LOCATION=0x1000
INSTALLER_ADDRESS=0x1100
CC = sdcc
SDCC_PREFIX = ../toolchain/sdcc-$(shell cat ../sdcc/* | cksum | cut -d' ' -f1)
ifeq ($(origin CC),default)
SDCC = $(SDCC_PREFIX)/bin/sdcc
CC = $(SDCC)
ASM = $(SDCC_PREFIX)/bin/sdas8051
endif
CC_FLAGS = -mmcs51
ASM = sdas8051
ASM ?= sdas8051
AFLAGS= -plosgff
BUILDDIR = output
@@ -13,9 +18,14 @@ OBJS = ${SRCS:%.c=$(BUILDDIR)/%.rel}
all: create_build_dir $(BUILDDIR)/updatebuilder $(BUILDDIR)/rtlplayground_oem_upgrade.bin
create_build_dir:
create_build_dir: $(SDCC)
mkdir -p $(BUILDDIR)
ifneq ($(SDCC),)
$(SDCC):
sh ../sdcc/build.sh $(abspath $(SDCC_PREFIX))
endif
$(BUILDDIR)/updatebuilder: updatebuilder.c
gcc $^ -o $@
+1 -1
View File
@@ -348,7 +348,7 @@ void installer(void)
{
CKCON = 0; // Initial Clock configuration
SFR_97 = 0; // HADDR?
MAP_FLASH_PAGE_TO_XDATA = 0; // HADDR?
// Set in managed mode:
SFR_b9 = 0x00;
-4
View File
@@ -84,10 +84,6 @@ struct sfp_port
struct machine {
char machine_name[30];
uint8_t isRTL8373;
// Lowest logical port number
uint8_t min_port;
// Highest logical port number
uint8_t max_port;
uint8_t n_sfp;
uint8_t n_10g;
uint8_t log_to_phys_port[9];
+31
View File
@@ -0,0 +1,31 @@
#include "machine_defs.h"
__code const struct machine machine = {
MACHINE_COMMON_SETTING,
.mac_flash_offset = 0x1FC000,
.n_sfp = 1,
.log_to_phys_port = {1, 2, 3, 4, 5, 6, 7, 8, 9},
.phys_to_log_port = {0, 1, 2, 3, 4, 5, 6, 7, 8},
.is_sfp = {0, 0, 0, 0, 0, 0, 0, 0, 1},
.sfp_port[0].pin_detect = GPIO38,
.sfp_port[0].pin_los = GPIO_NA,
.sfp_port[0].pin_tx_disable = GPIO_NA,
.sfp_port[0].sds = 1,
.sfp_port[0].i2c = I2CBUS( GPIO39_I2C_SDA4, GPIO40_I2C_SCL3_MDC1 ),
.reset_pin = GPIO48_I2C_SCL1,
.high_leds = { .mux = LED_28_SYS | LED_29, .enable = LED_28_SYS | LED_29 },
.port_led_set = { 0, 0, 0, 0, 0, 0, 0, 0, 1},
.led_mux = { 0x00, 0x01, 0x04, 0x05, 0x08, 0x09, 0x0c, 0x09, 0x0d, 0x10, 0x11, 0x0e, 0x14, 0x11, 0x12, 0x15, 0x15, 0x16, 0x18, 0x19, 0x1a, 0x19, 0x1d, 0x1e, 0x1c, 0x1d, 0x20, 0x21 },
.led_mux_custom = 1,
.led_sets = { { LEDS_2G5 | LEDS_LINK | LEDS_ACT,
LEDS_1G | LEDS_100M | LEDS_10M | LEDS_LINK | LEDS_ACT,
LEDS_DUPLEX,
LEDS_2G5 | LEDS_LINK | LEDS_ACT
},
{ LEDS_2G5 | LEDS_1G | LEDS_100M | LEDS_LINK | LEDS_ACT,
LEDS_10G | LEDS_LINK | LEDS_ACT,
LEDS_2G5 | LEDS_LINK,
LEDS_COL | LEDS_DUPLEX
},
},
};
@@ -0,0 +1,6 @@
#include "machine_init_defs.h"
void machine_custom_init(void) __banked
{
REG_SET(RTL837X_REG_LED_GLB_IO_EN, 0x7f24977f);
}
+2 -1
View File
@@ -2,6 +2,7 @@
__code const struct machine machine = {
MACHINE_COMMON_SETTING,
.mac_flash_offset = 0x1FC000,
.n_sfp = 2,
.log_to_phys_port = {0, 0, 0, 5, 1, 2, 3, 4, 6},
.phys_to_log_port = {4, 5, 6, 7, 3, 8, 0, 0, 0},
@@ -21,7 +22,7 @@ __code const struct machine machine = {
.sfp_port[1].sds = 1,
.sfp_port[1].i2c = I2CBUS( GPIO39_I2C_SDA4, GPIO40_I2C_SCL3_MDC1 ),
.reset_pin = GPIO_NA,
.reset_pin = GPIO48_I2C_SCL1,
.high_leds = { .mux = LED_28_SYS | LED_29, .enable = LED_27 | LED_28_SYS | LED_29 },
.port_led_set = { 0, 0, 0, 1, 0, 0, 0, 0, 1},
.led_sets = {
+54
View File
@@ -0,0 +1,54 @@
#include "machine_defs.h"
__code const struct machine machine = {
MACHINE_COMMON_SETTING,
.n_sfp = 2,
.log_to_phys_port = {0, 0, 0, 5, 1, 2, 3, 4, 6},
.phys_to_log_port = {4, 5, 6, 7, 3, 8, 0, 0, 0},
.is_sfp = {0, 0, 0, 1, 0, 0, 0, 0, 2},
// SFP port on SDS0 / logical port 3
.sfp_port[0].pin_detect = GPIO37,
.sfp_port[0].pin_los = GPIO_NA,
.sfp_port[0].pin_tx_disable = GPIO_NA,
.sfp_port[0].sds = 0,
.sfp_port[0].i2c = I2CBUS( GPIO41_I2C_SDA3_MDIO1, GPIO40_I2C_SCL3_MDC1 ),
// SFP port on SDS1 / logical port 8
.sfp_port[1].pin_detect = GPIO38,
.sfp_port[1].pin_los = GPIO_NA,
.sfp_port[1].pin_tx_disable = GPIO_NA,
.sfp_port[1].sds = 1,
.sfp_port[1].i2c = I2CBUS( GPIO39_I2C_SDA4, GPIO40_I2C_SCL3_MDC1 ),
.reset_pin = GPIO_NA,
.high_leds = { .mux = LED_28_SYS | LED_29, .enable = LED_27 | LED_28_SYS | LED_29 },
.port_led_set = { 0, 0, 0, 1, 0, 0, 0, 0, 1 },
.led_sets = {
{
LEDS_2G5 | LEDS_LINK | LEDS_ACT,
LEDS_1G | LEDS_100M | LEDS_10M | LEDS_LINK | LEDS_ACT,
0,
0
},
{
LEDS_2G5 | LEDS_1G | LEDS_100M | LEDS_LINK | LEDS_ACT,
LEDS_10G | LEDS_LINK | LEDS_ACT,
0,
0
},
},
.led_mux_custom = 1,
.led_mux = {
0x00, 0x01, 0x04, 0x05, 0x08,
0x09, 0x0c, 0x3f, 0x0d, 0x10,
0x11, 0x3f, 0x14, 0x3f, 0x3f,
0x15, 0x3f, 0x3f, 0x18, 0x3f,
0x3f, 0x19, 0x3f, 0x3f, 0x1c,
0x1d, 0x20, 0x21
},
};
// Boot 1-4G 6Y
// 1-4Y 5G 6Y
+7
View File
@@ -0,0 +1,7 @@
#ifndef _MACHINE_SWTG024AS_V2_1_0_H_
#define _MACHINE_SWTG024AS_V2_1_0_H_
#define MACHINE_MODEL "SWTG024AS-V2.1.0"
#define MACHINE_CHIPSET RTL8372
#endif
+22
View File
@@ -0,0 +1,22 @@
#include "machine_init_defs.h"
void machine_custom_init(void) __banked
{
// Stock LED_MODE: [8:5]=1, [12:9]=2 (leds_setup hard-codes 5/3); same as clearing bits 7 and 9
reg_bit_set(RTL837X_REG_LED_MODE, 17);
reg_bit_clear(RTL837X_REG_LED_MODE, 9);
reg_bit_clear(RTL837X_REG_LED_MODE, 7);
// Stock LED_GLB_IO_EN [27:0] = 0x0f249740: enable LED pins 6, 8, 9, 10, 12, 15, 18, 21, 24-27
reg_bit_set(RTL837X_REG_LED_GLB_IO_EN, 6);
reg_bit_set(RTL837X_REG_LED_GLB_IO_EN, 8);
reg_bit_set(RTL837X_REG_LED_GLB_IO_EN, 9);
reg_bit_set(RTL837X_REG_LED_GLB_IO_EN, 10);
reg_bit_set(RTL837X_REG_LED_GLB_IO_EN, 12);
reg_bit_set(RTL837X_REG_LED_GLB_IO_EN, 15);
reg_bit_set(RTL837X_REG_LED_GLB_IO_EN, 18);
reg_bit_set(RTL837X_REG_LED_GLB_IO_EN, 21);
reg_bit_set(RTL837X_REG_LED_GLB_IO_EN, 24);
reg_bit_set(RTL837X_REG_LED_GLB_IO_EN, 25);
reg_bit_set(RTL837X_REG_LED_GLB_IO_EN, 26);
}
+1
View File
@@ -0,0 +1 @@
PCB_AFGW_SWTG218ANS_V_2_0_2.c
+7
View File
@@ -0,0 +1,7 @@
#ifndef _MACHINE_SWTG218ANS_V_2_0_2_H_
#define _MACHINE_SWTG218ANS_V_2_0_2_H_
#define MACHINE_MODEL "RealHD PSW8-25MG"
#define MACHINE_CHIPSET RTL8373
#endif
+1
View File
@@ -0,0 +1 @@
PCB_AFGW_SWTG218ANS_V_2_0_2_init.c
+53
View File
@@ -0,0 +1,53 @@
#include "machine_defs.h"
// Sirivision S25G1109FP V1.3 (label: SR-S25G2109F): 8x2.5G RJ45 + 1x10G SFP+,
// 2 MB flash, RTL8373 (non-N) + RTL8224. Derived from the KP-9000-9XHML-X V3.1
// definition; every value below was confirmed on hardware, see the device doc
// in doc/devices/S25G1109FP_V1_3.md.
__code const struct machine machine = {
MACHINE_COMMON_SETTING,
.n_sfp = 1,
.log_to_phys_port = {1, 2, 3, 4, 5, 6, 7, 8, 9},
.phys_to_log_port = {0, 1, 2, 3, 4, 5, 6, 7, 8},
.is_sfp = {0, 0, 0, 0, 0, 0, 0, 0, 1},
/* ModAbs is GPIO30 (low with a module inserted, high with an empty cage),
* LOS is GPIO37 (high without light). GPIO38, the ModAbs pin of the
* KP-9000 8+1 reference, is tied low here and never changes, which made the
* firmware believe a module was always present. */
.sfp_port[0].pin_detect = GPIO30_ACL_BIT3_EN,
.sfp_port[0].pin_los = GPIO37,
.sfp_port[0].pin_tx_disable = GPIO_NA,
.sfp_port[0].sds = 1,
.sfp_port[0].i2c = I2CBUS( GPIO39_I2C_SDA4, GPIO40_I2C_SCL3_MDC1 ),
/* Reset button: GPIO54 reads high when idle and low while pressed.
* GPIO48, used by the KP-9000 reference, never changes on this board. */
.reset_pin = GPIO54_ACL_BIT2_EN,
.high_leds = { .mux = LED_27 | LED_29, .enable = LED_28_SYS | LED_29 },
.port_led_set = { 0, 0, 0, 0, 0, 0, 0, 0, 1},
.led_sets = {
{ /* RJ45: left LED = link/activity, right LED = 2.5G */
LEDS_2G5 | LEDS_1G | LEDS_100M | LEDS_10M | LEDS_LINK | LEDS_ACT,
/* speed-only conditions leave the LED dark: the LINK bit has to be
* set as well, otherwise the LED controller stays off */
LEDS_2G5 | LEDS_LINK,
0,
0,
}, { /* SFP PORT, SINGLE GREEN LED */
LEDS_2G5 | LEDS_1G | LEDS_100M | LEDS_10M | LEDS_LINK | LEDS_ACT | LEDS_10G,
0,
0,
0,
}},
/* 6 bits per LED pin, encoded as (port << 2) | led. This is the chip
* default for port-ordered wiring (pin 3n+m = port n+1, function m) with
* one change: pin 25 drives the SFP LED with function 0, the only function
* populated in led_sets[1]. See the device doc for the details. */
.led_mux_custom = 1,
.led_mux = {0x00, 0x01, 0x02, 0x04, 0x05, 0x06, 0x08, 0x09, 0x0a, 0x0c,
0x0d, 0x0e, 0x10, 0x11, 0x12, 0x14, 0x15, 0x16, 0x18, 0x19,
0x1a, 0x1c, 0x1d, 0x1e, 0x20, 0x20, 0x22, 0x23},
/* Factory MAC at flash 0x1FC000, verified in the stock dump. The stock
* value is a locally administered address, which is rejected, so a
* UUID-derived MAC is used unless a valid one is stored here. */
.mac_flash_offset = 0x1FC000,
};
+10
View File
@@ -0,0 +1,10 @@
#ifndef _MACHINE_S25G1109FP_V1_3_H_
#define _MACHINE_S25G1109FP_V1_3_H_
/* machine_name[] in struct machine is 30 bytes -> keep this <= 29 chars */
/* The product is sold as SR-S25G2109F; S25G1109FP is the PCB silkscreen and
* belongs to a different (unmanaged PoE) model that shares this PCB. */
#define MACHINE_MODEL "Sirivision SR-S25G2109F"
#define MACHINE_CHIPSET RTL8373
#endif
+1
View File
@@ -2,6 +2,7 @@
__code const struct machine machine = {
MACHINE_COMMON_SETTING,
.mac_flash_offset = 0x1FC000,
.n_sfp = 2,
.log_to_phys_port = {0, 0, 0, 6, 1, 2, 3, 4, 5},
.phys_to_log_port = {4, 5, 6, 7, 8, 3, 0, 0, 0},
+1
View File
@@ -2,6 +2,7 @@
__code const struct machine machine = {
MACHINE_COMMON_SETTING,
.mac_flash_offset = 0x1FC000,
.n_sfp = 1,
.log_to_phys_port = {1, 2, 3, 4, 5, 6, 7, 8, 9},
.phys_to_log_port = {0, 1, 2, 3, 4, 5, 6, 7, 8},
+1 -3
View File
@@ -43,8 +43,6 @@
#define MACHINE_COMMON_SETTING \
.machine_name = MACHINE_MODEL, \
.isRTL8373 = MACHINE_CHIPSET == RTL8373, \
.min_port = MACHINE_MIN_PORT, \
.max_port = MACHINE_MAX_PORT
.isRTL8373 = MACHINE_CHIPSET == RTL8373
#endif
+3
View File
@@ -19,6 +19,8 @@
#define MAC_ADDR_LEN 6
#define PORT_BIT(p) ((uint16_t)1 << (p))
#define NUL '\0'
#define STR_(x) #x
#define STR(x) STR_(x)
// Define Port-masks for 9-port devices and 6-port devices
#define PMASK_9 0x1ff
@@ -154,6 +156,7 @@ void print_long(uint32_t a);
void print_short(uint16_t a);
void print_byte(uint8_t a);
void itoa(uint8_t v);
void itoa_no_syslog(uint8_t v);
void itoa_short(uint16_t v);
void print_sfr_data(void);
void print_phy_data(void);
+4 -2
View File
@@ -221,9 +221,11 @@ void rtl8372_init(void) __banked
} else if(machine.n_10g == 2) {
REG_SET(RTL837X_REG_SDS_MODES, 0x1ad); // Both 10g ports use SDS_QXGMII
} else {
// Replace the reset value 0xbff (both SDS off) instead of ORing into it:
// RTL837X_REG_SEC_COUNTER only holds a written value while SDS 0 is on
reg_read_m(RTL837X_REG_SDS_MODES);
sfr_mask_data(1, 0, 0x03);
sfr_mask_data(0, 0, 0xe2);
sfr_mask_data(1, 0xff, 0x03);
sfr_mask_data(0, 0xff, 0xe2);
reg_write_m(RTL837X_REG_SDS_MODES);
}
+2 -2
View File
@@ -271,7 +271,7 @@ void vlan_setup(void) __banked
wait_table_ready();
// Set PVID 1 for every port. TODO: Skip unused ports!
for (uint8_t i = MACHINE_MIN_PORT; i <= MACHINE_MAX_PORT + 1; i++) { // Do this also for the CPU port (+1)
for (uint8_t i = MACHINE_MIN_PORT; i <= CPU_PORT; i++) { // CPU MAC is independent of the last user port
uint16_t reg = RTL837x_PVID_BASE_REG + ((i >> 1) << 2);
#ifdef DEBUG
print_byte(i); write_char(':'); write_char(' '); print_short(reg); write_char('=');
@@ -307,7 +307,7 @@ void vlan_setup(void) __banked
REG_SET(RTL837X_VLAN_L2_LRN_DIS_0, 0);
REG_SET(RTL837X_VLAN_L2_LRN_DIS_1, 0);
// Enable VLAN 1: Ports 0-9, i.e. including the CPU port are untagged members
// Enable VLAN 1: user ports untagged, CPU MAC9 tagged (untag bit 19 clear)
REG_SET(RTL837x_TBL_DATA_IN_A, machine_detected.isRTL8373? 0x0207ffff : 0x0207e3f8); // 02: Entry valid, 7...: membership
REG_SET(RTL837X_TBL_CTRL, 0x00010303); // Write VLAN 1
+1 -1
View File
@@ -55,7 +55,7 @@ __sfr __at(0xbb) SFR_BANK_RET;
__sfr __at(0x8e) CKCON;
__sfr __at(0x97) SFR_97; // HADDR?
__sfr __at(0x97) MAP_FLASH_PAGE_TO_XDATA; // 1 (hardware reset default): xdata 0x4000-0xbfff reads the flash image; 0: RAM
__sfr __at(0xb9) SFR_b9;
__sfr __at(0xba) SFR_ba;
+892 -488
View File
File diff suppressed because it is too large. Load diff
+31 -7
View File
@@ -2,13 +2,21 @@
#define _RTL837X_STP_H_
#include <stdint.h>
#include "rtl837x_common.h"
void stp_in(void) __banked;
void stp_setup(void) __banked;
void stp_timers(void) __banked;
void stp_off(void) __banked;
void stp_parse(void) __banked __reentrant;
void stp_defaults(void) __banked;
void stp_status(void) __banked;
void stp_port_admin(uint8_t port, uint8_t on) __banked;
void stp_prio_apply(void) __banked;
void stp_port_mcheck(uint8_t port) __banked;
uint8_t stp_port_role(uint8_t port) __banked;
uint8_t stp_port_state(uint8_t port) __banked;
uint8_t stp_ent_id(uint8_t e) __banked;
void stp_lag_map(void) __banked;
void stp_counters_clear(void) __banked __reentrant;
/* Tick rate of stp_timers(), also used by the web UI. */
#define STP_HZ 50
@@ -43,22 +51,38 @@ extern __xdata uint8_t stp_txhold;
#define STP_PF_TRIPPED 0x80 /* runtime: disabled by BPDU guard */
extern __xdata uint8_t stp_pflags[STP_ENTITIES];
extern __xdata uint32_t stp_pcost[STP_ENTITIES]; /* path cost; 0 = auto (20000) */
extern __xdata uint32_t stp_pcost[STP_ENTITIES];
extern __xdata uint8_t stp_pprio[STP_ENTITIES];
extern __xdata uint8_t stp_pp2p[STP_ENTITIES];
extern __xdata uint8_t stp_ent_of[STP_PORTS]; /* the STP entity a port answers to: itself, or its lag */
extern __xdata uint16_t stp_lag_mask[STP_LAG_COUNT]; /* member ports of each lag; 0 = not part of STP */
extern __xdata uint8_t stp_ent_of[STP_PORTS];
extern __xdata uint16_t stp_lag_mask[STP_LAG_COUNT];
/* Last-heard designated info per port (from received BPDUs); consult
* stp_bpdu_age to decide whether it is still current. */
extern __xdata struct bridge stp_dbridge[STP_ENTITIES];
extern __xdata uint16_t stp_dpid[STP_ENTITIES];
extern __xdata uint32_t stp_dcost[STP_ENTITIES];
extern __xdata uint16_t stp_bpdu_age[STP_ENTITIES]; /* ticks since a BPDU was heard */
extern __xdata uint16_t stp_bpdu_age[STP_ENTITIES];
#define STP_CNT_RX 0 /* BPDUs taken in on an STP port */
#define STP_CNT_TX 1 /* BPDUs put on the wire */
#define STP_CNT_TCRX 2 /* BPDUs received with the TC flag, TCNs */
#define STP_CNT_TCTX 3 /* BPDUs sent with the TC flag, TCNs */
#define STP_CNT_N 4
extern __xdata uint32_t stp_cnt[STP_CNT_N][STP_ENTITIES];
extern __xdata uint16_t stp_info_while[STP_ENTITIES];
extern __xdata struct bridge root_bridge;
extern __xdata uint32_t root_bridge_cost;
extern __xdata uint8_t stp_root_port;
extern __xdata uint8_t stp_root_maxage;
extern __xdata uint8_t stp_root_fwd;
extern __xdata uint16_t stp_link_prev;
extern __xdata uint16_t stp_alt;
extern __xdata uint16_t stp_backup;
extern __xdata uint16_t stp_legacy;
extern __xdata uint16_t stp_rrwhile[STP_ENTITIES];
extern __xdata uint16_t stp_rbwhile[STP_ENTITIES];
extern __xdata uint16_t stp_tc_count;
extern __xdata uint32_t stp_tc_secs;
extern __xdata uint8_t stp_pcost_short;
extern __xdata uint8_t stp_bpdu_filter;
#endif
+228
View File
@@ -0,0 +1,228 @@
/*
* Command line interface of the Spanning Tree Protocol for the RTL837x platform
* This code is in the Public Domain
*/
#include <stdint.h>
#include "rtl837x_common.h"
#include "rtl837x_stp.h"
#include "machine.h"
#pragma codeseg BANK2
#pragma constseg BANK2
extern __xdata uint8_t cmd_buffer[CMD_BUF_SIZE];
extern __xdata uint8_t cmd_words_len;
extern __xdata uint8_t cmd_words_b[15];
uint8_t cmd_compare(uint8_t start, __code const uint8_t * cmd);
uint8_t atoi_byte(uint8_t idx);
uint8_t cmd_parse_port_separator(uint8_t idx);
extern __xdata uint8_t atoi_results_u8;
extern __xdata char save_cmd;
extern __xdata uint8_t err_status;
#define TIMES_OK(h, m, f) ((uint16_t)2 * ((f) - 1) >= (m) && (m) >= (uint16_t)2 * ((h) + 1))
__xdata uint32_t stp_cli_cost;
__xdata uint8_t stp_cli_val;
void stp_parse(void) __banked __reentrant
{
uint8_t port;
if (cmd_compare(1, "on")) {
print_string("STP enabled\n");
stp_enabled = 1;
stp_setup();
return;
}
if (cmd_compare(1, "off")) {
print_string("STP disabled\n");
stp_off();
stp_enabled = 0;
return;
}
if (cmd_compare(1, "status")) {
stp_status();
return;
}
if (cmd_compare(1, "clear")) {
stp_counters_clear();
return;
}
if (cmd_words_len < 3)
goto err;
if (cmd_compare(1, "port") || cmd_compare(1, "lag")) {
if (cmd_words_len < 4)
goto err;
stp_lag_map();
if (cmd_compare(1, "lag")) {
if (!atoi_byte(cmd_words_b[2]) || !atoi_results_u8 || atoi_results_u8 > STP_LAG_COUNT)
goto err;
port = STP_LAG_BASE - 1 + atoi_results_u8;
} else {
if (!cmd_parse_port_separator(cmd_words_b[2]))
goto err;
port = atoi_results_u8;
if (stp_ent_of[port] != port) {
print_string("Error: the port is in lag ");
write_char('1' + stp_ent_of[port] - STP_LAG_BASE);
write_char('\n');
err_status = ERR_INVALID_ARGUMENT;
return;
}
}
if (cmd_words_len < 5 && !cmd_compare(3, "on") && !cmd_compare(3, "off")
&& !cmd_compare(3, "mcheck"))
goto err;
if (cmd_compare(3, "on")) {
stp_pflags[port] |= STP_PF_ENABLED;
stp_pflags[port] &= ~STP_PF_TRIPPED;
if (stp_enabled) { /* (re)join: listen first */
stp_port_admin(port, 1);
}
} else if (cmd_compare(3, "mcheck")) {
if (stp_enabled)
stp_port_mcheck(port);
} else if (cmd_compare(3, "off")) {
stp_pflags[port] &= ~STP_PF_ENABLED;
if (stp_enabled)
stp_port_admin(port, 0); /* plain forwarding */
} else if (cmd_compare(3, "edge")) {
/* Also drop the *operational* edge flag: it is what exempts the
* port from topology changes and lets it skip the listen period,
* so leaving it set would keep the old behaviour until the next
* "stp off"/"stp on". An admin edge is operational immediately. */
stp_pflags[port] &= ~(STP_PF_ADMEDGE | STP_PF_AUTOEDGE | STP_PF_OPEREDGE);
if (cmd_compare(4, "on"))
stp_pflags[port] |= STP_PF_ADMEDGE | STP_PF_OPEREDGE;
else if (cmd_compare(4, "auto"))
stp_pflags[port] |= STP_PF_AUTOEDGE;
else if (!cmd_compare(4, "off"))
goto err;
} else if (cmd_compare(3, "cost")) {
/* raw 802.1D value, 0..200000000; 0 = auto (speed-based) */
stp_cli_cost = 0;
{
__xdata uint8_t *cp = &cmd_buffer[cmd_words_b[4]];
if (*cp < '0' || *cp > '9')
goto err;
while (*cp >= '0' && *cp <= '9') {
stp_cli_cost = stp_cli_cost * 10 + (*cp - '0');
cp++;
}
}
if (stp_cli_cost > 200000000UL)
goto err;
stp_pcost[port] = stp_cli_cost;
} else if (cmd_compare(3, "p2p")) {
if (cmd_compare(4, "auto"))
stp_pp2p[port] = 0;
else if (cmd_compare(4, "on"))
stp_pp2p[port] = 1;
else if (cmd_compare(4, "off"))
stp_pp2p[port] = 2;
else
goto err;
} else if (cmd_compare(3, "prio")) {
if (!atoi_byte(cmd_words_b[4]))
goto err;
if (atoi_results_u8 > 240 || (atoi_results_u8 & 0x0f))
goto err;
stp_pprio[port] = atoi_results_u8;
} else if (cmd_compare(3, "guard")) {
stp_pflags[port] &= ~(STP_PF_BPDUGUARD | STP_PF_ROOTGUARD);
if (cmd_compare(4, "bpdu"))
stp_pflags[port] |= STP_PF_BPDUGUARD;
else if (cmd_compare(4, "root"))
stp_pflags[port] |= STP_PF_ROOTGUARD;
else if (!cmd_compare(4, "none"))
goto err;
} else if (cmd_compare(3, "filter")) {
if (cmd_compare(4, "on"))
stp_pflags[port] |= STP_PF_FILTER;
else if (cmd_compare(4, "off"))
stp_pflags[port] &= ~STP_PF_FILTER;
else
goto err;
} else {
goto err;
}
return;
}
if (!atoi_byte(cmd_words_b[2])) {
if (cmd_compare(1, "version")) {
if (cmd_compare(2, "rstp"))
stp_rstp = 1;
else if (cmd_compare(2, "stp"))
stp_rstp = 0;
else
goto err;
return;
}
if (cmd_compare(1, "pathcost")) {
if (cmd_compare(2, "long"))
stp_pcost_short = 0;
else if (cmd_compare(2, "short"))
stp_pcost_short = 1;
else
goto err;
return;
}
if (cmd_compare(1, "bpdu")) {
if (cmd_compare(2, "filter"))
stp_bpdu_filter = 1;
else if (cmd_compare(2, "flood"))
stp_bpdu_filter = 0;
else
goto err;
if (!stp_enabled)
stp_off();
return;
}
goto err;
}
stp_cli_val = atoi_results_u8;
if (cmd_compare(1, "prio")) {
if (stp_cli_val > 15)
goto err;
stp_prio = stp_cli_val << 4; /* n * 4096, as the BPDU's high byte */
stp_prio_apply();
} else if (cmd_compare(1, "hello")) {
if (stp_cli_val < 1 || stp_cli_val > 10)
goto err;
if (save_cmd && !TIMES_OK(stp_cli_val, stp_maxage_s, stp_fwddelay_s))
goto times;
stp_hello_s = stp_cli_val;
} else if (cmd_compare(1, "maxage")) {
if (stp_cli_val < 6 || stp_cli_val > 40)
goto err;
if (save_cmd && !TIMES_OK(stp_hello_s, stp_cli_val, stp_fwddelay_s))
goto times;
stp_maxage_s = stp_cli_val;
} else if (cmd_compare(1, "fwd")) {
if (stp_cli_val < 4 || stp_cli_val > 30)
goto err;
if (save_cmd && !TIMES_OK(stp_hello_s, stp_maxage_s, stp_cli_val))
goto times;
stp_fwddelay_s = stp_cli_val;
} else if (cmd_compare(1, "txhold")) {
if (stp_cli_val < 1 || stp_cli_val > 10)
goto err;
stp_txhold = stp_cli_val;
} else {
goto err;
}
return;
times:
print_string("Error: needs 2*(fwd-1) >= maxage >= 2*(hello+1)\n");
err_status = ERR_INVALID_ARGUMENT;
return;
err:
err_status = ERR_INVALID_ARGUMENT;
print_string("Error: stp on|off|status|clear | prio <0-15> | hello <1-10> | maxage <6-40> | fwd <4-30> | txhold <1-10> | version rstp|stp | pathcost long|short | bpdu filter|flood | port <1-9>|lag <1-4> on|off|mcheck|edge|cost|prio|guard|filter ...\n");
}
+23 -2
View File
@@ -305,6 +305,19 @@ void itoa(uint8_t v)
}
void itoa_no_syslog(uint8_t v)
{
uint8_t t = (v / 100);
uint8_t print_zeros = t;
if (print_zeros)
write_char_no_syslog('0' + t);
t = (v / 10) % 10;
print_zeros |= t;
if (print_zeros)
write_char_no_syslog('0' + t);
write_char_no_syslog('0' + (v % 10));
}
/* Same as itoa(), one decade wider: enough for a port number. Kept separate
* rather than widening itoa() itself, because every existing caller passes a
* byte and would start paying for 16-bit divisions it does not need. */
@@ -1203,7 +1216,7 @@ static void handle_tick(void)
lacp_timers();
health_phase(HEALTH_PH_STP);
lldp_tick();
health_phase(HEALTH_LLDP);
health_phase(HEALTH_PH_LLDP);
cabletest_tick();
}
@@ -1554,6 +1567,15 @@ void check_and_flash_update_image(void)
}
/* Called by the SDCC startup code before it copies XINIT to XISEG and clears XSEG.
* Hardware reset starts us with MAP_FLASH_PAGE_TO_XDATA = 1, which maps xdata
* 0x4000-0xbfff to the flash image (reads return flash, writes are lost), so it
* must be 0 by then. */
unsigned char __sdcc_external_startup(void)
{
MAP_FLASH_PAGE_TO_XDATA = 0;
return 0;
}
void main(void)
{
@@ -1564,7 +1586,6 @@ void main(void)
sbuf_ptr = 0;
CKCON = 0; // Initial Clock configuration
SFR_97 = 0; // HADDR?
// Set in managed mode:
SFR_b9 = 0x00;
+410
View File
@@ -0,0 +1,410 @@
; Assembler file to intercept register writes/reads and PHY writes/reads
; assemble with as31
; Compile with
; as31 -Fbyte -Orwintercept_zoerax_hg0402xg.hex rwintercept_zoerax_hg0402xg.asm
; ./injector zoerax_hg0402xg.bin RTLPlayground/rwintercept_zoerax_hg0402xg.hex zoerax_hg0402xg_patched.bin
; Start at 0x3b00, where there is always some space in BANK 0
.equ valptr, 0xc7
.equ SFR_DATA_0, 0xa7
.equ SFR_DATA_8, 0xa6
.equ SFR_DATA_16, 0xa5
.equ SFR_DATA_24, 0xa4
; Intercept Register Write
.org 0x4d62
lcall reg_write
nop
; Intercept Register Read
.org 0x4dad
lcall reg_read_head
.org 0x4e6f
lcall reg_read_tail
; Intercept PHY write
.org 0x522c
lcall phy_write
; Intercept PHY read
.org 0x526f
lcall phy_read_head
.org 0x5295
ljmp phy_read_tail
; Intercept SDS write
.org 0x503c
lcall sds_write
nop
; Intercept SDS read
.org 0x5061
lcall sds_read_head
nop
.org 0x507b
ljmp sds_read_tail
;
; BEGIN actual code at unused end of BANK0
;
.org 0x3b00
;
; print the address of a register write. The address is in R6/R7. A and r4 can be scratched
;
reg_write:
mov a, #'R'
acall putc
mov a, r6
acall wbyte
mov a, r7
acall wbyte
mov a, #'-'
acall putc
mov dptr, #valptr
movx a, @dptr
acall wbyte
mov dptr, #valptr + 1
movx a, @dptr
acall wbyte
mov dptr, #valptr + 2
movx a, @dptr
acall wbyte
mov dptr, #valptr + 3
movx a, @dptr
acall wbyte
mov a, #' '
acall putc
mov a, r7
mov r5, a
mov a, r6
mov r4, a
ret
;
; print the address of a register read The address is in R2/R3. Registers 4-7 are scratch
;
reg_read_head:
mov a, #'r'
acall putc
mov a, r2
acall wbyte
mov a, r3
acall wbyte
mov a, #':'
acall putc
clr a
mov r7, a
mov r6, a
mov r5, a
mov r4, a
ret
reg_read_tail:
mov dptr, #0xc5
movx a, @dptr
acall wbyte
mov dptr, #0xc5 + 1
movx a, @dptr
acall wbyte
mov dptr, #0xc5 + 2
movx a, @dptr
acall wbyte
mov dptr, #0xc5 + 3
movx a, @dptr
acall wbyte
mov a, #' '
acall putc
mov dptr,#0xc5
ret
;
; print phy write options
;
phy_write:
mov a, #'P'
acall putc
mov a, r4
acall wbyte
; phymask
mov a, r6
acall wbyte
mov a, r7
acall wbyte
mov a, #'.'
acall putc
; dev_id?
mov a, r5
acall wbyte
; x_phywrite_opt
mov dptr, #0xf1
movx a, @dptr
acall wbyte
; The register going to SFR_C2 and SFR_C3
mov a, r2
acall wbyte
mov a, r3
acall wbyte
mov a, #':'
acall putc
mov dptr, #0xf2
movx a, @dptr
acall wbyte
mov dptr, #0xf3
movx a, @dptr
acall wbyte
mov a, #' '
acall putc
mov dptr, #0xeb
mov r4, #0
ret
;
; print phy read options
;
phy_read_head:
mov r1, 4 ; actually r4
mov a, #'p'
acall putc
mov a, r7
acall wbyte
mov a, r5
acall wbyte
mov a, #'.'
acall putc
mov a, r2
acall wbyte
mov a, r3
acall wbyte
mov a, #':'
acall putc
mov r1, 7
mov a, r2
ret
phy_read_tail:
mov r6, 4
mov a, r6
acall wbyte
mov a, r7
acall wbyte
mov a, #' '
acall putc
mov r4, 6
ret
;
; print SDS write options
;
sds_write:
mov a, r4
mov r2, a ; R2 is scratch
mov a, #'Q'
acall putc
mov a, r7
acall wbyte
mov a, r5
acall wbyte
mov a, r3
acall wbyte
mov a, #':'
acall putc
mov dptr, #0xe1
movx a, @dptr
acall wbyte
mov dptr, #0xe2
movx a, @dptr
acall wbyte
mov a, #' '
acall putc
mov a, r7
mov r1, a
mov a, r5
mov r7, a
mov a, r2
mov r4, a
ret
;
; print SDS read options
;
sds_read_head:
mov a, r4
mov r1, a ; R1 is scratch
mov a, #'q'
acall putc
mov a, r7 ; sds_index bit
acall wbyte
mov a, r5 ; SFR_94 (2 x r5 | r7)
acall wbyte
mov a, r3 ; SFR_93
acall wbyte
mov a, #':'
acall putc
mov a, r1
mov r4, a ; R1 is scratch
mov a, r7
mov r1, a
mov a, r5
mov r7, a
ret
;
; print SDS read result
;
sds_read_tail:
mov a, r4
mov r6, a ; R6 is returned and preserved
mov a, r7 ; = r7 = SFR_DATA_8
acall wbyte
mov r7, SFR_DATA_0
mov a, r7 ; SFR_DATA_0
acall wbyte
mov a, #' '
acall putc
mov a, r6 ; R6 is returned and preserved
mov r4, a
ret
;
; Write a byte as hex to serial
;
nop
nop
wbyte:
mov r4, a
swap a
anl a, #0x0f
mov dptr,#hex
movc a, @a+dptr
jb ET0, wbyte1
ret
wbyte1:
jbc TI, wbyte2
sjmp wbyte1
wbyte2:
mov SBUF,a
mov a, r4
anl a, #0x0f
mov dptr,#hex
movc a, @a+dptr
wbyte3:
jbc TI, wbyte4
sjmp wbyte3
wbyte4:
mov SBUF,a
ret
;
; Intercept isr_ext0
;
isr_ext0:
mov a, #'i'
acall putc
mov r0,#0x51
inc @r0
ret
;
; Intercept isr_ext1
;
isr_ext1:
mov a, #'I'
acall putc
mov r0,#0x25
mov a, @r0
ret
;
; Intercept isr_ext2
;
isr_ext2:
mov a, #'J'
acall putc
mov r0, #0x1e
mov a, #0xa
ret
;
; Intercept isr_ext3
;
isr_ext3:
mov a, #'j'
acall putc
mov r0,#0x1e
mov a,#0x14
ret
;
; Write a character to serial
;
nop
nop
putc:
jb ET0, putc1
ret
putc1:
jbc TI,putc2
sjmp putc1
putc2:
mov SBUF,a
ret
hex:
.byte "0123456789abcdef"
.end
+15
View File
@@ -0,0 +1,15 @@
https://sourceforge.net/p/sdcc/bugs/4153/
Index: src/mcs51/ralloc.c
===================================================================
--- src/mcs51/ralloc.c (revision 17011)
+++ src/mcs51/ralloc.c (working copy)
@@ -1208,7 +1208,7 @@
int change = 0;
/* if the result has been spilt then cannot share */
- if (opsym->isspilt)
+ if (result->isspilt || opsym->isspilt)
return 0;
for (;;)
+24
View File
@@ -0,0 +1,24 @@
https://sourceforge.net/p/sdcc/bugs/4154/
Index: src/mcs51/ralloc.c
===================================================================
--- src/mcs51/ralloc.c (revision 17011)
+++ src/mcs51/ralloc.c (working copy)
@@ -641,6 +641,7 @@
spillThis (symbol * sym)
{
int i;
+ bool ownsRegs = bitVectBitValue (_G.regAssigned, sym->key);
/* if this is rematerializable or has a spillLocation
we are okay, else we need to create a spillLocation
for it */
@@ -658,7 +659,8 @@
{
if (sym->regs[i])
{
- freeReg (sym->regs[i]);
+ if (ownsRegs)
+ freeReg (sym->regs[i]);
sym->regs[i] = NULL;
}
}
+130
View File
@@ -0,0 +1,130 @@
https://sourceforge.net/p/sdcc/bugs/4070/
Index: sdas/asxxsrc/asexpr.c
===================================================================
--- sdas/asxxsrc/asexpr.c
+++ sdas/asxxsrc/asexpr.c
@@ -863,18 +863,59 @@
return((n & s_mask) ? (n | ~v_mask) : (n & v_mask));
}
-/*)Function void exprmasks(esp)
+/*)Function void exprmasks(n)
*
* int n T Line Bytes in Address
*
* The function exprmasks() configures the assembler
- * for 16, 24, or 32-Bit Data/Addresses.
+ * for 16, 24, or 32-Bit Data/Addresses. The arithmetic
+ * precision follows the address width unless a port
+ * calls valmasks() afterwards.
*
* local variables:
* none
*
* global variables:
* int a_bytes T Line Bytes in Address
+ *
+ * functions called:
+ * void valmasks() asexpr.c
+ *
+ * side effects:
+ * The address width and arithmetic precision
+ * parameters are set.
+ */
+
+void
+exprmasks(int n)
+{
+ switch(n) {
+ default:
+ n = 2;
+ case 2:
+ case 3:
+ case 4:
+ break;
+ }
+ a_bytes = n;
+ valmasks(n);
+}
+
+/*)Function void valmasks(n)
+ *
+ * int n Bytes of arithmetic precision
+ *
+ * The function valmasks() configures the 16, 24, or
+ * 32-Bit arithmetic precision independently of the
+ * T Line address width. A port whose addresses fit
+ * in 24 bits but whose symbols may hold 32-bit values
+ * (the 8051 __sfr32 address lists, for example) uses
+ * exprmasks(3) followed by valmasks(4).
+ *
+ * local variables:
+ * none
+ *
+ * global variables:
* a_uint a_mask Address mask
* a_uint s_mask Sign mask
* a_uint v_mask Value mask
@@ -885,16 +926,13 @@
* side effects:
* The arithmetic precision parameters are set.
*/
-
+
void
-exprmasks(int n)
+valmasks(int n)
{
- a_bytes = n;
-
#ifdef LONGINT
- switch(a_bytes) {
+ switch(n) {
default:
- a_bytes = 2;
case 2:
a_mask = (a_uint) 0x0000FFFFl;
s_mask = (a_uint) 0x00008000l;
@@ -914,9 +952,8 @@
break;
}
#else
- switch(a_bytes) {
+ switch(n) {
default:
- a_bytes = 2;
case 2:
a_mask = (a_uint) 0x0000FFFF;
s_mask = (a_uint) 0x00008000;
Index: sdas/asxxsrc/asxxxx.h
===================================================================
--- sdas/asxxsrc/asxxxx.h
+++ sdas/asxxsrc/asxxxx.h
@@ -1354,6 +1354,7 @@
extern int digit(int c, int r);
extern int is_digit(int c, int r);
extern void exprmasks(int n);
+extern void valmasks(int n);
extern void expr(struct expr *esp, int n);
extern void binop(int c, struct expr *esp, struct expr *re);
extern int is_abs(struct expr *esp);
Index: sdas/as8051/i51mch.c
===================================================================
--- sdas/as8051/i51mch.c
+++ sdas/as8051/i51mch.c
@@ -782,9 +782,12 @@
hilo = 1;
/*
- * Address Space
+ * Address Space: 24-bit addresses in the T lines,
+ * 32-bit arithmetic so that __sfr32 address lists
+ * like 0xa4a5a6a7 keep their most significant byte.
*/
- exprmasks(3);
+ exprmasks(3);
+ valmasks(4);
/*
* First time only:
Executable
+26
View File
@@ -0,0 +1,26 @@
#!/bin/sh
set -eu
V=4.6.0
SHA=5fd6a93e5997ce01756868fe35e441095cfb637894a80c262514a634094973b6
PREFIX=$1
D=$(cd "$(dirname "$0")" && pwd)
W=$(mktemp -d)
trap 'rm -rf "$W"' EXIT
curl -fL -o "$W/sdcc.tar.bz2" "https://downloads.sourceforge.net/project/sdcc/sdcc/$V/sdcc-src-$V.tar.bz2"
if command -v sha256sum >/dev/null; then
echo "$SHA $W/sdcc.tar.bz2" | sha256sum -c -
else
echo "$SHA $W/sdcc.tar.bz2" | shasum -a 256 -c -
fi
tar -C "$W" -xjf "$W/sdcc.tar.bz2"
cd "$W/sdcc-$V"
for p in "$D"/*.patch; do
patch -p0 < "$p"
done
off=""
for p in z80 z180 r2k r2ka r3ka r4k r5k r6k sm83 tlcs90 ez80 ez80_z80 z80n r800 ds390 ds400 pic14 pic16 hc08 s08 stm8 pdk13 pdk14 pdk15 mos6502 mos65c02 huc6280 f8 f8l; do
off="$off --disable-$p-port"
done
./configure --prefix="$PREFIX" $off --disable-sdcdb --disable-ucsim --disable-non-free
make -j"$(getconf _NPROCESSORS_ONLN)"
make install
+128
View File
@@ -0,0 +1,128 @@
/*
* Login sessions of the web interface.
*
* Up to SESSIONS logins at once, each with its own cookie and its own expiry.
* With a single one, logging in from a second browser (or a phone) ended the
* first browser's session at its next request.
*
* This sits in bank 3: the web server's own bank is full, so httpd.c only
* calls in here. It shares three things with httpd.c: the cookie value it
* parsed (session), the time read for the request (now) and the result
* (authenticated); the reply is written to outbuf at slen.
*/
#pragma codeseg BANK3
#pragma constseg BANK3
#include <stdint.h>
#include "rtl837x_common.h"
#include "sessions.h"
extern volatile __xdata uint8_t sfr_data[4];
extern __xdata uint16_t session_timeout;
extern __xdata uint32_t now;
extern __xdata uint8_t authenticated;
extern __xdata uint8_t * __xdata session;
extern __xdata uint8_t outbuf[];
extern __xdata uint16_t slen;
__xdata char session_id[SESSIONS][SESSION_ID_LENGTH + 1];
__xdata uint32_t last_session_use[SESSIONS];
static __xdata uint8_t ss_slot, ss_i, ss_oldest, ss_c;
/* The slot whose cookie the last request came with; only meaningful while
* authenticated is set, which only session_check() does */
static __xdata uint8_t ss_hit;
static __code const char ss_hex[] = "0123456789abcdef";
/* What may follow a cookie value: ';' separates cookies in a Cookie header */
static __code const char ss_end[] = " \t:?=\n\r;";
/* Leaf function, so the compiler keeps its 32-bit scratch in the overlay
* rather than in internal RAM. */
static uint8_t ss_stale(void)
{
if (now - last_session_use[ss_slot] > session_timeout)
return 1;
return 0;
}
/* Likewise a leaf: the 32-bit comparison needs no stack of its own this way */
static uint8_t ss_older(void)
{
if (last_session_use[ss_slot] < last_session_use[ss_oldest])
return 1;
return 0;
}
/* Whether the cookie value is the id of slot ss_slot */
static uint8_t ss_match(void)
{
for (ss_i = 0; ss_i < SESSION_ID_LENGTH; ss_i++) {
if (session[ss_i] != session_id[ss_slot][ss_i])
return 0;
}
ss_c = session[SESSION_ID_LENGTH];
if (!ss_c)
return 1;
for (ss_i = 0; ss_end[ss_i]; ss_i++) {
if (ss_c == ss_end[ss_i])
return 1;
}
return 0;
}
void session_init(void) __banked
{
for (ss_slot = 0; ss_slot < SESSIONS; ss_slot++)
session_id[ss_slot][0] = NUL;
}
void session_check(void) __banked
{
for (ss_slot = 0; ss_slot < SESSIONS; ss_slot++) {
if (!session_id[ss_slot][0])
continue;
if (ss_stale()) {
session_id[ss_slot][0] = NUL;
} else if (ss_match()) {
authenticated = 1;
ss_hit = ss_slot;
last_session_use[ss_slot] = now;
return;
}
}
}
void session_new(void) __banked
{
if (authenticated && session_id[ss_hit][0]) {
/* The login came with a cookie that is still valid: its slot gets the
* new id. A second slot for the same browser would leave the old one
* looking fresh and push out someone else at the next login. */
ss_slot = ss_hit;
} else {
/* A free or expired slot, else the least recently used */
ss_oldest = 0;
for (ss_slot = 0; ss_slot < SESSIONS; ss_slot++) {
if (!session_id[ss_slot][0] || ss_stale())
break;
if (ss_older())
ss_oldest = ss_slot;
}
if (ss_slot == SESSIONS)
ss_slot = ss_oldest;
}
last_session_use[ss_slot] = now;
for (ss_i = 0; ss_i < SESSION_ID_LENGTH; ss_i++) {
if (!(ss_i & 7))
get_random_32();
ss_c = sfr_data[(ss_i >> 1) & 3];
if (ss_i & 1)
ss_c >>= 4;
session_id[ss_slot][ss_i] = ss_hex[ss_c & 0xf];
outbuf[slen++] = ss_hex[ss_c & 0xf];
}
session_id[ss_slot][SESSION_ID_LENGTH] = NUL;
}
+19
View File
@@ -0,0 +1,19 @@
#ifndef _SESSIONS_H_
#define _SESSIONS_H_
#include <stdint.h>
#define SESSIONS 4
#define SESSION_ID_LENGTH 12
extern __xdata char session_id[SESSIONS][SESSION_ID_LENGTH + 1];
extern __xdata uint32_t last_session_use[SESSIONS];
/* No session at boot */
void session_init(void) __banked;
/* Sets authenticated if the cookie in session belongs to a live session */
void session_check(void) __banked;
/* Starts a session at now and writes its id to outbuf at slen */
void session_new(void) __banked;
#endif
+11
View File
@@ -29,6 +29,9 @@ TESTS = $(BUILDDIR)/test_cmd_editor \
$(BUILDDIR)/test_page_json \
$(BUILDDIR)/test_httpd_tx \
$(BUILDDIR)/test_tick_gate \
$(BUILDDIR)/test_cpu_vlan_full \
$(BUILDDIR)/test_cpu_vlan_restricted \
$(BUILDDIR)/test_cpu_vlan_8372 \
$(BUILDDIR)/test_nic_rx \
$(BUILDDIR)/test_lacp
@@ -69,6 +72,14 @@ $(BUILDDIR)/test_lacp: test_lacp.c $(SRCROOT)/rtl837x_lacp.c | $(BUILDDIR)
# this test carries the environment below them itself.
$(BUILDDIR)/test_httpd_tx: test_httpd_tx.c $(SRCROOT)/httpd/httpd.c $(SRCROOT)/uip/uip.c | $(BUILDDIR)
$(CC) $(CFLAGS) $(filter %.c,$^) -o $@ $(LDFLAGS)
# the login sessions httpd.c calls into
$(BUILDDIR)/test_httpd_tx: $(SRCROOT)/sessions.c
# CPU VLAN initialization with full and reduced user-port ranges.
$(BUILDDIR)/test_cpu_vlan_restricted: CFLAGS += -DMACHINE_MAX_PORT=7
$(BUILDDIR)/test_cpu_vlan_8372: CFLAGS += -DMACHINE_MAX_PORT=7 -DMACHINE_MIN_PORT=3 -DRTLP_TEST_IS8373=0
$(BUILDDIR)/test_cpu_vlan_full $(BUILDDIR)/test_cpu_vlan_restricted $(BUILDDIR)/test_cpu_vlan_8372: test_cpu_vlan.c $(TABLE_ENV) | $(BUILDDIR)
$(CC) $(CFLAGS) $(filter %.c,$^) -o $@ $(LDFLAGS)
run: $(TESTS)
@fail=0; for t in $(TESTS); do \
+10 -3
View File
@@ -57,15 +57,16 @@ const uint8_t hex_chars[] = "0123456789abcdef";
uint8_t *hex = (uint8_t *)hex_chars;
/* ---- a 9-port RTL8373 whose logical ports map to themselves ---- */
#ifndef RTLP_TEST_IS8373
#define RTLP_TEST_IS8373 1
#endif
const struct machine machine = {
.machine_name = "HOSTTEST",
.min_port = 0,
.max_port = 8,
.n_sfp = 0,
.log_to_phys_port = { 1, 2, 3, 4, 5, 6, 7, 8, 9 },
.phys_to_log_port = { 0, 1, 2, 3, 4, 5, 6, 7, 8 },
};
struct machine_runtime machine_detected = { .isRTL8373 = 1 };
struct machine_runtime machine_detected = { .isRTL8373 = RTLP_TEST_IS8373 };
/* ---- firmware state the modules read or write ---- */
uint8_t outbuf[TCP_OUTBUF_SIZE];
@@ -120,6 +121,12 @@ uint8_t stp_pflags[STP_ENTITIES];
uint8_t stp_pp2p[STP_ENTITIES];
uint8_t stp_pprio[STP_ENTITIES];
uint8_t stp_prio = 0x80, stp_root_port = 0xff, stp_rstp = 1, stp_txhold = 6;
uint8_t stp_bpdu_filter, stp_pcost_short, stp_root_fwd = 15, stp_root_maxage = 20;
uint16_t stp_legacy, stp_link_prev = 0x3ff;
uint16_t stp_info_while[STP_ENTITIES];
uint32_t stp_cnt[STP_CNT_N][STP_ENTITIES];
uint32_t stp_tc_secs;
uint8_t stp_ent_id(uint8_t e) { return e >= STP_LAG_BASE ? e + 101 - STP_LAG_BASE : machine.log_to_phys_port[e]; }
/* ---- SFP: no module present, reads fail ---- */
uint8_t sfp_buf[16];
+29
View File
@@ -0,0 +1,29 @@
CC ?= cc
SRCROOT = ../..
CFLAGS = -std=c11 -g -O1 -Wall -Wextra -Werror -Wno-unused-parameter -Wno-unknown-pragmas \
-Wno-pointer-sign \
-fpack-struct=1 \
-DRTLP_HOST_TEST \
-fsanitize=address,undefined -fno-omit-frame-pointer \
-include ../sdcc_shim.h -I../stub -I$(SRCROOT) -I$(SRCROOT)/httpd -I$(SRCROOT)/uip
ifeq ($(findstring clang,$(shell $(CC) --version 2>/dev/null)),)
CFLAGS += -Wno-builtin-declaration-mismatch
endif
LDFLAGS = -fsanitize=address,undefined
SRCS = stp_sim.c $(SRCROOT)/rtl837x_stp.c $(SRCROOT)/rtl837x_stp_cli.c
all: build/stp_sim
./build/stp_sim
verbose: build/stp_sim
./build/stp_sim -v
build/stp_sim: $(SRCS) $(SRCROOT)/rtl837x_stp.h
mkdir -p build
$(CC) $(CFLAGS) $(SRCS) -o $@ $(LDFLAGS)
clean:
rm -rf build
.PHONY: all verbose clean
+2381
View File
File diff suppressed because it is too large. Load diff
+24
View File
@@ -0,0 +1,24 @@
#include <stdint.h>
#include <stdio.h>
#include "rtl837x_common.h"
#include "rtl837x_port.h"
#include "machine.h"
#include "support.h"
#include "hw_mock.h"
extern const struct machine machine;
extern struct machine_runtime machine_detected;
int main(void) {
hw_reset();out_reset();
hw_reg_set(RTL837X_VLAN_PORT_EGR_TAG,0xa50fffff);
hw_reg_set(RTL837x_PVID_BASE_REG+16,0x5a123456);
vlan_setup();
uint32_t untouched=(1u<<(MACHINE_MIN_PORT*2))-1;
CHECK(hw_reg_get(RTL837X_VLAN_PORT_EGR_TAG)==(0xa5000000|untouched),"CPU egress set, unrelated bits preserved");
CHECK(hw_reg_get(RTL837x_PVID_BASE_REG+16)==0x5a001001,"CPU PVID initialized explicitly");
uint16_t active=((1u<<(MACHINE_MAX_PORT+1))-1)&~((1u<<MACHINE_MIN_PORT)-1);
CHECK(hw_reg_get(RTL837X_VLAN_PORT_IGR_FLTR)==(active|PMASK_CPU),"CPU ingress included, excluded user ports rejected");
CHECK(hw_vlan_word(1)==(machine_detected.isRTL8373?0x0207ffff:0x0207e3f8),"existing family VLAN membership preserved");
CHECK(hw_vlan_word(2)==0,"no unrelated VLAN changed");
printf("CPU VLAN min=%u max=%u RTL8373=%u: %d checks, %d failed\n",MACHINE_MIN_PORT,MACHINE_MAX_PORT,machine_detected.isRTL8373,tests_run,tests_failed);
return tests_failed?1:0;
}
+273 -11
View File
@@ -1,11 +1,10 @@
/*
* Host-side bench for the httpd transmit path.
* Host-side bench for the httpd transmit and firmware upload paths.
*
* Compiles the UNMODIFIED httpd/httpd.c and uip/uip.c and drives them through a
* real handshake and a real GET from a client that moves its receive window.
* real handshake, GETs with moving receive windows, and multipart POSTs.
* Everything below the two modules - flash, console, JSON pages - is mocked
* here, so what the bench observes is the byte stream the firmware would put on
* the wire.
* here, so the bench observes the wire response and the bytes staged to flash.
*
* The file served out of the simulated flash carries a position-dependent
* pattern, so a duplicated or skipped range shows up as a mismatch at a known
@@ -82,6 +81,9 @@ static uint8_t written_config[CONFIG_LEN];
static uint16_t written_config_len;
static uint32_t config_erase_addr, config_write_addr;
static int config_erases, config_writes;
static uint8_t flash_written[1024];
static size_t flash_written_len;
static int flash_write_bad;
const char * const mime_strings[] = { "text/html", "image/svg+xml", "image/x-icon",
"image/png", "text/javascript", "text/css", "text/plain" };
@@ -107,11 +109,21 @@ void flash_sector_erase(void)
void flash_write_bytes(uint8_t *ptr)
{
config_writes++;
config_write_addr = flash_region.addr;
written_config_len = flash_region.len;
if (written_config_len <= sizeof(written_config))
memcpy(written_config, ptr, written_config_len);
if (flash_region.addr < FIRMWARE_UPLOAD_START) {
config_writes++;
config_write_addr = flash_region.addr;
written_config_len = flash_region.len;
if (written_config_len <= sizeof(written_config))
memcpy(written_config, ptr, written_config_len);
return;
}
if (flash_region.addr != FIRMWARE_UPLOAD_START + flash_written_len ||
flash_region.len > sizeof(flash_written) - flash_written_len) {
flash_write_bad = 1;
return;
}
memcpy(flash_written + flash_written_len, ptr, flash_region.len);
flash_written_len += flash_region.len;
}
const char *get_flash_size_str(void) { return "512 kB"; }
void crc16_bank1(uint8_t *v) { (void)v; }
@@ -120,7 +132,9 @@ void reset_chip(void) { reset_calls++; }
void delay(uint16_t t) { (void)t; }
void write_char(char c) { (void)c; }
void write_char_no_syslog(char c) { (void)c; }
void itoa_no_syslog(uint8_t v) { (void)v; }
void print_string(const char *p) { (void)p; }
void print_string_no_syslog(const char *p) { (void)p; }
void print_string_newline_no_syslog(const char *p) { (void)p; }
void set_sys_led_state(uint8_t state) { (void)state; }
void cmd_parser(void) { }
@@ -130,7 +144,8 @@ void clear_command_history(void) { }
void udp_callbacks(void) { }
void tcpip_output(void) { }
void get_random_32(void) { }
void read_reg_timer(uint32_t *tmr) { *tmr = 0; }
static uint32_t fake_now;
void read_reg_timer(uint32_t *tmr) { *tmr = fake_now; }
/* uip-conf.h asks uIP not to define the packet buffer: on the switch it lives
* at a fixed XDATA address, so the bench provides the storage itself. */
@@ -179,7 +194,9 @@ void send_lldp(void) {}
extern uint8_t authenticated;
extern char session_id[];
extern uint32_t last_session_use[];
extern char passwd[21];
extern uint16_t session_timeout;
/* uip.c defines the listen table but no header declares it; the bench reads it
* to confirm the port httpd_init() asked for is the one uIP is watching. */
@@ -535,7 +552,7 @@ static void scenario_bad_l4_checksum(void)
/* Private HTTP lifecycle values; drive the real callback, not a model. */
enum { TSTATE_NONE = 0, TSTATE_TX = 1 };
enum { TSTATE_NONE = 0, TSTATE_TX = 1, TSTATE_POST = 4 };
extern uint16_t slen, cont_len, sent_len, bindex, crc_final, write_len;
extern uint8_t boundary[], verify_crc, outbuf[];
extern struct { uint8_t *p; uint16_t bptr, plen; } upload_settings;
@@ -863,6 +880,77 @@ static void scenario_split_get_header_terminator(void)
"fragmented GET terminator: CRLFCRLF may span receive events");
}
/* The sessions of the login tests below: four in use, slot 1 the least recently used */
#define ID_STRIDE 13 /* SESSION_ID_LENGTH + 1 */
static const char *slot_id(int i)
{
return session_id + i * ID_STRIDE;
}
static void fill_sessions(void)
{
static const char *const ids[4] = { "0123456789ab", "aaaaaaaaaaaa", "bbbbbbbbbbbb", "cccccccccccc" };
static const uint32_t used[4] = { 50, 10, 60, 70 };
fake_now = 100;
for (int i = 0; i < 4; i++) {
strcpy(session_id + i * ID_STRIDE, ids[i]);
last_session_use[i] = used[i];
}
}
static int others_kept(int first, int second, int third)
{
static const char *const ids[4] = { "0123456789ab", "aaaaaaaaaaaa", "bbbbbbbbbbbb", "cccccccccccc" };
return !strcmp(slot_id(first), ids[first]) && !strcmp(slot_id(second), ids[second]) &&
!strcmp(slot_id(third), ids[third]);
}
static void scenario_login_with_valid_cookie_reuses_its_slot(void)
{
static const char post[] =
"POST /login HTTP/1.1\r\n"
"Cookie: session=0123456789ab\r\n"
"Content-Length: 8\r\n"
"Content-Type: application/x-www-form-urlencoded\r\n"
"\r\n"
"pwd=1234";
login_session_start();
fill_sessions();
client_send(TCP_ACK | TCP_PSH, post, (int)strlen(post));
CHECK(stream_contains("Location: index.html\r\n"),
"login with a valid cookie: accepted");
CHECK(strcmp(slot_id(0), "0123456789ab") != 0,
"login with a valid cookie: its own slot gets the new id");
CHECK(others_kept(1, 2, 3),
"login with a valid cookie: nobody else is pushed out");
fake_now = 0;
}
static void scenario_login_without_cookie_replaces_least_recently_used(void)
{
static const char post[] =
"POST /login HTTP/1.1\r\n"
"Content-Length: 8\r\n"
"Content-Type: application/x-www-form-urlencoded\r\n"
"\r\n"
"pwd=1234";
login_session_start();
fill_sessions();
client_send(TCP_ACK | TCP_PSH, post, (int)strlen(post));
CHECK(stream_contains("Location: index.html\r\n"),
"login without a cookie: accepted");
CHECK(strcmp(slot_id(1), "aaaaaaaaaaaa") != 0,
"login without a cookie: the least recently used slot is taken");
CHECK(others_kept(0, 2, 3),
"login without a cookie: the other three stay");
fake_now = 0;
}
static void scenario_fragmented_get_without_cookie(void)
{
static const char first[] =
@@ -1012,6 +1100,171 @@ static void scenario_config_upload(void)
"multipart/config: every split writes the exact configuration and NUL to its sector");
}
/* Requiring octet-stream would leave alternate or absent MIME types buffered
* as headers forever instead of staging the binary and checking its CRC. */
static void scenario_upload(const char *name, const char *part_type, int split)
{
static const uint8_t payload[] = { 0x00, 0xff, 'R', '\r', '\n', '-', 0x80, 0x00 };
static const uint8_t staged[] = { 0x00, 0xff, 'R', '\r', '\n', '-', 0x80, 0x00, 0x00 };
static const char closing[] = "\r\n--upload-boundary--\r\n";
char preamble[256], request[512];
uint32_t saved_flash_size = flash_size;
int preamble_len, header_len, request_len;
printf("\n-- upload: %s --\n", name);
session_start(MSS_FULL);
strcpy(session_id, "0123456789ab");
session_timeout = 200;
last_session_use[0] = 0; /* read_reg_timer() returns zero in this bench. */
flash_size = 0x100000;
flash_written_len = 0;
flash_write_bad = 0;
reset_calls = 0;
preamble_len = snprintf(preamble, sizeof(preamble),
"--upload-boundary\r\n"
"Content-Disposition: form-data; name=\"file\"; filename=\"firmware.bin\"\r\n"
"%s\r\n", part_type);
header_len = snprintf(request, sizeof(request),
"POST /upload HTTP/1.1\r\nHost: sw\r\n"
"Cookie: session=0123456789ab\r\n"
"Content-Type: multipart/form-data; boundary=upload-boundary\r\n"
"Content-Length: %zu\r\n\r\n%s",
(size_t)preamble_len + sizeof(payload) + sizeof(closing) - 1, preamble);
memcpy(request + header_len, payload, sizeof(payload));
memcpy(request + header_len + sizeof(payload), closing, sizeof(closing) - 1);
request_len = header_len + sizeof(payload) + sizeof(closing) - 1;
if (split) {
/* Cut the part's CRLFCRLF between its final CR and LF. */
client_send(TCP_ACK | TCP_PSH, request, header_len - 1);
CHECK(stream_len == 0 && flash_written_len == 0,
"upload: incomplete part headers stage nothing and await more data");
client_send(TCP_ACK | TCP_PSH, request + header_len - 1,
request_len - header_len + 1);
} else {
client_send(TCP_ACK | TCP_PSH, request, request_len);
}
drain(MSS_FULL);
CHECK(!flash_write_bad && flash_written_len == sizeof(staged) &&
!memcmp(flash_written, staged, sizeof(staged)),
"upload: exact binary payload plus zero terminator staged at image address");
CHECK(stream_len >= 12 && !memcmp(stream, "HTTP/1.1 400", 12),
"upload: invalid firmware is rejected with HTTP 400");
CHECK(!uip_conn->appstate.fw_reset_pending && reset_calls == 0,
"upload: deliberately invalid CRC never schedules or triggers reset");
flash_size = saved_flash_size;
}
extern uint32_t uptr;
#define UPLOAD_LEN 600
#define UPLOAD_BOUNDARY "\r\n--XyZ"
#define UPLOAD_TRAILER UPLOAD_BOUNDARY "--\r\n"
static int upload_in_two(uint8_t *body, uint16_t body_len, uint16_t split)
{
uptr = FIRMWARE_UPLOAD_START;
write_len = 0;
bindex = 0;
verify_crc = 0;
flash_written_len = 0;
flash_write_bad = 0;
strcpy((char *)boundary, UPLOAD_BOUNDARY);
uip_conn = &uip_conns[0];
uip_conn->appstate.tstate = TSTATE_POST;
upload_settings.p = body;
upload_settings.bptr = 0;
upload_settings.plen = split;
stream_upload();
if (uip_conn->appstate.tstate == TSTATE_POST) {
upload_settings.p = body + split;
upload_settings.bptr = 0;
upload_settings.plen = body_len - split;
stream_upload();
}
return uip_conn->appstate.tstate == TSTATE_NONE
&& uptr == FIRMWARE_UPLOAD_START + UPLOAD_LEN && !flash_write_bad
&& flash_written_len == UPLOAD_LEN + 1 && !flash_written[UPLOAD_LEN];
}
static void scenario_upload_cr_before_boundary(void)
{
uint8_t body[UPLOAD_LEN + sizeof(UPLOAD_TRAILER)];
uint16_t body_len, split;
int completed = 0, intact = 0, splits = 0;
for (uint16_t i = 0; i < UPLOAD_LEN; i++)
body[i] = pattern(i);
body[UPLOAD_LEN - 2] = 'x';
body[UPLOAD_LEN - 1] = '\r';
body_len = UPLOAD_LEN + sizeof(UPLOAD_TRAILER) - 1;
memcpy(body + UPLOAD_LEN, UPLOAD_TRAILER, sizeof(UPLOAD_TRAILER) - 1);
for (split = 0; split <= body_len; split++) {
splits++;
if (upload_in_two(body, body_len, split)) {
completed++;
if (!memcmp(flash_written, body, UPLOAD_LEN))
intact++;
}
}
if (verbose)
printf(" %d of %d splits complete, %d intact\n", completed, splits, intact);
CHECK(completed == splits,
"upload: an image ending in CR still finds the closing boundary");
CHECK(intact == splits,
"upload: the CR and the bytes before it reach the flash unchanged");
}
static int upload_whole_and_split(uint8_t *body, uint16_t body_len)
{
return upload_in_two(body, body_len, body_len) && !memcmp(flash_written, body, UPLOAD_LEN)
&& upload_in_two(body, body_len, UPLOAD_LEN) && !memcmp(flash_written, body, UPLOAD_LEN);
}
static void scenario_upload_last_byte(void)
{
uint8_t body[UPLOAD_LEN + sizeof(UPLOAD_TRAILER)];
uint16_t body_len = UPLOAD_LEN + sizeof(UPLOAD_TRAILER) - 1;
int bad = -1;
for (uint16_t i = 0; i < UPLOAD_LEN; i++)
body[i] = pattern(i);
memcpy(body + UPLOAD_LEN, UPLOAD_TRAILER, sizeof(UPLOAD_TRAILER) - 1);
for (int last = 0; last < 256 && bad < 0; last++) {
body[UPLOAD_LEN - 1] = last;
if (!upload_whole_and_split(body, body_len))
bad = last;
}
if (bad >= 0)
printf(" last byte 0x%02x loses the closing boundary\n", bad);
CHECK(bad < 0, "upload: the part ends after the payload whatever its last byte");
body[UPLOAD_LEN - 1] = '\r';
body[UPLOAD_LEN - 2] = '\r';
CHECK(upload_whole_and_split(body, body_len), "upload: an even run of CR before the boundary");
body[UPLOAD_LEN - 3] = '\r';
CHECK(upload_whole_and_split(body, body_len), "upload: an odd run of CR before the boundary");
}
static void scenario_upload_broken_matches(void)
{
uint8_t body[UPLOAD_LEN + sizeof(UPLOAD_TRAILER)];
uint16_t body_len = UPLOAD_LEN + sizeof(UPLOAD_TRAILER) - 1;
memcpy(body + UPLOAD_LEN, UPLOAD_TRAILER, sizeof(UPLOAD_TRAILER) - 1);
memset(body, '\r', UPLOAD_LEN);
CHECK(upload_whole_and_split(body, body_len),
"upload: a run of CR longer than a flash page is staged intact");
for (uint16_t i = 0; i < UPLOAD_LEN; i++)
body[i] = "\r\n-"[i % 3];
CHECK(upload_whole_and_split(body, body_len),
"upload: repeated partial delimiters are staged as payload");
}
int main(int argc, char **argv)
{
if (argc > 1 && !strcmp(argv[1], "-v"))
@@ -1040,6 +1293,15 @@ int main(int argc, char **argv)
scenario_oversized_get_header();
scenario_split_method_names();
scenario_config_upload();
scenario_upload("octet-stream control", "Content-Type: application/octet-stream\r\n", 0);
scenario_upload("MacBinary, split part terminator", "Content-Type: application/macbinary\r\n", 1);
scenario_upload("no part Content-Type", "", 0);
scenario_upload_cr_before_boundary();
scenario_upload_last_byte();
scenario_upload_broken_matches();
scenario_login_with_valid_cookie_reuses_its_slot();
scenario_login_without_cookie_replaces_least_recently_used();
printf("\n%s (%d failure%s)\n",
failures ? "BENCH: FAILURES" : "BENCH: ALL PASS",
+5 -2
View File
@@ -14,8 +14,11 @@
uint8_t uip_buf[UIP_CONF_BUFFER_ALLOC_SIZE];
uint16_t uip_len;
struct uip_eth_addr uip_ethaddr = { .addr = {0x02,0x11,0x22,0x33,0x44,0x55} };
const struct machine machine = { .min_port = 0, .max_port = 7,
const struct machine machine = {
.log_to_phys_port = {5, 1, 2, 3, 4, 6, 7, 8, 9} };
/* Logical ports of the mock machine above */
#define TEST_MIN_PORT 0
#define TEST_MAX_PORT 7
struct machine_runtime machine_detected = { .isRTL8373 = 1 };
uint8_t lacpEnabled;
uint16_t management_vlan; /* mocked; slow-protocol path zeroes it per-frame */
@@ -216,7 +219,7 @@ int main(int argc, char **argv)
lacp_lag_set(0, 0x00ff);
step(8);
int all_tx = 1, sane = 1;
for (int i = machine.min_port; i <= machine.max_port; i++) {
for (int i = TEST_MIN_PORT; i <= TEST_MAX_PORT; i++) {
if (!tx_count[i]) all_tx = 0;
if (last_tx[i].ethertype != HTONS(0x8809) || last_tx[i].subtype != 1
|| last_tx[i].dst[5] != 0x02
+19 -1
View File
@@ -98,6 +98,7 @@
#include "../rtl837x_regs.h"
extern __xdata uint8_t rx_headers[16];
extern volatile __xdata uint32_t ticks;
/*---------------------------------------------------------------------------*/
/* Variable definitions. */
@@ -243,6 +244,9 @@ __xdata struct uip_stats uip_stat;
#if UIP_LOGGING == 1
#define UIP_LOG(m) print_string_newline_no_syslog(m);
#define FRAGDROP_WINDOW (5 * SYS_TICK_HZ)
static __xdata u16_t fragdrop_mark;
static __xdata u8_t fragdrop_count;
#else
#define UIP_LOG(m)
#endif /* UIP_LOGGING == 1 */
@@ -710,6 +714,17 @@ uip_process(u8_t flag) __banked
/* Check if we were invoked because of the perodic timer fireing. */
} else if(flag == UIP_TIMER) {
// print_string("T1\n");
#if UIP_LOGGING == 1
if(fragdrop_count && (u16_t)ticks - fragdrop_mark >= FRAGDROP_WINDOW) {
print_string_no_syslog("ip: fragments dropped: ");
itoa_no_syslog(fragdrop_count);
if(fragdrop_count == 0xff)
write_char_no_syslog('+');
write_char_no_syslog('\n');
fragdrop_mark = (u16_t)ticks;
fragdrop_count = 0;
}
#endif /* UIP_LOGGING == 1 */
#if UIP_IDLE_TIMEOUT
if(uip_connr == uip_conns) {
uip_idle_age = 0;
@@ -915,7 +930,10 @@ uip_process(u8_t flag) __banked
#else /* UIP_REASSEMBLY */
UIP_STAT(++uip_stat.ip.drop);
UIP_STAT(++uip_stat.ip.fragerr);
UIP_LOG("ip: fragment dropped.");
#if UIP_LOGGING == 1
if(fragdrop_count != 0xff)
++fragdrop_count;
#endif /* UIP_LOGGING == 1 */
goto drop;
#endif /* UIP_REASSEMBLY */
}