Compare commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
fe42b07e2b | ||
|
|
9984649177 | ||
|
|
7d47edb649 | ||
|
|
fdb19fe0ee | ||
|
|
b9b50f9a70 | ||
|
|
c31b67572b | ||
|
|
93003c8cb8 | ||
|
|
eaebeba528 | ||
|
|
b36fd95356 | ||
|
|
a7fc8303e9 | ||
|
|
a62866a93a | ||
|
|
42bbed9661 | ||
|
|
530e2683f0 | ||
|
|
9fedc6dd3d | ||
|
|
5e23d13771 | ||
|
|
09d5857fdd | ||
|
|
be309853c9 | ||
|
|
072edb36a8 | ||
|
|
10dd330e3c | ||
|
|
e2c4ed6be3 | ||
|
|
56ee56af2e | ||
|
|
8626b4c1d4 | ||
|
|
0cc09e4015 | ||
|
|
674dc1f35d | ||
|
|
7e6b443e07 | ||
|
|
73ec184c23 | ||
|
|
bf41fbd202 | ||
|
|
04ab442c6d | ||
|
|
c02623a610 | ||
|
|
c69bf4c5ec | ||
|
|
7f4f6dae03 | ||
|
|
4eeb12c020 | ||
|
|
7d81b15b6e | ||
|
|
d6253ddc25 | ||
|
|
8cac67cdf7 | ||
|
|
5620e950ce | ||
|
|
83491bc9e3 | ||
|
|
bece4a20a1 | ||
|
|
c39cc421a7 | ||
|
|
c1df7c23d1 | ||
|
|
13cc9db1ce | ||
|
|
974052877d | ||
|
|
ffde4eaf60 |
No files matched your search
@@ -7,3 +7,7 @@ version.h
|
||||
tools/httpd_sim
|
||||
tools/injector
|
||||
tools/fileadder
|
||||
|
||||
# host-test stand-ins for the two generated headers
|
||||
!test/stub/html_data.h
|
||||
!test/stub/version.h
|
||||
@@ -2,15 +2,15 @@
|
||||
A Playground for Firmware development for advanced user of RTL8372/RTL8373 based 2.5GBit Switches.
|
||||
|
||||
For each hardware configuration of these devices, there is usually a managed and an
|
||||
umanaged version sold, with mostly identical hardware. The aim is to provide management
|
||||
unmanaged version sold, with mostly identical hardware. The aim is to provide management
|
||||
features also for unmanaged devices with additional features such as Management VLAN,
|
||||
dhcp servers, multi-language support, IPv6 and TLS-encrypted web-pages. At present, however
|
||||
DHCP servers, multi-language support, IPv6 and TLS-encrypted web-pages. At present, however
|
||||
only the following features are provided:
|
||||
- A modern web-interface with mouse-over to display further information
|
||||
- A serial console interface to configure all features
|
||||
- IGMP to configure Multicast streaming
|
||||
- Port configuration showing detailed informtion about own and Link-partner advertised
|
||||
Speed settins and configuration of these settings on the local side
|
||||
- Port configuration showing detailed information about own and Link-partner advertised
|
||||
Speed settings and configuration of these settings on the local side
|
||||
- Per-port configuration of frame sizes (MTUs) for Jumbo-Frame support or limiting MTUs
|
||||
for particular devices
|
||||
- EEE (Energy Efficient Ethernet) can be configured per-port. Detailed information is
|
||||
@@ -25,7 +25,7 @@ only the following features are provided:
|
||||
- Firmware updates via the web
|
||||
- Installation as a firmware upgrade from the original web-interface
|
||||
|
||||
<img width="1420" height="623" alt="GUI" src="doc/images/gui.png" />
|
||||
<img width="1673" height="977" alt="GUI" src="doc/images/gui.png" />
|
||||
|
||||
While the firmware provides already considerable improvements over the original managed firmware,
|
||||
the firmware still lacks support for the proprietary loop prevention
|
||||
@@ -37,7 +37,7 @@ a backup of the original flash content via a SOIC clamp such as also used for BI
|
||||
backups and can re-install that firmware in case something is wrong. For this no soldering
|
||||
skills are necessary.
|
||||
|
||||
The firmware supports all hardware featues of devices with
|
||||
The firmware supports all hardware features of devices with
|
||||
- 4 2.5GBit ports + 2 SFP+ ports
|
||||
- 5 2.5GBIT + 1 SFP+ port
|
||||
- 8 2.5GBit + 1 SFP+ port
|
||||
@@ -131,7 +131,7 @@ rtlplayground-v0.1.0-12c98ba-dirty-LIANGUO_ZX_SWTGW215AS.bin
|
||||
|
||||
> [!CAUTION]
|
||||
> This image can be flashed directly to the chip OR through the firmware update/upgrade
|
||||
> interface of RTLPlaygound interface
|
||||
> interface of RTLPlayground interface
|
||||
|
||||
## (2) Compiling for OEM running device with management options (web upgrade)
|
||||
|
||||
@@ -179,7 +179,7 @@ ghidra see this information about [Ghidra images](doc/ghidra.md).
|
||||
|
||||
## (4) Installation through the Web interface (software way)
|
||||
|
||||
Managed switches (OEM firmware of RTLplaygroud firmware) can be upgraded via the web interface.
|
||||
Managed switches (OEM firmware of RTLplayground firmware) can be upgraded via the web interface.
|
||||
Unmanaged switch cannot be flashed this way (see 5).
|
||||
|
||||
Go to "Firmware update" tab, select the correct file.
|
||||
@@ -190,7 +190,7 @@ Go to "Firmware update" tab, select the correct file.
|
||||
|
||||
> [!CAUTION]
|
||||
> Check one more time that your device matches the machine type before flashing.
|
||||
> Be shure you have a backup of the original firmware before diving in RTLPlaygroung.
|
||||
> Be sure you have a backup of the original firmware before diving in RTLPlayground.
|
||||
|
||||
Finally, push the Upload File Button and you're done !
|
||||
|
||||
@@ -198,7 +198,7 @@ Finally, push the Upload File Button and you're done !
|
||||
## (5) Flashing the ROM directly (hardware way, but also only way to rescue)
|
||||
|
||||
This procedure is the only way to flash unmanaged switches, if the ROM chip is large enough.
|
||||
This is also the only way to unbrick your device if something went wroong.
|
||||
This is also the only way to unbrick your device if something went wrong.
|
||||
|
||||
> [!IMPORTANT]
|
||||
> You need a SOIC-8 clip to flash the ROM chip directly onboard.
|
||||
@@ -218,7 +218,7 @@ This is also the only way to unbrick your device if something went wroong.
|
||||
- ERASE THE ROM (BLANK) !
|
||||
- Load the firmware into IMSProg.
|
||||
- Flash is to the ROM chip.
|
||||
- Disconect the clip from the ROM chip.
|
||||
- Disconnect the clip from the ROM chip.
|
||||
- You're done, ready for the first boot.
|
||||
|
||||
## (6) Connecting a serial interface (optional)
|
||||
@@ -233,7 +233,7 @@ When you power up the switch, the device will perform some examples and provide
|
||||
## (8) The web-interface
|
||||
|
||||
The web-interface can be reached under the [default 192.168.10.247](http://192.168.10.247) unless you
|
||||
specified an IP adress in the config.txt before compilation.
|
||||
specified an IP address in the config.txt before compilation.
|
||||
|
||||
> [!TIP]
|
||||
> The default password is `1234`.
|
||||
@@ -333,13 +333,13 @@ While in compilation part, you might write directly to config.txt file before ma
|
||||
nano config.txt
|
||||
```
|
||||
|
||||
If you want to modify settings after the flash is done, go to the Advanced Settings tab in System Settings
|
||||
If you want to modify settings after the flash is done, go to the System tab and find the Startup Configuration.
|
||||
|
||||
<img width="1085" height="646" alt="ADVANCED SETTINGS" src="doc/images/advanced_settings.png" />
|
||||
<img width="1673" height="978" alt="ADVANCED SETTINGS" src="doc/images/advanced_settings.png" />
|
||||
|
||||
```
|
||||
ip xxx.xxx.xxx.xxx = IP adress of the switch
|
||||
gw yyy.yyy.yyy.yyy = IP adress of the gateway
|
||||
ip xxx.xxx.xxx.xxx = IP address of the switch
|
||||
gw yyy.yyy.yyy.yyy = IP address of the gateway
|
||||
netmask zzz.zzz.zzz.zzz = Network mask of the switch
|
||||
port x name xxx = Name xxx the port number x
|
||||
port z 1g = Set 1g speed for port z
|
||||
|
||||
@@ -426,6 +426,22 @@ err:
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void cmd_error(__code const char *msg)
|
||||
{
|
||||
err_status = ERR_INVALID_ARGUMENT;
|
||||
print_string("Error: ");
|
||||
print_string(msg);
|
||||
}
|
||||
|
||||
|
||||
static uint8_t name_char(uint8_t c)
|
||||
{
|
||||
if (c < 0x20 || c > 0x7e || c == '"' || c == '\\')
|
||||
return '.';
|
||||
return c;
|
||||
}
|
||||
|
||||
|
||||
// Sets the management MAC from a "mac <aa:bb:cc:dd:ee:ff>" command (or prints
|
||||
// the current one with a bare "mac"). Refuses blank/multicast/locally
|
||||
// administered/all-zero-OUI addresses so the running MAC stays reachable, and
|
||||
@@ -441,11 +457,11 @@ void parse_mac_cmd(void)
|
||||
return;
|
||||
}
|
||||
if (cmd_words_len != 2 || !parse_mac(cmd_words_b[1])) {
|
||||
print_string("Error: mac [<aa:bb:cc:dd:ee:ff>]\n");
|
||||
cmd_error("mac [<aa:bb:cc:dd:ee:ff>]\n");
|
||||
return;
|
||||
}
|
||||
if ((mac_parse_result[0] & 0x03) || ((mac_parse_result[0] | mac_parse_result[1] | mac_parse_result[2]) == 0)) {
|
||||
print_string("refusing: must be unicast, globally administered\n");
|
||||
cmd_error("the MAC must be unicast and globally administered\n");
|
||||
return;
|
||||
}
|
||||
if (memcmp(mac_parse_result, uip_ethaddr.addr, 6) == 0) {
|
||||
@@ -523,7 +539,7 @@ void parse_lag(void)
|
||||
port_lag_members_set(group, members);
|
||||
return;
|
||||
err:
|
||||
print_string("Error: lag (show | <1-4> (d | <port>...))\n");
|
||||
cmd_error("lag (show | <1-4> (d | <port>...))\n");
|
||||
}
|
||||
|
||||
|
||||
@@ -565,7 +581,7 @@ void parse_lag_hash(void)
|
||||
port_lag_hash_set(group, hash);
|
||||
return;
|
||||
err:
|
||||
print_string("Error: laghash <1-4> [smac|dmac|sip|dip|sport|dport]\n");
|
||||
cmd_error("laghash <1-4> [smac|dmac|sip|dip|sport|dport]\n");
|
||||
}
|
||||
|
||||
|
||||
@@ -619,7 +635,7 @@ void parse_vlan(void)
|
||||
*vlan_str++ = hex[(vlan_settings.vlan >> 4) & 0xf] ;
|
||||
*vlan_str++ = hex[vlan_settings.vlan & 0xf];
|
||||
while(!cmd_is_space_or_nul(pos)) {
|
||||
uint8_t c = cmd_buffer[pos++];
|
||||
uint8_t c = name_char(cmd_buffer[pos++]);
|
||||
write_char(c);
|
||||
*vlan_str++ = c;
|
||||
}
|
||||
@@ -663,7 +679,7 @@ void parse_vlan(void)
|
||||
}
|
||||
return;
|
||||
err:
|
||||
print_string("Error: vlan (<vlan-id>|show) [port][t]...\n");
|
||||
cmd_error("vlan (<vlan-id>|show) [port][t]...\n");
|
||||
}
|
||||
|
||||
|
||||
@@ -714,7 +730,7 @@ void parse_isolate(void)
|
||||
return;
|
||||
|
||||
err:
|
||||
print_string("Error: isolate <port> [show|off] [port]...\n");
|
||||
cmd_error("isolate <port> [show|off] [port]...\n");
|
||||
}
|
||||
|
||||
|
||||
@@ -751,7 +767,7 @@ void parse_ingress(void)
|
||||
// Setting mode for all ports at once
|
||||
for (log_port = machine.min_port; log_port <= machine.max_port; log_port++) {
|
||||
if (!port_ingress_filter(log_port, mode)) {
|
||||
print_string("Error setting ingress filter for port "); print_phys_port(log_port); write_char('\n');
|
||||
cmd_error("cannot set the ingress filter for port "); print_phys_port(log_port); write_char('\n');
|
||||
return;
|
||||
}
|
||||
print_string("All ports ingress filter set to: ");
|
||||
@@ -762,18 +778,18 @@ void parse_ingress(void)
|
||||
idx = cmd_words_b[w];
|
||||
uint8_t ret = cmd_parse_port(idx);
|
||||
if (ret != 1) {
|
||||
print_string("Invalid physical port number\n");
|
||||
cmd_error("Invalid physical port number\n");
|
||||
continue;
|
||||
}
|
||||
log_port = atoi_results_u8;
|
||||
idx += ret;
|
||||
|
||||
if (!vlan_ingress_mode_parse(cmd_buffer[idx++], &mode) || !cmd_is_space_or_nul(idx)) {
|
||||
print_string("Invalid ingress mode for port "); print_phys_port(log_port); print_string(" in ingress command\n");
|
||||
cmd_error("Invalid ingress mode for port "); print_phys_port(log_port); print_string(" in ingress command\n");
|
||||
goto err;
|
||||
}
|
||||
if (!port_ingress_filter(log_port, mode)) {
|
||||
print_string("Error setting ingress filter for port "); print_phys_port(log_port); write_char('\n');
|
||||
cmd_error("cannot set the ingress filter for port "); print_phys_port(log_port); write_char('\n');
|
||||
return;
|
||||
}
|
||||
print_string("Port "); print_phys_port(log_port);
|
||||
@@ -783,7 +799,7 @@ void parse_ingress(void)
|
||||
}
|
||||
return;
|
||||
err:
|
||||
print_string("Error: ingress [p]<u/t/a>...\n");
|
||||
cmd_error("ingress [p]<u/t/a>...\n");
|
||||
}
|
||||
|
||||
void parse_mirror(void)
|
||||
@@ -848,7 +864,7 @@ void parse_mirror(void)
|
||||
return;
|
||||
|
||||
err:
|
||||
print_string("Port/command missing: mirror [status/off/<mirroring port> [port][t/r]]...\n");
|
||||
cmd_error("Port/command missing: mirror [status/off/<mirroring port> [port][t/r]]...\n");
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -856,7 +872,7 @@ err:
|
||||
void parse_port(void)
|
||||
{
|
||||
if (cmd_words_len < 3) {
|
||||
print_string("\nUsage:" \
|
||||
cmd_error("\nUsage:" \
|
||||
"\nport <port> [show|on|off]" \
|
||||
"\nport <port> [10m|100m|1g|2g5|duplex] [half|full]" \
|
||||
"\nport <port> name [custom port name]\n");
|
||||
@@ -864,7 +880,7 @@ void parse_port(void)
|
||||
}
|
||||
|
||||
if (cmd_parse_port_separator(cmd_words_b[1]) == 0) {
|
||||
print_string("Invalid port number\n");
|
||||
cmd_error("Invalid port number\n");
|
||||
return;
|
||||
}
|
||||
phy_settings.port = atoi_results_u8;
|
||||
@@ -881,7 +897,7 @@ void parse_port(void)
|
||||
} else if (cmd_compare(2, "name")) {
|
||||
uint8_t i = 0;
|
||||
while ( (i < PORT_NAME_SIZE-1) && (cmd_buffer[cmd_words_b[3] + i] != NUL) ) {
|
||||
port_names[phy_settings.port][i] = cmd_buffer[cmd_words_b[3] + i];
|
||||
port_names[phy_settings.port][i] = name_char(cmd_buffer[cmd_words_b[3] + i]);
|
||||
i++;
|
||||
}
|
||||
port_names[phy_settings.port][i] = NUL;
|
||||
@@ -942,7 +958,7 @@ void parse_port(void)
|
||||
phy_settings.duplex = PHY_DUPLEX_HALF;
|
||||
phy_set_duplex();
|
||||
} else {
|
||||
print_string("Unknown port command\n");
|
||||
cmd_error("Unknown port command\n");
|
||||
}
|
||||
}
|
||||
|
||||
@@ -970,7 +986,7 @@ void parse_mtu(void)
|
||||
print_byte(p);
|
||||
|
||||
if (atoi_short(cmd_words_b[2]) == 0 || atoi_results_short < 64 || atoi_results_short > 0x3fff) {
|
||||
print_string("MTU must be 64..16383\n");
|
||||
cmd_error("MTU must be 64..16383\n");
|
||||
return;
|
||||
}
|
||||
REG_WRITE(RTL8373_REG_MAC_L2_PORT_MAX_LEN + ((uint16_t) p << 8), (atoi_results_short >> 10) & 0xf, (atoi_results_short >> 2) & 0xff,
|
||||
@@ -979,7 +995,7 @@ void parse_mtu(void)
|
||||
return;
|
||||
|
||||
err:
|
||||
print_string("mtu [port] [size]\n");
|
||||
cmd_error("mtu [port] [size]\n");
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -1036,11 +1052,11 @@ void parse_sfp(void)
|
||||
idx += ret;
|
||||
slot = atoi_results_u8 - 1;
|
||||
if (ret == 0 || !cmd_is_space(idx) || slot > 1) {
|
||||
print_string("Illegal SFP slot number\n");
|
||||
cmd_error("Illegal SFP slot number\n");
|
||||
return;
|
||||
}
|
||||
if (slot >= machine.n_sfp) {
|
||||
print_string("SFP slot not present\n");
|
||||
cmd_error("SFP slot not present\n");
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -1066,7 +1082,7 @@ void parse_sfp(void)
|
||||
handle_sfp();
|
||||
return;
|
||||
err:
|
||||
print_string("\nUsage:\n\tsfp\n\tsfp [1|2] [1g|2g5|10g]\n");
|
||||
cmd_error("\nUsage:\n\tsfp\n\tsfp [1|2] [1g|2g5|10g]\n");
|
||||
}
|
||||
|
||||
|
||||
@@ -1098,7 +1114,7 @@ void parse_regget(void)
|
||||
return;
|
||||
|
||||
err:
|
||||
print_string("usage: regget <hexvalue>\n\tlike: regget 0BB0 or regget 0c");
|
||||
cmd_error("regget <hexvalue>\n\tlike: regget 0BB0 or regget 0c");
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -1145,7 +1161,7 @@ void parse_regset(void)
|
||||
return;
|
||||
|
||||
err:
|
||||
print_string("usage: regset <hexvalue> <hexvalue>\n\tlike regset 0b abcd1234.");
|
||||
cmd_error("regset <hexvalue> <hexvalue>\n\tlike regset 0b abcd1234.");
|
||||
}
|
||||
|
||||
|
||||
@@ -1188,7 +1204,7 @@ void parse_sdsget(void)
|
||||
return;
|
||||
|
||||
err:
|
||||
print_string("usage: sdsget <sds-id> <hex:page> <hex:reg>\n");
|
||||
cmd_error("sdsget <sds-id> <hex:page> <hex:reg>\n");
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -1244,7 +1260,7 @@ void parse_sdsset(void)
|
||||
return;
|
||||
|
||||
err:
|
||||
print_string("usage: sdsset <sds-id> <hex:page> <hex:reg> <hex:val>\n");
|
||||
cmd_error("sdsset <sds-id> <hex:page> <hex:reg> <hex:val>\n");
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -1293,7 +1309,7 @@ void parse_phyget(void)
|
||||
return;
|
||||
|
||||
err:
|
||||
print_string("usage: phyget <phy-id> <dev-id> <hex:reg>\n");
|
||||
cmd_error("phyget <phy-id> <dev-id> <hex:reg>\n");
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -1353,7 +1369,7 @@ void parse_physet(void)
|
||||
return;
|
||||
|
||||
err:
|
||||
print_string("usage: physet <phy-id> <dev-id> <hex:reg> <hex:val>\n");
|
||||
cmd_error("physet <phy-id> <dev-id> <hex:reg> <hex:val>\n");
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -1388,7 +1404,7 @@ void parse_passwd(void)
|
||||
passwd[j] = NUL;
|
||||
return;
|
||||
}
|
||||
print_string("Missing password\n");
|
||||
cmd_error("Missing password\n");
|
||||
}
|
||||
|
||||
|
||||
@@ -1412,7 +1428,7 @@ void parse_eee(void)
|
||||
} else if (cmd_is_space_or_nul(idx)) {
|
||||
// Word 2 is a port number
|
||||
if (cmd_parse_port_separator(idx) == 0) {
|
||||
print_string("Speed word invalid, use: [100m|1g|2g5]\n");
|
||||
cmd_error("Speed word invalid, use: [100m|1g|2g5]\n");
|
||||
return;
|
||||
}
|
||||
port = atoi_results_u8;
|
||||
@@ -1431,7 +1447,7 @@ void parse_eee(void)
|
||||
speed = EEE_2G5;
|
||||
else
|
||||
{
|
||||
print_string("Speed word invalid, use: [100m|1g|2g5]\n");
|
||||
cmd_error("Speed word invalid, use: [100m|1g|2g5]\n");
|
||||
return;
|
||||
}
|
||||
}
|
||||
@@ -1451,7 +1467,7 @@ void parse_eee(void)
|
||||
else
|
||||
port_eee_status_all();
|
||||
} else {
|
||||
print_string("eee [on|off|status] [port] [100m|1g|2g5]\n");
|
||||
cmd_error("eee [on|off|status] [port] [100m|1g|2g5]\n");
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1522,7 +1538,7 @@ void parse_bw(void)
|
||||
return;
|
||||
|
||||
err:
|
||||
print_string("usage: bw [in|out|status] <port> [<hexvalue>|off|drop|fc]\n");
|
||||
cmd_error("bw [in|out|status] <port> [<hexvalue>|off|drop|fc]\n");
|
||||
}
|
||||
|
||||
void parse_syslog(void)
|
||||
@@ -1560,7 +1576,7 @@ void parse_syslog(void)
|
||||
if (was_enabled)
|
||||
syslog_start();
|
||||
} else {
|
||||
print_string("Invalid IP address\n");
|
||||
cmd_error("Invalid IP address\n");
|
||||
}
|
||||
} else if (cmd_compare(1, "port")) {
|
||||
if (cmd_words_len < 3) { // no additional argument -> print current port
|
||||
@@ -1571,7 +1587,7 @@ void parse_syslog(void)
|
||||
/* atoi_short() returns zero for no digits and for a value past
|
||||
* 65535; port zero is not a port either, so both tests are needed. */
|
||||
if (!atoi_short(cmd_words_b[2]) || !atoi_results_short) {
|
||||
print_string("Invalid port\n");
|
||||
cmd_error("Invalid port\n");
|
||||
return;
|
||||
}
|
||||
/* The port is frozen into the connection by uip_udp_new(), so
|
||||
@@ -1587,9 +1603,9 @@ void parse_syslog(void)
|
||||
}
|
||||
else
|
||||
{
|
||||
print_string("Error: syslog [on|off|ip [ip-address]|port [number]]\n");
|
||||
print_string(" on/off enables or disables syslog, ip sets the syslog server IP address,\n");
|
||||
print_string(" port sets the destination UDP port (default 514)\n");
|
||||
cmd_error("syslog [on|off|ip [ip-address]|port [number]]\n"
|
||||
" on/off enables or disables syslog, ip sets the syslog server IP address,\n"
|
||||
" port sets the destination UDP port (default 514)\n");
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1757,7 +1773,7 @@ void cmd_parser(void) __banked
|
||||
print_string("Setting ip: ");
|
||||
print_ip(ip); write_char('\n');
|
||||
} else {
|
||||
print_string("Invalid IP address\n" \
|
||||
cmd_error("Invalid IP address\n" \
|
||||
"Error: ip [<ip-address>|dhcp]\n" \
|
||||
" The dhcp option enables the dhcp client, calling ip without options prints the current IP\n" \
|
||||
" Calling with a valid IP address will stop any ongoing dhcp client and set the IP address\n");
|
||||
@@ -1773,7 +1789,7 @@ void cmd_parser(void) __banked
|
||||
print_string("Setting gw: ");
|
||||
print_ip(ip); write_char('\n');
|
||||
} else {
|
||||
print_string("Invalid IP address\n" \
|
||||
cmd_error("Invalid IP address\n" \
|
||||
"Error: gw <ip-address>\n");
|
||||
}
|
||||
}
|
||||
@@ -1787,7 +1803,7 @@ void cmd_parser(void) __banked
|
||||
print_string("Setting netmask: ");
|
||||
print_ip(ip); write_char('\n');
|
||||
} else {
|
||||
print_string("Invalid IP address\n");
|
||||
cmd_error("Invalid IP address\n");
|
||||
}
|
||||
}
|
||||
write_char('\n');
|
||||
@@ -1803,8 +1819,9 @@ void cmd_parser(void) __banked
|
||||
igmp_setup();
|
||||
else if (cmd_compare(1, "show"))
|
||||
igmp_show();
|
||||
else
|
||||
print_string("Error: igmp on|off|show\n");
|
||||
else {
|
||||
cmd_error("igmp on|off|show\n");
|
||||
}
|
||||
} else if (cmd_compare(0, "mac")) {
|
||||
parse_mac_cmd();
|
||||
} else if (cmd_compare(0, "hostname")) {
|
||||
@@ -1822,13 +1839,11 @@ void cmd_parser(void) __banked
|
||||
uint8_t c = *hp++;
|
||||
if (c == NUL || c == '\r' || c == '\n')
|
||||
break;
|
||||
if (c < 0x20 || c > 0x7e || c == '"' || c == '\\')
|
||||
c = '.';
|
||||
*dst++ = c;
|
||||
*dst++ = name_char(c);
|
||||
}
|
||||
*dst = NUL;
|
||||
} else {
|
||||
print_string("Error: hostname [name] - the name must not contain spaces\n");
|
||||
cmd_error("hostname [name] - the name must not contain spaces\n");
|
||||
}
|
||||
} else if (cmd_compare(0, "stp")) {
|
||||
stp_parse();
|
||||
@@ -1836,8 +1851,9 @@ void cmd_parser(void) __banked
|
||||
if (cmd_words_len == 3 && cmd_parse_port_separator(cmd_words_b[1]) != 0
|
||||
&& atoi_short(cmd_words_b[2]) && atoi_results_short && atoi_results_short <= 4094)
|
||||
port_pvid_set(atoi_results_u8, atoi_results_short);
|
||||
else
|
||||
print_string("Error: pvid <port> <1-4094>\n");
|
||||
else {
|
||||
cmd_error("pvid <port> <1-4094>\n");
|
||||
}
|
||||
} else if (cmd_compare(0, "vlan")) {
|
||||
parse_vlan();
|
||||
} else if (cmd_compare(0, "isolate")) {
|
||||
@@ -1897,11 +1913,11 @@ void cmd_parser(void) __banked
|
||||
parse_ingress();
|
||||
}
|
||||
else {
|
||||
print_string("Unknown command\n");
|
||||
cmd_error("Unknown command\n");
|
||||
}
|
||||
|
||||
|
||||
if (save_cmd && cmd_words_len) {
|
||||
if (save_cmd && cmd_words_len && err_status == ERR_OK) {
|
||||
// Find end of the cmd-buffer, looking for the NUL-byte.
|
||||
uint8_t i = cmd_words_b[cmd_words_len - 1];
|
||||
do {
|
||||
@@ -1995,6 +2011,7 @@ config_done:
|
||||
// If a command is too long or can't be tokenized, remaining commands are not executed
|
||||
// Returns the status via `err_status`-variable.
|
||||
void execute_commands(__xdata uint8_t *p) __banked {
|
||||
__xdata uint8_t failed = ERR_OK;
|
||||
err_status = ERR_OK;
|
||||
uint8_t cmd_idx = 0;
|
||||
while (1) {
|
||||
@@ -2005,9 +2022,13 @@ void execute_commands(__xdata uint8_t *p) __banked {
|
||||
if (err_status != ERR_OK)
|
||||
return;
|
||||
cmd_parser();
|
||||
if (failed == ERR_OK)
|
||||
failed = err_status;
|
||||
}
|
||||
if (*p == 0)
|
||||
if (*p == 0) {
|
||||
err_status = failed;
|
||||
return;
|
||||
}
|
||||
cmd_idx = 0;
|
||||
} else {
|
||||
if (cmd_idx < (CMD_BUF_SIZE - 1)) {
|
||||
|
||||
@@ -0,0 +1,53 @@
|
||||
# IG108
|
||||
|
||||
Following is documentation for unmanaged switch marked as `IG108`.
|
||||
|
||||
Original software is running UART on 9600 baud rate.
|
||||
|
||||
Using SPI clamp in-board is the only method for initial installation.
|
||||
|
||||
### Brands
|
||||
|
||||
| Brand | Type | Managed | PCB | Flash | Chip RTL |
|
||||
|---------|------------|---------|------------|-------------|---------------|
|
||||
| Goalake | IG108 | No | PB-2132 | cFeon Q32A | 8373N + 8224N |
|
||||
| Netcore | GS9 | No | PB-2132 | cFeon Q32A | 8373N + 8224N |
|
||||
|
||||
### What works
|
||||
|
||||
- All eight 2.5GBASE-T RJ45 ports at 10/100/1000/2500 Mbps
|
||||
- SFP port with 1G/2.5G/10G modules
|
||||
- LEDs
|
||||
|
||||
### PCB overview
|
||||
|
||||
**Board markings**
|
||||
|
||||
- Top silkscreen: PB-2132
|
||||
|
||||
Front Goalake:
|
||||
|
||||
<img src="photos/PB-2132/case.jpg" width="600" />
|
||||
|
||||
Label Goalake:
|
||||
|
||||
<img src="photos/PB-2132/label.jpg" width="600" />
|
||||
|
||||
Top side:
|
||||
|
||||
<img src="photos/PB-2132/PCB-top.jpg" width="600" />
|
||||
|
||||
Bottom:
|
||||
|
||||
<img src="photos/PB-2132/PCB-bottom.jpg" width="600" />
|
||||
|
||||
### Serial console
|
||||
|
||||
The PCB has unpopulated through-holes near the SFP Port, with description of each pin, works out of the box.
|
||||
|
||||
- **Settings**: 115200 baud / 8N1 / 3.3V TTL
|
||||
|
||||
### Power supply
|
||||
|
||||
Input power is delivered via barell plug, `12V 1A` adapter was provided.
|
||||
|
||||
|
After Width: | Height: | Size: 907 KiB |
|
After Width: | Height: | Size: 923 KiB |
|
After Width: | Height: | Size: 290 KiB |
|
After Width: | Height: | Size: 360 KiB |
|
Before Width: | Height: | Size: 72 KiB After Width: | Height: | Size: 94 KiB |
|
Before Width: | Height: | Size: 64 KiB After Width: | Height: | Size: 79 KiB |
|
Before Width: | Height: | Size: 83 KiB After Width: | Height: | Size: 120 KiB |
@@ -6,6 +6,7 @@ The following devices have been tested and are fully working:
|
||||
| Ampcom | WAM902-SWTG018AS| No | [SWTG018AS-A V2.0](devices/SWTG018AS_A_V2_0.md) | | 8 + 1 |
|
||||
| Davuaz | Da-K6501W | No | [PCB-K0501W-V2.0](devices/K0501W_V2_0.md) | | 5 + 1 |
|
||||
| FOXNEO | FNS-1200P | No | [PCB-K0402W-U13-V2.0](devices/FNS-1200P.md) | 2M | 4 + 2 |
|
||||
| Goalake | IG108 | No | [PB-2132](devices/PB-2132.md) | 4M | 8 + 1 |
|
||||
| Hisource | Hi-K0402WS | No | [PCB-K0402WS-V3.0](devices/PCB-K0402WS-V3.0.md) | | 4 + 2 |
|
||||
| Hisource | Hi-K0402WS | No | [PCB-K0402WS-V2.0](devices/PCB-K0402WS-V2.0.md) | 2M | 4 + 2 |
|
||||
| Hisource | Hi-K0801WS | No | [PCB-KO801W-V2.0](devices/HI-K0801WS.md) | | 8 + 1 |
|
||||
@@ -21,9 +22,11 @@ The following devices have been tested and are fully working:
|
||||
| keepLINK | KP-9000-9XHML-X | Yes | [2M-PCB23-V3.1](devices/2M-PCB23-V3_1.md) | 2M | 8 + 1 |
|
||||
| LIANGUO | SWTG024AS | No | [SWTG024AS-v2.0-17452](devices/SWTG024AS.md) | 0.5M | 4 + 2 |
|
||||
| Lianguo | ZX-SWTGW215AS | Yes | [PCB-SWTG115AS-V2.0 / PCB-SWTG115AS-V2.1](devices/SWTGW215AS.md) | 2M | 5 + 1 |
|
||||
| Lianguo | HYWS-SGT0108S | No | [2G5F_20G_V1.01](devices/HYWS-SGT0108S.md) | 0.5M | 8 + 1 |
|
||||
| Mokerlink| 2G040210GSM | Yes | [2M-PCB43-V1.1](devices/2M-PCB43-V1.1.md) | | 4 + 2 |
|
||||
| Mokerlink| ZX-SWTGW218AS | Yes | [SWTG118AS-V2.0-16029](devices/SWTGW218AS.md) | 2M | 8 + 1 |
|
||||
| MokerLink| POE-2G080110GS | No | [2M-PCB51-ML-V1.1](devices/2M-PCB51-ML-V1_1.md) | 4M | 8 + 1 |
|
||||
| Netcore | GS9 | No | [PB-2132](devices/PB-2132.md) | 4M | 8 + 1 |
|
||||
| Ruiying | RY-4GT-2SX | No | [FG-4GT-2SX_V2.0](devices/FG-4GT-2SX_V2.0.md) | 4M | 4 + 2 |
|
||||
| Ruiying | RY-8GT-1SX | No | [FG-8GT-1SX](devices/FG-8GT-1SX.md) | 1M | 8 + 1 |
|
||||
| SiscoLink | SL-G0402F | No | [PCB-K0402WS-V3.0](devices/PCB-K0402WS-V3.0.md) | | 4 + 2 |
|
||||
@@ -33,7 +36,6 @@ The following devices have been tested and are fully working:
|
||||
| Xikestore| SKS3200M-4GPY2XF| Yes | [SWTG024AS-v1.0](devices/SWTG024AS.md) | | 4 + 2 |
|
||||
| XikeStor | SKS3200-8E1X | Yes | [SWTG118AS-V2.1-17462](devices/SWTGW218AS.md) | 2M | 8 + 1 |
|
||||
| Ztyuav | Z-QWYT0402 | No | [PCB-K0402WS-V3.0](devices/PCB-K0402WS-V3.0.md) | | 4 + 2 |
|
||||
| Lianguo | HYWS-SGT0108S | No | [2G5F_20G_V1.01](devices/HYWS-SGT0108S.md) | 0.5M | 8 + 1 |
|
||||
|
||||
For KP-9000-6XH-X2 / KP-9000-6XHML-X2 / Mokerlink 2G040210GSM devices, select
|
||||
the machine target by PCB revision. The ML/non-ML or managed/unmanaged label
|
||||
|
||||
@@ -533,13 +533,18 @@ TABS.forEach(function(tb){
|
||||
});
|
||||
$("burger").addEventListener("click",function(){$("nav").classList.toggle("open")});
|
||||
|
||||
var IDLE_STOP_MS=600000,lastInput=Date.now();
|
||||
["mousemove","mousedown","keydown","touchstart","wheel"].forEach(function(ev){
|
||||
addEventListener(ev,function(){lastInput=Date.now()},{passive:true});
|
||||
});
|
||||
function Poller(fn,ms){this.fn=fn;this.ms=ms;this.on=false;this.t=null}
|
||||
Poller.prototype.start=function(){if(this.on)return;this.on=true;this.tick()};
|
||||
Poller.prototype.stop=function(){this.on=false;clearTimeout(this.t)};
|
||||
Poller.prototype.tick=function(){
|
||||
var self=this;
|
||||
if(!self.on)return;
|
||||
var run=document.hidden?Promise.resolve():Promise.resolve().then(self.fn).catch(function(){});
|
||||
var quiet=document.hidden||Date.now()-lastInput>IDLE_STOP_MS;
|
||||
var run=quiet?Promise.resolve():Promise.resolve().then(self.fn).catch(function(){});
|
||||
run.then(function(){ if(self.on)self.t=setTimeout(function(){self.tick()},self.ms); });
|
||||
};
|
||||
|
||||
@@ -723,6 +728,7 @@ tabHooks.dash={
|
||||
var SPEEDS=[["auto","c_auto"],["2g5","2.5G"],["1g","1G"],["100m full","100M full"],
|
||||
["100m half","100M half"],["10m full","10M full"],["10m half","10M half"]];
|
||||
var SFPRATES=[["auto","c_auto"],["10g","10G"],["2g5","2.5G"],["1g","1G"],["100m","100M"]];
|
||||
var ADVSEL={63:"auto",31:"auto",32:"2g5",16:"1g",8:"100m full",4:"100m half",2:"10m full",1:"10m half"};
|
||||
function speedLabel(s){
|
||||
if(s==="c_auto")return t("c_auto");
|
||||
return s.replace(" full"," "+t("c_full")).replace(" half"," "+t("c_half"));
|
||||
@@ -785,6 +791,8 @@ function portsStatus(){
|
||||
S.ports.forEach(function(p){
|
||||
var i=p.portNum-1,el=$("plink"+i);
|
||||
if(el)el.innerHTML=linkBadge(p);
|
||||
var sel=$("pspd"+i),v=p.isSFP?0:ADVSEL[parseInt(p.adv,2)];
|
||||
if(sel&&v&&document.activeElement!==sel)sel.value=v;
|
||||
});
|
||||
}
|
||||
function applyPort(i){
|
||||
|
||||
@@ -37,6 +37,7 @@ __xdata uint8_t outbuf[TCP_OUTBUF_SIZE];
|
||||
__xdata uint8_t entry;
|
||||
__xdata uint16_t slen;
|
||||
__xdata uint16_t o_idx;
|
||||
__xdata uint16_t sent_len;
|
||||
__xdata uint16_t len_left;
|
||||
__xdata uint16_t cont_len;
|
||||
__xdata uint32_t cont_addr;
|
||||
@@ -347,8 +348,9 @@ __xdata uint8_t *scan_header(__xdata uint8_t * __xdata p)
|
||||
if (is_word_x(session, session_id)) {
|
||||
authenticated = 1;
|
||||
last_session_use = now;
|
||||
} else
|
||||
} else {
|
||||
dbg_string("Invalid session cookie!\n");
|
||||
}
|
||||
}
|
||||
}
|
||||
return p;
|
||||
@@ -559,7 +561,7 @@ static void handle_config_fragment(__xdata uint8_t *p)
|
||||
__xdata uint16_t frag_len;
|
||||
uint8_t taken;
|
||||
|
||||
frag_len = uip_len - (p - uip_appdata);
|
||||
frag_len = uip_len - (p - (__xdata uint8_t *)uip_appdata);
|
||||
if (pre_acc + frag_len >= CONFIG_UPLOAD_BUF) {
|
||||
print_string("Configuration too large, aborting.\n");
|
||||
config_upload = 0;
|
||||
@@ -589,7 +591,7 @@ static void handle_firmware_fragment(__xdata uint8_t *p)
|
||||
__xdata struct httpd_state * __xdata s = &(uip_conn->appstate);
|
||||
__xdata uint16_t frag_len, payload_start;
|
||||
|
||||
frag_len = uip_len - (p - uip_appdata);
|
||||
frag_len = uip_len - (p - (__xdata uint8_t *)uip_appdata);
|
||||
if (pre_acc + frag_len >= CONFIG_UPLOAD_BUF) {
|
||||
print_string("Firmware upload header too large, aborting.\n");
|
||||
config_upload = 0;
|
||||
@@ -684,7 +686,7 @@ static uint8_t post_body_take(__xdata uint8_t *p)
|
||||
send_bad_request();
|
||||
return 0;
|
||||
}
|
||||
have = uip_len - (p - uip_appdata);
|
||||
have = uip_len - (p - (__xdata uint8_t *)uip_appdata);
|
||||
if (have >= content_length) {
|
||||
p[content_length] = NUL;
|
||||
return 1;
|
||||
@@ -826,6 +828,13 @@ void handle_post(void)
|
||||
}
|
||||
|
||||
|
||||
static void tx_send(uint16_t len)
|
||||
{
|
||||
sent_len = len;
|
||||
uip_send(outbuf + o_idx, len);
|
||||
}
|
||||
|
||||
|
||||
void httpd_appcall(void)
|
||||
{
|
||||
__xdata struct httpd_state * __xdata s = &(uip_conn->appstate);
|
||||
@@ -861,9 +870,9 @@ void httpd_appcall(void)
|
||||
}
|
||||
} else if (uip_acked() && s->tstate == TSTATE_TX) {
|
||||
dbg_string("ACK\n");
|
||||
if (slen > uip_mss()) {
|
||||
slen -= uip_mss();
|
||||
o_idx += uip_mss();
|
||||
if (slen > sent_len) {
|
||||
slen -= sent_len;
|
||||
o_idx += sent_len;
|
||||
} else {
|
||||
slen = 0;
|
||||
}
|
||||
@@ -872,11 +881,11 @@ void httpd_appcall(void)
|
||||
|
||||
if (slen > uip_mss()) {
|
||||
dbg_string("Sending A: "); dbg_short(slen); dbg_char('\n');
|
||||
uip_send(outbuf + o_idx, uip_mss());
|
||||
tx_send(uip_mss());
|
||||
s->tstate = TSTATE_TX;
|
||||
} else if (slen > 0) {
|
||||
dbg_string("Sending B: "); dbg_short(slen); dbg_char('\n');
|
||||
uip_send(outbuf + o_idx, slen);
|
||||
tx_send(slen);
|
||||
s->tstate = TSTATE_TX;
|
||||
} else if (cont_len) {
|
||||
dbg_string("CONT cont_len: "); dbg_short(cont_len);
|
||||
@@ -887,7 +896,7 @@ void httpd_appcall(void)
|
||||
flash_region.len = slen;
|
||||
flash_read_bulk(outbuf);
|
||||
o_idx = 0;
|
||||
uip_send(outbuf + o_idx, slen);
|
||||
tx_send(slen);
|
||||
cont_len -= slen;
|
||||
cont_addr += slen;
|
||||
s->tstate = TSTATE_TX;
|
||||
@@ -1045,24 +1054,20 @@ do_send:
|
||||
o_idx = 0;
|
||||
if (slen > uip_mss()) {
|
||||
dbg_string("Sending a: "); dbg_short(slen); dbg_char('\n');
|
||||
uip_send(outbuf + o_idx, uip_mss());
|
||||
tx_send(uip_mss());
|
||||
dbg_string("Sending a done\n");
|
||||
} else {
|
||||
dbg_string("Sending b: "); dbg_short(slen); dbg_char('\n');
|
||||
uip_send(outbuf + o_idx, slen);
|
||||
tx_send(slen);
|
||||
dbg_string("Sending b done\n");
|
||||
}
|
||||
s->tstate = TSTATE_TX;
|
||||
} else if (uip_rexmit()) { // Connection established, need to rexmit?
|
||||
dbg_string("RETRANSMIT requested\n");
|
||||
if (slen > uip_mss()) {
|
||||
dbg_string("Sending C: "); dbg_short(slen); dbg_char('\n');
|
||||
uip_send(outbuf + o_idx, uip_mss());
|
||||
if (sent_len) {
|
||||
dbg_string("Sending C: "); dbg_short(sent_len); dbg_char('\n');
|
||||
tx_send(sent_len);
|
||||
dbg_string("Sending C done\n");
|
||||
} else if (slen > 0) {
|
||||
dbg_string("Sending D: "); dbg_short(slen); dbg_char('\n');
|
||||
uip_send(outbuf + o_idx, slen);
|
||||
dbg_string("Sending D done\n");
|
||||
}
|
||||
s->tstate = TSTATE_TX;
|
||||
uip_len = 0;
|
||||
|
||||
@@ -70,6 +70,21 @@ void char_to_html(char c)
|
||||
}
|
||||
|
||||
|
||||
static void str_x_to_html(__xdata const char *s)
|
||||
{
|
||||
while (*s)
|
||||
outbuf[slen++] = *s++;
|
||||
}
|
||||
|
||||
|
||||
void json_char_to_html(uint8_t c)
|
||||
{
|
||||
if (c < 0x20 || c > 0x7e || c == '"' || c == '\\')
|
||||
c = '.';
|
||||
outbuf[slen++] = c;
|
||||
}
|
||||
|
||||
|
||||
// Convert uint8_t to ascii HEX char.
|
||||
void byte_to_html(uint8_t val)
|
||||
{
|
||||
@@ -175,6 +190,20 @@ void reg_to_html_long(uint16_t reg)
|
||||
}
|
||||
|
||||
|
||||
/* The 64-bit counter of the last STAT_GET as one number: the low word keeps
|
||||
* its leading zeros whenever the high word is set. */
|
||||
void counter_to_html(void)
|
||||
{
|
||||
reg_read_m(RTL837X_STAT_V_HIGH);
|
||||
if (SFR_DATA_24 | SFR_DATA_16 | SFR_DATA_8 | SFR_DATA_0) {
|
||||
sfr_data_to_html();
|
||||
reg_to_html_long(RTL837X_STAT_V_LOW);
|
||||
} else {
|
||||
reg_to_html(RTL837X_STAT_V_LOW);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
void send_sfp_info(uint8_t sfp)
|
||||
{
|
||||
// This loops over the Vendor-name, Vendor OUI, Vendor PN and Vendor rev ASCII fields
|
||||
@@ -184,8 +213,8 @@ void send_sfp_info(uint8_t sfp)
|
||||
if (i < 20 || i >= 60 || (i >= 36 && i < 40)) // Skip Non-ASCII codes
|
||||
continue;
|
||||
uint8_t c = sfp_buf[i & 0xf];
|
||||
if (c && c != 0xa0) // a0 is the byte read from a non-existant I2C EEPROM
|
||||
char_to_html(c);
|
||||
if (c)
|
||||
json_char_to_html(c);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -279,7 +308,7 @@ void send_vlan(uint16_t vlan)
|
||||
dbg_string("VLAN has no name\n");
|
||||
} else {
|
||||
while(vlan_names[n] && vlan_names[n] != ' ')
|
||||
char_to_html(vlan_names[n++]);
|
||||
json_char_to_html(vlan_names[n++]);
|
||||
}
|
||||
slen += strtox(outbuf + slen, "\",\"pvid\":\"0x");
|
||||
uint16_t pvid_mask = 0;
|
||||
@@ -311,8 +340,7 @@ bool send_counters(uint8_t phys_port)
|
||||
for (uint8_t counter = 0; counter < 0x37; counter++) {
|
||||
STAT_GET(counter, log_port);
|
||||
slen += strtox(outbuf + slen, "\"0x");
|
||||
reg_to_html(RTL837X_STAT_V_HIGH);
|
||||
reg_to_html_long(RTL837X_STAT_V_LOW);
|
||||
counter_to_html();
|
||||
char_to_html('\"');
|
||||
if (counter != 0x36)
|
||||
char_to_html(',');
|
||||
@@ -432,8 +460,6 @@ 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(RTL837X_TBL_CTRL);
|
||||
} while (SFR_DATA_0 & TBL_EXECUTE);
|
||||
@@ -814,7 +840,7 @@ void send_status(void)
|
||||
itoa_html(i);
|
||||
slen += strtox(outbuf + slen, ",\"name\":\"");
|
||||
for (uint8_t j = 0; j < PORT_NAME_SIZE && port_names[i][j]; j++) {
|
||||
char_to_html(port_names[i][j]);
|
||||
json_char_to_html(port_names[i][j]);
|
||||
}
|
||||
slen += strtox(outbuf + slen, "\"");
|
||||
|
||||
@@ -853,14 +879,11 @@ void send_status(void)
|
||||
sfp_send_data(sfp, 238, 1);
|
||||
}
|
||||
slen += strtox(outbuf + slen,"\",\"sfp_vendor\":\"");
|
||||
for (uint8_t s = 0; s < 16 && sfp_module_vendor[sfp][s]; s++)
|
||||
outbuf[slen++] = sfp_module_vendor[sfp][s];
|
||||
str_x_to_html(sfp_module_vendor[sfp]);
|
||||
slen += strtox(outbuf + slen,"\",\"sfp_model\":\"");
|
||||
for (uint8_t s = 0; s < 16 && sfp_module_model[sfp][s]; s++)
|
||||
outbuf[slen++] = sfp_module_model[sfp][s];
|
||||
str_x_to_html(sfp_module_model[sfp]);
|
||||
slen += strtox(outbuf + slen,"\",\"sfp_serial\":\"");
|
||||
for (uint8_t s = 0; s < 16 && sfp_module_serial[sfp][s]; s++)
|
||||
outbuf[slen++] = sfp_module_serial[sfp][s];
|
||||
str_x_to_html(sfp_module_serial[sfp]);
|
||||
slen += strtox(outbuf + slen,"\",\"sfp_los\":");
|
||||
if (machine.sfp_port[sfp].pin_los == GPIO_NA) {
|
||||
slen += strtox(outbuf + slen,"null");
|
||||
@@ -914,8 +937,7 @@ void send_status(void)
|
||||
|
||||
STAT_GET(STAT_COUNTER_TX_PKTS, i);
|
||||
slen += strtox(outbuf + slen, ",\"txG\":\"0x");
|
||||
reg_to_html(RTL837X_STAT_V_HIGH);
|
||||
reg_to_html(RTL837X_STAT_V_LOW);
|
||||
counter_to_html();
|
||||
|
||||
slen += strtox(outbuf + slen, "\",\"txB\":\"0x");
|
||||
STAT_GET(STAT_COUNTER_ERR_PKTS, i);
|
||||
@@ -923,8 +945,7 @@ void send_status(void)
|
||||
|
||||
slen += strtox(outbuf + slen, "\",\"rxG\":\"0x");
|
||||
STAT_GET(STAT_COUNTER_RX_PKTS, i);
|
||||
reg_to_html(RTL837X_STAT_V_HIGH);
|
||||
reg_to_html(RTL837X_STAT_V_LOW);
|
||||
counter_to_html();
|
||||
|
||||
slen += strtox(outbuf + slen, "\",\"rxB\":\"0x");
|
||||
STAT_GET(STAT_COUNTER_ERR_PKTS, i);
|
||||
@@ -1034,7 +1055,7 @@ void send_vlanlist(void)
|
||||
n = vlan_name(i);
|
||||
if (n != 0xffff) {
|
||||
while (vlan_names[n] && vlan_names[n] != ' ')
|
||||
char_to_html(vlan_names[n++]);
|
||||
json_char_to_html(vlan_names[n++]);
|
||||
}
|
||||
|
||||
slen += strtox(outbuf + slen, "\"}");
|
||||
|
||||
@@ -1226,7 +1226,89 @@ __code const struct machine machine = {
|
||||
0x1a, 0x19, 0x1d, 0x1e, 0x1c,
|
||||
0x1d, 0x20, 0x21 },
|
||||
};
|
||||
#elif defined MACHINE_PB_2132
|
||||
__code const struct machine machine = {
|
||||
.machine_name = "PB-2132",
|
||||
.isRTL8373 = 1,
|
||||
.min_port = 0,
|
||||
.max_port = 8,
|
||||
.n_sfp = 1,
|
||||
.log_to_phys_port = {1, 2, 3, 4, 5, 6, 7, 8, 9},
|
||||
.phys_to_log_port = {0, 1, 2, 3, 4, 5, 6, 7, 8},
|
||||
.is_sfp = {0, 0, 0, 0, 0, 0, 0, 0, 1},
|
||||
|
||||
.sfp_port[0].pin_detect = GPIO30_ACL_BIT3_EN,
|
||||
.sfp_port[0].pin_los = GPIO37,
|
||||
.sfp_port[0].pin_tx_disable = GPIO_NA,
|
||||
.sfp_port[0].sds = 1,
|
||||
.sfp_port[0].i2c = { .sda = GPIO39_I2C_SDA4, .scl = GPIO40_I2C_SCL3_MDC1 },
|
||||
|
||||
.reset_pin = GPIO_NA,
|
||||
|
||||
.high_leds = { .mux = LED_27 | LED_28_SYS | LED_29, .enable = LED_28_SYS | LED_29 },
|
||||
.port_led_set = { 0, 0, 0, 0, 0, 0, 0, 0, 1},
|
||||
|
||||
.led_sets = {
|
||||
{
|
||||
/* LED0 = RJ45: Amber LED: 2.5G */
|
||||
LEDS_2G5 | LEDS_LINK | LEDS_ACT,
|
||||
/* LED1 = RJ45: Green LED: 10M / 100M / 1000M */
|
||||
LEDS_1G | LEDS_100M | LEDS_10M | LEDS_LINK | LEDS_ACT,
|
||||
0,
|
||||
0
|
||||
},
|
||||
{
|
||||
/* SFP+ single Green LED. */
|
||||
LEDS_10G | LEDS_5G | LEDS_2G5 | LEDS_1G | LEDS_100M | LEDS_LINK | LEDS_ACT,
|
||||
0,
|
||||
0,
|
||||
0
|
||||
},
|
||||
},
|
||||
};
|
||||
|
||||
#elif defined MACHINE_HASIVO_S600W_4GT_2XGT_SE
|
||||
__code const struct machine machine = {
|
||||
.machine_name = "Hasivo S600W-4GT-2XGT-SE",
|
||||
.isRTL8373 = 0,
|
||||
.min_port = 3,
|
||||
.max_port = 8,
|
||||
.n_sfp = 0,
|
||||
.n_10g = 2,
|
||||
.log_to_phys_port = {0, 0, 0, 5, 1, 2, 3, 4, 6},
|
||||
.phys_to_log_port = {4, 5, 6, 7, 3, 8, 0, 0, 0},
|
||||
.is_sfp = {0, 0, 0, 0, 0, 0, 0, 0, 0},
|
||||
.reset_pin = GPIO54_ACL_BIT2_EN,
|
||||
.high_leds = { .mux = LED_28_SYS | LED_29, .enable = LED_28_SYS | LED_29 },
|
||||
.port_led_set = { 0, 0, 0, 1, 0, 0, 0, 0, 1 },
|
||||
/* Ports 1-4: Green: 2.5GBit, Orange: 10/100/1000MBit
|
||||
* Ports 5-6: Green: 10GBit, Orange: 5/2.5GBit, None: 10/100/1000MBit
|
||||
*/
|
||||
.led_sets = {
|
||||
{
|
||||
LEDS_2G5 | LEDS_1G | LEDS_100M | LEDS_10M | LEDS_LINK | LEDS_ACT, // Yellow
|
||||
LEDS_2G5 | LEDS_LINK, // Green
|
||||
LEDS_1G | LEDS_100M | LEDS_10M | LEDS_LINK, // Orange
|
||||
0, // Unused
|
||||
},
|
||||
{
|
||||
LEDS_10G | LEDS_5G | LEDS_2G5 | LEDS_1G | LEDS_100M | LEDS_10M | LEDS_LINK | LEDS_ACT, // Yellow
|
||||
LEDS_10G | LEDS_LINK, // Green
|
||||
LEDS_5G | LEDS_2G5 | LEDS_LINK, // Orange
|
||||
0, // Unused
|
||||
},
|
||||
},
|
||||
.led_mux_custom = 1,
|
||||
.led_mux = {
|
||||
0x3f, 0x3f, 0x3f, 0x3f, 0x3f,
|
||||
0x3f, 0x3f, 0x3f, 0x0f, 0x20,
|
||||
0x21, 0x22, 0x10, 0x11, 0x12,
|
||||
0x14, 0x15, 0x16, 0x18, 0x19,
|
||||
0x1a, 0x1c, 0x1d, 0x1e, 0x0c,
|
||||
0x0d, 0x0e, 0x23
|
||||
},
|
||||
|
||||
};
|
||||
#else
|
||||
#error "Please select a machine type in machine.h"
|
||||
#endif
|
||||
@@ -46,6 +46,8 @@
|
||||
// #define MACHINE_FG_8GT_1SX
|
||||
// #define MACHINE_LIANGUO_HYWS_SGT0108S
|
||||
// #define MACHINE_POE_2G080110GS
|
||||
// #define MACHINE_PB_2132
|
||||
// #define MACHINE_HASIVO_S600W_4GT_2XGT_SE
|
||||
|
||||
typedef struct {
|
||||
// GPIO pins for SDA/SCL
|
||||
|
||||
@@ -54,6 +54,7 @@ extern __xdata uint8_t sbuf[SBUF_SIZE];
|
||||
#define ERR_OK 0
|
||||
#define ERR_TOO_MANY_ARGUMENTS 1
|
||||
#define ERR_CMD_TOO_LONG 2
|
||||
#define ERR_INVALID_ARGUMENT 3
|
||||
|
||||
// For RX data, a propriatary RTL FRAME is inserted. Instead of 0x0800 for IPv4,
|
||||
// the RTL_FRAME_TAG_ID is used as part of an 8-byte tag. When VLAN is activated,
|
||||
@@ -134,9 +135,9 @@ extern __xdata uint8_t uip_buf[UIP_CONF_BUFFER_SIZE+2];
|
||||
extern __xdata struct uip_eth_addr uip_ethaddr;
|
||||
|
||||
// Headers for calls in the common code area (HOME/BANK0)
|
||||
void print_string_no_syslog(__code char *p);
|
||||
void print_string_newline_no_syslog(__code char *p);
|
||||
void print_string(__code char *p);
|
||||
void print_string_no_syslog(__code const char *p);
|
||||
void print_string_newline_no_syslog(__code const char *p);
|
||||
void print_string(__code const char *p);
|
||||
void print_string_x(__xdata char *p);
|
||||
void print_long(uint32_t a);
|
||||
void print_short(uint16_t a);
|
||||
|
||||
@@ -102,9 +102,9 @@ static void port_pvid_write(uint8_t port, __xdata uint16_t pvid)
|
||||
|
||||
reg_read_m(reg);
|
||||
if (port & 0x1) {
|
||||
REG_WRITE(reg, sfr_data[0], pvid >> 4, sfr_data[2] & 0x0f | (pvid << 4), sfr_data[3]);
|
||||
REG_WRITE(reg, sfr_data[0], pvid >> 4, (sfr_data[2] & 0x0f) | (pvid << 4), sfr_data[3]);
|
||||
} else {
|
||||
REG_WRITE(reg, sfr_data[0], sfr_data[1], sfr_data[2] & 0xf0 | (pvid >> 8), pvid);
|
||||
REG_WRITE(reg, sfr_data[0], sfr_data[1], (sfr_data[2] & 0xf0) | (pvid >> 8), pvid);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -285,7 +285,7 @@ void vlan_setup(void) __banked
|
||||
#endif
|
||||
reg_read_m(reg);
|
||||
if (i & 0x1) {
|
||||
REG_WRITE(reg, sfr_data[0], 0, sfr_data[2] & 0x0f | 0x10, sfr_data[3]);
|
||||
REG_WRITE(reg, sfr_data[0], 0, (sfr_data[2] & 0x0f) | 0x10, sfr_data[3]);
|
||||
} else {
|
||||
REG_WRITE(reg, sfr_data[0], sfr_data[1], sfr_data[2] & 0xf0, 0x01);
|
||||
}
|
||||
@@ -758,7 +758,7 @@ void port_eee_enable_all(__xdata uint8_t speed) __banked
|
||||
port_eee_enable(i, speed);
|
||||
} else {
|
||||
if (speed & EEE_10G)
|
||||
port_eee_enable(i, speed & EEE_NORESET | EEE_2G5);
|
||||
port_eee_enable(i, (speed & EEE_NORESET) | EEE_2G5);
|
||||
else
|
||||
port_eee_enable(i, speed);
|
||||
}
|
||||
@@ -899,7 +899,7 @@ void vlan_dump(void) __banked
|
||||
/** Set the ingress VLAN filtering */
|
||||
bool port_ingress_vlan_filter_set(uint8_t port, __xdata bool enabled) __banked
|
||||
{
|
||||
if (port < machine.min_port || port > machine.max_port && port != CPU_PORT) {
|
||||
if (port < machine.min_port || (port > machine.max_port && port != CPU_PORT)) {
|
||||
return false;
|
||||
}
|
||||
if (enabled)
|
||||
@@ -913,7 +913,7 @@ bool port_ingress_vlan_filter_set(uint8_t port, __xdata bool enabled) __banked
|
||||
/** Get the ingress VLAN filtering status */
|
||||
bool port_ingress_vlan_filter_get(uint8_t port) __banked
|
||||
{
|
||||
if (port < machine.min_port || port > machine.max_port && port != CPU_PORT) {
|
||||
if (port < machine.min_port || (port > machine.max_port && port != CPU_PORT)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
|
||||
@@ -144,6 +144,9 @@
|
||||
#define RTL837X_REG_CPU_TAG_AWARE_PMASK 0x603C
|
||||
#define RTL837X_REG_MAC_FORCE_MODE 0x6344
|
||||
|
||||
/* Flags in byte 1 of the RX frame descriptor */
|
||||
#define RX_TAG_L4_CSUM_BAD 0x04
|
||||
|
||||
/*
|
||||
* Statistics related registers
|
||||
*/
|
||||
|
||||
@@ -15,6 +15,8 @@
|
||||
#include "uip.h"
|
||||
#include "machine.h"
|
||||
|
||||
extern __xdata uint8_t err_status;
|
||||
|
||||
// All entry points are __banked and nothing here runs from an interrupt,
|
||||
// so the module does not need to stay in the resident bank
|
||||
#pragma codeseg BANK2
|
||||
@@ -1134,5 +1136,6 @@ void stp_parse(void) __banked __reentrant
|
||||
}
|
||||
return;
|
||||
err:
|
||||
err_status = ERR_INVALID_ARGUMENT;
|
||||
print_string("Error: stp on|off|status | prio <0-15> | hello <1-10> | maxage <6-40> | fwd <4-30> | txhold <1-10> | version rstp|stp | port <1-9>|lag <1-4> on|off|edge|cost|prio|guard|filter ...\n");
|
||||
}
|
||||
@@ -303,19 +303,19 @@ void itoa_short(uint16_t v)
|
||||
}
|
||||
|
||||
|
||||
void print_string(__code char *p)
|
||||
void print_string(__code const char *p)
|
||||
{
|
||||
while (*p)
|
||||
write_char(*p++);
|
||||
}
|
||||
|
||||
void print_string_no_syslog(__code char *p)
|
||||
void print_string_no_syslog(__code const char *p)
|
||||
{
|
||||
while (*p)
|
||||
write_char_no_syslog(*p++);
|
||||
}
|
||||
|
||||
void print_string_newline_no_syslog(__code char *p)
|
||||
void print_string_newline_no_syslog(__code const char *p)
|
||||
{
|
||||
write_char_no_syslog('\n');
|
||||
print_string_no_syslog(p);
|
||||
|
||||
@@ -88,7 +88,12 @@ bool sfp_read_field(__xdata char *dst, uint8_t sfp, uint8_t start, uint8_t lengt
|
||||
return false;
|
||||
|
||||
dst[length] = NUL;
|
||||
memcpy(dst, sfp_buf, length);
|
||||
for (uint8_t i = 0; i < length; i++) {
|
||||
uint8_t c = sfp_buf[i];
|
||||
if (c && (c < 0x20 || c > 0x7e || c == '"' || c == '\\'))
|
||||
c = '.';
|
||||
dst[i] = c;
|
||||
}
|
||||
|
||||
while (length > 0 && dst[--length] == ' ')
|
||||
dst[length] = NUL;
|
||||
|
||||
@@ -11,10 +11,11 @@
|
||||
CC = gcc
|
||||
SRCROOT = ..
|
||||
# -include shim first so __xdata/__code/__banked vanish before any firmware header.
|
||||
CFLAGS = -std=gnu11 -g -O1 -Wall -Wextra -Wno-unused-parameter -Wno-unknown-pragmas \
|
||||
CFLAGS = -std=gnu11 -g -O1 -Wall -Wextra -Werror -Wno-unused-parameter -Wno-unknown-pragmas \
|
||||
-Wno-pointer-sign \
|
||||
-DRTLP_HOST_TEST \
|
||||
-fsanitize=address,undefined -fno-omit-frame-pointer \
|
||||
-include sdcc_shim.h -I. -I$(SRCROOT) -I$(SRCROOT)/httpd -I$(SRCROOT)/uip
|
||||
-include sdcc_shim.h -Istub -I. -I$(SRCROOT) -I$(SRCROOT)/httpd -I$(SRCROOT)/uip
|
||||
ifeq ($(findstring clang,$(shell $(CC) --version 2>/dev/null)),)
|
||||
CFLAGS += -Wno-builtin-declaration-mismatch
|
||||
endif
|
||||
@@ -23,7 +24,15 @@ LDFLAGS = -fsanitize=address,undefined
|
||||
BUILDDIR = build
|
||||
|
||||
# One binary per subsystem under test.
|
||||
TESTS = $(BUILDDIR)/test_cmd_editor
|
||||
TESTS = $(BUILDDIR)/test_cmd_editor \
|
||||
$(BUILDDIR)/test_port_tables \
|
||||
$(BUILDDIR)/test_page_json \
|
||||
$(BUILDDIR)/test_httpd_tx
|
||||
|
||||
# The table tests link the register mock and one environment file; stub/
|
||||
# stands in for the two headers a firmware build generates.
|
||||
TABLE_ENV = support.c hw_mock.c env_tables.c $(SRCROOT)/cmd_editor.c \
|
||||
$(SRCROOT)/rtl837x_port.c $(SRCROOT)/httpd/page_impl.c
|
||||
|
||||
all: run
|
||||
|
||||
@@ -34,6 +43,18 @@ $(BUILDDIR):
|
||||
$(BUILDDIR)/test_cmd_editor: test_cmd_editor.c support.c $(SRCROOT)/cmd_editor.c | $(BUILDDIR)
|
||||
$(CC) $(CFLAGS) $^ -o $@ $(LDFLAGS)
|
||||
|
||||
# rtl837x_port.c and httpd/page_impl.c are compiled UNMODIFIED from the firmware tree.
|
||||
$(BUILDDIR)/test_port_tables: test_port_tables.c $(TABLE_ENV) hw_mock.h | $(BUILDDIR)
|
||||
$(CC) $(CFLAGS) $(filter %.c,$^) -o $@ $(LDFLAGS)
|
||||
|
||||
$(BUILDDIR)/test_page_json: test_page_json.c $(TABLE_ENV) hw_mock.h | $(BUILDDIR)
|
||||
$(CC) $(CFLAGS) $(filter %.c,$^) -o $@ $(LDFLAGS)
|
||||
|
||||
# httpd/httpd.c and uip/uip.c are compiled UNMODIFIED from the firmware tree;
|
||||
# this test carries the environment below them itself.
|
||||
$(BUILDDIR)/test_httpd_tx: test_httpd_tx.c $(SRCROOT)/httpd/httpd.c $(SRCROOT)/uip/uip.c | $(BUILDDIR)
|
||||
$(CC) $(CFLAGS) $(filter %.c,$^) -o $@ $(LDFLAGS)
|
||||
|
||||
run: $(TESTS)
|
||||
@fail=0; for t in $(TESTS); do \
|
||||
echo "=== $$t ==="; \
|
||||
|
||||
@@ -19,6 +19,16 @@ fires — so it drops straight into CI.
|
||||
- **`sdcc_shim.h`** (force-included) erases SDCC 8051 keywords (`__xdata`,
|
||||
`__code`, `__banked`, …) so firmware sources compile under host gcc. It changes
|
||||
no logic — the code under test is byte-for-byte the firmware source.
|
||||
- **`hw_mock.c` / `hw_mock.h`** simulate the ASIC edge: a flat register file
|
||||
behind `reg_read`/`reg_write` and the `SFR_DATA_*` registers, with a table
|
||||
engine that performs a VLAN or L2 operation the moment `TBL_CTRL` is written,
|
||||
fetches a MIB counter on `STAT_GET` and drops entries on `L2_TBL_FLUSH_CTRL`.
|
||||
Nothing ever reports busy, so a polling loop runs once. The header states the
|
||||
VLAN and L2 entry layouts independently of the firmware, so a test can hold
|
||||
what `rtl837x_port.c` writes against what `page_impl.c` reads.
|
||||
- **`env_tables.c`** carries the globals and leaf calls those two modules link
|
||||
against; **`stub/`** stands in for the two headers only a firmware build
|
||||
generates, so the harness needs no SDCC build first.
|
||||
- **`support.c` / `support.h`** mock the hardware edges: the 16-byte serial ring
|
||||
(`sbuf`), the command/history buffers, and the character-output sink
|
||||
(`write_char` etc.). Buffers are sized **exactly** as on target, so ASan
|
||||
@@ -35,6 +45,9 @@ fires — so it drops straight into CI.
|
||||
| Test binary | TU under test | Findings exercised |
|
||||
|-------------|---------------|--------------------|
|
||||
| `test_cmd_editor` | `cmd_editor.c` | **C4** — full-line hang + `cmd_buffer` 1-byte overflow; basic entry & backspace regressions |
|
||||
| `test_port_tables` | `rtl837x_port.c` | VLAN entry layout and round trip, PVID register sharing, static multicast and management entries, per-port flush, trunk membership and hash seed |
|
||||
| `test_page_json` | `httpd/page_impl.c` + `rtl837x_port.c` | `/vlan.json`, `/vlanlist`, `/l2.json` (walk, wrap marker, paging inside `outbuf`), 64-bit counters in `/status.json` and `/counters.json` |
|
||||
| `test_httpd_tx` | `httpd/httpd.c` + `uip/uip.c` | a GET of a static file against a client that moves its receive window: ACK accounting, continuation chunks out of flash, retransmission |
|
||||
|
||||
## Adding a test for another module
|
||||
1. Write `test_<module>.c` with `main()` driving the module's entry points and
|
||||
|
||||
@@ -0,0 +1,123 @@
|
||||
/*
|
||||
* env_tables.c - what rtl837x_port.c and httpd/page_impl.c link against
|
||||
* besides the register mock: the console, the buffers page_impl writes into,
|
||||
* the machine description and leaf calls into subsystems not under test.
|
||||
* Buffers keep their firmware sizes so AddressSanitizer sees the same bounds
|
||||
* the 8051 has.
|
||||
*/
|
||||
#include <stdint.h>
|
||||
#include <stdio.h>
|
||||
|
||||
#include "rtl837x_common.h"
|
||||
#include "rtl837x_port.h"
|
||||
#include "machine.h"
|
||||
#include "uip.h"
|
||||
#include "rtl837x_flash.h"
|
||||
#include "syslog.h"
|
||||
#include "rtl837x_phy.h"
|
||||
#include "support.h"
|
||||
|
||||
/* ---- console: everything lands in out_buf (support.c) ---- */
|
||||
void print_byte(uint8_t v)
|
||||
{
|
||||
static const char h[] = "0123456789abcdef";
|
||||
write_char(h[(v >> 4) & 0xf]);
|
||||
write_char(h[v & 0xf]);
|
||||
}
|
||||
void print_short(uint16_t v) { print_byte(v >> 8); print_byte(v); }
|
||||
void print_long(uint32_t v) { print_short(v >> 16); print_short(v); }
|
||||
void print_reg(uint16_t v) { print_short(v); }
|
||||
void itoa_short(uint16_t v)
|
||||
{
|
||||
char b[6]; int n = 0;
|
||||
do { b[n++] = '0' + v % 10; v /= 10; } while (v);
|
||||
while (n) write_char(b[--n]);
|
||||
}
|
||||
void dbg_string(char *p) { (void)p; }
|
||||
void dbg_short(uint16_t v) { (void)v; }
|
||||
void dbg_char(char c) { (void)c; }
|
||||
void dbg_byte(uint8_t v) { (void)v; }
|
||||
|
||||
/* ---- copied verbatim from rtlplayground.c ---- */
|
||||
uint16_t strtox(uint8_t *dst, const char *s)
|
||||
{
|
||||
uint8_t *b = dst;
|
||||
while (*s)
|
||||
*dst++ = *s++;
|
||||
*dst = 0;
|
||||
return dst - b;
|
||||
}
|
||||
|
||||
const uint16_t bit_mask[16] = {
|
||||
0x0001, 0x0002, 0x0004, 0x0008, 0x0010, 0x0020, 0x0040, 0x0080,
|
||||
0x0100, 0x0200, 0x0400, 0x0800, 0x1000, 0x2000, 0x4000, 0x8000
|
||||
};
|
||||
const uint8_t hex_chars[] = "0123456789abcdef";
|
||||
uint8_t *hex = (uint8_t *)hex_chars;
|
||||
|
||||
/* ---- a 9-port RTL8373 whose logical ports map to themselves ---- */
|
||||
const struct machine machine = {
|
||||
.machine_name = "HOSTTEST",
|
||||
.min_port = 0,
|
||||
.max_port = 8,
|
||||
.n_sfp = 0,
|
||||
.log_to_phys_port = { 1, 2, 3, 4, 5, 6, 7, 8, 9 },
|
||||
.phys_to_log_port = { 0, 1, 2, 3, 4, 5, 6, 7, 8 },
|
||||
};
|
||||
struct machine_runtime machine_detected = { .isRTL8373 = 1 };
|
||||
|
||||
/* ---- firmware state the modules read or write ---- */
|
||||
uint8_t outbuf[TCP_OUTBUF_SIZE];
|
||||
uint16_t slen;
|
||||
uint16_t management_vlan = 1;
|
||||
uint16_t cont_len;
|
||||
uint32_t cont_addr;
|
||||
uint8_t vlan_names[VLAN_NAMES_SIZE];
|
||||
uint16_t vlan_ptr;
|
||||
uint8_t sfp_pins_last = 0xff;
|
||||
uint8_t sfp_options[2];
|
||||
char sfp_module_vendor[2][17];
|
||||
char sfp_module_model[2][17];
|
||||
char sfp_module_serial[2][17];
|
||||
char hostname[24] = "hosttest";
|
||||
char port_names[9][PORT_NAME_SIZE];
|
||||
struct flash_region_t flash_region;
|
||||
struct syslog_state syslog_state;
|
||||
bool stp_enabled;
|
||||
|
||||
uip_ipaddr_t uip_hostaddr, uip_draddr, uip_netmask;
|
||||
struct uip_eth_addr uip_ethaddr = { .addr = { 0x02, 0x11, 0x22, 0x33, 0x44, 0x55 } };
|
||||
|
||||
/* ---- leaf calls into subsystems not under test ---- */
|
||||
void flash_read_bulk(uint8_t *dst) { (void)dst; }
|
||||
char *get_flash_size_str(void) { return (char *)"2M"; }
|
||||
uint8_t sfp_read_reg(uint8_t slot, uint8_t reg) { (void)slot; (void)reg; return 0; }
|
||||
bool gpio_pin_test(uint8_t pin) { (void)pin; return false; }
|
||||
void phy_read(uint8_t phy_id, uint8_t dev_id, uint16_t reg) { (void)phy_id; (void)dev_id; (void)reg; }
|
||||
void phy_write(uint8_t phy_id, uint8_t dev_id, uint16_t reg, uint16_t val) { (void)phy_id; (void)dev_id; (void)reg; (void)val; }
|
||||
void phy_reset(uint8_t port) { (void)port; }
|
||||
uint8_t stp_port_role(uint8_t port) { (void)port; return 0; }
|
||||
uint8_t stp_port_state(uint8_t port) { (void)port; return 3; }
|
||||
|
||||
/* ---- STP state send_stp() prints; the tree is not under test here ---- */
|
||||
#include "rtl837x_stp.h"
|
||||
struct bridge root_bridge;
|
||||
struct bridge stp_dbridge[STP_ENTITIES];
|
||||
uint16_t stp_bpdu_age[STP_ENTITIES];
|
||||
uint16_t stp_dpid[STP_ENTITIES];
|
||||
uint16_t stp_lag_mask[STP_LAG_COUNT];
|
||||
uint16_t stp_tc_count;
|
||||
uint32_t root_bridge_cost;
|
||||
uint32_t stp_dcost[STP_ENTITIES];
|
||||
uint32_t stp_pcost[STP_ENTITIES];
|
||||
uint8_t stp_ent_of[10] = { 0, 1, 2, 3, 4, 5, 6, 7, 8, 9 };
|
||||
uint8_t stp_fwddelay_s = 15, stp_hello_s = 2, stp_maxage_s = 20;
|
||||
uint8_t stp_pflags[STP_ENTITIES];
|
||||
uint8_t stp_pp2p[STP_ENTITIES];
|
||||
uint8_t stp_pprio[STP_ENTITIES];
|
||||
uint8_t stp_prio = 0x80, stp_root_port = 0xff, stp_rstp = 1, stp_txhold = 6;
|
||||
|
||||
/* ---- SFP: no module present, reads fail ---- */
|
||||
uint8_t sfp_buf[16];
|
||||
bool sfp_read_block(uint8_t slot, uint8_t reg, uint8_t len) { (void)slot; (void)reg; (void)len; return false; }
|
||||
void print_phys_port(uint8_t port) { itoa_short(machine.log_to_phys_port[port]); }
|
||||
@@ -0,0 +1,342 @@
|
||||
/*
|
||||
* hw_mock.c - the ASIC edge: SFR storage, register file, table engine.
|
||||
*
|
||||
* The shim declares every SFR extern so that including rtl837x_sfr.h from many
|
||||
* translation units does not define them repeatedly. Undefining the four
|
||||
* keywords here and re-including the header emits the definitions once, so
|
||||
* the mock tracks the real register list without a hand-kept copy.
|
||||
*/
|
||||
#include <stdint.h>
|
||||
#include <stdbool.h>
|
||||
#include <string.h>
|
||||
#include <stdlib.h>
|
||||
|
||||
#undef __sfr
|
||||
#undef __sfr16
|
||||
#undef __sfr32
|
||||
#undef __sbit
|
||||
#define __sfr volatile uint8_t
|
||||
#define __sfr16 volatile uint16_t
|
||||
#define __sfr32 volatile uint32_t
|
||||
#define __sbit bool
|
||||
#include "rtl837x_sfr.h"
|
||||
#undef __sfr
|
||||
#undef __sfr16
|
||||
#undef __sfr32
|
||||
#undef __sbit
|
||||
#define __sfr extern volatile uint8_t
|
||||
#define __sfr16 extern volatile uint16_t
|
||||
#define __sfr32 extern volatile uint32_t
|
||||
#define __sbit extern bool
|
||||
|
||||
#include "rtl837x_common.h"
|
||||
#include "rtl837x_regs.h"
|
||||
#include "hw_mock.h"
|
||||
|
||||
uint8_t sfr_data[4];
|
||||
unsigned long hw_reads, hw_writes;
|
||||
|
||||
static uint32_t regfile[0x10000];
|
||||
static uint32_t vlan_tbl[4096];
|
||||
static struct hw_l2 l2[HW_L2_MAX];
|
||||
static uint64_t counters[16][64];
|
||||
|
||||
void hw_reset(void)
|
||||
{
|
||||
memset(regfile, 0, sizeof(regfile));
|
||||
memset(vlan_tbl, 0, sizeof(vlan_tbl));
|
||||
memset(l2, 0, sizeof(l2));
|
||||
memset(counters, 0, sizeof(counters));
|
||||
memset(sfr_data, 0, sizeof(sfr_data));
|
||||
hw_reads = hw_writes = 0;
|
||||
}
|
||||
|
||||
void hw_reg_set(uint16_t addr, uint32_t v) { regfile[addr] = v; }
|
||||
uint32_t hw_reg_get(uint16_t addr) { return regfile[addr]; }
|
||||
uint32_t hw_vlan_word(uint16_t vid) { return vlan_tbl[vid & 0xfff]; }
|
||||
void hw_counter_set(uint8_t port, uint8_t counter, uint64_t value) { counters[port & 0xf][counter & 0x3f] = value; }
|
||||
|
||||
/* ---- L2 table -------------------------------------------------------- */
|
||||
|
||||
static int l2_cmp(const void *x, const void *y)
|
||||
{
|
||||
const struct hw_l2 *a = x, *b = y;
|
||||
if (a->present != b->present)
|
||||
return a->present ? -1 : 1;
|
||||
return (int)a->idx - (int)b->idx;
|
||||
}
|
||||
|
||||
static void l2_sort(void) { qsort(l2, HW_L2_MAX, sizeof(l2[0]), l2_cmp); }
|
||||
|
||||
int hw_l2_count(void)
|
||||
{
|
||||
int n = 0;
|
||||
for (int i = 0; i < HW_L2_MAX; i++)
|
||||
n += l2[i].present;
|
||||
return n;
|
||||
}
|
||||
|
||||
const struct hw_l2 *hw_l2_at(int n)
|
||||
{
|
||||
l2_sort();
|
||||
return (n >= 0 && n < hw_l2_count()) ? &l2[n] : 0;
|
||||
}
|
||||
|
||||
static void l2_key(const struct hw_l2 *e, uint8_t mac[6], uint16_t *vid)
|
||||
{
|
||||
mac[0] = e->b >> 8; mac[1] = e->b;
|
||||
mac[2] = e->a >> 24; mac[3] = e->a >> 16; mac[4] = e->a >> 8; mac[5] = e->a;
|
||||
*vid = ((e->b >> 16) & 0xfff);
|
||||
}
|
||||
|
||||
const struct hw_l2 *hw_l2_find(const uint8_t mac[6], uint16_t vid)
|
||||
{
|
||||
for (int i = 0; i < HW_L2_MAX; i++) {
|
||||
uint8_t m[6]; uint16_t v;
|
||||
if (!l2[i].present)
|
||||
continue;
|
||||
l2_key(&l2[i], m, &v);
|
||||
if (v == vid && !memcmp(m, mac, 6))
|
||||
return &l2[i];
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
static struct hw_l2 *l2_free(void)
|
||||
{
|
||||
for (int i = 0; i < HW_L2_MAX; i++)
|
||||
if (!l2[i].present)
|
||||
return &l2[i];
|
||||
return 0;
|
||||
}
|
||||
|
||||
static uint16_t l2_hash(const uint8_t mac[6], uint16_t vid)
|
||||
{
|
||||
uint16_t h = vid;
|
||||
for (int i = 0; i < 6; i++)
|
||||
h = (uint16_t)(h * 31 + mac[i]);
|
||||
return h & 0xfff;
|
||||
}
|
||||
|
||||
void hw_l2_put(uint16_t idx, const uint8_t mac[6], uint16_t vid, uint16_t port_or_pmask, bool is_static)
|
||||
{
|
||||
struct hw_l2 *e = (struct hw_l2 *)hw_l2_find(mac, vid);
|
||||
bool mc = mac[0] & 1;
|
||||
if (!e)
|
||||
e = l2_free();
|
||||
e->present = true;
|
||||
e->idx = idx & 0xfff;
|
||||
e->a = ((uint32_t)mac[2] << 24) | ((uint32_t)mac[3] << 16) | ((uint32_t)mac[4] << 8) | mac[5];
|
||||
e->b = (0x20u << 24) | ((uint32_t)(vid & 0xfff) << 16) | ((uint32_t)mac[0] << 8) | mac[1];
|
||||
if (mc) {
|
||||
e->b |= (uint32_t)(port_or_pmask & 0x3) << 30;
|
||||
e->c = (port_or_pmask >> 2) & 0xff;
|
||||
} else {
|
||||
e->b |= (uint32_t)(port_or_pmask & 0x3) << 30;
|
||||
e->c = (port_or_pmask >> 2) & 0x3;
|
||||
}
|
||||
if (is_static)
|
||||
e->c |= 1u << 16;
|
||||
}
|
||||
|
||||
void hw_l2_decode(const struct hw_l2 *e, uint8_t mac[6], uint16_t *vid, uint16_t *port_or_pmask,
|
||||
bool *valid, bool *is_static, bool *mc)
|
||||
{
|
||||
l2_key(e, mac, vid);
|
||||
*valid = (e->b >> 29) & 1;
|
||||
*is_static = (e->c >> 16) & 1;
|
||||
*mc = mac[0] & 1;
|
||||
if (*mc)
|
||||
*port_or_pmask = ((e->b >> 30) & 0x3) | ((e->c & 0xff) << 2);
|
||||
else
|
||||
*port_or_pmask = ((e->b >> 30) & 0x3) | ((e->c & 0x3) << 2);
|
||||
}
|
||||
|
||||
/* ---- the engine ------------------------------------------------------ */
|
||||
|
||||
static void l2_out(const struct hw_l2 *e, uint16_t idx)
|
||||
{
|
||||
regfile[RTL837x_L2_DATA_OUT_A] = e ? e->a : 0;
|
||||
regfile[RTL837x_L2_DATA_OUT_B] = e ? e->b : 0;
|
||||
regfile[RTL837x_L2_DATA_OUT_C] = e ? e->c : 0;
|
||||
regfile[RTL837x_TBL_DATA_0] = (regfile[RTL837x_TBL_DATA_0] & ~0x1fffu) | (e ? 0x1000 : 0) | (idx & 0xfff);
|
||||
}
|
||||
|
||||
static void table_exec(uint32_t ctrl)
|
||||
{
|
||||
uint16_t idx = ((ctrl >> 16) & 0xfff);
|
||||
uint8_t type = ctrl >> 8;
|
||||
bool write = ctrl & TBL_WRITE;
|
||||
|
||||
if (type == TBL_VLAN) {
|
||||
if (write)
|
||||
vlan_tbl[idx] = regfile[RTL837x_TBL_DATA_IN_A];
|
||||
else
|
||||
regfile[RTL837x_L2_DATA_OUT_A] = vlan_tbl[idx];
|
||||
return;
|
||||
}
|
||||
if (type != TBL_L2_UNICAST)
|
||||
return;
|
||||
|
||||
uint32_t d0 = regfile[RTL837x_TBL_DATA_0];
|
||||
uint8_t method = (d0 >> 14) & 0x3;
|
||||
bool clear = (d0 >> 18) & 1;
|
||||
|
||||
if (method == TBL_LUTREAD_MAC) {
|
||||
struct hw_l2 key = { .present = true, .a = regfile[RTL837x_TBL_DATA_IN_A],
|
||||
.b = regfile[RTL837x_TBL_DATA_IN_B], .c = regfile[RTL837x_TBL_DATA_IN_C] };
|
||||
uint8_t mac[6]; uint16_t vid;
|
||||
l2_key(&key, mac, &vid);
|
||||
struct hw_l2 *e = (struct hw_l2 *)hw_l2_find(mac, vid);
|
||||
if (write) {
|
||||
if (clear) {
|
||||
if (e)
|
||||
e->present = false;
|
||||
l2_out(0, 0);
|
||||
return;
|
||||
}
|
||||
if (!e) {
|
||||
e = l2_free();
|
||||
e->idx = l2_hash(mac, vid);
|
||||
}
|
||||
e->present = true;
|
||||
e->a = key.a; e->b = key.b; e->c = key.c;
|
||||
l2_out(e, e->idx);
|
||||
return;
|
||||
}
|
||||
l2_out(e, e ? e->idx : 0);
|
||||
return;
|
||||
}
|
||||
|
||||
/* next-entry reads: the first present entry at or after idx, wrapping */
|
||||
l2_sort();
|
||||
int n = hw_l2_count();
|
||||
struct hw_l2 *e = 0;
|
||||
for (int i = 0; i < n; i++)
|
||||
if (l2[i].idx >= idx) { e = &l2[i]; break; }
|
||||
if (!e && n)
|
||||
e = &l2[0];
|
||||
if (write) {
|
||||
/* a write by index replaces the entry that sits there */
|
||||
for (int i = 0; i < n; i++)
|
||||
if (l2[i].idx == idx) { e = &l2[i]; break; }
|
||||
if (!e)
|
||||
e = l2_free();
|
||||
e->present = (regfile[RTL837x_TBL_DATA_IN_B] >> 29) & 1;
|
||||
e->idx = idx;
|
||||
e->a = regfile[RTL837x_TBL_DATA_IN_A];
|
||||
e->b = regfile[RTL837x_TBL_DATA_IN_B];
|
||||
e->c = regfile[RTL837x_TBL_DATA_IN_C];
|
||||
return;
|
||||
}
|
||||
l2_out(e, e ? e->idx : idx);
|
||||
}
|
||||
|
||||
static void flush_exec(uint32_t ctrl)
|
||||
{
|
||||
uint16_t mask = ctrl & 0x3ff;
|
||||
bool dynamic_only = regfile[RTL837x_L2_TBL_FLUSH_CNF] == 0;
|
||||
for (int i = 0; i < HW_L2_MAX; i++) {
|
||||
uint8_t mac[6]; uint16_t vid, p; bool valid, st, mc;
|
||||
if (!l2[i].present)
|
||||
continue;
|
||||
hw_l2_decode(&l2[i], mac, &vid, &p, &valid, &st, &mc);
|
||||
if (mc || (st && dynamic_only))
|
||||
continue;
|
||||
if ((mask >> p) & 1)
|
||||
l2[i].present = false;
|
||||
}
|
||||
}
|
||||
|
||||
static void stat_exec(uint32_t v)
|
||||
{
|
||||
uint8_t cnt = (uint8_t)(((v >> 8) & 0xff) << 3) | ((v >> 5) & 0x7);
|
||||
uint8_t port = (v >> 1) & 0xf;
|
||||
uint64_t c = counters[port][cnt & 0x3f];
|
||||
regfile[RTL837X_STAT_V_HIGH] = (uint32_t)(c >> 32);
|
||||
regfile[RTL837X_STAT_V_LOW] = (uint32_t)c;
|
||||
}
|
||||
|
||||
static void reg_store(uint16_t addr, uint32_t v)
|
||||
{
|
||||
hw_writes++;
|
||||
regfile[addr] = v;
|
||||
switch (addr) {
|
||||
case RTL837X_TBL_CTRL:
|
||||
if (v & TBL_EXECUTE) {
|
||||
table_exec(v);
|
||||
regfile[addr] = v & ~(uint32_t)TBL_EXECUTE;
|
||||
}
|
||||
break;
|
||||
case RTL837x_L2_TBL_FLUSH_CTRL:
|
||||
if (v & L2_TBL_FLUSH_EXEC) {
|
||||
flush_exec(v);
|
||||
regfile[addr] = v & ~(uint32_t)L2_TBL_FLUSH_EXEC;
|
||||
}
|
||||
break;
|
||||
case RTL837X_STAT_GET:
|
||||
if (v & 1) {
|
||||
stat_exec(v);
|
||||
regfile[addr] = v & ~1u;
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
static uint32_t sfr_word(void)
|
||||
{
|
||||
return ((uint32_t)SFR_DATA_24 << 24) | ((uint32_t)SFR_DATA_16 << 16) | ((uint32_t)SFR_DATA_8 << 8) | SFR_DATA_0;
|
||||
}
|
||||
|
||||
static void sfr_load(uint32_t v)
|
||||
{
|
||||
SFR_DATA_24 = v >> 24; SFR_DATA_16 = v >> 16; SFR_DATA_8 = v >> 8; SFR_DATA_0 = v;
|
||||
}
|
||||
|
||||
void reg_read(uint16_t addr)
|
||||
{
|
||||
hw_reads++;
|
||||
sfr_load(regfile[addr]);
|
||||
}
|
||||
|
||||
void reg_read_m(uint16_t addr)
|
||||
{
|
||||
reg_read(addr);
|
||||
sfr_data[0] = SFR_DATA_24; sfr_data[1] = SFR_DATA_16; sfr_data[2] = SFR_DATA_8; sfr_data[3] = SFR_DATA_0;
|
||||
}
|
||||
|
||||
void reg_write(uint16_t addr)
|
||||
{
|
||||
reg_store(addr, sfr_word());
|
||||
}
|
||||
|
||||
void reg_write_m(uint16_t addr)
|
||||
{
|
||||
sfr_load(((uint32_t)sfr_data[0] << 24) | ((uint32_t)sfr_data[1] << 16) | ((uint32_t)sfr_data[2] << 8) | sfr_data[3]);
|
||||
reg_write(addr);
|
||||
}
|
||||
|
||||
void reg_bit_set(uint16_t addr, char bit)
|
||||
{
|
||||
reg_read_m(addr);
|
||||
sfr_data[3 - (bit >> 3)] |= 1 << (bit & 7);
|
||||
reg_write_m(addr);
|
||||
}
|
||||
|
||||
void reg_bit_clear(uint16_t addr, char bit)
|
||||
{
|
||||
reg_read_m(addr);
|
||||
sfr_data[3 - (bit >> 3)] &= ~(1 << (bit & 7));
|
||||
reg_write_m(addr);
|
||||
}
|
||||
|
||||
uint8_t reg_bit_test(uint16_t addr, char bit)
|
||||
{
|
||||
reg_read_m(addr);
|
||||
return (sfr_data[3 - (bit >> 3)] >> (bit & 7)) & 1;
|
||||
}
|
||||
|
||||
void sfr_mask_data(uint8_t n, uint8_t mask, uint8_t set)
|
||||
{
|
||||
sfr_data[3 - n] = (sfr_data[3 - n] & ~mask) | set;
|
||||
}
|
||||
@@ -0,0 +1,59 @@
|
||||
/*
|
||||
* hw_mock.h - a simulated RTL837x register file and table engine.
|
||||
*
|
||||
* The firmware reaches the ASIC only through reg_read/reg_read_m/reg_write/
|
||||
* reg_write_m and the SFR_DATA_* registers, so mocking that edge is enough to
|
||||
* run the table code on the host. Every register is a 32-bit word in a flat
|
||||
* file; a few addresses are live: writing TBL_CTRL with EXECUTE performs the
|
||||
* table operation at once and clears the bit, STAT_GET fetches a counter,
|
||||
* L2_TBL_FLUSH_CTRL drops entries. Nothing here ever reports busy, so a
|
||||
* polling loop in the firmware runs exactly once.
|
||||
*
|
||||
* The VLAN and L2 entry layouts mirror what rtl837x_port.c writes and what
|
||||
* httpd/page_impl.c reads; the decoders below are an independent statement
|
||||
* of that layout, so a test can compare the firmware's two ends against it.
|
||||
*/
|
||||
#ifndef TEST_HW_MOCK_H
|
||||
#define TEST_HW_MOCK_H
|
||||
|
||||
#include <stdint.h>
|
||||
#include <stdbool.h>
|
||||
|
||||
void hw_reset(void); /* every register 0, tables empty */
|
||||
void hw_reg_set(uint16_t addr, uint32_t v);
|
||||
uint32_t hw_reg_get(uint16_t addr);
|
||||
|
||||
extern uint8_t sfr_data[4]; /* the firmware's register scratch, owned here */
|
||||
extern unsigned long hw_reads; /* reg_read + reg_read_m calls since hw_reset */
|
||||
extern unsigned long hw_writes; /* reg_write + reg_write_m calls since hw_reset */
|
||||
|
||||
/* VLAN table: the 32-bit word the firmware wrote for that VID, 0 = never written */
|
||||
uint32_t hw_vlan_word(uint16_t vid);
|
||||
|
||||
/* L2 table: entries as the three data words the firmware exchanges with the
|
||||
* engine. Word B: bit 29 valid, bits 31:30 the low two bits of the port (or
|
||||
* of the member mask), bits 27:16 VID, bits 15:0 the first two MAC octets.
|
||||
* Word A: the last four MAC octets. Word C: bit 16 static, bits 1:0 the high
|
||||
* two bits of the port; for a multicast MAC bits 7:0 are the member mask
|
||||
* shifted right by two. */
|
||||
#define HW_L2_MAX 64
|
||||
struct hw_l2 {
|
||||
bool present;
|
||||
uint16_t idx;
|
||||
uint32_t a, b, c; /* DATA_IN_A/B/C on write, L2_DATA_OUT_A/B/C on read */
|
||||
};
|
||||
|
||||
/* Pointers returned below stay valid until the next table operation. */
|
||||
int hw_l2_count(void);
|
||||
const struct hw_l2 *hw_l2_at(int n); /* n-th present entry in idx order */
|
||||
const struct hw_l2 *hw_l2_find(const uint8_t mac[6], uint16_t vid);
|
||||
/* Put an entry the way the switch would hold it: a learned unicast entry on
|
||||
* one port, or a static one. mc entries use pmask instead of a port. */
|
||||
void hw_l2_put(uint16_t idx, const uint8_t mac[6], uint16_t vid, uint16_t port_or_pmask, bool is_static);
|
||||
void hw_l2_decode(const struct hw_l2 *e, uint8_t mac[6], uint16_t *vid, uint16_t *port_or_pmask,
|
||||
bool *valid, bool *is_static, bool *mc);
|
||||
|
||||
/* MIB counter the next STAT_GET for (port, counter) returns */
|
||||
void hw_counter_set(uint8_t port, uint8_t counter, uint64_t value);
|
||||
|
||||
#endif /* TEST_HW_MOCK_H */
|
||||
@@ -12,6 +12,7 @@
|
||||
#define SDCC_SHIM_H
|
||||
|
||||
#include <stdbool.h>
|
||||
#include <stdint.h>
|
||||
|
||||
/* Memory-space qualifiers → nothing (host has one flat address space). */
|
||||
#define __xdata
|
||||
@@ -22,12 +23,27 @@
|
||||
/* bool, not unsigned char: SDCC's bit types normalize any assigned value
|
||||
* to 0/1, which _Bool reproduces exactly. */
|
||||
#define __bit bool
|
||||
#define __sbit bool
|
||||
|
||||
/* NOTE: the firmware ships its own 8051 memcpy/memset/strlen/strcpy/sleep with
|
||||
* incompatible signatures (rtl837x_common.h). Host TUs must therefore build with
|
||||
* -fno-builtin (so gcc doesn't auto-declare the builtins) and must NOT include
|
||||
* <string.h>/<unistd.h>. Test code uses the t_* helpers in support.h instead. */
|
||||
/* Special-function registers are declared in rtl837x_sfr.h as
|
||||
* `__sfr __at(0xNN) NAME;`. With __at erased those become plain declarations,
|
||||
* so they must carry extern or every translation unit including the header
|
||||
* defines its own copy. __sbit is the bit-in-an-SFR form and only appears
|
||||
* there; the local bit variable keyword is __bit above. test/hw_mock.c
|
||||
* undefines these four and re-includes the header once to emit the storage. */
|
||||
#define __sfr extern volatile uint8_t
|
||||
#define __sfr16 extern volatile uint16_t
|
||||
#define __sfr32 extern volatile uint32_t
|
||||
#define __sbit extern bool
|
||||
|
||||
/* The firmware ships its own memcpy/memset/strlen/strcpy/sleep, and
|
||||
* rtl837x_common.h hides those prototypes under RTLP_HOST_TEST so host builds
|
||||
* take the C library versions. Something has to declare them, and firmware
|
||||
* sources do call memset and memcpy; this header is force-included ahead of
|
||||
* everything, so pulling <string.h> in here puts the libc versions in scope
|
||||
* before any firmware header and nothing clashes. sleep() is not in
|
||||
* <string.h> and is declared on its own rather than through <unistd.h>. */
|
||||
#include <string.h>
|
||||
extern unsigned int sleep(unsigned int seconds);
|
||||
|
||||
/* Function attributes → nothing (no banking / no 8051 calling conventions). */
|
||||
#define __banked
|
||||
|
||||
@@ -0,0 +1,11 @@
|
||||
/* Stand-in for the generated html_data.h: the table generators under test
|
||||
* never touch the embedded file list, and the real header exists only after
|
||||
* a firmware build, which the host harness must not depend on. */
|
||||
#ifndef FDATA_DEFS_H
|
||||
#define FDATA_DEFS_H
|
||||
#include <stdint.h>
|
||||
typedef enum mime_type_e { mime_HTML = 0, mime_SVG, mime_ICO, mime_PNG, mime_JS, mime_CSS, mime_TXT } mime_type_t;
|
||||
struct f_data { __code char *file; uint32_t start; uint16_t len; mime_type_t mime; uint8_t gzip; };
|
||||
#define FDATA_START_login_html 0x00040000UL
|
||||
#define FDATA_START_favicon_ico 0x00041000UL
|
||||
#endif
|
||||
@@ -0,0 +1,5 @@
|
||||
#ifndef VERSION_H
|
||||
#define VERSION_H
|
||||
#define VERSION_SW "host-test"
|
||||
#define BUILD_DATE "1970-01-01 00:00:00"
|
||||
#endif
|
||||
@@ -47,7 +47,7 @@ static void out_putc(char c) {
|
||||
|
||||
/* --- mocked print helpers (signatures match rtl837x_common.h post-shim) ---- */
|
||||
void write_char(char c) { out_putc(c); }
|
||||
void print_string(char *p) { while (*p) out_putc(*p++); }
|
||||
void print_string(const char *p) { while (*p) out_putc(*p++); }
|
||||
void print_string_x(char *p) { while (*p) out_putc(*p++); }
|
||||
void print_cmd_prompt(void) { print_string((char *)"> "); }
|
||||
void itoa(uint8_t v) { /* firmware prints a decimal number */
|
||||
|
||||
@@ -0,0 +1,508 @@
|
||||
/*
|
||||
* Host-side bench for the httpd transmit path.
|
||||
*
|
||||
* Compiles the UNMODIFIED httpd/httpd.c and uip/uip.c and drives them through a
|
||||
* real handshake and a real GET from a client that moves its receive window.
|
||||
* Everything below the two modules - flash, console, JSON pages - is mocked
|
||||
* here, so what the bench observes is the byte stream the firmware would put on
|
||||
* the wire.
|
||||
*
|
||||
* The file served out of the simulated flash carries a position-dependent
|
||||
* pattern, so a duplicated or skipped range shows up as a mismatch at a known
|
||||
* offset instead of an anonymous "content differs".
|
||||
*
|
||||
* Run: make -C test (exit code 0 = all scenarios pass)
|
||||
*/
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
|
||||
/* Some hosts ship these in <sys/_endian.h>, and they assign to their argument;
|
||||
* uip.h refuses to be included next to them. The firmware definitions are the
|
||||
* ones this bench needs, so the host macros go first. */
|
||||
#undef HTONS
|
||||
#undef NTOHS
|
||||
|
||||
#include "httpd.h"
|
||||
#include "uip.h"
|
||||
#include "rtl837x_common.h"
|
||||
#include "rtl837x_regs.h"
|
||||
#include "rtl837x_flash.h"
|
||||
#include "page_impl.h"
|
||||
#include "html_data.h"
|
||||
|
||||
/* Private to uip.c, and the client needs them to build segments. */
|
||||
#define TCP_SYN 0x02
|
||||
#define TCP_PSH 0x08
|
||||
#define TCP_ACK 0x10
|
||||
|
||||
#define TCPH ((struct uip_tcpip_hdr *)&uip_buf[UIP_LLH_LEN])
|
||||
|
||||
#define MSS_FULL 1460
|
||||
#define SMALL_WINDOW 600
|
||||
|
||||
/* httpd.c serves this one file without a session, which keeps the bench clear
|
||||
* of the login machinery: what is under test is the transmit path. */
|
||||
#define FILE_START FDATA_START_login_html
|
||||
#define FILE_NAME "/login.html"
|
||||
#define FILE_LEN 6000
|
||||
#define STREAM_MAX 16384
|
||||
|
||||
static int failures;
|
||||
static int verbose;
|
||||
|
||||
#define CHECK(cond, name) do { \
|
||||
if (cond) printf("PASS %s\n", name); \
|
||||
else { printf("FAIL %s\n", name); failures++; } \
|
||||
} while (0)
|
||||
|
||||
static uint8_t pattern(uint32_t addr)
|
||||
{
|
||||
return (uint8_t)((addr * 7u) + (addr >> 8));
|
||||
}
|
||||
|
||||
/* ---- firmware environment below httpd.c --------------------------------- */
|
||||
|
||||
volatile uint8_t sfr_data[4];
|
||||
volatile uint32_t ticks;
|
||||
uint8_t cmd_capture;
|
||||
uint8_t err_status;
|
||||
uint8_t *hex = (uint8_t *)"0123456789abcdef";
|
||||
uint16_t crc_value;
|
||||
uint8_t *HTTP_RESPONCE_TXT = (uint8_t *)"HTTP/1.1 200 OK\r\n\r\n";
|
||||
uint32_t flash_size = 0x80000;
|
||||
uint8_t flash_buf[FLASH_BUF_SIZE];
|
||||
uint8_t rx_headers[16];
|
||||
struct flash_region_t flash_region;
|
||||
|
||||
char *mime_strings[] = { "text/html", "image/svg+xml", "image/x-icon",
|
||||
"image/png", "text/javascript", "text/css", "text/plain" };
|
||||
|
||||
struct f_data f_data[] = {
|
||||
{ FILE_NAME, FILE_START, FILE_LEN, mime_HTML, 0 },
|
||||
{ 0, 0, 0, mime_HTML, 0 },
|
||||
};
|
||||
|
||||
void flash_read_bulk(uint8_t *dst)
|
||||
{
|
||||
for (uint16_t i = 0; i < flash_region.len; i++)
|
||||
dst[i] = pattern(flash_region.addr + i);
|
||||
}
|
||||
|
||||
void flash_init(uint8_t enable_dio) { (void)enable_dio; }
|
||||
void flash_sector_erase(void) { }
|
||||
void flash_write_bytes(uint8_t *ptr) { (void)ptr; }
|
||||
char *get_flash_size_str(void) { return "512 kB"; }
|
||||
void crc16_bank1(uint8_t *v) { (void)v; }
|
||||
void reset_chip(void) { }
|
||||
void delay(uint16_t t) { (void)t; }
|
||||
void write_char(char c) { (void)c; }
|
||||
void write_char_no_syslog(char c) { (void)c; }
|
||||
void print_string(const char *p) { (void)p; }
|
||||
void print_string_newline_no_syslog(const char *p) { (void)p; }
|
||||
void set_sys_led_state(uint8_t state) { (void)state; }
|
||||
void cmd_parser(void) { }
|
||||
void execute_config(void) { }
|
||||
void execute_commands(uint8_t *p) { (void)p; }
|
||||
void clear_command_history(void) { }
|
||||
void udp_callbacks(void) { }
|
||||
void tcpip_output(void) { }
|
||||
void get_random_32(void) { }
|
||||
void read_reg_timer(uint32_t *tmr) { *tmr = 0; }
|
||||
|
||||
/* uip-conf.h asks uIP not to define the packet buffer: on the switch it lives
|
||||
* at a fixed XDATA address, so the bench provides the storage itself. */
|
||||
u8_t uip_buf[UIP_BUFSIZE + 2];
|
||||
|
||||
uint16_t strlen_x(const char *s) { return (uint16_t)strlen(s); }
|
||||
|
||||
uint16_t strtox(uint8_t *dst, const char *s)
|
||||
{
|
||||
uint16_t n = 0;
|
||||
|
||||
while (s[n]) { dst[n] = (uint8_t)s[n]; n++; }
|
||||
return n;
|
||||
}
|
||||
|
||||
void memcpyc(uint8_t *dst, uint8_t *src, uint16_t len) { memcpy(dst, src, len); }
|
||||
|
||||
bool strstart(const uint8_t *a, const uint8_t *b)
|
||||
{
|
||||
while (*b) { if (*a++ != *b++) return false; }
|
||||
return true;
|
||||
}
|
||||
|
||||
bool strstart_x(const uint8_t *a, const uint8_t *b) { return strstart(a, b); }
|
||||
|
||||
bool send_counters(uint8_t phys_port) { (void)phys_port; return false; }
|
||||
void send_status(void) { }
|
||||
void send_vlan(uint16_t vlan) { (void)vlan; }
|
||||
void send_basic_info(void) { }
|
||||
void send_bandwidth(void) { }
|
||||
void send_eee(void) { }
|
||||
void send_l2(uint16_t idx) { (void)idx; }
|
||||
void l2_delete(uint16_t idx) { (void)idx; }
|
||||
void send_mirror(void) { }
|
||||
void send_mtu(void) { }
|
||||
void send_config(void) { }
|
||||
void send_cmd_log(void) { }
|
||||
void send_lag(void) { }
|
||||
void send_stp(void) { }
|
||||
void send_stp_counters(void) { }
|
||||
void send_vlanlist(void) { }
|
||||
|
||||
extern uint8_t authenticated;
|
||||
|
||||
/* uip.c defines the listen table but no header declares it; the bench reads it
|
||||
* to confirm the port httpd_init() asked for is the one uIP is watching. */
|
||||
extern u16_t uip_listenports[UIP_LISTENPORTS];
|
||||
|
||||
/* ---- the simulated client ----------------------------------------------- */
|
||||
|
||||
static uint32_t cli_seq; /* next sequence number we send */
|
||||
static uint32_t cli_rcv_nxt; /* next sequence number we expect */
|
||||
static uint16_t cli_window; /* what we advertise */
|
||||
|
||||
static uint8_t stream[STREAM_MAX]; /* bytes accepted from the server */
|
||||
static int stream_len;
|
||||
|
||||
static uint8_t last_tx[MSS_FULL + 64]; /* payload of the last server segment */
|
||||
static int last_tx_len;
|
||||
|
||||
static uint16_t hton16(uint16_t v)
|
||||
{
|
||||
return (uint16_t)((v << 8) | (v >> 8));
|
||||
}
|
||||
|
||||
static void wr32(uint8_t *p, uint32_t v)
|
||||
{
|
||||
p[0] = v >> 24; p[1] = v >> 16; p[2] = v >> 8; p[3] = v;
|
||||
}
|
||||
|
||||
static uint32_t rd32(const uint8_t *p)
|
||||
{
|
||||
return ((uint32_t)p[0] << 24) | ((uint32_t)p[1] << 16) |
|
||||
((uint32_t)p[2] << 8) | p[3];
|
||||
}
|
||||
|
||||
static void trace(const char *tag)
|
||||
{
|
||||
if (!verbose)
|
||||
return;
|
||||
printf(" [%-8s] uip_len=%-5u flags=0x%02x seq=%-6u ack=%-6u | conn: state=0x%02x len=%-5u mss=%-5u tstate=%u\n",
|
||||
tag, uip_len,
|
||||
uip_len ? TCPH->flags : 0,
|
||||
uip_len ? rd32(TCPH->seqno) : 0,
|
||||
uip_len ? rd32(TCPH->ackno) : 0,
|
||||
uip_conns[0].tcpstateflags, uip_conns[0].len, uip_conns[0].mss,
|
||||
uip_conns[0].appstate.tstate);
|
||||
}
|
||||
|
||||
/* Takes in one outgoing segment, if uIP produced one. */
|
||||
static int harvest(void)
|
||||
{
|
||||
uint8_t *payload;
|
||||
uint32_t seq;
|
||||
int plen, hlen;
|
||||
|
||||
if (uip_len == 0)
|
||||
return -1;
|
||||
|
||||
seq = rd32(TCPH->seqno);
|
||||
/* The SYNACK carries the MSS option, so the header is not always 20 B. */
|
||||
hlen = (TCPH->tcpoffset >> 4) * 4;
|
||||
payload = &uip_buf[UIP_LLH_LEN + 20 + hlen];
|
||||
plen = (int)uip_len - 20 - hlen;
|
||||
if (plen < 0)
|
||||
plen = 0;
|
||||
|
||||
if (plen > 0) {
|
||||
last_tx_len = plen > (int)sizeof(last_tx) ? (int)sizeof(last_tx) : plen;
|
||||
memcpy(last_tx, payload, last_tx_len);
|
||||
|
||||
/* A real client keeps what continues the stream and drops the
|
||||
* rest, so bytes sent twice under new sequence numbers land in
|
||||
* the file just as they would in a browser. */
|
||||
if (seq == cli_rcv_nxt) {
|
||||
if (stream_len + plen <= STREAM_MAX) {
|
||||
memcpy(stream + stream_len, payload, plen);
|
||||
stream_len += plen;
|
||||
}
|
||||
cli_rcv_nxt += plen;
|
||||
}
|
||||
if (verbose)
|
||||
printf(" server -> %d B, seq %u\n", plen, seq);
|
||||
}
|
||||
if (TCPH->flags & TCP_SYN)
|
||||
cli_rcv_nxt = seq + 1;
|
||||
|
||||
uip_len = 0;
|
||||
return plen;
|
||||
}
|
||||
|
||||
static void client_send(uint8_t flags, const char *payload, int plen)
|
||||
{
|
||||
int hlen = 20;
|
||||
|
||||
/* A SYN carries the MSS option, as every real client does: uIP takes the
|
||||
* connection MSS from it, and without one the server has nothing to send
|
||||
* with. */
|
||||
if (flags & TCP_SYN)
|
||||
hlen = 24;
|
||||
|
||||
memset(uip_buf, 0, UIP_LLH_LEN + 20 + hlen + (plen > 0 ? plen : 0));
|
||||
TCPH->vhl = 0x45;
|
||||
TCPH->tos = 0;
|
||||
TCPH->len[0] = (uint8_t)((20 + hlen + plen) >> 8);
|
||||
TCPH->len[1] = (uint8_t)((20 + hlen + plen) & 0xff);
|
||||
TCPH->ttl = 64;
|
||||
TCPH->proto = UIP_PROTO_TCP;
|
||||
TCPH->ipchksum = 0;
|
||||
TCPH->srcipaddr[0] = hton16(0x0a00); TCPH->srcipaddr[1] = hton16(0x0002);
|
||||
TCPH->destipaddr[0] = hton16(0x0a00); TCPH->destipaddr[1] = hton16(0x0001);
|
||||
TCPH->srcport = hton16(40000);
|
||||
TCPH->destport = hton16(80);
|
||||
wr32(TCPH->seqno, cli_seq);
|
||||
wr32(TCPH->ackno, cli_rcv_nxt);
|
||||
TCPH->tcpoffset = (uint8_t)((hlen / 4) << 4);
|
||||
TCPH->flags = flags;
|
||||
TCPH->wnd[0] = (uint8_t)(cli_window >> 8);
|
||||
TCPH->wnd[1] = (uint8_t)(cli_window & 0xff);
|
||||
TCPH->tcpchksum = 0;
|
||||
|
||||
if (hlen == 24) {
|
||||
uint8_t *opt = &uip_buf[UIP_LLH_LEN + 40];
|
||||
|
||||
opt[0] = 2; opt[1] = 4; /* kind = MSS, length 4 */
|
||||
opt[2] = MSS_FULL >> 8; opt[3] = MSS_FULL & 0xff;
|
||||
}
|
||||
if (plen > 0)
|
||||
memcpy(&uip_buf[UIP_LLH_LEN + 20 + hlen], payload, plen);
|
||||
|
||||
uip_len = 20 + hlen + plen;
|
||||
uip_input();
|
||||
trace("input");
|
||||
cli_seq += plen;
|
||||
if (flags & TCP_SYN)
|
||||
cli_seq++;
|
||||
harvest();
|
||||
}
|
||||
|
||||
static void client_ack(uint16_t window)
|
||||
{
|
||||
cli_window = window;
|
||||
client_send(TCP_ACK, NULL, 0);
|
||||
}
|
||||
|
||||
static void run_periodic(int rounds)
|
||||
{
|
||||
for (int i = 0; i < rounds; i++) {
|
||||
uip_periodic(0);
|
||||
trace("timer");
|
||||
harvest();
|
||||
}
|
||||
}
|
||||
|
||||
static void session_start(uint16_t window)
|
||||
{
|
||||
uip_init();
|
||||
httpd_init();
|
||||
authenticated = 1;
|
||||
|
||||
cli_seq = 1000;
|
||||
cli_rcv_nxt = 0;
|
||||
cli_window = window;
|
||||
stream_len = 0;
|
||||
last_tx_len = 0;
|
||||
|
||||
client_send(TCP_SYN, NULL, 0);
|
||||
client_ack(window);
|
||||
}
|
||||
|
||||
static void request_file(uint16_t window)
|
||||
{
|
||||
static const char get[] = "GET " FILE_NAME " HTTP/1.1\r\nHost: sw\r\n\r\n";
|
||||
|
||||
cli_window = window;
|
||||
client_send(TCP_ACK | TCP_PSH, get, (int)strlen(get));
|
||||
}
|
||||
|
||||
/* Drains the response, acknowledging every segment with the given window. */
|
||||
static void drain(uint16_t window)
|
||||
{
|
||||
for (int i = 0; i < 20 && stream_len < STREAM_MAX; i++) {
|
||||
client_ack(window);
|
||||
run_periodic(1);
|
||||
}
|
||||
}
|
||||
|
||||
static int body_offset(void)
|
||||
{
|
||||
for (int i = 0; i + 4 <= stream_len; i++)
|
||||
if (!memcmp(stream + i, "\r\n\r\n", 4))
|
||||
return i + 4;
|
||||
return -1;
|
||||
}
|
||||
|
||||
/* Offset of the first body byte that is not the one the file holds there, or
|
||||
* -1 when the body matches, or -2 when no header ever arrived. */
|
||||
static int first_body_mismatch(void)
|
||||
{
|
||||
int off = body_offset();
|
||||
|
||||
if (off < 0)
|
||||
return -2;
|
||||
for (int i = 0; i < FILE_LEN && off + i < stream_len; i++)
|
||||
if (stream[off + i] != pattern(FILE_START + i))
|
||||
return i;
|
||||
return -1;
|
||||
}
|
||||
|
||||
static void report(const char *tag)
|
||||
{
|
||||
if (verbose)
|
||||
printf(" %s: header %d B, stream %d B, first mismatch %d\n",
|
||||
tag, body_offset(), stream_len, first_body_mismatch());
|
||||
}
|
||||
|
||||
/* ---- scenarios ---------------------------------------------------------- */
|
||||
|
||||
/* Instrument check: with a window that never moves, the MSS at ACK time and the
|
||||
* length sent earlier are the same number, so the file must arrive intact. If
|
||||
* this one fails, the bench is wrong - not the firmware. */
|
||||
static void scenario_steady_window(void)
|
||||
{
|
||||
session_start(MSS_FULL);
|
||||
if (verbose)
|
||||
printf(" listen port=0x%04x, initial mss=%u\n",
|
||||
uip_listenports[0], uip_conns[0].initialmss);
|
||||
request_file(MSS_FULL);
|
||||
drain(MSS_FULL);
|
||||
report("steady");
|
||||
|
||||
CHECK(body_offset() >= 0, "control: response carries an HTTP header");
|
||||
CHECK(stream_len >= body_offset() + FILE_LEN,
|
||||
"control: the whole file arrives");
|
||||
CHECK(first_body_mismatch() == -1,
|
||||
"control: file content without duplicates or gaps");
|
||||
}
|
||||
|
||||
/* The client stops draining, so the window in the ACK is smaller than the
|
||||
* segment that ACK covers. */
|
||||
static void scenario_shrinking_window(void)
|
||||
{
|
||||
session_start(MSS_FULL);
|
||||
request_file(MSS_FULL);
|
||||
drain(SMALL_WINDOW);
|
||||
report("shrinking");
|
||||
|
||||
CHECK(body_offset() >= 0,
|
||||
"shrinking window: response carries an HTTP header");
|
||||
CHECK(stream_len >= body_offset() + FILE_LEN,
|
||||
"shrinking window: the whole file arrives");
|
||||
CHECK(first_body_mismatch() == -1,
|
||||
"shrinking window: file content without duplicates or gaps");
|
||||
}
|
||||
|
||||
/* The client catches up, so the window in the ACK is larger than the segment
|
||||
* that ACK covers. */
|
||||
static void scenario_growing_window(void)
|
||||
{
|
||||
session_start(SMALL_WINDOW);
|
||||
request_file(SMALL_WINDOW);
|
||||
drain(MSS_FULL);
|
||||
report("growing");
|
||||
|
||||
CHECK(stream_len >= body_offset() + FILE_LEN,
|
||||
"growing window: the whole file arrives");
|
||||
CHECK(first_body_mismatch() == -1,
|
||||
"growing window: file content without duplicates or gaps");
|
||||
}
|
||||
|
||||
/* A segment is lost, a window update shrinks the window while it is still
|
||||
* unacknowledged, and the retransmission timer fires. */
|
||||
static void scenario_rexmit_after_shrink(void)
|
||||
{
|
||||
uint8_t original[MSS_FULL + 64];
|
||||
int original_len, same = 1;
|
||||
|
||||
session_start(MSS_FULL);
|
||||
request_file(MSS_FULL);
|
||||
|
||||
original_len = last_tx_len;
|
||||
memcpy(original, last_tx, original_len);
|
||||
|
||||
/* The segment never arrived: take it back out of the client's stream and
|
||||
* send a pure window update, which acknowledges nothing. */
|
||||
if (original_len > 0 && stream_len >= original_len) {
|
||||
stream_len -= original_len;
|
||||
cli_rcv_nxt -= original_len;
|
||||
}
|
||||
cli_window = SMALL_WINDOW;
|
||||
client_send(TCP_ACK, NULL, 0);
|
||||
|
||||
memset(&uip_buf[UIP_LLH_LEN + 40], 0xaa, MSS_FULL);
|
||||
|
||||
last_tx_len = 0;
|
||||
run_periodic(UIP_RTO + 2);
|
||||
|
||||
if (last_tx_len != original_len || memcmp(last_tx, original, original_len))
|
||||
same = 0;
|
||||
|
||||
if (verbose)
|
||||
printf(" first %d B, retransmitted %d B\n",
|
||||
original_len, last_tx_len);
|
||||
|
||||
CHECK(original_len > 0, "retransmission: the first segment went out");
|
||||
CHECK(same, "retransmission: the repeat carries the same bytes");
|
||||
}
|
||||
|
||||
static void scenario_bad_l4_checksum(void)
|
||||
{
|
||||
uip_stats_t chkerr_before;
|
||||
uint32_t seq_before;
|
||||
int len_before;
|
||||
|
||||
session_start(MSS_FULL);
|
||||
|
||||
chkerr_before = uip_stat.tcp.chkerr;
|
||||
len_before = stream_len;
|
||||
seq_before = cli_seq;
|
||||
|
||||
rx_headers[1] = RX_TAG_L4_CSUM_BAD;
|
||||
request_file(MSS_FULL);
|
||||
rx_headers[1] = 0;
|
||||
cli_seq = seq_before;
|
||||
|
||||
CHECK(uip_stat.tcp.chkerr == chkerr_before + 1,
|
||||
"bad checksum: the segment is counted as a checksum error");
|
||||
CHECK(stream_len == len_before,
|
||||
"bad checksum: the request draws no reply");
|
||||
|
||||
request_file(MSS_FULL);
|
||||
drain(MSS_FULL);
|
||||
report("bad checksum");
|
||||
|
||||
CHECK(body_offset() >= 0,
|
||||
"bad checksum control: the same request with a good flag is served");
|
||||
CHECK(first_body_mismatch() == -1,
|
||||
"bad checksum control: the file that follows is intact");
|
||||
}
|
||||
|
||||
int main(int argc, char **argv)
|
||||
{
|
||||
if (argc > 1 && !strcmp(argv[1], "-v"))
|
||||
verbose = 1;
|
||||
|
||||
printf("== httpd: accounting for the bytes actually sent ==\n");
|
||||
scenario_steady_window();
|
||||
scenario_shrinking_window();
|
||||
scenario_growing_window();
|
||||
scenario_rexmit_after_shrink();
|
||||
scenario_bad_l4_checksum();
|
||||
|
||||
printf("\n%s (%d failure%s)\n",
|
||||
failures ? "BENCH: FAILURES" : "BENCH: ALL PASS",
|
||||
failures, failures == 1 ? "" : "s");
|
||||
return failures ? 1 : 0;
|
||||
}
|
||||
@@ -0,0 +1,216 @@
|
||||
/*
|
||||
* test_page_json.c - the JSON generators of httpd/page_impl.c, fed by the
|
||||
* table code of rtl837x_port.c over the simulated engine. Both modules are
|
||||
* compiled unmodified, so what the web page shows is checked against what
|
||||
* the CLI configured, through the same bytes the switch would hold.
|
||||
*/
|
||||
#include <stdint.h>
|
||||
#include <stdio.h>
|
||||
#include <string.h>
|
||||
#include <stdlib.h>
|
||||
|
||||
#include "rtl837x_common.h"
|
||||
#include "rtl837x_regs.h"
|
||||
#include "rtl837x_port.h"
|
||||
#include "machine.h"
|
||||
#include "page_impl.h"
|
||||
#include "support.h"
|
||||
#include "hw_mock.h"
|
||||
|
||||
extern uint8_t outbuf[TCP_OUTBUF_SIZE];
|
||||
extern uint16_t slen;
|
||||
extern uint8_t vlan_names[VLAN_NAMES_SIZE];
|
||||
extern uint16_t vlan_ptr;
|
||||
extern uint16_t management_vlan;
|
||||
|
||||
/* The response body: what follows the blank line after the header. */
|
||||
static const char *body(void)
|
||||
{
|
||||
outbuf[slen < TCP_OUTBUF_SIZE ? slen : TCP_OUTBUF_SIZE - 1] = 0;
|
||||
const char *p = strstr((const char *)outbuf, "\r\n\r\n");
|
||||
return p ? p + 4 : (const char *)outbuf;
|
||||
}
|
||||
|
||||
static int has(const char *needle) { return strstr(body(), needle) != 0; }
|
||||
|
||||
static int count(const char *needle)
|
||||
{
|
||||
int n = 0;
|
||||
for (const char *p = body(); (p = strstr(p, needle)); p += strlen(needle))
|
||||
n++;
|
||||
return n;
|
||||
}
|
||||
|
||||
/* Brackets and braces balance outside strings, quotes pair up. */
|
||||
static int balanced(void)
|
||||
{
|
||||
int depth = 0, instr = 0;
|
||||
for (const char *p = body(); *p; p++) {
|
||||
if (*p == '"') { instr = !instr; continue; }
|
||||
if (instr) continue;
|
||||
if (*p == '{' || *p == '[') depth++;
|
||||
if (*p == '}' || *p == ']') depth--;
|
||||
if (depth < 0) return 0;
|
||||
}
|
||||
return depth == 0 && !instr;
|
||||
}
|
||||
|
||||
static void name_vlan(uint16_t vid, const char *name)
|
||||
{
|
||||
static const char h[] = "0123456789abcdef";
|
||||
vlan_names[vlan_ptr++] = h[(vid >> 8) & 0xf];
|
||||
vlan_names[vlan_ptr++] = h[(vid >> 4) & 0xf];
|
||||
vlan_names[vlan_ptr++] = h[vid & 0xf];
|
||||
while (*name) vlan_names[vlan_ptr++] = *name++;
|
||||
vlan_names[vlan_ptr++] = ' ';
|
||||
vlan_names[vlan_ptr] = 0;
|
||||
}
|
||||
|
||||
static void make_vlan(uint16_t vid, uint16_t members, uint16_t tagged)
|
||||
{
|
||||
vlan_settings.vlan = vid;
|
||||
vlan_settings.members = members;
|
||||
vlan_settings.tagged = tagged;
|
||||
vlan_create();
|
||||
}
|
||||
|
||||
static void t_vlan(void)
|
||||
{
|
||||
printf("[test] /vlan.json shows what vlan_create and pvid wrote\n");
|
||||
hw_reset(); out_reset(); vlan_ptr = 0; vlan_names[0] = 0;
|
||||
make_vlan(10, 0x0015, 0x0004);
|
||||
name_vlan(10, "lab");
|
||||
port_pvid_set(0, 10);
|
||||
port_pvid_set(2, 10);
|
||||
port_pvid_set(4, 20);
|
||||
send_vlan(10);
|
||||
char want[32];
|
||||
snprintf(want, sizeof want, "\"members\":\"0x%x\"", hw_vlan_word(10));
|
||||
CHECK(has(want), "members is the entry word");
|
||||
CHECK(has("\"name\":\"lab\""), "name comes from the name table");
|
||||
CHECK(has("\"pvid\":\"0x0005\""), "pvid mask has ports 1 and 3, not 5");
|
||||
CHECK(balanced(), "valid JSON");
|
||||
}
|
||||
|
||||
static void t_vlanlist(void)
|
||||
{
|
||||
printf("[test] /vlanlist lists the VLANs that exist\n");
|
||||
hw_reset(); out_reset(); vlan_ptr = 0; vlan_names[0] = 0;
|
||||
make_vlan(10, 0x0015, 0x0004);
|
||||
make_vlan(20, 0x00f0, 0x0000);
|
||||
make_vlan(4094, 0x0001, 0x0000);
|
||||
name_vlan(10, "lab");
|
||||
name_vlan(4094, "top");
|
||||
management_vlan = 10;
|
||||
send_vlanlist();
|
||||
CHECK(has("\"mgmt\":10"), "management VLAN");
|
||||
CHECK(has("{\"id\":10,\"name\":\"lab\"}"), "VLAN 10 with its name");
|
||||
CHECK(has("{\"id\":20,\"name\":\"\"}"), "VLAN 20 without a name");
|
||||
CHECK(has("{\"id\":4094,\"name\":\"top\"}"), "VLAN 4094 is the last one listed");
|
||||
CHECK(count("\"id\":") == 3, "and nothing else");
|
||||
CHECK(balanced(), "valid JSON");
|
||||
vlan_delete(20);
|
||||
send_vlanlist();
|
||||
CHECK(count("\"id\":") == 2 && !has("\"id\":20"), "a deleted VLAN drops out");
|
||||
management_vlan = 1;
|
||||
}
|
||||
|
||||
static const uint8_t base_mac[6] = { 0x00, 0x11, 0x22, 0x33, 0x44, 0x00 };
|
||||
|
||||
static void put(uint16_t idx, uint8_t last, uint16_t vid, uint8_t port, bool st)
|
||||
{
|
||||
uint8_t m[6];
|
||||
memcpy(m, base_mac, 6);
|
||||
m[5] = last;
|
||||
hw_l2_put(idx, m, vid, port, st);
|
||||
}
|
||||
|
||||
static void t_l2(void)
|
||||
{
|
||||
printf("[test] /l2.json walks the table\n");
|
||||
hw_reset(); out_reset();
|
||||
put(0x010, 0x01, 1, 4, false);
|
||||
put(0x020, 0x02, 2, CPU_PORT, true);
|
||||
put(0x8f0, 0x03, 1, 7, false);
|
||||
send_l2(0);
|
||||
CHECK(count("\"mac\":") == 4, "three entries, and the first one again: that repeat is how the page sees the wrap");
|
||||
CHECK(count("\"idx\":\"0010\"") == 2 && strstr(body(), "\"idx\":\"0010\"}]"), "the repeat closes the list");
|
||||
CHECK(has("{\"vlan\":\"001\",\"mac\":\"00:11:22:33:44:01\",\"type\":\"l\",\"port\":4,\"lag\":0,\"idx\":\"0010\"}"), "learned entry, VLAN, port and index");
|
||||
CHECK(has("\"mac\":\"00:11:22:33:44:02\",\"type\":\"s\",\"port\":9"), "static entry on the CPU port");
|
||||
CHECK(has("\"idx\":\"08f0\""), "an index above 0x800 keeps its high bit");
|
||||
CHECK(balanced(), "valid JSON");
|
||||
|
||||
out_reset();
|
||||
send_l2(0x100);
|
||||
const char *b = body();
|
||||
CHECK(strstr(b, "44:03") && strstr(b, "44:03") < strstr(b, "44:01"), "starting at 0x100 lists 0x8f0 first and wraps");
|
||||
CHECK(count("\"mac\":") == 4 && count("44:03") == 2, "and still finds all three before repeating 0x8f0");
|
||||
}
|
||||
|
||||
/* Walk the table the way the page does: ask for the index after the last
|
||||
* entry of each page until an index comes back that was seen before. */
|
||||
static void t_l2_page(void)
|
||||
{
|
||||
printf("[test] /l2.json pages the way the web page walks it\n");
|
||||
hw_reset(); out_reset();
|
||||
for (int i = 0; i < 40; i++)
|
||||
put(0x100 + i * 3, 0x10 + i, 1, i % 8, false);
|
||||
uint8_t seen[4096 / 8] = { 0 };
|
||||
int pages = 0, unique = 0, wrapped = 0, idx = 0, biggest = 0;
|
||||
while (!wrapped && pages < 10) {
|
||||
out_reset();
|
||||
send_l2(idx);
|
||||
pages++;
|
||||
if (slen > TCP_OUTBUF_SIZE || !balanced())
|
||||
break;
|
||||
if ((int)slen > biggest)
|
||||
biggest = slen;
|
||||
int n = 0, last = -1;
|
||||
for (const char *p = body(); (p = strstr(p, "\"idx\":\"")); p += 7) {
|
||||
int v = (int)strtol(p + 7, 0, 16);
|
||||
n++;
|
||||
if (seen[v >> 3] & (1 << (v & 7))) { wrapped = 1; break; }
|
||||
seen[v >> 3] |= 1 << (v & 7);
|
||||
unique++;
|
||||
last = v;
|
||||
}
|
||||
if (!n)
|
||||
break;
|
||||
idx = last + 1;
|
||||
}
|
||||
CHECK(wrapped, "the walk ends on a repeated index");
|
||||
CHECK(unique == 40, "and has visited every entry once");
|
||||
CHECK(pages == 2, "in two pages");
|
||||
CHECK(biggest <= TCP_OUTBUF_SIZE, "no page outgrows outbuf");
|
||||
CHECK(balanced(), "every page is valid JSON");
|
||||
}
|
||||
|
||||
static void t_status_counters(void)
|
||||
{
|
||||
printf("[test] /status.json prints 64-bit packet counters in full\n");
|
||||
hw_reset(); out_reset();
|
||||
hw_counter_set(0, STAT_COUNTER_TX_PKTS, 0x100000005ULL);
|
||||
hw_counter_set(0, STAT_COUNTER_RX_PKTS, 0x00000000000000ffULL);
|
||||
hw_counter_set(1, STAT_COUNTER_TX_PKTS, 0x00000001ffffffffULL);
|
||||
send_status();
|
||||
CHECK(has("\"txG\":\"0x100000005\""), "a counter past 2^32 keeps the zeros of its low word");
|
||||
CHECK(has("\"rxG\":\"0xff\""), "a small counter stays short");
|
||||
CHECK(has("\"txG\":\"0x1ffffffff\""), "a low word of all ones");
|
||||
out_reset();
|
||||
send_counters(1);
|
||||
CHECK(has("\"0x100000005\"") && has("\"0x0\""), "/counters.json prints them the same way");
|
||||
CHECK(slen <= TCP_OUTBUF_SIZE, "fits the buffer");
|
||||
CHECK(balanced(), "valid JSON");
|
||||
}
|
||||
|
||||
int main(void)
|
||||
{
|
||||
printf("== httpd/page_impl.c JSON tests ==\n");
|
||||
t_vlan();
|
||||
t_vlanlist();
|
||||
t_l2();
|
||||
t_l2_page();
|
||||
t_status_counters();
|
||||
printf("\n%d checks, %d failed\n", tests_run, tests_failed);
|
||||
return tests_failed ? 1 : 0;
|
||||
}
|
||||
@@ -0,0 +1,181 @@
|
||||
/*
|
||||
* test_port_tables.c - rtl837x_port.c against the simulated table engine.
|
||||
*
|
||||
* What the firmware writes to the VLAN and L2 tables is checked against an
|
||||
* independent statement of the entry layout (hw_mock.h), and what it reads
|
||||
* back is checked against what it wrote. The module is compiled unmodified.
|
||||
*/
|
||||
#include <stdint.h>
|
||||
#include <stdio.h>
|
||||
#include <string.h>
|
||||
|
||||
#include "rtl837x_common.h"
|
||||
#include "rtl837x_regs.h"
|
||||
#include "rtl837x_port.h"
|
||||
#include "machine.h"
|
||||
#include "support.h"
|
||||
#include "hw_mock.h"
|
||||
|
||||
extern struct machine_runtime machine_detected;
|
||||
|
||||
/* VLAN entry as vlan_create() lays it out: bit 25 valid, bits 0-9 members,
|
||||
* bits 10-19 the untag set, which is every port not in the tagged set (the
|
||||
* bit of a non-member is meaningless to the switch and stays set). */
|
||||
static uint32_t vlan_word(uint16_t members, uint16_t tagged)
|
||||
{
|
||||
return (1u << 25) | ((uint32_t)(~tagged & 0x3ff) << 10) | members;
|
||||
}
|
||||
|
||||
static void t_vlan_roundtrip(void)
|
||||
{
|
||||
printf("[test] VLAN entry: create, read back, delete\n");
|
||||
hw_reset(); out_reset();
|
||||
vlan_settings.vlan = 10;
|
||||
vlan_settings.members = 0x0015; /* ports 1, 3, 5 */
|
||||
vlan_settings.tagged = 0x0004; /* port 3 tagged */
|
||||
vlan_create();
|
||||
/* the CPU port is always a tagged member */
|
||||
CHECK(hw_vlan_word(10) == vlan_word(0x0015 | 0x0200, 0x0004 | 0x0200), "vlan 10 entry has the documented layout");
|
||||
CHECK(hw_vlan_word(11) == 0, "the neighbouring entry is untouched");
|
||||
|
||||
uint32_t w = hw_vlan_word(10);
|
||||
CHECK(vlan_get(10) == 0, "vlan_get finds it");
|
||||
CHECK(((uint32_t)sfr_data[0] << 24 | (uint32_t)sfr_data[1] << 16 | (uint32_t)sfr_data[2] << 8 | sfr_data[3]) == w,
|
||||
"and returns the word that was written");
|
||||
CHECK(sfr_data[0] & 0x02, "with the valid bit the JSON generators test for");
|
||||
|
||||
unsigned long r = hw_reads;
|
||||
CHECK(vlan_get(4095) == -1, "VLAN 4095 is refused");
|
||||
CHECK(hw_reads == r, "without touching the engine");
|
||||
|
||||
vlan_delete(10);
|
||||
CHECK(hw_vlan_word(10) == 0, "vlan_delete clears the entry");
|
||||
vlan_delete(0);
|
||||
vlan_delete(4095);
|
||||
CHECK(hw_vlan_word(0) == 0 && hw_vlan_word(4095) == 0, "VLAN 0 and 4095 cannot be deleted");
|
||||
}
|
||||
|
||||
static void t_vlan_rtl8372(void)
|
||||
{
|
||||
printf("[test] VLAN entry on an RTL8372: ports 0-2 are not there\n");
|
||||
hw_reset();
|
||||
machine_detected.isRTL8373 = 0;
|
||||
vlan_settings.vlan = 20;
|
||||
vlan_settings.members = 0x03ff;
|
||||
vlan_settings.tagged = 0x0000;
|
||||
vlan_create();
|
||||
uint32_t w = hw_vlan_word(20);
|
||||
CHECK(((w >> 10) & 0x7) == 0, "the untagged bits of ports 0-2 stay clear");
|
||||
CHECK((w & 0x3ff) == 0x03ff, "the member bits are what was asked for");
|
||||
machine_detected.isRTL8373 = 1;
|
||||
}
|
||||
|
||||
static void t_pvid(void)
|
||||
{
|
||||
printf("[test] PVID: two ports share one register\n");
|
||||
hw_reset();
|
||||
port_pvid_set(2, 100);
|
||||
port_pvid_set(3, 4094);
|
||||
CHECK(port_pvid_get(2) == 100, "even port reads back");
|
||||
CHECK(port_pvid_get(3) == 4094, "odd port reads back");
|
||||
port_pvid_set(3, 7);
|
||||
CHECK(port_pvid_get(2) == 100, "writing the odd port leaves the even one alone");
|
||||
CHECK(port_pvid_get(3) == 7, "and takes the new value");
|
||||
port_pvid_set(2, 4001);
|
||||
CHECK(port_pvid_get(3) == 7, "writing the even port leaves the odd one alone");
|
||||
CHECK(port_pvid_get(8) == 0, "an untouched port reads 0");
|
||||
}
|
||||
|
||||
static void t_l2mc(void)
|
||||
{
|
||||
printf("[test] static multicast entry steering a group to the CPU\n");
|
||||
hw_reset();
|
||||
port_l2mc_set(0x0e, 2, PMASK_CPU);
|
||||
const uint8_t mac[6] = { 0x01, 0x80, 0xc2, 0x00, 0x00, 0x0e };
|
||||
const struct hw_l2 *e = hw_l2_find(mac, 2);
|
||||
CHECK(e != 0, "the entry exists for 01:80:c2:00:00:0e in VLAN 2");
|
||||
if (e) {
|
||||
uint8_t m[6]; uint16_t vid, pm; bool valid, st, mc;
|
||||
hw_l2_decode(e, m, &vid, &pm, &valid, &st, &mc);
|
||||
CHECK(valid, "valid");
|
||||
CHECK(mc, "multicast");
|
||||
CHECK(pm == PMASK_CPU, "member mask is the CPU port alone");
|
||||
}
|
||||
port_l2mc_set(0x0e, 2, PMASK_CPU | 0x0003);
|
||||
e = hw_l2_find(mac, 2);
|
||||
CHECK(hw_l2_count() == 1, "writing the same key again replaces, not adds");
|
||||
if (e) {
|
||||
uint8_t m[6]; uint16_t vid, pm; bool valid, st, mc;
|
||||
hw_l2_decode(e, m, &vid, &pm, &valid, &st, &mc);
|
||||
CHECK(pm == (PMASK_CPU | 0x0003), "with the new mask, low bits included");
|
||||
}
|
||||
}
|
||||
|
||||
static void t_static_mgmt(void)
|
||||
{
|
||||
printf("[test] the management MAC pinned to the CPU port\n");
|
||||
hw_reset();
|
||||
uint8_t mac[6] = { 0x02, 0x11, 0x22, 0x33, 0x44, 0x55 };
|
||||
port_l2_static_mgmt(mac, 2, false);
|
||||
const struct hw_l2 *e = hw_l2_find(mac, 2);
|
||||
CHECK(e != 0, "entry present in VLAN 2");
|
||||
if (e) {
|
||||
uint8_t m[6]; uint16_t vid, p; bool valid, st, mc;
|
||||
hw_l2_decode(e, m, &vid, &p, &valid, &st, &mc);
|
||||
CHECK(valid && st && !mc, "valid, static, unicast");
|
||||
CHECK(p == CPU_PORT, "on the CPU port");
|
||||
}
|
||||
port_l2_static_mgmt(mac, 0, false);
|
||||
CHECK(hw_l2_find(mac, 1) != 0, "management VLAN 0 means VLAN 1");
|
||||
port_l2_static_mgmt(mac, 2, true);
|
||||
CHECK(hw_l2_find(mac, 2) == 0, "removal deletes the VLAN 2 entry");
|
||||
CHECK(hw_l2_find(mac, 1) != 0, "and leaves the other one");
|
||||
port_l2_static_mgmt(mac, 3, true);
|
||||
CHECK(hw_l2_count() == 1, "removing an entry that is not there changes nothing");
|
||||
}
|
||||
|
||||
static void t_flush(void)
|
||||
{
|
||||
printf("[test] flushing a port forgets its learned entries only\n");
|
||||
hw_reset();
|
||||
const uint8_t a[6] = { 0x00, 0x11, 0x22, 0x33, 0x44, 0x01 };
|
||||
const uint8_t b[6] = { 0x00, 0x11, 0x22, 0x33, 0x44, 0x02 };
|
||||
const uint8_t c[6] = { 0x00, 0x11, 0x22, 0x33, 0x44, 0x03 };
|
||||
hw_l2_put(0x010, a, 1, 4, false);
|
||||
hw_l2_put(0x020, b, 1, 4, true);
|
||||
hw_l2_put(0x030, c, 1, 5, false);
|
||||
port_l2_forget_port(4);
|
||||
CHECK(hw_l2_find(a, 1) == 0, "learned entry on port 5 (index 4) gone");
|
||||
CHECK(hw_l2_find(b, 1) != 0, "static entry on the same port stays");
|
||||
CHECK(hw_l2_find(c, 1) != 0, "entry on another port stays");
|
||||
port_l2_forget();
|
||||
CHECK(hw_l2_find(c, 1) == 0 && hw_l2_find(b, 1) != 0, "forget-all drops every learned entry and keeps the static one");
|
||||
}
|
||||
|
||||
static void t_lag(void)
|
||||
{
|
||||
printf("[test] trunk membership and its hash seed\n");
|
||||
hw_reset();
|
||||
port_lag_members_set(1, 0x0180);
|
||||
CHECK(port_lag_members_get(1) == 0x0180, "members read back");
|
||||
CHECK(port_lag_of(7) == 1 && port_lag_of(8) == 1, "both ports belong to group 2");
|
||||
CHECK(port_lag_of(0) == PORT_LAG_NONE, "a port outside is in no group");
|
||||
CHECK((hw_reg_get(RTL837X_TRK_HASH_CTRL_BASE + 4) & 0xff) == LAG_HASH_DEFAULT, "an unset hash gets the default");
|
||||
hw_reg_set(RTL837X_TRK_HASH_CTRL_BASE + 8, 0x06);
|
||||
port_lag_members_set(2, 0x0060);
|
||||
CHECK((hw_reg_get(RTL837X_TRK_HASH_CTRL_BASE + 8) & 0xff) == 0x06, "a hash already set is kept");
|
||||
}
|
||||
|
||||
int main(void)
|
||||
{
|
||||
printf("== rtl837x_port.c table tests ==\n");
|
||||
t_vlan_roundtrip();
|
||||
t_vlan_rtl8372();
|
||||
t_pvid();
|
||||
t_l2mc();
|
||||
t_static_mgmt();
|
||||
t_flush();
|
||||
t_lag();
|
||||
printf("\n%d checks, %d failed\n", tests_run, tests_failed);
|
||||
return tests_failed ? 1 : 0;
|
||||
}
|
||||
@@ -95,6 +95,9 @@
|
||||
#endif /* UIP_CONF_IPV6 */
|
||||
|
||||
#include "../rtl837x_common.h"
|
||||
#include "../rtl837x_regs.h"
|
||||
|
||||
extern __xdata uint8_t rx_headers[16];
|
||||
|
||||
/*---------------------------------------------------------------------------*/
|
||||
/* Variable definitions. */
|
||||
@@ -1120,6 +1123,12 @@ uip_process(u8_t flag) __banked
|
||||
goto drop;
|
||||
}
|
||||
#else /* UIP_UDP_CHECKSUMS */
|
||||
if (rx_headers[1] & RX_TAG_L4_CSUM_BAD) {
|
||||
UIP_STAT(++uip_stat.udp.drop);
|
||||
UIP_STAT(++uip_stat.udp.chkerr);
|
||||
UIP_LOG("udp: bad checksum.");
|
||||
goto drop;
|
||||
}
|
||||
uip_len = uip_len - UIP_IPUDPH_LEN;
|
||||
#endif /* UIP_UDP_CHECKSUMS */
|
||||
|
||||
@@ -1200,15 +1209,12 @@ uip_process(u8_t flag) __banked
|
||||
|
||||
/* Start of TCP input header processing code. */
|
||||
|
||||
// BUG: We need to reimplement this with RTL837x TCP checksum checking
|
||||
// i.e. figure out which bit in the RX-tag states the checksum is correct
|
||||
// if(uip_tcpchksum() != 0xffff) { /* Compute and check the TCP
|
||||
// checksum. */
|
||||
// UIP_STAT(++uip_stat.tcp.drop);
|
||||
// UIP_STAT(++uip_stat.tcp.chkerr);
|
||||
// UIP_LOG("tcp: bad checksum.");
|
||||
// goto drop;
|
||||
// }
|
||||
if (rx_headers[1] & RX_TAG_L4_CSUM_BAD) {
|
||||
UIP_STAT(++uip_stat.tcp.drop);
|
||||
UIP_STAT(++uip_stat.tcp.chkerr);
|
||||
UIP_LOG("tcp: bad checksum.");
|
||||
goto drop;
|
||||
}
|
||||
|
||||
|
||||
/* Demultiplex this segment. */
|
||||
|
||||