httpd: emit valid JSON from send_l2

The MAC table listing wrote its separator once per iteration rather than
once per object. An entry the table engine reports as invalid produces no
object, so it contributed a bare comma, and two in a row give ",," which
JSON.parse rejects. The whole table then fails to load, not just the row
that was missing. The separator now goes before each object and the
closing bracket after the loop, which is the shape send_vlanlist already
uses further down the file.

The next index for an invalid entry was computed as h | low + 1, and the
addition binds tighter than the or. That agrees with (h | low) + 1 except
when the low byte reads 0xff and bit 8 of the index is already set, eight
of the 4096 combinations. There the result is the start of the current
block of 256 rather than the start of the next one, so the walk repeats a
block it has already covered. Reading the index once after the branch
rather than once in each arm removes the second copy of that expression
along with the bug.

The VLAN now comes first in each object. It is taken from the same
L2_DATA_OUT_B read that decides whether the entry is valid, which saves
reading that register a second time. The page addresses the fields by
name, so the order they arrive in does not matter to it.

A bound check on the output buffer goes in for consistency with
send_vlanlist. Thirty entries of at most 74 bytes plus the brackets fit in
the 2500 byte buffer with 179 to spare, so nothing changes today, but the
margin was nowhere stated and L2_MAX_TRANSFER is a tunable.

5 bytes of BANK1, nothing in BANK2, xdata or internal RAM. Built for SWTGW218AS
and KP_9000_6XHML_X2 on sdcc 4.5.0.
This commit is contained in:
d00f
2026-08-15 23:14:48 +02:00
parent 5103d1c168
commit 3be6667789
+27 -25
View File
@@ -356,6 +356,7 @@ void send_l2(uint16_t idx)
*/
__xdata uint16_t entry = idx & 0xfff;
__xdata uint16_t first_entry = 0xffff; // An illegal entry index
bool first = true;
char_to_html('[');
while (1) {
entries_left--;
@@ -369,9 +370,22 @@ void send_l2(uint16_t idx)
} while (sfr_data[3] & TBL_EXECUTE);
reg_read_m(RTL837x_L2_DATA_OUT_B);
if ((sfr_data[0] & 0x20)) { // Check entry is valid
bool valid = (sfr_data[0] & 0x20) != 0;
if (valid) {
/* separator + 74-byte worst-case entry + closing "]" */
if (slen + 76 > TCP_OUTBUF_SIZE)
break;
if (!first)
char_to_html(',');
first = false;
// VLAN, taken from the read above instead of reading the register twice
slen += strtox(outbuf + slen, "{\"vlan\":\"");
charhex_to_html(sfr_data[0] & 0x0f);
byte_to_html(sfr_data[1]);
// MAC
slen += strtox(outbuf + slen, "{\"mac\":\"");
slen += strtox(outbuf + slen, "\",\"mac\":\"");
byte_to_html(sfr_data[2]); char_to_html(':');
byte_to_html(sfr_data[3]); char_to_html(':');
port = (sfr_data[0] >> 6) & 0x3;
@@ -381,12 +395,6 @@ void send_l2(uint16_t idx)
byte_to_html(sfr_data[2]); char_to_html(':');
byte_to_html(sfr_data[3]);
// VLAN
slen += strtox(outbuf + slen, "\",\"vlan\":\"");
reg_read_m(RTL837x_L2_DATA_OUT_B);
charhex_to_html(sfr_data[0] & 0x0f);
byte_to_html(sfr_data[1]);
// type
reg_read_m(RTL837x_L2_DATA_OUT_C);
if (sfr_data[2] & 0x1)
@@ -396,32 +404,26 @@ void send_l2(uint16_t idx)
port |= (sfr_data[3] & 0x3) << 2;
itoa_html(port);
}
// Index
reg_read_m(RTL837x_TBL_DATA_0);
entry = (((uint16_t)sfr_data[2] & 0x0f) << 8) | sfr_data[3];
// Index
reg_read_m(RTL837x_TBL_DATA_0);
entry = (((uint16_t)sfr_data[2] & 0x0f) << 8) | sfr_data[3];
if (valid) {
slen += strtox(outbuf + slen, ",\"idx\":\"");
byte_to_html(entry >> 8);
byte_to_html(entry);
char_to_html('"');
char_to_html('}');
entry += 1; // We want the next entry following after the current entry
} else {
reg_read_m(RTL837x_TBL_DATA_0);
entry = (((uint16_t)sfr_data[2] & 0x0f) << 8) | sfr_data[3] + 1;
}
if (first_entry == 0xffff) {
char_to_html(',');
entry += 1; // We want the next entry following after the current entry
if (first_entry == 0xffff)
first_entry = entry;
} else {
if (first_entry == entry || !entries_left) {
char_to_html(']');
break;
} else {
char_to_html(',');
}
}
else if (first_entry == entry || !entries_left)
break;
}
char_to_html(']');
}