From e7df748b6aa33fcedad1c94d0666ca694d356d27 Mon Sep 17 00:00:00 2001 From: logicog Date: Mon, 12 Jan 2026 14:33:17 +0100 Subject: [PATCH 1/9] Fix error in calculating VLAN for L2 entries --- rtl837x_port.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/rtl837x_port.c b/rtl837x_port.c index 08f653c..ca83e92 100644 --- a/rtl837x_port.c +++ b/rtl837x_port.c @@ -287,7 +287,7 @@ void port_l2_learned(void) __banked // VLAN reg_read_m(RTL837x_L2_DATA_OUT_B); - print_short( ((uint16_t) (sfr_data[0] & 0x0f)) | sfr_data[1]); // VLAN + print_short( (((uint16_t) (sfr_data[0] & 0x0f)) << 8) | sfr_data[1]); // VLAN // type reg_read_m(RTL837x_L2_DATA_OUT_C); From e0c9338357ec116bb8a56ff96cebdbd0552052a8 Mon Sep 17 00:00:00 2001 From: logicog Date: Mon, 12 Jan 2026 14:33:54 +0100 Subject: [PATCH 2/9] Add support in web-interface for displaying L2 entries --- html/l2.html | 19 +++++++ html/l2.js | 143 +++++++++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 162 insertions(+) create mode 100644 html/l2.html create mode 100644 html/l2.js diff --git a/html/l2.html b/html/l2.html new file mode 100644 index 0000000..298c82a --- /dev/null +++ b/html/l2.html @@ -0,0 +1,19 @@ + + + + + FreeSwitchOS L2 Configuration + + + +
+
+

L2 Configuration

