diff --git a/firmware/skywalker1.c b/firmware/skywalker1.c index 5e0eda8..acb4493 100644 --- a/firmware/skywalker1.c +++ b/firmware/skywalker1.c @@ -80,6 +80,8 @@ #define ERR_EP2_TIMEOUT 0x09 #define ERR_NOT_SUPPORTED 0x0A #define ERR_DISEQC_LEN 0x0B +#define ERR_DISEQC_TIMER 0x0C +#define ERR_WDT_FIRED 0x0D /* configuration status byte bits */ #define BM_STARTED 0x01 @@ -586,13 +588,8 @@ static void bcm4500_shutdown(void) { static void i2c_hotplug_scan(void) { static __xdata BYTE hp_a, hp_byte, hp_bit, hp_diff; - /* Save current as previous before starting new scan. - * This keeps hp_prev valid between scans so the host can read - * both bitmaps and see the actual transition. */ - for (hp_a = 0; hp_a < 16; hp_a++) - hp_prev[hp_a] = hp_curr[hp_a]; - - /* Clear current scan buffer */ + /* Clear current scan buffer (hp_prev retains last SUCCESSFUL scan + * so aborted scans don't corrupt the comparison baseline) */ for (hp_a = 0; hp_a < 16; hp_a++) hp_curr[hp_a] = 0; @@ -641,6 +638,12 @@ static void i2c_hotplug_scan(void) { } } + /* Snapshot successful scan as baseline for next comparison. + * Done after comparison so hp_prev always reflects the last + * fully-completed scan, not a partial abort. */ + for (hp_a = 0; hp_a < 16; hp_a++) + hp_prev[hp_a] = hp_curr[hp_a]; + hp_scan_ok = 1; return; @@ -677,6 +680,8 @@ static void stream_diag_poll(void) { /* Rate-limited BCM4500 I2C reads for sync tracking. * Only attempt if BCM is booted and interval elapsed. */ + /* (WORD) cast: SDCC optimization — avoids 32-bit AND. The 12-bit + * mask (0x0FFF) only needs the low 16 bits, so the cast is safe. */ if ((config_status & BM_FW_LOADED) && ((WORD)sd_poll_count & (SD_I2C_INTERVAL - 1)) == 0) { sd_rd[0] = 0; @@ -781,6 +786,10 @@ static void gpif_stop(void) { IOD |= 0xE0; } +/* Forward declaration: diseqc_wait_ticks() is defined in the Manchester + * encoder section but used by diseqc_tone_burst() above it. */ +static void diseqc_wait_ticks(BYTE count); + /* ---------- DiSEqC tone burst ---------- */ /* @@ -791,45 +800,31 @@ static void gpif_stop(void) { * Uses Timer2 for timing as the stock firmware does. */ static void diseqc_tone_burst(BYTE sat_b) { - BYTE i; - if (sat_b) { last_error = ERR_NOT_SUPPORTED; return; } - /* Configure Timer2 auto-reload */ - /* CKCON.T2M = 0 -> Timer2 clk = 48MHz/12 = 4MHz */ + /* Configure Timer2 auto-reload. + * CKCON bit 5 (T2M) only — do not touch bit 3 (Timer0/watchdog). */ CKCON &= ~0x20; T2CON = 0x04; /* auto-reload, running */ RCAP2H = 0xF8; RCAP2L = 0x2F; /* reload = 63535 -> ~500us tick */ TL2 = 0xFF; TH2 = 0xFF; /* force immediate overflow */ + TF2 = 0; /* Pre-burst settling: 15 ticks (~7.5ms) with carrier off */ IOA &= ~PIN_22KHZ; - TF2 = 0; - for (i = 0; i < 15; i++) { - while (!TF2) - ; - TF2 = 0; - } + diseqc_wait_ticks(15); /* Burst: 25 ticks (~12.5ms) with carrier on */ IOA |= PIN_22KHZ; - for (i = 0; i < 25; i++) { - while (!TF2) - ; - TF2 = 0; - } + diseqc_wait_ticks(25); /* Carrier off */ IOA &= ~PIN_22KHZ; /* Post-burst settling: 5 ticks (~2.5ms) */ - for (i = 0; i < 5; i++) { - while (!TF2) - ; - TF2 = 0; - } + diseqc_wait_ticks(5); /* Stop Timer2 */ TR2 = 0; @@ -853,9 +848,15 @@ static void diseqc_tone_burst(BYTE sat_b) { static void diseqc_wait_ticks(BYTE count) { static __xdata BYTE dt_i; + WORD dt_timeout; for (dt_i = 0; dt_i < count; dt_i++) { - while (!TF2) - ; + dt_timeout = I2C_TIMEOUT; + while (!TF2) { + if (--dt_timeout == 0) { + last_error = ERR_DISEQC_TIMER; + return; + } + } TF2 = 0; } } @@ -2210,6 +2211,15 @@ void main(void) { while (TRUE) { wdt_kick(); /* main loop alive — reset watchdog */ + /* If watchdog fired while we were blocked in a vendor command, + * acknowledge it: set error code so host can read via 0xBC, + * and clear config flags so stale state doesn't cause confusion. */ + if (wdt_armed == 2) { + last_error = ERR_WDT_FIRED; + config_status &= ~(BM_STARTED | BM_FW_LOADED | BM_ARMED); + wdt_armed = 0; /* acknowledged — host must re-boot to recover */ + } + if (got_sud) { handle_setupdata(); got_sud = FALSE;