49 Commits
Author SHA1 Message Date
logicog f103daaafa Merge pull request #519 from dobodu/main
Added french translation
2026-09-26 06:26:17 +02:00
bloq 168add47a3 Merge pull request #452 from gnacho/feat/session-timeout-config
httpd: make the session timeout configurable
2026-09-25 16:57:51 -05:00
gnacho 423886dc54 web: keep service labels unindented and add the es session strings
Restore the sy_igmp and sy_services_note lines to their original
indentation so they do not show up as changed in the diff; the new
session timeout strings move to their own lines. Also add the es
strings, which were missing after the merge with main.
2026-09-25 23:22:35 +02:00
gnacho 4a9137387c test: define session_timeout in the host harness
test_port_tables and test_page_json link httpd/page_impl.c unmodified,
which now references session_timeout. Define it in support.c next to
the other firmware globals so the tests link again on current main.
2026-09-25 23:22:35 +02:00
gnacho 2fc08f40d2 httpd: drop Max-Age from the session cookie
The server timeout is an inactivity timeout: scan_header() refreshes
last_session_use on every authenticated request. Max-Age counts from
login instead, and the cookie is only set there, so the browser deletes
it session_timeout seconds after logging in however busy the session
is. Leave expiry to the server, which already handles it.

The five-digit itoa16_html() stays: information.json must print
timeouts from 10000 to 65535 correctly.
2026-09-25 23:22:35 +02:00
gnacho 4a7fd7a098 Merge remote-tracking branch 'origin/main' into feat/session-timeout-config 2026-09-25 23:16:16 +02:00
dobodu 19cd1200b4 Update French translations in app.js 2026-09-25 21:59:13 +02:00
bloq 333adbaf19 Merge pull request #523 from DrDoof/cleanup/drop-call-traces
port, dhcp: drop the called/done traces from the console and syslog
2026-09-25 14:21:09 -05:00
bloq 8051061fd7 Merge pull request #522 from DrDoof/cleanup/dhcp-dead-memcpy
dhcp: drop the MAC copy after the requested IP and server id
2026-09-25 14:20:59 -05:00
bloq 8ae3f5e70f Merge pull request #521 from DrDoof/fix/dhcp-option-length
dhcp: skip IP and time options that are shorter than four bytes
2026-09-25 14:20:50 -05:00
dobodu 9ce328d155 Update French translations in app.js 2026-09-25 21:07:50 +02:00
dobodu 2e463a2f30 Add French translations to login page 2026-09-25 20:53:25 +02:00
DrDoof 422dc3f24d port, dhcp: drop the called/done traces from the console and syslog
rtl837x_port.c prints "called" and "done" around mirroring, PVID, VLAN
and L2 setup, and dhcp.c around discover, request, start and stop. They
land on the console on every boot and config replay, and in the log
once syslog is running. The VLAN line keeps the VLAN, members and
tagged values, only without the function name in front.

On the KP_9000_6XHML_X2 this takes 359 bytes out of BANK1, 88 out of
BANK2 and 24 out of BANK3. BANK1 goes from 431 to 790 bytes free.
2026-09-25 07:45:03 +02:00
DrDoof 4b01ff9f8a dhcp: drop the MAC copy after the requested IP and server id
dhcp_addopt_request_ip() and dhcp_addopt_server_id() both end with a
copy of four bytes of our MAC to the current position, without moving
the position on. The next option always starts there, so those bytes
are written over before the packet goes out and never reach the wire.
The packet stays the same without the copy, and BANK3 is 102 bytes
smaller on the KP_9000_6XHML_X2.
2026-09-25 07:45:03 +02:00
DrDoof 456a4d9d3f dhcp: skip IP and time options that are shorter than four bytes
ip_opt() copies four bytes whatever the option length says, so a
subnet, router, DNS, server id or broadcast option shorter than four
bytes stores the start of the next option as the address. long_opt()
does not look at the length at all and always moves on by six bytes, so
a lease, renewal or rebind option of another length puts the parser out
of step for the rest of the packet.

Both now read the length. An address or time shorter than four bytes is
skipped, a longer one is taken from its first four bytes and stepped
over whole, and a skipped time leaves the previous value alone.

Found in the code, not on the wire; no server here sends such options.
BANK3 is 47 bytes smaller on the KP_9000_6XHML_X2.
2026-09-25 07:45:03 +02:00
dobodu 02e9189f5f Add French to language selection
Added French language options to the selection.
2026-09-25 01:00:14 +02:00
dobodu aff7d9c098 Add French translations for various UI elements 2026-09-25 00:56:34 +02:00
René van Dorst 770926abd1 Merge pull request #517 from DrDoof/fix/rx-drain
rx: keep draining while the NIC buffer holds data
2026-09-24 19:27:08 +00:00
René van Dorst 13f04d8acb Merge pull request #515 from polyfaces/hasivo_s1100wp_8gt_1sx_se
machine: add the Hasivo S1100WP-8GT-1SX-SE target
2026-09-24 16:55:35 +00:00
polyfaces 49284fee15 Update docuementation and PCB photos 2026-09-24 22:01:53 +08:00
DrDoof db9ce7543e rx: keep draining while the NIC buffer holds data
The main loop drains the NIC only after an RX interrupt, at most
RX_BUDGET frames, then clears the status and waits for the next one.
When frames are left and the buffer reaches the RX stop threshold, the
NIC takes no more frames and raises no further interrupt, so reception
stops for good.

Check the buffer after clearing the status and keep draining while it
holds data; the interrupt is re-enabled only once it is empty.
2026-09-24 15:44:10 +02:00
polyfaces 7a14c2d4a5 Add Hasivo S1100WP_8GT_1SX_SE to supported devices
Update support_devices.md
2026-09-24 18:58:07 +08:00
polyfaces 92948079a9 machine: add the Hasivo S1100WP-8GT-1SX-SE target
RTL8373-based 8×2.5G PoE + 1×SFP switch.

- 8 copper ports at 10/100/1000/2500 Mbps, SFP at 1G/2.5G/10G (SDS 1)
- board doc with label specs and photos
2026-09-24 12:28:54 +08:00
René van Dorst e406a3e82b Merge pull request #499 from DrDoof/up/nic-rx-irq
rtlplayground: receive on the NIC interrupt instead of polling the buffer
2026-09-23 18:32:47 +00:00
René van Dorst c43b955db0 Merge pull request #498 from DrDoof/up/tick-block
rtlplayground: run the timed work once per tick
2026-09-23 18:31:47 +00:00
logicog 0321763e91 Merge pull request #511 from DrDoof/perf/rtl8224-ready
rtl8224: wait for the PHY to answer instead of a fixed 2.5 s
2026-09-23 20:29:10 +02:00
René van Dorst 4bbbcd7d04 Merge pull request #513 from DrDoof/fix/port-name-no-arg
cmd: "port <n> name" without a name clears it instead of reading stale words
2026-09-23 18:21:14 +00:00
DrDoof 682ed861fa rtl8224: wait for the PHY to answer instead of a fixed 2.5 s
rtl8224_enable() waits 2.5 s after releasing the RTL8224 reset before
anything talks to the PHY. On an SWTGW218AS the chip id already reads
back 0x8224 at the first poll, about 265 ms after the release.

Remember when the reset was released and, once SMI is up, poll the chip
id until it answers, giving up after the same 2.5 s as before. The PHY
writes that follow read back the same as before.

The reset pulse before it goes from 0.5 s to 20 ms, twice the 10 ms the
related RTL8372 and RTL8221B need. The pulse does reset the chip: the
MDI reverse register (MMD 30, 0x0a90) still reads its power-on value
0x00f3 just before the init writes 0x00fc, as it does with 0.5 s.
Together the console banner comes 3 s sooner.

The SerDes write loop in phy_config_8224() waited on the busy bit with
no limit, so a PHY that never answers stopped the boot before the
network came up. Bound it and report a timeout.
2026-09-23 20:21:11 +02:00
d00f 3a9045b97e cmd: "port <n> name" without a name clears it instead of reading stale words
The name branch read the fourth word without checking that the line had
one, and the tokenizer does not reset unused word slots, so "port 5 name"
took the name from wherever the previous command's fourth word had been:
"port 5 name hello" followed by "port 5 name" gave "hello" again, and
after a longer line it gave the tail of that line. The web UI never sends
the form, the console can. With no argument the name is now empty, which
also gives the console a way to remove a name.
2026-09-23 17:46:05 +02:00
d00f 4b9f6c319f Merge pull request #508 from DrDoof/fix/rebuild-on-flag-change
build: rebuild objects when the compiler flags change
2026-09-23 16:38:08 +02:00
DrDoof 6e2701bc8c rtlplayground: receive on the NIC interrupt instead of polling the buffer
The receive interrupt was wired up but never used: EX1 was enabled, the
handler re-enabled itself and did nothing, and the mask bit for it in the
NIC stayed clear, so idle() read the receive buffer register on every
pass to find out whether a packet had arrived, and slept until the next
tick when none had. A packet that arrived during that sleep waited for
the tick: 2.2 ms on average, 5 ms at worst, for every frame the CPU
handles.

Now the handler latches rx_irq and masks itself, and idle() sleeps
unless a receive, a link change, a console command or the tick is
pending. On rx_irq the loop drains the buffer, clears the status bit and
re-enables EX1, in that order: the NIC holds the line as long as the
status bit is set, and the bit does not clear while the buffer still
holds a frame, so a frame that lands between the last read and EX1 = 1
raises the interrupt again rather than getting lost. The other order,
clearing before draining, left the line asserted and produced an
interrupt on every pass. The mask is written after nic_setup(), which
resets it.

Measured on an RTL8373 board with the same counters in both images,
clocked by the tick counter, traffic from a host behind an LACP trunk:

```
                          polling     interrupt
idle, register ops/s          788            79
25 pps, RTT avg/max     2.19/4.96     0.44/0.75 ms
500 pps, RTT avg/max    2.78/5.06     0.39/5.40 ms
500 pps, loss               0.08%         0.08%
```

The handler compiles to clr EX1 / setb rx_irq / reti.

The timer interrupt now raises a tick flag too, so idle() no longer works
out TICKS_DUE twice per pass, and cmd_available moves from xdata to a
bit. The four wake-up flags sit in the low bits of one byte at 0x20, so
the sleep test is a single read of that byte. tx_buf_empty deliberately
stays out: it is 1 when there is nothing to do, so including it would
keep the sleep test from ever firing.
2026-09-23 16:12:32 +02:00
DrDoof ac3b4b17ae rtlplayground: run the timed work once per tick
idle() ran everything on every pass: the seconds block, the SFP pin
test, the TCP periodic timer behind a gate of its own, the STP timers
behind theirs. The timed work moves into handle_tick(), which idle()
calls once per system tick from a single gate; handle_tx() loses its
private one.

handle_sfp() ran on every pass as well, two GPIO reads per pass to learn
that nothing changed; it now runs every ten ticks. A module inserted is
still seen within 50 ms, and the read itself has been off the loop since
the wake-up wait moved to the tick.

No change in behaviour otherwise: the link, receive and console paths
stay where they were. SWTGW218AS: code +45 B, xdata +1 B, stack
unchanged.

The health checkpoints follow the work they measure: the seconds block,
SFP, TX and STP phases are taken inside handle_tick(), the link, RX and
command phases in idle().
2026-09-23 16:09:18 +02:00
DrDoof f87eb70c6a rtlplayground: read the link registers when the switch reports a change
The link-change interrupt was set up at boot (mode 0x5f84, mask 0x5f34)
but never enabled: the handler did nothing and did not clear the event in
the switch, so EX0 stayed commented out and idle() read the two link
registers on every pass instead, 400 reads a second on a quiet switch.

