diff --git a/Makefile b/Makefile index d8a631f9..2c9a7c98 100644 --- a/Makefile +++ b/Makefile @@ -32,6 +32,17 @@ endif VERSION_EXTENSION = v$(VERSION)-$(GIT_VERSION) FILENAME_EXTENSION = $(VERSION_EXTENSION)-$(MACHINE) +# Deterministic build date: honor SOURCE_DATE_EPOCH, else the HEAD commit date, +# else wall-clock (no-git fallback). Keeps same-commit builds byte-identical +# (BUILD_DATE is baked into the image and covered by the trailing CRC). +SOURCE_DATE_EPOCH ?= $(shell git show -s --format=%ct HEAD 2>/dev/null) +ifeq ($(SOURCE_DATE_EPOCH),) +BUILD_DATE := $(shell date +"%Y-%m-%d %H:%M:%S") +else +BUILD_DATE := $(shell date -u -d @$(SOURCE_DATE_EPOCH) +"%Y-%m-%d %H:%M:%S" 2>/dev/null \ + || date -u -r $(SOURCE_DATE_EPOCH) +"%Y-%m-%d %H:%M:%S") +endif + all: create_build_dir $(VERSION_HEADER) $(SUBDIRS) $(BUILDDIR)/rtlplayground-$(FILENAME_EXTENSION).bin create_build_dir: @@ -87,18 +98,14 @@ html_min: $(HTML) mkdir -p $(HTML_MIN) @for f in $(HTML); do python3 tools/minify.py $$f $(HTML_MIN)/$$(basename $$f) || exit 1; done -html_data.c html_data.h: $(HTML) tools/output/fileadder html_min +html_data.c html_data.h &: $(HTML) | tools html_min tools/output/fileadder -a $(HTML_LOCATION) -s $(IMAGESIZE) -b BANK1 -z -d $(HTML_MIN) -p html_data -# httpd.c includes html_data.h; declare it explicitly so a parallel -# build (-j) does not compile httpd before the header is generated. -$(BUILDDIR)/httpd/httpd.rel: html_data.h $(VERSION_HEADER): - @echo "#ifndef VERSION_H" > $(VERSION_HEADER) - @echo "#define VERSION_H" >> $(VERSION_HEADER) - @echo "#define VERSION_SW \"$(VERSION_EXTENSION)\"" >> $(VERSION_HEADER) - @echo "#define BUILD_DATE \"$(shell date +"%Y-%m-%d %H:%M:%S")\"" >> $(VERSION_HEADER) - @echo "#endif" >> $(VERSION_HEADER) + @printf '%s\n' "#ifndef VERSION_H" "#define VERSION_H" \ + "#define VERSION_SW \"$(VERSION_EXTENSION)\"" \ + "#define BUILD_DATE \"$(BUILD_DATE)\"" \ + "#endif" > $(VERSION_HEADER) httpd: html_data.h @@ -113,20 +120,20 @@ distclean: -rm -f html_data.c html_data.h $(VERSION_HEADER) -rm -rf $(BUILDDIR) -$(BUILDDIR)/%.rel: %.c +$(BUILDDIR)/%.rel: %.c | create_build_dir html_data.h $(CC) -MMD $(CC_FLAGS) -o $@ -c $< -$(BUILDDIR)/%.rel: %.asm +$(BUILDDIR)/%.rel: %.asm | create_build_dir ${ASM} ${AFLAGS} -o $@ $< # mv -f $(addprefix $(basename $^), .lst .rel .sym) . -$(BUILDDIR)/rtlplayground.ihx: $(OBJS) $(BUILDDIR)/crtstart.rel $(BUILDDIR)/crc16.rel +$(BUILDDIR)/rtlplayground.ihx: $(OBJS) $(BUILDDIR)/crtbank.rel $(BUILDDIR)/crc16.rel $(CC) $(CC_FLAGS) -Wl-bHOME=0x00000 -Wl-bBANK1=0x14000 -Wl-bBANK2=0x24000 -Wl-r -o $@ $^ $(BUILDDIR)/rtlplayground.img: $(BUILDDIR)/rtlplayground.ihx objcopy --input-target=ihex -O binary $< $@ -$(BUILDDIR)/rtlplayground-$(FILENAME_EXTENSION).bin: $(BUILDDIR)/rtlplayground.img +$(BUILDDIR)/rtlplayground-$(FILENAME_EXTENSION).bin: $(BUILDDIR)/rtlplayground.img | tools if [ -e $@ ]; then rm $@; fi tools/output/imagebuilder -i $^ $@ tools/output/fileadder -a $(DEFAULT_CONFIG_LOCATION) -s $(IMAGESIZE) -d config.txt $@ @@ -135,7 +142,7 @@ $(BUILDDIR)/rtlplayground-$(FILENAME_EXTENSION).bin: $(BUILDDIR)/rtlplayground.i tools/output/crc_calculator -u $@ ln -sf $(MACHINE)/rtlplayground-$(FILENAME_EXTENSION).bin output/rtlplayground.bin -.PHONY: clean all $(SUBDIRS) $(VERSION_HEADER) +.PHONY: clean all $(SUBDIRS) $(VERSION_HEADER) create_build_dir .PHONY: machine_check: diff --git a/cmd_editor.c b/cmd_editor.c index 9a837e90..866cf403 100644 --- a/cmd_editor.c +++ b/cmd_editor.c @@ -40,21 +40,24 @@ void cmd_edit(void) __banked { while (l != sbuf_ptr) { if (sbuf[l] >= ' ' && sbuf[l] < 127) { // A printable character, copy to command line - if (cmd_line_len >= CMD_BUF_SIZE) - continue; - write_char(sbuf[l]); - // Shift buffer to right - for (uint8_t i = cmd_line_len; i > cursor; i--) - cmd_buffer[i] = cmd_buffer[i-1]; - // Insert char in comand buffer - cmd_buffer[cursor++] = sbuf[l]; - cmd_line_len++; - // Print rest of line - for (uint8_t i = cursor; i < cmd_line_len; i++) - write_char(cmd_buffer[i]); - // Move backwards - for (uint8_t i = cursor; i < cmd_line_len; i++) - write_char('\010'); // BS works like cursor-left + // Reserve one byte for the terminating NUL written on Enter. When the + // line is full, drop the character but still fall through to advance the + // serial-ring read pointer below; a 'continue' here would spin forever. + if (cmd_line_len < CMD_BUF_SIZE - 1) { + write_char(sbuf[l]); + // Shift buffer to right + for (uint8_t i = cmd_line_len; i > cursor; i--) + cmd_buffer[i] = cmd_buffer[i-1]; + // Insert char in comand buffer + cmd_buffer[cursor++] = sbuf[l]; + cmd_line_len++; + // Print rest of line + for (uint8_t i = cursor; i < cmd_line_len; i++) + write_char(cmd_buffer[i]); + // Move backwards + for (uint8_t i = cursor; i < cmd_line_len; i++) + write_char('\010'); // BS works like cursor-left + } } else if (sbuf[l] == '\033') { // ESC-Sequence // Wait until we have at least 3 characters including the ESC character in the serial buffer if (((sbuf_ptr + SBUF_SIZE - l) & SBUF_MASK) < 3) diff --git a/cmd_parser.c b/cmd_parser.c index 22a4c78a..996e743c 100644 --- a/cmd_parser.c +++ b/cmd_parser.c @@ -250,8 +250,7 @@ void parse_lag(void) print_string("LAG status:\n"); for (uint8_t i = 0; i < 4; i++) { write_char(' '); write_char('1' + i); - reg_read_m(RTL837X_TRK_MBR_CTRL_BASE + (i << 2)); - members = ((uint16_t)sfr_data[2]) << 8 | sfr_data[3]; + members = port_lag_members_get(i); if (!members) { print_string(" disabled\n"); continue; @@ -274,7 +273,9 @@ void parse_lag(void) if (cmd_words_len < 2 || !isnumber(cmd_buffer[cmd_words_b[1]])) goto err; - group = cmd_buffer[cmd_words_b[1]] - '0'; + group = cmd_buffer[cmd_words_b[1]] - '1'; + if (group > 3) /* '0' wraps well past three, so one test does both ends */ + goto err; uint8_t w = 2; while (w < cmd_words_len) { @@ -284,7 +285,9 @@ void parse_lag(void) port = cmd_buffer[cmd_words_b[w]] - '1'; if (isnumber(cmd_buffer[cmd_words_b[w] + 1])) port = (port + 1) * 10 + cmd_buffer[cmd_words_b[w] + 1] - '1'; - port = machine.phys_to_log_port[port]; + if (port > 8) /* phys_to_log_port holds nine entries */ + goto err; + port = machine.phys_to_log_port[port]; } else { goto err; } @@ -296,7 +299,7 @@ void parse_lag(void) port_lag_members_set(group, members); return; err: - print_string("Error: lag [port]...\n"); + print_string("Error: lag <1-4> [port]...\n"); } @@ -305,7 +308,11 @@ void parse_lag_hash(void) __xdata uint8_t group; __xdata uint8_t hash = 0; - group = cmd_buffer[cmd_words_b[1]] - '0'; + if (cmd_words_len < 2 || !isnumber(cmd_buffer[cmd_words_b[1]])) + goto err; + group = cmd_buffer[cmd_words_b[1]] - '1'; + if (group > 3) /* '0' wraps well past three, so one test does both ends */ + goto err; uint8_t w = 2; while (w < cmd_words_len) { @@ -331,6 +338,9 @@ void parse_lag_hash(void) w++; } port_lag_hash_set(group, hash); + return; +err: + print_string("Error: lag hash <1-4> [type]...\n"); } @@ -1530,10 +1540,12 @@ void cmd_parser(void) __banked } else if (cmd_compare(0, "igmp")) { if (cmd_compare(1, "on")) igmp_enable(); + else if (cmd_compare(1, "off")) + igmp_setup(); else if (cmd_compare(1, "show")) igmp_show(); else - igmp_setup(); // Reverts to default with IP-MC being flooded + print_string("Error: igmp on|off|show\n"); } else if (cmd_compare(0, "hostname")) { /* "hostname" alone reports the current name; "hostname " * sets it, sanitized to JSON-safe printable ASCII. A name with diff --git a/crtbank.asm b/crtbank.asm new file mode 100644 index 00000000..7a008687 --- /dev/null +++ b/crtbank.asm @@ -0,0 +1,29 @@ + .area HOME (CODE) + .area GSINIT0 (CODE) + .area GSINIT1 (CODE) + .area GSINIT2 (CODE) + .area GSINIT3 (CODE) + .area GSINIT4 (CODE) + .area GSINIT5 (CODE) + .area GSINIT (CODE) + .area GSFINAL (CODE) + .area CSEG (CODE) + + + .area HOME (CODE) + +__sdcc_banked_call:: + push _PSBANK + xch a,r0 + push a + mov a,r1 + push a + mov a,r2 + anl a,#0x1f + mov _PSBANK, a + xch a, r0 + ret + +__sdcc_banked_ret:: + pop _PSBANK + ret diff --git a/crtstart.asm b/crtstart.asm deleted file mode 100644 index 23146a75..00000000 --- a/crtstart.asm +++ /dev/null @@ -1,17 +0,0 @@ - .area GSFINAL (CODE) - -__sdcc_banked_call:: - push _PSBANK - xch a,r0 - push a - mov a,r1 - push a - mov a,r2 - anl a,#0x1f - mov _PSBANK, a - xch a, r0 - ret - -__sdcc_banked_ret:: - pop _PSBANK - ret diff --git a/dhcp.c b/dhcp.c index 18845015..ad5c5364 100644 --- a/dhcp.c +++ b/dhcp.c @@ -41,6 +41,7 @@ __xdata uip_ipaddr_t server; #define DHCP_REBIND_LEN 4 #define DHCP_CLIENT_ID 61 #define DHCP_CLIENT_ID_LEN 7 +#define DHCP_HOSTNAME 12 #define DHCP_REQUEST_IP 50 #define DHCP_REQUEST_IP_LEN 4 #define DHCP_PARAMS 55 @@ -116,6 +117,20 @@ void dhcp_addopt_client_id(void) } +void dhcp_addopt_hostname(void) +{ + uint8_t len = 0; + while (hostname[len]) + len++; + if (!len) + return; + DHCP_OPT[dhcp_state.opt_ptr++] = DHCP_HOSTNAME; + DHCP_OPT[dhcp_state.opt_ptr++] = len; + memcpy(&DHCP_OPT[dhcp_state.opt_ptr], hostname, len); + dhcp_state.opt_ptr += len; +} + + void dhcp_addopt_request_ip(void) { DHCP_OPT[dhcp_state.opt_ptr++] = DHCP_REQUEST_IP; @@ -152,6 +167,7 @@ void dhcp_send_discover(void) dhcp_addopt_client_id(); dhcp_addopt_request_ip(); + dhcp_addopt_hostname(); DHCP_OPT[dhcp_state.opt_ptr++] = DHCP_PARAMS; DHCP_OPT[dhcp_state.opt_ptr++] = 3; @@ -188,6 +204,7 @@ void dhcp_send_request(void) dhcp_addopt_client_id(); dhcp_addopt_request_ip(); dhcp_addopt_server_id(); + dhcp_addopt_hostname(); DHCP_OPT[dhcp_state.opt_ptr++] = DHCP_PARAMS; DHCP_OPT[dhcp_state.opt_ptr++] = 3; diff --git a/doc/link_aggregation.md b/doc/link_aggregation.md index b901d374..6bde787e 100644 --- a/doc/link_aggregation.md +++ b/doc/link_aggregation.md @@ -92,7 +92,7 @@ The following shows the network configuration On _both_ switches create a LAG with ports 1 and 2 inside and the default hash algorithm which takes source and destination ports into account, e.g. just use the default: ``` -> lag 0 1 2 +> lag 1 1 2 ``` diff --git a/doc/webui-compression.md b/doc/webui-compression.md index 39f2b1d1..775498b0 100644 --- a/doc/webui-compression.md +++ b/doc/webui-compression.md @@ -75,8 +75,10 @@ JSON polling of the visible section). - `fileadder` terminates the embedded files with a NUL directly after the content (previously at `data_read + 1`), so the `strlen()`-based size computation no longer depends on uninitialised buffer content. -- `version.h` is no longer `.PHONY`; every build previously rewrote it - while a parallel build (`make -j`) could read a half-written copy. +- `version.h` is now written by a single atomic `printf` (change from + upstream) instead of five separate `echo`s, so a parallel build + (`make -j`) can no longer read a half-written copy. It stays in + `.PHONY` so it is regenerated on every build. ## Verification diff --git a/html/main.js b/html/main.js index 7d14c621..78ba9c24 100644 --- a/html/main.js +++ b/html/main.js @@ -934,6 +934,10 @@ function setSectionInterval(fn, ms) { function clearSectionIntervals() { sectionIntervals.forEach(function(t) { clearInterval(t); }); sectionIntervals = []; + if (l2Timer) { + clearTimeout(l2Timer); + l2Timer = null; + } } function showSection(name) { @@ -1650,10 +1654,7 @@ sectionInits.stat = function() { }); }; -var l2GetInterval; -var l2Entries = []; -var l2CurrentEntry = 0; - +var l2Timer; function l2CMP(a, b) { @@ -1744,7 +1745,6 @@ function fillL2(s) s = uniq(s); l2All = s; renderL2(); - l2Entries = []; } function paintL2(tbl, s) @@ -1773,50 +1773,22 @@ function paintL2(tbl, s) } 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" ? t('l2_static') : t('l2_learned'); - e.port = e.port == 9 ? 'CPU' : 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; - } + walkL2(function(entries, ok) { + if (ok) { + for (var i = 0; i < entries.length; i++) + entries[i].type = entries[i].type == "s" ? t('l2_static') : t('l2_learned'); + fillL2(entries); } - }; - xhttp.open("GET", "/l2.json?idx=" + l2CurrentEntry, true); - xhttp.timeout = 1500; sendXHTTP(xhttp); + var sec = document.getElementById('page-l2'); + if (sec && sec.style.display === 'block') + l2Timer = setTimeout(getL2, 1000); + }); } sectionInits.l2 = function() { - l2Entries = []; - l2CurrentEntry = 0; update( () => { getL2(); setSectionInterval(update, 2000); - l2GetInterval = setSectionInterval(getL2, 1000); }); }; @@ -2497,3 +2469,66 @@ document.addEventListener("DOMContentLoaded", function () { }) .catch(error => console.error('Error fetching the data:', error)); }); + +function walkL2(onDone) +{ + var entries = []; + var idx = 0; + var tries = 0; + + function retry() { + if (++tries < 3) { + setTimeout(page, 1000); + return; + } + onDone(entries, false); + } + + function page() { + var xhttp = new XMLHttpRequest(); + xhttp.onreadystatechange = function() { + if (this.readyState != 4) + return; + if (this.status != 200) { + retry(); + return; + } + var s; + try { + s = JSON.parse(xhttp.responseText); + } catch (err) { + retry(); + return; + } + tries = 0; + s = s.map(function(e) { + e.vlan = parseInt(e.vlan, 16); + e.idx = parseInt(e.idx, 16); + e.port = e.port == 9 ? 'CPU' : logToPhysPort[e.port]; + return e; + }); + if (!s.length) { + onDone(entries, true); + return; + } + entries.push(...s); + for (var i = entries.length - 1; i > 0; i--) { + if (entries[0].idx == entries[i].idx) { + onDone(entries, true); + return; + } + } + if (entries.length >= 4096) { + onDone(entries, true); + return; + } + idx = s[s.length - 1].idx + 1; + setTimeout(page, 1000); + }; + xhttp.open("GET", "/l2.json?idx=" + idx, true); + xhttp.timeout = 1500; + sendXHTTP(xhttp); + } + + page(); +} diff --git a/httpd/httpd.c b/httpd/httpd.c index 622cacb2..5655426b 100644 --- a/httpd/httpd.c +++ b/httpd/httpd.c @@ -640,7 +640,7 @@ void httpd_appcall(void) p += 4; scan_header(p); __xdata uint8_t *q = p; - while (!is_separator(*p)) + while (*p && !is_separator(*p)) p++; *p = '\0'; dbg_string_x(q); @@ -664,7 +664,16 @@ void httpd_appcall(void) parse_short(q + 15); send_vlan(short_parsed); } else if (is_word(q, "/counters.json")) { - send_counters(q[20]-'0'); + /* The port is one raw character of the request line and + * indexes a nine entry table, so bound it here instead + * of trusting the client to have sent a digit. Anything + * below '0' wraps well past eight, so the one test + * covers both ends. */ + uint8_t cport = q[20] - '0'; + if (cport > 8) + send_bad_request(); + else + send_counters(cport); } else if (is_word(q, "/eee.json")) { send_eee(); } else if (is_word(q, "/bandwidth.json")) { diff --git a/httpd/page_impl.c b/httpd/page_impl.c index 44332e5f..bf3e2682 100644 --- a/httpd/page_impl.c +++ b/httpd/page_impl.c @@ -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 + __bit 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 + __bit 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(']'); } @@ -518,8 +520,7 @@ void send_lag(void) slen += strtox(outbuf + slen, "{\"lagNum\":"); itoa_html(l); slen += strtox(outbuf + slen, ",\"members\":\""); - reg_read_m(RTL837X_TRK_MBR_CTRL_BASE + (l << 2)); - uint16_t ports = ((uint16_t)sfr_data[2] << 8) | sfr_data[3]; + uint16_t ports = port_lag_members_get(l); for (uint8_t i = 0; i < 16; i++) { bool_to_html(!!(ports & 0x8000)); ports <<= 1; diff --git a/machine.c b/machine.c index 091097aa..c27b92a2 100644 --- a/machine.c +++ b/machine.c @@ -415,6 +415,7 @@ void machine_custom_init(void) { } __code const struct machine machine = { .machine_name = "SWTGW218AS 8+1 Managed Switch", .isRTL8373 = 1, + .mac_flash_offset = 0x1FC000, .min_port = 0, .max_port = 8, .n_sfp = 1, diff --git a/machine.h b/machine.h index c3368f6a..523beea3 100644 --- a/machine.h +++ b/machine.h @@ -85,6 +85,7 @@ typedef struct machine { uint32_t led_sets[4][4]; uint8_t led_mux_custom; uint8_t led_mux[28]; + uint32_t mac_flash_offset; }; typedef struct machine_runtime diff --git a/rtl837x_igmp.c b/rtl837x_igmp.c index e36dda62..feddf9ad 100644 --- a/rtl837x_igmp.c +++ b/rtl837x_igmp.c @@ -16,6 +16,7 @@ #include "machine.h" extern __code struct machine machine; +extern __xdata uint8_t igmpEnabled; #include "uip.h" @@ -85,6 +86,7 @@ void igmp_setup(void) __banked { uint8_t i; print_string("igmp_setup called\n"); + igmpEnabled = 0; // For now, forward all unkown IP-MC pkts (2 bits per port. 00: flood via floodmask, 01: drop, 10: trap, 11: to rport) REG_SET(RTL837X_IPV4_PORT_MC_LM_ACT, LOOKUP_MISS_FLOOD); REG_SET(RTL837X_IPV6_PORT_MC_LM_ACT, LOOKUP_MISS_FLOOD); @@ -96,11 +98,6 @@ void igmp_setup(void) __banked // Enable lookup of IPv4 MC addresses in table reg_bit_set(RTL837X_L2_CTRL, L2_CTRL_LUT_IPMC_HASH); - // Configure per-port IGMP configuration, bits 0-10 enable MC protocol snooping, - // bits 16-24 configure max MC group used by that port. For now all protocols are flooded (01) - for (i = machine.min_port; i <= machine.max_port; i++) - REG_SET(RTL837X_IGMP_PORT_CFG + (i << 2), 0x00ff7c15); - /* Configure per-port IGMP operations when protocol messages are received * bits 0-9 enable MC protocol snooping * bit 10: Enable dynamic router port learning @@ -135,6 +132,7 @@ void igmp_setup(void) __banked void igmp_enable(void) __banked { print_string("igmp_enable called\n"); + igmpEnabled = 1; // Configure trapping of unhandled IGMP protocol packets to CPU REG_SET(RTL837X_IGMP_TRAP_CFG, IGMP_CPU_PORT | IGMP_TRAP_PRIORITY); @@ -223,7 +221,7 @@ void igmp_packet_handler(void) __banked #endif #ifdef IPMC_USES_L3MC - memset(&entry, 0, sizeof(struct ipmc_table_entry)); + memset((__xdata uint8_t *)&entry, 0, sizeof(struct ipmc_table_entry)); // For IPv4 MC, the Source-IP is 0.0.0.0 entry.sip[0] = 0x00; entry.sip[1] = 0x00; entry.sip[2] = 0x00; entry.sip[3] = 0x00; // For IPv4 MC, the Destination-IP is the IPv4 MC address @@ -235,7 +233,7 @@ void igmp_packet_handler(void) __banked * yy = MC_IP[2] * zz = MC_IP[3] */ - memset(&entry, 0, sizeof(struct l2mc_table_entry)); + memset((__xdata uint8_t *)&entry, 0, sizeof(struct l2mc_table_entry)); entry.mac[0] = 0x01; entry.mac[1] = 0x00; entry.mac[2] = 0x5e; entry.mac[3] = IGMP_I->mc_ip[1] & 0x7f; entry.mac[4] = IGMP_I->mc_ip[2]; entry.mac[5] = IGMP_I->mc_ip[3]; entry.vlan = 1; //TODO: Get this out of the packet and compare with VLAN table! diff --git a/rtl837x_port.c b/rtl837x_port.c index d203e12d..a85e90df 100644 --- a/rtl837x_port.c +++ b/rtl837x_port.c @@ -733,6 +733,19 @@ void port_rldp_on(__xdata uint16_t p_ms) } +/* + * Reads the member port bitmask of a Link Aggregation Group. + * The groups have numbers 0-3; bit n is set when logical port n is a member. + * The bitmask reflects what the hardware holds, so it covers groups set up + * statically and groups a protocol brought up, without either having to say so. + */ +uint16_t port_lag_members_get(uint8_t lag) __banked +{ + reg_read(RTL837X_TRK_MBR_CTRL_BASE + (lag << 2)); + return ((uint16_t)SFR_DATA_8 << 8) | SFR_DATA_0; +} + + /* * Configure LAGs * Sets the members via port bitmask of a given Link Aggregation Group @@ -744,11 +757,14 @@ void port_lag_members_set(__xdata uint8_t lag, __xdata uint16_t members) __banke { print_string("port_lag_members_set, lag: "); print_byte(lag); print_string(", members: "); print_short(members); write_char('\n'); - if (lag > 3) - print_string("Link aggregation group must be 0-3!\n"); + if (lag > 3) { + print_string("Link aggregation group out of range\n"); + return; + } reg_read_m(RTL837X_TRK_HASH_CTRL_BASE + (lag << 2)); - if (!(sfr_data[0] | sfr_data [1] | sfr_data [2] | sfr_data [3])) - REG_SET(RTL837X_TRK_HASH_CTRL_BASE, LAG_HASH_DEFAULT); + if (!(sfr_data[0] | sfr_data[1] | sfr_data[2]) + && (sfr_data[3] == LAG_HASH_RESET || sfr_data[3] == 0)) + REG_SET(RTL837X_TRK_HASH_CTRL_BASE + (lag << 2), LAG_HASH_DEFAULT); REG_WRITE(RTL837X_TRK_MBR_CTRL_BASE + (lag << 2), 0, 0, members >> 8, members & 0xff); } @@ -761,8 +777,10 @@ void port_lag_hash_set(__xdata uint8_t lag, __xdata uint8_t hash_bits) __banked { print_string("port_lag_hash_set, lag: "); print_byte(lag); print_string(", hash: "); print_byte(hash_bits); write_char('\n'); - if (lag > 3) - print_string("Link aggregation group must be 0-3!\n"); + if (lag > 3) { + print_string("Link aggregation group out of range\n"); + return; + } REG_WRITE(RTL837X_TRK_HASH_CTRL_BASE + (lag << 2), 0, 0, 0, hash_bits); } diff --git a/rtl837x_port.h b/rtl837x_port.h index a5ce278c..b4598321 100644 --- a/rtl837x_port.h +++ b/rtl837x_port.h @@ -61,6 +61,7 @@ void port_mirror_set(register uint8_t port, __xdata uint16_t rx_pmask, __xdata u void port_mirror_del(void) __banked; bool port_ingress_filter(__xdata uint8_t port, __xdata vlan_ingress_mode_t type) __banked; void port_l2_setup(void) __banked; +uint16_t port_lag_members_get(uint8_t lag) __banked; void port_lag_members_set(__xdata uint8_t lag, __xdata uint16_t members) __banked; void port_lag_hash_set(__xdata uint8_t lag, __xdata uint8_t hash) __banked; void port_eee_enable_all(__xdata uint8_t speed) __banked; diff --git a/rtl837x_regs.h b/rtl837x_regs.h index f5bd9512..37a7e99d 100644 --- a/rtl837x_regs.h +++ b/rtl837x_regs.h @@ -232,6 +232,8 @@ #define LAG_HASH_L4_SPORT 0x20 #define LAG_HASH_L4_DPORT 0x40 #define LAG_HASH_DEFAULT (LAG_HASH_L2_SMAC | LAG_HASH_L2_DMAC | LAG_HASH_L3_SIP | LAG_HASH_L3_DIP | LAG_HASH_L4_SPORT | LAG_HASH_L4_DPORT) +#define LAG_HASH_RESET (LAG_HASH_SOURCE_PORT_NUMBER | LAG_HASH_L2_SMAC | LAG_HASH_L2_DMAC \ + | LAG_HASH_L3_SIP | LAG_HASH_L3_DIP | LAG_HASH_L4_SPORT) /* * Port isolation @@ -305,32 +307,40 @@ #ifdef REGDBG -#define REG_SET(r, v) SFR_DATA_24 = (((uint32_t)v) >> 24) & 0xff; \ +#define REG_SET(r, v) do { \ + SFR_DATA_24 = (((uint32_t)v) >> 24) & 0xff; \ SFR_DATA_16 = (((uint32_t)v) >> 16) & 0xff; \ SFR_DATA_8 = (((uint16_t)v) >> 8 & 0xff); \ SFR_DATA_0 = (v) & 0xff; \ reg_write(r); \ write_char('R'); print_byte(r >> 8); print_byte(r); write_char('-'); \ - print_byte(((v) >> 24) & 0xff); print_byte((v) >> 16 & 0xff); print_byte((v) >> 8 & 0xff); print_byte( (v) & 0xff); write_char(' '); + print_byte(((v) >> 24) & 0xff); print_byte((v) >> 16 & 0xff); print_byte((v) >> 8 & 0xff); print_byte( (v) & 0xff); write_char(' '); \ +} while (0) -#define REG_WRITE(r, v24, v16, v8, v0) SFR_DATA_24 = (v24); \ +#define REG_WRITE(r, v24, v16, v8, v0) do { \ + SFR_DATA_24 = (v24); \ SFR_DATA_16 = (v16); \ SFR_DATA_8 = (v8); \ SFR_DATA_0 = (v0); \ reg_write(r); \ - write_char('R'); print_byte(r>>8); print_byte(r); write_char('-'); print_byte(v24); print_byte(v16); print_byte(v8); print_byte(v0); write_char(' '); + write_char('R'); print_byte(r>>8); print_byte(r); write_char('-'); print_byte(v24); print_byte(v16); print_byte(v8); print_byte(v0); write_char(' '); \ +} while (0) #else -#define REG_SET(r, v) SFR_DATA_24 = (((uint32_t)v) >> 24) & 0xff; \ +#define REG_SET(r, v) do { \ + SFR_DATA_24 = (((uint32_t)v) >> 24) & 0xff; \ SFR_DATA_16 = (((uint32_t)v) >> 16) & 0xff; \ SFR_DATA_8 = (((uint16_t)v) >> 8 & 0xff); \ SFR_DATA_0 = (v) & 0xff; \ - reg_write(r); + reg_write(r); \ +} while (0) -#define REG_WRITE(r, v24, v16, v8, v0) SFR_DATA_24 = (v24); \ +#define REG_WRITE(r, v24, v16, v8, v0) do { \ + SFR_DATA_24 = (v24); \ SFR_DATA_16 = (v16); \ SFR_DATA_8 = (v8); \ SFR_DATA_0 = (v0); \ - reg_write(r); + reg_write(r); \ +} while (0) #endif #endif diff --git a/rtlplayground.c b/rtlplayground.c index ce739632..b9cc05d9 100644 --- a/rtlplayground.c +++ b/rtlplayground.c @@ -121,6 +121,7 @@ __xdata uint16_t management_vlan; __xdata uint8_t tx_seq; __xdata uint8_t stpEnabled; +__xdata uint8_t igmpEnabled; __xdata char hostname[24]; /* device hostname, default set at boot, see rtl837x_common.h */ __code uint16_t bit_mask[16] = { @@ -1152,7 +1153,7 @@ void handle_rx(void) print_string("STP TX\n"); tcpip_output(); } - } else if (uip_buf[0] == 0x01 && uip_buf[1] == 0x00 && uip_buf[2] == 0x5e // IPv4-MC packet? + } else if (igmpEnabled && uip_buf[0] == 0x01 && uip_buf[1] == 0x00 && uip_buf[2] == 0x5e // IPv4-MC packet? && uip_buf[3] == 0x00 && uip_buf[4] == 0x00 && uip_buf[5] == 0x16) { igmp_packet_handler(); if (uip_len) { @@ -2112,20 +2113,28 @@ void main(void) uip_ipaddr(&uip_hostaddr, ownIP[0], ownIP[1], ownIP[2], ownIP[3]); uip_ipaddr(&uip_draddr, gatewayIP[0], gatewayIP[1], gatewayIP[2], gatewayIP[3]); uip_ipaddr(&uip_netmask, netmask[0], netmask[1], netmask[2], netmask[3]); - reg_read_m(RTL837X_REG_CHIP_UUID); -#ifdef DEBUG - print_string("SoC UUID: "); print_sfr_data(); -#endif - uip_ethaddr.addr[0] = 0x06; // LAA prefix - uip_ethaddr.addr[3] = sfr_data[0] ^ sfr_data[3]; - uip_ethaddr.addr[4] = sfr_data[1] ^ sfr_data[3]; - uip_ethaddr.addr[5] = sfr_data[2] ^ sfr_data[3]; - reg_read_m(RTL837X_REG_CHIP_LOT_NO); -#ifdef DEBUG - print_string(", LOT: "); print_sfr_data(); write_char(' '); -#endif - uip_ethaddr.addr[1] = sfr_data[0] ^ sfr_data[2]; - uip_ethaddr.addr[2] = sfr_data[1] ^ sfr_data[3]; + uip_ethaddr.addr[0] = 0xff; + if (machine.mac_flash_offset) { + flash_region.addr = machine.mac_flash_offset; + flash_region.len = FLASH_BUF_SIZE; + flash_read_bulk(flash_buf); + // accept only a real unicast, globally-administered address (reject blank/LAA/multicast/all-zero OUI) + if (flash_buf[0] != 0xff && !(flash_buf[0] & 0x03) && (flash_buf[0] | flash_buf[1] | flash_buf[2])) { + uip_ethaddr.addr[0] = flash_buf[0]; uip_ethaddr.addr[1] = flash_buf[1]; + uip_ethaddr.addr[2] = flash_buf[2]; uip_ethaddr.addr[3] = flash_buf[3]; + uip_ethaddr.addr[4] = flash_buf[4]; uip_ethaddr.addr[5] = flash_buf[5]; + } + } + if (uip_ethaddr.addr[0] == 0xff) { // no valid flash MAC -> generate locally-administered + reg_read_m(RTL837X_REG_CHIP_UUID); + uip_ethaddr.addr[0] = 0x06; // LAA prefix + uip_ethaddr.addr[3] = sfr_data[0] ^ sfr_data[3]; + uip_ethaddr.addr[4] = sfr_data[1] ^ sfr_data[3]; + uip_ethaddr.addr[5] = sfr_data[2] ^ sfr_data[3]; + reg_read_m(RTL837X_REG_CHIP_LOT_NO); + uip_ethaddr.addr[1] = sfr_data[0] ^ sfr_data[2]; + uip_ethaddr.addr[2] = sfr_data[1] ^ sfr_data[3]; + } print_string("Setting MAC to: "); print_byte(uip_ethaddr.addr[0]); write_char(':'); print_byte(uip_ethaddr.addr[1]); write_char(':'); print_byte(uip_ethaddr.addr[2]); write_char(':'); print_byte(uip_ethaddr.addr[3]); write_char(':'); diff --git a/uip/uip-conf.h b/uip/uip-conf.h index 384f7fa5..2da25a3e 100644 --- a/uip/uip-conf.h +++ b/uip/uip-conf.h @@ -106,9 +106,23 @@ typedef unsigned short uip_stats_t; /** * uIP buffer size. * + * Sized to the largest frame the CPU port accepts on ingress: anything above + * that the NIC drops in hardware, so a larger buffer only costs XDATA. Measured + * with ICMP, which bypasses MSS and so probes the hardware directly: a 1502-byte + * payload is answered and 1503 is not, which puts the frame at 1556 bytes of + * uip_buf. The limit is the NIC's rather than a port's, so how the frame was + * tagged on the wire does not change it. + * * \hideinitializer */ -#define UIP_CONF_BUFFER_SIZE 2200 +#define UIP_CONF_BUFFER_SIZE 1556 + +/** + * Bytes of the buffer kept out of the advertised MSS. + * + * \hideinitializer + */ +#define UIP_CONF_BUFFER_EXTRA 30 /** * CPU byte order. diff --git a/uip/uipopt.h b/uip/uipopt.h index 16046cfe..731484c0 100644 --- a/uip/uipopt.h +++ b/uip/uipopt.h @@ -298,11 +298,8 @@ /** * The TCP maximum segment size. - * - * This is should not be to set to more than - * UIP_BUFSIZE - UIP_LLH_LEN - UIP_TCPIP_HLEN. */ -#define UIP_TCP_MSS (UIP_BUFSIZE - UIP_LLH_LEN - UIP_TCPIP_HLEN) +#define UIP_TCP_MSS (UIP_BUFSIZE - UIP_LLH_LEN - UIP_TCPIP_HLEN - UIP_BUFFER_EXTRA) /** * The size of the advertised receiver's window. @@ -381,6 +378,17 @@ #define UIP_BUFSIZE UIP_CONF_BUFFER_SIZE #endif /* UIP_CONF_BUFFER_SIZE */ +/** + * Bytes of uip_buf kept out of the advertised MSS. + * + * \hideinitializer + */ +#ifndef UIP_CONF_BUFFER_EXTRA +#define UIP_BUFFER_EXTRA 0 +#else /* UIP_CONF_BUFFER_EXTRA */ +#define UIP_BUFFER_EXTRA UIP_CONF_BUFFER_EXTRA +#endif /* UIP_CONF_BUFFER_EXTRA */ + extern __xdata uint8_t uip_buf[UIP_CONF_BUFFER_SIZE+2]; @@ -447,7 +455,7 @@ void uip_log(char *msg); #ifdef UIP_CONF_LLH_LEN #define UIP_LLH_LEN UIP_CONF_LLH_LEN #else /* UIP_CONF_LLH_LEN */ -#define UIP_LLH_LEN ETHER_HEADER_SIZE + RTL_FRAME_DESC_SIZE +#define UIP_LLH_LEN (ETHER_HEADER_SIZE + RTL_FRAME_DESC_SIZE) #endif /* UIP_CONF_LLH_LEN */ /** @} */