+ + + +
Port MAC VLAN Type Remove Entry
+
+ + + diff --git a/html/l2.js b/html/l2.js new file mode 100644 index 0000000..ec7ff3a --- /dev/null +++ b/html/l2.js @@ -0,0 +1,143 @@ +var l2GetInterval; +var l2Entries = []; +var l2CurrentEntry = 0; + +function fillStats() { + var tbl = document.getElementById('statstable'); + if (!numPorts) + return; + if (tbl.rows.length > 1) { + for (let i = 0; i < numPorts; i++) { + console.log("Table Update row: " + i + " state " + pState[i] + " is " + linkS[pState[i] +1]); + tbl.rows[i+1].cells[1].innerHTML = `${linkS[pState[i]+1]}`; + tbl.rows[i+1].cells[2].innerHTML = `${txG[i]} pkts`; + tbl.rows[i+1].cells[3].innerHTML = `${txB[i]} pkts`; + tbl.rows[i+1].cells[4].innerHTML = `${rxG[i]} pkts`; + tbl.rows[i+1].cells[5].innerHTML = `${rxB[i]} pkts`; + } + } else { + for (let i = 0; i < numPorts; i++) { + console.log("Table row: " + i); + const tr = tbl.insertRow(); + let td = tr.insertCell(); td.appendChild(document.createTextNode(`Port ${i+1}`)); + td = tr.insertCell(); td.appendChild(document.createTextNode(`${linkS[pState[i]+1]}`)); + td = tr.insertCell(); td.appendChild(document.createTextNode(`${txG[i]} pkts`)); + td = tr.insertCell();td.appendChild(document.createTextNode(`${txB[i]} pkts`)); + td = tr.insertCell();td.appendChild(document.createTextNode(`${rxG[i]} pkts`)); + td = tr.insertCell();td.appendChild(document.createTextNode(`${rxB[i]} pkts`)); + } + } +} + +function l2CMP(a, b) +{ + if (a.port < b.port) + return -1; + if (a.port > b.port) + return 1; + if (a.mac < b.mac) + return -1; + if (a.mac > b.mac) + return 1; + if (a.vlan < b.vlan) + return -1; + if (a.vlan > b.vlan) + return 1; + return 0; +} + +function uniq(a) { + return a.sort().filter(function(item, pos, ary) { + return !pos || item != ary[pos - 1]; + }); +} + +function delL2(idx) { + var xhttp = new XMLHttpRequest(); + xhttp.onreadystatechange = function() { + if (this.readyState == 4 && this.status == 200) { + console.log("Entry deleted"); + } + }; + xhttp.open("GET", "/l2_del.json?idx=" + idx, true); + xhttp.timeout = 1500; xhttp.send(); +} + +function fillL2(s) +{ + console.log("L2: ", JSON.stringify(s)); + var tbl = document.getElementById('l2table'); + if (!s.length) + return; + s.sort(l2CMP); + s = uniq(s); + var s = s.map(function(e) { e.port = e.port != 9 ? e.port : "CPU"; return e; }); + console.log("L2 now: ", JSON.stringify(s)); + for (let i = 0; i < s.length; i++) { + var e = s[i]; + console.log(i, e); + if (tbl.rows[i+1]) { + console.log(i, " Updating: port ", e.port, ", mac: ", e.mac); + tbl.rows[i+1].cells[0].innerHTML = `${e.port}`; + tbl.rows[i+1].cells[1].innerHTML = `${e.mac}`; + tbl.rows[i+1].cells[2].innerHTML = `${e.vlan}`; + tbl.rows[i+1].cells[4].innerHTML = ''; + } else { + console.log(i, " Inserting: port ", e.port, ", mac: ", e.mac); + const tr = tbl.insertRow(); + let td = tr.insertCell(); td.innerHTML = `${e.port}`; + td = tr.insertCell(); td.innerHTML = `${e.mac}`; + td = tr.insertCell(); td.innerHTML = `${e.vlan}`; + td = tr.insertCell(); td.innerHTML = `${e.type}`; + td = tr.insertCell(); td.innerHTML = ''; + } + } + console.log("s-length: ", s.length, " table-length: ", tbl.rows.length); + for (let i = tbl.rows.length - 1; i > s.length; i--) + tbl.deleteRow(i); + l2Entries = []; + console.log("L2 interval now ", l2GetInterval); +} + +function getL2() { + var xhttp = new XMLHttpRequest(); + xhttp.onreadystatechange = function() { + if (this.readyState == 4 && this.status == 200) { + var s = JSON.parse(xhttp.responseText); + var s = s.map(function(e) { + e.vlan = parseInt(e.vlan, 16); + e.idx = parseInt(e.idx, 16); + e.type = e.type == "s" ? "static" : "learned"; + e.port = e.port == 9 ? 9 : logToPhysPort[e.port]; + return e; + }); + l2Entries.push(...s); + if (l2Entries >= 4096) { + l2Entries = []; + l2CurrentEntry = 0; + clearInterval(l2GetInterval); + return; + } + var w = 0; + for (var i = l2Entries.length-1; i > 0; i--) { + if (l2Entries[0].idx == l2Entries[i].idx) { + w = 1; + break; + } + } + if (w) { + l2CurrentEntry = 0; + fillL2(l2Entries); + } else { + l2CurrentEntry = s[s.length-1].idx + 1; + } + } + }; + xhttp.open("GET", "/l2.json?idx=" + l2CurrentEntry, true); + xhttp.timeout = 1500; xhttp.send(); +} + +window.addEventListener("load", function() { + l2GetInterval = setInterval(getL2, 300); +}); + From e59158e006f6940e7f3a789688cbbc3a6d12d476 Mon Sep 17 00:00:00 2001 From: logicog Date: Mon, 12 Jan 2026 14:34:36 +0100 Subject: [PATCH 3/9] Add backend support for L2 entries in web --- httpd/httpd.c | 3 ++ httpd/page_impl.c | 96 +++++++++++++++++++++++++++++++++++++++++++++++ httpd/page_impl.h | 1 + 3 files changed, 100 insertions(+) diff --git a/httpd/httpd.c b/httpd/httpd.c index b50a3e7..bbb0a29 100644 --- a/httpd/httpd.c +++ b/httpd/httpd.c @@ -573,6 +573,9 @@ void httpd_appcall(void) send_counters(q[20]-'0'); } else if (is_word(q, "/eee.json")) { send_eee(); + } else if (is_word(q, "/l2.json")) { + parse_short(q + 13); // e.g.: /l2.json?idx=10 + send_l2(short_parsed); } else if (is_word(q, "/mirror.json")) { send_mirror(); } else if (is_word(q, "/mtu.json")) { diff --git a/httpd/page_impl.c b/httpd/page_impl.c index 871818d..8727d3a 100644 --- a/httpd/page_impl.c +++ b/httpd/page_impl.c @@ -16,6 +16,8 @@ // #define DEBUG #include "debug.h" +#define L2_MAX_TRANSFER 2 + #pragma codeseg BANK1 #pragma constseg BANK1 @@ -283,6 +285,100 @@ void send_counters(char port) } +void send_l2(uint16_t idx) +{ + slen = strtox(outbuf, HTTP_RESPONCE_JSON); + dbg_string("sending L2\n"); // TODO: Remove + print_short(idx); + __xdata uint8_t entries_left = L2_MAX_TRANSFER; + + do { + reg_read_m(RTL837X_TBL_CTRL); + } while (sfr_data[3] & 0x01); + + /* The L2 table in the ASIC can hold up to 4096 (0x1000) entries, which + * are accessed using an index. The index is the hash of the MAC address + * and forwarding ID (basically VID). The hash-table is 4-way associative, + * i.e. for a given hash value, 4 entries with that same hash can be stored + * (i.e. the hash points to a bucket with up to 4 entries). + * When the table or a hash bucket is full, further entries will lead to + * L2 flooding. + * To find all entries, we start with entry-index 0 and iteratively search for + * the next entry (with the next higher index), until we arrive again at the first + * entry. The indices are sorted, so if an entry has a smaller index than + * the previous one, we know that we have wrapped around the entire table. + */ + __xdata uint16_t entry = idx; + __xdata uint16_t first_entry = 0xffff; // An illegal entry index + char_to_html('['); + while (1) { + entries_left--; + uint8_t port = 0; + reg_read_m(RTL837x_TBL_DATA_0); + REG_WRITE(RTL837x_TBL_DATA_0, sfr_data[0], sfr_data[1],sfr_data[2] | 0xc0, sfr_data[3]); + + REG_WRITE(RTL837X_TBL_CTRL, entry >> 8, entry, TBL_L2_UNICAST, 0x1); + do { + reg_read_m(RTL837X_TBL_CTRL); + } while (sfr_data[3] & 0x1); + + reg_read_m(RTL837x_L2_DATA_OUT_B); + if ((sfr_data[0] & 0x20)) { // Check entry is valid + // 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; + reg_read_m(RTL837x_L2_DATA_OUT_A); + byte_to_html(sfr_data[0]); char_to_html(':'); + byte_to_html(sfr_data[1]); char_to_html(':'); + 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) + slen += strtox(outbuf + slen, "\",\"type\":\"s\", \"port\":"); + else + slen += strtox(outbuf + slen, "\",\"type\":\"l\", \"port\":"); + + 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]; + 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(','); + first_entry = entry; + } else { + if (first_entry == entry || !entries_left) { + char_to_html(']'); + break; + } else { + char_to_html(','); + } + } + } +} + + void send_mirror(void) { dbg_string("send_mirror called\n"); diff --git a/httpd/page_impl.h b/httpd/page_impl.h index 064a9df..a67d503 100644 --- a/httpd/page_impl.h +++ b/httpd/page_impl.h @@ -6,6 +6,7 @@ void send_status(void); void send_vlan(uint16_t vlan); void send_basic_info(void); void send_eee(void); +void send_l2(uint16_t idx); void send_mirror(void); void send_mtu(void); void send_config(void); From 88e706c2951985f1b6ca32efb7e4f858eab02f72 Mon Sep 17 00:00:00 2001 From: logicog Date: Mon, 12 Jan 2026 14:34:56 +0100 Subject: [PATCH 4/9] Add simulation support for L2 entries in web --- tools/httpd_sim.c | 79 +++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 79 insertions(+) diff --git a/tools/httpd_sim.c b/tools/httpd_sim.c index b2924dd..21cdd19 100644 --- a/tools/httpd_sim.c +++ b/tools/httpd_sim.c @@ -26,6 +26,31 @@ const uint8_t physToLogPort[] = { 0, 1, 2, 3, 4, 5, 6, 7, 8}; const uint8_t physToLogPort[] = { 4, 5, 6, 7, 3, 8}; #endif +struct l2_entry { + char mac[18]; + char vlan[4]; + char type; + uint8_t port; + uint16_t idx; +}; + +#define L2_ENTRY_NUM 12 +#define L2_MAX_TRANSFER 5 +const struct l2_entry l2_entries[12] = { + { "00:01:2f:00:00:01", "001", 'l', 7, 0x10 }, + { "00:06:78:00:00:02", "001", 'l', 7, 0x20 }, + { "00:01:2e:00:00:03", "001", 's', 7, 0x30 }, + { "60:7d:09:00:00:04", "001", 'l', 7, 0x40 }, + { "1c:2a:a3:00:00:05", "001", 'l', 7, 0x50 }, + { "1c:2a:a3:00:00:06", "001", 's', 7, 0x60 }, + { "02:e0:4c:00:00:07", "001", 'l', 7, 0x70 }, + { "00:e0:4c:00:00:08", "001", 'l', 4, 0x80 }, + { "1c:2a:a3:00:00:09", "001", 'l', 9, 0x90 }, // CPU-Port + { "3c:8c:f8:00:00:0a", "001", 'l', 7, 0xa0 }, + { "00:01:2e:00:00:0b", "001", 'l', 7, 0xb0 }, + { "00:01:2f:00:01:01", "001", 'l', 7, 0xc0 } +}; + time_t last_called; time_t last_session_use; @@ -33,6 +58,7 @@ uint64_t txG[PORTS], txB[PORTS], rxG[PORTS], rxB[PORTS]; char txG_buff[20], txB_buff[20], rxG_buff[20], rxB_buff[20], counter_buf[20], counter_name[20]; char sfp_temp[8], sfp_vcc[8], sfp_txbias[8], sfp_txpower[8], sfp_rxpower[8], sfp_laser[8], sfp_options[6]; char num_buff[20]; +char l2_idx_buff[20]; char upload_buffer[4194304]; // 4MB char *content_type = NULL; @@ -286,6 +312,51 @@ void send_mirror(int s) } +void send_l2(int s, int idx) +{ + struct json_object *entries, *v; + const char *jstring; + char *header = "HTTP/1.1 200 OK\r\n" + "Content-Type: application/json; charset=UTF-8\r\n\r\n"; + + entries = json_object_new_array(); + + // We find the next entry to transfer based on the index + printf("Starting with idx %04x\n", idx); + int j = 0; + while (j < L2_ENTRY_NUM && l2_entries[j].idx < idx) + j++; + + printf("First entry %d, idx: %04x\n", j, l2_entries[j].idx); + int transferred = 0; + while ((j < L2_ENTRY_NUM + 1) && (transferred < L2_MAX_TRANSFER)) { +// if (rand() / (RAND_MAX / 2) >= 1) +// continue; + int i = j % L2_ENTRY_NUM; + printf("Entry %d, idx: %04x, transferred %d\n", i, l2_entries[i].idx, transferred); + v = json_object_new_object(); + json_object_object_add(v, "mac", json_object_new_string(l2_entries[i].mac)); + json_object_object_add(v, "vlan", json_object_new_string(l2_entries[i].vlan)); + if (l2_entries[i].type == 'l') + json_object_object_add(v, "type", json_object_new_string("l")); + else + json_object_object_add(v, "type", json_object_new_string("s")); + json_object_object_add(v, "port", json_object_new_int(l2_entries[i].port)); + sprintf(l2_idx_buff, "%04x", l2_entries[i].idx); + json_object_object_add(v, "idx", json_object_new_string(l2_idx_buff)); + json_object_array_add(entries, v); + j++; + transferred++; + printf("j %d, transferred %d\n", j, transferred); + } + + write(s, header, strlen(header)); + jstring = json_object_to_json_string_ext(entries, JSON_C_TO_STRING_PLAIN); + write(s, jstring, strlen(jstring)); + json_object_put(entries); +} + + void send_lag(int s) { char lag_buf[20]; @@ -543,6 +614,14 @@ void launch(struct Server *server) else send_lag(new_socket); goto done; + } else if (!strncmp(&buffer[4], "/l2.json?idx=", 13)) { + int idx = atoi(&buffer[17]); + printf("L2 request\n"); + if (!authenticated) + send_unauthorized(new_socket); + else + send_l2(new_socket, idx); + goto done; } else if (!strncmp(&buffer[4], "/mtu.json", 9)) { printf("MTU request\n"); if (!authenticated) From 55d10ae3fcebac667b2d5d727d17401fd0bc8470 Mon Sep 17 00:00:00 2001 From: logicog Date: Mon, 12 Jan 2026 14:35:28 +0100 Subject: [PATCH 5/9] Make L2 information page available in navigation menu --- html/navigation.js | 1 + 1 file changed, 1 insertion(+) diff --git a/html/navigation.js b/html/navigation.js index 0444467..d7f3b80 100644 --- a/html/navigation.js +++ b/html/navigation.js @@ -3,6 +3,7 @@ document.getElementById('sidebar').innerHTML = + "
  • Port Configuration
  • " + "
  • Port Statistics
  • " + "
  • VLAN
  • " + + "
  • L2 Configuration
  • " + "
  • Mirroring
  • " + "
  • Link Aggregation
  • " + "
  • EEE
  • " From 3509f7da04454e132734a6bcf941f3ae765f74cd Mon Sep 17 00:00:00 2001 From: logicog Date: Fri, 16 Jan 2026 17:58:29 +0100 Subject: [PATCH 6/9] Add web functionality to delete L2 entries --- html/l2.js | 14 +++++--------- 1 file changed, 5 insertions(+), 9 deletions(-) diff --git a/html/l2.js b/html/l2.js index ec7ff3a..be75787 100644 --- a/html/l2.js +++ b/html/l2.js @@ -47,8 +47,8 @@ function l2CMP(a, b) } function uniq(a) { - return a.sort().filter(function(item, pos, ary) { - return !pos || item != ary[pos - 1]; + return a.filter(function(item, pos, ary) { + return !pos || item.idx != ary[pos - 1].idx; }); } @@ -56,7 +56,8 @@ function delL2(idx) { var xhttp = new XMLHttpRequest(); xhttp.onreadystatechange = function() { if (this.readyState == 4 && this.status == 200) { - console.log("Entry deleted"); + var s = JSON.parse(xhttp.responseText); + console.log("Entry deletion result: ", s.result); } }; xhttp.open("GET", "/l2_del.json?idx=" + idx, true); @@ -65,25 +66,22 @@ function delL2(idx) { function fillL2(s) { - console.log("L2: ", JSON.stringify(s)); var tbl = document.getElementById('l2table'); if (!s.length) return; s.sort(l2CMP); s = uniq(s); var s = s.map(function(e) { e.port = e.port != 9 ? e.port : "CPU"; return e; }); - console.log("L2 now: ", JSON.stringify(s)); + console.log("L2: ", JSON.stringify(s)); for (let i = 0; i < s.length; i++) { var e = s[i]; console.log(i, e); if (tbl.rows[i+1]) { - console.log(i, " Updating: port ", e.port, ", mac: ", e.mac); tbl.rows[i+1].cells[0].innerHTML = `${e.port}`; tbl.rows[i+1].cells[1].innerHTML = `${e.mac}`; tbl.rows[i+1].cells[2].innerHTML = `${e.vlan}`; tbl.rows[i+1].cells[4].innerHTML = ''; } else { - console.log(i, " Inserting: port ", e.port, ", mac: ", e.mac); const tr = tbl.insertRow(); let td = tr.insertCell(); td.innerHTML = `${e.port}`; td = tr.insertCell(); td.innerHTML = `${e.mac}`; @@ -92,11 +90,9 @@ function fillL2(s) td = tr.insertCell(); td.innerHTML = ''; } } - console.log("s-length: ", s.length, " table-length: ", tbl.rows.length); for (let i = tbl.rows.length - 1; i > s.length; i--) tbl.deleteRow(i); l2Entries = []; - console.log("L2 interval now ", l2GetInterval); } function getL2() { From e53f5e7134cbcfe0dc1c926fc90b9a527f8c91cb Mon Sep 17 00:00:00 2001 From: logicog Date: Fri, 16 Jan 2026 18:00:34 +0100 Subject: [PATCH 7/9] Add L2 entry deletion support via web --- html/l2.js | 2 +- httpd/httpd.c | 3 +++ httpd/page_impl.c | 55 ++++++++++++++++++++++++++++++++++++++++++++--- httpd/page_impl.h | 1 + 4 files changed, 57 insertions(+), 4 deletions(-) diff --git a/html/l2.js b/html/l2.js index be75787..b5af281 100644 --- a/html/l2.js +++ b/html/l2.js @@ -134,6 +134,6 @@ function getL2() { } window.addEventListener("load", function() { - l2GetInterval = setInterval(getL2, 300); + l2GetInterval = setInterval(getL2, 1000); }); diff --git a/httpd/httpd.c b/httpd/httpd.c index bbb0a29..8e6b28c 100644 --- a/httpd/httpd.c +++ b/httpd/httpd.c @@ -576,6 +576,9 @@ void httpd_appcall(void) } else if (is_word(q, "/l2.json")) { parse_short(q + 13); // e.g.: /l2.json?idx=10 send_l2(short_parsed); + } else if (is_word(q, "/l2_del.json")) { + parse_short(q + 17); + l2_delete(short_parsed); } else if (is_word(q, "/mirror.json")) { send_mirror(); } else if (is_word(q, "/mtu.json")) { diff --git a/httpd/page_impl.c b/httpd/page_impl.c index 8727d3a..dceff3c 100644 --- a/httpd/page_impl.c +++ b/httpd/page_impl.c @@ -16,7 +16,7 @@ // #define DEBUG #include "debug.h" -#define L2_MAX_TRANSFER 2 +#define L2_MAX_TRANSFER 30 #pragma codeseg BANK1 #pragma constseg BANK1 @@ -288,8 +288,8 @@ void send_counters(char port) void send_l2(uint16_t idx) { slen = strtox(outbuf, HTTP_RESPONCE_JSON); - dbg_string("sending L2\n"); // TODO: Remove - print_short(idx); + dbg_string("sending L2\n"); + dbg_short(idx); __xdata uint8_t entries_left = L2_MAX_TRANSFER; do { @@ -379,6 +379,55 @@ void send_l2(uint16_t idx) } +void l2_delete(uint16_t idx) +{ + slen = strtox(outbuf, HTTP_RESPONCE_JSON); + dbg_string("L2 DELETE\n"); + dbg_short(idx); + __xdata uint8_t entries_left = L2_MAX_TRANSFER; + + do { + reg_read_m(RTL837X_TBL_CTRL); + } while (sfr_data[3] & 0x01); + slen += strtox(outbuf + slen, "{\"result\":"); + // First, search for the entry based on the index + reg_read_m(RTL837x_TBL_DATA_0); + REG_WRITE(RTL837x_TBL_DATA_0, sfr_data[0], sfr_data[1],sfr_data[2] | 0xc0, sfr_data[3]); + + REG_WRITE(RTL837X_TBL_CTRL, idx >> 8, idx, TBL_L2_UNICAST, 0x1); + do { + reg_read_m(RTL837X_TBL_CTRL); + } while (sfr_data[3] & 0x1); + reg_read_m(RTL837x_L2_DATA_OUT_B); + if (!(sfr_data[0] & 0x20)) { + char_to_html('0'); + } else { + sfr_data[0] &= 0x3f; // Clear SPA + reg_write_m(RTL837x_TBL_DATA_IN_B); + + // Second half of MAC is copied + reg_read_m(RTL837x_L2_DATA_OUT_A); + reg_write_m(RTL837x_TBL_DATA_IN_A); + + reg_read_m(RTL837x_L2_DATA_OUT_C); + sfr_data[3] &= 0xc0; // Clear age, auth and second part of ports + sfr_data[1] &= 0xfe; // Clear nosalearn + reg_write_m(RTL837x_TBL_DATA_IN_C); + + reg_read_m(RTL837x_TBL_DATA_0); + REG_WRITE(RTL837x_TBL_DATA_0, sfr_data[0], sfr_data[1],TBL_L2_UNICAST, sfr_data[3]); + + REG_WRITE(RTL837X_TBL_CTRL, idx >> 8, idx, TBL_L2_UNICAST, TBL_WRITE | TBL_EXECUTE); + do { + reg_read_m(RTL837X_TBL_CTRL); + } while (sfr_data[3] & 0x1); + + char_to_html('1'); + } + char_to_html('}'); +} + + void send_mirror(void) { dbg_string("send_mirror called\n"); diff --git a/httpd/page_impl.h b/httpd/page_impl.h index a67d503..00c178c 100644 --- a/httpd/page_impl.h +++ b/httpd/page_impl.h @@ -7,6 +7,7 @@ void send_vlan(uint16_t vlan); void send_basic_info(void); void send_eee(void); void send_l2(uint16_t idx); +void l2_delete(uint16_t idx); void send_mirror(void); void send_mtu(void); void send_config(void); From 73a355cff5e9d1d057d42653b67b63957ad9cfb0 Mon Sep 17 00:00:00 2001 From: logicog Date: Sat, 17 Jan 2026 12:39:41 +0100 Subject: [PATCH 8/9] Add L2-table access method defintions --- httpd/page_impl.c | 26 +++++++++++++------------- rtl837x_regs.h | 9 +++++++++ 2 files changed, 22 insertions(+), 13 deletions(-) diff --git a/httpd/page_impl.c b/httpd/page_impl.c index dceff3c..2362a00 100644 --- a/httpd/page_impl.c +++ b/httpd/page_impl.c @@ -294,7 +294,7 @@ void send_l2(uint16_t idx) do { reg_read_m(RTL837X_TBL_CTRL); - } while (sfr_data[3] & 0x01); + } while (sfr_data[3] & TBL_EXECUTE); /* The L2 table in the ASIC can hold up to 4096 (0x1000) entries, which * are accessed using an index. The index is the hash of the MAC address @@ -308,19 +308,19 @@ void send_l2(uint16_t idx) * entry. The indices are sorted, so if an entry has a smaller index than * the previous one, we know that we have wrapped around the entire table. */ - __xdata uint16_t entry = idx; + __xdata uint16_t entry = idx & 0xfff; __xdata uint16_t first_entry = 0xffff; // An illegal entry index char_to_html('['); while (1) { entries_left--; uint8_t port = 0; reg_read_m(RTL837x_TBL_DATA_0); - REG_WRITE(RTL837x_TBL_DATA_0, sfr_data[0], sfr_data[1],sfr_data[2] | 0xc0, sfr_data[3]); + REG_WRITE(RTL837x_TBL_DATA_0, sfr_data[0], sfr_data[1] & 0xfc, sfr_data[2] | (TBL_LUTREAD_NEXT_L2UC << 6), sfr_data[3]); - REG_WRITE(RTL837X_TBL_CTRL, entry >> 8, entry, TBL_L2_UNICAST, 0x1); + REG_WRITE(RTL837X_TBL_CTRL, entry >> 8, entry, TBL_L2_UNICAST, TBL_EXECUTE); do { reg_read_m(RTL837X_TBL_CTRL); - } while (sfr_data[3] & 0x1); + } while (sfr_data[3] & TBL_EXECUTE); reg_read_m(RTL837x_L2_DATA_OUT_B); if ((sfr_data[0] & 0x20)) { // Check entry is valid @@ -336,7 +336,7 @@ void send_l2(uint16_t idx) byte_to_html(sfr_data[3]); // VLAN - slen += strtox(outbuf + slen, "\", \"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]); @@ -344,9 +344,9 @@ void send_l2(uint16_t idx) // type reg_read_m(RTL837x_L2_DATA_OUT_C); if (sfr_data[2] & 0x1) - slen += strtox(outbuf + slen, "\",\"type\":\"s\", \"port\":"); + slen += strtox(outbuf + slen, "\",\"type\":\"s\",\"port\":"); else - slen += strtox(outbuf + slen, "\",\"type\":\"l\", \"port\":"); + slen += strtox(outbuf + slen, "\",\"type\":\"l\",\"port\":"); port |= (sfr_data[3] & 0x3) << 2; itoa_html(port); @@ -388,13 +388,13 @@ void l2_delete(uint16_t idx) do { reg_read_m(RTL837X_TBL_CTRL); - } while (sfr_data[3] & 0x01); + } while (sfr_data[3] & TBL_EXECUTE); slen += strtox(outbuf + slen, "{\"result\":"); // First, search for the entry based on the index reg_read_m(RTL837x_TBL_DATA_0); - REG_WRITE(RTL837x_TBL_DATA_0, sfr_data[0], sfr_data[1],sfr_data[2] | 0xc0, sfr_data[3]); + REG_WRITE(RTL837x_TBL_DATA_0, sfr_data[0], sfr_data[1] & 0xfc, sfr_data[2] | (TBL_LUTREAD_NEXT_L2UC << 6), sfr_data[3]); - REG_WRITE(RTL837X_TBL_CTRL, idx >> 8, idx, TBL_L2_UNICAST, 0x1); + REG_WRITE(RTL837X_TBL_CTRL, (idx >> 8) & 0xf, idx, TBL_L2_UNICAST, TBL_EXECUTE); do { reg_read_m(RTL837X_TBL_CTRL); } while (sfr_data[3] & 0x1); @@ -415,12 +415,12 @@ void l2_delete(uint16_t idx) reg_write_m(RTL837x_TBL_DATA_IN_C); reg_read_m(RTL837x_TBL_DATA_0); - REG_WRITE(RTL837x_TBL_DATA_0, sfr_data[0], sfr_data[1],TBL_L2_UNICAST, sfr_data[3]); + REG_WRITE(RTL837x_TBL_DATA_0, sfr_data[0], sfr_data[1], TBL_L2_UNICAST, sfr_data[3]); REG_WRITE(RTL837X_TBL_CTRL, idx >> 8, idx, TBL_L2_UNICAST, TBL_WRITE | TBL_EXECUTE); do { reg_read_m(RTL837X_TBL_CTRL); - } while (sfr_data[3] & 0x1); + } while (sfr_data[3] & TBL_EXECUTE); char_to_html('1'); } diff --git a/rtl837x_regs.h b/rtl837x_regs.h index 6a43f95..1ebd6eb 100644 --- a/rtl837x_regs.h +++ b/rtl837x_regs.h @@ -127,6 +127,15 @@ // Table types #define TBL_L2_UNICAST 0x04 #define TBL_VLAN 0x03 +// Table read methods for the L2 table (TBL_L2_UNICAST): +#define TBL_LUTREAD_MAC 0 +#define TBL_LUTREAD_ADDRESS 1 +#define TBL_LUTREAD_NEXT_ADDRESS 2 +#define TBL_LUTREAD_NEXT_L2UC 3 +#define TBL_LUTREAD_NEXT_L2MC 4 +#define TBL_LUTREAD_NEXT_L3MC 5 +#define TBL_LUTREAD_NEXT_L2L3MC 6 +#define TBL_LUTREAD_NEXT_L2UCSPA 7 #define RTL837X_L2_CTRL 0x5350 #define L2_CTRL_LUT_IPMC_HASH 3 From 300f59955147f718177c5e9844813c269a185512 Mon Sep 17 00:00:00 2001 From: logicog Date: Sat, 17 Jan 2026 12:56:12 +0100 Subject: [PATCH 9/9] Fix output of l2 command --- rtl837x_port.c | 34 ++++++++++++++++------------------ 1 file changed, 16 insertions(+), 18 deletions(-) diff --git a/rtl837x_port.c b/rtl837x_port.c index ca83e92..3f48c77 100644 --- a/rtl837x_port.c +++ b/rtl837x_port.c @@ -263,14 +263,23 @@ void port_l2_learned(void) __banked __xdata uint16_t first_entry = 0xffff; // Table does not have that many entries while (1) { - uint8_t port = 0, other = 0; + uint8_t port = 0; reg_read_m(RTL837x_TBL_DATA_0); REG_WRITE(RTL837x_TBL_DATA_0, sfr_data[0], sfr_data[1],sfr_data[2] | 0xc0, sfr_data[3]); - REG_WRITE(RTL837X_TBL_CTRL, entry >> 8, entry, TBL_L2_UNICAST, 0x1); + REG_WRITE(RTL837X_TBL_CTRL, (entry >> 8) & 0xf, entry, TBL_L2_UNICAST, TBL_EXECUTE); do { reg_read_m(RTL837X_TBL_CTRL); - } while (sfr_data[3] & 0x1); + } while (sfr_data[3] & TBL_EXECUTE); + + reg_read_m(RTL837x_TBL_DATA_0); + entry = (((uint16_t)sfr_data[2] & 0x0f) << 8) | sfr_data[3]; + if (first_entry == 0xffff) { + first_entry = entry; + } else { + if (first_entry == entry) + break; + } // MAC reg_read_m(RTL837x_L2_DATA_OUT_B); @@ -278,7 +287,6 @@ void port_l2_learned(void) __banked print_byte(sfr_data[2]); write_char(':'); print_byte(sfr_data[3]); write_char(':'); port = (sfr_data[0] >> 6) & 0x3; - other = sfr_data[0]; reg_read_m(RTL837x_L2_DATA_OUT_A); print_byte(sfr_data[0]); write_char(':'); print_byte(sfr_data[1]); write_char(':'); @@ -298,22 +306,12 @@ void port_l2_learned(void) __banked port |= (sfr_data[3] & 0x3) << 2; if (port < 9) - write_char('1' + port); + write_char(machine.log_to_phys_port[port] + '0'); else - print_string("10"); + print_string("CPU"); } - reg_read_m(RTL837x_TBL_DATA_0); - entry = (((uint16_t)sfr_data[2] & 0x0f) << 8) | sfr_data[3] + 1; - if (first_entry == 0xffff) { - first_entry = entry; - } else { - if (first_entry == entry) - break; - } -#ifdef DEBUG - write_char(' '); print_sfr_data(); - write_char(' '); print_byte(other); -#endif + + entry++; print_string("\n"); } }