新增:FPGA 升级后热恢复射频链路

This commit is contained in:
2026-08-28 13:26:45 +08:00
parent 47b782e976
commit 33d8f4f428
7 changed files with 257 additions and 93 deletions
+3
View File
@@ -104,6 +104,9 @@ all: imx_atsc_app
install: all install: all
cp imx_atsc_app ../../out/upgrade_imx_fs/SMOD/src/aw/anywave/app/ cp imx_atsc_app ../../out/upgrade_imx_fs/SMOD/src/aw/anywave/app/
cp imx_atsc_app ../../out/rootfs/aw/anywave/app/ cp imx_atsc_app ../../out/rootfs/aw/anywave/app/
cp runme.sh ../../out/upgrade_imx_fs/SMOD/runme.sh
cp anywave_exc_start.sh ../../out/upgrade_imx_fs/SMOD/src/aw/anywave_exc_start.sh
cp anywave_exc_start.sh ../../out/rootfs/aw/anywave_exc_start.sh
@echo "\n\n\t ATSC_APP intall ok\n\n" @echo "\n\n\t ATSC_APP intall ok\n\n"
clean: clean:
+88
View File
@@ -0,0 +1,88 @@
#!/bin/sh
# The upgrade shell is forked by the running app and inherits every app socket.
# Re-exec after closing inherited non-stdio descriptors so the old process can
# release its ports cleanly. Keep upgrade diagnostics on the debug serial port.
if [ "$SMOD_FD_CLEAN" != "1" ]; then
export SMOD_FD_CLEAN=1
exec /bin/sh -c '
i=3
while [ $i -lt 256 ]; do
eval "exec ${i}>&-" 2>/dev/null
i=$((i + 1))
done
exec /bin/sh "$1" </dev/null >/dev/ttymxc0 2>&1
' smod_fd_clean "$0"
fi
# Kernel and device-tree updates only take effect after an IMX reboot. Pure
# userspace packages keep the clock/RF hardware powered and restart only the
# application so RF can recover without a power cycle.
need_reboot=0
reboot_reason=""
if [ -f ./src/zImage ]; then
need_reboot=1
reboot_reason="kernel"
echo "kernel image found: reboot required"
fi
if [ -f ./src/imx6ull-S-gpmi-1024x600c7.dtb ]; then
need_reboot=1
if [ -n "$reboot_reason" ]; then
reboot_reason="$reboot_reason+device-tree"
else
reboot_reason="device-tree"
fi
echo "device-tree image found: reboot required"
fi
killall logrotateStart daemon_system 2>/dev/null
sh remove.sh
sh cp.sh
sync
sleep 2
if [ "$need_reboot" = "1" ]; then
echo "boot component updated ($reboot_reason), reboot IMX now"
reboot
exit 0
fi
# pthread_setname_np changes the app's /proc comm, so identify it by the exact
# executable path. Also match "(deleted)" after cp.sh replaces the running ELF.
find_app_pids()
{
for exe in /proc/[0-9]*/exe; do
target=`readlink "$exe" 2>/dev/null` || continue
case "$target" in
/aw/anywave/app/imx_atsc_app|/aw/anywave/app/imx_atsc_app\ \(deleted\))
echo "$exe" | sed 's#/proc/##;s#/exe##'
;;
esac
done
}
old_pids=`find_app_pids`
[ -n "$old_pids" ] && kill $old_pids 2>/dev/null
i=0
while [ -n "`find_app_pids`" ] && [ $i -lt 15 ]; do
sleep 1
i=$((i + 1))
done
old_pids=`find_app_pids`
[ -n "$old_pids" ] && kill -9 $old_pids 2>/dev/null
while [ -n "`find_app_pids`" ]; do sleep 1; done
# Remove stale logging pipelines only after the old application has exited.
killall loglog 2>/dev/null
# Start without the legacy argument 1 so the normal no-power-off RF recovery
# path rebuilds the TALISE handle and FPGA-side JESD link.
sleep 2
cd /aw/anywave/app && nohup sh -c './imx_atsc_app 2>&1 | /usr/sbin/loglog /var/module_log/ imx_atsc_app.log 1' </dev/null >/dev/ttymxc0 2>&1 &
# Restore the system watchdog/housekeeper and log rotation. BusyBox on this
# target has no setsid, and /usr/sbin is not in the default PATH.
sleep 2
nohup sh -c '/usr/sbin/daemon_system 2>&1 | /usr/sbin/loglog /var/module_log/ daemon_system.log 1' >/dev/null 2>&1 &
nohup sh -c '/usr/sbin/logrotateStart 2>&1 | /usr/sbin/loglog /var/module_log/ logrotateStart.log 0' >/dev/null 2>&1 &
+5 -7
View File
@@ -1055,14 +1055,13 @@ void http_write_sys(S_CGI_SET *ppara)
} }
if(ppara->sys_reset) { if(ppara->sys_reset) {
log_printf(LOG_DEBUG, I_USER_CFG, "SYSTEM RESET NOW (IMX+FPGA)!!!!!!!!!!!!!\n"); /* A Web System Reset is an IMX warm reboot. Keep FPGA, AD9528 and
//复位 FPGA本身复位 * ADRV9009 powered so the normal boot path can preserve the RF link. */
fpga_byte_write(0x01,0xfe,0x5a); log_printf(LOG_DEBUG, I_USER_CFG,
fpga_byte_write(0x01,0xfe,0xa5); "WEB SYSTEM RESET NOW (IMX warm reboot, keep RF hardware)\n");
// [one@20220419 add imx reset]
system("sync"); system("sync");
system("reboot"); system("reboot");
return;
} }
} }
@@ -1624,4 +1623,3 @@ void http_save_change_para(void *p)
/*****************************END**************************************/ /*****************************END**************************************/
+4 -1
View File
@@ -1576,8 +1576,8 @@ int get_tsoip_systs1(char *pbuf, short bufMaxLen)
((pnet->udp_rtp&0xff)<<16)|((pnet->packet_len&0xff)<<8)|(pnet->receive_type&0xff), ((pnet->udp_rtp&0xff)<<16)|((pnet->packet_len&0xff)<<8)|(pnet->receive_type&0xff),
pnet->fec, pnet->fec,
pnet->delay, pnet->delay,
pnet->col,
pnet->row, pnet->row,
pnet->col,
pnet->dest_ip[0], pnet->dest_ip[0],
(pnet->dest_ip[1]<<16)|(pnet->dest_ip[2]<<8)|pnet->dest_ip[3], (pnet->dest_ip[1]<<16)|(pnet->dest_ip[2]<<8)|pnet->dest_ip[3],
pnet->dest_port, pnet->dest_port,
@@ -1777,6 +1777,9 @@ int start_fpga_upgrade(char *value,short maxlen)
return 0; return 0;
} }
log_printf(LOG_INFO, I_USER_CFG, "get FPGA .mcs upgrade cmd from web.\n"); log_printf(LOG_INFO, I_USER_CFG, "get FPGA .mcs upgrade cmd from web.\n");
/* Clear the previous request's terminal status before the asynchronous
* imx_up_thr starts. Otherwise the web poll can consume a stale DONE. */
fpga_up_write_status("PENDING", 0, 0);
set_fpga_up_flg(1); set_fpga_up_flg(1);
return 0; return 0;
} }
+4 -4
View File
@@ -863,7 +863,7 @@ static uint32 fpga_upgrade_process(void)
#define FPGA_UP_STATUS_PATH "/tmp/fpga_up_status" #define FPGA_UP_STATUS_PATH "/tmp/fpga_up_status"
/* Publish flash progress for the web page: one line "<PHASE> <PCT> <RET>". /* Publish flash progress for the web page: one line "<PHASE> <PCT> <RET>".
* PHASE: PENDING|ERASE|WRITE|DONE|ERROR ; fpga_up_status.cgi serves this file. */ * PHASE: PENDING|ERASE|WRITE|RELINK|READY|ERROR ; fpga_up_status.cgi serves this file. */
void fpga_up_write_status(const char *phase, int pct, int ret) void fpga_up_write_status(const char *phase, int pct, int ret)
{ {
FILE *sf = fopen(FPGA_UP_STATUS_PATH, "w"); FILE *sf = fopen(FPGA_UP_STATUS_PATH, "w");
@@ -990,7 +990,7 @@ int fpga_upgrade_from_file(const char *path)
delay_ms(200); delay_ms(200);
} }
g_now_only_upgrade_fpga = 0; g_now_only_upgrade_fpga = 0;
if(ret == 0) fpga_up_write_status("DONE", 100, 0); if(ret == 0) fpga_up_write_status("RELINK", 100, 0);
else fpga_up_write_status("ERROR", last_pct < 0 ? 0 : last_pct, ret); else fpga_up_write_status("ERROR", last_pct < 0 ? 0 : last_pct, ret);
log_printf(LOG_DEBUG, I_NOUSED, "fpga upgrade: finished ret=%d lines=%d\n", ret, upState.lineCnt); log_printf(LOG_DEBUG, I_NOUSED, "fpga upgrade: finished ret=%d lines=%d\n", ret, upState.lineCnt);
return ret; return ret;
@@ -2174,8 +2174,8 @@ void check_fpga_up_finished_reboot_sys(void)
fpga_up_sys_reboot=0; fpga_up_sys_reboot=0;
sec_cnt=0; sec_cnt=0;
/* PR10: the FPGA update image was already IPROG-loaded by reset_fpga_system() in /* PR10: the FPGA update image was already IPROG-loaded by reset_fpga_system() in
* check_fpga_upgrade_done(). The old reset_imx_system() reboot is a no-op on this * check_fpga_upgrade_done(). An IMX reboot is unnecessary for an FPGA-only update.
* board (Task10: no HW soft-reboot path). Instead re-establish RF in-place: reconfig * Re-establish RF in-place: reconfig
* the FPGA + re-handshake the JESD link (skip 9009, which stayed powered) + reload the * the FPGA + re-handshake the JESD link (skip 9009, which stayed powered) + reload the
* DPD HOLD curve. This makes a web FPGA .mcs upgrade self-recover WITHOUT a power cycle. */ * DPD HOLD curve. This makes a web FPGA .mcs upgrade self-recover WITHOUT a power cycle. */
extern void hot_reinit_rf(int reload_fpga); extern void hot_reinit_rf(int reload_fpga);
+14 -5
View File
@@ -59,6 +59,7 @@ extern int g_trigger_up_start_flg;
static volatile int g_hot_relink_busy = 0; /* re-entrancy guard: only one relink at a time static volatile int g_hot_relink_busy = 0; /* re-entrancy guard: only one relink at a time
* (the upgrade-completion path and the runtime * (the upgrade-completion path and the runtime
* cfg_valid poll can both request one). */ * cfg_valid poll can both request one). */
static volatile int g_fpga_upgrade_relink_pending = 0; /* web upgrade waits for RF relink */
void hot_reinit_rf(int reload_fpga) void hot_reinit_rf(int reload_fpga)
{ {
extern uint8 hot_get_adpc_mode(void); extern uint8 hot_get_adpc_mode(void);
@@ -70,6 +71,8 @@ void hot_reinit_rf(int reload_fpga)
return; return;
} }
g_hot_relink_busy = 1; g_hot_relink_busy = 1;
if(g_fpga_upgrade_relink_pending)
fpga_up_write_status("RELINK", 100, 0);
prev_adpc = hot_get_adpc_mode(); /* capture DPD mode BEFORE force-stop wipes it */ prev_adpc = hot_get_adpc_mode(); /* capture DPD mode BEFORE force-stop wipes it */
log_printf(LOG_INFO, I_USER_CFG, "[HOT-RESTART] begin (reload_fpga=%d, jesd-relink, skip TALISE, prev_adpc=%d)\n", reload_fpga, prev_adpc); log_printf(LOG_INFO, I_USER_CFG, "[HOT-RESTART] begin (reload_fpga=%d, jesd-relink, skip TALISE, prev_adpc=%d)\n", reload_fpga, prev_adpc);
@@ -105,6 +108,10 @@ void hot_reinit_rf(int reload_fpga)
hot_dpd_rearm(prev_adpc); hot_dpd_rearm(prev_adpc);
} }
log_printf(LOG_INFO, I_USER_CFG, "[HOT-RESTART] done\n"); log_printf(LOG_INFO, I_USER_CFG, "[HOT-RESTART] done\n");
if(g_fpga_upgrade_relink_pending){
fpga_up_write_status("READY", 100, 0);
g_fpga_upgrade_relink_pending = 0;
}
g_hot_relink_busy = 0; g_hot_relink_busy = 0;
} }
@@ -125,10 +132,9 @@ void sys_upgrade(void)
result = atoi(msg); result = atoi(msg);
//usleep(1000000); //usleep(1000000);
if(result == 0){ if(result == 0){
log_printf(LOG_DEBUG,I_NOUSED,"run upgrade sh, and will reboot\n"); log_printf(LOG_DEBUG,I_NOUSED,"run upgrade sh (kernel/DTB -> reboot, userspace-only -> app restart)\n");
system(DO_UPGRADE_RUN); system(DO_UPGRADE_RUN);
log_printf(LOG_DEBUG,I_NOUSED,"system rebooting....\n"); log_printf(LOG_DEBUG,I_NOUSED,"upgrade script started restart/reboot flow\n");
// reboot in runme.sh
} }
} }
} }
@@ -165,8 +171,11 @@ void* imx_up_thr(void * args)
atsc_adpc_force_stop_flag = TRUE; atsc_adpc_force_stop_flag = TRUE;
usleep(2000000); usleep(2000000);
int fret = fpga_upgrade_from_file(FPGA_UP_MCS_PATH); int fret = fpga_upgrade_from_file(FPGA_UP_MCS_PATH);
log_printf(LOG_DEBUG,I_NOUSED,"FPGA upgrade result=%d (0=ok, app will reset+reboot)\n", fret); if(fret == 0)
/* success path: check_fpga_upgrade_done() set fpga_up_sys_reboot=1 -> imx6 reboots */ g_fpga_upgrade_relink_pending = 1;
log_printf(LOG_DEBUG,I_NOUSED,"FPGA upgrade result=%d (0=ok, app will hot-relink RF)\n", fret);
/* Success path uses fpga_up_sys_reboot as a legacy delayed-work flag;
* check_fpga_up_finished_reboot_sys() now performs an in-place RF relink. */
} }
/* test hook: `touch /tmp/hot_restart_now` (or `echo 1 > ...` for reload_fpga) triggers /* test hook: `touch /tmp/hot_restart_now` (or `echo 1 > ...` for reload_fpga) triggers
+130 -67
View File
@@ -5,6 +5,7 @@
<meta name="viewport" content="width=device-width, initial-scale=1.0"> <meta name="viewport" content="width=device-width, initial-scale=1.0">
<title id="webtitle"></title> <title id="webtitle"></title>
<script src="js/theme.js"></script>
<link href="css/styles.css" rel="stylesheet" media="screen"> <link href="css/styles.css" rel="stylesheet" media="screen">
<script src="js/auth.js"></script> <script src="js/auth.js"></script>
<script src="js/jquery.js"></script> <script src="js/jquery.js"></script>
@@ -16,6 +17,13 @@ var lastTime = new Date().getTime();
var currentTime = new Date().getTime(); var currentTime = new Date().getTime();
var timeOut = 20 * 60 * 1000; // 20 min var timeOut = 20 * 60 * 1000; // 20 min
var upgrade_imx_flg = 0; var upgrade_imx_flg = 0;
/* Keep the settings page authenticated while a long FPGA upload/flash is active. */
var fpga_upgrade_in_progress = 0;
function finishFpgaUpgrade(){
fpga_upgrade_in_progress = 0;
upgrade_imx_flg = 0; // resume normal status refresh
lastTime = new Date().getTime();
}
var remot_local_ctrl = 0; // 0 remote 1 local var remot_local_ctrl = 0; // 0 remote 1 local
var g_old_adpc_status = 0; var g_old_adpc_status = 0;
var g_old_adpc = 0; var g_old_adpc = 0;
@@ -37,7 +45,7 @@ $(function(){
function testTime(){ function testTime(){
currentTime = new Date().getTime(); currentTime = new Date().getTime();
if(currentTime - lastTime > timeOut){ if(!fpga_upgrade_in_progress && currentTime - lastTime > timeOut){
window.location.href="login.cgi?logout=1" window.location.href="login.cgi?logout=1"
} }
@@ -145,6 +153,17 @@ $(document).ready(function(){
update_dpd_state(); update_dpd_state();
}); });
/* REG-3 (2026-08-05) — ONE canonical control-lock list. The grey/green mismatch the owner
reported came from hand-typed parallel lists (the load-lock had 9 IDs; the IMX-upgrade
lock was missing #upgrade_fpga_id/#key_set_id/#ntp_set_id; the FPGA flash locked only
its own button). Every full-page lock/unlock now goes through lockAllControls();
NEVER re-type the ID list anywhere. Text (Loading.../Local Mode/Set) stays owned by
each call site — this helper owns only the disabled state. */
var ALL_CONTROL_IDS = '#para_set_id, #net_set_id, #sys_rtc_id, #sysreset_bt_id, #upgrade_imx_id, #upgrade_fpga_id, #key_set_id, #ntp_set_id, #timezone_set_id';
function lockAllControls(lock) {
$(ALL_CONTROL_IDS).attr("disabled", !!lock);
}
function SetFormDefaults(){ function SetFormDefaults(){
if(upgrade_imx_flg == 1) return; if(upgrade_imx_flg == 1) return;
@@ -154,7 +173,7 @@ function SetFormDefaults(){
var queryData = "sys_cfg=&sysnet=&imxsystime=&web_title=&system_a1_key=&ntp_server=&imx_timezone=&"; var queryData = "sys_cfg=&sysnet=&imxsystime=&web_title=&system_a1_key=&ntp_server=&imx_timezone=&";
} else { } else {
// Remote mode: show LOADING indicator // Remote mode: show LOADING indicator
$('#para_set_id, #net_set_id, #sys_rtc_id, #sysreset_bt_id, #upgrade_imx_id, #upgrade_fpga_id, #key_set_id, #ntp_set_id, #timezone_set_id') $(ALL_CONTROL_IDS)
.attr("disabled", true) .attr("disabled", true)
.val("Loading..."); .val("Loading...");
var queryData = "sys_cfg=&sysnet=&imxsystime=&web_title=&system_a1_key=&ntp_server=&imx_timezone=&"; var queryData = "sys_cfg=&sysnet=&imxsystime=&web_title=&system_a1_key=&ntp_server=&imx_timezone=&";
@@ -279,12 +298,12 @@ function SetFormDefaults(){
var currentCtlValue = Number(cf0.ctl.value); var currentCtlValue = Number(cf0.ctl.value);
if(currentCtlValue == 1) { if(currentCtlValue == 1) {
// Local mode: keep buttons disabled // Local mode: keep buttons disabled
$('#para_set_id, #net_set_id, #sys_rtc_id, #sysreset_bt_id, #upgrade_imx_id, #upgrade_fpga_id, #key_set_id, #ntp_set_id, #timezone_set_id') $(ALL_CONTROL_IDS)
.attr("disabled", true) .attr("disabled", true)
.val("Local Mode"); .val("Local Mode");
} else { } else {
// Remote mode: enable buttons // Remote mode: enable buttons
$('#para_set_id, #net_set_id, #sys_rtc_id, #sysreset_bt_id, #upgrade_imx_id, #upgrade_fpga_id, #key_set_id, #ntp_set_id, #timezone_set_id') $(ALL_CONTROL_IDS)
.attr("disabled", false) .attr("disabled", false)
.val("Set"); .val("Set");
$('#upgrade_imx_id').val("Upgrade"); $('#upgrade_imx_id').val("Upgrade");
@@ -461,7 +480,7 @@ function set_syspara() {
postData += "#"+cf.orx_manu_gain.value; postData += "#"+cf.orx_manu_gain.value;
if(cf.ab_mode.value == "1" && Number(cf.adp.value) > 0) { if(cf.ab_mode.value == "1" && Number(cf.adp.value) > 0) {
alert("The ADPC can't be set to Hold or Update or UpdateLIN/UpdateNLIN mode when 'FSSI Refresh' switch 'On' !!! Please change 'FSSI Refresh' to 'Off'."); alert("The ADPC cannot be set to Hold or Update or UpdateLIN/UpdateNLIN mode when 'FSSI Refresh' switch is 'On'. Please change 'FSSI Refresh' to 'Off'.");
return; return;
} else { } else {
var bak_adpc = Number(g_old_adpc); var bak_adpc = Number(g_old_adpc);
@@ -510,6 +529,7 @@ function setsysnet() {
if(cmd_confirm()){ if(cmd_confirm()){
$.post("atsc_status_cfg.cgi?",postData,function(data, status){ $.post("atsc_status_cfg.cgi?",postData,function(data, status){
if(status!="success"){ upgrade_imx_flg = 0; lockAllControls(false); return; } // P-5: release on failure
if(status=="success"){ if(status=="success"){
SetFormDefaults(); SetFormDefaults();
} else{ } else{
@@ -522,6 +542,20 @@ function setsysnet() {
} }
function sysresetff() { function sysresetff() {
/* P-5 (2026-08-06). Two gaps closed here, both proven against the 1.0:
(a) LOCAL-LOCKOUT: the 1.0 refuses a restart in Local mode and re-syncs the
form (its sys_set.html:926); smod posted regardless. A restart is
destructive, so it must honour the same lockout as every other write.
(b) CONTROL LOCK: the restart path locked nothing, so every Set/Upgrade
button stayed live (and green) while the unit rebooted — the owner's
hardware finding. REG-3 already built ONE lockAllControls(); the restart
simply never called it. Same single-lock-list class as the upgrades. */
if(remot_local_ctrl == 1){
cmd_confirm();
SetFormDefaults();
return;
}
var postData = "sysreset="; var postData = "sysreset=";
var cf = document.getElementById("form_reset"); var cf = document.getElementById("form_reset");
var prog_val = 1; var prog_val = 1;
@@ -534,13 +568,14 @@ function sysresetff() {
if(cmd_confirm()){ if(cmd_confirm()){
upgrade_imx_flg = 1; upgrade_imx_flg = 1;
lockAllControls(true);
$.post("atsc_status_cfg.cgi?",postData,function(data, status){ $.post("atsc_status_cfg.cgi?",postData,function(data, status){
if(status=="success"){ if(status=="success"){
var t = setInterval(function() { var t = setInterval(function() {
$('#rst_tb').hide(); $('#rst_tb').hide();
$('#rst_progid').show(); $('#rst_progid').show();
$('#rst_note').text("System is now reseting. Please don't power off during the reseting. Wait for a minute and the page will automatically refreshed."); $('#rst_note').text("The unit is now restarting. Please do not power off during the restart. Wait for a minute and the page will automatically refresh.");
var rst_prog_num = document.getElementById("rst_progid"); var rst_prog_num = document.getElementById("rst_progid");
rst_prog_num.value = prog_val; rst_prog_num.value = prog_val;
@@ -599,18 +634,15 @@ function uploadfile(){
} }
var result=document.getElementById('result'); var result=document.getElementById('result');
$('#upgrade_imx_id').attr("disabled",true); // REG-3: ALL controls lock during an upgrade — including the FPGA/key/NTP buttons the
// old hand-typed list missed (the grey/green mismatch).
lockAllControls(true);
$('#formupgrade').attr("disabled",true); $('#formupgrade').attr("disabled",true);
$('#net_set_id').attr("disabled",true);
$('#para_set_id').attr("disabled",true);
$('#sys_rtc_id').attr("disabled",true);
$('#sysreset_bt_id').attr("disabled",true);
$('#timezone_set_id').attr("disabled",true);
var file = document.getElementById('Upgrade').files[0]; var file = document.getElementById('Upgrade').files[0];
if (!file) { if (!file) {
alert('Please select a file'); alert('Please select a file');
$('#upgrade_imx_id').attr("disabled",false); // undo the disables above lockAllControls(false); // undo the disables above (all of them)
$('#formupgrade').attr("disabled",false); $('#formupgrade').attr("disabled",false);
return return
} }
@@ -651,7 +683,7 @@ function uploadfile(){
$('#formupgrade').hide(); $('#formupgrade').hide();
$('#prog_id').show(); $('#prog_id').show();
$("#progress").hide(); $("#progress").hide();
$('#retnote').text("Code package has been uploaded successfully. System is now upgrading and reboot. Please don't power off the system during the upgrade. Wait for a minute and the page will automatically refreshed."); $('#retnote').text("Code package uploaded successfully. System is now upgrading and rebooting. Please do not power off during the upgrade. Wait for a minute and the page will automatically refresh.");
SetFormDefaults(); SetFormDefaults();
var prog_num = document.getElementById("prog_id"); var prog_num = document.getElementById("prog_id");
prog_num.value = widthddd; prog_num.value = widthddd;
@@ -689,7 +721,8 @@ function uploadFpgaFile(){
} }
var result = document.getElementById('fpga_result'); var result = document.getElementById('fpga_result');
document.getElementById('upgrade_fpga_id').disabled = true; // REG-3: a flash locks the whole page's controls, not only its own button
lockAllControls(true);
// The .mcs is ~23 MB and takes ~10 min to land on the board's flash. The board runs a // The .mcs is ~23 MB and takes ~10 min to land on the board's flash. The board runs a
// small forking httpd; if testTime() keeps firing update_dpd_state/get_ntp_server_state/ // small forking httpd; if testTime() keeps firing update_dpd_state/get_ntp_server_state/
@@ -699,6 +732,8 @@ function uploadFpgaFile(){
// the imx6 package; the FPGA path was missing it, which is why large .mcs uploads never // the imx6 package; the FPGA path was missing it, which is why large .mcs uploads never
// finished from the browser while a scripted POST (no background polling) did. // finished from the browser while a scripted POST (no background polling) did.
upgrade_imx_flg = 1; upgrade_imx_flg = 1;
fpga_upgrade_in_progress = 1;
lastTime = new Date().getTime();
var params = new FormData(); var params = new FormData();
params.append('file', file); params.append('file', file);
@@ -706,12 +741,12 @@ function uploadFpgaFile(){
var xhr = new XMLHttpRequest(); var xhr = new XMLHttpRequest();
xhr.onerror = function(){ xhr.onerror = function(){
alert('FPGA upload request failed'); alert('FPGA upload request failed');
document.getElementById('upgrade_fpga_id').disabled = false; lockAllControls(false);
upgrade_imx_flg = 0; // let the dashboard resume finishFpgaUpgrade();
}; };
xhr.onabort = xhr.ontimeout = function(){ xhr.onabort = xhr.ontimeout = function(){
document.getElementById('upgrade_fpga_id').disabled = false; lockAllControls(false);
upgrade_imx_flg = 0; finishFpgaUpgrade();
}; };
xhr.upload.onprogress = function(e){ xhr.upload.onprogress = function(e){
var percent = Math.round(e.loaded / e.total * 100); var percent = Math.round(e.loaded / e.total * 100);
@@ -721,7 +756,17 @@ function uploadFpgaFile(){
// upload done -> the board now erases+writes the flash; track its progress // upload done -> the board now erases+writes the flash; track its progress
result.innerHTML = "Upload complete. Flashing FPGA... DO NOT power off."; result.innerHTML = "Upload complete. Flashing FPGA... DO NOT power off.";
document.getElementById('fpga_progress').value = 0; document.getElementById('fpga_progress').value = 0;
}
};
xhr.onload = function(){
if(xhr.status >= 200 && xhr.status < 300){
/* The CGI has accepted this request and reset the server-side status.
* Start polling now so a previous DONE result cannot be consumed. */
startFpgaFlashPoll(); startFpgaFlashPoll();
}else{
alert('FPGA upload request failed');
lockAllControls(false);
finishFpgaUpgrade();
} }
}; };
xhr.open('POST', 'fpga_upload.cgi', true); xhr.open('POST', 'fpga_upload.cgi', true);
@@ -747,26 +792,35 @@ function startFpgaFlashPoll(){
if(phase==='PENDING'){ result.innerHTML = "Preparing (stopping DPD)..."; bar.value = 0; } if(phase==='PENDING'){ result.innerHTML = "Preparing (stopping DPD)..."; bar.value = 0; }
else if(phase==='ERASE'){ result.innerHTML = "Erasing flash (~30s)..."; bar.value = 0; } else if(phase==='ERASE'){ result.innerHTML = "Erasing flash (~30s)..."; bar.value = 0; }
else if(phase==='WRITE'){ sawProgress = true; result.innerHTML = "Writing FPGA image ... " + pct + "%"; bar.value = pct; } else if(phase==='WRITE'){ sawProgress = true; result.innerHTML = "Writing FPGA image ... " + pct + "%"; bar.value = pct; }
else if(phase==='DONE'){ else if(phase==='RELINK' || phase==='DONE'){
bar.value = 100; bar.value = 100;
result.innerHTML = "FPGA flash complete (100%). System is rebooting."; result.innerHTML = "FPGA flash complete (100%). Re-establishing RF...";
note.innerText = "Done. FPGA reloaded + RF re-established automatically (no power cycle needed)."; note.innerText = "FPGA reloaded. Waiting for RF/JESD relink...";
clearInterval(poll); if(btn) btn.disabled = false; }
else if(phase==='READY'){
var redirectLeft = 5;
bar.value = 100;
note.innerText = "RF re-established. Redirecting to login page...";
clearInterval(poll); lockAllControls(false); finishFpgaUpgrade();
result.innerHTML = "FPGA and RF are ready. Opening login page in " + redirectLeft + " s...";
var redirectTimer = setInterval(function(){
redirectLeft--;
if(redirectLeft <= 0){ clearInterval(redirectTimer); window.location.href = 'login.html'; }
else result.innerHTML = "FPGA and RF are ready. Opening login page in " + redirectLeft + " s...";
}, 1000);
} }
else if(phase==='ERROR'){ else if(phase==='ERROR'){
result.innerHTML = "<span style='color:#d00;font-weight:bold'>FPGA flash FAILED (code " + ret + "). Aborted — check the device log. The board falls back to golden on cold boot.</span>"; result.innerHTML = "<span style='color:#d00;font-weight:bold'>FPGA flash FAILED (code " + ret + "). Aborted — check the device log. The board falls back to golden on cold boot.</span>";
clearInterval(poll); if(btn) btn.disabled = false; clearInterval(poll); lockAllControls(false); finishFpgaUpgrade();
} }
}, },
error:function(){ error:function(){
// the board reboots on success and drops the connection; if we had // FPGA reload may temporarily interrupt CGI access. Do not redirect
// already seen write progress, treat a sustained drop as done-then-reboot // until the backend explicitly publishes READY.
errAfterProgress++; errAfterProgress++;
if(sawProgress && errAfterProgress >= 4){ if(sawProgress && errAfterProgress >= 4){
bar.value = 100; result.innerHTML = "FPGA flashed. Waiting for RF/JESD relink...";
result.innerHTML = "FPGA flashed; board is rebooting (connection lost). Cold-restart it when it returns."; note.innerText = "Connection temporarily unavailable; retrying status check.";
note.innerText = "Done. FPGA reloaded + RF re-established automatically (no power cycle needed).";
clearInterval(poll); if(btn) btn.disabled = false;
} }
} }
}); });
@@ -1249,12 +1303,13 @@ function set_timezone() {
<div class="app-layout"> <div class="app-layout">
<aside class="app-sidebar"> <aside class="app-sidebar">
<ul class="items"> <ul class="items">
<li><a href="overview.html" title="Overview">Overview</a></li>
<li><a href="status_info.html" title="Status">Status</a></li> <li><a href="status_info.html" title="Status">Status</a></li>
<li class="active"><a href="sys_set.html" title="Settings">Settings</a></li> <li class="active"><a href="sys_set.html" title="Settings">Settings</a></li>
<li><a href="sys_rf.html" title="RF Control">RF Control</a></li> <li><a href="sys_rf.html" title="RF Control">RF Control</a></li>
<li><a href="sys_psip.html" title="TSID&PSIP">TSID&PSIP</a></li> <li><a href="sys_psip.html" title="TSID&PSIP">TSID&PSIP</a></li>
<li><a href="sys_ts.html" title="TSoIP">TSoIP</a></li> <li><a href="sys_ts.html" title="TSoIP">TSoIP</a></li>
<li><a href="sys_spectrum.html" title="Spectrum">Spectrum</a></li> <li><a href="sys_spectrum.html" title="RF Performance">RF Performance</a></li>
<li><a href="log_output.html" title="Log">Log</a></li> <li><a href="log_output.html" title="Log">Log</a></li>
</ul> </ul>
<div class="sidebar-footer"> <div class="sidebar-footer">
@@ -1274,7 +1329,14 @@ function set_timezone() {
</button> </button>
<h1><a href="https://anywavecom.net/" class="logo-bg" title="Anywave Broadcast Inc."></a></h1> <h1><a href="https://anywavecom.net/" class="logo-bg" title="Anywave Broadcast Inc."></a></h1>
<h2 id="webtitle_header"><span></span></h2> <h2 id="webtitle_header"><span></span></h2>
<h3> <h3 class="has-toggle">
<div class="utility-cluster">
<button class="theme-toggle" onclick="toggleTheme()" aria-label="Switch color theme" title="Switch color theme">
<span class="tt-sun" aria-hidden="true"><svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><circle cx="12" cy="12" r="5"></circle><line x1="12" y1="1" x2="12" y2="3"></line><line x1="12" y1="21" x2="12" y2="23"></line><line x1="4.22" y1="4.22" x2="5.64" y2="5.64"></line><line x1="18.36" y1="18.36" x2="19.78" y2="19.78"></line><line x1="1" y1="12" x2="3" y2="12"></line><line x1="21" y1="12" x2="23" y2="12"></line><line x1="4.22" y1="19.78" x2="5.64" y2="18.36"></line><line x1="18.36" y1="5.64" x2="19.78" y2="4.22"></line></svg></span>
<span class="tt-moon" aria-hidden="true"><svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><path d="M21 12.79A9 9 0 1 1 11.21 3 7 7 0 0 0 21 12.79z"></path></svg></span>
<span class="tt-label"><span class="tt-to-light">Light Mode</span><span class="tt-to-dark">Dark Mode</span></span>
</button>
<span class="utility-divider" aria-hidden="true"></span>
<div class="dropdown"> <div class="dropdown">
<button class="dropbtn-modern">Admin &#9662;</button> <button class="dropbtn-modern">Admin &#9662;</button>
<div class="dropdown-content"> <div class="dropdown-content">
@@ -1295,6 +1357,7 @@ function set_timezone() {
</a> </a>
</div> </div>
</div> </div>
</div>
</h3> </h3>
</header> </header>
@@ -1321,16 +1384,6 @@ function set_timezone() {
<span class="summary-unit">dB</span> <span class="summary-unit">dB</span>
</div> </div>
</div> </div>
<div class="summary-card">
<div class="summary-label-row">
<span class="status-dot status-ok" id="dot_uimd" aria-hidden="true"></span>
<span class="summary-label">UIMD</span>
</div>
<div class="summary-value-wrapper">
<span class="summary-value value-ok" id="sum_uimd">--</span>
<span class="summary-unit">dBc</span>
</div>
</div>
<div class="summary-card"> <div class="summary-card">
<div class="summary-label-row"> <div class="summary-label-row">
<span class="status-dot status-ok" id="dot_limd" aria-hidden="true"></span> <span class="status-dot status-ok" id="dot_limd" aria-hidden="true"></span>
@@ -1341,6 +1394,16 @@ function set_timezone() {
<span class="summary-unit">dBc</span> <span class="summary-unit">dBc</span>
</div> </div>
</div> </div>
<div class="summary-card">
<div class="summary-label-row">
<span class="status-dot status-ok" id="dot_uimd" aria-hidden="true"></span>
<span class="summary-label">UIMD</span>
</div>
<div class="summary-value-wrapper">
<span class="summary-value value-ok" id="sum_uimd">--</span>
<span class="summary-unit">dBc</span>
</div>
</div>
<div class="summary-card"> <div class="summary-card">
<div class="summary-label-row"> <div class="summary-label-row">
<span class="status-dot status-off" id="dot_rf" aria-hidden="true"></span> <span class="status-dot status-off" id="dot_rf" aria-hidden="true"></span>
@@ -1596,7 +1659,7 @@ function set_timezone() {
</div> </div>
<br> <br>
<h2>RTC Configuration</h2> <h2>Time &amp; Clock Configuration</h2>
<div class="box"> <div class="box">
<form id="form_rtc"> <form id="form_rtc">
<table> <table>
@@ -1660,11 +1723,11 @@ function set_timezone() {
</tr> </tr>
</table> </table>
</form> </form>
</div> <!-- R4 (2026-08-11): RTC and NTP merged into ONE "Time & Clock Configuration"
section, matching the 1.0 (sys_set.html:1556-1642), which holds form_rtc and
<br> form_ntp in a single .box separated by this hairline rule. Both forms, both
<h2>NTP Server Configuration</h2> Set buttons and every field id are unchanged - only the section wrapper is. -->
<div class="box"> <hr style="border:none; border-top:1px solid var(--border-subtle); margin:10px 0;">
<form id="form_ntp"> <form id="form_ntp">
<table> <table>
<tr> <tr>
@@ -1696,16 +1759,16 @@ function set_timezone() {
</div> </div>
<br> <br>
<h2>SYSTEM KEY</h2> <h2>System Key</h2>
<div class="box"> <div class="box">
<form id="form_key"> <form id="form_key">
<table> <table>
<tr> <tr>
<td>PSIP KEY</td> <td>PSIP Key</td>
<td style="width:500px"><fieldset> <td style="width:500px"><fieldset>
<input style="width:100px" maxlength="8" size="9" name="psip_key" id="psip_key" value="" oninput="validateHex(this)"> <input style="width:100px" maxlength="8" size="9" name="psip_key" id="psip_key" value="" oninput="validateHex(this)">
</fieldset></td> </fieldset></td>
<td>EAS KEY</td> <td>EAS Key</td>
<td style="width:500px"><fieldset> <td style="width:500px"><fieldset>
<input style="width:100px" maxlength="8" size="9" name="eas_key" id="eas_key" value="" oninput="validateHex(this)"> <input style="width:100px" maxlength="8" size="9" name="eas_key" id="eas_key" value="" oninput="validateHex(this)">
</fieldset></td> </fieldset></td>
@@ -1718,51 +1781,51 @@ function set_timezone() {
</div> </div>
<br> <br>
<h2>System Reset Configuration</h2> <h2>Remote Restart</h2>
<div class="box"> <div class="box">
<form id="form_reset"> <form id="form_reset">
<table id="rst_tb"> <table id="rst_tb">
<tr> <tr>
<td>System Reset</td> <td>Remote Restart</td>
<td> <td>
<select name="sysreset" id="sysreset"> <select name="sysreset" id="sysreset">
<option value="0">No <option value="0">No
<option value="1">Yes <option value="1">Yes
</select> </select>
</td> </td>
<td><input type="button" class="btn-modern" disabled id="sysreset_bt_id" alt="Set" value="Set" onclick="return sysresetff();"> </td> <td><input type="button" class="btn-modern" disabled id="sysreset_bt_id" alt="Restart" value="Restart" onclick="return sysresetff();"> </td>
</tr> </tr>
</table> </table>
</form> </form>
</div> </div>
<div><span id="rst_note" style="color:#0B77B7; font-size:14px" ></span></div> <div><span id="rst_note" style="color:var(--info-text); font-size:14px" ></span></div>
<br><br> <br><br>
<div> <div>
<progress id="rst_progid" style="width:100%; margin-left:1px; display:none; color:#0bb710;" value="0" max="100"></progress> <progress id="rst_progid" class="aw-progress" style="width:100%; margin-left:1px; display:none" value="0" max="100"></progress>
<span id='rst_progsval' style="color:#0B77B7; font-size:18px;"></span> <span id='rst_progsval' style="color:var(--info-text); font-size:18px;"></span>
</div> </div>
<br> <br>
<h2>Remote Update</h2> <h2>MCU Update (.tar.gz)</h2>
<div class="box"> <div class="box">
<form id="formupgrade" target="myIframe" name="formupgrade" method="post" action="upload.cgi" enctype="multipart/form-data"> <form id="formupgrade" target="myIframe" name="formupgrade" method="post" action="upload.cgi" enctype="multipart/form-data">
<div id="divupgrade" style="padding: 20px;"> <div id="divupgrade" style="padding: 20px;">
<span id="LUpgrade" ></span> <span id="LUpgrade" ></span>
<input name="Upgrade" type="file" id="Upgrade"/> <input name="Upgrade" type="file" id="Upgrade" accept=".tar.gz,.tgz"/>
<input type="button" class="btn-modern" alt="UPGRADE" value="Upgrade" disabled id="upgrade_imx_id" onclick='uploadfile("imx")'> <input type="button" class="btn-modern" alt="MCU UPGRADE" value="Upgrade MCU" disabled id="upgrade_imx_id" onclick='uploadfile("imx")'>
</div> </div>
</form> </form>
<iframe name="myIframe" id="myIframe" style="display: none;"></iframe> <iframe name="myIframe" id="myIframe" style="display: none;"></iframe>
</div> </div>
<div><span id="retnote" style="color:#0B77B7; font-size:14px" ></span></div> <div><span id="retnote" style="color:var(--info-text); font-size:14px" ></span></div>
<br><br> <br><br>
<div> <div>
<progress id="prog_id" style="width:100%; margin-left:1px; display:none; color:#0bb710;" value="0" max="100"></progress> <progress id="prog_id" class="aw-progress" style="width:100%; margin-left:1px; display:none" value="0" max="100"></progress>
<br><br> <br><br>
<span id='result' style="color: #0B77B7 ;font-size:18px"></span> <span id='result' style="color:var(--info-text);font-size:18px"></span>
<progress id='progress' style="width:100%; margin-left:1px; color:#0B77B7; font-size:18px" value="0" max="100"></progress> <progress id='progress' class="aw-progress" style="width:100%; margin-left:1px" value="0" max="100"></progress>
</div> </div>
<br> <br>
@@ -1777,11 +1840,11 @@ function set_timezone() {
</form> </form>
<iframe name="myIframeFpga" id="myIframeFpga" style="display: none;"></iframe> <iframe name="myIframeFpga" id="myIframeFpga" style="display: none;"></iframe>
</div> </div>
<div><span id="fpga_retnote" style="color:#0B77B7; font-size:14px"></span></div> <div><span id="fpga_retnote" style="color:var(--info-text); font-size:14px"></span></div>
<br> <br>
<div> <div>
<span id='fpga_result' style="color: #0B77B7 ;font-size:18px"></span> <span id='fpga_result' style="color:var(--info-text);font-size:18px"></span>
<progress id='fpga_progress' style="width:100%; margin-left:1px; color:#0B77B7; font-size:18px" value="0" max="100"></progress> <progress id='fpga_progress' class="aw-progress" style="width:100%; margin-left:1px" value="0" max="100"></progress>
</div> </div>
</main> </main>