The handler now only latches a flag and masks itself; the register access
macros build a write in the shared SFR data bytes, so an interrupt that
touched the switch in the middle of a write from the main loop would
corrupt it. idle() clears the event register (write-one-to-clear, 0x3ff
for the ten ports), re-enables EX0 and then reads the link state, in that
order, so a change that lands during the read raises a new edge. The flag
is also set once a second from the seconds block, so a board whose INT0
line turns out not to be wired keeps detecting link changes, a second
late at worst. Boot drops the events pending from bringing the links up
before the mask is set.

Measured on an RTL8373 switch before this change: 0x5f88 held 0x3fd, nine
ports had reported a change nobody read. With the change a counter in the
loop went 2 -> 3 -> 4 on "port 5 off" / "port 5 on" and stayed at 4 over
three reads without events, so the event is delivered, cleared and
re-armed, and there is no flood.

SWTGW218AS: code +57 B, xdata 0, stack 151 -> 150 B; the flag is a bit
variable. The handler compiles to clr EX0 / setb link_irq / reti.

idle() also keeps in a bit what it saw in NIC_RX_BUFF_DATA when deciding
whether to sleep, and the first iteration of the receive loop uses it
instead of reading the register again a moment later. Between the two
only the link check and handle_sfp() run, and neither can make a queued
packet disappear. Every later iteration reads the register as before, so
the budget loop behaves as it did. This part was not measured on
hardware: one register read per pass needs a throughput run to show up.
SWTGW218AS: BANK1 and BANK2 +6 bytes each, BANK3 unchanged.
2026-09-23 16:04:39 +02:00
DrDoof 214cc6b737 rtlplayground: keep the STP and TCP timers on the system tick
stp_timers() ran every fourth pass of the main loop and stp_clock counted
passes, which only measures time while a pass is one tick long. It never
quite was: a pass is as long as the packets it handles, and #482 now takes
up to four of them, so under an IPv4 stream a pass is about 16 ms and STP
time runs at a third of its rate, with hellos 6 s apart and a root aged out
after a minute. Since #483 the loop also stops sleeping while packets wait,
so under a flood of cheap frames passes come faster than ticks and STP time
runs fast instead, shortening the forward delay in exactly the situation a
loop produces. uip_periodic() had the same problem from the other side: it
ran once per pass, so the retransmit timer sped up with the loop.

Both now go by the tick counter the timer interrupt keeps. The STP block
catches up on ticks a long pass missed, at most eight steps at a time, and
handle_tx() runs its periodics once per tick at most. One byte of xdata
more, 48 bytes of code.

A host test drives the gate with one pass every 5 ms, every 16 ms and
every 0.6 ms for ten seconds each; the STP clock lands on 500 steps in
all three, where counting passes gave 156 and 4166. A pass of 1.3 s loses
time rather than firing 65 steps at once, and the arithmetic survives the
tick counter wrapping.
2026-09-23 16:00:59 +02:00
feelfree69 40177f6318 Merge pull request #352 from DrDoof/feat/health-cmd
health: a CLI dump of where the main loop's time goes
2026-09-23 15:05:55 +02:00
feelfree69 d3dda09079 Merge pull request #489 from DrDoof/feat/port-media-pause
port: report link role and negotiated pause per port
2026-09-23 14:59:35 +02:00
feelfree69 277e729f2c Merge pull request #509 from DrDoof/phy/advertise-pause-above-1g
phy: advertise pause on the forced speeds from 1G up
2026-09-23 14:32:32 +02:00
d00f 2c231fdf69 phy: advertise pause on the forced speeds from 1G up
The advertisement written for a forced speed of 1G and above is 0x1001,
while auto and the forced 10M and 100M modes all carry bit 10, the PAUSE
capability. Forcing a port to 1g or faster therefore takes pause off the
link: the advertisement narrows and the far end has nothing left to
agree to.

Measured on a SWTGW218AS through phyget and physet, on a port with no
link so nothing was disturbed. Auto reads 0x15e1, port 1 1g reads
0x1001, port 1 auto reads 0x15e1 again, and writing 0x1401 by hand
sticks. That bit 10 is the pause capability follows from a second
reading: two ports advertise the same 0x15e1 from this side, and pause
is active only against the partner whose own advertisement carries the
bit.

Reported by feelfree69 in #489.
2026-09-22 23:12:50 +02:00
d00f 689751ffe6 build: rebuild objects when the compiler flags change
The .d files track headers, so adding or removing a flag on a tree that
was already built leaves every object that did not change for some other
reason compiled with the old flags, and the image ends up a mix of the
two.

How that shows depends on the flag. DIAG=1 fails the link on undefined
symbols, which at least stops the build. An opt-in whose hooks expand to
empty macros loses the calls instead, so the link succeeds and the
feature quietly measures nothing.

Objects now depend on a stamp holding CC_FLAGS, rewritten only when the
flags differ, so an unchanged flag set still does not force a rebuild.
2026-09-22 22:25:53 +02:00
d00f 340fdd97e5 port: report link role and negotiated pause per port
Two per-port MAC status registers that nothing in the firmware reads:
0x6408 says which end is master on a BASE-T link, 0x63FC and 0x6400 say
whether pause was negotiated in each direction. port <n> show prints
both at the end.

The read sits in rtl837x_port.c rather than phy_show(), because these
are MAC registers and phy_show() is skipped for SFP ports. Master/slave
keys off machine.is_sfp[] so it stays off the fibre ports, where the
notion does not apply.

Bit N of each register is chip port N, which is not how
RTL837X_REG_LINKS_STS indexes its bytes a few lines above in the same
header.
2026-09-22 20:28:54 +02:00
d00f 94187461c6 ci: build once with HEALTH=1 as well
Code behind a build flag nobody sets in CI rots quietly. One extra build of
the same machine with the instrumentation on keeps it compiling.
2026-09-21 22:25:32 +02:00
d00f fa0eb91586 health: build the instrumentation only when asked for
The counters, the phase hooks in idle() and the "health" command are now
behind HEALTH, set from the command line with HEALTH=1. Without it the
hooks are empty macros and the command does not exist, so a default build
carries none of it: on SWTGW218AS that is 1065 bytes of bank 2, 101 of the
common segment and 28 of xdata, plus seven banked calls per main-loop pass.

It is a developer's tool. Reading the dump takes knowing the main loop, and
a user who needs it for a report can build with the flag on, which is how
the kernel treats its lock and latency instrumentation as well.
2026-09-21 22:25:32 +02:00
d00f 2f872ea08e health: drop the constant-false branch the compiler warned about
The tcp slot dump printed the slot index behind `if (UIP_CONNS > 1)`, and
with one connection configured that is a constant-false condition around
live code: sdcc reported two "unreachable code" warnings for the body and
one "conditional flow changed by optimizer" for the loop that could only
run once.

Print the single slot directly and state the assumption as a build-time
check, so a build that raises UIP_CONNS fails loudly here instead of
quietly labelling every line as slot zero. Builds warning-free again.
2026-09-21 22:25:32 +02:00
d00f ab2d30a05b doc: describe the health command
What each line of the dump means, how two dumps give rates, and the
two things worth knowing before trusting the numbers: the command
inflates its own cmd phase counters by the cost of printing, and the
untouched figure is a high-water mark, not a live level.
2026-09-21 22:25:32 +02:00
d00f d37ce07119 health: a CLI dump of where the main loop's time goes
Every probe of a misbehaving switch so far measured it from the
outside. This puts the witnesses inside.

Each idle() pass increments a loop counter and stamps a checkpoint
after every phase: link polling, SFP, RX, TX, STP and the command
dispatch. A phase spanning two or more system ticks counts as slow and
its worst time is kept, in 5 ms units. handle_rx counts frames, so the
dump separates a flooded CPU from a stalled loop.

At boot the free stack area is painted, preserving the interrupt state
around the painting, and the dump reports how much of the pattern
survives next to the live stack pointer. The TCP connection table is
printed whole: state, timers, retransmissions, ports and peer, next to
the httpd transfer state, so a permanently occupied slot names its
occupant. With more than one slot configured every slot is listed.

Everything is raw hex; two dumps a known time apart give the rates.
The implementation sits in cmd_parser next to the other command
handlers and needs no storage beyond its counters.
2026-09-21 22:25:32 +02:00
gnacho cdc84c75e4 httpd: give the session cookie a Max-Age matching the timeout
The session cookie carried neither Expires nor Max-Age, so browsers that
clean up session cookies aggressively (LibreWolf, for one) may drop it
while the session is still valid. Send Max-Age=session_timeout so the
declared lifetime matches the timeout already enforced by the firmware.

itoa16_html() only converted four digits, so extend it to five to print
timeouts up to 65535. That also fixes information.json, which used the
same helper and reported wrong values from 10000 s up.
2026-09-16 19:05:48 +02:00
gnacho 6be1871bb4 Merge remote-tracking branch 'origin/main' into feat/session-timeout-config
# Conflicts:
#	httpd/httpd.c
2026-09-16 18:08:21 +02:00
gnacho 4686515de3 web: add a session timeout field to the Services card
Adds a numeric input next to the syslog settings to read and set the
session timeout without knowing the command. The current value is
reported in information.json, so the field also shows the default the
firmware was built with.

Costs 86 bytes of code (BANK1) and nothing in RAM.
2026-09-16 18:07:49 +02:00
gnacho f0b311fc43 httpd: make the session timeout configurable
The session timeout was a compile-time constant (200 s), so changing it
meant rebuilding the firmware. Make it a variable that keeps the same
default and add a "session <seconds>" command, so it can be set from
the console or from the startup configuration like the other settings.

Measured on KP_9000_9XHML_X_V3_1: ROM +180 bytes (BANK1 +17, BANK2
+161) and xdata +2 bytes; the internal RAM map is unchanged.
2026-09-15 00:24:45 +02:00
32 changed files with 874 additions and 123 deletions

No files matched your search

