From 14bc823024bd92fd9f7acfead41aa542eef431ba Mon Sep 17 00:00:00 2001 From: logicog Date: Wed, 4 Mar 2026 20:19:46 +0100 Subject: [PATCH 1/6] Add reg_bit_test function --- rtl837x_common.h | 1 + rtlplayground.c | 17 +++++++++++++++++ 2 files changed, 18 insertions(+) diff --git a/rtl837x_common.h b/rtl837x_common.h index 8fe67643..c69d303f 100644 --- a/rtl837x_common.h +++ b/rtl837x_common.h @@ -122,6 +122,7 @@ void print_reg(uint16_t reg); uint8_t sfp_read_reg(uint8_t slot, uint8_t reg); void reg_bit_set(uint16_t reg_addr, char bit); void reg_bit_clear(uint16_t reg_addr, char bit); +uint8_t reg_bit_test(uint16_t reg_addr, char bit); void sfr_mask_data(uint8_t n, uint8_t mask, uint8_t set); void sfr_set_zero(void); void reset_chip(void); diff --git a/rtlplayground.c b/rtlplayground.c index 6a16e31c..041c7ed6 100644 --- a/rtlplayground.c +++ b/rtlplayground.c @@ -457,6 +457,23 @@ void reg_bit_clear(uint16_t reg_addr, char bit) reg_write_m(reg_addr); } + +/* + * This sets a bit in the 32bit wide switch register reg_addr + */ +uint8_t reg_bit_test(uint16_t reg_addr, char bit) +{ + uint8_t bit_mask = 1 << (bit & 0x7); + + bit >>= 3; + reg_read_m(reg_addr); + bit_mask = bit_mask; + if (sfr_data[3-bit] & bit_mask) + return 1; + return 0; +} + + /* * This masks the sfr data fields, first &-ing with ~mask, then setting the bits in set */ From 31a488e4e9ff44a5431cbe464506d56b7e4de486 Mon Sep 17 00:00:00 2001 From: logicog Date: Wed, 4 Mar 2026 20:20:37 +0100 Subject: [PATCH 2/6] Add web page for bandwidth control --- html/bandwidth.html | 20 +++++++ html/bandwidth.js | 136 ++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 156 insertions(+) create mode 100644 html/bandwidth.html create mode 100644 html/bandwidth.js diff --git a/html/bandwidth.html b/html/bandwidth.html new file mode 100644 index 00000000..68a6b754 --- /dev/null +++ b/html/bandwidth.html @@ -0,0 +1,20 @@ + + + + + Ingress and Egress Bandwidth + + + +
+
+

Ingress and Egress Bandwidth

