stp: count the BPDUs each port has sent

The table could say a port was designated and had heard nothing, which is
two different situations wearing the same face: either we are not
announcing on that segment, or we are and nobody is answering. Telling them
apart needed a capture on the far side.

    port state role edge tx bpdu
     05  fwd   desg yes  2a 255
     01  block desg no   2a 21
     03  fwd   root no   00 0

The tx column counts BPDUs actually handed to the hardware, so it moves
only past the enable, filter and tx hold checks in stp_cnf_send(). A
designated port has to show it climbing once per hello time. The root port
never does, because we do not announce back towards the root, so a
neighbour that has taken us as root falls silent in both directions on that
link and the two columns together say exactly that rather than looking like
a fault.

The counter is a byte and wraps at 256. It is meant to be watched moving,
not summed, and it starts again when STP is enabled.

67 bytes of BANK2 and 10 of xdata, nothing in BANK1 or internal RAM. Built
for SWTGW218AS and KP_9000_6XHML_X2 on sdcc 4.5.0.
This commit is contained in:
d00f
2026-08-18 23:30:38 +02:00
parent 266c95e4d7
commit 2c26a4f389
+12 -2
View File
@@ -67,6 +67,7 @@ __xdata uint16_t port_timers[10]; /* listen-period countdown (0 = not listening)
__xdata uint16_t port_hello[10]; /* hello TX countdown */ __xdata uint16_t port_hello[10]; /* hello TX countdown */
__xdata uint16_t stp_bpdu_age[10]; /* ticks since last BPDU seen on port (saturating) */ __xdata uint16_t stp_bpdu_age[10]; /* ticks since last BPDU seen on port (saturating) */
__xdata uint8_t stp_tx_budget[10]; /* tx hold: BPDUs left in the current second */ __xdata uint8_t stp_tx_budget[10]; /* tx hold: BPDUs left in the current second */
__xdata uint8_t stp_tx_count[10]; /* BPDUs actually put on the wire, wraps at 256 */
__xdata uint16_t stp_sec_tick; /* 1 s window for the tx budget */ __xdata uint16_t stp_sec_tick; /* 1 s window for the tx budget */
__xdata uint16_t stp_link_prev; /* carrier bitmap as of the last check */ __xdata uint16_t stp_link_prev; /* carrier bitmap as of the last check */
__xdata uint16_t stp_link_now; __xdata uint16_t stp_link_now;
@@ -192,7 +193,7 @@ static void stp_status(void)
print_string("changes "); print_string("changes ");
print_short(stp_tc_count); print_short(stp_tc_count);
write_char('\n'); write_char('\n');
print_string("port state role edge bpdu\n"); print_string("port state role edge tx bpdu\n");
reg_read_m(RTL837X_MSTP_STATES); reg_read_m(RTL837X_MSTP_STATES);
for (stp_i = machine.min_port; stp_i <= machine.max_port; stp_i++) { for (stp_i = machine.min_port; stp_i <= machine.max_port; stp_i++) {
write_char(' '); write_char(' ');
@@ -206,11 +207,18 @@ static void stp_status(void)
print_field(stp_role_txt, stp_i == stp_root_port ? 1 : 0, 4); print_field(stp_role_txt, stp_i == stp_root_port ? 1 : 0, 4);
write_char(' '); write_char(' ');
print_field(stp_edge_txt, stp_pflags[stp_i] & STP_PF_OPEREDGE ? 1 : 0, 4); print_field(stp_edge_txt, stp_pflags[stp_i] & STP_PF_OPEREDGE ? 1 : 0, 4);
/* BPDUs we put on the wire here. A designated port must show this
* climbing once per hello; the root port never does, because we do
* not announce back towards the root. Without it the only way to
* tell "we are silent" from "the neighbour is not listening" is a
* capture on the far side. */
write_char(' ');
print_byte(stp_tx_count[stp_i]);
/* Seconds since the last BPDU on this port, capped at 255. Without /* Seconds since the last BPDU on this port, capped at 255. Without
* it nothing in the output separates "nobody is speaking (R)STP * it nothing in the output separates "nobody is speaking (R)STP
* out there" from "we are dropping what arrives", and stp_in() * out there" from "we are dropping what arrives", and stp_in()
* leaves on eight different conditions without saying so. */ * leaves on eight different conditions without saying so. */
print_string(" "); write_char(' ');
stp_scratch16 = stp_bpdu_age[stp_i] / STP_HZ; stp_scratch16 = stp_bpdu_age[stp_i] / STP_HZ;
itoa(stp_scratch16 > 255 ? 255 : (uint8_t)stp_scratch16); itoa(stp_scratch16 > 255 ? 255 : (uint8_t)stp_scratch16);
write_char('\n'); write_char('\n');
@@ -330,6 +338,7 @@ void stp_cnf_send(uint8_t port) __reentrant
return; return;
} }
stp_tx_budget[port]--; stp_tx_budget[port]--;
stp_tx_count[port]++;
STP_O->stp_addr[0] = 0x01; STP_O->stp_addr[1] = 0x80; STP_O->stp_addr[2] = 0xc2; STP_O->stp_addr[0] = 0x01; STP_O->stp_addr[1] = 0x80; STP_O->stp_addr[2] = 0xc2;
STP_O->stp_addr[3] = STP_O->stp_addr[4] = STP_O->stp_addr[5] = 0x00; STP_O->stp_addr[3] = STP_O->stp_addr[4] = STP_O->stp_addr[5] = 0x00;
@@ -745,6 +754,7 @@ void stp_setup(void) __banked
stp_pflags[stp_i] &= ~(STP_PF_OPEREDGE | STP_PF_TRIPPED); stp_pflags[stp_i] &= ~(STP_PF_OPEREDGE | STP_PF_TRIPPED);
stp_bpdu_age[stp_i] = 0; stp_bpdu_age[stp_i] = 0;
stp_tx_budget[stp_i] = stp_txhold; stp_tx_budget[stp_i] = stp_txhold;
stp_tx_count[stp_i] = 0;
if (!(stp_pflags[stp_i] & STP_PF_ENABLED) || (stp_pflags[stp_i] & STP_PF_ADMEDGE)) { if (!(stp_pflags[stp_i] & STP_PF_ENABLED) || (stp_pflags[stp_i] & STP_PF_ADMEDGE)) {
/* not participating, or admin edge: forwarding immediately */ /* not participating, or admin edge: forwarding immediately */
if (stp_pflags[stp_i] & STP_PF_ADMEDGE) if (stp_pflags[stp_i] & STP_PF_ADMEDGE)