+2
View File
@@ -26,3 +26,5 @@ jobs:
run: make CI=1 MACHINE="KP_9000_6XHML_X2"
- name: Make project (8x1/RTL8373)
run: make CI=1 MACHINE="KP_9000_9XHML_X_V3_1"
- name: Make project with the health instrumentation
run: make clean && make CI=1 MACHINE="KP_9000_6XHML_X2" HEALTH=1
+16 -2
View File
@@ -19,6 +19,10 @@ ifeq ($(MACHINE),)
else
CC_FLAGS += -DMACHINE_$(MACHINE)
endif
# Health instrumentation and the "health" console command: HEALTH=1 gmake ...
ifneq ($(HEALTH),)
CC_FLAGS += -DHEALTH
endif
ifeq ($(CI),1)
CC_FLAGS += --Werror
@@ -130,10 +134,20 @@ distclean: $(SUBDIRSCLEAN)
$(SUBDIRSCLEAN):
$(MAKE) -C $(@:clean=) clean
$(BUILDDIR)/%.rel: %.c | create_build_dir html_data.h
# Objects depend on the flags they were built with, through a stamp file that
# is rewritten only when CC_FLAGS actually changes.
CCFLAGS_STAMP := $(BUILDDIR)/.ccflags
.PHONY: FORCE
FORCE:
$(CCFLAGS_STAMP): FORCE | create_build_dir
@echo '$(CC_FLAGS)' | cmp -s - $@ 2>/dev/null || echo '$(CC_FLAGS)' > $@
$(BUILDDIR)/%.rel: %.c $(CCFLAGS_STAMP) | create_build_dir html_data.h
$(CC) -MMD $(CC_FLAGS) -o $@ -c $<
$(BUILDDIR)/%.rel: %.asm | create_build_dir
$(BUILDDIR)/%.rel: %.asm $(CCFLAGS_STAMP) | create_build_dir
${ASM} ${AFLAGS} -o $@ $<
# mv -f $(addprefix $(basename $^), .lst .rel .sym) .
+1
View File
@@ -344,6 +344,7 @@ netmask zzz.zzz.zzz.zzz = Network mask of the switch
port x name xxx = Name xxx the port number x
port z 1g = Set 1g speed for port z
igmp on/off = Turn IGMP on or off
session xxxx = Web session timeout in seconds (default 200)
```
[To be continue]
+3 -2
View File
@@ -21,6 +21,7 @@ extern __code const struct machine machine;
extern __xdata struct machine_runtime machine_detected;
extern __xdata uint8_t sfr_data[4];
extern volatile __xdata uint32_t ticks;
__xdata uint32_t rtl8224_release_tick;
extern __xdata uint8_t tx_seq;
extern __code const uint8_t * __code const hex;
@@ -240,10 +241,10 @@ void rtl8224_enable(void) __banked
reg_bit_clear(RTL837X_REG_GPIO_32_63_OUTPUT, 4);
// Configure Pin as output
reg_bit_set(RTL837X_REG_GPIO_32_63_DIRECTION, 4);
delay(100);
delay(4);
// Set pin 4 high
reg_bit_set(RTL837X_REG_GPIO_32_63_OUTPUT, 4);
delay(500);
rtl8224_release_tick = ticks;
}
+191 -2
View File
@@ -5,6 +5,8 @@
// #define DEBUG
// #define REGDBG 1
#include <8051.h>
#include "cmd_parser.h"
#include "rtl837x_common.h"
#include "rtl837x_port.h"
#include "rtl837x_flash.h"
@@ -41,6 +43,8 @@ extern __xdata struct flash_region_t flash_region;
extern __xdata char passwd[21];
extern __xdata uint16_t session_timeout;
extern __xdata struct dhcp_state dhcp_state;
__xdata uint8_t vlan_names[VLAN_NAMES_SIZE];
@@ -59,7 +63,6 @@ __xdata uint8_t hexvalue[4] = { 0 };
// Buffer for writing to flash 0x1fd000, copy to 0x1fe000
__xdata uint8_t cmd_buffer[CMD_BUF_SIZE];
__xdata uint8_t cmd_available;
__xdata char save_cmd;
@@ -893,10 +896,13 @@ void parse_port(void)
print_string_x(port_names[phy_settings.port]);
if (!machine.is_sfp[phy_settings.port]) {
phy_show(phy_settings.port);
} else {
write_char('\n');
}
port_media_show(phy_settings.port);
} else if (cmd_compare(2, "name")) {
uint8_t i = 0;
while ( (i < PORT_NAME_SIZE-1) && (cmd_buffer[cmd_words_b[3] + i] != NUL) ) {
while ( cmd_words_len > 3 && (i < PORT_NAME_SIZE-1) && (cmd_buffer[cmd_words_b[3] + i] != NUL) ) {
port_names[phy_settings.port][i] = name_char(cmd_buffer[cmd_words_b[3] + i]);
i++;
}
@@ -1609,6 +1615,25 @@ void parse_syslog(void)
}
}
void parse_session(void)
{
if (cmd_words_len >= 2) {
/* atoi_short() returns zero for no digits and for a value past
* 65535; a zero-second timeout is not useful either, so both
* tests are needed. */
if (!atoi_short(cmd_words_b[1]) || !atoi_results_short) {
print_string("Must be 1-65535 s\n");
return;
}
session_timeout = atoi_results_short;
}
/* Also the confirmation after setting: one message for both. */
print_string("Session timeout: ");
itoa_short(session_timeout);
print_string(" s\n");
}
// Parse command into words
// cmd_words_len contains the number of words found.
// cmd_words_b[] contains only start of a word offset.
@@ -1847,6 +1872,10 @@ void cmd_parser(void) __banked
}
} else if (cmd_compare(0, "stp")) {
stp_parse();
#ifdef HEALTH
} else if (cmd_compare(0, "health")) {
health_show();
#endif
} else if (cmd_compare(0, "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)
@@ -1911,6 +1940,8 @@ void cmd_parser(void) __banked
}
} else if (cmd_compare(0, "ingress")) {
parse_ingress();
} else if (cmd_compare(0, "session")) {
parse_session();
}
else {
cmd_error("Unknown command\n");
@@ -2051,3 +2082,161 @@ void execute_commands(__xdata uint8_t *p) __banked {
p++;
};
}
#ifdef HEALTH
/*
* Health instrumentation. The counters live in xdata and are fed from the
* main loop through the banked entry points below; the "health" command
* prints everything in one go. All figures are raw hex, diffing two dumps
* gives the rates. Phase times are in system ticks (SYS_TICK_HZ = 200,
* 5 ms each); a phase counts as slow when it spans 2 or more ticks.
*/
extern __xdata uint16_t len_left; /* httpd: bytes still to send of the current file */
extern __xdata uint8_t entry; /* httpd: index of the file being sent */
static __xdata uint32_t health_loops;
__xdata uint16_t health_rx_frames;
static __xdata uint16_t ph_last_t;
static __xdata uint8_t ph_max[HEALTH_PHASES];
static __xdata uint16_t ph_slow[HEALTH_PHASES];
/* The ISR increments ticks between the two byte reads of a 16-bit load, so
* read until two loads agree. */
static uint16_t ticks_lo(void)
{
uint16_t a, b;
a = (uint16_t)ticks;
do {
b = a;
a = (uint16_t)ticks;
} while (a != b);
return a;
}
void health_loop_start(void) __banked
{
health_loops++;
ph_last_t = ticks_lo();
}
void health_phase(uint8_t id) __banked
{
__xdata uint16_t now = ticks_lo();
__xdata uint16_t d = now - ph_last_t;
ph_last_t = now;
if (d > 0xff)
d = 0xff;
if ((uint8_t)d > ph_max[id])
ph_max[id] = d;
if (d >= 2)
ph_slow[id]++;
}
/* Fill the stack area above the current frame with a pattern; the dump scans
* for how much of it survived. The 8051 stack grows upward, so everything
* above SP is free at this point. Interrupts are held off so an ISR does not
* push into the area mid-paint. */
void health_stack_paint(void) __banked
{
__xdata uint8_t from = SP + 4;
__xdata uint8_t ea = EA;
EA = 0;
for (uint8_t i = 0xff; i >= from; i--)
*(__idata uint8_t *)i = 0xa5;
EA = ea;
}
static void ph_line(__code const char *name, uint8_t id)
{
print_string(name);
print_string(" max ");
print_byte(ph_max[id]);
print_string(" slow ");
print_short(ph_slow[id]);
write_char('\n');
}
void health_show(void) __banked
{
__xdata uint8_t i;
print_string("up ");
reg_read_m(RTL837X_REG_SEC_COUNTER);
print_byte(sfr_data[0]); print_byte(sfr_data[1]);
print_byte(sfr_data[2]); print_byte(sfr_data[3]);
print_string(" ticks ");
print_long(ticks);
print_string(" loops ");
print_long(health_loops);
print_string(" rx ");
print_short(health_rx_frames);
write_char('\n');
/* Phase timing: max duration seen (ticks of 5 ms) and slow-pass count */
ph_line("link", HEALTH_PH_LINK);
ph_line("sfp ", HEALTH_PH_SFP);
ph_line("rx ", HEALTH_PH_RX);
ph_line("tx ", HEALTH_PH_TX);
ph_line("stp ", HEALTH_PH_STP);
ph_line("cmd ", HEALTH_PH_CMD);
/* Stack: current depth plus how much of the painted area is untouched */
print_string("sp ");
print_byte(SP);
print_string(" untouched ");
i = 0;
while (*(__idata uint8_t *)(0xff - i) == 0xa5 && i < 0x86)
i++;
print_byte(i);
write_char('\n');
/* The TCP connection slot. One on this configuration; a build with more
* has to say which slot each line is, so make that a visible decision. */
#if UIP_CONNS != 1
#error "health: the tcp dump prints a single slot, extend it for UIP_CONNS > 1"
#endif
i = 0;
{
print_string("tcp ");
print_string("st ");
print_byte(uip_conns[i].tcpstateflags);
print_string(" tmr ");
print_byte(uip_conns[i].timer);
print_string(" rtx ");
print_byte(uip_conns[i].nrtx);
print_string(" rto ");
print_byte(uip_conns[i].rto);
print_string(" len ");
print_short(uip_conns[i].len);
print_string(" mss ");
print_short(uip_conns[i].mss);
write_char('\n');
print_string(" lport ");
print_short(HTONS(uip_conns[i].lport));
print_string(" rport ");
print_short(HTONS(uip_conns[i].rport));
print_string(" rip ");
print_short(HTONS(uip_conns[i].ripaddr[0]));
write_char(' ');
print_short(HTONS(uip_conns[i].ripaddr[1]));
write_char('\n');
}
/* httpd transfer state and the protocol flags */
print_string("httpd left ");
print_short(len_left);
print_string(" entry ");
print_byte(entry);
print_string(" stp ");
print_byte(stp_enabled);
print_string(" mvlan ");
print_short(management_vlan);
write_char('\n');
}
#endif
+27 -1
View File
@@ -6,9 +6,35 @@
#include "rtl837x_common.h"
extern __xdata uint8_t cmd_buffer[CMD_BUF_SIZE];
extern __xdata uint8_t cmd_available;
extern __bit __at(0x03) cmd_available;
extern __xdata uint8_t err_status;
/* Health instrumentation, built with HEALTH=1. Without it the hooks below
* compile to nothing and the "health" command does not exist. */
#ifdef HEALTH
/* Phase ids, in main-loop order */
#define HEALTH_PH_LINK 0
#define HEALTH_PH_SFP 1
#define HEALTH_PH_RX 2
#define HEALTH_PH_TX 3
#define HEALTH_PH_STP 4
#define HEALTH_PH_CMD 5
#define HEALTH_PHASES 6
extern __xdata uint16_t health_rx_frames;
#define health_rx_frame() health_rx_frames++
void health_loop_start(void) __banked; /* once per idle() pass, resets the phase baseline */
void health_phase(uint8_t id) __banked; /* after each phase; accounts ticks since last checkpoint */
void health_stack_paint(void) __banked; /* paint the unused stack area once at boot */
void health_show(void) __banked; /* "health" CLI dump */
#else
#define health_rx_frame()
#define health_loop_start()
#define health_phase(id)
#define health_stack_paint()
#endif
void cmd_tokenize(void) __banked;
void cmd_parser(void) __banked;
void execute_config(void) __banked;
+14 -16
View File
@@ -139,7 +139,6 @@ void dhcp_addopt_request_ip(void)
DHCP_OPT[dhcp_state.opt_ptr++] = dhcp_state.current_ip[1];
DHCP_OPT[dhcp_state.opt_ptr++] = dhcp_state.current_ip[2];
DHCP_OPT[dhcp_state.opt_ptr++] = dhcp_state.current_ip[3];
memcpy(&DHCP_OPT[dhcp_state.opt_ptr], uip_ethaddr.addr, 4);
}
@@ -151,13 +150,11 @@ void dhcp_addopt_server_id(void)
DHCP_OPT[dhcp_state.opt_ptr++] = dhcp_state.server[1];
DHCP_OPT[dhcp_state.opt_ptr++] = dhcp_state.server[2];
DHCP_OPT[dhcp_state.opt_ptr++] = dhcp_state.server[3];
memcpy(&DHCP_OPT[dhcp_state.opt_ptr], uip_ethaddr.addr, 4);
}
void dhcp_send_discover(void)
{
print_string("dhcp_send_discover called\n");
dhcp_prepare_request();
dhcp_state.opt_ptr = 0;
@@ -193,7 +190,6 @@ void dhcp_send_discover(void)
void dhcp_send_request(void)
{
print_string("dhcp_send_request called\n");
dhcp_prepare_request();
dhcp_state.opt_ptr = 0;
@@ -232,19 +228,21 @@ void ip_opt(__xdata uint8_t * ip)
{
dhcp_state.opt_ptr++;
uint8_t len = DHCP_OPT[dhcp_state.opt_ptr++];
*ip++ = DHCP_OPT[dhcp_state.opt_ptr++];
*ip++ = DHCP_OPT[dhcp_state.opt_ptr++];
*ip++ = DHCP_OPT[dhcp_state.opt_ptr++];
*ip++ = DHCP_OPT[dhcp_state.opt_ptr++];
if (len >= 4)
memcpy(ip, &DHCP_OPT[dhcp_state.opt_ptr], 4);
// There may be more than one IP option, such as 2 DNS servers advertised
dhcp_state.opt_ptr += len - 4;
dhcp_state.opt_ptr += len;
}
void long_opt(void)
uint8_t long_opt(void)
{
dhcp_state.opt_ptr++;
dhcp_state.opt_ptr++;
uint8_t len = DHCP_OPT[dhcp_state.opt_ptr++];
if (len < 4) {
dhcp_state.opt_ptr += len;
return 0;
}
long_value = DHCP_OPT[dhcp_state.opt_ptr++];
long_value <<= 8;
long_value |= DHCP_OPT[dhcp_state.opt_ptr++];
@@ -252,6 +250,8 @@ void long_opt(void)
long_value |= DHCP_OPT[dhcp_state.opt_ptr++];
long_value <<= 8;
long_value |= DHCP_OPT[dhcp_state.opt_ptr++];
dhcp_state.opt_ptr += len - 4;
return 1;
}
@@ -275,15 +275,15 @@ void parse_opts(void)
ip_opt(&dhcp_state.broadcast[0]);
break;
case DHCP_LEASE:
long_opt();
if (long_opt())
dhcp_state.lease = long_value;
break;
case DHCP_REBIND:
long_opt();
if (long_opt())
dhcp_state.rebind = long_value;
break;
case DHCP_RENEWAL:
long_opt();
if (long_opt())
dhcp_state.renewal = long_value;
break;
case DHCP_END:
@@ -376,13 +376,11 @@ void dhcp_start(void) __banked
*tid++ = SFR_DATA_8;
*tid = SFR_DATA_0;
dhcp_state.state = DHCP_START;
print_string("dhcp_start done\n");
}
void dhcp_stop(void) __banked
{
print_string("dhcp_stop called\n");
uip_udp_remove(dhcp_state.conn);
dhcp_state.state = DHCP_OFF;
}
+75
View File
@@ -0,0 +1,75 @@
# Hasivo S1100WP-8GT-1SX-SE
RTL8373-based 8×2.5G PoE + 1×SFP switch.
### Label specifications
- **Manufacturer**: Hasivo
- **Model**: S1100WP-8GT-1SX-SE
- **Ports**:
- 8 × RJ45: 10/100/1000/2500 Mbps with PoE
- 1 × SFP: 1G / 2.5G / 10G
- **Power input**: DC 52V 2.5A
- **Stock firmware defaults**: IP 192.168.0.1, user/password `admin`/`admin`
### What works
- All eight 2.5GBASE-T RJ45 ports at 10/100/1000/2500 Mbps (PoE is not configurable via RTLPlayground)
- SFP port supporting 1G, 2.5G and 10G modules
- LEDs: green (2.5G) and amber (1G/100M/10M) per copper port; combined link/act on the SFP port
### Photos
Front panel:
<img src="photos/HASIVO_S1100WP-8GT-1SX-SE/front.jpg" width="600" />
Label:
<img src="photos/HASIVO_S1100WP-8GT-1SX-SE/label.jpg" width="600" />
### PCB overview
Top side:
<img src="photos/HASIVO_S1100WP-8GT-1SX-SE/pcb_top.jpg" width="600" />
Bottom side:
<img src="photos/HASIVO_S1100WP-8GT-1SX-SE/pcb_bottom.jpg" width="600" />
### Port layout
| Logical port | Physical port | Type |
|--------------|---------------|--------|
| 1-8 | 1-8 | Copper |
| 9 | 9 | SFP |
### LED configuration
Copper ports use LED SET0, the SFP port uses LED SET1.
| SET | LED0 | LED2 |
|------|-----------------------------------------------|------------------------------------------------------|
| SET0 | Green — lights on 2.5G link with activity | Amber — lights on 1G / 100M / 10M link with activity |
| SET1 | All speeds — lights on any link with activity | — |
### SFP GPIO assignments
| SFP (logical 9) | pin_detect (ModAbs) | pin_los | pin_tx_disable | SerDes | I2C SDA | I2C SCL |
|-----------------|---------------------|---------|----------------|--------|-----------------|---------------------|
| SFP | GPIO30_ACL_BIT3_EN | GPIO37 | GPIO_NA | SDS1 | GPIO39_I2C_SDA4 | GPIO40_I2C_SCL3_MDC1 |
### Machine configuration
- `reset_pin`: GPIO54_ACL_BIT2_EN
- High LEDs (pads 27-29): mux = `LED_27 | LED_28_SYS | LED_29`, enable = `LED_28_SYS | LED_29`
### PoE
The 8 copper ports support PoE powered by a DC 52V 2.5A input. Power management is handled by two HS104PTI chipsets over I2C (GPIO47_I2C_SDA0, GPIO46_I2C_SCL0):
* Chip 1 (I2C address 0x1A): 2.5G Ports 1–4
* Chip 2 (I2C address 0x2A): 2.5G Ports 5–8
Note: The current implementation does not yet support PoE management. Consequently, PoE currently operates in standalone mode without software control.
Binary file not shown.

After

Width:  |  Height:  |  Size: 82 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 93 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 220 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 273 KiB

+79
View File
@@ -0,0 +1,79 @@
# The health command
`health` prints a one-page snapshot of the firmware's vital signs on the
serial console. It exists for the situations where the switch misbehaves and
every probe from the network measures the network instead of the device: the
dump is taken from inside, costs one command, and works while HTTP does not.
```
> health
up 0000001f ticks 0x00001f4c loops 0x000016da rx 0x024e
link max 01 slow 0x0000
sfp max 74 slow 0x0001
rx max 00 slow 0x0000
tx max 00 slow 0x0000
stp max 04 slow 0x0001
cmd max 02 slow 0x0001
sp 80 untouched 62
tcp st 00 tmr 00 rtx 00 rto 00 len 0x0000 mss 0x05b4
lport 0x0000 rport 0x0000 rip 0x0000 0x0000
httpd left 0x0000 entry 00 stp 01 mvlan 0x0002
```
The instrumentation is not part of a default build. It costs about 1.1 kB
of bank 2, a few bytes of xdata and seven banked calls per main-loop pass,
which is nothing a user needs unless they are chasing a problem, so it is
switched on at build time:
```
make MACHINE=SWTGW218AS HEALTH=1
```
Without it the hooks compile to nothing and `health` is not a command.
All figures are raw hex. The counters are cumulative: take two dumps a known
time apart and the differences give the rates.
## The first line
`up` is the seconds counter, `ticks` the system tick (200 per second),
`loops` the number of main-loop passes and `rx` the frames handle_rx has
taken off the NIC. A healthy loop completes one pass per tick, so the
difference in `loops` tracks the difference in `ticks`; a loop that falls
behind is being held up by one of the phases below. The `rx` rate separates
a flooded CPU port from a stalled loop, which look identical from outside.
## Phases
Each line is one section of the main loop: the link poll, SFP handling,
frame reception, TCP transmission, the STP timers and the command dispatch.
`max` is the worst time that section has taken, in ticks of 5 ms; `slow`
counts the passes that took two ticks or more. A misbehaving subsystem
names itself here, which is the difference between knowing that the loop is
slow and knowing why.
A dump of its own output takes the console some milliseconds, so running
`health` repeatedly increments the `cmd` counters by itself. That is the
instrument observing itself, not a fault.
## Stack
At boot the free stack area is painted with a pattern. `sp` is the live
stack pointer; `untouched` counts the painted bytes still intact at the top,
which is the closest thing to a high-water mark this hardware offers. A
shrinking `untouched` across dumps means something is reaching deeper than
anything before it.
## The TCP slot
The connection table is printed whole, one slot per pair of lines: TCP
state (3 is ESTABLISHED, 0 closed), retransmission timer and counter, bytes
in flight, negotiated MSS, then the ports and the peer address. With a
single slot serving the entire web interface, one glance answers the
question every management outage starts with: who is holding it. A slot
showing state 3 with nothing in flight and a peer that no longer answers is
a connection whose owner went away.
`httpd left` and `entry` describe the file transfer the web server believes
it is in the middle of, and the trailing flags show whether STP is running
and which VLAN carries management.
+1
View File
@@ -7,6 +7,7 @@ The following devices have been tested and are fully working:
| Davuaz | Da-K6501W | No | [PCB-K0501W-V2.0](devices/K0501W_V2_0.md) | | 5 + 1 |
| FOXNEO | FNS-1200P | No | [PCB-K0402W-U13-V2.0](devices/FNS-1200P.md) | 2M | 4 + 2 |
| Goalake | IG108 | No | [PB-2132](devices/PB-2132.md) | 4M | 8 + 1 |
| Hasivo | S1100WP_8GT_1SX_SE | Yes | [S1100WP_8GT_1SX_SE-V1.05](devices/HASIVO_S1100WP_8GT_1SX_SE.md) | 2M | 8 + 1 |
| Hisource | Hi-K0402WS | No | [PCB-K0402WS-V3.0](devices/PCB-K0402WS-V3.0.md) | | 4 + 2 |
| Hisource | Hi-K0402WS | No | [PCB-K0402WS-V2.0](devices/PCB-K0402WS-V2.0.md) | 2M | 4 + 2 |
| Hisource | Hi-K0801WS | No | [PCB-KO801W-V2.0](devices/HI-K0801WS.md) | | 8 + 1 |
+124 -1
View File
@@ -72,6 +72,7 @@ bw_exceed:"When exceeded",bw_fc:"Flow control",bw_drop:"Drop",
bw_in_err:"Ingress limit must be 0.016-10000 Mbit/s",bw_out_err:"Egress limit must be 0.016-10000 Mbit/s",
sy_network:"Network",sy_dhcp:"Use DHCP",sy_dhcp_t:"Request address via DHCP",sy_services:"Services",
sy_igmp:"IGMP snooping",sy_sysip:"server IP",sy_server:"Server",sy_port:"Port",
sy_sesstmo:"Session timeout (s)",sy_sesstmo_err:"Session timeout must be 1-65535 s",
sy_services_note:"Service state reflects the startup config; runtime state is not readable.",
sy_password:"Admin password",sy_newpw:"New password",sy_repeat:"Repeat",sy_pwapply:"Change password",
sy_pw_note:"Takes effect immediately; save to flash to keep it after reboot.",sy_console:"Console",
@@ -184,6 +185,7 @@ bw_exceed:"超過時の動作",bw_fc:"フロー制御",bw_drop:"破棄",
bw_in_err:"入力制限は 0.016〜10000 Mbit/s の範囲で指定してください",bw_out_err:"出力制限は 0.016〜10000 Mbit/s の範囲で指定してください",
sy_network:"ネットワーク",sy_dhcp:"DHCP を使用",sy_dhcp_t:"DHCP でアドレスを取得",sy_services:"サービス",
sy_igmp:"IGMP スヌーピング",sy_sysip:"サーバー IP",sy_server:"サーバー",sy_port:"ポート",
sy_sesstmo:"セッションタイムアウト (秒)",sy_sesstmo_err:"セッションタイムアウトは 1〜65535 秒で指定してください",
sy_services_note:"サービスの状態は起動設定を反映しています。実行時の状態は読み取れません。",
sy_password:"管理者パスワード",sy_newpw:"新しいパスワード",sy_repeat:"再入力",sy_pwapply:"パスワードを変更",
sy_pw_note:"即時に反映されます。再起動後も保持するにはフラッシュに保存してください。",sy_console:"コンソール",
@@ -296,6 +298,7 @@ bw_exceed:"超限动作",bw_fc:"流量控制",bw_drop:"丢弃",
bw_in_err:"入方向限速范围为 0.016-10000 Mbit/s",bw_out_err:"出方向限速范围为 0.016-10000 Mbit/s",
sy_network:"网络",sy_dhcp:"使用 DHCP",sy_dhcp_t:"通过 DHCP 获取地址",sy_services:"服务",
sy_igmp:"IGMP 侦听",sy_sysip:"服务器 IP",sy_server:"服务器",sy_port:"端口",
sy_sesstmo:"会话超时(秒)",sy_sesstmo_err:"会话超时必须为 1-65535 秒",
sy_services_note:"服务状态反映启动配置;运行时状态无法读取。",
sy_password:"管理员密码",sy_newpw:"新密码",sy_repeat:"重复输入",sy_pwapply:"修改密码",
sy_pw_note:"立即生效;如需重启后保留请保存到 Flash。",sy_console:"控制台",
@@ -408,6 +411,7 @@ bw_exceed:"Al excederse",bw_fc:"Control de flujo",bw_drop:"Descartar",
bw_in_err:"El límite de entrada debe estar entre 0.016 y 10000 Mbit/s",bw_out_err:"El límite de salida debe estar entre 0.016 y 10000 Mbit/s",
sy_network:"Red",sy_dhcp:"Usar DHCP",sy_dhcp_t:"Solicitar dirección mediante DHCP",sy_services:"Servicios",
sy_igmp:"IGMP snooping",sy_sysip:"IP del servidor",sy_server:"Servidor",sy_port:"Puerto",
sy_sesstmo:"Tiempo de sesión (s)",sy_sesstmo_err:"El tiempo de sesión debe estar entre 1 y 65535 s",
sy_services_note:"El estado del servicio refleja la configuración de inicio; el estado en ejecución no es legible.",
sy_password:"Contraseña de administrador",sy_newpw:"Nueva contraseña",sy_repeat:"Repetir",sy_pwapply:"Cambiar contraseña",
sy_pw_note:"Toma efecto inmediatamente; guarda en flash para conservarla tras el reinicio.",sy_console:"Consola",
@@ -446,6 +450,118 @@ 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"
},
fr:{
nav_dash:"Tableau de bord",nav_ports:"Ports",nav_stp:"Arborescence",nav_stats:"Statistiques",
nav_vlan:"VLANs",nav_l2:"Table MAC",nav_mirror:"Mise en mirroir",nav_lag:"LAG",nav_eee:"EEE",
nav_bw:"Bande passante",nav_system:"Système",nav_fw:"Micro logiciel",
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",
th_sel_light:"Clair (sélénisé)",th_sel_dark:"Sombre (sélénisé)",
c_port:"Port",c_name:"Nom",c_link:"Lien",c_apply:"Appliquer",c_cancel:"Annuler",c_confirm:"Confirmer",
c_close:"Fermer",c_refresh:"Raffraichir",c_off:"arrêt",c_offc:"Arrêt",c_down:"arrêt",c_on:"Marche",c_auto:"Auto",c_delete:"Supprimer",
c_type:"Type",c_state:"Etat",c_enabled:"Activé",c_load:"Charger",c_disable:"Désactivé",c_sfp:"SFP",
c_full:"full",c_half:"half",c_devices:"Equipements",c_yes:"oui",c_no:"non",
d_ports:"Ports",d_ports_h:"Cliquer pour le détail du port",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 mauvais",d_rxbad:"RX mauvais",
i_host:"Nom d'hote",i_ip:"Adresse IP",i_mask:"Masque réseau",i_gw:"Passerelle",i_mac:"MAC",i_fw:"Micro logiciel",
i_built:"Compilé",i_hw:"Matériel",i_flash:"Flash",i_syslog:"Log système",
p_state:"Etat",p_disabled:"désactivé",p_up:"up",p_txgb:"TX bon / mauvais",p_rxgb:"RX bon / mauvais",
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:"Annonces",
pt_title:"Configuration du port",pt_conn:"Connecté",pt_speed:"Vitesse",pt_mtu:"MTU",
pt_sfp_note:"La vitesse SFP est paramétrée par slot et n'est pas enregistrée dans la configuration de démarrage.",
pt_name_err:"Nom du port: 1 à 15 caractères, sans espaces",pt_mtu_err:"MTU doit être comrpis entre 64 et 16383",
stp_title:"Arborescence",stp_h:"RSTP, 802.1w",stp_version:"Version",stp_v_stp:"Compatibilité STP",
stp_prio:"Priorité du Pont",stp_hello:"Bonjour [s]",stp_maxage:"Age max [s]",stp_fwd:"Délai de retransmission [s]",
stp_txhold:"TX en attente",stp_bridge_apply:"Appliquer les paramètres du pont",
stp_off_msg:"Arborescence désactivée: Tous les ports sont retransmis.",stp_bridge:"Pont",stp_root:"Racine",
stp_root_self:"Ce pont est la racine",stp_via:"via le port",stp_cost:"coût du chemin",stp_tc:"changements de topologie",
stp_prio_note:"La priorité la plus basse devient la racine (défaut 8 à 32768). Les paramètres s'appliquent immediatement; utiliser Sauvegarder en flash pour les conserver.",
stp_portcfg:"Configuration du port",stp_edge:"Bord",stp_pcost:"Coût du chemin",stp_pprio:"Priorité",
stp_guard:"Garde",stp_filter:"Filtre BPDU",stp_p2p:"Point-à-point",
stp_port_note:"Les ports en bordure sont transmis immediatemment et ne change pas la topologie; Auto traite le port comme une bordure après 3s sans BPDUs, et chaque BPDU reçu le révoque. Le coût du chemin 0 = automatique.",
stp_status:"Statut du port",stp_role:"Rôle",stp_db:"Pont désigné",stp_dp:"Port désigné",
stp_dc:"Coût désigné",stp_oedge:"Oper. bord",stp_op2p:"Oper. P2P",
stp_s0:"désactivé",stp_s1:"bloquage",stp_s2:"apprentissage",stp_s3:"retransmission",
stp_r1:"Racine",stp_r2:"Designé",stp_r3:"Alternatif",stp_trip:"protection déclenchée",
stp_g_none:"Aucun",stp_g_bpdu:"BPDU",stp_g_root:"Racine",
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",
st_title:"Statiqtique du port",st_h:"cumul depuis le démarrage",st_txg:"TX good",st_txb:"TX bad",st_rxg:"RX good",
st_rxb:"RX mauvais",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",
v_title:"VLANs",v_vid:"VID",v_members:"Membres",v_tagged:"Tagué",v_untagged:"Détagué",v_pvid_on:"PVID on",
v_empty:"Aucun VLANs configuré.",v_mgmt:"VLAN de management",v_mgmt_none:"détagué",v_editor:"Editeur de VLAN",
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é, T = membre tagué, - = non membre. PVID assigne ce VLAN au trafic entrant non tagué.",
v_ingress:"Filtrage entrant",v_ingress_h:"ecriture seule: cet état n'est pas lisible depuis le communtateur réseau",
v_accept:"Accepté",v_ing_all:"Tous",v_ing_apply:"Appliquer les modes entrants",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_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",
l2_del_t:"Supprimer l'entrée",l2_flush_q:"Effacer toutes les entrées MAC apprises?",
m_title:"Dupplication de port",m_active:"actif",m_monitor:"Port de supervision",m_mirror:"Mirroir",m_both:"Les deux",
m_note:"Les deux = duplique RX et TX du port vers le port de supervision.",m_none:"Selectionnez au moint un port à dupliquer",
lag_hash:"Hachage:",lag_note:"Un LAG nécessite au moins un membre pour être sauvegardé dans la configuration de démarrage; Utiliser un groupe vide fait office de remise à zero.",
lag_clear_q:"Effacer l'agrégation {n}?",lag_clear_d:"Tous les ports membres vont redevenir normaux.",
e_title:"Efficacité Energétique des ports Ethernet",e_adv:"Annoncé",e_lp:"Lien partenaire",e_active:"Actif",e_enable:"Permis",
e_note:"Drapeaux Annonces et liens partenaires par vitesse : 100M, 1G, 2.5G. Les ports SFP ne peuvent pas bénéficier de l'EEE.",
e_idle:"en attente",e_na:"n/a",
bw_title:"Limite 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:"Contrôle du flux",bw_drop:"Laissés tombés",
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",
sy_network:"Réseau",sy_dhcp:"Uiliser DHCP",sy_dhcp_t:"Aquérir l'adresse via DHCP",sy_services:"Services",
sy_igmp:"Surveillance IGMP",sy_sysip:"IP du serveur",sy_server:"Serveur",sy_port:"Port",
sy_services_note:"L'état de service dépend de la configuration de démarrage; Le temps de fonctionnement n'est pas consultable.",
sy_password:"Mot de passe administrateur",sy_newpw:"Nouveau mot de passe",sy_repeat:"Saisir à nouveau",sy_pwapply:"Changer le mot de passe",
sy_pw_note:"Prends effet immédiatement; Sauvegarder en flash afin de conserver la configuration lors du redémarrage.",sy_console:"Console",
sy_cmd:"Ligne de commande CLI...",sy_send:"Envoyer",sy_startup:"Configuration de démarrage",sy_replayed:"Rejouée à chaque redémarrage",
sy_reload:"Recharger depuis la mémoire flash",sy_write:"Ecrire en mémoire flash",sy_unknown_note:"Les lignes incorrectes sont mises en évidence avant l'écriture.",
sy_maint:"Maintenance",sy_reboot:"Redémarre le commutateur",sy_lang:"Langage",
sy_ip_err:"Erreur d'IP / masque réseau / passerelle",sy_host_err:"Nom d'hôte : 1 à 23 caractères, sans espace ni apostrophes",
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_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.",
sy_rebooting:"Redémarrage, reconnectez-vous d'ici 20s",sy_bytes:"bytes",
cw_title:"Enregistrer cette configuration de démarrage?",cw_save_title:"Enregistrer la configuration active en mémoire flash?",
cw_info:"{n} / 2048 bytes, qui seront rejouées ligne par ligne à chaque redémarrage",
cw_toolarge:"Trop grand : La quota du secteur de démarrage ne peut excéder 2048 bytes. Supprimez des lignes.",
cw_longline:"La ligne {n} est longue de {m} bytes: Le commutateur ne peut exécuter des lignes de plus de 126 bytes et les omettra donc au démarragee, l'écriture est refusé. Raccourcissez les d'abord.",
cw_unknown:"Ne respecte pas la grammaire de configuration (sera néanmoins sauvegardé): ",cw_empty:"(vide)",
cw_writing:"Configuration enregistrée...",cw_failed:"Echec de l'enregistrement de la configuration: HTTP {n}",
cw_verify_fail:"Verification erronée: La mémoire flash présente des différences par rapport à ce qui a été transmis. Le log de commande ne sera pas éffacé.",
cw_saved:"Configuration de démarrage sauvegardée et vérifiée",cw_collect:"Récolement des modification des paramètres courants...",
t_applied:"Appliqué: {c}",t_cmds:"{n} commande(s) appliquée(s)",t_rejected:"commandes rejetées: {c}",t_failed:"échecs: {c}",
fw_title:"Mise à jour du micro-logiciel",
fw_intro:"Charge une image RTLPlayground (512 KiB .bin). L'image est envoée en mémoire flash et sa somme de contrôle est vérifiée; en cas de succès le commutateur va redémarrer, re-verifiez l'image et appliquez-là. LA configuration de démarrage est conservée.",
fw_upload:"Charger",fw_checking:"{f}: {n} bytes. Vérification...",fw_size_err:"la taille est {n} alors que nous attendons 524288 (512 KiB)",
fw_magic_err:"Entête de banque de donnée manquant / contrôle LJMP: ce n'est pas une image de firmware",fw_crc_err:"Somme de contrôle erronée: image corrompue",
fw_valid:"Image RTLPlayground valide (taille, LJMP et somme de contrôle OK)",fw_q:"Chargement du firmware?",
fw_d:"Si la somme de contrôle est correcte, le commutateur va redémarrer tout seul et charger la nouvelle image lors du démarrage (la configuration est préservée). Ne pas éteindre l'appareil avant que les voyants ne s'éteignent.",
fw_finishing:"fin d'écriture en mémoire flash... {s} s",fw_uploading:"chargement... {p}% / {s} s",
fw_verified:"somme de contrôle vérifiée, le commutateur redémarre...",
fw_rejected:"le commutateur rejette l'image en raison d'une somme de contrôle incorrecte, rien n'a été appliqué",
fw_lost:"erreur lors du chargement: connection perdue à mi-transfert",
fw_noreboot:"pas de redemarrage détecté: l'image a certainement été refusée. Si vous chargesz un ancien firmware, verifiez la version affichée après l'écran de connection.",
fw_applied:"mise à jour effectuée",fw_done_t:"Firmware mis à jour",
fw_done:"Le commutateur a vérifié l'image et vient de redémarrer. La session a été remise à zero, vous allez devoir vour reconnecter.",
fw_login:"Vers l'écran de connection",fw_rebooting:"Le commutateur redémarre...",
fw_timeout:"Le commutateur n'est plus joignable depuis 150 s: verifiez l'alimentation ou utilisez la console série"
}
};
var rtlLang=(function(){
@@ -541,6 +657,7 @@ 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}$/,
/^session\s+\d{1,5}$/,
/^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?)+$/,
@@ -1624,6 +1741,7 @@ function sysLoad(){
var sl=(S.info.syslog_server||"").split(":");
if(sl[0]&&sl[0]!=="0.0.0.0")$("sy-sysip").value=sl[0];
if(sl[1])$("sy-sysport").value=sl[1];
if(S.info.session_timeout)$("sy-sesstmo").value=S.info.session_timeout;
}).catch(function(){});
cfgReload();
}
@@ -1676,6 +1794,11 @@ $("sy-syslog").addEventListener("change",function(){
}else cmds.push("syslog off");
postCmds(cmds).catch(function(){});
});
$("sy-sesstmo").addEventListener("change",function(){
var v=+this.value;
if(!(v>=1&&v<=65535)){toast(t("sy_sesstmo_err"),"err");sysLoad();return;}
postCmd("session "+v).catch(function(){sysLoad()});
});
$("sy-pwapply").addEventListener("click",function(){
var a=$("sy-pw1").value,b=$("sy-pw2").value;
if(a.length<1||a.length>20){toast(t("sy_pw_len"),"err");return;}
@@ -1739,7 +1862,7 @@ tabHooks.system={enter:sysLoad};
var CONF_OVERWRITE=[
/^ip\b/,/^gw\b/,/^netmask\b/,/^hostname\b/,
/^syslog\s+ip\b/,/^syslog\s+port\b/,/^passwd\b/,
/^syslog\s+ip\b/,/^syslog\s+port\b/,/^passwd\b/,/^session\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/,
+5 -1
View File
@@ -375,6 +375,8 @@ button.danger{background:var(--bad);color:#fff;border-color:transparent}
<input class="in" id="sy-sysip" style="flex:1;min-width:0;width:0" data-i18n-p="sy_sysip">
<span class="small mut" data-i18n="sy_port">Port</span>
<input class="in" id="sy-sysport" style="width:64px" type="number" min="1" max="65535"></div>
<label class="row" style="justify-content:space-between"><span data-i18n="sy_sesstmo">Session timeout (s)</span>
<input class="in" id="sy-sesstmo" style="width:80px" type="number" min="1" max="65535"></label>
<p class="small mut" data-i18n="sy_services_note">Service state reflects the startup config; runtime state is not readable.</p>
</div>
<div class="card"><h2 data-i18n="sy_password">Admin password</h2>
@@ -394,9 +396,11 @@ button.danger{background:var(--bad);color:#fff;border-color:transparent}
<label class="row"><span class="lb" data-i18n="sy_lang">Language</span>
<select class="in" id="langSel">
<option value="en">English</option>
<option value="es">Español</option>
<option value="fr">Français</option>
<option value="ja">日本語</option>
<option value="zh">中文</option>
<option value="es">Español</option>
</select></label>
<label class="row"><span class="lb" data-i18n="sy_theme">Theme</span>
<select class="in" id="themeSel">
+2 -1
View File
@@ -84,7 +84,8 @@ button:hover{filter:brightness(1.1)}
en:["Switch Login","RTLPlayground management interface","Password","Sign in","Wrong password, try again."],
ja:["スイッチ ログイン","RTLPlayground 管理インターフェース","パスワード","ログイン","パスワードが違います。もう一度お試しください。"],
zh:["交换机登录","RTLPlayground 管理界面","密码","登录","密码错误,请重试。"],
es:["Inicio de sesión del switch","Interfaz de gestión de RTLPlayground","Contraseña","Iniciar sesión","Contraseña incorrecta, inténtalo de nuevo."]
es:["Inicio de sesión del switch","Interfaz de gestión de RTLPlayground","Contraseña","Iniciar sesión","Contraseña incorrecta, inténtalo de nuevo."],
fr:["Utilisateur du commutateur","Interface de management RTLPlayground","Mot de passe","Se connecter","Mot de passe erronné, essayez à nouveau."]
};
var l=null;
try{l=localStorage.getItem("rtl_lang");}catch(e){}
+6 -2
View File
@@ -12,7 +12,11 @@
#include "debug.h"
#define SESSION_ID_LENGTH 12
#define SESSION_TIMEOUT 200
#define SESSION_TIMEOUT_DEFAULT 200
/* Seconds of inactivity after which a session cookie stops validating.
* Configurable with the "session" command, see cmd_parser.c. */
__xdata uint16_t session_timeout = SESSION_TIMEOUT_DEFAULT;
#define CMARK_S 6
@@ -342,7 +346,7 @@ __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) {
if (now - last_session_use > session_timeout) {
dbg_string("Session expired\n");
} else {
if (is_word_x(session, session_id)) {
+8 -3
View File
@@ -28,6 +28,7 @@ extern __code const struct machine machine;
extern __xdata uint8_t outbuf[TCP_OUTBUF_SIZE];
extern __xdata uint16_t slen;
extern __xdata uint16_t management_vlan;
extern __xdata uint16_t session_timeout;
extern __xdata uint16_t cont_len;
extern __xdata uint32_t cont_addr;
extern __code const uint8_t * __code const hex;
@@ -114,12 +115,14 @@ void itoa_html(uint8_t v)
char_to_html('0' + (v % 10));
}
void itoa16_html(uint16_t v) /* sufficient for VLAN IDs (max 4094) */
void itoa16_html(uint16_t v) /* up to five digits (65535) */
{
uint8_t print_zeros = 0;
uint8_t d;
d = v / 1000;
d = v / 10000;
if (d) { char_to_html('0' + d); print_zeros = 1; }
d = (v / 1000) % 10;
if (d || print_zeros) { char_to_html('0' + d); print_zeros = 1; }
d = (v / 100) % 10;
if (d || print_zeros) { char_to_html('0' + d); print_zeros = 1; }
d = (v / 10) % 10;
@@ -258,7 +261,9 @@ void send_basic_info(void)
itoa_html(syslog_state.server_ip[2]); char_to_html('.');
itoa_html(syslog_state.server_ip[3]); char_to_html(':');
itoa16_html(syslog_state.server_port);
slen += strtox(outbuf + slen, "\",\"mac_address\":\"");
slen += strtox(outbuf + slen, "\",\"session_timeout\":");
itoa16_html(session_timeout);
slen += strtox(outbuf + slen, ",\"mac_address\":\"");
byte_to_html(uip_ethaddr.addr[0]); char_to_html(':');
byte_to_html(uip_ethaddr.addr[1]); char_to_html(':');
byte_to_html(uip_ethaddr.addr[2]); char_to_html(':');
+2
View File
@@ -5,6 +5,8 @@
extern __code const uint8_t * __code const HTTP_RESPONCE_TXT;
void itoa16_html(uint16_t v);
bool send_counters(uint8_t phys_port);
void send_status(void);
void send_vlan(uint16_t vlan);
+31
View File
@@ -1325,6 +1325,37 @@ __code const struct machine machine = {
},
};
#elif defined MACHINE_HASIVO_S1100WP_8GT_1SX_SE
__code const struct machine machine = {
.machine_name = "Hasivo S1100WP-8GT-1SX-SE",
.isRTL8373 = 1,
.mac_flash_offset = 0x1FC000,
.min_port = 0,
.max_port = 8,
.n_sfp = 1,
.log_to_phys_port = {1, 2, 3, 4, 5, 6, 7, 8, 9},
.phys_to_log_port = {0, 1, 2, 3, 4, 5, 6, 7, 8},
.is_sfp = {0, 0, 0, 0, 0, 0, 0, 0, 1},
.sfp_port[0].pin_detect = GPIO30_ACL_BIT3_EN,
.sfp_port[0].pin_los = GPIO37,
.sfp_port[0].pin_tx_disable = GPIO_NA,
.sfp_port[0].sds = 1,
.sfp_port[0].i2c = I2CBUS( GPIO39_I2C_SDA4, GPIO40_I2C_SCL3_MDC1 ),
.reset_pin = GPIO54_ACL_BIT2_EN,
.high_leds = { .mux = LED_27 | LED_28_SYS | LED_29, .enable = LED_28_SYS | LED_29 },
.port_led_set = { 0, 0, 0, 0, 0, 0, 0, 0, 1},
.led_sets = { { LEDS_2G5 | LEDS_LINK | LEDS_ACT, // Green LED (right)
0,
LEDS_1G | LEDS_100M | LEDS_10M | LEDS_LINK | LEDS_ACT, // Amber LED (left)
0
},
{ LEDS_10G | LEDS_5G | LEDS_2G5 | LEDS_1G | LEDS_100M | LEDS_LINK | LEDS_ACT, // SFP LED
0,
0,
0
},
},
};
#elif defined MACHINE_F7008_2_5
__code const struct machine machine = {
.machine_name = "F7008-2.5 8+1 SFP Port Switch",
+1
View File
@@ -48,6 +48,7 @@
// #define MACHINE_POE_2G080110GS
// #define MACHINE_PB_2132
// #define MACHINE_HASIVO_S600W_4GT_2XGT_SE
// #define MACHINE_HASIVO_S1100WP_8GT_1SX_SE
// #define MACHINE_F7008_2_5
#define LED_27 1
+8
View File
@@ -7,6 +7,14 @@
#define SYS_TICK_HZ 200
/* Timers of the main loop count system ticks, not loop passes: a pass is as
* long as the packets it handles, and since #483 the loop no longer sleeps
* while packets wait, so passes can outrun the tick as well as lag it. `last`
* is the low byte of ticks when the timer last ran; the byte arithmetic wraps
* every 256 ticks, so a pass longer than 1.28 s loses time rather than
* running the timer 64 times to catch up. */
#define TICKS_DUE(last, period) ((uint8_t)((uint8_t)ticks - (last)) >= (period))
#define CPU_PORT 9
#define NUL '\0'
+20 -3
View File
@@ -23,6 +23,8 @@
extern __code const uint16_t bit_mask[16];
extern __code const struct machine machine;
extern __xdata struct machine_runtime machine_detected;
extern volatile __xdata uint32_t ticks;
extern __xdata uint32_t rtl8224_release_tick;
__xdata struct phy_settings phy_settings;
@@ -214,9 +216,20 @@ void phy_config(uint8_t phy) __banked
}
void rtl8224_wait_ready(void) __banked
{
do {
rtl8224_read_reg_u16(RTL837X_REG_CHIP_ID + 1);
if (SFR_DATA_U16 == 0x8224)
return;
} while ((ticks - rtl8224_release_tick) < (SYS_TICK_HZ * 5 / 2));
print_string("\r\nRTL8224 not responding\r\n");
}
void phy_config_8224(void) __banked
{
uint16_t pval;
uint16_t tries;
print_string("\r\nphy_config_8224 called\r\nRTL8224 ID: ");
// Print RTL8224 chip id
@@ -244,9 +257,12 @@ void phy_config_8224(void) __banked
i++;
rtl8224_write_reg_u16(RTL837X_SDS_INDACS_CMD, rtl8224_sds0_setttings[i]);
i++;
tries = 0xffff;
do {
rtl8224_read_reg_u16(0x3f8);
} while (SFR_DATA_8 & 0x80);
} while ((SFR_DATA_8 & 0x80) && --tries);
if (SFR_DATA_8 & 0x80)
print_string("\r\nRTL8224 SerDes access timed out\r\n");
}
print_string("\r\nphy_config_8224 done\r\n");
@@ -354,8 +370,9 @@ void phy_set_speed(void) __banked
phy_modify(phy_settings.port, PHY_MMD31, PHY_MMD31_GBCR, 0x0200, 0x0000);
} else {
// AN Advertisement Register (MMD 7.0x0010)
// bits 0-4: 0x1 (802.3 supported), Extended Next Page format used
phy_write(phy_settings.port, PHY_MMD_AN, PHY_ANEG_ADV, 0x1001);
// bits 0-4: 0x1 (802.3 supported), bit 10: PAUSE,
// Extended Next Page format used
phy_write(phy_settings.port, PHY_MMD_AN, PHY_ANEG_ADV, 0x1401);
if (phy_settings.speed == PHY_SPEED_1G) {
// Multi-GBASE-TBASE-T AN Control 1 Register (MMD 7.0x0020)
// bit 14: SLAVE, bit 13: Multi-Port device, 1: LD Loop timin enableed
+1
View File
@@ -22,6 +22,7 @@ extern __xdata struct phy_settings phy_settings;
void rtl8224_phy_enable(void) __banked;
void phy_config(uint8_t phy) __banked;
void phy_config_8224(void) __banked;
void rtl8224_wait_ready(void) __banked;
void phy_set_speed(void) __banked;
void phy_set_duplex(void) __banked;
void phy_show(uint8_t port) __banked;
+33 -12
View File
@@ -40,7 +40,6 @@ static void wait_table_ready(void)
void port_mirror_set(uint8_t port, __xdata uint16_t rx_pmask, __xdata uint16_t tx_pmask) __banked
{
print_string("\nport_mirror_set called \n");
print_string("Mirroring port: "); print_byte(port); print_string(" with rx-mask: ");
print_short(rx_pmask); print_string(", tx mask: "); print_short(tx_pmask);
write_char('\n');
@@ -52,7 +51,6 @@ void port_mirror_set(uint8_t port, __xdata uint16_t rx_pmask, __xdata uint16_t t
void port_mirror_del(void) __banked
{
print_string("\nport_mirror_del called \n");
REG_SET(RTL837x_MIRROR_CTRL, 0);
}
@@ -112,7 +110,6 @@ void port_pvid_set(uint8_t port, __xdata uint16_t pvid) __banked
{
uint8_t lag = port_lag_of(port);
print_string("\nport_pvid_set called \n");
if (lag == PORT_LAG_NONE) {
port_pvid_write(port, pvid);
return;
@@ -141,7 +138,6 @@ void vlan_delete(uint16_t vlan) __banked
if (!vlan || vlan >= 0xfff)
return;
print_string("\nvlan_delete called \n"); print_short(vlan);
vlan_name_remove(vlan);
REG_WRITE(RTL837x_TBL_DATA_IN_A, 0, 0, 0, 0);
REG_WRITE(RTL837X_TBL_CTRL, vlan >> 8, vlan, TBL_VLAN, TBL_WRITE | TBL_EXECUTE);
@@ -232,7 +228,7 @@ void vlan_create(void) __banked
vlan_settings.members |= 0x0200; // Set 10th bit
vlan_settings.tagged |= 0x0200;
print_string("\nvlan_create called\nvlan: "); print_short(vlan_settings.vlan);
print_string("vlan: "); print_short(vlan_settings.vlan);
print_string(", members: "); print_short(vlan_settings.members);
print_string(", tagged: "); print_short(vlan_settings.tagged); write_char('\n');
@@ -250,7 +246,6 @@ void vlan_create(void) __banked
wait_table_ready();
print_string("vlan_create done \n");
}
@@ -263,7 +258,6 @@ void vlan_create(void) __banked
*/
void vlan_setup(void) __banked
{
print_string("\nvlan_setup called \n");
// No VLAN names set up so far
vlan_ptr = 0;
@@ -331,7 +325,6 @@ void vlan_setup(void) __banked
print_string("\nvlan_setup, REG 0x4f4c: "); print_reg(0x4f4c);
#endif
print_string("vlan_setup done \n");
}
@@ -354,7 +347,6 @@ void port_l2_forget_port(uint8_t port) __banked
*/
uint8_t port_l2_forget(void) __banked
{
print_string("\nport_l2_forget called\n");
// Configure the entries to be flushed:
// port-based (bits 0-1 are 0 and dynamic entries, bit 2 specifies dynamic entries
REG_SET(RTL837x_L2_TBL_FLUSH_CNF, 0x0);
@@ -370,7 +362,6 @@ uint8_t port_l2_forget(void) __banked
reg_read_m(RTL837x_L2_TBL_FLUSH_CTRL);
} while (sfr_data[1]);
print_string("port_l2_forget done\n");
return 0;
}
@@ -465,7 +456,6 @@ void port_l2mc_set(uint8_t mac_last, __xdata uint16_t vid, __xdata uint16_t pmas
*/
void port_l2_setup(void) __banked
{
print_string("\nport_l2_setup called\n");
port_l2_forget();
@@ -483,7 +473,38 @@ void port_l2_setup(void) __banked
// When maximim entries learned, then simply flood the packet
reg_bit_set(RTL837X_L2_LRN_PORT_CONSTRT_ACT, 0);
print_string("\nport_l2_setup done\n");
}
void port_media_show(uint8_t port) __banked
{
uint8_t pause;
if (!machine.is_sfp[port]) {
reg_read_m(RTL837X_REG_LINKS_STS);
if (sfr_data[(port / 8) + 1] >> (port % 8) & 1) {
print_string("Link role: ");
reg_read_m(RTL837X_MAC_MSTR_SLV_STS);
if (sfr_data[3 - (port >> 3)] & (1 << (port & 7)))
print_string("master\n");
else
print_string("slave\n");
}
}
print_string("Pause negotiated:");
reg_read_m(RTL837X_MAC_TX_PAUSE_STS);
pause = sfr_data[3 - (port >> 3)] & (1 << (port & 7));
if (pause)
print_string(" TX");
reg_read_m(RTL837X_MAC_RX_PAUSE_STS);
if (sfr_data[3 - (port >> 3)] & (1 << (port & 7))) {
print_string(" RX");
pause = 1;
}
if (!pause)
print_string(" none");
write_char('\n');
}
+1
View File
@@ -49,6 +49,7 @@ extern __xdata struct vlan_settings vlan_settings;
uint8_t port_l2_forget(void) __banked;
void port_l2_learned(void) __banked;
void port_stats_print(void) __banked;
void port_media_show(uint8_t port) __banked;
int8_t vlan_get(uint16_t vlan) __banked;
__xdata uint16_t vlan_name(uint16_t vlan) __banked;
void vlan_name_remove(uint16_t vlan) __banked;
+11
View File
@@ -83,6 +83,10 @@
#define RTL837X_REG_LINKS 0x63f0
#define RTL837X_REG_LINKS_89 0x63f4
#define RTL837X_REG_LINKS_STS 0x63E8
/* Per-port status bits: bit N is chip port N */
#define RTL837X_MAC_TX_PAUSE_STS 0x63FC
#define RTL837X_MAC_RX_PAUSE_STS 0x6400
#define RTL837X_MAC_MSTR_SLV_STS 0x6408
/* Each nibble encodes the link state of a port.
Port 0 appears to be the CPU port
@@ -141,6 +145,10 @@
#define RTL837X_REG_RX_CTRL 0x785c
#define RTL837X_REG_TX_CTRL 0x7860
#define RTL837X_REG_NIC_RX_BUFF_DATA 0x7874
#define RTL837X_NIC_INT_STS 0x7854
#define RTL837X_NIC_INT_MSK 0x7858
#define NIC_INT_RXIS 0x02
#define NIC_INT_RXIE 0x02
#define RTL837X_REG_CPU_RX_CURR_PKT 0x787c
#define RTL837X_REG_NIC_TX_CURR_PKT 0x7884
#define RTL837X_REG_CPU_TX_CURR_PKT 0x7890
@@ -270,6 +278,9 @@
*/
#define RTL8373_RLDP_TIMER 0x1074
#define RTL837X_RMA0_CONF 0x4ecc
#define RTL837X_IMR_INT_PORT_LINK_STS_CHG 0x5f34
#define RTL837X_ISR_SW_INT_MODE 0x5f84
#define RTL837X_ISR_INT_PORT_LINK_CHG 0x5f88
#define RTL837X_RMA_CONF 0x4f1c
#define RTL837X_MSTP_STATES 0x5310
#define RTL837X_REG_LED_RLDP_1 0x65F8
+123 -76
View File
@@ -88,11 +88,14 @@ __xdata struct uip_eth_addr uip_ethaddr;
volatile __xdata uint32_t ticks;
volatile __xdata uint8_t sec_counter;
volatile __xdata uint16_t sleep_ticks;
__xdata uint8_t stp_clock;
__xdata uint8_t stp_tick_last;
__xdata uint8_t arp_age_secs;
extern __xdata struct dhcp_state dhcp_state;
#define STP_TICK_DIVIDER 3
#define STP_TICK_STEP (SYS_TICK_HZ / STP_HZ)
#define STP_CATCH_UP 8
#define SFP_TICK_STEP 10
__xdata uint8_t sfp_tick_last;
/* Buffer for serial input, SBUF_SIZE must be power of 2 < 256
* Writing to this buffer is under the sole control of the serial ISR
@@ -134,6 +137,11 @@ __code const uint16_t bit_mask[16] = {
__xdata uint8_t linkbits_last[4];
__xdata uint8_t linkbits_last_p89;
__data __at(0x20) volatile uint8_t evflags;
volatile __bit __at(0x00) link_irq;
volatile __bit __at(0x01) rx_irq;
volatile __bit __at(0x02) tick_pending;
__bit __at(0x03) cmd_available;
// Last known state of the SFP detection/Loss of Signal pins
__xdata bool button_last;
__xdata uint8_t button_sec_counter_last;
@@ -195,6 +203,7 @@ void isr_timer0(void) __interrupt(1)
void isr_timer2(void) __interrupt(5)
{
ticks++;
tick_pending = 1;
if (sleep_ticks > 0)
sleep_ticks--;
sec_counter++;
@@ -489,14 +498,14 @@ void print_cmd_prompt(void)
}
/*
* External IRQ 0 Service Routine: Called on link change?
* Note that all registers are being put on the STACK because of calling a subroutine
* External IRQ 0 Service Routine: the switch reports a link change. The
* register access macros share the SFR data bytes with the main loop, so
* the handler only latches the event; idle() reads and clears it.
*/
void isr_ext0(void) __interrupt(0)
{
EX0 = 0; // Disable interrupt for the moment
IT0 = 1; // Trigger on falling edge of external interrupt
EX0 = 1; // Re-enable interrupt
EX0 = 0;
link_irq = 1;
}
@@ -505,9 +514,8 @@ void isr_ext0(void) __interrupt(0)
*/
void isr_ext1(void) __interrupt(2)
{
// This flag should only be reset after all packets have been read
EX1 = 0;
EX1 = 1;
rx_irq = 1;
}
/*
@@ -1015,6 +1023,7 @@ void handle_rx(void)
REG_SET(RTL837X_REG_NIC_RXCMD, 1);
return;
}
health_rx_frame();
#ifdef RXTXDBG
print_string("\n<< ");
@@ -1168,56 +1177,8 @@ void handle_button(void)
}
}
//
// An idle function that sleeps for 1 tick and does all the house-keeping
//
void idle(void)
void check_links(void)
{
reg_read(RTL837X_REG_NIC_RX_BUFF_DATA);
if (!SFR_DATA_U16)
PCON |= 1;
if (sec_counter >= SYS_TICK_HZ) {
sec_counter -= SYS_TICK_HZ;
reg_read_m(RTL837X_REG_SEC_COUNTER);
uint8_t v = sfr_data[3];
#ifdef DEBUG
print_string(" Tick counter: "); print_long(ticks); write_char('\n');
#endif
v++;
sfr_data[3] = v;
if (!v) {
v = sfr_data[2];
v++;
sfr_data[2] = v;
if (!v) {
v = sfr_data[1];
v++;
sfr_data[1] = v;
if (!v) {
v = sfr_data[0];
v++;
sfr_data[0] = v;
}
}
}
reg_write_m(RTL837X_REG_SEC_COUNTER);
reg_read_m(RTL837X_REG_SEC_COUNTER);
// Check for button presses once a second
handle_button();
// Age the ARP cache: uip_arp_timer() expects a 10 s cadence
if (++arp_age_secs >= 10) {
arp_age_secs = 0;
uip_arp_timer();
}
#ifdef DEBUG
print_sfr_data();
write_char('\n');
#endif
}
// Check for Link changes
reg_read_m(RTL837X_REG_LINKS_89);
__xdata uint8_t linkbits_p89 = sfr_data[3];
@@ -1250,24 +1211,102 @@ void idle(void)
cpy_4(linkbits_last, sfr_data);
}
}
}
// Check for changes with SFP modules
//
// An idle function that sleeps for 1 tick and does all the house-keeping
//
static void handle_tick(void)
{
if (sec_counter >= SYS_TICK_HZ) {
sec_counter -= SYS_TICK_HZ;
reg_read_m(RTL837X_REG_SEC_COUNTER);
uint8_t v = sfr_data[3];
#ifdef DEBUG
print_string(" Tick counter: "); print_long(ticks); write_char('\n');
#endif
v++;
sfr_data[3] = v;
if (!v) {
v = sfr_data[2];
v++;
sfr_data[2] = v;
if (!v) {
v = sfr_data[1];
v++;
sfr_data[1] = v;
if (!v) {
v = sfr_data[0];
v++;
sfr_data[0] = v;
}
}
}
reg_write_m(RTL837X_REG_SEC_COUNTER);
reg_read_m(RTL837X_REG_SEC_COUNTER);
// Check for button presses once a second
handle_button();
link_irq = 1;
// Age the ARP cache: uip_arp_timer() expects a 10 s cadence
if (++arp_age_secs >= 10) {
arp_age_secs = 0;
uip_arp_timer();
}
#ifdef DEBUG
print_sfr_data();
write_char('\n');
#endif
}
health_phase(HEALTH_PH_LINK);
if (TICKS_DUE(sfp_tick_last, SFP_TICK_STEP)) {
sfp_tick_last += SFP_TICK_STEP;
handle_sfp();
// Check new Packets RX
handle_rx();
// Check UIP for packets to transmit
}
health_phase(HEALTH_PH_SFP);
handle_tx();
// If STP protocol enabled, decrease STP timers to trigger actions
health_phase(HEALTH_PH_TX);
if (stp_enabled) {
if (!stp_clock) {
stp_clock = STP_TICK_DIVIDER;
uint8_t n = STP_CATCH_UP;
while (n-- && TICKS_DUE(stp_tick_last, STP_TICK_STEP)) {
stp_tick_last += STP_TICK_STEP;
stp_timers();
} else {
stp_clock--;
}
}
// Check whether a command is waiting in the cmd_buffer and execute
health_phase(HEALTH_PH_STP);
}
void idle(void)
{
if (!evflags)
PCON |= 1;
health_loop_start();
if (tick_pending) {
tick_pending = 0;
handle_tick();
}
if (link_irq) {
link_irq = 0;
REG_SET(RTL837X_ISR_INT_PORT_LINK_CHG, 0x3ff);
EX0 = 1;
check_links();
}
health_phase(HEALTH_PH_LINK);
if (rx_irq) {
rx_irq = 0;
handle_rx();
REG_SET(RTL837X_NIC_INT_STS, NIC_INT_RXIS);
reg_read(RTL837X_REG_NIC_RX_BUFF_DATA);
if (SFR_DATA_U16)
rx_irq = 1;
else
EX1 = 1;
}
health_phase(HEALTH_PH_RX);
if (cmd_available) {
cmd_available = 0;
cmd_tokenize();
@@ -1275,6 +1314,7 @@ void idle(void)
cmd_parser();
print_cmd_prompt();
}
health_phase(HEALTH_PH_CMD);
}
@@ -1300,14 +1340,16 @@ void reset_chip(void)
void setup_external_irqs(void)
{
REG_SET(0x5f84, 0x42);
REG_SET(0x5f34, 0x3ff);
REG_SET(RTL837X_ISR_SW_INT_MODE, 0x42);
REG_SET(RTL837X_ISR_INT_PORT_LINK_CHG, 0x3ff);
REG_SET(RTL837X_IMR_INT_PORT_LINK_STS_CHG, 0x3ff);
// EX0 = 1; // Enable external IRQ 0 (Link-change)
EX0 = 0;
IT0 = 1; // External IRQ on falling edge
link_irq = 1;
IT0 = 1; // External IRQ 0 on falling edge (link change)
EX0 = 1;
EX1 = 1; // External IRQ 1 enable
rx_irq = 1;
EX1 = 1; // External IRQ 1: the NIC has received a packet
EX2 = 1; // External IRQ 2 enable: bit EIE.0
EX3 = 1; // External IRQ 3 enable: bit EIE.1
PX3 = 1; // Set EIP.1 = 1: External IRQ 3 set to high priority
@@ -1588,7 +1630,8 @@ void check_and_flash_update_image(void)
void main(void)
{
ticks = 0;
stp_clock = STP_TICK_DIVIDER;
stp_tick_last = (uint8_t)ticks;
sfp_tick_last = (uint8_t)ticks;
dhcp_state.state = DHCP_OFF;
sbuf_ptr = 0;
@@ -1692,6 +1735,8 @@ void main(void)
REG_SET(RTL837X_PIN_MUX_2, 0x0); // Disable pins for ACL
init_smi();
if (machine_detected.isRTL8373)
rtl8224_wait_ready();
rtl8373_revision();
@@ -1732,6 +1777,7 @@ void main(void)
stp_enabled = 0;
stp_defaults(); /* 802.1D/w default config before any "stp ..." replay */
nic_setup();
REG_SET(RTL837X_NIC_INT_MSK, NIC_INT_RXIE);
vlan_setup();
port_l2_setup();
igmp_setup();
@@ -1769,6 +1815,7 @@ void main(void)
cmd_editor_init();
health_stack_paint();
while (1) {
cmd_edit();
idle(); // Enter Idle mode until interrupt occurs
+5 -1
View File
@@ -27,7 +27,8 @@ BUILDDIR = build
TESTS = $(BUILDDIR)/test_cmd_editor \
$(BUILDDIR)/test_port_tables \
$(BUILDDIR)/test_page_json \
$(BUILDDIR)/test_httpd_tx
$(BUILDDIR)/test_httpd_tx \
$(BUILDDIR)/test_tick_gate
# The table tests link the register mock and one environment file; stub/
# stands in for the two headers a firmware build generates.
@@ -47,6 +48,9 @@ $(BUILDDIR)/test_cmd_editor: test_cmd_editor.c support.c $(SRCROOT)/cmd_editor.c
$(BUILDDIR)/test_port_tables: test_port_tables.c $(TABLE_ENV) hw_mock.h | $(BUILDDIR)
$(CC) $(CFLAGS) $(filter %.c,$^) -o $@ $(LDFLAGS)
$(BUILDDIR)/test_tick_gate: test_tick_gate.c support.c $(SRCROOT)/cmd_editor.c | $(BUILDDIR)
$(CC) $(CFLAGS) $^ -o $@ $(LDFLAGS)
$(BUILDDIR)/test_page_json: test_page_json.c $(TABLE_ENV) hw_mock.h | $(BUILDDIR)
$(CC) $(CFLAGS) $(filter %.c,$^) -o $@ $(LDFLAGS)
+1
View File
@@ -27,6 +27,7 @@ uint8_t cmd_available;
uint8_t err_status;
uint8_t cmd_history[CMD_HISTORY_SIZE];
uint16_t cmd_history_ptr;
__xdata uint16_t session_timeout = 200; /* httpd global, referenced by page_impl.c */
/* editor-internal read pointer (defined in cmd_editor.c, external linkage) */
extern __xdata uint8_t l;
+83
View File
@@ -0,0 +1,83 @@
/*
* test_tick_gate.c - the timers of the main loop keep time by the system
* tick, however often the loop passes. The gate is the TICKS_DUE macro from
* rtl837x_common.h; the loop bodies of idle() and handle_tx() are restated
* here, so this checks the arithmetic, not those functions.
*/
#include <stdint.h>
#include <stdio.h>
#include "rtl837x_common.h"
#include "rtl837x_stp.h"
#include "support.h"
volatile uint32_t ticks;
#define STP_TICK_STEP (SYS_TICK_HZ / STP_HZ)
#define STP_CATCH_UP 8
static uint8_t stp_tick_last, tx_tick_last;
static unsigned stp_calls, tx_calls;
/* One pass of the loop: the STP block of idle() and the gate of handle_tx() */
static void pass(void)
{
uint8_t n = STP_CATCH_UP;
while (n-- && TICKS_DUE(stp_tick_last, STP_TICK_STEP)) {
stp_tick_last += STP_TICK_STEP;
stp_calls++;
}
if (TICKS_DUE(tx_tick_last, 1)) {
tx_tick_last = (uint8_t)ticks;
tx_calls++;
}
}
/* Run `seconds` of wall clock with one pass every `pass_us` microseconds. */
static void run(double pass_us, unsigned seconds)
{
double t = 0, next_tick = 0, tick_us = 1e6 / SYS_TICK_HZ, end = seconds * 1e6;
ticks = 0;
stp_tick_last = tx_tick_last = 0;
stp_calls = tx_calls = 0;
while (t < end) {
while (next_tick <= t) { ticks++; next_tick += tick_us; }
pass();
t += pass_us;
}
}
static void t_rates(void)
{
printf("[test] STP time follows the tick whatever the loop does\n");
run(5000, 10);
CHECK(stp_calls == 10 * STP_HZ, "one pass per tick: 500 STP steps in 10 s");
CHECK(tx_calls == 10 * SYS_TICK_HZ, "and 2000 TX periodics");
run(16000, 10);
CHECK(stp_calls >= 10 * STP_HZ - 1 && stp_calls <= 10 * STP_HZ, "a 16 ms pass, as under an IPv4 stream: still 500 (was 156 when the clock counted passes)");
CHECK(tx_calls == 625, "TX periodic once per pass when passes are slower than the tick");
run(600, 10);
CHECK(stp_calls == 10 * STP_HZ, "a 0.6 ms pass, the loop no longer sleeping: still 500 (was 4166)");
CHECK(tx_calls == 10 * SYS_TICK_HZ, "TX periodic capped at the tick rate");
run(1300000, 13);
CHECK(stp_calls <= 10 * STP_CATCH_UP, "a 1.3 s pass loses time instead of firing 65 steps at once");
}
static void t_wrap(void)
{
printf("[test] the byte arithmetic survives the tick counter wrapping\n");
ticks = 0xfffffffeUL;
stp_tick_last = (uint8_t)ticks;
stp_calls = 0;
for (int i = 0; i < 8; i++) { ticks++; pass(); }
CHECK(stp_calls == 2, "two STP steps across 0xffffffff -> 0x00000006");
}
int main(void)
{
printf("== tick gate tests ==\n");
t_rates();
t_wrap();
printf("\n%d checks, %d failed\n", tests_run, tests_failed);
return tests_failed ? 1 : 0;
}