+ + + +
Ingress Egress
Port Limit Bandwidth [kBit/s] Flow Control Limit Bandwidth [kBit/s] Apply
+ +
+ + + diff --git a/html/bandwidth.js b/html/bandwidth.js new file mode 100644 index 00000000..a29cc019 --- /dev/null +++ b/html/bandwidth.js @@ -0,0 +1,136 @@ +const iLayout = '" type="number" maxlength="10" size="10" onfocus="inputFocus('; +function createBW() { + var tbl = document.getElementById('bwtable'); + const limit = '' + if (tbl.rows.length <= 2 && numPorts) { + clearInterval(createBWInterval); + console.log("CREATING TABLE ", tbl.rows.length); + for (let i = 2; i < 2 + numPorts; i++) { + const tr = tbl.insertRow(); + let td = tr.insertCell(); td.appendChild(document.createTextNode(`Port ${i-1}`)); + td = tr.insertCell(); + td.innerHTML = limit.replaceAll("limit_port", "ilimit_port_" + i).replace("exec()", "iClicked(" + i + ")"); + td = tr.insertCell(); + td.innerHTML = 'UNLIMITED'; + td = tr.insertCell(); + td.innerHTML = limit.replaceAll("limit_port", "fc_port_" + i).replace("exec()", "document.getElementById('bwapply_" + i + "').disabled=false;"); + td = tr.insertCell(); + td.innerHTML = limit.replaceAll("limit_port", "elimit_port_" + i).replace("exec()", "eClicked(" + i + ")"); + td = tr.insertCell(); + td.innerHTML = 'UNLIMITED'; + var button = ''; + td = tr.insertCell(); + td.innerHTML = button; + document.getElementById("bwapply_" + i).disabled = true; + } + } +} + +function iClicked(i) +{ + document.getElementById("bwapply_" + i).disabled=false; + var tbl = document.getElementById('bwtable'); + var tr = tbl.rows[i]; + if (!document.getElementById("ilimit_port_" + i).checked) { + tr.cells[2].innerHTML = "UNLIMITED"; + document.getElementById("fc_port_" + i).disabled = true; + document.getElementById("fc_port_" + i).checked = true; + } else { + tr.cells[2].innerHTML = ''; + document.getElementById("fc_port_" + i).disabled = false; + document.getElementById("fc_port_" + i).checked = true; + } +} + +function eClicked(i) +{ + document.getElementById("bwapply_" + i).disabled=false; + var tbl = document.getElementById('bwtable'); + var tr = tbl.rows[i]; + if (!document.getElementById("elimit_port_" + i).checked) { + tr.cells[5].innerHTML = "UNLIMITED"; + } else { + tr.cells[5].innerHTML = ''; + } +} + +function inputFocus(i) +{ + document.getElementById("bwapply_" + i).disabled=false; +} + +async function doCMD(cmd) +{ + console.log("Sending >" + cmd + "<"); + try { + const response = await fetch('/cmd', { + method: 'POST', + body: cmd + }); + console.log('Completed!', response); + } catch(err) { + console.error(`Error: ${err}`); + } +} + +async function applyBandwidth(i) { + var tbl = document.getElementById('bwtable'); + var tr = tbl.rows[i]; + var cmd = "bw in " + (i-1) + " off"; + if (document.getElementById("ilimit_port_" + i).checked) + cmd = 'bw in ' + (i-1) + ' ' + parseInt(document.getElementById("ibw_" + i).value).toString(16).padStart(4, "0");; + doCMD(cmd); + if (document.getElementById("ilimit_port_" + i).checked) { + if (!document.getElementById("fc_port_" + i).checked) + cmd = "bw in " + (i-1) + " drop"; + doCMD(cmd); + } + var cmd = "bw out " + (i-1) + " off"; + if (document.getElementById("elimit_port_" + i).checked) + cmd = 'bw out ' + (i-1) + ' ' + parseInt(document.getElementById("ebw_" + i).value).toString(16).padStart(4, "0");; + doCMD(cmd); +} + +function getBW() { + var xhttp = new XMLHttpRequest(); + xhttp.onreadystatechange = function() { + if (this.readyState == 4 && this.status == 200) { + const s = JSON.parse(xhttp.responseText); + console.log("BW: ", JSON.stringify(s)); + var tbl = document.getElementById('bwtable'); + if (tbl.rows.length > 2 && numPorts) { + for (let i = 2; i < 2 + numPorts; i++) { + p = s[i-2]; + let n = p.portNum; + let tr = tbl.rows[n+1]; + if (!document.getElementById("bwapply_" + (n+1)).disabled) + continue; + console.log("Table Update row: " + i + " portNum is " + n + ", pState is " + pState[i-2] + ", row number is " + (n+1)); + let iBW = parseInt(p.iBW,16) * 16; let eBW = parseInt(p.eBW,16) * 16; + document.getElementById("ilimit_port_" + (n+1)).checked = p.iLimited; + document.getElementById("elimit_port_" + (n+1)).checked = p.eLimited; + if (!p.iLimited) { + tr.cells[2].innerHTML = "UNLIMITED"; + } else { + tr.cells[2].innerHTML = ''; + } + if (!p.eLimited) { + tr.cells[5].innerHTML = "UNLIMITED"; + } else { + tr.cells[5].innerHTML = ''; + } + document.getElementById("fc_port_" + (n+1)).checked = p.iFC==1?true:false; + document.getElementById("fc_port_" + (n+1)).disabled = p.iLimited==1?false:true; + } + } + } + }; + xhttp.open("GET", "/bandwidth.json", true); + xhttp.timeout = 1500; xhttp.send(); +} + +window.addEventListener("load", function() { + getBW(); + const iCount = setInterval(getBW, 2000); +}); +const createBWInterval = setInterval(createBW, 1010); From 20fb626cc1282f11ff3adb58edbebac5da84ee3a Mon Sep 17 00:00:00 2001 From: logicog Date: Wed, 4 Mar 2026 20:21:04 +0100 Subject: [PATCH 3/6] Make bandwidth control page accessible --- html/navigation.js | 1 + 1 file changed, 1 insertion(+) diff --git a/html/navigation.js b/html/navigation.js index d7f3b802..99a603e9 100644 --- a/html/navigation.js +++ b/html/navigation.js @@ -7,5 +7,6 @@ document.getElementById('sidebar').innerHTML = + "
  • Mirroring
  • " + "
  • Link Aggregation
  • " + "
  • EEE
  • " + + "
  • Bandwidth Limits
  • " + "
  • System Settings
  • " + "
  • Firmware Update
  • "; From 1374272db6b784873a799578024a2f10e913da34 Mon Sep 17 00:00:00 2001 From: logicog Date: Wed, 4 Mar 2026 20:21:28 +0100 Subject: [PATCH 4/6] Add simulation support for bandwidth control --- tools/httpd_sim.c | 62 ++++++++++++++++++++++++++++++++++++++++++++--- 1 file changed, 59 insertions(+), 3 deletions(-) diff --git a/tools/httpd_sim.c b/tools/httpd_sim.c index 21cdd199..cf809167 100644 --- a/tools/httpd_sim.c +++ b/tools/httpd_sim.c @@ -15,7 +15,7 @@ #define PASSWORD "1234" #define SESSION_TIMEOUT 2000 -#define PORTS 9 +#define PORTS 6 #define NSFP 1 #define N_COUNTERS 52 @@ -229,7 +229,7 @@ void send_status(int s) void send_counters(int s, int port) { - struct json_object *v, *counters; + struct json_object *counters; const char *jstring; char *header = "HTTP/1.1 200 OK\r\n" "Cache-Control: no-cache\r\n" @@ -239,7 +239,6 @@ void send_counters(int s, int port) counters = json_object_new_array_ext(N_COUNTERS); for (int i = 0; i < N_COUNTERS; i++) { - v = json_object_new_object(); sprintf(counter_buf, "0x%016lx", 0x1234UL + i); json_object_array_add(counters, json_object_new_string(counter_buf)); } @@ -283,6 +282,56 @@ void send_eee(int s) } +void send_bandwidth(int s) +{ + struct json_object *ports, *v; + const char *jstring; + uint32_t bw; + int limited, fcEnabled; + char *header = "HTTP/1.1 200 OK\r\n" + "Content-Type: application/json; charset=UTF-8\r\n\r\n"; + + ports = json_object_new_array_ext(PORTS); + for (int i = 1; i <= PORTS; i++) { + v = json_object_new_object(); + json_object_object_add(v, "portNum", json_object_new_int(i)); + if (i % 2) { + limited = 1; + fcEnabled = i % 4 ? 1 : 0; + bw = 0x100; + } else { + limited = 0; + fcEnabled = 0; + bw = 0xfffff; + } + char bw_buf[20]; + sprintf(bw_buf, "%08x", bw); + json_object_object_add(v, "iLimited", json_object_new_int(limited)); + json_object_object_add(v, "iFC", json_object_new_int(fcEnabled)); + json_object_object_add(v, "iBW", json_object_new_string(bw_buf)); + + if (i % 4) { + limited = 1; + bw = 0x1000; + } else { + limited = 0; + bw = 0xfffff; + } + sprintf(bw_buf, "%08x", bw); + json_object_object_add(v, "eLimited", json_object_new_int(limited)); + json_object_object_add(v, "eBW", json_object_new_string(bw_buf)); + + json_object_array_add(ports, v); + } + + write(s, header, strlen(header)); + + jstring = json_object_to_json_string_ext(ports, JSON_C_TO_STRING_PLAIN); + write(s, jstring, strlen(jstring)); + json_object_put(ports); +} + + void send_mirror(int s) { uint16_t mirror_tx, mirror_rx = 0; @@ -593,6 +642,13 @@ void launch(struct Server *server) else send_eee(new_socket); goto done; + } else if (!strncmp(&buffer[4], "/bandwidth.json", 15)) { + printf("Bandwidth request\n"); + if (!authenticated) + send_unauthorized(new_socket); + else + send_bandwidth(new_socket); + goto done; } else if (!strncmp(&buffer[4], "/information.json", 17)) { printf("Status request\n"); if (!authenticated) From 79499751916be69e8131a77bae2de909d70381d9 Mon Sep 17 00:00:00 2001 From: logicog Date: Wed, 4 Mar 2026 20:21:48 +0100 Subject: [PATCH 5/6] Add backend support for bandwidth control --- httpd/httpd.c | 2 ++ httpd/page_impl.c | 44 ++++++++++++++++++++++++++++++++++++++++++++ httpd/page_impl.h | 1 + 3 files changed, 47 insertions(+) diff --git a/httpd/httpd.c b/httpd/httpd.c index 28125245..6649b366 100644 --- a/httpd/httpd.c +++ b/httpd/httpd.c @@ -586,6 +586,8 @@ void httpd_appcall(void) send_counters(q[20]-'0'); } else if (is_word(q, "/eee.json")) { send_eee(); + } else if (is_word(q, "/bandwidth.json")) { + send_bandwidth(); } else if (is_word(q, "/l2.json")) { parse_short(q + 13); // e.g.: /l2.json?idx=10 send_l2(short_parsed); diff --git a/httpd/page_impl.c b/httpd/page_impl.c index 9769311a..82e5314b 100644 --- a/httpd/page_impl.c +++ b/httpd/page_impl.c @@ -546,6 +546,50 @@ void send_eee(void) } } + +void send_bandwidth(void) +{ + dbg_string("send_bandwidth called\n"); + slen = strtox(outbuf, HTTP_RESPONCE_JSON); + char_to_html('['); + for (uint8_t i = machine.min_port; i <= machine.max_port; i++) { + slen += strtox(outbuf + slen, "{\"portNum\":"); + itoa_html(machine.log_to_phys_port[i]); + slen += strtox(outbuf + slen, ",\"iLimited\":"); + reg_read_m(RTL837X_IGBW_PORT_CTRL + i * 4); + if (sfr_data[1] & 0x10) + char_to_html('1'); + else + char_to_html('0'); + slen += strtox(outbuf + slen, ",\"iBW\":\""); + byte_to_html(sfr_data[1] & 0x0f); + byte_to_html(sfr_data[2]); + byte_to_html(sfr_data[3]); + slen += strtox(outbuf + slen, "\",\"iFC\":"); + if (reg_bit_test(RTL837X_IGBW_PORT_FC_CTRL, i)) + char_to_html('1'); + else + char_to_html('0'); + reg_read_m(RTL837X_EGBW_PORT_CTRL + i * 1024); + slen += strtox(outbuf + slen, ",\"eLimited\":"); + if (sfr_data[1] & 0x10) + char_to_html('1'); + else + char_to_html('0'); + slen += strtox(outbuf + slen, ",\"eBW\":\""); + byte_to_html(sfr_data[1] & 0x0f); + byte_to_html(sfr_data[2]); + byte_to_html(sfr_data[3]); + char_to_html('"'); + char_to_html('}'); + if (i < machine.max_port) + char_to_html(','); + else + char_to_html(']'); + } +} + + void send_mtu(void) { dbg_string("send_mtu called\n"); diff --git a/httpd/page_impl.h b/httpd/page_impl.h index 00c178c5..5591e141 100644 --- a/httpd/page_impl.h +++ b/httpd/page_impl.h @@ -5,6 +5,7 @@ void send_counters(char port); void send_status(void); void send_vlan(uint16_t vlan); void send_basic_info(void); +void send_bandwidth(void); void send_eee(void); void send_l2(uint16_t idx); void l2_delete(uint16_t idx); From ed8dc0f43c6f37d62164a44e6573fc7a111a11b9 Mon Sep 17 00:00:00 2001 From: logicog Date: Mon, 9 Mar 2026 06:46:50 +0100 Subject: [PATCH 6/6] Fix bug in atoi_hex when uneven number of digits parsed We fix a bug in atoi_hex when parsing an unneven number of digits by rotating the entire number 4 bits right as would have been done if the number had been preceded by a 0 to make the number of digits even. --- cmd_parser.c | 10 +++++++++- 1 file changed, 9 insertions(+), 1 deletion(-) diff --git a/cmd_parser.c b/cmd_parser.c index e3595132..8b03c49e 100644 --- a/cmd_parser.c +++ b/cmd_parser.c @@ -150,6 +150,14 @@ uint8_t atoi_hex(uint8_t idx) h_idx++; } + if (h_idx & 1) { + hexvalue[h_idx >> 1] <<= 4; + hexvalue[3] = hexvalue[3] >> 4 | (hexvalue[2] << 4); + hexvalue[2] = hexvalue[2] >> 4 | (hexvalue[1] << 4); + hexvalue[1] = hexvalue[1] >> 4 | (hexvalue[0] << 4); + hexvalue[0] >>= 4; + } + return ((h_idx + 1) >> 1); } @@ -707,7 +715,7 @@ void parse_bw(void) goto err; port = cmd_buffer[cmd_words_b[2]] - '1'; - if (port < 0 || port > 9) + if (port > 9) goto err; port = machine.phys_to_log_port[port];