Merge upstream/main into the Spanning Tree branch

Both conflicts are the register keyword removal meeting a change of ours,
so each takes main's signature and keeps what the branch was saying:
nic_rx_packet() still returns a bool, and port_ingress_filter_get() stays
declared.

main has since put its own copy of the STP module in BANK2, so the pragma
this branch carried is now duplicated. The one with main's comment stays.
This commit is contained in:
d00f
2026-08-30 21:58:54 +02:00
21 changed files with 366 additions and 269 deletions
+99 -56
View File
@@ -50,6 +50,8 @@ __xdata uint8_t boundary[72];
__xdata uint8_t config_upload;
__xdata uint8_t config_buf[CONFIG_UPLOAD_BUF];
__xdata uint16_t cfg_pos, cfg_hdr, cfg_body, cfg_end, cfg_last;
// part-header bytes buffered so far; accumulates across TCP segments
__xdata uint16_t pre_acc;
__xdata uint8_t cfg_bl;
__xdata uint8_t * __xdata content_type = 0;
__xdata uint8_t * __xdata session = 0;
@@ -61,6 +63,9 @@ __xdata uint32_t max_upload;
__xdata uint16_t short_parsed;
__xdata char passwd[21];
// Set when a verified firmware upload awaits its response ACK, after
// which the chip resets to apply the staged image
__xdata uint8_t fw_reset_pending;
__xdata char session_id[SESSION_ID_LENGTH + 1];
__xdata uint8_t authenticated;
__xdata uint32_t now;
@@ -92,6 +97,7 @@ void httpd_init(void) __banked
// Start listening to port 80
uip_listen(HTONS(80));
s->tstate = TSTATE_CLOSED;
fw_reset_pending = 0; // xdata is not zeroed by the startup code
}
@@ -176,7 +182,7 @@ bool is_url_word_x(__xdata uint8_t *uri_str_p, __xdata uint8_t *src_str_p)
}
bool is_word_x(__xdata uint8_t *lhs_str_p, __xdata uint8_t *rhs_str_p)
bool is_word_x(__xdata uint8_t * lhs_str_p, __xdata uint8_t * rhs_str_p)
{
uint8_t u, c;
@@ -241,18 +247,7 @@ void send_unauthorized(void)
}
__xdata uint8_t *skip_boundary(__xdata uint8_t *p)
{
while (*p) {
if (is_word_x(p, boundary))
return p + strlen_x(boundary);
p++;
}
return p;
}
__xdata uint8_t *scan_header(__xdata uint8_t *p)
__xdata uint8_t *scan_header(__xdata uint8_t * __xdata p)
{
content_type = 0;
session = 0;
@@ -311,10 +306,12 @@ __xdata uint8_t *scan_header(__xdata uint8_t *p)
return p;
}
void gen_random_bytes(__xdata uint8_t *b, uint8_t bytes)
/*
* Generate random HEX-chars at the buffer location.
*/
void gen_random_hex_chars(__xdata uint8_t * b, __xdata uint8_t bytes)
{
__xdata uint8_t i = 0;
uint8_t i = 0;
while (bytes) {
if (!i)
get_random_32();
@@ -385,21 +382,47 @@ static uint8_t config_take(void)
}
// unlike scan_header(), keeps no auth state, so it may run on every buffered segment
static uint16_t preamble_payload_start(uint16_t n)
{
uint16_t pos;
for (pos = 0; pos + 24 <= n; pos++) {
if (strstart(&config_buf[pos], "application/octet-stream"))
break;
}
if (pos + 24 > n)
return 0;
pos += 24;
while (pos + 3 < n && !strstart(&config_buf[pos], "\r\n\r\n"))
pos++;
if (pos + 3 >= n)
return 0;
return pos + 4;
}
// Source window for stream_upload(); filled by the caller before the call
__xdata struct {
__xdata uint8_t *p;
uint16_t bptr;
uint16_t plen;
} upload_settings;
/*
* Reads post data from the http stream and writes it into flash memory
* Input: the current position in the TCP buffer (uip_appdata)
* Input: upload_settings, set by the caller
* Returns 1: More data to read, 0: Upload complete, all parts reads
*/
uint8_t stream_upload(uint16_t bptr)
uint8_t stream_upload(void)
{
__xdata uint8_t *p = uip_appdata;
__xdata struct httpd_state * __xdata s = &(uip_conn->appstate);
dbg_string("Stream_upload called: ");
dbg_short(bptr); dbg_char('\n');
dbg_short(upload_settings.bptr); dbg_char('\n');
do {
if (bptr >= uip_len) {
if (upload_settings.bptr >= upload_settings.plen) {
s->tstate = TSTATE_POST;
return 1;
}
@@ -412,17 +435,23 @@ uint8_t stream_upload(uint16_t bptr)
flash_write_bytes(flash_buf);
uptr += write_len;
write_len = 0;
// TODO: This is a bit premature, what about a nice web-page saying the device will reset???
if (verify_crc) {
dbg_string("CRC16: "); dbg_short(crc_final); dbg_char('\n');
// Both bodies are 33 bytes; Content-Length lets the
// browser complete the response without waiting for
// the connection close (which a reset would swallow)
if (crc_final == 0xb001) {
print_string("Checksum OK.\nUpload to flash done, will reset!\n");
// close connection to avoid retries by browser
uip_close();
reset_chip();
slen = strtox(outbuf, "HTTP/1.1 200 OK\r\nContent-Length: 33\r\n"
"Content-Type: text/plain\r\n\r\n"
"OK: checksum verified, rebooting\n");
// Reset once the response is fully ACKed
fw_reset_pending = 1;
} else {
print_string("Checksum incorrect! Aborting.\n");
uip_close();
slen = strtox(outbuf, "HTTP/1.1 400 Bad Request\r\nContent-Length: 33\r\n"
"Content-Type: text/plain\r\n\r\n"
"NO: checksum failed, not applied\n");
}
}
// Make sure there is a 0 at the end of the uploaded data
@@ -430,18 +459,15 @@ uint8_t stream_upload(uint16_t bptr)
flash_region.addr = uptr;
flash_region.len = 1;
flash_write_bytes(flash_buf);
if (bptr >= uip_len)
if (upload_settings.bptr >= upload_settings.plen)
return 0;
if(!verify_crc)
//ugly hack to signal connection finished after config upload.
uip_close();
return 1;
}
if (p[bptr] == boundary[bindex]) {
if (upload_settings.p[upload_settings.bptr] == boundary[bindex]) {
if (!bindex)
crc_final = crc_value;
crc16(p + bptr);
bptr++;
crc16(upload_settings.p + upload_settings.bptr);
upload_settings.bptr++;
bindex++;
} else {
if (bindex) {
@@ -449,8 +475,8 @@ uint8_t stream_upload(uint16_t bptr)
write_len += bindex;
bindex = 0;
}
crc16(p + bptr);
flash_buf[write_len++] = p[bptr++];
crc16(upload_settings.p + upload_settings.bptr);
flash_buf[write_len++] = upload_settings.p[upload_settings.bptr++];
if (write_len >= FLASH_PAGE_SIZE) {
dbg_string("len: "); dbg_short(write_len); dbg_char(' ');
dbg_string("CRC16: "); dbg_short(crc_value); dbg_char('\n');
@@ -511,6 +537,7 @@ void handle_post(void)
uptr = FIRMWARE_UPLOAD_START;
verify_crc = 1;
max_upload = 1024576;
pre_acc = 0;
} else if (is_word(request_path, "config")) {
if (!authenticated) {
send_unauthorized();
@@ -550,11 +577,11 @@ void handle_post(void)
if (is_url_word_x(p, passwd)) {
dbg_string("Password accepted!\n");
read_reg_timer(&last_session_use);
gen_random_bytes(session_id, SESSION_ID_LENGTH);
gen_random_hex_chars(session_id, SESSION_ID_LENGTH);
session_id[SESSION_ID_LENGTH] = NUL;
slen = strtox(outbuf, "HTTP/1.1 302 Found\r\nConnection: close\r\nLocation: index.html\r\n" \
"Set-Cookie: session=");
for (register uint8_t i = 0; i < SESSION_ID_LENGTH; i++)
for (uint8_t i = 0; i < SESSION_ID_LENGTH; i++)
outbuf[slen++] = session_id[i];
slen += strtox(outbuf + slen, "; SameSite=Strict\r\n\r\n");
} else {
@@ -599,21 +626,21 @@ void handle_post(void)
slen = strtox(outbuf, "HTTP/1.1 200 OK\r\nConnection: close\r\n\r\n");
return;
}
// We skip the intial parts as part of the header
do {
p = skip_boundary(p);
if (!*p) {
s->tstate = TSTATE_MULTIPART;
return;
}
p = scan_header(p);
if (!*p)
goto bad_request;
if (!content_type) // We are waiting for the part with the octet stream
continue;
} while (!is_word(content_type, "application/octet-stream"));
cfg_pos = uip_len - (p - uip_appdata);
if (pre_acc + cfg_pos >= CONFIG_UPLOAD_BUF) {
print_string("Firmware upload header too large, aborting.\n");
s->tstate = TSTATE_NONE;
send_bad_request();
return;
}
memcpy(config_buf + pre_acc, p, cfg_pos);
pre_acc += cfg_pos;
cfg_end = preamble_payload_start(pre_acc);
if (!cfg_end) {
s->tstate = TSTATE_MULTIPART;
return;
}
dbg_string("Have content octets\n");
p += 4; // Skip \r\n\r\n sequence at end of preamble of part
flash_init(0); // Re-initialize flash for non-DIO operation, otherwise flashing fails
set_sys_led_state(SYS_LED_FAST);
@@ -621,7 +648,14 @@ void handle_post(void)
crc_value = 0;
bindex = 0;
write_len = 0;
stream_upload(p - uip_appdata);
// A verdict is only built once the upload part completes;
// clear any stale response so the completion check in the
// appcall POST branch cannot send leftovers
slen = 0;
upload_settings.p = config_buf;
upload_settings.bptr = cfg_end;
upload_settings.plen = pre_acc;
stream_upload();
dbg_string("Done reading first fragment\n");
return;
@@ -632,9 +666,6 @@ void handle_post(void)
}
slen = strtox(outbuf, "HTTP/1.1 200 OK\r\nConnection: close\r\n\r\n");
return;
bad_request:
send_bad_request();
return;
}
@@ -698,11 +729,23 @@ void httpd_appcall(void)
cont_len -= slen;
cont_addr += slen;
s->tstate = TSTATE_TX;
} else if (fw_reset_pending) {
// The upload verdict has been fully ACKed by the client;
// now it is safe to reset and apply the staged image
print_string("Resetting to apply update\n");
reset_chip();
}
} else if (uip_newdata() && s->tstate == TSTATE_POST) {
// Check here maxupload by subtracting uip_len and close socekt if fails!
if (max_upload - uip_len > 0) {
stream_upload(0);
upload_settings.p = uip_appdata;
upload_settings.bptr = 0;
upload_settings.plen = uip_len;
stream_upload();
// A completed part with a built verdict must go out
// through the normal TX path
if (s->tstate == TSTATE_NONE && slen)
goto do_send;
write_char('.');
} else {
send_bad_request();
+5 -5
View File
@@ -158,14 +158,14 @@ void sfr_data_to_html(void)
}
void reg_to_html(register uint16_t reg)
void reg_to_html(uint16_t reg)
{
reg_read_m(reg);
sfr_data_to_html();
}
void reg_to_html_long(register uint16_t reg)
void reg_to_html_long(uint16_t reg)
{
reg_read_m(reg);
byte_to_html(sfr_data[0]);
@@ -811,13 +811,13 @@ void send_status(void)
sfp_send_data(sfp, 238, 1);
}
slen += strtox(outbuf + slen,"\",\"sfp_vendor\":\"");
for (register uint8_t s = 0; s < 16 && sfp_module_vendor[sfp][s]; s++)
for (uint8_t s = 0; s < 16 && sfp_module_vendor[sfp][s]; s++)
outbuf[slen++] = sfp_module_vendor[sfp][s];
slen += strtox(outbuf + slen,"\",\"sfp_model\":\"");
for (register uint8_t s = 0; s < 16 && sfp_module_model[sfp][s]; s++)
for (uint8_t s = 0; s < 16 && sfp_module_model[sfp][s]; s++)
outbuf[slen++] = sfp_module_model[sfp][s];
slen += strtox(outbuf + slen,"\",\"sfp_serial\":\"");
for (register uint8_t s = 0; s < 16 && sfp_module_serial[sfp][s]; s++)
for (uint8_t s = 0; s < 16 && sfp_module_serial[sfp][s]; s++)
outbuf[slen++] = sfp_module_serial[sfp][s];
slen += strtox(outbuf + slen,"\",\"sfp_los\":");
if (machine.sfp_port[sfp].pin_los == GPIO_NA) {