stp: actually promote ports out of blocking; calibrate timers

"stp on" put every port into blocking (stp_setup, port_timers = "10 s") but
nothing ever counted those timers down: stp_timers() only sent hello BPDUs.
On a network with no other (R)STP bridge - i.e. nobody sends us BPDUs - every
port therefore stayed blocking FOREVER and enabling STP took the whole
network down until "stp off".

- stp_timers(): count port_timers down; when a port's listen period expires
  with no better root heard, promote it to forwarding in MSTP_STATES (we are
  the designated bridge on that port).
- Calibrate the tick constants to the real stp_timers() rate (~64 Hz: main
  loop ~256 Hz / (STP_TICK_DIVIDER+1)): TIME_HELLO 0x200->0x80 is an actual
  2 s hello, port_timers 0xa00->0x280 an actual 10 s listen period. Measured
  before the fix, ports converged only after ~40 s.
- Move struct bridge into rtl837x_stp.h and export root_bridge/-_cost for
  the web UI status endpoint.

Verified on hardware: "stp on" -> ports report Blocking, after the 10 s
listen period all ports promote to Forwarding and LAN connectivity returns;
"stp off" restores forwarding immediately. We elect ourselves root (weRoot)
with no other bridge present.

(cherry picked from commit 8537a15ca254b2122272b20bec7a66426e86df4b)
This commit is contained in:
d00f
2026-08-04 03:18:40 +02:00
parent fad7170518
commit 115aa8d3f6
2 changed files with 33 additions and 8 deletions
+18 -7
View File
@@ -34,12 +34,7 @@ extern __xdata struct uip_eth_addr uip_ethaddr;
extern __xdata uint8_t uip_buf[UIP_CONF_BUFFER_SIZE + 2];
struct bridge {
uint8_t prio;
uint8_t ext;
uint8_t mac[6];
};
/* struct bridge now lives in rtl837x_stp.h (shared with the web UI). */
__xdata struct bridge root_bridge;
__xdata uint32_t root_bridge_cost;
@@ -211,6 +206,22 @@ void stp_timers(void) __banked
print_byte(i); write_char('\n');
stp_cnf_send(i);
}
/* Promote a port out of the initial blocking state once its listen
* period expires. stp_setup() puts every port into blocking with
* port_timers = 10 s, but nothing ever counted that down - so on a
* network with no other RSTP bridge (nobody sends us BPDUs) every
* port stayed blocking FOREVER and "stp on" killed the whole
* network. If no better root was heard during the listen period we
* are the designated bridge on that port: go to forwarding. */
if (port_timers[i]) {
if (!--port_timers[i]) {
reg_read_m(RTL837X_MSTP_STATES);
sfr_data[3 - (i >> 2)] |= (uint8_t)(0b11 << ((i << 1) & 0x7));
reg_write_m(RTL837X_MSTP_STATES);
print_string("STP: port forwarding ");
print_byte(i); write_char('\n');
}
}
}
}
@@ -258,7 +269,7 @@ void stp_setup(void) __banked
uint8_t bit_mask = 0b01 << ( (i << 1) & 0x7);
sfr_data[3 - (i >> 2)] |= bit_mask;
port_hello[i] = TIME_HELLO;
port_timers[i] = 0xa00; // 10 sec in blocking state
port_timers[i] = 0x280; // 10 s in blocking state (at the ~64 Hz stp_timers rate)
}
sfr_data[1] |= 0x0f; // Do not block CPU-Port
reg_write_m(RTL837X_MSTP_STATES); // R5310-000d555f
+15 -1
View File
@@ -7,6 +7,20 @@ void stp_setup(void) __banked;
void stp_timers(void) __banked;
void stp_off(void) __banked;
#define TIME_HELLO 0x200 // 2 sec
#define TIME_HELLO 0x80 // 2 sec (stp_timers runs at ~64 Hz: main loop ~256 Hz / STP_TICK_DIVIDER+1)
/* Bridge identifier as carried in a BPDU (priority, extension, MAC). */
struct bridge {
uint8_t prio;
uint8_t ext;
uint8_t mac[6];
};
/* Protocol state, exposed read-only for the web UI (page_impl.c send_stp())
* - the elected root bridge and our path cost to it. Owned by rtl837x_stp.c;
* stpEnabled is owned by rtlplayground.c. */
extern __xdata uint8_t stpEnabled;
extern __xdata struct bridge root_bridge;
extern __xdata uint32_t root_bridge_cost;
#endif