mirror of
https://github.com/logicog/RTLPlayground.git
synced 2026-10-29 17:54:14 +08:00
Merge branch 'main' into add-device-ONT-S207CW-62TS-SE
This commit is contained in:
commit
36459fdd76
17 files changed
+697
-40
No files matched your search
+11
-5
@@ -434,6 +434,14 @@ static void cmd_error(__code const char *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
|
// 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
|
// the current one with a bare "mac"). Refuses blank/multicast/locally
|
||||||
// administered/all-zero-OUI addresses so the running MAC stays reachable, and
|
// administered/all-zero-OUI addresses so the running MAC stays reachable, and
|
||||||
@@ -627,7 +635,7 @@ void parse_vlan(void)
|
|||||||
*vlan_str++ = hex[(vlan_settings.vlan >> 4) & 0xf] ;
|
*vlan_str++ = hex[(vlan_settings.vlan >> 4) & 0xf] ;
|
||||||
*vlan_str++ = hex[vlan_settings.vlan & 0xf];
|
*vlan_str++ = hex[vlan_settings.vlan & 0xf];
|
||||||
while(!cmd_is_space_or_nul(pos)) {
|
while(!cmd_is_space_or_nul(pos)) {
|
||||||
uint8_t c = cmd_buffer[pos++];
|
uint8_t c = name_char(cmd_buffer[pos++]);
|
||||||
write_char(c);
|
write_char(c);
|
||||||
*vlan_str++ = c;
|
*vlan_str++ = c;
|
||||||
}
|
}
|
||||||
@@ -889,7 +897,7 @@ void parse_port(void)
|
|||||||
} else if (cmd_compare(2, "name")) {
|
} else if (cmd_compare(2, "name")) {
|
||||||
uint8_t i = 0;
|
uint8_t i = 0;
|
||||||
while ( (i < PORT_NAME_SIZE-1) && (cmd_buffer[cmd_words_b[3] + i] != NUL) ) {
|
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++;
|
i++;
|
||||||
}
|
}
|
||||||
port_names[phy_settings.port][i] = NUL;
|
port_names[phy_settings.port][i] = NUL;
|
||||||
@@ -1831,9 +1839,7 @@ void cmd_parser(void) __banked
|
|||||||
uint8_t c = *hp++;
|
uint8_t c = *hp++;
|
||||||
if (c == NUL || c == '\r' || c == '\n')
|
if (c == NUL || c == '\r' || c == '\n')
|
||||||
break;
|
break;
|
||||||
if (c < 0x20 || c > 0x7e || c == '"' || c == '\\')
|
*dst++ = name_char(c);
|
||||||
c = '.';
|
|
||||||
*dst++ = c;
|
|
||||||
}
|
}
|
||||||
*dst = NUL;
|
*dst = NUL;
|
||||||
} else {
|
} else {
|
||||||
|
|||||||
@@ -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.
|
||||||
|
|
||||||
Binary file not shown.
|
After Width: | Height: | Size: 907 KiB |
Binary file not shown.
|
After Width: | Height: | Size: 923 KiB |
Binary file not shown.
|
After Width: | Height: | Size: 290 KiB |
Binary file not shown.
|
After Width: | Height: | Size: 360 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 |
|
| 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 |
|
| 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 |
|
| 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-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-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 |
|
| Hisource | Hi-K0801WS | No | [PCB-KO801W-V2.0](devices/HI-K0801WS.md) | | 8 + 1 |
|
||||||
@@ -21,11 +22,13 @@ 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 |
|
| 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 | 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 | 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| 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| 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 |
|
| MokerLink| POE-2G080110GS | No | [2M-PCB51-ML-V1.1](devices/2M-PCB51-ML-V1_1.md) | 4M | 8 + 1 |
|
||||||
| Binardat | 2G06-04210GSM | No | [PCB-SWTG024AS-A-2.0.1](devices/ONT-S207CW-62TS-SE.md) | 0.5M | 4 + 2 |
|
| Binardat | 2G06-04210GSM | No | [PCB-SWTG024AS-A-2.0.1](devices/ONT-S207CW-62TS-SE.md) | 0.5M | 4 + 2 |
|
||||||
| ONT | ONT-S207CW-62TS-SE | No | [PCB-SWTG024AS-A-2.0.1](devices/ONT-S207CW-62TS-SE.md) | 0.5M | 4 + 2 |
|
| ONT | ONT-S207CW-62TS-SE | No | [PCB-SWTG024AS-A-2.0.1](devices/ONT-S207CW-62TS-SE.md) | 0.5M | 4 + 2 |
|
||||||
|
| 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-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 |
|
| 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 |
|
| SiscoLink | SL-G0402F | No | [PCB-K0402WS-V3.0](devices/PCB-K0402WS-V3.0.md) | | 4 + 2 |
|
||||||
@@ -35,7 +38,6 @@ The following devices have been tested and are fully working:
|
|||||||
| Xikestore| SKS3200M-4GPY2XF| Yes | [SWTG024AS-v1.0](devices/SWTG024AS.md) | | 4 + 2 |
|
| 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 |
|
| 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 |
|
| 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
|
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
|
the machine target by PCB revision. The ML/non-ML or managed/unmanaged label
|
||||||
|
|||||||
+9
-1
@@ -533,13 +533,18 @@ TABS.forEach(function(tb){
|
|||||||
});
|
});
|
||||||
$("burger").addEventListener("click",function(){$("nav").classList.toggle("open")});
|
$("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}
|
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.start=function(){if(this.on)return;this.on=true;this.tick()};
|
||||||
Poller.prototype.stop=function(){this.on=false;clearTimeout(this.t)};
|
Poller.prototype.stop=function(){this.on=false;clearTimeout(this.t)};
|
||||||
Poller.prototype.tick=function(){
|
Poller.prototype.tick=function(){
|
||||||
var self=this;
|
var self=this;
|
||||||
if(!self.on)return;
|
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); });
|
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"],
|
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"]];
|
["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 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){
|
function speedLabel(s){
|
||||||
if(s==="c_auto")return t("c_auto");
|
if(s==="c_auto")return t("c_auto");
|
||||||
return s.replace(" full"," "+t("c_full")).replace(" half"," "+t("c_half"));
|
return s.replace(" full"," "+t("c_full")).replace(" half"," "+t("c_half"));
|
||||||
@@ -785,6 +791,8 @@ function portsStatus(){
|
|||||||
S.ports.forEach(function(p){
|
S.ports.forEach(function(p){
|
||||||
var i=p.portNum-1,el=$("plink"+i);
|
var i=p.portNum-1,el=$("plink"+i);
|
||||||
if(el)el.innerHTML=linkBadge(p);
|
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){
|
function applyPort(i){
|
||||||
|
|||||||
+24
-19
@@ -37,6 +37,7 @@ __xdata uint8_t outbuf[TCP_OUTBUF_SIZE];
|
|||||||
__xdata uint8_t entry;
|
__xdata uint8_t entry;
|
||||||
__xdata uint16_t slen;
|
__xdata uint16_t slen;
|
||||||
__xdata uint16_t o_idx;
|
__xdata uint16_t o_idx;
|
||||||
|
__xdata uint16_t sent_len;
|
||||||
__xdata uint16_t len_left;
|
__xdata uint16_t len_left;
|
||||||
__xdata uint16_t cont_len;
|
__xdata uint16_t cont_len;
|
||||||
__xdata uint32_t cont_addr;
|
__xdata uint32_t cont_addr;
|
||||||
@@ -347,10 +348,11 @@ __xdata uint8_t *scan_header(__xdata uint8_t * __xdata p)
|
|||||||
if (is_word_x(session, session_id)) {
|
if (is_word_x(session, session_id)) {
|
||||||
authenticated = 1;
|
authenticated = 1;
|
||||||
last_session_use = now;
|
last_session_use = now;
|
||||||
} else
|
} else {
|
||||||
dbg_string("Invalid session cookie!\n");
|
dbg_string("Invalid session cookie!\n");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
}
|
||||||
return p;
|
return p;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -559,7 +561,7 @@ static void handle_config_fragment(__xdata uint8_t *p)
|
|||||||
__xdata uint16_t frag_len;
|
__xdata uint16_t frag_len;
|
||||||
uint8_t taken;
|
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) {
|
if (pre_acc + frag_len >= CONFIG_UPLOAD_BUF) {
|
||||||
print_string("Configuration too large, aborting.\n");
|
print_string("Configuration too large, aborting.\n");
|
||||||
config_upload = 0;
|
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 struct httpd_state * __xdata s = &(uip_conn->appstate);
|
||||||
__xdata uint16_t frag_len, payload_start;
|
__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) {
|
if (pre_acc + frag_len >= CONFIG_UPLOAD_BUF) {
|
||||||
print_string("Firmware upload header too large, aborting.\n");
|
print_string("Firmware upload header too large, aborting.\n");
|
||||||
config_upload = 0;
|
config_upload = 0;
|
||||||
@@ -684,7 +686,7 @@ static uint8_t post_body_take(__xdata uint8_t *p)
|
|||||||
send_bad_request();
|
send_bad_request();
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
have = uip_len - (p - uip_appdata);
|
have = uip_len - (p - (__xdata uint8_t *)uip_appdata);
|
||||||
if (have >= content_length) {
|
if (have >= content_length) {
|
||||||
p[content_length] = NUL;
|
p[content_length] = NUL;
|
||||||
return 1;
|
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)
|
void httpd_appcall(void)
|
||||||
{
|
{
|
||||||
__xdata struct httpd_state * __xdata s = &(uip_conn->appstate);
|
__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) {
|
} else if (uip_acked() && s->tstate == TSTATE_TX) {
|
||||||
dbg_string("ACK\n");
|
dbg_string("ACK\n");
|
||||||
if (slen > uip_mss()) {
|
if (slen > sent_len) {
|
||||||
slen -= uip_mss();
|
slen -= sent_len;
|
||||||
o_idx += uip_mss();
|
o_idx += sent_len;
|
||||||
} else {
|
} else {
|
||||||
slen = 0;
|
slen = 0;
|
||||||
}
|
}
|
||||||
@@ -872,11 +881,11 @@ void httpd_appcall(void)
|
|||||||
|
|
||||||
if (slen > uip_mss()) {
|
if (slen > uip_mss()) {
|
||||||
dbg_string("Sending A: "); dbg_short(slen); dbg_char('\n');
|
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;
|
s->tstate = TSTATE_TX;
|
||||||
} else if (slen > 0) {
|
} else if (slen > 0) {
|
||||||
dbg_string("Sending B: "); dbg_short(slen); dbg_char('\n');
|
dbg_string("Sending B: "); dbg_short(slen); dbg_char('\n');
|
||||||
uip_send(outbuf + o_idx, slen);
|
tx_send(slen);
|
||||||
s->tstate = TSTATE_TX;
|
s->tstate = TSTATE_TX;
|
||||||
} else if (cont_len) {
|
} else if (cont_len) {
|
||||||
dbg_string("CONT cont_len: "); dbg_short(cont_len);
|
dbg_string("CONT cont_len: "); dbg_short(cont_len);
|
||||||
@@ -887,7 +896,7 @@ void httpd_appcall(void)
|
|||||||
flash_region.len = slen;
|
flash_region.len = slen;
|
||||||
flash_read_bulk(outbuf);
|
flash_read_bulk(outbuf);
|
||||||
o_idx = 0;
|
o_idx = 0;
|
||||||
uip_send(outbuf + o_idx, slen);
|
tx_send(slen);
|
||||||
cont_len -= slen;
|
cont_len -= slen;
|
||||||
cont_addr += slen;
|
cont_addr += slen;
|
||||||
s->tstate = TSTATE_TX;
|
s->tstate = TSTATE_TX;
|
||||||
@@ -1045,24 +1054,20 @@ do_send:
|
|||||||
o_idx = 0;
|
o_idx = 0;
|
||||||
if (slen > uip_mss()) {
|
if (slen > uip_mss()) {
|
||||||
dbg_string("Sending a: "); dbg_short(slen); dbg_char('\n');
|
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");
|
dbg_string("Sending a done\n");
|
||||||
} else {
|
} else {
|
||||||
dbg_string("Sending b: "); dbg_short(slen); dbg_char('\n');
|
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");
|
dbg_string("Sending b done\n");
|
||||||
}
|
}
|
||||||
s->tstate = TSTATE_TX;
|
s->tstate = TSTATE_TX;
|
||||||
} else if (uip_rexmit()) { // Connection established, need to rexmit?
|
} else if (uip_rexmit()) { // Connection established, need to rexmit?
|
||||||
dbg_string("RETRANSMIT requested\n");
|
dbg_string("RETRANSMIT requested\n");
|
||||||
if (slen > uip_mss()) {
|
if (sent_len) {
|
||||||
dbg_string("Sending C: "); dbg_short(slen); dbg_char('\n');
|
dbg_string("Sending C: "); dbg_short(sent_len); dbg_char('\n');
|
||||||
uip_send(outbuf + o_idx, uip_mss());
|
tx_send(sent_len);
|
||||||
dbg_string("Sending C done\n");
|
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;
|
s->tstate = TSTATE_TX;
|
||||||
uip_len = 0;
|
uip_len = 0;
|
||||||
|
|||||||
+23
-11
@@ -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.
|
// Convert uint8_t to ascii HEX char.
|
||||||
void byte_to_html(uint8_t val)
|
void byte_to_html(uint8_t val)
|
||||||
{
|
{
|
||||||
@@ -198,8 +213,8 @@ void send_sfp_info(uint8_t sfp)
|
|||||||
if (i < 20 || i >= 60 || (i >= 36 && i < 40)) // Skip Non-ASCII codes
|
if (i < 20 || i >= 60 || (i >= 36 && i < 40)) // Skip Non-ASCII codes
|
||||||
continue;
|
continue;
|
||||||
uint8_t c = sfp_buf[i & 0xf];
|
uint8_t c = sfp_buf[i & 0xf];
|
||||||
if (c && c != 0xa0) // a0 is the byte read from a non-existant I2C EEPROM
|
if (c)
|
||||||
char_to_html(c);
|
json_char_to_html(c);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -293,7 +308,7 @@ void send_vlan(uint16_t vlan)
|
|||||||
dbg_string("VLAN has no name\n");
|
dbg_string("VLAN has no name\n");
|
||||||
} else {
|
} else {
|
||||||
while(vlan_names[n] && vlan_names[n] != ' ')
|
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");
|
slen += strtox(outbuf + slen, "\",\"pvid\":\"0x");
|
||||||
uint16_t pvid_mask = 0;
|
uint16_t pvid_mask = 0;
|
||||||
@@ -825,7 +840,7 @@ void send_status(void)
|
|||||||
itoa_html(i);
|
itoa_html(i);
|
||||||
slen += strtox(outbuf + slen, ",\"name\":\"");
|
slen += strtox(outbuf + slen, ",\"name\":\"");
|
||||||
for (uint8_t j = 0; j < PORT_NAME_SIZE && port_names[i][j]; j++) {
|
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, "\"");
|
slen += strtox(outbuf + slen, "\"");
|
||||||
|
|
||||||
@@ -864,14 +879,11 @@ void send_status(void)
|
|||||||
sfp_send_data(sfp, 238, 1);
|
sfp_send_data(sfp, 238, 1);
|
||||||
}
|
}
|
||||||
slen += strtox(outbuf + slen,"\",\"sfp_vendor\":\"");
|
slen += strtox(outbuf + slen,"\",\"sfp_vendor\":\"");
|
||||||
for (uint8_t s = 0; s < 16 && sfp_module_vendor[sfp][s]; s++)
|
str_x_to_html(sfp_module_vendor[sfp]);
|
||||||
outbuf[slen++] = sfp_module_vendor[sfp][s];
|
|
||||||
slen += strtox(outbuf + slen,"\",\"sfp_model\":\"");
|
slen += strtox(outbuf + slen,"\",\"sfp_model\":\"");
|
||||||
for (uint8_t s = 0; s < 16 && sfp_module_model[sfp][s]; s++)
|
str_x_to_html(sfp_module_model[sfp]);
|
||||||
outbuf[slen++] = sfp_module_model[sfp][s];
|
|
||||||
slen += strtox(outbuf + slen,"\",\"sfp_serial\":\"");
|
slen += strtox(outbuf + slen,"\",\"sfp_serial\":\"");
|
||||||
for (uint8_t s = 0; s < 16 && sfp_module_serial[sfp][s]; s++)
|
str_x_to_html(sfp_module_serial[sfp]);
|
||||||
outbuf[slen++] = sfp_module_serial[sfp][s];
|
|
||||||
slen += strtox(outbuf + slen,"\",\"sfp_los\":");
|
slen += strtox(outbuf + slen,"\",\"sfp_los\":");
|
||||||
if (machine.sfp_port[sfp].pin_los == GPIO_NA) {
|
if (machine.sfp_port[sfp].pin_los == GPIO_NA) {
|
||||||
slen += strtox(outbuf + slen,"null");
|
slen += strtox(outbuf + slen,"null");
|
||||||
@@ -1043,7 +1055,7 @@ void send_vlanlist(void)
|
|||||||
n = vlan_name(i);
|
n = vlan_name(i);
|
||||||
if (n != 0xffff) {
|
if (n != 0xffff) {
|
||||||
while (vlan_names[n] && vlan_names[n] != ' ')
|
while (vlan_names[n] && vlan_names[n] != ' ')
|
||||||
char_to_html(vlan_names[n++]);
|
json_char_to_html(vlan_names[n++]);
|
||||||
}
|
}
|
||||||
|
|
||||||
slen += strtox(outbuf + slen, "\"}");
|
slen += strtox(outbuf + slen, "\"}");
|
||||||
|
|||||||
@@ -1289,7 +1289,89 @@ __code const struct machine machine = {
|
|||||||
0x1a, 0x19, 0x1d, 0x1e, 0x1c,
|
0x1a, 0x19, 0x1d, 0x1e, 0x1c,
|
||||||
0x1d, 0x20, 0x21 },
|
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
|
#else
|
||||||
#error "Please select a machine type in machine.h"
|
#error "Please select a machine type in machine.h"
|
||||||
#endif
|
#endif
|
||||||
@@ -53,6 +53,8 @@
|
|||||||
// #define MACHINE_FG_8GT_1SX
|
// #define MACHINE_FG_8GT_1SX
|
||||||
// #define MACHINE_LIANGUO_HYWS_SGT0108S
|
// #define MACHINE_LIANGUO_HYWS_SGT0108S
|
||||||
// #define MACHINE_POE_2G080110GS
|
// #define MACHINE_POE_2G080110GS
|
||||||
|
// #define MACHINE_PB_2132
|
||||||
|
// #define MACHINE_HASIVO_S600W_4GT_2XGT_SE
|
||||||
|
|
||||||
typedef struct {
|
typedef struct {
|
||||||
// GPIO pins for SDA/SCL
|
// GPIO pins for SDA/SCL
|
||||||
|
|||||||
@@ -88,7 +88,12 @@ bool sfp_read_field(__xdata char *dst, uint8_t sfp, uint8_t start, uint8_t lengt
|
|||||||
return false;
|
return false;
|
||||||
|
|
||||||
dst[length] = NUL;
|
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] == ' ')
|
while (length > 0 && dst[--length] == ' ')
|
||||||
dst[length] = NUL;
|
dst[length] = NUL;
|
||||||
|
|||||||
+8
-2
@@ -11,7 +11,7 @@
|
|||||||
CC = gcc
|
CC = gcc
|
||||||
SRCROOT = ..
|
SRCROOT = ..
|
||||||
# -include shim first so __xdata/__code/__banked vanish before any firmware header.
|
# -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 \
|
-Wno-pointer-sign \
|
||||||
-DRTLP_HOST_TEST \
|
-DRTLP_HOST_TEST \
|
||||||
-fsanitize=address,undefined -fno-omit-frame-pointer \
|
-fsanitize=address,undefined -fno-omit-frame-pointer \
|
||||||
@@ -26,7 +26,8 @@ BUILDDIR = build
|
|||||||
# One binary per subsystem under test.
|
# One binary per subsystem under test.
|
||||||
TESTS = $(BUILDDIR)/test_cmd_editor \
|
TESTS = $(BUILDDIR)/test_cmd_editor \
|
||||||
$(BUILDDIR)/test_port_tables \
|
$(BUILDDIR)/test_port_tables \
|
||||||
$(BUILDDIR)/test_page_json
|
$(BUILDDIR)/test_page_json \
|
||||||
|
$(BUILDDIR)/test_httpd_tx
|
||||||
|
|
||||||
# The table tests link the register mock and one environment file; stub/
|
# The table tests link the register mock and one environment file; stub/
|
||||||
# stands in for the two headers a firmware build generates.
|
# stands in for the two headers a firmware build generates.
|
||||||
@@ -49,6 +50,11 @@ $(BUILDDIR)/test_port_tables: test_port_tables.c $(TABLE_ENV) hw_mock.h | $(BUIL
|
|||||||
$(BUILDDIR)/test_page_json: test_page_json.c $(TABLE_ENV) hw_mock.h | $(BUILDDIR)
|
$(BUILDDIR)/test_page_json: test_page_json.c $(TABLE_ENV) hw_mock.h | $(BUILDDIR)
|
||||||
$(CC) $(CFLAGS) $(filter %.c,$^) -o $@ $(LDFLAGS)
|
$(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)
|
run: $(TESTS)
|
||||||
@fail=0; for t in $(TESTS); do \
|
@fail=0; for t in $(TESTS); do \
|
||||||
echo "=== $$t ==="; \
|
echo "=== $$t ==="; \
|
||||||
|
|||||||
@@ -47,6 +47,7 @@ fires — so it drops straight into CI.
|
|||||||
| `test_cmd_editor` | `cmd_editor.c` | **C4** — full-line hang + `cmd_buffer` 1-byte overflow; basic entry & backspace regressions |
|
| `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_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_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
|
## Adding a test for another module
|
||||||
1. Write `test_<module>.c` with `main()` driving the module's entry points and
|
1. Write `test_<module>.c` with `main()` driving the module's entry points and
|
||||||
|
|||||||
@@ -6,4 +6,6 @@
|
|||||||
#include <stdint.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;
|
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; };
|
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
|
#endif
|
||||||
@@ -0,0 +1,473 @@
|
|||||||
|
/*
|
||||||
|
* 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_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];
|
||||||
|
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");
|
||||||
|
}
|
||||||
|
|
||||||
|
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();
|
||||||
|
|
||||||
|
printf("\n%s (%d failure%s)\n",
|
||||||
|
failures ? "BENCH: FAILURES" : "BENCH: ALL PASS",
|
||||||
|
failures, failures == 1 ? "" : "s");
|
||||||
|
return failures ? 1 : 0;
|
||||||
|
}
|
||||||
Reference in new issue
Block a user